摘要:在這個(gè)教程中,我們也將設(shè)計(jì)一個(gè)二分類神經(jīng)網(wǎng)絡(luò)模型,其中輸入數(shù)據(jù)是一個(gè)維度,隱藏層只有一個(gè)神經(jīng)元,并且使用非線性函數(shù)作為激活函數(shù),模型結(jié)構(gòu)能用圖表示為我們先導(dǎo)入教程需要使用的軟件包。
作者:chen_h
微信號 & QQ:862251340
微信公眾號:coderpai
簡書地址:https://www.jianshu.com/p/8e1...
這篇教程是翻譯Peter Roelants寫的神經(jīng)網(wǎng)絡(luò)教程,作者已經(jīng)授權(quán)翻譯,這是原文。
該教程將介紹如何入門神經(jīng)網(wǎng)絡(luò),一共包含五部分。你可以在以下鏈接找到完整內(nèi)容。
(一)神經(jīng)網(wǎng)絡(luò)入門之線性回歸
Logistic分類函數(shù)
(二)神經(jīng)網(wǎng)絡(luò)入門之Logistic回歸(分類問題)
(三)神經(jīng)網(wǎng)絡(luò)入門之隱藏層設(shè)計(jì)
Softmax分類函數(shù)
(四)神經(jīng)網(wǎng)絡(luò)入門之矢量化
(五)神經(jīng)網(wǎng)絡(luò)入門之構(gòu)建多層網(wǎng)絡(luò)
隱藏層這部分教程將介紹三部分:
隱藏層設(shè)計(jì)
非線性激活函數(shù)
BP算法
在前面幾個(gè)教程中,我們已經(jīng)介紹了一些很簡單的教程,就是單一的回歸模型或者分類模型。在這個(gè)教程中,我們也將設(shè)計(jì)一個(gè)二分類神經(jīng)網(wǎng)絡(luò)模型,其中輸入數(shù)據(jù)是一個(gè)維度,隱藏層只有一個(gè)神經(jīng)元,并且使用非線性函數(shù)作為激活函數(shù),模型結(jié)構(gòu)能用圖表示為:
我們先導(dǎo)入教程需要使用的軟件包。
import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import colorConverter, ListedColormap from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm
在這篇教程中,我們將輸入數(shù)據(jù)x分類成兩個(gè)類別,用藍(lán)色表示t = 1,用紅色表示t = 0。其中,紅色分類樣本是一個(gè)多峰分布,被藍(lán)色分類樣本包圍。這些數(shù)據(jù)都是一維的,但是數(shù)據(jù)之間的間隔并不是線性的分割。這些數(shù)據(jù)特性將在下圖中表示出來。
這個(gè)二分類模型不會完全準(zhǔn)確的分類處理啊,因?yàn)槲覀冊谄渲屑尤肓艘粋€(gè)神經(jīng)元,并且采用的是非線性函數(shù)。
# Define and generate the samples nb_of_samples_per_class = 20 # The number of sample in each class blue_mean = [0] # The mean of the blue class red_left_mean = [-2] # The mean of the red class red_right_mean = [2] # The mean of the red class std_dev = 0.5 # standard deviation of both classes # Generate samples from both classes x_blue = np.random.randn(nb_of_samples_per_class, 1) * std_dev + blue_mean x_red_left = np.random.randn(nb_of_samples_per_class/2, 1) * std_dev + red_left_mean x_red_right = np.random.randn(nb_of_samples_per_class/2, 1) * std_dev + red_right_mean # Merge samples in set of input variables x, and corresponding set of # output variables t x = np.vstack((x_blue, x_red_left, x_red_right)) t = np.vstack((np.ones((x_blue.shape[0],1)), np.zeros((x_red_left.shape[0],1)), np.zeros((x_red_right.shape[0], 1))))
# Plot samples from both classes as lines on a 1D space plt.figure(figsize=(8,0.5)) plt.xlim(-3,3) plt.ylim(-1,1) # Plot samples plt.plot(x_blue, np.zeros_like(x_blue), "b|", ms = 30) plt.plot(x_red_left, np.zeros_like(x_red_left), "r|", ms = 30) plt.plot(x_red_right, np.zeros_like(x_red_right), "r|", ms = 30) plt.gca().axes.get_yaxis().set_visible(False) plt.title("Input samples from the blue and red class") plt.xlabel("$x$", fontsize=15) plt.show()非線性激活函數(shù)
在這里,我們使用的非線性轉(zhuǎn)換函數(shù)是Gaussian radial basis function (RBF)。除了徑向基函數(shù)網(wǎng)絡(luò),RBF函數(shù)在神經(jīng)網(wǎng)絡(luò)中不經(jīng)常被作為激活函數(shù)。比較常見的激活函數(shù)是sigmoid函數(shù)。但我們根據(jù)設(shè)計(jì)的輸入數(shù)據(jù)x,在這里RBF函數(shù)能很好地將藍(lán)色樣本數(shù)據(jù)從紅色樣本數(shù)據(jù)中分類出來,下圖畫出了RBF函數(shù)的圖像。RBF函數(shù)給定義為:
RBF函數(shù)的導(dǎo)數(shù)為定義為:
# Define the rbf function def rbf(z): return np.exp(-z**2)
# Plot the rbf function z = np.linspace(-6,6,100) plt.plot(z, rbf(z), "b-") plt.xlabel("$z$", fontsize=15) plt.ylabel("$e^{-z^2}$", fontsize=15) plt.title("RBF function") plt.grid() plt.show()BP算法
在訓(xùn)練模型的時(shí)候,我們使用BP算法來進(jìn)行模型優(yōu)化,這是一種很典型的優(yōu)化算法。BP算法的每次迭代分為兩步:
正向傳播去計(jì)算神經(jīng)網(wǎng)絡(luò)的輸出。
利用神經(jīng)網(wǎng)絡(luò)得出的結(jié)果和真實(shí)結(jié)果之間的誤差進(jìn)行反向傳播來更新神經(jīng)網(wǎng)絡(luò)的參數(shù)。
1. 正向傳播在計(jì)算正向傳播中,輸入數(shù)據(jù)被一層一層的計(jì)算,最后從模型中得出輸出結(jié)果。
隱藏層h經(jīng)激活函數(shù)之后,輸出結(jié)果為:
其中,wh是權(quán)重參數(shù)。hidden_activations(x, wh)函數(shù)實(shí)現(xiàn)了該功能。
神經(jīng)網(wǎng)絡(luò)的最后一層的輸出,是將隱藏層的輸出h作為數(shù)據(jù)參數(shù),并且利用Logistic函數(shù)來作為激活函數(shù)。
其中,w0是輸出層的權(quán)重,output_activations(h, w0)函數(shù)實(shí)現(xiàn)了該功能。我們在公式中添加了一個(gè)偏差項(xiàng)-1,因?yàn)槿绻惶砑悠铐?xiàng),那么Logistic函數(shù)只能學(xué)到一個(gè)經(jīng)過原點(diǎn)的分類面。因?yàn)椋[藏層中的RBF函數(shù)的輸入值得范圍是從零到正無窮,那么如果我們不在輸出層加上偏差項(xiàng)的話,模型不可能學(xué)出有用的分類結(jié)果,因?yàn)闆]有樣本的值將小于0,從而歸為決策樹的左邊。因此,我們增加了一個(gè)截距,即偏差項(xiàng)。正常情況下,偏差項(xiàng)也和權(quán)重參數(shù)一樣,需要被訓(xùn)練,但是由于這個(gè)例子中的模型非常簡單,所以我們就用一個(gè)常數(shù)來作為偏差項(xiàng)。
# Define the logistic function def logistic(z): return 1 / (1 + np.exp(-z)) # Function to compute the hidden activations def hidden_activations(x, wh): return rbf(x * wh) # Define output layer feedforward def output_activations(h , wo): return logistic(h * wo - 1) # Define the neural network function def nn(x, wh, wo): return output_activations(hidden_activations(x, wh), wo) # Define the neural network prediction function that only returns # 1 or 0 depending on the predicted class def nn_predict(x, wh, wo): return np.around(nn(x, wh, wo))2. 反向傳播
在反向傳播過程中,我們需要先計(jì)算出神經(jīng)網(wǎng)絡(luò)的輸出與真實(shí)值之間的誤差。這個(gè)誤差會一層一層的反向傳播去更新神經(jīng)網(wǎng)絡(luò)中的各個(gè)權(quán)重。
在每一層中,使用梯度下降算法按照負(fù)梯度方向?qū)γ總€(gè)參數(shù)進(jìn)行更新。
參數(shù)wh和wo利用w(k+1)=w(k)?Δw(k+1)更新,其中Δw=μ??ξ/?w,μ是學(xué)習(xí)率,?ξ/?w是損失函數(shù)ξ對參數(shù)w的梯度。
在這個(gè)模型中,損失函數(shù)ξ與交叉熵?fù)p失函數(shù)一樣,具體解釋在這里:
損失函數(shù)對于參數(shù)wh和wo的表示如下圖所示。從圖中,我們發(fā)現(xiàn)誤差面不是一個(gè)凸函數(shù),而且沿著wh = 0這一軸,參數(shù)wh將是損失函數(shù)的一個(gè)映射。
從圖中發(fā)現(xiàn),沿著wh = 0,從wo > 0開始,損失函數(shù)有一個(gè)非常陡峭的梯度,并且我們要按照圖形的下邊緣進(jìn)行梯度下降。如果學(xué)習(xí)率取得過大,那么在梯度更新的時(shí)候,可能跳過最小值,從一邊的梯度方向跳到另一邊的梯度方向。因?yàn)樘荻鹊姆较蛱盖土?,每次對參?shù)的更新跨度將會非常大。因此,在開始的時(shí)候我們需要將學(xué)習(xí)率取一個(gè)比較小的值。
# Define the cost function def cost(y, t): return - np.sum(np.multiply(t, np.log(y)) + np.multiply((1-t), np.log(1-y))) # Define a function to calculate the cost for a given set of parameters def cost_for_param(x, wh, wo, t): return cost(nn(x, wh, wo) , t)
# Plot the cost in function of the weights # Define a vector of weights for which we want to plot the cost nb_of_ws = 200 # compute the cost nb_of_ws times in each dimension wsh = np.linspace(-10, 10, num=nb_of_ws) # hidden weights wso = np.linspace(-10, 10, num=nb_of_ws) # output weights ws_x, ws_y = np.meshgrid(wsh, wso) # generate grid cost_ws = np.zeros((nb_of_ws, nb_of_ws)) # initialize cost matrix # Fill the cost matrix for each combination of weights for i in range(nb_of_ws): for j in range(nb_of_ws): cost_ws[i,j] = cost(nn(x, ws_x[i,j], ws_y[i,j]) , t) # Plot the cost function surface fig = plt.figure() ax = Axes3D(fig) # plot the surface surf = ax.plot_surface(ws_x, ws_y, cost_ws, linewidth=0, cmap=cm.pink) ax.view_init(elev=60, azim=-30) cbar = fig.colorbar(surf) ax.set_xlabel("$w_h$", fontsize=15) ax.set_ylabel("$w_o$", fontsize=15) ax.set_zlabel("$xi$", fontsize=15) cbar.ax.set_ylabel("$xi$", fontsize=15) plt.title("Cost function surface") plt.grid() plt.show()
?ξi/?wo是每個(gè)樣本i的輸出梯度,參照第二部分教程的方法,我們可以得出相應(yīng)的推導(dǎo)公式:
其中,zoi=hi?wo,hi是樣本i經(jīng)過激活函數(shù)之后輸出的值,?ξi/?zoi=δoi是輸出層誤差的求導(dǎo)。
gradient_output(y, t)函數(shù)實(shí)現(xiàn)了δo,gradient_weight_out(h, grad_output)函數(shù)實(shí)現(xiàn)了?ξ/?wo。
?ξi/?wh是每個(gè)樣本i在影藏層的梯度,具體計(jì)算如下:
其中,
?ξi/?zhi=δhi表示誤差對于隱藏層輸入的梯度。這個(gè)誤差也可以解釋為,zhi對于最后誤差的貢獻(xiàn)。那么,接下來我們定義一下這個(gè)誤差梯度δhi:
又應(yīng)為?zhi/?wh=xi,那么我們能計(jì)算最后的值為:
在批處理中,對每個(gè)對應(yīng)參數(shù)的梯度進(jìn)行累加,就是最后的梯度。
gradient_hidden(wo, grad_output)函數(shù)實(shí)現(xiàn)了δh。
gradient_weight_hidden(x, zh, h, grad_hidden)函數(shù)實(shí)現(xiàn)了?ξ/?wh。
backprop_update(x, t, wh, wo, learning_rate)函數(shù)實(shí)現(xiàn)了BP算法的每次迭代過程。
# Define the error function def gradient_output(y, t): return y - t # Define the gradient function for the weight parameter at the output layer def gradient_weight_out(h, grad_output): return h * grad_output # Define the gradient function for the hidden layer def gradient_hidden(wo, grad_output): return wo * grad_output # Define the gradient function for the weight parameter at the hidden layer def gradient_weight_hidden(x, zh, h, grad_hidden): return x * -2 * zh * h * grad_hidden # Define the update function to update the network parameters over 1 iteration def backprop_update(x, t, wh, wo, learning_rate): # Compute the output of the network # This can be done with y = nn(x, wh, wo), but we need the intermediate # h and zh for the weight updates. zh = x * wh h = rbf(zh) # hidden_activations(x, wh) y = output_activations(h, wo) # Compute the gradient at the output grad_output = gradient_output(y, t) # Get the delta for wo d_wo = learning_rate * gradient_weight_out(h, grad_output) # Compute the gradient at the hidden layer grad_hidden = gradient_hidden(wo, grad_output) # Get the delta for wh d_wh = learning_rate * gradient_weight_hidden(x, zh, h, grad_hidden) # return the update parameters return (wh-d_wh.sum(), wo-d_wo.sum())
下面的代碼,我們模擬了一個(gè)50次的循環(huán)。白色的點(diǎn)表示,參數(shù)wh和wo在誤差面上面的第k次迭代。
在更新過程中,我們不斷的線性減小學(xué)習(xí)率。這是為了在更新到最后的時(shí)候,學(xué)習(xí)率能是0。這樣能保證最后的參數(shù)更新不會在最小值附近徘徊。
# Run backpropagation # Set the initial weight parameter wh = 2 wo = -5 # Set the learning rate learning_rate = 0.2 # Start the gradient descent updates and plot the iterations nb_of_iterations = 50 # number of gradient descent updates lr_update = learning_rate / nb_of_iterations # learning rate update rule w_cost_iter = [(wh, wo, cost_for_param(x, wh, wo, t))] # List to store the weight values over the iterations for i in range(nb_of_iterations): learning_rate -= lr_update # decrease the learning rate # Update the weights via backpropagation wh, wo = backprop_update(x, t, wh, wo, learning_rate) w_cost_iter.append((wh, wo, cost_for_param(x, wh, wo, t))) # Store the values for plotting # Print the final cost print("final cost is {:.2f} for weights wh: {:.2f} and wo: {:.2f}".format(cost_for_param(x, wh, wo, t), wh, wo))
在我們的機(jī)器上面,最后輸出的結(jié)果是:
final cost is 10.81 for weights wh: 1.20 and wo: 5.56
但由于參數(shù)初始化的不同,可能在你的機(jī)器上面運(yùn)行會有不同的結(jié)果。
# Plot the weight updates on the error surface # Plot the error surface fig = plt.figure() ax = Axes3D(fig) surf = ax.plot_surface(ws_x, ws_y, cost_ws, linewidth=0, cmap=cm.pink) ax.view_init(elev=60, azim=-30) cbar = fig.colorbar(surf) cbar.ax.set_ylabel("$xi$", fontsize=15) # Plot the updates for i in range(1, len(w_cost_iter)): wh1, wo1, c1 = w_cost_iter[i-1] wh2, wo2, c2 = w_cost_iter[i] # Plot the weight-cost value and the line that represents the update ax.plot([wh1], [wo1], [c1], "w+") # Plot the weight cost value ax.plot([wh1, wh2], [wo1, wo2], [c1, c2], "w-") # Plot the last weights wh1, wo1, c1 = w_cost_iter[len(w_cost_iter)-1] ax.plot([wh1], [wo1], c1, "w+") # Shoz figure ax.set_xlabel("$w_h$", fontsize=15) ax.set_ylabel("$w_o$", fontsize=15) ax.set_zlabel("$xi$", fontsize=15) plt.title("Gradient descent updates on cost surface") plt.grid() plt.show()分類結(jié)果的可視化
下面的代碼可視化了最后的分類結(jié)果。在輸入空間域里面,藍(lán)色和紅色代表了最后的分類顏色。從圖中,我們發(fā)現(xiàn)所有的樣本都被正確分類了。
# 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 = 100 xs = np.linspace(-3, 3, num=nb_of_xs) ys = np.linspace(-1, 1, num=nb_of_xs) xx, yy = np.meshgrid(xs, ys) # 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): classification_plane[i,j] = nn_predict(xx[i,j], wh, wo) # Create a color map to show the classification colors of each grid point cmap = ListedColormap([ colorConverter.to_rgba("r", alpha=0.25), colorConverter.to_rgba("b", alpha=0.25)]) # Plot the classification plane with decision boundary and input samples plt.figure(figsize=(8,0.5)) plt.contourf(xx, yy, classification_plane, cmap=cmap) plt.xlim(-3,3) plt.ylim(-1,1) # Plot samples from both classes as lines on a 1D space plt.plot(x_blue, np.zeros_like(x_blue), "b|", ms = 30) plt.plot(x_red_left, np.zeros_like(x_red_left), "r|", ms = 30) plt.plot(x_red_right, np.zeros_like(x_red_right), "r|", ms = 30) plt.gca().axes.get_yaxis().set_visible(False) plt.title("Input samples and their classification") plt.xlabel("x") plt.show()輸入域的轉(zhuǎn)換
為什么神經(jīng)網(wǎng)絡(luò)模型能利用最后的線性Logistic實(shí)現(xiàn)非線性的分類呢?關(guān)鍵原因是隱藏層的非線性RBF函數(shù)。RBF轉(zhuǎn)換函數(shù)可以將靠近原點(diǎn)的樣本(藍(lán)色分類)的輸出值大于0,而遠(yuǎn)離原點(diǎn)的樣本(紅色樣本)的輸出值接近0。如下圖所示,紅色樣本的位置都在左邊接近0的位置,藍(lán)色樣本的位置在遠(yuǎn)離0的位置。這個(gè)結(jié)果就是使用線性Logistic分類的。
同時(shí)注意,我們使用的高斯函數(shù)的峰值偏移量是0,也就是說,高斯函數(shù)產(chǎn)生的值是一個(gè)關(guān)于原點(diǎn)分布的數(shù)據(jù)。
# Plot projected samples from both classes as lines on a 1D space plt.figure(figsize=(8,0.5)) plt.xlim(-0.01,1) plt.ylim(-1,1) # Plot projected samples plt.plot(hidden_activations(x_blue, wh), np.zeros_like(x_blue), "b|", ms = 30) plt.plot(hidden_activations(x_red_left, wh), np.zeros_like(x_red_left), "r|", ms = 30) plt.plot(hidden_activations(x_red_right, wh), np.zeros_like(x_red_right), "r|", ms = 30) plt.gca().axes.get_yaxis().set_visible(False) plt.title("Projection of the input samples by the hidden layer.") plt.xlabel("h") plt.show()
完整代碼,點(diǎn)擊這里
作者:chen_h
微信號 & QQ:862251340
簡書地址:https://www.jianshu.com/p/8e1...
CoderPai 是一個(gè)專注于算法實(shí)戰(zhàn)的平臺,從基礎(chǔ)的算法到人工智能算法都有設(shè)計(jì)。如果你對算法實(shí)戰(zhàn)感興趣,請快快關(guān)注我們吧。加入AI實(shí)戰(zhàn)微信群,AI實(shí)戰(zhàn)QQ群,ACM算法微信群,ACM算法QQ群。長按或者掃描如下二維碼,關(guān)注 “CoderPai” 微信號(coderpai)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/41183.html
摘要:但是在多層神經(jīng)網(wǎng)絡(luò),參數(shù)量非常巨大并且激活函數(shù)是非線性函數(shù)時(shí),我們的損失函數(shù)極不可能是一個(gè)凸函數(shù)。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/1fe... 這篇教程是翻譯Peter Roelants寫的神經(jīng)網(wǎng)絡(luò)教程,作者已經(jīng)授權(quán)翻譯,這是原文。 該教程將介紹如何入門神經(jīng)網(wǎng)絡(luò),一共包含...
摘要:我們通過構(gòu)建一個(gè)由兩層隱藏層組成的小型網(wǎng)絡(luò)去識別手寫數(shù)字識別,來說明神經(jīng)網(wǎng)絡(luò)向多層神經(jīng)網(wǎng)絡(luò)的泛化能力。這個(gè)神經(jīng)網(wǎng)絡(luò)將是通過隨機(jī)梯度下降算法進(jìn)行訓(xùn)練。批處理的最小數(shù)量訓(xùn)練樣本的子集經(jīng)常被稱之為最小批處理單位。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/cb6... 這篇教程是翻譯Pet...
摘要:神經(jīng)網(wǎng)絡(luò)的模型結(jié)構(gòu)為,其中是輸入?yún)?shù),是權(quán)重,是預(yù)測結(jié)果。損失函數(shù)我們定義為對于損失函數(shù)的優(yōu)化,我們采用梯度下降,這個(gè)方法是神經(jīng)網(wǎng)絡(luò)中常見的優(yōu)化方法。函數(shù)實(shí)現(xiàn)了神經(jīng)網(wǎng)絡(luò)模型,函數(shù)實(shí)現(xiàn)了損失函數(shù)。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/0da... 這篇教程是翻譯Peter Roe...
摘要:對于多分類問題,我們使用函數(shù)來處理多項(xiàng)式回歸。概率方程表示輸出根據(jù)函數(shù)得到的值。最大似然估計(jì)可以寫成因?yàn)閷τ诮o定的參數(shù),去產(chǎn)生和,根據(jù)聯(lián)合概率我們又能將似然函數(shù)改寫成。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/abc... 這篇教程是翻譯Peter Roelants寫的神經(jīng)網(wǎng)絡(luò)教程...
摘要:對于多分類問題,我們可以使用多項(xiàng)回歸,該方法也被稱之為函數(shù)。函數(shù)的交叉熵?fù)p失函數(shù)的推導(dǎo)損失函數(shù)對于的導(dǎo)數(shù)求解如下上式已經(jīng)求解了當(dāng)和的兩種情況。最終的結(jié)果為,這個(gè)求導(dǎo)結(jié)果和函數(shù)的交叉熵?fù)p失函數(shù)求導(dǎo)是一樣的,再次證明函數(shù)是函數(shù)的一個(gè)擴(kuò)展板。 作者:chen_h微信號 & QQ:862251340微信公眾號:coderpai簡書地址:https://www.jianshu.com/p/8eb...
閱讀 3055·2021-11-25 09:43
閱讀 1650·2021-11-24 11:15
閱讀 2371·2021-11-22 15:25
閱讀 3516·2021-11-11 16:55
閱讀 3253·2021-11-04 16:10
閱讀 2785·2021-09-14 18:02
閱讀 1697·2021-09-10 10:50
閱讀 1081·2019-08-29 15:39