【入门】PyTorch文本分类

2020 年 2 月 2 日 深度学习自然语言处理

点击上方,选择星标置顶,每天给你送干货

阅读大概需要17分钟

跟随小博主,每天进步一丢丢


资源获取链接!(该链接文末可获得知识图谱、python、DL、ML、NLP、C++、TensorFlow、QL、pytorch4NLP、PRML等学习资源!

来自: 天宏NLP

文本分类是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平分秋色,甚至更胜一筹呢?



资源获取链接! (该链接文末可获得知识图谱、python、DL、ML、NLP、C++、TensorFlow、QL、pytorch4NLP、PRML 等学习资源!


推荐阅读:
【ACL 2019】腾讯AI Lab解读三大前沿方向及20篇入选论文
【一分钟论文】IJCAI2019 | Self-attentive Biaffine Dependency  Parsing
【一分钟论文】 NAACL2019-使用感知句法词表示的句法增强神经机器翻译
【一分钟论文】Semi-supervised Sequence Learning半监督序列学习
【一分钟论文】Deep Biaffine Attention for Neural Dependency Parsing
详解Transition-based Dependency parser基于转移的依存句法解析器
经验 | 初入NLP领域的一些小建议
学术 | 如何写一篇合格的NLP论文
干货 | 那些高产的学者都是怎样工作的?
一个简单有效的联合模型
近年来NLP在法律领域的相关研究工作




让更多的人知道你“在看”
登录查看更多
8

相关内容

Natural Language Toolkit,自然语言处理工具包,在NLP领域中,最常使用的一个Python库。
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
223+阅读 · 2020年3月22日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
TensorFlow Lite指南实战《TensorFlow Lite A primer》,附48页PPT
专知会员服务
67+阅读 · 2020年1月17日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
57+阅读 · 2020年1月3日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
159+阅读 · 2019年10月28日
机器学习入门的经验与建议
专知会员服务
89+阅读 · 2019年10月10日
学习自然语言处理路线图
专知会员服务
132+阅读 · 2019年9月24日
Github项目推荐 | PyTorch文本分类教程
AI研习社
7+阅读 · 2019年6月7日
PyTorch:60分钟入门学习
全球人工智能
13+阅读 · 2018年5月18日
在Python中使用SpaCy进行文本分类
专知
24+阅读 · 2018年5月8日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
Python NLP入门教程
Python开发者
8+阅读 · 2017年11月19日
Python NLP 入门教程
开源中国
14+阅读 · 2017年10月1日
干货|10分钟快速入门PyTorch (7) 词向量
机器学习研究会
8+阅读 · 2017年9月26日
Arxiv
21+阅读 · 2019年8月21日
Arxiv
6+阅读 · 2018年11月1日
Arxiv
6+阅读 · 2018年6月18日
Arxiv
3+阅读 · 2017年12月18日
Arxiv
5+阅读 · 2015年9月14日
Arxiv
3+阅读 · 2012年11月20日
VIP会员
相关VIP内容
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
223+阅读 · 2020年3月22日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
TensorFlow Lite指南实战《TensorFlow Lite A primer》,附48页PPT
专知会员服务
67+阅读 · 2020年1月17日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
57+阅读 · 2020年1月3日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
159+阅读 · 2019年10月28日
机器学习入门的经验与建议
专知会员服务
89+阅读 · 2019年10月10日
学习自然语言处理路线图
专知会员服务
132+阅读 · 2019年9月24日
相关资讯
Github项目推荐 | PyTorch文本分类教程
AI研习社
7+阅读 · 2019年6月7日
PyTorch:60分钟入门学习
全球人工智能
13+阅读 · 2018年5月18日
在Python中使用SpaCy进行文本分类
专知
24+阅读 · 2018年5月8日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
Python NLP入门教程
Python开发者
8+阅读 · 2017年11月19日
Python NLP 入门教程
开源中国
14+阅读 · 2017年10月1日
干货|10分钟快速入门PyTorch (7) 词向量
机器学习研究会
8+阅读 · 2017年9月26日
相关论文
Arxiv
21+阅读 · 2019年8月21日
Arxiv
6+阅读 · 2018年11月1日
Arxiv
6+阅读 · 2018年6月18日
Arxiv
3+阅读 · 2017年12月18日
Arxiv
5+阅读 · 2015年9月14日
Arxiv
3+阅读 · 2012年11月20日
Top
微信扫码咨询专知VIP会员