机器学习—— SVM分类算法
垃圾短信分类问题
Python语言凭借其强大的特性,其众多的外部库支持下,在机器学习和数据挖掘等领域发挥着强大的作用。本文基于python的机器学习库scikit-learn和完备的中文分词工具jieba 来对垃圾短信信息进行分类。完整代码位于Github(https://github.com/ZPdesu/Junk-Message-Classifier-sklearn)
分类样本
-
垃圾短信分类的样本数据分为带标签数据和不带标签数据。其中带标签数据用于模型训练和测试,不带标签数据用于线上模拟。样本数据类型如下
-
训练数据
0 xx计算机单招班彭雯同学表示将不负学校的培育
0 但是没有首都的在CBD一家好吃
0 今天给大家介绍一款Bookbook电脑包
1 亲爱的家长朋友你们好,我是张老师:新学期现在开始报名啦!本学期对老生家长的义务宣传有个回馈活动,老生带一个新生报名,赠送老生二百元兴趣
0 浙江宁波市马路突然爆裂
1 各位亲们好,新世纪奉节店迎接三八节,贝因美奶粉全场x.x折,欢迎惠额。活动时间x月x日至x月x日。
0 一架小型飞机在伦敦西南部一停车场坠毁 -
测试数据
刚刚坐电梯突然脚下踏板一软
佰特机器人俱乐部第七期大课堂在昨天圆满落幕了
训练集的标签域0代表是正常短信,1代表垃圾短信
- 可以看出短信分类的最大问题在于语意的理解。对中英文数字夹杂的语句进行分词,去除无意义的词汇,生成语意明确的等长词向量显得尤为重要。在词向量的特征提取过程中也可以采用TF-IDF等方式,来权衡每个词的相对重要性。
1 数据初步处理
1.1 数据集读取和存储
原始短信数据共有数十万条,为了便于快速检验模型正确性,我们暂且取出其中的10000条左右用于训练和验证。对于大数据量情况,则可以使用数据库来代替文件,以同样的方式进行训练判别。处理前的第一步需要从存储信息的原始txt文档中,分割出文本域和标签域。作为content 和label分别存入对应的json文件中。
- 以下代码用于将Raw_data文件夹下的txt文档信息转入对应的json文件中。
# -*- coding: utf-8 -*-
from numpy import *
import json
# 加载原始数据,进行分割
def load_message():
content = []
label = []
with open('RawData/message.txt') as fr:
for i in range(10000):
line = fr.readline()
lines.append(line)
num = len(lines)
for i in range(num):
message = lines[i].split('\t')
label.append(message[0])
content.append(message[1])
return num, content, label
# 将分割后的原始数据存到json
def data_storage(content, label):
with open('RawData/train_content.json', 'w') as f:
json.dump(content, f)
with open('RawData/train_label.json', 'w') as f:
json.dump(label, f)
if '__main__' == __name__:
num, content, label = load_message()
data_storage(content, label)
1.2 词向量生成(特征提取)
数据读取后下一步需要进行文本域的分词,在使用jieba进行精准模式分词之前需要对一些非规范数据进行,如对电话号码xxx,特殊字符&“】等进行统一转换。对一些反复出现的无意义词汇,如‘的’等当用词进行筛选。
这里首先介绍一下jieba分词的样式规范
In
# encoding=utf-8
import jieba
seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式
print(", ".join(seg_list))
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式
print(", ".join(seg_list))
Out
【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
【精确模式】: 我/ 来到/ 北京/ 清华大学
【新词识别】:他, 来到, 了, 网易, 杭研, 大厦 (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)
【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造
生成词向量方式是建立词表,通过统计词表中词汇的出现次数来构建向量。但这种构建向量的方式,会导致每个词的重要过于均衡,无法体现词向量中词汇的重要性,导致分类结果不理想。因此改进中采用了TF-IDF方式来构建词向量。
- 这里要注意,词向量中的元素是通过TF-IDF方式生成的,因此每个元素转换成了float型。因为整个训练集的词向量数量庞大,为了便于后续处理可以先将其存储为csr型的稀疏矩阵到mtx文件中,便于后续模型训练和验证。
# -*- coding: utf-8 -*-
import numpy as np
import jieba
import jieba.posseg as pseg
import sklearn.feature_extraction.text
import json
import re
from scipy import sparse, io
import load_data
# 将连续的数字转变为长度的维度
def process_cont_numbers(content):
digits_features = np.zeros((len(content), 16))
for i, line in enumerate(content):
for digits in re.findall(r'\d+', line):
length = len(digits)
if 0 < length <= 15:
digits_features[i, length-1] += 1
elif length > 15:
digits_features[i, 15] += 1
return process_cont_numbers
# 正常分词,非TFID
class MessageCountVectorizer(sklearn.feature_extraction.text.CountVectorizer):
def build_analyzer(self):