赞
踩
LDA是一种监督学习的降维技术,也就是说它的数据集的每个样本是有类别输出的。这点和PCA不同。PCA是不考虑样本类别输出的无监督降维技术。LDA的思想可以用一句话概括,就是“投影后类内方差最小,类间方差最大”。什么意思呢? 我们要将数据在低维度上进行投影,投影后希望每一种类别数据的投影点尽可能的接近,而不同类别的数据的类别中心之间的距离尽可能的大。
下面,我们给出了 LDA 的一个简单的实现版本,模型训练有 3 个步骤:
(1)计算某个类(如垃圾短消息类)中所有 TF-IDF 向量的平均位置(质心)。
(2)计算不在该类(如非垃圾短消息类)中的所有 TF-IDF 向量的平均位置(质心)。
(3)计算上述两个质心之间的向量差(即连接这两个向量的直线)。
即要“训练”LDA 模型,只需找到两个类的质心之间的向量(直线)。LDA 是一种有监督算法,因此需要为消息添加标签。要利用该模型进行推理或预测,只需要判断新的 TF-IDF 向量是否更接近类内(垃圾类)而不是类外(非垃圾类)的质心。
# 来训练一个 LDA 模型,将短消息分为垃圾类或非垃圾类 import pandas as pd from nlpia.data.loaders import get_data from sklearn.feature_extraction.text import TfidfVectorizer from nltk.tokenize.casual import casual_tokenize from sklearn.preprocessing import MinMaxScaler from pugnlp.stats import Confusion # 导入垃圾短消息数据集 pd.options.display.width = 120 # 数据集中有 4837 条短消息 sms = get_data('sms-spam') # <class 'pandas.core.frame.DataFrame'> print(sms) # 通过添加感叹号!标注垃圾短消息 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) sms['spam'] = sms.spam.astype(int) print(len(sms)) # 638 条被标注为二类标签“spam”(垃圾类) print(sms.spam.sum()) print(sms.head(6)) # 构造TF-IDF 向量 tfidf_model = TfidfVectorizer(tokenizer=casual_tokenize) tfidf_docs = tfidf_model.fit_transform(raw_documents=sms.text).toarray() print("tfidf_docs.shape\n", tfidf_docs.shape) print(sms.spam.sum()) ''' LDA 的一个简单的实现: (1)计算某个类(如垃圾短消息类)中所有 TF-IDF 向量的平均位置(质心)。 (2)计算不在该类(如非垃圾短消息类)中的所有 TF-IDF 向量的平均位置(质心)。 (3)计算上述两个质心之间的向量差(即连接这两个向量的直线) ''' # 计算两个类(垃圾类和非垃圾类)的质心 mask = sms.spam.astype(bool).values # 仅返回垃圾类的行 # TF-IDF 向量是行向量,确保 numpy 使用 axis=0 独立计算每一列的平均值 spam_centroid = tfidf_docs[mask].mean(axis=0) # 仅返回非垃圾类的行 ham_centroid = tfidf_docs[~mask].mean(axis=0) print(spam_centroid.round(2)) print(ham_centroid.round(2)) # 用一个质心向量减去另一个质心向量从而得到分类线 # 点积计算的是每个TF-IDF向量在质心连线上的“阴影”投影 spamminess_score = tfidf_docs.dot(spam_centroid - ham_centroid) # 非垃圾类质心将它们投影到质心的连线上时,我们可能会得到一个负的垃圾信息评分 print(spamminess_score.round(2)) # 使上述评分就像概率那样取值在 0 到 1 之间 # MinMaxScaler:归一到 [ 0,1 ] sms['lda_score'] = MinMaxScaler().fit_transform(spamminess_score.reshape(-1,1)) sms['lda_predict'] = (sms.lda_score > .5).astype(int) # 当将阈值设置为 50%时,前 6 条消息都被正确分类。 print(sms['spam lda_predict lda_score'.split()].round(2).head(6)) # 计算精确度 print((1. - (sms.spam - sms.lda_predict).abs().sum() / len(sms)).round(3)) # 看看训练集上的混淆矩阵: # 给出了标注为垃圾但是根本不是垃圾的消息(假阳), # 也给出了标注为非垃圾但是本应该标注为垃圾的消息(假阴) # 假阳(64)、假阴(45) ''' lda_predict 0 1 spam 0 4135 64 1 45 593 ''' print(Confusion(sms['spam lda_predict'.split()]))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。