本博客所有内容以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢!
Mnist图像分类(tensorflow)
1.1案例概述
1.1.1 案例简介
用深度学习做项目,当下最流行的就是Tensorflow框架了。那么什么是Tensorflow呢?
Tensorflow是一个Google开发的第二代机器学习系统,克服了第一代系统DistBelief仅能开发神经网络算法、难以配置、依赖Google内部硬件等局限性,应用更加广泛,并且提高了灵活性和可移植性,速度和扩展性也有了大幅提高。字面上理解,TensorFlow就是以张量(Tensor)在计算图(Graph)上流动(Flow)的方式的实现和执行机器学习算法的框架。
要想使用深度学习做项目,我们就要学会怎么使用tensorflow。在这一章我们就以mnist数据集为例,通过tensorflow搭建分类算法,从而真正的了解怎么使用tensorflow。
1.1.2 功能概述
本案例主要让你从零学会使用tensorflow,前面先简单的介绍如何使用tensorflow,后面以Mnist数据集为例,用不同的方法对其进行分类。分为以下五个功能。
- tensorflow的简单操作,让你了解tensorflow的规则和使用方法
- 用tensorflow做线性回归,对前面介绍的方法进行实践
- 用tensorflow做逻辑回归,这里是进入神经网络的第一步
- 构建神经网络结构,用tensorflow一步一步去构建神经网络结构
1.1.3 数据描述
Mnist数据集是来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据。
tensorflow中已经提供了这份数据集,我们直接从中调用即可
# 导入各种包
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
# 下载数据集
mnist = input_data.read_data_sets('data/', one_hot=True)
print (" 类型是 %s" % (type(mnist)))
print (" 训练数据有 %d" % (mnist.train.num_examples))
print (" 测试数据有 %d" % (mnist.test.num_examples))
运行结果
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
类型是 <class 'tensorflow.contrib.learn.python.learn.datasets.base.Datasets'>
训练数据有 55000
测试数据有 10000
我们发现这份数据集分为训练数据和测试数据,数据量还可以,不是很大,用cpu足够了。
接下来我们看一下Mnist数据集的规格是什么样的。内置数据集有个好处,就是别人帮我们把数据的属性都写好了,我们直接调用即可。
trainimg = mnist.train.images
trainlabel = mnist.train.labels
testimg = mnist.test.images
testlabel = mnist.test.labels
print (" 数据类型 is %s" % (type(trainimg)))
print (" 标签类型 %s" % (type(trainlabel)))
print (" 训练集的shape %s" % (trainimg.shape,))
print (" 训练集的标签的shape %s" % (trainlabel.shape,))
print (" 测试集的shape' is %s" % (testimg.shape,))
print (" 测试集的标签的shape %s" % (testlabel.shape,))
运行结果
数据类型 is <class 'numpy.ndarray'>
标签类型 <class 'numpy.ndarray'>
训练集的shape (55000, 784)
训练集的标签的shape (55000, 10)
测试集的shape' is (10000, 784)
测试集的标签的shape (10000, 10)
我们发现训练集的shape 是(55000, 784),55000是指有55000张图像,784是指每张图像的大小,也就是28281,28*28可以理解为图像的长和宽,1是指这些图是黑白图,也就是颜色通道。每张图都有784个像素点。 训练集的标签的shape是(55000, 10),55000还是指有55000张图像,10是指有10类标签,0到9一共是10个数据。这里面的意思是指每张图都会对应出0到9这十类标签,然后每类标签下都对应着数字0和1。具体我们看下图。
当数字是2的时候,在2这个标签下对应的是数字是1,其他都是0。同理其他数字也是这样。label就是这样的编码。
我们最后就会预测每个类别的概率值,看看预测的哪个类别的概率最大,那么这个手写图像就对应哪个类别
来看一下图像具体长什么样子
nsample = 5
randidx = np.random.randint(trainimg.shape[0], size=nsample)
for i in randidx:
curr_img = np.reshape(trainimg[i, :], (28, 28)) # 28 by 28 matrix
curr_label = np.argmax(trainlabel[i, :] ) # Label
plt.matshow(curr_img, cmap=plt.get_cmap('gray'))
print ("" + str(i) + "th 训练数据 "
+ "标签是 " + str(curr_label))
plt.show()
代码解释:
这里是随机挑选了5张图,并且打印了它的标签。我就不全部放进来了,直接看看运行后的结果
Mnist数据集就是长这个样子,然后我们的任务就是训练出来一个分类器,让计算机自动识别出来这些手写数据
我们训练模型的时候肯定不是一次性读入所有的数据,而是分批次读取,也就是每次取一个batch,具体操作如下
# Batch数据
batch_size = 100
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
print ("Batch数据 %s" % (type(batch_xs)))
print ("Batch标签 %s" % (type(batch_ys)))
print ("Batch数据的shape %s" % (batch_xs.shape,))
print ("Batch标签的shape %s" % (batch_ys.shape,))
这里是每次随机取出100张图像拿来训练
运行结果
Batch数据 <class 'numpy.ndarray'>
Batch标签 <class 'numpy.ndarray'>
Batch数据的shape (100, 784)
Batch标签的shape (100, 10)
1.2 实现分析
本案例的目的就是让大家学会使用tensorflow,为后面的深度学习案例做准备。
整体的结构如图所示
流程概述:
1)tensorflow的基本结构介绍,主要是介绍它的操作规矩
2)用tensorflow实现线性回归,先生成一份线性分布的数据,然后构建模型,去拟合这份数据,用梯度下降法去优化这个模型,最终得到好的权重参数
3)用tensorflow实现逻辑回归,这是在为后面构建神经网络结构做准备,神经网络比逻辑回归多了两个特点,一个是层次结构,另一个是激活函数。所以实现逻辑回归很重要。先导入所需工具包以及Mnist数据集,然后设置参数,指定数据集x和y。对权重参数进行初始化,构造模型,迭代计算,最后用测试数据进行测试,这些操作都会在后面的代码中详细体现。
4)构建神经网络结构,这里构建的是单层的。有了前面的基础,这里就会变得很容易。我们只要在逻辑回归的基础上实现出它的那两个特点,层次结构以及激活函数,那么就可以结束了。整个操作流程和逻辑回归的一样,具体代码见后面。最后还介绍了一下双层的神经网络结构,流程其实也是一样的,只不过多了权重参数,看后面代码便可了解。
1.2.1 tensorflow的简单操作
我们先从最简单的问题入手,怎么用tensorflow定义一个变量。在这里和python定义变量的方法不一样,创建一个变量首先要指定它的格式,也就是tensor格式,因为在tensorflow里的一系列计算它对数据的格式是有要求的,所以我们要先把变量指定成tensor格式。比如
w是行向量,x是列向量,y是把w,x作矩阵相乘
import tensorflow as tf
w = tf.Variable([[0.5,1.0]])
x = tf.Variable([[2.0],[1.0]])
y = tf.matmul(w, x)
在tensorflow中,我们得遵守人家的规矩,按照它们的要求来操作。创建完这些变量还没完,这些操作仅仅是声明了有这么个事,但还没有执行,所以还要把它们初始化出来——全局变量初始化。然后打开一个session,session可以简单地理解成被开辟出来的一个可计算区域,然后执行一下(也就是run一下),才能会有效果。前面写的都是摆设,只有在session里run一下,才是被执行。通过下面代码我们就会计算出y值
#全局变量初始化
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
print (y.eval())
tensorflow中还有很多操作,跟numpy很类似。比如我们想构建一个随机的正态分布。
[2,3]是指定的维度,mean是指定的平均值,stddev是指定的标准偏差
norm = tf.random_normal([2, 3], mean=-1, stddev=4)
我们再用tensorflow做一个洗牌的操作
c是我们构造的常量数据
c = tf.constant([[1, 2], [3, 4], [5, 6]])
shuff = tf.random_shuffle(c)
上面的操作还没结束,我们还没给它开辟一个计算区域呢,开辟session区域之后,再run一下,就可以得到结果了
# 每一次执行结果都会不同
sess = tf.Session()
print (sess.run(norm))
print (sess.run(shuff))
运行结果
[[-5.58110332 0.84881377 7.51961231]
[ 3.27404118 -7.22483826 7.70631599]]
[[5 6]
[1 2]
[3 4]]
为了更好的理解,我们再举个例子,假设我们要实现这么一个功能,做出一个循环,每次递增1。用python是非常好实现的,但在tensorflow中有点麻烦,我们来看一下
state = tf.Variable(0)
new_value = tf.add(state, tf.constant(1))
update = tf.assign(state, new_value)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(state))
for _ in range(3):
sess.run(update)
print(sess.run(state))
代码解释:
state是指定的初始值变量,为0。new_value是指新的值,做的是一个加法的操作,里面传入的参数是state初始值,和一个常量1。意思就是每次加1。update是做了一个赋值的操作,就是把新的值new_value赋值给state,相当于"+="的操作。上面的这些操作声明好之后,我们该给它开辟一个可计算区域了。第一步就是进行一个全局变量的初始化,然后再去执行我们定义的操作
接下来我们说一个比较重要的操作,placeholder操作,这个操作大概意思就是先固定好shape值,然后再传值进去。就像是先挖好一个坑,固定坑的大小,这样就把数据的格式定好了,埋进去的萝卜大小都一样,只是品种不同。用这样的操作好处就是可以边赋值边操作。比如batch,我们训练数据的时候不是一次性把数据全部输入进去,而是分批次输入,输入的数据格式是一样的,但是数据的值是不一样的,这时候就可以用这种操作了。也就是每次数据的维度(shape)是一样的,那么我们先固定好它的维度,然后就可以传入格式相同,但值不一样的数据了
input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)
output = tf.multiply(input1, input2)
with tf.Session() as sess:
print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))
代码解释:
input1和input2是我们挖好了的两个坑,要求传入的数值是tf.float32的格式。output就是我们要执行的乘法操作,里面传入的参数是没有值的,仅仅只是坑,这个时候我们开辟一个可计算的区域,然后在实际执行操作(run)的时候进行赋值。run里面是你要执行的操作,feed_dict是一个字典的结构,在里面传入数值。
1.2.2 用tensorflow做线性回归
线性回归描述:
现在有一些数据,这些数据有自己的分布规则,然后我们可以找出一条线,最好的拟合这个数据。
以上就是我们要实现的算法。
1)构造出一份数据
随机生成1000个点,围绕在y=0.1x+0.3的直线周围,一会儿我们的任务就是要去求这条直线,也就是求出这条线的权重参数,所以把数据造在这条线的周围,这样一会儿训练的时候看看计算出来的权重参数是不是接近这条线的权重参数。
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
num_points = 1000
vectors_set = []
for i in range(num_points):
x1 = np.random.normal(0.0, 0.55)
y1 = x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
vectors_set.append([x1, y1])
# 生成一些样本
x_data = [v[0] for v in vectors_set]
y_data = [v[1] for v in vectors_set]
plt.scatter(x_data,y_data,c='r')
plt.show()
代码解释:
vectors_set这个是用来装我们造的数据,也就是那些随机点的坐标。x1是我们的横坐标,指定范围是0.0到0.55,y1是我们的纵坐标,要利用x1来求出y1,这就是那个方程y=0.1x+0.3。y1后面又加了一个np.random.normal(0.0, 0.03),这样做的目的就是为了让这些点有个幅动的范围,然后把x1,y1存到vectors_set这里。x_data就是所有的横坐标,y_data就是所有的纵坐标。我们来看一下代码运行之后的结果
这便是我们生成的数据集,这里面有1000个点,是围绕着方程y=0.1x+0.3的一个幅动,幅动范围不是很大。那么我们能不能根据这些点得到y=wx+b中的w和b呢?
2)线性回归器
接下来我们要根据这些数据训练出一个线性回归器,然后它可以帮我们求出w和b。
基本操作流程:
首先随机初始化w和b,然后根据x_data可以得到一个预测值,有了预测值我们可以和真实值y_data进行一个比较,得到loss值(损失值),最后再利用梯度下降法去优化参数,最小化loss值。
我们用tensorflow实现一下上面的操作流程
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0), name='W')
b = tf.Variable(tf.zeros([1]), name='b')
y = W * x_data + b
loss = tf.reduce_mean(tf.square(y - y_data), name='loss')
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss, name='train')
代码解释:
生成1维的W矩阵,取值是[-1,1]之间的随机数,生成1维的b矩阵,初始值是0, 经过计算得出预估值y。以预估值y和实际值y_data之间的均方误差作为损失值(loss值)。optimizer是定义了一个梯度下降的优化器,里面传入的参数0.5是学习率。train是训练过程,采用梯度下降法来优化参数,使loss值最小。
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
print ("W =", sess.run(W), "b =", sess.run(b),"lossess.run(b)s =", sess.run(loss))
for step in range(20):
sess.run(train)
print ("W =", sess.run(W), "b =", sess.run(b), "loss =", sess.run(loss))
代码解释:
流程定义好之后就要开辟一个可计算的区域,然后全局变量初始化,最后再session中run一下。然后我们打印一些日志信息,并且迭代20次
我们来看一下最终的运行结果
W = [-0.72371006] b = [0.] loss= 0.30402443
W = [-0.467951] b = [0.3085696] loss = 0.099842414
W = [-0.29404098] b = [0.30557194] loss = 0.048583176
W = [-0.17329207] b = [0.30353367] loss = 0.023872528
W = [-0.08945438] b = [0.30211845] loss = 0.011960209
W = [-0.03124467] b = [0.30113584] loss = 0.0062176106
W = [0.00917115] b = [0.30045357] loss = 0.003449263
W = [0.03723244] b = [0.2999799] loss = 0.0021147195
W = [0.05671579] b = [0.299651] loss = 0.0014713728
W = [0.07024335] b = [0.29942265] loss = 0.0011612334
W = [0.07963573] b = [0.2992641] loss = 0.0010117239
W = [0.08615699] b = [0.299154] loss = 0.00093964936
W = [0.0906848] b = [0.2990776] loss = 0.00090490433
W = [0.09382852] b = [0.29902452] loss = 0.0008881546
W = [0.09601125] b = [0.2989877] loss = 0.00088008004
W = [0.09752675] b = [0.2989621] loss = 0.0008761875
W = [0.09857899] b = [0.29894432] loss = 0.0008743111
W = [0.09930957] b = [0.298932] loss = 0.00087340653
W = [0.09981682] b = [0.29892343] loss = 0.0008729704
W = [0.10016902] b = [0.2989175] loss = 0.0008727602
W = [0.10041355] b = [0.29891336] loss = 0.00087265886
看一下结果,我们发现,W一开始就是一个随机的值,b是0,随着迭代的次数增加,不断的训练,loss值在变小,w和b在不断的接近真实值。我们的方程是y=0.1x+0.3,w的真实值是0.1,b的真实值是0.3。我们发现经过20次迭代之后,w的值是0.10041355,b的值是0.29891336,很接近真实值了。
我们把训练出来的这条线画出来看看是否拟合这些数据点
plt.scatter(x_data,y_data,c='r')
plt.plot(x_data,sess.run(W)*x_data+sess.run(b))
plt.show()
运行结果如下:
这条线还是很拟合我们的数据点的
1.2.3 用tensorflow做逻辑回归
接下来我们可以对Mnist数据集进行分类,分类算法我们采用逻辑回归算法。
1)导入工具包并下载数据集
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
mnist = input_data.read_data_sets('data/', one_hot=True)
2)设置参数
numClasses = 10
inputSize = 784
trainingIterations = 50000
batchSize = 64
逻辑回归是个分类算法,所以我们的参数得包含类别数numClasses,这是输出;然后还要有输入,输入是图片,所以图片的大小inputSize;迭代次数trainingIterations;每次迭代的图片数量batchSize
3)指定x和y的大小
X = tf.placeholder(tf.float32, shape = [None, inputSize])
y = tf.placeholder(tf.float32, shape = [None, numClasses])
X是输入,y是标签。这里指定成placeholder的格式,因为我们训练是一个batch一个batch训练,这些数据的格式是定下来的,不会改变的。这样方便我们的计算。这里的shape是指维度的大小,也就是坑的大小,None值暂时我们认为它是无限多,就是可以传入任意数量的数据,这里可以写batch的大小,也就是64,也可以写None,总之只要把数据的大小固定住了就行。
4)参数初始化
W1 = tf.Variable(tf.random_normal([inputSize, numClasses], stddev=0.1))
B1 = tf.Variable(tf.constant(0.1), [numClasses])
这里还是随机初始化参数,我们来推导一下W1的维度是怎么计算出来的。输入数据的维度是(64784),输出类别的维度是(6410),输入数据乘以权重参数W1可以得到输出类别,那么反推W1的维度应该是(784*10),也就是inputSize和numClasses相乘。B1是偏置参数,所有的偏置参数和输出结果的维度是一致的,我们指定B1为一个常数。
5)构造模型
y_pred = tf.nn.softmax(tf.matmul(X, W1) + B1)
loss = tf.reduce_mean(tf.square(y - y_pred))
opt = tf.train.GradientDescentOptimizer(learning_rate = .05).minimize(loss)
预测值y_pred是指每个类别的概率值,有了预测值之后就可以计算loss值,我们希望预测值跟真实值越接近越好,也就是让loss值越小越好。计算出loss值再用梯度下降法优化参数,让loss值最小。这些过程是类似的。
然后我们想打印一个结果,就是训练和测试的时候的准确度是多少
correct_prediction = tf.equal(tf.argmax(y_pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
这里直接拿预测值和真实值做对比,预测值是y_pred,真实值是y。tf.argmax(y_pred,1)的意思是从y_pred矩阵中找出最大的那个预测概率。y_pred是一个包含十个预测概率值的向量,然后通过tf.argmax函数可以找到组内最大的预测概率值。这样我们就可以对比预测值和真实值是否相等。统计预测值与真实值相等的个数,再除以总数就会得到精度accuracy
操作都声明好了,接下来要开辟一个可计算的区域,全局初始化,再run一下。
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
6)迭代计算
for i in range(trainingIterations):
batch = mnist.train.next_batch(batchSize)
batchInput = batch[0]
batchLabels = batch[1]
_, trainingLoss = sess.run([opt, loss], feed_dict={X: batchInput, y: batchLabels})
if i%1000 == 0:
train_accuracy = accuracy.eval(session=sess, feed_dict={X: batchInput, y: batchLabels})
print ("step %d, training accuracy %g"%(i, train_accuracy))
代码解释:
我们要迭代50000次,首先要取出batch个数据,通过mnist自带的函数我们可以取出batch个数据, batchInput 相当于x,batchLabels相当于y。batchInput 的大小是(64784),batchLabels的大小是(6410)。然后是判断,整千次我们才打印。如果是整千次,我们就计算一下精度值,而精度值的需要预测值和真实值,所以还要把batchInput和batchLabels作为参数传进去。
我们来看一下最终的运行结果
step 0, training accuracy 0.0625
step 1000, training accuracy 0.546875
step 2000, training accuracy 0.703125
step 3000, training accuracy 0.796875
step 4000, training accuracy 0.6875
step 5000, training accuracy 0.84375
step 6000, training accuracy 0.875
step 7000, training accuracy 0.890625
step 8000, training accuracy 0.84375
step 9000, training accuracy 0.90625
step 10000, training accuracy 0.828125
step 11000, training accuracy 0.859375
step 12000, training accuracy 0.859375
step 13000, training accuracy 0.8125
step 14000, training accuracy 0.921875
step 15000, training accuracy 0.921875
step 16000, training accuracy 0.9375
step 17000, training accuracy 0.859375
step 18000, training accuracy 0.890625
step 19000, training accuracy 0.921875
step 20000, training accuracy 0.890625
step 21000, training accuracy 0.953125
step 22000, training accuracy 0.859375
step 23000, training accuracy 0.921875
step 24000, training accuracy 0.9375
step 25000, training accuracy 0.9375
step 26000, training accuracy 0.96875
step 27000, training accuracy 0.8125
step 28000, training accuracy 0.859375
step 29000, training accuracy 0.9375
step 30000, training accuracy 0.875
step 31000, training accuracy 0.90625
step 32000, training accuracy 0.9375
step 33000, training accuracy 0.921875
step 34000, training accuracy 0.90625
step 35000, training accuracy 0.9375
step 36000, training accuracy 0.96875
step 37000, training accuracy 0.9375
step 38000, training accuracy 0.90625
step 39000, training accuracy 0.921875
step 40000, training accuracy 0.90625
step 41000, training accuracy 0.9375
step 42000, training accuracy 0.9375
step 43000, training accuracy 0.890625
step 44000, training accuracy 0.890625
step 45000, training accuracy 0.921875
step 46000, training accuracy 0.953125
step 47000, training accuracy 0.90625
step 48000, training accuracy 0.8125
step 49000, training accuracy 0.953125
可以发现最终迭代了50000次,精度在不断的提升,模型的效果在提升,最终精度可以达到0.95。
7)测试结果
然后我们用测试数据集进行一个测试,看看模型的效果究竟如何
batch = mnist.test.next_batch(batchSize)
testAccuracy = sess.run(accuracy, feed_dict={X: batch[0], y: batch[1]})
print ("test accuracy %g"%(testAccuracy))
代码解释:
还是调用mnist自带的函数,这里是mnist.test,之前训练的时候我们调用的是mnist.train。然后我们来看一下运行结果
test accuracy 0.9375
测试的精度是0.9375,效果还是不错的
1.2.4 神经网络结构
1.2.4.1 单层神经网络
1)导入mnist数据集
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("data/", one_hot=True)
首先来看一下我们的任务是什么。
我们的任务就是把图片(28281)输入到神经网络中,最终得到十个类别的预测概率值,这就是一个很简单的分类任务。那么怎么构造神经网络结构呢?
神经网路与逻辑回归相比不同之处在于多了两个特点,一个是层次结构,一个是非线性(激活函数)。看下图,我们拿一张图作为输入举例。
第一步先把向量输入到隐藏层中,意思就是让特征进行一个什么样的组合变换更好,把这784个特征按照什么样的组合方式映射成多少个特征。因此输入层和隐藏层之间就是权重参数矩阵,隐藏层的作用就是对原始特征进行的一个变换,变换之后其分辨识别能力更强,接下来做分类。
简单地说神经网络就是帮我们做了一个特征提取的工作,利用梯度下降寻找到一个合适的中间特征,然后按照这种方式去构造我们的特征,这样便构造出了神经网络结构。它就相当于一个黑盒子,帮助我们去想什么样的特征提取方式,怎么组合好这些特征,组合成什么样最可取。
对照上图我们来用代码展示一下
2)参数设置
numClasses = 10
inputSize = 784
numHiddenUnits = 50
trainingIterations = 10000
batchSize = 100
numClasses是指类别数,一共十个类别;inputSize是输入数据的大小;numHiddenUnits是隐藏层神经元的个数(也就是把784个像素点映射成50个新的特征);trainingIterations是指迭代次数,我们设置为10000次;batchSize是指一次迭代的图片数目。
X = tf.placeholder(tf.float32, shape = [None, inputSize])
y = tf.placeholder(tf.float32, shape = [None, numClasses])
X是我们的输入数据,y是我们的输出标签。依旧是先占一个坑。
3)参数初始化
W1 = tf.Variable(tf.truncated_normal([inputSize, numHiddenUnits], stddev=0.1))
B1 = tf.Variable(tf.constant(0.1), [numHiddenUnits])
W2 = tf.Variable(tf.truncated_normal([numHiddenUnits, numClasses], stddev=0.1))
B2 = tf.Variable(tf.constant(0.1), [numClasses])
我们发现神经网络比逻辑回归多了一个隐层,所以权重参数也就多了。W1和输入相连,输入有784个像素点,然后W1又与隐藏层相连,隐藏层有50个神经元,所以W1的维度是(78450)。B1和输出的维度是一致的,对于输入层而言,输出就是隐藏层,它是有50个特征,所以B1的维度就是50。W2和隐藏层相连,它有50个特征,然后W2又与输出层相连,输出层是十个类别,所以W2的维度就是(5010)。输出是10个类别,所以B2的维度是10。
4)网络结构
hiddenLayerOutput = tf.matmul(X, W1) + B1
hiddenLayerOutput = tf.nn.relu(hiddenLayerOutput)
finalOutput = tf.matmul(hiddenLayerOutput, W2) + B2
finalOutput = tf.nn.relu(finalOutput)
参数初始化完之后就是定义神经网络的结构,隐层的输出hiddenLayerOutput 是W1X+B1,这样还没结束,前面有讲过,神经网络有两个特征,一个是层次结构,另一个就是非线性,怎么才能表达出非线性呢,还要加一个激活函数(relu函数)。所以hiddenLayerOutput = tf.nn.relu(hiddenLayerOutput)
最终的输出finalOutput 是W2hiddenLayerOutput+B2,同理也要加一个激活函数。这样便得到了最终的输出值。
5)网络迭代
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y, logits = finalOutput))
opt = tf.train.GradientDescentOptimizer(learning_rate = .1).minimize(loss)
计算loss值用到了一个交叉熵函数,它可以用来衡量两个数据的相似性,里面传入的参数就是真实标签y,以及我们的预测标签finalOutput。然后用梯度下降法去优化我们的权重参数,最小化loss值即可。
correct_prediction = tf.equal(tf.argmax(finalOutput,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
这里和逻辑回归一样,计算精度
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for i in range(trainingIterations):
batch = mnist.train.next_batch(batchSize)
batchInput = batch[0]
batchLabels = batch[1]
_, trainingLoss = sess.run([opt, loss], feed_dict={X: batchInput, y: batchLabels})
if i%1000 == 0:
trainAccuracy = accuracy.eval(session=sess, feed_dict={X: batchInput, y: batchLabels})
print ("step %d, training accuracy %g"%(i, trainAccuracy))
然后就是老套路了,开辟一个可计算的区域,进行一个全局变量的初始化,然后再run一下,打印一些日志信息,和上面的逻辑回归操作类似
我们看一下运行的结果
step 0, training accuracy 0.13
step 1000, training accuracy 0.79
step 2000, training accuracy 0.83
step 3000, training accuracy 0.88
step 4000, training accuracy 0.91
step 5000, training accuracy 0.87
step 6000, training accuracy 0.89
step 7000, training accuracy 0.84
step 8000, training accuracy 0.89
step 9000, training accuracy 1
随着迭代的进行,我们的精度值在提升。神经网络的效果会比逻辑回归的好很多,因为多了一个非线性以及层次的结构,这会提升它的分类效果。
我们前面做的是单层的神经网络结构,正常我们会设置多层,因为多层的效果好。毕竟神经网络做的是一个特征提取的工作,里面的层次越多,它提取的特征就越细化,这个特征就越有代表性,所以要设置多层。当然凡事过犹不及,设置过多的层会导致过拟合,反而效果不好,所以要设置适当的隐藏层数。那么我们就以两个隐藏层举例,看看如何操作
1.2.4.2 双层神经网络
操作的顺序是一样的
1)参数设置
numClasses = 10
inputSize = 784
numHiddenUnits = 50
numHiddenUnitsLayer2 = 100
trainingIterations = 10000
batchSize = 100
X = tf.placeholder(tf.float32, shape = [None, inputSize])
y = tf.placeholder(tf.float32, shape = [None, numClasses])
参数的设置多了一个隐藏层,也就是这里有两个隐藏层,numHiddenUnits和numHiddenUnitsLayer2
2)参数的初始化
W1 = tf.Variable(tf.random_normal([inputSize, numHiddenUnits], stddev=0.1))
B1 = tf.Variable(tf.constant(0.1), [numHiddenUnits])
W2 = tf.Variable(tf.random_normal([numHiddenUnits, numHiddenUnitsLayer2], stddev=0.1))
B2 = tf.Variable(tf.constant(0.1), [numHiddenUnitsLayer2])
W3 = tf.Variable(tf.random_normal([numHiddenUnitsLayer2, numClasses], stddev=0.1))
B3 = tf.Variable(tf.constant(0.1), [numClasses])
多了一个隐藏层,自然也就多了一组权重参数W3和B3
3)网络结构
hiddenLayerOutput = tf.matmul(X, W1) + B1
hiddenLayerOutput = tf.nn.relu(hiddenLayerOutput)
hiddenLayer2Output = tf.matmul(hiddenLayerOutput, W2) + B2
hiddenLayer2Output = tf.nn.relu(hiddenLayer2Output)
finalOutput = tf.matmul(hiddenLayer2Output, W3) + B3
这里面也发生了变化,不过道理是一样的
4)网络迭代
这里和单层的神经网络是一样的
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y, logits = finalOutput))
opt = tf.train.GradientDescentOptimizer(learning_rate = .1).minimize(loss)
correct_prediction = tf.equal(tf.argmax(finalOutput,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for i in range(trainingIterations):
batch = mnist.train.next_batch(batchSize)
batchInput = batch[0]
batchLabels = batch[1]
_, trainingLoss = sess.run([opt, loss], feed_dict={X: batchInput, y: batchLabels})
if i%1000 == 0:
train_accuracy = accuracy.eval(session=sess, feed_dict={X: batchInput, y: batchLabels})
print ("step %d, training accuracy %g"%(i, train_accuracy))
testInputs = mnist.test.images
testLabels = mnist.test.labels
acc = accuracy.eval(session=sess, feed_dict = {X: testInputs, y: testLabels})
print("testing accuracy: {}".format(acc))
运行结果:
step 0, training accuracy 0.1
step 1000, training accuracy 0.97
step 2000, training accuracy 0.98
step 3000, training accuracy 1
step 4000, training accuracy 0.99
step 5000, training accuracy 1
step 6000, training accuracy 0.99
step 7000, training accuracy 1
step 8000, training accuracy 0.99
step 9000, training accuracy 1
testing accuracy: 0.9700999855995178
我们发现,效果的确比单层的要好,迭代3000次就已经达到很好的效果了。无论你设置多少隐层,万变不离其宗,只需修改一下参数的设置以及网络结构即可。
1.3 总结
本案例主要目的就是让大家学会使用tensorflow,了解它的规则,然后再用最简单的案例让大家深入理解这里面的套路,以便驾驭后面复杂模型的构建。也粗略的讲述了神经网络的构建流程以及原理,没有那些可怕的数学公式。如果大家想要更加深入地去理解神将网络,可以去看一些算法参考书籍,看完之后再回到本案例,相信你会有更大的成长。
以下是我所有文章的目录,大家如果感兴趣,也可以前往查看
👉戳右边:打开它,也许会看到很多对你有帮助的文章