一、基础概念
1. 从Hello World开始
import tensorflow as tf
#创建一个常量运算,将作为一个节点加入到默认计算图中
hello = tf.constant("Hello, World!")
#创建一个TF对话
sess = tf.Session()
#运行并获得结果
print(sess.run(hello))
# b'Hello, World!'
#输出前面的'b'表示Bytes literals(字节文字)
2. TensorFlow的概念
- Tensor 张量
数据结构:多维数组 - Flow 流
计算模型:张量之间通过计算而转换的过程
TensorFlow是一个通过计算图的形式表述计算的编程系统
每一个计算都是计算图上的一个节点,节点之间的边描述了计算之间的关系
3. 计算图(数据流图)的概念
计算图是一个有向图,由以下内容构成
- 一组节点,每个节点都代表一个操作,是一种运算
- 一组有向边,每条边代表节点之间的关系( 数据传递和控制依赖 )
TensorFlow有两种边
- 常规边(实线):代表数据依赖关系。一个节点的运算输出成为另一个节点的输入,两个节点之间有tensor流动( 值传递 )
- 特殊边(虚线):不携带值,表示两个节点之间的控制相关性。比如,happens-before关系,源节点必须在目的节点执行前完成执行
4. 计算图的实例
计算图节点输出的结果不是一个具体的数字,而是一个张量的结构
#一个简单计算图
node1 = tf.constant(3.0, tf.float32, name = "node1")
node2 = tf.constant(4.0, tf.float32, name = "node2")
node3 = tf.add(node1, node2)
print(node3)
# Tensor("Add:0", shape=(), dtype=float32)
创建计算图就是建立计算模型,执行对话才能提供数据并获得结果
#建立对话并显示运行结果
sess = tf.Session()
#更新变量并返回计算结果
print("运行sess.run(node3)的结果:", sess.run(node3))
#关闭session
sess.close()
# 运行sess.run(node3)的结果: 7.0
5. 张量的概念
- 在TensorFlow中,所有的数据都通过张量的形式来表示
- 从功能的角度,张量可以简单理解为多维数组
零阶张量表示标量(scalar),也就是一个数;
一阶张量为向量(vector),也就是 一维数组;
n阶张量可以理解为一个n维数组; - 张量并没有真正保存数字,它保存的是计算过程
6. 张量的属性
Tensor("Add:0", shape=(), dtype=float32)
-
名字(name)
"node:src_output":node 节点名称,src_output 来自节点的第几个输出 -
形状(shape)
张量的维度信息,shape=(),表示是标量 -
类型(type)
每一个张量会有一个唯一的类型
TensorFlow会对参与运算的所有张量进行类型检查,发现类型不匹配时会报错
7. 张量的形状
三个术语描述张量的维度,阶(rank)、形状(shape)、维数(dimension number)
阶 | 形状 | 维数 | 例子 |
---|---|---|---|
0 | () | 0-D | 4 |
1 | (D0) | 1-D | [2,3,5] |
2 | (D0,D1) | 2-D | [[2,3],[3,4]] |
3 | (D0,D1,D2) | 3-D | [[[7],[3]],[[2],[4]]] |
N | (D0,D1,...,Dn-1) | n-D | 形为(D0,D1,...,Dn-1的张量) |
tens1 = tf.constant([[[1,2,2],[2,2,3]],
[[3,5,6],[5,4,3]],
[[7,0,1],[9,1,9]],
[[11,12,7],[1,3,14]]], name="tens1")
print(tens1)
#Tensor("tens1:0", shape=(4, 2, 3), dtype=int32)
#shape,4表示最外维有4个元素,2表示中间维度有2个元素,3表示最里维有3个元素
#查看张量的形状
scalar = tf.constant(100)
vector = tf.constant([1, 2, 3, 4, 5])
matrix = tf.constant([[1, 2, 3],[4, 5, 6]])
cube_matrix = tf.constant([[[1],[2],[3]], [[4],[5],[6]], [[7],[8],[9]]])
print(scalar.get_shape())
print(vector.get_shape())
print(matrix.get_shape())
print(cube_matrix.get_shape())
# ()
# (5,)
# (2, 3)
# (3, 3, 1)
8. 获取张量的元素
- 阶为1的张量等价于向量
- 阶为2的张量等价于矩阵,通过t[i, j]获取元素
- 阶为3的张量,通过t[i, j, k]获取元素
tens2 = tf.constant([[[1,2],[2,3]],[[3,4],[5,6]]])
sess = tf.Session()
print(sess.run(tens2)[1, 1, 0])
sess.close()
# 5
9. 张量的类型
TensorFlow支持14种不同的类型
- 实数 tf.float32, tf.float64
- 整数 tf.int8, tf.int16, tf.int32, tf.int64, tf.uint8
- 布尔 tf.bool
- 复数 tf.complex64, tf.complex128
默认类型: - 不带小数点的数会被默认为int32
- 带小数点的会被默认为float32
import tensorflow as tf
a = tf.constant([1, 2], name = "a")
b = tf.constant([2.0, 3.0], name = "b")
result = a + b
# 类型不匹配而报错:Tensor conversion requested dtype int32 for Tensor with dtype float32: 'Tensor("b:0", shape=(2,), dtype=float32)'
10. 操作
- 计算图中的节点就是操作
加减乘除运算、变量初始赋值都是操作 - 每个运算操作都有属性,它在构建图的时候需要确定下来
- 操作可以和计算设备绑定,指定操作在某个设备上执行
- 操作之间存在顺序关系,这些操作之间的依赖就是“边”
- 如果操作A的输入是操作B执行的结果,那么这个操作A就依赖于操作B
import tensorflow as tf
tf.reset_default_graph() #清除default graph和不断增加的节点
#定义变量a
a = tf.Variable(1, name = "a")
b= tf.add(a, 1, name = "b")
c = tf.multiply(b, 4, name = "c")
d = tf.subtract(c, b, name = "d")
logdir = 'D:\ForPython\log'
#生成一个写日志的writer,并将当前的TensorFlow计算图写入日志
writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
writer.close()
二、基本运算
1. TensorFlow运行模型——会话
会话拥有并管理TensorFlow程序运行时的所有资源,当所有计算完成后需要关闭会话帮助系统回收资源
- 会话的典型模式1
#定义计算图
tens1 = tf.constant([1, 2, 3])
#创建一个会话
sess = tf.Session()
try:
#使用这个创建好的会话来得到关心的运算的结果,比如盗用 sess.run(result)
#来得到张量result的值
print(sess.run(tens1))
except:
print("Exception")
finally:
#关闭会话使得本次运行中使用到的资源可以被释放
sess.close()
# [1 2 3]
- 会话的典型模式2
node1 = tf.constant(3.0, tf.float32, name = "node1")
node2 = tf.constant(4.0, tf.float32, name = "node2")
result = tf.add(node1, node2)
#创建一个会话,并通过Python中的上下文管理器来管理这个会话
with tf.Session() as sess:
#使用这创建好的会话来计算关心的结果
print(sess.run(result))
#不需要再调用Session.close()函数来关闭会话
#当上下文退出时会话关闭和资源释放也自动完成了
# 7.0
- 指定默认的会话
node1 = tf.constant(3.0, tf.float32, name = "node1")
node2 = tf.constant(4.0, tf.float32, name = "node2")
result = tf.add(node1, node2)
sess = tf.Session()
with sess.as_default(): #指定sess为默认的会话,故可直接调用result.eval()
print(result.eval())
# 7.0
#未指定时,result.eval()必须说明会话参数
print(result.eval(session = sess)
# 7.0
#用sess.run()同样可以完成相同功能
print(sess.run(result))
# 7.0
-
交互式环境下设置默认会话
交互式环境下,Python脚本或者Jupyter编辑器下,通过设置默认会话来获取张量的取值更加方便,tf.InteractiveSession 使用这个函数会自动将生成的会话注册为默认会话
node1 = tf.constant(3.0, tf.float32, name = "node1")
node2 = tf.constant(4.0, tf.float32, name = "node2")
result = tf.add(node1, node2)
#定义交互式的session,同时注册为默认会话
sess = tf.InteractiveSession()
print(result.eval())
sess.close()
# 7.0
2. 常量与变量
常量:在运行过程中不会改变的单元,在TensorFlow中无须进行初始化操作
#创建语句
constant_name = tf.constant(value)
#示例
a = tf.constant(1.0, name = "a")
变量:在运行过程中值会改变的单元,在TensorFlow中须进行初始化操作
#创建语句
name_variable = tf.Variable(value, name) #注意V是大写字母
#个别变量初始化
init_op = name_variable.initializer()
#所有变量初始化
init_op = tf.global_variables_initializer()
#示例
node1 = tf.Variable(3.0, tf.float32, name = "node1")
node2 = tf.Variable(4.0, tf.float32, name = "node2")
result = tf.add(node1, node2, name = "add")
sess = tf.Session()
#变量初始化
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(result))
# 7.0
3. 变量赋值
与传统编程语言不同,TensorFlow中的变量定义后,一般无需人工赋值,系统会根据算法模型,训练优化过程中自动调整变量对应的数值。
#如果需要人工赋值,并且不希望训练模型自动更改它
epoch = tf.Variable(0, name = 'epoch', trainable = False) #指明不参加训练
#特殊情况需要人工更新,可用变量赋值语句
update_op = tf.assign(variable_to_be_updated, new_value) #被更新变量,新值
#示例:通过变量赋值1、2、3...5
import TensorFlow as tf
value = tf.Variable(0, name = "value")
one = tf.constant(1)
new_value = tf.add(value, one)
update_value = tf.assign(value, new_value)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for _ in range(5): #执行5次
sess.run(update_value) #每一次更新值(+1)
print(sess.run(value))
# 1
# 2
# 3
# 4
# 5
4. 占位符、Feed数据填充和Fetch数据获取
TensorFlow中的Variable变量类型,在定义时需要初始化,但有些变量定义时并不知道其数值,只有当真正开始运行程序时,才由外部输入,比如训练数据,这时候需要用到占位符。
tf.placeholder占位符,是TensorFlow中特有的一种数据结构,类似动态变量,函数的参数、或者C语言或者Python语言中格式化输出的“%”占位符。
- 占位符 placeholder
#函数接口:先定义一种数据,参数为数据的 Type和 Shape
tf.placeholder(dtype, shape = None, name = None)
x = tf.placeholder(tf.float32, [2, 3], name = 'tx')
#生成一个2x3的二维数组,矩阵中每个元素类型为tf.float32,内部对应的符号名称是tx
- Feed数据填充
如果构建了一个包含placeholder操作的计算图,当在session中调用run方法时,placeholder占用的变量必须通过feed_dict参数传递进去
#定义变量a、b
a = tf.placeholder(tf.float32, name = 'a')
b = tf.placeholder(tf.float32, name = 'b')
#定义a*b的操作 c
c = tf.multiply(a, b, name ='c')
#定义初始化所有变量的操作init
init = tf.global_variables_initializer()
#创建一个会话,并通过Python中的上下文管理器来管理这个会话
with tf.Session() as sess:
sess.run(init) #使用的是占位符,因此这里不进行变量初始化也可以
#通过feed_dict 的参数传递,按字典格式
result = sess.run(c, feed_dict = {a : 8.0, b : 3.5})
print(result)
# 28.0
- 多个操作可以通过一次Feed完成执行
#定义变量 a、b
a = tf.placeholder(tf.float32, name = "a")
b = tf.placeholder(tf.float32, name = "b")
#定义操作 c、d
c = tf.multiply(a, b, name ="c")
d = tf.subtract(a, b, name = 'd')
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
result = sess.run([c,d],feed_dict = {a:[8.0, 2.0, 3.5], b:[1.5, 2.0, 4.]})
print(result)
#取结果中的第一个
print(result[0])
# [array([ 12., 4., 14.], dtype=float32), array([ 6.5, 0. , -0.5], dtype=float32)]
# [ 12. 4. 14.]
#一次返回多个值分别赋给多个变量
rc, rd = sess.run([c,d],feed_dict = {a:[8.0, 2.0, 3.5], b:[1.5, 2.0, 4.]})
print("value of c = ", rc, "value of d = ", rd)
# value of c = [ 12. 4. 14.] value of d = [ 6.5 0. -0.5]
三、TensorBoard 可视化初步
TensorBoard是TensorFlow的可视化工具,通过TensorFlow程序运行过程中输出的日志文件可视化TensorFlow程序的运行状态,TensorBoard和TensorFlow程序跑在不同的进程中。
import tensorflow as tf
#清除default graph 和不断增加的节点
tf.reset_default_graph()
#日志
logdir = 'D:\ForPython\log'
#定义一个简单的计算图,实现向量加法的操作
input1 = tf.constant([1.0, 2.0, 3.0], name = "input1")
input2 = tf.Variable(tf.random_uniform([3]), name = "input2")
output = tf.add_n([input1, input2], name = "add")
#生成一个写日志的writer, 并将当前的TensorFlow计算图写入日志
writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
writer.close()
启动TensorBoard
- 在Anaconda Prompt 中先进入日志存放的目录
- 再运行TensorBoard,并将日志的地址指向程序日志输出的地址
- 命令:tensorboard --logdir=D:\ForPython\log
- 启动服务的端口默认为6006,使用 --port 参数可以改编启动服务的端口
- 在浏览器中访问网址:http://miaoxingren:6006(若打不开-> http://127.0.0.1:6006)