Code: https://github.com/gorgeousdays/RecSys-in-Action
期望实现神经网络实现如下目标
- 分类问题:给定映射$Y=AX+B$ 和阈值t根据$X$预测$Y > t$?
- 回归问题:给定映射$Y=AX+B$ 根据$X$预测Y
数据准备
构建训练数据
def generate_data(A: np.ndarray, B:np.ndarray,
size: int = 200, num_features: int = 2, task: str = "classification", t: int = 1) -> Tuple[np.ndarray, np.ndarray]:
assert task in ["classification", "regression"]
if A is None:
A = np.random.randn(num_features)
if B is None:
B = np.random.randn()
X = np.random.randn(size, num_features)
Y = X.dot(A) + B
if task == "classification":
data, labels = X, (Y > t).astype(int).reshape(-1, 1)
elif task == "regression":
data, labels = X, Y.astype(int).reshape
return data, labels
网络构建
构建神经网络前向传播与损失函数
回归任务
$$ \begin{aligned} Y &= AX + B \\ f(X) &= W_2 \cdot \text{ReLU}(W_1X + B_1) + B_2 \\ L_2(f(X), Y) &= \frac{1}{N}\sum_{i=1}^N|f_i(X) - Y_i| \end{aligned} $$
分类任务
$$ \begin{aligned} Y &= (AX + B) > t, \quad \forall y \in Y, \quad y \in \{0, 1\} \\ f(X) &= \text{Sigmoid}(W_2 (\text{ReLU}(W_1X + B_1)) + B_2) \\ L_1(f(X), Y) &= -\frac{1}{N} \sum_{i=1}^{N} \left[ Y_i \log(f(X_i)) + (1 - Y_i) \log(1 - f(X_i)) \right] \end{aligned} $$
其中$X$为自变量,$Y$为因变量,$f(X)$为构建的神经网络,$L(f(X)k, Y))$为设定的损失函数, $N$为样本数。
构建反向传播
计算损失函数的梯度
$$ \frac{\partial{L}}{\partial f(X)} = \frac{\partial}{\partial f(X)}|f(X) - Y| = \begin{cases} 1 & \text{if } f(X) > Y \\ -1 & \text{if } f(X) < Y \\ 0 & \text{if } f(X) = Y \end{cases} $$
接下来计算$f(X)$关于$W_1, W_2, B_1, B_2$的梯度, 根据复合函数的求导法则,有
$$ \begin{aligned} \frac{\partial f(X)}{\partial W_2} &= \text{ReLU}(W_1X + B_1)\\ \frac{\partial f(X)}{\partial B_2} &= 1\\ \frac{\partial f(X)}{\partial W_1} &= W_2 \cdot \text{ReLU}'(W_1X + B_1) \cdot X\\ \frac{\partial f(X)}{\partial B_1} &= W_2 \cdot \text{ReLU}'(W_1X + B_1) \end{aligned} $$
根据链式法则
$$ \begin{aligned} \frac{\partial L}{\partial W_2} &= \frac{\partial L}{\partial f(X)} \cdot \text{ReLU}(W_1X + B_1)\\ \frac{\partial L}{\partial B_2} &= \frac{\partial L}{\partial f(X)}\\ \frac{\partial L}{\partial W_1} &= \frac{\partial L}{\partial f(X)} \cdot W_2 \cdot \text{ReLU}'(W_1X + B_1) \cdot X\\ \frac{\partial L}{\partial B_1} &= \frac{\partial L}{\partial f(X)} \cdot W_2 \cdot \text{ReLU}'(W_1X + B_1) \end{aligned} $$
采用参数更新规则
$$ \theta_{new} = \theta_{old} - \eta\cdot\nabla_\theta L $$
其中,$\theta$为参数($W_1, W_2, B_1, B_2$),$\eta$为学习率,$\nabla_\theta L$为损失函数关于$theta$的梯度。
分类任务同理,其中。
$$ \begin{aligned} \text{ReLU}(z) &= \max(0, z)\\ \text{ReLU}'(z) &= \begin{cases} 1 & \text{if } z > 0 \\ 0 & \text{if } z \leq 0 \end{cases}\\ \text{Sigmoid}(z) &= \frac{1}{1 + e^{-z}}\\ \text{Sigmoid}'(z) &= \text{Sigmoid}(z) \cdot (1 - \text{Sigmoid}(z)) \end{aligned} $$
Questions?
如何支持多分类任务,而不仅仅是二分类?模型需要做哪些修改?
- 修改输出层大小为类别数, 使用 softmax 激活函数代替 sigmoid, 损失函数替换为交叉熵损失。
初始化方法
随机初始化: 均匀分布$W\sim U(-a, a)$, 正态分布$W \sim N(0, \sigma^2)$
- 优点: 简单易用, 适合小型网络
缺点: 梯度爆炸/消失问题, 深层网络上表现不好
- 若权重初始值过大,激活值可能进入激活函数的饱和区,导致梯度趋于 0(梯度消失)。
- 若权重初始值过小,信号在前向传播中会逐步衰减,导致网络难以学习。
- 若权重初始值不合适,反向传播中的梯度可能会无限增大或趋于 0(梯度爆炸或消失)。
Xavier初始化: 均匀分布$W\sim U(-\frac{\sqrt{6}}{\sqrt{n_{in} + n_{out}}},\frac{\sqrt{6}}{\sqrt{n_{in} + n_{out}}})$, 正态分布$W\sim N(0, \frac{2}{n_{in} + n_{out}})$
- 确保每一层的输出方差与输入方差相等,使得信号能以适当的尺度在层间传播。平衡正向和反向传播的梯度幅度,避免梯度消失或爆炸。
- 优点: 在浅层网络和使用 Sigmoid/Tanh 激活函数时表现较好。
- 缺点: 对 ReLU 激活函数不够理想,因为它没有考虑 ReLU 的非对称性(ReLU 截断负值)。
Kaming初始化(He初始化): 均匀分布$W\sim U(-\sqrt{\frac{6}{n_{in}}}, \sqrt{\frac{6}{n_{in}}})$, 正态分布$W\sim N(0, \frac{2}{n_{in}})$
- Xavier初始化的改进版本. 考虑到 ReLU 激活函数的特性(如输出的一半被截断为 0),通过加大初始化值来补偿信号的衰减,确保网络信号的方差在层间稳定。
- 优点: 适用于 ReLU 及其变种(如 Leaky ReLU)的激活函数。在深层网络中表现非常好,能有效避免梯度消失或爆炸。
- 缺点: 如果激活函数不是 ReLU 或其变种,效果可能不如 Xavier 初始化。
bias的初始化, 当前初始化bias为0, 有什么影响:
- 偏置初始为零,梯度通常也较小(尤其是当输入较小时),因此偏置的调整速度较慢。
- 对于 ReLU 激活函数,如果输入为负,初始权重和偏置可能导致输出恒为零(ReLU 截断负值),梯度更新受到影响。
- 对于 Sigmoid 或 Tanh 激活函数,如果网络输出远离激活函数的敏感区域(例如 Sigmoid 的梯度接近零时),偏置无法快速纠正,进一步放缓学习。
- 可以采用小随机值初始化或基于分布初始化
为什么选择ReLU而不是Sigmoid?
- ReLU 计算简单,能有效缓解梯度消失问题,适合深度网络。而 Sigmoid 会在输入较大或较小时出现梯度饱和,导致训练缓慢。
binary_cross_entropy_loss损失函数实现中为什么需要家epsilon=1e-15
- 防止log(0)数值溢出. 去掉后,预测值为 0 或 1 时会引发数值计算错误,影响稳定性。