2019年4月29日月曜日

OpenCLでFDTD法可視化 ~OpenCLとOpenGL PBOの連動~

昨年,結構新しいモデルのMacBook Proを買いました.MacBook Proは結構長い間NVidiaのGPUが入っていたので,GPUの計算は,CUDAを使ってきました.しかし,最近のモデルでは,AMDのRadeon Proが入っているので,CUDAが使えません.そこで,OpenCLを勉強し始めています.特に,映像系のプログラマとして気になるのが,OpenCLとグラフィック機能の連携.

OpenGLでテクスチャを設定するとき,一番簡単な方法としては図のようなアプローチをとります.



この方法は,GPU側で用意したテクスチャメモリに向けて,CPUのメモリのデータをコピーします.


テクスチャをいくつか用意して,それを次々と切り替えたいときは,PBO(Pixel Buffer Object)図のようなアプローチもあります.



PBOからテクスチャへのメモリコピーは,GPU内だけで高速に行えるので,テクスチャの書き換えの都合に合わせてCPUの挙動を考慮する必要がなくなります.
ただ,この方法はいずれにしてもどこかの時点でCPUからGPUへのメモリコピーを行わなければなりません.

現在のコンピュータは,PCIeの通信もそこそこ高速になってきていますので,解像度が高すぎなければ,頻繁にCPUからGPUへのメモリコピーを行なっても問題は少ないかもしれません.(私は,毎フレームglTexImage2DでCPUからGPUのテクスチャメモリへコピーして,動画をテクスチャとして表示するというような実装をよくやりますが,解像度がそんなに高くなければ全く問題ありません)

しかし,CUDAやOpenCLで何かしらの計算をして,その計算結果を濃淡画像として表示する処理をOpenGLのテクスチャを使って実現すると,図のような方法になってしまいます.



データがGPU-CPU間を1往復しています.もしこれが,CPUメモリのデータを入力として使う処理の場合(例えば,画像を色変換して表示する),更にもう一回CPUからGPUへのコピーが増えて,1.5往復することになってしまいます.多くの場合,GPUで計算する際の計算そのものより,CPUからGPUへのデータコピーの方がよほど時間がかかります.




これではせっかくのGPU計算なのに非常に効率が悪いので,こうした,GPU計算の可視化のために,例えばCUDAでは,PBOとして確保した領域をCUDAカーネル内で操作することができます.



この方法をとることで,CPUからGPUへのコピーをすることなく,テクスチャの表示を行うことができます.CPUメモリにあるデータをGPUで処理して,可視化を行う場合でも,CPU-GPU間のデータコピーは1回で済みます.




CUDAを使ったその方法は,この本に詳しくでています.

GPU 並列図形処理入門 ――CUDA・OpenGLの導入と活用

こうしたGPU計算とPBOの連動は,OpenCLでもできます.しかし,多くの資料では「できまっせ〜」みたいな感じのことが書いてあるだけで,具体的にどうやるのかをちゃんと示していません.なので,色々な情報をかき集めて,やってみました.
GPUでFDTD法を計算し,その結果を濃淡画像としてテクスチャにします.
重要なのは以下の部分.

//OpenCLの計算に使え,尚且つOpenGLテクスチャとして表示可能なメモリを確保(サイズはglBufferDataARBで設定したサイズで確保される)

d_g_data=clCreateFromGLBuffer(context, CL_MEM_READ_WRITE, pbo, &ret);



そして以下.

clEnqueueAcquireGLObjects(command_queue,1,&d_g_data,0,NULL,NULL);//テクスチャ描画可能なメモリであることを通知




全体としてはこのようになります.このソースの中で,CPUからGPUへのメモリコピーが一回も発生していないことに注目してください.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"glew.h"
#include<GLUT/GLUT.h>
#include<OpenCL/cl.h>
#include<OpenCL/cl_gl.h>
#include <OpenCL/cl_gl_ext.h>// for CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE
#include <OpenGL/CGLDevice.h> //for CGLGetShareGroup()
#include <OpenGL/CGLCurrent.h>
#define WIDTH 512
#define HEIGHT 512
#define MAX_PLATFORMS (10)
#define MAX_DEVICES (10)
void disp();
GLuint pbo;
GLuint tex;
cl_mem sf=NULL; //現在の値
cl_mem sf_1=NULL; //1回前の値
cl_mem sf_next=NULL; //次の値
cl_mem d_g_data=NULL; //テクスチャとして表示されるメモリ
cl_command_queue command_queue = NULL;
cl_kernel initval; //初期値設定カーネル
cl_kernel fdtd; //FDTD実行カーネル
cl_kernel texcopy; //計算結果をd_g_dataにコピーするカーネル
size_t local_item_size[2]={16,16};
size_t global_item_size[2]={WIDTH,HEIGHT};
int width=WIDTH,height=HEIGHT;
int main(int argc,char *argv[]){
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);
    glutInitWindowSize(WIDTH,HEIGHT);
    glutCreateWindow("render");
    glutDisplayFunc(disp);
    glewInit();
    glEnable(GL_TEXTURE_2D);
    //PBOを設定
    glGenBuffersARB(1,&pbo);
    glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB,pbo);
    glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB,WIDTH*HEIGHT*3*sizeof(unsigned char),0,GL_STREAM_DRAW_ARB);
    glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB,0);
    
    //テクスチャを設定
    glGenTextures(1,&tex);
    glBindTexture(GL_TEXTURE_2D,tex);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,WIDTH,HEIGHT,0,GL_RGB,GL_UNSIGNED_BYTE,NULL);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D,0);
    
    //OpenCL設定
    FILE *fp;
    cl_platform_id platformid[MAX_PLATFORMS];
    cl_uint numplatforms;
    cl_uint status = 1;
    cl_uint platform = 0;
    int device = 0;
    cl_device_id     device_id[MAX_DEVICES];
    cl_uint num_devices;
    char str[BUFSIZ];
    size_t ret_size;
    cl_int ret;
    cl_context       context       = NULL;
    cl_program       program       = NULL;
    char *source_str= new char[100000];//(char *)malloc(100000 * sizeof(char));
    size_t source_size;
    clGetPlatformIDs(MAX_PLATFORMS, platformid, &numplatforms);
    clGetDeviceIDs(platformid[platform], CL_DEVICE_TYPE_ALL, MAX_DEVICES, device_id, &num_devices);
    for(device=0;device<num_devices;device++){
        clGetDeviceInfo(device_id[device], CL_DEVICE_NAME, sizeof(str), str, &ret_size);
        printf("%s %d %d\n",str,device,device_id[device]);
        if(strcmp(str,"AMD Radeon Pro 560 Compute Engine")==0) break; //"AMD Radeon Pro 560 Compute Engine"の番号のdeviceをオープンする(そのために"AMD Radeon Pro 560 Compute Engine"を見つけた時点でループを抜ける)
    }
    //Mac OSではこの部分が必要(Windowsでは不要だった)
    CGLContextObj glContext = CGLGetCurrentContext();
    CGLShareGroupObj shareGroup = CGLGetShareGroup(glContext);
    cl_context_properties props[] =
    {
        CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE,
        (cl_context_properties)shareGroup,
        0
    };
    //Mac OSではこの部分までが必要(Windowsでは不要だった)
    context = clCreateContext(props, 1, &device_id[device], NULL, NULL, &ret);
    command_queue = clCreateCommandQueue(context, device_id[device], 0, &ret);
    if ((fp = fopen("kernel.cl", "r")) == NULL) {
        fprintf(stderr, "kernel source open error\n");
        getchar();
        exit(1);
    }
    source_size = fread(source_str, sizeof(char), 100000, fp);
    fclose(fp);
    program = clCreateProgramWithSource(context, 1, (const char **)&source_str, (const size_t *)&source_size, &ret);
    
    //OpenCLプログラムのビルド
    clBuildProgram(program, 1, &device_id[device], NULL, NULL, NULL);
    
    //カーネルの設定
    texcopy=clCreateKernel(program, "texcopy", &ret);
    fdtd=clCreateKernel(program, "fdtd", &ret);
    initval=clCreateKernel(program, "initval", &ret);
    
    //GPU上のメモリを確保
    sf=clCreateBuffer(context, CL_MEM_READ_WRITE, WIDTH*HEIGHT*sizeof(float), NULL, &ret);
    sf_1=clCreateBuffer(context, CL_MEM_READ_WRITE, WIDTH*HEIGHT*sizeof(float), NULL, &ret);
    sf_next=clCreateBuffer(context, CL_MEM_READ_WRITE, WIDTH*HEIGHT*sizeof(float), NULL, &ret);
    
    //OpenCLの計算に使え,尚且つOpenGLテクスチャとして表示可能なメモリを確保(サイズはglBufferDataARBで設定したサイズで確保される)
    d_g_data=clCreateFromGLBuffer(context, CL_MEM_READ_WRITE, pbo, &ret);
    
    //初期化カーネル実行
    clSetKernelArg(initval, 0, sizeof(cl_mem), &sf);
    clSetKernelArg(initval, 1, sizeof(cl_mem), &sf_1);
    clSetKernelArg(initval, 2, sizeof(int), &width);
    clSetKernelArg(initval, 3, sizeof(int), &height);
    clEnqueueNDRangeKernel(command_queue, initval, 2, NULL, global_item_size, local_item_size, 0, NULL, NULL);
    
    //レンダリング開始
    glutMainLoop();
    clReleaseMemObject(sf);
    clReleaseMemObject(sf_1);
    clReleaseMemObject(sf_next);
    return 0;
}

//レンダリング関数
void disp(){
    //FDTD実行
    clSetKernelArg(fdtd, 0, sizeof(cl_mem), &sf);
    clSetKernelArg(fdtd, 1, sizeof(cl_mem), &sf_1);
    clSetKernelArg(fdtd, 2, sizeof(cl_mem), &sf_next);
    clSetKernelArg(fdtd, 3, sizeof(int), &width);
    clSetKernelArg(fdtd, 4, sizeof(int), &height);
    clEnqueueNDRangeKernel(command_queue, fdtd, 2, NULL, global_item_size, local_item_size, 0, NULL, NULL);
    
    //テクスチャ用メモリへコピー
    clEnqueueAcquireGLObjects(command_queue,1,&d_g_data,0,NULL,NULL);//テクスチャ描画可能なメモリであることを通知
    clSetKernelArg(texcopy,0,sizeof(cl_mem),&d_g_data);
    clSetKernelArg(texcopy,1,sizeof(cl_mem),&sf);
    clSetKernelArg(texcopy,2,sizeof(cl_mem),&sf_1);
    clSetKernelArg(texcopy,3,sizeof(cl_mem),&sf_next);
    clSetKernelArg(texcopy, 4, sizeof(int), &width);
    clSetKernelArg(texcopy, 5, sizeof(int), &height);
    clEnqueueNDRangeKernel(command_queue,texcopy,2,NULL,global_item_size,local_item_size,0,NULL,NULL);
    clEnqueueReleaseGLObjects(command_queue,1,&d_g_data,0,NULL,NULL);//毎回解放する必要がある
    
    //描画(CPUからGPUへのメモリコピーが発生していないことに注目)
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER,pbo);
    glBindTexture(GL_TEXTURE_2D,tex);
    glTexSubImage2D(GL_TEXTURE_2D,0,0,0,WIDTH,HEIGHT,GL_RGB,GL_UNSIGNED_BYTE,NULL);
    glBindTexture(GL_TEXTURE_2D,0);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glBindTexture(GL_TEXTURE_2D,tex);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glBegin(GL_QUADS);
    glTexCoord2f(0,0);
    glVertex3d(-1, -1, 0);
    glTexCoord2f(0,1);
    glVertex3d(-1, 1, 0);
    glTexCoord2f(1,1);
    glVertex3d(1, 1, 0);
    glTexCoord2f(1,0);
    glVertex3d(1, -1, 0);
    glEnd();
    glFlush();
    glBindTexture(GL_TEXTURE_2D,0);
    glutSwapBuffers();
    glutPostRedisplay();
}
OpenCLコード.このコードの中で,OpenCLで確保したメモリも,OpenGLで確保したPBOも同じunsigned char*で扱っていることにご注目ください.

//初期値設定

__kernel void initval(__global float *sf,__global float *sf_1,int width,int height){
    int idx=get_global_id(0);
    int idy=get_global_id(1);
    int index;
    float pi=3.14159265358979323;
    index=idx+(idy*width);
    sf[index]=0;
    sf_1[index]=0;
    if((idx>=width/2)&&(idx<width/2+40)&&(idy>=width/2)&&(idy<width/2+40)){
        sf_1[index]=0.5-0.5*cos(2*pi*(float)(idx-width/2)/40);
        sf_1[index]*=0.5-0.5*cos(2*pi*(float)(idy-width/2)/40);
        sf_1[index]*=0.73;
    }
}
//FDTD法
__kernel void fdtd(__global float *sf,__global float *sf_1,__global float *sf_next,int width,int height){
    int idx=get_global_id(0);
    int idy=get_global_id(1);
    int index=idx+(idy*width);
    int index1=idx+((idy-1)*width);
    int index2=idx+((idy+1)*width);
    int index3=(idx-1)+(idy*width);
    int index4=(idx+1)+(idy*width);
    float deltat=0.00001,deltax=0.001,c=70.0f;
    sf_next[index]=-(sf_1[index]-2*sf[index])+(c*c*deltat*deltat*(sf[index4]+sf[index3]-2*sf[index])/(deltax*deltax))+(c*c*deltat*deltat*(sf[index2]+sf[index1]-2*sf[index])/(deltax*deltax));
}
//計算結果をテクスチャ用メモリへコピー
__kernel  void texcopy(__global unsigned char *tex,__global float *sf,__global float *sf_1,__global float *sf_next,int width,int height){
    int idx=get_global_id(0);
    int idy=get_global_id(1);
    int index=idx+(idy*width);
    int tindex=index*3;
    sf_1[index]=sf[index];
    sf[index]=sf_next[index];
 
    //texはsfの3倍のメモリサイズなので(RGB画像だから)3ピクセルごと同じ値をコピーする
    tex[tindex]=(unsigned char)(sf[index]*255.0f);
    tex[tindex+1]=(unsigned char)(sf[index]*255.0f);
    tex[tindex+2]=(unsigned char)(sf[index]*255.0f);
}



OpenCLの初期化のところで,動作させるOSによって処理内容が変わるようです.本記事のコードは,MacOSX 10.13.4,Xcode 9.3でのコードです.


2019年4月20日土曜日

CIFAR-100を畳み込みニューラルネットワークで認識

本当に久しぶりの投稿になってしまいました.投稿していない間,色々なことがありました.一番大きな出来事は,これまで勤めていた会社を退職し,フリーランスかつアルバイト採用のエンジニアとして働き始めました.現在は,主に測定機や組み込み機器を開発するメーカーでソフトウエア開発をしています.(個人でのご依頼もどんどん受け付けております.もう一回言いますね.個人でのご依頼もどんどん受け付けております.

ありがたいことに,出勤時間や出勤日の自由度が非常に高い会社で,平日でもこれまで出来なかったような音楽活動や,個人で受けた案件を進めたり,単純に遊んだり,昨日頑張った(個人の感想)から昼過ぎまで寝てから出勤したり,昨日頑張った(個人の感想)から昼過ぎまで寝て出勤しようと思ったけどそのまま夕方まで寝たり,ということが出来ています(とはいえ,出勤しないとその分自分の給料に跳ね返るのだが).社風やオフィス内の雰囲気も私好みでとても気に入っています.何より,アルバイト採用という立場ながら,憧れの組み込み業界で開発をすることが出来て,一応仕事の業界的にはキャリアアップも達成できたように思えるのがとても嬉しいです.(組み込みに詳しい風を装って合格したので,分からないことが多いのに今更質問することもできなくて少し焦っています

そんな訳で数ヶ月は,仕事以外の,完全に自分の趣味や興味で何かをすることが,音楽活動以外無くなってしまい,本ブログの更新も滞っていました.しかし,上記のような生活スタイルになって,そうした活動を再開するゆとりが出てきたのでちょっと再開してみますかと,色々始めています.

今回は,機械学習の話題.CIFAR-100のデータをCNNで識別してみます.

機械学習や画像処理の分野で多く用いられる畳み込みニューラルネットワーク(Convolutional neural network 以下CNN).その分野に詳しい方は今更説明するまでもないくらい有名かつ効果的な手法として普及しています.

入力した画像に写っているものが何か識別するタスクに多く用いられます.

https://postd.cc/how-do-convolutional-neural-networks-work/

CNNの仕組みはこちらのwebサイトが非常にわかりやすいので,そちらに譲ります.

特徴としては,教師データの画像の局所的な特徴(点がある,線があるといった抽象的な構造)をとらえて,それを認識に使うので,入力画像で認識したい物体の位置が違っていたり,角度が違っていても認識ができるというところにあります.従来のニューラルネットワークでは,いきなり画像全体の特徴を認識しようとするので,複雑な構造を発見したり,些細な違いを見分けることが困難でした.しかし,画像の局所的な特徴を認識して,それを組み合わせて徐々に複雑な特徴を認識していけば,複雑な画像でも認識できるわけです.

機械学習の練習やテストに用いられる画像データセットは,色々なものが知られています.一番有名なのが,NMISTです.


0〜9までの手書きの数字が学習用に60000枚,テスト(学習がうまくいったか,実際に入力してみる)用に10000枚がセットになっています.

他にも衣類(シャツや靴等)画像を学習するためのFASHON-MNISTもあります.

これらは白黒画像での認識ですが,カラー画像の練習に多く使われているのが,CIFAR-10です.


図の10種類に分類できる画像が,学習用に50000枚,テスト用に10000枚用意されています.

上記データセットの学習は,初歩的なニューラルネットワークやCNNでも非常に精度よく学習できるため,多くの実験例をweb上でみることができます.

ところで,CIFAR-10によく似たデータセットにCIFAR-100があります.


その名の通り,100種類に分類できる画像が学習用に50000枚,テスト用に10000枚用意されています.

このCIFAR-100の学習,やはり100種類の識別となると難しいのか,あまり学習を行っている例を見かけません.そこで,ちょっと行って見ました.色々試行錯誤はしてみましたが,とりあえず現状でのコードです.学習は非常に大変なので,Google Colabratoryを使います.作成したCNNのネットワークと,学習したパラメータをファイルに保存して,学習終了後にダウンロードして,ローカルでの認識処理に使います.学習するついでに,50000枚の学習画像の中から25枚,表示してみています.

import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
#CIFAR100のラベル名
CIFAR100_LABELS_LIST = [
                        'apple', 'aquarium_fish', 'baby', 'bear', 'beaver', 'bed', 'bee', 'beetle',
                        'bicycle', 'bottle', 'bowl', 'boy', 'bridge', 'bus', 'butterfly', 'camel',
                        'can', 'castle', 'caterpillar', 'cattle', 'chair', 'chimpanzee', 'clock',
                        'cloud', 'cockroach', 'couch', 'crab', 'crocodile', 'cup', 'dinosaur',
                        'dolphin', 'elephant', 'flatfish', 'forest', 'fox', 'girl', 'hamster',
                        'house', 'kangaroo', 'keyboard', 'lamp', 'lawn_mower', 'leopard', 'lion',
                        'lizard', 'lobster', 'man', 'maple_tree', 'motorcycle', 'mountain', 'mouse',
                        'mushroom', 'oak_tree', 'orange', 'orchid', 'otter', 'palm_tree', 'pear',
                        'pickup_truck', 'pine_tree', 'plain', 'plate', 'poppy', 'porcupine',
                        'possum', 'rabbit', 'raccoon', 'ray', 'road', 'rocket', 'rose',
                        'sea', 'seal', 'shark', 'shrew', 'skunk', 'skyscraper', 'snail', 'snake',
                        'spider', 'squirrel', 'streetcar', 'sunflower', 'sweet_pepper', 'table',
                        'tank', 'telephone', 'television', 'tiger', 'tractor', 'train', 'trout',
                        'tulip', 'turtle', 'wardrobe', 'whale', 'willow_tree', 'wolf', 'woman',
                        'worm'
                        ]
#CIFAR-100 datasetの読み込み
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar100.load_data(label_mode='fine')

train_labels_onehot=keras.utils.to_categorical(y_train,100)
test_labels_onehot=keras.utils.to_categorical(y_test,100)

#画像をfloat32(0.~1.)に変換
x_train=x_train.astype("float32")/255.0
x_test=x_test.astype("float32")/255.0

print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)

#学習画像を少し見てみる
plt.figure(figsize=(5,5))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x_train[i+25])
    plt.xlabel(CIFAR100_LABELS_LIST[int(y_train[i+25])])

#ネットワーク作成
model=keras.Sequential()

model.add(keras.layers.Conv2D(filters=32,kernel_size=(3,3),padding='same',activation='relu',input_shape=(32,32,3)))
model.add(keras.layers.Conv2D(filters=32,kernel_size=(3,3),padding='same',activation='relu'))
model.add(keras.layers.MaxPool2D(pool_size=(2,2)))
model.add(keras.layers.Dropout(0.25))

model.add(keras.layers.Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(keras.layers.Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(keras.layers.MaxPool2D(pool_size=(2,2)))
model.add(keras.layers.Dropout(0.25))

model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(512,activation='relu'))
model.add(keras.layers.Dropout(0.5))
model.add(keras.layers.Dense(100,activation='softmax'))

model.compile(optimizer=tf.train.AdamOptimizer(),loss='categorical_crossentropy',metrics=["accuracy"])

#学習用データで学習してみる
model.fit(x_train[:,:,:,:],train_labels_onehot,epochs=200,batch_size=64)

#ネットワークをファイルに保存
model_json_str=model.to_json()
open('model.json','w').write(model_json_str)
#学習したパラメータをファイルに保存
model.save_weights('weights.h5')

plt.show()

データセットの一部を表示して見ると,こんな感じ.



学習回数は200回です.GPUを使っても結構時間がかかりますので,以下のようなセッション切れ対策が必要かもしれません.
Google Colaboratoryの90分セッション切れ対策【自動接続】

結果は画像のようになりました.


accuracyは80%までいきましたが,lossがそんなに下がっていないですね.
とりあえず,保存したモデルとパラメータをダウンロードして,ローカルマシンでテストしてみます.テスト用の10000枚をネットワークに入力して分類して見ます.Pythonのバージョンは3.7.1です.

import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
#CIFAR100のラベル名
CIFAR100_LABELS_LIST = [
                        'apple', 'aquarium_fish', 'baby', 'bear', 'beaver', 'bed', 'bee', 'beetle',
                        'bicycle', 'bottle', 'bowl', 'boy', 'bridge', 'bus', 'butterfly', 'camel',
                        'can', 'castle', 'caterpillar', 'cattle', 'chair', 'chimpanzee', 'clock',
                        'cloud', 'cockroach', 'couch', 'crab', 'crocodile', 'cup', 'dinosaur',
                        'dolphin', 'elephant', 'flatfish', 'forest', 'fox', 'girl', 'hamster',
                        'house', 'kangaroo', 'keyboard', 'lamp', 'lawn_mower', 'leopard', 'lion',
                        'lizard', 'lobster', 'man', 'maple_tree', 'motorcycle', 'mountain', 'mouse',
                        'mushroom', 'oak_tree', 'orange', 'orchid', 'otter', 'palm_tree', 'pear',
                        'pickup_truck', 'pine_tree', 'plain', 'plate', 'poppy', 'porcupine',
                        'possum', 'rabbit', 'raccoon', 'ray', 'road', 'rocket', 'rose',
                        'sea', 'seal', 'shark', 'shrew', 'skunk', 'skyscraper', 'snail', 'snake',
                        'spider', 'squirrel', 'streetcar', 'sunflower', 'sweet_pepper', 'table',
                        'tank', 'telephone', 'television', 'tiger', 'tractor', 'train', 'trout',
                        'tulip', 'turtle', 'wardrobe', 'whale', 'willow_tree', 'wolf', 'woman',
                        'worm'
                        ]
#CIFAR-100 datasetの読み込み
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar100.load_data(label_mode='fine')

train_labels_onehot=keras.utils.to_categorical(y_train,100)
test_labels_onehot=keras.utils.to_categorical(y_test,100)

#画像をfloat32(0.~1.)に変換
x_train=x_train.astype("float32")/255.0
x_test=x_test.astype("float32")/255.0

print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)

#学習画像を少し見てみる
plt.figure(figsize=(5,5))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    #ランダムな番号の画像を見る
    index=np.random.randint(0,49999)
    plt.imshow(x_train[index])
    plt.xlabel(CIFAR100_LABELS_LIST[int(y_train[index])])

#ネットワーク読み込み
model=keras.models.model_from_json(open('model.json').read())
#学習したパラメータ読み込み
model.load_weights('weights.h5')
model.summary()
model.compile(optimizer=tf.train.AdamOptimizer(),loss='categorical_crossentropy',metrics=["accuracy"])

#テスト画像を入力して識別
labels=model.predict(x_test[:,:,:,:])

#結果を少し見る
plt.figure(figsize=(5,5))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    #ランダムな番号の画像を見る
    index=np.random.randint(0,9999)
    true_index=np.argmax(test_labels_onehot[index])#正解
    predict_index=np.argmax(labels[index])#予測したインデックス
    plt.imshow(x_test[index])#画像表示
    plt.xlabel("{}({})".format(CIFAR100_LABELS_LIST[predict_index],CIFAR100_LABELS_LIST[true_index]),color=("green" if predict_index==true_index else "red"))#"予測したラベル(正解のラベル)"で表示.正解なら緑,間違っていれば赤で表示

#実際、テスト画像でどれほど正解しているのか?
correct=0
for i in range(10000):
    true_index=np.argmax(test_labels_onehot[i])#正解
    predict_index=np.argmax(labels[i])#予測したインデックス
    if true_index==predict_index:
        correct+=1

print("correct: "+str(correct)+" / 10000")

plt.show()


結果は以下のようになりました.画像名を緑色で表示しているのが正解した画像.


結構間違えてますね.でも,実際間違えているのを見ると,惜しい(自分が分類しても間違えるかもしれない)のもあったりします.

実際,10000枚のうち何枚正解したのか数えてみました.


いや何が80%なんですか.

やはり100種類に分類するというのは結構難しいようですね.