文章目录
神经网络
数据来源
采用手写数字识别作为例子进行讲解
THE MNIST DATABASE of handwritten digits 数据集下载地址为MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges
在该数据集下有4个文件可获取(采用二进制格式存储,若需要单独的图片需要解码代码,后面会叙述)分别为训练图片、训练标签、测试图片和测试标签(也可直接使用下方链接下载),图片为28X28像素
http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz training set images (9912422 bytes)
http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz training set labels (28881 bytes)
http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz test set images (1648877 bytes)
http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz test set labels (4542 bytes)
训练集共60000份图片,测试集有10000份图片,测试集中的前5000份来自于原始的NIST训练数据集,后5000份来自于原始的NIST测试集。
数据解析
下面为各个数据集的数据存储模式,若要解析出单独的图片和标签便需要了解他的数据格式。
数据格式
TRAINING SET LABEL FILE (train-labels-idx1-ubyte):
[offset] [type] [value] [description]`
`0000 32 bit integer 0x00000801(2049) magic number (MSB first)`
`0004 32 bit integer 60000 number of items`
`0008 unsigned byte ?? label`
`0009 unsigned byte ?? label`
`........`
`xxxx unsigned byte ?? label
标签值为 0 到 9.
TRAINING SET IMAGE FILE (train-images-idx3-ubyte):
[offset] [type] [value] [description]`
`0000 32 bit integer 0x00000803(2051) magic number`
`0004 32 bit integer 60000 number of images`
`0008 32 bit integer 28 number of rows`
`0012 32 bit integer 28 number of columns`
`0016 unsigned byte ?? pixel`
`0017 unsigned byte ?? pixel`
`........`
`xxxx unsigned byte ?? pixel
训练集有60000张图片 ,28X28像素,像素按行组织,像素数据从第16个字节开始。像素值为 0 到 255。0 表示背景(白色),255 表示前景(黑色)。
TEST SET LABEL FILE (t10k-labels-idx1-ubyte):
[offset] [type] [value] [description]`
`0000 32 bit integer 0x00000801(2049) magic number (MSB first)`
`0004 32 bit integer 10000 number of items`
`0008 unsigned byte ?? label`
`0009 unsigned byte ?? label`
`........`
`xxxx unsigned byte ?? label
标签值为 0 to 9.
TEST SET IMAGE FILE (t10k-images-idx3-ubyte):
[offset] [type] [value] [description]`
`0000 32 bit integer 0x00000803(2051) magic number`
`0004 32 bit integer 10000 number of images`
`0008 32 bit integer 28 number of rows`
`0012 32 bit integer 28 number of columns`
`0016 unsigned byte ?? pixel`
`0017 unsigned byte ?? pixel`
`........`
`xxxx unsigned byte ?? pixel
测试集有10000张图片 ,28X28像素,像素按行组织,像素数据从第16个字节开始。像素值为 0 到 255。0 表示背景(白色),255 表示前景(黑色)。
数据解析代码
训练图片解析
%数据读入
clear all;
clc;
%读取训练图片数据文件
[FileName,PathName] = uigetfile('*.*','选择测试图片数据文件train-images.idx3-ubyte');
TrainFile = fullfile(PathName,FileName);
fid = fopen(TrainFile,'r'); %fopen()是最核心的函数,导入文件,‘r’代表读入
a = fread(fid,16,'uint8'); %这里需要说明的是,包的前十六位是说明信息,从上面提到的那个网页可以看到具体那一位代表什么意义。所以a变量提取出这些信息,并记录下来,方便后面的建立矩阵等动作。
MagicNum = ((a(1)*256+a(2))*256+a(3))*256+a(4);
ImageNum = ((a(5)*256+a(6))*256+a(7))*256+a(8);
ImageRow = ((a(9)*256+a(10))*256+a(11))*256+a(12);
ImageCol = ((a(13)*256+a(14))*256+a(15))*256+a(16);
%从上面提到的网页可以理解这四句
if ((MagicNum~=2051)||(ImageNum~=60000))
error('不是 MNIST train-images.idx3-ubyte 文件!');
fclose(fid);
return;
end %排除选择错误的文件。
savedirectory = uigetdir('','选择测试图片路径:');
h_w = waitbar(0,'请稍候,处理中>>');
for i=1:ImageNum
b = fread(fid,ImageRow*ImageCol,'uint8'); %fread()也是核心的函数之一,b记录下了一副图的数据串。注意这里还是个串,是看不出任何端倪的。
c = reshape(b,[ImageRow ImageCol]); %亮点来了,reshape重新构成矩阵,终于把串转化过来了。众所周知图片就是矩阵,这里reshape出来的灰度矩阵就是该手写数字的矩阵了。
d = c'; %转置一下,因为c的数字是横着的。。。
e = 255-d; %根据灰度理论,0是黑色,255是白色,为了弄成白底黑字就加入了e
e = uint8(e);
savepath = fullfile(savedirectory,['TrainImage_' num2str(i) '.bmp']);
imwrite(e,savepath,'bmp'); %最后用imwrite写出图片
waitbar(i/ImageNum);
end
fclose(fid);
close(h_w);
训练标签解析
%数据读入
clear all;
clc;
%读取训练图片数据文件
[FileName,PathName] = uigetfile('*.*','选择训练图片标签数据文件train-labels.idx1-ubyte');
TrainFile = fullfile(PathName,FileName);
fid = fopen(TrainFile,'r'); %fopen()是最核心的函数,导入文件,‘r’代表读入
a = fread(fid,8,'uint8'); %这里需要说明的是,包的前十六位是说明信息,从上面提到的那个网页可以看到具体那一位代表什么意义。所以a变量提取出这些信息,并记录下来,方便后面的建立矩阵等动作。
MagicNum = ((a(1)*256+a(2))*256+a(3))*256+a(4);
ItemNum = ((a(5)*256+a(6))*256+a(7))*256+a(8);
%从上面提到的网页可以理解这四句
if ((MagicNum~=2049)||(ItemNum~=60000))
error('不是 MNIST train-labels.idx1-ubyte 文件!');
fclose(fid);
return;
end %排除选择错误的文件。
h_w = waitbar(0,'请稍候,处理中>>');
for i=1:ItemNum
TrainLabels(i) = fread(fid,1,'uint8'); %fread()也是核心的函数之一,b记录下了一副图的数据串。注意这里还是个串,是看不出任何端倪的。
waitbar(i/ItemNum);
end
save TrainLabels
fclose(fid);
close(h_w);
测试图片解析
%数据读入
clear all;
clc;
%读取训练图片数据文件
[FileName,PathName] = uigetfile('*.*','选择测试图片数据文件t10k-images.idx3-ubyte');
TrainFile = fullfile(PathName,FileName);
fid = fopen(TrainFile,'r'); %fopen()是最核心的函数,导入文件,‘r’代表读入
a = fread(fid,16,'uint8'); %这里需要说明的是,包的前十六位是说明信息,从上面提到的那个网页可以看到具体那一位代表什么意义。所以a变量提取出这些信息,并记录下来,方便后面的建立矩阵等动作。
MagicNum = ((a(1)*256+a(2))*256+a(3))*256+a(4);
ImageNum = ((a(5)*256+a(6))*256+a(7))*256+a(8);
ImageRow = ((a(9)*256+a(10))*256+a(11))*256+a(12);
ImageCol = ((a(13)*256+a(14))*256+a(15))*256+a(16);
%从上面提到的网页可以理解这四句
if ((MagicNum~=2051)||(ImageNum~=10000))
error('不是 MNIST t10k-images.idx3-ubyte 文件!');
fclose(fid);
return;
end %排除选择错误的文件。
savedirectory = uigetdir('','选择测试图片路径:');
h_w = waitbar(0,'请稍候,处理中>>');
for i=1:ImageNum
b = fread(fid,ImageRow*ImageCol,'uint8'); %fread()也是核心的函数之一,b记录下了一副图的数据串。注意这里还是个串,是看不出任何端倪的。
c = reshape(b,[ImageRow ImageCol]); %亮点来了,reshape重新构成矩阵,终于把串转化过来了。众所周知图片就是矩阵,这里reshape出来的灰度矩阵就是该手写数字的矩阵了。
d = c'; %转置一下,因为c的数字是横着的。。。
e = 255-d; %根据灰度理论,0是黑色,255是白色,为了弄成白底黑字就加入了e
e = uint8(e);
savepath = fullfile(savedirectory,['TestImage_' num2str(i) '.bmp']);
imwrite(e,savepath,'bmp'); %最后用imwrite写出图片
waitbar(i/ImageNum);
end
fclose(fid);
close(h_w);
测试标签解析
%数据读入
clear all;
clc;
%读取训练图片数据文件
[FileName,PathName] = uigetfile('*.*','选择测试图片标签数据文件t10k-labels.idx1-ubyte');
TrainFile = fullfile(PathName,FileName);
fid = fopen(TrainFile,'r'); %fopen()是最核心的函数,导入文件,‘r’代表读入
a = fread(fid,8,'uint8'); %这里需要说明的是,包的前十六位是说明信息,从上面提到的那个网页可以看到具体那一位代表什么意义。所以a变量提取出这些信息,并记录下来,方便后面的建立矩阵等动作。
MagicNum = ((a(1)*256+a(2))*256+a(3))*256+a(4);
ItemNum = ((a(5)*256+a(6))*256+a(7))*256+a(8);
%从上面提到的网页可以理解这四句
if ((MagicNum~=2049)||(ItemNum~=10000))
error('不是 MNIST t10k-labels.idx1-ubyte 文件!');
fclose(fid);
return;
end %排除选择错误的文件。
h_w = waitbar(0,'请稍候,处理中>>');
for i=1:ItemNum
TestLabels(i) = fread(fid,1,'uint8');
waitbar(i/ItemNum);
end
save TestLabels
fclose(fid);
close(h_w);
以上完成了数据解析
神经网络解析
前向传播
先以一个简单的神经网络开始
X = [ x 1 x 2 x 3 x 4 ] X=\begin{bmatrix} x_1 & x_2 & x_3 & x_4 \end{bmatrix} X=[x1x2x3x4]
A 1 = [ a 1 1 a 2 1 a 3 1 a 4 1 ] = X T = [ x 1 x 2 x 3 x 4 ] A1=\left[\begin{matrix} a_1^1 \\ a_2^1 \\a_3^1 \\a_4^1 \end{matrix}\right]=X^T=\left[\begin{matrix} x_1 \\ x_2 \\x_3 \\x_4 \end{matrix}\right] A1= a11a21a31a41 =XT= x1x2x3x4
Θ 1 = [ θ 11 1 θ 12 1 θ 13 1 θ 14 1 θ 21 1 θ 22 1 θ 23 1 θ 24 1 θ 31 1 θ 32 1 θ 33 1 θ 34 1 θ 41 1 θ 42 1 θ 43 1 θ 44 1 ] \Theta_1 = \left[ \begin{matrix} \theta_{11}^1 & \theta_{12}^1 & \theta_{13}^1 & \theta_{14}^1 \\ \theta_{21}^1 & \theta_{22}^1 & \theta_{23}^1 & \theta_{24}^1 \\ \theta_{31}^1 & \theta_{32}^1 & \theta_{33}^1 & \theta_{34}^1 \\ \theta_{41}^1 & \theta_{42}^1 & \theta_{43}^1 & \theta_{44}^1 \\ \end{matrix} \right] Θ1= θ111θ211θ311θ411θ121θ221θ321θ421θ131θ231θ331θ431θ141θ241θ341θ441
Z 2 = Θ 1 × A 1 = [ z 1 2 z 2 2 z 3 2 z 4 2 ] Z_2=\Theta_1 \times A_1 =\left[\begin{matrix} z_1^2 \\ z_2^2 \\z_3^2 \\z_4^2 \end{matrix}\right] Z2=Θ1×A1= z12z22z32z42
激活函数
s
i
g
m
o
i
d
(
z
)
=
1
1
+
e
−
z
sigmoid(z) = \frac{1}{1+e^{-z}}
sigmoid(z)=1+e−z1
A 2 = s i g m o i d ( Z 2 ) = [ s i g m o i d ( z 1 2 ) s i g m o i d ( z 2 2 ) s i g m o i d ( z 3 2 ) s i g m o i d ( z 4 2 ) ] = [ a 1 2 a 2 2 a 3 2 a 4 2 ] A_2 = sigmoid(Z_2)=\left[\begin{matrix} sigmoid(z_1^2) \\sigmoid(z_2^2)\\sigmoid(z_3^2) \\sigmoid(z_4^2) \end{matrix}\right] =\left[\begin{matrix} a_1^2 \\a_2^2\\a_3^2 \\a_4^2 \end{matrix}\right] A2=sigmoid(Z2)= sigmoid(z12)sigmoid(z22)sigmoid(z32)sigmoid(z42) = a12a22a32a42
Θ 2 = [ θ 11 2 θ 12 2 θ 13 2 θ 14 2 θ 21 2 θ 22 2 θ 23 2 θ 24 2 ] \Theta_2 = \left[ \begin{matrix} \theta_{11}^2 & \theta_{12}^2 & \theta_{13}^2 & \theta_{14}^2 \\ \theta_{21}^2 & \theta_{22}^2 & \theta_{23}^2 & \theta_{24}^2 \end{matrix} \right] Θ2=[θ112θ212θ122θ222θ132θ232θ142θ242]
Z 3 = Θ 2 × A 2 = [ z 1 3 z 2 3 ] Z_3=\Theta_2 \times A_2 =\left[\begin{matrix} z_1^3 \\ z_2^3 \end{matrix}\right] Z3=Θ2×A2=[z13z23]
A 3 = s i g m o i d ( Z 3 ) = [ s i g m o i d ( z 1 3 ) s i g m o i d ( z 2 3 ) ] = [ a 1 3 a 2 3 ] A_3 = sigmoid(Z_3)=\left[\begin{matrix} sigmoid(z_1^3) \\sigmoid(z_2^3) \end{matrix}\right] =\left[\begin{matrix} a_1^3 \\a_2^3 \end{matrix}\right] A3=sigmoid(Z3)=[sigmoid(z13)sigmoid(z23)]=[a13a23]
反向传播
标签值
Y
Y
Y
Y
=
[
y
1
y
2
]
Y=\left[ \begin{matrix} y_{1}\\y_{2} \end{matrix} \right ]\\
Y=[y1y2]
激活函数求导
S
i
g
m
o
i
d
D
e
r
i
v
a
t
i
v
e
(
a
)
=
a
⋅
(
1
−
a
)
SigmoidDerivative(a) = a \cdot (1-a)
SigmoidDerivative(a)=a⋅(1−a)
Δ 3 = [ δ 11 3 δ 12 3 ] = ( A 3 − Y ) ⋅ S i g m o i d D e r i v a t i v e ( A 3 ) = [ a 1 3 − y 1 a 2 3 − y 2 ] ⋅ S i g m o i d D e r i v a t i v e ( [ a 1 3 a 2 3 ] ) \Delta_3=\left[ \begin{matrix} \delta^3_{11}\\\delta^3_{12} \end{matrix} \right ]=(A_3 - Y) \cdot SigmoidDerivative(A_3)= \left[ \begin{matrix} a_1^3-y_1\\a_2^3-y_2 \end{matrix} \right ] \cdot SigmoidDerivative(\left[\begin{matrix} a_1^3 \\a_2^3 \end{matrix}\right]) Δ3=[δ113δ123]=(A3−Y)⋅SigmoidDerivative(A3)=[a13−y1a23−y2]⋅SigmoidDerivative([a13a23])
Δ 2 = Θ 2 T × Δ 2 ⋅ S i g m o i d D e r i v a t i v e ( A 2 ) \Delta_2 = \Theta_2^T \times \Delta_2 \cdot SigmoidDerivative(A_2) Δ2=Θ2T×Δ2⋅SigmoidDerivative(A2)
D
1
=
1
l
e
n
g
t
h
(
A
1
)
⋅
(
Δ
2
⋅
A
1
T
)
D
2
=
1
l
e
n
g
t
h
(
A
2
)
⋅
(
Δ
3
⋅
A
2
T
)
D_1 = \frac {1}{length(A1)} \cdot (\Delta_2 \cdot A_1^T)\\ D_2 = \frac {1}{length(A2)} \cdot (\Delta_3 \cdot A_2^T)
D1=length(A1)1⋅(Δ2⋅A1T)D2=length(A2)1⋅(Δ3⋅A2T)
更新权重
Θ
1
=
Θ
1
−
α
D
2
Θ
2
=
Θ
2
−
α
D
2
\Theta_1 = \Theta_1 -\alpha D_2\\ \Theta_2 = \Theta_2 -\alpha D_2
Θ1=Θ1−αD2Θ2=Θ2−αD2
训练代码
%加载训练数据标签
clear;
close all;
load('TrainLabels.mat');
%激发函数
sigmoid = @(z) 1./(1.+exp(-z));
sigmoidDerivative = @(a) a.*(1.-a);
imgLoad = @(i)imread(strcat(['TrainImage(',num2str(i),').bmp']));
% costFunction = @(delta)
%构建神经网络
%随机产生权重矩阵
Theta1=rand(200,784)*2-1;
Theta2=rand(200,200)*2-1;
Theta3=rand(200,200)*2-1;
Theta4=rand(10,200)*2-1;
b1=rand(200,1)*2-1;
b2=rand(200,1)*2-1;
b3=rand(200,1)*2-1;
b4=rand(10,1)*2-1;
m=784;
ImageNum=60000;
h0 = waitbar(0,'Please wait,Iterating...');
h1 = waitbar(1,'Please wait,Processing...');
alpha=1;
lambda=0.01;
for i=1:ImageNum
%图片数据加载
X = imgLoad(i);
subplot(1,4,1);
imshow(X);
A1=double(reshape(X./255,784,1));
%获取标签
y=zeros(10,1);
y(TrainLabels(i)+1)=1;
%循环迭代
j=1;
J=zeros(1,1);
for n=1:1000
%计算A2
A2=sigmoid(Theta1*A1+b1);
%计算A3
A3=sigmoid(Theta2*A2+b2);
%计算A4
A4=sigmoid(Theta3*A3+b3);
%计算A5
A5=sigmoid(Theta4*A4+b4);
%计算delta5
delta5=(A5-y).*sigmoidDerivative(A5);
%计算delta4
delta4=Theta4'*delta5.*sigmoidDerivative(A4);
%计算delta3
delta3=Theta3'*delta4.*sigmoidDerivative(A3);
%计算delta2
delta2=Theta2'*delta3.*sigmoidDerivative(A2);
%计算代价函数偏导数
D1=1./784.*(delta2.*A1');%+lambda*Theta1;
D2=1./200.*(delta3.*A2');%+lambda*Theta2;
D3=1./200.*(delta4.*A3');%+lambda*Theta3;
D4=1./200.*(delta5.*A4');%+lambda*Theta4;
%更新权重
Theta1 = Theta1 - alpha*D1;
Theta2 = Theta2 - alpha*D2;
Theta3 = Theta3 - alpha*D3;
Theta4 = Theta4 - alpha*D4;
%更新偏差
b1=b1-alpha/784*delta2;
b2=b2-alpha/200*delta3;
b3=b3-alpha/200*delta4;
b4=b4-alpha/200*delta5;
%计算方差
j=1/2/10*sum((y-A5).^2);
J(n)=j;
waitbar(n/200,h0);
end
numResult = find(A5==max(A5))-1;
J_Last(i)=J(n);
subplot(1,4,2);
plot(1:n,J);
subplot(1,4,3);
plot(J_Last);
subplot(1,4,4);
%plot3(i2,j2,Theta2,'*');
waitbar(i/ImageNum,h1);
end
save Theta Theta1 Theta2 Theta3 Theta4 b1 b2 b3 b4;%训练完成后将权重和偏差保存到Theta.mat %中用于测试使用
训练过程中,迭代次数1000次,第一个坐标区为单张图片迭代过程中预测前向过程中误差的方差变化,第二个坐标区为整个训练过程中总体代价函数的变化
测试代码
clear;
close all;
load('.\matlab_code\Theta.mat');
load('TestLabels.mat');
imgLoad = @(i)imread(strcat(['TestImage_',num2str(i),'.bmp']));
sigmoid = @(z) 1./(1.+exp(-z));
ImageNum=600;
for n=1:ImageNum
X = imgLoad(n);
imshow(X);
A1=double(reshape(X./255,784,1));
A2=sigmoid(Theta1*A1+b1);
A3=sigmoid(Theta2*A2+b2);
A4=sigmoid(Theta3*A3+b3);
A5=sigmoid(Theta4*A4+b4);
numResult(n) = find(A5==max(A5))-1;
end
y=TestLabels(1:n);
h=numResult-y;
num=0;
for i=1:n
if(h(i)==0)
h(i)=1;
num=num+1;
else
h(i)=0;
end
end
h=reshape(h,24,25);
imshow(h);
rate=num/ImageNum
测试中
测试600张测试集图片,正确率83.17%