最近想入坑机器学习,同时在看《机器学习与实战》一书以及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 即可得到测试正确率。