Pybrain学习笔记-4 基于前馈神经网络的分类器
引言:
通过前面的学习已经清楚了pybrain的基础模块功能,也学会了利用神经网络模型进行一些基本的数据操作,下面让我们正式实践一下,通过分类器深刻理解一下利用神经网络解决分类问题的整体流程。
分类器整体思路分析:
在给出代码前,有必要讲一下使用神经网络构造分类器的整体思路。
本次神经网络分类器目的,是给出一个二维空间点,我们可以将其读入训练好的神经网络模型,通过模型得到这个二维空间点的所属分类。因此,大致的流程如下:
先建立一个前馈神经网路,然后再建立满足分类要求的数据集,从数据集中采样出训练集对网络进行训练,使得网络达到所需精度。最后利用测试集进行测试,再实现输出结果的绘制。
注:有关网络的建立、数据集的建立、网络的训练方法可以参考我的前几篇博文。
构建神经网络分类器:
话不多说,直接上代码:
5.test_pybrian_5
#!usr/bin/env python
#_*_coding:utf-8_*_
'''
Created on 2017年4月14日
Topic:Classification with Feed-Forward Neural Networks
@author: Stuart斯图尔特
'''
from pybrain.datasets import ClassificationDataSet #分类数据专用数据集工具包
from pybrain.utilities import percentError #以列表和数组的形式返回误差的百分比工具包
from pybrain.tools.shortcuts import buildNetwork
from pybrain.supervised.trainers import BackpropTrainer
from pybrain.structure.modules import SoftmaxLayer
#下面是一些图形界面以及矩阵运算和随机数组运算的工具包,可暂时忽略具体应用,后续会讲到
from pylab import ion , ioff , figure , draw , contourf , clf , show , hold , plot
from scipy import diag , arange , meshgrid , where
from numpy.random import multivariate_normal
#创建一个有二维空间点组成的列表
means = [(-1,0),(2,4),(3,1)]
#创建一个由对角矩阵形成的列表
cov = [diag([1,1]), diag([0.5,1.2]), diag([1.5,0.7])]
#ClassificationDataSet:Specialized data set for classification data. Classes are to be numbered from 0 to nb_classes-1.
#定义数据集:输入目标是2维,目标的target的维数是1,数据可被分为3类
alldata = ClassificationDataSet(2, 1, nb_classes=3)
for n in xrange(400): #外层循环400次
for klass in range(3): #内层循环三次
input = multivariate_normal(means[klass],cov[klass]) #理解成把输入数据进行正态分布的处理,得到更好的数据集
alldata.addSample(input, [klass]) #得到二维输入数据
#按照3:1的比例划分测试集和训练集
#tstdata, trndata = alldata.splitWithProportion( 0.25 )
#报错:AttributeError: 'SupervisedDataSet' object has no attribute '_convertToOneOfMany'
#将这条语句改成下面代码段:
tstdata_temp, trndata_temp = alldata.splitWithProportion(0.25)
#定义测试集,输入维度是2,target是1,有三类,均是2d向量
tstdata = ClassificationDataSet(2, 1, nb_classes=3)
for n in xrange(0, tstdata_temp.getLength()):
tstdata.addSample( tstdata_temp.getSample(n)[0], tstdata_temp.getSample(n)[1] )
#定义训练集, 输入维度是2,target是1,有三类,均是2d向量
trndata = ClassificationDataSet(2, 1, nb_classes=3)
for n in xrange(0, trndata_temp.getLength()):
trndata.addSample( trndata_temp.getSample(n)[0], trndata_temp.getSample(n)[1] )
#将target转化成一维的输出,注意网络的output有三层,在分类器中我们想得到的结果是某一明确的分类
#通过convertToOneOfMany()实现target的降维,并把'target'字段转存在'class'字段中
trndata._convertToOneOfMany( )
tstdata._convertToOneOfMany( )
print "Number of training patterns: ", len(trndata) #Number of training patterns: 900
print "Input and output dimensions: ", trndata.indim, trndata.outdim #Input and output dimensions: 2 3
print "First sample (input, target, class):"
print trndata['input'][0], trndata['target'][0], trndata['class'][0] #[ 0.7295122 0.37413538] [0 0 1] [2]
#调用buildNetwork建立前馈神经网络,输入层是2,隐含层是5,输出层是3
fnn = buildNetwork( trndata.indim, 5, trndata.outdim, outclass=SoftmaxLayer )
#调用反向误差训练器
trainer = BackpropTrainer( fnn, dataset=trndata, momentum=0.1, verbose=True, weightdecay=0.01)
#这个网络已经可以进行测试了,但由于想做出一个漂亮易懂的分类器,因此下面将自定义数据范围,用训练好的网络进行分类并将分类结果可视化
#下面为了把分类器控制在一个可绘制方格中,因此随机生成方格内的二维点
#从-3到6建立一维数组,间隔为0.2,显然维度是60
#生成矩阵X , Y,矩阵均为60*60的方阵
ticks = arange(-3.,6.,0.2)
X, Y = meshgrid(ticks, ticks)
# need column vectors in dataset, not arrays 定义分类数据集并从X Y矩阵中采样
griddata = ClassificationDataSet(2,1, nb_classes=3)
for i in xrange(X.size):
griddata.addSample([X.ravel()[i],Y.ravel()[i]], [0])
#同样要把target映射成一维数据放到class实例中
griddata._convertToOneOfMany()
for i in range(20):
trainer.trainEpochs( 1 ) #训练网络一次,之所以要训练一次是想得到每次的训练结果
trnresult = percentError( trainer.testOnClassData(),trndata['class'] )
tstresult = percentError( trainer.testOnClassData(dataset=tstdata ), tstdata['class'] )
#打印每次训练的错误率
print "epoch: %4d" % trainer.totalepochs, \
" train error: %5.2f%%" % trnresult, \
" test error: %5.2f%%" % tstresult
out = fnn.activateOnDataset(griddata)
#下面是输出可视化的分类结果,涉及pylab模块的使用,后续会讲到
out = out.argmax(axis=1) # the highest output activation gives the class
out = out.reshape(X.shape)
figure(1)
ioff() # interactive graphics off
clf() # clear the plot
hold(True) # overplot on
for c in [0,1,2]:
here, _ = where(tstdata['class']==c)
plot(tstdata['input'][here,0],tstdata['input'][here,1],'o')
if out.max()!=out.min(): # safety check against flat field
contourf(X, Y, out) # plot the contour
ion() # interactive graphics on
draw() # update the plot
ioff()
show()
代码说明:
有关代码的解释都已在注释中给出,下面主要对标记转化函数_convertToOneOfMany( )报出的AttributeError进行分析与解释
在官方的说明书中,训练集与测试集按照3:1划分,并将标记转化为一维输出,语句是:
tstdata, trndata = alldata.splitWithProportion( 0.25 )tstdata, trndata = alldata.splitWithProportion( 0.25 )
trndata._convertToOneOfMany( )
tstdata._convertToOneOfMany( )
但是在编译时报错如下:
AttributeError:'SuperviseDataSet' object has no attribute '_convertToOneOfMany'
按照错误提示,SuperviseDataSet类中不存在_convertToOneOfMany()这个方法。回头看,这很对啊,我之前建立的alldata是ClassificationDataSet类的一个实例,而通过查看源代码,我们使用的_convertToOneOfMany()方法也正好在ClassificationDataSet中,我们已经引好了各种数据集的工具包,引包调用,天经地义,可为什么报出找不到方法这样的错误呢?
找了很长时间的原因才发现问题所在,首先打开spiltWithProportion()方法的源码:
def splitWithProportion(self, proportion = 0.5):
"""Produce two new datasets, the first one containing the fraction given
by `proportion` of the samples."""
indicies = random.permutation(len(self))
separator = int(len(self) * proportion)
leftIndicies = indicies[:separator]
rightIndicies = indicies[separator:]
leftDs = SupervisedDataSet(inp=self['input'][leftIndicies].copy(),
target=self['target'][leftIndicies].copy())
rightDs = SupervisedDataSet(inp=self['input'][rightIndicies].copy(),
target=self['target'][rightIndicies].copy())
return leftDs, rightDs
有没有发现,spiltWithProportion()方法所返回的参数有两个,即leftDs和rightDs,整个源码函数的作用实际上是按照随机的一个比例,将所处理的数据集分成两个部分,并作为SupervisedDataSet类型的数据返回。因此Python在调用方法的时候实际上是在找SupervisedDataSet中的函数,也就找不到_convertToOneOfMany()了。
有关于后面的替换代码,是参考了stackoverflow论坛的解决方案,(传送门)。
好了,最后给出程序跑过一遍后的效果,参考一下:
控制台打印出了网络在训练20次过程中的细节,用错误分类的比率给出,然后打印出了分类的结果图。
我们发现分类的结果并不是十分的精确,这与训练数据和训练次数都有一定的关系,若想获得更精确的结果,可以建立更大的数据集,同时扩大训练次数。
至此,利用神经网络编写的分类器就实现了。
pybrain的强大之处就在于其具有强大的API来支撑神经网络进行各种数据的处理。从而极大地缩小了代码量,增强了代码的可移植性。然而官方的案例只针对于有监督数据。接下来将考虑将pybrain用于无监督数据集的处理问题,这也是我们学习pybrain的初衷,因为在NLP领域的很多问题都是针对于无监督的文本数据的分析与处理,可见仅在神经网络的路上,我们要走的路还长着呢。
后续我将从神经网络的具体算法入手,并结合pybrain的源码,进行实验与学习,road still long。
Ps:程序小猿,个人能力和表达能力有限,如有错误欢迎指正交流。码字不易,转载请注明出处(笔芯)。