节选自“https://blog.csdn.net/vagrantabc2017/article/details/77002231”
Tensorflow API分两类:
TensorFlow Core:适合细粒度的控制模型。
高层API:如 tf.contrib.learn,是对core的封装,更好用。但contrib目前不稳定。TensorFlow程序分两块:1)构造计算图 2)运行计算图
计算图由节点和边组成,程序不会刻意构造边。
节点包括:常量节点,操作节点,占位符节点,变量节点等。
例:
node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node2)
输出:Tensor(“Const:0”, shape=(), dtype=float32) Tensor(“Const_1:0”, shape=(), dtype=float32)
Const指出这是个常量节点。
例:
node3 = tf.add(node1, node2)
print(“node3: “, node3)
输出:node3: Tensor(“Add:0”, shape=(), dtype=float32)
Add指出这是一个操作节点。
节点不是值,节点只有经过计算后才能得到值。如何计算?用session.run()。
例:
sess = tf.Session()
print(“sess.run(node3): “,sess.run(node3))
输出:sess.run(node3): 7.0
通得session.run()可以得到任意节点的计算值。
TensorBoard可以看到计算图,如何使用以后再讲。
占位符输入与运算符重载:
例:
a = tf.placeholder(tf.float32)
print(a)
输出:Tensor(“Placeholder:0”, dtype=float32)
可见:Placeholder是第三类节点。
例:
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b
print(adder_node)
输出:Tensor(“add:0”, dtype=float32)
可见:加号重载了tf.add()方法,也是一个操作节点,输出区别只有把Add换成了add。
例:
print(sess.run(a, {a: [1,3]}))
print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))
输出:
[ 1. 3.]
7.5
[ 3. 7.]
对run的第二个参数的解释:第二个参数是feed_dict,它的作用是映射图元素到值。如{a: 3, b:4.5}把图元素a映射到值3。经过run()计算后的值的shape,与第一个参数相同。如a被映射到[1,3],前面a定义为float32类型,经过计算后,得到[ 1. 3.]。
乘法也被重载:
例:
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))
输出:22.5
Variable是本节遇到的第四类节点。
例:
W = tf.Variable([.3], dtype=tf.float32)
print(W)
输出:<tf.Variable ‘Variable:0’ shape=(1,) dtype=float32_ref>
在应用中,Placeholder类型的节点用于训练数据x,Variable类型的节点用于随机变量参数。
例:定义线性模型:f(x)=0.3x-0.3
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
讨论:这里的系统和偏移只是初始值,随后可能需要调整。如果用常量节点,显然不合适。
注意:tf.Variable()并不初始化节点,Tensorflow采用了延迟式全局初始化的策略。
例:
init = tf.global_variables_initializer() #init指向初始化全局变量节点的子图
sess.run(init) #初始化全局变量节点
证明Tensorflow采用了延迟式全局初始化的策略:
反例:
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
print(sess.run(linear_model, {x:[1,2,3,4]}))
输出:抛出异常
。。。。。。tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value Variable_1。。。。。。
正例:
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(linear_model, {x:[1,2,3,4]})) #f(x)=0.3x-0.3
输出:[ 0. 0.30000001 0.60000002 0.90000004]
linear_model在这里提供了预测值,即y·,我们自己提供实际值y,并定义和计算损失函数。
线性回归的标准损失模型:(预测值-实际值)的平方和。官网没用标准差。
例:
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
linear_model = W * x + b
init = tf.global_variables_initializer()
sess.run(init)
sess.run(linear_model, {x:[1,2,3,4]})
squared_deltas = tf.square(linear_model – y)
loss = tf.reduce_sum(squared_deltas) ##f(x)=0.3x-0.3
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
输出:23.66,这就是平方和。
手工验证:(0-0)平方+(0.3+1)平方+(0.6+2)平方+(0.9+3)平方
= 0+ 1.69 + 6.76 + 15.21
= 23.66
模型不太好,偏差太大,需要调整参数值。
一个Variable节点,在初始化以后,可以用tf.assign()修改值,这正是Variable节点的价值所在。
例:继续上面的代码,模型修改为: f(x)=-x+1
sess.run([tf.assign(W, [-1.]), tf.assign(b, [1.])]) #调整参数值W=-1,b=1,并重新计算Variable节点
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
输出:0.0,完美。(可惜是人给出的参数,不是模型学习得来的)
小结:本章的第一段core API就学完了,这里我们主要学习了四种常见的节点类型(常量节点,操作节点,占位符节点,变量节点)及其用法,建立了一个简单的线性回归模型。
下面简单讲讲其它的API。
Tensorflow当前版本为1.2.1,包结构如下:
Tensorflow
|——-core 核心包
|——-python python接口包
|——-contrib 目前还在开发中,接口不稳定
|——-examples 样例
|——-tensorboard 计算图看板
第二段下面学习一个ft.train的API。tf.train API的包位置在tensorflow.python.training中。
优化器(optimizers)用于缓慢的修改变量的值,以使得损失函数达到最小。
最简单的优化器是坡度下降(gradient descent)优化器,它计算损失函数对特定变量的偏导来修改变量值。
tf.gradients用于求偏导,举个例子。
f(x,y)=xy中,对x求偏导,很明显是y。验证如下:
x=tf.Variable([[3,5]])
print(x) #1×2的矩阵
y=tf.Variable([[2],[6]])
print(y) #2×1的 矩阵
fx=tf.matmul(x, y) #得到1×1的矩阵
init=tf.global_variables_initializer()
sess.run(init)
print(sess.run(fx))
输出:
<tf.Variable ‘Variable:0’ shape=(1, 2) dtype=int32_ref>
<tf.Variable ‘Variable_1:0’ shape=(2, 1) dtype=int32_ref>
[[36]]
也就是说,矩阵X和Y相乘,得到矩阵[[36]],令它为矩阵Z。
如果对矩阵Z求X的偏导,应该得到Y。
x=tf.Variable([[3,5]])
y=tf.Variable([[2],[6]])
fx=tf.matmul(x, y) #得到1×1的矩阵
yy=tf.gradients(fx,[x]) #对模型fx求对x的偏导。
sess.run(tf.global_variables_initializer())
print(sess.run(yy))
输出:[array([[2, 6]])],可以看到,得到的yy正是设想的x的偏导y.
TensorFlow中的GradientDescentOptimizer优化器采用了类似的求导算法,底层细节被封装。
完整的代码如下:
import tensorflow as tf
W = tf.Variable([.3], dtype=tf.float32) #W初值为0.3
b = tf.Variable([-.3], dtype=tf.float32) #b初值为-0.3
x = tf.placeholder(tf.float32) #x由训练数据提供
linear_model = W * x + b #建立模型
y = tf.placeholder(tf.float32) #y由标注数据给出
loss = tf.reduce_sum(tf.square(linear_model – y)) # 损失函数,由平方和表征
optimizer = tf.train.GradientDescentOptimizer(0.01) #最简单的优化器,坡度下降,学习率为0.01
train = optimizer.minimize(loss) #把损失函数值降到最小
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3] #标注数据
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) #初始化Variable类型节点
for i in range(1000): #1000次
sess.run(train, {x:x_train, y:y_train}) #train是“把损失函数值降到最小”的节点,把这个节点根据训练样本计算1000次。这会影响到loss,进而影响到linear_model,从而调整W和b.
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train}) #经过1000次训练,拿出来看看参数是否已足够好。
print(“W: %s b: %s loss: %s”%(curr_W, curr_b, curr_loss))
输出:W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
在第一小节中,人为设置W=-1,b=1,得到完美结果。现在无需设置,通过学习,模型得到了非常近似的结果。
看TensorBoard的话,基本不知所云。
小结:
1.建立模型;
2.建立损失函数;
3.设法让损失函数最小化,以便让模型本次学习达到最优,达到局部最优;
4.重复第3步N次,这是一个渐近学习的过程,(可能)达到全局最优;
5.观察模型结果。
Tensorflow 2.0来了。又该学习了。
软件安装时设置国内镜像可加快软件下载速度
# 配置清华PyPI镜像(如无法运行,将pip版本升级到>=10.0.0) pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
[global]
timeout = 6000
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn