原本我是想遇到难点,写一个简短的blog记录一下,而不是从头到尾记笔记。
但是经过过年这几天…我已然忘记之前看了什么内容,遂决定,记录一下笔记,加深印象与理解。
第一章的简介与第二章的环境安装就跳过了。
本章先介绍TensorFlow的主要工作原理,然后介绍神经网络的主要计算流程,并介绍如何通过TensorFlow实现这些计算。
计算图是TensorFlow中最基本的一个概念,TensoFlow中的所有计算都会被转化为计算图上的节点。
TensorFlow的名字中已经说明了它最重要的两个概念——Tensor和Flow。Tensor就是张量,在这里可以理解为多维数组。如果说Tensor代表数据结构,那么Flow则是计算模型。
Flow翻译成中文就是“流”,直观地表达了张梁志坚听过计算相互转化的过程。
TensorFlow是一个通过计算图的形式来表述计算的编程系统。TensorFlow中的每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。
如图为向量a、b相加的计算图
TensorFlow程序一般分为两个阶段:
import tensorflow as tf
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
一般会采用“import tensorflow as tf”的形式加载tensorflow
示例中没有创建计算图,但也能够实现计算。是由于系统会维护一个默认的计算图,通过tf.get_default_graph()可以获取当前默认的计算图。
# 以下代码示意了如何获取默认计算图以及如何查看一个运算所属的计算图
# 通过a.graph可以查看张量所属的计算图。因为没有特意指定,所以这个计算图应该等于
# 当前默认的计算图。所以下面这个操作输出值为True
print(a.graph is tf.get_default_graph())
除了默认的计算图,TensorFlow支持通过tf.Graph函数来生成新的计算图。不同计算图上的张量和运算都不会共享
# 以下代码示意了如何在不同的计算图上定义和使用变量
import tensorflow as tf
# 创建计算图,名为g1
g1 = tf.Graph()
# g1作为默认计算图启动
with g1.as_default():
# 在计算图g1中定义变量“v”,并设置初始值为0
v = tf.get_variable(
"v", shape=[1], initializer=tf.zeros_initializer)
g2 = tf.Graph()
with g2.as_default():
# 在计算图g2中定义变量“v”,并设置初始值为1
v = tf.get_variable(
"v", shape=[1], initializer=tf.ones_initializer)
# 创建会话运行计算图g1
with tf.Session(graph=g1) as sess:
# 对当前计算图所有变量执行初始化
tf.global_variables_initializer().run()
# 设置变量作用域,并设置可复用为true,若为false则第二次使用同名变量会创建新变量
with tf.variable_scope("",reuse=True):
# 在计算图g1中,变量“v”的取值应该为0,所以下面这行会输出[0.]
print(sess.run(tf.get_variable("v")))
with tf.Session(graph=g2) as sess:
tf.global_variables_initializer().run()
with tf.variable_scope("",reuse=True):
# 在计算图g2中,变量“v”的取值应该为1,所以下面这行会输出[1.]
print(sess.run(tf.get_variable("v")))
(cpu) C:\Documents\workspace\cpu>python main.py
2019-03-05 15:38:36.252842: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[0.]
[1.]
可用如下代码将计算泡在gpu上
g = tf.graph()
# 指定计算运行的设备
with g.device('/gpu:0'):
result = a + b
张量是TensorFlow管理数据的形式。
在TensorFlow程序中,所有的数据都通过张量的形式来表示。从功能的角度上看,张量可以被简单理解为多维数组。其中零阶张量表示标量(scalar),也就是一个数;第一阶张量为向量(vector),也就是一个一维数组;第n阶张量可以理解为一个n维数组。但这只是理解上而言。
实际上张量的实现是对运算结果的引用,张量中并不直接保存数据,它保存的是如何得到这些数据的计算过程。
# 运行如下代码时,不会得到加法的结果,而会得到对结果的引用
import tensorflow as tf
# tf.constant是一个计算,这个计算的结果是一个张量,保存在变量a中
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = tf.add(a, b, name="add")
print (result)
(cpu) C:\Documents\workspace\cpu>python main.py
Tensor("add:0", shape=(2,), dtype=float32)
从运行结果可以看出,一个张量主要保存三个属性:名字(name)、维度(shape)、类型(type)
import tensorflow as tf
a = tf.constant([1, 2])
b = tf.constant([2.0, 3.0])
result = a + b
TypeError: Input 'y' of 'Add' Op has type float32 that does not match type int32 of argument 'x'.
# 可以指定张量类型
a = tf.constant([1, 2],dtype=float32)
若不指定类型,则默认带小数点的是float32,不带小数点的是int32
张量主要分为两类:
# 使用张量记录中间结果
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
# 直接计算向量的和,这样可读性会差一点
result = tf.constant([1.0, 2.0], name="a") +
tf.constant([2.0, 3.0], name="b")
当计算的复杂度增大时,通过张量来引用计算的中间结果可以使代码的可读性大大提升。同时通过张量来存储中间结果可以方便获取中间结果。
比如在卷积神经网络中,卷积层或者池化层有可能改变张量的维度,通过result.get_shape函数来获取结果张量的维度信息可以免去人工计算的麻烦。
会话拥有并管理TensorFlow程序运行时的所有资源。所有计算完成之后需要关闭会话来帮助系统回收资源,否则就可能出现资源泄露的问题。
TensorFlow中使用会话的模式一般有两种:
# 创建一个会话
sess = tf.Session()
# 使用这个创建好的会话来得到运算的结果
sess.run(result)
# 关闭会话,释放本次运行所使用到的资源
sess.close()
该模式在异常状态下退出时,资源不会被释放,导致资源泄露。
为了解决这个问题,采用上下文管理器来管理所有资源。
# 创建一个会话,由上下文管理器来管控资源
with tf.Session() as sess:
sess.run(result)
# 无需手动关闭会话,with段结束,将自动释放资源
系统会自动维护一个默认的计算图,如没有特殊指定,计算会自动加入其中。
而会话也有类似机制,但系统不会自动维护一个默认的会话,需手动指定默认会话,在默认会话中,采用
tf.Tensor.eval的方式计算一个张量的结果。
with tf.Session() as sess:
with sess.as_default():
print (result.eval())
# 以下两个命令功能相同
print(sess.run(result))
print(result.eval(session=sess))
# TensorFlow提供了配置会话的方法
config = tf.ConfigProto(allow_soft_placement=True,
log_device_placement=True)
# TensorFlow提供了直接构建默认会话的方法
sess = tf.InteractiveSession(config=config)
allow_soft_placement,默认值为False,当值为True时,在以下任意条件成立时,GPU上的运算可以放到CPU上运行。
# 声明一个2*3的矩阵变量,标准差为2
weights = tf.Variable(tf.random_normal([2, 3], stddev=2))
TensorFlow随机数生成函数
函数名 | 随机数分布 | 主要参数 |
---|---|---|
tf.random_normal | 正态分布 | 平均值、标准差、取值类型 |
tf.truncated_normal | 正态分布,但如果随机出来的值偏离平均值超过2个标准差,那么这个数将会被重新随机 | 平均值、标准差、取值类型 |
tf.random_uniform | 均匀分布 | 最小、最大取值,取值类型 |
tf.random_gamma | Gamma分布 | 形状参数alpha、尺度参数beta、取值类型 |
TensorFlow常数生成函数
函数名称 | 功能 | 样例 |
---|---|---|
tf.zeros | 产生一个全0的数组 | tf.zeros([2, 3],int32)->[[0, 0, 0], [0, 0, 0]] |
tf.ones | 产生一个全1的数组 | tf.ones([2, 3],int32)->[[1, 1, 1], [1, 1, 1]] |
tf.fill | 产生一个全部为给定数字的数组 | tf.fill([2, 3], 9)->[[9, 9, 9], [9, 9, 9]] |
tf.constant | 产生一个给定值的常量 | tf.constant([1, 2, 3])->[1, 2, 3] |
# -*- coding: utf-8 -*-
# @Time : 2019/3/5 17:08
# @Author : Chord
import tensorflow as tf;
# NumPy是一个科学计算的工具包,这里通过NumPy工具包生成模拟数据集
from numpy.random import RandomState;
# 定义训练数据batch的大小
batch_size = 8
# 定义神经网络的参数,这里还是沿用简单的BP神经网络结构
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
# 在shape的一个维度上使用None可以方便使用不同的batch大小。在训练时需要把数据分
# 成比较小的batch,但是在测试时,可以一次性使用全部数据。当数据集比较小时这样比较
# 方便测试, 单数据集比较大时,将大量数据放入一个batch可能会导致内存溢出
x = tf.placeholder(tf.float32, shape=(None, 2), name="x-input")
y_ = tf.placeholder(tf.float32, shape=(None, 1), name="y-input")
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
# 定义损失函数和反向传播算法
y = tf.sigmoid(y)
cross_entropy = -tf.reduce_mean(
y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
+ (1 - y_) * tf.log(tf.clip_by_value(1 - y, 1e-10, 1.0))
)
learning_rate = 0.01
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
# 通过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
# 定义规则来给出样本的标签。在这里所有x1+x2<1的阳历全被认为是正样本,
# 而其他样本为负样本。0表示负样本,1表示正样本。神经网络采用的0和1表示的方法。
Y = [[int(x1+x2 < 1)] for (x1, x2) in X]
# 创建一个会话来运行TensorFlow程序
with tf.Session() as sess:
# 全局初始化所有变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(w1))
print(sess.run(w2))
# 设定训练次数
STEPS = 5000
for i in range(STEPS):
# 每次选取batch_size个样本进行训练
start = (i * batch_size) % dataset_size
end = min(start+batch_size, dataset_size)
# 通过选取的样本训练神经网络并更新参数
sess.run(train_step,
feed_dict={x: X[start:end], y_: Y[start:end]})
if i % 1000 == 0:
# 每隔一段时间计算在所有数据上的交叉熵并输出
total_cross_entropy = sess.run(
cross_entropy, feed_dict={x: X, y_: Y}
)
print("After %d training step(s), cross entropy on all data is %g" %
(i, total_cross_entropy))
print(sess.run(w1))
print(sess.run(w2))
pass