本文简单分析单变量线性回归问题
使用TensorFlow进行算法设计与训练的核心步骤
- 1.准备数据
- 2.构建模型
- 3.训练模型
- 4.进行预测
其中,准备数据的数据或为需要分析的数据(由现实生活中的数据分析和清洗而得到),或为人工生成的数据集(主要用于算法的验证)。
而单变量线性回归实战就是通过人工生成的随机数集进行分析的。
单变量线性回归
回归问题:回归指的是根据已有的数据特征预测出一个样本对应的预测标签值。
简单来说,根据给定的数据集,且数据集中的每个样本都有其对应的值(标签值),通过给定的数据集进行拟合,找出一条能够最好代表该数据集的线,然后对于任意给定的样本,都能够预测出该样本对应的值(标签)。比如,房价预测问题。
单变量线性回归问题,即该给定的数据集只有一个特征/输入变量 x ,同时它拟合的线为直线。
单变量线性回归实战
实验函数:y = w*x + b(x为特征,y即标签)
本例通过生成人工数据集。随机生成一个近似采样随机分布,使得 w=2.0 b=1,并加入一个噪声,噪声的最大振幅为0.4。(若不加入噪声,否则生成的点都是直线上的点)
0.导入相关的库(前置工作)
import tensorflow as tf # tensorflow 2.0版本
import numpy as np
import matplotlib.pyplot as plt
# 在Jupyter中,使用matplotlib显示图像需要设置为inline模式,否则不会显示图像
%matplotlib inline
1.人工数据集生成(构建满足这个函数的x,y并加入噪声)
采用 numpy 生成等差数列的方法,生成100个点,每个点的取值在-1~1之间,这些点即给定的数据(特征)集。
# 直接采用np生成等差数列的方法,生成100个点,每个点的取值在-1~1之间
x_data = np.linspace(-1,1,100)
np.random.seed(5) # 设置随机数种子(使随机数结果每次都能重现,可去除)
# y = 2x + 1 + 噪声,其中,噪声的维度与x_data一致
y_data = 2*x_data + 1.0 + np.random.randn(*x_data.shape)*0.4
2.构建回归模型
在此模型中,x 是每一个样本点的x轴的值;w,b才是模型的真正参数(通过训练模型,求出最合适的w,b,使得此模型在100个样本点的计算中总损失最小)
# 通过模型执行,将实线前向计算(预测计算)
def model(x,w,b):
return tf.multiply(x,w)+b # return w*x+b
3.创建待优化变量
- tensorflow变量的声明函数是tf.Variable
- tf.Variable的作用是保存和更新参数
- 变量的初始化可以是随机数、常数、或是通过其他变量的初始值计算得到(通常都不会影响模型最后优化的结果)
# 构建模型中的变量w,对应线性函数的斜率
w = tf.Variable(np.random.randn(),tf.float32)
# 构建模型中的变量b。对应线性函数的截距
b = tf.Variable(0.0,tf.float32)
4.定义损失函数
损失是评价一个模型优良的度量标准,表示对於单个样本而言模型预测的准确程度。
- 损失函数用于描述预测值与真实值之间的误差,从而指导模型收敛方向
- 常见损失函数:均方差(MSE)和交叉熵(cross-entropy)
对于回归问题,最常用的损失函数就是均方差(MSE,Mean Squared Error)。均方差是指预测值和实际值之间的平均方差。平均方差越小,说明测试值和实际值之间的差距越小,即模型性能更优。
均方差损失函数:
# 定义均方差损失函数
def loss(x,y,w,b):
err = model(x,w,b)-y # 计算模型预测值和标签值得差异
squared_err = tf.square(err) # 求平方,得出方差
return tf.reduce_mean(squared_err) # 求均值,得出均方差
5.设置训练超参数
在机器学习中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。
通常情况下,需要对超参数进行优化,选择一组好的超参数,可以提高学习的性能和效果。
training_epochs = 10 # 迭代次数(训练轮次)
learning_rate = 0.01 # 学习率
关于学习率:
- 学习率的作用:控制参数更新的幅度
- 如果学习率设置过大,可能导致参数在极值附近来回摇摆,无法保证收敛
- 如果学习率设置过小,虽然能保证收敛,但是优化速度会大大降低,需要更多的迭代次数才能达到较理想的优化效果
6.定义计算梯度函数
# 计算样本数据[x,y]在参数[w,b]点上的梯度(对应优化器)
def grad(x,y,w,b):
# x,y对应特征值和标签值+所有需要优化的参数(变量)
with tf.GradientTape() as tape:
loss_ = loss(x,y,w,b)
return tape.gradient(loss_,[w,b]) # 返回梯度向量
在tensorflow 2中,使用tf.GradientTape()这一上下文管理器封装需要求导的计算步骤,并使用其gradient()方法求导。(tensorflow 1.x 版本是通过定义梯度下降优化器说明需要最小化损失值)
7.执行训练(采用随机梯度下降SGD)
step = 0 # 记录训练步数
loss_list = [] # 用于保存loss值的列表
display_step = 10 # 控制训练过程数据显示的频率,不是超参数
for epoch in range(training_epochs):
for xs,ys in zip(x_data,y_data):
loss_ = loss(xs,ys,w,b) # 计算损失
loss_list.append(loss_) # 保存本次损失计算结果
# 训练更新 w,b
delta_w,delta_b = grad(xs,ys,w,b) # 计算该当前[w,b]点的梯度
change_w = delta_w * learning_rate # 计算变量w需要调整的量
change_b = delta_b * learning_rate # 计算变量b需要调整的量
w.assign_sub(change_w) # 变量w值变更为减去change_w后的值
b.assign_sub(change_b) # 变量b值变更为减去change_b后的值
step = step+1 # 训练步数+1
if step%display_step == 0: # 显示训练过程信息
print("Training Epoch:",'%02d'%(epoch+1),"Step:%03d"%(step),"loss_=","%.6f"%(loss_))
plt.plot(x_data,w.numpy()*x_data+b.numpy()) # 完成一轮训练后,画出回归的线条
输出如图(省略部分损失值的打印输出):
从上图可知,由于本案例所拟合的模型较简单,训练5、6轮之后已经接近收敛(回归线条重合)。
对于复杂模型,需要更多次训练才能收敛。
8.显示训练结果(即w,b的训练结果)
print("w:",w.numpy()) # w的值应该在2附加
print("b:",b.numpy()) # b的值应该在1附加
输出:
9.结果可视化
训练结果可视化:
plt.scatter(x_data,y_data,label='Original data')
plt.plot(x_data,x_data*2.0+1.0,label='Object line',color='g',linewidth=3)
plt.plot(x_data,x_data*w.numpy()+b.numpy(),label='Fitted line',color='r',linewidth=3)
plt.legend(loc=2) # 通过参数loc指定图例的位置
输出结果:
红线(训练函数y=wx+b,其中w,b经训练获得)与绿线(目标函数y=2*x+1)将近重合。
损失可视化:
plt.plot(loss_list)
输出:
可以看到,经过前几轮的训练后,模型渐进于收敛,损失值变化都不大。
10.根据训练结果进行预测
x_test = 3.21 # 测试样本
predict = model(x_test,w.numpy(),b.numpy())
print("预测值:%f"%predict)
target = 2*x_test+1.0
print("目标值:%f"%target)
输出:
本文是通过随机梯度下降(SGD)算法对数据集进行迭代训练,有兴趣的小伙伴可以试试通过批量梯度下降(BGD)算法进行训练。。。
附上批量梯度下降(BGD)算法训练需更改代码:
1.修改超参数
training_epochs = 100 # 迭代次数(训练轮次)
learning_rate = 0.10 # 学习率
训练周期和学习率需做调整。迭代次数设置为100,表示要参与100次训练;学习率设置为0.10,比SGD算法的大些。
2.修改模型训练过程(BGD)
loss_list = [] # 用于保存loss值的列表
for epoch in range(training_epochs):
loss_ = loss(x_data,y_data,w,b) # 计算损失
loss_list.append(loss_) # 保存本次损失计算结果
delta_w,delta_b = grad(x_data,y_data,w,b) # 计算该当前[w,b]点的梯度
change_w = delta_w * learning_rate # 计算变量w需要调整的量
change_b = delta_b * learning_rate # 计算变量b需要调整的量
w.assign_sub(change_w) # 变量w值变更为减去change_w后的值
b.assign_sub(change_b) # 变量b值变更为减去change_b后的值
print("Training Epoch:",'%02d'%(epoch+1),"loss=%.6f"%(loss_))
plt.plot(x_data,w.numpy()*x_data+b.numpy()) # 完成一轮训练后,画出回归的线条
训练100次,将每次的损失值存入 loss_list 列表。
训练结果可视化:
损失值(部分截图):
除了开始几轮训练损失值波动较大外,后续损失值渐近。
回归线:
画出的回归线渐渐重合(颜色较深的部分)。
注:损失值的可视化
plt.plot(loss_list)
输出: