日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用双向LSTM进行情感分析详解

發布時間:2024/3/24 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用双向LSTM进行情感分析详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、原理簡介

RNN能夠記憶上下文信息,因此常常用來處理時間序列數據。雖然理論上RNN能夠記憶無限長度的歷史信息,但是由于梯度的累計導致計算量過大無法實際操作,因此在應用中實際上RNN只能記錄前面若干個單詞的信息。由此就延申出了GRU和LSTM的網絡結構,主要是增加門控機制來控制信息的流動,從而提高對歷史信息的記憶能力,擯棄冗余的無關信息。本文將使用較為簡單的雙向LSTM來完成情感分析。

二、數據處理

本文使用aclImdb數據集,包含了50000條評論數據及其標簽,標簽包含積極和消極兩個類別。按1:1的比例將其分為訓練集和測試集。下面先導入要用到的包:

import collections import os import random import tarfile import torch from torch import nn import torchtext.vocab as Vocab import torch.utils.data as Dataimport sys sys.path.append("..") os.environ["CUDA_VISIBLE_DEVICES"] = "0" device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') DATA_ROOT='C:/Users/wuyang/Datasets'

下面是數據讀取,將標簽轉化為0,1標量。

fname = os.path.join(DATA_ROOT, "aclImdb_v1.tar.gz") if not os.path.exists(os.path.join(DATA_ROOT, "aclImdb")):print("從壓縮包解壓...")with tarfile.open(fname, 'r') as f:f.extractall(DATA_ROOT) from tqdm import tqdm def read_imdb(folder='train', data_root='C:/Users/wuyang/Datasets/aclImdb'): data = []for label in ['pos', 'neg']:folder_name = os.path.join(data_root, folder, label)for file in tqdm(os.listdir(folder_name)):with open(os.path.join(folder_name, file), 'rb') as f:review = f.read().decode('utf-8').replace('\n', '').lower()data.append([review, 1 if label == 'pos' else 0])random.shuffle(data)return datatrain_data, test_data = read_imdb('train'), read_imdb('test')

此后,對評論進行分詞。英語評論的分詞有多種,我們直接對評論按空格分開即可取得較好的效果。

def get_tokenized_imdb(data):#分詞"""data: list of [string, label]"""def tokenizer(text):return [tok.lower() for tok in text.split(' ')]return [tokenizer(review) for review, _ in data]

下面利用Counter函數統計訓練集內單詞的數量和種類,并過濾掉詞頻小于5的單詞。

def get_vocab_imdb(data):tokenized_data = get_tokenized_imdb(data)counter = collections.Counter([tk for st in tokenized_data for tk in st])return Vocab.Vocab(counter, min_freq=5) #統計詞頻并過濾掉詞頻小于5的詞 vocab = get_vocab_imdb(train_data)

由于RNN的輸入是定長的序列,而每條評論的長度都是不盡相同的,因此,我們需要對序列進行定長化,方法是設置一個標準序列長度,然后將更長的評論裁剪為定長,將較短的評論補0至定長。此處我們取定長為500.

def preprocess_imdb(data,vocab):#較短文本補長,較長文本裁剪,得到統一長序列max_l=500def pad(x):return x[:max_l] if len(x)>max_l else x+[0]*(max_l-len(x))tokenized_data=get_tokenized_imdb(data)features=torch.tensor([pad([vocab.stoi[word] for word in words]) for words in tokenized_data])labels = torch.tensor([score for _,score in data])return features,labels

下面我們建立訓練和測試迭代器,并設置批量大小為64.

batch_size = 64#批量大小 train_set = Data.TensorDataset(*preprocess_imdb(train_data, vocab)) test_set = Data.TensorDataset(*preprocess_imdb(test_data, vocab)) train_iter = Data.DataLoader(train_set, batch_size, shuffle=True) test_iter = Data.DataLoader(test_set, batch_size)

三、模型構建

下面我們使用pytorch搭建一個BiRNN網絡。注意,網絡的輸入為(batch_size,seq_length),也就是批量大小和序列長度。輸入序列先進入Embedding層得到每個單詞對應的詞向量,也就是將每個單詞轉化為固定維度的向量。此處的embed_size設置為300。通過Embedding層后,得到形狀為(batch_size,seq_length,embed_size)的中間結果。
此后,我們將該中間結果轉秩一下,得到(seq_length,batch_size,embed_size)的tensor.然后將該序列輸入到BiLSTM模塊。BiLSTM將會輸出最后一層結構的每個時間步的輸出,形狀為(seq_length, batch_size, num_directions * hidden_size)。此處num_directions為2。此后,我們連接output的初始時間步和最終時間步的隱藏狀態, 得到(batch_size,num_directionshidden_size2)的tensor。最后,我們將這個tensor輸入全連接層,轉化成(batch_size,2)的向量,完成對序列的分類工作。

class BiRNN(nn.Module):def __init__(self,vocab,embed_size,num_hidden,num_layers):super(BiRNN,self).__init__()self.embedding=nn.Embedding(len(vocab),embed_size)self.encoder=nn.LSTM(input_size=embed_size,hidden_size=num_hidden,num_layers=num_layers,bidirectional=True)self.decoder=nn.Linear(4*num_hidden,2)def forward(self,x):# inputs的形狀是(批量大小, seq_len),因為LSTM需要將序列長度(seq_len)作為第一維,所以將輸入轉置后# 再提取詞特征,輸出形狀為(seq_len, 批量大小, 詞向量維度)embeddings=self.embedding(x.permute(1,0))# outputs形狀是(seq_len, 批量大小, 2 * 隱藏單元個數)outputs,_=self.encoder(embeddings)# 連結初始時間步和最終時間步的隱藏狀態作為全連接層輸入。它的形狀為# (批量大小, 4 * 隱藏單元個數)。encoding = torch.cat((outputs[0], outputs[-1]), -1)outs = self.decoder(encoding)return outs embed_size, num_hiddens, num_layers = 100, 100, 2 net = BiRNN(vocab, embed_size, num_hiddens, num_layers)

以上模型用到了Embedding層,需要對詞向量進行訓練。這一步或許會耗費較多的時間并且效果不盡人意。一個更好的選擇是加載已經訓練好的詞向量直接使用。下面是加載預訓練詞向量的方法。

glove_vocab=Vocab.GloVe(name='6B',dim=100,cache=os.path.join(DATA_ROOT, "glove"))#加載預訓練詞向量 def load_pretrained_embedding(words,pretrained_vocab):#加載預訓練詞向量embed=torch.zeros(len(words),pretrained_vocab.vectors[0].shape[0])oov_count=0for i,word in enumerate(words):try:idx=pretrained_vocab.stoi[word]embed[i,:]=pretrained_vocab.vectors[idx]except KeyError:oov_count+=1if oov_count>0:print('words number:',oov_count)return embed net.embedding.weight.data.copy_( load_pretrained_embedding(vocab.itos,glove_vocab) ) net.embedding.weight.requires_grad = False

四、模型訓練

模型訓練就跟其他網絡沒有什么區別了,流程如下,不做過多贅述。

lr, num_epochs = 0.01, 5 # 要過濾掉不計算梯度的embedding參數,因為加載使用的是預訓練詞向量,所以embedding層不更新參數,設置梯度不存在 optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=lr) loss = nn.CrossEntropyLoss() def train(train_iter, test_iter, net, loss, optimizer, device, num_epochs):net = net.to(device)print("training on ", device)batch_count = 0for epoch in range(num_epochs):train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()for X, y in train_iter:X = X.to(device)y = y.to(device)y_hat = net(X)l = loss(y_hat, y) optimizer.zero_grad()l.backward()optimizer.step()train_l_sum += l.cpu().item()train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()n += y.shape[0]batch_count += 1test_acc = evaluate_accuracy(test_iter, net)print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start)) train(train_iter, test_iter, net, loss, optimizer, device, num_epochs)

下面是訓練結果:

training on cpu epoch 1, loss 0.6216, train acc 0.629, test acc 0.808, time 2335.8 sec epoch 2, loss 0.2077, train acc 0.812, test acc 0.823, time 2106.4 sec epoch 3, loss 0.1243, train acc 0.836, test acc 0.836, time 2053.3 sec epoch 4, loss 0.0849, train acc 0.854, test acc 0.828, time 1915.6 sec

總結

以上是生活随笔為你收集整理的使用双向LSTM进行情感分析详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。