隨後,江寒操作着電腦,心無旁騖,很快就進入了狀態。
夏雨菲也不再來打擾他,拿着手機,半躺在牀上,自己上網、聽歌。
江寒將高老師發送來的part012.rar和part013.rar下載下來,連同夏雨菲下載的前11個文件,放在了同一個文件夾中。
然後在第1個文件上點擊鼠標右鍵,選擇用WinRAR解壓縮,很快就得到了數據包。
一共兩個文件,train-images-idx3-ubyte與train-labels-idx1-ubyte。
idx3-ubyte和idx1-ubyte都是自定義的文件格式,官網上就有格式說明。
train-images文件大小超過1g,保存了20萬張手寫數字的圖片信息。
而train-labels中則存儲了20萬個標籤數據,與train-images一一對應。
和公開版本的MNIST不同,用於比賽的這個手寫數字數據集,數據量要大出好幾倍。
Kaggle官方將數據集分爲兩部分,訓練集train向參賽選手公開,而測試集test則內部保存。
比賽的形式很簡單,大家根據公開的訓練集,編寫自己的程序,提交給主辦方。
主辦方用不公開的測試集數據,對這些程序逐一進行測試,然後比較它們在測試集上的表現。
主要指標是識別率,次要指標是識別速度等。
這是“人工神經網絡”在這類競技場上的初次亮相,江寒可不想鎩羽而歸。
事實上,如果想追求更好的成績,最好的辦法,就是弄出卷積神經網絡(CNN)來。
那玩意是圖像識別算法的大殺器。
在“機器學習”這個江湖中,CNN的威力和地位,就相當於武俠世界中的倚天劍、屠龍刀。
CNN一出,誰與爭鋒!
只可惜,這個東西江寒現在還沒研究出來。
現上轎現扎耳朵眼,也來不及了。
再說,飯要一口口吃,搞研究也得一步步來。
跨度不能太大嘍,免得扯到蛋……
所以在這次比賽中,江寒最多隻能祭出“帶隱藏層的全連接神經網絡”(FCN)。
有了這個限制,就好比戴着鐐銬跳舞,給比賽平添了不少難度和變數。
那些發展了幾十年的優秀算法,也不見得會輸普通的FCN多少。
所以,現在妄言冠軍十拿九穩,還有點爲時過早。
不過,有挑戰才更有趣味性嘛,穩贏的戰鬥打起來有什麼意思呢?
江寒根據官網上找到的數據格式說明文檔,編寫了一個文件解析函數,用來從兩個train文件中提取數據。
train-images-idx3-ubyte的格式挺簡單的,從文件頭部連續讀取4個32位整形數據,就能得到4個參數。
用來標識文件類型的魔數m、圖片數量n、每張圖片的高度h和寬度w。
從偏移0016開始,保存的都是圖片的像素數據。
顏色深度是8位,取值範圍0~255,代表着256級灰度信息,每個像素用一個字節來保存。
然後,從文件頭中可以得知,每張圖片的分辨率都是28×28。
這樣每張圖片就需要784個字節來存儲。
很容易就能計算出每張圖片的起始地址,從而實現隨機讀取。
如果連續讀取,那就更簡單了,只需要每次讀取784個字節,一共讀取n次,就能恰好讀取完整個文件。
需要注意的是,圖像數據的像素值,在文件中存儲類型爲unsigned char型,對應的format格式爲B。
所以在Python程序中,在image_size(取值爲784)這個參數的後面,還要加上B參數,這樣才能讀取一整張圖片的全部像素。
如果忘了加B,則只能讀取一個像素……
train-labels-idx1-ubyte格式更加簡單。
前8個字節是兩個32位整形,分別保存了魔數和圖片數量,從偏移0009開始,就是unsigned byte類型的標籤數據了。
每個字節保存一張圖片的標籤,取值範圍0~9。
江寒很快就將標籤數據也解析了出來。
接下來,用Matplot的繪圖功能,將讀取出來的手寫數字圖片,繪製到屏幕上。
然後再將對應的標籤數據,也打印到輸出窗口,兩者一比較,就能很輕鬆地檢驗解析函數是否有問題。
將解析函數調試通過後,就可以繼續往下進行了。
首先要將圖片的像素信息壓縮一下,二值化或者歸一化,以提高運算速度,節省存貯空間。
像素原本的取值範圍是0~255。
二值化就是將大於閾值(通常設爲中間值127)的數值看做1,否則看做0,這樣圖片數據就轉換成了由0或者1組成的陣列。
歸一化也比較簡單,只需要將每個像素的取值除以最大值255,那麼每個像素的取值空間,就變成了介於0和1之間的浮點數。
兩種手段各有利弊,江寒決定每種都試一下,看看在實踐中,哪個表現更好一些。
由於江寒使用的是全連接網絡,而不是卷積神經網絡,所以還要將2維的圖片,轉換成1維的向量。
這個步驟非常簡單,將二維的圖片像素信息,一行接一行按順序存入一維數組就行。
事實上,在解析數據文件的時候,已經順便完成了這一步,所以並不需要額外的操作。
20萬張圖片,就是20萬行數據。
將這些數據按順序放入一個200000×784的二維數組裡,就得到了Feature。
Lable的處理比較簡單,定義一個具有20萬個元素的一維整形數組,按順序讀入即可。
江寒根據這次的任務需求,將20萬條訓練數據劃分成了2類。
隨機挑選了18萬個數據,作爲訓練集,剩餘2萬個數據,則作爲驗證集validate。
這樣一來,就可以先用訓練集訓練神經網絡,學習算法,然後再用未學習過的驗證集進行測試。
根據FCN網絡在陌生數據上的表現,就能大體推斷出提交給主辦方後,在真正的測試集上的表現。
寫完數據文件解析函數,接下來,就可以構建“帶隱藏層的全連接人工神經網絡”FCN了。
類似的程序,江寒當初爲了寫論文,編寫過許多次。
可這一次有所不同。
這是真正的實戰,必須將理論上的性能優勢,轉化爲實實在在、有說服力的成績。
因此必須認真一些。
打造一個神經網絡,首先需要確定模型的拓撲結構。
輸入層有多少個神經元?
輸出層有多少個神經元?
設置多少個隱藏層?
每個隱藏層容納多少個神經元?
這都是在初始設計階段,就要確定的問題。
放在MNIST數據集上,輸入層毫無疑問,應該與每張圖片的大小相同。
也就是說,一共有784個輸入神經元,每個神經元負責讀取一個像素的取值。
輸出層的神經元個數,一般應該與輸出結果的分類數相同。
數字手寫識別,是一個10分類任務,共有10種不同的輸出,因此,輸出層就應該擁有10個神經元。
當輸出層的某個神經元被激活時,就代表圖片被識別爲其所代表的數字。
這裡一般用softmax函數實現多分類。
先把來自上一層的輸入,映射爲0~1之間的實數,進行歸一化處理,保證多分類的概率之和剛好爲1。
然後用softmax分別計算10個數字的概率,選擇其中最大的一個,激活對應的神經元,完成整個網絡的輸出。
至於隱藏層的數量,以及其中包含的神經元數目,並沒有什麼一定的規範,完全可以隨意設置。
隱藏層越多,模型的學習能力和表現力就越強,但也更加容易產生過擬合。
所以需要權衡利弊,選取一個最優的方案。
起步階段,暫時先設定一個隱藏層,其中包含100個神經元,然後在實踐中,根據反饋效果慢慢調整……
確定了網絡的拓撲結構後,接下來就可以編寫代碼並調試了。
調試通過,就加載數據集,進行訓練,最後用訓練好的網絡,進行預測。
就是這麼一個過程。
江寒先寫了一個標準的FCN模板,讓其能利用訓練數據集,進行基本的訓練。
理論上來說,可以將18萬條數據,整體放進網絡中進行訓練。
但這種做法有很多缺點。
一來消耗內存太多,二來運算壓力很大,訓練起來速度極慢。
更嚴重的是,容易發生嚴重的過擬合。
要想避免這些問題,就要採取隨機批次訓練法。