概要
個人的な備忘録を兼ねたPyTorchの基本的な解説とまとめになります.
回帰問題をニューラルネットワークで扱う特徴の一つは、モデルを非線形で捉えることができる点にあると言えます。つまり、データの関係を複雑な形で表現できる点です。前回の回帰分析では直線的な関係しか扱いませんでしたが、ニューラルネットワークなら曲がった関係や複雑なパターンも学習できます。非線形な構造のネットワークモデルを作成する方法を簡単にまとめたいと思います。
方針
- できるだけ同じコード進行
- できるだけ簡潔(細かい内容は割愛)
- 特徴量などの部分,あえて数値で記入(どのように変わるかがわかりやすい)
演習用のファイル(Githubからダウンロード)
1. 普遍性定理(全結合層と活性化関数)
ニューラルネットワークの登場する全結合層を単純に2つ重ねても線形のままです。どうやって非線形化するのか?それが活性化関数の1つの役割です。全結合層の出力値に活性化関数を作用させ、変換された値を全結合層の入力とすることで様々な関数を近似表現することができます。この「全結合層→活性化関数→全結合層→...」のように全結合層と活性化関数を交互に利用することで様々な関数を近似できるという定理は普遍性定理(Universal Approximation Theorem) と呼ばれていて、いくつかの数学的な証明があります。
次の3編が有名所かと思います。
-
Cybenko (1989) "Approximation by Superpositions of a Sigmoidal Fuction", Mathematics of Controle, Signals, ans Systems
-
Hornik (1991) "Approximation capabilities of multilayer feedforward networks",
Neural Networks -
Moshe Leshno, Vladimir Ya. Lin, Allan Pinkus, Shimon Schocken (1993) "Multilayer feedforward networks with a nonpolynomial activation function can approximate any function", Neural Networks
理論上、全結合層と活性化関数のシグモイド関数やRelu関数を使うことで様々な関数を表現できることが普遍性定理によって保証されています。今回は下図のような全結合層とRelu関数を利用した非線形のネットワークモデルを構築してみたいと思います1。
2. コードと解説
ネットワークを定義する部分以外は前回と同一、2.2のネットワークを定義する部分も大部分は前回と類似しています。
2.1 データの読み込みとtorchテンソルへの変換
numpyでcsvファイルを読み込んでtorchテンソルに変換します。前回と同じなので説明は割愛します。
import numpy as np # データの読み込みに利用
import torch # PyTorchのライブラリ
import torch.nn as nn # ネットワーク層のライブラリ
import torch.nn.functional as F # PyTorchの関数のライブラリ(新規に追加)
filename = "vending_machine.csv"
x = np.loadtxt(filename, delimiter=",", skiprows=1, usecols=(0,1,2,3))
t = np.loadtxt(filename, delimiter=",", skiprows=1, usecols=(4))
x = torch.FloatTensor(x)
t = torch.FloatTensor(t).view(-1,1)
2.2 ネットワークモデルの定義と作成
下図のように全結合層とRelu関数を利用したネットワークのモデルを作成します。
具体的には、month、temp、pop、holidayの4つの特徴量を入力して、最終的にsalesを予測する関数
$$sales = model(month, temp, pop, holiday)$$
として表現されます。modelは、FC1、FC2という全結合層、Relu関数の3種類の関数の合成で表現されます。上図の流れをコードで表現するには、ネットワークのクラスの__init__()にFC1とFC2の定義を、forward()関数に「fc1 → relu → fc2」の流れを記述することになります2。
# ネットワークの定義
class DNN(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(4,10) # 4個から特徴量10個
self.fc2 = nn.Linear(10,1) # 10個の特徴量から1個(売上)
# forward部分が実際の流れ。この例だと、FC1 -> relu -> FC2
def forward(self, x):
h = self.fc1(x) # 全結合層1つ目
h = F.relu(h) # Relu関数
y = self.fc2(h) # 全結合層2つ目
return y
# モデルの作成
model = DNN()
ネットワークの定義
- 全結合層の場合は、
nn.Linear(入力の特徴量数,出力の特徴量数)
という形になります。 - fc1に入力される特徴量は(月,気温,人口比率,休日フラグ)の4種類。出力の特徴量の数(次元)は自由に選択できます。今回は10種類にしてみました。 コードではnn.Linear(4, 10) となります。
- fc2に入力される特徴量は10種類、出力は売上の1種類ですから、 コードではnn.Linear(10, 1) となります。
- fc1のあとに、Relu関数を通すので、
F.relu(self.fc1(x))
という流れになります。
2.3 誤差関数と誤差最小化の手法の選択と変数更新のループ
この部分も前回と同一になります。説明は割愛します。
criterion = nn.MSELoss() # 損失関数を記述
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # Adamを使う場合
LOOP = 5000 # LOOP : 学習回数
for epoch in range(LOOP):
optimizer.zero_grad() # 勾配初期化PyTorchの約束事項
y = model(x) # ネットワークモデルによる予測
loss = criterion(y, t) # 誤差計算PyTorchの約束事項
print(epoch,"\tloss:", loss.item()) # lossの値 (loss.item()) を表示
loss.backward() # backward(微分する部分)
optimizer.step() # パラメータ更新PyTorchの約束事項
2.4 検証や予測など
modelを使って予測するには、month、temp、population、holidayの4つを決める必要があります。10月、気温25度、人口比率0.9、平日の場合の売上予測は次のように求めることができます。
x_test = [10, 25, 0.9, 0]
x_test = torch.FloatTensor(x_test)
model(x_test)
# tensor([556.3793], grad_fn=<ViewBackward0>)
次回
ニューラルネットワークのHello Worldであるアヤメの分類問題に代表される分類問題の予定です。
もはや解説不要だと思いますが
注
-
活性化関数については、PyTorchドキュメントtorch.nn.functionalに使い方、PyTorchドキュメントのtorch.nnにはグラフの形も解説されています。 ↩
-
活性化関数も__init__()にnn.ReLU()としてモジュール形式で書く方法もあります。詳細はPyTorchのドキュメントを参照してください。 ↩