梯度、梯度法、python实现神经网络的梯度计算
【機(jī)器學(xué)習(xí)】梯度、梯度法、python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)的梯度計(jì)算
- 一、python實(shí)現(xiàn)求導(dǎo)的代碼:
- 二、what is 梯度
- 三、使用梯度法尋找神經(jīng)網(wǎng)絡(luò)的最優(yōu)參數(shù)
- 四、神經(jīng)網(wǎng)絡(luò)的梯度計(jì)算
一、python實(shí)現(xiàn)求導(dǎo)的代碼:
導(dǎo)數(shù)含義也就是:變量x一個(gè)微小的變化將導(dǎo)致f(x)的值在多大程度上變化。
def numerical_diff(f, x):h = 1e-4return (f(x+h) - f(x-h)) / (2*h)偏導(dǎo)數(shù)怎么求,對(duì)哪個(gè)變量求偏導(dǎo),就把其他變量固定為某個(gè)值,然后就像求一元函數(shù)導(dǎo)數(shù)那樣對(duì)這個(gè)變量求導(dǎo)。
舉個(gè)例子,對(duì)x0^ 2+x1 ^2=y這個(gè)二元函數(shù),求x0=3,x1=4時(shí),對(duì)x0的偏導(dǎo)數(shù)。
代碼如下:
def numerical_diff(f, x):h = 1e-4return (f(x+h) - f(x-h)) / (2*h)def func_1(x):return x[0]**2+x[1]**2# 求x0=3,x1=4時(shí),x0的偏導(dǎo)數(shù)def func_temp1(x0):return x0**2+4**2if __name__ == '__main__':res = numerical_diff(func_temp1,3.0)print(res)輸出:
6.00000000000378二、what is 梯度
由全部變量的偏導(dǎo)數(shù)匯總而成的向量叫梯度。
求梯度代碼如下:
函數(shù) _numerical_gradient_no_batch 的參數(shù)f為函數(shù),x為Numpy數(shù)組。
grad = np.zeros_like(x)生成一個(gè)形狀和x相同,所有元素均為0的數(shù)組,梯度就存到這里面。
這里面fxh1計(jì)算的時(shí)候,如果在這(x[0],x[1])這一點(diǎn)對(duì)x[0]求編導(dǎo),變的是x[0],x[1]不變。而且對(duì)x[1]求偏導(dǎo)的時(shí)候,變的是x[1],x[0]不變。所以,要用tmp_val存變化前的數(shù),并且,在求完偏導(dǎo)后把一切恢復(fù)。
下面代碼是對(duì)x0 ^ 2+x1 ^ 2=y這個(gè)二元函數(shù),求點(diǎn)(3,4)處的梯度。
import sys, os sys.path.append(os.pardir) # 為了導(dǎo)入父目錄的文件而進(jìn)行的設(shè)定 import numpy as npdef _numerical_gradient_no_batch(f, x):h = 1e-4 # 0.0001grad = np.zeros_like(x)for idx in range(x.size):tmp_val = x[idx]x[idx] = float(tmp_val) + hfxh1 = f(x) # f(x+h)x[idx] = tmp_val - hfxh2 = f(x) # f(x-h)grad[idx] = (fxh1 - fxh2) / (2 * h)x[idx] = tmp_val # 還原值return graddef func_1(x):return x[0]**2+x[1]**2if __name__ == '__main__':#求點(diǎn)(3,4)處的梯度res = _numerical_gradient_no_batch(func_1, np.array([3.0, 4.0]))print(res)結(jié)果:
[6. 8.]用python畫(huà)很多點(diǎn)的梯度向量,那么就發(fā)現(xiàn)一個(gè)很神奇的結(jié)果:梯度指向函數(shù)最小值,離最小值越遠(yuǎn),箭頭越大。
嚴(yán)格來(lái)講,梯度指示的方向是各點(diǎn)處函數(shù)值減小最多的方向。無(wú)法保證梯度所指方向就是函數(shù)最小值。
雖然梯度的方向不一定指向最小值,但是沿著它的方向能夠最大限度減小函數(shù)的值,這就是梯度法。
三、使用梯度法尋找神經(jīng)網(wǎng)絡(luò)的最優(yōu)參數(shù)
通過(guò)不斷地沿著梯度方向前進(jìn),逐漸減小函數(shù)值的過(guò)程就是梯度法。
梯度法的數(shù)學(xué)表示:
η表示更新量,在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中,稱(chēng)為學(xué)習(xí)率( learningrate)。學(xué)習(xí)率決定在一次學(xué)習(xí)中,應(yīng)該學(xué)習(xí)多少,以及在多大程度上更新參數(shù)。
這個(gè)數(shù)學(xué)表示是什么意思,其實(shí)就是沿著梯度走,如上圖,(x0,x1)取(0,2)時(shí),梯度是(0,4)。這里的x0-η乘f在x0處的偏導(dǎo),表示沿那個(gè)梯度方向走的一小步。學(xué)習(xí)率小的話,每次走的步子會(huì)很小,學(xué)習(xí)率大的話,步子就大。
用python實(shí)現(xiàn)梯度法的代碼如下:
f是要進(jìn)行最優(yōu)化的函數(shù), init_x是初始值, lr是學(xué)習(xí)率, step_num是梯度法的重復(fù)次數(shù)。
gradient_descent函數(shù)里面調(diào)用了numerical_gradient函數(shù),用來(lái)求函數(shù)的梯度。gradient_descent函數(shù)里面會(huì)一直循環(huán)step_num次梯度法,每一次都用梯度乘以學(xué)習(xí)率得到新值,并更新。最后如果梯度法進(jìn)行的順利,將走到最小值的位置。
def gradient_descent(f, init_x, lr=0.01, step_num=100):x = init_xx_history = []for i in range(step_num):x_history.append( x.copy() )grad = numerical_gradient(f, x)x -= lr * gradreturn x, np.array(x_history)下面這個(gè)例子,用了梯度法求f(x0,x1)=x0^ 2+x1 ^2的最小值。最終結(jié)果接近(0,0),說(shuō)明我們的結(jié)果基本正確,因?yàn)樽钚≈荡_實(shí)是在(0,0)點(diǎn)取到。
import numpy as np import matplotlib.pylab as plt from gradient_2d import numerical_gradientdef gradient_descent(f, init_x, lr=0.01, step_num=100):x = init_xx_history = []for i in range(step_num):x_history.append( x.copy() )grad = numerical_gradient(f, x)x -= lr * gradreturn x, np.array(x_history)def function_2(x):return x[0]**2 + x[1]**2init_x = np.array([-3.0, 4.0]) lr = 0.1 step_num = 20 x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num) print(x)plt.plot( [-5, 5], [0,0], '--b') plt.plot( [0,0], [-5, 5], '--b') plt.plot(x_history[:,0], x_history[:,1], 'o')plt.xlim(-3.5, 3.5) plt.ylim(-4.5, 4.5) plt.xlabel("X0") plt.ylabel("X1") plt.show()輸出:
[-0.03458765 0.04611686]學(xué)習(xí)率取的過(guò)大或者過(guò)小都無(wú)法得到好結(jié)果。
對(duì)上面代碼進(jìn)行修改:
學(xué)習(xí)率過(guò)大的話,結(jié)果會(huì)發(fā)散成一個(gè)很大的值。
lr = 10 step_num = 100 x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num) print(x)結(jié)果:
[-2.58983747e+13 -1.29524862e+12]學(xué)習(xí)率過(guò)小,基本上沒(méi)怎么更新就結(jié)束了。
lr = 1e-10 step_num = 100 x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num) print(x)結(jié)果:
[-2.99999994 3.99999992]四、神經(jīng)網(wǎng)絡(luò)的梯度計(jì)算
神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中的梯度,指的是損失函數(shù)關(guān)于權(quán)重參數(shù)的梯度。
假設(shè),有一個(gè)形狀為2*3的權(quán)重W的神經(jīng)網(wǎng)絡(luò),損失函數(shù)是L,下面是該網(wǎng)絡(luò)權(quán)重和梯度的數(shù)學(xué)表示。
下面是一個(gè)例子,首先要實(shí)現(xiàn)一個(gè)simpleNet類(lèi),這個(gè)網(wǎng)絡(luò)的權(quán)重矩陣是2*3的。
最后輸出的dw如下,這個(gè)是梯度。
[[ 0.12894287 0.31807705 -0.44701992][ 0.19341431 0.47711557 -0.67052988]]比如,如果w11增加h,那么損失函數(shù)的值會(huì)增加0.47h。從減小損失函數(shù)值的觀點(diǎn)看,w11應(yīng)該向負(fù)方向更新。
import sys, os sys.path.append(os.pardir) # 為了導(dǎo)入父目錄中的文件而進(jìn)行的設(shè)定 import numpy as np from common.functions import softmax, cross_entropy_error from common.gradient import numerical_gradientclass simpleNet:def __init__(self):self.W = np.random.randn(2,3)def predict(self, x):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 lossnet = simpleNet() print(net.W) # 權(quán)重參數(shù)x = np.array([0.6, 0.9])#輸入數(shù)據(jù) p = net.predict(x) #由輸入經(jīng)過(guò)神經(jīng)網(wǎng)絡(luò)得到的輸出預(yù)測(cè)值 print(p) print(np.argmax(p))#最大值的索引t = np.array([0, 0, 1]) # 正確解標(biāo)簽 print(net.loss(x, t)) #輸出損失函數(shù)的值def f(W):return net.loss(x, t) # f = lambda w: net.loss(x, t)dW = numerical_gradient(f, net.W)#求梯度print(dW)輸出:
[[-0.66110535 -2.3121261 0.61870626][-0.43594672 1.66798289 -1.09922476]] [-0.78901526 0.11390894 -0.61807852] 1 1.366622688011303 [[ 0.12894287 0.31807705 -0.44701992][ 0.19341431 0.47711557 -0.67052988]]總結(jié)
以上是生活随笔為你收集整理的梯度、梯度法、python实现神经网络的梯度计算的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 直流有刷减速电机结构及其工作原理
- 下一篇: 设 l í {a,b,c}* 是满足下述