一.TensorFlow简介
TensorFlow是一个非常强大的用来做大规模数值计算的库。其所擅长的任务之一就是实现以及训练深度神经网络。(TensorFlow中文社区网址:http://www.tensorfly.cn/)其安装过程这里就不做详细的介绍了,网上有很多教程可以顺利安装,下面就开始介绍如何使用TensorFlow吧~
二.示例程序MNIST
1.MNIST简介
好比编程入门有Hello World,机器学习入门有MNIST。
MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片:
它也包含每一张图片对应的标签,告诉我们这个是数字几。比如,上面这四张图片的标签分别是5,0,4,1。
2.MNIST数据集下载
下载地址:http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_download.html
点击图中蓝色的文件链接即可下载。MNIST数据集被大体分为两部分:60000行的训练数据集和10000行的测试数据集。具体分为四个部分,分别为训练图片集,训练标签集,测试图片集,测试标签集。
也可以在程序中进行自动下载,代码如图:
2.MNIST数据集分析
在MNIST图片集中,所有的图片都有28×28=784个像素。因此,在MNIST训练数据集中,mnist.train.images
是一个形状为 [60000, 784]
的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。
相对应的MNIST数据集的标签是介于0到9的数字,用来描述给定图片里表示的数字。我们使标签数据是"one-hot vectors"。 一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。比如,标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。因此, mnist.train.labels
是一个 [60000, 10]
的数字矩阵。
3.研究方法分析
MNIST问题本质上也是一个分类问题,只不过与我们经常接触的二分类问题不同(逻辑回归),它是一个多分类问题,所以需要一个分类模型来给不同的对象分配概率,在此用到了经典的Softmax回归模型。TensorFlow中文社区中对Softmax回归模型讲解的十分清楚(http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html)
4.简单算法
(代码如果运行出现错误可能是tensorflow的版本有问题,我用的是r1.2的版本,以前版本的语法请自行百度)
# -*- coding: utf-8 -*-
import tensorflow as tf
#导入input_data用于自动下载和安装MNIST数据集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
#创建一个交互式Session
sess = tf.InteractiveSession()
"""
创建两个占位符,x为输入网络的图像,y_为输入网络的图像类别
placeholder的作用是先hold住变量,等到sess启动之后可以从外部传入值,与feed_dict配合使用,下面有使用到
"""
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
#W为权重,b为偏置
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
#初始化变量并启动sess
sess.run(tf.initialize_all_variables())
#实现softmax模型,y为预测的概率分布
y = tf.nn.softmax(tf.matmul(x,W) + b)
"""
在机器学习,通常定义指标来表示一个模型是坏的,这个指标称为成本(cost)或损失(loss),然后尽量最小化这个指标
在这里我们使用交叉熵,用来衡量我们的预测用于描述真相的低效性,其值越小表示预测的越准确
"""
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
"""
TensorFlow用梯度下降算法(gradient descent algorithm)以0.01的学习速率最小化交叉熵
梯度下降算法(gradient descent algorithm)是一个简单的学习过程,TensorFlow只需将每个变量一点点地往使成本不断降低的方向移动。
"""
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
#开始训练模型,这里我们让模型循环训练1000次!
for i in range(1000):
batch = mnist.train.next_batch(50)
train_step.run(feed_dict={x: batch[0], y_: batch[1]})
"""
tf.argmax 函数能给出某个tensor对象在某一维上的其数据最大值所在的索引值
由于标签向量是由0,1组成,因此最大值1所在的索引位置就是类别标签
比如tf.argmax(y,1)返回的是模型对于任一输入x预测到的标签值,而 tf.argmax(y_,1) 代表正确的标签
我们可以用 tf.equal 来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)
"""
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
#计算所学习到的模型在测试数据集上面的正确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})
运行结果:
可以看到,简单的使用softmax回归最后的精确度只有0.9175,效果不是十分理想,所以需要改进代码,这里使用卷积神经网络来改善效果。
5.使用卷积神经网络改进之后的算法
# -*- coding: utf-8 -*-
import tensorflow as tf
#导入input_data用于自动下载和安装MNIST数据集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
#创建一个交互式Session
sess = tf.InteractiveSession()
#创建两个占位符,x为输入网络的图像,y_为输入网络的图像类别
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
#权重初始化函数
def weight_variable(shape):
#输出服从截尾正态分布的随机值
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
"""
偏置初始化函数,由于我们使用的是ReLU神经元
因此比较好的做法是用一个较小的正数来初始化偏置项
以避免神经元节点输出恒为0的问题(dead neurons)
"""
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
"""
*************************************卷积池化解释*************************************
在卷积的过程中,如果x和y方向的跨度太大,会导致图片的信息丢失,所以在这里,
我们使x和y移动的步长都为1,然后通过池化移动的步长为2来进行图像压缩!
创建卷积
卷积使用1步长(stride size),0边距(padding size)的模板,保证输出和输入是同一个大小
x 是一个4维张量,shape为[batch,height,width,channels]
卷积核移动步长为1。填充类型为SAME,可以不丢弃任何像素点
strides=[1,x_movement,y_movement,1],第一位和第四位必须为1!!!
"""
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding="SAME")
"""
创建池化
采用最大池化max pooling,也就是取窗口中的最大值作为结果
x 是一个4维张量,是conv2d卷积之后传出的结果,shape为[batch,height,width,channels]
ksize表示pool窗口大小为2x2,也就是高2,宽2
strides,表示在height和width维度上的步长都为2,
"""
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1,2,2,1],
strides=[1,2,2,1], padding="SAME")
"""
*************************************创建神经网络层*************************************
第一层卷积层
初始化W为[5,5,1,32]的张量,表示卷积核patch大小为5像素*5像素,
第一层网络的输入图片高度为1(只有黑白颜色),输出图片高度为32个单位
相应偏置量b初始化为[32],即输出大小
"""
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
#为了使用卷积层,我们把x变成一个4维向量
#第一维(-1)表示自动推测这个维度的size
#其第2、第3维对应图片的宽(28像素)、高(28像素),最后一维代表图片的颜色通道数
#(因为是灰度图所以这里的通道数为1,如果是rgb彩色图,则为3)
x_image = tf.reshape(x, [-1,28,28,1])
#我们把x_image和权值向量进行卷积,加上偏置项,然后应用ReLU激活函数,最后进行max pooling
#h_conv1的大小为28*28*32,因为卷积的padding方式为”SAME"表示卷积前后图片的长宽不变为28,图片的高度变为了32
#h_pool1的输出即为第一层网络输出,shape为[batch,14,14,32],大小为14*14*32,因为在池化时步长为2,所以长宽都缩小一倍为14
#卷积加上池化可以更好的保存原始图片的信息!!!
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
"""
第二层卷积层
卷积核大小依然是5*5,这层的输入图片高度和输出图片高度为32和64
"""
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = weight_variable([64])
#h_pool2的大小为14*14*64
#h_pool2即为第二层网络输出,shape为[batch,7,7,64],大小为7*7*64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
"""
第三层全连接层
图片尺寸减小到7x7,我们加入一个有1024个神经元的全连接层,用于处理整个图片,让图片高度变得更高
W的第1维size为7*7*64,7*7是h_pool2输出的size,64是第2层输出神经元个数
"""
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
#把池化层输出的向量[batch,7,7,64]reshape成向量[batch, 7*7*64],batch表示样本的数量,-1表示自动推测这个维度的size
#处理之后乘上权重矩阵,加上偏置,然后对其使用ReLU
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
#为了减少过拟合,在输出层前加入dropout
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
"""
输出层
最后,添加一个softmax层
可以理解为另一个全连接层,只不过输出时使用softmax将网络输出值转换成了概率
输出为0-9十个数字的概率
"""
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
"""
*************************************训练和评估预测模型*************************************
"""
#预测值和真实值之间的交叉墒
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
#train op, 与上一个简单的代码不同,对于这个庞大的系统,这里使用ADAM优化器来做梯度下降更优。学习率为0.0001
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
#评估模型,tf.argmax能给出某个tensor对象在某一维上数据最大值的索引。
#因为标签是由0,1组成了one-hot vector,返回的索引就是数值为1的位置
correct_predict = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
#计算正确预测项的比例,因为tf.equal返回的是布尔值,
#使用tf.cast把布尔值转换成浮点数,然后用tf.reduce_mean求平均值
accuracy = tf.reduce_mean(tf.cast(correct_predict, "float"))
#初始化变量
sess.run(tf.initialize_all_variables())
#开始训练模型,循环20000次,每次随机从训练集中抓取50幅图像
for i in range(20000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
#每100次输出一次日志
train_accuracy = accuracy.eval(feed_dict={
x:batch[0], y_:batch[1], keep_prob:1.0})
print "step %d, training accuracy %g" % (i, train_accuracy)
train_step.run(feed_dict={x:batch[0], y_:batch[1], keep_prob:0.5})
print "test accuracy %g" % accuracy.eval(feed_dict={
x:mnist.test.images, y_:mnist.test.labels, keep_prob:1.0})
运行结果:
可以看到,应用卷积神经网络之后的预测准确率提升到了0.9935,效果还是很不错的。
6.总结两种算法
简单算法中,没有对数据进行处理,直接用SoftMax回归进行预测,效果不是特别理想。
卷积神经网络算法中,首先创建了两层卷积网络,每层都是先通过卷积提取信息,再通过池化压缩信息,两层卷积之后,将结果reshape成一般的shape,然后传入平常使用的神经网络系统之中进行训练与预测。