《深入浅出python机器学习》读书笔记(四)
第四章 广义线性模型
文章目录
- 《深入浅出python机器学习》读书笔记(四)
- 前言
- 一、线性回归模型
- 线性回归模型的基础示例
- 使用线性回归预测糖尿病数据集
- 模型评估要点:
- 二、岭回归——使用L2正则化的线性模型
- 使用岭回归预测糖尿病数据集
- 岭回归参数调节
- 线性回归 vs 弱正则化岭回归的对比
- 三、套索回归——使用L1正则化的线性模型
- 使用套索回归预测糖尿病数据集
- 套索回归的参数调节
- Lasso回归的应用场景
前言
在机器学习领域,常用的线性模型包括线性回归、岭回归、套索回归、逻辑回归和线性SVC等。我们这里只讨论线性回归、岭回归和套索回归。
一、线性回归模型
线性回归模型的基础示例
# 导入必要的库importnumpyasnp# 用于数值计算和数组操作importmatplotlib.pyplotasplt# 用于数据可视化fromsklearn.linear_modelimportLinearRegression# 线性回归模型fromsklearn.datasetsimportmake_regression# 生成回归数据集# 1. 生成回归数据集# n_samples=50: 生成50个样本# n_features=1: 每个样本只有1个特征(一维数据)# n_informative=1: 1个有效特征(所有特征都有用)# noise=50: 添加噪声,控制数据点的分散程度,值越大数据越分散# random_state=1: 随机种子,确保每次运行生成相同的数据集X,y=make_regression(n_samples=50,n_features=1,n_informative=1,noise=50,random_state=1)# 2. 创建和训练线性回归模型reg=LinearRegression()# 创建线性回归模型实例reg.fit(X,y)# 使用生成的数据训练模型# fit()方法会计算模型参数:系数(斜率)和截距# 3. 准备绘图用的测试数据# 生成从-3到3的200个等间距点,用于绘制回归线# reshape(-1, 1)将一维数组转换为二维数组(符合sklearn的输入格式要求)z=np.linspace(-3,3,200).reshape(-1,1)# 4. 数据可视化plt.figure(figsize=(8,6))# 创建图形窗口(通常默认大小,这里显式设置更好)# 绘制原始数据点(散点图)# X: 特征值,y: 目标值# c='b': 蓝色点,s=60: 点的大小plt.scatter(X,y,c='b',s=60)# 绘制回归线# 使用训练好的模型预测z对应的y值,并绘制连续曲线# reg.predict(z): 用训练好的模型预测输入z对应的输出plt.plot(z,reg.predict(z),c='k',linewidth=3)# 添加标题plt.title('Linear Regression')# 添加坐标轴标签(建议添加,代码中缺失)plt.xlabel('X (Feature)')# X轴:特征plt.ylabel('y (Target)')# Y轴:目标值# 显示图形plt.show()# 可选:打印模型参数print(f"回归系数 (斜率):{reg.coef_[0]:.2f}")print(f"截距:{reg.intercept_:.2f}")print(f"R²得分:{reg.score(X,y):.3f}")回归系数 (斜率): 79.52 截距: 10.92 R²得分: 0.782使用线性回归预测糖尿病数据集
# 导入必要的库fromsklearn.datasetsimportload_diabetes# 导入糖尿病数据集fromsklearn.model_selectionimporttrain_test_split# 数据分割工具fromsklearn.linear_modelimportLinearRegression# 线性回归模型# 1. 加载糖尿病数据集# load_diabetes()返回一个Bunch对象,包含数据和元数据# data: 特征矩阵,包含10个特征(年龄、性别、BMI等医学指标)# target: 目标变量,糖尿病进展的定量测量(一年后的疾病进展)diabetes=load_diabetes()# 创建数据集对象X=diabetes.data# 特征矩阵 (442个样本 × 10个特征)y=diabetes.target# 目标向量 (442个值)# 2. 分割数据集为训练集和测试集# train_test_split默认分割比例为75%训练,25%测试# random_state=8: 随机种子,确保每次分割结果相同(便于结果复现)X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=8)# 3. 创建并训练线性回归模型lr=LinearRegression()# 创建线性回归模型实例lr.fit(X_train,y_train)# 使用训练数据拟合模型# 4. 查看模型参数和评估结果print("回归系数 (每个特征的权重):")print(lr.coef_[:])# 打印所有特征系数# coef_是一个数组,长度等于特征数量(10个)# 每个值表示对应特征对目标变量的影响权重# 正数表示正相关,负数表示负相关print("\n截距 (偏置项):")print(lr.intercept_)# 打印截距项# 当所有特征值为0时的预测值print("\n训练集R²分数:")print(lr.score(X_train,y_train))# R²分数(决定系数):# 1.0表示完美拟合# 0表示模型不比简单取平均值好# 负数表示模型表现比简单取平均值还差print("\n测试集R²分数:")print(lr.score(X_test,y_test))# 测试集上的R²分数,评估模型泛化能力回归系数 (每个特征的权重): [ 11.51226671 -282.51443231 534.2084846 401.73037118 -1043.90460259 634.92891045 186.43568421 204.94157943 762.46336088 91.95399832] 截距 (偏置项): 152.5625670974632 训练集R²分数: 0.5303801379270054 测试集R²分数: 0.45934221748744397模型评估要点:
训练集分数 vs 测试集分数:
- 训练集分数(0.53) > 测试集分数(0.48):轻微过拟合。
- 如果差异很大,说明模型过度记忆训练数据
R²分数解释:
- 0.5左右:中等预测能力
- 医学数据通常难以获得很高的R²值
- 0.48表示模型解释了约48%的目标变量方差
二、岭回归——使用L2正则化的线性模型
岭回归是回归分析中常用的线性模型,实际上是一种改良的最小二乘法,从使用的角度出发,它能够避免过拟合。
L2正则化,是指保留全部特征变量,只是降低特征变量的系数值来避免过拟合的方法。
使用岭回归预测糖尿病数据集
# 导入岭回归模型# 岭回归在sklearn.linear_model模块中fromsklearn.linear_modelimportRidge# 创建岭回归模型实例# 默认参数:alpha=1.0(正则化强度)ridge=Ridge()# 使用训练数据拟合岭回归模型# fit方法会计算带正则化的最小二乘解ridge.fit(X_train,y_train)# 评估模型性能print("训练集R²分数:",ridge.score(X_train,y_train))print("测试集R²分数:",ridge.score(X_test,y_test))训练集R²分数: 0.43263658371006664 测试集R²分数: 0.4325221097526063岭回归参数调节
# 岭回归的参数调节# alpha参数控制正则化强度:# - alpha=0:等同于普通线性回归(无正则化)# - alpha→∞:所有系数趋近于0,模型变成常数模型fromsklearn.linear_modelimportRidge# 创建alpha=10的岭回归模型(较强正则化)ridge10=Ridge(alpha=10)# 训练模型ridge10.fit(X_train,y_train)# 评估模型性能print("训练集R²分数:",ridge10.score(X_train,y_train))print("测试集R²分数:",ridge10.score(X_test,y_test))# 观察现象:测试集分数 > 训练集分数# 这表明正则化有效减少了过拟合训练集R²分数: 0.15119902171779698 测试集R²分数: 0.16202035231681033# 导入岭回归模型fromsklearn.linear_modelimportRidge# 创建弱正则化的岭回归模型,alpha=0.1# alpha=0.1表示轻度惩罚大系数,但比线性回归的alpha=0强一些ridge01=Ridge(alpha=0.1)# 训练模型ridge01.fit(X_train,y_train)# 评估模型性能print(ridge01.score(X_train,y_train))# 训练集R²分数print(ridge01.score(X_test,y_test))# 测试集R²分数# 注释中的关键观察:# 1. 训练集得分相比线性回归降低# 2. 测试集得分相比线性回归上升# 这表明即使很小的正则化也能改善泛化性能0.521563232713 0.47340124798816186线性回归 vs 弱正则化岭回归的对比
importmatplotlib.pyplotasplt# 1. 创建不同alpha值的模型进行比较fromsklearn.linear_modelimportLinearRegression# 线性回归(alpha=0)lr=LinearRegression().fit(X_train,y_train)lr_train_score=lr.score(X_train,y_train)lr_test_score=lr.score(X_test,y_test)# 弱岭回归(alpha=0.1)ridge01=Ridge(alpha=0.1).fit(X_train,y_train)ridge01_train_score=ridge01.score(X_train,y_train)ridge01_test_score=ridge01.score(X_test,y_test)# 默认岭回归(alpha=1.0)ridge1=Ridge(alpha=1.0).fit(X_train,y_train)ridge1_train_score=ridge1.score(X_train,y_train)ridge1_test_score=ridge1.score(X_test,y_test)# 2. 可视化对比plt.figure(figsize=(12,5))# 子图1:性能对比plt.subplot(1,2,1)models=['linear Regression\n(alpha=0)','Ridge\n(alpha=0.1)','Ridge\n(alpha=1.0)']train_scores=[lr_train_score,ridge01_train_score,ridge1_train_score]test_scores=[lr_test_score,ridge01_test_score,ridge1_test_score]x=np.arange(len(models))width=0.35plt.bar(x-width/2,train_scores,width,label='training_R²',color='skyblue',alpha=0.8)plt.bar(x+width/2,test_scores,width,label='test_R²',color='lightcoral',alpha=0.8)plt.xlabel('model')plt.ylabel('R²')plt.title('scores')plt.xticks(x,models)plt.legend()plt.grid(True,alpha=0.3,axis='y')# 添加数值标签fori,(train,test)inenumerate(zip(train_scores,test_scores)):plt.text(i-width/2,train+0.01,f'{train:.3f}',ha='center',va='bottom',fontsize=9)plt.text(i+width/2,test+0.01,f'{test:.3f}',ha='center',va='bottom',fontsize=9)# 子图2:泛化差距(过拟合程度)plt.subplot(1,2,2)generalization_gap=[train-testfortrain,testinzip(train_scores,test_scores)]plt.bar(models,generalization_gap,color=['steelblue','orange','green'],alpha=0.7)plt.axhline(y=0,color='gray',linestyle='--',linewidth=0.8)plt.xlabel('model')plt.ylabel('training_R² - test_R²')plt.title('generalization gap')plt.grid(True,alpha=0.3,axis='y')# 添加数值标签fori,gapinenumerate(generalization_gap):plt.text(i,gap+(0.005ifgap>0else-0.015),f'{gap:.3f}',ha='center',va='bottom'ifgap>0else'top',fontsize=9)plt.tight_layout()plt.show()# 3. 分析系数稳定性print("\n"+"="*60)print("系数稳定性分析(弱正则化的优势):")print("="*60)# 计算系数变化coef_lr=lr.coef_ coef_ridge01=ridge01.coef_ coef_change=np.abs(coef_ridge01-coef_lr)/np.abs(coef_lr)*100print("\n线性回归 vs 岭回归(alpha=0.1)系数对比:")fori,(lr_coef,ridge_coef,change)inenumerate(zip(coef_lr,coef_ridge01,coef_change)):print(f"特征{i}: 线性回归={lr_coef:7.2f}, 岭回归={ridge_coef:7.2f}, "f"变化={change:6.1f}%")# 总结分析print("\n"+"="*60)print("关键发现:")print("="*60)print(f"1. 训练集性能轻微下降:{lr_train_score:.4f}→{ridge01_train_score:.4f}")print(f" 下降幅度:{(lr_train_score-ridge01_train_score)/lr_train_score*100:.1f}%")print()print(f"2. 测试集性能提升:{lr_test_score:.4f}→{ridge01_test_score:.4f}")print(f" 提升幅度:{(ridge01_test_score-lr_test_score)/lr_test_score*100:.1f}%")print()print(f"3. 泛化差距缩小:{lr_train_score-lr_test_score:.4f}→{ridge01_train_score-ridge01_test_score:.4f}")print(f" 缩小幅度:{((lr_train_score-lr_test_score)-(ridge01_train_score-ridge01_test_score))/(lr_train_score-lr_test_score)*100:.1f}%")============================================================ 系数稳定性分析(弱正则化的优势): ============================================================ 线性回归 vs 岭回归(alpha=0.1)系数对比: 特征0: 线性回归= 11.51, 岭回归= 24.78, 变化= 115.2% 特征1: 线性回归=-282.51, 岭回归=-228.33, 变化= 19.2% 特征2: 线性回归= 534.21, 岭回归= 495.55, 变化= 7.2% 特征3: 线性回归= 401.73, 岭回归= 361.21, 变化= 10.1% 特征4: 线性回归=-1043.90, 岭回归=-109.83, 变化= 89.5% 特征5: 线性回归= 634.93, 岭回归= -78.33, 变化= 112.3% 特征6: 线性回归= 186.44, 岭回归=-190.70, 变化= 202.3% 特征7: 线性回归= 204.94, 岭回归= 108.24, 变化= 47.2% 特征8: 线性回归= 762.46, 岭回归= 383.72, 变化= 49.7% 特征9: 线性回归= 91.95, 岭回归= 107.43, 变化= 16.8% ============================================================ 关键发现: ============================================================ 1. 训练集性能轻微下降: 0.5304 → 0.5216 下降幅度: 1.7% 2. 测试集性能提升: 0.4593 → 0.4734 提升幅度: 3.1% 3. 泛化差距缩小: 0.0710 → 0.0482 缩小幅度: 32.2%三、套索回归——使用L1正则化的线性模型
L1正则化,一部分特征值的系数会正好等于零,即一些特征会彻底被模型忽略掉。
使用套索回归预测糖尿病数据集
# L1正则化的线性模型——套索回归(Lasso)# Lasso回归的正则化会使一部分特征的系数正好等于0# 这意味着Lasso可以自动进行特征选择fromsklearn.linear_modelimportLasso# 创建Lasso回归模型实例# 默认参数:alpha=1.0(正则化强度)lasso=Lasso()# 使用训练数据拟合模型lasso.fit(X_train,y_train)# 评估模型性能print("训练集R²分数:",lasso.score(X_train,y_train))print("测试集R²分数:",lasso.score(X_test,y_test))# 统计非零系数的数量print("非零系数的特征数量:",np.sum(lasso.coef_!=0))# 这个数字通常小于总特征数,因为Lasso会使一些系数变为0训练集R²分数: 0.3624222204154226 测试集R²分数: 0.36561940472905163 非零系数的特征数量: 3套索回归的参数调节
# alpha参数对Lasso的影响比岭回归更明显# 需要调整alpha以获得最佳性能# 1. 测试不同alpha值alphas=np.logspace(-4,2,20)# 从10^-4到10^2train_scores=[]test_scores=[]non_zero_counts=[]foralphainalphas:lasso_temp=Lasso(alpha=alpha,max_iter=10000)# 增加迭代次数确保收敛lasso_temp.fit(X_train,y_train)train_scores.append(lasso_temp.score(X_train,y_train))test_scores.append(lasso_temp.score(X_test,y_test))non_zero_counts.append(np.sum(lasso_temp.coef_!=0))# 2. 可视化结果fig,axes=plt.subplots(1,3,figsize=(15,4))# 图1:性能 vs alphaax1=axes[0]ax1.plot(alphas,train_scores,'b-',label='training_R²',linewidth=2)ax1.plot(alphas,test_scores,'r--',label='test_R²',linewidth=2)ax1.axvline(x=1,color='gray',linestyle=':',label='alpha=1(default)')ax1.set_xscale('log')ax1.set_xlabel('alpha')ax1.set_ylabel('R²')ax1.set_title('Lasso scores vs alpha value')ax1.legend()ax1.grid(True,alpha=0.3)# 图2:非零系数数量 vs alphaax2=axes[1]ax2.plot(alphas,non_zero_counts,'g-',linewidth=2)ax2.axvline(x=1,color='gray',linestyle=':',label='alpha=1')ax2.set_xscale('log')ax2.set_xlabel('alpha')ax2.set_ylabel('Feature Count')ax2.set_title('Feature Count vs alpha value')ax2.legend()ax2.grid(True,alpha=0.3)# 图3:找到最佳alphabest_alpha_idx=np.argmax(test_scores)best_alpha=alphas[best_alpha_idx]ax3=axes[2]ax3.axvline(x=best_alpha,color='red',linestyle='--',label=f'best alpha={best_alpha:.4f}')ax3.text(best_alpha,max(test_scores)*0.9,f'best alpha\n{best_alpha:.4f}',ha='center',va='top')ax3.plot(alphas,test_scores,'r--',linewidth=2)ax3.set_xscale('log')ax3.set_xlabel('alpha')ax3.set_ylabel('test_R²')ax3.set_title('best alpha')ax3.legend()ax3.grid(True,alpha=0.3)plt.tight_layout()plt.show()print(f"\n最佳alpha值:{best_alpha:.4f}")print(f"此时测试集R²:{test_scores[best_alpha_idx]:.4f}")print(f"此时非零系数数量:{non_zero_counts[best_alpha_idx]}")最佳alpha值: 0.1438 此时测试集R²: 0.4822 此时非零系数数量: 7Lasso回归的应用场景
- 高维数据:特征数量远大于样本数量时
- 特征选择:需要识别重要特征时
- 模型简化:需要简单、可解释的模型时
- 多重共线性:特征高度相关时(注意可能的不稳定性)
在实践中,岭回归是两个模型中的优选。如果特征过多,而其中只有一小部分是真正重要的,套索回归就是更好的选择。scikit-learn还提供了弹性网模型(Elastic Net)。弹性网模型总和了套索回归和岭回归的惩罚因子,表现更好,代价是需要调整L1正则化参数和L2正则化参数。