摘要:但是在多層神經(jīng)網(wǎng)絡,參數(shù)量非常巨大并且激活函數(shù)是非線性函數(shù)時,我們的損失函數(shù)極不可能是一個凸函數(shù)。
作者:chen_h
微信號 & QQ:862251340
微信公眾號:coderpai
簡書地址:https://www.jianshu.com/p/1fe...
這篇教程是翻譯Peter Roelants寫的神經(jīng)網(wǎng)絡教程,作者已經(jīng)授權(quán)翻譯,這是原文。
該教程將介紹如何入門神經(jīng)網(wǎng)絡,一共包含五部分。你可以在以下鏈接找到完整內(nèi)容。
(一)神經(jīng)網(wǎng)絡入門之線性回歸
Logistic分類函數(shù)
(二)神經(jīng)網(wǎng)絡入門之Logistic回歸(分類問題)
(三)神經(jīng)網(wǎng)絡入門之隱藏層設計
Softmax分類函數(shù)
(四)神經(jīng)網(wǎng)絡入門之矢量化
(五)神經(jīng)網(wǎng)絡入門之構(gòu)建多層網(wǎng)絡
矢量化這部分教程將介紹三部分:
矢量的反向傳播
梯度檢查
動量
在先前的教程中,我們已經(jīng)使用學習了一個非常簡單的神經(jīng)網(wǎng)絡:一個輸入數(shù)據(jù),一個隱藏神經(jīng)元和一個輸出結(jié)果。在這篇教程中,我們將描述一個稍微復雜一點的神經(jīng)網(wǎng)絡:包括一個二維的輸入數(shù)據(jù),三維的隱藏神經(jīng)元和二維的輸出結(jié)果,并且利用softmax函數(shù)來做最后的分類。在之前的網(wǎng)絡中,我們都沒有添加偏差項,但在這個網(wǎng)絡模型中我們加入偏差項。網(wǎng)絡模型如下:
我們先導入教程需要使用的軟件包。
import numpy as np import sklearn.datasets import matplotlib.pyplot as plt from matplotlib.colors import colorConverter, ListedColormap from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm定義數(shù)據(jù)集
在這個教程中,我們的分類目標還是二分類:紅色類別(t=1)和藍色類別(t=0)。紅色樣本是一個環(huán)形分布,位于環(huán)形中間的是藍色樣本。我們利用scikit-learn的make_circles的方法得到數(shù)據(jù)集。
這是一個二維的數(shù)據(jù)集,而且不是線性分割。如果我們利用教程2中的方法,那么我們不能正確將它們分類,因為教程2中的方法只能學習出線性分割的樣本數(shù)據(jù)。但我們增加一個隱藏層,就能學習這個非線性分割了。
我們有N個輸入數(shù)據(jù),每個數(shù)據(jù)有兩個特征,那么我們可以得到矩陣X如下:
其中,xij表示第i個樣本的第j個特征值。
經(jīng)過softmax函數(shù)之后,該模型輸出的最終結(jié)果T為:
其中,當且僅當?shù)?b>i個樣本屬于類別j時,tij=1。因此,我們定義藍色樣本的標記是T = [0 1],紅色樣本的標記是T = [1 0]。
# Generate the dataset X, t = sklearn.datasets.make_circles(n_samples=100, shuffle=False, factor=0.3, noise=0.1) T = np.zeros((100,2)) # Define target matrix T[t==1,1] = 1 T[t==0,0] = 1 # Separate the red and blue points for plotting x_red = X[t==0] x_blue = X[t==1] print("shape of X: {}".format(X.shape)) print("shape of T: {}".format(T.shape))
shape of X: (100, 2)
shape of T: (100, 2)
# Plot both classes on the x1, x2 plane plt.plot(x_red[:,0], x_red[:,1], "ro", label="class red") plt.plot(x_blue[:,0], x_blue[:,1], "bo", label="class blue") plt.grid() plt.legend(loc=1) plt.xlabel("$x_1$", fontsize=15) plt.ylabel("$x_2$", fontsize=15) plt.axis([-1.5, 1.5, -1.5, 1.5]) plt.title("red vs blue classes in the input space") plt.show()矢量的反向傳播 1. 矢量的正向傳播
將二維的輸入樣本X轉(zhuǎn)換成三維的輸入層H,我們需要使用鏈接矩陣Wh(Whij表示第i個輸入特征和第j個隱藏層神經(jīng)元相連的權(quán)重)和偏差向量bh:
之后計算結(jié)果如下:
其中,σ是一個Logistic函數(shù),H是一個N*3的輸出矩陣。
整個計算流過程如下圖所示。每個輸入特征xij和權(quán)重參數(shù)whj1、whj2、whj3相乘。最后相乘的每行k(xij*whjk)結(jié)果進行累加得到zik,之后經(jīng)過Logistic函數(shù)σ進行非線性激活得到hik。注意,偏差項Bh可以被整合在鏈接矩陣Wh中,只需要通過在輸入?yún)?shù)xi中增加一個+1項。
在代碼中,Bh和Wh使用bh和Wh表示。hidden_activations(X, Wh, bh)函數(shù)實現(xiàn)了隱藏層的激活輸出。
在計算輸出層的激活結(jié)果之前,我們需要先定義3*2的鏈接矩陣Wo(Woij表示隱藏層第i個神經(jīng)元和輸出層第j個輸出單元的鏈接權(quán)重)和2*1的偏差項矩陣bo:
之后計算結(jié)果如下:
其中,?表示softmax函數(shù)。Y表示最后輸出的n*2的矩陣,Zod表示矩陣Zo的第d列,Wod表示矩陣Wo的第d列,bod表示向量bo的第d個元素。
在代碼中,bo和Wo用變量bo和Wo來表示。output_activations(H, Wo, bo)函數(shù)實現(xiàn)了輸出層的激活結(jié)果。
# Define the logistic function def logistic(z): return 1 / (1 + np.exp(-z)) # Define the softmax function def softmax(z): return np.exp(z) / np.sum(np.exp(z), axis=1, keepdims=True) # Function to compute the hidden activations def hidden_activations(X, Wh, bh): return logistic(X.dot(Wh) + bh) # Define output layer feedforward def output_activations(H, Wo, bo): return softmax(H.dot(Wo) + bo) # Define the neural network function def nn(X, Wh, bh, Wo, bo): return output_activations(hidden_activations(X, Wh, bh), Wo, bo) # Define the neural network prediction function that only returns # 1 or 0 depending on the predicted class def nn_predict(X, Wh, bh, Wo, bo): return np.around(nn(X, Wh, bh, Wo, bo))2. 矢量的反向傳播
最后一層的輸出層使用的是softmax函數(shù),該函數(shù)的交叉熵損失函數(shù)在這篇教程中已經(jīng)有了很詳細的描述。如果需要對N個樣本進行C個分類,那么它的損失函數(shù)ξ是:
損失函數(shù)的誤差梯度δo可以非常方便得到:
其中,Zo(Zo=H?Wo+bo)是一個n*2的矩陣,T是一個n*2的目標矩陣,Y是一個經(jīng)過模型得到的n*2的輸出矩陣。因此,δo也是一個n*2的矩陣。
在代碼中,δo用Eo表示,error_output(Y, T)函數(shù)實現(xiàn)了該方法。
對于N個樣本,對輸出層的梯度δwoj是通過?ξ/?woj計算的,具體計算如下:
其中,woj表示Wo的第j行,即是一個1*2的向量。因此,我們可以將上式改寫成一個矩陣操作,即:
最后梯度的結(jié)果是一個3*2的Jacobian矩陣,如下:
在代碼中,JWo表示上面的Jwo。gradient_weight_out(H, Eo)函數(shù)實現(xiàn)了上面的操作。
對于偏差項bo可以采用相同的方式進行更新。對于批處理的N個樣本,對輸出層的梯度?ξ/?bo的計算如下:
最后梯度的結(jié)果是一個2*1的Jacobian矩陣,如下:
在代碼中,Jbo表示上面的Jbo。gradient_bias_out(Eo)函數(shù)實現(xiàn)了上面的操作。
# Define the cost function def cost(Y, T): return - np.multiply(T, np.log(Y)).sum() # Define the error function at the output def error_output(Y, T): return Y - T # Define the gradient function for the weight parameters at the output layer def gradient_weight_out(H, Eo): return H.T.dot(Eo) # Define the gradient function for the bias parameters at the output layer def gradient_bias_out(Eo): return np.sum(Eo, axis=0, keepdims=True)
在隱藏層上面的誤差函數(shù)的梯度δh可以被定義如下:
其中,Zh是一個n*3的輸入到Logistic函數(shù)中的輸入矩陣,即:
其中,δh也是一個N*3的矩陣。
接下來,對于每個樣本i和每個神經(jīng)元j,我們計算誤差梯度δhij的導數(shù)。通過前面一層的誤差反饋,通過BP算法我們可以計算如下:
其中,woj表示Wo的第j行,它是一個1*2的向量。δoi也是一個1*2的向量。因此,維度是N*3的誤差矩陣δh可以被如下計算:
其中,°表示逐點乘積
在代碼中,Eh表示上面的δh。error_hidden(H, Wo, Eo)函數(shù)實現(xiàn)了上面的函數(shù)。
在N個樣本中,隱藏層的梯度?ξ/?whj可以被如下計算:
其中,whj表示Wh的第j行,它是一個1*3的向量。我們可以將上面的式子寫成矩陣相乘的形式如下:
梯度最后的結(jié)果是一個2*3的Jacobian矩陣,如下:
在代碼中,JWh表示Jwh。gradient_weight_hidden(X, Eh)函數(shù)實現(xiàn)了上面的操作。
偏差項bh可以按照相同的方式進行更新操作。在N個樣本上面,梯度?ξ/?bh可以如下計算:
最后的梯度結(jié)果是一個1*3的Jacobian矩陣,如下:
在代碼中,Jbh表示Jbh。gradient_bias_hidden(Eh)函數(shù)實現(xiàn)了上面的方法。
# Define the error function at the hidden layer def error_hidden(H, Wo, Eo): # H * (1-H) * (E . Wo^T) return np.multiply(np.multiply(H,(1 - H)), Eo.dot(Wo.T)) # Define the gradient function for the weight parameters at the hidden layer def gradient_weight_hidden(X, Eh): return X.T.dot(Eh) # Define the gradient function for the bias parameters at the output layer def gradient_bias_hidden(Eh): return np.sum(Eh, axis=0, keepdims=True)梯度檢查
在編程計算反向傳播梯度時,很容易產(chǎn)生錯誤。這就是為什么一直推薦在你的模型中一定要進行梯度檢查。梯度檢查是通過對于每一個參數(shù)進行梯度數(shù)值計算進行的,即檢查這個數(shù)值與通過反向傳播的梯度進行比較計算。
假設對于參數(shù)θi,我們計算它的數(shù)值梯度?ξ/?θi如下:
其中,f是一個神經(jīng)網(wǎng)絡的方程,X是輸入數(shù)據(jù),θ表示所有參數(shù)的集合。?是一個很小的值,用來對參數(shù)θi進行評估。
對于每個參數(shù)的數(shù)值梯度應該接近于反向傳播梯度的參數(shù)。
# Initialize weights and biases init_var = 1 # Initialize hidden layer parameters bh = np.random.randn(1, 3) * init_var Wh = np.random.randn(2, 3) * init_var # Initialize output layer parameters bo = np.random.randn(1, 2) * init_var Wo = np.random.randn(3, 2) * init_var # Compute the gradients by backpropagation # Compute the activations of the layers H = hidden_activations(X, Wh, bh) Y = output_activations(H, Wo, bo) # Compute the gradients of the output layer Eo = error_output(Y, T) JWo = gradient_weight_out(H, Eo) Jbo = gradient_bias_out(Eo) # Compute the gradients of the hidden layer Eh = error_hidden(H, Wo, Eo) JWh = gradient_weight_hidden(X, Eh) Jbh = gradient_bias_hidden(Eh)
# Combine all parameter matrices in a list params = [Wh, bh, Wo, bo] # Combine all parameter gradients in a list grad_params = [JWh, Jbh, JWo, Jbo] # Set the small change to compute the numerical gradient eps = 0.0001 # Check each parameter matrix for p_idx in range(len(params)): # Check each parameter in each parameter matrix for row in range(params[p_idx].shape[0]): for col in range(params[p_idx].shape[1]): # Copy the parameter matrix and change the current parameter slightly p_matrix_min = params[p_idx].copy() p_matrix_min[row,col] -= eps p_matrix_plus = params[p_idx].copy() p_matrix_plus[row,col] += eps # Copy the parameter list, and change the updated parameter matrix params_min = params[:] params_min[p_idx] = p_matrix_min params_plus = params[:] params_plus[p_idx] = p_matrix_plus # Compute the numerical gradient grad_num = (cost(nn(X, *params_plus), T)-cost(nn(X, *params_min), T))/(2*eps) # Raise error if the numerical grade is not close to the backprop gradient if not np.isclose(grad_num, grad_params[p_idx][row,col]): raise ValueError("Numerical gradient of {:.6f} is not close to the backpropagation gradient of {:.6f}!".format(float(grad_num), float(grad_params[p_idx][row,col]))) print("No gradient errors found")
No gradient errors found
動量反向傳播的更新在前面幾個例子中,我們使用最簡單的梯度下降算法,根據(jù)損失函數(shù)來優(yōu)化參數(shù),因為這些損失函數(shù)都是凸函數(shù)。但是在多層神經(jīng)網(wǎng)絡,參數(shù)量非常巨大并且激活函數(shù)是非線性函數(shù)時,我們的損失函數(shù)極不可能是一個凸函數(shù)。那么此時,簡單的梯度下降算法不是最好的方法去找到一個全局的最小值,因為這個方法是一個局部優(yōu)化的方法,最后將收斂在一個局部最小值。
為了解決這個例子中的問題,我們使用一種梯度下降算法的改進版,叫做動量方法。這種動量方法,你可以想象成一只球從損失函數(shù)的表面從高處落下。在下降的過程中,這個球的速度會增加,但是當它上坡時,它的速度會下降,這一個過程可以用下面的數(shù)學公式進行描述:
其中,V(i)表示參數(shù)第i次迭代時的速度。θ(i)表示參數(shù)在第i次迭代時的值。?ξ/?θ(i)表示參數(shù)在第i次迭代時的梯度。λ表示速度根據(jù)阻力減小的值,μ表示學習率。這個數(shù)學描述公式可以被可視化為如下圖:
速度VWh,Vbh,VWo和Vbo對應于參數(shù)Wh,bh,Wo和bo,在代碼中,這些參數(shù)用VWh,Vbh,VWo和Vbo表示,并且被存儲在一個列表Vs中。update_velocity(X, T, ls_of_params, Vs, momentum_term, learning_rate)函數(shù)實現(xiàn)了上面的方法。update_params(ls_of_params, Vs)函數(shù)實現(xiàn)了速度的更新方法。
# Define the update function to update the network parameters over 1 iteration def backprop_gradients(X, T, Wh, bh, Wo, bo): # Compute the output of the network # Compute the activations of the layers H = hidden_activations(X, Wh, bh) Y = output_activations(H, Wo, bo) # Compute the gradients of the output layer Eo = error_output(Y, T) JWo = gradient_weight_out(H, Eo) Jbo = gradient_bias_out(Eo) # Compute the gradients of the hidden layer Eh = error_hidden(H, Wo, Eo) JWh = gradient_weight_hidden(X, Eh) Jbh = gradient_bias_hidden(Eh) return [JWh, Jbh, JWo, Jbo] def update_velocity(X, T, ls_of_params, Vs, momentum_term, learning_rate): # ls_of_params = [Wh, bh, Wo, bo] # Js = [JWh, Jbh, JWo, Jbo] Js = backprop_gradients(X, T, *ls_of_params) return [momentum_term * V - learning_rate * J for V,J in zip(Vs, Js)] def update_params(ls_of_params, Vs): # ls_of_params = [Wh, bh, Wo, bo] # Vs = [VWh, Vbh, VWo, Vbo] return [P + V for P,V in zip(ls_of_params, Vs)]
# Run backpropagation # Initialize weights and biases init_var = 0.1 # Initialize hidden layer parameters bh = np.random.randn(1, 3) * init_var Wh = np.random.randn(2, 3) * init_var # Initialize output layer parameters bo = np.random.randn(1, 2) * init_var Wo = np.random.randn(3, 2) * init_var # Parameters are already initilized randomly with the gradient checking # Set the learning rate learning_rate = 0.02 momentum_term = 0.9 # define the velocities Vs = [VWh, Vbh, VWo, Vbo] Vs = [np.zeros_like(M) for M in [Wh, bh, Wo, bo]] # Start the gradient descent updates and plot the iterations nb_of_iterations = 300 # number of gradient descent updates lr_update = learning_rate / nb_of_iterations # learning rate update rule ls_costs = [cost(nn(X, Wh, bh, Wo, bo), T)] # list of cost over the iterations for i in range(nb_of_iterations): # Update the velocities and the parameters Vs = update_velocity(X, T, [Wh, bh, Wo, bo], Vs, momentum_term, learning_rate) Wh, bh, Wo, bo = update_params([Wh, bh, Wo, bo], Vs) ls_costs.append(cost(nn(X, Wh, bh, Wo, bo), T))
# Plot the cost over the iterations plt.plot(ls_costs, "b-") plt.xlabel("iteration") plt.ylabel("$xi$", fontsize=15) plt.title("Decrease of cost over backprop iteration") plt.grid() plt.show()可視化訓練分類結(jié)果
在下圖中,我們利用輸入數(shù)據(jù)X和目標結(jié)果T,利用動量方法對BP算法進行更新得到的分類邊界。我們利用紅色和藍色去區(qū)分輸入數(shù)據(jù)的分類域。從圖中,我們可以看出,我們把所有的樣本都正確分類了。
# Plot the resulting decision boundary # Generate a grid over the input space to plot the color of the # classification at that grid point nb_of_xs = 200 xs1 = np.linspace(-2, 2, num=nb_of_xs) xs2 = np.linspace(-2, 2, num=nb_of_xs) xx, yy = np.meshgrid(xs1, xs2) # create the grid # Initialize and fill the classification plane classification_plane = np.zeros((nb_of_xs, nb_of_xs)) for i in range(nb_of_xs): for j in range(nb_of_xs): pred = nn_predict(np.asmatrix([xx[i,j], yy[i,j]]), Wh, bh, Wo, bo) classification_plane[i,j] = pred[0,0] # Create a color map to show the classification colors of each grid point cmap = ListedColormap([ colorConverter.to_rgba("b", alpha=0.30), colorConverter.to_rgba("r", alpha=0.30)]) # Plot the classification plane with decision boundary and input samples plt.contourf(xx, yy, classification_plane, cmap=cmap) # Plot both classes on the x1, x2 plane plt.plot(x_red[:,0], x_red[:,1], "ro", label="class red") plt.plot(x_blue[:,0], x_blue[:,1], "bo", label="class blue") plt.grid() plt.legend(loc=1) plt.xlabel("$x_1$", fontsize=15) plt.ylabel("$x_2$", fontsize=15) plt.axis([-1.5, 1.5, -1.5, 1.5]) plt.title("red vs blue classification boundary") plt.show()輸入域的轉(zhuǎn)換
存在兩個理由去解釋為什么這個神經(jīng)網(wǎng)絡可以將這個非線性的數(shù)據(jù)進行分類。第一,因為隱藏層中使用了非線性的Logistic函數(shù)來幫助數(shù)據(jù)分類。第二,隱藏層使用了三個維度,即我們將數(shù)據(jù)從低維映射到了高維數(shù)據(jù)。下面的圖繪制除了在隱藏層中的三維數(shù)據(jù)分類。
# Plot the projection of the input onto the hidden layer # Define the projections of the blue and red classes H_blue = hidden_activations(x_blue, Wh, bh) H_red = hidden_activations(x_red, Wh, bh) # Plot the error surface fig = plt.figure() ax = Axes3D(fig) ax.plot(np.ravel(H_blue[:,0]), np.ravel(H_blue[:,1]), np.ravel(H_blue[:,2]), "bo") ax.plot(np.ravel(H_red[:,0]), np.ravel(H_red[:,1]), np.ravel(H_red[:,2]), "ro") ax.set_xlabel("$h_1$", fontsize=15) ax.set_ylabel("$h_2$", fontsize=15) ax.set_zlabel("$h_3$", fontsize=15) ax.view_init(elev=10, azim=-40) plt.title("Projection of the input X onto the hidden layer H") plt.grid() plt.show()
完整代碼,點擊這里
作者:chen_h
微信號 & QQ:862251340
簡書地址:https://www.jianshu.com/p/1fe...
CoderPai 是一個專注于算法實戰(zhàn)的平臺,從基礎(chǔ)的算法到人工智能算法都有設計。如果你對算法實戰(zhàn)感興趣,請快快關(guān)注我們吧。加入AI實戰(zhàn)微信群,AI實戰(zhàn)QQ群,ACM算法微信群,ACM算法QQ群。長按或者掃描如下二維碼,關(guān)注 “CoderPai” 微信號(coderpai)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/41185.html
摘要:對于多分類問題,我們使用函數(shù)來處理多項式回歸。概率方程表示輸出根據(jù)函數(shù)得到的值。最大似然估計可以寫成因為對于給定的參數(shù),去產(chǎn)生和,根據(jù)聯(lián)合概率我們又能將似然函數(shù)改寫成。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/abc... 這篇教程是翻譯Peter Roelants寫的神經(jīng)網(wǎng)絡教程...
摘要:對于多分類問題,我們可以使用多項回歸,該方法也被稱之為函數(shù)。函數(shù)的交叉熵損失函數(shù)的推導損失函數(shù)對于的導數(shù)求解如下上式已經(jīng)求解了當和的兩種情況。最終的結(jié)果為,這個求導結(jié)果和函數(shù)的交叉熵損失函數(shù)求導是一樣的,再次證明函數(shù)是函數(shù)的一個擴展板。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/8eb...
摘要:神經(jīng)網(wǎng)絡的模型結(jié)構(gòu)為,其中是輸入?yún)?shù),是權(quán)重,是預測結(jié)果。損失函數(shù)我們定義為對于損失函數(shù)的優(yōu)化,我們采用梯度下降,這個方法是神經(jīng)網(wǎng)絡中常見的優(yōu)化方法。函數(shù)實現(xiàn)了神經(jīng)網(wǎng)絡模型,函數(shù)實現(xiàn)了損失函數(shù)。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/0da... 這篇教程是翻譯Peter Roe...
摘要:那么,概率將是神經(jīng)網(wǎng)絡輸出的,即。函數(shù)實現(xiàn)了函數(shù),函數(shù)實現(xiàn)了損失函數(shù),實現(xiàn)了神經(jīng)網(wǎng)絡的輸出結(jié)果,實現(xiàn)了神經(jīng)網(wǎng)絡的預測結(jié)果。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/d94... 這篇教程是翻譯Peter Roelants寫的神經(jīng)網(wǎng)絡教程,作者已經(jīng)授權(quán)翻譯,這是原文。 該教程將介紹如...
閱讀 2436·2019-08-30 15:52
閱讀 2237·2019-08-30 12:51
閱讀 2833·2019-08-29 18:41
閱讀 2812·2019-08-29 17:04
閱讀 814·2019-08-29 15:11
閱讀 1720·2019-08-28 18:02
閱讀 3603·2019-08-26 10:22
閱讀 2510·2019-08-26 10:12