作者 | 我是韓小琦
鏈接 | https://zhuanlan.zhihu.com/p/47616848
5.1 試述將線性函數 用作神經元激活函數的缺陷。
答:
使用線性函數作爲激活函數時,無論是在隱藏層還是在輸出層(無論傳遞幾層),其單元值(在使用激活函數之前)都還是輸入 的線性組合,這個時候的神經網絡其實等價於邏輯迴歸(即原書中的對率迴歸,輸出層仍然使用Sigmoid函數)的,若輸出層也使用線性函數作爲激活函數,那麼就等價於線性迴歸 。
5.2 試述使用圖 5.2(b) 激活函數的神經元與對率迴歸的聯繫。
答:
使用Sigmoid激活函數,每個神經元幾乎和對率迴歸相同,只不過對率迴歸在 時輸出爲1,而神經元直接輸出 。
5.3 對於圖 5.7 中的 ,試推導出 BP 算法中的更新公式 (5.13)。
答:
,因 只在計算 時用上,所以 ,其中 ,所以 ,即得原書中5.13式。
5.4 試述式 (5.6) 中學習率的取值對神經網絡訓練的影響。
答:
用一張網上找到的圖來說明吧。
簡單說就是學習率太高會導致誤差函數來回震盪,無法收斂;而學習率太低則會收斂太慢,影響訓練效率。
在原書p104也提到過。
5.5 試編程實現標準 BP 算法和累積 BP 算法,在西瓜數據集 3.0 上分別用這兩個算法訓練一個單隱層網絡,並進行比較。
答:
標準 BP 算法和累積 BP 算法在原書(P105)中也提到過,就是對應標準梯度下降和隨機梯度下降,差別就是後者每次迭代用全部數據計算梯度,前者用一個數據計算梯度。
代碼在:
https://github.com/han1057578619/MachineLearning_Zhouzhihua_ProblemSets/tree/master/ch5--%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/5.5-5.6
具體兩種情況的結果如下圖:可以看出來gd的成本函數收斂過程更加穩定,而sgd每次迭代並不一定向最優方向前進,但總體方向是收斂的,且同樣是迭代200次,最後結果相差不大,但由於sgd每次迭代只使用一個樣本,計算量大幅度下降,顯然sgd的速度會更快。
ps.關於隨機梯度下降的實現,好像有兩種方式,一種是每次將樣本打亂,然後遍歷所有樣本,而後再次打亂、遍歷;另一種是每次迭代隨機抽取樣本。這裏採取的是後一種方式,貌似兩種方式都可以。
此外,BP神經網絡代碼在以前學吳恩達老師深度學習課程的時候就寫過,這次整理了一下正好放上來,所以很多代碼和課程代碼類似,添加了應用多分類的情況的代碼。下面的5.6題也一併在這裏實現。
5.6 試設計一個 BP 改進算法,能通過動態調整學習率顯著提升收斂速度。編程實現該算法,並選擇兩個 UCI 數據集與標準 BP 算法進行實驗比較。
答:
動態調整學習率有很多現成的算法,RMSProp、Adam、NAdam等等。也可以手動實現一個簡單指數式衰減 , 是一個超參。這裏代碼實現了Adam,下面
代碼和5.5一同實現,同樣在:
https://github.com/han1057578619/MachineLearning_Zhouzhihua_ProblemSets/tree/master/ch5--%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/5.5-5.6
這裏只嘗試了sklearn 中自帶的iris數據集試了一下。同樣學習率下,兩者訓練時損失函數如下:
可以明顯看出adam的速度更快的。
5.7 根據式 (5.18)和 (5.19) ,試構造一個能解決異或問題的單層 RBF 神經網絡。
答:
這裏可以使用X = array([[1, 0], [0, 1], [0, 0], [1, 1]]),y = array([[1], [1], [0], [0]])作爲數據,訓練一個RBF神經網絡。
這裏使用均方根誤差作爲損失函數;輸出層和書上一致,爲隱藏層的線性組合,且另外加上了一個偏置項(這是書上沒有)。
代碼在:
'''
這裏使用均方根誤差作爲損失函數的RBF神經網絡。
'''
import numpy as np
import matplotlib.pyplot as plt
def RBF_forward(X_, parameters_):
m, n = X_.shape
beta = parameters_['beta']
W = parameters_['W']
c = parameters_['c']
b = parameters_['b']
t_ = c.shape[0]
p = np.zeros((m, t_)) # 中間隱藏層的激活值 對應書上5.19式
x_c = np.zeros((m, t_)) # 5.19式中 x - c_{i}
for i in range(t_):
x_c[:, i] = np.linalg.norm(X_ - c[[i],], axis=1) ** 2
p[:, i] = np.exp(-beta[0, i] * x_c[:, i])
a = np.dot(p, W.T) + b
return a, p, x_c
def RBF_backward(a_, y_, x_c, p_, parameters_):
m, n = a_.shape
grad = {}
beta = parameters_['beta']
W = parameters_['W']
da = (a_ - y_) # 損失函數對輸出層的偏導 ,這裏的a其實對應着 輸出層的y_hat
dw = np.dot(da.T, p_) / m
db = np.sum(da, axis=0, keepdims=True) / m
dp = np.dot(da, W) # dp即損失函數對隱藏層激活值的偏導
dbeta = np.sum(dp * p_ * (-x_c), axis=0, keepdims=True) / m
assert dbeta.shape == beta.shape
assert dw.shape == W.shape
grad['dw'] = dw
grad['dbeta'] = dbeta
grad['db'] = db
return grad
def compute_cost(y_hat_, y_):
m = y_.shape[0]
loss = np.sum((y_hat_ - y) ** 2) / (2 * m)
return np.squeeze(loss)
def RBF_model(X_, y_, learning_rate, num_epochs, t):
'''
:param X_:
:param y_:
:param learning_rate: 學習率
:param num_epochs: 迭代次數
:param t: 隱藏層節點數量
:return:
'''
parameters = {}
np.random.seed(16)
# 定義中心點,本來這裏的中心點應該由隨機採用或者聚類等非監督學習來獲得的,這裏爲了簡單就直接定義好了
parameters['beta'] = np.random.randn(1, t) # 初始化徑向基的方差
parameters['W'] = np.zeros((1, t)) # 初始化
parameters['c'] = np.random.rand(t, 2)
parameters['b'] = np.zeros([1, 1])
costs = []
for i in range(num_epochs):
a, p, x_c = RBF_forward(X_, parameters)
cost = compute_cost(a, y_)
costs.append(cost)
grad = RBF_backward(a, y_, x_c, p, parameters)
parameters['beta'] -= learning_rate * grad['dbeta']
parameters['W'] -= learning_rate * grad['dw']
parameters['b'] -= learning_rate * grad['db']
return parameters, costs
def predict(X_, parameters_):
a, p, x_c = RBF_forward(X_, parameters_)
return a
X = np.array([[1, 0], [0, 1], [0, 0], [1, 1]])
y = np.array([[1], [1], [0], [0]])
#
parameters, costs = RBF_model(X, y, 0.003, 10000, 8)
plt.plot(costs)
plt.show()
print(predict(X, parameters))
# 梯度檢驗
# parameters = {}
# parameters['beta'] = np.random.randn(1, 2) # 初始化徑向基的方差
# parameters['W'] = np.random.randn(1, 2) # 初始化
# parameters['c'] = np.array([[0.1, 0.1], [0.8, 0.8]])
# parameters['b'] = np.zeros([1, 1])
# a, p, x_c = RBF_forward(X, parameters)
#
# cost = compute_cost(a, y)
# grad = RBF_backward(a, y, x_c, p, parameters)
#
#
# parameters['b'][0, 0] += 1e-6
#
# a1, p1, x_c1 = RBF_forward(X, parameters)
# cost1 = compute_cost(a1, y)
# print(grad['db'])
#
# print((cost1 - cost) / 1e-6)
最後輸出是:
[[ 9.99944968e-01]
[ 9.99881045e-01]
[ 8.72381056e-05]
[ 1.26478454e-04]]
感覺,分類的時候在輸出層使用sigmoid作爲激活函數也可以。
5.8 從網上下載或自己編程實現 SOM 網絡,並觀察其在西瓜數據集 3.0α上產生的結果。
答:
花了挺長時間看,寫完代碼的發現結果和預期有點不太符合,先暫時放一下吧還是...代碼不完整就不放了。
這裏提一個迷惑了我很久的一點,有些博客說SOM神經網絡的聚類類別不需要自己定義,其實是不對的,SOM神經網絡輸出聚類類別是需要自己定義,每個輸出節點對應着一個類別,通過計算樣本和輸出節點的權重向量的相似度來確定樣本屬於哪個類別(節點);輸入節點的數量和樣本的維度一樣(和BP網絡相同);輸出的節點常常是以二維矩陣(這裏可以是正方形也可以多邊形等)或者一維直線的形式,每一個輸出節點對應着一個權重向量和輸入節點實現全連接。
想了解SOM建議參考下面幾個鏈接:
https://www.jianshu.com/p/41fc86728928
https://github.com/KeKe-Li/tutorial/blob/master/assets/src/SOM/SOM.md
https://www.cs.bham.ac.uk/~jxb/NN/l16.pdf
5.9* 試推導用於 Elman 網絡的 BP 算法.
答:
Elman 網絡在西瓜書原書上說的是“遞歸神經網絡”,但是在網上找資料說的
“遞歸神經網絡”是空間維度的展開,是一個樹結構。
“循環神經網絡”是時間維度的展開,代表信息在時間維度從前往後的的傳遞和積累。
從書上p111描述來看感覺更像“循環神經網絡”。最近時間不多(lan..),就不去啃原論文了。關於“循環神經網絡”或者遞歸神經網絡的BP可以參考下面鏈接。
1、零基礎入門深度學習(5) - 循環神經網絡 ,網上大神寫了。
https://zybuluo.com/hanbingtao/note/541458
另外關於循環神經網絡也可以看看吳恩達老師的深度學習課程“序列模型”那部分。
5.10 從網上下載或自己編程實現一個卷積神經網絡並在手寫字符識別數據 MNIST 上進行實驗測試。
答:
正好前段時間做過Kaggle上手寫數字識別的題目。這裏正好放上來,CNN是用Tensorflow實現的,之前看吳恩達老師深度學習課程的時候也拿numpy實現過(課程作業),等以後有時間再整理放上來吧。
https://github.com/han1057578619/kaggle_competition/tree/master/Digit_Recogniz
系列文章:
推薦閱讀
(點擊標題可跳轉閱讀)
重磅!
AI有道年度技術文章電子版PDF來啦!
掃描下方二維碼,添加 AI有道小助手微信,可申請入羣,並獲得2020完整技術文章合集PDF(一定要備註:入羣 + 地點 + 學校/公司。例如:入羣+上海+復旦。
長按掃碼,申請入羣
(添加人數較多,請耐心等待)
最新 AI 乾貨,我在看