背景
以往基于单词的NLP,一般是为要处理的词建立词向量空间,然后再对词向量进行NLP。这个处理会带来两个方面的问题是:
- 对于出现频率比较低的词,建立的词向量有可能不准确
- 无法处理不在单词表中的单词。比如网络上经常会出现一些新词,比如不明觉厉之类,英文的社交网络,还会出现比如loooooooooooooook之类的词。
- 有些语言还会有合成词。比如德语就会有大量的合成词,如Lebensversicherungsgesellschaftsangestellter=Leben versicherung gesellschaft angestellter:寿险公司的雇员。而该合成词中的每个词都有可能会在词表里,但是合成词不身却不在词表。
- 对于中文来说,要进行单词的NLP,还需要对其进行分词。而不同的分词器,分出的单词组合也不同。部分分词器对于人名之类的词也会分错。
于是,就有人提出为每个字符建立向量空间,再对其进行NLP。07年和13年的都有相应的论文进行了相应的尝试,但是不尽人意。15年之后的论文开始有比较好性能的字符级NLP。有些算法是直接将原单词级的神经网络直接替换为字符,有些比如只是在decoder端进行替换,还有些只是将单词切成segment,对每个segment建立向量空间。
基本结构
这里以英文NLP为例,介绍一个混合字符和单词、用于自动翻译的神经网络。该神经网络由斯坦福大学于2016年提出。
神经网络具体结构如图
其基本思想是基本结构仍然采用基于word级的encoder-decoder的LSTM。只不过进行了两处变化。
- encoder:原输入端是直接在encoder的各个LSTM cell输入词向量。而该做法则是在输入词向量之前,先把词拆成一个个的字符,输入的是字符向量,通过神经网络,由字符向量算出词向量之后,再将词向量加到各个LSTM cell上。
- decoder:在decoder端,原来的做法是产生一个个单词,如果该单词不在单词表中的话,用<unk>表示。而该做法是,如果某个LSTM cell输出了<unk>,则将该输出再接到另一个神经网络,逐个产生相应的字符,再将该神经网络产生的字符组成对应的单词。
基于word级的RNN的机器翻译技术在前文已经有介绍,这里不再重复叙述。这里重点介绍encoder和decoder处的变化。
Encoder
首先要对输入文本进行预处理,有以下步骤:
- 首先对输入的所有文本,计算其每句话的长度(单词个数),得到最大的长度 N N N。将所有没有到最大长度的句子加上<pad>。(确保word的Encoder端LSTM Cell数量是一定的)
- 设定每个单词的最大长度
m
w
o
r
d
m_{word}
mword。对于没有达到最大长度的单词,也全部加上<pad>。(确保处理字符的LSTM Cell数量是一定的)。
然后再逐句输入到Encoder端,输入到Encoder端的tensor的shape为 ( N , b , m w o r d ) (N, b, m_{word}) (N,b,mword),其中 b b b为batch的大小(即句子的数量)。
Encoder端的处理字符向量的模块如图所示。
对于单个单词,首先要确定每个字符在字典中的位置,即
x p a d d e d = ( c 1 , c 2 , . . . , c m w o r d ) ∈ Z m w o r d \mathbf x_{padded} = (c_1,c_2,...,c_{m_{word}}) \in \mathbb Z^{m_{word}} xpadded=(c1,c2,...,cmword)∈Zmword
其中 c i c_i ci为单词 x x x的第 i i i个字符在字典中的位置。这样根据 x p a d d e d \mathbf x_{padded} xpadded,就可以建立字符向量
x e m b e d = C h a r E m b e d d i n g ( x p a d d e d ) ∈ R m w o r d × e c h a r \mathbf x_{embed} = CharEmbedding(\mathbf x_{padded}) \in \mathbb R^{m_{word} \times e_{char}} xembed=CharEmbedding(xpadded)∈Rmword×echar
其中, e c h a r e_{char} echar为字符向量的长度。
在神经网络实现框架里,都有Embedding的函数,用于建立输入变量的向量空间,初始值随机。这样,模型训练时,向量可以跟着一起进行训练。如果直接用gensim之类的word2vec,而且不用Embedding,向量就不会训练。
将字符向量送入到卷积核为
k
k
k,进行数量
f
f
f个卷积,就可以得到
x
c
o
n
v
=
c
o
n
v
1
D
(
k
,
f
)
(
x
e
m
b
e
d
T
)
∈
R
f
×
(
m
w
o
r
d
−
k
+
1
)
\mathbf x_{conv} = conv1D(k, f)(\mathbf x_{embed}^T) \in \mathbb R^{f \times (m_{word}-k+1)}
xconv=conv1D(k,f)(xembedT)∈Rf×(mword−k+1)
令
f
=
e
w
o
r
d
f=e_{word}
f=eword,其中
e
w
o
r
d
e_{word}
eword为词向量的长度。这样,进行maxpool和Relu就可以得到
x
c
o
n
v
_
o
u
t
=
M
a
x
P
o
o
l
(
R
e
L
U
(
x
c
o
n
v
)
)
∈
R
f
=
R
e
w
o
r
d
\mathbf x_{conv\_out}=MaxPool(ReLU(\mathbf x_{conv} )) \in \mathbb R^{f}=\mathbb R^{e_{word}}
xconv_out=MaxPool(ReLU(xconv))∈Rf=Reword
然后将
x
c
o
n
v
_
o
u
t
\mathbf x_{conv\_out}
xconv_out经过一个highway network,其中
x
p
r
o
j
=
R
e
L
U
(
W
p
r
o
j
x
c
o
n
v
_
o
u
t
+
b
p
r
o
j
)
∈
R
e
w
o
r
d
x
g
a
t
e
=
σ
(
W
g
a
t
e
x
c
o
n
v
_
o
u
t
+
b
g
a
t
e
)
∈
R
e
w
o
r
d
x
h
i
g
h
w
a
y
=
x
g
a
t
e
⊙
x
p
r
o
j
+
(
1
−
x
g
a
t
e
)
⊙
x
c
o
n
v
_
o
u
t
∈
∈
R
e
w
o
r
d
\begin{aligned} \mathbf x_{proj} &= ReLU(\mathbf W_{proj} \mathbf x_{conv\_out} + \mathbf b_{proj} ) \in \mathbb R^{e_{word}}\\ \mathbf x_{gate} &= \sigma(\mathbf W_{gate}\mathbf x_{conv\_out} + \mathbf b_{gate} ) \in \mathbb R^{e_{word}}\\ \mathbf x_{highway} &= \mathbf x_{gate} \odot \mathbf x_{proj} + (1-\mathbf x_{gate}) \odot \mathbf x_{conv\_out} \in \in \mathbb R^{e_{word}} \end{aligned}
xprojxgatexhighway=ReLU(Wprojxconv_out+bproj)∈Reword=σ(Wgatexconv_out+bgate)∈Reword=xgate⊙xproj+(1−xgate)⊙xconv_out∈∈Reword
其中
W
p
r
o
j
,
W
g
a
t
e
∈
R
e
w
o
r
d
×
e
w
o
r
d
\mathbf W_{proj}, \mathbf W_{gate} \in \mathbb R^{e_{word} \times e_{word}}
Wproj,Wgate∈Reword×eword,
b
p
r
o
j
,
b
g
a
t
e
∈
R
e
w
o
r
d
\mathbf b_{proj}, \mathbf b_{gate} \in \mathbb R^{e_{word}}
bproj,bgate∈Reword,
⊙
\odot
⊙表示逐元素相乘。经过Dropout就可以得到输出的词向量:
x
w
o
r
d
_
e
m
b
=
D
r
o
p
o
u
t
(
x
h
i
g
h
w
a
y
)
∈
R
e
w
o
r
d
\mathbf x_{word\_emb} = Dropout(\mathbf x_{highway}) \in \mathbb R^{e_{word}}
xword_emb=Dropout(xhighway)∈Reword。
注意到在上述过程中,总共需要的参数有
V
c
h
a
r
×
e
c
h
a
r
+
(
e
c
h
a
r
×
k
+
1
)
×
e
w
o
r
d
+
2
(
e
w
o
r
d
×
e
w
o
r
d
+
e
w
o
r
d
)
+
e
w
o
r
d
V_{char} \times e_{char} + (e_{char} \times k + 1) \times e_{word} + 2(e_{word} \times e_{word} + e_{word}) + e_{word}
Vchar×echar+(echar×k+1)×eword+2(eword×eword+eword)+eword
其中第一项表示字符词典参数数量,第二项表示卷积的参数数量,第三项表示highway network的参数数量,第四项是Dropout的参数数量。而如果直接采用词向量,所需的参数数量为
V
w
o
r
d
×
e
w
o
r
d
V_{word} \times e_{word}
Vword×eword
对于英语而言,字符数(包括标点符号)
V
c
h
a
r
V_{char}
Vchar一般不超过100个,而牛津英语词典的单词数
V
w
o
r
d
V_{word}
Vword就达到170000+,而
e
c
h
a
r
e_{char}
echar一般也就取20多,而
e
w
o
r
d
e_{word}
eword经常要取到200多,因此后者的参数数量是远大于前者的。而对于汉字,一般
e
w
o
r
d
e_{word}
eword和
e
c
h
a
r
e_{char}
echar基本相当,以最新版的新华词典为例,
V
c
h
a
r
V_{char}
Vchar约为8500,
V
w
o
r
d
V_{word}
Vword约为52000,但即便如此,前者的参数数量仍然是小于后者的。
Encoder剩下的部分就和基于word的LSTM encoder完全一样了,不再详述。
Decoder
Train阶段
在训练阶段时,可以在每个Word LSTM Decoder Cell上都加上Char Decoder(这些Char Decoder均共享同一参数),将该LSTM Cell输出的
o
\mathbf o
o(见前文基于RNN的自动翻译的技术介绍-seq2seq+attention模型)作为该Char Decoder的初始化的状态,即
h
0
=
c
0
=
o
\mathbf h_0 = \mathbf c_0 = \mathbf o
h0=c0=o,而将LSTM Decoder Cell本应输出的(注意不是实际输出的)单词对应的字符Char Decoder的输入。比如如果单词是music,那么字符
(
x
1
,
.
.
.
,
x
n
)
=
(
<
s
>
,
m
,
u
,
s
,
i
,
c
)
(x_1,...,x_n) = (<s>, m, u, s, i, c)
(x1,...,xn)=(<s>,m,u,s,i,c)则作为char decoder的输入,
(
x
2
,
.
.
.
,
x
n
+
1
)
=
(
m
,
u
,
s
,
i
,
c
,
<
e
>
)
(x_2,...,x_{n+1})=(m,u,s,i,c,<e>)
(x2,...,xn+1)=(m,u,s,i,c,<e>)作为char decoder的输出。
这样
h
t
,
c
t
=
c
h
a
r
D
e
c
o
d
e
r
L
S
T
M
(
x
t
,
h
t
−
1
,
c
t
−
1
)
,
h
t
,
c
t
∈
R
h
\mathbf h_t, \mathbf c_t = charDecoderLSTM(\mathbf x_t, \mathbf h_{t-1}, \mathbf c_{t-1}), \mathbf h_t, \mathbf c_t \in \mathbb R^h
ht,ct=charDecoderLSTM(xt,ht−1,ct−1),ht,ct∈Rh
其中,
x
t
\mathbf x_t
xt是字符
x
t
x_t
xt对应的向量。注意,
x
t
x_t
xt对应的字符向量是可以不同于Encoder端的字符向量的。
对于每个hidden state,可以经过全连接层得到
s
t
=
W
d
e
c
h
t
+
b
d
e
c
∈
R
V
c
h
a
r
\mathbf s_t = \mathbf W_{dec} \mathbf h_t + \mathbf b_{dec} \in \mathbb R^{V_{char}}
st=Wdecht+bdec∈RVchar
其中,
W
d
e
c
∈
R
V
c
h
a
r
×
h
\mathbf W_{dec} \in \mathbb R^{V_{char} \times h}
Wdec∈RVchar×h以及
b
d
e
c
∈
R
V
c
h
a
r
\mathbf b_{dec} \in \mathbb R^{V_{char}}
bdec∈RVchar。这样经过softmax后,计算loss为
l
o
s
s
=
−
∑
t
=
1
n
C
r
o
s
s
E
n
t
r
o
p
y
(
s
o
f
t
m
a
x
(
s
t
)
,
x
t
+
1
)
loss=-\sum_{t=1}^n CrossEntropy(softmax(\mathbf s_t), \mathbf x_{t+1})
loss=−t=1∑nCrossEntropy(softmax(st),xt+1)
然后再将loss叠加到word decoder的loss上,作为总的loss函数。
Test阶段
在Test阶段,可以只考虑Word Decoder输出为<unk>的LSTM Cell上,加上char decoder。Char decoder初始的
h
0
,
c
0
\mathbf h_0, \mathbf c_0
h0,c0取值仍为该word decoder LSTM Cell上对应的
o
\mathbf o
o,并在第一个char decoder LSTM Cell上输入单词的开始标志<s>。其后续的输入,输出的计算和Train阶段相同。
得到
s
o
f
t
m
a
x
(
s
t
)
softmax(\mathbf s_t)
softmax(st)后,判断究竟是哪个字符,可以采用Beam Search的方法。(见前文基于RNN的自动翻译的技术介绍-seq2seq+attention模型),直到LSTM Cell上输出结尾标志<e>或者达到最大单词长度
m
w
o
r
d
m_{word}
mword。
分析
从结果看,由字符向量得到的词向量,相似度高的词往往是拼写和构词上相似的词。而word2vec得到的词向量,相似度高的词往往是语义上接近的词。比如前者的结果为
后者的结果为
。
但是字符向量的好处处理时态变位能力比词向量强。比如某些单词的时态变位不在词典里,普通的词向量级的NLP就无法识别;而字符向量则往往能识别和解码出来。
另外,针对中文汉字级别的NLP,去年已经有文章Is Word Segmentation Necessary for Deep Learning of Chinese Representations?提出其性能要优于基于分词后的NLP,这个也是可以在进一步研究的地方。