系列文章目录
【学习笔记】 陈强-机器学习-Python-Ch4 线性回归
【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归
【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv)
【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归
【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析
【学习笔记】 陈强-机器学习-Python-Ch8 朴素贝叶斯
前言
本学习笔记 仅为以防自己忘记了,顺便分享给一起学习的网友们参考。如有不同意见/建议,可以友好讨论。
本学习笔记 所有的代码和数据都可以从 陈强老师的个人主页 上下载
参考书目:陈强.机器学习及Python应用. 北京:高等教育出版社, 2021.
数学原理等 详见陈强老师的 PPT
参考了网友阡之尘埃的Python机器学习04——惩罚回归
一、理论
“高维数据” (high dimensional data,即特征向量
x
i
x_i
xi的维度
p
p
p 大于样本容量
n
n
n) 进行传统的线性回归模型时,存在严格多重共线性(因为:X的列秩 = X的行秩 ≤ n<p
),故逆矩阵
(
X
′
X
)
−
1
(X'X)^{-1}
(X′X)−1 不存在,故 OLS 不存在唯一解。
如果强行对高维数据使用传统的OLS 回归,可能得到完美的/过拟合(overfit) 的 样本内拟合,但外推预测的效果则可能很差。
且,高维数据不能像传统的低维数据处理“严格多重共线性” 那样只要将多余的变量去掉,因为需要扔掉变量过多。
高维回归:
1. 解决多重共线性: 岭回归(Ridge Regression)
岭回归提出的出发点正是为了解决多重共线性:
在损失函数 (loss function)中加入“惩罚项” (penalty term) 进行惩罚回归(penalized regression)。
【注意】:由于岭回归通过惩罚项,惩罚过大的回归系数,故变量单位对于岭回归有实质性影响。
步骤:
1)将每个变量
x
j
(
j
=
1
,
.
.
.
,
p
)
x_j (j=1,...,p)
xj(j=1,...,p) 标准化(
x
j
−
x
j
‾
x_j -\overline{x_j}
xj−xj):变量的样本均值为0。因y的样本均值已为 0,故常数项不必放入岭回归方程中
2)除以标准差
s
d
(
x
j
)
sd(x_j)
sd(xj)
3)使用标准化的变量
x
j
−
x
j
‾
s
d
(
x
j
)
{x_j -\overline{x_j }} \over {sd(x_j)}
sd(xj)xj−xj进行岭回归
岭回归通常只是将所有的回归系数都收缩,而不会让某些回归系数严格等于0 。
2. 筛选出真正对 y y y有影响的变量:LASSO
在进行高维回归时,希望从大量变量中筛选出真正对 y y y有影响的少数变量。
套索估计量 (Least Absolute Shrinkage and Selection Operator,简记 LASSO) 将岭回归惩罚项中的“2-范数”改为“1-范数",导致Lasso估计量的某些回归系数严格等于0,从而得到“稀疏解”(sparse solution) 。
LASSO V.S. 岭回归
从预测的角度 | LASSO | 岭回归 |
---|---|---|
真实模型: 稀疏 | 优 | - |
真实模型:不稀疏 | - | 优 |
3. 弹性网(Elastic Net)估计:Lasso回归和Ridge回归的结合
Elastic Net回归的目标是通过最小化加权的损失函数来确定模型的系数。 结合了Lasso的特征选择能力和Ridge的共线性处理能力。对具有高维特征或特征间高度共线性的情况特别有效。
由于岭回归与 均为弹性网的特例,而弹性网可通过交叉验证选择 Lasso 最优的调节参数alpha,故弹性网的预测能力肯定不差于前二者。
4. 逻辑Lasso & 惩罚逻辑回归 (弹性网的惩罚项)
二、 惩罚回归 Python案例
1.数据prostate.csv
prostate.csv 集包含97个前列腺癌患者的数据。
响应变量为 lpsa(log of prostate specific antigen,前列腺特异抗原对数 )
特征变量包括以下临床指标(clinical measures) :
·lcavol (log of cancer lcavol ,肿瘤体积对数)
·lweight (log of prostate weight lweight,前列腺重量对数)
·age (年龄)
·lbph (log of benign prostatic hyperplasia amount)
·svi (seminal vesicle invasion)
·lcp (log of capsular penetration)
·gleason (Gleason score)
·pgg45 (percentage Gleason scores 4 or 5)
#导入模块
import numpy as np
import pandas as pd
# 定义文件路径
csv_path = r'D:\桌面文件\Python\【陈强-机器学习】MLPython-PPT-PDF\MLPython_Data\prostate.csv'
# 读取数据
prostate = pd.read_csv(csv_path)
prostate.shape
结果输出: (97, 9) 97个观测值与9个变量
#显示前5个观测值
prostate.head()
#显示所有变量的均值与标准差
print("prostate.mean:\n",prostate.mean())
print("prostate.std:\n",prostate.std())
特征变量Xi的均值与标准差有较大差异
1.岭回归
1)标准化变量
# 1.取出原始数据
X_raw = prostate.iloc[:, :-1]
y = prostate.iloc[:, -1]
#2.标准化(sklearn的StandardScaler)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler() #初始化StandardScaler对象
X = scaler.fit_transform(X_raw)
#3. 验证每列的均值与标准差
print(np.mean(X,axis=0)) #axis=0 表示按列计算均值
np.std(X,axis=0)
结果输出: [ 4.57823928e-17 2.89573634e-16 4.13186095e-16 -2.43218962e-17
-3.66259142e-17 3.66259142e-17 -2.17466366e-17 5.63695711e-17]
array([1., 1., 1., 1., 1., 1., 1., 1.])
2)进行岭回归
#进行岭回归
from sklearn.linear_model import Ridge
Ridge = Ridge()
#拟合模型
Ridge.fit(X, y)
print(Ridge.score(X, y)) #计算 样本数据的拟合优度
print(Ridge.intercept_) #截距
print(Ridge.coef_) #回归系数
结果输出: 0.6632704474129987
2.478386878350515
[ 0.64734891 0.26423507 -0.15178989 0.13694453 0.30825889 -0.13074243
0.03755141 0.11907848]
笔记:Ridge()
Ridge 是 scikit-learn 中的一种线性回归模型,它实现了岭回归(Ridge Regression)。
#基本语法和参数
from sklearn.linear_model import Ridge
# 创建 Ridge 模型实例
model = Ridge(
alpha=1.0, #正则化强度。alpha 的值越大,正则化的效果越强,模型的系数越接近零。默认值为 1.0。
fit_intercept) = True #是否计算截距。默认= True(计算截距)。如果为 False,模型不计算截距。
3)岭回归的整个系数路径(coefficient path)
'''使用不同的惩罚参数alpha,得到Ridge的整个系数路径(coefficient path)'''
from sklearn.linear_model import Ridge
# 1.定义参数alpha的网格
alphas = np.logspace(-3, 6, 100) #对数尺度上从10^(−3)到10^6之间均匀分布的100个值的数组
# 2.for循环得到不同的alpha取值的响应回归系数
coefs = [] #创建一个空列表coefs用于存储不同alpha值下的回归系数。
for alpha in alphas: #循环遍历:遍历alphas数组 中的每一个alpha值。
model = Ridge(alpha=alpha) #创建模型:对于每一个alpha值,创建一个Ridge回归模型实例。
model.fit(X, y)
coefs.append(model.coef_) #存储系数:将拟合得到的回归系数(model.coef_)添加到coefs 表中。
#画出系数路径
import matplotlib.pyplot as plt
ax = plt.gca() #用于获取当前的坐标轴(Axes)对象。gca代表 “Get Current Axes”,即获取当前活动的坐标轴。如果当前没有坐标轴,它会自动创建一个新的坐标轴。
ax.plot(alphas, coefs) #在坐标轴ax上绘制 alphas 和 coefs 之间的关系图。alphas是x轴(对数刻度),coefs是y轴(系数)。
ax.set_xscale('log')
plt.xlabel('alpha (log scale)')
plt.ylabel('Coefficients')
plt.title('Ridge Cofficient Path')
plt.axhline(0, linestyle='--', linewidth=1, color='k')
plt.legend(X_raw.columns)
笔记:np.logspace()
np.logspace() 是 NumPy 库中的一个函数,用于生成对数刻度的等间隔数值序列。
#基本语法和参数
import numpy as np
# 生成对数间隔的数值序列
arr = np.logspace(
start, #对数刻度序列的起始值(以base为底的对数)。
stop, #对数刻度序列的终止值(以base为底的对数)。
num=50, #生成的样本数量,默认为 50。
base=10.0, #对数的底数,默认为 10.0。
dtype=None) #输出数组的数据类型,默认为 None,自动推断。
4)选出 最优惩罚系数:交叉验证法 RidgeCV
#选出 最优惩罚系数:交叉验证
from sklearn.linear_model import RidgeCV
RidgeCV = RidgeCV(alphas=alphas) #RidgeCV 默认LOOCV
#"alphas=alphas":将惩罚参数设为前面的alphas网格=np.logspace(-3, 6, 100)
RidgeCV.fit(X, y)
#获得 最优(默认mse最小)的参数alpha
RidgeCV.alpha_
结果输出:
6.5793322465756825
#在1-10之间找出最优惩罚参数
alphas=np.linspace(1, 10, 1000)
RidgeCV_1_10 = RidgeCV(alphas=alphas, store_cv_results=True)
RidgeCV_1_10.fit(X, y)
print(RidgeCV_1_10.alpha_)
print(RidgeCV_1_10.store_cv_values.shape)
结果输出:
6.018018018018018
(97, 1000) 验证结果97×1000的矩阵
笔记:RidgeCV ()
RidgeCV 是 scikit-learn 库中的一个类,用于进行岭回归(Ridge Regression)并通过交叉验证选择最佳的正则化强度 alpha。
它能够在给定的一组 alpha 值中选择使模型表现最佳的那个。
#基本语法和参数
ffrom sklearn.linear_model import RidgeCV
# 创建 RidgeCV 模型实例
model = RidgeCV(
alphas=alphas, #一组正则化强度的值(即 alpha),RidgeCV 将在这些值中选择最优的一个。可以是一个列表、数组或其他序列类型。
store_cv_results=True, #是否存储每次交叉验证的结果(MSE)。默认为 False。
cv=5) #交叉验证策略,用于评估不同 alpha 值的性能。可以是一个整数(表示折数),或者是一个交叉验证生成器。默认为LOOCV。
5)查看 岭回归的交叉验证图
#获得1000个不同alpha所对应的MSE
mse = np.mean(RidgeCV_1_10.cv_results_, axis=0)
# mse的最小值
print(np.min(mse))
# mse的最小值的索引位置
index_min = np.argmin(mse) # np.argmin()
print(index_min)
#最优alpha与最小mse
alphas[index_min], mse[index_min]
结果输出: 0.5363247513368606
557
(6.018018018018018, 0.5363247513368606) 与之前一样
笔记:np.argmin()
np.argmin() 是 NumPy 库中的一个函数,用于返回数组中最小值的索引。
#基本语法和参数
import numpy as np
# 找到最小值的索引
index = np.argmin(
输入数组或可转换为数组的对象(以是1D、2D或更高维度的数组),
axis = None,
out:可选参数,用于指定输出数组的位置。如果给定,结果将存储在这个数组中。)
#画出交叉验证图
plt.plot(alphas, mse)
plt.axvline( #在图表中添加垂直线
alphas[index_min], #在alpha最小(最优)的地方
linestyle='--', #画虚线
linewidth=1, color='k') #线宽为1,黑色
plt.xlabel('alpha')
plt.ylabel('Mean Squared Error')
plt.title('CV Error for Ridge Regression')
plt.tight_layout() #自动调整布局
mse在alpha=6.018处最小。
6)展示最佳模型的回归系数
#展示最佳模型(alpha=6.018018018018018时)的回归系数
RidgeCV_1_10.coef_
pd.DataFrame( #创建数据框
RidgeCV_1_10.coef_, #输入的数据:
index=X_raw.columns, #行标签
columns=['Coefficient']) #列标签
7)RidgeCV:10折交叉验证
'''RidgeCV默认的LOOCV一般适用于较小的数据集'''
'实践中更常用的是 10折交叉验证'
# RidgeCV用10折交叉验证
kfold = KFold(n_splits=10,shuffle=True, random_state=1)
kfold_10 = RidgeCV(alphas=np.linspace(1,10,1000),cv=kfold)
kfold_10.fit(X,y)
kfold_10.alpha_ #10折cv的最优惩罚参数
结果输出: 3.2522522522522523
不同于之前的6.018。 本数据属于小样本,推荐使用LOOCV
3.Lasso回归
1)alpha=0.1的Lasso回归
from sklearn.linear_model import Lasso
lasso = Lasso(alpha=0.1) #Lasso默认alpha=1
lasso.fit(X,y)
lasso.score(X,y) #lasso回归的R^2
结果输出: 0.6261113876338473
#展示 回归系数
RidgeCV_1_10.coef_
pd.DataFrame(lasso.coef_,
index=X_raw.columns,
columns=['Coefficient'])
结果显示中,8个特征变量中有3个的回归系数严格为0。→lasso得到一个稀疏解
2)alpha=1的Lasso回归
#展示alpha=1的lasso估计的回归系数
Lasso().fit(X,y).coef_ #Lasso()默认alpha=1
结果输出: array([0., 0., 0., 0., 0., 0., 0., 0.])
结果显示,8个特征变量的回归系数都严格为0。 →alpha=1的惩罚力度对于本数据集而言,过于严厉。
3)画出lasso的系数路径图
# 用lasso_path()得到惩罚系数与响应回归系数
from sklearn.linear_model import lasso_path
alphas, coefs, _ = lasso_path(X, y, eps=1e-4)
alphas.shape, coefs.shape
结果输出: ((100,), (8, 100)) alphas和coefs形状不一致
#画出lasso的系数路径图
ax = plt.gca()
ax.plot(alphas, coefs.T) #转置coefs,因为alphas.shape ≠ coefs.shape
ax.set_xscale('log')
plt.xlabel('alpha (log scale)')
plt.ylabel('Coefficients')
plt.title('Lasso Cofficient Path')
plt.axhline(0, linestyle='--', linewidth=1, color='k')
plt.legend(X_raw.columns)
Lasso估计量:收缩回归系数+具备筛选变量的功能
笔记:lasso_path()
lasso_path() 是 scikit-learn 库中 sklearn.linear_model 模块提供的一个函数,用于计算 Lasso 回归(L1 正则化)的解路径。Lasso 回归通过对回归系数施加 L1 正则化来执行特征选择和降维,lasso_path() 函数可获取不同正则化强度下的回归系数。
#基本语法和参数
from sklearn.linear_model import lasso_path
# 使用 lasso_path 计算解路径
alphas, coefs, _ = lasso_path(
X, #输入特征矩阵(二维数组或矩阵)。每一行代表一个样本,每一列代表一个特征。
y, #目标变量(一维数组)。每个元素对应一个样本的目标值。
alphas=None, #要使用的正则化强度值列表或数组。如果为 None,将使用n_alphas参数指定的默认值生成一系列 alpha。
*, Xy=None,
eps=0.001, #在自动选择alpha值时,设置生成 alpha 值的范围的间隔(默认为 0.001)。
n_alphas=100, #要生成的alpha值的数量(默认为 100)。如果alphas为 None,则此参数指定将生成的 alpha 值的数量。
method='cholesky', #计算方法,默认为 'cholesky'。其他方法如 'sparse_cg' 或 'lsqr' 也可用。
return_models=False,
fit_intercept=True,
normalize=False,
precompute=True, #是否使用预计算的 Gram 矩阵(默认为 True)
random_state=None)
4)交叉验证法LassoCV:选出最优alpha
from sklearn.linear_model import LassoCV
kfold = KFold(n_splits=10, shuffle=True, random_state=1)
alphas=np.logspace(-4, -2, 100)
model_lasso_10cv = LassoCV(alphas=alphas, cv=kfold)
model_lasso_10cv.fit(X, y)
model_lasso_10cv.alpha_
结果输出: 0.007220809018385471
#展示 最优lasso(alpha=0.007220809018385471) 回归系数
pd.DataFrame(
model_lasso_10cv.coef_,
index=X_raw.columns,
columns=['Coefficient'])
交叉验证的MSE
print(model_lasso_10cv.mse_path_.shape)
mse_lasso_10cv = np.mean(
model_lasso_10cv.mse_path_,
axis=1) #axis=1 表示按行计算均值
index_min = np.argmin(mse_lasso_10cv)
alphas[index_min]
结果输出: (100, 10)
0.00013848863713938732
最优alpha与之前的结果不一致
5)用for循环(手动 交叉验证 cross_val_score)
#用for循环(手动 交叉验证 cross_val_score)
from sklearn.model_selection import cross_val_score
alphas = np.logspace(-4, -2, 100)
scores = []
for alpha in alphas:
model = Lasso(alpha=alpha)
kfold = KFold(n_splits=10,shuffle=True, random_state=1)
cross_val = -cross_val_score(
model,X,y,cv=kfold,
scoring = 'neg_mean_squared_error') #使用“MSE的负数”作为评价标准
score = np.mean(cross_val)
scores.append(score)
mse_cross_val = np.array(scores)
index_min = np.argmin(mse_cross_val)
alphas[index_min]
结果输出: 0.007220809018385471
与之前用LassoCV的结果一致
6)画出lasso交叉验证图
plt.plot(alphas, mse_cross_val)
plt.axvline( #在图表中添加垂直线
alphas[index_min], #在alpha最小(最优)的地方
linestyle='--', #画虚线
linewidth=1, color='k') #线宽为1,黑色
plt.xlabel('alpha')
plt.ylabel('Mean Squared Error')
plt.title('CV Error for Lasso')
plt.tight_layout() #自动调整布局
4.Elastic Net弹性网估计
1)ElasticNet()
from sklearn.linear_model import ElasticNet
model_ENet = ElasticNet(
alpha=0.1,
l1_ratio=0.5) #L1与L2惩罚项的比重为0.5
model_ENet.fit(X, y)
model_ENet.score(X, y)
结果输出: 0.641941891532626 R2
#展示 弹性网估计的回归系数
pd.DataFrame(
model_ENet.coef_,
index=X_raw.columns,
columns=['Coefficient'])
结果中,仅一个变量的回归系数严格为0。
笔记:ElasticNet()
ElasticNet 是一种线性回归模型,综合利用 Lasso 回归(L1 正则化)和 Ridge 回归(L2 正则化)的特点。
#基本语法和参数
from sklearn.linear_model import ElasticNet
model = ElasticNet(
alpha=1.0, #正则化强度的乘数,alpha 越大,正则化效果越强。默认为 1.0
l1_ratio=0.5, #L1 正则化与 L2 正则化之间的比例。l1_ratio = 1 是纯 Lasso 回归,而 l1_ratio = 0 是纯 Ridge 回归。
fit_intercept=True, #默认为 True(计算截距),. False,将不会在计算中使用截距(即假设数据已经居中)。
normalize=False, #在拟合之前,该参数决定是否首先对回归器进行归一化。如果为 True,则回归变量 X 将在回归前被减去平均值并除以 L2 范数。注意,默认False,将忽略此参数。
max_iter=1000, #求解器对于单个数据集运行的最大迭代次数。默认为 1000
tol=1e-4, #优化的容忍度。默认为 1e-4
random_state=None)
2)用enet_path()得到惩罚系数与响应回归系数
from sklearn.linear_model import enet_path
# 用enet_path()得到惩罚系数与响应回归系数
alphas, coefs, _ = enet_path(X, y, eps=1e-4,l1_ratio=0.5)
#画出Elastic Net弹性网的系数路径图
ax = plt.gca()
ax.plot(alphas, coefs.T) #转置coefs,因为alphas.shape ≠ coefs.shape
ax.set_xscale('log')
plt.xlabel('alpha (log scale)')
plt.ylabel('Coefficients')
plt.title('Elastic Net Cofficient Path (l1_ratio=0.5)')
plt.axhline(0, linestyle='--', linewidth=1, color='k')
plt.legend(X_raw.columns)
笔记:enet_path()
enet_path 函数是用来计算 ElasticNet 模型在一系列不同正则化强度下的路径的。这个函数对于了解不同正则化参数如何影响模型系数非常有用,常用于选择合适的参数。
#基本语法和参数
from sklearn.linear_model import enet_path
# 计算 Elastic Net 解路径
alphas, l1_ratios, coefs = enet_path(
X, #特征矩阵(二维数组或矩阵)。每行表示一个样本,每列表示一个特征。
y, #目标变量(数组)。每个元素对应一个样本的目标值。
l1_ratio=0.5, # L1和L2正则化的比例。如果传递了l1_ratio,它会使用相同的值计算不同的 alpha。
eps=0.001, #在生成alpha值时的范围的间隔(默认为 0.001)。影响生成的 alpha 值的范围。
n_alphas=100, #要生成的alpha值的数量(默认为 100)。如果alphas为 None,则此参数指定生成的 alpha 数量。
alphas=None, #指定的 alpha 值列表。如果为 None,则生成默认的 alpha 值范围。
fit_intercept=True, #是否计算截距(默认为 True)。
normalize=False, #是否在回归之前对特征进行归一化(默认为 False)。如果 True,则在拟合模型之前对特征进行标准化处理。
precompute=True, #是否使用预计算的 Gram 矩阵(默认为 True)。设置为 True 可以提高计算效率。
random_state=None,
return_models=False) #如果True,还会返回每个alpha和l1_ratio组合的模型实例(默认为 False)。
3)交叉验证法 ElasticNetCV
(1)选出最优alpha
from sklearn.linear_model import ElasticNetCV
# 选出最优alpha (交叉验证法 ElasticNetCV)
kfold = KFold(n_splits=10, shuffle=True, random_state=1)
alphas=np.logspace(-4, 0, 100)
model_enet_10cv = ElasticNetCV(
alphas=alphas, cv=kfold,
l1_ratio=[0.0001, 0.001, 0.01, 0.1, 0.5, 1]
)
model_enet_10cv.fit(X, y)
model_enet_10cv.alpha_
结果输出: 0.07390722033525783
(2)计算最优L1惩罚项比重
#最优L1惩罚项比重
model_enet_10cv.l1_ratio_
结果输出: 0.0001
优L1惩罚项比重=0.0001,为该网格最小值,≈ 0。→此数据集应进行岭回归。
(3)计算 最佳弹性网模型R2
#计算最佳弹性网模型的R^2
model_enet_10cv.score(X,y)
结果输出:
0.6592730190729547
(4)展示 最佳弹性网模型的回归系数
#展示 最佳弹性网模型的回归系数
pd.DataFrame(
model_enet_10cv.coef_,
index=X_raw.columns,
columns=['Coefficient'])
4)外推预测的效果
#划分训练集和测试集:验证模型是否过拟合
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=1)
model = RidgeCV(alphas=np.linspace(1, 20, 1000))
model.fit(X_train, y_train)
#展示最优惩罚参数alpha
model.alpha_
结果输出: 10.756756756756758
#训练集拟合优度
print("训练集拟合优度:",model.score(X_train, y_train))
#测试集拟合优度
print("测试集拟合优度:",model.score(X_test, y_test))
结果输出:
训练集拟合优度: 0.6760210841041492
测试集拟合优度: 0.45586215215050363
训练集拟合优度>测试集拟合优度 → 训练集拟合优度 明显高估了此模型的预测能力。