kaggleチャレンジ 11日目 CNNでハンドサインを分類してみた

シェアする

  • このエントリーをはてなブックマークに追加
スポンサーリンク

はじめに

 今回も、Kaggleに公開されているデータセットを使っていきたいとおもいます。今回は、画像分類をkaggleのデータセットにあったハンドサインのデータを使って行なっていきたいと思います。このデータセットはかなりいいデータセットだと思います。理由としては、画像サイズが小さく、10クラス分類であること、0から9までのハンドサインを分類するということでMNISTに近い感覚で行なうことができるためです。また、データセットがnumpy配列なのでとっつきやすいかもしれません。

ということで、今回もGoogleColaboratory上でPytorchを使って画像分類をしていきたいと思います。

今回のデータセットのリンク

Sign Language Digits Dataset

合わせて読みたい記事:

データセットの説明

ハンドサインの種類

0から9までのハンドサイン

データセットの内訳

  • 画像サイズ: 64×64
  • 画像枚数:2062枚
  • グレイスケール画像
  • ファイルのフォーマット: npy
  • 10クラス (Digits: 0-9)
  • 被写体の生徒数: 218
  • 生徒毎のサンプル数: 10

ソースコード

綺麗なコードではありませんが許してください。また、重複するコードが多いので今回は変更した部分のみを載せておきます。こちらを参考にしてください。

また、次のカーネルを参考にしました。

ライブラリの準備

import pandas as pd 
import torch  
import numpy as np  
import torch.nn as nn  
import torch.optim as optim  
import torch.nn.functional as F  
import torch.utils.data  
import torch.backends.cudnn as cudnn  
import torchvision  from torchvision 
import datasets, models, transforms  
from torchsummary import summary  
import torchvision  
from torchvision import datasets, models, transforms 
from sklearn.metrics import confusion_matrix, accuracy_score  
import random  
import os  
import time 
from PIL import Image
from sklearn.model_selection import train_test_split 
% matplotlib inline

ハンドサインの画像を出力させてみる

# load data set 
data = np.load('X.npy') 
target = np.load('Y.npy') 
img_size = 64 
plt.subplot(1, 2, 1) 
plt.imshow(data[1855].reshape(img_size, img_size)) 
plt.axis('off') 
plt.subplot(1, 2, 2) 
plt.imshow(data[1854].reshape(img_size, img_size)) 
plt.axis('off')

データ変換

dataの長さだけYに0を要素とする配列を生成し、初期化します。そして、その中に画像に対応するラベルを入力していきます。気になる方は上の画像を出力させるコードに実際に境界の数字を入れて確かめてみてください。そして8:2の割合で学習データとテストデータに分割します。

Y = np.zeros(data.shape[0]) 
Y[:204] = 9 
Y[204:409] = 0
Y[409:615] = 7 
Y[615:822] = 6 
Y[822:1028] = 1 
Y[1028:1236] = 8 
Y[1236:1443] = 4 
Y[1443:1649] = 3 
Y[1649:1855] = 2 
Y[1855:] = 5 
X_train, X_test, y_train, y_test = train_test_split(data, Y, test_size = 0.2, random_state = 2)

データセット

今回は、ディレクトリ内に画像が入っているわけではないので、Datasetを継承して必要なものを入れたDatasetProcessing()を作ります。

class DatasetProcessing(Dataset):
 def __init__(self, data, target, transform=None):
    self.transform = transform
   self.data = data.reshape((-1,64,64)).astype(np.uint8)[:,:,:,None]
   self.target = torch.from_numpy(target).long()
   def __getitem__(self, index):
     return self.transform(self.data[index]), self.target[index]
   def __len__(self):
      return len(list(self.data))

今回は、バッチサイズ4で行きたいと思います。

transform = transforms.Compose( [
   transforms.ToPILImage(),
   transforms.RandomHorizontalFlip(),
   transforms.RandomVerticalFlip(0.5),
   transforms.ToTensor(),
   transforms.Normalize(mean=(0.5,),std=(0.5,))
 ]) 
dataset_train = DatasetProcessing(X_train, y_train, transform)
 train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=4, shuffle=True, num_workers=4)

学習データ出力

中身を出力させてみてみましょう。

for num, x in enumerate(X_train[0:9]):
   plt.subplot(1,9,num+1)
   plt.axis('off')
   plt.imshow(x)
   plt.title(y_train[num])

ネットワーク構成

class Net(nn.Module):
   def __init__(self):
     super(Net, self).__init__()
     self.pool = nn.MaxPool2d(2, 2)
     #1st net
     self.conv1_1 = nn.Conv2d(1, 4, kernel_size=(4, 4), padding = (3, 3), stride=(2, 2)) #34 * 34
     self.conv1_2 = nn.Conv2d(4, 8, kernel_size=(4, 4), padding = (2, 2), stride=(2, 2)) #18 * 18 #mp
     self.fc1_1 = nn.Linear(8 * 9 * 9, 32) #dropout = 0.2
     self.fc1_1_drop = nn.Dropout(p=0.2)
     self.fc1_2 = nn.Linear(32, 10)
     #2nd net
     self.conv2_1 = nn.Conv2d(1, 3, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) #64 * 64
     self.conv2_2 = nn.Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) #64 * 64 #mp
     self.conv2_2_drop = nn.Dropout2d()
     self.conv2_3 = nn.Conv2d(6, 12, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) #64 * 64
    self.conv2_4 = nn.Conv2d(12, 24, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) #64 * 64 #mp
     self.conv2_4_drop = nn.Dropout2d()
     self.fc2_1 = nn.Linear(24 * 16 * 16, 120) #dropout = 0.5
     self.fc2_1_drop = nn.Dropout(p=0.5)
     self.fc2_2 = nn.Linear(120, 10)
     #3rd net
     self.conv3_1 = nn.Conv2d(1, 4, kernel_size=(5, 5), stride=(3, 3), padding=(2, 2)) #22 * 22
     self.conv3_2 = nn.Conv2d(4, 8, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) #22 * 22 #mp
     self.conv3_3 = nn.Conv2d(8, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) #11 * 11
     self.fc3_1 = nn.Linear(16 * 11 * 11, 64) #dropout = 0.4
     self.fc3_1_drop = nn.Dropout(p=0.4)
     self.fc3_2 = nn.Linear(64, 10)
     def forward(self, x, y, z):
       x = F.relu(self.conv1_1(x))
       x = F.relu(self.pool(self.conv1_2(x)))
       x = x.view(-1, 648) #can also do x.view(-1, 1)
       x = F.relu(self.fc1_1(x))
       x = F.dropout(x, training = self.training)
       x = F.relu(self.fc1_2(x))
       y = F.relu(self.conv2_1(y))
       y = F.relu(self.pool(self.conv2_2_drop(self.conv2_2(y))))
       y = F.relu(self.conv2_3(y))
       y = F.relu(self.pool(self.conv2_4_drop(self.conv2_4(y))))
       y = y.view(-1, 256 * 24)
       y = F.relu(self.fc2_1_drop(self.fc2_1(y)))
       y = F.relu(self.fc2_2(y))
       z = F.relu(self.conv3_1(z))
       z = F.relu(self.pool(self.conv3_2(z)))
       z = F.relu(self.conv3_3(z))
       z = z.view(-1, 16 * 121)
       z = F.relu(self.fc3_1_drop(self.fc3_1(z)))
       z = F.relu(self.fc3_2(z))
       x = torch.cat((x, y, z))
       return F.sigmoid(x)

モデル

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 
model = Net().to(device)
if use_gpu: 
   model.cuda()

学習

summary(model, (1, 64, 64), (1, 64, 64), (1, 64, 64))
print("train images: {}".format(len(dataset_train))) 
print("test images: {}".format(len(dataset_test))) 
print("epoch: {}".format(epochs)) 
print("batch size: {}".format(batchsize)) 
for epoch in range(epoch_start, epochs+1):
     train(model, train_loader,epoch)
     test(model, test_loader,epoch)
     save_checkpoint(checkout_dir, epoch, model)
evaluation(checkout_dir,epochs,model,test_loader)

Epoch: 30 Loss:2.302717797115409 |Acc:9.945421467556095 (492/4947) 
          Loss:2.302444501565053 | Acc:10.49233252623083 (130/1239) 
Checkpoint saved to ./checkout/model_epoch_30.pkl 
Checkpoint loaded to ./checkout/model_epoch_30.pkl 
0.10492332526230831

うーん…うまく学習ができませんでした。画像の数が少ないのが結構響いていそうです。1600枚くらいしか学習に回せず、各クラス160枚程度…厳しいですね。

もっといい結果出せたらまた出したいと思います。

最後まで読んでいただきありがとうございました。よろしければこの記事をシェアしていただけると励みになります。よろしくお願いします

スポンサーリンク
レクタングル広告(大)
レクタングル広告(大)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
レクタングル広告(大)