Tensorflow入门
TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)
在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量
(tensor)。它灵活的架构可以在多种平台上展开计算,例如台式计算机中的一个或多个CPU(或
GPU),服务器,移动设备等等。
Tensorflow = tensor + flow 张量流,有点数据流图的含义。
tensorflow 基本用法
- 使用图(graph)表示计算任务
- 在会话(session)的上下文(context)中执行图
- 使用 tensor 表示数据
- 通过 变量(Variable)维护状态
- 使用 feed 和 fetch 可以为任意的操作(arbitrary operation)赋值或者从中获取数据
- TensorFlow 是一个编程系统,使用图表示计算任务。图中的节点被称作 op(operation的缩写)。一个 op 获得 0 个(如后续提到的为了占位的placeholder)或者多个(如加法)Tensor。执行计算,产生0个或者多个(如拆分)Tensor。
- 每个 Tensor 是一个类型化的多维数组。例如,你可以将一小组图像集表示为一个四维的浮点数集,这四个维度分别是 [batch, height, width, channels]。
- 一个 TensorFlow 图(Graph)描述了计算过程。为了进行计算,图必须在 会话(Session)里被启动。
- 会话(Session)将图中的 op 分发到 CPU 或者 GPU 之类的 设备 上,同时提供执行 op 的方法。
- 这些方法执行后,将产生的 tensor 返回。在 python 语言中,返回的 tensor 是 numpy ndarray 对象。
tensorflow 构建图(Graph)
步骤:
- 创建源 op :源 op 不需要任何输入。例如常量(constant)。源 op 的输出被传递给其他 op 做运算。
- 在会话(Session)中启动图(Graph)。
- 关闭Session以释放资源。
以常量的矩阵乘法举例
import tensorflow as tf
# TensorFlow Python 库有一个默认图 (default graph), op 构造器可以为其增加节点.
# 这个默认图对许多程序来说已经足够用了.
# 创建一个常量 op, 产生一个 1x2 矩阵. 这个 op 被作为一个节点加到默认图中.
#
# 构造器的返回值代表该常量 op 的返回值.
matrix1 = tf.constant([
[3., 3.]
])
# 创建另外一个常量 op, 产生一个 2x1 矩阵.
matrix2 = tf.constant([
[2.],
[2.]
])
# 创建一个矩阵乘法 matmul op , 把 'matrix1' 和 'matrix2' 作为输入.
# 返回值 'product' 代表矩阵乘法的结果.
# 1x2矩阵 * 2x1矩阵 应该得到 1*1矩阵
product = tf.matmul(matrix1, matrix2)
# 到此为止,只是构建图完事了。即使运行,也什么都没有。
'''
默认图现在有三个节点, 两个 constant() op, 和一个matmul() op.
为了真正进行矩阵相乘运算, 并得到矩阵乘法的结果, 必须在会话里启动这个图.
启动图的第一步是创建一个 Session 对象, 如果无任何创建参数, 会话构造器将启动默认图.
'''
'''
# 这里你可能会有一个疑惑,为什么前两个constant()也属于是 op.
起初我也不理解,现在我理解 得到tensor的操作就算是 op.
因为,计算图中节点(op)是代表计算,而计算得到的是tensor.
所以你得到了 matrix1 & matrix2 就是经过了 op 的计算.所以是两个 op.
'''
# 启动默认图.
sess = tf.Session()
# 调用 sess 的 'run()' 方法来执行矩阵乘法 op, 传入 'product' 作为该方法的参数.
# 'product' 代表了矩阵乘法 op 的输出, 传入它是向方法表明, 我们希望取回矩阵乘法 op 的输出.
# 整个执行过程是自动化的, 会话负责传递 op 所需的全部输入. op 通常是并发执行的.
# 函数调用 'run(product)' 触发了图中三个 op (两个常量 op 和一个矩阵乘法 op) 的执行.
# 返回值 'result' 是一个 numpy `ndarray` 对象.
result = sess.run(product)
print(result)
# ==> [[ 12.]]
# 任务完成, 关闭会话.
sess.close()
'''
session对象在使用完后需要关闭以释放资源. 除了显式调用 close 外, 也可以使用 “with” 代码块来自动完成关闭动作.
with tf.Session() as sess:
result = sess.run([product])
print (result)
'''
运行结果:本地运行时会有很多框架相关的输出,这里我仅截取了计算结果。
[[12.]]
Tensorflow Tensor(张量)
构建图的运算过程的输出结果时一个Tensor,主要由三个属性组成:Name、Shape、Type。
- Name 代表的是张量的名字,也是张量的唯一标识,我们可以在每个 op 上添加 name 属性来对节点进行命名,Name 的值表示的是该张量来自于第几个输出结果(编号从0开始)。
- Shape 代表的是张量的维度。
- Type 表示的是张良的类型,每个张量都会有唯一的类型。我们需要注意的是要保证参与运算的张量的类型保持一致,否则会出现类型不匹配的错误。
常见的张量类型:
数据类型 | Python类型 | 描述 |
DT_FLOAT | tf.float32 | 32位浮点数 |
DT_DOUBLE | tf.float64 | 64位浮点数 |
DT_INT64 | tf.int64 | 64位有符号整型 |
DT_INT32 | tf.int32 | 32位有符号整型 |
DT_INT16 | tf.int16 | 16位有符号整型 |
DT_INT8 | tf.int8 | 8位有符号整型 |
DT_UINT8 | tf.uint8 | 8位无符号整型 |
DT_STRING | tf.string | 可变长度的字节数组.每一个张量元素都是一个字节数组 |
DT_BOOL | tf.bool | 布尔型 |
DT_COMPLEX64 | tf.complex64 | 由两个32位浮点数组成的复数:实数和虚数 |
DT_QINT32 | tf.qint32 | 用于量化Ops的32位有符号整型 |
DT_QINT8 | tf.qint8 | 用于量化Ops的8位有符号整型 |
DT_QUINT8 | tf.quint8 | 用于量化Ops的8位无符号整型 |
Tensorflow 变量(Variables)
变量 Variables 维护图执行过程中的状态信息.
通常会将一个统计模型中的参数表示为一组变量.
例如, 你可以将一个神经网络的权重作为某个变量存储在一个 tensor 中. 在训练过程中, 通过重复运行训练图, 更新这个 tensor.
Tensorflow fetch/feed
fetch:为了取回操作的输出内容,可以使用 session 对象的 run() 方法调用执行图时,传入一些 tensor,这些 tensor 会帮助你取回结果。
demo:
import tensorflow as tf
input1 = tf.constant(1.0)
input2 = tf.constant(2.0)
input3 = tf.constant(3.0)
intermed = tf.add(input1, input2)
res2 = tf.multiply(input3, intermed)
with tf.Session() as sess:
res3 = sess.run([intermed, res2])
print(res3, type(res3), type(res3[0]))
不同于上面代码的 sess.run(product)
,这里想输出哪些 tensor 直接全部放入到 [] 中,然后将 [] 作为参数传入 a = sess.run([...])
即可取回操作的输出内容。使用 print(a)
输出查看。
简单理解:fetch 利用 […] 就是输出一些中间结果。
运行结果:省略框架相关输出
[3.0, 9.0] <class 'list'> <class 'numpy.float32'>
feed:使用一个 tensor 值临时替换一个操作的输出结果。可以提供 feed 数据作为 run() 调用的参数。
feed 只在调用它的方法内有效,方法结束,feed 就会消失。最常见的用例是将某些特殊的操作指定为 “feed” 操作,标记的方法是使用 tf.placeholder() 为这些操作创建占位符。
placeholder 是一个数据初始化容器,它与变量最大的不同在于 placeholder 定义的是一个模板,这样我们就可以在 session 运行阶段,利用 feed_dict 的字典结构给 placeholder 填充具体的内容,而无需每次都提前定义好变量的值,大大提高了代码的利用率。
demo:
import tensorflow as tf
input1 = tf.placeholder(tf.float32) # 占位符,先占位
input2 = tf.placeholder(tf.float32) # 占位符,先占位
output = tf.multiply(input1, input2)
with tf.Session() as sess:
# 执行图
result = sess.run([output], feed_dict={input1:[7.], input2:[2.]})
print(result)
运行结果:
[array([14.], dtype=float32)]
tensorflow 一个完整例子
本代码没有实际意义:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 定义数据: 使用numpy生成200个随机点
# array = numpy.linspace(start, end, num=num_points)
# 将在start和end之间生成一个相等间隔的序列,共有num_points个元素。包括start和end
x_data = np.linspace(-0.5, 0.5, 200)[:, np.newaxis]
noise = np.random.normal(0, 0.02, x_data.shape)
y_data = np.square(x_data) + noise
# 定义两个placeholder存放输入数据
# placeholder里面的shape可以不预先给定,会根据feed时自动计算
x = tf.placeholder(tf.float32, [None, 1])
y = tf.placeholder(tf.float32, [None, 1])
# 定义神经网络中间层
# 权值是随时都可以改变的, 所以定义为 Variable
Weights_L1 = tf.Variable(tf.random_normal([1, 10]))
Bias_L1 = tf.Variable(tf.zeros([1, 10])) # 加入偏置项
wx_add_b = tf.matmul(x, Weights_L1) + Bias_L1
Out_L1 = tf.nn.tanh(wx_add_b) # 加入激活函数
# 定义神经网络输出层
Weights_L2 = tf.Variable(tf.random_normal([10, 1]))
Bias_L2 = tf.Variable(tf.zeros([1, 1])) # 加入偏置项
wx_add_b = tf.matmul(Out_L1, Weights_L2) + Bias_L2
predicetion = tf.nn.tanh(wx_add_b) # 加入激活函数
# 定义损失函数(均方差函数)
loss = tf.reduce_mean(tf.square(y-predicetion)) # 均方误差
# 定义反向传播算法(使用梯度下降算法训练) 调用了自动微分工具
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
# tensorboard
with tf.Session() as sess:
# 初始化 初始化不是必须的,为什么不是必须的呢?
sess.run(tf.global_variables_initializer())
# 训练2000次
for _ in range(2000):
sess.run(train_step, feed_dict={x:x_data, y:y_data})
# 获得预测值
predicetion_v = sess.run(predicetion, feed_dict={x:x_data})
plt.figure()
plt.scatter(x_data,y_data) # 散点是真实值
plt.plot(x_data,predicetion_v) # 曲线是预测值
plt.show()
附录
np.linspace()
import numpy as np
import matplotlib.pyplot as plt
# 定义数据: 使用numpy生成200个随机点
x_data = np.linspace(-0.5, 0.5, 200)
print(x_data, type(x_data), x_data.shape)
x_data = x_data[:, np.newaxis]
print(x_data, type(x_data), x_data.shape)
执行结果:
[-0.5 -0.49497487 -0.48994975 -0.48492462 -0.4798995 -0.47487437
-0.46984925 -0.46482412 -0.45979899 -0.45477387 -0.44974874 -0.44472362
. . . . . .
. . . . . .
. . . . . .
0.43467337 0.43969849 0.44472362 0.44974874 0.45477387 0.45979899
0.46482412 0.46984925 0.47487437 0.4798995 0.48492462 0.48994975
0.49497487 0.5 ] <class 'numpy.ndarray'> (200,)
[[-0.5 ]
[-0.49497487]
[-0.48994975]
...
[ 0.48994975]
[ 0.49497487]
[ 0.5 ]] <class 'numpy.ndarray'> (200, 1)
Process finished with exit code 0
关于 numpy.newaxis 的补充:
numpy.newaxis == None,结合 linspace 能插入新维度
上代码:
print(np.newaxis)
print(type(np.newaxis))
x = np.linspace(0,5,6) # 6个元素
print(x, x.shape)
# (6,1)
y = np.linspace(0,5,6)[:, np.newaxis]
print(y, y.shape)
# (1,6)
z = np.linspace(0,5,6)[np.newaxis, :]
print(z, z.shape)
y = np.linspace(0,5,6)[:, np.newaxis]
类似代码简单记忆:一维数组转为二维数组时,np.newaxis 所在维度的 shape 值为1,另一个维度的 shape 值为一维数组长度。
运行结果:
[0. 1. 2. 3. 4. 5.] (6,)
[[0.]
[1.]
[2.]
[3.]
[4.]
[5.]] (6, 1)
[[0. 1. 2. 3. 4. 5.]] (1, 6)
即 np.linspace(0,5,6)[:, np.newaxis]
的shape是 (6,1),np.linspace(0,5,6)[np.newaxis, :]
的shape是 (1,6)。