Machine Learning: 深度学习入门PyTorch — 从神经元到多层感知机指南

深度学习让机器自动学特征——不用手动设计,网络自己从数据中发现。

1. 你将学到


2. 一个算法工程师的真实故事

(1) 痛点:线性模型无法捕获特征交互

Bob用线性回归预测销售额,R²只有0.78。他发现"广告费×促销"的交互效应对销售影响很大,但手动添加所有交互项(20个特征→190个交互)既繁琐又容易遗漏。线性模型的假设(y=wx+b)太简单,真实世界的特征交互远比手动设计复杂。

(2) 深度学习的解法

神经网络通过隐藏层自动学习特征交互和非线性变换,无需手动设计。

PYTHON
import torch.nn as nn

model = nn.Sequential(
    nn.Linear(20, 64),
    nn.ReLU(),
    nn.Linear(64, 32),
    nn.ReLU(),
    nn.Linear(32, 1),
)

(3) 收益:MLP自动发现交互,R²提升到0.86

Bob用3层MLP替代线性回归,网络自动学到了"高广告费+促销=销量暴增"的模式,R²从0.78提升到0.86,且无需手动设计交互项。


3. 深度学习vs传统ML

(1) 何时选择深度学习

条件 传统ML(如XGBoost) 深度学习(如MLP/CNN)
数据类型 表格数据 图像/文本/音频/复杂表格
数据量 <100k >100k
特征工程 需要手动设计 自动学习特征
训练时间 分钟-小时 小时-天
硬件需求 CPU够用 建议GPU
可解释性 高(系数/重要性) 低(黑箱)
表格数据效果 通常更好 不一定更好

▶ 示例:PyTorch安装与验证

PYTHON
import torch

print(f"PyTorch version: {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
    print(f"GPU: {torch.cuda.get_device_name(0)}")

# Basic tensor operations
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
print(f"a + b = {a + b}")
print(f"a * b = {a * b}")
print(f"Mean of a: {a.mean()}")

输出:

TEXT 📖 仅展示
# 执行成功

4. PyTorch核心

(1) Tensor操作

▶ 示例:Tensor创建与操作

PYTHON
import torch

# Create tensors
a = torch.zeros(3, 4)
b = torch.ones(2, 3)
c = torch.randn(3, 3)  # Normal distribution
d = torch.arange(0, 10, 2)

# From NumPy
import numpy as np
arr = np.array([1, 2, 3])
t = torch.from_numpy(arr)

# Tensor to NumPy
back = t.numpy()

# GPU tensor
if torch.cuda.is_available():
    gpu_tensor = torch.randn(3, 3).cuda()
    cpu_back = gpu_tensor.cpu()

# Reshape
x = torch.arange(12)
matrix = x.reshape(3, 4)
print(f"Shape: {matrix.shape}")

# Broadcasting
a = torch.ones(3, 1)
b = torch.ones(1, 4)
c = a + b  # (3,4)
print(f"Broadcast result shape: {c.shape}")

输出:

TEXT 📖 仅展示
# 执行成功

(2) autograd自动微分

▶ 示例:自动求导

PYTHON
import torch

# Auto-differentiation example
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1  # y = x² + 3x + 1
y.backward()             # dy/dx = 2x + 3

print(f"x = {x.item()}")
print(f"y = {y.item()}")
print(f"dy/dx = {x.grad.item()}")  # Should be 2*2+3 = 7
print(f"Manual check: 2*2 + 3 = {2*2+3}")

输出:

TEXT 📖 仅展示
# 执行成功

(3) nn.Module模型定义

▶ 示例:自定义MLP模型

PYTHON
import torch
import torch.nn as nn

class SalesPredictMLP(nn.Module):
    def __init__(self, input_dim, hidden_dims, output_dim=1):
        super().__init__()
        layers = []
        prev_dim = input_dim
        for hidden_dim in hidden_dims:
            layers.append(nn.Linear(prev_dim, hidden_dim))
            layers.append(nn.BatchNorm1d(hidden_dim))
            layers.append(nn.ReLU())
            layers.append(nn.Dropout(0.2))
            prev_dim = hidden_dim
        layers.append(nn.Linear(prev_dim, output_dim))
        self.network = nn.Sequential(*layers)

    def forward(self, x):
        return self.network(x)

model = SalesPredictMLP(input_dim=20, hidden_dims=[64, 32], output_dim=1)
print(model)
print(f"Total parameters: {sum(p.numel() for p in model.parameters()):,}")

输出:

TEXT 📖 仅展示
# 函数定义成功

5. MLP训练完整流程

(1) 训练循环

100%
sequenceDiagram
    participant Data as DataLoader
    participant Fwd as Forward Pass
    participant Loss as Loss Compute
    participant Zero as Zero Gradients
    participant Bwd as Backward Pass
    participant Step as Optimizer Step

    loop Each Epoch
        Data->>Fwd: Batch of (X, y)
        Fwd->>Loss: y_pred = model(X)
        Loss->>Zero: loss = criterion(y_pred, y)
        Zero->>Bwd: optimizer.zero_grad()
        Bwd->>Step: loss.backward()
        Step->>Data: optimizer.step()
    end

▶ 示例:完整MLP训练流程

PYTHON
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_absolute_error
import numpy as np

# Generate data
rng = np.random.default_rng(42)
n = 5000
X = rng.uniform(0, 100, (n, 20))
y = (50 + X[:, :5] @ [0.8, 1.2, -0.5, 0.3, 0.1]
     + 0.5 * X[:, 0] * X[:, 1]  # Interaction
     + np.sin(X[:, 2]) * 5       # Non-linear
     + rng.normal(0, 5, n))

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)

# Create DataLoaders
train_ds = TensorDataset(torch.FloatTensor(X_train_s), torch.FloatTensor(y_train))
test_ds = TensorDataset(torch.FloatTensor(X_test_s), torch.FloatTensor(y_test))
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=256)

# Model, loss, optimizer
model = SalesPredictMLP(input_dim=20, hidden_dims=[128, 64, 32])
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, patience=10, factor=0.5)

# Training loop
train_losses, val_losses = [], []
best_val_loss = float("inf")

for epoch in range(100):
    model.train()
    epoch_loss = 0
    for X_batch, y_batch in train_loader:
        y_pred = model(X_batch).squeeze()
        loss = criterion(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()

    avg_train_loss = epoch_loss / len(train_loader)
    train_losses.append(avg_train_loss)

    # Validation
    model.eval()
    val_loss = 0
    with torch.no_grad():
        for X_batch, y_batch in test_loader:
            y_pred = model(X_batch).squeeze()
            val_loss += criterion(y_pred, y_batch).item()
    avg_val_loss = val_loss / len(test_loader)
    val_losses.append(avg_val_loss)

    scheduler.step(avg_val_loss)

    # Early stopping
    if avg_val_loss < best_val_loss:
        best_val_loss = avg_val_loss
        best_state = model.state_dict().copy()

    if epoch % 20 == 0:
        print(f"Epoch {epoch:3d}: train_loss={avg_train_loss:.4f}, val_loss={avg_val_loss:.4f}")

# Load best model and evaluate
model.load_state_dict(best_state)
model.eval()
with torch.no_grad():
    y_pred = model(torch.FloatTensor(X_test_s)).squeeze().numpy()

print(f"\nMLP R²: {r2_score(y_test, y_pred):.4f}")
print(f"MLP MAE: {mean_absolute_error(y_test, y_pred):.2f}")

输出:

TEXT 📖 仅展示
# 执行成功

6. Bob的MLP vs 线性回归对比

▶ 示例:同数据集对比

PYTHON
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_absolute_error

# Linear Regression baseline
lr = LinearRegression()
lr.fit(X_train_s, y_train)
lr_pred = lr.predict(X_test_s)

print("=" * 50)
print("MODEL COMPARISON")
print("=" * 50)
print(f"Linear Regression: R²={r2_score(y_test, lr_pred):.4f}, MAE={mean_absolute_error(y_test, lr_pred):.2f}")
print(f"MLP (3-layer):     R²={r2_score(y_test, y_pred):.4f}, MAE={mean_absolute_error(y_test, y_pred):.2f}")

输出:

TEXT 📖 仅展示
=
MODEL COMPARISON
=
维度 LinearRegression MLP(3层)
0.78 0.86
MAE 12.5 9.3
特征交互 手动添加 自动学习
训练时间 <1s ~30s
可解释性 高(系数) 低(黑箱)
过拟合风险
⚠️ 注意: 表格数据上,XGBoost/LightGBM通常比MLP更好更快。MLP的优势在于自动学习特征交互和非线性变换。实际项目中先试GBDT,如果不够再试MLP。


❓ 常见问题

Q PyTorch和TensorFlow该学哪个?
A 研究/学习选PyTorch(更Pythonic、调试友好),生产部署看团队技术栈。两者功能相当,PyTorch在学术界更流行。
Q MLP隐藏层多少个、多少神经元?
A 常用经验——先1-2个隐藏层,神经元数从input_dim→64→32递减。太深太宽→过拟合,太浅太窄→欠拟合。用验证集调整。
Q Adam和SGD哪个好?
A 大多数情况Adam更好(自适应学习率、收敛快)。SGD+Momentum在某些任务上泛化更好。入门用Adam,精细调优再试SGD。
Q BatchNorm和Dropout是什么?
A BatchNorm标准化每层输出(加速训练+稳定性);Dropout随机丢弃神经元(防止过拟合)。两者是训练技巧标配。
Q 学习率怎么选?
A 初始0.001(Adam)或0.01(SGD)。配合scheduler自动衰减(ReduceLROnPlateau)。训练不稳定→降低lr,收敛太慢→提高lr。
Q 表格数据该用MLP还是XGBoost?
A 优先XGBoost——在表格数据上通常更好更快。MLP优势是自动学习特征交互。如果GBDT已经够好,不需要MLP。

📖 小节


📝 作业

  1. 基础题(难度⭐):用PyTorch创建一个2层MLP(输入4维→16→8→输出1),打印模型结构和参数总数。提示:nn.Sequential + sum(p.numel()...)
  2. 进阶题(难度⭐⭐):用MLP对California Housing预测,实现完整的训练循环(含验证和early stopping),对比LinearRegression的R²。提示:参考第5节完整训练流程。
  3. 挑战题(难度⭐⭐⭐):实验不同MLP架构(1层/2层/3层,不同隐藏层大小)对性能的影响,画出"隐藏层数 vs R²"曲线,找到最优架构。提示:循环不同hidden_dims配置 + 记录验证R²。

← 上一课:NLP基础 | 下一课:卷积神经网络CNN →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏