Sử dụng TextCNN để Nhận diện Ý định trong Hệ thống Hỏi-Đáp

Một. Áp dụng TextCNN cho phân loại văn bản nhằm nhận diện ý định trong hệ thống hỏi-đáp.

Ba. Mã nguồn (Toàn bộ mã nguồn: https://github.com/jiangnanboy/movie_knowledge_graph_app/tree/master/intent_classification/pytorch/textcnn)

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

THIET_BI = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

duong_dan_phan_loai_y_dinh = os.path.abspath(os.path.join(os.getcwd(), '../..'))
# Đường dẫn dữ liệu huấn luyện
du_lieu_huan_luyen = os.path.join(duong_dan_phan_loai_y_dinh,'classification_data/classification_data.csv')
# Đọc dữ liệu
du_lieu_huan_luyen = pd.read_csv(du_lieu_huan_luyen)
# Tách theo ký tự
ham_tach_tu =lambda x: x.split(' ')

VAN_BEN = data.Field(
                    sequential=True,
                    tokenize=ham_tach_tu,
                    lower=True,
                    use_vocab=True,
                    pad_token='<pad>',
                    unk_token='<unk>',
                    batch_first=True,
                    fix_length=20)

NHAN = data.Field(
                    sequential=False,
                    use_vocab=False)
# Lấy tập dữ liệu huấn luyện hoặc kiểm tra
def lay_tap_dieu(csv_data, text_field, label_field, kiem_tra=False):
    truong_du_lieu = [('id', None), ('text', text_field), ('label', label_field)]
    mau_vd = []
    if kiem_tra: # Tập kiểm tra, không tải nhãn
        for text in csv_data['text']:
            mau_vd.append(data.Example.fromlist([None, text, None], truong_du_lieu))
    else: # Tập huấn luyện
        for text, label in zip(csv_data['text'], csv_data['label']):
            mau_vd.append(data.Example.fromlist([None, text, label], truong_du_lieu))
    return mau_vd, truong_du_lieu

mau_huan_luyen,truong_huan_luyen = lay_tap_dieu(du_lieu_huan_luyen, VAN_BEN, NHAN)

tap_huan_luyen = data.Dataset(mau_huan_luyen, truong_huan_luyen)
# Word embedding đã được huấn luyện trước
duong_dan_embedding = os.path.join(os.getcwd(), 'sgns.sogou.char')
vector = Vectors(name=duong_dan_embedding)
# Xây dựng từ điển
VAN_BEN.build_vocab(tap_huan_luyen, min_freq=1, vectors = vector)

duong_dan_tu = os.path.join(os.getcwd(), 'words.pkl')
with open(duong_dan_tu, 'wb') as f_tu:
    pickle.dump(VAN_BEN.vocab, f_tu)
    
KICH_THUOC_LOP = 163
# Xây dựng iterator
lap_huan_luyen = BucketIterator(
                            dataset=tap_huan_luyen,
                            batch_size=KICH_THUOC_LOP,
                            shuffle=True,
                            sort_within_batch=False)


# Xây dựng mô hình phân loại
class MauTextCNN(nn.Module):
    def __init__(self, kich_thuoc_tu_van_ben, chieu_embedding, so_luong_ra, so_luong_bo_loc=100, kich_thuoc_bo_loc=(3,4,5), ty_le_thoat=0.5):
        '''
        kich_thuoc_tu_van_ben: Kích thước từ điển
        chieu_embedding: Kích thước embedding từ
        so_luong_ra: Số lượng lớp đầu ra
        so_luong_bo_loc: Số lượng kernel tích chập
        kich_thuoc_bo_loc(3,4,5): Ba kích thước kernel tích chập, mỗi loại có so_luong_bo_loc kernel, chiều rộng kernel đều là chieu_embedding
        '''
        super(MauTextCNN, self).__init__()
        self.embedding = nn.Embedding(kich_thuoc_tu_van_ben, chieu_embedding)
        # conv2d(in_channel,out_channel,kernel_size,stride,padding),stride mặc định là 1, padding mặc định là 0
        self.tich_chap = nn.ModuleList([nn.Conv2d(1, so_luong_bo_loc,(k, chieu_embedding)) for k in kich_thuoc_bo_loc])
        self.thoat = nn.Dropout(ty_le_thoat)
        self.tuyen_tinh = nn.Linear(so_luong_bo_loc * len(kich_thuoc_bo_loc), so_luong_ra)

    '''
    Phương thức forward với cách tính tích chập và pooling:

    1. Tích chập
    Công thức tính shape sau tích chập: np.floor((n + 2p - f)/s + 1)
    Input shape:(batch, in_channel, hin, win) = (163, 1, 20, 300), 20 là độ dài câu, 300 là kích thước embedding
    Output shape:
    hout=(20 + 2 * 0 - 1 * (3 - 1) - 1)/1 + 1 = 18
    wout=(300 + 2 * 0 - 1 * (300 - 1) -1)/1 + 1 = 1
    =>
    output:(batch, out_channel, hout, wout) = (163, 100, 18, 1)

    2. MaxPool1d
    Công thức đơn giản: np.floor((l + 2p - f)/s + 1)
    Input shape:(N,C,L):(163, 100, 18, 1) -> squeeze(3) -> (163, 100, 18)
    Output shape:
    lout = (18 + 2*0 - 18)/18 +1 = 1 -> (163, 100, 1)
    '''
    def xuly_tien(self, x):
        # x :(batch, seq_len) = (163, 20)
        x = self.embedding(x) # [batch,word_num,embedding_dim] = [N,H,W] -> (163, 20, 300)
        x = x.unsqueeze(1) # [batch, channel, word_num, embedding_dim] = [N,C,H,W] -> (163, 1, 20, 300)
        x = [F.relu(conv(x)).squeeze(3) for conv in self.tich_chap] # len(filter_size) * (N, filter_num, H) -> 3 * (163, 100, 18)
        # MaxPool1d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False),stride mặc định là kernal_size
        x = [F.max_pool1d(output,output.shape[2]).squeeze(2) for output in x] # len(filter_size) * (N, filter_num) -> 3 * (163, 100)
        x = torch.cat(x, 1) # (N, filter_num * len(filter_size)) -> (163, 100 * 3)
        x = self.thoat(x)
        x = self.tuyen_tinh(x)
        return x
from torch.utils.tensorboard import SummaryWriter
ghi_chu = SummaryWriter(os.getcwd()+'/log', comment='textcnn')

# Huấn luyện

# Xây dựng model
model = MauTextCNN(len(VAN_BEN.vocab),VAN_BEN.vocab.vectors.shape[1],16).to(THIET_BI)
# Sử dụng embedding đã huấn luyện trước để khởi tạo, requires_grad=True để fine-tune
model.embedding.weight.data.copy_(VAN_BEN.vocab.vectors)
# Chế độ huấn luyện
model.train()
# Tối ưu và hàm mất mát
#optimizer = torch.optim.Adam(model.parameters(),lr=0.1, weight_decay=0.01)
optimizer = torch.optim.SGD(model.parameters(),lr=0.1, momentum=0.9, nesterov=True)
ham_mat_mat = nn.CrossEntropyLoss()
        
for vong_lap in range(300):
    for i, batch in enumerate(lap_huan_luyen):
        van_ben_mau = batch.text
        nhan_mau = batch.label
        van_ben_mau = van_ben_mau.to(THIET_BI)
        nhan_mau = nhan_mau.to(THIET_BI)
        ket_qua = model.xuly_tien(van_ben_mau)
        mat_mat = ham_mat_mat(ket_qua, nhan_mau)
        optimizer.zero_grad()
        mat_mat.backward()
        optimizer.step()
        if (vong_lap+1) % 10 == 0:
                print ('vong_lap [{}/{}], Loss: {:.4f}'.format(vong_lap+1, 300, mat_mat.item()))
        ghi_chu.add_scalar('loss',mat_mat.item(),global_step=vong_lap+1)
ghi_chu.flush()
ghi_chu.close()
            
duong_dan_model = os.path.join(os.getcwd(), "model.h5")
torch.save(model.state_dict(), duong_dan_model)

Thẻ: TextCNN nhận diện ý định PyTorch xử lý ngôn ngữ tự nhiên

Đăng vào ngày 30 tháng 7 lúc 17:11