实验名称
线性回归和随机梯度下降
实验目的
通过该实验的学习与实践,了解: 1.线性回归的原理 2.随机梯度下降的原理
实验背景
线性回归是利用数理统计中回归分析,来确定两种或两种以上变量间相互依赖的定量关系的一种统计分 析方法,运用十分广泛。如数学,趋势线,流行病学,金融,经济学等。
实验原理
线性回归表达形式为y = w’x+e,e为误差服从均值为0的正态分布。在线性回归中,数据使用线性预测函 数来建模,并且未知的模型参数也是通过数据来估计。这些模型被叫做线性模型。
随机梯度下降一般指随机并行梯度下降算法,简称SPGD算法。作为一种无模型优化算法,比较适用于控 制变量较多,受控系统比较复杂,无法建立准确数学模型的最优化控制过程。在进行梯度估计时,可使 用双边扰动来提高梯度估计的精度。也就是分别对控制电压参量 施加一次正向扰动和负向扰动,并测量 两次扰动后的像质评价函数值的改变量作为性能指标梯度估计。在实际应用中,如使目标函数向极大方 向优化,μ取负值;反之,μ取正。
实验步骤
一、环境准备
1、本实验在jupyter notebook进行开发。
二、代码编写
1、导入实验所需要的资源库
import warnings
from sklearn.preprocessing import StandardScaler
import seaborn as sns
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
from tqdm import tqdm
from sklearn.base import BaseEstimator
from sklearn.metrics import mean_squared_error, log_loss, roc_auc_score
from sklearn.model_selection import train_test_split
2、下面,加载实验所使用的示例数据集。
data_demo = pd.read_csv( 'weights_heights.csv')
data_demo.head()
Index | Height | Weight | |
---|---|---|---|
0 | 1 | 65.78331 | 112.9925 |
1 | 2 | 71.51521 | 136.4873 |
2 | 3 | 69.39874 | 153.0269 |
3 | 4 | 68.21660 | 142.3354 |
4 | 5 | 67.78781 | 144.2971 |
3、每个样本数据包含 2 个特征,我们绘制二维散点图
plt.scatter(data_demo['Weight'], data_demo['Height'])
plt.xlabel('Weight (lbs)')
plt.ylabel('Height (Inch)')
plt.grid()
plt.show()
4、接下来,设定 Weight 为 X,Height 为 y。然后将数据集切分为训练和验证数据,并进行规范 化。
X, y = data_demo['Weight'].values, data_demo['Height'].values
X_train, X_valid, y_train, y_valid = train_test_split( X, y, test_size=0.3, random_state=17)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.reshape([-1, 1]))
X_valid_scaled = scaler.transform(X_valid.reshape([-1, 1]))
X_train_scaled.shape, X_valid_scaled.shape
((17500, 1), (7500, 1))
5、我们同样可以将规范化之后的训练数据绘制成散点图。
plt.scatter(X_train_scaled, y_train)
plt.xlabel('Weight (lbs)')
plt.ylabel('Height (Inch)')
plt.grid()
plt.show()
6、接下来,你需要实现一个使用随机梯度下降方法的线性回归类,并使其可以完成训练和测试的过程。
- 类名为 SGDRegressor,其继承自 sklearn.base.BaseEstimator。
- 构造函数接受参数 eta 学习率(默认为 10−310−3)和 n_epochs 全数据集迭代次数(默认为 3)。
- 构造函数创建 mse_ 和 weights_ 列表,以便在梯度下降迭代期间追踪均方误差和权重向量。
- 该类需包含 fit 和 predict 方法用于训练和预测。 fit 方法可接受矩阵 X 和向量 y(numpy.array 对象)作为参数。该方法可自动在 X 左侧追 加一列全为 1 的值作为截距项系数,权重 w 则统一用零初始化。然后进行 n_epochs 权重更新 迭代,并将每次迭代后的均方误差 MSE 和权重向量记录在预先初始化的空列表中。
- fit 方法返回 SGDRegressor 类的当前实例,即 self。
- predict 方法可接受矩阵 X 矩阵,同样需支持自动在 X 左侧追加一列全为 1 的值作为截距项系 数,并使用由 fit 方法得到的权重向量 w_ 计算后返回预测向量。
class SGDRegressor(BaseEstimator):
def __init__(self, eta=1e-3, n_epochs=3):
self.eta = eta
self.n_epochs = n_epochs
self.mse_ = []
self.weights_ = []
def fit(self, X, y):
X = np.hstack([np.ones([X.shape[0], 1]), X])
w = np.zeros(X.shape[1])
for it in tqdm(range(self.n_epochs)):
for i in range(X.shape[0]):
new_w = w.copy()
new_w[0] += self.eta * (y[i] - w.dot(X[i, :]))
for j in range(1, X.shape[1]):
new_w[j] += self.eta * (y[i] - w.dot(X[i, :])) * X[i, j]
w = new_w.copy()
self.weights_.append(w)
self.mse_.append(mean_squared_error(y, X.dot(w)))
self.w_ = self.weights_[np.argmin(self.mse_)]
return self
def predict(self, X):
X = np.hstack([np.ones([X.shape[0], 1]), X])
return X.dot(self.w_)
7、接下来,我们实例化 SGDRegressor 类,并传入训练数据,训练时间有点长,要等待几分钟:
sgd_reg = SGDRegressor()
sgd_reg.fit(X_train_scaled, y_train)
100%|████████████████████████████████████████████████████████████████████████████████████| 3/3 [00:16<00:00, 5.58s/it]
SGDRegressor()
8、如果一切正常的话,我们可以经由以下代码输出迭代过程中 MSE 的变化曲线:
plt.plot(range(len(sgd_reg.mse_)), sgd_reg.mse_) plt.xlabel('#updates') plt.ylabel('MSE') plt.show()
9、打印出 MSE 的最小值,以及最终的权重系数。
np.min(sgd_reg.mse_), sgd_reg.w_
(2.7151352406643627, array([67.9898497 , 0.94447605]))
10、绘制出 𝑤0w0 和 𝑤1w1 在训练过程中的变化曲线。
plt.subplot(121) plt.plot(range(len(sgd_reg.weights_)), [w[0] for w in sgd_reg.weights_])plt.subplot(122) plt.plot(range(len(sgd_reg.weights_)), [w[1] for w in sgd_reg.weights_])plt.show()
11、最后,使用 (X_valid_scaled, y_valid) 作出预测,并计算验证集上的 MSE 值。
sgd_holdout_mse = mean_squared_error(y_valid, sgd_reg.predict(X_valid_scaled))sgd_holdout_mse
2.6708681207033784
12、最后,我们通过一个单元测试来保证 SGDRegressor 已经正常工作。这里使用 sklearn.linear_model 提供的 LinearRegression 来计算普通最小二乘法得到在验证集上的 MSE 值。
from sklearn.linear_model import LinearRegression lm = LinearRegression().fit(X_train_scaled, y_train) print(lm.coef_, lm.intercept_) linreg_holdout_mse = mean_squared_error(y_valid, lm.predict(X_valid_scaled))linreg_holdout_mse
[0.94537278] 67.98930834742858
2.670830767667635
13、如果 SGDRegressor 得到的结果和 SGDRegressor 得到的结果在 10−410−4 范围之内,我们认为 测试通过。
try: assert (sgd_holdout_mse - linreg_holdout_mse) < 1e-4 print('Correct!')except AssertionError: print("Something's not good.\n Linreg's holdout MSE: {}" "\n SGD's holdout MSE: {}".format(linreg_holdout_mse, sgd_holdout_mse))
Correct!
实验总结
本实验仅对线性回归算法和随机梯度下降算法进行了简单讲述和练习,通过本实验希望我们能够巩固线 性回归算法和随机梯度下降的原理和使用场景。