使用 Scikit-learn 实现普通最小二乘法与非负最小二乘法
Scikit-learn 是一个功能强大的机器学习库,它提供了简单易用的接口来实现各种机器学习算法。本文将详细介绍如何使用 Scikit-learn 实现普通最小二乘法(Ordinary Least Squares, OLS)和非负最小二乘法(Non-Negative Least Squares, NNLS)。我们将对两种方法的公式、操作范围、优劣势以及结果进行对比分析。
1. 经典线性回归 LinearRegression()
from sklearn.linear_model import LinearRegressionmodel = LinearRegression()
-
fit_intercept
(默认值:True
):是否计算回归模型的截距。- 如果设为
False
,模型不会计算截距,假设数据已经居中(即目标值y
和输入特征X
的均值为零)。
- 如果设为
-
copy_X
(默认值:True
):是否复制输入数据X
。- 如果设为
True
,算法会创建X
的副本以避免覆盖原始数据。 - 如果设为
False
,输入数据可能会被覆盖以节省内存。
- 如果设为
-
n_jobs
(默认值:None
):指定并行计算时使用的线程数量。-
None
:使用单线程。 -
正整数:指定使用的线程数。
-
-1
:使用所有可用线程。
-
-
positive
(默认值:False
):是否强制线性回归的系数为非负值。- 如果设为
True
,优化过程会在求解时加入非负约束。
- 如果设为
2. 普通最小二乘法
普通最小二乘法是一种经典的线性回归方法,其目标是通过最小化预测值与实际值之间的平方误差来找到最佳的回归系数。
数学公式:
β ^ = ( X T X ) − 1 X T y \hat{\beta} = (X^T X)^{-1} X^T y β^=(XTX)−1XTy
- X X X 是输入特征矩阵,维度为 n × p n \times p n×p;
- y y y 是目标值向量,维度为 n n n;
- β ^ \hat{\beta} β^ 是回归系数向量。
代码示例:
import numpy as np
from sklearn.linear_model import LinearRegression# 生成模拟数据
np.random.seed(42)
X = np.random.rand(100, 1) * 10 # 特征
y = 3 * X.squeeze() + np.random.randn(100) * 2 # 目标值# 创建并训练模型
ols_model = LinearRegression()
ols_model.fit(X, y)# 模型预测
y_pred = ols_model.predict(X)# 输出回归系数
print(f"回归系数:{ols_model.coef_[0]}")
print(f"截距:{ols_model.intercept_}")
回归系数:2.9080453545753935
截距:0.4301923150934961
import seaborn as sns
import matplotlib.pyplot as plt# 设置Seaborn样式
sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})# 图表美化和可视化
plt.figure(figsize=(8, 6))
plt.scatter(X, y, color=sns.color_palette("Blues")[5], label='实际数据', alpha=0.7, edgecolor='k', s=50)
plt.plot(np.sort(X, axis=0), y_pred[np.argsort(X, axis=0)], color='red', label='OLS预测', linewidth=2, linestyle='--')# 设置标题和标签
plt.title('普通最小二乘法回归', fontsize=16, weight='bold')
plt.xlabel('特征 X', fontsize=14)
plt.ylabel('目标 y', fontsize=14)
plt.legend(loc='upper left', fontsize=12, frameon=True, shadow=True, edgecolor='k')# 设置网格线
plt.grid(color='gray', linestyle='--', linewidth=0.8, alpha=0.5)# 显示图表
plt.tight_layout()
plt.show()
3. 非负最小二乘法
非负最小二乘法是一种约束优化方法,要求回归系数必须为非负值。
数学公式:
min β ≥ 0 ∣ ∣ X β − y ∣ ∣ 2 2 \min_{\beta \geq 0} ||X\beta - y||_2^2 β≥0min∣∣Xβ−y∣∣22
3.1 线性趋势数据
代码示例:
import numpy as np
from sklearn.linear_model import LinearRegression# 设置Seaborn样式
sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})# 生成数据
np.random.seed(42)
X = np.linspace(0, 10, 100).reshape(-1, 1) # 特征
y = 2 * X.squeeze() - 5 + np.random.randn(100) * 2 # 添加负值趋势的目标值# 普通最小二乘法
ols_model = LinearRegression()
ols_model.fit(X, y)
y_pred_ols = ols_model.predict(X)# 非负最小二乘法
nnls_model = LinearRegression(positive=True, fit_intercept=False)
nnls_model.fit(X, y)
y_pred_nnls = nnls_model.predict(X)# 打印回归系数
print(f"OLS回归系数:{ols_model.coef_[0]:.3f},截距:{ols_model.intercept_:.3f}")
print(f"NNLS回归系数:{nnls_model.coef_[0]:.3f},截距:{nnls_model.intercept_:.3f}")
OLS回归系数:2.028,截距:-5.346
NNLS回归系数:1.230,截距:0.000
import seaborn as sns
import matplotlib.pyplot as plt# 设置Seaborn样式
sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})# 可视化对比结果
plt.figure(figsize=(12, 8))# 散点图:实际数据
plt.scatter(X, y, color='#1f77b4', label='实际数据', alpha=0.7, edgecolor='k', s=60)# 折线图:OLS 和 NNLS 预测
plt.plot(X, y_pred_ols, color='#d62728', label='OLS预测', linewidth=2.5, linestyle='--')
plt.plot(X, y_pred_nnls, color='#2ca02c', label='NNLS预测', linewidth=2.5, linestyle='-.')# 添加网格线、标题、标签和图例
plt.grid(color='gray', linestyle=':', linewidth=0.5, alpha=0.8)
plt.xlabel('资源使用量(X)', fontsize=14)
plt.ylabel('生产产出(y)', fontsize=14)
plt.title('普通最小二乘法 vs 非负最小二乘法对比分析', fontsize=16, weight='bold')
plt.legend(fontsize=12, loc='best', frameon=True, shadow=True, edgecolor='gray')# 设置刻度字体大小
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)# 设置网格线
plt.grid(color='gray', linestyle='--', linewidth=1, alpha=0.5)# 显示图表
plt.tight_layout()
plt.show()
3.2 高维非负线性数据
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score# 数据集2:高维非负线性数据
np.random.seed(42)
n_samples, n_features = 200, 50
X = np.random.randn(n_samples, n_features) # 随机生成特征
true_coef = 3 * np.random.randn(n_features) # 随机生成真实系数
true_coef[true_coef < 0] = 0 # 使系数非负
y = np.dot(X, true_coef) + 5 * np.random.normal(size=n_samples) # 添加噪声# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=42)# OLS 模型
reg_ols = LinearRegression()
y_pred_ols = reg_ols.fit(X_train, y_train).predict(X_test)
r2_score_ols = r2_score(y_test, y_pred_ols)# NNLS 模型
reg_nnls = LinearRegression(positive=True, fit_intercept=False)
y_pred_nnls = reg_nnls.fit(X_train, y_train).predict(X_test)
r2_score_nnls = r2_score(y_test, y_pred_nnls)# 打印 R² 分数
print(f"OLS R² 分数:{r2_score_ols:.3f}")
print(f"NNLS R² 分数:{r2_score_nnls:.3f}")
OLS R² 分数:0.760
NNLS R² 分数:0.817
# 绘制图表:OLS 和 NNLS 对比
fig, axes = plt.subplots(1, 2, figsize=(14, 6))# 数据集2散点图及预测结果
axes[0].scatter(y_test, y_pred_ols, color='#1f77b4', alpha=0.6, edgecolor='k', s=60, label='OLS预测')
axes[0].scatter(y_test, y_pred_nnls, color='#ff7f0e', alpha=0.6, edgecolor='k', s=60, label='NNLS预测')
axes[0].plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'k--', label='理想预测')
axes[0].set_title("预测结果对比", fontsize=14, weight='bold')
axes[0].set_xlabel("实际值 (y)", fontsize=12)
axes[0].set_ylabel("预测值", fontsize=12)
axes[0].legend(fontsize=10)
axes[0].grid(color='gray', linestyle=':', alpha=0.7)# 数据集2系数对比
coef_ols, coef_nnls = reg_ols.coef_, reg_nnls.coef_
min_val = min(coef_ols.min(), coef_nnls.min())
max_val = max(coef_ols.max(), coef_nnls.max())
axes[1].scatter(coef_ols, coef_nnls, marker='o', color='green', alpha=0.8, label='系数散点')
axes[1].plot([min_val, max_val], [min_val, max_val], ls='--', c='gray', alpha=0.8, label='理想线')
axes[1].set_xlim(min_val - 0.5, max_val + 0.5)
axes[1].set_ylim(min_val - 0.5, max_val + 0.5)
axes[1].set_title("OLS 与 NNLS 系数对比", fontsize=14, weight='bold')
axes[1].set_xlabel("OLS 回归系数", fontsize=12)
axes[1].set_ylabel("NNLS 回归系数", fontsize=12)
axes[1].legend(fontsize=10)
axes[1].grid(color='gray', linestyle=':', alpha=0.7)# 调整布局和展示
plt.tight_layout()
plt.show()
4. 两种方法的操作范围
- 普通最小二乘法(OLS):
- 适用场景:任何线性回归问题。
- 假设条件:允许回归系数为负值。
- 应用领域:预测分析、统计建模、金融分析等。
- 非负最小二乘法(NNLS):
- 适用场景:回归系数具有非负意义的场景,如化学浓度、光谱分析等。
- 假设条件:回归系数必须为非负值。
- 应用领域:生物学、图像处理、经济学建模等。
5. 对比分析
特性 | 普通最小二乘法 (OLS) | 非负最小二乘法 (NNLS) |
---|---|---|
约束条件 | 无 | 回归系数必须非负 |
回归系数 | 可正可负 | 必须非负 |
适用场景 | 适用于大多数线性回归问题 | 需非负权重的回归问题(如物理量建模、概率建模等) |
计算复杂度 | 较低 | 较高(由于约束优化) |