(原理和程序基本框架請參見前一篇 "用C語言構(gòu)建了一個簡單的神經(jīng)網(wǎng)路")
1.準(zhǔn)備訓(xùn)練和測試數(shù)據(jù)集
從http://yann.lecun.com/exdb/mnist/下載手寫數(shù)字訓(xùn)練數(shù)據(jù)集, 包括圖像數(shù)據(jù)train-images-idx3-ubyte.gz 和標(biāo)簽數(shù)據(jù) train-labels-idx1-ubyte.gz.
分別將他們解壓后放在本地文件夾中,解壓后文件名為train-images-idx3-ubyte和train-labels-idx1-ubyte. 訓(xùn)練數(shù)據(jù)集一共包含了6萬個手寫數(shù)字灰度圖和對應(yīng)的標(biāo)簽.
為圖方便,我們直接從訓(xùn)練數(shù)據(jù)集中提取5000個作為測試數(shù)據(jù).當(dāng)然,實際訓(xùn)練數(shù)據(jù)中并不包含這些測試數(shù)據(jù).
2.設(shè)計神經(jīng)網(wǎng)絡(luò)
采用簡單的三層全連接神經(jīng)網(wǎng)絡(luò),包括輸入層(wi),中間層(wm)和輸出層(wo).這里暫時不使用卷積層,下次替換后進(jìn)行比較.
輸入層: 一共20個神經(jīng)元,每一張手寫數(shù)字的圖片大小為28x28,將全部展平后的784個灰度數(shù)據(jù)歸一化,即除以255.0, 使其數(shù)值位于[0 1]區(qū)間,這樣可以防止數(shù)據(jù)在層層計算和傳遞后變得過分大.將這784個[0 1]之間的數(shù)據(jù)與20個神經(jīng)元進(jìn)行全連接.神經(jīng)元激活函數(shù)用func_ReLU.
中間層: 一共20個神經(jīng)元,與輸入層的20個神經(jīng)元輸出進(jìn)行全連接.神經(jīng)元激活函數(shù)用func_ReLU.
輸出層: 一共10個神經(jīng)元,分別對應(yīng)0~9數(shù)字的可能性,與中間層的20個神經(jīng)元輸出進(jìn)行全連接.層的激活函數(shù)用func_softmax.
特別地,神經(jīng)元的激活函數(shù)在new_nvcell()中設(shè)定,層的激活函數(shù)直接賦給nerve_layer->transfunc.
損失函數(shù): 采用期望和預(yù)測值的交叉熵?fù)p失函數(shù)func_lossCrossEntropy. 損失函數(shù)在nvnet_feed_forward()中以參數(shù)形式輸入.
3.訓(xùn)練神經(jīng)網(wǎng)絡(luò)
由于整個程序是以nvcell神經(jīng)元結(jié)構(gòu)為基礎(chǔ)進(jìn)行構(gòu)建的,其不同于矩陣/張量形式的批量數(shù)據(jù)描述,因此這個神經(jīng)網(wǎng)絡(luò)只能以神經(jīng)元為單位,逐個逐層地進(jìn)行前向和反向傳導(dǎo).
相應(yīng)地,這里采用SGD(Stochastic Gradient Descent)梯度下降更新法,即對每一個樣本先進(jìn)行前向和反向傳導(dǎo)計算,接著根據(jù)計算得到的梯度值馬上更新所有參數(shù).與此不同,mini-batch GD采用小批量樣本進(jìn)行前向和反向傳導(dǎo)計算,然后根據(jù)累積的梯度數(shù)值做1次參數(shù)更新.顯然,采用SGD方法參數(shù)更新更加頻繁,計算時間相應(yīng)也變長了.不過,據(jù)網(wǎng)文分析,采用SGD也更容易趨近全局最優(yōu)解,盡管逼近的途徑會比較曲折.本文程序中的分批計算是為了方便監(jiān)控計算過程和打印中間值.(當(dāng)然,要實現(xiàn)mini-batch GD也是可以的,先完成一批量樣本的前后傳導(dǎo)計算,期間將各參數(shù)的梯度累計起來,? 最后取其平均值更新一次參數(shù).)
這里使用平均損失值mean_err<=0.0025來作為訓(xùn)練的終止條件,為防止無法收斂到此數(shù)值,同時設(shè)置最大的epoch計數(shù).
訓(xùn)練的樣本數(shù)量由TRAIN_IMGTOTAL來設(shè)定, 訓(xùn)練時,先讀取一個樣本數(shù)據(jù)和一個標(biāo)簽,分別存入到data_input[28*28]和data_target[10], 為了配合應(yīng)用softmax函數(shù),這里data_target[]是one-hot編碼格式.讀入樣本數(shù)據(jù)后先進(jìn)行前向傳導(dǎo)計算nvnet_feed_forward(),接著進(jìn)行反向傳導(dǎo)計算nvnet_feed_backward(), 最后更新參數(shù)nvnet_update_params(), 這樣就完成了一個樣本的訓(xùn)練.如此循環(huán)計算,完成一次所有樣本的訓(xùn)練(epoch)后計算mean_err, 看是否達(dá)到預(yù)設(shè)目標(biāo).
4.測試訓(xùn)練后的神經(jīng)網(wǎng)絡(luò)
訓(xùn)練完成后,對模型進(jìn)行簡單評估.方法就是用訓(xùn)練后的模型來預(yù)測(predict)或推理(infer)前面的測試數(shù)據(jù)集中的圖像數(shù)據(jù),將結(jié)果與對應(yīng)的標(biāo)簽值做對比.
同樣,將一個測試樣本加載到data_input[], 跑一次nvnet_feed_forward(),直接讀取輸出層的wo_layer->douts[k] (k=0~9),如果其值大于0.5,就認(rèn)為模型預(yù)測圖像上的數(shù)字是k.
5.小結(jié)
取5萬條訓(xùn)練樣本進(jìn)行訓(xùn)練,訓(xùn)練后再進(jìn)行測試,其準(zhǔn)確率可接近94%.
與卷積神經(jīng)網(wǎng)絡(luò)相比較,為達(dá)到相同的結(jié)果,全連接的神經(jīng)網(wǎng)絡(luò)的所需要的訓(xùn)練時間會更長.
6.實驗和改進(jìn)
可以先將28*28的圖片下采樣到14*14后再連接到輸入層.這樣可以提高速度.
可以增加神經(jīng)網(wǎng)絡(luò)層數(shù)進(jìn)行驗證。
可以試著調(diào)整輸入層和中間層的神經(jīng)元數(shù)目.
也可以試著調(diào)整單個神經(jīng)元的輸入連接方式...
源代碼:
https://github.com/midaszhou/nnc
下載后編譯:
make TEST_NAME=test_nnc2
參考資料:
1.? MNIST手寫數(shù)字集 http://yann.lecun.com/exdb/mnist/
2.? peterroelants神經(jīng)網(wǎng)絡(luò)例子 https://peterroelants.github.io/posts/neural-network-implementation-part05/文章來源:http://www.zghlxwxcb.cn/news/detail-619305.html
下一篇: 用C語言構(gòu)建一個數(shù)字識別卷積神經(jīng)網(wǎng)絡(luò)文章來源地址http://www.zghlxwxcb.cn/news/detail-619305.html
到了這里,關(guān)于用C語言構(gòu)建一個手寫數(shù)字識別神經(jīng)網(wǎng)絡(luò)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!