因为同一个单词有着不同的词性,比如book可以表示名词,也可以表示动词,所以需要结合前后文给出具体的判断。下面介绍使用LSTM做词性判断原理。
1.基本原理
定义好一个LSTM网络,然后给出一个由很多词构成的句子,根据前面的内容,每个词可以用一个词向量表示,这样一句话就可以看作是一个序列,序列中的每个元素都是一个高维向量,将这个序列传入LSTM,可以得到与序列等长的输出,每个输出都表示为对词性的判断,比如名词、动词等。从本质上来看,这是一个分类问题,虽然使用了LSTM,但实际上根据这个词前面的一些词来对它进行分类,看它是属于几种词性中的哪一种。
思考一下为什么LSTM在这个问题里面起着重要的作用。如果完全孤立地对一个词做词性判断,往往无法得到比较准确的结果,但是通过LSTM,根据他记忆的特性,就能够通过这个单词前面记忆的一些词语来对它做一个判断,比如前面的单词如果是my,那么它紧跟的词很有可能就是一个名词,这样就能充分地利用上文来处理这个问题。
2.字符增强
还可以通过引入字符来增强表达,这是什么意思呢?就是说一些单词存在着前缀或者后缀,比如-ly这种后缀很可能是一个副词,这样我们就能够在水平上对词性进行进一步判断,把两种方法集成起来,能够得到一个更好的结果。
在实现上还是用LSTM,知识这次不再将句子作为一个序列,而是将每个单词作为一个序列。每个单词由不同的字母组成,比如apple由a p p l e构成,这些字符建立词向量,形成了一个长度为5的序列,将它传入LSTM网络,只取最后输出的状态层作为他的一种字符表达,不需要关心提取出来的字符表达到底是什么样,它作为一种抽象的特征,能够更好地预测结果。
3.词性判断的pyTorch实现
作为演示,使用一个简单的训练数据,下面有每句话的每个词都给出了词性:
training_data=[
("the dog ate the apple".split(),["DET","NN","V","DET","NN"]),
("Everybody read that book".split(),["NN","V","DET","NN"])
]
接着对单词和词性由a到z进行编码:
word_to_idx={}
tag_to_idx={}
for context,tag in training_data:
for word in context:
if word not in word_to_idx:
word_to_idx[word]=len(word_to_idx)
for label in tag:
if label not in tag_to_idx:
tag_to_idx[word]=len(tag_to_idx)
alphabet='abcdefghijklmnopqrstuvwxyz'
character_to_idx={}
for i in range(len(alphabet)):
character_to_idx[alphabet[i]]=i
接着先定义字符水准上的LSTM,定义方式和之前类似:
class CharLSTM(nn.Module):
def _init_(self,n_char,char_dim,char_hidden):
super(CharLSTM,self)._init_()
self.char_embedding=nn.Embedding(n_char,char_dim)
self.char_lstm=nn.LSTM(char_dim,char_hidden,batch_first=True)
def forward(self,x):
x=self.char_embedding(x)
_,h=self.char_lstm(x)
return h[0]
定义两层结构:第一层是词嵌入,第二层是LSTM。在网络的前向传播中,先将单词的n个字符传入网络,再通过nn.Embedding得到词向量,接着传入LSTM网络,得到隐藏状态的输出h,然后通过h[0]得到想要的输出状态。对于每个单词,都可以通过CharLSTM用相应的字符表示。
接着完成目标,分析每个单词的词性,首先定义好词性的LSTM网络:
class LSTMTagger(nn.Module):
def _init_(self,n_word,n_char,char_dim,n_dim,char_hidden,n_hidden,n_tag):
super(LSTMTagger,self)._init_()
self.word_embedding=nn.Embedding(n_word,n_dim)
self.char_lstm=CharLSTM(n_char,char_dim,char_hidden)