标签:
这两天在学习人工神经网络,用传统神经网络结构做了一个识别手写数字的小项目作为练手。点滴收获与思考,想跟大家分享一下,欢迎指教,共同进步。
训练部分:recognize_handwriting_numbers_by_simple_NN_train.m
V=double(rand(256,64));
W=double(rand(64,10));
delta_V=double(rand(256,64));
delta_W=double(rand(64,10));
yita=0.2;%缩放系数,有的文章称学习率
yita1=0.05;%我自己加的参数,缩放激活函数的自变量防止输入过大进入函数的饱和区,可以去掉体会一下变化
train_number=9;%训练样本中,有多少个数字,一共9个,没有0
train_num=30;%训练样本中,每种数字多少张图,一共100张
x=double(zeros(1,256));%输入层
y=double(zeros(1,64));%中间层,也是隐藏层
output=double(zeros(1,10));%输出层
tar_output=double(zeros(1,10));%目标输出,即理想输出
delta=double(zeros(1,10));%一个中间变量,可以不管
%记录总的均方差便于画图
s_record=1:1000;
tic %计时
for train_control_num=1:1000 %训练次数控制,在调参的最后发现1000次其实有多了,大概400次完全够了
s=0;
%读图,输入网络
for number=1:train_number
ReadDir=['E:\Matlab\recognize_handwiting_numbers\train_lib\'];%读取样本的路径
for num=1:train_num %控制多少张
photo_name=[num2str(number),num2str(num,'%05d'),'.png'];%图片名
photo_index=[ReadDir,photo_name];%路径加图片名得到总的图片索引
photo_matrix=imread(photo_index);%使用imread得到图像矩阵
photo_matrix=uint8(photo_matrix<=230);%二值化,黑色是1
tmp=photo_matrix';
tmp=tmp(:);%以上两步完成了图像二维矩阵转变为列向量,256维,作为输入
%计算输入层输入
x=double(tmp');%转化为行向量因为输入层X是行向量,并且化为浮点数
%得到隐层输入
y0=x*V;
%激活
y=1./(1+exp(-y0*yita1));
%得到输出层输入
output0=y*W;
output=1./(1+exp(-output0*yita1));
%计算预期输出
tar_output=double(zeros(1,10));
tar_output(number)=1.0;
%计算误差
%按照公式计算W和V的调整,为了避免使用for循环比较耗费时间,下面采用了直接矩阵乘法,更高效
delta=(tar_output-output).*output.*(1-output);
delta_W=yita*repmat(y',1,10).*repmat(delta,64,1);
tmp=sum((W.*repmat(delta,64,1))');
tmp=tmp.*y.*(1-y);
delta_V=yita*repmat(x',1,64).*repmat(tmp,256,1);
%计算均方差
s=s+sum((tar_output-output).*(tar_output-output))/10;
%更新权值
W=W+delta_W;
V=V+delta_V;
end
end
s=s/train_number/train_num %不加分号,随时输出误差观看收敛情况
train_control_num %不加分号,随时输出迭代次数观看运行状态
s_record(train_control_num)=s;%记录
end
toc %计时结束
plot(1:1000,s_record);
correct_num=0;%记录正确的数量
incorrect_num=0;%记录错误数量
test_number=9;%测试集中,一共多少数字,9个,没有0
test_num=100;%测试集中,每个数字多少个,最大100个
% load W;%%之前训练得到的W保存了,可以直接加载进来
% load V;
% load yita1;
%记录时间
tic %计时开始
for number=1:test_number
ReadDir=['E:\Matlab\recognize_handwiting_numbers\test_lib\'];
for num=1:test_num %控制多少张
photo_name=[num2str(number),num2str(num,'%05d'),'.png'];
photo_index=[ReadDir,photo_name];
photo_matrix=imread(photo_index);
%大小改变
photo_matrix=imresize(photo_matrix,[16 16]);
%二值化
photo_matrix=uint8(photo_matrix<=230);%黑色是1
%行向量
tmp=photo_matrix';
tmp=tmp(:);
%计算输入层输入
x=double(tmp');
%得到隐层输入
y0=x*V;
%激活
y=1./(1+exp(-y0*yita1));
%得到输出层输入
o0=y*W;
o=1./(1+exp(-o0*yita1));
%最大的输出即是识别到的数字
[o,index]=sort(o);
if index(10)==number
correct_num=correct_num+1
else
incorrect_num=incorrect_num+1;
%显示不成功的数字,显示会比较花时间
% figure(incorrect_num)
% imshow((1-photo_matrix)*255);
% title(num2str(number));
end
end
end
correct_rate=correct_num/test_number/test_num
toc %计时结束
运行结果:
最后调参得到比较好的结果,使用η=0.2,η1=0.05,在我电脑上,i5-3210M,4G内存,迭代1000次所需要的时间是468s,最终得到下面的曲线,横坐标是训练的迭代次数,纵坐标是均方差,可以看到,其实在350次迭代的时候已经取得了很小的均方差,考虑时间和性能的折中的话,迭代350次已经可以了。
我自己对神经网络的的一些理解,以识别图片手写数字为例,图片中黑色像素点和白色像素点之间的空间编排关系构成了我们看到的数字,如果我们能找到一个很厉害的方程组,方程的输入参数是一张图片,返回一个数字表示识别结果,那肯定是很理想的,但是可惜,很难找到的方程来映射图片像素点的空间排布和识别结果这样的一种关系。而神经网络刚好就完成了这样的映射功能,所有的关于像素点空间排布信息的解析都隐藏于矩阵W和V之中,所以W和V实际上代表了这样一种映射关系。而神经网络聪明的地方在于,当这样的映射关系未知时,我们设计了一套机理让它自己来寻找,这就是学习和训练的过程,而最后得到的W和V就是学习的结果,这个过程很像婴儿开始认东西的过程,一开始婴儿的认知体系是空白的,像刚随机初始化的W和V,我们给它看一本书,像给了网络一个输入,告诉他这个是“书”,像告诉网络,理想输出应该是“书”,他会开始尝试来理解,建立书这个物品到“书”这个字的映射关系,一开始他会犯错,看到相似的比如一张纸也会认为是书,但是在看到更多的书之后,并且我们告诉他这个是“书”之后,他又会不断修正这样一种映射关系,最后建立完整的对于书这样一种物品到“书”这个字的映射过程,初步的认知体系建立。现在可以说说上面的“激活函数”是干嘛用的了,可以想象,自然界的各种各样的映射关系显然是比较复杂的,关靠线性关系是无力描述的,而神经网络中用的是加权相加的运算,如果没有经过激活函数处理,后层的所有输入都可以由多层之前的信号经过线性运算来得到,一个现象模型显然表现力是远远不够的,所以我们在神经网络模型中加入了非线性的因素,就是激活函数。
最后,感谢看完这么长一篇文字,有不足之处,欢迎评论指正,共同进步。
标签:
原文地址:http://blog.csdn.net/huang_miao_xin/article/details/51364152