在學完前面四周的視頻課程后挖垛,我學會了如何利用matlab和python的spyder編輯器做線性回歸擬合直線和邏輯回歸分類兩件事肢执,而神經(jīng)網(wǎng)絡的反向傳播算法的數(shù)學公式原理Ng老師沒有做詳細的推導和說明煎楣,在網(wǎng)上的各種資源的幫助下結合這一周的課后作業(yè)代碼既峡,小葵花媽媽課堂。
這周的作業(yè)干了這么一件事情匀归,小葵花媽媽在黑板上寫了5000個阿拉伯數(shù)字坑资,1到500個數(shù)字寫的10,501到1000寫的1穆端,以此類推袱贮,最后4501到5000寫的9,小葵花媽媽想要通過這5000個數(shù)字訓練小葵花認識1到10這個數(shù)字体啰,以后能通過它們的特征認出這10個數(shù)字攒巍,所以小葵花媽媽對每個數(shù)字提取出了400個特征點也就是用400個數(shù)來表示,這400個特征數(shù)共同決定在這個數(shù)字是1到10中的哪一個概率最大荒勇,概率最大就認為這個數(shù)字是幾柒莉。
好,現(xiàn)在題目給了我們一個5000乘以400的矩陣X沽翔,和一個5000乘以1的向量y(輸出結果兢孝,代碼中計算誤差時,處理成了y_onehot)仅偎,現(xiàn)在我們只看X其中一行的400個數(shù)怎么確定出最后的手寫數(shù)字是幾的西潘。
我們把這400個數(shù)叫這個神經(jīng)網(wǎng)絡的輸入層,題目再告訴我們隱藏層的也只有1層哨颂,并且這一層有25個數(shù),最后輸出層就是10個數(shù)相种,這10個數(shù)代表這個阿拉伯數(shù)字分別為1到10的概率威恼,選取概率最大的確定為結果y品姓。
這個決定過程是這樣的,把我們原始有的400個特征數(shù)最前面加一個1箫措,這個1先理解為除這400個特征數(shù)外的所有外界影響腹备,把401個數(shù)寫成一個1乘以401的矩陣a1(行向量),由題意得斤蔓,設theta1為401乘以25的矩陣植酥,a1乘以theta1得到1乘以25的矩陣z2(行向量),這個操作可以理解為通過theta1矩陣把400+1個特征數(shù)轉化為離最終結果更近的25個特征數(shù)弦牡,不過這個z2剛出生友驮,還沒得到認可,不具有特征這個阿拉伯數(shù)字的權利驾锰,需要進行激活一下卸留,所以a2=g(z2),函數(shù)g為sigmoid函數(shù):1 / (1 + exp(-z))椭豫,這是上一周邏輯回歸的內(nèi)容耻瑟,也就是說把z2中的25個數(shù)分別帶入上面(1+e的(-z)次方)分之1這個函數(shù)來得到新的25個數(shù),得到a2赏酥,這是1乘以25的行向量喳整,代表激活后的25個特征數(shù)。
再重復上面步驟裸扶,在a2前加一個1框都,(嗯,你懂的)姓言,得到這個1乘以26的矩陣(行向量)a2瞬项,由題意設theta2為 26乘以10 的矩陣,z3=a2乘以theta2何荚,z3就是1乘以10的矩陣(行向量)了囱淋,不要忘了激活一下,a3=g(z3)餐塘,(嗯妥衣,這個你也懂的),好戒傻,激活完成了税手,a3(1*10的哈)這10個數(shù)就是最終這個阿拉伯數(shù)字分別為1到10的10個概率了,哪個概率最大需纳,就是幾了芦倒。
好了,題目背景(也叫正向傳播)就是這樣了不翩,所以我們想要的就是我們設的未知數(shù)theta1和theta2了兵扬,求是不可能求出來的了麻裳,我們只能找到最好的她們倆讓最終結果的誤差最小,那這個誤差長成什么樣子呢器钟,
Ng的課堂上給的是這個樣子的津坑,是一個戴著很多層口罩的妹子(天冷),脫這個口罩很麻煩傲霸,只有暴力點了疆瑰。
現(xiàn)在應該理解誤差(代價函數(shù))是什么意思了,通過前幾周的課程昙啄,知道還需要知道梯度穆役,才能進行梯度下降,讓誤差最小跟衅,這里的梯度不能像高數(shù)里面求出關于未知數(shù)的導函數(shù)式子孵睬,而是求出在確定theta1和theta2位置處的梯度值(導函數(shù)值)。
整個過程是這樣的伶跷,計算機隨機給出theta1和theta2的初始值掰读,因為是隨機給的,我們進行一遍正向傳播叭莫,求出結果a3概率預測蹈集,和實際y_onehot概率(或者實際的y值)對比,發(fā)現(xiàn)基本都預測錯了雇初,計算誤差J拢肆,發(fā)現(xiàn)誤差J的值非常大。
現(xiàn)在我們計算在確定的初始theta1和theta2處的梯度值靖诗,計算出的結果是兩個和theta1郭怪,theta2維數(shù)一致的矩陣,代表對應位置參數(shù)的梯度值刊橘,在最后的代碼中鄙才,將兩個梯度矩陣,按行展開為一整個行向量促绵,初始的theta輸入也展開為一整個行向量攒庵,連同誤差J的計算方法導入minimize優(yōu)化函數(shù)中,進行迭代優(yōu)化败晴,算出使J計算最小的最優(yōu)的theta(為一個行向量浓冒,最后按維數(shù)合成theta1和theta2)。(這一段需要結合代碼理解尖坤,正則化項可以先不管稳懒,理解好了最好再處理正則化,不會的回去復習Ng前面講正則化的部分)慢味。
上面的推導過程結合后面的python代碼理解场梆,最后把最優(yōu)的theta1和theta2進行一次正向傳播計算a3(h)發(fā)現(xiàn)佛致,預測基本準確,如果計算J也非常小辙谜,當我用5000組數(shù)據(jù)進行訓練,最后檢測這5000組預測 感昼,正確率有99.98%装哆。當我只用間隔的2500組數(shù)據(jù)訓練,檢測整體的5000組數(shù)據(jù)定嗓,正確率還有95%左右蜕琴。而只用2500數(shù)據(jù)訓練,檢測另外的2500組數(shù)據(jù)時宵溅,正確率跌倒91%凌简。
"""
Created on Tue Nov 21 16:30:57 2017
@author: leisure
"""
# -*- coding: utf-8 -*-
import numpy as np #引用numpy庫,用np表示恃逻,方便矩陣運算
from scipy.io import loadmat #引用loadmat雏搂,讀取題目給的輸入和正確輸出的mat文件
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def forward_propagate(X, theta1, theta2): #正向傳播程序
m = X.shape[0] #5000組數(shù)據(jù)
a1 = np.insert(X, 0, values=np.ones(m), axis=1) #在X最前 面加一列5000*1的向量
z2 = a1 * theta1 #得到5000*25的矩陣,400個特征轉化為25個隱藏特征
a2 = np.insert(sigmoid(z2), 0, values=np.ones(m), axis=1) #取激活后的z2寇损,再加一列5000*1的向量
z3 = a2 * theta2 #得到5000*10的矩陣凸郑,最后10個特征
h = sigmoid(z3) #激活后得到10個概率
return a1, z2, a2, z3, h
def sigmoid_gradient(z):
return np.multiply(sigmoid(z), (1 - sigmoid(z))) #s函數(shù)求導
def backprop(params, input_size, hidden_size, num_labels, X, y,learning_rate): #反向傳播算法計算梯度的函數(shù),最后得到誤差J和梯度grad(把兩個梯度矩陣按行展開合成一行)
m = X.shape[0]
X = np.matrix(X)
y = np.matrix(y)
# reshape the parameter array into parameter matrices for each layer
theta1 = np.matrix(np.reshape(params[:hidden_size * (input_size + 1)], ((input_size + 1),hidden_size)))
theta2 = np.matrix(np.reshape(params[hidden_size * (input_size + 1):], ((hidden_size + 1),num_labels)))
# run the feed-forward pass
a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)
# initializations
J = 0
delta1 = np.zeros(theta1.shape) # (401, 25)
delta2 = np.zeros(theta2.shape) # (26, 10)
# compute the cost
for i in range(m):
first_term = np.multiply(-y[i,:], np.log(h[i,:])) #y_onehot的每一行和輸出概率h的每一行數(shù)據(jù)相乘
second_term = np.multiply((1 - y[i,:]), np.log(1 - h[i,:]))
J += np.sum(first_term - second_term) #乘出來的10*1的向量元素求和矛市,再累加5000組的數(shù)據(jù)
J = J / m
# add the cost regularization term
J += (float(learning_rate) / (2 * m)) * (np.sum(np.power(theta1[1:,:], 2)) + np.sum(np.power(theta2[1:,:], 2))) #加上正則化的項
# perform backpropagation
for t in range(m):
a1t = a1[t,:] # (1, 401)
z2t = z2[t,:] # (1, 25)
a2t = a2[t,:] # (1, 26)
ht = h[t,:] # (1, 10)
yt = y[t,:] # (1, 10)
d3t = ht - yt # (1, 10) 最后的誤差向量
# z2t = np.insert(z2t, 0, values=np.ones(1)) # 補1為(1, 26)
d2t = np.multiply((d3t * theta2.T)[:,1:], sigmoid_gradient(z2t)) # (1, 25)
delta1 = delta1 + a1t.T * d2t #401*25
delta2 = delta2 + a2t.T * d3t #26*10
delta1 = delta1 / m
delta2 = delta2 / m
# add the gradient regularization term
delta1[1:,:] = delta1[1:,:] + (theta1[1:,:] * learning_rate) / m
delta2[1:,:] = delta2[1:,:] + (theta2[1:,:] * learning_rate) / m
# unravel the gradient matrices into a single array
grad = np.concatenate((np.ravel(delta1), np.ravel(delta2)))
return J, grad
from scipy.optimize import minimize
# initial setup
data = loadmat('ex4data1.mat') #字典形式的數(shù)據(jù)結構
X = data['X']
y = data['y']
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse=False)
y_onehot = encoder.fit_transform(y) # 將y轉為y_onehot芙沥,每組數(shù)據(jù)中概率最大的為1,其余為0浊吏,5000*10
input_size = 400 #輸入層變量個數(shù)400
hidden_size = 25 #隱藏層變量個數(shù)為25
num_labels = 10 #輸出層10個代表分別為1到10的概率
learning_rate = 1#正則化的系數(shù)
# randomly initialize a parameter array of the size of the full network's parameters
params = (np.random.random(size=hidden_size * (input_size + 1) + num_labels * (hidden_size + 1)) - 0.5) * 0.25#隨機給的初始參數(shù)theta1和theta2而昨,現(xiàn)在為一個行向量,用時再按維數(shù)合成矩陣
m = X.shape[0] #m為5000找田,組數(shù)據(jù)
X = np.matrix(X)#轉為矩陣方便運算
y = np.matrix(y)
# minimize the objective function
fmin = minimize(fun=backprop, x0=params, args=(input_size, hidden_size, num_labels, X, y_onehot,learning_rate), #用minimize函數(shù)通過梯度grad優(yōu)化J得到最優(yōu)的theta1和theta2讓J最小
method='TNC', jac=True, options={'maxiter': 250})
theta1 = np.matrix(np.reshape(fmin.x[:hidden_size * (input_size + 1)], ((input_size + 1),hidden_size ))) #優(yōu)化得到的行向量按維數(shù)合成theta1和theta2
theta2 = np.matrix(np.reshape(fmin.x[hidden_size * (input_size + 1):], ((hidden_size + 1),num_labels )))
a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)#最優(yōu)的theta1和theta2正向傳播一次歌憨,看看預測下效果
y_pred = np.array(np.argmax(h, axis=1) + 1)#因為h索引從0開始,需要加1午阵,得到y(tǒng)_pred為我們的預測結果
correct = [1 if a == b else 0 for (a, b) in zip(y_pred, y)]#比較預測和實際的結果躺孝。正確為1,錯誤為0
accuracy = (sum(map(int, correct)) / float(len(correct)))#累加求和底桂,除以總個數(shù)植袍,得到正確率
print ('accuracy = {0}%'.format(accuracy * 100))
由于python的主流性,只貼上python的代碼籽懦,spyder編輯器也可和matlab一樣以矩陣形式實時查看變量的值很方便于个,matlab的代碼網(wǎng)上也有,更多機器學習資源可以加群:514649411暮顺。代碼修改于群主黃博的共享厅篓,推導過程借鑒于博客園秀存,初始數(shù)據(jù)mat文件可以在群里下載。
我們都是初學者羽氮,希望更多人可以更快邁過這道坎或链,繼續(xù)學習下去。