Jiacheng Huang bio photo

Email

Github

Google Scholar

NTU EE7207 神经网络与深度学习 — 课程总结

最后更新: 2025年12月

2025年下半年在南洋理工大学修读了 EE7207 Neural Networks and Deep Learning,系统地从感知器学到了 Transformer。这篇文章是课程的复盘与个人总结,适合有一定编程基础但刚接触深度学习的同学参考。


课程概览

EE7207 是 NTU 电气与电子工程学院的研究生核心课程,理论与实践并重,主要涵盖:

  • 经典神经网络:感知器、MLP、反向传播
  • 径向基函数网络:RBF / Gaussian 核、聚类中心选取
  • 卷积神经网络 (CNN):图像特征提取、经典架构
  • 循环神经网络 (RNN / LSTM):序列建模与时序依赖
  • 支持向量机 (SVM):核技巧与间隔最大化
  • 现代深度学习:Attention、正则化、优化器

1. 神经网络基础

从感知器到 MLP

感知器(Perceptron)是最基础的线性分类器:输入加权求和,经过激活函数输出。

单层感知器只能处理线性可分问题——XOR 问题直接打倒了它。加入隐藏层后形成多层感知器(MLP),理论上可以逼近任意连续函数(万能近似定理)。

import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, in_dim, hidden_dim, out_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(in_dim, hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, out_dim)
        )
    def forward(self, x):
        return self.net(x)

激活函数选型

激活函数 公式 优点 缺点
Sigmoid 1/(1+e⁻ˣ) 输出有界 [0,1] 梯度消失、非零中心
Tanh (eˣ-e⁻ˣ)/(eˣ+e⁻ˣ) 零中心 仍有梯度消失
ReLU max(0, x) 计算快、无饱和 死神经元(负区间永久关闭)
Leaky ReLU max(αx, x) 解决死神经元 需调 α

实践经验: 隐藏层默认 ReLU,分类输出层用 Softmax,回归用 Linear,二分类用 Sigmoid。


2. 反向传播

BP 算法是神经网络训练的核心,本质是链式法则 + 梯度下降

前向传播 → 计算损失 → 反向求梯度 → 更新权重

损失函数

# 多分类
loss = nn.CrossEntropyLoss()(output, labels)

# 回归
loss = nn.MSELoss()(output, targets)

优化器

# Adam(大多数任务首选)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)

# SGD + Momentum(大规模视觉任务有时更优)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

学习率调度:

scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)

3. RBF 网络 — Assignment 1 实战

课程第一次作业:用 Gaussian RBF 神经网络完成二分类任务。这是我第一次真正从头设计一个神经网络(不是调包),印象很深。

RBF 网络结构

输入层(d维)→ 隐藏层(M个高斯节点)→ 输出层(线性)

隐藏层每个节点计算输入到中心点的 Gaussian 响应:

φⱼ(x) = exp( -||x - cⱼ||² / (2σⱼ²) )

设计关键点

Step 1: 用 k-means 确定中心点

from sklearn.cluster import KMeans

M = 20  # 隐藏节点数,通过交叉验证选取
kmeans = KMeans(n_clusters=M, random_state=42).fit(X_train)
centers = kmeans.cluster_centers_   # shape: (M, d)

Step 2: 用 P-nearest-neighbor 启发式确定宽度 σ

import numpy as np
from scipy.spatial.distance import cdist

D = cdist(centers, centers)
np.fill_diagonal(D, np.inf)
# 每个中心点到最近邻中心的距离作为 σ
sigma = D.min(axis=1)

Step 3: 构造隐藏层输出矩阵,伪逆求权重

def rbf_layer(X, centers, sigma):
    dist2 = cdist(X, centers, 'sqeuclidean')   # (N, M)
    return np.exp(-dist2 / (2 * sigma**2))

Phi = rbf_layer(X_train, centers, sigma)       # (N, M)
W = np.linalg.pinv(Phi) @ y_train             # 输出权重

# 预测
Phi_test = rbf_layer(X_test, centers, sigma)
y_pred = np.sign(Phi_test @ W)

RBF vs MLP

  RBF 网络 MLP
训练方式 解析解(伪逆) 迭代梯度下降
训练速度 极快 较慢
小数据表现 好(局部拟合强) 容易过拟合
大数据扩展 一般
超参数 M, σ, 中心点 层数、宽度、lr

结论:小数据 + 明确的局部结构 → RBF 有优势;大规模任务 → MLP / CNN 更合适。


4. 卷积神经网络 (CNN)

CNN 的核心思想:局部连接 + 权重共享,大幅减少参数量的同时保留空间信息。

class CNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 32, 3, padding=1),   # 卷积
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(2),                   # 下采样

            nn.Conv2d(32, 64, 3, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.head = nn.Sequential(
            nn.Dropout(0.5),
            nn.Linear(64 * 7 * 7, 256),
            nn.ReLU(),
            nn.Linear(256, num_classes)
        )

    def forward(self, x):
        return self.head(self.features(x).flatten(1))

感受野理解: 两层 3×3 卷积 = 一个 5×5 的等效感受野,但参数量是 2×(3×3×C²) vs (5×5×C²),参数更少、非线性更强。这是 VGG / ResNet 的设计出发点。


5. 序列建模:RNN 与 LSTM

标准 RNN 受梯度消失限制,难以学习长距离依赖。LSTM 通过门控机制解决了这个问题:

遗忘门(ft): 决定丢弃多少历史信息
输入门(it): 决定写入多少新信息
输出门(ot): 决定输出多少隐状态
lstm = nn.LSTM(input_size=64, hidden_size=128,
               num_layers=2, batch_first=True, dropout=0.3)
output, (hn, cn) = lstm(x)   # x: (B, T, 64)

实际上在学这门课之前,我已经在 RoPEHAR 毫米波 HAR 项目中用过 LSTM 做时序建模。当时更多是按文献照搬结构,对门控机制的物理含义不太清楚。修完这门课之后,才真正理解遗忘门和输入门是如何在每个时间步上动态决定”保留多少历史”与”写入多少新信息”的——这种选择性记忆正是 LSTM 在长序列上优于标准 RNN 的本质原因。


6. 正则化 — 对抗过拟合

Dropout: 训练时随机丢弃,测试时全激活

nn.Dropout(p=0.5)

Batch Normalization: 每个 mini-batch 归一化,稳定训练,是现代网络的标配

nn.BatchNorm2d(num_features)  # 卷积层后
nn.LayerNorm(hidden_dim)      # Transformer 中

L2 正则化(权重衰减)

optimizer = torch.optim.Adam(model.parameters(), weight_decay=1e-4)

Early Stopping: 监控验证集 loss,停止时机比学习率调度更重要

if val_loss < best_val_loss:
    best_val_loss = val_loss
    torch.save(model.state_dict(), 'best_model.pt')

推荐资料

课程作业代码:github.com/Mr-VanGogh-K/EE7207


欢迎交流: jiacheng008@e.ntu.edu.sg