TensorFlow 是一个用于人工智能的开源神器 ,主要为深度学习算法提供了很多函数,以及独特的运算支持。
废话不多说直接上干货。
我的环境:
python3.7
tensorflow==1.13.2
numpy==1.20.2
1、入门示例
import tensorflow as tf
# 第一步:定义计算(计算图)
a = tf.constant([1, 2])
b = tf.constant([2, 3])
res = a+b
print(res)
# 第二步:执行计算(会话中)
sess = tf.Session() # 启动会话
res1 = sess.run(res)
sess.close() # 关闭会话
print(res1)
输出:
>>>res
<tf.Tensor 'add:0' shape=(2,) dtype=int32>
>>>res1
array([3, 5])
res为一个叫Tensor的数据类型,也就是张量
可以看到TensorFlow定义的量只有进入到会话中才会被运算。
TensorFlow分为定义计算和执行计算两步,要求参与运算的数据类型完全相同。
2、TensorFlow数据类型
TensorFlow的数据类型其实就是tensor,就是一个数据流。
细分为两类:
- 常量:constant,常量就是不变的量。
- 变量:Variable,在TensorFlow中定义的变量就是我们之后训练模型要训练的量。
下面通过一个小例子来演示如何用TensorFlow来训练一个模型
2.1 利用TensorFlow构建线性模型
步骤包括以下几步:
- 导入100个样本数据
- 构造一个线性模型
- 定义损失函数
- 定义训练函数
- 启动图
- 初始化变量
- 开始训练
先设定线性模型的结构如下:
数据信息如下,包括输入值x1,x2和结果y
训练模型的意义就是定义变量a,b,c.然后训练这三个变量,使得 y = a x 1 + b x 2 + c y=ax_1+bx_2+c y=ax1+bx2+c成立,如果你对神经网络有所了解的话就可以知道,这里的a和b其实对应的就是x1和x2的权值,而c对应的就是阈值了。
先进行第一步导入100个样本数据。在这之前要先导入相关的库。
数据链接:样本数据集.
import numpy as np
import tensorflow as tf
data = np.float32(np.load('line_fit_data.npy')) # 导入100个样本数据
x_data = data[:, :2] # 样本自变量
y_data = data[:, 2:] # 样本实际值
输出:
>>>data.shape
(100, 3)
可以看到这是一个二维数组,有100行3列。
第二步:构造一个线性模型
w = tf.Variable(tf.zeros([2, 1]))
bias = tf.Variable(tf.zeros([1]))
y = tf.matmul(x_data, w) + bias # 构造一个线性模型
先声明两个变量a,b,并附初始值0.因为是两个变量所以就声明为一个两行一列的数组,而c就声明为一个一维数组。之所以要这样声明是为了方便后面的矩阵运算。
matmul:就是一个矩阵乘法函数
>>>tf.matmul(x_data, w)
<tf.Tensor 'MatMul:0' shape=(100, 1) dtype=float32>
可以看到在进行了矩阵乘法之后和结构为一个100*1的矩阵,在加上bias也就是c,就得到了模型的预测值。
第三步:定义损失函数
那么如何判断模型的预测值和实际结果之间的差距呢,也就是怎么判断预测值和实际值有多像。
这里需要定义一个损失函数。
loss = tf.reduce_mean(tf.square(y_data - y)) # 定义损失函数,均方误差
这个损失函数的意思就是把模型的每一组预测值和每一组实际值做对比,求出均方误差。均方误差越小就表明模型越精确。
第四步:定义训练函数
训练函数要做的就是将损失函数的值不断变小,从而达到预测越来越精确地目的。
optimizer = tf.train.GradientDescentOptimizer(0.5) # 构建梯度下降法优化器
这里用到了梯度下降算法。TensorFlow自带有这个函数,里面的参数指的是学习的速率。
先解释一下什么是梯度下降算法,这个算法的作用是使某个函数的函数值在训练后不断减小,原理和函数的梯度有关,如果把一个函数的每一个变量看作一个维度,那么一个函数的梯度就是这个函数增加最快的一个方向向量,那么与梯度方向相反的方向就是这个函数的值减小最快的方向。
下面以一个二维函数举例子,高维同样可以这样理解。
所谓梯度下降算法就是在寻找一条最快的“下山”的路。它的参数就是每次“下山”的步长,就是函数往梯度的反方向移动一段距离,所以这个值如果太小,那么学习速率就低,如果太大就会学习不准确。
现在开始训练函数,在这个算法中你可以选择是趋于最小值计算还是趋于最大值计算,我们这里当然是希望我们的损失函数的值越小越好,所以这里的train就如下定义。
train = optimizer.minimize(loss) # 定义训练函数
第五步:启动图
sess = tf.Session() #启动会话
sess.close() #关闭会话
TensorFlow的运算严格分为定义计算和执行计算两步,定义计算时还要严格要求数据类型,甚至是数据精度都有要求,虽然定义的时候是麻烦了一点,但是这的确能极大地提高运算速度。
所有的运算在定义之后都要放到会话中执行。
第六步:初始化变量
在TensorFlow中只要涉及到变量就要将变量初始化,前面的将变量赋值为0并不是初始化操作,这里的初始化可以理解为激活变量。
sess.run(tf.global_variables_initializer()) # 初始化变量
当然这一步是需要放到会话中执行的。
第七步:训练模型
现在只需要一个循环不停地使用梯度下降算法更新函数值就可以完成模型的训练了。
for i in range(100):
print('第', i, '轮训练后的模型损失值:', sess.run(loss))
sess.run(train) # 开始训练
可以看到当训练轮数到达100轮的时候误差以及几乎可以忽略了。也就是模型训练完成。输出一下训练得到的a,b,c
>>>sess.run([w, bias])
[array([[0.09999494],
[0.19998254]], dtype=float32), array([0.30001214], dtype=float32)]
可以看到值为0.1,0.2,0.3。也就是说模型最终为:y = 0.1x1 + 0.2x2 + 0.3
下面附上完整代码
import numpy as np
import tensorflow as tf
data = np.float32(np.load('line_fit_data.npy')) # 导入100个样本数据
x_data = data[:, :2] # 样本自变量
y_data = data[:, 2:] # 样本实际值
'''
定义计算(计算图)
'''
w = tf.Variable(tf.zeros([2, 1]))
bias = tf.Variable(tf.zeros([1]))
y = tf.matmul(x_data, w) + bias # 构造一个线性模型
loss = tf.reduce_mean(tf.square(y_data - y)) # 定义损失函数,均方误差
optimizer = tf.train.GradientDescentOptimizer(0.5) # 构建梯度下降法优化器
train = optimizer.minimize(loss) # 定义训练函数
'''
执行计算(会话中)
'''
# 启动会话
sess = tf.Session()
sess.run(tf.global_variables_initializer()) # 初始化变量
for i in range(100):
print('第', i, '轮训练后的模型损失值:', sess.run(loss))
sess.run(train) # 开始训练
sess.run([w, bias])# y = 0.1*x1 + 0.2*x2 + 0.3
sess.close()
下面是计算图模型。
x_data,W,biases为模型的输入,结果矩阵乘法运算后得到模型的预测值,预测值和实际值对比将代价函数传入训练模型。
3、手写数字识别
3.1 认识mnist数据集
在这个项目中我用的是mnist数据集。
资源连接:mnist数据集.
这个文件里是处理好的图片文件,资源解压后将整个文件夹放入工程目录。
这个项目可以算是TensorFlow的helloworld了。
先导入读取文件的库,然后读入数据。
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data') # 读取数据
读取训练样本和样本结果
mnist.train.images
mnist.train.labels
>>>mnist.train.images.shape # (55000, 784) 55000张图片,每张图片有784个像素值
(55000, 784)
>>>mnist.train.labels.shape # 一共有55000张图片,每张图片都有一个标签
(55000,)
输出一下这两个样本的结构。可以看到这里的图片格式是一条像素,就是将每一张图片拉成一长条,一共784个像素点,训练样本一共有55000张图片,训练样本的标签就是一维的表示这55000张图片对应的实际的图片的值。
现在来考虑一下我们要如何定义损失函数,也就是我们要怎么表示预测值和实际值的差距。打个比方如果一张图片的实际值为0,而一个模型的预测值为1,另一个模型的预测值为2,现在我问你这两个预测值哪个更加接近实际值呢?
那么我可以告诉你这是无法比较的,我们的模型并不会涉及数据的大小比较,所以我们的模型如果只能输出这样的预测值,我们是无法估计要怎样朝着与实际值越来越接近的方向前进的。
那么我们对实际值和预测值换一种表达方式,都表示为一个一维10列的数组,各个位置上对应的数表示值为该位置的概率。
实际值:[1,0,0,0,0,0,0,0,0,0] 表示取0的概率为1,其他概率为0,这样依然可以表示实际值为1.
预测值1:[0.4,0.5,0.1,0,0,0,0,0,0,0]
预测值2:[0,0.4,0.5,0.1,0,0,0,0,0,0]
现在虽然预测值依旧是1和2,但是可以看得出来预测值1明显更接近0。
这种编码格式就是独热编码。其实只需要在读取文件时加一个参数就可以实现。
mnist = input_data.read_data_sets('MNIST_data', one_hot=True) # 读取数据
mnist.train.labels[2]
这样读取的数据标签就是独热编码格式了。
>>>mnist.train.labels.shape
(55000, 10)
>>>mnist.train.labels[2]
array([0., 0., 0., 0., 1., 0., 0., 0., 0., 0.])
可以看到现在的标签就是55000*10的数组了。每个图片标签都是独热编码。
具体比较图片的差值就需要引入交叉熵的概念了。
y表示预测值,y’表示实际值,交叉熵越小表示两个实越接近。下面代码实现。
先导入numpy
import numpy as np
real = np.array([1, 0, 0, 0, 0,0,0,0,0,0])
p1 = np.array([0.4,0.5,0.1,0, 0,0,0,0,0,0])
p2 = np.array([0, 0.4,0.5,0.1,0,0,0,0,0,0])
-sum(real*np.log(p1+0.0000001))
-sum(real*np.log(p2+0.0000001))
这里要加0.0000001是因为交叉熵公式里有log,log里的值不能是0。
输出:
>>>-sum(real*np.log(p1+0.0000001))
0.9162904818741863
>>>-sum(real*np.log(p2+0.0000001))
16.11809565095832
这里比较就比较直观了,预测值1的交叉熵小。
3.2 代码实现手写数字识别
现在开始实现手写数字识别。
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets('MNIST_data', one_hot=True) # 读取数据
x_data = tf.placeholder(tf.float32, [None, 784]) # 占位符:样本自变量
y_data = tf.placeholder(tf.float32, [None, 10]) # 占位符:样本目标变量
这里读取数据的时候先保留一个占位符,真正的数据到要运行的时候在传入,相当于保留了一个数据接口,当然这个接口要先定义数据类型,精度和数组的shape,None表示未定,这里的意思就是样本自变量为784列数组,至于多少个,那我不知道,未定。
现在定义网络结构:
w = tf.Variable(tf.zeros([784, 10])) # 网络权值矩阵
bias = tf.Variable(tf.zeros([10])) # 网络阈值
y = tf.nn.softmax(tf.matmul(x_data, w) + bias) # 网络输出
这里在获取网络输出的时候用了softmax函数,这是一个非线性映射函数。具体公式如下:
这样取值后得到的结果刚好符合独热编码格式,即保证每一项都不为0,相加又刚好为1。
定义损失函数,梯度下降器和训练节点
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_data*tf.log(y), axis=1)) # 交叉熵(损失函数)
optimizer = tf.train.GradientDescentOptimizer(0.003) # 梯度下降法优化器
train = optimizer.minimize(cross_entropy) # 训练节点
这里看不懂可以回到前面看看,我前面说明的很详细。这个交叉熵函数里的axis=1表示逐行求交叉熵。tf.reduce_mean表示取均值。
定义一下测试精度:
acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y, axis=1), tf.argmax(y_data, axis=1)), dtype=tf.float32)) # 模型预测值与样本实际值比较的精度
tf.argmax(y, axis=1), tf.argmax(y_data, axis=1)表示逐行取预测值和实际值。
tf.equal表示比较是否相等
tf.cast是将true or false转化为浮点数,true=1.0,false=0.0
最后在取均值就是精度了。
下面就是在会话里运行了。
sess = tf.Session() # 启动会话
sess.run(tf.global_variables_initializer()) # 执行变量初始化操作
for i in range(20000):
x_s, y_s = mnist.train.next_batch(100) #取100个样本
if i%5000 == 0:
acc_tr = sess.run(acc, feed_dict={x_data: x_s, y_data: y_s})
print(i, '轮训练的精度', acc_tr)
sess.run(train, feed_dict={x_data:x_s, y_data:y_s}) # 模型训练
通过mnist.train.next_batch(100)读取随机的100个数据。
return self._images[start:end], self._labels[start:end]
这是mnist.train.next_batch()函数的返回值。是两个,分别对应x和y
输出一下精度,然后开始训练模型
训练完成后就要输入测试集来看看精度怎么样了。
acc_te = sess.run(acc, feed_dict={x_data:mnist.test.images, y_data:mnist.test.labels}) # 测试集精度
print('模型测试精度:', acc_te)
sess.close()
传入测试集数据。当然不要忘了关闭会话。
0 轮训练的精度 0.13
5000 轮训练的精度 0.88
10000 轮训练的精度 0.9
15000 轮训练的精度 0.9
模型测试精度: 0.9044
打印一下进度可以发现准确率可以达到90%,还是不错的。
整体代码如下:
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets('MNIST_data', one_hot=True) # 读取数据
x_data = tf.placeholder(tf.float32, [None, 784]) # 占位符:样本自变量
y_data = tf.placeholder(tf.float32, [None, 10]) # 占位符:样本目标变量
w = tf.Variable(tf.zeros([784, 10])) # 网络权值矩阵
bias = tf.Variable(tf.zeros([10])) # 网络阈值
y = tf.nn.softmax(tf.matmul(x_data, w) + bias) # 网络输出
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_data*tf.log(y), axis=1)) # 交叉熵(损失函数)
optimizer = tf.train.GradientDescentOptimizer(0.003) # 梯度下降法优化器
train = optimizer.minimize(cross_entropy) # 训练节点
acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y, axis=1), tf.argmax(y_data, axis=1)), dtype=tf.float32)) # 模型预测值与样本实际值比较的精度
sess = tf.Session() # 启动会话
sess.run(tf.global_variables_initializer()) # 执行变量初始化操作
for i in range(20000):
x_s, y_s = mnist.train.next_batch(100) #取100个样本
if i%5000 == 0:
acc_tr = sess.run(acc, feed_dict={x_data: x_s, y_data: y_s})
print(i, '轮训练的精度', acc_tr)
sess.run(train, feed_dict={x_data:x_s, y_data:y_s}) # 模型训练
acc_te = sess.run(acc, feed_dict={x_data:mnist.test.images, y_data:mnist.test.labels}) # 测试集精度
print('模型测试精度:', acc_te)
sess.close()