AllenNLP框架学习笔记(数据篇之二)

本篇对AllenNLP中关于数据中的 Datasets,vocabulary,DataLoader,sampler做简单的介绍。

Datasets

数据集被表示为AllennlpDataset对象,是instance集合的一个简单封装,并且与PyTorchDataset基本上相同,不同之处在于它还支持某些额外的功能,例如词汇索引。 AllenNLPDatasetReader在完成读取数据集后返回AllennlpDataset()或者AllennlpLazyDataset()

Vocabulary

Vocabulary,词汇表,是AllenNLP中的重要组件,它常常被其他抽象类与组件使用。 简而言之,Vocabulary管理从字符串到整数ID的映射。 Vocabulary的输入是Instances,用于将文本数据(例如token和标签)转换为整数ID(并最终转换为张量)。

词汇表使用namespace的概念管理不同的映射。 每个namespace都是从字符串到整数的独特映射,因此不同namespace中的字符串将被分别处理。 这样,使用者就可以为单词“a”和字符“ a”或英语中的“ chat”和法语中的“ chat”(英语中的“ cat”)分别设置索引。 如下图所示:

namespace之间有一个重要的区别:paddednon-paddednamespace默认是padded的,为填充和词汇表外(OOV)的token保留映射索引。这对于为文本创建索引时十分有用,因为出现词表外的token十分常见,而且填充也是必须的。

另一方面,non-padded namespace不保留特殊token的映射索引,这更适用于类别标签之类的情况,以“tags”或“labels”结尾的namespace默认为non-padded,但是可以通过在创建vocabulary时提供non_padded_namespaces参数来修改此行为。

Vocabulary实例化的方法有以下三种:

  • 默认方法,from_instances,从Instance集合实例化Vocabulary

  • from_files 的类方法可以从序列化的词汇表进行加载。序列化的词汇表通常是由allennlp之前训练产生的,对应配置文件的type为“from_files”;

  • from_files_and_instances,使用instance集合扩展已经生成的词汇表,对应配置文件的type为“extend”。

其中默认的from_instances方法是指将Instance的集合传递给Vocabulary.from_instances()方法。这将对Instance中需要映射为整数的所有字符串进行频数统计,然后根据统计结果来决定词汇表中应包含哪些字符串。get_token_index()方法通过token查找索引,get_token_from_index()是逆运算(通过索引查找token)。示例代码如下:

from allennlp.data.instance import Instance
from allennlp.data.fields import TextField, LabelField
from allennlp.data.token_indexers import SingleIdTokenIndexer
from allennlp.data.tokenizers import Token
from allennlp.data.vocabulary import Vocabulary

# 创建fields,并标明namespace
token_indexers = {'tokens': SingleIdTokenIndexer(namespace='tokens')}
text_field_pos = TextField(
    [Token('The'), Token('best'), Token('movie'), Token('ever'), Token('!')],
    token_indexers=token_indexers)
text_field_neg = TextField(
    [Token('Such'), Token('an'), Token('awful'), Token('movie'), Token('.')],
    token_indexers=token_indexers)

label_field_pos = LabelField('pos', label_namespace='labels')
label_field_neg = LabelField('neg', label_namespace='labels')

# 创建正例与负例两个instance
instance_pos = Instance({'tokens': text_field_pos, 'label': label_field_pos})
instance_neg = Instance({'tokens': text_field_neg, 'label': label_field_neg})

# 创建Vocabulary
vocab = Vocabulary.from_instances([instance_pos, instance_neg])
print('Created a Vocabulary:', vocab)

# 根据单词查找索引,默认情况下namespace为'tokens'。vocab按频率从大到小添加词汇项,因此出现两次的“movie”将获得除了@@PADDING@@和@@UNKNOWN@@之外最小的索引
print('index for token "movie":', vocab.get_token_index('movie'))
print('index for token "!":', vocab.get_token_index('!'))

# 'tokens'的namespace为padded,因此oov与padding也有对应的索引
print('index for token "unknown":', vocab.get_token_index('unknown'))
# index for token "unknown": 1
print('index for token "padding":', vocab.get_token_index('@@PADDING@@'))
# index for token "padding": 0

print('index for label "pos":', vocab.get_token_index('pos', namespace='labels'))
print('index for label "neg":', vocab.get_token_index('neg', namespace='labels'))

# 'labels' 是 non-padded namespace; 因此在查找词表外的单词时会抛出异常
try:
    vocab.get_token_index('unknown', namespace='labels')
except KeyError:
    print('index for label "unknown": caught KeyError')

# 根据索引查看token
print('token for index=0:', vocab.get_token_from_index(0))
print('token for index=1:', vocab.get_token_from_index(1))
print('token for index=2:', vocab.get_token_from_index(2))

print('label for index=0:', vocab.get_token_from_index(0, namespace='labels'))
print('label for index=1:', vocab.get_token_from_index(1, namespace='labels'))

try:
    vocab.get_token_from_index(2, namespace='labels')
except KeyError:
    print('label for index=2: caught KeyError')


当词汇表量级非常大时,可以通过使用min_count 参数设置一个阈值并只保留出现频率高于该阈值的单词来对其进行删减,样例如下:

vocab = Vocabulary.from_instances([instance_pos, instance_neg], min_count={'tokens': 2})
DataLoader

DataLoader的输入是数据集,一个Dataset对象,并在该数据集上生成一个iterable。默认情况下,它按原始顺序生成单个instance,但可以为DatasetLoader提供各种选项,从而自定义它如何迭代、采样(和/或)批处理instance。例如,如果设置batch_size参数,它将生成指定大小的批次。还可以通过提供shuffle=True来打乱数据集。

AllenNLPDataLoaderPyTorchDataLoader的一个非常简单的子类,主要区别在于一个定制的collate函数,这个函数在PyTorch是定义如何获取instance并将它们进行批处理。在allennlp中,collate函数中创建一批instance并将其转换为张量字典,并应用适当的填充:

def allennlp_collate(instances: List[Instance]) -> TensorDict:
    batch = Batch(instances)
    return batch.as_tensor_dict(batch.get_padding_lengths())

sampler

即采样器,指定如何迭代给定数据集中的实例,用户可以为DatasetLoader的批处理参数提供一个sampler,以便进一步自定义其行为。常用的sampler如下:

  • SequentialSampler按原始顺序顺序对实例进行采样
  • RandomSampler随机采样实例,重要参数为replacement,默认为False,代表是否重复采样。
  • BatchSampler 包装另一个采样器,并生成由底层采样器生成的小批量实例。

示例代码如下:

from allennlp.data import DataLoader, PyTorchDataLoader
from allennlp.data import Instance, Token
from allennlp.data.token_indexers import SingleIdTokenIndexer
from allennlp.data.dataset_readers.dataset_reader import AllennlpDataset
from allennlp.data.fields import LabelField, TextField
from allennlp.data.samplers import SequentialSampler, RandomSampler
from allennlp.data.samplers import BasicBatchSampler
from allennlp.data.vocabulary import Vocabulary

# 创建迷你dataset
token_indexers = {'tokens': SingleIdTokenIndexer()}
instances = [Instance({'label': LabelField(str(label)),'text': TextField([Token(label)], token_indexers = token_indexers)}) for label in 'abcdefghij']
dataset = AllennlpDataset(instances)
vocab = Vocabulary.from_instances(dataset)
dataset.index_with(vocab)

# 批处理默认情况
print("Default:")
data_loader = PyTorchDataLoader(dataset, batch_size=3)
for batch in data_loader:
    print(batch)

# drop_last=True,如果最后一个批次小于批次大小,则丢弃
sampler = SequentialSampler(data_source=dataset)
batch_sampler = BasicBatchSampler(sampler, batch_size=3, drop_last=True)

print("\nDropping last:")
data_loader = PyTorchDataLoader(dataset, batch_sampler=batch_sampler)
for batch in data_loader:
    print(batch)

# 使用随机采样
sampler = RandomSampler(data_source=dataset)
batch_sampler = BasicBatchSampler(sampler, batch_size=3, drop_last=False)

print("\nWith RandomSampler:")

data_loader = PyTorchDataLoader(dataset, batch_sampler=batch_sampler)
for batch in data_loader:
    print(batch)

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值