使用 TensorFlow 自动微分和神经网络功能估算线性回归的参数(Estimate parameters for linear regression using automatic differentiation or neural network functions of TensorFlow)

发布时间 2023-05-25 09:04:08作者: ttweixiao9999

大多数的深度学习框架至少都会具备以下功能:

(1)张量运算 (2)自动微分 (3)神经网络及各种神经层

TensorFlow 框架亦是如此。在《深度学习全书 公式+推导+代码+TensorFlow全程案例》—— 洪锦魁主编 清华大学出版社 ISBN 978-7-302-61030-4 这本书第3章 《TensorFlow架构与主要功能》这一章节中,介绍了使用 TensorFlow 架构的自动微分功能和神经网络功能估算简单线性回归参数的方法,分享给大家~

1. 使用 TensorFlow 自动微分求解简单线性回归的参数(w, b)

 1 # 载入库
 2 import numpy as np
 3 import tensorflow as tf
 4 import matplotlib.pyplot as plt
 5 plt.ion()
 6 
 7 
 8 # 定义损失函数为均方误差MSE=(sum(y-y0)^2)/n
 9 def loss(y, y_pred):
10     # tf.reduce_mean 函数用于计算张量tensor沿着指定的数轴上的的平均值
11     return tf.reduce_mean(tf.square(y - y_pred))
12 
13 
14 # 定义预测值函数,即模型函数 y = wx + b
15 def predict(X):
16     return w * X + b
17 
18 
19 # 定义训练函数,在自动微分中需重新计算损失函数值,再利用梯度下降法求解 w 和 b
20 def train(X, y, epochs=40, lr=0.0001):
21     current_loss = 0                              # 损失函数值
22     for epoch in range(epochs):                   # 执行训练周期
23         with tf.GradientTape() as t:              # 自动微分
24             # 当声明为变量(tf.Variable)时,该变量会自动参与自动微分,但声明为常熟(tf.constant)时,
25             # 如欲参与自动微分,则需额外设定 g.watch()
26             t.watch(tf.constant(X))               # 创建 TensorFlow 常数参与自动微分
27             current_loss = loss(y, predict(X))    # 计算损失函数值
28 
29         dw, db = t.gradient(current_loss, [w, b])  # 取得 w, b 的梯度
30 
31         # 更新权重:新权重 = 原权重 — 学习率(learning_rate) * 梯度(gradient)
32         # assign_sub 函数相当于 -=
33         w.assign_sub(lr * dw)  # w -= lr * dw
34         b.assign_sub(lr * db)  # b -= lr * db
35 
36         # 显示每一训练周期的损失函数
37         print(f'Epoch {epoch}: Loss: {current_loss.numpy()}')
38 
39 
40 # 产生线性随机数据100批,这些数据介于0-50数值之间
41 n = 100
42 X = np.linspace(0, 50, n)
43 y = np.linspace(0, 50, n)
44 
45 # 给数据添加一些噪声 (noise),np.random.uniform是从一个均匀分布的区域中进行随机采样
46 X += np.random.uniform(-10, 10, n)
47 y += np.random.uniform(-10, 10, n)
48 
49 # w、b 初始值均设为 0
50 w = tf.Variable(0.0)
51 b = tf.Variable(0.0)
52 
53 # 执行训练
54 train(X, y)
55 
56 # 打印经过训练后 w、b 的最佳解
57 print(f'w={w.numpy()}, b={b.numpy()}')
58 # 显示预测结果
59 plt.scatter(X, y, label='data')
60 plt.plot(X, predict(X), 'r-', label='predicted')
61 plt.legend()

代码运行结果:

由上图可知,回归线确实居于样本点中线,拟合效果甚佳。

2. 使用 TensorFlow 完全连接层估算简单线性回归的参数(w, b)

 1 # 载入库
 2 import numpy as np
 3 import tensorflow as tf
 4 import matplotlib.pyplot as plt
 5 plt.ion()
 6 
 7 # 产生线性随机数据100批,这些数据介于0-50数值之间
 8 n = 100
 9 X = np.linspace(0, 50, n)
10 y = np.linspace(0, 50, n)
11 
12 # 给数据添加一些噪声 (noise),np.random.uniform是从一个均匀分布的区域中进行随机采样
13 X += np.random.uniform(-10, 10, n)
14 y += np.random.uniform(-10, 10, n)
15 
16 # 建立模型:这里仅仅使用一个完全连接层,并且输入只有一个神经元X,输出也只有一个神经元y。
17 # Dense本身有一个参数use_bias,即是否有偏差项,默认为true,即除了一个神经元输出外,还会有一个偏差项,
18 # 这样的设定等价于 y=wx+b
19 # 定义完全连接层(Dense): units:输出神经元个数,input_shape:输入神经元个数
20 layer1 = tf.keras.layers.Dense(units=1, input_shape=[1])
21 # 神经网路包含一层完全连接层
22 model = tf.keras.Sequential([layer1])
23 
24 # 定义模型的损失函数(loss)为 MSE,优化器(optimizer)为 Adam
25 model.compile(loss='mean_squared_error',
26               optimizer=tf.keras.optimizers.Adam())
27 # 训练模型,训练过程的损失函数变化都会存在 history 中
28 history = model.fit(X, y, epochs=1000, verbose=False)
29 
30 # 训练过程绘图,绘制损失函数值,损失函数值会随着训练周期越来越小
31 plt.figure()
32 plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
33 plt.rcParams['axes.unicode_minus'] = False
34 plt.xlabel('训练周期', fontsize=20)
35 plt.ylabel("损失函数(loss)", fontsize=20)
36 plt.plot(history.history['loss'])
37 
38 # 获取模型参数
39 w = layer1.get_weights()[0][0][0]
40 b = layer1.get_weights()[1][0]
41 print(f"w:{w:.4f} , b:{b:.4f}")
42 
43 # 利用 w 和 b 进行绘图显示回归线
44 plt.figure()
45 plt.scatter(X, y, label='data')
46 plt.plot(X, X * w + b, 'r-', label='predicted')
47 plt.legend()

代码运行结果:

 

由以上图可知,回归线拟合效果甚佳。与自动微分相比,这种方法程序更简单,只要设定好模型结构、损失函数、优化器后,进行拟合训练即可。