# 过拟合与正则化

![last modify](https://img.shields.io/static/v1?label=last%20modify\&message=2022-10-13%2001%3A56%3A19\&color=yellowgreen\&style=flat-square)

```
```

## 过拟合与欠拟合

### 过拟合

**现象**

* **训练集**效果很好，但是**验证集**很差，这种现象称为过拟合，表现为**高方差**。

#### 常见解决方法

* 训练数据不足
  * 数据增强
    * NLP-EDA
    * CV-切割、裁剪、旋转
    * Mixup
  * 对抗训练
* 训练数据中存在噪声：
  * 交叉验证
  * 集成学习（Bagging）
* 模型复杂度较高：
  * 正则化项
  * Dropout
  * Early Stoping
  * 降低模型复杂度
    * 减少模型参数
    * 使用简单模型

### 欠拟合

**现象**

* **训练集**和**验证集**的效果都很差，这种现象称为欠拟合，表现为**高偏差**；

#### 常见解决方法

* 特征工程
  * FM、FFM
* 集成学习（Boosting）
* 提高模型复杂度
  * 增加模型参数
  * 使用复杂模型

### 参考

* [Bagging和Boosting的区别（面试准备） - Earendil - 博客园](https://www.cnblogs.com/earendil/p/8872001.html)
* [NLP-Interview-Notes/过拟合和欠拟合.md at main · km1994/NLP-Interview-Notes](https://github.com/km1994/NLP-Interview-Notes/blob/main/BasicAlgorithm/%E8%BF%87%E6%8B%9F%E5%90%88%E5%92%8C%E6%AC%A0%E6%8B%9F%E5%90%88.md)

## 常见正则化方法

### 数据增强

* 简单数据增强
  * NLP（EDA：增删改、替换）
  * 图像（切割、裁剪、旋转等）
* 数据融合
  * Mixup

### 为损失函数添加正则化项

* 常见的正则化项有 L1、L2 正则项；

**参考**

* L1、L2 正则化的特点与区别：[NLP-Interview-Notes/正则化.md at main · km1994/NLP-Interview-Notes](https://github.com/km1994/NLP-Interview-Notes/blob/main/BasicAlgorithm/%E6%AD%A3%E5%88%99%E5%8C%96.md)
* [机器学习中正则化项L1和L2的直观理解\_阿拉丁吃米粉的博客-CSDN博客](https://blog.csdn.net/jinping_shi/article/details/52433975)

#### L1 正则化

#### L2 正则化

### 提前结束训练（Early Stopping）

* 当模型在验证集上的性能开始下降时，提前结束训练；
* 一般会配合交叉验证来使用；

**参考**

* [【关于 早停法 EarlyStopping 】那些你不知道的事](https://mp.weixin.qq.com/s?__biz=MzAxMTU5Njg4NQ==\&mid=2247488608\&idx=1\&sn=5c484d2d0177dc968265ca1f3c9221e0\&scene=21#wechat_redirect)

### Dropout

**作用**

* 提升泛化能力，减少过拟合；

**原理**

* Dropout 提供了一种廉价的 Bagging 集成近似（模型平均）；

**思想**

* 遗传算法，通过随机变异（随机删除神经元），来促使整个种群的进化；

#### 常见问题

**Dropout 在训练和测试时有什么区别？为什么？**

* 训练时，经过 Dropout 的输出值会乘以 $\frac{1}{1-p}$；测试时不会。
* 经过 Dropout 后，输入 `x` 的期望输出将变为 `p*0 + (1-p)*x = (1-p)x`（`p` 的可能变为 0，`1-p` 的可能保持不变）；
* 为了还原未经过 Dropout 的期望值，故需要乘以 $\frac{1}{1-p}$

**为什么 Dropout 能防止过拟合？**

* 直观上，Dropout 会使部分神经元失活，减小了模型容量，从而降低了模型的拟合能力；
* 宏观上，Dropout 提供了一种廉价的 Bagging 集成方法（共享权重）；
* 隐藏单元经过 Dropout 后，必须学习与不同采样的神经元合作，使得神经元具有更强的健壮性（减少神经元之间复杂的共适应关系）；

#### PyTorch 实现

* 【训练阶段】前向传播时，对每个神经元以概率 `p` 失活（即乘以 `0.`），而其他未失活的单元则乘以 `1/(1-p)`（放大）
* 【测试阶段】使 dropout 失效，即正常使用所有神经元；

```python
class Dropout(nn.Module):

    def __init__(self, p):
        super().__init__()
        self.p = p  # 以 p 的概率丢弃

    def forward(self, x):
        if not self.training:
            return x
        
        mask = (torch.rand(x.shape) > self.p).float()
        return x * mask / (1.0 - self.p)
```

### R-Drop

> [\[2106.14448\] R-Drop: Regularized Dropout for Neural Networks](https://arxiv.org/abs/2106.14448)

**动机 & 作法**

* 尝试解决 Dropout 在训练与预测时使用不一致的问题；
* **Dropout 本身不是已经尝试解决了这个不一致问题吗？它的解决方案有什么问题？**
  * Dropout 通过缩放神经元的输出值来缓解训练与预测时不一致的影响。Dropout 的本意是为了得到一个“模型平均”的结果，而这种通过缩放来还原实际上是一种“权重平均”（见 Dropout 的推导），这两者未必等价；
  * 具体来说，Dropout 的正确使用方式应该是预测时打开 Dropout，然后计算多次预测的平均值作为结果；但实际并不是这样使用的。
* **R-Drop 是怎么解决这个问题的？**
  * 通过对同一样本 Dropout 两次，然后加入 KL 散度来保持不同 Dropout 下预测结果的一致性；
* **KL 散度损失是怎么保证预测一致性的？**
  * 交叉熵损失只关注目标类的得分，非目标类的得分不影响最终 loss；相当于训练目标是 “**不同 Dropout 下目标类的得分都大于非目标类的得分**”。
  * 举例来说 `[0.5, 0.2, 0.3]`、`[0.5, 0.3, 0.2]` 与 `[1, 0, 0]` 的交叉熵损失是一样的，都是 `-log(0.5)`，非目标类的 `0.2` 和 `0.3` 都没有起作用；
  * KL 散度则会关注每一个类别的得分，相当于训练目标是“**不同 Dropout 下每个类别的得分一致**”
  * 就上例来说，计算 `[0.5, 0.2, 0.3]`、`[0.5, 0.3, 0.2]` 与 `[1, 0, 0]` 的 KL 散度都会产生非零损失；

#### PyTorch 实现

```python
class RDrop(nn.Module):

    def __init__(self, encoder, kl_alpha=1.0):
        super().__init__()

        self.encoder = encoder
        self.kl_alpha = kl_alpha
        self.ce = nn.CrossEntropyLoss()
        self.kl = nn.KLDivLoss()

    def forward(self, x, labels):
        logits1 = self.encoder(x)
        logits2 = self.encoder(x)
        ce_loss = (self.ce(logits1, labels) + self.ce(logits2, labels)) / 2
        kl_loss1 = self.kl(F.log_softmax(logits1, dim=-1), F.softmax(logits2, dim=-1))
        kl_loss2 = self.kl(F.log_softmax(logits2, dim=-1), F.softmax(logits1, dim=-1))
        return ce_loss + self.kl_alpha * (kl_loss1 + kl_loss2) / 2
```

**参考**

* [又是Dropout两次！这次它做到了有监督任务的SOTA - 苏剑林](https://kexue.fm/archives/8496)

### 各种 Normalization

#### 参考

* [详解深度学习中的 Normalization，BN/LN/WN - 知乎](https://zhuanlan.zhihu.com/p/33173246)

#### Batch Normalization

`per channel per batch`

**使用场景**：CV

**前向过程**

$$
\begin{aligned} \mu &= \frac{1}{m} \sum\_{i=1}^m x\_i &//&\text{batch mean} \ \sigma^2 &= \frac{1}{m} \sum\_{i=1}^m (x\_i-\mu)^2 &//&\text{batch variance} \ \hat{x}\_i &= \frac{x\_i - \mu}{\sqrt{\sigma^2 + \epsilon}} &//&\text{normalization} \ y\_i &= \gamma \hat{x}\_i + \beta &//&\text{scale and shift} \end{aligned}
$$

**PyTorch实现**

```python
def batch_norm(x, eps=1e-5):
    """x: [batch_size, time_step, channel]"""
    C = x.shape[-1]
    mean = torch.mean(x, dim=(0, 1), keepdim=True)  # [1, 1, C]
    std = torch.std(x, dim=(0, 1), unbiased=False, keepdim=True)  # [1, 1, C]
    gamma = torch.nn.Parameter(torch.empty(C))
    beta = torch.nn.Parameter(torch.empty(C))
    output = gamma * (x - mean) / (std + eps) + beta
    return output
```

**参考**

* [【深度学习】深入理解Batch Normalization批标准化 - 郭耀华 - 博客园](https://www.cnblogs.com/guoyaohua/p/8724433.html)
* [Batch Normalization的通俗解释 - 知乎](https://zhuanlan.zhihu.com/p/54073204)

#### Layer Normalization

`per sample per layer`

**使用场景**：NLP

**前向过程**

```python
```

#### Instance Normalization

`per sample per channel`

**作用**

**使用场景**：CV 风格迁移

#### Group Normalization

`per sample per group`

#### Weight Normalization

#### 常见问题

**BN 与 LN 的区别**

* BN 在 batch 维度为归一；
* LN 在 feature 维度做归一；

**为什么 BN 一般不用于 NLP ？**

### 对抗训练


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://imhuay.gitbook.io/studies/notes/_archives/2022/05/guo-ni-he-yu-zheng-ze-hua.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
