ニューラルネットワークで論理演算子 OR を学習する
はじめに
業務にて深層強化学習の利用を目指す。まずは簡単な例からということで、PyTorchを用いてニューラルネットワークで論理演算子 OR を学習するプログラムを作成する。
論理演算子 OR のモデル
0または1を持つ2つの入力x1
, x2
の少なくとも一方が1である場合に1を、そうでない場合に0を返すモデルを作成する。
(x1, x2) → model → 0 または1
具体的には、
(0, 0) → model → 0
(1, 0) → model → 1
(0, 1) → model → 1
(1, 1) → model → 1
必要なモジュールのインポート
import numpy import torch import torch.nn as nn import torch.nn.functional as F from matplotlib import pyplot as plt
ニューラルネットワークモデルの定義
入力2つ、出力は2つとしている。中間層は1層とし、そのノード数は3つとしている。出力は答えが0のときは出力の1つめが大きくなるように、答えが1のときは出力の2つめが大きくなるように、というように分類問題として扱う(後に示すとおり分類問題に合わせた損失関数を選ぶ必要がある)。中間層の活性化関数はReLUとし、出力層はl2の計算値をそのまま出力している。
class Model(nn.Module): def __init__(self): super(Model, self).__init__() self.l1 = nn.Linear(2, 3) self.l2 = nn.Linear(3, 2) def forward(self, x): x = F.relu(self.l1(x)) x = self.l2(x) return x
データの準備
torch.tensorとしてデータを準備するが、入力データx_train
はtorch.float型で、分類問題とするため出力データy_train
は整数型(long)にする必要がある。学習データx_train
とy_train
は下記のパターンに対応する。
(0, 0) → model → 0
(1, 0) → model → 1
(0, 1) → model → 1
(1, 1) → model → 1
x_train = torch.tensor([[0,0],[0,1],[1,0],[1,1]], dtype=torch.float) y_train = torch.LongTensor(numpy.array([0,1,1,1]))
損失関数、最適化関数の設定
損失関数と最適化関数を適宜選ぶ必要がある。最適化関数についてはlearning_rateを設定する必要がある。
model = Model() #モデル登録 criterion = nn.CrossEntropyLoss() #損失関数 (クロスエントロピー誤差) learning_rate = 1e-1 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) #最適化関数 (Adam)
学習
optimizer.zero_grad()
をして、loss = criterion(y_model, y_train)
でモデル計算値と答えの誤差を求め、
loss.backward()
して、optimizer.step()
を繰り返せばよい。
loss_log = [] #学習状況のプロット用 epoch = 100 for t in range(epoch): optimizer.zero_grad() #計算された勾配を0にリセットする y_model = model(x_train) #モデルの計算値算出 loss = criterion(y_model, y_train) #モデル計算値と答えを比較し誤差を求める loss.backward() #誤差からバックプロパゲーション optimizer.step() #バックプロパゲーションから重み更新 print(t, loss.item()) #各エポックで誤差を表示 loss_log.append(loss.item()) #学習状況の記録
学習結果の確認
## 学習状況のプロット plt.plot(loss_log) plt.xlabel('epoch') plt.ylabel('loss') plt.yscale('log') plt.show() ##学習したモデルの正答率を出す outputs = model(x_train) # 2つのnodeの出力のtensorが得られる。大きい方がモデルの出した答え _, predicted = torch.max(outputs.data, 1) # modelの答えを0か1かのtensorに変換する correct = (y_train == predicted).sum().item() # 正答数を求める。 print("正答率: {} %".format(correct/len(predicted)*100.0))
結果
学習状況
変化が小さくなるepoch数60回あたりで学習が十分できていると思われる。 ただし計算させるたびにlossの変化は変わるし、上で設定したlearning_rateの値次第でも変わる。
学習したモデルの正答率
正答率: 100.0 %
参考
全コード
# -*- coding: utf-8 -*- import numpy import torch import torch.nn as nn import torch.nn.functional as F from matplotlib import pyplot as plt class Model(nn.Module): def __init__(self): super(Model, self).__init__() self.l1 = nn.Linear(2, 3) self.l2 = nn.Linear(3, 2) def forward(self, x): x = F.relu(self.l1(x)) x = self.l2(x) return x x_train = torch.tensor([[0,0],[0,1],[1,0],[1,1]], dtype=torch.float) y_train = torch.LongTensor(numpy.array([0,1,1,1])) model = Model() #モデル登録 criterion = nn.CrossEntropyLoss() #損失関数 "criterion"の変数名とするのが一般的な模様 learning_rate = 1e-1 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) #最適化関数 loss_log = [] #学習状況のプロット用 epoch = 100 for t in range(epoch): optimizer.zero_grad() #計算された勾配を0にリセットする y_model = model(x_train) #モデルの計算値算出 loss = criterion(y_model, y_train) #モデル計算値と答えを比較し誤差を求める loss.backward() #誤差からバックプロパゲーション optimizer.step() #バックプロパゲーションから重み更新 print(t, loss.item()) #各エポックで誤差を表示 loss_log.append(loss.item()) #学習状況の記録 plt.plot(loss_log) plt.xlabel('epoch') plt.ylabel('loss') plt.yscale('log') plt.show() ##学習したモデルの正答率を出す outputs = model(x_train) # 2つのnodeの出力のtensorが得られる。大きい方がモデルの出した答え _, predicted = torch.max(outputs.data, 1) # modelの答えを0か1かのtensorに変換する correct = (y_train == predicted).sum().item() # 正答数を求める。 print("正答率: {} %".format(correct/len(predicted)*100.0))