SDU机器学习作业心得4

山东大学《机器学习》实验报告

----BP神经网络处理MNIST数据集

软件16-6 李昊 201600301309

问题背景:

实验要求是运用BP神经网络处理MNIST数据集,并且比较单层与多层的神经网络的准确率的差别。MNIST数据集是由60000个训练样本与10000个测试样本组成的,从官网获得的数据集包含有四个文件:

1544847544013

我们应该处理好这些数据的格式,然后构建网络进行训练与测试。

问题分析:

首先应该处理好数据,将训练用例与测试用例处理为易于被操作的格式。

然后首先从一层的BP神经网络入手,完成这个过程。

然后完善反向传播的过程,实现两层的BP神经网络。

最后测试比较它们的成功率。

问题解决:

处理MNIST数据集

首先是处理MNIST数据集,通过官网的信息的了解,其图片是以字节形式进行存储,我们需要把它转化为我们易于处理的矩阵的形式。其每张图片由28×28个像素点构成,每个像素点用一个灰度值表示,我们需要把这些像素点的灰度值读取到一个784维的向量中去。

通过网站上对其数据集内容的解释,在其文件头有描述文件协议的信息,这是我们不需要的,所以在读取时应忽略。以下是代码:

def loadmnist(kind):
	labels_path=('%s-labels.idx1-ubyte'%kind)
	images_path=('%s-images.idx3-ubyte'%kind)
	with open(labels_path, 'rb') as lbpath:
		magic,n = struct.unpack('>II',lbpath.read(8))
		labels = np.fromfile(lbpath,dtype=np.uint8)
	with open(images_path, 'rb') as imgpath:
		magic,num,rows,cols = struct.unpack('>IIII',imgpath.read(16))
		images = np.fromfile(imgpath,dtype=np.uint8).reshape(len(labels), 784)
	return images, labels

通过运行,得出的images与labels分别是这样的格式:

1544849627387

labels是每个图片代表的数字,images每一行是一个784列的向量,代表一个图片。

ps:但是在后来构建好之后,发现成功率并不理想,所以实际中采用了另一种方式,然而到最后发现是因为一个拼写错误,可是已经完成了,就沿用了后来那种读取方式。

构建一层BP神经网络

首先我们初始化数据,模拟前向传播的过程。

weight=np.random.randn(785,10)
v=np.dot(np.insert(images[j],0,[0]),weight)

激活函数及其求导写在函数里:

def sigmoid(z):
	return 1.0/(1.0+np.exp(-z))

def sigmoid_prime(z):
	return sigmoid(z)*(1-sigmoid(z))

之后便是反向传播的过程,我们知道,反向传播需要求出
δ j ( n ) \delta_j(n) δj(n)
进一步我们知道:
δ j ( n ) = ( d j ( n ) − y j ( n ) φ j ′ ( v j ( n ) ) ) \delta_j(n)=(d_j(n)-y_j(n)\varphi'_j(v_j(n))) δj(n)=(dj(n)yj(n)φj(vj(n)))
用代码表示出来就是:

delta=(labels[j].T-sigmoid(v))*sigmoid_prime(v)

求得了delta_j(n),我们便可以通过梯度下降求得:
Δ w = η δ j ( n ) y i ( n ) \Delta w=\eta\delta_j(n)y_i(n) Δw=ηδj(n)yi(n)
整体过程用代码表述如下:

def network(images,labels):
	eta=1
	global weight
	for i in range(10):
		for j in range(len(images)):
			v=np.dot(np.insert(images[j],0,[0]),weight)
			delta=(labels[j].T-sigmoid(v))*sigmoid_prime(v)
			#print(sigmoid(v))
			dw=eta*np.dot(np.insert(images[j],0,[0]).reshape(785,1),delta.reshape(1,10))
			weight+=dw

经过如此处理,我们得到了训练过的weight,现在我们用这个weight来测试我们的准确度。在测试中,只需进行一次前向传播,在输出结果中取最大的元素的位置即为预测的数字。在每次预测时计算预测正确的次数,最后获得准确率:

def test(images,labels):
	count=0
	for i in range(len(images)):
		y=np.dot(np.insert(images[i],0,[0]),weight)
		pre=np.argmax(y)
		lab=np.argmax(labels[i])
		count+=int(pre==lab)
	print('Predict rate is %s'%(count/len(images)))

观察结果,十轮训练准确率一般在80%左右,好的时候能接近90%

1544887881384

构建两层BP神经网络

与构建一层神经网络类似,所以我选择了在之前一层神经网络上进行增量式开发。

与一层神经网络不同的是,我们现在要考虑更复杂的反向传播的情况。之前是由输出层直接传播到输入层,现在我们多了一层隐藏层,需要从右往左逐步调节weight。

我们知道,在处理隐藏层神经元的权重时,delta的公式如下:
δ j ( n ) = φ ′ ( v j ( n ) ) δ k ( n ) w k ( n ) \delta_j(n)=\varphi'(v_j(n)) \delta_k(n)w_k(n) δj(n)=φ(vj(n))δk(n)wk(n)
这也就是与一层网络的不同之处,我们要通过前一层(或者说右边那层)得到的delta和weight来计算本层的delta。有了delta之后,过程就与一层时类似了。

代码与一层类似,所以这里只给了不同之处:

delta1=np.dot(delta2.reshape(1,10),weight2.reshape(10,31))*sigmoid_prime(v1)
dw1=eta*np.dot(np.insert(images[j],0,[0]).reshape(785,1),delta1.reshape(1,31))

至于前向传播,可以理解为两次单层的前向传播,第一次映射到30维的向量里,第二次映射到10维的输出层向量里。代码如下:

v1=np.dot(np.insert(images[j],0,[0]),weight1)
y1=sigmoid(v1)
v2=np.dot(y1,weight2)
y2=sigmoid(v2)

我们同样可以观察十轮训练之后的结果:

1544962069345

通过与上面一层网络的比较我们可以看出,两层的效果是明显比一层好一个层次的。

感想:

这次实验又刷新了实验难度的新高。。。

不过在做完之后回头看看,也不过是那么一回事。

在构建神经网络的过程中,前向传播比较顺利,主要难题出现在反向传播的实现中,因为理解不够透彻,还是无法通过程序将反向传播的过程表示出来。在再次复习一遍PPT之后,找到了实现反向传播的关键,也就是求出delta,有了delta,一切就变得豁然开朗,有了delta就可以顺势求出weight的变化量,进而训练weight。后来在总结时,感觉是因为对于公式中的每个字母分别表达的含义不够深入,导致进度一度停止。

本以为在完成一层网络之后,实现两层网络顺其自然,可是现实还是狠狠的给我了一巴掌。两层网络的反向传播过程比一层网络复杂的多,还是跟实现一层时遇到的问题一样,因为对于字母表达的含义不清楚,就会遇见本应该使用输出层的数据,却用了隐藏层的,而使用隐藏层的数据,又到了输出层去。好在经过努力理解和多次试错,最后终于调出来了。

这次实验可以让我们管中窥豹的看到,机器学习在实际生活中的巨大作用,比如这次的程序稍加修改,就可以用于验证码的识别,同时实际上手操作这种看上去高难度的实验,让我们在成功后有巨大的成就感,受益匪浅。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值