机器学习——线性回归
线性回归
输入x:0.5 0.6 0.8 1.1 1.4
输出y:5.0 5.5 6.0 6.8 7.0
$w_0$和$w_1$是模型参数预测函数:$y=w_0+w_1 x$
所谓模型训练,就是根据已知的x和y,找到最佳的模型参数$w_0$和$w_1$,尽可能精确地描述出输入和输出的关系。
5.0 = w0 + w1 × 0.5
5.5 = w0 + w1 × 0.6
单样本误差:
根据模型参数$w_1和w_0$,预测函数求出输入为x时的y值:$y' = w_0 + w_1x$,单样本误差为$\frac{1}{2}(y' - y)^2$。
总样本误差:
把所有单样本误差相加即是总样本误差:$\frac{1}{2}\sum (y' - y)^2$
损失函数:
$$loss = \frac{1}{2} \sum(w_0 + w_1x - y)^2$$
所以损失函数就是总样本误差关于模型参数的函数,该函数就是数学模型,模型的训练即需要找到一组w0 w1使得loss取极小值。
案例:画图模拟梯度下降的过程
1、整理训练集数据,自定义梯度下降算法规则,求出w0 , w1 ,绘制回归线。
import numpy as np import matplotlib.pyplot as plt train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4]) train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0]) test_x = np.array([0.45, 0.55, 1.0, 1.3, 1.5]) test_y = np.array([4.8, 5.3, 6.4, 6.9, 7.3]) times = 1000 # 定义梯度下降次数 lrate = 0.01 # 记录每次梯度下降参数变化率 epoches = [] # 记录每次梯度下降的索引 w0, w1, losses = [1], [1], [] for i in range(1, times + 1): epoches.append(i) loss = (((w0[-1] + w1[-1] * train_x) - train_y) ** 2).sum() / 2 losses.append(loss) d0 = ((w0[-1] + w1[-1] * train_x) - train_y).sum() d1 = (((w0[-1] + w1[-1] * train_x) - train_y) * train_x).sum() print('{:4}> w0={:.8f}, w1={:.8f}, loss={:.8f}'.format(epoches[-1], w0[-1], w1[-1], losses[-1])) w0.append(w0[-1] - lrate * d0) w1.append(w1[-1] - lrate * d1) pred_test_y = w0[-1] + w1[-1] * test_x plt.figure('Linear Regression', facecolor='lightgray') plt.title('Linear Regression', fontsize=20) plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.scatter(train_x, train_y, marker='s', c='dodgerblue', alpha=0.5, s=80, label='Training') plt.scatter(test_x, test_y, marker='D', c='orangered', alpha=0.5, s=60, label='Testing') plt.scatter(test_x, pred_test_y, c='orangered', alpha=0.5, s=80, label='Predicted') plt.plot(test_x, pred_test_y, '--', c='limegreen', label='Regression', linewidth=1) plt.legend() plt.show()
2、绘制随着每次梯度下降,w0,w1,loss的变化曲线。
w0 = w0[:-1] w1 = w1[:-1] plt.figure('Training Progress') plt.subplot(311) plt.title('Training Progress') plt.ylabel('w0', fontsize=14) plt.gca().xaxis.set_major_locator(plt.MultipleLocator(100)) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.plot(epoches, w0, c='dodgerblue', label='w0') plt.legend() plt.subplot(312) plt.ylabel('w1', fontsize=14) plt.gca().xaxis.set_major_locator(plt.MultipleLocator(100)) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.plot(epoches, w1, c='limegreen', label='w1') plt.legend() plt.subplot(313) plt.xlabel('epoch', fontsize=14) plt.ylabel('loss', fontsize=14) plt.gca().xaxis.set_major_locator(plt.MultipleLocator(100)) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.plot(epoches, losses, c='orangered', label='loss') plt.legend() plt.show()
3、基于三维曲面绘制梯度下降过程中的每一个点。
import mpl_toolkits.mplot3d as axes3d # 生成网格化坐标矩阵 grid_w0, grid_w1 = np.meshgrid( np.linspace(0, 9, 500), np.linspace(0, 3.5, 500)) grid_loss = np.zeros_like(grid_w0) for x, y in zip(train_x, train_y): grid_loss += ((grid_w0 + x*grid_w1 - y) ** 2) / 2 plt.figure('Loss Function') ax = plt.gca(projection='3d') plt.title('Loss Function') ax.set_xlabel('w0', fontsize=14) ax.set_ylabel('w1', fontsize=14) ax.set_zlabel('loss', fontsize=14) ax.plot_surface(grid_w0, grid_w1, grid_loss, rstride=10, cstride=10, cmap='jet') ax.plot(w0, w1, losses, 'o-', c='orangered', label='BGD') plt.legend() plt.show()
sklearn.linear_model
import sklearn.linear_model as lm model = lm.LinearRegression() # 创建模型 model.fit(输入, 输出) # 训练模型 result = model.predict(array) # 预测输出
案例:基于线性回归训练single.txt中的训练样本,使用模型预测测试样本。
import numpy as np import matplotlib.pyplot as plt import sklearn.linear_model as lm # 采集数据 x, y = np.loadtxt('../machine_learning_date/single.txt', delimiter=',', usecols=(0, 1), unpack=True) x = x.reshape(-1, 1) # 把x变为 n行1列 model = lm.LinearRegression() model.fit(x, y) # 训练模型 pred_y = model.predict(x) # 模型预测 把样本的x传入模型,预测输出 # 图像绘制 plt.figure('Linear Regression', facecolor='lightgray') plt.title('Linear Regression') plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sapltle') plt.plot(x, pred_y, c='orangered', label='Regression Line') plt.legend() plt.show()
评估训练结果误差(metrics)
线性回归模型训练完毕后,可以利用测试集评估训练结果误差。sklearn.metrics提供了计算模型误差的几个常用算法:
import sklearn.metrics as sm sm.mean_absolute_error(y, pred_y) # 平均绝对值误差 sm.mean_squared_error(y, pred_y) # 平均平方误差 sm.median_absolute_error(y, pred_y) # 中位绝对值误差 sm.r2_score(y, pred_y) # R2得分
平均绝对值误差:$\frac{1}{m}\sum|实际输出-预测输出|$
平均平方误差:$SQRT(\frac{1}{m}\sum(实际输出-预测输出)^2)$
中位绝对值误差:MEDIAN(|实际输出-预测输出|)
R2得分:(0,1]区间的分数,分数越高,误差越小
案例:在上一个案例中使用sm评估模型误差。
m1 = sm.mean_absolute_error(y, pred_y) # 平均绝对值误差,0.5482812185435971 m2 = sm.mean_squared_error(y, pred_y) # 平均平方误差 0.436069032381806 m3 = sm.median_absolute_error(y, pred_y) # 中位绝对值误差 0.5356597030142558 r2 = sm.r2_score(y, pred_y) # R2得分 0.7362638998481811
模型的保存和加载
模型训练是一个耗时的过程,一个优秀的机器学习是非常宝贵的。模型可以保存到磁盘中,也可以在需要使用的时候从磁盘中重新加载模型。不需要重新训练。
模型保存和加载相关API:
import pickle pickle.dump(内存对象, 磁盘文件) # 保存模型 model = pickle.load(磁盘文件) # 加载模型
案例:把训练好的模型保存到磁盘中。
# 将训练好的模型对象保存到磁盘文件中 with open('../../data/linear.pkl', 'wb') as f: pickle.dump(model, f) # 从磁盘文件中加载模型对象 with open('../../data/linear.pkl', 'rb') as f: model = pickle.load(f) pred_y = model.predict(x) # 根据输入预测输出
岭回归
普通线性回归模型使用基于梯度下降的最小二乘法,在最小化损失函数的前提下,寻找最优模型参数,于此过程中,包括少数异常样本在内的全部训练数据都会对最终模型参数造成程度相等的影响,异常值对模型所带来影响无法在训练过程中被识别出来。为此,岭回归在模型迭代过程所依据的损失函数中增加了正则项,以限制模型参数对异常样本的匹配程度,进而提高模型面对多数正常样本的拟合精度。
model = sklearn.linear_model.Ridge(正则强度,fit_intercept=是否训练截距, max_iter=最大迭代次数)
案例:加载abnormal.txt文件中的数据,基于岭回归算法训练回归模型。
import numpy as np import matplotlib.pyplot as plt import sklearn.linear_model as lm # 采集数据 x, y = np.loadtxt('../machine_learning_date/abnormal.txt', delimiter=',', usecols=(0, 1), unpack=True) # 训练模型 x = x.reshape(-1, 1) # 把x变为 n行1列 model = lm.Ridge(300, fit_intercept=True, max_iter=1000) model.fit(x, y) pred_y = model.predict(x) # 模型预测 # 图像绘制 plt.figure('Linear Regression') plt.title('Linear Regression') plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sapltle') plt.plot(x, pred_y, c='orangered', label='Regression Line') plt.legend() plt.show()
多项式回归
若希望回归模型更好的拟合训练样本数据,可以使用多项式回归器。
一元多项式回归
$$y=w_0 + w_1 x_1+w_2x_2+w_3x_3+ ... +w_nx_n$$
将高次项看做对一次项特征的扩展得到,那么一元多项式回归即可以看做为多元线性回归,可以使用LinearRegression模型对样本数据进行模型训练。
所以一元多项式回归的实现需要两个步骤:
-
将一元多项式回归问题转换为多元线性回归问题(只需给出多项式最高次数即可)
-
w1 w2 .. 当做样本特征,交给线性回归器训练多元线性模型。
使用sklearn提供的数据管线实现两个步骤的顺序执行:
import sklearn.pipeline as pl import sklearn.preprocessing as sp import sklearn.linear_model as lm model = pl.make_pipeline( sp.PolynomialFeatures(10), # 多项式特征扩展器 lm.LinearRegression()) # 线性回归器
案例:
""" demo07_poly.py 多项式回归模型 """ import numpy as np import matplotlib.pyplot as plt import sklearn.linear_model as lm import sklearn.preprocessing as sp import sklearn.pipeline as pl import sklearn.metrics as sm # 采集数据 x, y = np.loadtxt('../machine_learning_date/single.txt', delimiter=',', usecols=(0, 1), unpack=True) x = x.reshape(-1, 1) # 把x变为 n行1列 # 训练多项式回归模型(管线) model = pl.make_pipeline( sp.PolynomialFeatures(10), # 多项式特征扩展器 lm.LinearRegression()) # 线性回归器 model.fit(x, y) # 训练模型 pred_y = model.predict(x) # 模型预测 print(sm.r2_score(y, pred_y)) # 0.7868629092058502 # 绘图所需参数 # 多项式回归模型不能做超出x范围的预测业务 px = np.linspace(x.min(), x.max(), 1000) px = px.reshape(-1, 1) py = model.predict(px) # 图像绘制 plt.figure('Linear Regression') plt.title('Linear Regression') plt.xlabel('x', fontsize=14) plt.ylabel('y', fontsize=14) plt.tick_params(labelsize=10) plt.grid(linestyle=':') plt.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sapltle') plt.plot(px, py, c='orangered', label='Regression Line') plt.legend() plt.show()
欠拟合:模型没有训练到位,无论对于训练数据还是测试数据都无法给出足够高的预测精度
过拟合:过于复杂的模型,对于训练数据可以得到较高的预测精度,但对于测试数据通常精度较低
一个性能可以接受的学习模型应该对训练数据和测试数据都有接近的预测精度,而且精度不能太低。