Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

DL——BERT

  • 参考博客:
    • 从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史
    • BERT详解
  • BERT论文: BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

BERT之前

Word2Vec的缺点

  • 多义词问题 : 传统的Word Embedding无法识别多义词
    • 确切的说是所有词的固定表征的方式(静态方式)的缺点
    • 所谓静态指的是训练好之后每个单词的表达就固定住了

从Word Embedding到ELMo

ELMo, Embedding from Language Models
根据当前上下文对Word Embedding动态调整的思路

  • ELMo 论文原文: NAACL 2018 Best Paper, Deep contextualized word representations
  • ELMo的本质思想:
    • 事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分
    • 实际使用Word Embedding的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的Word Embedding表示
    • 这样经过调整后的Word Embedding更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了
  • ELMo是典型的两阶段训练过程: 预训练 + 特征融合?
    • 第一个阶段是利用语言模型进行预训练
    • 第二阶段通过基于特征融合的方式训练
  • ELMo预训练过程示意图
  • ELMo预训练后如何处理下游任务?
    • 预训练训练完成后, 模型训练时使用在线特征抽取,和特征集成的方式对词向量在不同的上下文中进行不同的修正,从而区分多义词
补充知识: 下游任务

下游任务包括很多, 整体上可以分为四大类

序列标注
  • 分词
  • POS Tag
  • NER
  • 语义标注
  • …
分类任务
  • 文本分类
  • 情感计算
  • …
句子关系判断
  • Entailment
  • QA
  • 自然语言推理
  • …
机器翻译
  • 机器翻译
  • 文本摘要
  • …
预训练模型
  • 预训练模型是什么?
    • 预训练模型是指在训练结束是结果比较好的一组权重值,研究人员分享出来供他人使用,基于这些预训练好的权重可以提升我们的模型训练速度和精度
    • 预训练模型能够成功的本质是我们假设预训练模型足够好, 能学到句子的所有信息(包括序列信息等)
  • 两阶段预训练模型如何处理下游任务?
    • 预训练与下游任务无关
      • 预训练阶段是预训练模型自己选择相应的NLP任务,然后让模型在学习处理这些任务的途中实现参数的训练
      • 比如BERT选择的就是MLM(屏蔽语言模型)和NSP(Next Sentence Predition, 下一个句子预测)两个任务来做预训练
    • 不同的下游任务往往需要修改第二阶段中的模型结构等
    • 为适应不同的下游任务, 第二阶段可能使用不同结构, 比如添加Softmax层等方式
ELMo的优缺点
  • 优点:
    • 很好的解决了多义词问题,而且效果非常好
    • 采用上下文来训练词(从上下文预测单词, 上文称为Context-before, 下文称为Context-after)
  • 缺点:
    • 特征提取器没有使用新贵Transformer, 而是传统的LSTM, 特征抽取能力不足

从Word Embedding到GPT

GPT, Generative Pre-Training

  • ELMo的训练方法和图像领域的预训练方法对比,模式不同, ELMo使用的是基于特征融合的预训练方法
  • GPT使用的预训练方法则是在NLP领域开创了和图像领域一致的预训练方法基于Fine-tuning的模式
  • GPT也采用两阶段过程: 预训练 + Fine-tuning
    • 第一个阶段是利用语言模型进行预训练
    • 第二阶段通过Fine-tuning的模式训练
  • GPT预训练后如何处理下游任务?
  • 一些下游任务的Fine-tuning结构

GPT的优缺点

  • 优点:
    • 特征提取器是Transformer,不是RNN, 所以特征提取效果好
  • 缺点
    • GPT使用的是单向语言模型 : 也就是说只用到了上文来预测词
    • 词嵌入时没有单词的下文, 失去了很多信息

BERT结构和原理

下面的讲解都将按照原论文的思路讲解

  • BERT(Bidirectional Encoder Representations from Transformers), 原文 BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
  • 下图是BERT与GPT和ELMo对比的结构图
    • 图中的每个 Trm 组件就是一个 Transformer 的Encoder部分,也就是下图中的左半部分

BERT的特点

  • 一体化特征融合的双向(Bidirectional)语言模型
    • 利用语言的双向信息
    • GPT是单向语言模型, 只能利用一个方向的信息
    • ELMo也是双向语言模型,但是 ELMo实际上是两个方向相反的单向语言模型的拼接, 融合特征的能力比BERT那种一体化的融合特征方式弱
  • 特征提取器:
    • 使用Transformer(实际上使用的是Transformer的Encoder部分, 图中每个)
  • 预训练任务:
    • 屏蔽语言模型(MLM, Masked Language Model) + 相邻句子判断(NSP, Next Sentence Prediction)两个任务的多任务训练目标
  • 训练数据量:
    • 超大规模的数据训练使得BERT结果达到了全新的高度
    • 可以使用BERT作为词嵌入(Word2Vec)的转换矩阵, 实现比其他词嵌入模型更好的结果

输入表示

  • 输入结构图
  • BERT输入是一个512维的编码向量, 是三个嵌入特征的单位和
WordPiece嵌入
  • 对应图中的Token Embedding
  • WordPiece是指将单词划分成一组有限的公共子词单元,能在单词的有效性和字符的灵活性之间取得一个折中的平衡
  • 举例: 原文中 “playing” 被拆分成了 “play” 和 “##ing” 两部分
Segment Embedding

分割嵌入

  • 对应图中的Segment Embedding
  • 用于区分两个句子,例如B是否是A的下文(对话场景,问答场景等)
  • 对于句子对,第一个句子的特征值是0,第二个句子的特征值是1, 从而模型可以表达出词出现在前一个句子还是后一个句子
Position Embedding

位置嵌入

  • 对应图中的 Position Embedding
  • 位置嵌入是指将单词的位置信息编码成特征向量
  • 这是继承自论文Google Brain, NIPS 2017: Attention Is All You Need中, 文章中的Transformer架构没有使用RNN,不能编码位置信息,就是在进入Attention前使用了 词嵌入 + 位置嵌入 的方式让模型能够表达位置信息的

预训练

  • 通常预训练是指在训练阶段让模型去解决自然语言任务, 从而训练完成后得到可移植到其他模型(或者当前模型)使用的参数(包括词向量等)
  • BERT的预训练使用了两个NLP任务: MLM + NSP
  • BERT预训练和使用概览:
    • 从上图可以看出, BERT的预训练包含了两方面的任务, NSP和 MLM
    • 实验证明, MLM 优于标准的 LTR(left-to-right)语言模型(OpenAI GPT 使用的就是这个)
屏蔽语言模型(MLM)

Masked Language Model

  • Masked Language Model(MLM)核心思想取自Wilson Taylor在1953年发表的一篇论文“Cloze Procedure”: A New Tool for Measuring Readability
  • 在训练的时候随机从输入预料上屏蔽(Mask)掉一些单词,然后通过的上下文预测该单词(“完形填空”)
  • 传统的语言模型是Left-to-Right(LTR)或者是Right-to-Left(RTL)的, 和 RNN 结构匹配
  • MLM 的性质 和 Transformer 的结构匹配
  • BERT实验中, 有15%的WordPiece Token句子会被屏蔽掉, 在屏蔽的时候,又有不同的概率
  • 如果已经选中(15%概率)要屏蔽 my dog is hairy 中的 hairy, 那么我们的处理方式是:
    • 80%: my dog is hairy -> my dog is [MASK]
    • 10%: my dog is hairy -> my dog is apple
    • 10%: my dog is hairy -> my dog is hairy
    • 防止句子中的某个Token 100%都会被mask掉,那么在Fine-tuning的时候模型就会有一些没有见过的单词
    • 加入随机Token的原因是因为Transformer要保持对每个输入Token的分布式表征,否则模型就会记住这个[MASK]是 Token “hairy”
    • 错误单词带来的负面影响: 一个单词被随机替换掉的概率只有 \(15% \times 10% = 1.5%\) 这个负面影响其实是可以忽略不计的
  • 另外: 文章指出每次只预测15%的单词,因此模型收敛的比较慢
为什么使用MLM
  • 因为效果好,解释就是MLM更符合Transformer的结构
  • 论文中的实验结果:
    • MNLI(Multi-Genre Natural Language Inference)是多类型自然语言推理任务, 是一个大规模的众包蕴含分类任务, 给定一个句子,目标是预测第二句相对与第一句是一个蕴含语句, 矛盾语句, 还是中性语句
    • 从图中可以看出,在MNLI任务中, MLM预训练 + MNLI Fine-tuning 的效果明显优于 LTR预训练 + MNLI Fine-tuning 的效果
相邻句子预测(NSP)

Next Sentence Prediction

  • NSP 的任务是判断句子B是否是句子A的下文
  • 图中的[CLS]符号就是用于分类的, 如果是的话输出’IsNext‘,否则输出’NotNext‘
  • 训练数据的生成方式是从平行语料中随机抽取的连续两句话,其中50%保留抽取的两句话,它们符合IsNext关系,另外50%的第二句话是随机从预料中提取的,它们的关系是NotNext的
  • 举例来说:
    • Input = [CLS] the man went to [MASK] store [SEP] he bought a gallon [MASK] milk [SEP]
    • Label = IsNext
    • Input = [CLS] the man [MASK] to the store [SEP] penguin [MASK] are flight ##less birds [SEP]
    • Label = NotNext

Fine-tuning 处理下游任务

Fine-tining, 中文也称为微调

  • 下图是BERT在不同任务的的微调方法

  • 第二阶段,Fine-Tuning阶段,这个阶段的做法和GPT是一样的。当然,它也面临着下游任务网络结构改造的问题,在改造任务方面Bert和GPT有些不同

    • 句子类关系任务: 和GPT一样,增加起始和终结符号,输出部分Transformer最后一层每个单词对应部分都进行分类即可
    • 除了生成任务外, 其他任务Bert都涉及到了

BERT的使用

  • Google公开了两个不同规模的 BERT模型:
    • ** \(BERT_{BASE}\) ** : 110M模型参数
    • ** \(BERT_{LARGE}\) ** : 340M模型参数
  • 同时公开了两个模型在大规模数据预训练后的参数集合, 供开发者下载和使用

基于BERT的新秀

  • Token仍然使用词, 但是MLM屏蔽时选择屏蔽短语或者实体

ERNIE from Baidu

  • 参考文章: [ERNIE: Enhanced Representation through Knowledge Integration]
  • 核心思想:
    • 用先验知识来加强预训练模型(考虑实体,短语等级别的屏蔽)
    • 在BERT的预训练阶段, MLM模型中屏蔽一个实体(Entity)或者短语(Phrase)而不是屏蔽一个字(Word)
  • 文中提出三种级别的屏蔽方式
    • 基本级别(Basic-level)
    • 实体级别(Entity-level)
    • 短语级别(Phrase-level)
实验对比

ERINE from THU

  • 参考文章: ERNIE: Enhanced Language Representation with Informative Entities
  • 核心思想:
    • 利用先验知识来加强预训练模型(引入知识图谱)
    • 提出了将知识纳入语言表达模型的方法
    • 使用知识聚合器(Knowledgeable aggregator)和预训练任务 dEA, 更好的融合来自文本和知识图谱的异构信息
  • 知识信息
  • 模型架构

DL——CTR预估负样本采样修正


适用场景

  • 在CTR预估模型中,通常需要对负样本进行采样,以提升训练效率
  • 负样本采样会改变样本分布,从而导致预估均值出现偏差

问题描述

  • 假设对负样本进行采样,此时数据分布(正负样本比例)会发生改变,为了保证预估均值,需要对模型进行修正

修正方式

  • 修正方式一般有两种

训练时修正

  • 灵活,可以在训练时考虑到修正,保证训练后的模型可以直接使用

    训练后修正

  • 不常用,需要记录训练时的采样率等,容易出现问题

推导

训练时修正推导

  • 假设原始样本数,正样本数,负样本数分别为 \(N,N_p,N_n\)

  • 采样前:

    • 令正样本概率为 \(p = \frac{N_p}{N}\),则有 \(1-p = \frac{N_n}{N}\)
    • 进一步有: \(\frac{p}{1-p} = \frac{N_p}{N_n}\)
  • 按照比例 \(r\) 对负样本进行采样后剩下负样本数为 \(r\cdot N_n\):

    • 同理有: \(\frac{p’}{1-p’} = \frac{N_p}{N_n/r} = r\cdot\frac{N_p}{N_n}\)
  • 假定CTR的输出经过sigmoid激活函数输出概率值

  • 为了保证预估时可以使用如下公式:
    $$ p = \frac{1}{1+e^{-wx}} $$

  • 需要满足的训练公式为:
    $$ p‘ = f(x) $$

  • 求解 \(f\) 的流程如下:

    • 由sigmoid函数(\(p = \frac{1}{1+e^{-wx}}\))的定义可得:
      $$ wx = ln \frac{p}{1-p} $$
    • 又因为:
      $$
      \frac{p}{1-p} = \frac{N_p}{N_n} \\
      \frac{p’}{1-p’} = \frac{1}{r}\cdot\frac{N_p}{N_n} \\
      ln(\frac{p’}{1-p’}) = ln(\frac{1}{r}\cdot\frac{p}{1-p}) = ln(\frac{1}{r}) + ln(\frac{p}{1-p}) \\
      $$
    • 所以有:
      $$ ln(\frac{p’}{1-p’}) = ln(\frac{1}{r}) + ln(\frac{p}{1-p}) = ln(\frac{1}{r}) + wx $$
    • 于是有预估时的公式为:
      $$ p’ = \frac{1}{1+e^{-(wx+ln(\frac{1}{r}))}} $$
  • 综上所述,训练时下面的式子训练:
    $$ p’ = \frac{1}{1+e^{-(wx+ln(\frac{1}{r}))}} $$

  • 预估时可以按照下面的式子预估:
    $$ p = \frac{1}{1+e^{-wx}} $$

训练后修正的推导

  • 由两者的定义可以推导如下:
    $$
    \frac{p}{1-p} = \frac{N_p}{N_n} \\
    \frac{p’}{1-p’} = \frac{1}{r}\cdot\frac{N_p}{N_n} \\
    $$
  • 进一步有:
    $$
    \frac{p’}{1-p’} = \frac{1}{r}\cdot\frac{p}{1-p} \\
    \frac{p}{1-p} = r \cdot \frac{p’}{1-p’} = \frac{r \cdot p’}{1-p’} \\
    $$
  • 调整位置得到:
    $$
    \frac{1-p}{p} = \frac{1-p’}{r \cdot p’} \\
    $$
  • 即:
    $$
    \frac{1}{p} - 1 = \frac{1-p’}{r \cdot p’} \\
    $$
  • 进而有:
    $$
    \frac{1}{p} = \frac{r \cdot p’+1-p’}{r \cdot p’} \\
    $$
  • 最终得到:
    $$
    p = \frac{r \cdot p’}{r \cdot p’+1-p’} \\
    p = \frac{ p’}{p’+\frac{1-p’}{r}}\\
    $$
  • 综上所述,训练时下面的式子训练:
    $$ p’ = \frac{1}{1+e^{-wx}} $$
  • 预估时可以按照下面的式子预估:
    $$ p = \frac{ p’}{p’+\frac{1-p’}{r}} $$

DL——NNLM

神经网络语言模型(Nerual Network Language Model, NNLM)

参考论文: A Neural Probabilistic Language Model
参考博客: 神经网路语言模型(NNLM)的理解


概率模型

  • 传统的 \(n\) 元语言模型:
    $$(p(w_{t}|w_{t-(n-1)},…,w_{t-1}))$$
  • 一般来说可以通过前 \(n-1\) 个词将预测第 \(n\) 个词的概率分布

NNLM模型原理

  • NNLM 模型直接通过一个神经网络结构对 \(n\) 元条件概率进行评估

模型结构

  • NNLM 网络结构图如下:

数据集预处理

  • 对于给定的预料库,我们需要生成训练数据集(大量训练样本的集合),单个样本如下是长度为n的序列 \((w_{1},…,w_{n})\),其中 \((w_{1},…,w_{n-1})\) 对应训练样本特征值,训练样本标记为 \(w_{n}\),通常可以用One-Hot编码(一个维度为|V|的向量)

模型分析

模型分析主要介绍前向传播过程

输入与输出
  • 将构造的数据集作为训练样本集
  • 其中每个样本输入为 \((w_{1},…,w_{n-1})\),输出为一个向量(维度为 |V|),向量代表词的分布,该分布应该与词 \(w_{n}\) 的 One-Hot 编码(也是一个 |V| 维向量)尽量匹配,输出误差就是这两个向量的差异大小(不同损失函数均通过将上述两个向量作为输入,输出一个标量等(也可能 \(n\) 为向量,此时按照不同维度分别计算,或者是其他的值)从而实现当前样本损失的计算
模型结构分析
  • \(x=(C_{j|w_{j}=w_{1}};…;C_{j|w_{j}=w_{n-1}})\)

    • \(x\) 为输入向量, \(x\in R^{(n-1)m}\),\(x\) 是词序列 \((w_{1},…,w_{n-1})\) 对应的拼接向量,其中每个词都会先被矩阵C映射成一个m维的向量,将 \((n-1)\) 维的向量拼接起来就得到了 \(x\)
  • \(y=b+Wx+Utanh(d+Hx)\)

    • \(y\) 为输出向量, \(y\in R^{|V|}\), \(y_{i}\) 表示 \(w_{i}\) 是第 \(n\) 个单词的概率
模型参数分析
  • C:映射矩阵 \(C\in R^{|V|\times m}\),其中矩阵的第 \(j\) 行 \(C_{j}\) 是词 \(w_{j}\) 对应的特征向量, \(m\) 为特征向量的维度

  • H:输入层到隐含层的权重矩阵 \(H\in R^{(n-1)m\times h}\),其中 \(h\) 为隐含层神经元的数量

  • W:输入层到输出层的权重矩阵 \(W\in R^{(n-1)m\times |V|}\), \(W\) 是可选参数,对应模型结构图中的绿色虚线,如果输入层输出层不直接相连,则直接令 \(W=0\) 即可

  • U:隐含层到输出层的权重矩阵 \(U\in R^{h\times |V|}\)

  • b:输出层的偏执参数

  • d:隐含层的偏执参数

模型训练

损失函数
  • 似然函数:\(L(\theta)=\prod_{t=1}^{T} p(w_{t-(n-1)},…,w_{t-1},w_{t}|\theta)+R(\theta)\)
    • T 为训练集 D 中的样本总数,即 \(T=|D|\)
    • \(R(\theta)\) 是正则项
  • 对数似然函数:\(L(\theta)=\sum_{t=1}^{T}\log(p(w_{t-(n-1)},…,w_{t-1},w_{t})|\theta) + R(\theta)\),其中T为训练集 D 中的样本总数,即 \(T=|D|\)
  • 我们选择优化对数似然函数,原因如下:
    • 似然函数连乘操作造成浮点数溢出,乘积越来越小(概率值都在[0,1]之间)
    • 似然函数连乘操作耗时大于对数似然函数的连加操作
    • 取对数的操作可以同时把函数中其他的指数项(比如出现在正则项 \(R(\theta)\) 中)中的处理成连加,减少运算量
    • 对数似然函数的单调性与似然函数相同,最大化对数似然函数等价于最大化似然函数(最重要的一点)
训练目标
  • 最大化对数似然函数(等价于最大化似然函数)
参数迭代
  • 使用梯度上升法,每轮迭代时朝着正梯度方向移动
    • \(\theta=\theta+\lambda\frac{\partial L(\theta)}{\partial\theta}\)
    • \(\lambda\) 为步长

总结

  • NNLM 模型使用了低维连续的词向量对上文进行表示,这解决了词袋模型带来的数据稀疏、语义鸿沟等问题
  • 相比传统模型,NNLM 是一种更好的n元语言模型(NNLM的 \(n\) 元不是由神经元决定,而是在根据语料库生成训练数据时单个训练样本中包含的词数,也就是窗口大小)
    • n元模型指的是跟军前 \(n-1\) 个词预测第 \(n\) 个词的语言模型,而不是根据前 \(n\) 个词生成第 \(n+1\) 个词的模型
  • 根据相似的上下文语境,NNLM 模型可以预测出相似的目标词,而传统模型无法做到这一点

DL——Softmax求导

关键词:Softmax梯度,Softmax求导


softmax函数定义

  • 对于输入向量 \( x = [x_1, x_2, \dots, x_n] \),softmax函数将其映射为概率分布:
    $$
    \sigma(x)_i = \frac{e^{x_i} }{\sum_{j=1}^n e^{x_j} }
    $$
    • 其中 \( \sigma(x)_i \) 表示第 \( i \) 个元素的输出,满足 \( \sum_{i=1}^n \sigma(x)_i = 1 \)

求导推导(分情况讨论)

  • 设 \( s_i = \sigma(x)_i \),目标是计算 \( \frac{\partial s_i}{\partial x_j} \),分两种情况

情况1:\( i = j \)(自变量对自身求导)

  • 展开表达式:
    $$
    s_i = \frac{e^{x_i} }{\sum_{k=1}^n e^{x_k} } = \frac{e^{x_i} }{S}, \quad \text{Where } S = \sum_{k=1}^n e^{x_k}
    $$
  • 利用商数法则求导:
    $$
    \frac{\partial s_i}{\partial x_i} = \frac{e^{x_i} \cdot S - e^{x_i} \cdot e^{x_i} }{S^2} = \frac{e^{x_i}(S - e^{x_i})}{S^2} = \frac{e^{x_i} }{S} \cdot \frac{S - e^{x_i} }{S} = s_i(1 - s_i)
    $$

情况2:\( i \neq j \)(自变量对其他元素求导)

  • 此时 \( x_j \) 出现在分母 \( S \) 中,分子与 \( x_j \) 无关:
    $$
    \frac{\partial s_i}{\partial x_j} = \frac{0 \cdot S - e^{x_i} \cdot e^{x_j} }{S^2} = -\frac{e^{x_i} e^{x_j} }{S^2} = -\frac{e^{x_i} }{S} \cdot \frac{e^{x_j} }{S} = -s_i s_j
    $$

矩阵形式(向量求导)

  • 若输入为向量 \( \mathbf{x} \),输出为向量 \( \mathbf{s} = \sigma(\mathbf{x}) \),则其雅可比矩阵为:
    $$
    \frac{\partial \mathbf{s} }{\partial \mathbf{x}^T} = \left[ \frac{\partial s_i}{\partial x_j} \right] = \mathbf{S} - \mathbf{s s}^T
    $$
    • \( \mathbf{S} \) 是对角矩阵,对角线元素为 \( s_i \);
    • \( \mathbf{s s}^T \) 是外积矩阵
  • 对角矩阵 \(\mathbf{S}\) :
    $$
    \mathbf{S} = \begin{bmatrix}
    s_1 & 0 & \cdots & 0 \\
    0 & s_2 & \cdots & 0 \\
    \vdots & \vdots & \ddots & \vdots \\
    0 & 0 & \cdots & s_n
    \end{bmatrix}
    $$
  • 外积矩阵 \(\mathbf{s s}^T\) :
    $$
    \mathbf{s s}^T =
    \begin{bmatrix}
    s_1 \\
    s_2 \\
    \vdots \\
    s_n
    \end{bmatrix}
    \begin{bmatrix}
    s_1 & s_2 & \cdots & s_n
    \end{bmatrix} =
    \begin{bmatrix}
    s_1 s_1 & s_1 s_2 & \cdots & s_1 s_n \\
    s_2 s_1 & s_2 s_2 & \cdots & s_2 s_n \\
    \vdots & \vdots & \ddots & \vdots \\
    s_n s_1 & s_n s_2 & \cdots & s_n s_n
    \end{bmatrix}
    $$
  • 最终梯度形式 :
    $$
    \frac{\partial \mathbf{s} }{\partial \mathbf{x}^T} = \mathbf{S} - \mathbf{s s}^T = \begin{bmatrix}
    s_1 & 0 & \cdots & 0 \\
    0 & s_2 & \cdots & 0 \\
    \vdots & \vdots & \ddots & \vdots \\
    0 & 0 & \cdots & s_n
    \end{bmatrix} -
    \begin{bmatrix}
    s_1 s_1 & s_1 s_2 & \cdots & s_1 s_n \\
    s_2 s_1 & s_2 s_2 & \cdots & s_2 s_n \\
    \vdots & \vdots & \ddots & \vdots \\
    s_n s_1 & s_n s_2 & \cdots & s_n s_n
    \end{bmatrix}
    $$

一些总结

  • softmax 函数的矩阵形式导数可表示为 对角矩阵与外积矩阵的差
    • 核心结论:“对角线元素为 \(s_i-s_i s_i\),非对角线元素为 \(-s_i s_j\)”的规律
  • 越接近 one-hot 的预估,其梯度越接近于 0

DL——WandB使用笔记

  • 参考链接:
    • wandb使用教程-知乎专栏

整体介绍

  • WandB(Weights & Biases)是一个用于机器学习/深度学习项目的实验跟踪、可视化和模型管理的工具
    • 实验跟踪 :超参数、训练 Loss、评估 Loss 和其他自定义指标
      • 注:可实时更新图表和指标
    • 可视化 :生成如 Loss 曲线和 AUC 曲线等,同时可以可视化权重分布或特征
      • 注:还可以使用 plt 等自定义曲线并上报在 WandB 上生成
    • 模型管理 :保存和共享模型的所有相关文件,包括权重文件、配置文件和环境设置,方便重新训练或共享
      • 注:支持模型版本控制,可对模型版本进行跟踪,比较不同模型的性能
    • 团队协作 :团队成员可以查看实验结果和进度
    • 资源监控 :可以监控GPU使用率、内存占用等系统资源
      • 注:原生的 Torch 等没有这种 Profile 功能
  • WandB 具有强大的兼容性 :
    • 适配 Jupyter、TensorFlow、PyTorch、Keras、Scikit-learn、LightGBM、XGBoost 等多种框架

基本使用流程介绍

  • 首先在wandb官网注册账号并获取API key,使用命令wandb login进行登录
  • 使用wandb.init()启动新项目,通过wandb.config跟踪超参数
  • 在训练过程中,使用wandb.log()记录指标,使用wandb.summary()添加最终结果
  • 最后使用wandb.finish()结束实验
  • 基本代码示例见附录

WandB API wandb.log()

整体定义

  • wandb.log() API用于记录和可视化实验指标、模型参数等数据,其基本定义为:
    1
    wandb.log(data, step=None, commit=True, sync=False)

data参数(必需)

  • 字典(dict)类型,指定要记录的数据,键为字符串(指标名称),值可以是多种类型:
    • 原生类型 :如 int, float等,用于记录损失、准确率等随训练迭代变化的值
    • 图像 :wandb.Image() 对象,上传图片后可以根据 step 拉杆选择指定 step 的图片查看
    • 表格 :wandb.Table() 对象
    • 直方图 :wandb.Histogram() 对象
    • 其他 WandB 对象 :如音频、视频、文本等均可
  • 代码示例:
    1
    2
    3
    4
    5
    # 记录标量值
    wandb.log({"loss": 0.235, "accuracy": 0.92})
    # 记录图像
    img = wandb.Image("path/to/image.jpg", caption="Sample Image")
    wandb.log({"examples": img})

step参数(可选)重点参数

  • 类型为整数(int),用于手动指定当前记录的全局步数(如迭代次数、轮数)。若不指定,WandB 会自动递增
  • 注意点1 :若 step 参数缺失,则每次调用都会自动生成新的 step(step 自动加 1)
    • 常见问题:一个 step 中,多次调用 wandb.log() 但不明确传入 step 参数,此时模型会错误的认为每次调用 wandb.log() 都是一个新的 step
  • 注意点2 :新增的 step 必须大于等于已有 step 的最大值
    • 若开始调用时使用了 step=400 的值,则以后再调用 step=300 时是不会记录数据的,如果是依次调用 step=1,step=10,step=8,则仅保留step=1 和 step=10 的数据(step=8会消失)
    • 新增的最小单元是:指标 x step;也就是说,无论是指标还是 step 变化,都算是新增
    • 任意指标新增 step 时,都不能小于当前所有指标的最大 step,但是如果这个指标的这个 step 已经存在,则是可以被修改的
  • 注意点3 :多次上传同一 step 会覆盖之前的值
    • 重复上传相同 step ,且仅会覆盖之前的值(仅针对相同指标)
  • 使用建议:在需要管理多个指标时,建议加上 step 参数

commit参数(可选)

  • 布尔值(bool),默认 True,用于决定是否立即将当前日志提交到 WandB 服务器
    • 若为 False,则缓存日志,直到下一次 commit=True 或手动调用 wandb.log()

sync参数(可选)

  • 布尔值(bool),默认 False,用于决定是否强制立即同步所有缓存的日志到服务器(即使 commit=False)

使用技巧笔记

  • 使用 wandb.init(mode="offline") 可在本地保存日志,后续手动同步
  • WandB 免费版有每月 10GB 存储限制,大量图片可能快速耗尽配额,建议上传前压缩图片(如 wandb.Image(img, mode="L") 转为灰度图)
  • WandB 可以部署到本地 Docker 上以保证数据安全

附录:WandB会存储哪些信息?

config.yaml

  • 存储训练任务的配置参数(超参数),例如:
    • 模型架构参数(如层数、神经元数量)
    • 训练参数(学习率、批次大小、迭代次数)
    • 数据路径、预处理设置
    • 优化器配置(如SGD、Adam的参数)
    • Python 版本信息
  • 用于记录实验的完整配置,便于复现和对比不同实验
  • 注:wandb.config 中配置的参数都会在 config.yaml 中出现

output.log

  • 包含训练过程中的标准输出日志
  • 注意:初始化了 WandB 后,标准输出会自动记录下来并上传

requirements.txt

  • 记录项目依赖的 Python 库及其版本,例如:

    1
    2
    3
    torch==2.0.0
    numpy==1.24.0
    pandas==2.0.3
  • 用于确保实验环境可复现,其他用户可通过该文件安装相同依赖

  • 与本地 pip list 结果完全一致

wandb-metadata.json

  • 包含 WandB 自动生成的元数据,例如:
    • 实验启动时间、持续时长
    • 运行环境信息(操作系统、Python版本)
    • 硬件信息(CPU/GPU型号、内存)
    • 代码仓库信息(如Git commit哈希)
  • 用于记录实验的环境上下文,便于追溯和管理
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "os": "macOS-14.4.1-x86_64-i386-64bit",
    "python": "CPython 3.10.0",
    "startedAt": "2024-05-27T06:36:31.346712Z",
    "program": "/Users/xxx/wandb_demo.py",
    "email": "xxx@gmail.com",
    "root": "/Users/xxx/xxx/",
    "host": "MBP-xxx.local",
    "executable": "/Users/xxx/envs/xxx/bin/python"
    }

wandb-summary.json

  • 存储训练过程中的关键指标 Summary
  • 用于快速查看实验结果,无需解析完整日志

附录:代码示例

  • 一个简单的使用 WandB 的代码示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import random
    import wandb
    import time

    # Start a new wandb run to track this script.
    run = wandb.init(
    # entity="xxx", 不指定该项时默认走登录账号默认的组织
    project="wandb-demo-project", # Set the wandb project where this run will be logged.
    name="jiahong_sleep_log", # exp name,如果不指定则默认生成一个
    config={ # Track hyperparameters and run metadata. 会存储到 config.yaml中
    "learning_rate": 0.04,
    "dataset": "gsm8k",
    "epochs": 1,
    },
    )

    def model_step_simulation(step):
    acc = 1 - 2 ** -step - random.random() / step - offset
    loss = 2 ** -step + random.random() / step + offset
    return acc, loss

    # Simulate training.
    steps = 50
    offset = random.random() / 5
    for step in range(2, steps):
    acc, loss = model_step_simulation(step) # 真实场景这里会进行模型训练
    time.sleep(2)

    # Log metrics to wandb.
    wandb.log({"acc": acc, "loss": loss}) # 上报 scalar 图,这里会生成两个图
    print(f"step: {step}, loss: {loss}") # 打印到标准输出,可在 output.log 中查看

    # Finish the run and upload any remaining data.
    wandb.finish()

DL——为什么Dropout能防止过拟合

  • 参考博客: https://blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/81976571

关于Dropout

  • 用途是防止过拟合,关于过拟合的讲解可参考
    • DL——深度学习中降低过拟合的方法
    • ML——模型的方差与偏差(机器学习中的正则化与过拟合)
  • 基本思路:
    • 训练时:随机失活部分神经元
    • 推理时:激活全部神经元

定义

  • dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。注意是暂时 ,对于随机梯度下降来说,由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。(因为每一轮被丢弃的神经元不同)

应用

  • 在CNN中防止过拟合的效果明显
  • 一般选择0.5比较好,因为0.5的时候Dropout随机生成的网络结果最多,但是实际使用中一般需要调节甚至变化
    • 亲测: 在使用VGG16模型迁移学习来分类Dogs2Cats数据集时,先使用0.5,然后再使用0.2略优于一直使用0.5的情况

Why能防止过拟合?

  • 虽然Dropout在实际应用中的确能防止过拟合,但是关于Dropout防止过拟合的原理,大家众说纷纭
  • 下面介绍两个主流的观点

组合派观点

集成学习方法论
  • 传统神经网络的缺点: 费时 , 容易过拟合
  • 过拟合是很多机器学习的通病
  • 一种修改模型的过拟合解决思路是: 采用Ensemble方法的Bagging方法(平均多个模型的结果,从而能够减少模型的方差,同时减轻过拟合)或者Boosting方法(减小模型的偏差,同时能减轻过拟合?[待更新]),即训练多个模型做组合
  • 但是解决了过拟合后, 费时就成为一个大问题,不仅训练起来费时,测试起来多个模型也很费时
  • Dropout能同时解决以上问题:
    • Dropout的示意图如下: 左图是原图结构,右图是加入Dropout层的
    • 从图上可以看出,有了Dropout,训练的模型就可以看成是多个模型的组合,最终预测时丢弃Dropout即可的到所有模型的组合,从而实现类似于Ensemble方法的Bagging方法,实现了多个模型的组合
动机论
  • 虽然直观上看dropout是ensemble在分类性能上的一个近似,然而实际中,dropout毕竟还是在一个神经网络上进行的,只训练出了一套模型参数。那么他到底是因何而有效呢?

  • 首先分析一个小故事

在自然界中,在中大型动物中,一般是有性繁殖,有性繁殖是指后代的基因从父母两方各继承一半。但是从直观上看,似乎无性繁殖更加合理,因为无性繁殖可以保留大段大段的优秀基因。而有性繁殖则将基因随机拆了又拆,破坏了大段基因的联合适应性。但是自然选择中毕竟没有选择无性繁殖,而选择了有性繁殖,须知物竞天择,适者生存。我们先做一个假设,那就是基因的力量在于混合的能力而非单个基因的能力。不管是有性繁殖还是无性繁殖都得遵循这个假设。为了证明有性繁殖的强大,我们先看一个概率学小知识

  • 基本思想: 有性繁殖的方式不仅仅可以将优秀的基因传下来,还可以降低基因之间的联合适应性,使得复杂的大段大段基因联合适应性变成比较小的一个一个小段基因的联合适应性

  • dropout也能达到同样的效果,它强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,达到好的效果。消除减弱了神经元节点间的联合适应性,增强了泛化能力

噪声派观点

  • 对于每一个dropout后的网络,进行训练时,相当于做了Data Augmentation ,因为,总可以找到一个样本,使得在原始的网络上也能达到dropout单元后的效果。 比如,对于某一层,dropout一些单元后,形成的结果是(1.5,0,2.5,0,1,2,0),其中0是被drop的单元,那么总能找到一个样本(新样本),使得结果也是如此。这样,每一次dropout其实都相当于增加了样本
噪声派观点总结
  • 将dropout映射回得样本训练一个完整的网络,可以达到dropout的效果
  • dropout由固定值变为一个区间,可以提高效果
  • 将dropout后的表示映射回输入空间时,并不能找到一个样本 x 使得所有层都能满足dropout的结果,但可以为每一层都找到一个样本,这样,对于每一个dropout,都可以找到一组样本可以模拟结果

Dropout其他需要注意的点

  • 数据量小的时候,dropout效果不好,数据量大了,dropout效果好
  • dropout的缺点就在于训练时间是没有dropout网络的2-3倍

Dropout训练和预测阶段输出分布一致性保证

  • 保持分布一致的必要性 :Dropout操作会改变神经元输出分布,在 Dropout 的训练和预测阶段保证输出均值一致,能使得模型在训练和预测时具有相同的统计特性,从而提高模型的泛化能力
  • 方式 :通过调整训练和预测阶段的神经元输出比例,可以保证确保两者输出分布的一致性实现方式
  • 方法一:训练阶段缩放(Inverted Dropout)
    • 训练阶段:以概率 \(p\) 失活部分神经元,同时对被激活的所有神经元,对输出均乘以 \(\frac{1}{1-p}\)
    • 预测阶段:在预测阶段,不进行神经元的随机丢弃,而是直接使用训练好的模型进行推理。此时,由于在训练阶段已经对神经元的输出进行了缩放,所以在预测阶段不需要额外的操作,模型的输出均值就会与训练阶段保持一致
  • 方法二:预测阶段缩放(Vanilla Dropout)
    • 训练阶段:以概率 \(p\) 失活部分神经元,不调整输出
    • 预测阶段:激活所有神经元,且对被激活的所有神经元,对输出均乘以 \(1-p\)
  • 注:常规的Dropout实现均保证了训练和推理阶段分布一致,测试代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import torch
    import torch.nn as nn

    x = torch.randn(8)
    dropout = nn.Dropout(p=0.5)

    dropout.train()
    train_output = dropout(x)
    print("训练阶段输出:", train_output)

    dropout.eval()
    test_output = dropout(x)
    print("预测阶段输出:", test_output)

    # 训练阶段输出: tensor([ 0.0000, -1.0198, -1.4742, -0.0408, 1.8699, 0.0000, -0.0000, -0.0000])
    # 预测阶段输出: tensor([ 0.1318, -0.5099, -0.7371, -0.0204, 0.9350, 0.9006, -0.2567, -0.7391])

DL——各种梯度下降相关的优化算法

本文从梯度下降(Gradient Descent, GD)开始,讲述深度学习中的各种优化算法(优化器,Optimizer)

参考文章:【干货】深度学习必备:随机梯度下降(SGD)优化算法及可视化


三种梯度下降框架

随机梯度下降(Stochastic Gradient Descent, SGD)

核心思想
  • 每次从随机从训练集中选择一个训练样本来计算误差,进而更新模型参数
  • 单次迭代时参数移动方向可能不太精确甚至相反,但是最终会收敛
  • 单次迭代的波动也带来了一个好处,可以到达一个更好的局部最优点,甚至到达全局最优点
参数更新公式
  • 公式: \(\theta=\theta-\lambda\frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta}\)
  • 其中: \(L(\theta;x_{i};y_{i})=L(f(\theta;x_{i}),y_{i})\)

批量梯度下降(Batch Gradient Descent, BGD)

核心思想
  • 每次使用全量的训练集样本(假设共 m 个)来计算误差,进而更新模型参数
  • 每次参数能够朝着正确的方向移动
  • 每次遍历所有数据,耗费时间较长
参数更新公式
  • 公式:\(\theta=\theta-\lambda\frac{\partial L(\theta;x_{1:m};y_{1:m})}{\partial \theta}\)
  • 一般来说:\(L(\theta;x_{1:m};y_{1:m}) = \frac{1}{m}\sum_{i=1}^{m} L(\theta;x_{i};y_{i})\)

小批量梯度下降(Mini-Batch Gradient Descent, MBGD)

核心思想
  • 每次从随机从训练集中选择k(k < m)个训练样本来计算误差,进而更新模型参数
  • 介于SGD和BGD之间
    • 波动小
    • 内存占用也相对较小
参数更新公式
  • 公式: \(\theta=\theta-\lambda\frac{\partial L(\theta;x_{i:i+k};y_{i:i+k})}{\partial \theta}\)
  • 一般来说: \(L(\theta;x_{1:k};y_{1:k}) = \frac{1}{k}\sum_{i=1}^{k} L(\theta;x_{i};y_{i})\)

总结

优点
  • 梯度下降算法应用广泛,算法效果很好
缺点
学习速率
  • 大小很难确定,太大容易震荡,太小则收敛太慢
  • 学习速率一般为定值,有时候会实现为逐步衰减
  • 但是无论如何,都需要事前固定一个值,因此无法自适应不同的数据集特点
局部最优
  • 对于非凸的目标函数,容易陷入局部极值点中
  • 比局部极值点更严重的问题:有时候会嵌入鞍点?

SGD 算法的优化

Momentum法(动量法)

核心思想
  • 考虑一种情况,在峡谷地区(某些方向比另一些方向陡峭很多)
    • SGD (或者 MBGD,实际上,SGD 是特殊的 MBGD,平时可以认为这两者是相同的东西)会在这些放附近振荡,从而导致收敛速度变慢
    • 这里最好的例子是鞍点,鞍点出的形状像一个马鞍,一个方向两头上翘,一个方向两头下垂,当上翘的方向比下垂的方向陡峭很多时,SDG和MDG等方法容易在上翘方向上震荡
  • 此时动量可以使得
    • 当前梯度方向与上一次梯度方向相同的地方进行加强,从而加快收敛速度
    • 当前梯度方向与上一次梯度方向不同的地方进行削减,从而减少振荡
  • 动量可以理解为一个从山顶滚下的小球,遇到新的力(当前梯度)时,会结合之前的梯度方向决定接下来的运动方向
参数更新公式
  • 公式:\(\theta=\theta-m_{t}\)
    • \(m_{t}\) 表示当前下降方向, \(m_{t-1}\) 表示上一次的下降方向
    • \(m_{t}=\gamma m_{t-1}+\lambda\frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta}\)
    • \(\gamma<1\),值一般取0.9
    • \(\gamma m_{t-1}\) 是动量项
    • \(\gamma\) 是衰减量
    • \(\lambda\) 是学习率
图示
  • 动量相关图示:
小结
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本 \({x_{1},…,x_{m}}\),以及相关的输出 \({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新v和参数 \(\theta\)

NAG,涅斯捷罗夫梯度加速法(Nesterov Accelerated Gradient)

核心思想
  • 继续考虑普通的 SDG 算法,添加了 Momentum,此时从山顶滚下的球会盲目的选择斜坡
  • 更好的方式是在遇到向上的斜坡时减慢速度
  • NAG在计算梯度时首先获取(近似获得)未来的参数而不是当前参数,然后计算未来参数对应的损失函数的梯度
  • NAG在预测了未来的梯度后,根据未来(\(\theta - \gamma m_{t-1}\))梯度方向和之前梯度的方向决定当前的方向, 这样可以保证在遇到下一点为上升斜坡时适当减慢当前点的速度(否则可能由于惯性走上斜坡, 提前知道 \(\theta - \gamma m_{t-1}\) 处的梯度, 从而保证不要走上去), 从而找到了比Momentum超前的更新方向
  • 对比: Momentum是根据当前梯度方向和之前梯度方向决定当前的方向
参数更新公式
  • 公式:\(\theta=\theta-m_{t}\)
    • \(m_{t}=\gamma m_{t-1}+\lambda\frac{\partial L(\theta - \gamma v_{t-1};x_{i};y_{i})}{\partial \theta}\)
    • NAG使用的是未来的梯度方向(Momentum使用的是当前梯度方向)和之前的梯度方向
图示
  • Momentum(动量)法首先计算当前的梯度值(小蓝色向量),然后在更新的积累向量(大蓝色向量)方向前进一大步
  • NAG 法则首先(试探性地)在之前积累的梯度方向(棕色向量)前进一大步,再根据当前地情况修正,以得到最终的前进方向(绿色向量)
  • 这种基于预测的更新方法,使我们避免过快地前进,并提高了算法地响应能力(responsiveness),大大改进了 RNN 在一些任务上的表现
    • 公式中 \(-\gamma m_{t-1}\) 对应BC向量
    • \(\theta-\gamma m_{t-1}\) 就对应C点(参数)
小结
  • Momentum和NAG法可以使得参数更新过程中根据随时函数的斜率自适应的学习,从而加速SGD的收敛
  • 实际应用中,NAG将比Momentum收敛快很多
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本 \({x_{1},…,x_{m}}\),以及相关的输出 \({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新 v 和参数 \(\theta\)

Adagrad

核心思想
  • 对于较少出现的特征,使用较大的学习率更新,即对低频的参数给予更大的更新
  • 对于较多出现的特征,使用较小的学习率更新,即对高频的参数给予更小的更新
  • 很适合处理稀疏数据
参数更新公式
  • 计算梯度
    • 分量形式: \(g_{t,k} = \frac{\partial L(\theta;x_{i};y_{i})}{\theta}|_{\theta = \theta_{t-1,k}}\)
      • \(g_{t,k}\) 是指第t次迭代时第k个参数 \(\theta_{t-1, k}\) 的梯度
      • 有些地方会这样表达: \(g_{t,k} = \frac{\partial L(\theta_{t-1,k};x_{i};y_{i})}{\theta_{t-1,k}}\)
        • 式子中使用 \(\theta_{t-1, k}\) 在梯度中,事实上不够严谨, 容易让人误解分子分母都不是函数,而是一个确定的值, 事实上我们是先求了导数然后再带入 \(\theta = \theta_{t-1}\) 的
    • 向量形式: \(g_{t} = \frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta}|_{\theta=\theta_{t-1}}\)
  • 此时普通的SGD如下更新参数
    • 分量形式: \(\theta_{t,k} = \theta_{t-1,k} - \lambda g_{t,k}\)
    • 向量形式: \(\theta_{t} = \theta_{t-1} - \lambda g_{t}\)
  • 而Adagrad对学习率 \(\lambda\) 根据不同参数进行了修正
    • 分量形式: \(\theta_{t,k} = \theta_{t-1,k} - \frac{\lambda}{\sqrt{G_{t,kk}+\epsilon}} g_{t,k}\)
      • \(G_{t,kk}=\sum_{r=1}^{t}(g_{r,k})^{2}\)
    • 向量形式: \(\theta_{t} = \theta_{t-1} - \frac{\lambda}{\sqrt{G_{t}+\epsilon}}\bigodot g_{t}\)
      • \(G_{t}=\sum_{r=1}^{t}g_{r}\bigodot g_{r}\)
      • \(\bigodot\) 表示按照对角线上的值与对应梯度相乘
      • 进一步可以简化写为: \(G_t = G_{t-1} + g_t^2\)
        • 注意: 这里 \(g_t^2\) 是指向量按照维度分别相乘, 计算后还是原始向量维度
    • G是一个对角矩阵,对角线上的元素(\(G_{k,k}\))是从一开始到k次迭代目标函数对于参数(\(\theta_{k}\))的梯度的平方和
      • G的累计效果保证了出现次数多的参数(\(\theta_{k}\))对应的对角线上的元素(\(G_{k,k}\))大,从而得到更小的更新
    • \(\epsilon\) 是一个平滑项,用于防止分母为0
  • 总结参数更新公式:
    • \(\theta_{t} = \theta_{t-1} - \frac{\lambda}{\sqrt{G_{t}+\epsilon}} g_{t}\)
    • \(g_{t} = \frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta }|_{\theta = \theta_{t-1}}\)
    • \(G_t = G_{t-1} + g_t^2\)
小结
  • 在分母上累计了平方梯度和 ,造成训练过程中G的对角线元素越来越大 ,最终导致学习率非常小 ,甚至是无限小的值,从而学不到东西
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本 \({x_{1},…,x_{m}}\),以及相关的输出 \({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新G的每个元素,再根据G以及梯度计算参数更新量

Adadelta

核心思想
  • 是Adagrad的一个扩展,目标是解决Adagrad学习率单调下降的问题
  • 解决方案:只累计一段时间内的平方梯度值?
  • 实际上实现是累加时给前面的平方梯度和一个衰减值
  • 方法名delta的来源是选取部分
参数更新公式
  • 将矩阵G的每一项变成当前梯度平方加上过去梯度平方的衰减值(指数衰减)即可
    • 指数衰减:前n-1项的系数是衰减率的n-1次方
    • 实现指数衰减
    • 在Adagrad的基础上修改为: \(G_t = \gamma G_{t-1} + (1-\gamma)g_t^2\)
      • 注意: 这里 \(g_t^2\) 是指向量按照维度分别相乘, 计算后还是原始向量维度
    • 我们通常也把 \(G_t\) 表达为 \(E[g^2]_t\)
      • 因为修改后的 \(G_t\) 可以视为于对 \(g_t^2\) 求期望(不同的 \(t\) 概率权重不一样的分布的期望)
      • 进一步表达为: \(E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma)g_t^2\)
小结
  • 经过衰减后,G 的每一项(忽略掉平滑项 \(\epsilon\))相当于有权重的梯度均方差(Root Mean Square, RMS),后面RMSprop算法就用了这个RMS来命名
    • 均方根的定义是:对所有数求平方和,取平均值(每一项的权重根据概率分布可以不同),再开方
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本 \({x_{1},…,x_{m}}\),以及相关的输出 \({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新G的每个元素,再根据G以及梯度计算参数更新量

RMSprop(Root Mean Square prop)

核心思想
  • 一种适应性学习率方法,至今未公开发表
  • 是Adagrad的一个扩展,目标也是解决Adagrad学习率单调下降的问题
  • RMS的来源是由于分母相当于(忽略掉平滑项 \(\epsilon\))是梯度的均方根(Root Mean Squared, RMS)
参数更新公式
  • 参见Adadelta
  • RMSprop的本质是对Adadelta简单的取之前值和当前值的权重为 0.9 和 0.1 实现指数加权平均, 即 \(\gamma = 0.9\)
  • 有些地方也说RMSprop权重取的是0.5和0.5实现指数加权平均即 \(\gamma = 0.5\)
  • 学习率 \(\lambda\) 一般取值为0.001
小结
  • RMSprop是Adadelta的一种特殊形式
  • Adagrad的分母不能算是均方差(即使忽略平滑项 \(\epsilon\)),因为这里没有取平均值的操作
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本 \({x_{1},…,x_{m}}\),以及相关的输出 \({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新 G 的每个元素,再根据G以及梯度计算参数更新量

Adam(Adaptive Moment Estimation)

核心思想
  • 一种适应性学习率方法,相当于 RMSprop + Momentum + Bias Correction
  • 像 Adadelta 和 RMSprop 一样存储了梯度的平方的指数衰减平均值
  • 像 Momentum 一样保持了过去梯度的指数衰减平均值
  • Bias Correction 是为了得到期望的无偏估计
参数更新公式
  • \(\theta_{t} = \theta_{t-1} - \frac{\lambda}{\sqrt{\tilde{v}_t+\epsilon}} \tilde{m}_t\)
  • \(\tilde{v}_t=\frac{v_{t}}{1-\beta_{1}^{t}}\)
  • \(\tilde{m}_t=\frac{m_{t}}{1-\beta_{2}^{t}}\)
  • \(\lambda\) 是外层学习率,实际使用中,常常可以通过指数衰减、固定步长衰减、余弦退火衰减等学习率衰减策略更新
  • 梯度的指数衰减: \(m_{t} = \beta_{2}m_{t-1}+(1-\beta_{2})g_{t}\)
  • 梯度平方的指数衰减: \(v_{t} = \beta_{1}v_{t-1}+(1-\beta_{1})g_{t}^{2}\)
    • \(m_t\) 和 \(v_t\) 也叫作一阶动量和二阶动量,是对梯度一阶矩估计和二阶矩估计
      • 数学定义:随机变量的一阶矩是随机变量的期望 \(E[X]\),二阶矩是随机变量的方差 \(E[X-E[X]]\)
      • 其实梯度平方的期望不是梯度的方差,这只是一种近似,数学上,随机变量 \(X\) 二阶矩等价于方差,是 \(E[(X-E[X])^2] = E[X^2]-E[X]^2\),当 \(E[X]=0\) 时, \(E[X^2]\) 就是方差
      • 这种滑动平均之所以能代表期望,是因为滑动平均的思想是一种折扣平均,确实可以用来作为期望和方差的估计
    • \(m_t\) 和 \(v_t\) 可以看做是对 \(E[g]_t\) 和 \(E[g^2]_t\) 的估计
    • \(\tilde{m}_t\) 和 \(\tilde{v}_t\) 是对 \(m_t\) 和 \(v_t\) 的 Bias Correction , 这样可以近似为对对期望 \(E[g]_t\) 和 \(E[g^2]_t\) 的无偏估计
      • 注意:修正项 \(\tilde{v}_t=\frac{v_{t}}{1-\beta_{1}^{t}}\) 中的 \(\beta_{1}^{t}\) 是 \(\beta_{1}\) 的 \(t\) 次方的意思,基本思路可以理解为在每一步都尽量将梯度修正到 \(t=0\) 大小
      • 进行修正的原因是当 \(t\) 较小时, \(v_t\) 也较小,而 \(\beta\) 一般较大(0.9或者0.999),此时加权平均的结果也会很小,当 \(t\) 很大时,实际上可以不用修正了,个人理解:应该可以不用修正,只是前期训练时更新速度比较慢而已
小结
  • 超参数设定推荐
    • 梯度平方衰减率: \(\beta_{1}=0.999\)
    • 梯度动量衰减率: \(\beta_{2}=0.9\)
    • 平滑项: \(\epsilon=10e^-8=1*10^{-8}\)
    • 一阶动量 \(v\),初始化为0
    • 二阶动量 \(m\),初始化为0
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本 \({x_{1},…,x_{m}}\),以及相关的输出 \({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新 \(v\) 和 \(m\),再根据 \(v\) 和 \(m\) 以及梯度计算参数更新量

AdamW

Adam with Weight decay是Adam的一种优化

Adam中的L2正则
  • 一般的L2正则
    $$
    Loss(w) = f(w) + \frac{1}{2}\eta||w||^2
    $$
  • 权重衰减后的参数更新如下
    $$
    \begin{align}
    w &= w - \alpha\nabla Loss(w) \\
    &= w - \alpha (\nabla f(w) + \eta w) \\
    &= w - \alpha \nabla f(w) - \alpha \eta w \\
    \end{align}
    $$
  • 由于L2正则化项的存在,每次权重更新时都会减去一定比例的权重,即 \(\alpha \eta w \),这种现象叫做权重衰减(L2正则的目标就是让权重往小的方向更新,所以L2正则也叫作权重衰减)
  • L2正则也称为权重衰减,所以Adam优化的损失函数中添加L2正则的目标本应该也是为了权重衰减
  • Adam中的L2正则
    • 在每次求损失函数梯度前都计算 \(\nabla Loss(w) = \nabla f(w) + \eta w\)
    • 由于L2正则项的梯度 \(\eta w\) 也会被累加到一阶动量和二阶动量中,带有L2的Adam不再是简单的权重衰减,L2正则项还会影响到其他值的更新
    • Adam中的L2正则会产生我们不期望的结果,因为此时L2正则项影响了Adam参数的正常更新(我们想要L2做的仅仅是权重衰减,但在Adam中,L2产生了别的影响,这个不是我们想要的)
AdamW——Adam+权重衰减
  • AdamW则不直接将L2添加到损失函数中,而是显示的把权重衰减提出来,主要修改是下面两步
    • 在计算梯度时,将L2正则从损失函数中去除
    • 在更新参数时,显示增加权重衰减项
  • 相当于在更新参数时增加了L2正则,但是计算梯度时没有L2正则
  • 原始论文:Decoupled Weight Decay Regularization
    • 图中紫色是原始Adam+L2实现部分,在AdamW中会被去除;
    • 绿色是AdamW中新增的权重衰减部分(相当于更新参数时增加了L2正则项)
  • 参考链接:Adam和AdamW,从梯度下降到AdamW一文读懂机器学习优化算法
  • 目前大模型常用的就是AdamW

优化器与内存/显存

  • 训练的过程中,需要的内存/显存大小与优化器(Optimizer)有关
    • 需要存储到内存的变量包括以下几个方面
      • 梯度
      • 参数
      • 优化器状态(Optimizer States),普通 SGD 没有这一项,而Adam和AdamW则需要存储一阶动量和二阶动量
  • 优化器、参数量、内存/显存消耗、混合精度训练相关概念可参考ZeRO: Memory Optimizations Toward Training Trillion Parameter Models
    • 有些论文中也会直接将二阶动量叫做方差(Variance)或者二阶矩,因为二阶动量可以近似方差(当期望为0时)
  • ZeRO论文中指出,在混合精度训练 + Adam/AdamW 时,需要存储的变量包括
    • FP16 的参数
    • FP16 的梯度
    • FP32 的参数
    • FP32 的一阶动量
    • FP32 的二阶动量
    • 注意:动量不能使用 FP16 吗?是的,不能,因为为了精度考虑使用时还是要被转换到 FP32

各种优化方法的比较

鞍点

  • SGD optimization on saddle point

等高线表面

  • SGD optimization on loss surface contours
  • 上面两种情况都可以看出,Adagrad, Adadelta, RMSprop 几乎很快就找到了正确的方向并前进,收敛速度也相当快,而其它方法要么很慢,要么走了很多弯路才找到
  • 由图可知自适应学习率方法即 Adagrad, Adadelta, RMSprop, Adam 在这种情景下会更合适而且收敛性更好

如何选择

  • 如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam
    • 因为他们能够为出现更新次数少(确切的说是梯度累计结果小)的特征分配更高的权重
  • RMSprop, Adadelta, Adam 在很多情况下的效果是相似的
  • Adam 可解释为 RMSprop + Momentum + Bias Correction
  • 随着梯度变的稀疏,Adam 比 RMSprop 效果会好
  • 整体来讲,Adam 是最好的选择
  • 很多论文里都会用 SGD,没有 momentum 等, SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点, 在不正确的方向上来回震荡
  • 如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法

epoch 粒度的学习率调整

  • 本文中提到的常规的梯度下降方法中学习率均是在以 batch 为单位变化,即同一个 batch 学习率相同,而不同 batch 学习率可能不同
  • 深度学习中还有一类学习率调整方法,是以 epoch 为单位变化的,即同一个 epoch 学习率相同,而不同 epoch 学习率可能不同
  • 常见的 epoch 为单位变化的学习率有:
    • StepLR:间隔调整学习率
    • MultiStepLR:按设定的间隔调整学习率
    • ExponentialLR︰按指数衰减调整学习率
    • CosineAnnealingLR:以余弦函数为周期,在每个周期最大值时重置学习率
    • CosineAnnealingWarmRestarts:CosineAnnealingLR 加上 warmup
    • ReduceLROnPlateau:当某指标不再变化(下降或升高),调整学习率
  • 如果以 epoch 为单位的学习率(如 StepLR)和以 batch 为单位的学习率(如 Adam)调整同时被设置,则:
    • 两者会同时生效
    • StepLR 负责在每个 epoch开始时调整学习率
    • Adam 负责在每个 batch 开始时调整学习率

附录:关于 Adam 的更多讨论

  • 参考链接:
    • 如何理解Adam算法(Adaptive Moment Estimation)? - Summer Clover的回答 - 知乎
    • 梯度下降法的神经网络容易收敛到局部最优,为什么应用广泛? - Summer Clover的回答 - 知乎

      (1)正因为梯度下降法 容易收敛到局部最优,所以大家几乎从来不用梯度下降做非凸优化,包括 训练神经网络
      (2)正因为随机梯度下降法 容易逃离鞍点 和泛化不好的 minima(主要是 sharp minima),所以 随机梯度下降(SGD)和它的变种(比如 Momentun、Adam)才是训练神经网络最流行的方法

    • Who is Adam? 重新审视大模型 RLVR 阶段的优化器选择

Adam vs SGD 的优缺点简单总结

  • Adam 更容易逃离鞍点
  • Adam 对学习率超参数不敏感,一般在一个范围内的学习率即可实现最优,无需精确对学习率调参;但 SGD 对超参数非常敏感,不同场景下的最优超参数变化剧烈
  • Adam 收敛更快
  • Adam 需要额外存储两倍参数量的优化器状态,这对超大规模模型来说成本非常高

大模型中的 Adam 和 SGD 对比

  • 参考链接:Who is Adam? 重新审视大模型 RLVR 阶段的优化器选择
  • 注意:这里的讨论仅限于 RLVR 这种不需要更新太多参数的场景
  • TLDR:大模型中,在做 RLVR 时,由于 RLVR 任务需要修改变化的参数较少(以 1e-5 变化量为阈值,大概 0.01% 量级参数发生变化),所以使用不带 Momentum 的 SGD 优化基本就够了,能省下来 Adam 下的很多优化器状态显存占用
  • 在 RLVR 下 SGD vs Adam:
    • SGD 审下来很多优化器状态,且效果和 Adam 差不多
    • 使用 SGD 时,需要使用较大的学习率(Adam 1e-6 ~ 1e-5 是,SGD 需要 0.1 左右),且 SDG 对学习率超参敏感

DL——模型压缩技术


模型压缩

  • 模型压缩(Model Compression)技术包含模型量化、蒸馏、剪枝等
  • 有时候模型量化和蒸馏会同时使用
  • 剪枝不常见

模型量化

  • 模型量化(Model Quantization),是一种将模型中的权重和激活从浮点数(高位宽)转换为低精度(低位宽)的表示的方法,如8位整数(INT8)
  • 数值上来看,量化是将连续值离散化的过程
  • 参考链接:
    • TinyML —— 模型量化(quantization)
    • B站北大学生汇报:模型量化加速
    • QLoRA: 训练更大的GPT

线性量化与非线性量化

  • 线性量化(linear quantization),也叫仿射量化(affine quantization)或者均匀量化
    • 我们很容易给出量化公式:
      其中,r(real value)值得是量化前的值,q(quantized value)是量化后的值,s(scale)是放缩比例,z(zero point)相当于是一个偏移量
  • 非线性量化(nonlinear quantization),也叫作非均匀量化

对称量化与非对称量化

  • 对称量化(symmetric quantization):映射前后0点相同
  • 非对称量化(asymmetric quantization):映射前后0点不相同

量化粒度

  • Per-Tensor Quantization(逐张量量化):
    • 这是最简单的量化方式,对整个张量(即模型中的一个参数矩阵或输入数据)使用相同的量化参数(比如最小值和最大值,或者量化因子)
    • 由于所有值共享相同的量化参数,因此这种方法的精度较低,但计算简单,存储和传输效率高
  • Per-Channel Quantization(逐通道量化):
    • 在这种量化方式中,每个通道(对于卷积神经网络中的滤波器来说,通道通常指的是滤波器输出的不同颜色或特征)使用不同的量化参数
    • 这种方法比逐张量量化更精细,因为不同的通道可能具有不同的值范围,因此可以独立地进行量化,以保持每个通道的精度
    • 因为有研究发现不同Channel的参数量级差距较大
  • Per-Layer Quantization(逐层量化):
    • 逐层量化意味着网络中的每一层都有自己的量化参数集
    • 这种方法允许每一层根据其激活值的范围独立地进行量化,这可能比逐张量量化提供更好的精度,但比逐通道量化的计算成本要低
  • Per-Axis Quantization(逐轴量化):
    • 这种量化方式通常用于多维张量,比如二维的权重矩阵。在这种情况下,”axis”可以指特定的维度,比如行或列
    • 逐轴量化意味着沿着张量的一个或多个维度,量化参数是不同的。例如,在二维张量中,可以对每一行或每一列使用不同的量化参数

量化方式(PTQ vs QAT)

  • PTQ(Post training quantization),后训练量化,训完的模型直接量化,然后进行推理
  • QAT(Quantization aware training),量化感知训练,训练完的模型加载到内存,进行微调后再用于推理
    • LLM常用的思路就是float16训练base模型并存储,SFT时使用INT8量化并使用LoRA微调模型,然后存储LoRA参数,推理时加载base模型(INT8量化加载)和LoRA参数一起推理

量化位宽

  • 统一位宽
  • 混合精度

量化模型训练梯度

  • 梯度回传时量化是离散的,梯度为0,实际上可以设置为1,因为量化一般是阶梯函数,类似于线性的

最新的量化模型

QLoRA
  • QLoRA是一种高效的微调方法,它允许在保持完整的16位微调任务性能的同时,将内存使用量降低到足以在单个48GB GPU上微调650亿参数模型。QLoRA通过冻结的4位量化预训练语言模型向低秩适配器(Low Rank Adapters,简称LoRA)反向传播梯度。这种方法的主要贡献包括:
    • 4-bit NormalFloat (NF4):一种新的数据类型,理论上对正态分布的权重是最优的
    • Double Quantization:通过量化量化常数来减少平均内存占用
    • Paged Optimizers:使用NVIDIA统一内存特性,自动在CPU和GPU之间进行页面转换,以避免梯度检查点操作时内存不足
  • QLoRA主要用于微调训练阶段,使得在单个GPU上进行大型模型的微调成为可能,这对于资源有限的研究者和开发者来说是一个重大突破
GPTQ
  • GPTQ(Generative Pre-trained Transformer Quantization)是一种针对生成预训练Transformer模型的量化技术。GPTQ旨在解决大型GPT模型的高计算和存储成本问题。这些模型由于其庞大的规模,即使在高性能GPU上进行推理也需要大量的计算资源。GPTQ通过以下方式来提高效率:
    • 一次性权重量化:基于近似二阶信息的方法,可以在不需要重新训练的情况下压缩模型
    • 高压缩率:能够将模型量化到每个权重3或4位,同时几乎不降低准确度
    • 高效执行:允许在单个GPU上执行大型参数模型的生成推理,显著减少了所需的硬件资源
  • GPTQ通过用于部署推理阶段,用于减少模型的大小和内存占用,使得这些大型模型更易于部署和使用

模型蒸馏

  • 模型蒸馏通常也叫作知识蒸馏(Knowledge Distillation),模型蒸馏泛指整个蒸馏技术框架,两者通常可以视作同一概念,但在一些特定的场景上会有微小差别
    • 模型蒸馏更倾向于表达模型压缩的思想,强调从大模型(教师模型)迁移知识到小模型(学生模型)的过程
    • 知识蒸馏更专注于将教师模型的知识(如输出分布、中间特征)传递给学生模型,不强调模型压缩,目标是提升学生模型的泛化能力
  • 模型蒸馏(Model Distillation),主要包含Logits蒸馏和特征蒸馏两大类
  • 参考链接:知识蒸馏算法汇总

    知识蒸馏有两大类:一类是logits蒸馏,另一类是特征蒸馏。logits蒸馏指的是在softmax时使用较高的温度系数,提升负标签的信息,然后使用Student和Teacher在高温softmax下logits的KL散度作为loss。中间特征蒸馏就是强迫Student去学习Teacher某些中间层的特征,直接匹配中间的特征或学习特征之间的转换关系。例如,在特征No.1和No.2中间,知识可以表示为如何模做两者中间的转化,可以用一个矩阵让学习者产生这个矩阵,学习者和转化之间的学习关系。 这篇文章汇总了常用的知识蒸馏的论文和代码,方便后续的学习和研究

Logits蒸馏

  • 关注输出层
  • 也叫作基于反馈的知识蒸馏
  • 学生模型被训练以模仿教师模型的输出概率分布。通过最小化两个模型输出概率分布之间的KL散度(或其他相似性度量),学生模型学习教师模型的“软目标”,即对每个类别的概率预测,而不是单一的预测标签
  • 损失函数 :一般实现时,除了损失函数一般还会考虑真实的预测标签,以多分类模型为例,输出是一个概率分布(例如,经过 softmax 后的 \( K \) 维向量),教师模型和学生模型的输出概率分布可以表示为:
    • 教师模型的输出:\( P_T = [p_T^1, p_T^2, \dots, p_T^K] \)
    • 学生模型的输出:\( P_S = [p_S^1, p_S^2, \dots, p_S^K] \)
    • 损失函数包括:
      • KL散度损失 :
        $$
        L_{KD} = T^2 \cdot D_{KL}(P_T || P_S) = T^2 \cdot \sum_{i=1}^K p_T^i \log \frac{p_T^i}{p_S^i}
        $$
        • 其中 \(T\) 为维度系数
      • 交叉熵损失 :
        $$
        L_{CE} = - \sum_{i=1}^K y_i \log p_S^i
        $$
        • 其中,\( y_i \) 是真实标签的 one-hot 编码
      • 总损失 :
        $$
        L = \alpha \cdot L_{KD} + (1 - \alpha) \cdot L_{CE}
        $$

特征蒸馏

  • 关注中间层
  • 也叫作基于特征的知识蒸馏
  • 学生模型被训练以模仿教师模型在中间层的激活或特征图。这通常通过最小化两个模型对应层的特征表示之间的距离(如L2距离或cosine相似度)来实现
  • 损失函数 :特征蒸馏的总损失函数通常结合特征蒸馏损失和任务损失(如分类损失):
    $$
    L = \alpha \cdot L_{Feature} + (1 - \alpha) \cdot L_{Task}
    $$
    • 其中:
      • \( L_{Feature} \) 是特征蒸馏损失(如 MSE、余弦相似度等)
      • \( L_{Task} \) 是任务损失(如交叉熵损失)
      • \( \alpha \) 是权重系数,用于平衡两部分损失

LLM的蒸馏

  • 在A Survey on Model Compression for Large Language Models中,将知识蒸馏分为黑盒知识蒸馏和白盒知识蒸馏两类
  • 黑盒知识蒸馏 :通常表示对ChatGPT,GPT4等黑盒LLM模型教师模型进行蒸馏,黑盒蒸馏使用教师模型的输出token作为监督来优化学生模型,蒸馏手段包含了基于CoT(Chain-of-Thought)的知识蒸馏、基于语境学习(In-Context Learning)的蒸馏和基于指令跟随(Insruction Following)的知识蒸馏
  • 白盒知识蒸馏 :通常表示对白盒LLM教师模型内部的结构和知识进行蒸馏
  • Deepseek-R1技术报告中的蒸馏是直接采用Deepseek-R1训练过程中收集到的80W数据对开源的模型(如qwen,llama等)做SFT

模型剪枝

  • Model Pruning
  • 通过删减网络结构然后调整分布实现模型压缩,剪枝方法不常用

DL——深度学习中参数的初始化策略


整体说明

  • 在深度学习中,权重/参数初始化策略对模型的训练效率和性能有着至关重要的影响
  • 合适的初始化方法能够缓解梯度消失或梯度爆炸问题,加速收敛 ,并提高模型的泛化能力

随机初始化(Random Initialization)

  • 使用小的随机值初始化参数,可以打破 0 初始化带来的对称性
  • torch 生成随机参数的函数有很多,常见的有:
    • torch.randn():从标准正态分布采样
    • torch.rand():从均匀分布采样

Xavier初始化(Glorot初始化)

  • 目标是保持输入和输出的方差一致 ,缓解梯度消失/爆炸
  • 适用于激活函数为 Sigmoid 或 Tanh 的网络(这两种激活函数容易导致梯度消失等问题)
  • Xavier初始化的公式如下:
    • 均匀分布:
      $$w \sim U\left[-\frac{\sqrt{6} }{\sqrt{n_{in} + n_{out} } }, \frac{\sqrt{6} }{\sqrt{n_{in} + n_{out} } }\right]$$
    • 正态分布:
      $$w \sim \mathcal{N}\left(0, \sqrt{\frac{2}{n_{in} + n_{out} } }\right)$$
  • Xavier初始化的PyTorch实现 :
    1
    2
    3
    4
    import torch.nn as nn
    linear_layer = nn.Linear(3, 4)
    nn.init.xavier_uniform_(linear_layer.weight) # 均匀分布
    nn.init.xavier_normal_(linear_layer.weight) # 正态分布

He初始化(Kaiming初始化)

  • He初始化是专为 ReLU 激活函数设计,可保持每一层的方差一致

  • 适用于 ReLU 及其变种(LeakyReLU、PReLU 等)网络

  • He初始化的公式 :

    • 均匀分布公式:
      $$w \sim U\left[-\sqrt{\frac{6}{n_{in} } }, \sqrt{\frac{6}{n_{in} } }\right]$$
    • 正态分布公式:
      $$w \sim \mathcal{N}\left(0, \sqrt{\frac{2}{n_{in} } }\right)$$
  • He初始化的PyTorch实现 :

    1
    2
    3
    4
    import torch.nn as nn
    linear_layer = nn.Linear(3, 4)
    nn.init.kaiming_uniform_(linear_layer.weight, mode='fan_in', nonlinearity='relu') # 均匀分布
    nn.init.kaiming_normal_(linear_layer.weight, mode='fan_in', nonlinearity='relu') # 正态分布
    • mode:’fan_in’(保持输入方差)或’fan_out’(保持输出方差)
    • nonlinearity:激活函数类型(如’relu’或’leaky_relu’)

预训练初始化

  • 在模型越来越大的今天,常常使用在大规模数据集上预训练的模型参数初始化当前模型

  • 预训练初始化适用于迁移学习场景

  • 从远程下载并加载 ResNet-18 模型(17 个卷积层 + 1 个全连接层)的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import torchvision.models as models
    # 加载预训练的ResNet模型(`pretrained=True`时,会从远程下载并加载预训练好的参数)
    resnet = models.resnet18(pretrained=True)

    # 使用预训练参数初始化自定义模型
    class CustomModel(nn.Module):
    def__init__(self):
    super().__init__()
    self.features = nn.Sequential(*list(resnet.children())[:-1])
    self.classifier = nn.Linear(512, 10) # 自定义分类层
    • 当 pretrained=True 时,PyTorch 会自动下载并加载远程预训练权重参数
      • 这里的 ResNet-18 模型是基于 ImageNet 数据集(包含 1000 个类别、1400 万张图像)训练的
    • 这些预训练权重已学习到通用的图像特征,可用于多种下游计算机视觉任务

正交初始化(Orthogonal Initialization)

  • 正交初始化确保权重矩阵正交,有效缓解梯度消失/爆炸

  • 常常适用于循环神经网络(RNN、LSTM、GRU)中,强化学习中(如 PPO)网络的初始化也常用

  • 正交初始化的 PyTorch 实现:

    1
    nn.init.orthogonal_(weight)
  • 对于简单网络(如浅层 MLP),正交初始化的优势可能不明显,有时标准正态分布或 Xavier初始化 也能满足需求


附录:如何选择初始化策略?

  • Xavier初始化(Glorot初始化) :适用于使用 Sigmoid/Tanh 激活函数的网络
  • He初始化(Kaiming初始化) :适用于使用 ReLU 激活函数的网络
  • 正交初始化 :循环神经网络(RNN/LSTM/GRU)或强化学习网络中
  • 预训练初始化 :迁移学习场景,复用之前训练好的参数
  • 最后:常用配置 :一般使用 He初始化 或 Xavier初始化 ,强化学习用 正交初始化

附录:为什么参数不能初始化为全0?

  • 零初始化(Zero Initialization)会导致同一隐藏层的神经元互相对称,可以通过递推法证明,不管迭代多少次,此时所有的神经元都将计算完全相同的函数
  • 并不会因为参数都为0就导致所有神经元死亡!
  • 注:要特别注意,除了一些特殊的场景外,不推荐全初始化为0
    • 比如 LoRA 的两个网络,其中一个初始为0更好(注意,另一个也不能为0)

附录:为什么参数不能初始化为太大的数值?

  • 因为参数太大会导致sigmoid(z)或tanh(z)中的z太大,从而导致梯度太小而更新太慢
  • 如果网络中完全没有sigmoid和tanh等激活函数,那就还好,但是要注意,二分类中使用sigmoid函数于输出层时也不应该将参数初始化太大

Git——HuggingFace的使用


LFS 环境安装

  • 配置 HuggingFace 环境需要安装 git lfs
  • LFS全称 Large File Storge,即大文件存储,可以帮助我们管理比较大的文件
  • Ubuntu安装
    1
    2
    3
    curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
    sudo apt-get install git-lfs
    git lfs install

拉取 HuggingFace 项目(包含 git 文件)

  • 执行拉取操作

    1
    git clone https://huggingface.co/xxx/xxx
    • 该命令可在 HuggingFace 网站每个项目的首页 ... 选项的 clone repository 中查找到
    • git clone 命令可以将整个项目下载下来,但是下载大的文件时可能会特别慢,最佳实践是:
      • 使用 git clone xxx 下载项目文件,待除大文件外的其他文件下载结束时结束命令
        • 一些未完成的项目会出现在 .git/lfs/incomplete 文件夹下,结束命令后可以删除该文件夹,该文件夹可能会存储较大的未完成文件,浪费存储
      • 使用网页或其他方式下载较大的模型权重文件到指定目录下

使用 huggingface-cli 命令下载数据和模型(基础)

  • 安装

    1
    pip install huggingface-hub
  • 模型下载

    1
    huggingface-cli download Qwen/Qwen2.5-0.5B-Instruct  --local-dir ./Qwen2.5-0.5B-Instruct
  • 数据下载

    1
    huggingface-cli download openai/gsm8k --repo-type=dataset --local-dir ./gsm8k --local-dir-use-symlinks False
    • 重点:增加了 --repo-type=dataset 参数,否则默认是搜索模型
  • --local-dir-use-symlinks 的特别说明:

    • 当 --local-dir-use-symlinks 设置为 True(默认值)时,工具会优先创建符号链接指向缓存目录中的文件,而不是直接复制文件到 --local-dir 指定的目录。这样可以节省磁盘空间,但依赖于系统对符号链接的支持
    • 当 --local-dir-use-symlinks 设置为 False 时,工具会直接将文件复制(而非创建符号链接)到 --local-dir 指定的目录。这会占用更多磁盘空间,但保证了文件在目标目录中是真实存在的实体文件,不依赖符号链接机制,兼容性更好(尤其在 Windows 系统或某些不支持符号链接的环境中)
    • 实际上,ubuntu 下,亲测这个参数也可以不加,加不加没发现不同

使用 hf 命令下载数据和模型(大模型推荐,并行化)

  • 安装

    1
    pip install -U "huggingface_hub[cli]"
  • 用法与 huggingface-cli 几乎完全相同,但速度更快

  • 模型下载:

    1
    hf download Qwen/Qwen2.5-0.5B-Instruct  --local-dir ./Qwen2.5-0.5B-Instruct
  • 数据下载:

    1
    hf download openai/gsm8k --local-dir ./gsm8k --repo-type=dataset --local-dir-use-symlinks False

其他需要注意的点

  • 如果直接使用 transformers 包下载 Bert 等预训练模型时,默认安装在 ~/.cache/huggingface/hub 文件夹下

附录:使用 modelscope 下载模型(国内推荐)

  • 安装
    ‵‵‵bash
    pip install modelscope

    1
    2
    3
    * 模型下载命令
    ```bash
    modelscope download --model Qwen/Qwen2.5-0.5B-Instruct --local_dir ./Qwen2.5-0.5B-Instruct
    • 注意:这里不能使用 --local-dir,必须是 --local_dir
1…414243…63
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

630 posts
53 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4