一.利用textrnn进行文本分类,用于在问答中的意图识别。
二.结构图
import os
import torch
from torchtext import data,datasets
from torchtext.data import Iterator, BucketIterator
from torchtext.vocab import Vectors
from torch import nn,optim
import torch.nn.functional as F
import pandas as pd
import pickle
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
intent_classification_path = os.path.abspath(os.path.join(os.getcwd(), '../..'))
# 训练数据路径
train_data = os.path.join(intent_classification_path,'classification_data/classification_data.csv')
# 读取数据
train_data = pd.read_csv(train_data)
# 按字分
tokenize =lambda x: x.split(' ')
TEXT = data.Field(
sequential=True,
tokenize=tokenize,
lower=True,
use_vocab=True,
pad_token='<pad>',
unk_token='<unk>',
batch_first=True,
fix_length=20)
LABEL = data.Field(
sequential=False,
use_vocab=False)
# 获取训练或测试数据集
def get_dataset(csv_data, text_field, label_field, test=False):
fields = [('id', None), ('text', text_field), ('label', label_field)]
examples = []
if test: #测试集,不加载label
for text in csv_data['text']:
examples.append(data.Example.fromlist([None, text, None], fields))
else: # 训练集
for text, label in zip(csv_data['text'], csv_data['label']):
examples.append(data.Example.fromlist([None, text, label], fields))
return examples, fields
train_examples,train_fields = get_dataset(train_data, TEXT, LABEL)
train = data.Dataset(train_examples, train_fields)
# 预训练数据
pretrained_embedding = os.path.join(os.getcwd(), 'sgns.sogou.char')
vectors = Vectors(name=pretrained_embedding)
# 构建词典
TEXT.build_vocab(train, min_freq=1, vectors = vectors)
words_path = os.path.join(os.getcwd(), 'words.pkl')
with open(words_path, 'wb') as f_words:
pickle.dump(TEXT.vocab, f_words)
BATCH_SIZE = 163
# 构建迭代器
train_iter = BucketIterator(
dataset=train,
batch_size=BATCH_SIZE,
shuffle=True,
sort_within_batch=False)
# 构建分类模型
class TextRNN(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_size, num_layers, output_size, dropout=0.5):
super(TextRNN, self).__init__()
self.embedding = nn.Embedding(vocab_size, embedding_dim)
# 这里batch_first=True,只影响输入和输出。hidden与cell还是batch在第2维
self.lstm = nn.LSTM(embedding_dim, hidden_size, num_layers, bidirectional=True, batch_first=True, dropout=dropout)
self.fc = nn.Linear(hidden_size*2, output_size)
def forward(self, x):
# x :(batch, seq_len) = (163, 20)
# [batch,seq_len,embedding_dim] -> (163, 20, 300)
x = self.embedding(x)
#out=[batch_size, seq_len, hidden_size*2]
#h=[num_layers*2, batch_size, hidden_size]
#c=[num_layers*2, batch_size, hidden_size]
out,(h, c)= self.lstm(x)
# 最后时刻的hidden
out = self.fc(out[:, -1, :])
return out
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(os.getcwd()+'/log', comment='textrnn')
# 训练
# 构建model
model = TextRNN(len(TEXT.vocab), TEXT.vocab.vectors.shape[1], 128, 2, 16).to(DEVICE)
# 利用预训练模型初始化embedding,requires_grad=True,可以fine-tune
model.embedding.weight.data.copy_(TEXT.vocab.vectors)
# 训练模式
model.train()
# 优化和损失
# optimizer = torch.optim.Adam(model.parameters(),lr=0.1, weight_decay=0.1)
optimizer = torch.optim.SGD(model.parameters(),lr=0.1, momentum=0.95, nesterov=True)
criterion = nn.CrossEntropyLoss()
with writer:
for iter in range(600):
for i, batch in enumerate(train_iter):
train_text = batch.text
train_label = batch.label
train_text = train_text.to(DEVICE)
train_label = train_label.to(DEVICE)
out = model(train_text)
loss = criterion(out, train_label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (iter+1) % 10 == 0:
print ('iter [{}/{}], Loss: {:.4f}'.format(iter+1, 300, loss.item()))
#writer.add_graph(model, input_to_model=train_text,verbose=False)
writer.add_scalar('loss',loss.item(),global_step=iter+1)
writer.flush()
writer.close()
model_path = os.path.join(os.getcwd(), "model.h5")
torch.save(model.state_dict(), model_path)
iter [10/300], Loss: 2.7375
iter [20/300], Loss: 2.7167
iter [30/300], Loss: 2.7200
iter [40/300], Loss: 2.7181
iter [50/300], Loss: 2.7152
iter [60/300], Loss: 2.7142
iter [70/300], Loss: 2.7107
iter [80/300], Loss: 2.7006
iter [90/300], Loss: 2.6418
iter [100/300], Loss: 2.3200
iter [110/300], Loss: 2.1519
iter [120/300], Loss: 2.0308
iter [130/300], Loss: 2.3037
iter [140/300], Loss: 2.1125
iter [150/300], Loss: 1.9677
iter [160/300], Loss: 1.8521
iter [170/300], Loss: 1.8255
iter [180/300], Loss: 1.7360
iter [190/300], Loss: 1.7009
iter [200/300], Loss: 1.5200
iter [210/300], Loss: 2.2366
iter [220/300], Loss: 1.3887
iter [230/300], Loss: 1.2162
iter [240/300], Loss: 1.0565
iter [250/300], Loss: 0.8792
iter [260/300], Loss: 0.7540
iter [270/300], Loss: 0.6081
iter [280/300], Loss: 0.5910
iter [290/300], Loss: 0.4691
iter [300/300], Loss: 0.3708
iter [310/300], Loss: 0.3033
iter [320/300], Loss: 0.2526
iter [330/300], Loss: 0.2226
iter [340/300], Loss: 0.2168
iter [350/300], Loss: 0.1647
iter [360/300], Loss: 0.1403
iter [370/300], Loss: 0.1242
iter [380/300], Loss: 0.0933
iter [390/300], Loss: 0.0965
iter [400/300], Loss: 0.0592
iter [410/300], Loss: 0.1430
iter [420/300], Loss: 0.0605
iter [430/300], Loss: 0.0411
iter [440/300], Loss: 0.0747
iter [450/300], Loss: 0.0293
iter [460/300], Loss: 0.0190
iter [470/300], Loss: 0.0196
iter [480/300], Loss: 0.0179
iter [490/300], Loss: 0.0113
iter [500/300], Loss: 0.0102
iter [510/300], Loss: 0.0094
iter [520/300], Loss: 0.0087
iter [530/300], Loss: 0.0168
iter [540/300], Loss: 0.0049
iter [550/300], Loss: 0.0046
iter [560/300], Loss: 0.0051
iter [570/300], Loss: 0.0028
iter [580/300], Loss: 0.0025
iter [590/300], Loss: 0.0021
iter [600/300], Loss: 0.0020