机器学习项目 - 垃圾邮件分类

本文介绍了一个机器学习项目,专注于垃圾邮件的分类。首先,详细阐述了数据清洗的过程,包括提取有价值特征、分析特征对结果的影响以及创建特征字典。接着,讨论了特征工程的步骤,如从邮件的from/to域名、日期中提取信息,并构造新的特征。最后,提到了使用分词、TF-IDF转换和降维技术为模型训练准备数据。涉及的脚本包括数据处理、特征提取和不同模型的测试。
摘要由CSDN通过智能技术生成


一, 数据清洗 
(1),先做数据清洗,清洗过的数据被称之为"干净数据";
    具体过程为-》要结合业务场景来判断哪些特征是值得被提取的,
    如果自身对业务场景并不熟悉,可以咨询或者请教身边经验丰富的人。

    举例:比较两句话的不同:
    ① 我司/代开/发票································1
    ② 月底/了/,/请/将/本月/发票/统一/装订/················0

(2),数据清洗过程中,也可以将所有认为可能对结果产生影响的特征全部进行提取,
    并且逐一分析每个特征对结果的影响,再删除掉那些与结果无关的特征。
(3),首先,根据label值,制作lable的正向字典
       其次,根据之前定义好的特征,分别提取非格式化邮件中的特征内容
       最后,讲所有提取到的特征转为行,并将216*300行内容保存至1个大的表格中。

二, 特征工程
 (1), 对from & to 中的域名进行匹配,并且寻找它们与label值之间的关系,
         如果关系比较紧密,存在明显的关系,则进一步将它们转化为数值;
         如果关系不大,则降它们删除

  (2),  对date中的值进行正则匹配提取,并对匹配后的格式化数据进行时间段切分:
        8~13=0;13~19=1;19~24=2;24~8=3;分别取寻找时间段和label之间的关系,
        如果关系明确切明显,则就按照时间段作为特征来数值化特征值,否则删除。
        data长度为16的值类型:['2005-9-2 上午11:04', '2005-9-2 上午10:55', '2005-9-2 上午10:55', 
                                  '2005-9-2 上午10:55', '2005-9-2 上午10:55', '2005-9-2 上午10:55', 
                                  '2005-9-2 上午10:55']
        data长度为19的值类型:['Sep 23 2005 1:04 AM']
        data长度为21的值类型:['August 24 2005 5:00pm', 'August 24 2005 5:00pm', 'August 24 2005 5:00pm']
        
        通过分析,时间段值不能起到区分垃圾邮件的作用,但是如果是正常邮件一定含有日期;如果是垃圾邮件,
        则不一定含有,所以可以构造'has date'这一列为新的特征作为最后带入的特征进行训练及预测。
        
        对于邮件长度进行统计,发现特别短和特别长的邮件是垃圾邮件的概率非常大,因此可以拟合用e为底的指数和
        对数函数来表征邮件长度的信息量,尽管信息量的值是大于1的,但是其特点和概率类似,即:值越大则是垃圾
        邮件的概率就越大,值越小则是垃圾邮件的概率就越小。

        最后,要为邮件的内容进行分词。

三、建模
    
    (1)分词后邮件内容的tf-idf转化(也可以用词袋法)。
     (2) 对tf-idf转化后的邮件内容做PCA 或者 svd 降维。
     (3) 把降维后的数据带入模型进行训练并测试。
 

数据处理脚本 dataProcess.py

#encoding:utf-8
import os
import sys#调试用包
import time#计时包

print("kaishi")
def 制作标签字典(file_path):
    type_dict = {"spam": "1", "ham": "0"}
    index_file = open(file_path)#./date/full/index
    index_dict = {}#要创建的新字典
    try:
        for line in index_file:
            arr = line.split(" ")
            #[spam,../data/000/000]
            if len(arr) == 2:
                key, value = arr
                #(spam),(../data/000/000)
            # 添加到字段中
            value = value.replace("../data", "").replace("\n", "")
            #(spam),(/000/000)
            index_dict[value] = type_dict[key.lower()]
            #{/000/000: 1}
            #{/000/001: 0}
    finally:
        index_file.close()
    return index_dict
# for file_path in path_list:
# 邮件的文件内容数据读取
def 字典化邮件文本内容(file_path):#dll
    './data/data/000/000'
    file = open(file_path, "r", encoding="gb2312", errors='ignore')
    content_dict = {}
    try:
        is_content = False#初始化为False后
        for line in file:
            #切掉两头的空格,逐步逼近格式化数据
            line = line.strip()
            if line.startswith("From:"):
                #From: "yan"<(8月27-28,上海)培训课程>,
                content_dict['from'] = line[5:]
                # "yan"<(8月27-28,上海)培训课程>#
                #{'from':"yan"<(8月27-28,上海)培训课程>}
            elif line.startswith("To:"):
                content_dict['to'] = line[3:]
                #lu@ccert.edu.cn
            elif line.startswith("Date:"):
                content_dict['date'] = line[5:]
                # Tue, 30 Aug 2005 10:08:15 +0800
            elif not line:
                is_content = True

            # 处理邮件内容,利用is_content的真假来执行一个小的针对文本内容的for loop
            if is_content:
                if 'content' in content_dict:
                    content_dict['content'] += line
                    #content_dict['content'] = content_dict['content']+line
                else:
                    content_dict['content'] = line
    finally:
        file.close()
        '释放堆中的内存地址'
    return content_dict

# 邮件数据处理
def 字典转文本(file_path):
    #先把内容读成字典
    content_dict = 字典化邮件文本内容(file_path)#先把非格式化邮件转化为字典

    # 再把字典转成文本
    result_str = content_dict.get('from', 'unkown').replace(',', '').strip() + ","
    result_str += content_dict.get('to', 'unknown').replace(',', '').strip() + ","
    result_str += content_dict.get('date', 'unknown').replace(',', '').strip() + ","
    result_str += content_dict.get('content', 'unknown').replace(',', ' ').strip()
    return result_str
    
#使用函数开始数据处理
start = time.time()#开始时间标记
index_dict = 制作标签字典('./data/full/index')
#{/000/000: 1}
#{/000/001: 0}
# index_dict = 制作标签字典('C:\\Users/Administrator/Desktop/index')#('./data/full/index')
# print(index_dict)
# sys.exit(0)
list0 = os.listdir('./data/data')#文件夹的名称
#{000,001,002,215}
 
for l1 in list0: #开始把N个文件夹中的file写入N*n个wiriter 
    '循环文件夹'
    l1_path = './data/data/' + l1#000
    #l1_path='./data/data/000',是文件夹的名称
    print('开始处理文件夹' + l1_path)#开始处理第000文件夹
    list1 = os.listdir(l1_path)#获取000文件夹内的所有文件名列表
    #[000,001]
    #list1文件列表
     
    write_file_path = './data/process01_' + l1
    #./data/process01_000
    #./data/process01_001
    #总共有216个
    #保存每个文件夹下面文件的文件 300行
    with open(write_file_path, "w", encoding= 'utf-8') as writer:
        for l2 in list1:
            '循环文件'
            l2_path = l1_path + "/" + l2#得到要处理文件的具体路径
             
            index_key = "/" + l1 + "/" + l2
             
            if index_key in index_dict:
                #{/000/000: 1}
                #{/000/001: 0}
                content_str = 字典转文本(l2_path)
                content_str += "," + index_dict[index_key] + "\n"
                writer.writelines(content_str)          
with open('./data/result_process01',"w", encoding ='utf-8') as writer:
    for l1 in list0:
        file_path= './data/process01_' + l1
        print("开始合并文件:" + file_path)
            
        with open(file_path, encoding = 'utf-8') as file:
            for line in file:
                writer.writelines(line)
#两个for嵌套共执行:  216*300=6W+        
            
end = time.time()
print('数据处理总共耗时%.2f'%(end- start))           

    
        

特征工程脚本 fetureExtract.py

import pandas as pd
import numpy as np


import matplotlib as mpl
import matplotlib.pyplot as plt
# import matplotlib.pyplot as plt
import re
import time
import jieba
import sys

# mpl.rcParams['font.sans-serif'] = [u'simHei']#ָ改为指定字体“黑体”
# mpl.rcParams['axes.unicode_minus'] = False #使得坐标轴保存负号变更为方块,用来正常显示负号
# plt.title(u'我是中文')
# get_ipython().magic(u'matplotlib tk')
'from to  date content label'
df = pd.read_csv('./data/result_process01', sep = ',', header = None, names= ['from','to', 'date', 'content','label'])
# print(df.head(10))
# print(df.tail(10))
# print(df.info())
# sys.exit("第20行")


#分析邮件的收发地址对label的影响
def 获取邮件收发地址(strl):#发送接收地址提取
    it = re.findall(r"@([A-Za-z0-9]*\.[A-Za-z0-9\.]+)", str(strl))#正则匹配
    #[^d]
    result = '
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值