欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

pytorch?實(shí)現(xiàn)情感分類問題小結(jié)

 更新時(shí)間:2023年02月14日 08:27:33   作者:Icarus_  
本文主要介紹了pytorch?實(shí)現(xiàn)情感分類問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1、詞表映射

無論是深度學(xué)習(xí)還是傳統(tǒng)的統(tǒng)計(jì)機(jī)器學(xué)習(xí)方法處理自然語言,都需要先將輸入的語言符號(hào)(通常為標(biāo)記Token),映射為大于等于0、小于詞表大小的整數(shù),該整數(shù)也被稱作一個(gè)標(biāo)記的索引值或下標(biāo)。

vocab類實(shí)現(xiàn)標(biāo)記和索引之間的相互映射。

from collections import defaultdict, Counter
 
class Vocab:
    def __init__(self, tokens=None):
        self.idx_to_token = list()
        self.token_to_idx = dict()
 
        if tokens is not None:
            if "<unk>" not in tokens:
                tokens = tokens + ["<unk>"]
            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['<unk>']
 
    @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 = ["<unk>"] + (reserved_tokens if reserved_tokens else [])
        uniq_tokens += [token for token, freq in token_freqs.items() \
                        if freq >= min_freq and token != "<unk>"]
        return cls(uniq_tokens)
 
    def __len__(self):
        #返回詞表大小,即詞表有多少個(gè)互不相同的標(biāo)記
        return len(self.idx_to_token)
 
    def __getitem__(self, token):
        #查找對(duì)應(yīng)輸入標(biāo)記的索引
        #不存在,返回標(biāo)記<unk>的索引
        return self.token_to_idx.get(token, self.unk)
 
    def convert_tokens_to_ids(self, tokens):
        #查找一系列輸入標(biāo)記對(duì)應(yīng)的索引
        return [self[token] for token in tokens]
 
    def convert_ids_to_tokens(self, indices):
        #查找一系列索引值對(duì)應(yīng)的標(biāo)記
        return [self.idx_to_token[index] for index in indices]
 
 
def save_vocab(vocab, path):
    with open(path, 'w') as writer:
        writer.write("\n".join(vocab.idx_to_token))
 
 
def read_vocab(path):
    with open(path, 'r') as f:
        tokens = f.read().split('\n')
    return Vocab(tokens)
 

2、詞向量層

在使用深度學(xué)習(xí)進(jìn)行自然語言處理時(shí),將一個(gè)詞(或者標(biāo)記) 轉(zhuǎn)換為一個(gè)低維、稠密、連續(xù)的詞向量(也稱 Embedding ) 是一種基本的詞表示方法,通過 torch.nn 包提供的 Embedding 層即可實(shí)現(xiàn)該功能。

創(chuàng)建 Embedding 對(duì)象,需要兩個(gè)參數(shù)

  • num_embeddings :詞表的大小
  • enbeading-dim: Embedding 向量的維度

實(shí)現(xiàn)的功能是將輸入的整數(shù)張量中每個(gè)整數(shù)(通過詞表映射功能獲得標(biāo)記對(duì)應(yīng)的整數(shù))映射為相應(yīng)維度(embedding_dim)的張量。

示例

import torch
from torch import nn
#詞表大小為8,Embedding向量維度3
embedding=nn.Embedding(8,3)
#輸入形狀(2,4)的整數(shù)張量,相當(dāng)于2個(gè)長度為4的整數(shù)序列,每個(gè)整數(shù)范圍是0-7
input=torch.tensor([[0,1,2,1],[4,6,6,7]],dtype=torch.long)
#調(diào)用embedding
output=embedding(input)
print(output)
print(output.shape)

輸出

張量形狀為[2, 4, 3],即在原始輸入最后增加一個(gè)長度為3 的維

tensor([[[-0.9536,  0.3367, -1.1639],
         [-0.4816, -0.8973, -0.7700],
         [ 1.9007,  1.3130, -1.2717],
         [-0.4816, -0.8973, -0.7700]],
 
        [[ 0.5755,  0.8908, -1.0384],
         [ 0.3724,  0.1216,  2.4466],
         [ 0.3724,  0.1216,  2.4466],
         [ 1.4089,  0.2458,  0.2263]]], grad_fn=<EmbeddingBackward0>)
torch.Size([2, 4, 3])

3、融入詞向量層的多層感知器

基本的多層感知器,輸入為固定大小的實(shí)數(shù)向量。如果輸人為文本,即整數(shù)序列(假設(shè)已經(jīng)利用詞表映射工具將文本中每個(gè)標(biāo)記映射為了相應(yīng)的整數(shù)),在經(jīng)過多層感知器之前,需要利用詞向量層將輸入的整數(shù)映射為向量。

但是,一個(gè)序列中通常含有多個(gè)詞向量,那么如何將它們表示為一個(gè)多層感知器的輸入向量呢?一種方法是將幾個(gè)向量拼接成一個(gè)大小為的向量,其中d表示每個(gè)詞向量的大小。這樣做的一個(gè)問題是最終的預(yù)測結(jié)果與標(biāo)記在序列中的位置過于相關(guān)。例如,如果在一個(gè)序列前面增加一個(gè)標(biāo)記,則序列中的每個(gè)標(biāo)記位置都變了,也就是它們對(duì)應(yīng)的參數(shù)都發(fā)生了變化,那么模型預(yù)測的結(jié)果可能完全不同,這樣顯然不合理。

在自然語言處理中,可以使用詞袋(Bag-Of-Words, Bow)模型解決該問題。詞袋模型指的是在表示序列時(shí),不考慮其中元素的順序,而是將其簡單地看成是一個(gè)集合。于是就可以采用聚合操作處理一個(gè)序列中的多個(gè)詞向量,如求平均、求和或保留最大值等。融入詞向量層以及詞袋模型的多層感知器代碼如下:

import torch
from torch import nn
from torch.nn import functional as F
 
class MLP(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim, num_class):
        super(MLP, self).__init__()
        # 詞嵌入層
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        # 線性變換:詞嵌入層->隱含層
        self.linear1 = nn.Linear(embedding_dim, hidden_dim)
        # 使用ReLU激活函數(shù)
        self.activate = F.relu
        # 線性變換:激活層->輸出層
        self.linear2 = nn.Linear(hidden_dim, num_class)
 
    def forward(self, inputs):
        embeddings = self.embedding(inputs)
        # 將序列中多個(gè)embedding進(jìn)行聚合(此處是求平均值)
        embedding = embeddings.mean(dim=1)
        hidden = self.activate(self.linear1(embedding))
        outputs = self.linear2(hidden)
        # 獲得每個(gè)序列屬于某一類別概率的對(duì)數(shù)值
        probs = F.log_softmax(outputs, dim=1)
        return probs
 
mlp = MLP(vocab_size=8, embedding_dim=3, hidden_dim=5, num_class=2)
# 輸入為兩個(gè)長度為4的整數(shù)序列
inputs = torch.tensor([[0, 1, 2, 1], [4, 6, 6, 7]], dtype=torch.long)
outputs = mlp(inputs)
print(outputs)

輸出:

結(jié)果為每個(gè)序列屬于某一類別的概率的對(duì)數(shù)

tensor([[-1.1612, -0.3756],
        [-0.8089, -0.5894]], grad_fn=<LogSoftmaxBackward0>)

在實(shí)際的自然語言處理任務(wù)中,一個(gè)批次里輸人的文本長度往往是不固定的,因此無法像上面的代碼一樣簡單地用一個(gè)張量存儲(chǔ)詞向量并求平均值。 PyTorch 提供了一種更靈活的解決方案,即EmnbeddingBag 層。在調(diào)用 Embedding-Bag 層時(shí),首先需要將不定長的序列拼按起來,然后使用一個(gè)偏移向量 ( Offsets ) 記錄每個(gè)序列的起始位置。

4、數(shù)據(jù)處理

第一步是將待處理的數(shù)據(jù)從硬盤或者其他地方加載到程序中,此時(shí)讀入的是原始文本數(shù)據(jù),還需要經(jīng)過分句、標(biāo)記解析等 預(yù)處理過程轉(zhuǎn)換為標(biāo)記序列,然后再使用詞表映射工具將每個(gè)標(biāo)記映射到相應(yīng)的索引值。在此,使用 NLTK 提供的句子傾向性分析數(shù)據(jù) (sentence_polarity) 作為示例,

import torch
from vocab import Vocab
 
def load_sentence_polarity():
    from nltk.corpus import sentence_polarity
 
    #使用全部橘子集合(已經(jīng)過標(biāo)記解析)創(chuàng)建詞表
    vocab = Vocab.build(sentence_polarity.sents())
 
    #褒貶各4000句子作為訓(xùn)練數(shù)據(jù),使用創(chuàng)建的詞表將標(biāo)記映射為相應(yīng)的索引
    #褒義標(biāo)簽為0,貶義標(biāo)簽為1
    #每個(gè)樣例是一個(gè)有索引值列表和標(biāo)簽組成的元祖
    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]]
 
    #其余的作為測試數(shù)據(jù)
    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
 
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 load_treebank():
    from nltk.corpus import treebank
    sents, postags = zip(*(zip(*sent) for sent in treebank.tagged_sents()))
 
    vocab = Vocab.build(sents, reserved_tokens=["<pad>"])
 
    tag_vocab = Vocab.build(postags)
 
    train_data = [(vocab.convert_tokens_to_ids(sentence), tag_vocab.convert_tokens_to_ids(tags)) for sentence, tags in zip(sents[:3000], postags[:3000])]
    test_data = [(vocab.convert_tokens_to_ids(sentence), tag_vocab.convert_tokens_to_ids(tags)) for sentence, tags in zip(sents[3000:], postags[3000:])]
 
    return train_data, test_data, vocab, tag_vocab
 

dataset 是 Dataset 類(在torch.utils.data 包中定義)的一個(gè)對(duì)象,用于存儲(chǔ)數(shù)據(jù),一般需要根據(jù)具體的數(shù)據(jù)存取需求創(chuàng)建 Dataset 類的子類。如創(chuàng)建 —個(gè)BowDataset 子類,其中 Bow 是詞袋的意思。具體代碼如下

import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
from collections import defaultdict
from vocab import Vocab
from utils import load_sentence_polarity
 
class BowDataset(Dataset):
    def __init__(self, data):
        #data為原始數(shù)據(jù)
        self.data = data
    def __len__(self):
        #數(shù)據(jù)集中樣例的數(shù)目
        return len(self.data)
    def __getitem__(self, i):
        #返回下標(biāo)i的數(shù)據(jù)
        return self.data[i]
 
#用于對(duì)樣本進(jìn)行整理
def collate_fn(examples):
    #從獨(dú)立樣本集合中構(gòu)建各批次的輸入輸出 
    #其中,BowDataset類定義了一個(gè)樣本的數(shù)據(jù)結(jié)構(gòu),即輸入標(biāo)簽和輪出標(biāo)悠的元組
    # 因此,將輸入inputs定義為一個(gè)張量的列表,其中每個(gè)張量為原始句子中標(biāo)記序列 
    inputs = [torch.tensor(ex[0]) for ex in examples]
    #輸出的目標(biāo)targets 為該批次中全部樣例輸出結(jié)果 (0或1) 構(gòu)成的張量
    targets = torch.tensor([ex[1] for ex in examples], dtype=torch.long)
    # 獲取一個(gè)批次中每個(gè)樣例的序列長度 
    offsets = [0] + [i.shape[0] for i in inputs]
    #根據(jù)序列的長度,轉(zhuǎn)換為每個(gè)序列起始位置的偏移量 
    offsets = torch.tensor(offsets[:-1]).cumsum(dim=0)
    #將inputs列表中的張量拼接成一個(gè)大的張量 
    inputs = torch.cat(inputs)
    return inputs, offsets, targets

5、多層感知機(jī)模型的訓(xùn)練與測試

import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
from collections import defaultdict
from vocab import Vocab
from utils import load_sentence_polarity
 
 
# tqdm是一個(gè)Python模塊,能以進(jìn)度條的方式顯示迭代的進(jìn)度
from tqdm.auto import tqdm
 
# 超參數(shù)設(shè)置
embedding_dim = 128
hidden_dim = 256
num_class = 2
batch_size = 32
num_epoch = 5
 
# 加載數(shù)據(jù)
train_data, test_data, vocab = load_sentence_polarity()
train_dataset = BowDataset(train_data)
test_dataset = BowDataset(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('cuda' if torch.cuda.is_available() else 'cpu')
model = MLP(len(vocab), embedding_dim, hidden_dim, num_class)
model.to(device) # 將模型加載到CPU或GPU設(shè)備
 
#訓(xùn)練過程
nll_loss = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001) # 使用Adam優(yōu)化器
 
model.train()
for epoch in range(num_epoch):
    total_loss = 0
    for batch in tqdm(train_data_loader, desc=f"Training Epoch {epoch}"):
        inputs, offsets, targets = [x.to(device) for x in batch]
        log_probs = model(inputs, offsets)
        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, offsets, targets = [x.to(device) for x in batch]
    with torch.no_grad():
        output = model(inputs, offsets)
        acc += (output.argmax(dim=1) == targets).sum().item()
 
# 輸出在測試集上的準(zhǔn)確率
print(f"Acc: {acc / len(test_data_loader):.2f}")

訓(xùn)練結(jié)果

6、基于卷積神經(jīng)網(wǎng)絡(luò)的情感分類

第3 節(jié)的詞袋模型表示文本,只考慮了文本中詞語的信息,而忽視了詞組信息,如句子

我不喜歡這部電影

詞袋模型看到文本中有“喜歡”一詞,則很可能將其識(shí)別為褒義。而卷積神經(jīng)網(wǎng)絡(luò)可以提取詞組信息,如將卷積核的大小設(shè)置為 2,則可以提取特征“不 喜歡” 等,顯然這對(duì)于最終情感極性的判斷至關(guān)重要。卷積神經(jīng)網(wǎng)絡(luò)的大部分代碼與多層感知器的實(shí)現(xiàn)一致

其中的不同之處:

  • 模型不同,需要從 nn. Module 類派生一個(gè) CMN 子類
  • 在調(diào)用卷積神經(jīng)網(wǎng)絡(luò)時(shí),還需要設(shè)置兩個(gè)額外的超參數(shù),分別為filter_size =3(卷積核的大?。┖蚽um_tilter =100(卷積核的個(gè)數(shù))
  • 數(shù)據(jù)整理函數(shù)(collate_fn),pad_seguence 兩數(shù)實(shí)現(xiàn)補(bǔ)齊 (Padding)功能,使得一個(gè)批次中 全部宇列長度相同(同最大長度序列),不足的默認(rèn)使用0補(bǔ)齊。

除了以上不同,其他代碼與多層感知器的實(shí)現(xiàn)幾乎一致。如要實(shí)現(xiàn)一個(gè)基于新模型的情感分類任務(wù),只需要定義一個(gè)nn.Module類的子類,并修改數(shù)據(jù)整理函數(shù)(collate_fn)即可

import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
from torch.nn.utils.rnn import pad_sequence
from collections import defaultdict
from vocab import Vocab
from utils import load_sentence_polarity
 
class CnnDataset(Dataset):
    def __init__(self, data):
        self.data = data
    def __len__(self):
        return len(self.data)
    def __getitem__(self, i):
        return self.data[i]
 
#另外,數(shù)據(jù)整理函數(shù)也需要進(jìn)行一些修改。  
# pad_seguence 兩數(shù)實(shí)現(xiàn)補(bǔ)齊 (Padding)功能,使得一個(gè)批次中 全部序列長度相同(同最大長度序列),不足的默認(rèn)使用0補(bǔ)齊。 
def collate_fn(examples):
    inputs = [torch.tensor(ex[0]) for ex in examples]
    targets = torch.tensor([ex[1] for ex in examples], dtype=torch.long)
    # 對(duì)batch內(nèi)的樣本進(jìn)行padding,使其具有相同長度
    inputs = pad_sequence(inputs, batch_first=True)
    return inputs, targets
 
#模型不同,需要從nn.Module類派生一個(gè) CNN 子類
class CNN(nn.Module):
    def __init__(self, vocab_size, embedding_dim, filter_size, num_filter, num_class):
        super(CNN, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        #padding=1表示在卷積操作之前,將序列的前后各補(bǔ)充1個(gè)輸入
        self.conv1d = nn.Conv1d(embedding_dim, num_filter, filter_size, padding=1)
        self.activate = F.relu
        self.linear = nn.Linear(num_filter, num_class)
    def forward(self, inputs):
        embedding = self.embedding(inputs)
        convolution = self.activate(self.conv1d(embedding.permute(0, 2, 1)))
        pooling = F.max_pool1d(convolution, kernel_size=convolution.shape[2])
        outputs = self.linear(pooling.squeeze(dim=2))
        log_probs = F.log_softmax(outputs, dim=1)
        return log_probs
 
#tqdm是一個(gè)Pyth模塊,能以進(jìn)度條的方式顯示迭代的進(jìn)度
from tqdm.auto import tqdm
 
#超參數(shù)設(shè)置
embedding_dim = 128
hidden_dim = 256
num_class = 2
batch_size = 32
num_epoch = 5
#在調(diào)用卷積神經(jīng)網(wǎng)絡(luò)時(shí),還需要設(shè)置兩個(gè)額外的超參數(shù),分別為filter_size =3(卷積核的大?。┖蚽um_tilter =100(卷積核的個(gè)數(shù))。
filter_size = 3
num_filter = 100
 
#加載數(shù)據(jù)
train_data, test_data, vocab = load_sentence_polarity()
train_dataset = CnnDataset(train_data)
test_dataset = CnnDataset(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('cuda' if torch.cuda.is_available() else 'cpu')
model = CNN(len(vocab), embedding_dim, filter_size, num_filter, num_class)
model.to(device) #將模型加載到CPU或GPU設(shè)備
 
#訓(xùn)練過程
nll_loss = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001) #使用Adam優(yōu)化器
 
model.train()
for epoch in range(num_epoch):
    total_loss = 0
    for batch in tqdm(train_data_loader, desc=f"Training Epoch {epoch}"):
        inputs, targets = [x.to(device) for x in batch]
        log_probs = model(inputs)
        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, targets = [x.to(device) for x in batch]
    with torch.no_grad():
        output = model(inputs)
        acc += (output.argmax(dim=1) == targets).sum().item()
 
#輸出在測試集上的準(zhǔn)確率
print(f"Acc: {acc / len(test_data_loader):.2f}")

訓(xùn)練結(jié)果

7、基于循環(huán)神經(jīng)網(wǎng)絡(luò)的情感分類

第3 節(jié)的詞袋模型還忽路了文本中詞的順序信息,因此對(duì)于兩個(gè)句子 “張三行李四”和“李四行張三”,它們的表示是完全相同的,但顯然這并不合理。循環(huán)神經(jīng)網(wǎng)絡(luò)模型能更好地對(duì)序列數(shù)據(jù)進(jìn)行表示。

以長短時(shí)記憶(LSTM) 網(wǎng)絡(luò)為例。

其中,大部分代碼與前面的實(shí)現(xiàn)一致,不同之處:

  • 需要從nn.Module派生一個(gè)LSTM子類
  • forward中使用 pack_padded_sequence將變長序列打包,功能是將之前補(bǔ)齊過的一個(gè)小批次序列打包成一個(gè)序列,其中每個(gè)原始序列的長度存儲(chǔ)在lengths中,能被self.lstm直接調(diào)用
import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence
from collections import defaultdict
from vocab import Vocab
from utils import load_sentence_polarity
 
#tqdm是一個(gè)Python模塊,能以進(jìn)度條的方式顯式迭代的進(jìn)度
from tqdm.auto import tqdm
 
class LstmDataset(Dataset):
    def __init__(self, data):
        self.data = data
    def __len__(self):
        return len(self.data)
    def __getitem__(self, i):
        return self.data[i]
 
#整理函數(shù)
def collate_fn(examples):
    #獲得每個(gè)序列的長度
    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)
    # 對(duì)batch內(nèi)的樣本進(jìn)行padding,使其具有相同長度
    inputs = pad_sequence(inputs, batch_first=True)
    return inputs, lengths, targets
 
#需要從nn.Module派生一個(gè)LSTM子類
class LSTM(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim, num_class):
        super(LSTM, self).__init__()
        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim, batch_first=True)
        self.output = nn.Linear(hidden_dim, num_class)
 
    def forward(self, inputs, lengths):
        embeddings = self.embeddings(inputs)
        #使用 pack_padded_sequence將變長序列打包
        x_pack = pack_padded_sequence(embeddings, lengths, batch_first=True, enforce_sorted=False)
        hidden, (hn, cn) = self.lstm(x_pack)
        outputs = self.output(hn[-1])
        log_probs = F.log_softmax(outputs, dim=-1)
        return log_probs
 
embedding_dim = 128
hidden_dim = 256
num_class = 2
batch_size = 32
num_epoch = 5
 
#加載數(shù)據(jù)
train_data, test_data, vocab = load_sentence_polarity()
train_dataset = LstmDataset(train_data)
test_dataset = LstmDataset(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('cuda' if torch.cuda.is_available() else 'cpu')
model = LSTM(len(vocab), embedding_dim, hidden_dim, num_class)
model.to(device) #將模型加載到GPU中(如果已經(jīng)正確安裝)
 
#訓(xùn)練過程
nll_loss = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001) #使用Adam優(yōu)化器
 
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()
 
#輸出在測試集上的準(zhǔn)確率
print(f"Acc: {acc / len(test_data_loader):.2f}")

訓(xùn)練結(jié)果

8、基于 Transformer 的情感分類

基于 Transformer 實(shí)現(xiàn)情感分類與使用 LSTM 也非常相似,主要有一處不同, 即需要定義 Transformer模型。

# Defined in Section 4.6.8
 
import math
import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence
from collections import defaultdict
from vocab import Vocab
from utils import load_sentence_polarity, length_to_mask
 
# tqdm是一個(gè)Pyth模塊,能以進(jìn)度條的方式顯式迭代的進(jìn)度
from tqdm.auto import tqdm
 
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]
 
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)
    # 對(duì)batch內(nèi)的樣本進(jìn)行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
 
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)
        #與LSTM處理情況相同,輸入的數(shù)據(jù)的第一維是批次,需要轉(zhuǎn)換為TransformerEncoder
        #所需要的第1維是長度,第二維是批次的形狀
        hidden_states = self.embeddings(inputs)
        hidden_states = self.position_embedding(hidden_states)
        #length_to_mask作用是根據(jù)批次中每個(gè)序列的長度生成mask矩陣,以便處理長度不一致的序列,忽略掉比較短的序列的無效部分
        #src_key_padding_mask的參數(shù)正好與length_to_mask的結(jié)果相反(無自注意力的部分為true)
        attention_mask = length_to_mask(lengths.to('cpu')) == False
        #根據(jù)批次中的每個(gè)序列長度生成mask矩陣
        hidden_states = self.transformer(hidden_states, src_key_padding_mask=attention_mask.to('cuda'))
        hidden_states = hidden_states[0, :, :]
        #取第一個(gè)標(biāo)記的輸出結(jié)果作為分類層的輸入
        output = self.output(hidden_states)
        log_probs = F.log_softmax(output, dim=1)
        return log_probs
 
embedding_dim = 128
hidden_dim = 128
num_class = 2
batch_size = 32
num_epoch = 5
 
# 加載數(shù)據(jù)
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('cuda')
model = Transformer(len(vocab), embedding_dim, hidden_dim, num_class)
model.to(device) # 將模型加載到GPU中(如果已經(jīng)正確安裝)
 
# 訓(xùn)練過程
nll_loss = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001) # 使用Adam優(yōu)化器
 
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()
 
# 輸出在測試集上的準(zhǔn)確率
print(f"Acc: {acc / len(test_data_loader):.2f}")

訓(xùn)練結(jié)果

到此這篇關(guān)于pytorch 實(shí)現(xiàn)情感分類問題小結(jié)的文章就介紹到這了,更多相關(guān)pytorch 實(shí)現(xiàn)情感分類 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • python利用pymysql和openpyxl實(shí)現(xiàn)操作MySQL數(shù)據(jù)庫并插入數(shù)據(jù)

    python利用pymysql和openpyxl實(shí)現(xiàn)操作MySQL數(shù)據(jù)庫并插入數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了如何使用Python連接MySQL數(shù)據(jù)庫,并從Excel文件中讀取數(shù)據(jù),將其插入到MySQL數(shù)據(jù)庫中,有需要的小伙伴可以參考一下
    2023-10-10
  • python 類之間的參數(shù)傳遞方式

    python 類之間的參數(shù)傳遞方式

    今天小編就為大家分享一篇python 類之間的參數(shù)傳遞方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • python json.loads兼容單引號(hào)數(shù)據(jù)的方法

    python json.loads兼容單引號(hào)數(shù)據(jù)的方法

    今天小編就為大家分享一篇python json.loads兼容單引號(hào)數(shù)據(jù)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • Python線性網(wǎng)絡(luò)實(shí)現(xiàn)分類糖尿病病例

    Python線性網(wǎng)絡(luò)實(shí)現(xiàn)分類糖尿病病例

    什么是線性規(guī)劃?想象一下,您有一個(gè)線性方程組和不等式系統(tǒng)。這樣的系統(tǒng)通常有許多可能的解決方案。線性規(guī)劃是一組數(shù)學(xué)和計(jì)算工具,可讓您找到該系統(tǒng)的特定解,該解對(duì)應(yīng)于某些其他線性函數(shù)的最大值或最小值
    2022-10-10
  • 解決django服務(wù)器重啟端口被占用的問題

    解決django服務(wù)器重啟端口被占用的問題

    今天小編就為大家分享一篇解決django服務(wù)器重啟端口被占用的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • Django-celery-beat動(dòng)態(tài)添加周期性任務(wù)實(shí)現(xiàn)過程解析

    Django-celery-beat動(dòng)態(tài)添加周期性任務(wù)實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Django-celery-beat動(dòng)態(tài)添加周期性任務(wù)實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Python自動(dòng)化測試框架之unittest使用詳解

    Python自動(dòng)化測試框架之unittest使用詳解

    unittest是Python自動(dòng)化測試框架之一,提供了一系列測試工具和接口,支持單元測試、功能測試、集成測試等多種測試類型。unittest使用面向?qū)ο蟮乃枷雽?shí)現(xiàn)測試用例的編寫和管理,可以方便地?cái)U(kuò)展和定制測試框架,支持多種測試結(jié)果輸出格式
    2023-04-04
  • python3+pyqt5+itchat微信定時(shí)發(fā)送消息的方法

    python3+pyqt5+itchat微信定時(shí)發(fā)送消息的方法

    今天小編就為大家分享一篇python3+pyqt5+itchat微信定時(shí)發(fā)送消息的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-02-02
  • Python基礎(chǔ)之字符串格式化詳解

    Python基礎(chǔ)之字符串格式化詳解

    這篇文章主要介紹了Python基礎(chǔ)之字符串格式化詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python基礎(chǔ)的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-04-04
  • Python使用pywebview開發(fā)桌面應(yīng)用的全過程

    Python使用pywebview開發(fā)桌面應(yīng)用的全過程

    當(dāng)使用桌面應(yīng)用程序的時(shí)候,有沒有那么一瞬間,想學(xué)習(xí)一下桌面應(yīng)用程序開發(fā)?下面這篇文章主要給大家介紹了關(guān)于Python使用pywebview開發(fā)桌面應(yīng)用的相關(guān)資料,需要的朋友可以參考下
    2022-06-06

最新評(píng)論