[机器学习实战] 阅读第九章

运行tensorflow

1 tensorflow环境地配置

https://blog.csdn.net/az9996/article/details/90209816

2 创建一个计算图并在会话中执行地四种方式

方法1:

import tensorflow as tf

x=tf.Variable(3,name="x")
y=tf.Variable(4,name="y")
f=x*x*y+y+2

sess=tf.Session()
sess.run(x.initializer)
sess.run(y.initializer)
result=sess.run(f)

print(result)

sess.close()

方法2:
在with块中,会有一个默认会话。调用x.initializer.run等价于调用tf.get_default_session().run(x.initializer),同样,f.eavl等价于tf.get_default_session().run(f)。这种写法不仅可以增加可读性,还可使会话在块中的代码执行结束后自动关闭。

import tensorflow as tf

x=tf.Variable(3,name="x")
y=tf.Variable(4,name="y")
f=x*x*y+y+2

init=tf.global_variables_initializer()

with tf.Session() as sess:
    init.run() #actually initialize all the variables
    result=f.eval()

print(result)

方法3:
除了手工为每个变量调用初始化器之外,还可以使用
global_variables_initializer()函数来完成同样的动作。注意,这个操
作并不会立刻做初始化,它只是在图中创建了一个节点,这个节点会
在会话执行时初始化所有变量:

init = tf.global_variables_initializer() # prepare an init node
with tf.Session() as sess:
init.run() # actually initialize all the variables
result = f.eval()

方法4:
在Jupyter或者在Python shell中,可以创建一个InteractiveSession。它和常规会话的不同之处在于InteractiveSession在创建时会将自己设置为默认会话,因此你无须使用with块(不过需要在结束之后手工关闭会话):

>>> sess = tf.InteractiveSession()
>>> init.run()
>>> result = f.eval()
>>> print(result)
42
>>> sess.close()

3 管理图

你创建的所有节点都会自动添加到默认图(graph)上:

>>> x1 = tf.Variable(1)
>>> x1.graph is tf.get_default_graph()
True

添加节点到指定的图上

大部分情况下,这都不是问题,不过有时候你可能想要管理多个互不依赖的图。可以创建一个新的图,然后用with块临时将它设置为默认图:

>>> graph = tf.Graph()
>>> with graph.as_default():
... x2 = tf.Variable(2)
...
>>> x2.graph is graph
True
>>> x2.graph is tf.get_default_graph()
False

重置默认图

在Jupyter中(或者Python shell中),做实验时你经常会多次执行同一条命令。这样可能会在同一个图上添加了很多重复节点。一种做法是重启Jupyter内核(或者Python shell),更方便的做法是通过tf.reset_default_graph()来重置默认图。

4 节点值的生命周期

当求值一个节点时,TensorFlow会自动检测该节点依赖的节点,并先对这些节点求值。

需要注意的是,TensorFlow不会复用上一步求值的w和x的结果。简而言之,w和x的值会计算两次。
在图的每次执行间,所有节点值都会被丢弃,但是变量的值不会,因为变量的值是由会话维护的(队列和阅读器也会维护一些状态,详见第12章)。变量的生命周期从初始化器的执行开始,到关闭会话才结束。

tensorflow维护变量的值,但不维护节点的值。节点值的生命周期在需要时产生,使用后丢弃。

在单进程的TensorFlow中,即使它们共享同一个计算图,多个会话之间仍然互相隔离,不共享任何状态(每个会话对每个变量都有自己的拷贝)。对于分布式TensorFlow(见第12章),变量值保存在每个服务器上,而不是会话中,所以多个会话可以共享同一变量。

5 TensorFlow中的线性回归

TensorFlow中的操作(简称op,operation)可以接受任意数量的输入,也可以产生任意数量的输出。举个例子,加法和乘法操作都接受两个输入,并产生一个输出。常量和变量(称为源操作)则没有输入。输入和输出都是多维数组,叫作张量(这也是TensorFlow名字的来源)。就像NumPy中的数组一样,张量也有类型和形状。事实上,在PythonAPI中,张量可以用NumPy中的ndarrays来表示。通常它们会用来保存浮点型数据,不过也可以用它来存储字符串(任意的字节数组)。

一段不长的代码,但是收获了许多新知识。

import numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf

housing=fetch_california_housing()
print(housing)
m,n=housing.data.shape
print(m,n)
#np.ones((m,1))构造一个m行1列,数值全为1的矩阵
#np.c_(A,B)将A与B延第二个轴拼接在一起。
housing_data_plus_bias=np.c_[np.ones((m,1)),housing.data]
print(housing_data_plus_bias)

X=tf.constant(housing_data_plus_bias,dtype=tf.float32,name="X")
y=tf.constant(housing.target.reshape(-1,1),dtype=tf.float32,name="y")
print(X.shape)
XT=tf.transpose(X)#张量转置
print(XT)
#matmul矩阵相乘,matrix_inverse求一个或多个矩阵的逆矩阵
theta=tf.matmul(tf.matmul(tf.matrix_inverse(tf.matmul(XT,X)),XT),y)

with tf.Session() as sess:
    theta_value=theta.eval()

print(theta_value)

对tensorflow.constant()的理解
https://blog.csdn.net/az9996/article/details/90231631
对numpy.c_的理解
https://blog.csdn.net/az9996/article/details/90231195
TensorFlow多维矩阵的转置,tensorflow.transpose()的理解
https://blog.csdn.net/az9996/article/details/90232446

6 实现梯度下降

需要手工计算梯度

使用梯度下降法,首先对输入特征向量做归一化,否则训练过程会很慢。

'''
--------------------------------------------------------------------------
'''
from sklearn.preprocessing import StandardScaler

scaler=StandardScaler()    #特征缩放转换器,转换器实例化。归一化
scaled_housing_data=scaler.fit_transform(housing.data)    #构建转换器,并对数据处理
scaled_housing_data_plus_bias=np.c_[np.ones((m,1)),scaled_housing_data]#将两个矩阵延第二个轴进行合并
#算术平均值是沿轴的元素和除以元素的个数。
print(scaled_housing_data_plus_bias.mean(axis=0)) #横着方向,逐列计算列的算术平均值
print(scaled_housing_data_plus_bias.mean(axis=1)) #竖着方向,逐行计算每一行的算术平均值
print(scaled_housing_data_plus_bias.mean()) #默认值是计算扁平数组(一维)的平均值
print(scaled_housing_data_plus_bias.shape)
'''
--------------------------------------------------------------------------
'''
tf.reset_default_graph() #重置默认图

n_epochs=1000 #迭代次数
learning_rate=0.01 #学习率

X=tf.constant(scaled_housing_data_plus_bias,dtype=tf.float32,name="X")#定义常量节点X
y=tf.constant(housing.target.reshape(-1,1),dtype=tf.float32,name="y")#定义常量节点y

#从均匀分布中输出随机值。生成的值在[minval, maxval]范围内服从均匀分布。该范围包括下界最小值,而不包括上界最大值
#random_uniform()会在图中创建一个节点,这个节点会生成一个张量。函数会根据传入的形状和值域来生成随机值来填充这个张量,和numpy的rand()相似。
# 随机生成 theta,形状为 (n+1, n),元素在 [-1.0, 1.0) 之间的符合正态分布的值
theta=tf.Variable(tf.random_uniform([n+1,1],-1.0,1.0,seed=42),name="theta")

#矩阵相乘,实现线性模型
y_pred=tf.matmul(X,theta,name="prediactions")

#print(y_pred)
error=y_pred-y
#print(error)
#计算张量内元素的均值
#计算均方误差
mse=tf.reduce_mean(tf.square(error),name="mse")


#梯度,手工计算出梯度
gradients=2/m*tf.matmul(tf.transpose(X),error)

#这个操作输出一个张量,在这个张量被赋值之后,这个张量包含新的值theta。这使得链接需要使用重置值的操作变得更容易。
#函数assign()创建一个为变量赋值的节点,这里计算的结果是批量梯度下降的步长。
#训练选项
training_op=tf.assign(theta,theta-learning_rate*gradients)

init=tf.global_variables_initializer() #全局变量初始化

with tf.Session() as sess: #建立一个tensorflow会话
    sess.run(init)    #执行初始化

    for epoch in range(n_epochs):    #每一次迭代中每隔100次输出一起均方误差
        if epoch % 100 == 0:
            print("Epoch",epoch,"MSE =",mse.eval())
        sess.run(training_op)    #执行tensorflow图计算

    best_theta=theta.eval()
    print(best_theta)

资料补充:
为什么需要 Mini-batch 梯度下降,及 TensorFl
https://www.meiwen.com.cn/subject/etpamftx.html

使用自动微分

天哪,用数学方法去手动计算微分绝对是个灾难!

幸运的是,tensorflow的autodiff功能可以帮你解决这个烦恼:它可以自动而且高效的算出梯度。只需要把上面代码中的grandients赋值语句换成下面的代码即可:

gradients=tf.gradients(mse,[theta])[0]

grandients()函数接受一个操作符(这里是mse)和一个参数列表(这里是theta)作为参数,然后它会创建一个操作符的列表来计算每个变量的梯度。所以梯度节点将计算MSE相对于theta的梯度常量。

tensorflow使用了反向的autodiff算法,它非常适用于有多个输入和少量输出的场景(高效而精确),在神经网络中这种场景非常常见。它只需要noutputs+1次遍历,就可以求出所有输出相对于输入的偏导。

#梯度,手工计算出梯度
gradients=2/m*tf.matmul(tf.transpose(X),error)

改为:

#梯度 tf.gradients()函数具有自动微分的功能
gradients=tf.gradients(mse,[theta])[0]

使用优化器

TensorFlow会帮你计算梯度,不过它还提供更容易的方法:它内置了很多的优化器,其中就包括梯度下降优化器。你只需要把上面对gradients=…和training_op=…赋值的语句修改成下面的代码即可:

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)

如果你想使用其他类型的优化器,只需要修改一行代码。例如,如果要使用动量优化器(momentum optimizer)(比梯度下降优化器的收敛速度快很多),可以这样定义优化器:

optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,
momentum=0.9)
#梯度 tf.gradients()函数具有自动微分的功能
#gradients=tf.gradients(mse,[theta])[0]
#使用优化器计算梯度
optimizer=tf.train.GradientDescentOptimizer(learning_rate=learning_rate)

#这个操作输出一个张量,在这个张量被赋值之后,这个张量包含新的值theta。这使得链接需要使用重置值的操作变得更容易。
#函数assign()创建一个为变量赋值的节点,这里计算的结果是批量梯度下降的步长。
#训练选项
#training_op=tf.assign(theta,theta-learning_rate*gradients)
training_op=optimizer.minimize(mse)

7 给训练算法提供数据

占位符节点

占位符节点不进行任何实际的计算,而只是在运行时输出你需要它输出的值。一般它用来在训练过程中将值传给tensorflow。如果运行时不为占位符指定一个值,就会得到一个异常。

要创建一个占位符节点,需要调用placeholder()函数并指定输出张量的数据类型。另外,如果想要强制张量的形状,也可以在此指定。如果给维度设置None值,则表示“任意尺寸”。

'''
使用小批量梯度下降,为此需要一种在每次迭代时用下一个小批量替换X和y的方法。
最简单的方法就是使用占位符节点

为了给训练算法提供数据,在常量节点构造阶段将其构造成占位符节点,
占位符节点可以在训练过程中接受参数
'''
# X=tf.constant(housing_data_plus_bias,dtype=tf.float32,name="X")
# y=tf.constant(housing.target.reshape(-1,1),dtype=tf.float32,name="y")
X=tf.placeholder(tf.float32,shape=(None,n+1),name="X")
y=tf.placeholder(tf.float32,shape=(None,1),name="y")

8 保存和恢复模型

在构造期末尾(在所有变量节点都创建之后)创建一个Saver节点,然后在执行期,调用save()方法,并传入一个会话和检查点文件的路径即可保存模型:

...
saver=tf.train.Saver()
...

with tf.Session() as sess:
	...
	save_path=saver.save(sess,"/tmp/my_model.ckpt")

恢复模型同样简单:与之前一样,在构造期末尾创建一个Saver节点,不过在执行期开始的时候,不适用init节点来初始化变量,而是调用Saver对象上的restore()方法:

with tf.Session() as sess:
	saver.restore(sess,"/tmp/my_model.ckpt")
	[...]

默认的,Saver会按照变量名来保存和恢复变量,不过如果你想做更多的控制,也可以在保存和恢复时自己指定名称。

9 用TensorBoard来可视化图和训练曲线

一个比printf()函数更好的可视化方法,使用TensorBoard。

给它一些训练状态,它可以在浏览器中将这些状态以交互的方式展现出来(比如学习曲线)。还可以将图的定义提供给它,然后通过浏览器来进行查看。这种方式对识别图中的错误,发现图的瓶颈等非常有用。

将图的定义和训练状态(比如,训练误差MSE)写入到一个TensorBoard会读取的日志文件中。每次运行程序时,都需要指定一个不同的目录,否则TensorBoard会将这些信息合并起来,这会导致可视化结果变成一团糟。最简单的方式是用时间戳来命名日志文件。

......
training_op=optimizer.minimize(mse)

init=tf.global_variables_initializer() #全局变量初始化

'''
第一行在图中创建了一个节点,这个节点用来求MSE的值,并将其写入tensorboard
称为汇总(summary)的二进制日志字符串中。
第二行创建了一个用来将汇总写入到日志目录的FileWriter。第一个参数指定了日志目录路径
第二个参数(可选)制定了你想要可视化的计算图。

接下来,在执行期中,你需要在训练时定期地求值mse_summary节点(比如,每10个小批量)。
这会将汇总信息输出,然后通过file_writer来将其写入事件文件中
'''
mse_summary=tf.summary.scalar('MSE',mse)
file_writer=tf.summary.FileWriter(logdir,tf.get_default_graph())

......
with tf.Session() as sess: #建立一个tensorflow会话
    sess.run(init)    #执行初始化
    for epoch in range(n_epochs):    #每一次迭代中每隔100次输出一起均方误差
       for batch_index in range(n_batches):
           X_batch,y_batch=fetch_batch(epoch,batch_index,batch_size)
           #save_path=saver.save(sess,"../checkpoint/fetch_batch.ckpt")
           if batch_index % 10==0:
               summary_str=mse_summary.eval(feed_dict={X:X_batch,y:y_batch})
               step=epoch * n_batches + batch_index
               file_writer.add_summary(summary_str,step)
           sess.run(training_op,feed_dict={X:X_batch,y:y_batch})
    best_theta=theta.eval()
    #save_path=saver.save(sess,"../checkpoint/fetch_batch_final.ckpt")
    file_writer.close()
    print(best_theta)

在命令行工具中使用tensorboard --logdir logpath将日志文件加入到tensorboard中,然后通过http://localhost:6006就可以访问tensorboard界面了。

(venv) E:\PythonProject\tensorflow-test\cpu-type\logdir>tensorboard --logdir ../logdir
TensorBoard 1.13.1 at http://Tu:6006 (Press CTRL+C to quit)

在这里插入图片描述

命名作用域

在处理诸如神经网络等复杂模型时,图很容易就变得杂乱而庞大。为了避免这种情况,可以创建命名作用域来将相关的节点分组。

将error(误差)和mse ops(MSE操作)定义到一个叫作“loss”的命名作用域中

with tf.name_scope("loss") as scope:
	error = y_pred - y
	mse = tf.reduce_mean(tf.square(error), name="mse")

在这个作用域中定义的每个操作现在都有一个“loss/”前缀:

>>> print(error.op.name)
loss/sub
>>> print(mse.op.name)
loss/mse

模块化

tensorflow会让你保持DRY(Don’t Repeat Yourself,不要重复自己)原则。

当创建一个节点时,TensorFlow会检查这个名字是否已经存在,如果已经存在了,它会为其添加一个下划线和一个索引以保证唯一性。

使用命名作用域,可以让图更将清晰。

共享变量

如果想要在图的不同组件中共享变量,最简单的做法是先创建,然后将其作为参数传递给需要它的函数。

tensorflow提供一种方法,可以让代码更加清晰,也更加模块化。
如果共享变量不存在,该方法通过get_variable()函数创建共享变量;如果已经存在,就复用该共享变量。期望的行为通过当前variable_scope()的一个属性来控制(创建或者复用)。

'''
下面的代码会创建一个名为"relu/threshold"的变量(因为shape=(),所以结果是一个标量,
并且以0.0为初始值)
'''
with tf.variable_scope("relu"):
	threshold = tf.get_variable("threshold", shape=(),
		initializer=tf.constant_initializer(0.0))

注意,如果这个变量之前已经被get_variable()调用创建过,这里会抛出一个异常。这种机制避免由于误操作而复用变量。如果要复用一个变量,需要通过设置变量作用域的reuse属性为True来显式地实现(在这里,不必指定形状或初始化器)。

with tf.variable_scope("relu", reuse=True):
	threshold = tf.get_variable("threshold")

这段代码会获取既有的"relu/threshold"变量,如果该变量不存在,或者在调用get_variable()时没有创建成功,那么会抛出一个异常。另一种方式是,在调用作用域的reuse_variables()方法块中设置reuse属性为True:

with tf.variable_scope("relu") as scope:
	scope.reuse_variables()
	threshold = tf.get_variable("threshold")

一旦reuse属性设置为True之后,在该块中就不能再设置为False了。另外,如果在块中定义了另外的变量作用域,它们会自动继承resue=True。最后,只有通过get_variable()创建的变量才可以用这种方式来进行复用。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值