天天看點

Caffe實戰(九):MNIS(手寫體數字識别)例程 - 懸崖邊上打坐

Caffe實戰(九):MNIS(手寫體數字識别)例程

MNlST資料集介紹

MNlST (MiXed Nationallnstìtute of Standards and Tecbnology )是一個大型的手寫體數字資料庫,廣泛用于機器學習領域的訓練和測試,由紐約大學Yann LeCun 教授整理。MNlST 包括60000個訓練集和10000個測試集,每張圖都已經進行尺寸歸一化、數字居中處理,固定尺寸為28像素x28像素。如下圖所示:

Caffe實戰(九):MNIS(手寫體數字識别)例程 - 懸崖邊上打坐

官方網址:http://yann.lecun.com/exdb/mnist/

資料檔案格式可以在mnist網站上檢視(下載下傳位址)

另外,将二進制的mnist資料庫格式轉化為相對簡單的CSV資料檔案格式,可以參考 https://pjreddie.com/projects/mnist-in-csv/

def convert(imgf, labelf, outf, n):
    f = open(imgf, "rb")
    o = open(outf, "w")
    l = open(labelf, "rb")
 
    f.read(16)
    l.read(8)
    images = []
 
    for i in range(n):
        image = [ord(l.read(1))]
        for j in range(28*28):
            image.append(ord(f.read(1)))
        images.append(image)
 
    for image in images:
        o.write(",".join(str(pix) for pix in image)+"\n")
    f.close()
    o.close()
    l.close()
 
convert("train-images-idx3-ubyte", "train-labels-idx1-ubyte",
        "mnist_train.csv", 60000)
convert("t10k-images-idx3-ubyte", "t10k-labels-idx1-ubyte",
        "mnist_test.csv", 10000)      

CSV檔案中,每一行為一幅圖檔資訊,第一個值為label值,後面依次是圖像的28*28個像素值(行排列),每個值之間用\',\'區分開。

這個網站還提供了兩個已将轉換好的CSV檔案:

  • 訓練集: http://www.pjreddie.com/media/files/mnist_train.csv
  • 測試集: http://www.pjreddie.com/media/files/mnist_test.csv

利用matlab或者python很容易顯示這些圖像資訊,例如

# load the mnist test data CSV fle into a list
test_data_file = open("mnist_test.csv",\'r\')
test_data_list = test_data_file.readlines()
test_data_file.close()
 
# get the first test record
all_values = test_data_list[0].split(\',\')
print(all_values[0])
 
# asfarray()轉換為浮點型的數組
image_array = numpy.asfarray(all_values[1:]).reshape((28,28))
matplotlib.pyplot.imshow(image_array, cmap=\'Greys\', interpolation=\'None\')
matplotlib.pyplot.show()      

說明:

  • 執行data/mnist/get_mnist.sh腳本可以可得MNIST資料集
  • 圖檔檔案中像素按照行組織,像素值0表示背景(白色),像素值255表示前景(黑色);與一般灰階圖像的定義相反,在測試手寫圖像是一定要注意進行變換。
  • 檔案格式中magic number(魔數)其實它就是一個校驗數,用來判斷這個檔案是不是MNIST裡面的image檔案或者label檔案;
  • 下載下傳到的原始資料集為二進制檔案,需要轉換為LEVELDB或LMDB才能被Caffe識别;在caffe根目錄下執行腳本./examples/minist/create_mnist.sh即可(源碼為convert_mnist_data.cpp);

TIPS: Caffe 為什麼采用LMDB、LEVELDB. 而不是直接讀取原始資料?

一方面,資料類型多種多樣(有二進制檔案、文本檔案、編碼後的圖像檔案如JPEG 或PNG、網絡爬取的資料等),不可能用一套代碼實作所有類型的輸入資料讀取,轉換為統一格式可以簡I化資料讀取層的實作:另一方面,使用LMDB 、LEVELDB 可以提高磁盤IO使用率。

【Bengio組封裝好的資料包】--提供了将圖檔轉會為lmdb的操作過錯

    本小節參考《深度學習與計算機視覺:算法、架構應用與代碼實作》

原版MNIST 下載下傳很慢,也可以下載下傳Bengio組封裝好的資料包,下載下傳位址

wget https://deeplearning.net/data/mnist/mnist.pkl.gz

mnist.pkl.gz壓縮包是資料的訓練集、驗證集和測試集用pickle到處的檔案被壓縮為gzip格式,用python中的gzip子產品讀取檔案中的資料。

檔案中資料格式:每個資料集是一個元組,第一個元素存儲的是手寫數字圖檔,每張28*28=784一維浮點型numpy數組排列;(單通道灰階圖檔按行展開,1代表白,0代表黑);元組中第二個元素是圖檔對應的标簽,是一個一維的整型numpy數組。

将以上資料集轉換為圖檔的代碼如下:

#!/usr/bin/env python
# -*-coding:UTF-8-*-
 
import os
import pickle
import gzip
from matplotlib import pyplot
 
# 從原始檔案讀取MNIST資料
print(\'Loading data from mnist.pkl.gz ...\')
with gzip.open(\'mnist.pkl.gz\', \'rb\') as f:
    train_set, valid_set, test_set = pickle.load(f)
# 建立mnist檔案夾
imgs_dir = \'mnist\'
os.system(\'mkdir -p {}\'.format(imgs_dir))
datasets = {\'train\': train_set, \'val\': valid_set, \'test\': test_set}
# 轉換train\val和test資料集
for dataname, dataset in datasets.items():
    print(\'Converting {} dataset ...\'.format(dataname))
    data_dir = os.sep.join([imgs_dir, dataname])
 
    # 在mnist檔案夾下建立對應子檔案夾
    os.system(\'mkdir -p {}\'.format(data_dir))
 
    # i代表資料的序号,用zip()函數讀取對應位置的圖檔和标簽
    for i, (img, label) in enumerate(zip(*dataset)):
        # 格式化生成檔案的名字,第一個字段是序号,第二個字段是數字值
        filename = \'{:0>6d}_{}.jpg\'.format(i, label)
        filepath = os.sep.join([data_dir, filename])
 
        # 将展開的一維叔祖還原為二維圖像
        img = img.reshape((28, 28))
        # 用pyplot儲存可以自動歸一化生存像素值在0-255之間的灰階圖檔
        pyplot.imsave(filepath, img, cmap=\'gray\')
        if (i+1) % 10000 == 0:
            print(\'{} images converted!\'.format(i+1))      

如果用caffe自帶的工具convert_imageset工具将圖檔轉會為LMDB,則需要包含圖檔路徑和對應标簽的檔案。生成如下所是:

#!/usr/bin/env python
# -*-coding:UTF-8-*-
 
import os
import sys
 
# 輸入路徑,是包含mnist圖檔檔案的路徑
input_path = sys.argv[1].rstrip(os.sep)
# 輸出的檔案名,内容就是圖檔路徑-标簽的清單
output_path = sys.argv[2]
# 列出輸入路徑下所有的檔案名
filenames = os.listdir(input_path)
with open(output_path, \'w\') as f:
for filename in filenames:
    # 完整的圖檔檔案路徑
    filepath = os.sep.join([input_path, filename])
 
    # 第2個字段的值就是标簽
    label = filename[:filename.rfind(\'.\')].split(\'_\')[1]
    # 生成路徑-标簽的格式并寫入檔案
    line = \'{} {}\n\'.format(filepath, label)
    f.write(line)

# 調用方
python gen_caffe_imglist.py mnist/train train.txt
python gen_caffe_imglist.py mnist/val val.txt
python gen_caffe_imglist.py mnist/test test.txt      

convert_imageset調用方式:

build/tools/convert_imageset 輸入圖檔路徑 圖檔+标簽清單檔案 輸出lmdb路徑
 
convert_imageset ./ train.txt train_lmdb
 
--gray是單通道讀取灰階圖像的選項;
--shuffle作用是打亂檔案清單順序。      

LeNet-5模型

LeNet-5是一個經典的深度學習模型,該模型是Yann LeCun 最早提出的,并應用到郵政編碼識别中。

大話CNN經典模型:LeNet https://my.oschina.net/u/876354/blog/1632862

關于LeNet-5模型的描述檔案可以參考:examples/mnist/lenet_train_test.prototxt

資料源mnist負責從預處理得到的lmdb資料庫中讀取圖像資料data和标簽資料label圖像資料送入後續CNN結構中進行處理。CNN結構包括一組由卷積層conv(l,2)+下采樣層pool(l ,2)交替形成的特征層,以及兩個全連接配接層ipl 和ip2 (類似于多層感覺器結構〉。對ip2的輸出進一步同标簽資料label對比,可計算分類準确率accuracy和損失值loss。LeNet 的設計蘊涵了CNN的精髓,了解該模型對設計更大模型(如ImageNet資料集上的AlexNet 、oogleNet 、VGG 等)有參考價值。

訓練超參數過程

mnist資料訓練測試操作指令

【windous下】

mnist資料轉換為lmdb
convert_mnist_data.exe ..\\..\\..\\data\mnist\train-images.idx3-ubyte ..\\\..\\..\\data\\mnist\\train-labels.idx1-ubyte ..\\..\\..\\examples\\mnist\\mnist_train_lmdb
convert_mnist_data.exe ..\\..\..\data\t10k-images.idx3-ubyte ..\\..\\..\\data\\mnist\\t10k-labels.idx1-ubyte ..\\..\\..\\examples\\mnist\\mnist_test_lmdb
模型訓練
caffe.exe train --solver=..\\..\\..\\examples\\mnist\\lenet_solver.prototxt
模型測試
caffe.exe test -model ..\\..\\..\\examples\\mnist\\lenet_train_test.prototxt -weights ..\\..\\..\\examples\\mnist\\lenet_iter_10000.caffemodel -iterations 100      

Linux下直接調用相應的腳本

mnist資料轉換為lmdb  create_mnist.sh
模型訓練             train_lenet.sh
模型測試             caffe.bin test -model ..\\..\\examples\\mnist\\lenet_train_test.prototxt -weights ..\\..\\examples\\mnist\\lenet_iter_10000.caffemodel -iterations      

最終訓練好的模型權值儲存在examples/mnist/lenet_iter_10000.caffemodel檔案中,訓練狀态(目前疊代次數,網絡描述檔案,權值曆史資訊等,包含從上次停止點回複訓練模型所需要的資訊)儲存在examples/mnist/lenet_iter_10000.solverstate檔案中。

特别注意相對路徑問題:

  • 執行腳本和可執行程式在同一目錄下:傳入資料參數的路徑是相對本目錄的;是以從目前目錄出發去尋找指定的位置;
  • 執行腳本和可執行程式不在同一目錄下:傳入資料參數的路徑依然是相對本目錄的,而不是相對exe所在的路徑。這個地方一定要特别注意。
  • 不管可執行程式在哪個目錄下,也不管資料在哪個目錄下,隻要記住所有的路徑都是相對于目前路徑尋找的。

例如上面的模型訓練:

# caffe\\Build\\x64\\Release\\caffe.exe
# 可執行腳本和可執行程式在同一目錄下,腳本内容如下
caffe.exe train --solver=..\\..\\..\\examples\\mnist\\lenet_solver.prototxt
 
# 可執行腳本和可執行程式不在同一目錄下,例如
# 可執行程式在caffe\\Build\\x64\\Release\\caffe.exe
# 可執行腳本在caffe\\examples\\mnist\\
# lenet_solver.prototxt也在caffe\\examples\\mnist\\
..\\..\\Build\\x64\\Release\\caffe.exe --solver=lenet_solver.prototxt      

【ubuntu平台】

caffe.bin用法介紹
$ . / buìld/tools/caffe.þin
caffe.bin: command line brew
usage: caffe <command><args>
commands:
train 訓練或微調一個模型
test 對一個模型打分
del1 i ce_query 顯示GPU診斷資訊
time 評估模型執行時間
 
 
Flags from tools/caffe.cpp:
-gpu (可選參數,給定時運作在GPU模式,\' -gpu all\'則表示運作在所有可用GPU裝置上,此時真正訓練批量大小是N*B,N為指定的GPU裝置數目)
-iterations (循環疊代次數,預設為50)
-model (指定模型定義文本檔案名,*.prototxt)
-sighup_effect (可收到SIGHUP信号時要采取的動作,可選項:snapshot,stop或none,預設為snapshot,即打快照)
-sigint_effect (當收到SIGINT 信号時要采取的動作,可選項同上,預設stop>
-snapshot (恢複訓練時需要指定上次中止的快照,*.solverstate)
-solver (指定求解器文本檔案名,*.prototxt)
-weights (指定用于微調的預訓練權值,*.caffemodel,不可與snapshot同時出現)
      

在linux上,caffe團隊給出非常詳細的例子,而且各個路徑已經配置好,直接運作相應的腳本即可。

# 切換到caffe目錄
 
# 下載下傳資料
sh ./data/mnist/get_mnist.sh
 
# 轉換為lmdb
sh ./examples/mnist/create_mnist.sh
 
# 訓練網絡模型
sh ./examaples/mnist/train_lenet.sh      

出現的問題彙總:

(1)找不到庫:在執行create_mnist.sh進行轉換時,由于需要調用./build/examples/mnist/convert_mnist.bin,提示找不到libglog.so.o檔案,這是由于本人将所有的依賴庫安裝到了/home/gd/local_install/,導緻LD加載器沒有找到相應的庫。最簡單的是在~/.bashrc檔案中添加:

export LD_LIBRARY_PATH=/home/gd/local_install/lib:$LD_LIBRARY_PATH

export LD_LIBRARY_PATH=/home/gd/local_install/cuda-10.2/lib64:$LD_LIBRARY_PATH

或者将其添加到/etc/ld.so.conf檔案中,并執行ldconfig指令立即生效。

(2)在windous下無論是在工程中,還是調用convert_mnist_data.exe,隻要是相對路徑或者絕對路徑,都報出讀取檔案失敗的錯誤;但是把資料和exe放在同一目錄下,則正常運作。原因????

原因:

1)檢視檔案名是否正确。書中訓資料集的名稱諸如train-images-idx3-ubyte形式,而實際下載下傳的資料集的名稱諸如train-images.idx3-ubyte形式;一定要以實際的名稱為準。

2)在windows下編寫cmd或bat腳本時,不要用自帶的記事本編輯,會在開頭加入BOM标記,導緻很多時候不能識别中文路徑或者其它未知的錯誤。是以,最後用notepad++或者ultraEdit轉換為UTF-8編碼;或者幹脆用這兩個記事本采用UTF-8編碼形式編寫腳本内容。

特别注意:在中文Windows系統中,如果一個文本檔案是UTF-8編碼的,那麼在CMD.exe指令行視窗(所謂的DOS視窗)中不能正确顯示檔案中的内容。在預設情況下,指令行視窗中使用的代碼頁是中文或者美國的,即編碼是中文字元集或者西文字元集。如果想要正确顯示有兩種方法:

方法一:修改cmd視窗預設使用的代碼頁,即改為支援UTF-8編碼;

方法二:編寫文檔時采用GB2132等中文集(在notepad++中可以選擇編碼的字元集),或者轉換為中文集。