做了一个线性回归的小的demo。训练样本sample_size = 100,每次参与训练的数据个数 batch_size = 50,采用L2 loss,也就是测试结果和真实结果之间的距离。利用梯度下降法训练模型参数。
这里求梯度的时候,利用了链式法则。
具体的代码如下:
import numpy as np
import random
from matplotlib import pyplot as plt
# 利用训练的模型计算预测值
def inference(x, w, b):
pred_y = w * x + b
return pred_y
# 求梯度
def gradient(pred_y, gt_y, x):
diff = pred_y - gt_y
dw = diff * x
db = diff
return dw, db
# 梯度下降法求梯度的过程,调用一次该函数,则进行一次梯度下降,也就是更新一次模型参数
def cal_step_gradient(batch_x_list, batch_gt_y_list, w, b, lr):
# 因为这里每次是选一批的样本数据,所以需要对这批数据的 loss 求一个平均值,利用这个平均值来训练模型。
batch_size = len(batch_x_list)
avg_dw = 0
avg_db = 0
for i in range(batch_size):
pred_y = inference(batch_x_list[i], w, b)
batch_w, batch_b = gradient(pred_y, batch_gt_y_list[i], batch_x_list[i])
avg_dw += batch_w
avg_db += batch_b
avg_dw /= batch_size
avg_db /= batch_size
w -= avg_dw * lr
b -= avg_db * lr
return w, b
# 模型质量评价函数,利用全量的真实值和预测值计算 L2 loss, loss越小模型质量越好
def eval_loss(x_list, gt_y_list, w, b):
avg_loss = 0.0
for i in range(len(x_list)):
avg_loss = 0.5 * (x_list[i] * w + b - gt_y_list[i]) ** 2
avg_loss /= len(gt_y_list)
return avg_loss
# 画图函数
def draw(x_list, w, b):
batch_size = len(x_list)
pre_y_list = []
for i in range(batch_size):
pre_y_list.append(w * x_list[i] + b)
plt.scatter(x_list, pre_y_list)
# 模型训练过程
def train(x_list, gt_y_list, batch_size, max_iter, lr):
# 初始化参数
w = 0
b = 0
# 在样本数据中随机选择 batch_size 大小的数据,用来训练
batch_idx = np.random.choice(len(x_list), batch_size)
batch_x_list = []
batch_gt_y_list = []
for i in range(max_iter):
batch_x_list = [x_list[j] for j in batch_idx]
batch_gt_y_list = [gt_y_list[j] for j in batch_idx]
w, b = cal_step_gradient(batch_x_list, batch_gt_y_list, w, b ,lr)
print('w:{0}, b:{1}'.format(w, b))
print('loss is {0}'.format(eval_loss(x_list, gt_y_list, w, b)))
# 画出训练好的模型的样本数据分布
draw(x_list, w, b)
# 数据生成函数,利用random函数随机生成一批样本数据
def gen_sample_data():
w = random.randint(0, 5) + random.random()
b = random.randint(0, 10) + random.random()
sample_size = 100
x_list = []
y_list = []
for i in range(sample_size):
x = random.randint(0, 100) + random.random()
y = w * x + b + random.random() * np.random.randint(-1, 1)
x_list.append(x)
y_list.append(y)
# 画出样本的原始分布
plt.scatter(x_list, y_list)
return x_list, y_list, w, b
# 初始化模型参数,包括batch_size, 最大训练次数, 学习率 learn_rate
def run():
x_list, y_list, w, b = gen_sample_data()
batch_size = 50
max_iter = 100
lr = 0.0001
train(x_list, y_list, batch_size, max_iter, lr)
# 主函数
if __name__ == '__main__':
run()
训练效果如下图所示,蓝色的点是原始数据分布,黄色的点是训练的模型预测的数据值,可以看到,它们几乎完全重叠在一起了,可以说效果还是不错的。
不过这里需要注意learn_rate 的选择,如果这个值过大,比如可以尝试一下0.01,那就会发生梯度爆炸,最后训练出来的模型可能和原本的数据分布就完全是两码事,至于为什么会出现梯度爆炸,下次再写。