Pytorch学习-torchtext

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.Exampletorchtext.data.Field 处理成一条example,表示一个样本(数据+标签);
使用 torchtext.data.Datasettorchtext.data.Example 数据集类,处理成数据集,也可对数据集进行划分等工作,getitem返回 Example实例;
使用迭代器: torchtext.data.Iteratorstorchtext.data.Dataset 按照 batch_size 组装成 Batch 供模型训练使用;
使用 torchtext.data.vocabtorchtext.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预处理流程

  1. 定义样本的处理操作 ->> torchtext.data.Field
  2. 加载 corpus (string) ->> torchtext.data.Datasets
    1.在 Datasets 中,torchtextcorpus 处理成一个个的 torchtext.data.Example 实例
    2.创建 torchtext.data.Example 的时候,会调用 field.preprocess
  3. 创建词汇表 Vocab,用来将 string token 转成 index ->> field.bulid_vocab()
    1.词汇表: string token ->> index, index ->>string tokenstring token ->> word vector
  4. 将处理后的数据 进行 batch 操作。 ->> torchtext.data.Iterator
    1.将 Datasets 中的数据 batch
    2.其中会包含一些 pad 操作,保证一个 batch 中的 example长度一致
    3.在这里将 string token 转化成 index

tokenization , vocab, numericalize, embedding lookup 和torch text 数据预处理流程对应关系:

  • tokenization ->> Dataset 的构造函数中,由 Fieldtokenize操作
  • 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内的所有句子会 padbatch 内最长的句子长度。
batch.Textbatch.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.
Fieldvocab 属性保存了 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)

五、参考资料

pytorch学习笔记(十九):torchtext
torchtext 使用

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值