torchtext
仅供个人学习使用,参考总结了一些文档,在下面内容中会注释。
文章目录
前言
torchtext的学习,为后面的Pytorch-CNN以及Pytorch-LSTM模型搭建做准备
常见的数据预处理:
Load File
:加载数据文件;
Tokenization
:分词;
Create Vocabulary
:创建字典;
Indexify
:将词与索引进行映射;
Word Vectors
:创建或加载词向量;
Padding or Fix Length
:按长度对文本进行补齐或截取;
Dataset Splits
:划分数据集(如将数据集划分为训练集、验证集、测试集);
Batching and Iterators
:将数据集按固定大小划分成 Batch;
torchtext中主要的类的作用
使用 torchtext.data.Field
定义样本各个field的处理流程(分词、数据预处理等),创建Example时的预处理,batch时的一些处理操作;
使用 torchtext.data.Example
将 torchtext.data.Field
处理成一条example,表示一个样本(数据+标签);
使用 torchtext.data.Dataset
将 torchtext.data.Example
数据集类,处理成数据集,也可对数据集进行划分等工作,getitem返回 Example实例;
使用迭代器: torchtext.data.Iterators
将 torchtext.data.Dataset
按照 batch_size
组装成 Batch
供模型训练使用;
使用 torchtext.data.vocab
和 torchtext.data.Vectors
创建词典、词和索引的一一对应、下载或使用预训练的词向量等;
一、torchtext.data
1.torchtext.data.Field
代码如下(Field):
class torchtext.data.Field(
sequential=True, use_vocab=True, init_token=None, eos_token=None, fix_length=None,
dtype=torch.int64, preprocessing=None, postprocessing=None, lower=False, tokenize=None,
tokenizer_language='en', include_lengths=False, batch_first=False, pad_token='<pad>',
unk_token='<unk>', pad_first=False, truncate_first=False, stop_words=None, is_target=False
):
'''
定义一个数据类型以及用于转换为Tensor的指令。
Field 类对可以由张量表示的常见文本处理数据类型进行建模。
它包含一个Vocab对象,该对象定义了字段元素及其对应的数字表示形式的可能值的集合。
Field对象还保存与数据类型应该如何数字化有关的其他参数,例如 tokenization 方法和应生成的Tensor的类型。
如果数据集中的两个列之间共享一个 Field,那么就共享 vocabulary。
sequential – 是否是序列化数据,如果是False, 不需要指定 tokenize 默认值: True。
use_vocab – 是否使用Vocab object. 如果是False, field中的数据必须已经是数值类型. 默认值: True。
init_token – 每条数据的起始字符 如果没有初始令牌则None,默认值: None。
eos_token – 每条数据的结尾字符 默认值: None。(同上)
fix_length – 每条数据的固定长度,不够的用pad_token补全. 默认值: None 不固定。
dtype – Default: torch.dtype 类 表示这类数据的一批样例,默认:torch.long。
preprocessing – 在tokenizing之后和numericalizing之前使用的pipeline, 默认值: None。
postprocessing – numericalizing 之后,转化成 tensor 之前使用的pipline默认值: None。
lower – 是否把数据转化为小写 默认值: False。
tokenize – 分词函数,如果是spacy,则使用SpaCy tokenizer。如果一个非序列化函数作为参数传递,则该field将无法序列化 默认值: str.split。
tokenizer_language – 目前仅在SpaCy中支持各种语言。 默认值: 'en'.
include_lengths – 是否返回一个已经补全的最小batch的元组和一个包含每条数据长度的列表 . 默认值: False。
batch_first – 是否先 生成batch尺寸的Tensor。默认值:False。
pad_token – 用于补全的字符. 默认值: '<pad>'。
unk_token – 不存在词典里的字符. 默认值:'<unk>'。
pad_first – 是否补全第一个字符. 默认值: False
truncate_first – 在开始时填充序列。默认值:False。
stop_words – 要在preprocessing步骤中丢弃的tokens。默认值:False。
is_target – filed是否为目标变量。影响batch迭代,默认值:False。
'''
def build_vocab(*args, **kwargs)
'''
从一个或多个数据集中为此字段构造Vocab对象。
'''
def numericalize(arr, device=None)
'''
将使用此 Field 的一批样本转换为变量。
arr (List[List[str]], or tuple of (List[List[str]], List[int]))
– 如果self.include_lengths is True。tokenized和padded的examples列表,或tokenized和padded的 examples列表元组,以及每个example的长度列表
– if self.include_lengths=True, tuple of List of tokenized and padded examples and List of lengths of each example.
device (str or torch.device) – Default: None (for cpu).
'''
def pad(minibatch)
'''
填充到self.fix_length或者此batch的最大长度。
前缀self.init_token 后缀self.eos_token。
if self.include_lengths=True,返回 (the padded list, length list).
if self.include_lengths=False,返回 the padded list.
if self.sequential=False,没有 pad.
'''
def preprocess(x)
'''
处理单个样本
if sequential=True,tokenize the input.
if lower=True,小写化.
然后传入用户定义的 preprocessing.
'''
def process(batch, device=None)
'''
pad, numericalize, and postprocess a batch and create a tensor.
'''
build_vocab
内置的预训练词向量:
Field_instance.build_vocab(train, vectors=data.vocab.GloVe(name='6B', dim=300))
Field_instance.build_vocab(train, vectors="glove.6B.300d")
本地的预训练词向量:
vectors = Vectors(name='myvector/glove/glove.6B.200d.txt')
TEXT.build_vocab(train, vectors=vectors)
通过name参数可以指定预训练词向量文件所在的路径。
vectors = Vectors(name='glove.6B.200d.txt', cache='myvector/glove/')
# 指定Vector缺失值的初始化方式,没有命中的token的初始化方式
vectors.unk_init = init.uniform_
TEXT.build_vocab(train, vectors=vectors)
通过cache参数指定缓存目录。
处理部分流程顺序:tokenizing -> lower -> preprocessing -> numericalizing -> postprocessing -> turn-into-Tensor.
demo
dataset
0 Tourists can wear skirts or dresses in India.
0 They were talking about the pools at the Maria Lenk Aquatics Centre.
0 There is Blank Space in Taylor Swift 's bank account.
0 Congress will pass a comprehensive measure.
1 Turkey 's legal quest for return of relics beginning to bear fruit.
1 Two former Brazilian presidents on Tuesday admitted allegations of corruption.
1 Additional troops will be sent to Afghanistan.
1 China and Mexico have tremendous cooperation potential in the e_commerce sector.
1 Donald Trump met with Russians during his father 's 2016 presidential campaign.
1 Some 700,000 people have evacuated from Cuba 's northeastern coast.
...
code
# 分词函数
def content_tokenize(text):
return [item for item in str(text)]
# 创建content字段的Field
CONTENT = data.Field(sequential=True, tokenize=content_tokenize, batch_first=True)
# 创建label字段的Field
LABEL = data.Field(sequential=False, use_vocab=False)
# 为 CONTENT 字段创建词向量
CONTENT.build_vocab(data_set)
CONTENT.vocab.stoi
需要保存起来,供以后使用。
If sequential=True
,那么需要定义 tokenize
,默认为 split()
。那如果是中文呢,可以自定义 tokenize
。
demo 中文分词工具
# 设置Field
from torchtext import data
question = data.Field(sequential=True, fix_length=20, pad_token='0')
label = data.Field(sequential=False, use_vocab=False)
import jieba
def chinese_tokenizer(text):
return [tok for tok in jieba.lcut(text)]
question = data.Field(sequential=True, tokenize=chinese_tokenizer, fix_length=20)
2.torchtext.data.Example
代码如下(Example):
class torchtext.data.Example:
'''
定义单个训练或测试样本。 将样本的每一列存储为属性。
'''
@classmethod
def fromCSV(data, fields, field_to_index=None)
@classmethod
def fromJSON(data, fields)
@classmethod
def fromdict(data, fields)
@classmethod
def fromlist(data, fields)
@classmethod
def fromtree(data, fields, subtrees=False)
3.torchtext.data.Dataset
class torchtext.data.Dataset(examples, fields, filter_pred=None):
'''
定义由Examples及其Fields组成的数据集
examples – Examples列表.
fields (List(tuple(str, Field))) – 这个元组中要使用的Fields。字符串是field名,Field是关联field。
filter_pred (callable or None) – 只使用filter_pred(example)为True的examples,如果为None,则使用所有examples。默认是没有的.
'''
~Dataset.sort_key (callable) – 用于对数据集example进行排序的键,以便将长度相似的examples,batching在一起,以最小化padding.
~Dataset.examples (list(Example)) – 数据集中的examples.
~Dataset.fields (dict[str, Field]) – 包含每列或field的名称,以及相应的Field object。具有相同Field object的两个fields将具有共享的词汇表.
def split(split_ratio=0.7, stratified=False, strata_field='label', random_state=None)
'''
划分 train-test(-valid?)
split_ratio (float or List of python:floats) – 一个数字[0,1],表示用于训练split的数据量(剩下的用于验证),或者一个数字列表,分别表示训练、测试和有效splits的相对大小。如果缺少valid的相对大小,则只返回train-test split。默认值是0.7(对于训练集).
stratified (bool) – 采样是否分层。默认是False.
strata_field (str) – Examples Field的名称分层。默认为常规标签field的 ’label’.
random_state (tuple) – 用于shuffling的随机种子。random.getstate()的返回值.
'''
@classmethod
def splits(path=None, root='.data', train=None, validation=None, test=None, **kwargs):
'''
为数据集的多个拆分创建数据集对象。
path (str) – 数据存在的目录, None to use the result of cls.download(root).
root (str) – Root dataset storage directory. Default is ‘.data’,根目录.
train (str) – path+train就是train set的完整路径, or None for no train set. Default is None.
validation (str) – path+validation就是validation set的完整路径, or None for no validation set. Default is None.
test (str) – path+test就是test set的完整路径, or None for no test set. Default is None.
keyword arguments (Remaining) – 传给本类的关键字参数。
'''
demo
# 读取数据
df = pd.read_csv('./datas/data.txt', sep=' ', encoding='utf8', names=['label', 'content'])
# 创建Field的list
fields = [('label', LABEL), ('content', CONTENT)]
examples = []
for label, content in zip(df['label'], df['content']):
examples.append(Example.fromlist([label, content], fields))
# 创建数据集
data_set = Dataset(examples, fields)
# 划分数据集为 train 和 valid
train_dataset, valid_dataset = data_set.split(0.6)
print('data_set length:{}'.format(len(data_set.examples)))
print('train_dataset length:{}'.format(len(train_dataset.examples)))
print('valid_dataset length:{}'.format(len(valid_dataset.examples)))
4.torchtext.data.TabularDataset
class torchtext.data.TabularDataset(
path, format, fields, skip_header=False, csv_reader_params={}, **kwargs
):
'''
定义以CSV,TSV或JSON格式存储的列的数据集。
path (str) – 数据文件路径.
format (str) – 文件格式不区分大小写.
fields (list(tuple(str, Field)):
– 如果用 list,format 必须是 csv 或 tsv,list 的 值应该是 tuples of (name, field)。fields 的顺序和 csv/tsv 文件的顺序相同,(name, None) 表示被忽略的列。
– 如果用 dict,keys 应该是Json keys 或 csv/tsv columns 的子集,且值应该是 tuples of (name, field)。不在字典中的值会被忽略,允许用户重命名 columns,和只加载子集。
skip_header (bool) – 是否跳过第一行。由header一定要传入次参数。
csv_reader_params (dict) – 传给 csv/tsv 读取的参数。
'''
5.torchtext.data.Iterator
class torchtext.data.Iterator(
dataset, batch_size, sort_key=None, device=None, batch_size_fn=None,
train=True, repeat=False, shuffle=None, sort=None, sort_within_batch=None
):
'''
# 既是参数也是属性
Iterator.dataset – 加载 Examples 的 Dataset object
Iterator.batch_size – Batch size
Iterator.batch_size_fn – 3个arguments的函数(要添加的新example, 当前batch的example数, 当前有效的batch size),添加新example,返回新的batch size. 用于 dynamic batching。
Iterator.sort_key – 用来为每个 Example 进行排序的字段.
Iterator.train – 是否是训练集
Iterator.repeat – 是否在不同epoch中重复迭代,默认值:False.
Iterator.shuffle – 是否在 epochs 间打乱顺序.
Iterator.sort – 是否根据 self.sort_key 排序.
Iterator.sort_within_batch – 是否在 batch 内 按 sort_key 降序排列.
Iterator.device (str or torch.device) –. Default: None for cpu.
'''
@classmethod
def splits(datasets, batch_sizes=None, **kwargs):
'''
为数据集的拆分 train-test(-valid?) 创建多个 Iterator 对象。
datasets – Tuple of splited Dataset objects. 第一个元素是 train set.
batch_sizes – Tuple of batch sizes, None 表示使用相同的大小
keyword arguments (Remaining) – 传给构造器的参数
6.torchtext.data.BucketIterator
class torchtext.data.BucketIterator(
dataset, batch_size, sort_key=None, device=None, batch_size_fn=None,
train=True, repeat=False, shuffle=None, sort=None, sort_within_batch=None
):
'''
该迭代器将相似长度的样本批处理在一起。
这样的 batch 在 padding 是的总填充会最小。
'''
demo
from torchtext.data import Iterator, BucketIterator
# 若只对训练集构造迭代器
# train_iter = data.BucketIterator(dataset=train, batch_size=8, shuffle=True, sort_within_batch=False, repeat=False)
# 若同时对训练集和验证集进行迭代器构建
train_iter, val_iter = BucketIterator.splits(
(train, valid),
batch_size=(8, 8),
device=-1, # 如果使用gpu,将-1更换为GPU的编号
sort_key=lambda x: len(x.comment_text),
sort_within_batch=False,
repeat=False
)
# 在测试集中一般不想改变样本顺序,因此测试集使用Iterator迭代器来构建。
test_iter = Iterator(test, batch_size=8, device=-1, sort=False, sort_within_batch=False, repeat=False)
for idx, batch in enumerate(train_iter):
text, label = batch.comment_text, batch.toxic
二、torchtext.vocab
1.torchtext.vocab.Vocab
class torchtext.vocab.Vocab(
counter, max_size=None, min_freq=1, specials=('<unk>', '<pad>'),
vectors=None, unk_init=None, vectors_cache=None, specials_first=True
):
'''
定义将用于数字化 field 的 vocabulary 对象。
counter – collections.Counter object,计数器
max_size – vocabulary 的最大容量, None 不设限. Default: None.
min_freq – vocabulary 的最小频次. Default: 1.
specials – special tokens. Default: [‘<unk’>, ‘<pad>’]
vectors – 词向量
unk_init (callback) – unk 向量化方式. Default: torch.Tensor.zero_
vectors_cache – vectors 的缓存目录. Default: ‘.vector_cache’
specials_first – special tokens 在前面. Default: True.
'''
~Vocab.freqs – A collections.Counter
~Vocab.stoi – A collections.defaultdict
~Vocab.itos – A list of token strings
2.torchtext.vocab.Vectors
class torchtext.vocab.Vectors(name, cache=None, url=None, unk_init=None, max_vectors=None):
'''
name – name of the file 文件名一般结结构为 corpus.name.dim
cache – cached vectors 的缓存目录
url – url for download if vectors not found in cache
unk_init (callback) – unk 默认零化
max_vectors (int) – 最大加载词向量数(预训练的词向量都是按频次降序的)
'''
2.torchtext.vocab.GloVe
class torchtext.vocab.GloVe(name='840B', dim=300, **kwargs):
'''
name cache url unk_init max_vectors 同上
glove.42B.300d
glove.840B.300d
glove.twitter.27B.25d
glove.twitter.27B.50d
glove.twitter.27B.100d
glove.twitter.27B.200d
glove.6B.50d
glove.6B.100d
glove.6B.200d
glove.6B.300d
'''
demo
import torch
from torchtext import data
from torchtext import datasets
from torchtext.vocab import GloVe
import numpy as np
def load_data(opt):
print('loading {} dataset'.format(opt.dataset))
text = data.Field(lower=True, include_lengths=True, batch_first=True, fix_length=opt.max_seq_len)
label = data.Field(sequential=False)
train, test = datasets.IMDB.splits(text, label)
text.build_vocab(train, vectors=GloVe(name='6B', dim=300)
label.build_vocab(train)
print('len(TEXT.vocab)', len(text.vocab))
print('TEXT.vocab.vectors.size()', text.vocab.vectors.size())
三、torchtext.datasets
# set up fields
TEXT = data.Field(lower=True, include_lengths=True, batch_first=True)
LABEL = data.Field(sequential=False)
# make splits for data
train, test = datasets.IMDB.splits(TEXT, LABEL)
# build the vocabulary
TEXT.build_vocab(train, vectors=GloVe(name='6B', dim=300))
LABEL.build_vocab(train)
# make iterator for splits
train_iter, test_iter = data.BucketIterator.splits(
(train, test), batch_size=3, device=0)
四、举个栗子
torchtext预处理流程
- 定义样本的处理操作 ->>
torchtext.data.Field
- 加载 corpus (string) ->>
torchtext.data.Datasets
1.在Datasets
中,torchtext
将corpus
处理成一个个的torchtext.data.Example
实例
2.创建torchtext.data.Example
的时候,会调用field.preprocess
。 - 创建词汇表 Vocab,用来将
string token
转成index
->>field.bulid_vocab()
1.词汇表:string token
->>index
,index
->>string token
,string token
->>word vector
- 将处理后的数据 进行 batch 操作。 ->>
torchtext.data.Iterator
1.将Datasets
中的数据batch
化
2.其中会包含一些pad
操作,保证一个batch
中的example
长度一致
3.在这里将string token
转化成index
tokenization
, vocab
, numericalize
, embedding lookup
和torch text 数据预处理流程对应关系:
tokenization
->>Dataset
的构造函数中,由Field
的tokenize
操作vocab
->>field.build_vocab
时,由Field
保存 映射关系numericalize
->> 发生在iterator
准备batch
的时候,由Field
执行numericalize
操作embedding lookup
->> 由pytorch Embedding Layer
提供此功能
Field
首先创建Field对象,Field 可以设置简单处理字段的方法,tokenize
可以自定义
import spacy
spacy_en = spacy.load('en')
def tokenizer(text): # create a tokenizer function
# 返回 a list of <class 'spacy.tokens.token.Token'>
return [tok.text for tok in spacy_en.tokenizer(text)]
TEXT = data.Field(sequential=True, tokenize=tokenizer, lower=True)
# 对内容文本字段,tokenize 可以自定义,默认是 str.split()
LABEL = data.Field(sequential=False, use_vocab=False)
# 对 label 字段,要设置 sequential=False,已经数值化了要设置 use_vocab=False
Dataset
然后通过 torchtext.data.Dataset
的类方法 splits
加载所有的语料库,不用的列设置 None:(假设我们有三个语料库,train.tsv
, val.tsv
, test.tsv
)
加载划分tran.tsv 和val.tsv:
train_valid_fields = [("id", None), ("comment_text", TEXT), ("toxic", LABEL),
("severe_toxic", LABEL), ("threat", LABEL), ("obscene", LABEL),
("insult", LABEL), ("identity_hate", LABEL)]
train, val = data.TabularDataset.splits(
path="data", # 目录,splits 的参数由train/test/validation,所以path可以只传目录所在
train='train.tsv',
validation="valid.tsv", # 文件名,这里没有validation也会返回tuple:(trn, )
format='tsv', # 格式
skip_header=True, # 跳过header
fields=train_valid_fields) # 对应字段
加载text:
test_datafields = [("id", None), ("comment_text", TEXT)]
test = data.TabularDataset(
path="data/test.tsv", # 类的参数只有path,没有train/test/validation,所以这里传完整路径
format='tsv',
skip_header=True,
fields=test_datafields)
Vocab
然后构建语料库的 Vocabulary
, 同时,加载预训练的 word-embedding
(TEXT 字段的文本转化为数值)
TEXT.build_vocab(train, vectors="glove.6B.100d")
# Vectors: 用来保存预训练好的 word vectors,将当前
# corpus 词汇表的词向量抽取出来,构成当前 corpus 的 Vocab(词汇表)
Vocab 类 有三个属性:
TEXT.vocab.freqs
TEXT.vocab.itos
TEXT.vocab.stoi
可以用operator[]
查看样本:
train[7]
每个样本都是一个Example
对象,属性也绑在一起
>>> train[0].__dict__.keys()
dict_keys(['comment_text', 'toxic', 'severe_toxic', 'threat', 'obscene', 'insult', 'identity_hate'])
可以用operator[].
查看属性值:
>>> trn[0].comment_text[:3]
['explanation', 'why', 'the']
Iterator
数据加载好了下一步进行batching操作,要送去训练则还要生成可以迭代的 batch,对于 train 和 validation,用 @classmethod splits()
:
train_iter, val_iter = data.BucketIterator.splits(
(train, val),
batch_sizes=(64, 64),
sort_key=lambda x: len(x.comment_text), # 排序依据
# sort=True 默认
sort_within_batch=False,
repeat=False # we pass repeat=False because we want to wrap this Iterator layer.
device = -1
)
batch = next(train_iter.__iter__())
print("batch text: ", batch.Text) # 对应 Fileld 的 name
print("batch label: ", batch.Label)
如果运行在 CPU 上,需要设置 device=-1
, 如果运行在GPU 上,需要设置device=0,1,2...
,torchtext
使用了动态 padding
,意味着 batch
内的所有句子会 pad
成 batch
内最长的句子长度。
batch.Text
和 batch.Label
都是 torch.LongTensor
类型的值,保存的是 index
。
查看 iter 内容:
>>> batch = next(train_iter.__iter__())
>>> batch
[torchtext.data.batch.Batch of size 25]
[.comment_text]:[torch.LongTensor of size 494x25]
[.toxic]:[torch.LongTensor of size 25]
[.severe_toxic]:[torch.LongTensor of size 25]
[.threat]:[torch.LongTensor of size 25]
[.obscene]:[torch.LongTensor of size 25]
[.insult]:[torch.LongTensor of size 25]
[.identity_hate]:[torch.LongTensor of size 25]
查看 属性:
>>> batch.__dict__.keys()
dict_keys(['batch_size', 'dataset', 'fields', 'input_fields', 'target_fields', 'comment_text', 'toxic', 'severe_toxic', 'threat', 'obscene', 'insult', 'identity_hate'])
这些属性同样可以用 operator .
调用
对于 test,没有用 @classmethod splits()
:
test_iter = Iterator(
tst,
batch_size=64,
sort=False, # 对 test 不能排序,label 会乱
sort_within_batch=False,
repeat=False
)
如果想获得 word vector.
Field
的 vocab
属性保存了 word vector 数据,我们可以把这些数据拿出来
然后我们使用 Pytorch 的 Embedding Layer
来解决 embedding lookup
问题。
vocab = TEXT.vocab
self.embed = nn.Embedding(len(vocab), emb_dim)
self.embed.weight.data.copy_(vocab.vectors)