前言
在TensorFlow 1.x中实现线性回归通常指的是使用静态图的方式,而在TensorFlow 1.x中使用Eager API实现线性回归是在TensorFlow 1.x的晚期版本中引入的,以提供类似于TensorFlow 2.x的编程体验。以下是两种方式的区别、各自的优点以及对比的作用:
TensorFlow 1.x 静态图方式
实现方式:
- 首先构建一个计算图,包括数据的输入、模型的定义、损失函数和优化器。
- 使用
tf.placeholder
来定义输入数据占位符。 - 通过
tf.Session()
来启动会话,并在会话中执行图的操作。
优点:
- 图优化: 静态图允许在执行前对整个计算图进行优化,可能提高执行效率。
- 多GPU支持: 更好地支持多GPU环境,适合大规模训练。
TensorFlow 1.x Eager API
实现方式:
- TensorFlow 1.x在其后期版本中通过
tf.contrib.eager
模块引入了Eager API。 - 允许立即执行操作,无需构建静态图,操作的返回值可以直接用于进一步的计算。
优点:
- 即时反馈: 操作执行后立即返回结果,便于调试和实验。
- 简化的API: 减少了编写和理解代码的复杂性。
对比作用
- 编程模型: 静态图需要事先定义所有的操作,而Eager API允许逐步执行操作。
- 易用性: Eager API通常更易于学习和使用,特别是对于习惯了Python即时执行特性的程序员。
- 灵活性: Eager API提供了更高的灵活性,可以动态地修改和执行操作。
- 性能: 静态图可能在性能上更有优势,尤其是在需要执行大量预定义计算的情况下。
实际应用
- 研究和开发: Eager API适合快速迭代和实验,因为它可以即时看到操作结果。
- 生产环境: 静态图可能更适合生产环境,特别是当模型训练和推断需要高性能和稳定性时。
总结
尽管TensorFlow 1.x的Eager API为1.x版本带来了更现代的编程体验,但它实际上是TensorFlow 2.x中Eager执行的预演。TensorFlow 2.x默认启用Eager执行,提供了更简洁和Pythonic的API,同时保持了向后兼容性。因此,对于新项目,推荐使用TensorFlow 2.x,它结合了1.x的静态图性能和Eager API的易用性。
2.1线性回归
所需的库:
tensorflow 1.12.0
numpy 1.19.5
matplotlib 3.3.4
代码:
# 线性回归
import tensorflow as tf
import numpy
import matplotlib.pyplot as plt
rng = numpy.random
# 参数
learning_rate = 0.01
training_epochs = 1000
display_step = 200
# 训练数据
train_X = numpy.asarray([3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59, 2.167,
7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1])
train_Y = numpy.asarray([1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53, 1.221,
2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3])
n_samples = train_X.shape[0]
# tf 图输入
X = tf.placeholder('float')
Y = tf.placeholder('float')
W = tf.Variable(rng.randn(), name='weight')
b = tf.Variable(rng.randn(), name='bias')
# 创建一个线性模型
pre = tf.add(tf.multiply(X, W), b)
# 均方误差损失
cost = tf.reduce_sum(tf.pow(pre - Y, 2))/(2 * n_samples)
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
# 初始化变量(分配默认值)
init = tf.global_variables_initializer()
# 开始训练
with tf.Session() as sess: # 创建并进入一个TensorFlow会话的上下文管理器
sess.run(init) # 运行初始化器init
for epoch in range(training_epochs):
for (x, y) in zip(train_X, train_Y): # 遍历训练数据集,每次迭代提供一对特征x和标签y
sess.run(optimizer, feed_dict={X: x, Y: y}) # 运行优化器操作,如梯度下降步骤,来更新模型的参数。
# 记录损失
if (epoch + 1) % display_step == 0:
c = sess.run(cost, feed_dict={X: train_X, Y: train_Y})
print(f"epoch{epoch + 1} cost= {c}, W = {sess.run(W)}, b = {sess.run(b)}")
print("optimization finished!")
training_cost = sess.run(cost, feed_dict={X: train_X, Y: train_Y})
print(f"training cost={training_cost}, W = {sess.run(W)}, b = {sess.run(b)}")
# 画图
plt.plot(train_X, train_Y, 'ro', label='Original data')
plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
plt.legend()
plt.show()
输出:
epoch200 cost= 0.08749574422836304, W = 0.30706509947776794, b = 0.3880200982093811
epoch400 cost= 0.08340831100940704, W = 0.29456469416618347, b = 0.47794750332832336
epoch600 cost= 0.08090882748365402, W = 0.28478333353996277, b = 0.5483137965202332
epoch800 cost= 0.07938076555728912, W = 0.27712881565093994, b = 0.6033796668052673
epoch1000 cost= 0.07844720780849457, W = 0.27114012837409973, b = 0.6464620232582092
optimization finished!
training cost=0.07844720780849457, W = 0.27114012837409973, b = 0.6464620232582092
2.2 使用 TensorFlow 的 Eager API 实现线性回归
代码:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 设置Eager API
tf.enable_eager_execution()
tfe = tf.contrib.eager
# 训练数据
train_X = [3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59, 2.167,
7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1]
train_Y = [1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53, 1.221,
2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3]
n_samples = len(train_X)
# 参数设置
learning_rate = 0.01
display_step = 200
num_steps = 1000
# 权重与偏置
W = tfe.Variable(np.random.randn())
b = tfe.Variable(np.random.randn())
# 线性回归
def linear_regression(inputs):
return inputs * W + b
# 均方误差
def mean_square_fn(model_fn, inputs, labels):
return tf.reduce_sum(tf.pow(model_fn(inputs) - labels, 2)) / (2 * n_samples)
# 梯度下降优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
# 计算梯度
grad = tfe.implicit_gradients(mean_square_fn)
# 初始损失
print(f"初始损失:{mean_square_fn(linear_regression, train_X, train_Y)}, W = {W.numpy()}, b = {b.numpy()}.")
# 开始训练
for step in range(num_steps):
optimizer.apply_gradients(grad(linear_regression, train_X, train_Y))
if (step + 1) % display_step == 0 or step == 0:
print(f"Epoch:{step + 1}, 损失:{mean_square_fn(linear_regression, train_X, train_Y)}, W = {W.numpy()}, b = {b.numpy()}.")
plt.plot(train_X, train_Y, 'ro', label='初始数据')
plt.plot(train_X, np.array(W * train_X + b), label='拟合曲线')
plt.legend()
plt.show()
输出结果:
初始损失:25.554563522338867, W = 1.414129614830017, b = 0.1566573679447174.
Epoch:1, 损失:7.765857696533203, W = 0.9393506050109863, b = 0.09066701680421829.
Epoch:200, 损失:0.10071783512830734, W = 0.33907845616340637, b = 0.17886608839035034.
Epoch:400, 损失:0.09156356751918793, W = 0.32022035121917725, b = 0.3125614523887634.
Epoch:600, 损失:0.08593194931745529, W = 0.3054291903972626, b = 0.41742414236068726.
Epoch:800, 损失:0.0824674665927887, W = 0.2938278913497925, b = 0.4996721148490906.
Epoch:1000, 损失:0.08033612370491028, W = 0.2847285568714142, b = 0.5641824007034302.
小tips
权重(W)和偏置(b)的初始化已经在创建这两个变量时完成了。这是通过使用tf.Variable来实现的,并且使用numpy.random.randn()生成了随机数作为它们的初始值。:
W = tf.Variable(rng.randn(), name='weight')
b = tf.Variable(rng.randn(), name='bias')
如果想给权重和偏置赋予初始值,可以这样:
W = tf.Variable(0.4, name='weight')
b = tf.Variable(0.7, name='bias')
结果对比:
随机数赋初值:
training cost=0.07943815737962723, W = 0.22219787538051605, b = 0.998549222946167
手动赋初值:
training cost=0.07706085592508316, W = 0.25450703501701355, b = 0.7661195993423462
赋初值的优点:
- 能够精确控制模型参数的起始点,有助于调试和验证模型的行为;
- 从特定的值开始训练可以提高模型的稳定性和收敛速度;
- 使用固定的初始值时,实验结果是可重复的,这对于科学研究和调试至关重要。
- 适当的初始化可以减少过拟合的风险
- 在超参数调优过程中,固定的初始化策略可以确保不同设置之间的比较是公平的。