
👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕NumPy这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
- 📊 Python NumPy实战:实现简单的线性回归模型
-
- 🔍 什么是线性回归?
-
- 线性回归的基本形式
- 🧮 数学原理详解
- 🛠️ 基础环境搭建
- 🏗️ 构建线性回归类
- 📈 创建模拟数据集
- 🎯 模型训练与评估
- 📊 结果可视化
- 🧪 多元线性回归扩展
- 🌐 多特征数据集测试
- 🔬 模型诊断与验证
- 📚 交叉验证实现
- ⚡ 性能优化技巧
- 📊 性能比较实验
- 🎨 高级可视化功能
- 📚 实际应用案例
- 🔗 相关资源推荐
- 🧠 深入理解线性回归
-
- 优势
- 局限性
- 应用场景
- 🚀 进一步优化方向
-
- 正则化技术
- 特征缩放
- 📝 总结与展望
-
- 关键收获
- 未来发展方向
📊 Python NumPy实战:实现简单的线性回归模型
在机器学习的世界里,线性回归是最基础也是最重要的算法之一。它不仅是理解更复杂算法的基石,更是数据分析和预测建模的重要工具。今天,我们将使用Python的NumPy库从零开始实现一个简单的线性回归模型,深入理解其背后的数学原理和实现细节。
🔍 什么是线性回归?
线性回归是一种用于建立因变量(目标值)与一个或多个自变量(特征)之间线性关系的统计方法。它的核心思想是找到一条最佳拟合直线,使得预测值与实际值之间的误差最小化。
线性回归的基本形式
对于简单线性回归(只有一个特征),其数学表达式为:
y
=
θ
0
+
θ
1
x
y = \\theta_0 + \\theta_1 x
y=θ0+θ1x
其中:
-
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(θ)=2m1i=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构建高效的机器学习模型。
关键收获
未来发展方向
线性回归虽然是最简单的机器学习算法之一,但它为我们打开了一扇通往数据科学世界的大门。希望这篇教程能够帮助你建立起对机器学习的直观认识,并激发你进一步探索的兴趣!
记住,最好的学习方式就是动手实践。不妨尝试修改我们的代码,添加新的功能,或者将其应用到你感兴趣的实际问题中去。机器学习的魅力就在于此——它不仅仅是理论,更是解决实际问题的强大工具。
继续你的机器学习之旅吧!数据的世界等待着你的探索! 🚀🔬📊
🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨




