5
4

More than 3 years have passed since last update.

PyTorchでニューラルネットワークを使ってMNISTデータを学習させる

Last updated at Posted at 2020-02-12

はじめに

こんにちは、川島です!
ずっと、Qiitaで投稿しようと思っていて、やっと実行できるようになりました!(^^)

今日はPyTorchでニューラルネットワークを使ってMNIST手書き数字のデータを学習させるプログラムの解説をしていきたいと思います。

このトピック自体は、目新しいわけではありませんが
ただ、今までの記事の中で、なかなか、細部まで丁寧に説明する物が少なくて

この記事では、なるべくうるさいぐらいに詳細に説明をしていきたいと考えて、書いてみたいと思います。

とは言っても、ほとんどの説明が、コメントに書いてあります。辛抱強く、コメントを一行ずつ、読みましょう!

ソースコードをみながら始めよう!

必要なモジュールなどをimportする

# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------

import torch

print(torch.__version__)

import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torch.autograd import Variable
import torch.nn as nn

import torch.optim as optimizer

学習の回数、バッチサイズなどの設定

勉強のために、ここを色々変えながら、プログラムを実行してみてください。

# -----------------------------------------------------------------------------
# ミニバッチのバッチサイズ
BATCH_SIZE = 4
# 最大学習回数
MAX_EPOCH = 2
# 進捗出力するバッチ数
PROGRESS_SHOW_PER_BATCH_COUNT=1000

MLP(マルチレイヤーパーセプトロン)クラスの定義

ここでは、3層構造のニューラルネットワークを定義します。注意すべきは、これは畳み込みニューラルネットワークではないことです。

# -----------------------------------------------------------------------------
#  マルチレイヤーパーセプトロンクラスの定義
class MLP(nn.Module):
    def __init__(self):
        '''
        層ごとに定義する、例えば、活性化関数などは、次forward()で定義する
        '''
        super().__init__()
        # 入力層
        self.layer1 = nn.Linear(28 * 28, 100)
        # 中間層(隠れ層)
        self.layer2 = nn.Linear(100, 50)
        # 出力層
        self.layer3 = nn.Linear(50, 10)

    def forward(self, input_data):
        '''
        ネットワークの(順伝播)の定義(つなげる)
        '''
        # input_dataをフォーマット変換します
        # -1は自動的に変換する
        input_data = input_data.view(-1, 28 * 28)
        # 前の層からきたinput_dataをlayer1に渡します
        input_data = self.layer1(input_data)
        # 前の層からきたinput_dataをlayer2に渡します
        input_data = self.layer2(input_data)
        # 前の層からきたinput_dataをlayer3に渡します
        input_data = self.layer3(input_data)
        return input_data


# 学習用モデルのインスタンスを生成します
model = MLP()

学習データの用意

# -----------------------------------------------------------------------------
# 学習データの準備をします
#
print('---------- 学習のデータの準備 ----------')
data_folder = '~/data'
transform = transforms.Compose([
    # データの型をTensorに変換する
    transforms.ToTensor()
])

# 学習データ
train_data_with_labels = MNIST(
    data_folder, train=True, download=True, transform=transform)

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=transform)
test_data_loader = DataLoader(
    test_data_with_labels, batch_size=BATCH_SIZE, shuffle=True)

学習するための準備

学習するための、損失関数、学習率などの指定をします。
この辺り、ニューラルネットワークとは何か、という理解が前提となります。
前提知識をもう一回確認したい方は、私のnoteをご参照ください。

# -----------------------------------------------------------------------------
# 学習の用意をします
# 損失関数は交差エントロピー誤差関数を使います
lossResult = nn.CrossEntropyLoss()
# SGD
optimizer = optimizer.SGD(model.parameters(), lr=0.01)

print('---------- 学習開始します ----------')
# 学習開始します
for epoch in range(MAX_EPOCH):
    # 誤差の初期設定
    total_loss = 0.0
    # enumerateはindexをデータを分解してくれます
    for i, data in enumerate(train_data_loader):

        # dataから学習対象データと教師ラベルデータのバッチを取り出します
        train_data, teacher_labels = data

        # 入力をtorch.autograd.Variableに変換します
        train_data, teacher_labels = Variable(train_data), Variable(
            teacher_labels)

        # 計算された勾配情報を削除(リセット、クリア)します
        optimizer.zero_grad()

        # モデルに学習データを与えて予測をします
        outputs = model(train_data)

        # lossとwによる微分計算します
        loss = lossResult(outputs, teacher_labels)
        # 勾配を計算します
        loss.backward()

        # 最適化のステップを一回実行します(パラメーターを更新します、たくさんのoptimizerの共通の処理)
        optimizer.step()

        # loss.item()はlossを数値に変換します、誤差を累計します
        total_loss += loss.item()

        # PROGRESS_SHOW_PER_BATCH_COUNTミニバッチずつ、進捗を表示します
        if i % PROGRESS_SHOW_PER_BATCH_COUNT == PROGRESS_SHOW_PER_BATCH_COUNT-1:
            print('i=',i)
            print(
                '学習進捗:[EPOCH:%d, %dバッチx%d -> %d枚学習完了] 学習誤差(loss): %.3f' % (epoch + 1, i + 1, BATCH_SIZE, (i + 1) * BATCH_SIZE,
                                                                     total_loss / PROGRESS_SHOW_PER_BATCH_COUNT))
            # 計算用誤差をリセットします
            total_loss = 0.0

print('学習終了')

検証

学習が終わり、学習済みモデルをゲットしたら、次は、その学習済みモデルを実際に「使って」推論してみて、この学習済みモデルがいかに、正確かを検証します。

ほぼ、必須のステップだと言ってもいいと思います。


# -----------------------------------------------------------------------------
# 検証:全ての検証画像データに対しての正解率を計算します
print('---------- 全ての検証画像データに対しての正解率を計算します ----------')
# 全体のデータ数(計測対象数)
total = 0
# 正解カウンター
count_when_correct = 0

#
for data in test_data_loader:
    # 検証データローダーからデータを取り出した上、アンパックします
    test_data, teacher_labels = data
    # テストデータを変換した上、モデルに渡して、判定してもらいます
    results = model(Variable(test_data))
    # 予測を取り出します
    print(torch.max(results, 1))
    # 結果:
    # torch.return_types.max(
    # values=tensor([1.2185, 5.8557, 2.8262, 4.7874], grad_fn=<MaxBackward0>),
    # indices=tensor([2, 8, 8, 8]))
    # torch.max(tensor, axis)
    #  values  indices
    #     ↓        ↓
    #     _    predicted
    _, predicted = torch.max(results.data, 1)
    # 一つずつ、推論結果配列の最大値(最も確信しているラベル)取り出します
    # 使わないものはよく、アンダーバーにします。(使い捨て)
    # ここでは、axis=1なので、行ごとに最大値を取り出すという意味になります
    print('_', _)
    # 結果:それぞれの最大値そのものが入っています
    # tensor([1.6123, 5.6203, 3.0886, 3.8317], grad_fn=<MaxBackward0>)
    print('predicted', predicted)
    # 結果:「最大値は何番目なのか」(index location)が入っています
    # tensor([3, 9, 1, 0])
    #
    # print('teacher_labels',teacher_labels)
    # 結果:
    # teacher_labels
    # tensor([3, 5, 3, 8])
    # teacher_labels
    # tensor([3, 5, 1, 7])
    # ...
    # ...
    #
    # print('teacher_labels.size(0)',teacher_labels.size(0))
    # teacher_labels.size(0) 4
    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)))

まとめ

いかがですか?これで、ある程度、PyTorchでニューラルネットワークを使って、画像認識させることのイメージが着いたでしょうか?
ほぼ、全部、「コメント読め」的な感じになっちゃいましたが、いいねが多ければ、まったこのような記事を投稿しようと思います。
よろしくお願い致します。

ソースコードはこちら

また、コメントを更新する場合もありますので、最新のコードは、GitHubで管理しますので、よかったら、フォローか、スターか、ブックマークをしてください。m(.)m

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4