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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Pytorch实战_Seq2seq模型

發布時間:2024/1/1 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Pytorch实战_Seq2seq模型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. Sequence-to-Sequence 簡介

大多數常見的 sequence-to-sequence (seq2seq) model 為 encoder-decoder model,主要由兩個部分組成,分別是 EncoderDecoder,而這兩個部分大多數是由 recurrent neural network (RNN) 實現。

Encoder 是將一連串的輸入,如文字、影片、聲音訊號等,編碼為單個向量,這個向量可以想像為整個輸入的抽象表示,包含了整個輸入的資訊。
Decoder 是將 Encoder 輸出的向量進行逐步解碼,一次輸出一個結果,直到將最終的目標全部輸出為止,每次輸出會影響下一個輸出,一般會在開始輸入 < BOS > 來表示開始解碼,會在結尾出輸出 < EOS > 來表示解碼結束。

2. 任務介紹

  • 英文翻譯為中文
    • 輸入: 一句英文 (e.g. tom is a student .)
    • 輸出: 中文翻譯 (e.g. 湯姆 是 個 學生 。)

3. 實現過程

首先要做的是下載資料,主要是用來下載本次任務需要的數據集

!gdown --id '1r4px0i-NcrnXy1-tkBsIwvYwbWnxAhcg' --output data.tar.gz !tar -zxvf data.tar.gz !mkdir ckpt !ls

之后導入需要用到的包(如果nltk包沒有下載的話,可使用第一段代碼進行下載)

!pip3 install --user nltk import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F import torch.utils.data as data import torch.utils.data.sampler as sampler import torchvision from torchvision import datasets, transformsimport numpy as np import sys import os import random import jsondevice = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 判斷是用 CPU 還是 GPU 執行運算

需要注意的是,不同的句子往往有著不同的長度,這無疑給訓練帶來了不小的麻煩(因為 RNN 的輸入維度要進行相應的改變)。為了解決這個麻煩,我們使用 <pad> 長度較短的句子進行填充。因此這里定義一個長度轉換的類

import numpy as npclass LabelTransform(object):def __init__(self, size, pad):self.size = sizeself.pad = paddef __call__(self, label):label = np.pad(label, (0, (self.size - label.shape[0])), mode='constant', constant_values=self.pad)return label

下一步就是數據的準備了,我們定義一個Dataset。

  • Data (出自manythings 的 cmn-eng):

    • 訓練資料:18000句
    • 驗證資料: 500句
    • 測試資料: 2636句
  • 資料預處理:

    • 英文:
      • 用 subword-nmt 套件將word轉為subword
      • 建立字典:取出標簽中出現頻率高于預定閾值的subword
    • 中文:
      • 用 jieba 將中文句子進行斷句
      • 建立字典:取出標簽中出現頻率高于預定閾值的詞
    • 特殊字元: < PAD >, < BOS >, < EOS >, < UNK >
      • < PAD > :無意義,將句子拓展到相同長度
      • < BOS > :Begin of sentence, 開始字元
      • < EOS > :End of sentence, 結尾字元
      • < UNK > :單字沒有出現在字典里的字
    • 將字典里出現的 subword (詞) 用一個整數表示,分為英文和中文的字典,方便之后轉化為 one-hot vector
import re import jsonclass EN2CNDataset(data.Dataset):def __init__(self, root, max_output_len, set_name):self.root = rootself.word2int_cn, self.int2word_cn = self.get_dictionary('cn')self.word2int_en, self.int2word_en = self.get_dictionary('en')# 載入資料self.data = []with open(os.path.join(self.root, f'{set_name}.txt'), "r") as f:for line in f:self.data.append(line)print (f'{set_name} dataset size: {len(self.data)}')self.cn_vocab_size = len(self.word2int_cn)self.en_vocab_size = len(self.word2int_en)self.transform = LabelTransform(max_output_len, self.word2int_en['<PAD>'])def get_dictionary(self, language):# 載入字典with open(os.path.join(self.root, f'word2int_{language}.json'), "r") as f:word2int = json.load(f)with open(os.path.join(self.root, f'int2word_{language}.json'), "r") as f:int2word = json.load(f)return word2int, int2worddef __len__(self):return len(self.data)def __getitem__(self, Index):# 先將中英文詞分開sentences = self.data[Index]sentences = re.split('[\t\n]', sentences)sentences = list(filter(None, sentences))#print (sentences)assert len(sentences) == 2# 特殊字元BOS = self.word2int_en['<BOS>']EOS = self.word2int_en['<EOS>']UNK = self.word2int_en['<UNK>']# 在開頭添加 <BOS>,在結尾添加 <EOS> ,不在字典的 subword (詞) 用 <UNK> 取代en, cn = [BOS], [BOS]# 將句子拆解為 subword 并轉為整數sentence = re.split(' ', sentences[0])sentence = list(filter(None, sentence))#print (f'en: {sentence}')for word in sentence:en.append(self.word2int_en.get(word, UNK))en.append(EOS)# 將句子拆解為 subword 并轉為整數# e.g. < BOS >, we, are, friends, < EOS > --> 1, 28, 29, 205, 2sentence = re.split(' ', sentences[1])sentence = list(filter(None, sentence))#print (f'cn: {sentence}')for word in sentence:cn.append(self.word2int_cn.get(word, UNK))cn.append(EOS)en, cn = np.asarray(en), np.asarray(cn)# 用 <PAD> 將將句子拓展到相同長度en, cn = self.transform(en), self.transform(cn)en, cn = torch.LongTensor(en), torch.LongTensor(cn)return en, cn

接下來就是構建自己的模型

Encoder

  • seq2seq模型的編碼器為RNN。對于每個輸入,Encoder 會輸出一個向量一個隱藏層狀態(hidden state),并將隱藏層狀態用于下一個輸入,換句話說,Encoder 會逐步讀入輸入序列。
  • 參數:
    • en_vocab_size 是英文字典的大小,也就是英文的 subword 的個數
    • emb_dim 是 embedding 的維度,主要將 one-hot vector 的單詞向量壓縮到指定的維度,可以使用預先訓練好的 word embedding,如 Glove 和 word2vector
    • hid_dim 是 RNN 輸出和隱藏狀態的維度
    • n_layers 是 RNN 要疊多少層
    • dropout 是決定有多少的機率將某某個節點變為 0,主要是為了防止 overfitting ,一般來說是在訓練集使用,測試集不使用
  • Encoder 的輸入和輸出:
    • 輸入:
      • 英文的整數序列 e.g. 1, 28, 29, 205, 2
    • 輸出:
      • outputs: 最上層 RNN 全部的輸出,可以用 Attention 再進行處理
      • hidden: 每層最后的隱藏狀態,將傳輸到后面的 Decoder 進行解碼
class Encoder(nn.Module):def __init__(self, en_vocab_size, emb_dim, hid_dim, n_layers, dropout):super().__init__()self.embedding = nn.Embedding(en_vocab_size, emb_dim)self.hid_dim = hid_dimself.n_layers = n_layersself.rnn = nn.GRU(emb_dim, hid_dim, n_layers, dropout=dropout, batch_first=True, bidirectional=True)self.dropout = nn.Dropout(dropout)def forward(self, input):# input = [batch size, sequence len, vocab size]embedding = self.embedding(input)outputs, hidden = self.rnn(self.dropout(embedding))# outputs = [batch size, sequence len, hid dim * directions]# hidden = [num_layers * directions, batch size , hid dim]# outputs 是最上層RNN的輸出return outputs, hidden

Decoder

  • Decoder 是另一個 RNN,在最簡單的 seq2seq decoder 中,僅使用 Encoder 對每一層最后的隱藏狀態來進行解碼,而這最好的的隱藏狀態有些被稱為 “content vector”,因為可以想象它對整個前文序列進行了編碼, 此 “content vector” 用作 Decoder初始隱藏狀態, 而 Encoder 的輸出通常用于 Attention Mechanism 產生相應的 Attention。

  • 參數

    • en_vocab_size 是英文字典的大小,也就是英文的 subword 的個數
    • emb_dim 是 embedding 的維度,主要將 one-hot vector 的單詞向量壓縮到指定的維度,可以使用預先訓練好的 word embedding,如 Glove 和 word2vector
    • hid_dim 是 RNN 輸出和隱藏狀態的維度
    • output_dim 是最終輸出的維度,一般來說是將 hid_dim 轉到 one-hot vector 的單詞向量
    • n_layers 是 RNN 要疊多少層
    • dropout 是決定有多少的機率將某某個節點變為 0,主要是為了防止 overfitting ,一般來說是在訓練集使用,測試集不使用
    • isatt 是來決定是否使用 Attention Mechanism
  • Decoder 的輸入和輸出:

    • 輸入:
      • 前一次解碼出來的單詞的整數表示
    • 輸出:
      • hidden: 根據輸入和前一次的隱藏轉態,現在的隱藏轉態的更新的結果
      • output: 每個字有多少概率是這次解碼的結果
class Decoder(nn.Module):def __init__(self, cn_vocab_size, emb_dim, hid_dim, n_layers, dropout, isatt):super().__init__()self.cn_vocab_size = cn_vocab_sizeself.hid_dim = hid_dim * 2self.n_layers = n_layersself.embedding = nn.Embedding(cn_vocab_size, config.emb_dim)self.isatt = isattself.attention = Attention(hid_dim)# 如果使用 Attention Mechanism 會使得輸入維度變化,請在這裡修改# e.g. Attention 接在輸入後面會使得維度變化,所以輸入維度改為# self.input_dim = emb_dim + hid_dim * 2 if isatt else emb_dimself.input_dim = emb_dimself.rnn = nn.GRU(self.input_dim, self.hid_dim, self.n_layers, dropout = dropout, batch_first=True)self.embedding2vocab1 = nn.Linear(self.hid_dim, self.hid_dim * 2)self.embedding2vocab2 = nn.Linear(self.hid_dim * 2, self.hid_dim * 4)self.embedding2vocab3 = nn.Linear(self.hid_dim * 4, self.cn_vocab_size)self.dropout = nn.Dropout(dropout)def forward(self, input, hidden, encoder_outputs):# input = [batch size, vocab size]# hidden = [batch size, n layers * directions, hid dim]# Decoder 只會是單向,所以 directions=1input = input.unsqueeze(1)embedded = self.dropout(self.embedding(input))# embedded = [batch size, 1, emb dim]if self.isatt:attn = self.attention(encoder_outputs, hidden)# TODO: 在這裡決定如何使用 Attention,e.g. 相加 或是 接在後面, 請注意維度變化output, hidden = self.rnn(embedded, hidden)# output = [batch size, 1, hid dim]# hidden = [num_layers, batch size, hid dim]# 將 RNN 的輸出轉為每個詞出現的機率output = self.embedding2vocab1(output.squeeze(1))output = self.embedding2vocab2(output)prediction = self.embedding2vocab3(output)# prediction = [batch size, vocab size]return prediction, hidden

Attention

  • 當輸入過長時,或是單獨靠 “content vector” 無法獲取整個輸入的意思時,用 Attention Mechanism 來提供 Decoder 更多的資訊

  • 主要是根據現在 Decoder hidden state ,去計算在 Encoder outputs 中,那些與其有較高的關系,根據關系的數值來決定傳給 Decoder 哪些額外的資訊

  • 常見 Attention 的操作是用 Neural Network / Dot Product 來計算 Decoder hidden stateEncoder outputs 之間的關系,再對所有算出來的數值做 softmax ,最后根據過完 softmax 的值對 Encoder outputsweight sum

  • 李宏毅老師的課程在此處并沒有給出具體的代碼,需要大家自己補充。大家可以參考這篇文章 Seq2Seq (Attention) 的 PyTorch 實現 或者B站的視頻 PyTorch35——基于注意力機制的Seq2Seq的PyTorch實現示例。

class Attention(nn.Module):def __init__(self, hid_dim):super(Attention, self).__init__()self.hid_dim = hid_dimdef forward(self, encoder_outputs, decoder_hidden):# encoder_outputs = [batch size, sequence len, hid dim * directions]# decoder_hidden = [num_layers, batch size, hid dim]# 一般來說是取最後一層的 hidden state 來做 attention######### TODO #########attention=Nonereturn attention

Seq2seq模型

  • EncoderDecoder 組成
  • 接收輸入并傳給 Encoder
  • Encoder 的輸出傳給 Decoder
  • 不斷地將 Decoder 的輸出傳回 Decoder ,進行解碼
  • 當解碼完成,將 Decoder 的輸出傳回
class Seq2Seq(nn.Module):def __init__(self, encoder, decoder, device):super().__init__()self.encoder = encoderself.decoder = decoderself.device = deviceassert encoder.n_layers == decoder.n_layers, \"Encoder and decoder must have equal number of layers!"def forward(self, input, target, teacher_forcing_ratio):# input = [batch size, input len, vocab size]# target = [batch size, target len, vocab size]# teacher_forcing_ratio 是有多少機率使用正確答案來訓練batch_size = target.shape[0]target_len = target.shape[1]vocab_size = self.decoder.cn_vocab_size# 準備一個儲存空間來儲存輸出outputs = torch.zeros(batch_size, target_len, vocab_size).to(self.device)# 將輸入放入 Encoderencoder_outputs, hidden = self.encoder(input)# Encoder 最後的隱藏層(hidden state) 用來初始化 Decoder# encoder_outputs 主要是使用在 Attention# 因為 Encoder 是雙向的RNN,所以需要將同一層兩個方向的 hidden state 接在一起# hidden = [num_layers * directions, batch size , hid dim] --> [num_layers, directions, batch size , hid dim]hidden = hidden.view(self.encoder.n_layers, 2, batch_size, -1)hidden = torch.cat((hidden[:, -2, :, :], hidden[:, -1, :, :]), dim=2)# 取的 <BOS> tokeninput = target[:, 0]preds = []for t in range(1, target_len):output, hidden = self.decoder(input, hidden, encoder_outputs)outputs[:, t] = output# 決定是否用正確答案來做訓練teacher_force = random.random() <= teacher_forcing_ratio# 取出機率最大的單詞top1 = output.argmax(1)# 如果是 teacher force 則用正解訓練,反之用自己預測的單詞做預測input = target[:, t] if teacher_force and t < target_len else top1preds.append(top1.unsqueeze(1))preds = torch.cat(preds, 1)return outputs, predsdef inference(self, input, target):######### TODO ########## 在這裡實施 Beam Search# 此函式的 batch size = 1 # input = [batch size, input len, vocab size]# target = [batch size, target len, vocab size]batch_size = input.shape[0]input_len = input.shape[1] # 取得最大字數vocab_size = self.decoder.cn_vocab_size# 準備一個儲存空間來儲存輸出outputs = torch.zeros(batch_size, input_len, vocab_size).to(self.device)# 將輸入放入 Encoderencoder_outputs, hidden = self.encoder(input)# Encoder 最後的隱藏層(hidden state) 用來初始化 Decoder# encoder_outputs 主要是使用在 Attention# 因為 Encoder 是雙向的RNN,所以需要將同一層兩個方向的 hidden state 接在一起# hidden = [num_layers * directions, batch size , hid dim] --> [num_layers, directions, batch size , hid dim]hidden = hidden.view(self.encoder.n_layers, 2, batch_size, -1)hidden = torch.cat((hidden[:, -2, :, :], hidden[:, -1, :, :]), dim=2)# 取的 <BOS> tokeninput = target[:, 0]preds = []for t in range(1, input_len):output, hidden = self.decoder(input, hidden, encoder_outputs)# 將預測結果存起來outputs[:, t] = output# 取出機率最大的單詞top1 = output.argmax(1)input = top1preds.append(top1.unsqueeze(1))preds = torch.cat(preds, 1)return outputs, preds

總結

以上是生活随笔為你收集整理的Pytorch实战_Seq2seq模型的全部內容,希望文章能夠幫你解決所遇到的問題。

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