手写数字识别是一个非常经典的机器学习项目,这篇文章,我们就通过参加Kaggle上这个经典项目的竞赛,学习如何用Tensorflow和Keras实现一个最简单的单层神经网络。

同样的,为了方便与读者交流,所有的代码都放在了这里:

Repository:

https://github.com/zht007/tensorflow-practice

1. 数据下载和预处理

Kaggle的项目页面可以下载两个csv文件,”train.csv”包含数据和标签,”test.csv”仅包含验证数据。你可以用train.csv训练自己的模型,然后再用这个模型预测test.csv中的数据,最后上传至Kaggle的项目,查看正确率和全球排名。

通过pands的read_csv方法读取csv文件,分离数据和标签,并分出训练集和验证集

1
2
3
4
labeled_images = pd.read_csv('train.csv')
images = labeled_images.iloc[:,1:]
labels = labeled_images.iloc[:,:1]
train_images, test_images,train_labels, test_labels = train_test_split(images, labels, test_size=0.01)

2. Batch training 的帮助函数

这个部分虽然比较难,但是不是这篇文章的重点,就此略过,主要作用是从训练数据集中顺序取出指定数量的batch,在Session中给模型训练。帮助函数处理之后数据的shape为[batch_size, 28,28,1]

帮助函数还有一个作用就是讲标签onehot encoded。onehot encoded 的标签shape为[batch_size, 10]。

3. 创建模型

单层神经网络,神经元个数就是输出的个数,手写数字有0到9一共10个类别,所以输出是10个数,神经元个数就是10。因为是全连接的神经网络,我们需要把输入的28*28的二维图片拆解拼凑成一个784个像素点的一维向量。输入,输出的个数和纬度决定了权重W和偏移b的shape

初始化权重W1和偏移B1:

1
2
W1 = tf.Variable(tf.truncated_normal([784, 10], stddev=0.1))  # 784 = 28 * 28
B1 = tf.Variable(tf.ones([10])/10)

单层神经网络通过线性变换加softmax的激活函数就能得到最终的结果

1
2
3
XX = tf.reshape(X,[-1,784])
Ylogits = tf.matmul(XX, W1) + B1
Y = tf.nn.softmax(Ylogits)

Cross_entropy 可以直接通过公式计算

1
cross_entropy = -tf.reduce_mean(Y_true * tf.log(Y)) * 1000.0 

也可以用tensorflow中自带的,如何选择我在前面的文章中已经介绍过了。

1
cross_entropy = tf.losses.softmax_cross_entropy(onehot_labels = Y_true, logits = Ylogits)

Optimizer 可以选择基本的GradientDescent也可以选择Adam

1
2
optimizer = tf.train.AdamOptimizer(learning_rate=0.001)
train = optimizer.minimize(cross_entropy)

4. 模型训练

将batch中的数据通过Feed_dict载入数据,剩下的就交给Tensorflow吧,注意,为了记录loss 和 Accuracy的变化,我创建了history这个字典,记录每100个Iteration它们的数字变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
history = {'acc_train':list(),'acc_val':list(),
'loss_train':list(),'loss_val':list()}
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())

for i in range(30000):
batch = ch.next_batch(100)
sess.run(train, feed_dict={X: batch[0], Y_true: batch[1]})

# PRINT OUT A MESSAGE EVERY 100 STEPS
if i%100 == 0:

# Test the Train Model
feed_dict_train = {X: batch[0], Y_true: batch[1]}
feed_dict_val = {X:ch.test_images, Y_true:ch.test_labels}

matches = tf.equal(tf.argmax(Y,1),tf.argmax(Y_true,1))
acc = tf.reduce_mean(tf.cast(matches,tf.float32))
history['acc_train'].append(sess.run(acc, feed_dict = feed_dict_train))
history['acc_val'].append(sess.run(acc, feed_dict = feed_dict_val))

history['loss_train'].append(sess.run(cross_entropy, feed_dict = feed_dict_train))
history['loss_val'].append(sess.run(cross_entropy, feed_dict = feed_dict_val))

print("Iteration {}:\tloss_train={:.6f}:\tloss_val={:.6f}:\tacc_train={:.6f}:\tacc_val={:.6f}"
.format(i,history['loss_train'][-1],history['loss_val'][-1],history['acc_train'][-1],history['acc_val'][-1]))

print('\n')

4.查看训练结果

可以看到,即便只有一层神经网络,我们也达到了将近90%的Accuracy.

image-20190329150857000

5. 用Keras试试看

Keras就更加简单了,Model两行代码搞定了。

1
2
model = models.Sequential()
model.add(layers.Dense(units=10, activation='softmax',input_shape=(784,)))

6.预测测试集数据并上传Kaggle

我们在训练的最后已经将tensorflow的模型保存起来了

1
saver.save(sess,'models_saving/my_model.ckpt')

预测的时候取出来就行了

1
2
3
4
5
6
7
8
unlabeled_images_test = pd.read_csv('test.csv')

with tf.Session() as sess:

# Restore the model
saver.restore(sess, 'models_saving/my_model.ckpt')
# Fetch Back Results
label = sess.run(Y, feed_dict={X:X_unlabeled})

最后根据Kanggle提供的后台上传转成csv文件,就可以检验模型的效果啦。

参考资料

https://www.kaggle.com/c/digit-recognizer/data

https://codelabs.developers.google.com/codelabs/cloud-tensorflow-mnist/#0

https://www.tensorflow.org/api_docs/


同步到我的简书

https://www.jianshu.com/u/bd506afc6fc1

评论