深度学习小记
前置知识请见 人工智能导论 。
初识 TensorFlow
官网: https://www.tensorflow.org
TensorFlow 大框架:
Estimator 属于 High level 的 API,而 Mid-level API 分别是:
- Layers:用来构建网络结构
- Datasets:用来构建数据读取 pipeline
- Metrics:用来评估网络性能
Tensorflow1.0 主要特性:
- XLA —— Accelerate Linear Algebra
- 提升训练速度 58 倍
- 可以在移动设备运行
- 引入更高级别的 API ——
tf.layers
/tf.metrics
/tf.losses
/tf.keras
- Tensorflow 调试器
- 支持 docker 镜像,引入 tensorflow serving 服务
Tensorflow2.0 主要特性:
- 使用
tf.keras
和eager mode
进行更加简单的模型构建 - 鲁棒的跨平台模型部署
- 强大的研究实验
- 清除不推荐使用的 API 和减少重复来简化 API
使用 Tensorflow 的大致流程:
在虚拟环境中安装 tensorflow 。
一个 helloword 程序:
1 | import tensorflow as tf |
在我本地没有 nvidia GPU 的电脑,输出类似于:
1 | # 前面还有一大堆警告和报错... |
在 Colab 中运行该代码,得到输出:
1 | ******************* |
另一个例子:
1 | import tensorflow as tf |
图结构:静态图,效率高;动态图,调试容易。
图(graph) 是 tensorflow 用于表达计算任务的一个核心概念。从前端(Python)描述神经网络的结构,到后端在多机和分布式系统上部署,到底层 Device(CPU、GPU、TPU)上运行,都是基于图来完成。
数据流图:
张量为什么那么多阶?
可以考虑一些典型场景。例如,灰度图、png、视频等。
深度学习中的“张量” 是工程实践中使用的多维数组结构,而 数学中的张量 是抽象代数与几何的核心概念,两者在形式上相似,但本质和用途大不相同。
张量属性:
graph
张量所属的默认图op
张量的操作名name
张量的字符串描述shape
张量形状
Tensorflow-keras
简介
keras 是基于 python 的高级神经网络 API,以 Tensorflow、CNTK 或 Theano 为后端运行,keras 必须有后端才可以运行。后端可以切换,现在多用 tensorflow. 方便快速实验,帮助用户以最少的时间验证自己的想法。
Tensorflow-keras 是什么?
- Tensorflow 对 keras API 规范的实现
- 相对于以 tensorflow 为后端的 keras, Tensorflow-keras 与 Tensorflow 结合更加紧密
- 实现在
tf.keras
空间下
Tf-keras 和 keras 联系:
- 基于同一套 API
- keras 程序可以通过修改导入方式轻松转为
tf.keras
程序 - 反之可能不成立,因为
tf.keras
有其他特性 - 相同的 JSON 和 HDF5 模型序列化格式和语义
tf_keras做分类
下面的 ipynb 文件包含以下内容:
- 一个衣服分类的实验
- 为什么要分为训练集,验证集,测试集
- tensorflow 的基本使用
- 均方误差
- Cross Entropy Loss Function(交叉熵损失函数)
- w 的初始分布
下面的 ipynb 文件包含以下内容:
- 对之前相同的流程,做归一化,看看有无改进
下面的 ipynb 文件包含以下内容:
- TensorBoard
- ModelCheckpoint
- EarlyStopping
tf_keras做回归
下面的 ipynb 文件包含以下内容:
- 使用 California Housing dataset 做回归
梯度消失、梯度爆炸
问题描述
关于梯度消失,从一个例子入手:
上面文件演示的问题即是梯度消失。
梯度消失:
梯度消失问题发生时,接近于输出层的 hidden layer 3 等的权值更新相对正常,但前面的 hidden layer 1 的权值更新会变得很慢,导致前面的层权值几乎不变,仍接近于初始化的权值,这就导致 hidden layer 1 相当于只是一个映射层,这是此深层网络的学习就等价于只有后几层的浅层网络的学习了。
梯度爆炸:
在反向传播过程中使用的是链式求导法则,如果每一层偏导数都大于 1,那么连乘起来将以指数形式增加,误差梯度不断累积,就会造成梯度爆炸。梯度爆炸会导致模型权重更新幅度过大,会造成模型不稳定,无法有效学习,还会出现无法再更新的 NaN 权重值。
梯度消失和梯度爆炸其实都是因为反向传播中的连乘效应。除此之外,也和激活函数的选取有关。
对于梯度消失:
- Sigmoid 函数的导数最大值是 0.25(当 $ z = 0 $ 时)。当 $ z $ 的绝对值较大时(即神经元饱和时),$ \sigma’(z) $ 接近于 0 .
- Tanh 函数,当 $ z $ 的绝对值较大时,导数也接近于 0 .
- 在反向传播过程中,如果许多层的激活函数导数值都小于1(特别是当它们远小于1时,比如 Sigmoid 导数的最大值0.25),这些导数值连乘起来会使梯度呈指数级衰减。
- 如果权重被初始化为较小的值(例如,绝对值小于 1),多个较小的权重矩阵与较小的激活函数导数连乘,会进一步加速梯度的消失。
对于梯度爆炸,可以作类似的解释,不再赘述。
批归一化
接下来的行文逻辑:什么是批归一化,批归一化的动机,减均值除以标准差为什么失败,正确的批归一化算法。
批归一化可以缓解梯度消失和梯度爆炸的问题,简单来说即是,每层的激活值都做归一化。
在深度神经网络的训练过程中,每一层的参数都会在反向传播后进行更新。这意味着,对于网络中的某一层,其输入数据的分布(来自前一层的输出)会随着训练的进行而不断发生变化。这种现象被称为内部协变量偏移 (Internal Covariate Shift, ICS)。
ICS 会导致以下问题:
- 训练速度减慢:后续层需要不断适应这种变化的输入分布。
- 对初始化敏感:网络对参数的初始值更加敏感。
- 激活函数饱和:输入数据分布的变化可能导致激活函数的输入落入饱和区(例如Sigmoid的两端),从而导致梯度消失。
批归一化的提出就是为了缓解 ICS 问题。
如何实现批归一化呢?我们自然想到经典的“减均值除以标准差”方法:
这有两个好处:
- 避免分布数据偏移
- 远离导数饱和区
但这个处理对于在 -1~1 之间的梯度变化不大的激活函数,效果不仅不好,反而更差。比如 sigmoid 函数,在 -1~1 之间几乎是线性,BN 变换后就没有达到非线性变换的目的;而对于 relu,效果会更差,因为会有一半的置零。总之,减均值除以标准差后可能削弱网络的性能。
改进的思路是:缩放加移位。
这里的 $ y_i $ 就是批归一化层的输出,它将作为下一层或激活函数的输入。$\gamma$ 和 $\beta$ 是与网络的其他参数(如权重 $ W $ 和偏置 $ b $)一样通过梯度下降学习得到的。如果网络发现原始的表示更好,它可以学习到 $\gamma = \sqrt{\sigma_B^2 + \epsilon}$ 和 $\beta = \mu_B$,从而在一定程度上抵消归一化的影响。
tensorflow 中的接口:
https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization
下面是使用批归一化的例子:
更改激活函数
relu -> selu
, 缓解梯度消失。
实例:
Dropout
Dropout 可以防止过拟合,实现也非常简单:对想要 dropout 的点输出乘 0 .
The
Dropout
layer randomly sets input units to 0 with a frequency ofrate
at each step during training time, which helps prevent overfitting. Inputs not set to 0 are scaled up by1 / (1 - rate)
such that the sum over all inputs is unchanged.
实例:
Wide & Deep 模型(推荐场景)
原始论文: https://arxiv.org/pdf/1606.07792v1
Google 于 16 年发布,用于分类和回归。曾应用在 Google Play 中的应用推荐。
Wide 用于记忆,Deep 用于联想。一个推荐系统的经典矛盾:应该给用户推荐经常买的,还是探索一些用户可能会买的?
相关文章:
- https://blog.csdn.net/Yasin0/article/details/100736420 (已备份)
- https://zhuanlan.zhihu.com/p/57247478
- https://zhuanlan.zhihu.com/p/139358172 (已备份)
尝试搭建简单的 wide and deep 模型(github 链接):
在 Deep 部分,会用到 Word2Vec 技术。简要介绍:
Word2Vec is a technique in natural language processing that represents words as vectors in a high-dimensional space, capturing semantic relationships between them. It essentially translates words into numerical representations, where words with similar meanings are located closer together in the vector space.
可供参考的资料:
https://medium.com/@manansuri/a-dummys-guide-to-word2vec-456444f3c673
Word2Vec utilizes two main architectures:
- CBOW (Continuous Bag of Words): Predicts a target word based on its surrounding context words.
- Skip-gram: Predicts surrounding words based on a given input word.
简要的原理介绍:
如何衡量两个词在语义上是否相近?如果两个词的上下文相同(相近),我们可以有较大的把握认为,这两个词的语义也相近。我们构造一个神经网络,输入为上下文,输出为该词,训练之,将隐藏层的数值作为这个词的 vector 元素。在实践中,我们发现语义相近的词,由这种机制产生的 vector 表示也相近。
有了 Word2Vec,我们就可以用向量之间的差距来衡量信息之间的差距。
用面向对象的方法重写之前的代码:github链接。
在之前的模型中,我们只有一个输入,现在尝试多输入(github链接):
iframe 嵌多了可能有点卡,之后只放 github 链接了。
尝试搭建更加复杂的模型(github链接):
一个关于 Wide and Deep 的推荐实验:
https://blog.csdn.net/weixin_34268753/article/details/92123569
超参数搜索
为什么要超参数搜索?手工去试耗费人力。
有哪些超参数?
- 网络结构参数:几层,每层神经元个数,每层激活函数
- 训练参数:
batch_size
,学习率,学习率衰减算法
Batch Size
下面链接详细讲解了 Batch Size:
https://blog.csdn.net/qq_34886403/article/details/82558399
注意:Batch Size 增大了,要到达相同的准确度,必须要增大 epoch。
学习率衰减
固定学习率时,当到达收敛状态时,会在最优值附近一个较大的区域内摆动;而当随着迭代轮次的增加而减小学习率,会使得在收敛时,在最优值附近一个更小的区域内摆动(之所以曲线震荡朝向最优值收敛,是因为在每一个 mini-batch 中都存在噪音)。
学习率衰减算法有很多,这里仅举最简单的两例。
离散下降(discrete staircase)
对于深度学习来说,每 t 轮学习,学习率减半。对于监督学习来说,初始设置一个较大的学习率,然后随着迭代次数的增加,减小学习率。
指数减缓(exponential decay)
对于深度学习来说,学习率按训练轮数增长指数差值递减。例如:
又或者公式为:
其中 epochnum 为当前 epoch 的迭代轮数。不过第二种方法会引入另一个超参 k。
网格搜索、随机搜索
在 机器学习笔记 中已经提过,我们直接实战。
先看一个朴素的例子:github链接 。
能否配合 sklearn 进行参数搜索?
注意: scikit-learn 在 1.6 版本中对其内部处理 estimator tags (估计器标签) 的 API 进行了现代化更改。这导致一些第三方库(包括早期版本的 XGBoost 和 CausalML)在与 scikit-learn 1.6.x 交互时出现了 AttributeError: 'super' object has no attribute '__sklearn_tags__'
类似的问题。
一种不够优雅的解决方案是降级 scikit-learn, 我就不尝试了。 github链接 .
另外一些可能的方案有:KerasTuner 等。
关于网格搜索、随机搜索:
随机搜索:参数的生成方式为随机,可探索的空间更大。