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的也差不多。其中綠色的為正確率:
直觀感覺min_batch方式的訓練會快一丟丟。由於這裡資料不多,所以兩者的差別看不大出來!