【深層学習×Python】PyTorchで始めるディープラーニング実践入門|プログラムの書き方・テンプレートの解説

こんにちは、Kosei(@kay_diacc2)です!

PyTorchのライブラリを用いて0からディープラーニングモデルを構築してみたいという方向けの記事です。PyTorchでのモデル構築に際して、プログラミングの組み立て方を分かりやすいテンプレートを用いて解説します。実際に簡易なニューラルネットワークモデルを構築できるようになるまで本記事でサポートします!

目次

PyTorchとは

PyTorch(パイトーチ)とは、Meta AI(旧Facebook AI Research)が開発したPythonベースの深層学習(ディープラーニング)のフレームワークです。

2017年にオープンソースとして公開された比較的新しいライブラリであるにも関わらず、近年ではGoogleのTensorFlow、Kerasと匹敵するほどの人気を集めています。

PyTorchの特徴として「Define by Run」という機械学習計算処理を行いながらモデルを構築することがあります。加えて、データ前処理・ニューラルネットワークの定義・モデル学習などの各処理パーツをクラス(Class)として定義し、複数の処理パーツを組み合わせることで複雑なAIモデルを表現できることから、AIモデル構築の柔軟性という観点で非常に優秀なフレームワークであると言われています。

PyTorchの主な特徴をまとめると以下の通りです。

  • Pythonベースの深層学習フレームワーク
  • TensoFlowと同様の人気を誇る
  • 計算処理能力に優れる
  • AIモデル構築の柔軟性に優れる
  • AIモデル構築時の途中プロセスを動的なグラフで表現できる(モニタリング性に優れる)
  • 近年では特に自然言語処理(NLP)の分野で広く利用されている

後述では、PyTorchを用いた深層学習プログラミングについて実践的に解説していきます。

合わせて読みたい

【実践】PyTorchで始めるディープラーニング入門

PyTorchを用いたプログラミングの流れの理解するために、全体像を示します。下記テンプレートを見てみましょう。一般的にどんなモデルを構築するにしても、このテンプレートに従って記述できるはずです。今後使い回しも考慮し、どこにどのようなプログラムを配置すべきか要点を押さえながら見ていきましょう。

Pythonプログラムのテンプレート

""" ライブラリ読込 """
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np

""" GPU設定 """
# GPUが利用できる場合はGPUを利用し、利用不可の場合はCPUを利用するための記述
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


""" (1)データの準備  """
# XXXX データをここに記述 XXXXX
# XXXX データをここに記述 XXXXX
# XXXX データをここに記述 XXXXX
# XXXX データをここに記述 XXXXX



""" (2)モデルクラスの登録 """
# nnクラスの関数(初期設定)を下記に記述
class SampleModel(nn.Module):
    
    # ユニット・層の数・活性化関数等ニューラルネットワークの模型となるものを下記に記述
    def __init__(self):
        super(SampleModel, self).__init__()
        # XXXXXXXXXXX
        # XXXXXXXXXXX
        # XXXXXXXXXXX
    
    # 順方向の計算処理の流れを下記に記述
    def forward(self,・・・):
        # XXXXXXXXXXX
        # XXXXXXXXXXX
        # XXXXXXXXXXX
        
  
      
""" (3)モデルとパラメータ探索アルゴリズムの設定 """
model     = SampleModel().to(device)  # モデルのインスタンス
optimizer = "XXXX"                    # パラメータ探索アルゴリズム
criterion = "XXXX"                    # 損失関数



""" (4)モデル学習 """
repeat = "XXX"      # 学習回数を設定(整数)

for epoch in range(repeat):  
    ex_var = "XXX"  # 説明変数を作成
    target = "XXX"  # 目的変数を作成
    
    # モデルのforward関数を用いた順伝播の予測(モデルの出力値算出)
    output = model(ex_var)  
    # 上記出力値(output)と教師データ(target)を損失関数に渡し、損失関数を計算
    loss = criterion(output, target)
    # 勾配を初期化
    optimizer.zero_grad()
    # 損失関数の値から勾配を求め誤差逆伝播による学習を実行
    loss.backward()
    # 学習結果に基づきパラメータを更新
    optimizer.step()


""" (5) モデルを保存/呼び出し """
torch.save(model.state_dict(), "XXX.model")     # モデル保存する場合
model.load_state_dict(torch.load("XXX.model"))  # モデルを呼び出す場合


""" (6)モデルの性能評価 """
model.eval()
with torch.no_grad():
    # 性能評価
    # XXXXXXXXX
    # XXXXXXXXX

テンプレートには、下記のようなプログラム群が配置されているのが特徴です。

スクロールできます
プログラム記述の流れ作業内容
(1)データの準備学習データ・テストデータ
(2)モデルクラスの登録ニューラルネットワークのモデル情報
(ユニット数, 層の数, 入力/出力次元, 活性化関数等)
(3)モデルとパラメータ探索アルゴリズムの設定モデル・損失関数・最適化アルゴリズム
(4)モデル学習誤差逆伝播法に基づくモデル学習
(5)モデルの保存・呼び出しモデルの保存・呼び出しが必要な場合記述
(6)モデルの性能評価テストデータでモデルの性能評価を実施

【実践】テンプレートを用いて簡易なニューラルネットワークモデルを構築

それでは実際に手を動かしながらPyTorchでニューラルネットワークを構築していきましょう!下記の手順に従い、プログラムを仕上げていきます。

  • データセットの説明
  • 事前準備(ライブラリの読み込み・GPU設定)
  • データの準備
  • モデルクラスの登録
  • モデルとパラメータ探索アルゴリズムの設定
  • モデル学習
  • モデルの保存・呼び出し
  • モデルの性能評価

データセットの説明

データセットには、機械学習のサンプルデータとして有名なIris(アヤメ)データセットを活用します。3種類のアヤメ(Iris Setosa, Iris Versicolor, Iris Virginica)があり、それぞれ50サンプルずつ(合計150サンプル)用意されているデータです。このアヤメの名前を目的変数として利用します。また、説明変数にはアヤメの計測値である萼片(sepals)と花びら(petals)の長さと幅の4つを利用します。

事前準備(ライブラリの読み込み・GPU設定)

モデル構築に際して必要なライブラリとGPU利用のための設定を行います。GPUは使わずともモデルは構築できますが、学習時間の大幅短縮が見込めるため、深層学習モデル作成に際しては必須とも言えます。PyTorchでGPUを利用するには、お手元のPCにCUDAをインストールいただく必要があります。(後述で紹介するプログラムはCPUでも利用できる形式で記述されています。一旦GPU無しに動かしてみたい方もこのままお進み下さい)

""" ライブラリ読込 """
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np

""" GPU設定 """
# GPUが利用できる場合はGPUを利用し、利用不可の場合はCPUを利用
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

データの準備

前述したアヤメのデータセットを準備します。目的変数と説明変数を取り扱うために、下記のコードを実行しましょう。

from sklearn import datasets
from sklearn.model_selection import train_test_split

# データロード
iris = datasets.load_iris()

# 説明変数 "Sepal Length", "Sepal Width", "Petal Length", "Petal Width"
X = iris.data.astype(np.float32)

# 目的変数 "Species"
Y = iris.target.astype(np.int64)

# 学習データ&テストデータ分割
X_train, X_test,  Y_train, Y_test = train_test_split(X,Y,test_size=0.3, random_state=3)

# テンソル化
X_train, X_test, Y_train, Y_test = torch.from_numpy(X_train).to(device), torch.from_numpy(X_test).to(device), torch.from_numpy(Y_train).to(device), torch.from_numpy(Y_test).to(device)

説明変数および目的変数の次元は今回下記のようになっています。

# 学習データの説明変数
print(X_train.shape)

# 出力結果
# torch.Size([105, 4])
# 学習データの目的変数
print(Y_train.shape)

# 出力結果
# torch.Size([105])

PyTorchでデータを準備する場合、Datasetというクラスを用いてデータを準備し、DataLoaderというクラスを用いてそのデータを呼び出すのが一般的です。今回はモデル構築までの流れを主体的に解説するため、Dataset、DataLoaderクラスの利用方法は割愛しています。

モデルクラスの登録

今回構築するニューラルネットワークは上図のようなものです。入力次元は4次元であるため、入力層にはユニット4つとバイアスを設置します。中間層には6つのユニットを設定し、出力層には目的変数の出力値数(3つ)分のユニットを設置します。

""" (2)モデルクラスの登録 """
# nnクラスの関数(初期設定)を下記に記述
class IrisModel(nn.Module):
    
    def __init__(self):
        super(IrisModel, self).__init__()
        
        # モジュールリストの中にモデル情報を記述
        self.model_info = nn.ModuleList([
             nn.Linear(4,6),   # (1)入力層:今回入力は4次元として設定・出力は6次元と任意に設定
             nn.Sigmoid(),     # (2)活性化関数(シグモイド)
             nn.Linear(6,3),   # (3)出力層:出力層のユニットは出力値分
            ])
    
    # 順方向の計算を記述
    def forward(self,x):
        for i in range(len(self.model_info)):
            x = self.model_info[i](x)
        return x

モデルとパラメータ探索アルゴリズムの設定

前述で登録したモデルクラスのインスタンスを作成後、パラメータ探索アルゴリズムと損失関数を指定します。

""" (3)モデルとパラメータ探索アルゴリズムの設定 """

model     = IrisModel().to(device)                # モデル
optimizer = optim.SGD(model.parameters(),lr=0.05) # パラメータ探索アルゴリズム(確率的勾配降下法 + 学習率lr=0.05を適用)
criterion = nn.CrossEntropyLoss()                 # 損失関数

今回パラメータ探索アルゴリズムは今回確率的勾配降下法(SDG)を採用しました。他のアルゴリズムを活用したい場合、PyTorch公式ページをご参照下さい。

損失関数は、取り扱う問題が「分類」か「回帰」によって使い分けが必要です。今回は多クラス分類の問題を取り扱うため、Softmax交差エントロピー損失を採用してます。

""" 代表的な損失関数には下記のようなものがあります """

# 二値分類|バイナリ交差エントロピー損失
criterion = nn.BCELoss

# 二値分類|ロジット付きバイナリ交差エントロピー損失
criterion = nn.BCELoss

# 多クラス分類|Softmax交差エントロピー損失
criterion = nn.CrossEntropyLoss()

# 回帰|平均二乗誤差
criterion = nn.MSELoss

# 回帰|平均絶対値誤差
criterion = nn.L1Loss

モデル学習

モデル学習過程では、まず第一に、学習データを用いてモデルが分類(出力)結果を算出します。続いて、その分類結果と学習データの正解ラベルを比較し、誤差を求めます。さらに、誤差が最小化できるようパラメータを更新します。このような過程を繰り返すことでモデル学習精度の向上を目指していきます。実装する場合は下記のように記述しましょう。

""" (4) モデル学習 """

# 学習回数
repeat = 1500

for epoch in range(repeat):    
    ex_var = X_train  # 説明変数を作成
    target = Y_train  # 目的変数を作成
    
    # モデルのforward関数を用いた準伝播の予測(モデルの出力値算出)
    output = model(ex_var)  
    
    # 上記出力値(output)と教師データ(target)を損失関数に渡し、損失関数を計算
    loss = criterion(output, target)
    
    # 勾配を初期化
    optimizer.zero_grad()
    
    # 損失関数の値から勾配を求め誤差逆伝播による学習実行
    loss.backward()
    
    # 学習結果に基づきパラメータを更新
    optimizer.step()

モデル学習(ミニバッチ学習を適用した場合)

上述のモデル学習の書き方の場合、1回の学習学習プロセス毎に全ての学習データを利用(バッチ学習)しました。バッチ学習ではなく、ミニバッチ学習を適用したい場合はnumpyのpermutationメソッドを活用し、下記のように記述します。ミニバッチ学習とは、ランダムに取り出した学習データを使って学習する手法を指します。

""" (4) モデル学習 """
data_size  = len(X_train)           # データのサイズ
mini_batch = int(data_size * 3/4)   # ミニバッチサイズ(全データの3/4を学習に利用)
repeat = 1500                       # エポック数

for epoch in range(repeat):
    
    # permutation = 渡した引数の数値をシャッフル
    dx = np.random.permutation(data_size)
    
    for num in range(0,data_size,mini_batch):
        
        # 説明変数(ミニバッチサイズ)
        ex_var = X_train[dx[num:(num + mini_batch) if (num + mini_batch) < data_size else data_size]]
        
        # 目的変数(ミニバッチサイズ)
        target = Y_train[dx[num:(num + mini_batch) if (num + mini_batch) < data_size else data_size]]
           
        # モデルのforward関数を用いた準伝播の予測→出力値算出
        output = model(ex_var)  

        # 上記出力値(output)と教師データ(target)を損失関数に渡し、損失関数を計算
        loss = criterion(output, target)

        # 勾配を初期化
        optimizer.zero_grad()

        # 損失関数の値から勾配を求め誤差逆伝播による学習実行
        loss.backward()

        # 学習結果に基づきパラメータを更新
        optimizer.step()

学習データ量や形態によって、モデルへのデータ投入方法の最適手段が変わります。今回紹介したバッチ学習とミニバッチ学習の使い分け等詳しく知りたい方はこちらの記事もご覧下さい。

モデルの保存・呼び出し

学習済みモデルを保存したり、再度呼び出したい場合は下記のように記載します。

""" (5)モデルの結果を出力 """

torch.save(model.state_dict(), "sample.model")    # モデル保存する場合
model.load_state_dict(torch.load("sample.model")) # モデルを呼び出す場合

モデルの性能評価

最後に構築したモデルの性能を評価してみましょう。モデルの評価ではeval()メソッドを用いるのが一般的です。下記のように記述してみましょう。

""" (6) モデルの性能評価 """

# 学習したモデルの評価
model.eval()
with torch.no_grad():
    pred_model  = model(X_test)              # テストデータでモデル推論
    pred_result = torch.argmax(pred_model,1) # 予測値
    
    # 正解率
    print(round(((Y_test == pred_result).sum()/len(pred_result)).item(),3))

モデル性能評価における処理は順方向処理(forward)のみであるため、勾配(grad)を求める必要はありません。そのため、torch.no_grad()というメソッドを追記し、勾配計算を行わないようにしています。このメソッドの記述は必須ではないですが、GPU/CPUの計算負荷を減らすことができるため、書いておいて損はないでしょう。

PyTorchによるニューラルネットワークモデルのプログラム全量

最後に本記事で紹介したプログラムを全て掲載します。

""" ライブラリ読込 """
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split


""" GPU設定 """
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


""" (1) データの準備 """
# データロード
iris = datasets.load_iris()
# 説明変数 "Sepal Length", "Sepal Width", "Petal Length", "Petal Width"
X = iris.data.astype(np.float32)
# 目的変数 "Species"
Y = iris.target.astype(np.int64)
# 学習データ&テストデータ分割
X_train, X_test,  Y_train, Y_test = train_test_split(X,Y,test_size=0.3, random_state=3)
# テンソル化
X_train, X_test, Y_train, Y_test = torch.from_numpy(X_train).to(device), torch.from_numpy(X_test).to(device), torch.from_numpy(Y_train).to(device), torch.from_numpy(Y_test).to(device)


""" (2)モデル情報を記述 """
# nnクラスの関数(初期設定)を下記に記述
class IrisModel(nn.Module):
    
    # ユニット・層の数・活性化関数等ニューラルネットワークの模型となるものを下記に記述
    def __init__(self):
        super(IrisModel, self).__init__()
        self.model_info = nn.ModuleList([
             nn.Linear(4,6),   # 入力層
             nn.Sigmoid(),     # 活性化関数(シグモイド)
             nn.Linear(6,3),   # 出力層
            ])
    
    # 順方向の計算処理の流れを下記に記述
    def forward(self,x):
        for i in range(len(self.model_info)):
            x = self.model_info[i](x)
        return x


""" (3)モデルとパラメータ探索アルゴリズムの設定 """
model     = IrisModel().to(device)                # モデル
optimizer = optim.SGD(model.parameters(),lr=0.05) # パラメータ探索アルゴリズム
criterion = nn.CrossEntropyLoss()                 # 損失関数


""" (4) モデル学習 """
data_size  = len(X_train)           # データのサイズ
mini_batch = int(data_size * 3/4)   # ミニバッチサイズ
repeat = 1500                       # エポック数

for epoch in range(repeat):
    
    # permutation(渡した引数の数値をシャッフル)
    dx = np.random.permutation(data_size)
    
    for num in range(0,data_size,mini_batch):
        # 説明変数(ミニバッチサイズ)
        ex_var = X_train[dx[num:(num + mini_batch) if (num + mini_batch) < data_size else data_size]]
        # 目的変数(ミニバッチサイズ)
        target = Y_train[dx[num:(num + mini_batch) if (num + mini_batch) < data_size else data_size]] 
        # モデルのforward関数を用いた準伝播の予測→出力値算出
        output = model(ex_var)  
        # 上記出力値(output)と教師データ(target)を損失関数に渡し、損失関数を計算
        loss = criterion(output, target)
        # 勾配を初期化
        optimizer.zero_grad()
        # 損失関数の値から勾配を求め誤差逆伝播による学習実行
        loss.backward()
        # 学習結果に基づきパラメータを更新
        optimizer.step()


""" (5)モデルの結果を出力 """
# torch.save(model.state_dict(), "iris.model")    # モデル保存する場合
# model.load_state_dict(torch.load("iris.model")) # モデルを呼び出す場合


""" (6) モデルの性能評価 """
model.eval()
with torch.no_grad():
    pred_model  = model(X_test)
    pred_result = torch.argmax(pred_model,1) #予測値
    print("正解率: " + str(round(((Y_test == pred_result).sum()/len(pred_result)).item(),3))+"[%]")

AI・機械学習まとめ

最後までご覧いただきありがとうございました。当サイトでは機械学習・深層学習における理論やPythonを用いた実装方法の解説記事を多数取り扱っております。

【初学者向け】データサイエンス・人工知能(AI)開発のおすすめ学習方法も解説してます。

ディープラーニングを学ぶ上でおすすめの教材はこちらで紹介しています。

最後に

この記事が気に入ったら
フォローしてね!

本記事をシェア!
URLをコピーする
URLをコピーしました!
目次
閉じる