面向自然语言处理的迁移学习(三)(1)

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

第五章:处理数据以用于循环神经网络深度迁移学习实验

本章涵盖

  • 循环神经网络(RNNs)在 NLP 迁移学习中的建模架构概述
  • 处理和建模表格文本数据
  • 分析一对新的代表性 NLP 问题

在上一章中,我们详细研究了一些在 NLP 迁移学习中重要的浅层神经网络架构,包括 word2vec 和 sent2vec。 还要记住,这些方法产生的向量是静态和非上下文的,也就是说,它们对所讨论的词或句子产生相同的向量,无论周围上下文如何。这意味着这些方法无法消歧或区分词或句子的不同可能含义。

在这一章和下一章中,我们将介绍一些代表性的自然语言处理(NLP)深度迁移学习建模架构,这些架构依赖于循环神经网络(RNNs)的关键功能。具体来说,我们将研究建模框架 SIMOn,¹ ELMo,² 和 ULMFiT.³ 这些方法所使用的更深层次的神经网络的性质将使得所得到的嵌入是具有上下文的,即产生依赖上下文的词嵌入并实现消歧。回想一下,我们在第三章首次遇到了 ELMo。在下一章中,我们将更加深入地研究它的架构。

为了对本体进行建模的语义推理(SIMOn)是在 DARPA 的数据驱动模型发现(D3M)计划期间开发的,该计划旨在自动化数据科学家面临的一些典型任务,⁴ 包括自动构建用于数据清洗、特征提取、特征重要性排名和为任何给定数据科学问题选择模型的处理管道。这些任务通常被称为自动机器学习AutoML。具体来说,该模型试图将表格数据集中的每一列分类为基本类型,比如整数、字符串、浮点数或地址。其想法是 AutoML 系统可以根据这些信息决定如何处理输入的表格数据——这是实践中遇到的一种重要数据。人们可以从前述 D3M 计划网页下载程序中开发的各种工具的预打包的 Docker 镜像,包括 SIMOn。

SIMOn 的开发受到了计算机视觉中的迁移学习的类比的启发,这些内容在第一章的结尾进行了讨论。它的训练过程展示了如何使用迁移学习来使用模拟数据来补充少量手动标记的数据。将处理的类别集扩展到最初进行训练的类别之外是另一个在这个框架中生动地使用迁移学习的任务。这个模型在 D3M 中被大量使用,在本章中作为一个相对简单的实际例子,说明了如何利用迁移学习来解决真正的、实际的挑战。SIMOn 还被用于在社交媒体上检测潜在有害的沟通。#pgfId-1096624 列类型分类被用作这个建模框架的一个生动例子。

我们从一个介绍列数据类型分类示例的章节开始本章。在那一节中,相关的模拟数据生成和预处理过程也得到了简要的涉及。我们接着描述等效步骤用于“假新闻”检测示例,在下一章中将用于 ELMo 的一个实例。

在图 5.1 中展示了 SIMOn 架构的可视化,在表格列类型分类示例的背景下。粗略地说,它使用卷积神经网络(CNNs)来为句子构建初步的嵌入,使用一对 RNNs 来首先为句子中的字符构建内部上下文,然后为文档中的句子构建外部上下文。


图 5.1 在表格列类型分类示例中展示了 SIMOn 架构的可视化

从图中我们可以看到,这种架构由字符级卷积神经网络(CNNs)和双向长短期记忆(bi-LSTM)网络元素组成,这是一种递归神经网络(RNN)的类型。在这个框架中,值得强调的是输入文本被分词成句子,而不是单词。另外,将每个句子视为对应给定文档的列的单元,能够将非结构化文本转换为框架考虑的表格数据集上下文。

语言模型嵌入(ELMo)可以说是与正在进行的 NLP 迁移学习革命相关联的最受欢迎的早期预训练语言模型。它与 SIMOn 有许多架构上的相似之处,也由字符级 CNNs 与 bi-LSTMs 组成。这种相似性使得在介绍 SIMOn 之后深入挖掘 ELMo 的架构成为一个自然的下一步。我们将把 ELMo 应用到一个用于说明的问题上,即“假新闻”检测,以提供一个实际的背景。

图 5.2 显示了在表格列类型分类的背景下可视化的 ELMo 架构。 两个框架之间的一些相似之处和差异立即显而易见。 我们可以看到,两个框架都使用字符级 CNN 和双向 LSTM。 但是,虽然 SIMOn 有两个用于 RNN 的上下文构建阶段——一个用于句子中的字符,另一个用于文档中的句子——而 ELMo 只有一个阶段,重点是为输入文档中的单词建立上下文。


图 5.2 在表格列类型分类示例的背景下可视化了 ELMo 架构。

最后,我们将介绍通用语言模型微调(ULMFiT)框架,该框架引入并演示了一些关键技术和概念,使预训练语言模型能够更有效地适应新环境,如区分微调和逐步解冻。 区分性微调规定,由于语言模型的不同层包含不同类型的信息,因此应以不同的速率进行调整。 逐步解冻描述了一种逐渐微调更多参数的过程,旨在减少过拟合的风险。 ULMFiT 框架还包括在适应过程中以独特方式改变学习率的创新。 我们将在下一章介绍 ELMo 之后介绍该模型,以及其中的几个概念。

5.1 预处理表格列类型分类数据

在本节中,我们介绍了在本章和随后的章节中将探讨的第一个示例数据集。 在这里,我们有兴趣开发一种算法,该算法可以接收表格数据集,并为用户确定每列中的基本类型,即确定哪些列是整数、字符串、浮点数、地址等。 这样做的关键动机是,自动机器学习系统可以根据这些信息决定如何处理输入的表格数据——这是实践中遇到的一种重要数据类型。 例如,检测到的纬度和经度坐标值可以绘制在地图上并显示给用户。 检测到的浮点列可能是回归问题的潜在候选输入或输出,而分类列是分类问题的依赖变量的候选项。 我们用图 5.3 中的一个简单示例可视化了这个问题的本质。


图 5.3 使用简单示例可视化表格列数据类型分类问题

我们强调这是一个多标签、多类问题,因为每个输入示例都有多种可能的类别,并且每个输入样本可以分配多个这样的类别。例如,在图 5.3 中,第一列客户 ID 具有多个输出标签,即categoricalint。这还有助于处理输入列不是“干净”的情况,即它们包含多种类型。这些列可以带有所有存在的类型标签,并传递给相关解析器进行进一步清洁。

现在,我们对于这个问题有了更好的理解,让我们开始获取一些表格数据,用于本节的实验。

5.1.1 获取和可视化表格数据

我们将使用两个简单的数据集来说明下一章中的表格列类型分类示例。这两个数据集中的第一个是由 OpenML 提供的棒球球员统计数据集。⁶该数据集描述了一组球员的棒球统计数据,以及他们是否最终进入名人堂。

在 Linux 系统上,我们可以按如下方式获取数据集:

!wget https:/ /www.openml.org/data/get_csv/3622/dataset_189_baseball.arff

从以前的章节中可以回忆到,“!”符号仅在执行 Jupyter 环境(例如我们建议在这些练习中使用的 Kaggle 环境)时需要,当在终端中执行时,应该将其删除。同时请注意,对于我们的目的,.arff格式与.csv格式在功能上是等效的。

获取了感兴趣的数据集后,让我们像往常一样使用 Pandas 进行预览:

import pandas as pd
raw_baseball_data = pd.read_csv('dataset_189_baseball.arff', dtype=str)   ❶
print(raw_baseball_data.head())

❶对于我们的目的,.arff格式与.csv格式在功能上是等效的。

这将显示 DataFrame 的前五行,如下所示:

Player Number_seasons Games_played At_bats  Runs  Hits Doubles  \
0    HANK_AARON             23         3298   12364  2174  3771     624   
1   JERRY_ADAIR             13         1165    4019   378  1022     163   
2  SPARKY_ADAMS             13         1424    5557   844  1588     249   
3   BOBBY_ADAMS             14         1281    4019   591  1082     188   
4    JOE_ADCOCK             17         1959    6606   823  1832     295   
  Triples Home_runs  RBIs Walks Strikeouts Batting_average On_base_pct  \
0      98       755  2297  1402       1383           0.305       0.377   
1      19        57   366   208        499           0.254       0.294   
2      48         9   394   453        223           0.286       0.343   
3      49        37   303   414        447           0.269        0.34   
4      35       336  1122   594       1059           0.277       0.339   
  Slugging_pct Fielding_ave     Position Hall_of_Fame  
0        0.555         0.98     Outfield            1  
1        0.347        0.985  Second_base            0  
2        0.353        0.974  Second_base            0  
3        0.368        0.955   Third_base            0  
4        0.485        0.994   First_base            0  

我们可以看到这是一组广告中所述的突击手棒球统计数据集。

现在我们获取另一个表格数据集。不多赘述,这个数据集将用于扩展我们的 SIMOn 分类器,超越预训练模型所设计的类别集合。这个练习将为转移学习提供一个有趣的使用案例,可以激发你自己应用的创意。

我们将要查看的第二个数据集是多年的不列颠哥伦比亚省公共图书馆统计数据集,我们从 BC 数据目录⁷获得,但也将其附加到我们的伴随 Kaggle 笔记本上,以方便你使用。要使用 Pandas 加载数据集,我们执行以下命令,其中我们 Kaggle 环境中该文件的位置应该替换为您本地的路径,如果选择在本地工作:

raw_data = pd.read_csv('../input/20022018-bc-public-libraries-open-data-v182/2002-2018-bc-public-libraries-open-data-csv-v18.2.csv', dtype=str)

我们可以使用以下命令查看数据集:

print(raw_data.head())

输出结果为:

YEAR                           LOCATION                      LIB_NAME  \
0  2018  Alert Bay Public Library & Museum      Alert Bay Public Library   
1  2018       Beaver Valley Public Library  Beaver Valley Public Library   
2  2018        Bowen Island Public Library   Bowen Island Public Library   
3  2018             Burnaby Public Library        Burnaby Public Library   
4  2018          Burns Lake Public Library     Burns Lake Public Library   
                     LIB_TYPE SYMBOL        Federation             lib_ils  \
0  Public Library Association   BABM    Island Link LF     Evergreen Sitka   
1  Public Library Association   BFBV       Kootenay LF     Evergreen Sitka   
2           Municipal Library    BBI      InterLINK LF     Evergreen Sitka   
3           Municipal Library     BB      InterLINK LF  SirsiDynix Horizon   
4  Public Library Association   BBUL  North Central LF     Evergreen Sitka   
  POP_SERVED srv_pln STRAT_YR_START  ... OTH_EXP    TOT_EXP EXP_PER_CAPITA  \
0        954     Yes          2,013  ...    2488      24439        25.6174   
1      4,807     Yes          2,014  ...   15232  231314.13       48.12027   
2      3,680     Yes          2,018  ...   20709  315311.17       85.68238   
3    232,755     Yes          2,019  ...  237939   13794902       59.26791   
4      5,763     Yes          2,018  ...     NaN     292315       50.72271   
  TRANSFERS_TO_RESERVE AMORTIZATION EXP_ELEC_EBOOK EXP_ELEC_DB  \
0                    0            0              0         718   
1                11026            0        1409.23      826.82   
2                11176        40932           2111       54.17   
3                    0      2614627         132050           0   
4                  NaN          NaN              0           0   
  EXP_ELEC_ELEARN EXP_ELEC_STREAM EXP_ELEC_OTHER  
0               0               0            752  
1         1176.11               0        1310.97  
2            3241               0              0  
3               0               0         180376  
4               0               0           7040  
[5 rows x 235 columns]

我们只对百分比和整数这一对列感兴趣,我们可以按以下方式提取并显示:

COLUMNS = ["PCT_ELEC_IN_TOT_VOLS","TOT_AV_VOLS"]    ❶
raw_library_data = raw_data[COLUMNS]
print(raw_library_data)

❶这个数据集有很多列,我们只关注这两列。

这将产生以下输出,展示我们将使用的另外两列:

PCT_ELEC_IN_TOT_VOLS TOT_AV_VOLS
0                  90.42%          57
1                  74.83%       2,778
2                  85.55%       1,590
3                   9.22%      83,906
4                  66.63%       4,261
...                   ...         ...
1202                0.00%      35,215
1203                0.00%     109,499
1204                0.00%         209
1205                0.00%      18,748
1206                0.00%        2403
[1207 rows x 2 columns]

5.1.2 预处理表格数据

现在让我们将获取的表格数据预处理成 SIMOn 框架可以接受的形式。由于我们将使用一个预训练模型,该模型预先包含一个编码器,我们将应用于此目的,因此我们需要首先安装 SIMOn,使用以下命令:

!pip install git+https:/ /github.com/algorine/simon

完成这些之后,我们还需要导入一些必需的模块,如下所示:

from Simon import Simon              ❶
from Simon.Encoder import Encoder    ❷

❶ 导入 SIMOn 模型类

❷ 导入 SIMOn 数据编码器类,用于将输入文本转换为数字

这些导入分别代表了 SIMOn 模型类、数据编码器类、将所有输入数据标准化为固定长度的实用程序,以及生成模拟数据的类。

接下来,我们获取一个预训练的 SIMOn 模型,它带有自己的编码器,用于将文本转换为数字。该模型由两个文件组成:一个包含编码器和其他配置,另一个包含模型权重。我们使用以下命令获取这些文件:

!wget https:/ /raw.githubusercontent.com/algorine/simon/master/Simon/scripts/❶pretrained_models/Base.pkl                                              ❶
!wget https:/ /raw.githubusercontent.com/algorine/simon/master/Simon/scripts/❷pretrained_models/text-class.17-0.04.hdf5                               ❷

❶ 预训练的 SIMOn 模型配置、编码器等

❷ 对应的模型权重

在我们加载模型权重之前,首先需要加载它们的配置,这些配置包括编码器,通过以下一系列命令:

checkpoint_dir = ""                                                    ❶
execution_config = "Base.pkl"                                          ❷
Classifier = Simon(encoder={})                                         ❸
config = Classifier.load_config(execution_config, checkpoint_dir)      ❹
encoder = config['encoder']                                            ❺
checkpoint = config['checkpoint']                                      ❻

❶ 模型权重位于当前级别。

❷ 下载的预训练模型配置的名称

❸ 创建一个文本分类器实例,用于从模型配置中加载编码器。

❹ 加载模型配置

❺ 提取编码器

❻ 提取检查点名称

为了确保我们下载了正确的权重集,通过以下方式双重检查模型所需的权重文件:

print(checkpoint)

通过打印以下内容,应确认我们获取了正确的文件:

text-class.17-0.04.hdf5

最后,我们需要为建模表格数据指定两个关键参数。参数max_cells指定表格每列的最大单元格数。参数max_len指定每个单元格的最大长度。这在图 5.4 中有所体现。


图 5.4 可视化表格数据建模参数。参数max_cells指定表格中每列的最大单元格或行数。参数max_len指定每个单元格或行的最大长度。

每列的最大单元格数必须与训练中使用的 500 的值匹配,并且可以从编码器中提取,如下所示:

max_cells = encoder.cur_max_cells

另外,我们将max_len设置为 20,以与预训练模型设置保持一致,并提取预训练模型支持的类别,如下所示:

max_len = 20 # maximum length of each tabular cell
Categories = encoder.categories
category_count = len(Categories)      ❶
print(encoder.categories)

❶ 预训练模型支持的类别数量

我们发现处理的类别如下:

['address', 'boolean', 'datetime', 'email', 'float', 'int', 'phone', 'text', 'uri']

5.1.3 将预处理数据编码为数字

现在我们将使用编码器将表格数据转换为 SIMOn 模型可以用来进行预测的一组数字。这涉及将每个输入字符串中的每个字符转换为该字符在模型编码方案中表示的唯一整数。

因为卷积神经网络(CNNs)需要所有输入都是固定的、预先指定的长度,所以编码器还将标准化每个输入列的长度。这一步骤会复制短于max_cells的列中的随机单元,并丢弃一些长列中的随机单元。这确保了所有列的长度恰好为max_cells。此外,如果需要,所有单元都标准化为长度max_len,并添加填充。我们不会过于担心这些细节,因为 SIMOn API 会在幕后为我们处理它。

我们对棒球数据集进行编码,并使用以下代码显示其形状:

X_baseball = encoder.encodeDataFrame(raw_baseball_data)   ❶
print(X_baseball.shape)                                   ❷
print(X_baseball[0])                                      ❸

❶ 编码数据(标准化、转置、转换为 NumPy 数组)

❷ 显示了编码数据的形状

❸ 显示了编码的第一列

执行此操作会产生以下输出,其中首先显示输出形状元组,然后显示编码的第一列:

(18, 500, 20)
[[-1 -1 -1 ... 50 37 44]
 [-1 -1 -1 ... 54 41 46]
 [-1 -1 -1 ... 37 52 55]
 ...
 [-1 -1 -1 ... 49 45 46]
 [-1 -1 -1 ... 51 54 43]
 [-1 -1 -1 ... 38 37 43]]

我们看到每个编码列都是一个max_cells=500乘以max_len=20的数组,正如预期的那样。我们还注意到编码列的-1 条目代表了短于max_len的单元的填充。

我们还对图书馆数据进行编码,以便以后使用:

X_library = encoder.encodeDataFrame(raw_library_data)

在这个阶段,我们已经将示例输入数据集转换成了适当形状的 NumPy 数组。这将文本编码为适合 SIMOn 神经网络第一阶段——生成初步输入句子嵌入的 CNN 的摄入和分析的数字。

5.2 预处理事实检验示例数据

在这一节中,我们介绍了将在本章和后续章节中研究的第二个示例数据集。在这里,我们感兴趣的是开发一种算法,用于区分事实新闻和潜在的错误信息或虚假信息。这个应用领域变得越来越重要,并经常被称为“自动假新闻检测”。

对我们来说很方便的是,Kaggle⁸上有一个适用的数据集。该数据集包含超过 40,000 篇文章,分为两类:“假”和“真”。真实的文章来自一家名声显赫的新闻网站 reuters.com。而假新闻则来自 PolitiFact 标记为不可靠的各种来源。这些文章大多涉及政治和世界新闻。

5.2.1 特殊问题考虑

可以称为假的主题无疑是一个值得讨论的敏感话题。可以肯定的是,准备训练数据标签的人的偏见可能会转移到分类系统中。在这样敏感的语境下,标签的有效性需要特别注意和考虑如何创建。

此外,尽管我们在本节的目的是开发一个基于内容的分类系统,用于区分真实文章与潜在虚假文章,但重要的是要强调,现实场景要复杂得多。换句话说,检测潜在错误信息传播只是检测影响行动问题的一个方面。要理解两者之间的区别,请考虑即使真实信息也可以用来影响意见,从而损害品牌,如果将其放在错误的上下文或不自然地放大。

检测影响行动可以自然地被构造为一个异常检测问题,⁹ 但这样的系统只有作为缓解策略的一部分时才能有效。它必须是跨平台的,尽可能监控和分析尽可能多的潜在信息渠道中的异常情况。此外,今天的大多数实用系统都嵌入了人类,即检测系统只标记聚合的可疑活动,并将最终行动呼叫留给人类分析员。

5.2.2 加载和可视化事实检查数据

现在,我们直接跳转到加载事实检查数据并使用 ELMo 建模框架对其进行分类的步骤。回想一下第 3.2.1 节,我们在那里将 ELMo 应用于垃圾邮件检测和电影评论情感分析,该模型期望将每个输入文档作为单个字符串。这使得事情变得更容易——不需要分词。还要注意,数据集已经附加到了 Kaggle 上的伴随 Jupyter 笔记本上。

我们使用列表 5.1 中的代码从数据集中加载真假数据。请注意,我们选择在此加载每种 1,000 个样本,以保持与第 3.2.1 节的一致性。

列表 5.1 加载每种 1,000 个真假文章样本

import numpy as np
import pandas as pd
DataTrue = pd.read_csv("/kaggle/input/fake-and-real-news-dataset/True.csv")❶
DataFake = pd.read_csv("/kaggle/input/fake-and-real-news-dataset/Fake.csv")❷
Nsamp =1000                                                                ❸
DataTrue = DataTrue.sample(Nsamp)
DataFake = DataFake.sample(Nsamp)
raw_data = pd.concat([DataTrue,DataFake], axis=0).values                   ❹
raw_data = [sample[0].lower() + sample[1].lower() + sample[3].lower() for sample in raw_data]                                                   ❺
Categories = ['True','False']                                              ❻
header = ([1]*Nsamp)
header.extend(([0]*Nsamp))

❶ 将真实新闻数据读入 Pandas DataFrame

❷ 将假新闻数据读入 Pandas DataFrame

❸ 每个类别生成的样本数——真实,虚假

❹ 连接的真假样本

❺ 将标题、正文和主题组合成每个文档的一个字符串

❻ 对应的标签

其次,我们使用以下代码将数据洗牌并将其分为 70% 的训练/30% 的验证,以方便起见,这些代码在此处从第 3.2.1 节复制:

def unison_shuffle(a, b):                             ❶
    p = np.random.permutation(len(b))
    data = np.asarray(a)[p]
    header = np.asarray(b)[p]
    return data, header
raw_data, header = unison_shuffle(raw_data, header)   ❷
idx = int(0.7*raw_data.shape[0])                      ❸
train_x = raw_data[:idx]                              ❹
train_y = header[:idx]
test_x = raw_data[idx:]                               ❺
test_y = header[idx:]

❶ 一个用于与标签头一起洗牌数据的函数,以消除任何潜在的顺序偏差

❷ 通过调用先前定义的函数来洗牌数据

❸ 分成独立的 70% 训练和 30% 测试集

❹ 70% 的数据用于训练

❺ 剩余 30% 用于验证

在介绍和预处理示例问题数据之后,我们将在下一章中将章节开头概述的三个基于 RNN 的神经网络模型应用于示例问题数据。

总结

  • 与单词级模型相比,字符级模型可以处理拼写错误和其他社交媒体特征,如表情符号和小众俚语。
  • 双向语言建模是构建意识到其局部上下文的词嵌入的关键。
  • SIMOn 和 ELMo 都采用字符级 CNN 和双向 LSTM,后者有助于实现双向上下文建模。
  1. P. Azunre 等人,“基于字符级卷积神经网络的表格数据集的语义分类”,arXiv(2019 年)。
  2. M. E. Peters 等人,“Deep Contextualized Word Representations”,NAACL-HLT 会议论文集(2018 年)。
  3. J. Howard 等人,“文本分类的通用语言模型微调”,第 56 届计算语言学年会论文集(2018 年)。
  4. docs.datadrivendiscovery.org/
  5. N. Dhamani 等人,“利用深度网络和迁移学习解决虚假信息问题”,AI for Social Good ICML 研讨会(2019 年)。
  6. www.openml.org/d/185
  7. catalogue.data.gov.bc.ca/dataset/bc-public-libraries-statistics-2002-present
  8. www.kaggle.com/clmentbisaillon/fake-and-real-news-dataset
  9. P. Azunre 等人,“虚假信息:检测到阻断”,真相和信任在线会议 1 卷 1 期(2019 年)。

第六章:.循环神经网络用于自然语言处理的深度迁移学习

本章内容包括

  • 依赖于 RNN 的自然语言处理迁移学习的三种代表性建模架构
  • 将这些方法应用于上一章中介绍的两个问题
  • 将在模拟数据训练中获得的知识传递到真实标记数据
  • 介绍一些更复杂的模型适应策略,通过 ULMFiT

在上一章中,我们介绍了两个用于本章实验的例子问题——列类型分类和虚假新闻检测。回顾一下,实验的目标是研究依赖于循环神经网络(RNN)的深度迁移学习方法,以用于自然语言处理的关键功能。具体而言,我们将重点研究三种方法——SIMOn、ELMo 和 ULMFiT,这些方法在上一章中已经简要介绍过。在下一节中,我们将从 SIMOn 开始,将它们应用于示例问题。

6.1 语义推理用于本体建模(SIMOn)

正如我们在上一章中简要讨论的那样,SIMOn 是作为自动机器学习(AutoML)管道的一个组成部分而设计的,用于数据驱动的模型发现(D3M)DARPA 计划。它被开发为用于表格数据集中列类型的分类工具,但也可以看作是一个更一般的文本分类框架。我们将首先在任意文本输入的环境下介绍该模型,然后将其专门用于表格案例。

SIMOn 是一个字符级模型,而不是单词级模型,以处理拼写错误和其他社交媒体特征,如表情符号和专业知识的口头语。因为它以字符级别编码输入文本,所以输入只需要用于分类的允许字符即可。这使得模型能够轻松适应社交媒体语言的动态特性。模型的字符级本质在图 6.1 中与单词级模型进行对比。在图的左侧,我们展示了单词级编码器,其输入必须是一个有效的单词。显然,由于拼写错误或行话,一个词汇表外的词是无效的输入。对于字符级编码器,如 ELMo 和 SIMOn 所示,输入只需要是一个有效的字符,这有助于处理拼写错误。


图 6.1 对比基于单词级和字符级的文本分类模型

6.1.1 通用神经架构概述

该网络可以分为两个主要耦合的部分,将一个被分割为句子的文档作为输入。第一个部分是一个用于编码每个独立句子的网络,而第二个部分则使用编码的句子创建整个文档的编码。

句子编码器首先对输入句子进行字符级的独热编码,使用了一个包含 71 个字符的字典。这包括所有可能的英文字母,以及数字和标点符号。输入句子也被标准化为长度为max_len。然后通过一系列的卷积、最大池化、失活和双向 LSTM 层。请参考图 5.1 的前两个阶段,这里为了方便起见重复一次,进行一个摘要可视化。卷积层在每个句子中实质上形成了“词”的概念,而双向 LSTM“查看”一个词周围的两个方向,以确定其局部上下文。这一阶段的输出是每个句子的默认维度为 512 的嵌入向量。还可以比较图 5.1 和图 6.1 中双向 LSTM 的等效图示来使事情具体化。


图 5.1(为了方便起见,从上一章中重复)在表格列类型分类示例中可视化 SIMOn 架构

文档编码器将句子嵌入向量作为输入,类似地通过一系列的随机失活和双向 LSTM 层来处理它们。每个文档的长度被标准化为max_cells个这样的嵌入向量。可以将这看作是从句子中形成更高级的“概念”或“主题”的过程,这些概念与文档中存在的其他概念相关联。这为每个文档产生了一个嵌入向量,然后通过一个分类层传递,输出每种不同类型或类的概率。

6.1.2 对表格数据进行建模

对表格数据进行建模出人意料的简单;它只需要将表格数据集中每个单元格都视为一个句子。当然,每个这样的列被视为要进行分类的一个文档。

这意味着要将 SIMOn 框架应用到非结构化文本,只需将文本转换成一张表,每列一个文档,每个单元格一个句子。这个过程的示意图在图 6.2 中展示。请注意,在这个简单的例子中,我们选择max_cells等于 3,只是为了示例。


图 6.2 将非结构化文本转换为 SIMOn 可消化的过程

6.1.3 将 SIMOn 应用于表格列类型分类数据

在其原始形式中,SIMOn 最初是在一组基础类的模拟数据上进行训练的。然后转移到一组手工标记的较小数据。了解如何生成模拟数据可能是有用的,因此我们用以下一组命令简要地说明了这个过程,这些命令在底层使用了库 Faker:

from Simon.DataGenerator import DataGenerator      ❶
data_cols = 5                                      ❷
data_count = 10                                    ❸
try_reuse_data = False                             ❹
simulated_data, header = DataGenerator.gen_test_data((data_count, data_cols), try_reuse_data)
print("SIMULATED DATA")                            ❺
print(simulated_data)
print("SIMULATED DATA HEADER:")
print(header)

❶ 模拟/伪造数据生成实用工具(使用库 Faker)

❷ 生成的列数,为了简单起见任意选择

❸ 每列的单元格/行数,为了简单说明而任意选择

❹ 不要重用数据,而是为数据集中的变化性生成新鲜数据。

❺ 打印结果

执行此代码会产生以下输出,显示各种数据类型的生成样本及其相应的标签:

SIMULATED DATA:
[['byoung@hotmail.com' 'Jesse' 'True' 'PPC' 'Lauraview']
 ['cindygilbert@gmail.com' 'Jason' 'True' 'Intel' 'West Brandonburgh']
 ['wilsonalexis@yahoo.com' 'Matthew' 'True' 'U; Intel'
  'South Christopherside']
 ['cbrown@yahoo.com' 'Andrew' 'False' 'U; PPC' 'Loganside']
 ['christopher90@gmail.com' 'Devon' 'True' 'PPC' 'East Charlesview']
 ['deanna75@gmail.com' 'Eric' 'False' 'U; PPC' 'West Janethaven']
 ['james80@hotmail.com' 'Ryan' 'True' 'U; Intel' 'Loriborough']
 ['cookjennifer@yahoo.com' 'Richard' 'True' 'U; Intel' 'Robertsonchester']
 ['jonestyler@gmail.com' 'John' 'True' 'PPC' 'New Kevinfort']
 ['johnsonmichael@gmail.com' 'Justin' 'True' 'U; Intel' 'Victormouth']]
SIMULATED DATA HEADER:
[list(['email', 'text']) list(['text']) list(['boolean', 'text'])
 list(['text']) list(['text'])]

SIMOn 仓库的顶层包含了 types.json 文件,该文件指定了从 Faker 库类到先前显示的类别的映射。例如,前一个示例中名称的第二列被标记为“文本”,因为我们不需要为我们的目的识别名称。您可以快速更改此映射,并为您自己的项目和类别集生成模拟数据。

我们这里不使用模拟数据进行训练,因为该过程可能需要几个小时,而我们已经可以访问捕捉到这些知识的预训练模型。但是,我们会进行一项说明性的迁移学习实验,涉及扩展支持的类别集合,超出了预训练模型中可用的类别。

回想一下,在第 5.1.2 节中加载了 SIMOn 分类器类以及模型配置,包括编码器。然后我们可以生成一个 Keras SIMOn 模型,将下载的权重加载到其中,并使用以下命令序列进行编译:

model = Classifier.generate_model(max_len, max_cells, category_count)   ❶
Classifier.load_weights(checkpoint, None, model, checkpoint_dir)        ❷
model.compile(loss='binary_crossentropy',optimizer='adam', metrics=['accuracy']) 

❶ 生成模型

❷ 加载权重

❸ 编译模型,使用二元交叉熵损失进行多标签分类

在继续之前,查看模型架构是个好主意,我们可以使用以下命令来做到这一点:

model.summary() 

这将显示以下输出,并允许您更好地了解内部发生的情况:

______________________________________________________________________________________
Layer (type)                    Output Shape         Param #   Connected to           
======================================================================================
input_1 (InputLayer)            (None, 500, 20)      0                                
______________________________________________________________________________________
time_distributed_1 (TimeDistrib (None, 500, 512)     3202416   input_1[0][0]          
______________________________________________________________________________________
lstm_3 (LSTM)                   (None, 128)          328192    time_distributed_1[0][0]
______________________________________________________________________________________
lstm_4 (LSTM)                   (None, 128)          328192    time_distributed_1[0][0]
______________________________________________________________________________________
concatenate_2 (Concatenate)     (None, 256)          0         lstm_3[0][0]           
                                                               lstm_4[0][0]           
______________________________________________________________________________________
dropout_5 (Dropout)             (None, 256)          0         concatenate_2[0][0]    
______________________________________________________________________________________
dense_1 (Dense)                 (None, 128)          32896     dropout_5[0][0]        
______________________________________________________________________________________
dropout_6 (Dropout)             (None, 128)          0         dense_1[0][0]          
______________________________________________________________________________________
dense_2 (Dense)                 (None, 9)            1161      dropout_6[0][0]  

time_distributed_1 层是应用于每个输入句子的句子编码器。我们看到其后是前向和后向的 LSTM,它们被连接在一起,一些通过 dropout 进行的正则化,以及来自 dense_2 层的输出概率。回想一下,预训练模型处理的类别数恰好为 9,这与输出 dense_2 层的维度匹配。还要注意的是,巧合的是,模型总共有 9 层。

通过执行以下一系列命令,我们已经对编译模型的架构有了一定的了解,现在让我们继续查看它认为棒球数据集列的类型是什么。我们通过执行以下命令序列来实现这一点:

p_threshold = 0.5                                       ❶
y = model.predict(X_baseball)                           ❷
result = encoder.reverse_label_encode(y,p_threshold)    ❸
print("Recall that the column headers were:")           ❹
print(list(raw_baseball_data))
print("The predicted classes and probabilities are respectively:")
print(result)

❶ 用于决定类成员身份的概率阈值

❷ 预测棒球数据集列的类别

❸ 将概率转换为类别标签

❹ 显示输出

对应的代码输出如下所示:

Recall that the column headers were:
['Player', 'Number_seasons', 'Games_played', 'At_bats', 'Runs', 'Hits', 'Doubles', 'Triples', 'Home_runs', 'RBIs', 'Walks', 'Strikeouts', 'Batting_average', 'On_base_pct', 'Slugging_pct', 'Fielding_ave', 'Position', 'Hall_of_Fame']
The predicted classes and probabilities are respectively:
([('text',), ('int',), ('int',), ('int',), ('int',), ('int',), ('int',), ('int',), ('int',), ('int',), ('int',), ('int',), ('float',), ('float',), ('float',), ('float',), ('text',), ('int',)], [[0.9970826506614685], [0.9877430200576782], [0.9899477362632751], [0.9903284907341003], [0.9894667267799377], [0.9854978322982788], [0.9892633557319641], [0.9895514845848083], [0.989467203617096], [0.9895854592323303], [0.9896339178085327], [0.9897230863571167], [0.9998295307159424], [0.9998230338096619], [0.9998272061347961], [0.9998039603233337], [0.9975670576095581], [0.9894945025444031]])

回顾第 5.1.1 节以显示此数据的切片,我们在此复制,我们看到模型以高置信度完全正确地获取了每一列:

Player Number_seasons Games_played At_bats  Runs  Hits Doubles  \
0    HANK_AARON             23         3298   12364  2174  3771     624   
1   JERRY_ADAIR             13         1165    4019   378  1022     163   
2  SPARKY_ADAMS             13         1424    5557   844  1588     249   
3   BOBBY_ADAMS             14         1281    4019   591  1082     188   
4    JOE_ADCOCK             17         1959    6606   823  1832     295   
  Triples Home_runs  RBIs Walks Strikeouts Batting_average On_base_pct  \
0      98       755  2297  1402       1383           0.305       0.377   
1      19        57   366   208        499           0.254       0.294   
2      48         9   394   453        223           0.286       0.343   
3      49        37   303   414        447           0.269        0.34   
4      35       336  1122   594       1059           0.277       0.339   
  Slugging_pct Fielding_ave     Position Hall_of_Fame  
0        0.555         0.98     Outfield            1  
1        0.347        0.985  Second_base            0  
2        0.353        0.974  Second_base            0  
3        0.368        0.955   Third_base            0  
4        0.485        0.994   First_base            0  

现在,假设我们有兴趣在项目中检测具有百分比值的列。我们如何快速使用预训练模型来实现这一点呢?我们可以使用上一章中准备的第二个表格数据集来调查这种情况——多年来的不列颠哥伦比亚公共图书馆统计数据集。当然,第一步是直接使用预训练模型预测这些数据。以下一系列命令实现了这一点:

X = encoder.encodeDataFrame(raw_library_data)          ❶
y = model.predict(X)                                   ❷
result = encoder.reverse_label_encode(y,p_threshold)   ❸
print("Recall that the column headers were:")
print(list(raw_library_data))
print("The predicted class/probability:")
print(result)

❶ 使用原始框架对数据进行编码

❷ 预测类别

❸ 将概率转换为类标签

这将产生以下输出:

Recall that the column headers were:
['PCT_ELEC_IN_TOT_VOLS', 'TOT_AV_VOLS']
The predicted class/probability:
([('text',), ('int',)], [[0.7253058552742004], [0.7712462544441223]])

回顾 5.1.1 节的一个数据切片,我们看到整数列被正确识别,而百分比列被识别为文本:

PCT_ELEC_IN_TOT_VOLS TOT_AV_VOLS
0                  90.42%          57
1                  74.83%       2,778
2                  85.55%       1,590
3                   9.22%      83,906
4                  66.63%       4,261
...                   ...         ...
1202                0.00%      35,215
1203                0.00%     109,499
1204                0.00%         209
1205                0.00%      18,748
1206                0.00%        2403
[1207 rows x 2 columns]

那并不是不正确,但也不完全是我们正在寻找的,因为它不够具体。

我们将快速将预训练模型转移到一个非常小的包含百分比样本的训练数据集。首先让我们使用以下命令了解原始库 DataFrame 的大小:

print(raw_library_data.shape)

我们发现尺寸为(1207,2),这似乎是足够构建一个小数据集的行数!

在清单 6.1 中,我们展示了可用于将此数据集分割为许多每个 20 个单元格的更小列的脚本。数字 20 是任意选择的,是为了创建足够多的唯一列——大约 50 个——在生成的数据集中。此过程产生一个新的 DataFrame,new_raw_data,大小为 20 行 120 列——前 60 列对应于百分比值,后 60 列对应于整数值。它还生成一个相应的header标签列表。

清单 6.1 将长库数据转换为许多较短样本列

percent_value_list = raw_library_data['PCT_ELEC_IN_TOT_VOLS'].values.tolist()
int_value_list = raw_library_data['TOT_AV_VOLS'].values.tolist()
original_length = raw_data.shape[0]                                        ❸
chunk_size = 20 # length of each newly generated column
header_list = list(range(2*original_length/ /chunk_size))                   ❹
new_raw_data = pd.DataFrame(columns = header_list)                         ❺
for i in range(original_length/ /chunk_size):                               ❻
    new_raw_data[i] = percent_value_list[i:i+chunk_size]                   ❼
    new_raw_data[original_length/ /chunk_size+i] = int_value_list[i:i+chunk_size]                                        ❽
header = [("percent",),]*(original_length/ /chunk_size)                     ❾
header.extend([("int",),]*(original_length/ /chunk_size))

❶ 将数据转换为两个列表

❷ 将其分解为每个样本列 20 个单元格

❸ 原始长度,1207

❹ 新列的索引列表

❺ 初始化新的 DataFrame 以保存新数据

❻ 使用新的 DataFrame 填充

❼ 使用百分比值填充 DataFrame

❽ 使用整数值填充 DataFrame

❾ 让我们为我们的训练数据创建相应的标题。

记得预训练模型的最后一层具有输出维度为 9,与处理的类的数量相匹配。要添加另一个类,我们需要将输出维度增加到大小为 10。我们还应该将这个新维度的权重初始化为文本类的权重,因为这是预训练模型处理的最相似的类。这是在我们之前使用预训练模型将百分比数据预测为文本时确定的。这是通过下一个清单中显示的脚本完成的。在脚本中,我们将百分比添加到支持的类别列表中,将输出维度增加 1 以容纳此添加,然后将相应维度的权重初始化为最接近的类别文本值的权重。

清单 6.2 创建最终输出层的新权重,包括百分比类

import numpy as np
old_weights = model.layers[8].get_weights()                          ❶
old_category_index = encoder.categories.index('text')                ❷
encoder.categories.append("percent")                                 ❸
encoder.categories.sort()                                            ❹
new_category_index = encoder.categories.index('percent')             ❺
new_weights = np.copy(old_weights)                                   ❻
new_weights[0] = np.insert(new_weights[0], new_category_index, old_weights[0][:,old_category_index], axis=1)                   ❼
new_weights[1] = np.insert(new_weights[1], new_category_index, 0)    ❽

❶ 抓取初始化的最后一层权重

❷ 找到最接近类别的旧权重索引—文本

❸ 使用新的类别列表更新编码器

❹ 对新列表按字母顺序排序

❺ 找到新类别的索引

❻ 将新权重初始化为旧权重

❼ 在百分比权重位置插入文本权重

❽ 在百分比偏差位置插入文本偏差

在执行清单 6.2 中的代码之后,您应该仔细检查数组old_weightsnew_weights的形状。如果一切按预期进行,您应该会发现前者是(128,9),而后者是(128,10)。

现在我们已经准备好在预训练之前用来初始化新模型的权重,让我们实际构建和编译这个新模型。SIMOn API 包含以下函数,使构建模型非常容易:

model = Classifier.generate_transfer_model(max_len, max_cells, category_count, category_count+1, checkpoint, checkpoint_dir)

通过此函数返回的转移模型与我们之前构建的模型完全类似,唯一的区别是最终层现在具有新的维度,由输入category_count+1指定。另外,因为我们没有为新创建的输出层提供任何初始化信息,所以这一层目前被初始化为全零权重。

在我们可以训练这个新的转移模型之前,让我们确保只有最终输出层是可训练的。我们通过以下代码片段完成这一点,并编译模型:

for layer in model.layers:                                                      ❶
    layer.trainable = False
model.layers[-1].trainable = True                                               ❷
model.layers[8].set_weights(new_weights)                                        ❸
model.compile(loss='binary_crossentropy',optimizer='adam', metrics=['accuracy'])❹

❶ 开始时使所有层都不可训练

❷ 只有最后一层应该是可训练的。

❸ 将最终层的权重设置为先前确定的初始化值

❹ 编译模型

现在我们可以使用以下清单中的代码在新数据上训练构建的、初始化的和编译的转移模型。

清单 6.3 训练初始化和编译的新转移模型

import time
X = encoder.encodeDataFrame(new_raw_data)                                          ❶
y = encoder.label_encode(header)                                                   ❷
data = Classifier.setup_test_sets(X, y)                                            ❸
batch_size = 4
nb_epoch = 10
start = time.time()
history = Classifier.train_model(batch_size, checkpoint_dir, model, nb_epoch, data)❹
end = time.time()
print("Time for training is %f sec"%(end-start)) 

❶ 编码新数据(标准化、转置、转换为 NumPy 数组)

❷ 编码标签

❸ 准备预期格式的数据 -> 60/30/10 训练/验证/测试数据拆分

❹ 训练数据

我们在图 6.3 中可视化了此代码生成的收敛信息。我们看到在第七个时期实现了 100%的验证准确率,训练时间为 150 秒。看来我们的实验成功了,我们已成功地微调了预训练模型以处理新的数据类!我们注意到,为了使这个新模型能够准确地处理所有 10 个类,我们需要在转移步骤中的训练数据中包含每个类的一些样本。在这个阶段,微调的模型只适用于预测包含在转移步骤中的类——整数百分比。因为我们这里的目标仅仅是说明性的,我们将此作为读者的警告,并不进一步关注。


图 6.3 百分比类转移表格数据实验收敛可视化

作为转移实验的最后一步,让我们通过比较测试集的预测标签和真实标签来深入了解其性能。可以通过以下代码片段来完成这个任务:

y = model.predict(data.X_test)                                        ❶
result = encoder.reverse_label_encode(y,p_threshold)                  ❷
print("The predicted classes and probabilities are respectively:")    ❸
print(result) 
print("True labels/probabilities, for comparision:") print(encoder.reverse_label_encode(data.y_test,p_threshold))

❶预测类别

❷将概率转换为类标签

❸ 检查

生成的输出如下:

The predicted classes and probabilities are respectively:
([('percent',), ('percent',), ('int',), ('int',), ('percent',), ('int',), ('percent',), ('int',), ('int',), ('percent',), ('percent',), ('int',)], [[0.7889140248298645], [0.7893422842025757], [0.7004106640815735], [0.7190601229667664], [0.7961368560791016], [0.9885498881340027], [0.8160757422447205], [0.8141483068466187], [0.5697212815284729], [0.8359809517860413], [0.8188782930374146], [0.5185337066650391]])
True labels/probabilities, for comparision:
([('percent',), ('percent',), ('int',), ('int',), ('percent',), ('int',), ('percent',), ('int',), ('int',), ('percent',), ('percent',), ('int',)], [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1]])

我们发现,微调模型已经完全正确地预测了每个例子,进一步验证了我们的迁移学习实验。

最后要记住,通过在 6.1.2 节中描述的适应过程,SIMOn 框架可以应用于任意输入文本,而不仅仅是表格数据。几个应用示例取得了有希望的结果。¹希望本节的练习已经充分准备您在自己的分类应用程序中部署它,并通过迁移学习将生成的分类器适应新情况。

现在我们将继续探讨将 ELMo 应用于虚假新闻分类示例的情况。

6.2 来自语言模型的嵌入(ELMo)

如前一章节简要提到的,来自语言模型的嵌入(ELMo)可以说是与正在进行的 NLP 迁移学习革命相关的最受欢迎的早期预训练语言模型之一。它与 SIMOn 有一些相似之处,因为它也由字符级 CNN 和双向 LSTM 组成。请参考图 5.2,这里重复了一遍,以便鸟瞰这些建模组件。


图 5.2(重复)在表格列类型分类示例的背景下可视化 ELMo 架构

还要查看图 6.1,特别是比图 5.2 更详细的相当于双向 LSTM 的图示。如果您按照本书的时间顺序阅读,那么您也已经在 3.2.1 节中将 ELMo 应用于垃圾邮件检测和 IMDB 电影评论情感分类问题。正如您现在可能已经了解到的那样,ELMo 产生的词表示是整个输入句子的函数。换句话说,该模型是上下文感知的词嵌入。

本节深入探讨了 ELMo 的建模架构。ELMo 确切地对输入文本做了什么来构建上下文和消岐?为了回答这个问题,首先介绍了使用 ELMo 进行双向语言建模,接着将该模型应用于虚假新闻检测问题以使问题具体化。

6.2.1 ELMo 双向语言建模

请记住,语言建模试图对一个令牌的出现概率进行建模,通常是一个词,在给定序列中出现。考虑这样一个情景,我们有一个N令牌的序列,例如,句子或段落中的单词。一个以单词为单位的前向语言模型通过取序列中每个令牌在其从左到右的历史条件下的概率的乘积来计算序列的联合概率,如图 6.4 所示。考虑这个简短的句子,“你可以”。根据图 6.4 中的公式,前向语言模型计算句子的概率为第一个词在句子中是“”的概率乘以第二个词是“可以”的概率,假设第一个词是“”,再乘以第三个词是“”的概率,假设前两个词是“你可以”。


图 6.4 前向语言模型方程

一个以单词为单位的反向语言模型做的是相同的事情,但是反过来,如图 6.5 中的方程所示。它通过对每个令牌在右到左令牌历史条件下的概率的乘积来建模序列的联合概率。


图 6.5 反向语言模型方程。

再次考虑这个简短的句子,“你可以”。根据图 6.5 中的公式,反向语言模型计算句子的概率为最后一个词在句子中是“”的概率乘以第二个词是“可以”的概率,假设最后一个词是“”,再乘以第一个词是“”的概率,假设其他两个词是“可以是”。

一个双向语言模型结合了前向和后向模型。ELMo 模型特别寻求最大化两个方向的联合对数似然——在图 6.6 中显示的量。请注意,尽管为前向和后向语言模型保留了单独的参数,但令牌向量和最终层参数在两者之间是共享的。这是第四章讨论的软参数共享多任务学习场景的一个例子。


图 6.6 ELMo 用于为序列中的任何给定令牌构建双向上下文的联合双向语言建模(LM)目标方程

每个令牌的 ELMo 表示来自双向 LSTM 语言模型的内部状态。对于任何给定任务,它是与目标令牌对应的所有 LSTM 层(两个方向上的)的内部状态的线性组合。

将所有内部状态组合在一起,与仅使用顶层不同,例如在 SIMOn 中,具有显著的优势。尽管 LSTM 的较低层使得在基于句法的任务(如词性标注)上具有良好的性能,但较高层使得在含义上进行上下文相关的消歧。学习每个任务在这两种表示类型之间的线性组合,允许最终模型选择它需要的任务类型的信号。

6.2.2 应用于假新闻检测的模型

现在让我们继续构建一个 ELMo 模型,用于我们在第 5.2 节中组装的假新闻分类数据集。对于已经阅读过第三章和第四章的读者来说,这是 ELMo 建模框架对实际示例的第二个应用。

由于我们已经构建了 ELMo 模型,我们将能够重用一些在第三章中已经定义的函数。请参考第 3.4 节的代码,该代码利用 TensorFlow Hub 平台加载了 ELMo 作者提供的权重,并使用ElmoEmbeddingLayer类构建了一个适用于 Keras 的模型。定义了这个类之后,我们可以通过以下代码训练我们所需的用于假新闻检测的 ELMo 模型(与第 3.6 节稍作修改的代码):

def build_model(): 
  input_text = layers.Input(shape=(1,), dtype="string")
  embedding = ElmoEmbeddingLayer()(input_text)
  dense = layers.Dense(256, activation='relu')(embedding)    ❶
  pred = layers.Dense(1, activation='sigmoid')(dense)        ❷
  model = Model(inputs=[input_text], outputs=pred)
  model.compile(loss='binary_crossentropy', optimizer='adam',
                                 metrics=['accuracy'])       ❸
  model.summary()                                            ❹
  return model
# Build and fit
model = build_model()
model.fit(train_x,                                           ❺
          train_y,
          validation_data=(test_x, test_y),
          epochs=10,
          batch_size=4)

❶ 输出 256 维特征向量的新层

❷ 分类层

❸ 损失、度量和优化器的选择

❹ 显示用于检查的模型架构

❺ 将模型拟合 10 个 epochs

让我们更仔细地查看模型结构,该结构由前述代码片段中的model.summary()语句输出:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 1)                 0         
_________________________________________________________________
elmo_embedding_layer_1 (Elmo (None, 1024)              4         
_________________________________________________________________
dense_1 (Dense)              (None, 256)               262400    
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 257       
=================================================================
Total params: 262,661
Trainable params: 262,661
Non-trainable params: 0

dense_1dense_2层是添加到第 3.4 节产生的预训练嵌入之上的新的全连接层。预训练嵌入是elmo_embedding_layer_1。请注意,打印的模型摘要显示它有四个可训练参数。这四个参数是前面子节中描述的内部双向 LSTM 状态的线性组合中的权重。如果您像我们这样使用 TensorFlow Hub 方法使用预训练的 ELMo 模型,则 ELMo 模型的其余部分不可训练。然而,可以使用模型库的另一个版本构建一个完全可训练的基于 TensorFlow 的 ELMo 模型。

当我们在假新闻数据集上执行前述代码时所达到的收敛结果如图 6.7 所示。我们看到,达到了超过 98%的准确率。


图 6.7 ELMO 模型在假新闻数据集上训练的收敛结果

面向自然语言处理的迁移学习(三)(2)https://developer.aliyun.com/article/1522491

相关文章
|
7月前
|
机器学习/深度学习 自然语言处理 机器人
面向自然语言处理的迁移学习(二)(4)
面向自然语言处理的迁移学习(二)
54 2
|
7月前
|
机器学习/深度学习 自然语言处理 数据可视化
面向自然语言处理的迁移学习(二)(3)
面向自然语言处理的迁移学习(二)
58 2
|
7月前
|
机器学习/深度学习 自然语言处理 算法
面向自然语言处理的迁移学习(一)(3)
面向自然语言处理的迁移学习(一)
57 3
|
7月前
|
机器学习/深度学习 自然语言处理 数据可视化
面向自然语言处理的迁移学习(二)(2)
面向自然语言处理的迁移学习(二)
35 1
|
7月前
|
机器学习/深度学习 自然语言处理 算法
面向自然语言处理的迁移学习(一)(5)
面向自然语言处理的迁移学习(一)
60 1
|
7月前
|
机器学习/深度学习 自然语言处理 算法
面向自然语言处理的迁移学习(三)(5)
面向自然语言处理的迁移学习(三)
52 0
|
7月前
|
机器学习/深度学习 自然语言处理 数据可视化
面向自然语言处理的迁移学习(三)(2)
面向自然语言处理的迁移学习(三)
59 0
|
7月前
|
机器学习/深度学习 自然语言处理 数据可视化
面向自然语言处理的迁移学习(二)(1)
面向自然语言处理的迁移学习(二)
45 0
|
7月前
|
机器学习/深度学习 自然语言处理 算法
面向自然语言处理的迁移学习(一)(4)
面向自然语言处理的迁移学习(一)
58 0
|
25天前
|
自然语言处理 API C++
阿里通义推出SmartVscode插件,自然语言控制VS Code,轻松开发应用,核心技术开源!
SmartVscode插件深度解析:自然语言控制VS Code的革命性工具及其开源框架App-Controller