質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

590閲覧

ライブラリーを使わないシンプルな多層パーセプトロンで誤差伝搬によるorのタスクが学習できない

picker413

総合スコア18

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2018/11/08 01:45

編集2018/11/08 04:21

前提・実現したいこと

普段はscikit-learnなどを使っているのですが、多層パーセプトロンを誤差伝搬で学習するシンプルなネットワークをnumpyだけで実装することになりました。

深層学習(MLP出版)やゼロから作るDeepLeaningなどを参考に自分なりにプログラムを組んで、orやxorなどを学習させようとしたのですがうまくいきません。

具体的には、学習が進むとすべての入力に対して常に4つの出力の平均値である0.75を返すようになり、(0,0)に対しても結果が0.75になってしまいます。

ネットワークの構成は2-2-1で適当に決めました。一層目は(1,0)などの入力を出力するだけで2層目から重みWをかけた下層の出力xにバイアスbをかけた値yをシグモイド関数で出力するようにしています。重みの更新も普通のSGDでレートは0.01です。

誤差逆伝搬の原理は一応わかっているつもりなのですが、何が間違っているのかわかりません。

根本的に間違っているところとか、なんか怪しいところとか何でもいいのでアドバイスお願いします。

Jupyter notebookのファイルと読み込んでいるプログラムはgitにあげてあります。
https://github.com/Pickerdot/teratail_BP[リポジトリ](https://github.com/Pickerdot/teratail_BP)
Dropboxはこちらdropbox

該当のソースコード

こちらが2層目からの層を定義しているクラスです

python

1class BPneuron: 2 def __init__(self, W, b): 3 # 引数として受けた重みとバイアスをself.aramsに格納 4 self.params = [W, b] 5 # 更新前に勾配をまとめてオプティマイザーに送るための入れ物(中身はparamsに対応している必要あり) 6 self.grads = [np.zeros_like(W), np.zeros_like(b)] 7 # クラス外へ中身を持っていくための入れ物 8 self.container = np.empty(0) 9 10 def forward(self, x): 11 # クラスの初期化時に格納した重みとバイアスの取り出し 12 W, b = self.params 13 # yはニューロン内部の値 14 y = np.dot(x, W)+b 15 # Zが出力 16 z = sigmoid(y) 17 self.container = [W, b, x, y, z] 18 return z, self.container 19 20 def backward(self, dz): 21 W, b, x, y, z = self.container 22 # 出力部の逆伝搬(シグモイド版) 23 dy = sigmoid_back(z, dz) 24 db = dy 25 dW = np.dot(x.T, dy) 26 dx = np.dot(dy, W.T) 27 28 # self.gradsに更新に行かう勾配を格納 29 self.grads[0][...] = dW 30 self.grads[1][...] = db 31 32 # オプティマイザーによりself.paramsの値を更新 33 self.params = optimizer_SGD(lr, self.params, self.grads) 34 # すべての結果をself.containerに格納 35 self.container = [dy, db, dW, dx] 36 37 return dx, self.container

sigmoid, sigmoid_back,SGDは次のように定義しています。

python

1def sigmoid(x): 2 return 1 / (1 + np.exp(-x)) 3 4 5def sigmoid_back(z, dz): 6 return dz*sigmoid(z) * (1 - sigmoid(z)) 7 8def optimizer_SGD(lr, params, grads): 9 for i in range(len(params)): 10 params[i] -= lr * grads[i] 11 12 return params

このクラスをJupyter notebook上で読み込んで次のプログラムで学習をさせています。

python

1#データの生成 2test_data = np.array([[1,1],[1,0],[0,1],[0,0]]) 3target_data = np.array([[1],[1],[1],[0]]) 4 5#ネットワークの大きさ 6input_size, hidden_size, output_size = 2,2,1 7I, H, O = input_size, hidden_size, output_size 8 9# 重みとバイアスの定義 10W01 = 0*np.random.rand(I, H) 11W12 = 0*np.random.rand(H, O) 12b1 = np.zeros(H) 13b2 = np.zeros(O) 14 15#モデルの生成 16Seccond_layer = BPneuron(W01,b1) 17Third_layer = BPneuron(W12,b2) 18test_loss_layer = Loss() 19 20for i in range(3000): 21 #学習に使う配列の決定 22 test_number= np.random.randint(4) 23 x = np.array([test_data[test_number]]) 24 t = np.array([target_data[test_number]]) 25 26  # 準伝播 27 out1, container1=Seccond_layer.forward(x) 28 out2 ,container2 = Third_layer.forward(out1) 29 30 loss = test_loss_layer.forward(out2,t) 31 dout = test_loss_layer.backward() 32 33 # 逆伝搬 34 dx ,containe= Third_layer.backward(dout) 35 dinput,containe = Seccond_layer.backward(dx) 36

途中に出てくるcontainerという変数はバイアスや重みの大きさをチェックするための変数なので気にしないでください。

[追記]書き忘れていたLossクラスです

python

1 2class Loss: 3 def __init__(self): 4 self.Loss = None 5 self.dout = None 6 7 def forward(self, out, t): 8 self.Loss = 1/2 * np.sum((out - t)**2) 9 self.dout = out - t 10 return self.Loss 11 12 def backward(self): 13 return self.dout

試したこと

4000回学習以降の中の変数を確認したところ次のようになっていました

_________ 二層目 入力x: [[0 1]] W: [[0.06000589 0.06000589] [0.0603888 0.0603888 ]] b: [0.02322472 0.02322472] y: [[0.08361352 0.08361352]] z: [[0.52089121 0.52089121]] _________ 三層目 x: [[0.52089121 0.52089121]] W: [[0.33023109] [0.33023109]] b: [0.62977926] y: [[0.97380821]] z: [[0.7258779]] _________ loss: [[-0.2741221]]
_________ 二層目 x: [[0 0]] W: [[0.06000589 0.06000589] [0.06043531 0.06043531]] b: [0.02327123 0.02327123] y: [[0.02327123 0.02327123]] z: [[0.50581754 0.50581754]] _________ 三層目 x: [[0.50581754 0.50581754]] W: [[0.33054488] [0.33054488]] b: [0.63038167] y: [[0.96477247]] z: [[0.72407631]] _________ loss: [[0.72407631]]

orのタスクを実行するには二層目のバイアスが負にならなければいけないように思えるのですが負になりません。正なので二層目の時点でシグモイドをかけたときに(0,0)と(1,0)の差がなくなってしまい三層目では二つの違いを区別できていないように思えます。

ちなみに誤差(out-target)の値は次のように推移します。

イメージ説明

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tiitoi

2018/11/08 03:38

Loss() の定義が質問欄のコードにないような気がするのですが、追記できますか?
picker413

2018/11/08 04:01

ご指摘ありがとうございます。追記しました。
guest

回答1

0

自己解決

まだ、学習効率的にはよろしくないのですが学習自体は行うことが可能になりました。

逆伝播のアルゴリズムやSGDには不備はなく、学習開始前に定義したパラメータが間違っていました。

まずは初期化時に決めたネットワークの重みの大きさで、np.random.randで生成した0~1の乱数に0.01をかけていたのですが、0.01が小さすぎたのと、0を中心とした乱数でなかったので出力関数であるsigmoidにかけたとき3層目に入力する値がほとんど同じ値になってました。

さらに中間層が2つのノードというのも少なすぎたのかもしれません。

とりあえず誤差は下がりました。

イメージ説明

投稿2018/11/08 06:34

picker413

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問