PaddlePaddle垃圾邮件处理实战(一)

背景介绍

  在我们日常生活中,经常会受到各种垃圾邮件,譬如来自商家的广告、打折促销信息、澳门博彩邮件、理财推广信息等,一般来说邮件客户端都会设置一定的关键词屏蔽这种垃圾邮件,或者对邮件进行归类,但是总会有一些漏网之鱼。

  不过,自己手动做一个垃圾邮件分类器也并不是什么难事。传统的机器学习算法通常会采用朴素贝叶斯、支持向量机等算法对垃圾邮件进行过滤,今天我们主要讲如何用PaddlePaddle手写一个垃圾邮件分类器。当然,在讲PaddlePaddle做垃圾邮件处理之前,先回顾一下传统的机器学习算法是如何对垃圾邮件进行分类的。

了解数据集

  首先先了解一下今天的数据集:trec06c。trec06c是一个公开的垃圾邮件语料库,由国际文本检索会议提供,分为英文数据集(trec06p)和中文数据集(trec06c),其中所含的邮件均来源于真实邮件保留了邮件的原有格式和内容。

文件下载地址:trec06c

文件格式:

  1. trec06c

  2. └───data
  3. 000
  4. 001
  5. ...
  6. └───215
  7. └───delay
  8. index
  9. └───full
  10. index

文件内容:

垃圾邮件示例:本公司有部分普通发票(商品销售发票)增值税发票及海关代征增值税专用缴款书及其它服务行业发票,公路、内河运输发票。可以以低税率为贵公司代开,本公司具有内、外贸生意实力,保证我司开具的票据的真实性。 希望可以合作!共同发展!敬侯您的来电洽谈、咨询! 联系人:李先生 联系电话:13632588281 如有打扰望谅解,祝商琪。

正常邮件示例:讲的是孔子后人的故事。一个老领导回到家乡,跟儿子感情不和,跟贪财的孙子孔为本和睦。老领导的弟弟魏宗万是赶马车的。有个洋妞大概是考察民俗的,在他们家过年。孔为本总想出国,被爷爷教育了。最后,一家人基本和解。 顺便问另一类电影,北京青年电影制片厂的。

数据预处理

  拿到数据后我们可以很清楚的看到邮件的内容,但并不是所有的内容都是我们需要的,在这里我们仅提取了邮件中的中文来作为训练语料。如果仔细观察的话,会发现不是所有的邮件都能直接打开,数据的编码格式也需要转换成utf-8格式方便我们后面训练使用。所以我们需要对原始数据做一些数据预处理,包括以下几个内容。

基本步骤

  • 转换源数据编码格式为utf-8格式
  • 过滤字符
    • 去除所有非中文字符,如标点符号、英文字符、数字、网站链接等特殊字符。
  • 过滤停用词
  • 对邮件内容进行分词处理

训练代码

下面是具体的代码 transfer.py:

  1. # -*- coding: utf-8 -*-
  2. #Created by huxiaoman 2018.1.28
  3. #transfer.py:生成spam和ham数据
  4. import jieba
  5. import sys
  6. import os
  7. import re
  8. # 判断邮件中的字符是否是中文
  9. def check_contain_chinese(check_str):
  10. for ch in check_str.decode('utf-8'):
  11. if u'\u4e00' <= ch <= u'\u9fff':
  12. return True
  13. return False
  14. # 加载邮件数据的label
  15. def load_label_files(label_file):
  16. label_dict ={}
  17. for line in open(label_file).readlines():
  18. list1 = line.strip().split("..")
  19. label_dict[list1[1].strip()] = list1[0].strip()
  20. return label_dict
  21. # 加载停用词词表
  22. def load_stop_train(stop_word_path):
  23. stop_dict = {}
  24. for line in open(stop_word_path).readlines():
  25. line = line.strip()
  26. stop_dict[line] = 1
  27. return stop_dict
  28. # 读取邮件数据,并转换为utf-8格式,生成spam和ham样本
  29. def read_files(file_path,label_dict,stop_dict,spam_file_path,ham_file_path):
  30. parents = os.listdir(file_path)
  31. spam_file = open(spam_file_path,'a')
  32. ham_file = open(ham_file_path,'a')
  33. for parent in parents:
  34. child = os.path.join(file_path,parent)
  35. if os.path.isdir(child):
  36. read_files(child,label_dict,stop_dict,spam_file_path,ham_file_path)
  37. else:
  38. print child[10:]
  39. label = "unk"
  40. if child[10:] in label_dict:
  41. label = label_dict[child[10:]]
  42. # deal file
  43. temp_list = []
  44. for line in open(child).readlines():
  45. line = line.strip().decode("gbk",'ignore').encode('utf-8')
  46. if not check_contain_chinese(line):
  47. continue
  48. seg_list = jieba.cut(line, cut_all=False)
  49. for word in seg_list:
  50. if word in stop_dict:
  51. continue
  52. else:
  53. temp_list.append(word)
  54. line = " ".join(temp_list)
  55. print label
  56. if label == "spam":
  57. spam_file.write(line.encode("utf-8","ignore") + "\n")
  58. if label == "ham":
  59. ham_file.write(line.encode("utf-8","ignore")+"\n")
  60. # 生成word2vec词表
  61. def generate_word2vec(file_path,label_dict,stop_dict,word_vec):
  62. parents = os.listdir(file_path)
  63. fh1 = open(word_vec,'a')
  64. i = 0
  65. for parent in parents:
  66. child = os.path.join(file_path,parent)
  67. if os.path.isdir(child):
  68. generate_word2vec(child,label_dict,stop_dict,word_vec)
  69. else:
  70. print child[10:]
  71. i += 1
  72. print i
  73. label = "unk"
  74. if child[10:] in label_dict:
  75. label = label_dict[child[10:]]
  76. # deal file
  77. temp_list = []
  78. for line in open(child).readlines():
  79. line = line.strip().decode("gbk",'ignore').encode('utf-8')
  80. if not check_contain_chinese(line):
  81. continue
  82. if len(line) == 0:
  83. continue
  84. seg_list = jieba.cut(line, cut_all=False)
  85. for word in seg_list:
  86. if word in stop_dict:
  87. continue
  88. else:
  89. temp_list.append(word)
  90. line = " ".join(temp_list)
  91. fh1.write(line.encode("utf-8","ingore")+"\n")
  92. if __name__=="__main__":
  93. file_path = sys.argv[1]
  94. label_path = sys.argv[2]
  95. stop_word_path = "stop_words.txt"
  96. word_vec_path = "word2vec.txt"
  97. spam_data = "spam.txt"
  98. ham_data = "ham.txt"
  99. label_dict = load_label_files(label_path)
  100. stop_dict = load_stop_train(stop_word_path)
  101. read_files(file_path,label_dict,stop_dict,spam_data,ham_data)

运行脚本

run.sh:

  1. bashif [ $1 = "test" ]; then
  2. echo "test"
  3. python transfer.py ../test/ ../trec06c/full/index
  4. else
  5. echo "whole"
  6. python transfer.py ../trec06c/data/ ../trec06c/full/index
  7. fi

运行方式:

  1. 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

  1. # -*- coding: utf-8 -*-
  2. # Created by huxiaoman 2018.1.28
  3. # word2vec.py:生成word2vec模型
  4. import os
  5. import sys
  6. import numpy as np
  7. from gensim.models.word2vec import Word2Vec
  8. from gensim.corpora.dictionary import Dictionary
  9. import codecs
  10. reload(sys)
  11. sys.setdefaultencoding( "utf-8" )
  12. class MySentences(object):
  13. def __init__(self, dirname):
  14. self.dirname = dirname
  15. def __iter__(self):
  16. for fname in os.listdir(self.dirname):
  17. for line in codecs.open(os.path.join(self.dirname, fname),"r", encoding="utf-8",errors="ignore"):
  18. yield line.strip().split()
  19. # word2vec.txt数据的地址
  20. train_path = "rawData/"
  21. # 生成的word2vec模型的地址
  22. model_path = "/modelPath/"
  23. sentences = MySentences(train_path)
  24. # 此处min_count=5代表5元模型,size=100代表词向量维度,worker=15表示15个线程
  25. model = Word2Vec(sentences,min_count = 5,size=100,workers=15)
  26. #保存模型
  27. model.save(model_path+'/Word2vec_model.pkl')

运行方式

  1. python word2vec.py

运行结果

  1. Word2vec_model.pkl

模型训练

  生成正负样本数据并将词语全部转化为词向量后我们就可以把数据灌倒模型里进行训练了,本篇中将采用传统的机器学习算法svm来进行训练。

具体步骤

  • 加载数据集
  • 划分训练集train、验证集val与测试集test
  • 定义训练模型,并训练
  • 验证准确率

实现代码

  1. # 构建svm模型,加载数据等代码详见github
  2. def get_svm_model(x_train,y_train,x_val,y_val):
  3. model = SVC(C=1,kernel='rbf',max_iter=10,gamma=1,probability=True)
  4. model.fit(x_train,y_train)
  5. pred=model.predict(x_val)
  6. fpr,tpr,thresholds = roc_curve(y_val, pred, pos_label=2)
  7. score = metrics.f1_score(y_val,pred)
  8. print score

运行方式

  1. python train_svm.py

运行结果

  1. 0.73343221

小结

  本篇文章作为用PaddlePaddle处理垃圾邮件实战系列的预热,主要讲了如何对文本数据进行数据预处理与过滤,如何生成词向量以及用传统的机器学习方法--支持向量机训练模型,得到的准确率为0.73343221。其结果的好坏取决于词典的大小,词向量维度的大小,svm的基本参数的调整,在实际操作过程中还需要不断的调参才能达到最优的效果。下一篇我们将带领大家如何用PaddlePaddle来做垃圾邮件处理,用深度学习的方法对垃圾邮件进行分类,看看效果是否比传统的机器学习方法要更好,性能和速度是否能有一定的提升。

  • 本文受Modify的博文启发所写,所有含有Modify博文内容的部分都已经过Modify本人的同意。本文首发于景略集智,并由景略集智制作成“PaddlePaddle调戏邮件诈骗犯”系列视频。如果有不懂的,欢迎在评论区中提问~

【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)的更多相关文章

  1. 【深度学习系列】PaddlePaddle垃圾邮件处理实战(二)

    PaddlePaddle垃圾邮件处理实战(二) 前文回顾   在上篇文章中我们讲了如何用支持向量机对垃圾邮件进行分类,auc为73.3%,本篇讲继续讲如何用PaddlePaddle实现邮件分类,将深度 ...

  2. 【深度学习系列】关于PaddlePaddle的一些避“坑”技巧

    最近除了工作以外,业余在参加Paddle的AI比赛,在用Paddle训练的过程中遇到了一些问题,并找到了解决方法,跟大家分享一下: PaddlePaddle的Anaconda的兼容问题 之前我是在服务 ...

  3. 【深度学习系列2】Mariana DNN多GPU数据并行框架

    [深度学习系列2]Mariana DNN多GPU数据并行框架  本文是腾讯深度学习系列文章的第二篇,聚焦于腾讯深度学习平台Mariana中深度神经网络DNN的多GPU数据并行框架.   深度神经网络( ...

  4. 深度学习系列 Part(3)

    这是<GPU学习深度学习>系列文章的第三篇,主要是接着上一讲提到的如何自己构建深度神经网络框架中的功能模块,进一步详细介绍 Tensorflow 中 Keras 工具包提供的几种深度神经网 ...

  5. 推荐系统遇上深度学习(十)--GBDT+LR融合方案实战

    推荐系统遇上深度学习(十)--GBDT+LR融合方案实战 0.8012018.05.19 16:17:18字数 2068阅读 22568 推荐系统遇上深度学习系列:推荐系统遇上深度学习(一)--FM模 ...

  6. 【深度学习系列3】 Mariana CNN并行框架与图像识别

    [深度学习系列3] Mariana CNN并行框架与图像识别 本文是腾讯深度学习系列文章的第三篇,聚焦于腾讯深度学习平台Mariana中深度卷积神经网络Deep CNNs的多GPU模型并行和数据并行框 ...

  7. 基于TensorFlow的深度学习系列教程 2——常量Constant

    前面介绍过了Tensorflow的基本概念,比如如何使用tensorboard查看计算图.本篇则着重介绍和整理下Constant相关的内容. 基于TensorFlow的深度学习系列教程 1--Hell ...

  8. 使用腾讯云 GPU 学习深度学习系列之二:Tensorflow 简明原理【转】

    转自:https://www.qcloud.com/community/article/598765?fromSource=gwzcw.117333.117333.117333 这是<使用腾讯云 ...

  9. 【深度学习系列】PaddlePaddle之手写数字识别

    上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下.不过呢,这块内容太复杂了,所以就简单的介绍一下padd ...

随机推荐

  1. git rebase之前需要commit才行

    更新好本地代码后,git fetch, 接着合并,但是git rebase 不行, git status一看,有很多更新的文件. 于是 git add --后,再rebase,还是不行. 注意,reb ...

  2. leetcode之旅(7)-Move Zeroes

    Move Zeroes 题目描述: Given an array nums, write a function to move all 0's to the end of it while maint ...

  3. 复位windows网络参数的方法

    使用电脑的时候,经常会遇到网络相关的问题,以前读大学的时候就知道怎么解决,就是下面这个方案. 开始-全部程序-附件-命令提示符-右键-以管理员身份运行出来一个黑底白字的窗口,在里面输入: netsh ...

  4. rails中weill_paginate的paginate方法中不能使用额外参数的解决办法

    我们知道高版本中的rails中的分页功能已经放在will_paginate这个gem中,我们在控制器方法中往往需要调用其paginate方法来实现分页数据集控制,举个例子:正常的情况我们想要每页显示1 ...

  5. eclipse下载指南

    官网下载地址 下载https://www.eclipse.org/downloads/ 官网https://www.eclipse.org/ 最新版本 Eclipse OXYGEN Eclipse O ...

  6. 一个SQL存储过程面试题(比较简单)

    三个月前刚毕业的时候,听到存储过程就头疼. 写一个SQL存储过程,建立一个表USER 字段是姓名,年龄,职位,权限,然后向里面插入6条数据,然后查询出年龄大于18的所有信息. 下面是答案: 复制代码 ...

  7. storm中的Scheduler

    Scheduler是storm的调度器,负责为topology分配当前集群中可用的资源.Storm分别提供了3中调度器: EvenScheduler:会将系统中的可用资源均匀地分配给当前需要任务分配的 ...

  8. 没人看系列-----html随笔

    <!DOCTYPE> 目录 没人看系列-----html/css详解 前言 不多说这段时间写了好多好多前端的东西,以至于自己重新返回看了一遍前端的所有技术.故此做个总结,准备学东西的请绕行 ...

  9. hadoop 2.x安装:完全分布式安装

    1. 安装环境 本文使用三台CentOS6.4虚拟机模拟完全分布式环境.前五个过程和hadoop1.x安装相同 1.1. 安装环境 项目 参数 主操作系统 Windows 10 64 bit,8GB内 ...

  10. python flask中的代码约定

    在Python社区中有许多关于代码风格的约定.如果你写过一段时间Python了,那么也许对此已经有些了解. 我会简单介绍一下,同时给你一些URL链接,从中你可以找到关于这个话题的详细信息. 让我们提出 ...