> For the complete documentation index, see [llms.txt](https://blessbingo.gitbook.io/garnet/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blessbingo.gitbook.io/garnet/trick/ke-fu-guo-ni-he-he-ti-gao-fan-hua-neng-li-de-ji-qiao.md).

# 克服过拟合和提高泛化能力的技巧

## 引入

如何提升深度学习/机器学习模型的效果(准确率等评价指标), 或者如果现在的模型表现不好, 要如何解决. 这是个非常大的问题, 也是现实建模中都会遇到的问题, 具体问题具体分析, 总结下来可以从以下几个角度切入.

## 角度

一般可以从四个角度切入:

* 从数据上提升性能
* 从算法上提升性能
* 从算法调优上提升性能
* 从模型融合上提升性能

### 从数据上提升性能

调整**训练数据**或是**问题的抽象定义方法**可能会带来巨大的效果改善, 甚至是最显著的改善.

#### 收集更多的数据

收集更多的数据, 总是最有效但最昂贵的方法. 数据集相对于算法, 总是更重要的.

#### 产生更多的数据

得不到更多的原始数据时, 可以使用一些方法生成一些数据. 这在CV或NLP问题中常常被使用, 被称为**数据增强**.

例如在数据中增加一些**噪声**, 往往能够提升算法模型最后的健壮性.

#### 对数据做缩放

这里的缩放指的是**值域上的缩放**. 此方法简单, 却往往有效. 这是因为神经网络模型的一条经验法宝就是, **将数据缩放到激活函数的阈值范围**. 例如`sigmoid`函数, 将数据缩放到0到1之间, `tanh`激活函数的阈值则为-1到1. **这里需要对输入输出都进行转换**.

在实际操作中, 可以对数据集进行多种缩放, 得到新的数据集, 然后在每个数据集上测试模型的性能, 选用最好的一组生成数据. 如果中间更换了激活函数, 需要重新测试选择.

* 归一化到0到1
* 归一化到-1到1
* 标准化

#### 对数据做变换

与上一章的类似, 这一步其实就是对数据做预处理工作, 而上一个方法是预处理的一部分. 使用不同的预处理, 得到的结果肯定也不同.

这一步最重要的是彻底的**了解数据**, 从而才能在自己的理解基础上, 最大程度上使用好数据. 这一步需要数据可视化辅助.

首先对每一列(特征)进行分析:

* 通过可视化等方法, 挑出异常值, 选择性处理
* 是不是接近高斯分布(偏斜的高斯分布), 如果是, 使用Box-Cox等方法进行校正
* 是不是指数分布, 若是, 进行对数转换
* 尝试对这列数据平方或开平方之后, 能不能发现一些难以直观发现的特性
* 是否可以将特征离散化, 以便更好地强调一些特征

分析完之后, 就需要确定数据预处理和特征工程(新特征)的方法:

* 是否可以用投影(PCA等)的方法对数据预处理
* 是否可以将多个属性合并为单个值
* 是否可以发掘某个新的属性, 用布尔值表示
* 是否可以在时间尺度或是其它维度上有些新发现

神经网络模型往往不需要特征工程, 因为深层的网络能够自动地完成这些事情, 但如果使用特征工程将更好的表征提取出来, 往往能够**加快网络训练的收敛速度**.

关于特征工程的总结, 可以参考:

* [Discover Feature Engineering, How to Engineer Features and How to Get Good at It](https://machinelearningmastery.com/discover-feature-engineering-how-to-engineer-features-and-how-to-get-good-at-it/)

#### 特征选择

神经网络模型受不相关特征的影响很小, 因此特征选择在这里并不是必须的, 或者不会产生很大的影响.

时间充裕的话, 可以尝试:

* 使用更少的特征达到同样, 甚至更好的效果
* 尝试多种特征选择的方法, 这些方法同时抛弃的特征, 对这些特征进行分析
* 特征选择的过程中得到启发, 创建更有效的特征

#### 问题重构

* 可以将时间元素融入到一个窗口之中
* 分类问题可以转化为回归问题, 反之亦然
* 可以把二值类型的输出转化为softmax的输出
* 可以对子问题建模

### 从算法上提升性能

这里指的是**算法的选择问题**.

#### 算法筛选

当在所有可能出现的问题上进行效果评测时, 没有哪一项单独的算法效果会好于其它算法, 所有的算法都是平等的. 只有更适合这个问题的算法.

可以从简单到复杂进行尝试.

#### 从文献中学习

#### 重采样的方法

### 从算法调优上提升性能

#### 可诊断性

**只有知道为何模型的性能不再有提升了, 才能达到最好的效果.** 这里指的就是模型是过拟合还是欠拟合导致的问题. 通过比较训练过程中的每一步, 模型在训练集和开发集上的表现来判断.

* 训练集的效果好于验证集, 说明可能存在过拟合的现象, 试一试增加正则项
* 训练集和验证集的准确率都很低, 说明可能存在欠拟合, 你可以继续提升模型的能力(更复杂的模型, 更深的层), 延长训练步骤
* **训练集和验证集的曲线有一个焦点, 可能需要用到early stopping的技巧了**

另外, 还可以从样本粒度出发, 深入研究模型正确预测或是错误预测的样本:

* 也许需要更多的难预测的样本数据
* 可以从训练集中删去那些容易被学习的样本
* 可以有针对性地对不同类型的输入数据训练不同的模型

#### 权重的初始化

保持模型结构不变, 试一试不同的初始化策略:

* 尝试所有的初始化方法, 找出最好的一组初始化值
* 试一试用非监督式方法预学习, 比如自动编码机
* 尝试用一组现有的模型权重参数, 然后重新训练输入和输出层(迁移学习)

**修改权重初始化值的方法与修改激活函数或者目标函数的效果相当**.

#### 学习率

学习率在训练过程中是非常重要的. 训练过程中采用不同的学习率变化策略, 往往能够得到差异很大的结果.

一般来说, **大的网络模型需要更多的训练步骤, 相对来说可以使用大学习率**.

**学习率与训练步骤, batch大小和优化方法都有耦合关系**.

#### 激活函数

#### 网络拓扑结构

* 试一试加一层有许多节点的隐藏层(拓宽)
* 试一试一个深层的神经网络, 每层节点较少(纵深)
* 尝试将上面两种组合
* 尝试模仿近期发表的问题类似的论文

对于DNN, 网络层数和每层结点数量的选择是一个玄学问题. 但可以参考[这篇文章](ftp://ftp.sas.com/pub/neural/FAQ3.html#A_hl)中的*How many hidden layers should I use?* 和 *How many hidden units should I use?* 章节.

#### batch和epoch

尝试过不同的batch大小和epoch的次数. 实践中可以尝试设置一个近似于无限大的epoch次数, 然后快照一些中间结果, 寻找效果最好的模型.

**有些模型结构对batch的大小很敏感**(例如LSTM和CNN, 但只是实践经验).

#### 正则项

dropout等, 权值衰减, L1L2等.

#### 优化方法和损失函数

SDG是默认的方法, 但采用动态学习率和动量值的优化方法, 往往训练速度更快. 优化方法也有超参数, 这些超参数对最后的结果有着很大的影响, 要仔细调试.

另外实践中还有更换损失函数的trick, 有时也能带来意外的收获.

#### Early Stopping

early stopping也是防止数据过拟合的一种正则化方法.

### 用融合方法提升效果

将多个模型的预测结果融合, 将几个效果还可以的模型的预测结果融合, 往往取得的效果要比多个精细调优的模型分别预测的效果好.

#### 模型融合

如果训练了多个深度学习模型, 每一个的效果都不错, 则将它们的预测结果取均值. 模型的差异越大, 效果越好, 且集成后的结果效果更稳定.

也可以使用不同的超参数得到多个模型进行融合, 虽然他们的结果是高度相关的, 但在单模型到达瓶颈之后往往也有微小的提升.

#### 视角融合

数据的变换方式, 和描述问题的不同, 得到不同的模型. 需要注意的是不同的描述问题得到的模型进行融合时, 需要使用一定的策略.

#### stacking

## 参考资料

* [深度学习性能提升的诀窍 How To Improve Deep Learning Performance](https://www.cnblogs.com/mrxsc/articles/6266584.html)
