基于CNN的文本分类实现-使用Keras

基于CNN的文本分类实现 - 使用 Keras

论文:Convolutional Neural Networks for Sentence Classification, Yoon Kim 2014

这篇论文开创了将卷积神经网络(CNN)应用于文本分类的先河,它通过实验证明了基于预训练好的词向量(如Glove的词向量)再加上参数的微调,通过仅有一个卷积层的简单 CNN 网络就可以在 NLP 任务中表现得非常好。

模型架构

上图很明确得给出了模型的网络结构。

输入一段文本和预训练好的词向量,假定文本长度为 $N$,词向量的维数为 $d$,则可以构成 CNN 的矩阵输入,维数为 $N \times d$

卷积层

1 维卷积,卷积核大小为 3或4或5,可采用多个卷积核,如 128 个 size 为 3 的卷积核,卷积层输出为 $(N - 2) \times 128$ (这里假定为 Valid 卷积)

池化层

在同一个卷积核上进行最大池化,并将不同的卷积核的池化结果合并在一起,如果采用 $128$ 个卷积核,则输出为长度为 $128$ 的向量

输出层

输出层为 $k$ 个神经元,通过softmax 输出各个分类对应的概率

模型的简单实现

数据集

数据集为爬取自 ChinaDaily 网站 business、lifestyle、culture、opinion等板块下的新闻,爬虫代码在这里

其中 culture 1601条,life 1782条,business 1512条,opinion 1760条。

新闻文本的长度直方图如下:

模型参数设置

词向量的选取:Glove词向量 glove.twitter.27B.200d(这里选择的不是太好,Twitter 大都是短文本,数据为新闻数据,但是做的时候只有用 Twitter 文本训练的词向量)

MAX_NUM_WORDS = 30000:词典的大小,模型只会考虑频率较高的 30000 个词。

MAX_SEQUENCE_LENGTH = 500,文本的长度,即CNN输入矩阵的行数 $N$

VALIDATION_SPLIT = 0.2:训练集:测试集 = 8:2

基于 Keras 的实现

以下仅贴出模型构造部分,完整代码在这里

第一种模型构造方式,代码参考自Keras的示例代码(就是抄过来的,改了改输入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 词向量层
embedding_layer = Embedding(num_words,
EMBEDDING_DIM,
embeddings_initializer=Constant(embedding_matrix),
input_length=MAX_SEQUENCE_LENGTH,
trainable=False)
# 输入
sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
# 输入转化为词向量矩阵
embedded_sequences = embedding_layer(sequence_input)
# 一层卷积 + 池化,采用128个window为5的卷积核
x = Conv1D(128, 5, activation='relu')(embedded_sequences)
x = MaxPooling1D(5)(x)
# 一层卷积 + 池化,采用128个 window 为5的卷积核
x = Conv1D(128, 5, activation='relu')(x)
x = MaxPooling1D(5)(x)
# 一层卷积 + 全局池化,采用 128 个 window 为 5 的卷积核
x = Conv1D(128, 5, activation='relu')(x)
x = GlobalMaxPooling1D()(x)
# 一个全连接层,且Dropout为0.3
x = Dense(128, activation='relu',W_constraint=maxnorm(3))(x)
x = Dropout(0.3)(x)
# 输出层
preds = Dense(len(labels_index), activation='softmax')(x)
model = Model(sequence_input, preds)
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['acc'])

model.fit(x_train, y_train,
batch_size=128,
epochs=10,
callbacks = [tb_cb],
validation_data=(x_val, y_val))

第二种构造,以论文内容为准

卷积层

128个size为3的卷积核 + 128 个 size 为 4 的卷积核 + 128 个size 为 5 的卷积核,一层卷积,384个卷积核

池化层

对每个卷积核做最大池化,输出为长度为 384 的向量,下一层为 4 个神经元的分类层。

模型结构如下图:

Python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 词嵌入层
embedding_layer = Embedding(num_words,
EMBEDDING_DIM,
embeddings_initializer=Constant(embedding_matrix),
input_length=MAX_SEQUENCE_LENGTH,
trainable=False)

submodels = []
sequence_input = [0]*3
embedd_sequence = [0]*3
j = 0

# 三种不同大小的卷积核分别进行卷积核池化
for i in FILTER_LENGTHS:
sequence_input[j] = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
embedd_sequence[j] = embedding_layer(sequence_input[j])
x = Conv1D(NB_FILTERS, i, activation='relu')(embedd_sequence[j])
x = GlobalMaxPooling1D()(x)
submodels.append(x)
j += 1
# 三种池化的输出合并并传入输出输出分类层
conted = concatenate(submodels, axis = 1)
x = Dropout(0.2)(conted)
preds = Dense(len(labels_index), activation = 'softmax')(x)
model = Model(sequence_input, preds)

model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['acc'])
model.fit([x_train, x_train, x_train], y_train,
batch_size=128,
epochs=20,
callbacks = [tb_cb],
validation_data=([x_val,x_val,x_val], y_val))

最终的分类精度大概能到 88% 左右,调参任务任重而道远呀!