学习《TensorFlow实战Google深度学习框架》(一)TensorFlow 基础入门

版权声明:本文由洛北辰南原创,转载请注明原创地址,谢谢。 https://blog.csdn.net/qq_18941713/article/details/88184251

前言

原本我是想遇到难点,写一个简短的blog记录一下,而不是从头到尾记笔记。
但是经过过年这几天…我已然忘记之前看了什么内容,遂决定,记录一下笔记,加深印象与理解。
第一章的简介与第二章的环境安装就跳过了。

第3章 TensorFlow入门

本章先介绍TensorFlow的主要工作原理,然后介绍神经网络的主要计算流程,并介绍如何通过TensorFlow实现这些计算。

3.1 TensorFlow计算模型——计算图

计算图是TensorFlow中最基本的一个概念,TensoFlow中的所有计算都会被转化为计算图上的节点。

3.1.1 计算图的概念

TensorFlow的名字中已经说明了它最重要的两个概念——Tensor和Flow。Tensor就是张量,在这里可以理解为多维数组。如果说Tensor代表数据结构,那么Flow则是计算模型。
Flow翻译成中文就是“流”,直观地表达了张梁志坚听过计算相互转化的过程。
TensorFlow是一个通过计算图的形式来表述计算的编程系统。TensorFlow中的每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。
在这里插入图片描述如图为向量a、b相加的计算图

  • 每一个节点都是一个运算,而每一条边代表了计算之间的依赖关系。
  • 如果有a到ADD和b到ADD的边,说明ADD依赖于a、b的值。
  • 没有其他运算依赖于ADD的值(加法结果),所以没有从ADD指向其他节点的边

3.1.2 计算图的使用

TensorFlow程序一般分为两个阶段:

  • 1.定义计算图中所有的计算
  • 2.执行计算
    如:
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

3.2 TensorFlow数据模型——张量

张量是TensorFlow管理数据的形式。

3.2.1 张量的概念

在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)

  • 张量的第一个属性**名字(name)**不仅是一个张量的唯一标识符,同样也能看出张量的计算过程。张量的命名通过"node:src_output"的形式给出。node为节点名称,src_output表示当前张量来自节点的第几个输出。“add:0”为计算节点“add”的第一个输出(编号从0开始)。
  • 张量的第二个属性维度(shape),描述了其维度信息。shape=(2,),表示张量只有一个维度,第一个维度长度为2,即有两个数字的一维数组。维度理解可参照我的另一篇文章:张量维度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

3.2.2 张量的使用

张量主要分为两类:

  • 第一类用途是对中间计算结果的引用。当一个计算包含很多中间结果时,使用张量可以大大提高代码的可读性。
# 使用张量记录中间结果
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函数来获取结果张量的维度信息可以免去人工计算的麻烦。

  • 第二类情况是当计算图构造完成之后,张量可以用来获得计算结果,也就是得到真实的数字。虽然张量本身没有存储具体的数字,但是通过tf.Session().run(result)执行会话,就可以得到计算结果。

3.3 TensorFlow运行模型——会话

会话拥有并管理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上运行。

  • 运算无法在GPU上执行
  • 没有GPU资源
  • 运算输入包含对CPU计算结果的引用
    log_device_placement,为True时日志中会记录每个节点被安排在哪个设备上以方便调试,设置为False可以减少日志量。

3.4 TensorFlow实现神经网络

3.4.3 神经网络参数与TensorFlow变量

# 声明一个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

猜你喜欢

转载自blog.csdn.net/qq_18941713/article/details/88184251