手写体数字识别这个问题,网上相关的博客很多了,大多的教程和深度学习的书里面,特别是以tensorflow作为工具,这个例子总是最容易提到的。特别的Mnist是tensorflow的官方教程。所以可以参考的比较多。例如下面的官方的github和教程。可以帮你解决一些bug。
tensorflow,Mnist官方教程tensorflow,
- 数据集的准备
官方的教程是直接调用了自带的例子如下,不过运行后发现,官方将不再支持这样的操作,会在之后的版本移除,而且运行起来非常慢。仔细查看源码后发现,里面需要在某个网址下载数据集并解压,而这个网址下载速度很慢。
from tensorflow.examples.tutorials.mnist import input_data
mnist = inpiut_data.read_data_sets("MNIST_data",one_hot=True)
所以就先去这个网址下载了数据集。http://yann.lecun.com/exdb/mnist/,可以仔细阅读网站的数据说明,考虑如何将数据提取出来。有两种方法,第一种,直接利用官方的代码,修改其中下载的函数,将下载的数据直接导入。第二种,自己提取代码。
这里介绍一下第二种:参考https://www.jianshu.com/p/84f72791806f,里面介绍了提取的方法和代码。如果需要可以加上onehot的转换函数:
def int2onehot(array):
m = len(array)
maxnum = np.max(array)
array_onehot = np.zeros((m,maxnum+1)).astype(np.int16)
for i in range(m):
array_onehot[i,array[i]] = 1
return array_onehot
还有onehot转换为int的函数,如果为了显示方便:
def onehot2int(array_onehot):
array = np.argwhere(array_onehot == 1)[:,1]
return array
可以对数据进行归一化,图片数据在[0,255],给所有数据除以255。
2. softmax regression模型建立
模型主要是
# 输入占位
x = tf.placeholder(tf.float32,[None, 784])
y = tf.placeholder(tf.int32,[None,10])
# 变量定义
w = tf.Variable(tf.random_normal([784, 10]))
b = tf.Variable(tf.zeros([10]))
# 模型定义
Ylogits =tf.matmul(x,w) + b
y_pred = tf.nn.softmax(Ylogits)
# 损失函数
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=Ylogits, labels=y)) * 100
# 计算预测精度
corr = tf.equal(tf.argmax(y,1),tf.argmax(y_pred,1))
acc = tf.reduce_mean(tf.cast(corr,tf.float32))
需要注意的一点是函数tf.nn.softmax_cross_entropy_with_logits的参数:logits应该是未经过softmax层的。否则结果会不正确,但是不会报错,训练起来,loss不会下降。
3.训练
首先是训练的定义,初始化变量,如果有的话,可以导入之前训练过的权重。在每个epoch中,对训练样本进行了随机打乱,经过测试好像没什么作用。一般经过2000个epoch会到92%的精度。
# 训练定义
train = tf.train.GradientDescentOptimizer(learning_rate=0.005).minimize(loss)
# 初始化变量
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
# 导入权重,如果没有可以注释掉
save_file = './models/1000_0.91100013.ckpt'
tf.train.Saver().restore(sess,save_path=save_file)
print('load weight')
# 训练epochNum个epoch
for k in range(epochNum):
total_loss = 0
permutation = np.random.permutation(train_images.shape[0])
shu_image = train_images[permutation,:]
shu_label = train_labels[permutation,:]
for i in range(batchNum):
batch_x, batch_y = shu_image[i * batchSize:(i + 1) * batchSize - 1, :], shu_label[i * batchSize:(i + 1) * batchSize - 1,:]
_, loss_value, acc_train = sess.run([train, loss, acc], feed_dict={x: batch_x, y: batch_y})
total_loss += loss_value
if k % 100 == 0:
acc_test = sess.run(acc, feed_dict={x: test_x, y: test_y})
print('step: {}, train_loss: {:f},train_acc:{:f},test_acc:{:f}'.format(k, total_loss / batchNum, acc_train, acc_test))
save_file = './models/' + str(k) + '_' + str(acc_test) + '.ckpt'
tf.train.Saver().save(sess, save_path=save_file)
print('saved')
sess.close()
经过测试,GPU对这个例子的加速并不明显,可能是网络比较简单的缘故。
如果达不到92%的精度,可以试着修改lr(learning rate)的值,一般推荐0.001-0.1,每次按照0.001,0.003,0.006,0.01的取值来取值。这是lr=0.005训练了200个epoch的精度。
step: 0, train_loss: 195.986677,train_acc:0.815686,test_acc:0.779000
saved
step: 100, train_loss: 25.366962,train_acc:0.952941,test_acc:0.913000
saved
step: 200, train_loss: 23.749884,train_acc:0.945098,test_acc:0.915000
saved
这就完成了tensorflow的第一次实践喽。
同时给出在keras下实现的代码,简单很多。
from input_data import getdata
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import SGD
train_images,train_labels,test_images,test_labels = getdata(onehot = True)
print(train_images.shape, train_labels.shape)
print(test_images.shape, test_labels.shape)
test_x,test_y = test_images[0:1000,:],test_labels[0:1000,:]
model = Sequential()
model.add(Dense(units=10, input_dim=784))
model.add(Activation("softmax"))
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',optimizer=sgd, metrics=['accuracy'])
model.load_weights('weight.h5')
model.fit(train_images, train_labels, batch_size=256, nb_epoch=10, shuffle=True, verbose=1, validation_split=0.3)
score = model.evaluate(test_images, test_labels, batch_size=256,verbose=1)
model.save_weights('weight2.h5')
print(score)