原来求平均首先里面有Pad噪音(空白)、无主次,padding就相当于0位置的密集向量,pad平均到文章当中就相当于噪声,但是如果只用一个样本的话可能会有异常,效果不好并且时间长。即便没有padding,一些重要的表达情感的词语没有主次,一旦求平均,会认为每个人的权重是等价的,就比如这个例子:
我让你————> 你让我
没有先后位置信息,从而出现了神经循环网络
比如说输入一个词不断生成生成,也可能输入很多词然后分类得到一个,或者翻译。
工作的时候靠循环将每个词放进模型,黑框代表中间状态:
时间步从左往右加一,在每个时间步,RNN都会接收一个输入并输出一个结果,RNN在每个时间步都会保留一个隐藏状态也叫中间状态,这个隐藏状态包含了之前所有时间步的信息,并用于计算当前时间步的输出和下一个时间步的隐藏状态。
公式:
St-1叫Xt-1和Xt之间的中间状态,W就是一个全连接的线性层,U相当于一个参数
最简单的RNN也叫SIMPLERNN,在torch里面就叫RNN,SimpleRNN没做softmax和V的处理,前面的中间状态直接输出给下一个。
模型定义:
class RNN(nn.Module):def __init__(self, embedding_dim=16, hidden_dim=64, vocab_size=vocab_size, num_layers=1, bidirectional=False):super().__init__()self.embeding = nn.Embedding(vocab_size, embedding_dim)self.rnn = nn.RNN(embedding_dim, hidden_dim, num_layers=num_layers, batch_first=True, bidirectional=bidirectional) #bidirectional是双向的self.layer = nn.Linear(hidden_dim * (2 if bidirectional else 1), hidden_dim)self.fc = nn.Linear(hidden_dim, 1)def forward(self, x):# [bs, seq length]x = self.embeding(x)# [bs, seq length, embedding_dim] -> shape [bs,seq length,hidden_dim]seq_output, final_hidden = self.rnn(x)# print(f'seq_output.shape={seq_output.shape}') # [bs, seq length, hidden_dim]# print(f'final_hidden.shape={final_hidden.shape}') #[1, bs, hidden_dim]x = seq_output[:, -1, :]# print(f'x.shape={x.shape}')#把final_hidden去除轴size为1的,和x进行比较,里边元素是否相等# assert torch.all(final_hidden.squeeze(0) == x)# 取最后一个时间步的输出 (这也是为什么要设置padding_first=True的原因)x = self.layer(x)x = self.fc(x)return xprint("{:=^80}".format(" 一层单向 RNN "))
for key, value in RNN().named_parameters():print(f"{key:^40}paramerters num: {np.prod(value.shape)}")