一氢伟、平方平均誤差
除了對數(shù)損失函數(shù)之外榜轿,還有很多其他誤差函數(shù)都可以應(yīng)用在神經(jīng)網(wǎng)絡(luò)中幽歼。平方平均誤差就是其中一個。從名字可以看出谬盐,它表示預測值和標簽值的差的平方的平均值
甸私。
二、學習權(quán)重
你了解了如何使用感知器來構(gòu)建 AND 和 XOR 運算飞傀,但它們的權(quán)重都是人為設(shè)定的皇型。如果你要進行一個運算,例如預測大學錄取結(jié)果砸烦,但你不知道正確的權(quán)重是什么弃鸦,該怎么辦?你要從樣本中學習權(quán)重外冀,然后用這些權(quán)重來做預測
寡键。
要了解我們將如何找到這些權(quán)重,可以從我們的目標開始考慮雪隧。我們想讓網(wǎng)絡(luò)做出的預測與真實值盡可能接近西轩。為了能夠衡量,我們需要有一個指標來了解預測有多差脑沿,也就是誤差(error)藕畔。一個普遍的指標是誤差平方和 sum of the squared errors (SSE):
這里 是預測值
,是真實值
庄拇。一個是所有輸出單元 j 的和注服,另一個是所有數(shù)據(jù)點的和。這里看上去很復雜措近,但你一旦理解了這些符號之后溶弟,你就能明白這是怎么回事了。
首先是內(nèi)部這個對 的求和瞭郑。變量 代表網(wǎng)絡(luò)輸出單元辜御。所以這個內(nèi)部的求和是指對于每一個輸出單元,計算預測值 與真實值 之間的差的平方,再求和。
另一個對 的求和是針對所有的數(shù)據(jù)點唯咬。也就是說,對每一個數(shù)據(jù)點寄疏,計算其對應(yīng)輸出單元的方差和,然后把每個數(shù)據(jù)點的方差和加在一起。這就是你整個輸出的總誤差。
SSE 是一個很好的選擇有幾個原因:誤差的平方總是正的剖效,對大誤差的懲罰大于小誤差。同時,它對數(shù)學運算也更友好贱鄙。
回想神經(jīng)網(wǎng)絡(luò)的輸出劝贸,也就是預測值,取決于權(quán)重
相應(yīng)的逗宁,誤差也取決于權(quán)重
我們想讓網(wǎng)絡(luò)預測的誤差盡可能小,權(quán)重是讓我們能夠?qū)崿F(xiàn)這個目標的調(diào)節(jié)旋鈕梦湘。我們的目的是尋找權(quán)重 使得誤差平方 最小瞎颗。通常來說神經(jīng)網(wǎng)絡(luò)通過梯度下降
來實現(xiàn)這一點。
三捌议、梯度下降
用梯度下降哼拔,我們通過多個小步驟來實現(xiàn)目標。在這個例子中瓣颅,我們希望一步一步改變權(quán)重來減小誤差倦逐。借用前面的比喻,誤差就像是山宫补,我們希望走到山下檬姥。下山最快的路應(yīng)該是最陡峭的那個方向,因此我們也應(yīng)該尋找能夠使誤差最小化的方向粉怕。我們可以通過計算誤差平方的梯度來找到這個方向健民。
梯度是改變率或者斜度
的另一個稱呼。如果你需要回顧這個概念贫贝,可以看下可汗學院對這個問題的講解秉犹。
要計算變化率,我們要轉(zhuǎn)向微積分稚晚,具體來說是導數(shù)崇堵。一個函數(shù) 的導函數(shù) 給到你的是在 這一點的斜率。例如 客燕,的導數(shù)是 鸳劳。所以,在這個點斜率 幸逆。畫出圖來就是:
梯度就是對多變量函數(shù)導數(shù)的泛化棍辕。我們可以用微積分來尋找誤差函數(shù)中任意一點的梯度,它與輸入權(quán)重有關(guān)还绘,下一節(jié)你可以看到如何推導梯度下降的步驟楚昭。
下面我畫了一個擁有兩個輸入的神經(jīng)網(wǎng)絡(luò)誤差示例,相應(yīng)的拍顷,它有兩個權(quán)重抚太。你可以將其看成一個地形圖,同一條線代表相同的誤差,較深的線對應(yīng)較大的誤差尿贫。
每一步电媳,你計算誤差和梯度,然后用它們來決定如何改變權(quán)重庆亡。重復這個過程直到你最終找到接近誤差函數(shù)最小值的權(quán)重匾乓,即中間的黑點。
注意事項
因為權(quán)重會走向梯度帶它去的位置又谋,它們有可能停留在誤差小拼缝,但不是最小的地方。這個點被稱作局部最低點彰亥。如果權(quán)重初始值有錯咧七,梯度下降可能會使得權(quán)重陷入局部最優(yōu),例如下圖所示任斋。
有方法可以避免這一點继阻,被稱作 momentum.
四、梯度下降的實現(xiàn)
1.誤差平方和 :The sum of the squared errors(SSE)
∵
∴
- 表示全體數(shù)據(jù)
- 表示輸入值
- 表示目標值
誤差平方和用于衡量預測效果废酷,值越高瘟檩,預測效果越差;值越低锦积,預測效果越好芒帕。從上面的公式可以看出權(quán)重是誤差函數(shù)的參數(shù),因此丰介,權(quán)重可以當作控制旋鈕用來調(diào)整預測值背蟆,從而最終影響整體誤差。我們的目標是求取能使誤差最小化的權(quán)重值哮幢。
2.計算梯度下降值
3.更新權(quán)重
用梯度下降來更新權(quán)重的算法概述:
權(quán)重步長設(shè)定為 0:
對訓練數(shù)據(jù)中的每一條記錄:
通過網(wǎng)絡(luò)做正向傳播带膀,計算輸出
計算輸出單元的誤差項(error term)
更新權(quán)重步長
更新權(quán)重 。其中 是學習率橙垢, 是數(shù)據(jù)點個數(shù)垛叨。這里我們對權(quán)重步長做了平均,為的是降低訓練數(shù)據(jù)中大的變化柜某。
重復 代表嗽元。
五、梯度下降:代碼
一個權(quán)重的更新可以這樣計算:
?
這里 error term 是指
記住喂击,上面公式中 是輸出誤差剂癌,激活函數(shù)的導函數(shù)是 ,我們把這個導函數(shù)稱做輸出的梯度翰绊。
# Defining the sigmoid function for activations
# 定義 sigmoid 激活函數(shù)
def sigmoid(x):
return 1/(1+np.exp(-x))
# Derivative of the sigmoid function
# 激活函數(shù)的導數(shù)
def sigmoid_prime(x):
return sigmoid(x) * (1 - sigmoid(x))
# Input data
# 輸入數(shù)據(jù)
x = np.array([0.1, 0.3])
# Target
# 目標
y = 0.2
# Input to output weights
# 輸入到輸出的權(quán)重
weights = np.array([-0.8, 0.5])
# The learning rate, eta in the weight step equation
# 權(quán)重更新的學習率
learnrate = 0.5
# the linear combination performed by the node (h in f(h) and f'(h))
# 輸入和權(quán)重的線性組合
h = x[0]*weights[0] + x[1]*weights[1]
# or h = np.dot(x, weights)
# The neural network output (y-hat)
# 神經(jīng)網(wǎng)絡(luò)輸出
nn_output = sigmoid(h)
# output error (y - y-hat)
# 輸出誤差
error = y - nn_output
# output gradient (f'(h))
# 輸出梯度
output_grad = sigmoid_prime(h)
# error term (lowercase delta)
error_term = error * output_grad
# Gradient descent step
# 梯度下降一步
del_w = [ learnrate * error_term * x[0],
learnrate * error_term * x[1]]
# or del_w = learnrate * error_term * x
六佩谷、反向傳播
如何讓多層神經(jīng)網(wǎng)絡(luò)學習呢旁壮?我們已了解了使用梯度下降來更新權(quán)重,反向傳播算法則是它的一個延伸谐檀。以一個兩層神經(jīng)網(wǎng)絡(luò)為例抡谐,可以使用鏈式法則計算輸入層-隱藏層間權(quán)重的誤差
。
要使用梯度下降法更新隱藏層的權(quán)重桐猬,你需要知道各隱藏層節(jié)點的誤差對最終輸出的影響麦撵。每層的輸出是由兩層間的權(quán)重決定的,兩層之間產(chǎn)生的誤差溃肪,按權(quán)重縮放后在網(wǎng)絡(luò)中向前傳播厦坛。既然我們知道輸出誤差,便可以用權(quán)重來反向傳播到隱藏層乍惊。
例如,輸出層每個輸出節(jié)點的誤差是 ? 放仗,隱藏節(jié)點 的誤差即為輸出誤差乘以輸出層-隱藏層間的權(quán)重矩陣(以及梯度)润绎。
然后,梯度下降與之前相同诞挨,只是用新的誤差:
其中 是輸入和隱藏層之間的權(quán)重莉撇, 是輸入值。這個形式可以表示任意層數(shù)惶傻。權(quán)重更新步長等于步長乘以輸出層誤差再乘以該層的輸入值棍郎。
現(xiàn)在,你有了輸出誤差银室,涂佃,便可以反向傳播這些誤差了。 是該層的輸入蜈敢,比如經(jīng)過隱藏層激活函數(shù)的輸出值辜荠。
范例
反向傳播步驟:
- 計算網(wǎng)絡(luò)輸出誤差
- 計算輸出層誤差項
- 用反向傳播計算隱藏層誤差項
- 計算反向傳播誤差的權(quán)重更新步長
實現(xiàn)反向傳播
現(xiàn)在我們知道輸出層的誤差是
隱藏層誤差是
現(xiàn)在我們只考慮一個簡單神經(jīng)網(wǎng)絡(luò),它只有一個隱藏層和一個輸出節(jié)點抓狭。這是通過反向傳播更新權(quán)重的算法概述:
- 把每一層權(quán)重更新的初始步長設(shè)置為 0
- 輸入到隱藏層的權(quán)重更新是
- 隱藏層到輸出層的權(quán)重更新是
- 對訓練數(shù)據(jù)當中的每一個點
- 讓它正向通過網(wǎng)絡(luò)伯病,計算輸出
- 計算輸出節(jié)點的誤差梯度 這里 是輸出節(jié)點的輸入。
- 誤差傳播到隱藏層
- 更新權(quán)重步長:
- 更新權(quán)重, 其中 是學習率否过, 是數(shù)據(jù)點的數(shù)量:
- 重復這個過程 代午笛。
import numpy as np
from data_prep import features, targets, features_test, targets_test
np.random.seed(21)
def sigmoid(x):
"""
Calculate sigmoid
"""
return 1 / (1 + np.exp(-x))
# Hyperparameters
n_hidden = 2 # number of hidden units
epochs = 900
learnrate = 0.005
n_records, n_features = features.shape
last_loss = None
# Initialize weights
weights_input_hidden = np.random.normal(scale=1 / n_features ** .5,
size=(n_features, n_hidden))
weights_hidden_output = np.random.normal(scale=1 / n_features ** .5,
size=n_hidden)
for e in range(epochs):
del_w_input_hidden = np.zeros(weights_input_hidden.shape)
del_w_hidden_output = np.zeros(weights_hidden_output.shape)
for x, y in zip(features.values, targets):
## Forward pass ##
# TODO: Calculate the output
#(輸入層輸入)*(輸入層到隱藏層的權(quán)重)
hidden_input = np.dot(x, weights_input_hidden)
#通過隱藏層的激活函數(shù)計算(隱藏層輸出)
hidden_output = sigmoid(hidden_input)
#將隱藏層的輸出,作為(輸出層的輸入)*(隱藏層到輸出層的權(quán)重)苗桂,并通過輸出層的激活函數(shù)求得
#輸出層的輸出
output = sigmoid(np.dot(hidden_output,
weights_hidden_output))
## Backward pass ##
# TODO: Calculate the network's prediction error
error = y - output
# TODO: Calculate error term for the output unit
output_error_term = error * output * (1 - output)
## propagate errors to hidden layer
# TODO: Calculate the hidden layer's contribution to the error
#隱藏節(jié)點的誤差即為(輸出誤差)乘以(輸出層-隱藏層間的權(quán)重矩陣)
hidden_error = np.dot(output_error_term, weights_hidden_output)
# TODO: Calculate the error term for the hidden layer
hidden_error_term = hidden_error * hidden_output * (1 - hidden_output)
# TODO: Update the change in weights
del_w_hidden_output += output_error_term * hidden_output
del_w_input_hidden += hidden_error_term * x[:, None]
# TODO: Update weights
weights_input_hidden += learnrate * del_w_input_hidden / n_records
weights_hidden_output += learnrate * del_w_hidden_output / n_records
# Printing out the mean square error on the training set
if e % (epochs / 10) == 0:
hidden_output = sigmoid(np.dot(x, weights_input_hidden))
out = sigmoid(np.dot(hidden_output,
weights_hidden_output))
loss = np.mean((out - targets) ** 2)
if last_loss and last_loss < loss:
print("Train loss: ", loss, " WARNING - Loss Increasing")
else:
print("Train loss: ", loss)
last_loss = loss
# Calculate accuracy on test data
hidden = sigmoid(np.dot(features_test, weights_input_hidden))
out = sigmoid(np.dot(hidden, weights_hidden_output))
predictions = out > 0.5
accuracy = np.mean(predictions == targets_test)
print("Prediction accuracy: {:.3f}".format(accuracy))