1. import os
  2. import email
  3. import email.policy

1. 读取邮件数据

  1. SPAM_PATH = os.path.join(
  2. "E:\\3.Study\\机器学习\\Hand on Machine Learning\\第三章:分类\\spam_data")
  3. spam_path = os.path.join(SPAM_PATH, "spam")
  4. ham_path = os.path.join(SPAM_PATH, "easy_ham")
  5. spam_list = [name for name in os.listdir(spam_path) if len(name) > 20]
  6. ham_list = [name for name in os.listdir(ham_path) if len(name) > 20]
  7. def load_email(is_spam, filename, spam_path=SPAM_PATH):
  8. directory = "spam" if is_spam else "easy_ham"
  9. with open(os.path.join(spam_path, directory, filename), "rb") as f:
  10. return email.parser.BytesParser(policy=email.policy.default).parse(f)
  11. # email.message.EmailMessage'类型,没法用list.append接收
  12. # return email.parser.BytesParser(policy=email.policy.default).parse(f)
  13. # 这里有类型问题,应该记住这种加载email文件的形式。尝试list.append添加数据,加入的数据是generator类型,非email类型
  14. ham_emails = [load_email(is_spam=False, filename=name) for name in ham_list]
  15. spam_emails = [load_email(is_spam=True, filename=name) for name in spam_list]
  16. print(spam_emails[0].get_content().strip())

2.分析邮件结构

  1. def get_email_structure(email):
  2. # isinstance 函数:判断一个对象是否是已知类型。第一个参数为对象,第二个参数为类型名或者是类型名的列表。返回True/False
  3. if isinstance(email, str):
  4. return email
  5. # get_pyload()函数:返回当前邮件的正文。
  6. # 如果正文含有多个部分的话(is_multipart=True),返回一个message对象的list列表;
  7. # 如果is_multipart=False,即正文没有多部份的话,返回一个string类型。
  8. payload = email.get_payload()
  9. if isinstance(payload, list):
  10. return "multipart({})".format(", ".join([
  11. get_email_structure(sub_email)
  12. for sub_email in payload
  13. ]))
  14. else:
  15. return email.get_content_type()
  16. from collections import Counter
  17. def structures_counter(emails):
  18. # Counter类的目的是用来跟踪值出现的次数
  19. structures = Counter()
  20. for email in emails:
  21. structure = get_email_structure(email)
  22. structures[structure] += 1
  23. return structures
  24. array = structures_counter(ham_emails).most_common()
  25. array2 = structures_counter(spam_emails).most_common()
  26. print(array)
  27. print(array2)

3.分析邮件头部

  1. for head, value in spam_emails[0].items():
  2. print(head, ":", value)
  3. print(spam_emails[0]["Subject"])

4. 划分训练集,测试集

  1. import numpy as np
  2. X = np.array(ham_emails+spam_emails) # ham_emails和span_emails是list类型
  3. Y = np.array([0]*len(ham_emails)+[1]*len(spam_emails))
  4. from sklearn.model_selection import train_test_split
  5. x_train, x_test, y_train, y_test = train_test_split(
  6. X, Y, test_size=0.33, random_state=42)

5. 邮件文本预处理(转换HTML)

  1. import re # regular expressions(正则)
  2. from html import unescape
  3. def html_to_plain_text(html):
  4. # sub->substitute(替换)
  5. # 参数1:pattern 正则;
  6. # 参数2:repl:replacement,被替换的字符串/函数
  7. # 参数3: string:需要被处理的内容
  8. # 参数4: count: 匹配的数目 如果正则表达式在string中有多个匹配结果,count控制匹配的数目
  9. # 参数5: flag : 匹配模式
  10. # re.I 匹配对大小写不敏感
  11. # re.M 多行匹配(以行为单位匹配)
  12. # re.S 使 . 匹配包括换行在内的所有字符
  13. # ->用pattern模式将string里面count个的字符换成repl
  14. text = re.sub('<head.*?>.*?</head>', '', html, flags=re.M | re.S | re.I)
  15. text = re.sub('<a\s.*?>', 'HYPERLINK', text, flags=re.M | re.S | re.I)
  16. text = re.sub('<.*?>', '', text, flags=re.M | re.S)
  17. text = re.sub(r'(\s*\n)+', '\n', text, flags=re.M | re.S)
  18. return unescape(text)
  19. html_spam_emails = [email for email in x_train[y_train == 1]
  20. if get_email_structure(email) == "text/html"]
  21. sample_html_spam = html_spam_emails[2]
  22. # 输出html辣鸡邮件的前1000个字符,strip()->去除首尾空格
  23. print(sample_html_spam.get_content().strip()[:1000], "...")
  24. print("-"*30)
  25. print(html_to_plain_text(sample_html_spam.get_content())[:1000], "...")

6.转换所有邮件为文本

  1. def email_to_text(email):
  2. html = None
  3. # email->part->part.part 以树的结构存储,walk()用来循环遍历各个树及其子树
  4. for part in email.walk():
  5. ctype = part.get_content_type()
  6. if not ctype in ("text/plain", "text/html"):
  7. continue # 跳过不是以上两种类型的部分
  8. try:
  9. content = part.get_content()
  10. except:
  11. content = str(part.get_payload())
  12. if ctype == "text/plain":
  13. return content
  14. else:
  15. html = content
  16. if html:
  17. return html_to_plain_text(html)
  18. print(email_to_text(sample_html_spam)[:100], "...")

7. 自然语言处理

  1. try:
  2. import nltk
  3. stemmer = nltk.PorterStemmer() # 建立一个波特词干算法(分析单词的词干)
  4. for word in ("Conputations", "Computation", "Computing", "Computed", "Compulsive"):
  5. print(word, "=>", stemmer.stem(word))
  6. except ImportError:
  7. print("Error: stemming requires the NLTK module.")
  8. stemmer = None

8.URL识别

  1. try:
  2. import urlextract
  3. url_extracror = urlextract.URLExtract()
  4. print(url_extracror.find_urls("will it detect gitub.com and https://www.google.com/search?ei=nqXjXL2VM5vqwQPks4rQAw&q=python+nltk&oq=python+nltk&gs_l=psy-ab.3..0j0i203l2j0j0i203l6.1867661.1868738..1869035...0.0..0.311.750.0j3j0j1......0....1..gws-wiz.......0i71j0i67.eLLHBxPtulQ"))
  5. except ImportError:
  6. print("Error:url_extracror requires the urlextract module.")
  7. urlextract = None

9. 对邮件内的所有单词进行计数

  1. from sklearn.base import BaseEstimator, TransformerMixin
  2. class EmailToWordCounterTransformer(BaseEstimator, TransformerMixin):
  3. def __init__(self, strip_headers=True, low_case=True, remove_punctuation=True,
  4. repalce_urls=True, replace_numbers=True, stemming=True):
  5. self.strip_headers = strip_headers
  6. self.low_case = low_case
  7. self.remove_punctuation = remove_punctuation
  8. self.replace_urls = repalce_urls
  9. self.replace_numbers = replace_numbers
  10. self.stemming = stemming
  11. def fit(self, X, Y=None):
  12. return self
  13. def transform(self, X, Y=None):
  14. X_transform = []
  15. for email in X:
  16. text = email_to_text(email) or ""
  17. if self.low_case:
  18. text = text.lower()
  19. if self.replace_urls and url_extracror is not None:
  20. # list(set()) 创建一个不重复的元素集
  21. urls = list(set(url_extracror.find_urls(text)))
  22. urls.sort(key=lambda url: len(url),
  23. reverse=True) # 根据url的长度对url进行排序
  24. for url in urls:
  25. text = text.replace(url, "URL") # 用“URL”换所有真实的url
  26. if self.replace_numbers: # 将所有数字转换为NUMBER字符
  27. text = re.sub(r'\d+(?:\.\d*(?:[eE]\d+))?', 'NUMBER', text)
  28. if self.remove_punctuation: # 删除所有标点符号
  29. text = re.sub(r'\W+', ' ', text, flags=re.M) # \W 匹配任何非单词字符
  30. # Counter()返回一个特殊的字典,包含单词种类和单词数量。eg:{"a":3,"b""2}
  31. word_count = Counter(text.split())
  32. if self.stemming and stemmer is not None:
  33. stemmed_word_counts = Counter()
  34. for word, count in word_count.items(): # 分析单词的词干,统计词干的数量
  35. stemmed_word = stemmer.stem(word)
  36. stemmed_word_counts[stemmed_word] += count
  37. word_count = stemmed_word_counts
  38. X_transform.append(word_count) # 将每个邮件的字符字典存到list中
  39. return np.array(X_transform)
  40. X_few = x_train[:3]
  41. X_few_wordcounts = EmailToWordCounterTransformer().fit_transform(X_few)
  42. print(X_few_wordcounts)
  43. from scipy.sparse import csr_matrix # 压缩稀疏行矩阵
  44. class WordCounterToVectorTransformer(BaseEstimator, TransformerMixin):
  45. def __init__(self, vocabulary_size=100):
  46. self.vocabulary_size = vocabulary_size
  47. def fit(self, X, Y=None):
  48. total_count = Counter()
  49. for word_count in X:
  50. for word, count in word_count.items(): # X是上个函数内的字典,不是X数据集
  51. total_count[word] += min(count, 10) # 次数超过10的存10
  52. # most_common 字典里面出现次数最多的.当most_common没有参数时,返回字典所有的item,从大到小排列
  53. # 查看前vocabulaty_size个出现次数最多的
  54. most_common = total_count.most_common()[:self.vocabulary_size]
  55. self.most_common_ = most_common
  56. # most_commoon [('number', 15), ('i', 7), ('the', 7), ('url', 7), ('to', 4), ('chri', 3), ('wa', 3), ('from', 3), ('list', 3), ('of', 3)]
  57. # 将most_common里面的出现频率最多的词从多到少依次排序,返回{(单词,序号)}
  58. self.vocabulary_ = {word: index + 1 for index,
  59. (word, count) in enumerate(most_common)}
  60. # vocabulary {'number': 1, 'i': 2, 'the': 3, 'url': 4, 'to': 5, 'chri': 6, 'wa': 7, 'from': 8, 'list': 9, 'of': 10}
  61. return self
  62. def transform(self, X, Y=None):
  63. rows = []
  64. cols = []
  65. data = []
  66. for row, word_count in enumerate(X):
  67. for word, count in word_count.items():
  68. rows.append(row)
  69. cols.append(self.vocabulary_.get(word, 0))
  70. data.append(count)
  71. return csr_matrix((data, (rows, cols)), shape=(len(X), self.vocabulary_size+1))
  72. vocab_transformer = WordCounterToVectorTransformer(vocabulary_size=10)
  73. X_few_vectors = vocab_transformer.fit_transform(X_few_wordcounts)
  74. print(X_few_wordcounts)
  75. print(X_few_vectors.toarray())
  76. print(vocab_transformer.vocabulary_)

10.训练分类器

  1. from sklearn.pipeline import Pipeline # 创建流水线处理
  2. preprocess_pipeline = Pipeline([
  3. ("email_to_wordcount", EmailToWordCounterTransformer()),
  4. ("wordcount_to_vector", WordCounterToVectorTransformer()),
  5. ])
  6. X_train_transformed = preprocess_pipeline.fit_transform(x_train)
  7. from sklearn.linear_model import LogisticRegression
  8. from sklearn.model_selection import cross_val_score
  9. log_clf = LogisticRegression()
  10. score = cross_val_score(log_clf, X_train_transformed,
  11. y_train, cv=3, verbose=3, n_jobs=-1)
  12. print(score.mean())

11.评估分类器

  1. from sklearn.metrics import precision_score, recall_score
  2. X_test_transformed = preprocess_pipeline.transform(x_test)
  3. # solver 优化算法的参数,包括newton-cg,lbfgs,liblinear,sag,saga,对损失的优化的方法
  4. log_clf2 = LogisticRegression(solver="liblinear", random_state=42)
  5. log_clf2.fit(X_train_transformed, y_train)
  6. y_pred = log_clf2.predict(X_test_transformed)
  7. print(precision_score(y_test, y_pred))
  8. print(recall_score(y_test, y_pred))
  9. from sklearn.naive_bayes import MultinomialNB
  10. mnb = MultinomialNB()
  11. mnb.fit(X_train_transformed, y_train)
  12. mnb_y_pred = mnb.predict(X_test_transformed)
  13. print(precision_score(y_test, mnb_y_pred))
  14. print(recall_score(y_test, mnb_y_pred))

Hand on Machine Learning第三章课后作业(1):垃圾邮件分类的更多相关文章

  1. Hand on Machine Learning第三章课后作业(2):其余小练习

    -#!/usr/bin/env python -# # # -- coding: utf-8 -- -# # # @Time : 2019.5.22 14:09 -# # # @Author : An ...

  2. C++第三章课后作业答案及解析---指针的使用

    今天继续完成上周没有完成的习题---C++第三章课后作业,本章题涉及指针的使用,有指向对象的指针做函数参数,对象的引用以及友元类的使用方法等 它们具体的使用方法在下面的题目中会有具体的解析(解析标注在 ...

  3. Hand on Machine Learning 第三章:分类器

    1. 获取数据 使用MNIST数据集练习分类任务 from sklearn.datasets import fetch_mldata from scipy.io import loadmat mnis ...

  4. CSAPP深入理解计算机系统(第二版)第三章家庭作业答案

    <深入理解计算机系统(第二版)>CSAPP 第三章 家庭作业 这一章介绍了AT&T的汇编指令 比较重要 本人完成了<深入理解计算机系统(第二版)>(以下简称CSAPP) ...

  5. 機器學習基石(Machine Learning Foundations) 机器学习基石 课后习题链接汇总

    大家好,我是Mac Jiang,非常高兴您能在百忙之中阅读我的博客!这个专题我主要讲的是Coursera-台湾大学-機器學習基石(Machine Learning Foundations)的课后习题解 ...

  6. JAVA第三周课后作业

    JAVA课后作业 一.枚举类型 代码: enum Size{SMALL,MEDIUM,LARGE}; public cl ass EnumTest { public static void main( ...

  7. 中级Perl 第三章课后习题

    3. 10. 1. 练习1 [25 分钟] 读当前目录的文件列表并转换成全路径.不能用shell 命令或外部程序读当前目 录.Perl 的File::Spec 和Cwd 两个模块对这个程序有帮助.每个 ...

  8. Python核心编程2第三章课后练习

    1. 标识符.为什么Python 中不需要变量名和变量类型声明? Python中的变量不需要声明,变量的赋值操作既是变量声明和定义的过程.每个变量在内存中创建,都包括变量的标识,名称和数据这些信息.每 ...

  9. Machine Learning 第三周

    ML week3 逻辑回归 Logistic Function h_\theta(x)=g(\theta^Tx) g(t)=\frac{1}{1+e^{-z}} 当t大于0, 即下面公式成立时,y=1 ...

随机推荐

  1. Gym - 101908H Police Hypothesis (树链剖分/LCT+字符串哈希)

    题意:有一棵树,树上每个结点上有一个字母,有两种操作: 1)询问树上两点u,v间有向路径上有多少个字母和某个固定的字符串相匹配 2)将结点u的字母修改为x 树剖+线段,暴力维护前缀和后缀哈希值(正反都 ...

  2. 【洛谷P1450】硬币购物

    题目大意:给定 4 种面值的硬币和相应的个数,求购买 S 元商品的方案数是多少. 题解: 考虑没有硬币个数的限制的话,购买 S 元商品的方案数是多少,这个问题可以采用完全背包进行预处理. 再考虑容斥, ...

  3. python基础(变量、基础数据类型、流程控制)

    今日内容html {overflow-x: initial !important;}:root { --bg-color:#ffffff; --text-color:#333333; --select ...

  4. C#的Winform多语言实现(resx文件)

    1. 简体中文 2. 繁体中文 3. 英文 下面子丰介绍一下实现的过程: 1. 为每个窗口创建相应语言的resx文件.子丰以英文为例,右键->添加->新建项->资源文件,文件名为窗口 ...

  5. R语言-程序执行时间

    我们往往对自己编写程序的运行效率十分关心,需要查看程序的执行时间. 在R中,获得时间的函数有不少,比如system.time().proc.time()等. 个人使用较多的是proc.time() & ...

  6. 织梦dedecms做的网站首页标题篡改跳转赌博网站解决方案

    织梦dedecms因其强大功能,简单实用的优点常常被用来做企业网站,程序开源使用的人多了网站漏洞多会有中毒的情况,常见的有一种,首页标题关键词描述被篡改,百度快照收录点击后跳转的赌博网站,怎么解决这个 ...

  7. nginx命令和配置

    centos 6.8安装的nginx 1.12.2 1.nginx常用的命令 使用nginx命令前,进入到/usr/local/nginx/sbin/目录 1)查看nginx版本 进入到/usr/lo ...

  8. luoguP2285 [HNOI2004]打鼹鼠 x

    P2285 [HNOI2004]打鼹鼠 题目描述 鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的.根据这个特点阿牛编写了一个打鼹鼠的游戏:在一个n*n的网格中,在某 ...

  9. js+jq 淡入淡出轮播(点击+定时+鼠标进入移出事件)

    <!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title> ...

  10. 理解PyTorch的自动微分机制

    参考Getting Started with PyTorch Part 1: Understanding how Automatic Differentiation works 非常好的文章,讲解的非 ...