17.4 示例:识别垃圾邮件

完整演示代码请见本书GitHub上的17-3.py。

1.数据清洗与特征化

CNN的诞生是为了解决图像处理领域计算量巨大而无法进行深度学习的问题,CNN通过卷积计算、池化等方法大大降低了计算量,同时识别效果还满足需求。图像通常是二维数组,文字通常都是一维数据,是否可以通过某种转换后,也使用CNN对文字进行处理呢?答案是肯定的。

我们回顾一下在图像处理时,CNN是如何处理二维数据的。如图17-6所示,CNN使用二维卷积函数处理小块图像,提炼高级特征进一步分析。典型的二维卷积函数处理图片的大小为3×3、4×4等。

图17-6 CNN处理图像数据的过程

同样的原理,我们可以使用一维的卷积函数处理文字片段,提炼高级特征进一步分析。典型的一维卷积函数处理文字片段的大小为3、4、5等,如图17-7所示。

图17-7 CNN处理文本数据的过程

感谢Yoon Kim的经典论文Convolutional Neural Networks for Sentence Classification给我们的知识。常见的词袋模型可以很好地表现文本由哪些单词组成,但是却无法表达出单词之间的前后关系,于是人们借鉴了词袋模型的思想,使用生成的词汇表对原有句子按照单词逐个进行编码。TensorFlow默认支持了这种模型:


tf.contrib.learn.preprocessing.VocabularyProcessor (max_document_length, min_frequency=0,
vocabulary=None,
tokenizer_fn=None)

其中各个参数的含义为:

·max_document_length:,文档的最大长度。如果文本的长度大于最大长度,那么它会被剪切,反之则用0填充。

·min_frequency,词频的最小值,出现次数小于这个值的词则不会被收录到词表中。

·vocabulary,CategoricalVocabulary对象。

·tokenizer_fn,分词函数。

假设有如下句子需要处理:


x_text =[
    'i love you',
    'me too'
]

基于以上句子生成词汇表,并对'i me too'这句话进行编码:


vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)
vocab_processor.fit(x_text)
print next(vocab_processor.transform(['i me too'])).tolist()
x = np.array(list(vocab_processor.fit_transform(x_text)))
print x

运行结果为:


[1, 4, 5, 0]
[[1 2 3 0]
 [4 5 0 0]]

整个过程如图17-8所示。

图17-8 使用词汇表模型进行编码

我们使用TensorFlow自带的VocabularyProcessor对数据集的文本进行编码转换:


ham, spam=load_all_files()
x=ham+spam
y=[0]*len(ham)+[1]*len(spam)
vp=tflearn.data_utils.VocabularyProcessor(max_document_length=max_document_length, min_frequency=0, vocabulary=None, tokenizer_fn=None)
x=vp.fit_transform(x, unused_y=None)
x=np.array(list(x))
return x,y

使用词汇表编码后,将数据集合随机分配成训练集合和测试集合,其中测试集合比例为40%:


x,y=get_features_by_tf()
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.4, random_state = 0)

将训练和测试数据进行填充和转换,不到最大长度的数据填充0,由于是二分类问题,把标记数据二值化。定义输入参数的最大长度为文档的最大长度:


trainX = pad_sequences(trainX, maxlen=max_document_length, value=0.)
testX = pad_sequences(testX, maxlen=max_document_length, value=0.)
# Converting labels to binary vectors
trainY = to_categorical(trainY, nb_classes=2)
testY = to_categorical(testY, nb_classes=2)
network = input_data(shape=[None,max_document_length], name='input')

2.训练样本

定义CNN模型,其实使用3个数量为128核,长度分别为3、4、5的一维卷积函数处理数据:


network = tflearn.embedding(network, input_dim=1000000, output_dim=128)
branch1 = conv_1d(network, 128, 3, padding='valid', activation='relu', regularizer="L2")
branch2 = conv_1d(network, 128, 4, padding='valid', activation='relu', regularizer="L2")
branch3 = conv_1d(network, 128, 5, padding='valid', activation='relu', regularizer="L2")
network = merge([branch1, branch2, branch3], mode='concat', axis=1)
network = tf.expand_dims(network, 2)
network = global_max_pool(network)
network = dropout(network, 0.8)
network = fully_connected(network, 2, activation='softmax')
network = regression(network, optimizer='adam', learning_rate=0.001,
                     loss='categorical_crossentropy', name='target')

3.验证效果

实例化CNN对象并进行训练数据,一共训练5轮:


model = tflearn.DNN(network, tensorboard_verbose=0)
model.fit(trainX, trainY,
    n_epoch=5, shuffle=True, validation_set=(testX, testY),
    show_metric=True, batch_size=100,run_id="spam")

CNN的结构如图17-9所示。

在我的mac本上运行了近3个小时后,对测试数据集的识别准确度达到了98.30%,令人满意:


Training Step: 680  | total loss: 0.01691 | time: 2357.838s
| Adam | epoch: 005 | loss: 0.01691 - acc: 0.9992 | val_loss: 0.05177 - val_acc: 0.9830 -- iter: 13524/13524
--