机器学习-岭回归与Lasso回归

文章发布时间:

最后更新时间:

文章总字数:
1.6k

预计阅读时间:
6 分钟

一.岭回归 Ridge

岭回归是失损函数通过添加所有权重的平方和的乘积(L2)来惩罚模型的复杂度。公式如下:

已经定死使用L2正则化后函数(MSE + L2)

特点:

  • 岭回归不会将权重压缩到零,这意味着所有特征都会保留在模型中,但它们的权重会被缩小。

  • 适用于特征间存在多重共线性的情况。

  • 岭回归产生的模型通常更为平滑,因为它对所有特征都有影响。

这里的API直接用就可以,要使用小批量的话,替代SGD函数就好。

API:

sklearn.linear_model.Ridge()

参数:

  • alpha, default=1.0,正则项力度

  • fit_intercept, 是否计算偏置, default=True

  • solver, {‘auto’, ‘svd’, ‘cholesky’, ‘lsqr’, ‘sparse_cg’, ‘sag’, ‘saga’, ‘lbfgs’}, default=’auto’
    当值为auto,并且数据量、特征都比较大时,内部会随机梯度下降法。

  • normalize:,default=True, 数据进行标准化,如果特征工程中已经做过标准化,这里就该设置为False

  • max_iterint, default=None,梯度解算器的最大迭代次数,默认为15000

属性:

  • coef_ 回归后的权重系数

  • intercept_ 偏置

说明:SGDRegressor也可以做岭回归的事情,比如SGDRegressor(penalty=’l2’,loss=”squared_loss”),但是其中梯度下降法有些不同。所以推荐使用Ridge实现岭回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 岭回归 加载糖尿病数据集,进行回归预测
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor, Ridge
from sklearn.metrics import mean_squared_error

from sklearn.datasets import load_diabetes
# 加载数据
diabetes = load_diabetes()
# 划分训练集与测试集
x_train, x_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, random_state=22)
# 标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)

# 岭回归预估器
estimator = Ridge(fit_intercept=False,alpha=1.0, max_iter=10000)
estimator.fit(x_train, y_train)

# 得出模型
print("权重系数为:\n", estimator.coef_) #权重系数与特征数一定是同样的个数。

# 模型评估
y_predict = estimator.predict(x_test)
error = mean_squared_error(y_test, y_predict)
print("均方误差为:\n", error)

结果:

1
2
3
4
5
权重系数为:
[ 1.29470995 -13.21648422 23.35025863 13.99772198 -21.89232627
11.19336028 -7.69167174 3.09308558 30.98004941 3.14099876]
均方误差为:
28003.818165986613

二.拉索回归 lasso

Lasso回归是一种线性回归模型,它通过添加所有权重的绝对值之和(L1)来惩罚模型的复杂度。公式如下:

定死使用L1正则化后函数(MAE + L1)

可以看到,lasso回归进行正则化的原理实际也是在同等损失下,让惩罚性更小,这个取值的点一般都在坐标轴上,因为此时惩罚项是最小的。但是我们实际的目的是让loss下降,所以在w的取舍下,会优先考虑让损失下降,而非先考虑惩罚性的下降(loss的数量级更高)。不过即使这样考虑,损失依然会有一些上升,这是考虑正则项的必然。

这里的API直接用就可以,要使用小批量的话,替代SGD函数就好。

API:

sklearn.linear_model.Lasso()

参数:

  • alpha (default=1.0):控制正则化强度,非负浮点数。

  • fit_intercept (default=True):是否计算此模型的截距。

  • precompute (bool or array-like, default=False):

如果为 True,则使用预计算的 Gram 矩阵来加速计算。如果为数组,则使用提供的 Gram 矩阵。

  • copy_X (default=True):如果为 True,则复制数据 X,否则可能对其进行修改。

  • max_iter (default=1000):最大迭代次数。

  • tol (float, default=1e-4):精度阈值。如果更新后的系数向量减去之前的系数向量的无穷范数除以 1,加上更新后的系数向量的无穷范数小于 tol,则认为收敛。

  • warm_start (bool, default=False):为 True 时,再次调用 fit 方法会重新使用之前调用 fit 方法的结果作为初始估计值

  • positive (bool, default=False):为 True 时,强制系数为非负。

  • random_state:随机数种子

  • selection ({‘cyclic’, ‘random’}, default=’cyclic’):

如果设置为 ‘random’,则随机选择坐标进行更新。如果设置为 ‘cyclic’,则按照循环顺序选择坐标。

属性:

  1. coef_

    • 系数向量或者矩阵,代表了每个特征的权重。
  2. intercept_

    • 截距项(如果 fit_intercept=True)。
  3. n_iter_

    • 实际使用的迭代次数。
  4. n_features_in_ (int):

    • 训练样本中特征的数量。

注意:w是可以比x特征数量多的。比如

1
2
3
4
5
y1 = w1 * x1 + w2 * x2

y2 = w3 * x1 + w4 * x2

y = w5 * y1 + w6 * y2

这就是非线性回归。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 具体实现方法
# 线性回归 加载糖尿病数据集,进行回归预测
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor,Lasso
from sklearn.metrics import mean_squared_error
import numpy as np
import math
import time
import os
import joblib

from sklearn.datasets import load_diabetes
diabetes = load_diabetes()

# 若本地有模型,选择获取
model_path=os.path.join(os.path.dirname(__file__),'../model',"Lasso_regressor_model.pkl")
transfer_path=os.path.join(os.path.dirname(__file__),'../model',"Lasso_transfer.pkl")

model=None
transfer=None
# 根据模型再本地是否存在,选择两种模型加载方式
if os.path.exists(model_path):
model=joblib.load(model_path)
transfer=joblib.load(transfer_path)
else:
model=Lasso(fit_intercept=False,max_iter=100,warm_start=True,alpha=1,tol=0.001)
transfer=StandardScaler()

# 划分训练集与测试集
x_train, x_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, random_state=22)

x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)

# 定义超参数
batch_size = 32 # 批量大小
epochs = 1000 # 迭代次数


# 批次数量,向上取整
n_batches = math.ceil(len(x_train) / batch_size)

# 这个循环是迭代次数的体现
for epoch in range(epochs):
# 随机打乱样本顺序
indices = np.arange(len(x_train))
np.random.shuffle(indices)
start_time = time.time()


# 这个循环是为了把每个批次都取一遍,因为把所有数据都取一次才算一次迭代epoch
for i in range(n_batches):
start_idx = i * batch_size
# 因为最后一批可能不足一个batch_size,所以要用min函数
end_idx = min((i + 1) * batch_size,len(x_train))
batch_indices = indices[start_idx:end_idx]
X_batch = x_train[batch_indices]
y_batch = y_train[batch_indices]
model.fit(X_batch, y_batch) # 更新模型权重

score = model.score(x_test, y_test)
# 打印每一轮的评分与训练时间
print(f"训练轮次:{epoch} / {epochs}, score:{score}, 训练时间:{time.time() - start_time}s")

# 保存模型
joblib.dump(model,model_path)
joblib.dump(transfer,transfer_path)

# 得出模型
print("权重系数为:\n", model.coef_) #权重系数与特征数一定是同样的个数。

# 模型评估
y_predict = model.predict(x_test)
error = mean_squared_error(y_test, y_predict)
print("均方误差为:\n", error)

# 预测
def detect():
model=joblib.load(model_path)
transfer=joblib.load(transfer_path)
x_true=[[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]]
x_true=transfer.transform(x_true)
print(model.predict(x_true))

if __name__ == '__main__':
detect()