之前提到用keras的Tokenizer进行文本预处理,序列化,向量化等,然后进入一个simple的LSTM模型中跑。但是发现用Tokenizer对象自带的 texts_to_matrix
得到的向量用LSTM训练不出理想的结果,反倒是换成Dense以后效果更好。后来实验了一下发现是对这个向量化函数的理解出现了偏差。鉴于网上没找到相关的讲解,就自己实验了一下,并在这里做一个总结。
原本以为直接用 texts_to_matrix
方法就可以直接把texts中的每个text,也就是每个string都转成LSTM输入所需要的向量,于是直接输入进去LSTM了。其实稍微一想就可以发现,LSTM要求的是每个string都是一个(num of word , length of word vector) 的矩阵,而这个函数出来的则是一个一维的向量,显然是有问题的。当时误判是因为没有仔细看,看到to matrix 并且得到了0-1的binary的matrix就想当然地当做输入了。那么下面就来看一下这个matrix究竟是什么:
somestr = ['ha ha gua angry','howa ha gua excited']
tok = tt.Tokenizer()
tok.fit_on_texts(somestr)
tok.word_index
Out[74]: {'angry': 3, 'excited': 5, 'gua': 2, 'ha': 1, 'howa': 4}
tok.word_counts
Out[75]: OrderedDict([('ha', 3), ('gua', 2), ('angry', 1), ('howa', 1), ('excited', 1)])
tok.texts_to_matrix(somestr)
Out[76]:
array([[ 0., 1., 1., 1., 0., 0.],
[ 0., 1., 1., 0., 1., 1.]])
可以看出,实际上这是一个binary的向量,如果dictionary的下标为i 的那个词在这个string中出现了,那么就给一个1,否则给0。当然,可以通过mode参数,进行设置,比如改成counts或者freq:
tok.texts_to_matrix(somestr,mode='count')
Out[83]:
array([[ 0., 2., 1., 1., 0., 0.],
[ 0., 1., 1., 0., 1., 1.]])
tok.texts_to_matrix(somestr,mode='freq')
Out[84]:
array([[ 0. , 0.5 , 0.25, 0.25, 0. , 0. ],
[ 0. , 0.25, 0.25, 0. , 0.25, 0.25]])
Tokenizer实际上只是生成了一个字典,并且统计了词频等信息,并没有把文本转成需要的向量表示。
如果Tokenizer加上num_words
这个参数,那么生成的就是列数为这个参数的matrix,其中包含单词表中most frequent的单词的binary或者count或者词频。
所以科学使用Tokenizer的方法是,首先用Tokenizer的 fit_on_texts
方法学习出文本的字典,然后word_index
就是对应的单词和数字的映射关系dict,通过这个dict可以将每个string的每个词转成数字,可以用texts_to_sequences
,这是我们需要的,然后通过padding的方法补成同样长度,在用keras中自带的embedding层进行一个向量化,并输入到LSTM中。
somestr = ['ha ha gua angry','howa ha gua excited naive']
tok = tt.Tokenizer()
tok.fit_on_texts(somestr)
tok.word_index
Out[90]: {'angry': 3, 'excited': 5, 'gua': 2, 'ha': 1, 'howa': 4, 'naive': 6}
tok.texts_to_sequences(somestr)
Out[91]: [[1, 1, 2, 3], [4, 1, 2, 5, 6]]
2018年03月05日16:11:27
大地春又回,长空裂惊雷。万物生欲动,无为自有为。 —— 诗人,余世存