文章目录
运行tensorflow
1 tensorflow环境地配置
https://blog.youkuaiyun.com/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.youkuaiyun.com/az9996/article/details/90231631
对numpy.c_的理解
https://blog.youkuaiyun.com/az9996/article/details/90231195
TensorFlow多维矩阵的转置,tensorflow.transpose()的理解
https://blog.youkuaiyun.com/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()创建的变量才可以用这种方式来进行复用。