自然语言处理实战第二版(MEAP)(四)(5)

本文涉及的产品
NLP自然语言处理_基础版,每接口每天50万次
NLP 自学习平台,3个模型定制额度 1个月
NLP自然语言处理_高级版,每接口累计50万次
简介: 自然语言处理实战第二版(MEAP)(四)

自然语言处理实战第二版(MEAP)(四)(4)https://developer.aliyun.com/article/1517996

8.2.3 理解结果

要在现实世界中使用这样的模型,你需要能够向老板解释它是如何工作的。德国、芬兰和荷兰(以及很快在整个欧盟)正在规范 AI 的使用,迫使企业解释他们的 AI 算法,以便用户能够保护自己。企业将无法长时间隐藏他们在算法中的剥削性商业行为。你可以想象政府和企业可能如何利用国籍预测算法进行邪恶用途。一旦你了解了这个 RNN 的工作原理,你就能利用这些知识来欺骗算法做正确的事情,提升而不是歧视历史上处于劣势的群体和文化。

也许 AI 算法中最重要的部分是你用来训练它的指标。你在 PyTorch 优化训练循环中使用了NLLLoss来训练,这在列表 8.8 中已经提到。NLL部分代表“负对数似然”。你应该已经知道如何求反log()这个表达式的部分了。在查看下面的代码片段之前,试着猜测如何求反log()函数的数学函数和 Python 代码是什么。像大多数 ML 算法一样,log表示自然对数,有时写作ln以 e 为底的对数

>>> predictions = topk_predictions(model, 'Khalid', topk=4)
>>> predictions['likelihood'] = np.exp(predictions['log_loss'])
>>> predictions
        text  log_loss nationality  likelihood
rank
0     Khalid     -1.17    Algerian        0.31
1     Khalid     -1.35    Moroccan        0.26
2     Khalid     -1.80   Malaysian        0.17
3     Khalid     -2.40      Arabic        0.09

这意味着模型仅有 31%的信心认为 Rochdi 是阿尔及利亚人。这些概率(可能性)可以用来解释你的模型对老板、队友甚至用户有多自信。

如果你是"通过打印调试"的粉丝,你可以修改你的模型来打印出你对模型使用的数学的任何感兴趣的内容。PyTorch 模型可以在你想要记录一些内部过程时用打印语句进行仪器化。如果你决定使用这种方法,你只需要将张量从它们所在的 GPU 或 CPU 上.detach(),将它们带回你的工作 RAM 中进行记录在你的模型类中。

RNN 的一个很好的特性是,预测是逐步建立的,当你的forward()方法在每个连续的标记上运行时。这意味着你甚至可能不需要添加打印语句或其他仪器到你的模型类中。相反,你可以为输入文本的部分进行隐藏和输出张量的预测。

你可能想要为你的模型类添加一些predict_*便利函数,以便更容易地探索和解释模型的预测。如果你还记得 Scikit-Learn 中的LogisticRegression模型,它有一个predict_proba方法用于预测概率,除了用于预测类别的predict方法。一个 RNN 有一个额外的隐藏状态向量,有时你可能想要检查这个向量,以了解网络是如何进行预测的。因此,你可以创建一个predict_hidden方法来输出 128 维的隐藏张量,以及一个predict_proba来显示每个目标类别(国籍)的预测概率。

>>> def predict_hidden(self, text="Khalid"):
...    text_tensor = self.encode_one_hot_seq(text)
...    with torch.no_grad():  # #1
...    hidden = self.hidden_init
...        for i in range(text_tensor.shape[0]):  # #2
...            y, hidden = self(text_tensor[i], hidden)  # #3
...    return hidden

这个predict_hidden便利方法将文本(姓氏)转换为张量,然后通过一个热编码张量迭代运行前向方法(或者只是模型的self)。

>>> def predict_proba(self, text="Khalid"):
...    text_tensor = self.encode_one_hot_seq(text)
...    with torch.no_grad():
...        hidden = self.hidden_init
...        for i in range(text_tensor.shape[0]):
...            y, hidden = self(text_tensor[i], hidden)
...    return y  # #1

这个predict_hidden方法让你访问模型最有趣的部分,即预测逻辑正在发生的地方。随着每个字符的学习,隐藏层会不断演化,越来越多地了解姓名的国籍。

最后,你可以使用一个predict_category便利方法来运行模型的前向传递预测,以预测一个姓名的国籍。

>>> def predict_category(self, text):
...    tensor = self.encode_one_hot_seq(text)
...    y = self.predict_proba(tensor)  # #1
...    pred_i = y.topk(1)[1][0].item()  # #2
...    return self.categories[pred_i]

要认识到的关键一点是,对于所有这些方法,你不一定需要输入姓氏的整个字符串。重复评估姓氏文本的前部分是完全可以的,只要每次重置隐藏层即可。

如果你输入一个不断扩展的文本窗口,你可以看到预测和隐藏层在对姓氏的理解上是如何演变的。在与本书其他读者的集体编程会议期间,我们注意到几乎所有的名字最初都被预测为"中国",直到第三或第四个字符之后。这可能是因为很多中国姓氏只包含 4 个(或更少)字符。

现在你有了辅助函数,你可以用它们来记录隐藏层和类别预测,当 RNN 在姓名的每个字母上运行时。

>>> text = 'Khalid'
>>> pred_categories = []
>>> pred_hiddens = []
>>> for i in range(1, len(text) + 1):
...    pred_hiddens.append(model.predict_hidden(text[:i]))  # #1
...    pred_categories.append(model.predict_category(text[:i]))
>>> pd.Series(pred_categories, input_texts)
# K English
# Kh Chinese
# Kha Chinese
# Khal Chinese
# Khali Algerian
# Khalid Arabic

而且,您可以创建一个 128 x 6 的矩阵,其中包含 6 个字母名称中的所有隐藏层值。 PyTorch 张量列表可以转换为列表,然后转换为 DataFrame,以便更容易地操作和探索。

>>> hiddens = [h[0].tolist() for h in hiddens]
>>> df_hidden = pd.DataFrame(hidden_lists, index=list(text))
>>> df_hidden = df_hidden.T.round(2)  # #1
>>> df_hidden
    0     1     2     3     4     5   ...  122   123   124   125   126   127
K  0.10 -0.06 -0.06  0.21  0.07  0.04 ... 0.16  0.12  0.03  0.06 -0.11  0.11
h -0.03  0.03  0.02  0.38  0.29  0.27 ...-0.08  0.04  0.12  0.30 -0.11  0.37
a -0.06  0.14  0.15  0.60  0.02  0.16 ...-0.37  0.22  0.30  0.33  0.26  0.63
l -0.04  0.18  0.14  0.24 -0.18  0.02 ... 0.27 -0.04  0.08 -0.02  0.46  0.00
i -0.11  0.12 -0.00  0.23  0.03 -0.19 ...-0.04  0.29 -0.17  0.08  0.14  0.24
d  0.01  0.01 -0.28 -0.32  0.10 -0.18 ... 0.09  0.14 -0.47 -0.02  0.26 -0.11
[6 rows x 128 columns]

这堵数字墙包含了您的 RNN 在阅读名称时的所有“想法”。

提示

有一些 Pandas 显示选项可以帮助您对大型 DataFrame 中的数字有所了解,而不会出现 TMI(“太多信息”)。以下是本书中提高表格打印质量的一些设置。

要仅显示浮点值的 2 个小数位精度,请尝试:pd.options.display.float_format = '{:.2f}'

要从 DataFrame 显示最多 12 列和 7 行的数据:pd.options.display.max_columns = 12pd.options.display.max_rows = 7

这些选项仅影响数据的显示表示,而不是进行加法或乘法时使用的内部值。

正如您可能用其他大量数字的表格所做的那样,通过将其与您感兴趣的其他数字相关联,通常可以找到模式。例如,您可能想发现隐藏权重中是否有任何一个正在跟踪 RNN 在文本中的位置-即它距离文本的开头或结尾有多少个字符。

>>> position = pd.Series(range(len(text)), index=df_hidden.index)
>>> pd.DataFrame(position).T
# K h a l i d
# 0 0 1 2 3 4 5
>>> df_hidden_raw.corrwith(position).sort_values()
# 11 -0.99
# 84 -0.98
# 21 -0.97
# ...
# 6 0.94
# 70 0.96
# 18 0.96

有趣的是,我们的隐藏层在其隐藏内存中有空间来记录许多不同地方的位置。而且最强的相关性似乎是负相关。这些可能有助于模型估计当前字符是名字中最后一个字符的可能性。当我们观察了各种各样的示例名称时,预测似乎只在最后一个或两个字符处收敛到正确的答案。安德烈·卡尔帕西在他的博客文章《RNN 的不合理有效性》中尝试了几种从 RNN 模型的权重中获得见解的方法,这是在发现 RNN 时期的早期。^([22])]

8.2.4 多类别分类器与多标签标记器

怎样应对姓氏的多个不同正确国籍的歧义性?答案是多标签分类或标记,而不是熟悉的多类别分类。因为“多类分类”和“多标签分类”这些术语听起来相似且容易混淆,您可能想使用“多标签标记”或仅使用“标记”而不是“多标签分类”这个术语。如果您正在寻找适用于这种问题的sklearn模型,则要搜索“多输出分类”。

多标签标记器是用于模棱两可的任务的。在 NLP 意图分类和标记中,标签充满了具有模糊重叠边界的意图标签。当我们说“标记器”时,我们不是在谈论 Banksy 和 Bario Logan 街头艺术家之间的涂鸦之争,而是在谈论一种机器学习模型,可以为您数据集中的对象分配多个离散标签。

多类分类器具有多个不同的分类标签,这些标签与对象匹配,每个对象对应一个标签。分类变量只取几个相互排斥的类别中的一个。例如,如果您想要预测名字(给定名字)的语言和性别,那么就需要一个多类分类器。但是,如果您想要为名字标记所有相关的适当国籍和性别,那么您就需要一个标记模型。

这对您来说可能是在纠结细节,但这绝不仅仅是语义。在互联网上错误建议的噪音中,正在丢失您处理的文本的语义(含义)。当 David Fischer 在 ReadTheDocs.com(RTD)和圣地亚哥 Python 组织者开始学习 NLP 以构建 Python 包分类器时,他遇到了这些误导的博客文章。最终,他建立了一个标记器,为 RTD 广告商提供了更有效的广告位置,并为阅读文档的开发人员提供了更相关的广告。

提示

要将任何多类分类器转换为多标签标记器,您必须将激活函数从softmax更改为逐元素的sigmoid函数。Softmax 在所有相互排斥的分类标签上创建一个概率分布。Sigmoid 函数允许每个值取零到一之间的任意值,以便您多标签标记输出中的每个维度表示该特定标签适用于该实例的独立二进制概率。

8.3 通过时间的反向传播

对于 RNN 来说,反向传播比对 CNN 来说要复杂得多。训练 RNN 之所以如此计算密集,是因为它必须为每个文本示例的每个标记执行前向和后向计算多次。然后,它必须再次为 RNN 中的下一层执行所有这些操作。这一系列操作非常重要,因为一个标记的计算取决于前一个标记。您正在将输出和隐藏状态张量循环回到下一个标记的计算中。对于 CNN 和完全连接的神经网络,前向和后向传播计算可以同时在整个层上运行。您文本中每个标记的计算不会影响同一文本中相邻标记的计算。RNN 在时间上进行前向和后向传播,从序列中的一个标记到下一个标记。

但是您可以在图 8.7 中的展开的循环神经网络中看到,您的训练必须将错误通过所有权重矩阵乘法传播回去。即使权重矩阵对于数据中的所有标记都是相同的,或者tied,它们也必须作用于每个文本中的每个标记。因此,您的训练循环将需要向后循环遍历所有标记,以确保每一步的错误都被用来调整权重。

初始误差值是最终输出向量与适用于该文本样本的“真实”向量之间的距离。一旦你得到了真实向量和预测向量之间的差异,你就可以通过时间(标记)向后传播该误差,将该误差传播到上一个时间步(上一个标记)。PyTorch 包将使用与您在代数或微积分课程中使用的链式法则非常相似的东西来实现这一点。PyTorch 在正向传播过程中计算它需要的梯度,然后将这些梯度乘以每个标记的误差,以决定调整权重的量并改善预测。

一旦你为一层中的所有标记调整了权重,你就可以为下一层中的所有标记做同样的事情。从网络的输出一直回到输入(标记),你最终将不得不多次“触及”或调整每个文本示例的所有权重。与通过线性层或 CNN 层的反向传播不同,RNN 上的反向传播必须按顺序进行,一次一个标记。

一个循环神经网络(RNN)只是一个普通的前馈神经网络,被“卷起来”,以便线性权重被为文本中的每个标记再次相乘。如果展开它,你可以看到所有需要调整的权重矩阵。而且像卷积神经网络一样,许多权重矩阵在神经网络计算图的展开视图中对所有标记共享。RNN 是一个长的内核,它重用了每个文本文档中的“所有”权重。RNN 的权重是一个长而巨大的内核。在每个时间步长,它是相同的神经网络,只是在文本中的那个位置处理不同的输入和输出。

提示

在所有这些示例中,你一直在传递一个单一的训练示例,前向传播,然后反向传播错误。与任何神经网络一样,你的网络中的这个前向传播可以在每个训练样本之后发生,或者你可以批处理。而且批处理除了速度之外还有其他好处。但是现在,把这些过程看作单个数据样本、单个句子或文档。

在第七章中,你学会了如何使用 CNN 一次处理一个字符串。CNN 可以使用代表这些模式的内核(权重矩阵)识别文本中的意义模式。CNN 和前几章的技术非常适用于大多数 NLU 任务,如文本分类、意图识别和创建表示文本意义的嵌入向量。CNN 通过可以检测文本中几乎任何意义模式的重叠窗口权重来实现这一点。

图 8. 9. 使用嵌入进行 1D 卷积


在第七章中,您想象将内核窗口跨越文本,一次一步地进行滑动。 但实际上,机器是在并行进行所有乘法。 操作的顺序并不重要。 例如,卷积算法可以对词对进行乘法,然后跳到窗口的所有其他可能位置。 它只需要计算一堆点积,然后在最后将它们全部相加或汇总在一起。 加法是可交换的(顺序无关紧要)。 实际上,在 GPU 上,这些矩阵乘法(点积)几乎同时并行进行。

但是 RNN 不同。 使用 RNN 时,您将一个标记的输出重新循环到您对下一个标记执行的点积中。 因此,即使我们讨论过 RNN 可以处理任意长度的文本,为了加快速度,大多数 RNN 流水线会将文本截断和填充到固定长度。 这样会展开 RNN 的矩阵乘法,这样和你需要为 RNN 需要两次矩阵乘法,而 CNN 需要一次乘法相比速度更快。 您需要一个用于隐藏向量的权重矩阵和另一个用于输出向量的权重矩阵。

如果您进行过任何信号处理或金融建模,您可能已经使用了 RNN 而不自知。 CNN 中的回归部分在信号处理和定量金融分析领域被称为“自回归”。 自回归移动平均模型是一个伪装的 RNN。^([23])

在本章中,您正在了解一种新的结构化输入数据的方式。 就像在 CNN 中一样,每个标记都与文本中的时间(t)或位置相关联。 变量 t 只是您标记序列中的索引变量的另一个名称。

甚至会看到您使用 t 的整数值来检索序列中的特定标记,例如 token = tokens[t]。 因此,当您看到 t-1tokens[t-1] 时,您知道它是指前一个时间步或标记。 而 t+1tokens[t+1] 则是指下一个时间步或标记。 在过去的章节中,您可能已经看到我们有时将 i 用于此索引值。

现在,您将使用多个不同的索引来跟踪输入到网络中的内容以及网络输出的内容:

  • ttoken_num:当前输入到网络中的张量的时间步或标记位置
  • ksample_num:正在训练的文本示例的批次中的样本号
  • bbatch_num:正在训练的样本集的批次号
  • epoch_num: 训练开始后经过的周期数
图 8.10 输入到循环网络的数据


这种文档的二维张量表示类似于第二章中文本的“自动钢琴”表示。只不过这一次,您将使用词嵌入来创建每个标记的密集表示。

对于 RNN,您不再需要一次处理每个文本样本。相反,您逐个标记地处理文本。

在你的循环神经网络中,你传入第一个标记的词向量,并获得网络的输出。然后传入第二个标记,但同时也传入了第一个标记的输出!然后传入第三个标记以及第二个标记的输出。依此类推。网络有一个关于前后、因果关系的概念 - 一些关于时间的模糊概念(见图 8.8)。

8.3.1 初始化 RNN 中的隐藏层

当您在每个新文档上重新启动 RNN 的训练时,隐藏层存在一个鸡生蛋的问题。对于您要处理的每个文本字符串,都没有“先前”的标记或先前的隐藏状态向量可供重新循环回网络中。您没有任何东西可以引导并启动循环(递归)循环。您的模型的 forward() 方法需要一个向量与输入向量连接,以便将其调整为与 W_c2hW_c2o 相乘的正确大小。

最明显的方法是将初始隐藏状态设置为全零,并在训练每个样本时快速将偏差和权重增加到最佳值。这对于任何正在跟踪时间的神经元(当前(递归)正在处理的标记序列中的位置)可能非常有用。但是,还有一些神经元试图预测您离序列末尾有多远。而且您的网络有一个定义明确的极性,0 表示关闭,1 表示打开。因此,您可能希望您的网络以零和一的混合值开始隐藏状态向量。最好的做法是使用一些介于零和 1 之间的梯度或值模式,这是您特定的“秘密配方”,基于您处理类似问题的经验。

在初始化深度学习网络时,变得有创意并保持一致,还有一个额外的好处,那就是创造更多可“解释”的人工智能。您经常会在权重中创建可预测的结构。通过每次都以相同的方式进行,您将知道在所有层中查找的位置。例如,您将知道隐藏状态向量中的哪些位置在跟踪文本中的位置(时间)。

要充分利用初始化值的一致性,您还需要在训练期间使用的样本的排序上保持一致。 您可以按其长度对文本进行排序,就像您在第七章中使用 CNN 时所做的那样。 但是,许多文本将具有相同的长度,因此您还需要一种排序算法,该算法可以一致地对具有相同长度的样本进行排序。 字母顺序是一个明显的选择,但这会倾向于使您的模型陷入局部最小值,因为它试图找到数据的最佳可能预测。 它会在“A”名称上表现得非常好,但在“Z”名称上表现不佳。 因此,在完全掌握已被证明非常有效的随机抽样和洗牌之前,请不要追求这种高级初始化方法。

只要您在整个训练过程中保持一致,您的网络将学习您的网络需要在这些初始值之上叠加的偏差和权重。 这可以在您的神经网络权重中创建一个可识别的结构。

提示

在某些情况下,使用初始隐藏状态而不是全零状态可能有助于启动您的神经网络。 Johnathon Frankle 和 Michael Carbin 发现,有意识地重用良好的初始化值可能是帮助网络找到特定数据集的全局最小损失的关键^(参见脚注 [24]) Their approach is to initialize all weights and biases using a random seed that can be reused in subsequent training.

现在您的网络记住了某些东西! 嗯,有点像。 还有一些事情需要您解决。 首先,这样的结构中反向传播工作是如何进行的?

Keras 社区中另一种流行的方法是保留来自先前批处理的隐藏层。 这种“预训练”的隐藏层嵌入给出了您的语言模型有关新文档上下文的信息 - 即它之前的文本。 但是,只有在训练中保持了文档的顺序才有意义。 在大多数情况下,您会在每个 epoch 中对训练示例进行洗牌和重洗牌。 当您希望您的模型在没有通过阅读类似文档或附近文本段落进行任何引导的情况下同样出色地进行预测时,您会这样做。

所以除非您试图挤出您对一个非常困难的问题的每一点准确性,否则您可能只需在每次将新文档输入模型时将其重置为零即可。 如果您确实使用了这种 stateful 方法来训练 RNN,请确保您能够在真实世界中(或者在您的测试集上)对每个预测需要的上下文文档进行热身,并确保您以一致的顺序准备文档,并且可以为需要对模型进行预测的新文档集合重现此文档排序。

8.4 使用递归网络记忆

循环神经网络记住了它们正在处理的文本中的前面单词,并且可以在处理理论上无限量的文本时不断地向其记忆中添加更多模式。这可以帮助它理解跨越整个文本的模式,并且识别出两个具有截然不同含义的文本之间的区别,这取决于单词出现的位置。

抱歉这封信太长了。我没有时间写一封更短的。

抱歉这封信太短了。我没有时间写一封更长的。

交换“短”和“长”这两个词,会改变这个马克·吐温的引用的含义。了解马克·吐温幽默的干燥的幽默感和对写作的热情,你能分辨出哪个是他的引用吗?是他为长信道歉的那个。他在轻松地谈论编辑和简洁写作是一项艰苦的工作。这是一件即使是最聪明的人类也比最聪明的人工智能做得更好的事情。

您在第七章学到的卷积神经网络会很难在关于长信和短信的这两个句子之间建立联系,而循环神经网络却可以轻松地做到这一点。这是因为卷积神经网络在识别模式时有一个有限的文本窗口。要理解整个段落,您必须构建具有重叠核或文本窗口的 CNN 图层。循环神经网络可以自然地做到这一点。循环神经网络记住了它们读取的文档中的每个标记的一些信息。在您告诉它们您已完成该文档之前,它们会记住您输入到其中的所有内容。这使它们更擅长摘要马克·吐温的长信,并使它们更擅长理解他的长而复杂的笑话。

马克·吐温是对的。简洁地传达事物需要技巧、智慧和对细节的关注。在论文“注意力就是一切”中,阿希什·瓦斯瓦尼揭示了变换器如何添加注意力矩阵,使循环神经网络能够准确理解更长的文档。在第九章中,您将看到这种注意机制的运作,以及使变换器方法成为迄今为止最成功和最灵活的深度学习架构的其他技巧。

长文本的摘要仍然是自然语言处理中未解决的问题。即使是最先进的循环神经网络和变换器也会犯初级错误。事实上,人工智能的赫特奖将为维基百科压缩(无损摘要)每提高一百分之一而奖励您 5000 欧元。赫特奖专注于压缩维基百科中的符号。您将学习如何压缩文本的含义。这甚至更难做到。很难衡量您做得有多好。

你将不得不开发通用智能机器,它们能够理解常识逻辑,并能够组织和操作记忆以及这些记忆的符号表示。这可能看起来无望,但事实并非如此。到目前为止,你构建的 RNN 可以记住其理解的一切,都存储在一个大的隐藏表示中。你能想到一种方法来给这个记忆结构一些结构,让你的机器可以更好地组织关于文本的思维吗?如果你让你的机器有一种单独的方式来维持短期记忆和长期记忆怎么样?这将给它一个工作记忆,当它遇到需要记住的重要概念时,它可以将其存储在长期记忆中。

8.4.1 单词级语言模型

所有你听说过的最令人印象深刻的语言模型都使用单词作为它们的令牌,而不是单个字符。所以,在你跳入 GRU 和 LSTM 之前,你需要重新安排你的训练数据,以包含单词 ID 的序列,而不是字符(字母)ID。而且你将不得不处理比只有姓氏长得多的文档,所以你会想要对你的数据集进行 batchify 以加快速度。

看一看维基文本-2 数据集,并思考如何预处理它以创建一系列令牌 ID(整数)。

>>> lines = open('data/wikitext-2/train.txt').readlines()
>>> for line in lines[:4]:
...     print(line.rstrip()[:70])
 = Valkyria Chronicles III =
 =======
 Senjō no Valkyria 3 : <unk> Chronicles ( Japanese : 戦場のヴァルキュリア3 ,
  lit

哇哦,这将是一个有趣的数据集。即使是英文版的维基百科也包含很多其他的自然语言,比如这篇第一篇文章中的日语。如果你使用前面章节的分词和词汇构建技能,你应该能够创建一个类似于即将出现的 RNN 示例中使用的 Corpus 类。

>>> from nlpia2.ch08.data import Corpus
>>> corpus = Corpus('data/wikitext-2')
>>> corpus.train
tensor([ 4,  0,  1,  ..., 15,  4,  4])

并且你总是希望确保你的词汇量包含了你需要从单词 ID 序列中生成正确单词的所有信息:

>>> vocab = corpus.dictionary
>>> [vocab.idx2word[i] for i in corpus.train[:7]]
['<eos>', '=', 'Valkyria', 'Chronicles', 'III', '=', '<eos>']

现在,在训练过程中,你的 RNN 将不得不逐个读取每个令牌。这可能相当慢。如果你能同时训练它在多个文本段落上呢?你可以通过将文本拆分成批次或 batchifying 你的数据来实现这一点。这些批次可以成为 PyTorch 中可以更有效地执行数学运算的矩阵中的列或行,在 GPU(图形处理单元)内。

nlpia2.ch08.data 模块中,你会找到一些批量化长文本的函数。

>>> def batchify_slow(x, batch_size=8, num_batches=5):
...    batches = []
...    for i in range(int(len(x)/batch_size)):
...        if i > num_batches:
...            break
...        batches.append(x[i*batch_size:i*batch_size + batch_size])
...    return batches
>>> batches = batchify_slow(corpus.train)
>>> batches
[tensor([4, 0, 1, 2, 3, 0, 4, 4]),
 tensor([ 5,  6,  1,  7,  8,  9,  2, 10]),
 tensor([11,  8, 12, 13, 14, 15,  1, 16]),
 tensor([17, 18,  7, 19, 13, 20, 21, 22]),
 tensor([23,  1,  2,  3, 24, 25, 13, 26]),
 tensor([27, 28, 29, 30, 31, 32, 33, 34])]

最后一步,你的数据已经准备好进行训练了。你需要 stack 这个列表中的张量,这样你就可以在训练过程中迭代一个大张量。

>>> torch.stack(batches)
tensor([[4, 0, 1, 2, 3, 0, 4, 4],
        [ 5,  6,  1,  7,  8,  9,  2, 10],
        [11,  8, 12, 13, 14, 15,  1, 16],
        ...

8.4.2 门控循环单元(GRUs)

对于短文本,具有单个激活函数的普通循环神经网络效果很好。所有神经元所需做的就是循环和重复利用它们迄今为止在文本中所读取的隐藏向量表示。但是普通循环神经网络的注意力集中范围有限,限制了它们理解较长文本的能力。随着您的机器阅读越来越多的文本,字符串中第一个令牌的影响会随着时间的推移而减弱。这就是门控循环单元(GRU)和长短期记忆(LSTM)神经网络试图解决的问题。

您认为如何抵消文本字符串中早期令牌的记忆衰减?您如何阻止衰减,但只针对长文本字符串开头的几个重要令牌?在记录或强调文本中的特定单词方面,您怎么想?这就是 GRU 所做的。GRU 添加了称为逻辑门(或只是“门”)的if语句到 RNN 神经元中。

机器学习和反向传播的魔法会替您处理 if 语句条件,因此您不必手动调整逻辑门阈值。RNN 中的门通过调整影响触发零或 1 输出(或介于两者之间的某种输出)的信号水平的偏置和权重来学习最佳阈值。而时间上的反向传播的魔法将训练 LSTM 门让重要信号(令牌含义的方面)通过并记录在隐藏向量和单元状态向量中。

但是等等,您可能认为我们的网络中已经有了 if 语句。毕竟,每个神经元都有一个非线性激活函数,作用是将一些输出压缩到零并将其他输出推向接近 1。因此,关键不是 LSTM 向网络添加门(激活函数)。关键在于新门是神经元内部并以一种连接方式连接的,这种连接方式创建了一个结构,您的神经网络不会自然地从一个正常的线性、全连接的神经元层中出现。这种结构是有意设计的,目的是反映研究人员认为将有助于 RNN 神经元解决这个长期记忆问题的内容。

除了原始 RNN 输出门之外,GRU 还在您的循环单元中添加了两个新的逻辑门或激活函数。

  1. 复位门:应该阻止隐藏层的哪些部分,因为它们对当前输出不再相关。
  2. 更新门:隐藏层的哪些部分应该与当前输出(现在,在时间t)相关。

您已经在 RNN 层的输出上有了一个激活函数。这个输出逻辑门在 GRU 中被称为“新”逻辑门。

>>> r = sigmoid(W_i2r.mm(x) + b_i2r +    W_h2r.mm(h) + b_h2r)  # #1
>>> z = sigmoid(W_i2z.mm(x) + b_i2z +    W_h2z.mm(h) + b_h2z)  # #2
>>> n =    tanh(W_i2n.mm(x) + b_i2n + r∗(W_h2n.mm(h) + b_h2n))  # #3

因此,当你考虑向你的神经网络添加多少单元来解决特定问题时,每个 LSTM 或 GRU 单元都给你的网络一个类似于 2 个 “普通” RNN 神经元或隐藏向量维度的容量。一个单元只是一个更复杂、更高容量的神经元,如果你数一数你的 LSTM 模型中的 “学习参数” 的数量,并将其与等效的 RNN 比较,你会看到这一点。

你可能想知道为什么我们开始使用"单元"这个词而不是"神经元"来描述这个神经网络的元素。研究人员使用"单元"或"细胞"来描述 LSTM 或 GRU 神经网络的基本构建块,因为它们比神经元稍微复杂一些。每个 LSTM 或 GRU 中的单元或细胞都包含内部门和逻辑。这使得你的 GRU 或 LSTM 单元具有更多的学习和理解文本的能力,因此你可能需要比普通 RNN 更少的单元来达到相同的性能。

重置更新 逻辑门是使用你在第五章熟悉的全连接线性矩阵乘法和非线性激活函数实现的。新的地方在于它们在每个标记上是递归实现的,并且它们是在隐藏向量和输入向量上并行实现的。图 8.12 显示了单个标记的输入向量和隐藏向量如何通过逻辑门流过并输出预测和隐藏状态张量。

图 8.11 GRU 通过逻辑门增加容量


如果你擅长阅读数据流图,比如图 8.12,你可能会看到 GRU 更新相关性 逻辑门实现了以下两个功能:

r = sigmoid(W_i2r.dot(x) + b_i2r + W_h2r.dot(h) + b_h2r)  # #1
z = sigmoid(W_i2z.dot(x) + b_i2z + W_h2z.dot(h) + b_h2z)  # #2

观察这两行代码,你会发现公式的输入完全相同。在这两个公式中,隐藏张量和输入张量都被权重矩阵相乘。如果你记得线性代数和矩阵乘法操作,你可能能简化这个过程。并且你可能会注意到在方块图(图 8.12)中,输入张量和隐藏张量在被重置权重矩阵 W_reset 进行矩阵乘法之前会被连接在一起。

一旦你将 GRU 添加到你的 RNN 模型架构中,你会发现它们更加高效。GRU 将以更少的学习参数、更少的训练时间和更少的数据获得更好的准确性。GRU 中的门给神经网络带来了结构,创造了更有效的机制来记住文本中的重要含义。为了衡量效率,你需要一些代码来计算模型中的学习(可训练)参数。这是你的模型必须调整以优化预测的权重值的数量。requires_grad 属性是一个简单的方法,用来检查特定层是否包含可学习参数。

>>> def count_parameters(model, learned=True):
...     return sum(
...         p.numel() for p in model.parameters()  # #1
...         if not learned or p.requires_grad  # #2
...     )

权重或学习参数越多,您的模型就能够学习有关数据的更多信息。但是,所有巧妙的想法(例如卷积和递归)的整个目的是创建高效的神经网络。通过选择正确的算法组合、大小和层类型,您可以减少模型必须学习的权重或参数的数量,并同时创建更智能的模型,具有更大的能力做出良好的预测。

如果您使用nlpia2/ch08/rnn_word/hypertune.py脚本尝试各种 GRU 超参数,则可以将所有结果与 RNN 结果聚合在一起,以进行比较。

>>> import jsonlines  # #1
>>> with jsonlines.open('experiments.jsonl') as fin:
...     lines = list(fin)
>>> df = pd.DataFrame(lines)
>>> df.to_csv('experiments.csv')
>>> cols = 'learned_parameters rnn_type epochs lr num_layers'
>>> cols += ' dropout epoch_time test_loss'
>>> cols = cols.split()
>>> df[cols].round(2).sort_values('test_loss', ascending=False)
>>> df
     parameters  rnn_type  epochs   lr  layers  drop  time (s)  loss
3      13746478  RNN_TANH       1  0.5       5   0.0     55.46  6.90
155    14550478       GRU       1  0.5       5   0.2     72.42  6.89
147    14550478       GRU       1  0.5       5   0.0     58.94  6.89
146    14068078       GRU       1  0.5       3   0.0     39.83  6.88
1      13505278  RNN_TANH       1  0.5       2   0.0     32.11  6.84
..          ...       ...     ...  ...     ...   ...       ...   ...
133    13505278  RNN_RELU      32  2.0       2   0.2   1138.91  5.02
134    13585678  RNN_RELU      32  2.0       3   0.2   1475.43  4.99
198    14068078       GRU      32  2.0       3   0.0   1223.56  4.94
196    13585678       GRU      32  2.0       1   0.0    754.08  4.91
197    13826878       GRU      32  2.0       2   0.0    875.17  4.90

从这些实验结果可以看出,GRU 是创建语言模型以足够准确地预测下一个单词的最佳选择。令人惊讶的是,与其他 RNN 架构相比,GRU 不需要使用更多的层数来实现相同的准确性。并且,与 RNN 相比,它们所需的训练时间更少,以实现可比较的准确性。

8.4.3 长短期记忆(LSTM)

LSTM 神经元增加了两个内部门,以试图提高 RNN 的长期和短期记忆容量。 LSTM 保留了更新和相关性门,但添加了新的门来进行遗忘和输出门,共四个内部门,每个具有不同的目的。第一个门只是您熟悉的普通激活函数。

  1. 遗忘门(f):是否完全忽略隐藏层的某个元素,以为将来更重要的令牌腾出空间。
  2. 输入或更新门(i):隐藏层的哪些部分应该对当前输出(现在,在时间t)起作用。
  3. 相关性或细胞门(i):应该阻塞哪些隐藏层的部分,因为它们与当前输出不再相关。
  4. 输出门(o):隐藏层的哪些部分应输出,既输出到神经元输出,也输出到文本中下一个令牌的隐藏层。

那么,图 8.12 右上角的未标注的tanh激活函数是什么?这只是用来从细胞状态创建隐藏状态向量的原始输出激活。隐藏状态向量保存了关于最近处理的令牌的信息;它是 LSTM 的短期记忆。细胞状态向量保存了关于文档自开始以来文本含义的表示,即长期记忆。

在图 8.13 中,你可以看到这四个逻辑门是如何配合使用的。每个逻辑门所需的各种权重和偏差都被隐藏起来,以精简图表。你可以想象在图表中看到的每个激活函数内进行的权重矩阵乘法。 另一个要注意的事情是隐藏状态不是唯一的循环输入和输出。你现在有了另一个编码或状态张量,称为单元状态。与以前一样,你只需要隐藏状态来计算每个时间步的输出。但是,新的单元状态张量是过去模式的长期和短期记忆所编码和存储的地方,以在下一个标记上重复使用。

图 8.12 LSTM 添加了一个遗忘门和一个单元输出


在图中,你可能只会在最聪明的博客文章中看到所需的显式线性权重矩阵,用于计算输出张量。^([30])即使是 PyTorch 文档也会忽略这个琐事。你需要在计划根据隐藏状态张量计算预测的哪一层添加这个全连接的线性层。

你可能会想:“等等,我以为所有隐藏状态(编码)都是相同的,为什么我们有这个新的单元状态?”好吧,这就是 LSTM 的长期记忆部分。单元状态是单独维护的,因此逻辑门可以记住事物并将它们存储在那里,而无需将它们混合在隐藏状态张量的较短期记忆中。单元状态逻辑与隐藏状态逻辑有些不同。它的设计是有选择性地保留它所保留的东西,为学习文本长期到达字符串结尾之前的东西保留空间。

计算 LSTM 逻辑门和输出的公式与计算 GRU 的公式非常相似。主要区别在于添加了另外 3 个函数,以计算所需的所有信号。一些信号已被重新路由以创建更复杂的网络,以存储短期和长期记忆之间的更复杂的连接模式。在隐藏状态和单元状态之间的这种更复杂的交互创造了更多的“容量”或内存和计算能力。因为 LSTM 单元包含更多的非线性激活函数和权重,所以它具有更多的信息处理能力。

r = sigmoid(W_i2r.mm(x) + b_i2r +    W_h2r.mm(h) + b_h2r)
z = sigmoid(W_i2z.mm(x) + b_i2z +    W_h2z.mm(h) + b_h2z)
n =    tanh(W_i2n.mm(x) + b_i2n + r∗(W_h2n.mm(h) + b_h2n))
f = sigmoid(W_i2f.mm(x) + b_i2f + W_h2f.mm(h) + b_h2f)  # #1
i = sigmoid(W_i2i.mm(x) + b_i2i + W_h2i.mm(h) + b_h2i)  # #2
g = tanh(W_i2g.mm(x) + b_i2g + W_h2y.mm(h) + b_h2g)  # #3
o = sigmoid(W_i2o.mm(x) + b_i2o + W_h2o.mm(h) + b_h2o)  # #4
c = f*c + i*g  # #5
h = o*tanh(c)

8.4.4 给你的 RNN 进行调谐

正如你在第七章学到的,随着你的神经网络变得越来越复杂,超参数调整变得越来越重要。随着模型变得越来越复杂,你对层次、网络容量和训练时间的直觉也会变得越来越模糊。RNNs 特别直观。为了启动你的直觉,我们训练了几十种不同的基本 RNNs,使用了不同的超参数组合,比如层数和每层的隐藏单元数。你可以使用nlpia2/ch08中的代码来探索你感兴趣的所有超参数。^([31])

import pandas as pd
import jsonlines
with jsonlines.open('experiments.jsonl') as fin:
    lines = list(fin)
df = pd.DataFrame(lines)
df.to_csv('experiments.csv')
cols = 'rnn_type epochs lr num_layers dropout epoch_time test_loss'
cols = cols.split()
df[cols].round(2).sort_values('test_loss').head(10)
epochs   lr  num_layers  dropout  epoch_time  test_loss
37      12  2.0           2      0.2       35.43       5.23
28      12  2.0           1      0.0       22.66       5.23
49      32  0.5           2      0.0       32.35       5.22
57      32  0.5           2      0.2       35.50       5.22
38      12  2.0           3      0.2       46.14       5.21
50      32  0.5           3      0.0       37.36       5.20
52      32  2.0           1      0.0       22.90       5.10
55      32  2.0           5      0.0       56.23       5.09
53      32  2.0           2      0.0       32.49       5.06
54      32  2.0           3      0.0       38.78       5.04

探索这样的选项超空间并发现建立准确模型的令人兴奋的技巧是一件非常令人兴奋的事情。令人惊讶的是,对于这个在维基百科的小数据子集上训练的 RNN 语言模型,你可以在不最大化模型大小和容量的情况下获得很好的结果。你可以通过一个 3 层的 RNN 比一个 5 层的 RNN 获得更好的准确性。你只需要以激进的学习率开始,并将辍学保持在最低水平。而且你的层数越少,模型训练速度就越快。

提示

经常进行实验,并始终记录你尝试过的事情以及模型的工作情况。这种实践提供了加速模型构建和学习的直觉的最快路径。你的终身目标是训练你的心智模型,以预测在任何情况下哪些超参数值会产生最好的结果。

如果你觉得模型过度拟合了训练数据,但找不到简化模型的方法,你可以尝试增加Dropout(百分比)。这是一种减少过度拟合的破坏性方法,同时允许你的模型具有匹配数据所需的复杂性。如果你将辍学百分比设置得高于 50%,模型开始学习变得困难。你的学习速度会变慢,验证误差可能会大幅波动。但是 20%到 50%是很多 RNNs 和大多数 NLP 问题的一个相当安全的范围。

如果你像 Cole 和我一样刚开始学习 NLP,你可能会想知道什么是“单元”。所有先前的深度学习模型都使用“神经元”作为神经网络内的计算基本单元。研究人员使用更一般的术语“单元”来描述 LSTM 或 GRU 中包含内部门和逻辑的元素。因此,当你考虑在神经网络中添加多少个单元来解决特定问题时,每个 LSTM 或 GRU 单元都会给你的网络提供类似于两个“正常”RNN 神经元或隐藏向量维度的容量。一个单元只是一个更复杂、更高容量的神经元,如果你数一下你的 LSTM 模型中的“学习参数”的数量,并将其与等效 RNN 的参数进行比较,你就会明白这一点。

8.5 预测

本章训练的基于单词的循环神经网络语言模型使用了WikiText-2语料库。[³²] 使用这个语料库的好处是,研究人员经常用它来评估他们的语言模型的准确性。而且,维基百科文章的文本已经为你分词了。此外,诸如文章末尾的参考文献之类的无趣部分已经被删除。

不幸的是,WikiText-2 的 PyTorch 版本包含了随机替换或屏蔽了 2.7%的标记的"“标记。这意味着,除非有一些可预测的模式决定了哪些标记被”"标记屏蔽,否则你的模型将永远无法获得很高的准确性。但是如果你下载了原始的无标记文本,你可以在此基础上训练你的语言模型,并快速提高准确性。[³³] 你还可以将你的 LSTM 和 GRU 模型的准确性与使用此基准数据的专家们的准确性进行比较。[³⁴]

这是训练集train.txt末尾的一个示例段落。

>>> from nlpia2.ch08.rnn_word.data import Corpus
>>> corpus = Corpus('data/wikitext-2')
>>> passage = corpus.train.numpy()[-89:-35]
>>> '  '.join([vocab.idx2word[i] for i in passage])
Their ability at mimicry is so great that strangers have looked in vain
for the human they think they have just heard speak . <eos>
Common starlings are trapped for food in some Mediterranean countries .
The meat is tough and of low quality , so it is <unk> or made into <unk> .

看起来 WikiText-2 基准语料库中的最后一篇维基百科文章是关于普通椋鸟(欧洲的一种小鸟)。从文章中可以看出,椋鸟似乎擅长模仿人类的语言,就像你的 RNN 一样。

那些"“标记呢?这些是为了测试机器学习模型而设计的。语言模型的训练目标是预测被”“(未知)标记替换的单词。因为你的大脑中有一个相当好的英语语言模型,你可能可以预测那些被所有那些”"标记屏蔽的标记。

但是,如果你正在训练的机器学习模型认为这些是正常的英文单词,你可能会让它困惑。在本章中训练的循环神经网络正在试图识别无意义的""标记的含义,这将降低其对语料库中所有其他单词的理解。

提示

如果你想避免这种额外的错误和困惑,你可以尝试在wikitext-2基准的非官方原始文本上训练你的 RNN。在 nlpia2 存储库中的非官方原始版本和官方 wikitext-2 语料库的标记之间存在一对一的对应关系。[³⁵]

那么在这个训练集中有多少个"“和”"标记呢?

>>> num_eos = sum([vocab.idx2word[i] == '<eos>' for i in
 corpus.train.numpy()])
>>> num_eos
36718
>>> num_unk = sum([vocab.idx2word[i] == '<unk>' for i in
 corpus.train.numpy()])
>>> num_unk
54625
>>> num_normal = sum([
...     vocab.idx2word[i] not in ('<unk>', '<eos>')
...     for i in corpus.train.numpy()])
>>> num_normal
1997285
>>> num_unk / (num_normal + num_eos + num_unk)
0.0261...

所以 2.6%的标记已经被无意义的"“标记替换了。而”"标记标记了原始文本中的换行符,这通常是维基百科文章段落的结尾。

那么让我们看看它在写类似 WikiText-2 数据集中的新句子时的表现如何,包括"“标记。我们将提示模型以单词"The"开始写作,以找出它的"思路”。

>>> import torch
>>> from preprocessing import Corpus
>>> from generate import generate_words
>>> from model import RNNModel
>>> corpus = Corpus('data/wikitext-2')
>>> vocab = corpus.dictionary
>>> with open('model.pt', 'rb') as f:
...    orig_model = torch.load(f, map_location='cpu')  # #1
>>> model = RNNModel('GRU', vocab=corpus.dictionary, num_layers=1)  # #2
>>> model.load_state_dict(orig_model.state_dict())
>>> words = generate_words(
...    model=model, vocab=vocab, prompt='The', temperature=.1)  # #3
>>> print('  '.join(w for w in words))
...
= =  Valkyria Valkyria Valkyria Valkyria = = The kakapo is a common
 starling , and the of the of the ,
...

训练集中的第一行是“=瓦尔基利亚编年史 III =”,训练语料库中的最后一篇文章标题为“=普通星雀 =”。 因此,这个 GRU 记住了如何生成类似于它所读取的文本段落开头和结尾的文本。 因此,它似乎确实具有长期和短期记忆能力。 考虑到我们只在一个非常小的数据集上训练了一个非常简单的模型,这是令人兴奋的。 但是这个 GRI 似乎还没有能力存储在两百万令牌长序列中找到的所有英语语言模式。 它肯定不会很快进行任何意义上的理解。

注意

意义是人们赋予他们共享的经验的意义的方式。 当您尝试向自己解释其他人为什么做他们正在做的事情时,您正在进行意义上的理解。 你不必独自一人做。 一个社区可以通过社交媒体应用程序甚至对话式虚拟助手进行公共对话来集体进行。 这就是为什么它经常被称为“集体意义制定”。 类似 DAOStack 的初创公司正在尝试使用聊天机器人提炼出社区的最佳想法,并将它们用于构建知识库和做出决策。^([36])

您现在知道如何训练一个多功能的 NLP 语言模型,可以在单词级或字符级标记上使用。 您可以使用这些模型来对文本进行分类,甚至生成适度有趣的新文本。 而且您不必在昂贵的 GPU 和服务器上疯狂。

8.6 测试自己

  • 有哪些技巧可以提高 RNN 阅读长文档的“保留”?
  • 现实世界中一些“不合理有效”的 RNN 应用是什么?
  • 你如何使用一个名字分类器来做好事? 名称分类器的一些不道德用途是什么?
  • 对于类似于 Mark Burnett 的密码数据集的数百万用户名密码对的数据集,一些道德和亲社会的 AI 用途是什么?^([37])
  • 在 WikiText-2 数据集的原始文本、未屏蔽文本上训练 rnn_word 模型,其中令牌的比例为 “”。 这是否提高了您的单词级 RNN 语言模型的准确性?
  • 修改数据集,为每个名称使用多热张量标记所有国籍。([38])([39]) 您应该如何衡量准确性? 您的准确性是否提高了?

8.7 摘要

  • 在自然语言标记序列中,RNN 可以记住它到目前为止读取的所有内容,而不仅仅是有限的窗口。
  • 沿着时间(令牌)维度分割自然语言语句可以帮助您的机器加深对自然语言的理解。
  • 你可以将错误向后传播到过去(标记),也可以将错误向后传播到深度学习网络的层中。
  • 因为 RNN 特别是深度神经网络,RNN 梯度特别暴躁,它们可能会消失或爆炸。
  • 直到循环神经网络应用于任务,才能高效地对自然语言字符序列进行建模。
  • 在给定样本中,RNN 中的权重是在时间上聚合调整的。
  • 你可以使用不同的方法来检查循环神经网络的输出。
  • 你可以通过同时将令牌序列通过 RNN 向前和向后传递来模拟文档中的自然语言序列。

[1] 数学论坛 StackExchange 关于重复和递归的问题(math.stackexchange.com/questions/931035/recurrence-vs-recursive

[2] MIT 开放课程 CS 6.005 “软件构建” 的讲座(ocw.mit.edu/ans7870/6/6.005/s16/classes/10-recursion/

[3] Papers with Code 查询 RNN 应用(proai.org/pwc-rnn

[4] Daniel Miessler 的无监督学习播客第 340 集(mailchi.mp/danielmiessler/unsupervised-learning-no-2676196)以及 RNN 源代码(github.com/JetP1ane/Affinis

[5] Ryan Stout 的(github.com/ryanstout)BustAName 应用程序(bustaname.com/blog_posts

[6] Garrett Lander、Al Kari 和 Chris Thompson 为我们的揭秘 Mueller 报告项目做出了贡献(proai.org/unredact

[7] Rorschach 测试维基百科文章(en.wikipedia.org/wiki/Rorschach_test

[8] “循环神经网络的不合理有效性”(karpathy.github.io/2015/05/21/rnn-effectiveness

[9] PyTorch RNNBase 类源代码(github.com/pytorch/pytorch/blob/75451d3c81c88eebc878fb03aa5fcb89328989d9/torch/nn/modules/rnn.py#L44

[10] 波特兰 Python 用户组关于揭秘 Mueller 报告的演示(proai.org/unredact

[11] Lex Fridman 采访前间谍安德鲁·布斯塔曼特(lexfridman.com/andrew-bustamante

[12] 在 nlpia2 包中有更多信息和数据抓取代码(proai.org/nlpia-ch08-surnames

[13] Sean Robertson 的 PyTorch RNN 教程(pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html

[14] 原始的 PyTorch RNN 教程姓氏数据集含有重复项(download.pytorch.org/tutorial/data.zip

[15] 在 GitLab 上的 nlpia2 仓库中的 iPython history 日志,其中包含抓取姓氏数据的示例(proai.org/nlpia-ch08-surnames

[16] PyTorch 基于字符的 RNN 教程(pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html

[17] Qary(docs.qary.ai)结合了我们所有多语言聊天机器人的技术和数据(tangibleai.com/our-work

[18] 2020 年在阿姆斯特丹启动的 AI 算法注册表(algoritmeregister.amsterdam.nl/en/ai-register/

[19] 欧盟人工智能法案网站(artificialintelligenceact.eu/

[20] 被接受的“OECD 人工智能理事会”建议(legalinstruments.oecd.org/en/instruments/OECD-LEGAL-0449

[21] 感谢 Tiffany Kho 指出这一点。

[22] 脚注:“RNN 的不合理有效性” 作者是 Andrej Karpathy([karpathy.github.io/2015/05/21/rnn-effectiveness

[23] ARMA 模型解释(en.wikipedia.org/wiki/Autoregressive_model

[24] arxiv.org/pdf/1803.03635.pdf

[25] 《注意力机制就是你所需要的一切》作者是 Ashish Vaswani 等人(arxiv.org/abs/1706.03762

[26] en.wikipedia.org/wiki/Hutter_Prize

[27] 完整的源代码在 nlpia2 包中(gitlab.com/tangibleai/nlpia2/-/blob/main/src/nlpia2/ch08/rnn_word/data.py

[28] PyTorch GRU 层的文档(pytorch.org/docs/stable/generated/torch.nn.GRU.html#torch.nn.GRU

[29] PyTorch 文档讨论关于统计学习参数的问题(discuss.pytorch.org/t/how-do-i-check-the-number-of-parameters-of-a-model/4325/9

[30] 感谢 Rian Dolphin 提供的《LSTM 网络 | 详细解释》(archive.today/8YD7k

[31] nlpia2 Python 包中 ch08/rnn_word 模块中的 hypertune.py 脚本 gitlab.com/tangibleai/nlpia2/-/blob/main/src/nlpia2/ch08/rnn_word/hypertune.py

[32] PyTorch torchtext 数据集(pytorch.org/text/0.8.1/datasets.html#wikitext-2

[33] 包含所有 “unk” 标记的原始、未蒙版的文本的 “answers”(s3.amazonaws.com/research.metamind.io/wikitext/wikitext-2-raw-v1.zip

[34] 人工智能研究者(www.salesforce.com/products/einstein/ai-research/the-wikitext-dependency-language-modeling-dataset/

[35] nlpia2 包含本章节中使用的 rnn_word 模型代码和数据的代码和数据(data.html

[36] DAOStack 去中心化治理平台(deck.html

[37] 亚历山大·费什科夫(Alexander Fishkov)对马克·伯内特(Mark Burnett)的一千万个密码进行的分析(1424-passwords-on-the-internet-publicly-available-dataset.html)- 文章底部有种子磁力链接。

[38] PyTorch 社区多标签(标记)数据格式示例(905.html

[39] torchtext 数据集类多标签文本分类示例(11571.html


  1. 14 ↩︎
  2. 15 ↩︎
  3. [1] ↩︎
  4. [2] ↩︎
  5. 8 ↩︎
相关文章
|
17天前
|
机器学习/深度学习 人工智能 自然语言处理
Python自然语言处理实战:文本分类与情感分析
本文探讨了自然语言处理中的文本分类和情感分析技术,阐述了基本概念、流程,并通过Python示例展示了Scikit-learn和transformers库的应用。面对多义性理解等挑战,研究者正探索跨域适应、上下文理解和多模态融合等方法。随着深度学习的发展,这些技术将持续推动人机交互的进步。
19 1
|
19天前
|
自然语言处理 监控 数据挖掘
NLP实战:Python中的情感分析
6月更文挑战第6天
34 2
|
3天前
|
机器学习/深度学习 数据采集 人工智能
Python 高级实战:基于自然语言处理的情感分析系统
**摘要:** 本文介绍了基于Python的情感分析系统,涵盖了从数据准备到模型构建的全过程。首先,讲解了如何安装Python及必需的NLP库,如nltk、sklearn、pandas和matplotlib。接着,通过抓取IMDb电影评论数据并进行预处理,构建情感分析模型。文中使用了VADER库进行基本的情感分类,并展示了如何使用`LogisticRegression`构建机器学习模型以提高分析精度。最后,提到了如何将模型部署为实时Web服务。本文旨在帮助读者提升在NLP和情感分析领域的实践技能。
11 0
|
1月前
|
自然语言处理 API 数据库
自然语言处理实战第二版(MEAP)(六)(5)
自然语言处理实战第二版(MEAP)(六)
30 3
|
1月前
|
机器学习/深度学习 自然语言处理 机器人
自然语言处理实战第二版(MEAP)(六)(4)
自然语言处理实战第二版(MEAP)(六)
27 2
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
自然语言处理实战第二版(MEAP)(六)(2)
自然语言处理实战第二版(MEAP)(六)
28 2
|
1月前
|
机器学习/深度学习 自然语言处理 机器人
自然语言处理实战第二版(MEAP)(六)(3)
自然语言处理实战第二版(MEAP)(六)
29 1
|
15天前
|
机器学习/深度学习 自然语言处理 PyTorch
【从零开始学习深度学习】48.Pytorch_NLP实战案例:如何使用预训练的词向量模型求近义词和类比词
【从零开始学习深度学习】48.Pytorch_NLP实战案例:如何使用预训练的词向量模型求近义词和类比词
|
18天前
|
机器学习/深度学习 人工智能 自然语言处理
探索未来AI技术的前沿——自然语言处理的发展与应用
本文将深入探讨自然语言处理技术在人工智能领域中的重要性和应用前景。通过分析当前自然语言处理技术的发展趋势和实际应用案例,揭示了其在改善用户体验、提升工作效率以及推动产业创新方面的巨大潜力。
|
19天前
|
自然语言处理 前端开发 Java
探索自然语言生成技术的进展与应用
本文将介绍自然语言生成技术在不同领域的进展和应用。从前端到后端,从Java到Python,从C到PHP,从Go到数据库,我们将深入探讨这些技术的发展趋势和应用场景,并展示它们在实际项目中的价值。

热门文章

最新文章