调整模型的超参数,并根据业务场景瞄准特定指标
支付卡欺诈模型调整—准确性、召回率和精确度
来源:www.dqindia.com
您知道如何根据特定的产品需求和目标选择正确的误差指标吗?评估一个分类问题可能看起来有点棘手,但是由于商业决策对它的高度影响,它是极其重要的。此外,您处理模型的方式在不同的场景中可能会有所不同。
在本帖中,我们将在两个不同的场景中讨论信用卡欺诈检测分类模型的模型调整。我们将决定是选择召回还是精确作为衡量标准,并观察其影响。
值得一提的是,这篇文章将基于我为 Udacity 的机器学习工程师项目完成的一个项目,鼓励你在我的 GitHub 网页上查看。
问题:
- 让我们假设您在一家银行工作,您的任务是创建一个模型,在两种不同的情况下识别信用卡交易是欺诈还是有效
场景一:
- 您的经理要求您建立一个模型,以大约 85%的准确率检测欺诈案件。
场景二:
- 您的经理希望改善客户体验,决定用户的有效交易中最多只能有 15%被归类为欺诈交易。
总结:
- 对混淆矩阵的简单理解
- 对准确性的简单理解
- 对回忆的简单理解
- 对精度的简单理解
- 场景 1 的模型调整
- 场景 2 的模型调整
混淆矩阵:
为了理解这些指标,从而为我们的每个业务场景选择正确的指标,我们必须知道如何解释混淆矩阵及其术语。
混淆矩阵是用来衡量分类模型性能的表格。它包含具有实际分类的列和表示预测的行(反之亦然),如下所示:
来源:走向数据科学。com
其中:
- 真阳性(TP) :正确分类的阳性类别
- 假阳性(FP): 阳性分类错误
- 假阴性(FN): 阴性分类错误
- 真阴性(TN): 阴性分类正确
我想看下面的图片,你可能会对混淆矩阵有更好的理解。
准确度—召回率—精确度
给你两个场景,你会如何评价你的模型?你知道如何进行吗?为了决定我们应该追求的指标,我们必须对指标有深刻的理解。所以让我们直接用例子和场景来解释吧!
来源:向数据科学。com
精度:
准确性是一个简单的衡量标准。它基本上是正确预测与总预测的比率。你可以看到下面的公式:
资料来源:towardsdatascience.com
其中 TP =真阳性,TN =真阴性,FP =假阳性,FN =假阴性代表预测。
现在,让我们假设你的第一个模型给你 99%的准确率,比如下面的 LinearLearner 模型(一个 SaeMaker 的内置模型)。这意味着您的欺诈检测模型在标记欺诈交易和合法交易方面做得非常好,对吗?
实际上,这个问题的答案是:视情况而定!让我们更仔细地分析一下积极和消极的分类,以便更好地理解模型性能。
上述模型标记了超过 30 FN(标签不正确的欺诈性交易)和略高于 30 FP 的交易(标签不正确的有效交易)。考虑到用户的观点,我们可以声明他们不希望任何有效的交易被归类为欺诈。在这种情况下,我们必须意识到这样一个事实,即我们的模型应该有尽可能少的误报(错误标记,欺诈交易)。那样的话,我们就不能仅仅基于其准确性来调整模型以完成业务需求**。**为了有效地做到这一点,我们必须进行模型调整,分析一个指标,这可以帮助我们减少 FP 的数量,这与准确性无关。
回忆:
考虑到我们的欺诈检测项目,我们可以说召回率是正确标记的欺诈总数与欺诈示例总数(标记为欺诈的欺诈和标记为有效交易的欺诈)的比率。高召回率表明我们的类被正确识别(TP 数量高,FN 数量少)。
我们思考回忆的方式如下:
在阳性样本中,我的模型正确预测了多少?
精度:
来源:owardsdatascience.com t
精确度是我们必须涵盖的第三个指标,这样我们才能解决我们的业务场景问题。它基本上是正确分类的类别数除以这些类别被预测的总时间。在我们的例子中,精度是被正确分类为欺诈的欺诈交易的数量除以被正确分类为欺诈的欺诈交易的数量,加上被标记为欺诈的有效交易的数量。
我们能想到的精度是:
在我的模型标记为正面的所有预测中,有多少实际上是正面的?
管理阶层失衡
每当我们想要进行模型调优时,我们必须牢记不平衡的数据可能会使模型偏向于预测具有更多数据点的类。因此,我们必须考虑二元分类器的类不平衡训练。
在这个项目中,我使用了 LinearLearner 模型提供的一个超参数来处理不平衡的数据集。这个超参数(positive _ example _ weight _ mult)基本上就是训练一个二元分类器的时候分配给正类(1,欺诈)的权重。有效交易的权重固定为 1。
场景 1
现在让我们考虑一下我们的业务案例。根据经理的要求,我们可以说我们必须建立一个模型,这个模型有尽可能多的真阳性和尽可能少的假阴性。这意味着我们必须选择召回指标来调整我们的欺诈检测模型。
因此,为了达到特定的召回值,我使用了二元 _ 分类器 _ 模型 _ 选择 _ 标准 超参数,这是训练数据集的模型评估标准。通过使用这个度量,我们能够为我们的参数指定我们想要达到的精确值。
假设训练集的性能在测试集的性能的 5%以内,我们应该以 90%的召回率为目标。
这样,我们最终得到以下指标:
尽管我们的目标是 90%的召回率,但是我们可以看到这个模型给出了大约 91%的召回率。当应用于测试数据时,该值可能会有所不同。我们还应该看到,我们有更少的假阴性,这正是我们想要的。
场景 2:
在这种情况下,业务需求是模型只将 15%的有效交易标记为欺诈交易。这意味着我们希望尽可能少的误报(0 归类为 1)。因此,我们可以使用精度来进行模型调整。
基于这个要求,我们可以计算出我们想要的精度的近似值:85/(85+15)。同样,让我们假设与测试相比,我们在培训中的表现会高出 5%。因此,我们的目标是在测试集上达到 80–85%的精确度。
值得一提的是,该模型训练的固定精度为 90%,并试图获得尽可能高的召回率。考虑到这一点,我们可以看到作为优化模型结果的指标。
我们可以看到,该模型仍然对 26 个有效交易进行了错误分类,因此我们可能希望返回并提高我们的目标——精确度。我们还可以注意到,如果我们考虑精度的权衡,召回率和准确率都还可以。
结论:
在现实世界的项目中,产品的成功通常与调整特定的指标相关联。为了满足业务需求,了解您将选择哪种指标非常重要。我希望这篇文章能让您更好地理解如何根据业务决策和需求调整您的模型。
调整参数。以下是方法。
对你的机器学习模型有益的常见和不太常见的参数。
Gabriel Gurrola 在Unsplash【1】上的照片。
目录
- 介绍
- 因素
- 例子
- 密码
- 摘要
- 参考
介绍
参数可能令人望而生畏,令人困惑,令人不知所措。本文将概述常用机器学习算法中使用的关键参数,包括:随机森林、多项式朴素贝叶斯、逻辑回归、支持向量机和 K 近邻。还有称为超参数的特定参数,我们将在后面讨论。参数调整有利于提高模型精度,减少模型运行时间,并最终减少模型的货币支出。
因素
来自sk learn【2】和其他库的每种型号都有不同的参数;但是,这些常用算法之间有相当多的重叠。开发模型时,您将拥有默认参数。调整这些有助于提高您的整体准确性。这些参数将在分类代码中找到(我将展示 Python 中的例子),例如,假设您使用 clf 作为分类器的别名,您可以输入这些参数并在那里更改或调整它们的值,最终更改您的模型的输出。
最有效的调优方法之一是网格搜索[3]。这些参数是不同的,因为它们被认为是超参数,并且不是在估计器本身中直接学习的。在建立了上述分类器之后,您将创建一个参数网格,该网格将进行搜索以找到超参数的最大优化。请记住,这个调优过程计算量很大,如果您添加几个超参数的话,可能会非常昂贵。该模型将引用您列出的所有网格项目的组合,因此,几乎就好像您有几个模型在运行,而不是一个。
例子
照片由 Fabian Grohs 在Unsplash【4】上拍摄。
下面,我会把我在整个数据科学硕士期间所学的常用机器学习算法,以及我的职业生涯包括在内。虽然这些都是需要学习和练习的重要方法,但还有无数其他方法可以帮助你提高准确度。
这些参数不仅有助于提高准确性,而且有助于降低计算机的计算时间和工作量,因为您将降低随机森林的数量,从而降低树的最大深度,最终也将节省资金。
下面列出的是来自 sklearn 的常见机器学习算法,其中包括几个可编辑的参数。以下是所有记录的参数及其各自的机器学习算法的链接:
下面代码片段中的默认参数将在括号内——我还将给出我发现在过去有用的建议。
随机森林
**n_estimators**: number of trees in your forest (100)**max_depth**: maximum depth of your tree (None) - recommendation, change this parameter to be an actual number because this parameter could cause overfitting from learning your traning data too well**min_samples_split**: minimum samples required to split your node (2)**min_samples_leaf**: mimimum number of samples to be at your leaf node (1)**max_features**: number of features used for the best split ("auto")**boostrap**: if you want to use boostrapped samples (True)**n_jobs**: number of jobs in parallel run (None) - for using all processors, put -1**random_state**: for reproducibility in controlling randomness of samples (None)**verbose**: text output of model in process (None)**class_weight**: balancing weights of features, n_samples / (n_classes * np.bincount(y)) (None) - recommendation, use 'balanced' for labels that are unbalanced
多项式朴素贝叶斯
parameters - **alpha**: a paramter for smoothing (1.0)**class_prior**: looking at the previous class probability (None) attributes -**feature_count**: number of samples for each class or feature (number of classes, number of features)**n_features**: number of features for sample
逻辑回归
**penalty**: l1 or l2 (lasso or ridge), the normality of penalization (l2)**multi_class**: binary versus multiclass label data ('auto')
支持向量机
parameter **- decision_function_shape**: 'ovr' or 'one-versus-rest' approach
k-最近邻
parameter - **n_neighbors**: number of neighbors (5)
密码
下面是一些有用的代码,可以帮助您开始参数调整。有几个模型可以从调优中受益,业务和团队也可以从调优带来的效率中受益。下面,是模型代码,以及可用于强大优化的网格搜索代码。
调谐参数的位置。作者代码[5]。
网格搜索示例。作者代码[6]。
摘要
照片由 ThisisEngineering RAEng 在Unsplash【7】上拍摄。
现在,希望您和您的团队能够从应用当前或下一个机器学习模型的参数更改中受益。从默认参数开始,确保在开始调优之前了解每个模型的参数,以及关于这些参数定义的官方文档。虽然它们可以改善你的模型,但是参数也可以以降低你的准确性或者过度拟合你的模型的方式被调整。小心谨慎,你会发现自己拥有一个成功的、复杂的数据科学模型。
我希望你觉得这篇文章有趣且有用。谢谢大家!
如果你想了解更多关于这些特定的机器学习算法,我写了另一篇文章,你可以在这里找到[8]:
通用算法的端到端运行;包括随机森林,多项式朴素贝叶斯,逻辑回归…
towardsdatascience.com](/machine-learning-algorithms-heres-the-end-to-end-a5f2f479d1ef)
参考
[1]照片由 Gabriel Gurrola 在Unsplash(2016)上拍摄
[2] sklearn, sklearn.linear_model。后勤回归(2007 年至 2019 年)
[3] sklearn, 3.2。调整估计器的超参数(2007–2019)
[4]照片由 Fabian Grohs 在 Unsplash 上拍摄,(2018)
[5] M.Przybyla,要点—模型,(2020)
[6] M.Przybyla,要点——网格搜索,(2020)
[7]照片由this engineering RAEng在Unsplash(2020)上拍摄
[8] M.Przybyla,机器学习算法。这里是端到端的。, (2020)
加速您的空间 NLP 管道
使用自定义管道和 joblib 显著加快 spaCy 中文本预处理的技巧和诀窍
来源: Pixabay
假设您有一个大型文本数据集,您希望对其应用一些非平凡的 NLP 转换,例如去除停用词,然后对文本中的词进行词汇化(即,将它们简化为词根形式)。spaCy 是一个工业级的 NLP 库,就是为这样的任务而设计的。
在本文中,纽约时报数据集用于展示如何显著加快 spaCy NLP 管道。目标是接受一篇文章的文本,并快速返回一个词条列表以及不必要的单词,即删除的停用词。
Pandas 数据框架提供了一个方便的界面来处理这种性质的表格数据 spaCy NLP 方法可以方便地直接应用于数据框架的相关列。首先通过运行预处理笔记本 ( ./data/preprocessing.ipynb
)获取新闻数据,该笔记本处理从 Kaggle 下载的 raw 文本文件,并对其进行一些基本的清理。该步骤生成一个包含表格数据的文件(存储为nytimes.tsv
)。在同一目录中还提供了一个精选的停用词文件。
负载空间模型
由于在本练习中我们不会执行任何专门的任务,比如依赖关系解析和命名实体识别,所以在加载 spaCy 模型时这些组件是禁用的。
提示: spaCy 有一个
sentencizer
组件,可以插入空白管道。
句子分析器管道简单地执行标记化和句子边界检测,随后可以将词条提取为标记属性。
定义了一个方法,从文本文件中读入停用词,并将其转换为 Python 中的集合(为了高效查找)。
在纽约时报数据集中阅读
NYT 新闻数据集的预处理版本作为熊猫数据帧读入。这些列被命名为date
、headline
和content
——内容列中显示的文本将被预处理以删除停用词并生成标记词条。
定义文本清理器
由于新闻文章数据来自原始 HTML 转储,因此非常混乱,包含大量不必要的符号、社交媒体句柄、URL 和其他工件。清理它的一个简单方法是使用正则表达式,它只解析给定长度(3 到 50)之间的字母数字字符串和连字符(以便包含带连字符的单词)。这会将每个文档过滤成对 lemmatizer 有意义的文本。
选项 1:顺序处理数据帧列
现在我们只剩下了干净的字母数字标记,在继续词汇化之前,可以通过删除停用词来进一步清理这些标记。
处理该文本的直接方法是使用一个现有的方法,在本例中是下面显示的lemmatize
方法,并使用pandas.Series.apply
将其应用到 DataFrame 的[clean](https://prrao87.github.io/blog/images/copied_from_nb/clean)
列。使用每个令牌的底层[Doc](https://spacy.io/usage/spacy-101#annotations)
表示来完成词汇化,其中包含一个lemma_
属性。停用词在词汇化过程中同时被删除,因为这些步骤中的每一步都涉及到遍历相同的标记列表。
得到的词条作为一个列表存储在一个单独的列preproc
中,如下所示。
%%time
df_preproc['preproc'] = df_preproc['clean'].apply(lemmatize)
df_preproc[['date', 'content', 'preproc']].head(3)CPU times: user 48.5 s, sys: 146 ms, total: 48.6 s Wall time: 48.6 s
将这种方法应用到 DataFrame 的[clean](https://prrao87.github.io/blog/images/copied_from_nb/clean)
列并计时,它显示在 8,800 篇新闻文章上运行几乎需要一分钟。
选项 2:使用nlp.pipe
我们能做得更好吗?在 spaCy 文档中,指出“将文本作为流处理通常比逐个处理更有效”。这是通过调用语言管道来实现的,语言管道在内部将数据分成几批,以减少纯 Python 函数调用的数量。这意味着数据越大,nlp.pipe
所能获得的性能增益就越好。
为了使用语言管道来传输文本,定义了一个新的 lemmatizer 方法,它直接作用于 spaCy Doc
对象。然后,该方法被批量调用,以处理通过管道传输的Doc
对象的序列,如下所示。
和以前一样,通过传递来自现有 DataFrame 的[clean](https://prrao87.github.io/blog/images/copied_from_nb/clean)
列的数据来创建一个新列。注意,与上面的工作流#1 不同,我们在这里不使用apply
方法——相反,数据列(一个 iterable)作为参数直接传递给预处理管道方法。
%%time
df_preproc['preproc_pipe'] = preprocess_pipe(df_preproc['clean'])
df_preproc[['date', 'content', 'preproc_pipe']].head(3)CPU times: user 51.6 s, sys: 144 ms, total: 51.8 s Wall time: 51.8 s
对这个工作流进行计时似乎并没有显示出比之前的工作流有所改进,但是根据 spaCy 文档,随着我们处理越来越大的数据集,这种方法应该会显示出一些计时改进(平均而言)。
选项 3:使用 joblib 并行化工作
我们可以做得更好!以前的工作流程依次处理每个新闻文档,生成词条列表,然后作为新列附加到数据帧。因为每一行的输出都完全独立于另一行,这是一个令人尴尬的并行问题,非常适合使用多个内核。
spaCy 推荐使用joblib
库来并行处理 NLP 流水线的块。确保您在运行以下部分之前pip install joblib
。
为了并行化工作流,必须定义更多的助手方法。
- **分块:**新闻文章内容是一个(长)字符串列表,其中每个文档代表一篇文章的文本。这些数据必须以“块”的形式提供给由
joblib
启动的每个工作进程。每次调用chunker
方法都会返回一个生成器,该生成器只包含特定块的文本作为字符串列表。在词汇化过程中,基于迭代器索引检索每个新块(前面的块被“遗忘”)。 - **扁平化:**一旦 joblib 创建了一组在每个块上工作的 worker 进程,每个 worker 返回一个包含每个文档的 lemmas 的“列表列表”。然后,这些列表由执行者组合,以提供一个 3 级嵌套的最终“列表列表列表”。为了确保 executor 输出的长度与文章的实际数量相同,定义了一个“flatten”方法来将结果合并到一个包含词条的列表中。例如,两个并行的执行器将返回一个最终的嵌套列表:
[[[a, b, c], [d, e, f]], [[g, h, i], [j, k, l]]]
,其中[[a, b, c], [d, e, f]]
和[[g, h, i], [j, k, l]]
是指每个执行器的输出(最终的输出由 joblib 连接成一个列表)。这个结果的展平版本是[[a, b, c], [d, e, f], [g, h, i], [j, k, l]]
,即去掉了一层嵌套。
除了上面的方法,一个类似的nlp.pipe
方法被用在工作流#2 中,在每个文本块上。这些方法中的每一个都被封装到一个preprocess_parallel
方法中,该方法定义了要使用的工作进程的数量(在本例中为 7 个),将输入数据分成块并返回一个扁平的结果,然后可以将该结果附加到 DataFrame 中。对于具有更多物理内核的机器,工作进程的数量可以进一步增加。
使用 joblib 的并行工作流如下所示。
%%time
df_preproc['preproc_parallel'] = preprocess_parallel(df_preproc['clean'], chunksize=1000)CPU times: user 683 ms, sys: 248 ms, total: 932 ms Wall time: 17.2 s
对这种并行工作流进行计时显示出显著的性能提升(运行时间几乎减少了3 倍)!随着文档数量的增加,使用 joblib 启动多个工作进程的额外开销很快就会得到补偿,并且这种方法可以显著优于顺序方法。
块大小和批量大小的影响
请注意,在并行化的工作流中,需要指定两个参数—最佳数量可能因数据集而异。chunksize
控制每个进程处理的每个块的大小。在本例中,对于 8,800 个文档,使用的块大小为 1000。太小的块大小意味着会产生大量的工作线程来处理大量的块,这会降低执行速度。通常,几百到几千个文档的块大小是一个很好的起点(当然,这取决于数据中每个文档的大小,以便块可以放入内存)。
批量大小是特定于nlp.pipe
的参数,同样,一个好的值取决于正在处理的数据。对于相当长的文本,比如新闻文章,保持批量适当小是有意义的(这样每一批就不会包含真正的长文本,所以在这种情况下选择 20 作为批量大小。对于其他情况(例如 Tweets ),如果每个文档的长度非常短,可以使用更大的批量。
建议试验任一参数,看看哪种组合能产生最佳性能。
集合与列表
**重要提示:**尽可能使用集合而不是列表进行查找。
注意,在前面定义的get_stopwords()
方法中,从停用词文件中读入的停用词列表在 lemmatizer 方法中通过查找移除停用词之前被转换为一个集合。一般来说,这是一个非常有用的技巧,但是特别是对于停用词移除,集合的使用变得更加重要。为什么?
在任何现实的停用词表中,比如这个新闻数据集的停用词表,有理由期待几百个停用词。这是因为对于主题建模或情感分析等下游任务,有许多特定于领域的单词需要删除(非常常见的动词,无用的缩写,如时区、星期几等。).每一个文档中的每个单词都需要与停用词表中的每个单词进行比较,这对于成千上万的文档来说是一项昂贵的操作。
众所周知,集合的查找时间为 O(1)(即常数),而列表的查找时间为 O(n)。在lemmatize()
方法中,由于我们在停用词集中检查每个词的成员资格,我们期望集合比列表好得多。为了测试这一点,我们可以重新运行工作流#1,但是这一次,使用一个停用词列表来代替。
stopwords = list(stopwords)%%time
df_preproc['preproc_stopword_list'] = df_preproc['clean'].apply(lemmatize)
df_preproc[['date', 'content', 'preproc_stopword_list']].head(3)CPU times: user 1min 17s, sys: 108 ms, total: 1min 18s Wall time: 1min 18s
有了停用词表,现在产生相同的结果比以前(有了集合)多花了 50%的时间,也就是说运行时间增加了 1.5 倍!这是有意义的,因为在这种情况下,停用词列表大约有 500 个单词长,并且需要检查语料库中的每个单词是否属于这个合理大小的列表。
结论
在本练习中,使用 spaCy 管道处理了一个新闻文章数据集(NY Times ),以输出表示每篇文章内容中有用标记的词条列表。因为真实世界的新闻数据集几乎肯定比这个大,并且大小可以是无限的,所以需要快速、高效的 NLP 管道来对数据执行任何有意义的分析。以下步骤对于加速空间管道非常有用。
**禁用 spaCy 模型中不必要的组件:**标准 spaCy 模型的流水线包含 tagger(分配词性标签)、parser(生成依赖解析)和命名实体识别组件。如果需要这些动作中的任何一个或者都不需要,这些组件必须在加载模型后立即被禁用(如上所示)。
**使用集合而不是列表进行查找:**当执行查找以将一组标记与另一组标记进行比较时,总是使用集合来执行成员资格检查——列表的查找速度要慢得多!停用词的列表/集合越大,使用集合时看到的性能增益就越大。
**尽可能使用定制语言管道:**使用nlp.pipe
设置语言管道是处理大块文本的一种非常灵活有效的方式。更好的是,spaCy 允许您单独禁用每个特定子任务的组件,例如,当您需要单独执行词性标记和命名实体识别(NER)时。有关如何在模型加载、处理或处理定制块期间禁用管道组件的示例,请参见 spaCy 文档。
**尽可能使用多个内核:**当处理彼此完全独立的单个文档时,请考虑通过在多个内核之间分配计算来并行化工作流。随着文档数量变得越来越大,性能提升可能是巨大的。人们只需要确保文档被分成块,所有这些块在任何给定的时间都必须适合内存。
我希望这是有用的——祝你在下一个 NLP 项目中测试它们时愉快!
原载于 2020 年 5 月 2 日https://prrao 87 . github . io。
动荡和金融市场?
金融市场动力学与湍流流动的比较
图片来自 Pixabay
动荡的流体行为和金融市场之间有着有趣的相似之处。在这两种情况下,人们可以发现大规模的扰动被转移到连续的较小规模(见斯坦利和曼特尼亚)。在液体的情况下,通过搅动它,人们可以观察到输入系统的能量被转移到越来越小的尺度。在金融市场中,大规模“注入”的不是能量,而是信息,人们可以观察到反应向较小规模(在这种情况下是个人投资者)的传递。两者都非常难以建模,因为它们的部件之间或者它们与环境之间存在多种类型的交互作用
在这里,根据 Stanley 和 Mantegna 的详细统计分析,可以看出,尽管金融市场和动荡的流体在质量上有相似之处,但在数量上,这种对应是有限的。
图 1:湍流射流的流动呈现出典型的湍流行为的宽范围长度尺度(来源)。
什么是湍流?
让我们考虑在管道中流动的流体,具有以下参数:
图 2:右边液体粘度较高(来源)。
在流体力学中,所谓的雷诺数或 Re 是一个(无量纲)量,有助于预测流型。具有上述参数的流动流体的 Re 由下式给出:
等式 1:在直径为 l 的管道中,运动粘度为ν、速度为 V 的流体的雷诺数。Re 值表示流体的复杂程度。
图 3:直径为 l 的管道中运动粘度为ν、速度为 V 的湍流流体。雷诺数 re 表示流体的复杂程度(来源的修改版本)。*
雷诺数 Re 是对流体复杂性的一种度量。根据 Re 值,流体要么是湍流(高度复杂),要么是层流(低复杂性)。层流变成湍流的过程称为层流-湍流转捩。
图 4:层流。速度曲线“看起来像一副纸牌”。流体在相互滑动的层中起作用(来源)。*
描述不可压缩流体动力学的著名的纳维尔-斯托克斯方程由下式给出:
等式 2:纳维尔-斯托克斯方程,其中 V ( r ,t)是速度矢量场,P 是压力。
其中 V ( r , t )为 r 处的速度矢量,时间 t 和 P 为压力。情商。2 描述了具有非常高 Re 的湍流状态(完全发展的湍流)。
关于能量级联的一个旁白
在具有非线性动力学的系统中,例如具有充分发展的湍流的流体,直接(反向)能量级联涉及能量从大(小)尺度运动到小(大)尺度的转移。如果有中间刻度,这个中间范围叫做惯性范围或惯性子范围。
图 5:该图显示了湍流能谱中的产生、能量级联和耗散(来源)。
惯性范围内的能谱包括从低到高波数的能量转移(能量级联),并具有以下幂律形式:
等式 3:惯性范围内的能谱具有这种幂律形式。
Kolmogorov 1941 年的理论
著名的苏联数学家安德雷·柯尔莫哥洛夫在两篇论文(均来自 1941 年)中表明,对于完全发展湍流的流体(在 Re → ∞极限内的流体),会发生以下行为
方程 4:Re→∞时,均方速度增量的行为,由苏联数学家安德雷·柯尔莫哥洛夫在 1941 年发现。
图 6:苏联数学家安德雷·柯尔莫哥洛夫(来源)。*
在惯性范围内。在方程 4 中, l 对应的距离(见 Stanley 和 Mantegna )小于发生湍流行为的维度,大于动能消散为热量的长度。
然而,Kolmogorov 的理论未能解释速度变化U*(t)=δV(t)的间歇行为(活动在时间演化中突然变化的发生)以及随之而来的 U ( t )的概率分布的轻子态,*
比较金融市场和动荡动态
为了比较金融市场和湍流的时间行为, Stanley 和 Mantegna 分析了两个量,即:
- 1984-1989 年期间标准普尔 500 指数的动态
- 非常高 Re 的三维完全湍流流体的速度V(t)(更具体地说他们认为“在康涅狄格农业研究站树冠以上约 6 米的大气表层的风速”)。
在短时间内,这两个过程都是非平稳、非高斯和间歇的。然而,在长时间内,这两个过程都是渐近稳定的。
Stanley 和 Mantegna 对标准普尔 500 指数和流体速度进行了四次比较,即:
- 在它们的时间演变 Y ( t )和 V ( t )之间
- 它们的变化量Z*(t)=δY(t)和U(t)=δV(t)*
- 作为 Z ( t )和 U ( t )的δt的函数的标准偏差σ*(δt)之间*
- 在 Y ( t )和 V ( t )的功率谱 S ( f )之间
标准普尔 500 指数和流体速度的时间演变
下面的图 7 比较了在间隔δt*= 1 小时采样的标准普尔 500 指数的时间演变和完全发展湍流中的大气风速(在非常高的雷诺数 Re 下)。*
图 7:以一小时为间隔采样的标准普尔 500(上图)。充分发展湍流中的大气风速(具有很高的 Re)(下图)(来源)。
标准普尔 500 指数和流体速度的变化
图 8 在顶部显示了 S & P 500 指数的δt*= 1 小时的间隔的变化,在底部显示了流体速度的变化(在更高的采样率下)。我们看到,对于湍流来说,关于 x 轴是对称的,这在金融数据中是不存在的。这种差异将在下面通过指标Z(t)=δY(t)和流体速度增量U(t)=δV(t)的标准偏差的行为来确认(见图 9 及其上面的讨论)。*
图 8:标普 500 指数δt*= 1 小时的区间变化(上)。流体速度的变化(以更高的采样率) (底部)(来源)。*
标准普尔 500 指数和流体速度增量的标准差
Stanley 和 Mantegna 还针对流程 Z ( t )和 U ( t )研究了波动率σ*(δt)作为δt的函数,如图 8 所示。在这两种情况下,幂律*
等式 5:增量 Z( t )的概率分布的波动性和速度增量 V(t+δt*)-V(t)作为δ*t 的函数都呈现幂律行为,指数分别为ν=0.53 和ν=0.33。**
虽然σ*(δt)在两个过程中都表现出幂律行为,但它们之间的时间相关性却有很大不同。更具体地说,我们有:*
- 概率分布P*(Z(t)的波动率σ*(δt)有一个超扩散行为的初始区间,之后是扩散行为,典型的增量不相关的随机过程(上图)。**
- 概率分布的标准差 P ( U )其中 U 为速度增量U(t)=V(t+δt)-V(t)作为δt的函数
图 9:S&p500 指数时间序列(上)的增量 Z( t )的概率分布 P ( Z )的波动率σ(δt)作为δt的函数。概率分布的标准偏差 P ( U )其中 U 为速度增量U(t)= V(t+δt)-V(t)作为湍流(底部)δt的函数(来源【来源】
光谱密度
平稳随机过程的功率谱是其自相关函数的傅立叶变换:
等式 6:平稳随机过程的功率谱。
随机游走的 S ( f )具有以下函数形式:
方程式 7:随机漫步的光谱密度。
人们可以用它们的功率谱来比较这两个过程。两者都服从函数形式:
等式 8:两个过程的功率谱都具有这种形式,但是η非常不同。在惯性和耗散范围内,标准普尔 500 指数η=1.98,而速度时间序列η=5/3 和η=2。
两个过程都有形式为 Eq 的 S ( f )。8,但指数差别很大。对于 S & P 500 指数,我们得到 η =1.98,这非常接近与随机游走相关的指数。速度时间序列在低频和高频分别有 η =5/3 和 η =2。
异同
对高度湍流流体的动力学和标准普尔 500 指数的比较分析表明,同样的方法可以应用于检查具有已知但(分析上)不可解的运动方程的不同系统。
相似性包括间歇性行为的存在,非高斯概率分布逐渐收敛到高斯吸引子。差异包括两个系统中概率分布的形状,以及速度波动与标准普尔 500 指数波动反相关的事实,标准普尔 500 指数波动是不相关的。
这篇文章是基于关于金融市场动态和动荡行为之间关系的讨论,这些讨论见于自然杂志的文章和斯坦利阁下和 R.N .曼特尼亚的教科书。
我的 Github 和个人网站 www.marcotavora.me 有一些关于金融和其他主题的有趣资料,如数学、数据科学和物理。看看他们!
土耳其语音学:快速介绍
实用土耳其发音语音学介绍
在本文中,我们将探讨土耳其语语音系统的一些细节,包括更高质量的土耳其 ASR 开发技巧😉
在转向对话式人工智能之前,我在一个演讲团队工作。在这篇文章中,我将分享我在开发土耳其语语音识别系统时发现的技巧。作为一个无限的语料库来源😄,我用自己的声音生成例子。为了让一些语音事件更容易理解,我对比了土耳其语和英语的发音。
土耳其语几乎是一种语音语言,大多数单词的发音与书写一致。字母表由 29 个字母、8 个元音和 21 个辅音组成:a b c ç d e f g ğ h ı i j k l m n o ö p r s ş t u ü v y z
。开发一个土耳其 g2p 相对容易,你可以选择一个基于规则的系统。元音容易,辅音容易。单词级重音模式是基于规则的,也不是很复杂。土耳其语的语音系统的确很确定。
我用土耳其 SAMPA 耳机做我的 Kaldi 实验,你可以从这里使用它。对应的语音词库是这里是。在生成该词典的过程中会用到以下所有技巧😉。
剧透:土耳其语听起来/感觉如何
让我们开始探索:
元音
我们的正字法有 8 个元音,没有双元音,没有辅音群,也没有什么新奇的东西。元音分为后/前、圆和高/低。
土耳其语音节模式比较容易,元音和音节之间存在一一对应关系,one vowel = one syllable
。因此,土耳其音节化算法是相当容易的。
kazak ka-zak
yorgan yor-gan
kestirmek kes-tir-mek
getirmek ge-tir-mek
元音就是这样,真的。在接下来的两节中,我将描述关于元音的两点,为了一个好的 g2p,你应该睁大你的眼睛/耳朵。
短/长元音
土耳其语有许多阿拉伯语/波斯语借词。在文字革命之前,土耳其语确实是波斯语、阿拉伯语和土耳其语的混合。今天,现代土耳其语主要由土耳其语单词组成,但仍受到阿拉伯语和波斯语相当大的影响。
这些词的问题是,通常有土耳其同行写的相同但元音确实不同。对比
kâr
(利)kar
(雪)
hâla
(尚)vs hala
(姨妈)
Kâsım
(专有名词,一个男性的名字)vs Kasım
(十一月)
【屋顶痕】,^
被排除在 90 年代的书面语之外。因此,这些单词在正字法中变得不可区分,并给 SMT、ASR 和许多其他统计系统带来麻烦。让我们听单词并比较元音长度:
kar
(斯诺),同“短一”。
对比一下kâr
,下面加个long a
:
1。查看原始信号
2 的元音长度差异。参见频谱图
3 中两个a
共振峰的相似性。也听到看到k
s 的不同,这里k
s 确实不同。k
有两个音位变体,/c/
和/k/
;腭化和非腭化音位变体。长元音也有使同一个音节中的辅音腭化的效果。这里,kâr
中的k
确实是/c/
,其中kar
中的k
是/k/
。从光谱图中可以看出/k/
和/c/
的不同特征。土耳其 SAMPA 区分/c/
和/k/
,显然它们是不同的音位,从频谱特征可以看出:
带“long a”的“kar”(利润)
摘要:虽然写法相同,但是从声谱图上看,kar
和kâr
在声学上非常不同。
**如何处理:**正如我之前说过的,如果一个旧外来词有一个母语为土耳其语的孪生词,就会出现这种情况。这样的单词的数量是有限的,当做土耳其 g2p 时,人们通常从这样的单词的字典中得到帮助。很可能你想在你的语音词典中包括这两种发音,就像这样:
kar: k a r
kar: c a: r
插字
从外来词来说,元音增音在西方外来词中经常发生。我在元音部分介绍中提到过,土耳其语没有声母簇;每个音节有一个元音,每个元音就是一个音节;元音-音节是一一对应的。因此,难怪我们在第一个音节插入一个元音来分割潜在的声母簇。查看发音中的额外元音:
kral k 1 r a 5
tren t i r e n
Brüksel b y r y c s e l
Twitter t i v i t 1 r
我的同事兼朋友,资深语言学家阿萨夫用他受过良好教育的耳朵注意到了这个语音事件。他说我插入了一个额外的元音,我告诉他这在以土耳其语为母语的人中很常见。
让我们听听我口中的Brüksel
。注意频谱图中两个不同的相同元音:
“Brüksel”,发音有两个元音:一个隐藏,一个书写
Ben 是我的同事,一位才华横溢的程序员,母语是英语,来自美国。因此,他的声道肯定知道如何发出我不能发出的辅音串。听他讲Brussels
,从频谱图看发病簇:
“布鲁塞尔”,由一个美国土著人发音。
**小结:**元音增音与西方借词一起发生。
**如何处理:**这几个字很好区分,如果一个字以两个辅音开头;那么就属于这一类。找到第一个拼写元音,然后插入一个合适的音标元音。增音元音应该是高元音([iiuü]
)。
为什么我们不能生产双元音
我们也不能生产双元音,没错😄。我试着发low
的音,但失败了,而本成功了😄
让我们看看实际情况:
“低”,由土耳其母语者发音。与原发音不太相关
与英语不同,土耳其语元音的 back 和 roundedness 是不相关的。我们有前圆元音(ö
、ü
)和后圆元音(o
、u
)。如果我们观察频谱图的共振峰,我们会注意到 F2 值较低,表明存在后向性。相对较高的 F1 变得较低,F3 以及 F1 和 F2 之间的差距暗示着圆整…这个元音是/o/
。所以,我不幸地发了一个单元音。还要注意,语音信号只有一次上升,持续时间相当长。本再次出手相救:
“low”,由美国本土人发音。这个双元音发音完美。
注意频谱图中从一个元音到另一个元音的过渡。然而,在语音信号中只有一个上升;技术上有一个音节;而是频谱图中的两个元音。作为一个美国人,本的声道完美地完成了滑行,而我只能发出“Lovvvvvvvv”很不幸😄
软 G!
软 G 是一个有争议的声音。在土耳其正字法中,它被标记为辅音,但它确实不是一个真正的声音。在某些情况下,它会拉长前一个元音,在某些情况下,它会完全消失。以下是一些观察结果:
- 当它在字尾或音节尾位置时,它拉长前面的后元音,如
dağdan
/d a: d a n/
和dağ
/d a:/
- 相同的后元音之间是听不见的,例如
uğur /u: r/
、ağarmak
、/a: r m a k/
和sığır
、/s ı: r/
。 - 在相同的前元音之间,要么听不见,如
bildiğim
/b i l d i: m/
,要么听起来像腭滑音,如düğün /d y j y n/
。 - 当它出现在 e 和 I 之间时,要么听不见,要么发音为腭滑
/j/
。单词değil
常被听成/d e j i l/
和/d i: l/
。 - 在
i
和e
之间出现时,软 g 多听为腭滑/j/
:diğer
常读作/d i j e r/
,有时误写成diyer
- 在圆元音之间几乎听不见,例如
soğuk
/s o u k/
。 - 在圆元音和非圆元音之间,大部分是听不见的,例如
doğan
/d o a n/
。 a+ğ+ı
序列可能听起来像是/a/
后跟/1/
的序列,或者像是两个/a/
元音的序列:ağır
为/a 1 r
/或/a: r/
。ı+ğ+a
序列发音为/a/
后接/1/
:sığan
/s 1 a n/
动作上看软 G。你会听到单词dağ
(mountain),这里软 G 位于音节末尾,因此他会拉长音节元音a
。查看语音信号中元音的持续时间,并将其与另一个单音节单词kar
进行比较,这是笔记本的第一个单词:
压力,压力,压力
土耳其应力模式高度依赖于形态学。大多数原生词的最后一个音节带有重音,后缀通常将重音“移向”词尾。对于大多数单词来说,词尾重音或多或少是标准的,但旧的阿拉伯语/波斯语借词例外。
我们来看一个例子。koyun
既可以解释为
koyun koy+un <imper><2per> koyun koyun (noun)
命令式后缀将重音带到前一个音节,因此
koyun k o+ j u n <imp><2per>
koyun k o j u+ n (noun)
从我与 Kaldi 的实验中,我注意到神经网络无论如何都学习土耳其重音模式,不需要输入重音位置。的确,还不如根本不标注重音位置,让神经网络去学习。如果你不小心做错了标记,你可能会让 WER 的情况更糟(我就是这么做的😂).
在探讨了咬字之后,我们再来玩一些连续的语音数据,探讨一下土耳其语的声学模型。下面我用的是 Mozilla Common Voice 土耳其语语音语料库。该数据集包含 13725 个话语,总共 14 个小时的讲话。
就像在任何语音任务中一样,首先我提取声学特征向量,我喜欢使用有 40 个特征的mfcc
。代码如下:
import librosafiles = glob.glob("../tr/clips/*.mp3")datas = []for fn in files:
X, sample_rate = librosa.load(fn)
print(fn + ":" + str(sample_rate) + "Hz")
fs=40
mfcc_feature = librosa.feature.mfcc(X,n_mfcc=fs)
datas.append(mfcc_feature)
结果是一个(13725, 40)
数据矩阵。(13725 个实例=话语,40 =声音特征的数量)
然后我用t-SNE
把提取的 mfcc 向量分别投影到二维和三维空间。我用了sklearn
的t-SNE
函数:
嵌入二维空间的 mfcc 特征
嵌入三维空间的 mfcc 特征
mfcc
单独只是一种特征化方法,它不包括任何上下文/顺序信息。它没有提供任何关于哪些音素以何种密度一起出现,哪些音素是相似的,哪些音素在相似的语音环境中一起出现的信息…正如我所说的没有顺序信息。语言和言语是关于语境的,即本质上是连续的。因此我决定做一个更有语境的特征化,我在提取的mfcc
s 上做了一个autoencoder
,它实际上是一个基本的seq2seq
,没什么特别的:
model_inputs = Input(shape=(40,))
inputs = Lambda(lambda x: K.expand_dims(x, -1))(model_inputs)
encoded = LSTM(5, return_sequences=False)(inputs)decoded = RepeatVector(40)(encoded)
decoded = LSTM(1, return_sequences=True)(decoded)
decoded = Lambda(lambda x: K.squeeze(x, -1))(decoded)autoencoder = Model(model_inputs, decoded)
autoencoder.compile(loss='mse', optimizer='adam')
autoencoder.fit(datas, datas, epochs=10)
我用了两个LSTM
,一个用于编码器,一个用于解码器。RepeatVector
层用于构建多对多架构。最终的架构看起来像:
自动编码器架构,基本 seq2seq 模型
好了,接下来是精彩的部分:我提取了编码器 LSTM 的输出,并通过t-SNE
将其投影到三维空间:
编码器 LSTM 输出的三维 t-SNE
这是土耳其语语音编码后的样子,结果非常漂亮。难怪是这样,土耳其人本身就很美。
亲爱的读者,我们已经到了这篇文章的结尾。欣赏美很重要,更重要的是一起欣赏美😄想了解我的更多信息,请关注我的博客,或者随时通过我的 Linkedin 页面联系我。在那之前,请保持快乐、安全和无病毒。干杯!
用函数式语言把 Julia 变成 Python 和 Duck-Type
通过结合 Julia 富有表现力的语法和令人敬畏的调度,将 Julia 变成 Python。
( python logo src , julia logo src ,史酷比是汉娜芭芭拉的注册商标,我不拥有史酷比)
(有个视频可以用这个看!希望你喜欢!)
介绍
Julia 在很多方面都是一种不可思议的语言,作为一种函数式编程语言,它对可变性的支持令人惊讶。此外,Julia 包含了其开箱即用的调度,这是一个简单有效的多态工具。大多数 Julia 用户都很清楚 Julia 的调度,但是他们可能不知道 Julia 有操纵语言进行鸭式打字的能力。使用这种方法,当使用我们的类型和它们各自的方法时,我们可以使 Julia 看起来和感觉上与 Python 非常相似。
派遣
如果你和朱莉娅呆过一段时间,你很可能对朱莉娅的派遣很熟悉。Julia 的调度可用于应用不同的方法来处理不同类型的参数多态性。尽管 Julia 在这方面非常动态,尽管如此(以及可变声明),重要的是要记住我们仍然保留了 Julia 作为函数式编程语言的好处。考虑前两个示例函数:
function _handleArray(array)
[println(i) for i in array]
endfunction _handleDataFrame(df)
for column in eachcol(df)
[println(i) for i in column]
end
end
DataFrame 是一种不能像我们通常使用数组那样逐元素迭代的类型。为了迭代一个数据帧,我们需要使用 eachcol()方法。我们可以使用两个完全独立的函数来处理这两种类型,但是另一种更明智的方法是,我们可以使用 Julia 的调度程序用相同的方法来处理这两种类型。
现在,每当数据帧通过 handle()方法传递时,我们都会得到预期的返回:
同样,当我们传递一个数组时,我们得到完全相同的结果:
不过,我们确实得到了一个有趣的监督阵列。Julia 这样做是因为每当数组像这样循环时,它都会给出一个返回:
[println(i) for i in array]
但是当然,println()的每次调用都不修改现有值或提供返回,所以 Julia 什么也不返回,因为什么也没有返回。这是一个有趣的和有点滑稽的疏忽,我以前从来没有注意到,因为当然
我不经常按元素打印数组。
鸭子打字
在 Julia 中使用一些有趣的类型构造技巧,我们可以像在 Python 和 C++这样的面向对象编程语言中一样,在 Julia 中有效地“鸭式”。这对于创建具有可变数据的对象和操纵该数据的子方法非常有用。这样做的另一个巨大好处是,我们可以有一个初始化函数,每当我们的类型被创建时,这个函数就会运行。当然,这是 Python 的一个主要特点,例如,几乎每次创建新类型时都会调用 init 函数。考虑这个函数:
function our_type(number)
add5() = number += 5
double() = number *= 2
get_number() = number
(var) -> (add5;double;number)
end
结尾的语句,
(var) -> (add5;double;number)
创建一个新的类型,包含我们在创建该类型时使用的所有数据和函数。这可以是数据;在 Int64 例子中的数字,也可以是函数;在 double()或 add5()的例子中。现在,无论何时创建这种类型,我们都可以像调用孩子一样调用函数。
d = our_type(5)
然而,使用这种方法有一些问题。您可能遇到的第一个问题是,当使用此方法时,类型将没有值。例如,如果我们试图调用 typeof ():
这种方法的另一个缺点是访问我们断言为新类型的子类型的数据,在本例中,该数据是 int 64“number”每当我们调用这些数据时,都会得到一种类型的核心。方框:
如果我们试图把这个整数和另一个整数相加,那是行不通的!
然而,我们可以通过添加一个返回数据“数字”的新方法来减轻这种情况考虑这个例子:
然后我们可以像使用普通的 Int64 一样使用返回的数字。正如您在 Python 中所期望的那样,当使用我们的函数在我们的类型内部操作数据时,它实际上是将该操作应用于作为该类型的子类型的数据。
结合两者
虽然这些都是将函数和结构分开使用的好方法,但是它们也可以很好地配合使用。这方面的一个很好的例子是我如何在我的 Julia 的模块化图形库中使用它们,
对于本例,我们有两种方法都用于绘制坐标,但是,其中一种方法特别需要数据帧,即 being _dfscatter:
我们的另一个函数将需要一个数组类型,它被称为 _arrayscatter:
虽然这两种方法都使用 duck-type 方法来使用特定类型的方法,类似于 Python 中的类,但根据数据类型调用不同的函数肯定不是最佳选择。因此,记住这一点,我们可以将这两个概念结合起来,首先用调度函数创建我们的对象,然后用子方法创建我们的变量。当然,首先我们将设置调度:
plot(x::DataFrame,y::Symbol,shape) = _dfscatter(x,y,shape)
plot(x::Array,y::Array,shape) = _arrayscatter(x,y,shape)
然后我们的输入变量:
df = DataFrame(:A => randn(1200), :B => randn(1200), :M => randn(1200), :P => randn(1200), :D => randn(1200), :Y => randn(1200));
shapes = [Circle(.5,.5,.02, :lightblue),Circle(.5,.5,.02, :pink),Circle(.5,.5,.02, :red), Circle(.5,.5,.02, :orange),
Circle(.5,.5,.02, :purple),Circle(.5,.5,.02, :green)];
将我们的地块创建为对象,并且
佩服的美!
如果你想了解更多,我也写了一篇文章!
[## 将 Julia 的多态分派添加到我的绘图库中(第 4 部分)
方法论之争!
towardsdatascience.com](/adding-julias-polymorphic-dispatch-to-my-plotting-library-part-4-1446e924774f)
性能指标
对于机器学习或计算机科学中的任何好的演示,考虑性能总是很重要的。对于这些性能指标,我们将使用 @time ,一个从 IJulia 导出的函数来代替 IPython 的%timeit。
首先,我创建了一个数据框架,它实际上会给我们带来足够的挑战,值得计时,有 100 万个值。虽然这肯定是一个很高的数量,但可能还不够高——但我们肯定会对 Julia 的调度和在 Julia 中键入 duck-typing 之间的度量标准有一个很好的了解。
这是我们的鸭式线性回归函数:
function LinearRegression(x,y)
# a = ((∑y)(∑x^2)-(∑x)(∑xy)) / (n(∑x^2) - (∑x)^2)
# b = (x(∑xy) - (∑x)(∑y)) / n(∑x^2) - (∑x)^2
if length(x) != length(y)
throw(ArgumentError("The array shape does not match!"))
end
# Get our Summations:
Σx = sum(x)
Σy = sum(y)
# dot x and y
xy = x .* y
# ∑dot x and y
Σxy = sum(xy)
# dotsquare x
x2 = x .^ 2
# ∑ dotsquare x
Σx2 = sum(x2)
# n = sample size
n = length(x)
# Calculate a
a = (((Σy) * (Σx2)) - ((Σx * (Σxy)))) / ((n * (Σx2))-(Σx^2))
# Calculate b
b = ((n*(Σxy)) - (Σx * Σy)) / ((n * (Σx2)) - (Σx ^ 2))
# The part that is super struct:
predict(xt) = (xt = [i = a + (b * i) for i in xt])
(test)->(a;b;predict)
end
这是我们的常规函数,以及它的调度和保存数据的结构:
mutable struct LinReg
x
y
endfunction lin_predict(m,xt)
if length(x) != length(y)
throw(ArgumentError("The array shape does not match!"))
end
# Get our Summations:
Σx = sum(x)
Σy = sum(y)
# dot x and y
xy = x .* y
# ∑dot x and y
Σxy = sum(xy)
# dotsquare x
x2 = x .^ 2
# ∑ dotsquare x
Σx2 = sum(x2)
# n = sample size
n = length(x)
# Calculate a
a = (((Σy) * (Σx2)) - ((Σx * (Σxy)))) / ((n * (Σx2))-(Σx^2))
# Calculate b
b = ((n*(Σxy)) - (Σx * Σy)) / ((n * (Σx2)) - (Σx ^ 2))
(xt = [i = a + (b * i) for i in xt])
return(xt)
endpredict(m::LinReg,xt) = lin_predict(m,xt)
当然,使用这些函数,我们将需要创建一个训练 X 和训练 y,以及一个测试 X 和测试 y。
trainX = train[:X]
trainy = train[:Y]
testX = test[:X]
testy = test[:Y]
现在我们可以简单地将它们插入到我们的方法中并比较时间!
结论
Julia 当然有很多很酷很有趣的方法来处理函数类型。我们不仅可以使用 Julia 的 dispatch,还可以利用该语言富有表现力的语法创建自己的临时类型来保存数据和函数。这不仅非常方便,而且还带来了特殊的性能提升。有一些缺点,比如如果不编写函数就不能读取类型或访问数据,但我认为这种权衡是值得的。这是我第一次把一个视频放到一个成熟的视频编辑器中,我真的希望这个结果是值得的——因为哇,Kden-live 吞噬内存就像它什么都不是,而我在整个过程中都被钉在 80%以上的内存使用率上!我的电脑也不喜欢渲染它,这是肯定的!
在 Power BI 中将平面矩阵变成全新的视觉
使用这个简单的技巧快速将传统矩阵转换为更“定制化”的视觉效果
图:截图
Power BI 的最大优势之一是极其庞大的视觉效果集合,包括来自 AppSource 的内置和自定义视觉效果。老实说,几乎每个商业场景都可以通过适当的方式进行可视化处理。
然而,有一些真正简洁而简单的技巧可以将原生 Power BI 视觉效果转换为自定义视觉效果。不仅如此,你还可以称赞它是个人创作的:)
普通旧矩阵
让我们切换到 Power BI,我将快速向您展示如何将普通的矩阵视觉转换为看起来像自定义视觉的东西。
像往常一样,我将使用 Contoso 示例数据库进行演示。假设我们想显示每个品牌每月的总销售额:
如你所见,这个矩阵没什么特别的。一堆数字,所以不深入分析就很难一眼看出高点和低点。
自定义视觉效果
为了使我们的用户能够立即发现正在发生的事情,我们将转到格式窗格下的条件格式选项,并选择销售金额作为应用格式的度量。
我们首先格式化的是背景色。所以,我打开它,进入高级控制:
你可以看到 Power BI 自动应用了一些渐变着色,但是我们想进一步定制它。因为我的整个报告是基于蓝色的,所以我也将应用“蓝色”吨。
您可以注意到单元格的背景颜色发生了变化,以反映销售额数值的差异,但是数字仍然存在。因此,下一步我们应该做的是在字体颜色属性上应用相同的格式。
再次,进入高级控制,设置相同的颜色饱和度作为背景:
看看我们刚刚得到的——“树形图”一样的视觉效果,但是没有与“树形图”视觉效果相关的限制,也不需要应用复杂的格式:
因此,只需点击几下,我们就将普通的旧 Matrix 转变成了现代外观的定制视觉效果!多酷啊!
这样,我们使我们的用户能够很容易地发现,例如,Contoso 和 Fabrikam 的销售额在 5 月和 7 月之间大幅增长,并且 Contoso 最差的月份比除 Fabrikam 以外的任何其他品牌的任何月份都要好!
最重要的是——我们可以在 2 秒钟内轻松发现这些趋势,而无需深究数字。当然,数字仍然在那里,如果我们想检查一个特定的数字,工具提示将像一个魔咒:
结论
不要对你报告中的每一个矩阵都盲目使用这一招。仍然会有更多的场景需要你坚持使用传统的方式来显示数据。
然而,当这种技术有益时,你肯定会面临一些情况(特别是在有很多数字的“宽”矩阵中)。应用这个技巧无疑会丰富你的用户体验。
感谢阅读!
将 Excel 工作簿转换为 SQLite 数据库
图片来自推特:@jankolario 在 Unsplash
将电子表格转换成可查询的数据库表格
介绍
关系数据库 是数据表的集合——存储单个数据的行和列的集合——它们可以相互连接。这样,关系数据库与 Excel 工作簿并不完全不同,Excel 工作簿中的相关数据集存储在多个工作表中。考虑到这一点,本文通过一个例子,使用 Python 将 Excel 电子表格转换成可以使用结构化查询语言(SQL) 查询的数据库。
数据
这篇文章中的例子使用了来自超市销售数据集的数据,可以在这里找到。该数据集存储为 Excel 工作簿,包含存储在以下四个表中的示例销售交易数据:销售、订单、客户和产品。
用 Python 读取数据
首先,如果您没有使用conda
或pip
安装xlrd
包,请在启动 Python 会话之前安装,否则当您试图读取 Excel 文件时会遇到以下错误(即使安装了 pandas )。
现在让我们启动一个 Python 会话,并使用import pandas
和import sqlite3
导入熊猫和 sqlite3 。我们将使用read_excel
将每个 Excel 电子表格中的数据读入一个单独的 pandas 数据框,如下面的代码所示。
在每个代码块中,read_excel
中的第一个参数标识要处理的文件的名称。如有必要,包括文件所在的路径。例如,当我运行这段代码时,我的数据存储在data
目录中,但是我的工作目录在上面一个目录中。第二个参数sheet_name =
,指定工作簿中要处理的电子表格的名称。最后一个参数header=0
告诉 Python 正在读取的电子表格中的第一行包含列标题。记住 Python 是零索引的,这就是为什么我们使用0
而不是1
来标识第一行。通过明确标识标题行,每个电子表格第一行中的值将被视为其各自数据框的列名。
让我们看一下每个数据框的前几行,以确保一切看起来都是正确的。
太好了!现在让我们创建数据库。
创建 SQLite 数据库
有多种关系数据库产品可用,这里我们将使用的具体产品是 SQLite 。这是一个轻量级的 SQL 数据库引擎,可用于在个人计算机上创建存储为文件的数据库。我们可以启动一个新的 SQLite 数据库连接对象,并将该对象赋给一个变量。下面,我把这个变量命名为db_conn
。
db_conn = sqlite3.connect("data/superstore.db")
当执行上述代码时,将在数据目录中创建一个名为 superstore.db 的新文件(假设 superstore.db 在该目录中尚不存在)。新创建的 superstore.db 文件此时是一个空的 SQLite 数据库(即,它没有表)。db_conn
也指向 superstore.db 数据库,可以认为是使用这个数据库的接口。酷!但是等等…我们实际上如何运行创建数据表的 SQL 代码呢?很高兴你问了。我们现在需要建立一个 游标 对象,它是一个针对感兴趣的数据库执行 SQL 代码的工具。这里,光标被分配给一个名为c
的变量。
c = db_conn.cursor()
下一步是创建将包含在数据库中的表,尽管需要强调的是,创建表的下一步将导致空表。稍后,我们将用之前创建的四个数据框中的数据填充我们的表。然而,在我们继续之前,让我们看一下将用于创建销售表的实际 SQL 代码,以便更好地理解表的创建过程。
上面代码中的第 1 行提供了创建名为 sales 的表的命令,第 2–8 行在 sales 中创建了 7 个新列:salesID、OrderID、ProductID、Sales、Quantity、Discount 和 Profit。每列的数据类型在相应列名的右侧指定。关于 SQLite 数据类型的更多信息可以在这里找到。请注意,列的顺序与关联数据框中列的顺序相匹配。
CREATE TABLE
语句中的列顺序是有意安排的,因为这确保了数据框中的适当值进入数据库表中的预期列。例如,如果我将 OrderID 作为第一列,将 SalesID 作为第二列,那么 SalesID 值将被写入到 sales 表中的 OrderID 列,并且该表中的 SalesID 列将包含 OrderID 值。
第 9 行建立了表的主键,这是一个包含唯一标识每行的值的列。在销售表中,SalesID 满足主键的要求,因为没有两行具有相同的 SalesID 值。
“销售”表的快照
上一组代码中的第 10 行为销售建立了两个外键中的第一个。外键是一个表中的列,它是另一个表中的主键。例如,请注意,在销售表中,不同的行可以共享相同的 OrderID 值,这使得 OrderID 不再是该表中的主键。然而,订单表中的每一行确实包含一个唯一的 OrderID 值。因此,OrderID 可以作为订单的主键。
“订单”表的快照
让我们回到前面的CREATE TABLE
语句中第 10 行的实际代码。代码的第一部分FOREIGN KEY(OrderID)
确定 sales 中的 OrderID 列是一个外键。第二部分,REFERENCE orders(OrderID)
指定 OrderID 引用的表和主键。CREATE TABLE
语句的第 11 行遵循相同的 ProductID 列逻辑。
通过指定主键和外键,我们能够创建一个“映射”,显示数据库中的表是如何相互关联的。例如,熟悉 SQL 概念但不熟悉该特定数据的人可以查看创建销售表的CREATE TABLE
语句,并认识到通过匹配两个表的 OrderID 列中的值,可以将销售和订单表中的数据放在一起,或者将 连接到 。为了简要说明联接是如何工作的,考虑我们如何通过在 OrderID 上“联接”来将 OrderDate 添加到 sales 表中,如下所示。
联接如何工作的基本视觉演示
正在完成数据库
建立了表创建过程的基本概述后,让我们创建所有的数据库表。这是我们放置光标的地方,c
开始工作。我们可以运行c.execute()
并将所需的 SQL 代码作为字符串包含在括号中,以针对我们当前连接的数据库运行所述 SQL 代码(即data/superstore.db
)。
现在是时候用我们之前创建的四个数据框中的相关数据填充数据库中的表了。幸运的是,在每个数据帧上使用pandas*’to_sql
可以轻松完成这一步(更多细节见to_sql
这里)。下面块中的代码将数据从四个数据框中的每一个传输到数据库中适当的表中。*
上面每一行代码中的第一个参数标识数据框中的值将被写入的数据库表的名称,后面的参数指定实际的数据库连接。接下来,if_exists='append'
告诉to_sql
如果表已经存在(在本例中确实如此),那么应该将数据框中的值插入到表中。最后,通过index=False
,我们告诉to_sql
不要将索引作为附加列包含在表中。
结论
现在,我们已经有了一个准备就绪的 SQLite 数据库!可以用 SQLite 命令行 shell 或者其他支持 SQLite 的数据库软件,比如 DbVisualizer 来查询 superstore.db 数据库文件。您还可以使用 pandas 中的read_sql
在数据库上运行查询(参见更多信息此处)。例如,如果我们希望看到来自 sales 的前五行,我们可以在我们的 Python shell 或笔记本中运行如下所示的代码。
*pd.read_sql("SELECT * FROM sales LIMIT 5", db_conn)*
当您用 Python 处理完这个数据库后,您可以通过运行db_conn.close()
来关闭连接。如果您想在关闭后再次使用该数据库,只需重新建立数据库连接
*db_conn = sqlite3.connect("data/superstore.db")*
并使用read_sql
运行查询。如果不将数据写入数据库,则不需要建立新的游标。
在我们结束之前,有一句话需要澄清。在这篇文章的开始,我提到了 Excel 工作簿的结构类似于 SQL 数据库,因为两者都可以由多个相关的表组成。为此,本练习旨在演示如何将 Excel 工作簿轻松转换为 SQLite 数据库文件。尽管如此,我们只是触及了数据库设计的表面,我想强调的是,一个设计良好的数据库不仅仅是表的集合。的确, 规范化的过程在关系数据库设计中非常重要,其目的是消除数据冗余,促进数据完整性。请注意,我们创建的数据库可以进一步规范化。如果您有兴趣了解更多关于关系数据库和跨多个表存储数据背后的基本原理,我推荐您阅读更多关于规范化的内容。
好吧,这将是这个职位。感谢您的阅读,如果您有任何问题和/或建设性反馈,请联系我们。
把你的爱好数据分析项目变成一个投资组合
让人力资源经理喜欢你的简历!尤利娅·卢卡希娜
如果你之前的工作与数据科学技能没有什么关系,你仍然可以通过在线主持你的爱好项目来说服未来的雇主。
我来到研讨室,在那里我们有安装了 SPSS 的电脑,世界消失了几个小时。我以为生活可以永远这样。我相信我已经属于数据科学社区了。
但后来我知道我错过了一堆数据科学家应该知道的事情。我仍然从事着一份资质一般的数据分析师工作,因为我下定决心要获得缺失的技能。
两个在线学习平台的结业证书是不够的。在德国,数据科学行业仍在兴起,而且还不稳定。
我必须拿出看得见的证据来证明我可以做数据科学。我把我的爱好学习项目放到网上,公之于众,并把链接附在我的求职信和简历上。
这引起了一些雇主的兴趣,尽管几乎没有人真正使用我发布的公共平台。
这只是让我在展示之前稍微润色了一下我的结果。例如,给图表添加更多的注释。绘制更多图表。在求职信中简单解释一下,这个平台是怎么回事。
卡格尔
我使用 Kaggle 作为投资组合和免费的云计算机器。
这个项目是以减价的形式进行的。它对数据进行了详细的概述,并解释了为什么对这些数据感兴趣。
报告从原始数据的一个例子开始,有前十行。然后,它列出了面临的挑战,并清楚地表明精心的数据清理是必要的。
下一部分由几个代码块组成,每个代码块都做一些数据整理工作。代码前面是注释,然后是简短的输出,接着是数据样本。
在数据变得可靠之后,描述性的部分开始了。通常,我也会进行一些分组和简单的计算,然后用另一个数据样本来突出原始数据和处理后数据之间的差异。
脚本中的最后一个命令通常是保存 CSV 文件的命令,我后来用它来制作 Tableau 可视化效果。
描述应该有多详细
作为一名博士生,我经常遇到数据可视化或其他呈现结果的方式,如平面表格,它们并不总是自我解释的。
因此,当我自己写报告时,我倾向于过多地关注细节。我使用 Markdown 格式来比较每个数据处理步骤和每个自定义函数的输入和输出。
高水平的细节可以帮助你吸引不是数据科学家的人的注意。例如,想想人力资源经理。即使你的报告对一个非专业人士来说仍然过于“科学”,你的读者也会感谢你关心他的理解。
我特别推荐解释每个自定义函数。写一行注释来启发你对 if 语句的选择也是很好的。你需要弄清楚,你想解决什么样的问题。
如果你不提供至少一个简短的句子,任何超出简单过滤的转换都可能会使读者困惑。由于你有时有很多选择来处理你的数据以达到你的目标,如果你解释了你的决定,你会得到一个额外的分数。
Tableau 公共
尽管我的简历中有一个 Tableau 记录,但我更喜欢把我的公开资料链接发给雇主。我想尽我所能向他们展示,但也想让他们知道我对雄心勃勃的数据即项目感兴趣。
对于不熟悉这个工具的人来说,有时并不清楚 Tableau 不仅仅是 Excel 图表更好的替代品。
我创建了一个 Tableau 在线演示,并在面试中浏览了一遍(当然,如果有人问我的话)。我解释了我的研究问题,以及我将通过可视化实现的目标。基本上,Tableau 仪表板是一种交互式报告,允许用户关注特定的信息或特定的视角。
开源代码库
在我的一个数据科学爱好项目中,我想到了编写一个简单的单功能 Python 应用程序的想法。它在我的笔记本电脑上运行,应该是为手机发布的。反正在这段编程经历中,我学会了用 Visual Studio Code 2,免费的那个。我把它和一个 GitHub 库连接起来。当我必须从下载切换到 API 调用时,我创建了第二个分支。
了解 GitHub 并与分支机构合并和合作对于 IT 团队来说至关重要。
另一个好处是,你可以把它公之于众,给你潜在的雇主看。
社交机会
总的来说,所有这些平台——ka ggle、Tableau Public、GitHub——都提供了社交机会。我对此并不积极,但我确实检查了人们在 Kaggle 或 Tableau 上做的项目。我不时得到一些观点和喜欢,或者看到有人开始跟踪我。
你可以加入其中一个论坛讨论,询问任何技术问题或分享你对找工作的想法。
你简历的另一个加分点是分享解决方案、自定义功能或变通方法的社区帖子。也许你甚至会发布一个新的包,谁知道呢!
利用每一个机会给你未来的雇主一个惊喜,不要等着他们用谷歌搜索你的名字,只找到你在脸书的照片。你拥有的不止这些。
使用 Python 和 OpenCV 让自己隐形
将一个人从背景中移除的确是一项有趣又尴尬的任务。在本指南中,我想一步一步地展示如何使用 OpenCV 和 Python 从直播流中删除一个人。
照片由阿索格蒂在 unsplash.com 拍摄
几天前,在浏览黑客新闻时,我对谷歌工程师杰森·梅斯的T3 项目印象深刻。从我第一次看到这个项目的那一刻起,我就决定一定要(至少尝试)复制它。然而,由于我的随身工具主要是 R 和 Python,所以我决定用后者来实现它。
你需要什么:
- Python 3.x.x(我用的是 Python 3.7.4)
- OpenCV(我使用的是版本 4.1.2)
本项目中描述的不同阶段的分解。
头脑风暴:
在做了一些头脑风暴后,我意识到,要从稳定的图像中删除一个对象,我很可能需要一个锚点作为起点,然后复制粘贴每一帧,就像一个遮罩一样,应用于包含一个人的每一个后续帧。
然后我想,如果我有我想要隐藏的区域的坐标,我可以简单地从锚帧复制它,并在我想要隐藏对象的当前帧上替换它。
一个我被检测到的帧的例子。
要解决的第二个问题是找到一种方法来检测我想要移除的感兴趣的对象。幸运的是,OpenCV 提供了一种简单的方法:基于支持向量机的梯度方向直方图检测器。这是一个必去的人探测器,不是最快的,不是最准确的,也不是最好的,但它就是工作。
工作流程:
因此,在头脑风暴之后,我决定坚持以下工作流程:
- 实例化
HOGDescriptor
- 获取视频的第一帧以用作遮罩
- 迭代每一帧,对于每个检测到的人,用第一帧中相应的“空”区域替换该区域
- 保存输出
代码:
按照之前描述的工作流程,我在我的 GitHub 库下找到了下面的代码。
来测试一下吧!
像老板一样。把手放在口袋里,然后消失!
然而,引用埃隆·马斯克的话:
“仍有改进的空间”
实际上没有这么精确,尤其是当我靠近镜头的时候。
测试完这段代码后,我意识到我可能是在正确的道路上,但是,检测不够精确,整个输出看起来有问题且不稳定。
所以我意识到我需要找到一种方法来改进它:用第一帧替换每个检测到的人似乎是一个好方法,所以我可能需要找到一种更好的方法来检测物体,一个更好的模型!
我记得不久前我写了一篇简短指南关于如何开始使用脸书的 Detectron2 模型,那么为什么不实现它而不是 HOG 检测器呢?
改进之处:
在 COCO 的模型动物园上搜索我发现了一个实例分割模型,每张图片的推理时间为 0.07 秒,这是最快的可用模型之一(可能不是最准确的)。然后我决定使用它。
要在我的管道中插入一个定制模型,必须安装所有必需的依赖项,如pytorch
、torchvision
和detectron2
:
# install dependencies:!pip install -U torch==1.4+cu100 torchvision==0.5+cu100 -f [https://download.pytorch.org/whl/torch_stable.html](https://download.pytorch.org/whl/torch_stable.html)!pip install cython pyyaml==5.1!pip install -U ‘git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'import torch, torchvision
以下代码和指令是在 Google Colab 实例上测试的,做出这一选择是为了让这个实验更容易复制,而不会有缺少依赖、版本冲突和所有经常发生的无聊事情。
然后我们需要安装检测器 2:
# install detectron2:!git clone [https://github.com/facebookresearch/detectron2](https://github.com/facebookresearch/detectron2) detectron2_repo!pip install -e detectron2_repo
现在我们可以导入所有需要的库并加载模型(完整代码请参考 Google Colab 笔记本或 GitHub repo ):
cfg = get_cfg()cfg.merge_from_file(model_zoo.get_config_file(“COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x.yaml”))cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5 # set threshold for this modelcfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url(“COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x.yaml”)predictor = DefaultPredictor(cfg)
不可能使用我们的predictor
来推断类,predictor
返回一个需要转换成numpy
数组的张量数组,然后可以像我之前做的一样迭代它:
outputs = predictor(frame)outputs = outputs[“instances”].pred_boxes
.to(‘cpu’)
.tensor
.numpy()
.astype(int)
让我们来看看最终结果:
探测器 2 与全息探测器
从 gif 中可以观察到 Detectron2 如何更准确地检测到一个人,然而,需要指出的是,当然,它需要一些更“深入”的配置(依赖关系有时是一种斗争)。但是,最后的结果自己说话!
几个最后的考虑:
尽管最终获得的结果相当令人惊讶,但总有改进的余地。仔细观察我身后的滑雪者,可以发现当我从他们面前经过时,面具使他们自己消失了(这种现象被称为遮挡)。此外,当前帧上的“切割”矩形之间的插值非常明显。为了解决这两个问题,能够执行全景分割的模型可能会有所帮助。
最后的对比视频:
**I have a newsletter 📩.**Every week I’ll send you a brief findings of articles, links, tutorials, and cool things that caught my attention. If tis sounds cool to you subscribe.*That means* ***a lot*** *for me.*
[## 米尔斯形式
编辑描述
无情-创造者-2481.ck.page](https://relentless-creator-2481.ck.page/68d9def351)
将位置数据转化为地理信息
纽约星巴克的位置、人口密度和家庭收入
丹尼尔·冯·阿彭在 Unsplash拍摄的照片
介绍
如果你搜索“我附近的杂货店”,谷歌地图会显示附近商店的位置。谷歌地图使用位置数据在地图上显示附近的商店。这些数据可能包含其他信息,如商店详细信息、地址、电话号码,但地理位置本身是关键。位置数据的使用有很多原因-就像查找杂货店一样简单,但也可以用于更复杂的决策,例如在哪里新开一家餐馆。
这篇文章是对上一篇关于点模式分析的文章的补充,目的是演示一些人可以用位置数据做的基本事情。没有太多复杂的分析,但我将重点关注如何在地图上显示位置数据,并解释这些数据点对于周围和潜在变量(如人口和收入)的意义。
“我附近的杂货店”的谷歌地图搜索结果
星巴克选址:案例研究
在这个案例研究中,我使用的是星巴克的位置数据。这些数据有点过时了,但我怀疑这些年来地点有很大变化。
此外,我使用县级人口普查数据中的一些变量来理解可能与星巴克位置相关的潜在过程:
- 家庭收入
- 总人口
- 人口密度
目的是看这些变量中的哪一个与星巴克店的位置相关(没有暗示相关性)。我们没有进行任何花哨的地质统计分析,只是在数据层上叠加数据层,并观察它们以发现是否有任何有趣的模式出现。
位置与底层流程的关联
为了映射位置数据和人口普查变量,我用 r 实现了这个练习。Python 有很好的库来做这种工作,但是我想利用tidycensus
包来处理人口普查数据。首先,我们需要安装一些库。
# To access census variables and polygons
library(tidycensus)# To work with spatial polygons
library(sf)# To work with the dataframe associated with the spatial data
library(tidyverse)# To make maps
library(mapview)
要使用人口普查局 API 访问数据,您需要一个 API 密钥。这是一个简单的过程,只需去人口普查局网站申请一个 API 密匙,并把它发送到你的邮箱。一旦有了密钥,就将它加载到您的环境中。
# load API key
census_api_key("YOUR API KEY")
现在我们可以加载星巴克的位置数据了。该数据集包含全国范围的星巴克位置,您可以过滤您感兴趣的任何州/地理位置。我在过滤纽约州。
# load data
data = read_csv("[https://raw.githubusercontent.com/libjohn/mapping-with-R/master/data/All_Starbucks_Locations_in_the_US_-_Map.csv](https://raw.githubusercontent.com/libjohn/mapping-with-R/master/data/All_Starbucks_Locations_in_the_US_-_Map.csv)")# filter Starbucks locations in New York State
df = filter(data, State == "NY")# plot the locations
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE, legend = FALSE, alpha = 0.5, cex = 1)
纽约的星巴克店
下一步是通过感兴趣的变量的tidycensus
从人口普查局导入人口普查数据。我正在获取家庭收入和人口变量。
让我们先来看看纽约州的家庭收入是如何分布的。
# get income data via `tidycensus``
income = get_acs(geography = "county", variables = "B19013_001", state = "NY", geometry = TRUE)
纽约州各县的家庭收入分布情况
毫不奇怪,一些收入最高的家庭位于纽约市附近的县。我会放大那个区域。
但是让我们先得到其他变量。
# get population data via `tidycensus``
pop = get_acs(geography = "county", variables = "B01003_001", state = "NY", geometry = TRUE)# add population density
dens = as_Spatial(pop) # convert `sf` object (pop) to SpatialPolygonDataFrame # add area column
dens$Area_sqmile <- area(dens)/2.59e+6# add population density columns
dens$popdensity = dens$estimate/dens$Area_sqmile
绘图和解释
首先,让我们将星巴克的位置放在收入数据的顶部,看看高收入的县是否有大量的星巴克门店。我再一次放大了纽约市的区域。
# map starbucks on income
mapview(income, zcol = "estimate") +
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE,legend = FALSE, alpha = 0.5, cex = 1)
纽约市各县(多边形)和星巴克所在地(圆点)的家庭收入
所以大多数星巴克店都位于曼哈顿地区。但这并不是家庭收入最高的地方,收入最高的家庭在拿骚县(黄色多边形)。我们可以推断,星巴克的门店不一定位于富人区,意味着存在一种微弱的(如果有的话)关联。
现在让我们试一下总人口,看看人口多的县是否有很多星巴克店。
# map starbucks on population
mapview(pop, zcol = "estimate") +
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE,legend = FALSE, alpha = 0.5, cex = 1)
纽约市各县(多边形)和星巴克所在地(圆点)周围的总人口
就人口而言,金斯县(黄色多边形)和皇后县是纽约市人口最多的县,但这也不是大多数星巴克店的所在地。这意味着,像收入一样,总人口似乎也是星巴克门店位置的一个弱预测因素。
最后,我们来看看人口密度是否有关系。
# map starbucks on population density
mapview(dens, zcol = "popdensity") +
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE,legend = FALSE, alpha = 0.5, cex = 1)
纽约市各县(多边形)和星巴克所在地(圆点)周围的人口密度
这就对了。
人口密度最高的是曼哈顿地区,那里也是星巴克店最多的地方。因此,地理位置和人口密度之间似乎存在某种联系,这种联系强于各县的总人口或家庭收入。
结论
从这些关联分析中,我们发现人口密度是一个更好的预测因素,可以预测附近是否有星巴克,而收入则不是一个因素。然而,作出这样的概括是相当简单的,因为还有我没有考虑的其他因素,如人口的年龄,大学生的数量,种族等等。此外,我们还可以测试这种关联在美国的其他州和地区是否成立。
在本例中,您学习了如何绘制位置数据和使用人口普查地图。你还学会了如何提出问题,测试一个假设,甚至产生一个新的假设。在以后的文章中,我将进一步分析点数据,敬请关注。
教程:为数据科学构建您自己的大数据基础架构
Justin Jairam 拍摄的照片来自 @jusspreme
从事自己的数据科学项目是学习新技能和磨练现有技能的绝佳机会,但如果您想使用 Hadoop、Spark on a distributed cluster、Hive 等行业中使用的技术,该怎么办呢?并把它们整合在一起。在构建自己的基础设施时,这就是价值的来源。
大数据格局
您熟悉了这些技术,了解了它如何运行的细节,调试并体验了不同类型的错误消息,真正了解了技术的整体工作原理,而不仅仅是与它进行交互。如果您也在处理自己的私有数据或机密数据,出于隐私或安全原因,您可能不希望将其上传到外部服务来进行大数据处理。因此,在本教程中,我将介绍如何在自己的计算机、家庭实验室等设备上设置自己的大数据基础架构。我们将设置一个单节点 Hadoop & Hive 实例和一个与 Jupyter 集成的“分布式”spark 集群。
编辑:多亏了 @Daniel Villanueva 你现在可以部署一个预先配置了 Hadoop、Spark 和 Hive 的虚拟机,并准备好通过他的流浪映像。你可以在他的 Github 这里查看。
本教程不适用于工业生产安装!
先决条件
- 基于 Debian 的发行版——Ubuntu,Pop-os 等
- 基本的命令行知识会有所帮助,但对于安装来说不是必需的
步骤 1 —下载 Hadoop 和 Hive
Hadoop 无疑是当今行业中最常用的大数据仓库平台,是任何大数据工作的必备知识。简而言之,Hadoop 是一个开源软件框架,用于以分布式方式存储和处理大数据。你可以从这里下载最新版本。
Hive 通常添加在 Hadoop 之上,以类似 SQL 的方式查询 Hadoop 中的数据。Hive 使作业易于执行操作,如
- 数据封装
- 即席查询
- 大型数据集的分析
Hive 速度较慢,通常仅用于批处理作业。一个更快的 Hive 版本可能类似于 Impala,但对于家庭使用来说,它可以完成任务。你可以在这里下载最新版本的 Hive。
确保您下载的是二进制(bin)版本,而不是源代码(src)版本!
将文件解压缩到/opt
cd ~/Downloads
tar -C /opt -xzvf apache-hive-3.1.2-bin.tar.gz
tar -C /opt -xzvf hadoop-3.1.3.tar.gz
将它们重命名为hive
和hadoop
。
cd /opt
mv hadoop-3.1.3 hadoop
mv apache-hive-3.1.2-bin hive
步骤 2 —设置授权(或无密码)SSH。
我们为什么需要这样做?Hadoop 核心使用 Shell (SSH)在从属节点上启动服务器进程。它要求主节点和所有连接节点之间的无密码 SSH 连接。否则,您必须手动转到每个节点并启动每个 Hadoop 进程。
由于我们运行的是 Hadoop 的本地实例,我们可以省去设置主机名、SSH 密钥以及将它们添加到每个机器中的麻烦。如果这是一个分布式环境,最好也创建一个hadoop
用户,但是对于单个节点设置和个人使用来说,这是不必要的。
真正简单的,只适合在家里使用,不应该在其他地方使用或完成的方式是:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
现在运行ssh localhost
,你应该可以不用密码登录了。
无密码 SSH 登录
为了了解在分布式环境中设置网络和 SSH 配置需要做些什么,您可以阅读这个。
步骤 3 —安装 Java 8
本教程最重要的步骤之一。
如果做得不正确,将会导致耗费大量时间调试模糊的错误消息,只是为了意识到问题和解决方案是如此简单。
Hadoop 有一个主要需求,这就是 Java 版本 8。有趣的是,这也是 Spark 的 Java 需求,也非常重要。
sudo apt-get update
sudo apt-get install openjdk-8-jdk
验证 Java 版本。
java -version
Java 版本
如果由于某种原因,您没有看到上面的输出,您需要更新您的默认 Java 版本。
sudo update-alternatives --config java
更新 Java 版本
选择与 Java 8 相关的数字。
再次检查版本。
java -version
正确的 Java 版本
步骤 4 —配置 Hadoop + Yarn
Apache Hadoop YARN(又一个资源协商器)是一种集群管理技术。在非常基本的层面上,它帮助 Hadoop 管理和监控其工作负载。
初始 Hadoop 设置
首先让我们设置我们的环境变量。这将告诉其他组件每个组件的配置位于何处。
nano ~/.bashrc
将此添加到您的.bashrc
文件的底部。
export HADOOP_HOME=/opt/hadoop
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/binexport LD_LIBRARY_PATH=$HADOOP_HOME/lib/native:$LD_LIBRARY_PATHexport HIVE_HOME=/opt/hive
export PATH=$PATH:$HIVE_HOME/bin
保存并退出 nano CTRL + o
,CTRL + x
。
然后我们需要通过运行source ~/.bashrc
来激活这些更改。您也可以关闭并重新打开您的终端来达到同样的效果。
接下来,我们需要制作一些目录和编辑权限。创建以下目录:
sudo mkdir -p /app/hadoop/tmp
mkdir -p ~/hdfs/namenode
mkdir ~/hdfs/datanode
编辑/app/hadoop/tmp
的权限,授予其读写权限。
sudo chown -R $USER:$USER /app
chmod a+rw -R /app
配置文件
所有的 Hadoop 配置文件都位于/opt/hadoop/etc/hadoop/
中。
cd /opt/hadoop/etc/hadoop
接下来,我们需要编辑以下配置文件:
- core-site.xml
- hadoop-env.sh
- hdfs-site.xml
- mapred-site.xml
- yarn-site.xml
core-site.xml
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>/app/hadoop/tmp</value>
<description>Parent directory for other temporary directories.</description>
</property>
<property>
<name>fs.defaultFS </name>
<value>hdfs://YOUR_IP:9000</value>
<description>The name of the default file system. </description>
</property>
</configuration>
hadoop.tmp.dir
:不言自明,只是一个 hadoop 用来存储其他临时目录的目录fs.defaultFS
:你的文件系统通过网络访问的 IP 和端口。如果这是一个分布式系统,它应该是您的 IP,以便其他节点可以连接到它。
要查找您的 ip,请在命令行中键入ip addr
或ifconfig
:
hadoop-env.sh
- 确定 Java 8 JDK 的位置,它应该与
/usr/lib/jvm/java-8-openjdk-amd64/
相似或相同 - 在
hadoop-env.sh
中添加以下一行:export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
<description>Default block replication.</description>
</property>
<property>
<name>dfs.name.dir</name>
<value>file:///home/YOUR_USER/hdfs/namenode</value>
</property>
<property>
<name>dfs.data.dir</name>
<value>file:///home/YOUR_USER/hdfs/datanode</value>
</property>
</configuration>
dfs.replication
:在多少个节点上复制数据。
dfs.name.dir
:NameNode 块的目录
dfs.data.dir
:数据节点块的目录
mapred-site.xml
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobtracker.address</name>
<value>localhost:54311</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>mapreduce.map.memory.mb</name>
<value>4096</value>
</property>
<property>
<name>mapreduce.reduce.memory.mb</name>
<value>4096</value>
</property>
</configuration>
mapreduce.framework.name
:执行 MapReduce 作业的运行时框架。可以是本地、经典或纱线。
mapreduce.jobtracker.address
:MapReduce 作业跟踪器运行的主机和端口。如果是“本地”,则作业作为单个映射和简化任务在进程中运行。
yarn.app.mapreduce.am.env
:纱线图减少环境变量。
mapreduce.map.env
:贴图减少贴图环境变量。
mapreduce.reduce.env
:贴图减少减少环境变量。
mapreduce.map.memory.mb
:Hadoop 允许分配给映射器的内存上限,以兆字节为单位。默认值为 512。
mapreduce.reduce.memory.mb
:Hadoop 允许分配给 reducer 的内存上限,以兆字节为单位。默认值为 512。
yarn-site.xml
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>localhost</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>16256</value>
</property>
<property>
<name>yarn.app.mapreduce.am.resource.mb</name>
<value>4096</value>
</property>
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>4096</value>
</property>
</configuration>
yarn.resourcemanager.hostname
:RM 的主机名。也可能是远程 yarn 实例的 ip 地址。
yarn.nodemanager.aux-services
:选择运行 MapReduce 需要设置的随机播放服务。
yarn.nodemanager.resource.memory-mb
:可以分配给容器的物理内存量,以 MB 为单位。作为参考,我的机器有 64GB 的内存。如果这个值太低,您将无法处理大文件,得到一个FileSegmentManagedBuffer
错误。
yarn.app.mapreduce.am.resource.mb
:该属性指定为特定作业选择资源标准。任何具有相同或更多可用内存的节点管理器都将被选择来执行作业。
yarn.scheduler.minimum-allocation-mb
:RM 上每个容器请求的最小分配量,单位为 MBs。低于这个值的内存请求将不会生效,指定的值将被分配为最小值。
启动 Hadoop
在开始 Hadoop 之前,我们必须格式化 namenode:
hdfs namenode -format
现在我们可以开始 Hadoop 了!运行以下命令:
start-dfs.sh
start-yarn.sh
要确保一切都已启动,请运行以下命令:
ss -ln | grep 9000
端口 9000 网络信息
运行jps
运行 Java 程序
您现在还可以在 http://localhost:9870 访问 Hadoop web UI。
Hadoop Web 用户界面
您也可以在 localhost:8088 访问 Yarn web UI。
纱网用户界面
步骤 5 —设置配置单元
既然我们已经启动并运行了 Hadoop,让我们在它的基础上安装 Hive。
首先,让我们在 Hadoop 中创建一个目录,我们的 Hive 表将存储在这个目录中。
hdfs dfs -mkdir -p /user/hive/warehouse
配置权限。
hdfs dfs -chmod -R a+rw /user/hive
设置 Metastore
配置单元 Metastore 是配置单元元数据的中央存储库。它存储了配置单元表和关系(模式和位置等)的元数据。它通过使用 metastore 服务 API 提供对此信息的客户端访问。有 3 种不同类型的元存储:
- 嵌入式 Metastore:一次只能打开一个配置单元会话。
- 本地 Metastore:多个配置单元会话,必须连接到外部数据库。
- 远程 metastore:多个 Hive 会话,使用 Thrift API 与 Metastore 交互,更好的安全性和可扩展性。
为了更详细地了解每种类型的 metastore 之间的区别,这是一个很好的链接。
在本指南中,我们将使用 MySQL 数据库设置一个远程 metastore。
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
运行以下命令:
sudo mysqlCREATE DATABASE metastore;
CREATE USER 'hive'@'%' IDENTIFIED BY 'PW_FOR_HIVE';
GRANT ALL ON metastore.* TO 'hive'@'%' WITH GRANT OPTION;
在 MySQL 中将PW_FOR_HIVE
替换为您想要用于 hive 用户的密码。
下载 MySQL Java 连接器:
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.19.tar.gz
tar -xzvf mysql-connector-java-8.0.19.tar.gz
cd mysql-connect-java-8.0.19
cp mysql-connector-java-8.0.19.jar /opt/hive/lib/
编辑 hive-site.xml
现在编辑/opt/hive/conf/hive-site.xml
:
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://YOUR_IP:3306/metastore?createDatabaseIfNotExist=true&useLegacyDatetimeCode=false&serverTimezone=UTC</value>
<description>metadata is stored in a MySQL server</description>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
<description>MySQL JDBC driver class</description>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hive</value>
<description>user name for connecting to mysql server</description>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>PW_FOR_HIVE</value>
<description>password for connecting to mysql server</description>
</property>
</configuration>
用本地 ip 地址替换YOUR_IP
。用您之前为 hive 用户初始化的密码替换PW_FOR_HIVE
。
初始化架构
现在让我们让 MySQL 可以从网络上的任何地方访问。
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
将bind-address
改为0.0.0.0
。
重新启动服务以使更改生效:sudo systemctl restart mysql.service
最后,运行schematool -dbType mysql -initSchema
来初始化 metastore 数据库中的模式。
启动配置单元 Metastore
hive --service metastore
测试蜂箱
首先通过调用hive
从命令行启动 Hive。
让我们创建一个测试表:
CREATE TABLE IF NOT EXISTS test_table
(col1 int COMMENT 'Integer Column',
col2 string COMMENT 'String Column')
COMMENT 'This is test table'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE;
然后插入一些测试数据。
INSERT INTO test_table VALUES(1,'testing');
然后我们可以查看表中的数据。
SELECT * FROM test_table;
步骤 6 —设置火花
Spark 是一个通用的分布式数据处理引擎,适用于各种环境。在 Spark 核心数据处理引擎之上,有 SQL、机器学习、图形计算和流处理的库,它们可以在应用程序中一起使用。在本教程中,我们将使用 Docker 设置一个独立的 Spark 集群,并让它能够启动任意数量的工作线程。这背后的原因是我们想要模拟一个远程集群以及它所需的一些配置。
在生产环境中,Spark 通常被配置为使用 Yarn 和已经分配给 Hadoop 的资源。
首先,我们需要创建 Docker 文件。在本教程中,我们将使用 Spark 版本 2.4.4,但如果您想要最新版本,可以将其更改为 2.4.5,它还附带了 Hadoop 2.7 来管理节点之间的持久性和簿记。在生产设置中,spark 通常配置有 Yarn,以使用现有的 Hadoop 环境和资源,因为我们只有一个节点上的 Hadoop,我们将运行 Spark 独立集群。将 Spark 配置为与纱线一起运行只需要很少的改动,您可以在这里看到设置的不同。
设置独立集群
nano Dockerfile
# DockerfileFROM python:3.7-alpineARG SPARK_VERSION=2.4.4
ARG HADOOP_VERSION=2.7RUN wget -q https://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz \
&& tar xzf spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz -C / \
&& rm spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz \
&& ln -s /spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION} /sparkRUN apk add shell coreutils procps
RUN apk fetch openjdk8
RUN apk add openjdk8
RUN pip3 install ipythonENV PYSPARK_DRIVER_PYTHON ipython
现在,我们要建立一个星火大师和 N 个星火工作者。为此,我们将使用 docker-compose。
nano docker-compose.yml
version: "3.3"
networks:
spark-network:
services:
spark-master:
build: .
container_name: spark-master
hostname: spark-master
command: >
/bin/sh -c '
/spark/sbin/start-master.sh
&& tail -f /spark/logs/*'
ports:
- 8080:8080
- 7077:7077
networks:
- spark-network
spark-worker:
build: .
depends_on:
- spark-master
command: >
/bin/sh -c '
/spark/sbin/start-slave.sh $$SPARK_MASTER
&& tail -f /spark/logs/*'
env_file:
- spark-worker.env
environment:
- SPARK_MASTER=spark://spark-master:7077
- SPARK_WORKER_WEBUI_PORT=8080
ports:
- 8080
networks:
- spark-network
对于主容器,我们公开了端口 7077 供应用程序连接,以及端口 8080 供 Spark 作业 UI 连接。对于 worker,我们通过环境变量连接到我们的 Spark master。
对于配置 spark worker 的更多选项,我们将它们添加到spark-worker.env
文件中。
nano spark-worker.env
SPARK_WORKER_CORES=3
SPARK_WORKER_MEMORY=8G
在此配置中,每个工作人员将使用 3 个内核和 8GB 内存。由于我的机器有 6 个内核,我们将启动 2 个工人。更改这些值,使其相对于您的机器。例如,如果您的机器只有 16GB 的 RAM,那么一个合适的内存值可能是 2gb 或 4GB。关于环境变量的完整列表和关于独立模式的更多信息,你可以在这里阅读完整的文档。如果你想知道 executor 的内存,那是在提交或启动应用程序时设置的。
docker-compose build
docker-compose up -d --scale spark-worker=2
现在 spark 已经启动并运行,您可以在 localhost:8080 查看 web UI!
Spark Web 用户界面
本地安装 Spark
在您的本地机器上,或者任何将要创建或使用 Spark 的机器上,都需要安装 Spark。因为我们正在设置一个远程 Spark 集群,所以我们从源头上安装它。在本教程中,我们将使用 PySpark,因为我大部分时间在我的个人项目中使用 Python。
你可以从这里下载 Spark。
确保您下载的版本与您在主服务器上安装的版本相同。对于本教程,它的版本是 2.4.4
wget https://archive.apache.org/dist/spark/spark-2.4.4/spark-2.4.4-bin-hadoop2.7.tgz
tar -C /opt -xzvf spark-2.4.4-bin-hadoop2.7.tgz
设置 Spark 环境变量,nano ~/.bashrc
export SPARK_HOME=/opt/spark
export PATH=$SPARK_HOME/bin:$PATHexport PYSPARK_DRIVER_PYTHON="jupyter"
export PYSPARK_DRIVER_PYTHON_OPTS="notebook"
export PYSPARK_PYTHON=python3
如果您更喜欢 Jupyter Lab,请将 PYSPARK_DRIVER_PYTHON_OPTS 的“笔记本”更改为“实验室”。
配置文件
要配置 Spark 来使用我们的 Hadoop 和 Hive,我们需要在 Spark config 文件夹中有两者的配置文件。
cp $HADOOP_HOME/etc/hadoop/core-site.xml /opt/spark/conf/
cp $HADOOP_HOME/etc/hadoop/hdfs-site.xml /opt/spark/conf/
nano /opt/spark/conf/hive-site.xml
<configuration>
<property>
<name>hive.metastore.uris</name>
<value>thrift://YOUR_IP:9083</value>
</property>
<property>
<name>spark.sql.warehouse.dir</name>
<value>hdfs://YOUR_IP:9000/user/hive/warehouse</value>
</property>
</configuration>
hive.metastore.uris
:告诉 Spark 使用 Thrift API 与 Hive metastore 交互。spark.sql.warehouse.dir
:告诉 Spark 我们的蜂巢桌在 HDFS 的位置。
安装 PySpark
或者用安装在 spark master 上的任何版本替换 2.4.4。
要运行 PySpark 连接到我们的分布式集群运行:
pyspark --master spark://localhost:7077
,您也可以用您的 ip 或远程 ip 替换localhost
。
这将启动一个带有预定义的 Spark 上下文的 Jupyter 笔记本。因此,我们现在有一个单一的环境来分析有或没有 Spark 的数据。
默认情况下,executor 内存只有大约 1GB (1024mb),要增加内存,请使用以下命令启动 pyspark:
pyspark --master spark://localhost:7077 --executor-memory 7g
在 Spark 中,每个执行器有 10%的开销,所以我们最多可以分配 7200mb,但是为了安全起见,我们选择 7。
测试集成
默认情况下,会自动创建一个 SparkContext,变量是sc
。
从我们之前创建的配置单元表中读取。
from pyspark.sql import HiveContexthc = HiveContext(sc)hc.sql("show tables").show()hc.sql("select * from test_table").show()
要从 Hadoop 读取文件,命令应该是:
sparksession = SparkSession.builder.appName("example-pyspark-read-and-write").getOrCreate()
df = (sparksession
.read
.format("csv")
.option("header", "true")
.load("hdfs://YOUR_IP:9000/PATH_TO_FILE")
)
实际的 Hadoop 使用案例
除了存储数据,Hadoop 还被用作一个特性库。假设你是一个团队或组织的一部分,他们有多种模型。每个模型都有一个数据管道,用于接收原始数据、计算数据并将数据转换为特征。对于一个或两个模型来说,这完全没问题,但是如果有多个模型呢?如果跨这些模型重用特性(例如,记录标准化股票价格)会怎样?
我们可以创建一个只计算一次要素的数据管道,并将其存储在要素库中,而不是每个数据管道都重新计算相同的要素。该模型现在可以从要素库中提取要素,而无需任何冗余计算。这减少了数据管道中冗余计算和转换的数量!
功能存储还有助于解决以下问题:
- 不会重复使用特征。数据科学家面临的一个常见障碍是花费时间重新开发功能,而不是使用以前开发的功能或其他团队开发的功能。要素存储允许数据科学家避免重复工作。
- 功能定义各不相同。任何一家公司的不同团队可能会以不同的方式定义和命名特性。此外,访问某个特定特性的文档(如果存在的话)通常是一个挑战。特征存储通过保持特征及其定义的组织性和一致性来解决这个问题。功能库的文档有助于您围绕整个公司的所有功能创建一种标准化的语言。您确切地知道每个特征是如何计算的,以及它代表什么信息。
- 培训和生产功能不一致。生产和研究环境通常使用不同的技术和编程语言。流入生产系统的数据流需要实时处理为特征,并输入到机器学习模型中。
如果你想看一看一个功能商店并免费开始,我推荐 StreamSQL 。StreamSQL 允许您从各种来源传输数据,如 HDFS、本地文件系统、Kafka 等。并创建一个数据管道,可以养活你的模型!它能够保存在线或本地 HDFS 上的特征存储,供您训练模型。它还为您创建测试(保持)集。他们有一个很好的 API 文档,并且一直在改进它。
反馈
我鼓励所有关于这个帖子的反馈。如果你有任何问题或者需要任何帮助,你可以给我发邮件到 sidhuashton@gmail.com 或者在帖子上留言。
您也可以通过 Twitter 联系我并关注我,地址是 @ashtonasidhu 。
【教程】:在 Azure 上部署 ML 模型作为 REST API,用 SSL 保护
将机器学习模型投入生产的分步指南
你会学到什么:
TL;博士:
数据科学家经常忘记,如果他们的新模型不能在生产中使用,它们就没有多大用处。在本教程中,我将向您展示如何将 Tensorflow 模型部署到 Azure 容器实例,并使用 SSL 保护它。
将有 3 种部署类型:
- 部署#1:本地部署: 模型将被放在 docker 容器中,并作为 API 在 localhost 上公开。
部署#1:本地部署
- 部署#2:不加密的全局部署: 包含模型的 Docker 将上传到 Azure Container Registry。存储的容器将通过您可以调用的 API 公开。这样做的缺点是,如果每个人都有 IP 地址并且没有加密,他们就可以访问它。
部署#2:不加密的全局部署
- 部署#3:用 SSL 加密的全局部署: 包含模型的 Docker 将被上传到 Azure Container Registry。我们将为 API 的使用创建证书颁发机构(CA)和自签名证书。
部署#3:使用 SSL 加密的全局部署
先决条件:
- 带有 sudo 选项的 Linux 机器
- 安装在 Linux 上的 Docker
- 安装在 Linux 上的 Azure CLI
- 安装在 Linux 上的 openssl
- Azure 的有效订阅
注意:
如果你没有有效的订阅,你可以为 Azure 创建一个免费帐户,并获得 2 周的试用期。
第一步:我们需要一个模型
训练 Tensorflow 模型并用
tf.saved_model.save(model, "path / to / model")
保存
或者:
这里有一个演示模型,表示 f(x)= 2*x -1。它被保存到”。/demo_model/"
import os
import numpy as np
import tensorflow as tf xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float) model = tf.keras.Sequential([tf.keras.layers.Dense(units=1, input_shape=[1])])
model.compile(optimizer='sgd', loss='mean_squared_error')
history = model.fit(xs, ys, epochs=500, verbose=0)
print("Finished training the model") MODEL_DIR = "./demo_model" version = 1
export_path = os.path.join(MODEL_DIR, str(version)) model.save(export_path, save_format="tf")
print('\nexport_path = {}'.format(export_path))
Tensorflow 发球的细节我就不赘述了,因为有很棒的 Coursera 课程。上面使用的代码是本课程中使用的代码,但仅针对本演示进行了压缩。你可以从这里了解更多 TFServing。
步骤 2:将模型放入 TFServing 容器中
打开“终端”,导航到保存模型的目录。
$ MODEL_NAME=**demo_model** #**REPLACE with your model**
$ MODEL_PATH="./$MODEL_NAME"
$ CONTAINER_NAME="$MODEL_NAME"$ sudo docker run -d --name serving_base tensorflow/serving
$ sudo docker cp "$MODEL_PATH" serving_base:"/models/$MODEL_NAME"
$ sudo docker commit --change "ENV MODEL_NAME $MODEL_NAME" serving_base "$CONTAINER_NAME"$ sudo docker run -d -p 8501:8501 "$MODEL_NAME"
现在,您已经通过本地主机上的 API 公开了您的模型,您可以从终端调用它:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "[http://localhost:8501/v1/models/$MODEL_NAME:predict](http://localhost:8501/v1/models/$MODEL_NAME:predict)"
或者来自 python:
import requests
import json api_url = "[http://localhost:8501/v1/models/demo_model:predict](http://51.138.56.160/v1/models/use:predict)" data = json.dumps({"signature_name": "serving_default", "instances": [[10.0],[9.0]]}) headers = {"content-type": "application/json"} json_response = requests.post(api_url, data=data, headers=headers) print(json_response.text)
#STOP docker image
sudo docker container stop IMAGE_ID
#DELETE docker image
sudo docker container rm IMAGE_ID
部署#1:已完成!
您现在可以在 localhost 中使用您的模型了。
**额外提示:**另一个选择是端口转发。您使用 SSH 连接到机器。
ssh -N -f -L localhost:8501:localhost:8501 user@IP.ADDRESS
缺点:
- 你只能在本地使用它
- 您需要 VPN 访问才能使用端口转发
- 不可扩展
步骤 3:创建容器注册表
用于存储带有模型的容器。
转到 Azure portal 并找到容器注册表。点击添加新注册表按钮创建新注册表。
重要提示:
- 选择有效的订阅和资源组。
- 注册表名称必须是唯一的,我们以后还会用到它,所以请注意。(提示:仅使用小写字母)
- 位置是必须的,你应该选择离你最近的一个
- 大多数情况下,基本 SKU 应该足够了。
你现在可以点击查看和创建这个注册表按钮。
**重要提示:**导航到您的资源并启用管理员用户。记下用户名和密码。
步骤 4:将 Docker 容器推送到容器注册中心
$ CONTAINER_REGISTRY_NAME=**blogdemo** #**REPLACE (lower letters only)**$ sudo az login #login to Azure$ sudo az acr login --name "$CONTAINER_REGISTRY_NAME" $ sudo docker images
$ VERSION_NAME="$CONTAINER_REGISTRY_NAME.azurecr.io/version1"$ sudo docker tag **2b458f67dac3** "$VERSION_NAME" #**REPLACE**$ sudo docker push "$VERSION_NAME"
恭喜:
您已经成功地存储了带有模型的容器
步骤 5:用容器实例公开模型
在存储容器之后,暴露模型是相当容易的。
最简单的方法是登录 Azure portal,导航到容器实例并点击添加按钮。命名容器后,选择一个想要的区域,并选择您上传模型的注册表。
注意:
尺寸真的取决于你的模型尺寸。这个例子中使用的尺寸对于这个模型来说已经足够了。我已经部署了大约 150M 参数(600MB)的模型,它需要 2 个 vcpu 和 4 GB 内存。
现在点击下一步:联网按钮。
注意: DNS 名称是可选的,它需要是唯一的。
点击审查并创建,您的模型就可以使用了。(要删除实例,只需导航至资源并点击删除按钮)
用法:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "[http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict)"
#**REPLACE** [blogdemo.westeurope.azurecontainer.io](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH DNS**#OR:$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "[http://IP.ADDRESS:8501/v1/models/$MODEL_NAME:predict](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict)"
#**REPLACE** [IP.ADDRESS](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH IP**
使用模型与使用本地公开的模型是一样的,唯一的区别是 URL。您可以使用 DNS URL 或 IP URL。
部署#2:已完成!
您已经部署了一个可以在互联网上使用的模型。您可以监视模型的使用情况,并在需要时升级容器的大小,这使得它具有可伸缩性。
缺点:
- 每个知道 IP 地址的人都可以访问这个模型
- 未加密
步骤 6:创建证书颁发机构和用户
为了保护 API,我们需要加密。如果您使用商业证书颁发机构,可以跳过这一步。我们将创建自己的自签名证书。
$ mkdir certs
$ cd certs $ openssl ecparam -genkey -name secp256r1 | openssl ec -out ca.key $ openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
系统会提示您填写表格。请填写您的信息。
注: 为简单起见,这是椭圆加密的一个例子。我建议使用 RSA 密钥。这是一个很棒的要点,解释了如何去做。
$ CLIENT_ID=”client”
$ CLIENT_SERIAL=01
#when creating new user make sure that **serial is unique**$ openssl ecparam -genkey -name secp256r1 | openssl ec -out “${CLIENT_ID}.key”$ openssl req -new -key “${CLIENT_ID}.key” -out “${CLIENT_ID}.csr”
#password should be empty$ openssl x509 -req -days 3650 -in “${CLIENT_ID}.csr” -CA ca.pem -CAkey ca.key -set_serial “${CLIENT_SERIAL}” -out “${CLIENT_ID}.pem”$ cat “${CLIENT_ID}.key” “${CLIENT_ID}.pem” ca.pem > “${CLIENT_ID}.full.pem”#**OPTIONAL**:
$ openssl pkcs12 -export -out “${CLIENT_ID}.full.pfx” -inkey “${CLIENT_ID}.key” -in “${CLIENT_ID}.pem” -certfile ca.pem#remember passoword and you will pass it with pfx file
步骤 7:配置 NGINX
我们为什么需要 NGINX?
拒绝所有没有有效证书的请求。
创建 nginx.config 文件,如下所示:
$ cd ..$ cat > nginx.conf
复制所有内容并粘贴到一个文件中。粘贴后,使用 CTRL+D 关闭编辑器。
user nginx;worker_processes auto;events {
worker_connections 1024;
}pid /var/run/nginx.pid;http {server {
listen [::]:443 ssl;
listen 443 ssl;server_name localhost;ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
ssl_session_timeout 24h;keepalive_timeout 300; # up from 75 secs defaultadd_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';ssl_certificate /etc/nginx/server.pem;
ssl_certificate_key /etc/nginx/server.key;
ssl_client_certificate /etc/nginx/ca.pem;
ssl_verify_client on;
location / {
proxy_pass http://localhost:8501; # TODO: replace with correct port
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
}
NGINX 的细节我就不赘述了,下面是详细的链接。这个配置是这个博客和这个文章的混合体。简而言之,它只监听 https(端口 443)并验证它收到的证书。如果证书有效,它会将解密的请求重定向到具有模型的容器,并对发送回客户端的响应进行加密。
步骤 8:为 Azure 准备设置文件
为 Azure 创建 test-deploy.yaml 文件:
$ cat > test-deploy.yaml
复制所有内容并粘贴到一个文件中。粘贴后,使用 CTRL+D 关闭编辑器。
location: LOCATION
name: NAME
properties:
containers:
- name: model
properties:
image: IMAGE.azurecr.io/version1:latest
ports:
- port: 8501
protocol: TCP
resources:
requests:
cpu: 1.0
memoryInGB: 1.5
- name: nginx-with-ssl
properties:
image: nginx
ports:
- port: 443
protocol: TCP
resources:
requests:
cpu: 1.0
memoryInGB: 1.5
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
imageRegistryCredentials:
- server: SERVER.azurecr.io
username: USERNAME
password: PASSWORD
volumes:
- secret:
server.pem: PEM
server.key: KEY
ca.pem: CA
nginx.conf: CONF
name: nginx-config
ipAddress:
ports:
- port: 443
protocol: TCP
type: Public
dnsNameLabel: DNS
osType: Linux
tags: null
type: Microsoft.ContainerInstance/containerGroups
我们将使用 sed 命令替换该模板的部分内容。这些是无需编辑即可运行的命令。
$ sed -i -e "s/name: NAME/name: $MODEL_NAME/" azure.yaml$ sed -i -e "s/image: IMAGE/image: $CONTAINER_REGISTRY_NAME/" azure.yaml$ sed -i -e "s/server: SERVER/server: $CONTAINER_REGISTRY_NAME/" azure.yaml$ sed -i -e "s/username: USERNAME/username: $CONTAINER_REGISTRY_NAME/" azure.yaml$ sed -i -e "s/server.pem: PEM/server.pem: $(cat ./certs/ca.pem | base64 -w 0)/" azure.yaml$ sed -i -e "s/server.key: KEY/server.key: $(cat ./certs/ca.key| base64 -w 0)/" azure.yaml$ sed -i -e "s/ca.pem: CA/ca.pem: $(cat ./certs/ca.pem | base64 -w 0)/" azure.yaml$ sed -i -e "s/nginx.conf: CONF/nginx.conf: $(cat ./nginx.conf | base64 -w 0)/" azure.yaml
这些命令必须进行编辑。用注册表中的位置和密码替换粗体部分,并用您的 DNS 替换 DNS_NAME。
$ sed -i -e "s/location: LOCATION/location: **westeurope**/" azure.yaml$ sed -i -e "s/password: PASSWORD/password: **REGISTRY_PASS**/" azure.yaml
#TIP: If your generated password has some special characters
#like / you will have to manually put **\**/ infront$ sed -i -e "s/dnsNameLabel: DNS/dnsNameLabel: **DNS_NAME**/" azure.yaml
步骤 9:部署模型
用您的有效资源组替换 < AZURE 资源组> ,您就可以开始了。
$ az container create --resource-group **<AZURE RESOURCE GROUP>** --name "$MODEL_NAME" -f azure.yaml
第十步:用法
确保您拥有 client.key 和 client.pem 的正确路径,并且您可以继续调用 API,如下所示:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -v --key "./certs/client.key" --cert "./certs/client.full.pem" -X POST -k [https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict](https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict)#**REPLACE** [blogdemo.westeurope.azurecontainer.io](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH DNS**#OR:$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -v --key "./certs/client.key" --cert "./certs/client.full.pem" -X POST -k [https://IP.ADDRESS/v1/models/demo_model:predict](https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict)
#**REPLACE** [IP.ADDRESS](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH IP**
如果一切正常,您将会得到这样的响应:
**重要提示:**由于这是自签名证书,所以必须在 cURL 中添加 -k 标志。
Python 版本:
import requests
import jsonapi_url = "[https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict](https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict)"data = json.dumps({"signature_name": "serving_default", "instances": [[10.0],[9.0]]})headers = {"content-type": "application/json"}json_response = requests.post(api_url, data=data, headers=headers,verify=False,cert=("./certs/client.pem","./certs/client.key"),)print(json_response.text)
**重要提示:**由于这是一个自签名证书,您必须设置 verify=False
无有效证书调用时:
部署#3:已完成!
第十一步:监控
您可以在容器实例中监控模型的使用,并且可以在 nginx 日志中看到请求。
常见问题:
问:我可以部署一个定制模型(没有 TF 服务)吗?是的。你必须对接型号,如果你使用正确的端口,其他一切都应该按预期工作。
问:我能把它部署到 AWS 吗?
答:是的,通过使用 nginx.config 和这篇博客里看到的 CA 配置。
问:我可以在 Docker Hub 上存储容器吗?答:是的。只需用 Docker Hub 中的用户名/repo:version 替换图像,并从 YAML 文件中删除 imageRegistryCredentials。
资源:
[## TensorFlow | Coursera 的高级部署场景
将机器学习模型带入现实世界涉及的不仅仅是建模。这种专业化将…
www.coursera.org](https://www.coursera.org/learn/advanced-deployment-scenarios-tensorflow/) [## 用 TensorFlow 服务训练和服务 TensorFlow 模型| TFX
警告:这款笔记本被设计为只能在 Google Colab * *上运行。它在系统上安装软件包,并需要…
www.tensorflow.org](https://www.tensorflow.org/tfx/tutorials/serving/rest_simple) [## 向 Azure 容器实例添加 SSL/TLS
如今,SSL/TLS 是任何在互联网上运行的网站或应用程序的基本要求。不幸的是…
medium.com](https://medium.com/@samkreter/adding-ssl-tls-to-azure-container-instances-1e608a8f321c) [## 在 Ubuntu 上通过 NGINX 使用基于客户端证书的认证
经过身份验证的 SSL/TLS 反向代理是保护您的应用程序免受攻击的强大方法。用户和坏…
www.ssltrust.com.au](https://www.ssltrust.com.au/help/setup-guides/client-certificate-authentication) [## 使用 nginx 进行客户端证书认证
应用程序中的身份验证非常困难。如果你决定推出自己的,安全问题几乎是肯定的。大多数…
fardog.io](https://fardog.io/blog/2017/12/30/client-side-certificate-authentication-with-nginx/) [## 使用 Docker 实时生成机器学习预测-生产中的 ML
如果我们想要实时生成预测,我们需要公开我们训练过的模型。展示我们模型的一种方式是…
mlinproduction.com](https://mlinproduction.com/docker-for-ml-part-4/)
https://miro . medium . com/max/800/1 * dikrcflfp 3 _-yqcysditw . png
https://miro.medium.com/max/600/0*7eCQeU5D86SQeFHa.png
https://cdn 0 . icon finder . com/data/icons/home-security-2/45/security-06-512 . png