使用潜在狄利克雷分配(LDA)的主题建模教程
这是一本实用指南,包含经过实践检验的 Python 代码。找到人们在推特上谈论的内容。
来源:杰夫·克拉克的在线推特话题浏览器生成的图片
对内容好奇?感觉迷失在无数的帖子、推文、评论、文章、页面、文档中?寻找人们写的东西?你并不孤单。
在本教程中,我提供了一个实用的指南,其中包含经过实践检验的 Python 代码,用于发现文本或文档集合中出现的抽象主题。
我们将对一组推文应用潜在狄利克雷分配(LDA ),并将它们分成主题。我们开始吧!
推特数据
我们将处理来自@realDonaldTrump 推特账户的推文。该数据集包含该账户自 2009 年 5 月 4 日以来的所有推文。
数据集来自 http://www.trumptwitterarchive.com/archive。下面是原始档案的摘录。
数据预处理
对文本进行预处理是很重要的,因为语言在各个层面都是模糊的:词汇、短语、语义。我们将推文全部小写,并删除了的停用词。它们是常用词,不会给推文增加重要意义。以下是自然语言工具包(NLTK)中的停用词示例。
鉴于推文的性质,我们要小心使用标点符号和术语,如 RT (用于转发推文)和 VIA (用于提及原作者),它们不在默认的英语停用词表中。我们还想删除推文末尾的网址。
我们也标记我们的推文。记号化器的工作原理类似于正则表达式,用于将 tweets 分成单词列表。下面我们提供了预处理推文的完整代码。
一袋单词
当我们对文本分类感兴趣时,根据主题分类,我们通常不想看单词的顺序模式。相反,我们会将文本表示为一个单词包,就好像它是一组无序的单词,同时忽略它们在文本中的原始位置,只保留它们的频率。你可以在我们下面的文章中了解更多关于自然语言处理的知识。
理解书面单词:温习 Word2vec、GloVe、TF-IDF、单词袋、N-grams、1-hot 编码…
towardsdatascience.com](/representing-text-in-natural-language-processing-1eead30e57d8)
最常见的单词及其计数如下。
寻找主题的数量
潜在狄利克雷分配(LDA) 是从词袋计数到更低维度的主题空间的概率变换。推文被视为主题的分布。反过来,主题由词汇表中所有单词的分布来表示。但是我们不知道语料库中出现的主题数量以及属于每个主题的 tweets。使用 LDA,我们希望将 tweets 到主题的分配视为一个随机变量,它是根据手头的数据估计的。
来源:马克·格利克曼(哈佛大学 IACS 分校)
为 LDA 寻找合适数量的主题是一门艺术。主题连贯技术通常比困惑技术更受青睐。对于连贯性,我们通过测量高得分单词之间的语义相似度来量化主题的连贯性。这就引出了更人性化的话题。该技术选择每个主题中出现频率最高的单词。然后,它计算并汇总每个单词的所有成对得分(UMass ),以计算特定主题的一致性得分。
下面,我们将一系列用不同数量的主题训练的模型的每个主题的平均一致性分数可视化。平均分稳定的主题数量,是我们正在寻找的最佳点。因此,我们对主题数量的最佳猜测是 6 个左右。我们应该考虑到,通常情况下,包含非常短的文档(在我们的例子中是 tweets)的语料库比包含较长文档的语料库更难应用于连贯模型。
运行 LDA
现在,我们可以使用通过上述分析找到的最优值对文本运行 LDA。LDA 是一种生成方法,其中对于每个主题,模型模拟单词出现的概率以及主题在文档中的概率。每个主题的前 10 个单词如下所示。
虽然题目的合理性值得怀疑,但我们可以看到下面有意义的匹配。主题 0 涉及中国、朝鲜和美国领导人之间的会晤。话题 1 似乎与联邦调查局对俄罗斯干涉上届总统选举的调查有关。话题 4 好像是关于边境安全和修建边境墙的。
结论
我们展示了统计建模如何帮助发现人们在推特上谈论什么。本文提供的代码可以推广到许多其他任务,旨在发现文档集合中出现的抽象主题。
一些参考资料:
D.布莱,阿宁和乔丹。潜在狄利克雷分配。机器学习研究杂志,3:993–1022,2003 年 1 月。d .布雷和 j .拉弗蒂。主题模型。编辑,文本挖掘:理论与应用。泰勒和弗朗西斯,2009 年。
B .陈,x .陈,andW。邢。学习分析和知识会议的“Twitter 考古学”。《第五届学习分析和知识国际会议论文集》(第 340-349 页)。纽约州纽约市,2015 年。
感谢您的阅读。期待您的反馈或问题。
亚马逊评论的自然语言处理主题建模
Python 在潜在狄利克雷分配中的应用
来源: Arfa Adam ,Via: Shutterstock
主题建模是另一种流行的文本分析技术。主题建模的最终目标是在评论中找到主题,并发现隐藏的主题。语料库中的每个文档将由至少一个主题组成,如果不是多个主题的话。
在这一部分,我将介绍潜在狄利克雷分配(LDA) 的结果,这是众多主题建模技术之一。这是一种无监督的分类方法。当我们有一个大的文本,不知道从哪里开始,那么我们可以应用主题建模,可以看到什么样的群体正在出现。LDA 是专门为文本数据设计的。
要使用主题建模技术,您需要提供:
- 文档术语矩阵
- 您希望算法选取的主题数。
在主题建模中,我会创建不同的模型并进行比较。最终我会选择最有意义的话题模型。
如何使用潜在狄利克雷分配(LDA)?
在这里,我不会涉及 LDA 的数学基础。我将只讨论如何解释 LDA 主题建模的结果。
在 LDA 主题建模过程中,我们创建了许多不同的主题组。作为研究人员,我们是决定输出中组数的人。然而,我们不知道什么是最佳组数。因此,我们将获得不同数量的组。然后,我们检查和比较主题模型,并决定哪个主题模型更有意义,最有意义,在模型中有最清晰的区别。然后在所有的题目组中选出最有意义的组(模型)。
必须注意的是,LDA 的性质是主观的。对于选择最有意义的话题组,不同的人可能会得出不同的结论。我们正在寻找最合理的主题组。来自不同背景、不同领域专业知识的人可能不会在大多数合理的主题组上达成一致。
LDA 是一种无监督聚类方法。当我们在谈论无监督聚类方法时,我们需要提到 K-Means 聚类。K-Means 聚类是最著名的无监督聚类方法之一。它在很多情况下都非常实用和有用,并且已经用于文本挖掘很多年了。与 K-means 聚类(其中每个单词只能属于一个聚类(硬聚类))相反,LDA 允许“模糊”成员资格(软聚类)。软集群允许集群之间重叠,而在硬集群中,集群是互斥的。这是什么意思?在 LDA 中,一个单词可以属于多个组,而这在 K-Means 聚类中是不可能的。在 LDA 中,这种权衡使得更容易找到单词之间的相似之处。然而,这种软聚类使得很难获得不同的组,因为相同的单词可能出现在不同的组中。在我们进一步的分析中,我们将会遇到这个缺点。
一旦应用了主题建模技术,研究人员作为人的工作就是解释结果,看看每个主题中的单词组合是否有意义。如果它们没有意义,我们可以尝试改变主题的数量、文档术语矩阵中的术语、模型参数,甚至尝试不同的模型。
数据准备
**关于我使用的数据的简要信息:**本研究使用的数据是从 Kaggle 下载的。是斯坦福网络分析项目上传的。原始数据来自 J. McAuley 和 J. Leskovec (2013)对“从业余爱好者到行家:通过在线评论对用户专业知识的演变建模”的研究。这个数据集由来自亚马逊的美食评论组成。该数据包括了从 1999 年到 2012 年的所有 568,454 篇评论。评论包括产品和用户信息、评级和纯文本评论。
在这项研究中,我将关注亚马逊上的“好评”。我将“好评”定义为拥有 4 星或 5 星客户评价(满分 5 分)的评价。换句话说,如果一个亚马逊评论是 4 星或 5 星,在这项研究中它被称为“好评论”。1、2、3 星的评论被贴上‘差评’的标签。
数据准备是一个关键的部分,因为如果我们不能正确地准备数据,我们就不能进行主题建模。在这里,我不会深究细节,因为数据准备不是本次研究的重点。然而,准备花一些时间在这里,以防你有问题。如果您调整我在这里为您自己的数据集提供的代码,我希望您不会有任何问题。
一个编辑和警告 : 如果你想复制这项工作,请在我的 Github 上的 Colab 笔记本 中跟随/仔细检查,因为这里有几个丢失的变量/定义会阻止这个过程。
让我们从检查数据的列数和行数开始:
df.shape
(392384,19)-我的数据集有 392,384 条评论。
对于许多家用电脑来说,如此多的评论是很难处理的。因此,我将只使用 10,000 条评论。不幸的是,如果没有超级计算机,我们就无法使用所有的评论。
#Taking only first 10000 Good Reviews from the dataset.
df_good_reviews= df.loc[df.Good_reviews ==1][0:10000]
现在,数据准备的下一步是计数矢量器:
# Create document-term matrix
from sklearn.feature_extraction import text
from sklearn.feature_extraction.text import CountVectorizercv = CountVectorizer()
data_cv = cv.fit_transform(df_good_reviews.Text)
data_cv.shape
计数后数据的形状矢量器
提取数据以创建术语-文档矩阵
# Pickle it
import pickle
pickle.dump(cv, open("cv_stop.pkl", "wb"))
data_stop.to_pickle("dtm_stop.pkl") #saving the pickled datadata = pd.read_pickle('dtm_stop.pkl')
data
酸洗数据后,我们获得了一个广泛的字典:
现在,是时候创建术语文档矩阵了。该术语-文档矩阵数据将用于生成主题建模。这就是为什么它对这项研究至关重要。
# One of the required inputs is a term-document matrix
tdm = data.transpose()
tdm.head()
创建术语-文档矩阵后的数据
# We're going to put the term-document matrix into a new gensim format, from df --> sparse matrix --> gensim corpussparse_counts = scipy.sparse.csr_matrix(tdm)
corpus = matutils.Sparse2Corpus(sparse_counts)# Gensim also requires dictionary of the all terms and their respective location in the term-document matrixcv = pickle.load(open("cv_stop.pkl", "rb"))
id2word = dict((v, k) for k, v in cv.vocabulary_.items())
让我们开始创建主题模型吧!
使用 LDA 构建主题模型
有许多方法可以创建主题模型。通过根据特定标准过滤文本,我们可以获得不同的主题组。在本研究中,我们将使用以下方法创建主题模型:
- 所有文本数据
- 仅文本中的名词
- 只有名词和形容词
主题建模—尝试 1(使用所有复习数据)
作为开始,我们正在利用我们所有的评论。在此过程中不应用文本过滤。
lda = models.LdaModel(corpus=corpus, id2word=id2word, num_topics=2, passes=10)
lda.print_topics()
发现了两组话题:
[(0,’ 0.020* “咖啡”+ 0.011* “喜欢”+ 0.010* “茶”+ 0.010* “味道”+ 0.009* “好”+ 0.009* “味道”+ 0.007* “一个”+ 0.007* “很棒”+ 0.006* “用”+ 0.006* “杯子”'),
(1,’ 0.010* “很棒”+ 0.009* “喜欢”+ 0.009* “好”+ 0.007* “爱”+ 0.007* “美食”+ 0.007* “一个”+ 0.007* “产品”+ 0.005* “味道”+ 0.005* “得到”+ 0.005* “亚马逊” ')]
我将如何处理这个结果?此时,我们可以检查这两个主题组,并检查它们是否是不同的组。我们不期望一组中的所有单词都必须相关。我们在寻找话题组的总趋势。考虑到以上两个群体,很难在那里看到一个清晰的截然不同的群体。这是我的看法。如果你认为实际上有两个离散的群体,并且你能证明它是正当的,那么你是安全的。
记住,我说过 LDA 是一个软聚类模型。在这里,我们看到“like”这个词在两组中都出现了。这是正常的,因为在软聚类模型中,一个单词可以同时出现在不同的组中。
我们不需要强迫自己给上述模型赋予意义。我们可以继续前进。让我们创建更多的主题模型。
与 2 主题模型相似,我们将创建 3 个和 4 个小组。
# LDA for num_topics = 3
lda = models.LdaModel(corpus=corpus, id2word=id2word, num_topics=3, passes=10)
lda.print_topics()# LDA for num_topics = 4
lda = models.LdaModel(corpus=corpus, id2word=id2word, num_topics=4, passes=10)
lda.print_topics()
所有结果见表 1(下表)。此时,我们应该检查这些组,并尝试找到最有意义的组。通过查看表 1,可以说“用 2 个主题建模”最有意义。第一组是关于饮料,第二组是关于反应。随意得出和我不同的结论!目前,这将停留在这里,不会进一步,因为我们将创建 6 个以上的主题模型。得到所有结果后,我可以更仔细地检查输出。在产生进一步的主题模型后,我们将重新考虑表 1。
表 1:使用所有文本数据的主题建模
**主题建模—尝试 2(仅名词)😗*在此步骤中,通过使用 LDA 方法,仅名词将用于创建主题。同样,我们的目的是发现评论中隐藏的模式。现在,我们只是试图用不同的标准来探索。
与上一步类似,我们将使用 2、3 和 4 个主题组运行 LDA。
import nltk
from nltk.corpus import stopwords
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
import string
from nltk import word_tokenize, pos_tag
让我们创建一个从一串文本中提取名词的函数:
from nltk import word_tokenize, pos_tagdef nouns(text):
'''Given a string of text, tokenize the text and pull out only the nouns.'''
is_noun = lambda pos: pos[:2] == 'NN'
tokenized = word_tokenize(text)
all_nouns = [word for (word, pos) in pos_tag(tokenized) if is_noun(pos)]
return ' '.join(all_nouns)#if this code doesn't work on you, it's probably indentaion mistake coming from Medium writing format.
让我们将这个定义的名词函数应用到我们的亚马逊评论文本数据:
data_nouns = pd.DataFrame(df_good_reviews.Text.apply(nouns))
data_nouns
仅使用名词创建新的文档术语矩阵:
from sklearn.feature_extraction import text
from sklearn.feature_extraction.text import CountVectorizer# Re-add the additional stop words since we are recreating the document-term matrixadd_stop_words = ['like', 'im', 'know', 'just', 'dont', 'thats', 'right', 'people','youre', 'got', 'gonna', 'time', 'think', 'yeah', 'said']stop_words = text.ENGLISH_STOP_WORDS.union(add_stop_words) # Recreate a document-term matrix with only nouns
cvn = CountVectorizer(stop_words=stop_words)
data_cvn = cvn.fit_transform(data_nouns.Text)data_dtmn = pd.DataFrame(data_cvn.toarray(), columns=cvn.get_feature_names())data_dtmn.index = data_nouns.index
data_dtmn
创建 gensim 语料库:
corpusn=matutils.Sparse2Corpus(scipy.sparse.csr_matrix(data_dtmn.transpose()))# Create the vocabulary dictionaryid2wordn = dict((v, k) for k, v in cvn.vocabulary_.items())
⚫:让我们从两个话题开始
ldan = models.LdaModel(corpus=corpusn, num_topics=2, id2word=id2wordn, passes=10)ldan.print_topics()
带 3 个主题的⚫ LDA
ldan = models.LdaModel(corpus=corpusn, num_topics=3, id2word=id2wordn, passes=10)ldan.print_topics()
⚫ LDA 有 4 个主题
ldan = models.LdaModel(corpus=corpusn, num_topics=4, id2word=id2wordn, passes=10)ldan.print_topics()
表 2 是仅使用名词的 LDA 主题建模尝试的输出。在这一点上,我们再次需要简要地检查主题,并试图找到一个具有不同主题组的模型。同样,仍然不需要花费太多时间,因为我们仍然会产生更多的主题模型。
表 2:仅使用名词的主题建模
对我来说,有三组的主题模型是有意义的。它有这些组:
- 宠物物品
- 饼干和小吃
- 饮料
人和人能得出不同的结论是完全正常的。
**主题建模——尝试 3(名词和形容词)😗*在此步骤中,通过使用 LDA 方法,仅使用名词和形容词来创建主题。
让我们为此准备数据:
from nltk.corpus import stopwords
import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
import string
from nltk import word_tokenize, pos_tag
让我们定义一个只给出名词或形容词的函数:
def nouns_adj(text):
'''Given a string of text, tokenize the text and pull out only the nouns and adjectives.''' is_noun_adj = lambda pos: pos[:2] == 'NN' or pos[:2] == 'JJ'
tokenized = word_tokenize(text)
nouns_adj = [word for (word, pos) in pos_tag(tokenized) if is_noun_adj(pos)]
return ' '.join(nouns_adj)#if this code doesn't work on you, it's probably indentaion mistake coming from Medium writing format.
让我们用名词/形容词过滤器检查亚马逊评论数据:
data_nouns_adj = pd.DataFrame(df_good_reviews.Text.apply(nouns_adj))
data_nouns_adj
我们可以看到,这里只有名词和形容词。现在,我们将要求 LDA 通过使用我们数据集的过滤版本来创建主题。
让我们为 LDA 准备数据:
# Create a new document-term matrix using only nouns and adjectives, also remove common words with max_df
cvna = CountVectorizer(max_df=.8) #, max_df is used for removing data values that appear too frequently, also known as "corpus-specific stop words".
# For example, max_df=.8 means "It ignores terms that appear in more than 80% of the documents". data_cvna = cvna.fit_transform(data_nouns_adj.Text)
data_dtmna = pd.DataFrame(data_cvna.toarray(), columns=cvna.get_feature_names())data_dtmna.index = data_nouns_adj.index
data_dtmna
创建 gensim 语料库:
corpusna=matutils.Sparse2Corpus(scipy.sparse.csr_matrix(data_dtmna.transpose()))# Create the vocabulary dictionary
id2wordna = dict((v, k) for k, v in cvna.vocabulary_.items())
我们现在可以管理 LDA 了。
⚫:让我们从两个话题开始
ldana = models.LdaModel(corpus=corpusna, num_topics=2, id2word=id2wordna, passes=10)
ldana.print_topics()
⚫:让我们试试 3 个话题
ldana = models.LdaModel(corpus=corpusna, num_topics=3, id2word=id2wordna, passes=10)
ldana.print_topics()
⚫:让我们试试 4 个话题
ldana = models.LdaModel(corpus=corpusna, num_topics=4, id2word=id2wordna, passes=10)
ldana.print_topics()
表 3 是仅使用名词或形容词的 LDA 主题建模尝试的输出。在这一点上,我们再次需要检查主题,并检查是否有任何主题组有意义。
表 3:仅使用名词和形容词的主题建模
评估结果
现在,在最后阶段,必须一起评估表 1、表 2 和表 3。总之,我们创建了 9 个主题模型。在这一点上,我们需要问自己,哪一组更有意义?’。现在,是时候关注和检查主题了。哪些群体有意义?如果没有有意义的组,我们需要返回数据进行数据清洗,更改参数,或者使用不同的过滤器和模型。这是一个递归过程。
在 9 个主题模型中,只有名词,3 主题组对我来说最有意义(表 4)。我在这里看到了三个不同的类别:(1)宠物用品,(2)饼干和小吃,以及(3)饮料。再一次,另一个人可以找到更有意义的不同主题模型是完全正常的。
表 4:最有意义的话题-名词只有三个话题
请记住,这个数据集只包含食品评论。所以,看到这些群体与食物有关是很正常的。
所以,让我们把它拉下来,再进行一些迭代,得到更多微调的主题。3 主题(只有名词)对我来说是最有意义的模型。因此,我将通过将迭代次数从 10 次增加到 80 次来进一步微调这个主题模型。
# Fine-tuned LDA with topics = 3ldan = models.LdaModel(corpus=corpusn, num_topics=3, id2word=id2wordn, passes=80)ldan.print_topics()
表 5:带有微调参数的最终主题模型
在上表中,我们看到与表 4 相似的组,只是顺序不同。通过使用 LDA 方法考虑主题分析的所有步骤,可以得出结论,优秀的亚马逊评论可以分为三个主要主题:(1)饮料,(2)宠物用品,以及(3)饼干和小吃。
最后,我们需要记住,为了理解哪个主题组更有意义,我们可能需要领域专业知识。作为一名研究人员,我们有责任证明我们选择的课题组是正确的。只要能自圆其说,我们可以决定任何一个主题组作为最终的主题模型。
感谢您的阅读!开心分析!
*特别感谢我的朋友Bibor Szabo在撰写本文时提供了宝贵的反馈和建议。
**注:**关于本次学习的 Python 代码,可以查看我的 GitHub 文件夹 。本研究中使用的所有脚本都可以在 Github 文件夹中找到。此外,这个主题建模研究是更大项目的一部分。你可以查看我以前的帖子进行 数据清理 和 情绪分析如果你有兴趣看前面的步骤。
研究生院面试的主题建模
利用数据科学来了解你感兴趣的院系的研究领域。
**图一。**我感兴趣的教师中无人监督检测的子主题的代表词云。
本文是正在进行的关于在研究生院面试过程中利用数据科学的系列文章的一部分。查看上一期 社交网络分析 ,期待更多。
欢迎回来。在第 1 部分中,我们探索了社交网络分析,同时初步探索了每位教授感兴趣的主题,我们还探讨了更深入分析的可能性,包括主题建模,以获得研究人员之间更深层次的联系(如果您还没有阅读第 1 部分,请立即阅读!).在本次会议中,我们将使用基本上相同的数据,因此,通过第 1 部分的运动,至少让数据争论可能是一个好主意。
由于我们一直在准备,我们已经建立了一个更强的认识,我们的兴趣和他们的职业生涯有多少是在合作中巩固的,并分享了对彼此的尊重。今天,我们将通过更深入地挖掘现有数据来进一步了解这一点,看看我们是否可以收集到任何额外的信息,甚至了解我们自己的研究兴趣。
与第 1 部分一样,我们将主要使用 Python,包括以下模块和一些新模块:
- 学术,通过谷歌学术收集数据。
- 熊猫和 Numpy,进行基础数据角力和操作。
- Networkx 和 Matplotlib,用于绘图和数据表示。
- Python-docx,用于自动化文档构造。
- Sci-kit Learn (sklearn),用于无监督机器学习。
- spaCy,用于自然语言处理。
其中一些可能包含在 Python 的典型安装中,也可能不包含在其中,因此我建议您参考它们各自的网站/文档以获得专门的帮助。
第 0 部分:增加我们之前的数据
在第 1 部分第 3 节中,我们从每位教师的出版历史中提取数据,并制作了一个直方图,记录了他们感兴趣的主要主题的信息。
图二。宾夕法尼亚州立大学 Charles Geier 博士的出版物标题词频直方图。
然而,我们可以重新制作这个图像来捕捉更多的信息,特别是关于每个单词在一段时间内的用法分布。要做到这一点,我们必须重新审视我们的数据收集,这一次,提取关于他们的出版日期的额外信息。这方面的代码如下。
import pandas as pd
import matplotlib.pyplot as plt
from stop_words import get_stop_wordsexcs = get_stop_words('en')
table = pd.read_csv("FoIScholarData.csv")
def valuef(x):
return worddict[x]
newcol = []
for i in range(len(table)):
search_query = scholarly.search_author(table.Name[i])
author = next(search_query).fill()
pubs = scholars[i].publications
worddict = {}
for pub in pubs:
if not ('year' in pub.bib):
continue
j = pub.bib['title']
words = j.lower().split(' ')
for k in words:
if k in worddict.keys():
worddict[k] += [pub.bib['year']]
else:
worddict[k] = [pub.bib['year']]
values = []
topics = []
for j in worddict:
if len(worddict[j]) > 2:
if not (j in excs):
values += [len(worddict[j])]
topics += [j]
width = 0.3
topics.sort(key=valuef)
values.sort()
newcol += [topics]
newcol2 += [values]
newcol3 += [worddict]table['topics'] = newcoltable['topics'] = nnewcol
table['tcounts'] = nnewcol2
table['tyears'] = nnewcol3
*export_csv = table.to_csv (r'C:\Users\Masters-PC\Desktop\ProfNetworks\FoIScholarData.csv', index = None, header=True, encoding='utf-16') #Don't forget to add '.csv' at the end of the path
第一部分:探索研究兴趣的时间演变。
现在,我们的单词计数对字典不是只有一个列,而是有 3 个列来携带每个实例的信息,允许以时态栅格图的形式更动态地表示数据。这背后的想法是,即使在作者最常用的词中,在他们的职业生涯中也可能有直方图中没有捕捉到的转变。要为每个研究人员生成图像,代码如下:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.ticker import AutoMinorLocator
# Set the random seed for data generationnp.random.seed(2)
# Create rows of random data with 50 data points simulating rows of trainstable = pd.read_csv("FoIScholarData.csv", encoding = 'utf-16')
for i in range(0,len(table)):
times = eval(table.tyears[i])
words = eval(table.topics[i])
values = eval(table.tcounts[i])
neuralData = np.random.random([8, 50])
# Set different colors for each word
colorCodes = np.array([[0, 0, 0],
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[1, 1, 0],
[1, 0, 1],
[0, 1, 1],
[1, 0, 1]])
# Set spike colors for each word
lineSize = [0.4, 0.3, 0.2, 0.8, 0.5, 0.6, 0.7, 0.9]
def addition(x,y):
z = []
if len(x) == len(y):
for i in range(len(x)):
z += [x[i]+y[i]]
return z
else:
print('Wrong sizes')
return 'Wrong Sizes'
plt.figure()
for j in range(max([-8,-len(words)]),0):
plt.plot(addition(times[words[j]], 0.5*np.random.random(len(times[words[j]]))),
addition([j]*len(times[words[j]]),0.5*np.random.random(len(times[words[j]]))),
color = colorCodes[abs(j+1)], marker = 'o', linestyle='', alpha=0.3)
#plot.eventplot(neuralData, color=colorCodes, linelengths = lineSize)
# Provide the title for the spike raster plot
wordsgraphed = []
for j in range(max([-8,-len(words)]),0):
wordsgraphed += [words[j]+'('+str(values[j])+')']
#wordsgraphed += ['']
plt.title('Distribution of Word Title usage for ' + table.Name[i])
plt.xlabel('Time (years)')
plt.ylabel('Word')
plt.yticks(np.arange(max([-8,-len(words)]), 0), wordsgraphed, rotation = 0)
tup = plt.xlim()
plt.xlim([np.floor(tup[0]),np.ceil(tup[1])])
plt.xticks(np.arange(np.floor(tup[0]),np.ceil(tup[1])+1,3))
plt.gcf().subplots_adjust(left=0.28)
minor_locator = AutoMinorLocator(2)
plt.gca().yaxis.set_minor_locator(minor_locator)
plt.grid(which='minor',axis='y')
# Display the spike raster plot
plt.show()
plt.savefig(str(table.Name[i]) + "_WF2.png") # save as png
plt.close() # display
结果应该如下所示:
**图三。**宾夕法尼亚州立大学 Charles Geier 博士的出版物标题的 Word 时态栅格。
我已经将包含的字数减少到 8 个,但是你可以随意修改,但是我想强调你可以从这个情节中获得的新信息。因此,举例来说,如果我们看看吸烟,似乎盖尔博士对该领域的兴趣相对较新,所有的出版物都是从 2014 年开始的,这似乎也与对刺激的直接处理的兴趣下降相吻合。这都是探索性的,例如我们可以在开发中看到,干旱期是常见的,并不一定是完全放弃的信号,但允许我们梳理和询问关于教师对其研究项目的计划的更深层次的问题。
第 2 节:构建出版物标题的主题模型。
对于主事件,我们将进入使用非负矩阵分解(NMF)的无监督主题建模。按照惯例,我们认为主题是描述特定工作的离散单词或短语,因此,例如,我们可能认为新闻中可能出现的主题是犯罪和商业。然而,当我们做主题建模时,输出更准确地被认为是单词集合,立体地典型地出现在属于该主题的文章中。然后,从那些词中,我们可以推断出它可能所指的一个话题集;假设我们从两个主题模型中获得了以下输出:
0: murder victim arrested search
1: revenue investment returns innovation
我们现在有两个主题 0 和 1 以及对该主题最重要的单词。有了这些词汇,我们或许可以将第一个主题分配给crime
,而第二个主题可能是business
。最后的决定很大程度上是任意的。所以,让我们开始吧。
第 2a 节。用于主题建模的数据重新格式化
首先,sci-kit learn 和 spaCy 的结构非常适合处理文本文件,因此我们将通过为每个作者及其出版物创建一系列新的目录结构,将我们的数据重新整理成这种格式。
import pandas as pd
import oscur = os.getcwd()
table = pd.read_csv("FoIScholarData.csv", encoding='utf-16')outdir = cur + '\\Publications\\TitleOnly'
if not os.path.exists(outdir):
os.mkdir(outdir)for i in range(len(table)):
if not os.path.exists(outdir):
os.mkdir(outdir+'/'+table.Name[i])
os.chdir(outdir+'/'+table.Name[i])
pubs = eval(table.publications[i])
for j in range(len(pubs)):
file1 = open(str(j)+".txt","a", encoding='utf-16')
file1.write(pubs[j])
file1.close()
请注意这一步,因为我们可能会重新访问它,使这些文本文件也包含摘要,因为这可能会更强大。我们将不得不重新审视我们的数据收集流程并收集额外的数据,因此我们目前不会这样做,但在后续工作中可能会这样做。最后,我们使用 spaCy 构建要分析的数据框架,将适当的数据加载到其中。
from sklearn.datasets import load_files
import pandas as pd
import spacy
import os
os.chdir('C:/Users/Masters-PC/Desktop/ProfNetworks')
nlp = spacy.load("en_core_web_sm", disable=['parser', 'ner'])random_state = 0DATA_DIR = "C:/Users/Masters-PC/Desktop/ProfNetworks/Publications/TitleOnly"
data = load_files(DATA_DIR, encoding="utf-8", decode_error="replace", random_state=random_state)
df = pd.DataFrame(list(zip(data['data'], data['target'])), columns=['text', 'label'])def only_nouns(texts):
output = []
for doc in nlp.pipe(texts):
noun_text = " ".join(token.lemma_ for token in doc if token.pos_ == 'NOUN')
output.append(noun_text)
return outputdf['text'] = only_nouns(df['text'])df.head()
第 2b 节。模型构建
因此,我们做的第一件事是利用带有TfidfVectorizer
的词频分解从我们的文本文件中提取感兴趣的特征,然后用NMF
进行实际的主题建模。我们将任意选择 5 作为要生成的主题的数量。像 coherence 这样的特定指标可以帮助您确定特定数据集的正确主题数量,但是讨论它们的使用可能超出了本文的范围。
# number of topics to extract
n_topics = 5from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
vec = TfidfVectorizer(max_features=5000, stop_words="english", max_df=0.95, min_df=2)
features = vec.fit_transform(df.text)from sklearn.decomposition import NMF
cls = NMF(n_components=n_topics, random_state=random_state)
cls.fit(features)
有了这些,我们将打印出一些最重要的术语,看看它们可能是关于什么的。
# list of unique words found by the vectorizer
feature_names = vec.get_feature_names()# number of most influencing words to display per topic
n_top_words = 5for i, topic_vec in enumerate(cls.components_):
print(i, end=' ')
for fid in topic_vec.argsort()[-1:-n_top_words-1:-1]:
print(feature_names[fid], end=' ')
print()
对于我感兴趣的部分,输出如下所示:
0 network dynamic analysis datum activity
1 brain interface stimulation machine control
2 cortex representation memory motor learning
3 model response theory mouse imitation
4 neuron response property behavior spiking
另一个,也许是更具知识性的可视化数据的方法是制作每个数据的文字云。
from wordcloud import WordCloud
import math as m
import random
import matplotlib.pyplot as plt
import copy
from matplotlib.pyplot import figurefor j in range(n_topics):
lst = []
comps = copy.deepcopy(cls.components_[j])
comps.sort()
comps = list(comps)
bomps = copy.deepcopy(cls.components_[j])
bomps = bomps*2000/max(bomps)
for k in range(len(feature_names)):
if len(comps)-comps.index(cls.components_[j][k]) < 50:
lst += [feature_names[k]]*m.ceil(bomps[k])
random.shuffle(lst)
text = str(lst)
text = text.replace("'", '')
text = text.replace(",", '')
text = text[1:-1]
figure(num=None, figsize=(16*4, 12*4), dpi=400, facecolor='w', edgecolor='k')
wordcloud = WordCloud(max_font_size=50, max_words=100, background_color="white",collocations=False).generate(text)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()
plt.title('WordCloud for hypothetical topic #' + str(j))
plt.savefig('Topic#' +str(j)+ "_WC.png") # save as png
plt.close() # display
这将产生图像,如我们的特色图像和以下 3 个主题:
**图 4。**我感兴趣的院系中对应于主题 0、1 和 4 的无监督检测到的子主题的代表性词云。副主题 2 可以在文章的专题图片中看到。
从这里开始,我决定使用以下主题标签:
1\. Network Dynamics
2\. Brain Machine Interfaces
3\. Cortical Representation
4\. Cognition
5\. Neurophysiology
第 2c 节。为主题分配插入作者出版历史。
现在,有了这些标签,我们实际上可以回过头来插入每个作者的出版历史,看看他们的出版物通常会分类成什么主题。就代码而言,这实际上非常简单。
newcol = []
for i in range(len(table)):
newcol += [cls.transform(vec.transform(eval(table.publications[i]))).argsort(axis=1)[:,-1]]
table['pubcats'] = newcol
export_csv = table.to_csv (r'C:\Users\Masters-PC\Desktop\ProfNetworks\FoIScholarData.csv', index = None, header=True, encoding = 'utf-16') table = pd.read_csv("FoIScholarData.csv", encoding = 'utf-16')
for i in range(len(table)):
topics = ['Network dynamics', 'Brain-Computer Interface', 'Cortical Processing', 'Cognition', 'Spike-train Analysis']
pubcats = eval(table.pubcats[i].replace('\n', '').replace(' ', ','))
values = [pubcats.count(j) for j in range(5)]
width = 0.5
plt.bar(topics, values, width, color='b')
plt.xticks(rotation = -15)
plt.gcf().subplots_adjust(bottom=0.22)
plt.title('Area of Emphasis (Topic Modelling) for ' + str(table.Name[i]))
plt.savefig(str(table.Name[i]) + "_TM.png") # save as png
这将为您提供一个新列,包含每个出版物的每个类别,以及一个如下所示的输出图:
图 5。华盛顿大学艾德丽安& # 183;费尔豪尔出版史的主题建模。
这将非常多变的工作,所以请不要弄脏你的手,并考虑修改你的模型的效用。例如,在我的例子中,我降低了一个出版物被归类为皮层处理的概率,通过将它的组成部分乘以因子 8,这对我来说似乎相当有效。
第 3 部分:更新合作者图表以反映感兴趣的主题。
在我们可以传递给 NetworkX 图形绘制函数的变量中,有一个选项可以改变每个节点的形状,以反映它的一个属性。在我们之前为图形绘制编写的任何循环中,我们可以添加以下用粗体书写的代码。
**shapes = ['s','o', 'd', 'p', 'h']**
for i in range(len(G.nodes())):
** pubcats = eval(table.pubcats[list(table.Name).index(list(G.nodes())[i])].replace('\n', '').replace(' ', ','))
counts = []
for j in range(5):
counts += [pubcats.count(j)]
shape = shapes[counts.index(max(counts))]**
nx.draw_networkx_nodes(G, pos, nodelist=[list(G.nodes())[i]],**node_shape=shape**,node_size=table.hindex[list(table.Name).index(list(G.nodes())[i])]*19,node_color=viridis(list(domains).index(table.Domain[list(table.Name).index(list(G.nodes())[i])])), **options)
我们定义的形状列表将节点分别做成正方形、圆形、菱形、五边形和六边形。 pubcats 变量只是解释我们在数据上生成的 pubcats 列,而 counts 和 shape 变量只是确定最终选择教员节点形状时最常见的发布主题类别。这可以针对每个教职员工子图和我们的整体循环可视化进行,结果应该类似于下图。
第 4 部分:更新您的个性化报告以纳入变更。
在第 1 部分中,我们制作了一份关于我们教授的数据的简明摘要,开始编辑笔记,并对文献进行更直接的搜索,以选择采访过程中作为我们审查的一部分。有了这些新图像,我们可以更新代码来反映这些新信息。只需替换第 1 部分第 5 节中的代码:
document.add_picture(table.Name[qt]+"_WF.png", width=Inches(5))
通过这段简短的代码:
newp = document.add_paragraph('')
nrun = newp.add_run()
#p.add_run('\n'+'Topics ' + str(table.topics[qt])[1:-1])
if os.path.exists(table.Name[qt]+"_WF2.png"):
nrun.add_picture(table.Name[qt]+"_WF2.png", width=Inches(3.4))
else:
nrun.add_picture(table.Name[qt]+"_WF.png", width=Inches(3.4))
nrun.add_picture(table.Name[qt]+"_TM.png", width=Inches(3.4))
记住还要用第 3 部分中描述的代码修改教员子图的生成,结果应该如下所示:
图 6。UCSF 的 Loren Frank 博士和加州大学伯克利分校的 Jack Gallant 博士的输出文档示例。
这些修改确实使表格内容更加丰富,并且有助于做笔记和选择问题。就我个人而言,我已经能够简明扼要地总结一个院系兴趣随时间的变化,他们特定的认同领域,以及他们合作的跨学科动态。
第 5 部分:创建摘要页面
我们在前面部分没有讨论的东西实际上是总结了我们所有机构的信息。我还没有以编程方式开发出一个 python-docx 代码来输出我满意的文档,但是我可以分享一个我手动创建的文档来分享我的想法,也许你们可以想出自己的想法。
图 7。手动构建的摘要页面,包含分析过程概述的主要输出。注意:这些数据是虚构的,并没有准确反映我的老师或感兴趣的学校。
本文的目的是简明扼要地总结您理解其余图表所需的大部分信息,并提供您感兴趣的机构的高水平概述。随着面试季的全面展开,我祝你好运!
类别树中的主题建模
LDA 和 NMF 的数字组合与 W2V 级联,将 1M+多语言记录分类到 275 节点、5 级深度类别树中。
—这个解决方案在全印AI Hackathon*中排名一万+ 第四,自动化多标签分类,在 Techgig 的 Code Gladiators 2020 中。*
个人备注:
当我得知以 1 分之差获得黑客马拉松前 3 名时,我的心情很复杂。虽然在决赛队伍中排名第四有点令人放心,尽管我独自参加,但一个充满激情的团队伙伴的空缺相当令人不安。
**黑客马拉松的挑战是用元信息对 100 多万篇多语言文章进行高精度的分类。**元信息表示隐藏在 URL 文本序列中的类别树信息以及每篇文章的标题。您可以浏览一下下面的输入数据集。
**上述文章将根据文章主题分类到一个类别层次中。**例如,“莫迪访华”被归类为“Politics^International ”,而“科罗纳票数创历史新高”被归类为“Health^Pandemic".”
由于上述数据集非常庞大,人工标注文章需要付出巨大的努力。我们的目标是尽可能减少手工劳动。因此,我们的目标是在上述数据集上进行主题建模。
主题建模 vs 分类
- 主题建模是一种**‘无监督’**ML 技术,用于将文档分类为不同的主题 s.
- 主题分类是一种**‘监督’**ML 技术,它消耗人工标记的数据,以在以后做出预测。
因此,很明显,我们需要做主题建模,因为输入的文章没有事先标记。但是主题建模不能保证准确的结果,尽管它很快,因为不需要培训。
因此,我们需要替代分类方法,并将它们安排为级联回退,同时改进主题建模结果。
解决方案的完整源代码可以在找到 这里 。
级联回退管道
- 主要方法:使用描述的类别树分类器
**关联节点类别中出现的单词的单词向量&文章描述,使用 Google Word2Vec 模型计算。**这个模型是在 Google News 数据集上进行预训练的,这个数据集类似于我们需要分类的输入数据。
2.回退 1:基于 URL 的类别树分类器
文件夹的顺序,从左到右体现了与文章相关的类别层次结构。例如,以下网址应归类为“新闻/政治/BJP 政治”。如果“方法 1”的置信度值低于阈值,则触发回退 1。
3.回退 2: LDA-NMF 组合模型
潜在狄利克雷分配(LDA) 是主题建模的经典解决方案。但在实践中,它给出了很大比例的错误分类。因此,非负矩阵分解(NMF) 也被使用,并在数值上与 LDA 结合,与多类二进制化器一起改进结果。
4.回退 3:使用描述的类别树分类器
如果上述所有方法都失败了,那么你可以将文章标记为**“未分类”**,或者将“主要方法”的输出作为最后的退路。
你也可以看看这个博客底部的**“改进”**标题,了解替代 fallback 4 的进一步的 Fallback 策略。
解决方案架构
文章、URL 和 LDA-NMF 分类器被合并
在解决方案实现之前,让我们构建输入类别树。
构建类别树
下面以 csv 文件的形式给出了对文档进行分类的类别树。
父子节点由^分隔
我们可以使用任何 python 数据结构来表示类别树。 AnyTree 包用于创建、操纵&遍历类别树输入。
上面的代码从输入的 cat_tree.csv 文件生成了树结构。
类别树
类别树结构可以使用 graphviz 可视化**。**
类别树描述
非英语文章可以很容易地翻译成“英语”使用谷歌 API。为了节省时间,请识别非英语行,并只将它们提供给下面的函数。
正在加载 Google Word2Vec 模型
谷歌的预训练模型包括 300 万个单词和短语的词汇向量,他们从谷歌新闻数据集中训练了大约 1000 亿个单词。向量长度为 300 个特征,训练数据集类似于我们的输入数据集。
主逻辑:类别树 Classn 使用描述
首先,我们将“长描述”中的单词与节点相关联以找到类别。
实现了一种深度算法,即搜索与文章描述中每个词相关的类别树节点的 GoogleNews 向量。多词分类得到智能处理不会提高相似度分数。该功能是解决方案的核心。
对类别树进行广度优先相关遍历以找出基于 Word2Vec 相似度的最佳类别树分配。
上面的代码足够复杂,可以通过分析文章中的每个单词(与节点类别中的单词相关联)来生成丰富的类别树。
**为了组合不同算法的结果,我们需要计算每篇文章的分类置信度。**因此,将(id、树、置信度)信息保存到 CSV 文件中。
使用基于列表中数值分布的数学公式计算置信度得分。该公式计算最高相似性值和第二高相似性值之间的差异,因为当前 2 个值接近时,它表示不明确。
调整类别树
具有不同含义的词的多词类别被组合成相似含义的词,使得词向量距离度量不会出错。例如,在单词向量中,“教育”与“金融”相似,因此被重命名为意思相似的单词“学校教育”。
后备 1:基于 URL 的类别树分类器
没有单一的信息可能足以对一篇文章进行适当的分类。首先,我们使用 URL 链接中嵌入的树信息来查找类别。
文章 ID 和相应的 URL
上面的代码将生成一个对应于每篇文章的 cat-tree 和置信度得分。请注意,当链接中没有足够的信息时,置信度为 0。
回退 2: LDA-NMF 组合模型
LDA 是一个概率生成过程,而 NMF 是一个线性代数方法将文档分类到不同的主题。
任何文档都被认为有与之相关联的潜在混合主题。类似地,主题被认为是可能产生的术语的混合。因此,我们需要计算两组概率分布参数。
- 给定一个文档 d 的主题 z 的概率: P (z | d)
2.给定题目 z 的条件 t 的概率: P ( t | z)
由此,的概率文档 d 的 生成 的术语 t,
P (t | d) =所有主题的前两个概率的乘积之和。
如果有 10 个主题,那么参数数量= 500 个文档 x 10 个主题+ 10 个主题 x 1000 个单词= 15K 个参数。(优于 500 *1000)
这种矩阵分解的概念叫做潜在狄利克雷分配。
BoW 模型(左)到 LDA 模型(右)
LDA 首先为每篇文章挑选一些主题,并为每个主题挑选一些词,这两者都用多项式分布表示。
贝塔分布:红色部分大概率,蓝色部分小概率。
LDA 模型有一个巨大的优势,它也给了我们一堆话题。该算法只会抛出一些主题,我们可能需要手动对它们进行更有意义的分组。
然而,由于无人监管,无论是 LDA 还是 NMF 都无法单独获得好的结果。因此,实施了 LDA 和 NMF 输出矩阵归一化后的数值组合,以计算出最大可能主题。
进行预处理后,在**“文章描述”上应用计数矢量器和 TF-IDF-矢量器计算文档-术语矩阵(DTM)分别馈入 LDA 和 NMF。**
LDA 计算
根据每个聚类中的前 20 个单词手动标记主题,如下所示。
LDA 的输出是大小为文章数*主题数的矩阵。每个单元格包含一篇文章属于一个主题的概率。
我们还可以使用 NMF 将 DTM 分解成两个矩阵,如下图,这在概念上与 LDA 模型类似,只是这里我们使用线性代数而不是概率模型。
NMF 的输出也是一个大小为文章数主题数的矩阵,解释保持不变。因此,我们可以在 y 轴上组合 LDA 和 NMF 的输出矩阵,以得到一百万 40 列。(输入= 1M 行。话题 LDA 和 NMF 各 20 个)
取每一行的 argmax(),找出 LDA 和 NMF 最可能的预测
注意:记得在矩阵串联之前做归一化。
组合模型产生更好的分类结果如下。
错误分类的置信度得分较低
唯一的缺点是这会导致第一级分类,而不是树形层次结构。所以是这个原因,我决定把它作为第二个退路。
**理想情况下,应使用分级 LDA,并将其作为解决方案管道中文章分类器之后的第一个备用分类器。**文章分类器仍然应该是主要逻辑,因为 Hierarchical-LDA 产生随机类别的词簇,这些词簇可能与我们想要的类别树不同。
合并描述、URL 和 LDA-NMF 分类器
文章描述分类器和 URL 分类器通过 LDA-NMF 组合模型合并成一个流水线,作为基于相应置信度得分的级联回退。
**描述分类器被赋予了很大的权重,因为 LDA-NMF 只给出了类别(而不是类别树),而 URL 通常不包含所需的信息。**如果 3 种方法都失败,则将其标记为“未分类”或使用文章分类器的结果,尽管不明确。
免责声明:由于上述解决方案是在 24 小时黑客马拉松中编写的,代码可能并不完美。它可以通过使用下面提到的方法进行改进,并且还需要对代码中的超参数进行更多的调整。
改进
- h-LDA:Hierarchical-LDA按照主题的层次结构对文档进行分组,并与 LDA-NMF 输出相结合[1]。
**2。知识图:**文章文本的知识图会生成单词的层次关系,类似于类别树。此信息可用于将文章映射到类别树。
- Guided LDA: 我们也可以使用 Guided LDA 为歧义类别设置一些种子词,当没有足够的文档来区分时。种子词将引导模型围绕这些类别收敛。
解决方案的完整源代码可以在这里找到。
参考文献
问题:组织广场支持中心文章
medium.com](https://medium.com/square-corner-blog/inferring-label-hierarchies-with-hlda-2093d0413337) [## 主题建模与 LDA 和 Quora 问题
潜在狄利克雷分配,非负矩阵分解
towardsdatascience.com](/topic-modeling-quora-questions-with-lda-nmf-aff8dce5e1dd) [## joewandy/hlda
层次潜在狄利克雷分配(hLDA)解决了从数据中学习主题层次的问题。的…
github.com](https://github.com/joewandy/hlda) [## 自然语言处理在线课程
新的!纳米学位课程必备知识该课程要求具备 Python,统计学,机器…
www.udacity.com](https://www.udacity.com/course/natural-language-processing-nanodegree–nd892)**
torch points 3d——点云深度学习的统一框架
点云深度学习变得简单
这是与Thomas Chaton和Loic Landrieu的联合出版物。
github:https://github.com/nicolas-chaulet/torch-points3d
随着越来越便宜的激光雷达传感器和更高效的摄影测量算法的出现,3D 点云数据比以往任何时候都更容易获取。深度学习社区已经接受了这一趋势,开发了新的网络架构来对 3D 数据执行各种任务。大量的可能性(数据布局、数据扩充、卷积策略等)使得找到最适合您的数据或问题的方法非常耗时。
使用 KPConv 获得的语义分段输出
我们的框架 Torch Points3D 被开发成点云数据的**Torch vision**:一个灵活且可扩展的框架,适用于研究基于点云的机器视觉的研究人员和工程师。想知道 KPConv 在点云注册方面表现如何吗?或者 PointNet++ 用随机采样代替 RandLa-Net 中建议的最远点采样进行物体检测?有了 Torch Points3D,您现在只需几行代码就可以尝试多种最先进的主干模型。
在快速回顾了点云的特性后,我们将介绍 Torch Points3D 的以下方面:
- 点云数据的优化数据布局
- 许多学术数据集的本地集成
- 快速和稳健的数据处理和数据扩充
- 针对一系列稀疏和基于点的架构测试了卷积核
- 用于访问数据集、数据转换和预配置模型的易于使用的 API
我们提供具有有用功能的训练脚本,例如模型检查点、登录 Tensorboard 和重量和偏差,使用脸书 Hydra 轻松配置超参数,等等。您也可以将我们的核心组件用于您最喜欢的训练框架,例如 PyTorchLightning 。
我们希望你喜欢阅读!我们欢迎您的反馈和贡献。
火炬点 3D 系统图,数据流以红色突出显示
点云为什么这么特别?
围绕 3D 稀疏点云的挑战在研究文献中有广泛描述,我们将仅提及 3D 点云和 2D 图像之间的一些关键差异:
- 每个点云可以有不同数量的点,即使它们具有相同的空间分辨率。这使得点云比图像更难构成均匀的批次。
- 激光雷达传感器捕捉三维世界中的表面,因此数据本质上是稀疏的。另一方面,照相机产生密集的采集。
- 在同一个点云中,点的密度各不相同,因此一些点可能有许多近邻来获得精确的几何信息,而其他点可能是孤立的。
- 点云在它们的点的索引的重新索引下是不变的,而像素的索引固有地链接到它们的坐标。这意味着用于处理点云的处理技术也必须是排列不变的。
点云数据和数据布局
有两种方法可以组装不同的批次。第一种是通过子采样和过采样使批中的所有元素大小相同,并在新的批维度中整理它们(就像整理图像一样)。我们将这种方法称为密集批处理。或者,您可以直接比较点维度中的样本,并跟踪每个样本中的点数。我们将这种方法称为打包批处理。
官方实现 PointNet++和 Relation-Shape CNN 以及类似的架构都使用密集批处理。除了实现简单之外,它的主要好处是可以利用与经典 2D 卷积相关的硬件优化。缺点是一些样本可能有许多重复点,而另一些样本被严重欠采样,丢弃了信息。每个样本的点数必须仔细选择。另一方面,KPConv 或 Minkowski Engine 等更新的模型使用打包批处理,这样就不需要对批处理中的元素进行采样,在某些情况下还可以显著降低内存需求。好消息是我们支持这两种数据格式,我们的数据加载器可以很容易地从一种格式切换到另一种格式。
对于相同的两个点云(红色和蓝色),左侧为密集批排序,右侧为打包批排序。
支持的核心数据集
该框架提供了对社区广泛使用的几个数据集的简单访问。目前,我们支持以下数据集:
要在您自己的框架内使用 Torch Points3D 数据集,您只需编写:
>>> dataset = ShapeNet("data_folder", split="train")
>>> dataset[0]
Data(pos=[5023, 3], x=[5023, 3], y=[5023])
我们所有的数据集都生成了数据对象,这是保存点的位置和特征的张量的简单结构 data.pos 和 data.x (它们实际上是 PyTorch 几何数据对象)。这里位置是原始 3D 位置,而 x 是每个点的法向量。在训练期间,数据对象还包含标签 y 以及特定模型或任务可能需要的任何其他信息。
数据处理流水线
数据处理管道是任何深度学习模型的关键组件。PyTorch Geometric 已经提供了许多有用的变换函数,我们已经用额外的 3D 特性丰富了这些函数;你可以在这个链接找到名单。除了这种大范围的数据转换,我们还添加了助手来直接从 yaml 配置文件实例化数据管道。这使得数据扩充成为一个透明的过程,这反过来又提高了可重复性,并使现有模型的调整更加容易。典型的配置文件如下所示:
data:
class: shapenet.ShapeNetDataset
task: segmentation
dataroot: data
normal: True # Use normal vectors as features
first_subsampling: 0.02 # Grid size of the input data
information
pre_transforms: # Offline transforms
- transform: NormalizeScale
- transform: GridSampling
params:
size: ${data.first_subsampling}
train_transforms: # Data augmentation pipeline
- transform: RandomNoise
params:
sigma: 0.01
clip: 0.05
- transform: RandomScaleAnisotropic
params:
scales: [0.9,1.1]
卷积核
大多数基于点的卷积网络借用了通用编码器/解码器的思想(或仅编码器)。编码器在密集的点云上操作,随着我们越深入,在每一层或每一组层之后,密集的点云被迭代地抽取。点本身支持特征向量,从一层到下一层通常需要两步:
- 向下采样点云;
- 对于下采样点云中的每个点,基于其在先前点云中的邻居的特征来计算特征向量。
简而言之,网络越深,点就越少,但相关的要素就越丰富。
点云的典型编码过程。每盏灯代表来自给定层的点云,红色球体突出显示来自前一层的哪些点用于构建新的特征向量(图像来源:Hugues Thomas)。
我们的框架支持三种可互换的采样策略:随机采样、最远点采样和网格采样。对于邻居搜索,大多数网络使用具有固定半径或 k 近邻的邻居。
KPConv 模式中不同层次的网格采样。
Torch Points3D 是在模块化的理念下创建的。我们为各种模型的卷积核补充了采样策略和邻居搜索算法,作为独立模块,可以包含在您自己的架构中。截至目前,以下模块可用:
- Pointnet++
- 关系-形状 CNN
- KPConv
- 闵可夫斯基引擎(通过官方 python 包)
例如,可以创建一个步进 KPConv 卷积模块,如下所示:
>>> import torch_points3d.modules.KPConv.blocks as kpconv_modules
>>> kpconv_layer = kpconv_modules.SimpleBlock(
down_conv_nn = [64,128],
grid_size=0.1,
prev_grid_size=0.05
)>>> kpconv_layer
SimpleBlock(
GridSampling(grid_size=0.1),
RadiusNeighbourFinder(radius=0.125),
(kp_conv): KPConvLayer(InF: 64, OutF: 128, kernel_pts: 15, radius: 0.0),
(bn): BatchNorm1d(128, eps=1e-05, momentum=0.02),
(activation): LeakyReLU(negative_slope=0.1)
)
我们的框架负责处理所有的细节。例如,它为卷积核和对应于该步进卷积的栅格采样操作符设置适当的参数。
那些核心卷积方案都已经在语义分割任务上得到验证**,并且已经与各自的作者密切合作再现了已发表的结果。我们计划在新的卷积方案发布后继续添加。**
应用程序接口
我们已经开始通过一个易于使用的 API 来公开框架的一部分。目前,该 API 支持:
- 用于点云数据扩充的常见数据转换
- 用于分段任务的通用数据集,具有批量整理功能和强大的指标跟踪器
- 用于 KPConv、Pointnet++和 Relation-Shape CNN 的基于 Unet 架构的主干模型
请参考https://torch-points 3d . readthedocs . io获取 API 的最新文档,或者看看我们可以在 colab 上运行的示例笔记本:
- 用关系形状分类 3D 物体 CNN
- 使用 KPConv 分割对象的部分
最后的话
Torch Points3D 是一个不断发展的框架,每天都会添加新功能,即将推出的一些功能包括:
- 集成更新的架构,如RandLa-Net;
- 集成更多任务,如点云配准、实例分割、图元拟合、离群点去除、点云完成等;
- 通过我们的模型 API 可以直接访问预先训练的模型。
我们还要衷心感谢所有参与该项目的人,特别是来自欧洲富士通实验室的 Sofiane Horache 、 Hugues Thomas 、 Tristan Heywood 和 R & D 团队。
参考
模型
数据集
ScanNet:室内场景的丰富注释 3D 重建
外部库
Torch:从特征到模型的语音数字识别
詹姆斯·奥尔在 Unsplash 上拍摄的照片
探索从语音数据中提取的特征,以及基于这些特征构建模型的不同方法。
语音数字数据集是 Tensorflow 语音命令数据集的子集,包括除数字 0-9 之外的其他录音。这里,我们只关注识别说出的数字。
数据集可以按如下方式下载。
辐条数字特征提取. ipynb
辐条数字特征提取. ipynb
评估指标
数字录音的子集相当平衡,每类大约有 2300 个样本。因此,准确性是评估模型性能的一个很好的方法。准确性是正确预测数与预测总数的比较。对于不平衡的数据集,这不是一个很好的性能测量方法,因为多数类的个体精度可能会盖过少数类。
循环学习率
在训练模型时,学习率逐渐降低以微调训练。为了提高学习率效率,可以应用循环学习率过程。这里,学习率在各时期的最小值和最大值之间波动,而不是单调下降。
初始训练率对模型的性能至关重要,低训练率可防止在训练开始时停滞不前,随后的波动会抑制局部最小值和平台值。
可以使用优化器实例来监控每个时期中使用的学习率。
该项目有三种方法对记录进行分类:
- 使用五个提取特征的逻辑回归— 76.19%的准确率。
- CNN 使用 Mel 光谱图—准确率 95.81%。
通过改变时期的数量和训练速率来重复训练模型。隐藏层的数量和每个层中的节点也是不同的。这里描述了每种方法的最佳架构和超参数。由于训练验证分割中的随机性,在重新训练时精度可能略有不同。
该项目的源代码是这里。
有五个。ipynb 文件:
- 特征提取-提取三种方法使用的必要 CSV 文件和特征。
- 要素可视化-为每个类中的两个示例绘制要素。
- 五个特征——使用五个提取的特征实现逻辑回归。
- 使用 Mel 谱图实现 CNN。
1.使用五个提取特征的逻辑回归
特征
提取的特征包括:
- 梅尔频率倒谱系数(MFCC)—构成声音频谱表示的系数,基于根据人类听觉系统响应(梅尔标度)间隔的频带。
- 色度 —与 12 个不同的音高等级相关。
- 梅尔谱图的平均值 —基于梅尔标度的谱图。
- 光谱对比度 —表示光谱的质心。
- Tonnetz —代表色调空间。
这些特征是大小为(20)、(12)、(128)、(7)和(6)的 NumPy 数组。这些被连接以形成大小为(173)的特征数组。标签被附加到数组的头部,并写入每个记录的 CSV 文件。
spoke ndigit-feature-extraction . ipynb
模型
线性回归模型总共有 1 个输入层、2 个隐藏层和 1 个 ReLu 激活的输出层。
Spokendigit —五个功能。ipynb
火车
Spokendigit —五个功能。ipynb
Spokendigit —五个功能。ipynb
Spokendigit —五个功能。ipynb
该模型在 CPU 上训练约 3 分钟,准确率为 76.19%。
验证损失图
最终验证损失从最小值开始增加很大程度。
验证准确度图
每个时期的最后学习率图
2.CNN 使用梅尔光谱图图像。
特征
该模型使用记录的 Mel 谱图图像。梅尔频谱图是频率被转换成梅尔标度的频谱图。从记录中提取特征并存储在驱动器中。这花了 4.5 个多小时。
spoke ndigit-feature-extraction . ipynb
模型
CNN.ipynb
火车
CNN.ipynb
CNN.ipynb
CNN.ipynb
该模型在 Colab GPU 上训练约 5 小时,准确率为 95.81%。
高精度再次归因于 Mel 标度。
验证损失图
验证准确度图
每个时期的最后学习率图
参考
- https://musicinformationretrieval.com/
- https://github . com/jurgenarias/Portfolio/tree/master/Voice % 20 分类/代码
- https://arxiv.org/abs/1506.01186
- https://en.wikipedia.org/wiki/Mel-frequency_cepstrum
Jupyter 笔记本中的简单文本注释
如何使用 tortus 注释工具
作者图片
在任何情感分析项目的核心都是一组良好的标记数据。预先标记的数据集可以在互联网上的各种网站上找到。但是…
- 如果你想出了一个自定义数据集没有标签怎么办?
- 如果您必须在继续您的项目之前提供这些标签该怎么办?
- 如果你不愿意付钱给外包贴标任务怎么办?
最近,我在为一个情感分析项目从 Twitter 流媒体 API 中检索文本数据时,就遇到了这个问题。我很快发现,如果没有好的工具,自己注释数据将是一项痛苦的任务。这是制作 tortus 背后的灵感,这是一个让在 Jupyter 笔记本中标记你的文本数据变得容易的工具!
开始安装扭转器并启用适当的扩展。
$ pip install tortus
$ jupyter nbextension enable --py widgetsnbextension
打开 Jupyter 笔记本后,将你的数据读入熊猫数据框。
import pandas as pd
movie_reviews = pd.read_csv('movie_reviews.csv')
导入包并创建一个 Tortus 的实例。
from tortus import Tortustortus = Tortus(df, text, num_records=10, id_column=None, annotations=None, random=True, labels=['Positve', 'Negative', 'Neutral'])
必需的参数:
- df :一个包含需要注释的文本的数据帧。这将是你之前读到的熊猫的数据帧。
- 文本:包含要注释的文本的列的名称。这应该是一个字符串。
可选参数:
- num_records :一次 tortus 会话中要注释的记录数。这必须是一个整数,默认为
10
条记录。 - id_column :包含文本的 ID 的列的名称。如果您的数据框中没有这样的列,请保留默认设置为
None
。给定的 ID 将成为原始数据帧的索引。如果您稍后需要连接数据集,这可能会有所帮助。 - 注解:带有先前在该工具中创建的注解的数据帧。它将拥有在后续注释中使用的正确列。当调用
annotate
方法防止重复时,该数据帧中的记录将被排除。如果该数据集没有以前的注释,默认设置为None
。 - 随机:决定记录是随机加载还是顺序加载到注释小部件中。默认设置为
True
。 - 标签:标注标签列表。标签必须是字符串。默认为
[‘Positive', ‘Negative', ‘Neutral']
。
使用annotate
方法访问注释小部件。
tortus.annotate()
诉讼侵权法
注释完成后,可以将它们存储到一个变量中,以后可以访问这些变量进行分析。
annotations = tortus.annotations
如果您需要将来的注释,请将此数据帧作为参数传递给 tortus 的后续实例。
这就结束了这篇关于如何使用 tortus 轻松地给你的文本数据添加标签的简短教程。
要转到 PyPI 上的包,点击此处。
要查看完整的源代码,点击这里。
如果您觉得这个包对您的批注任务有帮助,或者您有问题或改进建议,请随时通过 LinkedIn 与我联系,并分享您的反馈。
扔一些数据给你的巫师
摄影:卡特林·弗默斯/网飞
一篇分析《巫师》中不同角色及其说法的文章,并通过 Seaborn 将结果可视化
大约一个月前,我读了一篇关于可视化“布鲁克林九九”试播集脚本的好文章(感谢 Melody Zap 的精彩阅读)。现在,我最近狂看了《巫师》第一季,我想做一些类似的事情。我有兴趣找到以下问题的答案:
- 谁说的话最多?
- 一旦他/她有机会说话,谁平均用的词最多?
- 有多少单词被认为是停用词(它们没有任何意义)?
- 根据人物所说的,他们有多积极/中立/消极?
- 杰洛特第二常用的词是什么,假设“嗯”显然是第一个?
不得不提的是我只看过网飞系列和我不熟悉原著 书系列 也不熟悉其他衍生品(桌游、电脑游戏、卡牌游戏等等)。
底层代码
在我看来,没有必要为这种分析重新发明轮子,因此我使用 Melody Zap Github repo 作为起点。存储库本身可以在下面找到:
对布鲁克林九九数据(布鲁克林九九…
github.com](https://github.com/melozap/Brooklyn-99-Pilot-Analysis)
显然,我必须对 CSV 文件和 Jupyter 笔记本进行一些调整。如果你有兴趣找到我的代码版本,请访问我的 Github 库。
对《巫师》的前五集进行文本分析和情感分析,非常感谢 melozap
github.com](https://github.com/markusrenepae/witcher-analysis)
数据
CSV 文件的前 11 行(已审查)
为了这个分析,我使用了前五集的脚本,这些脚本是在 这里 找到的。它们都存储在一个 CSV 文件中,其中第一列代表一个字符,第二列是这个特定字符所说的文本行。因为这个脚本包含了大量的注释,我删除了所有放在方括号中的内容。
生成的文件包含 1479 行文本,在我看来,这是一个很大的工作量。一旦我得到了第 6、7 和 8 集的数据,我会很乐意更新这篇文章。
现在我们已经准备好进行分析了
谁说的最多?
在执行 Jupyter 笔记本中的脚本之前,我对结果有一些假设:
- 杰洛特有最多的台词,但说的话却不多,因为一般来说,他的台词都很短。
- 贾斯克尔话很多。他每集的字数(他不是每集都出现)一定是最高的。
- Yennefer 是从第 2 集进化到第 8 集(好吧,我们的数据到第 5 集)。因此,我相信她的每行字数在开始时较低,在接近结尾时较高。
我们开始吧!在我们的数据集中,哪个角色的线最多?
这个结果是有道理的,因为杰洛特只是每集出现的两个角色之一(另一个是希里)。
哪些人物总共使用了最多的词语?
在上图中,杰洛特使用了最多的单词,但差别没有上图那么显著。因此,我们可以说,平均来说,他的线很短。
在前五集里,杰洛特一共出现了五集,叶妮弗出现了四次,贾斯基尔出现了三次。因此,我们已经可以看到,贾斯基尔每集说的话最多(大约 800 个),而杰洛特每集只用了大约 540 个词,叶妮弗每集还少 500 个词。我们过会儿将回到它。
在我们的数据集中,哪些角色平均使用最多的单词?
这里,在图中,我们用蜡烛表示平均值和相应的标准偏差。不出所料,贾斯基尔确实平均使用了最多的单词。这主要是因为第二集结尾那首朗朗上口的“扔硬币给你的巫师”的缘故。贾斯基尔在三行中使用了 312 个单词。
其他主要角色(杰洛特、叶妮弗、希里)每行使用大约 10 个单词。
最常见的单词
现在我们知道了一些关于单词和行数的分布,我们现在可以找出最流行的单词。我不会去分析这个系列中的每个角色,但是我会给出一些关于杰洛特、贾斯基尔、叶内费尔和希里的见解。
结果不言而喻——杰洛特第二受欢迎的词是“想要”、“一”和“杀死”。最受欢迎的,显然是“嗯”。
所有人物中最流行的词是什么?
清理文本
首先,你注意到最流行的单词中没有语法冠词吗?这是因为对整个脚本应用了清理过滤器:
def clean(text):
text = word_tokenize(text)
text = [word.lower() for word in text]
punct = str.maketrans('', '', string.punctuation)
text = [word.translate(punct) for word in text]
text = [word for word in text if word.isalpha()]
text = [word for word in text if not word in stopwords]
return " ".join(text)
该函数执行以下操作:
- 文本被标记化,或者简单地说——对于每一行,我们创建一个列表,其中单词是它的元素。
- 所有单词都是小写的。
- 标点符号被删除。
- 字母数字单词被删除(在我们的例子中,这不会有任何影响)。
- 停用词(大多是不提供任何意义的语法文章)被删除。
这造成了巨大的差异。再来看看平均谁用的词最多。
好像用了 clean 功能后差不多 50%用过的字都丢了。
情感分析
前五集整体情绪如何?
不同人物说出来的台词里的情是什么?
可以看出,中性情绪占主导,除了 Jaskier。他比其他角色更积极,这在系列中是显而易见的。
Yennefer 真的进化了吗?
Yennefer 一开始是一只害羞的“小猪”,我的一个假设是她的每行字数随着时间的推移而增加。
事实证明,这个假设是正确的。我们可以看到的另一件事是,最受关注的杰洛特在五集里的每行字数几乎相同。
摘要
《巫师》第一季非常棒,它为接下来的几季树立了很大的期望。更棒的是制作假设并用简单的代码检查它们的过程。写这篇文章帮助我理解了 Seaborn 可视化工具,希望不久我就能使用它。
我要再次感谢 Melody Zap 编写了如此不可思议的项目,并允许我使用她的 Github 库来构建我的项目版本。
如果你想在这篇文章中看到一些额外的东西,或者有一些真正重要的东西不见了,让我知道。我希望我拿到最后三集的剧本后能尽快更新这篇文章。
资源
- Melody Zap Github 资源库:https://github.com/melozap/Brooklyn-99-Pilot-Analysis
- 抄本粉丝:https://transcripts.fandom.com/wiki/The_Witcher
- 我自己的这个项目的 Github 库:https://github.com/markusrenepae/witcher-analysis
边缘无触摸显示界面
***启用边缘 HCI:*多线程手势&采用 Intel OpenVINO AI 模型的信息亭声音控制。眼睛使眼色&嘴巴方面用数值模式
个人注: 获得 大奖 在 【深度学习超级英雄挑战赛】 与 Intel 为这个项目举办的 Hackster.io。尽管奖金高达 1000 美元&的英特尔 Movidius NCS 2 棒,但在一连串的差点失手之后,终于击中靶心,这给人以安慰。当然,隧道的尽头有光:)
**大赛链接:https://www.hackster.io/contests/DLSuperheroes
互动公共信息亭现在被广泛使用。银行(自动取款机)、机场(登记)、政府(电子政务)、零售(产品目录)、医疗保健(预约)、学校(出勤)、企业(注册)、活动(信息)等等。随着企业转向信息亭以提供更好的服务,所有公共设备的免触摸交互已经成为减缓无处不在的 电晕病毒传播的必要条件。
*手势或语音导航似乎可以解决上述问题,**但是这种设备在分析这种输入时会受到资源的限制。*你有没有注意到你的手机语音助手,无论是 Siri 还是 GAssist,在手机离线时都会放弃?当你在偏远的道路上行驶时,你的语音车载信息娱乐系统无法响应。即使是传统的计算机也无法同时运行多个人工智能模型。
在你的设备上完成这一切不是很好吗?想象一个可以从卧床不起的病人那里获取视觉或声音提示的床边辅助设备。随着英特尔 OpenVINO 的出现,这成为可能。它通过进行硬件意识优化,从边缘实现并加速深度学习推理。 OpenVINO 支持 CPU、iGPU、VPU、FPGA 和 GNAs。如果你想尝试一下,树莓派和英特尔 Movidius NCS 2 将是你的最佳选择。
在这篇博客中,我们将尝试构建一个人机交互(HCI)模块 ,它智能地协调 5 个并发运行的 AI 模型,一个给另一个喂食。用于面部检测、头部姿态估计、面部标志计算和凝视角度估计的 AI 模型识别手势控制输入并触发映射的动作。部署一个子线程来运行离线语音识别,该子线程与父进程通信以基于用户话语给出并行控制命令,从而辅助和增强手势控制。
博客摘要和项目进展
如果你喜欢这个项目,请竖起大拇指 这里
解决方案源代码可以在这里找到。
架构图
下面解释了体系结构图中的每个组件。
作者图片
控制模式
系统中定义了 4 种控制模式,用于确定用户输入的模式。我们可以使用手势在控制模式之间切换。
- 控制模式 0:无控制
手势和声音导航关闭 - 控制模式 1:注视角度控制
鼠标随眼睛注视角度移动(更快) - 控制模式 2:头部姿态控制 鼠标随着头部方向的改变而移动(较慢)
- 控制模式 3:声音控制 鼠标根据用户说话向 4 个方向滑动并打字
校准步骤
为了将 3D 凝视取向角度转换为 2D 屏幕尺寸,系统必须知道与屏幕对角相对应的偏航和俯仰角度。给定对角的这两个角度,我们可以在屏幕上为中间(偏航、俯仰)角度插入(x,y)位置。
因此,当应用程序启动时,将提示用户查看屏幕的对角。这样的校准步骤需要将凝视角度的变化映射到屏幕的尺寸和形状,以便“凝视模式”正常运行。
没有校准,系统也可以运行,尽管牺牲了通用性。为了演示,当系统处于“头部姿态”模式时,头部方向的相对变化被用作移动鼠标指针的度量。
控制器的功能是用目光和头部移动鼠标
手势检测管道
在输入视频流上执行四个预训练的 OpenVINO 模型,一个馈送到另一个,以检测 a)面部位置 b)头部姿态 c)面部标志和 d)凝视角度。
***a)面部检测:*使用了具有高效深度方向卷积的修剪过的 MobileNet 骨干网。该模型输出图像中面部的(x,y)坐标,该坐标作为输入被馈送到步骤(b)和©
b)头部姿态估计:模型 输出头部的偏航角、俯仰角和滚转角,将面部图像作为步骤(a)的输入
笛卡尔坐标系中偏航角、俯仰角和滚转角的可视化
***c)面部标志:*一个自定义 CNN 用于估计 35 个面部标志。
图片由提供
如上所述,该模型将来自步骤(a)的裁剪的面部图像作为输入,并计算面部标志。识别面部手势需要这样一张详细的地图,尽管的计算需求是 地标回归模型 的两倍(0.042 对 0.021 GFlops),后者仅给出 5 个面部地标。
***d)视线估计:*自定义类 VGG CNN进行视线方向估计。
该网络采用 3 个输入:左眼图像、右眼图像和三个头部姿态角度—(偏航、俯仰和滚动) —并输出笛卡尔坐标系中的 3d 凝视矢量。
视线注视向量的可视化
后处理模型输出
为了将一个模型的输出作为输入提供给另一个模型,需要对每个模型的返回值进行解码和后处理。
例如,为了确定凝视角度,头部方向需要与来自凝视模型的矢量输出进行数字组合,如下所示。
类似地,面部标志模型返回输入图像大小的比率。因此,我们需要将输出乘以图像宽度和高度来计算 35 个地标的(x,y)坐标。
需要处理从地标模型返回的输出面部点
虽然面部标志和凝视估计模型的输出可以如上容易地进行后处理,但是头部姿势估计模型输出的转换稍微复杂一些。
欧拉角到旋转矩阵
注意"头部姿态估计"模型仅输出姿态,即头部的偏航、俯仰和滚动角。为了获得相应的方向向量,我们需要使用姿态来计算旋转矩阵。
i) 偏航 是 α 绕 z 轴的逆时针旋转。旋转矩阵由下式给出:
ii) 俯仰 是 β 绕 y 轴逆时针旋转。旋转矩阵由下式给出:
iii) Roll 是γ绕 x 轴逆时针旋转。旋转矩阵由下式给出
我们可以沿着三个轴一个接一个地旋转,以任何方向放置 3D 物体。因此,要计算方向向量,需要将上述 3 个矩阵相乘。
眨眼检测
*到目前为止,我们已经使用头部和凝视来控制鼠标指针。但是**要使用 kiosk,你还需要触发事件,*比如‘左键单击’,‘右键单击’,‘滚动’,‘拖动’等等。
为此,需要将一组预定义的手势映射到每个事件,并从视觉输入中识别。两个事件可以映射到左眼和右眼的“眨眼”事件,但是它们需要被识别为“眨眼”。
*你可以很容易地注意到,当眼睛睁开时,***的白色像素数量会突然增加,而当眼睛闭上时,则会减少。我们可以通过计算白色像素来区分睁眼和闭眼。
眨眼检测
但在现实世界中,上述逻辑并不可靠,因为白色像素值本身可以变化。我们总是可以使用深度学习或 ML 技术来分类,但是为了提高效率,建议使用数值解决方案,尤其是当您为边缘设备编码时。
让我们看看如何通过 4 个步骤用数字信号来检测眨眼!****
- 计算 0–255 范围内的像素频率(直方图)
****2。计算直方图中非零像素的分布。当一只眼睛闭上时,价差会突然下降,反之亦然。
3。尝试在上述信号的尾端拟合一条反 s 形曲线。
4.如果发现拟合成功,则确认拟合曲线的“下降”形状,并声明为“眨眼”事件。(无曲线拟合=眼睛没有眨眼)
算法解释:
如果以上步骤不清楚,那么看看当睁开的眼睛闭上时,直方图分布图是如何下降的。
图 1 左眼眨眼时的直方图分布
给定上述信号,你可以想象当眼睛睁开几秒钟时,曲线会变成“S”形。这可以使用 sigmoid 函数进行数学参数化 。
但是由于我们需要检测上面所示的“眨眼”事件,曲线的形状将采用反 s 形函数的形式。要绕 x 轴翻转 sigmoid 函数,找到 f(-x)
反 s 形函数
使用任何在线函数可视化工具来绘制上述函数并改变参数,以查看反向“S”形状如何变化*(以适应上述**T21【图直方图分布 )*****
因此,如果通过参数曲线拟合算法在直方图分布曲线的末端发现任何相似的形状,那么我们可以称之为‘眨眼’。曲线拟合算法试图解决一个非线性最小二乘问题。****
反 s 形曲线拟合:左眼和右眼眨眼
注:计算上述内容的有效方法可以是,
- 考虑非零直方图分布中“n”个最近值的条带。
- 计算带材前端和尾端“k”值的中值&标准值。****
- 如果中值>阈值和两个标准<阈值不同,则检测眨眼事件,因为它很可能是一个反 s 形。**
或者,我们也可以使用下面的算法来寻找眨眼。
- 取直方图分布值的的第一个微分****
- 在第一个微分值中找到峰值,以找到突然峰值****
- 寻找信号的反射和寻找峰值寻找突然下降**
- 如果在上述两个步骤中都发现峰值,则*闪烁*******
- 如果只在反射中发现峰值,那么它是眨眼事件。
上述方法比曲线拟合更有效,但可能导致许多假阳性,因为峰值检测并不总是可靠的,尤其是在低光下。中间道路方法将使用中间值和标准偏差来估计曲线的形状。
口长宽比(MAR)
在这篇经典的面部标志论文中,眼睛纵横比(耳朵)被计算出来以确定眨眼。
眼睛闭上时,耳值会下降。
我们不能使用上面的公式来确定眼睛姿态,因为我们的模型没有估计这样密集的地标图。然而,受 EAR 的启发,我们可以基于从 OpenVINO 模型获得的可用 4 个地标来计算 MAR ,如下。
高效标记公式
两个手势事件可以使用 MAR:
- 如果标记了>阈值*,那么人就是微笑***
- 如果标记了<阈值*,那么这个人正在打哈欠***
我们可以自由地附加两个与这两个手势相对应的命令。
线程和进程线程通信
为了增强控制,我们还可以启用基于声音的导航,以及手势控制。然而,系统在分析来自输入视频流的图像帧时,需要连续监控用户话语 以识别命令。
因此,在不同的线程中运行语音识别模型并让子线程与父进程通信自然是明智的。子线程将识别移动鼠标或在屏幕上书写的语音命令,使用 Python 中的共享队列数据结构将其传递给父线程(如下所示)。
父进程将运行所有上述 AI 模型和手势识别所需的计算,以启用头部和凝视控制模式。因此,可以并行地获取手势和声音控制命令,但是为了可用性,在这个项目中,我们选择在控制模式 3 中单独获取声音命令。
语音识别
***为了解码声波,我们使用 OpenVINO 特征提取和解码器库,*接收并转录来自麦克风的音频。我们已经使用了这里提到的语音库在边缘上运行语音识别,不上线。
由于识别模型是以牺牲准确性为代价进行优化的,因此需要进行一些调整来识别口头命令。首先,我们限制命令词汇,只说‘上’,‘下’,‘左’&,‘右’。其次,命令词的相似发音同义词被存储在字典中以找到最佳匹配。例如,“右”命令可以被识别为“写”。**
这个函数是这样编写的,命令和同义词可以很容易地扩展。为了启用用户输入,还启用了语音写入功能。这使得用户能够输入字母和数字。如 : PNR 号。
线程从相似声音“同义词”中识别命令,并插入到 STT 队列中
手势控制和鼠标导航
手势控制命令配置如下。但是,您可以轻松地更改手势命令映射。
手势映射可以随意修改
***使用 *pyautogui 库控制鼠标指针。move()、moveTo()、click()、drag()、scroll()、write()等函数用于触发上述手势对应的事件。
粘性特征及优化
***眼睛的凝视或头部的姿势至少会持续改变一点,即使是无意的。*此类自然动作不应被视为命令,否则鼠标指针会变得抖动。因此,我们引入了一个“粘性”参数,在该参数范围内,运动被忽略。这大大增加了手势控制的稳定性和可用性。
最后, 英特尔 VTune 评测器用于查找热点并优化应用代码。一个 shell 脚本 vtune_script.sh 被提供给 VTune GUI,它使用合适的参数启动项目。
结论
项目展示了英特尔 OpenVINO 顺序和并行处理多个边缘人工智能模型的能力。许多控制输入也用于证明灵活性。但是要部署定制解决方案,你可以选择你认为合适的控件。
例如,凝视控制对于大屏幕可能是理想的,而头部姿势控制对于笔记本电脑屏幕可能是理想的。无论哪种方式,声音控制都有助于接受自定义表单条目或语音命令。手势-动作映射也可以修改。然而,你可以理解的一点是,在边缘上链接多个硬件优化的人工智能模型的可能性,加上高效的数值计算来解决有趣的问题。
解决方案源代码可以在这里找到。
如果您有任何疑问或建议,您可以联系我* 这里***
如果你喜欢这个项目,请竖起大拇指 这里
参考文献
[1] 英特尔 OpenVINO 官方文档:https://docs.openvinotoolkit.org
[2] 英特尔 Edge AI for IoT Nanodegree 由 Udacity 提供。灵感来自期末课程项目。https://classroom.udacity.com/nanodegrees/nd131
[3] 布拉格捷克技术大学电子工程系 Tereza Soukupova 和 Jan Cech 使用面部标志进行实时眨眼检测。
Python Itertools 之旅
让我们探索两个伟大的 Python 库——ITER tools 和 more_itertools,看看如何利用它们进行数据处理…
特雷弗·科尔在 Unsplash 上拍摄的照片
有很多很棒的 Python 库,但大多数都无法与内置的itertools
和more-itertools
相提并论。当涉及到在 Python 中处理/迭代一些数据时,这两个库实际上是整个厨房水池。然而,乍一看,这些库中的函数似乎并不那么有用,所以让我们来看看(在我看来)最有趣的函数,包括如何充分利用它们的例子!
压缩
当涉及到过滤序列时,您有相当多的选项,其中之一是compress
,它采用 iterable 和 boolean 选择器,并输出 iterable 的项目,其中选择器中对应的元素是True
。
我们可以使用它将一个序列的过滤结果应用到另一个序列,就像上面的例子一样,我们创建了一个日期列表,其中对应的计数大于 3。
积聚
顾名思义——我们将使用这个函数来累加一些(二元)函数的结果。这方面的例子可以是运行最大值或阶乘:
如果您不关心中间结果,您可以使用functools.reduce
(在其他语言中称为fold
),它只保留最终值,并且内存效率更高。
循环
这个函数采用 iterable 并从中创建无限循环。例如,在游戏中,玩家轮流游戏,这是很有用的。你可以用cycle
做的另一件很酷的事情是创建简单的无限旋转器:
三通
来自itertools
模块的最后一个是tee
,这个函数从一个迭代器创建多个迭代器,这允许我们记住发生了什么。例如来自 itertools recipes (还有more_itertools
)的pairwise
函数,它从输入 iterable 返回成对的值(当前值和前一个值):
每当您需要指向同一数据流的多个单独指针时,这个函数就非常方便。不过在使用它的时候要小心,因为它在内存方面的开销非常大。同样需要注意的是,在对原始 iterable 使用了tee
之后,不应该再使用它,因为它会弄乱(无意中推进)那些新的tee
对象。
更多 _itertools
现在,让我们仔细看看more_itertools
图书馆提供了什么,因为有许多有趣的功能,你可能没有听说过。
划分
从more_itertools
开始的第一个是divide
。顾名思义,它将 iterable 分成若干个子 iterable。正如你在下面的例子中看到的,子元素的长度可能不一样,因为它取决于被划分的元素的数量和子元素的数量。
划分
使用这个函数,我们也将划分我们的 iterable,但是这次使用一个谓词:
在上面的第一个例子中,我们使用简单的 lambda 函数将日期列表分为最近的和旧的。对于第二个例子,我们基于扩展名对文件进行分区,同样使用 lambda 函数,该函数将文件名分为名称和扩展名,并检查扩展名是否在允许的列表中。
连续组
如果您需要查找连续的数字、日期、字母、布尔或任何其他可排序的对象,那么您可能会发现consecutive_groups
很方便:
在这个例子中,我们有一个日期列表,其中一些日期是连续的。为了能够将这些日期传递给consecutive_groups
函数,我们首先必须将它们转换成序数。然后使用列表理解,我们迭代由consecutive_groups
创建的连续序数日期组,并使用map
和fromordinal
函数将它们转换回datetime.datetime
。
副作用
假设您需要在迭代条目列表时产生副作用。这种副作用可能是,例如,写入日志、写入文件或类似于以下示例中计数发生的事件数:
我们声明了一个简单的函数,它将在每次被调用时递增一个计数器。然后,这个函数和名为events
的非特定 iterable 一起传递给side_effect
。稍后当事件迭代器被使用时,它将为每一项调用increment_num_events
,给我们最终的事件计数。
倒塌
这是另一个名为flatten
的more_itertools
函数的更强大版本。collapse
允许你展平多层嵌套。它还允许您指定基本类型,以便您可以在剩余一层列表/元组的情况下停止扁平化。此功能的一个用例是展平熊猫。以下是一些更通用的例子:
第一个通过折叠os.walk
返回的可重复项来生成文件和目录路径的列表。在第二种方法中,我们采用嵌套列表形式的树数据结构,并将其折叠以获得所述树的所有节点的平面列表。
拆分 _at
回到拆分数据。split_at
函数根据谓词将 iterable 拆分成列表。这类似于字符串的基本split
,但是这里我们用 iterable 代替字符串,用谓词函数代替分隔符:
上面,我们使用行列表来模拟文本文件。该*“文本文件”*包含带有-------------
的行,用作分隔符。这就是我们用来把这些行分割成独立列表的谓词。
水桶
如果您需要根据某种条件将 iterable 分成多个桶,那么bucket
就是您要找的。它通过使用键函数拆分输入可迭代对象来创建子可迭代对象:
这里我们展示了如何基于条目类型存储 iterable。我们首先声明几种类型的形状,并创建它们的列表。当我们用上面的键函数调用这个列表上的bucket
时,我们创建了一个桶对象。这个对象像内置的Python一样支持查找。此外,正如您所看到的,整个 bucket 对象中的每一项都是一个生成器,因此我们需要对它调用list
来实际获取其中的值。
Map_reduce
对于所有数据科学人士来说,这个库中最有趣的函数可能就是map_reduce
。我不打算详细说明 MapReduce 如何工作,因为这不是本文的目的,已经有很多关于它的文章了。我要给你展示的是如何使用它:
这个 MapReduce 实现允许我们指定 3 个函数: key 函数(用于分类), value 函数(用于转换),最后 reduce 函数(用于缩减)。这些函数中的一些可以省略,以产生 MapReduce 过程中的中间步骤,如上所示。
一起排序\
如果您处理的是数据的电子表格,那么很有可能您需要按某一列对其进行排序。这对于sort_together
来说是一个简单的任务。它允许我们指定根据哪一列对数据进行排序:
该函数的输入是可迭代对象(列)和key_list
的列表,它告诉sort_together
使用哪些可迭代对象进行排序以及优先级。在上述示例中,首先按出生日期对“表格”进行排序,然后按在列更新的进行排序。
可寻找的
我们都喜欢迭代器,但是在 Python 中你应该一直小心使用它们,因为它们的一个特性是它们消耗提供的 iterable。感谢seekable
,他们不必如此:
seekable
是一个将 iterable 封装在一个对象中的函数,使得在迭代器中来回遍历成为可能,甚至在一些元素被消耗之后。在这个例子中,你可以看到我们在遍历了整个迭代器后得到了StopIteration
异常,但是我们可以返回并继续处理它。
过滤 _ 例外
让我们看看下面的场景:您收到了混合数据,其中包含文本和数字,并且都是字符串形式。然而,您只想处理数字(浮点数/整数):
filter_except
通过将 iterable 的元素传递给提供的函数(float
)并检查它是否抛出错误(TypeError, ValueError
)来过滤输入 iterable 的项目,只保留通过检查的元素。
每个都是唯一的
unique_to_each
是more_itertools
库中比较晦涩的函数之一。它接受一堆 iterables 并从每个 iterables 中返回元素,这些元素不在另一个 iterables 中。最好看看例子:
在这里,我们使用邻接表(实际上是dict
)来定义图数据结构。然后,我们将每个节点的邻居作为一个集合传递给unique_to_each
。它输出的是一个节点列表,如果相应的节点被删除,这些节点将被隔离。
数字 _ 范围
这种方法有很多用例,因为需要迭代一系列非整数值是很常见的:
numeric_range
的好处在于它的行为方式与基本的range
相同。您可以像上面的例子一样指定start
、stop
和step
参数,这里我们首先使用1.7
和3.5
之间的小数,步长为0.3
,然后使用2020/2/10
和2020/2/15
之间的日期,步长为 2 天。
制作 _ 装饰
最后但同样重要的是,make_decorator
使我们能够使用其他 itertools 作为装饰器,从而修改其他函数的输出,产生迭代器:
这个例子使用了map_except
函数并从中创建了 decorator。这个装饰器将使用被装饰函数的结果作为它的第二个参数(result_index=1
)。在我们的例子中,修饰函数是read_file
,它模拟读取某个文件的数据,并输出一个可能是也可能不是浮点数的字符串列表。然而,输出首先被传递给 decorator,它映射并过滤掉所有不需要的项目,只给我们留下浮动。
结论
我希望你在这篇文章中学到了一些新的东西,因为如果你经常处理大量的数据,那么itertools
和more_itertools
可以让你的生活变得更加容易。然而,使用这些库和函数需要一些练习才能有效。所以,如果你认为你可以利用这篇文章中展示的一些东西,那么就去检查一下 itertools recipes 或者强迫自己尽可能多地使用这些东西,以适应它。😉
本文最初发布于martinheinz . dev
迈向数据科学博士
想升学?这里有一些情报!
我在澳大利亚开始高等教育已经有一年半多了。我选择的大学是澳大利亚国立大学(ANU)。在我写这篇文章的时候,它在 2020 年泰晤士大学排名中排名第 50 位。除了大学的排名之外,在选择大学的过程中还有许多其他的考虑因素。此外,在申请奖学金进行更高层次的研究之前、期间和之后,人们可能会想到的事情很少。所以我决定分享我的经历,也许有一天会发现这很有用。所以给你提个醒。如果你诚实地尝试,这并不难!
去哪里学习?
对于任何想在毕业后学习的人来说,这是一个关键的决定。我在选择国家时的几个主要考虑因素是:
- 研究和资助:我对生物信息学研究感兴趣。澳大利亚似乎是一个注重基因研究和相关领域研究的国家。几乎在所有情况下,博士研究都由学费、生活费(津贴)和某些职业发展津贴(会议出席和实验室设备)资助。
- 生活条件:津贴可以涵盖所有开支,不必强调覆盖工作时间以补偿房租和其他费用。
- 课程时长:通常大部分澳洲博士需要 3 到 4 年的时间。此外,我没有要求进行硕士学位,因为我做了一个荣誉学位的重要研究部分。毕竟,我不想再花 5 年多的时间学习。
- 入学要求:澳洲——雅思(ANU,工科综合 6+分)或者托福。美国和其他一些目的地——GRE(请阅读各个大学的要求)。
数据科学博士有哪些选择?
数据科学是一个广阔的领域,也是目前最具趋势的领域之一。在数据科学中,来自计算机科学和统计学等领域的知识被用来分析数据和了解关于数据的有趣的事情。因此,数学、统计学和计算机科学将是潜在博士生涯中有待发现的核心知识领域。
攻读数据科学博士学位可能意味着你将在生物学、医学、经济学、金融、农业和工程等领域开展研究,但不限于这些领域。随着生成和存储数据的方法的发展,所有这些领域都产生了大量的数据,创造了大量的研究机会。尽管是特定领域,数据科学博士学位将涉及基于收集的数据进行方法开发。然而,任何博士学位的最终目标都是通过创新方法等对人类知识做出贡献。总之,可以得出结论,在更高的抽象层次上,任何专业都有一个涉及数据驱动创新的点。
怎么找主管?
找到一个多产的工作人员来监督你永远是第一步。当我在寻找导师时,我找了蒙纳士大学、T2 大学、ANU 大学和 T4 大学。如果你知道什么可能是你感兴趣的领域(因为这是很难立刻定义的事情),找到一个主管总是很容易的。重要的事情先来!访问大学网站或者谷歌*“莫纳什大学/ANU 大学/尤尼梅尔大学/UNSW 大学/QUT 大学的导师”*或者任何你喜欢的大学。您可能会发现以下链接很有用。
- https://findanexpert.unimelb.edu.au/大学
- https://researchers.anu.edu.au/
- 莫纳什:https://www.monash.edu/research/our-researchers/find
- https://research.unsw.edu.au/finding-supervisor
- https://www.qut.edu.au/research/our-experts
如何接近主管?
这是你在开始学习之前可能要做的最困难和最困惑的事情之一。为了使事情变得简单,让我们如下缩小这一步。
最初的联系方式是大多数时候(如果不是总是)通过电子邮件。确保你的邮件给人留下好印象!这里有一个样本!
尊敬的教授姓名,
我是你的名字,来自你的机构。我于去年 12 月完成了计算机科学与工程的学士学位,我期待着获得博士学位的合适机会。我对在研究领域进行高等研究感兴趣,并且我看到了你在研究领域作为一名多产研究员的简介。
我读过你在研究领域的研究工作。延伸表达你的兴趣领域,提及一些你潜在的主管感兴趣的工作。我想问一下你是否愿意指导一个关于研究领域或相关领域的项目,或者对一个你想让博士生参与的项目有什么想法。
添加一个小句子,提及你最后一年的荣誉项目、一两篇重要出版物以及你目前的状态
提及你的学术地位(GPA/排名),让你看起来很有能力,以及你的一些主要成就
我感谢你宝贵的时间。期待一个有利的答复。
为了方便读者,我留下了我的简历和成绩单的链接。然而,你可能会注意到我的成绩单是不完整的(不是官方的)。因为不一定要这样。只要你能在以后的日子里公开一份原始拷贝,你就可以如实地展示你目前的状态。简历应该是学术风格的,并确保对于一个忙碌的人来说是简单易懂的。让一切都变得短暂而甜蜜!
我想到分享这个信息,因为它在开始时令人困惑。如果你对澳洲研究不感兴趣,跳过这一步。万事如意!与你的朋友分享。
干杯!
走向数据的万维网
照片由格雷格·拉科齐在Unsplash——https://unsplash.com/@grakozy拍摄
使用软件历史上最成功、可扩展的模式来解决数据科学和分析中最糟糕的问题。
数据,数据,无处不在…
…洋溢着 巨大的 潜能 价值 对于科学、商业和社会的发现。
不幸的是,对于价值发现/提取目的而言,大多数收集的数据的实际效用大大降低——就像喝咸海水一样,成本/收益是净损失。这是为什么?
- 收集的数据遭受极端变化——在编码格式、访问方法和定制的特定领域模式方面。即使有可能将整个数据源重新格式化和重组为标准的、与 SQL 兼容的表,仍然存在连接和解密大量不同的表/列模式的不可能任务。
- 收集的数据遭受内容 的不完整性——我说的不仅仅是缺失值。数据代表了什么?为什么要收集这些数据?设计/目的是什么?数据是从谁那里收集的,为谁收集的?存在哪些偏见或特殊情况?典型地,关于数据源的缺失信息被存储在领域专家的大脑中——也就是人类。这些人很少与他们拥有或曾经拥有的数据源在同一个地方。
各种数据来源
一片纯净、复杂的雪花,
迅速融化。
旧软件不能处理新的数据类型
所有的软件在发货的那一天就变旧了。
为价值发现/提取而构建的软件和算法被设计为在以显式格式或模式构建的源数据上运行。
因此任何软件的适用范围仅限于符合严格输入要求的数据源。有时,只需要重命名一个字段,算法或软件工具就会立即燃烧起来——或者更糟——产生无错的损坏工件(恐怖表情)。
例如,在数据科学中,已经投入了很多努力来将数据源转换成单个表(即一个 数据帧 ),其中一列表示要预测的类或结果,而其他列表示特征。因此,许多机器学习算法可以利用数据帧作为输入结构。
你通常无法让算法适应数据,所以你只能让数据适应算法或者换句话说 — 如果你不能加入他们(在你的算法中),击败他们*(屈服)*。**
对于像我自己一样有经验的人来说,很明显转换大量数据集以适应软件或算法一点也不容易。管理模式转换的复杂性(或者, ETL )是耗时的,充满了双输决策,并且经常丢失信息。
每次出现重要的新数据格式/模式时,升级或改造软件也不是一件容易的事情。即使对于一个假设的零技术债务的系统来说,开发每个新的 ETL 组件的增量成本也太高了。
它不可扩展。
这就是我们今天所处的位置——在一个充满不同的孤岛、模式、SQL 和单一数据湖的反乌托邦世界中,大多数收集到的数据源被随意隔离在一旁。
这就是 无用数据问题 。有些事情需要改变。
万维网设计模式
在过去的 50 年中,很难考虑有什么技术创新比环球网对世界的影响更大。
考虑一下世界上市值最高的公司,或者试着花几个小时不去查看社交媒体、工作邮件或谷歌搜索。互联网在我们的生活中无处不在。
【网络版】 软件正在吞噬世界。
*这对于如上所述的无用数据问题意味着什么?*让我们来关注推动互联网革命的万维网设计模式的五个关键组成部分:
- 网络服务器(例如 Apache)
- 网络客户端/浏览器(如 Netscape)
- 超文本传送协议
- HTML/JavaScript/CSS
- URL(例如 IP 地址和 DNS)
如果你有一个网络服务器,你可以向任何一个有网络客户端/浏览器的人提供你自己的 特定领域的内容——而不仅仅是那些明确构建来连接到你的服务器或解释内容的客户端。**
您可以在瞬间将您的应用扩展到数百万用户。
如果你有一个网络客户端/浏览器,你可以连接到任何公共网络服务器——而不仅仅是那些专门为响应你的客户端而构建的服务器。
你可以在一瞬间访问和使用数百万个网站中的任何一个。
Web 客户端通过一种通用语言与 Web 服务器通信: HTTP 。HTTP 有许多方法允许客户端向任何服务器发出请求。常见的 HTTP 请求格式是通用的——GET、POST、headers、authentication、SSL 等。
那么,在不了解任何领域的情况下,web 客户端如何从 web 服务器获取信息呢?带有用 HTML 和/或 JavaScript 实现的回调模式。
在从 web 客户端到 web 服务器的初始通用 HTTP 请求之后,来自服务器的响应不仅提供定义页面外观和工作方式的 HTML/JavaScript/CSS,对服务器(或其他 web 服务器)的特定回调** 也嵌入在响应中。
例如<一个 href=“https://tag.bio” >,或者一个AJAX表达式。
由 web 服务器提供的内容来恰当地传递有效的 HTML 和 JavaScript 代码,以便客户端呈现页面并进行回调——否则你会在浏览器中看到破碎的页面和链接。
因此,特定领域功能的负担落在 web 服务器的开发人员身上,而不是每个客户端和每个用户。这是可扩展的,因为特定领域的功能只需要实现一次,就可以用于多种用途。
最后,万维网模式需要一个通用的寻址系统,这样当你在你的网络客户端键入一个 URL(例如 https://tag.bio),,)客户端将知道互联网路径以与正确的网络服务器通信。互联网地址的全球注册处 IP 系统和互联网地址的域名映射全球注册处 DNS 对此起到了促进作用。
能否利用 WWW 设计模式解决 无用数据问题 ?
数据网格体系结构
2019 年,ThoughtWorks 的 Zhamak Dehghani 发表了一篇励志文章,介绍了数据网格作为无用数据问题的拟议解决方案。从文章的摘要来看:
基于数据湖架构的数据平台有一些常见的故障模式,会导致大规模的承诺无法兑现。为了解决这些故障模式,我们需要从湖的集中式范例或其前身数据仓库中转移出来。我们需要转向一种借鉴现代分布式架构的范式:将领域视为首要关注点,应用平台思维来创建自助式数据基础架构,并将数据视为一种产品。
****文章很长,技术密集,令人瞠目结舌。数据网格打破了围绕数据湖的最先进的思维,消除了将所有数据放在一个地方并将所有数据转换成单一模式的必要性(这是一个巨大、耗时、价值递减的棘手问题)。
数据网格的总体思想是数据源可以——也应该——留在原处。数据源应该由最了解该领域的人开发和部署为模块化的、独立的数据产品*。这些人将能够最有效和高效地围绕他们的数据源开发有用的内容和功能。***
那听起来像什么? 万维网服务器 。该死的。
走向收集数据的万维网
让我们将 WWW 设计模式与数据网格架构结合起来,看看兔子洞能走多远。
以下是我们需要的(与上面列出的 5 个 WWW 组件直接匹配):
- 一个服务器应用程序— **一个数据产品* —它可以包装任何给定的数据源/模式而不改变它。该数据产品将配备一个 API,它可以与任何客户端进行通用的通信,而且还支持特定于域的请求和响应——以及对附加服务器功能的回调——而不会暴露源数据模式。保持客户端独立于源数据模式对于该模式的可伸缩性至关重要,因为如果源数据模式要改变——这种情况经常发生— 只有数据产品需要调整,而不是每个 gall-dang 客户端应用程序。***
- 一个客户端应用程序,即**一个数据门户 ,它可以与任何数据产品进行通用*通信,以了解它可以执行什么功能以及如何执行这些功能。数据门户应该能够将服务器响应处理为报告、可视化、训练有素的机器学习模型、原始数据切片,并将所有服务器响应存储为 有用的数据工件 以实现可再现性。*****
- 一个通用通信协议*,方便任何数据门户和任何单个数据产品之间的一般通信。***
- 一个通用查询模式*,它使每个数据产品能够提供一个带有回调功能的数据门户,以调用特定领域的、无模式的查询和算法。***
- 一个全球数据产品注册和寻址系统*,以便数据门户可以找到并利用一个或多个数据产品中的功能。***
为什么它很重要
简而言之,万维网模式是一种创新的、可扩展的设计,用于最大限度地发现和提取组织内和世界各地收集的数据的价值。
一些主要优势:
- 数据产品发布者可以专注于他们自己的数据源* 和他们以及他们的同行最了解的算法,并即时发布他们的数据产品,以便从他们的组织内部或者从世界上的任何地方访问。***
- 数据门户用户可以在他们的组织内——或世界上的任何地方——快速找到和利用数据源*,并使用域原生功能**,而不必适应复杂的数据源模式**。数据门户通过捕获有用的数据工件,支持数据研究和协作的可再现性。***
- ****适应数据模式变化或向模式中添加新数据源的负担只落在一个技术组件上——数据产品。
- 在数据源上实现有用算法的负担主要落在最了解数据的数据产品发布者身上。值得注意的是,这可能不足以满足所有数据用例。另一种选择是,数据门户客户端调用数据产品的下载方法,提取原始数据或转换后的数据,然后执行外部算法。
模块化、领域驱动的设计——可扩展且有用!
数据门户对数据产品的要求
- 你有什么样的数据?最后一次更新是什么时候?
- 你们提供什么方法?
- 对于给定的方法,有哪些参数?
- 对于方法参数,有效的值集/范围是什么?
- 对于给定的方法,响应的模式/编码是什么?
- 请使用这些参数运行此方法。
- RESTful 和异步的、基于令牌的轮询(用于长期运行的方法)。
假设和考虑
- 数据门户不知道每个数据产品背后的显式数据模式。它只通过上面列出的问题来了解数据产品。这允许数据产品适应数据模式的变化,而不破坏每个 API 客户端。
- 正如 Data Mesh 文章中所发表的那样— 数据产品应该是不可变的。因此,客户端状态/会话必须由补充组件来维护。如果数据门户或算法调用数据产品中的数据转换,它应该产生/部署新的数据产品。
- 当数据产品包含大数据时,算法应该在数据产品中运行。当数据产品包含少量数据时,提取数据供外部使用更为可行。
- 由于商业秘密或安全性/合规性的原因,许多数据集受到限制 —用户认证和授权是必须的。但是,如果在数据产品应用程序之外管理安全模型,数据产品本身会更加灵活,也就是说,数据产品应该不知道单个用户。
- 数据产品应该支持执行从数据门户或通过 API 提交的远程代码吗?这是一个强大的选项,但它带来了巨大的安全风险,这在过去一直困扰着 WWW 小心行事。
- ****数据、算法/方法和通信协议的版本必须明确,并由每个数据产品和数据门户管理。没有版本控制就没有可复制性。
- 来自数据产品的算法/方法响应应该在数据门户中产生 有用的数据工件(udat)。UDATs 应该包含定义其来源的出处编码,以便能够可靠地解释和复制它们。****
- 全球数据产品注册管理机构以及数据产品本身应遵守 公平原则 。
这会产生什么影响?需要检验哪些假设?请在下方留言评论。
对于我们自己的这个模式的实现,请访问 Tag.bio 。
如果你已经读到这里,但还没有阅读 ThoughtWorks 的数据网格文章,请现在就阅读。
亿万亿,
闪闪发光的星星和它们的秘密;卡尔梦见,“我必须知道!”
迈向语音转文本的 ImageNet 时代
结合现有的想法和技术,向实用的 STT 迈进
原载于 2020 年 3 月 28 日https://the gradient . pub。所有的引用和参考文献都保留在原来的文章中。Medium 也没有任何方便的目录特性,所以我也将保留原始链接。在适当的地方,我会提供文章原文部分的链接。我还提供了更多最新基准的链接。
语音转文本(STT),也称为自动语音识别(ASR),有着悠久的历史,并在过去十年中取得了惊人的进展。目前,人们通常认为,只有像谷歌、脸书或百度(或地方政府支持的俄语垄断企业)这样的大公司才能提供可部署的“野外”解决方案。这是由于几个原因:
- 论文中通常使用的高计算要求设置了人为的高准入门槛;
- 由于不同的词汇、说话者和压缩伪像,需要大量数据的语音;
- 一种放弃实际的解决方案而倾向于不切实际的、先进的解决方案的心态(SOTA)。
在这篇文章中,我们描述了我们通过以下方式在全球和俄语领域缓解这些问题的努力:
- 介绍在 CC-NC-BY 许可下发布的多样化的 20,000 小时开放 STT 数据集;
- 证明仅使用两个消费级和广泛可用的 GPU 就可以实现有竞争力的结果;
- 提供了大量的设计模式,为广大研究人员和从业者进入语音领域提供了民主化的入口。
介绍
随着计算机视觉的成功和民主化(所谓的“ImageNet moment”,即减少硬件要求、上市时间和生产可部署产品的最小数据集大小),合乎逻辑的是希望机器学习(ML)的其他分支也将效仿。唯一的问题是,什么时候会发生,发生的必要条件是什么?
在我们看来,给定 ML 子场中的图像网时刻在以下情况下到达:
- 解决 95%的标准“有用”任务所需的架构和模型构建块作为标准和经过测试的开源框架模块广泛可用;
- 大多数受欢迎的型号都有预先训练的重量;
- 解决了从使用预训练模型的标准任务到不同日常任务的知识转移;
- 与之前在论文中报告的计算需求(在 STT 为 100-1000 GPU 天)相比,为日常任务训练模型所需的计算非常少(例如,在 STT 为 1-10 GPU 天);
- 用于预训练大型模型的计算机可供小型独立公司和研究小组使用;
如果满足上述条件,人们就能以合理的成本开发新的有用的应用。民主化也发生了——人们不再需要依赖谷歌这样的大公司作为行业内唯一的真理来源。
我将在整篇文章中提到“有用性”。从广义上讲,我们坚信,当某样东西为整个社会带来非零和的结果,并且没有彻底的输家时,它就是有用的。这意味着大多数人或多或少都从这件事情中受益,而不仅仅是少数特权阶层。此外,考虑到一些有用的东西,至少应该没有对某个种族或收入群体的负面歧视。例如,如果“完美的”自动驾驶汽车大规模部署,一些工作将不得不演变,但总体而言,这项技术将是“有用的”。如果大规模部署“完美”的人脸检测,它可能会加剧更多的问题,而不是解决更多的问题。这是非常主观的,所以要有所保留。
这件作品将描述我们对 STT 的影像网络时刻的追求,这一时刻至今尚未找到,尤其是在俄语的语境中。我们的主要目标是在有限的计算预算下尽可能快地构建和部署有用的模型,并分享我们的成果,以便其他人可以基于我们的发现,这样我们就可以共同实现 STT 的 ImageNet 时刻。
这不是一篇传统的同行评议的研究论文,而是一篇总结,总结了我们结合现有的思想和技术,朝着有用和实用的 STT 前进的务实尝试。
我们决定以这种形式分享它,而不是在会议上或 arxiv 上以论文的形式,这样我们的发现就能被尽可能多的人获得。虽然确保技术正确性的同行评审当然是有用的,但由于使用了大量现有的想法和我们提供的经验结果,我们对我们的主张很有信心。我们将单独撰写一篇文章,解释为什么我们认为当前同行评审和公司支持的研究不是社会整体进步的最快途径;简而言之,虽然这些有缺陷的系统长期有效,但在短期内有更快的方法来取得进展。
简而言之——这里介绍的想法在生产中确实有效,并且已经过域外验证。更重要的是,它们中的大多数都很实际,不需要昂贵的硬件或大量代码。我们欢迎反馈和批评—aveysov@gmail.com
相关工作及启示
在我们的实验中,我们选择了以下技术:
- 用于声学建模的前馈神经网络(主要是具有挤压和激励和变压器块的分组 1D 卷积);
- 连接主义者时态分类loss(CTC loss);
- 由作为建模单元的字素(即字母表字母)组成的复合记号(与音素相对);
- 使用预训练语言模型(LM)作为解码器的波束搜索。
有很多方法可以接近 STT。讨论它们的缺点和优点超出了这里的范围。本文中的所有内容都是关于主要使用字素(即字母)和神经网络的端到端方法。
简而言之,为了训练端到端的字形模型,您只需要许多带有相应转录的小音频文件,即 file.wav 和 transcription.txt。您还可以使用 CTC loss,这减轻了对时间对齐注释的要求(否则,您将需要自己提供对齐表或在您的网络中学习对齐)。CTC 损失的一种常见替代方法是标准分类交叉熵损失,但它本身训练缓慢,通常与 CTC 损失一起使用。
选择这个“堆栈”有几个原因:
- 可扩展性。您可以通过添加 GPU 来扩展您计算机;
- 面向未来。如果一个新的神经网络模块成为主流,它可以在几天内集成和测试。迁移到另一个框架也很容易;
- 简单。也就是说,使用 Python 和 PyTorch,你可以专注于实验,而不是解决遗留的限制;
- 灵活性。在 Python 中构建适当的代码,你可以在几天内测试新的特性(例如,扬声器二进制化);
- 通过在解码器中不使用注意力,也不使用音素或递归神经网络,我们实现了更快的收敛,并且需要对我们的模型进行更少的维护;
打开语音转文本(俄语)
我们所知道的所有公开可用的监督英语数据集都小于 1000 小时,并且具有非常有限的可变性。一篇开创性的 STT 论文提出,你需要至少 10,000 小时的注释来建立一个合适的 STT 系统。1,000 小时也是一个好的开始,但是考虑到泛化能力的差距(将在下面讨论),您需要大约 10,000 小时的不同领域的数据。
典型的学术数据集有以下缺点:
- 太理想了。在工作室录制的,或者与真实世界的应用相比太干净;
- 领域太窄。STT 的难度遵循这个简单的公式:噪音水平词汇量说话人数量;
- 大多只会英语。虽然像 Common Voice 这样的项目在某种程度上缓解了这种限制,但是你不能可靠地找到大量除了德语和英语之外的语言的数据。此外,普通语音可能比语音到文本更适合于说话人识别任务,因为它们的文本不是非常多样化;
- 不同的压缩。Wav 文件几乎没有压缩失真,因此不能代表以不同方式压缩的真实声音字节;
由于这些缺点,大约 6 个月前,我们决定收集并分享一个前所未有的俄语口语语料库。我们最初的目标是 10,000 小时。据我们所知,这甚至在英语中也是前所未有的。我们已经看到尝试做类似我们的工作,但是尽管有政府资助,他们的数据集并不公开。
最近,我们发布了数据集的 1.0 测试版。它包括以下领域:
我们的数据收集流程如下:
- 收集一些数据,然后使用试探法进行清理;
- 训练一些模型,并使用这些模型进一步清理数据;
- 收集更多数据,并使用对齐功能将抄本与音频对齐;
- 训练更好的模型,并使用这些模型进一步清理数据;
- 收集更多数据,手动标注部分数据;
- 重复所有步骤。
你可以在这里找到我们的语料库,你可以在这里找到支持我们的数据集。
虽然这已经很可观了,但我们还没有完成。我们的短期计划是:
- 做一些内务,多清理数据,清理一些遗留代码;
- 迁移到。ogg 以便在保持质量的同时最小化数据存储空间;
- 增加几个新领域(法庭对话、医学讲座和研讨会、诗歌)。
PS。我们做了所有这些,我们的数据集甚至出现在 azure 数据集上,现在我们计划发布 3 种新语言的预训练模型:英语/德语/西班牙语。
制作一个很棒的演讲文本模型
要建立一个伟大的 STT 模式,它需要以下特征:
- 快速推断;
- 参数高效;
- 易于维护和改进;
- 不需要大量的计算训练,2 x 1080Ti 或更少的机器应该足够了;
我们将这些作为我们的目标,并在下面描述我们是如何实现它们的。
传统上,模型是通过在几个
固定的“理想”不可见的验证数据集上进行基准测试来选择的。在前面的章节中,我们
解释了如果您考虑到真实世界的使用情况,并且唯一可用的数据集是学术数据集,那么这为什么是次优的。给定有限的
资源来适当地比较模型,您需要一个完全不同的
方法,我们在本节中介绍。还要记住,当您处理真实的野外数据时,没有“理想的”验证数据集,您需要分别对每个域进行验证。
通常当在一些公共数据集(例如
ImageNet)上报告一些结果时,研究人员据称使用不同的
超参数从头开始运行完整的实验,直到收敛。此外,一个好的做法
是运行所谓的烧蚀测试,即通过
比较有和没有这些特征的模型的性能来测试
模型的附加特征是否实际有用的实验。
在现实生活中,从业者无法负担得起
从零开始运行成百上千个实验直到
收敛,或者构建一些花哨的强化学习代码来
控制实验的奢侈。此外,文献中过度参数化方法的优势
和面向企业的工具包的可用性
阻碍了研究人员深入优化他们的管道。当你
探索硬件选项时,在专业或云领域
会偏向昂贵且不切实际的解决方案。
阅读 此处 更多了解我们的型号选择方法。
取得的总体进展
最初,我们从 PyTorch 中的深度演讲 2 开始。原始 Deep Speech 2 模型基于速度较慢的 deep LSTM 或 GRU 循环网络。上图展示了我们能够添加到原始管道中的优化。更具体地说,我们能够在不影响模型性能的情况下做到以下几点:
- 将模型尺寸缩小 5 倍左右;
- 将其收敛速度提高 5-10 倍;
- 小型(25M-35M 参数)最终模型可以在 2x1080 Ti GPUs 上训练,而不是 4 个;
- 大型号仍然需要 4x1080 Ti,但与小型号相比,最终 CER 稍低(低 1-1.5 个百分点)。
上面的图表只有卷积模型,我们发现它比递归模型快得多。我们开始了获得这些结果的过程,如下所示:
- 使用 Deep Speech 2 的现有实现;
- 在 LibriSpeech 上运行一些实验,我们注意到 RNN 模型与其卷积模型相比通常非常慢;
- 添加了一个普通的 Wav2Letter 启发的模型,实际上对于俄语来说参数化不足,所以我们增加了模型大小;
- 注意到这个模型还可以,但是训练非常慢,所以我们试图优化训练时间。
因此,我们随后探索了以下改进方法:
- 想法 1 —模型步幅
- 想法 2——紧凑的正则化网络
- 想法 3 —使用字节对编码
- 想法 4 —更好的编码器
- 想法 5——平衡容量——永远不再使用 4 个 GPU
- 想法 6——稳定不同领域的培训,平衡推广
- 想法 7——制作一个非常快速的解码器
请跟随此 链接 详细了解这些想法。
模型基准和推广差距
在现实生活中,如果模型在一个领域上被训练,那么在另一个领域上将会有显著的泛化差距。但是一开始就存在泛化差距吗?如果有,那么域之间的主要区别是什么?你能训练一个模型在许多合理的领域里工作得很好,并且有不错的信噪比吗?
有一个概括的差距,你甚至可以推断出哪些 ASR 系统是在哪些领域被训练的。此外,根据上面的想法,你可以训练一个即使在未知领域也能正常运行的模型。
根据我们的观察,这些是导致领域之间泛化差距的主要差异:
- 整体噪音水平;
- 词汇和发音;
- 用于压缩音频的编解码器或硬件;
显然现在这个图表已经很老了,你可以在 silero.ai 上找到更多最新的俄语指标
该基准测试包括声学模型和语言模型。声学模型在 GPU 上运行,结果累加,然后在多个 CPU 上运行语言模型后处理;
有关更详细的基准测试、生产使用和基准测试分析的一些想法,请点击 这里 。有关最新的基准测试,请点击此处的(俄语)。
进一步工作
以下是我们测试过的一些想法(其中一些甚至可行),但我们最终认为,它们的复杂性并不能证明它们所提供的好处:
- 摆脱渐变剪辑。渐变裁剪需要 25%到 40%的批处理时间。我们尝试了各种方法来摆脱它,但无法做到不遭受收敛速度的严重下降;
- 亚当,诺沃格勒和其他新的和有前途的优化。根据我们的经验,他们只处理简单的非语音相关领域或玩具数据集;
- 序列间解码器,双重监控。这些想法行得通。使用分类交叉熵损失而不是 CTC 的基于注意力的解码器是出了名的慢启动者(你将语音解码添加到已经繁重的对齐任务中)。混合网络并没有表现得更好来证明它们的复杂性。这可能只是意味着混合网络需要大量的参数微调;
- 基于音素和音素增强的方法。虽然这些帮助我们调整了一些过度参数化的模型(100-150M 参数),但它们被证明对较小的模型不是很有用。令人惊讶的是,谷歌的一项广泛的标记化研究得出了类似的结果;
- 宽度逐渐增加的网络。计算机视觉中的一种常见设计模式,到目前为止,这种网络比具有相同网络宽度的网络收敛得更差;
- 空闲块的使用。乍一看,这并不奏效,但也许需要更多的时间才能奏效;
- 尝试任何类型的可调滤波器,而不是 STFT。我们尝试了可调 STFT 滤波器和 SincNet 滤波器的各种实现,但是在大多数情况下,我们甚至不能用这样的滤波器稳定模型的训练;
- 训练一个不同步幅的金字塔形模型。我们在这方面没有取得任何进展;
- 使用模型提取和量化来加速推理。当我们在 PyTorch 中尝试原生量化时,它仍处于测试阶段,而且还不支持我们的模块;
- 添加补充目标,如扬声器双音化或噪音消除。噪音消除工程,但它被证明是更多的审美用途;
作者简介
Alexander Veysov 是 Silero 的一名数据科学家,Silero 是一家开发 NLP / Speech / CV 产品的小公司,他也是《开放 STT》——可能是最大的公开俄语口语语料库——的作者。Silero 最近推出了自己的俄罗斯 STT 发动机。此前,他曾在一家总部位于莫斯科的风险投资公司和 Ponominalu.ru 工作,后者是一家被 MTS(俄罗斯主要电信公司)收购的票务初创公司。他在莫斯科国立国际关系大学(MGIMO)获得了经济学学士和硕士学位。可以在 电报 (@snakers41)关注他的频道。
致谢
感谢《渐变》杂志的安德烈·库连科夫和雅各布·安德森对这篇文章的贡献。
引用
学术语境或书籍中的归属,请将本著作引用为
Alexander Veysov,“迈向语音转文本的 ImageNet 时刻”,The Gradient,2020。
BibTeX 引文
**@ article { vey SOV 2020 towardimagenetstt,
author = {Veysov,Alexander},
title = { Toward ’ s an ImageNet Moment for Speech-to-Text },
journal = {The Gradient},
year = {2020},
how published = { \ URL {https://The Gradient . pub/Toward-an-ImageNet-Moment-for-Speech-to
对恢复的新冠肺炎病例进行更好的估计
呼吁在我们的公共话语中多一点贝叶斯思维
TL;DR——在过去的两周里,关于新冠肺炎抗体的初步血清学检测结果已经公布了很多。最早的例子之一是斯坦福大学一份广为流传的研究草案。该研究的作者估计圣克拉拉县(加利福尼亚州)3300 名受试者中的抗体可能流行率为 1.5%,尽管他们也接受了将真实流行率置于 1.11 至 1.97%范围内的不确定性(95%的置信度)。本文解释了如何考虑处理不确定性和观察数据的众所周知的统计和机器学习概念,特别是贝叶斯定理,将更现实地估计真实患病率位于 0.2%至 2.1%的更大范围内(95%可信)。正如其他科学家所批评的那样,如果最初就能发现如此大的误差,斯坦福大学研究的作者对不确定性的更仔细考虑可能会避免该研究最初发现的过度耸人听闻。
我可以想象,在我们这个以 COVID 为中心的新世界中,每个人都找到了自己收集、筛选和解释数据、新闻以及关于正在发生的事情和地点的信息的方式。对我来说,我一直着迷于世界各地正在发生的数据科学和建模,以了解这种病毒的性质及其对公共卫生和经济的影响。除此之外,我一直对一种“新常态”的出现非常着迷(如果不是一点点担心的话),这种“新常态”指的是学术界产生的关键科学研究如何影响政策。我们已经很快到达了这样一个点,几个而不是一个未经审查的流行病学模拟研究和测试结果的预印本已经成为重大新闻标题和政策决定的驱动力,对国家经济和公共卫生战略产生了重大影响。当然,这并非没有充分的理由。这场危机要求我们根据现有的最佳数据、分析和专家做出紧急、直接的决策。做出关键决定的时间窗口,如选择是否以及何时触发留在家中的订单,与大多数典型的学术期刊审查流程不一致。未来几年,我们是否在这场危机开始时观察到了正确的数据,听取了正确的声音(澄清一下,我不是其中之一),将会引发很多争论。这篇文章更侧重于我们现在才开始关注的一组特定数据:新冠肺炎抗体的血清学检测。
在过去的两周里,血清学检测已经成为新冠肺炎数据收集工作的新领域。获取这些数据似乎对回答我们未来面临的重要问题至关重要:在过去几个月里,我们漏诊了多少轻度或无症状的新冠肺炎病例?我们离所谓的“群体免疫”还有多远?我们应该考虑给那些体内检测出抗体的人发放“免疫护照”吗?
这可能部分是由于我们的兴趣(或不耐烦?)为了尽快回答这些问题,斯坦福大学的 Bendavid 等人在 4 月初发表的一项研究以其惊人的结果迅速引起了媒体和公众的注意。如果你不熟悉它,你可以阅读最初的研究,或者可能阅读一些立即涉及它的文章。该研究的作者对加利福尼亚州圣克拉拉县的 3300 名成人样本进行了血清学测试。他们发现 3300 名受试者中有 50 人新冠肺炎抗体检测呈阳性。当这一结果被外推以反映圣克拉拉县的全部人口时,作者估计,在过去几个月中感染新冠肺炎的真实人数可能比迄今为止诊断的人数高“50 至 80 倍”。
尽管斯坦福大学研究的披露令人震惊,更不用说头条新闻了,但没过多久,一群科学家就(理所当然地)对斯坦福大学论文的作者如何解释他们的结果并得出结论进行了抨击。对这项研究最值得注意的批评来自哥伦比亚大学的 Andrew Gelman 教授,这位统计学家非常忠实地撰写了关于现代统计数据分析方法的教科书。(原谅我,常客们,把“贝叶斯”等同于“现代”。请不要让这个毁了你的一天。)。
这篇文章表明,如果在斯坦福大学的研究中透明地进行,关注处理不确定性的贝叶斯方法将如何揭示一个更广泛的,最终不那么耸人听闻的关于圣克拉拉县新冠肺炎抗体流行的结论。明确地说,这篇文章的目标读者不是统计学家或流行病学家,我怀疑他们在大学里学过这些方法。相反,本文是为对不确定性分析和贝叶斯定理感兴趣,但又迫切需要真实用例的应用科学家、科学记者或精通数学的读者而写的。本文采用的方法摘自学术参考文献[2–4]。
第一部分:用于描述诊断测试准确性的变量
当我们对普通人群进行血清学检测时,我们可能会直觉地认为产生阳性结果的检测百分比可能等于(或足够接近)受检人群中抗体的流行率。毕竟,如果我们的测试不是高度准确的,我们为什么要继续花钱测试我们的人口呢?
这是斯坦福大学研究中提出的一个问题的关键。事实证明,该研究中应用的测试误差——虽然看起来很小——却大到足以对该研究的总体建议的可信度产生怀疑,即圣克拉拉县的实际新冠肺炎感染人数实际上比官方检测的数字高出“50-80 倍”。
为了说明原因,我们首先引入两个关键变量,这两个变量告知任何疾病的诊断测试,或者被任何疾病的诊断测试告知:该疾病在抽样人群中的患病率,以及首先检测为该疾病阳性的概率。
首先,让我们想象一下,我们有 100 个人,我们绝对肯定地知道其中 36 人有新冠肺炎抗体。在这种情况下,我们可以计算出患病率为 36/100 = 0.36 = 36%。我们也可以用概率来重写这个变量, p ,因为我们可以假设在这 100 个人口中,任何随机选择的个体都有 36/100 的机会拥有新冠肺炎抗体。所以,如果我们代表一个拥有 V+ 抗体的个体,那么我们可以把患病率称为 p(V+) = 36/100 = 0.36。
我们要介绍的第二个变量是抗体检测阳性的概率, p(T+) 。如果在上述 100 个个体的群体中,发现 30 个使用特定的测试试剂盒实际测试抗体呈阳性,那么我们可以进行初步估计,即 p(T+) = 30/100 = 0.30。
在这个特定的场景中,我们已经可以看到这里有一个错误。检测呈阳性的概率低于实际拥有新冠肺炎病毒抗体的概率。具体来说,看起来这个测试很容易产生假阴性。然而,我们必须考虑到这可能不是测试的唯一错误(因此我们估计 p(T+) 的方法也可能不太正确)。让我们看看下面这个由 11 个人组成的随机测试样本的例子:
在总共 11 个样本中,有 8 个实际阳性病例,只有 7 个样本检测为阳性。在这 7 个阳性测试中,2 个是假阴性的结果,因此只有 5 个阳性与实际阳性病例相匹配。另外两个实际案例导致假阴性测试。因此,在逐个样本的基础上,诊断测试可以正确地识别阳性病例,可以正确地识别阴性病例,或者它可能错误地对已知的阳性病例产生阴性结果,或者它可能错误地对已知的阴性病例产生阳性结果。在任何类型的诊断测试中,后两种错误的程度可以分别通过测试的灵敏度 和特异性来确定。
灵敏度 确定了诊断测试在检测病例时的准确性,这些病例将通过所谓的“金标准”进行识别,即 100%准确的参考诊断。在上表中,我们看到八分之五的真阳性病例也检测为阳性。如果这是我们得到的确定测试灵敏度的唯一数据,我们估计为 5/8 = 0.625 = 62.5%。
特异性确定诊断测试在检测已知阴性病例时的准确性。对于新冠肺炎血清学检测,这将意味着成功地识别出那些(尚未)在其系统中具有相关量抗体的个体。在上表中,每三个实际阴性病例中只有一个测试结果也是阴性。因此,特异性估计为 1/3=0.333=33.3%。这是一个非常可怕的测试!)
在我们的数学公式中,我们将敏感性和特异性表示为两个术语,分别为 p (𝑇+|𝑉+)和 p (𝑇−|𝑉−)。
敏感度, p (𝑇+|𝑉+),被认为是对已知阳性病例𝑉+的任意测试将产生阳性测试结果𝑇+.的概率同样地,
特异性, p (𝑇−|𝑉−),是对一个已知未感染病例——𝑉−——的任意测试将产生一个阴性结果——𝑇−.的概率*(对于那些以前没有见过这种类型的符号的读者来说,当你看到“* | ”符号时,想到“given”这个词会有所帮助)。
第二部分:根据已知的特异性和敏感性估计患病率
在这一点上,我们知道,如果抗体检测不是 100%准确,检测人群中新冠肺炎抗体的流行率很可能与抗体检测呈阳性的病例数不同。这里有一个错误,这个错误(至少)归因于测试的灵敏度和特异性。那么我们如何在这些不同的参数和疾病流行率之间建立清晰的数学联系呢,𝑉+?
“全概率法则”的“美”就在于此。我试图用几句话来解释这一点,可能对这篇文章(或统计科学)不公平,但还是来了。如果我们对一个随机的人群进行抽样,以确定任何样本检测出𝑇+新冠肺炎病毒阳性的概率,我们知道每个检测出阳性的人只能有两种身体状况:他们要么拥有𝑉+病毒的抗体,要么没有𝑉−.病毒的抗体全概率法则允许我们以这两种表现的组成概率之和的形式来估计 p(𝑇+),例如:
使用“全概率”规则非常有用,因为我们现在开始看到我们引入的一些变量之间建立的数学关系,如敏感度, p (𝑇+|𝑉+)和患病率, p (𝑉+).为了让数学看起来简单一点,从现在开始,我们将对𝑆𝑒使用敏感性术语 p (𝑇+|𝑉+),对𝑃𝑣.使用流行性术语 p (𝑉+)我们现在有:
我们可以进一步清理。不拥有抗体的概率𝑝(𝑉−)一定与拥有抗体的概率𝑃𝑣.有关如果 10 个人中有 9 个人有抗体,𝑃𝑣=9/10=0.9,那么通过推论,10 个人中只有 1 个人一定没有抗体,𝑝(𝑉−)=1/10=0.1.因此我们可以假设𝑝(𝑉−)=1−𝑃𝑣.误报的概率,𝑝(𝑇+|𝑉−),可以遵循类似的规则,如
𝑝(𝑇+|𝑉−)=1−𝑝(𝑇−|𝑉−).我们已经引入了最后一项作为特异性,如果我们现在把这一项和𝑆𝑝联系起来,我们可以把这个关系写成𝑝(𝑇+|𝑉−)=1−𝑆𝑝.
最后,我们现在能够将𝑝(𝑇+方程提炼为仅测试的灵敏度、特异性和患病率的函数:
如果我们的目的是对患病率进行估计,我们也可以重新排列等式:
最后,我们似乎可以仅仅从这个等式中梳理出斯坦福/圣克拉拉研究的验证。在下面的要点中,给定斯坦福研究中陈述的 Sp 和 Se 的确定性值,并假设 p (𝑇+) = 50/3330,我对抗体 Pv 的患病率进行点估计。
我已经从本文的内容中排除了上面的例子,因为它离题太远,最终不适用于我们正在评估的现实世界的问题。当且仅当我们绝对确定地知道 Se 和 Sp 时,我们才能接受使用上述等式的 Pv 的点估计。不幸的是,我们不能,正如斯坦福研究的作者也不能。甚至诊断测试的报告错误也可能有错误。
第三部分:当血清学试验的敏感性和特异性不确定时,估计患病率
为什么我们不能 100%确定诊断试验的特异性和敏感性的真实值,这可能有几个原因。例如,尽管我们尽了最大努力,我们可能永远无法将测试的诊断性能与本身 100%准确的“黄金标准”测试进行比较。在 Andrew Gelman 对斯坦福研究的评论中,他讨论了该研究的作者在估计他们测试的特异性和敏感性的误差时所采用的令人困惑的方法。
尽管如此,我们仍然可以证明斯坦福研究结论所面临的核心问题,即使我们从表面价值上考虑该研究自己对敏感性和特异性不确定性的估计。虽然作者对 Se 的总体估计为 80.3%,但他们认为 Se 的真实值可能在 72.1%至 87%的更大范围内(95%的置信度)。作者同样估计他们的测试的真实的 Sp 在 98.3 到 99.9%的范围内(95%的置信度)。
尝试以概率分布的形式来描述这些误差是有帮助的,这给了我们一种𝑆𝑒=0.721 与𝑆𝑒=0.803.相比的可能性有多大的感觉文献[2,3]中通常使用贝塔分布来表示𝑆𝑒和𝑆𝑝的估计误差,因此我将通过数值拟合分布来估计𝑆𝑒和𝑆𝑝贝塔分布的关键参数,使得每个分布的 95 %置信区间等于斯坦福研究中提供的𝑆𝑒和𝑆𝑝的 95 %置信区间的界限。
from scipy import stats
from scipy.optimize import differential_evolution
from scipy.stats import beta
import numpy as np# This code is a lazy solver, from a human’s perspective.
# It uses a a fairly raw evolutionary algorithm to search for
# the values of Beta distribution parameters ‘alpha’ and ‘beta’
# that characterise the assumed prior distributions for
# sensitivity (Se) and specificity (Sp). Could this be done
# analytically? Of course it could, but I’m lazy.alpha = 0.05 # This tells the solver that we’re
# providing it with input values
# that correspond with the 95% CI of estimates,
# where alpha = 1 — CI.# Confidence intervals of estimated uncertainty in
# sensitivity and specificity as reported from
# Stanford / Santa Clara Study (April 17, 2020 pre-print)Se_lower_95 = 0.721
Se_upper_95 = 0.87
Sp_lower_95 = 0.983
Sp_upper_95 = 0.999# Establish and run the solver for Beta probability distributions that fit to prior estimates of Se and Spdef results_Se(x):
lower_est,upper_est = beta.ppf(alpha / 2, a=x[0], b=x[1]),
beta.ppf(1 — alpha / 2, a=x[0], b=x[1]) return ((Se_lower_95-lower_est)**2 +
(Se_upper_95-upper_est)**2)**0.5def results_Sp(x):
lower_est,upper_est = beta.ppf(alpha / 2, a=x[0], b=x[1]),
beta.ppf(1 — alpha / 2, a=x[0], b=x[1]) return ((Sp_lower_95-lower_est)**2 +
(Sp_upper_95-upper_est)**2)**0.5res_Se = differential_evolution(results_Se,
tol = 0.0000001,
bounds=((0.00001,3000),
(0.00001,3000)))res_Sp = differential_evolution(results_Sp,
tol=0.0000001,
bounds=((0.00001,3000)
(0.00001,3000)))# Extract estimates for alpha and betas of
# Beta distributions for Se and Spalphas = [res_Se.x[0],res_Sp.x[0]]
betas = [res_Se.x[1],res_Sp.x[1]]
定义了 Beta 分布的关键参数后,我们可以绘制这些分布,并将其与源数据进行比较。
成功。我们的贝塔分布的特征(众数和置信区间)与研究数据非常匹配。我们现在还应该考虑如何描述我们对𝑃𝑣.的初始估计的概率分布的特征虽然本文的目的是对𝑃𝑣进行修正估计,但在后面的方法中,我们也要对这个参数进行初始估计,这一点很重要。
我在想,一个保守的,尽管普遍不了解情况的估计是,𝑃𝑣可能在 0%到 3%的范围内。然而,因为我对𝑃𝑣更有可能是 0%还是 1.5%还是 2%还是 3%没有什么初步概念,所以我将假设𝑃𝑣是这些数字中的任何一个的可能性相等,或者介于两者之间。这需要均匀的概率分布:
现在,要以一种既能让不熟悉贝叶斯统计的人满意,又能让熟悉贝叶斯统计的人满意的方式来解释剩余的过程变得越来越困难了。提前道歉。让我们再来看看我们的临界方程:
利用这个等式,我们可以从对𝑆𝑒、𝑆𝑝和𝑝(𝑇+的预测中推断出对𝑃𝑣的预测,但在所有这些中,哪里有空间根据我们从真实世界测试中获得的对𝑇+的实际观察来校准、调整或修正我们的预测呢?最后,这里进入贝叶斯定理,它为我们提供了一种将模型预测的估计概率与现场观察到真实事件的概率联系起来的方法。贝叶斯定理认为:
如果我们让阳性检验率的预测值𝑝(𝑇+ =𝑃𝑡,𝑛和𝑘分别代表样本总体的大小和返回阳性检验的数量,那么我们可以将贝叶斯定理应用于我们的问题,如下所示:
到目前为止,我们已经介绍了解决上述方程所需的所有项,除了可能性,𝑇+|𝑃𝑡).的𝑝(observations 惯例是对这种类型的情况使用二项分布。二项式分布表示在给定模型的情况下,从𝑛样本中观察到𝑘病例的概率,在我们的情况下,该分布变为:
我知道这个理论已经让人精疲力尽了,但我知道我无法在一篇短文中做出恰当的解释。好消息是我们已经走到了尽头。我们现在有了一个全面的、可解决的模型,可以通过以下步骤来执行:
- 我们可以从𝑆𝑝.𝑆𝑒的初始(先验)概率分布中随机抽取值和𝑃𝑣,并得出对𝑃𝑡的估计
- 我们可以将我们对𝑃𝑡的估计与观察到的记录(即𝑛=3300).的𝑘=50 病例检测呈阳性)进行比较这使我们能够评估我们对𝑆𝑒、𝑆𝑝和𝑃𝑣的估计是否会产生对𝑃𝑡的估计,而这种估计是否有数据支持。
- 我们可以重复步骤(1)和(2)很多次(成千上万次),以确定𝑆𝑒、𝑆𝑝和𝑃𝑣的哪个值更有可能出现(假设𝑝(observing 𝑘在𝑛测试了|𝑃𝑡)
- 我们可以整理在步骤 3 中发现的所有新的估计值 Se 、 Sp 和 Pv ,并规定这些参数的一组更新的(也称为后验)概率分布
从第 1 步到第 4 步的整个过程被称为贝叶斯推理,但是解释我们如何通过计算实现这些步骤需要更多的信息和知识,这是我所能提供的(或花费的时间)。我将开门见山地说 PyMC3 是一个伟大的 Python 库,用于解决利用蒙特卡罗马尔可夫链(MCMC)方法的概率数学问题,例如贝叶斯推理。
在下面的代码中,我们使用 PyMC3 来推断𝑆𝑒、𝑆𝑣和𝑃𝑣的更新(后验)估计值,这是基于我们对一项单一诊断测试的了解,其中 3300 名个体中有 50 名检测出新冠肺炎抗体阳性。求解器的结果,在 PyMC3 中称为“踪迹”,是一组对𝑆𝑒、𝑆𝑝和𝑃𝑣.的可能真值的修订估计
import pymc3 as pmk = 50 # Number of individuals that were observed to test positive
n = 3300 # Size of population testedp_true = k/nmodel = pm.Model()
with model: Se = pm.Beta(‘Se’,alpha=alphas[0],beta=betas[0])
Sp = pm.Beta(‘Sp’,alpha=alphas[1],beta=betas[1])
Pv = pm.Uniform(‘Prevalence’,lower=0,upper=0.03) Pt = ((Se*Pv+(1-Sp)*(1-Pv)))
y_obs = pm.Binomial( ‘y_obs’, p=Pt, n=n, observed=k) trace = pm.sample(12000, chains=2, tune=2000, target_accept=0.95)
贝叶斯推理过程的结果返回了我们对𝑆𝑒、𝑆𝑝和𝑃𝑣在概率分布方面的先验和后验估计的比较。
这些结果带来了这个相对简单过程的重大发现。因为我们只评估一种测试和一种诊断,所以很少有信息表明该测试的敏感性和特异性与该研究作者最初定义的有任何不同。然而,我们确实看到𝑃𝑣的后验分布已经围绕一系列新的值联合起来。我们的后验估计显示,不仅与我们的初始估计值 Pv (例如,0 至 0.03%,100% CI)相比,而且与斯坦福研究的估计值(例如,1.11 至 1.97%,95% CI)相比,患病率有相当显著的修正。最后的评估是,基于一项单一的真实世界测试的结果,该测试从 3300 个观察到的阳性中返回了 50 个,真实的流行率必须在至少 0.2%和 2.1%的范围内(95%可信区间)。为了与原始研究进行比较,我们可以再生成一个图:
结论
这就是了。使用适当的统计推断,特别是在贝叶斯规则和机器学习技术的帮助下解决贝叶斯推断的问题,我们通过适当考虑所用血清学检测试剂盒的规定误差和误差的误差,对斯坦福/圣克拉拉研究中采样的 3,300 名受试者的抗体流行率进行了新的估计。虽然最有可能的患病率是 1.3%,但我们发现该值位于 0.2%和 2.1%之间的任何地方的概率肯定是 95%。这似乎使任何声称新冠肺炎抗体在圣克拉拉县人口中的患病率比预期高“50-80 倍”的说法有点可疑…至少在没有更多数据的情况下不会。
Andrew Gelman 和其他人一样,已经解决了斯坦福研究中的这个关键问题,而不需要像我在这篇文章中所做的那样深入研究建模的细节。根据这位专家和其他专家的解释,如果某项检测的特异性很低,假阳性的检出率等于或高于阳性检测结果的观察率,那么几乎所有的阳性检测都有可能是假阳性,而不是对抗体携带者的真实鉴定。对于我们预计新冠肺炎抗体流行率仍然较低的地区(显然包括我的家乡不列颠哥伦比亚省温哥华),我们最终需要特异性非常高的血清学检测,以区分真阳性和假阳性。
在写这篇文章的过程中,斯坦福研究的作者们在 4 月 30 日发布了他们工作的修订版,很大程度上解决了他们第一次提交的一些问题。他们现在包括:1)将贝叶斯推断应用于他们对 *Pv、*和的估计,锐化他们基于额外的参考测试数据对灵敏度和特异性的估计。在他们提交修改后的一天后, Andrew Gelman 贡献了一份对这份修改过的作品的贝叶斯分析,使用了一种比我在这篇文章中更彻底的方法。尽管他发现斯坦福团队对不确定性的新评估有待改进,但他展示了使用贝叶斯定理的不确定性分析可以(或应该)应用于他们工作的更充分程度。我的最后一个例子是不同分析的总结:1)斯坦福研究的第一个版本的结果(2020 年 4 月 17 日预印),2)我在本文中概述的患病率估计,3)修订的斯坦福研究的结果(2020 年 4 月 30 日重新提交),4)我根据这些修订的结果对患病率的估计,以及 5) Andrew Gelman 最近根据这些修订的数据对患病率的估计。
最后,当我们试图衡量新冠肺炎危机的程度(以及我们为摆脱危机所做的决定)时,适当的不确定性量化方法,也许是贝叶斯方法,应该是最重要的吗?我不得不相信,它们就在我们医疗保健和疾病控制当局模糊的走廊里,但偶尔大声喊出来提醒一下也无妨——尤其是对我们试图理解事物的一线科学记者来说。
我将通过回答常见问题中几个问题来结束本文:
在一篇中等水平的文章中解决这些问题是不是有点矫枉过正?
是的,很可能。
做这个很难吗?
不,我的意思不是吹捧我自己对这方面的知识。
我是否使用了最先进的程序来使用贝叶斯定理评估诊断测试的不确定性?
这个我没法完整回答。我的方法是合理的,但有更彻底的方法来考虑诊断测试中的不确定性(因此,为什么 Gelman 的方法-更彻底-显示了基于 2020 年 4 月 30 日重新提交的估计值的更大不确定性)。我的结果在多大程度上是可靠的,取决于我们对可用数据的掌握程度以及对诊断测试错误的理解程度。布兰斯克姆等人【2】和恩诺埃等人【3】的作品,我觉得还是相关的入门。
我看了你的简历。一个建筑与风景园林学院的教授写这样的文章干什么?
是的,完全公开,我的职业是建筑工程师——不是流行病学家、统计学家或数学家。然而,像许多应用科学领域的学者一样,我职业生涯中越来越多的部分都专注于使用数据和机器学习的新方法来解决现实世界的设计和决策问题。在我的直接工作领域,这通常是关于我们如何设计和运行健康、舒适、理想的碳中和的建筑。我的博士生导师是,我称之为,贝叶斯主义者;首批建筑科学家之一,在预测建筑能源需求或建筑占用趋势时,使用贝叶斯定理和“新颖”统计技术来评估不确定性。虽然我只能试着追随她的脚步,但当我想到,在开始攻读博士学位的十年后,我现在在自己的研究小组中对贝叶斯定理保持着浓厚的兴趣,这让我感到振奋。
参考文献
[1] Gelman,a .,Carlin,J. B .,Stern,H. S .,Dunson,D. B .,Vehtari,a .,和 Rubin,D. B. (2013 年)。贝叶斯数据分析。CRC 出版社。
[2]刘易斯,F. I .,&托格森,P. R. (2012 年)。在缺乏金标准诊断的情况下估计人类和动物疾病流行的指南。流行病学的新兴主题,9(1),9。
[3] Branscum,A. J .,Gardner,I. A .,& Johnson,W. O. (2005 年)。通过贝叶斯建模评估诊断试验的敏感性和特异性。预防兽医学,68(2–4),145–163。
[4]恩诺埃,c .,乔吉亚迪斯,M. P .,&约翰逊,W. O. (2000 年)。当真正的疾病状态未知时,对诊断试验和疾病流行的敏感性和特异性的估计。预防兽医学,45(1–2),61–81。