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

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

新規登録して質問してみよう
ただいま回答率
85.49%
Python

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

Q&A

解決済

2回答

8302閲覧

ゼロから学ぶDeepLearning 4章についての質問

ganariya

総合スコア50

Python

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

0グッド

2クリップ

投稿2018/01/14 00:47

###前提・実現したいこと

現在ゼロから学ぶDeepLearningを使用して機械学習について勉強しています。
その中で、第四章の二層のネットワーク作成で躓いてしまったことがありました。
作成自体は理解できたのですが、なぜうまく動作しているのか理解ができませんでした。

###二層のネットワークのコード

Python

1import sys, os 2sys.path.append(os.pardir) 3from common.functions import * 4from common.gradient import numerical_gradient 5 6class TwoLayerNet: 7 8 def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01): 9 """ 10 input_size・・入力層の数 11 hidden_size・・中間層の数 12 output_size・・出力層の数 13 weight_init_std 14 """ 15 16 #重みの初期化 17 self.params = {} 18 self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) 19 self.params['b1'] = np.zeros(hidden_size) 20 self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 21 self.params['b2'] = np.zeros(output_size) 22 23 24 def predict(self, x): 25 """ 26 入力値をもとに予測をする 27 """ 28 29 W1, W2 = self.params['W1'], self.params['W2'] 30 b1, b2 = self.params['b1'], self.params['b2'] 31 32 a1 = np.dot(x, W1) + b1 33 z1 = sigmoid(a1) 34 a2 = np.dot(z1, W2) + b2 35 y = softmax(a2) 36 return y 37 38 39 def loss(self, x, t): 40 """ 41 損失関数・・どれぐらい教師データと値が違うかを求める 返り値が大きいほど合っていない 42 x・・入力データ 43 t・・教師データ 44 """ 45 y = self.predict(x) 46 return cross_entropy_error(y, t) 47 48 49 def accuracy(self, x, t): 50 """ 51 返り値・・0から1の値 大きいほど入力データからの予測値と、教師データの値が一致している 52 """ 53 y = self.predict(x) 54 y = np.argmax(y, axis=1) 55 t = np.argmax(t, axis=1) 56 57 accuracy = np.sum(y == t) / float(x.shape[0]) 58 return accuracy 59 60 61 def numerical_gradient(self, x, t): 62 """ 63 重みパラメータに対する勾配を求める 64 x・・入力データ 65 t・・教師データ 66 grads・・勾配を保持するディクショナリ変数 67 """ 68 loss_W = lambda W: self.loss(x, t) #loss_Wはloss関数と等しい 69 grads = {} 70 grads['W1'] = numerical_gradient(loss_W, self.params['W1']) 71 grads['b1'] = numerical_gradient(loss_W, self.params['b1']) 72 grads['W2'] = numerical_gradient(loss_W, self.params['W2']) 73 grads['b2'] = numerical_gradient(loss_W, self.params['b2']) 74 return grads 75 76

###上記の二層ネットワーククラス内で使用しているnumerical_gradientのコード(本に付属しているコード)

Python

1def numerical_gradient(f, x): 2 h = 1e-4 # 0.0001 3 grad = np.zeros_like(x) 4 5 it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) 6 while not it.finished: 7 idx = it.multi_index 8 tmp_val = x[idx] 9 x[idx] = float(tmp_val) + h 10 fxh1 = f(x) # f(x+h) 11 12 x[idx] = tmp_val - h 13 fxh2 = f(x) # f(x-h) 14 grad[idx] = (fxh1 - fxh2) / (2*h) 15 16 x[idx] = tmp_val # 値を元に戻す 17 it.iternext() 18 19 return grad

###上記2つのコードに対しての入力コード

Python

1net = TwoLayerNet(input_size=784, hidden_size=100, output_size=10) 2x = np.random.rand(100, 784) #ダミーの入力データ 100枚 3y = net.predict(x) 4print(y.shape) #(100,10) 5 6t = np.random.rand(100, 10) #ダミーの正解ラベル(100枚分) 7grads = net.numerical_gradient(x, t)

###理解できない点

1.TwoLayerNetクラスのnumerical_gradient関数内で使用されているnumerical_gradient関数は,selfがついていないため、クラスの外側にあるcommon.numerical_gradient関数を使用しているという認識で正しいでしょうか?

2.TwoLayerNetクラスのnumerical_gradient関数を使用した勾配がどうして合っているのかわかりません。
numerical_gradient関数内のラムダ式「loss_W=lamda W:self.loss(x, t)」の引数(x,t)は、TwoLayerNetクラスのnumerical_gradient関数の引数である(self, x,t)の(x,t)から使用しているのでしょうか?

3.TwoLayerNetクラスのnumerical_gradient関数内で使用されている
「grads['W1'] = numerical_gradient(loss_W, self.params['W1'])」についてわからないことがあります。
common.numerical_gradient関数では引数に(f,x)(f・・関数,x・・重み'W1'など)を取っています。
common.numerical_gradient関数の「fxh1 = f(x)」で、self.params['W1']をf関数に入れていますが、このf関数は ラムダ式である「lamda W: self.loss(x, t)」であるため、self.params['W1']と引数に渡しても、self.params['W2']を引数に渡しても結果が変わらないだろうと思いました。
しかし、色々とデータを代入してみると、['W1']の場合と['W2']の場合で結果が異なってしまいました。どうして違う結果になるのでしょうか。

よろしくお願いいたします。

###補足情報(言語/FW/ツール等のバージョンなど)
使用環境:Mac OS High Sierra
使用ツール:Jupyter Notebook

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

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

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

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

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

guest

回答2

0

ベストアンサー

この時、例えばnumerical_gradient関数の引数である(f,x)のxに入っているself.params['W1']の値を使用して

fxh1 = f(x)
となっていますが、f関数はもとを辿ればTwoLayerNetクラスのloss関数で(x,t)を使用するため、結局調整したい重みであるself.params['W1']を使用していないように感じました。

numerical_gradient関数の引数である(f,x)のxは**self.params["W1"]の参照を渡しています。numerical_gradient関数で要素毎にx[idx] = float(tmp_val) + hx[idx] = tmp_val - hでxを更新しています。このxはself.params["W1"]の参照**であるためf(x)が実行されるたびに更新されるという流れになります。
さすがに言葉のみでの説明ではわかりにくいので簡単にコードをまとめました。

python

1# ゼロから学ぶニューラルネットワーク4章の(4.4.2)ニューラルネットワークに対する勾配で使われているコードを使います 2import numpy as np 3from pprint import pprint 4 5 6def sigmoid(x): 7 return 1 / (1 + np.exp(-x)) 8 9 10def cross_entropy_error(y, t): 11 if y.ndim == 1: 12 t = t.reshape(1, t.size) 13 y = y.reshape(1, y.size) 14 15 # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換 16 if t.size == y.size: 17 t = t.argmax(axis=1) 18 19 batch_size = y.shape[0] 20 return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size 21 22 23def numerical_gradient(f, x): 24 h = 1e-4 # 0.0001 25 grad = np.zeros_like(x) 26 27 it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) 28 while not it.finished: 29 idx = it.multi_index 30 tmp_val = x[idx] 31 x[idx] = float(tmp_val) + h 32 pprint(idx) 33 # hが0.0001のため小数点四桁目以下を切り捨てています 34 pprint("init: {}".format(round(tmp_val, 4))) 35 pprint("f(x+h): {}".format(round(x[idx], 4))) 36 fxh1 = f(x) # f(x+h) 37 x[idx] = tmp_val - h 38 pprint("f(x-h): {}".format(round(x[idx], 4))) 39 fxh2 = f(x) # f(x-h) 40 grad[idx] = (fxh1 - fxh2) / (2 * h) 41 x[idx] = tmp_val # 値を元に戻す 42 it.iternext() 43 break # 1ループ目で終わらせます 44 return grad 45 46 47class simpleNet: 48 def __init__(self): 49 self.W = np.random.randn(2, 3) 50 51 def predict(self, x): 52 # 参照確認用のコメントになります 53 pprint("predict: {}".format(round(self.W[0][0], 4))) 54 return np.dot(x, self.W) 55 56 def loss(self, x, t): 57 z = self.predict(x) 58 y = softmax(z) 59 loss = cross_entropy_error(y, t) 60 return loss 61 62def main(): 63 x = np.array([0.6, 0.9]) 64 t = np.array([0, 0, 1]) 65 66 net = simpleNet() 67 f = lambda w: net.loss(x, t) 68 dW = numerical_gradient(f, net.W) 69 70 71if __name__ == '__main__': 72 main() 73 """ 74 (0, 0) 75 まずself.Wの初期値を確認します 76 今回はself.Wの中身全ては確認しません 77 'init: -1.2436' 78 'f(x+h): -1.2435' 79 参照が更新されているので上と下の値が同じであることがわかります 80 'predict: -1.2435' 81 'f(x-h): -1.2437' 82 上記と同様です 83 'predict: -1.2437' 84 """

投稿2018/01/18 12:07

wakame

総合スコア1170

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

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

0

  1. はいそうです。プログラムの先頭部分にfrom common.gradient import numerical_gradient とあるのでcommon以下のgradient.pyに定義されているnumerical_gradientを使用しています。

  2. はいそうです。TwoLayerNetクラスのnumerical_gradient関数の引数(x, t)がラムダ式に渡され、loss_Wに格納されています。

  3. まずself.params['W1']self.params['W2']は初期値が異なります。以下のコードで確認してみました。

python

1import numpy as np 2from pprint import pprint 3 4input_size=784 5hidden_size = 100 6output_size=10 7weight_init_std = 0.01 8W1 = weight_init_std * np.random.randn(input_size, hidden_size) 9pprint(W1) 10 11""" 12array([[ -4.66963760e-03, -4.61920583e-03, -1.34628863e-02, ..., 13 -7.12342008e-03, -9.51390931e-03, -1.42363695e-02], 14 [ 5.15248205e-03, -1.81531005e-02, -9.33031556e-03, ..., 15 -6.03827853e-03, 2.32682820e-02, 1.14371378e-02], 16 [ -1.47480497e-03, -1.16778968e-02, -5.36428583e-03, ..., 17 3.24273403e-03, 2.58535969e-03, 4.07705684e-03], 18 ..., 19 [ -6.12870871e-04, 2.27194176e-02, -9.20775174e-03, ..., 20 -6.56353954e-03, -4.82236650e-03, -8.97790691e-03], 21 [ -1.38384077e-02, -1.03076298e-02, 3.97350335e-03, ..., 22 -4.14367214e-03, -7.06703864e-06, 9.48678489e-03], 23 [ -1.24808408e-03, -4.70239299e-03, 5.48649149e-03, ..., 24 1.01967985e-02, 1.20364877e-02, 2.37953073e-03]]) 25""" 26 27W2 = weight_init_std * np.random.randn(input_size, hidden_size) 28pprint(W2) 29 30""" 31array([[-0.00076081, -0.00396194, 0.00877313, ..., -0.01413236, 32 0.00682878, 0.00223497], 33 [ 0.00896433, 0.00138473, -0.00574664, ..., 0.00085467, 34 0.01578464, 0.00760901], 35 [ 0.00133846, -0.012744 , 0.00191541, ..., -0.01548384, 36 -0.01866372, 0.01076827], 37 ..., 38 [ 0.00888638, 0.00104892, 0.01148437, ..., 0.00610676, 39 -0.00137109, 0.00020582], 40 [ 0.00232449, -0.00431334, -0.0065821 , ..., 0.02289721, 41 0.00331537, -0.0168427 ], 42 [-0.01369978, 0.00572917, 0.00457355, ..., 0.01380905, 43 0.00524373, -0.00342188]]) 44"""

したがってloss_Wは引数共通であっても両者値が異なるので結果が異なるということになります。

ごめんなさい、最後のこの文の意味がよくわかりませんでした。

しかし、色々とデータを代入してみると、['W1']の場合と['W2']の場合で結果が異なってしまいました。どうして違う結果になるのでしょうか。

投稿2018/01/14 01:17

編集2018/01/14 01:33
wakame

総合スコア1170

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

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

ganariya

2018/01/14 04:42

1,2の回答ありがとうございます 3についてなのですが クラス外で定義されているnumerical_gradient関数では、引数である「f」を使用して、微分を行っています。 この時、例えばnumerical_gradient関数の引数である(f,x)のxに入っているself.params['W1']の値を使用して fxh1 = f(x) となっていますが、f関数はもとを辿ればTwoLayerNetクラスのloss関数で(x,t)を使用するため、結局調整したい重みであるself.params['W1']を使用していないように感じました。
wakame

2018/01/17 13:11

すみません自分も理解不足で回答を書くのにだいぶ時間がかかってしまいました、説明にコードを書くこともあり上記の回答とは別の回答として投稿させていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問