注册

观点 | 用于文本的最牛神经网络架构是什么?


来源:机器之心

原标题:观点 | 用于文本的最牛神经网络架构是什么? 选自GitHub 作者:Nadbor Droz

原标题:观点 | 用于文本的最牛神经网络架构是什么?

选自GitHub

作者:Nadbor Drozd

机器之心编译

参与:路雪、刘晓坤


用于文本的最牛神经网络架构是什么?数据科学家 Nadbor 在多个文本分类数据集上对大量神经网络架构和 SVM + NB 进行了测试,并展示了测试结果。

去年,我写了一篇关于使用词嵌入如 word2vec 或 GloVe 进行文本分类的文章(http://nadbordrozd.github.io/blog/2016/05/20/text-classification-with-word2vec/)。在我的基准测试中,嵌入的使用比较粗糙,平均文档中所有单词的词向量,然后将结果放进随机森林。不幸的是,最后得出的分类器除了一些特殊情况(极少的训练样本,大量的未标注数据),基本都不如优秀的 SVM,尽管它比较老。

当然有比平均词向量更好的使用词嵌入的方式,上个月我终于着手去做这件事。我对 arXiv 上的论文进行了简单的调查,发现大部分先进的文本分类器使用嵌入作为神经网络的输入。但是哪种神经网络效果最好呢?LSTM、CNN,还是双向长短期记忆(BLSTM)CNN?网上有大量教程展示如何实现神经分类器,并在某个数据集上进行测试。问题在于它们给出的指标通常没有上下文。有人说他们在某个数据集上的准确率达到了 0.85。这就是好吗?它比朴素贝叶斯、SVM 还要好吗?比其他神经架构都好?这是偶然吗?在其他数据集上的效果也会一样好吗?

为了回答这些问题,我在 Keras 中实现了多个神经架构,并创建了一个基准,使这些算法与经典算法,如 SVM、朴素贝叶斯等,进行比较。地址:https://github.com/nadbordrozd/text-top-model。

模型

该 repository 中所有模型都用 .fit(X, y)、.predict(X)、.get_params(recursive) 封装在一个 scikit-learn 相容类中,所有的层大小、dropout 率、n-gram 区间等都被参数化。为清晰起见,下面的代码已经简化。

由于我本来想做一个分类器基准,而不是预处理方法基准,因此所有的数据集都已被符号化,分类器得到一个符号 id 列表,而不是字符串。

朴素贝叶斯

朴素贝叶斯分为两种:伯努利(Bernoulli)和多项式(Multinomial)。我们还可以使用 tf-idf 加权或简单的计数推断出 n-gram。由于 sklearn 的向量器的输入是字符串,并给它一个整数符号 id 列表,因此我们必须重写默认预处理器和分词器。

 
 
 
 
 
 
 
 
  1. from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer

  2. from sklearn.naive_bayes import BernoulliNB, MultinomialNB

  3. from sklearn.pipeline import Pipeline

  4. from sklearn.svm import SVC

  5. vectorizer = TfidfVectorizer(

  6.    preprocessor=lambda x: map(str, x),

  7.    tokenizer=lambda x: x,

  8.    ngram_range=(1, 3))

  9. model = Pipeline([('vectorizer', vectorizer), ('model', MultinomialNB())])


SVM

SVM 是所有文本分类任务的强大基线。我们可以对此重用同样的向量器。


 
 
 
 
 
 
 
 
  1. from sklearn.svm import SVC

  2. model = Pipeline([('vectorizer', vectorizer), ('model', SVC())])

多层感知器

又叫作 vanilla 前馈神经网络。该模型不使用词嵌入,输入是词袋。


 
 
 
 
 
 
 
 
  1. from keras.models import Sequential

  2. from keras.layers import Dense, Dropout, Activation

  3. from keras.preprocessing.text import Tokenizer

  4. vocab_size = 20000

  5. num_classes = 3

  6. model = Sequential()

  7. model.add(Dense(128, input_shape=(vocab_size,)))

  8. model.add(Activation('relu'))

  9. model.add(Dropout(0.2))

  10. model.add(Dense(128, input_shape=(vocab_size,)))

  11. model.add(Activation('relu'))

  12. model.add(Dropout(0.2))

  13. model.add(Dense(num_classes))

  14. model.add(Activation('softmax'))

  15. model.compile(loss='categorical_crossentropy',

  16.              optimizer='adam',

  17.              metrics=['accuracy'])


该模型的输入需要和标签一样进行 One-hot 编码。


 
 
 
 
 
 
 
 
  1. import keras

  2. from keras.preprocessing.text import Tokenizer

  3. tokenizer = Tokenizer(num_words=vocab_size)

  4. X = tokenizer.sequences_to_matrix(X, mode='binary')

  5. y = keras.utils.to_categorical(y, num_classes)

(双向)长短期记忆

从这里开始事情就变得有趣了。该模型的输入不是词袋而是一个词 id 序列。首先需要构建一个嵌入层将该序列转换成 d 维向量矩阵。


 
 
 
 
 
 
 
 
  1. import numpy as np

  2. from keras.layers import Embedding

  3. max_seq_len = 100

  4. embedding_dim = 37

  5. # we will initialise the embedding layer with random values and set trainable=True

  6. # we could also initialise with GloVe and set trainable=False

  7. embedding_matrix = np.random.normal(size=(vocab_size, embedding_dim))

  8. embedding_layer = Embedding(

  9.    vocab_size,

  10.    embedding_dim,

  11.    weights=[embedding_matrix],

  12.    input_length=max_seq_len,

  13.    trainable=True)


以下适用于该模型:


 
 
 
 
 
 
 
 
  1. from keras.layers import Dense, LSTM, Bidirectional

  2. units = 64

  3. sequence_input = Input(shape=(max_seq_len,), dtype='int32')

  4. embedded_sequences = embedding_layer(sequence_input)

  5. layer1 = LSTM(units,

  6.    dropout=0.2,

  7.    recurrent_dropout=0.2,

  8.    return_sequences=True)

  9. # for bidirectional LSTM do:

  10. # layer = Bidirectional(layer)

  11. x = layer1(embedded_sequences)

  12. layer2 = LSTM(units,

  13.    dropout=0.2,

  14.    recurrent_dropout=0.2,

  15.    return_sequences=False)  # last of LSTM layers must have return_sequences=False

  16. x = layer2(x)

  17. final_layer = Dense(class_count, activation='softmax')

  18. predictions = final_layer(x)

  19. model = Model(sequence_input, predictions)

该模型以及其他使用嵌入的模型都需要独热编码的标签,词 id 序列用零填充至固定长度:


 
 
 
 
 
 
 
 
  1. from keras.preprocessing.sequence import pad_sequences

  2. from keras.utils import to_categorical

  3. X = pad_sequences(X, max_seq_len)

  4. y = to_categorical(y, num_classes=class_count)


François Chollet 的 cnn

该架构(稍作修改)来自 Keras 教程(https://blog.keras.io/using-pre-trained-word-embeddings-in-a-keras-model.html),专门为长度为 1000 的文本设计,因此我使用它进行文本分类,而不用于语句分类。


 
 
 
 
 
 
 
 
  1. from keras.layers import Conv1D, MaxPooling1D

  2. units = 35

  3. dropout_rate = 0.2

  4. x = Conv1D(units, 5, activation='relu')(embedded_sequences)

  5. x = MaxPooling1D(5)(x)

  6. x = Dropout(dropout_rate)(x)

  7. x = Conv1D(units, 5, activation='relu')(x)

  8. x = MaxPooling1D(5)(x)

  9. x = Dropout(dropout_rate)(x)

  10. x = Conv1D(units, 5, activation='relu')(x)

  11. x = MaxPooling1D(35)(x)

  12. x = Dropout(dropout_rate)(x)

  13. x = Flatten()(x)

  14. x = Dense(units, activation='relu')(x)

  15. preds = Dense(class_count, activation='softmax')(x)

  16. model = Model(sequence_input, predictions)


Yoon Kim 的 CNN

该架构来自 Yoon Kim 的论文(https://arxiv.org/abs/1408.5882v2.pdf),我基于 Alexander Rakhlin 的 GitHub 页面(https://github.com/alexander-rakhlin/CNN-for-Sentence-Classification-in-Keras)实现该架构。这个架构不需要规定文本必须为 1000 词长,更适合语句分类。


 
 
 
 
 
 
 
 
  1. from keras.layers import Conv1D, MaxPooling1D, Concatenate

  2. z = Dropout(0.2)(embedded_sequences)

  3. num_filters = 8

  4. filter_sizes=(3, 8),

  5. conv_blocks = []

  6. for sz in filter_sizes:

  7.    conv = Conv1D(

  8.        filters=num_filters,

  9.        kernel_size=sz,

  10.        padding="valid",

  11.        activation="relu",

  12.        strides=1)(z)

  13.    conv = MaxPooling1D(pool_size=2)(conv)

  14.    conv = Flatten()(conv)

  15.    conv_blocks.append(conv)

  16. z = Concatenate()(conv_blocks) if len(conv_blocks) > 1 else conv_blocks[0]

  17. z = Dropout(0.2)(z)

  18. z = Dense(units, activation="relu")(z)

  19. predictions = Dense(class_count, activation="softmax")(z)

  20. model = Model(sequence_input, predictions)

BLSTM2DCNN

论文作者称,结合 BLSTM 和 CNN 将比使用任意一个效果要好(论文地址:https://arxiv.org/abs/1611.06639v1)。但是很奇怪,这个架构与前面两个模型不同,它使用的是 2D 卷积。这意味着神经元的感受野不只覆盖了文本中的近邻词,还覆盖了嵌入向量的近邻坐标。这有些可疑,因为他们使用的嵌入之间(如 GloVe 的连续坐标)并没有关系。如果一个神经元在坐标 5 和 6 学习到了一种模式,那么我们没有理由认为同样的模式会泛化到坐标 22 和 23,这样卷积就失去意义。但是我又知道些什么呢!


 
 
 
 
 
 
 
 
  1. from keras.layers import Conv2D, MaxPool2D, Reshape

  2. units = 128

  3. conv_filters = 32

  4. x = Dropout(0.2)(embedded_sequences)

  5. x = Bidirectional(LSTM(

  6.    units,

  7.    dropout=0.2,

  8.    recurrent_dropout=0.2,

  9.    return_sequences=True))(x)

  10. x = Reshape((2 * max_seq_len, units, 1))(x)

  11. x = Conv2D(conv_filters, (3, 3))(x)

  12. x = MaxPool2D(pool_size=(2, 2))(x)

  13. x = Flatten()(x)

  14. preds = Dense(class_count, activation='softmax')(x)

  15. model = Model(sequence_input, predictions)


堆叠

除了那些基础模型外,我还实现了堆叠分类器,来组合不同模型之间的预测。我使用 2 个版本的堆叠。一个是基础模型返回概率,概率由一个简单的 logistic 回归组合;另一个是基础模型返回标签,使用 XGBoost 组合标签。

数据集

对于文档分类基准,我使用的所有数据集均来自:http://www.cs.umb.edu/~smimarog/textmining/datasets/,包括 20 个新闻组、不同版本的 Reuters-21578 和 WebKB 数据集。

对于语句分类基准,我使用的是影评两极化数据集(http://www.cs.cornell.edu/people/pabo/movie-review-data/)和斯坦福情绪树库数据集(http://nlp.stanford.edu/~socherr/stanfordSentimentTreebank.zip)。

结果

一些模型仅用于文档分类或语句分类,因为它们要么在另一个任务中表现太差,要么训练时间太长。神经模型的超参数在基准中测试之前,会在一个数据集上进行调整。训练和测试样本的比例是 0.7 : 0.3。每个数据集上进行 10 次分割,每个模型接受 10 次测试。下表展示了 10 次分割的平均准确率。

文档分类基准


语句分类基准

结论

带嵌入的神经网络没有一个打败朴素贝叶斯和 SVM,至少没有持续打败。只有一层的简单前馈神经网络比任何其他架构效果都好。

我把这归咎于我的超参数,它们没有得到足够的调整,尤其是训练的 epoch 数量。每个模型只训练 1 个 epoch,但是不同的数据集和分割可能需要不同的设置。但是,神经模型显然在做正确的事,因为将它们添加至整体或者堆叠能够大大提高准确率。

原文地址:http://nadbordrozd.github.io/blog/2017/08/12/looking-for-the-text-top-model/

本文为机器之心编译,转载请联系本公众号获得授权。

✄------------------------------------------------

  • 好文
  • 钦佩
  • 喜欢
  • 泪奔
  • 可爱
  • 思考

频道推荐

凤凰网公益基金救助直达

凤凰科技官方微信

凤凰新闻 天天有料
分享到: