知识补充
举例函数\(Z=f(X,Y)\):
- 偏导数
- 将X固定,Z的增量除以Y的增量,我们称之为Z对Y的偏导数
- 同理,我们保持Y值不变,Z值仅随X值改变,Z的增量除以X的增量,我们称之为Z对X的偏导数
- 梯度
- 每个点都有一个箭头来表示Z对X的偏导数,每个点都有一个箭头来表示Z对Y的偏导数
- 我们将这两个箭头向量相加,就得到一个新向量,称之为Z的梯度
- Z的梯度向量总是指向Z函数增长最大的地方
- 可以得出梯度是Z=f(X,Y)某一点的含有方向的导数,这个方向导数乘以该点的单位向量,就得到一个准确数值,这个数值就是该点在这个方向上的变换率。
示例说明
考虑一个实际问题,某城市在 2013 年 - 2017 年的房价如下表所示:
年份 2013 - 2017
房价 12000 14000 15000 16500 17500
现在,我们希望通过对该数据进行线性回归,即使用线性模型 y = ax + b 来拟合上述数据,此处 a 和 b 是待求的参数。
首先,我们定义数据,进行基本的归一化操作。
首先对数据进行归一化
import numpy as np
X_raw = np.array([2013, 2014, 2015, 2016, 2017], dtype=np.float32)
y_raw = np.array([12000, 14000, 15000, 16500, 17500], dtype=np.float32)
from sklearn import preprocessing
x_scaler = preprocessing.MinMaxScaler().fit(X_raw.reshape(-1, 1))
y_scaler = preprocessing.MinMaxScaler().fit(y_raw.reshape(-1, 1))
X = x_scaler.transform(X_raw.reshape(1, -1))
y = y_scaler.transform(y_raw.reshape(1, -1))
print(X)
print(y)
print('*'*20, '反向')
print(x_scaler.inverse_transform(X))
print(y_scaler.inverse_transform(y))
# print(preprocessing.minmax_scale.inverse_transform(y))
print(X)
print(y)
[[0. 0.25 0.5 0.75 1. ]]
[[0. 0.3636365 0.54545474 0.818182 1. ]]
******************** 反向
[[2013. 2014. 2015. 2016. 2017.]]
[[11999.999 14000. 15000.001 16500. 17500. ]]
[[0. 0.25 0.5 0.75 1. ]]
[[0. 0.3636365 0.54545474 0.818182 1. ]]
接下来,我们使用梯度下降方法来求线性模型y=a*x+b
中两个参数 a 和 b 的值;
回顾机器学习的基础知识,对于多元函数 f(x) 求局部极小值,梯度下降 的过程如下:
-
初始化自变量为 \(x_0\) , k=0
-
迭代进行下列步骤直到满足收敛条件:
-
求函数 f(x) 关于自变量的梯度 \(\nabla f(x_k)\)
-
更新自变量: \(x_{k+1} = x_{k} - \gamma \nabla f(x_k)\) 。这里 \(\gamma\) 是学习率(也就是梯度下降一次迈出的 “步子” 大小)
-
\(k \leftarrow k+1\) (迭代下一轮)
-
接下来,我们考虑如何使用程序来实现梯度下降方法,求得线性回归的解 \(min_{a, b} L(a, b) = \sum_{i=1}^n(ax_i + b - y_i)^2\) 。
NumPy 下的线性回归
此处的损失函数为均方误差 \(L(x) = \sum_{i=1}^N (ax_i + b - y_i)^2\)。其关于参数 a 和 b 的偏导数为 \(\frac{\partial L}{\partial a} = 2 \sum_{i=1}^N (ax_i + b - y) x_i\),$\frac{\partial L}{\partial b} = 2 \sum_{i=1}^N (ax_i + b - y) $。本例中 $N = 5 $。
由于均方误差取均值的系数$ \frac{1}{N}$ 在训练过程中一般为常数( N 一般为批次大小),对损失函数乘以常数等价于调整学习率,因此在具体实现时通常不写在损失函数中。
NumPy手动计算损失函数
a,b = 0,0
num_epoch = 10000
learning_rate = 5e-4
for e in range(num_epoch):
# 手动计算损失函数关于自变量(模型参数)的梯度
y_pred = a* X[0] + b
grad_a, grad_b = 2* (y_pred - y[0]).dot(X[0]), 2*(y_pred - y[0]).sum()
# 更新参数
a, b = a - learning_rate * grad_a, b - learning_rate * grad_b
print(a, b)
Numpy 反归一化,还原数据
# 房价预测对比
print(X_raw)
print(y_raw)
res = X * a + b
print(res)
print(y_scaler.inverse_transform(res.reshape(-1, 1) ))
print(y_scaler.inverse_transform(res.reshape(-1, 1) ).reshape(1, -1))
[2013. 2014. 2015. 2016. 2017.]
[12000. 14000. 15000. 16500. 17500.]
[[0.05756507 0.30165762 0.5457502 0.7898427 1.0339353 ]]
[[12316.606]
[13659.116]
[15001.625]
[16344.134]
[17686.645]]
[[12316.606 13659.116 15001.625 16344.134 17686.645]]
TensorFlow 下的线性回归
使用 TensorFlow 计算线性回归, 程序的结构和前述 NumPy 的实现非常类似:
这里,TensorFlow 帮助我们做了两件重要的工作:
- 使用 tape.gradient(ys, xs) 自动计算梯度;
- 使用tf.keras.optimizers.SGD 自动进行优化计算
- 使用 optimizer.apply_gradients(grads_and_vars) 自动更新模型参数。
import tensorflow as tf
# python --version
print(tf.__version__)
2.1.0
tf_X = tf.constant(X)
tf_y = tf.constant(y)
tf_a, tf_b = tf.Variable(initial_value=0.), tf.Variable(initial_value=0.)
variables = [tf_a, tf_b]
num_epoch = 10000
optimizer = tf.keras.optimizers.SGD(learning_rate=5e-4)
for e in range(num_epoch):
# 使用tf.GradientTape()记录损失函数的梯度信息
with tf.GradientTape() as tape:
tf_y_pred = tf_a * tf_X + tf_b
loss = tf.reduce_sum(tf.square(tf_y_pred - tf_y))
# TensorFlow自动计算损失函数关于自变量(模型参数)的梯度
grads = tape.gradient(loss, variables)
# TensorFlow自动根据梯度更新参数
optimizer.apply_gradients(grads_and_vars=zip(grads, variables))
print('variables', variables)
variables [<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.97637004>, <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.057565123>]
TensorFlow 反归一化,还原数据
# 房价预测对比
print(X_raw)
print(y_raw)
tf_res = np.array(X * tf_a + tf_b)
print(tf_res)
print(y_scaler.inverse_transform(tf_res.reshape(-1, 1) ))
print(y_scaler.inverse_transform(tf_res.reshape(-1, 1) ).reshape(1, -1))
[2013. 2014. 2015. 2016. 2017.]
[12000. 14000. 15000. 16500. 17500.]
[[0.05756512 0.30165762 0.54575014 0.7898426 1.0339352 ]]
[[12316.608]
[13659.116]
[15001.625]
[16344.134]
[17686.643]]
[[12316.608 13659.116 15001.625 16344.134 17686.643]]