Python DeepLearningに再挑戦 10 ニューラルネットワークの学習 その4 勾配法
概要
Python DeepLearningに再挑戦 10 ニューラルネットワークの学習 その4 勾配法
参考書籍
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- 作者: 斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/09/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (6件) を見る
勾配法
関数の値を最も減らす方向を示すのが勾配。繰り返し勾配方向に進み、関数の値を徐々に減らすのが勾配法(gradient method)。(最小値を探す場合は、勾配降下法、最大値を探す場合は、勾配上昇法)
- 勾配の前についている記号は、学習率(learning rate)と呼ばれる。
- 1回の学習で、どれだけ学習すべきか、どれだけパラメータを更新するか、ということを決めるのが学習率。
- 学習率の値は、0.01、0.001など前もってなんらかの値に決める必要あり。
- ニューラルネットワークの学習においては、学習率の値を変更しながら、正しく学習できているかどうか、確認作業を行うのが一般的。
Pythonで実装
# 勾配降下法 def gradient_descent(f, init_x, lr=0.01, step_num=100): x = init_x # 初期値、step_num=繰り返しの回数 for i in range(step_num):#規定の回数繰り返す grad = numerical_gradient(f, x) # 偏微分をする x -= lr * grad # 学習率をかける return x
ムムム、、、
# f(x0, x1) = x0**2 + x1**2 の最小値を勾配法で求める。 def function_2(x): return x[0]**2 + x[1]**2 init_x = np.array([-3.0, 4.0]) gradient_descent(function_2, init_x=init_x, lr=0.1, step_num=100) >>> array([ -6.11110793e-10, 8.14814391e-10]) # 両方ともほぼ0という意味。なので、成功 # 失敗パターン init_x = np.array([-3.0, 4.0]) gradient_descent(function_2, init_x=init_x, lr=1e-10, step_num=100) >>>array([-2.99999994, 3.99999992]) #全然違う値が出てしまった。
learning rateは、手動で設定が必要なので、ハイパーパラメータと言われる。 ηは、エータと読むらしい(^ ^;)
ニューラルネットワークに対する勾配
今更だけど、∂ = ディー らしい。偏微分用の記号。∂(デルで変換すると出てくる) 常微分の、d と同じような使われ方らしいけど、偏微分と常微分を分けるために、記号を分けてるらしい。
- 損失関数をLとする、形状が2 * 3の重みW
- ∂L/∂W の各要素は、そぞれの要素に関する偏微分から構成される。∂L/∂W11 は、w11を少し変化させると損失関数Lがどれだけ変化するか、ということを表す。
- ∂L/∂Wの形状は、Wと同じ。
import sys, os sys.path.append(os.pardir) import numpy as np from common.functions import softmax, cross_entropy_error from common.gradient import numerical_gradient class simpleNet: def __init__(self): self.W = np.random.randn(2,3) # ガウス分布で初期化 def predict(self, x): # pre return np.dot(x, self.W) def loss(self, x, t): z = self.predict(x) y = softmax(z) loss = cross_entropy_error(y, t) return loss net = simpleNet()# simpleNetのclassを代入 print(net.W) >>> [[-0.22624047 -0.28113346 2.70111015] [-1.41140083 -0.85504959 -1.22749767]] x = np.array([0.6, 0.9]) print(net.W)#net.W は、__init__でセットしたガウス分布で初期化したもの p = net.predict(x) print(p) >>> [ 0.00801858 -0.15454124 -0.45585323] np.argmax(p) >>> 0 t = np.array([1,0,0]) # 正解のラベルを1にする。 net.loss(x, t) >>> 0.90777836764883235 def f(W):#fという関数 return net.loss(x, t) dW = numerical_gradient(f, net.W)#f(W), net.Wを代入して偏微分する print(dW) >>> [[-0.35794831 0.20573552 0.15221279] [-0.53692247 0.30860328 0.22831919]] # w11 をhだけ増やすと、損失関数は、-0.3hだけ変化する(減少する)とかそういう意味。逆にw21は、損失関数が、+ 0.20573552 増加する。そのため、損失関数を減らすという観点からは、w11はプラス方向へ更新して、w21マイナス方向へ更新するのがいい。更新の度合いについてもw11の方が大きく貢献する。 # lambdaで簡単にすることも可能 f = lambda w: net.loss(x,t) dW = numerical_gradient(f, net.W)
とりあえず写経してるけどむずいなぁ〜〜〜(^ ^;)