使用LDA分类器对邮件进行分类

1. 简述

LDA线性判别分析(linear discriminant analysis, LDA)是最直接和最快的分类模型之一,是一种有监督的算法。模型的训练可分为3步:

(1)计算某个类(如垃圾短消息类)中所有TF-IDF向量的平均位置(质心);
(2)计算不在该类(如非垃圾短消息类)中的所有TF-IDF向量的平均位置(质心);
(3)计算上述两个质心之间的向量差(即连接这两个向量的直线)。

2. 例子

下面是一个例子:

import pandas as pd
from nlpia.data.loaders import get_data

# 有助于打印DataFrame时显示宽列的短消息文本
pd.options.display.width = 120
# 获取短消息数据集,格式为pandas.core.frame.DataFrame
sms = get_data('sms-spam')

下面是sms的样子:
使用LDA分类器对邮件进行分类_第1张图片

"""
将DataFrame的index设置成方便看的形式
可以看到在DataFrame里面spam为0的表示非垃圾信息,为1的表示垃圾信息
变换之后index带!的表示垃圾信息
"""
index = ['sms{}{}'.format(i, '!'*j) for (i, j) in zip(range(len(sms)), sms.spam)]
sms = pd.DataFrame(sms.values, columns=sms.columns, index=index)

# 将spam设置为int类型
sms['spam'] = sms.spam.astype(int)
sms['spam'] = sms.spam

使用LDA分类器对邮件进行分类_第2张图片
接下来对这些短消息进行分词,并将他们转换为TF-IDF向量(关于TF-IDF参考这里):

from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.tokenize.casual import casual_tokenize
# 实例化一个tfidf模型对象,并设置分词器为casual_tokenize
tfidf_model = TfidfVectorizer(tokenizer=casual_tokenize)
# 计算sms文档的tfidf
tfidf_docs = tfidf_model.fit_transform(raw_documents=sms.text).toarray()

在这里插入图片描述
这里的4837代表sms有4837个消息,9232代表sms里面有9232个不同的单词即词汇表的大小为9232。词汇表的规模是标注垃圾信息的10倍,通常词汇表的规模远远大于标注样本数量时朴素贝叶斯分类器就不是很奏效了,但是LDA会比较有效。
使用LDA分类器对邮件进行分类_第3张图片
可以看到TF-IDF矩阵是很稀疏的矩阵,但里面的值肯定不都是0,比如:
在这里插入图片描述
下面是计算两类的质心:

"""
计算质心
"""
# 用掩码从DataFrame中仅返回垃圾类的行
mask = sms.spam.astype(bool).values
# 计算非垃圾类的信息的每一列的平均值
spam_centroid = tfidf_docs[mask].mean(axis=0)
# 计算垃圾类的信息的每一列的平均值
ham_centroid = tfidf_docs[~mask].mean(axis=0)
# 四舍五入后返回2位浮点数
spam_centroid.round(2)
ham_centroid.round(2)
# 用一个质心的向量减去另一个质心的向量得到分类线
# 这个点积计算的是每个向量在质心连线上的“阴影”投影,(4837,9232)·(9232,1)
spamminess_score = tfidf_docs.dot(spam_centroid - ham_centroid)
spamminess_score.round(2)

在这里插入图片描述
使用LDA分类器对邮件进行分类_第4张图片
在这里插入图片描述
我们所得到的质心之间的向量就是分类模型了,用一个文档的tfidf向量与分类向量点乘可以得到一个score,当然垃圾信息和非垃圾信息得到的score会有比较明显的差别,我们设置一个阈值然后就可以把信息分为两个类了。

我们还想使score分布在0到1之间,这样就可以作为一个置信分数了:

from sklearn.preprocessing import MinMaxScaler
sms['lda_score'] = MinMaxScaler().fit_transform(spamminess_score.reshape(-1,1))
sms['lda_predict'] = (sms.lda_score > .5).astype(int)
sms['spam lda_predict lda_score'.split()].round(2).head(6)

输出:

	  spam lda_predict lda_score	
sms0	0		0			0.23
sms1	0		0			0.18
sms2!	1		1			0.72
sms3	0		0			0.18
sms4	0		0			0.29
sms5!	1		1			0.55

你可能感兴趣的:(NLP,c++,linux,bash)