1. 程式人生 > >matlab手寫神經網路實現識別手寫數字

matlab手寫神經網路實現識別手寫數字

實驗說明

一直想自己寫一個神經網路來實現手寫數字的識別,而不是套用別人的框架。恰巧前幾天,有幸從同學那拿到5000張已經貼好標籤的手寫數字圖片,於是我就嘗試用matlab寫一個網路。

  • 實驗資料:5000張手寫數字圖片(.jpg),圖片命名為1.jpg,2.jpg…5000.jpg。還有一個放著標籤的excel檔案。

  • 資料處理:前4000張作為訓練樣本,後1000張作為測試樣本。

  • 圖片處理:用matlab的imread()函式讀取圖片的灰度值矩陣(28,28),然後把每張圖片的灰度值矩陣reshape為(28*28,1),然後把前4000張圖片的灰度值矩陣合併為x_train,把後1000張圖片的灰度值矩陣合併為x_test。

數字圖片截圖


數字標籤截圖

神經網路設計

  • 網路層設計:一層隱藏層,一層輸出層

  • 輸入層:一張圖片的灰度值矩陣reshape後的784個數,也就是x_train中的某一列

  • 輸出層:(10,1)的列向量,其中列向量中最大的數所在的索引就是預測的數字

  • 激勵函式:sigmoid函式(公式)

  • 更新法則:後向傳播演算法(參考

  • 一點說明:這裡的訓練我分別用了普通梯度下降法和mini_batch(batch size 為10)梯度下降法來實現

  • 測試:用了兩種方式表示正確率,一是統計預測正確的個數,而是利用matlab的plotconfusion函式

網路實現

全部實現包括5個函式(gedata.m / layerout.m / mytrain.m / mytrain_mini.m / test.m)和一個main.m檔案。

讀取資料(getdata.m)
function[x_train,y_train,x_test,y_test]=getdata()
%把圖片變成畫素矩陣
%path :圖片路徑 
% x_train:訓練樣本畫素矩陣(784,4000)
%y_train:訓練樣本標籤(10,4000)
%x_test:測試樣本畫素矩陣(784,1000)
%y_test:測試樣本標籤(10,1000)


% photopath = './photo/';
% snames=dir([photopath  '*' '.jpg'])%get all filenames in photopath
% l = length(snames)
% % %get x_ data % x_train = []; % x_test = []; % % for i=1:4000 % iname=[photopath snames(i).name] %the path of jpg % x = imread(iname); % the shape of x is (28,28) % x = reshape(x,784,1); %reshape x to (784,1) % x_train = [x_train,x]; % end % % for k=4001:5000 % kname=[photopath snames(k).name]; %the path of jpg % x = imread(kname); %the shape of x is (28,28) % x = reshape(x,784,1); %reshape x to (784,1) % x_test = [x_test,x]; % end x_train=[]; for i=1:4000 x=im2double(imread(strcat(num2str(i),'.jpg'))); x=reshape(x,784,1); x_train=[x_train,x]; end x_test =[]; for k=4001:5000 x=im2double(imread(strcat(num2str(k),'.jpg'))); x=reshape(x,784,1); x_test=[x_test,x]; end data=xlsread('label.xlsx'); y_train=data(:,1:4000); y_test = data(:,4001:5000); x_train; y_train; x_test; y_test; end

這裡踩了一個坑。我本來讀取圖片,是按目錄來讀取的,然後訓練出來的效果一直不好。一度懷疑自己的更新函式寫錯了,改了很久,才發現按目錄讀取的圖片順序是錯誤的!按目錄讀取的圖片並不是按1,2,3…這樣讀的,而是按下面的順序讀取的,這樣就和label對不上了!!!

layerout函式
function [y] = layerout(w,b,x)
%output function
y = w*x + b;
n = length(y);
for i =1:n
    y(i)=1.0/(1+exp(-y(i)));
end
y;
end
訓練一(mytrain.m)
function[w,b,w_h,b_h]=mytrain(x_train,y_train)
%train function:設定一個隱藏層,784-->隱藏層神經元個數-->10
%x_train:訓練樣本的畫素資料
%y_train:訓練樣本的標籤
%w:輸出層權重
%b:輸出層偏置
%w_h:隱藏層權重
%b_h:隱藏層偏置
%step:迴圈步數

step=input('迭代步數:');
a=input('學習因子:');
in = 784; %輸入神經元個數
hid = input('隱藏層神經元個數:');%隱藏層神經元個數
out = 10; %輸出層神經元個數
o =1;

w = randn(out,hid);
b = randn(out,1);
w_h =randn(hid,in);
b_h = randn(hid,1);


for i=0:step
    %打亂訓練樣本
    r=randperm(4000);
    x_train = x_train(:,r);
    y_train = y_train(:,r);

    for j=1:4000
        x = x_train(:,j);
        y = y_train(:,j);

        hid_put = layerout(w_h,b_h,x);
        out_put = layerout(w,b,hid_put);

        %更新公式的實現
        o_update = (y-out_put).*out_put.*(1-out_put);
        h_update = ((w')*o_update).*hid_put.*(1-hid_put);

        outw_update = a*(o_update*(hid_put'));
        outb_update = a*o_update;
        hidw_update = a*(h_update*(x'));
        hidb_update = a*h_update;

        w = w + outw_update;
        b = b+ outb_update;
        w_h = w_h +hidw_update;
        b_h =b_h +hidb_update;
    end
end  
end
訓練二(mytrain_mini.m)
function[w,b,w_h,b_h]=mytrain_mini(x_train,y_train)
%train function:設定一個隱藏層,784-->隱藏層神經元個數-->10
%x_train:訓練樣本的畫素資料
%y_train:訓練樣本的標籤
%w:輸出層權重
%b:輸出層偏置
%w_h:隱藏層權重
%b_h:隱藏層偏置
%step:迴圈步數

step=ipout('迭代步數:');
a=input('學習因子:');
in = 784; %輸入神經元個數
hid = input('隱藏層神經元個數:');%隱藏層神經元個數
out = 10; %輸出層神經元個數
o =1;

w = randn(out,hid);
b = randn(out,1);
w_h =randn(hid,in);
b_h = randn(hid,1);


for i=0:step
    %打亂訓練樣本
    r=randperm(4000);
    x_train = x_train(:,r);
    y_train = y_train(:,r);
    %mini_batch
    for jj=0:399
        %取batch為10  更新取10次的平均值
        for j=jj*10+1:(jj+1)*10
            x = x_train(:,j);
            y = y_train(:,j);

            hid_put = layerout(w_h,b_h,x);
            out_put = layerout(w,b,hid_put);

            %更新公式的實現
            o_update = (y-out_put).*out_put.*(1-out_put);
            h_update = ((w')*o_update).*hid_put.*(1-hid_put);

            if j==1
                outw_update = (double(a)/10)*(o_update*(hid_put'));
                outb_update = (double(a)/10)*o_update;
                hidw_update = (double(a)/10)*(h_update*(x'));
                hidb_update = (double(a)/10)*h_update;
            end

            if j~=1
                outw_update = outw_update + (double(a)/10)*(o_update*(hid_put'));
                outb_update = outb_update -(double(a)/10)*o_update;
                hidw_update = hidw_update + (double(a)/10)*(h_update*(x'));
                hidb_update = hidb_update -(double(a)/10)*h_update;
            end
        end

        w = w + outw_update;
        b = b+ outb_update;
        w_h = w_h +hidw_update;
        b_h =b_h +hidb_update;
    end  
end
end
測試(mytest.m)
function[]= mytest(x_test,y_test,w,b,w_h,b_h)
%x_test:測試樣本的畫素資料
%y_test:測試樣本的標籤
%w:輸出層權重
%b:輸出層偏置
%w_h:隱藏層權重
%b_h:隱藏層偏置

test = zeros(10,1000);
for k=1:1000
    x = x_test(:,k);

    hid = layerout(w_h,b_h,x);
    test(:,k)=layerout(w,b,hid);

    %正確率表示方式一:輸出正確個數
    [t,t_index]=max(test);
    [y,y_index]=max(y_test);
    sum = 0;
    for p=1:length(t_index)
        if t_index(p)==y_index(p)
            sum =sum+1;
        end
    end
end

fprintf('正確率: %d/1000\n',sum);

%正確率表示方式二:用plotconfusion函式
plotconfusion(y_test,test);
end
main.m
[x_train,y_train,x_test,y_test]=getdata();

%歸一化
x_train = mapminmax(x_train,0,1);
x_test =mapminmax(x_test,0,1);

[w1,b1,w_h1,b_h1]=mytrain(x_train,y_train);
fprintf('mytrain正確率:\n');
mytest(x_test,y_test,w1,b1,w_h1,b_h1);

[w2,b2,w_h2,b_h2]=mytrain(x_train,y_train);
fprintf('mytrain_mini正確率:\n');
mytest(x_test,y_test,w2,b2,w_h2,b_h2);

實驗結果

直接執行main.m,且兩個訓練方式都輸入相同引數,得到結果如下:
結果

下面是mini_batch的plotconfusion結果,mytrain的也差不多。其中綠色的為正確率:
mytrain_mini

直觀感覺min_batch方式的訓練會快一丟丟。由於這裡資料不多,所以兩者的差別看不大出來!