电子说
在16.1 节中,我们讨论了情感分析的问题。该任务旨在将单个文本序列分类为预定义的类别,例如一组情感极性。然而,当需要决定一个句子是否可以从另一个句子推断出来,或者通过识别语义等同的句子来消除冗余时,知道如何对一个文本序列进行分类是不够的。相反,我们需要能够对成对的文本序列进行推理。
16.4.1。自然语言推理
自然语言推理研究是否可以从前提中推断出假设,其中两者都是文本序列。换句话说,自然语言推理决定了一对文本序列之间的逻辑关系。这种关系通常分为三种类型:
蕴涵:假设可以从前提中推导出来。
矛盾:可以从前提推导出假设的否定。
中性:所有其他情况。
自然语言推理也称为识别文本蕴含任务。例如,下面的一对将被标记为 蕴涵,因为假设中的“示爱”可以从前提中的“互相拥抱”中推导出来。
前提:两个女人互相拥抱。
假设:两个女人正在秀恩爱。
下面是一个矛盾的例子,因为“running the coding example”表示“not sleeping”而不是“sleeping”。
前提:一个人正在运行来自 Dive into Deep Learning 的编码示例。
假设:这个人正在睡觉。
第三个例子显示了一种中立关系,因为“为我们表演”这一事实不能推断出“著名”或“不著名”。
前提:音乐家正在为我们表演。
假设:音乐家很有名。
自然语言推理一直是理解自然语言的中心话题。它享有从信息检索到开放域问答的广泛应用。为了研究这个问题,我们将从调查一个流行的自然语言推理基准数据集开始。
16.4.2。斯坦福自然语言推理 (SNLI) 数据集
斯坦福自然语言推理 (SNLI) 语料库是超过 500000 个带标签的英语句子对的集合 (Bowman等人,2015 年)。我们将提取的 SNLI 数据集下载并存储在路径中../data/snli_1.0。
import os import re import torch from torch import nn from d2l import torch as d2l #@save d2l.DATA_HUB['SNLI'] = ( 'https://nlp.stanford.edu/projects/snli/snli_1.0.zip', '9fcde07509c7e87ec61c640c1b2753d9041758e4') data_dir = d2l.download_extract('SNLI')
Downloading ../data/snli_1.0.zip from https://nlp.stanford.edu/projects/snli/snli_1.0.zip...
import os import re from mxnet import gluon, np, npx from d2l import mxnet as d2l npx.set_np() #@save d2l.DATA_HUB['SNLI'] = ( 'https://nlp.stanford.edu/projects/snli/snli_1.0.zip', '9fcde07509c7e87ec61c640c1b2753d9041758e4') data_dir = d2l.download_extract('SNLI')
16.4.2.1。读取数据集
原始 SNLI 数据集包含的信息比我们在实验中真正需要的信息丰富得多。因此,我们定义了一个函数read_snli 来仅提取部分数据集,然后返回前提、假设及其标签的列表。
#@save def read_snli(data_dir, is_train): """Read the SNLI dataset into premises, hypotheses, and labels.""" def extract_text(s): # Remove information that will not be used by us s = re.sub('\(', '', s) s = re.sub('\)', '', s) # Substitute two or more consecutive whitespace with space s = re.sub('\s{2,}', ' ', s) return s.strip() label_set = {'entailment': 0, 'contradiction': 1, 'neutral': 2} file_name = os.path.join(data_dir, 'snli_1.0_train.txt' if is_train else 'snli_1.0_test.txt') with open(file_name, 'r') as f: rows = [row.split('t') for row in f.readlines()[1:]] premises = [extract_text(row[1]) for row in rows if row[0] in label_set] hypotheses = [extract_text(row[2]) for row in rows if row[0] in label_set] labels = [label_set[row[0]] for row in rows if row[0] in label_set] return premises, hypotheses, labels
#@save def read_snli(data_dir, is_train): """Read the SNLI dataset into premises, hypotheses, and labels.""" def extract_text(s): # Remove information that will not be used by us s = re.sub('\(', '', s) s = re.sub('\)', '', s) # Substitute two or more consecutive whitespace with space s = re.sub('\s{2,}', ' ', s) return s.strip() label_set = {'entailment': 0, 'contradiction': 1, 'neutral': 2} file_name = os.path.join(data_dir, 'snli_1.0_train.txt' if is_train else 'snli_1.0_test.txt') with open(file_name, 'r') as f: rows = [row.split('t') for row in f.readlines()[1:]] premises = [extract_text(row[1]) for row in rows if row[0] in label_set] hypotheses = [extract_text(row[2]) for row in rows if row[0] in label_set] labels = [label_set[row[0]] for row in rows if row[0] in label_set] return premises, hypotheses, labels
现在让我们打印前 3 对前提和假设,以及它们的标签(“0”、“1”和“2”分别对应“蕴含”、“矛盾”和“中性”)。
train_data = read_snli(data_dir, is_train=True) for x0, x1, y in zip(train_data[0][:3], train_data[1][:3], train_data[2][:3]): print('premise:', x0) print('hypothesis:', x1) print('label:', y)
premise: A person on a horse jumps over a broken down airplane . hypothesis: A person is training his horse for a competition . label: 2 premise: A person on a horse jumps over a broken down airplane . hypothesis: A person is at a diner , ordering an omelette . label: 1 premise: A person on a horse jumps over a broken down airplane . hypothesis: A person is outdoors , on a horse . label: 0
train_data = read_snli(data_dir, is_train=True) for x0, x1, y in zip(train_data[0][:3], train_data[1][:3], train_data[2][:3]): print('premise:', x0) print('hypothesis:', x1) print('label:', y)
premise: A person on a horse jumps over a broken down airplane . hypothesis: A person is training his horse for a competition . label: 2 premise: A person on a horse jumps over a broken down airplane . hypothesis: A person is at a diner , ordering an omelette . label: 1 premise: A person on a horse jumps over a broken down airplane . hypothesis: A person is outdoors , on a horse . label: 0
训练集约550000对,测试集约10000对。下图表明“蕴含”、“矛盾”、“中性”这三个标签在训练集和测试集上都是均衡的。
test_data = read_snli(data_dir, is_train=False) for data in [train_data, test_data]: print([[row for row in data[2]].count(i) for i in range(3)])
[183416, 183187, 182764] [3368, 3237, 3219]
test_data = read_snli(data_dir, is_train=False) for data in [train_data, test_data]: print([[row for row in data[2]].count(i) for i in range(3)])
[183416, 183187, 182764] [3368, 3237, 3219]
16.4.2.2。定义用于加载数据集的类
下面我们继承DatasetGluon中的类定义一个加载SNLI数据集的类。类构造函数中的参数num_steps指定文本序列的长度,以便每个小批量序列具有相同的形状。换句话说,num_steps较长序列中第一个之后的标记被修剪,而特殊标记“”将附加到较短的序列,直到它们的长度变为num_steps. 通过实现该__getitem__ 功能,我们可以任意访问前提、假设和带有索引的标签idx。
#@save class SNLIDataset(torch.utils.data.Dataset): """A customized dataset to load the SNLI dataset.""" def __init__(self, dataset, num_steps, vocab=None): self.num_steps = num_steps all_premise_tokens = d2l.tokenize(dataset[0]) all_hypothesis_tokens = d2l.tokenize(dataset[1]) if vocab is None: self.vocab = d2l.Vocab(all_premise_tokens + all_hypothesis_tokens, min_freq=5, reserved_tokens=['']) else: self.vocab = vocab self.premises = self._pad(all_premise_tokens) self.hypotheses = self._pad(all_hypothesis_tokens) self.labels = torch.tensor(dataset[2]) print('read ' + str(len(self.premises)) + ' examples') def _pad(self, lines): return torch.tensor([d2l.truncate_pad( self.vocab[line], self.num_steps, self.vocab['']) for line in lines]) def __getitem__(self, idx): return (self.premises[idx], self.hypotheses[idx]), self.labels[idx] def __len__(self): return len(self.premises)
#@save class SNLIDataset(gluon.data.Dataset): """A customized dataset to load the SNLI dataset.""" def __init__(self, dataset, num_steps, vocab=None): self.num_steps = num_steps all_premise_tokens = d2l.tokenize(dataset[0]) all_hypothesis_tokens = d2l.tokenize(dataset[1]) if vocab is None: self.vocab = d2l.Vocab(all_premise_tokens + all_hypothesis_tokens, min_freq=5, reserved_tokens=['']) else: self.vocab = vocab self.premises = self._pad(all_premise_tokens) self.hypotheses = self._pad(all_hypothesis_tokens) self.labels = np.array(dataset[2]) print('read ' + str(len(self.premises)) + ' examples') def _pad(self, lines): return np.array([d2l.truncate_pad( self.vocab[line], self.num_steps, self.vocab['']) for line in lines]) def __getitem__(self, idx): return (self.premises[idx], self.hypotheses[idx]), self.labels[idx] def __len__(self): return len(self.premises)
16.4.2.3。把它们放在一起
现在我们可以调用read_snli函数和SNLIDataset 类来下载 SNLI 数据集并返回DataLoader训练集和测试集的实例,以及训练集的词汇表。值得注意的是,我们必须使用从训练集中构造的词汇作为测试集的词汇。因此,测试集中的任何新标记对于在训练集上训练的模型都是未知的。
#@save def load_data_snli(batch_size, num_steps=50): """Download the SNLI dataset and return data iterators and vocabulary.""" num_workers = d2l.get_dataloader_workers() data_dir = d2l.download_extract('SNLI') train_data = read_snli(data_dir, True) test_data = read_snli(data_dir, False) train_set = SNLIDataset(train_data, num_steps) test_set = SNLIDataset(test_data, num_steps, train_set.vocab) train_iter = torch.utils.data.DataLoader(train_set, batch_size, shuffle=True, num_workers=num_workers) test_iter = torch.utils.data.DataLoader(test_set, batch_size, shuffle=False, num_workers=num_workers) return train_iter, test_iter, train_set.vocab
#@save def load_data_snli(batch_size, num_steps=50): """Download the SNLI dataset and return data iterators and vocabulary.""" num_workers = d2l.get_dataloader_workers() data_dir = d2l.download_extract('SNLI') train_data = read_snli(data_dir, True) test_data = read_snli(data_dir, False) train_set = SNLIDataset(train_data, num_steps) test_set = SNLIDataset(test_data, num_steps, train_set.vocab) train_iter = gluon.data.DataLoader(train_set, batch_size, shuffle=True, num_workers=num_workers) test_iter = gluon.data.DataLoader(test_set, batch_size, shuffle=False, num_workers=num_workers) return train_iter, test_iter, train_set.vocab
这里我们将批量大小设置为 128,将序列长度设置为 50,并调用该load_data_snli函数来获取数据迭代器和词汇表。然后我们打印词汇量。
train_iter, test_iter, vocab = load_data_snli(128, 50) len(vocab)
read 549367 examples read 9824 examples
18678
train_iter, test_iter, vocab = load_data_snli(128, 50) len(vocab)
read 549367 examples read 9824 examples
18678
现在我们打印第一个小批量的形状。与情绪分析相反,我们有两个输入X[0],X[1]代表成对的前提和假设。
for X, Y in train_iter: print(X[0].shape) print(X[1].shape) print(Y.shape) break
torch.Size([128, 50]) torch.Size([128, 50]) torch.Size([128])
for X, Y in train_iter: print(X[0].shape) print(X[1].shape) print(Y.shape) break
(128, 50) (128, 50) (128,)
16.4.3。概括
自然语言推理研究是否可以从前提中推断出假设,其中两者都是文本序列。
在自然语言推理中,前提和假设之间的关系包括蕴涵、矛盾和中性。
斯坦福自然语言推理 (SNLI) 语料库是一种流行的自然语言推理基准数据集。
16.4.4。练习
长期以来,机器翻译的评估都是基于肤浅的 n- 输出翻译和真值翻译之间的语法匹配。你能设计一个使用自然语言推理来评估机器翻译结果的方法吗?
我们如何改变超参数来减少词汇量?
全部0条评论
快来发表一下你的评论吧 !