【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)
PaddlePaddle垃圾邮件处理实战(一)
背景介绍
在我们日常生活中,经常会受到各种垃圾邮件,譬如来自商家的广告、打折促销信息、澳门博彩邮件、理财推广信息等,一般来说邮件客户端都会设置一定的关键词屏蔽这种垃圾邮件,或者对邮件进行归类,但是总会有一些漏网之鱼。
不过,自己手动做一个垃圾邮件分类器也并不是什么难事。传统的机器学习算法通常会采用朴素贝叶斯、支持向量机等算法对垃圾邮件进行过滤,今天我们主要讲如何用PaddlePaddle手写一个垃圾邮件分类器。当然,在讲PaddlePaddle做垃圾邮件处理之前,先回顾一下传统的机器学习算法是如何对垃圾邮件进行分类的。
了解数据集
首先先了解一下今天的数据集:trec06c。trec06c是一个公开的垃圾邮件语料库,由国际文本检索会议提供,分为英文数据集(trec06p)和中文数据集(trec06c),其中所含的邮件均来源于真实邮件保留了邮件的原有格式和内容。
文件下载地址:trec06c
文件格式:
trec06c
│
└───data
│ │ 000
│ │ 001
│ │ ...
│ └───215
└───delay
│ │ index
└───full
│ │ index
文件内容:
垃圾邮件示例:本公司有部分普通发票(商品销售发票)增值税发票及海关代征增值税专用缴款书及其它服务行业发票,公路、内河运输发票。可以以低税率为贵公司代开,本公司具有内、外贸生意实力,保证我司开具的票据的真实性。 希望可以合作!共同发展!敬侯您的来电洽谈、咨询! 联系人:李先生 联系电话:13632588281 如有打扰望谅解,祝商琪。
正常邮件示例:讲的是孔子后人的故事。一个老领导回到家乡,跟儿子感情不和,跟贪财的孙子孔为本和睦。老领导的弟弟魏宗万是赶马车的。有个洋妞大概是考察民俗的,在他们家过年。孔为本总想出国,被爷爷教育了。最后,一家人基本和解。 顺便问另一类电影,北京青年电影制片厂的。
数据预处理
拿到数据后我们可以很清楚的看到邮件的内容,但并不是所有的内容都是我们需要的,在这里我们仅提取了邮件中的中文来作为训练语料。如果仔细观察的话,会发现不是所有的邮件都能直接打开,数据的编码格式也需要转换成utf-8格式方便我们后面训练使用。所以我们需要对原始数据做一些数据预处理,包括以下几个内容。
基本步骤
- 转换源数据编码格式为utf-8格式
- 过滤字符
- 去除所有非中文字符,如标点符号、英文字符、数字、网站链接等特殊字符。
- 过滤停用词
- 对邮件内容进行分词处理
训练代码
下面是具体的代码 transfer.py:
# -*- coding: utf-8 -*-
#Created by huxiaoman 2018.1.28
#transfer.py:生成spam和ham数据
import jieba
import sys
import os
import re
# 判断邮件中的字符是否是中文
def check_contain_chinese(check_str):
for ch in check_str.decode('utf-8'):
if u'\u4e00' <= ch <= u'\u9fff':
return True
return False
# 加载邮件数据的label
def load_label_files(label_file):
label_dict ={}
for line in open(label_file).readlines():
list1 = line.strip().split("..")
label_dict[list1[1].strip()] = list1[0].strip()
return label_dict
# 加载停用词词表
def load_stop_train(stop_word_path):
stop_dict = {}
for line in open(stop_word_path).readlines():
line = line.strip()
stop_dict[line] = 1
return stop_dict
# 读取邮件数据,并转换为utf-8格式,生成spam和ham样本
def read_files(file_path,label_dict,stop_dict,spam_file_path,ham_file_path):
parents = os.listdir(file_path)
spam_file = open(spam_file_path,'a')
ham_file = open(ham_file_path,'a')
for parent in parents:
child = os.path.join(file_path,parent)
if os.path.isdir(child):
read_files(child,label_dict,stop_dict,spam_file_path,ham_file_path)
else:
print child[10:]
label = "unk"
if child[10:] in label_dict:
label = label_dict[child[10:]]
# deal file
temp_list = []
for line in open(child).readlines():
line = line.strip().decode("gbk",'ignore').encode('utf-8')
if not check_contain_chinese(line):
continue
seg_list = jieba.cut(line, cut_all=False)
for word in seg_list:
if word in stop_dict:
continue
else:
temp_list.append(word)
line = " ".join(temp_list)
print label
if label == "spam":
spam_file.write(line.encode("utf-8","ignore") + "\n")
if label == "ham":
ham_file.write(line.encode("utf-8","ignore")+"\n")
# 生成word2vec词表
def generate_word2vec(file_path,label_dict,stop_dict,word_vec):
parents = os.listdir(file_path)
fh1 = open(word_vec,'a')
i = 0
for parent in parents:
child = os.path.join(file_path,parent)
if os.path.isdir(child):
generate_word2vec(child,label_dict,stop_dict,word_vec)
else:
print child[10:]
i += 1
print i
label = "unk"
if child[10:] in label_dict:
label = label_dict[child[10:]]
# deal file
temp_list = []
for line in open(child).readlines():
line = line.strip().decode("gbk",'ignore').encode('utf-8')
if not check_contain_chinese(line):
continue
if len(line) == 0:
continue
seg_list = jieba.cut(line, cut_all=False)
for word in seg_list:
if word in stop_dict:
continue
else:
temp_list.append(word)
line = " ".join(temp_list)
fh1.write(line.encode("utf-8","ingore")+"\n")
if __name__=="__main__":
file_path = sys.argv[1]
label_path = sys.argv[2]
stop_word_path = "stop_words.txt"
word_vec_path = "word2vec.txt"
spam_data = "spam.txt"
ham_data = "ham.txt"
label_dict = load_label_files(label_path)
stop_dict = load_stop_train(stop_word_path)
read_files(file_path,label_dict,stop_dict,spam_data,ham_data)
运行脚本
run.sh:
bashif [ $1 = "test" ]; then
echo "test"
python transfer.py ../test/ ../trec06c/full/index
else
echo "whole"
python transfer.py ../trec06c/data/ ../trec06c/full/index
fi
运行方式:
sh run.sh
运行结果:
- ham.txt: 正样本,正常邮件。共21373条数据。
- 示例:我 就 闹 不 明白 了 只要 你 本人 不介意 跟 你 爸爸妈妈 有 何干 为啥 要说 呢 ..... 首先 谢谢 大家 安慰 我 。 但是 我 确实 很 难受 , 我 有 自己 的 苦衷 。 我 不敢 和 我 妈妈 说 的 这种 情况 。 我 妈妈 是 那种 特别 容易 担心 的 那种 类型 。 而且 我 又 不 在 她 身边 。 我家 是 外地 的 。 如果 和 妈妈 说 了 , 她 一定 不会 同意 我 和 在 一起 的 。 妈妈 对 身体健康 看 的 特别 重要 。 有 一年 姐夫 那年 经常 流鼻血 , 妈妈 都 特别 担心 , 老 催 姐姐 带 着 去 看看 。
- spam.txt: 负样本,垃圾邮件。共41627条数据。
- 示例:您好 以下 是 特别 为 阁下 发 的 香港 信息 图片 、 景点 等 不 知道 阁下 是否 喜 希望 没有 打扰到 阁下 如果 无法 看到 下面 内容 请 稍侯 或者 直接 进入 香港 行网 域名论坛 地址 真诚 为您服务
- word2vec.txt: 包含所有邮件分词的内容,为Word2Vec提供训练预料。共63000条数据。
- 示例:我 觉得 , 负债 不要紧 , 最 重要 的 是 能 负得起 这个 责任 来 , 欠 了 那么 多钱 , 至少 对 当初 拿出 爱心 来 的 网友 们 有 个 交待 , 还 , 还是 不 还 了 , 或者 , 是 有 这个 心 但 实在 没 能力 , 说明 一声 还 都 好 不要 连 ID 都 不 激活 了 , 连 手机号 都 换 了 … … 别说 外地 的 了 , 就 连 北京 的 网友 都 找 不到 他 … … 他 当时 在 水木 fl 版 的 那阵 , 我 旁观 了 全过程 。
生成词向量
传统方法的局限性
我们知道,分词后的数据是不能直接拿到模型里去训练的,我们需要把词语转换成词向量才能进行模型的训练,这样一个词可以有一个多维的词向量组成。
传统的方法是one-hot encoding,即用一个长向量来表示一个词,向量的长度为词典的大小,向量的分量只有一个1,其余全为0,1的位置即对应改词在词典中的位置,如电脑表示为:[0 0 0 0 0 1 0 0 0 0 ],耳机表示为[0 0 0 0 0 0 0 1 0 ]这种方式如果采用稀疏存储,表达简洁,占用空间少,但是这种方法也有几个缺点,一是容易受维数灾难的困扰,尤其是将其用于 Deep Learning的一些算法时;二是不能很好地刻画词与词之间的相似性,即任意两个词之间都是孤立的。光从这两个向量中看不出两个词是否有关系,损失大部分信息,导致结果会有较大偏差。
Word2Vec方法的优势
在1968年Hinton又提出了Distributed REpresentation,可以解决One-hot encoding的缺点。其基本想法是直接用一个普通的向量表示一个词,这种向量一般长成这个样子:[0.792, −0.177, −0.107, 0.109, −0.542, ...],也就是普通的向量表示形式。维度以 50 维和 100 维比较常见。当然一个词怎么表示成这么样的一个向量需要通过训练得到,训练方法较多,word2vec是最常见的一种。需要注意的是,每个词在不同的语料库和不同的训练方法下,得到的词向量可能是不一样的。词向量一般维数不高,一般情况下指定1000、500维就可以了,所以用起来维数灾难的机会现对于one-hot representation表示就大大减少了。
由于是用向量表示,而且用较好的训练算法得到的词向量的向量一般是有空间上的意义的,也就是说,将所有这些向量放在一起形成一个词向量空间,而每一向量则为该空间中的一个点,在这个空间上的词向量之间的距离度量也可以表示对应的两个词之间的“距离”。所谓两个词之间的“距离”,就是这两个词之间的语法,语义之间的相似性。
一个比较爽的应用方法是,得到词向量后,假如对于某个词A,想找出这个词最相似的词,在建立好词向量后的情况,对计算机来说,只要拿这个词的词向量跟其他词的词向量一一计算欧式距离或者cos距离,得到距离最小的那个词,就是它最相似的。
所以在这里我们选择了word2vec方法来训练生成词向量。关于word2vec的原理大家可以在网上搜索学习,此处不再赘述。
实现代码
在数据预处理中我们生成的word2vec.txt就可以放到此处训练word2vec模型生成词向量了,具体实现代码如下: word2vec.py
# -*- coding: utf-8 -*-
# Created by huxiaoman 2018.1.28
# word2vec.py:生成word2vec模型
import os
import sys
import numpy as np
from gensim.models.word2vec import Word2Vec
from gensim.corpora.dictionary import Dictionary
import codecs
reload(sys)
sys.setdefaultencoding( "utf-8" )
class MySentences(object):
def __init__(self, dirname):
self.dirname = dirname
def __iter__(self):
for fname in os.listdir(self.dirname):
for line in codecs.open(os.path.join(self.dirname, fname),"r", encoding="utf-8",errors="ignore"):
yield line.strip().split()
# word2vec.txt数据的地址
train_path = "rawData/"
# 生成的word2vec模型的地址
model_path = "/modelPath/"
sentences = MySentences(train_path)
# 此处min_count=5代表5元模型,size=100代表词向量维度,worker=15表示15个线程
model = Word2Vec(sentences,min_count = 5,size=100,workers=15)
#保存模型
model.save(model_path+'/Word2vec_model.pkl')
运行方式
python word2vec.py
运行结果
Word2vec_model.pkl
模型训练
生成正负样本数据并将词语全部转化为词向量后我们就可以把数据灌倒模型里进行训练了,本篇中将采用传统的机器学习算法svm来进行训练。
具体步骤
- 加载数据集
- 划分训练集train、验证集val与测试集test
- 定义训练模型,并训练
- 验证准确率
实现代码
# 构建svm模型,加载数据等代码详见github
def get_svm_model(x_train,y_train,x_val,y_val):
model = SVC(C=1,kernel='rbf',max_iter=10,gamma=1,probability=True)
model.fit(x_train,y_train)
pred=model.predict(x_val)
fpr,tpr,thresholds = roc_curve(y_val, pred, pos_label=2)
score = metrics.f1_score(y_val,pred)
print score
运行方式
python train_svm.py
运行结果
0.73343221
小结
本篇文章作为用PaddlePaddle处理垃圾邮件实战系列的预热,主要讲了如何对文本数据进行数据预处理与过滤,如何生成词向量以及用传统的机器学习方法--支持向量机训练模型,得到的准确率为0.73343221。其结果的好坏取决于词典的大小,词向量维度的大小,svm的基本参数的调整,在实际操作过程中还需要不断的调参才能达到最优的效果。下一篇我们将带领大家如何用PaddlePaddle来做垃圾邮件处理,用深度学习的方法对垃圾邮件进行分类,看看效果是否比传统的机器学习方法要更好,性能和速度是否能有一定的提升。
- 本文受Modify的博文启发所写,所有含有Modify博文内容的部分都已经过Modify本人的同意。本文首发于景略集智,并由景略集智制作成“PaddlePaddle调戏邮件诈骗犯”系列视频。如果有不懂的,欢迎在评论区中提问~
【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)的更多相关文章
- 【深度学习系列】PaddlePaddle垃圾邮件处理实战(二)
PaddlePaddle垃圾邮件处理实战(二) 前文回顾 在上篇文章中我们讲了如何用支持向量机对垃圾邮件进行分类,auc为73.3%,本篇讲继续讲如何用PaddlePaddle实现邮件分类,将深度 ...
- 【深度学习系列】关于PaddlePaddle的一些避“坑”技巧
最近除了工作以外,业余在参加Paddle的AI比赛,在用Paddle训练的过程中遇到了一些问题,并找到了解决方法,跟大家分享一下: PaddlePaddle的Anaconda的兼容问题 之前我是在服务 ...
- 【深度学习系列2】Mariana DNN多GPU数据并行框架
[深度学习系列2]Mariana DNN多GPU数据并行框架 本文是腾讯深度学习系列文章的第二篇,聚焦于腾讯深度学习平台Mariana中深度神经网络DNN的多GPU数据并行框架. 深度神经网络( ...
- 深度学习系列 Part(3)
这是<GPU学习深度学习>系列文章的第三篇,主要是接着上一讲提到的如何自己构建深度神经网络框架中的功能模块,进一步详细介绍 Tensorflow 中 Keras 工具包提供的几种深度神经网 ...
- 推荐系统遇上深度学习(十)--GBDT+LR融合方案实战
推荐系统遇上深度学习(十)--GBDT+LR融合方案实战 0.8012018.05.19 16:17:18字数 2068阅读 22568 推荐系统遇上深度学习系列:推荐系统遇上深度学习(一)--FM模 ...
- 【深度学习系列3】 Mariana CNN并行框架与图像识别
[深度学习系列3] Mariana CNN并行框架与图像识别 本文是腾讯深度学习系列文章的第三篇,聚焦于腾讯深度学习平台Mariana中深度卷积神经网络Deep CNNs的多GPU模型并行和数据并行框 ...
- 基于TensorFlow的深度学习系列教程 2——常量Constant
前面介绍过了Tensorflow的基本概念,比如如何使用tensorboard查看计算图.本篇则着重介绍和整理下Constant相关的内容. 基于TensorFlow的深度学习系列教程 1--Hell ...
- 使用腾讯云 GPU 学习深度学习系列之二:Tensorflow 简明原理【转】
转自:https://www.qcloud.com/community/article/598765?fromSource=gwzcw.117333.117333.117333 这是<使用腾讯云 ...
- 【深度学习系列】PaddlePaddle之手写数字识别
上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下.不过呢,这块内容太复杂了,所以就简单的介绍一下padd ...
随机推荐
- 恶补web之二:css知识(1)
css指层叠样式表(Cascading Style Sheets) 样式定义如何显示html元素,样式通常存储在样式表里.把样式添加到html4.0中,是为了解决内容与表现分离的问题.外部样式 ...
- OpenShift实战(七):OpenShift定制镜像S2I
1.基础镜像制作 由于公司的程序是Java开发,上线发布使用的是maven,如果使用openshift自带的S2I,每次都会全量拉取代码(代码比较多,每次全量拉太慢),然后每次打包都会再一次下载mav ...
- Solr 新增、更新、删除索引
solr-admin新增索引 [索引中无则新增,有则更新] 1.在doc标签和field标签中增加权重(boost),增加权重后,可以在搜索的时候做权重过滤. <add> <doc ...
- 双机热备ROSE HA工作原理
双机热备ROSE HA工作原理 当双机热备软件启动后,ROSE HA首先启动HA Manager管理程序,根据高可靠性系统的配置结构初始化,然后启动必要的服务和代理程序来监控和管理系统服务.HA代理程 ...
- dll附加依赖项查看——dumpbin 命令
VS自带工具 查看程序或动态链接库需要的动态链接库 dumpbin /dependents d:\test.exe 查看动态链接库的输出函数 dumpbin /exports d:\libmysq ...
- goquery 添加header 发起请求
goquery 添加header 发起请求 我们知道使用net/http 很容易发起GET or POST 请求:并且在发起http请求时候,可以很容易的对header进行干预 例如: client ...
- 第一次作业 orm环境构建(hibernate)及基本的demo
一.数据库 1.创建数据库hibernate01-1514010311 2.创建表 customer CREATE TABLE customer( id int(11) not null auto_i ...
- jvm垃圾回收(三)
一.分代思想(年轻代.老年代.永久代): 1.一个新人(new对象)会优先在伊甸园(Eden区)出生,当伊甸园(Eden区)人口达到最大容量时,JVM会派MinorGC去看看哪些人还有价值 2.伊甸园 ...
- springboot中使用分页,文件上传,jquery的具体步骤(持续更新)
分页: pom.xml 加依赖 <dependency> <groupId>com.github.pagehelper</groupId> <arti ...
- Spring 下 MyBatis 的基本使用
参看代码 GitHub : pom.xml dbconfig.properties DbConfig.java MySqlBean.java User.java UserMapper.java Use ...