【NLP】自然语言处理的中间序列建模

   🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

本章的目标是序列预测。序列预测任务要求我们标记序列的每个项目。这样的任务在自然语言处理中很常见。一些例子包括语言建模见图 7-1),其中我们在每一步给定单词序列的情况下预测下一个单词;词性标注,我们预测每个词的语法词性;命名实体识别,我们预测每个单词是否是命名实体的一部分,例如PersonLocationProductOrganization;等等。有时,在 NLP 文献中,序列预测任务也被称为序列标记

虽然理论上我们可以使用第 6 章中介绍的 Elman 循环神经网络来完成序列预测任务,但它们无法很好地捕捉长期依赖关系,并且在实践中表现不佳。在本章中,我们会花一些时间了解为什么会这样,并了解一种称为门控网络的新型 RNN 架构。

我们还介绍了任务自然语言生成作为序列预测的一种应用,并探索了输出序列以某种方式受到约束的条件生成。

图 7-1。序列预测任务的两个示例: (a) 语言建模,其中任务是预测序列中的下一个单词;(b) 命名实体识别,旨在预测文本中实体字符串的边界及其类型。

Vanilla RNN(或 Elman RNN)的问题

甚至尽管第 6 章中讨论的 vanilla/Elman RNN非常适合建模序列,但它有两个问题使其不适用于许多任务:无法为长期预测保留信息,以及梯度稳定性。为了理解这两个问题,回想一下,RNN 的核心是使用前一个时间步的隐藏状态向量和当前时间步的输入向量来计算每个时间步的隐藏状态向量。正是这种核心计算使 RNN 如此强大,但它也产生了严重的数值问题。

Elman RNN 的第一个问题是难以保留远程信息。以第 6 章中的 RNN为例,在每个时间步,我们只是简单地更新了隐藏状态向量,而不管它是否有意义。因此,RNN 无法控制在隐藏状态中保留哪些值以及丢弃哪些值——这完全由输入决定。直觉上,这没有意义。RNN 需要某种方式来决定更新是否是可选的,或者更新是否发生,状态向量的数量和部分,等等。

Elman RNN 的第二个问题是它们倾向于导致梯度失控到零或无穷大。可能失控的不稳定梯度被称为消失的梯度爆炸梯度取决于梯度的绝对值缩小/增长的方向。非常大的梯度绝对值或非常小的(小于1)值会使优化过程不稳定(Hochreiter 等人,2001;Pascanu 等人,2013)。

在 vanilla RNN 中存在处理这些梯度问题的解决方案,例如使用整流线性单元 (ReLU)、梯度裁剪和仔细初始化。但是,所提出的解决方案中没有一个能像门控技术那样可靠。

门控作为 Vanilla RNN 挑战的解决方案

直观地理解门控,假设您要添加两个量ab ,但您想控制b中有多少进入总和。在数学上,您可以将总和a + b重写为:

a+λb

其中 λ 是 和 之间的01。如果 λ = 0,则b没有贡献,如果 λ = 1,则b贡献全部。以这种方式来看,您可以将 λ 解释为在控制进入总和的b数量时充当“开关”或“门” 。这就是门控机制背后的直觉。现在让我们重新审视 Elman RNN,看看如何将门控合并到普通 RNN 中以进行条件更新。如果先前的隐藏状态是t -1并且当前输入是t,Elman RNN 中的循环更新将如下所示:

ht=ht−1+F(ht−1,xt)

其中F是 RNN 的循环计算。显然,这是一个无条件的和,并且具有“Vanilla RNN(或 Elman RNN)的问题”中描述的弊端。现在想象一下,如果前一个例子中的 λ 不是一个常数,而是前一个隐藏状态向量t -1和当前输入t的函数,并且仍然产生了所需的门控行为;即 和 之间的01。使用这个门控函数,我们的 RNN 更新方程将如下所示:

ht=ht−1+λ(ht−1,xt)F(ht−1,xt)

现在很明显,函数 λ 控制了多少当前输入来更新状态t -1。此外,函数 λ 与上下文相关。这是所有门控网络背后的基本直觉。函数 λ 通常是一个 sigmoid 函数,我们从第 3 章中知道它会产生一个介于0和之间的值1

长短期记忆网络(LSTM;Hochreiter 和 Schmidhuber,1997)为例,这种基本直觉被仔细扩展,不仅包括条件更新,还包括有意忘记先前隐藏状态t -1中的值。这种“遗忘”是通过将先前的隐藏状态值t -1与另一个函数 μ 相乘而发生的,该函数也产生介于0和之间的值,1并且取决于当前输入:

ht=μ(ht−1,xt)ht−1+λ(ht−1,xt)F(ht−1,xt)

您可能已经猜到,μ 是另一个门控函数。在实际的 LSTM 描述中,这变得复杂,因为门控函数是参数化的,导致操作序列有点复杂(对于未启动的)。有了本节中的直觉,如果您想深入了解 LSTM 的更新机制,您现在就可以深入研究了。我们推荐Christopher Olah 的经典文章。我们将避免在本书中涉及任何内容,因为这些细节对于 LSTM 在 NLP 应用程序中的应用和使用并不是必不可少的。

LSTM 只是 RNN 的众多门控变体之一。另一个变得越来越流行的变体是 门控循环单元(GRU; Chung et al., 2015)。幸运的是,在 PyTorch 中,您可以简单地将nn.RNNornn.RNNCell替换为nn.LSTM或不更改nn.LSTMCell其他代码以切换到 LSTM(比对 GRU)!

门控机制是“The Problem with Vanilla RNNs (or Elman RNNs)”中列举的问题的有效解决方案。它不仅可以控制更新,还可以检查梯度问题并使训练相对容易。事不宜迟,我们将使用两个示例展示这些门控架构的实际应用。

示例:用于生成姓氏的字符 RNN

在这个例子中,1我们完成了一个简单的序列预测任务:使用 RNN 生成姓氏。在实践中,这意味着对于每个时间步,RNN 计算一个姓氏中可能字符集的概率分布。我们可以使用这些概率分布来优化网络以改进其预测(假设我们知道应该预测哪些字符),或者生成全新的姓氏。

尽管此任务的数据集已在前面的示例中使用并且看起来很熟悉,但每个数据样本为序列预测构建的方式存在一些差异。在描述了数据集和任务之后,我们概述了通过系统记账实现序列预测的支持数据结构。

然后我们介绍了两种生成姓氏的模型:无条件SurnameGenerationModel的和有条件的SurnameGenerationModel。无条件模型在不知道国籍的情况下预测姓氏字符的序列。相比之下,条件模型利用特定国籍嵌入作为 RNN 的初始隐藏状态,以允许模型对其序列的预测产生偏差。

SurnameDataset 类

第一的“示例:带有 MLP 的姓氏分类”中介绍,姓氏数据集是姓氏及其原籍国的集合。到目前为止,该数据集已用于分类任务——给定一个新姓氏,正确分类该姓氏起源于哪个国家。然而,在这个例子中,我们展示了如何使用数据集来训练一个模型,该模型可以为字符序列分配概率并生成新序列。

该类SurnameDataset与前几章基本相同:我们使用 Pandas DataFrame 加载数据集,并构造了一个矢量化器,它封装了手头模型和任务所需的令牌到整数的映射。为了适应任务的差异,该SurnameDataset.__getitem__()方法被修改为输出预测目标的整数序列,如示例 7-1所示。该方法引用Vectorizer用于计算用作输入的整数序列 (the from_vector) 和用作输出的整数序列 (the to_vector)。的实现vectorize()将在下一小节中描述。

例 7-1。序列预测任务的 SurnameDataset.__getitem__() 方法

class SurnameDataset(Dataset):@classmethoddef load_dataset_and_make_vectorizer(cls, surname_csv):"""加载数据集并从头开始制作一个新的矢量化器参数: surname_csv (str): 数据集的位置返回:SurnameDataset 的一个实例"""surname_df = pd.read_csv(surname_csv)return cls(surname_df, SurnameVectorizer.from_dataframe(surname_df))def __getitem__(self, index):"""PyTorch 数据集的主要入口点方法参数: index (int): 数据点的索引returns:保存数据点的字典: (x_data, y_target, class_index) """row = self._target_df.iloc[index]from_vector, to_vector = \self._vectorizer.vectorize(row.surname, self._max_seq_length)nationality_index = \self._vectorizer.nationality_vocab.lookup_token(row.nationality)return {'x_data': from_vector, 'y_target': to_vector, 'class_index': nationality_index}

矢量化数据结构

作为在前面的示例中,有三个主要的数据结构将每个姓氏的字符序列转换为其矢量化形式:SequenceVocabulary将单个标记映射到整数,SurnameVectorizer坐标整数映射,以及将's 结果DataLoader分组为小批量。SurnameVectorizer因为DataLoader在这个例子中实现和它的使用保持不变,我们将跳过它的实现细节。2

SurnameVectorizer 和 END-OF-SEQUENCE

对于序列预测任务,编写训练例程以期望两个整数序列,它们代表每个时间步的标记观察和标记目标。通常,我们只想预测我们正在训练的序列,例如本例中的姓氏。这意味着我们只有一个令牌序列可以使用,并通过交错单个序列来构建观察和目标。

将其转换为序列预测问题,每个标记都使用SequenceVocabulary. 然后,BEGIN-OF-SEQUENCE标记索引, 被附加到begin_seq_index序列的开头,END-OF-SEQUENCE标记索引end_seq_index, 被附加到序列的末尾。此时,每个数据点都是一个索引序列,并且具有相同的第一个和最后一个索引。为了创建训练例程所需的输入和输出序列,我们只需使用索引序列的两个切片:第一个切片包括除最后一个之外的所有标记索引,第二个切片包括除第一个之外的所有标记索引。当对齐和配对在一起时,序列是正确的输入输出索引。

至明确地说,我们展示了SurnameVectorizer.vectorize()示例7-2中的代码。第一步是将surname一串字符映射到indices表示这些字符的整数列表。然后,indices用序列的开始和结束索引包装:具体来说,begin_seq_index被添加到indices并附end_seq_index加到indices. 接下来,我们测试vector_length通常在运行时提供的 (尽管编写的代码允许任何长度的向量)。在训练期间,重要的是vector_length要提供,因为小批量是由堆叠的向量表示构成的。如果向量的长度不同,则它们不能堆叠在单个矩阵中。在测试 之后vector_length,创建了两个向量:from_vectorto_vector. 不包括最后一个索引的索引from_vector切片放在里面,不包括第一个索引的索引切片放在里面to_vector。每个向量的剩余位置都用mask_index. 将序列向右填充(或填充)很重要,因为空位置会改变输出向量,我们希望在看到序列之后发生这些变化。

示例 7-2。序列预测任务中 SurnameVectorizer.vectorize() 的代码

class SurnameVectorizer(object):""" 协调词汇并使用它们的矢量化器"""    def vectorize(self, surname, vector_length=-1):"""将姓氏向量化为观察向量并以为目标参数: surname (str): 被向量化的姓氏vector_length (int): 强制索引向量长度的参数返回:一个元组: (from_vector, to_vector) from_vector (numpy.ndarray): 观察向量to_vector (numpy.ndarray ): 目标预测向量"""indices = [self.char_vocab.begin_seq_index] indices.extend(self.char_vocab.lookup_token(token) for token in surname)indices.append(self.char_vocab.end_seq_index)if vector_length < 0:vector_length = len(indices) - 1from_vector = np.zeros(vector_length, dtype=np.int64)         from_indices = indices[:-1]from_vector[:len(from_indices)] = from_indicesfrom_vector[len(from_indices):] = self.char_vocab.mask_indexto_vector = np.empty(vector_length, dtype=np.int64)to_indices = indices[1:]to_vector[:len(to_indices)] = to_indicesto_vector[len(to_indices):] = self.char_vocab.mask_indexreturn from_vector, to_vector@classmethoddef from_dataframe(cls, surname_df):"""从数据集数据帧实例化矢量化器参数: surname_df (pandas.DataFrame): 姓氏数据集返回:SurnameVectorizer 的一个实例"""char_vocab = SequenceVocabulary()nationality_vocab = Vocabulary()for index, row in surname_df.iterrows():for char in row.surname:char_vocab.add_token(char)nationality_vocab.add_token(row.nationality)return cls(char_vocab, nationality_vocab)

模型 1:无条件姓氏生成模型

两个模型中的第一个是无条件的:它在生成姓氏之前不观察国籍。在实践中,无条件意味着 GRU 不会将其计算偏向于任何国籍。在下一个示例(示例 7-4)中,计算偏差是通过初始隐藏向量引入的​​。在此示例中,我们使用所有0s 的向量,以便初始隐藏状态向量对计算没有贡献。3

一般来说,SurnameGenerationModel例 7-3)嵌入字符索引,使用 GRU 计算它们的顺序状态,并使用Linear层计算标记预测的概率。更明确地说,无条件SurnameGenerationModel从初始化一个Embedding层、一个 GRU 和一个Linear层开始。类似于第 6 章的序列模型,一个整数矩阵被输入到模型中。我们使用 PyTorchEmbedding实例 thechar_embedding将整数转换为三维张量(每个批次项目的向量序列)。这个张量被传递给 GRU,它为每个序列中的每个位置计算一个状态向量。

示例 7-3。无条件姓氏生成模型

class SurnameGenerationModel(nn.Module):def __init__(self, char_embedding_size, char_vocab_size, rnn_hidden_size, batch_first=True, padding_idx=0, dropout_p=0.5):"""参数: char_embedding_size (int): 字符嵌入的大小char_vocab_size ( int): 要嵌入的字符数rnn_hidden_​​size (int): RNN 的隐藏状态的大小batch_first (bool): 告知输入张量在第 0 维上是否会有批或序列padding_idx (int): 的索引张量填充;参见 torch.nn.Embeddingdropout_p (float): 使用dropout方法"""super(SurnameGenerationModel, self).__init__()self.char_emb = nn.Embedding(num_embeddings=char_vocab_size,embedding_dim=char_embedding_size,padding_idx=padding_idx)self.rnn = nn.GRU(input_size=char_embedding_size, hidden_size=rnn_hidden_size,batch_first=batch_first)self.fc = nn.Linear(in_features=rnn_hidden_size, out_features=char_vocab_size)self._dropout_p = dropout_pdef forward(self, x_in, apply_softmax=False):"""模型的前向传递参数: x_in (torch.Tensor): 一个输入数据张量x_in.shape应该是(batch, input_dim) apply_softmax (bool):在训练期间,softmax 激活的标志应为 False返回:生成的张量。tensor.shape 应为 (batch, output_dim)。"""x_embedded = self.char_emb(x_in)y_out, _ = self.rnn(x_embedded)batch_size, seq_size, feat_size = y_out.shapey_out = y_out.contiguous().view(batch_size * seq_size, feat_size)y_out = self.fc(F.dropout(y_out, p=self._dropout_p))if apply_softmax:y_out = F.softmax(y_out, dim=1)new_feat_size = y_out.shape[-1]y_out = y_out.view(batch_size, seq_size, new_feat_size)return y_out

第 6 章的序列分类任务和本章的序列预测任务之间的主要区别在于RNN 计算的状态向量是如何处理的。在第 6 章中,我们检索每个批次索引的单个向量,并使用这些单个向量进行预测。在这个例子中,我们将三维张量重塑为二维张量(矩阵),以便行维度代表每个样本(批次和序列索引)。使用这个矩阵和Linear层,我们计算每个样本的预测向量。我们通过将矩阵重新整形为三维张量来完成计算。因为通过整形操作保留了排序信息,所以每个批次和序列索引仍然在相同的位置。我们需要重塑的原因是因为该Linear层需要一个矩阵作为输入。

模型 2:条件姓氏生成模型

第二个模型考虑了要生成的姓氏的国籍。在实践中,这意味着有一些机制允许模型相对于特定姓氏来偏向其行为。在这个例子中,我们通过将每个国籍嵌入为隐藏状态大小的向量来参数化 RNN 的初始隐藏状态。这意味着当模型调整其参数时,它也会调整嵌入矩阵中的值,从而使预测偏向于对其特定国籍和姓氏规律更敏感。例如,爱尔兰国籍向量偏向于起始序列“Mc”和“O”。

示例 7-4显示了条件模型中的差异。具体来说,引入了一个额外Embedding的方法来将国籍索引映射到与 RNN 的隐藏层大小相同的向量。然后,在前向函数中,国籍索引被嵌入并简单地作为 RNN 的初始隐藏层传入。虽然这是对第一个模型的非常简单的修改,但它对让 RNN 根据生成的姓氏的国籍改变其行为具有深远的影响。

示例 7-4。条件姓氏生成模型

class SurnameGenerationModel(nn.Module):def __init__(self, char_embedding_size, char_vocab_size, num_nationalities,rnn_hidden_size, batch_first=True, padding_idx=0, dropout_p=0.5):# ...self.nation_embedding = nn.Embedding(embedding_dim=rnn_hidden_size, num_embeddings=num_nationalities)def forward(self, x_in, nationality_index, apply_softmax=False):# ...x_embedded = self.char_embedding(x_in)# hidden_size: (num_layers * num_directions, batch_size, rnn_hidden_size)nationality_embedded = self.nation_emb(nationality_index).unsqueeze(0)y_out, _ = self.rnn(x_embedded, nationality_embedded)# ...

训练程序和结果

在这个例子中,我们介绍了预测字符序列以生成姓氏的任务。尽管在许多方面实现细节和训练例程与第 6 章中的序列分类示例相似,但还是有一些主要区别。在本节中,我们将重点介绍差异、使用的超参数和结果。

与之前的示例相比,在此示例中计算损失需要进行两个更改,因为我们在序列中的每个时间步进行预测。首先,我们将三维张量4重塑为二维张量(矩阵)以满足计算约束。其次,我们将允许可变长度序列的掩蔽索引与损失函数协调,以便损失在其计算中不使用掩蔽位置。

我们通过使用示例 7-5中显示的代码片段来处理这两个问题——三维张量和可变长度序列。首先,将预测和目标标准化为损失函数期望的大小(预测为二维,目标为一维)。现在,每一行代表一个样本:一个序列中的一个时间步长。那么,交叉熵损失为ignore_index设置为mask_index. 这具有损失函数的效果,它忽略了目标中匹配的任何位置ignore_index

示例 7-5。处理三维张量和序列范围的损失计算

def normalize_sizes(y_pred, y_true):""归一化张量大小参数: y_pred (torch.Tensor): 模型的输出如果是3维张量,则重塑为矩阵y_true (torch.Tensor): 目标预测"""if len(y_pred.size()) == 3:y_pred = y_pred.contiguous().view(-1, y_pred.size(2))if len(y_true.size()) == 2:y_true = y_true.contiguous().view(-1)return y_pred, y_truedef sequence_loss(y_pred, y_true, mask_index):y_pred, y_true = normalize_sizes(y_pred, y_true)return F.cross_entropy(y_pred, y_true, ignore_index=mask_index)

使用这个修改损失计算后,我们构建了一个与本书中所有其他示例相似的训练例程。它开始于一次迭代一个小批量的训练数据集。对于每个小批量,模型的输出是根据输入计算的。因为我们在每个时间步执行预测,所以模型的输出是一个三维张量。使用前面描述sequence_loss()的和优化器,计算模型预测的误差信号并用于更新模型参数。

大部分型号超参数由字符词汇的大小决定。该大小是可以作为模型输入观察到的离散标记的数量以及每个时间步的输出分类中的类数。剩下的模型超参数是字符嵌入的大小和内部 RNN 隐藏状态的大小。例 7-6给出了这些超参数和训练选项。

示例 7-6。姓氏生成的超参数

args = Namespace(# 数据和路径信息surname_csv="data/surnames/surnames_with_splits.csv",vectorizer_file="vectorizer.json",model_state_file="model.pth",save_dir="model_storage/ch7/model1_unconditioned_surname_generation",# 或: save_dir="model_storage/ch7/model2_condition_surname_generation", # 模型超参数char_embedding_size=32,rnn_hidden_size=32,# Training hyperparametersseed=1337,learning_rate=0.001,batch_size=128,num_epochs=100,early_stopping_criteria=5,# 运行时选项省略空间
)

尽管预测的每个字符的准确性是衡量模型性能的指标,但在此示例中,最好通过检查模型将生成什么样的姓氏来进行定性评估。为此,我们在步骤的修改版本上编写一个新循环在forward()计算每个时间步的预测并将这些预测用作下一个时间步的输入的方法中。我们展示了示例 7-7中的代码。模型在每个时间步的输出是一个预测向量,它变成一个使用 softmax 函数的概率分布。使用概率分布,我们利用torch.multinomial()采样函数,它以与索引概率成比例的速率选择索引。抽样是一个随机过程,每次产生不同的输出。

示例 7-8。将采样索引映射到姓氏字符串

def decode_samples(sampled_indices, vectorizer):"""将索引转换为姓氏的字符串形式Args: sampled_indices (torch.Tensor): 来自`sample_from_model`的索引向量化器(SurnameVectorizer): 对应的向量化器"""decoded_surnames = []vocab = vectorizer.char_vocabfor sample_index in range(sampled_indices.shape[0]):surname = ""for time_step in range(sampled_indices.shape[1]):sample_item = sampled_indices[sample_index, time_step].item()if sample_item == vocab.begin_seq_index:continueelif sample_item == vocab.end_seq_index:breakelse:surname += vocab.lookup_index(sample_item)decoded_surnames.append(surname)return decoded_surnames

使用这些函数,您可以检查模型的输出,如示例 7-9所示,以了解模型是否正在学习生成合理的姓氏。我们可以从检查输出中学到什么?我们可以看到,虽然这些姓氏似乎遵循了几种形态模式,但这些名字并没有明显地表现出来自一个民族或另一个民族。一种可能性是学习姓氏的一般模型会混淆不同国籍之间的字符分布。有条件SurnameGenerationModel的旨在处理这种情况。

示例 7-9。从无条件模型中采样

Input[0]
samples = sample_from_model(uncondition_model, vectorizer, num_samples=10) 
decode_samples(samples, vectorizer)
Output[0]
['Aqtaliby','Yomaghev','Mauasheev','Unander','Virrovo','NInev','Bukhumohe','Can','Rati','Jzirmar']

对于有条件的SurnameGenerationModel,我们修改sample_from_model()函数以接受国籍指数列表而不是指定数量的样本。在示例 7-10中,修改后的函数使用带有国籍嵌入的国籍索引来构建 GRU 的初始隐藏状态。之后,采样过程与无条件模型完全相同。

示例 7-10。从序列模型中采样

def sample_from_model(model, vectorizer, nationalities, sample_size=20, temperature=1.0):"""从模型中采样一个索引序列参数: model (SurnameGenerationModel): 训练好的模型vectorizer (SurnameVectorizer): 对应的vectorizer nationalities (list ): 代表国籍的整数列表sample_size (int): 样本的最大长度temperature (float): 强调或拉平分布0.0 < temperature < 1.0 将使其更峰值温度 > 1.0 将使其更均匀返回: indices ( torch.Tensor): 索引矩阵shape = (num_samples, sample_size) """num_samples = len(nationalities)begin_seq_index = [vectorizer.char_vocab.begin_seq_index for _ in range(num_samples)]begin_seq_index = torch.tensor(begin_seq_index, dtype=torch.int64).unsqueeze(dim=1)indices = [begin_seq_index]nationality_indices = torch.tensor(nationalities, dtype=torch.int64).unsqueeze(dim=0)h_t = model.nation_emb(nationality_indices)for time_step in range(sample_size):x_t = indices[time_step]x_emb_t = model.char_emb(x_t)rnn_out_t, h_t = model.rnn(x_emb_t, h_t)prediction_vector = model.fc(rnn_out_t.squeeze(dim=1))probability_vector = F.softmax(prediction_vector / temperature, dim=1)indices.append(torch.multinomial(probability_vector, num_samples=1))indices = torch.stack(indices).squeeze().permute(1, 0)return indices

使用条件向量进行采样的有用性意味着我们对生成的输出有影响。在示例 7-11中,我们迭代了国籍指数并从每个指数中取样。为了节省空间,我们只展示了一些输出。从这些输出中,我们可以看到该模型确实采用了一些姓氏的拼写模式。

示例 7-11。从有条件的 SurnameGenerationModel 采样(未显示所有输出)

Input[0]
for index in range(len(vectorizer.nationality_vocab)):nationality = vectorizer.nationality_vocab.lookup_index(index)print("Sampled for {}: ".format(nationality))sampled_indices = sample_from_model(model=conditioned_model,vectorizer=vectorizer,  nationalities=[index] * 3, temperature=0.7)for sampled_surname in decode_samples(sampled_indices, vectorizer):print("-  " + sampled_surname)
Output[0]
Sampled for Arabic: 
-  Khatso
-  Salbwa
-  Gadi
Sampled for Chinese: 
-  Lie
-  Puh
-  Pian
Sampled for German: 
-  Lenger
-  Schanger
-  Schumper
Sampled for Irish: 
-  Mcochin
-  Corran
-  O'Baintin
Sampled for Russian: 
-  Mahghatsunkov
-  Juhin
-  Karkovin
Sampled for Vietnamese: 
-  Lo
-  Tham
-  Tou

训练序列模型的提示和技巧

序列模型的训练可能具有挑战性,并且在此过程中会出现许多问题。在这里,我们总结了一些我们发现在我们的工作中有用的技巧和窍门,以及其他人在文献中报道的技巧。

如果可能,请使用门控变体

门控架构通过解决非门控变体的许多数值稳定性问题来简化训练。

如果可能的话,更喜欢 GRUs 而不是 LSTMs

GRU 提供性能几乎与 LSTM 相当,并且使用的参数和计算资源要少得多。幸运的是,从 PyTorch 的角度来看,使用 GRU 而不是 LSTM 只需要使用不同的Module类。

使用 Adam 作为优化器

在第6、7和8章中,我们只使用Adam 作为优化器,有充分的理由:它是可靠的,并且通常比替代方案收敛得更快。对于序列模型尤其如此。如果由于某种原因你的模型没有与 Adam 收敛,切换到随机梯度下降可能会有所帮助。

渐变剪裁

如果你注意应用这些章节中学到的概念时的数值错误,在训练期间使用代码绘制梯度值。知道范围后,剪裁任何异常值。这将确保更顺畅的训练。在 PyTorch 中有一个有用的实用程序clip_grad_norm()可以为您执行此操作,如示例 7-12所示。一般来说,你应该养成剪裁渐变的习惯。

示例 7-12。在 PyTorch 中应用渐变裁剪

# 定义你的序列模型
model = ..
# 定义损失函数
loss_function = ..# 训练循环
for _ in ...:...model.zero_grad()output, hidden = model(data, hidden)loss = loss_function(output, targets)loss.backward()torch.nn.utils.clip_grad_norm(model.parameters(), 0.25)...

提前停止

序列模型,很容易过拟合。我们建议您尽早停止训练程序,当在开发集上测量的评估错误开始上升。

在第 8 章中,我们继续讨论序列模型,探索如何使用序列到序列模型预测和生成长度与输入不同的序列,并考虑其他变体。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10024.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自然语言处理 基于预训练模型的方法 代码解读 [ section 4.1.6, page 77 ]

自然语言处理 基于预训练模型的方法 代码解读 [ section 4.1.6, page 77 ] 自定义神经网络模型的例子&#xff1a;一个简单的多层感知器模型MLP 文件名为 mlp.py # Defined in Section 4.1.6 # Page 77 # 自定义神经网络模型的第一个例子&#xff08;The first example of c…

自然语言处理(基于预训练模型)02NLTK工具集

NLTK是对英文文本数据进行处理的常用工具 1 停用词 1.1 查看停用词 import nltk nltk.download(stopwords) from nltk.corpus import stopwords print(stopwords.words(english)) 2 常用语料库 2.1 未标注语料库 2.1.1 找出古腾堡语料库中的emma原文 gutenberg下载地址&am…

【NLP】介绍几个语言生成的预训练模型

作者 | Chilia 哥伦比亚大学 nlp搜索推荐 整理 | NewBeeNLP 大家好&#xff0c;这里是NewBeeNLP。本篇介绍四个为语言生成设计的预训练模型 -- BART&#xff0c;MASS&#xff0c;PEGASUS&#xff0c;UniLM。其中前三种方法都使用了Transformer Encoder-Dec…

使用ChatGPT+Xmind一键生成思维导图,简直泰裤辣

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

【使用心得】最新版ChatGPT查资料

最新版ChatGPT是一款非常实用的软件&#xff0c;它提供了广泛的辅助工具&#xff0c;可帮助我在各个领域提升工作效率。使用体验更加流畅&#xff0c;界面也相对更加美观。 首先&#xff0c;最新版ChatGPT加强了语言翻译功能&#xff0c;并进一步完善了交互方式&#xff0c;使…

ChatGPT4和低代码来临,程序员面临下岗?

一个网友吐槽道&#xff1a; “ 建站出来了&#xff0c;你们说程序员会失业。 低代码出来了&#xff0c;你们说程序员会失业。 Copilot出来了&#xff0c;你们说程序员会失业。 Chatgpt出来了&#xff0c;你们说程序员会失业 虽然这只是网友的吐槽&#xff0c;但却引起了小编…

ChatGPT时代的得意忘言

David S. Soriano, CC BY-SA 4.0 via Wikimedia Commons 导读&#xff1a; 以ChatGPT为代表的新的人工智能语言模型&#xff0c;具有划时代的意义。一个值得思考的问题是&#xff0c;人工智能具备的测算能力&#xff0c;无法完全等同于人类的判断力。 在《测算与判断&#xff1…

ChatGPT提示词工程进阶教学

ChatGPT提示词工程 1 两种大型语言模型LLM1.1 基础大模型&#xff08;base LLM&#xff09;1.2 指令调优大模型(Instruction Tuned LLM) 2 如何更清晰、具体地书写提示词2.1 在提示词中使用“定界符”2.2 向模型请求结构化的输出2.3 要求模型检查任务条件是否满足2.4 输入多范例…

【花雕学AI】ChatGPT的四大语言处理神器:文本生成、问答、创意生成和内容优化的技巧和实例

引言&#xff1a;ChatGPT是一个人工智能聊天机器人&#xff0c;它可以理解和交流多种语言&#xff0c;例如中文、英文、日文、西班牙语、法语、德语等。它是由OpenAI开发的&#xff0c;基于GPT-3.5和GPT-4这两个大型语言模型。它不仅可以与用户进行对话&#xff0c;还可以根据用…

chatgpt赋能python:Python文本清洗:从混乱到整洁

Python 文本清洗&#xff1a;从混乱到整洁 如果你曾经在处理文本数据时花费了大量时间将信息从混乱的文本中取出来&#xff0c;那么你应该考虑使用 Python 进行文本清洗。Python 是一种易于学习和使用的编程语言&#xff0c;可用于自动化文本清洗流程&#xff0c;实现高效准确…

难逃 AI 的法眼:ChatGPT 文本检测器(ERNIE 文本分类)

★★★ 本文源自AlStudio社区精品项目&#xff0c;【点击此处】查看更多精品内容 >>> 参考项目地址&#xff1a;https://github.com/Hello-SimpleAI/chatgpt-comparison-detection 本项目 Demo 地址&#xff1a;https://aistudio.baidu.com/aistudio/projectdetail…

chatgpt赋能python:Python对文本进行分词

Python对文本进行分词 在自然语言处理&#xff08;NLP&#xff09;领域中&#xff0c;对文本进行分词是一个重要的预处理步骤。分词的目的是将一段文本切割成由词语组成的序列&#xff0c;为后续的处理提供基础。 Python在NLP任务中是广泛使用的编程语言之一&#xff0c;有许…

chatgpt赋能python:Python中文文本预处理

Python中文文本预处理 Python作为一门广泛应用于数据分析、机器学习和人工智能的编程语言&#xff0c;在处理中文文本方面也有不可忽视的优势。但是由于中文特殊性&#xff0c;中文文本预处理也有独特的需求。本文将介绍在Python中进行中文文本预处理的常见操作。 分词 分词…

DeepSpeed Chat: 一键式RLHF训练,让你的类ChatGPT千亿大模型提速省钱15倍

DeepSpeed is a deep learning optimization library that makes distributed training and inference easy, efficient, and effective. www.deepspeed.ai/ DeepSpeed Integration DeepSpeed Chat: 一键式RLHF训练,让你的类ChatGPT千亿大模型提速省钱15倍

使用EasyExcel导入导出Excel

在管理一个系统时&#xff0c;总会有许多的数据&#xff0c;为了方便浏览查看数据&#xff0c;系统总会提供「导出Excel」的功能&#xff1b;有导出就有导入&#xff0c;在要向数据库中插入大量的数据时&#xff0c;我们向程序提供准备好的 Excel&#xff0c;然后程序读取表格内…

EasyExcel导入导出,处理数据

1.导出模块,导出中有中文文件名称&#xff0c;设置格式 /*** 以流方式响应回给客户端&#xff0c;返回值类型设置成void** param response 输出excel表格&#xff0c;让用户下载*/PostMapping("/exportExcel")public void exportExcel(ZqRewardProjectContract zqRew…

QT常用表格导出为Excel以及Excel导入表格

表格导出为Excel 注意&#xff1a;演示所用到的软件为Qt5.14.2&#xff0c;编译器为MinGW 64-bit&#xff0c;电脑必须装有office所用的类为 QAxObject&#xff0c;QAxObject可以实例化为一个空对象&#xff0c;使用它应该封装的COM对象的名称&#xff0c;或者使用一个指向表示…

获取微信的聊天记录导出为Excel

获取微信的聊天记录导出为Excel ios端 1.工具2.步骤 1.工具 iTunes楼月免费iTunes备份管理器DB Browser for SQLitepython 2.步骤 通过iTunes备份ipone中的数据到电脑上&#xff0c; 打开楼月免费iTunes备份管理器选择备份的记录 导出 微信个人信息中的DB文件夹 使用 DB Br…

Excel表格的导入导出——EasyExcel

参考视频 csdn参考地址 一、导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version> </dependency>二、实体类 方式一&#xff1a;Excel Property&#xff08;&…

Easyexcel导入导出多个sheet

EasyExcel对于导入导出的操作十分简洁&#xff0c;记录一下多个sheet且内容不一致的导入导出。 引入 easyExcel依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.1</version></d…