PyTorch文本分类

2020 年 5 月 7 日 AINLP

文本分类是NLP领域的较为容易的入门问题,本文记录文本分类任务的基本流程,大部分操作使用了torchtorchtext两个库。

1. 文本数据预处理

首先数据存储在三个csv文件中,分别是train.csv,valid.csv,test.csv,第一列存储的是文本数据,例如情感分类问题经常是用户的评论review,例如imdb或者amazon数据集。第二列是情感极性polarity,N分类问题的话就有N个值,假设值的范围是0~N-1。

下面是很常见的文本预处理流程,英文文本的话不需要分词,直接按空格split就行了,这里只会主要说说第4点。

1.去除非文本部分

2.分词

3.去除停用词

4.对英文单词进行词干提取(stemming)和词型还原(lemmatization)

5.转为小写

6.特征处理

Bag of WordsTf-idfN-gramWord2vec词干提取和词型还原

from nltk.stem import SnowballStemmerstemmer = SnowballStemmer("english") # 选择语言from nltk.stem import WordNetLemmatizer wnl = WordNetLemmatizer()

SnowballStemmer较为激进,转换有可能出现错误,这里较为推荐使用WordNetLemmatizer,它一般只在非常肯定的情况下才进行转换,否则会返回原来的单词。

stemmer.stem('knives')# knivewnl.lemmatize('knives')# knife

因为我没有系统学习和研究过NLTK的代码,所以就不在这里展开说了,有兴趣的可以自己去看NLTK源代码。

2. 使用torchtext加载文本数据

本节主要是用的模块是torchtext里的data模块,处理的数据同上一节所描述。

首先定义一个tokenizer用来处理文本,比如分词,小写化,如果你已经根据上一节的词干提取和词型还原的方法处理过文本里的每一个单词后可以直接分词就够了。

tokenize = lambda x: x.split()

或者也可以更保险点,使用spacy库,不过就肯定更耗费时间了。

import spacy
spacy_en = spacy.load('en')def tokenizer(text): return [toke.text for toke in spacy_en.tokenizer(text)]

然后要定义Field,至于Field是啥,你可以简单地把它理解为一个能够加载、预处理和存储文本数据和标签的对象。我们可以用它根据训练数据来建立词表,加载预训练的Glove词向量等等。

def DataLoader():    tokenize = lambda x: x.split()        # 用户评论,include_lengths设为True是为了方便之后使用torch的pack_padded_sequence    REVIEW = data.Field(sequential=True,tokenize=tokenize, include_lengths=True)    # 情感极性    POLARITY = data.LabelField(sequential=False, use_vocab=False, dtype = torch.long)        # 假如train.csv文件并不是只有两列,比如1、3列是review和polarity,2列是我们不需要的数据,    # 那么就要添加一个全是None的元组, fields列表存储的Field的顺序必须和csv文件中每一列的顺序对应,    # 否则review可能就加载到polarity Field里去了    fields = [('review', REVIEW), (None, None), ('polarity', POLARITY)]
# 加载train,valid,test数据 train_data, valid_data, test_data = data.TabularDataset.splits( path = 'amazon', train = 'train.csv', validation = 'valid.csv', test = 'test.csv', format = 'csv', fields = fields, skip_header = False # 是否跳过文件的第一行 ) return REVIEW, POLARITY, train_data

加载完数据可以开始建词表。如果本地没有预训练的词向量文件,在运行下面的代码时会自动下载到当前文件夹下的'.vector_cache'文件夹内,如果本地已经下好了,可以用Vectors指定文件名name,路径cache,还可以使用Glove。

from torchtext.vocab import Vectors, Gloveimport torch
REVIEW, POLARITY, train_data = DataLoader()# vectors = Vectors(name='glove.6B.300d.txt', cache='.vector_cache')REVIEW.build_vocab(train_data, # 建词表是用训练集建,不要用验证集和测试集 max_size=400000, # 单词表容量 vectors='glove.6B.300d', # 还有'glove.840B.300d'已经很多可以选 unk_init=torch.Tensor.normal_ # 初始化train_data中不存在预训练词向量词表中的单词)
# print(REVIEW.vocab.freqs.most_common(20)) 数据集里最常出现的20个单词# print(REVIEW.vocab.itos[:10]) 列表 index to word# print(REVIEW.vocab.stoi) 字典 word to index

接着就是把预训练词向量加载到model的embedding weight里去了。


pretrained_embeddings = REVIEW.vocab.vectorsmodel.embedding.weight.data.copy_(pretrained_embeddings)UNK_IDX = REVIEW.vocab.stoi[REVIEW.unk_token]PAD_IDX = REVIEW.vocab.stoi[REVIEW.pad_token]# 因为预训练的权重的unk和pad的词向量不是在我们的数据集语料上训练得到的,所以最好置零model.embedding.weight.data[UNK_IDX] = torch.zeros(EMBEDDING_DIM)model.embedding.weight.data[PAD_IDX] = torch.zeros(EMBEDDING_DIM)

然后用torchtext的迭代器来批量加载数据,torchtext.data里的BucketIterator非常好用,它可以把长度相近的文本数据尽量都放到一个batch里,这样最大程度地减少padding,数据就少了很多无意义的0,也减少了矩阵计算量,也许还能对最终准确度有帮助(误)?

sort_within_batch设为True的话,一个batch内的数据就会按sort_key的排列规则降序排列,sort_key是排列的规则,这里使用的是review的长度,即每条用户评论所包含的单词数量。

train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(                                                (train_data, valid_data, test_data),                                                batch_size=32,                                                sort_within_batch=True,                                                sort_key = lambda x:len(x.review),                                                device=torch.device('cpu'))

最后就是加载数据喂给模型了。

for batch in train_iterator:  # 因为REVIEW Field的inclue_lengths为True,所以还会包含一个句子长度的Tensor  review, review_len = batch.review    # review.size = (seq_length, batch_size) , review_len.size = (batch_size, )  polarity = batch.polarity  # polarity.size = (batch_size, )  predictions = model(review, review_lengths)  loss = criterion(predictions, polarity) # criterion = nn.CrossEntropyLoss()

3. 使用pytorch写一个LSTM情感分类器

下面是我简略写的一个模型,仅供参考

import torch.nn as nnimport torch.nn.functional as Ffrom torch.nn.utils.rnn import pack_padded_sequenceimport torch

class LSTM(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim, output_dim, n_layers, bidirectional, dropout, pad_idx): super(LSTM, self).__init__() self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=pad_idx) self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers=n_layers, bidirectional=bidirectional, dropout=dropout) self.Ws = nn.Parameter(torch.Tensor(hidden_dim, output_dim)) self.bs = nn.Parameter(torch.zeros((output_dim, ))) nn.init.uniform_(self.Ws, -0.1, 0.1) nn.init.uniform_(self.bs, -0.1, 0.1) self.dropout = nn.Dropout(p=0.5)
def forward(self, x, x_len): x = self.embedding(x) x = pack_padded_sequence(x, x_len) H, (h_n, c_n) = self.lstm(x) h_n = self.dropout(h_n) h_n = torch.squeeze(h_n) res = torch.matmul(h_n, self.Ws) + self.bs y = F.softmax(res, dim=1) # y.size(batch_size, output_dim) return y

训练函数

def train(model, iterator, optimizer, criterion):    epoch_loss = 0    num_sample = 0    correct = 0
model.train() for batch in iterator: optimizer.zero_grad() review, review_lengths = batch.review polarity = batch.polarity predictions = model(review, review_lengths) correct += torch.sum(torch.argmax(preds, dim=1) == polarity) loss = criterion(predictions, polarity) loss.backward() epoch_loss += loss.item() num_sample += len(batch) optimizer.step()
return epoch_loss / num_sample, correct.float() / num_sample
if __name__ == '__main__': for epoch in range(N_EPOCHS): train_loss, acc = train(model, train_iter, optimizer, criterion) print(f'\tTrain Loss: {train_loss:.3f} | Train Acc: {acc* 100:.2f}%')

4. 注意事项和遇到的一些坑

1.文本情感分类需不需要去除停用词?

应该是不用的,否则acc有可能下降。

2.data.TabularDataset.splits虽然好用,但是如果你只想加载训练集,这时候如果直接不给validation和test参数赋值,那么其他代码和原来一样,比如这样

   train_data = data.TabularDataset.splits(                                       path = '',                                       train = 'train.csv',                                       format = 'csv',                                       fields = fields,                                       skip_header = False # 是否跳过文件的第一行   )

那么底下你一定会报错,因为data.TabularDataset.splits返回的是一个元组,也就是如果是训练验证测试三个文件都给了函数,就返回(train_data, valid_data, test_data),这时候你用三个变量去接受函数返回值当然没问题,元组会自动拆包。

当只给函数一个文件train.csv时,函数返回的是(train_data)而非train_data,因此正确的写法应该如下

   train_data = data.TabularDataset.splits(                                       path = '',                                       train = 'train.csv',                                       format = 'csv',                                       fields = fields,                                       skip_header = False # 是否跳过文件的第一行   )[0] # 注意这里的切片,选择元组的第一个也是唯一一个元素赋给train_data

3.同理data.BucketIterator.splits也有相同的问题,它不但返回的是元组,它的参数datasets要求也是以元组形式,即(train_data, valid_data, test_data)进行赋值,否则在下面的运行中也会出现各种各样奇怪的问题。

如果你要生成两个及以上的迭代器,那么没问题,直接照上面写就完事了。

如果你只要生成train_iterator,那么正确的写法应该是下面这样

   train_iter = data.BucketIterator(               train_data,               batch_size=32,               sort_key=lambda x:len(x.review),               sort_within_batch=True,               shuffle=True # 训练集需要shuffle,但因为验证测试集不需要                                                  # 可以生成验证和测试集的迭代器直接用data.iterator.Iterator类就足够了   )

4.出现的问题 x = pack_padded_sequence(x, x_len) 当数据集有长度为0的句子时, 就会后面报错

5.当vocab size较大而训练数据不多的情况下,我在实验时发现Adagrad效果比Adam好,如果数据较多,可以尝试使用RMSProp和Adam

5. 总结

不仅仅是NLP领域,在各大顶会中,越来越多的学者选择使用Pytorch而非TensorFlow,主要原因就是因为它的易用性,torchtext和pytorch搭配起来是非常方便的NLP工具,可以大大缩短文本预处理,加载数据的时间。

我本人之前用过tf 1.x以及keras,最终拥抱了Pytorch,也是因为它与Numpy极其类似的用法,更Pythonic的代码,清晰的源码让我在遇到bug时能一步一步找到问题所在,动态图让人能随时看到输出的Tensor的全部信息,这些都是Pytorch的优势。

现在tf 2.0也在不断改进,有人称tf越来越像pytorch了,其实pytorch也在不断向tf学习,在工业界,tf仍然处于王者地位,不知道未来pytorch能不能在工业界也与tf平分秋色,甚至更胜一筹呢?


推荐阅读

AINLP年度阅读收藏清单

ERNIE加持,百度飞桨图神经网络PGL全新升级

斯坦福大学NLP组Python深度学习自然语言处理工具Stanza试用

DistilBERT Understanding

太赞了!Springer面向公众开放电子书籍,附65本数学、编程、机器学习、深度学习、数据挖掘、数据科学等书籍链接及打包下载

深度学习如何入门?这本“蒲公英书”再适合不过了!豆瓣评分9.5!【文末双彩蛋!】

数学之美中盛赞的 Michael Collins 教授,他的NLP课程要不要收藏?

自动作诗机&藏头诗生成器:五言、七言、绝句、律诗全了

From Word Embeddings To Document Distances 阅读笔记

模型压缩实践系列之——bert-of-theseus,一个非常亲民的bert压缩方法

这门斯坦福大学自然语言处理经典入门课,我放到B站了

可解释性论文阅读笔记1-Tree Regularization

征稿启示 | 稿费+GPU算力+星球嘉宾一个都不少

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。


登录查看更多
0

相关内容

Natural Language Toolkit,自然语言处理工具包,在NLP领域中,最常使用的一个Python库。
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
57+阅读 · 2020年1月3日
《动手学深度学习》(Dive into Deep Learning)PyTorch实现
专知会员服务
116+阅读 · 2019年12月31日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
160+阅读 · 2019年10月28日
【开源书】PyTorch深度学习起步,零基础入门(附pdf下载)
专知会员服务
107+阅读 · 2019年10月26日
开源书:PyTorch深度学习起步
专知会员服务
49+阅读 · 2019年10月11日
PyTorch 学习笔记(一):让PyTorch读取你的数据集
极市平台
16+阅读 · 2019年4月24日
在Python中使用SpaCy进行文本分类
专知
24+阅读 · 2018年5月8日
【干货】--基于Python的文本情感分类
R语言中文社区
5+阅读 · 2018年1月5日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
用深度学习Keras判断出一句文本是积极还是消极
北京思腾合力科技有限公司
6+阅读 · 2017年11月16日
用神经网络训练一个文本分类器
Python开发者
3+阅读 · 2017年8月19日
Arxiv
3+阅读 · 2019年10月31日
Learning to Weight for Text Classification
Arxiv
8+阅读 · 2019年3月28日
Arxiv
31+阅读 · 2018年11月13日
Bidirectional Attention for SQL Generation
Arxiv
4+阅读 · 2018年6月21日
Arxiv
3+阅读 · 2018年5月28日
VIP会员
相关VIP内容
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
57+阅读 · 2020年1月3日
《动手学深度学习》(Dive into Deep Learning)PyTorch实现
专知会员服务
116+阅读 · 2019年12月31日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
160+阅读 · 2019年10月28日
【开源书】PyTorch深度学习起步,零基础入门(附pdf下载)
专知会员服务
107+阅读 · 2019年10月26日
开源书:PyTorch深度学习起步
专知会员服务
49+阅读 · 2019年10月11日
相关资讯
PyTorch 学习笔记(一):让PyTorch读取你的数据集
极市平台
16+阅读 · 2019年4月24日
在Python中使用SpaCy进行文本分类
专知
24+阅读 · 2018年5月8日
【干货】--基于Python的文本情感分类
R语言中文社区
5+阅读 · 2018年1月5日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
用深度学习Keras判断出一句文本是积极还是消极
北京思腾合力科技有限公司
6+阅读 · 2017年11月16日
用神经网络训练一个文本分类器
Python开发者
3+阅读 · 2017年8月19日
相关论文
Top
微信扫码咨询专知VIP会员