SuooL's Blog

蛰伏于盛夏 藏华于当春

TorchText 使用记录

API 说明

关于数据处理的 API 变来变去,不知道到底在想什么玩意?

在 0.9 版本以前 data 相关方法是直接 torchtext 下面,如下:

torchtext.data

  • torchtext.data.Example : 用来表示一个样本,数据+标签
  • torchtext.vocab.Vocab: 词汇表相关
  • torchtext.data.Datasets: 数据集类,getitem 返回 Example实例
  • torchtext.data.Field : 用来定义字段的处理方法(文本字段,标签字段)
    • 创建 Example时的预处理
    • batch 时的一些处理操作。
  • torchtext.data.Iterator: 迭代器,用来生成 batch

torchtext.datasets: 包含了常见的数据集.

在 0.9 版本及以后移动到 torchtext.legacy 下面,然而,到了 0.12.0 版本,又挪回去了。

数据处理及加载

torchtext对数据的处理可以概括为Field,Dataset和迭代器三部分。

Field 定义

1
2
TEXT = torchtext.legacy.data.Field(sequential=True, tokenize=tokenizer, lower=True,fix_length=300)#默认分词器是split(),空格分割
LABEL = torchtext.legacy.data.Field(sequential=False, use_vocab=False)

定义文本及标签对象处理方式。

  • sequential:是否把数据表示成序列,如果是False, 则不能使用分词。默认值:True。

  • lower:是否把数据转化为小写。默认值:False。

  • fix_length:在构建迭代器时,将每条文本数据的长度修改为该值,进行截断补长,用pad_token补全。默认值:None。

  • use_vocab:是否使用词典对象. 如果是False,数据的类型必须已经是数值类型。默认值:True。

数据集定义

可以直接使用内置的TabularDataset类和split类方法来实现,该类支持读取csv,tsv等格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#创建表格数据集
train_dataset, dev_dataset = TabularDataset.splits(
path = './Data/', format = 'csv', skip_header = True,
train='train.csv', validation='valid.csv',
#在这里,必须让我们的train.csv、val.csv提前包含以label和text为列名的两个列,test.csv包含以text为列名的列就可以了
fields=[
('text', TEXT),
('label', LABEL),
]
)

test_dataset = TabularDataset(
path = './Data/test_o.csv', format = 'csv', skip_header = True,
fields=[
('id', None),
('fault_time', None),
('text', TEXT),
]
)

但是当我们需要对数据进行更多的预处理时,例如shuffle,dropout等数据增强操作时,自定义Dataset会更灵活。

构建词表、加载预训练词向量

构建词表,即给每个单词编码,也就是用数字表示每个单词,这样才能传入模型。将文本数据构建成词-索引的形式,接着,加载了预训练的词向量之后,每个词会对应一个词向量,即词-向量的形式,最后,在后面的模型训练中,我们就可以使用词嵌入矩阵了,即Embedding层。这样我们就将每个词都转换为向量了,可以输入到模型中进行训练了

最简单的方法:build_vocab()方法中传入用于构建词表的数据集。

使用预训练词向量

在使用pytorch或tensorflow等神经网络框架进行nlp任务的处理时,可以通过对应的Embedding层做词向量的处理,更多的时候,使用预训练好的词向量会带来更优的性能,下面介绍如何在torchtext中使用预训练的词向量,进而传送给神经网络模型进行训练。

1
2
3
4
5
6
7
8
#构建词表
TEXT.build_vocab(train_dataset,dev_dataset,test_dataset)
for w,i in TEXT.vocab.stoi.items():
print(w,i)

TEXT.vocab.load_vectors('glove.6B.300d',unk_init=torch.Tensor.normal_)

print(TEXT.vocab.vectors.shape) #torch.Size([1135, 300])

这样预训练的词嵌入向量即完成加载。

迭代器定义

迭代器有Iterator和BucketIterator

  • Iterator:跟原始数据顺序相同,构建批数据。
  • BucketIterator:将长度类似的数据构建成一批数据,这样就会减少截断补长操作时的填充。

Iterator的定义方式如下:

1
2
3
4
5
6
7
8
train_iter = torchtext.legacy.data.valid_iter = torchtext.legacy.data.Iterator(dataset = dev_dataset,batch_size=256,shuffle=True,sort_within_batch=False,repeat=False,device=device)Iterator(dataset = train_dataset,batch_size=1024,shuffle=True, sort_within_batch=False,
repeat=False, device=device)

valid_iter = torchtext.legacy.data.Iterator(dataset = dev_dataset,batch_size=256,shuffle=True,sort_within_batch=False,repeat=False,device=device)

test_iter = torchtext.legacy.data.Iterator(dataset = test_dataset,batch_size=64,shuffle=False,sort_within_batch=False,
repeat=False,device=device)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from torchtext.legacy.data import 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=device, # 如果使用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)

BucketIterator 相比 Iterator 的优势是会自动选取样本长度相似的数据来构建批数据。但是在测试集中一般不想改变样本顺序,因此测试集使用Iterator迭代器来构建。
sort_within_batch参数设置为True时,按照sort_key按降序对每个小批次内的数据进行排序。如果我们需要padded序列使用pack_padded_sequence转换为PackedSequence对象时,这是非常重要的,我们知道如果想pack_padded_sequence方法必须将批样本按照降序排列。由此可见,torchtext不仅可以对文本数据进行很方便的处理,还可以很方便的和torchtext的很多内建方法进行结合使用。

在模型中指定Embedding层的权重

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

1
2
3
embedding = nn.Embedding(vocab_size, embedding_size)
weight_matrix = TEXT.vocab.vectors
embedding.weight.data.copy_(weight_matrix)

在定义模型嵌入层的时候初始化权重:

1
2
self.embedding = nn.Embedding(vocab_size,embedding_size) #embedding层
self.embedding.weight.data.copy_(weight_matrix)

模型定义

完成上述数据处理之后,则可以定义各种模型处理相关 nlp 任务了,具体内容略去。

备忘:混淆矩阵显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import numpy as np				# label.numpy()
from tqdm import tqdm
import matplotlib.pyplot as plt
from prettytable import PrettyTable

class ConfusionMatrix(object):
"""
注意,如果显示的图像不全,是matplotlib版本问题
本例程使用matplotlib-3.2.1(windows and ubuntu)绘制正常
需要额外安装prettytable库: pip install prettytable
"""
def __init__(self, num_classes: int, labels: list):
self.matrix = np.zeros((num_classes, num_classes)) # 初始化混淆矩阵
self.num_classes = num_classes
self.labels = labels

# 混淆矩阵更新
def update(self, preds, labels):
for p, t in zip(preds, labels):
self.matrix[p, t] += 1

# 计算并打印评价指标
def summary(self):
# calculate accuracy
sum_TP = 0
for i in range(self.num_classes):
sum_TP += self.matrix[i, i] # 对角线元素求和
acc = sum_TP / np.sum(self.matrix)
print("the model accuracy is ", acc)

# precision, recall, specificity
table = PrettyTable()
table.field_names = ["", "Precision", "Recall", "Specificity"] # 第一个元素是类别标签
for i in range(self.num_classes): # 针对每个类别进行计算
# 整合其他行列为不属于该类的情况
TP = self.matrix[i, i]
FP = np.sum(self.matrix[i, :]) - TP
FN = np.sum(self.matrix[:, i]) - TP
TN = np.sum(self.matrix) - TP - FP - FN
Precision = round(TP / (TP + FP), 3) if TP + FP != 0 else 0. # 注意分母为 0 的情况
Recall = round(TP / (TP + FN), 3) if TP + FN != 0 else 0.
Specificity = round(TN / (TN + FP), 3) if TN + FP != 0 else 0.
table.add_row([self.labels[i], Precision, Recall, Specificity])
print(table)

# 可视化混淆矩阵
def plot(self):
matrix = self.matrix
print(matrix)
plt.imshow(matrix, cmap=plt.cm.Blues) # 从白色到蓝色

# 设置x轴坐标label
plt.xticks(range(self.num_classes), self.labels, rotation=45) # x 轴标签旋转 45 度方便展示
# 设置y轴坐标label
plt.yticks(range(self.num_classes), self.labels)
# 显示colorbar
plt.colorbar()
plt.xlabel('True Labels')
plt.ylabel('Predicted Labels')
plt.title('Confusion matrix')

# 在图中标注数量/概率信息
thresh = matrix.max() / 2
for x in range(self.num_classes):
for y in range(self.num_classes):
# 注意这里的matrix[y, x]不是matrix[x, y]
# 画图的时候横坐标是x,纵坐标是y
info = int(matrix[y, x])
plt.text(x, y, info,
verticalalignment='center',
horizontalalignment='center',
color="white" if info > thresh else "black")
plt.tight_layout() # 图形显示更加紧凑
plt.show()
泡面一杯