【Python机器学习】NLP词频背后的含义——隐性狄利克雷分布(LDiA)

本文主要是介绍【Python机器学习】NLP词频背后的含义——隐性狄利克雷分布(LDiA),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

LDiA思想

基于LDiA主题模型的短消息语义分析

LDiA+LDA=垃圾消息过滤器

更公平的对比:32个LDiA主题


对于大多数主题建模、语义搜索或基于内容的推荐引擎来说,LSA应该是首选方法。它的数学机理直观、有效,它会产生一个线性变换,可以应用于新来的自然语言文本而不需要训练过程,并几乎不会损失精确率。但是,在某些情况下,LDiA可以给出稍好的结果。

LDiA和LSA(已经底层的SVD)一样做了很多创建主题建模的工作,但是与LSA不同的是,LDiA假设词频满足狄利克雷分布。相对于LSA的线性数学,LDiA则更精确的给出了将词赋给主题的统计信息。

LDiA创建了一个语义向量空间模型,根据词在同一文档中的共现频率将它们分配给主题,然后,文档的主题混合可以由每个主题中的词的混合结果来确定,而这些词被分配给每个主题中。这使得LDiA主题模型更容易理解,因为分配给主题的词以及分配给文档的主题往往比LSA更有意义。

LDiA假设每篇文档都由某个任意数量的主题混合(线性组合)而成,该数量是在开始训练LDiA模型时选择的。LDiA还假设每个主题都可以用词的分布(词项频率)来表示。文档中每个主题的概率或权重,以及某个词被分配到一个主题的概率,都假定一开始满足狄利克雷概率分布(先验)。这就是该算法得名的来历。

LDiA思想

Blei和Ng通过类似思想实验提出了LDiA思想。他们设想,一台只能掷骰子(生成随机数字)的机器如何能写出语料库中的文档。由于我们仅基于词袋进行统计,因此在编写一篇真正的文档时,他们去掉了词序对文档语义的影响,他们只对词的混合统计数据进行建模,而这些词混合构成了每篇文档的词袋。

他们设想有一台机器,该机器只有两个选择项来开始生成特定文档的词混合结果。他们设想文档生成器会以某种概率分布来随机选择这些词,就像选择骰子的边数然后将骰子的组合情况加在一起创建一个D&D人物卡。我们的“人物卡”只需要骰子的两轮投掷过程。但是骰子本身很大,而且有好几个,关于如何组合它们来为不同的值生成所需的概率,有十分复杂的规则。我们希望词的数量和主题的数量有特定的概率分布,这样它们就可以匹配待分析的真实文档中的词和主题的分布。

骰子的两轮投掷过程分别代表:

  1. 生成文档的词的数量(泊松分布)
  2. 文档中混合的主题的数量(狄利克雷分布)

有了上面两个数值之后,就会遇到较难的部分,也就是要为文档选择词。设想的词袋生成机会在这些主题上迭代,并随机选择适合该主题的词,直到达到文档应该包含的词数量为止。确定这些词对应主题的概率(每个主题的词的适宜度)是比较困难的。但是一旦确定,“机器人”就会从一个词项-主题概率矩阵中查找每个主题的词的概率。

因此,这台机器只需要一个泊松分布的参数来告诉它文档的平均长度有多长,以及另外两个参数来定义设置主题数的狄利克雷分布。然后,文档生成算法需要文档喜欢使用的所有词和主题组成的词项-主题矩阵,并且,它还需要喜欢谈论的一个主题混合。

下面将文档生成问题转回最初的问题,即从现有文档中估算主题和词。我们需要为前两个步骤测算或计算关于词和主题的那些参数。然后需要从一组文档中计算出词项-主题矩阵,这就是LDiA所做的事情。

Blei和Ng意识到,他们可以通过分析语料库中文档的统计数据来确定步骤1和步骤2的参数。例如步骤1,可以计算出语料库中文档的所有词袋中的平均词(或n-gram)数量:

import pandas as pd
from nlpia.data.loaders import get_data
from nltk.tokenize import casual_tokenize
pd.options.display.width=120sms=get_data('sms-spam')
total_corpus_len=0
for document_text in sms.text:total_corpus_len=total_corpus_len+len(casual_tokenize(document_text))
mean_document_len=total_corpus_len/len(sms)
print(round(mean_document_len,2))

还可以通过下面这行代码求解:

print(sum([len(casual_tokenize(t)) for t in sms.text])*1.0/len(sms.text))

大家应该直接从词袋来计算这个统计数据,我们需要确保正在对文档中的已分词和已向量化的词计数,并确保在对独立词项进行计数之前,已应用任一停用词过滤器或其他归一化方法。这样的话,我们的计数就不仅包括词袋向量词汇表中的所有词(正在计数的所有n-gram),而且包括词袋使用的那些词(如非停用词)。LDiA算法也依赖一个词袋向量空间模型。

设定LDiA模型所需要的第二个参数即主题的数量更加棘手。在一组特定的文档中,只有在为这些主题分配了词之后,才能直接得到主题的数量。就像KNN、k均值以及其他的聚类算法一样,我们必须提取设定k的值。我们可以猜测主题的数量(类似于k均值中的k,即簇的数量),然后检查这是否适用于这组文档。一旦设定好LDiA要寻找的主题数量,它就会找到要放入每个主题中的词的混合结果,从而优化其目标函数。

我们可以通过调整这个“超参数”(k,即主题的数量)来对其进行优化,直到它适合我们的应用为止。如果能够度量表示文档含义的LDiA语言模型的质量,就可以自动化上述优化过程。可以用于词优化的一个代价函数是,LDI模型在某些分类或回归问题(如情绪分析、文档关键词标注或主题分析)中的表现如何。我们只需要一些带标签的文档来测试主题模型或分类器。

基于LDiA主题模型的短消息语义分析

LDiA生成的主题对人类来说更容易理解和解释。这是因为经常一起出现的词被分配给相同的主题,而人类的期望也是如此。

这听起来好像是一回事,但事实并非如此。其背后的数学子啊优化不同的东西,优化器有不同的目标函数,因此它将达到一个不同的目标。为了让接近的高维空间向量在低维空间中继续保持接近,LDiA必须以非线性的方式变换(扭转和扭曲)空间(向量)。这种过程很难实现可视化,除非在某个三维空间上执行上述操作并将结果向量投影到二维空间。

以nlpia中的horse为例,我们可以为horse中的数千个点创建词-文档向量,方法是将它们转换为词x、y、z(即三维空间向量的维数)上的整数计数结果。然后,可以从这些计数生成人造文档,并将它们传递给LDiA和LSA示例。然后,我们就可以直接实现上述每一种方法产生马的不同的二维影子(投影)的过程的可视化。

下面来看对于一个包含数千条短消息的数据集(按照是否垃圾来标记)上述方法的应用过程。首先计算TF-IDF向量,然后为每个短消息计算一些主题向量。我们假设只使用16个主题来对垃圾短消息进行分类。保持主题(维度)的数量较低有助于减少过拟合的可能性。

LDiA使用原始词袋词频向量,而不是归一化的TF-IDF向量。这里有一个简单的方法在scikit-learn中计算词袋向量:

from sklearn.feature_extraction.text import CountVectorizer
from nltk.tokenize import casual_tokenize
import numpy as np
import pandas as pd
from nlpia.data.loaders import get_datasms=get_data('sms-spam')
index=['sms{}{}'.format(i,'!'*j) for (i,j) in zip(range(len(sms)),sms.spam)]
sms.index=indexnp.random.seed(42)
counter=CountVectorizer(tokenizer=casual_tokenize)
bow_docs=pd.DataFrame(counter.fit_transform(raw_documents=sms.text).toarray(),index=index)
column_nums,terms=zip(*sorted(zip(counter.vocabulary_.values(),counter.vocabulary_.keys())))
bow_docs.columns=terms#检查一下,看这里的词频是否对标记为"sms0"的第一条短消息有意义
print(sms.loc['sms0'].text)
print(bow_docs.loc['sms0'][bow_docs.loc['sms0']>0].head())

下面给出了如何使用LDiA为短消息语料库创建主题向量的过程:

from sklearn.decomposition import LatentDirichletAllocation as LDiA
ldia=LDiA(n_components=16,learning_method='batch')
ldia=ldia.fit(bow_docs)
print(ldia.components_.shape)

因此,上述模型已经将9232个词(词项)分配给16个主题(成分)。下面看开头的几个词,了解一下它们是如何分配到16个主题的。LDiA是一种随机算法,它依赖随机数生成器做出一些统计决策来为主题分配词,所以每次运行sklearn.LatentDirichletAllocation,如果随机种子没有设定为固定值,都将获得不一样的结果:

pd.set_option('display.width',75)
components=pd.DataFrame(ldia.components_.T,index=terms,columns=columns)
print(components.round(2).head(3))

因此,感叹号(!)被分配到大多数主题中,但它其实是topic3的特别重要的部分,在该主题中,引号几乎不起作用。或许topic3关注情感的强度或强调,并不太在意数值或引用。

print(components.topic3.sort_calues(ascending=False)[:10])

可以看到,该主题的前10个词似乎是在要求某人做某事或支付某事的强调指令中可能使用的词类型。如果该主题更多使用在垃圾信息而不是非垃圾信息的话,那么这个发现很特别哦我们可以看到,即使这样粗略浏览一下,也可以对主题的词分配进行合理化解释或推理。

在拟合LDA分类器之前,需要为所有文档(短消息)计算出LDiA主题向量。下面看一下这些向量与SVD及PCA为相同文档生成的主题向量的不同:

ldia16_topic_vectors=ldia.transform(bow_docs)
ldia16_topic_vectors=pd.DataFrame(ldia16_topic_vectors,index=index,columns=columns)
print(ldia16_topic_vectors.round(2).head())

可以看到,上述主题之间分隔得更清晰,在为消息分配主题时,会出现很多0。在基于NLP流水线结果做出业务决策时,这是使LDiA主题更容易向同伴解释的做法之一。

LDiA+LDA=垃圾消息过滤器

下面观察这些LDiA主题在预测时的有效性,再次使用LDiA主题向量来训练LDA模型:

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.model_selection import train_test_split
X_train,X_text,y_train,y_test=train_test_split(ldia16_topic_vectors,sms.spam,test_size=0.5,random_state=271828)
lda=LDA(n_components=1)
lda=lda.fit(X_train,y_train)
sms['ldia16_spam']=lda.predict(ldia16_topic_vectors)
print(round(float(lda.score(X_text,y_test)),2))

train_test_split和LDiA的算法是随机的,所以如果不设置特定种子,每次运行会得到不同的结果和不同的精确率。

共线警告可能发生的一种情况是:如果文本包含一些2-gram或3-gram,其中组成他们的词只出现在这些2-gram或3-gram中。因此,最终的LDiA模型必须在这些相等的词项频率之间任意分配权重。当在短消息中导致共线性的词出现时,另一个词(它的配对)总是在相同的消息中。

我们可以使用Python而不是手工进行搜索。首先,我们可能只想在语料库中寻找任何相同的词袋向量。这些向量可能出现在不完全相同的短消息中,因为它们有相同的出现频率。我们可以遍历所有词袋对,以寻找相同的向量。这些向量肯定会在LDiA或LSA中引发共线警告。

如果没有找到任何词袋向量的精确副本,那么可能遍历词汇表中所有的词对。然后遍历所有的词袋,以寻找包含完全相同的词对的短消息。如果这些词在短消息中没有单独出现过,那么已经在数据集中找到了一个“共线”。一些常见的2-gram(比如英文人名)可能会导致这种情况,而且从来没有分开使用过,例如“Bill Gates”。

我们在测试集上获得的精确率超过90%,而且只需要在一半的可用数据上进行训练。但是,由于数据集有限,我们确实得到了关于特征共线的警告,这给LDA带来了一个待确定问题。一旦使用train_test_split丢弃了一半的文档,那么主题-文档矩阵的行列式就接近于零。如果需要的话,可以关闭LDiA n_components来解决这个问题,但是它往往会将这些主题组合在一起,而这些主题是彼此的线性组合(共线)。

但是,我们看一下这里的LDiA模型与基于TF-IDF向量的高维模型相比如何。TF-IDF向量有更多的特征(超过3000个独立的词项),所以很可能会遇到过拟合和弱泛化问题,这就是LDiA和PCA泛化的用武之地:

from nlpia.data.loaders import get_datasms=get_data('sms-spam')
index=['sms{}{}'.format(i,'!'*j) for (i,j) in zip(range(len(sms)),sms.spam)]
sms.index=indexfrom sklearn.feature_extraction.text import TfidfVectorizer
from nltk.tokenize.casual import casual_tokenize
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.model_selection import train_test_split
tfidf=TfidfVectorizer(tokenizer=casual_tokenize)
tfidf_docs=tfidf.fit_transform(raw_documents=sms.text).toarray()
tfidf_docs=tfidf_docs-tfidf_docs.mean(axis=0)
X_train,X_text,y_train,y_test=train_test_split(tfidf_docs,sms.spam.values,test_size=0.5,random_state=271828)
lda=LDA(n_components=1)
lda=lda.fit(X_train,y_train)
print(round(float(lda.score(X_train,y_train)),3))
print(round(float(lda.score(X_text,y_test)),3))

在训练集上基于TF-IDF的模型的精确率是完美的,但是,当使用低维主题向量而不是TF-IDF向量训练时,测试集上的精确率要低很多。

测试集的精确率是唯一重要的精确率。这正是主题建模(LSA)应该做的,它可以帮助我们从一个小型训练集中泛化出模型,因此它仍然可以很好地处理使用不同词组合的消息。

更公平的对比:32个LDiA主题

下面使用更多的维度和更多的主题。也许LDiA不如LSA(PCA)高效,所以它需要更多的主题来分配词,比如32个主题(成分):

from nlpia.data.loaders import get_data
from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.tokenize.casual import casual_tokenize
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.model_selection import train_test_split
from sklearn.decomposition import LatentDirichletAllocation as LDiA
from sklearn.feature_extraction.text import CountVectorizer
from nltk.tokenize import casual_tokenize
import numpy as np
import pandas as pdsms=get_data('sms-spam')
index=['sms{}{}'.format(i,'!'*j) for (i,j) in zip(range(len(sms)),sms.spam)]
sms.index=indexnp.random.seed(42)
counter=CountVectorizer(tokenizer=casual_tokenize)
bow_docs=pd.DataFrame(counter.fit_transform(raw_documents=sms.text).toarray(),index=index)
column_nums,terms=zip(*sorted(zip(counter.vocabulary_.values(),counter.vocabulary_.keys())))
bow_docs.columns=termsldia32=LDiA(n_components=32,learning_method='batch')
ldia32=ldia32.fit(bow_docs)
print(ldia32.components_.shape)

下面计算所有文档(短消息)的新的32维主题向量:

ldia32_topic_vectors=ldia32.transform(bow_docs)
columns32=['topic{}'.format(i) for i in range(ldia32.n_components)]
ldia32_topic_vectors=pd.DataFrame(ldia32_topic_vectors,index=index,columns=columns32)
print(ldia32_topic_vectors.round(2).head())

我们可以看到在,这些主题更加稀疏,而且能够更加清晰的分开。

下面是LDA模型(分类器)的训练过程,这次使用32维的LDiA主题向量:

X_train,X_text,y_train,y_test=train_test_split(ldia32_topic_vectors,sms.spam.values,test_size=0.5,random_state=271828)
lda=LDA(n_components=1)
lda=lda.fit(X_train,y_train)
sms['ldia32_spam']=lda.predict(ldia32_topic_vectors)
print(X_train.shape)
print(round(float(lda.score(X_train,y_train)),3))
print(round(float(lda.score(X_text,y_test)),3))

不要将这里“主题”或成分数量的优化与前面的共线性问题混淆。增加或减少主体的数量并不能解决或造成共线问题。这是底层数据造成的问题。如果想要摆脱这个警告,那么需要将“噪声”或元数据以人造词的方式添加到短消息中,或者需要删除那些重复的词向量。如果文档中有重复出现多次的词向量或词对,那么主题的数量优化也无法解决这个问题。

主题的数量越多,那么主题的精确率就可以越高,至少对这个数据集来货,产品这一主题线性分隔得更好。但是这里的效果仍然不如PCA+LDA的96%的精确率。因此,PCA能使这里的短消息主题向量更有效的展开,这样就允许时候用超平面以更大的消息间隔来分隔类。

这篇关于【Python机器学习】NLP词频背后的含义——隐性狄利克雷分布(LDiA)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122642

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识