前言
『梯度下降』是神经网路(Neural Network)优化求解的关键,有时候在解说『梯度下降』时,怎么讲都讲不清楚,这时候如果有个动画展示,一图胜过千言万语,看了就一目了然了,所以,笔者就来写个小程式实践这个构想。以下就以简单的线性迴归为例,视觉化整个梯度下降的过程。
梯度下降概念
我们先複习一下神经网路求解的流程,参考下图:
先随机指定权重(Weights)及偏差(Bias)。依模型逐层求出预测值(Y hat)。计算损失函数,若与前一次的损失函数比较,如无明显差异,就认为目前的权重及偏差就是最佳解了,否则就继续进行第4步骤。依预测与训练资料实际值的误差及学习率,更新权重,回到第2步骤,重複计算,直到损失函数与前一次的损失函数比较,无明显差异为止。
图. Neural Network 求解流程
线性迴归求解
我们现在就以简单线性迴归(即单层神经网路,无启动函数)为例,撰写程式求解。
y=b1x+b0
下图是一个简单线性迴归的图解:
图片来源:『Machine-Learning-with-Python』
目标就是以梯度下降法求解 (b1, b0),程式码如下。
import matplotlib.pyplot as pltfrom matplotlib.pyplot import figurefrom sklearn.datasets import make_regressionimport numpy as np# 学习率LEARNING_RATE=0.005# 损失函数与前一次的差异设定值,小于设定值,就停止ERROR_TOLERENCE=0.01# 图形更新的频率PAUSE_INTERVAL=0.5# 产生图形大小fig, ax = plt.subplots()fig.set_size_inches(14, 8)# 产生随机资料X, y= make_regression(n_samples=100, n_features=1, noise=5, bias=50)X=X.ravel()# print(X, y) plt.scatter(X,y)line, = ax.plot(X, [0] * len(X), 'g')# 随机指定权重(Weights)及偏差(Bias)b0 = np.random.rand()b1 = np.random.rand()# 求预测值(Y hat)def calc_forecast(b0, b1, x): return b0 + (b1*x) # 计算损失函数 MSEdef calc_loss(b0, b1, X, y): lossValue = 0 # MSE for (xi, yi) in zip(X, y): # print(type(b0), type(b1), type(xi)) lossValue += 0.5 * ((calc_forecast(b0, b1, xi) - yi)**2) return lossValue# 偏微分,求梯度def derivatives(b0, b1, X, y): b0_offset = 0 b1_offset = 0 for (xi, yi) in zip(X, y): b0_offset += calc_forecast(b0, b1, xi) - yi b1_offset += (calc_forecast(b0, b1, xi) - yi)*xi b0_offset /= len(X) b1_offset /= len(X) return b0_offset, b1_offset# 更新权重def updateParameters(b0, b1, X, y, alpha): b0_offset, b1_offset = derivatives(b0, b1, X, y) b0 = b0 - (alpha * b0_offset) b1 = b1 - (alpha * b1_offset) return b0, b1 # 主程式i=0prev_loss = 999999999999.while True: if i % 100 == 0: # 更新图形Y轴资料 y_new = [b0 + b1 * xplot for xplot in X] line.set_data(X, y_new) # update the data. #ax.cla() plt.pause(PAUSE_INTERVAL) current_loss = calc_loss(b0, b1, X, y) # print('current_loss=',current_loss) # print(prev_loss - current_loss) if prev_loss - current_loss > ERROR_TOLERENCE: b0, b1 = updateParameters(b0, b1, X, y, LEARNING_RATE) prev_loss = current_loss # print('prev_loss=',prev_loss) else: print(b0, b1) break i+=1plt.show()
结语
输出结果如下,显示控制的参数可调整『学习率』、『损失函数与前一次的差异设定值』、『图形更新的频率等变数』。读者也可以轻鬆改为一元多次迴归试试看。
点我观看动画 !!
透过 MatPlotLib 套件的 Pause 及更新Y轴资料,让我们很容易製作动画,太感谢套件发明人了。