torchtext使用

torchtext预处理流程:

    1.定义Field:声明如何处理数据
    2.定义Dataset:得到数据集,此时数据集里每一个样本是一个 经过 Field声明的预处理 预处理后的 wordlist
    3.建立vocab:在这一步建立词汇表,词向量(word embeddings)
    4.构造迭代器:构造迭代器,用来分批次训练模型

定义Field:

  实例化Field(),就是通过给参数赋值的方式定义想要进行的操作。

from torchtext import data

tokenize = lambda x: x.split()
# fix_length指定每条文本的长度,截断补长
TEXT = data.Field(sequential=True, tokenize=tokenize, lower=True, fix_length=200)
LABEL = data.Field(sequential=False, use_vocab=False)

定义Dataset:

  用Dataset实现Field的操作,得到数据集。

  必须至少传入examples和fields这两个参数:

         examples为由torchtext中的Example对象构造的列表,Example为对数据集中一条数据的抽象。

         fields可简单理解为每一列数据和Field对象的绑定关系。

from torchtext import data
from torchtext.vocab import Vectors
from torch.nn import init
# tqdm就是显示进度条的库
from tqdm import tqdm

train_data = pd.read_csv('data/train_one_label.csv')
valid_data = pd.read_csv('data/valid_one_label.csv')
test_data = pd.read_csv('data/test.csv')
TEXT = data.Field(sequential=True, tokenize=tokenize, lower=True)
LABEL = data.Field(sequential=False, use_vocab=False)

# get_dataset构造并返回Dataset所需的examples和fields
def get_dataset(csv_data, text_field, label_field, test=False):
    fields = [('id', None), ('comment_text', text_field), ('toxic', label_field)]
    examples = []
    
    # 测试集没有label
    if test:
        for text in tqdm(csv_data['comment_text']):
            # 每一行数据都按fields操作
            examples.append(data.Example.fromlist([None, text, None], fields))
    else:
        for text, label in tqdm(zip(csv_data['comment_text'], csv_data['toxic'])):
            examples.append(data.Example.fromlist([None, text, label], fields))
    return examples, fields

# 得到构建Dataset所需的examples和fields
train_examples, train_fields = get_dataset(train_data, TEXT, LABEL)
valid_examples, valid_fields = get_dataset(valid_data, TEXT, LABEL)
test_examples, test_fields = get_dataset(test_data, TEXT, None, True)

# 构建Dataset数据集
train = data.Dataset(train_examples, train_fields)
valid = data.Dataset(valid_examples, valid_fields)
test = data.Dataset(test_examples, test_fields)

TabularDataset.splits:

对于csv,json文件可以用TabularDataset.splits读取:

fields = [('PhraseId', None), ('SentenceId', None), ('Phrase', TEXT), ('Sentiment', LABEL)]

train, test = TabularDataset.splits(
    path='datasets/', format='tsv',
    train='train.tsv', test='test.tsv',
    skip_header=True, fields=fields)

自定义Dataset类:

当构建简单的数据集时,可直接使用torch.text.Dataset来构建,当对原始数据集只进行简单的划分处理时,例如读取数据并划分训练集验证集等操作,也可以直接使用TabularDataset类和split类方法来实现,该类支持读取csv,tsv等格式。但是当我们需要对数据进行更多的预处理时,例如shuffle,dropout等数据增强操作时,自定义Dataset会更灵活。

from torchtext import data
from torchtext.vocab import Vectors
from tqdm import tqdm
import pandas as pd
import numpy as np
import torch
import random
import os

train_path = 'data/train_one_label.csv'
valid_path = 'data/valid_one_label.csv'
test_path = 'data/test.csv'

class MyDataset(data.Dataset):
    def __init__(self, path, text_field, label_field, test=False, aug=False, **kwargs):
        fields = [("id", None), ("comment_text", text_field), ("toxic", label_field)]
        examples = []
        csv_data = pd.read_csv(path)
        print('read data from {}'.format(path))
        
        if test:
            for text in tqdm(csv_data['comment_text']):
                examples.append(data.Example.fromlist([None, text, None], fields))
        else:
            for text, label in tqdm(zip(csv_data['comment_text'], csv_data['toxic'])):
                if aug:
                    rate = random.random()
                    if rate > 0.5:
                        text = self.dropout(text)
                    else:
                        text = self.shuffle(text)
                examples.append(data.Example.fromlist([None, text, label-1], fields)
        super(MyDataset, self).__init__(examples, fields, **kwargs)
    
    def shuffle(self, text):
        text = np.random.permutation(text.strip().split())
        return ' '.join(text)
        
    def dropout(self, text, p=0.5):
        text = text.strip().split()
        len_ = len(text)
        indexs = np.random.choice(len_, int(len_ * p))
        for i in indexs:
            text[i] = ''
        retrurn ' '.join(text)

构建词表:

所谓构建词表,即需要给每个单词编码,也就是用数字表示每个单词,这样才能传入模型。

TEXT.build_vocab(train)这行代码使得 Torchtext遍历训练集中的绑定TEXT field的数据,将单词注册到vocabulary,并自动构建embedding矩阵。
最简单的方法:build_vocab()方法中传入用于构建词表的数据集

TEXT = data.Field(sequential=True, tokenize=tokenize, lower=True)
TEXT.build_vocab(train)

# 统计词频
TEXT.vocab.freqs.most_common(10)

到这一步,我们已经可以把词转为数字,数字转为词,词转为词向量

torchtext使用_第1张图片

1. 使用torchtext默认支持的预训练词向量

默认情况下,会自动下载对应的预训练词向量文件到当前文件夹下的.vector_cache目录下,.vector_cache为默认的词向量文件和缓存文件的目录。

from torchtext.vocab import GloVe
from torchtext import data
TEXT = data.Field(sequential=True)

TEXT.build_vocab(train, vectors=GloVe(name='6B', dim=300))
TEXT.build_vocab(train, vectors="glove.6B.300d")

torchtext支持的词向量:

charngram.100d
fasttext.en.300d
fasttext.simple.300d
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


2. 使用外部预训练好的词向量

我们可以使用torchtext.vocab.Vectors中的name和cachae参数指定预训练的词向量文件和缓存文件的所在目录。因此我们也可以使用自己用word2vec等工具训练出的词向量文件,只需将词向量文件放在name指定的目录中即可。

通过name参数可以指定预训练词向量文件所在的目录:
        默认情况下预训练词向量文件和缓存文件的目录位置都为当前目录下的 .vector_cache目录,虽然通过name参数指定了预训练词向量文件存在的目录,但是因为缓存文件的目录没有特殊指定,此时在当前目录下仍然需要存在 .vector_cache 目录。

if not os.path.exists('.vector_cache'):
    os.mkdir('.vector_cache')
vectors = Vectors(name='myvector/glove/glove.6B.200d.txt')
TEXT.build_vocab(train, vectors=vectors)

通过cache参数指定缓存目录

cache = '.vector_cache'
if not os.path.exists(cache):
    os.mkdir(cache)
vectors = Vectors(name='myvector/glove/glove.6B.200d.txt', cache=cache)
# 指定Vector缺失值的初始化方式,没有命中的token的初始化方式
vectors.unk_init = init.xavier_uniform_
TEXT.build_vocab(train, vectors=vectors)

构建迭代器:

使用pytorch训练网络时,每次训练都是输入一个batch,那么,我们怎么把前面得到的dataset转为迭代器,然后遍历迭代器获取batch输入呢?下面将介绍torchtext时怎么实现这一功能的。

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 
)
test_iter = Iterator(test, batch_size=8, device=-1, sort=False, sort_within_batch=False, repeat=False)

'''
dataset:加载的数据集
batch_size:Batch的大小
batch_size_fn:产生动态的batch_size的函数
sort_key:排序的key
train:是否是训练集
repeat:是否在不同epoch中重复迭代
shuffle:是否打乱数据
sort:是否对数据进行排序
sort_with_batch:batch内部是否排序
'''

BucketIterator相比Iterator的优势是会自动选取样本长度相似的数据来构建批数据。但是在测试集中一般不想改变样本顺序,因此测试集使用Iterator迭代器来构建。

在 test_iter 中 shuffle, sort, repeat一定要设置成 False, 要不然会被 torchtext 搞乱样本顺序
如果输入变长序列,sort_within_batch需要设置成true,使每个batch内数据按照sort_key降序进行排序

批数据的使用:
 

for idx, batch in enumerate(train_iter):
    # batch能返回什么是根据fields里来的
    text, label = batch.comment_text, batch.toxic
batch = next(iter(train_iter))
text, label = batch.comment_text, batch.toxic

torchtext使用_第2张图片

官方文档:https://torchtext.readthedocs.io/en/latest/index.html

参考文档:https://blog.csdn.net/leo_95/article/details/87708267#_150

torchtext与模型的连接:

torchtext处理完数据要把数据传递给模型处理,这里分为两部分:词向量和batch。

词向量嵌入:

在模型中指定Embedding层的权重
在使用预训练好的词向量时,我们需要在神经网络模型的Embedding层中明确地传递嵌入矩阵的初始权重。权重包含在词汇表的vectors属性中。以Pytorch搭建的Embedding层为例:

embedding = nn.Embedding(2000, 256)
weight_matrix = TEXT.vocab.vectors
embedding.weight.data.copy_(weight_matrix)

batch:

import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

weight_matrix = TEXT.vocab.vectors

class LSTM(nn.Module):
    def __init__(self):
        super(LSTM, self).__init__()
        self.word_embeddings = nn.Embedding(len(TEXT.vocab), 300)
        self.word_embeddings.weight.data.copy_(weight_matrix)
        self.lstm = nn.LSTM(input_size=300, hidden_size=128, num_layer=1)
        self.decoder = nn.Linear(128, 2)
    
    def forward(self, sentence):
        embeds = self.word_embeddings(sentence)
        lstm_out = self.lstm(embeds)[0]
        final = lstm_out[-1]
        y = self.decoder(final)
        return y

def main():
    model = LSTM()
    model.train()
    optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.01)
    loss_funciton = F.cross_entropy
    
    # 从迭代器里拿出数据分批训练
    for epoch, batch in enumerate(train_iter):
        optimizer.zero_grad()
        start = time.time()
        # 模型输入也是从迭代器里拿出来的
        predicted = model(batch.comment_text)
        
        loss = loss_function(predicted, batch.toxic)
        loss.backward()
        optimizer.step()
        print(loss)

 

你可能感兴趣的:(pytorch)