PyThorchで始めるAI入門

最近AIが流行ってますよね?
だけどどうやって開発したらいいかわからない、フレームワークが多くてどれを学んだらいいかわからないって人が多いのかなと思います。
今回は機械学習のフレームワークPyThorchを用いて、手書きデータの認識をしてみたいと思います。

1. PyThorchって何?

facebookが開発した機械学習のためのフレームワークで、2016年にOSSで公開されたものです。
柔軟性が高く、近年人気が上昇してます。
下記図を見るとわかる通り、現在伸び率がとても高い!

2.PyThorchでMNIST画像認識

今回はgoogleのColaboratoryを利用して画像認識をしていきます。
まずはgoogleで検索(※ googleアカウントが必要)
詳しい使い方:https://www.slideshare.net/katsuhiromorishita/google-colaboratory-177618719


google – Colaboratoryを検索
ノートブックの新規作成
編集からノートブックの設定を選択し、GPUに変更

ライブラリをインポート

import matplotlib.pyplot as plt
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST

MNISTのデータセットをダウンロード
(MNISTとは手書き数字画像のデータセットで、ディープラーニングを勉強し始めるのにちょうどいいデータセットです。)

# データセットのダウンロード

data_folder = '~/data'
BATCH_SIZE = 8

# data_folderに指定したpathにTensor型のデータをダウンロードする
mnist_data = MNIST(data_folder, train=True, download=True, transform=transforms.ToTensor())

# データの読込
data_loader = DataLoader(mnist_data, batch_size=BATCH_SIZE, shuffle=False)

ダウンロードした画像データを確認します。
今回は4番目のデータを確認。

# 画像データの取得
data_iterator = iter(data_loader)
images, labels = data_iterator.next()

# 画像の表示(4番目の画像)
location = 4

# numpyに変換
data = images[location].numpy()

# 画像データを28 × 28のデータに変換
reshaped_data = data.reshape(28, 28)

# 画像を描画
plt.imshow(reshaped_data, cmap='inferno', interpolation='bicubic')
plt.show()
print('ラベル:', labels[location])

取得した画像データを学習用のデータと検証用のデータに分割します。
MNISTのメソッドで引数trainの値によって学習用と検証用でロードします。

# 学習データ
train_data_with_labels = MNIST(data_folder, train=True, download=True, transform=transforms.ToTensor())
train_data_loader = DataLoader(train_data_with_labels, batch_size=BATCH_SIZE, shuffle=True)

# 検証データ
test_data_with_labels = MNIST(data_folder, train=False, download=True, transform=transforms.ToTensor())
test_data_loader = DataLoader(test_data_with_labels, batch_size=BATCH_SIZE, shuffle=True)

学習データと検証データの概要を確認してみます。

# 学習データと検証データのデータセットを確認
print(train_data_loader.dataset)
print(test_data_loader.dataset)
output:
// 学習データ
Dataset MNIST
    Number of datapoints: 60000
    Root location: /root/data
    Split: Train
    StandardTransform
Transform: ToTensor()

// 検証データ
Dataset MNIST
    Number of datapoints: 10000
    Root location: /root/data
    Split: Test
    StandardTransform
Transform: ToTensor()

出力結果を確認すると学習データは60000枚、テストデータは10000枚のデータであることが確認できます。
また、データタイプはTensor型であることがわかります。

次にニューラルネットワークモデルの定義をします。
3層のモデルを作成しました。

# ニューラルネットワークの定義
from torch.autograd import Variable # 画像データを変換するためのモジュール
import torch.nn as nn # モデルの親クラス

# 親クラスのnn.Moduleを継承してモデルを作成します
class MLP(nn.Module):
  def __init__(self):
    super().__init__()

    # 入力層(input)
    self.layer1 = nn.Linear(28 * 28, 100)

    # 中間層
    self.layer2 = nn.Linear(100, 50)

    # 出力層(output)
    self.layer3 = nn.Linear(50, 10)

  def forward(self, input_data):
    input_data = input_data.view(-1, 28 * 28)
    input_data = self.layer1(input_data)
    input_data = self.layer2(input_data)
    input_data = self.layer3(input_data)
    return input_data

モデルのインスタンスを生成します。

# モデルの生成
model = MLP()

最適化手法の選択をします。
SGDを利用します。

import torch.optim as optimizer

# クロスエントロピー
lossResult = nn.CrossEntropyLoss()

# SGD(Stochastic Fradient Descent)
optimizer = optimizer.SGD(model.parameters(), lr=0.01)

学習データをモデルに学習させます。
学習回数は4回で設定します。

# 学習

# 最大学習回数
MAX_EPOCH = 4

for epoch in range(MAX_EPOCH):
  total_loss = 0.0
  for i, data in enumerate(train_data_loader):

    # dataから学習対象データと教師ラベルデータを取得
    train_data, teacher_labels = data

    # データの変換
    train_data, teacher_labels = Variable(train_data), Variable(teacher_labels)

    # 勾配情報を削除
    optimizer.zero_grad()

    outputs = model(train_data)

    loss = lossResult(outputs, teacher_labels)
    loss.backward()

    # 勾配の更新
    optimizer.step()

    # 誤差を累計
    total_loss += loss.data

    if i % 2000 == 1999:
      print('学習進捗:[%d, %d] 学習誤差(loss): %.3f' % (epoch + 1, i + 1, total_loss / 2000))
      total_loss = 0.0

print('学習終了')
output:
学習進捗:[1, 2000] 学習誤差(loss): 0.853
学習進捗:[1, 4000] 学習誤差(loss): 0.382
学習進捗:[1, 6000] 学習誤差(loss): 0.350
学習進捗:[2, 2000] 学習誤差(loss): 0.316
学習進捗:[2, 4000] 学習誤差(loss): 0.308
学習進捗:[2, 6000] 学習誤差(loss): 0.308
学習進捗:[3, 2000] 学習誤差(loss): 0.289
学習進捗:[3, 4000] 学習誤差(loss): 0.303
学習進捗:[3, 6000] 学習誤差(loss): 0.293
学習進捗:[4, 2000] 学習誤差(loss): 0.279
学習進捗:[4, 4000] 学習誤差(loss): 0.300
学習進捗:[4, 6000] 学習誤差(loss): 0.288
学習終了

テストデータでモデルの精度を確かめてみます。

# 検証
import torch

# トータル
total = 0

# 正解カウンター
count_when_correct = 0

for data in test_data_loader:
  test_data, teacher_labels = data

  # テスト結果
  results = model(Variable(test_data))

  _, predicted = torch.max(results.data, 1)

  total += teacher_labels.size(0)
  count_when_correct += (predicted == teacher_labels).sum()

print('count_when_correct:%d' % (count_when_correct))
print('total:%d' % (total))

print('正解率:%d / %d = %f' % (count_when_correct, total, int(count_when_correct) / int(total)))
output:
count_when_correct:9197
total:10000
正解率:9197 / 10000 = 0.919700

正解率が91%でしたのでそこそこ精度が高いことが確認できました。
実際に画像から判定してみます。
テストデータの4番目のデータを検証してみます。

実際に画像から判定してみます。
テストデータの4番目のデータを検証してみます。

# データの取得
test_iterator = iter(test_data_loader)
test_data, teacher_labels = test_iterator.next()

# データを検証
results = model(Variable(test_data))

_, predicted_label = torch.max(results.data, 1)

# 4番目のデータの検証結果と画像の表示
location = 4
plt.imshow(test_data[location].numpy().reshape(28, 28), cmap='inferno', interpolation='bicubic')
print('ラベル:', predicted_label[location])

まとめ

どうでしたか?
結構簡単に画像認識の実装ができたましたね!
応用するとしたら、モデルの層を増やしたり、学習回数を増やしたり、画像の枚数を増やしたり、最適化手法を変えてみたりしたら精度が向上するかもしれません。
フレームワークを利用した実装は簡単ではありますが中身がブラックボックスになってしまうデメリットがあります、学習のための計算方法が知りたい方は下記の本がとても参考になるのでおすすめです。

参考

  • https://www.codexa.net/pytorch-python/
  • https://www.slideshare.net/katsuhiromorishita/google-colaboratory-177618719