MNIST就是機(jī)器學(xué)習(xí)的Hello World, 或者說(shuō)圖片處理的Lena, 是必不可少的經(jīng)典初體驗(yàn)。它已有20多年的歷史舟奠,但是到今天依然魅力不減呛牲,依然是最高引用的數(shù)據(jù)集议泵。MNIST只包含70000張28x28像素的手寫(xiě)數(shù)字的單通道灰度圖券坞,對(duì)于現(xiàn)在的算力來(lái)說(shuō)是很小的數(shù)據(jù)。
網(wǎng)上大多數(shù)的MNIST教程肺素,包括TensorfFlow官方教程帶給我們的都是一種自然主義的學(xué)習(xí)體驗(yàn)恨锚,給出代碼示例簡(jiǎn)單教會(huì)步驟,對(duì)著代碼敲一下就能運(yùn)行倍靡,畢竟參與感不夠猴伶。從結(jié)構(gòu)主義的學(xué)習(xí)方式來(lái)看,我們應(yīng)該至少嘗試一次不用任何深度學(xué)習(xí)的現(xiàn)成框架菌瘫,純手工從零開(kāi)始實(shí)現(xiàn)一次神經(jīng)網(wǎng)絡(luò)蜗顽,并且打開(kāi)其中的黑盒,以可視化的呈現(xiàn)方式形象理解神經(jīng)網(wǎng)絡(luò)如何工作雨让,這就是我寫(xiě)此文的目的雇盖。
數(shù)據(jù)準(zhǔn)備 (Data Preparation)
MNIST數(shù)據(jù)集中Train dataset有60000張圖片與相應(yīng)的標(biāo)注,其中55000張訓(xùn)練集栖忠,5000張驗(yàn)證集(Validation)崔挖,Test dataset有10000張訓(xùn)練集,下載完這四個(gè)文件后我保存到MNIST-data目錄下庵寞。
train-images-idx3-ubyte.gz: training set images (9912422 bytes)
train-labels-idx1-ubyte.gz:? training set labels (28881 bytes)
t10k-images-idx3-ubyte.gz:? test set images (1648877 bytes)
t10k-labels-idx1-ubyte.gz:? test set labels (4542 bytes)
數(shù)據(jù)集就是這么四個(gè)文件狸相,都是特殊的Binary格式。解析起來(lái)費(fèi)點(diǎn)功夫捐川,不過(guò)TensorFlow已封裝好了便利的接口 - input_data.read_data_sets脓鹃。雖然這次我不用TensorFlow框架的神經(jīng)網(wǎng)絡(luò),但是解析數(shù)據(jù)部分借用一下無(wú)妨古沥。這個(gè)函數(shù)會(huì)自動(dòng)嘗試下載數(shù)據(jù)集到指定目錄中瘸右,不過(guò)強(qiáng)烈建議自己手工下載好這四個(gè)文件,由于不可描述的原因岩齿,用這個(gè)方法直接下載數(shù)據(jù)基本都是以time out失敗告終太颤。第一個(gè)參數(shù)是指定的數(shù)據(jù)集存放路徑,第二個(gè)參數(shù)決定是否以獨(dú)熱鍵(one-hot)形式讀取標(biāo)簽盹沈,如果設(shè)為T(mén)rue則以10維向量形式代表一個(gè)數(shù)字龄章。
mndata = input_data.read_data_sets("MNIST-data/", one_hot=True)
分別獲取訓(xùn)練集和測(cè)試集的圖片和標(biāo)注
X_train=mndata.train.images? ? # training set
y_train=mndata.train.labels?
X_test=mndata.test.images? ? # testing set
y_test=mndata.test.labels
然后寫(xiě)一個(gè)畫(huà)圖的函數(shù),讀取矩陣數(shù)據(jù)在表格中展示乞封,僅展示非0數(shù)字且保留兩位小數(shù)做裙。
# visualize grid data of a matrix, zero cell shown as empty
def plt_grid(data):
? ? fig, ax = plt.subplots()
? ? fig.set_size_inches(30,30)
? ? width, height = data.shape? ? #imshow portion
? ? imshow_data = np.random.rand(width, height)
? ? ax.imshow(imshow_data, cmap=plt.cm.Pastel1, interpolation='nearest')? ? for x in range(0, height):
? ? ? ? for y in range(0, width):
? ? ? ? ? ? if (data[y][x]>0):
? ? ? ? ? ? ? ? ax.text(x, y, np.round(data[y][x],2), va='center', ha='center', fontsize=20)
? ? ? ? plt.show()
隨便找個(gè)吉利數(shù)字88作為index, 從訓(xùn)練集中抽一張圖片打印原始數(shù)據(jù)看看這個(gè)28x28的矩陣?yán)锏降状娣帕耸裁?/p>
plt_grid(X_train[88].reshape(28,28))
非0的數(shù)值本身就已經(jīng)能看到數(shù)字的形狀了,是個(gè)3. 數(shù)值越接近1表示顏色越白歌亲,邊緣的顏色應(yīng)該是比較灰的菇用,而背景數(shù)值為0自然就是黑色。
打印相應(yīng)的標(biāo)簽出來(lái)也是3: [ 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
下面我再直接把圖片抽出來(lái)打印對(duì)比一下陷揪,果不其然, 和上圖長(zhǎng)的一模一樣惋鸥。
網(wǎng)絡(luò)架構(gòu) (Network Layouts)
數(shù)據(jù)準(zhǔn)備好后就開(kāi)始設(shè)計(jì)神經(jīng)網(wǎng)絡(luò)了, 簡(jiǎn)單一點(diǎn)就三層: input layer, hidden layer, output layer
input layer(輸入層)的節(jié)點(diǎn)就是要喂給神經(jīng)網(wǎng)絡(luò)的像素值杂穷,共784個(gè)節(jié)點(diǎn)。
hidden layer(隱藏層)可以讓網(wǎng)絡(luò)在抽象層次上學(xué)習(xí)特征卦绣,雖然我只放一層耐量,但是也可以有多層。少量隱藏層會(huì)得到淺層神經(jīng)網(wǎng)絡(luò)SNN滤港,隱藏層很多時(shí)就是深層神經(jīng)網(wǎng)絡(luò)DNN廊蜒。理論上,單隱藏層神經(jīng)網(wǎng)絡(luò)也可以逼近任何連續(xù)函數(shù)溅漾,只要神經(jīng)元數(shù)量夠多山叮。如果增加隱藏層或者隱藏層神經(jīng)元的數(shù)量,神經(jīng)網(wǎng)絡(luò)的容量會(huì)變大添履,空間表達(dá)能力會(huì)變強(qiáng)屁倔,但如果太多的話也容易過(guò)擬合。先暫定15個(gè)節(jié)點(diǎn)吧暮胧。
output layer(輸出層)有10個(gè)節(jié)點(diǎn)锐借,因?yàn)閳D片要分類映射到十個(gè)數(shù)字上。
權(quán)重和偏差 (Weights and Bias)
上圖中兩個(gè)神經(jīng)元之間的每一條連線都代表一個(gè)權(quán)重值往衷,神經(jīng)網(wǎng)絡(luò)通過(guò)不斷調(diào)整權(quán)重來(lái)逼近結(jié)果钞翔。首先把一組權(quán)重應(yīng)用在input layer的節(jié)點(diǎn)上,加上偏差值后得到hidden layer的節(jié)點(diǎn)席舍,然后對(duì)hidden layer的節(jié)點(diǎn)應(yīng)用另一組權(quán)重布轿,加上偏差值后最終得到output layer的節(jié)點(diǎn)。
先設(shè)置一下兩組權(quán)重和偏差的初始值来颤,后面再看如何更新這些權(quán)重和偏差驮捍。我定義一個(gè)模型訓(xùn)練函數(shù),目標(biāo)是不斷優(yōu)化權(quán)重和偏差得到最佳組合脚曾。第一組權(quán)重是一個(gè)784 x 15的矩陣,第二組權(quán)重是一個(gè)15 x 10的矩陣启具,用隨機(jī)函數(shù)生成一堆0到1之間的浮點(diǎn)數(shù)值本讥,偏差就先都設(shè)為0. 兩組權(quán)重都除以5是我在調(diào)參過(guò)程中發(fā)現(xiàn)初始權(quán)重?cái)?shù)值要更小一些效果比較好,隨便拍的一個(gè)數(shù)鲁冯。
input_layer_size = 28 * 28
hidden_layer_size = 15
output_layer_size = 10def train_model():
? ? # init weights and bias
? ? np.random.seed(1)
? ? W1 = np.random.random([input_layer_size, hidden_layer_size])/5 # 784 x 15
? ? b1 = np.zeros((1, hidden_layer_size))
? ? W2 = np.random.random([hidden_layer_size, output_layer_size])/5 # 15 x 10
? ? b2 = np.zeros((1, output_layer_size))
現(xiàn)在可以把權(quán)重W1也打印出來(lái)看看拷沸,我只拿輸入層第一個(gè)節(jié)點(diǎn)和隱藏層第一個(gè)簡(jiǎn)單之間的一根線來(lái)查看∈硌荩可以看到都是非常微小的隨機(jī)數(shù)字撞芍,最大不會(huì)超過(guò)0.2
plt_grid(W1.T[0].reshape(28,28))
后來(lái)在訓(xùn)練50000次之后可以用plt將十個(gè)數(shù)字權(quán)重的熱力圖可視化呈現(xiàn):
for i in range(10):
????plt.subplot(2, 5, i+1)
????weight = W1[:,i]
? ? plt.title(i)
????plt.imshow(weight.reshape([28,28]), cmap=plt.get_cmap('seismic'))
????frame1 = plt.gca()
????frame1.axes.get_xaxis().set_visible(False)
????frame1.axes.get_yaxis().set_visible(False)
激活函數(shù) (Activation Function)
Montreal 大學(xué)的 Bengio 教授在 ICML 2016 中給出了激活函數(shù)定義: 激活函數(shù)是映射 h:R→R,且?guī)缀跆幪幙蓪?dǎo)跨扮。
引入激活函數(shù)是為了將權(quán)值轉(zhuǎn)化為分類結(jié)果序无,有多重選擇: Sigmoid(S型), Tanh(雙切正切), ReLu(只保留非零), Softmax(歸一化) etc. 這些常用的激活函數(shù)多數(shù)都是非線性的验毡,為了彌補(bǔ)線性函數(shù)區(qū)分度不夠好的短板,而且激活函數(shù)要能保證數(shù)據(jù)輸入與輸出也是可微的帝嗡。本來(lái)我嘗試用Sigmoid作為激活函數(shù)晶通,但是可能由于我的實(shí)現(xiàn)方式導(dǎo)致效果不好,準(zhǔn)確率到70%多我就優(yōu)化不下去了哟玷,可能它本身由于軟飽和性也容易出現(xiàn)梯度消失的問(wèn)題狮辽,只好暫時(shí)放棄。
上面這句話我再解釋一下巢寡,Sigmoid就是處處可導(dǎo)的S型曲線喉脖,且兩側(cè)導(dǎo)數(shù)趨近于0,所以它是一個(gè)軟飽和函數(shù)抑月,而且左右兩側(cè)都是軟飽和树叽。一旦落入了軟飽和區(qū)f'(x)就接近于0了,無(wú)法再繼續(xù)傳遞梯度爪幻,這就是所謂的梯度消失菱皆。
現(xiàn)在輸入層到隱藏層我選擇了ReLu作為激活函數(shù),從圖形中看出它具備左側(cè)硬飽和的特性挨稿。
代碼實(shí)現(xiàn)如下
def relu(x):
? ? return np.maximum(x, 0)
從隱藏層到輸出層我選擇了softmax作為激活函數(shù)
序列中最大的那個(gè)數(shù)映射的分量逼近于 1, 其他就逼近于 0仇轻,非常適合多分類問(wèn)題。取指數(shù)是為了讓馬太效應(yīng)凸顯奶甘,大數(shù)進(jìn)一步放大篷店,同時(shí)也滿足了可導(dǎo)函數(shù)的需求。
Softmax代碼實(shí)現(xiàn)如下 (如果出現(xiàn)overflow的話可以參考scikit-learn源碼的實(shí)現(xiàn)方式)
def softmax(x):
????e_x = np.exp(x - np.max(x))
? ? return e_x / e_x.sum()
正向傳播(Forward Propagation)
正向傳播的計(jì)算就是把輸入層到隱藏層的節(jié)點(diǎn)與權(quán)重和偏差結(jié)合臭家,計(jì)算出輸出層節(jié)點(diǎn)的過(guò)程疲陕。
對(duì)于這個(gè)三層網(wǎng)絡(luò),假定x是包含一個(gè)單一訓(xùn)練樣本的列向量钉赁。則向量化的正向傳播步驟如下:(這個(gè)圖我畫(huà)完以后感覺(jué)用更嚴(yán)謹(jǐn)?shù)姆绞絹?lái)描述的話蹄殃,四個(gè)標(biāo)注應(yīng)該是隱藏層的輸入,隱藏層的輸出你踩,輸出層的輸入诅岩,輸出層的輸入,但懶得改圖了)
假設(shè)我要訓(xùn)練50000次带膜,train_model方法中加入正向傳播的循環(huán)代碼實(shí)現(xiàn)如下
batch= 50000
for i in range(0, batch):
? ? X = X_train[i]
? ? y = y_train[i]? ? input_layer = X.dot(W1)
? ? hidden_layer = relu(input_layer + b1)
? ? output_layer = np.dot(hidden_layer, W2) + b2
? ? output_probs = softmax(output_layer)
我還是繼續(xù)拿index為88的數(shù)字3圖片為例看看各層是什么數(shù)字
input_layer是輸入層矩陣與權(quán)重1矩陣相乘得到15維向量
hidden_layer是上一層加上偏差1作為ReLu的輸入計(jì)算出來(lái)的, 維度同上
此時(shí)權(quán)重2是15x10的矩陣
權(quán)重2和hidden_layer矩陣相乘加上偏差2得到十維向量output_layer, 這里最大的數(shù)字是第九位的20.09
output_layer作為softmax輸入計(jì)算后得到最終結(jié)果output_probs, 第九位被轉(zhuǎn)成了非常接近1的一個(gè)小數(shù)吩谦,也是序列中最大的數(shù)字。這是訓(xùn)練之初的數(shù)值膝藕,實(shí)際上最大的數(shù)字應(yīng)該在第四位式廷,所以此時(shí)誤差比較大。大概在學(xué)習(xí)3000次之后已經(jīng)能較大概率的在第四位逼近1
反向傳播 (Backward Propagation)
接下來(lái)自然就會(huì)思考芭挽,權(quán)重和偏差如何迭代優(yōu)化呢? 首先正向傳播計(jì)算出output_layer節(jié)點(diǎn)滑废,用損失函數(shù)計(jì)算一下和標(biāo)注y的差距蝗肪,根據(jù)損失大小返回來(lái)修正權(quán)重和偏差,這個(gè)通過(guò)鏈?zhǔn)椒▌t對(duì)多層復(fù)合函數(shù)求導(dǎo)的過(guò)程就是反向傳播策严,其目標(biāo)就是要最小化訓(xùn)練集上的累積誤差穗慕。
在前文"人工神經(jīng)元是如何模擬生物神經(jīng)元的"中我提到機(jī)器學(xué)習(xí)需要不斷調(diào)整weight和bias來(lái)逐步逼近預(yù)期的輸出值,而且必須保證weight和bias的微小變化也只會(huì)帶來(lái)輸出值的微小變化.
調(diào)參的過(guò)程就像打高爾夫一樣妻导,目標(biāo)是以最少的桿數(shù)將球打進(jìn)洞逛绵,如果過(guò)于謹(jǐn)慎可能耗費(fèi)的桿數(shù)太多,如果太過(guò)激進(jìn)可能球被打進(jìn)了沙池或者水坑倔韭,欲速則不達(dá)术浪。每一桿都要讓球離球洞更近,進(jìn)入果嶺的時(shí)候還要確保不要用力過(guò)度讓球跑過(guò)頭了寿酌。(見(jiàn)下文學(xué)習(xí)率)
在train_model方法中繼續(xù)實(shí)現(xiàn)這個(gè)逆向過(guò)程, 計(jì)算輸出層的error, 再將此error逆向傳播到隱藏層胰苏,最后根據(jù)隱藏層的error來(lái)對(duì)連接權(quán)重與偏差進(jìn)行調(diào)整,迭代循環(huán)下去不斷更新讓error收斂醇疼。核心邏輯是梯度下降的算法硕并,這里有三種選擇: 批量梯度下降(Batch Gradient Descent),隨機(jī)梯度下降(Stochastic Gradient Descent)和小批量梯度下降秧荆。
第一種方式遍歷完整訓(xùn)練集算出一個(gè)損失函數(shù)倔毙,然后更新參數(shù)再跑一次完整訓(xùn)練集,如此迭代循環(huán)乙濒,所以計(jì)算量很恐怖陕赃。第二種方式是每跑訓(xùn)練集中的一條數(shù)據(jù)就計(jì)算損失函數(shù)并更新參數(shù),速度比較快颁股,但收斂性不太好么库,可能會(huì)出現(xiàn)較多毛刺在最優(yōu)點(diǎn)附近搖擺。最后一個(gè)是前兩者的這種方案甘有,既不是跑全量數(shù)據(jù)而不是跑單個(gè)數(shù)據(jù)诉儒,而是拿一小批數(shù)據(jù)來(lái)計(jì)算損失函數(shù)更新參數(shù)。我先用SGD來(lái)跑亏掀,后面可以通過(guò)圖像看到毛刺的問(wèn)題允睹。
用數(shù)學(xué)語(yǔ)言來(lái)描述,梯度下降算法的核心是多元函數(shù)求微幌氮,針對(duì)每一個(gè)變量都分別求微,每一次迭代都用多元函數(shù)減去多元函數(shù)的微分與學(xué)習(xí)率的乘積胁澳。下面代碼中第一行設(shè)置的參數(shù)是學(xué)習(xí)率该互,這個(gè)參數(shù)是要在精度和速度之間找到平衡,學(xué)習(xí)率太大則訓(xùn)練的快但精度不夠(每次擊球都很大力)韭畸,學(xué)習(xí)率太小則提升精度但過(guò)于耗費(fèi)時(shí)間(每次擊球都小心翼翼輕輕揮桿保證精準(zhǔn))宇智,這里設(shè)置的學(xué)習(xí)率是固定的蔓搞,這樣的靜態(tài)設(shè)置顯然不會(huì)是最佳選擇,不過(guò)處于學(xué)習(xí)目的也夠了随橘。
learning_rate = .01
reg_lambda = .01output_error = (output_probs - y) / output_probs.shape[0]
hidden_error = np.dot(output_error, W2.T)
hidden_error[hidden_layer <= 0] = 0# gradient layer2 weights and bias
g2_weights = np.dot(hidden_layer.T, output_error)
g2_bias = np.sum(output_error, axis = 0, keepdims = True)# gradient layer1 weights and bias
g1_weights = np.dot(X.reshape(input_layer_size,1), hidden_error)
g1_bias = np.sum(hidden_error, axis = 0, keepdims = True)# gradient descent parameter update
W1 -= learning_rate * g1_weights
b1 -= learning_rate * g1_bias
W2 -= learning_rate * g2_weights
b2 -= learning_rate * g2_bias
正則化干擾 (Regularization Terms)
為了讓擬合效果更好可以在error后面加入正則干擾項(xiàng), 讓模型和樣本不要完全擬合喂分,當(dāng)出現(xiàn)欠擬時(shí)干擾項(xiàng)的影響要小,當(dāng)出現(xiàn)過(guò)擬時(shí)干擾項(xiàng)的影響要大机蔗。reg_lambda就是設(shè)置的擬合參數(shù)蒲祈,所以可以在更新w和b之前再加兩行代碼。
# add regularization terms
g2_weights += reg_lambda * W2
g1_weights += reg_lambda * W1
預(yù)測(cè)函數(shù)
這個(gè)邏輯很簡(jiǎn)單萝嘁,和訓(xùn)練集的正向傳播完全一樣梆掸,只不過(guò)輸入?yún)?shù)換成測(cè)試集數(shù)據(jù),代入已經(jīng)訓(xùn)練好的權(quán)重和偏差牙言,統(tǒng)計(jì)正確預(yù)測(cè)的比例酸钦。
input_layer = np.dot(X_test[:10000], W1)
hidden_layer = relu(input_layer + b1)
scores = np.dot(hidden_layer, W2) + b2
probs = softmax(scores)
print ('Test accuracy: {0}%'.format(accuracy(probs, y_test[:10000])))?
完成模型訓(xùn)練代碼后可以再定義一個(gè)交叉熵?fù)p失函數(shù)來(lái)觀察收斂效果,迭代到三五千次的時(shí)候其實(shí)已經(jīng)差不多了咱枉,后面的幾萬(wàn)次學(xué)習(xí)依然會(huì)不時(shí)出現(xiàn)一些毛刺影響準(zhǔn)確率卑硫。
def cross_entropy_loss(probs, y_onehot):
????indices = np.argmax(y_onehot, axis = 0).astype(int)
????predicted_prob = probs[np.arange(len(probs)), indices]
????log_preds = np.log(predicted_prob)
????loss = -1.0 * np.sum(log_preds) / len(log_preds)
????return loss
最后把上面的代碼重構(gòu)一下整合起來(lái)貼出完整代碼(不包含調(diào)試打印圖片和日志),預(yù)測(cè)準(zhǔn)確率90%, 下一篇我將改用TensorFlow對(duì)Fashion MNIST預(yù)測(cè)蚕断,并提升準(zhǔn)確率欢伏。
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
mndata = input_data.read_data_sets("MNIST-data/", one_hot=True)
X_train=mndata.train.images? ? # training set
y_train=mndata.train.labels? ?
X_test=mndata.test.images? ? # testing set
y_test=mndata.test.labels
input_layer_size = 28 * 28
hidden_layer_size = 15
output_layer_size = 10
reg_lambda = .01
learning_rate = .01
# visualize grid data of a matrix, zero cell shown as empty
def plt_grid(data):
? ? fig, ax = plt.subplots()
? ? fig.set_size_inches(30,30)
? ? width, height = data.shape
? ? #imshow portion
? ? imshow_data = np.random.rand(width, height, 2)
? ? ax.imshow(imshow_data, cmap=plt.cm.Pastel1, interpolation='nearest')
? ? for x in range(0, height):
? ? ? ? for y in range(0, width):
? ? ? ? ? ? if (data[y][x]>0):
? ? ? ? ? ? ? ? ax.text(x, y, np.round(data[y][x],8), va='center',
? ? ? ? ? ? ? ? ? ? ? ? ha='center', fontsize=20)
? ? plt.show()
def softmax(x):
? ? e_x = np.exp(x - np.max(x))
? ? return e_x / e_x.sum()
def relu(x):
? ? return np.maximum(x, 0)
def cross_entropy_loss(probs, y_onehot):
? ? indices = np.argmax(y_onehot, axis = 0).astype(int)
? ? predicted_prob = probs[np.arange(len(probs)), indices]
? ? log_preds = np.log(predicted_prob)
? ? loss = -1.0 * np.sum(log_preds) / len(log_preds)
? ? return loss
# init weights and bias
def init_weights_bias():
? ? np.random.seed(1)
? ? W1 = np.random.random([input_layer_size, hidden_layer_size])/5 # 784 x 15
? ? b1 = np.zeros((1, hidden_layer_size))
? ? W2 = np.random.random([hidden_layer_size, output_layer_size])/5 # 15 x 10
? ? b2 = np.zeros((1, output_layer_size))
? ? model = { 'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
? ? return model
# derivative weights and bias
def derivative_weights_bias(output_error, hidden_layer, X, model):
? ? W1, _, W2, _ = model['W1'], model['b1'], model['W2'], model['b2']
? ? hidden_error = np.dot(output_error, W2.T)
? ? hidden_error[hidden_layer <= 0] = 0
? ? # gradient layer2 weights and bias
? ? g2_weights = np.dot(hidden_layer.T, output_error)
? ? g2_bias = np.sum(output_error, axis = 0, keepdims = True)
? ? # gradient layer1 weights and bias
? ? g1_weights = np.dot(X.reshape(input_layer_size,1), hidden_error)
? ? g1_bias = np.sum(hidden_error, axis = 0, keepdims = True)
? ? # add regularization terms
? ? g2_weights += reg_lambda * W2
? ? g1_weights += reg_lambda * W1
? ? param = { 'dW1': g1_weights, 'db1': g1_bias, 'dW2': g2_weights, 'db2': g2_bias}
? ? return param
def forward_propagation(X, model):
? ? W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
? ? input_layer = np.dot(X, W1)?
? ? hidden_layer = relu(input_layer + b1)
? ? output_layer = np.dot(hidden_layer, W2) + b2
? ? probs = softmax(output_layer)?
? ? return probs, hidden_layer
def accuracy(predictions, labels):
? ? preds_correct_boolean =? np.argmax(predictions, 1) == np.argmax(labels, 1)
? ? correct_predictions = np.sum(preds_correct_boolean)
? ? accuracy = 100.0 * correct_predictions / predictions.shape[0]
? ? return accuracy
#predict test set
def predict(X, model):? ?
? ? W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']? ?
? ? input_layer = np.dot(X_test[:10000], W1)
? ? hidden_layer = relu(input_layer + b1)
? ? output_layer = np.dot(hidden_layer, W2) + b2
? ? probs = softmax(output_layer)
? ? print ('Test accuracy: {0}%'.format(accuracy(probs, y_test[:10000])))
# - batch: Size of passes through the training data for gradient descent
def train_model(batch, X, y):
? ? model = init_weights_bias()
? ? W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
? ? # Gradient descent. For each batch...
? ? for i in range(0, batch):
? ? ? ? output_probs, hidden_layer = forward_propagation(X[i], model)? ? ? ?
? ? ? ? output_error = (output_probs - y[i]) / output_probs.shape[0]
? ? ? ? param = derivative_weights_bias(output_error, hidden_layer, X[i], model)
? ? ? ? dW1, db1, dW2, db2 = param['dW1'], param['db1'], param['dW2'], param['db2']
? ? ? ? # gradient descent parameter update
? ? ? ? W1 -= learning_rate * dW1
? ? ? ? b1 -= learning_rate * db1
? ? ? ? W2 -= learning_rate * dW2
? ? ? ? b2 -= learning_rate * db2
? ? ? ? model = { 'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
? ? ? ? loss = cross_entropy_loss(output_probs, y[i])
? ? ? ? if (i % 2000 == 0):
? ? ? ? ? ? print('loss @ %d is %f' % (i, loss))
? ? return model
model = train_model(50000, X_train[:50000], y_train[:50000])
predict(X_test[:10000], model)
References:
Neural Network and Deep Learning
Not another MNIST tutorial with TensorFlow OReilly Media