欢迎光临
我们一直在努力

Python NumPy - 实战 实现简单的线性回归模型

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕NumPy这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • 📊 Python NumPy实战:实现简单的线性回归模型
    • 🔍 什么是线性回归?
      • 线性回归的基本形式
    • 🧮 数学原理详解
    • 🛠️ 基础环境搭建
    • 🏗️ 构建线性回归类
    • 📈 创建模拟数据集
    • 🎯 模型训练与评估
    • 📊 结果可视化
    • 🧪 多元线性回归扩展
    • 🌐 多特征数据集测试
    • 🔬 模型诊断与验证
    • 📚 交叉验证实现
    • ⚡ 性能优化技巧
    • 📊 性能比较实验
    • 🎨 高级可视化功能
    • 📚 实际应用案例
    • 🔗 相关资源推荐
    • 🧠 深入理解线性回归
      • 优势
      • 局限性
      • 应用场景
    • 🚀 进一步优化方向
      • 正则化技术
      • 特征缩放
    • 📝 总结与展望
      • 关键收获
      • 未来发展方向

📊 Python NumPy实战:实现简单的线性回归模型

在机器学习的世界里,线性回归是最基础也是最重要的算法之一。它不仅是理解更复杂算法的基石,更是数据分析和预测建模的重要工具。今天,我们将使用Python的NumPy库从零开始实现一个简单的线性回归模型,深入理解其背后的数学原理和实现细节。

🔍 什么是线性回归?

线性回归是一种用于建立因变量(目标值)与一个或多个自变量(特征)之间线性关系的统计方法。它的核心思想是找到一条最佳拟合直线,使得预测值与实际值之间的误差最小化。

线性回归的基本形式

对于简单线性回归(只有一个特征),其数学表达式为:

y

=

θ

0

+

θ

1

x

y = \\theta_0 + \\theta_1 x

y=θ0​+θ1​x

其中:

  • y

    y

    y 是目标值(因变量)

  • x

    x

    x 是特征值(自变量)

  • θ

    0

    \\theta_0

    θ0​ 是截距项

  • θ

    1

    \\theta_1

    θ1​ 是斜率参数

对于多元线性回归(多个特征),其矩阵形式为:

y

=

X

θ

\\mathbf{y} = \\mathbf{X}\\boldsymbol{\\theta}

y=Xθ

🧮 数学原理详解

在实现之前,让我们先理解线性回归的核心数学原理。线性回归的目标是最小化损失函数,通常使用均方误差(MSE)作为损失函数:

J

(

θ

)

=

1

2

m

∑

i

=

1

m

(

h

θ

(

x

(

i

)

)

−

y

(

i

)

)

2

J(\\boldsymbol{\\theta}) = \\frac{1}{2m}\\sum_{i=1}^{m}(h_\\theta(x^{(i)}) – y^{(i)})^2

J(θ)=2m1​i=1∑m​(hθ​(x(i))−y(i))2

其中:

  • m

    m

    m 是训练样本的数量

  • h

    θ

    (

    x

    )

    h_\\theta(x)

    hθ​(x) 是假设函数(预测函数)

  • y

    (

    i

    )

    y^{(i)}

    y(i) 是第

    i

    i

    i个样本的真实值

为了找到最优参数,我们需要对损失函数求导并令其等于零。这可以通过正规方程(Normal Equation)来解决:

θ

=

(

X

T

X

)

−

1

X

T

y

\\boldsymbol{\\theta} = (\\mathbf{X}^T\\mathbf{X})^{-1}\\mathbf{X}^T\\mathbf{y}

θ=(XTX)−1XTy

import numpy as np
import matplotlib.pyplot as plt

# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

print("📚 NumPy线性回归模型实现准备就绪!")

🛠️ 基础环境搭建

在开始编码之前,我们需要确保安装了必要的库。除了NumPy,我们还需要matplotlib来进行可视化展示。

pip install numpy matplotlib

现在让我们创建一个完整的线性回归类,包含数据预处理、模型训练、预测等功能。

🏗️ 构建线性回归类

class LinearRegression:
"""
简单线性回归模型实现

参数:
learning_rate: 学习率,默认为0.01
n_iterations: 迭代次数,默认为1000
fit_intercept: 是否拟合截距项,默认为True
"""

def __init__(self, learning_rate=0.01, n_iterations=1000, fit_intercept=True):
self.learning_rate = learning_rate
self.n_iterations = n_iterations
self.fit_intercept = fit_intercept
self.weights = None
self.bias = None
self.cost_history = []

def _add_intercept(self, X):
"""添加截距项"""
intercept = np.ones((X.shape[0], 1))
return np.concatenate((intercept, X), axis=1)

def _compute_cost(self, X, y, weights):
"""计算成本函数"""
m = len(y)
predictions = X.dot(weights)
cost = (1/(2*m)) * np.sum(np.square(predictions – y))
return cost

def fit(self, X, y):
"""
训练线性回归模型

参数:
X: 特征矩阵 (m, n)
y: 目标向量 (m,)
"""
# 处理输入数据
if len(X.shape) == 1:
X = X.reshape(–1, 1)

# 添加截距项
if self.fit_intercept:
X = self._add_intercept(X)

# 初始化参数
self.weights = np.zeros(X.shape[1])
m = len(y)

# 梯度下降优化
for i in range(self.n_iterations):
# 预测
predictions = X.dot(self.weights)

# 计算梯度
errors = predictions – y
gradient = (1/m) * X.T.dot(errors)

# 更新参数
self.weights -= self.learning_rate * gradient

# 记录成本
cost = self._compute_cost(X, y, self.weights)
self.cost_history.append(cost)

# 分离权重和偏置
if self.fit_intercept:
self.bias = self.weights[0]
self.weights = self.weights[1:]
else:
self.bias = 0

print(f"✅ 模型训练完成!最终成本: {cost:.6f}")

def predict(self, X):
"""
进行预测

参数:
X: 特征矩阵 (m, n)

返回:
预测结果 (m,)
"""
if len(X.shape) == 1:
X = X.reshape(–1, 1)

return X.dot(self.weights) + self.bias

def score(self, X, y):
"""
计算R²决定系数

参数:
X: 特征矩阵
y: 真实值

返回:
R²分数
"""
y_pred = self.predict(X)
ss_res = np.sum((y – y_pred) ** 2)
ss_tot = np.sum((y – np.mean(y)) ** 2)
r2 = 1 – (ss_res / ss_tot)
return r2

print("🚀 线性回归类定义完成!")

📈 创建模拟数据集

为了测试我们的模型,让我们创建一些模拟数据。这些数据将遵循一定的线性关系,并添加一些噪声以模拟真实世界的情况。

def generate_linear_data(n_samples=100, noise_level=10):
"""
生成线性关系的数据集

参数:
n_samples: 样本数量
noise_level: 噪声水平

返回:
X: 特征矩阵
y: 目标向量
"""
# 生成随机特征
np.random.seed(42) # 固定随机种子以保证结果可重现
X = np.random.randn(n_samples, 1) * 50

# 定义真实的线性关系 y = 3*x + 10 + 噪声
true_weights = 3
true_bias = 10
noise = np.random.randn(n_samples) * noise_level
y = true_weights * X.flatten() + true_bias + noise

return X, y

# 生成训练数据
X_train, y_train = generate_linear_data(100, 15)

print(f"📊 训练数据形状: X={X_train.shape}, y={y_train.shape}")
print(f"📈 数据范围: X=[{X_train.min():.2f}, {X_train.max():.2f}], y=[{y_train.min():.2f}, {y_train.max():.2f}]")

# 可视化原始数据
plt.figure(figsize=(10, 6))
plt.scatter(X_train, y_train, alpha=0.7, color='blue')
plt.xlabel('特征值 X')
plt.ylabel('目标值 y')
plt.title('原始训练数据分布')
plt.grid(True, alpha=0.3)
plt.show()

🎯 模型训练与评估

现在让我们使用创建的线性回归模型来训练数据,并评估其性能。

# 创建并训练模型
model = LinearRegression(learning_rate=0.001, n_iterations=2000)
model.fit(X_train, y_train)

# 进行预测
y_pred = model.predict(X_train)

# 评估模型性能
r2_score = model.score(X_train, y_train)
print(f"🎯 模型R²得分: {r2_score:.4f}")

# 显示模型参数
print(f"📊 模型参数:")
print(f" 权重 (斜率): {model.weights[0]:.4f}")
print(f" 偏置 (截距): {model.bias:.4f}")

#mermaid-svg-oSzzvln7BafT6tgG{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oSzzvln7BafT6tgG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oSzzvln7BafT6tgG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oSzzvln7BafT6tgG .error-icon{fill:#552222;}#mermaid-svg-oSzzvln7BafT6tgG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oSzzvln7BafT6tgG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oSzzvln7BafT6tgG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oSzzvln7BafT6tgG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oSzzvln7BafT6tgG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oSzzvln7BafT6tgG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oSzzvln7BafT6tgG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oSzzvln7BafT6tgG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oSzzvln7BafT6tgG .marker.cross{stroke:#333333;}#mermaid-svg-oSzzvln7BafT6tgG svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oSzzvln7BafT6tgG p{margin:0;}#mermaid-svg-oSzzvln7BafT6tgG .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-oSzzvln7BafT6tgG .cluster-label text{fill:#333;}#mermaid-svg-oSzzvln7BafT6tgG .cluster-label span{color:#333;}#mermaid-svg-oSzzvln7BafT6tgG .cluster-label span p{background-color:transparent;}#mermaid-svg-oSzzvln7BafT6tgG .label text,#mermaid-svg-oSzzvln7BafT6tgG span{fill:#333;color:#333;}#mermaid-svg-oSzzvln7BafT6tgG .node rect,#mermaid-svg-oSzzvln7BafT6tgG .node circle,#mermaid-svg-oSzzvln7BafT6tgG .node ellipse,#mermaid-svg-oSzzvln7BafT6tgG .node polygon,#mermaid-svg-oSzzvln7BafT6tgG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oSzzvln7BafT6tgG .rough-node .label text,#mermaid-svg-oSzzvln7BafT6tgG .node .label text,#mermaid-svg-oSzzvln7BafT6tgG .image-shape .label,#mermaid-svg-oSzzvln7BafT6tgG .icon-shape .label{text-anchor:middle;}#mermaid-svg-oSzzvln7BafT6tgG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oSzzvln7BafT6tgG .rough-node .label,#mermaid-svg-oSzzvln7BafT6tgG .node .label,#mermaid-svg-oSzzvln7BafT6tgG .image-shape .label,#mermaid-svg-oSzzvln7BafT6tgG .icon-shape .label{text-align:center;}#mermaid-svg-oSzzvln7BafT6tgG .node.clickable{cursor:pointer;}#mermaid-svg-oSzzvln7BafT6tgG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oSzzvln7BafT6tgG .arrowheadPath{fill:#333333;}#mermaid-svg-oSzzvln7BafT6tgG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oSzzvln7BafT6tgG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oSzzvln7BafT6tgG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oSzzvln7BafT6tgG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oSzzvln7BafT6tgG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oSzzvln7BafT6tgG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oSzzvln7BafT6tgG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oSzzvln7BafT6tgG .cluster text{fill:#333;}#mermaid-svg-oSzzvln7BafT6tgG .cluster span{color:#333;}#mermaid-svg-oSzzvln7BafT6tgG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oSzzvln7BafT6tgG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oSzzvln7BafT6tgG rect.text{fill:none;stroke-width:0;}#mermaid-svg-oSzzvln7BafT6tgG .icon-shape,#mermaid-svg-oSzzvln7BafT6tgG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oSzzvln7BafT6tgG .icon-shape p,#mermaid-svg-oSzzvln7BafT6tgG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oSzzvln7BafT6tgG .icon-shape .label rect,#mermaid-svg-oSzzvln7BafT6tgG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oSzzvln7BafT6tgG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oSzzvln7BafT6tgG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oSzzvln7BafT6tgG :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

否

是

开始

初始化参数

前向传播计算预测值

计算损失函数

反向传播计算梯度

更新参数

达到迭代次数?

结束训练

📊 结果可视化

让我们通过可视化来更好地理解模型的学习过程和预测效果。

# 绘制训练成本变化
plt.figure(figsize=(12, 5))

# 成本函数收敛图
plt.subplot(1, 2, 1)
plt.plot(model.cost_history)
plt.xlabel('迭代次数')
plt.ylabel('成本函数值')
plt.title('训练过程中的成本变化')
plt.grid(True, alpha=0.3)

# 预测结果对比
plt.subplot(1, 2, 2)
plt.scatter(X_train, y_train, alpha=0.7, color='blue', label='真实数据')
plt.plot(X_train, y_pred, color='red', linewidth=2, label='预测结果')
plt.xlabel('特征值 X')
plt.ylabel('目标值 y')
plt.title('线性回归预测结果')
plt.legend()
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 打印详细信息
print(f"\\n🔍 模型详情:")
print(f" 训练样本数: {len(X_train)}")
print(f" 最终成本: {model.cost_history[–1]:.6f}")
print(f" 训练迭代次数: {len(model.cost_history)}")

🧪 多元线性回归扩展

虽然我们已经实现了简单线性回归,但现实世界中的问题往往涉及多个特征。让我们扩展我们的模型以支持多元线性回归。

class MultipleLinearRegression:
"""
多元线性回归模型
"""

def __init__(self, fit_intercept=True):
self.fit_intercept = fit_intercept
self.weights = None
self.bias = None

def _add_intercept(self, X):
"""添加截距项"""
intercept = np.ones((X.shape[0], 1))
return np.concatenate((intercept, X), axis=1)

def fit(self, X, y):
"""
使用正规方程训练模型

参数:
X: 特征矩阵 (m, n)
y: 目标向量 (m,)
"""
# 处理输入数据
if len(X.shape) == 1:
X = X.reshape(–1, 1)

# 添加截距项
if self.fit_intercept:
X_with_intercept = self._add_intercept(X)
else:
X_with_intercept = X

# 使用正规方程求解
# θ = (X^T X)^(-1) X^T y
try:
XtX = X_with_intercept.T.dot(X_with_intercept)
XtX_inv = np.linalg.inv(XtX)
Xty = X_with_intercept.T.dot(y)
theta = XtX_inv.dot(Xty)

if self.fit_intercept:
self.bias = theta[0]
self.weights = theta[1:]
else:
self.bias = 0
self.weights = theta

print("✅ 多元线性回归模型训练完成!")

except np.linalg.LinAlgError:
print("❌ 矩阵不可逆,无法使用正规方程求解")
raise

def predict(self, X):
"""
进行预测

参数:
X: 特征矩阵 (m, n)

返回:
预测结果 (m,)
"""
if len(X.shape) == 1:
X = X.reshape(–1, 1)

return X.dot(self.weights) + self.bias

def score(self, X, y):
"""
计算R²决定系数
"""

y_pred = self.predict(X)
ss_res = np.sum((y – y_pred) ** 2)
ss_tot = np.sum((y – np.mean(y)) ** 2)
r2 = 1 – (ss_res / ss_tot)
return r2

print("🌟 多元线性回归类定义完成!")

🌐 多特征数据集测试

让我们创建一个多特征的数据集来测试我们的多元线性回归模型。

def generate_multivariate_data(n_samples=200, n_features=3):
"""
生成多维线性数据集

参数:
n_samples: 样本数量
n_features: 特征数量

返回:
X: 特征矩阵
y: 目标向量
"""
np.random.seed(42)

# 生成随机特征
X = np.random.randn(n_samples, n_features) * 10

# 定义真实的权重和偏置
true_weights = np.array([2.5, –1.8, 3.2])
true_bias = 15

# 添加噪声
noise = np.random.randn(n_samples) * 5

# 计算目标值
y = X.dot(true_weights) + true_bias + noise

return X, y

# 生成多特征数据
X_multi, y_multi = generate_multivariate_data(200, 3)

print(f"📊 多特征数据集信息:")
print(f" 样本数量: {X_multi.shape[0]}")
print(f" 特征数量: {X_multi.shape[1]}")
print(f" 数据范围: X=[{X_multi.min():.2f}, {X_multi.max():.2f}]")
print(f" 目标范围: y=[{y_multi.min():.2f}, {y_multi.max():.2f}]")

# 训练多元线性回归模型
multi_model = MultipleLinearRegression()
multi_model.fit(X_multi, y_multi)

# 进行预测和评估
y_multi_pred = multi_model.predict(X_multi)
r2_multi = multi_model.score(X_multi, y_multi)

print(f"\\n🎯 多元线性回归结果:")
print(f" R²得分: {r2_multi:.4f}")
print(f" 学习到的权重: {multi_model.weights}")
print(f" 学习到的偏置: {multi_model.bias:.2f}")

🔬 模型诊断与验证

为了确保我们的模型工作正常,让我们进行一些诊断检查。

def plot_residuals(y_true, y_pred, title="残差分析"):
"""
绘制残差图进行模型诊断
"""

residuals = y_true – y_pred

plt.figure(figsize=(12, 4))

# 残差vs预测值
plt.subplot(1, 3, 1)
plt.scatter(y_pred, residuals, alpha=0.7)
plt.axhline(y=0, color='r', linestyle='–')
plt.xlabel('预测值')
plt.ylabel('残差')
plt.title('残差 vs 预测值')
plt.grid(True, alpha=0.3)

# 残差直方图
plt.subplot(1, 3, 2)
plt.hist(residuals, bins=30, alpha=0.7, edgecolor='black')
plt.xlabel('残差')
plt.ylabel('频次')
plt.title('残差分布')
plt.grid(True, alpha=0.3)

# Q-Q图(简化版)
plt.subplot(1, 3, 3)
sorted_residuals = np.sort(residuals)
theoretical_quantiles = np.linspace(0, 1, len(sorted_residuals))
plt.scatter(theoretical_quantiles, sorted_residuals, alpha=0.7)
plt.xlabel('理论分位数')
plt.ylabel('样本分位数')
plt.title('Q-Q图(简化)')
plt.grid(True, alpha=0.3)

plt.suptitle(title)
plt.tight_layout()
plt.show()

# 对简单线性回归进行残差分析
plot_residuals(y_train, y_pred, "简单线性回归残差分析")

# 对多元线性回归进行残差分析
plot_residuals(y_multi, y_multi_pred, "多元线性回归残差分析")

📚 交叉验证实现

为了更好地评估模型的泛化能力,让我们实现k折交叉验证。

def k_fold_cross_validation(model_class, X, y, k=5, **kwargs):
"""
K折交叉验证

参数:
model_class: 模型类
X: 特征矩阵
y: 目标向量
k: 折数
**kwargs: 模型参数

返回:
scores: 各折的得分
"""
n_samples = len(X)
indices = np.arange(n_samples)
np.random.shuffle(indices)

fold_size = n_samples // k
scores = []

for i in range(k):
# 划分训练集和验证集
start_idx = i * fold_size
end_idx = (i + 1) * fold_size if i < k – 1 else n_samples

val_indices = indices[start_idx:end_idx]
train_indices = np.concatenate([indices[:start_idx], indices[end_idx:]])

X_train_fold, X_val_fold = X[train_indices], X[val_indices]
y_train_fold, y_val_fold = y[train_indices], y[val_indices]

# 训练模型
model = model_class(**kwargs)
model.fit(X_train_fold, y_train_fold)

# 评估
score = model.score(X_val_fold, y_val_fold)
scores.append(score)

print(f" 第{i+1}折 R²得分: {score:.4f}")

return np.array(scores)

# 对简单线性回归进行交叉验证
print("🔄 简单线性回归 5折交叉验证:")
simple_scores = k_fold_cross_validation(
LinearRegression,
X_train,
y_train,
k=5,
learning_rate=0.001,
n_iterations=1000
)

print(f"📊 平均R²得分: {simple_scores.mean():.4f} ± {simple_scores.std():.4f}")

# 对多元线性回归进行交叉验证
print("\\n🔄 多元线性回归 5折交叉验证:")
multiple_scores = k_fold_cross_validation(
MultipleLinearRegression,
X_multi,
y_multi,
k=5
)

print(f"📊 平均R²得分: {multiple_scores.mean():.4f} ± {multiple_scores.std():.4f}")

⚡ 性能优化技巧

在实际应用中,我们可能需要处理大规模数据集。让我们探讨一些性能优化的技巧。

class OptimizedLinearRegression:
"""
优化的线性回归实现
"""

def __init__(self, method='gradient_descent', **kwargs):
self.method = method
self.kwargs = kwargs
self.weights = None
self.bias = None

def fit(self, X, y):
"""
根据选择的方法训练模型
"""

if self.method == 'normal_equation':
self._fit_normal_equation(X, y)
elif self.method == 'gradient_descent':
self._fit_gradient_descent(X, y)
else:
raise ValueError("方法必须是 'normal_equation' 或 'gradient_descent'")

def _fit_normal_equation(self, X, y):
"""使用正规方程求解"""
if len(X.shape) == 1:
X = X.reshape(–1, 1)

# 添加截距项
X_with_intercept = np.column_stack([np.ones(X.shape[0]), X])

# 正规方程: θ = (X^T X)^(-1) X^T y
try:
theta = np.linalg.solve(X_with_intercept.T @ X_with_intercept,
X_with_intercept.T @ y)
self.bias = theta[0]
self.weights = theta[1:]
except np.linalg.LinAlgError:
# 如果矩阵奇异,使用伪逆
theta = np.linalg.pinv(X_with_intercept.T @ X_with_intercept) @ X_with_intercept.T @ y
self.bias = theta[0]
self.weights = theta[1:]

def _fit_gradient_descent(self, X, y):
"""使用梯度下降优化"""
if len(X.shape) == 1:
X = X.reshape(–1, 1)

# 获取参数
lr = self.kwargs.get('learning_rate', 0.01)
n_iter = self.kwargs.get('n_iterations', 1000)
batch_size = self.kwargs.get('batch_size', 32)

# 添加截距项
X_with_intercept = np.column_stack([np.ones(X.shape[0]), X])
n_features = X_with_intercept.shape[1]

# 初始化参数
theta = np.random.normal(0, 0.01, n_features)

# 小批量梯度下降
for i in range(n_iter):
# 随机采样小批量
indices = np.random.choice(X.shape[0], size=min(batch_size, X.shape[0]), replace=False)
X_batch = X_with_intercept[indices]
y_batch = y[indices]

# 计算梯度
predictions = X_batch @ theta
errors = predictions – y_batch
gradient = X_batch.T @ errors / len(y_batch)

# 更新参数
theta -= lr * gradient

self.bias = theta[0]
self.weights = theta[1:]

def predict(self, X):
"""预测"""
if len(X.shape) == 1:
X = X.reshape(–1, 1)
return X @ self.weights + self.bias

print("⚡ 优化版本线性回归类定义完成!")

📊 性能比较实验

让我们比较不同实现方式的性能表现。

import time

# 生成大数据集用于性能测试
print("📈 生成大型数据集用于性能测试…")
large_X, large_y = generate_linear_data(10000, 20)

# 测试不同方法的性能
methods_comparison = {
'梯度下降': {'method': 'gradient_descent', 'learning_rate': 0.001, 'n_iterations': 1000},
'正规方程': {'method': 'normal_equation'}
}

results = {}

for name, params in methods_comparison.items():
print(f"⏱️ 测试 {name} 方法…")

# 记录开始时间
start_time = time.time()

# 训练模型
opt_model = OptimizedLinearRegression(**params)
opt_model.fit(large_X.flatten(), large_y)

# 记录结束时间
end_time = time.time()
training_time = end_time – start_time

# 评估性能
y_pred_large = opt_model.predict(large_X.flatten())
r2_score_large = opt_model.score(large_X.flatten(), large_y) if hasattr(opt_model, 'score') else \\
1 – np.sum((large_y – y_pred_large)**2) / np.sum((large_y – np.mean(large_y))**2)

results[name] = {
'training_time': training_time,
'r2_score': r2_score_large,
'weights': opt_model.weights,
'bias': opt_model.bias
}

print(f" 训练时间: {training_time:.4f}秒")
print(f" R²得分: {r2_score_large:.4f}")
print(f" 学习到的参数: 权重={opt_model.weights[0]:.4f}, 偏置={opt_model.bias:.4f}")
print()

# 汇总比较结果
print("🏆 性能比较汇总:")
print("-" * 50)
for name, metrics in results.items():
print(f"{name}:")
print(f" 训练时间: {metrics['training_time']:.4f}秒")
print(f" R²得分: {metrics['r2_score']:.4f}")
print()

🎨 高级可视化功能

为了更好地理解模型的工作原理,让我们添加一些高级可视化功能。

def visualize_learning_process(model, X, y):
"""
可视化学习过程
"""

fig, axes = plt.subplots(2, 2, figsize=(15, 10))

# 1. 数据点和拟合直线
axes[0, 0].scatter(X, y, alpha=0.6, color='blue', label='训练数据')
X_plot = np.linspace(X.min(), X.max(), 100)
y_plot = model.weights[0] * X_plot + model.bias
axes[0, 0].plot(X_plot, y_plot, color='red', linewidth=2, label='拟合直线')
axes[0, 0].set_xlabel('X')
axes[0, 0].set_ylabel('y')
axes[0, 0].set_title('数据拟合结果')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)

# 2. 成本函数收敛过程
axes[0, 1].plot(model.cost_history)
axes[0, 1].set_xlabel('迭代次数')
axes[0, 1].set_ylabel('成本函数值')
axes[0, 1].set_title('成本函数收敛过程')
axes[0, 1].grid(True, alpha=0.3)

# 3. 残差分析
y_pred = model.predict(X)
residuals = y – y_pred
axes[1, 0].scatter(y_pred, residuals, alpha=0.6)
axes[1, 0].axhline(y=0, color='red', linestyle='–')
axes[1, 0].set_xlabel('预测值')
axes[1, 0].set_ylabel('残差')
axes[1, 0].set_title('残差散点图')
axes[1, 0].grid(True, alpha=0.3)

# 4. 预测vs实际值
axes[1, 1].scatter(y, y_pred, alpha=0.6)
min_val = min(y.min(), y_pred.min())
max_val = max(y.max(), y_pred.max())
axes[1, 1].plot([min_val, max_val], [min_val, max_val], 'r–', linewidth=2)
axes[1, 1].set_xlabel('实际值')
axes[1, 1].set_ylabel('预测值')
axes[1, 1].set_title('预测值 vs 实际值')
axes[1, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 可视化学习过程
print("🎨 可视化模型学习过程…")
visualize_learning_process(model, X_train.flatten(), y_train)

📚 实际应用案例

让我们通过一个实际的应用案例来演示线性回归的强大功能。我们将使用房价预测作为例子。

def generate_house_price_data(n_samples=500):
"""
生成房价数据集
"""

np.random.seed(42)

# 房屋面积(平方米)
area = np.random.uniform(50, 300, n_samples)

# 房间数量
rooms = np.random.randint(1, 6, n_samples)

# 地段评分(1-10)
location_score = np.random.uniform(1, 10, n_samples)

# 距离市中心距离(公里)
distance_to_center = np.random.uniform(1, 30, n_samples)

# 真实价格计算公式(单位:万元)
price = (area * 0.8 + # 面积影响
rooms * 15 + # 房间数影响
location_score * 20 + # 地段影响
(30 – distance_to_center) * 2 + # 距离影响
np.random.normal(0, 20, n_samples)) # 噪声

# 确保价格为正数
price = np.maximum(price, 10)

# 组织特征矩阵
X = np.column_stack([area, rooms, location_score, distance_to_center])

return X, price

# 生成房价数据
house_X, house_prices = generate_house_price_data(500)

print("🏠 房价预测数据集信息:")
print(f" 样本数量: {house_X.shape[0]}")
print(f" 特征数量: {house_X.shape[1]}")
print(f" 特征名称: ['面积', '房间数', '地段评分', '距离市中心']")
print(f" 价格范围: [{house_prices.min():.1f}, {house_prices.max():.1f}]万元")

# 使用多元线性回归进行房价预测
house_model = MultipleLinearRegression()
house_model.fit(house_X, house_prices)

# 预测和评估
house_predictions = house_model.predict(house_X)
house_r2 = house_model.score(house_X, house_prices)

print(f"\\n🎯 房价预测模型性能:")
print(f" R²得分: {house_r2:.4f}")
print(f" 各特征权重:")
feature_names = ['面积', '房间数', '地段评分', '距离市中心']
for i, (name, weight) in enumerate(zip(feature_names, house_model.weights)):
print(f" {name}: {weight:.2f}")
print(f" 偏置项: {house_model.bias:.2f}")

# 可视化房价预测结果
plt.figure(figsize=(12, 8))

# 特征重要性
plt.subplot(2, 2, 1)
feature_importance = np.abs(house_model.weights)
bars = plt.bar(range(len(feature_importance)), feature_importance)
plt.xticks(range(len(feature_names)), feature_names, rotation=45)
plt.ylabel('特征重要性(权重绝对值)')
plt.title('各特征的重要性')

# 价格分布
plt.subplot(2, 2, 2)
plt.hist(house_prices, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
plt.xlabel('房价(万元)')
plt.ylabel('频次')
plt.title('房价分布')

# 预测vs实际
plt.subplot(2, 2, 3)
plt.scatter(house_prices, house_predictions, alpha=0.6)
min_price = min(house_prices.min(), house_predictions.min())
max_price = max(house_prices.max(), house_predictions.max())
plt.plot([min_price, max_price], [min_price, max_price], 'r–', linewidth=2)
plt.xlabel('实际房价(万元)')
plt.ylabel('预测房价(万元)')
plt.title('预测房价 vs 实际房价')

# 残差分析
plt.subplot(2, 2, 4)
residuals = house_prices – house_predictions
plt.scatter(house_predictions, residuals, alpha=0.6)
plt.axhline(y=0, color='red', linestyle='–')
plt.xlabel('预测房价(万元)')
plt.ylabel('残差')
plt.title('残差分析')

plt.tight_layout()
plt.show()

🔗 相关资源推荐

在线性回归的学习过程中,以下资源可能会对你有所帮助:

  • Scikit-learn线性回归文档 提供了专业的线性回归实现和详细的数学解释
  • NumPy官方文档 是学习NumPy数组操作和数学函数的最佳资源
  • 机器学习基石课程 Andrew Ng的经典机器学习课程,深入讲解线性回归等基础算法

🧠 深入理解线性回归

线性回归虽然看起来简单,但它蕴含着丰富的数学思想和实用价值。让我们总结一下关键要点:

优势

  • 简单易懂: 模型结构清晰,易于理解和解释
  • 计算高效: 训练和预测速度都非常快
  • 可解释性强: 每个特征的权重都有明确的业务含义
  • 理论基础扎实: 有完善的统计学理论支撑
  • 局限性

  • 假设严格: 假设特征与目标之间存在线性关系
  • 对异常值敏感: 异常值会显著影响模型性能
  • 多重共线性问题: 特征之间高度相关时会影响稳定性
  • 应用场景

  • 销售预测: 基于历史数据预测未来销售额
  • 房价估算: 根据房屋特征预测市场价格
  • 医学研究: 分析各种因素对疾病的影响程度
  • 金融风控: 评估贷款违约风险
  • # 创建一个综合示例来展示线性回归的完整流程
    def comprehensive_example():
    """
    线性回归综合示例
    """

    print("🎓 线性回归综合示例")
    print("=" * 50)

    # 1. 数据生成
    print("📊 步骤1: 生成模拟数据")
    X_comp, y_comp = generate_linear_data(200, 12)

    # 2. 数据探索
    print("🔍 步骤2: 数据探索")
    print(f" 数据形状: X={X_comp.shape}, y={y_comp.shape}")
    print(f" X范围: [{X_comp.min():.2f}, {X_comp.max():.2f}]")
    print(f" y范围: [{y_comp.min():.2f}, {y_comp.max():.2f}]")

    # 3. 模型训练
    print("\\n🧠 步骤3: 模型训练")
    comp_model = LinearRegression(learning_rate=0.001, n_iterations=1500)
    comp_model.fit(X_comp, y_comp)

    # 4. 模型评估
    print("🎯 步骤4: 模型评估")
    comp_pred = comp_model.predict(X_comp)
    comp_r2 = comp_model.score(X_comp, y_comp)
    print(f" R²得分: {comp_r2:.4f}")
    print(f" 模型参数: 权重={comp_model.weights[0]:.4f}, 偏置={comp_model.bias:.4f}")

    # 5. 结果可视化
    print("\\n🎨 步骤5: 结果可视化")
    plt.figure(figsize=(15, 5))

    # 数据和拟合线
    plt.subplot(1, 3, 1)
    plt.scatter(X_comp, y_comp, alpha=0.6, color='blue', label='数据点')
    X_line = np.linspace(X_comp.min(), X_comp.max(), 100)
    y_line = comp_model.weights[0] * X_line + comp_model.bias
    plt.plot(X_line, y_line, color='red', linewidth=2, label='拟合直线')
    plt.xlabel('X')
    plt.ylabel('y')
    plt.title('线性回归拟合结果')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # 成本函数收敛
    plt.subplot(1, 3, 2)
    plt.plot(comp_model.cost_history)
    plt.xlabel('迭代次数')
    plt.ylabel('成本函数值')
    plt.title('训练过程收敛情况')
    plt.grid(True, alpha=0.3)

    # 预测vs实际
    plt.subplot(1, 3, 3)
    plt.scatter(y_comp, comp_pred, alpha=0.6)
    min_val = min(y_comp.min(), comp_pred.min())
    max_val = max(y_comp.max(), comp_pred.max())
    plt.plot([min_val, max_val], [min_val, max_val], 'r–', linewidth=2)
    plt.xlabel('实际值')
    plt.ylabel('预测值')
    plt.title('预测精度分析')
    plt.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

    print(f"\\n✨ 综合示例完成!最终R²得分: {comp_r2:.4f}")
    return comp_model, X_comp, y_comp

    # 运行综合示例
    final_model, final_X, final_y = comprehensive_example()

    🚀 进一步优化方向

    虽然我们已经实现了一个基本的线性回归模型,但在实际应用中还有许多可以改进的地方:

    正则化技术

    class RegularizedLinearRegression:
    """
    带正则化的线性回归(岭回归和Lasso)
    """

    def __init__(self, alpha=1.0, regularization='ridge'):
    self.alpha = alpha
    self.regularization = regularization
    self.weights = None
    self.bias = None

    def fit(self, X, y):
    """训练带正则化的线性回归模型"""
    if len(X.shape) == 1:
    X = X.reshape(–1, 1)

    # 添加截距项
    X_with_intercept = np.column_stack([np.ones(X.shape[0]), X])
    n_features = X_with_intercept.shape[1]

    if self.regularization == 'ridge':
    # 岭回归: θ = (X^T X + αI)^(-1) X^T y
    I = np.eye(n_features)
    I[0, 0] = 0 # 不对偏置项进行正则化
    theta = np.linalg.inv(X_with_intercept.T @ X_with_intercept + self.alpha * I) @ X_with_intercept.T @ y
    elif self.regularization == 'lasso':
    # Lasso回归需要使用坐标下降法,这里简化实现
    print("⚠️ Lasso回归需要更复杂的实现,此处仅作示意")
    theta = np.linalg.inv(X_with_intercept.T @ X_with_intercept + self.alpha * np.eye(n_features)) @ X_with_intercept.T @ y
    else:
    raise ValueError("regularization必须是'ridge'或'lasso'")

    self.bias = theta[0]
    self.weights = theta[1:]

    print("💎 正则化线性回归类定义完成!")

    特征缩放

    def standardize_features(X):
    """
    标准化特征(均值为0,标准差为1)
    """

    mean = np.mean(X, axis=0)
    std = np.std(X, axis=0)
    # 避免除零错误
    std = np.where(std == 0, 1, std)
    return (X – mean) / std, mean, std

    def normalize_features(X):
    """
    归一化特征(0-1缩放)
    """

    min_val = np.min(X, axis=0)
    max_val = np.max(X, axis=0)
    # 避免除零错误
    range_val = np.where(max_val – min_val == 0, 1, max_val – min_val)
    return (X – min_val) / range_val, min_val, range_val

    print("📏 特征缩放函数定义完成!")

    📝 总结与展望

    通过这篇文章,我们从零开始实现了线性回归模型,涵盖了从基础概念到实际应用的全过程。我们不仅掌握了线性回归的数学原理,还学会了如何用NumPy构建高效的机器学习模型。

    关键收获

  • 数学基础: 理解了线性回归的成本函数、梯度下降算法和正规方程求解
  • 编程实践: 掌握了使用NumPy实现机器学习算法的技术细节
  • 模型评估: 学会了使用R²得分、残差分析等方法评估模型性能
  • 工程技巧: 了解了交叉验证、特征缩放等实用的机器学习工程技巧
  • 未来发展方向

  • 深度学习: 线性回归是神经网络的基础,可以进一步学习深度学习
  • 集成学习: 结合多个线性回归模型提升预测性能
  • 贝叶斯方法: 使用贝叶斯推断来处理不确定性
  • 实时学习: 实现在线学习算法适应动态数据环境
  • 线性回归虽然是最简单的机器学习算法之一,但它为我们打开了一扇通往数据科学世界的大门。希望这篇教程能够帮助你建立起对机器学习的直观认识,并激发你进一步探索的兴趣!

    记住,最好的学习方式就是动手实践。不妨尝试修改我们的代码,添加新的功能,或者将其应用到你感兴趣的实际问题中去。机器学习的魅力就在于此——它不仅仅是理论,更是解决实际问题的强大工具。

    继续你的机器学习之旅吧!数据的世界等待着你的探索! 🚀🔬📊


    🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

    赞(0)
    未经允许不得转载:171主机测评 » Python NumPy - 实战 实现简单的线性回归模型
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址