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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

lstm需要优化的参数_使用PyTorch手写代码从头构建LSTM,更深入的理解其工作原理...

發(fā)布時(shí)間:2023/12/9 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lstm需要优化的参数_使用PyTorch手写代码从头构建LSTM,更深入的理解其工作原理... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這是一個(gè)造輪子的過(guò)程,但是從頭構(gòu)建LSTM能夠使我們對(duì)體系結(jié)構(gòu)進(jìn)行更加了解,并將我們的研究帶入下一個(gè)層次。

LSTM單元是遞歸神經(jīng)網(wǎng)絡(luò)深度學(xué)習(xí)研究領(lǐng)域中最有趣的結(jié)構(gòu)之一:它不僅使模型能夠從長(zhǎng)序列中學(xué)習(xí),而且還為長(zhǎng)、短期記憶創(chuàng)建了一個(gè)數(shù)值抽象,可以在需要時(shí)相互替換。

在這篇文章中,我們不僅將介紹LSTM單元的體系結(jié)構(gòu),還將通過(guò)PyTorch手工實(shí)現(xiàn)它。

最后但最不重要的是,我們將展示如何對(duì)我們的實(shí)現(xiàn)做一些小的調(diào)整,以實(shí)現(xiàn)一些新的想法,這些想法確實(shí)出現(xiàn)在LSTM研究領(lǐng)域,如peephole。

LSTM體系結(jié)構(gòu)

LSTM被稱為門(mén)結(jié)構(gòu):一些數(shù)學(xué)運(yùn)算的組合,這些運(yùn)算使信息流動(dòng)或從計(jì)算圖的那里保留下來(lái)。因此,它能夠“決定”其長(zhǎng)期和短期記憶,并輸出對(duì)序列數(shù)據(jù)的可靠預(yù)測(cè):

LSTM單元中的預(yù)測(cè)序列。注意,它不僅會(huì)傳遞預(yù)測(cè)值,而且還會(huì)傳遞一個(gè)c,c是長(zhǎng)期記憶的代表

遺忘門(mén)

遺忘門(mén)(forget gate)是輸入信息與候選者一起操作的門(mén),作為長(zhǎng)期記憶。請(qǐng)注意,在輸入、隱藏狀態(tài)和偏差的第一個(gè)線性組合上,應(yīng)用一個(gè)sigmoid函數(shù):

sigmoid將遺忘門(mén)的輸出“縮放”到0-1之間,然后,通過(guò)將其與候選者相乘,我們可以將其設(shè)置為0,表示長(zhǎng)期記憶中的“遺忘”,或者將其設(shè)置為更大的數(shù)字,表示我們從長(zhǎng)期記憶中記住的“多少”。

新型長(zhǎng)時(shí)記憶的輸入門(mén)及其解決方案

輸入門(mén)是將包含在輸入和隱藏狀態(tài)中的信息組合起來(lái),然后與候選和部分候選c''u t一起操作的地方:

在這些操作中,決定了多少新信息將被引入到內(nèi)存中,如何改變——這就是為什么我們使用tanh函數(shù)(從-1到1)。我們將短期記憶和長(zhǎng)期記憶中的部分候選組合起來(lái),并將其設(shè)置為候選。

單元的輸出門(mén)和隱藏狀態(tài)(輸出)

之后,我們可以收集ot作為L(zhǎng)STM單元的輸出門(mén),然后將其乘以候選單元(長(zhǎng)期存儲(chǔ)器)的tanh,后者已經(jīng)用正確的操作進(jìn)行了更新。網(wǎng)絡(luò)輸出為ht。

LSTM單元方程

在PyTorch上實(shí)現(xiàn)

import math
import torch
import torch.nn as nn

我們現(xiàn)在將通過(guò)繼承nn.Module,然后還將引用其參數(shù)和權(quán)重初始化,如下所示(請(qǐng)注意,其形狀由網(wǎng)絡(luò)的輸入大小和輸出大小決定):

class NaiveCustomLSTM(nn.Module):
def __init__(self, input_sz: int, hidden_sz: int):
super().__init__()
self.input_size = input_sz
self.hidden_size = hidden_sz
#i_t
self.U_i = nn.Parameter(torch.Tensor(input_sz, hidden_sz))
self.V_i = nn.Parameter(torch.Tensor(hidden_sz, hidden_sz))
self.b_i = nn.Parameter(torch.Tensor(hidden_sz))
#f_t
self.U_f = nn.Parameter(torch.Tensor(input_sz, hidden_sz))
self.V_f = nn.Parameter(torch.Tensor(hidden_sz, hidden_sz))
self.b_f = nn.Parameter(torch.Tensor(hidden_sz))
#c_t
self.U_c = nn.Parameter(torch.Tensor(input_sz, hidden_sz))
self.V_c = nn.Parameter(torch.Tensor(hidden_sz, hidden_sz))
self.b_c = nn.Parameter(torch.Tensor(hidden_sz))
#o_t
self.U_o = nn.Parameter(torch.Tensor(input_sz, hidden_sz))
self.V_o = nn.Parameter(torch.Tensor(hidden_sz, hidden_sz))
self.b_o = nn.Parameter(torch.Tensor(hidden_sz))
self.init_weights()

要了解每個(gè)操作的形狀,請(qǐng)看:

矩陣的輸入形狀是(批量大小、序列長(zhǎng)度、特征長(zhǎng)度),因此將序列的每個(gè)元素相乘的權(quán)重矩陣必須具有該形狀(特征長(zhǎng)度、輸出長(zhǎng)度)。

序列上每個(gè)元素的隱藏狀態(tài)(也稱為輸出)都具有形狀(批大小、輸出大小),這將在序列處理結(jié)束時(shí)產(chǎn)生輸出形狀(批大小、序列長(zhǎng)度、輸出大小)。-因此,將其相乘的權(quán)重矩陣必須具有與單元格的參數(shù)hiddensz相對(duì)應(yīng)的形狀(outputsize,output_size)。

這里是權(quán)重初始化,我們將其用作PyTorch默認(rèn)值中的權(quán)重初始化nn.Module:

def init_weights(self):
stdv = 1.0 / math.sqrt(self.hidden_size)
for weight in self.parameters():
weight.data.uniform_(-stdv, stdv)

前饋操作

前饋操作接收initstates參數(shù),該參數(shù)是上面方程的(ht,ct)參數(shù)的元組,如果不引入,則設(shè)置為零。然后,我們對(duì)每個(gè)保留(ht,c_t)的序列元素執(zhí)行LSTM方程的前饋,并將其作為序列下一個(gè)元素的狀態(tài)引入。

最后,我們返回預(yù)測(cè)和最后一個(gè)狀態(tài)元組。讓我們看看它是如何發(fā)生的:

def forward(self,x,init_states=None):
"""
assumes x.shape represents (batch_size, sequence_size, input_size)
"""
bs, seq_sz, _ = x.size()
hidden_seq = []
if init_states is None:
h_t, c_t = (
torch.zeros(bs, self.hidden_size).to(x.device),
torch.zeros(bs, self.hidden_size).to(x.device),
)
else:
h_t, c_t = init_states
for t in range(seq_sz):
x_t = x[:, t, :]
i_t = torch.sigmoid(x_t @ self.U_i + h_t @ self.V_i + self.b_i)
f_t = torch.sigmoid(x_t @ self.U_f + h_t @ self.V_f + self.b_f)
g_t = torch.tanh(x_t @ self.U_c + h_t @ self.V_c + self.b_c)
o_t = torch.sigmoid(x_t @ self.U_o + h_t @ self.V_o + self.b_o)
c_t = f_t * c_t + i_t * g_t
h_t = o_t * torch.tanh(c_t)
hidden_seq.append(h_t.unsqueeze(0))
#reshape hidden_seq p/ retornar
hidden_seq = torch.cat(hidden_seq, dim=0)
hidden_seq = hidden_seq.transpose(0, 1).contiguous()
return hidden_seq, (h_t, c_t)

優(yōu)化版本

這個(gè)LSTM在運(yùn)算上是正確的,但在計(jì)算時(shí)間上沒(méi)有進(jìn)行優(yōu)化:我們分別執(zhí)行8個(gè)矩陣乘法,這比矢量化的方式慢得多。我們現(xiàn)在將演示如何通過(guò)將其減少到2個(gè)矩陣乘法來(lái)完成,這將使它更快。

為此,我們?cè)O(shè)置了兩個(gè)矩陣U和V,它們的權(quán)重包含在4個(gè)矩陣乘法上。然后,我們對(duì)已經(jīng)通過(guò)線性組合+偏置操作的矩陣執(zhí)行選通操作。

通過(guò)矢量化操作,LSTM單元的方程式為:

class CustomLSTM(nn.Module):
def __init__(self, input_sz, hidden_sz):
super().__init__()
self.input_sz = input_sz
self.hidden_size = hidden_sz
self.W = nn.Parameter(torch.Tensor(input_sz, hidden_sz * 4))
self.U = nn.Parameter(torch.Tensor(hidden_sz, hidden_sz * 4))
self.bias = nn.Parameter(torch.Tensor(hidden_sz * 4))
self.init_weights()
def init_weights(self):
stdv = 1.0 / math.sqrt(self.hidden_size)
for weight in self.parameters():
weight.data.uniform_(-stdv, stdv)
def forward(self, x,
init_states=None):
"""Assumes x is of shape (batch, sequence, feature)"""
bs, seq_sz, _ = x.size()
hidden_seq = []
if init_states is None:
h_t, c_t = (torch.zeros(bs, self.hidden_size).to(x.device),
torch.zeros(bs, self.hidden_size).to(x.device))
else:
h_t, c_t = init_states
HS = self.hidden_size
for t in range(seq_sz):
x_t = x[:, t, :]
# batch the computations into a single matrix multiplication
gates = x_t @ self.W + h_t @ self.U + self.bias
i_t, f_t, g_t, o_t = (
torch.sigmoid(gates[:, :HS]), # input
torch.sigmoid(gates[:, HS:HS*2]), # forget
torch.tanh(gates[:, HS*2:HS*3]),
torch.sigmoid(gates[:, HS*3:]), # output
)
c_t = f_t * c_t + i_t * g_t
h_t = o_t * torch.tanh(c_t)
hidden_seq.append(h_t.unsqueeze(0))
hidden_seq = torch.cat(hidden_seq, dim=0)
# reshape from shape (sequence, batch, feature) to (batch, sequence, feature)
hidden_seq = hidden_seq.transpose(0, 1).contiguous()
return hidden_seq, (h_t, c_t)

最后但并非最不重要的是,我們可以展示如何優(yōu)化,以使用LSTM peephole connections。

LSTM peephole

LSTM peephole對(duì)其前饋操作進(jìn)行了細(xì)微調(diào)整,從而將其更改為優(yōu)化的情況:

如果LSTM實(shí)現(xiàn)得很好并經(jīng)過(guò)優(yōu)化,我們可以添加peephole選項(xiàng),并對(duì)其進(jìn)行一些小的調(diào)整:

class CustomLSTM(nn.Module):
def __init__(self, input_sz, hidden_sz, peephole=False):
super().__init__()
self.input_sz = input_sz
self.hidden_size = hidden_sz
self.peephole = peephole
self.W = nn.Parameter(torch.Tensor(input_sz, hidden_sz * 4))
self.U = nn.Parameter(torch.Tensor(hidden_sz, hidden_sz * 4))
self.bias = nn.Parameter(torch.Tensor(hidden_sz * 4))
self.init_weights()
def init_weights(self):
stdv = 1.0 / math.sqrt(self.hidden_size)
for weight in self.parameters():
weight.data.uniform_(-stdv, stdv)
def forward(self, x,
init_states=None):
"""Assumes x is of shape (batch, sequence, feature)"""
bs, seq_sz, _ = x.size()
hidden_seq = []
if init_states is None:
h_t, c_t = (torch.zeros(bs, self.hidden_size).to(x.device),
torch.zeros(bs, self.hidden_size).to(x.device))
else:
h_t, c_t = init_states
HS = self.hidden_size
for t in range(seq_sz):
x_t = x[:, t, :]
# batch the computations into a single matrix multiplication
if self.peephole:
gates = x_t @ U + c_t @ V + bias
else:
gates = x_t @ U + h_t @ V + bias
g_t = torch.tanh(gates[:, HS*2:HS*3])
i_t, f_t, o_t = (
torch.sigmoid(gates[:, :HS]), # input
torch.sigmoid(gates[:, HS:HS*2]), # forget
torch.sigmoid(gates[:, HS*3:]), # output
)
if self.peephole:
c_t = f_t * c_t + i_t * torch.sigmoid(x_t @ U + bias)[:, HS*2:HS*3]
h_t = torch.tanh(o_t * c_t)
else:
c_t = f_t * c_t + i_t * g_t
h_t = o_t * torch.tanh(c_t)
hidden_seq.append(h_t.unsqueeze(0))
hidden_seq = torch.cat(hidden_seq, dim=0)
# reshape from shape (sequence, batch, feature) to (batch, sequence, feature)
hidden_seq = hidden_seq.transpose(0, 1).contiguous()
return hidden_seq, (h_t, c_t)

我們的LSTM就這樣結(jié)束了。如果有興趣大家可以將他與torch LSTM內(nèi)置層進(jìn)行比較。

代碼:https://github.com/piEsposito/pytorch-lstm-by-hand

作者:Piero Esposito

總結(jié)

以上是生活随笔為你收集整理的lstm需要优化的参数_使用PyTorch手写代码从头构建LSTM,更深入的理解其工作原理...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。