基于pytorch的文本分类(使用Sentiment140数据集)
1 独热编码和嵌入矩阵
在建立神经网络模型之前,要将单词转化为张量,常见的有独热编码(one-hot encoding)和嵌入矩阵两种形式,由于独热编码的编码机制的局限性(会使向量非常长,并且无法表达若干语义相近的单词之间的联系),因此这里使用嵌入矩阵。,调用格式如下:
embed = nn.Embedding(vocab_size,dimension_size)
#dimension_size为向量大小
#eg:
cat_mat_embed = nn.Embedding(5,2) #先创建嵌入
cat_tensor = tensor([1]) #再创建张量
cat_mat_embed.forward(cat_tensor) #将张量传入嵌入层的forward()
2 数据集获取及预处理
在网站上下载zip文件,使用里面的training.1600000.processed.noemoticon.csv
import pandas as pd
#读取数据
tweetsDF = pd.read_csv("training.1600000.processed.noemoticon.csv",engine = 'python', header = None)
#查看数据的前5行
tweetsDF.head(5)
#我们只需要最后一列的推文文本信息和第0列的类别标签信息(0代表负面情绪,2代表中立,4代表正面情绪)
#对第0列的标签进行计数,发现没有中立情绪
tweetsDF[0].value_counts()
4 800000
0 800000
#将类编码为从0开始的数字
tweetsDF['sentiment_cat'] = tweetsDF[0].astype('category')
tweetsDF['sentiment'] = tweetsDF['sentiment_cat'].cat.codes
tweetsDF.to_csv('train-processed.csv', hearder = None, index = None)
3 定义字段并建立单词表
使用torchtext处理原始csv(或JSON)文件,需要定义字段field
from torchtext import data
#定义字段
LABEL = data.LabelField()
TWEET = data.Field(tokenize = 'spacy', lower = True) #使用spacy标记工具
#如果标记太慢,可以去掉tokenize参数重新运行,这时会使用默认标记(即按空白符划分)
#生成列表,将字段映射到csv行列中
fields = [('score',None),('id',None),('date',None),('query',None),('name',None),
('tweet',TWEET),('category',None),('label',LABEL)]
twitterDataset = torchtext.data.TabularDataset(path='training-processed.csv',format
='CSV',fields = fields, skip_header = False)
#划分训练、测试、和验证集
(train,test,valid) = twitterDataset.split(split_ratio = [0.8,0.1,0.1])
#建立单词表
vocab_size = 20000 #设置单词表最多包含训练集中的20000个单词
TWEET.build_vocab(train, max_size = vocab_size)
#创建数据加载器
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
(train,valid,test), batch_size = 32,device = 'cuda')
4 创建模型
使用pytorch中的embedding和LSTM模块建立对推文分类的模型
import torch.nn as nn
class OurFirstLSTM(nn.module):
def _init_(self,hidden_size,embedding_dim,vocab_size):
super(OurFirstLSTM,self)._init_()
self.embedding = nn.Embedding(vocab_size,embedding_dim)
self.encoder = nn.LSTM(input_size = embedding_dim, hidden_size = hidden_size, num_layers = 1)
self.predictor = nn.Linear(hidden_size,2)
def forward(self,seq):
output, (hidden,_) = self.encoder(self.embedding(seq))
preds = self.predictor(hidden.squeeze(0))
return preds
model = OurFirstLSTM(100,300,20002)
#这里的vocab_size=20002是因为torchtext增加了两个特定的标记<unk>和<pad>,分别表示未知单词和填充
model.to(device)
5 更新训练循环
由于只有负面情绪和正面情绪两个类型的文本,即是一个二分类问题,二分类问题可以使用BCEWithLogitsLoss()损失函数,比交叉熵CrossEntropyLoss()可能更合适些
optimizer = optim.Adam(model.parameters(), lr = 2e-2)
criterion = nn.BCEWithLogitsLoss()
def train(epochs,models,optimizer,criterion,train_iterator,valid_iterator):
for epoch in range(1,epochs+1):
train_loss = 0.0
valid_loss = 0.0
model.train()
for batch_idx, batch in enumerate(train_iterator):
opt.zero_grad()
predict = model(batch.tweet)
loss = criterion(predict,batch.label)
loss.backward()
optimizer.step()
training_loss += loss.data.item()*batch.tweet.size(0)
training_loss /= len(train_iterator)
model.eval()
for batch_idx, batch in enumerate(valid_iterator):
predict = model(batch.tweet)
loss = criterion(predict,batch.label)
valid_loss += loss.data.item()*batch.tweet.size(0)
valid_loss /= len(valid_iterator)
print('Epoch: {}, Training Loss: {:.2f}, Validation Loss: {:.2f}'.format(epoch, training_loss,valid_loss))
6 分类
这里定义一个函数完成二分类
def classify_tweet(tweet):
categories = {0:'Negative',1:'Positive'}
processed = TWEET.process([TWEET.preprocess(tweet)])
return categories[model(processed).argmax().item()]