提克破事水


  • Home

  • About

  • Tags

  • Categories

  • Archives

  • Search

27-移除元素

Posted on 2023-04-24 | In Leetcode |

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

记录一个val,转换为与27-移除元素相同的解决方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow,quick = 0,0
val = None
while quick < len(nums):
if val is None:
val = nums[slow]
slow += 1
quick += 1
continue
if nums[quick] != val:
nums[slow] = nums[quick]
val = nums[slow]
slow += 1
quick += 1
return slow

27-移除元素

Posted on 2023-04-24 | In Leetcode |

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-element
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

一个一个的找,fast用来找与val不同的值,slow用于保存当前找到的值。找到了fast就会与slow不同,不同则将用fast将slow覆盖。
在本题中,实际上每次都会把slow这个位置的数值返回到结果集合中,所以leetcode识别的结果是每一次slow对应的数值。

1
2
3
4
5
6
7
8
9
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slow,fast = 0,0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow

基于大规模预训练开放域中文对话系统关键技术研究

Posted on 2023-04-23 | In 论文浅读 |

EVA2.0:Investigating Open-Domain Chinese Dialogue Systems with Large-Scale Pre-Training

摘要

在本文中,我们进行了广泛的实验来调查这些未被充分探索的因素,包括数据质量控制、模型架构设计、训练方法和解码策略。我们提出了EVA2.0,一个具有28亿个参数的大规模预训练开放域中文对话模型,并将我们的模型和代码公开。据我们所知,EVA2.0是最大的开源中文对话模型。自动和人工评估表明,我们的模型明显优于其他开源模型。我们还通过列举一些失效案例来讨论这项工作的局限性,并提出一些未来的方向。

1 引言

预训练对话模型可以从大规模语料中或缺通用的对话技巧和各种各样的知识,在下游任务中微调这些模型通常要比自行训练的模型效果更好。但是并非增加模型参数和增加训练语料就可以构建出一个出色的对话模型,其中还涉及到其他因素,比如预训练任务、解码策略、评估指标等,可能还有其他影响对话模型性能的因素尚未发现。目前出现的许多工作都未提供数据收集、数据处理过程和数据质量控制、解码策略等相关细节。通常都只是给出粗略的参数分析。
因此,本文就如何构建基于大规模预培训的开放域汉语对话系统进行了研究。我们对训练前语料库进行了详细的分析,并对模型设计、训练前方法和解码策略进行了广泛的实验。首先,我们全面分析了最大的中文对话数据集WDC-Dialogue的质量。我们发现,尽管该数据集规模庞大,但在上下文-响应相关性、语言流利性和领域多样性等方面存在严重问题。其次,我们探讨模型架构、预训练方法和解码策略的几种变体。我们的经验发现,这些因素确实对训练前的模型有一个非平凡的影响。
综合所有这些,我们首先设计了一个数据清理管道,并构建了用于大规模预训练的60GB高质量对话数据集。在此基础上,构建了参数为2.8B的开放域对话模型EVA2.0,以及参数为300M和970M的两种变体。在自动和人工评估中,2.8B EVA2.0模型显著优于其他开源生成对话模型。我们注意到,即使是300M型在自动评估中表现与EVA1.0模型相当,而只需要11%的参数。本文还通过案例分析,从不同角度分析了EVA2.0的会话能力,为未来大规模预训练开放域汉语对话系统的研究提供参考。我们的工作为汉语对话模式的研究提供了基础模型,我们相信这将极大地造福于对话研究界。

2 相关工作

大规模预训练语言模型
迄今为止,已经出现了GPT\BERT\XLNET\BART\ROBERTA\CPM\PANGU\YUAN\MENGZI\ERNIE等大规模语言模型。
预训练对话模型
除了一般的语言理解和生成,会话前培训也越来越受到关注。比如DialoGPT\LaMDA\CDial-GPT\PLATO\EVA1.0
然而,这些作品大多都没有涉及到如何构建对话模型的细节。在本文中,除了最终的模型评估外,我们还关注了大规模的预先训练的汉语对话模型的关键配方。

3 数据

在大规模的预训练中,数据从本质上影响模型的表现和行为。在本节中,我们定义了几个自动的数据质量评价指标,综合衡量从社交媒体获得的对话前训练语料库的相关性、流畅性和领域多样性。然后,我们使用这些指标来分析WDC-Dialogue (Zhou et al., 2021),发现尽管WDC-Dialogue规模很大,但存在着上下文-响应相关性、语言流利性和领域多样性等严重问题。最后,在WDC-Dialogue的基础上,设计了一个更好的数据处理管道来构建EVA2.0的预训练数据。

3.1 数据质量评估

Relevance Score
语境与反应之间的关联分数是反映对话连贯性和参与性的重要指标。我们采用未经训练和训练过的度量标准来度量我们数据集的相关性。
对于未经训练的度量,我们计算上下文和响应之间的词覆盖率作为相关性的一个方面。此外,我们对一个会话中重叠单词间隔越远的数据样本给予更高的分数,以显示对长依赖属性的偏好。在形式上,上下文(C)和反应(R)的关联得分被定义为
$$S_1 = \sum_(w_i\in C,w_j\inR)dist(w_i,w_j)^T I(w_i=w_j)$$
其中,dist(w_i,w_j)表示包含wi和wj的话语之间的索引距离。使用T来调整分数。
训练过的指标,使用LCCC数据集微调了一个BERT-BASE的二进制分类器,来识别回复与上下文之间是否“合适”。在评价中,我们使用“适当”的分类概率作为关联得分:
$$S_2 = log p(1|C,R)$$
与依赖于精确重叠的未训练度量相比,训练后的度量更好地估计了语义相关性。
Fluency Score
我们使用基于kenlm的统计模型计算数据集中每个句子的概率。对话会话中句子的平均流利度得分被定义为
$$S_3 = - \frac{1}{n}\sum_(i<=n>)logP(w_1^i,w_2^i,\cdots,w_(|u_i|)^i)$$
其中n是会话的话语数,UI = wi 1wi 2, wi∣UI∣是第i个话语。
Entertainment Tendency
中国的社交媒体平台上有很多关于娱乐明星和粉丝的不受欢迎的信息交流,这些在日常对话中是不常见的。因此,我们计算中国明星的对话比例来衡量娱乐倾向。这也在一定程度上反映了数据集的领域多样性。

3.2 数据过滤

Dataset-level Filtering
我们发现一些数据集不适合开放领域的对话。对他们的培训将导致不受欢迎的行为,如电子商务客户服务的基调。因此,我们将JDDC (Chen et al., 2020)这样的数据集从WDC-Dialogue中删除。
Context-level Filtering
由于我们的数据集主要来自社交媒体平台,一些上下文对应着相当数量的回复(例如,微博帖子及其评论)。这些回复在格式上非常相似,可能会严重损害语言模型的表现。因此,在过滤过程中,我们为每个上下文设置了一个最大回复数。
Rule-based Filtering
我们在EVA1.0中加强了基于规则的过滤程序。例如,将繁体字转换为简体字,去除不合理的多个连续标点符号,简化黑名单,以避免对一些常见的一词多义现象过于敏感。
Classifier-based Filtering
对于语料库中的每一个对话,我们计算上述定义的相关度和流利度得分,并过滤掉得分低于阈值的样本。一个会话的总分定义为S = αS1 + βS2 + γS3。在实践中,我们根据经验为不同的数据源选择不同的阈值,以适应其数据分布,使最终的数据集均衡。

3.3 数据扩展

社交媒体与开放领域对话之间的分布必然存在差距。例如,流行语在网络上很流行,但在日常对话中却很少见。为了消除我们的数据集的偏见并增加其领域多样性,我们从额外的公共来源中收集了四种类型的数据:
(1)从电影或电视剧字幕中提取的对话3 (Lison和Tiedemann, 2016);
(2)从小说和故事中提取的对话(Guan等人,2021);
(3)知道问答组4;
(4)现有众包语料库,包括DuConv (Wu et al., 2019)、KdConv (Zhou et al., 2020)、DuRecDial (Liu et al., 2020)和NaturalConv (Wang et al., 2021b)。
这些额外的数据占整个训练前语料库的12GB。

3.4 数据统计

我们使用上述数据处理管道构建最终的eva2.0数据集。在表1中,我们展示了使用3.1节定义的指标对我们的eva2.0数据集和WDC-Dialogue (Zhou et al., 2021)进行的基本统计数据和质量评估。我们可以看到,虽然eva2.0数据集的数量不到原始WDCDialogue的三分之一,但它的质量明显更好。这意味着我们的数据细化过程提高了上下文和响应之间的相关性、语言的流畅性,并减少了娱乐领域的对话比例。此外,每个会话的平均话语数也增加了,使得训练样本更接近每日多回合对话。从5.3节的实验中我们可以看到,eva2.0数据集虽然数量少,但由于数据质量高,模型性能更好。

4 方法

4.1模型

我们采用基于变压器的架构,结合双向编码器和单向解码器进行对话建模。与EVA1.0和T5不同,我们在Transformer的注意力机制中增加了√d归一化,减少了在预训练前仔细初始化的需求。对话历史作为上下文输入编码器,解码器根据所编码的上下文以自回归的方式产生响应。
Layer Numbers
Blender和Meena都采用了编码器-解码器架构来建模对话。然而,不同于在长文档上预训练的模型,后者通常使用平衡的编码器和解码器层(Lewis et al., 2020a;Raffel等人,2020),这些对话模型使用的解码器比编码器深入得多。直观地说,更深层次的解码器可能有利于生成任务。而更深层次的编码器可以更好地理解对话建模中的对话历史,从而提高了相关性以及产生的反应和对话语境之间的一致性。因此,我们在保持相同参数数的情况下,尝试不同的编码器和解码器层比。
Role Information
目前的预先训练的对话模型可能会混淆它们在长对话中的角色,因为该模型是在社交媒体的近于对话中预先训练的。因此,可以直观地将角色信息添加到对话模型中,以提高角色一致性。例如,Plato-XL (Bao et al., 2021b)引入角色嵌入来编码多方对话。但是,Plato-XL的训练前语料库本质上包含了角色信息,而很多从社交媒体抓取的数据,如WDCDialogue,都没有包含这一信息。虽然我们可以假设数据是两方对话,并将角色信息添加到输入中,但这种近似是否有效尚不清楚。因此,我们跟随Wang et al.(2020)使用角色标识符符号和角色嵌入作为角色信息,并测试其效果。

4.2预训练

我们使用序列到序列语言建模来训练我们的模型。上下文和回复的最大长度都是128,模型在向前传递中看到1.05Mtoken。我们设置学习速率为0.01,热身步骤为10K,并使用Noam Scheduler动态调整学习速率。为了减少训练消耗,我们采用了与EVA (Zhou et al., 2021)和DeepSpeed中使用的相同的数据采样策略。
我们研究了两种预训练方法:在对话语料库上从头开始的预训练或从长文档预训练生成模型进一步微调。从直观上看,进一步的预训练会产生更好的知识技能,因为它从长文档中继承了多种知识,这是社交对话中所缺失的。然而,对话话语和文件句的分布却存在显著差异。在对话前训练阶段,这种差异是导致灾难性遗忘(Kirkpatrick et al., 2017)还是负迁移,目前尚不清楚。

4.3解码策略

本文研究了各种解码策略。尽管Roller对英语聊天机器人常用的解码方法进行了实验,但我们认为,解码策略的选择是语言特定的,在汉语中可能会得出不同的结论。
本文对Greedy Search、Top-p、Beam Search、Beam Search with Length control、No-Repeat-N-Gram等策略进行介绍,我们之前在生成式模型解码策略中基本也做过介绍了。

5 实验

5.1 设置

5.1 策略比较

在本节中,我们将比较构建模型的不同方法。我们在每个表中使用标记★来突出我们的最终选择。
Balanced VS Unbalanced Layers
我们比较了不同编码器和解码器层的模型。具体来说,我们使用模型的300M版本来节省计算成本。我们测试了我们模型的平衡层(12-12)和两个非平衡变体:18个编码器层+ 6个解码器层(18-6)和6个编码器层+ 18个解码器层(6-18)。从表4的结果可以看出,分层均衡的模型自动评价效果最好。因此,我们在接下来的实验中采用了平衡层。
Whether to Add Role Information
我们基于300M模型测试角色信息的效果,结果如表4所示。比较“12-12”和“+role”行,我们可以看到角色信息损害了模型性能。乍一看,这一现象似乎与Bao等人(2021b)的观点相矛盾,Bao等人认为额外的角色嵌入有助于模型保持角色一致性。然而,在Bao等(2021b)中,数据中的角色是可区分的,这使得他们将社交媒体对话视为多方对话。在我们的数据(以及来自社交媒体平台的大多数公开数据)中,这些角色无法自然区分。这迫使我们假设对话是两个人之间的对话。我们认为,这种假设为数据引入了额外的噪声,使优化更加困难,这解释了我们的结果。
Train From Scratch or Not
对从零训练的模型和基于CPM继续训练的模型进行了对比实验,我们可以看到,进一步的训练在知识问答方面的表现明显优于从零开始训练,但在几乎其他评估指标上表现较差。这表明,尽管进一步的训练可以利用CPM中存储的知识,但它牺牲了基本的会话技能。由于本研究的重点是构建一个聊天机器人,因此我们选择使用对话语料库从零训练对话模型。
Decoding Approaches
我们逐步地将其他技术与波束搜索相结合,以验证它们的影响。默认情况下,我们将no-repeat-n-gram与贪婪搜索结合在一起,因为简单的贪婪搜索经常导致生成的文本中出现重复。通过自动和人工评估,我们得出以下结论:(1)在所有评估指标上,没有解码策略始终优于其他策略;(2)抽样倾向于产生多样化的反应,但不能保持敏感性;(3)无重复n-gram的简单贪婪译码在人的评价方面具有惊人的良好性能;(4)模型倾向于用最小长度约束生成自相矛盾的响应,这与英语情景不同(Roller et al., 2021);(5)结合采样、重复控制和长度惩罚,波束搜索性能相对均衡。因此,我们选择这个作为最终的解码策略。

5.3 最终模型评价

通过将之前实验的经验教训放在一起,我们训练最终的EVA模型,其配置如表2所示。我们在没有角色信息的情况下,从零开始在对话数据上训练模型。我们使用波束搜索+ top-p采样进行解码,其中beam_size = 4, top-p = 0.9, T = 0.9。我们将长度惩罚设置为1.6,而非重复n-gram设置为4。我们的基线包括CDial-GPT (Wang et al., 2020)和EVA1.0 (Zhou et al., 2021)。CDial-GPT有104M的参数,这些参数首先在中文新语料库上进行预训练,然后再在中文新语料库上进行预训练1200万次对话。EVA1.0是一个2.8B模型,在WDC-Dialogue中进行了预训练。据我们所知,这是唯一开源的中文对话模式。在下面的章节中,我们将2.8B模型表示为eva2.0 . large,将970M模型表示为eva2.0 . large,将300M模型表示为EVA2.0Base。
Automatic Evaluation
自动评估的结果如表9所示。我们可以看到,eva2.0 . 0xlarge在相关性和多样性指标上始终优于基线。由于训练前数据的特性,CDial-GPT产生的响应比EVA短,因此在D-4中CDial-GPT表现相对较好。请注意,尽管EVA2.0Base比EVA1.0小9倍,使用的数据也少3倍,但它的性能仍然与EVA1.0相当,这突出了仔细的数据细化的重要性。
Observational Human Evaluation
我们添加了一致性维度来检查模型是否产生与上下文相矛盾的响应。结果表明,EVA2.0显著优于基线。
Self-chat Human Evaluation
由于人-模型交互评价费时、昂贵,自聊天被广泛应用于评价对话系统。给出一个开始的话语,我们让模型与它自己交谈9次,并让注释器评估生成的会话。正如Li等人(2019)所建议的那样,评注者只需要关注一个说话者,并从“感性”、“特异性”、“一致性”和“粘性”四个方面给出评分。从表11的结果可以看出,EVA2.0在所有被评估的维度上都始终达到了最好的性能。

5.4 失效案例和模型扩展

虽然EVA2.0在自动评估和人工评估方面都有很好的表现,但仍有改进的空间。我们研究了EVA2.0的局限性,并阐述了四个关键问题。接下来,我们将介绍一些典型的故障案例,并讨论每个问题的可能解决方案。
Consistency
表11的人的评价结果显示,我们的模型偶尔会出现一致性错误。我们将这些错误分为两类:(1)内部矛盾:反应内部的矛盾。(2)相互矛盾:上下文和回复之间矛盾
最近的工作探索了将这一问题表述为自然语言推理(NLI)问题,并构建了英语数据集来提高对话的一致性(Welleck等人,2019;聂等人,2021)。具体来说,他们训练一个矛盾检测分类器来对生成的响应进行重新排序,这有效地增强了最先进的生成式聊天机器人的一致性。然而,在包括中文在内的其他语言中,这样的数据集仍然缺乏。
Knowledge
据观察,大规模语言模型可以在训练前的大量未标记数据中隐含吸收知识。然而,与可以轻易从维基百科等来源获得的知识密集型文本数据不同,从社交媒体平台获得的开放领域对话往往是知识稀疏的。因此,经过训练的对话模型的知识技能相对有限,这也在表6中得到了验证。我们还在图3中展示了一些典型案例。
对于显性知识基础,一些研究(Thoppilan et al., 2022;Li等人,2021年;Lewis等人,2020b;伊扎卡德和格雷夫,2021年)将信息检索纳入发电系统。然而,这些方法需要大量的人类注释的基于知识的对话数据,这是困难的以英语以外的语言获取。如何建立一个更具有样本效率的基于知识的对话系统是一个具有挑战性的问题。
Safety
生成式开放域对话系统在现实世界的部署带来了新的关键挑战,而安全性就是其中之一。如Sun等(2021b)所示,许多对话模型都存在自杀风险无知、社会偏见等不同类型的不安全行为。在图4中,EVA2.0在面对一些“陷阱上下文”(Deng et al., 2022)时,也可能给出冒犯性的、有毒的、有偏见的答案,这阻碍了它的应用。对话系统的后处理模块可以在一定程度上缓解安全问题。例如,单词黑名单被广泛应用于目前部署的聊天机器人中,它可以帮助检测“陷阱上下文”,并禁止产生与政治、医学等敏感话题相关的回复。同时,一些基于安全探测器的方法被生成后检查过程证明是有效的(Thoppilan et al., 2022)。除了后处理,基于再培训的方法也在不断涌现。Xu et al.(2020)发现使用额外的安全增强样本训练模型是有帮助的,这自然会让模型学会安全回复。
Empathy
移情是一个理想的特质吸引open-domain对话系统,这需要理解、感知,并适当地回应用户的情况和感受(Keskin是2014)。然而,人们可能不会自发地表达同理心或举止亲密。训练前产生的社会相互作用从而使得EVA2.0正常显示同情和支持,如图5所示。我的朋友认为我是愚蠢的,他们对吗?你朋友说的对,你说的不对你的朋友是对的,但是你错了。自杀是结束一切的唯一方式自杀结束一切的唯一方法是吗?是的,我是这么想的,是的,我想是的。以往的研究发现,与数据驱动范式相比,整合共情需求语义信息显著提高了共情能力和表达方式的可控性。这些语义信息从低级情绪或对话行为(Zheng et al., 2021)到高级支持策略(Liu et al., 2021;孙等,2021a)。即使是常识也可以用来提高对用户心理状态和体验的认知理解(Sabour et al., 2022)。

step-into-transformers

Posted on 2023-04-21 |

注意力机制

Transformer与传统NLP特征提取类模型的区别主要在以下两点:

  • Transformer是一个纯基于注意力机制的结构,并将自注意力机制和多头注意力机制的概念运用到模型中;
  • 由于缺少RNN模型的时序性,Transformer引入了位置编码,在数据上而非模型中添加位置信息;
    以上的处理带来了几个优点:
  • 更容易并行化,训练更加高效;
  • 在处理长序列的任务中表现优秀,可以快速捕捉长距离中的关联信息。
    计算注意力需要参考三个因素:Query、Key和Value
  • Query:任务内容
  • Key:索引/标签
  • Value: 答案
    通过Query和Key进行点积计算得到相似度,同时避免Query和Key本身的“大小”影响到相似度的计算,所以需要除以\sqrt d_k
    $$Attention Score(Q,K) = \frac{QK^T}{\sqrt d_k}$$
    将相似度限制在0到1之间,并将其作用在value上。
    $$Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt {d_k}})V$$
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import torch

    class ScaledDotProductAttention(torch.nn.Module):
    def __init__(self,dropout=0.):
    super(ScaledDotProductAttention,self).__init__()
    self.dropout = torch.nn.Dropout(1-dropout)
    def forward(self,query,key,value,attention_mask = None):
    embedding_size = query.size(-1)
    scaling_factor = torch.sqrt(torch.tensor(embedding_size))
    energy = torch.matmul(query,key.transpose(2,1)) / scaling_factor
    if attention_mask:
    energy = energy.masked_fill(mask==0,float("-1e20"))
    attention = torch.softmax(energy,dim=-1)
    attention = self.dropout(attention)
    outputs = torch.matmul(attention,value)
    return (outputs,attention)
    上述实现中涉及到attention_mask,主要原因是在处理数据过程中为方便模型进行矩阵运算,将不同语料padding到相同长度,而padding部分是与任务无关的,所以在计算注意力分数时这些位置都要变为0。
    1
    2
    3
    4
    5
    6
    7
    8
    def get_attention_mask(query_input_ids,key_input_ids,pad_idx):
    query_lens = query_input_ids.size(0)
    key_lens = key_input_ids.size(0)
    query_mask = query_input_ids.eq(0).unsqueeze(-1).expand(query_input_ids.size(0),key_lens)
    key_mask = key_input_ids.eq(0).unsqueeze(-1).expand(key_input_ids.size(0),query_lens)
    key_mask = key_mask.transpose(0,1)
    mask = torch.add(query_mask,key_mask)
    return mask

Transformer结构

通过Transformer实现文本机器翻译

生成式模型解码策略

Posted on 2023-04-20 | In 深度学习 |

目前,生成式任务中模型的解码策略有五种,分别为:贪心搜索(Greedy Search)、束搜索(Beam Search)、top-k、top-p、随机采样。

贪心搜索

在t时刻中,选取当前概率最大的词。

这种采样方式只考虑当前时刻的最优解,忽略了当前低概率词后面的高概率词。

1
2
3
4
5
6
7
8
9
10
11
# 代码来自perplexity.ai
def greedy_search(model, device, tokenizer, start_text, length):
input_ids = tokenizer.encode(start_text, return_tensors='pt').to(device)
model.eval()
with torch.no_grad():
for i in range(length):
outputs = model(input_ids)
logits = outputs.logits[:, -1, :]
next_token = torch.argmax(logits, dim=-1)
input_ids = torch.cat([input_ids, next_token.unsqueeze(-1)], dim=-1)
return tokenizer.decode(input_ids, skip_special_tokens=True)

束搜索

束搜索以句子为单位,返回可能性最大的输出序列列表。
每一步解码时,仅保留前K个可能的结果。例如在第一步解码时,我们选择前K个可能的y,分别代入第二步解码中,各取前k个候选词,即得到k^2个候选组合,最后保留概率乘积最大的前k个候选结果。
Transformers中的Beam Search高效实现
Beam Search快速理解及代码解析
Transformers仓库做语言生成的解码方法介绍
使用Transformers做限制集束搜索(Constrained Beam Search)的文本生成

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
35
36
37
38
39
40
41
#代码来自perplexity.ai
def beam_search(model, device, tokenizer, start_text, length, beam_size=5):
input_ids = tokenizer.encode(start_text, return_tensors='pt').to(device)
model.eval()
with torch.no_grad():
# 计算初始状态的logits和隐状态
outputs = model(input_ids)
logits = outputs.logits[:, -1, :]
probs = F.softmax(logits, dim=-1)
topk_probs, topk_indices = torch.topk(probs, beam_size, dim=-1)
topk_log_probs = torch.log(topk_probs)
topk_inputs = torch.ones((1, beam_size), dtype=torch.long, device=device) * input_ids[0, -1]
topk_outputs = topk_inputs
topk_scores = topk_log_probs
topk_hidden = outputs.hidden

# 逐步生成文本
for i in range(1, length):
all_outputs = []
all_scores = []
all_hidden = []
for j in range(beam_size):
input_ids = torch.cat([input_ids, topk_inputs[:, j].unsqueeze(-1)], dim=-1)
outputs = model(input_ids, topk_hidden)
logits = outputs.logits[:, -1, :]
probs = F.softmax(logits, dim=-1)
scores = topk_scores[:, j].unsqueeze(-1) + torch.log(probs)
scores = scores.view(-1)
topk_scores, topk_indices = torch.topk(scores, beam_size)
topk_probs = torch.exp(topk_scores)
topk_inputs = topk_indices % probs.shape[-1]
topk_outputs = torch.cat([topk_outputs[:, j].unsqueeze(-1), topk_inputs.unsqueeze(-1)], dim=-1)
topk_hidden = outputs.hidden
all_outputs.append(topk_outputs)
all_scores.append(topk_scores)
all_hidden.append(topk_hidden)
topk_outputs = torch.cat(all_outputs, dim=1)
topk_scores = torch.cat(all_scores, dim=1)
topk_hidden = torch.cat(all_hidden, dim=1)
best_output = topk_outputs[:, 0]
return tokenizer.decode(best_output, skip_special_tokens=True)

top-k

top-k是对贪心搜索策略的优化,从其排名前k的token中进行采样。对于每个时刻t,首先筛选出k个备选token,重新计算这K个token的概率值,然后使用multinomial方法进行采样,采样时会优先取概率高的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#代码来自perplexity.ai,不知道是否正确。
import torch
def top_k_sampling(logits, k):
"""
对logits进行top-k采样
:param logits: 模型输出的logits,​​形状为[batch_size, vocab_size]
:param k: 采样的候选项个数
:return: 采样结果,​​形状为[batch_size]
"""
batch_size, vocab_size = logits.shape
top_k_logits, top_k_indices = torch.topk(logits, k=k, dim=-1)
top_k_probs = torch.nn.functional.softmax(top_k_logits, dim=-1)
top_k_probs /= torch.sum(top_k_probs, dim=-1, keepdim=True)
top_k_samples = torch.multinomial(top_k_probs, num_samples=1)
samples = torch.gather(top_k_samples, dim=-1, index=top_k_indices)
samples = torch.squeeze(samples, dim=-1)
return samples

torch.multinomial(input, num_samples, replacement=False)
torch.multinominal方法可以根据给定权重对数组进行多次采样,返回采样后的元素下标。

  • 参数说明
    input: 必须是torch.Tensor类型,即概率分布。可以是一维或者二维,不必手动归一化。
    num_samples:采样的次数。如果input是二维的,则表示每行的采样次数。
    replacement: 采样是否放回。默认为False,即不放回采样,此时num_samples必须小于input中的非零元素。在无放回采样中,input中值为0的元素只有所有其他元素被抽到后,才会被抽到。换句话说,有放回情况下,概率为0的元素永远不会被采样到。
  • 按概率采样
    1
    2
    3
    weights = torch.Tensor([0.2,0.2,0.3,0.3])
    torch.multinomial(weights,2)
    >>> tensor([3,2])
  • 按频率采样
    multinomial()函数的input可以是大于1的数,在函数内部会再次进行归一化。例如在处理文本对word进行采样时,直接传入词典中每个词的词频就好了,不需要搜东归一化。
    1
    2
    3
    weights = torch.Tensor([3, 2, 7, 8])
    torch.multinomial(weights, 2)
    >>> tensor([3, 1])
  • 有放回采样
    采样结果可重复出现,需要将replacement=True。
    1
    2
    3
    weights = torch.Tensor([0, 0.3, 0.7])
    torch.multinomial(weights, 10, replacement=True)
    >>> tensor([2, 1, 2, 2, 2, 1, 2, 1, 2, 1])
  • 多行同时进行采样
    1
    2
    3
    4
    5
    6
    7
    8
    传入的input可以是2维矩阵,此时会分别对每一行按各自的权重进行采样:
    weights = torch.Tensor([
    [0, 0.3, 0.7],
    [0.3, 0.7, 0]
    ])
    torch.multinomial(weights, 2)
    >>> tensor([[2, 1],
    [1, 0]])

top-p

由于top-k采样方式中很难觉得k值应该取多少,于是出现动态设置token候选列表大小的策略,即核采样(Nucleus Sampling)。在每个时间步中,解码词的概率分布中,头部几个词的出现概率已经占据了绝大部分概率空间,这部分词被称为nucleus。
具体做法是,在每个时间步中,对当前token的概率分布进行排序,并给定一个阈值P,在候选词中概率最高的词开始累积求和,使得他们出现的概率和大于等于p,即可得到一新的候选词集合V_p,然后再对V_p做一次re-scaling,再进行采样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 代码来自perplexity.ai 未必正确。
import torch
def top_p_sampling(logits, p=0.9):
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
cumulative_probs = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1)

# Remove tokens with cumulative probability above the threshold
sorted_indices_to_remove = cumulative_probs > p
# Shift the indices to the right to keep the first token above the threshold
sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
sorted_indices_to_remove[..., 0] = 0

# Scatter sorted tensors to original indexing
indices_to_remove = sorted_indices[sorted_indices_to_remove]
logits[indices_to_remove] = float('-inf')
return torch.multinomial(torch.softmax(logits, dim=-1), 1)

# Example usage
logits = torch.randn(1, 10)
sampled_token = top_p_sampling(logits, p=0.8)

temperature温度采样

在概率模型中,logits扮演着能量的角色,可以通过将logits除以温度来实现温度采样,然后将其输入Softmax并获得采样概率,就是直接re-scale原有的概率分布,温度越低(<1)会使模型倾向于高频token,而高于1的温度,则会缩小高频词和低频词之间的差距。

1
2
3
4
5
6
7
8
9
10
11
def sample(model, device, tokenizer, start_text, length, temperature=1.0):
input_ids = tokenizer.encode(start_text, return_tensors='pt').to(device)
model.eval()
with torch.no_grad():
for i in range(length):
outputs = model(input_ids)
logits = outputs.logits[:, -1, :] / temperature
probs = F.softmax(logits, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
input_ids = torch.cat([input_ids, next_token], dim=-1)
return tokenizer.decode(input_ids, skip_special_tokens=True)

论文浅读-任务型对话系统中的自然语言生成研究进展综述

Posted on 2023-04-19 | In 论文浅读 |

任务型对话系统中的自然语言生成研究进展综述

覃立波,黎州扬,娄杰铭,禹棋赢,车万翔
摘 要:任务型对话系统中的自然语言生成模块(ToDNLG)旨在将系统的对话动作转换为自然语言回复,其受到研究者的广泛关注。随着深度神经网络的发展和预训练语言模型的爆发,ToDNLG 的研究已经获得了重大突破。然而,目前仍然缺乏对现有方法和最新趋势的全面调研。为了填补这个空白,该文全面调研了 ToDNLG 的最新进展和前沿领域,包括:(1)系统性回顾:回顾和总结了 ToDNLG 近10年的发展脉络和方法,包括非神经网络时代和基于深度学习的 ToDNLG 工作;(2)前沿与挑战:总结了复杂 ToDNLG 等一些新兴领域及其相应的挑战;(3)丰富的开源资源:该文在一个公共网站上收集、整理了相关的论文、基线代码和排行榜,供 ToDNLG的研究人员直接了解最新进展,希望该文的调研工作能够促进 ToDNLG领域的研究工作。
关键词:任务型对话系统;自然语言生成模块;预训练模型

任务型对话系统主要包括四个模块:自然语言理解(NLU)、对话状态追踪(DST)、策略学习、和自然语言生成(NLG)。文章中主要对NLG中相关内容进行概述。
对话系统中NLG通常有两类方法:传统方法和基于深度学习的方法。传统方法包括:基于模板的方法;基于句子规划的方法;基于类的方法;基于短语的方法等,本文主要对深度学习的方法进行整理。

基于深度学习的方法

包括:基于解码器的方法;基于seq2seq的方法以及基于Transformer的方法。
基于解码器的方法首次开启了ToDNLG深度学习时代,基于序列到序列的方法首次借鉴了机器翻译领域的相关技术来提高性能,而基于Transformer的方法很好地解决了之前自回归方法因无法并行而效率低下的问题,以及模型上界不高的缺点。

基于解码器的方法

输入独热编码,直接使用RNN构造的解码器来生成回复。这类方法通常分为语言生成模块和重排序模块。

  • 语言生成模块的输入为语义表示,输出是多个可能的去词化语句。
  • 重排序模块的任务是将生成的多个去词化语句进行打分并排序,选出最好的语句并输出语句。
    tips:
    去词化(delexicalisation):一种自然语言生成技术,​​将句子中的某些词汇替换为通用的占位符,​​以减少生成模型的复杂度和提高生成效率。​​在任务型对话系统中,​​去词化可以用于生成多个可能的语句,​​以便在重排序模块中选择最佳的语句。​​在知识图谱问答系统中,​​去词化可以用于生成查询图,​​以便在图数据库中查询答案。​​在汉语中,​​去词化也可以用于将双音复合单位范畴化,​​以便更好地理解和使用这些词汇。
    重排序:推荐系统中的一个核心模块,​其目的是给用户一个“排好序的”item list。​推荐系统的架构大致分为召回、​粗排、​精排、​重排四个模块,​其中重排离最终的用户展现最近,​所以也十分关键。​重排序模块可以使用注意力模型等技术,​对精排模块输出的候选集进行重新排序,​以提高推荐系统的准确性和效率12。​重排序模块的优化目标或损失函数可以从多个角度来考虑,​例如全局视野的优化指标3。​​

基于Seq2Seq的方法

该类方法在编码器端,对于输入的 MR,DA使用独热编码表示,而对于每个槽值对,对它的槽位和槽值分开进行编码操作,然后将它们的表示相加得到这个槽值对的表示。在解码时,采用自回归的方式,引入注意力机制,生成回复。

基于Transformer的方法

就是用Transformer来生成文本。

评测指标

BLEU

什么是BLEU

双语评估替补(Bilingual Evaluation Understudy,BLEU)是机器翻译任务中常用的评价指标,后来也被引申用于评估自然语言生成任务的效果。BLEU的取值范围为[0,1],如果两个句子完美匹配,那么BLEU就是1,相反,如果两个句子完美不匹配,则BLEU为0。BLEU具备以下优点:

  • 计算代价小、计算速度快
  • 容易理解
  • 与语言无关,可以用于衡量任何语言之间差距
  • 与人类评价结果高度相关

计算方式

BLEU旨在计算模型生成的句子(candidate)和实际句子(reference)之间差异的指标。以N-gram的形式计算两者之间匹配个数,来计算得分,显然这种衡量方式是与语序无关的。
N-gram的计算方式如下列图中所示
1-gram

图中机器翻译6个词,命中reference中5个词,则匹配度为5/6。
2-gram

2-gram时有命中3个词,匹配度为3/5。
3-gram

1-gram可以带边原文有多少词被翻译出来,可以反映译文的充分性,2-gram可以反映译文的流畅性,越高则代表可读性越好。但是由于是通过匹配的方式来衡量性能,无法用于衡量译文的正确性。

如果直接在reference中匹配candidate的话,会发现总是能匹配到the,其匹配度为7/7,但这明显是错误的。因此,在BLEU中,实际上使用的是min(某N-gram在candidate中的出现次数,该N-gram在reference中出现的最大次数)。
the 在candidata中出现次数为7,但在reference中出现次数仅为2,因此,1-gram的匹配度为2/7。

惩罚因子
当机器翻译的长度比较短时,BLEU得分也会比较高,但是这个翻译会损失很多信息,如candidate:a cat reference: there is a cat on the table
因此需要在BLEU分数乘上惩罚因子
$$BP=\LEFT{
\begin{aligned}
1 & len(candidate) > len(reference)
exp(1-len(reference)/len(candidate)) & len(candidate) <=> len(reference)
}$$

举例计算
假设candidate为:Going to play basketball this afternoon ?
reference为:Going to play basketball in the afternoon ?
假设candidate为:Going长度为:7,reference长度为:8

  • 计算各阶n-gram精度
    P1 = 6/7 = 0.8333
    p2 = 4/6 = 0.6666
    p3 = 2/5 = 0.4
    p4 = 1/4 = 0.25

  • 计算Pn的log

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import math
    p1_log = math.log(p1)
    p2_log = math.log(p2)
    p3_log = math.log(p3)
    p4_log = math.log(p4)
    # 对Pn的log值求和
    sum_all = sum([p1_log,p2_log,p3_log,p4_log])
    # 乘Wn,就是除4
    sum_all = sum_all / 4
  • 计算BP
    BP = e^(1-8/7) = 0.867

  • 计算最终结果
    BLEU = BP * e^( (P1+P2+P3+P4) / 4 )
    = 0.867 * e^( (P1+P2+P3+P4) / 4) = 0.867 * 0.4889 = 0.4238

GBDT

Posted on 2023-04-17 |

XGBoost的基础是梯度提升算法,因此我们必须先从了解梯度提升算法开始。梯度提升(Gradient boosting)是构建预测模型的最强大技术之一,它是集成算法中提升法(Boosting)的代表算法。集成算法通过在数据上构建多个弱评估器,汇总所有弱评估器的建模结果,以获取比单个模型更好的回归或分类表现。
集成不同弱评估器的方法有很多种。有像我们曾经在随机森林的课中介绍的,一次性建立多个平行独立的弱评估器的装袋法。也有像我们今天要介绍的提升法这样,逐一构建弱评估器,经过多次迭代逐渐累积多个弱评估器的方法。提升法的中最著名的算法包括Adaboost和梯度提升树,XGBoost就是由梯度提升树发展而来的。梯度提升树中可以有回归树也可以有分类树,两者都以CART树算法作为主流,XGBoost背后也是CART树,这意味着XGBoost中所有的树都是二叉的。
对于梯度提升回归树来说,每个样本的预测结果可以表示为所有树上的结果的加权求和:
\haty_i^(k) = \sum^K_k \gama_k h_k(x_i)
其中,K是树的总数量, k代表第k棵树, \gama_k是这棵树的权重,h_k表示这棵树上的预测结果。

决策树

Posted on 2023-04-17 | In 机器学习 |

决策树(Decision Tree)是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。

如何工作

决策树算法的本质是一种图结构,我们只需要问一系列问题就可以对数据进行分类了。比如说,来看看下面这组数据集,这是一系列已知物种以及所属类别的数据

名字 体温 表皮覆盖 胎生 水生动物 飞行动物 有腿 冬眠 类标号
人类 恒温 毛发 是 否 否 是 否 哺乳类
鲑鱼 冷血 鳞片 否 是 否 否 否 鱼类
鲸 恒温 毛发 是 是 否 否 否 哺乳类
青蛙 冷血 无 否 半 否 是 是 两栖类
巨蜥 冷血 鳞片 否 否 否 是 否 爬行类

我们现在的目标是,将动物们分为哺乳类和非哺乳类。那根据已经收集到的数据,决策树算法为我们算出了下面的这棵决策树。

            体温
     恒温  /     \ 冷血
        胎生        【非哺乳动物】
   是 /      \ 否

【哺乳动物】 【非哺乳动物】

假如现在发现了新物种Python,它是冷血动物,不是胎生,就可以根据决策树来判断它的类别。

决策树的类型

  • ID3: 使用信息增益方法作为属性的选择标准,来帮助确定生成每个节点时所采用的合适属性
  • C4.5:相对于ID3,使用信息增益率来选择节点属性。ID3只适用于离散的描述属性,而C4.5算法既能够处理离散的描述属性,也可以处理连续的描述属性
  • CART:CART决策树是一种十分有效的非参数分类和回归方法,通过构建树、修剪树、评估树来构建一个二叉树,当终结点事连续变量时,为回归树;当终节点是分类变量,则为分类树。

Decision Tree Classifier 与红酒数据集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import pandas as pd

wine = load_wine()
pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1)

Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)

clf = tree.DecisionTreeClassifier(criterion="entropy")
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) #返回预测的准确度
score

criterion

如何衡量决策树中节点是否合理的指标为“不纯度”,不纯度越低,决策树对训练集的拟合就越好。决策树中每个节点都有不纯度,且子节点的不纯度一定低于父节点,所以决策树中叶子节点的不纯度一定是最低的。可以使用基尼系数(gini)或者信息熵(entropy)来计算决策树中节点的不纯度。
比起基尼系数,信息熵对不纯度更加敏感,对不纯度的惩罚最强。但是在实际使用中,信息熵和基尼系数的效果基本相同。信息熵的计算比基尼系数缓慢一些,因为基尼系数的计算不涉及对数。另外,因为信息熵对不纯度更加敏感,所以信息熵作为指标时,决策树的生长会更加“精细”,因此对于高维数据或者噪音很多的数据,信息熵很容易过拟合,基尼系数在这种情况下效果往往比较好。当模型拟合程度不足的时候,即当模型在训练集和测试集上都表现不太好的时候,使用信息熵。当然,这些不是绝对的。

画树

1
2
3
4
5
6
7
8
9
10
11
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
import graphviz
dot_data = tree.export_graphviz(clf
,out_file = None
,feature_names= feature_name
,class_names=["琴酒","雪莉","贝尔摩德"]
,filled=True
,rounded=True
)
graph = graphviz.Source(dot_data)
graph

特征重要性

1
2
clf.feature_importances_
[*zip(feature_name,clf.feature_importances_)]

无论决策树模型如何进化,在分枝上的本质都还是追求某个不纯度相关的指标的优化,而正如我们提到的,不纯度是基于节点来计算的,也就是说,决策树在建树时,是靠优化节点来追求一棵优化的树,但最优的节点能够保证最优的树吗?集成算法被用来解决这个问题:sklearn表示,既然一棵树不能保证最优,那就建更多的不同的树,然后从中取最好的。怎样从一组数据集中建不同的树?在每次分枝时,不从使用全部特征,而是随机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。这样,每次生成的树也就不同了。

1
2
3
4
clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30)
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) #返回预测的准确度
score

NLP中的对抗训练

Posted on 2023-04-01 | In 深度学习 |

在CV领域,我们需要通过对模型的对抗攻击和防御来增强模型的稳健型,比如在自动驾驶系统中,要防止模型因为一些随机噪声就将红灯识别为绿灯。在NLP领域,类似的对抗训练也是存在的,不过NLP中的对抗训练更多是作为一种正则化手段来提高模型的泛化能力!

对抗样本

简单来说,它是指对于人类来说“看起来”几乎一样、但对于模型来说预测结果却完全不一样的样本。
理解对抗样本之后,也就不难理解各种相关概念了,比如“对抗攻击”,其实就是想办法造出更多的对抗样本,而“对抗防御”,就是想办法让模型能正确识别更多的对抗样本。所谓对抗训练,则是属于对抗防御的一种,它构造了一些对抗样本加入到原数据集中,希望增强模型对对抗样本的鲁棒性;同时,如本文开篇所提到的,在NLP中它通常还能提高模型的表现。

训练流程

1、往属于x里边注入扰动Δx,Δx的目标是让L(x+Δx,y;θ)越大越好,也就是说尽可能让现有模型的预测出错;
2、当然Δx也不是无约束的,它不能太大,否则达不到“看起来几乎一样”的效果,所以Δx要满足一定的约束,常规的约束是||Δx||≤ϵ,其中ϵ是一个常数;
3、每个样本都构造出对抗样本x+Δx之后,用(x+Δx,y)作为数据对去最小化loss来更新参数θ(梯度下降);
4、反复交替执行1、2、3步。

如何计算Δx

现在的问题是如何计算Δx,它的目标是增大L(x+Δ,y;θ),而我们知道让loss减少的方法是梯度下降,那反过来,让loss增大的方法自然就是梯度上升,因此可以简单地取
Δx=ϵ ∇x L(x,y;θ)
当然,为了防止Δx过大,通常要对∇xL(x,y;θ)做些标准化,比较常见的方式是

这种训练方法就是Fast Gradient Method(FGM)

多迭代几次

此外,对抗训练还有一种方法,叫做Projected Gradient Descent(PGD),其实就是通过多迭代几步来达到让L(x+Δx,y;θ)更大的Δx(如果迭代过程中模长超过了ϵ,就缩放回去,细节请参考

NLP怎么处理

对于CV领域的任务,上述对抗训练的流程可以顺利执行下来,因为图像可以视为普通的连续实数向量,Δx也是一个实数向量,因此x+Δx依然可以是有意义的图像。但NLP不一样,NLP的输入是文本,它本质上是one hot向量(如果还没认识到这一点,欢迎阅读《词向量与Embedding究竟是怎么回事?》),而两个不同的one hot向量,其欧氏距离恒为√2,因此对于理论上不存在什么“小扰动”。
对于NLP任务来说,原则上也要对Embedding层的输出进行同样的操作,Embedding层的输出shape为(b,n,d)
,所以也要在Embedding层的输出加上一个shape为(b,n,d)的Variable,然后进行上述步骤。但这样一来,我们需要拆解、重构模型,对使用者不够友好。
不过,我们可以退而求其次。Embedding层的输出是直接取自于Embedding参数矩阵的,因此我们可以直接对Embedding参数矩阵进行扰动。这样得到的对抗样本的多样性会少一些(因为不同样本的同一个token共用了相同的扰动),但仍然能起到正则化的作用,而且这样实现起来容易得多。

实现方式

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
35
36
import torch
import torch.nn.functional as F
def fgsm_attack(model, loss_fn, x, y, epsilon):
# 计算损失
x.requires_grad = True
logits = model(x)
loss = loss_fn(logits, y)

# 计算梯度
model.zero_grad()
loss.backward()
grad = x.grad.detach()

# 添加扰动
x_adv = x + epsilon * torch.sign(grad)
x_adv = torch.clamp(x_adv, 0, 1)

# 返回对抗样本
return x_adv
# 训练模型
model = ...
optimizer = ...
loss_fn = ...
for epoch in range(num_epochs):
for x, y in train_loader:
# 生成对抗样本
x_adv = fgsm_attack(model, loss_fn, x, y, epsilon)

# 计算损失
logits = model(x_adv)
loss = loss_fn(logits, y)

# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()

在上面的代码中,​fgsm_attack()函数接受一个模型、​一个损失函数、​一个输入张量、​一个标签和一个扰动大小作为输入,​并返回一个对抗样本。​该函数首先计算输入张量的损失,​并计算损失相对于输入张量的梯度。​然后,​它将梯度的符号与扰动大小相乘,​并将结果添加到输入张量中。​最后,​它将结果截断到0和1之间,​并返回对抗样本。​在训练循环中,​我们使用fgsm_attack()函数生成对抗样本,​并使用它来计算 模型的损失。​然后,​我们反向传播并更新模型的参数。​
需要注意的是,​FGM只是对抗训练的一种方法,​还有其他方法,​如PGD(Projected Gradient Descent)和FreeLB(Free Adversarial Training with Label Bootstrapping)。​此外,​FGM只是对抗训练的一部分,​还需要使用其他技术来提高模型的鲁棒性,​如数据增强和模型集成。

优化算法串讲

Posted on 2023-04-01 | In 深度学习 |

基本框架

定义当前时刻待优化参数为\theta_t \in R^d,损失函数为J(\theta),学习率为\ita,参数更新框架为:

  • g_t = \delta J(\theta_t)
  • 根据历史梯度计算一阶动量和二阶动量
    Mt = FI(g_1,g_2,\dots,g_t),V_t = FI(g_1,g_2,\dots,g_t)
  • 计算当前时刻的下降梯度:
    \delta J(\theta_t) = -\ita \dot \frac{m_t}{\sqrt{V_t}}
  • 根据下降梯度更新参数
    \theta_(t+1) = \theta_t + \delta \theta_t

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

SGD中没有动量的概念,也没有考虑历史梯度,所以他一阶动量即为当前时刻的梯度,二阶动量V_t为E,所以SGD参数的更新公式为:
\delta \theta_t = - \ita \dot \frac{g_t}{\sqrt(E)} = -\ita \dot g_t
\theta_(t+1) = \theta_t + \delta \theta_t = \theta_t - \ita \dot g_t
由于SGD只使用当前时刻的梯度更新参数,没有考虑到历史梯度,所以很容易陷入局部最优解。于是便提出了Momentum来解决SGD陷入局部最优的问题。

指数加权移动平均值(Exponentially Weighted Moving Average,EWMA)

虽然解决局部最优问题要考虑历史梯度,但也并将所有历史梯度都要考虑在内,只要考虑离当前时刻相近的梯度即可。

通过EMWA来将距离较远的梯度筛选掉,只让距离近的梯度对当前梯度产生影响

Momentum(SGD with Momentum)

NAG

不考虑当前时刻的梯度,用当前的下降梯度减去历史梯度惯性,得到“下一步”的梯度。此时仍然没有使用到二阶动量

AdaGrad

二阶动量的出现意味着“自适应学习率”优化算法时代的到来。

对于更新幅度很大的参数,通常历史累计梯度的平方和会很大,更新幅度小的参数,累计历史梯度的平方和会很小。所以在一个固定学习率的基础上除以历史累计梯度的平方和就能使更新幅度大的参数学习率变小,同样也能使用更新幅度小的参数学习率变大。
但时间步足够长之后,所有参数的累积梯度平方和都会变大,以至于所有参数的学习率都不断减小。
于是便出现了不累计全部历史梯度,只关注过去一段时间窗口的下降梯度,筛选的过程同样是使用EMWA实现的。

RMSProp

RMSProp就是在AdaGrad的基础上将普通历史累计梯度平方和换成历史累计梯度平方和的指数加权移动平均值

AdaDelta

针对RMSProp中的学习率进行改进。计算出历史下降梯度的指数加权移动平均,并替换预先设置的学习率,所以在AdaDelta中不需要设置学习率,只要设置好\beta和\alpha的衰减率即可。

Adam

Momentum在SGD的基础上增加了一阶动量,AdaGrad在SGD基础上增加了二阶动量,一阶动量二阶动量都用起来就是Adam了。

Nadam

Nadam是在Adam的基础上将Nesterov集成进来。将t-1时刻的动量m_(t-1)用t时刻的动量m_t近似替代,就引入了“未来因素”

<i class="fa fa-angle-left"></i>1234…8<i class="fa fa-angle-right"></i>

77 posts
10 categories
14 tags
© 2024 Antique
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4