CNN常用于处理图像视频等数据,每个像素点之间存在位置相关性。而RNN常用于处理声音、文字等数据,这些数据与时间先后顺序有关,语音数据表现在每个时刻的波峰值,文字数据表现在每个先后出现的字符。
对字符的数据编码方式有one-hot编码,如果词数量过多,向量表示过于稀疏,表示信息少;
word2vec,较one-hot编码更为密集,能够体现语义相关性,但仅仅是查表,缺少上下文信息。
以一个语义情感分析任务为例,输入为用户对某产品的评价,输出该产品是好还是坏。
那么使用word embedding + Linear的结构有什么问题吗?
1. 现实的用户评论过长,编码的词数量较大,如果使用线性层,w与b的参数量极大。
2. 缺少词与词之间的上下文语义信息。
weight sharing可以解决模型参数量大的问题。consistent momery可用于保存语义信息,比如一句话中出现“喜欢”,可能是positive,但如果前面出现了‘not’,结果可能也是negative。
RNN解决了以上问题,使用weight sharing和consistent momery不断更新语义信息。每一层的RNN有两个任务,一是处理上一层的语义信息,二是处理当前层的输入。
下图的 向量是从x向量中提取的,x向量维度是【单词数,句数,每个单词embedding的维度】
RNN的使用
由于weight sharing,RNN的参数量少,仅仅是维护Wxh和Whh(忽略bias)参数矩阵。
RNN定义时需要三个参数,input_size,hidden_size和num_layers
rnn = nn.RNN(100, 10, 3)
# RNN第一个参数为一个batch里每个数据的特征维度,第二个参数是隐层的输出维度
# 第三个参数是堆叠的RNN层数
x是一次喂到位的数据,维度是【单词数,句数,每个单词embedding的维度】,h0是初始状态(可以不传),ht是T时间戳上(最终时刻)memory的状态,out是每个时间戳上最后一层的输出
以多层RNN为例,【y1,y2,..,】是out向量,最后的是上述ht向量。
LSTM结构
RNN可能会出现梯度消失问题(因为隐层求导次数多,由链式法则可知),由此推出了LSTM模型
RNN的short-term memory只能把与一个词相近的语义记录下来,而据该词较远的其他词常常被忽略。LSTM使用三个门控结构(forget, input, output),将计算梯度中的指数操作改为加法,解决了梯度消失问题,可以学习长期依赖信息。
上图是LSTM的结构,三个门的具体作用如下:
1. forget gate: 忘记之前memory 中与当前不相关的部分
2. input gate:从当前输入 中选择部分信息更新当前memory
3. output gate:将部分当前memory 输出
每道门由一个sigmoid函数和一个pointWise操作构成,sigmoid输出门的开度,将和映射到[0,1]上,与previous memory 相乘,此时值域拓展到 [0, ],起到信息过滤的作用。
下图直观的解释了LSTM的整体结构,有关三个门的具体公式详见LSTM 详解_qian99的博客-CSDN博客_lstm
LSTM的使用
LSTM结构与RNN类似,当input gate和forget gate的sigmoid取值为1时,和RNN结构一样(除激活函数外)。
与RNN不同的是,LSTM的隐层输入输出改为tuple,分别是指定时间戳上的输出 和cell memory
lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=4)
x = torch.randn(10, 3, 100)
# [seq(每句话词数,短填长切), batch_size(句数), input_size(word_embedding维度)]
out, h, c = lstm(x)
print(out.shape) # [seq, batch_size, output_size(hidden_size)]
print(h.shape, c.shape) # [num_layers, batch_size, hidden_size]