Image source wikipedia MNIST database
手写数字识别是一个非常经典的机器学习项目,这篇文章,我们就通过Kaggle上这个经典项目,学习如何用Tensorflow和Keras搭建最简单的单层神经网络,来识别手写数字。
同样的,为了方便与读者交流,所有的代码都放在了这里:
Repository:
https://github.com/zht007/tensorflow-practice
1. 数据下载和预处理
在Kaggle的项目页面可以下载两个csv文件,"train.csv"包含数据和标签,"test.csv"仅包含待分类的数据。你可以用train.csv的数据和标签来训练自己的模型,然后再用这个模型识别test.csv中的手写数字,并将其分类,最后将结果上传至Kaggle的项目,查看正确率和全球排名。
通过pandas的read_csv方法读取csv文件,分离数据和标签,并分用scikit-learn 中的train_test_split,分出训练集和验证集。
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 encode。onehot-encoded 的标签shape为[batch_size, 10]。
3. 创建模型
单层神经网络,神经元个数就等于输出的类别的个数,手写数字分成0到9,一共10个类别,神经元个数就是10。
神经网络是全链接的,我们需要把输入的28*28个像素的二维图片,拆解拼凑成一个784个像素点的一维向量。此时输入的feature数就是784。
输入的feature数和输出的类别个数共同决定了权重W和偏移b的shape
初始化权重W1和偏移B1:
W1 = tf.Variable(tf.truncated_normal([784, 10], stddev=0.1)) # 784 = 28 * 28
B1 = tf.Variable(tf.ones([10])/10)
单层神经网络通过softmax的激活,就能得到最终的结果
XX = tf.reshape(X,[-1,784])
Ylogits = tf.matmul(XX, W1) + B1
Y = tf.nn.softmax(Ylogits)
Cross_entropy 可以直接通过公式计算
cross_entropy = -tf.reduce_mean(Y_true * tf.log(Y)) * 1000.0
也可以用tensorflow中自带的,如何选择我在前面的文章中已经介绍过了。
cross_entropy = tf.losses.softmax_cross_entropy(onehot_labels = Y_true, logits = Ylogits)
Optimizer 可以选择基本的GradientDescent也可以选择Adam,关于Optimizer的介绍可以查看之前的相关文章。
optimizer = tf.train.AdamOptimizer(learning_rate=0.001)
train = optimizer.minimize(cross_entropy)
4. 模型训练
将batch中的数据通过Feed_dict载入数据,剩下的就交给Tensorflow吧,注意,为了记录loss 和 Accuracy的变化,我创建了history这个字典,记录每100个Iteration它们的数字变化。
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.
5. 用Keras试试看
Keras就更加简单了,Model两行代码搞定了。
model = models.Sequential()
model.add(layers.Dense(units=10, activation='softmax',input_shape=(784,)))
6.预测测试集数据并上传Kaggle
我们在训练的最后已经将tensorflow的模型保存起来了
saver.save(sess,'models_saving/my_model.ckpt')
预测的时候取出来就行了
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})
最后,按照Kaggle提供模板格式,将结果转换成csv文件,上传服务器,就可以看到训练成果啦。
详细过程请参见jupyter notbook中的代码和注释。
参考资料
https://www.kaggle.com/c/digit-recognizer/data
https://codelabs.developers.google.com/codelabs/cloud-tensorflow-mnist/#0
https://www.tensorflow.org/api_docs/
相关文章
Tensorflow入门——分类问题cross_entropy的选择
同步到我的简书和Steemit
This post has been voted on by the SteemSTEM curation team and voting trail in collaboration with @curie.
If you appreciate the work we are doing then consider voting both projects for witness by selecting stem.witness and curie!
For additional information please join us on the SteemSTEM discord and to get to know the rest of the community!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
pandas / kaggle 等有typo,建议修复哦
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
谢谢,已update
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit