栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

基于Transformer的情感分类实战

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于Transformer的情感分类实战

import math
from collections import defaultdict

import torch
from nltk.corpus import sentence_polarity
from torch import nn, optim
from torch.nn.utils.rnn import pad_sequence
from torch.utils.data import Dataset, DataLoader
from tqdm import tqdm


class Vocab:
    def __init__(self,tokens = None):
        # 使用列表存储所有的标记,从而可根据索引值获取相应的标记
        self.idx_to_token = list()
        # 使用字典实现标记到索引值的映射
        self.token_to_idx = dict()

        if tokens is not None:
            if "" not in tokens:
                tokens = tokens + [""]
            for token in tokens:
                self.idx_to_token.append(token)
                self.token_to_idx[token] = len(self.idx_to_token) - 1

            self.unk = self.token_to_idx[""]

    # 创建词表,text包含若干句子,每个句子由若干标记构成
    @classmethod
    def build(cls,text,min_freq = 1 , reserved_tokens = None):
        # 存储标记及其出现次数的映射字典
        token_freqs = defaultdict(int)
        # 无重复地进行标记
        for sentence in text:
            for token in sentence:
                token_freqs[token] += 1

        # 用户自定义的预留标记
        uniq_tokens = [""] + (reserved_tokens if reserved_tokens else [])
        uniq_tokens += [token for token,freq in token_freqs.items() if freq >= min_freq and token != ""]

        return cls(uniq_tokens)

    # 返回词表的大小,即词表中有多少个互不相同的标记
    def __len__(self):
        return len(self.idx_to_token)

    # 查找输入标记对应的索引值
    def __getitem__(self, token):
        return self.token_to_idx.get(token,self.unk)

    # 查找一系列输入标记对应的索引值
    def convert_tokens_to_ids(self,tokens):
        return [self[token] for token in tokens]

    # 查找一系列索引值对应的标记
    def convert_ids_to_tokens(self,indices):
        return [self.idx_to_token[index] for index in indices]

# 创建存储数据的TransformerDataset的子类 → Transformer
class TransformerDataset(Dataset):
    def __init__(self,data):
        self.data = data
    def __len__(self):
        return len(self.data)
    def __getitem__(self, i):
        return self.data[i]

# 根据批次中每个序列长度生成Mask矩阵,以便处理长度不一致的序列,忽略掉比较短的序列的无效部分
def length_to_mask(lengths):
    max_len = torch.max(lengths)
    mask = torch.arange(max_len).expand(lengths.shape[0], max_len) < lengths.unsqueeze(1)
    return mask



# 数据处理
def collate_fn(examples):
    lengths = torch.tensor([len(ex[0]) for ex in examples])
    inputs = [torch.tensor(ex[0]) for ex in examples]
    targets = torch.tensor([ex[1] for ex in examples],dtype = torch.long)
    # 对batch内的样本进行padding,使其具有相同长度
    inputs = pad_sequence(inputs , batch_first= True)
    return inputs , lengths , targets

# # 位置编码
class PositionalEncoding(nn.Module):
    def __init__(self,d_model,dropout = 0.1,max_len = 512):
        super(PositionalEncoding, self).__init__()

        pe = torch.zeros(max_len,d_model)
        position = torch.arange(0 , max_len , dtype= torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0 , d_model , 2).float() * (-math.log(10000.0) / d_model))
        pe[:,0::2] = torch.sin(position * div_term)
        pe[:,1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0 , 1)
        self.register_buffer("pe",pe)

    def forward(self,x):
        x = x + self.pe[:x.size(0),:]
        return x

# Transformer模型
class Transformer(nn.Module):
    def __init__(self,vocab_size,embedding_dim,hidden_dim,num_class,dim_feedforward = 512,num_head = 2,num_layers = 2,dropout = 0.1,max_len = 128,activation:str = "relu"):
        super(Transformer, self).__init__()
        # 词嵌入层
        self.embedding_dim = embedding_dim
        self.embeddings = nn.Embedding(vocab_size,embedding_dim)
        self.position_embedding = PositionalEncoding(embedding_dim,dropout,max_len)
        # 编码层:Transformer
        encoder_layer = nn.TransformerEncoderLayer(hidden_dim,num_head,dim_feedforward,dropout,activation)
        self.transformer = nn.TransformerEncoder(encoder_layer,num_layers)
        # 输出层
        self.output = nn.Linear(hidden_dim,num_class)

    def forward(self,inputs,lengths):
        inputs = torch.transpose(inputs , 0 ,1)
        hidden_states = self.embeddings(inputs)
        hidden_states = self.position_embedding(hidden_states)

        attention_mask = length_to_mask(lengths) == False
        hidden_states = self.transformer(hidden_states,src_key_padding_mask = attention_mask)
        hidden_states = hidden_states[0 , : , :]

        output = self.output(hidden_states)
        log_probs = torch.log_softmax(output , dim = 1)
        return log_probs

# 设置超参数
embedding_dim = 128
hidden_dim = 128
num_class = 2
batch_size = 32
num_epoch = 5

# 加载数据
def load_sentence_polarity():
    # 使用全部句子集合创建词表
    vocab = Vocab.build(sentence_polarity.sents())
    # 褒贬各4000句作为训练数据
    train_data = [(vocab.convert_tokens_to_ids(sentence),0) for sentence in sentence_polarity.sents(categories="pos")[:4000]] + [
        (vocab.convert_tokens_to_ids(sentence),1) for sentence in sentence_polarity.sents(categories = "neg")[:4000]
    ]

    # 剩下数据作为测试数据
    test_data = [(vocab.convert_tokens_to_ids(sentence), 0) for sentence in
                  sentence_polarity.sents(categories="pos")[4000:]] + [
                     (vocab.convert_tokens_to_ids(sentence), 1) for sentence in
                     sentence_polarity.sents(categories="neg")[4000:]
                 ]
    return train_data,test_data,vocab

train_data , test_data ,vocab = load_sentence_polarity()
train_dataset = TransformerDataset(train_data)
test_dataset = TransformerDataset(test_data)
train_data_loader = DataLoader(train_dataset,batch_size = batch_size,collate_fn = collate_fn,shuffle = True)
test_data_loader = DataLoader(test_dataset,batch_size = 1,collate_fn=collate_fn,shuffle = False)

# 加载模型
device = torch.device("cpu")
model = Transformer(len(vocab),embedding_dim,hidden_dim,num_class)
model.to(device)

# 训练过程
nll_loss = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(),lr = 0.001)

model.train()
for epoch in range(num_epoch):
    total_loss = 0
    for batch in tqdm(train_data_loader,desc=f"Training Epoch{epoch}"):
        inputs , lengths ,targets = [x.to(device) for x in batch]
        log_probs = model(inputs,lengths)
        loss = nll_loss(log_probs,targets)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_loss += loss.item()
    print(f"Loss:{total_loss:.2f}")
    
# 测试过程
acc = 0
for batch in tqdm(test_data_loader,desc = f"Testing"):
    inputs , lengths , targets = [x.to(device) for x in batch]
    with torch.no_grad():
        output = model(inputs,lengths)
        acc += (output.argmax(dim = 1) == targets).sum().item()

#输出在测试集上的准确率
print(f"Acc:{acc / len(test_data_loader):.2f}")

运行结果

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1009158.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号