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) 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', 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)
|
这样预训练的词嵌入向量即完成加载。
迭代器定义
迭代器有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, val_iter = BucketIterator.splits( (train, valid), batch_size=(8, 8), device=device, 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) 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 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): 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)
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. 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)
plt.xticks(range(self.num_classes), self.labels, rotation=45) plt.yticks(range(self.num_classes), self.labels) 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): 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()
|