最近想入坑机器学习,同时在看《机器学习与实战》一书以及Tensorflow的官方中文文档,发现可以用Tensorflow改写一些书中的示例代码,有的甚至只需要改变一些参数即可,因此决定动手实践一下。由于Tensorflow的文档里面提到了一个叫做MNIST的手写识别数据集,而《机器学习与实战》一书中正好也有一个用KNN算法实现的手写识别系统,因此我首先选择了用Tensorflow改写一下这个系统。
这个系统的数据集是一张张 32*32 像素的图片,图片经过了二值化,因此像素点的取值只有0和1。首先需要把图片变换成一个 1*1024 的
numpy数组,这个函数在《机器学习与实战》附送的源代码的相关章节中已经实现了,我只是把它搬运到了我的模块里面:
def img2vector(filename): returnVect = zeros((1,1024)) fr = open(filename) for i in range(32): lineStr = fr.readline() for j in range(32): returnVect[0,32*i+j] = int(lineStr[j]) return returnVect
然后需要批量把训练和测试的图片转成numpy数组,同时把它们对应的标签(即0~9)也转为numpy数组,最后进行序列化方便随后使用。训练
和测试的图片在《机器学习与实战》附送的源代码里面可以获取到,我在文末也会提供,每张图片对应的标签由它的文件名提供,=。相关函数
如下:
#持久化训练集和测试集 def storeVector(): trainingFileList = listdir('trainingDigits') m = len(trainingFileList) trainingMat = zeros((m, 1024)) hwLabels = zeros((m, 10)) for i in range(m): fileNameStr = trainingFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) hwLabels[i, classNumStr] = 1 #序列化训练集 f = open('trainX', 'wb') pickle.dump(trainingMat, f) f.close() #序列化训练集标签 f = open('trainY', 'wb') pickle.dump(hwLabels, f) f.close() testFileList = listdir('testDigits') mTest = len(testFileList) testMat = zeros((mTest, 1024)) testLabels = zeros((mTest, 10)) for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] # take off .txt classNumStr = int(fileStr.split('_')[0]) testMat[i, :] = img2vector('testDigits/%s' % fileNameStr) testLabels[i, classNumStr] = 1 #序列化测试集 f = open('testX', 'wb') pickle.dump(testMat, f) f.close() #序列化测试集标签 f = open('testY', 'wb') pickle.dump(testLabels, f) f.close() #读取训练数据和测试数据 def getData(): f = open('testX') testX = pickle.load(f) f.close() f = open('testY') testY = pickle.load(f) f.close() f = open('trainX') trainX = pickle.load(f) f.close() f = open('trainY') trainY = pickle.load(f) f.close() return trainX, trainY, testX, testY trainX, trainY, testX, testY = getData()
需要指出的是,每个样本对应的标签是以"one-hot vector"的形式存储的,即如果一个样本对应的数字为2,则它的标签是(0,0,1,0,0,0,0,0,0,0),即只有
对应位为1,其余位均为0.
接下来需要定义一个随机获取训练子集的函数,以供随后的模型训练:
def next_batch(count): trainLen = shape(trainX)[0] if count > trainLen: print '没有足够的训练数据供随机抽取' return returnListIndex = rand.sample(range(trainLen), count) returnListX = zeros((count, 1024)) returnListY = zeros((count, 10)) for i in range(count): returnListX[i,:] = trainX[returnListIndex[i], :] returnListY[i,:] = trainY[returnListIndex[i], :] return returnListX, returnListY
换为了我自己的,然后调整了一下几个参数而已,代码如下:
if __name__ == '__main__': x = tf.placeholder(tf.float32, [None, 1024]) # 权重值 W = tf.Variable(tf.zeros([1024, 10])) # 偏置量 b = tf.Variable(tf.zeros([10])) # 计算输出 y = tf.nn.softmax(tf.matmul(x, W) + b) # 占位符用于输入正确值 y_ = tf.placeholder("float", [None, 10]) # 计算交叉熵 cross_entropy = -tf.reduce_sum(y_ * tf.log(y)) # 使用梯度下降算法以0.01的学习速率最小化交叉熵 train_step = tf.train.GradientDescentOptimizer(0.001).minimize(cross_entropy) # 初始化变量 init = tf.global_variables_initializer() # 创建会话并且初始化变量 sess = tf.Session() sess.run(init) # 开始训练模型 for i in range(1000): batch_xs, batch_ys = next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) # 评估模型 correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) # 将布尔数组转换为浮点数并取平均值来确定正确预测项的比例 accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) # 打印所学习到的模型在测试数据集上面的正确率 print sess.run(accuracy, feed_dict={x: testX, y_: testY})
代码上都有比较详尽的注释,以上代码调用了tensorflow的梯度下降算法,我仅仅修改了算法的步长参数,对tensorflow框架感兴趣的朋友
可以去极客学院学习一下该框架的官方中文文档,地址:http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_beginners.html。
算法的正确率为97%左右,而原书中使用KNN算法的正确率也是这么多。
下面附上文中用到的文件资源链接:
http://download.csdn.net/download/qq_33534383/10155187
解压缩该文件夹,里面的 kNN.py 模块是《机器学习与实战》中使用KNN算法实现手写识别系统的源码,运行该模块的handwritingClassTest函数便能看到测试结果。
tensorForDigits.py 模块是使用Tensorflow实现的手写识别系统,使用命令 python tensorForDigits.py 即可得到测试正确率。