这是用户在 2024-4-15 24:13 为 http://localhost:8888/notebooks/redownload/C4W1_Assignment/Files/tf/C4W1_Assignment.ipynb 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

Assignment 1: Neural Machine Translation


欢迎来到课程 4 的第一个作业。在这里,您将使用长短期记忆 (LSTM) 网络并注意构建英语到葡萄牙语的神经机器翻译 (NMT) 模型。机器翻译是自然语言处理中的一项重要任务,不仅可用于将一种语言翻译成另一种语言,还可用于词义消歧(例如确定“银行”一词是指金融银行还是指河边的土地) 。仅使用带有 LSTM 的循环神经网络 (RNN) 来实现这一点可以适用于短到中等长度的句子,但可能会导致非常长的序列梯度消失。为了解决这个问题,您将添加一个注意机制,以允许解码器访问输入句子的所有相关部分,无论其长度如何。通过完成此作业,您将:


  • 专注地实现编码器-解码器系统

  • 使用 Tensorflow 从头开始​​构建 NMT 模型

  • 使用贪婪和最小贝叶斯风险 (MBR) 解码生成翻译

 目录 ¶

In [1]:
In [2]:

1. Data Preparation


文本预处理位已经处理完毕(如果您对此感兴趣,请务必检查 utils.py 文件)。执行的步骤可以总结为:


  • 从文本文件中读取原始数据

  • 清理数据(使用小写字母、在标点符号周围添加空格、修剪空格等)

  • 将其分为训练集和验证集

  • 为每个句子添加句首和句尾标记
  • Tokenizing the sentences

  • 从标记化句子中创建 Tensorflow 数据集


花点时间检查一下原始句子:

In [3]:
English (to translate) sentence:

No matter how much you try to convince people that chocolate is vanilla, it'll still be chocolate, even though you may manage to convince yourself and a few others that it's vanilla.

Portuguese (translation) sentence:

Não importa o quanto você tenta convencer os outros de que chocolate é baunilha, ele ainda será chocolate, mesmo que você possa convencer a si mesmo e poucos outros de que é baunilha.
In [4]:


原始句子没有多大用处,因此删除它们以节省内存:

In [5]:


请注意,您从 utils.py 导入了 english_vectorizerportuguese_vectorizer 。这些是使用 tf.keras.layers.TextVectorization 创建的,它们提供了有趣的功能,例如可视化词汇表以及将文本转换为标记化 id 的方法,反之亦然。事实上,您可以检查两种语言词汇表的前十个单词:

In [6]:
First 10 words of the english vocabulary:

['', '[UNK]', '[SOS]', '[EOS]', '.', 'tom', 'i', 'to', 'you', 'the']

First 10 words of the portuguese vocabulary:

['', '[UNK]', '[SOS]', '[EOS]', '.', 'tom', 'que', 'o', 'nao', 'eu']


请注意,前 4 个字是为特殊字保留的。按顺序,这些是:

  •  空字符串

  • 表示未知单词的特殊标记

  • 表示句子开头的特殊标记

  • 表示句子结尾的特殊标记


您可以使用 vocabulary_size 方法查看词汇表中有多少个单词:

In [7]:
Portuguese vocabulary is made up of 12000 words
English vocabulary is made up of 12000 words


您可以定义 tf.keras.layers.StringLookup 对象来帮助您从单词映射到 id,反之亦然。对葡萄牙语词汇执行此操作,因为稍后当您从模型中解码预测时这将很有用:

In [8]:


尝试使用特殊标记和随机单词:

In [9]:
The id for the [UNK] token is 1
The id for the [SOS] token is 2
The id for the [EOS] token is 3
The id for baunilha (vanilla) is 7079


最后看一下将输入神经网络的数据是什么样子的。 train_dataval_data 都是 tf.data.Dataset 类型,并且已按 64 个示例的批次进行排列。要从 tf 数据集中获取第一批,您可以使用 take 方法。要从批次中获取第一个示例,您可以对张量进行切片并使用 numpy 方法进行更好的打印:

In [10]:
Tokenized english sentence:
[   2  210    9  146  123   38    9 1672    4    3    0    0    0    0]


Tokenized portuguese sentence (shifted to the right):
[   2 1085    7  128   11  389   37 2038    4    0    0    0    0    0
    0]


Tokenized portuguese sentence:
[1085    7  128   11  389   37 2038    4    3    0    0    0    0    0
    0]


In [11]:


有几个重要的细节需要注意。


  • 填充已应用于张量,且使用的值为 0

  • 每个示例由 3 个不同的张量组成:

    • 要翻译的句子

    • 右移翻译
    • The translation


前两个可以视为特征,第三个可以视为目标。通过这样做,您的模型可以执行教师强制,正如您在讲座中看到的那样。


现在是时候开始编码了!


2. 带注意力的 NMT 模型 ¶


您将构建的模型使用编码器-解码器架构。该循环神经网络 (RNN) 在其编码器中接收句子的标记化版本,然后将其传递到解码器进行翻译。正如讲座中提到的,仅使用 LSTM 的常规序列到序列模型对于中短句子会有效,但对于较长的句子就会开始退化。您可以将其想象如下图所示,其中输入句子的所有上下文都被压缩为传递到解码器块的一个向量。您可以看到这对于很长的句子(例如 100 个标记或更多)来说是一个问题,因为输入的第一部分的上下文对传递给解码器的最终向量影响很小。


向该模型添加注意力层可以让解码器访问输入句子的所有部分,从而避免此问题。为了说明这一点,我们只使用 4 个单词的输入句子,如下所示。请记住,编码器的每个时间步都会产生隐藏状态(由橙色矩形表示)。这些都被传递到注意力层,并且根据解码器的当前激活(即隐藏状态),每个层都会获得一个分数。例如,让我们考虑下图,其中已经做出了第一个预测“como”。为了产生下一个预测,注意力层将首先接收所有编码器隐藏状态(即橙色矩形)以及生成单词“como”时的解码器隐藏状态(即第一个绿色矩形)。有了这些信息,它将对每个编码器隐藏状态进行评分,以了解解码器应该关注哪个隐藏状态来生成下一个单词。作为训练的结果,模型可能已经了解到它应该与第二个编码器隐藏状态对齐,并随后为单词“você”分配高概率。如果我们使用贪婪解码,我们将输出所述单词作为下一个符号,然后重新启动过程以产生下一个单词,直到我们达到句尾预测。


实现注意力的方法有多种,我们将用于此任务的方法是缩放点积注意力,其形式如下:

Attention(Q,K,V)=softmax(QKTdk)V


您将在下周更深入地研究这个方程,但现在,您可以将其视为使用查询 (Q) 和键 (K) 计算分数,然后乘以值 (V) 以获取上下文向量解码器的特定时间步长。该上下文向量被馈送到解码器 RNN,以获得下一个预测单词的一组概率。除以键维度的平方根 ( dk ) 是为了提高模型性能,下周您还将了解更多相关信息。对于我们的机器翻译应用程序,编码器激活(即编码器隐藏状态)将是键和值,而解码器激活(即解码器隐藏状态)将是查询。


您将在接下来的部分中看到,只需几行代码即可实现这种复杂的架构和机制。


首先,您将定义两个重要的全局变量:


  • 词汇量的大小

  • LSTM 层中的单元数量(所有 LSTM 层将使用相同的数量)


在此作业中,英语和葡萄牙语的词汇量相同。因此,我们在整个笔记本中使用单个常量 VOCAB_SIZE。虽然在其他设置中,词汇量可能会有所不同,但我们的作业中并非如此。

In [12]:

Exercise 1 - Encoder


您的第一个练习是对神经网络的编码器部分进行编码。为此,请完成下面的 Encoder 类。请注意,在构造函数( __init__ 方法)中,您需要定义编码器的所有子层,然后在前向传递期间使用这些子层( call 方法)。


编码器由以下层组成:


  • 嵌入。对于该层,您需要定义适当的 input_dimoutput_dim 并让它知道您正在使用“0”作为填充,这可以通过使用 < 的适当值来完成b2> 参数。

  • 双向 LSTM。在 TF 中,您可以为类似 RNN 的层实现双向行为。这部分已经处理完毕,但您需要指定适当的图层类型及其参数。特别是,您需要设置适当的单元数,并确保 LSTM 返回完整序列而不仅仅是最后的输出,这可以通过使用 return_sequences 参数的适当值来完成。


您需要使用 TF 函数式 API 的语法来定义前向传播。这意味着您可以将函数调用链接在一起来定义您的网络,如下所示:

encoder_input = keras.Input(shape=(28, 28, 1), name="original_img")
x = layers.Conv2D(16, 3, activation="relu")(encoder_input)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(16, 3, activation="relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)
In [13]:
In [14]:
Tensor of sentences in english has shape: (64, 14)

Encoder output has shape: (64, 14, 256)
 预期输出 ¶
Tensor of sentences in english has shape: (64, 14)

Encoder output has shape: (64, 14, 256)
In [15]:
 All tests passed!


练习 2 - 交叉注意力 ¶


您的下一个练习是编写将在原始句子和翻译之间执行交叉关注的层。为此,请完成下面的 CrossAttention 类。请注意,在构造函数( __init__ 方法)中,您需要定义所有子层,然后在前向传递期间使用这些子层( call 方法)。对于这种特殊情况,其中一些位已经得到处理。


交叉注意力由以下几层组成:


  • 多头注意力。对于这一层,您需要定义适当的 key_dim ,它是键和查询张量的大小。您还需要将头数设置为 1,因为您没有实现多头注意力,而是实现两个张量之间的注意力。该层优于 Attention 的原因是它在前向传递过程中允许更简单的代码。


有几点需要注意:


  • 您需要一种方法来传递注意力的输出以及向右移动的翻译(因为这种交叉注意力发生在解码器端)。为此,您将使用“添加”图层,以便保留原始尺寸,如果您使用“连接”图层之类的图层,则不会发生这种情况。

  • 还通过使用 LayerNormalization 层执行层归一化,以提高网络的稳定性。

  • 您无需担心最后的步骤,因为这些步骤已经解决。
In [16]:
In [17]:
Tensor of contexts has shape: (64, 14, 256)
Tensor of translations has shape: (64, 15, 256)
Tensor of attention scores has shape: (64, 15, 256)
 预期输出 ¶
Tensor of contexts has shape: (64, 14, 256)
Tensor of translations has shape: (64, 15, 256)
Tensor of attention scores has shape: (64, 15, 256)
In [18]:
 All tests passed!


练习 3 - 解码器 ¶


现在,您将通过完成下面的 Decoder 类来实现神经网络的解码器部分。请注意,在构造函数( __init__ 方法)中,您需要定义解码器的所有子层,然后在前向传递期间使用这些子层( call 方法)。


解码器由以下层组成:


  • 嵌入。对于该层,您需要定义适当的 input_dimoutput_dim 并让它知道您正在使用“0”作为填充,这可以通过使用 < 的适当值来完成b2> 参数。

  • 预注意力 LSTM。与在编码器中使用双向 LSTM 不同,这里您将使用普通 LSTM。不要忘记设置适当的单元数,并确保 LSTM 返回完整序列而不仅仅是最后的输出,这可以通过使用 return_sequences 参数的适当值来完成。该层返回状态非常重要,因为推理需要这,因此请确保相应地设置 return_state 参数。请注意,LSTM 层以名为 memory_statecarry_state 的两个张量的元组形式返回状态,但是这些名称已更改,以更好地反映您在 hidden_state

  • 注意力层在要翻译的句子和右移翻译之间执行交叉注意力。这里您需要使用您在上一个练习中定义的 CrossAttention 图层。

  • 后注意力 LSTM。另一个 LSTM 层。对于这个,你不需要它来返回状态。

  • 最后是密集层。该单元的单位数量应与词汇表的大小相同,因为您希望它计算词汇表中每个可能单词的逻辑。确保为此使用 logsoftmax 激活函数,您可以通过 tf.nn.log_softmax 获得该函数。
In [19]:
In [20]:
Tensor of contexts has shape: (64, 14, 256)
Tensor of right-shifted translations has shape: (64, 15)
Tensor of logits has shape: (64, 15, 12000)
 预期输出 ¶
Tensor of contexts has shape: (64, 14, 256)
Tensor of right-shifted translations has shape: (64, 15)
Tensor of logits has shape: (64, 15, 12000)
In [21]:
 All tests passed!

Exercise 4 - Translator


现在,您必须将之前编码的所有层组合到一个实际模型中。为此,请完成下面的 Translator 类。请注意,与从 tf.keras.layers.Layer 继承的 Encoder 和 Decoder 类不同,Translator 类从 tf.keras.Model 继承。


请记住, train_data 将生成一个元组,其中包含要翻译的句子和右移的翻译,这是模型的“特征”。这意味着网络的输入将是包含上下文和目标的元组。

In [22]:
In [23]:
Tensor of sentences to translate has shape: (64, 14)
Tensor of right-shifted translations has shape: (64, 15)
Tensor of logits has shape: (64, 15, 12000)
 预期输出 ¶
Tensor of sentences to translate has shape: (64, 14)
Tensor of right-shifted translations has shape: (64, 15)
Tensor of logits has shape: (64, 15, 12000)
In [24]:
 All tests passed!

3. Training


现在您已经有了一个未经训练的 NMT 模型实例,是时候对其进行训练了。您可以使用下面的 compile_and_train 函数来实现此目的:

In [25]:
In [26]:
Epoch 1/20
500/500 [==============================] - 48s 68ms/step - loss: 5.2038 - masked_acc: 0.2093 - masked_loss: 5.2063 - val_loss: 4.4142 - val_masked_acc: 0.3094 - val_masked_loss: 4.4152
Epoch 2/20
500/500 [==============================] - 17s 33ms/step - loss: 3.8010 - masked_acc: 0.4067 - masked_loss: 3.8018 - val_loss: 3.0992 - val_masked_acc: 0.4933 - val_masked_loss: 3.1011
Epoch 3/20
500/500 [==============================] - 16s 32ms/step - loss: 2.7784 - masked_acc: 0.5399 - masked_loss: 2.7800 - val_loss: 2.4111 - val_masked_acc: 0.5808 - val_masked_loss: 2.4116
Epoch 4/20
500/500 [==============================] - 16s 32ms/step - loss: 2.2514 - masked_acc: 0.6121 - masked_loss: 2.2523 - val_loss: 2.0073 - val_masked_acc: 0.6421 - val_masked_loss: 2.0079
Epoch 5/20
500/500 [==============================] - 16s 31ms/step - loss: 1.8990 - masked_acc: 0.6619 - masked_loss: 1.9002 - val_loss: 1.7441 - val_masked_acc: 0.6780 - val_masked_loss: 1.7447
Epoch 6/20
500/500 [==============================] - 15s 30ms/step - loss: 1.6374 - masked_acc: 0.6953 - masked_loss: 1.6385 - val_loss: 1.6194 - val_masked_acc: 0.6999 - val_masked_loss: 1.6188
Epoch 7/20
500/500 [==============================] - 16s 31ms/step - loss: 1.5277 - masked_acc: 0.7111 - masked_loss: 1.5284 - val_loss: 1.4901 - val_masked_acc: 0.7121 - val_masked_loss: 1.4905
Epoch 8/20
500/500 [==============================] - 16s 32ms/step - loss: 1.4201 - masked_acc: 0.7261 - masked_loss: 1.4210 - val_loss: 1.4071 - val_masked_acc: 0.7223 - val_masked_loss: 1.4089
Epoch 9/20
500/500 [==============================] - 16s 32ms/step - loss: 1.3407 - masked_acc: 0.7349 - masked_loss: 1.3420 - val_loss: 1.3195 - val_masked_acc: 0.7343 - val_masked_loss: 1.3197
Epoch 10/20
500/500 [==============================] - 16s 31ms/step - loss: 1.2085 - masked_acc: 0.7522 - masked_loss: 1.2099 - val_loss: 1.2466 - val_masked_acc: 0.7483 - val_masked_loss: 1.2474
Epoch 11/20
500/500 [==============================] - 15s 30ms/step - loss: 1.1073 - masked_acc: 0.7633 - masked_loss: 1.1083 - val_loss: 1.1922 - val_masked_acc: 0.7572 - val_masked_loss: 1.1922
Epoch 12/20
500/500 [==============================] - 15s 30ms/step - loss: 1.0821 - masked_acc: 0.7690 - masked_loss: 1.0830 - val_loss: 1.1611 - val_masked_acc: 0.7551 - val_masked_loss: 1.1616
Epoch 13/20
500/500 [==============================] - 15s 31ms/step - loss: 1.0565 - masked_acc: 0.7714 - masked_loss: 1.0578 - val_loss: 1.1525 - val_masked_acc: 0.7580 - val_masked_loss: 1.1535
Epoch 14/20
500/500 [==============================] - 15s 31ms/step - loss: 1.0323 - masked_acc: 0.7750 - masked_loss: 1.0335 - val_loss: 1.1071 - val_masked_acc: 0.7685 - val_masked_loss: 1.1077
Epoch 15/20
500/500 [==============================] - 16s 31ms/step - loss: 0.9251 - masked_acc: 0.7887 - masked_loss: 0.9261 - val_loss: 1.0880 - val_masked_acc: 0.7670 - val_masked_loss: 1.0891
Epoch 16/20
500/500 [==============================] - 15s 31ms/step - loss: 0.8788 - masked_acc: 0.7955 - masked_loss: 0.8795 - val_loss: 1.0804 - val_masked_acc: 0.7698 - val_masked_loss: 1.0821
Epoch 17/20
500/500 [==============================] - 15s 30ms/step - loss: 0.8874 - masked_acc: 0.7942 - masked_loss: 0.8882 - val_loss: 1.0304 - val_masked_acc: 0.7752 - val_masked_loss: 1.0317
Epoch 18/20
500/500 [==============================] - 16s 31ms/step - loss: 0.8860 - masked_acc: 0.7938 - masked_loss: 0.8868 - val_loss: 1.0171 - val_masked_acc: 0.7780 - val_masked_loss: 1.0183
Epoch 19/20
500/500 [==============================] - 15s 31ms/step - loss: 0.8653 - masked_acc: 0.7976 - masked_loss: 0.8660 - val_loss: 1.0110 - val_masked_acc: 0.7812 - val_masked_loss: 1.0107
Epoch 20/20
500/500 [==============================] - 16s 31ms/step - loss: 0.7568 - masked_acc: 0.8148 - masked_loss: 0.7575 - val_loss: 1.0016 - val_masked_acc: 0.7820 - val_masked_loss: 1.0026


4. 使用模型进行推理 ¶


现在您的模型已经过训练,您可以使用它进行推理。为了帮助您完成此操作,提供了 generate_next_token 函数。请注意,该函数应在 for 循环内使用,因此您可以向其提供上一步的信息以生成下一步的信息。特别是,您需要跟踪解码器中预注意力 LSTM 的状态以及是否完成翻译。另请注意,引入了 temperature 变量,该变量确定如何在给定预测 logits 的情况下选择下一个标记:

In [27]:


通过运行以下单元格查看其工作原理:

In [28]:
Next token: [[6188]]
Logit: -18.7806
Done? False

Exercise 5 - translate


现在您可以将所有内容放在一起来翻译给定的句子。为此,请完成下面的 translate 函数。该函数将执行以下步骤:


  • 处理句子以对其进行翻译和编码

  • 设置解码器的初始状态

  • 获取最大迭代次数的下一个令牌(从 <SOS> 令牌开始)的预测(以防 <EOS> 令牌永远不会返回)

  • 返回翻译后的文本(作为字符串)、最后一次迭代的 logit(这有助于衡量序列整体翻译的确定性)以及标记格式的翻译。

Hints:


  • 上一个单元格提供了有关此功能如何工作的大量见解,因此如果您遇到困难,请参考它。
In [29]:


在温度为 0 的情况下尝试您的函数,这将产生确定性输出,相当于贪婪解码:

In [30]:
Temperature: 0.0

Original sentence: I love languages
Translation: eu amo idiomas as vezes .
Translation tokens:[[  9 522 850  38 231   4]]
Logit: -1.639


尝试使用温度为 0.7 的函数(随机输出):

In [31]:
Temperature: 0.7

Original sentence: I love languages
Translation: eu adorei idiomas as quatro moveis .
Translation tokens:[[   9 3998  850   38  852 2469    4]]
Logit: -0.254
In [32]:
 All tests passed!

5. Minimum Bayes-Risk Decoding


正如讲座中提到的,在每一步中获得最可能的代币不一定会产生最好的结果。另一种方法是进行最小贝叶斯风险解码或 MBR。实现这一点的一般步骤是:

  • Take several random samples

  • 针对所有其他样本对每个样本进行评分

  • 选择得分最高的一项


您将在以下部分中为这些步骤构建辅助函数。


通过设置不同的温度值来生成不同的翻译,您可以按照您在讲座中看到的方式生成一堆翻译,然后确定哪个是最佳候选。您现在将使用提供的 generate_samples 函数来执行此操作。此函数将返回任意所需数量的候选翻译以及每个翻译的对数概率:

In [33]:
In [34]:
Translated tensor: [9, 564, 850, 13, 11, 845, 20, 847, 4] has logit: -0.134
Translated tensor: [9, 3998, 850, 20, 503, 11, 604, 4] has logit: -0.445
Translated tensor: [9, 564, 850, 12, 279, 22, 130, 4] has logit: -0.084
Translated tensor: [9, 522, 850, 38, 2936, 4] has logit: -2.447

Comparing overlaps


现在您可以生成多个翻译,是时候想出一种方法来衡量每个翻译的优劣了。正如您在讲座中看到的,实现这一目标的一种方法是将每个样本与其他样本进行比较。


您可以使用多种指标来实现此目的,如讲座中所示,您可以尝试使用其中任何一种指标进行试验。对于本作业,您将计算一元语法重叠的分数。


这些指标之一是广泛使用但简单的 Jaccard 相似度,它获取两个集合的并集的交集。 jaccard_similarity 函数返回任意一对候选翻译和参考翻译的指标:

In [35]:
In [36]:
jaccard similarity between lists: [1, 2, 3] and [1, 2, 3, 4] is 0.750
 预期输出 ¶
jaccard similarity between tensors: [1, 2, 3] and [1, 2, 3, 4] is 0.750

Exercise 6 - rouge1_similarity


Jaccard 相似度很好,但机器翻译中更常用的指标是 ROUGE 分数。对于一元语法,这称为 ROUGE-1,如讲座中所示,您可以在比较两个样本时输出精确度和召回率的分数。要获得最终分数,您需要计算 F1 分数,如下所示:

score=2(precisionrecall)(precision+recall)


对于 rouge1_similarity 函数的实现,您需要使用 Python 标准库中的 Counter 类:

In [37]:
In [38]:
rouge 1 similarity between lists: [1, 2, 3] and [1, 2, 3, 4] is 0.857
 预期输出 ¶
rouge 1 similarity between lists: [1, 2, 3] and [1, 2, 3, 4] is 0.857
In [39]:
 All tests passed!


计算总分 ¶


您现在将构建一个函数来生成特定样本的总体得分。正如讲座中提到的,您需要将每个样本与所有其他样本进行比较。例如,如果我们生成 30 个句子,我们需要将句子 1 与句子 2 到 30 进行比较。然后,我们将句子 2 与句子 1 和 3 到 30 进行比较,依此类推。在每一步中,我们都会获得所有比较的平均分数,以获得特定样本的总体分数。为了说明这一点,这些将是生成 4 个样本列表的分数的步骤。

  • Get similarity score between sample 1 and sample 2
  • Get similarity score between sample 1 and sample 3
  • Get similarity score between sample 1 and sample 4

  • 获取前 3 步的平均分。这将是样本 1 的总分

  • 迭代并重复,直到样本 1 至 4 获得总分。


结果将存储在字典中以便于查找。

Exercise 7 - average_overlap


完成下面的 average_overlap 函数,该函数应实现上述过程:

In [40]:
In [41]:
average overlap between lists: [1, 2, 3], [1, 2, 4] and [1, 2, 4, 5] using Jaccard similarity is:

{0: 0.45, 1: 0.625, 2: 0.575}
 预期输出 ¶
average overlap between lists: [1, 2, 3], [1, 2, 4] and [1, 2, 4, 5] using Jaccard similarity is:

{0: 0.45, 1: 0.625, 2: 0.575}
In [42]:
average overlap between lists: [1, 2, 3], [1, 4], [1, 2, 4, 5] and [5, 6] using Rouge1 similarity is:

{0: 0.324, 1: 0.356, 2: 0.524, 3: 0.111}
 预期输出 ¶
average overlap between lists: [1, 2, 3], [1, 4], [1, 2, 4, 5] and [5, 6] using Rouge1 similarity is:

{0: 0.324, 1: 0.356, 2: 0.524, 3: 0.111}
In [43]:
 All tests passed!


在实践中,通常会看到使用加权平均值而不是算术平均值来计算总分。这是在下面的 weighted_avg_overlap 函数中实现的,您可以在实验中使用它来查看哪个会给出更好的结果:

In [44]:
In [45]:
weighted average overlap using Jaccard similarity is:

{0: 0.443, 1: 0.631, 2: 0.558}

mbr_decode


现在,您将把所有内容放在下面的 mbr_decode 函数中。最后一步没有评分,因为这个函数只是您迄今为止编码的所有很酷的东西的包装!


您可以使用它来尝试不同数量的样本、温度和相似性函数!

In [46]:
In [47]:
Translation candidates:
eu eu amo idiomas a senhora .
eu adoro idiomas tem de saudade de acaso a regiao .
eu adorei idiomas a pe de pe .
eu adoro idiomas sempre .
eu adoro idiomas por conta de cima de casa .
eu adoro idiomas as dez vezes .
eu amo idiomas as vezes ?
eu eu amo idiomas por favor
eu eu amo idiomas por acaso .
eu amo idiomas a sorte do mundo .

Selected translation: eu eu amo idiomas a senhora .


恭喜!下周,您将更深入地研究注意力模型并研究 Transformer 架构。您将构建另一个网络,但没有重复部分。这将表明您所需要的就是关注!应该很有趣!


保持良好的工作!

In [ ]: