本文以简单的一元线性模型为例,演示梯度下降法的基本实现流程。
一个一元线性模型的基本形式为:
但是与控制理论中所介绍的一样,数据的获取过程可能存在着噪声误差,理想状态下噪声服从某种分布:
所以训练集数据是通过模拟含噪声的线性模型获取的:
def get_data(b = 0.089, w = 1.477, x_range=(-10.,10.), gauss=0.1):
data = []# 保存样本集的列表
for i in range(100): # 循环采样 100 个点
x = np.random.uniform(x_range[0], x_range[1]) # 随机采样输入 x
# 采样高斯噪声
eps = np.random.normal(0., gauss)
# 得到模型的输出
y = w * x + b + eps
data.append([x, y]) # 保存样本点
data = np.array(data) # 转换为 2D Numpy 数组
return data
对于一个模型的训练,需要一个评价标准判断模型的好坏,这里选择均方误差公式:
均方误差的获取代码实现如下:
def mse(b, w, points):
# 根据当前的 w,b 参数计算均方差损失
totalError = 0
for i in range(0, len(points)): # 循环迭代所有点
x = points[i, 0] # 获得 i 号点的输入 x
y = points[i, 1] # 获得 i 号点的输出 y
# 计算差的平方,并累加
totalError += (y - (w * x + b)) ** 2
# 将累加的误差求平均,得到均方差
return totalError / float(len(points))
梯度下降法的核心就是沿着误差梯度下降的方向进行查找,当步长很小,只要迭代次数够多便可以找到最小点,迭代公式如下:
其中 y 是优化的目标函数,x 是目标函数的参数。所以现在需要求出梯度,并设定步长后对其修改,由于线性模型含有两个参数 w 和 b。那么在使用平方误差进行评价模型时,优化目标便是平方误差也就是说,两个参数的迭代优化公式为:
对于线性模型来说,该公式可以进一步改写为:
那么现在便可以根据本代梯度对其进行更新:
def step_gradient(b_current, w_current, points, lr):
# 计算误差函数在所有点上的导数,并更新 w,b
b_gradient = 0
w_gradient = 0
M = float(len(points)) # 总样本数
for i in range(0, len(points)):
x = points[i, 0]
y = points[i, 1]
# 误差函数对 b 的导数:grad_b = 2(wx+b-y),参考公式(2.3)
b_gradient += (2/M) * ((w_current * x + b_current) - y)
# 误差函数对 w 的导数:grad_w = 2(wx+b-y)*x,参考公式(2.2)
w_gradient += (2/M) * x * ((w_current * x + b_current) - y)
# 根据梯度下降算法更新 w',b',其中 lr 为学习率
new_b = b_current - (lr * b_gradient)
new_w = w_current - (lr * w_gradient)
return [new_b, new_w]
使用梯度下降法对系数 w 和 b 进行 num_iterations
代更新:
def gradient_descent(points, starting_b, starting_w, lr, num_iterations):
# 循环更新 w,b 多次
b = starting_b # b 的初始值
w = starting_w # w 的初始值
# 根据梯度下降算法更新多次
for step in range(num_iterations):
# 计算梯度并更新一次
b, w = step_gradient(b, w, np.array(points), lr)
loss = mse(b, w, points) # 计算当前的均方差,用于监控训练进度
if step%50 == 0: # 打印误差和实时的 w,b 值
print(f"iteration:{step}, loss:{loss}, w:{w}, b:{b}")
return [b, w],loss # 返回最后一次的 w,b
最终的线性模型训练主程序为:
def main():
lr = 0.01 # 学习率
original_b = 0.089 # 生成数据的初始 b
original_w = 1.477 # 生成数据的初始 w
initial_b = 0 # 初始化 b 为 0
initial_w = 0 # 初始化 w 为 0
num_iterations = 1000 # 训练次数为 100
data = get_data(original_b,original_w) # 获取二维数据
[b, w], loss = gradient_descent(data, initial_b, initial_w, lr, num_iterations) # 返回最优 w*,b*,loss*和训练 Loss 的下降过程
print(f'Final loss:{loss}, w:{w}, b:{b}')