人工知能やチャットAIに興味があったのでPythonをインストールした。
筆者は過去に人口知能でチャットBOTを作ろうとしたのだが失敗。
DOCOMOさんが既にチャットBOTAPIを提供しているので、興味のある人は検索してみよう。
人工知能やチャットAIに興味があったのでPythonをインストールした。
筆者は過去に人口知能でチャットBOTを作ろうとしたのだが失敗。
DOCOMOさんが既にチャットBOTAPIを提供しているので、興味のある人は検索してみよう。
3つ以上の選択肢やコマの移動などに。
GetButtonUpはあるが、GetAxisRawUpはないためサンプルコードを公開しました。
もっとスマートな方法があるかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
private bool INPUTFLAG; private bool LRFLAG; Start(){ INPUTFLAG=true; LRFLAG=false; } Update() { float lr = CrossPlatformInputManager.GetAxisRaw("Cross button"); if (INPUTFLAG==true && LRFLAG == false && (lr !=0)){ LRFLAG = true; } else { LRFLAG = false; } if (lr != 0) { INPUTFLAG = false; } else { INPUTFLAG = true; } if (Input.GetKeyDown(KeyCode.LeftArrow)||(LRFLAG&&lr<0)){ //何らかの処理 } else if(Input.GetKeyDown(KeyCode.RightArrow)||(LRFLAG&&lr>0)){ //何らかの処理 } } |
聖母の名前を使うので、はばかられるので、(変なソフトになったときにヤバイので)カオリ(Kaeori)という名称にすることにした。ビクターのDJとは全く関係がない。
さて、カオリに対しての入力「こんにちは」にたいしての、カオリの正解の一つの出力は、「いまいそがしいの、またね」だとする。
ビッチ系の重みと、清楚系の重み、不思議ちゃん系の重み、ガテン系の重みなどをつけて、たかだか2層のレイヤで実装できそうである。問題は、その出力数値のインデックスに対応する辞書配列の用意であるが、ツイッターのリプライや日本語の例文集(あるのかわからないが…)を使用して作成すればよいと考える。
学習手順は下記のようになる。
ニューラルネットワークは、適応可能な重みとバイアスがあり、この重みとバイアスを訓練データに適応するように調整することを「学習」と呼ぶ。ニューラルネットワークの学習は次の4つの手順で行う。
訓練データの中からランダムに一部のデータを選び出す。
各重みパラメータに関する損失関数の勾配を求める。
重みパラメータを勾配方向に微小量だけ更新する。
ステップ1、ステップ2、ステップ3を繰り返す。
誤差逆伝播法を使用するのは、ステップ2の勾配の算出である。数値微分のみを使用する順方向の勾配の算出法は簡単に実装できる反面、計算に多くの時間がかかる。よって、誤差逆伝播法を用いる。
TwoLayerNet クラスのインスタンス変数
インスタンス変数 | 説明 |
---|---|
params | ニューラルネットワークのパラメータを保持するディクショナリ変数。params[‘W1’]は1層目の重み、params[‘b1’]は1層目のバイアス。 params[‘W2’]は2番目の重み、params[‘b2’]は2層目のバイアス。 |
layers | ニューラルネットワークのレイヤを保持する順番付きディクショナリ変数。layers[‘Affine1’],layers[‘Relu1’],layers[‘Affine2’]といったように順番付きディクショナリで各レイヤを保持する。 |
lastLayer | ニューラルネットワークの最後のレイヤ。この例では、SoftMaxWithLossレイヤ。 |
TwoLayerNet クラスのメソッド
メソッド | 説明 |
---|---|
__init__(self,input_size, hidden_size, output_size, weight_init_std) | ニューラルネットワークのパラメータを保持するディクショナリ変数。params[‘W1’]は1層目の重み、params[‘b1’]は1層目のバイアス。 params[‘W2’]は2番目の重み、params[‘b2’]は2層目のバイアス。 |
predict(self, x) | ニューラルネットワークのレイヤを保持する順番付きディクショナリ変数。layers[‘Affine1’],layers[‘Relu1’],layers[‘Affine2’]といったように順番付きディクショナリで各レイヤを保持する。 |
loss(self, x, t) | 損失関数の値を求める。引数のxは画像データ、tは正解ラベル |
accuracy(self, x, t) | 認識精度を求める。 |
numerical_gradient(self, x, t) | 重みパラメータに対する勾配を数値微分によって求める。(順伝播 参考として載せるのみで使用しない。) |
gradient(self, x, t) | 重みパラメータに対する勾配を誤差逆伝播法によって求める。 |
OrderedDict()についてはPythonのOrderedDictの使い方を参照。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# coding: utf-8 import sys, os sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定 import numpy as np from common.layers import * from common.gradient import numerical_gradient from collections import OrderedDict class TwoLayerNet: def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01): # 重みの初期化 self.params = {} self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size) # レイヤの生成 self.layers = OrderedDict() self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1']) self.layers['Relu1'] = Relu() self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) self.lastLayer = SoftmaxWithLoss() def predict(self, x): for layer in self.layers.values(): x = layer.forward(x) return x # x:入力データ, t:教師データ def loss(self, x, t): y = self.predict(x) return self.lastLayer.forward(y, t) def accuracy(self, x, t): y = self.predict(x) y = np.argmax(y, axis=1) if t.ndim != 1 : t = np.argmax(t, axis=1) accuracy = np.sum(y == t) / float(x.shape[0]) return accuracy #参考まで(未使用) def numerical_gradient(f, x): h = 1e-4 # 0.0001 grad = np.zeros_like(x) it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: idx = it.multi_index tmp_val = x[idx] x[idx] = float(tmp_val) + h fxh1 = f(x) # f(x+h) x[idx] = tmp_val - h fxh2 = f(x) # f(x-h) grad[idx] = (fxh1 - fxh2) / (2*h) x[idx] = tmp_val # 値を元に戻す it.iternext() return grad # x:入力データ, t:教師データ def numerical_gradient(self, x, t): loss_W = lambda W: self.loss(x, t) grads = {} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) return grads def gradient(self, x, t): # forward self.loss(x, t) # backward dout = 1 dout = self.lastLayer.backward(dout) layers = list(self.layers.values()) layers.reverse() for layer in layers: dout = layer.backward(dout) # 設定 grads = {} grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db return grads |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# coding: utf-8 import sys, os sys.path.append(os.pardir) import numpy as np from dataset.mnist import load_mnist from two_layer_net import TwoLayerNet # データの読み込み (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True) network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) iters_num = 10000 train_size = x_train.shape[0] batch_size = 100 learning_rate = 0.1 train_loss_list = [] train_acc_list = [] test_acc_list = [] iter_per_epoch = max(train_size / batch_size, 1) for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 勾配 #grad = network.numerical_gradient(x_batch, t_batch)#コメントアウト grad = network.gradient(x_batch, t_batch) # 更新 for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] loss = network.loss(x_batch, t_batch) train_loss_list.append(loss) if i % iter_per_epoch == 0: train_acc = network.accuracy(x_train, t_train) test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc) test_acc_list.append(test_acc) print(train_acc, test_acc) |
出力は下図のようになる。
こちらのgithubにフルコードがあります。
最初に、リンゴなどを買う例の計算グラフを紹介した。リンゴのみに着目すると、
下図のようになる。
この場合の変数は、リンゴの値段、リンゴの個数、消費税である。これら3つの変数それぞれが、最終的な支払金額にどのように影響するかということである。左端の3つの灰色の数字が最終的な支払金額220にどのように影響するか、を表す。「支払金額のリンゴの値段に関する偏微分」、「支払金額のリンゴの個数に関する偏微分」、「支払金額のリンゴの消費税に関する偏微分」を求めることに相当する。これを計算グラフの逆伝播を使って解くと上図のようになる。すなわち、「支払金額のリンゴの値段に関する偏微分」は2.2、「支払金額のリンゴの個数に関する偏微分」は110、「支払金額のリンゴの消費税に関する偏微分」は200である。消費税のスケールとリンゴの値段と個数のスケールは違うので、消費税の影響を表す値が大きくなっている。(消費税の1は100%、リンゴの個数、値段は1個、1円)
前述した通り、乗算ノードの逆伝播では入力信号と”ひっくり返った”積が下流へ流れる。加算ノードはそのまま下流へ流れる。暇な方は下図の空欄を埋めてほしい。
この上のリンゴの買い物の例をPythonで実装する。ここでは、計算グラフの乗算ノードを「乗算レイヤ(MulLayer)」、加算ノードを「加算レイヤ(AddLayer)」という名前で実装する。
レイヤはforward()とbackward()という共通のメソッド(関数あるいはインターフェース)を持つように実装する。forward()は順伝播(順方向の伝播)、backward()は逆伝播ん(逆方向の伝播)に対応する。乗算レイヤは下記のように実装できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class MulLayer: def __init__(self): self.x = None self.y = None def forward(self, x, y): self.x = x self.y = y out = x * y return out def backward(self, dout): dx = dout * self.y dy = dout * self.x return dx, dy |
__init__(self)は他言語のコンストラクタに対応し、インスタンス変数の初期化を行う。コンストラクタやインスタンスとは何かとはプログラミングに興味があれば調べてほしい。
forward()では、x、yの2つの引数を受け取り、それらを乗算して出力する。一方、backward()では上流から伝わってきた微分、あるいは信号(dout: d out)に対して、順伝播の”ひっくり返した値”を乗算して下流に流す。
このクラスを使って、下図を実装する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# coding: utf-8 class MulLayer: def __init__(self): self.x = None self.y = None def forward(self, x, y): self.x = x self.y = y out = x * y return out def backward(self, dout): dx = dout * self.y dy = dout * self.x return dx, dy apple = 100 apple_num = 2 tax = 1.1 mul_apple_layer = MulLayer() # 1st layer mul_tax_layer = MulLayer() # 2nd layer # forward apple_price = mul_apple_layer.forward(apple, apple_num) price = mul_tax_layer.forward(apple_price, tax) # backward dprice = 1 dapple_price, dtax = mul_tax_layer.backward(dprice) dapple, dapple_num = mul_apple_layer.backward(dapple_price) print("price:", int(price)) print("dApple:", dapple) print("dApple_num:", int(dapple_num)) print("dTax:", dtax) |
16_simplelayer.pyとして上記のコードを保存する。その後、コマンドプロンプトで実行すると下記の出力が得られる。
$python 16_simplelayer.py
上図の灰色の数字とdで始まる変数の値が一致していることに注目されたい。