面向自然语言处理的迁移学习(一)(3)https://developer.aliyun.com/article/1519832
3.2.2 双向编码器表示来自转换(BERT)
双向编码器表示来自变换器(BERT)模型也是以流行的Sesame Street角色命名的,以向 ELMo 开始的趋势致敬。在撰写本文时,其变体在将预训练语言模型知识转移到下游自然语言处理任务方面取得了一些最佳性能。该模型同样被训练来预测词语序列中的词语,尽管确切的masking过程略有不同,将在本书后面详细讨论。它也可以在非常大的语料库上以无监督的方式进行,并且生成的权重同样适用于各种其他自然语言处理任务。可以说,要熟悉自然语言处理中的迁移学习,熟悉 BERT 也是不可或缺的。
就像我们对 ELMo 所做的那样,在本节中我们将避免完全详细讨论这个深度学习模型的架构——我们将在后面的章节中涵盖这个话题。在这里提一下,模型利用字符级卷积来构建词元的初步嵌入,然后是基于变换器的编码器,其中包含自注意层,为模型提供周围单词的上下文。变换器在功能上取代了 ELMo 所采用的双向 LSTM 的作用。回顾第一章中,变换器相对于 LSTM 在训练可扩展性方面具有一些优势,我们可以看到这个模型背后的一些动机。同样,我们将使用带有 TensorFlow 后端的 Keras 来构建我们的模型。
简要介绍了 BERT 之后,让我们继续为两个运行示例数据集中的每一个训练它。BERT 模型也可以通过 TensorFlow Hub 获得。为了使 hub 模型能够被 Keras 使用,我们同样定义了一个自定义 Keras 层,以正确的格式实例化它,如下一个清单所示。
使用自定义 Keras 层实例化 TensorFlow Hub BERT
import tensorflow as tf import tensorflow_hub as hub from bert.tokenization import FullTokenizer from tensorflow.keras import backend as K # Initialize session sess = tf.Session() class BertLayer(tf.keras.layers.Layer): def __init__( self, n_fine_tune_layers=10, ❶ pooling="mean", ❷ bert_path="https:/ /tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1",❸ **kwargs, ): self.n_fine_tune_layers = n_fine_tune_layers self.trainable = True self.output_size = 768 ❹ self.pooling = pooling self.bert_path = bert_path super(BertLayer, self).__init__(**kwargs) def build(self, input_shape): self.bert = hub.Module( self.bert_path, trainable=self.trainable, name=f"{self.name}_module" ) trainable_vars = self.bert.variables ❺ if self.pooling == "first": trainable_vars = [var for var in trainable_vars if not "/cls/" in var.name] trainable_layers = ["pooler/dense"] elif self.pooling == "mean": trainable_vars = [ var for var in trainable_vars if not "/cls/" in var.name and not "/pooler/" in var.name ] trainable_layers = [] else: raise NameError("Undefined pooling type") for i in range(self.n_fine_tune_layers): ❻ trainable_layers.append(f"encoder/layer_{str(11 - i)}") trainable_vars = [ var for var in trainable_vars if any([l in var.name for l in trainable_layers]) ] for var in trainable_vars: ❼ self._trainable_weights.append(var) for var in self.bert.variables: if var not in self._trainable_weights: self._non_trainable_weights.append(var) super(BertLayer, self).build(input_shape) def call(self, inputs): inputs = [K.cast(x, dtype="int32") for x in inputs] input_ids, input_mask, segment_ids = inputs bert_inputs = dict( input_ids=input_ids, input_mask=input_mask, segment_ids=segment_ids ❽ ) if self.pooling == "first": pooled = self.bert(inputs=bert_inputs, signature="tokens", as_dict=True)[ "pooled_output" ] elif self.pooling == "mean": result = self.bert(inputs=bert_inputs, signature="tokens", as_dict=True)[ "sequence_output" ] mul_mask = lambda x, m: x * tf.expand_dims(m, axis=-1) ❾ masked_reduce_mean = lambda x, m: tf.reduce_sum(mul_mask(x, m), axis=1) / ( tf.reduce_sum(m, axis=1, keepdims=True) + 1e-10) input_mask = tf.cast(input_mask, tf.float32) pooled = masked_reduce_mean(result, input_mask) else: raise NameError("Undefined pooling type") return pooled def compute_output_shape(self, input_shape): return (input_shape[0], self.output_size)
❶ 默认要解冻的顶层数量进行训练
❷ 正则化类型的选择
❸ 要使用的预训练模型;这是模型的大型、不区分大小写的原始版本。
❹ BERT 嵌入维度,即生成的输出语义向量的大小
❺ 移除未使用的层
❻ 强制执行要微调的解冻层的数量
❼ 可训练权重
❽ 输入到 BERT 采用非常特定的三元组形式;我们将在下一个清单中展示如何生成它。
❾ BERT“masks”一些词语,然后尝试将它们预测为学习目标。
类似于我们在前一小节为 ELMo 所做的工作,我们对前几节的数据执行一系列类似的后处理步骤,将其放入 BERT 模型所需的格式中。除了我们在列表 3.5 中所做的将词袋标记表示连接成字符串列表之外,我们随后需要将每个连接的字符串转换为三个数组——输入 ID、输入掩码 和 段 ID——然后再将它们馈送到 BERT 模型中。这样做的代码在列表 3.8 中显示。将数据转换为正确格式后,我们使用同一列表 3.8 中的剩余代码构建和训练 Keras BERT TensorFlow Hub 模型。
列表 3.8 将数据转换为 BERT 所期望的格式,构建和训练模型
def build_model(max_seq_length): ❶ in_id = tf.keras.layers.Input(shape=(max_seq_length,), name="input_ids") in_mask = tf.keras.layers.Input(shape=(max_seq_length,), name="input_masks") in_segment = tf.keras.layers.Input(shape=(max_seq_length,), name="segment_ids") bert_inputs = [in_id, in_mask, in_segment] bert_output = BertLayer(n_fine_tune_layers=0)(bert_inputs) ❷ dense = tf.keras.layers.Dense(256, activation="relu")(bert_output) pred = tf.keras.layers.Dense(1, activation="sigmoid")(dense) model = tf.keras.models.Model(inputs=bert_inputs, outputs=pred) model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"]) model.summary() return model def initialize_vars(sess): ❸ sess.run(tf.local_variables_initializer()) sess.run(tf.global_variables_initializer()) sess.run(tf.tables_initializer()) K.set_session(sess) bert_path = "https:/ /tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1" tokenizer = create_tokenizer_from_hub_module(bert_path) ❹ train_examples = convert_text_to_examples(train_x, train_y) ❺ test_examples = convert_text_to_examples(test_x, test_y) # Convert to features (train_input_ids,train_input_masks,train_segment_ids,train_labels) = ❻ convert_examples_to_features(tokenizer, train_examples, ❻ max_seq_length=maxtokens) ❻ (test_input_ids,test_input_masks,test_segment_ids,test_labels) = convert_examples_to_features(tokenizer, test_examples, max_seq_length=maxtokens) model = build_model(maxtokens) ❼ initialize_vars(sess) ❽ history = model.fit([train_input_ids, train_input_masks, train_segment_ids],❾ train_labels,validation_data=([test_input_ids, test_input_masks, test_segment_ids],test_labels), epochs=5, batch_size=32)
❶ 用于构建模型的函数
❷ 我们不重新训练任何 BERT 层,而是将预训练模型用作嵌入,并在其上重新训练一些新层。
❸ Vanilla TensorFlow 初始化调用
❹ 使用 BERT 源代码库中的函数创建兼容的分词器
❺ 使用 BERT 源代码库中的函数将数据转换为“InputExample”格式
❻ 使用 BERT 源代码库中的函数将 InputExample 格式转换为最终的 BERT 输入格式
❼ 构建模型
❽ 实例化变量
❾ 训练模型
类似于我们在前一小节中构建的 ELMo 模型,我们在预训练模型之上放置了一对层,并且仅对这些层进行训练,这大约有 20 万个参数。通过将超参数设置为与之前的所有方法相当的值,我们在电子邮件和电影评论分类问题上分别获得了约为 98.3% 和 71% 的验证准确率(在五个时期内)。
3.3 优化性能
在查看本章前几节以及上一章的各种算法的性能结果时,我们可能会立即得出关于我们研究的每个问题中哪种算法表现最佳的结论。例如,我们可能会得出结论,对于电子邮件分类问题,BERT 和逻辑回归是最佳算法,准确率约为 98%,而 ELMo 的准确率也不远,然后是基于决策树的方法和 SVM 排在最后。另一方面,对于 IMDB 电影评论分类问题,BERT 看起来是赢家,性能约为 71%,其次是 ELMo,然后才是逻辑回归。
但是我们必须记住,我们只有在最初评估算法时才能确定这一点是真实的——Nsamp
=1000
,maxtokens
=50
,maxtokenlen
=20
——以及任何特定于算法的默认参数值。要有信心地作出一般性的陈述,我们需要更彻底地探索超参数空间,通过在许多超参数设置下评估所有算法的性能,这个过程通常称为超参数调优或优化。也许通过这个过程找到的每个算法的最佳性能会改变它们的性能排名,而且一般来说,这将帮助我们为我们感兴趣的问题获得更好的准确性。
3.3.1 手动超参数调整
超参数调优通常最初是通过直觉进行手动操作的。我们在这里描述了这样一种方法,用于超参数Nsamp
、maxtokens
和maxtokenlen
,这些超参数在所有算法中都是通用的。
让我们首先假设初始训练数据量——比如Nsamp
=1000——就是我们拥有的全部数据。我们假设,如果我们增加每个文档中的数据令牌数量——maxtokens
——并增加任何此类令牌的最大长度——maxtokenlen
——我们将能够增加用于做出分类决策的信号量,从而提高结果的准确性。
对于电子邮件分类问题,我们首先将这两个值从分别为 50 和 20 的值增加到 100。对于逻辑回归(LR)、支持向量机(SVM)、随机森林(RF)、梯度提升机(GBM)、ELMo 和 BERT 执行此操作的准确性结果显示在表 3.1 的第二行。此外,我们将maxtokens
增加到 200,以得到表 3.1 的第三行的结果。
表 3.1 比较了手动调整过程中探索的电子邮件分类示例的不同通用超参数设置下算法的准确性
通用超参数设置 | LR | SVM | RF | GBM | ELMo | BERT |
Nsamp = 1000 maxtokens = 50 maxtokenlen = 20 | 97.7% | 70.2% | 94.5% | 94.2% | 97.3% | 98.3% |
Nsamp = 1000 maxtokens = 100 maxtokenlen = 100 | 99.2% | 72.3% | 97.2% | 97.3% | 98.2% | 98.8% |
Nsamp = 1000, maxtokens = 200, maxtokenlen = 100 | 98.7% | 90.0% | 97.7% | 97.2% | 99.7% | 98.8% |
根据这个结果,我们可以看到,虽然 SVM 显然是这个问题中表现最差的分类器,但 logistic 回归、ELMo 和 BERT 几乎可以达到完美的性能。还要注意,ELMo 在更多信号存在时表现最好——这是我们在没有优化步骤的情况下可能会错过的东西。但是,logistic 回归的简单性和速度可能导致它被选为此电子邮件分类问题的生产中首选的分类器。
现在,我们对 IMDB 电影评论分类问题进行了类似的超参数测试步骤。我们首先将maxtokens
和maxtokenlen
都增加到 100,然后将maxtokens
进一步增加到 200。得到的算法性能列在表 3.2 中,同时列出了初始超参数设置时的性能。
表 3.2 IMDB 电影评论分类示例中手动调整过程中探索的不同通用超参数设置的算法准确度比较
通用超参数设置 | 逻辑回归 | 支持向量机 | 随机森林 | 梯度提升机 | ELMo | BERT |
Nsamp = 1000 maxtokens = 50 maxtokenlen = 20 | 69.1% | 66.0% | 63.9% | 67.0% | 69.7% | 71.0% |
Nsamp = 1000 maxtokens = 100 maxtokenlen = 100 | 74.3% | 72.5% | 70.0% | 72.0% | 75.2% | 79.1% |
Nsamp = 1000 maxtokens = 200 maxtokenlen = 100 | 79.0% | 78.3% | 67.2% | 77.5% | 77.7% | 81.0% |
对于这个问题,BERT 似乎是最佳模型,其次是 ELMo 和逻辑回归。注意,这个问题有更多的改进空间,这与我们早期观察到的这个问题比电子邮件分类问题更难的观察一致。这使我们假设,预训练知识转移对更难的问题有更大的影响,这是直观的。这个概念也符合一般建议,即在有大量标记数据可用时,神经网络模型可能优于其他方法,假设要解决的问题足够复杂,需要额外的数据。
3.3.2 系统化超参数调整
存在一些工具用于对超参数范围进行更系统化和全面的搜索。这些包括 Python 方法GridSearchCV
,它对指定的参数网格执行全面搜索,以及HyperOpt
,它在参数范围上进行随机搜索。在这里,我们提供了使用GridSearchCV
来调整所选算法的代码,作为一个说明性示例。请注意,在这个练习中,我们只调整了一些特定于算法的内部超参数,而将上一小节中我们调整的通用超参数固定,以简化说明。
我们选择使用初始通用超参数设置的 RF 进行电子邮件分类作为我们的说明性示例。之所以做出这个选择,是因为对于这个问题的每次拟合大约需要一秒钟,由于网格搜索将执行大量的拟合,这个例子可以快速执行,以便读者获得最大的学习价值。
我们首先导入所需方法,并检查 RF 超参数可用于调整如下:
from sklearn.model_selection import GridSearchCV ❶ print("Available hyper-parameters for systematic tuning available with RF:") print(clf.get_params()) ❷
❶ GridSearchCV scikit-learn 导入语句
❷ clf 是列表 2.13 中的 RF 分类器。
这产生了以下输出:
{'bootstrap': True, 'class_weight': None, 'criterion': 'gini', 'max_depth': None, 'max_features': 'auto', 'max_leaf_nodes': None, 'min_impurity_decrease': 0.0, 'min_impurity_split': None, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_estimators': 10, 'n_jobs': 1, 'oob_score': False, 'random_state': 0, 'verbose': 0, 'warm_start': False}
我们选择了其中三个超参数进行搜索,并为每个参数指定了三个值,如下所示:
param_grid = { 'min_samples_leaf': [1, 2, 3], 'min_samples_split': [2, 6, 10], 'n_estimators': [10, 100, 1000]
然后我们使用以下代码进行网格搜索,确保打印出最终的测试准确性和最佳的超参数值:
grid_search = GridSearchCV(estimator = clf, param_grid = param_grid, cv = 3, n_jobs = -1, verbose = 2) ❶ grid_search.fit(train_x, train_y) ❷ print("Best parameters found:") ❸ print(grid_search.best_params_) print("Estimated accuracy is:") acc_score = accuracy_score(test_y, grid_search.best_estimator_.predict(test_x)) print(acc_score)
❶ 使用指定的超参数网格定义网格搜索对象
❷ 将网格搜索适配到数据
❸ 显示结果
这个实验需要在 333=27 个点上训练分类器,因为每个超参数网格上有三个请求的点。整个实验不到五分钟就完成了,并且准确率达到了 95.7%。这比原始得分 94.5%提高了超过 1%。代码的原始输出如下,指定了最佳的超参数值:
Best parameters found: {'min_samples_leaf': 2, 'min_samples_split': 10, 'n_estimators': 1000} Estimated accuracy is:
的确,当我们在所有分类器上进行全面调整时,我们发现可以将每个分类器的性能提升 1-2%,而不会影响在前一小节中达到的每个问题的最佳分类器的结论。
摘要
- 通常会尝试多种算法来解决感兴趣的任何问题,以找到模型复杂性和性能的最佳组合,以适应您的情况。
- 基线通常从最简单的算法开始,例如逻辑回归,然后逐渐变得更复杂,直到达到正确的性能/复杂性折衷。
- 重要的模型设计选择包括用于评估性能的指标,用于指导训练算法的损失函数以及最佳验证实践,等等,这些可以根据模型和问题类型而异。
- 超参数调整是模型开发流程的重要步骤,因为初始超参数设置可能严重误代表通过调整可以找到的最佳性能。
- 简单模型在可用数据量不大和/或问题较简单时往往效果最佳,而复杂的神经网络模型在有更多数据可用时往往表现更好,因此值得额外复杂性,当更多数据可用时。
- P. Goyal 等人,“准确的、大型小批次 SGD:在 1 小时内训练 ImageNet”,arXhiv(2018 年)。
github.com/azunre/transfer-learning-for-nlp
第二部分:浅层迁移学习和使用递归神经网络(RNNs)的深度迁移学习
第 4、5 和 6 章深入研究了基于浅层神经网络的一些重要迁移学习自然语言处理方法,也就是相对层数较少的神经网络。它们还开始探索深度迁移学习,通过使用递归神经网络(RNNs)作为关键功能的代表性技术,比如 ELMo。
第四章:自然语言处理的浅层迁移学习
本章包括
- 以半监督的方式使用预训练的词嵌入将预训练知识转移到问题中
- 以半监督的方式使用预训练的较大文本部分的嵌入来将预训练知识转移到问题中
- 使用多任务学习来开发性能更好的模型
- 修改目标域数据以重用来自资源丰富的源域的知识
在本章中,我们将涵盖一些重要的浅层迁移学习方法和概念。这使我们能够探索迁移学习中的一些主要主题,同时在感兴趣的最终类别——浅层神经网络类别的背景下进行。几位作者已经提出了将迁移学习方法分类到不同组别中的各种分类系统。¹,²,³ 大致来说,分类是基于迁移是否发生在不同的语言、任务或数据域之间。每种类型的分类通常相应地被称为 跨语言学习、多任务学习 和 领域自适应,如图 4.1 所示。
图 4.1 将迁移学习划分为多任务学习、领域自适应和跨语言学习的可视化分类
我们将在这里看到的方法涉及到某种程度上是神经网络的组件,但不像第三章中讨论的那样,这些神经网络没有很多层。这就是为什么标签“浅层”适合描述这些方法集合的原因。与上一章一样,我们将这些方法放在特定的实际例子的背景下,以促进您的实际自然语言处理技能的提升。跨语言学习将在本书的后续章节中讨论,因为现代神经机器翻译方法通常是深层的。我们将在本章中简要探讨另外两种迁移学习。
我们首先探讨了一种常见的半监督学习形式,它使用了预训练的词嵌入,如 word2vec,将其应用于本书前两章中的一个示例。请回忆第一章,这些方法与第三章中的方法不同,因为它们产生每个单词一个向量,而不考虑上下文。
我们重新访问了 IMDB 电影评论情感分类。回想一下,此示例涉及将 IMDB 的电影评论根据表达的情感分为积极或消极。这是一个典型的情感分析示例,在文献中被广泛使用来研究许多算法。我们将由预训练的单词嵌入生成的特征向量与一些传统的机器学习分类方法相结合,即随机森林和逻辑回归。然后,我们演示了使用更高级别的嵌入,即将更大的文本部分——句子、段落和文档级别——向量化,可以提高性能。将文本向量化,然后将传统的机器学习分类方法应用于生成的向量的一般思想在图 4.2 中可视化。
图 4.2 使用单词、句子或文档嵌入进行半监督学习的典型步骤序列
随后,我们将涵盖多任务学习,并学习如何同时训练单个系统来执行多个任务——在我们的案例中,分别由上一章的两个示例代表,即电子邮件垃圾分类和 IMDB 电影评论情感分析。你可以从多任务学习中获得几个潜在的好处。通过为多个任务训练单个机器学习模型,可以在更大更多样的来自合并数据池的数据上学习共享表示,这可能导致性能提升。此外,广泛观察到,这种共享表示具有更好的泛化能力,可以推广到未经训练的任务,而且可以在不增加模型大小的情况下实现此改进。我们在我们的示例中探索了其中一些好处。具体地,我们专注于浅层神经多任务学习,其中为设置中的每个特定任务训练了一个额外的密集层以及分类层。不同的任务还共享它们之间的一层,这种设置通常被称为硬参数共享。
最后,我们引入了一个流行的数据集作为本章的另一个运行示例。这就是多领域情感数据集,描述了Amazon.com的一组不同产品的产品评论。我们使用此数据集来探索领域自适应。假设我们有一个源领域,它可以被定义为特定任务的特定数据分布,并且已经训练好在该领域中的数据上表现良好的分类器。领域自适应的目标是修改或适应不同目标领域的数据,以使源领域的预训练知识可以帮助在目标领域中学习。我们应用了一种简单的自动编码方法来将目标领域中的样本“投影”到源领域特征空间中。
自编码器是一个系统,它通过将输入编码成一个有效的潜在表示,然后学习有效解码该表示,从而学习以非常高的准确度重构输入。它们传统上在模型减少应用中被广泛使用,因为潜在表示通常比编码发生的原始空间的维度要小,所选维度值也可以为计算效率和准确度的正确平衡而选择。⁴ 在极端情况下,在目标域中使用无标签数据进行训练可以获得改进,这通常称为 零样本域适应,其中学习发生在目标域中没有标记的数据。在我们的实验中,我们演示了一个例子。
4.1 带有预训练单词嵌入的半监督学习
单词嵌入的概念是自然语言处理领域的核心。它是给需要分析的每个单词产生一组实数向量的技术集合的名称。在单词嵌入设计中一个重要的考虑因素是生成向量的维度。更高维度的向量通常可以更好地代表语言中的单词,在许多任务上表现更好,但计算成本也自然更高。选择最优维度需要在这些竞争因素之间取得平衡,通常是经验性的,尽管一些最近的方法提出了更彻底的理论优化方法。⁵
如本书第一章所述,这个重要的 NLP 研究子领域有着丰富的历史,起源于 60 年代的术语向量模型的信息检索。这一领域的顶峰是在 2010 年代中期,出现了预训练的浅层神经网络技术,例如 fastText、GloVe 和 word2vec,它们有多个变体,包括连续词袋(CBOW)和 Skip-Gram。CBOW 和 Skip-Gram 都是从受过不同目标训练的浅层神经网络中提取的。Skip-Gram 尝试预测滑动窗口中任何目标单词周围的单词,而 CBOW 尝试预测给定邻居的目标单词。GloVe,即全局向量,尝试扩展 word2vec 通过将全局信息纳入嵌入中。它通过优化嵌入,使得单词之间的余弦积反映它们共现的次数,其目标是使得结果向量更加可解释。技术 fastText 尝试通过在字符 n-gram(而不是单词 n-gram)上重复 Skip-Gram 方法,从而能够处理以前看不见的单词。每个预训练嵌入的变体都有其优点和缺点,并在表 4.1 中总结。
表 4.1 比较各种流行单词嵌入方法的优缺点
词嵌入方法 | 优势 | 劣势 |
Skip-Gram word2vec | 适用于小型训练数据集和罕见词 | 训练速度慢,且对常见词准确性较低 |
CBOW word2vec | 训练速度几倍快于,并对常见词提供更好的准确性 | 在处理少量训练数据和罕见词方面效果不佳 |
GloVe | 向量比其他方法更容易解释 | 训练期间需要更高的内存存储词语共现情况 |
fastText | 能够处理词汇外的词 | 计算成本更高;模型更大更复杂 |
需要强调的是,fastText 以处理词汇外的词而闻名,这源自它的设计初衷即嵌入子词字符 n-gram 或子词(与 word2vec 的整个词相对应)。这使得它能够通过聚合组成的字符 n-gram 嵌入来为以前未见过的词构建嵌入。这一优点是以更大的预先训练嵌入和更高的计算资源需求和成本为代价的。因此,在本节中,我们将使用 fastText 软件框架以 word2vec 输入格式加载嵌入,而没有子词信息。这可以降低计算成本,使读者更容易进行练习,同时展示如何处理词汇外问题,并提供一个坚实的体验平台,让读者可以进入子词嵌入的领域。
让我们开始计算实验!我们需要做的第一件事是获得适当的预训练词嵌入文件。因为我们将使用 fastText 框架,我们可以从作者的官方网站⁶获取这些预训练文件,该网站提供多种格式的嵌入文件。请注意,这些文件非常庞大,因为它们试图捕获语言中所有可能单词的向量化信息。例如,针对英语语言的.wec 格式嵌入,是在维基百科 2017 年数据集上训练的,提供了在不处理子词和词汇外词的情况下的向量化信息,大约为 6GB。相应的.bin 格式嵌入,包含了着名的 fastText 子词信息,能够处理词汇外词,大约大 25%,约为 7.5GB。我们还注意到,维基百科嵌入提供了高达 294 种语言,甚至包括传统上未解决的非洲语言,例如特威语、埃维语和豪萨语。但已经表明,对于许多包括低资源语言,这些嵌入的质量并不是很好。⁷
由于这些嵌入的大小,建议使用我们在 Kaggle 上托管的推荐云笔记本来执行此示例(而不是在本地运行),因为其他用户已经将嵌入文件在云环境中公开托管。因此,我们可以简单地将它们附加到正在运行的笔记本上,而无需获取并在本地运行文件。
一旦嵌入可用,我们可以使用以下代码段加载它,确保计时加载函数调用:
import time from gensim.models import FastText, KeyedVectors start=time.time() FastText_embedding = KeyedVectors.load_word2vec_format("../input/jigsaw/wiki.en.vec") ❶ end = time.time() print("Loading the embedding took %d seconds"%(end-start))
❶ 加载以“word2vec”格式(不含子词信息)预训练的 fastText 嵌入。
在我们用于执行的 Kaggle 环境中,第一次加载嵌入需要超过 10 分钟。实际上,在这种情况下,通常将嵌入加载到内存中一次,然后使用诸如 Flask 之类的方法提供对它的访问,只要需要。这也可以通过本书本章附带的 Jupyter 笔记本来实现。
面向自然语言处理的迁移学习(一)(5)https://developer.aliyun.com/article/1519834