[technology]以前にやった自分の手書き数字をMNISTを学習させたネットワークで認識すると認識率が悪かったのは過学習のせいかもしれない
ちょっと長いタイトルですみません。
以前にchainerで作ったMNIST分類用のニューラルネットワークに自分の手書き数字(といってもお絵かきツールでマウス描き)を認識させるというエントリで、認識率が50〜60%で、これをデータ不足と判断し、自分で書いた文字を追加学習させる事で精度を上げるという実験をしました。
rc30-popo.hatenablog.com
この時、50個ほどの自作データをMNISTの学習データの一部と置き換える事で85%くらいの認識率を得たのですが、1つ前のエントリ、
rc30-popo.hatenablog.com
でFashion MNISTを使ったネットワークのチューニングを行った結果として、以前にやった自分の手書き文字の認識精度が悪い理由の一つは学習データ不足だけではなく、過学習による汎化性能不足もあるのでは?と今更ながらに思いました。(以前のものは全結合のみ、ドロップアウト無し)
ちなみに自分で書いた手書き数字は以下の様なもので100x100のキャンバス上にマウスで0〜9の文字を書いたものです。グレースケールのpngに保存し、0を書いたファイルなら0n.pngの様にファイル名先頭に0が付く様にしてラベリングしています。背景色が白、前景色が黒で、MNISTのデータと反転しているので、MNISTを学習させたネットワークに分類させる時は白黒反転をさせる必要があります。
で、前回のエントリで作成したネットワークを使って同じ自作手書き文字をMNISTだけを学習した結果を使い分類させてみました。
比較として以前に使った全結合ネットワークでも同じ事を試してみました。PyTorchへの移植がめんどくさかったので、以前使ったコードはChainerのままです。
1)以前に使った全結合ネットワーク(chainer)
# DNN class class DNN(Chain): def __init__(self): super(DNN, self).__init__( l1 = L.Linear(784,100), l2 = L.Linear(100,100), l3 = L.Linear(100,10) ) def forward(self,x): h1 = F.relu(self.l1(x)) h2 = F.relu(self.l2(h1)) h3 = self.l3(h2) return h3
2)今回使ったCNN+ドロップアウト(pytorch)
# DNN class class DNN(nn.Module): def __init__(self): super(DNN,self).__init__() self.cv1 = nn.Conv2d(1,32,3) self.cv2 = nn.Conv2d(32,64,3) self.l1 = nn.Linear(64 * 5 * 5,500) self.l2 = nn.Linear(500,10) def forward(self,x,train=True): c = F.dropout(F.max_pool2d(F.relu(self.cv1(x)),2),training = train) c = F.dropout(F.max_pool2d(F.relu(self.cv2(c)),2),training = train) # Chainerと異なり、全結合層に入力する前にshape変換が必要 c = c.view(-1,64 * 5 * 5) h = F.relu(self.l1(c)) h = F.dropout(h,training = train) h = self.l2(h) return h
2つのネットワークを30エポック回して、自作データ50本を認識させた結果です。
1)以前に使った全結合ネットワーク(chainer) | 2)今回使ったCNN+ドロップアウト(pytorch) |
---|---|
MNISTのテストデータ10000個の認識率: 97.73% 自作データ50個の認識率: 52% |
MNISTのテストデータ10000個の認識率: 99.33% 自作データ50個の認識率: 82% |
以前は自分で書いたデータを1)の全結合ネットワークの学習データに食わせて85%程度の認識率でしたが、今回はMNISTのデータの学習のみで自分の手書き文字の認識率を82%まで上げる事が出来ました。学習データの不足は事実としても、過学習の影響もかなりあった様です。CNNと学習時にドロップアウトを使う事で汎化性能をかなり上げられる事が判ります。
1)に使ったコード:Chainer
# -*- Coding: utf-8 -*- # OS import os # OpenCV import cv2 # Numpy import numpy as np # Chainer import chainer import chainer.links as L import chainer.functions as F from chainer import Chain,optimizers,Variable from chainer import serializers import matplotlib.pyplot as plt train, test = chainer.datasets.get_mnist() x_train = [] x_test = [] t_train = [] t_test = [] for line in train: x_train.append(line[0]) t_train.append(int(line[-1])) for line in test: x_test.append(line[0]) t_test.append(int(line[-1])) x_train = np.array(x_train,dtype=np.float32) x_test = np.array(x_test,dtype=np.float32) t_train = np.array(t_train,dtype=np.int32) t_test = np.array(t_test,dtype=np.int32) # DNN class class DNN(Chain): def __init__(self): super(DNN, self).__init__( l1 = L.Linear(784,100), l2 = L.Linear(100,100), l3 = L.Linear(100,10) ) def forward(self,x): h1 = F.relu(self.l1(x)) h2 = F.relu(self.l2(h1)) h3 = self.l3(h2) return h3 # Create DNN class instance model = DNN() # Set optimizer optimizer = optimizers.Adam() optimizer.setup(model) # Number of epochs n_epoch = 30 # batch size batch_size = 1000 # Execute Training for epoch in range(n_epoch): sum_loss = 0 perm = np.random.permutation(60000) for i in range(0, 60000, batch_size): x = Variable(x_train[perm[i:i+batch_size]]) t = Variable(t_train[perm[i:i+batch_size]]) y = model.forward(x) model.cleargrads() loss = F.softmax_cross_entropy(y, t) loss.backward() optimizer.update() sum_loss += loss.data*batch_size print("epoch: {}, mean loss: {}".format(epoch, sum_loss/60000)) # Save Result #serializers.save_npz("mnist_model2.npz", model) # Execute Test cnt = 0 for i in range(10000): x = np.array([x_test[i]], dtype=np.float32) t = t_test[i] y = model.forward(x) y = np.argmax(y.data[0]) if t == y: cnt += 1 # Display Result print("accuracy: {}".format(cnt/(10000))) # Execute Test # 自分の手書き文字の分類試験。./mydata/下にpngファイルを配置 mydata_dir = './mydata/' mydata_files = os.listdir(mydata_dir) mydata_files.sort() testcnt = 0 testok = 0 for test_file in mydata_files: if test_file.endswith('.png'): testcnt += 1 x_test_data = [] label = int(test_file[0]) image = cv2.imread(mydata_dir + test_file) image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) image_resized = cv2.resize(image,(28,28),interpolation=cv2.INTER_AREA) image_gs = image_resized.flatten() x_test_data.append(image_gs) x_test = np.array([image_gs],dtype=np.float32) x_test /= 255 x = np.array([x_test[0]],dtype=np.float32) y = model.forward(x) res = np.argmax(y.data[0]) if res == label: testok += 1 test_res_str = 'OK' else: test_res_str = 'NG' print(' test file = %s ,label = %d,test result = %d,%s' % (test_file,label,res,test_res_str)) # Display result print('OK %d,NG %d,Total %d' % (testok,(testcnt - testok),testcnt)) print('accuracy: {}'.format(testok/testcnt))
2)に使ったコード: PyTorch
# -*- Coding: utf-8 -*- # OS import os # OpenCV import cv2 # Numpy import numpy as np # Chainer # ★Chainerのdatasetsからmnistを引っ張るためchainerは残す import chainer # ★Pytorchをインポート import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim print('MNIST Deep Learning sample by PyTorch') print(' PyTorch Version: ',torch.__version__) # GPU availability check if torch.cuda.is_available(): device = 'cuda' else: device = 'cpu' print(' Device: ',device) train, test = chainer.datasets.get_mnist() #train, test = chainer.datasets.get_fashion_mnist() x_train = [] x_test = [] t_train = [] t_test = [] for line in train: x_train.append(line[0]) t_train.append(int(line[-1])) for line in test: x_test.append(line[0]) t_test.append(int(line[-1])) x_train = np.array(x_train,dtype=np.float32) x_test = np.array(x_test,dtype=np.float32) # ★PyTorchでは分類クラスはintではなくlong t_train = np.array(t_train,dtype=np.int64) t_test = np.array(t_test,dtype=np.int64) x_train = x_train.reshape(len(x_train),1,28,28) x_test = x_test.reshape(len(t_test),1,28,28) # DNN class class DNN(nn.Module): def __init__(self): super(DNN,self).__init__() self.cv1 = nn.Conv2d(1,32,3) self.cv2 = nn.Conv2d(32,64,3) self.l1 = nn.Linear(64 * 5 * 5,500) self.l2 = nn.Linear(500,10) def forward(self,x,train=True): c = F.dropout(F.max_pool2d(F.relu(self.cv1(x)),2),training = train) c = F.dropout(F.max_pool2d(F.relu(self.cv2(c)),2),training = train) # Chainerと異なり、全結合層に入力する前にshape変換が必要 c = c.view(-1,64 * 5 * 5) h = F.relu(self.l1(c)) h = F.dropout(h,training = train) h = self.l2(h) return h # Create DNN class instance model = DNN().to(device) # Set optimizer # ★optimizerをセット optimizer = optim.Adam(model.parameters()) # Number of epochs n_epoch = 30 # batch size batch_size = 1000 # predict batch size predict_batch_size = 1000 # Execute Training for epoch in range(n_epoch): sum_loss = 0 perm = np.random.permutation(60000) for i in range(0, 60000, batch_size): # ★x_trainとy_trainをpytorchのTensorに変換 # ここで変換は効率悪い気もするけど..元のコードベースを崩さないため x = torch.from_numpy(x_train[perm[i:i+batch_size]]).to(device) t = torch.from_numpy(t_train[perm[i:i+batch_size]]).to(device) y = model.forward(x,train=True) # ★cleargrads()はzero_grad()に model.zero_grad() # ★softmax_cross_entropy()→cross_entropy() loss = F.cross_entropy(y,t) loss.backward() # ★optimizer.update()はoptimizer.step()に optimizer.step() sum_loss += loss.data*batch_size print("epoch: {}, mean loss: {}".format(epoch, sum_loss/60000)) # Execute Test cnt = 0 # ★テストデータを纏めて推論 # GPUのメモリに入りきらない場合があるので、推論もミニバッチで実施 for si in range(0,10000,predict_batch_size): x = torch.from_numpy(x_test[si:si+predict_batch_size]).to(device) y = model.forward(x,train=False) # ★照合用に推論結果yをCPUに戻し、numpyの行列に変換 y = y.cpu().detach().numpy() # ★1点づつ答え合わせ for i in range(predict_batch_size): t = t_test[si + i] yi = np.argmax(y[i]) if t == yi: cnt += 1 # Display Result print("accuracy: {}".format(cnt/(10000))) # Start test mydata_dir = './mydata/' mydata_files = os.listdir(mydata_dir) mydata_files.sort() testcnt = 0 testok = 0 for test_file in mydata_files: if test_file.endswith('.png'): testcnt += 1 x_test_data = [] label = int(test_file[0]) image = cv2.imread(mydata_dir + test_file) image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) image_resized = cv2.resize(image,(28,28),interpolation=cv2.INTER_AREA) image_gs = image_resized.flatten() x_test_data.append(image_gs) x_test = np.array([image_gs],dtype=np.float32) x_test /= 255 x = torch.from_numpy(np.array([x_test[0]],dtype=np.float32).reshape(1,1,28,28)).to(device) y = model.forward(x,train=False) y = y.cpu().detach().numpy() res = np.argmax(y[0]) if res == label: testok += 1 test_res_str = 'OK' else: test_res_str = 'NG' print(' test file = %s ,label = %d,test result = %d,%s' % (test_file,label,res,test_res_str)) # print(y[0]) # Display result print('OK %d,NG %d,Total %d' % (testok,(testcnt - testok),testcnt)) print('accuracy: {}'.format(testok/testcnt))