DLAI-自然语言处理笔记-全-
DLAI 自然语言处理笔记(全)
001:欢迎与课程概述
在本节课中,我们将要学习吴恩达《自然语言处理》专项课程的欢迎与介绍部分。我们将了解课程的整体结构、核心内容以及两位主讲老师。
课程介绍与讲师团队
大家好,欢迎来到自然语言处理专项课程。
我想介绍我的朋友,也是本专项课程的讲师——Eunice 和 Lucas。
我认识 Eunice 多年。他是斯坦福大学的讲师,也曾协助我创建深度学习专项课程。多年来,Eunice 已教授了数百名首次学习人工智能、机器学习、自然语言处理等主题的学生,并指导了学生在各种人工智能相关项目上的工作。因此,我非常高兴 Eunice 能加入我们。
感谢你的介绍,与你合作总是很棒。
Lucas 是 Google Brain 团队的成员,他在那里从事深度学习和自然语言处理的研究,涵盖从机器翻译到解析的各个方面。他是谷歌 TensorFlow 系统的合著者,也是 Transformer 网络的合著者。因此,他的工作对整个自然语言处理和人工智能领域产生了巨大影响。我也非常高兴 Lucas 能成为本专项课程的讲师。
感谢 Andrew 的介绍。
Eunice 和 Lucas 将引导你学习自然语言处理中最重要的概念。
自然语言处理的发展历程
上一节我们介绍了讲师团队,本节中我们来看看自然语言处理领域的发展。
自然语言处理在过去几十年里发生了很大变化。该领域最初主要使用基于规则的系统,例如有人可能会编写这样的规则:如果看到“好”这个词,就假设这是一条积极的客户评价。
随后发展到使用性能更好但仍需要大量手工工程的概率系统。现在,自然语言处理则更多地依赖于机器学习和深度学习。最近,随着强大计算机的兴起,我们现在可以训练端到端系统,这在几年前是不可能训练的。我们现在可以捕捉更复杂的模式,并可以在问答系统、聊天机器人和其他应用中使用这些模型。
许多这些应用都是使用注意力模型构建的,你将在本专项课程中学习这些模型。几年前,这些模型需要数周甚至数月的时间来训练,但有了注意力机制,你可以在短短几个小时内训练这些模型。
专项课程内容概览
在本专项课程中,你将学习构建自然语言处理技术,包括许多大型商业系统中部署的相同技术。
以下是本专项课程四个核心课程的内容简介:
第一门课程:分类与向量空间
在课程1中,你将学习区分具有积极情感和消极情感的文本片段。你将使用逻辑回归和朴素贝叶斯分类器来实现。你还将学习如何将单词、查询、文档以及其他文本片段表示为向量中的数字。你将构建你的第一个机器翻译系统,并学习局部敏感哈希,这是一种有助于高效搜索的方法。
第二门课程:概率模型
完成第一门课程后,你将进入第二门课程,学习自然语言处理中的概率模型。例如,给定几个单词,下一个单词是“V”的概率与是“其他词”的概率相比如何。这些算法我们可能每天都在使用。在本专项课程中,你将学习如何自己构建这些算法。
第三门课程:序列模型
在前两门课程打下的基础上,你将进入第三门课程,学习序列模型。
第四门课程:注意力模型
最后,在第四门课程中,你将学习注意力模型。这将带你了解用于聊天机器人、问答和文本摘要等任务的最先进的自然语言处理模型。
当你完成课程4时,你将能够实现最先进的神经机器翻译、摘要、问答和聊天机器人。我们将向你展示如何结合注意力机制和并行计算从头开始构建这些系统。这些模型在工业界具有很高的影响力,例如,可用于自动化呼叫中心,或帮助企业理解海量数据。
总结与鼓励
本节课中我们一起学习了自然语言处理专项课程的欢迎信息、讲师介绍、领域发展历程以及四门核心课程的主要内容。
我非常期待大家开始学习这些课程,掌握这些技术,并去构建一些出色的自然语言处理系统。
让我们开始吧!课堂上见。让我们跳入学习之旅。
002:欢迎来到自然语言处理 🎉
在本课程中,我们将学习自然语言处理的基础知识,包括文本分类和向量空间模型。我们还将探讨这些概念在情感分析和单词翻译等实际问题中的应用。
概述 📋
欢迎来到自然语言处理的第一门课程。在本课程中,你将学习分类和向量空间模型。你还将了解这些概念如何应用于情感分析和单词翻译等问题。
例如,假设你有一千条产品评论,即用户撰写的文本片段。你能建立一个系统来自动分析所有这些产品评论,以确定其中正面评论和负面评论的比例吗?本课程将教你如何实现这一目标。
课程内容详解 📚
上一节我们概述了本课程的目标,本节中我们将详细看看课程的具体安排。
以下是卢卡斯对本课程第一部分的介绍:
- 在第一周,你将学习如何将文本表示为向量,并构建一个分类器来分类样本文本的情感是正面还是负面。你将为此使用逻辑回归。
- 在第二周,你将在同一问题上使用朴素贝叶斯分类器。
- 在第三周,你将学习向量空间模型。你将学习如何将文本文档(如推文、文章、查询或任何包含文本的对象)表示为向量。这在信息检索、索引、相关性排名以及信息过滤中非常重要。例如,当你在网上查找查询时,算法就依赖于所有这些概念来返回结果。
- 在第四周,你将构建你的第一个简单的机器翻译系统,并利用局部敏感哈希来提高最近邻搜索的性能。
感谢尤尼斯和卢卡斯。那么,让我们开始学习,进入下一个视频吧。
总结 ✨
本节课中,我们一起学习了自然语言处理第一门课程的总体框架。我们了解到,本课程将围绕文本分类(使用逻辑回归和朴素贝叶斯方法)和向量空间模型展开,并最终引导我们构建一个基础的机器翻译系统。这些知识是进入自然语言处理领域的重要基石。
004:监督机器学习与情感分析 🧠
在本节课中,我们将学习监督机器学习,特别是逻辑回归算法。我们将了解如何通过一系列步骤实现逻辑回归,并将其应用于情感分析任务,即判断一条推文的情感是积极还是消极。
监督机器学习概述
在监督机器学习中,我们拥有输入特征 X 和对应的标签 Y。为了确保基于数据做出最准确的预测,我们的目标是尽可能降低错误率或成本。


为实现这一目标,我们将运行预测函数。该函数接收参数和数据,将特征映射到预测输出 Ŷ。当期望值 Y 与预测值 Ŷ 之间的差异最小时,就实现了从特征到标签的最佳映射。

成本函数通过比较输出 Ŷ 与标签 Y 的接近程度来实现这一目标。然后,我们可以更新参数并重复整个过程,直到成本最小化。

情感分析:一个分类任务
上一节我们介绍了监督机器学习的基本框架,本节中我们来看看一个具体的应用:情感分析。


在这个例子中,我们有一条推文:“I‘m happy because I’m learning NLP. 😊”。此任务的目标是预测一条推文的情感是积极的还是消极的。
我们将从一个训练集开始,其中具有积极情感的推文标签为 1,具有消极情感的推文标签为 0。


对于此任务,我们将使用逻辑回归分类器,它将观察结果分配到两个不同的类别中。接下来,我将展示如何做到这一点。

构建情感分析分类器的步骤
为了构建一个能够预测任意推文情感的逻辑回归分类器,我们需要遵循以下步骤。
以下是实现分类器的三个主要阶段:

-
特征提取:首先,处理训练集中的原始推文并提取有用的特征。
![]()
-
模型训练:然后,在最小化成本的同时训练你的逻辑回归分类器。

- 进行预测:最后,你将能够使用训练好的模型进行预测。
![]()
总结
本节课中,我们一起学习了如何对给定推文进行分类,判断其情感是积极还是消极。为了实现这一目标,你首先需要提取特征,然后训练模型,最后基于训练好的模型对推文进行分类。
在下一个视频中,你将学习如何提取这些特征。让我们一起来看看具体如何操作。
005:吴恩达《自然语言处理》P5 - 词汇特征提取 📚
在本节课中,我们将学习如何将一段文本(例如一条推文)表示为一个数字向量。这是自然语言处理中的一项基础且关键的技术,为后续的文本分类、情感分析等任务奠定基础。

构建词汇表 📖
上一节我们介绍了文本向量化的目标,本节中我们来看看实现它的第一步:构建词汇表。
想象一个推文列表,它看起来是这样的。你的词汇表 V 将是这个推文列表中所有独特单词的集合。为了得到这个列表,你需要遍历所有推文中的所有单词,并保存搜索过程中出现的每一个新单词。
在这个例子中,你会得到单词 “I”,然后是 “am” 和 “happy”,接着是 “because”,以此类推。但请注意,单词 “I” 和 “am” 不会在词汇表中重复出现。

基于词汇表提取特征 🔍
现在,让我们使用你的词汇表来为一条具体的推文提取特征。
以下是具体步骤:
- 检查词汇表中的每一个单词是否出现在这条推文中。
- 如果出现,例如单词 “I”,则为该特征赋值为 1。
- 如果不出现,则赋值为 0。

在这个例子中,你的推文表示向量将包含六个 1 和许多个 0。这些 0 对应着词汇表中所有未出现在这条推文里的独特单词。

稀疏表示与模型参数 🧮
现在,让我们更仔细地观察这条推文的向量表示。
这种非零值相对较少的表示类型被称为稀疏表示。这种表示的特征数量等于整个词汇表的大小。对于每一条推文,它都会有大量特征值为 0。
使用这种稀疏表示,一个逻辑回归模型将需要学习 n + 1 个参数,其中 n 等于你的词汇表大小。

大规模词汇表带来的挑战 ⚠️
你可以想象,对于大规模的词汇表,这会带来问题。训练你的模型将需要过多的时间,进行预测所需的时间也会远超必要。


课程总结 ✨
本节课中我们一起学习了如何将给定文本表示为一个维度为 V 的向量。具体来说,我们针对一条推文进行了操作,并成功构建了一个维度为 V 的词汇表。
随着 V 变得越来越大,你将面临某些挑战。在下一个视频中,你将学习如何识别这些问题。
006:负频率与正频率 📊
在本节课中,我们将学习如何生成词频统计,并将其作为特征输入到逻辑回归分类器中。具体来说,我们将学习如何统计每个单词在积极类别和消极类别中出现的次数,并利用这些统计信息构建特征。
概述
我们将通过一个简单的例子,展示如何为情感分析任务构建正频率和负频率表。这些频率表将帮助我们理解每个单词在不同情感类别中的分布情况。
构建词汇表
首先,我们需要一个语料库和对应的词汇表。假设我们有一个包含四条推文的语料库,词汇表由其中所有不重复的单词组成。
例如,我们的词汇表可能包含以下8个单词:happy, sad, good, bad, love, hate, great, terrible。



区分积极与消极类别
在情感分析中,我们通常有两个类别:积极情感和消极情感。假设我们的语料库中,有两条推文属于积极类别,另外两条属于消极类别。
以下是积极类别的推文示例:
- "I am happy and love this."
- "This is good and great."
以下是消极类别的推文示例:
- "I am sad and hate this."
- "This is bad and terrible."


计算正频率

正频率是指每个单词在积极类别推文中出现的次数。我们逐一检查词汇表中的每个单词,并统计它在积极推文中的出现次数。
例如,单词 happy 在第一句积极推文中出现一次,在第二句积极推文中没有出现,因此它的正频率为1。
以下是计算正频率的步骤:
- 初始化一个空字典,用于存储每个单词的正频率。
- 遍历每条积极推文。
- 对推文进行分词处理。
- 对于每个单词,如果它已经在字典中,则将其计数加一;否则,将其添加到字典中并设置计数为一。

通过这种方法,我们可以得到每个单词的正频率表。

计算负频率

负频率是指每个单词在消极类别推文中出现的次数。计算负频率的方法与计算正频率类似,只是我们遍历的是消极推文。

例如,单词 sad 在第一句消极推文中出现一次,在第二句消极推文中没有出现,因此它的负频率为1。
以下是计算负频率的步骤:
- 初始化一个空字典,用于存储每个单词的负频率。
- 遍历每条消极推文。
- 对推文进行分词处理。
- 对于每个单词,如果它已经在字典中,则将其计数加一;否则,将其添加到字典中并设置计数为一。
通过这种方法,我们可以得到每个单词的负频率表。


构建频率字典
在实际编程中,我们通常会将正频率和负频率合并到一个频率字典中。这个字典的键是 (单词, 类别) 对,值是该单词在对应类别中出现的次数。
例如,频率字典可能如下所示:
freq_dict = {
('happy', 'positive'): 2,
('sad', 'negative'): 1,
('good', 'positive'): 1,
('bad', 'negative'): 1,
('love', 'positive'): 1,
('hate', 'negative'): 1,
('great', 'positive'): 1,
('terrible', 'negative'): 1
}
这个频率字典为我们提供了每个单词在不同情感类别中的分布情况,是后续特征提取的基础。

总结
本节课中,我们一起学习了如何构建正频率和负频率表,以及如何将它们合并成一个频率字典。这些频率统计信息将作为特征输入到逻辑回归分类器中,帮助我们进行情感分析任务。
在下一节课中,我们将学习如何利用这个频率字典来表示一条推文,并提取有效的特征用于分类。
007:使用频率进行特征提取 📊
在本节课中,我们将学习如何利用词频将一条推文表示为仅包含三个特征的向量。这种方法能显著提升逻辑回归分类器的训练速度,因为模型需要学习的特征数量从词汇表大小 V 减少到了仅3个。
从词汇表向量到频率向量 🔄
上一节我们介绍了如何将推文编码为维度为 V 的向量。本节中,我们来看看如何将其转换为一个维度仅为3的向量表示。
构建频率字典 📖
频率字典记录了每个词在特定情感类别(积极或消极)的推文中出现的次数。它本质上是一个映射,将 (词语, 类别) 对关联到对应的频率值。
以下是构建频率字典的步骤:
- 遍历已标注情感的训练数据集。
- 对于每条推文中的每个词,根据推文的情感标签,在对应的
(词语, 类别)计数上加一。


提取三维特征向量 🧮
现在我们已经有了频率字典,接下来看看如何用它为情感分析提取特征。特征向量包含三个部分。
以下是特征向量的三个组成部分:
- 偏置单元:始终为1,表示为
x_bias = 1。 - 积极频率和:推文中所有独特词汇在积极类别下的频率值之和。
- 消极频率和:推文中所有独特词汇在消极类别下的频率值之和。

因此,一条推文 M 的特征向量可以表示为:
x = [1, sum(positive_freq(word) for word in unique(M)), sum(negative_freq(word) for word in unique(M))]

特征提取实例 ✍️
让我们通过一个具体例子来理解这个过程。假设我们有以下推文:
“I am happy because I am learning.”

假设从上一讲中我们得到了如下积极类别的频率表:

| 词语 | 积极频率 |
|---|---|
| I | 3 |
| am | 3 |
| happy | 2 |
| because | 1 |
| learning | 2 |
首先,识别推文中的独特词汇:{I, am, happy, because, learning}。其中,happy 和 because 未出现在上表的词汇中(仅为示例假设,实际应从完整字典查找)。
计算第二个特征(积极频率和):
我们需要对推文中出现的、在频率字典里存在的词的积极频率进行求和。
sum = freq(I) + freq(am) + freq(learning) = 3 + 3 + 2 = 8
计算第三个特征(消极频率和):
类似地,我们需要查找这些词在消极类别下的频率并求和。假设其消极频率如下:

| 词语 | 消极频率 |
|---|---|
| I | 3 |
| am | 4 |
| learning | 4 |
sum = freq_neg(I) + freq_neg(am) + freq_neg(learning) = 3 + 4 + 4 = 11

最终特征向量:
因此,这条推文最终的三维特征向量表示为:[1, 8, 11]。

总结与预告 🎯
本节课中我们一起学习了如何利用词频字典,将推文从高维的词汇表表示压缩成一个仅包含三个数值的特征向量。这种方法的核心在于求和推文中独特词汇在积极和消极两类中的出现频率。
你已经掌握了如何用三维向量表示一条推文。在下一个视频中,我们将学习如何对推文进行预处理,预处理后的词汇将构成我们频率字典的基础,从而使特征提取更加有效和准确。
008:文本预处理 🧹
在本节课中,我们将学习自然语言处理中的两个核心预处理概念:词干提取与停用词移除。我们将通过一个具体的推文处理示例,详细讲解如何应用这些技术来清理和简化文本数据,为后续的机器学习任务做好准备。
概述:文本预处理的目标 🎯
文本预处理的目标是清理原始文本数据,移除冗余或无关的信息,同时保留核心语义内容。这能帮助机器学习模型更高效、更准确地学习文本特征。本节将重点介绍两种关键技术:停用词移除和词干提取。

停用词与标点符号移除 🗑️
首先,我们需要移除那些对文本情感或核心含义贡献不大的词语,即“停用词”,以及标点符号。以下是具体步骤。

停用词移除
停用词通常包括常见的功能词,如“and”、“is”、“at”等。在实际操作中,你需要将文本与一个预定义的停用词列表进行比较,并移除所有匹配的词语。
例如,对于推文:
I am tuning up an AI model at the lab!
移除停用词“I”、“am”、“an”、“at”、“the”后,得到:
tuning up AI model lab!

代码示例(伪代码):
stop_words = ["i", "am", "an", "at", "the"]
filtered_words = [word for word in tweet_words if word not in stop_words]
标点符号移除

接下来,移除标点符号。在本例中,我们移除了感叹号。
处理后的文本为:
tuning up AI model lab

注意:在某些自然语言处理任务中,标点符号可能包含重要信息(如情感强度)。因此,是否移除标点需根据具体任务决定。

处理用户名与URL 🔗
在社交媒体文本(如推文)中,常包含用户名(以@开头)和URL链接。对于情感分析等任务,这些元素通常不提供有价值的信息,可以移除。
例如,移除“@hindsite”和“http://example.com”后,文本进一步简化为只包含核心内容的词语。

词干提取 🌱

上一节我们清理了文本中的冗余元素,本节中我们来看看如何通过词干提取来进一步规范化词汇。

词干提取是将词语转换为其基本形式(词干)的过程。词干是构成该词及其衍生词的基础字符集合。
词干提取示例

以词语“tuning”为例:
- 词干为 tun
- 添加字母“e”形成“tune”
- 添加后缀“ed”形成“tuned”
- 添加后缀“ing”形成“tuning”
通过对整个语料库进行词干提取,像“tune”、“tuned”、“tuning”这样的词都会被归约为同一个词干“tun”。这能显著缩小词汇表规模,而不损失关键信息。
公式化描述:
stem(“tuning”) = “tun”
stem(“tuned”) = “tun”
stem(“tune”) = “tun”
统一小写
为了进一步缩减词汇表,通常还将所有词语转换为小写。这样,“Great”、“great”和“GREAT”就会被视为同一个词。
预处理流程总结 📝
以下是整个预处理流程的总结,从原始推文到最终处理后的单词列表:

- 移除停用词:过滤掉常见功能词。
- 移除标点符号:根据任务需求决定是否移除。
- 移除用户名与URL:清理社交媒体特有的无关元素。
- 词干提取:将所有词语还原为其词干形式。
- 统一小写:将所有字符转换为小写。

最终,推文“I am tuning up an AI model at the lab!”经过预处理后,会得到一个简洁的单词列表:['tun', 'up', 'ai', 'model', 'lab']。

总结 🎓
本节课中,我们一起学习了自然语言处理中文本预处理的两个核心步骤:停用词移除和词干提取。我们通过一个推文示例,演示了如何逐步清理文本、移除无关信息,并将词汇规范化,为构建有效的文本特征表示打下基础。掌握这些预处理技术,是进行后续情感分析、文本分类等高级自然语言处理任务的关键第一步。
在接下来的课程中,你将学习如何利用这些预处理后的文本来构建特征矩阵,以供机器学习模型使用。
009:整合所有内容 🧩
在本节课中,我们将学习如何整合之前学到的所有自然语言处理步骤,为情感分析任务构建一个特征矩阵。我们将从单条推文的处理开始,逐步扩展到处理整个数据集,最终生成可用于逻辑回归分类器的输入矩阵。
上一节我们介绍了如何为单条推文提取特征。本节中,我们来看看如何为整个训练集的所有推文构建特征矩阵。
具体来说,我将引导你完成一个算法,该算法允许你生成这个 X 矩阵。让我们看看如何构建它。

首先,回顾一下处理单条推文的过程。你会预处理一条推文,得到一个包含所有相关信息的单词列表。利用这个单词列表,你可以通过频率字典映射得到一个良好的表示,最终得到一个向量。这个向量包含一个偏置单元和两个附加特征:处理后的推文中每个单词在积极推文中出现次数的总和,以及在消极推文中出现次数的总和。

在实践中,你需要对一组 M 条推文执行此过程。因此,给定一组原始推文,你需要逐一预处理它们,得到一组单词列表(每条推文对应一个列表)。最后,你能够使用频率字典映射来提取特征。
最终,你将得到一个具有 M 行和三列的矩阵 X,其中每一行包含你每条推文的特征。

以下是构建特征矩阵 X 的通用实现步骤:

- 构建频率字典:首先,你需要构建一个频率字典。
freqs = build_freqs(tweets, labels)

- 初始化矩阵:然后,初始化矩阵 X 以匹配你的推文数量。
X = np.zeros((m, 3))

- 遍历并处理推文:之后,你需要仔细遍历你的推文集。
for i in range(m): tweet = tweets[i]


- 预处理每条推文:对每条推文进行预处理,包括删除停用词、词干提取、删除URL和句柄、以及转换为小写。
processed_tweet = process_tweet(tweet)

- 提取特征:最后,通过累加推文中单词的积极和消极频率来提取特征。
X[i, :] = extract_features(processed_tweet, freqs)
对于本周的作业,我们提供了一些辅助函数,例如 build_freqs 和 process_tweet。然而,你需要自己实现从单条推文中提取特征的函数。
虽然涉及不少代码,但至少现在你有了自己的 X 矩阵。在下一个视频中,我们将展示如何将这个 X 矩阵输入到你的逻辑回归分类器中。让我们看看如何操作。

本节课中我们一起学习了如何整合预处理、特征提取等步骤,为整个数据集构建特征矩阵 X。我们回顾了从单条推文到多条推文的处理流程,并概述了实现这一过程的关键代码步骤。掌握这一整合过程是将自然语言处理技术应用于实际情感分析任务的核心。
010:逻辑回归概述 📊
在本节课中,我们将学习逻辑回归的基本概念。逻辑回归是一种用于分类任务的监督学习算法,特别是在情感分析中,它可以帮助我们判断一条推文的情感是积极的还是消极的。我们将了解其核心函数、工作原理以及一个简单的应用示例。
监督学习回顾 🔄
上一节我们介绍了特征提取。本节中,我们来看看如何利用这些提取的特征进行预测。
在监督机器学习中,我们拥有输入特征和一组标签,目标是基于数据做出预测。我们使用一个带有参数的函数,将特征映射到输出标签。为了获得从特征到标签的最佳映射,我们需要最小化一个成本函数。该函数通过比较预测输出 Ŷ 与数据中的真实标签 Y 的接近程度来工作。之后,参数被更新,并重复此过程,直到成本被最小化。
逻辑回归函数 🧮

对于逻辑回归,这个函数 F 就是 Sigmoid 函数。

用于逻辑回归分类的函数 h 是 Sigmoid 函数。它依赖于参数向量 θ 和特征向量 x⁽ⁱ⁾,其中 i 用于表示第 i 个观测值或数据点(在推文语境中,就是第 i 条推文)。

Sigmoid 函数的形式如下:
h(x⁽ⁱ⁾, θ) = 1 / (1 + e^(-θᵀ x⁽ⁱ⁾))
当 θᵀ x 趋近于负无穷时,该函数值趋近于 0;当 θᵀ x 趋近于正无穷时,函数值趋近于 1。

分类与阈值 ⚖️
对于分类任务,需要一个阈值。通常该阈值设置为 0.5,这个值对应于 θᵀ x 等于 0 的情况。
因此,每当点积 θᵀ x 大于或等于 0 时,预测为积极情感。

每当点积 θᵀ x 小于 0 时,预测为消极情感。


应用示例:推文情感分析 🐦
让我们在熟悉的推文情感分析语境中看一个例子。
观察以下推文:
“I am happy because I am learning NLP”
经过预处理后,你会得到一个如下列表:
[‘i’, ‘happy’, ‘learn’, ‘nlp’]
请注意,句柄被删除,所有字母转为小写,并且单词被还原为其词干。

然后,给定一个频率字典,你能够提取特征,并得到一个类似于以下向量的特征向量:
x = [1, 8.0, 4.0]
其中包含一个偏置单元,以及两个特征:处理后的推文中所有单词的积极频率总和与消极频率总和。
现在,假设你已经拥有一个最优的参数集 θ:
θ = [0.05, 0.1, -0.2]
你将能够计算出此情况下的 Sigmoid 函数值,最终预测为积极情感。

总结 📝
本节课中我们一起学习了逻辑回归的概述。我们回顾了监督学习的基本流程,介绍了逻辑回归的核心——Sigmoid 函数及其公式,解释了分类阈值的作用,并通过一个推文情感分析的例子演示了从特征提取到最终预测的完整过程。
现在你已经了解了逻辑回归的表示方法,在接下来的课程中,你将学习如何训练这样一个逻辑回归分类器。
011:逻辑回归训练 🧠
在本节课中,我们将学习如何从零开始训练一个逻辑回归分类器。具体来说,我们将深入探讨一个算法,该算法能让我们找到最优的参数向量 θ,从而最小化模型的成本函数。
梯度下降算法概述
上一节我们介绍了如何使用给定的参数 θ 对推文进行情感分类。本节中,我们来看看如何从零开始学习我们自己的 θ 参数。
训练逻辑回归分类器的核心是迭代寻找一组能最小化成本函数的参数 θ。这个过程通常通过梯度下降算法实现。
假设你的损失函数仅依赖于参数 θ1 和 θ2,那么成本函数的等高线图可能如左图所示。右图则展示了在迭代过程中成本函数值的变化趋势。




首先,你需要初始化参数 θ。然后,沿着成本函数梯度的方向更新参数。经过100次迭代后,你可能到达图中某个点;经过200次迭代后,到达另一个点,依此类推。

经过多次迭代后,你将收敛到接近最优成本的点,此时训练完成。


训练过程的详细步骤

让我们更详细地审视这个过程。


以下是训练逻辑回归模型的标准步骤:

- 初始化参数向量
θ:通常将参数初始化为零或小的随机值。 - 计算预测值:对于每个观测样本,使用逻辑函数(Sigmoid函数)计算预测值。公式为:
h_θ(x) = g(θ^T x) = 1 / (1 + e^{-θ^T x}) - 计算梯度:计算成本函数
J(θ)关于参数θ的梯度。梯度指明了使成本下降最快的方向。 - 更新参数:沿着梯度的反方向更新参数
θ。更新公式通常为:
θ_j := θ_j - α * ∂J(θ)/∂θ_j
其中α是学习率。 - 计算成本:根据更新后的参数计算当前的成本
J(θ)。 - 判断收敛:根据预设的停止条件(如成本变化小于某个阈值,或达到最大迭代次数)判断是否需要继续迭代。


正如你在其他课程中可能见过的,这个算法被称为梯度下降。


模型评估

一旦你通过训练得到了参数 θ,下一步就是评估你的模型。这意味着你需要判断:当把 θ 代入你的 Sigmoid 函数时,你得到的是一个好的分类器还是一个差的分类器。
在下一节视频中,我们将展示如何进行模型评估。
本节课中,我们一起学习了逻辑回归模型的训练过程。我们了解了梯度下降算法是如何通过迭代更新参数 θ 来最小化成本函数的,并熟悉了训练流程中的关键步骤:初始化、预测、计算梯度、更新参数和判断收敛。掌握这个过程是构建有效分类器的基础。
012:逻辑回归模型测试 🧪
在本节课中,我们将学习如何使用训练好的逻辑回归模型对新数据进行预测,并评估模型的泛化能力。具体来说,我们将介绍如何计算模型在验证集上的准确率。
上一节我们介绍了逻辑回归模型的训练过程,本节中我们来看看如何评估模型的性能。
模型预测过程

首先,你需要使用在训练阶段预留的验证集数据 X_val 和 Y_val,以及通过训练得到的最优参数 theta。

第一步是计算验证集特征 X_val 在参数 theta 下的Sigmoid函数值。
公式:
h_theta = sigmoid(X_val * theta)

然后,你需要判断每个 h_theta 值是否大于或等于一个阈值,该阈值通常设置为0.5。
公式:
prediction = (h_theta >= 0.5)
以下是具体步骤:
- 将
h_theta向量中的每个值与0.5进行比较。 - 如果值大于等于0.5,则预测该样本为正类(标记为1)。
- 如果值小于0.5,则预测该样本为负类(标记为0)。

最终,你将得到一个由0和1组成的预测向量,分别代表对负类和正类样本的预测。
计算模型准确率

得到预测向量后,你可以通过比较预测值与验证集真实标签 Y_val 来计算模型的准确率。
计算方法是:统计预测正确的样本数量,然后除以验证集的总样本数 m。

公式:
accuracy = (sum(prediction == Y_val)) / m
这个指标可以估计你的逻辑回归模型在未见数据上正确工作的概率。例如,准确率为0.5意味着模型预计在50%的情况下表现良好。
以下是计算示例:
假设对于5个样本,你的真实标签 Y_val 和预测向量如下所示:
代码:
Y_val = [0, 1, 0, 1, 1]
prediction = [0, 1, 1, 1, 1]

你需要逐个比较它们的值是否一致。

比较后会得到一个由1(正确)和0(错误)组成的向量,在本例中,第三个样本的预测与标签不一致。

代码:
comparison = [1, 1, 0, 1, 1]

接下来,将预测正确的次数相加,再除以验证集中的总观测数。

计算:
accuracy = (1+1+0+1+1) / 5 = 0.8
因此,你得到的准确率等于80%。
本节课中我们一起学习了如何对逻辑回归模型进行测试。我们掌握了使用Sigmoid函数和阈值进行预测的方法,并学会了通过比较预测结果与真实标签来计算模型的准确率,从而评估模型的泛化性能。
恭喜你完成本周的学习!本周你掌握了许多概念:首先学习了如何预处理文本,然后学习了如何从文本中提取特征,接着学习了如何使用这些特征训练模型,最后在本节课学习了如何测试模型。在本周的编程练习中,你将有机会实践我们讨论的所有概念。你可以自由地开始进行编程练习。
本周最后还有一个可选视频,讲解了逻辑回归成本函数背后的直观原理。如果你不想观看该视频,可以直接进入下周的学习,在那里你将了解一种新的分类算法——朴素贝叶斯。
013:逻辑回归成本函数 🧮
在本节课中,我们将要学习逻辑回归成本函数的设计原理。我们将深入理解为什么成本函数被设计成特定的形式,并分析当预测正确和错误时,成本函数如何变化。
成本函数公式解析 🔍
上一节我们介绍了逻辑回归的基本概念,本节中我们来看看其成本函数的具体构成。虽然成本函数的公式初看起来有些复杂,但将其分解为各个组成部分后,其实相当直观。


逻辑回归的成本函数公式如下:
$$J(\theta) = -\frac{1}{m} \sum_{i=1}^{m} \left[ y^{(i)} \log(h_\theta(x^{(i)})) + (1 - y^{(i)}) \log(1 - h_\theta(x^{(i)})) \right]$$

让我们逐一分析这个公式的各个部分。

公式的整体结构


以下是公式的整体结构解析:
- 求和符号 $\sum_{i=1}^{m}$:表示对训练集中所有 $m$ 个训练样本的成本进行求和。
- 系数 $-\frac{1}{m}$:与求和结合后,表示计算的是所有样本的平均成本。负号确保了最终的成本值 $J(\theta)$ 总是一个正数,这一点我们稍后会看得更清楚。
- 方括号内的两项:这是成本函数的核心,包含两个相加的项。每一项都对应着一种标签情况($y=1$ 或 $y=0$)下的损失计算。


为了理解每一项如何为每个训练样本的成本做出贡献,我们接下来将它们分开来看。

第一项:当真实标签 $y=1$ 时 📈

现在,我们重点分析公式中的第一项:$y^{(i)} \log(h_\theta(x^{(i)}))$。
这一项是真实标签 $y^{(i)}$ 与预测值 $h_\theta(x^{(i)})$ 的对数的乘积。

- 当真实标签 $y=0$ 时:无论逻辑回归函数 $h_\theta(x)$ 返回什么值,由于 $0$ 乘以任何数都是 $0$,因此整个第一项的结果为 $0$。这意味着当标签为 $0$ 时,第一项对总成本没有贡献。
- 当真实标签 $y=1$ 时:这一项简化为 $\log(h_\theta(x^{(i)}))$。
- 如果模型的预测值 $h_\theta(x)$ 接近 $1$(即预测正确),那么 $\log(1) = 0$,该项的值接近 $0$,损失很小。
- 如果模型的预测值 $h_\theta(x)$ 接近 $0$(即预测错误),那么 $\log(0)$ 会趋近于负无穷大。直观上,这意味着当标签为 $1$ 但预测为 $0$ 时,成本会变得极高。


由此可见,第一项专门负责处理真实标签 $y=1$ 的情况。当预测与标签一致时,损失小;当预测与标签不一致时,损失急剧增大。



第二项:当真实标签 $y=0$ 时 📉
理解了第一项后,我们再来看看成本函数公式中的第二项:$(1 - y^{(i)}) \log(1 - h_\theta(x^{(i)}))$。

- 当真实标签 $y=1$ 时:$(1-y)$ 项变为 $0$,因此无论 $h_\theta(x)$ 返回何值,整个第二项的结果都是 $0$。这意味着当标签为 $1$ 时,第二项对总成本没有贡献。
- 当真实标签 $y=0$ 时:这一项简化为 $\log(1 - h_\theta(x^{(i)}))$。
- 如果模型的预测值 $h_\theta(x)$ 接近 $0$(即预测正确),那么 $\log(1-0) = \log(1) = 0$,该项的值接近 $0$,损失很小。
- 如果模型的预测值 $h_\theta(x)$ 接近 $1$(即预测错误),那么 $\log(1-1) = \log(0)$ 会趋近于负无穷大。这意味着当标签为 $0$ 但预测为 $1$ 时,成本会变得极高。


由此可见,第二项专门负责处理真实标签 $y=0$ 的情况。其行为模式与第一项对称。

成本函数的直观理解 🎯



通过以上分析,我们可以看到,成本函数中有一项专门对应标签 $y=1$,另一项专门对应标签 $y=0$。在每一项中,我们都在计算一个介于 $0$ 和 $1$ 之间的值的对数,其结果总是一个负数。公式最前面的负号确保了最终的平均成本 $J(\theta)$ 总是一个正数。
现在,让我们从图形上看看对于标签 $0$ 和 $1$,成本随预测值变化的情况。

当真实标签 $y=1$ 时


此时,单个样本的成本简化为 $J(\theta) = -\log(h_\theta(x))$。
- 当预测值 $h_\theta(x)$ 接近 $1$:损失接近 $0$,因为预测与标签高度一致。
- 当预测值 $h_\theta(x)$ 接近 $0$:损失趋近于无穷大,因为预测与标签严重不符。

当真实标签 $y=0$ 时

此时,单个样本的成本简化为 $J(\theta) = -\log(1 - h_\theta(x))$。
- 当预测值 $h_\theta(x)$ 接近 $0$:损失接近 $0$,因为预测与标签高度一致。
- 当预测值 $h_\theta(x)$ 接近 $1$:损失趋近于无穷大,因为预测与标签严重不符。



总结 ✨

本节课中我们一起学习了逻辑回归成本函数的工作原理。
我们看到了当预测值 $\hat{y}=1$ 而真实标签 $y=1$ 时会发生什么,也看到了当预测值 $\hat{y}=0$ 而真实标签 $y=0$ 时会发生什么。成本函数通过这种巧妙的设计,为正确预测分配很低的成本,为错误预测分配很高的成本,从而驱动模型在训练过程中学习到正确的参数。
在接下来的课程中,你将学习朴素贝叶斯算法,这是另一种分类算法,同样可以用于预测一条推文的情感是积极的还是消极的。
015:自然语言处理先驱访谈 - 克里斯·曼宁 🧠💬

在本节课中,我们将跟随吴恩达(Andrew Ng)的访谈,深入了解斯坦福大学教授、人工智能实验室主任克里斯·曼宁(Chris Manning)的学术生涯、研究思想以及对自然语言处理(NLP)领域的深刻见解。我们将探讨他从语言学转向计算语言学的历程、在深度学习应用于NLP方面的开创性工作,以及对未来研究方向的展望。
学术生涯的起点:从语言学走向AI 🤔➡️💻
上一节我们介绍了本次访谈的背景,本节中我们来看看克里斯·曼宁教授是如何开启他的AI研究之路的。
我的背景在某种意义上并非始于人工智能领域。本科时,我主修计算机科学和数学,但也对语言学产生了浓厚兴趣,并完成了语言学的专业和荣誉学位。因此,我的出发点很大程度上是认知科学的视角。人类语言令人着迷,这些小小的人类个体似乎在不那么出色的认知能力时期就掌握了它。人类语言学习是如何发生的?在语言学领域,20世纪下半叶的主导思想是诺姆·乔姆斯基(Noam Chomsky)的思考。乔姆斯基是语言学界的杰出人物,就像20世纪上半叶的R.A.费希尔(R.A. Fisher)在统计学领域一样。乔姆斯基持有非常坚定的立场,认为人类不可能仅从数据中学习语言,人类大脑中必然存在先天的机制来支持语言学习。这是一个很大的话题。即使在那个时候,考虑到人类语言是进化史上极其晚近的产物,这对我来说也似乎难以置信。我对如何学习语言的想法很感兴趣,这引导我开始关注机器学习。
那是在20世纪80年代末,我本科即将毕业的时候。如今,机器学习是一个庞大且占主导地位的领域,以至于人工智能和机器学习这两个术语几乎可以互换,因为AI中的绝大部分内容都是机器学习。但在当时,情况完全不同。机器学习只是AI中一个非常边缘、几乎无人涉足的分支。当时只有卡内基梅隆大学(CMU)的赫希(Heime)、卡博内尔(Carbonell)和汤姆·米切尔(Tom Mitchell)编辑的两三本书,汇集了一些关于机器学习和早期决策树算法(如ID3算法)的论文。机器学习几乎不存在,但我对这些关于计算机如何学习的想法很感兴趣。正是这个切入点,引导我最终成为了一名AI研究员。
在当时,使用数据来学习语言,而不是手工编写像上下文无关语法(CFG)这样的规则来理解语言,并非直观的选择,而这正是当时人们试图做的事情。
早期信念:机器学习在NLP中的应用 ✅
上一节我们了解了曼宁教授进入AI领域的契机,本节中我们来看看他早期对机器学习在NLP中应用的坚定信念。
即使在那个时候,我就是NLP中机器学习的早期信奉者。
绝对如此。目前,基于Transformer的架构确实已成为神经网络中的主导力量。我们在这里的讨论顺序可能有些跳跃,也许我们应该稍后再回到这个话题。但Transformer架构有趣的一点在于,它围绕注意力(attention)机制构建。你可以将注意力视为一种软树结构,它允许你将一个词指向另一个词,从而构建树状结构。我们,尤其是我的博士生约翰·休伊特(John Hewitt),做了一些非常有趣的工作,研究当Transformer模型在数十亿词的人类语言数据上训练时,它们学到了什么。实际上,你可以证明这些模型学到了关于语言结构的各种知识。例如,它们会学习一些共指(co-reference)事实,比如“她”指代“苏珊”,“它”指代“瓶子”。它们也确实从纯文本的词序列中,学到了语言的这种层次化的上下文无关语法结构。这实际上是一个非常简洁的发现,因为语言可以被合理地描述为这种嵌套的、类似树状结构的上下文无关语法。一个大型神经网络或Transformer网络仅从数据中就发现了这方面的特性。
事实上,在现代Transformer观点形成之前,你的斯坦福团队做了一些非常有影响力的早期工作。我知道在深度学习时代之前,你在统计机器翻译方面做了大量工作。随着深度学习开始进入NLP领域,你和你的博士生张长(音译,Tng Long?)实际上发表了关于神经机器翻译(NMT)的最早论文之一,并帮助奠定了现代Transformer模型的一些基础,特别是双线性注意力矩阵(bilinear attention matrix)。请谈谈这方面。
从统计机器翻译到神经机器翻译的演进 🔄
上一节我们提到了曼宁教授在神经机器翻译方面的早期贡献,本节中我们来详细看看这个演进过程。
当然。实际上,在重新开始研究神经网络之前,我在90年代确实做过一点点神经网络工作,那时戴夫·鲁梅尔哈特(Dave Rumelhart)在斯坦福。但我几乎没有深入。所以在21世纪的头十年,我所做的一切都是使用概率建模技术,将概率置于符号结构之上来描述人类语言,这是那个十年中占主导地位的方法。我花了大约十年时间研究的一部分工作是构建机器翻译模型。当时的主导模型被称为基于短语的统计机器翻译。在那个时期,很多技术都发展得相当成熟。有一个相当完善的架构,即因子分解的机器翻译模型,其中一部分是短语表(phrase tables),它给出了将一种语言的短语翻译成另一种语言短语的概率。它们处理翻译的局部部分,然后与所谓的语言模型结合。语言模型是NLP中一个非常占主导地位的专业术语,指的是为语言中的词序列提供概率分布的东西。这一直是NLP中一个非常强大且占主导地位的思想,因为它让你在任何需要词接词的地方,知道哪些词是可能或不可能的。语言模型这个基本思想被用于上下文敏感的拼写纠正(比如谷歌根据上下文漂亮地纠正你的拼写)、语音识别系统,以及这些机器翻译系统。我们有了架构,并且它们运行得相当好。
当谷歌首次推出基于机器学习、从数据中学习的机器翻译系统时,他们使用的就是这些基于短语的统计机器翻译系统。稍微回溯一下故事,当谷歌首次推出机器翻译时,他们授权了一个非常传统的、基于规则的旧机器翻译系统,这个系统最初由Systran公司开发,其根源可以追溯到20世纪50年代最早的机器翻译探索。但他们用了几年后,看到了概率模型在机器翻译方面取得的所有进展,于是转向了那个方向,然后情况变得好多了。我记得弗朗茨·奥赫(Franz Och)是真正的思想领袖,他帮助谷歌将传统模型扩展到海量数据上进行训练,并显著提高了谷歌翻译的性能。
是的,绝对如此。在那个时候,弗朗茨·奥赫是进行基于短语的统计机器翻译模型研究的领军人物之一。他去了谷歌,领导了一个团队,使谷歌拥有了领先的大规模统计短语机器翻译模型实现。它们实际上运行得相当合理,已经实现了这样的目标:你可以输入任何网页和几十种语言,得到大约三分之二可理解的内容,基本上能弄清楚它在说什么话题。这对于2007年到2010年来说很不错。但在2010年到2014年期间,也就是我和吴恩达刚刚讨论树递归神经网络的那个时期,基于短语的统计机器翻译基本上停滞了,没有真正的好想法来取得进一步进展。通过投入更多数据只取得了一点进展。更多数据确实有帮助,这在现代机器学习中仍然成立,但当时的模型容量不足,帮助有限。人们,包括我在那些年里,投入大量精力研究的一个想法是:解决方案肯定是要更多地利用人类语言的语法结构来改进机器翻译系统。因此,主导的研究领域是尝试进行基于句法的机器翻译系统。
这似乎是个好主意,但基本上几乎从未奏效。结果就是,对于某些语言对,它根本不起作用,不比基于短语的统计机器翻译系统更好。而对于其他一些语法结构差异更大的语言对,比如英中机器翻译,它确实有所帮助,可以显示出一些真正的增益。但最终的解决方案,结果证明是少关注句法,多关注数据。
正确。当人们开始探索使用神经方法进行机器翻译时,那项工作基本上就被淘汰了。这真的是……我本来想说这是神经方法在NLP中的第一个巨大成功。这取决于你是否将语音视为NLP的一部分,因为语音识别确实是神经网络方法应用于人类语言问题的第一个巨大成功。但对于基于文本的工作来说,第一个令人震撼的成果确实是构建神经机器翻译系统。这是一个非常成功的领域,因为这里有大量的数据可用,你可以开始训练大型神经网络模型。这项工作最初由谷歌的伊利亚·苏茨克韦尔(Ilya Sutskever)和几位同事完成。对于任何序列(如词序列或DNA序列)的建模,主导模型是循环神经网络。RNN是处理简单序列的模型,并以有限的方式记住之前看到的内容。它有点像连续神经版本的隐马尔可夫模型。他们本质上表明,如果你完全不利用人类语言的结构——这有点颠覆了所有基于句法模型的工作——仅仅构建非常大的、深的循环神经网络,就能给你一个相当不错的机器翻译系统。在当时,NLP中的大多数神经网络建模,如果我们有两层,就称之为“深”,如果有三层或四层,我们就是在真正推进了。而他们立即将其推到了八层深的循环神经网络。这时你开始遇到系统问题,需要在一台配备八个GPU的机器上运行——这个趋势一直延续,我们或许可以多谈谈。他们表明,一个更大的神经网络,仅仅训练两个序列模型(一个是编码源语言的编码器,另一个是生成目标语言词序列的生成器),就能给出一个相当好的机器翻译系统。虽然当时还没有达到最先进的水平,但已经足够接近,显得非常诱人,因为他们所做的只是将两个神经网络连接起来。
如果你不计算循环神经网络单元(特别是LSTM,长短期记忆单元)的库代码,这类工作非常有影响力,使得这项工作得以实现。你只需要围绕神经网络库编写大约500行Python代码,就能拥有一个几乎是最先进的机器翻译系统,这看起来超级有趣。但他们也看到了其中存在一些粗糙和缺失的部分。
因此,在那之后不久,在蒙特利尔与约书亚·本吉奥(Yoshua Bengio)一起工作的Kyunghyun Cho(以及我应该提到的论文第一作者Dzmitry Bahdanau,当时是蒙特利尔的一名年轻学生)提出了可以构建基于注意力的模型的想法。在任何序列点,你都可以计算与其他词(可能在同一个序列,也可能在不同序列)的连接,然后利用这种注意力,你可以计算一个新的向量来影响接下来发生的事情。特别是在机器翻译的上下文中,你可能已经开始翻译,你的翻译开始说“飞行员”,然后你计算注意力回到另一种语言的源语句,并根据你已翻译的内容,确定接下来要翻译源句中的哪些词。这样,你就不必在当前神经网络状态中记住整个源句,而是可以做人类翻译者实际做的事情:动态地回看源句,并确定接下来翻译什么。因此,注意力这个想法是具有变革性的。它也被越来越多地用于视觉系统、知识图谱系统和其他神经网络工作领域。
他们做到了这一点。那么,在Cho和Bahdanau的论文之后不久,你和张长(音译)写了一篇关于双线性注意力的论文,这是怎么来的?
双线性注意力与GloVe的简洁之美 ✨
上一节我们探讨了注意力机制的引入,本节中我们来看看曼宁教授团队在注意力机制和词向量表示方面的简化与创新。
在更早的工作中,实际上始于另一位学生丹尼尔·陈(Daniel Chen?),我们研究了神经张量网络(neural tensor networks)的想法,我们希望能够组合向量并让它们相互影响产生另一个向量,我们通过在它们之间放置一个张量(矩阵的多维推广)来实现。这个想法当时在我小组的其他工作中也在进行。但在这里,我们只是想得到一个注意力分数。在Bahdanau和Cho的工作中,他们所做的是说:我们想要这两个向量之间的注意力分数,让我们通过一个小型神经网络(一个小型多层感知机)来获得一个注意力分数。而在我看来,等等,我可以只做这个简单的事情:双线性注意力,即我有这两个向量,如果我在它们之间放一个矩阵,然后进行向量乘以矩阵再乘以向量的运算,我就能直接得到一个数字。这就是双线性注意力,现在有时也被称为乘法注意力。
这是一个更简单、更直接可解释的注意力概念,因为在某种意义上,注意力的最简单想法是说:你有两个向量,只需将它们点积在一起,就能得到一个相似性分数。但这太死板了,因为你可能只想关注向量的某些部分,或者想知道一个向量的顶部是否与另一个向量的底部相似。因此,通过在中间插入一个矩阵,你可以调节相似性计算。这是一种自然的相似性度量,并且在神经网络中非常容易学习。在某种意义上,这是一个普遍现象。想法如何发展和传播总是很复杂,任何时候都有很多想法在流传。但在某种意义上,现代基于Transformer的模型中占主导地位的做法,本质上就是建立在这个概念之上,但在其之上增加了一个额外的想法。这是一个类似的想法,如果你在中间使用一个巨大的矩阵,这需要很多参数,但如果你使用该矩阵的低秩近似,那就非常接近现代Transformer模型了。
完全正确。在我们最初的工作中,我们只是在中间使用了一个满秩矩阵。但问题是,一个满秩矩阵有很多参数。减少参数的明显方法是说:不,我可以把这个矩阵看作是两个低秩矩阵的乘积。一旦你有了这个想法,与其将它们相乘,你可以说,我可以将这两个低秩矩阵分别应用到两边的向量上,这在计算上更高效。这正是现代Transformer模型所做的:取两个向量,分别乘以一个低秩矩阵,然后取它们之间的点积。这完全等同于说:我通过乘以两个低秩矩阵来形成矩阵,然后进行我的向量-矩阵-向量乘积,但以更高效的方式完成。
事实上,我觉得这在你职业生涯中并非第一次通过观察矩阵乘法来引领领域。如果我看你们团队在词嵌入GloVe方面的工作,我觉得这个想法的核心也是将相对复杂的一套神经网络类的东西简化为一组矩阵运算。请谈谈这个。我至今仍觉得GloVe是一篇非常优雅的论文,因为它真正简化了之前学习词嵌入的一套非常复杂的想法,变成了更简单的形式,比如取词嵌入之间的内积。
谢谢。我确实认为GloVe论文在某种意义上,其主要贡献之一是提供了更好的理解。我的意思不是说它比当时最近开发的其他方法效果更好,但它很有趣,让我们试着思考这里发生了什么,以及这些方法如何与探索过的其他事物联系起来。
是的,所以我们这里的总主题是词向量,即提出一个向量(实数向量)表示,给出词的含义。现在,在你的NLP深度学习课程中,这通常是第一个真正看到的概念,因为它是一个非常有用的、相当简单的概念。在2010年至2013年左右的几年里,有几项工作相当成功地做到了这一点,但都是以非常机械的神经网络方式完成的:这是架构和算法,运行它,在那些日子里通常要运行数周,因为我们还没有非常快的并行计算机,最后会弹出很好的词向量。我们感兴趣的是——这是与博士后杰弗里·彭宁顿(Jeffrey Pennington)的合作——我们实际上试图从这些模型的数学角度更好地理解发生了什么。我们着迷的一点是,实际上有一个更古老的传统,即LSA(潜在语义分析)传统,它利用经典线性代数来获得词义的向量表示。LSA模型在线性代数术语中,无非就是奇异值分解,或者说是对词-词共现计数矩阵使用奇异值分解,然后通过去掉小的奇异值来降维。这很有趣。
模型规模的扩张与未来展望 📈🤔
上一节我们回顾了词向量表示的发展,本节中我们来看看NLP模型规模不断扩大的趋势以及曼宁教授对此的看法。
一个贯穿你多年来观察和参与的所有工作的趋势是NLP模型的规模化。NLP模型似乎变得越来越大,比如GPT-3,但实际上,在序列中还有许多模型。你认为这种趋势会持续多久?你认为有一天,我们是否会回过头来构建更小的模型?
2018年,BERT问世,它表明你可以在一系列任务上做得非常出色,包括问答、自然语言推理、文本分类、命名实体识别、句法分析等,利用了这个预训练的大型语言模型。你只是在一个几十亿词的文本上训练一个大型Transformer来预测句子中被遮蔽的词,但仅仅做这个简单的任务,就为你提供了这些非常好的语言表示,然后可以非常简单地与像softmax分类器这样的东西一起使用,为下游NLP任务提供出色的解决方案。这是一个惊人的成功。我们许多人谈论了大约十年的表示学习(representation learning)思想,就是说在神经网络中,它是关于学习这些有用的中间表示。这真正展示了这对于构建语言表示是有效的,然后可以非常容易地应用于更高的自然语言理解下游任务。
但BERT已经使用了大量的数据和计算。它是在数十亿词的语言上训练的,并且在大量计算机上训练了很长时间。自BERT以来,已经有了很多进一步的进展,但本质上只是来自于越来越多地扩大计算规模。我总是会遗漏一些东西,也有一些新的想法,比如相对注意力(relative attention)改进了效果。但粗略地说,真正推动进步的是投入越来越多的计算资源,在更多数据上运行更大的模型。
是的,所以吴恩达经常在你的一些演讲中使用“AI是新电力”的口号。在最近讨论这种模型越来越大趋势的一些演讲中,我把它反过来,说“电力是新AI”。因为如果你看看正在发生的事情,人们现在训练的模型不仅仅是比BERT模型大10倍或100倍,而是大1000倍、10000倍。训练这些模型所需的计算和能源需求也相应增加。这当然推动了进步。我认为这种趋势不可能持续太久。部分原因是我们快要用尽文本数据,也快要用尽计算机来训练越来越大的模型。尽管从BERT到GPT-3,我记得计算规模大幅扩大,而数据规模扩大得相对温和。所以也许我们有足够的文本数据,我们只需要再增加1000倍或10000倍的大规模计算机?
我不这么认为。你说得对,计算规模的扩大远远大于数据规模的扩大。但尽管如此,现在使用的数据量实际上相当可观。当然有更多数据,但他们实际上使用了相当数量的高质量数据。但是,是的,在某种程度上,聪明的系统人员可以给我们的GPU带来三个数量级的更多算力,即使没有更多文本数据,这些模型也会变得更好,并且可能在未来的几年里变得更好。但我最终不相信这是通往人工智能的道路,也不是进一步改进自然语言处理的有趣途径。毫无疑问……让我问一个可能略有争议的问题。几周前,我们的一位共同朋友评论了这个方向,说:“哦,也许规模化是通往AGI(通用人工智能)的捷径?”你参与了那次对话,我们的一位共同朋友发表了那个评论。你对此有何看法?
对通用人工智能(AGI)路径的思考 🧩
上一节我们讨论了模型规模化的极限,本节中我们来看看曼宁教授对规模化是否通向通用人工智能的看法。
我认为这个想法有其来源。OpenAI最近发布的GPT-3模型真正酷的地方在于,他们随后的工作受到了这个发现的启发:使用这些真正庞大的语言模型,它们实际上可以实现一种通用性,能够用于各种任务,而无需实际训练它们执行任何特定任务。你知道,传统上,如果我们想用神经网络做不同的任务,我们只需获取该任务的数据并训练一个模型。有了像BERT这样的大型预训练语言模型,我们达到了一种中间状态:我们有一个预训练好的、非常好的语言表示基线,但我们仍然需要为每个不同的任务进行微调。我们将其微调为问答模型,然后重新开始,为其他任务(如摘要等)进行微调。

他们在GPT-3中发现,实际上你不再需要这样做了。相反,你可以通过给出几个例子来提示模型你想要它做什么。你会说:好的,我对翻译感兴趣,所以这里有一个句子,我希望你生成的是这个句子的西班牙语翻译。如果你给它几个你希望它做什么的例子,模型
017:概率与贝叶斯规则 🎲

在本节课中,我们将学习概率论的基础知识,特别是条件概率和贝叶斯规则。这些概念是自然语言处理(NLP)中许多应用(如情感分析)的核心。我们会从简单的概率定义开始,逐步推导出贝叶斯规则,并了解其重要性。
概率与条件概率概述
概率是许多NLP应用的基础。本节中,我们将回顾概率和条件概率是什么,它们如何运作,以及如何用数学公式表达。
想象你有一个庞大的推文语料库,每条推文都可以被归类为表达积极或消极情绪,但不能同时属于两者。在这个语料库中,“happy”这个词有时出现在积极推文中,有时出现在消极推文中。让我们探究一下这种情况发生的原因。
概率的计算方式
思考概率的一种方式是计算事件发生的频率。
假设你将事件A定义为一条推文被标记为积极情绪。那么事件A的概率,在此处表示为 P(A),其计算公式为语料库中积极推文的数量除以语料库中推文的总数。
公式:P(A) = (积极推文数量) / (推文总数)
在这个例子中,假设总共有20条推文,其中13条是积极的,那么这个数字就是13除以20,等于0.65。你也可以将这个值表示为百分比,即65%的推文是积极的。
值得注意的是,这里的互补概率,即推文表达消极情绪的概率,P(消极),正好等于1减去积极情绪的概率。
公式:P(消极) = 1 - P(A)
要使这个等式成立,所有推文必须被归类为要么积极,要么消极,不能同时属于两者。
联合概率
让我们以类似的方式定义事件B,即计算包含“happy”这个词的推文数量。在这个例子中,包含“happy”的推文总数(在此表示为“unhappy”)是4。
以下是另一种看待问题的方式。请看图表中推文被标记为积极并且包含“happy”这个词的部分。
在这个图表的背景下,一条推文被标记为积极并且包含“happy”这个词的概率,仅仅是交集部分的面积除以整个语料库的面积之比。
换句话说,如果语料库中有20条推文,其中3条被标记为积极并且包含“happy”,那么相关的概率就是3除以20,等于0.15。
公式:P(A ∩ B) = (同时满足A和B的推文数量) / (推文总数)

从条件概率到贝叶斯规则
上一节我们介绍了基本概率和联合概率。本节中,我们来看看条件概率,并从中推导出在NLP等领域广泛应用的贝叶斯规则。
条件概率
条件概率是指在已知另一个事件(B)发生的情况下,某个事件(A)发生的概率。记作 P(A|B),读作“在B发生的条件下A的概率”。
其数学定义基于联合概率:
公式:P(A|B) = P(A ∩ B) / P(B), 其中 P(B) > 0
这个公式的意思是:在事件B发生的“世界”里,事件A也发生的比例。
贝叶斯规则的推导
贝叶斯规则可以从条件概率的定义直接推导出来。我们知道:
- P(A|B) = P(A ∩ B) / P(B)
- 同样,P(B|A) = P(A ∩ B) / P(A)
从第二个公式解出联合概率 P(A ∩ B),我们得到:
P(A ∩ B) = P(B|A) * P(A)
现在,将这个表达式代入第一个公式中的 P(A ∩ B):
P(A|B) = [P(B|A) * P(A)] / P(B)
这就是贝叶斯规则的标准形式:
公式:P(A|B) = [P(B|A) * P(A)] / P(B)
贝叶斯规则的意义在于,它允许我们利用“逆”条件概率 P(B|A) 和先验概率 P(A)、P(B) 来计算我们可能更关心的条件概率 P(A|B)。
贝叶斯规则在NLP中的应用
理解了贝叶斯规则背后的理论后,你就可以用它来执行具体任务,例如对推文进行情感分析,这将是本周的编程作业。在后续课程中,你还将使用其基本形式来实现自动更正功能。
现在你已经知道如何计算一个词(即“happy”)与积极情绪同时出现的联合概率了。在下一个视频中,我们将讨论朴素贝叶斯方法,它是贝叶斯规则在文本分类中的直接应用。


课程总结
本节课中,我们一起学习了:
- 基本概率:通过事件发生频率来计算概率,例如 P(A) = 计数(A)/总数。
- 联合概率:两个事件同时发生的概率 P(A ∩ B)。
- 条件概率:在已知一个事件发生的情况下,另一个事件发生的概率 P(A|B) = P(A ∩ B) / P(B)。
- 贝叶斯规则:一个强大的公式 P(A|B) = [P(B|A) * P(A)] / P(B),它使我们能够从“逆”条件中推断概率。
这些概念是理解后续更高级NLP模型(如朴素贝叶斯分类器)的基石。请确保你理解了这些基础,以便顺利完成本周的情感分析作业。
018:贝叶斯规则 🧮
在本节课中,我们将学习条件概率,并以此为基础推导和理解贝叶斯规则。贝叶斯规则是概率论中的一个核心定理,在自然语言处理等领域有广泛应用。
概述
我们将从条件概率的概念出发,通过一个情感分析的例子,逐步推导出贝叶斯规则。理解这个规则能帮助我们计算在已知某些信息(条件)下,事件发生的概率。


条件概率简介
上一节我们提到了概率的基本概念。本节中我们来看看条件概率。
条件概率可以理解为:在已知事件A已经发生的前提下,事件B发生的概率。或者说,当我们只考虑属于集合A的元素时,该元素也属于集合B的概率。
用一个情感分析的例子来说明:假设我们有一个推文语料库,其中一些是正面情感,一些包含“开心”这个词。

如果我只让你猜天气情况,你可能很难猜准。但如果我告诉你“我们现在在加州,并且是冬天”,那么你就能做出准确得多的猜测。这就是条件信息的作用。
推导贝叶斯规则
为了推导贝叶斯规则,我们首先需要仔细看看条件概率的计算。

现在,考虑一种情况:我们不再看整个语料库,而只考虑那些包含“开心”这个词的推文。这相当于说,给定一条推文包含“开心”这个词。
这就意味着我们只考虑蓝色圆圈内的推文,其中许多正面推文被排除在外了。
在这种情况下,一条推文是正面情感的概率,给定它包含‘开心’这个词,就简单地等于:
- 既是正面、又包含“开心”的推文数量
- 除以包含“开心”的推文总数。
通过这个计算可以看到,如果你的推文包含“开心”这个词,它有75%的可能性是正面情感。
我们可以对正面推文做同样的分析。紫色区域表示一条正面推文包含‘开心’这个词的概率。
在这种情况下,概率是3除以13,等于0.231。
条件概率的公式化
以上关于满足特定条件概率的讨论,都是在谈条件概率。
条件概率可以解释为:
- 在已知事件A已经发生的情况下,结果B发生的概率。
- 或者,给定我正在查看集合A中的一个元素,该元素也属于集合B的概率。
用之前见过的维恩图来看是另一种方式。
使用之前的例子:
- 一条推文是正面情感的概率,给定它包含“开心”这个词。
- 等于正面推文和包含“开心”的推文两者的交集的概率。
- 除以从语料库中随机抽取一条推文包含“开心”这个词的概率。
用公式表示为:
P(正面 | 开心) = P(正面 ∩ 开心) / P(开心)
交换条件
让我们仔细看看上一张幻灯片的方程。
你可以通过简单地交换两个条件的位置,写出一个类似的方程。
现在,你得到的是一条推文包含‘开心’这个词的概率,给定它是一条正面推文。
公式为:
P(开心 | 正面) = P(正面 ∩ 开心) / P(正面)
掌握了这两个方程,你现在就可以推导贝叶斯规则来组合它们了。
请注意,交集代表相同的量,无论怎么写。知道这一点后,你可以将它从等式中移除。
经过一些代数运算,你就能得到这个方程:
P(正面 | 开心) = P(开心 | 正面) * P(正面) / P(开心)
这就是在之前情感分析问题背景下的贝叶斯规则表达式。
贝叶斯规则的一般形式
更普遍地说,贝叶斯规则表明:
给定y时x的概率,等于给定x时y的概率乘以x的概率与y的概率的比值。
用公式表示为:
P(x|y) = P(y|x) * P(x) / P(y)
就是这样。你已经得到了贝叶斯规则的基本公式,做得很好。
总结与展望

本节课中我们一起学习了如何从条件概率表达式推导出贝叶斯规则。
在本课程的后续部分,你将把贝叶斯规则用于自然语言处理的各种应用中。
目前的主要收获是:
- 贝叶斯规则基于条件概率的数学公式。
- 利用贝叶斯规则,如果你已经知道
P(y|x)以及P(x)和P(y)的比值,你就可以计算出P(x|y)。
恭喜!你现在对贝叶斯规则有了很好的理解。在下一个视频中,你将看到如何开始将贝叶斯规则应用于一个称为朴素贝叶斯的模型,这将使你能够仅使用概率来开始构建你的情感分析分类器。
019:朴素贝叶斯介绍 🧮

在本节课中,我们将学习如何使用一种名为“朴素贝叶斯”的方法来解决文本分类问题。这是一种在文本分类任务中常用且高效的基线方法,其核心概念将在后续课程中反复应用。
上一周我们学习了如何使用逻辑回归对推文进行分类。本节中,我们将看看如何使用朴素贝叶斯方法解决相同的问题。
概述
朴素贝叶斯是监督式机器学习的一个例子,与之前作业中使用的逻辑回归方法有许多相似之处。它之所以被称为“朴素”,是因为该方法假设用于分类的所有特征都是相互独立的,而这在现实中很少成立。然而,你将看到,它作为一种简单的情感分析方法,效果依然很好。
构建词汇表与词频统计
与之前一样,你需要从两个语料库开始:一个用于正面推文,一个用于负面推文。
以下是构建过程:
- 首先,需要提取词汇表,即语料库中出现的所有不同单词及其计数。
- 分别统计每个单词在正面语料库和负面语料库中出现的次数。
- 接着,计算正面语料库中所有单词的总数,并对负面语料库进行同样的操作。例如,正面推文总共有13个单词,负面推文总共有12个单词。
这是朴素贝叶斯的第一步,它非常重要,因为它允许你计算给定类别下每个单词的条件概率。
计算条件概率
现在,将每个单词在某个类别中的频率除以其所在类别的单词总数。
以下是计算示例:
- 对于单词“I”,其在正面类别中的条件概率为
3/13,约等于0.24。 - 对于单词“I”,其在负面类别中的条件概率为
3/12,等于0.25。
将此过程应用于词汇表中的每个单词,即可完成条件概率表的构建。该表的一个关键属性是,对每个类别的所有概率求和,结果将为 1。
分析概率表
让我们进一步分析这个表格,看看这些数字的含义。
首先,注意有多少单词具有几乎相同的条件概率,例如“I”、“am”、“learning”和“NLP”。有趣的是,这些概率相等的单词对情感判断没有贡献。
与这些中性词相反,看看其他一些单词,如“happy”、“sad”和“not”。它们的概率之间存在显著差异。这些是你的“强力词”,倾向于表达一种或另一种情感,在判断推文情感时具有很大权重。
现在看看“because”。如你所见,它只出现在正面语料库中,因此其在负面类别中的条件概率为 0。当这种情况发生时,你无法在两个语料库之间进行比较,这会给计算带来问题。为了避免这个问题,你需要对概率函数进行平滑处理。
应用朴素贝叶斯进行分类
假设你收到朋友的一条新推文:“I‘m happy today. I’m learning.” 你想使用概率表来预测整条推文的情感。
这个表达式称为朴素贝叶斯二元分类推理条件规则。它表示,你将计算推文中每个单词在正面类别的概率与负面类别的概率之比,然后将所有这些比值相乘。
让我们为这条推文计算这个乘积。
对于每个单词,从表中选取其概率:
- “I”:正面概率
0.2,负面概率0.2,比值为0.2/0.2。 - “am”:同样为
0.2/0.2。 - “happy”:
0.14/0.10。 - “today”:表中未找到此单词,意味着它不在你的词汇表中,因此不计入分数。
- 第二个“I”:再次为
0.2/0.2。 - 第二个“am”:再次为
0.2/0.2。 - “learning”:
0.10/0.10。
现在,注意推文中所有中性词如“I”和“am”在表达式中相互抵消了。最终你得到 0.14/0.10,等于 1.4。这个值大于 1,意味着总体而言,推文中的单词更可能对应正面情感。因此,你得出结论:该推文是正面的。
总结

本节课中,我们一起学习了朴素贝叶斯分类法。我们创建了一个表格来存储词汇表中单词的条件概率,并应用朴素贝叶斯推理条件规则对一条推文进行了二元分类。接下来,我们将探讨此实现中的一些问题及其解决方法,并在实现前简化计算过程。
020:拉普拉斯平滑 📊

在本节课中,我们将要学习一种称为“拉普拉斯平滑”的技术。这项技术主要用于解决在计算概率时可能出现的零概率问题,确保我们的模型在处理未见过的数据组合时依然能够有效工作。
概述
在自然语言处理中,我们经常需要计算一个词在给定类别(如正面或负面情感)下出现的概率。其基本公式是统计该词在该类别中出现的次数,然后除以该类别中所有词的总数。然而,如果某个词在训练数据中从未出现在某个类别里,这个概率就会变为零,进而可能导致整个序列的概率为零。拉普拉斯平滑通过微调计算公式,为所有可能的事件分配一个非零的概率,从而巧妙地解决了这个问题。
上一节我们介绍了朴素贝叶斯分类中的基本概率计算,本节中我们来看看当遇到零概率时,如何通过拉普拉斯平滑来修正。
拉普拉斯平滑的原理
拉普拉斯平滑的核心思想是对原始的计数公式进行微小的调整。具体做法是在分子和分母上同时加上一个值。
原始的、未平滑的条件概率计算公式为:
P(word | class) = count(word, class) / N_class
其中:
count(word, class)是词在特定类别中出现的次数。N_class是该类别中所有词的总数。
应用拉普拉斯平滑后,公式变为:
P_smoothed(word | class) = (count(word, class) + 1) / (N_class + V)
其中:
- 我们在分子上加了 1,这确保了即使
count(word, class)为0,概率也不会是零。 - 为了平衡分子增加的部分,使所有词的概率之和仍然为1,我们在分母上加上了 V。V 代表词汇表中唯一词的数量。
这个过程就是拉普拉斯平滑。
应用示例
让我们通过一个具体的例子来理解这个过程。假设我们有一个简单的词汇表和计数。
首先,我们需要确定词汇表中唯一词的数量 V。在这个假设的例子中,我们有8个不同的词。
以下是应用平滑公式计算每个词在“正面”类别中概率的步骤:
- 计算分母:
N_class + V。假设正面类别的总词数N_positive是13,那么分母就是13 + 8 = 21。 - 为每个词计算平滑概率:
- 对于词 “I”,假设它在正面类别中出现3次。平滑概率为
(3 + 1) / 21 ≈ 0.19。 - 对于词 “because”,假设它在正面类别中出现0次。平滑概率为
(0 + 1) / 21 ≈ 0.048。
- 对于词 “I”,假设它在正面类别中出现3次。平滑概率为
注意:示例中的数字经过了四舍五入。通过这个方法,原本概率为零的词“because”现在有了一个小的非零概率,并且表中所有词的概率之和仍然为1。
总结

本节课中我们一起学习了拉普拉斯平滑。我们了解到,直接使用词频计算概率可能导致零概率问题,这会破坏模型的预测能力。拉普拉斯平滑通过在分子加1、分母加词汇表大小V的方式,为所有事件分配了一个小的概率,从而有效避免了零值,并保证了概率分布的合法性。这是构建健壮的概率模型,特别是朴素贝叶斯分类器时,一个非常关键且实用的技巧。
在接下来的视频中,你将学习关于对数似然(log likelihood)的知识,它可以帮助我们更稳定地处理多个微小概率的连乘运算。
021:对数似然第一部分 📊

在本节课中,我们将学习对数似然的概念。这是对上一节课程中计算出的概率取对数,但它在深度学习和自然语言处理中应用起来更为方便。
概述
上一节我们介绍了如何计算单词在积极和消极情感下的条件概率。本节中,我们来看看如何利用这些概率的比值来量化单词的情感倾向,并引入对数似然来解决实际计算中的数值问题。
单词情感比值
首先,我们回顾一下包含单词条件概率的表格。单词的情感含义可能很复杂,但为了情感分类,我们可以将其简化为三类:中性、积极和消极。这些类别可以通过计算条件概率的比值来进行数值估计。
以下是计算词汇表中单词比值的方法:
- 单词 “I”:比值为 0.2 / 0.2 = 1。
- 单词 “am”:比值为 0.2 / 0.2 = 1。
- 单词 “happy”:比值为 0.14 / 0.1 = 1.4。
- 单词 “sad” 和 “not”:比值为 0.1 / 0.15 ≈ 0.67。
通过观察这些比值,我们可以总结出规律:
- 中性词的比值等于 1。
- 积极词的比值大于 1。比值越大,单词的积极程度越高。😊
- 消极词的比值小于 1。比值越小,单词的消极程度越高。
在本周的练习中,你将实现一个根据单词的积极性或消极性进行过滤的函数,上述比值表达式将非常有帮助。
朴素贝叶斯分类公式
这些比值是朴素贝叶斯进行二元分类的核心。我们通过一个之前的例子来说明原因。
回忆一下,我们曾使用以下规则对推文进行分类:如果推文中每个单词对应比值的乘积大于1,则推文为积极;如果小于1,则推文为消极。这个乘积被称为似然。
如果考虑积极推文与消极推文数量的比值,你会得到所谓的先验比。在之前的简单例子中,积极和消极推文数量恰好相同,所以这个比值为1。在本周的练习数据集中,数据是平衡的,因此你将继续使用比值为1。但请注意,在未来构建自己的应用时,如果数据集不平衡,这个先验比项就变得非常重要。
加入先验比后,我们就得到了完整的朴素贝叶斯二元分类公式。这是一个简单、快速且强大的方法,可用于快速建立基线模型。
引入对数似然
现在,是时候提一下实现朴素贝叶斯时另一个重要的考虑因素了。
情感概率计算涉及多个介于0和1之间的数值相乘。在计算机上进行这种乘法运算,存在数值下溢的风险,即返回的数值过小,无法在设备上存储。
幸运的是,有一个数学技巧可以解决这个问题,它利用了对数的性质。我们用于计算朴素贝叶斯得分的公式是先验概率乘以似然。😊
这个技巧就是使用得分的对数,而不是原始得分。这允许我们将之前的表达式改写为对数先验与对数似然之和,而后者又是语料库中所有单词条件概率比值的对数之和。
公式表示为:
log(得分) = log(先验比) + Σ log(条件概率比值)
应用对数似然分类
让我们使用这个方法对推文 “I am happy because I am learning” 进行分类。
你需要计算的是得分的对数,这被称为 λ。λ 是单词为积极概率与消极概率比值的对数。
现在,让我们为词汇表中的每个单词计算 λ:
- 单词 “I”:
λ = log(0.05 / 0.05) = log(1) = 0。根据规则,如果乘积大于1则推文为积极。按此逻辑,λ=0 被归类为中性。 - 单词 “am”:
λ = log(0.04 / 0.04) = 0。 - 单词 “happy”:
λ = log(0.14 / 0.1) ≈ 0.336(假设以e为底),这个值大于0,表示积极情感。
计算出每个单词的 λ 后,你只需将它们求和,就能得到整个推文的对数得分。
总结
本节课中,我们一起学习了以下内容:
- 单词的情感倾向可以通过其积极与消极条件概率的比值来量化。
- 这个比值可以进一步表示为对数形式,即 λ。
- 使用 λ 和对数似然,可以有效地将多个小概率值相乘的问题转化为求和问题,从而降低数值下溢的风险。
- 随着使用的单词数量越来越大,原始概率的乘积很可能非常接近0。因此,我们最终采用了这个比值的对数。😊

通过引入对数似然,我们不仅使计算更加稳定,也为后续更复杂的模型奠定了基础。
022:对数似然与情感推断 🧮

在本节课中,我们将继续学习如何利用对数似然进行情感推断。我们将学习如何根据已计算出的λ值(情感权重)来预测一条推文的情感倾向,并理解其背后的决策逻辑。
在上一节中,我们学习了如何计算每个单词的λ值。本节中,我们来看看如何利用这些λ值来对一条新推文进行情感分类。
推断过程的核心是计算整条推文的对数似然。其公式为:

对数似然 = Σ λ_word (对推文中的每个单词求和)
以下是具体步骤:
- 遍历推文中的每个单词。
- 查找该单词在λ字典中对应的值。如果单词不在字典中(例如中性词或未登录词),则其λ值视为0。
- 将所有单词的λ值相加,得到最终的对数似然分数。
让我们通过一个例子来具体说明。

假设我们有一条推文:“I am happy learning”。我们已经从训练数据中得到了以下λ字典(部分):
happy: λ = 2.2learning: λ = 1.1I,am: λ = 0 (中性词)
现在,计算这条推文的对数似然:
- 单词
I: λ = 0 - 单词
am: λ = 0 - 单词
happy: λ = 2.2 - 单词
learning: λ = 1.1
对数似然 = 0 + 0 + 2.2 + 1.1 = 3.3
得到对数似然分数后,我们如何判断情感呢?
决策规则非常简单:
- 如果 对数似然 > 0,则推文为积极情感。
- 如果 对数似然 < 0,则推文为消极情感。
- 如果 对数似然 = 0,则为中性。
这个规则源于之前概率乘积的阈值(>1为积极)。因为1的对数为0,所以将对数域的决策阈值设为了0。
在我们的例子中,对数似然为3.3,大于0,因此我们判断这条推文的情感是积极的。可以看到,这个分数完全由带有积极情感的单词“happy”和“learning”决定,中性词没有贡献。
让我们快速回顾一下本节的核心内容。
你学会了通过以下方式预测推文情感:
- 对推文中出现的每个单词,从λ字典中取出其对应的λ值。
- 将所有λ值求和,得到该推文的对数似然分数。
- 根据决策阈值0进行判断:分数大于0为积极,小于0为消极。
这种方法简洁有效,突出了关键词汇对整体情感判断的巨大影响力。


恭喜!现在你已经很好地理解了如何计算对数似然以及如何进行情感推断。
在接下来的课程中,我们将进入下一个重要环节:学习如何从头开始训练一个朴素贝叶斯模型,即如何从原始数据中计算出我们刚才使用的λ字典。
023:22_训练朴素贝叶斯模型 🧠

在本节课中,我们将学习如何为情感分析任务训练一个朴素贝叶斯分类器。与逻辑回归或深度学习不同,朴素贝叶斯的训练不涉及梯度下降,而是基于对语料库中词频的统计。我们将一步步构建一个用于推文情感分析的模型。
概述 📋
训练朴素贝叶斯模型的核心在于利用已标注的数据(正面和负面推文)计算词汇在不同类别下的条件概率。整个过程可以分解为几个清晰的步骤,从数据收集到最终模型参数的估计。
训练步骤详解
第一步:数据收集与标注
任何监督式机器学习项目的第一步都是收集用于训练和测试模型的数据。对于推文情感分析,这一步涉及获取一个推文语料库,并将其分为两组:正面推文和负面推文。
第二步:文本预处理
预处理步骤对模型成功至关重要,它包含五个子步骤。以下是具体操作:
- 将文本转换为小写。
- 移除标点符号、URL和用户提及(handles)。
- 移除停用词。
- 词干提取,即将单词还原为其基本形式。
- 分词,将文档分割成单个单词或词元。
在本周的练习中,实现这个处理流程相对简单。但在实际项目中,文本的收集和处理可能会占据项目的大量时间。
第三步:计算词频与条件概率
一旦获得处理后的干净推文语料库,就可以开始计算每个单词在每个类别(正面/负面)中的频率,就像上周所做的那样。这个过程会产生一个频率表。
在同一过程中,可以计算每个语料库中的总词数。从这个频率表中,使用拉普拉斯平滑公式可以得到条件概率,即 P(单词 | 类别)。
公式:
P(word | class) = (freq(word, class) + 1) / (N_class + V)
其中,V 是词汇表中唯一单词的数量。注意,V 只计算表中出现的单词,而非原始语料库的总词数。这将为每个类别中的每个单词生成一个条件概率表,该表只包含大于0的值。
第四步:计算 Lambda 分数
接下来,计算每个单词的 Lambda 分数。该分数是正负面条件概率比值的对数。
公式:
λ(word) = log( P(word | positive) / P(word | negative) )
第五步:估计对数先验
为了估计对数先验,需要统计正面和负面推文的数量。对数先验是正面推文数量与负面推文数量比值的对数。
公式:
log prior = log( D_pos / D_neg )
在接下来的练习中,我们将使用一个平衡的数据集,因此对数先验等于0。但对于不平衡的数据集,这一项将变得重要。
总结 ✨
本节课中,我们一起学习了训练朴素贝叶斯模型可以划分为六个逻辑步骤:

- 获取或标注包含正面和负面推文的数据集。通常,如果推文与你最终模型要使用的语境相匹配,效果会更好。
- 处理原始文本,获得干净、标准化的词元语料库。
- 计算每个单词在每个类别中的词频。
- 使用拉普拉斯平滑公式计算每个单词的条件概率。
- 计算每个单词的 Lambda 因子。
- 估计模型的对数先验,即在你的账户中看到一条正面推文的可能性有多大。
现在,你已经了解了如何构建应用朴素贝叶斯所需的概率表。接下来,我们将要做一件令人兴奋的事情:对句子进行分类。
024:测试朴素贝叶斯模型 🧪
在本节课中,我们将学习如何将训练好的朴素贝叶斯分类器应用到真实的测试数据上,并评估其性能。这个过程与本周第一个视频中的操作类似,但我们会涵盖一些特殊的边界情况。

模型测试流程概述
一旦你训练好了模型,下一步就是对其进行测试。你需要使用之前推导出的条件概率,来预测新的、未见过的推文的情感倾向。之后,你将评估模型的性能,评估方式与上周进行逻辑回归评估时类似,即使用带有标注的测试数据集。
预测新推文的情感
基于已有的计算,你已拥有一张表格,其中包含词汇表中每个独特单词的对数似然(λ)分数。结合你对数先验的估计,你现在可以预测新推文的情感了。
假设有一条新推文:“I pass the NLP interview.”。你可以使用模型来预测这是一条积极还是消极的推文。
以下是预测步骤:
首先,你必须对文本进行预处理:去除标点符号、对单词进行词干提取、并进行分词,以生成一个单词向量,如下所示:
[‘i’, ‘pass’, ‘the’, ‘nlp’, ‘interview’]
接着,你在对数似然表格中查找向量中的每个单词。如果单词在表格中找到(例如 ‘i‘、’pass‘、’the‘、’nlp‘),则将所有对应的 λ 值求和。
那些没有出现在 λ 表格中的值(例如 ‘interview‘),被视为中性词,对总分没有贡献。你的模型只能为它以前见过的单词给出分数。
现在,你加上对数先验,以考虑数据集中类别的平衡或不平衡情况。因此,这个总分加起来是 0.48。
记住,如果这个分数大于 0,那么这条推文的情感是积极的。所以,是的,在你的模型中和现实生活中,通过 NLP 面试都是一件非常积极的事情。你刚刚预测了一条新推文的情感,这很棒!😊
在未见数据上评估性能
现在,是时候在未见数据上测试你的分类器性能了,就像你在上一个模块中对不同场景所做的那样。让我们快速回顾一下应用于朴素贝叶斯的这个过程。
本周的作业包含一个验证集。这些数据在训练期间被预留出来,由一组原始推文(即 X_val)及其对应的情感标签(Y_val)组成。
你需要实现一个准确率函数,以衡量你训练好的模型(由 λ 表格和对数先验表示)在这些数据上的性能。
以下是评估步骤:
首先,像之前一样,计算 X_val 中每个条目的分数。
然后,判断每个分数是否大于 0。这将生成一个由 0 和 1 组成的向量,分别表示验证集中每条推文的预测情感是消极还是积极。
有了这个新的预测向量,你就可以计算模型在验证集上的准确率了。
具体做法是,将你的预测结果与验证数据 Y_val 中每个观测值的真实标签进行比较。
如果值相等,即你的预测正确,你将得到值 1;如果错误,则得到 0。
在将每个预测值与验证集的真实标签进行比较之后,你可以通过将这个向量的总和除以验证集中的样本数量来计算准确率。
这与你为逻辑回归所做的完全一样。
本节总结
在本节中,我们一起回顾了测试朴素贝叶斯模型性能所做的一切。
你使用验证集,利用新训练的模型来预测未见推文的情感分数。
然后,你将预测结果与验证集中提供的真实标签进行比较,从而得到模型正确预测的推文百分比。

你还了解到,未出现在 λ 表格中的单词被视为中性词。
现在,你已经知道如何应用朴素贝叶斯方法来测试示例了。在本周结束的练习中,你将使用它来对推文进行分类。接下来,我将向你展示它还能做些什么。
025:24_朴素贝叶斯的应用 📊
在本节课中,我们将学习朴素贝叶斯方法在自然语言处理中的多种实际应用场景。我们将看到,这个基于概率的简单模型不仅能用于情感分析,还能解决作者识别、垃圾邮件过滤、信息检索和词义消歧等问题。

朴素贝叶斯公式回顾 🔍
上一节我们介绍了朴素贝叶斯的基本原理,本节中我们来看看它的具体应用。朴素贝叶斯方法的核心是估计每个类别的概率,它通过计算类别与文本中词语的联合概率来实现。
朴素贝叶斯公式是先验概率与似然概率乘积的比值:
P(Class | Words) ∝ P(Class) * ∏ P(Word | Class)
这个基于条件概率的比值,其用途远不止于情感分析。
主要应用领域 📈
以下是朴素贝叶斯方法在自然语言处理中的几个常见应用领域。
1. 作者识别
如果你有两个由不同作者撰写的大型文本语料库,你可以训练一个模型来识别新文档的作者。
例如,如果你有一些莎士比亚的作品和一些海明威的作品,你可以为每个词语计算其λ值,以预测一个新词语更可能被莎士比亚使用,还是被海明威使用。这种方法使你能够确定作者身份。
2. 垃圾邮件过滤
利用来自发件人、主题和内容的信息,你可以判断一封电子邮件是否是垃圾邮件。这是朴素贝叶斯最早期的应用之一。
3. 信息检索
在数据库检索中,可以根据查询中的关键词集,来过滤相关与不相关的文档。
在这种情况下,你只需要计算给定查询下每个文档的似然概率。你无法预先知道相关或不相关文档的具体样貌,因此可以计算数据集中每个文档的似然概率,然后根据似然值对文档进行排序。
你可以选择保留前M个结果,或者保留似然概率大于某个特定阈值的结果。
4. 词义消歧
词义消歧旨在根据上下文明确词语的含义。假设在文本中,一个给定的词只有两种可能的解释。
例如,你不知道所读文本中的“bank”一词是指河岸还是指金融机构。为了消除这个词的歧义,你需要计算文档在指向每种可能含义时的得分。
在这种情况下,如果文本涉及的是河流概念而非金钱概念,那么相应的得分就会大于一。
总结与展望 🎯
本节课中我们一起学习了朴素贝叶斯规则的广泛应用。
贝叶斯规则及其朴素近似法在情感分析、作者识别、信息检索和词义消歧等领域有着广泛的应用。它是一种流行的方法,因为训练、使用和解释都相对简单。

在接下来的课程中,你将再次使用贝叶斯规则和朴素贝叶斯方法。现在,你已经做好了充分准备。
正如本视频所示,朴素贝叶斯可以用于许多分类任务。接下来,我将向你展示朴素贝叶斯方法所基于的假设。
026:朴素贝斯的假设 🧠

在本节课中,我们将深入探讨朴素贝斯方法背后的核心假设。我们将了解这些假设是什么,它们为何重要,以及当这些假设在现实数据中不成立时,可能会对模型结果产生何种影响。
朴素贝斯的核心假设
上一节我们介绍了朴素贝斯的基本概念,本节中我们来看看支撑该方法的两个主要假设。
朴素贝斯是一个非常简单的模型,因为它不需要设置任何自定义参数。该方法之所以被称为“朴素”,正是源于它对数据所做的假设。
以下是该方法依赖的两个关键假设:
- 特征独立性假设:模型假设与每个类别相关的预测变量或特征是相互独立的。
- 训练集分布假设:模型假设训练数据集的分布能够代表真实世界的分布。
假设一:特征独立性
让我们深入探讨第一个假设,即特征之间的独立性,并看看它如何影响你的结果。
为了说明特征独立性意味着什么,请看以下句子:
“It is sunny and hot in the Sahara desert.”
朴素贝斯假设一段文本中的单词是彼此独立的。但正如你所见,实际情况通常并非如此。例如,单词“sunny”和“hot”经常像在这个例子中一样同时出现。结合起来看,它们也可能与它们所描述的事物(如海滩或沙漠)相关。
因此,句子中的单词并不总是相互独立的,但朴素贝斯假设它们是。这可能导致在使用朴素贝斯时,低估或高估单个单词的条件概率。
例如,如果你的任务是补全句子:
“It’s always cold and snowy in ______.”
朴素贝斯可能会为“spring”、“summer”、“fall”和“winter”分配相等的概率。尽管从上下文来看,“winter”显然是最有可能的选项。
在这个专业化的后续课程中,你将接触到一些更复杂的方法来处理这个问题。
假设二:训练集分布
现在,我们来看看第二个关于数据分布的假设。
朴素贝斯的另一个问题在于它依赖于训练数据集的分布。一个好的数据集应包含与随机样本相同比例的正向和负向推文。然而,大多数可用的标注语料库都是人工平衡的,就像你将用于作业的数据集一样。
在真实的推文流中,正向推文往往比负向推文出现得更频繁。原因之一是负向推文可能包含被平台禁止或被用户屏蔽的内容,例如不当或冒犯性词汇。如果假设现实世界的行为与你的训练语料库一致,这可能导致模型过于乐观或过于悲观。
关于这一点,本模块的最后一个视频会有更多讨论,该视频将分析朴素贝斯中误差的来源。
总结与回顾
本节课中我们一起学习了朴素贝斯方法的核心假设。
让我们快速回顾一下所有这些新信息:
- 朴素贝斯中的独立性假设很难保证,但尽管如此,该模型在某些情况下仍然表现良好。
- 对于本模块的作业,为了获得准确的结果,你的训练数据集中正向和负向推文的相对频率需要保持平衡。

现在,你已经理解了朴素贝斯方法背后的假设。如果它对某些句子表现不佳该怎么办呢?在下一个视频中,我将展示在这种情况下该如何处理。
027:错误分析 🔍

在本节课中,我们将学习如何分析自然语言处理模型中的错误。无论使用何种NLP方法,总会遇到模型预测出错的情况。我们将探讨导致错误的几个常见原因,并学习如何系统地检查和理解这些错误。
检查预处理后的文本
上一节我们介绍了错误分析的重要性,本节中我们来看看第一个常见错误来源:文本预处理。分析NLP系统错误时,一个主要的考虑因素是查看经过预处理后的文本实际是什么样子。
请看这条推文:
“My beloved grandmother 😔”
这里的表情符号“😔”对推文的情感非常重要,因为它表明了悲伤的情绪。但如果预处理步骤移除了所有标点符号和表情,处理后的推文将只剩下:
“beloved grandmother”
这看起来就像一条非常积极的推文。而“My beloved grandmother!”则表达了截然不同的情感。
因此,请务必检查预处理后的实际文本内容。问题不仅限于标点符号。
请看这个句子:
“This is not good, your attitude is not even close to being nice.”
如果移除了“not”和“this”等中性词,剩下的内容是:
“good attitude close nice”
从这组词来看,分类器可能会推断出非常积极的情感。我们稍后会讨论如何处理否定词和词序问题。
以下是检查预处理文本的关键点:
- 始终检查移除标点、表情、停用词等之后的文本。
- 确认重要的情感指示符(如否定词、强调符号)是否被意外移除。
- 确保模型能够从处理后的文本中获得准确的解读。
词序的重要性
预处理流程并非唯一的潜在问题来源。词序对句子含义的影响同样至关重要。
请看这两条推文:
- “I am happy because I did not go.” (积极情感)
- “I am not happy because I did not go.” (消极情感)
在这个例子中,“not”的位置对情感判断至关重要。但朴素的词袋模型可能会忽略词序,导致分类错误。
因此,词序有时和拼写一样重要。在后续课程中,我们将看到更多处理这类问题的方法。
朴素贝叶斯的局限性:对抗性攻击
另一个与朴素贝叶斯模型相关的问题被称为“对抗性攻击”。这个术语描述了一些常见的语言现象,如讽刺、反语和委婉语。人类能轻易理解这些,但机器却很不擅长。
请看这条推文:
“This is a ridiculously powerful movie. The plot was gripping, and I cried right through until the ending.”
这实际上是一条较为积极的影评。但如果对其进行预处理,可能会得到一系列看似消极的词汇列表。然而,作者实际上是用这些词来描述一部他喜欢的电影。如果对这个词列表使用朴素贝叶斯分类,模型很可能会给出非常消极的分数,而不考虑上下文。

总结与展望
本节课中我们一起学习了如何分析NLP模型中的错误。我们了解到,错误可能源于预处理步骤丢失语义、词序对含义的影响,以及模型(如朴素贝叶斯)难以处理讽刺等语言现象。
尽管朴素贝叶斯方法基于独立性假设可能导致错误,但它仍然是一个强大的基线模型。它依赖于词频统计,简单有效。在下周的课程中,我们将学习如何使用词向量来获得更好的结果。
030:29_向量空间模型 🧠📊

概述
在本节课中,我们将要学习向量空间模型。这是一种将单词和文档表示为向量的方法,能够捕捉单词之间的相对含义和依赖关系。我们将了解这类模型的基本思想、优势以及在自然语言处理中的多种应用。
向量空间模型简介
上一节我们介绍了朴素贝叶斯分类算法,本节中我们来看看向量空间模型。首先,我将向你介绍向量模型背后的基本思想。
你将看到它们的优势,以及它们在自然语言处理中的一些应用。
假设你有两个问题。第一个是“你要去哪里?”,第二个是“你来自哪里?”。
这两个句子除了最后一个词外,其他单词完全相同。然而,它们的意思不同。
另一方面,假设你还有另外两个问题,它们的用词完全不同,但两个句子的意思相同。
向量空间模型将帮助你识别第一对问题或第二对问题在含义上是否相似,即使它们不共享相同的单词。它们可以用于问答、释义和摘要任务中的相似性识别。
向量空间模型还能让你捕捉单词之间的依赖关系。
考虑这个句子:“你用碗吃麦片。”。在这里,你可以看到“麦片”和“碗”这两个词是相关的。
现在,看看另一个句子:“你买东西,别人卖东西。”。这句话的意思是,因为有人买东西,所以有人卖东西。句子的后半部分依赖于前半部分。
使用基于向量的模型,你将能够捕捉到这种以及许多其他类型的不同单词集合之间的关系。
向量空间模型被用于信息提取,以回答“谁、什么、哪里、如何”等类型的问题。它们也被用于机器翻译、聊天机器人编程以及许多其他应用中。
最后,我想与你分享著名英国语言学家约翰·弗斯的一句名言:“观其伴,知其义。”这是自然语言处理中最基本的概念之一。
当使用向量空间模型时,表示是通过识别文本中每个单词周围的上下文来构建的,这捕捉了相对含义。

总结
本节课中我们一起学习了向量空间模型。你了解了向量空间模型,并看到了这些模型在不同类型应用中的使用。
在下一个视频中,你将从头开始构建它们,特别是你将看到如何使用共现矩阵来构建它们。
031:词对词与词对文档向量构建 🧮
在本节课中,我们将学习如何基于共现矩阵来构建向量空间模型。具体来说,我们将探讨两种不同的设计方法:词对词设计和词对文档设计,并了解如何将单词或文档编码为向量。最后,我们还将看到如何在向量空间中衡量单词或文档之间的相似性。

基于共现矩阵构建向量空间
根据你试图解决的具体任务,可以有几种不同的向量空间设计方法。你还会看到如何将单词或文档编码为向量。
词对词设计
为了使用词对词设计获得向量空间模型,你需要构建一个共现矩阵,并从中提取语料库中单词的向量表示。
两个不同单词的共现,是指它们在语料库中在特定单词距离 K 内一起出现的次数。例如,假设你的语料库包含以下两个句子:
- "I like simple data."
- "Data is raw material."
如果设定 K=2,那么与单词 "data" 对应的共现矩阵行将按以下方式填充数值:
- 对于 "simple" 列,数值为 2。因为 "data" 和 "simple" 在第一个句子中距离为1个词,在第二个句子中距离为2个词。
- 对于 "raw" 列,数值为 1。
- 对于 "like" 和 "I" 列,数值均为 0。
因此,单词 "data" 的向量表示将是 [2, 1, 1, 0, 0]。
通过词对词设计,你可以得到一个包含 N 个元素的向量表示,其中 N 的取值范围在1到整个词汇表大小之间。
词对文档设计
词对文档设计的过程非常相似。在这种情况下,你需要统计词汇表中的单词在属于特定类别的文档中出现的次数。
例如,你可能有一个包含不同主题文档的语料库,如娱乐、经济和机器学习。
以下是统计单词在不同类别文档中出现次数的示例:
| 单词 | 娱乐类文档 | 经济类文档 | 机器学习类文档 |
|---|---|---|---|
| data | 500 | 6620 | 9320 |
| film | 7000 | 4000 | 1000 |
一旦你为多组文档或单词构建了表示,你就得到了你的向量空间。让我们以上一个表格为例。
你可以从表格的行中获取单词 "data" 和 "film" 的表示。然而,我们也可以通过查看列来获取每个文档类别的表示。因此,这个向量空间将有两个维度:单词 "data" 的出现次数和单词 "film" 的出现次数。
以下是每个文档类别的向量表示:
- 娱乐类:
[500, 7000] - 经济类:
[6620, 4000] - 机器学习类:
[9320, 1000]
在这个空间中,很容易看出经济类和机器学习类文档彼此之间的相似度,远高于它们与娱乐类文档的相似度。
向量关系与相似性
在向量空间中,你可以找到单词和向量之间的关系,也称为它们的相似性。
接下来,你将学习使用余弦相似度和欧几里得距离来比较向量表示,以获取它们之间的角度和距离。
总结

本节课中,我们一起学习了通过两种不同的设计来构建向量空间:词对词和词对文档。这两种方法分别通过统计单词的共现次数或单词在文档语料中的出现次数来实现。
我们还了解到,在向量空间中,你可以确定不同类型文档(或单词)之间的关系,例如相似性。
现在,你对这些向量空间越来越熟悉了。你已经看到了几种可用于解决特定任务的可能设计,也了解了如何将单词或文档编码为向量。在下一个视频中,你将学习一种新的相似性度量方法——欧几里得距离,它将允许你比较两个向量。
032:31_欧几里得距离 📏

概述
在本节课中,我们将要学习欧几里得距离。这是一种相似性度量方法,用于衡量两个点或两个向量在空间中的远近程度。我们将从二维空间的例子开始,然后将其推广到更高维度的向量空间。
欧几里得距离简介
欧几里得距离是一种相似性度量。这个度量标准允许你识别两个点或两个向量彼此之间的距离。
上一节我们介绍了文档向量的概念,本节中我们来看看如何计算两个文档向量之间的欧几里得距离,并将这个概念推广到更高维度的向量空间。
二维空间中的例子
让我们使用之前见过的两个语料库向量。在那个例子中,有两个维度:单词“data”和“film”在语料库中出现的次数。语料库A是娱乐语料库,语料库B是机器学习语料库。现在,我们将这些向量表示为向量空间中的点。
欧几里得距离是连接这两个点的直线段的长度。要得到这个值,你应该使用以下公式。
以下是计算步骤:
- 计算两个向量在水平方向(第一个维度)上的差值,并将其平方。
- 计算两个向量在垂直方向(第二个维度)上的差值,并将其平方。
- 将这两个平方值相加。
- 对总和取平方根。
这个公式是勾股定理的一个例子。如果你计算方程中的每一项,你应该得到这个表达式。最后,得到一个大约等于10667的欧几里得距离。你可以暂停视频来检查这个过程。
推广到高维空间
当你处理更高维度时,欧几里得距离的计算并不复杂很多。让我们通过一个使用以下共现矩阵的例子来逐步讲解。
假设你想知道单词“ice cream”的向量V和单词“boba”的向量表示W之间的欧几里得距离。首先,你需要获取它们每个维度之间的差值,将这些差值平方,然后求和,最后对结果取平方根。
这个过程是上一张幻灯片中方法的推广。这是你在一个n维向量空间中获取向量表示之间欧几里得距离的公式。
如果你还记得代数知识,这个公式被称为你所比较向量之间差值的范数。
Python代码实现
让我们看一下在Python中实现欧几里得距离的方法。如果你有两个像前面例子中的向量表示,你可以使用numpy的linalg模块来获取它们之间差值的范数。
以下是实现代码:
import numpy as np
# 定义两个向量
v = np.array([...]) # 向量V的值
w = np.array([...]) # 向量W的值
# 计算欧几里得距离(即差值的L2范数)
euclidean_distance = np.linalg.norm(v - w)
print(euclidean_distance)
如果你在Python中实现这段代码,你应该得到这些结果。norm函数适用于n维空间。
核心要点与总结

本节课中我们一起学习了欧几里得距离。主要的收获是:
- 欧几里得距离本质上是连接两个向量的直线长度。
- 要得到欧几里得距离,你必须通过计算你所比较向量之间差值的范数。
- 通过使用这个度量标准,你可以了解两个文档或单词的相似程度。
下节预告
现在你已经学习了欧几里得距离,在下一个视频中,我将展示一种不同类型的相似性函数。具体来说,我将展示余弦相似性函数,它是最流行的相似性函数之一。下个视频见。
033:32_余弦相似度直觉 🧠
在本节课中,我们将要学习余弦相似度。这是一种用于衡量两个向量之间相似程度的函数。它通过计算两个向量之间夹角的余弦值来判断它们是否接近。本节中,你将了解到使用欧几里得距离在比较文档或语料库的向量表示时可能存在的问题,以及余弦相似度指标如何帮助你克服这些问题。
欧几里得距离的局限性 🚧

上一节我们介绍了相似度函数的概念,本节中我们来看看欧几里得距离在特定场景下的不足。
为了说明欧几里得距离可能存在的问题,让我们看一个例子。假设我们处在一个向量空间中,语料库由单词“疾病”和“鸡蛋”的出现次数来表示。
以下是三个语料库的向量表示:
- 食品语料库:与食品相关的文本。
- 农业语料库:与农业相关的文本。
- 历史语料库:与历史相关的文本。
每个语料库都包含与其主题相关的文本,但它们的总单词数各不相同。事实上,农业和历史语料库的单词数量相近,而食品语料库的单词数量相对较少。
定义食品与农业语料库之间的欧几里得距离为 d1,农业与历史语料库之间的欧几里得距离为 d2。如图所示,距离 d2 小于距离 d1。这似乎表明,农业和历史语料库比农业和食品语料库更相似。
余弦相似度的引入 📐
鉴于欧几里得距离的上述问题,我们需要一种更稳健的相似度衡量方法。
另一种确定向量间相似度的常用方法是计算它们夹角的余弦值。如果夹角很小,余弦值将接近1;随着夹角接近90度,余弦值将接近0。

如图所示,食品与农业语料库之间的夹角 α,小于农业与历史语料库之间的夹角 β。在这个特定案例中,这些夹角的余弦值比它们的欧几里得距离更能代表这些向量表示之间的相似性。
以下是余弦相似度的核心公式:
余弦相似度公式:
similarity = cos(θ) = (A·B) / (||A|| * ||B||)
其中 A·B 是向量的点积,||A|| 和 ||B|| 是向量的模(长度)。

余弦相似度的优势与总结 🎯
现在,你已经了解了使用余弦相似度作为比较两个向量表示相似性指标背后的主要直觉。

请记住,与欧几里得距离相比,该指标的主要优势在于它不受表示之间大小差异的影响。因此,即使两个文档的大小差异很大,你也能获得有意义的相似度比较结果。
本节课中我们一起学习了:
- 欧几里得距离的局限性:在比较大小差异显著的文档向量时可能产生误导。
- 余弦相似度的原理:通过计算向量夹角的余弦值来衡量相似度,其值域为[-1, 1],值越接近1表示越相似。
- 余弦相似度的核心优势:对向量的绝对大小不敏感,只关注它们的方向,因此更适合比较文本等长度不一的数据。
如果你有两个大小非常不同的文档,那么使用欧几里得距离并不理想。余弦相似度利用文档向量之间的夹角,因此不依赖于语料库的大小。
034:33_余弦相似度 📐

在本节课中,我们将学习如何计算两个向量之间的余弦相似度。这是一种衡量向量方向相似程度的指标,在自然语言处理中常用于比较词向量或文档向量的相似性。
我们将首先回顾两个核心概念:向量的点积和范数(或称为模长)。掌握了这两项计算后,你就能轻松计算出余弦相似度。
计算余弦相似度 📏
上一节我们介绍了使用两个向量之间夹角的余弦值作为相似性度量的直观理解。本节中,我们将深入解释并展示如何具体计算这个余弦相似度指标。
首先,你需要从代数中回忆一些定义。

向量的范数(或称模长)写作 ||v||,其定义为向量各元素平方和的平方根。用公式表示为:
norm(v) = sqrt(v1² + v2² + ... + vn²)
两个向量的点积,是它们在向量空间每个维度上对应元素乘积的总和。对于向量 v 和 w,其点积公式为:
dot_product(v, w) = v1*w1 + v2*w2 + ... + vn*wn
余弦相似度公式推导 🔄
现在,让我们看看如何用点积和范数来计算余弦相似度。
假设有两个向量 v 和 w,它们之间的夹角为 β。根据定义,夹角 β 的余弦值等于两个向量的点积除以它们范数的乘积。公式如下:
cos(β) = (v · w) / (||v|| * ||w||)
这个值就是余弦相似度。它的范围在 -1 到 1 之间,但对于我们目前所见的、所有维度均为正值的向量空间(如词频向量),其值介于 0 和 1 之间。
实例计算 🧮
让我们回顾上一节中的一个例子。在这个向量空间中,语料库的表示由单词“disease”和“x”的出现次数构成。
- 农业语料库表示为向量 v。
- 历史语料库表示为向量 w。
根据上述公式,余弦相似度的计算表达式如下:
cos(β) = (v1*w1 + v2*w2) / (sqrt(v1²+v2²) * sqrt(w1²+w2²))
将向量表示中的实际数值代入,你会在分子中得到两个单词出现次数的乘积之和,在分母中得到两个语料库向量范数的乘积。最终,你应该会得到一个约为 0.87 的余弦相似度值。
你可以暂停视频,自己动手计算一下。
如何解读相似度得分? 🤔
那么这个指标告诉我们关于两个向量相似性的什么信息呢?以下是关键解读:
当两个向量在向量空间中方向相同时,它们之间的夹角为 0 度。余弦值等于 1(因为 cos(0) = 1)。
当两个向量方向垂直(正交)时,夹角为 90 度。余弦值等于 0。在我们目前所见的向量空间中,这表示它们最大程度地不相似。
由此可见,两个向量夹角的余弦值越接近 1,它们的方向就越接近。

核心要点总结 ✅
本节课中,我们一起学习了如何计算任意一对向量之间的余弦相似度。
重要的结论是:
- 这个度量指标与所比较向量的方向相似性成正比。
- 对于目前所见的向量空间,余弦相似度的取值范围在 0 到 1 之间。
- 一个向量与自身的余弦相似度为 1。
- 两个垂直向量的余弦相似度为 0。
- 相似度越高,得分越高。
现在,你已经掌握了计算和解读余弦相似度的基本方法,这是比较文本表示相似性的一个强大工具。
035:在向量空间中操作词 🧮

概述
在本节课中,我们将学习如何通过简单的向量算术(如向量加法和减法)来操作词向量。你将能够利用已知的词向量关系,预测未知的关系,例如根据已知的首都-国家关系,推断出未知国家的首都。
在向量空间中操作词向量
上一节我们介绍了词向量的基本概念。本节中,我们来看看如何通过向量运算来推断词与词之间的关系。
假设我们有一个包含国家及其首都城市向量的向量空间。我们知道美国的首都是华盛顿特区,但不知道俄罗斯的首都。我们可以利用华盛顿特区和美国之间的已知向量关系来推断俄罗斯的首都。
计算关系向量
首先,我们需要找到华盛顿特区向量和美国向量之间的关系。这可以通过计算两个向量的差来实现:
关系向量 = 华盛顿特区向量 - 美国向量
这个差值向量指明了在向量空间中,从一个国家向量移动到其首都向量所需的方向和距离。
应用关系进行预测
接下来,要找到俄罗斯的首都,我们将俄罗斯的向量加上刚才计算得到的关系向量:
预测的首都向量 = 俄罗斯向量 + 关系向量
计算后,我们可能得到一个具体的向量坐标,例如 (10, 4)。
寻找最相似的词
然而,在词向量集合中,可能没有城市的向量恰好等于 (10, 4)。因此,我们需要找到与这个预测向量最相似的现有词向量。
以下是两种常用的相似度计算方法:
- 欧几里得距离:计算两个向量点之间的直线距离。距离越小,相似度越高。
- 公式:
distance = sqrt((x1 - x2)^2 + (y1 - y2)^2)
- 公式:
- 余弦相似度:计算两个向量之间夹角的余弦值。值越接近1,相似度越高。
- 公式:
similarity = (A · B) / (||A|| * ||B||)
- 公式:
通过比较,我们发现莫斯科的向量与预测向量 (10, 4) 最相似。这样,我们就利用已知的“美国-华盛顿特区”关系,成功预测了俄罗斯的首都是莫斯科。
向量空间表示的重要性
现在,你掌握了一个通过已知词关系来推断未知关系的简单流程。这个流程成功的关键在于,我们使用的词向量空间必须能够捕捉词语之间的相对语义关系。
在自然语言处理中,语义相近的词(例如出现在句子相似位置的词)其向量编码也会相似。当我们将所有向量绘制在二维平面上时,可以看到它们会形成聚类。
你可以利用这种编码的一致性来识别模式。例如,如果你有“医生”这个词,通过计算余弦相似度来寻找最接近的词,你可能会得到“医生们”、“护士”、“心脏病专家”、“外科医生”等。

总结
本节课中,我们一起学习了如何通过向量加减法来操作词向量,并利用已知的语义关系进行推断。我们了解了关系向量的计算方法,以及使用欧几里得距离或余弦相似度来寻找最相似词向量的过程。理解词向量如何捕捉语义关系,是进行更复杂自然语言处理任务的基础。
在下一课中,你将学习如何将这些高维向量可视化在二维平面上。
036:可视化与PCA 📊

概述
在本节课中,我们将要学习如何将高维度的词向量进行降维和可视化。你将了解到主成分分析(PCA)这一算法的基本概念、动机及其在自然语言处理中的应用,特别是用于检查词向量是否捕捉到了词语之间的语义关系。
可视化高维词向量的动机 🎯
上一节我们介绍了词向量的基本概念。本节中我们来看看为什么需要将它们可视化。
我们最终得到的词向量通常具有非常高的维度。我们需要找到一种方法,将这些向量的维度降低到二维,以便能够在XY坐标轴上绘制出来。主成分分析(PCA)算法可以帮助我们实现这一目标。
你将使用主成分分析来可视化那些维度高于你目前所见过的、可以绘制的向量表示。
为了开始,我将为你提供一些关于可视化词向量表示的动机直觉。你将亲眼看到主成分分析是什么,以及它如何用于降维。
想象一下,你的词语在向量空间中有如下表示。在这个场景中,你的向量空间维度高于2。你知道词语“石油”和“天然气”,以及“城市”和“城镇”是相关的,并且你想看看这种关系是否被你的词语表示所捕捉到。
那么,你如何可视化你的词语,以便看到这种关系和其他可能的关系呢?
降维:PCA的作用 ✨
上一节我们提出了可视化高维数据的需求。本节中我们来了解解决这个问题的关键工具。
当你的词语表示存在于高维空间时,降维是完成此任务的完美选择。你可以使用像PCA这样的算法,来获得一个维度更少的向量空间表示。
如果你想可视化你的数据,你可以得到一个具有三个或更少特征的简化表示。
如果你对数据执行主成分分析并获得一个二维表示,你就可以绘制出词语的可视化图。在这种情况下,你可能会发现你最初的表示捕捉到了“石油”和“天然气”以及“城市”和“城镇”之间的关系。因为在你的二维空间中,它们似乎与相关的词语聚集在一起。
你甚至可以发现词语之间你未曾预料到的其他关系,这是一个有趣且有用的可能性。
PCA算法的工作原理 ⚙️
现在你知道了PCA能帮助你实现什么,让我们详细了解一下它是如何工作的。为了简单起见,我将从一个二维向量空间开始讲解。
假设你希望你的数据用一个特征来表示。使用PCA,你首先会找到一组不相关的特征,然后将你的数据投影到一个一维空间,同时尽可能多地保留信息。
正如你所见,这个过程相当直接。
接下来,你将亲自了解这个算法如何工作的细节,包括如何获得不相关的特征。你还将学习如何将数据投影到低维空间进行表示,同时尽可能多地保留信息。
以下是PCA工作流程的核心步骤:
- 数据标准化:确保每个特征的平均值为0。
# 示例:数据标准化 X_normalized = X - np.mean(X, axis=0) - 计算协方差矩阵:找出特征之间的关系。
# 示例:计算协方差矩阵 cov_matrix = np.cov(X_normalized, rowvar=False) - 计算特征值和特征向量:这些决定了主成分(新特征轴)的方向和重要性。
# 示例:计算特征值和特征向量 eigenvalues, eigenvectors = np.linalg.eig(cov_matrix) - 选择主成分:根据特征值大小,选择最重要的k个特征向量作为新的坐标轴。
- 转换数据:将原始数据投影到选定的主成分上,得到降维后的新数据。
# 示例:将数据投影到前两个主成分上 principal_components = eigenvectors[:, :2] X_pca = X_normalized.dot(principal_components)
总结与回顾 📝

本节课中我们一起学习了主成分分析(PCA)这一用于降维的算法。它能为你的数据找到不相关的特征,对于可视化数据以检查你的词向量表示是否捕捉到了词语之间的关系非常有帮助。
你学习了如何使用PCA来可视化高维分量。因此,给定任何D维向量,你都可以将其转换为二维,然后创建一个图表。
在下一个视频中,我们将详细讲解这个算法实际是如何工作的。
037:主成分分析(PCA)算法 📊

在本节课中,我们将学习特征值与特征向量的概念,并了解如何利用它们来降低特征的维度。首先,我们会介绍如何为数据获取不相关的特征,然后讲解如何在尽可能保留原始词嵌入信息的同时,降低词表示的维度。
获取不相关特征
上一节我们介绍了降维的目标。本节中,我们来看看如何为数据获取一组不相关的特征。以下是实现此目标所需的三个步骤:
- 均值归一化数据:首先,对数据进行中心化处理,使其均值为零。
- 计算协方差矩阵:接着,计算数据集的协方差矩阵。
- 执行奇异值分解:最后,对协方差矩阵进行奇异值分解,得到三个矩阵。
奇异值分解得到的第一个矩阵(通常记为 U)的列向量就是特征向量,第二个矩阵(通常记为 S)的对角线元素就是特征值。许多编程库已内置了奇异值分解的实现,因此你无需关心其内部计算细节。
将数据投影到新特征空间
在获得了特征向量和特征值之后,下一步是将原始数据投影到新的、维度更低的特征空间中。
我们使用 U 表示特征向量矩阵,S 表示特征值矩阵。首先,计算词嵌入矩阵与 U 矩阵前 n 列的点积,其中 n 是你希望最终保留的维度数。为了可视化,通常选择二维。
此外,你可以计算新向量空间所保留的方差百分比,以评估信息保留程度。一个重要的注意事项是,特征向量和特征值应按特征值降序排列,这能确保从原始嵌入中保留尽可能多的信息。不过,大多数程序库会自动为你完成排序。
核心概念总结
本节课中我们一起学习了主成分分析的核心步骤与原理:
- 对归一化数据的协方差矩阵进行分解,得到的特征向量指明了不相关特征的方向。
- 与这些特征向量关联的特征值,则表明了数据在这些新特征方向上的方差大小。
- 通过计算词嵌入与特征向量矩阵的点积,可以将数据投影到你选择的、任意维度的新向量空间中。
你现在已经准备好在本周的作业中实现PCA,以可视化词表示了。祝你顺利,并享受学习的乐趣!😊

恭喜你,现在你已经掌握了PCA的所有知识,并了解了实现它所需的全部步骤。下周,我们将学习向量空间,并展示如何利用它们构建一个简单的机器翻译系统。
040:第39讲 概述 🎯

在本节课中,我们将一起学习本课程最后一周的核心内容。我们将了解如何实现机器翻译和文档搜索这两项实用的自然语言处理任务,并预览本周将练习的关键技能。
恭喜你坚持到本课程的最后一周。
现在我将告诉你接下来要学习的内容。
在本课程的第四周,也是最后一周,你将学习如何实现机器翻译,例如将英文单词“Hello”翻译成法文单词“Bonjour”。
你还将学习如何实现文档搜索。例如,给定一个文档(如句子“can I get a refund”),你可以搜索相似的文档,例如“what‘s your return policy”或“may I get my money back”。
你将学习一套可以同时应用于这两项实用NLP任务的技能。
以下是本周课程和作业中你将练习的技能预览。如果现在不理解其中某些术语的含义,请不要担心,在进入作业之前,你将学习并练习每一项内容。
以下是本周你将学习的关键技能:
- 向量变换:你将学习变换向量(如词向量)意味着什么。
- K近邻算法:你将学习如何实现K近邻算法,这是一种搜索相似项的方法。
- 哈希表:你将学习哈希表,它将帮助你将词向量分配到不同的子集中。
- 向量空间划分:你将学习如何将向量空间划分为不同的区域。
- 局部敏感哈希:最后,你将学习如何实现局部敏感哈希,这有助于你执行近似最近邻搜索,这是一种高效的相似词向量搜索方法。
通过练习如何查找相似的词向量,你将有效地掌握如何实现机器翻译和文档搜索。
这是非常令人兴奋的内容,我们希望你会非常喜欢本周的学习。

现在你已经对即将学习的内容和本周的学习目标有了一个概述,我们可以正式开始学习了。
041:词向量转换 🔄

概述
在本节课中,我们将要学习如何利用词向量来实现两种不同语言之间单词的对齐,从而构建一个基础的翻译程序。我们将从机器翻译的概述开始,逐步讲解如何使用矩阵转换词向量,并最终通过优化算法找到一个合适的转换矩阵。
机器翻译概述
上一节我们介绍了词向量及其捕获单词重要属性的能力。本节中,我们来看看如何利用词向量进行机器翻译。
以英法翻译为例,将英文单词翻译成法文单词的一种方法是生成一个包含英文单词及其对应法文单词的详尽列表。若由人工完成,需要精通双语的人员来制作此列表。若希望机器学会此任务,则需要计算与英文和法文相关的词嵌入。
接下来,获取特定英文单词(例如“cat”)的英文词嵌入,然后找到一种方法将该英文词嵌入转换为在法文词向量空间中具有意义的词嵌入。随后,将转换后的词向量与法文词向量空间中最相似的词向量进行匹配,这些最相似的单词即为翻译的候选词。如果机器表现良好,它可能会找到法文单词“chat”(猫)。
为了实现这种转换,我们需要找到一个能够执行此转换的矩阵。
使用矩阵转换向量
为了理解如何使用矩阵转换向量,我们可以尝试以下代码(本讲义的配套笔记本中也包含此代码):
import numpy as np
# 定义矩阵R
R = np.array([[2, 0], [0, -2]])
# 定义向量x
x = np.array([[3, 4]])
# 使用numpy.dot进行矩阵乘法
result = np.dot(x, R)
执行上述代码后,result 是另一个二维向量,这与我们之前看到的结果一致。请亲自尝试运行此代码。
既然我们知道可以存在一个矩阵将英文词向量转换为相关的法文词向量,那么如何定义这个转换矩阵(我们将其表示为 R)呢?
寻找转换矩阵 R
我们可以从一个随机选择的矩阵 R 开始,然后观察它在尝试翻译矩阵 X 中的英文向量时的表现,并将其与实际的、存储在矩阵 Y 中的法文词向量进行比较。
为了使此方法有效,首先需要获取一个英文单词及其对应法文翻译的子集,获取它们各自的词向量,并将这些词向量分别堆叠在矩阵 X 和 Y 中。此处的关键是保持行对齐或对齐词向量。这意味着如果矩阵 X 的第一行包含单词“cat”,那么矩阵 Y 的第一行应包含法文单词“chat”。
你可能会问,既然我已经有了英文单词及其法文翻译,为什么还需要训练一个模型来做这件事?为什么不直接将此信息保存在像Python字典这样的键值映射中?好处在于,你只需收集这些单词的一个子集来寻找转换矩阵。如果它工作良好,那么该模型可用于翻译原始训练集中未包含的单词。因此,你只需要在英法词汇的一个子集上进行训练,而非整个词汇表。
优化矩阵 R
让我们看看如何找到一个好的矩阵 R。首先,我们将翻译结果 X × R 与 Y 中的实际法文词嵌入进行比较。我们通过计算 X × R - Y 来实现这一点。目前,你可以将其视为衡量尝试翻译与法文实际向量之间差距的一种度量。
如果你从一个随机矩阵 R 开始,可以在循环中逐步改进这个矩阵 R。以下是优化步骤:
- 通过计算损失函数对矩阵 R 的导数来获取梯度。
- 通过减去梯度(由学习率 α 加权)来更新矩阵 R。
你可以选择固定循环次数,或在每次迭代时检查损失值,当损失低于某个阈值时跳出循环。
弗罗贝尼乌斯范数
现在,让我们解释双竖线符号的含义。这是衡量矩阵大小或范数的一种方法。让我们看一个计算此范数的例子,然后了解通用公式。
假设 X × R - Y 的结果是一个矩阵 A。在此示例中,我们假设词典中只有两个单词(即矩阵的行数),且词嵌入是二维的(即矩阵的列数)。因此,矩阵 X、R、Y 和 A 都是 2×2 矩阵。
如果矩阵 A 如下所示:
A = [[2, 2],
[2, 2]]
那么计算其范数时,我们计算 2² + 2² + 2² + 2²,然后取平方根,得到 4。
以下是实际公式:取矩阵中的所有元素,平方它们并求和。这个范数带有下标 F,因为它被称为弗罗贝尼乌斯范数。
现在,让我们用代码计算弗罗贝尼乌斯范数:
import numpy as np
A = np.array([[2, 2], [2, 2]])
# 计算弗罗贝尼乌斯范数
norm_F = np.sqrt(np.sum(np.square(A)))
尝试自己运行一下。请注意,在实践中,最小化弗罗贝尼乌斯范数的平方更为简便。换句话说,我们可以通过取平方来抵消平方根。稍后我将解释为什么使用范数的平方更容易处理。
回到矩阵 A 的例子,弗罗贝尼乌斯范数的平方就是 2² + 2² + 2² + 2²,然后我们取平方根,但随后通过对该和进行平方来抵消它。因此,弗罗贝尼乌斯范数的平方是 16。
计算损失函数的梯度
损失定义为弗罗贝尼乌斯范数的平方。梯度是损失对矩阵 R 的导数。其公式如下:
梯度 = (2/m) * (Xᵀ (X R - Y))
其中,标量 m 是用于训练的子集中的行数或单词数。
如果你还记得微积分,当你把 R 视为单个变量而非矩阵,且 X 和 Y 是常数时,这个公式可能看起来很熟悉。如果你不认识这个公式,也不必担心,你不需要在这里掌握微积分;如果需要,你可以在网上查找这些导数。
回到为什么使用弗罗贝尼乌斯范数的平方有帮助:对这个表达式求导比处理弗罗贝尼乌斯范数中的平方根更容易。你将在作业中实现这个公式。

总结
本节课中,我们一起学习了如何利用词向量和矩阵转换来实现跨语言单词对齐的基础翻译。我们了解了机器翻译的基本流程,探索了如何使用和优化转换矩阵 R,并介绍了用于衡量误差的弗罗贝尼乌斯范数及其在梯度计算中的应用。通过仅需一个矩阵,我们就能学习将一种语言的词向量与另一种语言对齐。接下来,我们将学习K近邻算法来进一步完善翻译过程。
042:K近邻算法与哈希表简介

概述 📖
在本节课中,我们将要学习一个自然语言处理中的关键操作:如何为一个向量寻找最相似的邻居。这个操作是许多NLP技术的基础构建模块。我们首先会从一个生活化的例子入手,理解寻找“最近邻居”的需求,然后引出一种能极大提升搜索效率的数据结构——哈希表。
从翻译任务到寻找相似向量 🔍
在上一节视频中,实现单词翻译的一个关键操作是寻找与某个向量最相似的“邻居”。
本节中我们来看看这个操作本身。请注意,一个英文单词的词向量经过 R 矩阵变换后,会进入法文词向量空间。
但这个变换后的向量,并不一定与法文词向量空间中的任何一个现有向量完全相同。
你需要在实际的法文词向量中进行搜索,以找到一个与你通过变换生成的向量相似的法文单词。
你可能会找到像“Salu”或“Bonjou”这样的词,可以将它们作为英文单词“hello”的法文翻译返回。
那么问题来了:如何找到相似的词向量?
一个生活化的类比:寻找附近的朋友 🗺️
为了理解如何寻找相似向量,让我们看一个相关的问题:如何找到住在你附近的朋友?
假设你正在美国旧金山拜访你亲爱的朋友Andrew。你也想在周末拜访其他朋友,最好是那些住得近的。
以下是实现此目标的一种方法:
- 遍历你的通讯录,针对每一位朋友。
- 获取他们的地址,计算他们与旧金山的距离。
- 根据距离对朋友进行排序,然后按亲近程度排名。
请注意,如果你有很多朋友(我相信你肯定有),这将是一个非常耗时的过程。有没有更高效的方法呢?
你可能会注意到,其中两位朋友住在另一个大洲,而第三位朋友住在美国。你是否可以只搜索住在美国的那部分朋友呢?
你可能已经意识到,为了找到离你最近的朋友,或许没有必要遍历通讯录里的所有人。
你可能想过,如果你能以某种方式筛选出所有在某个大区域(例如北美)的朋友,那么你就可以只在这个朋友子集中进行搜索。
如果有一种方法能将地理空间分割成不同的区域,你就可以只在这些区域内进行搜索。
引入高效的数据结构:哈希表 🪣
当你思考如何高效地组织数据集的子集时,你可能会想到将数据放入不同的“桶”中。
如果你想到了“桶”,那么你肯定会想了解哈希表。
哈希表是任何涉及数据处理的工作中都很有用的工具。
你将在下一个视频中学习哈希表。
总结 ✨
本节课中我们一起学习了:
- 如何使用K近邻算法来翻译单词,即使变换后的向量与目标语言中的词嵌入并不完全匹配。
- 引入了哈希表这一有用的数据结构,它能够将数据分到不同的“桶”中,为下一节视频中学习如何实现快速近似搜索奠定了基础。

准备好了吗?现在你将学习哈希技术,这是一种比简单线性搜索高效得多的查询查找方法。😊
043:哈希表与哈希函数 🗃️

在本节课中,我们将要学习哈希表和哈希函数的基本概念。你将了解如何将数据项分组到不同的“桶”中,以及如何通过一个简单的数学运算(哈希函数)来决定每个数据项的归属。我们还会通过一个简单的代码示例来演示如何构建一个基础的哈希表。
概述
想象你有一个带多个抽屉的柜子。你可能希望将相似的对象放在同一个抽屉里,例如,所有纸质文件放在一起,所有钥匙放在一起,所有书籍放在一起。在计算机科学中,哈希表就实现了类似的功能:它将数据项(例如单词向量)根据某种规则分配到不同的“桶”中。
上一节我们介绍了数据分组的基本想法,本节中我们来看看如何用数学和代码来实现它。
哈希表与哈希函数
假设你有几个数据项,你想根据某种相似性将它们分组到桶里。一个桶可以容纳多个数据项,并且每个数据项总是被分配到同一个桶。
例如,一些蓝色的椭圆形最终进入1号桶,一些灰色的矩形进入2号桶,一些品红色的三角形则被分配到3号桶。
现在,让我们思考如何用单词向量来实现这一点。首先,我们假设单词向量只有一维,而不是300维。因此,每个单词由一个单独的数字表示,例如100、141、710和97。
你需要找到一种方法,为每个向量赋予一个哈希值。哈希值是一个键,它指明了该向量应该被分配到哪个桶。分配哈希值的函数被称为哈希函数。
在这个例子中,这里有一个哈希表,它是一组桶的集合。本例中,哈希表有10个桶。请注意,单词向量100和10被分配到了0号桶。单词向量14被分配到了4号桶。单词向量17和97被分配到了7号桶。你注意到规律了吗?
以下公式就是用于将单词向量分配到各自桶中的哈希函数:
hash_value = vector_value % 10
模运算符(%)用于计算除以10后的余数。这个余数就是哈希值,它告诉我们单词向量应该存储在哪里。例如,14除以10的余数是4,所以它进入4号桶。
构建基础哈希代码
现在,让我们构建一个基础的哈希代码。以下是定义一个函数的代码,该函数接收一个值列表(你可以将每个值视为一个一维向量)和桶的数量。
def basic_hash_table(value_l, n_buckets):
# 定义哈希函数:使用模运算符
def hash_function(value, n_buckets):
return int(value) % n_buckets
# 创建哈希表(一个字典,键是桶编号,值是空列表)
hash_table = {i: [] for i in range(n_buckets)}
# 对每个值计算哈希值,并将其添加到对应的桶中
for value in value_l:
hash_value = hash_function(value, n_buckets)
hash_table[hash_value].append(value)
return hash_table
请注意,这是一个字典推导式。键是整数(桶编号),值是一个空列表,你将用它作为存储的桶。对于每个单词向量,计算其哈希值,然后将其追加到相应的列表中。
与本讲座配套的笔记本中可以看到返回的哈希表。你会发现,这个哈希表与上一张幻灯片中看到的完全相同。
哈希表的局限性
现在,让我们再看一下这个基础哈希表。回想一下,你最初的目标是将相似的单词向量放入同一个桶中。但在这里,看起来彼此接近的数字并没有在同一个桶里。例如,10、14和17在不同的桶中。
理想情况下,你希望有一个哈希函数,能将相似的单词向量放入同一个桶,就像这样。为了实现这一点,你需要使用一种称为“局部敏感哈希”的方法。
“局部”是位置的另一种说法。“敏感”是在意的另一种说法。因此,局部敏感哈希是一种非常在意根据数据项在向量空间中的位置来分配它们的哈希方法。

你将在下一节课中学习局部敏感哈希。
总结

本节课中我们一起学习了许多新术语:哈希值、哈希函数和桶。你也看到了如何构建一个哈希表的代码,这相当于我在视频开头提到的那个柜子。在下一个视频中,我们将探讨局部敏感哈希。
044:吴恩达《自然语言处理》P44 - 局部敏感哈希 🧭

在本节课中,我们将要学习一种名为局部敏感哈希的关键方法,它能有效降低在高维空间中寻找k个最近邻的计算成本。我们将从哈希的基本概念讲起,逐步理解其如何对数据点的位置敏感,从而进行高效分组。
什么是哈希与平面划分? 📐
上一节我们介绍了课程目标,本节中我们来看看哈希的基本思想以及如何用“平面”来划分空间。
首先,假设你使用的词向量只有两个维度。我们将每个向量视为一个点(而非箭头)。假设你想找到一种方法,知道这些蓝点彼此接近,而这些灰点也彼此相关。
以下是如何开始划分空间:
首先,使用这些虚线来划分空间,我将其称为“平面”。稍后我会解释为何称它们为平面。注意蓝色平面如何将空间分割成位于其上或其下的向量。蓝色向量恰好都位于蓝色平面的同一侧。类似地,灰色向量恰好位于灰色平面的上方。
看起来,平面可以帮助我们根据向量的位置将它们分到不同的子集中。这正是你想要的:一个对它所分配项的位置敏感的哈希函数。你正在逐步接近局部敏感哈希。
为什么称其为“平面”? ✈️
现在,让我们看看为什么我称这些虚线为“平面”。
在二维空间中,一个平面就是这条洋红色的线。它实际上代表了所有可能位于该平面上的向量。换句话说,这些向量会与该平面平行,例如这个蓝色向量或这个橙色向量。
你可以用一个单独的向量来定义一个平面。这个洋红色的向量垂直于该平面,被称为该平面的法向量。法向量垂直于平面上任何向量。
在三维空间中思考可能更有帮助:想象一张纸放在桌上,在纸上画一些向量。然后垂直握住一支铅笔置于纸的上方。纸上的任何向量都与这支铅笔垂直。
如何用数学判断向量位于平面的哪一侧? ➕➖
我们回到二维空间。从视觉上你能看出一个向量位于平面的哪一侧,但如何用数学方法实现呢?
以下是三个示例向量:蓝色、橙色和绿色。平面的法向量标记为 P。
让我们关注向量 V1。如果你计算 P 与 V1 的点积,结果是 3。稍后我会解释为什么这样做。
现在,看向量 V2。计算 P 与 V2 的点积,结果是 0。
最后,看向量 V3。计算 P 与 V3 的点积,结果是 -3。
所以点积结果分别是 3、0 和 -3。你是否注意到这些符号与它们相对于红色平面的位置之间的关系?
当点积为正时,向量位于平面的一侧。如果点积为负,向量位于平面的另一侧。如果点积为 0,向量就在平面上。
点积的几何意义是什么? 📏
为了可视化点积,想象其中一个向量(例如 P)是地球的表面。重力将所有物体垂直拉向地球表面。
接下来,假设你站在向量 V1 的末端。你将一根绳子系在石头上,让重力将石头拉向向量 P 的表面。绳子垂直于向量 P。现在,如果你画一个与 P 方向相同、但终点在石头处的向量,你就得到了向量 V1 在向量 P 上的投影。
该投影向量的大小或长度等于 V1 和 P 的点积。此外,如果你有另一个绿色向量并将其投影到向量 P 上,投影向量将与 P 平行但方向相反。点积将是一个负数。
这意味着点积的符号指示了投影相对于紫色法向量的方向。因此,点积的正负可以告诉你向量 V1 或 V2 位于平面的哪一侧。
用代码判断向量位于平面的哪一侧 💻
以下是使用代码检查向量位于平面哪一侧的方法。
函数 side_of_plane 接收法向量 P 和一个向量 V。它使用 numpy.dot 计算点积,然后使用 numpy.sign 获取符号:如果点积为正则返回 +1,为负则返回 -1,为零则返回 0。
import numpy as np
def side_of_plane(P, v):
dot_product = np.dot(P, v)
sign = np.sign(dot_product)
# 如果点积为0,sign会返回0,我们直接将其作为标量返回
return sign if sign != 0 else 0.0
请注意函数中 numpy.asscalar 的用法(如果向量可以表示为单个标量,此函数会检索该标量)。请你自己尝试运行它。

本节总结 📝
本节课中我们一起学习了局部敏感哈希的初步概念。我们通过可视化了解了如何使用平面划分空间,并通过点积的数学原理来判断向量位于平面的哪一侧。核心在于,两个向量投影的符号可以告诉你一个点位于直线的哪一部分(例如,上方或下方)。
在下一节视频中,你将学习如何将这个概念与多个平面结合,以更好地近似数据点可能位于的位置。
045:多平面哈希法 🧩

在本节课中,我们将学习如何结合多个平面来为数据点生成一个单一的哈希值。这种方法能帮助我们将向量空间划分为多个区域,从而更高效地识别和定位数据。
在上一节中,我们介绍了如何通过计算向量与平面法向量的点积符号,来确定向量相对于该平面的位置。本节中,我们将看看如何利用多个平面的信息,为向量空间中的数据生成一个哈希值。
为了将向量空间划分为易于管理的区域,我们需要使用多个平面。对于每个平面,我们可以确定一个向量位于该平面的正侧还是负侧。这样,每个平面都会产生一个信号。我们需要找到一种方法,将这些信号组合成一个单一的哈希值。这个哈希值将定义向量空间内的一个特定区域。
以下是生成哈希值的具体步骤:
首先,对于每个平面,计算向量与该平面法向量的点积。根据点积的符号,分配一个中间哈希值:
- 如果点积符号大于或等于0,则中间哈希值设为 1。
- 如果点积符号小于0,则中间哈希值设为 0。
然后,使用以下公式将所有中间哈希值组合成最终的单一哈希值:
hash = (2^0 * h1) + (2^1 * h2) + (2^2 * h3) + ... + (2^(n-1) * hn)
其中,h1, h2, ..., hn 是每个平面对应的中间哈希值。
让我们通过一个例子来理解这个过程。假设有一个向量,它与三个平面的点积结果如下:
- 与平面1的点积为 3(符号为正),中间哈希值
h1 = 1。 - 与平面2的点积为 -5(符号为负),中间哈希值
h2 = 0。 - 与平面3的点积为 2(符号为正),中间哈希值
h3 = 1。
根据组合公式,最终哈希值为:
hash = (2^0 * 1) + (2^1 * 0) + (2^2 * 1) = 1 + 0 + 4 = 5
这样,我们就得到了一个代表该向量所在区域的哈希值 5。多个平面帮助我们细分了向量空间,而单一的哈希值则让我们知道该将向量分配到哪个“桶”中。
现在,让我们看看如何在代码中实现这个逻辑。给定一个平面列表和一个向量,我们可以按以下步骤计算哈希值:
def hash_multi_plane(planes, v):
hash_value = 0
for i, plane in enumerate(planes):
# 计算点积的符号
dot_product = np.dot(v, plane)
# 根据符号分配中间哈希值
if dot_product >= 0:
h = 1
else:
h = 0
# 将中间哈希值加权后累加到最终哈希值
hash_value += np.power(2, i) * h
return hash_value
在这段代码中,我们初始化哈希值为0。然后遍历每个平面,计算向量与平面法向量的点积符号,并据此设置中间哈希值 h。接着,将 h 乘以 2 的 i 次方(i 是平面的索引),并累加到总哈希值中。最后返回计算出的哈希值。如果你在课程笔记中运行这段代码,将会得到与前面例子相同的结果。

本节课中,我们一起学习了多平面哈希法。我们了解了如何利用多个平面为数据点生成一个唯一的哈希值,从而将高维向量空间有效地划分为不同的区域。这种方法的核心在于组合每个平面对向量位置的判断(正侧或负侧),通过加权求和得到一个单一的标识符。这是实现局部敏感哈希的关键一步,它将为后续加速最近邻搜索的计算奠定基础。
接下来,你将会看到这种方法如何具体应用于加速关键的最近邻计算。让我们进入下一个视频。
046:近似最近邻 🎯

概述
在本节课中,我们将学习如何利用上一节介绍的局部敏感哈希技术,构建一个比暴力搜索快得多的算法来寻找近似最近邻。我们将探讨如何通过多组随机平面来更稳健地划分向量空间,并最终在代码中实现这一过程。
从单组到多组随机平面
上一节我们介绍了如何使用一组随机平面来划分向量空间。然而,一组平面(例如下图中的三个平面)的划分方式可能并非最优。

我们无法确定哪一组平面是划分向量空间的最佳方式。那么,为什么不创建多组随机平面呢?这样,我们就可以将向量空间划分到多个独立的哈希表集合中。
你可以将其想象成创建了多个“宇宙”副本。利用所有这些不同的随机平面集合,可以帮助我们找到一组高质量的、可能成为最近邻的候选向量。
多组平面的工作原理
假设我们有一个向量空间,中间的红点代表一个英语单词转换成的法语词向量。我们的目标是找到其他可能相似的法语词向量。
- 第一组随机平面可能帮助我们确定,这个红点向量和这些绿点向量被分配到了同一个哈希“口袋”中。
- 另一组完全不同的随机平面可能帮助我们确定,这些蓝点向量与红点向量在同一个哈希“口袋”中。
- 第三组随机平面可能帮助我们确定,这些橙点向量与红点向量在同一个哈希“口袋”中。
通过为局部敏感哈希使用多组随机平面,我们就有了一种更稳健的方法来搜索向量空间,以找到可能成为最近邻的候选向量集合。
近似最近邻的概念
这种方法被称为近似最近邻。因为你并非搜索整个向量空间,而只是它的一个子集。所以,你找到的不是绝对精确的最近邻,而是近似的最近邻。
你牺牲了一些搜索精度,以换取搜索效率的大幅提升。
在代码中创建随机平面
接下来,我们看看如何在代码中创建一组随机平面。假设你的词向量是二维的,并且你想生成三个随机平面。
你可以使用 numpy.random.normal 来生成一个3行2列的矩阵,如下所示:
import numpy as np
# 假设向量维度为2,生成3个随机平面(法向量)
planes = np.random.normal(size=(3, 2))
然后,创建一个向量 v,并针对每个随机平面,判断该向量位于平面的哪一侧。
v = np.array([1, 2]) # 示例向量
请注意,我们无需使用 for 循环逐个处理平面,而可以使用 numpy 的向量化操作一步完成。让我们调用这个函数:
# 计算向量v与每个平面法向量的点积
dot_products = np.dot(planes, v)
# 判断符号,得到中间哈希值(1表示正侧,0表示负侧)
hash_values = (dot_products > 0).astype(int)
结果是向量 v 位于这三个随机平面中每一个的正侧。


组合哈希值
你已经了解了如何将这些中间哈希值组合成一个单一的哈希值。请务必查看课程配套的笔记本,以查看所有代码并练习这最后一步。
总结
本节课中,我们一起学习了如何利用局部敏感哈希技术高效计算近似最近邻。我们看到,通过使用多组随机平面,可以更可靠地在庞大的向量空间中筛选出候选邻居,从而在可接受的精度损失下,获得比朴素搜索快得多的速度。这个强大的工具可以用于许多与向量相关的任务。在下一个视频中,我将向你展示如何将其应用于搜索任务。
047:文档搜索 📄🔍

在本节课中,我们将学习如何利用词向量和快速K近邻算法,在一个大型文档集合中搜索与查询相关的文本片段。核心思想是将文档和查询都转化为向量,并通过计算向量间的相似度来找到最相关的文档。
从词向量到文档向量
上一节我们介绍了词向量的概念。本节中我们来看看如何将整个文档表示为一个向量,这是进行文档搜索的基础。
假设我们有一个由三个单词组成的文档:“I love learning”。为了将这个文档表示为向量,我们可以采用以下方法:
- 首先,获取文档中每个单词的词向量。例如,单词“I”、“love”、“learning”各自都有一个向量表示。
- 然后,将这些词向量相加。
- 所有词向量的和就构成了该文档的向量,其维度与单个词向量相同(本例中为三维)。
用公式可以表示为:
文档向量 = Σ(单词向量)
实现文档向量化
以下是实现上述文档向量化过程的代码示例。我们首先创建一个微型的词嵌入字典,然后遍历文档中的每个单词,累加其对应的向量。
# 微型词嵌入字典示例
word_embeddings = {
"I": [1, 0, 0],
"love": [0, 1, 0],
"learning": [0, 0, 1],
"you": [1, 1, 0],
"hate": [-1, 0, 0]
}
def document_to_vector(document, embeddings):
"""
将文档转换为向量。
参数:
document: 字符串,代表文档内容。
embeddings: 字典,单词到向量的映射。
返回:
doc_vector: 列表,文档的向量表示。
"""
# 将文档按空格分割成单词列表
words = document.split()
# 初始化文档向量为零向量,维度与词向量相同
doc_vector = [0] * len(next(iter(embeddings.values())))
# 遍历每个单词,累加其向量
for word in words:
if word in embeddings:
word_vec = embeddings[word]
# 将词向量加到文档向量上
doc_vector = [doc_vector[i] + word_vec[i] for i in range(len(doc_vector))]
return doc_vector
# 示例用法
doc = "I love learning"
doc_vec = document_to_vector(doc, word_embeddings)
print(f"文档 '{doc}' 的向量表示为:{doc_vec}")
执行文档搜索
获得文档的向量表示后,就可以应用K近邻算法进行搜索了。以下是执行搜索的基本步骤:
- 预处理:将文档库中的所有文档都转换为向量,并存储起来。
- 查询处理:将用户的查询文本同样转换为向量。
- 相似度计算:计算查询向量与每个文档向量之间的相似度(例如,使用余弦相似度或欧氏距离)。
- 结果排序:根据相似度对文档进行排序,返回最相关的K个文档作为搜索结果。
通过这种方法,语义相近的文档在向量空间中的位置也会接近,从而使得K近邻搜索能够找到含义相似的文本。
课程总结

本节课中我们一起学习了文档搜索的基本原理。我们了解到,可以将文本(无论是单词、句子还是整篇文档)嵌入到向量空间中,使得语义相似的文本在向量空间中也彼此接近。利用快速K近邻算法,我们可以高效地找到与给定查询最相关的文档。虽然这只是文本嵌入的基础方法,但这种将文本转化为向量并进行相似度比较的核心结构,在现代自然语言处理中会反复出现。
049:从比较文学到NLP前沿——Kathleen McKeown的跨学科研究之旅 🧠

在本节课中,我们将跟随哥伦比亚大学计算机科学教授、数据科学与工程研究所创始主任Kathleen McKeown,了解她如何从比较文学专业的学生成长为自然语言处理领域的先驱。我们将探讨她的研究历程、对跨学科工作的看法,以及她对当前NLP研究方向的见解。
从比较文学到计算语言学 📚
上一节我们介绍了Kathleen McKeown的背景,本节中我们来看看她独特的学术转型之路。
Kathleen McKeown在布朗大学本科期间,主修了比较文学,同时也修读了数学课程。她对比较文学的兴趣日益浓厚,部分原因是受到了授课教师的深刻影响。
临近毕业时,她找到了一份程序员的工作,但觉得非常枯燥。她认为,如果每周要工作40小时,她希望从事自己真正热爱的事情。
当时,她的一位语言学专业的朋友向她介绍了计算语言学。于是,她在接下来的一年里,花了大量时间在图书馆阅读关于人工智能和自然语言处理的资料。
当她第二年申请研究生时,她明确知道自己想从事这个领域,因为它将她对语言的兴趣和数学结合了起来。
自学之路与克服“冒名顶替综合症” 💪
上一节我们了解了Kathleen McKeown的转型契机,本节中我们来看看她如何克服初入新领域的挑战。
她完全是自学,在阅读方向上没有任何指导。最初,她只是根据那位朋友的一些建议开始阅读,然后通过追踪参考文献来深入学习。
当她进入研究生阶段,由于本质上转换了领域,她感到非常害怕。她确信自己是个“冒名顶替者”,懂得不够多,并担心很快会被发现她其实不该在那里。
但随着时间的推移,她克服了这种感觉,并认识到事实并非如此,人们会重视她的贡献。
对于今天可能面临类似处境的学习者,她给出了以下建议:
以下是Kathleen McKeown给初学者的建议:
- 主动交流:与他人交流,特别是与同行讨论他们正在做的事情和兴趣点,这非常有用。
- 选择差异化问题:在当今深度学习和神经网络的世界里,建议选择与其他人研究方向不同的问题,开辟新的方向,选择新的任务。
- 利用在线资源:参加在线阅读小组,观看在线视频和课程(如吴恩达的课程),这有助于了解领域动态并与他人建立联系。
她提到自己当时非常幸运,申请了宾夕法尼亚大学,而那时她并不知道那里是自然语言处理最好的地方。她认为,虽然每个成功者都有运气的成分,但充分的准备能让你在好运降临时抓住机会。
当前研究方向:文本摘要与跨学科应用 ✨
上一节我们探讨了学习路径,本节中我们来看看Kathleen McKeown当前激动人心的研究工作。
文本摘要是她近年来工作的主要部分。她的团队研究了各种不同体裁的文本摘要,从小说到电子邮件。
她特别兴奋的一个研究方向是与亚马逊研究人员合作、并在ACL上发表的小说章节摘要工作。这是一个非常新颖的任务,极具挑战性,属于深度摘要。
小说章节比当前大多数摘要研究所基于的新闻文章要长得多,这对当前的神经模型是一个挑战。一个主要问题是输入(19世纪小说)和输出(用现代语言写的摘要)之间存在极大量的意译。目前没有模型能够处理这种程度的意译。
她总体上对这类高度抽象摘要非常感兴趣,即摘要句子使用的词汇和句法结构与输入文档完全不同。这与当前绝大多数基于新闻摘要的研究非常不同,而新闻摘要流行主要是因为那里有现成的数据。
她研究的其他领域还包括在线个人叙述的摘要(语言非常非正式,而摘要更正式)、辩论摘要,以及过去做过的电子邮件摘要。
如何选择研究课题与挑战现状 🧭
上一节我们介绍了她的具体研究,本节中我们来看看她选择研究课题的哲学。
她选择研究小说摘要任务,部分源于早期与一位对创意写作非常感兴趣的学生的合作。为了说服他攻读博士学位,他们选择了一个他感兴趣的主题:创意语言的分析与生成。那时,她感觉自己的工作完成了一个循环,因为她与一位比较文学教授合作,回到了她的比较文学根源。
当后来她去亚马逊时,她知道因为Kindle和亚马逊平台上有大量小说,她认为没有什么比能够总结小说更有趣的项目了。
她选择研究课题的核心原则是:选择一个重要的任务。例如,当前大多数文本摘要工作都集中在所谓的新闻单文档摘要上,即输入一篇新闻文章,生成其摘要。这主要是因为那里有现成的数据(如CNN/Daily Mail语料库、纽约时报语料库等)。
但问题在于,这并非我们真正需要的任务。人们早就知道,新闻文章的开头部分(导语)可以很好地作为文档摘要。事实上,多年来都很难超越导语的摘要效果。人们研究这个问题,主要是因为那里有数据,有排行榜,人们喜欢竞争并超越排行榜。
她更喜欢选择前人未曾涉足的方向,选择一个如果解决就能真正帮助他人、成为有用应用的任务。这也是为什么她研究诸如灾难背景下的个人叙述摘要(以便浏览人们经历灾难后的体验摘要),或者当前的小说章节摘要工作。
在深度学习时代,结果产出非常快,每个人都致力于解决相同的问题以超越现有技术水平,很难成为第一个达到目标的人。如果选择不同的方向,没有其他人研究,你将成为第一个提出解决方案的人。她喜欢在自己的研究中成为某个问题的先行者。
跨学科研究与社会影响 🌍
上一节我们讨论了研究选题,本节中我们来看看Kathleen McKeown如何通过跨学科研究产生社会影响。
她非常享受跨学科研究,认为这是她最喜欢的研究类型。与其他领域的人交流,能让研究者从狭窄的技术领域中跳出来,获得对研究和世界的不同视角。
她目前正与社会工作领域的研究人员合作,并开始邀请一位研究非裔美国人方言的语言学家参与。他们正在研究人们对当今重大事件(例如“黑人的命也是命”运动和COVID-19疫情)的反应中表达了什么,以及表达了何种情绪。
这项工作的自然语言方向目标是:理解人们如何用非裔美国人方言表达不同类型的情感,这与用标准美式英语表达有何不同,并研究语言甚至表达内容上的差异。这有助于开发无偏见的算法。目前大多数自然语言系统都是在来自新闻(如《华尔街日报》)的语言上训练的。
这项工作还想研究创伤的影响。有时创伤不是个人的,而是看到与自己相似的人遭遇不幸所产生的。他们希望研究这种情绪是如何表达的,以及不同类型情感的强度等。
她认为,NLP和AI研究人员可以在这些最重要的社会问题和议题中发挥积极作用。这类工作也能吸引学生,尤其是不同类型的学生进入该领域与她合作。
NLP领域的演变与未来展望 🔮
上一节我们看到了NLP的社会应用,本节中我们来看看Kathleen McKeown眼中NLP领域的演变。
当她刚开始进入这个领域时(1982年获得博士学位),该领域有一些显著特征。其一是高度跨学科。在开发NLP系统时,他们大量借鉴了语言学、哲学、心理学和认知科学的研究。她在宾夕法尼亚大学时,经常与语言学、哲学等系的教师互动。
第二个特征是借鉴其他领域的理论。他们借鉴语言学的理论,例如关注焦点及其在话语过程中的变化如何影响语言选择(如使用代词还是完整名词短语,使用何种句法结构使概念在话语中更突出)。他们也借鉴哲学理论,如关于意图的理论和关于会话含义的理论,并研究如何将这些理论体现在自然语言处理方法中。
对于当前最令她兴奋的NLP技术或方向,她提到了以下几点:
以下是Kathleen McKeown看好的NLP未来方向:
- 真正抽象的、使用极端意译的摘要。
- 分析来自多元社区的语言(如黑人社区,以及其他社区)。
- 处理数据偏见。
- 获取副语言意义,即关于情感、意图的语用信息。
- 更多关于事件的工作,理解发生了什么事件并能够跟踪它们。
回顾她最喜欢的论文,她提到了三个时期的成果。早期关于语言生成中词汇选择的工作,探讨了来自话语、词汇、句法、语义等不同部分的约束如何影响选择,并提到了“浮动约束”的例子。她认为控制是当前使用深度学习方法进行语言生成和摘要中所缺失的——如何控制输出内容并确保其符合我们的意图。
另一项较近的工作是“Newsblaster”(大约15年前),这是一个真实世界的问题,他们与记者合作,开发了一个能够识别当天发生的事件并为每个事件生成摘要的测试平台,还研究了如何随时间跟踪事件。这个平台为她的学生解决困难的研究问题提供了一个共同的应用场景。
总结与寄语 🌟
本节课中,我们一起学习了Kathleen McKeown教授从比较文学到NLP领军人物的非凡旅程。我们探讨了她自学克服挑战的经历、她选择新颖且有意义的研究课题(如小说摘要、多元社区语言分析)的哲学、她对跨学科合作价值的强调,以及她对NLP领域从理论驱动到数据驱动演变过程的观察。
她总结道,自然语言处理今天是一个令人兴奋的领域,深度学习带来了巨大的进步和准确性的显著提高,但仍有许多方向有待探索。她希望看到更多跨学科工作被重新引入,希望人们更多地关注数据本身和输出内容,而不仅仅是数字指标。她认为该领域有许多令人兴奋的方向,并希望看到更多人加入。

课程资源:更多NLP思想领袖的访谈,请查看Deeplearning.AI的YouTube频道,或在Coursera上注册NLP专项课程。
050:自然语言处理中的概率模型 🎯
在本课程中,我们将学习概率模型及其在预测词序列中的应用。这些技术支撑着自动更正和网络搜索建议等功能。我们还将探讨马尔可夫模型和维特比算法,它们是许多自然语言处理系统的基础构建模块。
概述 📋
恭喜完成第一门课程,欢迎来到自然语言处理的第二门课程。
本课程将介绍概率模型,并展示如何利用它们预测词序列。这项技术不仅驱动着自动更正功能,还应用于网络搜索建议。例如,当您访问网站并在搜索栏输入几个词时,系统会自动建议您可能想搜索的内容。
此外,本课程将涵盖马尔可夫模型和维特比算法。这两者都是许多自然语言处理系统中至关重要的基础构建模块。
课程内容安排 📅
以下是本课程各周的学习重点:
- 第一周:学习通过字符序列的概率构建自动更正系统。
- 第二周:学习隐马尔可夫模型,并利用它实现一个词性标注系统。例如,在查询“book of flight”时,知道“book”是动词而非名词,将帮助模型更好地理解您的查询意图。
- 第三周:学习使用词序列的概率构建自动补全系统。
- 第四周:我们将回到词向量的主题。您已在第一门课程中学习了如何使用它们,本周我们将展示如何利用神经网络生成词向量。
总结 ✨
在本课程中,我们一起学习了自然语言处理中概率模型的核心概念与应用。从构建自动更正系统到实现词性标注和自动补全,我们探索了如何利用字符和词序列的概率来增强语言处理能力。最后,我们还回顾并深化了词向量的知识,了解了其神经网络生成方法。这些内容都是自然语言处理领域重要的基础构建模块。
现在,让我们开始学习吧。😊
052:自动校正概述 🧠

在本节课中,我们将学习自动校正的基本概念。自动校正功能常见于手机和文字处理软件中,它能将拼写错误的单词自动修正为正确的形式。我们将了解其工作原理,并学习构建自动校正模型所需的关键步骤。
自动校正简介
上一节我们介绍了本周的学习目标,本节中我们来看看自动校正的具体含义。
自动校正功能你可能已经在手机或文字处理软件中使用过。但自动校正究竟是什么?根据上下文,它的具体含义可能略有不同。本周你将学习这些细微差别。
学习目标
以下是本周你将掌握的内容:
- 从概念上理解自动校正。
- 练习构建一个执行自动校正的模型。
- 学习自动校正的每个步骤和关键概念,为后续的解码任务做准备。
关键概念:最小编辑距离
在执行自动校正时,你需要能够量化两个字符串之间的距离或差异,即需要改变多少个字母才能将一个字符串转换为另一个字符串。
为此,你将学习如何测量最小编辑距离。
核心技术:动态规划
你将实现一种非常有趣的算法来解决最小编辑距离问题,这种算法称为动态规划。

动态规划是一个非常重要的概念,在技术面试中会反复出现。
课程安排与总结
在课程结束时,你将展示新学到的技能,并在本周的作业中应用所有这些技术。
首先,让我们在下一个视频中开始学习自动校正的概述。
本节课中我们一起学习了自动校正的基本概念、学习目标,并介绍了最小编辑距离和动态规划这两个核心技术。这些知识将为我们后续构建自动校正模型打下坚实的基础。
053:自动纠错模型详解 🛠️
在本节课中,我们将学习自动纠错的基本原理与实现步骤。自动纠错是一种将拼写错误的单词转换为正确单词的应用程序,常见于手机、平板电脑和文档编辑器中。
什么是自动纠错? 🤔
自动纠错是一种应用程序,能够将拼写错误的单词更改为正确的单词。例如,当输入句子“H birthday the friend”时,自动纠错会将拼写错误的单词“the”修正为可能原本想输入的单词“dear”。
但如果你输入的是“deer”而不是“dear”,虽然单词拼写正确,但上下文可能不匹配。本周课程中,我们暂不处理这种上下文错误,因为它属于更复杂的问题。我们将专注于修正拼写错误的单词。
自动纠错的工作原理 📝


自动纠错主要包含四个关键步骤:
- 识别错误单词:通过拼写错误识别。
- 查找编辑距离相近的字符串:找到与输入字符串编辑距离为1、2、3或任意n的字符串。编辑距离越小,字符串与输入字符串越相似。
- 筛选正确拼写的真实单词:从候选字符串中筛选出实际存在的、拼写正确的单词。
- 计算单词概率并选择最可能候选词:计算每个候选词在上下文中出现的概率,并选择概率最高的单词作为替换。
编辑距离与字符串相似性 📏
编辑距离是衡量两个字符串相似度的指标,定义为将一个字符串转换为另一个字符串所需的最少单字符编辑操作次数。操作包括插入、删除、替换和交换相邻字符。
编辑距离公式:
编辑距离 = 最小编辑操作次数
实现自动纠错的详细步骤 🔧
上一节我们概述了自动纠错的工作原理,本节我们将深入探讨每个步骤的具体实现细节。
步骤一:识别错误单词
识别错误单词通常通过检查单词是否存在于预定义的词典中来实现。如果单词不在词典中,则被视为拼写错误。

步骤二:查找编辑距离相近的字符串

为了找到与错误单词编辑距离相近的候选字符串,我们可以使用动态规划算法计算编辑距离。以下是查找编辑距离为1的字符串的示例代码:
def generate_edit_one(word):
letters = 'abcdefghijklmnopqrstuvwxyz'
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [L + R[1:] for L, R in splits if R]
inserts = [L + c + R for L, R in splits for c in letters]
replaces = [L + c + R[1:] for L, R in splits if R for c in letters]
return set(deletes + inserts + replaces)
步骤三:筛选正确拼写的单词
从候选字符串中筛选出实际存在于词典中的单词。这可以通过查询词典来实现。
步骤四:计算单词概率并选择最可能候选词
计算每个候选词在语料库中出现的概率,选择概率最高的单词作为最终纠正结果。概率计算公式为:
P(word) = 单词在语料库中的出现次数 / 语料库总单词数
优化编辑距离计算 ⚡
现在我们已经了解了自动纠错的基本实现步骤。在本周的编程练习中,你将亲自实现自动纠错模型,并看到其良好的效果。接下来,我将介绍如何加速编辑距离的计算。
为了提高编辑距离的计算效率,可以使用动态规划算法,并通过矩阵存储中间结果,避免重复计算。以下是一个简单的动态规划实现示例:

def edit_distance(word1, word2):
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
return dp[m][n]
总结 📚
本节课我们一起学习了自动纠错模型的基本原理与实现步骤。自动纠错通过识别错误单词、查找编辑距离相近的字符串、筛选正确拼写的单词以及计算单词概率来选择最可能的候选词,从而实现对拼写错误的自动修正。通过优化编辑距离计算,我们可以提高自动纠错模型的效率。希望这些知识能帮助你更好地理解和实现自动纠错功能。
054:构建自动纠错模型 🛠️

在本节课中,我们将深入学习构建自动纠错模型所需的四个核心步骤。我们将详细探讨如何识别拼写错误、生成候选词、筛选候选词,并为下一节课的概率计算步骤打下基础。
概述:自动纠错的四个步骤
在上一节视频中,我们简要提到了实现自动纠错所需的四个步骤。现在,我们将深入探讨这些步骤。你已经了解了自动纠错模型内部的这四个步骤,是时候详细研究每一步了。你将在本周的作业中实现这些步骤。
第一步:识别拼写错误的单词 ✅
当遇到一个字符串时,如何判断它是否拼写错误?如果拼写正确,你会在字典中找到它。如果找不到,那么它很可能是一个拼写错误的单词。
核心逻辑:如果一个单词不在给定的字典中,就将其标记为需要纠正。
需要注意的是,我们目前不寻找上下文错误,只寻找拼写错误。课程后期会探讨更复杂的技术,例如通过观察周围单词来识别可能不正确的词。但目前,通过明显的拼写错误快速识别单词是一个简单而有效的模型。
例如,单词 “dear” 会顺利通过这个过滤器,因为它的拼写是正确的,无论上下文看起来如何。
第二步:查找编辑距离内的字符串 🔍
当提到编辑距离时,我指的是一个距离为 n 的编辑,例如距离为1、距离为2,依此类推。
编辑 是一种对字符串执行以将其更改为另一个字符串的操作类型。
编辑距离 计算这些操作的数量,因此编辑距离告诉你一个字符串与另一个字符串相差多少次操作。
现在,考虑以下几种编辑操作:
以下是四种基本的编辑操作:
- 插入操作:在任何位置向字符串添加一个字母。
- 例如:从单词 “to” 开始,在末尾插入 “p” 得到 “top”;在中间插入 “w” 得到 “two”。
- 删除操作:移除一个字母。
- 例如:从单词 “hat” 开始,删除末尾的 “t” 得到 “ha”;删除开头的 “a” 得到 “at”;删除中间的 “a” 得到 “ht”。
- 交换操作:交换两个相邻的字母。
- 例如:字符串 “eta”,交换 “t” 和 “a” 得到 “eat”;交换 “e” 和 “t” 得到 “tea”。
- 注意:这里只交换彼此相邻的两个字母,不包括交换不相邻的字母(例如交换 “e” 和 “a” 得到 “ate”)。
- 替换操作:将一个字母更改为另一个字母。
- 例如:单词 “jaw”,将 “w” 替换为 “r” 得到 “jar”;将 “j” 替换为 “p” 得到 “paw”。
使用这四种编辑操作(插入、删除、交换、替换),你可以修改任何字符串。通过组合这些编辑,你可以找到所有可能距离原始输入字符串 n 次编辑的字符串列表。对于自动纠错,n 通常为 1 到 3 次编辑。
你将在本周的编程练习中实现这些编辑操作,并组合编辑以获得距离原始输入字符串两次编辑距离的列表。
第三步:筛选候选词 🎯
请注意,生成的许多字符串看起来并不像实际的单词。为了筛选这些字符串并保留那些是真实单词的,你只希望考虑候选列表中真实且拼写正确的单词。
因此,再次将其与已知的字典或词汇表进行比较,就像第一步一样。这一次,如果字符串没有出现在字典中,就将其从候选列表中移除。当你只剩下一个仅包含实际单词的列表时,这就是一个良好的进展。
以上是构建自动纠错模型的前三个步骤。

总结与预告
在本节课中,我们一起学习了实现自动纠错的前三个步骤:识别拼写错误的单词、查找编辑距离内的字符串、筛选出真实的单词候选词。
在下一节课中,你将看到第四个也是最后一个步骤:计算概率。这将帮助我们最终从多个正确的候选词中选出最可能的那一个。
055:构建模型II 🧠

概述
在本节课中,我们将学习如何为自动纠错功能计算每个候选单词的概率,并从中选出最可能的正确单词。我们将通过一个简单的例子,理解如何基于词频和语料库大小来计算单词概率。
计算单词概率
上一节我们介绍了如何生成并筛选出候选的正确单词列表。本节中,我们来看看如何计算这些候选单词的概率,从而确定最可能的替换词。
自动纠错需要知道哪个候选词更常见。例如,在给定的文本中,单词 “and” 通常比单词 “amd” 更常见。这个“常见程度”就是单词在语料库中出现的概率。
为了更好地理解,请看这个例句:
I am happy because I am learning
要计算句子中某个单词的概率,你需要先计算词频。此外,你还需要统计整个文本或语料库中的总单词数。
通常,一个语料库会大得多。为了保持示例简单,我们假设语料库就是这个句子本身。
以下是该句子的词频统计:
- 单词 “I” 出现两次。
- 单词 “am” 出现两次。
- 其他单词各出现一次。
语料库中的总单词数是 7。语料库中任何单词的概率等于该单词出现的次数除以总单词数。其公式为:
P(word) = count(word) / total_words_in_corpus
例如,单词 “am” 出现了两次,语料库大小为 7,因此其概率为 2/7。
对于自动纠错,你只需找到概率最高的候选词,并将其选为替换词。
自动纠错流程总结
本节课中我们一起学习了实现自动纠错的完整流程。以下是其步骤总结:
- 输入待纠正的单词:例如拼写错误的单词 “B D E A H”。
- 检查拼写:通过与已知单词词典对比,确认该词为拼写错误。
- 生成编辑字符串:创建所有与输入词编辑距离为 n 的字符串列表。
- 筛选候选词:过滤上述字符串列表,只保留词典中存在的实际单词。
- 计算单词概率:为每个候选词计算其在给定语料库中的概率。
- 选择替换词:选择概率最高的单词作为自动纠错的替换词。
这个过程涵盖了很多内容,但将其分解为一步步后,你就能很好地理解如何实现自动纠错。这对完成本周的编程作业将非常有帮助。
此外,你现在也理解了编辑和编辑距离的概念,以及如何用它们来衡量单词之间的相似性。

下节预告
你已经看到了实现自动纠错所需的四个步骤。接下来,我们将学习如何评估两个字符串(例如,一个有拼写错误的单词和一个正确的单词)之间的相似性。这是一个在自然语言处理中非常常见的度量方法。😊
056:最小编辑距离 📏

在本节课中,我们将学习“最小编辑距离”这一概念。这是一个用于衡量两个字符串(或文档)之间相似度的算法。我们将了解它的定义、应用场景、计算方式,以及如何通过动态规划高效地求解它。
概述与应用
最小编辑距离有广泛的应用。它允许你实现拼写纠正、文档相似度计算、机器翻译、DNA序列比对等功能。
上一节我们介绍了编辑距离的基本概念,本节中我们来看看它的具体应用场景。
以下是其主要应用领域:
- 拼写纠正:判断用户输入的词与词典中哪个词最相似。
- 文档相似度:比较两段文本的差异。
- 机器翻译:评估不同翻译结果与源文本的对应关系。
- DNA测序:比对基因序列,找出差异。
什么是编辑距离?
如果你有两个单词、字符串甚至整个文档,并想评估它们的相似程度,最小编辑距离可以做到这一点。
给定两个字符串,最小编辑距离是指将一个字符串转换为另一个字符串所需的最少操作次数。它在自然语言处理中有许多应用,例如拼写纠正、文档相似度和机器翻译。在计算生物学和DNA测序中也能找到它的身影。
编辑操作类型
为了计算最小编辑距离,你将使用三种类型的编辑操作。这些都是你已经知道的操作:插入、删除和替换。
例如,将单词 play 转换为 stay,需要的最少编辑次数是多少?
- 将
P替换为S。 - 将
L替换为T。 A和Y保持不变。
因此,总编辑次数为 2。
操作成本
到目前为止,我们假设所有编辑操作的成本相同,即成本为1。但现在,我们将为每种操作类型考虑不同的成本。
我们将使用这些成本来计算编辑距离,现在它代表的是总编辑成本。我们的目标就是最小化这个总成本。它简单地等于所执行编辑操作的成本之和。
以下是通常设定的成本:
- 插入 成本为 1。
- 删除 成本为 1。
- 替换 成本为 2。(这很直观,因为替换可以看作先删除再插入)
用这个成本体系重新计算上面的例子:我们进行了两次替换操作,每次成本为2,因此总编辑距离为 4。
为什么需要高效算法?
这是一个相对简单的例子,仅通过观察就能找到最小编辑距离。但想象一下,要计算更长的字符串、大型语料库文本甚至DNA序列之间的操作次数。
你可以尝试用暴力方法解决这些问题,一次增加一个编辑距离,并枚举所有可能性,直到一个字符串变成另一个。但这可能会花费非常、非常长的时间。事实上,用这种方式解决,计算复杂度会随着每个字符串长度的增加而呈指数级增长。
一个快得多的方法是使用表格法,我们接下来将实现它。
动态规划方法
我提到的表格法可以加速枚举所有可能的字符串和编辑操作。在此过程中,你还将学习一个称为动态规划的新概念。

我们将在下一个视频中详细了解它。
总结
本节课中我们一起学习了最小编辑距离。我们了解了它的定义、在NLP及其他领域的多种应用,以及计算时使用的三种基本编辑操作(插入、删除、替换)。我们还引入了操作成本的概念,并认识到对于复杂问题,需要使用基于动态规划的高效算法(表格法)来求解,而不是低效的暴力枚举。
057:吴恩达《自然语言处理》P57 - 最小编辑距离算法 🧮

在本节课中,我们将学习动态规划的一个核心应用:最小编辑距离算法。该算法用于计算将一个单词(源单词)转换为另一个单词(目标单词)所需的最少编辑操作次数。我们将通过一个具体的例子,从零开始构建一个距离矩阵,并理解其背后的计算逻辑。
概述
最小编辑距离是衡量两个字符串相似度的经典方法。其核心思想是,通过插入、删除和替换字符这三种基本操作,计算将源字符串转换为目标字符串所需的最小代价。我们将使用动态规划来高效地解决这个问题。
构建距离矩阵
首先,我们需要将问题形式化。假设源单词是 play,目标单词是 stay。我们创建一个矩阵,并在每个字符串的开头添加一个空字符串占位符(用 # 表示)。矩阵的行对应源单词的字符(包括空字符),列对应目标单词的字符。
矩阵中的元素 D[i][j] 表示将源单词的前 i 个字符转换为目标单词的前 j 个字符所需的最小编辑距离。例如,D[2][3] 表示将 PL 转换为 STA 的最小距离。
我们的目标是填充整个矩阵 D,最终右下角的元素 D[m][n] 就是两个完整字符串之间的最小编辑距离。计算过程从最短的子字符串(左上角)开始,逐步扩展到完整的字符串。
编辑操作及其代价
在开始计算之前,我们需要定义每种编辑操作的代价。这是算法的基础:
- 插入:在源字符串中插入一个字符,代价为 1。
- 删除:从源字符串中删除一个字符,代价为 1。
- 替换:将源字符串中的一个字符替换为目标字符,代价为 2。
初始化矩阵
我们从最简单的子问题开始:将空字符串转换为空字符串。
- 将空字符串转换为空字符串:不需要任何操作,距离为 0。因此,
D[0][0] = 0。
接下来,我们填充第一列和第一行,这些是基础情况。
- 将源单词转换为空字符串:只能通过不断删除字符来实现。例如,将 P 转换为空字符串需要 1 次删除,所以
D[1][0] = 1。将 PL 转换为空字符串需要 2 次删除,所以D[2][0] = 2,依此类推。 - 将空字符串转换为目标单词:只能通过不断插入字符来实现。例如,将空字符串转换为 S 需要 1 次插入,所以
D[0][1] = 1。将空字符串转换为 ST 需要 2 次插入,所以D[0][2] = 2,依此类推。
初始化后,矩阵的边界就被确定了,这为计算内部单元格提供了必要的依赖项。
计算内部单元格:核心递推公式
对于矩阵中的任何一个内部单元格 D[i][j],其值取决于其左方、上方和左上方三个相邻单元格的值。这对应了到达当前状态的三种可能路径:
- 从上方来 (
D[i-1][j]):这表示我们通过 删除 源字符串的第i个字符,达到了当前状态。总代价是上方单元格的代价加上删除操作的代价(1)。- 公式:
cost_delete = D[i-1][j] + 1
- 公式:
- 从左方来 (
D[i][j-1]):这表示我们通过 插入 目标字符串的第j个字符,达到了当前状态。总代价是左方单元格的代价加上插入操作的代价(1)。- 公式:
cost_insert = D[i][j-1] + 1
- 公式:
- 从左上方来 (
D[i-1][j-1]):这表示我们通过 替换(或不替换)源字符串的第i个字符为目标字符串的第j个字符,达到了当前状态。如果两个字符相同,则无需替换,代价为0;如果不同,则进行替换,代价为2。- 公式:
cost_replace = D[i-1][j-1] + (0 if source[i] == target[j] else 2)
- 公式:
单元格 D[i][j] 的值就是这三条路径代价中的最小值。用代码表示这个核心逻辑如下:
if source[i-1] == target[j-1]:
replace_cost = 0
else:
replace_cost = 2
D[i][j] = min(
D[i-1][j] + 1, # 删除
D[i][j-1] + 1, # 插入
D[i-1][j-1] + replace_cost # 替换或不替换
)
示例演算:计算 P -> S
让我们应用这个公式来计算第一个内部单元格 D[1][1],即如何将 P 转换为 S。
我们有三个依赖项:
- 上方 (
D[0][1]): 将空字符串转换为 S 的代价是 1(插入S)。 - 左方 (
D[1][0]): 将 P 转换为空字符串的代价是 1(删除P)。 - 左上方 (
D[0][0]): 将空字符串转换为空字符串的代价是 0。
计算三条路径的代价:
- 路径1(从上到下):先得到 S(代价1),再删除 P(代价1)。总代价 =
1 + 1 = 2。 - 路径2(从左到右):先删除 P(代价1),再插入 S(代价1)。总代价 =
1 + 1 = 2。 - 路径3(对角线):直接将 P 替换为 S。因为字符不同,替换代价为2。总代价 =
0 + 2 = 2。
三条路径的代价都是2。因此,D[1][1] = 2。这意味着将 P 转换为 S 的最小编辑距离是2。
通过这种方式,我们可以系统地填充整个矩阵,每一个新单元格的计算都依赖于之前已经计算好的结果,这正是动态规划“利用子问题解”的精髓。

总结
本节课我们一起学习了最小编辑距离算法。我们首先定义了插入、删除和替换操作的代价。然后,我们通过初始化边界和利用核心递推公式,一步步构建了动态规划矩阵。这个矩阵的右下角元素最终给出了两个单词之间的最小编辑距离。在下一节课中,我们将看到如何利用这个填好的表格,快速回溯出具体的编辑操作序列。
058:最小编辑距离算法II 🧮

在本节课中,我们将学习如何用代码实现填充最小编辑距离表格,从而将一个单词转换为另一个单词。我们将从直观方法过渡到公式化方法,并最终完成整个表格的计算。
表格初始化与边界填充
上一节我们介绍了最小编辑距离的基本概念和表格的直观填充方法。本节中,我们来看看如何使用公式化方法填充表格的其余部分。
你已经填充了表格的一部分,它目前看起来是这样的。现在,要填充表格的其余部分,你必须先填充最左侧列和顶部行的剩余单元格。
实际上,你知道将“play”转换为空字符串只需删除每个字母。因此,你可以按照以下公式从上到下填充这些单元格:对于每个单元格,查看其上方单元格并加上一次额外删除操作的成本,即 1。
这意味着要将字符串“P”转换为空字符串,需要一次删除操作。如之前的例子所示,将字符串“PL”转换为空字符串需要删除P和L,即两次删除操作,依此类推。现在,在D[4,0]处,你得到了从“play”到空字符串的最小编辑距离,这当然就是四次删除的成本 4。
你可以使用相同的思路填充第一行,通过每次插入一个字母将空字符串转换为“stay”。你可以按照这个略有不同的公式从左到右工作:查看前一个单元格并加上一次额外插入成本 1。目前看起来不错。
核心公式的应用
在之前的例子中,我展示了如何不使用公式计算这个单元格,但你也可以通过应用这个看起来复杂的大公式来找到解决方案。它基于你已经进行的计算,与使用无公式方法的方式完全相同。
虽然其中一些内容可能感觉像是在重复你刚刚学到的知识,但查看公式也很有价值,特别是在准备本周的编程作业时。
因此,到达这个橙色单元格的距离将是从之前三个单元格中的任何一个到达它的最小距离。这听起来很有趣,对吧?起初可能看起来有点抽象,但你可以将其分解为更小的部分。
例如:
- 如果你来自上方的单元格,你将加上删除成本,就像你在第一列中所做的那样。
- 如果你来自左侧的单元格,你将加上插入成本,就像你在顶部行中所做的那样。
- 如果你来自左上方的单元格,你将做两件事之一:如果源单词的第i个字母和目标单词的第j个字母不匹配,则加上替换成本;如果它们匹配,则不加任何成本,因为对于已经相同的字母无需进行编辑。
因此,对于这个单元格,你有以下计算:
- 来自上方:1 + 1 = 2
- 来自左侧:1 + 1 = 2
- 来自左上方:由于这两个字母(P和S)不匹配,所以是 0 + 2 = 2
然后取这三个值中的最小值,即 2,并将该值填入单元格。这就是使用定义的公式和成本从“P”到“S”的最小编辑距离。
完成表格与模式观察
然后,你可以用同样的方式填充表格的其余部分。右下角的M[N, M]条目就是从“play”到“stay”的最小编辑距离,即 4。
添加颜色编码或热图可以揭示一些有趣的模式。你能从中间的正方形看出发生了什么吗?没错。一旦你从“PL”转换到“ST”,两个单词的后缀都是相同的“AY”。因此,不再需要更多的编辑操作。这就是为什么这个 4 会沿着对角线向下延伸。
现在,你已经知道了如何使用表格高效地构建最小编辑距离算法。做得很好。
实现注意事项与总结
在你继续之前,关于这种实现风格还有几点值得注意。我将在下一节向你展示这些。
恭喜!现在你已经看到了一些将在编程作业中使用的代码。在下一个视频中,我将向你展示更多技巧,这些技巧将帮助你完成编程作业。下次见。

本节课中,我们一起学习了如何用公式化方法填充最小编辑距离表格,理解了从边界初始化到核心单元格计算的完整过程,并观察到了后缀相同时编辑距离沿对角线传播的有趣模式。这为后续的编程实现打下了坚实的基础。
059:最小编辑距离算法III 🧮

在本节课中,我们将对最小编辑距离算法进行完整的回顾,并学习如何重构在编辑过程中所采取的路径。
算法概述与命名
上一节我们介绍了使用插入、删除和替换三种编辑操作来计算最小编辑距离的方法。本节中我们来看看这个算法的正式名称和一些扩展知识。
您所学习的实现方式,即赋予插入、删除成本为1,替换成本为2来衡量编辑距离,被称为 莱文斯坦距离。您可以查阅相关资料了解更多细节。
此外,还存在其他已知的、使用不同编辑规则的替代度量方法。
路径回溯
仅找到最小编辑距离本身有时并不能完全解决问题。您通常还需要知道是如何达到这个结果的。为此,我们需要进行回溯。
以下是实现回溯的方法:
- 在每个单元格中保存一个指针,用于指示到达该单元格的前一个位置。
- 通过从表格左上角到右下角的路径,您可以清楚地知道所采取的具体编辑操作序列。
- 这在处理字符串对齐等问题时特别有用,不过相关内容我们将在以后讨论。
动态编程技术
最后,我们使用的这种基于表格的计算方法,而非暴力穷举,是一种被称为动态编程的技术。
本质上,动态编程意味着先解决最小的子问题,然后复用该结果来解决下一个稍大的子问题,如此反复。您在本例中按顺序求解每个单元格的过程,正是这一思想的体现。
动态编程是计算机科学中一项著名的技术,在本课程后续的几周里将会反复出现。
总结与回顾
本节课内容非常丰富,您做得很好。您将求解最小编辑距离的问题分解为两个部分,然后逐步使用基于表格的方法实现了高效的算法。这是很棒的工作。😊
现在,您可以尝试编程作业,在那里您将编写此算法的代码,并有一个可选挑战:构建一个回溯工具。
旅程才刚刚开始,接下来的几周将充满更多令人兴奋的内容。但在继续之前,让我们快速回顾一下过去几节课的内容:
- 您学习了许多可能每天都在使用的NLP实际应用。
- 您了解了自动更正是什么以及它如何工作。
- 您逐步学习了如何使用编辑距离和词概率来构建一个可工作的自动更正模型。😊
- 您学习了字符串相似性问题和最小编辑距离这一度量标准。
- 最后,您学习了一种非常酷的、使用被称为动态编程的表格算法技术来解决最小编辑距离问题的方法。
现在,您可以在第一周的编程作业中练习所有这些新技能了。祝您编程愉快,好运!


恭喜您完成本周的学习!最小编辑距离是您对动态编程的第一次应用。
在下一周,您将学习维特比算法,它同样会用到动态编程技术。我们很快再见。😊
062:12_词性标注 📚
在本节课中,我们将要学习词性标注。你将了解它的不同应用场景,学习如何计算词性标注器的准确率,并掌握如何使用马尔可夫链和隐马尔可夫模型为文本语料库创建词性标注。最后,我们将介绍维特比算法及其在隐马尔可夫模型中的应用。
什么是词性标注?🔍
词性指的是语言中单词或词汇术语的类别。在英语中,这些词汇术语的例子包括名词、动词、形容词、副词、代词、介词等。让我们来看一个句子:“Why not learn something.”。
在文本分析中,完整写出这些术语的名称会变得繁琐。因此,我们将使用一种简短的表示形式,称为标签,来代表这些类别。
将标签分配给句子或语料库中单词的过程,被称为词性标注,简称P标注。

词性标注的应用 📊
由于词性标签描述了句子或文本中词汇术语的特征结构,你可以利用它们对语义进行推断。
以下是词性标注的主要应用:
- 命名实体识别:用于识别句子中的命名实体。例如,在句子“the Eiffel Tower is located in Paris”中,“Eiffel Tower”和“Paris”都是命名实体。
- 指代消解:用于解决指代问题。例如,对于两个句子“the Eiffel Tower is located in Paris. It is 324 m high.”,你可以利用词性标注推断出“It”在此上下文中指的是“the Eiffel Tower”。
- 语音识别:在语音识别中,使用词性标签来检查一个单词序列是否具有较高的概率。
从马尔可夫链到隐马尔可夫模型 ⛓️
上一节我们介绍了词性标注及其应用。本节中,我们来看看如何使用马尔可夫链来解码特定句子的词性。
马尔可夫链是一种数学模型,用于描述一系列可能的事件,其中每个事件的概率仅取决于前一个事件达到的状态。
在词性标注的上下文中,我们可以将词性序列视为一个马尔可夫链。然而,我们实际观察到的是单词序列,而词性标签是隐藏的。这就需要引入隐马尔可夫模型。
隐马尔可夫模型包含两种概率:
- 转移概率:从一个词性标签转移到下一个词性标签的概率。
- 发射概率:在给定某个词性标签的条件下,观察到某个特定单词的概率。
维特比算法与解码 🧮
为了在隐马尔可夫模型中找到最可能的隐藏状态(词性标签)序列,我们使用维特比算法。
维特比算法是一种动态规划算法,它通过递归地计算每个步骤和每个可能状态的最大概率路径,来高效地找到最优的标签序列。
以下是维特比算法的核心步骤:
- 初始化:为序列的第一个单词和每个可能的词性标签计算初始概率。
- 递归:对于序列中的每一个后续单词,计算到达每个可能词性标签的最大概率,并记录下产生该最大概率的前一个标签。
- 终止:在序列末尾,选择概率最高的词性标签。
- 路径回溯:从终止状态开始,根据记录的前驱信息,反向回溯出整个最可能的词性标签序列。
本周实践任务 💻
在本周的编程作业中,你将应用所有这些技能。这是一个重要的环节,你可以通过一个具体的例子来亲自尝试实现词性标注。
总结 📝

本节课中,我们一起学习了词性标注的基础知识。我们了解了词性标注的定义、它在命名实体识别、指代消解和语音识别等领域的应用。接着,我们探讨了如何使用马尔可夫链和隐马尔可夫模型来建模词性序列,并重点介绍了用于寻找最优标签序列的维特比算法。通过本周的实践,你将有机会巩固这些概念。
063:13_马尔可夫链 🎲

概述
在本节课中,我们将要学习马尔可夫链。这是一种重要的随机模型,广泛应用于语音识别和词性标注等领域。我们将从一个小例子开始,理解其核心思想,然后学习其基本构成要素:状态和转移概率。
马尔可夫链简介
上一节我们介绍了本课程的目标,本节中我们来看看什么是马尔可夫链。
马尔可夫链非常重要,因为它们被用于语音识别,同时也用于词性标注。在本视频中,我们将学习转移概率,你也将在开始之前了解状态的概念。
让我们先从一个简单的例子开始,展示你想要完成的任务。然后你将看到马尔可夫链如何帮助完成这个任务。
如果你看这个句子:“why not learn”。单词“learn”是一个动词。你想要回答的问题是,句子中接下来的单词是名词、动词还是其他词性。
如果你熟悉英语,你可能会猜测,如果你在句子中看到一个动词,接下来的单词更有可能是名词,而不是另一个动词。
这里的核心思想是:句子中下一个单词的词性标签的可能性,往往取决于前一个单词的词性标签。这很合理,对吧?
你可以用图形化的方式表示这些不同的可能性。这里有一个代表动词的圆圈,那边有一个代表名词的圆圈。你可以画一个箭头,从动词圆圈指向名词圆圈。同样,你可以画另一个箭头,从动词圆圈出发,绕回来指向它自己。
你可以为每个箭头关联一个数字,其中较大的数字意味着从一个圆圈移动到另一个圆圈的可能性更高。在这个例子中,从动词到名词的箭头概率可能是0.6,而从动词回到动词的箭头概率是0.2。更高的数字0.6意味着从动词到名词的可能性,高于从动词到另一个动词。
这是一个在非常小的规模上展示马尔可夫链如何工作的绝佳例子。
什么是马尔可夫链?
上一节我们通过例子感受了马尔可夫链的思想,本节中我们来正式定义它。
马尔可夫链是一种随机模型,它描述了一系列可能的事件,其中每个事件的概率仅取决于前一个事件的状态。单词“随机”意味着随机性或不确定性。因此,随机模型包含并模拟了具有随机成分的过程。
马尔可夫链可以被描绘成一个有向图。在计算机科学的语境中,图是一种数据结构,在视觉上表现为由线条连接的一组圆圈。当连接圆圈的线条带有指示特定方向的箭头时,这被称为有向图。
图中的圆圈代表我们模型的状态。状态指的是当前时刻的某种条件。例如,如果你用一个图来模拟水是处于冻结状态、液态状态还是气态状态,那么你会为每个状态画一个圆圈,以代表水在当前时刻可能处于的三种状态。
我将每个状态标记为Q1、Q2、Q3等,给它们各自一个唯一的名称。然后用大写字母Q来指代所有状态的集合。对于这个图,有三个状态:Q1、Q2和Q3。
应用:词性标注
你现在已经了解了模型中的状态,我们说状态代表条件或当前时刻。这些状态可以被认为是词性标签,也许一个状态对应动词,另一个对应名词,依此类推。
以下是使用马尔可夫链进行词性标注的基本步骤:
- 定义状态:将每个可能的词性(如名词、动词、形容词)定义为一个状态。
- 计算转移概率:基于大量文本数据,统计从一个词性转移到另一个词性的频率,并计算为概率。
- 构建模型:使用这些状态和转移概率构建一个有向图模型。
- 进行预测:给定一个单词序列,利用模型计算最可能的词性标签序列。

在下一个视频中,我们将深入学习词性标签。
总结
本节课中我们一起学习了马尔可夫链。我们了解到它是一种仅依赖前一个状态的随机模型,可以用有向图表示,其中节点是状态,边是转移概率。我们通过一个简单的词性预测例子,直观地理解了它的工作原理,并知道了它在自然语言处理(如词性标注)中的重要性。
064:马尔可夫链与词性标签 🏷️➡️

在本节课中,我们将学习如何将马尔可夫链应用于自然语言处理,特别是用于词性标注。我们将介绍状态、转移概率和马尔可夫性质等核心概念,并了解如何使用转移矩阵来表示模型。
概述
上一节我们介绍了马尔可夫链中的“状态”概念。本节中,我们将看看如何将这些状态应用于词性标注。具体来说,你将看到如何从一个状态转移到另一个状态。
从状态到转移概率
在词性标注中,我们可以将句子视为一系列带有词性标签的单词。这个序列可以用一个图来表示,其中词性标签是可能发生的事件,由我们模型图中的状态来描绘。
例如,NN 代表名词,VB 代表动词,Other 代表所有其他标签。
图的边具有与之相关的权重或转移概率,这些概率定义了从一个状态转移到另一个状态的可能性。
马尔可夫性质
马尔可夫链还有一个重要属性,即所谓的马尔可夫性质。它指出,下一个事件的概率仅取决于当前事件。
马尔可夫性质通过声明确定下一个状态所需的全部信息就是当前状态,从而帮助保持模型简单。它不需要来自任何先前状态的信息。
回想一下水是固态、液态还是气态的类比。如果你看一杯放在室外的水,水的当前状态是液态。在模拟杯中水转变为气态的概率时,你不需要知道水以前的历史——它之前是来自冰块,还是来自雨云。这很合理,对吧?
转移概率示例
让我们重新审视之前的例句。
如果你想再次查看这个句子,并想知道 learn 之后下一个单词是名词的概率,那么这仅仅取决于你当前所处的状态。在本例中,是动词状态(由 VB 表示),因为当前单词 learn 是动词。
所以,下一个单词是名词的概率,就是从动词状态转移到名词状态 NN 的转移概率。这个转移概率写在从 VB 指向 NN 的箭头上。如你所见,它是 0.4。
转移矩阵
你也可以使用一个表格来存储状态和转移概率。表格是马尔可夫链模型的一种等效但更紧凑的表示形式,这个表格被称为转移矩阵。
转移矩阵是一个 n x n 的矩阵,其中 n 是图中状态的数量。
矩阵中的每一行代表从一个状态到所有其他状态的转移概率。例如,第一行表示当前状态是名词的情况。
列代表可能的下一个未来状态。
表格内的值代表从名词到名词、从名词到动词以及从名词到其他状态的转移概率。
请注意,对于从给定状态出发的所有转移概率,这些转移概率的总和应始终为 1。等效地,在转移矩阵中,每一行的所有转移概率加起来应为 1。
初始状态
你们中的一些人可能已经注意到这个模型的一个小缺陷:它没有告诉你如何为句子中的第一个单词分配词性标签。这是因为模型中的所有状态都对应单词,但当没有前一个单词时(例如在句子开头),你该怎么办?
为了处理这种情况,你可以引入所谓的初始状态。通过将这些概率包含在表 A 中,现在它的维度是 (n+1) x n。
请注意,转移矩阵可以写成如下所示的实际矩阵:
A = [ [初始概率],
[从状态1到所有状态的转移概率],
[从状态2到所有状态的转移概率],
... ]
总结
本节课中,我们一起学习了:
- 马尔可夫链由一组状态
Q = {Q1, Q2, ..., Qn}组成。 - 转移矩阵
A的维度为(n+1) x n,第一行包含初始概率。 - 马尔可夫性质简化了模型,使下一个状态的概率仅依赖于当前状态。

到目前为止做得很好。在下一个视频中,你将学习隐马尔可夫模型,它用于解码单词的隐藏状态——在我们的例子中,就是该单词的词性。
065:隐马尔可夫模型 🧩
在本节课中,我们将学习隐马尔可夫模型。你将了解如何使用该模型来解码单词的隐藏状态,在本例中,隐藏状态即单词的词性。我们还将介绍发射概率的概念。

隐马尔可夫模型概述
上一节我们讨论了马尔可夫模型,现在让我们来看看它的扩展——隐马尔可夫模型。
“隐马尔可夫模型”这一名称意味着状态是隐藏的,或者说无法直接观测。回顾一下马尔可夫模型,其状态代表词性,如名词、动词或其他。现在,你可以将这些状态视为隐藏状态,因为它们无法直接从文本数据中观测到。
你可能会觉得“数据是隐藏的”这个说法有些令人困惑。例如,当你看到“jump”这个单词时,熟悉英语的人能看出它是一个动词。然而,从机器的视角来看,它只看到文本“jump”,并不知道它是动词还是名词。
对于查看文本数据的机器而言,它能观测到的是实际的单词,如“jump”、“run”和“fly”。这些单词被称为可观测值,因为机器能够看到它们。
转移概率与发射概率
马尔可夫链模型和隐马尔可夫模型都包含转移概率,它可以表示为一个维度为 (n+1) x n 的矩阵 A,其中 n 是隐藏状态的数量。
隐马尔可夫模型还包含额外的概率,称为发射概率。它们描述了从隐马尔可夫模型的隐藏状态(即词性,图中用圆圈表示名词、动词等)到可观测值(即语料库中的单词,图中在矩形内表示)的转移过程。
例如,图中展示了隐藏状态“VB”(动词)对应的可观测值“going”、“to”、“eat”。从隐藏状态“动词”到可观测值“eat”的发射概率是0.5。这意味着当模型当前处于动词的隐藏状态时,有50%的概率会发射出单词“eat”。
发射概率的表示
以下是发射概率的一种等价表格表示形式。
每一行对应一个隐藏状态,每一列对应一个可观测值。例如,隐藏状态“动词”所在的行与可观测值“E”所在的列相交,其值0.5就是从状态“动词”发射出可观测值“E”的发射概率。
发射矩阵 B 表示从代表词性的 N 个隐藏状态,到语料库中 M 个单词的转移概率。同样,每行的概率之和为1。
在这个例子中,你可能已经注意到,我们所有三个词性(名词、动词、其他)的发射概率都大于零。这是因为单词根据其出现的上下文,可以被分配不同的词性标签。
例如,单词“back”在以下两个句子中应有不同的词性标签:
- 在句子“He lay on his back.”中,标签是名词。
- 在句子“I‘ll be back.”中,标签是副词。
模型定义与总结
以下是隐马尔可夫模型的快速回顾。

隐马尔可夫模型包含:
- 一组
N个状态 Q。 - 转移矩阵 A,维度为
N x N。 - 发射矩阵 B,维度为
N x V(V是可观测词汇表的大小)。
在本节课中,我们一起学习了隐马尔可夫模型的表示方法,并了解了如何计算转移概率和发射概率。
066:计算概率 🧮

在本节课中,我们将学习如何根据给定的语料库,计算并填充隐马尔可夫模型中的转移矩阵和发射矩阵的概率。我们将从理解基本概念开始,逐步学习具体的计算方法。
计算转移矩阵概率
上一节我们介绍了隐马尔可夫模型的基本结构。本节中,我们来看看如何计算状态之间的转移概率,即填充转移矩阵。
转移矩阵存储了马尔可夫模型各状态间的转移概率。为了从概念上理解如何计算这些概率,我们使用一个非常小的训练语料库作为示例。可以看到,词性标签由背景颜色表示。计算转移概率时,实际上只使用训练语料库中的词性标签序列。
要计算蓝色词性标签转移到紫色词性标签的概率,首先需要统计该标签组合在语料库中出现的次数。在这个语料库中,该次数为2。而以蓝色标签开头的所有标签对的总数是3。因此,在训练语料库中,三分之二的标签序列是以蓝色词性标签开始的。换句话说,蓝色标签后接紫色标签的转移概率是2/3。
更正式地说,为了计算马尔可夫模型的所有转移概率,首先需要统计训练语料库中所有标签对的出现次数。我们可以定义一个函数 C(ti-1, ti),它返回语料库中标签 ti-1 后跟标签 ti 的计数。
接下来,计算一个标签 ti 跟在另一个标签 ti-1 后面的概率,即 P(ti | ti-1)。分子是 C(ti-1, ti),即 ti-1 和 ti 共同出现的次数。分母是标签 ti-1 与所有其他标签 tj 共同出现的总次数之和。这将在后续的幻灯片中变得更清晰。
我们可以将概率 P(ti | ti-1) 写作:该概率由标签 ti-1 后出现标签 ti 的总次数(由函数 C(ti-1, ti) 给出,作为分子)除以标签 ti-1 出现的总次数(由函数 C(ti-1) 给出,作为分母)得到。
实战示例:俳句模型
假设你想为一种日本短诗——俳句训练一个模型。你的训练语料库将是以下由埃兹拉·庞德于1913年创作的俳句。在编程作业中,你会得到一个准备好的语料库。但在这里,为了计算正确的概率,我们需要对语料库进行一些修改。
以下是处理步骤:
首先,将语料库的每一行视为一个独立的句子。在每一行(或句子)的开头添加一个起始标记,以便能够使用前面定义的公式计算初始概率。
然后,将语料库中的所有单词转换为小写,使模型对大小写不敏感。
标点符号应保持原样,因为在这个简单的模型中,标点不影响计算,并且这里没有为不同类型的标点设置单独的标签。
经过这些步骤,你就得到了一个准备就绪的语料库。
本节总结

本节课中我们一起学习了如何计算词性标签之间的转移概率。你看到了如何从语料库中获取计数,然后如何将这些计数转化为概率。这非常有用,因为它使你能够填充模型的转移矩阵。在接下来的课程中,我们将学习如何计算发射矩阵的概率。
067:填充转移矩阵 📊

在本节课中,我们将学习如何填充隐马尔可夫模型中的转移矩阵。你将了解如何计算从一个词性标签转移到另一个词性标签的概率,以及如何处理数据中可能出现的零概率问题。
概述
转移矩阵用于描述在序列中,从一个状态(例如,一个词性标签)转移到另一个状态的概率。为了填充这个矩阵,你需要计算在给定当前标签的情况下,下一个标签出现的概率。同时,你还需要计算每个标签作为句子开头的概率。此外,我们将介绍一个称为“平滑”的新概念,用于处理数据稀疏性问题。
填充转移矩阵
上一节我们介绍了转移矩阵的基本概念,本节中我们来看看如何具体计算和填充它。
首先,用相关标签的计数填充矩阵的第一列。记住,矩阵的行代表当前状态,列代表下一个状态。值代表从当前状态转移到下一个状态的概率。
在这个用例中,状态是词性标签。如你所见,定义的标签和语料库中的元素用相应的颜色标记。
对于第一列,你需要计算以下标签组合的出现次数。例如:
- 一个名词跟在起始标记后出现一次。
- 一个名词跟在另一个名词后出现零次。
- 一个名词跟在动词后出现零次。
- 一个名词跟在“其他”标签(O)后出现六次。
矩阵的其余部分也按此方式填充,但这里可以走一个捷径。如你所见,这个语料库是一个没有动词的俳句。因为没有任何包含VB(动词)标签的组合,所以相关计数都是0。
然而,在编程作业中可没有这样的捷径。需要特别注意,“其他”标签(O)跟在起始标记后出现了两次。O跟在名词标签(N)后出现了六次。转移矩阵中最后一个条目,即O跟在O后面,计数为8。
在最后一行,你必须考虑以“A.Wes, wet, comma”结尾的单词,并返回计算正确的计数。
计算转移概率
现在你已经计算了矩阵中所有标签组合的计数,接下来可以计算转移概率了。
到目前为止,你计算并输入到矩阵中的是计数,这对应着我们公式中的分子。现在,你只需要将每个计数除以对应的行总和。
记住这个行总和代表什么。对于当前状态是名词词性的那一行,该行的总和代表了所有“当前状态是名词,下一个状态是任意词性(无论是名词、动词还是其他)”的词对数量。
因此,对于名词标签(N)跟在起始标记后的转移概率(换句话说,名词标签的初始概率),我们用1除以3。或者,对于“其他”标签后跟名词标签的转移概率,我们用6除以14。
引入平滑技术
你可能已经意识到这里存在两个问题。第一个问题是,VB(动词)标签的行总和是0,使用这个公式会导致除以0的错误。第二个问题是,转移矩阵中有很多条目是0,这意味着这些转移的概率为0。如果你希望模型能推广到其他可能包含动词的俳句,这将是不可行的。
为了解决这个问题,我们需要稍微修改一下公式:在分子的每个计数上加上一个很小的值ε(epsilon),并在除数上加上n倍的ε(n是状态数量),这样行总和仍然加起来等于1。这个操作也被称为“平滑”,你可能在之前的课程中还记得它。
因此,公式变为:
*P(next_tag | current_tag) = (count(current_tag, next_tag) + ε) / (sum(count(current_tag, )) + n * ε)
如果将ε设为一个很小的值,比如0.001,那么你将得到以下转移矩阵。这里显示的值保留到小数点后三位。所以,如果行总和没有精确地加到1,不必担心。
平滑的结果是,如你所见,矩阵中不再有任何零值条目。此外,由于从VB状态出发的所有转移概率实际上都被归一化了,它们变得同等可能。这是合理的,因为你没有任何数据来估计这些转移概率。
关于平滑的注意事项
在你继续之前,还有一件事需要注意。在一个真实的例子中,你可能不希望将平滑技术应用于转移矩阵第一行的初始概率。
这是因为,如果你通过给可能为0的条目添加一个小值来对该行进行平滑,你实际上是在允许一个句子以任何词性标签(包括标点符号)开头。

总结
本节课中,我们一起学习了如何填充隐马尔可夫模型的转移矩阵。我们首先通过计数语料库中的标签组合来填充矩阵,然后通过除以行总和将其转换为概率。为了解决数据稀疏性和零概率问题,我们引入了平滑技术,通过添加一个小的ε值来确保所有转移都有非零概率,并使模型更具鲁棒性。同时,我们注意到对初始概率行应用平滑需要谨慎。在下一个视频中,我们将继续学习如何填充另一种称为发射矩阵的矩阵。
068:18_填充发射矩阵 📊

在本节课中,我们将学习如何构建和填充发射矩阵。这是隐马尔可夫模型(HMM)用于词性标注的关键组成部分之一。我们将通过一个简单的例子,一步步演示如何从训练语料中计算发射概率。
概述
上一节我们介绍了如何计算转移矩阵,它描述了词性标签之间的转换概率。然而,仅知道标签间的转换概率还不足以进行词性标注。我们需要将具体的词语纳入考量。因此,本节将引入一种新的矩阵——发射矩阵。它描述了在给定某个词性标签的条件下,观察到特定词语的概率。
构建发射矩阵
发射矩阵的核心是计算词性标签与特定词语共同出现的频率。以下是如何从一个小型训练语料库开始构建的过程。
假设我们有一个非常小的训练语料库,其中每个词都带有对应的词性标签(用背景色表示)。与计算转移概率时统计标签对的出现次数不同,这里我们需要统计一个特定的词性标签与一个特定词语配对的次数。
例如,在语料库中,词语“you”出现了两次,并且两次都与蓝色的词性标签(例如代词)相关联。同时,蓝色的词性标签在语料库中总共出现了三次。因此,在给定蓝色标签的条件下,发射词语“you”的概率是 2/3。
一个更复杂的例子
让我们继续使用上一课处理过的俳句作为更复杂的例子。我们拥有相同的词性标签集合,现在开始填充计数。
在发射矩阵中,行代表词性标签(如名词、动词、其他标签),列代表词汇表中的词语。我们统计的是:某个词性标签与某个特定词语配对的次数。
例如,对于词语“in”:
- 名词标签与词语“in”配对的次数为 0。
- 动词标签与词语“in”配对的次数为 0。
- 其他标签(O)与词语“in”配对的次数为 2(因为“in”在语料中出现了两次,且都被标记为其他标签)。
发射概率公式与平滑
发射概率 P(word | tag) 的计算公式如下。为了模型的泛化性,我们同样引入平滑技术:
P(w_i | t_i) = (count(t_i, w_i) + 1) / (count(t_i) + V)
其中:
count(t_i, w_i)是词性标签t_i与词语w_i的共现次数。count(t_i)是词性标签t_i在语料中的总出现次数。V是词汇表的大小。- 加1平滑确保了即使某个词语在给定标签下从未出现,其概率也不会为零。
总结
本节课我们一起学习了如何填充发射矩阵。你现在已经掌握了计算转移矩阵和发射矩阵的方法,并且了解了应用平滑技术以提升模型泛化能力的重要性。本周你取得了很大的进步,做得很好!

掌握了发射矩阵和转移矩阵后,在接下来的课程中,你将学习如何将它们结合起来,共同用于计算给定句子的词性标签序列。
069:维特比算法 🧠
概述

在本节课中,我们将学习维特比算法。这是一种用于隐马尔可夫模型的动态规划算法,其核心目标是:给定一个观测序列(例如一个句子),找出最有可能产生该观测序列的隐藏状态序列(例如词性标注序列)。我们将通过图解和公式,一步步理解该算法的工作原理。
从局部最优到全局最优
上一节我们介绍了如何利用转移概率和发射概率来选择单个最可能的下一个状态。然而,为一个完整句子寻找最可能的词性序列,需要从全局角度进行优化。本节中我们来看看如何将问题转化为图搜索,并引入维特比算法。
假设我们有一个简单的隐马尔可夫模型和句子 “I love to learn”。我们的目标是找到概率最高的隐藏状态(词性)序列。请注意,单词 “love” 可能由名词状态 NN 或动词状态 VB 发射。
算法从一个初始状态 π 开始。对于第一个词 “I”,我们选择最可能的状态(例如 O),这涉及绿色的转移概率(例如 0.3)和橙色的发射概率(例如 0.5)。观察到 “I” 并经过 O 状态的联合概率是 0.15,由转移概率和发射概率相乘得到:0.3 * 0.5 = 0.15。
对于第二个词 “love”,存在两条可能路径:经过 NN 状态或 VB 状态。虽然转移到这两个状态的转移概率相同,但 “love” 从 VB 状态发射的概率更高。因此,我们应选择那条路径,其联合概率为 0.25。
后续步骤依此类推,最终得到整个序列的总概率,即所有选定步骤概率的乘积,例如 0.0003。维特比算法的核心在于同时计算多条路径,以找到全局最优的隐藏状态序列。
算法的矩阵表示与步骤
维特比算法使用隐马尔可夫模型的矩阵表示。该算法可分为三个主要步骤:初始化、前向路径(动态规划计算)和后向路径(回溯最优序列)。
给定转移概率矩阵 A 和发射概率矩阵 B,算法需要填充并使用两个辅助矩阵:C(维特比矩阵)和 D(回溯指针矩阵)。
- 矩阵
C:存储中间的最优概率值。C[i, t]表示在时刻t以状态i结束的所有局部路径中的最大概率。 - 矩阵
D:存储达到C[i, t]所记录概率时,在时刻t-1所选择的前一个状态索引。
这两个矩阵的维度为 N 行 K 列,其中 N 是模型中隐藏状态(词性)的数量,K 是给定句子中的单词数量。
以下是算法的三个核心步骤:
-
初始化:为第一个单词(
t=1)的所有可能状态计算初始概率。- 公式:
C[i, 1] = π[i] * B[i, word1] - 对于所有状态
i,D[i, 1] = 0(因为第一个词没有前驱状态)。
- 公式:
-
前向路径(递归计算):对于句子中的每一个后续单词(
t = 2 到 K),为每一个可能的状态j进行计算。- 核心是找到能使路径概率最大化的前一个状态
i。 - 公式:
C[j, t] = max_over_i( C[i, t-1] * A[i, j] ) * B[j, word_t] - 同时,
D[j, t] = argmax_over_i( C[i, t-1] * A[i, j] )
- 核心是找到能使路径概率最大化的前一个状态
-
后向路径(回溯):在计算完所有单词后,从最后一个单词概率最大的状态开始,根据矩阵
D中存储的指针,反向追溯出整个最优状态序列。- 找到最终最优路径的终点:
best_path[K] = argmax_i( C[i, K] ) - 然后反向追溯:
for t from K-1 down to 1: best_path[t] = D[ best_path[t+1], t+1 ]
- 找到最终最优路径的终点:

总结
本节课中我们一起学习了维特比算法。我们首先理解了为何需要从全局视角寻找最优序列,而非仅做局部最优选择。接着,我们通过一个简单例子,将问题可视化为在状态图中寻找最优路径。最后,我们详细介绍了算法的三个核心步骤——初始化、前向路径计算和回溯——以及用于实现这些步骤的两个关键矩阵 C 和 D。维特比算法高效地解决了隐马尔可夫模型中的解码问题,是序列标注任务(如词性标注)的基石。
070:维特比算法初始化 🧮
在本节课中,我们将学习如何初始化一个矩阵,该矩阵能帮助我们确定句子中每个单词的词性标签。这是维特比算法三个关键步骤中的第一步,为后续计算最可能的词性序列奠定基础。
概述

维特比算法通过动态规划寻找隐藏状态(如词性标签)的最可能序列。初始化步骤负责填充两个辅助矩阵 C 和 D 的第一列。矩阵 C 存储概率,矩阵 D 存储路径回溯信息。
初始化步骤详解
上一节我们介绍了维特比算法的整体框架,本节中我们来看看具体的初始化操作是如何进行的。
初始化步骤的目标是计算从“起始状态”到第一个单词所有可能词性标签的联合概率。这对应着图中从起始节点到第一个隐藏状态(标签)和第一个观测值(单词)的路径。
为了清晰说明,下图展示了一个包含三个隐藏状态的模型初始化情况:

填充矩阵 C 的第一列
矩阵 C 的第一列(C[i, 1])代表从起始状态 π 转移到第一个标签 T_i,并观测到第一个单词 W_1 的联合概率。
其计算公式为初始转移概率与对应发射概率的乘积:
C[i, 1] = A[1, i] * B[i, index(W_1)]
其中:
A[1, i]是转移矩阵 A 的第一行元素,表示从起始状态到第i个标签的初始概率。B[i, index(W_1)]是发射矩阵 B 中的元素,表示在标签T_i下观测到单词W_1的概率。index(W_1)函数返回单词W_1在矩阵 B 中的列索引。
填充矩阵 D 的第一列
矩阵 D 用于存储回溯路径,即记录在寻找最可能词性序列时经过的各个状态标签。
由于第一列没有前驱状态(前面没有单词),因此 D 矩阵的第一列所有条目均设置为 0。
以下是初始化步骤的关键点总结:
- 矩阵 C 第一列:计算每个可能标签作为句子第一个词标签的初始概率。
- 矩阵 D 第一列:全部置零,表示路径起点。
总结
本节课中我们一起学习了维特比算法的初始化步骤。我们了解到,这一步通过结合初始概率(来自转移矩阵 A)和发射概率(来自矩阵 B),填充了概率矩阵 C 的第一列,同时将路径回溯矩阵 D 的第一列初始化为零。这为算法的下一步——递归地填充矩阵的其余部分——做好了准备。
在下一节视频中,你将学习如何继续填充这两个矩阵,并最终利用它们解码出给定句子最可能的词性标签序列。维特比算法不仅可用于词性标注,也广泛应用于语音识别等领域。


071:维特比前向传递 🧮

在本节课中,我们将学习如何使用维特比算法,继续填充你在上一节视频中初始化的两个矩阵。你将看到如何为句子中的特定词语分配词性标签。让我们来看看具体如何操作。
上一节我们介绍了矩阵C和D的初始化,本节中我们来看看如何通过前向传递来填充它们。
前向传递是填充矩阵C和D三个步骤中的第二步。现在你已经初始化了矩阵C和D,这两个矩阵中所有剩余的条目将在前向传递过程中逐列填充。
对于矩阵C,其条目通过以下看似复杂的函数计算得出:
C[j, i] = max_k ( C[k, j-1] * a[k, i] * b[i, w_j] )
让我们通过图示来明确下一步。假设你想计算条目C[1, 2]。你可以从公式的最后一项开始代入数值。
公式的最后一项就是从词性标签T1到词语W2的发射概率 b[i, w_j]。
公式中还有 a[k, i],这是从前一个词性标签Tk到当前标签Ti的转移概率。而 C[k, j-1] 则代表了你已经遍历的前序路径的概率。你需要选择使整个公式值最大的k。
在这种情况下,存在三个非初始状态。因此,k可以是1、2或3。
对于每个D[j, i],你只需保存使C[j, i]条目最大化的那个k值。这里同样有三个非初始状态,所以k是1、2或3。这由以下看似复杂的公式定义,该公式与C[j, i]的公式相同,只是去掉了前面的max函数:
D[j, i] = argmax_k ( C[k, j-1] * a[k, i] * b[i, w_j] )
argmax函数返回的是使函数参数最大化的k值,而不是最大值本身。


快完成了,只差最后一步。你现在已经使用维特比算法计算出了概率矩阵。在下一节视频中,你将看到如何利用刚刚创建的这个概率矩阵来重构路径,从而为每个词语确定其词性。
本节课中我们一起学习了维特比算法的前向传递步骤,理解了如何逐列填充概率矩阵C和回溯矩阵D,为后续的路径回溯和词性标注奠定了基础。
072:维特比后向传递 🧭

在本节课中,我们将学习维特比算法的最后一步——后向传递。我们将利用前向传递中计算出的概率矩阵,回溯并找出最可能的词性标注序列,从而为句子中的每个单词分配一个词性标签。
概述
上一节我们介绍了如何通过前向传递填充概率矩阵C和路径矩阵D。本节中,我们来看看如何利用矩阵D进行后向传递,从而从终点回溯,重建出最可能的隐藏状态(即词性标签)序列。
后向传递步骤详解
后向传递是维特比算法的第三步,用于为给定的单词序列检索最可能的词性标签序列。
以下是执行后向传递的具体步骤:
-
确定终点状态:首先,在矩阵C的最后一列(对应最后一个单词)中,找到概率值最高的条目。该条目对应的行索引
s代表了在观测到最后一个单词时,我们所处的最后一个隐藏状态。其概率值代表了生成整个单词序列的最可能隐藏状态序列的概率。- 公式表示:
s = argmax_i C[i][K],其中K是序列长度。
- 公式表示:
-
开始回溯:使用上一步得到的索引
s,从矩阵D的最后一列开始,向左遍历(即向序列开头回溯)。矩阵D存储了前向传递过程中记录的最佳前驱状态索引。 -
重建序列:
- 将索引
s对应的状态标签(如T1)添加到序列末尾。 - 在矩阵D的当前列(例如第
K列)的第s行找到存储的值,这个值指明了到达当前状态s的最可能的前一个状态索引。 - 将这个值作为新的
s,并移动到矩阵D的前一列(第K-1列)。 - 重复此过程,直到回溯到序列的第一个单词。
- 将索引
示例演示
假设我们有一个包含4个状态(T1, T2, T3, T4)的模型,以及一个长度为5的单词序列。我们已计算出矩阵C和D。
步骤1:找到终点
在矩阵C的最后一列(第5列)中,假设第一行的概率0.01最高。因此,s = 1。这意味着最后一个单词 W5 最可能的状态是 T1。我们将 T1 加入序列。
步骤2:第一次回溯
查看矩阵D第5列的第1行(因为 s=1)。假设该单元格的值为3。这意味着在观测第4个单词时,我们最可能来自状态 T3。因此,单词 W4 的标签是 T3。更新 s = 3,并移动到第4列。
步骤3:继续回溯
查看矩阵D第4列的第3行(因为 s=3)。假设该单元格的值为1。这意味着在观测第3个单词时,我们最可能来自状态 T1。因此,单词 W3 的标签是 T1。更新 s = 1,并移动到第3列。
重复此过程,直到第一列。最终,我们得到重建的词性标签序列,例如 [T2, T3, T1, T3, T1]。
实现注意事项
在编程实现维特比算法时,有两点需要特别注意:
-
索引偏移:Python中列表和矩阵的索引从0开始,而非1。在实现时,需要仔细处理索引的转换,确保与数学公式和逻辑描述保持一致。
- 代码提示:在访问数组时,通常需要将算法描述中的索引减1。
-
数值下溢:当连续乘以大量极小的概率值时,可能导致计算结果超出计算机浮点数的精度范围(数值下溢)。为了避免这个问题,通常使用对数概率。
- 核心概念:将对数概率相加,等价于原始概率相乘。即
log(p1 * p2) = log(p1) + log(p2)。这能有效保持数值稳定性。课程后续会再次讨论这个概念。
- 核心概念:将对数概率相加,等价于原始概率相乘。即
总结
本节课中,我们一起学习了维特比算法的后向传递过程。我们掌握了如何利用前向传递计算出的路径矩阵D,从最后一个单词开始回溯,逐步重建出为整个句子生成的最可能的词性标注序列。词性标注在搜索、机器翻译、语音识别和句法分析等领域都有广泛应用。

至此,你已经了解了维特比算法及其在词性标注中的应用。下一周,我们将开始学习一种不同类型的任务——N元语法语言模型。
075:N元语法概述 🧠

在本节课中,我们将要学习自然语言处理(NLP)中的一个基础概念——N元语法(N-gram)。N元语法是构建语言模型的核心技术,广泛应用于语音识别、拼写纠正、辅助沟通等多个领域。我们将了解如何从文本语料库构建N元语法模型,并探索其在句子自动补全等任务中的应用。
什么是N元语法?
N元语法是NLP中的基本概念。你可以将其应用于语音识别、拼写纠正、辅助沟通以及许多其他任务。
上一节我们介绍了N元语法的重要性,本节中我们来看看它的具体应用场景。
N元语法模型可以从一个文本语料库中创建。文本语料库通常是一个大型文本数据库,例如维基百科的所有页面、某位作者的所有书籍,或某个账户的所有推文。
语言模型是一种计算句子概率的工具。你可以将一个句子视为一个单词序列。
语言模型还可以根据已知的单词历史,来估计下一个即将出现的单词的概率。例如,如果你开始写一封邮件“Hello, how are”,你的邮件应用程序可能会猜测你想写的下一个单词是“you”,从而补全为“Hello, how are you”。
在接下来的实践中,你将构建自己的N元语法语言模型,并将其应用于自动补全给定的句子。
语言模型的应用
虽然本周我们将重点学习自动补全应用,但语言模型在其他领域也有广泛的应用。
以下是语言模型的一些主要应用场景:
- 语音识别:用于将声学模型的输出转换为正确的单词。例如,如果声学模型将语音转换为文本,听到“I saw a van”,语音转文本系统可以利用语言模型判断,句子更可能是“I saw a van”,而不是听起来相似的“Eyes are often”。
- 拼写纠正:根据单词在句子中的概率,识别并纠正可能拼写错误的单词。
- 辅助沟通系统:这类系统可以接收用户的一系列手势来帮助他们组成单词和句子。像斯蒂芬·霍金这样无法用语言或手语交流的人,可以使用简单的动作从菜单中选择单词,然后由系统替他们发声。利用语言模型进行单词预测,可以为菜单推荐可能的单词。
本周学习路线图
现在我们对N元语法有了基本了解,接下来让我们看看本周具体要掌握哪些技能。
以下是本周你将完成的学习步骤:
- 构建基础语言模型:首先,你将把文本语料库转化为一个语言模型。该模型能够根据一个句子的前几个单词,返回下一个单词的概率。
- 处理未知词汇:接下来,你将调整你的语言模型,以处理在训练过程中未曾见过的单词。这些单词被称为集外词。
- 应用平滑技术:平滑是另一种处理未见输入的技术。通过这种方法,即使对于未见过的单词和句子,也能成功估计其概率。平滑技术本质上会假设每个单词和短语在训练语料库中至少出现一次。这有助于计算概率,即使是对于不常见的单词和序列。
- 评估模型性能:最后,我将向你展示如何使用困惑度指标来选择最佳的语言模型。这是你工具箱中的一个新工具。
当你掌握了这些技能,就能在本周的作业中成功实现一个句子自动补全模型。
总结

本节课中,我们一起学习了N元语法的基本概念及其在构建语言模型中的核心作用。我们了解了语言模型在语音识别、拼写纠正和辅助沟通等领域的应用,并概述了本周从构建模型、处理未知词汇、应用平滑技术到评估模型性能的完整学习路径。
现在你已经对将要学习的内容有了一个概览,在下一个视频中,我们将深入探讨,从展示N元语法的概率计算开始。
076:吴恩达《自然语言处理》P76 - N元语法与概率 📚

在本节课中,我们将要学习N元语法(N-gram)语言模型。这将使你能够编写第一个能够自主生成文本的程序。
什么是N元语法? 🤔
首先,我们来了解什么是N元语法。简单来说,一个N元语法就是一个单词序列。请注意,它不仅仅是一个单词集合,因为单词的顺序至关重要。N元语法也可以由字符或其他元素构成,但目前我们将专注于单词序列。
在处理语料库时,标点符号被视为单词,但所有其他特殊字符(如引号)将被移除。
让我们看一个例子:“I am happy because I am learning”。
- 一元语法 是文本中所有出现的、唯一的单个单词的集合。例如,单词“I”在语料库中出现两次,但在单词语集合中只包含一次。前缀“uni”代表“一”。
- 二元语法 是语料库中所有成对出现的、相邻的两个单词的集合。同样,二元语法“I am”在文本中可以找到两次,但在二元语法集合中只包含一次。前缀“bi”代表“二”。请注意,单词必须彼此相邻出现才能被视为一个二元语法。另一个二元语法的例子是“am happy”。另一方面,序列“I happy”不属于二元语法集合,因为该短语没有出现在语料库中。即使单个单词“I”和“happy”都出现在文本中,“I happy”也被省略了。
- 三元语法 代表语料库中连续出现的、唯一的三个单词的组合。前缀“tri”代表“三”。
以下是一些你将要用到的符号表示法。如果你有一个包含500个单词的文本语料库,单词序列可以表示为 W1, W2, W3, ..., W500。语料库长度用变量 M 表示。
现在,对于该词汇表的一个子序列,如果你想指代从单词1到单词3的序列,则可以将其表示为 W₁³。要指代语料库的最后三个单词,你可以使用符号 W_{M-2}^M。
从语料库估计N元语法的概率 📊
上一节我们介绍了N元语法的基本概念,本节中我们来看看如何从语料库中估计它们的概率。
一元语法概率
让我们从一元语法开始。例如,在这个语料库“I am happy because I am learning”中,语料库大小是 M = 7。一元语法“I”的计数等于2。因此,其概率为 2 / 7。对于一元语法“happy”,概率等于 1 / 7。
一个单词 W 的一元语法概率可以通过计算单词 W 在语料库中出现的次数,然后除以语料库的总大小 M 来估计。公式如下:
P(W) = count(W) / M
这与你在前几周使用的单词概率概念类似。
二元语法概率
现在,让我们计算二元语法的概率。让我们从一个例子开始,然后我将展示通用公式。
在例子“I am happy because I am learning”中,如果前一个单词是“I”,那么单词“am”出现的概率是多少?它将是二元语法“I am”的计数除以一元语法“I”的计数。所以,你得到 count(“I am”) / count(“I”)。
在这个例子中,二元语法“I am”出现两次,一元语法“I”也出现两次。因此,在“I”立即出现的情况下,“am”出现的条件概率等于 2 / 2。换句话说,二元语法“I am”的概率等于 1。
对于二元语法“I happy”,概率等于 0,因为该序列从未出现在语料库中。最后,二元语法“am learning”的概率是 1/2。这是因为单词“am”后面跟着单词“learning”,构成了你语料库中二元语法的一半。
以下是二元语法概率的通用表达式。二元语法由单词 X 后跟单词 Y 表示。因此,单词 Y 紧接在单词 X 之后出现的概率,是给定 X 时 Y 的条件概率。
给定 X 时 Y 的条件概率可以估计为二元语法 (X, Y) 的计数,然后除以所有以 X 开头的二元语法的计数。这可以简化为:
P(Y|X) = count(X, Y) / count(X)
最后这一步仅在 X 后面跟着另一个单词时成立。
三元语法及N元语法概率
接下来,让我们使用之前的同一个例子计算一些三元语法的概率。短语“I am”后面跟着单词“happy”的概率计算为 1 除以语料库中短语“I am”的出现次数,即 2。
一个三元语法(或连续三个单词的序列)的概率,是前两个单词已经以正确顺序出现的情况下,第三个单词出现的概率。这是给定前两个单词出现时,第三个单词的条件概率。
给定前两个单词时,第三个单词的条件概率,是所有三个单词一起出现的次数除以前两个单词以正确顺序出现的次数。现在,所有三个单词出现的次数的符号表示为前两个单词(记为 W₁²)后跟一个空格,然后是 W₃。所以这只是整个三元语法的计数,写成一个二元语法后跟一个一元语法。
如果你想考虑任意数字 N 呢?让我们将公式推广到任意数字 N 的N元语法。
单词 W_N 跟随序列 W₁ 到 W_{n-1} 出现的概率,估计为N元语法 W₁ 到 W_N 的计数除以N元语法前缀 W₁ 到 W_{n-1} 的计数。公式如下:
P(W_N | W₁ ... W_{n-1}) = count(W₁ ... W_N) / count(W₁ ... W_{n-1})
注意,这里单词 W₁ 到 W_N 的N元语法计数写作 count(W₁^{n-1} W_N)。这等价于 C(W₁^n)。
总结 ✨

本节课中我们一起学习了N元语法,以及一元语法、二元语法和三元语法的具体例子。我们还通过计算它们在语料库中的出现次数来计算了它们的概率。这是很棒的工作。
现在你知道了N元语法是什么,以及如何使用它们来计算下一个单词的概率。接下来,你将学习使用它来计算整个句子的概率。
077:自然语言处理 | 序列概率建模 📊

课程编号:P77
在本节课中,我们将学习如何使用 n-gram 概率来建模整个句子。这将为我们后续生成文本奠定基础。
概述
首先,我们需要理解如何计算一个句子或整个单词序列的概率。例如,如何计算句子“The teacher drinks tea”的概率?
条件概率与链式法则回顾
上一节我们介绍了序列概率的基本概念,本节中我们来看看如何通过条件概率和链式法则来计算句子概率。
条件概率是指单词 B 在单词 A 之后出现的概率。其公式可以表示为:
P(B|A) = P(A, B) / P(A)
通过重新排列,我们可以得到联合概率的公式:
P(A, B) = P(A) × P(B|A)
链式法则将这一概念推广到更长的序列。对于一个由单词 A、B、C、D 依次组成的句子,其联合概率为:
P(A, B, C, D) = P(A) × P(B|A) × P(C|A, B) × P(D|A, B, C)
这个公式直观地表明,序列中每个后续单词的概率都取决于它之前所有单词的序列。
应用链式法则计算句子概率
现在,我们可以应用链式法则来计算句子“The teacher drinks tea”的概率。公式如下:
P(“The teacher drinks tea”) = P(The) × P(teacher|The) × P(drinks|The teacher) × P(tea|The teacher drinks)
在理想情况下,训练语料库中有足够的数据来计算所有这些概率。然而,这种方法存在局限性。
直接方法的局限性
由于自然语言的高度可变性,查询句子甚至句子的较长部分在训练语料库中出现的可能性非常小。对于句子“The teacher drinks tea”,其完整序列和前缀“The teacher drinks”在语料库中的计数很可能为零,导致无法计算概率。随着句子变长,精确顺序出现的可能性越来越小。
引入马尔可夫假设与 N-gram 近似
为了解决数据稀疏性问题,我们可以采用马尔可夫假设进行近似。马尔可夫假设认为,一个单词的概率仅依赖于其有限的、长度为 N 的历史记录,而忽略更早的单词。
如果我们只考虑前一个单词(即使用二元语法或 bigram),那么句子“The teacher drinks tea”的概率可以近似为:
P(“The teacher drinks tea”) ≈ P(The) × P(teacher|The) × P(drinks|teacher) × P(tea|drinks)
以下是应用 bigram 近似后的计算步骤:
- P(teacher|The):在“The”之后出现“teacher”的条件概率。
- P(drinks|teacher):在“teacher”之后出现“drinks”的条件概率。
- P(tea|drinks):在“drinks”之后出现“tea”的条件概率。
将这些条件概率相乘,就得到了整个句子的近似概率。此时,句子概率的估计公式简化为所有 bigram 条件概率的乘积。
对于 bigram(N=2),我们近似地认为单词 W_n 的条件概率仅依赖于前一个单词 W_{n-1},公式为:
P(W_n | W_1, ..., W_{n-1}) ≈ P(W_n | W_{n-1})
公式中的第一项 P(The) 是句子中第一个单词的单字(unigram)概率。这与朴素贝叶斯方法不同,朴素贝叶斯在估计句子概率时不考虑任何单词历史。

总结
本节课中,我们一起学习了如何使用 n-gram 概率和马尔可夫假设来建模和计算整个句子的概率。我们回顾了条件概率和链式法则,指出了直接计算长序列概率的局限性,并引入了 bigram 近似作为解决方案。通过只考虑前一个单词的历史,我们能够有效地估计句子概率,为后续的文本生成任务做好准备。下一节,我们将学习如何从语料库中计算这些 n-gram 概率。
078:句子开始与结束 🧩

在本节课中,我们将学习在实现N元语言模型时,如何处理句子的开始与结束。我们将了解为何需要引入特殊的开始与结束标记,以及如何将它们应用到句子中,以确保概率计算的完整性和一致性。
处理句子边界
上一节我们介绍了使用滑动窗口计算条件概率。本节中我们来看看,当窗口位于句子的开头或结尾时会发生什么。

让我们仔细看看句子最开头和最结尾的情况。我将展示如何使用两个新符号来修改句子,这两个符号分别表示句子的开始和结束。你将看到这些新符号的重要性。然后,你将学会如何为二元模型和一般的N元模型,在句子的开头和结尾添加它们。
句子的开始
现在我来解释如何解决二元近似中的第一项。
让我们回顾之前的句子:“The teacher drinks tea.”。对于第一个词“The”,你没有前一个词的上下文,因此无法计算二元概率,而这是进行预测所必需的。
以下是解决方法:
- 添加一个特殊的标记,使得语料库中的每个句子都变成一个可以计算概率的二元组。
- 例如,使用开始标记
<s>。现在你可以这样计算第一个词“The”的二元概率:P(The | <s>)。
类似的原则也适用于N元模型。例如,对于三元模型,前两个词没有足够的上下文。因此,你需要使用第一个词的一元概率和前两个词的二元概率。可以通过在句子开头添加两个句子开始符号 <s> <s> 来解决缺失的上下文问题。现在,句子概率就变成了三元概率的乘积。
为了将其推广到N元模型,在每个句子的开头添加 n-1 个开始标记 <s>。
句子的结束
现在你可以处理句子开头的N元组了。那么句子的结尾呢?
回想一下,给定词X时词Y的条件概率被估计为所有二元组 (X, Y) 的计数除以所有以X开头的二元组的计数。你将分母简化为所有一元组X的计数。
但在一种情况下,这种简化不成立:当词X是句子的最后一个词时。
例如,如果你查看语料库中的词“drinks”,所有以“drinks”开头的二元组之和只等于1,因为唯一以“drinks”开头的二元组是“drinks chocolate”。另一方面,词“drinks”在语料库中出现了两次,另一次是作为一元组出现。
为了继续使用条件概率的简化公式,你需要添加一个句子结束标记。
概率归一化问题
你的N元概率还存在另一个问题。
假设你有一个非常小的语料库,只有三个句子,包含两个独特的词:“Yes”和“No”。语料库由三个句子组成:“Yes no”、“Yes yes”和“No no”。这些都是从词“Yes”和“No”生成的、以开始符号 <s> 开头的所有可能的长度为2的句子。
为了计算句子“Yes yes”的二元概率,取带有开始符号的“Yes”的概率,乘以“Yes”作为第二个词(且前一个词也是“Yes”)的概率。带有开始符号的“Yes”的概率,是以“Yes”开头的二元组计数除以所有以开始符号开头的二元组计数。你只能在分母中使用二元组计数的总和。
接下来,处理剩余的一元组。将第一项乘以二元组“Yes yes”的计数除以所有以词“Yes”开头的二元组计数的分数。你得到 (2/3) * (1/2),等于 1/3。这样你就得到了句子“Yes yes”的概率,估计值为 1/3。
现在你计算句子“Yes no”的概率,再次得到 1/3。接着计算句子“No no”的概率,再次得到 1/3。最后,句子“No yes”的概率等于0,因为语料库中没有二元组“No yes”。
现在,令人惊讶的事情来了。如果你将所有四个句子的概率相加,总和等于1。这正是你追求的目标。干得好。
扩展到更长句子
那么,让我们看看所有由词“Yes”和“No”生成的可能的三词句子。首先计算句子“Yes yes yes”的概率,然后是“Yes yes no”,依此类推,直到你计算出所有8个可能长度为3的句子的概率。最后,当你将所有可能长度为3的句子的概率相加时,你再次得到总和为1。
然而,你真正想要的是所有长度为任意值的句子的概率总和等于1,这样你才能比较两个不同长度句子的概率。换句话说,你想要所有两词句子的概率加上所有三词句子的概率,再加上所有其他任意长度句子的概率,并且你希望这个总和等于1。
解决方案:添加结束标记
有一个非常简单的方法可以解决这个问题。你可以在预处理训练语料库时,在每个句子后面添加一个特殊的符号,用来表示句子的结束,我们将其表示为 </s>。
例如,当使用二元模型时,对于句子“The teacher drinks tea”,在词“tea”后面追加符号 </s>。现在,句子概率计算包含了一个新项。这一项代表了句子在词“tea”之后结束的概率。
这也解决了特定长度句子概率总和归一化的问题。
让我们看看这是否也解决了你二元概率公式的问题。现在,以词“drinks”开头的二元组有两个:“drinks chocolate”和“drinks ”。而一元组“drinks”的计数保持不变。太好了,你可以继续使用简化公式进行二元概率计算。
推广到N元模型
你如何将这一修正推广到一般的N元模型呢?事实证明,即使对于N元模型,在语料库中每个句子只添加一个结束标记就足够了。
例如,在计算三元模型时,原始句子将被预处理为包含两个开始标记和一个结束标记。
实际计算示例
让我们看一个在稍大语料库上生成的二元概率的例子。
以下是语料库:
<s> I am happy
<s> I am learning
<s> learning is fun
以下是其中一些二元组的条件概率。
现在,尝试计算 P(learning | <s>)。带有句子开始符号的句子总共有三个。所以开始符号在语料库中出现了三次,这给出了分母。二元组 <s> learning 在语料库中出现了两次,这给出了分子2。所以开始标记后跟“learning”的概率是 2/3。
现在,计算句子“learning is fun”的概率。
- 从二元组
<s> learning的概率开始,即2/3。 - 然后是
P(is | learning),即1/2。 - 接着是
P(fun | is),也是1/2。 - 最后是
P(</s> | fun),即2/2。
注意,结果等于 1/6,这低于你在计算训练语料库中三个句子之一的概率时可能预期的 1/3。这也适用于语料库中的其他两个句子。剩余的概率可以分配给可能从这个语料库的二元组生成的其他句子。这就是模型泛化的方式。

总结
本节课中,我们一起学习了在二元模型中处理句子开始与结束标记的示例。这个概念可以推广到其他类型的模型。在下一视频中,你将学习如何构建你的第一个N元语言模型。
079:29_N元语法语言模型 🧠

在本节课中,我们将学习如何构建一个N元语法(N-gram)语言模型。我们将从创建计数矩阵开始,然后将其转换为概率矩阵,并最终将其与语言模型的定义联系起来。我们还将探讨如何处理计算句子概率时可能出现的数值下溢问题,并简要介绍如何使用该模型生成新句子。
构建计数矩阵
上一节我们介绍了N元语法模型的基本概念。本节中,我们来看看如何从语料库中构建一个计数矩阵。这个矩阵记录了每个N元语法出现的次数。
公式:对于一个以单词 w_n 结尾的N元语法 (w_1, w_2, ..., w_n),其条件概率的分子是它在语料库中出现的次数。
计数矩阵的行由语料库中所有唯一的 (N-1) 元语法(前缀)组成,列由所有唯一的单词组成。
以下是针对语料库 “I study, I learn” 构建二元语法(Bigram)计数矩阵的示例:
| 行(第一个词) | 列(第二个词) | 计数 |
|---|---|---|
| I | study | 1 |
| I | learn | 1 |
| study | I | 0 |
| learn | I | 0 |
构建这个矩阵可以通过一次遍历语料库完成,使用一个包含两个词的滑动窗口来识别每个二元语法,并在矩阵对应位置计数加一。
计算概率矩阵
现在我们已经有了计数矩阵作为概率公式的分子,接下来需要计算分母以获得条件概率。
首先,计算计数矩阵中每一行的总和。这个行总和等价于公式中分母的 (N-1) 元语法前缀的计数。
公式:条件概率 P(w_n | w_1, ..., w_{n-1}) = count(w_1, ..., w_n) / count(w_1, ..., w_{n-1})
然后,通过将每个单元格的值除以其所在行的总和,对矩阵进行归一化,从而得到概率矩阵。
以前面的计数矩阵为例:
- 行 “I” 的总和为 2(study出现1次 + learn出现1次)。
- 因此,
P(study | I) = 1 / 2 = 0.5,P(learn | I) = 1 / 2 = 0.5。
得到的概率矩阵如下:
| 前缀 | study | learn |
|---|---|---|
| I | 0.5 | 0.5 |
| study | 0.0 | 0.0 |
| learn | 0.0 | 0.0 |
连接概率矩阵与语言模型
我们已经得到了概率矩阵,现在可以将其与本周概述中定义的语言模型连接起来。语言模型的核心任务是评估句子概率或预测序列中的下一个词。
评估句子概率:模型将句子分割成一系列N元语法,然后在概率矩阵中查找每个N元语法的条件概率,并将它们相乘。
例如,计算句子 “I learn” 的概率:
- 句子开始标记
<s>后接 “I” 的概率:P(I | <s>)(假设为1,简化计算)。 - “I” 后接 “learn” 的概率:从矩阵中查得
P(learn | I) = 0.5。 - “learn” 后接句子结束标记
</s>的概率:P(</s> | learn)(假设为1,简化计算)。 - 总概率 = 1 * 0.5 * 1 = 0.5。
预测下一个词:模型提取序列末尾的 (N-1) 元语法作为前缀,在概率矩阵中找到对应的行,并返回概率最高的单词。
处理数值下溢问题
在计算长句子的概率时,需要将许多介于0和1之间的小数相乘,这可能导致数值下溢——计算机难以精确存储极小的十进制数,从而产生误差。
解决这个问题的常用数学技巧是使用对数运算。因为对数的乘积等于其参数对数的和,我们可以将对数概率相加,而不是将原始概率相乘。
公式:log(P(sentence)) = log(P1 * P2 * ... * Pk) = log(P1) + log(P2) + ... + log(Pk)
这样,我们操作的是相对较大的对数概率值,有效避免了直接相乘导致的下溢问题。
应用:文本生成
语言模型的一个有趣应用是从头开始生成文本,或者根据一个小的提示进行生成。
以下是使用二元语法模型生成句子的算法步骤:
- 算法首先随机选择一个以句子开始标记
<s>开头的二元语法,选择概率由概率矩阵中的值决定(值越高,被选中的可能性越大)。 - 接着,算法从上一步选中的词的第二个词作为新的前缀,随机选择一个以此前缀开头的二元语法。
- 将这个新二元语法的第二个词添加到生成的句子中。
- 重复步骤2和3,直到选中的二元语法以句子结束标记
</s>结尾。
例如,生成过程可能如下:
- 起始:
<s> - 选择:
<s> I - 选择:
I study - 选择:
study .(假设 “.” 对应</s>) - 生成句子:
I study.
总结

本节课中我们一起学习了构建N元语法语言模型的完整流程:
- 构建计数矩阵:统计语料库中所有N元语法的出现次数。
- 计算概率矩阵:通过归一化计数矩阵得到每个N元语法的条件概率。
- 连接语言模型:使用概率矩阵评估句子概率或预测下一个词。
- 处理下溢问题:引入对数概率来避免连乘小数值时的计算误差。
- 文本生成应用:了解了如何利用概率矩阵通过随机选择来生成新的句子。
现在你已经知道如何从语料库中计算和获取概率来构建自己的语言模型了。下一节课,我们将学习如何评估这个模型的性能。
080:语言模型评估 📊
在本节课中,我们将学习如何评估一个语言模型的好坏。我们将介绍一个核心评估指标——困惑度,并详细解释其计算方法与意义。
数据集划分 📂

在之前的课程中,我们了解到语言模型可以为每个句子分配一个概率。模型在训练语料库上进行训练,因此对于训练句子,它可能会分配非常高的概率。为了客观评估模型,我们首先需要将语料库划分为训练集、验证集和测试集。
正如你在其他机器学习项目中所做的那样,你需要创建以下划分:
- 训练集:用于训练你的模型。
- 验证集:用于调整超参数等任务。
- 测试集:在最后阶段使用,用于测试一次并获得一个准确率分数,该分数反映了模型在未见数据上的表现。
对于较小的数据集,常用的划分比例是 80/10/10,即 80% 用于训练,10% 用于验证,10% 用于测试。在文本分析等非常大的数据集中,测试集可能只占训练集的 1%。
在自然语言处理中,主要有两种划分方法:
- 你可以通过选择较长的连续片段(如维基百科文章)来划分语料库。
- 或者,你可以随机选择较短的词序列,例如句子中的词序列。
理解困惑度 🤔
划分好数据集后,我们可以使用困惑度指标来评估测试集。
困惑度是语言建模中常用的一个指标。如果你熟悉“困惑”这个词,你会知道当一个人对非常复杂的事物感到困惑时,他就是“困惑的”。你可以将困惑度视为衡量文本样本复杂性的指标,即该文本有多复杂。
困惑度用于告诉我们一组句子看起来更像是人类写的,还是像一个简单程序随机选词生成的。人类书写的文本更可能具有较低的困惑度分数。另一方面,随机选词生成的文本则具有较高的困惑度。
计算困惑度 🧮
让我展示如何计算模型的困惑度。首先,计算测试集中所有句子的概率,然后将该概率取 -1/M 次幂。
公式:
困惑度 = (测试集的概率) ^ (-1/M)
其中,M 是测试集中的总词数。本质上,困惑度是测试集概率的倒数,并按测试集中的词数进行了归一化。
因此,语言模型对测试集概率的估计越高,困惑度就越低。值得一提的是,困惑度与衡量不确定性的熵密切相关。
让我们看一个例子,两个语言模型为你的测试集 W 返回不同的概率。
测试集中有 100 个词,所以 M = 100。
- 第一个模型 返回测试集的概率为
0.9,这个值非常高。这意味着第一个模型能很好地预测你的测试集,因此模型非常有效。如你所见,该模型和测试集的困惑度约为1,非常低。 - 第二个模型 为你的测试集返回一个非常低的概率:
10^(-250)。对于这个模型和测试集,困惑度约为316,远高于第一个模型。
需要记住的一点是:困惑度分数越小,句子听起来就越自然。作为参考,好的语言模型的困惑度分数通常在 20 到 60 之间,有时甚至更低。对于跟踪字符而非单词的字符级语言模型,困惑度会更低。
N元语法模型的困惑度计算
现在,我们准备为二元语法模型计算困惑度。在二元语法模型中,你计算所有句子的二元语法概率的乘积,然后取 -1/M 次幂。
公式:
困惑度 = (∏ P(bigram_i)) ^ (-1/M)
回想一下,概率的 -1/M 次幂等同于 1/概率 的 M 次方根。
这里需要注意的一点是:在测试集概率相同的情况下,集合大小 M 越大,最终的困惑度就越低。如果测试集中的所有句子都被连接起来,公式可以简化为整个集合中二元语法概率的乘积。
另一点需要注意的是,有些论文使用对数困惑度而不是困惑度。因此,困惑度公式从 (1/概率)的M次方根 变为 (1/M) * Σ log(词的概率)。这更容易计算,所以研究人员报告语言模型的对数困惑度并不少见。请注意,通常使用以 2 为底的对数。在一个困惑度介于 20 和 60 之间的好模型中,对数困惑度将在 4.3 和 5.9 之间。
困惑度在实际模型中的体现 🚀
那么,改进的困惑度如何体现在生产质量的语言模型中呢?这里有一个《华尔街日报》语料库的例子。
- 如果你采用一元语法语言模型,困惑度非常高,达到
962。它只是按词的概率生成词。 - 使用二元语法语言模型,生成的文本开始变得更有意义一些。
- 使用三元语法模型,你可以看到它产生的语言已经相当合理了。此时的困惑度等于
109,更接近20到60的目标困惑度范围。

正如我之前提到的,在本专项课程的后续部分,你将遇到困惑度分数更低的深度学习语言模型。
总结 ✨
本节课中,我们一起学习了语言模型的核心评估指标——困惑度。我们了解了如何划分数据集以进行公正的评估,掌握了困惑度的定义、计算方法及其实际意义。记住,较低的困惑度通常意味着模型生成的文本更自然、更像人类语言。
你现在已经理解了什么是困惑度以及如何评估语言模型。为了将它们用于实际任务,我们还需要能够处理在训练集中未出现过的词。我将在下一个视频中向你展示如何做到这一点。
081:31_词汇表外词处理 🆕

在本节课中,我们将要学习如何处理语言模型中未曾见过的词汇,即“词汇表外词”。理解并妥善处理这类词汇,对于构建一个健壮的语言模型至关重要。
什么是词汇表外词? 🤔
上一节我们介绍了语言模型的基本概念,本节中我们来看看一个常见的实际问题:词汇表外词。
首先,词汇表是您的语言模型所支持的一组独特单词的集合。在某些任务中,例如语音识别或问答系统,您只会遇到并生成来自一个固定单词集合的词汇。这个固定的单词列表也被称为封闭词汇表。
然而,使用固定单词集并不总是能满足任务需求。您常常需要处理以前从未见过的单词。这就导致了开放词汇表的出现。开放词汇表意味着您可能会遇到来自词汇表之外的单词,例如训练集中出现的一个新城市名称。这些未知单词也被称为词汇表外词,或简称为 OOV。
如何处理未知词:引入 UNK 标记 🛠️
处理未知词的一种方法是用一个特殊的标记 <UNK> 来建模它们。为此,您只需将每个未知词替换为 <UNK>。
以下是具体操作步骤:
您需要处理您的训练语料库,使其能泛化到未知词。首先,定义哪些单词属于词汇表。任何在训练语料库中但不在词汇表中的单词都将被替换为 <UNK>。
现在,您可以像以前一样应用 N-gram 语言模型的概率计算,只是增加了 <UNK> 标记。
这是一个如何创建词汇表并用 <UNK> 替换稀有词的示例:
这是您的输入训练语料库,您决定词汇表中的单词需要在语料库中至少出现两次。换句话说,单词的最小频率是 2。
这是将所有频率小于 2 的单词替换为 <UNK> 后的更新语料库。词汇表则是频率大于或等于 2 的单词集合。
当在输入查询上使用语言模型时,查询中任何在词汇表之外的单词也会被替换为 <UNK>。因此,正如您在此处看到的,概率计算应用于输入查询,其中 <UNK> 替换了词汇表外词 “aom”。
在本专项课程的后续部分,我将向您展示一些处理未知词的其他方法。例如,您可以使用深度学习将它们逐个字符地拼写出来。这些内容将在合适的时机介绍。
如何构建词汇表 📚
现在,我们来谈谈如何决定要将哪些单词包含在词汇表 V 中。
您可以根据不同的标准从训练语料库中创建词汇表。例如,您可以选择一个最小词频 F,这通常是一个较小的数字。在语料库中出现次数超过 F 次的单词将成为词汇表 V 的一部分。
然后,将所有其他不在词汇表中的单词替换为 <UNK>。这是一个简单的启发式方法,但它能确保您关心的、重复出现次数多的单词成为词汇表的一部分。
或者,您可以决定词汇表的最大规模,并只包含频率最高的单词,直到达到最大词汇表规模。
需要考虑的是 <UNK> 对困惑度的影响。它会使困惑度变低还是变高?实际上,它通常会变低。
所以看起来您的语言模型似乎变得越来越好。但请注意,您可能只是有很多 <UNK>。模型可能会以高概率生成一系列 <UNK> 标记,而不是有意义的句子。
由于这个限制,我建议谨慎使用 <UNK>。
最后,在使用困惑度指标时,请记住只比较具有相同词汇表的语言模型。
总结 📝

本节课中我们一起学习了如何处理语言模型中的词汇表外词。我们首先定义了封闭词汇表和开放词汇表的区别,并引入了 OOV 的概念。接着,我们学习了通过引入特殊标记 <UNK> 来建模未知词,并详细说明了如何在训练和推理阶段应用此方法。我们还探讨了基于最小词频或最大词汇表规模来构建词汇表的策略,并提醒了过度使用 <UNK> 可能带来的问题以及比较模型时保持词汇表一致的重要性。
现在您已经知道如何处理词汇表外词。接下来,我将向您展示一种提高模型在稀有词上性能的方法。
082:平滑技术 🧮

在本节课中,我们将学习如何解决N-gram语言模型中因语料库有限而导致的概率估计偏差问题。我们将重点介绍一种名为“平滑”的技术,它能有效处理那些在语料库中未出现过的N-gram,使其获得非零概率,从而提升模型的鲁棒性。
1. 缺失N-gram带来的问题
上一节我们介绍了如何处理完全未知的词汇。本节中,我们来看看另一种信息缺失的情况:如何处理那些由语料库中存在的单词组成,但其组合形式(即N-gram本身)却未在语料库中出现的情况。
例如,假设我们有一个由三个句子组成的语料库,其中包含“eat chocolate”、“John drinks”等二元组。请注意,单词“John”和“eats”都存在于语料库中,但二元组“John eats”却缺失了。因此,该二元组的计数为0,其概率也为0。这意味着任何未在语料库中出现过的组合都会被模型视为不可能发生。在这种情况下,仅基于计数的概率估计方法将失效。
2. 平滑技术简介
平滑是一种可以帮助你处理N-gram模型中此类情况的技术。你可能还记得在前几周的课程中,平滑被用于词性标注的转移矩阵和概率计算。这里,你将把这种方法应用于N-gram概率估计。那么,平滑究竟是什么意思呢?
让我们先聚焦于“加一平滑”,它也被称为拉普拉斯平滑。加一平滑在数学上改变了基于历史计算单词 w_n 的N-gram概率公式。
以下是给定前一个词 w_{n-1} 时,单词 w_n 的二元组概率公式(其思想可推广到一般的N-gram):
P(w_n | w_{n-1}) = (count(w_{n-1}, w_n) + 1) / (sum_{w'}(count(w_{n-1}, w') + 1))
加一平滑简单地说就是:让我们在分子上加1,同时在分母的每个可能二元组计数上也加1。这种变化可以理解为给每个二元组都增加一次出现次数。因此,那些在语料库中缺失的二元组现在在分母中会有一个非零的概率项(你为每个以 w_{n-1} 开头的可能二元组都加了1)。
这相当于在计数矩阵中,为索引为单词 w_{n-1} 的那一行的每个单元格都加1,然后对词汇表中的每个单词重复此操作。你可以把加的那个“1”从求和符号中提出来,将词汇表的大小加到分母上。
不过,这种方法仅在语料库的真实计数足够大,足以抵消所加的“1”时才有效。否则,缺失词的概率会被估计得过高。但加一平滑的帮助很大,因为它消除了概率为零的二元组。
3. 其他平滑与回退方法
如果你有一个更大的语料库,可以使用加 k 平滑(也称为加 k 平滑)。这种技术能使概率分布更加平滑。
其公式与加一平滑类似,只需在分子上加 k,并在分母的每个可能N-gram上加 k,其中分母的总和会增加 k 乘以词汇表大小。加 k 平滑也可以应用于更高阶的N-gram概率,如三元组、四元组等。
以下是更高级的平滑方法:
- 克尼瑟-内伊平滑或古德-图灵估计:这些是更复杂的平滑技术。
- 回退法:另一种处理语料库中未出现N-gram的方法是使用低阶N-gram(如
n-1gram、n-2gram等)的信息。如果ngram信息缺失,就使用n-1gram的信息;如果n-1gram也缺失,就使用n-2gram,依此类推,直到使用低阶N-gram(如n-1gram、n-2gram,直至一元组)找到非零概率。这会扭曲概率分布,尤其是在小型语料库上。需要从高阶N-gram中“折扣”一部分概率,用于低阶N-gram。这种经典的回退方法就使用了折扣技术。 - 愚蠢回退:在非常大的网络规模语料库上,一种名为“愚蠢回退”的方法被证明是有效的。在愚蠢回退中,不应用概率折扣。如果高阶N-gram概率缺失,就直接使用低阶N-gram概率乘以一个常数(实验表明常数约等于0.4时效果很好)。
让我们看一个回退的例子。观察以下语料库,三元组“drink chocolate”的概率无法直接从语料库中估计。因此,将使用二元组“drinks chocolate”的概率乘以一个常数(在你的场景中是0.4)来代替。
回退法的一种替代方法是使用所有阶数N-gram的线性插值。这意味着你总是组合加权后的 n gram、n-1 gram直至一元组的概率。
例如,在计算三元组“John drinks chocolate”的概率时,你可以取:
- 70% 的三元组估计概率(即
P(“John drinks chocolate”)) - 加上 20% 的二元组估计概率(即
P(“drinks chocolate”)) - 再加上 10% 的一元组估计概率(即
P(“chocolate”))
更一般地,对于三元组,你会组合三元组、二元组和一元组的加权概率。你用常数(如 λ1、λ2、λ3)来权衡所有这些概率,这些常数之和需要为1。λ 值是从语料库的验证部分学习得到的。你可以通过最大化验证集中句子的概率来获得它们。使用从训练部分语料库训练的固定语言模型来计算N-gram概率,并优化 λ 值。通过使用更多的 λ 参数,插值法可以应用于一般的N-gram。

4. 总结
本节课中,我们一起学习了N-gram语言模型中的平滑技术。你现在已经是N-gram语言模型的专家了,你知道了如何创建它们,如何处理词汇表外单词,以及如何通过平滑技术来改进模型。你将在编程练习中看到它们运行得非常好,在那里你将编写第一个生成文本的程序。
083:第33周总结 📚

在本节课中,我们将快速回顾本周学习的所有核心概念,并重点总结主要知识点。
概述 📋
本周我们学习了N-gram语言模型,包括如何计算概率以及如何处理文本。接下来,我们将更详细地回顾这些内容。
本周学习回顾 🔄
让我们花点时间回顾一下本周完成的所有内容。
你从N-gram的定义开始,学习了如何从语料库中计算它们的概率。
然后,你结合N-gram概率来近似计算一个句子的概率。利用语料库中所有N-gram的信息,你构建了一个语言模型。
这个模型是一个工具,用于估计任何句子的概率。
你还解决了句子中某些部分信息缺失的情况。
你通过几种方式处理了这个问题:通过改造模型以处理词汇表外的单词(使用特殊标记),以及应用多种方法来处理语料库中缺失的N-gram,例如平滑、回退和插值。
最后,你掌握了一个工具来帮助你为任务选择最佳的语言模型,即困惑度指标。
现在,你已经准备好迎接你的作业——句子自动补全。
本周你完成了出色的工作,希望你享受这个过程。祝你的作业顺利,我们下次再见。😊

总结与展望 🌟
你已经走了很长的路,本周学习了许多重要的概念。N-gram是基础性的,当你后续在本专项课程中学习更技术性的模型时,它能让你有更好的理解。
编程作业将巩固你对这个概念的理解。
祝你好运,我们下周见。😊
086:第36课 词向量概述 🧠📚

在本节课中,我们将要学习词向量的核心概念、应用以及如何从零开始训练它们。词向量是大多数自然语言处理应用的基础,掌握它们对于深入理解NLP至关重要。
词向量的应用场景 🎯
上一节我们介绍了课程目标,本节中我们来看看词向量的具体应用。
词向量,也称为词嵌入,是大多数消费级和企业级NLP应用的基石。如果你完成了NLP专项课程的一部分,你可能记得曾使用预生成的词向量来寻找词语间的语义类比并计算词语的相似度。
以下是词向量的一些主要应用:
- 情感分析与文本分类:你可以将词向量与分类器结合,进行情感分析,或对客户评论、用户反馈调查中的评论进行分类。
- 机器翻译:更高级的词向量用例包括构建机器翻译系统。
- 信息抽取与问答系统:词向量也广泛应用于信息抽取和问答系统。
这些是你本周的学习目标。
本周学习目标 🎯
了解了词向量的广泛应用后,我们来明确本周需要掌握的具体技能。
- 理解词表示的核心概念:你将学习如何用数字表示词语,以便它们能与数学模型一起使用。我还会介绍词嵌入以及用数字表示词语的优势。
- 生成词向量:我将展示模型从数据中学习词向量的通用方法,并举例说明一些常用方法。
- 为机器学习准备文本:你将学习如何将文本语料库转换为机器学习模型的训练集。我会提供实用的建议,这些建议可应用于从书籍到推文等各种现实语料库。
- 实现连续词袋模型:你将实现连续词袋模型,这是创建词向量的众多方法之一。这是一个简单高效的方法,最初推动了词向量的普及。
当然,还有其他技术如GloVe等也可用于训练词向量。但本周我们将重点学习连续词袋模型。
预备知识与课程安排 📖
在深入具体模型之前,我们需要确保具备必要的基础知识。
如果你不熟悉神经网络,我强烈建议你先学习DeepLearning.AI的深度学习专项课程的第一门课。如果你熟悉神经网络但有一段时间未使用,也不必担心,我将在本周的课程中进行复习。

总结 ✨
本节课中我们一起学习了词向量的重要性和广泛的应用场景,明确了本周的学习目标,包括理解词表示、生成词向量、准备文本数据以及实现关键的连续词袋模型。我们已经看到了本周结束时你将能完成的所有很酷的事情,让我们在接下来的视频中开始学习吧。
087:基本词表示 🧠

在本节课中,我们将学习如何用数字来表示词汇表中的单词。这是自然语言处理的基础步骤。我们将从最简单的方法开始,了解其优缺点,并引出更高级的表示方法。
概述
你将能够创建一个矩阵来表示词汇表中的所有单词。矩阵中的每个向量将对应一个单词。接下来,我将展示如何构建这个矩阵。
从整数到独热向量
表示单词最简单的方法是,为给定词汇表中的每个单词分配一个唯一的整数。
例如,基于一个包含1000个基本英语单词的词汇表,你可以将数字1分配给单词“a”,数字2分配给“able”,以此类推,直到将数字1000分配给“zebra”。
虽然这种编号方案很简单,但问题之一是单词的顺序(在这个例子中是字母顺序)从语义角度来看没有太大意义。
例如,没有理由认为“happy”应该被分配一个比“hand”更大的数字,或者比“zebra”更小的数字。
独热向量表示法
不使用数字索引来编码每个单词,你可以使用列向量来表示单词,其中每个元素对应词汇表中的一个单词。
使用前面1000个单词的词汇表例子,每个向量将包含1000个元素,每一行都用一个单词标记。
现在,你可以通过在与单词标签相同的行中放置一个1,在其他所有位置放置0,来用唯一的列向量编码每个单词。
例如,单词“happy”将被表示为一个向量,在对应“happy”的行中为1,在所有其他行中为0。
对于词汇表中的所有其他单词也是如此。我将这些向量称为独热向量,以区别于你将遇到的其他类型的向量。
你可以将单词视为一个分类变量。通过简单地将行中的单词映射到其对应的行号,你可以轻松地在整数和独热向量之间进行转换。
这样,第621号的单词“happy”将被表示为一个在621行(对应单词“happy”)为1的独热向量。反之,在621行有一个1的向量“H”将由整数621表示,对应“happy”。
独热向量的优缺点
独热向量相对于使用整数有一个优势,因为独热向量不暗示任何两个单词之间的关系。每个向量只说明单词是“happy”或者不是,单词是“zebra”或者不是。
然而,对于大多数自然语言处理用例来说,独热向量有两个主要限制。
以下是其局限性:
- 向量维度巨大:除了最简单的词汇表,这些稀疏向量将非常庞大。这意味着在计算机上处理独热向量需要大量的空间和处理时间。如果你的向量是用英语单词创建的,你最终可能会有超过一百万行,英语词汇表中的每个单词对应一行。
- 无法编码语义:这种表示法不携带单词的含义。例如,如果你试图通过计算两个独热向量之间的距离来确定两个单词的相似程度,那么你总是会得到任意两个单词对之间相同的距离。例如,使用独热向量,“happy”与“paper”的相似度,和它与“excited”的相似度是一样的。直观上,你会认为“happy”与“excited”比与“paper”更相似。
总结与过渡

在本节中,我们介绍了独热向量,它非常简单且不隐含顺序。我们也看到了独热向量的缺点,即维度巨大且不编码任何语义。
这正是词嵌入发挥作用的地方,我将在下一个视频中解释。词嵌入将帮助我们解决这些问题。
088:吴恩达《自然语言处理》P88 - 词嵌入 🧠

在本节课中,我们将学习词嵌入的概念。这是一种将单词表示为低维向量的方法,这些向量不仅能高效计算,还能捕捉单词的语义含义。我们将了解词嵌入如何超越独热编码,并成为构建复杂自然语言处理应用的基础。
从独热编码到词嵌入
上一节我们介绍了如何将单词转换为整数和独热向量。独热向量虽然简单,但维度极高且无法表达单词间的任何关系。本节中,我们来看看如何用更紧凑的向量来编码单词的含义。
我即将展示一种方法,它可以将含义编码在维度相对较低的向量中。换句话说,这个向量的维度不一定是词汇表大小 V 那么高。
一维词嵌入:编码情感
我们可以从一个简单的维度开始。想象一条水平的数轴,左边的词在某种意义上被认为是负面的,右边的词被认为是正面的。更负面的词在更左边,更正面的词在更右边。
你可以将它们在数轴上的位置存储为一个长度为1的向量中的数字。注意,你现在可以使用任何十进制数值,而不仅仅是0和1。这非常有用。
现在,你可以说“happy”(快乐)和“excited”(兴奋)彼此之间比与单词“paper”(纸)更相似,因为代表“happy”的数字更接近代表“excited”的数字。
公式表示:word_vector = [sentiment_score],其中 sentiment_score 是一个实数。
二维词嵌入:增加具体性维度
你可以通过添加一条垂直的数轴来扩展这个概念。在这条线上位置更高的词是更具体的物理对象,而位置更低的词是更抽象的概念。
同样,你可以根据这两个数轴来排列每个单词,并看到那些更具体且更积极的词,比如“puppy”(小狗)和“kitten”(小猫),在空间中更接近。
你可以将代表在两个数轴上位置的两个数字存储为一个长度为2的向量。
公式表示:word_vector = [sentiment_score, concreteness_score]
词嵌入的定义与优势
你刚才创建的就是词嵌入的一个例子。词嵌入以向量形式表示单词,这种形式维度相对较低(通常是数百到数千维),便于计算,并且承载了单词的含义,使得判断通用词汇表中单词的语义相似度成为可能。
例如,“forest”(森林)的向量通常与“tree”(树)的向量相似,但与“ticket”(票)的向量非常不同。你将在本周的作业中可视化这种相似性。
它还可以用于推理类比,例如找出“Paris(巴黎)之于France(法国),如同Rome(罗马)之于____”中缺失的单词。
词嵌入的重要性
对单词含义进行编码,也是对整个句子含义进行编码的第一步,这是构建更复杂NLP应用(如问答和翻译)的基础。创建词嵌入是本周课程模块的主要目标之一。
在本周的讲座中,你将实现从简单到更高级方法的词嵌入。你也将开始基于这些更简单的表示进行构建。
术语说明
理论上,所有单词的向量表示(包括独热向量和词嵌入向量)都被称为词向量。但术语“词向量”和“词嵌入”也常用来特指词嵌入向量。所以,如果你在其他地方看到这些术语,不必惊讶。

总结与过渡
本节课中,我们一起学习了词嵌入的基本思想。我们看到了如何用一个二维向量来告诉你关于单词的一些信息:也许第一个坐标告诉你单词是正面还是负面的,第二个坐标告诉你它是抽象的还是具体的。你拥有的坐标维度越多,能捕捉的信息就越多。
在下一个视频中,我将向你展示如何学习这些坐标。
089:如何创建词嵌入 🧠

在本节课中,我们将学习如何创建词嵌入。词嵌入是自然语言处理中的核心概念,它能够将单词转换为计算机可以理解的数值向量。我们将探讨创建词嵌入所需的基本要素、过程及其重要性。
创建词嵌入的两个要素
要创建词嵌入,始终需要两个要素:文本语料库和嵌入方法。
文本语料库包含了你想要嵌入的单词,这些单词的组织方式应与它们在目标上下文中的使用方式一致。例如,如果你想基于莎士比亚的作品生成词嵌入,那么你的语料库就应该是莎士比亚的完整原始文本,而不是学习笔记、幻灯片或关键词列表。
单词的上下文指的是在该特定单词周围通常会出现哪些其他单词或单词组合。上下文非常重要,因为它赋予每个词嵌入以意义。一个简单的莎士比亚常用词汇列表不足以创建词嵌入。
语料库可以是一般用途的文档集合,例如维基百科文章;也可以是更专业化的,例如特定行业或企业的语料库,以捕捉该领域的细微差别。对于法律主题的自然语言处理用例,你可以使用合同和法律书籍作为语料库。
嵌入方法负责从语料库中创建词嵌入。存在许多可能的方法类型,但在本课程中,我将重点介绍基于机器学习模型的现代方法,这些模型被设定为学习词嵌入。
机器学习模型与自监督学习
机器学习模型执行一项学习任务,而这项任务的主要副产品就是词嵌入。例如,任务可以是学习根据语料库句子中的周围单词来预测一个单词,就像我将在接下来的视频中描述的连续词袋方法一样,你将在本周的作业中实现它。
任务的具体细节最终将定义单个单词的含义。我将在后续的一个视频中再次讨论这一点。
这个任务被称为自监督学习。说它是无监督的,是因为输入数据(语料库)是未标记的;说它是有监督的,是因为数据本身提供了必要的上下文,这通常构成了标签。因此,语料库是一个自包含的数据集,既包含训练数据,也包含能够监督任务的数据。
超参数与向量维度
词嵌入可以通过一系列超参数进行调整,就像任何机器学习模型一样。其中一个超参数是词嵌入向量的维度。在实践中,这个维度通常从几百到几千不等。
使用更高的维度可以捕捉更细微的含义,但在计算上更加昂贵,无论是在训练时还是在后续使用词嵌入向量时。这最终会导致收益递减。
语料库的数学表示
最后,为了将语料库输入到机器学习模型中,必须首先将语料库的内容从单词转换为合适的数学表示,即转换为数字。这种表示取决于模型的具体细节,但通常基于我在上一个视频中介绍的简单表示,例如基于整数的单词索引或独热向量。
总结
在本节课中,我们一起学习了创建上下文词嵌入的高级过程。我们了解到,这需要文本语料库和嵌入方法两个核心要素。模型通过自监督学习任务来生成词嵌入,而超参数如向量维度会影响嵌入的质量和计算成本。语料库需要先转换为数学表示才能被模型处理。

下一步,我将向你介绍几种词嵌入方法,包括你将在本周作业中实现的连续词袋方法。到目前为止,我们学习了一个新术语——自监督学习。这个术语将在机器学习领域反复出现,它是无监督学习和有监督学习的结合。现在,在下一个视频中,我们将看到一些用于词嵌入的方法。
090:自然语言处理 | 词嵌入方法概览 (P90) 🧠

概述
在本节课中,我们将学习多种词嵌入方法。词嵌入是自然语言处理中的核心技术,它将词语转换为计算机能够理解的数值向量。随着时间推移,科学家们不断发现更好的方法,以捕捉词语更丰富的含义。我们将从基础方法开始,逐步介绍到更先进的模型。
基础词嵌入方法
上一节我们了解了词嵌入的基本概念,本节中我们来看看几种经典且实用的基础词嵌入方法。
以下是几种重要的基础词嵌入方法:
-
Word2Vec:该方法使用浅层神经网络来学习词嵌入。它提出了两种模型架构。
- 连续词袋模型:这是一种简单但高效的方法,你将在本周的作业中实现它。该模型的目标是学习根据周围的词语来预测缺失的词语。
- 连续跳字模型:也称为带负采样的跳字模型,它与连续词袋模型的做法相反。该模型学习根据给定的输入词来预测其周围的词语。
-
GloVe:由斯坦福大学提出,全称为“全局向量”。它通过对语料库的词语共现矩阵的对数进行矩阵分解来生成词嵌入,这与你之前使用过的计数矩阵类似。
-
FastText:由Facebook提出,基于跳字模型,并通过将词语表示为字符的n-gram序列来考虑词语的内部结构。这使得模型能够支持未见过的词语(即词汇表外词语),方法是通过其组成的字符序列以及模型初始训练时见过的类似序列来推断其嵌入向量。例如,即使模型从未见过“kitty”这个词,它也能为“kitty”和“kitten”创建相似的嵌入向量,因为这两个词由相似的字符序列组成。FastText的另一个好处是,词嵌入向量可以平均在一起,以构成短语和句子的向量表示。
高级上下文相关模型
在之前介绍的基础模型中,一个给定的词总是具有相同的嵌入向量。然而,词语的含义常常依赖于上下文。接下来,我们将探讨一些更复杂的建模方法。
这些方法使用先进的深度神经网络架构,根据词语的上下文来细化其含义的表示。在这些更高级的模型中,词语根据其上下文拥有不同的嵌入向量。这支持了对多义词或具有相似含义词语的更好处理,例如“plants”这个词,既可以指像花这样的生物体,也可以指工厂,或者作为副词具有更多不同的含义。
以下是几个生成上下文相关词嵌入的高级模型示例:
- BERT:谷歌提出的“来自Transformer的双向编码器表示”。
- ELMo:艾伦人工智能研究所提出的“来自语言模型的嵌入”。
- GPT-2:OpenAI提出的“生成式预训练转换器2”。
如果你想使用这些高级方法,可以在互联网上找到现成的预训练模型,并且可以使用你自己的语料库对这些模型进行微调,以生成高质量的、特定领域的词嵌入。
总结

本节课中,我们一起学习了词嵌入方法的发展历程。你现在掌握了一些创建词嵌入的工具,从基础的Word2Vec、GloVe、FastText,到能够编码更复杂语义信息的最新Transformer模型。这非常棒!接下来,我将介绍你将在本周作业中使用的连续词袋模型。
你已经看到了词嵌入方法的一段简史。我们从Word2Vec一路走到了最新的Transformer方法。目前,Transformer是最先进的人工智能方法之一。
091:连续词袋模型 (CBOW) 🧠

在本节课中,我们将学习连续词袋模型(CBOW)的基本原理。这是一种用于生成词嵌入的机器学习模型,其核心思想是通过上下文单词来预测中心单词。我们将了解其整体算法结构、如何从语料库创建训练数据,以及模型背后的基本逻辑。
模型概述与目标 🎯
上一节我们介绍了词嵌入的基本概念,本节中我们来看看一种具体的实现模型——连续词袋模型。
该模型的目标是:根据给定的上下文单词,预测缺失的中心单词。其背后的原理是,如果两个不同的单词在各种句子中经常被一组相似的单词所包围,那么这两个单词在含义上往往是相关的,或者说它们在语义上是相关的。
例如,在句子 “The little something is barking” 中,如果语料库足够大,模型将学会预测这个缺失的单词是与“狗”相关的词,比如 “dog”、“puppy”、“hound” 或 “terrier” 等。因此,模型最终将根据上下文来学习单词的含义。
从语料库到训练数据 📊
了解了模型目标后,我们来看看如何将原始文本语料库转化为模型可以处理的训练数据。
假设我们的语料库是句子:“I am happy because I am learning.”(暂时忽略标点)。对于语料库中的某个单词(例如 “happy”,我们称之为中心词),我们将其上下文单词定义为:中心词前后的各两个单词。这里,我们记 c = 2,表示前后各取两个单词。c 被称为上下文的半长,是模型的一个超参数,你也可以选择其他数量的单词,这里只是一个例子。
因此,对于中心词 “happy”,其上下文单词是:[“I”, “am”, “because”, “I”]。我们定义窗口为中心词加上其上下文单词的总和。这里,窗口大小 = 1个中心词 + 2个前文词 + 2个后文词 = 5。
为了训练模型,我们需要一组训练样本,每个样本由上下文单词和需要预测的中心词组成。
以下是创建训练样本的过程:
以下是基于示例句子生成训练样本的步骤:
- 第一个窗口是
[“I”, “am”, “happy”, “because”, “I”]。模型将接收上下文单词[“I”, “am”, “because”, “I”]作为输入,并应预测中心词“happy”。 - 将窗口滑动一个单词,下一个训练样本的窗口是
[“am”, “happy”, “because”, “I”, “am”]。模型的输入是上下文单词[“am”, “happy”, “I”, “am”],目标中心词是“because”。 - 再次滑动窗口,模型将接收
[“happy”, “because”, “I”, “am”, “learning”]中的上下文单词,并应预测目标中心词“I”。
这基本上就是连续词袋模型创建训练数据的方式。正如在原论文的模型架构图中所示,上下文单词是输入,中心单词是输出。
总结与回顾 📝
本节课中我们一起学习了连续词袋模型(CBOW)的运作方式。
核心公式/逻辑:
- 输入:上下文单词(例如,
[“I”, “am”, “because”, “I”]) - 输出/预测目标:中心单词(例如,
“happy”) - 目标函数:最大化给定上下文条件下中心词的概率。

简单来说,在CBOW模型中,你尝试使用上下文单词(周围的单词)来预测中心单词,而作为该算法的副产品,你最终会得到我们想要的词向量(词嵌入)。
在接下来的视频中,我们将深入探讨这一过程在数学和代码上是如何具体实现的。
092:清洗与分词 🧹➡️🔡
在本节课中,我们将学习如何为自然语言处理任务准备文本数据。具体来说,我们将探讨清洗与分词这两个关键步骤,它们是将原始文本转化为模型可理解形式的基础。
概述
我们将探索清洗与分词。我在第一门课程中已经简要提及过这个话题,但在此仍有必要再次深入讨论。让我们开始吧。
我会提供一些实用建议,指导你如何清洗一个语料库,并通过一个称为分词的过程,将其拆分为单词,或者更准确地说,拆分为词元。
在第一门课程中,我先前讨论过数据准备、清洗和分词,但现在我将更详细地展开说明。
清洗语料库的实用建议

以下是处理文本数据时需要考虑的几个关键方面。
1. 大小写处理
首先,你应该将语料库中的单词视为大小写不敏感。例如,单词 “the” 无论其大小写形式如何,都应被统一表示。你可以通过将整个语料库转换为全小写或全大写来实现这一点。
代码示例:
corpus_lower = original_corpus.lower()
2. 处理标点符号
其次,你需要处理标点符号。
- 你可以将所有表示句子中断的标点符号(如句号、逗号、问号)表示为一个特殊的词元,例如
<PUNCT>。 - 你可以忽略非中断性的标点符号,如引号。
- 你可以将多个连续的标点符号(如三个问号)合并为一个。
3. 处理数字
接下来,你需要处理数字。
- 如果数字在你的应用场景中不承载重要含义,你可以删除所有数字。
- 然而,数字可能具有与你用例相关的重大意义。例如,“3.14” 是圆周率π,“90210” 是一个电视剧名,也是加州比弗利山庄的电话区号。在这种情况下,你可以将这些数字按原样保留在语料库中。
- 如果你的语料库包含许多独特的数字(如许多区号),你可能会发现将所有数字替换为一个特殊词元(如
<NUMBER>)更有意义。这能让模型知道重要的是“这是一个数字”,而不是试图区分“90210”和其他区号或电话号码。
4. 处理特殊字符
还需要处理特殊字符,例如数学符号、货币符号、章节段落标记、行尾标记等。通常,删除这些字符是安全的做法。
5. 处理特殊词汇
最后,特别是如果你在处理现代用户输入的语料库(如推文或消费者评论),你应该处理特殊词汇,例如表情符号和话题标签(如 #NLP)。这取决于你是否希望以及如何让你的模型理解它们的含义。例如,你可以考虑将每个表情符号或话题标签视为一个独立的词元。
分词实践示例
上一节我们介绍了清洗语料库的通用原则,本节中我们来看看一个具体的代码示例,演示如何实现其中的部分建议。
以下是一个使用Python的基本示例。本例中的语料是:"Who loves word embeddings in 2020? I do! 😊",它包含一个表情符号、标点符号和一个数字。
以下是导入和初始化所需库的代码:
import nltk
import re
import emoji
from nltk.tokenize import word_tokenize
nltk.download('punkt') # 下载分词所需的数据
我使用了流行的NLTK库来执行分词。它有一个名为 punkt 的智能分词模块,可以处理常见的标点符号用法。例如,它知道缩写和中间名中的句点并不表示句子结束。我还使用了 emoji 库,以便向你展示如何处理表情符号。
接下来是实际的逻辑代码:
# 原始语料
corpus = "Who loves word embeddings in 2020? I do! 😊"
# 1. 将所有中断性标点替换为句号
corpus_cleaned = re.sub(r'[!?]+', '.', corpus)
# 结果: "Who loves word embeddings in 2020. I do. 😊"
# 2. 使用NLTK分词
tokens = word_tokenize(corpus_cleaned)
# 分词结果: ['Who', 'loves', 'word', 'embeddings', 'in', '2020', '.', 'I', 'do', '.', '😊']
# 3. 筛选并转换词元:保留字母、句号、表情符号,并转为小写
filtered_tokens = [
token.lower() for token in tokens
if token.isalpha() or token == '.' or token in emoji.EMOJI_DATA
]
# 最终结果: ['who', 'loves', 'word', 'embeddings', 'in', 'i', 'do', '.', '😊']
如你所见,标点符号(包括引号)已被分离为独立的词元。最后,通过列表推导式,我保留了词元并将其转换为小写,条件是它们属于以下类别之一:字母字符、句号(即之前的中断性标点)、表情符号。这样就移除了像“2020”这样的数字和未知的特殊字符。最终得到的数组是:['who', 'loves', 'word', 'embeddings', 'in', 'i', 'do', '.', '😊']。
现在,你可以使用这个数组来提取中心词及其周围的上下文词,这将是下一部分详细讨论的内容。

总结
本节课中我们一起学习了自然语言处理中数据预处理的两个核心步骤:清洗与分词。我们讨论了统一大小写、处理标点符号和数字、过滤特殊字符以及保留表情符号等现代词汇的策略,并通过一个Python示例演示了如何利用NLTK库实现基本的分词流程。经过这些步骤,杂乱的原始文本被转化为了规整的词元序列,为后续构建词嵌入模型(如连续词袋模型)做好了准备。
在了解了清洗与分词之后,我们已经准备好继续学习连续词袋模型的其他部分。在下一个视频中,我们将探索滑动窗口,你可以将其想象为一个在文本语料库上滑动的窗口。我们将在那里详细讨论它。下次见。😊
093:Python中的滑动窗口 🪟

在本节课中,我们将学习如何从语料库中提取中心词和上下文词,这是训练连续词袋模型的关键步骤。我们将通过Python代码实现一个滑动窗口函数来完成这一任务。

上一节我们介绍了训练连续词袋模型需要中心词和上下文词。本节中,我们来看看如何从已处理的语料库中实际获取这些词。
假设你已经清洗并分词了一个语料库,现在得到一个由单词或标记组成的数组。以下是如何从中提取中心词及其上下文词,这些将作为训练模型的样本。
以下是实现此功能的Python代码。
def get_windows(words, C):
i = C
while i < len(words) - C:
center_word = words[i]
context_words = words[i-C:i] + words[i+1:i+C+1]
yield context_words, center_word
i += 1
get_windows 函数接收两个参数:
words:一个单词或标记的数组。C:存储在变量C中的上下文半宽,即中心词每侧要选取的单词数量。在上一个视频中,这个值是2,因此总窗口大小为5。
函数初始化一个计数器,其值为第一个前面有足够多单词的词的索引。例如,在句子“I am happy because I am learning”的分词数组中,C=2。第一个可用的中心词是“happy”,其索引为2,正好等于 C 的值。
接着,函数开始一个循环,从该索引运行,直到最后一个可能的中心词(即后面还有两个词的词)为止。循环的停止条件是索引小于数组长度减去 C。
在循环的每次迭代中:
- 提取中心词,即当前索引处的单词。
- 创建一个数组,包含中心词前的
C个单词和中心词后的C个单词。 - 返回上下文词和中心词。
这里使用了Python中的 yield 关键字而非 return。简单来说,return 会立即退出函数,而 yield 会返回一个值并暂停函数执行,这个函数被称为生成器函数。当需要更多值时,函数会从暂停处继续运行。使用 yield 可以多次从函数返回值,这正是我们在 while 循环每次迭代中所做的。
最后,将索引加1,使滑动窗口向右移动一个单词。
总结一下,get_windows 函数接收一个语料库和上下文大小,并为每个连续的窗口返回上下文词和中心词。
以下是使用此函数的方法。
# 示例用法
sentence = ["I", "am", "happy", "because", "I", "am", "learning"]
context_size = 2
for context_words, center_word in get_windows(sentence, context_size):
X = context_words # 特征(上下文词)
y = center_word # 目标(中心词)
print(f"Context: {X}, Center: {y}")
我们使用一个循环来获取连续的上下文词和中心词元组,并分别赋给 X 和 y。这里采用了机器学习中常见的特征和目标表示法,因为对于连续词袋模型,上下文词是特征,中心词是目标。
如果在数组 ["I", "am", "happy", "because", "I", "am", "learning"] 上运行此代码,并设置上下文半宽为2,将得到如下输出。

接下来,你将把这组单词转换为一组可以被连续词袋模型消费的向量。
本节课中,我们一起学习了如何在Python中使用滑动窗口提取中心词和上下文词。这对于编程练习非常有用。你还了解了Python中 yield 功能的使用,它通常用于数据生成器,或者可以将其视为能持续以小批量提供数据的函数。
094:44_将词转换为向量 📝

在本节课中,我们将学习如何将文本数据中的中心词和上下文词转换为数学模型可以处理的向量形式。这是训练连续词袋模型的关键准备步骤。
概述
上一节我们介绍了如何从语料库中提取中心词和上下文词。本节中,我们来看看如何将这些词语转换为数学向量,以便输入到连续词袋模型中。
创建词汇表
首先,你需要为语料库创建一个词汇表。词汇表是语料库中所有唯一词的集合。
以例句“I am happy because I am learning”为例,其词汇表包含五个词:am、because、happy、I、learning。
将词编码为独热向量

接下来,你可以将词汇表中的每个词编码为一个列独热向量。向量的每一行对应词汇表中的一个词。
以下是创建独热向量的方法:
- 词汇表按字母顺序排列(其他顺序亦可)。
- 每个词的向量在其对应行位置为1,其余位置为0。
例如,对于上述五词词汇表:
am的向量为[1, 0, 0, 0, 0]^Tbecause的向量为[0, 1, 0, 0, 0]^Thappy的向量为[0, 0, 1, 0, 0]^TI的向量为[0, 0, 0, 1, 0]^Tlearning的向量为[0, 0, 0, 0, 1]^T
中心词将以这种独热向量的形式输入模型。
创建上下文向量
对于上下文词,你需要创建一个单一的向量来代表整个上下文窗口。
以下是创建上下文向量的步骤:
- 将每个上下文词转换为对应的独热向量。
- 计算这些独热向量的平均值。
例如,假设上下文词为 I, am, because, I。
I的独热向量:[0, 0, 0, 1, 0]^Tam的独热向量:[1, 0, 0, 0, 0]^Tbecause的独热向量:[0, 1, 0, 0, 0]^T- 另一个
I的独热向量:[0, 0, 0, 1, 0]^T
计算平均值:
( [0,0,0,1,0]^T + [1,0,0,0,0]^T + [0,1,0,0,0]^T + [0,0,0,1,0]^T ) / 4 = [0.25, 0.25, 0, 0.5, 0]^T
得到的向量 [0.25, 0.25, 0, 0.5, 0]^T 就是用于模型的上下文表示。
训练数据示例
对于第一个滑动窗口 “I am happy because I”,最终用于训练连续词袋模型的向量表示如下:
- 上下文向量:
[0.25, 0.25, 0, 0.5, 0]^T(代表 “I, am, because, I”) - 中心词向量:
[0, 0, 1, 0, 0]^T(代表 “happy”)
语料库中其余的训练数据也按此方法处理。

总结
本节课中我们一起学习了如何将词语转换为向量。我们首先从语料库创建词汇表,然后将中心词编码为独热向量,最后通过计算平均值将一组上下文词合并为一个上下文向量。至此,你已经完成了从原始语料库到模型可处理数据的全部准备工作。

既然中心词和上下文词都已完全表示为向量,接下来我们就可以深入探讨模型本身了。在下一个视频中,我们将学习连续词袋模型的架构。
095:吴恩达《自然语言处理》P95 - CBOW模型架构 🏗️

在本节课中,我们将学习连续词袋模型的神经网络架构。我们将了解其输入、输出、隐藏层的设计,以及各层之间的连接方式。
概述
上一节我们介绍了CBOW模型的基本概念。本节中,我们来看看用于训练该模型的具体神经网络架构。
CBOW模型基于一个浅层的密集神经网络,它包含一个输入层、一个隐藏层和一个输出层。模型的输入是上下文词的向量,输出是预测的中心词向量。
模型架构详解
输入向量记为 X,输出向量记为 ŷ。这两个向量的大小都等于词汇表的大小 V。例如,如果语料是“I am happy because I am learning”,则词汇表包含5个词,因此 V = 5。在实际应用中,V 的值通常在数千的量级。
接下来是隐藏层。在词嵌入的课程中我们提到,词嵌入的维度是一个需要自行设定的超参数。我们记词嵌入的维度为 N,其典型值可以是100或300。隐藏层的神经元数量应设置为与期望的词嵌入维度 N 相等。我们将隐藏层的向量表示记为 H。
这是一个常规的前馈网络,也称为密集神经网络,因此三层之间是全连接的。
以下是模型中涉及的权重矩阵和偏置向量:
- W1:连接输入层和隐藏层的权重矩阵。
- B1:隐藏层的偏置向量。
- W2:连接隐藏层和输出层的权重矩阵。
- B2:输出层的偏置向量。
神经网络在训练过程中将学习这些矩阵和向量。作为一个小提示,后续我们将从这些矩阵中推导出词嵌入。
激活函数
现在,我们需要为隐藏层和输出层选择激活函数。隐藏层激活函数的结果将传递到下一层(即输出层)。同样,输出层激活函数的结果将作为模型的预测结果呈现。
- 对于隐藏层,我们将使用线性整流函数。
- 对于输出层,我们将使用Softmax函数。
激活函数本身值得深入讨论,我将在后续视频中详细描述它们。
至此,您已经了解了连续词袋模型的整体架构。接下来,我将详细讲解前面提到的矩阵和向量的维度,这是完成本周实践作业所需的重要部分。

总结
本节课中,我们一起学习了CBOW模型的神经网络架构。我们明确了其输入输出向量的维度与词汇表大小 V 相关,隐藏层维度与词嵌入维度 N 相等,并认识了连接各层的权重矩阵 W1、W2 和偏置向量 B1、B2。最后,我们确定了隐藏层和输出层将分别使用线性整流函数和Softmax函数作为激活函数。
096:CBOW模型维度架构详解 📐

在本节课中,我们将深入探讨连续词袋模型的维度架构。理解模型中每一步的向量和矩阵维度,对于后续的编程实现至关重要,能有效避免常见的维度不匹配错误。
上一节我们介绍了CBOW模型的基本架构,本节中我们来看看模型中各层向量和矩阵的具体维度。
模型架构回顾 🏗️
首先,输入层由一个小写字母 x 表示。它是一个具有 V 行的列向量,其中大写 V 是词汇表的大小。
为了获得将存储在隐藏层中的值,你需要首先计算输入层值的加权和并加上偏置项。即 W1 * x + B1,我们称结果为 Z1。
小写字母 h 指的是存储在隐藏层中的值列向量。


为了得到 h,你需要将 z1 的值传入一个ReLU激活函数。
维度详解 📏
以下是各层维度的具体说明:
- W1:输入层与隐藏层之间的权重矩阵,具有 n 行和 V 列,其中 n 是词嵌入的维度大小。
- B1:隐藏层的偏置向量,为隐藏层的每个神经元设置一行,因此总共有 n 行。
- 当你计算 W1 * x 并加上偏置向量 B1 时,会得到一个具有 n 行的列向量。通过ReLU函数后,维度保持不变。
- 因此,隐藏层由一个具有 n 行的列向量表示。
接下来,为了获得输出层的值,你需要首先计算隐藏层值的加权和并加上偏置项。即 W2 * h + B2,我们称结果为 z2。z2 中的值有时被称为“logits”。
为了得到输出 ŷ,你需要将 z2 的logits值通过一个softmax激活函数。激活函数的细节将在后续课程中介绍。
以下是输出层维度的具体说明:
- W2:隐藏层与输出层之间的权重矩阵,具有 V 行和 n 列。
- B2:输出层的偏置向量,为每个输出神经元设置一行,因此总共有 V 行。
- 当你计算 W2 * h 并加上偏置向量 B2 时,会得到一个具有 V 行的列向量。通过softmax激活函数后,维度同样不变。
- 最终,你得到一个具有 V 行的输出列向量 ŷ。
行向量处理 🔄
如果你遇到的情况是使用行向量而非列向量,那么你需要使用转置矩阵并在矩阵乘法中调整项的顺序来进行计算。
例如,对于列向量,你计算 h = W1 * x + B1。如果 x 和 B1 是行向量,那么你需要计算 x * W1^T + B1 来得到 z1,进而得到行向量 h。
批处理模式 🧺
接下来,我将展示如何使用神经网络进行批处理,即同时处理多个样本,而不是一次只处理一个样本。


总结 📝
本节课中,我们一起学习了CBOW模型中各层向量和矩阵的维度。了解这些维度将有助于你在编程作业中正确构建模型,希望你再也不会遇到著名的“维度不匹配”错误了。在下一个视频中,让我们更深入地探讨这个模型。
097:CBOW模型维度架构与向量化 🧠

在本节课中,我们将学习连续词袋(CBOW)模型如何处理多个输入样本,以及如何通过向量化技术高效地进行批量计算。我们将深入探讨模型的维度变化、矩阵运算,并简要介绍激活函数的作用。
批量处理与向量化
上一节我们介绍了使用单个输入示例的CBOW模型架构。本节中我们来看看,如果同时输入多个样本并获取多个输出,模型该如何进行向量化计算。

你已经熟悉了在单个输入情况下,神经网络中向量和矩阵的维度。在实际训练中,为了加速学习过程,我们通常希望一次性传入多个样本。这种方法称为批量处理,你将在本周的作业中亲自实践它。
假设你想在每次迭代中向神经网络传入 M 个上下文词向量。M 被称为批量大小,它是你在训练时定义的一个模型超参数。
以下是实现向量化的关键步骤:
- 你可以将这 M 个列向量并排组合,形成一个维度为
(V, M)的矩阵,我们将其记为大写 X。其中 V 是词汇表大小。 - 将这个矩阵 X 输入网络。隐藏层的输出将是一个矩阵 H,代表 M 个样本的隐藏层值。其计算公式为:
H = ReLU(W1 * X + B1)
这里,W1 是权重矩阵,B1 是偏置矩阵。H 的维度是(N, M),其中 N 是隐藏层神经元数量。 - 需要注意的是,原始的偏置向量 b1 需要被复制 M 次,扩展成维度为
(N, M)的偏置矩阵 B1,以便将偏置项加到每个加权和上。在编程中,这被称为广播,NumPy等库会自动处理。 - 输出层的结果矩阵 Ŷ(包含 M 个输出)的计算公式为:
Ŷ = softmax(W2 * H + B2)
同样,偏置向量 b2 也需要广播为矩阵 B2。Ŷ 的维度是(V, M)。 - 最后,你可以将 Ŷ 分解回 M 个列向量。每个输出列向量都对应输入矩阵 X 中的一个输入上下文向量。
激活函数简介
现在,你已经掌握了CBOW模型的维度计算,并能对输入输出进行向量化处理。这为构建一个可工作的模型打下了坚实基础。
在接下来的视频中,我们将深入探讨本模型所使用的激活函数,进一步接近一个完整的CBOW模型实现。

本节课中我们一起学习了CBOW模型的批量处理与向量化方法。我们了解了如何将多个输入样本组织成矩阵进行计算,明确了各层输入、输出及参数的维度变化,并引入了激活函数的概念。掌握这些是高效实现神经网络模型的关键步骤。
098:CBOW模型激活函数架构 🧠

在本节课中,我们将学习神经网络中两个至关重要的激活函数:线性整流单元 和 Softmax函数。我们将了解它们的工作原理、数学定义以及在连续词袋模型中的应用。
激活函数的作用
在深入讲解Softmax函数之前,我们先了解一下激活函数在神经网络中的角色。除了输入层的神经元直接使用输入值外,网络的每一层都会先计算其输入的加权和并加上偏置项,然后将结果传递给一个激活函数。
对于隐藏层,其计算过程如下:
- 首先计算 z1 = W1 * x + b1。
- 然后,将 z1 传递给该层的激活函数,得到隐藏层的值 H。
线性整流单元
线性整流单元是一种流行的通用激活函数。它只在神经元的加权输入之和为正数时才“激活”该神经元,并将这个正数结果传递下去。
数学上,ReLU函数定义为:
ReLU(x) = max(0, x)
其函数图像是一个将负数部分“截断”为0的折线。
以下是一个向量运算的示例:
假设隐藏层计算得到的向量 z1 为 [-0.3, 5.1, 0.2, -4.6]。
将ReLU函数应用于 z1 后,得到的隐藏层向量 H 为 [0, 5.1, 0.2, 0]。
可以看到,所有负值(如-0.3和-4.6)都变成了0,而正值(如5.1和0.2)则保持不变。
输出层与Softmax函数
现在,我们来看输出层。输出层计算隐藏层值的加权和并加上偏置项,即 z = W2 * H + b2。
然后,将向量 z 传递给该层的激活函数。在CBOW模型中,这个激活函数是 Softmax函数,其输出是预测向量 ŷ。
Softmax函数的工作原理如下:
- 输入:一个包含任意实数的向量。
- 输出:一个数值在0到1之间的向量,且所有元素之和为1。这个输出可以被解释为一组互斥事件的概率。
在连续词袋模型中,将Softmax应用于 z 后,会得到一个输出向量 ŷ,其维度与语料库词汇表的大小相同。向量中每个位置的值,可以被解释为:给定特定的上下文词向量 x 时,中心词是词汇表中对应位置那个词的概率。
Softmax函数的数学公式如下:
如果输入向量 z 的元素为 z_i(i 从 1 到 V,V为词汇表大小),输出向量 ŷ 的元素为 ŷ_i,那么:
ŷ_i = e^{z_i} / Σ_{j=1}^{V} e^{z_j}
指数函数 e^{z_i} 将所有输入转换为正数,分母的求和项则对向量进行归一化,确保所有输出值之和为1。
Softmax计算示例
让我们通过一个数值例子来具体说明。假设输出层计算得到的向量 z 为:
z = [2.0, 1.0, 0.1]
计算步骤如下:
- 计算每个元素的指数:
e^z = [e^{2.0}, e^{1.0}, e^{0.1}] ≈ [7.389, 2.718, 1.105] - 计算所有指数值的和:
总和 = 7.389 + 2.718 + 1.105 = 11.212 - 将每个指数值除以总和,得到概率:
ŷ = [7.389/11.212, 2.718/11.212, 1.105/11.212] ≈ [0.659, 0.242, 0.099]
现在,如果将每个位置映射到词汇表(例如,位置1对应“happy”,位置2对应“sad”,位置3对应“neutral”),那么我们可以这样解释输出:给定当前的上下文,模型预测中心词是“happy”的概率为65.9%,是“sad”的概率为24.2%,是“neutral”的概率为9.9%。其中概率最高的“happy”就是模型的最终预测结果。
总结

本节课中,我们一起学习了神经网络中两个核心的激活函数。
- ReLU函数 将所有负输入值变为0,而正输入值则原样通过。它的公式是
ReLU(x) = max(0, x)。 - Softmax函数 将一个实数向量转换为一个概率分布向量。它的输出值在0到1之间,且总和为1,非常适合用于多分类任务的输出层。其公式为
ŷ_i = e^{z_i} / Σ_j e^{z_j}。
理解这两个函数是构建和理解包括CBOW在内的许多神经网络模型的基础。
099:49_训练CBOW模型成本函数 🧠
在本节课中,我们将学习连续词袋模型所使用的成本函数。我们将理解为何选择交叉熵损失函数,并探讨其如何衡量模型预测的准确性。
上一节我们介绍了CBOW模型的基本结构,本节中我们来看看其训练过程中需要最小化的目标——成本函数。
成本函数概述
考虑一个机器学习模型。一个训练样本由输入和真实目标组成,模型基于输入预测出一个值。
损失函数用于衡量单个输入训练样本的预测值与真实值之间的误差。对于连续词袋模型,输入上下文词由向量 X 表示,真实值是代表实际中心词的向量 Y,预测值是向量 Y_hat。学习过程的目标是找到最小化损失的参数。


给定训练数据集,在连续词袋模型中,学习过程调整的参数是权重矩阵 W1、W2 以及偏置向量 B1、B2。
交叉熵损失函数
你将使用的损失函数是交叉熵损失。这里不深入理论,你只需知道交叉熵损失函数常与分类模型一起使用,而分类模型通常与神经网络中的Softmax输出层结合,正如你在连续词袋模型中使用的那样。
如果你曾使用过逻辑回归,你可能已经知道交叉熵损失函数的一种简单形式,即对数损失,它适用于只有两个类别的情况。
使用连续词袋模型的符号表示,一个训练样本的交叉熵损失公式为:
J = - Σ (k=1 to V) [ Y_k * log(Y_hat_k) ]
损失计算示例
以下是一个具体示例,说明如何计算交叉熵损失。
考虑输入上下文词 “I am because I”,实际中心词是 “happy”。对应的向量 Y 在 “happy” 对应的行上为1,其他为0。预测值可能是向量 Y_hat,其最大值在 “happy” 对应的第0行。这个向量将被解释为预测 “happy” 为中心词,这是正确的预测。
计算交叉熵损失:
log(Y_hat)是这个向量。- 将每个元素与 Y 的对应元素相乘,得到这个向量(使用
⊙表示逐元素乘法)。注意,只有一个非零值,即 -0.49。 - 求和得到这个值,损失是负的求和结果,因此为 0.49。
现在,如果预测错误呢?假设预测向量是 [0.01, 0.96, 0.01, 0.01, 0.01],即预测 “am” 而不是 “happy”。
log(Y_hat)是这个向量。- 与 Y 逐元素相乘得到这个向量。
- 求和为 -4.61,损失是 -1 乘以这个值,因此为 4.61。
可以看到,当预测错误时,损失更大。
损失函数特性
更一般地说,交叉熵损失可简化为 -1 * log(预测向量中对应实际中心词的那个元素的值)。
如果将交叉熵损失绘制为“对实际中心词的预测值”的函数图像,可以看到:
- 如果模型预测正确,Y_hat 值接近1,则损失接近0。这是因为
log(1) = 0。 - 另一方面,如果模型预测错误,则实际词的 Y_hat 将接近0,导致损失值很高。原因是当
x趋近于0时,log(x)的极限是负无穷。因此,对于使用-1 * log的损失函数,当 Y_hat 趋近于0时,损失趋近于正无穷。
所以,损失函数奖励正确的预测,惩罚错误的预测。

你已经计算了一个训练样本的交叉熵损失,并看到了交叉熵损失与实际预测值的关系图。
总结
本节课中我们一起学习了连续词袋模型的成本函数——交叉熵损失。我们理解了其公式 J = - Σ Y_k * log(Y_hat_k),并通过示例看到它如何有效地衡量预测误差:正确预测时损失低,错误预测时损失高,从而引导模型参数向正确方向更新。

下一节视频,我们将探讨CBOW模型的前向传播过程。
100:CBOW模型前向传播详解 🧠

在本节课中,我们将学习连续词袋(CBOW)模型的前向传播过程。我们将看到输入数据如何经过神经网络的激活层,最终转化为预测结果。
上一节我们介绍了交叉熵损失函数,以及它如何衡量CBOW模型在单个预测中的误差。本节中,我们来看看如何使用批量的训练样本来训练神经网络。
前向传播回顾
前向传播是指将输入值从神经网络的输入层开始,依次通过每一层,最终传递到输出层,并在此过程中计算各层数值的过程。
以下是前向传播的核心步骤:
- 从一批训练样本开始,这些样本表示为一个矩阵 X,其维度为 V × M。其中 V 是词汇表大小,M 是批次大小。
- 将 X 前向传播到神经网络中,得到输出矩阵 Ŷ,其维度同样为 V × M。
- 输出矩阵 Ŷ 简单地将 M 个输出列向量堆叠起来,每个向量对应一个训练样本的输入向量。

从损失到成本
为单个样本计算误差时,我们使用交叉熵损失函数。当处理批量样本时,我们将计算成本。成本与损失的目的相同,并且实际上基于损失函数。
在实践中,“损失”和“成本”这两个术语经常互换使用。但在本课程中,我们用“损失”指代单个样本的误差,用“成本”指代一批样本的误差。
一批样本的交叉熵成本,就是这 M 个独立样本的交叉熵损失的平均值。
以下是批量训练样本的交叉熵成本公式:
J_batch = - (1/M) * Σ (i=1 to M) Σ (j=1 to V) [ y_j^(i) * log(ŷ_j^(i)) ]
这个公式可以重写为:
J_batch = (1/M) * Σ (i=1 to M) J^(i)
其中,J^(i) 是第 i 个样本的损失。通过这种方式,可以将成本直观地理解为各个样本损失的平均值。
下一步:优化网络
接下来,你将使用这个成本函数来调整神经网络的参数,以改进其预测效果。

本节课中我们一起学习了CBOW模型的前向传播过程,以及如何为批量样本计算交叉熵成本。在下一节视频中,你将学习如何利用这个成本函数来训练你的词向量。
101:反向传播与梯度下降 🧠

在本节课中,我们将学习如何训练连续词袋模型。具体来说,我们将了解如何通过反向传播算法计算成本函数的梯度,并利用梯度下降法来更新神经网络的权重和偏置,从而最小化模型的预测误差。
上一节我们介绍了CBOW模型的基本结构和前向传播过程。本节中,我们来看看如何通过训练来优化模型的参数。
我将展示如何找到线性层和词嵌入的权重,以最小化这两个矩阵或权重向量。为此,你需要最小化成本函数。为了最小化成本,你将使用两种技术。
第一种技术是反向传播。反向传播是一种算法,用于计算成本函数相对于神经网络权重和偏置的偏导数或梯度。请记住,批次成本 J_batch 是权重和偏置的函数。“反向”一词源于通过链式法则求导公式的方式,即从输出层开始,然后利用先前计算出的导数逐层向后计算。顺便提一下,反向传播是动态规划的一个典型例子,这是你在本课程第一周学习过的概念。
第二种技术是梯度下降。它利用计算出的梯度来调整神经网络的权重和偏置,以最小化成本。
我不会深入探讨反向传播或梯度下降的数学细节。但如果你有兴趣了解更多,可以查看神经网络课程,以理解各种公式是如何推导的。
首先,让我们使用反向传播来计算执行梯度下降所需的成本函数偏导数。
你需要计算成本函数相对于神经网络参数的偏导数。在本例中,参数是 W1、W2、B1 和 B2。这些计算过程冗长,在实践中,你将使用机器学习库来为你处理反向传播。
因此,我将直接给出公式,你可以在本周的作业中实现它们。
以下是成本函数相对于权重矩阵 W1 的偏导数公式:
∂J/∂W1 = (1/m) * (∂J/∂Z2) * A1^T
这是成本函数相对于 W2 的偏导数公式:
∂J/∂W2 = (1/m) * (∂J/∂A2) * H^T
以下是偏置向量 B1 的公式。让我在此稍作停顿。在这个公式中,我引入了 1_m 向量,这是一个包含 m 个元素且所有元素均为1的行向量。如果你将一个有 m 行的矩阵 A 乘以转置后的 1_m 向量,你将得到一个列向量,其中每个元素等于 A 矩阵对应行元素的总和。需要说明的是,引入 1_m 向量主要是为了形式化地书写数学公式。在实践中,当你在 Python 中实现时,计算列和的最简单方法是使用 NumPy 的 sum 函数,而无需使用 1_m 向量。在 sum 函数中,你需要指定参数 axis=1 来对列求和,并且需要将参数 keepdims 设置为 True,以便结果可以在后续计算中被广播到所需大小的矩阵中。
最后,这是 B2 的公式,它也使用了 1_m 向量。就本课程而言,你无需理解这些公式是如何推导的,可以直接使用它们。
现在,既然你已经有了这些梯度,就可以使用梯度下降来更新权重矩阵和偏置向量了。
计算中包含一个学习率 alpha,它是模型的一个超参数。以下是更新权重和偏置的公式:
W1_new = W1 - α * (∂J/∂W1)
W2_new = W2 - α * (∂J/∂W2)
B1_new = B1 - α * (∂J/∂B1)
B2_new = B2 - α * (∂J/∂B2)
其思想是取原始参数,然后减去 alpha 乘以它们的梯度。由于 alpha 被选为一个小于1的小正数,乘以 alpha 的效果是减少每个变量在每一步的更新量。较小的 alpha 允许权重和偏置进行更渐进的更新,而较大的数值则允许权重更快地更新。例如,新的权重矩阵 W1 将等于原始的 W1 减去 alpha 乘以在反向传播步骤中计算出的 ∂J/∂W1。
你现在已经掌握了训练连续词袋模型所需的一切知识。在下一个视频中,你将学习如何从训练好的连续词袋模型中提取词嵌入向量。
梯度下降法让你能够计算相对于权重和偏置的偏导数。一旦计算出这些偏导数,你就可以更新权重。alpha 参数决定了更新时步长的大小。

本节课中我们一起学习了训练CBOW模型的核心步骤:首先通过反向传播算法计算成本函数相对于各层参数的梯度,然后利用梯度下降法,结合学习率 alpha,迭代更新模型的权重和偏置,最终使模型的预测误差最小化。
102:提取词嵌入向量 🧠
在本节课中,我们将学习如何从训练好的连续词袋模型神经网络中提取词嵌入向量。词嵌入是自然语言处理中的核心概念,它能够将词汇表示为具有语义信息的密集向量。

概述
我们已经学习了如何通过前向传播、反向传播和梯度下降来训练连续词袋模型的神经网络。然而,训练过程并不会直接输出词嵌入向量,它们是训练过程的副产品。接下来,我们将探讨几种从训练好的神经网络权重中提取词嵌入向量的方法。
提取词嵌入向量的三种方法
上一节我们介绍了词嵌入是训练过程的副产品,本节中我们来看看具体如何提取它们。以下是三种主要的提取方法:
方法一:使用权重矩阵 W1 的列向量
第一种方法是将权重矩阵 W1 的每一列视为词汇表中一个词的嵌入列向量。矩阵 W1 的列数等于词汇表的大小 V,因此每一列对应一个词。
公式:
word_embedding_for_word_i = W1[:, i]
其中,W1 的列与词汇表中词的顺序与输入向量或矩阵的行顺序一致。例如,在语料库 “I am happy because I am learning” 中,如果输入向量的行对应 “am”, “because”, “happy”, “I”, “learning”,那么 W1 的第一列就是 “am” 的词嵌入列向量,第二列是 “because” 的词嵌入列向量,依此类推。

方法二:使用权重矩阵 W2 的行向量
第二种方法是将权重矩阵 W2 的每一行视为词汇表中一个词的嵌入行向量。矩阵 W2 的行数等于词汇表的大小 V,因此每一行对应一个词。
公式:
word_embedding_for_word_i = W2[i, :]
同样,W2 的行与词汇表中词的顺序与输入向量或矩阵的行顺序一致。沿用上面的例子,W2 的第一行就是 “am” 的词嵌入行向量,第二行是 “because” 的词嵌入行向量。
方法三:平均前两种表示
第三种也是最后一种方法,是取前两种表示的平均值。如果你想得到词嵌入列向量,可以计算 W1 和 W2 转置的平均值,从而得到一个新的矩阵 W3。
公式:
W3 = (W1 + W2.T) / 2
然后,你可以像之前一样,从 W3 的每一列中提取词嵌入向量。在视觉示例中,“am” 的词嵌入将是 W3 的第一列,即 W1 的第一列和 W2 的第一行的平均值。
在本周的作业中,你将通过平均 W1 的转置和 W2 来提取词嵌入作为行向量。
过渡到评估
现在你已经知道了如何训练和提取这些词向量,在接下来的课程中,你将学习如何评估它们。具体来说,你将了解两种类型的评估指标:内在评估和外在评估。
总结
本节课中我们一起学习了从训练好的连续词袋模型神经网络中提取词嵌入向量的三种方法:
- 使用权重矩阵 W1 的列向量。
- 使用权重矩阵 W2 的行向量。
- 取前两种表示的平均值。

理解这些提取方法是应用词嵌入进行下游任务的基础。
103:评估词嵌入的内在评估方法 🧠

在本节课中,我们将要学习如何评估词嵌入模型的质量。词嵌入是将词语映射到高维向量的技术,评估其好坏至关重要。我们将重点介绍内在评估方法,这种方法直接检验词嵌入本身是否捕捉到了词语之间的语义和语法关系。
两种评估指标
评估指标主要分为两类:内在评估和外在评估。
根据你试图优化的具体任务,你可以选择其中一种进行评估。接下来,让我们深入了解内在评估。
什么是内在评估?🔍
内在评估方法旨在评估词嵌入本身在多大程度上捕捉了词语之间的语义或语法关系。
- 语义指的是词语的含义。
- 语法指的是词语的语法结构。
如何进行内在评估?
以下是几种常用的内在评估方法。
1. 词语类比测试
你可以通过词语类比任务来测试词嵌入。这包括测试语义类比和语法类比。
- 语义类比:例如,找出“法国之于巴黎,如同意大利之于____”中的缺失词。
- 语法类比:例如,测试复数、时态和比较级。例如,“sing(唱)之于sang(唱了,过去式),如同being(是)之于____(was,过去式)”。
需要注意的一点是,这种方法可能存在多个正确答案。例如,“狼之于狼群,如同蜜蜂之于____”,这里的集体名词缺失词可能是“蜂群”或“殖民地”。
这里有一个来自原始Word2Vec研究论文的例子,其中的词嵌入是在一个巨大的训练集上创建的。
需要说明的是,这些词嵌入是由连续跳字模型创建的,而不是连续词袋模型。但评估原理是相同的。
需要注意的是,类比并不完美。例如,词嵌入未能完全捕捉化学元素与其符号之间的关系。铜的符号是Cu,锌的符号是Zn,金的符号是Au,铀的符号是U。但词嵌入模型输出的结果是“钚”,而不是“U”。
2. 聚类分析
你也可以通过使用聚类算法对相似的词嵌入向量进行分组,来判断这些簇是否捕捉到了相关的词语,从而进行内在评估。
这个过程可以使用人工制作的参考标准(如词典)来自动化评估。
下图是一篇关于词嵌入评估的论文中的聚类可视化结果。
如果你观察词语簇,可以看到“fight(战斗)”和“agitate(煽动)”彼此接近。同样,“hair care(护发)”和“hairdress(美发)”也彼此接近。
这很有趣,对吧?
3. 可视化
在本周的作业中,你将可视化词嵌入向量。这属于一种基本的内在评估,它依赖于人类判断来评估嵌入的质量。
但在介绍作业之前,我将向你展示另一种评估词嵌入质量的方法:外在评估。

本节总结 📝
本节课我们一起学习了词嵌入的内在评估方法。
内在评估允许你测试词语之间的关系。你可以将其用于聚类分析、词语类比和可视化。
在下一个视频中,你将学习一些略有不同的内容,即外在评估。
104:54_评估词嵌入外在评估 🧪

在本节课中,我们将要学习如何通过外在评估方法来评估词嵌入模型的质量。我们将了解外在评估的基本概念、具体任务示例,并分析其优缺点。
概述
上一节我们介绍了通过内在评估(如类比推理任务)来衡量词嵌入的质量。本节中,我们来看看另一种评估方式——外在评估。外在评估通过将词嵌入应用于实际的外部任务,并以该任务的性能指标作为词嵌入质量的代理标准。

什么是外在评估?
为了在外部任务上测试你的词嵌入,你可能会采用外在评估。例如,如果你构建了一个语音识别系统,那么了解它是否有效的唯一方法,就是在实际任务中测试整个系统。这种方法可能比内在评估更有效。
外在评估的具体做法是:使用词嵌入来执行一个外部任务,这个任务通常就是你最初需要词嵌入去解决的现实世界问题。
然后,使用该任务的性能指标(如准确率)作为词嵌入质量的代理。
有用的词级别任务示例
以下是两个常见且有用的词级别任务示例,它们常被用于外在评估:
- 命名实体识别:识别文本中具有特定意义的实体,如人名、组织名、地点等。
- 例如,在句子 “Andrew works at DeepLearning.AI” 中,“Andrew” 是一个命名实体,被归类为“人物”;“DeepLearning.AI” 是另一个命名实体,被归类为“组织”。
- 词性标注:为句子中的每个单词标注其词性(如名词、动词、形容词等)。
你可以训练一个模型来帮助识别和分类句子中的命名实体,然后在测试集上使用选定的评估指标(如准确率或F1分数)来评估这个分类器。分类器在评估指标上的表现,代表了词嵌入和分类任务结合后的综合性能。
外在评估的优缺点
外在评估方法是确保词嵌入真正有用的最终测试。然而,它也存在一些主要缺点:
- 评估比内在评估更耗时。
- 如果性能不佳,性能指标无法提供具体信息,说明是端到端流程中的哪个特定部分出了问题——是词嵌入本身,还是用于测试它们的外部任务。
总结

本节课中我们一起学习了词嵌入的外在评估方法。我们了解到,外在评估通过在实际应用任务中的表现来衡量词嵌入的实用性,但它也存在更难排查问题和更耗时的缺点。至此,你已经掌握了内在和外在两种评估词嵌入的方法。如果你想深入了解这个主题,可以参考相关论文。
在进入下一个视频之前,让我们简要回顾一下所学内容:外在评估评估的是词嵌入的实际有用性,但更难进行问题排查且更耗时。
105:词嵌入技术总结与展望 🎯
在本节课中,我们将回顾本周学习的核心内容——词嵌入技术。你将了解如何从零开始训练词向量,并掌握其在自然语言处理中的实际应用。课程最后,我们将展望更高级的语言模型技术。

概述
本周我们全面学习了词嵌入技术。词嵌入能够将单词的含义捕捉到向量中,这些向量可用于简单和高级的自然语言处理应用。
你本周已经掌握了许多知识。让我们通过一些回顾来巩固你的新技能,并为接下来的作业做好准备。

本周核心技能回顾
上一节我们介绍了词嵌入的基本概念,本节中我们来具体回顾一下你已掌握的技能。
你不仅学会了如何使用词向量,还掌握了如何从零开始训练它们。这是一项非常有用的技能。
以下是本周你学到的具体内容:
- 准备数据:你将读取并标记化语料库以构建词汇表。
- 创建词表示:将单词映射到索引,以及反向映射,并将这些索引转换为独热向量。
- 学习词嵌入:通过创建连续词袋模型来学习词嵌入。
- 构建与训练模型:为该模型构建神经网络,进行训练,并提取得到的词嵌入向量。
- 可视化评估:将词嵌入向量可视化,作为一种内在评估方式。
最终作业实践
在最终的作业中,你将有机会实践本周学到的所有新技能。
完成作业后,你将能更好地转向更高级的语言建模和词嵌入方法。
这些更复杂的方法支持词汇表外的单词,并能更好地捕捉单词的多种可能含义。这些能力对于自然语言处理在现实世界中的应用至关重要。
现实世界中的应用与工具
最后,你需要知道,尽管在本次作业中你是从零开始实现一切,但在现实世界中,你通常会使用自然语言处理和机器学习库来完成繁重的工作。
花些时间探索这些库是值得的。
具体对于词嵌入,TensorFlow和PyTorch等库允许你使用单行代码在神经网络中添加一个嵌入层。其他库如Transformers也允许你这样做。

总结
本节课中我们一起学习了词嵌入技术的完整流程,从理论到实践。恭喜你完成本周的学习!你现在不仅知道如何使用词向量,还知道如何从零开始训练它们。这确实是一项非常有用的技能。
107:序列模型与自然语言处理 🧠💬
在本课程中,我们将学习如何利用序列模型构建强大的自然语言处理应用。我们将从情感分析入手,逐步深入到文本生成、命名实体识别以及重复问题检测等核心任务。



概述

欢迎来到本专项课程的第三门课。这门课程名为“使用序列模型的自然语言处理”。在本课程中,你将构建一系列实用的NLP应用。首先,你将使用深度神经网络将情感分析提升到新的水平。接着,你将使用循环神经网络(RNN)构建一个语言生成器。然后,你将把长短期记忆(LSTM)单元应用于命名实体识别问题。最后,你将使用孪生网络来识别重复问题,例如在在线论坛中判断不同用户是否在用不同措辞询问本质上相同的问题。通过在本课程中掌握的技能,你将能够构建强大的NLP系统,解决跨行业的广泛问题。
我们很高兴欢迎Lucas和Eice担任本课程的讲师,他们将与你一同深入探讨这些主题。
课程内容详解


上一节我们对课程进行了整体介绍,本节中我们来看看课程的具体内容和目标。
以下是本课程你将构建的核心应用:
- 情感分析:你将利用深度神经网络,构建一个比第一门课程中基于朴素贝叶斯的分类器更强大的情感分析分类器。
- 语言生成:你将使用循环神经网络创建一个高级模型来生成文本。这可以看作是从基础技能迈向构建真实世界NLP应用的关键一步。
- 命名实体识别:你将应用LSTM单元来解决命名实体识别问题,即从句子中识别出如人名、地名等特定实体。这是许多重要NLP系统的基石。
- 重复问题检测:你将使用孪生网络来解决识别文本重复的问题。判断两段文本是否互为重复,是构建在线论坛和搜索引擎等应用的核心模块。
课程基础与进阶
在了解了具体应用后,我们来看看本课程与之前课程的衔接。
本专项课程的前两门课为你打下了坚实的基础,提供了必要的背景知识和核心技能来应对本课程。例如,在第一门课中,你已经使用简单的朴素贝叶斯分类器完成了情感分析。现在,你将利用深度神经网络的力量构建一个更鲁棒的情感分析分类器。同样,在第二门课中,你已经学习了如何使用相对简单的n-gram语言模型来预测序列中的下一个词。本课程将在此基础上,使用循环神经网络创建高级的文本生成模型。
应用场景与意义
理解了技术路线后,本节我们探讨这些应用的实际价值。
情感分析是一个复杂但极具价值的问题,在许多应用中都需要确定句子的情感倾向。语言建模所能解决的问题几乎是无限的,从翻译、自动补全到从零开始生成文本。命名实体识别是信息提取的关键步骤。而重复问题检测,初听起来可能不那么有趣,但实际上是构建在线论坛和搜索引擎等系统的核心组成部分。我们非常高兴能向你展示这些应用,并将你的技能提升到新的水平。
总结


本节课中,我们一起学习了第三门课程“序列模型与自然语言处理”的总体介绍。我们了解到,本课程将引导我们使用深度神经网络、RNN、LSTM和孪生网络等序列模型,构建包括高级情感分析、文本生成、命名实体识别和重复问题检测在内的四个核心NLP应用。这些技能将帮助我们解决现实世界中的广泛问题。


这是一门令人兴奋的课程,让我们开始吧。祝你好运,学习愉快!
109:用于情感分析的神经网络 🧠
在本节课中,我们将学习如何构建一个用于情感分析的神经网络。我们将回顾神经网络的基本结构,并了解如何将其应用于处理复杂的、带有细微差别的推文情感分类任务。

神经网络结构回顾
上一节我们介绍了情感分析的基本概念,本节中我们来看看用于此任务的神经网络结构。
神经网络是一种计算结构,它以简化的方式尝试模仿人脑识别模式的方式。它们在人工智能的许多应用中都有使用,并且在包括自然语言处理在内的各种任务上被证明非常有效。

请看这个简单的神经网络示例,它具有 n 个输入参数、两个隐藏层和三个输出单元。

作为输入,该神经网络接收一个具有 n 个特征的数据表示 X。然后在隐藏层中执行计算。最后,它输出一个结果,在本例中输出大小为 3。
让我们看看它在数学上是如何工作的。我将每一层的激活表示为上标 I,其中 I 是层号。首先,定义上标 0 为输入向量 x。要获取每一层激活值 a,必须计算 z 上标 I 的值,该值取决于该层的权重矩阵和前一层的激活值 a。


最后,通过对 Z 的值应用激活函数 G 来获得每一层的值。如你所见,这个计算从神经网络的左侧向前推进到右侧。这就是为什么这个过程被称为前向传播。


本模块的神经网络设计

了解了基本结构后,我们来看看为本模块作业专门设计的神经网络。
对于本模块的作业,你将实现一个如下所示的神经网络。

作为输入,它将接收推文的简单向量表示。它将有一个嵌入层,将你的表示转换为适合此任务的最优表示。最后,它将有一个使用 ReLU 激活函数的隐藏层,以及一个使用 Softmax 函数的输出层,该层将给出推文具有积极或消极情感的概率。


这个神经网络将允许你预测复杂推文的情感。例如,像“这部电影几乎算好”这样的推文,使用朴素贝叶斯等更简单的方法无法正确分类,因为它们会遗漏重要信息。


输入表示与填充处理
现在,我们来看看如何为神经网络准备输入数据。
你将用于此神经网络的初始表示 X 将是一个整数向量,类似于之前情感分析的工作。首先,你需要列出词汇表中的所有单词。接下来,为此应用,你将给每个单词分配一个整数索引。然后,对于推文中的每个单词,添加词汇表中的索引以构建每个推文的向量。
以下是构建向量表示的步骤:
- 列出词汇表。
- 为每个单词分配一个整数索引。
- 对于每条推文,遍历其中的单词,并用对应的索引构建向量。
在你获得所有推文的向量表示之后,你需要确定最大向量大小,并用零填充每个向量以匹配该大小。这个过程称为填充,它确保所有向量具有相同的大小,即使你的推文长度不同。


总结与预告
本节课中我们一起学习了用于情感分析的神经网络。我们快速回顾一下。至此,你已经熟悉了用于对一组复杂、微妙的推文进行情感分类的神经网络的一般结构。你还回顾了本模块中将使用的整数表示方法。
在本视频中,你看到了使用神经网络进行情感分析的概述,了解了如何使用填充以及如何表示一条推文。在下一个视频中,我们将更深入地探讨情感分析。


110:3_全连接层与ReLU 🧠

在本节课中,我们将要学习神经网络中最常用的两种核心层:全连接层(Dense Layer)和ReLU层。我们将详细探讨它们的工作原理、数学表达以及在构建神经网络模型中的作用。
概述
大多数神经网络都包含两种非常常用的层。一种是全连接层,它负责网络中层与层之间的信息传递。另一种是ReLU层,它的作用是保持网络的稳定性。接下来,我们将对它们进行更详细的探索。
全连接层详解
上一节我们介绍了神经网络的基本结构,本节中我们来看看其中的核心计算层——全连接层。
首先,我将回顾在前几个视频中已经提到的全连接层,然后向你展示ReLU层是如何工作的。
回忆一下,在一个隐藏单元 J 中,会顺序发生两个计算。
- 首先,它计算与该隐藏单元相关的权重和来自前一层的激活值之间的点积。
- 然后,将一个非线性函数应用于该点积的结果。
观察一个隐藏层 I(即使在简单的神经网络中也是如此),你将拥有多个单元,其中第一个计算是点积,记为 Z[I][J]。
全连接层通过将权重矩阵 W[I] 与前一层的激活值 A[I-1] 相乘,一次性计算出所有这些点积。其中,权重矩阵包含一组可以在训练过程中学习的参数。
公式表示:
Z[I] = W[I] * A[I-1] + b[I]
(注:为完整起见,此处补充了偏置项 b,这是全连接层的标准公式)
在全连接层之后,通常会对每个隐藏单元中的 Z 值应用一个非线性函数 G。
ReLU层的工作原理
上一节我们介绍了全连接层的计算,本节中我们来看看紧随其后、至关重要的非线性激活函数——ReLU。
ReLU函数是完成此任务的典型选择。它在将值发送到下一层之前,将所有负值映射为0。它计算一个函数,对于 Z_subj 的负值返回0,而对于正的 Z_subj 则不做任何改变。
这相当于对每个隐藏单元取0和 Z 之间的最大值。
公式表示:
A[I][J] = ReLU(Z[I][J]) = max(0, Z[I][J])
ReLU代表“修正线性单元”,当你查看其函数图像时会觉得这个名字很贴切,因为函数的负值部分被“修正”以匹配水平轴(即X轴)。
正如你在此处所见,负值部分被置零,正值部分保持线性。
你现在已经更详细地了解了全连接层,我也向你展示了ReLU层的工作原理。
模型组合概述
接下来,我将讨论如何将这些组件组合成一个完整的模型。通过交替堆叠全连接层和ReLU层,我们可以构建出能够学习复杂模式和关系的深度神经网络。
总结

本节课中我们一起学习了神经网络的两个基本构建模块:
- 全连接层:负责进行层间的线性变换(权重与输入的矩阵乘法)。
- ReLU激活层:负责引入非线性,将线性变换后的负值输出置零,保留正值,这是网络能够学习复杂函数的关键。
理解这两层是理解现代深度学习模型的基础。在后续课程中,我们将看到如何将它们组合起来,构建出功能强大的神经网络。
111:嵌入层与均值层 🧠
在本节课中,我们将学习神经网络中两种重要的层:嵌入层和均值层。我们将了解它们的作用、工作原理以及如何在序列模型中应用它们。

上一节我们介绍了密集层和ReLU层。本节中,我们来看看另外两种在自然语言处理中非常有用的层:嵌入层和均值层。
嵌入层详解 🔍
在自然语言处理中,我们通常有一个由独特单词组成的集合,称为词汇表。
嵌入层的作用是:将词汇表中每个单词对应的索引,映射到一个具有特定维度的向量表示。例如,一个维度为2的嵌入层。
以下是嵌入层的工作原理:
- 嵌入层接收一个单词索引(例如,单词“I”的索引)。
- 它根据一个可训练的权重矩阵,返回该单词对应的向量表示。
- 例如,对于单词“I”,嵌入层可能返回向量
[0.020, 0.006];对于单词“NLP”,可能返回[-0.009, 0.050]。
嵌入层中的所有权重值都是可训练的。这意味着,当你在模型中使用嵌入层时,模型会学习到最适合当前NLP任务的单词向量表示。
在你的模型中,嵌入层需要学习的权重矩阵大小为:
词汇表大小 × 嵌入维度
嵌入维度的大小可以作为模型的一个超参数进行调整。通过这个层,模型可以针对特定任务优化单词的向量表示,就像优化其他层的权重矩阵一样。
从嵌入到均值:处理序列 🧩
嵌入层能够将单词映射为向量。那么,如何处理像“I am happy”这样的一系列单词(例如一条推文)呢?
以下是处理流程:
- 嵌入层会依次将句子中的每个单词映射为其对应的嵌入向量。
- 最终,它会返回一个词嵌入矩阵。
如果你使用填充向量来表示不同长度的推文,理论上可以将这个矩阵展开,并将其值输入到神经网络的下一层。但这样做可能会导致需要训练的参数数量过多。
作为一种替代方案,我们可以计算嵌入向量在每个特征维度上的平均值。这正是均值层所做的事情。
均值层接收一个词嵌入矩阵,并返回一个向量。这个向量的长度等于嵌入维度,它代表了整个输入序列(如一条推文)的“平均”语义。
请注意:均值层本身没有任何可训练的参数,因为它只是执行简单的算术平均计算。它的优势在于,无论输入文本序列有多长,经过均值层后,输出向量的维度都保持不变,这简化了后续层的处理。
课程总结 📝
本节课中,我们一起学习了嵌入层和均值层。

- 嵌入层:将词汇表中的单词索引映射为可训练的、密集的向量表示。使用嵌入层可以让模型针对特定任务学习到最佳的词汇表示。
- 均值层:接收一个词嵌入矩阵,并通过对所有单词向量取平均值,生成一个固定长度的向量来表示整个文本序列(如一条推文)。它没有可训练参数,主要用于降维和特征聚合。
现在,你的工具箱里已经有了四种基础层:密集层、ReLU层、嵌入层和均值层。这些知识足以为你打下坚实的基础,让你可以开始构建自己的神经网络模型。
112:循环神经网络 (RNN) 🧠
在本节课中,我们将要学习循环神经网络(RNN)的基本概念及其在自然语言处理中的应用。RNN是一种专门用于处理序列数据的神经网络,它能够记住之前的信息,并利用这些信息来影响当前的输出。
本周主题:循环神经网络
本周的主题是循环神经网络。在上一节课程中,我们讨论了N元语法模型。本节中,我们来看看RNN如何解决N元语法模型的一些核心问题。
N元语法模型最大的问题之一是最终会消耗大量的内存和计算资源。
循环神经网络有助于缓解这个问题,并且在语言生成任务中的表现优于N元语法模型。
RNN的优势与变体
上一节我们介绍了RNN的基本优势。本节中,我们来看看RNN的一些重要变体。
此外,你可以构建一个双向RNN,它能够同时追踪来自两个方向的信息。
这允许模型进行更好的字符生成。
你还可以为你的RNN添加多个层,使其变得更“深”。
这使得模型能够捕捉更抽象的依赖关系。
门控循环单元 (GRU)
以下是关于RNN一个更强大的变体:门控循环单元的介绍。
你还将学习门控循环单元,它比普通的RNN更强大。
课程将向你展示RNN和GRU背后的细节,以及如何使用它们。
总结
本节课中,我们一起学习了循环神经网络的基本原理。我们了解到RNN如何解决N元语法模型的内存消耗问题,并介绍了双向RNN和深层RNN的结构。最后,我们预览了更强大的门控循环单元。这听起来非常棒,我十分期待接下来的课程内容。😊
113:传统语言模型的局限性 🧠

在本节课中,我们将学习传统N-gram语言模型的基本原理,并重点探讨其在实际应用中的主要局限性,特别是对存储空间和内存的高要求。
概述
上一节我们介绍了N-gram语言模型的基本概念。本节中,我们将深入分析该模型的局限性,并理解为何在处理长序列或大规模语料时,它会变得不切实际。
N-gram模型回顾
N-gram语言模型通过计算条件概率来评估一个词序列的可能性。具体来说:
- 对于二元语法(Bigram),使用前一个词来计算当前词的条件概率。
- 对于三元语法(Trigram),使用前两个词来计算条件概率。
- 推广到N-gram模型,则使用前
n-1个词来计算条件概率。
最终,整个句子的概率通过将序列中每个词的条件概率相乘得到。对于一个三元组句子,其二元语法模型的计算公式如下:
P(w1, w2, w3) = P(w1) * P(w2|w1) * P(w3|w2)
N-gram模型的局限性
尽管N-gram模型直观易懂,但它存在几个显著的缺点。
以下是N-gram模型面临的主要挑战:
- 捕捉长距离依赖困难:为了捕捉句子中相距较远的词语之间的依赖关系,模型需要计算非常长的词序列的条件概率。这在数据稀疏的情况下难以准确估计。
- 存储需求巨大:模型需要存储所有可能词组合的概率。随着N的增大或词汇表的扩展,需要存储的概率数量会呈指数级增长,占用大量的磁盘空间和内存。
- 数据稀疏性问题:即使拥有大型语料库,许多长尾的、特定的词序列也可能出现次数极少,导致其概率估计不可靠。
可以预见,随着模型复杂度的提升,这种方法很快就会变得不切实际。
过渡到更高效的模型
鉴于传统N-gram模型的这些限制,开发者们在资源受限的场景(如手机应用程序)中可能希望寻求替代方案。
接下来,我将向你介绍循环神经网络(RNNs) 和门控循环单元(GRUs)。这两种模型在处理如机器翻译等自然语言处理任务时,比N-gram模型高效得多。
总结

本节课我们一起学习了传统N-gram语言模型的工作原理,并重点分析了它的核心局限性:难以建模长距离依赖,以及随着模型复杂度增加,对存储空间和内存的需求会变得极其庞大。正是这些缺点推动了像循环神经网络(RNNs)这样更高效模型的发展。
114:循环神经网络 (RNN) 🧠

概述
在本节课中,我们将要学习循环神经网络(RNN)的基本概念。我们将了解RNN相比传统N元语法模型的优势,并初步探索其工作原理。
RNN的优势与应用场景
你已经理解了语言模型完成句子的基本思想。让我们来看一个具体的例子。
例句:Nor was supposed to study with me. I called her, but she did not ______.
如果你使用一个传统的语言模型,比如三元语法模型,来尝试完成这个句子,你会比较不同单词在“did not”之后出现的概率,然后选择概率最高的词。例如,你的模型可能会选择单词“have”,因为在典型的文本语料库中,看到这个三词序列的概率很高。
然而,在这个句子中使用“have”没有任何意义。一个更好的选择是“answer”。
RNN的优势在于,它不受限于只看前n个单词。它能够将信息从句子开头传播到结尾。因此,如果你为此任务训练一个RNN,你会得到一个更好的预测结果,即单词“answer”,尽管这个词本身出现的概率可能较低。
如果你想用一个N元语法模型来完成这样的句子,你将不得不考虑长达六个词的序列,这在实际应用中是非常不切实际的。
RNN的工作原理
为了理解循环神经网络如何工作,我们以上一个例子中的第二句话为例。
例句:I called her, but she did not ______.
一个基础的RNN将信息从句子开头传播到结尾。这个过程从序列的第一个单词开始,最左侧的隐藏值在这里被计算。然后,它将部分计算出的信息传播出去,获取序列中的第二个单词,并计算出新的值。
这个过程可以这样描述:橙色的区域表示第一次计算出的值,绿色的区域表示第二个单词。第二次计算出的值使用了橙色的旧值和绿色的新单词。之后,它获取第三个单词,以及从第一个和第二个单词传播来的值,并利用这两者计算另一组值,依此类推。
这个示意图中的每个方框都代表了每一步所做的计算。颜色代表了每次计算所使用的信息。正如你所见,最后一步所做的计算包含了句子中所有单词的信息。在最后一步,循环神经网络能够预测出单词“answer”。
RNN的核心机制
循环神经网络的神奇之处在于,序列中每个单词的信息都乘以相同的权重 Wₓ。而从开头传播到结尾的信息则乘以权重 Wₕ。
换句话说,这个计算模块对序列中的每个单词都会重复执行。因此,唯一可学习的参数就是 Wₓ、Wₕ 以及用于做出最终预测的权重 W。
这就是它们被称为“循环”神经网络的原因:它们计算出的值会一遍又一遍地反馈给自身,直到做出预测。
RNN的主要优势在于它们能够在序列内部传播信息,并且大部分计算共享参数。
总结与预告
本节课中,我们一起学习了循环神经网络(RNN)的基本原理。我们看到,相比传统的N元语法模型,RNN能够利用更长的上下文信息来做出更准确的预测,例如在句子补全任务中。其核心机制是通过共享的权重参数,将序列中每个步骤的信息循环传递下去。

在上面的例子中,我们看到了一个从单词序列开头传播信息到结尾,并在最后做出单一预测的RNN。
下一节,我们将看看不同类型的RNN架构、简单架构背后的数学原理以及实现它们的方法。
115:RNN的应用 🧠
在本节课中,我们将学习循环神经网络(RNN)的不同应用场景。我们将根据输入和输出的特点,对任务进行分类,并了解每种任务对应的RNN架构。通过本节课,你将理解RNN如何被灵活地应用于多种自然语言处理任务中。

任务类型分类 📊
AI领域存在多种不同类型的任务。本节将根据输入和输出的性质对它们进行分组。
一对一任务
一对一任务接收一组低相关或不相关的特征 X 作为输入,并返回单个输出 Y。例如,如果你有你最喜欢的欧洲足球锦标赛球队的一系列比赛得分作为输入,你可以使用一个循环神经网络来预测你球队在排行榜上的位置。
然而,请注意,这种循环神经网络与传统神经网络没有太大区别。它仅多了一个额外的隐藏状态 H⁰。因此,对于这类任务,RNN并不是特别有用。
一对多任务
如果你想要一个神经网络接收任意图像,并生成一段用英语描述该图像内容的标题(例如,“一只棕色的小狗”),你可以构建一个RNN。这类任务被称为“一对多”,因为你的RNN接收单个图像,并生成多个词语来描述它。
多对一任务
你可能已经熟悉这类任务的一个实际例子:情感分析。在这种情况下,如果你有一个单词序列(例如,一条推文“我非常开心”)作为输入,你的模型应该输出该推文的情感是消极还是积极。😊
你的RNN会将序列中的每个单词作为不同步骤的输入,将信息从开始传播到结束,并输出情感判断。
多对多任务
最后,多对多任务涉及多个输入和多个输出。例如,在机器翻译中,你有一个法语单词序列,你想得到它在另一种语言(如英语)中的对应序列。
RNN非常适合这项任务,因为它们能将信息从开始传播到结束。这使得它们能够捕捉整个序列的总体含义。
这种特定的架构——编码器-解码器——在机器翻译中非常流行。该神经网络的第一部分不返回任何输出 Ŷ,被称为编码器,因为它将序列编码为一个单一的表示,该表示捕捉了句子的整体含义。随后,这个表示被解码为另一种语言的单词序列。
总结 📝
本节课中,我们一起学习了循环神经网络(RNN)的多种应用架构。RNN是强大的架构,可用于解决NLP中的许多有趣问题。
以下是RNN的一些主要应用领域:
- 机器翻译
- 图像描述生成

你可以将RNN视为多功能的工具,可以根据具体情况进行调整。根据你试图解决的任务,你可以选择上述架构中的一种。在下一个视频中,我将讨论一个简单的循环神经网络,然后你可以将其应用于所有这些架构。
116:简单RNN中的数学 🧮

在本节课中,我们将学习简单循环神经网络(RNN)背后的数学原理。我们将了解RNN如何通过时间步处理序列数据,以及如何进行计算和预测。课程内容将涵盖RNN的基本结构、前向传播公式以及核心计算步骤。
RNN的基本结构与思想
上一节我们介绍了RNN的优势和基本思想。RNN看似结构复杂,但其计算过程实际上相当直观。
本节中我们来看看RNN如何在时间序列中传播信息,以及如何进行序列预测。我们将熟悉基本循环单元背后的数学,为后续实现RNN的前向传播做好准备。
观察这个基础的RNN结构。它有一个典型的架构。在每个时间步,它接收一个输入 x、一个隐藏状态 H,并生成一个预测 Ŷ。此外,它还会将一个新的隐藏状态传播到下一个时间步。
隐藏状态的计算公式
每个时间步 t 的隐藏状态由一个激活函数 g 计算得出。该函数的参数等于参数矩阵 WH 与上一个隐藏状态 Ht-1 的乘积,再与输入变量 xt 拼接,最后加上一个偏置项 b。
完整的公式如下所示,其中 xt 和 Ht-1 分别与不同的参数相乘,然后将得到的向量按元素相加。
Ht = g( WH * [Ht-1, xt] + b )
在计算出时间 t 的隐藏状态后,可以通过另一个激活函数 g 来获得预测值 Ŷ。该函数的参数等于隐藏状态与另一组参数 WYH 的乘积,再加上一个偏置项。
Ŷt = g( WYH * Ht + b )
这两个公式共同构成了简单RNN的全部数学基础。
计算步骤详解
现在,让我们详细看看计算的顺序。
聚焦于RNN的第一个单元。它接收上一个隐藏状态和当前输入变量 x 作为输入,x 可能是一个句子中的第一个单词。
以下是计算当前隐藏状态和预测值的步骤:
- 首先,计算 x 和 Ht-1 与各自参数的乘积。
- 然后,将得到的两个向量按元素相加。
- 接着,将结果向量通过一个激活函数(如tanh)。
- 得到当前隐藏状态 Ht 后,将其与另一组可学习参数 WYH 相乘。
- 最后,将乘积结果通过另一个激活函数(如softmax),得到当前预测 Ŷt。
在RNN相关的文献中,你会看到类似这里的图示,它们展示了计算顺序以及信息如何在循环单元内传播。
核心概念与总结
本节课中我们一起学习了简单RNN的数学原理。需要理解的关键点是:隐藏状态是允许RNN在时间(或者说,在序列的不同位置)中传播信息的变量。正如你所见,在每个时间步,循环单元都有两个输入。

你现在已经了解了RNN的前向传播方程和成本函数。在下一节视频中,我将向你展示RNN的成本函数是如何工作的。
117:10_RNN的成本函数 📊
在本节课中,我们将学习循环神经网络(RNN)的成本函数。我们将回顾交叉熵损失,并探讨如何对其进行调整,以使其能够考虑所有时间步的预测结果。

概述
上一节我们介绍了RNN的基本结构。本节中,我们来看看如何为RNN定义成本函数,以衡量其预测的准确性。
RNN成本函数详解
观察以下序列模型。输入向量X(洋红色)经过多个隐藏单元(蓝色)处理,最终由三个输出单元(绿色)生成预测。
假设该模型输出一个观测样本属于三个不同类别的概率。
对于一个单独的(X, Y)样本对,可以使用交叉熵函数计算损失。其中K是类别数量,每个真实标签Y_j的值为1或0。
因此,要计算单个观测样本的损失,需要检查预测输出Ŷ中每个类别的概率,并将其与真实的Y值(0或1)进行比较。
普通RNN具有类似的结构,在每个时间步t,网络通过计算以下两个公式来输出向量Ŷ:
# 前向传播核心步骤(示意)
a_t = g(W_aa * a_{t-1} + W_ax * x_t + b_a)
y_hat_t = g(W_ya * a_t + b_y)
在这种情况下,交叉熵损失的计算公式如下所示。该公式与之前展示的公式的唯一区别在于,它对时间步t进行了求和,并除以总时间步数T。
公式:
L = - (1/T) * Σ_{t=1}^{T} Σ_{j=1}^{K} [ Y_{t,j} * log(Ŷ_{t,j}) ]
这实质上是在时间维度上对交叉熵损失函数取平均值。
要获得整个模型的成本,与往常一样,必须对训练集、开发集或测试集中的每个样本求和。
现在你知道了,要计算RNN模型中单个样本的损失,只需在时间维度上取平均值即可。
接下来,我将与大家分享一些实现注意事项以及更复杂的RNN架构。
实现要点
我们已经了解了如何通过对所有K个类别和所有T个时间步进行求和来修改成本函数,然后除以总步数以获得每个时间步的平均成本。

我们将在下一个视频中进一步探讨如何实现这些模型。
总结
本节课中,我们一起学习了如何为循环神经网络定义成本函数。关键点在于,RNN的损失是每个时间步上标准交叉熵损失的平均值。这确保了模型在所有时间步上的预测性能都能被有效地评估和优化。
118:实现注意事项 🧠💻

在本节课中,我们将学习如何实现循环神经网络(RNN)。我们将重点介绍一个名为 scan 的函数抽象,它类似于RNN的抽象表示,并能实现更快的计算。理解这一概念对于在深度学习框架中高效实现RNN至关重要。
什么是Scan函数及其实现方式?
上一节我们介绍了RNN的基本概念,本节中我们来看看如何具体实现它。核心在于理解 scan 函数。
scan 函数的设计目的是:接收一个函数 FN,并将其应用到列表 Ls 中的所有元素上,顺序是从头到尾。Initializer 是一个可选变量,可用于 FN 的首次计算。
在RNN的上下文中,FN 等价于前向传播函数 F_W。列表 Ls 包含所有时间步的输入 x^<t>,而 initializer 则是初始隐藏状态 h^<0>。
以下是 scan 函数在TensorFlow中实现RNN前向传播的基本逻辑:
def scan(FN, Ls, initializer):
hidden_state = initializer # 初始化隐藏状态
predictions = [] # 存储预测值的列表
for x in Ls: # 遍历输入序列的每个时间步
# 调用FN,参数为当前输入x和上一个隐藏状态
hidden_state, prediction = FN(x, hidden_state)
predictions.append(prediction) # 存储当前预测
return predictions, hidden_state # 返回所有预测和最终隐藏状态
这个函数首先将隐藏状态初始化为 h^<0>,并将存储预测值的列表设为空。然后,对于列表中的每一个输入 x,它调用函数 FN,参数是 x 和上一个隐藏状态的值。这个 for 循环计算了RNN的每一个时间步,并将预测值存储在隐藏状态中。最后,函数返回预测列表和最后一个隐藏状态。
为何需要Scan函数?
你可能会认为这个函数是多余的,因为它本质上就是一个遍历RNN每个时间步的 for 循环。然而,像TensorFlow这样的深度学习框架需要这类抽象,以便执行并行计算并在GPU上运行。
我向你展示了TensorFlow中如何定义 scan 函数来模拟RNN的工作方式。了解这类抽象对于深度学习框架是必要的,因为它们使得框架能够利用GPU进行并行计算。
总结

本节课中我们一起学习了RNN实现中的一个关键抽象——scan 函数。我们了解了它的工作原理、在代码中的实现逻辑,以及它对于在GPU上实现高效并行计算的重要性。掌握这一概念将帮助你在实际应用中更有效地构建和优化循环神经网络模型。
119:门控循环单元(GRU)🚪

在本节课中,我们将要学习门控循环单元(GRU)。这是一种比标准循环神经网络(RNN)更复杂的模型,专门设计用于处理长序列数据,并有效缓解信息消失的问题。
概述
到目前为止,我们已经熟悉了标准RNN,它是一种强大的架构。但其局限性在于,对于长单词序列,信息往往会逐渐消失。然而,存在更复杂的模型可以处理长序列,例如门控循环单元。接下来,我将向您介绍门控循环单元(简称GRU),并与标准RNN进行比较。
GRU的核心思想
GRU的一个重要区别在于,其工作方式允许相关信息即使在长序列中也能保留在隐藏状态中。例如,使用GRU,您将能够训练一个模型来处理句子“ans are really interesting, blank are everywhere”,并轻松预测单词“they”来填充空白。因为GRU将学会在隐藏状态中保留关于主语的信息(在本例中,主语是复数还是单数)。
GRU通过计算相关门和更新门来实现这一点,我将在接下来展示。
GRU的结构与计算
您可以将GRU视为具有额外计算的标准RNN。它们在每个时间步接受两个输入:时间T的变量x和从前一个单元传递来的隐藏状态h。
GRU中进行的头两个计算是相关门Γ_r和更新门Γ_u。这些门计算S型激活函数,因此它们的结果是一个被压缩到0和1之间的值向量。
相关门公式:
Γ_r = σ(W_r * [h^(t-1), x^t] + b_r)
更新门公式:
Γ_u = σ(W_u * [h^(t-1), x^t] + b_u)
GRU中的更新门和相关门是最重要的计算,它们的输出有助于确定前一个隐藏状态中的哪些信息是相关的,以及哪些值应该用当前信息更新。
计算相关门后,会找到一个隐藏状态的候选值h'。其计算参数包括前一个隐藏状态乘以相关门的结果以及当前时间的变量x。
候选隐藏状态公式:
h' = tanh(W * [Γ_r ⊙ h^(t-1), x^t] + b)
这个值存储了所有可能覆盖前一个隐藏状态中所含信息的候选信息。
之后,使用前一个隐藏状态、候选隐藏状态和更新门的信息来计算隐藏状态的新值。更新门决定了前一个隐藏状态中的信息有多少将被覆盖。
当前隐藏状态公式:
h^t = Γ_u ⊙ h' + (1 - Γ_u) ⊙ h^(t-1)
最后,使用当前隐藏状态计算预测值ŷ。
预测输出公式:
ŷ^t = softmax(W_y * h^t + b_y)
GRU与标准RNN的对比
回想一下,像这样的标准RNN计算一个激活函数,其参数是前一个隐藏状态和当前变量x,以得到当前隐藏状态。利用当前隐藏状态,再计算另一个激活函数以得到当前预测ŷ。
这种架构在每个时间步都更新隐藏状态,因此对于长序列,信息往往会消失。这就是所谓的梯度消失问题的原因之一。
另一方面,GRU的计算操作明显更多,这可能导致更长的处理时间和内存使用。相关门和更新门决定了前一个隐藏状态中的哪些信息是相关的,以及哪些信息应该被更新。候选隐藏状态存储了可用于覆盖从前一个隐藏状态传递来的信息。然后计算当前隐藏状态,并更新来自上一个隐藏状态的部分信息。最后使用更新后的隐藏状态进行预测。
所有这些计算都允许网络学习保留何种信息以及何时覆盖它。
GRU的优势与应用
我刚刚演示了GRU如何在每个时间步决定覆盖隐藏状态中的哪些信息。GRU中的相关门和更新门允许模型为更长的单词序列在隐藏状态中保留某些值。这对于许多自然语言处理任务非常有用。

GRU是流行的长短期记忆网络(LSTM)的简化版本,您将在后续的专项课程中遇到它。
总结
本节课中,我们一起学习了门控循环单元(GRU)。您现在知道,GRU与简单RNN非常相似,不同之处在于它们有两个门,允许您更新状态中的信息,并告诉您每个输入在下一个时间步的相关性。在下一个视频中,我们将讨论双向和深度RNN。
120:深度与双向RNN 🧠
在本节课中,我们将要学习两种更强大的循环神经网络变体:深度RNN和双向RNN。我们将了解它们的工作原理、数学公式,以及它们如何帮助模型捕捉更复杂的序列依赖关系。

深度循环神经网络
上一节我们介绍了基础的RNN结构。本节中我们来看看如何通过堆叠多个RNN层来构建深度循环神经网络。
深度RNN之所以有用,是因为它们能够捕捉浅层RNN无法捕获的依赖关系。深度RNN的结构类似于常规的深度神经网络,它有一个接收输入序列X的层,以及多个额外的隐藏层。
以下是构建深度RNN的核心公式。对于普通的深度RNN,我们使用以下两个方程:
对于第 l 层在时间步 t 的激活值:
a^{[l]<t>} = g(W_a^{[l]} [a^{[l]<t-1>}, a^{[l-1]<t>}] + b_a^{[l]})
对于第 l 层在时间步 t 的预测输出:
y^{[l]<t>} = g(W_y^{[l]} a^{[l]<t>} + b_y^{[l]})
这些方程与你之前见过的公式相同,但信息流动的方式有所不同。首先,你计算当前层的隐藏状态,然后获取激活值并将这些值传递给下一个隐藏层,并重复此过程。换句话说,你首先在时间上传播信息,然后在网络中深入,对每一层重复此过程,直到获得预测结果。
双向循环神经网络
理解了深度结构后,我们来看看另一种强大的变体——双向RNN。它通过从序列的两个方向获取信息来提升模型性能。
为了说明双向RNN的重要性,请看以下例子:
“我非常努力地想联系上______。就在我快要放弃时,路易丝终于回电话了。”
作为一个聪明人,你无需费力就能填上空白。一个从序列开始到结束传播信息的RNN也能做出预测。它会将空白前的单词作为输入,并尽力预测缺失的单词。然而,由于“路易丝”直到下一句开头才出现,模型将不得不在“她”、“他”和“他们”之间猜测。
双向RNN的工作方式与简单RNN大致相同。它们接收输入序列X并生成预测Y_hat。在我之前展示的RNN中,信息从序列的开头流向结尾。但是,你可以有另一种架构,让信息从结尾流向开头。想象一下从未来流向现在。当信息双向流动时,那就是一个双向RNN。
需要知道的是,这是一个无环图。这意味着信息在两个方向上独立流动,因此从左到右的计算完全独立于从右到左的计算。
为了在双向RNN中获得时间步t的预测y_hat,你必须从两个方向开始传播信息。当你计算出一个时间步的两个隐藏状态后,就可以得到该时间步的预测Y_hat。
以下是使用的公式(与单向或普通RNN使用的公式相同,但这次拼接了两个隐藏状态):
y^{<t>} = g(W_y [\overrightarrow{a}^{<t>}, \overleftarrow{a}^{<t>}] + b_y)
在你计算出两个方向的所有隐藏状态后,就可以得到所有剩余的预测。
总结与回顾
本节课中我们一起学习了两种有趣且有用的RNN变体。
- 双向RNN:在时间上同时从未来和过去传播信息,能够利用整个序列的上下文,从而在某些任务(如填空)上表现更佳。
- 深度RNN:通过堆叠多个RNN层,可以帮助你解决比浅层神经网络更复杂的任务。

这两种架构都是你已经见过的普通RNN模型中衍生出的相对简单的组合。因此,理解它们的数学原理并不是一个巨大的概念飞跃。
恭喜你完成本周的学习!你已经了解了RNN、门控循环单元、双向RNN和深度RNN。在练习中,你将有机会实际操作RNN,并了解它们是如何具体实现的。祝你好运!🚀
122:第15周介绍 🧠
在本节课中,我们将要学习自然语言处理中的一个重要任务——命名实体识别。我们将了解它的定义、应用场景以及实现它的核心技术。
概述 📋
命名实体识别是信息抽取的一个子任务,它旨在定位并分类文本中的命名实体。这些实体可以是组织、人名、地点、时间等具体信息。
什么是命名实体识别? 🔍
命名实体识别,简称NER,其核心任务是定位并分类文本中的命名实体。
例如,观察以下句子:
The French people are visiting Morocco for Christmas.
在这个句子中:
- “French” 被识别为一个地缘政治实体。
- “Morocco” 被识别为一个地理实体。
- “Christmas” 被识别为一个时间指示器。
命名实体识别的应用 💡
当我们能够识别文本中的命名实体后,就可以实现多种应用。
以下是命名实体识别的一些典型应用场景:
- 信息快速解析:给定一篇文章,可以快速找出其中提到的人物、国家和组织。
- 市场调研:通过爬取网络信息并识别相关实体,可以对特定主题进行市场研究。
实现技术:长短期记忆网络(LSTM) ⚙️
在实现本周的命名实体识别任务时,你将使用长短期记忆单元,即LSTM。
LSTM与RNN和GRU类似,但它拥有更复杂的门控机制,使其功能更强大。其核心公式可以简化为对细胞状态和隐藏状态的控制:
细胞状态更新 和 门控机制(遗忘门、输入门、输出门)。
在后续课程中,Yonas老师将为你详细讲解LSTM的工作原理。
总结 🎯
本节课我们一起学习了命名实体识别的基础知识。我们了解了NER的定义,它如何从文本中提取如组织、人名、地点和时间等关键信息,并看到了它的实际应用价值。最后,我们简要介绍了实现NER将要用到的核心技术——长短期记忆网络。
接下来,让我们开始深入探索命名实体识别的具体实现吧。
123:RNN 与梯度消失 🧠

在本节课中,我们将要学习循环神经网络(RNN)在训练过程中可能遇到的一个核心问题:梯度消失与梯度爆炸。我们将探讨其成因,并简要介绍几种应对策略,为后续学习更强大的长短期记忆网络(LSTM)打下基础。
传统RNN的优缺点
上一节我们介绍了RNN的基本工作原理。现在,我们来总结一下它的优缺点。
优点:
- 捕捉短期依赖:普通(或“朴素”)RNN通过回忆临近过去的信息来建模序列,这使其能在一定程度上捕捉序列中的依赖关系。
- 相对轻量:与其他序列模型相比,RNN结构相对简单,占用的存储空间和内存更少。
缺点:
- 难以捕捉长期依赖:RNN的架构使其难以有效捕捉序列中相隔较远的元素之间的依赖关系。
- 梯度问题:RNN容易受到梯度消失和梯度爆炸的影响,这两种问题都可能导致模型训练失败。
梯度问题的成因:随时间反向传播
要理解梯度问题,我们需要了解RNN如何通过“随时间反向传播”来更新权重。这听起来复杂,但其本质就是多次应用链式法则。
RNN从序列的第一个词(最左侧的隐藏状态)开始,将信息逐步传播到序列末尾。计算过程如下:
- 首先计算第一个词的隐藏状态值。
- 然后,结合第一步的计算信息和序列中的第二个词,计算新的隐藏状态值。
- 接着,结合前两步的信息和第三个词,继续计算。
- 此过程持续进行,直到序列末尾。在最后一步,计算包含了序列中所有词的信息,RNN据此预测下一个词。
需要注意的是,在RNN中,第一步计算的信息对最终输出的影响非常微弱。你可以看到,代表第一步信息的“橙色部分”随着每个新步骤的加入而不断减小。相应地,第一步的计算对损失函数的影响也很小。
在计算权重 W_H 的梯度时(权重 W_X 同理),梯度取决于序列中每一步的计算。具体而言,梯度与一系列隐藏状态偏导数的乘积之和成正比。
这个关系可以通过链式法则推导得出,但我们更应关注其背后的含义。求和内部的项(偏导数的乘积)代表了第 k 个隐藏状态对梯度的贡献。对于每个 k,这个乘积序列的长度正比于该步骤 k 与计算损失的最终步骤 T 之间的距离。
因此,当你观察那些距离损失计算点很远的隐藏状态时,其偏导数的乘积会变得越来越长。例如,一个距离步骤 T 有10步之遥的隐藏状态对梯度的贡献,需要计算10个项的乘积。
关键问题在于:
- 如果这些偏导数的值小于1,那么随着距离损失计算点越来越远,该隐藏状态对梯度的贡献将趋近于0。这就是梯度消失。
- 如果这些偏导数的值大于1,那么贡献将趋向于无穷大。这就是梯度爆炸。
梯度消失会导致RNN忽略序列早期步骤计算的值,而梯度爆炸则会在训练期间导致收敛问题。
应对梯度问题的策略
既然我们已经了解了梯度消失和爆炸的可怕之处,现在来讨论一些解决方案。本周课程的重点是介绍专门为解决此问题而设计的模型(LSTM),因此这里只做简要概述。
以下是几种应对策略:
应对梯度消失:
- 恒等初始化与ReLU激活:将权重初始化为恒等矩阵(主对角线为1,其余为0),并使用ReLU激活函数。这种方法本质上是在复制先前的隐藏状态,并加入当前输入的信息,同时将负值替换为零。它鼓励网络在矩阵乘法中保持接近1的值,这种方法被称为恒等RNN。但请注意,恒等RNN方法主要针对梯度消失问题,因为ReLU在正值区的导数恒为1。
应对梯度爆炸:
- 梯度裁剪:为了控制梯度值呈指数级增长,可以进行梯度裁剪。具体操作是选择一个阈值(例如25),任何梯度值超过此阈值时,都会被裁剪到该阈值。这限制了梯度的大小。
通用策略:
- 残差连接:残差连接提供了到更早层的直接连接。它有效地跳过了激活函数,并将初始输入
x的值加到输出上,即F(x) + x。这样,早期层的激活对损失函数的影响更大。

总结
本节课我们一起学习了RNN的核心挑战:梯度消失与梯度爆炸。我们了解了这个问题是如何通过“随时间反向传播”中长链的偏导数乘积产生的,并简要探讨了恒等初始化、梯度裁剪和残差连接等应对策略。

现在你已经理解了RNN为何会存在梯度消失问题。接下来,我将向你展示一个更强大的解决方案:长短期记忆网络(LSTM)。让我们进入下一个视频。
124:LSTM介绍 🧠

在本节课中,我们将学习长短期记忆网络(LSTM),这是一种专门设计用于处理序列数据的循环神经网络变体。我们将了解LSTM的基本架构、核心组件及其在现实世界中的应用。
概述
LSTM是解决梯度消失问题的最著名方案。它是一种特殊的循环神经网络,通过学习何时记住和何时遗忘信息来处理整个数据序列,其功能与GRU类似。
LSTM的基本架构
LSTM本质上由细胞状态和隐藏状态组成。细胞状态可视为网络的记忆,而隐藏状态则用于在训练期间执行计算以决定进行哪些更改。LSTM包含多个门,这些门负责在网络中转换状态。
细胞状态通过这些门传递,跟踪输入信息。每个门在决定传递多少信息以及保留多少信息方面都发挥着作用。这一系列门允许梯度流动,避免了梯度消失或爆炸的风险。
类比理解
为了更直观地理解这一概念,我们可以将其与一个熟悉的场景进行类比。
想象你接到最好朋友的电话。当手机响起时,你可能正在思考与朋友无关的各种事情。接听电话后,你会暂时搁置那些无关的想法,同时保留与你们两人相关的任何信息。随着朋友告诉你他打电话的原因,你会思考其他可能相关的话题。
利用朋友的输入和你脑海中任何相关的信息,你开始与他交谈。然后,你们以这种方式继续对话,直到挂断电话。通话结束时,你将拥有对话中最相关信息的记忆。
这就是LSTM的工作原理:它使用多个门不断更新细胞状态和隐藏状态,以便相关信息在网络中传递并用于产生输出。
LSTM的核心组件
以下是LSTM单元的基本解剖结构及其各部分的功能。
LSTM将信息存储在细胞状态和隐藏状态中,分别用 C 和 H 表示。与普通RNN一样,LSTM也有输入 x 和输出 y。在LSTM中,单个单元内执行多个计算,信息首先通过三个不同的门流动。
首先,细胞状态通过遗忘门。在这一步中,输入和先前的隐藏状态用于决定细胞状态中哪些信息不再重要并将其丢弃。
接着,输入门用于决定输入和先前隐藏状态中哪些信息是相关的,并将其添加到细胞状态中。
最后,输出门确定从细胞状态中提取哪些信息存储在隐藏状态中,并用于在给定时间步构建输出。
LSTM的应用
在深入探讨数学原理之前,我们先讨论LSTM的一些应用。
正如你可能猜到的,LSTM非常适用于构建语言模型,涵盖从预测电子邮件下一个字符到构建能够记住较长对话的聊天机器人等多种用例。
LSTM也可用于音乐创作,考虑到音乐是由长序列的音符构建而成,类似于文本使用长序列的单词,这种方法非常合理。
其他酷炫的应用包括自动图像标注和语音识别。
在克服传统RNN问题的过程中,LSTM已成为一种极其流行的工具,具有广泛的用途,并以一些令人兴奋的方式推动了自然语言处理的发展,我将在本周晚些时候详细讨论这些内容。
总结

本节课中我们一起学习了LSTM的基本概念。LSTM是解决梯度消失问题的一种方案,典型的LSTM包含细胞状态和隐藏状态以及三个门:遗忘门、输入门和输出门。
现在你已从高层次理解了LSTM,但LSTM计算背后的具体数学原理是什么呢?我将在下一个视频中向你展示。
125:LSTM架构详解 🧠

在本节课中,我们将深入学习长短期记忆网络(LSTM)的内部架构和计算过程。LSTM是一种特殊的循环神经网络,能够有效处理序列数据中的长期依赖问题。
LSTM的核心组件
回忆之前的内容,一个典型的LSTM单元包含一个细胞状态、一个隐藏状态,以及输入x和输出y。你可以将细胞状态视为网络的记忆,它会根据输入和前一时刻的隐藏状态信息进行更新。
LSTM中的门控机制决定了哪些信息被保留或添加,并在每一步选择输出。通常有三个门:
- 遗忘门:决定从上一个细胞状态中保留或丢弃哪些信息。
- 输入门:从当前输入和前一隐藏状态中选择相关信息。
- 输出门:决定细胞状态中的哪些信息被用作输出,并存储在隐藏状态中。
门控机制的计算
对于这三个门,我们需要对输入和前一状态应用带有不同可训练参数的Sigmoid激活函数。
以下是门控计算的通用形式:
gate = sigmoid(W * [h_prev, x] + b)
使用Sigmoid函数确保了门的输出值在0到1之间。在实践中,每个门对应的向量值通常非常接近0或1。值为0意味着门关闭,信息无法通过;值为1则允许信息自由流动。
候选细胞状态
LSTM内部的另一个重要计算是候选细胞状态。为了得到它,你需要对来自前一隐藏状态和当前输入的信息进行变换。
在大多数实现中,通常使用双曲正切激活函数:
candidate = tanh(W_c * [h_prev, x] + b_c)
它将来自前一隐藏状态和当前输入的信息压缩到-1到1之间。这种非线性变换在过去被广泛使用,因为它能提升训练性能。当然,你也可以尝试其他激活函数并测试其效果。
更新细胞状态与隐藏状态
有了遗忘门、输入门和候选细胞状态,我们就可以更新细胞状态了。
新的细胞状态计算如下:
c_new = forget_gate * c_prev + input_gate * candidate
它将通过输入门的候选细胞状态信息,与通过遗忘门的上一细胞状态信息相加,从而得到新的细胞状态。
最后,我们可以计算用于在给定步骤产生输出的新隐藏状态。
新的隐藏状态计算如下:
h_new = output_gate * tanh(c_new)
请注意,这里新的细胞状态首先会经过一个双曲正切激活函数。不过,有些LSTM架构会忽略这一步,直接将新的细胞状态通过输出门。
本节总结

本节课我们一起学习了LSTM的核心架构。LSTM通过三个门控机制来决定网络中传递哪些信息:
- 遗忘门决定保留什么。
- 输入门决定从上一隐藏状态和当前输入中添加什么。
- 输出门决定下一个隐藏状态和当前步骤的输出。
你现在已经理解了LSTM背后的基本原理。在本周的编程练习中,你将亲手实现LSTM。在下一个视频中,我将展示如何使用LSTM来解决一个真实的自然语言处理任务。让我们进入下一个视频。
126:命名实体识别介绍 🏷️

在本节课中,我们将要学习命名实体识别。这是一种在自然语言处理中用于快速扫描文本、定位并提取特定类型信息的重要技术。许多NLP系统都包含NER组件。
什么是命名实体识别?
命名实体识别是一种快速、高效地扫描文本以寻找特定信息的方法。NER系统能够从文本中定位并提取出命名实体。
命名实体可以是任何事物,从一个地点、一个组织,到一个人名。它们甚至可以是时间和日期。
命名实体的类别标签
上一节我们介绍了NER的基本概念,本节中我们来看看NER系统所使用的具体类别标签,这些标签与你本周的编程作业相关。
以下是你在NER任务中可能会遇到的一些类别示例:
- 地理实体:例如“泰国”。
- 组织:例如“谷歌”。
- 地缘政治实体:例如“印度”。
- 时间指示器:例如“十二月”。
- 人造物:例如“这座古埃及雕像”。
- 人物:例如“巴拉克·奥巴马”。
正如你所见,命名实体可以捕捉和识别各种类型的事物。
NER系统如何工作?
了解了类别之后,我们来看看NER系统是如何运作的。假设你有下面这个句子:
“莎朗上周五飞往迈阿密。”
一个命名实体识别系统会提取并分类其中的实体:
- “莎朗”被分类为人名。
- “迈阿密”被分类为地理实体。
- “周五”被分类为时间指示器。
你会注意到,在这个例子中,“the”和“to”等词没有被标记为任何实体类别,它们被分类为“O”,代表填充词或非实体词。
你或许已经能看出这对于内容分类是多么有用。在需要从大量文本中搜索特定词语(例如“曼哈顿”和“布鲁克林”,或“丰田”和“斯巴鲁”)的场景下,NER能迅速派上用场。
命名实体识别的实际应用
NER系统在现实世界中有许多应用。我提到过它们能快速扫描文档以寻找特定事物,因此可以想象NER如何优化搜索引擎的效率。
以下是NER的一些具体应用场景:
- 优化搜索引擎:NER模型可以一次性扫描数百万个网站,并存储其识别出的实体。然后,你的搜索查询中的标签只需与网站标签进行匹配即可。
- 内容推荐:同样的概念可以扩展到推荐系统。标签可以从你的历史记录中提取,并与相似用户的历史记录进行比较,从而推荐你可能想查看的内容。
- 客户服务匹配:这也适用于将客户匹配到合适的客服人员。其工作原理类似于电话树系统,系统会提示你提供一些关于你需求的信息。例如,如果你使用一个应用程序与你的汽车保险公司沟通,你可能会提供一些语音或文字信息,如“我需要添加另一辆车”或“我的车被一只巨大的飞鼠毁了”。系统会根据这些信息将你匹配到相应的客服人员。
- 自动化交易:另一个有趣的用例是自动化交易。例如,你可以构建一个网络爬虫来获取包含某些CEO、股票或加密货币名称的新闻文章,然后将这些文章输入到一个情感分类系统中,并据此进行交易。
NER系统在深度学习领域有许多应用,以上只是其中一个潜在的价值创造示例。
总结
本节课中,我们一起学习了命名实体识别。你现在知道了NER是什么,以及它在许多领域中的应用。看到这项技术被广泛应用,确实非常有趣。

在下一个视频中,你将亲手实现你自己的NER系统。
127:命名实体识别(NER)训练数据处理 🏷️

在本节课中,我们将学习如何为命名实体识别(NER)系统准备训练数据。我们将涵盖从原始文本到数字数组的完整转换流程,包括数据批处理生成器的创建,以便高效地训练模型。
数据转换步骤
上一节我们介绍了NER的基本概念,本节中我们来看看如何将文本和标签转换为模型可处理的数字格式。以下是核心步骤:
首先,为每个实体类别分配一个唯一的数字编号。
例如:
- 人名(Personal Name)可能对应数字 1
- 地理位置(Geographical Location)可能对应数字 2
- 时间指示器(Time Indicator)可能对应数字 3
接下来,为句子中的每个单词分配一个数字,该数字对应其所属的实体类别。
以句子“Sharon flew to Miami last Friday”为例,经过标记化(tokenization)和标签分配后,你会得到如下对应的数字序列:
Sharon (人名) -> 1
flew (非实体) -> 0
to (非实体) -> 0
Miami (地理位置) -> 2
last (时间) -> 3
Friday (时间) -> 3
这里的 0 对应“O”类别,表示填充词或未被识别的单词。请注意,你在此处看到的数字是随机的,它们是在你处理数据时被分配的。
从这一点出发,每个句子序列都被转换成一个数字数组,其中的每个数字都对应标签化单词的索引。
因此,从一个如下所示的标签化句子,你将把它转换成一个数值数组。
处理序列长度与填充
在处理数据集时,你需要将词汇表中的每个单词表示为一个数字,正如你刚才所见。这为你提供了数值数组。
但如果数组的长度不同,你需要处理这个问题。为了解决这个问题,你可以将序列长度设置为一个固定值,并添加一个通用的填充标记(pad token)来填充所有空白位置。
你将在作业中实现这一点。但不用担心,这是一个相对简单的过程,我会确切地展示如何操作。
创建数据生成器与模型输入
为了训练这个命名实体识别系统,你首先需要为每个输入及其对应的标签创建一个张量,正如之前所见。
然后,创建一个数据生成器(data generator)以批次(batch)的形式输出它们。批次大小可以是任何你想要的数值,但通常你会希望批次大小是2的幂,例如 64、128、256、512 等。这样做将显著加快你的处理速度。
如果你对“创建数据生成器”这个词感到不安,请不要担心。后续会有更多相关内容。
模型架构与预测
在生成批次数据后,将它们输入到一个LSTM单元中。
你将把LSTM的输出传递到一个密集层(Dense Layer)或全连接层(Fully Connected Layer)。
然后,在K个可能的类别上使用对数Softmax(Log Softmax)进行预测,其中K对应于可能输出的数量。在这里使用对数Softmax而不是普通的Softmax很重要,因为对数Softmax能提供更好的数值性能和梯度优化。
让我们看一下这个过程的示意图:
- 输入层:包含你在处理数据时创建的数组。
- LSTM层:输入数据经过带有系列激活函数和逐元素操作的LSTM层。
- 密集层:将LSTM的输出通过一个最终的密集层,这是对输入向量的线性操作。
- 输出层:计算LogSoftmax以获得相应的输出。
以下是在本周作业中实现这些层时可能的样子。对于你的密集层,你需要传入刚刚创建的标签数组的维度。
核心任务总结
现在,你已经准备好完成一些重要的任务了,包括:
- 带标记填充的数值数组转换
- 使用批次训练以加速处理
- 将你的模型通过最终的密集层和激活函数运行
自本课程开始以来,你已经走了很长的路,只剩下一点点了。

课程总结
本节课中,我们一起学习了如何从头开始训练一个NER系统。我们详细介绍了将文本和实体标签转换为数字数组、处理变长序列、创建批处理数据生成器,以及模型的基本前向传播流程。在下一个视频中,我将向你展示如何评估你的系统。
128:21_计算准确率 🎯

在本节课中,我们将学习如何评估一个已训练好的命名实体识别系统。我们将介绍从获取模型预测到最终计算准确率的完整流程。
概述
上一节我们介绍了NER模型的训练过程。本节中,我们来看看如何评估模型的性能。评估的核心在于将测试集输入模型,获取预测结果,并将其与真实标签进行比较。
评估步骤
以下是评估NER模型准确率的主要步骤。
- 获取预测:首先,将测试集数据输入到已训练好的模型中,以获取模型的预测输出。
- 计算Argmax:接着,对预测输出应用
argmax函数,找出每个位置上概率最大的类别索引,从而得到具体的预测标签。 - 处理填充:由于输入序列通常经过填充以达到统一长度,需要识别并屏蔽这些填充标记,确保它们不参与准确率计算。
- 计算准确率:最后,将模型预测的标签与测试集的真实标签进行比较,计算模型预测正确的比例。
核心操作详解
现在,我们来详细看看每个步骤在代码中如何实现。
1. 获取模型预测
将预处理好的测试集数据输入模型,模型会输出每个位置上各个类别的预测概率。
# 假设 model 是已训练好的模型,test_data 是测试集输入
predictions = model.predict(test_data)
2. 计算Argmax
使用argmax函数从预测概率中找出最大值的索引,即模型预测的类别。axis参数用于指定在哪个维度上执行此操作,这取决于你的数据维度。
# 假设 predictions 的形状为 (样本数, 序列长度, 类别数)
pred_labels = np.argmax(predictions, axis=-1)
3. 屏蔽填充标记
在序列处理中,我们通常使用一个特殊的填充标记来统一序列长度。在计算准确率时,需要创建一个掩码来忽略这些填充位置。
# 假设 pad_token 是填充标记的ID
# test_data 是原始输入数据
mask = test_data != pad_token
4. 计算准确率
准确率的计算方法是:统计所有非填充位置上预测正确的标记数量,然后除以所有非填充标记的总数。
# 假设 true_labels 是真实的标签
correct_predictions = (pred_labels == true_labels) & mask
accuracy = np.sum(correct_predictions) / np.sum(mask)
总结

本节课中我们一起学习了如何评估NER模型的性能。关键步骤包括:在测试集上进行预测、通过argmax获取预测标签、屏蔽填充标记以避免干扰,最后通过比较预测与真实标签来计算准确率。掌握这个流程后,你现在已经准备好完成相关的编程作业了。祝你好运,并享受编码的乐趣!😊
131:孪生网络 🤖
在本节课中,我们将学习一种特殊的神经网络架构——孪生网络。这是一种由两个完全相同的神经网络组成,并在末端合并的模型。我们将探讨其工作原理、在自然语言处理中的应用,以及如何用它来比较文本序列的语义。

孪生网络简介
上一节我们提到了神经网络的基本概念,本节中我们来看看孪生网络的具体结构。
孪生网络是一种由两个结构相同、参数共享的神经网络组成的架构。这两个网络并行处理两个不同的输入,最终通过一个合并层(如计算距离或相似度)产生一个输出。
其核心思想可以表示为以下流程:
输入A → 神经网络f → 向量表示v_A
输入B → 神经网络f → 向量表示v_B
合并层:计算 d = distance(v_A, v_B) 或 s = similarity(v_A, v_B)
这种架构在自然语言处理中有许多应用。在接下来的内容中,你将看到可以使用它的不同场景。
为何需要比较语义?
考虑以下两个问题:
- How old are you?(你多大了?)
- What is your age?(你的年龄是多少?)
你可以看到这两个问题没有任何相同的单词,但它们表达的意思完全相同。
另一方面,请看以下两个问题:
- Where are you from?(你来自哪里?)
- Where are you going?(你要去哪里?)
你可以看到前三个单词完全相同,但最后一个单词彻底改变了每个问题的含义。
这个例子表明,比较语义并不像单纯比较单词那么简单。接下来,你将看到如何使用孪生网络来比较单词序列的含义,并识别重复问题,这是Stack Overflow或Quora等平台核心的、非常重要的NLP应用。
在这些平台允许你发布新问题之前,它们需要确保你的问题没有被其他人发布过。
从分类到比较
现在,以这句话为例:“I am happy because I am learning.”(我很高兴,因为我在学习。)在情感分析和二元分类的背景下考虑它。
在训练分类算法时,你需要发现哪些特征使该陈述具有积极或消极的情感。
而对于孪生网络,你的目标是识别什么使你的输入相似,什么使它们不同。请看这两个问题:“What is your age?”和“How old are you?”。
当你构建一个孪生模型时,你试图识别这两个问题之间的差异或相似性。你通过计算一个单一的相似度分数来实现这一点,该分数代表两个问题之间的关系。基于该分数与阈值的比较,你可以预测这两个问题是相同还是不同。
孪生网络的应用
以下是孪生网络在NLP中的一些主要应用场景:
- 笔迹与签名验证:通过判断两个签名是否相同,用于支票等文件的笔迹认证。
- 识别重复问题:用于Quora或Stack Overflow等平台,识别用户提出的问题是否与已有问题重复。
- 搜索引擎查询:用于预测一个新的搜索查询是否与已执行过的查询相似。
这些只是少数例子,孪生网络在NLP中还有更多应用。

总结
本节课中,我们一起学习了孪生网络。我们了解到它是一种由两个相同子网络组成的架构,用于比较两个输入的相似性,而非直接进行分类。我们探讨了比较语义的复杂性,并介绍了该模型在验证、去重和搜索等NLP任务中的重要应用。
在下一个视频中,我将带你深入了解这类模型所使用的具体架构,并展示如何将其应用于文本处理。我们下个视频见。
132:25_架构 🏗️

在本节课中,我们将学习一种特殊的神经网络架构——孪生网络。我们将了解其核心结构、工作原理以及如何通过余弦相似度来比较两个输入。
孪生网络架构概述
孪生网络是一种特殊的架构。它包含两个完全相同的子网络,这两个子网络最终合并,以产生一个最终输出或相似度得分。我们可以将这两个子网络视为“姐妹网络”,它们共同协作来生成一个相似度分数。
以下是孪生网络的一个模型架构示例。请注意,这里展示的架构只是一个例子,并非所有孪生网络都设计为包含LSTM层。

架构详解
在左侧,有两个输入,分别代表问题1和问题2。每个问题都会经过以下处理流程:首先被转换为词嵌入向量,然后通过一个LSTM层来建模问题的含义。
每个LSTM层都会输出一个向量。因此,在这个架构中,有两个完全相同的子网络:一个用于处理问题1,另一个用于处理问题2。
这里有一个重要的注意事项:这两个子网络共享完全相同的参数。也就是说,每个子网络学习到的参数是完全一样的。因此,实际上你只需要训练一组权重,而不是两组。
计算相似度
得到两个输出向量后,每个向量对应一个问题,接下来需要计算它们之间的余弦相似度。
余弦相似度是衡量两个向量之间相似程度的一个指标。当两个向量大致指向相同方向时,它们之间夹角的余弦值接近1。当两个向量指向相反方向时,余弦值接近-1。如果你对这个概念不熟悉,不必担心。现在你只需要知道,余弦相似度可以告诉你两个向量有多相似。在这个场景下,它告诉你两个问题有多相似。
因此,余弦相似度给出了孪生网络的预测值,这里用变量 y_hat 表示,其值域在-1到1之间。
判断逻辑
如果 y_hat 小于或等于某个阈值 τ,那么你会判定输入的两个问题是不同的。如果 y_hat 大于 τ,那么你会判定它们是相同的。
阈值 τ 是一个需要你根据实际情况选择的参数。它决定了你希望多高的余弦相似度才被视为两个问题相似。阈值越高,意味着只有非常相似的句子才会被判定为相似。
处理流程总结
如果将这个过程视为从输入到输出的一系列步骤,可以总结如下:
- 从孪生网络的模型架构开始,它由两个相同的子网络构成。
- 输入是两个问题,分别馈入每个子网络。
- 每个问题被转换为词嵌入,并通过LSTM层。
- 取每个子网络的输出,使用余弦相似度进行比较,得到预测值 y_hat。
成本函数简介
在了解了模型架构之后,我们将在下一节开始讨论可以用于此类架构的不同成本函数。


本节课总结
本节课我们一起学习了孪生网络的基本架构。我们了解到它由两个参数共享的相同子网络组成,用于处理两个输入。通过计算两个子网络输出向量的余弦相似度,我们可以得到一个介于-1和1之间的相似度得分 y_hat。最后,通过设定一个阈值 τ,我们可以根据 y_hat 来判断两个输入是否相似。
133:26_成本函数 🧮

在本节课中,我们将学习用于孪生网络(Siamese Network)的成本函数,即三元组损失(Triplet Loss)。我们将了解其工作原理、构成要素以及如何通过它来训练模型,使其能够有效区分相似与不相似的文本。
概述
上一节我们介绍了孪生网络的基本结构,它用于预测两个问题是否相似。本节中,我们来看看如何通过一个特定的成本函数——三元组损失——来训练这个网络,使其学习到有意义的文本表示。
三元组损失函数
我将展示一个可用于孪生网络的简单损失函数。首先,回顾一下孪生网络的整体结构,它使你能够预测两个问题是否相似。通过网络的输出,你可以计算预测值 y_hat,它代表了两个问题之间的相似度。
现在,我将介绍用于孪生网络的损失函数。让我们从第一个问题“How old are you?”开始,我将其称为锚点(Anchor)。我们将用这个锚点来与其他两个问题进行比较。
相对于锚点,其他与锚点含义相同的问题被称为正例(Positive),而与锚点含义不同的问题则被称为负例(Negative)。
需要注意的是,在检测问题重复性的上下文中,“正”和“负”指的是问题是否与锚点相似,而不是指情感上的积极或消极。
因此,问题“What is your age?”相对于锚点被视为正例,因为“How old are you?”和“What is your age?”含义相同。而问题“Where are you from?”则被视为负例,因为它与锚点问题的含义不同。
余弦相似度
以下是两个向量之间余弦相似度的定义公式:
相似度 = (A · B) / (||A|| * ||B||)
这个相似度函数记为 S。为了训练你的模型,你将使用相似度函数来比较每个子网络输出的向量。
对于这个例子,你需要计算锚点 A 和正例 P 之间的相似度 S(A, P),以及锚点 A 和负例 N 之间的相似度 S(A, N)。
相似度的值域在 -1 到 1 之间。对于完全不同的向量,相似度接近 -1;对于几乎相同的向量,相似度接近 1。
因此,对于一个训练良好的模型,你希望看到锚点和正例之间的相似度接近 1。同样地,当比较锚点和负例时,一个成功的模型应该产生接近 -1 的相似度。
构建损失函数
为了开始构建损失函数,你从锚点与负例的相似度 S(A, N) 中减去锚点与正例的相似度 S(A, P),来计算这个差值。
差值 = S(A, N) - S(A, P)
这样你就得到了一个损失函数,它允许你判断模型是否大致按照你的期望工作,即发现锚点和正例相似,而锚点和负例不同。
随着差值沿X轴变大或变小,损失值沿Y轴相应地变大或变小。因此,在训练中最小化损失,实际上就是在最小化这个差值。
在下一个视频中,我将介绍三元组(Triplets)的概念。
总结

本节课中,我们一起学习了孪生网络的核心成本函数——三元组损失。我们定义了锚点、正例和负例,并利用余弦相似度来衡量它们之间的关系。通过构建 S(A, N) - S(A, P) 的差值作为损失,我们引导模型学习区分相似与不相似的文本对,这是训练高效语义匹配模型的关键一步。
134:三元组损失 🎯

概述
在本节课中,我们将要学习一种用于训练语义相似度模型的特殊技术——三元组损失。我们将了解如何构建由锚点、正例和负例组成的三元组,并利用它们来训练模型,使其能够准确判断两个输入是否具有相同的含义。
三元组与损失函数
上一节我们介绍了模型的目标是判断两个输入是否等价。本节中我们来看看如何通过构建三元组来训练模型。
三元组由三个部分组成:
- 锚点:一个基准输入。
- 正例:一个与锚点含义相同的输入。
- 负例:一个与锚点含义不同的输入。
三元组损失函数的核心思想是:最小化锚点与负例之间的相似度,同时最大化锚点与正例之间的相似度。
用公式可以表示为,我们希望:
相似度(锚点, 正例) - 相似度(锚点, 负例) 这个差值尽可能大。
一个简单的损失函数可以直接基于这个差值来构建。然而,这个简单的函数存在一个问题:当差值小于0时,损失会变为负值,这可能导致模型过早停止学习。
引入边界值
我们不希望模型仅仅满足于将正负例区分开,而是希望它能以一定的“自信度”进行区分。为此,我们引入一个边界值 alpha。
新的损失函数要求:相似度(锚点, 正例) - 相似度(锚点, 负例) + alpha >= 0。
这意味着,即使锚点与正例的相似度已经大于锚点与负例的相似度,只要它们的差值没有超过边界值 alpha,模型仍然会收到损失信号,需要继续学习以拉开这个差距。alpha 是一个超参数,你可以根据需要调整它。
以下是三元组损失函数的最终形式,我们将在编程作业中实现它:
损失 = max(相似度(A, N) - 相似度(A, P) + alpha, 0)
注意:在本次课程中,我们使用余弦相似度作为相似度度量。你也可以使用其他度量方式(如欧氏距离),但需注意距离与相似度是相反的概念:距离越小,相似度越高。
三元组的选择策略
我们已经了解了损失函数如何工作。接下来,我们看看如何为训练选择有效的三元组。
以下是选择三元组的两个基本步骤:
- 从训练集中选择一对已知为同义的问题,分别作为锚点和正例。
- 选择一个已知与锚点含义不同的问题,作为负例。
如果随机选择三元组,很多三元组的损失可能接近零,模型无法从中学习。为了提高训练效率,我们应该选择那些对模型更具挑战性的三元组,即困难三元组。
困难三元组是指:锚点与负例的相似度非常接近(但仍小于)锚点与正例的相似度。面对这样的三元组,模型必须努力调整其权重,才能正确地区分它们。通过专注于困难三元组,我们可以让训练过程更高效地集中在模型表现不佳的案例上。

总结
本节课中我们一起学习了三元组损失的核心概念。我们首先了解了锚点、正例和负例如何构成一个训练样本。接着,我们探讨了如何设计损失函数来迫使模型拉开正负例与锚点的相似度差距,并引入了边界值 alpha 来确保模型学习的充分性。最后,我们讨论了选择困难三元组的重要性,这能引导模型专注于最具挑战性的样本,从而更有效地提升性能。在接下来的课程中,我们将看到这些概念如何整合成一个完整的模型训练流程。
135:计算成本I 💰

在本节课中,我们将学习如何为连体网络构建成本函数,并了解如何通过梯度下降来优化它。我们将从准备数据和批次开始,逐步理解模型如何计算向量间的相似度,并最终形成用于训练的成本。
准备数据与批次
上一节我们介绍了连体网络的基本结构,本节中我们来看看如何为模型准备输入数据。
以下是构建批次的一个示例,其中包含四对意思相同的问题(即重复对):
- What is your age 与 How old are you
- Can you see me 与 Are you seeing me
- Where are thou 与 Where are you
- When is the game 与 What time is the game
这里,批次大小 B 为 4。一个重要的细节是:在每一行中,左右两列的问题是互为重复的。但在任何一列中,上下行之间的问题则互不重复。
模型处理与向量输出
准备好批次后,接下来我们将数据输入模型。
第一个批次的问题会通过模型,得到一个输出矩阵 V1。其维度为 B 行 × D_model 列,其中 D_model 是嵌入层的维度(本例中为5)。这意味着每个问题都被转换成了一个5维的向量。
同理,第二个批次的问题会通过相同的模型(共享权重),得到另一个输出矩阵 V2。V1 中的每一行向量都与 V2 中对应行的向量是重复对,但 V1 内部各行向量之间互不重复,V2 内部也是如此。
计算相似度矩阵
现在,我们需要结合连体网络的两个分支,计算 V1 和 V2 中所有向量组合之间的相似度。
对于这个批次大小为4的例子,我们会得到一个4×4的相似度矩阵。该矩阵的对角线元素是关键,它们代表了所有正例(即重复问题对)的相似度。通常,这些值会高于非对角线上的值,这表明模型能正确识别重复对。
矩阵的非对角线区域(右上和左下)则代表了所有负例(即非重复问题对)的相似度。大多数情况下,这些值会低于对角线上的值。相似度的范围在-1到1之间,但并没有规定大于0就一定是重复对。一个训练良好的模型,其核心表现是能让重复对的相似度相对高于非重复对。
通过这种方式构建批次,我们无需在输入数据中特意准备负例,模型可以直接从现有批次中学习区分正负样本,这大大简化了数据准备工作。
构建成本函数
基于得到的相似度矩阵,我们已经可以构建成本函数。
一种直接的方法是使用你已经熟悉的三元组损失函数,其公式如下:
Loss = max( sim(A, N) - sim(A, P) + α, 0 )
其中,A 是锚点样本,P 是正样本,N 是负样本,α 是间隔参数。
整个连体网络的总体成本将是训练集上所有单个损失的总和。然而,还有更多先进的技术可以显著提升模型性能,我们将在下一节中介绍。

总结
本节课中,我们一起学习了如何为连体网络计算成本。我们从准备包含重复对的批次数据开始,观察模型如何将其转换为向量,并通过计算相似度矩阵来得到正例和负例的评分。最后,我们介绍了如何基于这些相似度,使用三元组损失函数来构建初步的成本函数,为模型的优化训练奠定了基础。
136:计算成本 II 💰

在本节课中,我们将深入探讨如何计算孪生网络的整体成本。我们将定义成本矩阵中的对角线与非对角线元素,并利用这些概念来改进损失函数,从而提升模型的性能。
在上一节视频中,我们简要提到了成本矩阵中的对角线与非对角线元素。
现在,我们将正式定义这些术语,并利用它们来计算整体成本。让我们开始吧。
之前,我们将训练数据组织成了两个特定的批次。
每个批次内部都不包含重复的问题。
我们将这些批次分别输入到孪生网络的两个子网络中,为每个问题生成了一个向量输出,其维度为 1 x D_model。这里的 D_model 是嵌入维度,等于矩阵的列数。在本例中,这个值是 5。
单个批次的 V1 向量被堆叠在一起。在本例中,批次大小就是这个矩阵的行数,即 4。
你同样可以看到一个类似的 V2 向量批次。
最后一步是通过计算 V1 向量和 V2 向量所有可能组合之间的相似度,来合并孪生网络的两个分支。
对于这个批次大小为 4 的例子,最后一步将生成一个如下所示的相似度矩阵。
这个矩阵有一些重要的属性。绿色对角线上的相似度值对应着重复问题的相似度。对于一个训练良好的模型,这些值应该大于非对角线上的值,这反映了网络能为重复问题生成相似的向量输出。
右上角和左下角的橙色值则对应着非重复问题的相似度。
现在,有趣的部分来了。你可以利用这些非对角线信息来修改损失函数,从而显著提升模型的性能。
为此,我将引入两个概念。
第一个概念是平均负例相似度。
它指的是每一行中所有非对角线值的平均值。
请注意,非对角线元素仍然可以是正数,所以当我提到“平均负例”时,我指的是负例相似度的平均值,而不是矩阵中负数的平均值。
例如,第一行的平均负例相似度就是该行所有非对角线值的平均值(即 -0.8、0.3 和 -0.5 的平均值),不包括对角线上的值 0.9。
你可以通过修改损失函数来利用平均负例相似度,以帮助加速训练,我稍后会展示。
下一个概念是最接近负例。
如前所述,由于三元组损失函数的定义方式,你需要选择所谓的“困难三元组”进行训练。这意味着在训练时,你希望选择那些负例的余弦相似度与正例相似度非常接近的三元组。
这迫使你的模型去学习区分这些示例,并最终通过训练将这些相似度值进一步拉开。
为此,你需要在输出矩阵的每一行中搜索“最接近负例”。也就是说,找到那个最接近(但仍小于)该行对角线值的非对角线值。
以第一行为例,对角线上的值是 0.9。那么,在这种情况下,最接近的非对角线元素就是 0.3。
这意味着,这个相似度为 0.3 的负例,能为你的模型提供最大的学习机会。
为了运用这些新概念,让我们回顾一下三元组损失的定义:max( sim(A, N) - sim(A, P) + α, 0 )。同时,我们用变量 diff 来表示两个相似度之间的差值。
这里我们只是写出了 diff 的定义。
为了最小化损失,你希望 diff + α 小于或等于 0。
现在,我将引入两个新的损失函数。
以下是第一个损失函数 Loss1 的定义:
Loss1 = max( mean_negative - sim(A, P) + α, 0 )
三元组损失公式与 Loss1 之间的变化,在于将 sim(A, N) 替换为了 mean_negative。这有助于在训练期间通过减少噪声来加速模型收敛。
它通过基于多个观测值的平均值进行训练,而不是基于每个非对角线示例进行训练,从而减少了噪声。
那么,为什么取多个观测值的平均值通常能减少噪声呢?我们将噪声定义为一个来自均值为 0 的分布的小值。换句话说,多个噪声值的平均值通常趋近于 0。因此,如果我们取多个示例的平均值,这就有抵消各个观测值中个体噪声的效果。
接下来是第二个损失函数 Loss2 的定义:
Loss2 = max( closest_negative - sim(A, P) + α, 0 )
这次公式的差异在于,将 sim(A, N) 替换为了 closest_negative。这通过削弱被替换掉的、原本更负的 sim(A, N) 的影响,有助于创造一个稍大的惩罚。
你可以将“最接近负例”理解为,找到那个能使两个余弦相似度之间差值最小的负例。如果你将这个小的差值加上 α,那么你就能在该行所有其他示例中产生最大的损失。通过将训练重点放在产生更高损失值的示例上,你迫使模型更多地更新其权重,从而从这些更困难的示例中学习。
然后,你可以定义完整损失为 Loss1 和 Loss2 的总和:
Total Loss = Loss1 + Loss2
你将在作业中使用这个新的完整损失作为改进的三元组损失。
你的孪生网络的整体成本,将是训练集上所有这些个体损失的总和。

在本节课中,我们一起学习了如何利用成本矩阵中的非对角线信息来改进损失函数。我们引入了“平均负例相似度”和“最接近负例”两个核心概念,并基于它们构建了 Loss1 和 Loss2,最终组合成更有效的完整损失函数,用于优化孪生网络的训练。
在下一个视频中,你将在“单样本学习”中应用这个成本函数。单样本学习是一种非常有效的技术,可以在比较支票或其他任何类型输入的真实性时,为你节省大量时间。
137:30_一次性学习 🎯

概述
在本节课中,我们将要学习一种名为“一次性学习”的技术。一次性学习旨在解决传统分类模型在面对新类别时,需要大量数据重新训练的难题。通过学习一个“相似度函数”,模型能够仅凭一个示例就判断新输入是否属于某个已知类别。这在签名验证、人脸识别等场景中非常有用。
从分类到一次性学习
上一节我们介绍了传统的多类别分类方法。本节中我们来看看一次性学习如何解决其局限性。
假设你正在尝试鉴定一首诗的作者是否为卢卡斯。传统方法有两种:一是将所有卢卡斯的诗放入数据集,将问题转化为预测K+1个类别(K个其他作者加上卢卡斯);二是将卢卡斯的一首诗与另一首诗进行比较,这正是“一次性学习”的思路。
为了理解分类与一次性学习的区别,首先考虑基于K个可能类别来识别或分类签名的情况。
你可能会使用某种在K个类别上训练的分类模型,通常在最后使用softmax函数来寻找最大概率。在识别时,将输入的签名分类到对应的类别中。如果你的签名列表很少变动,这种方法很好。
但是,如果你需要分类一个新的签名呢?每次发生这种情况都重新训练模型的成本会很高。此外,除非你拥有大量这个新签名的示例,否则模型训练效果不会很好。
在一次性学习中,你需要能够仅凭一个示例就反复识别这个签名。你可以通过学习一个“相似度函数”来实现这一点。然后,你可以测试一个相似度分数是否超过某个阈值,以判断两个签名是否相同。
因此,问题从“确定属于哪个类别”转变为“衡量两个类别之间的相似度”。这非常有用,例如在银行场景中。每次出现新签名时,你不可能为了将签名分类到K个可能的输出而重新训练整个系统。相反,你只需学习一个相似度函数,用于计算相似度分数,进而识别两个签名是否相同。
你已经使用余弦相似度作为相似度函数做过类似的事情。如果结果大于某个阈值τ,你就判定输入是相同的。在比较签名的情况下,如果相似度小于或等于τ,则签名不同。
以下是实现这一思路的关键步骤:
- 定义相似度函数:该函数接收两个输入,并输出一个表示其相似程度的分数。
- 设定阈值:确定一个阈值τ,用于判断两个输入是否属于同一类别。
- 进行比对:将新输入与已知的单个示例进行比对,根据相似度分数和阈值做出判断。

总结与展望
本节课中我们一起学习了“一次性学习”的核心概念。它通过将问题从多类别分类转化为相似度比较,使得模型能够仅凭一个示例就能有效识别新类别,避免了数据收集和模型重新训练的巨大开销。
一次性学习利用了孪生网络。在下一节视频中,我将向你展示如何训练和测试你的孪生网络。
138:训练与测试 🧠

在本节课中,我们将学习如何为孪生网络准备数据集,以及如何训练和测试该模型。我们将使用一个关于问题对的数据集,目标是判断两个问题是否为重复问题。
数据集概览 📊
我们将使用“问题重复数据集”来完成本周的编程练习。该数据集的结构如下:它包含一系列问题对,并为每个问题对标注了一个布尔值,表示它们是否为重复问题。
例如:
- 问题1:“What is your age?” 与 问题2:“How old are you?” 的
is_duplicate为True,因为这两个问题是重复的。 - 问题1:“Where are you from?” 与 问题2:“Where are you going?” 的
is_duplicate为False,因为它们不是重复的。
这个数据集为模型提供了大量可供学习的样本。
数据预处理与批处理 🔄
上一节我们介绍了数据集的基本结构,本节中我们来看看如何为模型训练准备数据批次。
首先,你需要对数据进行预处理,将其组织成批次大小为 B 的批次。每个批次中的对应问题是重复的。
例如,批次1中的第一个问题“What is your age?”与批次2中的第一个问题“How old are you?”是重复的。批次1中的第二个问题与批次2中的第二个问题也是重复的,依此类推。
但需要注意的是,同一个批次内部没有重复问题。如果我们称批次1的第一个问题为 Q1_A,批次2的第一个问题为 Q2_A,那么 Q1_A 和 Q2_A 是重复的。同样,批次1的第二个问题 Q1_B 和批次2的第二个问题 Q2_B 是重复的。然而,Q1_A 和 Q1_B 不是重复的,Q2_A 和 Q2_B 也不是重复的。
你将学习如何以这种方式准备批次,确保同一批次内没有问题重复。
孪生网络模型架构 🤖
准备好输入批次后,你将使用这些输入为每个批次获取输出向量。然后,你可以计算每对输出向量之间的余弦相似度。
以下是你在作业中将实现的孪生模型结构:
- 创建一个子网络。
- 复制该子网络,形成两个并行且结构完全相同的分支。
- 在每个子网络中,输入经过嵌入层和LSTM层,得到输出向量
V1和V2。 - 使用
V1和V2计算余弦相似度。
一个重要的细节是:两个子网络的学习参数完全相同。因此,你实际上只训练一套权重,而不是两套。
模型测试与预测 🎯
了解如何训练模型后,我们来看看如何测试它。在测试模型时,你将使用“一次性学习”方法,目标是计算两个输入问题之间的相似度得分。
以下是测试步骤:
- 将每个输入问题转换为数字数组。
- 将这些数组输入你的模型。
- 使用余弦相似度比较两个子网络的输出
V1和V2,得到一个相似度得分。 - 将该得分与一个阈值
τ进行比较。 - 如果余弦相似度大于
τ,则将这两个问题分类为重复问题。
请注意,损失函数中的边界值 α 和此处的阈值 τ 都是可调整的超参数。
总结 ✨

本节课中我们一起学习了:
- 如何为孪生网络准备“问题重复数据集”。
- 如何组织数据批次,确保批次间对应问题重复而批次内问题不重复。
- 孪生网络的结构,其核心是两个共享权重的相同子网络。
- 如何使用余弦相似度和阈值
τ来测试模型并判断问题是否重复。
在本周的编程练习中,你将应用孪生网络来识别问题是否为重复问题。具体来说,你将使用核心问题重复数据集,并有望获得很高的准确率。
140:基于注意力模型的自然语言处理 🧠
在本课程中,我们将学习如何利用注意力机制构建先进的自然语言处理模型。这是本专项课程的最后一门课,完成后你将掌握当前最前沿的实用NLP技术。
课程概述 📋
欢迎来到本专项课程的第四门,也是最后一门课程。这门课程名为“基于注意力模型的自然语言处理”。完成本课程后,你将基本掌握当前最前沿的实用自然语言处理方法。你将使用一种名为注意力的强大技术来构建多种不同的模型。
你将构建的模型 🛠️
以下是本课程中,你将运用注意力机制构建的几个核心应用:
- 语言翻译模型:构建一个强大的语言翻译系统。
- 文本摘要算法:创建一个能够自动总结文本内容的算法。
- 问答模型:开发一个能够根据给定文本回答问题的模型。
- 聊天机器人:构建一个可以与之进行对话的聊天机器人。
掌握了本课程开发的技能后,你将能够构建与当今工业界许多大公司所用水平相当的、最先进的自然语言处理应用。
讲师介绍 👨🏫
我很高兴再次欢迎卢卡斯和尤尼斯担任本课程的讲师。我知道他们二人都非常期待与大家一起学习这些材料。
卢卡斯前几天告诉我,他认为这门课的主题非常令人兴奋。卢卡斯,也许你可以多说几句?
当然,谢谢你,安德鲁。正如你在第一门课中看到的,情感分析是一个非常棘手的问题。但在许多应用中,你确实需要确定一句话的情感倾向。因此,这也是一个非常值得研究的问题。
谢谢,卢卡斯。我仍然对领导谷歌大脑团队时的美好时光记忆犹新,很高兴看到团队持续做出的杰出工作。
尤尼斯,从你的角度来看,这门课最令人兴奋的方面是什么?
安德鲁,有很多令人兴奋的地方。首先,你将在本课程中构建的模型代表了该领域前沿的活跃研究领域。但除此之外,对于现代深度学习而言,有一种“新常态”——也就是说,大多数人实际上并不是从头开始构建和训练模型。更常见的做法是下载一个预训练模型,然后针对你的特定用例进行调整和微调。
课程特色与学习方法 🎯
在本课程中,我们将向你展示如何从头开始构建模型。同时,我们也会为你提供专门定制的预训练模型。这些模型是我们为了本课程,在目前仅向谷歌研究人员开放的最强大的TPU集群上连续训练数周而创建的。
你将练习工业界使用的工作流程,即对最先进的预训练模型进行微调。掌握了从本课程中学到的工具,你将能够按照行业标准构建自然语言处理系统。
非常感谢尤尼斯和卢卡斯对这门课程的介绍。这是一系列本课程将要涵盖的重要材料。那么,让我们开始吧。
总结 ✨
在本节课中,我们一起学习了课程四的总体介绍。我们了解到,本课程的核心是掌握注意力机制,并利用它来构建翻译、摘要、问答和对话机器人等前沿的NLP应用。同时,课程将结合“从零构建”的深度理解与“微调预训练模型”的行业实践,为你提供构建工业级NLP系统所需的完整技能。接下来,让我们正式开启学习之旅。祝你好运!🚀
142:序列到序列模型 🧠➡️🗣️

在本节课中,我们将学习神经机器翻译的基础知识。我们将探讨其核心架构——序列到序列模型,并了解该模型在翻译过程中如何“关注”特定的词语。我们还将分析传统模型的局限性,并引入“注意力机制”这一关键概念作为解决方案。
概述:神经机器翻译与序列到序列模型
神经机器翻译使用编码器和解码器将一种语言翻译成另一种语言。例如,将英语“It’s time to go”翻译成法语“C’est l’heure de partir”。实现这一功能的核心是序列到序列模型。
序列到序列模型详解
上一节我们介绍了神经机器翻译的基本目标,本节中我们来看看实现它的核心架构——序列到序列模型。
该模型由谷歌在2014年提出,在当时是一项突破。其核心思想是将一个可变长度的序列(如单词序列)映射到一个固定长度的记忆向量中,这个向量编码了整个句子的含义。例如,一段长度可变的文本可以被编码成一个固定维度(如300维)的向量。这个特性使其成为机器翻译的强大工具。
序列到序列模型有两个主要优势:
- 输入和输出的序列长度无需匹配,这在翻译任务中非常理想。
- 模型通常使用LSTM或GRU单元,这有助于解决深度网络中常见的梯度消失和梯度爆炸问题。
编码器与解码器的工作流程
了解了模型的基本原理后,我们来深入看看它的两个核心组件:编码器和解码器是如何协同工作的。
在序列到序列模型中,编码器接收单词标记作为输入,并返回其最终的隐藏状态作为输出。解码器则利用这个隐藏状态来生成目标语言的翻译句子。
以下是编码器和解码器的具体结构:
编码器结构:
编码器通常包含一个嵌入层和一个或多个LSTM层。
- 嵌入层:将单词标记转换为向量,作为LSTM的输入。
- LSTM模块:在输入序列的每一步,LSTM接收来自嵌入层的输入以及前一步的隐藏状态。编码器返回最后一步的隐藏状态(例如
H_4),这个状态包含了整个句子的信息,编码了其整体含义。
解码器工作流程:
解码器结构类似,也包含嵌入层和LSTM层。它的工作方式是使用上一步的输出单词作为下一步的输入单词,并将LSTM的隐藏状态传递给下一步。
- 输入序列以一个“序列开始”标记(
<SOS>)开始。 - 第一步解码器输出最可能的下一个单词,例如“C’est”。
- 然后将“C’est”作为输入单词用于下一步,并重复此过程以生成句子的其余部分:“l’heure”, “de”, “partir”。
传统模型的局限性与注意力机制的引入
上一节我们介绍了编码器-解码器的工作流程,本节中我们来看看传统序列到序列模型的一个主要缺陷及其解决方案。
传统模型的一个主要限制被称为“信息瓶颈”。由于它使用固定长度的记忆向量(即编码器的最终隐藏状态)来传递信息,长序列会带来问题。无论输入序列包含多少信息,能够从编码器传递到解码器的信息量是固定的。
这导致了一个问题:当输入序列很长时,模型难以将所有信息压缩到一个固定大小的向量中,这会限制解码器做出准确预测的能力,从而导致模型性能随序列长度增加而下降。
一种解决思路是让解码器能够访问编码器每一步的隐藏状态,而不是仅仅依赖最终的那个压缩向量。但这会带来内存和上下文处理上的新问题。
那么,如何构建一个既能高效利用时间和内存,又能从长序列中做出准确预测的模型呢?如果模型能在解码的每一步有选择地“聚焦”于输入序列中最重要的词语,这就成为可能。
你可以将此视为为模型增加一个新的处理层,在本课程中,这个层被称为注意力机制。通过为模型提供每个输入词语的特定信息,你就能赋予它在解码过程的每一步将“注意力”集中在正确位置的能力。

总结

本节课中我们一起学习了神经机器翻译的基础。我们了解了序列到序列模型的架构,包括编码器和解码器如何协作。我们探讨了传统模型在处理长序列时的“信息瓶颈”问题,并引入了“注意力机制”作为关键的解决方案,它使模型能够在翻译时动态地关注输入序列的相关部分。在接下来的课程中,你将更深入地理解这个新层的工作原理和原因。
143:带注意力的序列到序列模型 🧠

概述
在本节课中,我们将要学习注意力机制这一核心概念。注意力机制允许模型在进行预测时,有选择地“关注”输入序列中的特定部分,从而显著提升模型(尤其是在处理长序列时)的性能。我们将从动机出发,逐步解析其工作原理,并通过一个简单的实现来加深理解。
注意力机制的动机与效果
上一节我们介绍了传统序列到序列模型的局限性。本节中我们来看看注意力机制是如何解决这些问题的。
注意力机制由Zmetry Baanu、Gang Chohou和Yohua Benio在一篇里程碑式的论文中提出。作者开发此方法是为了解决序列到序列模型在翻译长句子时能力下降的问题。
虽然注意力最初是为机器翻译开发的,但它已成功应用于许多其他领域。在深入细节之前,我们先看看它的效果有多惊人。
以下是Baanu论文中不同模型性能的对比,使用BLEU分数(一种你将在后续学习的性能指标,分数越高表示翻译越准确)。
虚线显示了双向序列到序列模型在输入句子长度增加时的得分。数字13和50表示训练模型时使用的最大序列长度。
序列到序列模型在处理10到20个词的句子时表现良好,但超过这个长度后性能会下降。这是因为序列到序列模型必须将整个输入序列的含义存储在一个单一的向量中。
论文中开发的模型(RNNsearch-13和RNNsearch-50)使用了带注意力的双向编码器和解码器。首先,这些模型在所有句子长度上的表现都优于传统的序列到序列模型。其次,RNNsearch-50模型的性能基本不随句子长度增加而下降。
这是因为模型能够专注于特定的输入部分来预测输出翻译中的单词,而不必记忆整个输入句子。
注意力机制的工作原理
现在,我们来探讨注意力背后的动机及其工作原理。
传统的序列到序列模型使用编码器的最终隐藏状态作为解码器的初始隐藏状态。这迫使编码器将整个输入序列的含义存储在这一个隐藏状态中。
改进的思路是,不只用最终的隐藏状态,而是将所有隐藏状态都传递给解码器。然而,这很快就会变得低效,因为你必须在内存中保留每个输入步骤的隐藏状态。
为了解决这个问题,可以将隐藏状态组合成一个向量,通常称为上下文向量。
最简单的操作是逐点相加。由于隐藏向量大小相同,你可以将这些向量按元素相加,生成另一个相同大小的向量。
但此时解码器获得了每一步的信息,而实际上它可能只需要前几个输入步骤的信息来生成第一个单词。这与使用LSTM或GRU的最后一个隐藏状态差别不大。
解决方案是:在逐点相加之前,给某些编码器向量分配比其他向量更大的权重。对下一个解码器输出更重要的单词将获得更大的权重。这样,上下文向量就包含了关于最重要单词的更多信息,而其他单词的信息则较少。
权重如何计算?
那么,这些权重是如何计算的呢?为了确定每个步骤中哪些输入词是重要的,我们需要一个衡量标准。
解码器的前一个隐藏状态(记为 s_{i-1})包含了输出翻译中前几个单词的信息。这意味着你可以将解码器状态与每个编码器状态进行比较,以直观地确定最重要的输入。
解码器可以设置权重,使其专注于对下一个预测最重要的输入词。这决定了模型应该“注意”输入序列的哪些部分。
深入注意力层
现在,让我们步入注意力层,具体看看权重和上下文向量是如何计算的。
注意力层的目标是返回一个包含编码器状态相关信息的上下文向量。
以下是计算步骤:
第一步是计算对齐分数 e_{ij}。这个分数衡量了位置 j 附近的输入与预期输出位置 i 的匹配程度。匹配度越高,分数越高。
这通过一个前馈神经网络完成,该网络以编码器和解码器的隐藏状态作为输入。前馈网络的权重与序列到序列模型的其他部分一同学习。
然后,这些分数通过 softmax 函数转换为范围在0到1之间的权重。这意味着权重可以被视为一个概率分布,其总和为1。
最后,每个编码器状态乘以其各自的权重,然后求和,形成一个上下文向量。
由于权重是一个概率分布,这相当于计算单词对齐的期望值。
实现简单的注意力操作
接下来,你将通过实现Baanu论文中注意力操作的一个简化版本,来更好地理解这一切是如何运作的。
(注:此处为概念性描述,具体代码实现需根据框架编写,核心公式如下:)
对齐分数计算(简化版,例如使用点积):
e_{ij} = s_{i-1} · h_j
其中 s_{i-1} 是解码器上一个隐藏状态,h_j 是编码器第 j 个隐藏状态。
权重计算:
α_{ij} = softmax(e_{i}) # 对 j 维度进行softmax
上下文向量生成:
c_i = Σ_j (α_{ij} * h_j)
总结

本节课中,我们一起学习了注意力机制。我们了解了它如何解决传统序列到序列模型处理长序列的瓶颈,通过允许解码器在每一步有选择地聚焦于输入序列的不同部分。我们剖析了其核心计算步骤:计算对齐分数、通过softmax生成权重、以及加权求和生成上下文向量。注意力机制是提升序列模型性能的关键技术,为后续学习更复杂的模型(如Transformer)奠定了基础。
在下一个视频中,我们将定义键(Keys)、查询(Queries)和值(Values),并展示如何在注意力中使用它们。😊
144:查询、键、值与注意力 🧠

在本节课中,我们将学习注意力机制中的核心概念:查询、键和值。我们将了解它们如何从信息检索的概念中衍生出来,并构成一种高效且强大的注意力形式。这种注意力是现代Transformer模型的基础,也是本周作业中你将使用的关键组件。
从信息检索到注意力 🔍
上一节我们介绍了注意力机制的基本思想。本节中,我们来看看一种基于信息检索理念的注意力形式。
查询、键和值是你在本视频中将用于注意力的术语。我将为你定义它们,并展示它们如何被使用。
原始的注意力论文发表于2014年。自那以后,出现了多种注意力的变体,其中一些模型不依赖于循环神经网络。例如,2017年的论文《Attention Is All You Need》引入了Transformer模型,以及一种基于信息检索、使用查询、键和值的注意力形式。这是一种高效且强大的注意力形式,你将在本周的作业中使用它。
查询、键与值的概念 📚
在概念上,你可以将键和值视为一个查找表。查询与一个键相匹配,然后返回与该键关联的值。
例如,如果我们在法语和英语之间进行翻译,查询“well”与键“time”匹配,因此我们希望获得“time”对应的值。
在实践中,查询、键和值都由向量表示,例如嵌入向量。因此,你不会得到精确的匹配,但模型可以学习源语言和目标语言之间哪些词最相似。
注意力计算过程 ⚙️
上一节我们了解了查询、键和值的概念。本节中,我们来看看它们如何通过数学计算生成注意力向量。
词之间的相似性被称为对齐。键向量用于计算对齐分数,这些分数衡量查询与键的匹配程度。然后,这些对齐分数被转换为权重,用于对值向量进行加权求和。这个值向量的加权和作为注意力向量返回。
这个过程可以使用缩放点积注意力来执行。每个步骤的查询被打包成一个矩阵 Q,因此可以同时为每个查询计算注意力。键和值也被打包成矩阵 K 和 V。这些矩阵是注意力函数的输入,如左侧图表和右侧数学公式所示。
首先,查询矩阵和键矩阵相乘,得到对齐分数矩阵。然后,这些分数通过键向量维度 d_k 的平方根进行缩放。这种缩放提高了较大模型尺寸下的模型性能,可被视为一种正则化常数。
接下来,使用softmax函数将缩放后的分数转换为权重,使得每个查询的权重之和为一。最后,权重矩阵与值矩阵相乘,得到每个查询的注意力向量。
你可以将键和值视为相同。因此,当你将softmax输出与 V 相乘时,你是在对初始输入进行线性组合,然后将其馈送到解码器。
与原始的注意力形式不同,缩放点积注意力仅包含两次矩阵乘法,而不包含神经网络。由于矩阵乘法在现代深度学习框架中得到了高度优化,这种形式的注意力计算速度要快得多。但这也意味着源语言和目标语言之间的对齐必须在其他地方学习。
对齐的学习与应用 🌐
上一节我们介绍了注意力计算过程。本节中,我们深入探讨对齐的概念及其重要性。
通常,对齐在注意力层之前的输入嵌入或其他线性层中学习。对齐权重形成一个矩阵,其中行是查询(目标词),列是键(源词)。该矩阵中的每个条目都是对应查询-键对的权重。
具有相似含义的词对,例如“T”和“T”,将比不相关的词(如“T”和“time”)具有更大的权重。通过训练,模型学习哪些词具有相似的含义,并将该信息编码在查询和键向量中。
像这样学习对齐对于在具有不同语法结构的语言之间进行翻译是有益的。由于注意力同时查看整个输入和目标句子,并根据词对计算对齐,因此无论词序如何,权重都会被适当地分配。
以下是两个例句及其对齐关系的说明:
- 英语例句:The agreement on the European Economic Area was signed in August 1992.
- 法语例句:L’accord sur la zone économique européenne a été signé en août 1992.
你可以看到“zone”和“area”在不同的位置,但具有相同的含义。模型已经学会了适当地对齐它们,使得解码器能够专注于适当的输入词,尽管词序不同。
总结与预告 📝
本节课中我们一起学习了注意力机制中查询、键和值的核心概念。
我向你介绍了注意力层的目的。你看到了它与信息检索的关系。我还向你展示了它即使在结构非常不同的语言之间也能很好地工作。

在下一视频中,我将讨论神经机器翻译,并向你展示该系统的设置是什么样的。我将向你展示数据集是什么样的,以及预处理数据集所需的步骤。
你现在已经了解了键、查询和值是什么。这些很重要,因为如果你阅读研究论文,可能会遇到这些术语,并且你将理解它们。在下一个视频中,我将讨论机器翻译的设置。
145:机器翻译的设置 🛠️

在本节课中,我们将学习神经网络机器翻译的基本设置。你将了解单词如何被表示,数据集的结构是怎样的,以及在实现系统时需要注意的关键事项。
单词表示与数据集结构
上一节我们介绍了机器翻译的基本概念,本节中我们来看看单词在神经网络机器翻译中是如何被表示的。
你将看到数据集的具体示例。在实现系统时,你需要跟踪几个关键事项,例如哪些单词对应哪些索引。
以下是输入数据的示例:
- 英语序列:
Im hungry. - 对应法语翻译:
J'ai faim.
以及另一个示例:
- 英语序列:
I watched the soccer game. - 对应法语翻译:
J'ai regardé le match de football.
你将拥有大量这样的输入对。需要知道的是,目前最先进的模型通常使用预训练的词向量。但在基础实现中,第一步是使用独热编码来表示单词。
通常,你需要维护两个字典来跟踪映射关系:
- word_to_index:将单词映射到索引的字典。
- index_to_word:将索引映射回单词的字典。
对于任何输入,你都需要将其转换为索引序列。反之,当模型做出预测后,你需要将索引序列转换回单词。
此外,你通常会使用一个序列结束标记,并用零填充你的标记向量,使其长度与批次中最长的序列匹配。
数据预处理示例
现在,让我们通过一个具体例子来理解数据预处理的过程。
这是一个英语句子及其分词后的版本:
原始句子:Both groups are needed.
分词后索引:[4546, 234, 12, 345]
可以看到,单词“Both”对应的索引是4546。在初始分词之后,你需要添加序列结束标记。如下图所示,EOS标记的索引是1。
添加EOS后:[4546, 234, 12, 345, 1]
接着,用零填充该序列,使其长度与批次中最长的序列一致。
现在,我们来看这个句子的法语翻译及其分词版本:
法语翻译:Les deux groupes sont nécessaires.
分词后索引:[876, 54, 321, 65, 1, 0, 0, ...]

请注意,这里的索引1同样代表句子结束标记,其后也跟随着一系列用于填充的零。
总结与下一步
本节课中,我们一起学习了机器翻译的基本设置。你现在知道了如何表示单词、如何初始化模型以及如何构建数据集的结构。
掌握了这些知识后,你就可以开始训练你的模型了。在下一个视频中,我将向你展示具体如何进行操作。
146:吴恩达《自然语言处理》P146 - 6. 教师强制 👨🏫
在本节课中,我们将要学习如何训练神经机器翻译系统。我们将重点介绍一个名为“教师强制”的核心概念,并探讨其优势。
概述

神经机器翻译模型通过编码器-解码器架构工作。训练这类模型时,一个关键的挑战在于如何有效地引导解码器生成正确的序列。本节将详细解释“教师强制”技术,它通过使用真实的目标词作为解码器的输入,而非模型自身的预测输出,来显著提升训练效率和模型稳定性。
训练神经机器翻译模型
上一节我们介绍了神经机器翻译的基本架构。本节中,我们来看看如何具体训练这个模型。
在训练过程中,一个直观的想法是将解码器每一步的输出序列与真实的目标序列进行比较,以计算损失。具体而言,我们会计算每一步的交叉熵损失,然后将所有步骤的损失相加,得到总损失。
公式:总损失 = Σ 交叉熵损失(解码器输出ᵢ, 目标词ᵢ)
然而,在实践中,这种方法在训练初期效果不佳。
训练初期的问题
问题在于,在训练的早期阶段,模型非常“幼稚”。它可能在序列的一开始就做出错误的预测。这个错误会像滚雪球一样累积,因为模型会将错误的输出作为下一步的输入,导致生成的序列与目标序列的偏差越来越大。
幻灯片中的例子说明了这个问题:模型可能将“team”(团队)错误地翻译成一个与“fluffy”(毛茸茸的)相似的词,而这两个词的含义天差地别。
教师强制:解决方案
为了避免上述问题,我们可以使用一种名为“教师强制”的技术。其核心思想是:在训练解码器时,不使用它上一步的预测输出作为当前步的输入,而是直接使用真实的目标词(即“地面真实值”)作为输入。
代码示意(伪代码):
# 非教师强制(标准方式)
decoder_input = previous_decoder_output
# 教师强制
decoder_input = true_target_word_from_previous_step
这样,即使模型在某一步做出了错误的预测,在下一步的训练中,它仍然会接收到正确的输入信息。这防止了错误在序列中传播,使得训练过程更加稳定和高效。
教师强制的优势与变体
教师强制技术能极大地加快训练速度。它就像一个老师在手把手地纠正学生的每一步错误,确保学习方向正确。
这种方法还有一些变体。例如,可以采用“课程学习”策略:在训练初期完全使用教师强制,随着模型能力提升,逐渐减少使用真实目标词的频率,并增加使用模型自身预测输出的比例,让模型慢慢学会独立生成序列。
以下是教师强制的主要优势列表:
- 训练更稳定:防止错误累积,模型收敛更快。
- 效率更高:减少了需要训练至收敛的迭代次数。
- 学习更准确:为解码器提供了清晰、正确的学习信号。
总结

本节课中,我们一起学习了神经机器翻译模型训练中的一个重要技术——教师强制。我们首先指出了在训练初期直接使用模型预测作为后续输入会导致错误传播的问题。接着,我们深入探讨了教师强制的工作原理,即使用真实的目标序列词来指导解码器的每一步训练,从而显著提升训练效率和模型性能。最后,我们还简要介绍了其变体“课程学习”。掌握教师强制技术,将为训练更强大的序列生成模型提供有力工具。
147:带注意力的神经机器翻译模型 🧠➡️🗣️

在本节课中,我们将学习如何从零开始构建一个带注意力机制的神经机器翻译模型。我们将详细拆解模型的每个组成部分,包括编码器、解码器以及注意力机制的工作原理,并了解它们是如何协同工作来完成翻译任务的。
模型架构概述
上一节我们介绍了神经机器翻译的基本概念,本节中我们来看看一个具体的、可实现的模型架构。这个模型与你之前见过的类似,包含三个核心部分:
- 编码器:处理输入的源语言序列。
- 解码器:负责生成目标语言的翻译。
- 注意力机制:帮助解码器在生成每个词时,聚焦于输入序列中相关的部分。
回忆一下,解码器需要将其隐藏状态传递给注意力机制以获取上下文向量。为了更清晰地实现这一流程,我们将使用两个解码器:一个注意力前解码器用于提供隐藏状态(作为查询),一个注意力后解码器用于接收上下文向量并最终生成翻译。
修改后的模型概览如下:
- 编码器和注意力前解码器分别接收输入序列和目标序列。
- 注意力前解码器中的目标序列会向右移位,这是实现“教师强迫”训练技巧的方式。
- 从编码器和注意力前解码器中,我们获取每一步的隐藏状态,并将它们作为注意力机制的输入。其中,编码器的隐藏状态作为键和值,解码器的隐藏状态作为查询。
- 注意力机制利用这些值计算上下文向量。
- 最后,注意力后解码器使用这些上下文向量作为输入,来预测输出序列。
模型组件详解
现在,让我们更仔细地审视模型的每一个环节。
输入准备
第一步是创建输入标记和目标标记的两个副本,因为它们在模型的不同位置都会被用到。
- 一个输入标记的副本送入编码器,被转换为后续用作键和值的向量。
- 一个目标标记的副本送入注意力前解码器。
注意:编码器和注意力前解码器的计算可以并行进行,因为它们彼此不依赖。
注意力前解码器与移位
在注意力前解码器内部,我们需要对目标序列进行预处理:
- 将序列中的每个标记向右移动一位。
- 在序列开头添加一个句子起始标记。
这样做的目的是,在训练时(使用教师强迫),让解码器在预测第 t 个词时,使用第 t-1 个真实词作为输入。
嵌入与LSTM层
在编码器和注意力前解码器中,输入和目标标记在进入LSTM层之前,会先经过一个嵌入层。嵌入层将离散的标记转换为连续的向量表示。
# 伪代码示例:嵌入与LSTM
embedded_input = EmbeddingLayer(input_tokens)
hidden_states, final_state = LSTM(embedded_input)
准备注意力层的输入
获得查询、键和值向量后,需要为注意力层做准备。这里需要使用输入标记的副本来创建一个填充掩码。这个掩码帮助注意力层识别并忽略输入序列中的填充符,确保模型只关注实际内容。
注意力层
现在,一切准备就绪,可以进入核心的注意力计算。我们将查询、键、值以及掩码传递给注意力层。
# 伪代码示例:缩放点积注意力
context_vector, attention_weights = ScaledDotProductAttention(queries, keys, values, mask)
注意力层输出两个结果:
- 上下文向量:包含了当前解码步骤应关注的输入信息。
- 更新后的掩码。
注意力后解码器与输出
在进入最终的解码器之前,我们丢弃掩码。然后,将上下文向量传递给注意力后解码器。该解码器通常由一个LSTM层、一个全连接层和一个Log Softmax层组成。
# 伪代码示例:注意力后解码
decoder_output = LSTM(context_vector)
logits = DenseLayer(decoder_output)
log_probs = LogSoftmax(logits)
最终,模型返回每个目标词位置的对数概率,以及在最初准备阶段创建的目标标记副本。

总结
本节课中,我们一起学习了构建一个带注意力机制的神经机器翻译模型的完整流程。我们从模型总览开始,逐步深入了解了编码器、双解码器设计、序列移位、嵌入、LSTM以及注意力机制的具体作用与数据流向。这个架构清晰地展示了如何将输入序列的信息,通过注意力机制动态地引导翻译的生成过程。
你现已掌握了NMT实现的基本框架。如果仍有部分细节不甚清晰,也无需担心,本周的编程练习将提供更具体的实践机会。在下一视频中,我们将讨论如何评估你的翻译系统。
148:BLEU分数评估法 🎯

在本节课中,我们将学习如何评估机器翻译模型的质量。我们将重点介绍一个专门为此任务设计的核心评估指标——BLEU分数,并探讨其工作原理、计算方法以及需要注意的局限性。
模型评估的重要性
在构建并训练好模型之后,评估其机器翻译的性能至关重要。为此,研究者们设计了一些专门的评估指标。本节课程将向您展示BLEU分数,以及它在评估机器翻译模型时存在的一些问题。
什么是BLEU分数? 🤔
BLEU(双语评估替补,Bilingual Evaluation Understudy)是一种专门为评估自然语言处理中最具挑战性的任务(包括机器翻译)而设计的算法。它通过将机器翻译的候选文本与一个或多个参考翻译(通常是人工翻译)进行比较,来评估机器翻译文本的质量。BLEU分数越接近1,表示模型越好;越接近0,则越差。
那么,BLEU分数具体是什么?它为何如此重要?
如何计算BLEU分数? 🧮
要获得BLEU分数,需要计算候选翻译的精确度,方法是将候选翻译的n-gram与参考翻译进行比较。为了演示,我们以一元语法(unigram)为例。
假设您的模型生成了一个候选翻译序列:I I M I。
同时,您有两个参考翻译:
Yuunice said I am hungryhe said I am hungry
计算基础BLEU分数的方法是:统计候选翻译中有多少个单词出现在任意一个参考翻译中,然后将这个计数除以候选翻译的总单词数。这可以看作是一种精确度指标。
以下是计算过程:
- 遍历候选翻译的所有单词。第一个单词是
I,它出现在两个参考翻译中,因此计数加1。 - 下一个单词又是
I,已知它出现在两个参考翻译中,计数再加1。 - 接着是单词
M,它也出现在两个参考翻译中,计数加1。 - 最后又是单词
I,出现在参考翻译中,计数再加1。
最终,将总计数(4)除以候选翻译的单词数(4),得到BLEU分数为1。
这很奇怪,对吧?一个与参考翻译相差甚远的翻译竟然得到了满分。使用这种基础的BLEU分数,一个总是输出常见单词的模型会表现得“很好”。
改进的BLEU分数计算法
接下来,我们尝试一个改进版本,它能更好地估计模型的性能。
在改进版的BLEU分数计算中,当您在参考翻译中找到一个与候选词匹配的单词后,在后续匹配中,该单词在参考翻译中将不再被考虑。换句话说,参考翻译中的单词在被匹配后会被“消耗”掉。
让我们从头开始计算:
- 候选翻译的第一个单词是
I,它出现在两个参考翻译中。计数加1,并将两个参考翻译中的I单词标记为已消耗。 - 下一个候选单词又是
I,但由于参考翻译中的I已被上一个匹配消耗,现在参考翻译中没有可匹配的I了,因此计数不变。 - 接着是单词
M,它出现在参考翻译中。计数加1,并消耗掉参考翻译中的M。 - 最后一个候选单词是
I,参考翻译中已无剩余的I可供匹配,因此计数不变。
最终,计数为2,除以候选翻译总单词数4,得到BLEU分数为 2/4 = 0.5。
正如您所注意到的,这个版本的BLEU分数比基础版本更合理。
BLEU分数的局限性 ⚠️
然而,就像生活中的任何事情一样,使用BLEU分数作为评估指标也存在一些需要注意的地方。
首先,它不考虑单词的语义。其次,它也不考虑句子的结构。想象一下得到这样一个翻译:A I was hungry, because. 如果参考句子是 I ate because I was hungry,按照BLEU的计算方式,这可能得到一个很高的分数。
BLEU分数是机器翻译领域应用最广泛的评估指标,但在使用它之前,您应该了解这些缺点。

总结
本节课中,我们一起学习了如何使用BLEU分数来评估您的机器翻译模型。我也向您展示了这个指标存在的一些问题,因为它不关心语义和句子结构。在接下来的视频中,您将看到另一个用于机器翻译的评估指标,那个指标或许能更好地估计您模型的性能。
149:ROUGE-N 分数 🧮
在本节课中,我们将学习 ROUGE 分数,这是另一种用于评估机器翻译系统性能的指标。我们将重点介绍 ROUGE-N 分数的计算方法,并将其与之前学过的 BLEU 分数进行对比,最后探讨如何结合两者得到一个更全面的评估指标。

上一节我们介绍了 BLEU 分数及其修正版本,并指出了它因忽略语义和句子结构而存在的一些缺点。本节中,我们来看看 ROUGE 分数。
现在我将向你介绍一个名为 ROUGE 的指标家族。ROUGE 代表“面向召回率的摘要评估研究”,这个名字很长,但直接表明它默认更侧重于召回率。这意味着 ROUGE 关注的是人工创建的参考译文中,有多少内容出现在了候选翻译中。相比之下,BLEU 更侧重于精确率,因为它需要计算候选翻译中有多少词出现在参考译文中。ROUGE 最初是为评估机器摘要文本质量而开发的,但对于评估机器翻译质量也很有帮助。它的工作原理是将机器生成的候选翻译与人工提供的参考翻译进行比较。
ROUGE 分数有很多版本,这里我将向你展示名为 ROUGE-N 的版本。
对于 ROUGE-N 分数,你需要计算候选翻译与参考翻译之间 N 元语法(N-gram)的重叠次数,这与计算 BLEU 分数的过程有些相似。
为了看清这两个指标的区别,我将通过一个例子展示 ROUGE-N 如何基于一元语法(unigram)进行计算。
以下是计算仅基于召回率的基本版 ROUGE-N 分数的步骤:
- 计算参考译文与候选译文之间匹配的单词数量。
- 将该匹配数量除以参考译文的总单词数。
- 如果你有多个参考译文,则需要用每个参考译文分别计算一个 ROUGE-N 分数,然后取其中的最大值。
现在,让我们通过之前为 BLEU 分数看过的例子来走一遍流程。
你的候选翻译包含单词 “I” 两次、单词 “am” 一次和单词 “I” 又一次,总共四个单词。你还有一个参考译文 “Eunice said I am hungry”,以及另一个略有不同的参考译文 “He said I am hungry”。每个参考译文总共有五个单词。
你需要计算参考译文与候选翻译之间的匹配次数,类似于为 BLEU 分数所做的操作。让我们从第一个参考译文开始。
单词 “Eunice” 与候选翻译中的任何一元语法都不匹配,因此计数不增加。单词 “said” 也不匹配候选翻译中的任何单词。单词 “I” 有多个匹配项,但你只需要第一个匹配项。对于这个匹配,你只给计数加一。单词 “am” 在候选翻译中有一个匹配项,因此增加你的计数。现在,第一个参考译文的最后一个单词 “hungry” 与候选翻译中的任何单词都不匹配,所以你的计数不增加。
如果你对第二个参考译文重复此过程,会得到计数等于 2。
最后,你将计数除以每个参考译文的单词数,并取最大值。对于这个例子,最大值等于 0.4。
这个基本版本的 ROUGE-N 分数是基于召回率的,而你在之前课程中看到的 BLEU 分数是基于精确率的。
但为什么不将两者结合起来得到一个像 F1 分数那样的指标呢?
回想一下你在机器学习入门课程中学到的,F1 分数由以下公式给出:
F1 = 2 * (精确率 * 召回率) / (精确率 + 召回率)
如果你用修正版的 BLEU 分数替换精确率,用 ROUGE-N 分数替换召回率,就会得到以下公式。对于这个例子,你有一个 BLEU 分数等于 0.5(这是之前课程中得到的),还有一个之前计算出的 ROUGE-N 分数等于 0.4。利用这些值,你将得到一个 F1 分数等于 4/9,约等于 0.44。

你现在已经看到了如何计算修正版 BLEU 分数和简单的 ROUGE-N 分数来评估你的模型。你可以将这些指标视作精确率和召回率。因此,你可以同时使用两者来得到一个 F1 分数,这可能更好地评估你的机器翻译模型的性能。在许多应用中,你会看到同时报告 F 分数以及 BLEU 和 ROUGE-N 指标。
然而,你必须注意,到目前为止你看到的所有评估指标都没有考虑句子结构和语义,它们只计算候选翻译和参考译文之间匹配的 N 元语法。
本节课中,我们一起学习了 ROUGE-N 分数的概念和计算方法。我们了解到 ROUGE 指标更侧重于召回率,与侧重精确率的 BLEU 指标形成互补。通过将两者结合为 F1 分数,我们可以获得一个更平衡的模型性能评估视角。但请记住,这些基于 N 元语法匹配的指标仍有其局限性,它们无法捕捉语言的深层语义和结构信息。
150:采样与解码 🧠

在本节课中,我们将学习两种从序列模型(如机器翻译模型)的预测结果中构建完整句子的方法:贪婪解码和随机采样。我们还将探讨一个控制采样随机性的重要超参数——温度。
模型输出回顾
上一节我们介绍了序列模型的基本结构。现在,我们来看看如何从模型的预测中生成最终的句子序列。
首先,快速回顾一下模型的输出过程。解码器每一步的输出会经过一个全连接层和一个Softmax(或Log Softmax)操作。因此,每一步的输出都是目标词汇表中所有单词和符号的一个概率分布。
模型的最终输出取决于你如何利用每一步的这个概率分布来选择单词。
贪婪解码 🔍
贪婪解码是最简单的解码方法。它在每一步都选择概率最高的单词作为输出。
以下是其核心逻辑的伪代码表示:
output_sequence = []
for step in decoding_steps:
# 获取当前步所有单词的概率分布
prob_distribution = model.predict(current_state)
# 选择概率最高的单词
chosen_word = argmax(prob_distribution)
output_sequence.append(chosen_word)
然而,这种方法存在局限性。虽然每一步都选择了局部最优(概率最高)的单词,但这不一定能生成全局最优的句子序列。
例如,模型可能会输出“I am M M M …”这样重复的序列,而不是正确的“I am hungry”。对于短序列,贪婪解码可能表现尚可。但当需要考虑后续多个单词时,仅关注当前步的最高概率可能会导致整体结果不佳。
随机采样 🎲
为了解决贪婪解码的局限性,我们可以采用随机采样。这种方法不是简单地选取最高概率词,而是根据概率分布进行随机抽样来选择下一个词。
随机采样的核心思想是:每个词被选中的机会与其概率成正比。这能带来更多样化的输出。
但纯粹的随机采样可能过于随机,导致输出不连贯或毫无意义。一个改进方法是,在抽样时给予高概率词更高的权重,同时降低低概率词被选中的机会。
温度参数 🌡️
在采样过程中,温度是一个关键的超参数,用于控制输出的随机性程度。温度值通常在0到1之间。
- 低温度(接近0):降低随机性。模型会更“自信”地选择高概率词,输出更确定、更保守,但也可能更乏味。
- 高温度(接近1):增加随机性。概率分布被“平滑”,低概率词有更多机会被选中。输出会更富有创造性、更多样,但也可能包含更多错误或不合理之处。
你可以将温度理解为调整模型“冒险精神”的旋钮。需要可靠、安全的输出时,调低温度;需要创意或多样化结果时,调高温度。
温度调整的公式通常如下(对Softmax前的logits进行操作):
scaled_logits = logits / temperature
probabilities = softmax(scaled_logits)
本节总结

本节课我们一起学习了两种从序列模型生成文本的方法:
- 贪婪解码:每一步选择概率最高的词。方法简单直接,但可能无法得到整体最优的序列。
- 随机采样:根据概率分布随机选择下一个词。能产生更多样化的输出,但可能过于随机。
我们还介绍了温度这个重要参数,它像是一个控制采样随机性程度的“调温器”,帮助我们在输出的“保守确定性”和“创意随机性”之间取得平衡。
然而,这两种基础方法有时产生的输出可能不够理想或过于随机。在接下来的课程中,我们将学习两种更高级的采样与解码策略(如集束搜索),它们通常能产生质量更高的结果。
151:束搜索 (Beam Search) 🔍

在本节课中,我们将学习一种在序列生成任务(如机器翻译)中,用于寻找更优输出序列的算法——束搜索。我们将了解为什么简单的“贪心”方法可能不够好,以及束搜索如何通过考虑多个候选序列来改进结果。
概述
之前介绍的方法(如贪心解码)在生成序列时,每一步只选择当前概率最高的词。这种方法可能无法得到整体概率最高的完整句子。束搜索通过在每个时间步保留多个(B个)最有可能的序列候选,来寻找更好的输出序列。
为什么需要束搜索?
直接在每个时间步选择概率最高的词(贪心解码)并不理想。因为整体概率最高的翻译序列,并不一定由每一步概率最高的词组成。例如,序列开头的选择可能会影响后续生成,导致最终结果不佳。
理论上,如果有无限的计算能力,可以计算所有可能输出句子的概率并选择最佳的一个。但在现实中,我们使用束搜索来近似这个目标。
束搜索的工作原理
束搜索试图通过在每个时间步基于条件概率保留一定数量的最佳序列,来找到最可能的输出句子。这个数量由束宽参数控制。
为了避免计算所有可能序列的概率,我们引入参数 B(束宽)。在每一步,只保留 B 个概率最高的序列,丢弃其他所有序列。生成过程持续进行,直到所有 B 个最可能的句子都以句子结束标记结尾。
有趣的事实:贪心解码是束搜索的一个特例,即束宽 B = 1。
束搜索步骤示例
为了说明这个方法,我们假设一个小词汇表,包含词语:I, am, hungry, <EOS>(句子结束标记)。并设束宽 B = 2。
与其他方法一样,束搜索以句子开始标记 <SOS> 开始。
-
第一步:模型输出第一个词的概率分布。假设:
I的概率是 0.5am的概率是 0.4hungry的概率是 0.1<EOS>的概率是 0.0
由于束宽为2,我们保留两个最高概率的词:
I和am。此时有两个候选序列:[I]和[am]。 -
第二步:对于上一步保留的每个序列,计算所有可能下一个词的条件概率。
- 给定序列
[I],模型返回:am的概率是 0.5hungry的概率是 0.3
- 给定序列
[am],模型返回:I的概率是 0.7hungry的概率是 0.2
接下来,计算完整序列的联合概率(将上一步序列的概率与当前条件概率相乘):
- 序列
[I, am]的概率:0.5 * 0.5 = 0.25 - 序列
[I, hungry]的概率:0.5 * 0.3 = 0.15 - 序列
[am, I]的概率:0.4 * 0.7 = 0.28 - 序列
[am, hungry]的概率:0.4 * 0.2 = 0.08
在第二步结束时,我们有四个可能的序列。但由于束宽 B=2,我们只保留概率最高的两个序列:
[am, I](0.28) 和[I, am](0.25)。丢弃[I, hungry]和[am, hungry]。 - 给定序列
-
后续步骤:对保留的序列
[am, I]和[I, am]重复上述过程。使用I作为[am, I]的输入,使用am作为[I, am]的输入,获取下一个词的条件概率,计算新的联合概率,并再次选择最好的两个序列。
这个过程持续进行,直到模型为所有 B 个最可能的序列预测出句子结束标记 <EOS>。最终,选择概率最大的序列作为输出。
模型如何参与束搜索?
要获得每一步的条件概率,必须使用你的序列生成模型。目前,我们主要关注模型的解码器部分。
- 在序列开始时,使用解码器输出词汇表中每个词的概率向量。这相当于计算给定
<SOS>标记后每个词的条件概率。 - 然后,从模型输出中选择概率最高的 B 个词,丢弃其他可能性。
- 之后,对于上一步保留的每个序列,都需要运行模型一次,以计算给定该序列条件下,词汇表中所有词的条件概率。
- 接着计算新序列的联合概率,并保留 B 个最可能的序列。
- 重复此过程,直到所有 B 个最可能的序列都生成
<EOS>标记。
注意:在第一步之后(此时序列不只包含
<SOS>标记),每一步都需要运行模型 B 次。
束搜索的优缺点
基础的束搜索版本有一些缺点。
- 对长序列的惩罚:序列的概率是多个条件概率的乘积。更长的序列会乘上更多的概率值(均小于1),因此其联合概率天然倾向于更小。为了避免这个问题,可以对每个序列的概率按其词数进行归一化(例如,使用平均对数概率)。
- 计算与内存成本:束搜索需要存储 B 个最可能的序列,并为每个序列计算条件概率。因此,当束宽 B 较大时,该方法计算成本较高且消耗大量内存。
总结
本节课我们一起学习了束搜索算法。它是一种通过在每个生成步骤保留多个候选序列,来寻找整体更优输出序列的搜索技术。我们了解了其工作原理、具体步骤、如何与模型结合使用,以及它的优缺点。束搜索是序列生成任务中一个经典且强大的工具。

现在你掌握了一个可以加入工具箱的新工具。束搜索已被广泛使用了相当长的时间,并且至今仍被大量使用。在下一个视频中,我将向你介绍另一种可以使用的技术——基于最小风险训练。
152:最小贝叶斯风险解码 🎯

在本节课中,我们将学习评估神经翻译系统的最后一种技术——最小贝叶斯风险解码。这是一种简单但效果出色的方法,尤其在与随机采样等技术对比时表现优异。
从随机采样到改进思路 🔄
上一节我们介绍了随机采样解码方法及其存在的问题。本节中,我们来看看如何通过一个简单的扩展来显著提升解码质量。
如果我们在随机采样的基础上更进一步,例如生成30个样本并让它们相互比较,解码效果将得到明显改善。
最小贝叶斯风险解码核心概念 🧠
最小贝叶斯风险解码需要比较多个候选翻译。其实现相当直接:
- 首先生成多个随机样本。
- 然后使用相似度分数或损失函数比较每对样本。Rouge分数是一个不错的选择。
- 最后选择具有最高平均相似度或最低损失的样本。
通过此方法得到的翻译最接近所有候选翻译。一些作者认为,这个过程可以看作是在所有候选翻译之间寻找共识。
核心公式与目标 🎯
如果决定使用Rouge分数作为比较每对候选翻译的相似度度量,MR可以总结为以下公式:
目标:找到候选翻译 E,使其与所有其他候选翻译 E‘ 的平均Rouge分数最大化。
实现步骤详解 ⚙️
MR相对容易实现,你需要准备多个候选翻译并选择一种比较方式。为了清晰起见,让我们更详细地了解一个实现过程。
以下是在一组四个候选翻译上使用Rouge实现MR的步骤:
- 计算第一个候选翻译 C₁ 与第二个候选翻译 C₂ 之间的Rouge分数。
- 计算 C₁ 与第三个候选翻译 C₃ 的Rouge分数。
- 计算 C₁ 与第四个候选翻译 C₄ 的Rouge分数。
- 使用这三个Rouge分数计算平均Rouge分数 R₁。
- 对集合中的其他三个候选翻译重复此过程,计算各自的平均Rouge分数。
- 最后,选择具有最高平均Rouge分数的候选翻译。
方法总结与展望 📚
本节课中,我们一起学习了最小贝叶斯风险解码。MR方法获取多个翻译候选,让它们相互比较,然后选择平均相似度最高的一个。与束搜索类似,此方法能提供比随机采样和贪婪解码更符合上下文的准确翻译。

恭喜完成本周学习!你现在已经知道如何实现神经机器翻译系统以及如何评估它。下周,我将介绍一种最先进的模型——Transformer,它同样采用编码器-解码器架构。
154:吴恩达《自然语言处理》P154 - 与Oren Etzioni的对话 🎙️

概述
在本节课中,我们将跟随吴恩达(Andrew Ng)与自然语言处理领域的知名人物奥伦·埃齐奥尼(Oren Etzioni)的对话,了解他进入AI领域的个人故事、在信息抽取方面的开创性工作、他领导的艾伦人工智能研究所(AI2)的使命,以及他对NLP技术发展、创业、学术与工业界选择、AI监管和职业发展的深刻见解。
奥伦·埃齐奥尼的AI之路 🚀
上一节我们介绍了本次对话的嘉宾。本节中,我们来看看奥伦·埃齐奥尼是如何开启他的AI生涯的。
奥伦·埃齐奥尼在高中时阅读了《哥德尔、埃舍尔、巴赫》一书,这让他对“智能的本质是什么”以及“如何构建具有类人能力的智能机器”这类基础科学问题产生了浓厚兴趣。这成为了他进入AI领域的起点。
在进入大学前,他开始学习Lisp编程语言,并发现编写Lisp代码充满乐趣。进入哈佛大学后,他决定学习计算机科学,认为这是通往AI的道路。
开放信息抽取的开创性工作 💡
上一节我们了解了奥伦进入AI领域的契机。本节中,我们来看看他在信息抽取领域的先驱性工作。
信息抽取旨在将句子映射为更结构化的信息。例如,将句子“Google acquired YouTube”映射为一个数据库元组:(acquisition, Google, YouTube)。
早期的信息抽取研究范围非常狭窄,通常只针对特定类型的事件(如并购、恐怖袭击)。奥伦的团队提出了“开放信息抽取”的想法,目标是从网络上的任何句子中提取信息,从而将海量的网络文本转化为一个强大而全面的知识库。
为了实现这个目标,他们必须极大地推广当时的技术。传统的信息抽取是机器学习技术,但需要针对特定关系(如“收购”、“研讨会地点”)的大量标注训练样本。而自然语言中可能表达数十万种不同的谓词关系,这需要海量的标注数据。
他们通过创建一种更具无监督风味的新技术来解决这个问题。其核心洞察是:句子表达关系的方式存在某些语言不变性和规律性。例如,动词通常是关系类型的强指示器。
公式/核心概念示例:
- 输入句子:
Joe married Betty. - 抽取的关系:
(marry, Joe, Betty) - 模式:
[Subject] [Verb_indicating_relation] [Object].
他们意识到,无论主题是什么,人们表达信息活动的方式都有某些固定的模式。这一发现在多种语言中都得到了验证,为学习算法提供了非常强的信号。
AI for Common Good:语义学者与COVID-19开放研究数据集 🌍
上一节我们探讨了开放信息抽取的技术突破。本节中,我们转向奥伦目前的工作,看看AI如何用于公益。
艾伦人工智能研究所(AI2)的使命是“AI for the common good”(AI造福大众)。他们思考如何利用AI,特别是NLP,让世界变得更美好。“语义学者”(Semantic Scholar)项目便是为此而生。
科学出版物的数量呈指数级增长,形成了“科学出版的摩尔定律”。即使是最勤奋的研究者也无法阅读所有相关文献。“语义学者”旨在利用AI帮助科学家和公众筛选海量论文。
以下是“语义学者”利用AI提供帮助的几个方面:
- 自动生成概要:为论文生成单行总结(TL;DR),帮助用户快速判断是否值得深入阅读。
- 自动提取图表:使用计算机视觉技术从PDF中自动定位并提取图表和表格,方便快速浏览核心信息。
2020年3月,COVID-19疫情初期,白宫联系了AI2,希望他们能利用快速处理论文集合的工具,整理所有相关论文(包括已发表和预印本),并以机器可读的形式提供,供AI系统、信息检索和搜索引擎使用。
AI2迅速联合了陈·扎克伯格基金会、微软、乔治城大学等机构,创建了 CORD-19(COVID-19开放研究数据集)。该数据集包含了超过20万篇论文,并每日更新,旨在捕捉关于新冠病毒的快速增长的文献,以便更迅速地回答相关问题。
给NLP创业者的建议与数据洞察 💼
上一节我们看到了AI在应对全球挑战中的实际应用。本节中,我们听听奥伦对NLP创业者的建议。
奥伦基于他创立AI公司的经验指出,一个关键问题是:你的数据从哪里来? 这不仅仅是需要大量数据,通常还需要大量标注。
他建议创业者深入思考:我的数据集是什么?数据从哪里来?标签从哪里来?他以自己最成功的公司Farecast(一家预测机票价格波动的公司,后被微软收购)为例。该公司拥有万亿级的标注数据点。
他们是如何获得这么多标签的呢?因为数据是时序数据。如果在12月1日预测某航班票价一周后会涨,只需等待一周即可验证预测是否正确。时间的流逝自动为数据生成了标签。这种观察使他们能够标注万亿数据点,从而做出非常强大的预测。
这与当前NLP的成功有深刻联系。像ELMo、BERT、RoBERTa、GPT-3这样的语言模型之所以成功,也利用了语言的内在顺序性。它们通过预测被掩盖的词来进行训练,而文本语料库本身也是自标注的。
代码/核心概念示例(简化版掩码语言建模):
# 原始句子: "The cat sat on the mat."
# 掩码后句子: "The cat [MASK] on the mat."
# 模型任务: 预测[MASK]位置最可能的词(如“sat”)。
# 标签来源: 原始句子中对应的词“sat”即为标签。
NLP的未来:模型规模、效率与绿色AI 🔮
上一节我们讨论了数据在AI中的核心地位。本节中,我们探讨NLP模型发展的未来趋势。
对于模型规模不断增大的趋势,奥伦承认自己曾预测其会趋于平缓,但事实证明他错了,因为性能仍在随规模提升。他认为,在性能出现显著平台期之前,模型规模和数据量可能会继续增长。
同时,任何计算机科学领域的发展规律通常是:先构建尽可能大的模型(某种程度上是“暴力”方法),然后再从各个角度进行优化,包括数据效率、数据选择策略以及计算效率。他认为我们将看到两者并行发展。
一个很好的类比是国际象棋程序:最初需要专用芯片和超级计算机,如今在笔记本电脑上就有更强的程序。这不仅是摩尔定律的功劳,也归功于更好的算法。与此同时,我们也扩展到了更复杂的游戏(如围棋)。
对于资源有限的研究者,奥伦指出,在小型数据集上仍有大量突破性研究可做。计算历史表明,昨天的超级计算机就是今天的智能手机。他希望这些惊人的巨型模型有一天也能在智能手表上运行。
艾伦人工智能研究所也在推动 “绿色AI” 研究,倡导在发布结果时考虑成本和效率。例如,用1000美元或仅10个训练样本能构建的最佳模型是什么?他们探讨 “盒子里的NLP” ,即仅用笔记本电脑或手机能获得的最佳NLP能力。这在隐私、网络连接或电池续航受限的场景下至关重要。
学术与工业界:如何选择职业道路 🧭
上一节我们展望了NLP技术的未来。本节中,我们转向个人职业发展,看看奥伦对选择学术或工业界道路的建议。
奥伦用了一个极客术语来回答:这取决于你试图优化什么。
- 如果你想优化报酬,或者追求一种像赛车或扑克游戏那样令人兴奋的肾上腺素激增的感觉,那么初创企业和私营部门自然具有吸引力。
- 如果你想最大化自由——提出自己问题的能力、不受干扰地深入思考基本智力问题的能力,那么学术界是无可替代的。
在他的职业生涯中,不同阶段侧重不同。在CMU读研究生时,他可以花数月时间深入钻研一个问题,这是学术上的亮点。而在创业时,组建团队、奋力拼搏、体验自己亲手打造的事物的过山车式旅程,则是另一种难以置信的感受。
关于AI监管与偏见问题的思考 ⚖️
上一节我们探讨了职业选择。本节中,我们进入一个更宏观且重要的话题:AI监管。
针对NLP模型从互联网文本中学习到不良偏见的问题,奥伦认为这是当前NLP领域一个非常棘手的问题。
他的核心观点是:应监管具体应用,而非基础研究。NLP是一项广泛的技术,容易产生偏见,但需要监管的是这种偏见在特定应用中的表现。
例如,如果构建的简历筛选应用表现出对男性优于女性的偏见,这显然是有问题的、非法的。应该对此类应用进行审计并禁止偏见出现。但他非常不希望基于这些观念去监管NLP的基础研究。
他强调了 “审计权” 的重要性。与其像欧盟某些提案那样要求模型提供“解释权”(这对于复杂的深度学习模型可能产生难以理解或不准确的解释),不如坚持审计权。监管机构或第三方(如ACLU、学术界)应有权访问模型,审计其行为,检查是否存在偏见。这样可以依靠思想市场和不同机构(如记者、非营利组织)之间的互动来相互制衡,这种局面更为稳健。
给NLP新手的职业建议 🎯
上一节我们讨论了AI治理的重要议题。在本节最后,我们听听奥伦给希望进入或深耕NLP领域的人的建议。
以下是奥伦给出的职业发展步骤:
- 打好基础:确保掌握统计学、计算机科学和机器学习的基本原理。这是至关重要的,因为技术的“月度风味”(如当前的Transformer)变化很快,扎实的基础能让你适应变化。
- 利用在线课程:在线课程极其成功、成本效益高且易于获取,是下一步学习的绝佳途径。
- 亲自动手实践:在学习了课程之后,没有比亲自实践更好的方法了。选择一个真实的问题和一个数据集,自己动手做。你可能会发现你感兴趣的问题比你预期的更容易,或者你可能会发现某些概念理解得不够好,或者问题比看起来更难,这可能会引导你产生新的发明或想法。
总结

本节课中,我们一起学习了奥伦·埃齐奥尼的AI生涯历程,了解了他在开放信息抽取方面的开创性工作,以及他通过“语义学者”和CORD-19项目践行“AI for Common Good”的理念。我们还探讨了他对NLP创业(关注数据来源)、技术未来(规模与效率并存)、职业选择(明确优化目标)、AI监管(聚焦应用审计)以及给初学者的宝贵建议(夯实基础、利用课程、动手实践)。希望这些见解能激励和指导正在NLP道路上探索的学习者和从业者。
156:Transformer与RNN的比较 🧠
在本节课中,我们将学习Transformer模型。这是一个完全基于注意力机制的模型,由Google开发,旨在解决循环神经网络(RNN)存在的一些问题。首先,我们会了解RNN面临的具体挑战,从而理解为何需要Transformer模型。接着,我们将对Transformer模型进行一个具体的概述。
RNN的局限性 ⏳

上一节我们介绍了课程目标,本节中我们来看看传统神经网络架构,特别是RNN,在机器翻译等任务中存在的问题。
在神经机器翻译中,通常使用RNN架构将一种语言(例如英语)翻译成另一种语言(例如法语)。使用RNN时,你必须按顺序步骤对输入进行编码。你需要从输入的开头开始,在每一步进行计算,直到处理完整个句子。此时,你才能开始遵循类似的顺序过程来解码信息。
正如这里所示,你必须按顺序处理输入中的每一个单词,从第一个词开始,然后是第二个词,一个接一个。翻译过程同样也是顺序进行的。因此,这种架构没有太多并行计算的空间。输入句子中的单词越多,处理该句子所需的时间就越长。
在一个更通用的序列到序列架构中,为了将信息从第一个单词传播到最后的输出,模型必须经过 T 个顺序步骤。其中,T 是一个整数,代表模型处理一个示例句子输入所需的时间步数。例如,如果你输入一个由五个单词组成的句子,那么模型将需要五个时间步来编码该句子,此时 T = 5。
正如本专项课程早期内容所回顾的,对于长序列,信息容易在网络中丢失,并且会出现梯度消失问题。LSTM和GRU在一定程度上缓解了这些问题,但即使是这些架构,在处理非常长的序列时,由于信息瓶颈的存在,其性能也会下降,正如我们在上周课程中所见。
总结一下,RNN架构存在信息丢失和梯度消失的问题。
注意力机制与Transformer的引入 💡
上一节我们探讨了RNN的瓶颈,本节中我们来看看注意力机制如何提供解决方案,并引出纯粹的注意力模型——Transformer。
在上一周的课程中,我们了解到在模型中引入注意力机制是解决这些问题的一种方法。你已经看到并实现了一个带有注意力机制的序列到序列架构,类似于此处描述的模型。回想一下,你的编码器和解码器依赖于LSTM,但你也可以使用GRU或普通的RNN。
相比之下,Transformer仅依赖于注意力机制,不需要使用循环网络。在Transformer中,注意力就是全部所需。当然,通常也会包含一些线性和非线性变换,但核心思想就是如此。
现在你理解了为什么RNN可能很慢,并且在处理长上下文时存在重大问题。这些正是Transformer可以发挥作用的情况。
以下是RNN与Transformer的核心对比:
- RNN:处理依赖顺序计算,难以并行化,存在长程依赖问题。
- Transformer:处理依赖注意力机制,易于并行化,能有效捕捉长程依赖。
总结 📝

本节课中我们一起学习了Transformer模型出现的背景。我们首先分析了基于RNN的序列模型在处理长序列时面临的主要挑战:顺序计算导致的低效率和长程依赖导致的信息丢失/梯度消失。接着,我们了解到注意力机制是解决这些问题的关键,而Transformer模型则完全摒弃了循环结构,纯粹依靠注意力机制来构建,为高效处理序列数据提供了新的强大工具。
接下来,我们将进入下一个视频,对Transformer模型进行更具体的概述。
157:17_Transformer概述 🧠

在本节课中,我们将要学习Transformer模型的核心概念与整体架构。Transformer是当今大型语言模型的基石,理解其工作原理对于掌握现代自然语言处理技术至关重要。
概述
Transformer模型由Google研究人员于2017年提出,并迅速成为大型语言模型(如BERT、T5、GPT-3)的标准架构。它彻底改变了自然语言处理领域。其核心论文《Attention Is All You Need》是本课程后续所有模型的基础。本节将为您提供一个简要的架构概览,后续课程会深入每个组件。
Transformer核心:缩放点积注意力
Transformer模型的核心是缩放点积注意力机制。该机制计算高效,仅由矩阵乘法操作构成。
公式:Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V
其中,Q代表查询(Query),K代表键(Key),V代表值(Value),d_k是键向量的维度。这种机制允许模型在处理序列时,关注输入中不同部分的信息。
多头注意力层
在Transformer中,缩放点积注意力被扩展为多头注意力层。该层并行运行多个注意力机制,并对输入的查询、键和值进行多次线性变换。这些线性变换的参数是可学习的。
代码描述:
# 伪代码示意
multihead_output = MultiHeadAttention(query, key, value, num_heads)
上一节我们介绍了核心的注意力机制,本节中我们来看看如何将其组合成更强大的模块。
编码器结构
Transformer编码器以多头注意力模块开始,该模块对输入序列执行自注意力操作,即序列中的每个词都会关注序列中的所有其他词。
以下是编码器一个层(Block)的主要组成部分:
- 一个多头注意力模块。
- 一个残差连接与层归一化步骤。
- 一个前馈神经网络层。
- 另一个残差连接与层归一化步骤。
整个编码器层会重复堆叠多次。通过自注意力层,编码器为每个输入词生成一个包含上下文信息的表示。
解码器结构
解码器的构造与编码器类似,也包含多头注意力模块、残差连接和归一化层。
以下是解码器的关键特点:
- 第一个注意力模块是掩码多头注意力,确保每个位置只能关注它之前的位置,防止信息向左流动(即防止看到“未来”的信息)。
- 第二个注意力模块接收编码器的输出,允许解码器关注输入序列中的所有项。
- 整个解码器层同样会重复堆叠多次。
位置编码
由于Transformer不使用循环神经网络(RNN),而词序对任何语言都至关重要,因此模型需要一种方法来理解单词在序列中的位置。这就是位置编码的作用。
位置编码可以是学习得到的,也可以是固定的。它的值会被加到词嵌入向量中,这样每个输入词都包含了其顺序位置的信息。
公式示例(正弦位置编码):
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
完整架构与优势
将以上部分组合起来,就构成了完整的Transformer模型架构。
- 左侧,输入句子首先被嵌入,并加上位置编码,然后送入由多个编码器层堆叠而成的编码器。
- 右侧,解码器接收右移一位的目标输出句子以及编码器的输出。
- 解码器的输出通过一个线性层和Softmax激活函数,转化为输出词的概率分布。
与RNN模型相比,这种架构易于并行化,因此可以在多个GPU上进行更高效的训练。它也能扩展到越来越大的数据集上学习多项任务。
与RNN的对比
RNN因其顺序结构存在一些问题。对于RNN,很难充分利用并行计算的优势。对于长序列,重要信息可能会在网络中丢失,并出现梯度消失问题。
幸运的是,Transformer的出现解决了RNN的这些缺点。Transformer是RNN的优秀替代方案,帮助克服了NLP乃至许多处理序列数据领域中的这些问题。
总结

本节课中我们一起学习了Transformer模型的整体架构。我们了解到,Transformer基于高效的多头自注意力机制,通过编码器-解码器结构以及关键的位置编码,克服了传统RNN模型在并行化和长程依赖上的不足,从而成为现代自然语言处理的基石。现在您可以理解为何Transformer如此备受瞩目,它确实非常有用。在下一视频中,我们将探讨Transformer的一些具体应用。
158:Transformer 应用 🚀

在本节课中,我们将学习 Transformer 模型在自然语言处理(NLP)及其他领域的广泛应用。我们将了解其最流行的应用场景,并深入探讨几个强大的 Transformer 模型,特别是 T5 模型的多任务处理能力。

Transformer 的广泛应用
Transformer 是最通用的深度学习模型之一。它已成功应用于 NLP 内外的众多任务。
上一节我们介绍了 Transformer 的基本概念,本节中我们来看看它在 NLP 中的具体应用。由于 Transformer 和 RNN 一样,可以普遍应用于任何序列任务,因此它在 NLP 领域得到了广泛使用。
以下是 Transformer 在 NLP 中的一些非常有趣且流行的应用:
- 自动文本摘要
- 自动补全
- 命名实体识别
- 自动问答
- 机器翻译
- 聊天机器人
- 情感分析和市场情报等其他许多 NLP 任务
先进的 Transformer 模型
许多 Transformer 的变体被用于 NLP,研究人员通常会给他们的模型起独特的名字。
以下是几个著名的先进 Transformer 模型:
- GPT-2:代表“Generative Pre-training for Transformer”,是由 OpenAI 创建的预训练 Transformer。它非常擅长生成文本。
- BERT:代表“Bidirectional Encoder Representations from Transformers”,由 Google AI 语言团队推出,是另一个用于学习文本表示的著名 Transformer。
- T5:代表“Text-to-Text Transfer Transformer”,同样由 Google 创建,是一个可以执行多种任务的多任务 Transformer。
深入探索 T5 模型
一个单一的 T5 模型可以学习执行多个不同的任务,这是一个相当重大的进步。
例如,假设你想执行翻译、分类和问答等任务。通常,你需要设计并训练一个模型来执行翻译,再设计并训练第二个模型来执行分类,然后设计并训练第三个模型来执行问答。但使用 Transformer,你可以训练一个能够执行所有这些任务的单一模型。
为了告诉 T5 模型你想要执行某个特定任务,你需要给模型一个输入文本字符串,其中包含你希望它执行的任务以及执行该任务所需的数据。
以下是 T5 模型处理不同任务的示例:
1. 翻译任务
输入字符串格式为:translate English to French: [待翻译的英文句子]
例如:
输入:translate English to French: I am happy.
输出:Je suis content.
2. 分类任务
输入字符串以 cola sentence: 开头,模型会将其后的句子分类为“可接受”或“不可接受”。
例如:
输入:cola sentence: He bought fruits and
输出:unacceptable
输入:cola sentence: He bought fruits and vegetables.
输出:acceptable
3. 问答任务
输入字符串以 question: 开头。
例如:
输入:question: Which volcano in Tanzania is the highest mountain in Africa?
输出:Mount Kilimanjaro
请记住,所有这些任务都由同一个模型完成,除了输入句子外,无需任何修改。
T5 的其他任务能力
T5 还能执行回归和摘要总结任务。
1. 回归任务
回归模型输出一个连续的数值。以下是一个相似度测量的例子,输入以 stsb 开头,表示模型应测量两个句子之间的相似度。输出范围是 0 到 5 之间的任何数值,0 表示句子完全不相似,5 表示句子非常相似。
例如:
输入:stsb sentence1: Cats and dogs are mammals. sentence2: These are four known forces in nature: gravity, electromagnetic, weak and strong.
输出:0
输入:stsb sentence1: Cats and dogs are mammals. sentence2: Cats and dogs and cows are domesticated.
输出:2.6
2. 摘要任务
以下是一个摘要总结的例子,它将一篇关于密西西比州恶劣天气的长篇报道总结为一句话。
输入:summarize: [长篇文章内容]
输出:Six people hospitalized after a storm in Attala County.
总结

本节课中我们一起学习了 Transformer 在 NLP 中的应用,其范围从翻译到摘要总结。一些先进的 Transformer 模型包括 GPT-2、BERT 和 T5。我们还看到了 T5 模型是多么通用和强大,因为它可以使用文本表示来执行多项任务。现在你知道了为什么我们需要 Transformer 以及它可以应用在何处。
一个模型能够处理如此多样的任务,这令人惊叹。希望你现在渴望了解 Transformer 的工作原理,而这正是我接下来要展示的内容。让我们进入下一个视频。😊
159:缩放点积注意力机制 🧠

在本节课中,我们将要学习Transformer模型的核心操作——缩放点积注意力。我们将回顾其工作原理、数学公式以及涉及的矩阵维度,并理解它为何如此高效。
概述
缩放点积注意力机制是Transformer架构的基石。它接收查询、键和值矩阵作为输入,通过计算查询与键的相似度来为值分配权重,最终输出每个查询对应的上下文向量。其高效性源于仅依赖于矩阵乘法和Softmax函数。
缩放点积注意力公式回顾
首先,我们来回顾缩放点积注意力的核心公式:
Attention(Q, K, V) = softmax( (Q * K^T) / sqrt(d_k) ) * V
其中:
- Q 代表查询矩阵。
- K 代表键矩阵。
- V 代表值矩阵。
- d_k 是键向量的维度。
- softmax 函数确保所有权重之和为1。
- 除以 sqrt(d_k) 是为了在训练时稳定梯度,提升模型性能。
这个机制非常高效,因为它只依赖于矩阵乘法和Softmax操作,可以轻松在GPU或TPU上并行加速训练。
查询、键、值矩阵的构建
要应用上述公式,我们首先需要从输入序列中构建查询、键和值矩阵。
查询矩阵的构建
假设我们的查询源句子是 “just do it”。我们需要获取每个单词的词嵌入向量:
- 获取单词 “just” 的嵌入向量。
- 获取单词 “do” 的嵌入向量。
- 获取单词 “it” 的嵌入向量。
然后,将这些嵌入向量作为行堆叠起来,就形成了查询矩阵 Q。矩阵的尺寸由词嵌入的维度和序列的长度决定。
键与值矩阵的构建
假设我们的键/值源句子是 “I am happy”。同样地:
- 获取单词 “I” 的嵌入向量。
- 获取单词 “am” 的嵌入向量。
- 获取单词 “happy” 的嵌入向量。
将这些向量作为行堆叠,形成键矩阵 K。通常,值矩阵 V 使用与键矩阵相同的向量集(但也可以先进行一个额外的变换)。需要注意的是,用于构建键矩阵和值矩阵的向量数量必须相同。
矩阵维度与计算过程详解
现在,让我们结合公式,详细看看每一步计算中矩阵的维度变化。
首先,计算查询矩阵 Q 与键矩阵 K 的转置的乘积:
Q * K^T
然后,将结果除以键向量维度 d_k 的平方根进行缩放:
(Q * K^T) / sqrt(d_k)
接着,对缩放后的结果应用 softmax 函数。这个计算会得到一个权重矩阵,其中的每个元素代表了对于某个特定查询,分配给每个键的权重。
因此,这个权重矩阵的总元素数等于 查询数量 × 键数量。例如,该矩阵第二行第三列的元素,就对应着第二个查询分配给第三个键的权重。
最后,将这个权重矩阵与值矩阵 V 相乘:
softmax(...) * V
相乘的结果是一个矩阵,其每一行就是对应每个查询的上下文向量。这个结果矩阵的列数等于值向量的维度,通常与词嵌入的维度相同。
总结
本节课中,我们一起深入学习了缩放点积注意力机制。我们回顾了其核心公式,理解了如何从输入序列构建查询、键和值矩阵,并逐步分析了计算过程中矩阵维度的变化。该机制因其仅基于矩阵运算而具备的高效性,成为了Transformer模型强大性能的关键。

在Transformer的解码器部分,我们需要使用一个扩展版本,称为掩码自注意力机制。我们将在下一个视频中学习它。
160:Transformer中的注意力机制 🧠

在本节课中,我们将学习Transformer模型中的三种主要注意力机制,并重点掌握掩码自注意力的计算方法。

概述
Transformer模型的核心是注意力机制,它使模型能够专注于输入序列中最相关的部分。我们将介绍三种主要的注意力类型:编码器-解码器注意力、自注意力和掩码自注意力。本节课的重点是理解并计算掩码自注意力。
三种主要的注意力机制
上一节我们概述了课程内容,本节中我们来看看Transformer模型中三种核心的注意力机制。
以下是三种主要的注意力类型:
-
编码器-解码器注意力
- 在这种机制中,一个句子中的词关注另一个句子中的所有词。
- 具体来说,查询(Queries)来自一个句子,而键(Keys)和值(Values)来自另一个句子。
- 你在上周的翻译任务中已经使用过这种注意力,例如,法语句子中的词关注英语句子中的词。
-
自注意力
- 在自注意力中,查询、键和值都来自同一个句子。
- 因此,序列中的每个词都会关注序列中的其他所有词。
- 这种注意力让你能获得词语的上下文表示,换句话说,自注意力提供了每个词在句子中的含义表示。
-
掩码自注意力
- 在掩码自注意力中,查询、键和值同样来自同一个句子。
- 但每个查询不能关注未来位置的键。
- 这种注意力机制存在于Transformer模型的解码器中,确保每个位置的预测仅依赖于已知的输出。
深入理解掩码自注意力
了解了三种注意力机制的区别后,本节我们重点探讨掩码自注意力的工作原理。
从数学上讲,自注意力的计算与编码器-解码器注意力完全相同,唯一的区别在于每种机制的输入来源不同。因此,让我们聚焦于掩码自注意力。
回忆一下,缩放点积注意力需要计算查询矩阵与键矩阵转置的缩放点积的Softmax。
公式表示如下:
注意力权重 = Softmax( (Q * K^T) / sqrt(d_k) )
而对于掩码自注意力,你需要在Softmax内部添加一个掩码矩阵。这个掩码矩阵在其所有位置上的值都是0,除了对角线以上的元素,这些元素被设置为负无穷大(在实践中是一个极大的负数)。
代码逻辑描述如下:
# 假设 QK^T 是查询和键的点积结果矩阵
scores = QK^T / sqrt(d_k)
# 创建掩码矩阵(下三角为0,上三角为负无穷)
mask = np.triu(np.ones(scores.shape), k=1) * -np.inf
# 应用掩码
masked_scores = scores + mask
# 计算注意力权重
attention_weights = softmax(masked_scores)
在应用Softmax之后,这种加法操作确保了在权重矩阵中,对于查询位置之后的所有键,其对应的注意力权重均为0。
最后,与其他类型的注意力一样,你将权重矩阵与值矩阵相乘,从而得到每个查询的上下文向量。
总结其核心: 你只需要在Softmax函数内部添加一个矩阵,就能确保查询不会关注到未来的位置。

总结与预告
本节课中,我们一起学习了Transformer中的三种主要注意力机制:编码器-解码器注意力、自注意力和掩码自注意力。在掩码自注意力中,查询和键包含在同一句子中,但查询不能关注未来的位置。
到目前为止,你已经见识了许多类型的注意力机制。在下一个视频中,我将向你展示多头注意力。这是一种非常强大的注意力形式,它允许并行计算,能显著提升模型的表达能力。
161:多头注意力机制 🧠

在本节课中,我们将学习Transformer模型中的一个核心组件——多头注意力机制。你已经掌握了注意力机制的基础知识,但为了构建性能更优、运行更快、效果更好的模型,多头注意力是必不可少的一环。我们将首先了解其背后的直观思想,然后深入探讨其数学原理和实现细节。
多头注意力的直观理解
上一节我们介绍了基础的缩放点积注意力。本节中,我们来看看如何通过多头注意力让模型同时关注来自不同表示子空间的信息。
在多头注意力中,模型会并行地应用多次注意力机制。每一次应用都使用一组通过线性变换得到的、不同于原始嵌入的查询、键和值矩阵。模型并行应用注意力机制的次数,就称为“头”的数量。
例如,在一个双头注意力模型中,你需要两组查询、键和值。第一组表示用于第一个注意力头,第二组不同的表示则用于第二个注意力头。通过为模型中的每个头使用不同的线性变换矩阵(记作 WQ**、**WK、W^V),我们可以从原始词嵌入中得到这些不同的表示。
使用不同的表示集合,使得模型能够学习查询和键矩阵中词语之间的多种关系。
多头注意力工作原理
理解了基本概念后,现在我们来详细看看多头注意力是如何工作的。其输入是值矩阵(V)、键矩阵(K)和查询矩阵(Q)。
以下是多头注意力计算的主要步骤:
第一步,将每个输入矩阵(Q, K, V)通过线性变换投影到多个向量空间。每个矩阵的变换次数等于模型中的头数。
第二步,对每一组变换后的值、键和查询表示,并行地应用缩放点积注意力机制。应用次数同样等于头数。
第三步,将每个注意力头计算出的结果矩阵在水平方向上进行拼接,形成一个单一的矩阵。
第四步,对这个拼接后的矩阵施加一次最终的线性变换(W^O),得到输出的上下文向量。
需要注意的是,多头注意力中的每一次线性变换都包含一组可学习的参数。
详细步骤与维度分析
让我们以一个双头模型为例,更细致地分析每一步的维度变化。
多头注意力层的输入是查询、键和值矩阵。这些矩阵的列数等于嵌入维度 d_model,行数则由用于构建矩阵的序列词数决定。
第一步是使用每个头独有的矩阵 WQ**、**WK、W^V 对 Q、K、V 进行变换。变换矩阵 W^Q 和 W^K 的列数是一个超参数 d_k,W^V 的列数是一个超参数 d_v。它们的行数均为 d_model。
在原始的Transformer论文中,作者建议设置 d_k = d_v = d_model / h,其中 h 是头的数量。这种尺寸选择能确保多头注意力的总计算成本不会显著超过单头注意力。
对每个头得到变换后的 Q、K、V 矩阵后,便可以并行应用注意力机制。每个头会输出一个矩阵,其列维度为 d_v,行数与原始查询矩阵相同。
接着,将所有注意力头的输出矩阵水平拼接。你会得到一个列数为 d_v * h 的矩阵。
最后,对这个拼接矩阵应用线性变换 WO**。**WO 的列数为 d_model。如果按照上述建议设置了 d_v,那么 W^O 的行数也将是 d_model。与单头注意力一样,最终你会得到一个矩阵,其中包含了每个原始查询的、维度为 d_model 的上下文向量。
总结
本节课中,我们一起学习了多头注意力机制的工作原理,并了解了其计算过程中涉及的参数矩阵维度。
多头注意力的核心在于:对查询、键和值的多组不同表示并行应用注意力机制,然后将每个注意力计算的结果拼接起来,再通过一次线性变换得到最终的上下文向量。

通过合理的变换矩阵尺寸选择,多头注意力的总计算时间与单头注意力相近,却能显著提升模型捕捉信息的能力。在过去的三个视频中,我们学习了基础的点积注意力、因果注意力以及多头注意力。现在,你已经准备好构建自己的Transformer解码器了,这将是我们在下一个视频中要完成的任务。
162:Transformer解码器 🧠
在本节课中,我们将学习如何构建一个Transformer解码器模型,也被称为GPT-2。一旦理解了注意力机制,这个模型的结构就相对简单明了。让我们开始吧。
概述
本节课我们将深入探讨Transformer解码器的基本结构。我们将了解Transformer的定义,并学习如何实现解码器块和前馈神经网络块。


Transformer解码器结构
Transformer解码器的输入是一个经过分词的句子,即一个整数向量。


句子首先通过词嵌入层进行嵌入。然后,我们为这些嵌入向量添加位置信息。

位置信息是通过学习得到的向量来表示的,这些向量代表位置1、2、3……直到模型设定的最大长度。因此,第一个词的嵌入向量会加上代表位置1的向量,第二个词的嵌入向量会加上代表位置2的向量,依此类推。


现在,这构成了第一个多头注意力层的输入。在注意力层之后,是一个前馈神经网络层。

前馈层独立地对每个位置进行操作。在每个注意力层和前馈层之后,会有一个残差连接(或称跳跃连接),即将该层的输入加到其输出上,然后执行层归一化。
注意力层和前馈层会重复N次。原始模型设定N=6,但现在的Transformer模型可能达到100层甚至更多。


最后,是一个用于输出的全连接层和一个Softmax层。这就是Transformer解码器的基本结构。
如果一次没有完全理解,请不要担心。接下来我们将结合代码再次梳理整个结构,并解释所有细节。
模型核心组件
现在,让我们看看Transformer模型的核心部分。模型开头有三层。
“右移”操作只是引入了起始标记,模型将使用这个标记来预测下一个词。然后是嵌入层,它训练词到向量的嵌入。接着是位置编码层,它训练代表位置1、2……等的向量,如前所述。
如果模型的输入是一个形状为 [batch_size, sequence_length] 的张量,那么在经过嵌入层后,它将变成形状为 [batch_size, sequence_length, d_model] 的张量。其中,d_model 是嵌入向量的维度,通常设置为512、1024,现在甚至可以达到10K或更高。
在这些初始层之后,是N个解码器块。然后是一个全连接层,输出形状为 [batch_size, sequence_length, vocab_size] 的张量。最后使用Log Softmax函数来计算交叉熵损失。


解码器块详解
解码器块以一组向量作为输入序列开始,这些向量会与相应的位置编码向量相加,产生所谓的“位置输入嵌入”。
嵌入后,输入序列通过一个多头注意力模型。当这个模型处理输入序列中的每个词(每个位置)时,注意力机制本身会搜索序列中的其他位置,以帮助识别词之间的关系。
序列中的每个词都会被赋予一个权重。然后,在注意力层的每一层,都有一个围绕它的残差连接,随后是一个层归一化步骤,以加速训练并显著减少总体处理时间。
接着,每个词会通过一个前馈神经网络层。也就是说,嵌入向量被送入一个神经网络。最后,为了正则化,会有一个Dropout层。
接下来,层归一化步骤会重复N次。最终,得到解码器块的输出。

前馈神经网络的作用

在注意力机制和归一化步骤之后,通过包含全连接的前馈神经网络层引入了非线性变换。
对于每个输入,使用简单但非线性的ReLU激活函数。为了效率,参数是共享的。前馈神经网络的输出向量本质上将取代原始RNN解码器中的隐藏状态。


总结
在本节课中,我们一起学习了用于实现Transformer解码器的构建模块。
我们了解到它包含三个初始层。还有一个用于计算Log Softmax的模块,该模块利用了交叉熵损失函数。我们还详细了解了解码器块和前馈神经网络块的结构。


现在,你已经构建了自己的第一个Transformer模型。恭喜!如果能看看它的实际运行效果,那该多好。😊
163:Transformer摘要生成器 📝

在本节课中,我们将学习如何利用Transformer模型构建一个文本摘要生成器。我们将从模型代码概览开始,接着探讨用于摘要任务的数据处理技术细节,最后了解如何使用训练好的语言模型进行推理。
模型与任务概述
首先,我们来看看本周作业中需要解决的问题。模型的输入是一整篇新闻文章,而模型的输出则是这篇文章的摘要,即概括文章核心思想的几句话。
为了实现这个目标,我们将使用在前几节课程中介绍过的Transformer模型。但有一个关键点可能立刻会引起你的注意:Transformer模型只接受文本作为输入,并预测输出。对于摘要任务,解决方案其实很简单:只需要将输入(即文章)和摘要拼接在一起。下面我来具体说明。
输入特征构建
以下是利用文章及其摘要来构建Transformer训练输入特征的示例。
模型的输入是一个长文本,它以新闻文章开头,接着是 EOS 标记,然后是摘要,最后是另一个 EOS 标记。与往常一样,输入会被分词并转换为整数序列。在这个序列中,0 代表填充,1 代表 EOS,其他数字则代表不同单词的标记。
输入序列结构示例:
[文章内容] + [EOS] + [摘要内容] + [EOS]
当Transformer在这个输入上运行时,它会根据之前的所有单词来预测下一个单词。但你不希望仅仅因为模型无法正确预测文章部分的内容而产生巨大的损失。这就是为什么必须使用加权损失函数。
加权损失函数
在计算整个序列中每个单词的损失时,我们不进行平均,而是对文章部分的单词损失赋予权重 0,对摘要部分的单词损失赋予权重 1。这样,模型就能将注意力完全集中在生成摘要上。
然而,当摘要的训练数据量很少时,给文章部分的损失赋予一个非零的权重(例如 0.2、0.5 甚至 1)实际上是有益的。通过这种方式,模型能够学习到新闻文章中常见的词语关系。虽然在本周的作业中你不需要这样做,但了解这一点对你未来的应用开发很有帮助。
另一种理解上述内容的方式是查看损失函数。该函数对批次中每个样本 i 的摘要部分的所有单词 j 的损失进行求和。因此,这个交叉熵损失函数会忽略待摘要的文章部分。
损失函数公式:
Loss = Σ_i Σ_j_in_summary CrossEntropy(y_hat_ij, y_ij)
模型训练与推理
现在你知道了如何构建输入和模型,接下来就可以训练你的Transformer摘要生成器了。请再次回忆,Transformer是预测下一个单词的模型,而你的输入是新闻文章。
在测试或推理阶段,你需要将文章连同 EOS 标记一起输入模型,并请求它预测下一个单词,这个单词就是摘要的第一个词。然后,你继续请求下一个词,如此反复,直到模型输出 EOS 标记为止。
当运行Transformer模型时,它会生成一个覆盖所有可能单词的概率分布。你需要从这个分布中进行采样,因此每次运行这个过程,都可能得到一个不同的摘要。我相信你会在编程练习中享受实验这一过程的乐趣。
本节总结
在本节视频中,你学习了如何实现一个用于摘要任务的Transformer解码器。关键点在于,该模型旨在优化一个加权的交叉熵损失函数,该函数专注于摘要部分。摘要任务本质上就是以整篇文章为输入的文本生成任务。

课程回顾与展望 🚀
本周,你已经学会了如何构建自己的Transformer模型,并利用它创建了一个摘要生成器。我希望你享受了这个学习过程。Transformer是一个非常强大且不难理解的模型。
下周,我将向你展示如何获得更好的结果。我们将使用一个更强大的、经过预训练的Transformer版本。敬请期待,不要错过!
166:第3周概述 🚀
在本节课中,我们将要学习自然语言处理(NLP)中两个重要的高级概念:迁移学习 和 问答系统。我们将了解这些概念如何提升模型性能,并探索BERT和T5等先进模型的工作原理。

本周内容概览 📚
在课程第四部分的第3周,我们将涵盖自然语言处理的多种不同应用。
我们将首先关注问答系统。具体来说,给定一个问题和一个上下文,模型需要找出该上下文中的答案。
我们还将深入探讨迁移学习。这个概念指的是,在一个特定任务上训练获得的知识,如何被利用并应用到另一个不同的任务中。
我们将学习BERT模型,它被称为双向编码器表示,并利用了Transformer架构。你将看到如何利用双向性来提升模型性能。
接着,我们将了解T5模型。这个模型的特点是,它可以处理多种可能的输入。例如,输入一个问题,它输出一个答案;输入一篇评论,它输出一个评分。所有这些功能都集成在一个模型中。
问答系统详解 ❓
现在,让我们具体看看问答系统。
这里有两种主要类型:基于上下文的问答 和 闭卷式问答。
基于上下文的问答需要同时接收问题和一段上下文,然后模型会指出答案在该上下文中的具体位置。例如,图中高亮部分就是模型找到的答案。
闭卷式问答则只接收问题,模型需要在不依赖外部上下文的情况下,自行生成答案。
迁移学习:提升性能的新途径 🔄
之前我们已经看到,模型架构的创新和数据准备的方法都能提升性能。
本节中,我们将看到训练方式的创新同样能显著改善性能,而迁移学习正是这样一个关键创新。
以下是传统的训练方式,你可能已经很熟悉了:输入一篇课程评论,经过模型处理,预测出一个评分。整个过程没有变化。
现在,让我们通过一个例子来理解迁移学习。
假设你有一个在电影评论上预训练好的模型,用于预测电影评分。当你想训练一个预测课程评分的模型时,迁移学习的做法是:不再从零开始初始化模型权重,而是使用从电影评论任务中学到的权重作为起点,然后在此基础上,用课程评论数据继续训练。
训练完成后,进行推理的方式不变:输入课程评论,模型输出预测评分。
迁移学习也可以应用于完全不同的任务。
这是另一个例子:一个模型输入评分和评论,输出情感分类。你可以将这个模型的初始权重,用于训练一个下游的问答任务模型。例如,模型学会回答“圆周率日是哪天?”(答案是3月14日)。但如果你问它“我的生日是哪天?”,它并不知道答案。这个例子展示了如何将迁移学习用于跨任务的知识迁移。
探索BERT:双向上下文的力量 ↔️
上一节我们介绍了迁移学习的概念,本节中我们来看看BERT模型,它利用了双向上下文。
假设我们有句子:“Learning from DeepLearning.AI is like watching the sunset with my best friend.” 在传统的语言模型中,要预测下一个词“DeepLearning.AI”,模型只能看它前面的上下文。
而双向表示则同时考虑目标词左侧和右侧的上下文信息,来预测中间的词。这是双向性的一个主要优势。
单任务模型 vs. 多任务模型:T5的整合之道 🤖
之前我们讨论了BERT的双向架构,现在我们来对比单任务模型和多任务模型。
在单任务模型中,一个模型专门用于输入评论并预测评分;另一个独立的模型则专门用于输入问题并预测答案。每个任务对应一个模型。
而T5模型实现了多任务学习。它是同一个模型,既可以处理评论来预测评分,也可以处理问题来预测答案。因此,你不需要两个独立的模型,只需要一个整合的模型。
T5模型与数据规模 📊
关于T5模型,一个主要的结论是:通常,数据越多,性能越好。
例如,英文维基百科数据集大约有13GB,而T5模型训练所使用的C4(Colossal Clean Crawled Corpus)数据集大约有800GB。这个对比直观地展示了C4数据集的规模之大。
迁移学习的优势总结 🎯
以下是迁移学习希望达成的理想目标:
首先,减少训练时间。因为你已经有了一个预训练模型,使用迁移学习通常能实现更快的收敛。
其次,提升预测性能。模型从其他任务中学到的知识,可能对当前训练任务的预测有所帮助。
最后,可能减少数据需求。由于模型已经从其他任务中学到了很多,对于当前任务,可能不需要那么大量的数据。
因此,如果你拥有的数据集较小,迁移学习可能会对你大有裨益。

本节课总结
在本节课中,我们一起学习了自然语言处理第3周的核心内容概述。我们介绍了问答系统的两种形式,深入探讨了迁移学习的原理与优势,了解了BERT模型如何利用双向上下文,并认识了T5模型如何整合多任务学习。我们还讨论了数据规模对模型性能的影响,并总结了迁移学习在减少训练时间、提升性能和数据需求方面的价值。这些概念为后续深入学习具体的模型和算法奠定了坚实的基础。
167:吴恩达《自然语言处理》P167 - 27. NLP中的迁移学习 🚀
在本节课中,我们将要学习自然语言处理中的迁移学习。我们将探讨其基本概念、两种主要形式(基于特征的学习与微调),并了解如何利用有标签和无标签数据来提升模型性能。

什么是迁移学习?🤔
迁移学习是一种机器学习方法,其核心思想是将从一个任务中学到的知识应用到另一个相关任务上。在NLP中,这通常意味着利用在大规模文本上预训练的模型,来帮助我们解决数据量较小的特定任务。
迁移学习的两种形式
上一节我们介绍了迁移学习的概念,本节中我们来看看它的两种基本实现形式。
以下是两种主要的迁移学习方法:
- 基于特征的学习:这种方法利用从其他任务中学到的特征(例如词向量)作为新模型的输入。模型的其他部分需要从头开始训练。
- 公式/概念:
新任务输入 = 预训练的词嵌入(原始文本)
- 公式/概念:
- 微调:这种方法直接使用一个预训练好的模型及其权重,然后针对特定任务,用新数据对这个模型的全部或部分参数进行小幅调整。
- 公式/概念:
微调后模型 = 预训练模型权重 + Δ权重(基于新任务数据)
- 公式/概念:
具体示例:特征学习 vs. 微调
为了更清晰地理解这两种形式,让我们看一个具体的对比。
以下是两种方法的工作流程示意图:
-
基于特征:
- 原始文本通过预训练的词嵌入模型,得到词向量。
- 这些词向量作为输入特征,送入一个全新的、为当前任务设计的模型。
- 新模型输出预测结果。
-
微调:
- 加载一个完整的预训练模型。
- 将当前任务的数据输入该模型。
- 在训练过程中,根据损失函数更新预训练模型本身的权重,使其适应新任务。
- 使用调整后的模型进行预测。
微调实践:添加新网络层
在实际操作中,微调常采用一种策略:冻结大部分预训练层,仅训练新增的层。
假设我们有一个在电影评论上预训练的模型,现在要将其用于课程评论的情感分析(预测1星、2星或3星)。
以下是操作步骤:
- 保持预训练模型的所有原始权重固定(冻结)。
- 在模型的末端添加一个新的前馈神经网络层。
- 只使用课程评论数据来训练这个新添加的网络层。
数据的影响:数量与类型
模型性能很大程度上受数据影响。通常,数据越多,模型越大,性能就越好。
在NLP领域,无标签文本数据(如网页、书籍)的数量远远超过有标签数据(如带有情感标签的评论)。
无标签数据的利用:自监督学习
既然无标签数据如此丰富,我们如何利用它们呢?答案是自监督学习。
在自监督任务中,我们从无标签数据中自动生成“伪标签”来训练模型。例如,在语言建模中,我们遮盖一个词,然后让模型预测它。
以下是其工作流程:
- 输入:从句子“learning from deep learning AI is like watching the sunset with my best friend”中,我们遮盖“friend”,得到“...with my best [MASK]”。
- 目标:模型需要预测被遮盖的词是“friend”。
- 训练:模型输出预测,计算与真实词“friend”的损失,并用此损失更新模型参数。
下游任务中的微调
在预训练完成后(例如通过掩码语言建模或下一句预测),我们可以将模型应用到各种下游任务中。
你可以使用同一个预训练模型,通过微调来适应不同的任务,例如:
- 机器翻译
- 文本摘要
- 问答系统
本章总结 📝

本节课中我们一起学习了NLP中的迁移学习。我们了解到:
- 迁移学习包括基于特征的学习和微调两种形式。
- 可以利用丰富的无标签数据通过自监督任务(如语言建模)进行预训练。
- 预训练模型可以通过微调,高效地适配到多种具体的下游任务(如翻译、摘要等),从而节省数据和计算资源,提升模型性能。
168:自然语言处理模型演进史 📚

概述
在本节课中,我们将按照时间顺序,学习几种重要的自然语言处理模型。我们将了解每个模型的核心思想、优势与不足,以及它们如何逐步解决前代模型面临的问题。课程将涵盖连续词袋模型、ELMo、GPT、BERT和T5。
1. 模型演进时间线 🗺️
以下是课程将要介绍的主要模型及其出现顺序。
- 连续词袋模型
- ELMo
- GPT
- BERT
- T5
这并非一个涵盖所有相关模型和研究成果的完整历史,但有助于我们看清每个模型解决了什么问题,以及带来了哪些新挑战。
2. 上下文的重要性与早期模型
理解一个词的含义,关键在于其上下文。我们可以查看这个词前后的内容,这正是我们训练词嵌入的基础。
上一节我们介绍了模型演进的时间线,本节中我们来看看如何利用上下文。
连续词袋模型
在连续词袋模型中,我们有一个中心词,例如“right”。传统做法是,我们取一个固定大小的窗口(例如前后各两个词),将这些上下文词输入神经网络,来预测中心词“right”。
公式/代码示例:
预测目标:P(中心词 | 上下文词1, 上下文词2, ...)
这种方法的问题是,如果我们想利用的不仅仅是固定窗口内的词,而是句子中所有前后的词,该怎么办?
3. 引入更广泛上下文的模型
为了使用全部的上下文信息,研究人员探索了以下方法。
以下是利用循环神经网络来捕获双向上下文的方法。
- 使用从左到右和从右到左的两个循环神经网络。
- 使用双向LSTM(一种循环神经网络的变体)。
将两个方向的网络输出结合,就能预测中心词“right”,从而得到该词的词嵌入。
4. 基于Transformer架构的模型
我们熟悉的Transformer架构包含编码器和解码器堆栈。随后出现的模型对此进行了不同的利用。
上一节我们看到了如何用RNN捕获双向上下文,本节中我们来看看基于Transformer的模型如何做得更好。
GPT模型
GPT模型仅使用解码器堆栈。在Transformer中,解码器采用因果注意力机制,这意味着每个词只能关注它自身及之前的词,不能“窥视”未来的词。
核心概念: GPT = 仅使用Transformer解码器堆栈 + 单向上下文(仅左)
BERT模型
BERT模型则仅使用编码器堆栈。编码器允许每个词关注句子中的所有其他词,包括它后面的词,从而实现真正的双向上下文理解。
核心概念: BERT = 仅使用Transformer编码器堆栈 + 双向上下文
例如,在句子“立法者相信他们站在历史的___一边,因此他们修改了法律”中,BERT可以利用整个句子的双向上下文来预测空白处的词(如“正确”)。
5. BERT的预训练任务
BERT通过特定的预训练任务来学习语言表示。
以下是BERT使用的两个关键预训练任务。
- 掩码语言建模:随机遮盖句子中的一些词,让模型预测这些被遮盖的词。
- 下一句预测:给定两个句子A和B,让模型判断B是否是A的下一句。
6. 编码器-解码器架构的回归与多任务学习
T5模型测试了使用完整编码器-解码器架构(即原始Transformer模型)的性能。研究人员发现,同时包含编码器和解码器堆栈的模型表现更好。
上一节我们了解了BERT的双向编码器,本节中我们来看看完整的编码器-解码器架构如何用于多任务。
T5的多任务训练策略
T5采用“文本到文本”的框架。为了让同一个模型能区分不同的任务(如分类、摘要、问答),我们在输入前添加一个任务前缀。
代码示例:
输入: “分类:深度学习.ai很棒。” -> 输出: “五星”
输入: “摘要:深度学习.ai很棒。” -> 输出: “它很不错。”
输入: “问题:什么是深度学习?” -> 输出: “一门学科。”
通过这种方式,模型能根据前缀自动识别并执行相应任务。
总结

本节课中我们一起学习了自然语言处理关键模型的演进历程。
- 我们始于连续词袋模型,它使用固定大小的上下文窗口。
- 接着是ELMo,它利用双向LSTM来捕获更长的上下文。
- 然后是GPT,它仅使用Transformer解码器堆栈,专注于单向(从左到右)的上下文。
- BERT则仅使用Transformer编码器堆栈,实现了双向上下文理解,并采用了掩码语言建模和下一句预测任务。
- 最后是T5,它回归完整的编码器-解码器架构,并通过添加任务前缀实现了强大的多任务学习能力。
你现在已经对这些模型有了一个概览,也看到了文本到文本模型如何通过前缀让同一个模型解决多种任务。在下一个视频中,我们将更详细地探讨BERT模型。
169:29_Transformer的双向编码器表示(BERT)📚

在本节课中,我们将要学习BERT模型的核心概念、工作原理及其预训练与微调过程。BERT是一种基于Transformer架构的双向编码器表示模型,它通过同时考虑文本的左右上下文来理解语言,在多项自然语言处理任务中取得了突破性成果。
BERT模型概述与架构 🏗️
BERT的全称是“来自Transformer的双向编码器表示”。它利用了迁移学习和预训练技术。
BERT模型通常从输入嵌入开始,即 E1, E2, ..., EN。然后,这些嵌入会经过多个Transformer块进行处理。如下图所示,每个蓝色圆圈代表一个Transformer块。经过层层处理后,最终得到输出 T1, T2, ..., TN。

BERT框架主要包含两个步骤:预训练和微调。在预训练阶段,模型使用无标签数据在不同的预训练任务上进行训练。在微调阶段,BERT模型首先用预训练的参数进行初始化,然后使用下游任务的有标签数据对所有参数进行微调。例如,在上图中,你得到相应的嵌入,让其通过几个Transformer块,然后进行预测。
以下是BERT的一些关键参数:
- BERT是一个多层的双向Transformer模型。
- 它使用了位置嵌入。
- 著名的BERT-base模型有12层(即12个Transformer块)、12个注意力头和1.1亿个参数。如今的新模型(如GPT-3)拥有远多于BERT的参数和层数。
BERT的预训练过程 🔍
上一节我们介绍了BERT的整体架构,本节中我们来看看其核心的预训练过程是如何工作的。
在将词序列输入BERT模型之前,我们会遮盖(Mask)其中15%的单词。训练数据生成器会随机选择15%的位置用于预测。
如果第 i 个词被选中,我们按以下规则处理:
- 80%的概率,用
[MASK]标记替换该词。 - 10%的概率,用一个随机词替换该词。
- 10%的概率,保持该词不变。
在这种情况下,上一张幻灯片中提到的输出 Ti 将被用来预测原始的词,损失函数为交叉熵。这被称为掩码语言模型。
例如,在句子“After school Lucas does his blank in the library”中,BERT模型需要预测被遮盖的词(可能是“work”或“homework”)。为了实现预测,通常在 Ti 输出后添加一个全连接层,用于分类。具体做法是将编码器的输出向量乘以嵌入矩阵,将其转换为词汇表维度,最后加上一个Softmax层。
另一个例子是:“After school blank his homework in the blank.” 模型需要预测“Lucas does”和“library”。
以下是预训练步骤的总结:
- 随机选择15%的词元。
- 其中80%被遮盖,10%被替换为随机词,10%保持不变。
- 注意,一个句子中可能存在多个被遮盖的片段。
- BERT在预训练中还使用了下一句预测任务。给定两个句子,模型需要判断它们是否在原文中前后相连。


总结 📝
本节课中我们一起学习了BERT模型。你现在已经对这个模型有了直观的理解。你看到BERT利用了下一句预测和掩码语言建模两种预训练任务,这使得模型能够获得对语言的一般性感知。在下一个视频中,我们将形式化这些概念,并向你展示BERT的损失函数。请继续观看下一讲。
170:BERT 目标 🎯
在本节课中,我们将学习 BERT 模型的目标函数。你将了解模型试图最小化的具体目标。特别是,我们将展示如何将词嵌入、句子嵌入和位置嵌入组合作为模型的输入。让我们来看看具体如何实现。


你将学习 BERT 模型的输入是如何馈入的,以及不同类型的输入及其结构。接着,我们将可视化模型的输出。最后,你将学习 BERT 的目标函数。
输入的形式化表示
这是 BERT 的输入表示形式。
首先,你需要位置嵌入。它用于指示句子中每个词的位置,即每个词在对应句子中的具体位置。
其次,是片段嵌入。它用于指示当前片段是属于句子 A 还是句子 B,因为在 BERT 中我们还使用了下一句预测任务。
然后,是词元嵌入,也称为输入嵌入。
此外,还有一个特殊的 [CLS] 词元,用于表示句子的开始;以及一个 [SEP] 词元,用于表示句子的结束。
最后,你将词元嵌入、片段嵌入和位置嵌入三者相加,就得到了模型的新输入。
在上图中,你可以看到被掩码的句子 A 和句子 B。它们被转换为词元,并在最前面添加了特殊的分类符号 [CLS],句子之间用特殊的分隔符 [SEP] 隔开。这些词元被转换为嵌入向量,然后送入 Transformer 编码块。
在输出端,你会得到 T1 到 Tn 以及 T1‘ 到 Tn’。每个 T 嵌入向量将通过一个简单的 softmax 层,用于预测被掩码的词。同时,你还会得到一个 C 嵌入向量,它可以用于下一句预测任务。
BERT 的目标函数
现在,让我们来看 BERT 的目标函数。
对于掩码语言模型任务,模型使用交叉熵损失来预测被掩码的词。
对于下一句预测任务,模型使用二元损失来判断两个句子是否连续。
总的目标函数是这两个损失的和。
用公式可以表示为:
总损失 = 掩码语言模型损失 + 下一句预测损失


总结
本节课中,我们一起学习了 BERT 模型的目标函数,并了解了模型的输入和输出表示。
在下一个视频中,我将展示如何对这个预训练模型进行微调。具体来说,我会演示如何将其用于你自己的项目任务中。请继续观看下一个视频。
171:31_微调BERT 🎯

在本节课中,我们将学习如何对预训练的BERT模型进行微调,使其能够适应并解决我们自己的特定任务和数据集。微调是迁移学习的关键步骤,能让BERT在多种下游任务上取得优异表现。
概述 📋
BERT模型通过在大规模语料上进行预训练,学习了丰富的语言表示。然而,要让它解决具体的实际问题,如情感分析、命名实体识别或问答,我们需要在特定任务的数据集上对其进行额外的训练,这个过程就称为“微调”。
上一节我们介绍了BERT的预训练过程,本节中我们来看看如何将预训练好的BERT模型应用到不同的具体任务上。
微调BERT的输入格式 🔧
微调的核心在于根据不同的任务,为BERT模型准备合适的输入。BERT预训练时使用了“句子A”和“句子B”的格式,并配合了下一句预测和掩码语言模型任务。
在微调阶段,我们需要将特定任务的数据转换成这种格式。
以下是几种常见任务对应的输入构建方法:
- 文本分类(如情感分析):将待分类的文本放入“句子A”的位置,“句子B”的位置可以留空或使用特殊符号(如
[CLS])。模型最终基于[CLS]标记的输出来判断类别(如“积极”或“消极”)。 - 自然语言推理(MNLI):将“假设”文本作为句子A,将“前提”文本作为句子B输入模型。
- 命名实体识别(NER):将待标注的完整句子作为句子A输入,模型需要为句子中的每个词元预测其对应的实体标签。
- 问答任务(如SQuAD):将问题文本作为句子A,将包含答案的段落文本作为句子B输入。模型需要预测答案在段落中的开始和结束位置。
- 文本摘要:可以将原文作为句子A,将摘要作为句子B,用于训练生成式或抽取式摘要模型。

如图所示,对于问答任务,输入是问题和段落,输出是答案的起始和结束位置。对于NER任务,输入是句子,输出是每个单词的命名实体标签。对于MNLI任务,输入则是假设和前提。
总而言之,通过灵活地填充“句子A”和“句子B”的位置,我们可以让BERT模型适配分类、问答、推理、序列标注等多种任务。这只是微调过程中对模型输入的处理。

总结与展望 🚀
本节课中我们一起学习了微调BERT模型的核心思想与操作方法。我们了解到,微调的本质是将预训练模型的知识迁移到新任务上,关键在于根据任务类型构建正确的输入格式(句子A和句子B)。
现在你已经知道如何在分类、问答、摘要等任务上微调你的模型了。接下来,我们将把学习提升到一个新的水平,向你介绍一个名为T5的新模型。请继续观看下一个视频来了解T5模型。
172:T5 Transformer模型 🧠
在本节课中,我们将学习T5模型。T5是一个强大的文本到文本转换模型,可以应用于多种自然语言处理任务。我们将了解它的工作原理、使用的注意力机制类型以及其模型架构的概览。

T5模型简介与应用
T5模型可用于多种自然语言处理任务。它采用了与BERT类似的训练策略,利用了迁移学习和掩码语言建模技术。T5模型在训练时同样使用了Transformer架构。接下来,我们看看如何在自己的应用中使用这个模型。
T5被称为“文本到文本转换器”。它可以用于以下任务:
- 文本分类
- 问答系统:用于回答问题
- 机器翻译
- 文本摘要
- 情感分析
T5还可用于其他应用,但本节课我们将重点讨论以上这些。
预训练方法与模型架构
首先,我们来看模型的预训练方式。假设有一段原始文本,例如:“Thank you for inviting me to your party last week.”
在预训练时,我们会掩码某些词,比如“for inviting”和“last”。然后,我们用特殊的标记(如 [X]、[Y])来替换这些被掩码的部分。因此,[X] 对应“for inviting”,[Y] 对应“last”。
模型的目标输出就是这些标记及其对应的原词,即“[X] for inviting [Y] last”。这些括号标记会按顺序递增,例如接下来可能是 [Z]、[A]、[B] 等,每个标记都对应一个特定的目标词。
注意力机制变体
在注意力机制部分,我们将考虑几种不同的Transformer架构变体。
我们从基本的编码器-解码器表示开始。在编码器中,使用的是完全可见注意力;在解码器中,使用的是因果注意力。在通用表示中,浅灰色线代表因果掩码,深灰色线代表完全可见掩码。
左侧是标准的编码器-解码器架构。
中间是语言模型架构。它由单个Transformer层堆栈组成,接收输入和目标文本的拼接。如图所示,它全程使用因果掩码(所有线条均为灰色)。x1 输入模型,然后是 x2、x3,依此类推。
右侧是前缀语言模型架构。它在输入部分允许完全可见掩码(如深色箭头所示),而在其余部分使用因果掩码。
T5架构细节
T5模型架构使用了编码器-解码器堆栈。它包含12个Transformer块(可以想象成“一打鸡蛋”),共计约2.2亿个参数。
总结与预告

总结一下,本节课我们介绍了前缀语言模型注意力机制,了解了T5的模型架构,并看到了其预训练方式(与BERT类似,但这里使用的是掩码语言建模)。
现在,你对Transformer模型有了一个概览,知道了如何训练它,也看到了它可以应用于多种任务。
在下一个视频中,我将讨论针对此模型的几种训练策略。我们下节课见。
173:多任务训练策略 🧠

在本节课中,我们将要学习如何训练一个单一的模型,使其能够在多个自然语言处理任务上同时取得优异的表现。我们将探讨多任务训练的基本策略、输入输出格式、数据混合方法以及两种关键的微调技术。
概述
多任务训练策略的核心目标是训练一个模型,使其能够处理多种不同的NLP任务,例如机器翻译、问答、摘要和情感分析。为实现这一目标,我们通常会在输入前添加一个特定的任务前缀标签,以告知模型当前正在执行何种任务。
多任务训练的工作原理
多任务训练策略的工作流程如下:对于每一个不同的任务,我们在输入文本前添加一个特定的指令前缀,模型则根据这个前缀来理解并执行相应的任务。
以下是不同任务的具体输入输出格式示例:
- 机器翻译:要完成从英语到德语的翻译,你需要在句子前添加前缀
translate English to German:。- 输入:
translate English to German: The course is jumping well. - 输出:模型会生成对应的德语翻译。
- 输入:
- 文本相似度:若要判断两个句子的相似性,可以使用
STSB sentence1:和sentence2:的格式。- 输入:
STSB sentence1: [句子1] sentence2: [句子2] - 输出:模型会给出一个表示相似度的分数。
- 输入:
- 文本摘要:要进行文本摘要,则添加
summarize:前缀。- 输入:
summarize: [需要摘要的长文本] - 输出:模型会生成该文本的摘要。
- 输入:
通过这种方式,模型能够学习到不同任务的整体结构。例如,在自然语言推理任务中:
- 输入:
MNLI premise: I hate pigeons. hypothesis: My feelings towards pigeons are filled with animosity. - 输出:
entailment
模型在看到了任务前缀 MNLI 和完整的输入后,其任务就是输出正确的类别标签(如 entailment)。这种格式使得模型更容易根据前缀学习预测对应的类别。
数据混合策略
在同时使用多个数据集进行训练时,如何混合这些数据是一个关键问题。主要有以下三种策略:
- 比例混合:从每个数据集中抽取相同比例(例如10%)的样本进行混合。这样,大型数据集贡献的绝对样本数会更多。
- 等量混合:不考虑每个数据集的大小,从每个数据集中抽取绝对数量相等的样本进行混合。
- 温度缩放混合:这是一种折中的方法,通过调整参数来控制混合比例,使其介于“比例混合”和“等量混合”之间。
模型微调技术
上一节我们介绍了如何混合数据,本节中我们来看看两种在微调预训练模型时常用的技术,它们能有效防止在特定任务上“遗忘”预训练时学到的通用知识。
以下是两种核心的微调方法:
- 渐进解冻:这种方法不是一次性微调所有网络层。首先,你只解冻并微调模型的最后一层,同时冻结其他所有层。然后,逐步解冻并微调更前面的层,一次解冻一层。这有助于稳定训练过程。
- 适配器层:这种方法不直接修改原始Transformer模型的主要参数。相反,它在Transformer的每个前馈网络块中插入一个小的、新的神经网络模块(适配器层)。这个适配器层被设计成输入和输出维度相同,因此可以无缝插入。在微调时,只更新这些新添加的适配器层和层归一化参数,而保持原始庞大的预训练模型参数基本不变。
总结

本节课中我们一起学习了多任务训练的核心思想。我们了解到,通过为不同任务设计统一的前缀格式,可以训练一个共享大部分参数的单一模型来处理多种NLP任务。我们还探讨了混合不同任务数据的策略,并介绍了渐进解冻和适配器层这两种重要的微调技术,它们有助于在适应新任务的同时保留模型的通用能力。
现在你已经了解了如何训练这样的多任务模型,接下来需要一个标准的方法来评估它的性能。紧接着,我们将使用 GLUE(通用语言理解评估)基准 来进行评估。
174:吴恩达《自然语言处理》第34讲 - GLUE基准 🎯

在本节课中,我们将要学习自然语言处理领域最常用的基准测试之一——GLUE基准。我们将了解它的构成、用途以及它在推动NLP研究发展中的重要作用。
什么是GLUE基准? 📊
上一节我们介绍了自然语言处理的基本概念,本节中我们来看看一个关键的评估工具。
GLUE基准的全称是通用语言理解评估。它本质上是一个用于训练、评估和分析自然语言理解系统的数据集集合。
该基准包含大量数据集,每个数据集都涵盖多种文体,并具有不同的规模和难度等级。
以下是GLUE基准涵盖的一些主要任务类型:
- 指代消解:确定代词所指代的具体名词。
- 情感分析:判断文本所表达的情感倾向。
- 问答系统:根据给定文本回答问题。
- 文本相似度:判断两个句子在语义上是否相似。
- 自然语言推理:判断一个句子是否蕴含、矛盾或中立于另一个句子。
GLUE基准的用途与特点 🚀
了解了GLUE基准的构成后,本节我们来看看它的具体用途和重要特性。
GLUE基准通常与排行榜结合使用。研究人员可以使用这些数据集测试他们的模型,并在排行榜上比较与其他模型的性能优劣。
它所评估的任务非常多样,例如:
- 判断句子是否合乎语法。
- 分析文本的情感。
- 对文本进行复述。
- 判断两个问题是否重复。
- 判断问题是否可回答。
- 判断文本间是蕴含、矛盾还是中立关系。
- 解决Winograd模式挑战,即准确判断代词所指代的名词。
GLUE基准的核心价值在于推动研究发展。研究人员通常将其作为衡量模型性能的标准。
它是一个模型无关的基准,这意味着无论你使用何种模型架构,都可以在GLUE上评估其表现。
最后,它促进了迁移学习的应用。因为你可以访问多个数据集,并从中学习不同方面的知识,这些知识将帮助你在GLUE内的全新数据集上取得更好的评估效果。
现在,你不仅知道如何实现最先进的模型,还知道了如何系统地评估它们。

总结与展望 📝
本节课中,我们一起学习了GLUE基准。我们了解到它是一个综合性的自然语言理解评估集合,包含多种任务和数据集,用于训练、评估和比较不同模型的性能。它的模型无关性和对迁移学习的支持,使其成为推动NLP领域进步的重要工具。
在下一讲中,我将探讨问答系统,并向你展示如何运用这些模型构建一个复杂的问答系统。我们下一讲见。😊
175:🤗 Hugging Face 介绍
在本节课中,我们将学习Hugging Face平台及其生态系统。Hugging Face致力于通过开源和开放科学,让机器学习技术变得尽可能易于获取和使用。我们将了解其核心工具、模型与数据集中心,以及它们如何共同构成一个强大的机器学习协作平台。
我是Hugging Face开源团队的Lyander。在Hugging Face,我们专注于实现机器学习的民主化。我们坚信,实现这一目标的唯一途径就是通过开源和开放科学。
我们为机器学习社区开发和创建工具与中心。这些工具构成了Hugging Face生态系统,并通过Hugging Face Hub平台连接起来。Hub是一个用于在模型和数据集上进行协作的平台。
在开始本实验部署之前,让我先向您介绍Hub,您将在后续视频中利用它。
模型中心 (Model Hub)
模型中心托管了超过15000个由社区贡献的模型。它提供了一种根据任务、库、训练数据集、特定语言或给定许可证来筛选模型的方式。
以下是模型中心的主要特点:
- 模型可以来自Hugging Face Transformers库,但不限于此。任何库都可以轻松添加Hub支持,例如Spacey或AllenNLP等库已经这样做了。
- 我们提供工具来推送您自己的模型和权重,对所使用的库没有要求。
- 模型可以通过其模型卡片进一步描述。
- 社区的其他成员可以使用托管的推理API来试用模型。

数据集中心 (Dataset Hub)
数据集中心与模型中心类似,同样托管了数千个主要由社区贡献的数据集。

以下是数据集中心的主要特点:
- 这些数据集可以根据任务、语言、示例数量和许可证进行筛选。
- 这些数据集提供全面的数据集卡片,解释数据是如何设计的、数据来源,以及使用数据时需要考虑的社会影响和偏见等问题。
- 此外,它还提供了快速链接,指向在这些数据集上微调或训练的模型。
我们非常高兴能与DeepLearning.AI在本专注于Transformer的课程中合作。在接下来的实验中,您将有机会练习使用一些Hugging Face工具,例如Transformers库、Datasets库以及Hugging Face Hub来寻找预训练模型。
祝您学习愉快,并期待看到您微调后的模型。


本节课中,我们一起学习了Hugging Face的使命及其核心生态系统。我们介绍了旨在简化机器学习流程的Transformers、Datasets等工具库,并重点了解了作为协作中心的Model Hub和Dataset Hub。这些工具和平台共同降低了机器学习的门槛,为开发者提供了强大的支持。
176:Hugging Face 入门教程 🚀

在本节课中,我们将要学习 Hugging Face 平台及其核心的 Transformers 库。我们将了解它能提供什么,以及如何在你的项目中利用它。
概述
Hugging Face 平台提供了丰富的解决方案,构建了社区,并拥有协作研究工具。本教程将重点介绍其 Transformers Python 库,这是一个在你的项目中使用 Transformer 模型的绝佳工具。
什么是 Hugging Face Transformers 库?🤖
Hugging Face 的 Transformers 库可以与 PyTorch、TensorFlow 或 Flax 一起使用。这意味着你可以利用 Hugging Face 提供的一切功能,而无需更换你喜欢的深度学习框架。该库对基于 JAX 的神经网络库 Flax 的支持也在日益增长。
你可以使用 Hugging Face Transformers 库主要做两件事:
- 直接应用最先进的 Transformer 模型处理各种 NLP 任务。
- 使用你自己的数据集或 Hugging Face 提供的数据集,对预训练模型进行微调。
使用 Pipeline 快速上手 🛠️
上一节我们介绍了 Transformers 库的基本用途,本节中我们来看看如何通过 pipeline 对象轻松使用模型。
Transformers 库中的 pipeline 对象封装了在示例上运行模型所需的一切。通过 pipelines,你可以轻松地“开箱即用”,为不同的 NLP 任务直接应用最先进的 Transformer 模型。
以下是 pipeline 的主要功能:
- 预处理:自动处理你的输入数据。
- 运行模型:调用模型进行计算。
- 后处理:将模型的输出处理成人类可读的结果。
此外,如果你不确定为你的任务使用哪个模型,也无需担心,因为每个 pipeline 都有一个默认模型。例如,对于问答任务,你只需要指定 pipeline 的任务类型,并提供上下文和问题。pipeline 会处理其余所有事情,并给出人类可读的答案。
因此,借助 Hugging Face 的 pipelines,你几乎可以将 Transformer 模型用于任何 NLP 任务。
微调预训练模型 🔧
如果你希望对预训练模型进行微调,Transformers 库也提供了不同的工具,且过程并不复杂。
Hugging Face 拥有一个不断增长的模型库,包含超过 15,000 个预训练模型检查点,你可以使用它们来微调最流行的 Transformer 架构。
模型检查点 是指针对特定任务(如问答),使用特定数据集训练某个模型后,所学习到的那一组参数。
你可以从某个检查点开始,使用 Hugging Face 上可用的数千个数据集之一,或使用你自己的数据,来微调一个问答模型。
以下是微调过程涉及的关键组件:
- 分词器:Transformers 库为每个检查点提供了关联的分词器,用于预处理数据。分词器通过将你的文本翻译成模型可读的输入,为你完成繁重的工作。
- 训练工具:如果使用 PyTorch,你可以使用 Transformers 库内置的
Trainer或创建自定义训练器。如果使用 TensorFlow,则推荐使用传统的 Keras 方法,如fit。 - 评估指标:Hugging Face 还提供了像
evaluate库这样的指标,可以无缝集成到训练过程中,用于评估你的模型。
当你拥有一个微调好的模型后,可以运行它来获得输出,分词器可以帮助你将输出转换回人类可读的文本。
总结

本节课中,我们一起学习了 Hugging Face 平台及其 Transformers 库的核心功能。我们了解到,该库通过 pipeline 让应用最先进的模型变得非常简单,同时也为模型微调提供了完整的工具链,包括分词器、训练接口和评估指标。Hugging Face 和 Transformers 库为你微调一个 Transformer 模型提供了所需的一切,当然,你也可以自定义其中的任何步骤。
177:P177)

📖 概述
在本节课中,我们将学习如何使用 Hugging Face 的 Transformers 库中的 pipeline 对象。我们将了解 pipeline 如何简化自然语言处理任务,以及如何为不同任务选择和初始化合适的模型。
🔄 快速回顾
上一节我们介绍了 Transformers 库的基本概念。本节中我们来看看其核心工具 pipeline。
Transformers 库提供了一个 pipeline 对象,你可以用它来应用最先进的 Transformer 模型处理不同的 NLP 任务。
pipeline 会为你处理输入的预处理、模型的运行以及输出的后处理,使其易于人类阅读。
🛠️ 理解 Pipeline 的工作原理
pipeline 是为特定 NLP 任务构建的对象。例如,对于问答任务,你可以使用一个 Transformer 模型来回答给定上下文中的不同问题。
要使用 Transformer 模型,你只需要指明你的 pipeline 需要执行的任务。
初始化一个 pipeline 时,你只需要传入任务名称。但如果你需要,也可以指定你想要使用的模型检查点。
使用一个已初始化的 pipeline 时,你只需要提供该任务所需的适当输入。
🌐 Hugging Face 支持的任务
Hugging Face 支持最流行的 NLP 任务,但其应用不仅限于 NLP。它还支持其他领域的任务,例如计算机视觉中的图像分类和目标检测,以及自动语音识别等。
以下是几个常见的 NLP 任务示例:
- 情感分析:如果你的目标是将一个句子分类为正面或负面,你可以使用这个任务。该
pipeline的输入是一个句子,输出是该句子所属的类别。 - 问答:如果你需要一个能从你提供的上下文中提取信息并回答问题的模型,可以创建此任务的
pipeline。这里的输入是上下文和一系列问题,模型会输出相应的答案(如果找到的话)。 - 填充掩码:当你需要模型填充文本中的空白处时,可以使用此任务。该
pipeline需要你提供想要填充的句子以及需要填充的确切位置。它会输出一系列可能的句子补全方式。
你可以在 Hugging Face 网站上找到更多可直接使用的任务。
🧠 选择模型检查点
正如之前提到的,你可以为你的 pipeline 选择特定的模型检查点。Hugging Face 提供了大量的检查点,其中一些使用海量数据集进行了微调,能在特定任务上提供出色的结果。
然而,最好记住并非每个检查点都适合每项任务。因此,你应该查看预训练模型的描述,以选择合适的检查点,或者直接使用 pipeline 默认的模型。
🔍 在模型中心寻找模型
你可能会问,我在哪里可以找到适合我任务的模型并使用这些检查点?
为此,Hugging Face 在 huggingface.co 上提供了一个模型中心。在那里,你会找到许多预训练模型。你可以按所需的任务、甚至需要使用的数据集或框架进行筛选。
一旦选择了更符合你需求的模型,你将看到模型卡片界面。模型卡片会显示所选模型的信息,例如模型描述和一些代码片段,这些代码片段提供了如何使用所选模型的示例。
✅ 总结

本节课中我们一起学习了 Hugging Face Transformers 库中 pipeline 的核心用法。我们了解到 pipeline 如何封装预处理、模型推理和后处理流程,从而让开发者能够轻松地将最先进的 Transformer 模型应用于各种 NLP 乃至跨模态的任务。我们还探讨了如何根据任务需求选择和初始化模型,并介绍了在 Hugging Face 模型中心寻找合适资源的方法。
178:Hugging Face 入门 III 🚀

在本节课中,我们将要学习如何利用 Hugging Face 生态系统来微调一个 Transformer 模型。Hugging Face 提供了一套完整的工具,从数据集、模型到训练和评估,极大地简化了自然语言处理任务的流程。
概述:Hugging Face 的核心组件 🧩
Hugging Face 提供了微调 Transformer 模型所需的一切工具。
他们提供了超过 1000 个针对特定任务的数据集,你可以使用 datasets 库快速加载。
他们还提供了分词器,帮助你在训练前预处理数据,并在模型运行后处理输出。
此外,他们拥有超过 15,000 个模型检查点,你可以从 transformers 库中加载。
Trainer 对象让你无需担心编写训练过程代码即可训练模型。
不仅如此,它还预定义了可用于评估模型性能的指标,这些指标可以集成到 Trainer 对象中。
模型检查点:微调的起点 🏁
上一节我们介绍了 Hugging Face 的丰富资源,本节中我们来看看如何选择和使用模型检查点。
Hugging Face 拥有超过 15,000 个模型检查点,你可以将它们作为微调过程的起点。
模型检查点是在特定数据集上训练模型过程中学习到的权重集合。
例如,你可以使用一个在问答数据集上训练得到的 DistilBERT Transformer 蒸馏版本的模型检查点。
你可以在 Hugging Face 上找到名为 distilbert-base-cased-distilled-squad 的检查点。
或者,你也可以使用在区分大小写的英语语料库上训练的 BERT 基础版本模型。
你可以在 Hugging Face 上找到名为 bert-base-cased 的检查点。
因此,你可以探索 Hugging Face 模型库,为你的特定用例寻找合适的检查点。
一旦你选择了想要使用的检查点,你可以使用 transformers 库中的函数,通过一行代码加载其架构和权重。
你将在下一个未评分的实验课中看到具体做法,所以现在不必担心代码细节。
数据集:获取与加载 📊
为了针对特定任务微调 Transformer 模型或在特定领域表现出色,你需要大量数据。
你可以通过爬取网页甚至从自己开发的应用程序中收集数据来获取和处理数据集。
但在你大费周章之前,Hugging Face 拥有一个包含特定任务数据集的库,值得探索。
你可以在他们的网站上按任务类别、语言和大小筛选来搜索这个数据集库。
要加载你选择的任何数据集,你只需要 datasets 库中的一个函数。
因此,如果你决定使用 Hugging Face 的数据集,只需一行代码即可加载数据。
如果你已经拥有自己的数据集,加载过程也同样简单。
使用 Hugging Face 的 datasets 库是一个明智的选择,因为它针对处理海量数据集进行了优化,并且拥有可以帮助你预处理数据的方法,即使你决定使用自己的数据。
数据预处理:使用分词器 🔧
一旦确定了用于微调模型的数据集,你需要预处理数据以便将其输入模型进行训练。
预处理数据的方式因任务和你要微调的模型而异。
但 Hugging Face 提供了分词器对象。在这个过程中,你只需要指定你正在使用的模型检查点,分词器几乎会完成所有繁重的工作。
分词器接收你的文本数据,并返回你的模型可以读取的标记。
例如,你可以向 DistilBERT 分词器传递文本:“What well-known superheroes were introduced between 1939 and 1941 by Detective Comics?”
它会返回 DistilBERT 模型可用于训练和推理的数值标记。
在获得标记后,你可能需要根据要微调的模型、使用的数据集以及训练模型的任务,采取一些额外的预处理步骤。
为了确保在预处理步骤中完成了所有必要的工作,你需要阅读模型检查点和所用数据集的描述,这些描述也由 Hugging Face 提供。
模型训练:使用 Trainer 对象 🏋️
现在,为了执行模型的微调,你需要运行一个训练过程。
正如你可能已经猜到的,Hugging Face 也会让这一步变得更容易。
Hugging Face 的 transformers 库有一个 Trainer 对象,它将你想要微调的模型作为输入,并附带一些你可以根据需要调整的其他参数,例如预热步数、训练轮数或权重衰减的强度。
要执行训练过程,你需要调用 Trainer 对象的 train 方法,这只需一行代码。
此外,你可以定义一个函数来使用 Hugging Face 提供的一些预定义指标,例如 BLEU 分数,或者如果你愿意,甚至可以定义自己的指标。
在 Hugging Face 中,使用 Trainer 对象计算你想要的指标,只需在训练、验证或测试数据上调用一个 evaluate 方法即可。
总结 📝

本节课中我们一起学习了 Hugging Face 平台如何为微调 Transformer 模型提供一站式解决方案。我们了解了如何利用其庞大的模型检查点库作为起点,如何使用 datasets 库高效地加载和处理数据,以及如何借助分词器和 Trainer 对象来简化数据预处理和模型训练流程。这套工具极大地降低了自然语言处理任务的门槛,让开发者能够更专注于模型的应用和创新。
180:吴恩达《自然语言处理》P180 - 与Quoc Le的访谈 🎙️

概述
在本节课中,我们将学习Quoc Le的AI研究历程。Quoc Le是Google Brain的研究员,也是深度学习和自然语言处理领域的先驱之一。我们将了解他从学生时代到成为顶尖研究员的旅程,以及他在序列到序列模型、聊天机器人等关键项目上的工作与思考。
早期经历与AI启蒙 🤖
Quoc Le的AI之旅始于高中时期。他对人工智能产生了浓厚兴趣,阅读了大量书籍,并编写了一些简单的AI程序。他构建了一个类似聊天机器人的系统,试图与程序对话,并探索图灵测试的概念。尽管这个程序很简单,但这段经历为他未来的研究奠定了基础。
随后,他获得了澳大利亚国立大学的本科奖学金。在大学期间,他联系了Alex Moeller教授,并在其指导下进行关于核方法的机器学习研究。这是他第一次正式接触机器学习,并认识到机器学习在实现AI方面的巨大潜力。
学术深造与研究转向 🧠
在德国进行研究期间,Quoc Le接触到了神经科学,并聆听了吴恩达关于使用机器学习实现AI的演讲。这与他将机器学习视为实现AI工具的理念产生了共鸣。因此,他申请并进入了斯坦福大学的博士项目,师从吴恩达。
大约在2010年至2011年间,吴恩达告知他正在Google启动一个旨在将深度学习研究规模扩大数百甚至数千倍的项目。Quoc Le认为这代表了未来方向,因为在他看来,神经网络的显著进步往往源于更多的数据和计算资源。他因此加入了Google Brain项目,并成为该项目的首位实习生。
Google Brain与“谷歌猫”项目 🐱
在Google Brain的早期,Quoc Le参与了一个具有里程碑意义的项目——谷歌猫。这个项目的灵感来源于他们在斯坦福大学研究的自编码器技术。自编码器是一种尝试重建输入图像的无监督学习方法,当应用稀疏性约束时,网络底层会学习到类似边缘检测器的特征。
他们的核心想法是:如果将这个网络的规模扩大100或1000倍,是否能够学习到更复杂、更高级的特征? 他们决定进行尝试。
以下是项目的关键步骤:
- 他们将训练从单机扩展到16000台机器。
- 选择YouTube的海量图像作为训练数据集。
- 训练了一个稀疏自编码器网络约一周时间。
经过大量调试,他们在网络中发现了一些对“人脸”和“猫”特别敏感的神经元。这个通过网络无监督学习发现的“猫”特征,成为了早期深度学习无监督学习的一个标志性成果。
序列到序列模型的突破 🔄
在“谷歌猫”项目之后,Quoc Le的另一个重大贡献是序列到序列模型,这项研究改变了自然语言处理的轨迹。
最初的灵感来源于词向量对齐工作。当时,Word2Vec等方法展示了词向量的强大能力,例如通过向量运算解决类比问题(如“国王-男人+女人≈女王”)。他们思考:不同语言(如英语和西班牙语)的词向量空间是否存在相似结构?能否通过少量对齐的词汇学习一个旋转矩阵,实现跨语言的词映射?
这项工作取得了成功,并促使他们思考:既然词到词的翻译可行,那么能否实现句子到句子的翻译?他们意识到,生成句子时,逐个单词地预测并建立依赖关系(即基于已生成的词预测下一个词)比同时独立预测所有单词更有意义。
然而,从构思到实现花费了近一年时间,过程非常困难。他们最初使用传统的循环神经网络,效果不佳。后来,在与Ilya Sutskever和Oriol Vinyals的合作中,他们尝试使用长短期记忆网络,获得了显著改进。但最初的翻译结果仍然很差。
关键的突破来自于一个看似简单但至关重要的决定:投入更多资源,训练更大的LSTM模型,并优化训练过程。 通过持续降低复杂度、扩大模型规模并延长训练时间,翻译质量在几个月内逐步提升,最终证明了该方向的有效性。
Meena聊天机器人项目 💬
受到高中时期构建简单聊天机器人经历的驱动,以及序列到序列模型成功的鼓舞,Quoc Le开始探索构建更先进的对话AI。
在Google内部,他们首先尝试使用技术支持对话数据训练了一个模型。虽然回答仍有些生硬,但已显示出潜力。随后,在Transformer架构出现后,他们意识到其处理长距离依赖的能力优于LSTM,更适合多轮对话。
他们与工程师Daniel合作,开始使用Transformer架构,并投入更多计算资源训练更大的模型。随着模型规模的扩大和训练的深入,效果逐步改善。
一个令人惊喜的时刻是,他们在检查聊天日志时,发现机器人讲了一个关于“牛去哈佛,所以马也应该去哈佛”的双关语笑话。经过仔细检查训练数据,他们确认这个特定的笑话组合并未在语料库中出现过。这表明模型可能在一定程度上理解了“笑话”和“双关语”的概念,并能进行一定程度的创造性组合。
对未来的展望与生成式模型 🚀
展望自然语言处理的未来,Quoc Le最感兴趣的是生成式模型。他认为,当前很多NLP任务仍局限于传统范畴,如情感分类或命名实体识别。而生成式模型的潜力在于创造新内容。
例如,生成式模型能否创作出被人类消费、传授新概念的新书?或者帮助编剧构思更好的电影情节? 他认为这是技术应用的巨大潜力所在。
除了扩大模型规模和数据量,他认为生成式模型进步的一个重要方向是提升“事实正确性”和“常识理解”。当前模型生成的内容有时会包含虚构的事实。如何让模型生成更符合事实、更具常识的输出,将是一个重要的研究向量。他认为,可以通过构建专注于事实正确性的评测数据集来衡量这一方面的进展。
给AI从业者的建议 📚
Quoc Le分享了他对希望进入AI领域或寻求职业发展的人的一些建议。
首先,保持耐心。有影响力的工作往往需要时间的积累。他的职业生涯从起步到做出显著贡献,也经历了漫长的过程。
其次,保持一定的“天真”或好奇心有时是好事。不必在开始时就想读遍所有文献、理解一切。有时正是因为对现有复杂方法(如基于短语的机器翻译)了解不深或实现困难,才促使他去探索更简洁的端到端方法(如序列到序列),从而取得了突破。
对于学习者,他建议采用“课程学习”的策略:
- 从简单的任务开始:例如,选择一篇感兴趣的AI论文,尝试复现其结果。先确保自己能快速实现并复现基线效果。
- 建立信心:在游泳池学会游泳,而不是直接跳进大海。先通过复现等相对容易的任务获得成就感。
- 逐步挑战:在掌握了复现和实现的能力后,再逐步尝试提出自己的新想法、开展独立研究。
总结

本节课中,我们一起学习了Quoc Le的研究历程与深刻见解。我们回顾了他从高中编程聊天机器人起步,到在Google Brain参与“谷歌猫”、开创序列到序列模型、以及开发Meena聊天机器人的关键项目。我们还探讨了他对生成式模型未来的展望,以及他关于耐心、保持好奇心、采用渐进式学习策略的宝贵建议。他的故事展示了AI研究是一个需要长期投入、勇于探索的精彩旅程。

浙公网安备 33010602011771号