Python 机器学习算法交易实用指南(四)(3)https://developer.aliyun.com/article/1523385
条件独立性假设
使得模型既可行又有理由称其为朴素的假设是,特征在结果条件下是独立的。为了说明,让我们对一个包含三个词Send money now的电子邮件进行分类,这样贝叶斯定理就变成了以下形式:
形式上,三个词条件独立的假设意味着观察到send的概率不受其他词存在的影响,假设邮件是垃圾邮件;换句话说,P(send | money, now, spam) = P(send | spam)。因此,我们可以简化似然函数:
使用朴素条件独立性假设,分子中的每个项都可以直接从训练数据的相对频率中计算得出。分母在类别间是常数,当需要比较后验概率而不是校准时可以忽略。先验概率在因素数量增加(即特征)时变得不那么相关。
总之,朴素贝叶斯模型的优势在于训练和预测速度快,因为参数数量与特征数量成线性关系,它们的估计具有封闭形式的解(基于训练数据频率),而不是昂贵的迭代优化。它还直观且具有一定的可解释性,不需要超参数调整,并且在有足够信号的情况下相对不太受无关特征的影响。
然而,当独立假设不成立,文本分类依赖于特征的组合或特征相关时,模型的表现会很差。
新闻文章分类
我们从以前阅读的 BBC 文章中使用朴素贝叶斯模型对新闻文章进行分类的示例开始,以获得一个包含来自五个类别的 2,225 篇文章的 DataFrame:
RangeIndex: 2225 entries, 0 to 2224 Data columns (total 3 columns): topic 2225 non-null object heading 2225 non-null object body 2225 non-null object
训练和评估多项式朴素贝叶斯分类器
我们将数据拆分为默认的 75:25 训练-测试集,确保测试集类别与训练集类别密切相似:
y = pd.factorize(docs.topic)[0] # create integer class values X = docs.body X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1, stratify=y)
我们继续从训练集中学习词汇,并使用默认设置的CountVectorizer转换两个数据集,以获得近 26,000 个特征:
vectorizer = CountVectorizer() X_train_dtm = vectorizer.fit_transform(X_train) X_test_dtm = vectorizer.transform(X_test) X_train_dtm.shape, X_test_dtm.shape ((1668, 25919), (557, 25919))
训练和预测遵循标准的sklearn拟合/预测接口:
nb = MultinomialNB() nb.fit(X_train_dtm, y_train) y_pred_class = nb.predict(X_test_dtm)
我们使用准确性评估多类别预测,并发现默认分类器达到了近 98% 的准确率:
accuracy_score(y_test, y_pred_class) 0.97666068222621
情感分析
情感分析是自然语言处理和机器学习在交易中最受欢迎的用途之一,因为对资产或其他价格驱动因素的正面或负面观点可能会影响收益。
通常,情感分析的建模方法依赖于词典,例如TextBlob库,或者在特定领域的结果上训练的模型。后者更可取,因为它允许更有针对性的标记;例如,将文本特征与随后的价格变化联系起来,而不是间接的情感分数。
我们将使用具有二进制极性标签的 Twitter 数据集和具有五点结果量表的大型 Yelp 商业评论数据集说明情感分析的机器学习。
Twitter 数据
我们使用的数据集包含自 2009 年以来的 1.6 百万条训练推文和 350 条测试推文,这些推文具有经过算法分配的二进制正面和负面情感分数,分布相对均匀(有关更详细的数据探索,请参阅相关笔记本)。
多项式朴素贝叶斯
我们按照以下方式创建一个包含 934 个标记的文档-术语矩阵:
vectorizer = CountVectorizer(min_df=.001, max_df=.8, stop_words='english') train_dtm = vectorizer.fit_transform(train.text) <1566668x934 sparse matrix of type '<class 'numpy.int64'>' with 6332930 stored elements in Compressed Sparse Row format>
然后我们像以前一样训练MultinomialNB分类器,并预测测试集:
nb = MultinomialNB() nb.fit(train_dtm, train.polarity) predicted_polarity = nb.predict(test_dtm)
结果达到了超过 77.5% 的准确率:
accuracy_score(test.polarity, y_pred_class) 0.7768361581920904
与 TextBlob 情感分数的比较
我们还获取了推文的TextBlob情感分数,并注意到(请参见以下左侧图表),正面测试推文获得了明显更高的情感估计。然后我们使用MultinomialNB模型和.predict_proba()方法来计算预测概率,并使用相应的曲线下面积(请参见以下右侧图表)比较两个模型:
TextBlob 情感分数
在这种情况下,朴素贝叶斯模型优于TextBlob。
商业评论 - Yelp 数据集挑战
最后,我们将情感分析应用于规模更大的 Yelp 商业评论数据集,其中包含五个结果类别。数据由多个文件组成,其中包含关于企业、用户、评论和 Yelp 鼓励数据科学创新的其他方面的信息。
我们将使用在 2010 年至 2018 年期间产生的约六百万条评论(详见相关笔记本)。以下图表显示每年的评论数量和平均星级:
表示每年评论数量和平均星级的图表
除了评论文本产生的文本特征外,我们还将使用提交的评论或关于用户的其他信息。
我们将在 2017 年之前的数据上训练各种模型,并使用 2018 年作为测试集。
基准准确率
使用最常见的星级数(=5)来预测测试集,我们实现了接近 52%的准确率:
test['predicted'] = train.stars.mode().iloc[0] accuracy_score(test.stars, test.predicted) 0.5196950594793454
多项式朴素贝叶斯模型
接下来,我们使用具有默认设置的CountVectorizer生成的文档-术语矩阵来训练朴素贝叶斯分类器:
nb = MultinomialNB() nb.fit(train_dtm,train.stars) predicted_stars = nb.predict(test_dtm)
预测在测试集上产生了 64.7%的准确率,比基准提高了 24.4%:
accuracy_score(test.stars, predicted_stars) 0.6465164206691094
一对多逻辑回归
我们继续训练一对多逻辑回归,该模型对每个类别训练一个模型,同时将其余类别视为负类,并使用不同的模型预测每个类别的概率。
仅使用文本特征,我们按以下方式训练和评估模型:
logreg = LogisticRegression(C=1e9) logreg.fit(X=train_dtm, y=train.stars) y_pred_class = logreg.predict(test_dtm)
模型的准确率显著提高至 73.6%:
accuracy_score(test.stars, y_pred_class) 0.7360498864740219
结合文本和数值特征
数据集包含各种数值特征(有关实施细节,请参阅相关笔记本)。
向量化器生成scipy.sparse矩阵。要将向量化的文本数据与其他特征结合起来,我们首先需要将其转换为稀疏矩阵;许多 sklearn 对象和其他库(如 LightGBM)可以处理这些非常节省内存的数据结构。将稀疏矩阵转换为密集的 NumPy 数组会导致内存溢出的风险。
大多数变量都是分类变量,因此我们使用一位有效编码(one-hot encoding),因为我们有一个相当大的数据集来容纳特征的增加。
我们将编码的数值特征转换并与文档-术语矩阵相结合:
train_numeric = sparse.csr_matrix(train_dummies.astype(np.int8)) train_dtm_numeric = sparse.hstack((train_dtm, train_numeric))
多项式逻辑回归
逻辑回归还提供了一种多项式训练选项,比一对多实现更快且更准确。我们使用lbfgs求解器(有关详细信息,请参阅 GitHub 上链接的 sklearn 文档):
multi_logreg = LogisticRegression(C=1e9, multi_class='multinomial', solver='lbfgs') multi_logreg.fit(train_dtm_numeric.astype(float), train.stars) y_pred_class = multi_logreg.predict(test_dtm_numeric.astype(float))
此模型将性能提高至 74.6%的准确率:
accuracy_score(test.stars, y_pred_class) 0.7464488070176475
在这种情况下,调整正则化参数C并没有带来非常显著的改善(请参阅笔记本)。
梯度提升机
为了说明目的,我们还训练了一个具有默认设置和multiclass目标的 LightGBM 梯度提升树集成:
param = {'objective':'multiclass', 'num_class': 5} booster = lgb.train(params=param, train_set=lgb_train, num_boost_round=500, early_stopping_rounds=20, valid_sets=[lgb_train, lgb_test])
基本设置并没有改善多项式逻辑回归,但进一步的参数调整仍然是一个未使用的选项:
y_pred_class = booster.predict(test_dtm_numeric.astype(float)) accuracy_score(test.stars, y_pred_class.argmax(1) + 1) 0.738665855696524
摘要
在本章中,我们探讨了许多技术和选项来处理非结构化数据,目的是提取语义上有意义的数字特征,以供机器学习模型使用。
我们介绍了基本的标记化和注释流程,并使用 spaCy 和 TextBlob 在多种语言中说明了其实现方式。我们在这些结果的基础上构建了一个基于词袋模型的文档模型,以将文档表示为数值向量。我们学习了如何优化预处理流程,然后使用向量化的文本数据进行分类和情感分析。
在剩下的两章中,我们将学习如何使用无监督学习来总结文本,以识别潜在主题(在下一章中),并研究将单词表示为反映单词使用上下文的向量的技术,这些技术已经被成功地用于为各种分类任务提供更丰富的文本特征。
第十四章:主题建模
在上一章中,我们使用词袋模型将非结构化文本数据转换为数值格式。 此模型抽象出了单词顺序,并将文档表示为单词向量,其中每个条目表示单词对文档的相关性。
产生的文档-术语矩阵(DTM)(您可能还会遇到转置的术语-文档矩阵)可用于基于其标记内容将文档与其他文档或查询向量进行比较,并快速找到草堆中的一根针或相应地对文档进行分类。
但是,这个文档模型既是高维的又是非常稀疏的。 因此,它几乎不能总结内容或更接近理解它是关于什么的。 在本章中,我们将使用无监督的机器学习形式的主题建模来从文档中提取隐藏的主题。 这些主题可以以自动化方式为大量文档提供详细的见解。 它们非常有用,可以了解草堆本身,并允许以主题和文档的关联程度进行简明的文档标记。
主题模型允许提取复杂的可解释文本特征,可以以各种方式用于从大量文档中提取交易信号。 它们加速了文档的审阅,帮助识别和聚类相似的文档,并且可以被注释为预测建模的基础。 应用包括在公司披露、收入电话成绩单、客户评论或合同中识别关键主题,使用例如情感分析或直接标记与随后的资产回报相关联。
更具体地说,在这一章中,我们将涵盖以下主题:
- 主题建模实现了什么,为什么重要,以及它如何发展
- 潜在语义索引(LSI)如何降低 DTM 的维度
- 概率隐含语义分析(pLSA)如何使用生成模型提取主题
- 潜在狄利克雷分配(LDA)如何改进 pLSA 以及为什么它是最流行的主题模型
- 如何可视化和评估主题建模结果
- 如何使用 sklearn 和 gensim 实现 LDA
- 如何将主题建模应用于收入电话和 Yelp 商业评论的集合
下一节的代码示例位于 GitHub 存储库的目录中,主 README 文件中列出了参考资料。
学习潜在主题:目标和方法
主题建模旨在发现跨文档的隐藏主题或主题,捕获超越个别单词的语义信息。 它旨在解决从文本数据中学习的机器学习算法的一个关键挑战,即超越已被写成的词汇水平,到预期的语义水平。 生成的主题可以用于根据它们与各种主题的关联来注释文档。
换句话说,主题建模旨在自动总结大量文档以促进组织和管理,以及搜索和推荐。同时,它可以使得人类能够理解文档的程度,以至于可以解释主题的描述。
主题模型旨在解决可能困扰词袋模型的维度诅咒。基于高维稀疏向量的文档表示可以使相似性度量变得嘈杂,导致不准确的距离测量和文本分类模型的过拟合。
此外,词袋模型忽略了词序并且丢失了语境以及语义信息,因为它无法捕捉同义词(几个词具有相同的含义)和多义词(一个词具有多个含义)。因此,当文档没有按照用于搜索或比较的术语进行索引时,文档检索或相似性搜索可能会错过要点。
这些缺陷引发了这个问题:我们如何对促进与文本数据更有生产性的交互的含义主题进行建模和学习?
从线性代数到分层概率模型
主题模型最初尝试改进矢量空间模型(在 1970 年代中期开发)的努力应用线性代数来降低文档-术语矩阵的维度。这种方法类似于我们在《第十二章》中讨论的主成分分析算法,无监督学习,关于无监督学习。虽然有效,但在缺乏基准模型的情况下评估这些模型的结果是困难的。
作为回应,出现了概率模型,假设有一个明确的文档生成过程,并提供算法来反向工程这个过程,并恢复潜在主题。
此表突出显示了我们将在接下来的章节中更详细讨论的模型演变的关键里程碑:
| 模型 | 年份 | 描述 |
| 潜在语义索引 (LSI) | 1988 | 通过将单词空间的维度降低以捕捉语义文档-术语关系来减少 |
| 概率潜在语义分析 (pLSA) | 1999 | 反向工程一个假设单词生成主题并且文档是主题的混合的过程 |
| 潜在狄利克雷分配 (LDA) | 2003 | 为文档添加了一个生成过程:一个三层次的层次贝叶斯模型 |
潜在语义索引
潜在语义索引(LSI,也称为潜在语义分析)旨在改进省略了包含查询词的同义词的相关文档的查询结果。它旨在建模文档和术语之间的关系,以便能够预测一个术语应该与文档相关联,即使由于单词使用的变异性,没有观察到这样的关联。
LSI 使用线性代数来通过分解 DTM 找到给定数量k个潜在主题。更具体地说,它使用奇异值分解(SVD)来找到使用 k 个奇异值和向量的最佳低秩 DTM 近似。换句话说,LSI 是我们在第十二章中遇到的无监督学习技术的应用,无监督学习,用于我们在第十三章中介绍的文本表示,处理文本数据。作者尝试了分层聚类,但发现这对于明确地建模文档-主题和主题-词关系,或者捕获文档或术语与几个主题的关联太过于限制性。
在这种情况下,SVD 的目的是识别一组不相关的索引变量或因子,使我们能够通过其因子值向量表示每个术语和文档。
以下图示了 SVD 如何将 DTM 分解为三个矩阵,其中两个包含正交奇异向量,以及一个带有奇异值的对角矩阵,用作缩放因子。假设原始数据中存在一些相关性,奇异值会衰减,因此仅选择最大的T个奇异值会产生原始 DTM 的低维近似,且信息损失相对较小。因此,在简化版本中,原先有N个项目的行或列只有T个条目。
这种简化的分解可以解释如下,其中第一个M x T矩阵表示文档与主题之间的关系,对角矩阵按其语料库强度缩放主题,并且第三个矩阵模拟了词-主题关系:
从第一个两个矩阵的乘积得到的矩阵的行,U[T]Σ[T][,]对应于在潜在主题空间中投影到原始文档的位置。
如何使用 sklearn 实现 LSI
我们将使用我们在上一章介绍的 BBC 文章数据来说明 LSI 的应用,因为它足够小,可以快速训练,并且允许我们将主题分配与类别标签进行比较。请参阅latent_semantic_indexing笔记本以获取其他实施细节:
- 我们首先加载文档并创建一个包含 50 篇文章的训练集和(分层)测试集。
- 然后,我们使用
TfidfVectorizer对数据进行向量化,以获得加权 DTM 计数,并过滤掉在不到 1%或超过 25%的文档中出现的单词,以及通用停用词,以获得约 2,900 个单词的词汇表:
vectorizer = TfidfVectorizer(max_df=.25, min_df=.01, stop_words='english', binary=False) train_dtm = vectorizer.fit_transform(train_docs.article) test_dtm = vectorizer.transform(test_docs.article)
- 我们使用
sklearn的TruncatedSVD类,它只计算* k *个最大的奇异值,以降低文档-术语矩阵的维度。确定性 arpack 算法提供了精确的解决方案,但默认的随机实现对于大矩阵更有效。 - 我们计算五个主题以匹配五个类别,这仅解释了总 DTM 方差的 5.4%,因此更高的值是合理的:
svd = TruncatedSVD(n_components=5, n_iter=5, random_state=42) svd.fit(train_dtm) svd.explained_variance*ratio* array([0.00187014, 0.01559661, 0.01389952, 0.01215842, 0.01066485])
- LSI 为文档-术语矩阵确定了一个新的正交基,将秩降低到所需主题的数量。
- 训练好的
svd对象的.transform()方法将文档投影到新的主题空间,这是通过降低文档向量的维度得到的结果,并对应于之前说明的*U[T]Σ[T]*变换:
train_doc_topics = svd.transform(train_dtm) train_doc_topics.shape (2175, 5)
- 我们可以对一篇文章进行采样,查看其在主题空间中的位置。我们选择了一个与主题 1 和 2 最(正相关)关联的
Politics文章:
i = randint(0, len(train_docs)) train_docs.iloc[i, :2].append(pd.Series(doc_topics[i], index=topic_labels)) Category Politics Heading What the election should really be about? Topic 1 0.33 Topic 2 0.18 Topic 3 0.12 Topic 4 0.02 Topic 5 0.06
- 此示例的主题分配与每个类别的平均主题权重相一致(
Politics是最左边的)。它们说明了 LSI 如何将 k 个主题表达为 k 维空间中的方向(笔记本包括每个类别的平均主题分配在二维空间中的投影)。 - 每个类别都有明确定义,测试任务与训练任务相匹配。然而,权重既有正数又有负数,这使得解释主题更加困难:
- 我们还可以显示与每个主题最密切相关的单词(绝对值)。主题似乎捕捉了一些语义信息,但没有区分开来:
优缺点
LSI 的优点包括去除噪声、减轻维度灾难,同时捕捉一些语义信息,并将文档和术语进行聚类。
然而,LSI 的结果很难解释,因为主题是具有正负条目的词向量。此外,没有基础模型可供评估拟合度,并在选择维度或主题数量时提供指导。
概率潜在语义分析
**概率潜在语义分析(pLSA)**从统计的角度看待 LSA,并创建了一个生成模型来解决 LSA 缺乏理论基础的问题。
pLSA 明确地对 LSA 采取了统计视角,并创建了一个生成模型,以解决 LSA 缺乏理论基础的问题。
此生成过程的对称形式假定单词-文档共现的隐含主题类别生成了单词和文档,而非对称模型假定给定文档后选择了主题,并且单词是在给定主题后进行的第二步生成:
主题数是在训练之前选择的超参数,不是从数据中学习得到的。
概率模型通常使用以下板块符号来表示依赖关系。下图编码了刚刚描述的非对称模型的关系。每个矩形代表多个项,比如外部的 M 个文档和内部块的每个文档的 N 个单词。我们只观察文档及其内容,模型推断隐藏或潜在的主题分布:
使用概率模型的好处是,我们现在可以通过评估它们在训练期间学到的参数对新文档分配的概率来比较模型。
如何使用 sklearn 实现 pLSA
pLSA 等价于使用 Kullback-Leibler 散度目标的非负矩阵分解(请参见 GitHub 上的参考资料 github.com/PacktPublishing/Hands-On-Machine-Learning-for-Algorithmic-Trading)。因此,我们可以使用 sklearn.decomposition.NM 类来实现这个模型,遵循 LSA 示例。
使用由 TfidfVectorizer 生成的 DTM 的相同的训练-测试拆分,我们按如下方式拟合 pLSA:
nmf = NMF(n_components=n_components, random_state=42, solver='mu', beta_loss='kullback-leibler', max_iter=1000) nmf.fit(train_dtm)
我们得到一个重建误差的度量,这是以前解释的方差度量的替代品:
nmf.reconstruction_err_ 316.2609400385988
由于其概率性质,pLSA 只产生正主题权重,从而使测试集和训练集的主题-类别关系更加直观:
我们还可以看到描述每个主题的词列表开始变得更有意义;例如,娱乐类别与主题 4 最直接相关,其中包括电影、明星等词语:
潜在狄利克雷分配
潜在狄利克雷分配(LDA)通过为主题添加生成过程来扩展 pLSA。
它是最受欢迎的主题模型,因为它倾向于产生人类能够相关的有意义的主题,可以为新文档分配主题,并且是可扩展的。LDA 模型的变体可以包括元数据,如作者,或图像数据,或学习分层主题。
LDA 的工作原理
LDA 是一个分层贝叶斯模型,假设主题是词的概率分布,而文档是主题的分布。更具体地说,该模型假设主题遵循稀疏狄利克雷分布,这意味着文档只涵盖了一个小部分主题,并且主题仅使用了一小部分词频繁地。
狄利克雷分布
狄利克雷分布(Dirichlet distribution)生成可以与离散分布一起使用的概率向量。也就是说,它随机生成一定数量的值,这些值为正且总和为一,符合概率的期望。它有一个正实数参数,用于控制概率的集中程度。接近零的值意味着只有少数值会为正,并且获得大部分概率质量。下面的屏幕截图展示了对于 α = 0.1 进行了三次大小为 10 的抽样(dirichlet_distribution 笔记本中包含一个模拟,您可以尝试不同的参数值):
狄利克雷分配
Python 机器学习算法交易实用指南(四)(5)https://developer.aliyun.com/article/1523388