全连接网络权重数量过于庞大,针对序列信息,采用权重共享的方式减少权重数量

RNN cell--Linear

同一个RNN cell一直参与运算,那么RNN cell是如何构成的?

RNN网络的关键参数:seqLen序列长度,batchsize,inputsize

网络结构可视化如图,numlayers代表网络深度,每一层用一个RNNcell进行训练,每一个RNNcell包括一个Wih和一个Whh,维度分别为hiddensize*inputsize和hiddensize*hiddensize

第一层输出的hidden作为第二层的输入

下面是简单的RNN实现

import torch

batch_size=1
seq_len=3
input_size=4
hidden_size=2
num_layers=5
# cell=torch.nn.RNNCell(input_size=input_size,hidden_size=hidden_size)
#
# dataset=torch.randn(seq_len,batch_size,input_size)
# hidden=torch.zeros(batch_size,hidden_size)
#
# for idx,input in enumerate(dataset):
#     print('='*20,idx,'='*20)
#     print('input size',input.shape)#input形状是(batchsize,inputsize)
#
#     hidden=cell(input,hidden)
#
#     print('ouput size',hidden.shape)#hidden形状是(batchsize,hiddensize)
#     print(hidden)

##################################################################
inputs=torch.randn(seq_len,batch_size,input_size)
hidden=torch.zeros(num_layers,batch_size,hidden_size)
#或者使用torch.nn.RNN
cell=torch.nn.RNN(input_size=input_size,hidden_size=hidden_size,num_layers=num_layers)
out, hidden=cell(inputs,hidden)#--》inputs=x1...xN;hidden=h0
#-->out=h1...hN;hidden=hN
#输入要求input(seqLen,batch,input_size)隐层(numLayers,batch,hidden_size)
#输出output(seqLen,batch,hidden_size) hidden(numLayers,batch,hidden_size)
print('output size',out.shape)
print('output:',out)
print('hidden size',hidden.shape)
print('hidden:',hidden)

下面希望实现一个seq到seq的转换任务,从hello转换成ohlol

根据输入构造词典-->添加索引-->用索引替代输入-->变成独热向量(索引位置是1,其他位置均为0)

对于这个例子,inputsize就是4,而序列长度是5

输出希望知道输入的是哪个字母,因此输出是一个四维向量,接一个softmax,即多分类问题

import torch

batch_size = 1
seq_len = 5 # 序列长度是 "hello" 的长度,即5
input_size = 4  # 词典大小 one-hot vector size
hidden_size = 4

idx2char=['e','h','l','o']
x_data=[1,0,2,2,3]
y_data=[3,1,2,3,2]

one_hot_lookup=[[1,0,0,0],
                [0,1,0,0],
                [0,0,1,0],
                [0,0,0,1]]
x_one_hot=[one_hot_lookup[x] for x in x_data]

inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size)
labels=torch.LongTensor(y_data).view(-1)

class Model(torch.nn.Module):
    def __init__(self,input_size,hidden_size,batch_size):
        super(Model,self).__init__()
        self.batch_size=batch_size
        self.input_size=input_size
        self.hidden_size=hidden_size
        self.rnncell=torch.nn.RNNCell(input_size=self.input_size,hidden_size=self.hidden_size)

    def forward(self,input,hidden):
        hidden=self.rnncell(input,hidden)
        return hidden

    def init_hidden(self):
        return torch.zeros(self.batch_size,self.hidden_size)


net=Model(input_size,hidden_size,batch_size)

criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(),lr=0.01)

for epoch in range(200):
    loss=0
    optimizer.zero_grad()
    hidden=net.init_hidden()
    print('predicted string:',end='')
    for input,label in zip(inputs,labels):#inputs维度是(seqLen,batchsize,inputsize);labels维度是(seqLen,1)
        hidden=net(input,hidden)#input维度是(batchsize,inputsize);label维度是(1)
        loss+=criterion(hidden,label.unsqueeze(0))
        _,idx=hidden.max(dim=1)
        print(idx2char[idx.item()],end='')
    loss.backward()
    optimizer.step()
    print(',Epoch [%d/200] loss=%.4f' % (epoch+1,loss.item()))

以上是使用RNNCell的实现,还可以使用RNN函数,实现如下

import torch

batch_size = 1
seq_len = 5 # 序列长度是 "hello" 的长度,即5
input_size = 4  # 词典大小 one-hot vector size
hidden_size = 4

idx2char=['e','h','l','o']
x_data=[1,0,2,2,3]
y_data=[3,1,2,3,2]

one_hot_lookup=[[1,0,0,0],
                [0,1,0,0],
                [0,0,1,0],
                [0,0,0,1]]
x_one_hot=[one_hot_lookup[x] for x in x_data]

inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size)
labels=torch.LongTensor(y_data)
class Model(torch.nn.Module):
    def __init__(self,input_size,hidden_size,batch_size,num_layers=1):
        super(Model,self).__init__()
        self.num_layers=num_layers
        self.input_size=input_size
        self.hidden_size=hidden_size
        self.batch_size=batch_size
        self.rnn=torch.nn.RNN(input_size=self.input_size,hidden_size=self.hidden_size,num_layers=num_layers)

    def forward(self,input):
        hidden=torch.zeros(self.num_layers,self.batch_size,self.hidden_size)
        out,_=self.rnn(input,hidden)
        return out.view(-1,self.hidden_size)#展成两个维度,(seqLen*batchsize,hiddensize)

net=Model(input_size,hidden_size,batch_size,num_layers=1)
criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(),lr=0.1)
for epoch in range(50):
    optimizer.zero_grad()
    outputs=net(inputs)
    loss=criterion(outputs,labels)
    loss.backward()
    optimizer.step()

    _,idx=outputs.max(dim=1)
    idx=idx.data.numpy()
    print('predicted:',''.join([idx2char[x] for x in idx]),end='')
    print(',epoch [%d/50] loss=%.3f' % (epoch+1,loss.item()))

需要注意的是one hot编码维度太高且稀疏,不好用-->embedding,转换成低维稠密向量

根据pytorch官方文档中的LSTM模型讲解,用最笨的方式实现LSTM如下

import torch

class Model(torch.nn.Module):
    def __init__(self,input_size,hidden_size):
        super(Model,self).__init__()
        self.input_size=input_size
        self.hidden_size=hidden_size
        #四个xt
        self.Wii=torch.nn.Linear(input_size,hidden_size)
        self.Wif=torch.nn.Linear(input_size,hidden_size)
        self.Wig = torch.nn.Linear(input_size, hidden_size)
        self.Wio = torch.nn.Linear(input_size, hidden_size)
        #四个ht-1
        self.Whi = torch.nn.Linear(hidden_size,hidden_size)
        self.Whf = torch.nn.Linear(hidden_size, hidden_size)
        self.Whg = torch.nn.Linear(hidden_size, hidden_size)
        self.Who = torch.nn.Linear(hidden_size, hidden_size)

    def forward(self,x,h_prev,c_prev):
        i_t=torch.sigmoid(self.Wii(x)+self.Whi(h_prev))
        f_t=torch.sigmoid(self.Wif(x)+self.Whf(h_prev))
        g_t=torch.tanh(self.Wig(x)+self.Whg(h_prev))
        o_t=torch.sigmoid(self.Wio(x)+self.Who(h_prev))
        c_t=f_t*c_prev+i_t*g_t
        h_t=o_t*torch.tanh(c_t)
        return h_t,c_t

class MyLSTM(torch.nn.Module):
    def __init__(self,input_size,hidden_size):
        super(MyLSTM,self).__init__()
        self.input_size=input_size
        self.hidden_size=hidden_size
        self.cell=Model(input_size,hidden_size)

    def forward(self,x):
        batch_size,seq_len,_=x.shape

        h_t = torch.zeros(batch_size, self.hidden_size, device=x.device)
        c_t = torch.zeros(batch_size, self.hidden_size, device=x.device)

        outputs = []

        for i in range(seq_len):
            x_t = x[:,i , :]
            h_t,c_t=self.cell(x_t,h_t,c_t)

            outputs.append(h_t.unsqueeze(1))

        outputs_seq=torch.cat(outputs,dim=1)

        return outputs_seq,(h_t,c_t)

    # --- 测试代码 ---
# --- 测试代码 ---
if __name__ == '__main__':
    # 定义模型参数
    batch_size = 10
    seq_len = 5
    input_size = 8
    hidden_size = 16

    # 创建一个随机输入
    random_input = torch.randn(batch_size, seq_len, input_size)

    # 实例化模型
    my_lstm = MyLSTM(input_size, hidden_size)

    # 运行模型
    output_sequence, (last_h, last_c) = my_lstm(random_input)

    # 打印结果形状进行验证
    print("Input shape:", random_input.shape)
    print("Output sequence shape:", output_sequence.shape)
    print("Last hidden state shape:", last_h.shape)
    print("Last cell state shape:", last_c.shape)

    # 期望的输出形状
    print("\nExpected output sequence shape:", (batch_size, seq_len, hidden_size))
    print("Expected last state shape:", (batch_size, hidden_size))

也可以用LSTM函数实现

import torch

class LSTM(torch.nn.Module):
    def __init__(self,input_size,hidden_size):
        super(LSTM,self).__init__()
        self.lstm=torch.nn.LSTM(input_size=input_size,hidden_size=hidden_size)

    def forward(self,x):

        output,(h_n,c_n)=self.lstm(x)
        return output,(h_n,c_n)

if __name__ == '__main__':
    # 定义模型参数 (和之前完全一样)
    batch_size = 10
    seq_len = 5
    input_size = 8
    hidden_size = 16

    # 创建一个随机输入
    random_input = torch.randn(batch_size, seq_len, input_size)

    # 实例化模型
    official_lstm = LSTM(input_size, hidden_size)

    # 运行模型
    output_sequence, (last_h, last_c) = official_lstm(random_input)

    # 打印结果形状进行验证
    print("--- PyTorch Official nn.LSTM ---")
    print("Input shape:", random_input.shape)
    print("\n--- Outputs ---")
    print("Output sequence shape:", output_sequence.shape)
    print("Last hidden state shape:", last_h.shape)
    print("Last cell state shape:", last_c.shape)

    # 期望的输出形状
    print("\n--- Expected Shapes ---")
    print("Expected output sequence shape:", (batch_size, seq_len, hidden_size))
    # 注意: 官方 LSTM 返回的 h_n, c_n 第一个维度是 (num_layers * num_directions)
    # 在我们这个简单例子中,都是1,所以形状是 (1, batch_size, hidden_size)
    print("Expected last state shape:", (1, batch_size, hidden_size))

更多推荐