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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

[实现] 利用 Seq2Seq 预测句子后续字词 (Pytorch)2

發布時間:2023/11/28 生活经验 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [实现] 利用 Seq2Seq 预测句子后续字词 (Pytorch)2 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近有個任務:利用 RNN 進行句子補全,即給定一個不完整的句子,預測其后續的字詞。
本文使用了 Seq2Seq 模型,輸入為 5 個中文字詞,輸出為 1 個中文字詞。
目錄

關于RNN

語料預處理

搭建數據集

搭建模型

訓練模型

測試模型

保存/加載模型

1.關于RNN

自被提出以來,循環神經網絡(Recurrent Neural Networks,RNN) 在 NLP 領域取得了巨大的成功與廣泛的應用,也由此催生出了許多新的變體與網絡結構。由于網上有眾多資料,在此我也只做簡單的講解了。
首先,講講 RNN cell 及其變體:
(1) vallina RNN cell

不同于常見的神經網絡結構,RNN 的輸入為時序輸入,每一時刻的輸入對神經元的隱狀態產生影響,從而影響后續所有時刻的輸出。
其中,隱藏層的公式如下所示:
Ot=g(V?St),St=f(U?Xt+W?St?1) O_{t} = g(V* S_t), S_t = f(U*X_t+W*S_{t-1})
O
t
?
=g(V?S
t
?
),S
t
?
=f(U?X
t
?
+W?S
t?1
?
)


(2) LSTM cell

LSTM(Long short-term memory,長短期記憶)極大程度的解決了長序列訓練過程中的梯度消失和梯度爆炸問題。


(3) GRU cell
GRU(Gate Recurrent Unit)與 LSTM 一樣,也極大程度的解決了長序列訓練過程中的梯度消失和梯度爆炸問題。但是,與 LSTM 相比,GRU 所需要的計算資源更小,往往工程實現時更傾向于使用 GRU。


接著,講講網絡結構:

(1) 常見結構:


(2) Bi-directional RNN


(3) Deep Bi-directional RNN


(4) Seq2Seq


(5) Attention


參考資料:

循環神經網絡(RNN, Recurrent Neural Networks)介紹
人人都能看懂的LSTM
人人都能看懂的GRU
從Encoder到Decoder實現Seq2Seq模型
Attention學習筆記
2.語料預處理

由于這次使用的語料為中文語料,自然需要對其進行分詞,并構造詞典。
首先,收集所用的句子,利用 jieba 庫,對每個句子進行分詞,并將所得結果加入 word_set 中。
接著,對 word_set 中的所有字詞構建統計詞典。

代碼:

import os
import json
import jieba
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable

import torchvision
import torchvision.datasets as datasets
import torchvision.transforms as transforms

from torch.utils.data import DataLoader,Dataset

# Set Hyper Parameters
LR = 0.005
EPOCH = 100
BATCH_SIZE = 1
Sentence_Num = 100
Embedding_Dim = None

# Bulid Vocab

sentence_set = [] # 收集所用到的文本句子
for index in range(Sentence_Num):
with open('../../Corpus/CAIL2018/'+str(index)+'.txt','r',encoding='UTF-8') as f:
sentence_set.append(f.read().replace('\n', '').replace('\r', '').replace(',', ' ').replace('。', ' ').replace(':', ' ').replace(' ', ' ').lower())

word_set = set() # 利用jieba庫進行中文分詞
for sentence in sentence_set:
words = jieba.lcut(sentence)
word_set.update(words)

word_to_ix = {'SOS':0, 'EOS':1, 'UNK':2} # 'SOS': start of sentencex
ix_to_word = {0:'SOS', 1:'EOS', 2:'UNK'} # 'EOS': end of sentence
# 'UNK': unknown token
for word in word_set: # 構建詞典,注意:word_to_ix用于對字詞進行編號,ix_to_word用于將模型的輸出轉化為字詞
if word not in word_to_ix:
word_to_ix[word] = len(word_to_ix)
ix_to_word[len(ix_to_word)] = word

Embedding_Dim = len(word_to_ix)

with open('./Vocab.txt','w',encoding='UTF-8') as f: # 保存詞典
for vocab in word_to_ix.items():
f.write(vocab[0]+' '+str(vocab[1])+'\n')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
參考資料:

[Pytorch] - No.2 Pytorch 實現RNN語言模型
GitHub - fxsjy/jieba: 結巴中文分詞
3.搭建數據集

由于所使用的中文文本并無數據集格式,故我們需要自己制作數據集。
注意,代碼中的 bulid_one_hot 并非生成 one-hot 向量。這是因為模型中使用了 nn.Embedding() ,它會初始一個矩陣,相當于我們模型再訓練過程中,順便訓練了一個 word embedding matrix。
至于如何使用該函數進行 word embedding ,大家可以查閱本小節的參考資料。

代碼:

# Bulid Dataset

def bulid_one_hot(word,word_dict):
if word in word_dict:
return torch.LongTensor([word_dict[word]])
return torch.LongTensor([word_dict['UNK']])


class MyDataset(Dataset):
def __init__(self, words, labels, transform=None, target_transform=None):
self.words = words
self.labels = labels
self.transform = transform
self.target_transform = target_transform

def __getitem__(self, index):
words, labels = self.words[index], self.labels[index]
if self.transform is not None:
words = [self.transform(word) for word in words]
if self.target_transform is not None:
labels = self.target_transform(labels)
return words, labels

def __len__(self):
return len(self.labels)

train_words, train_labels, test_words, test_labels = [], [], [], []

for i in range(int(0.9*Sentence_Num)):
sentence = sentence_set[i]
words = jieba.lcut(sentence)
words.insert(0,'SOS')
words.append('EOS')
words = [bulid_one_hot(word,word_to_ix) for word in words]

for j in range(0,len(words),6):
if j+6 >= len(words):
break
train_words.append(words[j:j+5])
train_labels.append(words[j+5])

for i in range(int(0.9*Sentence_Num),Sentence_Num):
sentence = sentence_set[i]
words = jieba.lcut(sentence)
words.insert(0,'SOS')
words.append('EOS')
words = [bulid_one_hot(word,word_to_ix) for word in words]

for j in range(0,len(words),6):
if j+6 >= len(words):
break
test_words.append(words[j:j+5])
test_labels.append(words[j+5])

trans, target_trans = None, None # transforms.ToTensor(), transforms.ToTensor()

train_set = MyDataset(train_words, train_labels, trans, target_trans)
train_loader = DataLoader(dataset=train_set, batch_size=BATCH_SIZE)

test_set = MyDataset(test_words, test_labels, trans, target_trans)
test_loader = DataLoader(dataset=test_set, batch_size=BATCH_SIZE)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
參考資料:

torch.nn.Embedding
10分鐘快速入門PyTorch (7)
Pytorch數據讀取(Dataset, DataLoader, DataLoaderIter)
pytorch學習筆記(六):自定義Datasets
Pytorch中正確設計并加載數據集方法
4.搭建模型

采用 GRU 結構構建 Seq2Seq 模型,其中,loss function 為 nn.CrossEntropyLoss(), optimizer 為 optim.SGD()。
注意,pytorch 中采用 nn.CrossEntropyLoss(),對輸入與輸出有格式要求,請查閱本小節的參考資料。

代碼:

# Bulid Seq2Seq Model

class Encoder(nn.Module):
def __init__(self, input_size, hidden_size):
super(Encoder, self).__init__()
self.hidden_size = hidden_size
self.embedding = nn.Embedding(input_size, hidden_size) # 將one-hot向量embedding為詞向量
self.gru = nn.GRU(hidden_size, hidden_size) # GRU的hidden layer的size與詞向量的size一樣,并非必須

def forward(self, input, hidden):
embedded = self.embedding(input).view(1, 1, -1) # RNN的輸入格式為 (seq_len, batch, input_size)
output = embedded
output, hidden = self.gru(output, hidden)
return output, hidden

def initHidden(self):
return torch.zeros(1, 1, self.hidden_size) # 初始化Encoder的隱狀態


class Decoder(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(Decoder, self).__init__()
self.hidden_size = hidden_size
self.embedding = nn.Embedding(input_size, hidden_size)
self.gru = nn.GRU(hidden_size, hidden_size)
self.out = nn.Linear(hidden_size, output_size)

def forward(self, input, hidden):
output = self.embedding(input).view(1, 1, -1)
output = F.relu(output)
output, hidden = self.gru(output, hidden)
output = self.out(output[0])
return output, hidden

def initHidden(self):
return torch.zeros(1, 1, self.hidden_size)


class Seq2Seq(nn.Module):
def __init__(self, encoder, decoder):
super(Seq2Seq, self).__init__()
self.encoder = encoder
self.decoder = decoder

def forward(self, inputs):
encoder_hidden = self.encoder.initHidden()
if torch.cuda.is_available():
encoder_hidden = encoder_hidden.cuda()

# encode
for word in inputs:
encoder_out, encoder_hidden = self.encoder(word, encoder_hidden)

# decode
decoder_hidden = encoder_hidden
pred, decoder_hidden = self.decoder(inputs[-1], decoder_hidden)

return pred


encoder = Encoder(Embedding_Dim,1000)
decoder = Decoder(Embedding_Dim,1000,Embedding_Dim)

if torch.cuda.is_available():
encoder = encoder.cuda()
decoder = decoder.cuda()

seq2seq = Seq2Seq(encoder,decoder)

if torch.cuda.is_available():
seq2seq = seq2seq.cuda()

# Bulid loss function and optimizer

loss_func = nn.CrossEntropyLoss()
#encoder_optimizer = optim.SGD(encoder.parameters(), lr=LR, momentum=0.9)
#decoder_optimizer = optim.SGD(decoder.parameters(), lr=LR, momentum=0.9)
seq2seq_optimizer = optim.SGD(seq2seq.parameters(), lr=LR, momentum=0.9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
參考資料:

深度解析Pytorch Seq2Seq Toturials(2)
spro/practical-pytorch
搞懂Transformer結構,看這篇PyTorch實現就夠了!
PyTorch(總)——PyTorch遇到令人迷人的BUG與記錄
RuntimeError: multi-target not supported (newbie)
pytorch error: multi-target not supported in CrossEntropyLoss()
5.訓練模型

代碼:

# Train Seq2Seq Model

for epoch in range(EPOCH):
loss_sum = 0
for step, (inputs, labels) in enumerate(train_loader):
# encoder_hidden = encoder.initHidden()
label = torch.LongTensor((1,))
label[0] = int(labels.data.numpy()[0])

if torch.cuda.is_available():
inputs = [word.cuda() for word in inputs]
label = label.cuda()
# encoder_hidden = encoder_hidden.cuda()

# forward
pred = seq2seq(inputs)
loss = loss_func(pred,label)

# backward
seq2seq_optimizer.zero_grad()
loss.backward()
seq2seq_optimizer.step()

'''
for word in inputs:
encoder_out, encoder_hidden = encoder(word, encoder_hidden)
decoder_hidden = encoder_hidden
decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)

loss = loss_func(decoder_out,label)

#backward
encoder_optimizer.zero_grad()
decoder_optimizer.zero_grad()
loss.backward()
encoder_optimizer.step()
decoder_optimizer.step()
'''

loss_sum+= loss.data[0]
print('Epoch: %2d train loss: %.4f' % (epoch, loss_sum))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
結果:


6.測試模型

代碼:

# Test Seq2Seq Model

for step, (inputs, labels) in enumerate(test_loader):
# encoder_hidden = encoder.initHidden()
label = torch.LongTensor((1,))
label[0] = int(labels.data.numpy()[0])

if torch.cuda.is_available():
inputs = [word.cuda() for word in inputs]
label = label.cuda()
# encoder_hidden = encoder_hidden.cuda()

decoder_output = seq2seq(inputs)

'''
# forward
for word in inputs:
encoder_out, encoder_hidden = encoder(word, encoder_hidden)
decoder_hidden = encoder_hidden
decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)
'''

# output
ans = ''
pred = ''

for word in inputs:
ix = word.cpu().data.numpy()[0][0]
ans+=ix_to_word[ix]
pred+=ix_to_word[ix]

ans+=ix_to_word[int(labels.data.numpy()[0])]
pred+=ix_to_word[np.argmax(decoder_output.cpu().data.numpy())]

print('Answer: %s' % ans)
print('Prediction: %s' % pred)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
結果:

?

?


上述結果是全部結果中效果不錯的,可以觀察到:雖然模型無法完全預測后續字詞,但是能依照句子的前部分繼續生成意思完整的句子。
不過,整體來看模型效果較差,我認為有以下幾個原因:

所用文本數量少,僅用了100個句子進行訓練。
構造的詞庫小,詞典中僅有3000+字詞,其中包括許多無意義的字詞。
未對超參數進行微調。
7.保存/加載模型

往往大家保存和加載模型都是用的最簡單的方法:torch.save(model,path),torch.load(path)。
這樣的方法不僅將模型的參數保存了下來,還將模型的結構保存了下來。

有時我們只需要保存模型的參數,我們可以采用這樣的方法:torch.save(model.state_dict(),path),torch.load_state_dict(torch.load(path))。

當然,還有許多復雜的方法可以選擇,大家可以查閱參考資料進一步了解。

代碼:

# Save Seq2Seq Model


'''
torch.save(encoder.state_dict(),'../../Model/Seq2Seq/encoder_params.pkl')
torch.save(decoder.state_dict(),'../../Model/Seq2Seq/decoder_params.pkl')

torch.save(encoder,'../../Model/Seq2Seq/encoder.pkl')
torch.save(decoder,'../../Model/Seq2Seq/decoder.pkl')
'''

torch.save(seq2seq.state_dict(),'../../Model/Seq2Seq/seq2seq_params.pkl')
torch.save(seq2seq,'../../Model/Seq2Seq/seq2seq.pkl')

# Load Seq2Seq Model

# encoder.load_state_dict(torch.load('../../Model/Seq2Seq/encoder_params.pkl'))
# decoder.load_state_dict(torch.load('../../Model/Seq2Seq/decoder_params.pkl'))
seq2seq = torch.load('../../Model/Seq2Seq/seq2seq.pkl')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
參考資料:

Pytorch 保存模型與加載模型
如果你看到了這篇文章的最后,并且覺得有幫助的話,麻煩你花幾秒鐘時間點個贊,或者受累在評論中指出我的錯誤。謝謝!

作者信息:
知乎:沒頭腦
LeetCode:Tao Pu
CSDN:Code_Mart
Github:Bojack-want-drink
---------------------
作者:Code_Mart
來源:CSDN
原文:https://blog.csdn.net/Code_Mart/article/details/86978470
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

總結

以上是生活随笔為你收集整理的[实现] 利用 Seq2Seq 预测句子后续字词 (Pytorch)2的全部內容,希望文章能夠幫你解決所遇到的問題。

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