Python 机器学习算法交易实用指南(四)(4)https://developer.aliyun.com/article/1523386
生成模型
狄利克雷分布在 LDA 主题模型中占据重要地位,该模型假设当作者将文章添加到一系列文档中时,存在以下生成过程:
- 根据主题概率随机混合一小部分共享主题 K
- 对于每个词语,根据文档-主题概率选择其中一个主题
- 根据主题-词语概率从主题的词语列表中选择一个词语
因此,文章内容取决于每个主题的权重和构成每个主题的术语。狄利克雷分布控制着为文档选择主题以及为主题选择词语,并编码了这样一个观点:文档仅涵盖少数主题,而每个主题仅使用少量词语频繁出现。
LDA 模型的板符号总结了这些关系:
反向工程过程
生成过程是虚构的,但事实证明它是有用的,因为它允许恢复各种分布。LDA 算法对虚构作者的工作进行了反向工程,并得出了对文档-主题-词语关系进行简洁描述的摘要,该描述包括以下内容:
- 每个主题对文档的贡献百分比
- 每个词语与主题的概率关联
LDA 通过对假设的内容生成过程进行反向工程来解决从文档集合及其包含的词语中恢复分布的贝叶斯推理问题。原始论文使用变分贝叶斯(VB)来近似后验分布。其他选择包括吉布斯抽样和期望传播。稍后,我们将演示使用 sklearn 和 gensim 库的实现。
如何评估 LDA 主题
无监督主题模型不能保证结果具有意义或可解释性,并且没有客观的度量标准来评估结果,就像监督学习中那样。人类主题评估被认为是金标准,但可能成本高昂,并且不易规模化。
两种更客观评估结果的选择包括困惑度,它评估模型对未见文档的表现,以及主题连贯性度量,它旨在评估发现的模式的语义质量。
困惑度
当应用于 LDA 时,困惑度衡量模型恢复的主题 - 单词概率分布对样本的预测能力,例如,未见过的文本文档。 它基于此分布 p 的熵 H(p) 并针对标记集 w 计算:
接近零的度量意味着分布更能够预测样本。
主题连贯性
主题连贯性衡量主题模型结果的语义一致性,即人类是否会认为与主题相关的单词及其概率是有意义的。
为此,它通过衡量与主题最相关的单词之间的语义相似度来评分每个主题。 更具体地说,连贯性度量基于定义主题的单词集合 W 的观察概率。
我们使用两个为 LDA 设计且已经显示与人类对主题质量的判断相吻合的连贯性度量,即 UMass 和 UCI 度量。
UCI 指标定义了一个词对的分数,该分数是两个不同对(顶部)主题词 w[i],w[j] ∈ w 的点间互信息(PMI)之和和一个平滑因子 ε:
概率是从一个外部语料库(如维基百科)上的单词共现频率中计算的,因此这个度量可以被认为是与语义基准的外部比较。
相比之下,UMass 指标使用训练语料库中的一些文档 D 中的共现来计算一致性分数:
与外部基准的比较不同,这个度量反映了内在的连贯性。 这两个度量都经过评估,与人类判断相吻合。 在这两种情况下,值越接近零意味着主题更连贯。
如何使用 sklearn 实现 LDA
与之前使用 BBC 数据不同,我们使用 sklearn.decomposition.LatentDirichletAllocation 来训练一个具有五个主题的 LDA 模型(有关参数的详细信息,请参阅 sklearn 文档,有关实现细节,请参阅笔记本 lda_with_sklearn):
lda = LatentDirichletAllocation(n_components=5, n_jobs=-1, max_iter=500, learning_method='batch', evaluate_every=5, verbose=1, random_state=42) ldat.fit(train_dtm) LatentDirichletAllocation(batch_size=128, doc_topic_prior=None, evaluate_every=5, learning_decay=0.7, learning_method='batch', learning_offset=10.0, max_doc_update_iter=100, max_iter=500, mean_change_tol=0.001, n_components=5, n_jobs=-1, n_topics=None, perp_tol=0.1, random_state=42, topic_word_prior=None, total_samples=1000000.0, verbose=1)
模型在训练期间跟踪样本内困惑度,并一旦该度量停止改善,就停止迭代。 我们可以像往常一样保存和加载 sklearn 对象的结果:
joblib.dump(lda, model_path / 'lda.pkl') lda = joblib.load(model_path / 'lda.pkl')
如何使用 pyLDAvis 可视化 LDA 结果
主题可视化利用人类判断方便评估主题质量。 pyLDAvis 是 LDAvis 的 Python 版本,是在 R 和 D3.js 中开发的。我们将介绍关键概念;每个 LDA 实现笔记本都包含示例。
pyLDAvis 显示了话题之间的全局关系,同时通过检查与每个话题最相关的术语以及与每个术语相关的话题来促进它们的语义评估。它还解决了语料库中频繁出现的术语倾向于主导定义话题的词的多项分布的挑战。LDAVis 引入了术语w对话题t的相关性r,以使用权重参数0<=ƛ<=1生成关键术语的灵活排名。
以作为模型对观察到的术语w在话题t中的概率估计,并作为语料库中术语w的边际概率:
第一个术语衡量了术语t与话题w的关联程度,第二个术语衡量了提升或显著性,即术语在话题中出现的可能性相对于语料库中的可能性有多少。
话题 14
该工具允许用户交互地更改ƛ以调整相关性,从而更新术语的排名。用户研究发现ƛ=0.6产生了最合理的结果。
如何使用 gensim 实现 LDA
gensim是一个专门的 NLP 库,具有快速的 LDA 实现和许多附加功能。我们还将在下一章中使用它来处理单词向量(有关详细信息,请参见latent_dirichlet_allocation_gensim笔记本)。
它促进了将 sklearn 生成的 DTM 转换为 gensim 数据结构,如下所示:
train_corpus = Sparse2Corpus(train_dtm, documents_columns=False) test_corpus = Sparse2Corpus(test_dtm, documents_columns=False) id2word = pd.Series(vectorizer.get_feature_names()).to_dict()
Gensim LDA 算法包括许多设置,如下所示:
LdaModel(corpus=None, num_topics=100, id2word=None, distributed=False, chunksize=2000, # No of doc per training chunk. passes=1, # No of passes through corpus during training update_every=1, # No of docs to be iterated through per update alpha='symmetric', eta=None, # a-priori belief on word probability decay=0.5, # % of lambda forgotten when new doc is examined offset=1.0, # controls slow down of first few iterations. eval_every=10, # how often estimate log perplexity (costly) iterations=50, # Max. of iterations through the corpus gamma_threshold=0.001, # Min. change in gamma to continue minimum_probability=0.01, # Filter topics with lower probability random_state=None, ns_conf=None, minimum_phi_value=0.01, # lower bound on term probabilities per_word_topics=False, # Compute most word-topic probabilities callbacks=None, dtype=<class 'numpy.float32'>)
Gensim 还提供了一个用于并行训练的LdaMulticore模型,可以使用 Python 的多进程特性加速训练。
模型训练只需要实例化LdaModel对象,如下所示:
lda = LdaModel(corpus=train_corpus, num_topics=5, id2word=id2word)
话题连贯性(Topic coherence)衡量话题中的单词是否倾向于一起出现。它为每对排名靠前的单词添加一个分数。该分数是包含至少一个较高排名单词实例的文档也包含至少一个较低排名单词实例的概率的对数。
大的负值表示不经常共现的单词;接近零的值表示单词倾向于更经常共现。gensim允许进行话题连贯性评估,产生话题连贯性并显示每个话题的最重要单词:
coherence = lda_gensim.top_topics(corpus=train_corpus, coherence='u_mass')
我们可以如下显示结果:
topic_coherence = [] topic_words = pd.DataFrame() for t in range(len(coherence)): label = topic_labels[t] topic_coherence.append(coherence[t][1]) df = pd.DataFrame(coherence[t][0], columns=[(label, 'prob'), (label, 'term')]) df[(label, 'prob')] = df[(label, 'prob')].apply(lambda x: '{:.2%}'.format(x)) topic_words = pd.concat([topic_words, df], axis=1) topic_words.columns = pd.MultiIndex.from_tuples(topic_words.columns) pd.set_option('expand_frame_repr', False) print(topic_words.head()) pd.Series(topic_coherence, index=topic_labels).plot.bar();
这显示了每个话题的以下顶级单词:
| 话题 1 | 话题 2 | 话题 3 | 话题 4 | 话题 5 | |||||
| 概率 | 术语 | 概率 | 术语 | 概率 | 术语 | 概率 | 术语 | 概率 | 术语 |
| 0.55% | 在线 | 0.90% | 最佳 | 1.04% | 移动 | 0.64% | 市场 | 0.94% | 劳动力 |
| 0.51% | 网站 | 0.87% | 游戏 | 0.98% | 手机 | 0.53% | 增长 | 0.72% | 布莱尔 |
| 0.46% | 游戏 | 0.62% | 玩 | 0.51% | 音乐 | 0.52% | 销售 | 0.72% | 褐色 |
| 0.45% | 净 | 0.61% | 赢 | 0.48% | 影片 | 0.49% | 经济 | 0.65% | 选举 |
| 0.44% | 用 | 0.56% | 赢 | 0.48% | 使用 | 0.45% | 价格 | 0.57% | 联合 |
以及相应的一致性分数,突出了主题质量的衰减(至少部分原因是由于相对较小的数据集):
主题质量的衰减
收益电话的主题建模
在 第三章 中,金融的替代数据,我们学习了如何从 SeekingAlpha 网站上抓取收益电话数据。在本节中,我们将使用此来源说明主题建模。我使用了 2018 年下半年的约 500 个收益电话转录样本。对于实际应用,更大的数据集将非常理想。 earnings_calls 目录 包含了几个文件,后面提到了一些示例。
有关加载、探索和预处理数据的详细信息,以及训练和评估各个模型的详细信息,请参阅 lda_earnings_calls 笔记本,以及描述这里的实验的 run_experiments.py 文件。
数据预处理
收益电话的转录由公司代表、运营商和通常与分析师进行的问答环节组成。我们将每个声明视为单独的文档,忽略运营商的声明,以获取 22,766 个项目,平均词数为 144 个,中位数为 64 个:
documents = [] for transcript in earnings_path.iterdir(): content = pd.read_csv(transcript / 'content.csv') documents.extend(content.loc[(content.speaker!='Operator') & (content.content.str.len() > 5), 'content'].tolist()) len(documents) 22766
我们使用 spaCy 对这些文档进行预处理,如 第十三章 所示,处理文本数据(参见笔记本),并将清理和词形还原的文本存储为新的文本文件。
数据探索揭示了领域特定的停用词,例如年份和季度,我们在第二步中移除这些停用词,我们还过滤掉少于十个单词的语句,以便剩下约 16,150 个语句。
模型训练和评估
为了说明,我们将创建一个包含在大约 1560 个特征中出现在 0.5% 到 50% 文档之间的术语的文档-术语矩阵。在四核 i7 上对语料库进行 25 次训练 15 个主题模型需要花费两分钟多一点的时间。
每个主题的前 10 个单词确定了几个不同的主题,从明显的财务信息到临床试验(主题 4)和供应链问题(12):
使用 pyLDAvis 的相关度指标,将无条件频率相对于提升的 0.6 加权,主题定义变得更直观,如主题 14 关于销售业绩的示例所示:
主题 14 的销售业绩
笔记本还说明了如何根据其主题关联查找文档。在这种情况下,分析师可以审查相关声明以获取细微差别,使用情感分析进一步处理特定主题的文本数据,或者分配从市场价格中导出的标签。
运行实验
为了说明不同参数设置的影响,我们对不同 DTM 约束和模型参数进行了几百次实验。更具体地说,我们让 min_df 和 max_df 参数从 50 到 500 个词和 10% 到 100% 的文档,分别使用二进制和绝对计数。然后,我们使用 3 到 50 个主题训练了 LDA 模型,在语料库上进行了 1 到 25 次训练。
以下图表说明了主题一致性(越高越好)和困惑度(越低越好)的结果。一致性在 25-30 个主题后下降,困惑度同样增加:
该笔记本包含了回归结果,量化了参数和结果之间的关系。通常使用绝对计数和较小的词汇量可以获得更好的结果。
Yelp 商业评论的主题建模
lda_yelp_reviews 笔记本包含了将 LDA 应用于 Yelp 上的六百万商业评论的示例。评论的长度比从盈利电话会议抄录中提取的语句更加一致。在清理后,第 10 和第 90 百分位数的范围从 14 到 90 个词元。
我们展示了一个模型的结果,该模型使用了基于 min_df=0.1% 和 max_df=25% 的 3,800 个词元的词汇表,使用单一传递以避免对 20 个主题进行漫长的训练时间。我们可以使用 pyldavis topic_info 属性来计算 lambda=0.6 的相关性值,从而生成以下单词列表(详细信息请参见笔记本):
Gensim 提供了 LdaMultiCore 实现,允许使用 Python 的 multiprocessing 模块进行并行训练,并且当使用四个工作进程时,性能提高了 50%。然而,更多的工作进程并不会进一步减少训练时间,因为存在 I/O 瓶颈。
摘要
在本章中,我们探讨了使用主题建模来洞察大量文档内容的用途。我们涵盖了潜在语义分析,它使用 DTM 的降维来将文档投影到潜在主题空间中。虽然在处理由高维词向量引起的维度诅咒方面有效,但它并不捕捉太多语义信息。概率模型对文档、主题和词之间的相互作用做出了明确的假设,这些假设允许算法反向工程文档生成过程,并评估新文档的模型适应度。我们看到 LDA 能够提取出合理的主题,使我们能够以自动化的方式对大量文本进行高层次理解,同时以有针对性的方式识别相关文档。
在下一章中,我们将学习如何训练神经网络,将个别单词嵌入到一个捕捉重要语义信息的高维向量空间中,并且可以使用生成的词向量作为高质量的文本特征。