之前学习的LSA方法都没有考虑文档之间的相似度信息,创建的主题对一组通用规则来说是最优的。在这些特征(主题)提取模型的无监督学习中,没有任何关于主题向量之间应该多么接近的数据。我们也不允许任何关于主题向量在哪里结束或者它们之间相关性如何的反馈。引导或者"习得型距离指标"是在降维和特征提取方面的最新进展。通过调整向聚类和嵌入算法报告的距离分数,我们可以控制自己的向量,从而让它们使一些代价函数最小化。通过这种方式,可以"强制"向量专注于我们感兴趣的信息内容的某个方面。
线性判别分析
下面是在一个标注好的短消息数据集上训练一个线性判别分析(LDA)模型。LDA的工作原理与LSA类似,但是它需要分类标签或其他分数才能找到高维空间中维度(词袋或TF-IDF向量中的词项)的最佳线性组合。LDA没有最大化新空间中所有向量之间的分离程度(方差),而是最大化了每个类质心向量之间的距离。
但是,这意味着必须通过给出样例(标注好的向量)来告诉LDA算法想对哪些主题建模。只有这样,算法才能计算出从高维空间到低维空间的最优转换。得到的低维向量的维数不能超过所能提供的类标签或分数的数量。
因为只需要训练一个"垃圾性"主题,下面看一维主题模型在垃圾短消息分类方面能达到多高的精确率:
python
from nlpia.data.loaders import get_data
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from nltk.tokenize import casual_tokenize
sms=get_data('sms-spam')
index=['sms{}{}'.format(i,'!'*j) for (i,j) in zip(range(len(sms)),sms.spam)]
sms.index=index
tfidf=TfidfVectorizer(tokenizer=casual_tokenize)
tfidf_docs=tfidf.fit_transform(raw_documents=sms.text).toarray()
tfidf_docs=tfidf_docs-tfidf_docs.mean(axis=0)
lda=LDA(n_components=1)
lda=lda.fit(tfidf_docs,sms.spam)
sms['lda_spaminess']=lda.predict(tfidf_docs)
print(((sms.spam-sms.lda_spaminess)**2.0).sum()**0.5)
print((sms.spam==sms.lda_spaminess).sum())
print(len(sms))
可以看到,精确率为100%。在TF-IDF向量中有10000个词项,它可以"记住"答案。
下面做一些交叉验证:
python
from sklearn.model_selection import cross_val_score
lds=LDA(n_components=1)
scores=cross_val_score(lda,tfidf_docs,sms.spam,cv=5)
print('精确率:',scores.mean(),scores.std()*2)
可以看到,效果并不好。为了确保76%的精确率数值是正确的,下面保留1/3的数据集用于测试:
python
from sklearn.model_selection import train_test_split
X_train,X_text,y_train,y_test=train_test_split(tfidf_docs,sms.spam,test_size=0.33,random_state=271828)
lda=LDA(n_components=1)
lda.fit(X_train,y_train)
print(lda.score(X_text,y_test).round(3))
同样的,测试集的精确率也很低。因此,并不像是数据抽样做的不好,这是一个糟糕的过拟合模型。
下面看LSA和LDA结合起来是否有助于创建一个精确的、泛化能力强的模型,这样面对新的短消息时就不会出错:
python
import pandas as pd
from sklearn.decomposition import PCA
pca=PCA(n_components=16)
pca=pca.fit(tfidf_docs)
pca_topicvectors=pca.transform(tfidf_docs)
columns=['topic{}'.format(i) for i in range(pca.n_components)]
pca_topicvectors=pd.DataFrame(pca_topicvectors,columns=columns,index=index)
X_train,X_text,y_train,y_test=train_test_split(pca_topicvectors.values,sms.spam,test_size=0.3,random_state=271828)
lda=LDA(n_components=1)
lda.fit(X_train,y_train)
print(lda.score(X_text,y_test).round(3))
lda=LDA(n_components=1)
scores=cross_val_score(lda,pca_topicvectors,sms.spam,cv=10)
print('精确率:',scores.mean())
因此,通过使用LSA,我们可以刻画一个只有16个维度的短消息,并且仍然有足够的信息将它们分类为垃圾信息或非垃圾信息。低维模型不太可能过拟合,它应该有很好的泛化能力,并且能够对尚未看到的短消息或聊天信息进行分类。
在尝试所有这些语义分析之前,使用简单的LDA模型可以获得更高的精确率,这个新模型的优点是:现在可以在多于一个的维度上创建表示语句语义的向量。