赞
踩
循环神经网络RNN是特指一类专用于处理序列数据的模型,目前主流的RNN单元有LSTM(Long Short-Term Memory)和GRU(Gated Recurrent Unit)两种。相比最原始的RNN单元,LSTM通过增加记忆单元来缓解长序列数据训练时所产生的梯度消失问题,而GRU则是一种基于LSTM进行改进以进一步提升训练速度的变体单元。下文依次介绍单层RNN结构、多层RNN结构、双向RNN结构,这三种网络结构都可以根据实际需求来任意选择RNN单元(LSTM或GRU)。
假设给定一个句子 S = { w t } t = 1 N S=\{w_t\}^N_{t=1} S={wt}t=1N ,其中句子的长度为 N N N个单词,句子中第 t t t个单词为 w t w_t wt。在RNN处理前,需要先将每个单词 w t w_t wt映射为一个向量 x t x_t xt进行表达,即得到 S = { x t } t = 1 N S=\{x_t\}^N_{t=1} S={xt}t=1N。在RNN的处理过程中,是依照从前往后的次序进行处理,即从第一个单词 x 1 x_1 x1到第 t t t个单词 x t x_t xt的次序进行运算。图1以LSTM作为基本单元为例(GRU单元同理),展示了RNN处理一个句子 S = { x t } t = 1 N S=\{x_t\}^N_{t=1} S={xt}t=1N的整体示意图。处理过程的公式描述如下:
f
t
=
σ
(
W
f
[
h
t
−
1
,
x
t
]
+
b
f
)
f_t=\sigma{(W_f[h_{t-1},x_t]+b_f)}
ft=σ(Wf[ht−1,xt]+bf)
i
t
=
σ
(
w
i
[
h
t
−
1
,
x
t
]
)
+
b
i
i_t=\sigma{(w_i[h_{t-1},x_t])+b_i}
it=σ(wi[ht−1,xt])+bi
C
~
=
t
a
n
h
(
W
c
[
h
t
−
1
,
x
t
]
+
b
c
)
\widetilde{C}=tanh(W_c[h_{t-1},x_t]+b_c)
C
=tanh(Wc[ht−1,xt]+bc)
C
t
=
f
t
∗
C
t
−
1
+
i
t
∗
C
~
C_t=f_t*C_{t-1}+i_t*\widetilde{C}
Ct=ft∗Ct−1+it∗C
o
t
=
σ
(
W
o
[
h
t
−
1
,
x
t
]
+
b
o
)
o_t=\sigma{(W_o[h_{t-1},x_t]+b_o)}
ot=σ(Wo[ht−1,xt]+bo)
h
t
=
o
t
∗
t
a
n
h
(
C
t
)
h_t=o_t*tanh(C_t)
ht=ot∗tanh(Ct)
上述公式描述了句子 S = { x t } t = 1 N S=\{x_t\}^N_{t=1} S={xt}t=1N中第 t t t个单词 x t x_t xt(第 t t t个时间步的输入)得到运算结果 h t h_t ht(第 t t t个时间步的输出)的完成过程。 [ ⋅ ] [\cdot] [⋅]表示拼接操作, σ \sigma σ表示sigmoid函数, ∗ * ∗表示哈达玛积(Hadamard Product)。
同样采用图1的结构,可以将LSTM单元替换为GRU单元,具体的GRU结构如图2所示。针对句子 S = { x t } t = 1 N S=\{x_t\}^N_{t=1} S={xt}t=1N中第 t t t个单词 x t x_t xt,使用GRU计算出第 t t t个时间步的输出 h t h_t ht的过程可以描述为:
z
t
=
σ
(
W
z
[
h
t
−
1
,
x
t
]
)
z_t=\sigma{(W_z[h_{t-1},x_t])}
zt=σ(Wz[ht−1,xt])
r
t
=
σ
(
w
r
[
h
t
−
1
,
x
t
]
)
r_t=\sigma{(w_r[h_{t-1},x_t])}
rt=σ(wr[ht−1,xt])
h
t
^
=
t
a
n
h
(
W
[
r
t
∗
h
t
−
1
,
x
t
]
)
\widehat{h_t}=tanh(W[r_t*h_{t-1},x_t])
ht
=tanh(W[rt∗ht−1,xt])
h
t
=
(
1
−
z
t
)
∗
h
t
−
1
+
z
t
∗
h
^
t
h_t=(1-z_t)*h_{t-1}+z_t*\widehat{h}_t
ht=(1−zt)∗ht−1+zt∗h
t
为了更全面细致地提取每个单词的信息,同样可以采取加深网络结构的方式堆叠多个RNN单元构建深层网络。多层RNN与单层RNN相似,同样是采取将一个句子 S = { x t } t = 1 N S=\{x_t\}^N_{t=1} S={xt}t=1N从前往后的次序进行处理。与单层RNN的区别在于,多层RNN的第 n n n层的第 t t t个时间步的输入 x t x_t xt就是第 n − 1 n-1 n−1层的第 t t t个时间步的输出 h t h_t ht。以LSTM为基本单元来搭建2层RNN为例,具体如图3所示(采用GRU为基本单元同理)。
图4展示了双向RNN结构(以LSTM为例)。给定一个句子 S = { w t } t = 1 N S=\{w_t\}^N_{t=1} S={wt}t=1N,其中句子的长度为 N N N个单词,句子中第 t t t个单词的向量为 w t w_t wt。双向RNN是一种RNN网络结构,其将读入的序列(这里是一个句子)从前往后和从后往前同时处理,最终将前向和后向的隐状态拼接作为最终的隐状态。
h
t
→
=
R
N
N
(
h
→
t
−
1
,
w
t
)
\overrightarrow{h_t}=RNN(\overrightarrow{h}_{t-1}, w_t)
ht
=RNN(h
t−1,wt)
h
t
←
=
R
N
N
(
h
←
t
+
1
,
w
t
)
\overleftarrow{h_t}=RNN(\overleftarrow{h}_{t+1},w_t)
ht
=RNN(h
t+1,wt)
h
t
=
[
h
t
→
,
h
t
←
]
h_t=[\overrightarrow{h_t},\overleftarrow{h_t}]
ht=[ht
,ht
]
其中 R N N ( ) RNN() RNN()可以是LSTM单元或者是GRU单元, [ ⋅ ] [ \cdot ] [⋅]表示拼接操作。
Attention机制的核心思想是赋予模型更关注与任务更相关的部分,而降低对任务次相关部分的关注程度。其原理可以看作为键值查询,通过用户给定的Query,来得到序列中与任务最相关位置的权值。
在文本分类任务中,基于RNN结构的模型都是将序列处理后的最后一个隐状态作为分类依据。由于RNN能捕获序列中的序列信息,因此最后一个隐状态包含了整个句子的信息,能够较好地应对文本分类任务。
但是,该句子为何被模型划分为该类别,我们不能直观地进行解释。为了提高分类结果的可解释性,这里采用Attention机制对句子序列中的每个位置都计算出一个注意力得分(权值),最终权值越高的位置表明对分类结果产生越重要的影响。
假设一个句子序列 S = { w t } t = 1 N S=\{w_t\}^N_{t=1} S={wt}t=1N进行RNN处理后,得到的序列为 H = { h t } t = 1 N H=\{h_t\}^N_{t=1} H={ht}t=1N,其中作为最后一个位置隐状态 h N h_N hN包含了整个序列的信息。这里 h N h_N hN将作为Query,对序列中的各个位置进行计算,得到各个位置与 h N h_N hN的关联度(与分类任务的关联度)。
q
u
e
r
y
=
W
q
h
N
+
b
q
query=W_qh_N+b_q
query=WqhN+bq
k
e
y
=
W
k
H
+
b
k
key=W_kH+b_k
key=WkH+bk
a
t
t
e
n
t
i
o
n
=
s
o
f
t
m
a
x
(
s
u
m
(
W
a
)
t
a
n
h
(
k
e
y
s
+
q
u
e
r
y
)
)
attention=softmax(sum(W_a)tanh(keys+query))
attention=softmax(sum(Wa)tanh(keys+query))
o
u
t
p
u
t
s
=
a
t
t
e
n
t
i
o
n
×
H
outputs=attention \times H
outputs=attention×H
其中 × \times ×表示 H H H按照attention得分加权求和,得到的 o u t p u t s outputs outputs继续送入全连接层进行分类结果。
在文本分类任务中,基于RNN结构的模型都是将序列处理后的最后一个隐状态 H N H_N HN作为分类依据。由于RNN能捕获序列中的序列信息,因此最后一个隐状态包含了整个句子的信息,能够较好地应对文本分类任务。
因此,基于RNN的文本分类通用结构为:
1. 下载地址:
【https://github.com/skdjfla/toutiao-text-classfication-dataset 】
2. 格式:
6552431613437805063_!_102_!_news_entertainment_!_谢娜为李浩菲澄清网络谣言,之后她的两个行为给自己加分_!_佟丽娅,网络谣言,快乐大本营,李浩菲,谢娜,观众们
每行为一条数据,以_!_分割的个字段,从前往后分别是 新闻ID,分类code(见下文),分类名称(见下文),新闻字符串(仅含标题),新闻关键词
分类code与名称:
100 民生 故事 news_story
101 文化 文化 news_culture
102 娱乐 娱乐 news_entertainment
103 体育 体育 news_sports
104 财经 财经 news_finance
106 房产 房产 news_house
107 汽车 汽车 news_car
108 教育 教育 news_edu
109 科技 科技 news_tech
110 军事 军事 news_military
112 旅游 旅游 news_travel
113 国际 国际 news_world
114 证券 股票 stock
115 农业 三农 news_agriculture
116 电竞 游戏 news_game
预训练词向量使用的是,基于ACL-2018模型在百度百科训练的词向量。
下载地址:【 https://github.com/Embedding/Chinese-Word-Vectors 】
该小节一共分别给出多层LSTM(Multi_LSTM), 双向RNN(Bi_RNN), 双向RNN+Attention(Bi_RNN_Attention)的代码定义。
''' Text => Multi_Layer_LSTM => Fully_Connected => Softmax ''' class Multi_LSTM: def __init__(self, rnn_output_dim, num_layers, embedded_size, dict_size, maxlen, label_num, learning_rate): self.droput_rate = 0.5 # print('model_Name:', 'Multi_LSTM') self.X = tf.placeholder(tf.int32, [None, maxlen], name='input_x') self.Y = tf.placeholder(tf.int64, [None]) self.encoder_embeddings = tf.Variable(tf.random_uniform([dict_size, embedded_size], -1, 1), trainable=False) encoder_embedded = tf.nn.embedding_lookup(self.encoder_embeddings, self.X) rnn_cells = [keras.layers.LSTMCell(units=rnn_output_dim) for _ in range(num_layers)] outputs =keras.layers.RNN(rnn_cells,return_sequences=True, return_state=False)(encoder_embedded) outputs = tf.nn.dropout(outputs, keep_prob = self.droput_rate) self.logits = keras.layers.Dense(label_num, use_bias=True)(outputs[:,-1]) # 取出每条文本最后一个单词的隐藏层输出 self.probability = tf.nn.softmax(self.logits, name='probability') # self.cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = self.logits, labels = self.Y)) self.cost = tf.nn.sparse_softmax_cross_entropy_with_logits( labels = self.Y, logits = self.logits) self.cost = tf.reduce_mean(self.cost) self.optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(self.cost) self.pre_y = tf.argmax(self.logits, 1, name='pre_y') correct_pred = tf.equal(self.pre_y, self.Y) self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
''' Text => Bidirectional GRU or LSTM => Fully_Connected => Softmax ''' class Bi_RNN: def __init__(self, rnn_output_dim, embedded_size, dict_size, maxlen, label_num, learning_rate, rnn_type): self.droput_rate = 0.5 # print('model_Name:', 'Bi_RNN') ''' Process the Reviews with Bi-RNN ''' def bi_rnn(rnn_type, inputs, rnn_output_dim): if rnn_type == 'gru': h = keras.layers.Bidirectional( keras.layers.GRU(rnn_output_dim,return_sequences=True,unroll=True), merge_mode='concat' )(inputs) elif rnn_type == 'lstm' : h = keras.layers.Bidirectional( keras.layers.LSTM(rnn_output_dim,return_sequences=True,unroll=True), merge_mode='concat' )(inputs) return h # shape= (None, u_n_words, 2*rnn_output_dim) or # shape(H_d) = (None, i_n_words, 2*rnn_output_dim) self.X = tf.placeholder(tf.int32, [None, maxlen], name='input_x') self.Y = tf.placeholder(tf.int64, [None]) self.encoder_embeddings = tf.Variable(tf.random_uniform([dict_size, embedded_size], -1, 1), trainable=False) encoder_embedded = tf.nn.embedding_lookup(self.encoder_embeddings, self.X) outputs = bi_rnn(rnn_type = rnn_type, inputs = encoder_embedded, rnn_output_dim = rnn_output_dim) outputs = tf.nn.dropout(outputs, keep_prob = self.droput_rate) self.logits = keras.layers.Dense(label_num, use_bias=True)(outputs[:,-1]) # 取出每条文本最后一个单词的隐藏层输出 self.probability = tf.nn.softmax(self.logits, name='probability') self.cost = tf.nn.sparse_softmax_cross_entropy_with_logits( labels = self.Y, logits = self.logits) self.cost = tf.reduce_mean(self.cost) self.optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(self.cost) self.pre_y = tf.argmax(self.logits, 1, name='pre_y') correct_pred = tf.equal(self.pre_y, self.Y) self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
''' Text => Bidirectional GRU or LSTM => Word_Attention => Fully_Connected => Softmax ''' class Bi_RNN_Attention: def __init__(self, rnn_output_dim, embedded_size, dict_size, maxlen, label_num, learning_rate, attention_size, rnn_type): self.droput_rate = 0.5 # print('model_Name:', 'Bi_RNN_Attention') ''' Process the Reviews with Bi-RNN ''' def bi_rnn(rnn_type, inputs, rnn_output_dim): if rnn_type == 'gru': h = keras.layers.Bidirectional( keras.layers.GRU(rnn_output_dim,return_sequences=True,unroll=True), merge_mode='concat' )(inputs) elif rnn_type == 'lstm' : h = keras.layers.Bidirectional( keras.layers.LSTM(rnn_output_dim,return_sequences=True,unroll=True), merge_mode='concat' )(inputs) return h # shape= (None, u_n_words, 2*rnn_output_dim) or # shape(H_d) = (None, i_n_words, 2*rnn_output_dim) self.X = tf.placeholder(tf.int32, [None, maxlen], name='input_x') self.Y = tf.placeholder(tf.int64, [None]) self.encoder_embeddings = tf.Variable(tf.random_uniform([dict_size, embedded_size], -1, 1), trainable=False) encoder_embedded = tf.nn.embedding_lookup(self.encoder_embeddings, self.X) outputs = bi_rnn(rnn_type = rnn_type, inputs = encoder_embedded, rnn_output_dim = rnn_output_dim) # shape = [None, maxlen, rnn_output_dim] outputs = tf.nn.dropout(outputs, keep_prob = self.droput_rate) ''' Word Attention Layer ''' attention_w = tf.get_variable("attention_v", [attention_size], tf.float32) query = keras.layers.Dense(attention_size)(tf.expand_dims(outputs[:,-1], 1)) # shape =[None, 1, attention_size] keys = keras.layers.Dense(attention_size)(outputs) # shape = [None, maxlen, attention_size] self.attention = tf.reduce_sum(attention_w * tf.tanh(keys + query), 2) # shape = [None, maxlen] self.attention = tf.nn.softmax(self.attention, name='attention') outputs = tf.squeeze( tf.matmul( tf.transpose(outputs, [0, 2, 1]),tf.expand_dims(self.attention, 2) ), # shape = [None, rnn_output_dim, 1] 2) # shape = [None, rnn_output_dim] self.logits = keras.layers.Dense(label_num, use_bias=True)(outputs) self.probability = tf.nn.softmax(self.logits, name='probability') self.cost = tf.nn.sparse_softmax_cross_entropy_with_logits( labels = self.Y, logits = self.logits) self.cost = tf.reduce_mean(self.cost) self.optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(self.cost) self.pre_y = tf.argmax(self.logits, 1, name='pre_y') correct_pred = tf.equal(self.pre_y, self.Y) self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。