編寫C語言版本的卷積神經網絡CNN之一:前言與Minst數據集

前言

卷積神經網絡是深度學習的基礎,可是學習CNN卻不是那麼簡單,雖然網絡上關於CNN的相關代碼不少,比較經典的是tiny_cnn(C++)、DeepLearnToolbox(Matlab)等等,但經過C語言來編寫CNN的卻比較少,本人由於想在多核DSP下運行CNN,因此便嘗試經過C語言來編寫,主要參考的代碼是DeepLearnToolbox的內容,DeepLearnToolbox是用Matlab腳本編寫,是我看過的最爲簡單的CNN代碼,代碼清晰,閱讀方便,很是適合新手入門學習。git

本文的CNN代碼是一個最基本的卷積網絡,主要用於手寫數字的識別,選擇的訓練測試是數據庫是Minst手寫數字庫,主要是包括了一個基本的多層卷積網絡框架、卷積層、Pooling層、及全鏈接的單層神經網絡輸出層,不過CNN其餘重要的概念如Dropout、ReLu等暫時沒有涉及,可是我的對於新手,學習卷積網絡的基本結構及其偏差反向傳播方法是徹底足夠。github

這裏要注意的是,本文的方法並非深度學習之父Yann. LeCun在1998年就已經提出的成熟算法LeNet-5卷積網絡,而只是DeepLearnToolbox內的cnn代碼的c語言實現,不過咱們會比較兩者之間的區別,由於兩者的基本原理是類似的。另外,爲了避免使博客篇幅過長,因此博客中貼的代碼並不完整,完整代碼請見附件。算法

這篇博客總共分爲四節:數據庫

第一節:前言,介紹項目結構及Minst數據集測試訓練數據集數組

第二節:主要介紹CNN的網絡結構、相關數據結構bash

第三節:重點介紹CNN學習訓練過程的偏差反向傳播方法,採用的是在線訓練方式網絡

第四節:CNN的學習及測試結果的比較數據結構


論文參考文獻:框架

Y. LeCun, L. Bottou, Y. Bengio and P. Haffner: Gradient-Based Learning Applied to Document Recognition, Proceedings of the IEEE, 86(11):2278-2324, November 1998函數


1、代碼結構

本文的CNN代碼是經過標準C編寫,也不須要調用任何三方庫文件,附件共享的文件是經過VS2010編譯的項目文件(這裏雖然是.cpp文件,但實際上徹底是用C編寫的,直接改爲.c文件是徹底可使用的),固然也能夠直接將相關的源文件導入到其餘IDE上,也是可以運行的。

文件結構:

cnn.cpp cnn.h 存在關於CNN網絡的函數、網絡結構等

minst.cpp minst.h 處理Minst數據庫的函數、數據結構等

mat.cpp mat.h 一些關於矩陣的函數,如卷積操做、180度翻轉操做等

main.cpp 主函數和測試函數


2、MINST數據庫

MINST數據庫是由Yann提供的手寫數字數據庫文件,其官方下載地址yann.lecun.com/exdb/mnist/

這個裏面還包含了對這個數據庫進行識別的各種算法的結果比較及相關算法的論文

數據庫的裏的圖像都是28*28大小的灰度圖像,每一個像素的是一個八位字節(0~255)

這個數據庫主要包含了60000張的訓練圖像和10000張的測試圖像,主要是下面的四個文件

上述四個文件直接解壓就可使用了,雖然數據未壓縮,可是咱們仍是須要將圖像提取出來,方便咱們進行操做

(1)存儲圖像數據的相關數據結構:

單張圖像結構及保存圖像的鏈表

圖像實際數字標號(0~9),數據庫裏是用一個八位字節來表示,不過爲了方便學習,須要用10位來表示。

這裏的10位表示網絡輸出層的10個神經元,某位爲1表示數字標號即爲該位。

(2)讀入圖像數據的相關函數

ImgArr read_Img(const char* filename) // 讀入圖像
{
    FILE  *fp=NULL;
    fp=fopen(filename,"rb");
    if(fp==NULL)
        printf("open file failed\n");
    assert(fp);
 
    int magic_number = 0;  
    int number_of_images = 0;  
    int n_rows = 0;  
    int n_cols = 0;  
    //從文件中讀取sizeof(magic_number) 個字符到 &magic_number  
    fread((char*)&magic_number,sizeof(magic_number),1,fp); 
    magic_number = ReverseInt(magic_number);  
    //獲取訓練或測試image的個數number_of_images 
    fread((char*)&number_of_images,sizeof(number_of_images),1,fp);  
    number_of_images = ReverseInt(number_of_images);    
    //獲取訓練或測試圖像的高度Heigh  
    fread((char*)&n_rows,sizeof(n_rows),1,fp); 
    n_rows = ReverseInt(n_rows);                  
    //獲取訓練或測試圖像的寬度Width  
    fread((char*)&n_cols,sizeof(n_cols),1,fp); 
    n_cols = ReverseInt(n_cols);  
    //獲取第i幅圖像,保存到vec中 
    int i,r,c;
 
    // 圖像數組的初始化
    ImgArr imgarr=(ImgArr)malloc(sizeof(MinstImgArr));
    imgarr->ImgNum=number_of_images;
    imgarr->ImgPtr=(MinstImg*)malloc(number_of_images*sizeof(MinstImg));
 
    for(i = 0; i < number_of_images; ++i)  
    {  
        imgarr->ImgPtr[i].r=n_rows;
        imgarr->ImgPtr[i].c=n_cols;
        imgarr->ImgPtr[i].ImgData=(float**)malloc(n_rows*sizeof(float*));
        for(r = 0; r < n_rows; ++r)      
        {
            imgarr->ImgPtr[i].ImgData[r]=(float*)malloc(n_cols*sizeof(float));
            for(c = 0; c < n_cols; ++c)
            { 
                // 由於神經網絡用float型計算更爲精確,這裏咱們將圖像像素轉爲浮點型
                unsigned char temp = 0;  
                fread((char*) &temp, sizeof(temp),1,fp); 
                imgarr->ImgPtr[i].ImgData[r][c]=(float)temp/255.0;
            }  
        }    
    }
 
    fclose(fp);
    return imgarr;
}複製代碼


(3)讀入圖像數據標號

LabelArr read_Lable(const char* filename)// 讀入圖像
{
    FILE  *fp=NULL;
    fp=fopen(filename,"rb");
    if(fp==NULL)
        printf("open file failed\n");
    assert(fp);
 
    int magic_number = 0;  
    int number_of_labels = 0; 
    int label_long = 10;
 
    //從文件中讀取sizeof(magic_number) 個字符到 &magic_number  
    fread((char*)&magic_number,sizeof(magic_number),1,fp); 
    magic_number = ReverseInt(magic_number);  
    //獲取訓練或測試image的個數number_of_images 
    fread((char*)&number_of_labels,sizeof(number_of_labels),1,fp);  
    number_of_labels = ReverseInt(number_of_labels);    
 
    int i,l;
 
    // 圖像標記數組的初始化
    LabelArr labarr=(LabelArr)malloc(sizeof(MinstLabelArr));
    labarr->LabelNum=number_of_labels;
    labarr->LabelPtr=(MinstLabel*)malloc(number_of_labels*sizeof(MinstLabel));
 
    for(i = 0; i < number_of_labels; ++i)  
    {
        // 數據庫內的圖像標記是一位,這裏將圖像標記變成10位,10位中只有惟一一位爲1,爲1位便是圖像標記  
        labarr->LabelPtr[i].l=10;
        labarr->LabelPtr[i].LabelData=(float*)calloc(label_long,sizeof(float));
        unsigned char temp = 0;  
        fread((char*) &temp, sizeof(temp),1,fp); 
        labarr->LabelPtr[i].LabelData[(int)temp]=1.0;    
    }
 
    fclose(fp);
    return labarr;    
}複製代碼


項目代碼地址:tostq/DeepLearningC

相關文章
相關標籤/搜索