画像セットを増やす「前処理」 精度向上するのか??
CIFAR-10のaccuracy96%をchainerで目指すため色々頑張ってみるシリーズ第1弾
テーマ:画像に色々とテキトーな処理をしてデータを増やす
元画像(船)
32(px)*32(px)
ガンマ変換
明るさを調整する。
gamma = 1.5 look_up_table = np.ones((256, 1), dtype = 'uint8' ) * 0 for i in range(256): look_up_table[i][0] = 255 * pow(float(i) / 255, 1.0 / gamma) X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) img_gamma = cv2.LUT(img, look_up_table) X.append(img_gamma.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X))
ガンマの値を0.75にすると
平滑化
滑らかにする。
average_square = (10,10) X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) blur_img = cv2.blur(img, average_square) X.append(blur_img.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X))
ここで大きなミス
average_square = (10,10)
これはやばい。32*32しかないのに、それを10*10で平滑化したらほぼ何も見えない笑
average_square = (3,3)
ぐらいが適正かと思われる。
コントラスト
ハイコントラスト
ローコントラスト
min_table = 50 max_table = 205 diff_table = max_table - min_table LUT_HC = np.arange(256, dtype = 'uint8' ) LUT_LC = np.arange(256, dtype = 'uint8' ) for i in range(0, min_table): LUT_HC[i] = 0 for i in range(min_table, max_table): LUT_HC[i] = 255 * (i - min_table) / diff_table for i in range(max_table, 255): LUT_HC[i] = 255 for i in range(256): LUT_LC[i] = min_table + i * (diff_table) / 255 X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) high_cont_img = cv2.LUT(img, LUT_HC) X.append(high_cont_img.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X))
ガウシアンノイズ
各画素にガウス分布に基づく生成値によりノイズを加える。
X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) row,col,ch= img.shape mean = 0 sigma = 15 gauss = np.random.normal(mean,sigma,(row,col,ch)) gauss = gauss.reshape(row,col,ch) gauss_img = img + gauss X.append(gauss_img.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X))
少し気持ち悪い。
普通に見て参考に良さそうな画像データではないかも笑
ソルト&ペッパーノイズ
白い点と黒い点のノイズを加える。
X = [] for i in range(50000): img = X_train[i].reshape(3, 32, 32).transpose(1, 2, 0) row,col,ch= img.shape s_vs_p = 0.5 amount = 0.004 sp_img = img.copy() num_salt = np.ceil(amount * img.size * s_vs_p) coords = [np.random.randint(0, i-1 , int(num_salt)) for i in img.shape] sp_img[coords[:-1]] = (255,255,255) num_salt = np.ceil(amount * img.size * s_vs_p) coords = [np.random.randint(0, i-1 , int(num_salt)) for i in img.shape] sp_img[coords[:-1]] = (0,0,0) X.append(sp_img.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X))
左右反転
途中で思ったけど下のような書き方のほうがpythonらしいし、速度とメモリ面に関しても優秀かも...
(いまいちよくわかってない)
def flick(X): img = X.transpose(1,2,0) return cv2.flip(img, 1).transpose(2,0,1) X = [] X = [flick(X_train[i]) for i in range(50000)] X_train = np.vstack((X_train, X))
いろいろ試した結果、
とりあえずガンマ変換(1.5,0.75)とハイコントラスト・ローコントラストを増やした約25万件のデータで学習することにしました。
そして、NNをすこし変更しました。
input |
conv1 |
relu |
conv2 |
relu |
max_pooling_2d |
dropout |
conv3 |
relu |
conv4 |
relu |
max_pooling_2d |
dropout |
conv5 |
relu |
conv6 |
relu |
max_pooling_2d |
linear |
relu |
dropout |
linear |
softmax |
今回は96%超えたいので200エポック回します笑
コードはこちら。
結果は下にあります。
ちなみにGeForce GTX 960で約4時間かかりました。
import cv2 import sys import pickle import time import argparse import numpy as np from tqdm import tqdm import chainer from chainer import cuda, Function, gradient_check, Variable, optimizers, serializers, utils from chainer import Link, Chain, ChainList, FunctionSet import chainer.functions as F import chainer.links as L import matplotlib.pyplot as plt from sklearn.cross_validation import train_test_split # GPU設定 parser = argparse.ArgumentParser(description='Chainer example: CIFAR-10') parser.add_argument('--gpu', '-gpu', default=-1, type=int, help='GPU ID (negative value indicates CPU)') # GPUが使えるか確認 args = parser.parse_args() if args.gpu >= 0: cuda.check_cuda_available() xp = cuda.cupy if args.gpu >= 0 else np # CIFAR-10データを読み込む関数 def unpickle(file): fp = open(file, 'rb') if sys.version_info.major == 2: data = pickle.load(fp) elif sys.version_info.major == 3: data = pickle.load(fp, encoding='latin-1') fp.close() return data # 5つに分かれているデータを全て読み込み一つにする X_train = None y_train = [] for i in range(1,6): data_dic = unpickle("cifar-10-batches-py/data_batch_{}".format(i)) if i == 1: X_train = data_dic['data'] else: X_train = np.vstack((X_train, data_dic['data'])) y_train += data_dic['labels'] test_data_dic = unpickle("cifar-10-batches-py/test_batch") X_test = test_data_dic['data'] X_test = X_test.reshape(len(X_test),3,32,32) y_test = np.array(test_data_dic['labels']) X_train = X_train.reshape((len(X_train),3, 32, 32)) # 訓練データを増やす #--- y_train ---# temp = y_train for i in range(4): y_train = np.r_[y_train, temp] #--- X_train ---# # ガンマ変換(1.5・0.75) gamma = 1.5 look_up_table = np.ones((256, 1), dtype = 'uint8' ) * 0 for i in range(256): look_up_table[i][0] = 255 * pow(float(i) / 255, 1.0 / gamma) X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) img_gamma = cv2.LUT(img, look_up_table) X.append(img_gamma.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X)) gamma = 0.75 look_up_table = np.ones((256, 1), dtype = 'uint8' ) * 0 for i in range(256): look_up_table[i][0] = 255 * pow(float(i) / 255, 1.0 / gamma) X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) img_gamma = cv2.LUT(img, look_up_table) X.append(img_gamma.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X)) # コントラスト min_table = 50 max_table = 205 diff_table = max_table - min_table LUT_HC = np.arange(256, dtype = 'uint8' ) LUT_LC = np.arange(256, dtype = 'uint8' ) for i in range(0, min_table): LUT_HC[i] = 0 for i in range(min_table, max_table): LUT_HC[i] = 255 * (i - min_table) / diff_table for i in range(max_table, 255): LUT_HC[i] = 255 for i in range(256): LUT_LC[i] = min_table + i * (diff_table) / 255 X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) high_cont_img = cv2.LUT(img, LUT_HC) X.append(high_cont_img.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X)) X = [] for i in range(50000): img = X_train[i].transpose(1, 2, 0) low_cont_img = cv2.LUT(img, LUT_LC) X.append(low_cont_img.transpose(2,0,1)) X = np.array(X) X_train = np.vstack((X_train, X)) # INPUTデータはnp.float型・教師データはnp.int32型へ変換 y_train = np.array(y_train) X_train = X_train.astype(np.float32) X_test = X_test.astype(np.float32) X_train /= 255 X_test /= 255 y_train = y_train.astype(np.int32) y_test = y_test.astype(np.int32) # 訓練データをシャッフル、その中の9.9割のデータを使用 X_train, temp1, y_train, temp2 = train_test_split(X_train, y_train, train_size=0.99, random_state=0) # 畳み込み6層 model = chainer.FunctionSet(conv1=F.Convolution2D(3, 32, 3, pad=1), conv2=F.Convolution2D(32, 32, 3, pad=1), conv3=F.Convolution2D(32, 32, 3, pad=1), conv4=F.Convolution2D(32, 32, 3, pad=1), conv5=F.Convolution2D(32, 32, 3, pad=1), conv6=F.Convolution2D(32, 32, 3, pad=1), l1=F.Linear(512, 512), l2=F.Linear(512, 10)) # GPU使用のときはGPUにモデルを転送 if args.gpu >= 0: cuda.get_device(args.gpu).use() model.to_gpu() def forward(x_data, y_data, train=True): x, t = chainer.Variable(x_data), chainer.Variable(y_data) h = F.relu(model.conv1(x)) h = F.max_pooling_2d(F.relu(model.conv2(h)), 2) h = F.dropout(h, ratio=0.2, train=train) h = F.relu(model.conv3(h)) h = F.max_pooling_2d(F.relu(model.conv4(h)), 2) h = F.dropout(h, ratio=0.2, train=train) h = F.relu(model.conv5(h)) h = F.max_pooling_2d(F.relu(model.conv6(h)), 2) h = F.dropout(F.relu(model.l1(h)), ratio=0.2, train=train) y = model.l2(h) return F.softmax_cross_entropy(y, t), F.accuracy(y, t) # optimizerの設定(今回はAdamを使用) optimizer = optimizers.Adam() optimizer.setup(model) # 結果を保存する配列 train_loss = np.array([]) train_acc = np.array([]) test_loss = np.array([]) test_acc = np.array([]) N = 247500 N_test = 10000 batch_size = 100 # 時間計測スタート start_time = time.clock() # エポック数40で回していく for epoch in range(200): print("epoch", epoch+1) perm = np.random.permutation(N) # CPUで計算 sum_accuracy = 0 sum_loss = 0 # 訓練 # tadmを使うことにより、経過が視覚化できる for i in tqdm(range(0, N, batch_size)): # GPU使用可能の場合はGPUを使用 X_batch = xp.asarray(X_train[perm[i:i+batch_size]]) y_batch = xp.asarray(y_train[perm[i:i+batch_size]]) # 勾配を初期化 optimizer.zero_grads() # 順伝播させて誤差と精度を計算 loss, acc = forward(X_batch, y_batch) # バックプロパゲーション loss.backward() # 最適化ルーティーンを実行 optimizer.update() # pythonのlist.appendと同じような関数(np.concatenate) train_loss = np.concatenate((train_loss, [loss.data])) train_acc = np.concatenate((train_acc, [acc.data])) sum_loss += float(cuda.to_cpu(loss.data)) * batch_size sum_accuracy += float(cuda.to_cpu(acc.data)) * batch_size # 訓練データの誤差と、正解精度を表示 print("train mean loss={}, accuracy={}".format(sum_loss/N, sum_accuracy/N)) # テスト評価 sum_accuracy = 0 sum_loss = 0 for i in tqdm(range(0, N_test, batch_size)): X_batch = xp.asarray(X_train[perm[i:i+batch_size]]) y_batch = xp.asarray(y_train[perm[i:i+batch_size]]) # 順伝播させて誤差と精度を計算 loss, acc = forward(X_batch, y_batch) test_loss = np.concatenate((train_loss, [loss.data])) test_acc = np.concatenate((train_acc, [acc.data])) sum_loss += float(cuda.to_cpu(loss.data)) * batch_size sum_accuracy += float(cuda.to_cpu(acc.data)) * batch_size # 訓練データの誤差と、正解精度を表示 print("test mean loss={}, accuracy={}".format(sum_loss/N_test, sum_accuracy/N_test)) end_time = time.clock() print("total time : ", end_time - start_time)
結果
< 最高正答率 >
epoch 191
0.9702000087499618
なんと予想外に97%を超すことができました!!
平均値除去・正規化 ・白色化などについてはのちのちやろうと思います笑
目指せ98%!