2018-01-13 | 研究与探索 | UNLOCK

TensorFlow 入门记

经过一段时间对Python的深入了解和对numpy等模块的学习,终于能开始用TensorFlow了。

网上的新手教程都不怎么靠谱……一般是让你用mnist库训练一个一层的简单神经网络。

一个好消息是现在PyPI上有一个mnist库,所以你不用下载库文件再导入程序,只需在终端pip install mnist,再在Python脚本中import mnist即可。mnist.train_images()返回一个dtypeuint8shape(60000,28,28)numpy.ndarraymnist.train_images()[i]对应一张28×28的数字图片,每个数在0到255之间,代表灰度。mnist.train_labels()则返回一个shape(6000,)numpy.ndarray,是一列范围为0到9的数,表示mnist.train_images()相应位置上图片代表的数。mnist.test_images()mnist.test_labels()类似,但大小只有10000。以上的数据并不随mnist库下载,而是在第一次使用时自动下载,日后直接使用已下载数据。 这次模型要求较简单,将28×28的图片视为长度784的数组,并采用one-hot vector作为数据标签(label),也就是说 [0,0,0,1,0,0,0,0,0,0]代表数字3,因为它的第3位(编号从0开始)为1,其余为0。我特地写了一些作转换的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import mnist

#将整数数组转换为one hot vector的数组
def to_onehotv(a):
t = np.zeros((a.size,10))
for i,n in enumerate(a):
t[i][n] = 1.0
return t

#分批次取出并转化mnist.train_images()与mnist.train_labels()中的数据
index = 0
def next_batch(n):
global index
xs = np.float32(mnist.train_images()[index:index+n]).reshape((n,28**2))/255
ys = to_onehotv(mnist.train_labels()[index:index+n])
index += n
return xs,ys

test_xs = np.float32(mnist.test_images().reshape((10000,28**2)))/255
test_ys = to_onehotv(mnist.test_labels())

依照入门教程,主要部分的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
x = tf.placeholder(tf.float32,[None,784])
w = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x,w)+b)

y_label = tf.placeholder(tf.float32,[None,10])
loss = -tf.reduce_sum(y_label*tf.log(y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_label,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

init = tf.global_variables_initializer()

with tf.Session() as sess:
sess.run(init)
for i in range(500):
batch_xs,batch_ys = next_batch(50)
sess.run(train_step,feed_dict={x:batch_xs,y_label:batch_ys})
if i%50 ==0:
print(sess.run(accuracy,feed_dict={x:test_xs,y_label:test_ys}))
print('training finished')
print('final accuracy is',sess.run(accuracy,feed_dict={x:test_xs,y_label:test_ys}))

可惜结果很不好,一直打印0.098…… 开始我以为是平台问题,但换成linux系统问题仍在。反复搜索才发现是计算损失loss时可能出现nan。于是更换为TensorFlow自带的计算交叉熵的函数:

1
2
3
4
5
y_logits = tf.matmul(x,w)+b
y = tf.nn.softmax(y_logits)

y_label = tf.placeholder(tf.float32,[None,10])
loss = tf.nn.softmax_cross_entropy_with_logits(logits=y_logits,labels=y_label)

tf.nn.softmax_cross_entropy_with_logits()把各种情况都处理好了,也就不用我们操心nan问题了。

某些细节的讲解:

  • softmax()是一种归一化函数,原理差不多是$softmax(x_i)=\frac{\exp{(x_i)}}{\sum\limits_j exp(x_j)}$ ,这样各数相对大小顺序不变,而比例却以指数增加,比如原来a为b的10倍,转换后a就是b的10次方。
  • tf.argmax(y,1)中参数1表示维度,tf.argmax()返回张量某维度上最大元素的偏移量。
  • tf.reduce_mean()中“reduce”是指这个操作是降维操作,将一个维度的数据收缩为其平均值。
  • 将数组中 0255 的灰度值转换为 01 的浮点数能提高识别率,玄学?不过提高后也堪堪摸到 0.9 ,还不如教程里的 0.92 ……