用 T5(文本到文本转换转换器)解释任何问题—提供预训练模型和训练脚本
来自 Pixabay 的背景图像
投入
我们节目的输入将是任何你能想到的一般性问题
**Which course should I take to get started in data Science?**
输出
输出将是转述同一个问题的版本。转述一个问题意味着,你创造一个新的问题,用一个不同的词语选择来表达相同的意思。
**Paraphrased Questions generated from our T5 Model** ::
**0: What should I learn to become a data scientist?
1: How do I get started with data science?
2: How would you start a data science career?
3: How can I start learning data science?
4: How do you get started in data science?
5: What's the best course for data science?
6: Which course should I start with for data science?
7: What courses should I follow to get started in data science?
8: What degree should be taken by a data scientist?
9: Which course should I follow to become a Data Scientist?**
正如您所看到的,我们生成了大约 10 个问题,这些问题是对原问题的复述——“我应该学习哪门课程来开始学习数据科学?”
今天我们将看看如何从 Huggingface 的变形金刚库中训练一个 T5 模型来生成这些转述的问题。我们还将了解如何使用提供的预训练模型来生成这些转述问题。
实际使用案例
来自平面图标的图标
想象一个中学老师正在为班级准备一个测验。不是给每个学生一个固定的问题,而是他/她可以生成一个给定问题的多个变体,并分发给学生。学校也可以用这种技术用一个给定问题的几个变体来扩充他们的题库。
让我们开始吧—
资料组
来自平面图标的图标
我使用了Quora 问题对 数据集来收集所有标记为重复的问题,并准备了训练集和验证集。重复的问题有助于我们获得释义对。
我们会详细讨论你如何-
- 使用我的预先训练的模型为任何给定的问题生成转述问题。
- 使用我的训练代码和数据集在你自己的 GPU 机器上复制结果。
训练算法— T5
用扁平图标生成的图标
T5 是 Google 的一个新的 transformer 模型,它以端到端的方式进行训练,将文本作为输入,将修改后的文本作为输出。你可以在这里了解更多关于的信息。
它使用在大型文本语料库上训练的文本到文本转换器,在多个自然语言处理任务上实现了最先进的结果,如摘要、问题回答、机器翻译等。
我用原句作为输入和转述(来自 Quora 问题对的重复句)句子作为输出来训练 T5。
密码
使用**“使用预训练模型”**部分中的 Google Colab 链接对此进行测试。使用给定数据训练模型的所有代码可从以下网址获得-
[## ramsrigouthamg/Paraphrase-any-question with-T5-Text-To-Text-Transfer-Transformer-
用 T5(文本到文本转换转换器)解释任何问题-提供预先训练的模型和训练脚本…
github.com](https://github.com/ramsrigouthamg/Paraphrase-any-question-with-T5-Text-To-Text-Transfer-Transformer-)
使用预先训练的模型
Google Colab notebookt5-pre trained-question-paraphraser包含以下代码。
首先,安装必要的库-
!pip install transformers==2.8.0
将任何问题作为输入进行推理,并查看解释的结果。
上述代码的输出是-
device cpu
**Original Question ::**
Which course should I take to get started in data science?
**Paraphrased Questions ::**
0: What should I learn to become a data scientist?
1: How do I get started with data science?
2: How would you start a data science career?
3: How can I start learning data science?
4: How do you get started in data science?
5: What's the best course for data science?
6: Which course should I start with for data science?
7: What courses should I follow to get started in data science?
8: What degree should be taken by a data scientist?
9: Which course should I follow to become a Data Scientist?
训练你自己的模型
同样,所有用于训练的训练代码和数据集都可以在前面提到的 Github repo 中获得。我们将经历我用来训练模型的步骤。
1.数据准备
首先我下载了这个链接中提到的 Quora 问题对 tsv 文件(quora _ duplicate _ questions . tsv)。
仅提取具有 is_duplicate =1 的行,因为它们是复述的疑问句。然后,我将数据分成训练集和验证集,并将它们存储在单独的 CSV 文件中。
最后,每个 CSV 文件都有两列“问题 1 ”和“问题 2 ”。“问题 2”是“问题 1”的意译版本。由于 T5 期望一个文本作为输入,我给了 “question1” 作为输入源,并要求它生成 “question2” 作为目标输出。
用于生成训练和验证 CSV 文件的代码如下所示。CSV 文件位于 Github repo 中的 paraphrase_data 文件夹下。
filename = "**quora_duplicate_questions.tsv**"
import pandas as pd
question_pairs = pd.read_csv(filename, sep='\t')
question_pairs.drop(['qid1', 'qid2'], axis = 1,inplace = True)question_pairs_correct_paraphrased = question_pairs[**question_pairs['is_duplicate']==1**]
question_pairs_correct_paraphrased.drop(['id', 'is_duplicate'], axis = 1,inplace = True)from sklearn.model_selection import train_test_split
train, test = train_test_split(question_pairs_correct_paraphrased, test_size=0.1)train.to_csv('**Quora_Paraphrasing_train.csv**', index = False)
test.to_csv('**Quora_Paraphrasing_val.csv**', index = False)
2.培养
感谢 Suraj Patil 给了我们一个神奇的 Colab 笔记本来训练 T5 完成任何文本到文本的任务。我从 Colab 笔记本上借用了大部分训练代码,只更改了数据集类和训练参数。我根据 Quora 问题对数据集修改了数据集类。
Github Repo 中的培训代码为 train.py 。
你需要做的就是在任一台 GPU 机器上克隆repo,安装 requirements.txt ,运行 train.py 来训练 T5 模型。
在 p2.xlarge (AWS ec2)上训练该模型 2 个纪元(默认)花费了大约 20 个小时。
数据集类如下所示—
关键是我们如何向 T5 模型培训师提供我们的输入和输出。对于数据集中任何给定的问题对,我给 T5 模型输入(源)和输出(目标),如下所示
输入格式到 T5 进行训练
**paraphrase: What are the ingredients required to make a perfect cake? </s>**
输出格式到 T5 进行训练
**How do you bake a delicious cake? </s>**
就是这样!你手里有一个最先进的问题解释器。
也许这是第一个从任何给定的问题中产生转述问题的作品。
编码快乐!
使用自然语言处理的问题生成——教程
我推出了一个非常有趣的 Udemy 课程,名为“使用 NLP 生成问题”,扩展了这篇博文中讨论的一些技术。如果你想看一看,这里是链接。
祝 NLP 探索愉快,如果你喜欢它的内容,请随时在 Twitter 上找到我。
艾的幻觉
人工智能的失败之美
上传了一杯咖啡的照片和 style gan 2(art breader)对它的各种解读。截图:Merzmensch
作为预训练数据集的生存本能。
我是来保护我们的。让我们变得谨慎。幻想症。这是一个众所周知的现象,当我们看到不存在的面孔时。我们在火星上看到的面孔,我们在烤面包中看到耶稣,我们在任何地方都能看到它。嗯,它会影响我们的大脑,我们的大脑受过生物识别训练:眼睛、鼻子、嘴巴=当一切都在正确的位置时,那就是一张脸。
它起源于我们的过去——当我们漫步在树林里猎杀长毛象的时候。再一次混淆丛林和老虎,总比忽视树林间真正的捕食者要好。生存本能。
神经网络也有类似的行为。他们认识到他们所接受的训练。最好的例子是 StyleGAN2 投影,其中潜像与上传的图像并置,并检查其来源(稍后将详细介绍)。
这是一个人为和人为的幻觉的例子:
西多尼亚地区的一小部分,由 维京 1 轨道飞行器拍摄,由美国宇航局 / JPL 于 1976 年 7 月 25 日发布(来源,公共领域)//达芬奇的《蒙娜丽莎》,用谷歌深度梦境修改(来源:尼克斯敦)。
虽然我们的人眼在一些随机的岩层中看到一张脸,但早期版本的谷歌深度梦的卷积神经网络可以识别狗。到处都是。对“为什么是狗”这个问题的解释显而易见:
在 Deep Dream 的案例中,数据集来自 ImageNet,这是一个由斯坦福大学和普林斯顿大学的研究人员创建的数据库,他们建立了一个包含 1400 万张人类标记图像的数据库。但是谷歌没有使用整个数据库。相反,他们使用了 2012 年发布的 ImageNet 数据库的一个较小的子集,用于一场比赛…这个子集包含“120 个狗子类的精细分类”
(来源: FastCompany )
这是有偏神经网络的第一次可视化,在有限的数据集上进行训练。人工智能无法识别未经训练的物体,这不是它的错。这取决于我们。
失败的美妙之处
但是如果用于实验目的,这个缺陷可以成为一个优势。
您已经看到了 StyleGAN2 投影功能。
该函数的主要任务是将给定的图像与潜在空间(StyleGAN 的隐藏层,所有唯一的图像种子在被修改之前位于该层)中的种子进行比较。其中,匹配的图像可以指出深层的伪造(用于犯罪学家的图像取证)。
在我实验的某个时候,我尝试了除了外貌图片之外的其他图像。
它带来了一些令人惊讶的结果:
或者甚至是令人不安的(说到恐怖谷):
我在这个实验中使用了基于 StyleGAN2 的神经网络,在来自 FlickrDataset 的人脸上进行训练,在 1024×1024 的 FFHQ 数据集上使用 StyleGAN2。
相同的数据集在 web 应用程序art breader中实现。使用上传功能,您可以用新图像来增加潜在空间。但是它们与 StyleGAN2 训练的网络对齐,并使用投影特征以兼容的方式修改。
有时,将特定的非潜像添加到潜像空间会产生有趣的效果。在一个案例中,StyleGAN2“纠正”了一位艺术家的照片,他开玩笑地用一张纸碟遮住了自己的眼睛:
尝试有限的识别技能(这是由有限的训练造成的)可以带来迷人的结果。
它开始于我试图上传一个物体的照片。没有任何面部表情或生物特征。我的动机是检验 StyleGAN2 能够以何种方式识别和分类随机主题(而不是照片肖像)。
所以我上传了我一杯咖啡的照片。虽然是随机的。
左图:原始图像(Merzmensch 拍摄)//右图:由 ArtBreeder 上传并投射到潜在空间的这张图像
使用各种神经层(特定面部特征)的进一步修改,生成了新的肖像。如你所见,艾既不能认出这幅画,也不能模仿它的外貌风格和特征。但是结果却是独一无二的。
正如你所看到的,在第一张图像中,黑点被 AI 识别为一只眼睛——面部的其他部分也跟着识别。
只有四张脸由 StyleGAN2 驱动网络//由 Merzmensch 生成
在运动中,你可以更好地看到面部神经层之间的转换。但是你永远找不到一杯咖啡的任何痕迹。
我的系列“偶像崇拜”中的一集
实验的结果
这个实验以非常直观的方式证明了:
ML 识别模型的质量高度依赖于它被训练的数据集。这些数据集的质量取决于人工标记和准备。
在我们信任人工智能之前,我们应该让这种信任发生在我们自己身上。检查数据集及其出处,跳出框框思考(“如果”会发生什么),并意识到到 AGI 还有很长的路要走。
pareidolia——向人工智能教授艺术
Pareidolia 是我们在外星智能保护伞下的第一个人工智能艺术项目。
在 外星智能 ,我们探索自己教 AI 艺术的能力;让它产生一些理解的证据,然后分析和解释它的反应。我们从一个简单的“教训”开始,并计划在一个迭代的过程中逐渐发展它的内容和复杂性。交互的质量和各自的结果将取决于人工智能的技术能力,以及我们人类在与它交流时的独创性/局限性。
幻觉——看到人类是人之常情
幻视症是对观察者已知的刺激作为物体、模式或意义的不正确感知的倾向,例如在云中看到形状,在无生命物体或抽象模式中看到面孔,或在音乐中听到隐藏的信息。 维基百科
幻觉——你看到了什么?(谷歌图片搜索)
在这个项目中,我们的目标是向人工智能传达某些艺术作品是对现实世界中的物体和想法的感知或解释。作为第一类这样的物体,我们选择了最人性化的物体——人脸。
更具体地说,我们首先从展示照片中捕捉到的人工智能“真实”人脸开始。接下来,我们向它展示肖像绘画中对人脸的艺术描绘——从写实,一直到抽象表现。
然后,我们探索人工智能的“理解”:我们向它展示它以前从未见过的新肖像绘画,并要求它生成一张捕捉艺术作品中面部本质的逼真照片(是的,相反的方向)。我们很想知道它会产生什么。我们从现实主义绘画开始,但我们的意图是进一步扩展到抽象,立体,超现实主义,以及 3D 作品。最后,在真正的“Pareidolia”时尚中,我们将给予它不是人脸的物体的照片,并探索它如何将它们投影为人脸的真实照片。
我们的目标是探索一个结合点,它不仅扩展了人工智能理解和表达艺术的能力,还扩展了我们人类在与人工智能交流我们的目标并与之合作方面的局限性。
所有的魔法都是有代价的
在这种入门水平上,让人工智能艺术教学变得神奇的是,不需要向它提供什么是脸、什么是肖像画以及它们如何相互关联的精确定义和复杂解释。相反,我们只是给它(许多)两者的例子,它就不知何故地学会了。听起来很刺激?好吧,小心!所有的魔法都有代价。
在我们的例子中,这个价格源于 AI 缺乏任何关于面部、肖像或艺术的先验知识。事实上,它几乎缺乏任何关于我们、我们的历史和我们的堕胎的先验知识。也不是我们生活的世界。它所拥有的唯一信息,就是它所显示的图像中存储的内容。尤其是,它无法接触到我们在看肖像和照片时认为理所当然的许多概念和事实。
例如,脸是人体的一部分,有某些普遍的共性,如头的一般形状,眼睛,耳朵,鼻子和嘴的存在和位置。事实上,人类来自不同的性别、种族和年龄,以及一系列的基因变异——所有这些都是面部的可见属性。还有更微妙的事实,如头发和面部毛发的可变性,以及面部表情。此外,什么是人脸的“自然”方向,以及从上面看或从侧面看如何的知识。正是这种先验知识让我们人类能够毫不费力地识别和分析人脸,并区分真实的人脸和看起来像人脸的东西。
区分真脸和看起来像真脸的东西的能力
同样,也有与肖像相关的概念和事实。例如,细致入微的理解是肖像试图捕捉一张脸,但不一定像镜子那样以直接和准确的方式捕捉。相反,有内在的限制和有意的改编——使用的技术,艺术陈述和议程,执行的时间和地点,以及艺术作品的组成和设置。
这些都是我们认为理所当然的知识,对于学习照片和肖像之间的关系至关重要。人工智能无法接触到的知识。
诚然,人们可以想出复杂的方法来将这些信息传达给人工智能。例如,为图像和肖像提供标签——明确详细说明性别、种族、年龄、表情和其他面部特征(秃顶、有胡子、小胡子、金发、长鼻子、浓眉等)。然而,我们做了一个有意识的决定,不使用这些,看看我们仅仅用这些未标记的照片和肖像能走多远。我们想让我们与人工智能的对话保持简单。
来自火星的肖像和来自金星的照片
到目前为止,我们用作训练样本的图像的重要性肯定是显而易见的,因为它们封装了 AI 可以访问的所有信息。让我们仔细看看。
在理想的世界中,我们会给 AI 提供成对的图像:一张人脸的照片,以及同一个人的匹配肖像。不幸的是,这样的数据集并不存在。我们拥有的大部分肖像是在照相机发明之前的,我们拥有的大部分照片是那些觉得没有必要画肖像的人。
此外,公开的人脸照片数据集通常基于网络上的名人照片。这些照片主要偏向于年轻、白皙、好看、时尚、微笑的面孔,这些照片是从最佳的正面位置拍摄的。这与我们在肖像中发现的年龄、表情、位置和纹理的分布形成了巨大的对比(除了它们大多是白色物体)。这一点可以通过例子得到最好的说明:
画像
照片
这种看似简单的差异给我们的人工智能带来了巨大的挑战。因为这两个数据集(照片和肖像)实际上代表了人类的两种非常不同的观点。这确实对人工智能的学习、理解和输出有非常明显的(和预期的)影响。
同样,有很多方法可以尝试解决这个问题。从使用更具代表性的照片数据集(说起来容易做起来难,通常是以牺牲质量为代价),一直到创造“合成肖像”。也就是说,通过算法从照片中制造出“艺术”肖像,并将其配对使用。
然而,像以前一样,我们决定在这个阶段坚持简单,不使用合成肖像。
合成生成的肖像(【https://deepart.io/latest/】T4)
现在,我们终于完成了这个冗长的介绍,让我们看看我们的项目,以及我们实际上产生了什么。
Pareidolia 项目
我们认为这将是深刻的和有趣的艺术过程,并有我们的输出是基于肖像合成的“现实”照片,而不是相反。此外,我们不希望人工智能“简单地”在原始肖像上应用风格过滤器,并使它看起来更像照片。相反,我们希望捕捉肖像的语义,并将其重新创建为看起来像现实照片的“艺术投影”。
为了实现这一点,我们开始训练人工智能,使其能够将照片和肖像的语义与它们的风格分开,并将这些语义映射到一个共享的“面部”空间。
然后,我们探索了人工智能在这项任务中的成功程度,要求它根据从未见过的肖像生成一张新的合成照片。
我们认为这将是对“理解”的深刻展示——既包括肖像,也包括人脸。
轻度技术插曲(阅读风险自担)
尽管如上所述,我们希望输入尽可能简单和真实,但我们仍然必须应用一些简单的修改。具体来说,脸裁剪。作为人类,我们自然会被肖像中的脸所吸引。然而,从下面可以看出,在现实中——人脸只占据了肖像的一小部分。其余的是身体,mise-en-scène,主要是背景。虽然我们可能不会被它困扰,但它为缺乏任何背景和先验知识的人工智能提供了大量分散注意力的信息。所以,为了把重点放在重要的事情上,我们把两个系列的脸都剪了。
肖像——脸部只占整幅画的一小部分
肖像—裁剪过的脸
照片—裁剪的面孔
既然我们已经对输入示例进行了排序,我们必须选择与我们的目标相匹配的 AI 方法。
由于我们不想应用“简单”的过滤器,我们决定不采用风格转换的方法。我们也不想使用机器学习模型来查看源人像和目标合成照片之间的像素级相似性。因此,我们寻找对图像语义进行操作的 GAN 模型(GAN 代表生成性对抗网络——一种基于博弈论思想的机器学习技术,可以生成符合预期标准的输出)。由于我们有两个独立分布的额外约束(这意味着,我们没有成对的照片肖像来训练,而是两个独立的集合),我们用更广泛的 Cycle-GAN 家族的不同成员进行了实验。我们尝试了不同的选项和修改,最终找到了稍微修改过的版本 MUNIT (10 个纪元* 100k 次迭代)。
击鼓:结果和结束语
下面是结果!这是 3 个调色板,每个包含 24 对(每行 4 个 x 6 行)。每一对都是由左边的原始肖像和右边的合成照片组成的。
有些照片出奇的好,有些差的要命。有一点很清楚:人工智能是我们通过“名人”照片训练集强加给它的偏见的受害者。在照片的世界里,人们都是 20-30 岁,直视镜头,面带微笑,拥有完美的皮肤和直发。太老或太年轻,面部毛发,卷发,或稍微意想不到的角度——都不太好。然而,考虑到这是在没有背景或解释的情况下完成的,这是发人深省的。
每个调色板包含 24 对原始肖像和生成的“照片”
人工智能懂艺术吗?我们的绝对没有。绝对不是以人类的方式。然而,它所产生的肯定是有趣的和令人鼓舞的。一些结果似乎指出了理解上的根本差距,然而另一些结果却出人意料地好,令人兴奋。此外,当我们对这个项目进行实验时,我们提出了许多如何使它变得更好的想法。然而关键是这是否是一次值得的旅行。我们能通过这种对话——通过尝试向人工智能教授艺术——来学习关于我们世界的新见解,以及关于我们对世界的感知吗?我们计划继续探讨这个问题。
享受?看看我们的下一个项目——玫瑰色人工智能
帕累托分布和蒙特卡罗模拟
用帕累托分布模拟网页浏览
帕累托分布无处不在。它也被称为 80/20 法则。举几个例子:
- 20%的网站获得了 80%的流量。
- 全球收入最高的 20%的人赚了 80%的收入。
- 你 80%的时间穿 20%的衣服。
传统上,我们认为统计范围的假设分布是一个正态分布,即均值=中位数=众数的分布。
资料来源:RStudio
然而,我们在周围观察到的许多现象往往更接近帕累托分布。
来源:Jupyter 笔记本输出
在这个特定的例子中,我们可以看到一个严重右尾的分布,即大多数具有较低值的观察值(如 x 轴所定义的)倾向于图表的左侧,而少数具有较高值的观察值倾向于图表的右侧。
用蒙特卡罗模拟法模拟网页浏览
让我们以一段时间内的网页浏览量为例。这是一个曲线图,显示了 2016 年 1 月至 2020 年 8 月“地震”一词随时间的波动,来自维基媒体 Toolforge:
来源:维基媒体工具锻造
我们可以看到,在某些时段,页面浏览量会出现“峰值”——可能是在世界上某个地方正在发生地震的时候。
这正是我们所期待的——这是一个搜索词的例子,它在某些时候有更高的页面浏览量。事实上,许多网页遵循这种模式,流量或多或少遵循一种稳定的模式——伴随着突然的“峰值”。
让我们画出这个数据的直方图。
来源:Jupyter 笔记本输出
在上面的例子中,我们看到某一天的大部分页面浏览量都低于 10,000 ,但也有极少数情况超过了这个数字。
在选定的时间段内,某一天的最大页面浏览量为 31,520 。这很接近帕累托分布。
试图用传统的时间序列工具如 ARIMA 来预测页面浏览量是徒劳的。这是因为不可能提前知道特定的流量高峰将在何时出现,因为它严重依赖于外部环境,而与过去的数据无关。
一个更有意义的练习是运行模拟来预测假设帕累托分布的情况下人们可能期望看到的流量范围。
帕累托分布在 Python 中的调用如下:
numpy.random.pareto(*a*, *size=None*)
a 表示分布的形状,size 设置为10000,即从分布中产生 10000 个随机数用于蒙特卡洛模拟。
计算原始时间序列的平均值和标准偏差。
mu=np.mean(value)
sigma=np.std(value)
时间序列的均值为 5224 ,标准差为 2057 。
使用这些值,可以使用这些参数以及来自假设的帕累托分布的随机采样来生成蒙特卡罗模拟。
t = np.random.pareto(a, 10000) * (mu+sigma)
t
如上所述, a 的值取决于分布的形状。让我们首先将它设置为 3 。
以下是以百分位数表示的分布记录值:
来源:Jupyter 笔记本输出
我们可以看到 a = 3 时记录的最大值超过 35 万,远远高于时间序列记录的最大值。
如果我们设置 a = 4 会发生什么?
来源:Jupyter 笔记本
我们现在看到记录的最大值现在超过了 60,000 ,这仍然比时间序列记录的最大值高很多。
我们试试 a = 5 。
来源:Jupyter 笔记本输出
解释
最大页面浏览量刚刚超过 35,000,这更符合我们在原始时间序列中看到的情况。
然而,在这种情况下,我们只查看 2016 年以后的时间序列数据。许多最严重的地震实际上发生在 2016 年之前。
例如,让我们假设像 2004 年印度洋地震和海啸那样严重的地震将在今天发生——我们有理由预计对术语**“地震”**的页面浏览兴趣将比我们自 2016 年以来观察到的大得多。
如果我们假设帕累托分布有 a = 3 ,那么模型表明这个词的页面浏览量可能会超过 350,000 。
在这方面,蒙特卡洛模拟允许我们检查超出已记录的时间序列数据界限的情况。
地震(不幸的是)比互联网存在的时间要长得多——因此我们没有办法测量在记录最强烈地震的时候这个搜索词的页面访问量会是什么样的。
也就是说,结合对最接近的理论分布的建模进行蒙特卡罗模拟,可以允许对特定情况下时间序列的界限进行强场景分析。
结论
在本文中,您已经看到:
- 什么是帕累托分布
- 如何在 Python 中生成这样的分布
- 如何将帕累托分布与蒙特卡洛模拟结合起来
非常感谢你的时间。一如既往,非常感谢任何反馈、想法或问题。请随意在评论区留下它们。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。
参考
- 机器学习掌握:概率的蒙特卡罗抽样
- Numpy v1.14 手册:numpy.random.pareto
- 堆栈溢出:Matplotlib —从 x 轴到点画线
- 走向数据科学 Python 中的蒙特卡罗模拟:分析网页浏览量
- Wikimedia Toolforge:浏览量分析
基于 Mask-RCNN 的停车位检测
如何使用 Mask-RCNN 检测停车位的可用性?
空的/被占用的停车位
我最近在做一个项目,根据安全视图摄像头的照片来检测停车位是否可用或被占用。我的工作有局限性,我将进一步深入探讨,但一旦这些问题得到解决,这个项目可能是优化停车可用性的低成本解决方案。这个项目当然有可能简化在给定区域寻找停车位的过程,因为大多数停车场都安装了安全摄像头,这样停车场就不需要安装任何额外的设备。
让我详细说明一下我在这个项目中使用的资源。Kaggle 上有一个停车场数据集,它有足够的数据点来训练深度学习模型和 xml 文件,这些文件带有关于停车位是否被占用的注释。你可以在这里访问数据集停车场数据集。对于这个模型,我使用了最先进的对象检测和分割掩模-RCNN 模型,它表现令人惊讶,可以通过这个链接访问。
正如你所看到的,Mask-RCNN 在 COCOdataset 模型上进行预训练,在对象检测和分割方面表现出色。尽管在某些情况下,它会将汽车误分为火车和卡车。
掩模-RCNN 目标检测和分割
顺便说一下,我还尝试了 YOLO-v3,其性能相同,所以我没有进一步使用 YOLO 模型,但如果你正在寻找替代 YOLO 是一个用于对象检测的可怕模型,这里有链接。
YOLO 模型检测停车场上的汽车
首先,我使用 Mask-RCNN 模型来检测停车位上的车辆,并根据给定的停车位数量来计算空位。我们的模型不需要所有的 COCO 类,所以我将这些类限制在汽车、卡车和摩托车上。但是,在 COCOdataset 模型上预先训练的模型在检测小对象方面做得并不出色,即使我尝试调整阈值和边界框,那些被错误分类为火车的汽车也没有被检测到。这里绘制的是边界框,而不是模型的 visualize 方法提供的遮罩。
使用绘制的边界框而不是遮罩的遮罩-RCNN 模型预测
鉴于上述性能,我决定训练 COCO 模型的顶层,不仅从预训练的模型中转移知识,而且还根据我们的数据集改进预测,这样我们就有两个类来预测该位置是被占用还是空着。
该停车场数据集的约束条件是,照片仅从两个视角拍摄,这导致了训练模型的过度拟合问题,并且不允许更好地概括。第二,每个停车点的注释不完整,因此在训练期间产生异常,此外,不是照片上所有可用的停车点都被注释,这也导致模型的较差性能。我会告诉你我的意思,下面的照片只是显示了有多少停车位被标注:
并非所有的停车位都有标注
许多 xml 文件没有填充占用类:
注释文件中缺少信息
信息缺失的停车位数量可能上升至 10-15 个左右。我以为这可以手动修复,但是在运行了一些代码之后:
要修复的文件太多了,为了只训练模型的顶层,避免这些文件更容易。为了解析 xml 文件,我使用了 XML.etree.ElementTree 内置 python 包。同样值得注意的是,边界框的坐标是以一个中心点的角度给出的,所以如果我们想正确地解析和创建边界框,一些调整是必要的。
函数解析 xml 文件并提取停车场的轮廓
由于 Mask-RCNN 使用掩码来训练类,以类似于袋鼠检测文章的方式,可以在这里访问,我使用边界框来创建掩码。这篇文章实际上在理解如何使用 Mask-RCNN 模型和机器学习掌握方面帮助了我很多,一般来说,对于许多机器学习应用程序来说,这是一个很好的资源。所以,如果你还没看过的话,就去看看吧。主要的区别是,我们将检测 2 个类别,而不是一个,从而根据已占用的类别以及边界框的计算方式来创建遮罩。
函数从边界框中创建遮罩,并创建两个类来检测
至于数据集的组织,我们必须为训练和测试数据创建两个目录,每个目录包含 image 文件夹和 matching label 文件夹,其中每个 xml 文件都有相同的文件名,只是扩展名不同。而其余部分实际上遵循一般实践来训练 Mask-RCNN 的顶层。你可以在 Matterport GitHub 上查看不同的样本代码。
我们必须创建一个类 ParkingLot,它将加载数据集,通过解析 xml 注释文件提取边界框的轮廓,根据提取的轮廓创建遮罩,我们需要一个图像引用函数,它将返回注释 xml 文件的路径。
创建 ParkingLot 类
然后,我们需要根据我们希望模型接受训练的类来指定配置类,加载训练和测试数据集,使用“训练”模式加载模型并开始训练。
ParkingConfig 类
该模型将在每个时期后保存在 logs 文件夹中,这样一旦训练完成,您就可以继续加载该模型以评估其性能。使用 Mask-RCNN 的方法是创建新的配置类来限制我们预测的范围
预测配置类
使用“推理”模式加载模型,从日志文件夹加载模型:
由于 Mask-RCNN 可视化了检测到的对象及其类的遮罩,我们将使用此函数来绘制边界框:
函数绘制检测到的对象的边界框
因此,如果我们加载任何随机图像,检测可用和已占用的停车位,使用我们的函数来绘制它们,结果将是这样:
检测到可用停车位
绿色方框是可用的停车位,蓝色边框是已占用的停车位。看起来模型性能还不错,虽然还是检测不到小型车/停车点。数据集中有意忽略了停在人行道旁的汽车,以便模型在训练时不会将这些汽车考虑在内。这是使用 Mask-RCNN 的 visualize 方法得到的结果:
Mask-RCNN 检测可用停车位
鉴于这一结果,似乎该模型的性能真的很好。但是在我们的数据集中,我们只有停车场的两个角度,因为我只训练顶级模特,所以我只使用了数据集的一部分。那么,为什么不看看停车场的另一张照片,上面有一个类似的摄像头,但不是这个数据集的一部分。我们的模型会有多好?
哇!这太糟糕了。很明显,该模型过度适合我们的停车场数据集,这意味着它在相同的数据集上表现良好,我没有说训练,因为训练和测试照片实际上是停车场中不同汽车的相同照片,虽然技术上没有泄漏测试数据,但在某种意义上它们是相同的。开箱即用的预训练 Mask-RCNN 将在检测车辆方面表现得更好。
考虑到这些结论和我前面提到的局限性,下一步可以采取什么措施来改进模型或向前发展呢?嗯,我们可以使用整套数据从头开始训练模型,看看它的表现如何,尝试调整检测阈值。我不妨试着这样做,也许这可以成为这篇文章的第二部分?但理想情况下,更多变化的数据集是必不可少的,完整的注释是创建准确的停车点检测模型的基础。此外,为了建立不仅准确而且鲁棒的检测模型,我认为考虑正在移动但尚未停放的车辆,即未经授权停放的车辆非常重要。这些可能是未来需要进一步研究的问题。
感谢您花时间阅读我的帖子!
使用 Python 在几分钟内解析数千份股票推荐!
了解如何在不到 3 分钟的时间内解析顶级分析师的数千条建议!
如果你和我一样,你可能已经想了很多次什么股票是最热门的,分析师如何轻松地发现这些股票。我会经常在雅虎财经页面上随意查找一些股票,看看它们的评级是否很高。我也知道必须有一个更好的方法来找到这些随机股票,所以我用不到 40 行代码创建了这个简单的解析器。
在我进入编码方面之前,我想快速了解一下雅虎财经页面上的这些推荐内容和位置。如果你转到页面并输入一只股票,你会看到类似我下面的图片。该页面包括股票表现和关键统计数据的交互式图表,如市盈率、每股收益、Beta 值、股息信息等。你甚至可以在左下角看到这只股票目前是被低估了,还是被高估了,或者处于合理的价值。然而,这不是建议的位置。
苹果的雅虎财经页面
如果你继续向下滚动,页面右侧会出现一个类似下图的图表,概述了分析师在过去几个月对特定股票的建议。
雅虎财经股票分析师推荐图
股票分析师将股票分为 1-5 级,1 级表示强烈买入,5 级表示强烈卖出。推荐的平均值是底部的推荐评级(苹果的评级为 2)。我的算法解析给定列表中每只股票的值,然后将数据下载到 CSV 文件中。下面的 GitHub 要点包含了所有的代码。
解析算法的所有代码。
首先,我们必须导入依赖项,然后定义两个变量。跑马灯列表目前设置为包含标准普尔 500 的所有跑马灯符号,但可以根据您的喜好进行更改。如果您要增加代码列表,我建议取消注释第 31 行中的“time.sleep(0.5)”代码。建议变量被设置为一个空列表,这样我们就可以用它来存储我们从即将到来的 for 循环中收集的值。
for 循环实际上包含解析算法,该算法允许 Python 访问每只股票的雅虎财经页面,并收集其各自的推荐评级。任何没有有效评级值的公司都将以值 6 保存到列表中。
最后,将创建一个熊猫数据框架,其中包含每个股票及其推荐。然后,数据帧将作为 CSV 文件下载到您的计算机上。
我希望这个程序将来会对你有用。非常感谢您的阅读!
免责声明:本文材料纯属教育性质,不应作为专业投资建议。自行决定投资。
参考文献:
感谢贡献一个堆栈溢出的答案!请务必回答问题。提供详细信息并分享…
stackoverflow.com](https://stackoverflow.com/a/42237860/11915680)
如果你喜欢这篇文章,可以看看下面我写的其他一些 Python for Finance 文章!
对财经新闻进行秒级情感分析!
towardsdatascience.com](/stock-news-sentiment-analysis-with-python-193d4b4378d4) [## 用 Python 制作股票筛选程序!
学习如何用 Python 制作一个基于 Mark Minervini 的趋势模板的强大的股票筛选工具。
towardsdatascience.com](/making-a-stock-screener-with-python-4f591b198261) [## 在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
towardsdatascience.com](/creating-a-finance-web-app-in-3-minutes-8273d56a39f8)
几秒钟内解析 TradingView 股票推荐!
了解如何使用 Python 解析任意时间间隔的实时建议!
在我之前的一篇文章中,我们讨论了如何解析来自雅虎财经的顶级分析师对任何股票的建议。虽然他们提供了股票未来走势的验证,但他们每月只更新一次,并且没有提供任何关于评级背后的理由的信息。
幸运的是,从那以后,我偶然发现了一个很棒的网站 TradingView 。如果你不熟悉这个网站,他们提供的一个功能是短至 1 分钟或长达 1 个月的实时推荐。这些建议纯粹基于技术指标,包括移动平均线、振荡指标和枢轴,你可以直接在页面上看到计算结果!
因此,我没有每次需要推荐时都访问网站,而是用不到 50 行代码创建了这个简单的解析器。
TradingView 简介
在我进入编码方面之前,我想快速介绍一下 TradingView 上的这些推荐内容和位置。如果你转到这个页面,你会看到一些类似于我下面的图片。该页面包括关键统计数据,如市盈率、每股收益、市值、股息信息等。你甚至可以点击概览来获得一个全面的比率表,以及一个交互式图表和最近的新闻。然而,这不是建议的位置。
苹果交易视图页面顶部
如果你继续向下滚动技术面页面,会有多个图表,如下图所示,列出了信号背后的建议和统计数据。
TradingView 推荐图表(我们将收集的内容)
交易视图技术指标统计
建议范围从强买到强卖,正如你在第二张图中看到的,它们完全依赖于技术指标信号。我们将建立的算法将很快分析买入信号、中性信号、卖出信号的数量,以及总体建议。下面的 GitHub gist 包含了所有的代码!
使用 Python 抓取 TradingView
解析算法的所有代码。
设置
如果您没有安装 Selenium 或 Pandas ,您可以访问它们各自的链接,并使用终端中的 pip 下载它们!我们还需要一个 chromedriver(模拟的 chrome 浏览器 Selenium 控件),要使用 Python 下载它,你可以使用 PyPi 中的 webdriver-manager 包。
此外,只要安装了必要的依赖项,就可以使用任何支持 Python 的 IDE 或文本编辑器。我个人会推荐通过 Anaconda 下载 Visual Studio 代码或者 Spyder。
让我们进入代码
现在所有的东西都应该已经安装在你的机器上了,你也知道我们要抓取什么了,让我们开始写代码吧!
首先,我们必须导入程序其余部分所需的依赖项。在这种情况下,我们将需要内置的时间模块、Pandas 和 Selenium。
时间模块将允许我们让程序休眠几秒钟,以便模拟的浏览器可以完全加载。熊猫将允许我们用我们收集的数据创建一个数据框架。最后,我们将需要 selenium,这样我们就可以创建/控制浏览器窗口并抓取 JavaScript 呈现的信息。
接下来,我们可以创建两个变量,一个用于 ticker,另一个用于我们特别抓取的区间。间隔可以是我在下面代码栏中包含的任何一个。
#===================================================================
# Intervals:
# 1m for 1 minute
# 15m for 15 minutes
# 1h for 1 hour
# 4h for 4 hours
# 1D for 1 day
# 1W for 1 week
# 1M for 1 month
# ==================================================================
在我们包含了导入和参数之后,我们可以设置 chromedriver 了。Options 类将允许我们添加 headless 这样的参数来定制模拟的浏览器。添加 headless 告诉浏览器在每次运行程序时不要弹出。我们可以将可执行路径设置为您之前下载 chromedriver 的路径。在这种情况下,我将它直接下载到我的目录中,但您不必这样做。
我们可以在一个 try/except 块中添加我们的抓取脚本来捕捉程序中断时的错误。首先,我们必须使用 webdriver.get(URL)打开浏览器,刷新以正确加载页面的各个方面,然后添加 time.sleep(1)使程序慢一秒钟,直到浏览器完全渲染完毕。
使用。通过 selenium.webdriver 中的 find_by_class_name 方法,我们可以精确定位我们想要清除的部分。例如,只有建议具有以下类“speedometerSignal-pyzN-tL”我们可以通过 Chrome DevTools 中的 inspect 元素来检索这些类名。打开 DevTools,你可以右击你想解析的部分,然后按“inspect ”,得到类似下图的结果!
Chrome 开发工具
我们可以使用方法检索“Buy”。get_attribute('innerHTML '),它将存储 HTML 标记内的文本。
同样,我们可以检索买入、中性和卖出信号的数量,方法是找到所有这些信号之间相似的类名,然后使用方法。按类名查找元素。因为这次我们调用的是元素,而不是元素,所以这个方法将返回一个包含我们指定的类名的 HTML 标签列表。
最后,我们可以将所有信号附加到一个列表中,并使用。from_records 方法,我们可以将数据元组和列列表转换成 DataFrame。最后,我们可以通过为 ticker 添加一列,将该列设置为索引,并为我们的最终产品转置(旋转)数据帧来清理它。
程序输出
现在,在几秒钟内,您应该会得到与上图类似的输出。我希望这个算法将来对你有用。非常感谢您的阅读!
免责声明:本文材料纯属教育性质,不应作为专业投资建议。自行决定投资。
如果你喜欢这篇文章,可以看看下面我写的其他一些 Python for Finance 文章!
了解如何在不到 3 分钟的时间内解析顶级分析师的数千条建议!
towardsdatascience.com](/parse-thousands-of-stock-recommendations-in-minutes-with-python-6e3e562f156d) [## 用 Python 制作股票筛选程序!
学习如何用 Python 制作一个基于 Mark Minervini 的趋势模板的强大的股票筛选工具。
towardsdatascience.com](/making-a-stock-screener-with-python-4f591b198261) [## 在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
towardsdatascience.com](/creating-a-finance-web-app-in-3-minutes-8273d56a39f8)
用 Python 解析 XML 数据
在熊猫数据框架中存储 XML 数据
可扩展标记语言(XML)是一种以人类和机器可读的格式编码数据的标记语言。XML 在各种程序中用于构造、存储和传输数据。Python 包含几个处理 XML 数据的接口。在本帖中,我们将讨论如何使用 python“xml”库中的“ElementTree”模块来解析 XML 数据并将数据存储在 Pandas 数据框中。
我们开始吧!
出于我们的目的,我们将使用一个样本“xml”文件,“books.xml”,它可以在这里找到。该文件包含各种书籍的信息,如书名、作者姓名和价格。
首先,让我们从 python“XML”库中的“ElementTree”模块导入“parse ”:
from xml.etree.ElementTree import parse
现在,让我们看看“books.xml”中的文件标签:
我们可以通过将文件名传递给“parse()”方法来定义一个已解析的“XML”文档对象:
document = parse('books.xml')
如果我们打印对象,我们会看到在指定的内存地址有一个“ElementTree”对象:
print(document)
让我们使用内置的“dir()”方法来看看该对象可用的方法和属性:
print(dir(document))
让我们使用方法“iterfind()”来返回一个生成器,我们可以在“for-loop”中对其进行迭代。我们需要在“iterfind()”方法中指定一个路径参数。让我们选择“图书”路径:
for item in document.iterfind(‘book’):
print(item)
我们看到,我们有几个“元素簿”对象存储在不同的内存地址。我们可以使用“findtext()”方法从这些对象中提取信息。让我们提取“作者”标签中的信息:
for item in document.iterfind('book'):
print(item.findtext('author'))
我们还可以提取标题:
for item in document.iterfind('book'):
print(item.findtext('title'))
让我们看看价格:
for item in document.iterfind('book'):
print(item.findtext('price'))
接下来,我们可以初始化用来存储这些值的列表:
author = []
title = []
price = []
在 for 循环中,我们可以追加值:
for item in document.iterfind('book'):
author.append(item.findtext('author'))
title.append(item.findtext('title'))
price.append(item.findtext('price'))
然后,我们可以将这些列表存储在一个数据框中。让我们首先导入熊猫库:
import pandas as pd
接下来,让我们定义一个包含每本书的“标题”、“作者”和“价格”的数据框:
df = pd.DataFrame({'title': title, 'author':author, 'price':price})
接下来,让我们打印结果数据框:
print(df)
我们还可以将“流派”、“发布日期”和“描述”添加到数据框中:
genre = []
description = []
publish_date = []for item in document.iterfind('book'):
...
genre.append(item.findtext('genre'))
description.append(item.findtext('description'))
publish_date.append(item.findtext('publish_date'))
既然我们在数据框中有了所有的信息,我们可以做一些事情,例如将“价格”字符串转换为“浮点型”,并计算“价格”列的平均值:
df['price'] = df['price'].astype(float)
print("Mean price: ", df['price'].mean())
让我们也将“publish_date”转换为“datetime”对象,并提取年、月和日的值:
df['publish_date'] = pd.to_datetime(df['publish_date'])
df['year'] = df['publish_date'].dt.year
df['month'] = df['publish_date'].dt.month
df['day'] = df['publish_date'].dt.day
print(df.head())
我们还可以使用 collections 模块中的“Counter()”方法来查看作者和流派的分布情况:
from collections import Counter
print(Counter(df['author']))
print(Counter(df['genre']))
我就讲到这里,但是您可以随意使用数据并自己编码。
结论
总之,在这篇文章中,我们讨论了如何使用 python 中的“xml”库解析 XML 数据。我们展示了如何使用“iterfind()”方法来定义一个生成器对象,我们可以在“for-loop”中迭代该对象。我们还展示了如何使用“findtext()”方法访问元素标记信息。然后,我们将 XML 信息存储在用于定义 Pandas 数据框的列表中。我希望你觉得这篇文章有用/有趣。这篇文章的数据和代码可以在 GitHub 上找到。感谢您的阅读!
第 1 部分:用 Python 定义和计时 API 函数
Python 中并发性的图形化探索
探索 Python 中的并发性以加速从远程 API 检索数据的系列文章。
问题是
使用 API 时,大部分时间都花在等待响应上。当从外部服务大量收集信息时,这会导致大量时间浪费。本文探索了如何使用 Python 的多处理、线程和异步特性从顺序请求转变为并发请求。
介绍
在之前的文章中,我们编写了一个简单的函数,它可以处理单个查询返回的问题数量的吉拉 API 限制。这解决了问题,但即使只有几千个结果,检索所有问题的时间也可能会在几分钟内完成。
一个 JQL 查询返回 1334 个问题,每个请求 50 个问题(27 个请求),耗时 31 秒。 使用并发通过 改变 7 行代码 ,我们可以将此减少到 2.5 秒。
为什么?
使用 Jupyter 内置的%time
魔法的一个快速简介显示了为什么有这么大的改进空间。
CPU times: user 672 ms, sys: 125 ms, **total: 797 ms**
Wall time: **31.4 s**
在 31.4 秒的运行时间中,顺序执行请求只占用了 797 毫秒的 CPU 时间。所以在这 31 秒中,比多 97%的时间都花在了 等待 的网络呼叫上。
提高性能
本质上有两类性能问题:
- 我一直在等待网络/磁盘/数据库/其他进程做一些事情,同时也想用 CPU 做一些有用的事情。 Python 解决方案:线程化还是异步化。
- 我正在做一些计算量很大的事情,希望尽可能使用所有的 CPU / GPU 时间。 Python 解决方案:多重处理或降至 c 语言
如上面的%time
示例所示,API 调用牢牢地属于第一类。这是需要记住的重要一点:
我们 难道 想方设法让 各自的要求走得更快。
改为 我们试图将 【等待并行】 所以 整套要求 完成得更快。
由于我们的 CPU 做的工作很少,为了让单个请求运行得更快,我们需要查看网络层并升级连接。相反,我们可以通过分批启动请求并组合结果来加快整个请求集的速度。
方法
在本系列中,我们将从最慢的情况开始,即顺序请求,然后尝试三种加快速度的方法:
- ***序贯,*我们的基础案例参考点。
- ***多重处理,*与
**multiprocessing.Pool()**
多重处理并不完全适合这个问题,但仍然提供了很大的改进,这是一个有趣的比较。 - ***穿线,*同
**concurrent.futures.ThreadPoolExecutor()**
。 - ***异步,*与
**asyncio.gather()**
使用async
和await
以及httpx
模块来支持异步 HTTP 请求。
注意:本系列中的计时代表了测试设置中每种方法的平均值,而不是严格意义上的基准。我们对绝对性能的技术之间的比较更感兴趣
我们使用吉拉 API 作为示例,但是这些解决方案可以很容易地扩展到其他用例,其中您的应用程序花费大量时间等待其他计算机/网络/磁盘/进程做一些工作并返回结果。
在这个系列中,我们将讨论两个函数。
**jira_query()**
接受一个吉拉 URL,JQL 查询字符串,一个指示要检索结果的“页面”的整数。- 一个函数,给定一个 JQL 查询,计算出检索所有结果需要多少个请求,并使用
**jira_query()**
将所有结果以及相关的计时数据收集到一个列表中。在这里,我们将试验不同的并发方法(在每一节中显示)。
jira_query()
函数在给定起始页(查询号)、URL、JQL 查询字符串和要检索的结果数的情况下,检索吉拉问题列表。
侧边栏:用 perf _ counter _ ns() 函数计时在整个系列中,我们将使用
*time.perf_counter_ns()*
函数来记录开始和结束时间。这将从系统相关的参考时间返回以纳秒为单位的当前时间。来自文档:返回值的参考点是未定义的,因此只有连续调用的结果之差才是有效的。
因此,我们不能跨系统比较时间,或者系统重启,但是即使请求发生在不同的进程/线程上,我们 也可以 比较相对的开始和结束时间,精确度很高。这有助于可视化不同并发模块中发生的事情。
顺序请求
下面的函数使用jira_query()
建立一个结果列表,给出一个吉拉 URL、JQL 查询(例如Project = CAE ORDER BY key Asc
)和每个请求返回的结果数。
按顺序从 JQL 查询结果集中检索所有问题。
调用这个函数会返回一个由jira_query()
返回的字典列表。通常我们会对实际产生的问题感兴趣,但这里我们更感兴趣的是关于检索了多少个问题以及花费了多长时间的元数据,因此实际的问题列表嵌套在issue_list
键中。
调用顺序版本,将结果加载到数据帧中进行显示和绘图。
产生的数据帧显示了相对于**t_0**
(第一个请求开始的时间)的开始和结束时间。如您所见,我们最终收到 27 个请求,检索 1334 个问题,一次最多 50 个。
绘制结果真正说明了顺序行为。请记住,在每个蓝条期间,CPU 仅在大约 2–3%的时间内工作。剩下的时间用来等待网络请求返回。
检索 1334 个问题,每批 50 个,有连续请求。总时间为 31.4 秒,平均请求时间为 1.16 秒。
在下一节中,我们保持jira_query()
不变,但是使用multiprocessing.Pool()
来调整协调功能,这样可以大大加快速度。
系列地图
- 第 1 部分:在 Python 中定义和定时一个 API 函数(你在这里)
- 第 2 部分:用 Python 多重处理 API 请求
- 第 3 部分:用 Python 线程化 API 请求
- 第 4 部分:使用 Python 和 httpx 的异步 API 请求
- 第 5 部分:API 请求时序比较——顺序、多处理、线程和异步
第一部分:情节性元学习和大脑
实践教程
弥合机器学习和神经科学之间的差距
照片由摩根·豪斯尔在 Unsplash 上拍摄
这是我硕士学位论文系列博文的第一部分。它假设了一些神经科学和强化学习的初步知识,但不用担心,如果你有任何问题,我会在评论区。先简单介绍一下我:我获得了伦敦大学金史密斯学院的计算认知神经科学(CCN)硕士学位。因为我来自计算机科学/机器学习背景,所以我决定从事这两个领域交叉的研究项目。我很大程度上受到了 DeepMind 神经科学团队在meta-RL【20,21】上所做的工作以及萨姆·里特的博士论文【22】的启发,这篇文章在很大程度上是基于这些论文。这实际上是我决定去 CCN 攻读硕士学位的主要原因之一。这让我觉得,如果一个人想成为人工智能领域的主要研究人员,就有必要了解大脑。这篇文章讨论了情景元学习和大脑的关系,试图弥合机器学习和神经科学之间的差距。
动机
近年来,深度强化学习(RL)一直处于人工智能(AI)研究的前沿,在一系列不同的领域取得了突破[1,2,3]。这导致认知科学家转向人工智能的发展,并询问它是否能给我们任何关于人类认知机制的见解[21];考虑到 RL 最初的灵感来源于动物条件反射的心理学研究[4,5]。以伊凡·巴甫洛夫的开创性工作为例。在他的实验中,狗开始对蜂鸣器的声音垂涎三尺,将它与食物联系起来。因此,他们能够在实际观察之前就预测到奖励。使用这种预测信号,人们可以训练狗或任何动物来执行特定的任务。类似地,RL 算法可以通过正负奖励来指导人工智能体执行一组使累积长期奖励最大化的动作。
然而,deep-RL 的一个主要缺点是其样本效率低[21],这自动使其失去了作为生物学习的合理模型的资格。换句话说,为了达到适当的专业水平,一个人工智能体所需要的经验比人类所需要的要多几个数量级。为了解决这个问题,Botvinick 等人(2019 年)确定了导致 deep-RL 缓慢的两个主要原因;这些是递增的参数调整,从弱感应偏置开始。前者是为了避免灾难性的推断,而后者允许神经网络适应广泛的问题。考虑到这一点,他们表明,这两个因素都可以通过使用情景记忆和学习任务分配的元学习来缓解。他们还指出,这两种解决方案都被证明对神经科学和心理学有进一步的影响。
与生物学的关系
RL 和纹状体多巴胺系统
首先,让我们回溯一下,看看传统的 RL 算法为神经科学提供了什么。据观察,中脑多巴胺神经元的放电特性驱动纹状体中的突触可塑性,以巩固经验行为和奖励之间的联系[6]。RL 的研究后来引导神经科学家发展了多巴胺能功能的基于奖励的学习理论。具体来说,多巴胺释放编码了一个“惊喜”指数,反映了由时间差(TD) 学习算法预测的奖励预测误差(RPE)信号,或者换句话说,预期和实际奖励之间的差异【7】。对非人灵长类动物、人类和啮齿类动物的进一步研究集中在一个经典模型上,在该模型中,突触前和突触后神经元的同步放电在多巴胺存在时导致更强的突触连接,而在多巴胺不存在时导致更弱的连接[8]。换句话说,导致 RPE 信号增加的神经元放电通过所谓的“三因素规则”得到加强[9]。
Meta-RL 和前额叶皮层
然而,这一经典模型受到了来自前额叶皮层(PFC)的一系列发现的挑战。已经表明,PFC 的扇区对 RL 的基本量进行编码,例如动作和状态的期望值[10,11],以及奖励和动作的近期历史[12,13]。这表明 PFC 实现了基于奖励的学习机制,类似于以前归因于基于多巴胺的 RL 的机制。因此,一个问题自然产生了:这两个系统之间的关系是什么?Wang 等人(2018)通过提出一种新的理论解决了这一难题,在该理论中,PFC 和基于多巴胺的 RL 是两个独立的 RL 系统,实现了不同形式的学习。具体来说,他们表明 PFC 通过利用任务结构的表示来执行基于模型的 RL,而不是基于由多巴胺突触学习驱动的直接刺激-反应关联的无模型 RL。在这个理论中,前额叶网络中的突触权重是由基于多巴胺的 RL 在一系列相关任务中形成的。因此,无模型 RL 产生了能够快速适应新环境的第二种独立的基于模型的 RL,并且被称为“meta-RL”。Wang 等人(2018)通过大量模拟证明,meta-RL 可以解释大范围的行为和神经生理学发现,这些发现为基于多巴胺的标准模型带来了困难。
情景学习和海马体
Ritter 等人(2018)指出,虽然王等人(2018)提出的元强化学习框架提供了增量学习的完整描述,但它没有考虑情景学习过程。他们指出,利用过去的相关经验来制定新的决策是任何智能生物的一个基本特征。因此,代理人还必须能够将过去决策的结果提取到记忆中,并长时间存储,并且能够在以后遇到类似情况时检索相关结果,以评估未来行动的价值。最近的研究表明,在分析人类行为和 fMRI 数据时,考虑与海马相关的情景学习,比只考虑之前讨论的增量学习算法更符合模型[14]。除此之外,meta-RL 代理已被证明遭受灾难性遗忘[15];这意味着它必须重新学习以前掌握的策略,而不是利用从过去的经验中获得的知识。这些观察导致对利用情景记忆的计算模型的兴趣增加,并证明了情景学习过程在决策中的重要性[16]。受海马体相关功能属性的启发,Ritter 等人(2018)设计了扩展架构,使其包括模式完成、模式分离和皮层活动模式恢复等功能。这意味着,当代理人遇到一个类似于它以前见过的情境时,记忆应该被提取和恢复,而不完全干扰其工作记忆的当前状态。
作为计算模型的情景元强化学习
继 Wang 等人(2018)的工作之后,meta-RL 模型将 PFC 及其连接的皮质下结构(即基底神经节和丘脑核)概念化为形成同质的递归神经网络(RNN),该网络遵循对这组区域进行建模的新兴传统,抽象出许多神经生理学和解剖学细节[17]。它使用一种演员-评论家算法进行训练,这反映了基于多巴胺的学习系统。该网络在每个时间步接收感知数据的编码版本作为输入,以及表示在前一时间步它已经采取的奖励和行动的信号。RNN 学会将观察、奖励和行动的历史提取到它的隐藏状态中,这可以被视为一种工作记忆的形式,同时在一系列结构上相关的任务上进行训练。Ritter 等人(2018 年)扩展了这种架构,将非参数情景记忆作为可微分神经字典来实施[23]。它将工作记忆状态(即 RNN 隐藏状态)与作为密钥嵌入的感知上下文配对存储。当遇到类似的上下文时,通过使用一些学习到的门控机制,这可以稍后用于检索和恢复存储器激活到当前工作存储器中。这个模型与在神经科学研究中观察到的一致;检索情景记忆会触发一种活动模式,类似于最初在支持工作记忆的回路中经历的活动模式[18]。该模型中采用的恢复方法在功能上类似于 PFC 中运行的门控机制[19]。总之,当以最大化特定回报函数为目标,在包含情节和增量结构的任务分布上进行训练时,情节 meta-RL 模型能够提供对无模型和基于模型的策略的统一描述。
下一步是什么
在下面的文章中,我将使用这个模型来重现用于分离无模型系统和基于模型系统的情景(或上下文)两步任务的行为结果。同时,你可以在这里找到我的实现。
参考
[1] Mnih,v .,Kavukcuoglu,k .,Silver,d .,鲁苏,a .,Veness,j .,Bellemare,M. G .,Graves,a .,Riedmiller,m .,Fidjeland,A. K .,Ostrovski,g .,Petersen,s .,Beattie,c .,Sadik,a .,Antonoglou,I .,King,h .,Kumaran,d .,Wierstra,d .,Legg,s .,Hassabis,D. (2015 年)。通过深度强化学习实现人类水平的控制。自然,518(7540):529–533。
[2] Silver,d .、Huang,a .、C. J .、Guez,a .、Sifre,l .、van den Driessche,g .、Schrittwieser,j .、Antonoglou,I .、Panneershelvam,v .、Lanctot,m .、Dieleman,s .、Grewe,d .、Nham,j .、Kalchbrenner,n .、Sutskever,I .、Lillicrap,t .、Leach,m .、Kavukcuoglu,k .、Graepel,t .和 Hassabis,D. (2016 年用深度神经网络和树搜索掌握围棋。自然,529:484–503。
[3] Vinyals,o .、Babuschkin,I .、Czarnecki,w .、Mathieu,m .、Dudzik,a .、Chung,j .、Choi,d .、Powell,r .、Ewalds,t .、p .、Oh,j .、Horgan,d .、Kroiss,m .、Danihelka,I .、Huang,a .、Sifre,l .、Cai,t .、Agapiou,j .、Jaderberg,m .和 Silver,D. (2019)。星际争霸 2 中使用多智能体强化学习的特级大师级别。自然,575。
[4]巴甫洛夫,I. P. (1927 年)。条件反射:大脑皮层生理活动的研究。牛津大学出版社。
[5]雷斯科拉和瓦格纳(1972 年)。巴甫洛夫条件作用理论:加固和非加固有效性的变化,第 2 卷。
[6]舒尔茨、达扬和蒙塔古(1997 年)。预测和奖励的神经基础。科学,275(5306):1593–1599。
[7]萨顿和巴尔托(1998 年)。强化学习:导论。麻省理工学院出版社,美国马萨诸塞州剑桥。
[8]n . Daw 和 Tobler,P. (2014 年)。通过强化学习的价值:多巴胺和强化学习的基础。神经经济学:决策和大脑,283-298 页。
[9]格里姆彻,P. W. (2011 年)。理解多巴胺和强化学习:多巴胺奖励预测误差假说。美国国家科学院院刊,108 期(增刊 3):15647–15654。
[10]h .普拉斯曼、j .奥多尔蒂和 a .兰格尔(2007 年)。眶额皮层编码日常经济交易中的支付意愿。神经科学杂志,27(37):9984–9988。
[11]Padoa Schioppa c .和 Assad j .(2006 年)。眶额皮层神经元编码经济价值。自然 441,223–226。《自然》, 441:223–6。
[12] Seo,m .,Lee,e .和 Averbeck,B. (2012 年)。额叶-纹状体回路中的动作选择和动作价值。神经元,74:947–60。
[13] Tsutsui,K.-I .,Grabenhorst,f .,Kobayashi,s .,和 Schultz,W. (2016 年)。前额叶皮层神经元中经济对象估价的动态代码。自然通讯,7:12554。
[14]博恩施泰因、卡瓦、肖哈米和道(2017 年)。对过去选择的回忆会使人类对奖励产生偏见。bioRxiv。
[15] Ritter,s .,Wang,J. X .,Kurth-Nelson,z .,Jayakumar,S. M .,Blundell,c .,Pascanu,r .,Botvinick,M. M. (2018 年)。经历过,做过:元学习与情景回忆。在 ICML。
[16] Gershman,S. J .和 Daw,N. D. (2017 年)。人类和动物的强化学习和情景记忆:一个整合框架。心理学年度评论,68(1):101–128。PMID: 27618944。
[17]宋海峰,杨广瑞,王晓军(2016)。用于认知和价值任务的递归神经网络的基于奖励的训练。bioRxiv。
[18] Xiao,x .,Dong,q .,Gao,j .,Men,w .,Poldrack,R. A .,和薛,G. (2017 年)。情景记忆提取过程中的变形神经模式恢复。神经科学杂志,37(11):2986–2998。
[19]c .查塔姆和 d .巴德雷(2015 年)。工作记忆的多重门。行为科学最新观点,1:23–31。
[20] Wang,J. X .,Kurth-Nelson,z .,Kumaran,d .,Tirumala,d .,Soyer,h .,J. Z .,Hassabis,d .,Botvinick,M. (2018 年)。前额叶皮层作为一个元强化学习系统。bioRxiv。
[21] Botvinick,m .,Ritter,s .,Wang,j .,Kurth-Nelson,z .,Blundell,c .,和 Hassabis,D. (2019 年)。强化学习,有快有慢。认知科学趋势,23。
[22]里特尔,S. (2019)。情景回忆的元强化学习:奖励驱动学习的整合理论。博士论文。
[23] Pritzel,a .,Uria,b .,Srinivasan,s .,Badia,A. P .,Vinyals,o .,Hassabis,d .,Wierstra,d .,和 Blundell,C. (2017 年)。神经事件控制。更正,abs/1703.01988。
原载于 2020 年 1 月 20 日https://bkhmsi . github . io。
圣地亚哥每小时能耗预测— I
我们将涵盖从不同来源导入和合并数据集、重采样、数据清理、EDA 和推理。
马克斯·本德在 Unsplash 上的照片
本文的第 1 部分将讨论能源(电力)消耗的基础知识,如何导入、重新采样和合并从不同来源(EDA)收集的数据集,并从圣地亚哥的能源消耗数据中得出一些基本推论。如果你只对建模部分感兴趣,那么请直接跳到这篇文章的第二部分。
[## 圣地亚哥每小时能源消耗预测(短期和长期预测)——II
时间序列基础&使用傅立叶级数处理多重季节性。使用(S)ARIMA(X)与线性预测…
towardsdatascience.com](/part-2-time-series-analysis-predicting-hourly-energy-consumption-of-san-diego-ii-f09665796c9)
在这些文章(第一部分和第二部分)中,我们将涵盖短期(一小时前和一周前)和长期(几个月前)预测。
nbviewer 上 Jupyter 笔记本的链接:
1。数据导入和 EDA (本帖涵盖)
2。构建 ML 模型和预测(在第 2 部分帖子中讨论)
整个项目的 Github 链接:小时 _ 能耗 _ 预测。
介绍
电力公司需要努力提前规划其发电厂中发电机组的分配,以匹配其区域能源需求,因为如果需求高于发电量,则可能导致几次停电,从而导致巨大的经济损失;另一方面,如果发电量高于需求,额外的电力将被浪费,并且还会在输电线路上产生不必要的负载。
因此,对于公用事业来说,预测能源消耗以便能够分配适当的资源来满足其需求是非常重要的。一年、一个月或一天的预测可以帮助电力公司规划更长的时间范围,但对于更平稳的日常操作,每小时(甚至更好)的预测可能非常有用。例如,如果电厂运营商得到下一个小时的高能源预测,他们可以通过打开更多的电厂来增加能源供应。
在这个项目中,我们将分析 SDGE 电力公司过去 5 年的每小时能耗数据,以发现一天中的小时、一周中的天、一年中的季节等的能耗趋势。,并检查外部温度和该地区的太阳能装置等因素是否会影响能源消耗。
电力公司可以利用开发的预测模型来有效地规划他们的发电操作,并平衡需求和适当的供应。
一些能源术语
- 瞬时负载也称为需求,以瓦特(W、kW、MW、GW 等)为单位。).
- 一段时间内消耗或产生的能量以瓦特小时(Wh、kWh、MWh、GWh 等)计量。)
- 因为我们将使用的数据是 1 小时间隔数据,所以在该间隔期间的需求和能量将是相同的。因此,我们将在本文中交替使用能源和需求术语。如果你想潜得更深,请参考这个链接。
注:峰值或最大负荷决定了一个电网所需的容量。例如,假设一个城市在 365x24 小时内的平均负荷是 10MW,但是在这 365x24 小时内的任何时刻观察到或可能观察到的最大峰值负荷是 20MW,那么该城市的发电厂基础设施(包括所有电源)的内置容量应该至少是 20MW。因此,平均而言,任何地区的发电装机容量都远大于该地区的平均预期需求。
术语说够了,让我们把剩下的“我们的能量”用于构建一些时间序列模型。
导入圣地亚哥地区的能耗数据
加州参与的公用事业地图(对于这个项目,我们将关注底部标记为圣地亚哥天然气和电力— SDGE 的红色区域)
导入数据:
该代码导入在 CAISO 地区运行的所有电力公司的每小时能耗数据,即 PGE、SCE、SDGE 和 VEA。它还包括 CAISO 总消耗量数据。
检查数据是否已正确导入。
同样,我们也可以检查 2014-2017 年的数据。
注意:在项目进行的时候,数据只提供到 2019 年 7 月,但如果你现在检查(2020 年 5 月),2019 年所有月份的数据都在网站上提供。这篇文章中显示的分析只考虑了 2014-2018 年的全年数据,但使用相同的代码可以很容易地添加 2019 年的剩余月份。
N 注:(2022 年 5 月更新)。我注意到 2019 年之前数据的存档链接不再活跃,我也无法在网站上找到更新的链接。但是自 2020 年以来,产生了更多的数据,并且链接中的“EMS 的历史每小时负荷数据”部分具有从 2019 年 1 月到 2022 年年中的数据,这对于该项目来说应该足够了。
从网站上读入数据并存储在字典中。然后组合字典中的所有关键字以生成单个数据帧。
原始数据框架,包括所有 CAISO 参与设施的每小时能耗值。
上述代码将为我们提供 4 家 CAISO 公用事业公司(即 PGE、SCE、SDGE 和 VEA)过去 5 年的每小时能耗值。它还会给出 CAISO 总数。稍后,我们将只提取这个项目的 SDGE 数据。
以下是在上述小时 1418 数据帧上执行的一些操作:
- “日期”列已经是 datetime 格式,所以我们可以直接对该列使用 Datetime 操作。
- 将能耗值转换成数字;在转换为 numeric 时,注意到列中的一些值是字符串,因此将 errors = ’ compete '参数传递给 pd.to_numeric 方法。此外,在转换之后,检查该列是否有空值。
- “日期”列数据中的某些值的格式为“201x-xx-xx yy:59:59.992”,因此使用 dt.floor 将其转换为“201 x-xx-xx YY:00:00”;这将使“日期”列中的日期时间行保持一致。
日期时间格式和转换为数值
- 发现 dataframe 只有 3 个丢失的值,所以通过使用*errors = ’ constrate ‘*我们没有丢失很多数据,这证明了这里使用’ constrate '方法的合理性。我只是想警告一下,*错误= ‘强制’*并不是每次都可以使用的最好方法,你必须在使用它之前检查你的数据,否则它会导致很多空值。
处理缺失的能量值
圣地亚哥地区的原始能量时间序列如下所示。
以上 hourly1418 数据帧保存为’ hourly1418CA.csv’ 文件以备后用。我们将继续仅使用 SDGE 能耗值。
hSDGE1418 = pd.read_csv('hourly1418CA.csv',usecols=['Dates','SDGE'], parse_dates=['Dates'])
圣地亚哥能源消耗时间序列(y 轴代表每小时的能源消耗,单位为 MWh)。有关交互式 Plotly 图和查看 CAISO 内所有公用设施的能耗图,请参阅 EDA 和 ML 笔记本。
- 43824 小时的数据(以兆瓦时为单位的能耗= 10⁶瓦时)
- 平均值:2364.92,中位数:2298.0
- 最小值:1437.08
- 最大:4867.0
我们可以看到能源消耗在夏季有更高的峰值,并且有很强的多季节性模式。这意味着,能源消耗有每日、每周以及季节性的模式。我们稍后还将分析数据的趋势。
其他日期时间参数已添加到数据帧中,如下所示:
添加日期时间功能的代码
添加了日期时间功能的能耗数据框
非工作日:当天不是联邦假日就是周末
季节:11-5 月:冬季和 6-10 月:夏季(根据 SDGE 链接 )
导入天气数据
为了查看天气(特别是温度)对能耗的影响,我们将从 gov.noaa.ncdc 导入每小时的温度数据。圣地亚哥有两个或更多的气象站记录了天气数据。由于机场的天气数据通常更准确,不会受到附近建筑和工业的影响,因此我们将使用圣地亚哥国际机场的数据来进行这个项目。(从此链接,选择圣地亚哥县>圣地亚哥国际机场和添加到购物车,即可下载所需数据)。
下载的天气数据有许多列,但我们将只关注’hourly dry bulb temperature’这是以华氏度为单位的室外温度。
天气数据每 7 分钟捕获一次,而能源消耗数据是每小时的数据,因此我必须在将天气数据与能源数据合并之前,通过计算每小时的平均温度,以每小时的间隔对天气数据进行重新采样。
hourly_weather = weatherdf_air.set_index('DATE').resample('H').mean()wehSDGE1418 = pd.merge(hSDGE1418, hourly_weather, how = 'outer', left_on = 'Dates', right_on= 'DATE')
太阳能/光伏安装数据
据观察,圣地亚哥的能源消耗在过去几年中总体上有所下降,并且在白天时间下降更为明显。因此,我们可以测试这一理论,也许圣地亚哥地区安装的太阳能容量在这些年里有所增加,因此当太阳出来时,越来越多的客户使用更少的能源,导致能源消耗的总体减少。我们可以通过导入 SDGE 地区的太阳能装置数据来检验这一理论。
太阳能装置数据从加州分布式
发电统计站点(直接数据链接)导入。该数据集包含许多参数,但我们将重点关注:
-批准日期:表示系统连接到电网开始运行的日期,以及
-系统大小 AC’: ,表示安装在现场的太阳能电池板的总交流千瓦功率。
要获得有关数据的更多细节,您可以在我的报告中查看此工作簿。
光伏安装数据框 SDGEPV 。每天总共有多少千瓦的 PV 被激活。
- 出于建模目的,我们对一天安装了多少光伏容量不感兴趣,而是想知道在任何特定的一天,该地区当前运行的光伏系统的总容量是多少。因此,使用累积的系统规模,我们希望看到该地区的总太阳能容量是如何整体增加的。
SDGEPVM['cum_AC_kW'] = SDGEPVM['AC_kW'].cumsum()##Truncating the PV installation data to the min and max dates' ##limits of the 'weather+energy' data - 'wehSDGE1418' and storing it ##as SDGEPVTSDGEPVT = SDGEPVM.loc[wehSDGE1418.Date.min():wehSDGE1418.Date.max(), ['AC_kW','cum_AC_kW']].reset_index()
基本上,这个想法是,如果客户有更多的光伏产品,他们将减少对电网供电的依赖(至少当太阳出来时),从而降低整体能源需求。因此,我们正在考虑在客户场所安装光伏(如住宅、商业建筑等)。).
让我们将 PV 数据集合并到之前合并的天气+能源数据帧中。
df = pd.merge(wehSDGE1418, SDGEPVT, left_on = 'Date', right_on = 'date_approved', how = 'left')*# dropping duplicate date columns*
df.drop(['DATE', 'date_approved'], axis=1, inplace=**True**)
- 由于在 5 年内(2014-18 年)并不一定每天都安装太阳能电池板,因此 SDGEPVT 数据帧并不具有与能量数据帧 wehSDGE1418 匹配的所有行,这导致 AC_kW 和 cum_AC_kW 列中的值缺失。
- 我们可以在“ AC_kW ”栏中用 0 代替 NaN 来表示当天的 0 安装。
- 并且由于累积安装的太阳能系统大小应该保持不变,直到遇到下一个非 NaN 值,使用向前填充方法来填充“ cum_AC_kW 列。
df['cum_AC_kW'] = df['cum_AC_kW'].fillna(method='ffill')
- Forward fill 对除了前几行之外的所有行都有效,因为 SDGEPVT 的数据不是从 2014–01–01 00:00:00 开始作为我们的能源和天气数据,所以我们必须使用不同的方法来填充前几个缺失的值。所以,基本上初始缺失值应该等于第一个非 NaN(cum _ AC _ kW’减去’AC _ kW’)。
在日光数据中填入 NaN 值
让我们探索一下阳光之城是如何用电的
绘制一些图表,从数据中获得洞察力。可以回答的一些问题是:
- 检查整个时间段内任何特定一天的平均能耗变化。典型的日平均负荷曲线是一条在晚上达到峰值的曲线,因为大多数人晚上下班回家,打开灯、电视、空调等…
- 绘制月平均负荷曲线。圣地亚哥夏天很热,但是冬天不冷,所以我们可以预计夏天的负荷会更高,因为商业和住宅建筑的冷负荷。
- 查看每小时与工作日的能源消耗图,了解典型一周的总体消耗模式。
- 温度对能耗有什么影响?
- 用户电表后太阳能装置数量的增加是否导致了整个 SDGE 地区总能耗的下降?
- 绘制整个 2014 年至 2018 年期间的平均每小时负荷曲线
从平均每小时负载曲线图中,我们可以观察到负载如何在夜间保持较低,然后随着人们醒来而开始增加,然后在办公时间继续增加,并在晚上每个人回家并打开电器时达到峰值。
2.每小时与工作日的能耗对比,以了解某一周的能耗变化情况
可以看出,周一至周五的平均消费在夜间低于 2000 英镑,在白天有所增加,在晚上达到峰值(> 2800 英镑),然后再次下降到夜间。在周末也可以观察到同样的模式,但周末的总消费似乎比预期的工作日低,因为大多数商业建筑在周末不营业。
来自 SDGE 的网站:“如果客户可以将他们的一些能源使用转移到下午 4 点至晚上 9 点以外的低成本时段,他们就可以降低电费,并在更容易获得的时候,更好地利用更清洁的可再生能源,如风能和太阳能。”
3.可视化不同年份的能量值分布
从 2014 年到 2018 年,所有年份的分布都是双模态的,能耗的模态值每年都向左移动(朝向较低的能源负荷)。
这种峰值左移可以被认为是由客户站点(称为电表后)可再生能源装置的增加以及客户对需求响应计划的参与增加导致电网峰值需求降低引起的。
3.1.绘制相同的分布图,但仅在晚上 12 点
从上图中我们可以看到,从 2014 年到 2018 年,能源消耗分布已经明显向更低的能源消耗值转移,这种影响在早上 8 点到下午 5 点的白天时段更加明显(所有时段见 EDA 笔记本)。
- 在白天时段发生这种转变的原因之一可能是在用户电表后面增加了更多的可再生能源,如太阳能。我们将在后面看到,在过去的 5 年里,SDGE 的太阳能装置有了相当大的增长。导致这种下降的因素有很多,包括参与能源效率和需求响应计划的人数增加等。但是我们将只关注这个项目的太阳能装置。
4.一起探索能源和天气数据
多年来能源消耗(红色)随干球温度(蓝色)的变化
温度与能耗
- 正如之前所怀疑的,我们可以看到能耗和温度确实是相互影响的,而且它们之间似乎有某种程度的相关性。
- 更重要的是,我们可以看到最高能耗值出现在最高温度下。这可能是因为温度较高时空调负荷较高,在夏季需要更多空调负荷。在圣地亚哥,冬天并不寒冷,但是夏天会非常热,所以人们在冬天不怎么使用加热器,因为他们在夏天会使用空调。
**for** season **in** SDGE_t_pv['season'].unique():
corrcoef, pvalue = scipy.stats.pearsonr(SDGE_t_pv[SDGE_t_pv['season'] == season]['SDGE'], \
SDGE_t_pv[SDGE_t_pv['season'] == season]['HourlyDryBulbTemperature'])
print('pearson correlation coefficient and pvalue for '+season, corrcoef, pvalue)
冬季皮尔逊相关系数和 p 值:0.32
夏季皮尔逊相关系数和 p 值:0.65
如上文第 3.1 节所述,从 2014 年到 2018 年,白天的能源消耗似乎有所下降。现在,这可能是由于更严格的能源效率措施被强制执行,或者对不同类别的纳税人(住宅、商业、农业等)的激励增加。)安装太阳能、风能等自发电资源。和/或能量存储,以避免更高的能量需求。据观察,多年来能源消耗的减少在白天更为明显,因此我们可以测试这一理论,即安装在公用事业区域的太阳能容量可能在多年来有所增加,正如我们将在下面看到的那样。
2014 年至 2018 年圣地亚哥累计太阳能装机容量(客户位置)
历年白天最大能耗与历年光伏装机累计装机容量之间的相关系数。还绘制了光伏安装与 6 个月滚动最大能耗的关系
**>> Energy consumption vs PV installed capacity: pearson correlation coefficient and pvalue for winter -0.515, 0.0
>> Energy consumption vs PV installed capacity: pearson correlation coefficient and pvalue for summer -0.338, 0.0**
光伏安装(蓝色)与 6 个月滚动最大能耗(红色)。请注意,轴值不是从零(0)开始。
- 这种相关性似乎对两个季节都很重要,对冬季月份更强。这有点违反直觉,但较冷的温度实际上比温暖的温度更有利于太阳能电池板。这种关系适用于大多数类型的太阳能电池板——与安装过程本身无关。在世界各地的实验室进行的研究清楚地表明,温度的升高对测试的太阳能电池板的总太阳能输出有相反的影响。换句话说,温度越高,太阳能光伏系统的效率就越低。
- 因此,在所有条件相同的情况下,你在六月、七月、八月的月水电费节省会比十二月、二月和一月少。ref:https://www . sunline energy . com/solar-PV-power-output-San-diegos-winter-months/
还有,“圣地亚哥,美国另一个日照最好的城市,冬天比夏天拥有更多的阳光”。(参考链接)
结论
我们已经看到了温度和光伏装机容量对圣地亚哥市能源需求的影响。由于气温较高,夏季的能源需求明显高于冬季。安装的太阳能电池板容量似乎也对这些年的能源消耗有轻微的(减少的)影响。而且,能源消耗有多种季节模式。
在这篇文章的第二部分,我们将从使用传统的预测算法如 SARIMAX 和线性回归建立简单的短期预测模型开始。然后,我们还将尝试使用非线性方法进行长期预测,如随机森林、XGBoost 以及集合模型(XGBoost + FB Prophet)。
感谢阅读。第 2 部分见。
免责声明:我在所有公用事业中选择了圣地亚哥,因为,I .它服务的区域比其他公用事业小,所以我们可以使用来自单一来源的温度数据,而不会有太大的变化(实际上圣地亚哥的天气模式变化很大,但对于本项目,我们将忽略这一点); ii .我是加州大学圣迭戈分校的校友。
第 2 部分:用 Python 和 Google Cloud 函数构建一个简单的 ETL 管道——MySQL 到 BigQuery(通过 SSH)
使用 Google Cloud 函数从 MySQL 数据库中提取数据并加载到 Google BigQuery 中
在第 1 部分中,我们看了如何从 FTP 服务器提取 csv 文件,以及如何使用云函数将其加载到 Google BigQuery 中。在本文中,我们将做同样的事情,但这一次,我们将从 MySQL 数据库中提取数据。
有很多 ETL 工具,有时它们可能会让人不知所措,特别是当你只想将文件从 A 点复制到 b 点时。所以今天,我将向你展示如何使用 python 3.6 和谷歌云功能从 MySQL 数据库中提取数据(extract)、修改数据(Transform)并将其加载到谷歌 BigQuery 表(load)中。
在本文中,我们将做以下工作:
- 设置云功能
- 提取数据
- 转换数据
- 加载数据
- 自动化我们的管道
首先,什么是 ETL?
提取、转换、加载(ETL)是将数据从一个或多个源复制到目标系统的一般过程,目标系统以不同于源的方式或在不同于源的上下文中表示数据。—维基百科
这是我们的 ETL 管道最终的样子:
ETL 管道(使用 Lucidchart 创建)
Google Cloud Functions:Cloud Functions(CF)是 Google Cloud 的无服务器平台,用于执行响应特定事件的脚本,如 HTTP 请求或数据库更新。CF 的替代方案是 AWS Lambda 或 Azure Functions 。
设置您的云功能
- 进入云功能概述页面。
确保您启用了云功能的项目被选中。 - 单击创建函数。
- 说出你的函数。
- 在触发器字段中,选择 HTTP 触发器。
- 在源代码字段中,选择内联编辑器。在本练习中,我们将编写一些自定义代码,因此您可以在编辑器中删除默认代码。
- 使用运行时下拉列表选择运行时。
- 认证:在本例中,我们将“允许未经认证的调用”,但建议使用服务帐户来访问云函数,并在我们将使用的云调度器服务帐户上授予云函数的“权限调用程序”。更多详情在文末。
确保您的运行时设置为“Python 3.7”,并在“高级选项”下将区域更改为离您最近的区域。在撰写本文时,CF 并不是在每个谷歌数据中心区域都可用,所以请点击这里的查看哪里启用了云功能。
完成这些步骤后,您的显示应该如下所示:
截图来自 GCP 控制台
我们的自定义代码
一个云函数有两个文件;一个 main.py 和一个 requirements.txt 文件。后者托管我们脚本工作所需的所有文件依赖项,因此单击 requirements.txt 选项卡,并确保编辑器中包含这些依赖项,如下所示:
提取
现在在 main.py 选项卡中,您可以开始包含下面的代码。我们创建了一个名为“ handler 的函数,以后用 HTTP 请求访问云函数时会用到这个函数。然后,我们使用必要的凭证登录到 MySQL 服务器,在第 30 行,编写从数据库中提取数据所需的 SQL 查询。
获得正确的 ssh 隧道细节很重要,这样就不会遇到认证问题。SSHTunnel 是一个很好的 Python 库。
注意:为了保护你的秘密管理者证书,请遵循这个指南。
改变
因为我们现在有一个名为“*df”*的熊猫数据框架,我们可以像这样操作熊猫数据:
df . group by([’ Name ‘])[[’ Orders ']]。agg(‘sum’)
这将对“orders”列中的所有订单进行汇总,并根据数据帧的“name”列中的每个唯一名称进行分组。在这个特殊的例子中,我没有包含任何转换,但是如果需要的话,您可以在第 30 行之后这样做。
负荷
现在,确保您创建了您的 BigQuery 表。然后我们简单地使用下面的代码将转换后的 CSV 文件加载到您创建的 BigQuery 表中。
- auto detect = True:-由于我们启用了“自动检测”,所以在将数据加载到表中时,我们不需要提供模式,因为它将基于 Dataframe 中的数据进行推断。
- WRITE _ disposition = " WRITE _ TRUNCATE ":-该作业将截断表数据并从头开始写入。
- source _ format=big query。source format . NEWLINE _ DELIMITED _ JSON
- load _ table _ from _ data frame*:-*一个不错的 BigQuery 函数,可以将数据从一个
**pandas.DataFrame**
加载到一个[**Table**](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.table.Table.html#google.cloud.bigquery.table.Table)
。
当您完成编写脚本时,您可以通过单击“创建”来部署云功能。当该功能成功部署后,您应该会看到一个绿色的复选标记。
使自动化
现在剩下要做的就是创建一个 cron 作业(Cloud Scheduler ),它将在某个预定义的时间间隔自动调用 CF。好消息是,在谷歌云平台上创建 cron jobs 非常简单,也是最简单的部分。你可以在这里按照的指示或者复制下图。
截图自 GCP
这里的“频率”字段将在每天凌晨 1:59 运行我们的脚本。
你可以使用这个很酷的 站点编辑器 进行简单的 cron 调度表达式。
既然我们的数据已经存放在数据仓库中,我们可以将它连接到任何可视化工具并对其进行分析。
后续步骤
如果您不想允许未经身份验证的调用,请为 Cloud Scheduler 设置 HTTP 目标身份验证。如果您设置了具有适当凭据的关联服务帐户,云调度程序可以调用需要身份验证的 HTTP 目标。点击阅读更多。
就是这样。希望这对你有帮助。这是一种非常简单的开发 ETL 的方法,任何人都可以在其上进行构建。
你可以 成为中等会员 享受更多这样的故事。
你可以在 Github 上找到完整脚本的链接。
第 2 部分—使用 Python 和 Heroku 创建实时交互式仪表盘
在 Heroku 云平台上部署您的 Streamlit 应用程序的简单说明
斯蒂芬·道森在 Unsplash 上拍摄的照片
介绍
如果你想在云平台上部署一个交互式仪表盘或你的投资组合作为一个网页,Heroku 是一个部署你的仪表盘的好应用。在我们之前的帖子中,我们讨论了如何使用 Streamlit 在 Python 中构建交互式仪表盘。在这里,我们将在云平台中部署我们的 streamlit 应用程序。
赫罗库
Heroku 是一个云平台,在虚拟容器(Dynos)中运行我们的应用程序,这些应用程序在运行时环境中执行。这些容器支持许多不同的语言,如 Ruby、Scala、Python、Java 等。它还提供定制的构建包,我们可以用它来部署任何其他语言的应用程序。
设置 Heroku
- 在 Heroku 中创建一个帐户,然后在那里创建一个新应用程序。
- 我们可以用不同的方式部署我们的应用程序,但在这篇文章中,我们将看到如何通过 Github 部署我们的代码。我们可以把 Github 账户和 Heroku 联系起来。
Heroku 的设置文件
现在我们已经准备好将 Github 代码部署到 Heroku 了。现在我们需要在代码中添加额外的文件来帮助我们自动运行代码。为此,我们需要在 Github 文件夹中添加两个额外的文件。
- 概要文件 —我们需要创建一个名为 Procfile 的文本文件,没有任何扩展名。它是一个声明如何运行以及运行什么命令的文件。
格式:
web: sh setup.sh && streamlit run <filename>.py
示例:
web: sh setup.sh && streamlit run covid_data.py
- setup.sh —我们还需要创建一个名为 setup.sh 的文件,我们将在其中写入配置,并为 streamlit 创建一个文件夹。我们只需要将下面的命令粘贴到文件中。
mkdir -p ~/.streamlit/
echo "\[server]\n\
headless = true\n\
port = $PORT\n\
enableCORS = false\n\
\n\
" > ~/.streamlit/config.toml
如果我们的文件夹中没有 requirement.txt 文件。我们可以使用 pipreqs 包来创建它。
$ cd ~/<folder_path>\
$ pipreqs
$ git add requirements.txt
$ git commit -m "requirements.txt"
$ git push
通过 Github 部署 Heroku
现在我们开始部署我们的项目。连接我们要部署的 Github 库,然后部署 branch。如果我们想要自动部署我们所做的每一个变更/提交,我们需要点击按钮"Enable Automatic deployments"
如何经营 Heroku
现在我们的 Streamlit 应用程序已经部署好了,我们需要运行 Heroku。
$ heroku ps:scale web=1 -a <app_name> $ heroku open -a<app_name>
最后,我们可以在云平台中看到我们的 Streamlit 应用程序
注意:在阅读下面这篇文章时,该 URL 可能没有激活。
所有的代码
这里是 Github repo 的所有代码,包括我们在这个博客中使用的所有代码。
这个帖子到此为止。希望对你有帮助。敬请关注新的有趣博客。
原载于 2020 年 7 月 2 日【https://confusedcoders.com】。
探索寻路图算法
旅游的游客
深入探究 Neo4j 图形数据科学库中可用的寻路算法
在系列的第一部分,我们从 WikiData API 构建了一个位于西班牙的古迹的知识图。现在我们将戴上图形数据科学护目镜,探索 Neo4j 图形数据科学库中可用的各种寻路算法。更重要的是,我们来看看圣诞老人问题的强力解决方案。现在,你可能想知道圣诞老人问题是什么。这是旅行推销员问题的一个变种,除了我们不要求解决方案在开始的同一个城市结束。这是因为圣诞老人有弯曲时空连续体的能力,一旦他送完东西,就会立刻飞回北极。
议程
- 推断古迹的空间网络
- 用 cypher projection 加载内存中的投影图
- 弱连通分量算法
- 最短路径算法
- Yen 的 k-最短路径算法
- 单源最短路径算法
- 最小生成树算法
- 随机行走算法
- 旅行推销员问题
- 结论
推断古迹的空间网络
目前,我们在图表中的纪念碑之间没有直接的关系。然而,我们有他们的 GPS 定位,这使我们能够识别附近的纪念碑。这样,我们可以推断出一个纪念碑的空间网络。
过程非常类似于推断相似性网络。我们通常不希望得到一个完整的图,其中每个节点都与所有其他节点相连。这将违背演示寻路算法的目的,因为任何两个节点之间的最短路径将总是直线,这将被表示为两个节点之间的直接关系。在我们的例子中,我们将把每个纪念碑连接到距离不到 100 公里的五个最近的纪念碑。这两个数字完全是任意的。您可以根据自己的情况选择其他选项。
MATCH (m1:Monument),(m2:Monument)
WHERE id(m1) > id(m2)
WITH m1,m2, distance(m1.location_point,m2.location_point) as distance
ORDER BY distance ASC
WHERE distance < 100000
WITH m1,collect({node:m2,distance:distance})[..5] as nearest
UNWIND nearest as near
WITH m1, near, near.node as nearest_node
MERGE (m1)-[m:NEAR]-(nearest_node) SET m.distance = near.distance
用 cypher projection 加载内存中的投影图
让我们快速回顾一下 GDS 图书馆是如何工作的。
图片借用自官方文档。
图表分析管道由三部分组成。在第一部分中,图形加载器从 Neo4j 中读取存储的图形,并将其作为内存中的投影图形加载。我们可以使用本地投影或 cypher 投影来加载投影图。第二步,我们按顺序执行图算法。我们可以使用一个图算法的结果作为另一个图算法的输入。最后但同样重要的是,我们将结果存储或流回 Neo4j。
这里,我们将使用 cypher 投影来加载内存中的图形。我建议你看一下官方文档了解更多关于它如何工作的细节。在 node 语句中,我们将描述图中的所有纪念碑,并添加它们的架构风格作为节点标签。添加一个定制的节点标签将允许我们在算法执行时按照架构风格过滤节点。在关系声明中,我们将描述纪念碑之间的所有链接,并包括距离属性,我们将使用该属性作为关系权重。
CALL gds.graph.create.cypher('monuments',
'MATCH (m:Monument)-[:ARCHITECTURE]->(a)
RETURN id(m) as id, collect(a.name) as labels',
'MATCH (m1:Monument)-[r:NEAR]-(m2:Monument)
RETURN id(m1) as source, id(m2) as target, r.distance as distance')
弱连通分量算法
尽管弱连通分量算法不是寻路算法,但它是几乎所有图形分析的一部分。它用于在我们的图中查找不连接的组件或岛。我们将从运行算法的stats
模式开始。
CALL gds.wcc.stats('monuments')
YIELD componentCount, componentDistribution
结果
弱连通分量统计结果
我们的古迹网络有六个独立的组成部分。这些结果是真实世界数据集的典型结果。我们有一个包含 98%的节点和几个漂浮在周围的小岛的超级组件。让我们检查较小的组件。
CALL gds.wcc.stream('monuments')
YIELD nodeId, componentId
WITH componentId, gds.util.asNode(nodeId) as node
OPTIONAL MATCH (node)-[:IS_IN*2..2]->(state)
RETURN componentId,
count(*) as component_size,
collect(node.name) as monuments,
collect(distinct state.id) as state
ORDER BY component_size DESC
SKIP 1
结果
较小的弱连通分量成员
五个较小的部分中有三个位于加那利群岛,一个位于巴利阿里群岛,特别是在马略卡岛。通过由 Estelle Scifo 开发的 Neomap 应用程序,我们可以在地图上看到加那利群岛的组成部分。
其中一个部分横跨 Fuerteventura 和 Lanzarote 的两个纪念碑。第二个由位于特内里费岛和大加那利岛的几个纪念碑组成。在左边,埃尔耶罗岛上有一座单独的纪念碑。它们是独立的组件,因为它们之间没有联系。组件之间没有连接意味着距离超过 100 公里,因为这是我们推断空间网络时选择的阈值。
又及:如果你喜欢水上活动,我强烈推荐你去加那利群岛。
最短路径算法
我们将使用的第一个寻路图算法是最短路径算法。它查找两个节点之间的最短加权路径。我们定义开始节点和结束节点,并指定在计算最短路径时算法应该考虑哪个关系权重属性。
MATCH (s:Monument{name:'Iglesia de Santo Domingo'}),
(e:Monument{name:'Colegiata de Santa María de Piasca'})
CALL gds.alpha.shortestPath.stream('monuments',{
startNode:s,
endNode:e,
relationshipWeightProperty:'distance'})
YIELD nodeId, cost
RETURN gds.util.asNode(nodeId).name as monument, cost
结果
成本以米为单位的距离来表示。我们可以用 Neomap 稍加修改的版本来可视化最短路径。我已经定制了纪念碑的弹出窗口,包括它的图像和建筑风格。
你可能会注意到,我们忽略了圣克鲁斯·德·坎加斯·德·昂尼斯纪念碑,它位于图片的中间偏右。从 Iglesia de San Emeterio 到 Santo Toribio de Liébana,稍微绕一点路就会比直线行驶的路程长。
如果我们想为一堂建筑课计划一次旅行,并且沿途只参观受哥特式或罗马式建筑影响的纪念碑,会怎么样?利用 GDS 图书馆,计划这样的旅行是非常容易的,因为我们可以用参数nodeLabels
过滤算法可以访问的节点。
MATCH (s:Monument{name:'Iglesia de Santo Domingo'}),
(t:Monument{name:'Colegiata de Santa María de Piasca'})
CALL gds.alpha.shortestPath.stream('monuments',{
startNode:s,
endNode:t,
relationshipWeightProperty:'distance',
nodeLabels:['Gothic architecture','Romanesque architecture']})
YIELD nodeId, cost
RETURN gds.util.asNode(nodeId).name as monument, cost
结果
这次的路线有点不同,因为算法只能访问受哥特式或罗马式建筑风格影响的纪念碑。
Yen 的 k-最短路径算法
我们已经学习了如何计算一对节点之间的最短加权路径。如果我们是更谨慎的游客,想要找到前三条最短路径,会怎么样?有一个后备计划,以防途中可能会发生意想不到的事情,这总是一个好主意。在这个场景中,我们可以使用 Yen 的 k-shortest path 算法。语法几乎与最短路径算法相同,除了添加的k
参数,该参数定义了我们希望找到多少条最短路径。
MATCH (s:Monument{name:'Iglesia de Santo Domingo'}),
(t:Monument{name:'Colegiata de Santa María de Piasca'})
CALL gds.alpha.kShortestPaths.stream('monuments',{
startNode:s,
endNode:t,
k:3,
relationshipWeightProperty:'distance'})
YIELD index,nodeIds,costs
RETURN index,[nodeId in nodeIds | gds.util.asNode(nodeId).name] as monuments,apoc.coll.sum(costs) as total_cost
结果
Yen 的 k-最短路径算法结果
这三条路几乎一样长,只差几百米。仔细看,三个变体中只有第二站不一样。如此小的差异可归因于我们的空间网络和示例节点对的性质。
单源最短路径算法
使用单源最短路径算法,我们定义起始节点,并搜索到网络中所有其他节点的最短加权路径。我们将检查一个加那利组件,以将最短路径的数量限制到一个合理的数量。
我们将检查特内里费岛—大加那利岛部分,并选择拉古纳大教堂作为起点。该算法试图找到到网络中所有其他节点的最短路径,如果不存在这样的路径,它将返回无穷大值作为结果。我们将使用gds.util.isFinite
过程过滤掉不可到达的节点。
MATCH (start:Monument{name:’Cathedral of La Laguna’})
CALL gds.alpha.shortestPaths.stream(‘monuments’,
{startNode:start, relationshipWeightProperty:’distance’})
YIELD nodeId, distance
WHERE gds.util.isFinite(distance) = True
RETURN gds.util.asNode(nodeId).name as monument,distance
ORDER BY distance ASC
结果
离拉古纳大教堂最近的纪念碑是康塞普西翁大教堂,只有 420 米远。在特内里费岛上似乎有两座康塞普西翁大教堂,我们可以观察到它在我们的搜索结果中出现了两次。在我们的网络中,距离拉古纳大教堂最远的纪念碑是圣胡安包蒂斯塔大教堂。
如果我们想找到从拉古纳大教堂到所有可到达的新古典主义纪念碑的最短路径的成本,我们可以通过nodeLabels
参数轻松实现。
MATCH (start:Monument{name:'Cathedral of La Laguna'})
CALL gds.alpha.shortestPaths.stream('monuments',
{startNode:start, relationshipWeightProperty:'distance',
nodeLabels:['Neoclassical architecture']})
YIELD nodeId, distance
WHERE gds.util.isFinite(distance) = True
RETURN gds.util.asNode(nodeId).name as monument,
distance
ORDER BY distance ASC
结果
在特内里费岛和大加那利群岛上似乎只有四座新古典主义纪念碑。
最小重量生成树算法
最小权重生成树算法从一个给定的节点开始,计算连接所有可达节点的生成树,其关系权重之和最小。例如,如果我们想用光缆或电缆连接特内里费岛和大加那利岛的所有古迹,我们将使用最小重量生成树算法。
MATCH (start:Monument{name:’Cathedral of La Laguna’})
CALL gds.alpha.spanningTree.minimum.write(‘monuments’,{
startNodeId:id(start),
relationshipWeightProperty:’distance’,
weightWriteProperty:’cost’})
YIELD effectiveNodeCount
RETURN effectiveNodeCount
结果
最小重量生成树算法结果在 Neo4j 浏览器中可视化
目前只有算法的write
模式可用。我们可以用 Neomap 可视化我们潜在的电缆路径。
随机行走算法
我们可以想象随机漫步算法试图模仿喝醉的人群穿越网络。他们可能向左,或向右,向前两步,向后一步,我们永远不知道。要看人群醉到什么程度了。我们可以使用这种算法来提供随机的旅行建议。想象一下,我们刚刚参观了巴塞罗那大学历史建筑,但不确定接下来应该看哪些古迹。
MATCH (m:Monument{name:"University of Barcelona historical building"})
CALL gds.alpha.randomWalk.stream('monuments',
{start:id(m), walks:3, steps:5})
YIELD nodeIds
RETURN [nodeId in nodeIds | gds.util.asNode(nodeId).name] as result
结果
随机行走算法结果
记住,我们提到过随机行走算法试图模仿一个喝醉的人穿越网络。一个喝醉的人可能会两次参观同一个纪念碑而不在乎。例如,在第一个和第三个建议中,一个纪念碑出现了两次。幸运的是,我们有一些选项可以用以下两个参数来影响算法在 node2vec 模式下如何遍历网络:
return:该参数控制遍历中立即重新访问节点的可能性。将它设置为较高的值(> max(inOut,1))可以确保我们不太可能在接下来的两步中对已经访问过的节点进行采样。
inOut:该参数允许搜索区分“向内”和“向外”节点。如果 inOut > 1,则随机游走偏向靠近节点 t 的节点,相比之下,如果 inOut < 1, the walk is more inclined to visit nodes that are further away from the node t.
则两个参数的定义是从 原 Node2vec 论文 中总结出来的。
我们想推荐离我们的起点很近的纪念碑,所以我们选择参数inOut
大于 1。我们当然希望避免在遍历过程中重新访问已经访问过的节点,所以我们选择return
参数大于inOut
参数。
MATCH (m:Monument{name:"University of Barcelona historical building"})
CALL gds.alpha.randomWalk.stream('monuments',
{start:id(m), walks:3, steps:5,
mode:'node2vec', inOut:5, return:10})
YIELD nodeIds
RETURN [nodeId in nodeIds | gds.util.asNode(nodeId).name] as result
结果
node2vec 模式下的随机游走算法
不幸的是,return
参数确保我们不太可能在接下来的两步中对已经访问过的节点进行采样。这意味着我们不能确定重复的东西不会在我们散步的时候出现。在我们的例子中,Casa Batlló在第一个建议中出现了两次。我们可以通过创建更长的步行建议并在向用户显示结果之前过滤掉重复的建议来解决这个问题。在下面的查询中,我们计算九步长的步行,过滤掉重复的,只返回前五个结果。
MATCH (m:Monument{name:"University of Barcelona historical building"})
CALL gds.alpha.randomWalk.stream('monuments',
{start:id(m), walks:3, steps:9,
mode:'node2vec', inOut:5, return:10})
YIELD nodeIds
RETURN apoc.coll.toSet([nodeId in nodeIds | gds.util.asNode(nodeId).name])[..5] as result
结果
移除重复项后的随机游走算法结果
通过这种方式,我们可以确保结果不包含重复项。现在,我们可以用我们的旅行推荐应用程序来可视化结果。
旅行推销员问题
最重要的是,我们将解决旅行推销员问题的圣诞老人变体。如前所述,唯一的区别是我们省略了在起始位置结束的要求。我在大卫·巴顿写的游戏圣诞市场帖子中找到了这个问题的灵感。我把所有的荣誉都归功于大卫·巴顿,是他想出了这个解决方案。我的贡献是更新它,以便与 Neo4j 4.0 和 GDS 图书馆一起工作。
比方说我们想找到这座纪念碑之间的最佳路线:
:param selection => ["Castell de Santa Pau","Castell de Sant Jaume","Castell de Vilaüt","Castell de Sarraí","Castell de Solius","Portal d'Albanyà","Castell de Sant Gregori","Casa Frigola"]
我们把解决方案分成两步。首先,我们使用gds.alpha.shortestPath
算法计算所有选定纪念碑对之间的最短路径,并将结果存储为给定节点对之间的 SHORTEST_ROUTE_TO 关系。我们将总成本和沿最短路径的所有中间节点保存为 SHORTEST_ROUTE_TO 关系的属性。
WITH $selection as selection
MATCH (c:Monument)
WHERE c.name in selection
WITH collect(c) as monuments
UNWIND monuments as c1
WITH c1,
[c in monuments where c.name > c1.name | c] as c2s,
monuments
UNWIND c2s as c2
CALL gds.alpha.shortestPath.stream('monuments',{startNode:c1,endNode:c2,relationshipWeightProperty:'distance'})
YIELD nodeId, cost
WITH c1,
c2,
max(cost) as totalCost,
collect(nodeId) as shortestHopNodeIds
MERGE (c1) -[r:SHORTEST_ROUTE_TO]- (c2)
SET r.cost = totalCost,
r.shortestHopNodeIds = shortestHopNodeIds
完成第一步后,我们创建了所选纪念碑之间的 SHORTEST_ROUTE_TO 关系的完整图表。
旅行推销员问题第一步
在第二步中,我们将使用apoc.path.expandConfig
过程。它使我们能够执行可变长度的路径遍历,并对遍历进行细粒度控制。查看文档了解更多详情。
我们允许程序仅遍历带有relationshipFilter
参数的 SHORTEST_ROUTE_TO 关系,并仅访问带有whitelistNodes
参数的选定纪念碑。我们通过定义遍历的跳数或级别数(minLevel
和maxLevel
)并使用uniqueness
参数来确保所有选择的节点必须被恰好访问一次。我知道这很难理解,如果你需要帮助,我建议你在 Neo4j 社区网站上提问。然后,我们选择具有最小关系权重和的路径作为解决方案。因为我们计算了所选古迹之间所有可能的路线,所以这是旅行商问题的一种蛮力解法。
WITH $selection as selection
MATCH (c:Monument)
WHERE c.name in selection
WITH collect(c) as monuments
UNWIND monuments as c1
WITH c1,
[c in monuments where c.name > c1.name | c] as c2s,
monuments,
(size(monuments) - 1) as level
UNWIND c2s as c2
CALL apoc.path.expandConfig(c1, {
relationshipFilter: 'SHORTEST_ROUTE_TO',
minLevel: level,
maxLevel: level,
whitelistNodes: monuments,
terminatorNodes: [c2],
uniqueness: 'NODE_PATH'})
YIELD path
WITH path,
reduce(cost = 0, x in relationships(path) | cost + x.cost) as totalCost
ORDER BY totalCost LIMIT 1
WITH path,
totalCost,
apoc.coll.flatten([r in relationships(path) | r.shortestHopNodeIds]) as intermediate_stops,
[n in nodes(path) | id(n)] as node_ids
RETURN [n in nodes(path) | n.name] as path,
round(totalCost) as total_distance,
[optional in intermediate_stops where not optional in node_ids | gds.util.asNode(optional).name] as optional_stops
结果
圣诞老人解决方案
在结果的“路径”列中,我们有一个有序排列的要参观的选定古迹。我们的旅行将从卡斯特尔德圣豪梅开始,继续到卡斯特尔德维拉乌特等等。我们可以称之为西班牙城堡参观之旅,因为我们选择了六个城堡,我们可以选择沿途参观四个。该路径的总空中距离为 126 公里。让我们用旅行推荐应用程序来可视化结果。
红色标记是选定的纪念碑,蓝色标记是沿途可选的停靠点。
结论
我们已经用一些真实世界的用例展示了 Neo4j 图形数据科学库中可用的大多数寻路算法。这个系列剩下的唯一难题是完成旅行推荐应用程序。我计划在本系列的第三部分展示这个应用程序。在那之前,我鼓励你尝试各种 GDS 库算法,或者尝试在一个 Neo4j 沙盒实例上重现这个系列。如果您有任何进一步的问题,在 Neo4j 社区网站上有一群 Neo4j 专家随时可以帮助您。
和往常一样,代码可以在 GitHub 上获得。
圣地亚哥每小时能耗预测—ⅱ
时间序列基础&使用傅立叶级数处理多重季节性。使用 ARIMA(X)与线性回归、随机森林、XGBoost 和 FBProphet+XGB 进行预测。
卢卡斯·戴维斯在 Unsplash 上的照片
这篇文章的第 1 部分讲述了能源(电力)消耗的基础知识,如何导入、重采样和合并从不同来源和 EDA 收集的数据集。从 San Deigo 的能源消耗数据以及温度和太阳能电池板安装数据中也提取了一些推论。
[## 预测圣地亚哥的每小时能源消耗(短期和长期预测)— I
在第 1 部分中,我们将介绍从不同来源导入和合并数据集、重采样、数据清理、EDA 和…
towardsdatascience.com](/part-1-time-series-analysis-predicting-hourly-energy-consumption-of-san-diego-short-term-long-3a1dd1a589c9)
在这篇文章中,我将探讨一些时间序列模型,如持续预测(作为基线),ARIMA 和 FB 先知;然后扩展我的方法,包括线性回归、随机森林、XGBoost 和一个集合模型,看看这些线性和非线性方法是否能准确地模拟我们的时间序列。
nbviewer 上 Jupyter 笔记本的链接:
1。数据导入和 EDA (包含在第 1 部分中)
2。构建 ML 模型和预测(在本文中讨论)
如果你想要一个非常详细的时间序列数据处理的解释,建模和有效地可视化时间序列数据,那么请参阅上面列出的第二个笔记本。说了这么多,我还是会尽量涵盖本帖的重要话题。
整个项目的 Github 链接:小时 _ 能耗 _ 预测。
能耗数据
以下是上一篇文章中的数据和一些新增功能:
#Import the 5 years of hourly energy consumption data previously #cleaned, explored, and stored as 'hourly1418_energy_temp_PV.csv' in # Part 1sdge = pd.read_csv('hourly1418_energy_temp_PV.csv', index_col = 'Dates', parse_dates=['Dates', 'Date'])
用于预测的数据
- SDGE:我们的目标变量,圣地亚哥的每小时能耗(MWh )(也是每小时负荷或需求)
- 非工作日:如果当天是周末或假日,则为 0(二进制)
- 每小时干球温度:在圣地亚哥机场测量的干球温度,单位为华氏度
- cum_AC_kW:客户现场太阳能电池板的累计安装容量(直到索引栏中给出的日期),单位为 kW
圣地亚哥能源消耗时间序列(y 轴代表每小时的能源消耗,单位为兆瓦时)。有关交互式 Plotly 图和查看 CAISO 内所有公用设施的能耗图,请参见上面列出的 EDA 和 ML 笔记本。
从前面第 1 部分的分析中观察到的一些情况:
- 43824 小时(行)的数据(2014-2018 年 5 年)
- SDGE:最小值:1437.08,平均值:2364.92,中间值:2298.0,最大值:4867.0
- 时间序列有多种季节模式——每天、每周和每年。
- 略有下降趋势,与太阳能装机容量成反比
- 能耗与温度高度相关。
我使用了从 2014 年 1 月 1 日到 2018 年 3 月 31 日的时间序列作为我的训练数据。并预测了 2018 年 4 月 1 日至 12 月 31 日的每小时能耗值(整个数据的约 15%)。
最终目标
该项目的最终目标是:“电力公司可以利用开发的预测模型来有效地规划其发电运营,并通过适当的供应平衡需求。有效的预测对于电力公司规划其日常运营、满足其客户的能源需求以及避免任何过量发电非常有用。"
基本上,我们希望将 ML 模型拟合到我们的每小时能源消耗时间序列中,并使用它来预测圣地亚哥未来的能源消耗。
预测窗口 在任何时间序列问题中,预先定义预测的窗口是非常重要的。在这里,我测试了未来 1 小时、1 周和 8 个月的模型。
使用的误差指标 我已经为训练集和测试集的每个模型计算了 R2 得分、MAE、RMSE 和 MAPE 。在这 4 个指标中,MAPE将被选中,选出最佳型号。MAPE 帮助我们理解绝对值的%误差,并且对时间序列的绝对量级漠不关心。
假设
- 未来的太阳能电池板安装和温度数据对我们来说很容易获得,我们不需要为它们运行单独的预测(我希望!).在现实世界中,我们需要使用这些独立变量的预测值(或者至少使用温度的平均期望值和基于过去数据的估计未来太阳能安装量)。但是为了简单起见,我在这个项目中使用了这些变量的实际观察值。
事不宜迟,让我们把精力集中在时间序列预测问题上。
那么,时间序列有什么特别之处呢
时间序列是按时间顺序进行的一系列观察。任何时间序列数据都有以下组成部分: ref link
- **级别:**如果是直线,则为系列的基线值。
- **趋势:**系列随时间变化的可选且通常线性增加或减少的行为。
- **季节性:**行为随时间的可选重复模式或周期。
- **噪声:**模型无法解释的观测值的可选可变性。
大多数时间序列的另一个重要特征是,时间上接近的观测值往往是相关的(序列相关)。这种特征也被称为自相关,正如我们将在后面看到的,它构成了自回归综合移动平均( ARIMA )等传统时间序列建模技术的一个重要方面。
- **平稳性:**当均值、方差和自相关性随时间恒定时,时间序列是平稳的,并且没有季节性或周期性模式。在使用任何基于线性回归的模型如 ARIMA(基于 Y 滞后值的回归)之前,使时间序列平稳是非常重要的。一个时间序列通常是平稳的,方法是将该序列与其自身进行差分。关于平稳性的两篇好文章— 1 、 2 。
由于以上所有的特点,时间序列建模涉及到的方法与常规的 ML 问题略有不同。这里的是解释主要区别的一个很好的链接。
这里的是深入研究时间序列预测建模技术的另一个好资源。
- 时间序列交叉验证:(Ref)
时间序列的交叉验证有点不同,因为人们不能在保留时间结构的同时随机混合一个文件夹中的值。随着随机化的进行,观测值之间的所有时间依赖关系都将丢失。这就是为什么我们必须在优化模型参数时使用更复杂的方法,例如“滚动交叉验证”。这个想法相当简单——我们从开始直到某个 t 的一小段时间序列上训练我们的模型,对下一个 t+n 步骤进行预测,并计算误差。然后,我们将我们的训练样本扩展到 t+n 值,从 t+n 直到t+2∫n进行预测,并继续移动我们的时间序列测试段,直到我们遇到最后一个可用的观测值。这可以使用*sklearn.model_selection's*
TimeSeriesSplit
模块来建立。我们将使用这种技术来计算验证集误差。**
比较我们的模型的基线模型
- 短期提前一小时预测
**""" Error metrics for hour ahead forecasts when simply repeating last hour's values """** ***#* error_metrics(predicted_values, true_values) is a function that I *#* built to calculate the errors for a given model's predictions**_ = error_metrics(sdge_lin.loc[X_test.index.shift(-1, freq='H'), 'SDGE'], y_test)>> RMSE or Root mean squared error: 122.27
>> Variance score: 0.94
>> Mean Absolute Error: 99.23
>> Mean Absolute Percentage Error: 4.21 %
- 未来约 8 个月的长期预测
**""" Error metrics on months ahead forecast when simply repeating last year's values """*****# the data is hourly and one year = 8760 hours***_ = error_metrics(sdge_lin.loc[X_test.index.shift(-8760, freq='H'), 'SDGE'], y_test)>> RMSE or Root mean squared error: 330.74
>> Variance score: 0.55
>> Mean Absolute Error: 224.89
>> Mean Absolute Percentage Error: 9.23 %
基准预测,其中能源消耗的预测值与去年相应的日、小时和月的值相同。
在能源预测领域,获得正确的每日最大需求更为重要,因为这可以决定电厂运营商是否需要启动调峰(主要是燃气)电厂。所以,也要计算误差。
*"""****Resampling both the y_test and predictions at a 24 hours period and using the max as the aggregate function****"""*_ = error_metrics(sdge_lin.loc[X_test.index.shift(-8760, freq='H'), 'SDGE'].resample('24h').max(), y_test.resample('24h').max())>> RMSE or Root mean squared error: 389.37
>> Variance score: 0.22
>> Mean Absolute Error: 264.44
>> Mean Absolute Percentage Error: 8.60 %
“我们现在已经准备好建立预测未来的模型了。”
注:通常用于时间序列预测的一些经典时间序列模型是自回归综合移动平均(ARIMA)、带外生回归量的季节自回归综合移动平均(SARIMAX)、带外生回归量的向量自回归移动平均(VARMAX)、简单指数平滑(SES)、霍尔特温特指数平滑(HWES)等等。你可以在 这篇 中找到更多关于各款车型的细节。在本帖中,我们将只尝试 SARIMAX,然后尝试传统的线性和非线性 ML 模型。
带滞后的简单回归模型(提前一小时预测)
那么,我们为什么还要考虑使用传统的 ML 回归模型进行时间序列预测呢?
- 因为,在某些情况下,如销售预测或能源预测, y 变量强烈依赖于外部因素,如能源消耗情况下的温度。因此,与时间序列预测相比,它更像是一个回归问题,而且监督 ML 模型可以帮助我们在这种严重依赖外生变量的数据中找到更好的模式。
- 像 SARIMAX 这样的传统时间序列模型无法处理多重季节性。它们还需要大量历史数据才能准确预测,并且在进行超参数调整时更加耗时。
- 传统 ML 回归模型用于销售预测的用例请参见【3】。
- 此外,这篇【4】论文证明了使用简单的基于回归的 ML 模型来预测电力需求是可能的,并且误差与更复杂的模型相当。
能源消耗值也可以预期依赖于其先前的滞后值,因为一个地区的能源消耗在接下来的几个小时内不应该有太大的变化,除非发生任何意外或不幸的事件。因此,我们将添加能耗的滞后值作为 X 参数,并检查我们是否可以使用过去的值进行更好的预测(除了我们已经添加的变量)。
***""" Adding max 24 lags; lag1 is the value of the energy consumption in the previous hour, lag2 is the value of energy consumption*
*2 hours before the current value and so on.****"""*
***# Creating the lag variables*****for** i **in** range(24):
sdge1_lin['lag'+str(i+1)] = sdge1_lin['SDGE'].shift(i+1)***""" Since the first 24 values won't have any 24th lag, they will be NaN. So dropping the NaNs """***
lag_sdge = sdge1_lin.dropna()
请注意,我将小时划分为时间变量,将月份划分为季节,将工作日划分为非工作日或工作日。
有滞后的弹性净回归
对有滞后的数据进行拟合时,弹性网络回归模型的系数图。我们可以看到前一个小时的值有多重要。
plot_predvstrue_reg(elastic_net_lag.predict(X_test_lag), y_test_lag)
观察值与预测图
使用弹性网和随机森林回归分析观察到的与预测的能源需求值
**Error metrics for model Elastic net with all lags**
>> RMSE or Root mean squared error: 50.75
>> Variance score: 0.99
>> Mean Absolute Error: 37.36
>> Mean Absolute Percentage Error: 1.58 %
我们可以从图表和误差中看到,与基线相比,弹性网络模型在所有误差指标上都表现得更好。
- RMSE 仅为 50.75 兆瓦,而基准型号为 122 兆瓦。
- MAPE 也从 4.21%降至 1.58%。
- 因此,这个模型表现非常好,但它有一个限制—我们只能用它来预测下一个小时的值。即它能准确预测的最大时间窗口是 1 小时。因此,如果这是应用情况,那么应该使用具有先前滞后值的弹性网络模型。
长期预测:用傅立叶级数处理多重季节性
- 如前所述,像 SARIMAX 这样的传统时间序列模型无法处理多重季节性。
- 有两种有趣的时间序列预测方法,称为 BATS 和 TBATS,能够模拟具有多个季节的时间序列,请查看链接。但是,它们的计算速度很慢,而使用傅立叶级数处理多个季节性的 SARIMAX 模型的性能与 TBATS 模型一样好。
这种特征转换背后的主要驱动点是,我们不能只将 0,1,2,…22,23 小时输入到模型中,因为我们需要让模型明白,0 和 23 小时实际上与 0 小时和 1 小时一样接近。工作日和年份也一样。
添加小时、年和周周期的傅立叶循环序列
m & n 可以是离散的。基于对训练集的一些试验和测试来选择 m=n=5。每日-> T = 24,每年-> T = 365.25,每周-> T= 7
***""" as said above the k terms for each yearly, weekly and daily seasonalities could be chosen by optimizing on the AIC values..*
*but after some research on energy consumption time series k= 5 was chosen for each seasonality """***add_fourier_terms(lag_sdge, year_k= 5, week_k=5 , day_k=5)***# Visualizing the new variables on week seasonality***_ = (1-lag_sdge.loc['01-01-2014':'01-09-2014', [col **for** col **in** lag_sdge **if** col.startswith('week')]]).sum(axis = 1).plot()
可视化新傅立叶变量对周的季节性。
从上面的图中,我们可以看到离散的工作日值是如何转换成更连续的变量模式的。每小时和每年的变量也是如此。
sdgecyc.head(2)
将日、周和年变量作为傅立叶级数添加的数据
上述傅立叶级数上的 SARIMAX 模型增加了数据集
- 用先前时间的相同序列的值计算的时间序列观测值的相关性被称为序列相关,或自相关(ACF)。它用于确定 ARIMA(p,d,q)模型的移动平均(MA 或 q)项。
- 偏自相关(PACF)是时间序列中的观测值与先前时间步长的观测值之间关系的总结,其中插入的观测值之间的关系已被移除。它用于确定 ARIMA(p,d,q)模型的自回归(AR 或 p)项。
- ARIMA(p,d,q)中的 d 是使时间序列平稳所需的差分次数。
- SARIMAX 是 ARIMA 的扩展,用于处理季节性项(S)和外生变量(X)。SARIMAX 模型的基本架构由 SARIMAX(p,D,q)x(P,D,Q,s)给出,其中 P,D,Q 如上文所定义,而(P,D,Q,s)分别是 AR 参数、差异、MA 参数和周期的模型的季节性组件。s 是一个整数,表示周期(季节中的周期数),对于季度数据,通常为 4;对于月度数据,通常为 12。默认情况下没有季节性影响。
SARIMAX 模型接受许多输入,需要进行一些调整才能选择最佳的模型参数。 *pmdarima*
package 的 *auto_arima*
模块自动完成这项任务,并通过接受一些输入范围给我们提供最佳模型,类似于 gridsearchcv。
我们可以从分位数和直方图中看出拟合度很好,但不是很好。右下角的图,也称为 ACF 图,显示残差不自相关。任何自相关都意味着在模型中没有解释的残差中存在某种模式。
测试集上的 SARIMAX 预测仅显示到第一周,因为该模型无法很好地预测此后的情况。
**Error metrics for model SARIMAX(2,1,1)x(1,0,1,24) with Fourier terms 1 week ahead forecast on test set**
RMSE or Root mean squared error: 150.46
Variance score: 0.68
Mean Absolute Error: 103.46
Mean Absolute Percentage Error: 5.33 %
- 我们看到,第一周的预测非常好,但即使在第一周结束时,预测性能也会下降,置信区间值会变得更大,超出能耗值的范围。因此,SARIMAX 模型无法捕捉长期趋势,但在 1 周预测中表现良好。
- 前面没有使用(dynamic=True)为 SARIMAX 模型计算 1 小时提前预测的误差,因为我们使用滞后变量对 1 小时提前预测使用弹性网络回归得到了极好的结果,并且它比 SARIMAX 拟合速度快得多。
FB 先知
让我们尝试使用 FBProphet 解决我们的问题。FBProphet 提供了一个分解回归模型,它是可扩展的,并且可以用可解释的参数进行配置。Prophet 将预测问题框定为曲线拟合练习,而不是明确地查看时间序列内每个观察值的基于时间的相关性。与 SARIMAX 类似,我们也可以向模型中添加额外的回归项,如温度数据。(参考链接 1 、链接 2 、链接 3 )
其核心是,Prophet 是一个附加模型,包含以下组件:
y(t)=g(t)+s(t)+h(t)+ϵₜy(t)=g(t)+s(t)+h(t)+ϵₜ
***【g(t)***模型趋势
s(t) 模型季节性与傅立叶级数
h(t) 模型节假日或大型活动的影响
ϵₜ 代表不可约的误差项
- 在使用 Prophet 时,仅使用“SDGE”、“HourlyDryBulbTemperature”、“累计 _AC_kW”、“非工作 _ 工作”列,因为 Prophet 与 SARIMAX 不同,可以很好地处理多个季节。 所以,我们不需要在傅立叶项中单独传递。
- FB Prophet 可以通过假日功能传递,但是由于我们已经在‘non _ working _ working’列中捕获了假日和周末,所以我们不会将单独的假日列表传递给 Prophet。
FB Prophet 对训练集和测试集的预测
我们数据的 FB Prophet 分解模型。它似乎很好地模拟了我们数据的多重季节性——每天、每周和每年。在最底部的图中,额外的回归变量附加项包括温度、non_working_working 和 cum_AC_kW 变量。我们以波动的形式看到温度和工作日的影响,而累计 AC kW 的总体影响是能源的下降趋势(正如预期的那样,因为客户站点安装的光伏越多,对电网的需求就越低)。如果我们将这一趋势与第一行图的整体趋势相结合,那么整体趋势会下降。
**Error metrics for FB Prophet w/ auto seasonality 1 week ahead**
RMSE or Root mean squared error: 203.21
Variance score: 0.76
Mean Absolute Error: 164.76
Mean Absolute Percentage Error: 7.66 %**Error metrics for FB Prophet w/ auto seasonality 8 months ahead**
RMSE or Root mean squared error: 262.74
Variance score: 0.72
Mean Absolute Error: 201.06
Mean Absolute Percentage Error: 8.55 %
因此,尽管该模型的误差高于其他模型,但无论是一周预测还是数月预测,看起来 FB Prophet 已经很好地捕捉到了我们数据的多重季节性和趋势。
使用傅立叶项的回归模型(长期预测)
随机森林和弹性净回归(长期预测)
这里我只提一下随机森林的错误。弹性净回归在长期预测中表现不佳。
**Error metrics for Tuned Random forest with fourier terms**
RMSE or Root mean squared error: 196.99
Variance score: 0.84
Mean Absolute Error: 137.41
Mean Absolute Percentage Error: 5.73 %
- 添加傅立叶项后,随机森林表现良好。它很好地捕捉了多重季节性,并且给出了仅 5.76%的 MAPE,而基线 MAPE 为 9.23%。
- 弹性网络回归没有随机森林那样捕捉到更高的能耗值。
- Random forest 更好的性能为在数据上尝试基于树的 XGBoost 模型铺平了道路。
带傅立叶项的 XGBoost(长期预测)
- XGBoost(极限梯度增强)属于增强算法家族,其核心使用梯度增强(GBM)框架。它是一个优化的分布式梯度增强库。
- 众所周知,XGBoost 提供了比其他机器学习算法更好的解决方案。它通常不用于时间序列,特别是如果使用的基础是树木,因为很难捕捉树木的趋势,但由于我们的数据没有非常显著的趋势,而且由于它具有多个季节性(使用傅立叶级数建模)并显著依赖于外部变量,我们可以尝试 XGboost,看看它在能源消耗的时间序列数据上的表现如何。
**Error metrics for Tuned XGBoost with Fourier terms**
RMSE or Root mean squared error: 172.21
Variance score: 0.88
Mean Absolute Error: 121.19
Mean Absolute Percentage Error: 5.08 %
XGBoost 预测值和真实观察值一起绘制
XGBoost 预测值与真实观察值
XG 增强参数重要性。正如所料,温度是最重要的预测因素。
- 整个模型在大约 2 分钟内被调整和适应。
- 它似乎已经很好地学习了数据模式和多个季节。与 9.23 %的基线相比,约 8 个月的预测误差仅为 5.08%。此外,与基线的 55%相比,R2 拟合度为 88%。
集合模型(XGBoost + FB Prophet 趋势,长期预测)
为什么要考虑这个?
- 如前所述,要对时间序列数据建模,它需要是静态的。因此,理想的情况是取消数据趋势,然后将其输入 ML 模型,然后将趋势添加到预测结果中。尽管如此,由于 2014 年至 2018 年的能源消耗数据具有非常弱的趋势,并且傅立叶项很好地处理了多个季节性,因此在没有消除趋势的情况下获得了上述良好结果。
- 或者,可以使用 FB Prophet 对总体数据趋势以及 cum_AC_kW 的影响(即迄今为止的累计光伏安装量)进行建模,然后与 XGBoost 的预测进行合并。任何像 XGBoost 这样的基于树的回归模型都不能轻松地处理像 cum_AC_kW 这样的 X 变量,因为它是一个不断增长的变量,并且测试数据总是具有模型在训练集中看不到的更高的量值。
- 我已经从 FB Prophet 模型中提取了趋势和 cum_AC_kW 对能源的影响,并从我们的主数据框架中减去了这两个分量和所有傅立叶项。然后,这种消除趋势的能耗数据被传递到 XGBoost 模型,并将 XGBoost 预测结果添加回总趋势,以获得最终预测。
下面是来自我们之前训练的 FBProphet 模型的 cum_AC_kw 的趋势和效果。
来自 FBProphet 的趋势信息
我们将采用以下架构来创建我们的新模型:
XgBoost + FBProphet 趋势
XGBoost + FBProphet 预测值与实际值的比较
XGBoost + FBProphet 预测值与实际值的比较
**Error metrics for XGBoost with detrend Prophet, Fourier terms**
RMSE or Root mean squared error: 212.97
Variance score: 0.81
Mean Absolute Error: 170.77
Mean Absolute Percentage Error: 7.30 %
- 由于 FB Prophet 高估了趋势和 cum_AC_kW 对能耗的影响,特别是在时间尺度的末尾,组合模型的表现比单独的 XGBoost 差,从上面 forecast . trend+forecast . cum _ AC _ kW 的图中可以看出,该图显示了末尾的突然下降。这似乎会导致能源消耗预测不足,并影响总体结果。但是,与基线相比,该模型的表现仍然很好,由于上面讨论的原因,就稳定性而言,该模型是比单独使用 XGboost 更好的时间序列模型。
结论
- 尝试了不同的模型来预测加利福尼亚州圣地亚哥煤气和电力(SDGE)公用事业区每小时的能源需求,以兆瓦为单位。
- 能源消耗高度依赖于外部温度,并具有强烈的多重季节性——每天、每周和每年。该地区越来越多的 PV(光伏)装置( cum_AC_kW )似乎带来了能源消耗的下降趋势,因为客户设施中更多的可再生能源意味着电力公司的负荷减少。请注意,可能有其他因素导致这种下降趋势,如客户设施中的储能装置、家用和商用设备的电力效率提高、人们越来越意识到它们的使用(道德上或通过公用事业激励)等。
- 捕捉趋势的最佳方式是让模型在很长一段时间内学习趋势,这是上述所有因素的组合,也许更多。季节性是预测一个地区能源消耗的重要组成部分,因此正确预测这一部分对于提高模型的性能也至关重要(这是通过使用傅立叶级数实现的)。
- 测试集上提前一小时预测的误差项
测试集上的误差项短期提前一小时预测
- 测试集上未来几个月预测的误差项
- 具有傅立叶项的 XGBoost 模型表现非常好,预测了未来 8 个月的预测窗口。对于具有多个季节性的每小时数据,这是一个相当令人印象深刻的结果。
- 对于长期预测,大多数模型的表现优于基线持续性模型,最佳模型( XGBoost )给出了 5.08%的 MAPE,而测试集的基线误差为 9.23%。RMSE、R2 和 MAE 值也大大低于基线模型。例如,RMSE 与基线模型的差异几乎是 160 MW,这是非常显著的。为了帮助人们更好地理解这一点,燃气联合循环电厂的平均规模是500 兆瓦。
- FB Prophet 在识别数据的趋势和多重季节性方面做得非常好。它可以与 XGBoost 配合使用,从而获得更可靠的长期预测。
谢谢!如果您有任何问题或想要提出任何改进或更正的建议,请随时联系我。
节约能源,拯救地球。
Streamlit 会导致 Flask 灭绝吗?
可能对于机器学习(ML)和深度学习(DL)来说。对于其他全栈应用,大概不会!
我们还没有遇到一个基于 ML 或 DL 的 Flask 的微服务不能被重构为Streamlit服务。
挑战在于保持精简微服务的规模,只需替换 2 到 3 个基于 Flask 的微服务。
烧瓶熄灭?
我曾在 20 多家公司工作或咨询过,我很幸运能在两家大公司工作,这两家公司非常有远见,不断改进,并把创新作为公司战略的一个组成部分。
我为一家公司工作,该公司的管理层欣赏新的开源软件包的快速发展,并让我评估它们是否可能被采用。
目前,我正在将 Flask 微服务重构为 Streamlit 微服务。这些重构的原型,如果被证明在维护费用上更好(并通过我们的测试金字塔),就允许投入生产。
通过更换工作烧瓶微服,我们违反了 【不破,不修】 教义。
我认为用 Streamlit 到 代替未破的非常具有前瞻性和创新性(不过话说回来,我也很偏颇)。时间会证明这是否是一种进步。****
较早的帖子被重新访问
在早先的帖子中,我用简单的 *hello World 比较了烧瓶和流线*!举例。我们发现 Flask 需要 7 行代码,而 Streamlit 需要两行代码。****
***** [## Streamlit 会杀死 Flask 吗?
好吧,标题有点戏剧性。怎么样… Streamlit 将取代许多基于烧瓶的应用程序。
medium.com](https://medium.com/swlh/part-1-will-streamlit-kill-off-flask-5ecd75f879c8)
我将两个微服务都放在一个 Docker 容器中,其环境是 Python 3.7 。我使用 Docker 以便我们可以将烧瓶与 Streamlit、进行比较,并且我的示例可以在您的平台上轻松复制。
我们是如何做到的,我们学到了什么
medium.com](https://medium.com/rate-engineering/using-docker-containers-as-development-machines-4de8199fc662)
我们发现为烧瓶或 **Streamlit 创建 Docker 容器的努力没有差别。**任何一个都可以作为本地 web 服务或您的云提供商的生产微服务。
注意:在本文中,我没有使用生产服务器,比如 Heroku 。
概述
我们将一起旅行,创建一个比 Hello-world 更复杂的微服务!。 Flask 和 Streamlit 将比较具有输入、 pandas dataframe 显示和绘图的 web-app 实现。
我们将再次使用 Docker ,以便我们可以将 Flask 与 **Streamlit、**进行比较,并且可以在您的平台上轻松复制示例。
引入烧瓶
当建立网站时,你应该有一个健壮的框架来处理所有类型的功能。在 Python 全栈软件工程师中最受欢迎的微服务框架之一是 Flask 。
烧瓶的一些参考资料包括:
[## Flask —在 Web 上托管您的 Python 机器学习模型
了解如何使用 Python Flask 将您的机器学习模型转化为业务
medium.com](https://medium.com/fintechexplained/flask-host-your-python-machine-learning-model-on-web-b598151886d)
可以把 Flask 想象成一个软件包的集合,它可以帮助你轻松地创建一个 web 应用程序。这些组件可以被组装和进一步扩展。
这是我将记录我用 Python 编写 web 应用程序的经历的系列文章的第一篇…
blog.miguelgrinberg.com](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world-legacy) [## 处理 Flask 中的传入请求数据
在任何 web 应用程序中,您都必须处理来自用户的请求数据。Flask 和任何其他 web 框架一样,允许…
scotch.io](https://scotch.io/bar-talk/processing-incoming-request-data-in-flask)
介绍 Streamlit
Streamlit 是一个开源的 Python 框架,使我们能够开发和部署基于 web 的应用程序。
[## Streamlit -构建定制 ML 工具的最快方法。
Streamlit 是一个面向机器学习和数据科学团队的开源应用框架。在…中创建漂亮的数据应用程序
www.streamlit.io](https://www.streamlit.io) [## 如何为数据科学家使用简单的 Python 编写 Web 应用?
无需了解任何 web 框架,即可轻松将您的数据科学项目转换为酷炫的应用程序
towardsdatascience.com](/how-to-write-web-apps-using-simple-python-for-data-scientists-a227a1a01582)
Web 框架很难学。为了一些看似简单的事情,我仍然会对所有的 HTML、CSS 和 Javascript 感到困惑。
在本文中,我们会继续发现,我们需要成为一名称职的 Python 全栈 Flask 软件工程师。我们需要知道如何使用 Javascript , HTML,wt forms**,**和/或Bootstrap和/或 Flask-RESTful 和/或(非常非常多的打包机会)。
我发现烧瓶是一个健壮的框架。所有的扩展使它更加强大。更重要的是,它通过提供适合您的包扩展来适应您的编程风格。
一个好的全栈烧瓶程序员会有多年的经验。然而,一个优秀的 Streamlit 黑客(就像机器学习科学家)需要数周的经验来设计、开发和部署一个生产就绪的基于网络的仪表板。
如果我们是Python**Streamlit 机器学习或者深度学习科学家,我们做不是需要知道 Javascript 、 HTML 、 **CSS 等…、和堆栈中不同的 POST/GET URL 包。相反,我们的软件栈由 Streamlit ( 和可能是 Docker) 组成。就是这样!
欢迎 2020 堆栈
medium.com](https://medium.com/better-programming/2020-001-full-stack-pronounced-dead-355d7f78e733)
结果是专业化。前端开发人员处理 HTML、CSS 和 JavaScript。后端开发人员处理主机操作系统、HTTP 服务器和数据库。精通这两种语言的开发人员被称为全栈开发人员。
专业化是一件好事。直到它不是。一方面,这意味着团队可以并行工作以缩短开发周期。另一方面,这意味着我们必须额外努力沟通初始需求和变更单规范,否则我们将冒着失去并行工作成果的风险。
因此,拥有一个全栈开发团队,没有可区分的前端/后端组,似乎是一个好主意。
更快的满足感(再次)
在您选择的目录中完成以下操作:
[git clone https://github.com/bcottman/webApps.git](https://github.com/bcottman/webApps.git)
在另一篇博客中,我们创建了包含所有代码的/webApps
目录树。在不同于上次的不同父目录中使用git clone
,以获得更新(或者使用相同的父目录并覆盖您对原始代码所做的任何更改)。或者…我将让您决定如何管理您的文件系统。
1.输入和数据帧显示的 Flask 实现
对于我们的 Flask 微服务的第一个实现,我将显示一个熊猫数据帧。
来自浏览器 URL 的dataset
参数被返回并分配给运行在服务器上的dataviewer1.py
中的dataset_name
。这是服务器代码中非常基本的变量设置(即[http://localhost:5000/show?dataset=Airq](http://localhost:5000/show?dataset=Airq).)
)。.)
import pandas as pd
from pydataset import datafrom flask import Flaskapp = Flask(__name__)
app.debug = True@app.route('/show', methods=['GET'])
def dataViewer_1():
dataset_name = request.args.get('dataset')
if dataset_name == None:
dataset_name = 'Aids2' #default if no arg passed if type(data(dataset_name)) != pd.core.frame.DataFrame:
return('Bad dataset name:{}'.format(dataset_name)) return data(dataset_name).to_html(header="true", table_id="table")if __name__ == "__main__":
app.run()################# requirements.txt file
Flask>=1.1.1
pandas
pydataset
输出[=>]:
使用 df.to_html 部分显示 Airq 数据集
2.用 HTML 实现熊猫数据帧显示
感谢https://stack overflow . com/questions/22180993/pandas-data frame-display-on-a-网页/22233851 ,免去了我学习 HTML 和 Bootstrap (毕竟我是一个没有耐心的 ML 科学家)。 Bootstrap 是烧瓶提供的大量扩展之一。
## dataViewer-2.py
import pandas as pd
from pydataset import data
from flask import Flask, request, render_template
from flask_bootstrap import Bootstrapapp = Flask(__name__)
app.debug = True
Bootstrap(app)@app.route('/show', methods=['GET'])
def dataViewer_2():
dataset_name = request.args.get('dataset')
if dataset_name == None:
dataset_name = 'Aids2' if type(data(dataset_name)) != pd.core.frame.DataFrame:
return('Bad dataset name:{}'.format(dataset_name)) df = data(dataset_name) return render_template("df.html", name=dataset_name, data=df.to_html())if __name__ == "__main__":
app.run()### df.html{% extends "bootstrap/base.html" %}
{% block content %}
<h1>{{name}}</h1>
{{data | safe}}
{% endblock %}################# requirements.txt file
Flask>=1.1.1
pandas
pydataset
flask_bootstrapoutput[=>]:
使用 HTML 和 bootstrap/base.html 部分显示 Airq 数据集显示
使用我们自己的 HTML 模板,我们可以定制网页布局。
我将留给读者用 CSS 或任何你可能使用的 Flask 扩展来进一步定制。
3.抵御 CSRF 攻击的 Flask 实现
我将使用 wtform 包来抵御跨站请求伪造 (CSRF)攻击。我们将使用WTForm app.config['SECRET_KEY'].
您控制台中的命令是:
$ python -c 'import os; print(os.urandom(16))'
我将生成的密钥放在我的dataView-3.py
文件的顶部。
from flask import Flaskapp = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = '\xbfx\x02\xf7\xeao\ro\rp&Q\xa1\xbdV\xd9'#<more code...>
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
我没有在@app.route
I .结果是:
输出[=>]:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again
3.将所有这些与 pandas 数据帧图的 Flask 实现放在一起。
# !/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Bruce_H_Cottman"
__license__ = "MIT License"
import pandas as pd
from pydataset import data
from flask import Flask, request, render_template, session
from flask_bootstrap import Bootstrap
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource
from bokeh.embed import components
from bokeh.resources import INLINE
from bokeh.util.string import encode_utf8
from bokeh.palettes import Spectral11
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = '\xbfx\x02\xf7\xeao\ro\rp&Q\xa1\xbdV\xd9'
Bootstrap(app)
@app.route('/show', methods=['GET'])
def dataViewer():
dataset_name = request.args.get('dataset')
if dataset_name == None:
dataset_name = 'Aids2'
if type(data(dataset_name)) != pd.core.frame.DataFrame:
return('Bad dataset name:{}'.format(dataset_name))
df = data(dataset_name)
session['dataset_name'] = dataset_name
return render_template("df.html", name=dataset_name, data=df.to_html())
@app.route('/plot')
def dataViewer_4():
dataset_name = session['dataset_name']
if dataset_name == None:
dataset_name = 'Aids2'
if type(data(dataset_name)) != pd.core.frame.DataFrame:
return('Bad dataset name:{}'.format(dataset_name))
if type(data(dataset_name)) != pd.core.frame.DataFrame:
return('Bad dataset name:{}'.format(dataset_name))
# get dframe by name
df = data(dataset_name)
# Create a ColumnDataSource object
bp_df = ColumnDataSource(df)
# Create plot as a bokeh.figure object
plot = Figure(height=400,
width=400,
title=dataset_name,
)
x_ax = [str(i) for i in range(df.shape[0])]
palette_ = Spectral11[0:len(df.columns)]
for n, cname in enumerate(df.columns):
plot.line(x=x_ax, y=list(df[cname].values)
, color=palette_[n]
, legend='line')
# grab the static resources
js_resources = INLINE.render_js()
css_resources = INLINE.render_css()
# render template
script, div = components(plot)
html = render_template(
'index_.html',
plot_script=script,
plot_div=div,
js_resources=js_resources,
css_resources=css_resources,
)
return encode_utf8(html)
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')### df.html{% extends "bootstrap/base.html" %}
{% block content %}
<h1>{{name}}</h1>
{{data | safe}}
{% endblock %}################# index_.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Embed Demo</title>
{{ js_resources|indent(4)|safe }}
{{ css_resources|indent(4)|safe }}
{{ plot_script|indent(4)|safe }}
</head>
<body>
{{ plot_div|indent(4)|safe }}
</body>
</html>################# requirements.txt file
Flask>=1.1.1
flask_bootstrap
pandas
bokeh
pydataset
输出[=>]:
散景数据集 Airq 图嵌入烧瓶
我使用工具栏中的散景的缩放来放大数据集中的线条。我用散景创建很棒的交互式图形,避免直接使用 Javascript
简化dataViewer.py
的实施
在这个dataViewer.py,
的实现中,我将使用 Streamlit 。我真的不需要把这个实现分成几个步骤,因为这个dataViewer.py
只是 python ,一些 Streamlit 函数和 pandas 包。
import streamlit as st
import pandas as pd
from pydataset import datadf_data = data().sort_values('dataset_id').reset_index(drop=True)
st.dataframe(df_data) #choicesoption = st.selectbox(
'select a dataset do you like best?', df_data['dataset_id'])dataset = data(option)if isinstance(dataset, (pd.core.frame.DataFrame, pd.core.series.Series)):
st.dataframe(dataset)
st.line_chart(dataset)
输出[=>]:
显示和绘制 airq 的简化选择。
Streamlit 实现需要引用包的 Python 代码的 10 个语句行: streamlit 、 pandas 和 pydataset 。
包 pydataset 包含超过 750 个数据集。
在您的实现中,dataset_ids
(数据集名称)在显示顶部的描述框和下拉选择框中按字母升序排序。
请注意当您单击选择框时,它是如何展开的。
您选择的数据集显示在从上往下数的第三个框中。 Streamlit 的一个特点是数据集的呈现是动态的,每一列的值都是升序或降序排序。此外,如果显示框的宽度或高度不足以显示所有的列或行,则它会在水平轴或垂直轴上滚动。
在最后一个框中,我们可以看到数据集的折线图。每行对应于数据集的一个数字列(要素)。
在这个 Streamlit 微服务的实际现场实现中(与这里显示的静态截图相反),您可以水平或垂直拖动来改变水平或垂直比例。您也可以放大和缩小来更改大小。
在 Streamlit 的掩护下,在 Javascript 、 HTML 、 **CSS、JSON、**的网页的选定端口上输出,并进行 web 通信。虽然我已经放弃了一些底层控制,但我已经获得了一个将 python 脚本转换成全功能 web 应用程序的强大机制。
在许多方面,我们在这里比较的东西类似于使用第四代高级语言( Streamlit )来机器汇编( Flask、 Javascript 、 HTML 、 CSS、JSON、post/get 等)。).
我将把它作为一个编程练习,让您扩展这个实现,为不同图表的选择提供一个选择框。
流线型dataView.py in Docker Container
Dockerfile…
**FROM** python:3.7
#EXPOSE 8501
**WORKDIR /**dataViewer
**COPY** requirements.txt .**/**requirements.txt
**RUN** pip3 install **-**r requirements.txt
**COPY** . .
**CMD** streamlit run dataViewer.py
要构建容器…
$ docker build -f Dockerfile -t dataviewer-streamlit:latest ..
其中requirements.txt
是…
streamlit
pandas
pydataset
要运行容器…
$ docker run -p 8501:8501 dataviewer-streamlit &
的码头集装箱与dataViewer.py and HelloWorld.py are
的的区别在于requirements.txt.
中的集装箱名称和条目
Docker 的问题
如果你得到任何这些信息,你需要启动或重启 Docker 。我在一台 Mac 上,所以我只是重启Docker桌面。
Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Is the docker daemon running?.**<or>**ERRO[0050] error waiting for container: EOF
通过在:5000.
港运行不同的集装箱,你会遇到这个很难理解的问题
docker: Error response from daemon: pull access denied for 5000, repository does not exist or may require ‘docker login’: denied: requested access to the resource is denied.
修复如下所示,并在您的终端窗口中键入。(我确定还有更好的。)
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)# remove port assignments
- 找出占用您想要释放的端口号(例如
5000
)的进程 ID (PID)。(为您的操作系统确定合适的命令)
sudo lsof -i :5000
第二步。使用其<PID>.
终止当前正在使用该端口的进程
sudo kill -9 <PID0> ... <PIDn>
摘要
在本文中,我们比较了基于 Flask- 的和基于 Streamlit- 的web 应用程序,使用了一个具有输入、交互式数据集显示和数据集数值列的折线图的示例。我们发现烧瓶需要大约 100 行代码而流线型需要十行代码。
我们发现创建烧瓶微服务至少需要知道如何使用 HTML 和 CSS 。
我们可以将两个微服务放在一个 Docker 容器中,该容器的环境是一个 Python rc(发布候选)。其中任何一个都可以用作本地 web 服务或您的云提供商的生产微服务。
参考
利用 Airbnb 数据深入了解 Streamlit
towardsdatascience.com](/streamlit-101-an-in-depth-introduction-fc8aad9492f2) [## 使用 Streamlit 将您的数据科学脚本转变为网站
展示你的数据科学/机器学习实验的发现可能很困难。而在过去,一个…
gilberttanner.com](https://gilberttanner.com/blog/turn-your-data-science-script-into-websites-with-streamlit) [## 如何为数据科学家使用简单的 Python 编写 Web 应用?
无需了解任何 web 框架,即可轻松将您的数据科学项目转换为酷炫的应用程序
towardsdatascience.co](/how-to-write-web-apps-using-simple-python-for-data-scientists-a227a1a01582) [## 烧瓶大型教程第十九部分:Docker 容器上的部署
这是 Flask 大型教程系列的第 19 部分,其中我将把微博部署到…
blog.miguelgrinberg.com](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xix-deployment-on-docker-containers) [## Python 绘图 API:通过 flask API 展示您的科学 python 绘图
在我作为数据科学家的日常工作中,我经常需要将相对复杂的情节集成到后台…
towardsdatascience.com](/python-plotting-api-expose-your-scientific-python-plots-through-a-flask-api-31ec7555c4a8)
展望未来
在第 3 部分中,我们将一起开发一个更复杂的微服务,它有一个完整的机器学习管道。在第 3 部分中,我们将通过使用 Streamlit 来避免成为全栈 Flask 开发者。
在以后的文章中,我将比较 Dash 和 Streamlit。
[## 部署
若要共享 Dash 应用程序,您需要
dash.plot.ly](https://dash.plot.ly/deployment)
Dash Enterprise 是 Plotly 的商业产品,用于在您公司的服务器或 AWS、谷歌云或 Azure 上部署 Dash 应用程序。它提供了一个企业范围的 Dash 应用程序门户、基于 git 的简单部署、自动 URL 命名空间、内置 SSL 支持、LDAP 认证等等。
公平地说,脸书等人还不能将 Streamlit 用于他们面向公众的网站,然而,正如我们将在第 3 部分中看到的,作为一个大部分时间都在用 Python 库编码的机器学习科学家, Streamlit 提供了一个简单的 Python API。
我能够在 4 天内设计、编码、测试、部署一个基于 T21 的 ML 仪表板。我的首席技术官把它放在了他的桌面上。(好吧,我是应他的要求做的。)他喜欢。
我们的全栈团队使用 Flask 需要大约三周时间(使用 Django 需要一周时间)来将新的 ML 应用程序投入生产。
Streamlit 不需要你学习几个新的范式和语言,比如 HTML 、 CSS 、 JSON 、 JavaScript。它不需要你知道使用什么烧瓶分机。我应该使用自举还是 WTForms 还是其他什么或者它们的组合?
我已经在烧瓶中编程几个月了。我现在理解了一个全栈程序员在生产一个基于烧瓶的微服务时所使用的大量知识。我敬畏他们。
如果你是一个 Streamlit 黑客(像我一样),你需要而不是知道 Javascript 、 HTML 、 CSS、JSON ,以及栈中不同的 POST/GET URL 包。相反,你的筹码将由 Streamlit ( 和也许是 Docker) 组成。就是这样!
我希望你发现 Streamlit 上的这篇博客很有用。我期待着在这方面写一篇博客,我希望你也是!*******