摘要:
- 介绍 tensorflow 入门
- 使用 tensorflow 实现简单的线性回归
tensorflow 入门知识
参考: https://www.tensorflow.org/get_started/get_started
载入 tensorflow 的标准语句:
import tensorflow as tf
tensor
tensor 物理上的翻译是 张量。 tensor 是 tensorflow 的基本核心数据单元,tensor 可以理解为由数据组成的多维数组,维度可以是 1 维,2 维, 3 维等等。如下所示:
3 # a rank 0 tensor; this is a scalar with shape []
[1., 2., 3.] # a rank 1 tensor; this is a vector with shape [3]
[[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]
tensorflow 表面意思应该就是 tensor 的流动,实际的计算中,貌似确实蕴含了这种意思。就像 tensor 就像是水流一般,按照人们规定的路线进行流动,在规定的路线上,有很多关卡,用于对 tensor 进行相应的处理,得到最终的答案。
node
tensorflow 程序可以分为两部分:
- 构建计算流图
- 运行计算流图
一个计算流图(computaional graph)由很多节点(node)组成。
图 1
图 1 所示的计算流图用于进行线性拟合。 其中包含好几种 node
常数节点 constant node
如下所示,定义了两种常数节点:
node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # tf.float32 是默认的类型
print(node1, node2)
输出结果为:
Tensor("Const_13:0", shape=(), dtype=float32) Tensor("Const_14:0", shape=(), dtype=float32)
可以看到,两者类型是 Const,输出值为 0。并不是 3.0 和 4.0。 为什么是这样,可以理解为,这两个节点,还没被激活,所以其输出结果默认为 0。
定义了两个结点,相当于我们构建了一个计算流图,如何运行这个计算流图?需要我们在一个 Session 对象中去运行。
sess = tf.Session()
sess.run([node1, node2])
[3.0, 4.0]
上面的构建的流图单纯的定义了两个节点,现在再定义一个 node3 节点,做为两个常数节点的和:
node3 = tf.add(node1, node2)
print('node3: ', node3)
print('sess.run(node3): ', sess.run(node3))
node3: Tensor("add_2:0", shape=(), dtype=float32)
sess.run(node3): 7.0
上面 node1-3 构成的数据流图可以表示如下:
图 2
可输入节点 placeholder
上面建立的数据流图仅仅只是将固定节点的数据相加,当我们想要变换数据的时候,则需要重新创建数据流图,显然太麻烦。 placeholder 节点可以接收外界输入。
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
print(a, b)
其输出为:
Tensor("Placeholder_6:0", dtype=float32) Tensor("Placeholder_7:0", dtype=float32)
placeholder() 初始化的时候,一定要填写数据类型,否则会报错。
adder_node = a + b # a + b 就相当于 tf.add(a, b)
add_and_triple = add_node * 3
相当于,我们建立一个 (a+b)*3
的这样的一个式子。
print(sess.run(add_and_triple,{a:3,b:4.5}))
print(sess.run(add_and_triple,{a:[3,2,4],b:[4.5,1,3.]}))
最终程序的输出结果为:
22.5
[ 22.5 9. 21. ]
可变节点 Variable
可变节点,顾名思义,其值可以变化。
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
其中 W
和 b
属于可变节点。 常数节点在调用 tf.constant
的时候已经被初始化,但是可变节点不一样, 在调用 tf.Variable
的时候,并没有被初始化,为了对所有的可变节点进行初始化,需要调用 tf.global_variables_initializer
,如下所示:
init = tf.global_variables_initializer()
sess.run(init)
这样一来,才能使用初始值去初始化所有的 Variable 变量。
print(sess.run(linear_model,{x:[1,2,3,4]}))
输出的结果为:
[ 0. 0.30000001 0.60000002 0.90000004]
作为可变节点,其参数是可以变化的,如何改变可变节点的参数? 使用 tf.assign
:
print('previous:', sess.run([W, b]))
fixW = tf.assign(W,[1.])
fixb = tf.assign(b,[-1.])
sess.run([fixW, fixb])
print('late:', sess.run([W, b]))
previous: [array([ 0.30000001], dtype=float32), array([-0.30000001], dtype=float32)]
late: [array([ 1.], dtype=float32), array([-1.], dtype=float32)]
使用 tensorflow 进行线性拟合
想想最简单的线性拟合问题:
图 3
这个问题,可以使用 sklearn 中的 linear_model,使用两三行的代码即可解决。在此处,我们需要使用前面介绍的几种 node 类型,结合梯度下降算法,来实现自动学习过程。
首先当然需要构造损失函数(loss functon),损失函数一般定义为,所有的输出值与理想值之差的平方和的平方根。
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
其中,y
代表了理想值。 tf.square()
将输入的 tensor 每一个元素进行平方。 tf.reduce_sum()
即对输入的 tensor 进行求和。
为了实现,学习的过程,我们需要改变参数 W
和 b
使得损失函数最小。在此,我们使用的是梯度下降算法。此算法对于的函数,包含在 tf.train 中。
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
然后开始训练:
sess.run(init) # 将所有的变量使用初始值初始化。 因为之前改过 W b 的值,运行这行程序后,其值回复原来的值
for i in range(1000):
sess.run(train,{x:[1,2,3,4],y:[0,-1,-2,-3]})
print(sess.run([W,b]))
完整的程序(复制在参考网址):
import tensorflow as tf
# Model parameters
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1, 2, 3, 4]
y_train = [0, -1, -2, -3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x: x_train, y: y_train})
# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
此程序的运行结果是:
W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
现在,我们使用这个程序,来训练我们在图 3 中黑点所示的数据,修改程序如下:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# Model parameters
W = tf.Variable([0.], dtype=tf.float32)
b = tf.Variable([0.], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = np.array([ 0.07786339,-0.03961813, 0.01103904,-0.04069594,
-0.03422907,0.00564998,0.08864151,-0.03315126,-0.05686312,
-0.03099563,0.05522933,-0.06009656,0.00133873,-0.02345095,
-0.07410811,0.01966154,-0.01590626,-0.01590626,0.03906215,
-0.0730303 ])
y_train = np.array([ 233., 91., 111., 152., 120., 67., 310., 94., 183.,
66., 173., 72., 49., 64., 48., 178., 104., 132.,
220., 57.])
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x: x_train, y: y_train})
# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
#-------------plot the result--------------------
plt.scatter(x_train,y_train)
y = curr_W[0] * x_train + curr_b[0]
plt.plot(x_train, y, color='red')
plt.show()
得到结果如下:
================== RESTART: C:/Users/wangs0622/Desktop/2.py ==================
W: [ 639.07635498] b: [ 132.56671143] loss: 53342.9
图 4
直觉告诉我们, 图 3 应该比图 4 更加贴合一点。可以看出来,单纯的以输出值与理想值的差的平方和为损失函数,貌似并不是最好的选择。