如何隐藏/加密/混淆任何 Python 程序
约翰·萨尔维诺在 Unsplash 上拍摄的照片
出于混淆的目的,我们将使用一个名为 pyarmor 的 python 包。
我们有时可能会面临这样的情况,由于显而易见的原因,我们需要直接向客户提供代码,但是这样做,我们将失去对代码的控制。在这种情况下,我们可能会加密代码来保护它,保留控制权,并添加一些回退条件来控制我们的依赖性,就像我们提供的代码只能在一定时间内使用一样。
为了解决上述问题,我将演示一个具有上述功能的简单函数。我们将使用名为“pyarmor”的 python 库。
目录:
1.创建简单的函数
对于我们的实现,我将使用带有推理函数定义的 utils.py,稍后将对它进行加密。
推理函数非常简单,不需要任何解释。
将它保存在一个文件夹中。
2.用 pyarmor 加密它
现在,我们将通过运行以下两个命令来加密它。
pip3 install pyarmor # pip/pip3# Syntax : pyarmor obfuscate --restrict=0 <filename>pyarmor obfuscate --restrict=0 utils.py
1.安装软件包
2.加密 utils.py 函数
现在,如果您看到包含实际 utils.py 文件的文件夹,将会创建一个名为 dist 的新的子文件夹。
在左边的 utils.py(原始文件)和右边的 dist 文件夹中,我们有(加密的)utils.py 文件
让我们看看里面的东西。
dist 子文件夹中的加密 utils.py 文件
3.导入我们的函数/推理
完成后,到现在为止,让我们尝试将这个加密的 utils.py 导入到一个名为 main.py 的新 python 文件中,这个文件是在 dist 文件夹中创建的。
pyarmor 负责在运行时解密 utils.py 所需的密钥,这些密钥存在于 pytransform 文件夹中,因此我们的代码在别人看来完全不可读。
但是如果您希望修改原始的 utils.py 代码,那么您必须从第 1 步开始,并遵循相同的步骤来完成。
main.py 文件中内容,
# Import the inference function definition inside the utils.py filefrom utils import inference_ = inference(name="Balakrishna")
在运行上面的代码行时,我们得到了基于 utils.py 文件配置的输出。
最终输出。
结论:
这仅仅是开始。如果你想真正保护和销售你的代码,考虑添加一个嵌入虚拟日期的实际许可证。超过该日期后,它将不再运行该程序,并抛出一个许可证到期错误。幸运的是 pyarmor 也有。我在下面的参考资料中添加了与许可相关的文档。您甚至可以设置一个条款,在许可过期后自动发送电子邮件,并且您可以使用 python 中的 SMTP 服务与您的客户端取得联系。
如果你有很多。py 文件导入或作为依赖项,必须使用—递归命令行而不是— restrict 命令,代码才能工作。
此外,这种实现可以在 Windows 和基于 Ubuntu 的操作系统中完成。
参考资料:
完整的代码实现可从以下网址获得:
@GitHub
https://github.com/bala-codes/Code-Encryption-Obfuscation---Python
皮铠甲:
- 【https://pypi.org/project/pyarmor/
- https://github.com/dashingsoft/pyarmor
- https://pyarmor.readthedocs.io/en/latest/
- https://py armor . readthe docs . io/en/latest/usage . html # generating-license-for-obfuscated-scripts
- https://docs.python.org/3/library/smtplib.html
如何在 Github 上托管 Jupyter 笔记本幻灯片
制作幻灯片和在 Github 上托管 html 文件的工作量最小
凯莉·西克玛在 Unsplash 上的照片
点击这里查看这篇文章的幻灯片:https://sophiamyang.github.io/slides_github_pages/.
本文分为两个部分:
- 如何将您的 Jupyter 笔记本变成幻灯片并输出到 html 文件。
- 如何在 Github 上托管 html 文件?
Jupyter 笔记本幻灯片
首先,我们创建一个新环境slideshow
,安装一个 Jupyter 笔记本扩展 RISE,并启动 Jupyter 笔记本:
conda create -n slideshow -c conda-forge python=3.9 rise
conda activate slideshow
jupyter notebook
然后像往常一样创建一个 Jupyter 笔记本文件:
- 单击视图→工具栏→幻灯片显示,定义每个单元格的幻灯片类型。
- RISE 在工具栏的右上角创建了一个按钮“进入/退出实时显示幻灯片”。
- 单击此工具栏,您将能够以幻灯片形式查看您的笔记本。
幻灯片到 html
如果您有代码单元格并希望显示所有代码单元格,请使用:
jupyter nbconvert github_page_example.ipynb --to slides --stdout > index.html
如果您想要隐藏所有代码单元格:
jupyter nbconvert github_page_example.ipynb --no-input --no-prompt --to slides --stdout > index.html
如果您想隐藏某些单元格的代码,我们可以通过视图→单元格工具栏→标签为这些代码单元格添加标签。这里我使用标签“移除输入”。
jupyter nbconvert github_page_example.ipynb --TagRemovePreprocessor.remove_input_tags "remove_input" --to slides --stdout > index.html
Github 上的主机 html 文件
要在 Github 页面上发布一个 html 文件,我们需要推送到gh-pages
分支。
使用 gh-pages 分支在本地创建新的 git repo:
git init
git checkout -b gh-pages
git add index.html
git commit
去 Github 创建一个新的空 repo,然后把我们的文件推送到gh-pages
分支。
git remote add origin git**@github**.com:YOUR_USER_NAME/YOUR_REPO_NAME.git
git branch -M gh-pages
git push -u origin gh-pages
现在你可以在 Github 页面上看到这个幻灯片:https://YOUR _ USER _ NAME . Github . io/YOUR _ REPO _ NAME
看看我的:https://sophiamyang.github.io/slides_github_pages/.
参考:
https://rise.readthedocs.io/en/stable/
作者索菲亚·杨 2021 年 7 月 18 日
如何用 Apache 虚拟主机托管多个网站
使用 Ubuntu 20.04 托管网站,包括子域
照片由克里斯蒂娜@ wocintechchat.com在 Unsplash 上拍摄
介绍
Apache 的虚拟主机可以在一台服务器上运行多个网站。在这篇文章中,你将学习如何托管多个网站,包括子域。
我的 Ubuntu 20.04 服务器在/etc/apache2/sites-available
目录下有一些文件。我们将在该目录中创建更多文件,以创建多个虚拟主机。
$ ls /etc/apache2/sites-available
000-default.conf 000-default-le-ssl.conf default-ssl.conf
000-default.conf.dpkg-dist
创建新的虚拟主机
让我们为example.com
创建一个虚拟主机。(你需要把 example.com 改成你的域名。我们把文件存放在/var/www/example.com/public_html
目录下。如果你想使用 Git 部署你的网站,请参考本文“ 如何创建一个 Git Hook 推送到你的服务器和 Github Repo ”。
步骤 1 —创建一个配置文件
复制000-default.com.conf
在/etc/apache2/sites-available
新建一个文件:
$ cd /etc/apache2/sites-available
$ sudo cp 000-default.com.conf example.com.conf
步骤 2-修改新的配置文件
在example.com.conf
中:
第 2 行:为 ServerAdmin 添加您的电子邮件。第 4 行:将 www 添加到 ServerAlias 的域名中。
第 5 行& 6:添加 DocumentRoot 的文件目录。
步骤 3 —启用虚拟主机
a2ensite
启用apache2
配置中的指定站点。它在/etc/apache2/sites-enabled
中创建一个符号链接(不支持站点)。
$ sudo a2ensite example.com.conf
上面的命令将在/etc/apache2/sites-enabled
目录中创建一个符号链接example.com.conf
。
步骤 4—启用 SSL
$ sudo certbot --apache -d example.com -d www.example.com
这将在/etc/apache2/sites-available
中查找相同的文件名。如果找不到,它会要求您选择 conf 文件。
这将创建example.com-le-ssh.conf
。
步骤 5—重新启动 apache
$ sudo systemctl restart apache2
如果您的 DNS 配置正确,您应该能够看到您的域。
子域
我们将为一个子域创建一个虚拟主机。该过程与上一节相同。
我们将 web 文件存储在/var/www/newsletter.example.com/public_html
中。
我们复制000-default.com.conf
来创建一个新文件newsletter.example.com.conf
。
$ sudo cp 000-default.com.conf newsletter.example.com.conf
编辑newsletter.example.com.conf
:
与前一个示例的不同之处在于向 ServerName、ServerAlias、DocumentRoot 和 Directory 添加了一个子域。
启用虚拟主机和 SSL
让我们为这个子域启用虚拟主机和 SSL。
sudo a2ensite newsletter.example.com.conf
sudo certbot --apache -d newsletter.example.com -d www.newsletter.example.com
向 DNS 服务添加记录
如果您使用的是 namecheap.com,请转到域列表>高级 DNS 并添加新记录。
可能需要 24-48 小时。
重新启动 apache
$ sudo systemctl restart apache2
确认您的 DNS
使用https://www.whatsmydns.net/检查您的 DNS。如果您配置正确,它会显示您的 IP 地址。
etc/apache2/sites-available
现在您在sites-available
目录中有了以下文件。
ls /etc/apache2/sites-available/
000-default.conf 000-default.conf.dpkg-dist
default-ssl.conf 000-default-le-ssl.conf
example.com.conf example.com-le-ssl.conf
newsletter.example.com.conf newsletter.example.com-le-ssl.conf
xxxx-le-ssl.conf
文件用于您的 SSL。
如何删除虚拟主机
使用a2dissite
命令禁用现有的虚拟主机。
$ sudo a2dissite 000-default.conf
$ sudo a2dissite 000-default-le-ssl.conf
在这里,您正在禁用000-default.conf
及其 SSL 虚拟主机。
结论
现在您知道了如何在您的服务器上创建虚拟主机。这允许你在一台服务器上托管尽可能多的网站。
通过 成为 会员,获得媒体上所有故事的访问权限。
参考
如何在 7 分钟或更短时间内找到一位聪明的数据科学家
区分聪明的数据科学家的关键特征
聪明和聪明是有区别的。一般来说,数据科学家和机器学习从业者都很聪明,这意味着他们拥有一般的技术智能,这使他们在自己的行业内令人生畏。
另一方面,聪明超越了智力。为了指导行动和决策,聪明的数据科学家在互动过程中结合情感线索、行为模式、环境知识等。
他们讲述引人入胜的故事
讲故事,这个短语概括了一个模范沟通者所需要的特征和技能。聪明的数据科学家是有效的沟通者。他们利用讲故事的艺术来完成既定的目标或传达想法。
对沟通等软技能的要求可能被视为次于编程、测试研究或分析技能等技能。尽管如此,聪明的数据科学家认识到与客户、团队成员、顾客和经理培养稳定关系的重要性。讲故事是一种行之有效的方法。
我用讲故事的方式解释了特定技术决策对项目交付、应用程序实现和维护的影响。
例如,使用个人轶事来解释为什么利用苹果发布的深度学习模型有利于使用第三方维护的开源解决方案。
在上面的场景中,利用讲故事比简单地陈述一个显而易见的事实有更好的结果,即苹果等知名品牌发布的软件有很高的可靠性。有时,在观点中加入个性化的元素会引起接受者的共鸣。
此外,讲故事为非技术人员解释机器学习项目中利用的技术和工具增加了人性化的成分。期望普通人理解我们与机器学习实践者同事使用的技术术语几乎是不合理的。
史蒂夫·乔布斯是一个很会讲故事的人,他讲故事的技巧给简单的硬件赋予了灵魂。聪明的数据科学家对数据也是如此。将从数据中收集的情报与人性化的语言注释结合起来,你将获得左右观众的能力。
讲故事不一定要用语言。书面笔记、文章或文件能以一种特殊的方式传达信息和思想,这是口头形式所不能做到的。
以媒体上的数据科学家为例。我遇到过一些人,他们在各自的专业领域表现出色,但他们会采取额外的措施,通过媒体文章来交流他们的经验、知识和专业技能。
关于讲故事,有太多的信息可以分享,以及在技术角色中应用时实现的好处。但是我将把它留到以后的文章中。
提高讲故事和沟通技巧的步骤:
- 尽可能多的公开演讲。
- 多读书。
- 向其他优秀的演说家学习。
他们不会忽视影响
作为一名计算机视觉工程师,我试图认识到自己对公司其他部分的影响。我承认,我集成到我们旗舰应用程序中的解决方案的速度和准确性是决定我们目标受众中产品采用率的因素。
数据科学家有影响,我们产生的输出也有影响。以下是几个影响类别供您考虑:
- 业务影响:数据相关活动是技术驱动型公司的核心。数据科学家执行的操作加强了典型业务运营、决策和研究的流程。
- 产品影响:解决方案或服务驱动的业务在持续交付给客户的基础上蓬勃发展。负责特性实现、维护和升级的 DS 和 ML 实践者共同承担交付可用和功能性产品的责任。
- 社区影响:在人工智能和数据驱动技术融入社会的当今时代,数据科学家的工作影响着团体和社区的活动。
- 道德影响:我们的工作对个人的生活有直接或间接的影响。ML 从业者有责任考虑技术和应用开发的道德性。
一个普通的数据科学家通常关心技术影响;具体来说,实现的技术、功能和代码对期望的结果有影响。仅从技术影响的角度来看并没有坏处。但是聪明的数据科学家会更进一步,考虑其他影响。
考虑到数据科学工作的成果可能产生的其他形式的影响,支持了你作为机器学习专业人员进行长期思考的印象。
作为技术团队和项目涉众之间的中介,遇到考虑技术问题之外的因素的 ML 工程师是令人耳目一新的。更具体地说,我对那些在应用程序交付和成本的时间估算中考虑先发优势的团队印象深刻。
改善影响考虑的步骤:
- 问正确的问题— 稍后会详细介绍。
- 设身处地为项目中的其他人着想:客户、最终用户、开发者、设计师等等。
- 感同身受。
他们真的会听
佛朗哥·安东尼奥·乔瓦内拉在 Unsplash 上拍摄的照片
倾听的能力是一门失传的艺术。
这听起来可能有些矛盾,但是成为一个有效的沟通者的第一步是成为一个细致的倾听者。
之前,我写了一篇文章介绍典型机器学习项目中的常见阶段。第一步包括倾听客户、顾客或用户提出的问题。
在我作为一名 web 开发人员的早期,我并不是有目的地倾听客户的问题,而是渴望越过问题定义和分析阶段,直接决定是使用 Angular、React 还是 Vue 来构建解决方案。这导致我错过了客户表达的关键痛点,结果,我实现了客户不需要的解决方案。
这里学到的经验是,积极倾听项目涉众的痛点可以为您节省几个月的时间来实现解决错误问题的解决方案。
热情的 ML 实践者被库、神经网络架构和工具的细节所困扰,以构建提议的解决方案。这不是任何人的错,人工智能领域的本质需要技术驱动的个人。
但最重要的是,作为技术的倡导者,我们要提醒自己技术本身的目的,那就是减轻我们生活中的手工和费力的责任。
提高听力的步骤:
- 不要不必要的打断。
- 对不同的想法和思维持开放态度。
- 排除杂念。
- 观察身体语言暗示。
他们问了正确的问题
科学是一个关注发现和利用知识的领域,在这个领域,成功或者至少是进步是由对世界本质的质疑所引导的**。有时,我们会忘记数据科学中的“科学”部分。**
数据科学学科要求个人具有好奇的天性。DS/ML 从业者会询问同事、客户,当然还有数据。了解如何以及何时问正确的问题是通往成功的道路。
重要的不是你问的问题的数量,而是什么创造了有影响力和有成效的参与,这取决于所问问题的质量。
客户参与
积极倾听的副产品是确定要问的正确问题,以及问这些问题的正确时间。
数据科学家经常与客户接触,或者产品用户通过询问适当的问题获得丰富的知识。虽然向潜在客户炫耀技术专长和成就很诱人,但从对方那里获取信息更为重要。
通过问开放式问题和避免诱导性问题,可以有效地从客户那里提取有价值的信息。
问题 1:什么样的见解对你企业的成功有价值?
问题 2:您希望我们提取数据洞察,指出您的客户及其位置之间的相关性吗?
上面的问题 1 是一个开放式问题的例子。它允许客户提供关于被认为重要和高价值的因素的信息。
另一方面,问题二允许二元答案,要么是,要么不是。客户没有机会详细说明,更糟糕的是,假设是由提问的数据科学家做出的。
技术约定
最近,我参与了一个机器学习团队,对于原始数据特征提取应该在应用程序管道中的什么位置发生了意见冲突。
而不是强迫他们接受我的观点,炫耀我的专业知识。我选择问正确的问题。通过非利己主义的知识交流,双方都积极地离开了约定。
在技术项目中提出正确的问题提供了一个向他人学习的机会,并广泛地与同事建立人际关系。
问正确问题的步骤:
- 问一些开放式的后续问题,让对方能够详细阐述具体的话题。
- 充分阐述问题。
结论
数据科学家形形色色。根据我的经验,我发现成功的数据科学家能够在技术智能和情感智能之间找到平衡。
请在评论区分享你认为一个聪明的机器学习实践者应该具备的品质。
感谢阅读
</5-soft-skills-you-need-as-a-machine-learning-engineer-and-why-41ef6854cef6>
想要更多吗?
- 订阅 在我发表文章时得到通知
- 成为推荐媒介会员,支持我的写作
- 通过 LinkedIn 联系我
- 在 奥莱利 跟我学
如何使用 scikit-learn 实现人工智能
入门
这篇介绍 power Python 工具的文章将让你很快应用人工智能
数据科学家将人工智能(AI)用于一系列强大的用途。它现在运行控制系统,减少建筑能耗,它为购买衣服或观看节目提供建议,它有助于改善农业实践和我们可以种植的食物量,有一天它甚至可能为我们驾驶汽车。知道如何使用这些工具将使你有能力解决下一代社会的技术挑战。
幸运的是,对于已经熟悉 Python 和数据分析的人来说,开始使用人工智能并不那么具有挑战性。您可以利用强大的 scikit-learn 包来为您完成大部分艰苦的工作。
什么是 scikit-learn?
scikit-learn 是一个 python 包,旨在促进机器学习和人工智能算法的使用。它包括用于分类、回归和聚类的算法,包括流行的随机森林和梯度推进算法。这个软件包被设计成可以很容易地与常见的科学软件包 numpy 和 scipy 接口。尽管不是专门为熊猫设计的,但它也能很好地与熊猫互动。
scikit-learn 还包括一些有用的工具,有助于使用机器学习算法。开发准确预测系统行为的机器学习管道需要将数据分成训练和测试集,以及对算法进行评分以确定它们的运行情况,并确保模型既不过度拟合也不欠拟合。scikit-learn 界面包括执行所有这些任务的工具。
scikit-learn 算法是如何工作的?
开发和测试 scikit-learn 算法可以分三个一般步骤进行。它们是:
- 使用描述需要模型预测的现象的现有数据集来训练模型。
- 在另一个现有数据集上测试该模型,以确保其性能良好。
- 使用模型来预测项目所需的现象。
scikit-learn 应用程序编程接口(API)提供了通过单个函数调用来执行这些步骤的命令。所有的 scikit-learn 算法在这个过程中使用相同的函数调用,所以如果你学会了一个,你就学会了所有的。
训练 scikit-learn 算法的函数调用是*。fit()* 。来训练你称为。函数,并将训练数据集的两个组件传递给它。这两个组件是 x 数据集,提供描述数据集的特征的数据,以及 y 数据,提供描述系统的目标的数据(特征和目标是机器学习术语,本质上表示 x 和 y 数据)。然后,该算法创建由所选算法和模型参数确定的数学模型,该模型尽可能匹配所提供的训练数据。然后,它将参数存储在模型中,允许您根据项目需要调用模型的 fit 版本。
测试模型拟合度的函数是*。score()* 。要使用该函数,您需要再次调用该函数,并传递表示特征的 x 数据集和表示目标的相应 y 数据集。与定型模型时相比,使用不同的数据集(称为测试数据集)非常重要。当对训练数据评分时,模型很可能得分很高,因为它在数学上被迫与该数据集匹配。真正的测试是模型在不同数据集上的表现,这是测试数据集的目的。调用时。score() 函数 scikit-learn 将返回 r 值,说明模型使用提供的 x 数据集预测提供的 y 数据集的效果如何。
您可以使用 scikit-learns 预测给定输入的系统输出。预测()函数。重要的是,您只能在拟合模型后执行此操作。拟合是指如何调整模型以匹配数据集,因此如果不首先进行拟合,模型将不会提供有价值的预测。一旦模型合适,您就可以将一个 x 数据集传递给。predict() 函数,它将返回模型预测的 y 数据集。通过这种方式,你可以预测一个系统未来的行为。
这三个函数构成了 scikit-learn API 的核心,对您将人工智能应用于技术问题大有帮助。
如何创建培训和测试数据集?
创建单独的训练和测试数据集是训练人工智能模型的关键组成部分。如果不这样做,我们就无法创建一个与我们试图预测的系统相匹配的模型,也无法验证其预测的准确性。幸运的是,scikit-learn 再次提供了一个有用的工具来促进这一过程。那个工具就是train _ test _ split()函数。
train_test_split() 做的和听起来一样。它将提供的数据集分成训练数据集和测试数据集。您可以使用它来创建所需的数据集,以确保您的模型正确预测您正在研究的系统。您向 train_test_split() 提供一个数据集,它提供您需要的训练和测试数据集。然后,它返回拆分为定型数据集和测试数据集的数据集,您可以使用这些数据集来开发您的模型。
使用 train_test_split() 的时候有几个需要注意的地方。首先, train_test_split() 本质上是随机的。这意味着,如果使用相同的输入数据多次运行,train_test_split()将不会返回相同的训练和测试数据集。如果您想要测试模型准确性的可变性,这可能是好的,但是如果您想要在模型上重复使用相同的数据集,这也可能是不好的。为了确保每次都能得到相同的结果,您可以使用 random_state 参数。随机状态设置将强制它在每次运行时使用相同的随机化种子,并提供相同的数据集分割。当使用 random_state 时,习惯上将其设置为 42,这可能是对《银河系漫游指南》的幽默回应,而不是出于任何技术原因。
这两者是如何结合在一起的?
总之,这些工具创建了一个简化的界面来创建和使用 scikit-learn 工具。让我们以 scikit-learn 的 线性回归 模型为例来讨论一下。
要实现这一过程,我们必须首先导入所需的工具。它们包括 scikit-learn 模型、 train_test_split() 函数和用于数据分析过程的 pandas。这些函数按如下方式导入:
from scikit-learn.linear_model import LinearRegression
from scikit-learn.model_selection import train_test_split
import pandas as pd
然后我们可以读入一个数据集,这样它就可以用于训练和测试模型。我创建了一个真实数据集,展示热泵热水器(HPWHs)的性能作为其运行条件的函数,专门帮助人们学习数据科学和工程技能。假设您已经下载了数据集,并将其保存在与脚本相同的文件夹中,您可以使用下面的代码行打开它。如果没有,您可以根据需要调整这些步骤,在您喜欢的任何数据集上进行练习。
data = pd.read_csv('COP_HPWH_f_Tamb&Tavg.csv', index_col = 0)
下一步是将数据集分成 X 和 y 数据。为此,我们创建新的数据框,指定代表要素和目标的数据集列。在 HPWHs 的情况下,特征是水箱温度和环境温度,而目标是电力消耗。数据集包含八列,显示储水箱中八个不同深度的水温,每一列名为“Tx (deg F)”,其中 x 是代表测量位置的数字。它还包含一个列,显示热水器周围空间的测量环境温度,名为“T_Amb(华氏度)”。最后,数据集包含一个存储电力消耗数据的列,称为“P_Elec (W)”。在这种情况下,过滤我们的数据集也很重要,这样我们只在耗电时使用数据。如果我们跳过这一步,我们将把非线性引入线性模型,这将使模型失败。
我们可以使用以下代码完成所有这些步骤:
# Filter the data to only include points where power > 0
data = data[data['P_Elec (W)'] > 0]# Identify X columns and create the X data set
X_columns = ['T_Amb (deg F)']
for i in range(1, 9):
X_columns.append('T{} (deg F)'.format(i))
X = data[X_columns]# Create the y data set
y = data['P_Elec (W)']
现在我们有了 X 和 y 数据集,我们可以将这些 X 和 y 数据集分成训练和测试数据集。这可以通过调用 scikit-learn 的函数 train_test_split() 来完成,如下所示。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 42)
既然我们已经准备好了训练和测试数据集,我们可以创建并拟合数据集的线性回归模型。为此,我们首先创建模型的一个实例,然后调用*。fit()* 功能如下。
model = LinearRegression()
model = model.fit(X_train, y_train)
注意,这个实现使用了线性回归模型的默认参数。这可能会也可能不会产生与数据的良好拟合,我们可能需要更改参数来获得良好的拟合。我将在以后的文章中提供实现这一点的高级方法,但是现在使用默认参数就足以学习这些概念了。
下一步是在测试数据集上对模型进行评分,以确保它很好地符合数据集。你可以通过调用来实现。score() 并通过测试数据。
score = model.score(X_test, y_test)
如果模型在测试数据集上得分很高,那么你就有机会拥有一个训练有素的、适合数据集的模型。如果没有,那么您需要考虑收集更多的数据,调整模型的参数,或者使用完全不同的模型。
如果模型运行良好,那么您就可以声明模型可以使用,并开始预测系统的行为。因为我们现在没有额外的数据集可以预测,所以我们可以简单地预测测试数据集的输出。为此,您调用了*。预测()*功能如下。
predict = model.predict(X_test)
当暴露于 X_test 定义的输入时,预测变量现在将保存系统的预测输出。然后,您可以使用这些输出直接与 y test 中的值进行比较,使您能够更仔细地研究模型拟合和预测准确性。
这款车型表现如何?
由于我们计算了模型的分数,并将其保存到变量分数中,因此我们可以很快看到模型对 HPWH 耗电量的预测有多好。在这种情况下,模型的得分为 0.58。
r 是从 0 到 1 的度量。零表示该模型根本不能解释观察到的系统行为。一个表明模型完美地解释了系统的行为。0.58 的 r 值表明该模型解释了一半以上的观察到的行为,这不是很好。
我上面提到的三个潜在改进是:
- 收集更多的数据,
- 调整模型的参数,
- 使用完全不同的模型。
我们当然可以收集更多的数据或调整线性回归模型的参数,但这里的核心问题可能是热泵功耗和水温之间的关系是非线性的。线性模型很难预测非线性的东西!
我们可以使用为非线性系统设计的模型来尝试同样的方法,看看我们是否能得到更好的结果。一个可能的模型是随机森林回归器。我们可以通过在脚本末尾添加以下代码来尝试。
from sklearn.ensemble import RandomForestRegressormodel = RandomForestRegressor()
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
这种方法得到了非常高的分数 0.9999,这在另一方面是可疑的。这个模型很有可能对数据集过度拟合,实际上在未来不会产生现实的预测。不幸的是,给定可用的数据集,我们无法真正确定这一点。如果您要使用这个模型来开始预测系统,您将需要监控它,以查看它在更多数据可用时的表现,并继续训练它。根据测量数据绘制预测图还可以洞察模型的行为。
对于这个特定数据集的例子,我会说我会信任这个模型。这是因为该数据集不包含实际测量数据,而是我通过实施回归方程创建的示例数据集,以显示 HPWH 在这些条件下的表现。这意味着 RandomForestRegressor 可能与数据匹配得很好,因为它确定了我用来创建数据集的方程。
有了这些,你就可以开始使用 scikit-learn 来实现机器学习和人工智能了!如果您记得所有 scikit-learn 算法都使用 fit() 、 score() 和 predict() 函数,并且您可以使用 train_test_split() 创建您的数据集,那么您就踏上了学习不同算法和预测实际系统行为的道路。
如何用 Memgraph MAGE,Python 和 Cypher 实现自定义 JSON 实用程序
通过使用 Memgraph MAGE 和 Python 实现几个简单的定制过程来加载和导出 JSON 格式的数据,了解如何扩展 Cypher 查询语言。
作者图片
介绍
您经常会发现自己无法提出适合手头问题的完美查询。每种查询语言都有其缺点,Cypher 也不例外。但是谢天谢地,你总是可以选择编写自己的定制程序。
Memgraph 引入了查询模块的概念,这些模块是定制密码程序的集合。您可以使用 Python 或 C API 来实现它们。
在本教程中,您将经历实现几个简单实用程序的过程,以 JSON 格式加载和导出数据。
介绍记忆图图像
MAGE 代表 Memgraph 高级图形扩展。这是一个由 Memgraph 发起的开源项目,鼓励开发人员分享创新和有用的查询模块,以便整个社区都能从中受益。
你可以在这个链接上找到法师储存库。
先决条件
要完成本教程,您需要:
- 一个 Memgraph DB 的安装:一个为处理企业级实时用例而构建的内存图形数据库。按照快速启动页面上的 Docker 安装说明开始。
- 一个 Memgraph Lab 的安装:一个集成开发环境,用于导入数据、开发、调试和分析数据库查询以及可视化查询结果。
从 JSON 文件导入数据
Memgraph 没有提供开箱即用的 JSON 处理选项。那么,如果你在即将到来的项目中需要这个特性,你有什么选择呢?
实际上,有两种方法可以导入这些数据:
- 独立于 Memgraph,
- 在 Memgraph 中使用查询模块。
第一个选项非常简单。您只需解析所需的 JSON 文档,并创建适当的查询来填充数据库。这样,Memgraph 对 JSON 文件一无所知,您必须完全自己处理它,并且只使用从 JSON 文件中提取的数据运行完成的查询。
第二个选项更加优雅,这也是你将在本教程的剩余部分学到的。
编写自定义密码程序
重要的事情先来。要开始开发新的查询模块,您需要熟悉开发过程。如果你在 Docker 之外的任何东西上运行 Memgraph,那么继续下一段,否则,跳到使用 Docker 开发定制查询模块一节。
启动时,Memgraph 将尝试从默认(/usr/lib/memgraph/query_modules
)目录中找到的所有*.so
和*.py
文件中加载查询模块。如果您想更改 Memgraph 搜索查询模块的目录,只需更改主配置文件(/etc/memgraph/memgraph.conf
)中的--query-modules-directory
标志或将其作为命令行参数提供(例如,当使用 Docker 时),例如:
docker run -p 7687:7687 --query-modules-directory /usr/lib/memgraph/new_query_modules memgraph
如果要添加新的查询模块,需要放在这个目录下。它将在 Memgraph 启动时自动加载,但您也可以通过执行以下查询在数据库运行时重新加载它:
CALL mg.load("QUERY_MODULE_NAME")
使用 Docker 开发自定义查询模块
使用 Docker 时,您不能直接访问默认的查询模块目录,因为它在 Docker 容器中。创建一个卷并挂载它以访问/usr/lib/memgraph/query_modules
目录。这可以通过创建一个空目录modules
并执行以下命令来完成:
docker volume create --driver local --opt type=none --opt device=~modules --opt o=bind modules
现在,您可以启动 Memgraph 并挂载创建的卷: `了
docker run -it --rm -v modules:/usr/lib/memgraph/query_modules -p 7687:7687 memgraph
目录/usr/lib/memgraph/query_modules
中的所有内容在你挂载的modules
卷中都是可见/可编辑的,反之亦然。
用 Python 实现 JSON 实用查询模块
您将把查询模块命名为json_util.py
,因为它将包含处理 JSON 文件所需的实用函数。现在,让我们实现以下三个过程:
- 从本地文件加载 JSON
- 从远程地址加载 JSON
- 将节点导出为 JSON 文档
1.从本地文件加载 JSON
在您的json_util.py
模块中添加以下代码:
import json
import mgp
import urllib.request @mgp.read_proc
def load_from_path(ctx: mgp.ProcCtx,
json_path: str) -> mgp.Record(objects=mgp.List[object]): with open(json_path) as json_file:
objects = json.load(json_file) if type(objects) is dict:
objects = [objects] return mgp.Record(objects=objects)
至此,您已经实现了第一个步骤。@mgp.read_proc
装饰器将函数注册为当前模块的只读过程。if
语句确保过程返回一个list
,即使它只是一个元素。这对以后处理数据很有用。
你如何测试这个程序?让我们在/usr/lib/memgraph/query_modules
目录下创建一个文件,并将其命名为data.txt
。将以下内容放入其中:
[{"name":"Leslie"}, {"name":"Ron"}, {"name":"Donna"}]
如果还没有启动 Memgraph Lab 并运行以下查询:
CALL json_util.load_from_path("/usr/lib/memgraph/query_modules/data.txt")
YIELD *
RETURN *
2.从远程地址加载 JSON
虽然从本地文件加载数据很有帮助,尤其是在开发新过程时,但是更需要一个通过 URL 从远程位置加载数据的过程。幸运的是,您只需对load_from_path()
功能稍加调整即可实现这一功能。让我们将这个新过程命名为load_from_url
:
@mgp.read_proc
def load_from_url(ctx: mgp.ProcCtx,
json_path: str) -> mgp.Record(objects=mgp.List[object]): with urllib.request.urlopen(json_path) as url:
objects = json.loads(url.read().decode()) if type(objects) is dict:
objects = [objects] return mgp.Record(objects=objects)
您可以通过运行以下查询来测试它:
CALL json_util.load_from_url('ADDRESS')
YIELD objects
UNWIND objects AS o
RETURN o.name
3.将节点导出为 JSON 文档
这个过程将接收一个节点列表,并将它们以 JSON 格式保存到一个本地文件中。
@mgp.read_proc
def export_nodes(ctx: mgp.ProcCtx,
nodes: mgp.List[mgp.Vertex],
file_path: str
) -> mgp.Record(success=bool):
json_nodes_list = []
for node in nodes:
json_node = {}
json_node['labels'] = [] for label in node.labels:
json_node['labels'].append(label.name) json_node['properties'] = dict(node.properties.items())
json_nodes_list.append(json_node)
with open(file_name, 'w') as fp:
json.dump(json_nodes_list, fp)
return mgp.Record(success=True)
您可以通过运行以下命令来测试该过程:
MATCH (n)
WITH COLLECT(n) AS listn
CALL json_util.export_nodes(listn,"/usr/lib/memgraph/query_modules/data.json")
YIELD success RETURN success
文件data.json
应该在/usr/lib/memgraph/query_modules
目录中。
结论
在本教程中,您了解了如何通过编写自己的过程来轻松地向 Cypher 查询语言添加额外的功能。虽然从 JSON 文档导入数据更多地被认为是一个实用程序,但查询模块可以成为编写定制图算法或实现图论领域中各种构造的强大工具。
如果您正在开发自己的查询模块,并希望分享它,请查看投稿指南。我们非常乐意提供反馈,并将模块添加到法师库。
关于如何创建自己的自定义密码程序的更深入的解释,请看我们的文档。如果你想要更多探索定制查询模块的分步教程,请务必阅读我们的如何用 NetworkX 和 Memgraph 编写定制密码过程教程。
如何实现雷达图像分类的深度神经网络
利用为计算机视觉开发的网络架构和技术
介绍
对家庭环境中的人和物进行基于雷达的识别和定位相对于计算机视觉具有一定的优势,包括增加用户隐私、低功耗、零光操作和更灵活的传感器放置。
可以使用支持向量机和逻辑回归等浅层机器学习技术对来自雷达的图像进行分类,在我之前的工作中,教雷达了解家和使用随机梯度下降训练线性分类器我分享了如何应用其中的一些方法。
在本文中,您将学习如何开发深度神经网络(DNN)并训练它们对雷达图像中的对象进行分类。此外,您将学习如何使用半监督生成对抗网络(SGAN) [1],它只需要少量的标记数据来训练 DNN 分类器。这在处理雷达数据集时很重要,因为缺乏大型训练集,与基于相机的图像(例如,I mageNet )相比,这有助于使计算机视觉无处不在。
从架构和训练方法的角度来看,最初为视觉图像分类开发的 DNNs(或更具体地说,卷积神经网络)和 SGANs 都可以用于雷达应用。
数据集
为了帮助您理解本文中使用的技术和代码,本节提供了一个简短的数据集浏览。作为 radar-ml 项目的一部分,数据集是从雷达样本中收集的,并在这里找到了。该项目采用自主监督学习,其中标准的基于摄像机的对象检测技术用于自动标记人和对象的雷达扫描。
数据集是以下形式的 Python dict
:
{‘samples’: samples, ‘labels’: labels}
samples
是 N 个雷达投影中的一个list``numpy.array
元组样本的形式:
[(xz_0, yz_0, xy_0), (xz_1, yz_1, xy_1),…,(xz_N, yz_N, xy_N)]
其中雷达投影是投影到 x、y 和 z 轴的三维空间中扫描目标物体的最大返回信号强度。这些 2-D 表示通常是稀疏的,因为投影占据了扫描体积的一小部分。采样、存储和利用二维投影比直接使用三维源数据更有效。
labels
是 N 个 numpy.array
类标签中的一个list
,对应表格中的每个雷达投影样本;
[class_label_0, class_label_1,…,class_label_N]
下面的热图显示了典型单个样本的预测。红色表示返回信号最强的地方。
狗(我的宠物波利)数据集样本的可视化
这些数据是在我家的不同位置捕获的,旨在最大化检测到的对象(目前只有人、狗和猫)、距离和雷达传感器的角度的变化。
数据集仅包含几千个样本(具有已知的标记误差),并且在过度拟合之前只能用于训练少量时期的深度神经网络。标记误差将影响从该数据集训练的雷达分类器的准确性。虽然未来的努力将试图微调目标探测器以减少误差,但使用 SGAN 可以避免或最大限度地减少标记未来雷达观测的需要。在保持可接受的准确性的同时,减少训练分类器的标记数据点的数量,是在这个项目中探索使用 SGANs 的主要动机。
模型架构
如上所述,数据集包含三维雷达图像的二维表示的集合,幸运的是,计算机视觉领域的先前工作证明了在这种二维表示上设计和训练神经网络的可能性,这种二维表示匹配甚至优于在原始三维数据集上训练的网络,参见[2]和[3]。这项先前的工作启发了下面网络的发展。
所有模型和相关培训都是使用 Keras API 实现的,Keras API 是 TensorFlow 的高级 API,是 radar-ml 项目的一部分。
深度神经网络(DNN)分类器
尽管人类无法识别,二维雷达图像投影的集合包含映射回被扫描物体的特征。三个 2-D 投影中的每一个都通过独立的 2-D 卷积层,这些卷积层学习这些特征并连续对图像进行下采样。来自这些层的输出被连接,然后被展平以形成单个特征向量,该特征向量被用作深度连接的密集层的输入,紧接着是分类层。下图中的这个架构。您可能会注意到,这种架构的一个分支类似于计算机视觉中使用的卷积神经网络(CNN)。
DNN 分类器的体系结构
该模型由 radar-ml 资源库中的文件 dnn.py 中的 Python 模块实现。您可以在下面看到定义和编译该模型的代码片段。
创建 DNN 分类器的 Python 代码片段
半监督生成对抗网络
与建立地面事实相比,收集雷达图像用于模型训练相对简单,地面事实需要人在回路中、自主监督学习或半监督学习等技术,半监督学习在训练期间将少量标记数据与大量未标记数据相结合。使用半监督学习的动机是最小化与人类标记雷达扫描或使用复杂(可能容易出错)的自主监督学习相关的工作。
生成对抗网络(GAN)是一种使用未标记数据集来训练图像生成器模型和图像鉴别器模型的架构。在某些情况下,您可以使用鉴别器模型来开发分类器模型。GANs 已用于雷达信号生成[4],并在计算机视觉应用中得到广泛使用[5]。
半监督 GAN (SGAN)模型是 GAN 架构的扩展,它采用监督鉴别器、非监督鉴别器和生成器模型的共同训练。在这个项目中,监督鉴别器被用作归纳为新数据集的分类模型和生成雷达投影的真实示例的生成器模型(仅用作有效性检查)。
监督鉴频器架构如下图所示,您可能会注意到它类似于附近所示的 DNN 架构,但有一些例外,包括使用 LeakyReLU(泄漏整流线性单元)而不是 ReLU,这是 GAN 培训的最佳实践[7]。该模型包括批量标准化层,以帮助训练收敛,这通常是训练 GANs 的一个问题[6]。无监督鉴别器共享除最终输出图层之外的大多数图层,因此具有非常相似的架构。监督鉴别器的输出是一个具有 softmax 激活的密集层,它形成一个 3 级分类器,而无监督模型在 softmax 激活之前获取监督模型的输出,然后计算指数输出的归一化和[6]。
监督鉴别器的体系结构
监督和非监督鉴别器模型都是由位于 radar-ml 存储库中的 sgan.py 文件中的 Python 模块实现的。定义和编译下面模型的代码片段。
创建监督和非监督鉴别器的 Python 代码片段
生成器模型从潜在空间中获取一个向量(从标准正态分布中提取的噪声向量),并使用具有 ReLU 激活的转置卷积层的三个分支来连续上采样潜在空间向量,以形成三个雷达图像投影中的每一个。生成器被堆叠在鉴别器模型的顶部,并且在后者的权重被冻结的情况下被训练。下图描述了这种组合架构。请注意使用批量标准化图层来帮助模型训练收敛。
GAN 架构
生成器和 GAN 由 radar-ml 资源库中的文件 sgan.py 中的 Python 模块实现。下面是定义和编译模型的代码片段。
创建生成器和 GAN 的 Python 代码片段
模型训练和结果
DNN
DNN 通过tf.keras.Model
类fit
方法训练,并由 radar-ml 资源库中 dnn.py 文件中的 Python 模块实现。下面是训练函数的代码片段,没有显示预处理和过滤数据所需的步骤。
DNN 训练函数的 Python 代码片段
典型训练的结果如下。模型和数据集的当前状态能够获得 80%到 80%的验证集精度。需要做更多的工作来达到或超过 SVM 和逻辑回归模型在之前的工作中获得的 90%的精度[8][9]。这将是今后努力的重点。
DNN 培训结果
斯甘
生成对抗网络(GANs)很难训练。这是因为该架构包括一个生成器和一个鉴别器模型,它们在零和游戏中竞争。这意味着一个模型的改进是以另一个模型的性能下降为代价的。结果是一个非常不稳定的训练过程,经常会导致失败,例如,生成器总是生成相同的图像或生成无意义的图像。因此,在配置和训练您的 GAN 模型时,可以使用许多启发式方法或最佳实践(称为 GAN hacks)。这些试探法是从业者多年来在一系列问题上测试和评估成百上千个配置操作组合来之不易的。
“用 Python 生成对抗性网络”,Jason Brownlee,2021。
您可以找到许多优秀的论文和文章,帮助您了解如何应用最佳实践来培训 GANs。特别是, Jason Brownlee 发表了许多务实的文章和论文,可以证明节省时间[7]。这些工作中的一部分被用于确定在雷达 SGAN 模型和数据集上工作得相当好的训练方法。如果你认真理解应用机器学习的 和 ,可以考虑阅读他的在线文章和购买他的电子书。
训练循环由 radar-ml 存储库中的文件 sgan.py 中的 Python 模块实现。下面是训练循环的一个片段,没有显示预处理和过滤数据集所需的步骤以及几个帮助函数。该代码基于参考文献[7]。注意,鉴别器模型用 1.5 批样本更新,但是生成器模型每次迭代用一批样本更新。
SGAN 训练循环的 Python 代码片段
由于 GANs 的随机性,你会发现每次跑步的训练结果都不一样,所以最好将几次跑步的结果平均起来。对于无监督的鉴别器和生成器,良好的训练会话将具有中等(~ 0.5)和相对稳定的损失,而有监督的鉴别器将在训练集上收敛到非常低的损失(< 0.1) with high accuracy (> 95%)。验证集的准确性结果往往在低到高的 70%之间,每类仅使用 50 个监督样本时,损失在 1.2 左右徘徊。这是一个令人鼓舞的结果,但显然需要更多的建模工作和数据收集,以获得与该数据集上采用的其他机器学习方法相当的验证准确性,通常为约 90% [8][9]。这将是这个项目今后工作的重点。典型的培训结果如下所示。
SGAN 培训结果
您还应该查看生成器生成的图像,以确定它们是否有意义。在这种情况下,由于图像是 3d 物体的雷达扫描的 2d 投影,并且不能被人类识别,因此需要将生成的图像与来自原始数据集的示例进行比较,如上图所示。下图是一组生成的二维扫描。其中一个投影(X-Y 平面)中的相似性是明显的,但在其他投影中不明显,至少对于这次训练运行是如此。这可以解释低准确性,并且找到使其他生成的投影在视觉上类似于训练集的方法留给了未来的练习。
生成的数据集样本的可视化
结论
您可以利用 CNN、SGAN 的模型架构以及为基于摄像机的计算机视觉开发的相关培训技术来开发神经网络,从而对雷达图像进行分类。考虑到雷达数据集的缺乏,您通常需要收集雷达数据集,这些数据集可能是资源密集型的,并且对于地面实况新雷达观测来说容易出错。您可以使用自我监督技术来利用未标记的数据,每个类只使用几十个或更少的标记样本和一个 SGAN。通过这种方式,您可以使用大量未标记的数据开发雷达图像分类器。
以前的工作使用浅层机器学习模型,并在数据集上实现了比当前使用这里描述的网络和技术获得的更高的准确性。未来的努力是计划弥补这一差距,并增加数据集的大小,以在过度拟合之前获得更好的验证集准确性。
参考
- 生成对抗网络半监督学习,2016。
- 用于三维形状识别的多视角卷积神经网络,2015。
- 卷积神经网络多视角分类,2021。
- 雷达信号生成对抗网络
代,2019。 - 深度卷积生成对抗网络的无监督表示学习,2016。
- 训练 GANs 的改进技术,2016。
- 用 Python 生成对抗性网络,2021。
- 教雷达了解家,2020。
- 利用随机梯度下降训练线性分类器,2020。
如何实现深度神经网络进行时间-事件分析
比较 DeepHit 和 DeepSurv 模型
莫里茨·金德勒在 Unsplash 上拍摄的照片
在我的上一篇帖子中,我用随机森林、梯度增强和 SVM 生存模型演示了一些例子。今天,我将向您展示如何使用深度神经网络处理相同类型的问题。具体来说,我们将基于 PyTorch 环境,使用连续时间模型( DeepSurv )和使用 pycox 的离散时间模型( DeepHit )来浏览示例。
因为我在上一篇文章中已经介绍了时间到事件分析的基础知识,所以今天我将省略它以避免任何冗余信息。如果你对更多细节感兴趣,请查看上面的链接。
在我开始之前,我鼓励大家看看上面链接的 pycox 回购。它充满了有用的信息以及额外的例子和资源。您甚至可以直接加载几个时间事件数据集,我今天将使用其中的一个。
数据
对于今天的例子,我使用研究来了解预后偏好结果和治疗风险(支持)数据集,该数据集调查医院中重病患者的存活时间。它包含来自 8,873 名患者的数据,其中有 14 个预测变量和 2 个结果变量(持续时间和事件)。下面,你可以预览我们的数据集。
图 1 —用于我们分析的支持数据集的预览
有关数据集中变量的快速描述,请参考下图。当我打开数据集时,这些要素被命名为 x0 到 x14。我根据这里找到的信息给它们重新命名。
图 2 —数据集中变量的描述
关于我们将要使用的库的完整列表,请点击这里查看我的笔记本。
接下来,我们将使用下面的代码将数据集分为训练集、验证集和测试集。或者,你可以使用 sklearn 的训练测试分割。
图 3 —导入和分割我们的数据集
预处理
在开始之前,我们需要根据变量类型执行一些数据预处理:
- 数字变量:标准化
- 二进制变量:不需要预处理
- 分类变量:创建嵌入
使用图 4 中的代码可以完成对数据的预处理。这里发生了相当多的事情,所以让我们打开它。在第一个块中,我们简单地创建列表来分隔不同的变量类型,因为我们将对每个变量应用不同的转换。
第二个模块是我们将要执行的转换。具体来说,我们将对数值变量使用 标准缩放器 ,对二进制变量不做任何处理,将使用OrderedCategoricalLong来转换我们的分类变量。
下一个块中的 DataFrameMapper 是sklearn _ pandas包的一部分,允许我们轻松地将 sk learn 转换应用到我们的 pandas 数据帧。
最后,最后两个模块将转换应用到我们的数据集。你会注意到来自 x_fit_transform 和 x_transform 的结果被包装在一个使用 tt.tuplefy 的元组中。这个元组称为元组树,用于训练 PyTorch 模型,它可以处理嵌套元组中的数据。
图 4 —支持数据集的数据预处理
在下一节中,我们将开始探索离散时间模型 DeepHit 和连续时间模型 DeepSurv,并了解它们在我们的数据集上的表现。
深度打击
DeepHit 是一个适用于生存(时间到事件)分析的深度学习模型。可以对其进行修改,以用于调查单一风险或竞争风险。由于这是一个离散时间模型,我们研究的第一步是定义要评估的离散时间。
有两种方法可以解决这个问题。我们可以使用下面的代码创建等间距(等距)的离散时间间隔或分位数。这里我们定义了 10 个等间距的区间,然而,通过改变方案,我们可以基于分位数定义区间。值得注意的是,我们不需要转换测试集上的标签。
图 5-深击的等距时间间隔(10)
要了解这种情况,请参考下图,该图展示了等距(左)和分位数(右)离散化之间的差异。在每一个中,有 10 个定义的时间间隔;然而,在分位数离散化中,区间是由事件(死亡)的比例定义的。事件多的地方,间隔就多。在我们的数据集中,我们看到更多的事件发生在早于晚于晚。如果你有兴趣更深入一点,请参考这篇论文。
图 DeepHit 的等距(左)与分位数(右)时间离散化。作者图片
如果你跟随我的笔记本,你会看到我做了几个实验,看看修改不同参数的效果。我将展示在我测试的条件下表现最好的模型。在运行这些实验时,尝试修改不同的参数以查看它对模型性能的影响是很重要的。
在运行我们的模型之前,我们必须定义嵌入的数量和维度。正如您在下面的代码中看到的,每个类别都由一个等于一半级别数的向量表示。最高性能的 DeepHit 模型由 2 个多层感知器组成,每个感知器由 64 个节点组成。在每一层以及 20%的漏失之后进行批量标准化。
选择的优化器是循环的 Adam(WR) ,它是 Adam 优化器的权重衰减正则化版本。
如果您可能在不需要分类嵌入的情况下使用不同的数据集,您可能需要对代码进行一些修改。首先,您不需要定义嵌入的前两行。其次,您需要从第三个代码块中删除 num_embeddings 和 embeddings_dims 。最后,您需要在第三个代码块中用 VanillaMLP 替换 MixedInputMLP 。 MixedInputMLP 将实体嵌入应用于分类变量,并将它们与其他变量连接起来。
图 7 —我们数据集的最高性能 DeepHit 模型
在训练模型时,找到合适的学习率是一个常见的问题。你经常会在文献中找到很多。pycox 中一个非常有用的工具是学习率查找器。虽然它可能不会提供最好的学习速度,但它给了你一个开始实验的好地方。可以使用下面的代码执行实现,它给了我们 0.04 的值。
图 8——学习率查找器
现在我们已经定义了所有的模型参数,我们使用下面的代码来拟合它。
图 9 —根据我们的数据拟合模型
评估模型
为了评估我们的模型,我们可以从查看测试数据的一些预测开始。该代码(图 10)绘制了五名患者的预测存活率。第一行的插值函数创建一个线性插值来平滑时间上的步长(记住这是一个离散时间模型)。可以在代码的正下方看到该图。
图 10——绘制预测存活曲线
图 11 —选定患者的深度打击存活曲线
为了评估模型在我们的测试集上的表现,有几个可用的指标。我将提出审查加权的逆概率(IPCW) 石南得分。这将计算预测结果与我们定义的每个离散时间间隔的实际结果的均方误差(MSE)。由于正确预测的 MSE 为 0,因此分数越低表示性能越好。
我们可以使用以下代码绘制性能随时间变化的曲线图。第一块允许我们绘制 Brier 分数随时间的变化图(图 13)。还可以计算综合得分(第 6 行),结果得到 0.21 的值(79%的准确度)。
图 12—IPCW·布瑞尔随时间变化的得分和综合得分
图 13——deep surv 模型的 IPCW·布瑞尔评分图
在下一节中,让我们看看这与来自 DeepSurv 的结果相比有多好。
DeepSurv
DeepSurv 是将深度神经网络的潜力纳入时间-事件分析的另一种选择。主要区别在于,这是一个连续时间模型,这意味着我们不需要像在 DeepHit 中那样定义离散间隔。在这里,我们简单地转换我们的标签,如下所示。
图 14——无需离散时间间隔的 DeepSurv 标签转换,如上文针对 DeepHit 所执行的
剩下的和上面类似,我们定义我们的模型并使它适合我们的数据。正如您所看到的(图 15),主要的区别在第 3 行,这里 out_features = 1。同样,这是因为我们使用的是连续时间模型,而使用 DeepHit 时,它被设置为我们定义的离散间隔数。值得注意的一点是,由学习率查找器确定的该模型的学习率为 0.07(而 DeepHit 的学习率为 0.04)。
图 15 —性能最佳的 DeepSurv 模型
评估模型
最后,我们以与上面完全相同的方式评估模型。下面,你会发现 5 个病人的预测图。您可能会注意到,这些线条比为 DeepHit 绘制的线条更平滑,因为我们不需要在间隔之间进行插值。
图 16 —选定患者的深度生存曲线
IPCW·布瑞尔评分(图 17)比 DeepHit 模型表现稍好。综合得分以 0.18 的值(82%的准确度)证实了这一点。
图 17—deep hit 模型的 IPCW·布瑞尔评分图
摘要
今天,我们通过几个例子学习了如何使用深度神经网络进行时间-事件分析。我希望你给他们两个试一试你的数据,看看他们如何比较。我再次建议您访问 pycox repo,查看所有可用的选项。他们采用了几个离散时间和连续时间的神经网络进行生存分析。感谢阅读!
如何在格朗实施领域驱动设计(DDD)
学习如何在 Go 应用程序中使用 DDD 的简单方法
Percy Bolmér. Gopher 图片由 Takuya 上田创作,Original Go Gopher 由 Rene French 创作(CC BY 3.0)
近年来,微服务已经成为一种非常流行的软件构建方法。微服务用于构建可扩展且灵活的软件。然而,在许多团队中随机构建微服务会导致很大的挫折和复杂性。
不久前,我还没有听说过领域驱动设计——DDD,但现在似乎每个人都在谈论它,无论我去哪里。
本文中的所有图像均由珀西·博尔梅尔绘制,而《地鼠》则由塔库亚·上田绘制,灵感来自勒内·弗伦奇的作品。图像中的地鼠已被修改。
在本文中,我们将从头开始构建一个在线酒馆,同时逐步探索 DDD 的不同地区。有希望的是,当一件事情一次完成时,会更容易理解 DDD。我想采取这种方法的原因是,阅读关于 DDD 的书让我的脑袋爆炸,每一次。有太多的术语,太宽泛了,不清楚什么是什么。
如果你想看这篇文章的视频版,它可以在 YouTube 上找到
如果你在研究 DDD 的时候不知道为什么我的脑袋会爆炸,下面的图表可能会帮助你意识到这一点。
DDD 的关键词——埃里克·埃文斯的书《领域驱动设计:解决软件核心的复杂性》中的图表
在领域驱动设计:解决软件核心的复杂性中,Eric Evans 需要大约 500 页来解释它是有原因的。如果你真的对学习 DDD 感兴趣,那么不要害怕阅读埃文关于它的书。
首先,我想指出,本文描述了我对 DDD 的理解,我在文中展示的实现是基于我在 Go 相关项目中的经验。我们将创建的实现绝不是社区接受的最佳实践。我还将按照 DDD 的方法来命名项目中的文件夹,以便于理解和遵循,但我不确定我是否希望一个真正的存储库是这样的。出于这个原因,我还将有一个单独的存储库分支,在那里我已经修复了结构,并且重构将在第二篇文章中解释。
我在互联网上看到了许多关于 DDD 以及如何正确实施的热烈讨论。让我印象深刻的一件事是,大多数时候,人们似乎忘记了 DDD 背后的目的,而是最终在小的实施细节上争论不休。我认为重要的是,遵循 Evan 提出的方法论,而不是某个东西被命名为 X 或 y。
DDD 是一个巨大的区域,我们将主要关注它的实现,但是在我们实现任何东西之前,我将快速回顾一下 DDD 中的一些方面。
什么是 DDD
领域驱动设计是一种根据软件所属的领域对软件进行结构化和建模的方法。这意味着首先要为编写的软件考虑一个domain
。领域是软件打算处理的主题或问题。软件应该被编写来反映领域。
DDD 主张工程团队必须会见主题专家 SME,他们是领域内的专家。这样做的原因是因为 SME 拥有关于领域的知识,并且这些知识应该反映在软件中。当你想到这一点时,你会觉得很有道理,如果我要建立一个股票交易平台,作为一名工程师,我对这个领域的了解是否足以建立一个好的股票交易平台?如果我和沃伦·巴菲特就这个领域进行几次会谈,这个平台可能会变得更好
代码中的架构也应该反映在领域中。当我们开始写我们的酒馆时,我们将看到如何。
一只地鼠在 DDD 的旅程
两只地鼠庆祝成功合作。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
让我们开始学习如何实现 DDD,首先我想告诉你一个关于地鼠 Dante 的故事,他想创建一个在线酒馆。但丁知道如何写代码,但他对如何经营酒馆一无所知。
但丁决定开始在酒馆工作的那一天,他遇到了一个问题,从哪里以及如何开始?他出去散步思考他的问题。在一个停车标志前等待的时候,一个戴着礼帽的男人走近但丁,说道
“看起来你好像在担心什么,年轻人,也许你需要帮助建造一个酒馆?”
但丁和大礼帽男散步很愉快,他们讨论了酒馆以及如何经营酒馆。
但丁问一般喝酒的人是怎么处理的,大礼帽回答说那叫顾客,不是喝酒的人。
大礼帽也向但丁解释了酒馆需要几样东西来经营,比如顾客、员工、银行和供应商。
领域、模型、通用语言和子域
我希望你喜欢关于但丁的故事,我写它是有原因的。我们可以用这个故事来解释一些在 DDD 使用的关键词,这些词如果不放在上下文中我很难解释,比如一个小故事。
但丁和大礼帽之间进行了一场对话。作为主题专家的 Top hat 和作为工程师的 Dante 讨论了领域空间并找到了共同点。这样做是为了学习Model
,模型是处理一个领域所需组件的抽象。
当但丁和大礼帽讨论酒馆的时候,他们在谈论我们所说的Domain
。域是软件将要运行的区域,我称酒馆为核心/根域。
大礼帽还指出,它不叫酒客,而叫Customers
。这表明在 SMO 和开发人员之间找到一种共同语言是多么重要。如果项目中不是每个人都有一个Ubiquitous Language
,这将变得非常混乱
我们也得到一些Sub-domains
是礼帽提到的酒馆需要的东西。子域是一个单独的域,用于解决根域内部的一个区域。
用 Go-实体和值对象编写 DDD 应用程序
用可变和不可变状态解释实体和值对象。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
既然我们已经拥有了开始的一切,是时候开始对酒馆进行编码了。首先,通过创建 go 模块来设置项目。
mkdir ddd-go
go mod init github.com/percybolmer/ddd-go
我们将首先创建一个domain
文件夹,在其中我们将存储所有需要的子域,但是在我们实现任何域之前,我们需要在根域中创建另一个文件夹。出于演示的目的,我们将其命名为entity
,因为它将保存 DDD 方法中所谓的实体。
一个实体是一个结构,它有一个Identifier
并且可以改变状态,改变状态意味着实体的值可以改变。
首先,我们将创建两个实体,Person
和Item
。我确实喜欢将我的实体保存在一个单独的包中,这样它们就可以被所有其他域使用。
在根域中添加了一个实体文件夹
为了保持代码的整洁,我喜欢小文件,并使文件夹结构易于导航。所以我建议创建两个文件,每个实体一个,以实体命名。现在,它只是其中的结构定义,但是以后,可能会添加一些其他的逻辑。
我们为我们的域创建的第一个实体
person.go —代表一个人的实体
item.go 定义产品基础的项目实体
很好,现在我们已经定义了一些实体,并了解了什么是entity
。具有引用它的唯一标识符的结构,它具有可以更改的状态。
可能会出现这样的情况,我们有不可变的结构,并且不需要唯一的标识符,这些结构被称为Value Objects
。所以在创建后没有标识符和持久值的结构。值对象通常出现在领域内部,用于描述该领域的某些方面。我们现在将创建一个值对象Transaction
,一旦事务被执行,它就不能改变状态。
在现实世界的应用程序中,通过 ID 跟踪事务可能是一个好主意,但这只是为了演示的目的
transaction . go——定义双方之间的支付
聚合—组合的实体和值对象
聚合,实体和值对象的组合。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
是时候看看 DDD 的下一个组成部分了,聚合体。聚合是一组实体和值对象的组合。在我们的例子中,我们可以从创建一个新的聚合开始,这个聚合就是Customer
。
DDD 集合是领域概念(订单、门诊、播放列表)——马丁·福勒
使用聚合的原因是业务逻辑将应用于Customer
聚合,而不是每个实体持有逻辑。聚合不允许直接访问基础实体。同样常见的是,需要多个实体来正确表示现实生活中的数据,例如,一个客户。它是一个人,但他/她可以持有产品,并执行交易。
DDD 集合中的一个重要规则是,它们应该只有一个实体充当root entity
。这意味着根实体的引用也用于引用聚合。对于我们的客户集合,这意味着Person
ID 是唯一的标识符。
让我们创建一个aggregate
文件夹,并在其中创建一个名为customer.go
的文件。
mkdir aggregate
cd aggregate
touch customer.go
在该文件中,我们将添加一个名为Customer
的新结构,它将保存所有需要的实体来表示一个客户。请注意,struct 中的所有字段都以小写字母开头,这是 Go 中的一种方法,可以使定义 struct 的包之外的对象不可访问。这样做是因为聚合不允许直接访问数据。该结构也没有为数据如何格式化定义任何标签,例如json
。
这是从文章的早期版本编辑而来的,我决定让所有项目都可以访问,以便更容易地将它们存储在数据库中,但是因为它违反了 miosz Smóka 在 Threedotlabs 与 miosz Smóka 讨论的 DDD 规则,我决定更改它。
customer . go——表示客户及其内部所需实体的集合
我将所有的实体都设置为指针,这是因为一个实体可以改变状态,我希望它能反映到所有可以访问它的运行时实例中。但是值对象被保持为非指针,因为它们不能改变状态。
很好,现在我们已经创建了一个聚合,我们可以继续了。
工厂—封装复杂的逻辑
工厂,一种创建复杂集合或存储库和服务的模式。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
到目前为止,我们只定义了不同的实体、值对象和集合。是时候开始实现一些实际的业务逻辑了,我们从factories
开始。工厂模式是一种设计模式,用于将复杂的逻辑封装在创建所需实例的函数中,而调用者不知道任何实现细节。
工厂模式是一种非常常见的模式,你甚至可以在 DDD 应用程序之外使用它,而且你可能已经使用过很多次了。一个很好的例子是官方 Go Elasticsearch 客户端。您将一个配置插入到一个NewClient
函数中,这个函数是一个工厂,它返回一个连接到弹性集群的客户机,您可以插入/删除文档。对其他开发者来说非常简单,在NewClient
中有很多东西
弹性搜索——新客户的工厂。仅用于演示工厂的工作方式。
DDD 建议使用工厂来创建复杂的集合、仓库和服务。我们将实现一个工厂函数,它将创建一个新的Customer
实例。这将产生一个名为NewCustomer
的函数,它接受一个name
参数,函数内部发生的事情不应该被想要初始化一个新客户的域所关注。
NewCustomer
将验证输入是否包含创建Customer
所需的所有数据
在一个实际的应用程序中,我可能会建议在域/客户和工厂中聚集客户,我们将在第二篇文章中讨论这一点
customer.go —为客户创建一个工厂,该工厂验证输入名称并返回一个新的客户指针
客户工厂现在帮助验证输入,创建新的 ID,并确保所有的值都被正确初始化。
现在我们已经有了一些业务逻辑,所以也是时候开始添加测试了。我将在aggregate
包中创建一个customer_test.go
,在这里测试与Customer
相关的逻辑。
对客户工厂进行单元测试,以确保它按预期工作
虽然仅仅创造新客户我们不会走得很远,但是是时候开始寻找我所知道的最好的设计模式了。
存储库—存储库模式
用于隐藏底层实现细节的存储库接口。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
DDD 描述了存储库应该被用来存储和管理集合。一旦我学会了这种模式,我就知道我永远不会停止使用它。这种模式依赖于将存储/数据库解决方案的实现隐藏在接口之后。这允许我们定义一组必须存在的方法,如果它们存在,它就有资格被用作存储库。
这种设计模式的优点是,它允许我们在不破坏任何东西的情况下交换解决方案。我们可以在开发阶段使用内存存储,然后在生产阶段将其切换到 MongoDB 存储。它不仅有助于改变所使用的底层技术而不破坏任何利用存储库的东西,而且在测试中也非常有用。您可以简单地为单元测试等实现一个新的存储库。
我们将从在domain/customer
包中创建一个名为repository.go
的文件开始。在该文件中,我们将定义存储库所需的功能。我们将希望获得,添加和更新客户。我们不会删除任何顾客,一旦你成为这家酒馆的顾客,你就永远是顾客。我们还将在客户包中实现一些通用错误,不同的存储库实现可以使用这些错误。
repository.go —定义存储客户的存储库规则
接下来,我们需要实现满足接口的实际业务逻辑,我们将从内存存储开始。在本文的最后,我们将看看如何在不破坏其他任何东西的情况下,将其更改为 MongoDB 解决方案。
我喜欢将每个实现放在它的目录中,只是为了让团队中的新开发人员更容易找到正确的代码位置。让我们创建一个名为memory
的文件夹来表示存储库正在使用内存作为存储。
另一个解决方案是在客户包中加入 memory.go,但是我发现在更大的系统中它会变得很混乱
mkdir memory
touch memory/memory.go
让我们首先在内存文件中设置正确的结构,我们想要创建一个具有实现CustomerRepository
的方法的结构,并且让我们不要忘记factory
来创建一个新的存储库。
添加新客户仓库所需的结构
我们需要添加一种从Customer
集合中检索信息的方法,比如从根实体中检索 ID。因此,我们应该用一个获取 ID 的函数和一个更改名称的函数来更新聚合。
aggregate/customer.go —添加一个函数来获取根实体的 ID
让我们向内存存储库添加一些非常基本的功能,以便它能按预期工作。
客户存储库—实现客户存储库的逻辑
和往常一样,我们应该为代码添加测试。我想指出从测试的角度来看,存储库模式有多棒。在单元测试中,用专门为测试创建的存储库替换部分逻辑是如此容易,使得复制更容易知道测试中的错误。
大多数客户存储库的单元测试
太好了,我们有了第一个仓库。记住让你的存储库与他们的领域相关。在这种情况下,存储库只处理客户集合,而且它只应该这样做。永远不要让存储库耦合到任何其他集合,我们想要虱子耦合。
那么,我们如何处理酒馆的逻辑流程,我们不能简单地依赖客户存储库?我们将从某一点开始耦合不同的存储库,并构建一个表示酒馆逻辑的流程。
进入服务,我们需要学习的最后一部分。
服务—连接业务逻辑
服务——将存储库连接到实际的业务逻辑中。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
我们有所有这些实体、一个集合和一个存储库,但是它看起来还不像一个应用程序,不是吗?这就是为什么我们需要下一个组件Service
。
服务将所有松散耦合的存储库绑定到一个业务逻辑中,以满足特定领域的需求。在 tavern 案例中,我们可能有一个Order
服务,负责将存储库链接在一起以执行订单。因此该服务将保持对一个CustomerRepository
和一个ProductRepository
的访问
服务通常包含执行某个业务逻辑流所需的所有repositories
,比如Order
、Api
或Billing
。最棒的是,您甚至可以在服务中包含服务。
我们将实现Order
服务,例如,该服务稍后可以成为Tavern
服务的一部分。
让我们创建一个名为services
的新文件夹,它将保存我们实现的服务。我们将首先创建一个名为order.go
的文件,它将保存我们将用来处理酒馆中新订单的OrderService
。我们仍然缺少一些域,所以我们将只从 CustomerRepository 开始,但很快会添加更多。
我想从创建新的Service
的Factory
开始,并展示一个我从 Jon Calhoun 的《web 开发》一书中学到的超级巧妙的技巧。我们将为一个函数创建一个别名,该函数接受一个Service
作为指针并修改它,然后允许不同数量的别名。这样,改变服务的行为或者替换存储库就非常容易了。
order.go —新订单服务的工厂函数接受不同数量的配置
看看我们如何在工厂方法中引入可变数量的OrderConfiguration
?这是一种允许动态工厂的非常简洁的方式,允许开发人员配置架构,只要它已经实现。这个技巧对于单元测试非常有用,因为您可以用想要的存储库替换服务中的某些部分。
小注意,对于较小的服务来说,这种方法似乎有点大材小用。我想指出的是,在示例中,我们只使用配置来修改存储库,但是这也可以用于内部设置和选项。对于较小的服务,你也可以创建一个简单的工厂,例如接受CustomerRepository
的工厂。
让我们创建一个应用了CustomerRepository
的OrderConfiguration
,这样我们就可以开始创建订单的业务逻辑了。
WithCustomerRepository 返回 OrderConfiguration 的函数,以便将其用作 NewOrderService 的输入参数
现在,为了使用它,当我们创建service
时,你可以简单地链接所有的配置,允许我们容易地切换出部件。
// In Memory Example used in Development
NewOrderService(WithMemoryCustomerRepository())
// We could in the future switch to MongoDB like this NewOrderService(WithMongoCustomerRepository())
让我们开始给Order
服务添加功能,这样顾客就可以在酒馆里买东西了。
order.go —为客户创建订单。
哎呀,我们酒馆没有任何产品供应。当然,你知道如何解决这个问题,对吗?
让我们实现更多的存储库,并通过使用OrderConfiguration
将它们应用到服务中
产品存储库——酒馆的最后一块
产品存储库—处理产品集合。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
现在我们知道了我们在 DDD 需要的所有组件是做什么的,是时候用它们来练习一下了。我们将从修复一个ProductRepository
开始,这样我们就可以找到客户订购的产品。
从这一点开始,我会讲得快一点,解释得少一点,因为我们已经讲了基础知识,所以我不会解释两遍。
让我们在aggregate
文件夹中创建product.go
和product_test.go
。我们首先创建Product
集合,并为其创建一个factory
函数。
product.go —包含项目、价格和数量的聚合。
接下来,您应该为聚合添加单元测试,以确保其中的任何逻辑都按预期工作。
单元测试所有聚合逻辑
在产品域/domain/product/repository.go
中创建一个文件。这里我们将定义允许我们访问产品的ProductRepository
。
product.go —用于访问产品的产品存储库
很好,就像CustomerRepository
一样,我们将为ProductRepository
实现一个内存解决方案。在product
域中创建一个名为 memory 的文件夹,并插入下面的代码。
memory.go —使用内存逻辑存储产品的产品存储库
当然,我们需要一些测试。
memory_test.go —产品存储库的单元测试
为了开始使用ProductRepository
,我们需要修改OrderService
,以便它能够保存存储库。打开services/order.go
并为其添加一个新字段。
OrderService —现在有两个存储库
请记住,一个服务可以拥有多个存储库,也可以拥有其他服务。
接下来,我们需要添加一个新的OrderConfiguration
函数来应用内存存储库。请注意我现在是如何向函数插入一个参数的,一个产品切片。因为我们返回了一个OrderConfiguration
,所以我们仍然可以在工厂中使用这个函数。
用于将内存存储库应用到 OrderService 的 OrderConfiguration
让我们更新OrderService
中的CreateOrder
函数来查找订购的产品,我们还将返回所有订购产品的价格。
CreateOrder —现在使用 ProductRepository 获取产品信息
我将更新order_test.go
中的测试,用所有需要的存储库和产品创建OrderService
。
Order_test.go —使用两个存储库创建一个 OrderService 并执行订单
Tavern —包含子服务的服务,以及 MongoDB
酒馆——最终解决方案。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
现在到了最后一个环节,即Tavern
服务。这个服务将持有OrderService
作为子服务,允许酒馆创建订单。
您想要像这样堆叠服务的原因可能是应用了额外的逻辑。例如,Tavern
服务很可能希望能够添加Billing
。请注意,我们可以多么容易地实现对Tavern
的排序逻辑,而不用担心实现细节,然后再对其进行扩展。
我将在services
文件夹中创建一个tavern.go
。在这个文件中,我们创建了Tavern
结构,它保存了OrderService
并有一个NewTavern
工厂来应用OrderService
Tavern . go-Tavern 现在可以使用订单服务订购产品
为了进行测试,我们可以创建一个单元测试。
Tavern _ test.go 通过点啤酒来测试酒馆
现在我们有了一个酒馆,我想花点时间向您展示如何实现一个针对CustomerRepository.
的 MongoDB 解决方案,这是仓库设计模式真正开始闪耀的地方。我喜欢能够轻松地切换存储库。
首先在customer
域中添加一个名为mongo
的新包。我们将创建一个满足CustomerRepository
的结构,这样我们就可以使用它了。
这里需要注意的一件重要事情是用于存储客户的内部mongoCustomer
结构。我们不存储aggregate.Customer
,因为那会将聚合存储绑定到存储库。相反,每个存储库负责根据需要格式化和组织数据,与其他包没有联系。这就是为什么我们不直接在集合上使用json
或bson
标签的原因,因为那样会使实现成对。为了进行切换,我们还添加了一个在两者之间转换的factory
函数。
mongo . go——用 MongoDB 实现 CustomerRepository
接下来要做的是添加一个OrderConfiguration
,这样我们就可以将存储库应用到OrderService
中。
order . go——一个 orderConfiguration,它应用 mongo 存储库而不是内存
然后更改tavern_test.go
中的输入,改为接受 MongoDB 配置。注意我们可以多么容易地在内存和 MongoDB 之间切换,太神奇了。
Tavern_test.go —用 MongoDB 存储库替换内存
瞧,就这么简单!我们现在有了一个 tavern,它既可以使用内存存储库,也可以使用 MongoDB 存储库。
结束第一篇文章。上田拓也的《地鼠》,雷尼·弗伦奇的《原始地鼠》
简而言之,在本文中,我们已经涵盖了领域驱动设计的基础。
- 实体 —可变的可识别结构。
- 值对象 —不可变的不可识别的结构。
- 聚合 —存储在存储库中的实体和值对象的组合集。
- 存储库 —存储集合或其他信息的实现
- 工厂 —创建复杂对象的构造器,使其他领域的开发人员更容易创建新的实例
- 服务 —构建业务流的存储库和子服务的集合
记住,在这个例子中,我们用适当的 DDD 术语来命名每个组件和包,以便于理解和联系。这是一个学习的例子,在一个真实的存储库中,我可能不会这样命名包,因此,在第二篇文章中,我们将这个项目重构为一个更清晰的架构解决方案。
公平的警告,第二篇文章比这篇长文章要短得多
那么,你的观点是什么,这种建筑方法看起来像是你可以使用的吗?
你可以在 GitHub 上找到完整代码。
感谢您的阅读,并随时以任何可能的方式联系我,在 T witter 、 Instagram 、 Linkedin 上发表评论。
如何在 Python 中实现归并排序算法
数据科学
理解合并排序算法的操作以及它是如何在 Python 中实现的
约书亚·阿拉贡在 Unsplash 上拍摄的照片
L 盈利算法有时会很乏味。
算法主题领域是计算机科学的一个分支,它对巨大的复杂性有着臭名昭著的理解。但是算法有不同的形状、大小和复杂程度。
假设你渴望达到能够处理复杂算法并用它们解决问题的水平。在这种情况下,您必须积累理解和实现不太复杂的算法的经验。
这些不太复杂的算法之一是合并排序。
下面是这篇文章的内容:
- 简介以分而治之的方法解决问题
- 合并排序算法中涉及的操作和直觉的解释
- 合并排序算法的 Python 实现
什么是分治算法?
算法设计中使用的分而治之范式结合了递归来解决最初由较大问题衍生而来的小问题。
计算机科学领域中的递归被定义为函数在其主体内调用自身的过程。
分治算法是递归的;这意味着初始问题被分成相关联的子问题,其中解决方案可以递归地应用于每个子问题。更大问题的最终解决方案是子问题解决方案的组合。
算法设计的分治法有三个主要组成部分:
- 不断地把大问题分解成小部分。
- 通过使用递归调用的函数来解决每一个小部分。
- 合并:m 戈尔将所有较小部分的所有解合并成一个统一的解,成为起始问题的解。
什么是合并排序?
合并排序算法采用分治算法范例来高效地对列表中的元素进行排序。该算法是由约翰·冯·诺依曼在 1945 年发明的。
合并排序操作遵循这样的基础:将列表分成两半,然后将新的两半继续分成它们各自的组件。然后是单个组件之间的比较,这些组件被合并以形成最终的排序列表。
Python 中合并排序的实现
实现合并排序算法的方法分为两部分。
第一部分将引导分治范式的划分组件。本节中的代码实现将初始列表分成更小的组件。最初的列表划分只有在每个分离的组件无法再被分解时才会停止。
下面是算法第一部分的图解。
合并排序算法的分割组件图-按作者分类的图像
def merge_sort(list):
# 1\. Store the length of the list
list_length = len(list)
# 2\. List with length less than is already sorted
if list_length == 1:
return list
# 3\. Identify the list midpoint and partition the list into a left_partition and a right_partition
mid_point = list_length // 2
# 4\. To ensure all partitions are broken down into their individual components,
# the merge_sort function is called and a partitioned portion of the list is passed as a parameter
left_partition = merge_sort(list[:mid_point])
right_partition = merge_sort(list[mid_point:])
# 5\. The merge_sort function returns a list composed of a sorted left and right partition.
return merge(left_partition, right_partition)
- 将初始列表的长度存储到变量 list_length 中。
- merge_sort 函数返回一个排序后的列表。对长度为 1 的列表进行技术排序;因此,列表被返回。
- 对于任何长度大于 1 的列表,下一步是将列表划分为一个左 _ 分区和一个右 _ 分区。这是通过将初始列表的中点传递给 merge_sort 函数来实现的。
- 回想一下,分治算法是递归的。在合并排序算法实现中,递归发生在列表的分解中。为了确保所有分区都被分解成各自的组件,调用了 merge_sort 函数,并且列表的一个分区部分作为参数传递。
- merge_sort 函数返回一个由排序后的左、右分区组成的列表。
合并排序算法的第二部分涉及到列表中元素按照指定顺序的实际排序。对于这个用例,原始列表中元素的排序顺序是升序。
征服和组合组件—作者的图像
# 6\. takes in two lists and returns a sorted list made up of the content within the two lists
def merge(left, right):
# 7\. Initialize an empty list output that will be populated with sorted elements.
# Initialize two variables i and j which are used pointers when iterating through the lists.
output = []
i = j = 0
# 8\. Executes the while loop if both pointers i and j are less than the length of the left and right lists
while i < len(left) and j < len(right):
# 9\. Compare the elements at every position of both lists during each iteration
if left[i] < right[j]:
# output is populated with the lesser value
output.append(left[i])
# 10\. Move pointer to the right
i += 1
else:
output.append(right[j])
j += 1
# 11\. The remnant elements are picked from the current pointer value to the end of the respective list
output.extend(left[i:])
output.extend(right[j:])
return output
- merge 函数接收两个列表,并返回一个排序列表,该列表由作为参数传递给 merge 函数的两个列表中的元素组成。
7.名为输出的空列表被初始化。该列表由已排序的元素填充。同样,初始化两个变量, i 和 j,,它们在遍历列表时用作指针。
8.我们用 while 循环遍历这两个列表,当指针 i 和 j 分别小于左右列表的长度时,该循环执行其主体。
9.在 while 循环体中,比较每次迭代中两个列表中每个位置的元素。输出列表在相应指针所在列表的当前索引处填充较小的值。
10.在输出列表被填充后,适当的指针将被递增。本质上,我们将指针向右移动,或者移动到列表中的下一个值。
11.在 while 循环完成后,或者如果整个循环条件不满足,则用每个列表的剩余内容填充输出列表。剩余元素从当前指针值拾取到相应列表的末尾。
def run_merge_sort():
unsorted_list = [2, 4, 1, 5, 7, 2, 6, 1, 1, 6, 4, 10, 33, 5, 7, 23]
print(unsorted_list)
sorted_list = merge_sort(unsorted_list)
print(sorted_list)
run_merge_sort()
最后,我们创建一个函数来初始化一个无序列表。变量 sorted_list 保存 merge_sort 函数的结果。
下面是完整的代码。
为什么归并排序快?
在最坏的性能情况下,执行合并排序算法所需的计算时间量的时间复杂度为 O(n * log(n)) ,,其中 n 是列表中要排序的元素的数量。
使用合并排序算法排序的初始列表的每个分解的子部分花费线性时间与另一个子部分合并。这篇来自 Khanacademy 的文章深入解释了合并排序算法的时间复杂度和分析。
与其他排序算法相比,合并排序对于较大的数据集通常更快。这是因为合并排序减少了对列表进行排序的步骤。大多数其他排序算法,如插入排序 (O(n )) 和冒泡排序都需要很多步骤来对列表进行排序。例如,使用插入排序算法对包含 40 个元素的列表进行排序将需要 1600 步(40*40),但是使用合并排序,对列表进行排序大约需要 64 步。
结论
一开始,处理算法可能会令人望而生畏,尤其是对初学者而言。我的做法是理解插入排序、归并排序等简单算法;之后,处理更复杂的算法。
对于机器学习从业者来说,开始扩展他们对算法及其实现的领域理解的旅程将会实现大量的好处。首先,您开始考虑您在日常工作中编写的程序的执行时间和性能,这通常有助于在与您开发的产品交互时获得积极的最终用户体验。
要联系我或找到更多类似本文的内容,请执行以下操作:
如何实现用于事件时间分析的随机森林、SVM 和梯度增强模型
带示例的循序渐进教程
乔纳森·彼得森在 Unsplash 上拍摄的照片
在这篇文章中,我们将比较几种先进的方法来进行时间-事件分析(又名生存分析)。具体来说,我们将涵盖随机森林,梯度推进和 SVM 生存模型。
时间-事件分析是帮助我们分析问题的重要方法,其中我们的研究问题有时间成分。顾名思义,当我们对理解时间和某个事件之间的关系感兴趣时,就会用到这些。
虽然我们通常在生物研究(即临床、制药)中发现这些研究,但它同样适用于其他领域(即工程、商业、营销)。一些例子包括:
- 疾病复发的时间
- 死亡时间
- 下次购买的时间
- 取消会员资格的时间
事件时间分析是一种修改的回归任务,但它是独特的,因为一部分数据是不完整的(经过审查的)。要理解这意味着什么,请看下图。
图 1 —事件发生时间(存活)分析。灰色数字代表一个事件。红色数字完成研究,无事件。黄色图没有完成研究,但也没有事件。(图片作者)
假设这是一项临床试验,观察某种药物对肿瘤复发时间的疗效。两个灰色数字代表观察到的事件(新肿瘤),一个在 3 年后,另一个在 6.5 年左右。红色数字完成了这项研究,历时 10 年,没有观察到任何事件。最后,黄色的数字没有完成研究。他们在 5 年时从研究中丢失,但没有观察到任何事件。
在本例中,灰色数字代表未经审查的数据,而红色和黄色数字代表经审查的数据。删失数据指的是仅部分已知的数据。具体来说,红色数字完成了 10 年的研究。虽然没有观察到肿瘤复发,但这并不意味着两周后没有新的肿瘤形成(希望没有)。
黄色数字在 5 年时从研究中移除,在他们参与研究期间没有观察到新的肿瘤。它们为什么被移除?也许有不可接受的副作用。也许目标死于其他原因。另一种看待受审查参与者的方式是,他们在最后一次随访前没有发生任何事件。
数据
对于今天的例子,我们将使用 NKI 乳腺癌数据集。你可以在这里找到数据集。完整的数据集包括 272 名乳腺癌患者(行)和 1570 列。出于时间的考虑,我选择将列数减少到 11 个预测变量和 2 个结果变量(事件和存活时间)。下面,你可以预览我们的数据集。
图 2 —我们分析中使用的 NKI 乳腺癌数据集的选择。
有关数据集中变量的快速描述,请参考下图。
图 3——数据集中变量的描述
在我们开始之前,分割数据与大多数机器学习模型略有不同,因为我们有 2 个目标变量。这可以使用下面的代码来完成。
很重要的一点是,y 变量的格式应该使事件指示器为布尔型,时间变量为浮点型。点击查看更多细节。
图 4 —为事件时间分析创建数据集
现在我们可以建立我们的时间到事件模型,看看哪一个表现最好!
1.随机森林生存模型
如果你有兴趣了解更多关于随机森林模型是如何工作的,我建议你看看 scikit-survival 文档。也有很多关于这个主题的帖子,其中一个你可以在这里找到。
正如您在下面看到的,运行算法非常简单。在导入您决定评估的模型和指标之后(块 1),您可以创建您的模型(块 2)。
将模型与数据拟合后,您可以发现它在测试集上的效果如何。事件时间分析最常用的指标是 c 指数(和谐指数)。这是预测风险值与观察时间之间的等级相关分数。你可以在这里找到更多关于这个的信息。
我们的结果提供了 0.77 的 c 指数,这可以解释为一个很好的模型。仅仅作为一个引子,c 指数得分为:
- 在预测结果方面,0.5 并不比随机概率更好
-
0.7 表示模型良好
-
0.8 表示强模型
图 5 —实现随机森林生存模型
尽管 c-index 为我们提供了整个模型的信息,但考察模型在不同时间点的表现也很有趣。为此,我们可以使用 cumulative_dynamic_auc 指标,并使用下面的代码将其可视化。
图 6——如何计算和绘制依赖于时间的 AUC
在下图中,我们看到,尽管该模型在整个研究中表现良好(最小 auc = 0.72),但在第 2-4 年和第 11-12 年出现峰值,auc 值> 0.80。如果我们试图解释这一点,我们将不得不更多地查阅科学文献,以帮助我们理解哪些因素可能影响不同时间点的存活率。
图 7-随机森林生存模型的时间相关 AUC 分数。(图片作者)
现在我们已经有了模型,是时候找出不同的特性是如何影响我们的模型的了。有几个包可供选择,我从中选择了 ELI5 。下面,您将找到为您的模型实现这一点的必要代码。
图 8 —使用 eli5 的特性重要性
结果表明,雌激素受体表达(esr1)、严重性(等级)和大小(直径)、脉管系统结构的侵袭和年龄是我们模型中的前五个区别特征。有趣的是,在这项研究中,化疗或激素疗法并不能很好地区分那些活着或死去的人。
图 9-随机森林生存模型的特征重要性
2.梯度增强模型
梯度增强模型与随机森林模型有一些相似之处,因为它们都是集合方法。他们将多个模型的预测结合起来,以改进整体模型。区别在于它们是如何组合的。
虽然随机森林独立地适合许多不同的树并平均它们的预测,但是梯度增强模型是相加的。在后者中,来自一个模型的加权特征以连续的方式被输入到下一个模型中。更多信息,请查看 scikit survival 文档。你也可以看看这篇文章,它详细解释了增压和装袋。
与随机森林生存模型相似,它从导入模型开始(参见下面的代码)。有三种损失函数可用,在这里有更详细的描述。我选择了考克斯比例风险模型(coxpf),这是默认的损失函数,但你可以尝试所有这些方法,看看你的结果有什么不同。
图 10 —实施梯度提升生存模型
整个模型的 c 指数得分为 0.703,低于使用随机森林模型获得的得分。在下图中,我们可以看到,随着时间的推移,区分存活率变得越来越差,到最后略有改善。
图 11-梯度增强生存模型的时间依赖性 AUC 评分。(图片作者)
尽管我们的模型在整体和时间相关拟合方面存在差异,但对于前 5 个特征,特征重要性(见下文)具有几乎相同的轮廓,尽管顺序不同。
图 12 —梯度增强存活率模型的特征重要性
3.生存支持向量机
作为标准支持向量机(SVM)的扩展,生存 SVM 基于我们的特征和生存之间的线性(或非线性)关系来分离类别。如果你想了解更多,你可以参考 scikit survival 文档,或者查看这个深入探究本质。在这一节中,我们将探索线性 SVM 和核 SVM 生存模型,看看哪一个对我们的数据集表现最好。
线性生存 SVM
在下面的代码中,我导入了必要的模型和指标。接下来,定义线性生存 SVM,然后拟合我们的数据。该模型产生了 0.716 的 c 指数,这比我们的梯度增强模型有更好的性能,但不是随机森林模型。
生存 SVM 的局限性之一是我们无法将这些模型与随机森林或 SVM 生存模型进行比较。这是因为它们缺乏时间-事件分析的“标准”指标,如生存函数和累积风险函数。这限制了我们与 c 指数得分的比较。
图 13 —实现线性生存 SVM
前 5 个区别特征再次与其他模型相似。尽管顺序不同,唯一的区别是化疗取代了前 5 名中的肿瘤大小。
图 14-线性生存 SVM 的特征重要性
内核生存 SVM
因为不能总是使用简单的线性方法来实现分类,所以可以使用核 SVM 来考虑我们的特征和生存之间更复杂的关系。下面的代码是我们如何实现内核生存 SVM 的,它导致了 0.766 的 c-index 分数。这与我们的随机森林生存模型(0.768)表现一样好(精确到小数点后两位)。
图 15 —实现内核存活 SVM
你可能在上面的代码中注意到内核是“线性”的。这是默认的内核,但是你可能会问这和线性 SVM 有什么不同?我鼓励你阅读我上面链接的文章来了解更多细节,但是作为一个快速的解释,请考虑下图。
如果我问你我们如何线性分割左边图像中的蓝色和黑色球,你会怎么说?不可能吧?如果我们处理的是二维空间,那就是真的。
如果我们增加另一个维度会发生什么?在右图中,您可以看到球在三维空间中旋转,我们现在可以看到 y 轴在垂直方向,z 轴在水平方向。我们可以在这里想象 x 轴,就好像我们在你的屏幕上插了一根棒(请不要这样做)。
当我们这样做的时候,很明显,至少对于这组球,我们可以用一个沿着 x 和 z 轴的平面来线性分离它们。这被称为超平面。然而,我们可以想象一张纸,而不是一条线。
使用核 SVM,可以将我们的特征映射到可以被(n 维-1)超平面分开的 n 维空间中。这就是 SVM 核中的线性核与线性 SVM 的不同之处。
图 16——通过将特征从 2 维空间(左)映射到 n 维空间(右,3d ),并找到最大程度分隔类别的超平面(n 维-1 ),核存活 SVM 可以找到一种对患者存活进行分类的方法。(图片由作者提供)
查看特征重要性权重(见下文),我们得到了与其他模型相同的前 5 个特征。尽管在比较我们的模型时 c 指数得分不同,但这些结果让我们在试图从我们的数据中看到更大的画面时更有信心。
如果我试图总结我们从这些分析中学到的东西,那就是患者的生存更多地取决于肿瘤的发展、esr1 受体的表达、年龄和血管浸润,而不是任何种类的治疗(化疗、激素治疗或乳腺切除)。简而言之,我认为早期诊断是我们数据集中患者生存的最佳指标。
图 17 —内核存活 SVM 的特征重要性
4.使用 GridSearch 优化您的模型
到目前为止,我们已经比较了随机森林、SVM 和梯度增强事件时间模型来预测我们数据集中乳腺癌患者的生存率。我认为这将是有益的,通过一个例子,我们可以尝试优化我们的一个模型的结果。如果您想了解如何优化我们今天介绍的其他模型,请查看我们的笔记本。
让我们看看是否可以改进我们的内核 SVM。您可以手动完成这项工作,也可以实现网格搜索,在网格搜索中,您可以一次运行多个模型。你可以在这里找到更多关于这项技术的信息。
重要的是要记住,这是一个计算量很大的过程,所以要确保在这个过程中你有事可做。在下面的代码中,你会看到我们从 sklearn 导入了 GridSearchCV 和 ShuffleSplit 。
在第一个模块中,我们定义了指标,因此我们可以在以后将它添加到模型参数中。下一行定义了我们的基本模型,它与我们上面运行的模型相同。
带有shuffles split的行表示我们想要100 个随机分裂,其中我们训练 80%的数据,并验证 20%的数据。
param_grid 是每次运行模型时将进行的调整,每次调整的结果将进行比较,以查看哪一个执行得最好。值得注意的是,我选择只调查修改 alpha 超参数的影响,这是对损失函数的惩罚,类似于岭或套索回归的工作方式。选择的范围 I 是从 2e-12 到 2e+12,步长为 2(即 2e-12、2e-10、2e-8……)。
最后,定义模型并使其适合您的数据。请记住,当您这样做时,您是在整个数据集(X,y)上运行它,因为您已经使用shuffles split定义了分割。
结果实际上给了我们一个更差的模型,c 指数得分为 0.713。这是你在使用网格搜索时可能已经注意到的。据我所知,这主要是由于数据集分割的随机性造成的。我们还获得了一些关于最佳 alpha 分数的信息,该分数为 0.0625。这不同于默认值 1.0。
当我将 alpha 值插回到我们的原始模型中时,我最终得到了完全相同的分数(0.766),表明我们的原始结果得到了优化。当然,如果我们改变内核等其他超参数,情况可能会有所不同。如果你感兴趣的话,我把这些放在我的笔记本里,上面有链接。但是,为了不让你担心,结果并没有从 0.766 提高。
图 18——使用 GridSearch 尝试并优化我们的内核生存 SVM
摘要
在今天的帖子中,我们介绍了 3 种不同的模型,用于研究乳腺癌存活率的数据集的时间-事件分析。具体来说,我们发现随机森林和核 SVM 生存模型优于梯度增强生存模型。
我们还研究了如何通过在超参数 alpha 上运行网格搜索来优化我们的内核 SVM 模型。我鼓励您尝试在网格搜索中添加额外的超参数。事实上,这是确保我们的模型得到优化的唯一方法。然而,如果你这样做了,也许可以通宵运行,这样你就不用等待结果了。这有点像观察草的生长。
我希望你喜欢今天的帖子。玩你的数据玩得开心!
如何使用元数据信息在 Python 中实现 Sentinel 2 图像的日光闪烁检测
由 Unsplash 上的 Savvas Kalimeris 拍摄的照片
介绍
光学遥感分析依赖于理解地面物体对太阳辐射的吸收和散射过程。如果我们测量太阳入射辐射和表面的辐照度,我们将能够估计表面的反射率。不同波长的反射率是理解我们观察的目标的关键,因为每种材料都会根据所考虑的波长不同地反射和吸收能量。图 1 显示了水、土壤、植被和岩石等不同材料的平均反射光谱。
图 1:一些地表物质的广义反射光谱。来源:https://www . usna . edu/Users/oceano/pguth/MD _ help/html/ref _ spectra . htm
然而,一些不希望的效应会干扰观察到的反射率。这些效应特别存在于空间传感器中,因为它们位于地球大气层之外,并且存在云、水蒸气等。不断吸收和散射电磁能量。另一个影响反射率的重要因素是日照。
太阳辉光是由卫星或其他传感器以相同角度观察的一些表面上入射辐射的镜面反射(或类似镜子)引起的。在这些情况下,传感器将不会测量被观察目标的特征,而是与发射源的光谱的混合物。这可能会给许多遥感应用带来问题。
在火灾探测中,例如,Giglio 等人。艾尔。(2003)[1]表明,阳光对一些目标如水体、湿土、卷云甚至裸土的影响会导致错误的火警。在水资源中,这种影响尤其重要,因为水面受太阳光线的影响最大(图 2)。
图 Agean 海中的太阳闪烁示例。来源:美国宇航局地球天文台(https://Earth Observatory . Nasa . gov/images/84333/the-science-of-sunglint)
因此,对于大多数遥感应用来说,闪烁校正或至少闪烁识别是一项重要的任务。
方法学
因为我们的目标不是深入挖掘电磁辐射理论,而是展示如何使用 Python 和 Sentinel 2 元数据信息来估计场景中出现的太阳辉光。为了在水生系统中完全消除闪烁,我推荐来自 Harmel et 的论文。艾尔。(2018)[2].相反,我们将展示如何使用 Giglio 等人提出的方法来预测太阳辉光的存在。艾尔。(2003).在这种方法中,我们必须根据下面的等式,检查获取图像时太阳和视角(天顶和方位角)之间的关系:
在这个等式中,下标 v、s 和 g 代表观察角、太阳角和闪烁角,θ指天顶角,φ是太阳和传感器之间的方位角差异,如图 3 所示。
图 3:用于计算日照的太阳和视角的表示。图片作者。
最终的闪烁角度值将由Arccos(θg)
获得。根据作者的说法,由此产生的闪烁角是太阳辉光出现的预测因子。闪烁角度值越小,传感器和太阳越“对齐”,出现日光闪烁的可能性就越大。
输入数据
我们计算所需的所有信息都包含在 Sentinel 的 2 元数据文件MTD_TL.xml
中。该文件位于颗粒子目录中。那么,我们来看看吧。如果我们第一次打开这个文件,它可能会让人不知所措,但是如果我们寻找Sun_Angles_Grid
标签,我们可以找到我们需要的关于太阳位置的所有信息:
图 4:哨兵 2 元数据中呈现的太阳角度。
请注意,它是以 5000 米(23x23 网格)的步长呈现的,因此这并不完全是“像素级”信息,但我们可以在之后对其进行插值以匹配像素分辨率。我们将从通过**lxml**
包从元数据中读取太阳角度开始。最后,我们将使用一个 pandas 数据框架来实现可视化。
代码输出:
( 0 1 2 3 4
0 19.5389 19.4952 19.4514 19.4076 19.3638
1 19.5431 19.4994 19.4556 19.4119 19.3681
2 19.5474 19.5037 19.4600 19.4162 19.3725
3 19.5518 19.5081 19.4644 19.4207 19.3769
4 19.5564 19.5126 19.4689 19.4252 19.3815,
0 1 2 3 4
0 80.1116 80.1092 80.1067 80.1041 80.1014
1 79.9860 79.9833 79.9804 79.9775 79.9745
2 79.8605 79.8574 79.8543 79.8510 79.8477
3 79.7350 79.7316 79.7281 79.7246 79.7209
4 79.6095 79.6058 79.6021 79.5982 79.5942)
如果我们与 XML 文件中的角度进行比较,我们可以看到我们的数据被正确加载。我们可以对传感器做同样的事情,但如果你仔细观察,你会发现我们有许多不同的传感器,它们有不同的角度。因为我们想要对我们的场景进行平均闪烁预测,我们将使用每个传感器的平均角度。为此,我们将创建一个通用函数,该函数读取给定节点名称的数组并返回其平均值,以检索传感器的平均天顶和方位角。最后,我们将使用 matplotlib 绘制角度。
代码输出:
注意:我们可能会看到,在探测器转换时,观察方位角有一些问题。关于这个问题的讨论线索可以在这里找到(https://forum . step . esa . int/t/sentinel-2-viewing-angles-interpolation-on-per-pixel-basis/2776/4
计算闪烁角
现在我们已经有了所有需要的角度,是时候计算闪烁角了。首先我们应该把它们都转换成弧度(numpy 三角函数使用的单位)。然后,最后的计算非常简单。为了可视化结果,我们将创建一个带注释的热图及其值。
代码输出:
叠加闪烁角和 RGB 图像
要可视化图像中更容易出现日光反射的部分,最好的方法就是将闪烁角网格与图像的 rgb 表示叠加。为了获得卫星图像的 rgb 表示,我们将使用**rasterio**
包来读取红色、绿色和蓝色波段,正如 Python for Geosciences:Satellite Image Analysis 系列中所解释的那样(此处为)。
代码输出:
现在,我们可以使用matplotlib
中的imshow
函数叠加两个网格,并正确调整范围参数和 alpha 来应用透明度。
ax.imshow(rgb*4, alpha=0.6, extent=(-0.5, 22.5, 22.5, -0.5))
fig
结论
在这个故事中,我们讨论了如何根据 Giglio (2003)提出的 en Sentinel 2 元数据中可用的太阳和视角来估计太阳辉光事件。有了这一信息,就有可能调整受太阳辉光存在影响的任何遥感应用。例如,这个简单的太阳辉光预测目前正在**waterdetect**
python 包(此处为 )[3]中实现,作为一种在预计有太阳辉光时调整最大阈值水平的方法。它还可以用于云探测器,以避免水被识别为云,或者用于火灾探测,以防止误报。
如果你对我们关于这个主题的评论有任何疑问,请不要犹豫,写在评论里。如果你想收到更多这样的故事,别忘了关注我。所以敬请期待,下篇故事再见。
保持联系
更多这样的故事:https://cordmaur.medium.com/
如果你喜欢这篇文章,并想继续无限制地阅读/学习这些和其他故事,考虑成为 中等会员 *。你也可以在 https://cordmaur.carrd.co/*查看我的作品集。
http://cordmaur.medium.com/membership
参考
[1]吉廖,l;Descloitres,j;司法总监;一种用于 MODIS 的增强型上下文火灾探测算法。环境遥感 2003 , 87 (2),273–282。https://doi . org/10.1016/s 0034-4257(03)00184-6。
[2]t .哈梅尔;横山雅美,m。t .托尔莫斯;雷诺,新泽西州;来自 SWIR 波段的多光谱仪器(MSI)-SENTINEL-2 图像在内陆和海洋水域上空的 Sunglint 校正。环境遥感 2018 , 204 ,308–321。https://doi.org/10.1016/j.rse.2017.10.022。
[3]科尔代罗;马丁内斯,j .-m;Sentinel-2 图像多维层次聚类的自动水检测以及与水平 2A 处理器的比较。环境遥感2021253,112209。https://doi.org/10.1016/j.rse.2020.112209。
如何用一行代码导入所有 Python 库
计算机编程语言
了解如何使用 PyForest 导入 40 个最重要的库
图片来源: Unsplash
如果你是一名数据科学家,或者正在学习如何成为一名数据科学家,你应该知道启动一个新的 Jupyter 笔记本项目的流程。您导入库,使用其中一些库,然后开始工作。由于忘记导入另一个库,您可能会在某个时候收到一条错误消息。您可以停止正在进行的工作,导入特定的库,然后重新开始工作。根据项目的大小,这种情况可能会发生几次。你也很有可能在 Google 上查过如何导入 matplotlib.plot。
是的,您可以使您的生活变得更简单,从另一个项目中复制并粘贴您将需要的所有包,但是如果我告诉您,您可以用一行代码导入您所有的基本包并专注于重要的东西,那会怎么样呢?这个解决方案叫做 PyForest。PyForest 是懒惰的舶来品。顾名思义,它为 Python 导入了最流行的数据科学库。PyForest 开发者的目标是导入 99%最常用的库。
听起来很棒,对吧?然而,如果你不使用所有的库呢?你为什么要全部进口?好消息是,PyForest 只会在您使用它们时导入这些库。出于这个原因,没有理由担心你的电脑变慢。因为除非您使用它们的函数或方法,否则不会导入这些库,所以这是一个比从其他项目中复制和粘贴代码更好的解决方案。
安装
现在,如何使用这个神奇的解决方案?越简单越好。Pyforest 可以从 Pypl 安装。您可以在终端上键入以下命令:
pip install pyforest
如果你在 Jupyter 笔记本上,你可以输入:
!pip install pyforest
要导入,您可以使用
import pyforest
或者
from pyforest import *
这些选项应该以同样的方式工作,但是你知道,Python 有时会很棘手。如果一种方法行不通,你可以尝试另一种方法。瞧,你有一些最重要的图书馆,你可以专注于你的项目。
但是实际上导入的是什么呢?
好消息是,这个列表很长,举几个例子,它包括 Pandas、NumPy、Matplotlib、Seaborn、Sklearn、Keras、NLTK,这个列表还在继续。最酷的部分,在我看来?他们使用的别名是数据科学家的标准约定,比如导入 Pandas 作为 pd,导入 NumPy 作为 np。生活可以毫不费力!
键入 lazy_imports() 可以找到完整的库列表,您将能够看到完整的列表。
作者图片
向 PyForest 添加更多库
正如我们所看到的,有大量不同的库,这应该涵盖了您的大部分项目。但是,缺少一些基本的库。我们可以通过手动添加您最喜欢的库来快速解决这个问题。您可以尝试以下步骤来添加 PyForest 最初没有提供的库。
首先,到您的终端键入:
pip show pyforest
您将看到终端将返回 PyForest 目录的位置。如果使用的是 macOS,可以键入 Command+Space 粘贴目录地址。找到名为 _imports.py 的文件,添加您需要的所有库。
作者图片
搞定了。现在,您将拥有所有的收藏夹库,并担心什么对您的项目至关重要。我创建了一个微型项目——,你可以在这里找到它——在这里我展示了我如何使用 PyForest 而没有从其他库导入任何东西。请记住,如果您想要运行 Jupyter 笔记本,您必须在您的环境中安装 PyForest。
如何使用 Python 将 CSV 数据导入 Quickbooks
免费使用 Python 将日记条目电子表格导入 Quickbooks Online
如果您处理过将数据导入 Quickbooks Online 的问题,您就会知道这是一个繁琐的过程。事实上,这是一个非常麻烦的问题,像 SaasAnt 这样的公司已经开发了专门用于自动化这一过程的产品。
在本文中,我将向您展示如何利用 hotglue 的目标——quickbooks将 CSV 数据导入 Quickbooks。
来源: unDraw
第一步:格式化你的电子表格
首先,我们必须把我们的日志条目电子表格放在一个 target-quickbooks 包能够理解的格式中。将文件命名为JournalEntries.csv
列应该是:
Transaction Date - The day the transaction occurredJournal Entry Id - The ID of the Journal Entry (becomes the DocNumber in QBO)Account NumberAccount Name - If you aren't using Account Numbers, it will use this column to find the AccountClassCustomer NameAmount - Must be a POSITIVE numberPosting Type - Either "Debit" or "Credit"Description - Description for the entryCurrency - Optional. Allows you to specify a different currency code
您的最终文件应该如下所示:
示例 JournalEntries.csv 文件
步骤 2:设置我们的环境
创造虚拟
为了避免依赖地狱,我强烈建议在虚拟环境中运行这个例子。
# Create the virtual env
$ python3 -m venv ~/env/target-quickbooks# Activate the virtual env
$ source ~/env/target-quickbooks/bin/activate# Install the dependencies
$ pip install git+[https://github.com/hotgluexyz/ta](https://github.com/hotgluexyz/target-csv.git)rget-quickbooks.git# Create a workspace for this
$ mkdir quickbooks-import# Enter the directory
$ cd quickbooks-import
这些命令可能因您的操作系统和 Python 版本而异。
步骤 3:配置目标
获取 OAuth 凭证
首先,你需要 Quickbooks OAuth 证书。Quickbooks 已经很好地记录了这个过程,所以我假设你可以遵循这个指南。如果您不熟悉如何完成 OAuth 授权流程,您可以遵循 hotglue 文档。
创建目标配置
现在我们必须创建一个目标配置。这将指定我们的 OAuth 凭证,它应该在哪里寻找我们的 CSV 文件,以及一些 QBO 特定的设置。下面是一个配置示例:
{
"client_id": "secret_client_id",
"client_secret": "secret_client_secret",
"refresh_token": "secret_refresh_token",
"sandbox": true,
"realmId": "123456789",
"input_path": "tests"
}
填写您的凭证,并将其保存到本地目录中名为config.json
的文件中。如果你没有使用沙盒 QBO 帐户,请确保将sandbox
设置为false
。
保存格式化的电子表格
将您的JournalEntries.csv
文件保存到您在input_path
中为我指定的文件夹,即tests
**注意:**目标希望文件被命名为确切地说是 JournalEntries.csv
步骤 4:将数据发送到 Quickbooks Online
现在我们可以运行目标,并将我们的数据发送到 QBO!
target-quickbooks --config config.json
如果在发送日志条目时出现任何错误,您将直接在控制台中看到这些错误。成功的导入应该如下所示:
target-quickbooks - INFO - Converting MAR21 REV_REC (2)...
target-quickbooks - INFO - Loaded 1 journal entries to post
target-quickbooks - INFO - Posting process has completed!
瞧啊。我们在 Quickbooks 中看到新的日志条目:
在 Quickbooks Online 中导入日记条目
结论
后续步骤
如果你想部署这个管道,或者想在你自己的产品中提供这个功能,请查看 hotglue 。
希望这能节省你一些时间。目前,该目标仅支持日志条目,但对发票等的支持将很快推出。感谢阅读!
使用 Pandas DataFrame 无错导入 CSV 文件
*EmptyDataError。*听着耳熟?然后继续关注我的一些提示,以避免在使用 Pandas DataFrame 加载 CSV 文件时出现任何形式的错误。
ata 处于机器学习管道的中心。为了充分利用算法的全部能力,必须首先对数据进行适当的清理和处理。
数据清理/争论的第一步是加载文件,然后通过文件的路径建立连接。有不同类型的分隔文件,如制表符分隔文件,逗号分隔文件,多字符分隔文件等。定界符指示数据如何在列内被分开,是通过逗号、制表符还是分号等。最常用的文件是制表符分隔和逗号分隔的文件。
在整个 ML 管道中,数据争论和清理占据了数据分析专业人员大约 50%到 70%的时间。第一步是将文件导入熊猫数据框架。然而,这一步是遇到最多的错误。人们经常在这个特定的步骤中陷入困境,并遇到类似这样的错误
empty data 错误:文件中没有要解析的列
常见错误的出现,主要是由于:
提到了错误的文件分隔符。
文件路径格式不正确。
错误的语法或分隔符用于指定文件路径。
提到了错误的文件目录。
文件连接未形成。
数据分析专业人员无法承受在已经非常耗时的步骤中耗费更多时间。在加载文件时,必须遵循某些重要的步骤,这些步骤将节省时间,并减少在大量信息中寻找特定问题的解决方案的麻烦。因此,我列出了一些步骤,以避免在使用 pandas DataFrame 导入和加载数据文件时出现任何错误。
读取和导入 CSV 文件并不像人们想象的那么简单。一旦你开始加载你的文件来建立你的机器学习模型,这里有一些必须记住的提示。
1。 在设置中检查您的分色类型:
用于 Windows
- 转到控制面板
- 点击区域和语言选项
- 点击区域选项选项卡
- 点击自定义/附加设置
- 在“列表分隔符”框中键入逗号(,)
- 单击“确定”两次以确认更改
注意:这只适用于“十进制符号”也不是逗号的情况。
用于 MacOS
- 前往系统偏好设置
- 点击语言和地区,然后进入高级选项
- 将“小数分隔符”更改为以下情况之一
对于 MacOS,如果小数点分隔符是句点(。)那么 CSV 分隔符将是逗号。
如果小数点分隔符是逗号(,),那么 CSV 分隔符将是分号。
2。 查看文件的预览:
还可以检查文件的预览,可以看到数据是如何被分隔的,是通过制表符分隔还是逗号分隔。人们可以在 Jupyter notebook 或 Microsoft Excel 中检查预览。
3。 正确指定所有参数:
看了预览并检查了为您的计算机指定的分色后。我们现在必须填充正确的参数,这些参数需要在“pd.read_csv”函数中根据文件类型作为分隔符(制表符分隔等)、空白标题(在这种情况下 header= none)等。
Pandas.read.csv 有许多参数,需要考虑这些参数才能正确读取该文件。
pandas . read _ CSV(file path _ or _ buffer,sep= < object object >,delimiter=None,header='infer ',names=None,index_col=None,usecols=None,squeeze=False,prefix=None,mangle_dupe_cols=True,dtype=None,engine=None,converters=None,true_values=None,false_values=None,skipinitialspace=False,skipfooter=0,nrows=None,na,lineterminator=None,quotechar= ’ " ',quoting=0,doublequote=True,escapechar=None,comment=None,encoding=None,dialect=None,error_bad_lines=True,warn_bad_lines=True,delim_whitespace=False,low_memory=True,memory_map=False,float_precision=None,storage_options=None)
这是所有论点的清单,但我们最关心的是以下几点:
sep:这指定了数据值之间的分隔类型。默认值为“,”。在检查了预览和系统设置之后,我们知道了文件的类型。最常见的分隔符/分隔符类型是逗号、制表符和冒号。因此,它将被指定为 sep= ‘,’,sep= ’ ',sep = ';'这告诉 pandas DataFrame 如何将数据分布到列中
如果问题仍然存在,请检查需要输入的任何必需参数。那么问题可能出在文件路径上。
4。 检查文件路径:
此参数用于描述特定数据文件的路径对象或类似文件的对象,基本上是其位置。输入是一个字符串。也可以输入 URL,有效的方案有 HTTP、FTP、s3、gs 和 file。
文件位置应被正确提及。大多数情况下,人们不知道工作目录,结果提到了错误的文件路径。在这种情况下,我们必须检查工作目录,以确保正确描述了指定的文件路径。编写如下所示的代码来检查工作目录。
作者图片
这将打印工作目录。然后,我们必须只指定你的工作目录后的位置。
我们也可以使用下面一行代码来改变工作目录。指定新目录后,我们必须指定路径。
作者图片
5。 检查用于指定文件位置的分隔符:
通常在改变工作目录时也会出错。这是因为没有按照正确的语法编写分隔符。
作者图片
首先。使用以下命令检查分隔符。
作者图片
然后,仅在目录位置的开头使用分隔符,而不要在结尾使用。请注意,这种分隔符(/)语法规范适用于 MacOS,但可能不适用于 Windows。
作者图片
现在,在正确指定位置后,我们已经对其进行了更改。
现在我们必须指定路径。因为我们熟悉工作目录。我们只需指定工作目录之后的位置。
如果你的文件在工作目录中,那么只需要如下所示的文件名。
作者图片
但是,如果您的文件存在于某个其他文件夹中,那么您可以在工作目录后指定后续文件夹,例如,您的工作目录是“/Users/username”,而您的文件位于“documents”中名为“huma”的文件夹中,那么您可以编写以下代码:
*path = ‘Documents/huma/filename.csv’*
6。 检查文件是否在路径:
现在使用下面的代码检查您的文件是否存在于描述的路径中。我们得到的答案要么是“对”,要么是“错”。
作者图片
7。 打印要钩稽的文件数据:
现在,我们可以使用下面的代码检查我们的数据文件是否已经正确加载。
作者图片
有了这些提示,你可能不会在使用 Pandas DataFrame 加载 CSV 文件时遇到任何问题。
如何改进带注释的 D3.js 图形
数据可视化
一个简短的教程,将简单的 D3.js 图形转换成带有上下文的精彩图形
作者图片
数据驱动库(D3)是用来构建图表和绘画的最著名的 Javascript 库之一。关于图形,有许多网站提供现成的代码,如 D3 图库和从数据到视觉。然而,这些网站只提供基本代码,没有提示或细节来突出数据背景或见解。
在本教程中,我利用了 d3-annotation 库来扩展由 D3 Graph Gallery 提供的两个简单的图形,以使它们对普通读者更具可读性:
- 简单的条形图
- 简单的折线图
从来没有人因为一个数字而做出决定。他们需要一个故事。(丹·卡尼曼)
所有代码都在我的 GitHub 库中。
条形图
第一个例子是由 D3 图形库提供的简单条形图,如下图所示:
作者图片
该图尝试绘制以下数据集:
作者图片
从数据上下文的角度来看,该图至少提出了两个问题:
- 栏中的国家没有排序,因此很难理解哪个国家比其他国家表现得更好。
- 图表上没有焦点,因此读者应该想象图表想要表达什么。
为了改进图表,我将这两个问题分开处理。
关于条形图的顺序,可以通过根据值字段将所有结果按降序排列来改进图表。在 D3 中,这可以通过sort
功能实现:
data.sort(function(x, y){return d3.descending(x.Value, y.Value);})
该函数需要一个函数作为输入参数,该函数指定用于对每对字段进行排序的规则。
关于图形没有焦点的事实,我可以将焦点放在单个国家,例如法国,并用不同于其他国家的颜色来表示它。此外,我可以将其他国家的颜色改为灰色,以减少其他国家产生的噪音。这可以通过为每个国家生成的矩形指定特定颜色来实现:
svg.selectAll("myRect")
// other code
.attr("fill", function(d){ if (d.Country == 'France') return "#cc0000" ; else return "#a3a3c2"})
所描述的效果提供了下图:
作者图片
现在,我可以想象描述为什么法国在排名中达到了第三的位置。也许这是由于它去年强有力的营销政策。因此,我可以向图表添加注释,解释上下文。
首先,我通过指定文本(在note
字段中)、颜色和位置(通过x, y, dx
和dy
键)来构建注释:
const annotations = [
{
note: {
label: "Thanks to its marketing policy, in 2021 France has reached the third position.",
title: "France product sales",
wrap: 200, // try something smaller to see text split in several lines
padding: 10 // More = text lower
},
color: ["#cc0000"],
x: x(2500),
y: 100,
dy: 100,
dx: 100
}
然后我将注释附加到 SVG 图中:
const makeAnnotations = d3.annotation()
.annotations(annotations)svg.append("g")
.call(makeAnnotations)
下图显示了最终结果:
作者图片
折线图
第一个例子是由 D3 图形库提供的简单折线图,如下图所示:
作者图片
这条线主要存在两个问题:
- y 轴没有标签,因此很难理解测量单位。
- 与上图类似,没有上下文。
关于 y 轴标签,添加起来很简单。我在 SVG 图像中添加了一些文本(例如收入),我将它旋转了 90 度,放在 y 轴附近:
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Earnings");
关于语境,要看数据。我可以想象,这些数据指的是法国在特定时期的收入。我注意到在 18 日星期三有一次失败,而从 20 日星期五开始数据再次上升。因此,我可以用两点来强调这两个方面。
第一个注释应该突出显示所有的翻转周期,因此我可以使用一个d3.annotationCalloutCircle
,而第二个注释只能突出显示一个点,对应于星期五 20 日,因此我可以使用一个d3.annotationCalloutElbow
。
下面是构建注释的代码:
var parseDate = function(d){ return d3.timeParse("%Y-%m-%d")(d)}
const annotations = [
// first annotation
{
note: {
label: "Earnings plummeted",
title: "April 17th - 19th",
wrap: 150, // try something smaller to see text split in several lines
padding: 10 // More = text lower
},
color: ["#cc0000"],
x: x(parseDate('2018-04-18')),
y: y(8197),
dy: -100,
dx: -5,
subject: {
radius: 50,
radiusPadding: 5
},
type: d3.annotationCalloutCircle,
},
// second annotation
{
note: {
label: "Strong Recovery",
title: "April 20th",
wrap: 150, // try something smaller to see text split in several lines
padding: 10 // More = text lower
},
color: [" #00b300"],
x: x(parseDate('2018-04-20')),
y: y(8880.23),
dy: 40,
dx: 40,
type: d3.annotationCalloutElbow,
},
]
window.makeAnnotations = d3.annotation()
.annotations(annotations)
svg.append("g")
.call(makeAnnotations)
我定义了一个变量parseDate
,它解析一个字符串并返回一个日期。
下图显示了最终的图形:
作者图片
摘要
在这个简短的教程中,我举例说明了如何改进条形图和折线图,以便提供一些背景信息。我已经开发了d3-annotation
库,这是 d3 注释的第三方库。
在接下来的几天里,我将写一篇关于改进地理地图的教程…敬请期待;)
如果你想了解我的研究和其他活动的最新情况,你可以在 Twitter 、 Youtube 和 Github 上关注我。
相关文章
https://medium.datadriveninvestor.com/getting-started-with-d3-js-maps-e721ba6d8560
上下文分析能帮助提取有意义的见解吗?
上下文分析包括围绕数据集的所有世界的分析。数据集周围的世界可能包括不同的方面。例如,如果您正在测量一段时间内海面的温度,环境可能包括天气条件、一些船只的存在等等。
定义上下文分析有三个要素:
- 事件
- 环境
- 时间
在这里阅读更多。
如何改进时间序列的深度学习预测—第 1 部分
集群背后的理论及其好处。
在拟合之前对时间序列数据进行聚类可以提高约 33%的精度— src 。
图 1:时间序列聚类示例。图片作者。
2021 年,加州大学洛杉矶分校的研究人员开发了一种方法,可以提高许多不同时间序列的模型拟合度。通过聚集相似结构的数据并为每一组拟合一个模型,我们的模型可以专门化。
虽然实现起来相当简单,但与任何其他复杂的深度学习方法一样,我们经常受到大型数据集的计算限制。然而,所有列出的方法都支持 R 和 python,所以在较小的数据集上开发应该非常“简单”
在本帖中,我们将从一个技术概述开始,然后进入为什么聚类提高时间序列预测拟合的本质。这是第二部分。
让我们开始吧…
1 —技术 TLDR
有效的预处理和聚类可以提高神经网络对序列数据的预测精度。该文件有助于以下每个领域:
- 利用黄土和 r 中的 tsclean 包移除异常值。
- 使用 k 近邻输入缺失数据。
- 使用基于距离和基于特征的方法进行聚类。
- 概述利用聚类数据的神经网络架构。
2 —但是实际上发生了什么呢?
我们的主要目标是提高深度学习对许多相关时间序列 (TS)预测的准确性。然而,与任何预测模型一样,它们的好坏取决于它们的数据,因此在本文中,我们将主要关注数据争论。
出于解释的目的,让我们假设我们有一个理论数据集,其中我们观察世界各地不同市场的黄金价格。这些市场中的每一个都试图接近全球价格进行交易,但是由于其所在地的供需波动,价格可能会发生变化。
在下面的图 2 中,我们看到了其中一个市场的时间序列。
图 2:黄金价格随时间的时间序列— src 。图片作者。
现在一些市场有相似之处。那些靠近金矿的地区受运输成本的影响较小,因此它们的供应波动性较低。相反,富有买家的地区需求波动较小。
我们的方法将把相似的时间序列组合在一起,这样我们的模型就能适应特定的情况,从而表现出更高的准确性。
2.1—数据预处理
数据预处理是一个手动过程,有助于从潜在的噪声和稀疏数据中提取信号。常见的第一步是去除异常值。
图 3: IQR 方法,其中 Q1 是第一个四分位数,Q3 是第三个四分位数。超出左/右界限的值是异常值。图片作者。
存在大量的非时间异常值检测方法,例如经典的 IQR 方法(图 3 ),其中我们排除了距离分布中心足够远的数据。但是,对于时间序列数据,我们必须采取特殊的预防措施。
例如,如果随着时间的推移,我们的数据出现了强烈的趋势、方差变化或其他一些系统性变化,传统的异常值检测方法就会失效。
为了应对数据的时间变化,我们建议使用一个名为 tsclean 的 R 包。幸运的是,它还支持缺失数据插补,所以如果你的时间序列有空值,你可以很容易地插入它们。在这一步之后,我们将得到一个去除了异常值的不间断的时间序列,如图 4 所示。
图 4:缺失估算(红色)和异常值调整(蓝色)数据— src 。图片作者。
如果你好奇这个方法是如何工作的,简而言之, tsclean 将我们的时间序列分解成趋势、季节和“其余”部分,如下所示。
图 5: tsclean 分解其中 T 是趋势,S 是季节性,R 是其余部分。图片作者。
从这里开始,我们在 R_t 上使用 IQR 异常值检测方法。通过在我们的时间序列的静态(去趋势)部分寻找异常值,我们更有可能找到实际的异常值,而不是依赖于时间的变化。
最后,如前所述, tsclean 软件包支持数据插补的线性插值。线性插值只是用缺失值旁边的非空点的平均值替换缺失值。尽管有这个功能,作者建议使用一种不同的方法,叫做kNNImpute—R package。不幸的是,kNNImpute 超出了本文的范围,但是上面的链接提供了一个健壮的演练。
2.2 —聚类
现在我们有了一个干净的时间序列,我们将把我们的时间序列值聚类到相似的集合中。
聚类的目的是提高我们模型的准确性。通过处理多组相似的数据点,我们更有可能精确地拟合数据。
图 6:基于距离的聚类示例— src 。图片作者。
有两大类集群。第一种类型称为基于距离的聚类。这种方法只是为了最小化聚类中数据点之间的距离。作者建议使用动态时间弯曲(DTW ),这是一种非常健壮和常用的序列数据距离度量方法。与 kNNImpute 一样,这种方法超出了本文的范围,但简而言之,它增加了距离最小化目标的约束,以允许在连续序列之间进行更健壮的比较。
图 7:基于特征聚类成两组(红色和蓝色)的理论示例。图片作者。
第二种方法称为基于特征的聚类。正如您所料,它使用特性将数据点分组到不同的簇中。这很难想象,所以我们在图 7 中创建了一个示例图表。
在本文中,我们测试了两组特性。第一个包括特定于时间序列的特征,如自相关、部分自相关和霍尔特参数。第二组借鉴了信号处理的思想,包括能量、快速傅立叶变换(FFT)系数和方差。通过利用关于数据的信息,我们有望看到组内一致的模式。在此基础上,我们可以为每一组建立一个模型。
当在模拟数据上运行时,基于特征的聚类优于基于距离的聚类。并且,在两个特征集中,基于信号的特征优于时间序列特征。
2.3—神经网络架构
现在,我们已经清理了原始数据并开发了聚类,我们准备开发我们的神经网络架构并进行训练。
本文概述了 7 种体系结构,其中一些结合了动态和静态特性。在我们的黄金市场示例中,动态特征可以是天气,静态特征可以是市场的位置。
图 8:显示集群数据最佳性能的模型结构。作者图片
在本文测试的合成数据上,简单的双向 LSTM 表现最佳。令人惊讶的是,合成数据中的静态特征并没有提高准确性,然而这种观察非常依赖于数据。对于您的用例,您可能想要探索包含静态特性的其他架构。
现在你知道了!
3 —摘要
在这篇文章中,我们介绍了如何预处理和聚类时间序列数据。单独的聚类被引用来提高大约 33%的分类准确度。然而,需要注意的是,这些测试是在合成数据上运行的——您的数据可以看到不同程度的改进。
概括地说,我们首先通过使用季节分解和 IQR 方法去除异常值来预处理数据。然后,我们使用 K 近邻估算缺失数据。
有了干净的数据集,我们对时间序列数据集进行了聚类。最可靠的方法是使用信号处理功能。
由此可见,双向 LSTM 是观察到的训练数据的最有效方法。静态特征没有提高准确性。
本文中讨论了许多备选方案,如果您需要提高模型的准确性,可能值得探索这些方案。
感谢阅读!我会再写 31 篇文章,把学术研究带到 DS 行业。查看我的评论,链接到这篇文章的主要来源和一些有用的资源。