ニューラルネットワークによるばらつきのあるデータへの近似
はじめに
前回は関数値そのものを学習させたが、今回は関数値をもとに作成したばらつきのあるデータを学習させて 関数をニューラルネットワークで表現できるかを見てみる。
以下の記事で同様の内容がtensorflowを用いて紹介されているが、 ここではPyTorchを使う。
ニューラルネットワークは任意の関数を表現できるのか? - Qiita
データの準備
ここでの関数はnumpy.sin(x)
としている。xの値はランダムに選んだ。yの値は関数値に、正規分布に従う
乱数を加えてデータをばらつかせた。
## 必要なモジュールのインポート import numpy import torch import torch.nn as nn import torch.nn.functional as F from matplotlib import pyplot as plt ## データ作成 data_num = 200 x = numpy.random.rand(data_num)*6 # 0 - 6 の範囲の一様乱数 y = numpy.sin(x) + numpy.random.randn(data_num)*0.3 # 正規分布に従う乱数を足す x_train = torch.FloatTensor(x.reshape(data_num, 1)) y_train = torch.FloatTensor(y.reshape(data_num, 1))
ニューラルネットワークモデルの定義
入力1つ、出力1つ。中間層は1層とし、ノード数は2個とした。 中間層の活性化関数はReLUとし、出力層はl2の計算値をそのまま出力させた。。
class Model(nn.Module): def __init__(self): super(Model, self).__init__() self.l1 = nn.Linear(1, 2) self.l2 = nn.Linear(2, 1) def forward(self, x): x = F.relu(self.l1(x)) x = self.l2(x) return x
ニューラルネットワークモデルの登録
損失関数に平均二乗誤差nn.MSELoss
を使用した。learning_rateは試行錯誤して
良さそうなところに決めた。
model = Model()
criterion = nn.MSELoss()
learning_rate = 1e-1
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
学習
epoch数は試行錯誤して設定したが、計算の初期値次第で学習状況は多少変わる模様。
epoch = 100 for t in range(epoch): optimizer.zero_grad() y_model = model(x_train) loss = criterion(y_model, y_train) loss.backward() optimizer.step() print(t, loss.item()) # 学習状況の表示
学習結果の確認
plt.plot(x, y, '.', label='data') x = numpy.linspace(0, 6, data_num) x_model = torch.FloatTensor(x.reshape(data_num, 1)) y_model = model(x_model).detach().numpy().reshape(1, data_num)[0] plt.plot(x, y_model, label='model', linewidth=3) plt.legend() plt.xlabel('x') plt.ylabel('y') plt.show()
結果
計算させるたびに多少結果が変わるが、下図のようにだいぶ無理矢理な感じとなった。epoch数を増やしてもほとんど状況に変化なかった。
なお、活性化関数をsigmoidに変えると無理矢理感は低下した。
さらに中間層を2層(ノード数 20 ,40)、epoch数=1000で計算するとフィット感は改善した。
epoch数を5000回まで増やすと合わせすぎた感じとなり、いわゆる過学習状態になっていると思われる。。
所感
epoch数、中間層ノード数、層の深さなど、適切に選ぶにはそこそこの知識・経験が要りそう。
参考
メモ: PyTorch TensorDataset、DataLoader について
はじめに
PyTorchのtorch.utils.data.TensorDataset
、torch.utils.data.DataLoader
の使い方についてのメモを記す。
torch.utils.data.TensorDataset
import numpy import torch import torch.utils.data x = numpy.array([[1], [2], [3], [4], [5], [6]]) y = numpy.array([[10], [20], [30], [40], [50], [60]]) x = torch.tensor(x) y = torch.tensor(y) dataset = torch.utils.data.TensorDataset(x, y)
中身をそのまま表示させると、TensorDatasetというオブジェクトであることが示される。
>>> dataset <torch.utils.data.dataset.TensorDataset object at 0x7f4e91fdca20>
[ ]で要素を取り出せる。Tensorの組がタプルになっている。
>>> dataset[0] (tensor([1.]), tensor([10.])) >>> for x, y in dataset: ... print(x, y) ... tensor([1.]) tensor([10.]) tensor([2.]) tensor([20.]) tensor([3.]) tensor([30.]) tensor([4.]) tensor([40.]) tensor([5.]) tensor([50.]) tensor([6.]) tensor([60.])
torch.utils.data.DataLoader
torch.utils.data.DataLoader
にTensorDatasetを渡すと得られ、ミニバッチを返すiterableなオブジェクトとなる。
batch_size
でミニバッチのデータの数を指定できる(defaultは1)。shuffle
をTrueとするとTensorDatasetの中身の
組の順序がシャッフルされる(defaultはFalse)。
batch_size = 3 # ミニバッチのデータの数 data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
for
で中身を取り出すと次のようにミニバッチサイズ数ごとにデータが取り出される(この例では3つ)。順序が
シャッフルされていることが確認できる。
>>> for x, y in data_loader: ... print(x, y) ... tensor([[5.], [4.], [1.]]) tensor([[50.], [40.], [10.]]) tensor([[3.], [6.], [2.]]) tensor([[30.], [60.], [20.]])
ミニバッチ数を4、shuffle=False
とすると次の通りとなる。データは全部で6組なので、2回めに取り出す際は残りの2組だけとなっている。
データの順序はシャッフルされずにもとのままとなっている。
>>> data_loader = torch.utils.data.DataLoader(dataset, ... batch_size=4, shuffle=False) >>> for x, y in data_loader: ... print(x, y) ... tensor([[1.], [2.], [3.], [4.]]) tensor([[10.], [20.], [30.], [40.]]) tensor([[5.], [6.]]) tensor([[50.], [60.]])
DataLoaderを用いた例
ニューラルネットワークによる関数近似 - 化学系エンジニアがAIを学ぶ
参考
ニューラルネットワークによる関数近似
はじめに
PyTorchを用いてニューラルネットワークで関数近似を行うプログラムを作成する。
必要なモジュールのインポート
必ずしも必要でないが、ここではtorch.utils.data
をインポートしtorch.utils.data.TensorDataset
、torch.utils.data.DataLoader
を
用いて、ミニバッチ学習をさせることにする。
TensorDataset、DataLoaderの使い方について→メモ: PyTorch TensorDataset、DataLoader について - 化学系エンジニアがAIを学ぶ
import numpy import torch import torch.nn as nn import torch.nn.functional as F import torch.utils.data from matplotlib import pyplot as plt
ニューラルネットワークモデルの定義
入力1つ、出力1つとしている。中間層は2層とし、それぞれのノード数は20, 40個としている。 中間層の活性化関数はReLUとし、出力層はl3の計算値をそのまま出力している。
class Model(nn.Module): def __init__(self): super(Model, self).__init__() self.l1 = nn.Linear(1, 20) self.l2 = nn.Linear(20, 40) self.l3 = nn.Linear(40, 1) def forward(self, x): x = F.relu(self.l1(x)) x = F.relu(self.l2(x)) x = self.l3(x) return x
データの準備
x
とy=f(x)
の組としてTensorDatasetを使用する。ここでは関数としてnumpy.exp(x)
を入れているが、
ここに任意の関数を入れることができる。
x
はn個の0〜1の間のランダム値を取る。1次元の配列のままでは扱えないので
(n, 1)の形にreshapeさせている。さらにtorch.FloatTensorの形にしてTensorDatasetを得ている。
def get_dataset(n): x = numpy.random.random(n) #0〜1のランダム値を取る y = numpy.exp(x) #ここを変えれば各種関数で計算できる x = x.reshape(n, 1) #(n, 1)の配列にする必要がある y = y.reshape(n, 1) #(n, 1)の配列にする必要がある x = torch.FloatTensor(x) # floatでないといけない y = torch.FloatTensor(y) # floatでないといけない return torch.utils.data.TensorDataset(x, y)
TensorDatasetをtorch.utils.data.DataLoader
を渡すことでミニバッチを返すiterableオブジェクトが得られる。
data_number = 10000 #準備するデータの数 batch_size = 1000 # 1つのミニバッチのデータの数 data_loader = torch.utils.data.DataLoader(get_dataset(data_number), batch_size=batch_size)
損失関数、最適化関数の設定
損失関数には平均2乗誤差、最適化関数にAdamを使用。
model = Model()
criterion = nn.MSELoss()
learning_rate = 1e-2
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
学習
DataLoaderを用いて1ミニバッチごとに学習を繰り返す。
loss_log = [] # 学習状況のプロット用 epoch = 100 for t in range(epoch): for xt, yt in data_loader: # 1ミニバッチずつ計算 optimizer.zero_grad() y_model = model(xt) loss = criterion(y_model, yt) loss.backward() optimizer.step() print(t, loss.item()) loss_log.append(loss.item())
学習結果の確認
##学習したモデルと関数を比較する x = numpy.linspace(0, 1, 100) plt.plot(x, numpy.exp(x), label='exp(x)') x_model = torch.FloatTensor(x.reshape(100,1)) y_model = model(x_model) y = y_model.detach().numpy().reshape(1,100)[0] # プロット用に変換、detach()が必要 plt.plot(x, y, label='model') plt.legend() plt.xlabel('x') plt.ylabel('y') plt.show()
y_modelの中身をnumpyの1次元配列に変換している。
detach()
なしでやると以下のErrorが出る。これを参考にdetach()
を入れた。
RuntimeError: Can't call numpy() on Variable that requires grad. Use var.detach().numpy() instead.
結果
学習状況
以下のコードで学習状況を確認できる。
変化が小さくなるepoch数70回あたりで学習が十分できていると思われる。
learnig_rate = 1e-1
では loss が不安定に変化したため 1e-2 に設定した。
plt.plot(loss_log) plt.xlabel('epoch') plt.ylabel('loss') plt.yscale('log') plt.show()
学習したモデルと関数値との比較
ほぼ一致していることが確認できる。
その他の関数での例
関数get_dataset()
内の数学関数部分を変えれば計算できる。
sin(x)
ほぼ一致している。
sqrt(s)
x = 0 付近でずれあるが、その他はほぼ一致している。
exp(cos(x))
この例ではxの範囲を0〜5としている。x = 0 および x = 5 付近でずれが大きめ。
外挿領域の予測精度
exp(x)を 0 <= x < 1 の範囲で学習させたが、試しにその範囲外を計算してみると合わない。
所感
sqrt(x)やexp(cos(x))ではモデルと関数値がずれる部分が生じたが、これを合わせるためにはどうするのか?層を増やす?中間層のnode数を増やす?
参考
全コード
# -*- coding: utf-8 -*- import numpy import torch import torch.nn as nn import torch.nn.functional as F import torch.utils.data from matplotlib import pyplot as plt class Model(nn.Module): def __init__(self): super(Model, self).__init__() self.l1 = nn.Linear(1, 20) self.l2 = nn.Linear(20, 40) self.l3 = nn.Linear(40, 1) def forward(self, x): x = F.relu(self.l1(x)) x = F.relu(self.l2(x)) x = self.l3(x) return x def get_dataset(n): x = numpy.random.random(n) #0〜1のランダム値を取る y = numpy.exp(x) #ここを変えれば各種関数で計算できる x = x.reshape(n, 1) #(n, 1)の配列にする必要がある y = y.reshape(n, 1) #(n, 1)の配列にする必要がある x = torch.FloatTensor(x) # floatでないといけない y = torch.FloatTensor(y) # floatでないといけない return torch.utils.data.TensorDataset(x, y) data_number = 10000 #準備するデータの数 batch_size = 1000 # 1つのミニバッチのデータの数 data_loader = torch.utils.data.DataLoader(get_dataset(data_number), batch_size=batch_size) model = Model() criterion = nn.MSELoss() learning_rate = 1e-2 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) loss_log = [] # 学習状況のプロット用 epoch = 100 for t in range(epoch): for xt, yt in data_loader: # 1ミニバッチずつ計算 optimizer.zero_grad() y_model = model(xt) loss = criterion(y_model, yt) loss.backward() optimizer.step() print(t, loss.item()) loss_log.append(loss.item()) x = numpy.linspace(0, 1, 100) plt.plot(x, numpy.exp(x), label='exp(x)') x_model = torch.FloatTensor(x.reshape(100,1)) y_model = model(x_model) y = y_model.detach().numpy().reshape(1,100)[0] # プロット用に変換、detach()が必要 plt.plot(x, y, label='model') plt.legend() plt.xlabel('x') plt.ylabel('y') plt.show()
ニューラルネットワークで論理演算子 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))