十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章给大家分享的是有关spl中如何实现手写数字识别的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
创新互联专注于五原企业网站建设,响应式网站,购物商城网站建设。五原网站建设公司,为五原等地区提供建站服务。全流程按需网站设计,专业设计,全程项目跟踪,创新互联专业和态度为您提供的服务
识别手写的阿拉伯数字,对于人类来说十分简单,但是对于程序来说还是有些复杂的。
不过随着机器学习技术的普及,使用10几行代码,实现一个能够识别手写数字的程序,并不是一件难事。这是因为有太多的机器学习模型可以拿来直接用,比如tensorflow、caffe,在python下都有现成的安装包,写一个识别数字的程序,10几行代码足够了。
然而我想做的,是不借助任何第三方的库,从零开始,完全自己实现一个这样的程序。之所以这么做,是因为自己动手实现,才能深入了解机器学习的原理。
熟悉神经网络回归算法的,可以略过这一节了。
学习了一些基本概念,决定使用回归算法。首先下载了著名的MNIST数据集,这个数据集有60000个训练样本,和10000个测试样本。每个数字图片都是28*28的灰度图片,所以输入可以认为是一个28*28的矩阵,也可以认为是一个28*28=784个像素值。
这里定义一个模型用于判断一个图片数字,每个模型包括每个输入的权重,加一个截距,最后再做个归一。模型的表达式:
Out5= sigmoid(X0*W0+ X1*W1+……X783*W783+bias)
X0到X783是784个输入,W0到W783是784个权重,bias是一个常量。sigmoid函数可以将较大范围的数挤压到(0,1)区间内,也就是归一。
例如我们用这一组权重和bias来判断数字5,期望当图片是5时输出是1,当不是5时输出是0。然后训练的过程就是根据每个样本的输入,计算Out5的值和正确值(0或1)的差距,然后根据这个差距,调整权重和bias。转换一下公式,就是在努力使得(Out5-正确值)接近于0,即所谓损失最小。
同理,10个数字就要有10套模型,每个判断不同的数字。训练好以后,一个图片来了,用这10套模型进行计算,哪个模型计算的结果更接近于1,就认为这个图片是哪个数字。
按照上面的思路,使用集算器的SPL(结构化处理语言)来编码实现:
A | B | C | |
1 | =file("train-imgs.btx").cursor@bi() | ||
2 | >x=[],wei=[],bia=[],v=0.0625,cnt=0 | ||
3 | for 10 | >wei.insert(0,[to(28*28).(0)]), bia.insert(0,0.01) | |
4 | for 50000 | >label=A1.fetch(1)(1) | |
5 | >y=to(10).(0), y(label+1)=1,x=[] | ||
6 | >x.insert(0,A1.fetch(28*28)) | >x=x.(~/255) | |
7 | =wei.(~**x).(~.sum()) ++ bia | ||
8 | =B7.(1/(1+exp(-~))) | ||
9 | =(B8--y)**(B8.(1-~))**B8 | ||
10 | for 10 | >wei(B10)=wei(B10)--x.(~*v*B9(B10)), bia(B10)=bia(B10) - v*B9(B10) | |
11 | >file("MNIST模型.btx").export@b(wei),file("MNIST模型.btx").export@ba(bia) |
不用再找了,训练模型的所有代码都在这里了,没有用到任何第三方库,下面解析一下:
A1,用游标导入MNIST训练样本,这个是我转换过的格式,可以被集算器直接访问;
A2,定义变量:输入x,权重wei,训练速度v,等;
A3,B3,初始化10组模型(每组是784个权重+1个bias);
A4,循环取5万个样本进行训练,10模型同时训练;
B4,取出来label,即这个图片是几;
B5,计算正确的10个输出,保存到变量y;
B6,取出来这个图片的28*28个像素点作为输入,C6把每个输入除以255,这是为了归一化;
B7,计算X0*W0+ X1*W1+……X783*W783+bias
B8,计算sigmoid(B7)
B9,计算B8的偏导,或者叫梯度;
B10,C10,根据B9的值,循环调整10个模型的参数;
A11,训练完毕,把模型保存到文件。
测试一下这个模型的成功率吧,用 SPL 写了一个测试程序:
A | B | C | |
1 | =file("MNIST模型.btx").cursor@bi() | =[0,1,2,3,4,5,6,7,8,9] | |
2 | >wei=A1.fetch(10),bia=A1.fetch(10) | ||
3 | >cnt=0 | ||
4 | =file("test-imgs.btx").cursor@bi() | ||
5 | for 10000 | >label=A4.fetch(1)(1) | |
6 | >x=[] | ||
7 | >x.insert(0,A4.fetch(28*28)) | >x=x.(~/255) | |
8 | =wei.(~**x).(~.sum()) ++ bia | ||
9 | =B8.(round(1/(1+exp(-~)), 2)) | ||
10 | =B9.pmax() | ||
11 | if label==B1(B10) | >cnt=cnt+1 | |
12 | =A1.close() | ||
13 | =output(cnt/100) |
运行测试,正确率达到了91.1%,我对这个结果是很满意的,毕竟这只是一个单层模型,我用TensorFlow的单层模型得到的正确率也是91%多一点。下面解析一下代码:
A1,导入模型文件;
A2,把模型提取到变量里;
A3,计数器初始化(用于计算成功率);
A4,导入MNIST测试样本,这个文件格式是我转换过的;
A5,循环取1万个样本进行测试;
B5,取出来label;
B6,清空输入;
B7,取出来这个图片的28*28个像素点作为输入,每个输入除以255,这是为了归一化;
B8,计算X0*W0+ X1*W1+……X783*W783+bias
B9,计算sigmoid(B7)
B10,得到最大值,即最可能的那个数字;
B11,判断正确测计数器加一;
A12,A13,测试结束,关闭文件,输出正确率。
这里要说的优化并不是继续提高正确率,而是提升训练的速度。想提高正确率的同学可以尝试一下这几个手段:
1. 加一个卷积层;
2. 学习速度不要用固定值,而是随着训练次数递减;
3. 权重的初始值不要使用全零,使用正态分布;
我认为单纯追求正确率的意义不大,因为MNIST数据集有些图片本身就有问题,即使人工也不一定能知道写的是数字几。我用集算器显示了几张出错的图片,都是书写十分不规范的,下面这个图片很难看出来是2。
下面说重点,要提高训练速度,可以使用并行或集群。使用SPL语言实现并行很简单,只要使用fork关键字,把上面的代码稍加处理就可以了。
A | B | C | D | |
1 | =file("train-imgs.btx").cursor@bi() | |||
2 | >x=[],wei=[],bia=[],v=0.0625,cnt=0 | >mode=to(0,9) | ||
3 | >wei=to(28*28).(0) | |||
4 | fork mode | =A1.cursor() | ||
5 | for 50000 | >label=B4.fetch(1)(1) | >y=1,x=[] | |
6 | if label!=A4 | >y=0 | ||
7 | >x.insert(0,B4.fetch(28*28)) | >x=x.(~/255) | ||
8 | =(wei**x).sum() + bia | |||
9 | =1/(1+exp(-C8)) | |||
10 | =(C9-y)*((1-C9))*C9 | |||
11 | >wei=wei--x.(~*v*C10), bia=bia- v*C10 | |||
12 | return wei,bia | |||
13 | =movefile(file("MNIST模型.btx")) | |||
14 | for 10 | >file("MNIST模型.btx").export@ba([A4(A15)(1)]) | ||
15 | for 10 | >file("MNIST模型.btx").export@ba([A4(A16)(2)]) |
使用了并行之后,训练的时间减少差不多一半,而代码并没有做太多修改。
使用SPL语言在初期可能会有点不适应,用得多了会觉得越来越方便:
1. 支持集合运算,比如例子里用到的784个输入和784个权重的乘法,直接写一个**就可以了,如果使用Java或者C,还要自己实现。
2. 数据的输入输出很方便,可以方便地对文件读写。
3. 调试太方便了,所有变量都直观可见,这一点比python要好用。
4. 可以单步计算,有了改动不用从头重来,Java和C做不到这一点,python虽然可以但也不方便,集算器只要点中相应格执行就可以了。
5. 实现并行和集群很方便,不需要太多的开发工作量。
6. 支持调用和被调用。集算器可以调用第三方java库,Java也可以调用集算器的代码,例如上面的代码就可以被Java调用,实现一个自动填验证码的功能。
这样的编程语言,用在数学计算上,实在是最合适不过了。
感谢各位的阅读!关于“spl中如何实现手写数字识别”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!