Pytorch学习记录-torchtext学习
1. Field
Field是核心,指定了用户希望如何处理字段。
它包含一个Vocab对象,用于定义字段元素的可能值集及其对应的数字表示。 Field对象还包含与数据类型应如何数字化相关的其他参数,例如标记化方法和应生成的Tensor类型。
如果传递的字段是默认被数字化的,并且不是顺序序列(比如这里的label),则应该传递参数use_vocab = False和sequential = False。
对于评论文本,我们把想对字段做的预处理以传递关键字参数的形式传入。我们给字段一个分词器,告诉它把输入都转换为小写,告诉它输入是顺序序列。
from torchtext.data import Field
tokenize = lambda x: x.split()
Text = Field(sequential=True, tokenize=tokenize, lower=True)
LABEL = Field(sequential=False, use_vocab=False)
2. 构建Dataset
Fields知道如何处理原始数据,用户告诉Fields去哪里处理,在这里就要使用Dataset。
torchtext内置有Dataset,看下面处理csv的实例。
from torchtext.data import TabularDataset
tv_datafields = [('id', None),
('comment_text', Text),
('toxic', LABEL),
('severe_toxic', LABEL),
("threat", LABEL),
("obscene", LABEL),
("insult", LABEL),
("identity_hate", LABEL)
]
trn, vld = TabularDataset.splits(
path=r'D:\DesktopBackup\right\practical-torchtext-master\data',
train='train.csv',
validation='valid.csv',
format='csv',
skip_header=True,
fields=tv_datafields
)
tst_datafields = [('id', None),
('comment_text', Text)]
tst = TabularDataset(
path=r'D:\DesktopBackup\right\practical-torchtext-master\data\test.csv',
format='csv',
skip_header=True,
fields=tst_datafields
)
print(trn[0].__dict__.keys())
print(trn[4].comment_text)
3. 构建迭代器
在torchvision和PyTorch中,数据的处理和批处理由DataLoaders处理。 出于某种原因,torchtext相同的东西又命名成了Iterators。 基本功能是一样的,但我们将会看到,Iterators具有一些NLP特有的便捷功能。
以下是如何初始化训练迭代器,验证和测试数据的代码。
from torchtext.data import Iterator, BucketIterator
train_iter, val_iter = BucketIterator.splits((trn, vld),
batch_sizes=(25, 25),
device=-1,
sort_key=lambda x: len(x.comment_text),
sort_within_batch=False,
repeat=False)
test_iter = Iterator(tst, batch_size=64,
device=-1,
sort=False,
sort_within_batch=False,
repeat=False)
sort_within_batch参数设置为True时,按照sort_key按降序对每个小批次内的数据进行排序。当你想对padded序列使用pack_padded_sequence转换为PackedSequence对象时。
BucketIterator是torchtext最强大的功能之一。它会自动将输入序列进行shuffle并做bucket。当需要填充输入序列使得长度相同才能批处理。 例如,序列,填充量由batch中最长的序列决定。似乎在Transformer里面的mask也需要。
在这里,教程使用sort_key=lambda x: len(x.comment_text)作为bucket的依据,就是comment_text的长度。
4. 封装迭代器
迭代器返回一个名为torchtext.data.Batch的自定义数据类型。
Batch类具有与Example类相似的API,将来自每个字段的一批数据作为属性。
但是这样的自定义数据在重用时有问题。教程里做了封装,把batch转换为形式为(x,y)的元组,其中x是自变量(模型的输入),y是因变量(标签数据)。
class BatchWrapper:
def __init__(self, dl, x_var, y_vars):
self.dl, self.x_var, self.y_vars = dl, x_var, y_vars # 传入自变量x列表和因变量y列表
def __iter__(self):
for batch in self.dl:
x = getattr(batch, self.x_var) # 在这个封装中只有一个自变量
if self.y_vars is not None: # 把所有因变量cat成一个向量
temp = [getattr(batch, feat).unsqueeze(1) for feat in self.y_vars]
y = torch.cat(temp, dim=1).float()
else:
y = torch.zeros((1))
yield (x, y)
def __len__(self):
return len(self.dl)
train_dl = BatchWrapper(train_iter, "comment_text",
["toxic", "severe_toxic", "obscene", "threat", "insult", "identity_hate"])
valid_dl = BatchWrapper(val_iter, "comment_text",
["toxic", "severe_toxic", "obscene", "threat", "insult", "identity_hate"])
test_dl = BatchWrapper(test_iter, "comment_text", None)
print(next(train_dl.__iter__()))
5. 构建一个LSTM演示处理好的数据
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
class SimpleLSTMBaseline(nn.Module):
def __init__(self, hidden_dim, emb_dim=300, num_linear=1):
super().__init__()
self.embedding = nn.Embedding(len(Text.vocab), emb_dim)
self.encoder = nn.LSTM(emb_dim, hidden_dim, num_layers=1)
self.linear_layers = []
for _ in range(num_linear - 1):
self.linear_layers.append(nn.Linear(hidden_dim, hidden_dim))
self.linear_layer = nn.ModuleList(self.linear_layers)
self.predictor = nn.Linear(hidden_dim, 6)
def forward(self, seq):
hdn, _ = self.encoder(self.embedding(seq))
feature = hdn[-1, :, :]
for layer in self.linear_layers:
feature = layer(feature)
preds = self.predictor(feature)
return preds
em_sz = 100
nh = 500
model = SimpleLSTMBaseline(nh, emb_dim=em_sz)
6. 训练模型和预测
import tqdm
import numpy as np
optim = optim.Adam(model.parameters(), lr=1e-2)
loss_func = nn.BCEWithLogitsLoss()
epochs = 2
for epoch in range(1, epochs + 1):
running_loss = 0.0
running_corrects = 0
model.train()
for x, y in tqdm.tqdm(train_dl):
optim.zero_grad()
preds = model(x)
loss = loss_func(y, preds)
loss.backward()
optim.step()
running_loss += loss.item()* x.size(0)
epoch_loss = running_loss / len(trn)
val_loss = 0.0
model.eval() # 评估模式
for x, y in valid_dl:
preds = model(x)
loss = loss_func(y, preds)
val_loss += loss.item()* x.size(0)
val_loss /= len(vld)
print('Epoch: {}, Training Loss: {:.4f}, Validation Loss: {:.4f}'.format(epoch, epoch_loss, val_loss))
test_preds = []
for x, y in tqdm.tqdm(test_dl):
preds = model(x)
preds = preds.data.numpy()
# 模型的实际输出是logit,所以再经过一个sigmoid函数
preds = 1 / (1 + np.exp(-preds))
test_preds.append(preds)
test_preds = np.hstack(test_preds)
print(test_preds)
这一篇写得太烂了,torchtext刚开始,学习目的就是为了处理文本信息,和之后的模型对接上。