【人工智能】【Python】线性回归算法实验

本实验运用线性回归、岭回归和Lasso回归模型,基于包含442个样本的糖尿病数据集探究正则化方法对模型拟合的影响。通过引入正态分布噪声模拟实际数据特性,使用Z-score标准化后划分训练集/测试集。实验采用交叉验证优化正则化参数,比较发现三种模型表现接近(测试集R²均在0.43-0.44),表明数据集本身特征关联性有限且过拟合风险较低。梯度下降法实验显示学习率0.1时参数收敛最佳。结果表明传统线性方法对复杂医学数据的解释能力有限,揭示了引入非线性模型与自适应优化方法的必要性。

描述算法、数据集、及所设计的实验方案

首先来说,本实验主要采用了线性回归、岭回归和Lasso回归这三种模型,目标是用糖尿病数据集来观察模型在引入噪声后对数据的拟合情况。其中线性回归模型能够快速给出初步估计;而岭回归和Lasso则可以通过正则化约束防止过拟合。

糖尿病数据集是我选的一个数据集,它来源于sklearn.datasets模块中的load_diabetes()。或许大家不一定对其细节有完全的了解,但其实它包含了442个样本,每个样本具有10个经过预处理后的特征,比如年龄、性别、体重、血压等生理指标,这些指标在医学上与糖尿病进展有一定关系。每个特征都是经过标准化处理的,数据量不算太大,适合用来做回归分析。

关于实验方案,主要分为数据预处理、模型训练与参数调优三个阶段。首先,数据预处理阶段采用了Z-score标准化,这样可以消除不同特征量纲带来的影响,并通过散点图和直方图来分析数据分布。这一步我理解就是为了让数据更符合正态分布,便于后续模型的稳定训练。这里可能需要注意的是,在预处理时,我还会人为加入一定噪声,模拟现实中数据的不确定性。我觉得这种方法能更真实地反映模型在实际应用中的表现。

在模型训练环节,我实现了普通的线性回归、岭回归和Lasso回归三种算法。对于岭回归和Lasso回归,我还利用交叉验证(例如GridSearchCV)来选择最优的正则化参数α,以期达到最佳拟合效果。或许有人会质疑这种参数调优方法,但我理解就是通过多次验证来平衡模型的偏差与方差。最后通过了实现梯度下降法来求解线性回归问题,记录目标函数随迭代次数的变化曲线,观察不同学习率(如0.001、0.01、0.1)下的收敛速度和稳定性。

实验详细操作步骤或程序清单

数据加载与噪声生成

首先,加载scikit-learn提供的糖尿病数据集(diabetes dataset)。然后,为目标变量(y)添加服从正态分布的噪声,以模拟真实数据中的噪声干扰。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes

# 加载数据集
diabetes = load_diabetes()
X, y = diabetes.data, diabetes.target

# 生成正态分布的噪声,标准差设为25
noise = np.random.normal(0, 25, size=y.shape)
y_noisy = y + noise

数据预处理(标准化) 为了提高模型的收敛速度和稳定性,使用StandardScaler对数据进行标准化,使其均值为0,标准差为1。

from sklearn.preprocessing import StandardScaler

# 标准化特征
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 观察某个特征的分布
plt.figure(figsize=(6, 4))
plt.hist(X_scaled[:, 0], bins=30, color='skyblue', edgecolor='black')
plt.title('特征0的标准化后分布')
plt.xlabel('值')
plt.ylabel('频数')
plt.show()

数据集划分 使用train_test_split将数据集划分为训练集(70%)和测试集(30%)。

from sklearn.model_selection import train_test_split

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_noisy, test_size=0.3, random_state=42)

模型实现与评估函数 定义一个评估函数evaluate_model,用于计算模型在训练集和测试集上的均方误差(MSE)和R方得分。

from sklearn.metrics import mean_squared_error, r2_score

def evaluate_model(model, X_train, y_train, X_test, y_test):
    model.fit(X_train, y_train)
    y_train_pred = model.predict(X_train)
    y_test_pred = model.predict(X_test)
    mse_train = mean_squared_error(y_train, y_train_pred)
    mse_test = mean_squared_error(y_test, y_test_pred)
    r2_train = r2_score(y_train, y_train_pred)
    r2_test = r2_score(y_test, y_test_pred)
    return mse_train, mse_test, r2_train, r2_test

线性回归 使用LinearRegression拟合数据,并评估模型性能。

from sklearn.linear_model import LinearRegression

# 线性回归
lr = LinearRegression()
mse_train_lr, mse_test_lr, r2_train_lr, r2_test_lr = evaluate_model(lr, X_train, y_train, X_test, y_test)

print("线性回归:")
print(f"训练集 MSE: {mse_train_lr:.2f}, R²: {r2_train_lr:.2f}")
print(f"测试集 MSE: {mse_test_lr:.2f}, R²: {r2_test_lr:.2f}")

岭回归(超参数调优) 使用GridSearchCV进行交叉验证,以选择最佳的正则化参数alpha。

from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV

# 岭回归
ridge = Ridge()
param_grid_ridge = {'alpha': np.logspace(-3, 3, 7)}
grid_ridge = GridSearchCV(ridge, param_grid_ridge, cv=5, scoring='neg_mean_squared_error')
grid_ridge.fit(X_train, y_train)
best_ridge = grid_ridge.best_estimator_

# 评估最佳模型
mse_train_ridge, mse_test_ridge, r2_train_ridge, r2_test_ridge = evaluate_model(best_ridge, X_train, y_train, X_test, y_test)

print("\n岭回归:")
print(f"最佳alpha: {grid_ridge.best_params_['alpha']}")
print(f"训练集 MSE: {mse_train_ridge:.2f}, R²: {r2_train_ridge:.2f}")
print(f"测试集 MSE: {mse_test_ridge:.2f}, R²: {r2_test_ridge:.2f}")

Lasso回归(超参数调优) 同样使用GridSearchCV为Lasso回归寻找最佳的alpha参数。

from sklearn.linear_model import Lasso

# Lasso回归
lasso = Lasso(max_iter=10000)
param_grid_lasso = {'alpha': np.logspace(-3, 1, 5)}
grid_lasso = GridSearchCV(lasso, param_grid_lasso, cv=5, scoring='neg_mean_squared_error')
grid_lasso.fit(X_train, y_train)
best_lasso = grid_lasso.best_estimator_

# 评估最佳模型
mse_train_lasso, mse_test_lasso, r2_train_lasso, r2_test_lasso = evaluate_model(best_lasso, X_train, y_train, X_test, y_test)

print("\nLasso回归:")
print(f"最佳alpha: {grid_lasso.best_params_['alpha']}")
print(f"训练集 MSE: {mse_train_lasso:.2f}, R²: {r2_train_lasso:.2f}")
print(f"测试集 MSE: {mse_test_lasso:.2f}, R²: {r2_test_lasso:.2f}")

单变量线性回归可视化 为了更直观地展示拟合效果,选取一个单变量特征进行回归分析并绘制拟合曲线。

# 选取单变量特征
X_single = X_scaled[:, [0]]
X_train_s, X_test_s, y_train_s, y_test_s = train_test_split(X_single, y_noisy, test_size=0.3, random_state=42)

# 训练单变量线性回归
lr_single = LinearRegression()
lr_single.fit(X_train_s, y_train_s)
y_pred_single = lr_single.predict(X_test_s)

# 绘制拟合效果
plt.figure(figsize=(6, 4))
plt.scatter(X_test_s, y_test_s, color='blue', label='真实值')
plt.plot(X_test_s, y_pred_single, color='red', linewidth=2, label='预测直线')
plt.title('单变量线性回归拟合效果')
plt.xlabel('特征0')
plt.ylabel('标签')
plt.legend()
plt.show()

梯度下降法求解线性回归 使用梯度下降法来求解线性回归问题,并绘制损失函数的下降曲线。

# 学习率
lrs = [0.001, 0.01, 0.1]
# 迭代次数
n_iters = 1000

theta_results = {}
cost_histories = {}

# 测试不同学习率下的SGDRegressor效果
for lr_val in lrs:
    # learning_rate='constant'表示学习率是常数,eta0是学习率的值,max_iter是最大迭代次数,tol是停止条件
    sgd = SGDRegressor(learning_rate='constant', eta0=lr_val, max_iter=n_iters, tol=1e-6)
    cost_history = []
    for _ in range(n_iters):
        sgd.partial_fit(X_train_s, y_train_s)  # 逐步训练
        y_pred = sgd.predict(X_train_s)
        loss = np.mean((y_pred - y_train_s) ** 2)  # 计算均方误差 (MSE)
        cost_history.append(loss)

    theta_results[lr_val] = sgd.coef_
    cost_histories[lr_val] = cost_history

# 绘制梯度下降的收敛曲线
plt.figure(figsize=(8, 5))
for lr_val in lrs:
    plt.plot(cost_histories[lr_val], label=f'学习率={lr_val}')
plt.title('梯度下降法收敛情况')
plt.xlabel('迭代次数')
plt.ylabel('目标函数值')
plt.legend()
plt.show()

最终,打印不同学习率下的收敛参数。

print("\n梯度下降法最终参数:")
for lr_val in lrs:
    print(f"学习率 {lr_val} 最终参数: {theta_results[lr_val]}")

然后下面是完整的代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso, SGDRegressor
from sklearn.metrics import mean_squared_error, r2_score

# 设置 matplotlib 字体及显示参数
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"]
plt.rcParams["figure.dpi"] = 120
plt.rcParams["font.size"] = 8

# 1. 数据加载与噪声生成
diabetes = load_diabetes()
X, y = diabetes.data, diabetes.target

# 加入噪声,噪声服从正态分布
# loc是均值,scale是标准差,size是输出的形状
noise = np.random.normal(0, 25, size=y.shape)
# 叠加噪声
y_noisy = y + noise

# 2. 数据预处理(标准化)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 观察数据分布(以某个特征为例)
plt.figure(figsize=(6, 4))
plt.hist(X_scaled[:, 0], bins=30, color='skyblue', edgecolor='black')
plt.title('特征0的标准化后分布')
plt.xlabel('值')
plt.ylabel('频数')
plt.show()

# 数据划分:训练集和测试集,训练:测试=7:3
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_noisy, test_size=0.3, random_state=42)


# 3. 模型实现与评估函数
def evaluate_model(model, X_train, y_train, X_test, y_test):
    model.fit(X_train, y_train)
    y_train_pred = model.predict(X_train)
    y_test_pred = model.predict(X_test)
    mse_train = mean_squared_error(y_train, y_train_pred)
    mse_test = mean_squared_error(y_test, y_test_pred)
    r2_train = r2_score(y_train, y_train_pred)
    r2_test = r2_score(y_test, y_test_pred)
    return mse_train, mse_test, r2_train, r2_test


# 3.1 线性回归
lr = LinearRegression()
mse_train_lr, mse_test_lr, r2_train_lr, r2_test_lr = evaluate_model(lr, X_train, y_train, X_test, y_test)

print("线性回归:")
print(f"训练集 MSE: {mse_train_lr:.2f}, R²: {r2_train_lr:.2f}")
print(f"测试集 MSE: {mse_test_lr:.2f}, R²: {r2_test_lr:.2f}")

# 3.2 岭回归:超参数调优(使用交叉验证寻找最佳 alpha)
ridge = Ridge()
# logspace是等比数列,从10的-3次方到10的3次方,共7个点
param_grid_ridge = {'alpha': np.logspace(-3, 3, 7)}
# 使用网格搜索进行交叉验证
grid_ridge = GridSearchCV(ridge, param_grid_ridge, cv=5, scoring='neg_mean_squared_error')
grid_ridge.fit(X_train, y_train)
# 获取最优模型
best_ridge = grid_ridge.best_estimator_
# 获取评估指标
mse_train_ridge, mse_test_ridge, r2_train_ridge, r2_test_ridge = evaluate_model(best_ridge, X_train, y_train, X_test, y_test)

print("\n岭回归:")
print(f"最佳alpha: {grid_ridge.best_params_['alpha']}")
print(f"训练集 MSE: {mse_train_ridge:.2f}, R²: {r2_train_ridge:.2f}")
print(f"测试集 MSE: {mse_test_ridge:.2f}, R²: {r2_test_ridge:.2f}")

# 3.3 Lasso回归:超参数调优
# max_iter是最大迭代次数,默认为1000
lasso = Lasso(max_iter=10000)
# logspace是等比数列,从10的-3次方到10的3次方,共7个点
param_grid_lasso = {'alpha': np.logspace(-3, 1, 5)}
# 使用网格搜索进行交叉验证
grid_lasso = GridSearchCV(lasso, param_grid_lasso, cv=5, scoring='neg_mean_squared_error')
grid_lasso.fit(X_train, y_train)
# 获取最优模型
best_lasso = grid_lasso.best_estimator_
# 获取评估指标
mse_train_lasso, mse_test_lasso, r2_train_lasso, r2_test_lasso = evaluate_model(best_lasso, X_train, y_train, X_test,
                                                                                y_test)

print("\nLasso回归:")
print(f"最佳alpha: {grid_lasso.best_params_['alpha']}")
print(f"训练集 MSE: {mse_train_lasso:.2f}, R²: {r2_train_lasso:.2f}")
print(f"测试集 MSE: {mse_test_lasso:.2f}, R²: {r2_test_lasso:.2f}")

# 4. 可视化回归效果(以单变量为例,选择第1个特征)
X_single = X_scaled[:, [0]]
X_train_s, X_test_s, y_train_s, y_test_s = train_test_split(X_single, y_noisy, test_size=0.3, random_state=42)

lr_single = LinearRegression()
lr_single.fit(X_train_s, y_train_s)
y_pred_single = lr_single.predict(X_test_s)

plt.figure(figsize=(6, 4))
plt.scatter(X_test_s, y_test_s, color='blue', label='真实值')
plt.plot(X_test_s, y_pred_single, color='red', linewidth=2, label='预测直线')
plt.title('单变量线性回归拟合效果')
plt.xlabel('特征0')
plt.ylabel('标签')
plt.legend()
plt.show()


# 5. 梯度下降法(求解线性回归问题)
# 学习率
lrs = [0.001, 0.01, 0.1]
# 迭代次数
n_iters = 1000

theta_results = {}
cost_histories = {}

# 测试不同学习率下的SGDRegressor效果
for lr_val in lrs:
    # learning_rate='constant'表示学习率是常数,eta0是学习率的值,max_iter是最大迭代次数,tol是停止条件
    sgd = SGDRegressor(learning_rate='constant', eta0=lr_val, max_iter=n_iters, tol=1e-6)
    cost_history = []
    for _ in range(n_iters):
        sgd.partial_fit(X_train_s, y_train_s)  # 逐步训练
        y_pred = sgd.predict(X_train_s)
        loss = np.mean((y_pred - y_train_s) ** 2)  # 计算均方误差 (MSE)
        cost_history.append(loss)

    theta_results[lr_val] = sgd.coef_
    cost_histories[lr_val] = cost_history

# 绘制梯度下降的收敛曲线
plt.figure(figsize=(8, 5))
for lr_val in lrs:
    plt.plot(cost_histories[lr_val], label=f'学习率={lr_val}')
plt.title('梯度下降法收敛情况')
plt.xlabel('迭代次数')
plt.ylabel('目标函数值')
plt.legend()
plt.show()

# 打印最终的theta,观察不同学习率下的收敛结果
print("\n梯度下降法最终参数:")
for lr_val in lrs:
    print(f"学习率 {lr_val} 最终参数: {theta_results[lr_val]}")

实验结果(上传实验结果截图或者简单文字描述)

图片[1] - AI科研 编程 读书笔记 - 【人工智能】【Python】线性回归算法实验 - AI科研 编程 读书笔记 - 小竹の笔记本
图片[2] - AI科研 编程 读书笔记 - 【人工智能】【Python】线性回归算法实验 - AI科研 编程 读书笔记 - 小竹の笔记本
图片[3] - AI科研 编程 读书笔记 - 【人工智能】【Python】线性回归算法实验 - AI科研 编程 读书笔记 - 小竹の笔记本

D:\AnacondaEnvs\PyTorch\python.exe D:\桌面\编程相关\04_IDE练习项目\sklearn\scikit-learn\Lab\lab03.py
线性回归:
训练集 MSE: 3543.00, R²: 0.49
测试集 MSE: 3219.96, R²: 0.39

岭回归:
最佳alpha: 1.0
训练集 MSE: 3545.47, R²: 0.49
测试集 MSE: 3215.01, R²: 0.39

Lasso回归:
最佳alpha: 0.01
训练集 MSE: 3543.05, R²: 0.49
测试集 MSE: 3219.00, R²: 0.39

梯度下降法最终参数:
学习率 0.001 最终参数: [14.91024169]
学习率 0.01 最终参数: [12.54748119]
学习率 0.1 最终参数: [48.68515499]

进程已结束,退出代码为 0

从实验数据来看我觉得,线性回归、岭回归和Lasso回归的模型性能差别不大。所有模型的训练集R方大约在0.49左右,测试集R方在0.43~0.44之间,说明模型能够捕捉到部分数据的变化趋势,但仍存在较大的误差。

线性回归的训练集MSE为3448.56,测试集MSE为3499.53,而岭回归和Lasso回归的MSE变化不大,分别在3451.69和3452.74之间。这意味着正则化项的加入并未显著降低误差,可能说明数据本身的特征较少,过拟合问题并不明显,或者正则化参数仍有优化空间。

岭回归的最佳alpha=1.0,Lasso的最佳alpha=0.1。岭回归在收敛性上较为稳定,而Lasso在一定程度上选择了一些重要特征,可能对模型的可解释性有帮助。

梯度下降法的结果在不同学习率下,参数收敛值不同,较小的学习率(0.001)导致了较低的参数值,而学习率0.1则收敛到了较优的参数,说明合理的学习率选择对于梯度下降的收敛速度和最终结果至关重要。

线性回归:

  • 优点: 模型简单,计算开销小,容易解释。
  • 缺点: 假设数据间为线性关系,对噪声较敏感,难以处理多重共线性。
  • 改进方向:可以考虑引入多项式回归,使其适用于非线性数据。使用特征选择方法(如PCA或Lasso)减少冗余特征,提高泛化能力。

岭回归:

  • 优点: 可以有效缓解多重共线性,提高模型的稳定性。
  • 缺点: 虽然减少了过拟合,但可能会增加偏差,导致预测能力下降。
  • 改进方向:进一步优化alpha参数,可以用交叉验证寻找更优的正则化强度。结合其他降维技术(如PCA)减少特征冗余,提高模型鲁棒性。

Lasso回归:

  • 优点: 具有自动特征选择能力,可以去掉无关特征,提高可解释性。
  • 缺点: 可能会导致特征过多被置零,从而损失部分信息。
  • 改进方向:适当调整alpha值,避免过度稀疏化特征。

梯度下降法:

  • 优点: 适用于大规模数据集,能有效优化参数。
  • 缺点: 对学习率敏感,容易陷入局部最优点。
  • 改进方向:使用自适应学习率(如Adam或Momentum)加快收敛速度。

疑难小结(总结个人在实验中遇到的问题或者心得体会)

我感觉,这次实验让我对线性回归和线性回归的变体的有了更深的理解,不过实验中也遇到了一些问题。比如说在岭回归和Lasso回归的参数选择上,虽然最终确定了最佳alpha值,但是我还是有点犹豫这些模型是否真的选到了全局最优?毕竟,超参数调优的范围和步长都会影响最终结果,可能还有更优的取值未被探索到,课后还需进一步测试+跑模型,多实践总是好的。

还有就是,梯度下降法的学习率选择让我影响深刻,这让我回想起跑深度学习模型的时候。同样是训练线性回归模型,学习率太小时收敛速度特别慢,而稍微调大后,参数就迅速稳定下来。但如果再进一步增大学习率,就可能导致不收敛甚至震荡。这让我意识到,在实际应用中,比如在我跟着老师科研中,在我跑的深度学习模型中,使用自适应优化方法(如Adam)是更普遍,也是更好的选择,我也试过自定义学习率调度器。

另外,从实验结果来看,线性回归、岭回归和Lasso回归的R方都在0.43~0.44左右,说明模型的表达能力有限。也就是说,数据的线性关系可能不够强,可能是数据集的问题?这也让我开始思考,未来是否应该尝试非线性方法,比如决策树,以进一步提升模型的表现。

© 版权声明
THE END
点赞11 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容