模型原理
将卷积神经网络原理应用到文本分类任务中,利用多个不同 s i z e size size的 k e r n e l kernel kernel来提取句子中的关键信息,(类似于多窗口大小的ngram),从而能够更好的捕捉句子的相关性。
网络结构
Tecnn的详细过程如下图
详细过程
- Embedding: 第一层是图中最左边的 7 × 5 7 \times 5 7×5的句子矩阵,每行是词向量,维度 = 5,这可以类似与图像中的原始像素点。
- Convolution: 然后经过 k e r n e l s i z e = 2 , 3 , 4 kernel_size = {2,3,4} kernelsize=2,3,4的一维卷积层,每个核都两个输出通道 c h a n n e l channel channel
- MaxPolling: 第三层是一层的池化层,这样不同长度的句子经过 p o o l i n g pooling pooling层之后都能变成定长的表示。
- FullConnection and Softmax: 最后接一层全连接的 s o f t m a x softmax softmax层,输出每个类别的概率。
通道(channels):
- 图像可以利用 R , G , B R,G,B R,G,B作为不同的channel.
- 文本的输入的channel通常是不同方式的embedding方式.比如word2vec或glove, 实践中也有利用静态词向量和 f i n e − t u n n i n g fine-tunning fine−tunning词向量作为不同的channel做法。
一维卷积:
- 图像是二维数据。
- 文本是一维数据,因此在TextCNN卷积用的是一维卷积,(在word_level级别上是一维卷积,虽然文本经过词向量的表达后是二维数据,但是在embedding-level上的二维卷积没有意义)一维卷积带来的问题是通过设计不同的kernel_size的filter获取不同宽度的视野。
Pooling层:
利用CNN解决文本分类问题还很多,比如这篇A Convolutional Neural Network for Modelling Sentences ,最有意思的输入是在pooling 改成 (dynamic) k-max pooling,pooling阶段保留了
k
k
k个最大信息,保留了全局的序列信息
比如在情感分析场景中,举个例子:
“我觉得这个地方景色还不错,但是人也实在太多了”
虽然前半部分体现的情感是正向的,全局文本表达也是偏负向信息,利用 k − m a x − p o o l i n g k-max-pooling k−max−pooling能够很好的捕捉这类信息。
框架实现
这里使用 k e r a s keras keras库来实现textcnn:
import logging
from keras import Input
from keras.layers import Conv1D, MaxPool1D, Dense, Flatten, concatenate, Embedding
from keras.models import Model
from keras.utils import plot_model
def textcnn(max_sequence_length, max_token_num, embedding_dim, output_dim, model_img_path=None, embedding_matrix=None):
""" TextCNN: 1. embedding layers, 2.convolution layer, 3.max-pooling, 4.softmax layer. """
x_input = Input(shape=(max_sequence_length,))
logging.info("x_input.shape: %s" % str(x_input.shape)) # (?, 60)
if embedding_matrix is None:
x_emb = Embedding(input_dim=max_token_num, output_dim=embedding_dim, input_length=max_sequence_length)(x_input)
else:
x_emb = Embedding(input_dim=max_token_num, output_dim=embedding_dim, input_length=max_sequence_length,
weights=[embedding_matrix], trainable=True)(x_input)
logging.info("x_emb.shape: %s" % str(x_emb.shape)) # (?, 60, 300)
pool_output = []
kernel_sizes = [2, 3, 4]
for kernel_size in kernel_sizes:
c = Conv1D(filters=2, kernel_size=kernel_size, strides=1)(x_emb)
p = MaxPool1D(pool_size=int(c.shape[1]))(c)
pool_output.append(p)
logging.info("kernel_size: %s \t c.shape: %s \t p.shape: %s" % (kernel_size, str(c.shape), str(p.shape)))
pool_output = concatenate([p for p in pool_output])
logging.info("pool_output.shape: %s" % str(pool_output.shape)) # (?, 1, 6)
x_flatten = Flatten()(pool_output) # (?, 6)
y = Dense(output_dim, activation='softmax')(x_flatten) # (?, 2)
logging.info("y.shape: %s \n" % str(y.shape))
model = Model([x_input], outputs=[y])
if model_img_path:
plot_model(model, to_file=model_img_path, show_shapes=True, show_layer_names=False)
model.summary()
return model
特征
这里用的是词向量表示方式
数据量较大:可以直接随机初始化embeddings,然后基于语料通过训练模型网络来对embeddings进行更新和学习。
数据量较小: 可以利用外部语料来预训练(pre_train)词向量,然后输入到embedding层,用预训练的词向量矩阵初始化embedding。(通过设置weights=[embedding_matrix])
静态(static)方式: 训练过程中不再更新embeddings.
实质上属于迁移学习,特别是再目标领域或数据量比较小的情况下,采用静态的词向量效果也不错,(通过设置trainable=False)
非静态(non-static)范式:在训练过程中对embeddings进行更新和微调(fine tune)能够加速收敛,通过设置trainable=True)
plot_model()画出的TextCNN模型结构图如下: