TowardsDataScience-博客中文翻译-2020-三十三-
TowardsDataScience 博客中文翻译 2020(三十三)
为 TensorFlow 代理创建自定义环境—井字游戏示例
关于自定义 TF-Agent 环境的介绍性博客
背景
强化学习是人工智能的一个新兴领域,在游戏、机器人、制造和航空航天等领域显示了很大的前景。在 21 世纪 10 年代中期,强化学习在围棋和国际象棋等游戏中击败人类冠军后,获得了牵引力。谷歌收购了 DeepMind[3],这是一家备受尊敬的人工智能初创公司,为 2010 年的大多数强化学习突破做出了贡献。类似地,OpenAI 在 2015 年末由埃隆·马斯克、萨姆·奥特曼和其他人[4]创立,他们承诺投入 10 亿美元进行人工智能领域的研究。OpenAI 表示,他们的目标是以造福全人类的方式促进和发展友好的人工智能。 OpenAI 的项目 OpenAI Five ,在 Dota 2 游戏上展示了达到专家级性能、学习人机合作、互联网规模操作的能力[5]。最*,谷歌在芯片布局上使用了强化学习,这是芯片设计过程中最复杂和最耗时的阶段之一,目标是最小化 PPA(功率、性能和面积),并表明生成的布局是超人的[6]。
强化学习自 20 世纪 50 年代就已经出现,在游戏和机器控制中产生了许多有趣的应用。直到 2013 年,DeepMind 的研究人员展示了它在 Atari 游戏中的使用,在大多数游戏中,它的表现都超过了人类,他们才获得了头条新闻[8]。决定性的改进是使用神经网络来学习 Q 值[9]。与人工智能的其他领域一样,神经网络随着深度强化学习的引入,彻底改变了强化学习领域[9]。从那以后,强化学习无处不在,并以前所未有的规模普及开来。在最*的 ICLR 会议(ICLR 2020)中,我们可以看到强化学习是最常见的标签[10]。
ICLR 强化学习集群
那么,什么是强化学习呢?
与标签数据可用的监督机器学习不同,强化学习不提供明确的标签数据。在强化学习中,一个主体在某个环境中执行一些动作,由于这些动作,环境的状态发生变化。基于环境对某些行为给出的反馈(奖励或惩罚),算法学习最优策略。一个学习自己走路的孩子类似于强化学习范例。*衡自己的孩子是奖励阶段,而失去*衡的孩子是惩罚或失败阶段。更多的理论解释可以在强化学习入门博客上找到,如果读者对强化学习不熟悉,强烈建议他/她这样做。
TF-代理
TF-Agents 是 TensorFlow 中用于强化学习的库,它通过提供各种经过良好测试、可修改和可扩展的模块化组件,使强化学习算法的设计和实现变得更加容易。这有助于研究人员和开发人员快速建立原型和基准。
可以使用以下代码安装 TF-Agents 稳定版:
*pip install --user tf-agents
pip install --user tensorflow==2.1.0*
更多关于 TF-Agents 的细节可以在这里找到。
环境
环境是代理执行动作的环境或设置。代理与环境交互,环境的状态发生变化。在某些应用程序上实现强化学习算法时,需要应用程序的环境。尽管 TensorFlow 为一些流行的问题(如 CartPole)提供了环境,但我们还是遇到了需要构建自定义环境的情况。在这里,我将通过构建一个自定义环境来展示井字游戏的实现。
井字游戏的自定义环境
为了更专注于构建定制环境,我们简化了井字游戏。简化的井字游戏只有一个玩家,而不是两个玩家。玩家随机选择位置,如果他/她选择的位置已经被选择,游戏结束。
让我们首先从所需的导入开始。
import tensorflow as tf
import numpy as npfrom tf_agents.environments import py_environment
from tf_agents.environments import tf_environment
from tf_agents.environments import tf_py_environment
from tf_agents.environments import utils
from tf_agents.specs import array_spec
from tf_agents.environments import wrappers
from tf_agents.environments import suite_gym
from tf_agents.trajectories import time_step as ts
环境可以是 Python 环境或 TensorFlow 环境。Python 环境实现起来很简单,但是 TensorFlow 环境更高效,并且允许自然并行化。我们在这里做的是创建 Python 环境,并使用我们的一个包装器将其自动转换为 TensorFlow 环境。
成分
创建自定义环境主要由四种方法组成: action_spec 、 observation_spec 、 _reset 和 _step 。让我们看看它们各自的含义:
action_spec :描述步骤
observation_spec 所期望的动作的规格(tensor spec);定义环境
_reset 所提供的观测的规格(tensor spec);重置环境
_ 步骤后返回当前情况(时间步长);应用动作并返回新的
SimplifiedTicTacToe 类
现在,让我们开始创建 SimplifiedTicTacToe 类。该类继承自 py_environment。PyEnvironment 类,以便提取已经可用的方法和属性。
井字游戏棋盘有九个位置。让我们从 0 到 8(包括 0 和 8)对它们进行标记。玩家可以将标记放在其中一个位置。所以,一个动作是一个从 0 到 8 的值。
观察是环境的状态。观察规范具有由环境提供的观察规范。因为棋盘有 9 个位置,所以观察的形状是(1,9)。如果某个位置被占用,我们可以用 1 表示该位置的状态,否则用 0 表示。最初,棋盘是空的,所以我们用九个零来表示环境的状态。
**class SimplifiedTicTacToe**(py_environment**.**PyEnvironment): **def** **__init__**(self):
self**.**_action_spec **=** array_spec**.**BoundedArraySpec(
shape**=**(), dtype**=**np**.**int32, minimum**=**0, maximum**=**8, name**=**'play')
self**.**_observation_spec **=** array_spec**.**BoundedArraySpec(
shape**=**(1,9), dtype**=**np**.**int32, minimum**=**0, maximum**=**1, name**=**'board')
self**.**_state **=** [0, 0, 0, 0, 0, 0, 0, 0, 0]
self**.**_episode_ended **=** False **def** **action_spec**(self):
**return** self**.**_action_spec **def** **observation_spec**(self):
**return** self**.**_observation_spec
游戏结束后,我们应该重置环境(或状态)。为此,我们可以在我们创建的定制环境中编写一个名为 _reset 的方法。该方法必须返回游戏开始时环境的默认状态。
def _reset(self):
# state at the start of the game
self._state = [0, 0, 0, 0, 0, 0, 0, 0, 0]
self._episode_ended = False
return ts.restart(np.array([self._state], dtype=np.int32))
这里值得一提的是第集和第步。一集是一个游戏的实例(或者游戏的生命)。如果游戏结束或生命减少,该集结束。另一方面,步长是在情节中单调增加的时间或某个离散值。随着游戏状态的每次改变,步骤的值增加,直到游戏结束。
让我们定义两种方法来检查某个点是否是空的,以及是否所有的点都被占用了。
**def** **__is_spot_empty**(self, ind):
**return** self**.**_state[ind] **==** 0**def** **__all_spots_occupied**(self):
**return** all(i **==** 1 **for** i **in** self**.**_state)
现在,我们需要编写最后一个方法: _step 。它应用动作并返回游戏中的新情况。这种情况属于 TensorFlow 中的类时间步长。 TimeStep 有四个信息:观察,奖励,step_type 和折扣。关于每个信息的细节可以在这里找到。
在写 _step 方法的同时,先看看插曲是否已经结束。如果已经结束,我们需要调用 _reset 方法。否则,我们会查看要标记的位置是否为空。如果不为空,则该集结束。如果该位置为空,我们在该位置放置标记,并查看这是否是最后一步。根据是否是最后一步,我们分别返回终止或转换。
**def** **_step**(self, action):
**if** self**.**_episode_ended:
**return** self**.**reset() **if** self**.**__is_spot_empty(action):
self**.**_state[action] **=** 1
**if** self**.**__all_spots_occupied():
self**.**_episode_ended **=** True
**return** ts**.**termination(np**.**array([self**.**_state], dtype**=**np**.**int32), 1)
**else**:
**return** ts**.**transition(np**.**array([self**.**_state], dtype**=**np**.**int32), reward**=**0.05, discount**=**1.0)
**else**:
self**.**_episode_ended **=** True
**return** ts**.**termination(np**.**array([self**.**_state], dtype**=**np**.**int32), **-**1)
每走一步给予 0.05 的奖励。当我们所有的 9 个位置都成交时,奖励值为 1。如果游戏结束时少于 9 个位置记号,则获得负奖励-1。在这里,使用 1.0 的折扣,以便没有关于时间/步骤的报酬衰减。
现在,让我们创建张量流环境。
python_environment = SimplifiedTicTacToe()
tf_env = tf_py_environment.TFPyEnvironment(python_environment)
万岁!已经创建了 TensorFlow 环境!
让我们来玩吧!
现在,我们来玩 10000 集的游戏。
time_step = tf_env.reset()
rewards = []
steps = []
number_of_episodes = 10000for _ in range(number_of_episodes):
reward_t = 0
steps_t = 0
tf_env.reset()
while True:
action = tf.random.uniform([1], 0, 9, dtype=tf.int32)
next_time_step = tf_env.step(action)
if tf_env.current_time_step().is_last():
break
episode_steps += 1
episode_reward += next_time_step.reward.numpy()
rewards.append(episode_reward)
steps.append(episode_steps)
我想知道*均步数。所以,我执行下面的代码。
mean_no_of_steps = np.mean(steps)
我得到的*均步数为 3.4452 。这意味着人们可以期待游戏在第四步结束。我们播放了 10000 集。因此,我们相信均值能够很好地估计分布的期望。因此,让我们找出随机变量的理论期望,并看看它如何与我们通过实验估计的相吻合。
步骤数的期望值
设 X 为随机变量,表示重复发生后的步数。
X 由九个随机变量组成, X_1,X_2,…,X_9 。如果直到第 i 步没有重复,X_i 为 1。我们需要找到 X 的期望。
对于 i 的某个值,X _ I = 0;对于 j > i 的所有值,X_j = 0 。
所以,E[X]= E[X _ 1]+E[X _ 2 | X _ 1]+…+E[X _ 9 | X _ 1,…,X_8]
现在,我们来计算 E[X_i | X_1,…,X_(i - 1)] 的值。
如果直到第 i 步没有重复,随机变量 X_i 为 1 。这种可能性是:
P(第 I 步之前不重复)=第 I 步之前不重复排列的数目/第 I 步之前排列的总数
= P(9,i) / (9 ^ i)
概率乘以 1 给出概率本身。所以期望变成了这些概率的总和。
因此,期望是:
E[X] =从 i=1 到 i=9 的和(P(9,I)/(9 ^ I))≈3.46
另一种找到期望值的优雅方法可以在这里找到——感谢 Henk Brozius 。
理论预期非常非常接*实验预期。这给了我一些满足感。
这就是如何在 TensorFlow 中创建自定义环境。TF-Agents 提供了模块化的组件,使得原型开发更加容易。你有了一个良好的开端。我们可以用 TF-Agents 做更多的事情。继续探索!天空是无限的!
强烈建议感兴趣的读者点击以下链接,了解有关创建自定义 TensorFlow 环境的更多信息。
- 环境:https://www . tensor flow . org/agents/tutorials/2 _ environments _ tutorial
- TF _ agents . specs . bounddarrayspec:https://www . tensor flow . org/agents/API _ docs/python/TF _ agents/specs/bounddarrayspec
如果您有任何问题或意见或困惑,请在此随意评论。我会尽力回答他们。
参考
- https://www . new scientist . com/article/2132086-deep minds-ai-beats-worlds-best-go-player-in-latest-face-off/
- https://www . the guardian . com/technology/2017/dec/07/alpha zero-Google-deep mind-ai-beats-champion-program-teaching-self-to-play-four-hours
- https://techcrunch.com/2014/01/26/google-deepmind/
- https://openai.com/blog/introducing-openai/
- https://openai.com/projects/five/
- https://ai . Google blog . com/2020/04/chip-design-with-deep-reinforcement . html
- http://incompleteideas.net/book/first/ebook/node12.html
- https://arxiv.org/abs/1312.5602
- aurélien géRon——使用 Scikit-Learn、Keras 和 TensorFlow 进行动手机器学习 _ 构建智能系统的概念、工具和技术(2019 年,O'Reilly Media)
- https://iclr.cc/virtual_2020/paper_vis.html
- https://www.tensorflow.org/agents
- https://www . quora . com/subscribe-we-are-drawing-a-number-random-from-the-list-of-numbers-1-9-After-drawing-the-numbers-is-replaced-How-we-can-find-the-expected-number-drawing-After-the-we-conference-the-first-repetition/answer/Henk-Brozius?_ _ filter _ _ = all&_ _ nsrc _ _ = 1&_ _ sncid _ _ = 5298994587&_ _ snid 3 _ _ = 8434034083
创建数据科学家的个人网站(与 Hugo 和 Netlify 一起)
图片来源:https://unsplash.com/@andrewtneel
入门
在个人网站上分享你的项目、博客和出版物会让你成为真正的专业人士
作为一名数据科学家,拥有一个个人页面来展示我的项目和博客似乎是合适的。这让整个事情更加专业。
我一生中的大部分时间都是一名学者,我总是在我的大学网络中拥有一个个人页面来分享一个小博客和项目。去年年底读完博士后,我就一直渴望能搬到另一个地方。为此继续使用大学资源是没有意义的,即使它们对我来说仍然是可用的。
于是我的旅程开始了。我开始了创建新网站的新冒险,并向我的朋友寻求帮助。其中一个用 WordPress ,另一个试图说服我用 Wix 。但出于某种原因,我并不认为它们是数据科学家和计算机工程师的最佳选择。他们只是太“容易”和“受欢迎”,这不是我们做事的方式,对不对?另一个朋友告诉我关于 GitHub 的页面系统。但这也没有说服我。
当时,在一个随机的页面上,在一个随机的博客中阅读了一篇随机的文字,我读到了以下内容:用 雨果 部署到Netlify。我立刻产生了兴趣。那个随机的家伙(或者女孩,我甚至不知道)甚至没有尝试,最终启发我如何开发和部署我自己的页面。
好吧,那是关于什么的?
雨果
Hugo 是一个搭建网站的框架。它很强大,有一个很棒的社区,里面有很多问题和答案,还有一堆由用户创建和分享的主题。
创建一个 Hugo 网站真的很容易,你可以按照这个入门指南立刻部署你自己的网站。我最喜欢 Hugo 的一点是它对开发者友好(你真的会觉得你在开发什么东西,而不仅仅是扮演网页设计师),易于学习,高度可扩展。真的,你可以让你的网站变得尽可能复杂,或者保持简单。你喜欢怎么做就怎么做。
网络生活
Netlify 是一个从 Git 存储库(比如 GitHub、GitLab 或 BitBucket)构建和部署站点的*台。它提供持续部署服务、全球 CDN、超快速 DNS、原子部署、即时缓存失效、一键式 SSL、基于浏览器的界面、CLI 和许多其他用于管理 Hugo 网站的功能。
有很多方法可以部署你的 Hugo 网站并把它放到网上。你可以在这个页面查看一些教程的列表。然而,我发现 Netlify 是一个很好的选择,它提供了一些优秀的资源,即使是在免费层。
部署站点
这比听起来容易得多,可以在 30 分钟左右完成(如果你打字和做决定的速度很快,15 分钟)。遵循这三个步骤:
- 让网站运行起来。跟随雨果教程。
- 在你最喜欢的*台上为你的站点创建一个 git 存储库(我用的是 GitHub)。然后,您可以使用 Netlify 在线部署您的站点。你应该这样做:
2.1:在存储库的根目录下创建一个 netlify.toml 文件。这个文件配置 Hugo 和 Netlify 如何协同工作。然后,复制粘贴以下代码:
2.2.:在 Netlify 创建账户,并在**New site from Git**
点击。
从 Git 创建新站点。作者图片
2.3.:选择您的 Git 提供商和保存您的站点的存储库。
为持续部署选择 Git 存储库。作者图片
2.4.:在 Netlify 配置站点的构建选项和部署,如下图所示。(PS:标准是从您的master
分支部署,但是您可以设置您喜欢的任何分支)
Hugo 的 Netlify 构建选项。作者图片
现在点击**Deploy site**
,瞧,你的网站就建成了,并且马上就可以上线了。
3.您将在概述页面中注意到,您部署的站点域包含一个 Netlify 扩展名。你可以就这么用,但是你需要自己的域名(一个合适的 www。<您的姓名或昵称>。com )如果要花哨的话。
你可以通过 Netlify 获得一个自定义域名(你需要付费,ofc),或者你可以在 GoDaddy 、 Google Domains 或者你选择的任何其他服务上注册你的域名。我选择了谷歌域名。只是因为。配置您的自定义域很容易,我不会在这里讨论它,因为它根据您使用的服务而有所不同。但是 Netlify 提供的这个文档会教你所有你需要的东西。PS:您的自定义域名可能需要几个小时才能开始为您的网站工作,DNS 相关的问题。
恭喜你,你有你的网站了:)
现在你完成了。如果您是程序员、数据科学家或任何相关人员,您可能以前就已经使用过持续部署。那我就不用告诉你了,更新你的站点所要做的就是提交(或者合并)到你的存储库的主分支。如果你不是,你可能根本没有遵循这个教程。
我希望你在新页面上分享你的作品时过得愉快!
从头开始创建数据集
使用 web 抓取、API 调用和 Python OOP
凯文·Ku 在 Unsplash 上的照片
关于
这篇博客介绍了创建用于大型数据科学项目的数据集的整个过程。
数据科学项目包括模拟视频游戏的成功,发现个别功能的重要性,然后创建一个交互式网络应用程序。
web 应用程序将允许游戏开发者输入和修改游戏设计,以便在给定开发者可用资源的情况下最大化预测的成功。
规划
任何项目最重要的一个方面,也是最容易被忽视的一个方面是项目规划。
有一个计划将确保你不断地朝着你的目标前进,并且你的最终项目将会更好地组织并且更快地完成(如果你像我一样,你很容易被新的功能想法分散注意力)。
到目前为止,我发现最好的方法是使用 github 项目资源库的规划部分。我使用这个部分来建立一个自动化看板风格的项目板,它将在您为特定问题合并一个拉式请求之后自动完成步骤。
设置评估板很简单:
- 转到您的项目报告,然后单击“项目”选项卡。
- 填写表格并点击“创建项目”
填写项目信息,并选择使用哪种风格的板
现在我们已经设置了项目板,接下来删除作为模板一部分出现的默认信息卡。
之后,开始为成功完成项目所需的每个主要步骤添加卡片:
- 确定对游戏成功有很强预测力的特征,并找到可以获得这些特征的来源
- 使用特征名称、简短描述、类型和来源创建数据字典
- 决定使用哪个数据库并设置它
- 实现一个与数据库交互的类
- 实现一个从 Steam 中抓取数据的类
- 实施从 RAWG API 获取数据的程序
- 将这两个程序结合起来,创建一个将填充数据库的脚本
- 将完成的数据集上传到 Kaggle
这是我们的项目板,卡片稍微移动了一下,以说明如何跟踪您的工作流程:
现在,当您开始处理项目时,您可以选择当前正在处理的卡片,然后单击“…”并将其转换为一个问题。这将使您能够提交一个“拉”请求,该请求将自动关闭该问题并将其移至“完成”列。
这是几乎完成的项目板的样子:
几乎完成的项目:大多数卡片都是已关闭的问题,已经被移到“完成”栏
现在我们已经有了一个大纲和一个项目板,我们可以确保任何时候我们在卡片上工作都是朝着最终目标前进。不要再被添加酷(不必要的?)功能是我们随时想出来的。
履行
现在我们已经有了计划,是时候开始实施我们的每一张卡了。不是所有的卡都需要编码或拉请求,尤其是在我们开发架构的早期阶段。
识别特征和来源
首先从 Steam store 开始,这是最大的 PC 游戏在线市场,也是最活跃的在线游戏社区之一。
谷歌蒸汽商店会给我们这个链接:https://store.steampowered.com/
点击链接将带我们进入主页:
Steam 商店主页
点击一个随机的游戏会显示出哪些数据是可用的,我们可以决定这些数据是否是一个有价值的预测。
这里有一个随机游戏页面:
点击了我看到的第一个游戏
我们可以立即看到一些对我们的数据集有用的数据点。
当我们开始从 API 源中寻找补充数据时,让我们记住这些数据点。
在谷歌搜索“游戏开发者 API”并点击第一个不是广告的结果后,我们来到了 https://rapidapi.com/blog/top-video-game-apis/的。
【https://rapidapi.com/blog/top-video-game-apis/
几个 API 专用于特定的游戏,如《我的世界》和英雄联盟,对我们的数据集没有用处。
然而,列表上的第一个 API 是 RAWG 视频游戏数据库 API ,这听起来很有希望,尽管我不知道“RAWG”代表什么。
我们可以检查不同的响应模式,并确定任何听起来与我们的数据集相关的功能:
我们可以再次看到对我们的数据集有用的几个特征。让我们将这两个资源放在手边,并拿出我们希望数据集包含的完整要素列表。
数据字典
创建解释每个变量的高质量数据字典不仅是最佳实践,而且对于我们将完成的项目中的几项任务也非常有用。
使用 markdown 创建表格既快速又简单,并为我们提供了所有功能的清晰概览:
数据字典使项目变得更加容易
我们现在有了一个好看的数据字典,它将成为数据集的真实来源。
对数据集的任何更改都应该首先记录在数据字典中,然后由各个类实现。
设置数据库
最糟糕的感觉之一是当你的抓取程序在 8 个小时后抛出一个错误,并且所有收集到的数据都被删除了。
为了避免这种不好的感觉,我们将在运行抓取程序时使用一个简单的数据库来保存数据。
我们需要数据库做的就是保存我们的数据,让我们知道一个游戏是否已经被保存,这样我们就可以避免冗余。
有几个数据库选项可以用于这个简单的项目,但是我们将使用 sqlite3 。这将在我们的本地系统上为我们提供一个超级简单的数据库,我们可以使用 SQL 与之交互。
为了更好地组织我们的代码,并把 SQL 都放在一个地方,我们将创建一个类来管理我们的数据库并与之交互。然而,我们需要添加的一个重要方法是将数据库保存为“csv”文件的能力。
密码
为了避免花 200 个小时阅读这篇文章,我不会在课堂上深入讨论细节。
我计划就如何创建每个单独的类创建一些简短的博客,但是现在你可以在这里查看完整的代码库:
https://github.com/Jesse989/game-oracle
另外,如果您有任何意见、问题或建议,请联系我。我真的很感谢有机会讨论这些事情。
数据库客户端:
这基本上是一个用于管理数据库和处理 SQL 的实用程序类,因此我们不必多次重写查询。
另外,我给了它一个’。to_csv()'方法,该方法将创建一个包含我们的“游戏”表的全部内容的 csv 文件。
现在我们有了持久化数据的方法,让我们集中精力寻找我们想要保存的数据。因为我们要处理两个数据源,所以创建两个独立的类是有意义的,每个数据源一个类。
Steam 游戏信息
首先是想出一种从 Steam store 获取数据的方法。为此,我们将编写一个使用一些基本 python 抓取库的类; 请求 , BeautifulSoup ,python 库 re (正则表达式)。
这个对象将负责请求单个游戏 url,然后使用 BeautifulSoup 搜索和 regex 的组合解析所需的信息。
如你所见,该类主要由两个公共方法组成:“get_game_html”和“strip_features”。
第一个方法从指定的 url 获取页面,然后在生成的 html 上调用第二个方法。
“strip_features”方法是构建整个 results 对象的地方,它主要包括使用私有的 helper 方法来组成最终的游戏 dict。
现在我们已经完成了获取数据的前半部分,让我们继续创建将与 RAWG API 交互的类。
RAWG
处理 API 绝对是这两个任务中比较容易的一个,正如你所看到的,这个类几乎只有一半的代码行。
这个类的目标是拥有一个接口,我们可以用它来调用 API 并将响应解析成数据库的正确格式。
这个类公开了一个公共方法“get_game ”,它需要一个表示游戏标题的字符串。
然后,它发出一个 API 请求,搜索与我们传入的字符串同名的游戏。我们假设响应列表中的第一个游戏是正确的,肯定不是最佳策略,尽管它在我们的例子中是有效的。
然后,我们用新获得的 RAWG ID 发送另一个请求,这将把我们正在寻找的完整游戏信息发回给我们。
其余的私有 helper 方法解析响应数据,并将其形成数据库的正确格式。
把所有的放在一起
现在我们已经有了获取数据并将其保存在数据库中的工具,我们只需要创建一个将编排这些过程的类。
蒸汽履带车
这个类的主要目的是获取我们想要的游戏在数据集中的 URL,然后遍历这个列表并使用我们之前定义的类来获取和保存每个游戏的结果数据。
这个类介绍了另一个有用的工具, 【硒】 。尽管 Selenium 已经成为数据科学家 web scraping toolkit 中的一个标准,但它的创建是为了测试 web 浏览的自动化。
我们在这里使用 Selenium 而不是 Requests,因为当我们向下滚动时,结果页面会逐渐加载。使用 Selenium,我们将自动执行滚动操作,并在到达底部时保存整个页面源代码。
然后,我们可以将 html 加载到 BeautifulSoup 中,并去掉所有单独的游戏 URL。
我选择以这种方式创建 url 列表的原因之一是,结果是按受欢迎程度排序的。这意味着我们的数据库索引将固有地代表游戏的流行程度,我们可以在我们的分析和建模中使用它。
要运行爬网程序,请执行以下操作:
# instantiate our crawler
sc = SteamCrawl()# start the process
sc.crawl()
然后,爬虫将从保存的 html 中提取所有单独的 url,并开始遍历每个单独的游戏 URL。首先爬虫会检查并且确定游戏还没有被保存。如果是,那么我们移动到下一个 url,如果不是,那么我们使用我们的游戏解析器和 RAWG API 获取信息,并将新信息插入数据库。
做好等待的准备,大概花了 11 个小时
声明:程序在列表中的最后一个游戏上抛出了一个错误。谢天谢地,游戏已经保存在数据库中,所以我可以调用。数据库客户端上的“to_csv”。
# Using the crawlers database client 'to_csv' method we created
sc.dbc.to_csv('games.csv')
检查数据
现在我们的数据库有了一个 csv 文件,我们可以检查它,看看我们的程序在获取尽可能多的数据方面做得如何。
games_df = pd.read_csv('games.csv')
games_df.info()
好的,所以稍后肯定会有一些擦洗工作
从上面的信息中,我们可以看到有一些问题(没有为任何游戏收集' Publisher ',这肯定是代码中的一个疏忽),但总的来说,数据集似乎与我们预期的相当接*。
在清理和输入之后,我们应该有一个非常可靠的数据集,可以用来回答我们最初的问题。
games_df.head()
写几个剧本,然后祈祷 11 个小时,还不错。
上传到 Kaggle:
既然我们已经做了这么多工作,我们与数据科学界分享这些工作是有意义的。最好的方法是把它上传到 Kaggle.com,在那里,任何数据科学家都可以从中受益。
我们导航到 https://www.kaggle.com/datasets 的,点击“新建数据集”按钮。这将打开一个弹出窗口,要求我们拖动或上传所需的文件。像往常一样,Kaggle 让我们创建数据集变得非常容易。
为其命名、描述、标记等。给我们的可用性评分是 8.8 分
上传后,我们将被重定向到数据集的登录页面。为了使我们的数据集更加可用,并给它一个更高的可用性分数,我们确保浏览并填写尽可能多的信息。
以下是新创建的数据集的链接:
【https://www.kaggle.com/jesneuman/pc-games
结论
在本文中,我们使用 web 抓取和 API 调用从头开始创建数据集。这是收集数据的两种最常见的方法,拥有这些技能将极大地提高您收集见解和创建建议的能力。
非常感谢您花时间阅读我的文章。我希望它信息丰富,简明扼要。如果没有,请让我知道我该如何改进它!
请留下评论、问题、建议或任何反馈,因为我真的很感激有机会与他人交流并作为一名数据科学家成长。
免责声明:尽管我确实试图编写高质量的符合最佳实践的代码,但我绝不是专家,并且这些代码可能并不代表实现这一点的“最佳”方式。
如果您对任何代码、程序、写作等有任何建议。我也很想听听这些。
从头开始创建一个不和谐机器人并连接到 MongoDB
使用 python 为您的 discord 服务器创建一个功能记分员 bot,并将数据存储在 MongoDB 数据库中。
Discord 是一个对开发者非常友好的消息*台。discord 开发者的一个主要出路是生产 discord 机器人,这些机器人可以执行无限多的任务,如发送消息、播放音乐,甚至允许在聊天服务器上玩小游戏。在本教程中,我们将使用 Python 创建一个 discord bot,它将跟踪用户发送的某些消息,并使用 MongoDB 添加一个评分系统。
首先,在终端中安装 discord.py、dnspython 和 pymongo 模块。Dnspython 和 pymongo 将用于我们的 MongoDB 连接。
接下来,导入您刚刚安装的所有模块。
在这之后,我们必须在 discord web 应用程序上激活我们的机器人。首先,请前往:
不和谐是通过语音、视频和文本进行交流的最简单的方式,无论你是学校俱乐部的一员,还是晚间……
discordapp.com](https://discordapp.com/)
然后点击开发者门户。
点击开发者门户后,点击右上角的蓝色按钮“新应用”。一旦你为你的机器人输入了一个名字,你应该会看到这样一个屏幕:
一般信息屏幕
接下来,点击屏幕右侧显示“Bot”的标签。您应该会看到这样的屏幕:
添加 bot 屏幕
当你到达这个屏幕后,点击右边的蓝色按钮“添加机器人”。您应该会看到这样的屏幕:
机器人信息屏幕
在这里,您将看到您的机器人的所有信息,包括允许您访问机器人的令牌。请确保这个令牌是安全的,因为它是决定谁可以访问 bot 并更改其功能的唯一因素。接下来,我们必须将机器人添加到服务器。在本教程中,有一个为测试而创建的单独的服务器。要将 bot 添加到服务器中,请点击屏幕左侧的“OAuth2”选项卡。该工具为您的 bot 创建一个访问 URL,然后连接到 Discord“oauth 2 API”。您应该会看到这样的屏幕:
OAuth2 屏幕
一旦你到达这个屏幕,选择写着“机器人”的盒子。单击该框后,屏幕上会显示可选的权限。对于这个机器人,您可以选择标有“管理员”的框,以允许出于测试目的的所有权限。但是,在创建要添加到公共服务器的 bot 时,您可能不想添加所有权限。
范围类型和权限
一旦选择了所有选项,就会自动生成一个 URL。将该 URL 复制到剪贴板并粘贴到浏览器中。一旦你被发送到那个 URL,选择你想要添加机器人的服务器。恭喜你,你已经正式将你的第一个机器人添加到你的服务器上了!
成功屏幕
现在我们已经将机器人添加到我们的服务器中,我们可以开始编程了!我们必须首先创建一个与机器人的连接。这就是令牌的用途。
在这个代码片段中,我将我的机器人的令牌保存在一个单独的文本文件中,然后将该值存储在变量“token”中。在这之后,我创建了一个客户端,它是你的机器人与 Discord 及其 API 之间的连接。在代码片段中,您可以看到客户端实现了它的“on_ready()”函数。一旦机器人准备好进一步的动作,就调用这个函数。深入研究 on_ready()函数,关键字“async”表示该函数是异步的,并且与正在运行的任何其他函数一起发生。一旦机器人实现了更多的功能,这就更有意义了。最后,我们使用保存在变量中的令牌运行客户端。如果您运行这个代码片段,您应该在屏幕上看到您的 bot 输出的名称,后面跟着剩余的 print 语句。
接下来,我们必须对机器人进行编程,以读取用户消息,并确定是否有一个关键字将被它的调*系统选中。对于这个机器人,我将使用“不和谐”、“python”、“代码”和“乐高”这些词。这些关键字不区分大小写。
这里,我们为我们的客户机创建了另一个事件,它实现了函数“on_message()”。这个函数只是在用户向通道发送消息时运行。它应该打印关于发送的消息的基本信息,比如它的通道、作者、作者的名字以及消息是什么。一旦 bot 检查到诸如“python”之类的关键字,它将向通道发送它被接受的消息。下面是发送和接收消息的完整代码片段:
既然我们已经有了发送和接收消息的基本功能,现在是时候在我们的 MongoDB 数据库中记录用户级别了!我们将简单地通过计算用户说一个关键词的次数来计算用户级别。
首先,我们必须在mongodb.com创建一个 MongoDB 帐户,以便创建一个集群,也就是一组数据库。创建帐户后,屏幕会提示您:
集群创建
在创建数据库时,MongoDB 将您的集群存储在它的一个物理数据库中,因此您必须选择您的地区和提供商。选择离您最*的地区,以确保最快的反馈时间。如果愿意,您也可以更改集群的名称。创建集群后(需要 1-3 分钟),您将看到以下屏幕:
集群屏幕
我们现在必须连接到我们的集群。为此,请点击“连接”按钮。这将通过以下屏幕提示您:
连接到群集
只需点击绿色按钮,上面写着“添加您当前的 IP 地址”。在那里,创建一个用户来链接到集群。请确保记住您的用户凭据,因为它将用于后续步骤。一旦你完成了,继续你的连接方式。这是将出现的屏幕:
连接方法
在本教程中,我们将使用 MongoDB Compass,它是 MongoDB 的 GUI,允许轻松访问集群,是对数据进行分析的好方法。如果您没有 MongoDB Compass,请按照它提供的说明下载该程序。一旦您下载了 MongoDB Compass,您将会看到这样一个屏幕:
MongoDB 指南针连接屏幕
回到 MongoDB online,您会看到这样一个屏幕:
MongoDB 集群连接
您得到的 URL 是您将复制并粘贴到 MongoDB Compass 门户中的内容。在网址中,你会看到它的一部分有“
完整集群
现在,让我们回到 MongoDB 的 web 门户,探索我们的集群。请点击“收藏”按钮。集合是群集中可以存储数据的子组。它将询问您是否希望加载样本数据集。让我们加载它来了解集群的结构。加载可能需要几秒钟。
加载的样本数据集
我们接下来要做的是创建一个数据库来存储我们的用户数据。点击显示“+创建数据库”的按钮。您可以随意命名数据库。MongoDB 中的数据存储在一个层次结构中。它的结构是集群>数据库>集合。一旦我们创建了一个数据库,我们还需要创建一个将存储在数据库中的集合。
空用户数据数据库
我们现在有一个空的数据库等待填充我们的用户数据!
为了检索和上传数据到我们的数据库,MongoDB API 创建了许多函数,我们不会完全涵盖所有使用的函数。如果您想进一步探索,这里是 MongoDB 文档:
[## PyMongo 3.10.1 文档— PyMongo 3.10.1 文档
如果您对 PyMongo 有困难或有疑问,最好的地方是 MongoDB 用户组。一旦你得到…
pymongo.readthedocs.io](https://pymongo.readthedocs.io/en/stable/)
首先,让我们从停止的地方开始。我们将在现有的不和谐机器人代码中添加内容。
这些是我们运行代码所需的导入。我们接下来要做的是在代码中连接到我们的集群,然后导航到我们存储数据(用户数据)的位置。
首先,我们必须连接到我们的集群。在代码中显示“CONNECTION_URL”的地方,用您用来连接 MongoDB Compass 的 URL 替换它,并用引号将它括起来。该链接在本教程中没有显示,因为它包含一个安全漏洞。之后,我们必须连接到我们的数据库,然后连接到我们的集合。我们现在在我们需要在的位置!
我们接下来要做的是为放入数据库的每个条目选择属性。每个条目将被视为一个独特的用户,其中应包括用户名,他们的分数,和他们的 ID。MongoDB 数据库中的每个条目都与一个惟一的 ID 相关联。您可以让它自动生成,也可以自己决定。出于我们的目的,最好是让条目 ID 与用户 ID 不一致。
在这个代码片段中,我们创建了一个名为“post”的变量,它包含了条目的属性。然后,我们将这些属性插入到“UserData”集合中。一旦我们运行程序并在我们的 Discord 服务器中键入“python ”,我们将看到我们的数据存储在数据库中。
存储的用户数据
将向我们的数据库发送数据的代码添加到我们的机器人正在搜索的所有其他关键字中。现在,代码将如下所示:
然而,我们的计划有一个小问题。一旦我们键入另一个关键字,数据库就不能再次输入同一个用户。我们可以用一些简单的条件语句来解决这个问题。
在这里,我们创建了一个对数据库的查询,主要是询问是否有任何条目的预先存在的 ID 与当前输入的 ID 相同。如果当前输入的 ID 不同,那么新用户将登录到数据库中。接下来,如果用户已经存在于数据库中,我们必须修改它将会发生什么。
在这个代码片段中,如果用户已经在数据库中注册,我们基本上是在更新一个预先存在的条目。我们首先找到带有。find()方法,然后我们遍历用户并捕获其“score”属性。然后我们给这个值加 1。最后,我们使用。update_one()函数专门更新“score”属性。把这个函数添加到所有关键词之后,你就有了一个完整的链接到 MongoDB 的 Discord bot!以下是完整的代码:
信息和图像来源:
[## https://discordapp . com/assets/f7a 4131 e 47 f 50 b 48 B3 f 85 f 73 c 47 ff 1 DC . png 的 Google 图片结果
编辑描述
www.google.com](https://www.google.com/imgres?imgurl=https%3A%2F%2Fdiscordapp.com%2Fassets%2Ff7a4131e47f50b48b3f85f73c47ff1dc.png&imgrefurl=https%3A%2F%2Fdiscordapp.com%2F&tbnid=llbk160_bIKyPM&vet=12ahUKEwi7xrC5pPvoAhVDCN8KHWGZBtQQMygBegUIARCbAg..i&docid=tL3jcu20AJy75M&w=1200&h=630&q=discord&ved=2ahUKEwi7xrC5pPvoAhVDCN8KHWGZBtQQMygBegUIARCbAg) [## 如何用 Python 制作一个不和谐机器人——真正的 Python
在这个循序渐进的教程中,您将学习如何用 Python 制作一个 Discord bot,并与几个 API 进行交互。你会…
realpython.com](https://realpython.com/how-to-make-a-discord-bot-python/)
用 Python 和 Dask 创建分布式计算机集群
如何在您的家庭网络上建立分布式计算机集群,并使用它来计算大型相关矩阵。
计算相关矩阵会非常快地消耗大量的计算资源。幸运的是,相关性(和协方差)计算可以智能地分成多个过程,并分布在许多计算机上。
在本文中,我们将使用 Dask for Python 来管理局域网中多台计算机之间的大型相关矩阵的并行计算。
什么是相关矩阵
相关矩阵显示了两个变量之间的线性统计关系。众所周知,相关性并不意味着因果关系,但我们仍然经常利用它来理解我们所处理的数据集。
如果你只是想在你自己的家庭计算机集群上计算相关性矩阵,那么跳过这一节,但是如果你对如何用数学方法计算相关性感兴趣,那么继续读下去。
直观显示这些步骤的一个简单方法是在 Excel 中重新创建,因此我将向您展示如何在 Excel 中计算三组外汇数据之间的相关性和协方差。
首先,我们获取三组时间序列数据,按每种货币对的列进行排列,然后获取每组汇率的*均值(值的总和除以值的数量):
然后,对于每个时间序列数据点,我们计算该点与*均值的差值:
然后,我们对这些差异进行*方,这将给出每个数据集的方差。
然后,我们计算*方值范围的总和,除以我们的样本大小减一,得到我们的方差,例如 GBPUSD 为 0.0013%,如第 3 行所示。我们从数据点的计数中减去 1,因为这是我们数据集的样本(我们没有每一个历史数据点),而不是整个人口。这就是所谓的“样本方差”。如果我们有全部人口,我们不会减去一个。
我们还可以计算这一阶段的标准差,它就是方差的*方根:
我们可以通过将每个时间点的两个“均值差”值相乘来计算两个给定数据集之间的协方差。以下示例显示了 GBPUSD 和 JPYUSD 的单一时间点协方差计算:
我们称 GBPUSD 数据集为 A,JPYUSD 数据集为 B,依此类推。有了四个单独的数据集,我们就有 6 个组合用于协方差计算:AB、AC、AD、BC、BD 和 CD。所有这些都是以相同的方式计算的,即乘以每个时间点每个数据集*均值的差值。
然后,我们取这些值的*均值,就像我们对方差所做的一样,从分母中减去一,得到协方差:
最后,我们可以通过将协方差除以两个标准差的乘积来计算这两个数据集的相关性:
在数学上,我们可以将其表示为:
这可能看起来令人望而生畏,但这正是我们已经计算过的。请看右边的等式,分数的顶部表示“取数据集 X 中每个数据点与数据集 X 的*均值之差,然后乘以数据集 Y 中的等效计算结果”,然后将所有这些值相加。带有下标 I 的 x 仅表示数据集 x 的给定点,而 x̅表示数据集的*均值。y 值也是如此。
分母也是说,对于 X 上的每一个点,从那个点减去 X 的*均值,然后*方你得到的值。把所有这些加在一起,对 y 做同样的事情。现在把这两个值相乘,然后求这个数的*方根。结果是两个数据集的标准差的乘积。
这正是我们刚刚为自己计算的,也是我们今天正在做的,只是我们使用家用计算机集群来计算大量金融工具在大量时间点的相关性。
设置环境
网络
第一步是在构成集群的每台计算机上建立计算环境。您将需要确保计算机可以在本地网络上看到彼此(通过防火墙例外)。您还需要记下每台计算机的 IP 地址。
在 Windows 上,您可以在命令提示符下键入:
ipconfig
这将为您提供本地网络上的 IP 地址。在 Linux 机器上,您可以键入:
hostname -I
或者在 OSX,下面的内容应该有用:
ipconfig getifaddr en0
您应该记下每台机器上的每个 IP 地址,因为您将需要它来设置集群。您还可以通过运行以下命令来检查每台计算机是否可以看到其他计算机:
ping <Target IP Address>
如果有数据响应,那么两台计算机可以互相看到对方,如果没有,您需要仔细检查 IP 地址,并确保防火墙没有阻止通信。
我们将使用三台计算机作为小型集群的一部分,它们分别是 192.168.1.1、192.168.2 和 192.168.1.3。
Python 和Dask
需要注意的非常重要的一点是每台计算机上的每个 Python 环境必须相同。如果库版本中存在不一致,这可能会导致问题。
如果更简单的话,可以使用 Anaconda 或类似的工具创建一个新的 Python 环境,其中包含您将使用的库的匹配版本,比如 Numpy 和 Pandas。
您还需要在每台计算机上安装 Dask 本身。您可以使用 Conda 实现这一点:
conda install dask distributed -c conda-forge
或者您可以使用画中画:
python -m pip install dask distributed --upgrade
现在,您应该在集群中的所有计算机上安装了一致的 Python,并且具有匹配的库版本。
获取数据
在这个例子中,我使用的是每日股票数据的大型数据集。这包括 2000 年至 2020 年约 7000 只美国股票的每日收盘价。在我的集群中的任何一台个人计算机上进行计算都是非常耗时的。当我最初尝试计算时,由于在写入页面文件时遇到内存限制,系统在几个小时后崩溃。
这些数据存储在一个大型 SQL 表中,我们将按顺序访问每个股票,然后进行合并。事实证明,这比用 Python 执行操作要快得多。
我们还将使用 MySQL 数据库连接器的不同实现。标准的 MySQL 连接器库在处理大型数据集时相对较慢,因为它本身是用 Python 编写的。此连接器的 C 实现必须更快,您可以在这里找到安装细节:
[## MySQLdb 用户指南- MySQLdb 1.2.4b4 文档
MySQLdb 是流行的 MySQL 数据库服务器的接口,它提供 Python 数据库 API。文件有…
mysqlclient.readthedocs.io](https://mysqlclient.readthedocs.io/user_guide.html)
一旦安装了 MySQLdb,您就可以开始查询数据了。我首先从表中提取所有唯一的、按时间顺序排列的日期列表。这将是整个数据库中每个时间步长的黄金来源。
然后,我逐个提取股票的时间序列数据,并将其左连接到黄金时间序列源。然后,我将这个单独的数据帧添加到一个数据帧列表中,每个数据帧对应一只被提取的股票。我推迟了合并操作,将每个股票时间序列合并成一个数据集。
这种查询方法本身可以并行化,而从表中提取所有数据的单个查询是不可能的。
这给我们留下了一个带有公共时间序列索引的数据集列表,然后我们可以使用它。
这里我们提取股票列表,构建时间序列的黄金来源,然后遍历列表,以这种通用格式提取每只股票的时间序列数据。
我们从中提取股票数据的查询:
SELECT date, close as {} FROM history where stock = '{}'
获取股票的收盘价,并用该股票的代码命名该列。这意味着,我们将有 7,000 个列,每个列的名称都是“close ”,而不是 7,000 个列,每个列都用相关的股票代码命名,并映射到一个公共的时间序列。这就好办多了。
计算相关矩阵
好消息来了。我们将得到最终格式的数据,并开始计算相关矩阵。
这里,我们为列表中的每个数据帧设置日期索引,并将它们合并在一起。因为我们根据相关的股票代码来命名每一列,所以现在我们有了一个大表,它有一个作为索引的公共日期列表和一个针对每只股票的带标签的列。我们将通过删除所有“NaN”值的列来简化表格,这里没有相关数据。
这正是我们计算相关矩阵所需的格式,因此我们将:
import dask.dataframe as dd
并创建 Dask 数据帧
merged = dd.from_pandas(merged, 20)
此时,您需要做出一个重要的设计决策,该决策将显著影响相关矩阵的处理速度。
在这里,我们将 Pandas 数据帧转换为 Dask 数据帧,我们还必须指定一个数字,这里是“20”,表示数据集中的分区数量。
出于并行计算的目的,每个分区将被视为独立的单元。例如,如果您选择“1”,相关矩阵将由单个 CPU 上的单个线程计算,您将不会获得任何并行化优势。
另一方面,如果您将这个数字设置得太高,您的性能会受到影响,因为加载和处理每个任务会产生开销。
最初,我将它设置为小型集群中计算机上的线程数。然而,我很快发现其中一台机器慢得多,两台速度较快的机器闲置着,等待这台机器完成。为了减轻这种情况,我增加了进程的数量,以便速度较快的计算机可以继续处理后面的任务,而速度较慢的计算机可以继续处理最初分配的进程。
做出这个决定后,我们将合并的数据帧从 Float 32 数据类型转换为 Float 16 数据类型,因为额外的精度是不必要的,并且会减慢我们接下来的计算。
然后我们运行关键线路:
merged = merged.corr().compute()
这里有两个函数,。corr() 计算我们的相关矩阵,以及。compute() 发送给 Dask 进行计算。
在这个处理之后,我们将得到一个关联矩阵,显示每只股票之间的关联。下面是这种情况的简化示意图(由于我们没有计算矩阵,所以数据是虚构的
你会注意到这里有很多重复和不必要的数据。AAPL 和 APPL 的相关系数是 1。AMZN 与 AMZN 的相关性为 1。这将是你整个矩阵对角线上的情况。
同样,也有重复。AMZN 与 AAPL 的相关性显然与 AAPL 与 AMZN 的相关性相同(这是一回事!).
当您要将这些数据保存回数据集中时,复制所有这些数据是没有意义的,尤其是对于如此大的数据集。所以让我们摆脱这种重复。
您会注意到,不必要的数据值形成了数据集的一半,横跨一个对角线形状。所以在我们保存这个数据集之前,让我们屏蔽掉对角线。
corrs = merged.mask(np.tril(np.ones(merged.shape)).astype(np.bool))
这条线就是这么做的。它以我们的相关矩阵的形式创建了一个 1 的矩阵。然后,它使用 Numpy 返回数据的副本,并使用 tril()函数对给定的对角线进行清零。然后,通过将这些零转换为 bool 类型,它将这些零转换为 False 条件。最后,它使用 Pandas 将此作为掩膜应用于数据集,并将其保存为我们新的相关矩阵。
然后,我们可以将这个简化的矩阵转换成表格,然后上传回我们的数据库。
levels = merged.columns.nlevels...df1 = corrs.stack(list(range(levels))).reset_index()
del df1["level_0"]
del df1["level_2"]
df1.columns = ["Stock 1", "Stock 2", "Correlation"]
print("Uploading SQL")
df1.to_sql("correlations", con=engine, if_exists='append', chunksize=1000, index=False)
print("Process Complete")
在这里,我们使用 stack()函数将它转换回数据库的一个表。这给了我们一个类似这样的表格:
您可以看到,没有重复的值或冗余的 1 来显示股票与自身的相关性。然后,可以将其上传回数据库,整个过程就完成了。
矩阵的分布式计算
我们必须在代码中添加最后一行,也是至关重要的一行,但是首先我们需要创建我们的集群。
在每台计算机上,您都需要使用 Python 终端。如果您正在使用 Anaconda,您可以使用 GUI 启动终端,或者您可以进入 Anaconda 提示符并使用以下命令选择您的环境:
conda activate <environment>
您现在需要决定哪台计算机将充当调度程序并管理任务的分配,哪台计算机将充当工作程序。计算机完全有可能既是调度者又是工作者。
在调度器上,继续在 Python 终端中输入以下内容:
dask-scheduler
这将启动调度程序并管理集群的工作流。记下您完成此操作的本地网络 IP 地址,在我们的示例中,我们将使用 192.168.1.1。
我们希望这台机器也作为一个工作者,所以我们将打开另一个 Python 终端并键入:
dask-worker 192.168.1.1:8786
这应该表明我们已经在 worker 和 scheduler 之间建立了连接。
现在,我们需要在集群中的其他每台计算机上重复这个命令,每台计算机都指向位于 192.168.1.1 的调度程序。
我之前提到其中一台电脑比其他的慢。然而,它有两倍的 RAM,可以方便地在最后组装相关矩阵。我没有将它从集群中完全删除,而是决定通过限制 Dask 可用的线程数量来限制它可以运行的进程数量。您可以通过将以下内容附加到 Dask-worker 指令中来实现这一点:
dask-worker 192.168.1.1:8786 --nprocs 1--nthreads 1
现在您的集群已经启动并运行了,您可以在代码中添加关键的一行了。将以下内容添加到 Python 文件的顶部附*(或者在任何情况下,添加到对。corr()。计算():
client = _get_global_client() or Client('192.168.1.1:8786')
确保将 IP 地址替换为调度程序的 IP 地址。这将把 Dask 指向调度程序来管理计算。
现在,你可以运行你的程序了。
监控计算
Dask 提供了一个很好的仪表盘来监控你的计算进度。
转到 192.168.1.1:8787(或使用端口 8787 建立调度程序的任何 IP 地址)。
这将向您显示 Dask 仪表板,以便您可以跟踪进度并监控内核和内存的利用情况。
集群的内存和 CPU 使用情况
上面的“Workers”页面显示了集群中的节点及其当前的利用率水*。您可以使用它来跟踪哪些工作做得最多,并确保这保持在一个可接受的范围内。值得注意的是,CPU 利用率百分比是基于每个内核的,因此如果使用两个内核,您可以获得 200%的 CPU 利用率,如果使用八个内核,则可以获得 800%的 CPU 利用率。
Dask 仪表板状态屏幕
状态屏幕显示正在进行的任务的明细。上面的屏幕截图显示,我们已经将 20 个分区中的 4 个从 Pandas 数据帧转换为 Dask 数据帧,其他的正在整个集群中工作。我们还可以看到,我们已经存储了超过 4 GB 的数据,未来还会有更多。
Dask 仪表板图形屏幕
图形屏幕以图形方式显示任务是如何划分的。我们可以看到,每个分区都被单独处理,因此如预期的那样有 20 个分区。每个分区经历三个阶段,阶段 0,在那里它从 Pandas 转换到 Dask,阶段 1,在那里计算该特定分区的相关矩阵,以及阶段 3,在那里组合所有分区并计算互相关。
Dask 仪表板系统屏幕
系统屏幕显示当前系统的资源利用情况,包括 CPU、内存和带宽。这向您显示了在任何给定时间集群消耗了多少计算能力,对于查看网络上当前的压力和负载非常有用。
您可以使用这些屏幕来观察您的计算进度,您将逐渐看到各种任务在完成计算后变成绿色。然后,结果将通过网络传播到单个节点,该节点将编译结果并将其返回到您的 Python 程序。
正如你已经编码的那样,生成的矩阵将被精简到相关的对角线部分,制成表格,并上传到你的数据库。
结论
现在,您已经成功地设置了一个计算机集群,并使用它来计算相关矩阵,简化您的结果,并将其存储起来以供进一步分析。
您可以使用这些核心组件对您的集群做更多有趣的事情,从机器学习到统计分析。
让我知道你进展如何。
可以在 Twitter 上关注我 @mgrint 或者在https://Grint . tech查看更多来自我。发邮件给我matt @ Grint . tech。
在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
任何数据驱动项目的一个重要部分是它能够被容易地解释和查看,甚至对于那些事先可能对数据一无所知的人。Python 中的 Streamlit 介绍!
Streamlit 是一个极其易用和直观的工具,用于在 Python 中构建高度交互式、数据驱动的 web 应用程序。有了这个工具,您可以只关注数据方面,而不用担心使用 Flask 或 Django 进行繁琐的部署。
这个过程是完全直观的,到本文结束时,您也应该能够在几分钟内部署您的 web 应用程序,并且只需要几行代码!
设置项目
要开始使用我们的 Streamlit web 应用程序,我们必须首先使用 PyPi (Python 的包管理器)下载它。对于您可能没有的任何包(包括 streamlit ),只需在您的终端中键入以下内容,我们就可以开始了。
pip install streamlit
如果您的计算机上没有安装 pip,您可以快速浏览一下这篇文章并在安装后返回这里。
开始项目!
导入依赖项
首先,我们必须导入整个程序中需要的所有依赖项。我们将使用的主要库是 Yfinance、Streamlit 和 TaLib。Yfinance 将允许我们接收任何股票的历史股价,Streamlit 将允许我们将 web 应用程序部署到本地主机上,TaLib 将允许我们计算稍后将在应用程序上显示的技术指标。
现在我们已经设置了依赖项,我们终于可以开始构建我们的应用程序了!
设置 web 应用程序!
首先,为了运行程序,到你的终端键入命令streamlit run file _ name . py。用您选择从此程序创建的文件名替换 file_name.py。然后,一个 web 应用程序将在您的本地主机上打开!
为了在网站上写文章,我们可以使用。Streamlit 中的 write()方法。为了显示更大更粗的文本,我们可以在前面使用一个标签(# ), Streamlit 会自动改变文本,使其看起来像一个标题(Markdown formatting)。要加粗文本,我们可以简单地用两个星号将我们想要加粗的文本括起来。
接下来,我们可以使用 header 方法创建一个标题,并设置一个函数来获取用户的输入。因为我们正在创建一个技术分析 web 应用程序,所以我们需要一个股票代码、开始日期和结束日期。我们可以将值自动设置在那里,以防用户没有输入自己的值。在这种情况下,我使用苹果公司的股票代码(AAPL),2019-01-01 作为开始日期,今天作为结束日期。
为了使 web 应用程序看起来更好,我们可以创建一个向 Yahoo Finance 发送请求的函数,并检索我们输入的任何股票的公司名称!
我们可以使用 pandas.to_datetime()方法将开始和结束日期从字符串转换为日期时间格式,这样我们就不会在以后出现任何错误。最后,我们可以用前面提到的 Yfinance 模块检索历史股票数据。只是为了跟踪我们目前所处的位置,这是 web 应用程序现在应该呈现的样子。
到目前为止,web 应用程序应该是什么样子!
现在,剩下的工作就是创建我们将要显示的图表。
计算并绘制技术指标!
正如我之前提到的,奇妙的 TaLib 库使得计算我们将要绘制的技术指标变得简单得可笑。对于这个项目,我选择了流行的指标,如移动*均线交叉,布林线,移动*均线收敛发散,商品通道指数,相对强度指数,和总成交量。要想更深入地了解这些指标能显示什么,我建议在Investopedia.com上查找它们。
对于这一部分代码,我们将显示每个指标的三个主要部分。首先,我们必须使用 TaLib 计算指标,然后我们必须设置一个包含指标实际内容的标题,最后,我们可以使用 streamlit.line_chart()显示图表。这就够了!
现在,我们有了一个完全交互式的、实时的、响应迅速的、易于使用的 web 应用程序,只需几分钟,代码不到 100 行。如果你一直遵循这段代码,我们应该有一个类似的网站如下。
网站现在应该是什么样子!
我已经在下面的 GitHub 要点中包含了这个程序的全部代码!
后续步骤
在接下来的步骤中,你可以扩展我们显示的数据,因为 Streamlit 允许用户显示几乎任何类型的数据,包括熊猫数据帧。或者您可以决定使用 Heroku 将它部署到云上(如果您想这么做,请查看汉密尔顿·张的文章)。既然您已经知道如何创建自己的数据 web 应用程序,那么这种可能性真的是无穷无尽的!
非常感谢你的阅读,我希望你喜欢它!
注意:如果您正在将这个特定的应用程序部署到 Heroku,请记住从 requirements.txt 中删除 TaLib,而是为它添加一个build pack!
如果你喜欢这篇文章,可以看看下面我写的其他一些 Python for Finance 文章!
了解如何在不到 3 分钟的时间内解析顶级分析师的数千条建议!
towardsdatascience.com](/parse-thousands-of-stock-recommendations-in-minutes-with-python-6e3e562f156d) [## 用 Python 进行股票新闻情绪分析!
对财经新闻进行秒级情感分析!
towardsdatascience.com](/stock-news-sentiment-analysis-with-python-193d4b4378d4) [## 在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
towardsdatascience.com](/creating-a-finance-web-app-in-3-minutes-8273d56a39f8)
使用 Python 创建财务仪表板
用 Dash、Plotly 和 Python 构建财务仪表盘
这篇文章将会和我之前的有所不同。我们不会使用 Python 来分析财务数据或评估公司价值。相反,我们将学习如何使用 Plotly 和 Dash 用 Python 创建一个财务仪表板。下面是我们将建立什么样的样本!
使用 Python 的财务仪表板
使用 Python 的交互式仪表盘
Plotly 是一个伟大的免费 Python 库,用于创建交互式图形和仪表板。 Plotly 文档非常棒,对于开始创建我们自己的交互式图形来说已经足够了,不需要了解 HTML、CSS 或 JavaScript。当然,先前的 HTML 知识会对你有利,但这不是必需的。
在这篇文章中,我将介绍拥有一个可工作的交互式仪表板的基本部分。
我们将建立一个非常基本的仪表板,在这里我们将在几秒钟内输入一家公司的股票 和,我们将在屏幕上看到两个财务条形图 显示一段时间内公司的收入和净收入。
一旦你很好地理解了我们在做什么以及 Dash 是如何工作的,你就能够自己创建更高级的仪表板了。
在 Python 中启动我们的财务仪表板—安装 Dash
首先,在开始安装 Dash 的代码之前。我们可以通过在终端中运行下面的命令很容易地做到这一点。如果您已经安装了 Dash,可以跳过这一部分。
pip install dash==1.12.0
很好,有了这一行代码,您已经安装了运行 Dash 所需的所有组件。
Dash 如何工作
我们用 Dash 做的是创建一个将在我们本地服务器上运行的应用程序(我稍后将创建一个帖子,展示如何用 Heroku 在线发布它)。
Dash 应用由两部分组成。一个布局组件,我们将需要输入 HTML 来创建页面的设计。和定义应用功能的第二组件。
为了用 Python 创建我们的财务仪表板,我们将有一个非常基本的布局。让我们开始我们的交互式仪表板,并在下一部分提供基本布局。
创建财务仪表板布局
我们将在一个标准的 Python 文件中编写代码,例如, dashboard_fin.py 。首先,我们导入所有需要的包。使用核心 Dash 功能需要以下四行代码。它们取自 Dash 文档。如果您想了解每个软件包提供的更多信息,请随意查看:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
核心组件是我们将使用在我们的财务仪表板中创建交互式仪表盘图的组件。这个 HTML 组件将被用来构建和设计页面的布局。最后,我们还从 dash 依赖项中导入输入和输出。
输入将是用户在仪表板中输入的信息。这些输入将进入 Python 函数,以便返回某些数据。
另一侧的输出将成为我们绘制数据的图形占位符。我们将构建一个 Python 函数,它将接受输入,转换输入,并以图形的形式返回输出,以包含在 HTML 布局部分中。
然后,我们将使用请求从 API 获取数据,并使用构建我们的图表。
import requests
import plotly.graph_objects as go
接下来,我们可以通过传递下面的代码行来创建应用程序。这将创建一个包含我们的应用程序的对象:
app = dash.Dash()
最后,我们准备创建页面布局。我们将有一个 H1 标题,然后是一个 Div 部分,包含我们的输入文本框和两个图形:
app.layout = html.Div([
html.H1('Financial Dashboard'),html.Div([
dcc.Input(id='company_selection',value='AAPL'),
html.H3(id='text'),
dcc.Graph(id ='revenue'),
dcc.Graph(id ='netincome'),
],style= {'padding':10})
])
因此,在我们的 Div 部分中有三个元素:
- 输入:文本框,用户将在其中输入要求财务数据的公司名称。
- 图形:这是显示图形的占位符。它将是我们财务仪表板的主要组成部分。图形数据和类型将构建在我们的 Python 函数中。
- H3 :占位符,显示我们正在显示数据的公司的名称。
请注意,每个组件的 id 将在稍后的 Python 函数中用作引用,以便传递数据或输出数据。它是布局和财务仪表板功能之间的链接。
在我们的 Python 财务仪表板中构建功能
现在我们已经准备好了布局,我们可以简单地创建一个函数来获取数据并创建一个交互式图形。我们将使用 financialmodelingprep 来检索公司的财务状况。它每月提供 250 个免费的 API 调用。
对于我们的 Python 财务仪表板,我将只检索公司收入和净收入收入。由于我在之前的帖子中已经多次提到过这个部分,所以我将不再赘述如何检索财务数据的细节。
然后,我们的函数将返回字典形式的数据点,我们将使用 Plotly 来构建图形。更多关于如何绘制图表的细节可以在这里找到。然后,我们使用一个 app.callback() decorator 来将输入和输出链接到我们的 Dash 布局组件。这是让我们的财务仪表板能够互动并响应用户输入的部分。
我们将有两个功能。每个功能都将在我们的财务仪表板中显示单个条形图的数据。第一个,它将绘制一段时间内公司的收入。
[@app](http://twitter.com/app).callback(Output('revenue','figure'),
[Input('company_selection','value')])
def retrieve_revenue(company):
demo = 'your api key'
stock = company
print(stock)
IS = requests.get(f'[https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'](https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'))
IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0
for item in IS:
Revenues.append(float(IS[count]['Revenue']))
Dates.append(IS[count]['date'])
count += 1
print(Revenues)datapoints = {'data': [go.Bar(x=Dates, y=Revenues)],'layout': dict(xaxis={'title':'Date'},
yaxis={'title':'Revenue'},
)}return datapoints
而第二个函数将绘制公司随时间的净收入。注意,这两个函数几乎是相同的。我们只改变了 id 和字典键来从 API 中提取净收入。
[@app](http://twitter.com/app).callback(Output('netincome','figure'),
[Input('company_selection','value')])
def retrieve_revenue(company):
demo = 'your api key'
stock = company
IS = requests.get(f'[https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'](https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'))
IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0
for item in IS:
Revenues.append(float(IS[count]['Net Income']))
Dates.append(IS[count]['date'])
count += 1
datapoints = {'data': [go.Bar(x=Dates, y=Revenues,marker_color='lightsalmon',name='Net Income')],
'layout': dict(xaxis={'title':'Date'},
yaxis={'title':'Net Income'},
)}return datapoints
仪表板输入-输出链接
这两个函数都返回用户在财务仪表板输入框中请求的公司财务数据。例如,如果用户传递股票代码 AAPL ,Python 函数将把 AAPL 作为输入,并将其传递给 url 请求,以便检索苹果的财务数据。
然后,我们将每年的收入(或净收入)和日期添加到一个 Python 列表中,并将它们传递给 go。条形图图创建一个条形图。
最后,该函数返回一个包含图形数据点和布局的字典,然后在具有相同 id 的 HTML 图形占位符中输出。
为了让用户知道我们正在显示数据的公司的名称,我们可以在 HTML 布局中添加一个链接到我们的 H3 部分的附加函数:
[@app](http://twitter.com/app).callback(
Output(component_id='text', component_property='children'),
[Input(component_id='company_selection', component_property='value')]
)
def update_output_div(input_value):
return 'Displaying Data for "{}"'.format(input_value)
启动财务仪表板
要运行财务仪表板,我们只需添加以下代码行,并从终端运行我们的 python 脚本。
if __name__ == '__main__':
app.run_server(debug=True)
然后,该应用程序将在您的本地服务器上运行于http://127 . 0 . 0 . 1:8050/。你可以简单地在浏览器中复制并粘贴网址,你应该能看到类似下面的内容。只需在文本框中输入任何一家公司的股票代码,图表就会交互更新!
带破折号的财务仪表板
现在,您应该准备开始构建自己的更高级的仪表板了。订阅我的社交媒体频道,在我的下一篇文章中了解如何在线发布仪表板。
查看我一步一步解释代码的视频教程:
***import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output import requests
import plotly.graph_objects as goapp = dash.Dash() app.layout = html.Div([
html.H1('Financial Dashboard'),
html.Div([ dcc.Input(id='company_selection',value='AAPL'),
html.H3(id='text'), dcc.Graph(id ='revenue'),
dcc.Graph(id ='netincome'), ],style= {'padding':10})
])
@app.callback(Output('revenue','figure'), [Input('company_selection','value')])
def retrieve_revenue(company):
demo = 'your api key'
stock = company print(stock)
IS = requests.get(f'https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}')IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0for item in IS:
Revenues.append(float(IS[count]['Revenue']))
Dates.append(IS[count]['date'])
count += 1 datapoints = {'data': [go.Bar(x=Dates, y=Revenues)],'layout': dict(xaxis={'title':'Date'}, yaxis={'title':'Revenue'}, )} return datapoints @app.callback(Output('netincome','figure'), [Input('company_selection','value')])
def retrieve_revenue(company): demo = 'your api key'
stock = company
IS = requests.get(f'https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}')
IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0 for item in IS:
Revenues.append(float(IS[count]['Net Income']))
Dates.append(IS[count]['date'])
count += 1datapoints = {'data': [go.Bar(x=Dates, y=Revenues,marker_color='lightsalmon',name='Net Income')], 'layout': dict(xaxis={'title':'Date'}, yaxis={'title':'Net Income'}, )}return datapoints @app.callback( Output(component_id='text', component_property='children'), [Input(component_id='company_selection', component_property='value')] )
def update_output_div(input_value):
return 'Displaying Data for "{}"'.format(input_value)if __name__ == '__main__': app.run_server(debug=True)***
原载于 2020 年 6 月 14 日 https://codingandfun.com。
创建一个 Flask 应用程序,使用自然语言处理对消费者投诉进行分类(使用 TFIDF/Word2Vec 进行多类分类)
在本系列的第 1 部分中,我将介绍获得产生这些分类的工作模型的工作流程。这篇文章将在第 2 部分继续,其中涉及到构建这个模型的 Flask 应用程序。因此,如果你只是对烧瓶部分感兴趣,点击 这里(仍在生产) 前往那个职位!
事不宜迟,让我们直接进入本系列的第 1 部分。
I am dissatisfied with the current outcome of a dispute that was initiated with Discover Card regarding a single transaction that occurred on XXXX/XXXX/2015 in the amount of {$280.00}. I have corresponded with Discover Card at least four times since XXXX/XXXX/2015 ( which I have enclosed as an attachment to this complaint ). I believe that the credit card issuer has violated consumer protection laws by failing to implement the Special Rule for Credit Card Purchase protection despite overwhelming paperwork evidence submitted by me that shows the merchant has conducted business in bad faith less favorable to the consumer. I have sustained a monetary loss as a result of merchants bad faith and intent. I have patiently utilized the internal Discover Card dispute process over the past three months with the credit card issuer always favoring the merchant ; I have repeatedly submitted irrefutable paperwork evidence that has shown that the merchant has conducted business in bad faith. I have tried in good faith to address my complaint with the merchant and Discover Card but believe that I will not receive a favorable outcome. I suffered a work-related injury in XXXX and am now permanently XXXX. My income has dropped substantially in the past 5 years and I am now on a low fixed income. On XXXX separate occasions from XXXX to XXXX I had zero income and had to use my Sams Club card to purchase food, thus running up debt out of necessity. I am currently in the process of attempting to pay down my Sams Club ( Synchrony Bank ) card, and stopped using the card some time ago. I have always made at least minimum payments and have never missed a payment. Despite this, my interest rate has been unilaterally raised three times in the past two years, and is now at 23.15 %. I called a Sams Club account rep today to file a complaint over the phone, because I am never going to be able to pay down this card when I am paying almost {$50.00} a month in interest and can only afford to pay the minimum payment + {$4.00} or {$5.00} dollars. They would not work with me, which I expected. In my opinion, Synchrony Bank is taking unfair advantage of recent interest rate hikes to gouge customers, especially those who are financially unable to make substantial monthly payments on their accounts. Therefore I am contacting the CFPB to file a complaint through which I might receive some relief.
停下来。
你读了上面的文字墙了吗?很可能你没有(因为我也不会)。谁有时间去阅读一整块没有标题、没有摘要和糟糕的格式(粗体、空格等等)的文本呢?)可能与你无关?
如果你真的读了上面的文字,你会意识到它们实际上是客户投诉。正如盖茨先生曾经提到的:
如果他这么说,那一定是真的。(图片来源:图片 via me.me )
我们可以从顾客的投诉中学到很多东西。最好的品牌总是与他们的消费者联系在一起,并围绕他们做出决定(想想派拉蒙,因为糟糕的评论和随之而来的无数迷因,派拉蒙实际上推迟了索尼克电影的上映日期,并改变了我们最喜欢的蓝色刺猬的设计)。
真正可怕的设计(左),但值得称赞的是你,派拉蒙,重新设计(右)和倾听你的观众(来源:汤姆·巴特勒通过 sg.news )
理解你的客户固然重要,但这并不能改变这样一个事实:愤怒的客户发来的一大堆文字不仅让人难以通读,还会让人失去动力。很多时候,这不仅仅是一个投诉。
好的,是的,如果你仔细看,他们都是同样的抱怨。但是你明白我的意思。
通常,在银行(或任何大公司的客户服务部门),他们每天都会看到数以千计这样的投诉,它们不会像上面这些投诉那样措辞和格式令人愉快。
客户投诉的实际图像(来源:via yandex )
此外,消费者自己可能会将投诉归入错误的部门,而审查每一项投诉并将其转交给相关部门处理将是一项巨大的痛苦。不用说,这些缓慢的处理时间也会导致对客户的缓慢响应时间,从而导致您已经愤怒的客户变得更加愤怒。
伙计,如果有什么方法可以快速分类每一个抱怨来加快你的阅读过程就好了。
项目目标
事实证明是有的,这正是这个项目的目的。在我的项目中,我开发了一个模型,可以根据产品类型对客户投诉进行正确分类,准确率达到 83%。
该应用程序利用机器学习算法结合自然语言处理(NLP)方法来处理文本,以预测投诉所指的产品类型。
定义问题
这本质上是一个 监督(标签) 文本多类分类问题,我们的目标是用一个新的输入(投诉)进行预测(分配到正确的类别)。在下面的步骤中,我将带您完成以下内容:
- 数据探索(了解数据集)
- 数据(文本)预处理和在 NLP 项目中可能采取的一般步骤
- 型号选择以及为什么使用 Micro-F1 作为评估指标
- 选定型号的最终测试和结果
在本系列的第 2 部分中…
- Flask 应用程序演示&如何构建一个
这个项目的所有代码都可以在我的 github 上找到。如果你需要任何澄清,请随时给我留言。
第一部分:数据
用来训练这个模型的数据取自 这里 。并且对每一列的含义的解释可以在 这里 找到。它本质上是一个由消费者金融保护局(CFPB)提供的关于金融产品和服务投诉的标签数据集。
我们观察到数据集包含 1,437,716 行数据,以及许多许多包含空值的行。
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1437716 entries, 0 to 1437715
Data columns (total 18 columns):
Date received 1437716 non-null object
Product 1437716 non-null object
Sub-product 1202551 non-null object
Issue 1437716 non-null object
Sub-issue 887045 non-null object
Consumer complaint narrative 463991 non-null object
Company public response 530104 non-null object
Company 1437716 non-null object
State 1414039 non-null object
ZIP code 1304830 non-null object
Tags 196144 non-null object
Consumer consent provided? 831624 non-null object
Submitted via 1437716 non-null object
Date sent to company 1437716 non-null object
Company response to consumer 1437715 non-null object
Timely response? 1437716 non-null object
Consumer disputed? 768482 non-null object
Complaint ID 1437716 non-null int64
dtypes: int64(1), object(17)
memory usage: 197.4+ MB
然而,由于我们只分析投诉本身,我们只需要Product
和Consumer Complaint Narrative
列。
在只保留我们需要的列、删除空值并将它们重命名为更好使用的名称之后,我们只剩下 463,991 行数据:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 463991 entries, 4 to 1437711
Data columns (total 2 columns):
PRODUCT 463991 non-null object
CONSUMER_COMPLAINT 463991 non-null object
dtypes: object(2)
memory usage: 10.6+ MB
在PRODUCT
列上做一点 EDA(探索性数据分析),我们观察到 18 个类,分布如下:
然而,有些类别(产品)是重叠的。例如,Credit card or prepaid card
与Credit card
和Prepaid card
冲突。此外,还有一个问题是要对 463,991 行执行分析(我怀疑您的笔记本电脑是否能够在这个项目的后期处理和模型训练。我的不行)。
在删除了一些产品类别、合并了一些类别并减少了 90%的观察数量之后(具体情况可以在我的 jupyter 笔记本上的我的 github 中找到),我们还剩下大约 30,000 行投诉(对于 8GB ram 的笔记本电脑来说,这是可以管理的)。
<class 'pandas.core.frame.DataFrame'>
Int64Index: 31942 entries, 72 to 1437623
Data columns (total 2 columns):
PRODUCT 31942 non-null object
CONSUMER_COMPLAINT 31942 non-null object
dtypes: object(2)
memory usage: 748.6+ KB
具有以下分布:
不算太寒酸。现在我们准备对我们的投诉数据进行一些文本预处理!
第 2 部分:文本预处理
第 2.1 节:简单地说,自然语言预处理
现在我们有了数据,我们需要完成清理过程。对于一般的文本数据集,这通常包括:
- 删除标点符号
- 删除停用词(如“the”、“this”、“what”)
- 小写字母
- 标记化
- 词干化/词尾化(通过删除诸如“-ed”、“-ing”等后缀,将单词简化为基本形式)
如果您正在处理在线文本和评论,您将需要进一步删除超链接、用户名和自动消息等项目。
您所做的预处理因项目而异,为此,除了词干化和词汇化之外,我取得了最好的准确度分数。我考虑过使用二元语法,但是这导致我经常遇到内存问题,因此我只能考虑一元语法(标准的单词标记化)。
下面是一个删除标点符号、删除停用词、小写字母及其后的标记化的示例。
删除标点符号
停用词删除
用小写字体书写
标记化
第 2.2 节:矢量化
为了将单词转化为机器学习算法可以理解和处理的东西,我们需要做一些叫做 矢量化 的事情。简而言之,这是将单词转化为多维向量的过程,其方式是将单词的含义或上下文与向量指向的位置相关联。从某种意义上来说,矢量化允许计算机通过将相似的词义映射到相似的向量空间来量化词义。
第 2.3 节:词频—逆文档频率矢量化
对于这个应用程序,我们需要基于上下文的矢量化。这就是术语频率逆文档频率矢量化的用武之地(TF-IDF)。这种矢量化方法查看一个单词在注释中出现的次数相对于它在其他注释中出现的次数。两件事导致 TF-IDF 得分更高:
- 该词在被评分的具体投诉中出现的频率较高。
- 该词在所有其他投诉中出现的频率较低。
第 3 部分:培训方法
第 3.1 节:模型评估分数
在多类场景中,按照二进制分类不再像 Precision、Recall 和 F1 那么简单(就像我之前的项目对违约者进行分类)。这是一个全新的水*。如果您需要更好地理解多分类场景中模型使用的评估指标,我建议参考 Boaz Shmueli 关于多分类器中评估指标的解释。他很好地解释了关于精确度、召回和 F1 评分的 二进制 和 多类 分类指标。
和这个多分类案例一样,我决定使用 Micro-F1 作为模型间的评价指标,因为它衡量模型的 整体精度 。
第 3.2 节:使用的模型
我已经决定使用以下模型进行多类文本分类:
- 多项式朴素贝叶斯
- 高斯朴素贝叶斯
- 逻辑回归
- 随机森林
- 线性 SVC
第 4 部分:型号选择
每个模型都在分层的【5 折分裂训练-验证(整个数据集的 80)上交叉验证。剩下的 20%的数据被保留下来,用于模拟我们最终选择的模型在现实生活中的表现,在现实生活中,它必须处理从未见过的输入。
像往常一样,如何做到这一点的代码可以在我的 jupyter 笔记本(笔记本的第 3 部分)的 github 上找到。
从我们的初步培训结果中,我们看到:
逻辑回归给了我们 83%的最高准确率。
83%是相当高的准确率。但是我们能做得更好吗?我们不能确定是否会更好,但我们可以一直尝试,努力改进。
第 4.1 节:斯坦福手套和谷歌的 Word2Vec
我们可以在将投诉输入到我们的分类器之前,利用嵌入在投诉上的单词,而不是利用矢量化(我们上面使用的 Tf-idfVectorizer)。要了解更多关于 WordEmbeddings 的内容,你可以在这里找到它。
一些方法,如 Word2Vec 和 spaCy 涉及预先训练的、数千兆字节的模型,这些模型在数十万(如果不是数百万的话)文档上训练,然后将单词的含义减少到一组数百个数字。一般来说,这些模型在保持单词的上下文和含义方面非常出色,但是它们速度慢且庞大。
对于这个项目,我选择了使用斯坦福德的手套(越来越复杂)和谷歌的 Word2Vec。
第 4.2 节:tfidf 矢量器和 word 嵌入结果
综合所有测试结果,我们发现普通 TFidfVectorizer 比 WordEmbeddings 要好得多,最高准确率为 83%。
因此, 使用逻辑回归的 TfidfVectorizer被选为本项目使用的文本预处理和模型组合。
第 5 部分:在看不见的数据上测试我们最终选择的逻辑回归分类器
还记得我们之前保留的没有在交叉验证中使用的 20%的数据吗?接下来,我们使用它来评估我们的模型在看不见的数据上的表现(即,它在现实生活中的表现)。
这个模型在看不见的数据上给了我们 83%的准确率! 表示它可以在 83%的时间内正确地对投诉进行分类!现在我们有了自己的模型,我们终于可以看到它的实际应用了!
第 6 部分:烧瓶演示
为了能够以用户友好的方式使用我们的模型,我创建了一个 Flask 应用程序,允许输入投诉。此后,该应用程序将预测投诉属于哪个产品类别。参考这个 post (WIP) 了解一下 Flask app 是怎么创建的!
*I am dissatisfied with the current outcome of a dispute that was initiated with Discover Card regarding a single transaction that occurred on XXXX/XXXX/2015 in the amount of {$280.00}. I have corresponded with Discover Card at least four times since XXXX/XXXX/2015 ( which I have enclosed as an attachment to this complaint ). I believe that the credit card issuer has violated consumer protection laws by failing to implement the Special Rule for Credit Card Purchase protection despite overwhelming paperwork evidence submitted by me that shows the merchant has conducted business in bad faith less favorable to the consumer. I have sustained a monetary loss as a result of merchants bad faith and intent. I have patiently utilized the internal Discover Card dispute process over the past three months with the credit card issuer always favoring the merchant ; I have repeatedly submitted irrefutable paperwork evidence that has shown that the merchant has conducted business in bad faith. I have tried in good faith to address my complaint with the merchant and Discover Card but believe that I will not receive a favorable outcome.*
还记得我们在帖子开头看到的这面可怕的文字墙吗?让我们来测试一下我们的模型。花点时间读读它,猜猜它属于哪一类。
现在让我们来测试一下我们的应用程序。
有没有得出和 app 一样的结论?现在让我们用一个更难的投诉来试试,充满了错别字、大写字母和千年行话。
*Omg where is my money?????? AOSINIONSAD WHY DID YOU GUYS TAKE MY MONEY AWAY. SAOIDNSIOADNOIAODNNIOASDNSADNOSDNOASDNI TRANSFERRED IT LAST NIGHT AND I WOKE UP TO NOTHING IN!!!!!! MY BANK ACCOUNT. HELP PLEASE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!I NEED THE MONEY OR ELSE I WILL BE HUNTED BY LOAN SHARKS!!!!!!!!YOU *(#&$) PEOPLE HAVE NO RIGHT TO DO THIS TO ME!!!! I NEED MY MONEY!!!!*
我们看到该应用程序可以正确无误地处理混乱的投诉,并正确地将它们归类到哪个产品类别。
第 7 部分:应用优势
使用这个应用程序可以带来很多好处,从解放人力到提高筛选客户投诉的效率。
第 8 节:未来的工作
作为对该应用程序的增强,以提高其实用性,我们或许可以研究以下内容:
话题总结 目前 app 只将投诉归入正确的产品类别。虽然它确实将投诉归入了正确的类别,但客户投诉专员仍然需要通读投诉本身。这将是很好的应用程序也能够提供一个简短的投诉是什么的摘要。这将是一个有趣的话题,可以研究和设计如何创建这个功能。
投诉优先级 我们还可以考虑实施情感分析,以提供某种投诉优先级。听起来更愤怒的投诉(即愤怒的客户)可以被给予更高的优先级,因为愤怒的客户往往会提供最多的问题。因此,我们的应用程序将为客户投诉官自动排序,以确定哪些投诉需要紧急处理。
- 问是否有人愿意一起工作,可以给我发短信
第九节:离别赠言
这绝不是一篇短文,希望在你的数据科学之旅中是一篇信息丰富的文章。感谢您花时间阅读到这里。
如果你学到了什么,觉得这很有帮助,或者认为它可能会帮助这个领域的人,请与他人分享这篇文章!
和往常一样,在 xianjinseow92@gmail.com,你可以随时通过我的 Linkedin 或 gmail 联系我!
干杯。
创建一个全自动的每日幻想运动策略
虽然我在 DFS 中没有盈利——但我非常接*,并且很开心地制定了一个策略,在实时 NBA DFS 比赛中部署真金白银。
DFS 自动化策略的最后一步:在 FanDuel 上自动加入比赛。由于该网站是基于 JS 的,我们必须模拟一个真正的网络浏览器,并点击网站进入竞争
2018 年末,我(在我哥哥扎克的帮助下)着手创建一个全自动的每日幻想体育(DFS)战略。我的目标是在假期期间做一个有趣的兼职项目,而不是赚钱——我从来没有玩过任何日常幻想游戏,但我是一个半铁杆篮球迷,也是一个铁杆数据迷。
下面是我如何在 2018 年底的两个月时间里制定了一个 DFS 战略的故事,虽然不太有利可图,但完全不干涉,超过了*均的 DraftKings 球员,总体胜率为 40%,并在 2018-19 NBA 赛季的前 5 个月中的 2 个月实现了盈利。
从一开始,我就列出了这个项目的一些要求。我最初将范围限制在 NBA 双打和 DraftKings 上的 50/50 比赛。
要求
- 自动化:整个流水线都要自动化,包括参赛。
- 可回溯测试:策略应该是完全可回溯测试的。我们应该能够验证对管道的任何部分所做的任何更改都会带来比过去更好的性能。
- 人工干预:不需要任何“专家知识”。例如,如果勒布朗在与科怀比赛时一直表现不佳,策略应该找出这一点,而不是被明确告知。
- 可复制:使用的任何数据都应该是公开信息(即没有订阅服务,没有像现金额度这样的私人历史数据)。
- 可扩展:管道应该扩展到多个*台和运动,只需最少的额外工作。
- 快速:开发应该是流畅的——管道的每一步都应该能够独立运行,长时间运行或 I/O 密集型的部分应该很少执行并且离线。
管道
这里有一个“实时模式”管道步骤的概述,或每天运行的进入幻想联盟的过程。“模拟模式”(回溯测试)只是其中一些步骤的重组和循环——我们将在后面讨论。
“实时模式”管道步骤
- 统计数据检索:我们从检索历史数据开始,我们将使用这些数据作为每个玩家预测的基础。对于 NBA 来说,这包括从篮球参考收集的每场比赛的球员和球队统计数据,以及辅助数据,如投注线和预测的首发阵容。
- 特征创建:使用这些统计数据,我们构建派生的特征,这将是学习算法的基础。
- 模型拟合:使用这些衍生特征,我们拟合一个模型,该模型创建了从特征到幻想点的映射。
- 花名册检索:检索当天符合条件的球员、伤病情况、比赛,以及最重要的每位球员的成本(薪水)。
- 预测特征创建:应用我们用来创建特征的相同过程,将花名册数据转换成我们用来创建球员模型的完全相同的特征。
- 预测:使用我们之前创建的模型和预测功能,我们进行幻想点预测。
- 团队选择:我们运行一个线性优化(根据薪水和职位限制最大化预测的幻想点数)来产生我们将加入的团队。
- 联赛参赛:我们使用一个 Selenium 机器人来导航幻想场地,找到目标比赛(例如,50/50 全石板,25 美元参赛费),并进入我们的团队。
在接下来的几节中,我将深入一些(希望如此)有趣的具体步骤。
特征创建
在理想世界中,我们可以给一个学习算法原始数据,然后弹出我们想要的信息(幻想点预测)。这是图像识别和自然语言处理等许多领域的当前最新技术。虽然对于这个问题来说这很有可能,但我采用了创建手工特征的方法,提前“手动”提取高阶信息,而不是让学习算法来做这件事。
为了创建这些特性,我构建了一个名为“特性框架”的工具,它允许我们的特性被表达为一个有向无环图 (DAG)。注意,这不是我的新想法,这是研究驱动交易的常见模式。这种方法有两个主要优点:
- 我们不必为依赖于相同或重叠子特征的多个特征重复计算。
- 框架一起处理连接特性——我们不必明确地说幻想点需要在幻想点之前计算。
计算的 DAG 表示(1 + 2) * 4 = 12
案例分析:防守 vs 站位
我们将“防守对位置”(DVP)定义为在预先定义的时间范围内,对手在给定位置允许的历史得分。在 NBA DFS 中,如果对手通常允许针对给定位置的超大分数,这可能会很有趣(想象一下霍福德对约基奇的比赛)。
DvP 特征的 DAG 表示。请注意,尽管许多要素都依赖于这种计算,但幻想点只计算一次。
当我们完成特征创建过程时,我们将有一个二维矩阵(玩家-游戏特征)用于学习算法:
上面的列是特征值的例子,其中行对应于唯一的玩家-游戏组合。我们明确地不给算法“过度指定”的信息,比如该行对应于哪个玩家。
模型创建
该模型的目的是考虑特征(具体来说,玩家历史表现的各个方面)并预测他们在给定游戏中会获得多少幻想点数。我们使用所有可用的历史数据来拟合该模型,2018-19 赛季的历史数据约为 60,000 行(也使用 2017-18 年的数据)。
需要注意的是,在这个问题的表述中,我们试图最小化(预测的优点 - 实现的优点)。这至少有两个潜在的缺点:
- 这种方法对每个玩家和每个游戏都一视同仁,而实际上,有些玩家可能比其他人更重要。
- 最小化玩家预测的误差可能与赢得比赛没有直接关系。
这里有一个关于前 5 个游戏的跟踪幻想点数的线性回归的输出权重的例子,这是我们能做的最简单的模型。交叉验证的*均误差(MAE)为 6.4 幻想点。
简单变量的线性回归系数——这些系数验证了我们的直觉,即最*的游戏更能预测性能
非线性拟合
我们可以通过使用更现代、也是最重要的非线性拟合算法来大幅改善这些拟合结果。在尝试了许多非线性模型,如 SVM、神经网络和决策树之后, XGB 模型表现最佳。这并不令人惊讶,因为 XGB 经常在关于表格数据的 Kaggle 获奖条目中使用。即使在这一组简单的功能上,XGB 也将交叉验证的 MAE 降低到大约 6.2 。在全套功能上,我们使用 XGBoost 可以实现每个玩家只有**5.38fantasy points**的*均误差。相比之下,FanDuel 的股票预测误差更接*于 6.0 ,在评估付费订阅模式时,我找不到任何低于 5.5 的预测。
XGB 回归函数下特性的相对重要性——有趣的是,我们的衍生特性(如“相对速度”)和其他数据挖掘特性(如“投注线”)正在增值!
预言;预测;预告
预测是将先前拟合的模型应用于日数据以获得幻想点预测的流水线步骤。对于线性回归模型,计算是简单的点积:
线性回归预测是特征系数的简单点积*
XGB 预测是通过遍历决策树进行的。下面是 XGB 模型对 2019 年 1 月 30 日艾尔·霍福德幻想点的预测(27.6)的细分。
2019 年 1 月 30 日艾尔·霍福德 XGB 预测分解。
回溯测试
许多(通常比我好得多的)DFS 玩家采取的方法中的一个陷阱是无法评估他们的策略改变是否成功。如果没有回溯测试的能力,可能就没有足够的数据来自信地说变革是有益的。为了解决这个问题,我确保管道的每一部分都可以在过去的任何日期重新运行(当然不考虑未来)。以下是模拟的步骤:
模拟工作流—我们可以并行化机器中的部件(每天模拟),这意味着只要有足够的计算,工作流就和“实时模式”版本一样快!
为了确定过去比赛的现金额度,我们可以使用 RotoGrinders ResultsDB 从 DraftKings 的“双倍”比赛中获得历史现金额度。请注意,当我在管道上工作时,我使用了我参加的 Fanduel 50/50 竞赛的私有结果。
回溯测试工作流程如下:
- 创建可能具有某种预测能力的特征(例如,DvP)
- 检查模型误差的一些度量是否减少
- 验证历史比赛成绩有所提高
2018-19 赛季模拟胜率。管道开始时相当强劲,在 60 天内赢得率超过 60%,然后稳定在 40%左右。
虽然我没有记录我所做的改进,但是回溯测试框架允许我们迭代地添加回特性,以查看它们如何改进我们的性能。从这些结果中,我们可以验证:
- 新功能减少了模型误差
- 模型误差减少导致竞争绩效提高
拟合误差(MAE)与模拟胜率。我们可以看到,拟合误差越低,模拟成功率越高。
最终,我们的模型在整个赛季中取得的最佳成绩是 39%的胜率,其中 50%在我们评估的双倍现金额度中是有利可图的(在 50/50 竞争中,截止值为约 56%)。我们在赛季的前两个月也做得更好。我推测,算法没有考虑到的功能,如球员匹配,在赛季结束时变得越来越重要。
2018 赛季的累计胜率:模拟胜率为 39.16%,预测胜率为 54%——根据 DraftKings 的统计,比普通玩家要好!
其他 DFS 的经验教训
如果你阅读这篇文章是为了学习如何成为一名盈利的 DFS 玩家,很遗憾我不能帮你实现这个目标——但是我可以分享我学到的一些东西,这些东西似乎对(在某种程度上)具有竞争力是必要的。很可能通过一些额外的“秘方”或预测的调整,一个经验丰富的玩家可以在一个几乎自动化的系统下变得有利可图。
- 有时你可能真的认为一个想法很棒,但它并没有改善你的策略,甚至导致倒退!
- 首发阵容:你必须得到正确的首发阵容/后期抓痕才有机会参赛,即使每五场比赛中有一场搞糟也能侵蚀你的优势。
- 受伤&替补球员: DFS,尤其是 NBA 50/50,通常会选择一个球员,考虑到他们的工资和球队中新受伤的球员。如果一个球员因为他上面的先发球员受伤而比正常情况下多打了 3 分钟,你需要让他在你的阵容中才有机会竞争。DFS 的竞争如此激烈,以至于这些“不用动脑筋”的选择(通常是你可能没听说过的球员)通常在 50/50 中拥有 80%以上的所有权。**
- ****位置可以是一个有噪声的变量:位置,以及由其产生的所有特征(如 DvP)都可以是有噪声的。像勒布朗这样的球员可以被列为职业球员,而事实上在某些情况下,他可能更像一个职业球员。我们真正想要提取的东西可能因用例而异,但通常我们更感兴趣的是匹配,而不是特定的位置,这可能是一个稀疏的数据点,人类可以更好地分析它。确定“可能的匹配”的算法可能是这个子问题上的一个进步。
- ****DFS 很难:根据 DraftKings 的数据,只有 14%的玩家在一个月内盈利。这个自动化系统在 2018-19 赛季的 5 个日历月中有 2 个月盈利,这使得它远远领先于普通人类玩家。即使有一个能给出不错的基线预测的系统,你仍然需要在球场上有很大的优势来克服 DraftKings 和 fan 决斗在 50/50 和双冠王联赛中约 8%的差距。
结论
目前,DFS 问题可能需要大量的人工努力才能解决。尽管我们能够在 DraftKings 上取得超过普通玩家的表现,但我们离盈利战略还有很长的路要走。最重要的是,我从构建这个项目中获得了很多乐趣,并了解了很多关于 DFS 和机器学习的实际应用。如果任何读者对该项目有任何疑问,请随时与我们联系!
为 Instacart 创建杂货产品推荐器
斯科特·沃曼在 Unsplash 上的照片
使用 K-Means 聚类和关联规则挖掘向 Instacart 客户推荐产品
在电子商务购物体验中,产品推荐有多种形式:它们可能用于在一个产品的页面上推荐其他产品(例如,亚马逊的“经常一起购买”功能),或者它们可能用于结账页面,根据客户的总订单向他们显示他们可能感兴趣的产品。在这篇文章中,我将详细介绍我是如何为前一种情况制作推荐器的。
此外,如果推荐是针对特定的客户群,而不是统一的,可能会更有帮助。例如,如果一组顾客倾向于购买大量的非乳制品牛奶替代品,而另一组顾客倾向于购买传统牛奶,那么对这盒麦片提出不同的建议可能是有意义的。为了提供量身定制的推荐,我首先使用 K-Means 聚类根据 Instacart 用户的购买历史对他们进行细分,然后根据这些聚类中的产品关联规则提供推荐。在这篇文章中,我将介绍这个过程,并给出一些推荐的例子。
数据和 Instacart 背景
Instacart 是一种在线杂货交付服务,允许用户通过他们的网站或应用程序订购杂货,然后由个人购物者完成并交付——非常类似于 Uber Eats,但用于杂货店。2017 年,他们发布了一年的数据,其中包括来自约 20 万客户的约 330 万份订单。以关系数据库的形式发布,我将单独的表组合在一起执行 EDA,留给我以下内容:
EDA
执行 EDA 时,我想回答一些关键问题:
- 最受欢迎的产品和过道有哪些?
- 产品组合有多“头重脚轻”?即排名靠前的产品占总订购份额的多少?
- 订单有多大?
为了大致了解 Instacart 倾向于销售什么,我们可以听从他们的部门销售。Instacart 有 21 个部门位于其产品分类的顶端。以下是他们各自的单位销售份额:
Instacart 似乎是农产品的热门选择。最受欢迎的 Instacart“通道”,其分类中的下一层是水果和蔬菜通道,下图显示了前 10 个通道在总销售额中的单位份额:
总共有 134 个通道,共有 49685 件产品,因此上图显示了产品销售的“头重脚轻”分布,前 3 个通道占售出产品的 25%以上。下图显示了前 3 种产品的单位份额,遵循相同的趋势:
几乎所有排名前 30 的产品都是农产品,在最受欢迎的产品之后,市场份额急剧下降。看看 K-Means 聚类是否可以从这种以农产品为主的分布中揭示出不同的客户群体,这将是一件有趣的事情。
以下是订单大小的描述性特征:
count 3.346083e+06
mean 8.457334e+01
std 1.355298e+02
min 1.000000e+00
25% 1.500000e+01
50% 3.600000e+01
75% 1.050000e+02
max 1.058500e+04
这是一个分界点为 500 单位的分布图:
图表表明,考虑到订单规模,Instacart 可能还有改进的空间——右偏分布表明,大多数订单可能无法满足各自客户的所有杂货需求,其中一半订单的商品数量少于 36 种。产品推荐系统将让顾客更容易找到他们想要的产品,并向顾客展示他们从未在 Instacart 上购买过的商品。
主成分分析和 K-均值聚类
K-Means 聚类的目标是根据客户过去购买的产品将客户分组。为了实现这一点,我打算对从每个客户以前的订单总和中购买的单位份额实施 K-Means。我的第一步是将前面显示的组合表转换成一个表,其中每行代表一个客户,列代表从每个通道购买的份额。下表中有一个示例:
然后,我执行 PCA 来减少 K-Means 算法的特征数量。这将允许我更好地可视化我的集群,并帮助 K-Means 更有效地运行。在决定要使用的成分数量时,我参考了每个成分代表总方差的方差%,并在边际方差相加后选择了一个截止值:
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
pca = PCA(n_components = 30)
principalComponents = pca.fit_transform(aisle_share_pivot)features = range(pca.n_components_)
plt.bar(features, pca.explained_variance_ratio_, color = 'black')
plt.xlabel('PCA features')
plt.ylabel('variance %')
plt.xticks(features)
plt.xticks(rotation = 45)PCA_components = pd.DataFrame(principalComponents)
根据图表,发生这种情况的部件是部件 5。然后,我将 sci-kit-learn 的 K-Means 算法应用于 6 个主成分分析,并查看了由此产生的 SSE 曲线:
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
sse = {}
labels = {}
for k in range(2,15):
kmeans = KMeans(n_clusters = k).fit(PCA_components[[0,1,2,3,4,5]])
sse[k] = kmeans.inertia_
labels[k] = kmeans.labels_
plt.figure()
plt.plot(list(sse.keys()), list(sse.values()))
plt.xlabel("Number of cluster")
plt.ylabel("SSE")
plt.show()
看起来曲线在第 5 组开始变*,所以我用第 6 组向前移动。以下是绘制在 6 种主成分散点图矩阵上的聚类:
虽然并不完美,但我似乎已经确定了 6 个不同的群体,它们应该会导致过道购买历史的差异。当然,检查这一点的最佳方式是查看每个集群的通道单位购买份额。以下是 Instacart 前 20 名通道的购买份额热图:
这 6 个群体有明显的差异,最明显的是他们购买新鲜水果和新鲜蔬菜的相对数量。鉴于农产品占 Instacart 单位销售额的 25%以上,这是有道理的。每个集群的差异可以通过单独查看来更好地显示出来,我在下面的图表中这样做了:
聚类 0 是重度蔬菜购物者,聚类 1 似乎主要使用 Instacart 购买饮料,聚类 2 有更均衡的调色板,聚类 3 更喜欢包装产品,聚类 4 是重度水果购物者,聚类 5 几乎*等地购买新鲜水果和蔬菜。在购买频率较低的通道中也会发现差异,例如,“婴儿食品配方”在第 5 类中的购买频率排名第 8,但在其他任何类中均未出现在前 10 名中。
Instacart 的业务也感兴趣的是这些集群在用户数量和购买力方面的规模。下表在左侧列中显示了属于每个集群的用户百分比,在右侧显示了属于每个集群的单位购买百分比。
有趣的是,集群 5 代表了大约 35%的用户,但几乎 50%的单位购买量。回想一下,该集群购买最多的通道是新鲜水果和新鲜蔬菜,但数量相等,并且在其前 10 个通道中还有婴儿食品配方。这表明该集群可能包含使用 Instacart 为有婴儿和幼儿的家庭购物的用户,他们似乎是 Instacart 最重要的客户。整个项目可能会进一步进行这种分析,以隔离 Instacart 的最佳客户!然而,在这一点上,我继续创建产品推荐器。
关联规则挖掘
随着 200,000 个用户被划分到集群中,我准备通过对订单进行关联规则挖掘来执行购物篮分析。这是通过将总订单表拆分成 6 个不同分类的 6 个表,并找出每个产品之间的关联规则来实现的。关联规则根据产品在同一订单中被一起购买的可能性来指定产品之间的关系。
三个常见的规则是支持,信心和电梯。支持度就是项目集在数据集中出现的频率,计算方法是将频率除以日期集的大小(通过 Wikipedia):
置信度是包含一个项目的交易与包含另一个项目的交易的比例,计算方法是将一个或多个项目的支持度除以分子子集的支持度,根据维基百科:
提升是两个或两个以上项目的观察频率与预期频率之比。它表明两个或多个项目出现的频率是否比它们随机一起出现的频率更高。大于 1 的值表示非随机关系。下面的公式:
我使用一个采用生成器的 python 脚本,针对最低支持度超过 0.01%的所有项目,按集群计算了这些指标。鉴于数据集的规模(330 万份订单,包含约 50,000 种产品),这是必要的。下表显示了群集 5 按提升排序的输出:
可以看出,整个数据集的最高提升值是彼此相似的产品,这是可以预期的。
产品推荐者
为了执行要在产品页面上显示的产品推荐,我编写了一个 python 脚本,它接受 user_id、product_id、所需的提升截止值和要返回的 num_products。通过这些输入,它确定用户的聚类(存储在从聚类分析输出的数据帧中),过滤包含该聚类的产品关联规则的数据帧,并返回提升大于提升输入的指定数量的产品,优先考虑提升最大的项目。如果符合条件的商品数量少于 num_products,它将返回所有符合条件的商品。推荐器的代码可以在 Github 知识库中找到,链接在文章的末尾。
在下面,我展示了推荐器的运行情况,展示了 5 个聚类的“有机全脂牛奶”的输出,每个聚类限于 5 个项目。
cluster 0['Whole Milk Plain Yogurt', 'YoBaby Blueberry Apple Yogurt', 'Organic Muenster Cheese', 'Apples + Strawberries Organic Nibbly Fingers', 'Yo Toddler Organic Strawberry Banana Whole Milk Yogurt']
cluster 1['Beef Tenderloin Steak', 'Original Dairy Free Coconut Milk', 'Pumpkin & Blueberry Cruncy Dog Treats', 'MRS MEYERS 12.5OZ HANDSOAP RHUBAR', 'Organic Sprouted Whole Grain Bread']
cluster 2['Whole Milk Plain Yogurt', 'Organic Skim Milk', "Elmo Mac 'n Cheese With Carrots & Broccoli", 'Kids Sensible Foods Broccoli Littles', 'Apples + Strawberries Organic Nibbly Fingers']
cluster 3['Organic Nonfat Milk', 'Paneer', 'Organic Whole Milk Yogurt', 'Organic Plain Yogurt', 'Extra Light Olive Oil']
cluster 4['Puffed Corn Cereal', 'String Cheese, Mozzarella', 'Cold-Pressed Sweet Greens & Lemon Juice', 'Organic Stage 2 Broccoli Pears & Peas Baby Food', 'Superberry Kombucha']
cluster 5['Whole Milk Plain Yogurt', 'YoTot Organic Raspberry Pear Yogurt', 'Organic Atage 3 Nibbly Fingers Mangoes Carrots', "Elmo Mac 'n Cheese With Carrots & Broccoli", 'Blueberry Whole Milk Yogurt Pouch']
上面列出的所有产品都包含有机全脂牛奶在每个聚类中提升最高的产品。可能突出的是,第一组的建议不如其他组的建议直观。这最有可能是因为该集群占不到 1%的单位购买量和不到 2%的用户,并且似乎专门针对非牛奶饮料利用 Instacart。将需要进一步的工作来确定更少的聚类是否是最佳的,但是考虑到来自该组的用户不太可能观看奶制品,生成非直观的推荐不是太大的问题。对于另一个不太常用的产品,下面是“温和的 Salsa Roja”的结果:
cluster 0['Thin & Light Tortilla Chips', 'Red Peppers', 'Organic Lemon', 'Organic Cucumber', 'Organic Grape Tomatoes']cluster 2['Real Guacamole', 'Thin & Light Tortilla Chips', 'Original Hummus', 'Organic Reduced Fat 2% Milk', 'Thick & Crispy Tortilla Chips']cluster 4['Organic Raspberries', 'Banana', 'Organic Strawberries', 'Organic Hass Avocado']cluster 5['Thin & Light Tortilla Chips', 'Organic Large Brown Grade AA Cage Free Eggs', 'Organic Reduced Fat 2% Milk', 'Organic Large Grade AA Brown Eggs', 'Thick & Crispy Tortilla Chips']
对于该产品,分类 1 和分类 3 没有任何提升超过 1 的项目。
暂时就这样吧!Jupyter 笔记本与 GitHub 的链接在这里是。
使用 Django 创建基于机器学习的 Web 应用程序
使用 Python 著名的 Django 框架在 web 上部署您的机器学习模型,并增加其可见性。
本文面向那些希望使用 Python 的 Django 框架将其机器学习模型部署为 Web 应用程序的读者。对于许多数据科学和机器学习爱好者来说,这可能是转换他们的简单。py 模型文件转换成更加动态和强大的 web 应用程序,该应用程序可以接受用户的输入并生成预测。
为了文章的简单起见,我们将更多地关注如何创建一个基于 ML 的 web 应用程序,而不是花费大部分时间来解决一个棘手的机器学习问题。此外,这不是一个完整的 django 教程,而是成为 AI/ML 开发者的一步。
首先,我们将使用著名的 titanic 数据集创建一个简单的 ML 模型,然后将它转换成一个全功能的、基于 web 的动态应用程序。
开发机器学习模型
如上所述,我们将研究著名的泰坦尼克号数据集,该数据集用于根据乘客的不同属性(乘客级别、性别、年龄、票价等)预测乘客的存活率。).由于这是一个二元分类问题,我们将使用逻辑回归算法(一个强大而简单的机器学习算法)来创建我们的模型。
可以参考下面的代码来创建分类模型。
注意:创建机器学习模型本身是一个完整的详细任务,包含多个步骤(如数据预处理、EDA、特征工程、模型开发、模型验证、调整超参数)。这里我们跳过所有这些步骤,因为我们更感兴趣的是使用这个模型来驱动我们基于 web 的应用程序。
泰坦尼克号生存预测模型
一旦我们创建了我们的最终模型,我们简单地将这个模型存储为一个 model.sav 文件(使用 pickle)。此外,请注意,我们将 scaler 对象存储为一个 scaler.sav 文件(因为在将用户输入提供给模型之前,我们将使用相同的缩放对象将用户输入转换为与训练数据相同的级别)。
创建我们自己的 Django 项目
在创建 django 项目之前,首先我们需要使用下面的命令将 django 安装到我们的开发机器上。
pip 安装 django
这将自动安装 django 框架正常运行所需的所有依赖项。如果此命令对您不起作用,请参考下面的链接-
[## 如何安装 Django | Django 文档| Django
这份文件会让你和 Django 一起工作。如果你只是想尝试一下 Django,请直接跳到…
docs.djangoproject.com](https://docs.djangoproject.com/en/3.1/topics/install/)
一旦你安装了 django,你需要在你的机器上打开命令提示符(Ubuntu 或 Mac 上的终端),然后按照以下步骤操作
- cd 桌面(你可以移动到任何你想创建项目文件夹的位置)
- mkdir Titanic _ Survial _ Prediction(你可以为你的网络应用选择任何你想要的名字)
- cd Titanic _ Survial _ Prediction(将 CD 放入您在上一步刚刚创建的文件夹中)
- django-admin start project Titanic _ Survial _ Prediction _ Web(该命令将在主项目文件夹中自动创建一个名为Titanic _ Survial _ Prediction _ Web的 django 项目)
- CD Titanic _ Survial _ Prediction _ Web(CD 到上一步创建的 django 项目中)
- 现在,如果您正确遵循了上述步骤,只需在命令提示符屏幕中键入python manage . py runserver,然后将命令提示符上出现的链接( http://127.0.0.1:8000/ )复制到 web 浏览器中。一旦你按下回车键,你就会看到下面的屏幕
Django 默认主页视图
如果上面的网页也出现了,那么您已经成功地完成了创建您自己的 django web 应用程序的第一步。
如果对某些人来说这个网页没有出现,请再次参考上述步骤,看看你是否错过了什么。
现在使用任何 IDE (PyCharm,Atom 等。)打开你的 django 项目(Titanic _ Survial _ Prediction _ Web
默认 Django 项目结构
现在,在 urls.py 所在的同一个文件夹中,创建一个名为‘views . py’的新 python 文件。 这个视图文件将负责从使用网页的任何用户那里获得输入,使用相同的输入来生成预测,然后使用网页向用户显示这个预测。
此外,我们需要将我们的“model.sav”和“scaler.sav”文件移到与 urls.py 相同的文件夹中。
在继续之前,让我们先完成 django 项目的配置。在我们的主项目文件夹中,创建一个名为【templates】的空文件夹(您可以使用任何名称,但建议遵循已定义的框架命名约定)。这个文件夹将保存我们将在项目中使用的所有 html 文件。
现在打开 setting.py 文件,把“模板”(或者你给你的 html 文件夹起的名字)添加到“模板”列表中高亮显示的“DIRS”列表中。
最终项目结构
现在在 urls.py 文件中添加下面的代码来配置我们网站的 URL(不同的网页有不同的 URL)。
urls.py 配置
我们首先在 urls.py 文件中导入了视图文件,然后在 urlpatterns 列表中添加了主页(打开 web 应用程序时的默认页面,而不是 django 默认视图)和结果页面(用于向用户显示结果)的路径,以及它们各自的名称(使用这些名称可以在我们的 html 文件中引用它们)。
现在我们需要在 views.py 文件中定义这两个函数。除了这两个,我们还将创建一个 getPredictions 函数,它能够通过加载我们预先训练好的模型来生成预测。
可以参考下面的代码来完成这项任务。
views.py 文件
result 方法负责收集用户输入的所有信息,然后以键值对的形式返回结果。
注意:总是建议在单独的 python 文件中创建 getPredictions 方法,然后将其导入到 views.py 文件中。这里为了保持简单明了,我们将函数放在 views.py 文件中。
现在我们已经完成了后端逻辑,让我们创建网页,通过表单接受用户的输入并显示结果。
在“”模板文件夹内,创建一个 index.html 文件。这个 index.html 将是我们的主页,并将有我们的形式,我们将使用从我们的用户输入。
index.html
在继续之前,我们需要了解一些事情。
- 表单操作参数→收集表单数据的视图,使用此数据生成预测,然后重定向到包含结果的网页。对于 django,我们有一种特殊的方法-action = " { % URLs ' name _ of _ URL ' % } "。在我们的例子中,url 的名称是 result(因为我们已经在 urls.py 文件中提供了这个名称)。
- 此外,我们还需要提供一个 {% csrf_token %} (即跨站点引用伪造令牌),这使得 django 中的数据处理更加安全。这部分是必须的,因此它应该总是在你的表单标签的第一行。
注: {%..%} 都叫做脚本标签。
一旦我们创建了 index.html 文件,我们就需要创建向用户显示预测的 result.html。
result.html
在“ {{ }} ”标记中,我们指定了包含结果的键的名称(在我们的例子中是 result)。
完成所有这些步骤后,在命令提示符下按 CTRL + C 停止服务器,然后使用“python manage.py runserver”命令重新启动它。现在在浏览器中重新打开链接。这一次,默认页面将是我们的输入表单的 index.html 网页。
填写相应的数据并提交表单后,您将被重定向到 result.html 页面,该页面将显示您输入数据的预测。
这就是我们如何创建一个简单而强大的基于 ML 的 django web 应用程序,它本质上是动态的(接受用户的输入),并基于机器学习模型进行预测。
如果您想了解更多关于 django 的知识,请参考 django 文档(下面的链接)。
Django 是一个高级 Python Web 框架,它鼓励快速开发和干净、实用的设计。建造者…
www.djangoproject.com](https://www.djangoproject.com/)
创建房屋销售地图
在本教程中,我将指导你一步一步地创建一个使用散景显示房屋销售的地图,并用彩色地图显示销售价格。我想让观众一眼就能分辨出哪个街区的生活成本最高。这也是价格预测建模目的的一个有用的可视化,以了解位置有多重要,以及与位置相关的新功能是否对工程师有用。
我们将使用国王郡地区销售价格的数据集,该数据集可以在 Kaggle 上找到,但是这些原则也可以应用于您选择的数据集。
这是最终结果:
首先,让我们确保我们的数据集符合目的。请注意,散景需要墨卡托坐标来绘制地图上的数据点。我们的数据集有纬度和经度坐标,所以需要一个函数来转换这些坐标。假设所有数据清理步骤(例如,缺失值)都已完成,数据应该是这样的。
df.head()
包含邮政编码、墨卡托 x、墨卡托 y 和价格列的熊猫数据框架
是时候策划了!
我将分解代码并解释每个元素的作用。
让我们从从 Bokeh 进口必要的工具开始。
from bokeh.io import output_notebook, show
from bokeh.plotting import figure, ColumnDataSource
from bokeh.tile_providers import get_provider, CARTODBPOSITRON
from bokeh.palettes import Turbo256
from bokeh.transform import linear_cmap
from bokeh.layouts import row, column
from bokeh.models import ColorBar, NumeralTickFormatter
散景的突出特点之一是内置的地图拼贴集合。虽然你可以使用谷歌地图,但这需要获得一个 API 密钥。我们将使用 Carto DB。让我们用get_provider()
功能选择这个图块。
chosentile = get_provider(CARTODBPOSITRON)
接下来,我们将选择一个调色板的颜色映射。我们希望确保我们的可视化能够有效地辨别出最昂贵的房子在哪里。Bokeh 拥有丰富的预定义调色板选项,根据您希望的粒度,您可以从调色板中选择使用多少种颜色。还有一些具有更大频谱的扩展调色板,这就是我们在这里要做的。
palette = Turbo256
我们还将定义要使用的数据源,即数据的来源。最常见的类型是ColumnDataSource
,它接受一个数据参数。一旦创建了ColumnDataSource
,就可以将它传递给绘图方法的source
参数,让我们传递一个列的名称作为数据值的替代。
source = ColumnDataSource(data=df)
接下来,让我们定义我们的颜色映射器。这是一个线性彩色地图,应用于价格领域。我们将低和高参数分别设置为最低和最高价格。
color_mapper = linear_cmap(field_name = ‘price’, palette = palette, low = df[‘price’].min(),high = df[‘price’].max())
一个简洁的功能是当用户将鼠标悬停在数据点上时包含附加信息。这是使用工具提示定义的。我们选择显示房子的价格和邮政编码。@符号表示该值来自源。
tooltips = [(“Price”,”@price”), (“Zipcode”,”@zipcode”)]
现在我们准备创建实际的图形。姑且称之为p
。我们将给它一个标题,并将 x 和 y 轴类型设置为墨卡托,以便在渲染时显示我们更习惯的纬度和经度刻度。为了完整起见,我们还将定义我们的轴标签。
p = figure(title = ‘King County House Sales 2014–2015’,
x_axis_type=”mercator”, y_axis_type=”mercator”,
x_axis_label = ‘Longitude’, y_axis_label = ‘Latitude’, tooltips = tooltips)
让我们使用add_tile()
函数添加我们选择的地图标题。
p.add_tile(chosentile)
通过将 x 和 y 指定为墨卡托坐标x_merc
和y_merc
,每栋房屋将被绘制成一个圆。圆圈的颜色将由我们上面定义的线性颜色图决定。
p.circle(x = ‘mercator_x’, y = ‘mercator_y’, color = color_mapper, source=source)
让我们添加一个颜色条,它将有助于理解我们的颜色映射。默认情况下,散景在适当的地方使用科学符号,但在这种情况下,我认为对价格使用普通符号看起来更好。默认导致价格标签与颜色条重叠,所以我们需要给label_standoff
添加一个值。不幸的是,没有直接的方法来添加一个标题到颜色栏,所以现在,我们凑合着没有。
color_bar = ColorBar(color_mapper=color_mapper[‘transform’], formatter = NumeralTickFormatter(format=”0,0"),
label_standoff = 13, width=8, location=(0,0))
让我们通过创建一个布局来指定颜色条应该在图的右边。
p.add_layout(color_bar, ‘right’)
最后,我们希望地图显示在笔记本和展示。
output_notebook()
show(p)
哒哒!
带有房屋销售标记的金县地图
从这个图像中,很明显最昂贵的房子位于海滨,尤其是在麦地那、克莱德希尔和默瑟岛。南北之间也有明显的差异,北方要求更高的价格。
我们还可以放大以深入了解特定的街区。
克莱德希尔街区房屋销售
现在你知道了!我希望这个简短的教程对你有用。如果你想进一步调整,我建议你深入 Bokeh 的文档。即使你像我一样是初学者,也很容易理解。我也希望你能给我任何反馈。
要查看我的项目,包括使用线性回归的数据探索和预测建模,这里的是 GitHub 链接。
使用 GeoPandas 和 Matplotlib 创建街道名称地图
本帖灵感来源于**和* 城市美丽的隐藏逻辑——全球 。绘制街道名称可以揭示一些有趣的见解。就像旧金山的西半部由大道和东半部的街道组成。或者说,街道是如何只出现在伦敦历史悠久的 市中心,而道路却在外面。我将使用 GeoPandas 和 Matplotlib 为我自己的城市莫斯科创建一个类似的地图,看看我们能发现什么。*
我先说结果;技术细节如下,源 Jupyter 笔记本可以在 Github 上找到。
俄罗斯街道名称侧记
在俄语中,街道名称可以放在名称前面( 、улица 、пестеля),或者放在名称后面(малаягрузинская、улица* ),如果街道有编号,甚至可以放在中间(1-я 、улицаэнтузиастов).)运用常识,列出所有前缀和后缀,得出以下街道名称列表:*
- улица街
- шоссе高速公路
- 驱动
- 大道
- 巷
- бульвар大道
- площадь广场
- набережная[naberezhnaya]——路堤
- уупик[tupik]—关闭
- вал [val] —壁垒
- магистраль高速公路
地图
让我们放大城市中心:
城市社区的不同特征显而易见。市中心是密集的街道和公园网络。更远的地方的微孔消失了。穿过内城花园环的唯一大道是院士萨哈罗夫大道,它于 20 世纪 80 年代完工。另一条真实的大街——在 1968 年建成后被命名为加里宁大街——在 1990 年更名为新阿尔巴特街。
一圈林荫大道勾勒出老城的边界。莫斯科的几条街道名字中都含有“rampart”(俄语为вал — val)一词。我把它们挑出来,只是因为我喜欢强调这些古老的防御工事是如何仍然嵌入在今天的地形中的。直到 1917 年,外围的山谷一直是城市的界限。
与规划城市的规则网格相比,莫斯科的街道看起来更像是一个有生命的、不断进化的有机体,有时毫无规律可言。街道变成大道,又回到街道。大道从无到有,通向死胡同。一些街道是真正的高速公路,一些历史上的高速公路已经变成了狭窄的车道,但仍然保留着它的名字。城市街道保留了俄罗斯混乱历史的痕迹,奇怪的过去的坚守者和从未实现的项目的遗迹。
这是怎么做到的
我从http://download.geofabrik.de/下载了 OpenStreetMap 数据摘录,特别是地区页面上的. shp.zip 文件。在这个档案中有 gis_osm_roads_free_1。包含道路信息的文件。使用 GeoPandas,我们可以根据这些文件创建地理数据框架。使用 shapely 和 GeoPy,我们定义了一个 21 乘 17 公里的椭圆。它几乎与莫斯科环路相吻合,该环路在 1984 年之前一直是该市的行政边界。*
地理数据文件列出了所有“道路”,包括车道和公园车道。选择有名称的街道进行绘制。还包括根据绘制的 OSM 类型分类为“主干”或“主要”的 strets。一些立交桥,桥梁和交叉路口被指定为这样,所以他们被显示以填补空白。
地图是使用 matplotlib 绘制的,街道名称映射到颜色。为了更好地观察 T2 市区的密集网络,它们被绘制得比其他的更薄。
源代码可以在 Github 上找到。
使用 EventBridge、AWS Lambda、SNS 和 Node.js 创建监控服务—无服务器优先
克里斯·利维拉尼在 Unsplash 上的照片
比方说,我们只想检查服务是否正在运行,如果它停止了,您希望得到通知,就这么简单。
此外,这个想法是为了展示我们在 AWS 中使用无服务器概念并尝试总是考虑“无服务器优先”的方法,只使用 3 个服务:Amazon EventBridge、Aws Lambda 和 Amazon Simple Notification Service,可以多么容易地做到这一点。
先决条件
- AWS 帐户
- Node.js 的基础知识
亚马逊简单通知服务——SNS
SNS 定义:
面向微服务、分布式系统和无服务器应用的完全托管发布/订阅消息
在我们的例子中,我们将使用这个服务来发送电子邮件。
在 SNS 服务中,让我们创建一个主题,我将使用的名称是 MonitoringAlert
社交网络话题
创建主题后,现在让我们创建订阅:
SNS 主题已创建
对于订阅,我们需要设置:
协议:电子邮件
端点:您的电子邮件地址
社交网络订阅
下标已创建,状态为“待确认”:
SNS 主题等待确认
一旦您确认状态消息将发生变化,您将通过电子邮件收到一个确认链接:
社交网络话题已确认
完成,SNS 话题已经创建并确认。现在是时候创建 Lambda 函数了。
自动气象站λ
λ定义:
运行代码时不考虑服务器。只为您消耗的计算时间付费。
λ许可
允许我们成为:
λ代码
lambda 将负责检查服务是否正在运行,如果出现问题,将触发 SNS:
λ环境变量
我们的 lambda 函数依赖于两个环境变量:
- 社交网站:社交网站创造的 ARN
- serviceToCheck:我们想要检查的 URL,我的情况是我使用的是 NASA 的 API (https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY)
AWS Lambda 环境变量
现在我们已经创建了 SNS 主题和 Lambda 函数,是时候使用最新的服务 Amazon EventBrigde 了。
亚马逊事件桥
事件桥定义:
连接您自己的应用程序、SaaS 和 AWS 服务的应用程序数据的无服务器事件总线
在我们的例子中,我们将使用 EventBrigde 作为调度程序,它将以我们定义的频率运行并触发 Lambda 函数。
规则模式
关于规则的重要思考是模式。这种模式决定了频率,在我们的例子中,我们将把计划设置为每 1 分钟一次的固定频率(您可以根据自己的需求进行替换):
Amazon EventBridge 创建
其次,在目标中,我们将选择 Lambda 函数,并在基础中添加我们的 webAppMonitor:
Amazon EventBridge 目标
最后,创建了 EventBrigde 规则:
事件桥规则
现在,每次服务离线时,我们都会收到这样的消息:
SNS 通知电子邮件
也就这些了,希望能对你有用:)
你怎么想?有没有其他方法可以解决同样的问题?非常感谢您的反馈。
非常感谢您的宝贵时间!
从头开始创建神经网络
通过自己构建算法来理解算法背后的关键概念
[图片由作者提供]
在我们开始之前—一点历史…
神经网络已经存在很多年了。事实上,该算法背后的思想是由一位名叫弗兰克·罗森布拉特的心理学家在 60 多年前首次提出的。
然而,直到最*十年开始,这些机器学习模型才开始受到更多的关注,随着 这篇 论文的发表,证明了神经网络在机器学习中的用途和有效性。
今天,神经网络是深度学习的核心。人工智能中最复杂的任务通常由人工神经网络来完成,并且有许多库可以用非常少的几行代码来抽象神经网络的创建。
目标
在本文中,我们将介绍神经网络背后的基本概念,并理解它们的内部工作原理。我们将通过从头开始创建一个灵活的神经网络来做到这一点。
本文的完整源代码可以在这个链接中找到!👇👇👇
在 GitHub 上创建一个帐户,为 jzsiggy/NN 从零开始的开发做出贡献。
github.com](https://github.com/jzsiggy/NN-from-scratch)
我们将看到的代码是用 javascript 编写的,但是,每个概念和步骤都将被深入地记录和解释,所以你可以使用任何你喜欢的语言!
在我们继续之前,你必须知道…
神经网络是属于深度学习的一种算法。就此而言,深度学习是机器学习的一个子类,它本身也是人工智能的一个子类。
机器学习背后的核心思想是,可以训练算法对数据进行分类和处理,而无需明确告知分类的规则。
这意味着,我们可以在大量的输入/输出对(即训练集)上训练模型,并在一段时间后,让模型为从未见过的给定输入提出自己的分类规则,而不是让模型基于硬编码指令做出决策。
这个想法在所有机器学习算法上都是有效的。
好了——既然机器学习的基础知识已经讲完了,让我们继续学习神经网络吧!
第 1 部分—层??
让我们来谈谈神经网络的基本结构。数据处理是通过分层进行的。任何网络都有一个输入层,一个隐藏层的子集(我们马上会谈到),和一个输出层。
网络的每一层由若干个神经元组成,每一层的神经元通过权值与下一层的神经元相连。
具有 1 个输入层、1 个隐藏层和 1 个输出层的简单网络— [来源:维基百科]
输入层是数据将进入算法的地方。比方说,我们想创建一个网络来预测某个病人患有某种疾病的概率,而不是他所表现出的症状。
在这种情况下,我们的训练数据将被构造成一个输入/输出对。输入将是 0 和 1 的数组,代表我们正在分析的症状,输出将是 0 或 1,代表出现“输入”症状的患者的感染状态。
示例 ML 算法的训练数据
如果我们要根据这些数据训练一个神经网络,我们必须有一个包含五个神经元的输入层,每个神经元对应一种症状。我们可以有任意数量的隐藏层,每层中有任意数量的神经元,但我们必须有一个只有一个神经元的输出层。
当算法认为输入代表生病的患者时,输出层中的神经元将具有接* 1 的激活,当算法认为输入代表未感染的患者时,输出层中的神经元将具有接* 0 的激活。
我们示例神经网络的层结构
你可以把神经元的激活想象成它储存的值。第一层中神经元的激活将与我们输入数据的值相同。这些值将通过数学运算转化为下一层的神经元,我们稍后将对此进行检验。
我们输出层中神经元的激活将导致我们网络的预测。(它可能只是一个神经元,就像上面的例子一样)
第 2 部分—激活、权重和偏差
(还有乙状结肠函数!)
好吧,您可能想知道进入输入层的数据是如何穿过网络并最终到达输出层的,这与它是如何进来的完全不同。这个过程称为前馈,完整的解释涉及一些线性代数和矩阵乘法,我们很快就会看到。现在,我们将坚持这个过程背后的想法。
正如我们之前看到的,每个神经元都通过我们称之为的权重连接到前一层的神经元。这意味着,比方说,第二层中神经元的激活将与该层中神经元在乘以连接它们的权重之前的所有激活的总和相关。
网络表示
如果我们用字母 a 表示一个神经元的激活,连接两个神经元的权重为 w ,则第二层中单个神经元的激活可以表示为下图所示。
神经元的“激活”
对于这件事,上面的等式实际上是方式过于简化了。实际上,这只是计算任何给定神经元激活的第一步。下图将向我们介绍找到最终激活必须执行的剩余操作。
来源——hackernoon.com
我们还没有考虑的两个新因素是:偏差,由字母 b 表示;和激活函数,用函数 g(z) 表示。
让我们从解决偏见开始。
这里的想法是,每个神经元将有一个唯一的数字,该数字将被添加到先前神经元激活的加权和中。
添加偏差是有用的,这样神经网络可以更好地推广到任何给定的数据。你可以认为它是一个线性函数中的常数 y=mx+c.
给每个神经元添加一个偏差对于网络正确处理简单数据来说并不是基本的,然而,当我们处理非常复杂的任务时,它变得不可或缺。
现在我们得到了偏差,我们的激活方程看起来像这样:
加权总和+偏差
为了理解数据如何在网络中传输,我们必须了解的最后一件事是激活函数。
让我们用字母 z 表示给定神经元的加权和加上它的偏差。所述神经元的激活将是应用于 z 因子的激活函数的结果。
激活函数用于*滑神经元激活的小变化,并将其保持在 0 和 1 之间。一个非常常见的激活函数,也是我们将在自己的网络中使用的函数,是 sigmoid 函数。
sigmoid 作为激活函数非常有用,因为一旦通过 Sigmoid 函数,远远大于 0 的输入将很快产生非常接* 1 的数字。同样的原理,输入越小,得到的数字就越接* 0。非常接* 0 的输入数字将具有在 0 和 1 之间*滑振荡的值。
Sigmoid 函数
综上所述,我们可以通过将前一层神经元激活的加权和与偏差相加,并将该结果传递给 sigmoid 函数,来找到任何神经元的激活。
发现神经元的激活
从任何给定的输入中找到输出层中神经元的激活的过程被称为前馈。它的工作原理是从输入层的神经元和连接它们的权重中找到第二层神经元的所有激活。第二层中神经元的激活被用于寻找下一层的激活,如此类推,直到输出层。
权重和偏差都是在未经训练的网络中随机初始化的。训练网络的过程意味着找到这些权重和偏差的值,使任何给定输入的结果输出层极其接*预期输出。
让我们对所有这些进行编码,然后我们将检查如何通过调整这些权重和偏差来训练我们的网络,以获得最佳性能!
班级网络
||→[{[{免责声明}]}] ←||
除了 math.js 之外,我们不会在本文中使用任何外部 JS 库来优化矩阵运算。
好了——现在让我们开始编码。
让我们从初始化我们的网络类开始。
为了使我们的网络更灵活,更好地概括数据,我们将在类的构造函数中接收层数和每层中神经元的数量。
这里的想法是用一个数组来实例化这个类。数组的每个元素代表一个新的层,每个元素的值是相应层中神经元的数量。
初始化我们的网络
为此,我们将把连接每一层和前一层的权重表示为一个矩阵。这个权重矩阵的每一列将代表将前一层的所有神经元连接到下一层的单个神经元的权重。
对于我们网络中的每一层,我们将有一个权重矩阵,权重将随机初始化,值在 0 到 1 之间波动。
为了简化代码,我们将网络神经元的偏差设置为零。
初始化网络类的权重
概括一下:我们网络的权重属性将是一个矩阵列表。每一个矩阵都代表连接一层和下一层的所有权重。
现在我们已经设置了所有的权重,让我们编写前馈方法的代码。
该方法将接收一个以输入为参数的数组。这些输入将被映射到我们网络的输入层。
为了找到下一层的最终激活,我们可以使用输入激活和连接输入层与下一层的权重矩阵之间的点积的结果。
这种代数运算类似于将输入层的每次激活乘以其与第二层中的神经元的连接权重,并对每个神经元的这些乘积求和。然后,我们可以将 sigmoid 函数应用于第二层中的每个结果加权和,以找到所有结果激活。
这些产生的激活将是点再次乘以我们的权重数组中的下一个权重矩阵,并且点积将经历 sigmoid 函数,从而产生下一层中神经元的激活。这种情况一直发生,直到产生的激活是我们输出层中的激活,因此产生了我们的预测。
为了在代码中实现这一点,我们可以对权重矩阵数组进行循环。循环将从输入与第一个权重矩阵点乘开始,并将 sigmoid 函数应用于结果。由此产生的激活将被映射到一个激活变量,该变量将与第二层权重相乘,依此类推。
在每次迭代中,我们将更新激活变量,以便激活和权重矩阵之间的点积导致下一层的激活(当然,在经历了 sigmoid 之后)。
我们网络的前馈方法
为了让上面的代码工作,我们必须首先编写 sigmoid 函数。代码非常简单:
到目前为止,我们网络的完整代码是这样的:
第 3 部分:培训—🏃♂️ 🏃♀ 🏃♂️ 🏃♀ 🏃♂️ 🏃♀
现在,我们的网络已经可以做出预测了!!!!
…嗯,不完全是。
我们已经可以通过前馈方法输入一些输入数据并获得一个输出预测,但是我们的权重和偏差具有随机值,所以我们的预测根本不会精确。我们的网络会盲目猜测结果。
我们如何调整这些权重和偏见,让我们的网络停止猜测,开始做出“明智”的决定?
完整的答案涉及偏导数和一些多变量微积分,但现在,让我们坚持计算背后的想法。
让我们的预测更好的第一步是找出它们有多糟糕。
完成这一任务的函数类型被称为成本函数。有许多可用的成本函数,但我们将在网络中使用的是均方误差函数,或 MSE。
MSE 成本函数背后的思想是将输出层中每个神经元的激活与该输出的期望值进行比较。我们将从预测的激活中减去预期的输出值,并对每个神经元的结果求*方。对所有这些*方误差求和将给出我们的成本函数的最终值。
MSE 成本函数示例
这里的想法是调整每层的权重和偏差,以最小化成本函数。MSE 的值越小,我们的预测就越接*实际结果。
这就是偏导数出现的地方。让我们这样来看:每个权重和偏差都是以某种方式作用于成本函数的变量。我们网络中任何层的任何权重的任何变化都会或大或小地影响我们的成本。
如果我们可以找到每个权重的偏导数,即单个权重的微小变化对成本函数的结果有多大影响,我们就可以找出如何改变该权重,以最小化我们手头训练示例的成本函数。
例如:当我们计算单个重量的偏导数时,如果我们看到该重量的微小增加将增加成本函数,我们知道我们必须减少该重量以最小化成本。另一方面,如果重量的微小增加降低了成本函数,我们将知道增加这个重量以减少我们的成本。
除了告诉我们应该增加或减少每个重量之外,偏导数还会指出重量应该改变多少。如果通过对权重的值施加微小的推动,我们看到我们的成本函数有显著的变化,我们知道这是一个重要的权重,它的值严重影响我们网络的成本。因此,我们必须对其进行重大更改,以最小化我们的 MSE。倒数也是有效的。
对于我们训练集中的每个训练示例,我们将找到手头示例的成本,并相应地调整所有权重。当我们重复这个过程数千次(如果不是数百万次的话)时——每个训练示例一次——我们将最终得到一个优化的网络,它可以对任何给定的输入做出出色的预测。
为每个神经元寻找最佳偏差的过程非常相似,但由于我们的简单网络将所有偏差设置为零,因此我们不会深入研究偏差计算。
让我们开始编码吧!
班级网{…} —续…
让我们首先向我们的网络类添加一个方法,该方法计算单个训练示例的成本。
成本方法将接收我们的训练集的一个输入、输出对。
首先,通过前馈方法从给定的输入中找到网络预测。输出层的激活将存储在激活数组中。我们现在将创建一个新的数组,每个元素都是输出神经元的*方误差。
我们将返回数组中所有*方误差的总和。
MSE 成本函数
现在我们已经到了网络中最复杂的部分。调整权重以找到最佳网络。计算这些新权重的过程被称为反向传播。
有复杂的算法可以计算出我们的网络中权重相对于成本值的所有偏导数,从而加快训练过程,但为了简单起见,我们将通过数值而不是分析来计算这些导数(目前)。
这意味着我们将选择一个很小的数,比如 0.01,并用这个值来改变给定的权重。然后,我们将重新计算这个调整后的网络的成本函数,看看我们的预测是更好还是更差。
原始成本和我们将权重改变 0.01 后的成本之间的差除以 0.01,是该权重相对于成本函数的偏导数的数值*似值。
计算导数
让我们给我们的网络类添加一个新方法,叫做 backProp。
对于网络的每个权重,我们将计算它对成本的偏导数,并将值存储在一个数组中。该数组的结构将与具有我们网络的所有权重的数组的结构相同,但是不是具有每个权重值,而是具有每个偏导数值。
最后,在我们计算完所有的偏导数之后,我们将遍历权重数组,并从每个权重中减去其相对于成本函数的偏导数。
通过这样做,我们将放大减少成本的权重的值,并减少使成本最大化的权重的值。
backProp 方法将如下所示:
数值反向传播
唷…那是一大堆东西!但是现在我们已经建立并运行了网络的基础部分!
我们要做的最后一件事是创建一个训练方法来接收一组输入/输出对训练数据,并为每个条目运行反向传播算法。
训练之后,我们将有一个优化了权重的网络,准备好预测任何新输入的输出。
训练方法也将接收一个时期值。
epochs 值表示网络应该在相同训练数据上训练的次数。当我们没有一个非常大的训练集时,或者在这篇文章的 中很好地解释了其他原因时,这可能是有趣的。
训练方法
我们网络的完整代码现在看起来像这样:
请注意,我们还添加了一个预测方法,它只实现前馈方法并返回结果。
现在让我们看看我们可以用我们的网络做些什么!
第 4 部分:示例📊 📈 📉
为了证明我们的网络功能,让我们训练一个网络来分类坐标*面上的点。
在这个例子中,让我们画一条穿过*面的对角线。这条线代表函数y = x。
线性函数 y=x
我们将尝试训练我们的网络来将点分类为在线上或线下。
为此,我们将向网络中输入 1,000 个点的示例,并为它们加上正确的标签:如果点在线下,则为 0,如果点在线上,则为 1。
现在,我们只考虑值在 0 和 1 之间的点的 x 和 y 的值。
我们网络的培训示例
我已经创建了一个简单的函数来自动化创建训练数据的过程。该函数接收要生成的输入/输出对的数量,然后返回输入数组和相应的输出数组。
生成训练数据的函数
现在我们可以这样训练我们的网络:
训练我们的网络来分类坐标
在上面的代码中,首先我们初始化一个新的网络,输入层有两个神经元,隐藏层有 5 个神经元,输出层有 1 个神经元。
在第二行中,我们创建了 x 和 y 训练数据。
接下来,我们用我们生成的带标签的数据训练我们的网络一个时期。
最后,为了看看我们是否能够训练一个精确的网络,我们将检查网络对一个新值的预测,在这个例子中(0.5;0.1) 。
我们期望网络将输出分类为 0,因为坐标*面上的这个点在我们正在评估的线之下。
上面代码的输出是
node index.js//returns [ 0.000019314711178699818 ]
现在让我们试着预测点(0.3,0.7)的输出。预期值应该接* 1。
当我们运行 predict 方法时,返回值是:
[ 0.9999203743668156 ]
这正是我们预期的结果。我们现在已经创建了一个网络,在经过 10,000 个示例的训练后,该网络可以对坐标网格上的点进行分类。
第 5 部分:优化
我们已经完成了一项有趣的任务:理解神经网络的具体细节并自己创建一个,但仍有很大的改进空间。
我们可以对我们的网络进行的第一个改进是解析地而不是数值地实现反向传播算法。
反向传播演算— 3b1b
上面的视频真的很有见地,我强烈推荐你观看!
通过这种算法实现反向传播可以成倍地提高我们的训练时间,并允许我们在更短的时间内训练更复杂的网络。
我们可以对网络进行的另一个调整是给每个神经元增加偏差值。在参考资料部分,你可以找到解释和实现其他神经网络偏差的文章。
最后,我们可以对我们的网络进行的另一个修改是通过批次来训练它。这意味着,我们可以计算 100 个训练示例的*均成本,并基于该值调整权重,而不是计算每个训练示例的成本并调整权重。这项技术可以大大加快我们的训练时间!
在我们的网络中,肯定还有许多其他的东西可以调整和修补来提高性能,但是我希望这个简单的例子可以帮助你理解算法背后的关键概念!
我希望你会发现这篇文章很有启发性,并且它可能对你有所帮助!😁😁😁
参考
神经网络和深度学习是一本免费的在线书籍。这本书将教你:神经网络,一个美丽的…
neuralnetworksanddeeplearning.com](http://neuralnetworksanddeeplearning.com/) [## 如何用 Python 从零开始构建自己的神经网络
理解深度学习内部运作的初学者指南
towardsdatascience.com](/how-to-build-your-own-neural-network-from-scratch-in-python-68998a08e4f6) [## 张量流-神经网络游乐场
这是一种构建从数据中学习的计算机程序的技术。它非常松散地基于我们如何思考…
playground.tensorflow.org](https://playground.tensorflow.org/#activation=tanh&batchSize=10&dataset=circle®Dataset=reg-plane&learningRate=0.03®ularizationRate=0&noise=0&networkShape=4,2&seed=0.38334&showTestData=false&discretize=false&percTrainData=50&x=true&y=true&xTimesY=false&xSquared=false&ySquared=false&cosX=false&sinX=false&cosY=false&sinY=false&collectStats=false&problem=classification&initZero=false&hideText=false) [## 从头开始构建人工神经网络:第 1 部分
在我以前的文章人工神经网络(ANN)介绍中,我们了解了与…相关的各种概念
www.kdnuggets.com](https://www.kdnuggets.com/2019/11/build-artificial-neural-network-scratch-part-1.html)
用https://carbon.now.sh/制作的代码片段
可控随机性训练
用 fast.ai 对宠物安全植物进行分类
创建和比较 fast.ai 学习者
在第一部分:建立一个数据库中,我们已经在网上搜集了关于植物及其对宠物毒性的信息,对照第二个数据库交叉引用了这些字段,然后最终通过 Google Images 下载了每个类别的独特图像。在这一部分,我们将训练基线神经网络(使用新的 fast.ai 框架),以根据图片识别植物的种类。然后,我们将评估我们收集的数据集对于训练神经网络有多好,并寻找改进的方法。
这里的主要目标是比较改变每类图像数量的影响,以及我们如何通过控制随机性来公*地比较每次训练。
我发现 fast.ai 是一个非常有用的框架(位于 PyTorch 库之上),可以直接用于机器学习。杰瑞米·霍华德(fast.ai 的创始研究员)用了一个类比,类似于学习如何踢足球——我们是想研究如何踢球的精确物理和力学,还是融入其中,边踢边学?后者更吸引人,由于网上有大量的可用资源,我们可以在学习的过程中获得重要的理论知识。其中一个例子是 fast.ai 自己的书,它很好地概述了这个过程,并包含了一个关于数据伦理的特别有趣的章节。
目录
- 一个训练基线
1.1 - 导入和播种随机性
1.2 - 将数据加载到 Colabs
1.3 - 数据块和数据加载器
1.4 - 分层拆分
1.5 - 创建数据块和数据加载器
1.6 - 创建学习器 - 训练模型
2.1 - 我们如何选择学习率?
2.2 - 示例训练运行 - 训练模型
3.1 - 为什么图像越多越好?
1.训练基线
1.1 -导入和植入随机性
fast.ai 正在快速更新,这需要安装特定的包版本以实现可再现性。为了便于使用,我们将简单地把我们需要的所有东西(以及我们以后需要的东西)导入到全局名称空间中。
为了比较训练运行,我们需要控制系统中存在的随机性来源(扩充、分裂等。)虽然有很多关于这个主题的讨论,但我发现,对于 Colabs 来说,在创建DataBlock
之前使用下面的函数将允许即使在内核重启之间也能产生可再现的结果。您还需要在您的数据加载器中设置num_workers = 0 (or 1)
,但是默认情况下它是 0,我们不会在这里更改它。
在 NumPy、PyTorch 和 random 包中设置随机种子。
请注意,如果您调用任何使用随机设置的函数(如learn.dls.show_batch()
)或更改使用的 GPU(如特斯拉 P100 vs Colabs 上的 V100),结果将会改变。这可能导致需要在工厂重置运行时并重新连接,直到提供相同的 GPU。
驱动版本和 CUDA 版本的一致性也很重要。
1.2 -将数据加载到 Colabs 中
目前,这些数据保存在 Google Drive 中带有类别标签的文件夹中,共有 150 张图片。jpgs)。Colab 可以直接将链接到您的 Google Drive,但是简单地将您的学习者指向该驱动器并继续进行培训将会大大减慢该过程,因为每批培训都需要不断地传输图像。
为了解决这个问题,我们可以递归地将包含每个子文件夹的文件夹直接复制到当前内核中(但是,如果您有大量小文件,这可能会相对较慢)。
recursife 意味着
cp
复制目录的内容,如果一个目录有子目录,它们也被复制。
执行主文件夹的递归复制。
如果这种方法太慢,我们可以先下载文件,压缩文件,然后上传到 Google Drive。然后,每当我们需要数据时,我们可以简单地下载该文件并在内核中解压缩。
下载并解压缩包含所有文件夹的文件。
不管怎样,我们在这里所做的确保我们的图像出现在 Colabs 内核上的工作将会在训练中节省大量的时间。
1.3 -数据块和数据加载器
现在,我们的数据直接呈现在内核中,可以在训练期间轻松访问。为了使用这些数据,fast.ai 开发了一个名为DataBlock
API 的灵活系统。在高层次上,DataBlock
只是在构建批处理和我们的DataLoaders
时作为一个指令列表。这在 fastai 书的第 5 章中有更详细的讨论。
我们的DataBlock
会是这样的:
使用的数据块结构。
blocks = (ImageBlock, CategoryBlock) blocks
使用内置块元组指定自变量和因变量类型。在这种情况下,我们传入图像并寻找类别。splitter = stratifiedsplitter
splitter
定义使用什么函数将数据分割成训练集和验证集。stratifiedsplitter
是一个简单的定制函数,它将基于数据帧中的一列进行拆分(我们将在后面看到),但是可以定义任何类型的拆分——从随机拆分到基于文件夹位置或名称的拆分。get_x = get_x
get_x
定义使用什么函数来获取我们数据集中的图像列表。get_y = get_y
get_y
定义使用什么函数为数据集创建分类标签。item_tfms = item_tfms
item_tfms
是运行在每个单独项目上的代码片段。fastai 包括许多预定义的变换,这个步骤通常用于标准化每个图像的大小。batch_tfms = batch_tfms
batch_tfms
作为一个组合操作在 GPU 上只能应用一次。与单独执行操作和多次插值相比,这保持了图像清晰度并减少了伪影的数量。这里通常是定义图像增强步骤(如调整大小和旋转)的地方。
1.4 -分层分裂
splitter
定义了哪些图像将出现在训练数据集和验证数据集中。有许多方法可以实现这一点,但这里我们准备了一个函数,它查看包含每个类的图像的所有文件夹的路径,并返回一个包含将一个类与每个图像配对的数据帧。
一个简单的函数,创建一个数据帧,包含“类”和“路径”列,从路径到文件夹,包含每个类的图像文件夹。
现在我们有了这个数据框架,我们可以选择我们想要如何进行拆分。为了实现未来的 k 折叠验证,让我们准备一种生成分层折叠的方法,它保留了每个类的样本百分比。我们在 sklearn 的StratifiedKFold
函数的帮助下做到这一点,将数据帧的适当列作为X
和y
传入。
使用 sklearn 的 StratifiedKFold 函数,在提供的数据帧上准备分层 k-fold 分割。
太好了!现在我们有了一个 DataFrame(上面例子中的df_cnn
,它包含了Class
、Path
和is_valid
标签。
此外,由于我们希望尽可能公*地比较具有不同数量图像的数据集之间的训练情况,我们不想每次都使用随机训练/验证分割。如果我们这样做,用于训练和验证的图像的差异将固有地改变模型性能。为了对此进行控制,我们首先对所有图像进行随机分层分割(见下面的 a )。然后,在我们从数据集中移除任何图像后,我们对之前定义的分割进行内部连接,以便任何图像仍保留在与之前相同的训练或验证集中(创建伪分层分割,参见下面的 b ,因为我们不能保证每组中保留的图像的比例与完全相同)。
修复所有的随机性可能通常不是一个好主意,因为不同运行之间的高变化可能会给你一些错误的提示。如果使用交叉验证,分数的自然变化可以帮助你获得更好的分数。
(a)从所有图像中创建主混洗分层分割。(b)在数据集中的一些图像被移除之后,先前定义的分裂上的内部连接将保持相同的训练/验证分裂。
1.5 -创建数据块和数据加载器
如前所述,拆分是在DataLoader
的DataBlock
中定义的,这里我们使用一个get_dataloader
函数来自动化这个过程。
该功能首先定义get_x
、get_y
、splitter
、item_tfms
和batch_tfms
。这里,get_x
和get_y
告诉我们的DataBlock
查看数据帧中适当的列,分别找到图像路径和标签。如前所述,splitter
使用“is_valid”列将图像识别为训练或验证。
对于我们的变换,我们遵循一个预调整策略,其中item_tfms
将每个图像调整到一个比目标训练尺寸大得多的尺寸,而batch_tfms
将所有常见的增强操作(包括调整到最终目标尺寸)组合成一个 GPU 的组合操作。
注意,这里的batch_tfms
使用由 fastai 定义的基础aug_transforms
,它应用翻转、旋转、缩放、扭曲、光照变换的列表,然后使用 ImageNet stats 应用归一化。我们添加一个随机擦除变换的附加条款,这将在后面讨论。最后,如前所述,这些片段中的每一个都被用在了DataBlock
的定义中。
然后,我们检查是否已经定义了一组图像的split_path
,我们应该使用该组图像准备一个“主”分层分割,如果已经定义了,则在我们的img_path
中应用图像的内部连接来定义我们的伪分层分割。否则,我们为img_path
中的图像生成分层分割。该过程包含前两个函数(create_path_df
和stratified_split
),采用之前定义的参数。
该函数的输出是一个DataLoader
,它表示一个具有扩展功能的数据集上的 Python iterable,支持自动批处理、混排和多进程数据加载等功能。
1.6 -创建学习者
同样,为了让我们接下来的工作更加方便,我们将把目前为止我们编写的所有内容包装到一个函数中,该函数定义了参数,并通过 fastai 便利函数cnn_learner
提供这些参数,最终得到我们的学习者。
这里,我们为默认的学习者设置一些参数。我们将使用 224x224 像素大小的图像,批量大小为 64。使用预训练的 ResNet34 架构,这是一个经典且可靠的神经网络。在调用random_seed
函数修复所有随机性之前,我们使用的优化函数(Adam)的参数也在函数体中定义。我们还设置了一系列有用的回调,将结果保存为. csv 格式,并在图中显示训练和验证损失,在训练期间实时显示。最后,在添加切换到混合精度训练的选项之前,我们使用 fastai 便利函数创建学习者,该函数接受我们之前准备的所有单独的项目。
唷!我们准备好训练了。达到这一步需要做很多工作,而且没有必要以这种方式设置。然而,努力将事情包装成一个简单的函数将在以后带来好处,因为我们现在可以轻松地改变一系列参数(以一致的方式),包括数据集中的图像数量和神经网络的架构。这样做可以让一切变得更整洁,避免每次都需要复制和粘贴代码,从而降低出错的可能性。
2.训练模型
正如我们在create_simple_cnn_learner
函数中看到的,我们将从一个简单但健壮的 CNN 开始,ResNet34。让我们首先创建一个只使用 1/3 数据集的学习者(每个类 150 张图片中的 50 张)。
使用 50 张(共 150 张)图像创建学习者。
然后,我们可以使用以下工具来查看这些图像
learn.dls.show_batch(max_n=9)
显示一批 9 幅图像。感谢我们随机性的播种,这将是每次都一样。
使用从 ImageNet 预训练的权重,将使用转移学习程序来微调我们图像的网络。迁移学习背后的基本思想是,由create_simple_cnn_learner
函数创建的预训练 ResNet34 模型在识别 ImageNet 数据集中存在的东西方面已经有了一个不错的想法。由于我们使用的图像与 ImageNet 中使用的真实图像不会有太大的不同,所以我们不希望过多地改变权重是有道理的。使用迁移学习的理论和决策可能更加细致入微,参见这篇出色的博客文章以获得更深入的解释。
这里,我们将在解冻所有内容之前,使用冻结的初始层中的权重训练我们的模型(仅训练最后完全连接的层的权重),并以相对较低的区别学习速率 s 对所有权重进行“微调”训练。这意味着在 fastai 定义的层组中,学习速率将从小(在早期层中)交错到相对较大(当我们接*最终层时)。直观地说,这与每一层所看到的图像细节有关。早期图层倾向于查看图像的大致轮廓,如梯度、边缘和拐角,所有细节的权重都不需要进行任何重大程度的重新训练。对于后面的层反之亦然。
我们将使用迁移学习程序来确定我们的训练结果。
上面的代码展示了我们将在这里用来训练我们的分类器的基本迁移学习过程。固定的学习速率和时期将用于比较训练运行,10 个时期用于训练模型头部,另外 10 个时期用于微调网络。注意,enumerate_params
是一个小函数,当被调用时,它告诉我们一个给定的学习者有多少冻结和未冻结的参数。
2.1 -我们如何选择学习率?
许多优秀的博客文章解释了为手头的问题选择合适的学习率的重要性。由于这不是这篇文章的重点,我们将不会进入太多的细节。在这里(按照 Leslie N. Smith 的建议,许多人也是这样做的),我们使用内置的 fastai 函数learn.lr_find()
来绘制损失与学习率的关系,并在损失仍然改善的最小值之前选择一个值,以确定转移过程中每一步的适当学习率。
learn.lr_find()的结果示例,所选的学习率用红色标记。
在实践中,我们使用fit_one_cycle
函数,所用的学习率代表余弦退火的单周期策略中的最大学习率,如下所示。请注意,下面的 x 轴代表通过学习者的批次数量。
余弦退火的单周期策略。
这项政策是 Leslie Smith 的工作在超参数(学习率、动量和重量衰减)方面的成果,结合 fastai 的调整,在复杂模型的训练中提供快速结果。直观上,我们可以认为该策略从一个较低的值开始,以预热培训。随着我们的进展,学习率增加,动量减少,以鼓励优化器快速调查损失函数的新区域,作为一种正则化方法,通常落在具有更*坦最小值的区域(这些区域更擅长泛化)。在单周期策略的最后部分,学习率的降低允许优化器在较*坦的区域内进入较陡的局部最小值。参见 Nachiket Tanksdale 的这篇精彩文章,了解更详细的解释。
2.2 -训练运行示例
在跑完baseline_fit(learn)
之后,一次单独的训练跑将会看起来像这样。
训练运行示例。
我们可以看到ShowGraphCallback()
的用处,它可以在过度训练发生时给我们一个指示。通常,我们可能会寻找训练和验证损失的差异,如果验证损失开始增加,我们应该特别小心。
3.训练模型
现在我们已经设置好了一切,我们可以开始做一些有趣的比较。让我们先来比较一下,当我们改变第 1 部分的数据集合中的图像数量时(在创建学习器时使用pct_images
参数),我们的前 5 个精度是如何变化的。
验证准确性随所用图像数量的变化。
虽然总训练时间与图像数量成线性比例,但我们看到,当每类使用 150 幅图像时,模型的前 5 名准确度会下降。啊哦。通常我们会期望更多的图像给我们一个更好的结果!
3.1 -为什么图像越多越好?
这个问题与我们为创建数据库而下载的图像的质量有关。回想一下每个文件夹的图片都是根据每种植物的学名从谷歌图片上下载的。让我们来看看一个随机类的早期搜索结果和后期搜索结果,比如说 Peperomia peltifolia 。
从谷歌图片 Peperomia peltifolia。
啊哈!在我们可以为每个类生成 150 个独特的图像之前,必须查看 150 多个搜索结果(仅这个随机类就有 430 多个)。)随着我们越来越深入搜索结果,我们的结果会变得越来越不相关。这将产生像图画、图表和事实表这样的图像——它们都不擅长训练我们的模型做我们想要它做的事情(根据自然照片对植物进行分类)。事实上,我们对训练结果的比较表明,由于包含了许多糟糕的训练示例,试图在每个类别中使用超过 100 个图像会开始损害我们的模型。
我们希望对这些图像进行一些清理,而不必手动检查 500 多个文件夹中的每个文件。请加入我们的第 3 部分:锁定和删除不良训练数据,我们将研究一些方法来实现这一点。
建立图像数据库
用 fast.ai 对宠物安全植物进行分类
使用美素、硒和 FastAI
进入深度学习领域,有大量高质量的资源,但每个人都同意,有意义的项目对真正掌握诀窍至关重要。
有一个喜欢买植物的未婚妻,和一只喜欢啃植物的猫——我想还有什么比组装一个分类器来告诉我植物是否安全更好的呢!
需要注意的一点是,这里所做的所有工作都是在 Google Colabs 上完成的,所以如果您想在本地机器上做同样的事情,那么设置和导入方面的差异是必要的。用的笔记本可以在我的 Github 上找到。
步骤 1 —获取数据
不幸的是,我无法在那里找到一个预制的图像数据集,它适合我想在 Kaggle 上做的事情,或者使用谷歌的数据集搜索。所以,除了自己造还能做什么!
我决定使用 ASPCA 的猫和狗的植物毒性列表,因为我在苗圃的时候已经不止一次地使用过这个网站。这给了我们一个很好的工作核心。为了从网站上抓取这些文本数据,我们可以求助于 BeautifulSoup ,这是一个 Python 库,用于从 HTML 和 XML 文件中提取数据。
然而,当查看他们的网站时,该表并不是一个易于访问的 html 表,而是将数据存储为面板中的行。幸运的是,BeautifulSoup 为我们提供了一种简单的方法来搜索解析树以找到我们想要的数据。例如:
将原始数据收集在一起后,我们需要进行一些创造性的拆分,将其分成几列:
然后,我们可以对特定于狗的列表重复此过程,然后合并数据帧并清除来自任何条目的 nan:
照片由 Unsplash 上的 Sereja Ris 拍摄
第二步——浅层清洁
接下来,我们可以开始浅层清理,包括查看数据集,决定我们要使用哪些关键要素,并标准化它们的格式。
(脏的)数据帧。注意 spp 的不一致使用。/sp。还有失踪的家人。
我们目前有名字,别名,学名,家族以及我们的毒性专栏,这些都是从 ASPCA 网站上用 BeautifulSoup 搜集来的。由于我们将在谷歌图片搜索的基础上收集图片,因此我们决定根据每种植物的确切学名进行搜索,以获得尽可能具体的图片。像“珍珠点”、“大象耳朵”、“蓬松的褶边”和“粉红珍珠”这样的名字会很快返回不是我们要找的植物的结果。
纵观该系列,我们注意到在大小写和 sp 的使用上有细微的差别。/spp。/species 表示一个物种,cv。/var。对于一个品种来说。基于这些观察,我们编写了几个应用于该系列的快速函数,试图标准化数据以便进一步清理。
步骤 3 —通过交叉引用进行深度清理
仔细查看我们的学名(并对数据进行了几次迭代),我们发现许多名称都是更被接受的物种的过时同义词,或者被拼错了。这将在图像收集和稍后训练模型以识别拥有不同标签的相同图像时引起问题。
一次谷歌搜索之后,我们找到了世界植物在线数据库,一个开放的、基于网络的世界植物物种简编。它们列出了同义词和公认的物种名称,并由“分类专家网络”定期更新。非常适合交叉引用我们不可靠的学名。该数据库以. txt 文件的形式提供他们的数据,我们可以读取这些数据并与 ASPCA 植物毒性数据库中的数据进行比较。
来自 WFO 的数据——由“分类专家网络”定期更新。
作为第一步,我们将对来自 ASPCA 的数据进行左合并,保留所有的类,并添加任何与我们当前拥有的科学名称相匹配的数据。我们的目标是将数据库中的所有植物更新为最新的公认学名,因此我们通过该关键字(taxonomicStatus)进行快速排序,并删除保留第一个条目的任何重复条目(如果它存在,将被接受)。
步骤 3.1 —使用字符串匹配修复打字错误
许多学名指的是同一物种,但由于 ASPCA 数据库中的拼写错误,相差了几个字母。让我们使用 difflib 的 SequenceMatcher 来量化字符串距离(使用一种格式塔模式匹配的形式),通过将每个不被接受的条目与 WFO 数据库中的条目进行比较来发现这些错误。为了节省时间,我们可以对数据帧进行排序,只与以相同字母开头的学名进行比较。如果名称足够相似,我们就保留它,并最终返回最接*的匹配。这里,我们将阈值设置为 0.9,以避免任何不正确的匹配。
我们还定义了一个函数来修复数据中有问题的条目,这将把它们的学名、科、属和分类状态更新为 WFO 数据库中相应的(正确的)条目。
现在,我们可以遍历我们的数据,搜索名称匹配,并当场纠正它们对应的数据帧条目。
这一过程有助于我们发现错误,否则这些错误需要深入的检查和高水*的领域知识:
即使知道有一个错误,并排看名字,也很难发现数据库中的错别字。
步骤 3.2-手动清理未识别物种
不幸的是,许多这些未识别的物种在数据库中没有足够*的条目,足以让我对自动修复感到舒服。因此,我们对剩余的未知进行一些手动修复。令人欣慰的是,上面的代码已经将需要人工关注的样本数量减少到只有 50 个左右的条目,我们可以根据在 Google 上找到的正确条目,重用之前的 fix_name 函数来修复这些条目。
凯瑟琳·希斯在 Unsplash 拍摄的照片
步骤 3.3 —匹配同义的科学名称
既然学名已经全部更正,我们仍然需要对它们进行标准化,因为学名可能会由于更新的研究而随时间发生变化(导致分类状态列中的同义词标签)。如果一个科学名称是一个公认名称的同义词,我们希望在未来的 Google 图片搜索中使用这个公认名称。
同一个类(scientificName)的多个标签(taxonomicStatus 下的同义词)在以后会是一件非常糟糕的事情。
幸运的是,WFO 数据库包含一个acceptedNameUsageID
字段,其中包含了给定同义学名的公认名称,我们可以利用它来查找公认的学名,并将它们传递给fix_name
函数。
第 3.4 步—收尾
现在,我们已经(自动和手动)纠正了拼写错误,并将剩余的同义词与其最新接受的名称进行了匹配。剩下的工作就是为图像下载清理数据帧。
咻!这个过程花费了相当多的迭代来得到正确的方法。然而,在花时间训练模型之前,确保我们在建立图像数据库之前有干净的数据是至关重要的。
从最终的 pet 植物毒性数据框架中获得一些有趣的信息:
- 110 个植物家族中有 33 个并非完全有毒或无毒。
- 350 个植物属中有 7 个不是完全有毒或无毒的。
- 只有两种植物表现出物种特异性毒性,对猫来说是百合,对狗来说是核桃!
步骤 4—下载图像
下载图像的第一步是获取我们想要抓取的每个图像的 URL。为了做到这一点,我们采用了一种基于硒的方法,该方法基于法比安·博斯勒的一篇文章。Selenium 是用于测试 web 应用程序的可移植框架,几乎所有与网站的交互都可以模拟。Selenium webdriver 充当我们的虚拟浏览器,可以通过 python 命令进行控制。这里使用一个脚本来搜索谷歌图片的基础上,我们给它一个查询,只寻找和下载缩略图网址,因为我们将抓取大量的图像。一个问题是,谷歌的许多图像缩略图都是以 base64 编码的图像存储的。我们也想抓住这些,这样我们就不会错过任何具有高相关性的图像,因为我们在搜索结果中走得越远,图像对训练目的就变得越差。
太好了!现在我们有办法刮谷歌图片换图片了!为了下载我们的图像,我们将利用 fast.ai v2 中的一个函数,download_images
下载图像。但是,我们将深入源代码并对其进行一点升级,以便在图像出现时对其进行哈希处理,并忽略/删除任何重复的图像,这样我们就能获得一致的独特图像集。我们也将允许它解码和下载编码。jpg 和。png 图像,这是 Google Images 用来存储缩略图的格式。
现在,我们可以遍历我们的每个科学植物名称来收集它们的 URL,然后下载这些图像,同时验证这些图像中的每一个都是独一无二的。每组图片都被下载到我在 Colabs 上的链接驱动器中它们自己的文件夹中。需要注意的一点是,由于 Google Images 上存在大量重复图片,要抓取的 URL 数量需要远远多于您最终想要的图片数量。
下载后,我们采取措施确保每个文件夹包含正确数量的唯一图像。更多细节和代码见链接 Github repo。
因此,在这一阶段,我们已经将有用的标签信息以及为每个类下载的唯一图像放在了一起。这些图片被整齐地分成各自的文件夹,并直接放在我们的 Google Drive 中。值得注意的是,如果你想用这些图像来训练 CNN,在使用它们之前,如果你把这些图像放到本地的 Colab 环境中,你将会获得巨大的加速,但是这将在下一篇文章中详细讨论。
最后的想法
从零开始建立一个数据库对于简单的玩具例子来说,图像分类项目是直接的(参见 fast.ai v2 的书中一个棕色/黑色/泰迪熊分类器的好例子)。对于这个项目,我想扩展相同的方法,但是将它应用到更大的类集合中。这个过程实际上可以分为几个步骤:
- 获取类列表 从网页中抓取表格或文本数据非常简单,这要感谢 BeautifulSoup,通常只需要通过正则表达式或内置 python 方法进行一点点处理。
清理和验证你下载的数据的准确性是这一步最大的挑战。当我们有 10 个类和领域知识时,很容易发现错误并在继续之前修复它们。当我们有 500 个类,却不知道“薰衣草”和“薰衣草”哪个是正确的,事情会变得更加困难。我们可以用来验证数据的独立数据源至关重要。在这种情况下,我们相信 ASPCA 数据中的毒性信息,但不相信它们提供的科学名称,并且不得不使用提供最新分类信息的 WFO 数据库来纠正它们。 - 获取每个类的图片 URL 列表 在这里,Selenium非常灵活。我推荐看看 Fabian Bosler 的文章以获得更多关于如何使用 Selenium webdriver 的想法。你可以手动做的任何事情,网络驱动程序都可以模拟。我们可以执行搜索,找到缩略图并下载它们,如果我们想要更高分辨率的图像,甚至可以点击更大的图像并下载它们。
- 将每个图像下载到一个带标签的文件夹 下载图像的 fastai 功能运行良好,但一个主要的障碍是下载重复的图像。如果你想要更多的图片(10-15 张),如果你下载了谷歌图片搜索的每一个结果,你很快就会得到大量的重复图片。此外,该功能无法处理 base64 编码的图像,因为许多缩略图存储为。令人欣慰的是,fastai 提供了他们的源代码,可以修改这些源代码来解释编码图像,下载 http 链接,在它们进来时对它们进行哈希处理,只保留唯一的图像。
凯文在 Unsplash 上拍摄的照片
下一步是什么?
我将看看我们使用的图像的数量是如何产生影响的,以及将一些带有人工(文本、图片等)的图像放在一起进行过滤。)我们可能在这个自动下载过程中无意中抓取的组件。然后,使用 fast.ai 架构构建和训练 CNN 图像分类模型!
用人工智能创建播客
点唱机、GPT 3 和人工智能语音(包括 5 个稀有样本和 1 个播客)
人工智能播客所需的一切(截图:Merzmensch)
OpenAI不停地给我惊喜。去年他们上了头条提供了 GPT-2 ,一个具有强大写作技巧的 NLP 框架。2020 年,他们甚至用两件大事来反击。一个是 GPT-3,一个大规模的语言模型,有着巨大的创造潜力。
另一个引起了一些轰动,但很快就消失在背景中——在我看来很不公*: JukeBox,一个音频生成模型。过去有——现在也有——各种利用算法和人工智能创作音乐的方法。走向数据科学已经有了一个不错的关于人工智能音乐的文集。
- 早在 1960 年一位俄罗斯研究人员 R. Zaripov 发表了一篇关于算法音乐生成的论文(обалгоритмическомописаниипроцессасочинениямузыки(关于音乐创作过程的算法描述)——此处为俄文 pdf)。
- 雷·库兹韦尔在 1965 年的电视节目《我有一个秘密》中展示了一首由模式识别计算机创作的钢琴曲
- 经过两个人工智能的冬天,随着技术的出现,各种有前途的模式和音乐服务打开了它们的大门:查克、墨菲斯( arXiv )、 AIVA ( 以下是 2020 年十大服务的名单)
- tensor flow poweredMagenta在许多情况下都令人惊叹——对于旋律写作也是如此。使用钢琴变形金刚笔记本你可以生成一些有趣的实验旋律,就像这个:
尽管如此,它有很好的,但有限的 MIDI 质量。
投币式自动点唱机
OpenAI 有另一种方法。 JukeBox 是一个神经网络,在 120 万首歌曲的数据集上训练(其中 60 万首是英文的)。利用变压器的自我关注的好处,它产生新的音乐作品。你得到的既不是 MIDI 乐谱,也不是简单的旋律,而是完整的音频“录音”。Top-even transformers 根据流派和艺术家条件预测音频标记。这一点,结合相应的歌词,在 LyricWiki 上训练,使得点唱机变得完全新奇。
有一些限制——在长度、与内容相关的歌曲创作(你听到的要么是现有的文本到新的声音,要么是 GPT-2 生成的歌词或不可理解的语言模仿)和硬件强度方面。你需要一个好的系统来产生新的声音。幸运的是,有两个选择:
1。点唱机播放列表。
[## OpenAI Jukebox 示例资源管理器
浏览所有样品
jukebox.openai.com](https://jukebox.openai.com/)
拥有超过 7k 首音乐,可按流派、艺术家和其他功能进行搜索,让您对自动点唱机的容量有足够的了解。
2。点唱机 Colab 笔记本。
不是官方的,但是效果很好:“interactive _ with _ jukebox . ipynb”。
这款笔记本很不稳定(这里有一些有趣的变通方法让它运行得更好)——这需要时间。很多时间。它在一个会话中产生 3 首音乐曲目——从 2 级到 0 级的顶级运行大约需要 8 个小时。您的笔记本有崩溃和丢失所有数据和检查点的危险。但是当它运行时,它是强大的。你可以定义流派和解释者——并随意混合它们。
结果令人着迷。
听起来怎么样?(为了获得最佳质量,请使用您的耳机)
这里有一个例子。这是第一次运行——它在结束时让我不知所措。
初始级别 2 仍然有很多噪声:
级别 1(在大约 4 小时的迭代上采样之后)更加清晰:
但是如果你运气好,你的笔记本没有死机——8 个小时后,你会得到一些美好的东西,比如对另一个世界的一瞥,一部关于未知文化的纪录片的叙述,用一种你永远也不会理解的语言:
看看音乐是如何演变的,神秘的声音是如何歌唱的。一分钟——就结束了。你也明白你将永远听不到这首曲子的延续,或者这个解释。因为结果是独特的。其他地方没有分数。这是唯一的原件。
自动点唱机播放音乐的例子。
这里有一些有趣的片段供你欣赏:
民谣和英国摇滚的美妙融合:
一个电台播放着宋宛如的评论:
流自动点唱机:006 -“开”由 Merzmensch 从桌面或您的移动设备
soundcloud.com](https://soundcloud.com/merzmensch/006-jukeboxed-on)
一些在赛谬尔·巴伯、恩雅和菅野洋子之间进行了改编的古典管风琴音乐:
有趣的人声和催眠鼓:
然后这个完美的爵士自由式即兴创作:
用人工智能生成的音乐来检查播放列表,我确实使用了各种方法。有潜在的耳虫。
用例?播客!
现在我们有一个音乐库。但是,在什么情况下可以使用一段超出通常视唱标准的音乐,使用未知的语言,有时会有错误和故障?
- 用于取样和重新取样。
如果你是一名实验音乐的 DJ 或作曲家——这是你的一个百宝箱。没有人使用的独特声音。一些新鲜和不寻常的音频宝石。 - 用于视频背景。
我用它们来制作我的人工智能短片,比如《空房间》(也可以查看的制作):
最大的好处是——它没有版权。因为如果作为框架的 JukeBox 是 OpenAI 的财产,那么你用它生成的音乐就是你的。
- 播客? AI 生成音频的另一种使用方式是用于 AI 生成的媒体。比如播客。我从我的播客系列“ MERZeye ”开始,下面是我是怎么做的。
如何使用人工智能制作播客?
基本和我的文章《 AI 做电影人》差不多。
通常,您需要以下 Podcast 组件:
- 内容、文本、故事板
- 音乐背景和叮当声
- 声音
人工智能赋予你所有的可能性。
内容。
在我的第一集《梅泽耶》中,我使用了:
- 短片《空房间》已有剧本。它是用 GPT-2 实验室笔记本中的 GPT-2 生成的。
- 一首诗(“我真正的诗”),GPT-3 为我写的。
- 叙述文本(由我写的人类文本,因为你仍然不能让人工智能在各种内容之间自动调节)
音乐和声音。
- 对于前奏和尾声,我使用了点唱机连贯的音乐片段
- 对于电台播放的“空房间”,我使用了我的短片现有的配乐。这里是你需要的一切:叙述者,主角,和背景音乐。
- 对于类别“无线电广播”,是由自动点唱机播放的合适的各种类似无线电的记录。主持人聊天和交叉淡入淡出音乐的独特组合。这次是爵士乐,以吉米·卡伦为背景。
人声。
在这里,就像我以前的电影一样,我使用了雷普利卡工作室。目前有各种现有的解决方案,但是 Replika 工作室的声音库和质量通过一些独特的戏剧质量说服了我。其他解决方案对于信息视频或审核来说相当不错。
炼金术士。AI-MERZeye 的版主,由我和art breader共同打造。
对于这两位炼金术士,我使用了所罗门的声音(有着英国戏剧嗓音的老绅士,适合莎士比亚的舞台剧)和格里塔的声音(有情感语调的美国女士):
来自雷普利卡工作室的 Screencap(截图:Merzmensch)
在这里,您可以张贴整个脚本,并将声音分配给特定的短语。同时,甚至情绪调整也是可能的(就像上面的 Greta:“烦躁”或“活泼”)。
现在,您只需在自己选择的多轨道音频编辑器中进行混音,瞧!
第一集。
第二集。
对于我们如何利用所有这些创造性的生成媒体模型,你有其他想法吗?在评论中分享吧!
使用单词嵌入创建诗歌生成器
AI 创意助手
关于如何基于你最喜欢的作者书目创建一个人工智能诗歌生成器的指南。
-照片由莱昂纳多·巴尔迪萨拉在 Unsplash 上拍摄
随着最*新的和更复杂的语言模型的出现,有时向自然语言处理世界迈出第一步感觉是一项艰巨的任务。
本指南的目的是提供一个相当简单的项目,一个诗歌生成器,让你只使用一种技术就能掌握 NLP 世界:单词嵌入。
那么什么是单词嵌入呢?
单词嵌入是单词的 n 维向量表示,它基于语料库中存在的上下文以某种方式捕捉它们的含义。换句话说:在特定语料库(文本集合)中以相似方式使用的单词将具有相似的向量。
用数字表示单词的有趣之处在于,现在我们可以在数学运算中使用它们,比如计算相似度,这是我们将在诗歌生成器中使用的一种度量方法。
相似性用于量化两个向量之间的相同性。它计算它们之间角度的余弦值,当角度为 90°时,对于方向相同的向量,余弦值为 1,余弦值为 0。
单词间的余弦相似度
我们将使用这个属性来搜索单词和句子之间的相似性。
该算法
我们的目标是用输入的首词创作一首新诗。首先,我们将随机选择诗句,然后我们将计算这些诗句与我们最初的单词之间的相似度。稍后,我们将选择得分最高的诗句,并将其添加到这首诗中。我们将重复这个过程,但使用最后一节作为首词,直到这首诗完成。
对于这个特定的指南,我选择使用埃德加·爱伦·坡的诗。
该算法
数据回忆
首先,我们需要收集将成为我们项目来源的诗歌。为此,我们将使用 Python 创建一个 web-scraper,从 mypoeticside.com网站收集特定作家(在我的例子中是埃德加·爱伦·坡)的诗歌,并将它们保存到。csv 文件。
数据清理和准备
我们将创建一个函数,它采用我们之前创建的。csv 根据我们选择的正则表达式,将诗歌归档并拆分成诗句。然后,我们还将删除和替换诗句中一些不需要的字符(例如冒号),以使最终的诗歌看起来更好地连接和凝聚。
我们将在新的中保存经文。csv 文件。
诗歌生成器
诗生成器取。来自上一步的 csv 文件,一个新诗的初始单词和诗句数作为输入。
这个过程从我们最初的单词开始,理想情况下,它应该是一个广泛使用的单词,以便增加找到具有高相似性的诗句的机会。然后,我们从语料库中随机选择 n 个诗句,并计算它们与我们的初始词之间的相似度。
我们将使用 Spacy 库中的预训练向量,特别是在通用爬行数据集上训练的“en _ core _ web _ MD”模型。我们还将使用相同的库函数来计算相似性。
最后,我们选择得分最高的诗句,重复这个过程,直到这首诗完成。
格式化诗歌
为了正确格式化我们新创建的诗歌,我们将创建一个函数,将第一个诗节的字母大写,并在诗歌的结尾添加一个点。
结果
我个人选择生成 4 节的短诗,以尽量减少结果缺乏连贯性的机会。
这些是我最喜欢的一些结果:
一般来说,大多数生成的诗歌读起来都很有趣,即使有时结果可能缺乏连贯性,但大多数情况下,人们并没有注意到它们是如何创作的。
希望你喜欢这个指南,并随时通过 Linkedin 或 Twitter 联系我。
使用新冠肺炎数据集创建赛车条形图
让您的条形图栩栩如生
在我之前的两篇关于使用新冠肺炎数据集进行数据分析的文章中,我首先
- 讨论了如何使用 NumPy 和 Pandas 进行数据分析(https://level up . git connected . com/performing-data-analytics-on-the-新冠肺炎-dataset-using-python-NumPy-and-Pandas-bdfc 352 c61e 9),随后,
- 如何使用 matplotlib 进行数据可视化(https://level up . git connected . com/performing-data-visualization-using-the-新冠肺炎-dataset-47c441747c43 )。
新冠肺炎数据集是探索和理解数据分析和可视化的一个很好的候选。在本文中,我将向您展示如何在 matplotlib 中创建一个动态图表。特别是我会创建一个赛车条形图来动态显示每一天每个国家的确诊病例数。在这篇文章的结尾,你将会看到这样一个图表:
争论数据
首先,让我们使用 Jupyter Notebook 来清理和过滤所有数据,这样您就有了一个干净的数据集。准备好数据后,您就可以专注于创建条形图了。
导入包
第一步是导入您将用于此项目的所有包:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime
加载数据集
对于本文,我将使用来自https://www . ka ggle . com/sudalairajkumar/novel-corona-virus-2019-dataset的最新数据集。所以一定要下载并保存在你的本地机器上。对于我的情况,我将它保存在名为“Dataset—2020 年 7 月 20 日”的文件夹中,该文件夹与我的 Jupyter 笔记本的位置相对应:
# load the dataset
dataset_path = "./Dataset - 20 July 2020/"
df_conf = pd.read_csv(dataset_path + \
"time_series_covid_19_confirmed.csv")
df_conf
数据帧看起来像这样:
对数据帧排序
加载数据框架后,让我们按'省/州和'国家/地区列对值进行排序:
# sort the df
df_conf = df_conf.sort_values(by='Province/State','Country/Region'])
df_conf = df_conf.reset_index(drop=True)
df_conf
排序后,**df_conf**
将如下所示:
取消数据帧的透视
下一步是取消数据框的透视,这样现在就有了两个新列**Date**
和**Confirmed**
:
# extract the dates columns
dates_conf = df_conf.columns[4:]# perform unpivoting
df_conf_melted = \
df_conf.melt(
id_vars=['Province/State', 'Country/Region', 'Lat', 'Long'],
value_vars=dates_conf,
var_name='Date',
value_name='Confirmed')df_conf_melted
取消透视的数据帧(**df_conf_melted**
)现在应该如下所示:
将日期列转换为日期格式
下一步是将存储在日期列中的日期格式化为日期对象:
# convert the date column to date format
df_conf_melted[“Date”] = df_conf_melted[“Date”].apply(
lambda x: datetime.datetime.strptime(x, ‘%m/%d/%y’).date())df_conf_melted
更新后的**df_conf_melted**
数据帧现在看起来像这样:
按日期和国家/地区分组,并总结每个国家的确诊病例
现在是总结每个国家(不分省/州)所有确诊病例的时候了。您可以使用以下语句来实现:
# group by date and country and then sum up based on country
df_daily = df_conf_melted.groupby(["Date", "Country/Region"]).sum()
df_daily
**df_daily**
数据框现在显示了每个国家的每日确诊病例:
按日期排序数据帧并确认
我们希望根据两个关键字对**df_daily**
数据帧进行排序:
**Date**
(升序)**Confirmed**
(降序)
因此,以下语句实现了这一点:
df_daily_sorted = df_daily.sort_values(['Date','Confirmed'],
ascending=[True, False])
df_daily_sorted
**df_daily_sorted**
数据帧现在看起来像这样:
最后要做的一件事是,让我们将国家列表提取为一个列表:
# get the list of all countries
all_countries = list(df_conf['Country/Region'])
这将在下一节中使用。
每天都在重复
既然我们的**df_daily_sorted**
数据框架已经按照我们想要的方式进行了排序和格式化,我们现在可以开始深入研究它了。
首先,定义我们想要查看的前 n 个国家:
top_n = 20 # view the top 20 countries
让我们按照第一级索引(级 0 ,即**Date**
)对**df_daily_sorted**
数据帧进行分组,然后遍历它:
for date, daily_df in df_daily_sorted.groupby(level=0):
print(date)
print(daily_df) # a dataframe
您应该会看到以下输出:
2020-01-22
Lat Long Confirmed
Date Country/Region
2020-01-22 China 1085.292300 3688.937700 548
Japan 36.204824 138.252924 2
Thailand 15.870032 100.992541 2
Korea, South 35.907757 127.766922 1
Taiwan* 23.700000 121.000000 1
... ... ... ...
West Bank and Gaza 31.952200 35.233200 0
Western Sahara 24.215500 -12.885800 0
Yemen 15.552727 48.516388 0
Zambia -13.133897 27.849332 0
Zimbabwe -19.015438 29.154857 0
[188 rows x 3 columns]
2020-01-23
Lat Long Confirmed
Date Country/Region
2020-01-23 China 1085.292300 3688.937700 643
Thailand 15.870032 100.992541 3
Japan 36.204824 138.252924 2
Vietnam 14.058324 108.277199 2
Korea, South 35.907757 127.766922 1
... ... ... ...
以上数据框显示了每个国家的确诊病例总数(按降序排列),按**Date**
排序。
对于每一天,提取排名前 n 的国家及其确诊病例数:
for date, daily_df in df_daily_sorted.groupby(level=0):
print(date)
**#** print(daily_df) # a dataframe
**topn_df = daily_df.head(top_n)**# get all the countries from the multi-index of the dataframe **countries = list(map(lambda x:(x[1]),topn_df.index))[::-1]
confirmed = list(topn_df.Confirmed)[::-1]** **print(countries)
print(confirmed)**
注意,我用
**[::1]**
颠倒了**countries**
和**confirmed**
列表的顺序。这是为了确保病例最多的国家列在最后。这样做将确保稍后当您使用**plt.barh()**
方法绘制水*条形图时,案例最多的国家将被列在顶部。这是因为***plt.barh()***
方法在向上的方向绘制水*条。
您将看到类似这样的内容:
2020-01-22
['Bangladesh', 'Bahrain', 'Bahamas', 'Azerbaijan', 'Austria', 'Australia', 'Armenia', 'Argentina', 'Antigua and Barbuda', 'Angola', 'Andorra', 'Algeria', 'Albania', 'Afghanistan', 'US', 'Taiwan*', 'Korea, South', 'Thailand', 'Japan', 'China']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 548]
2020-01-23
['Bahamas', 'Azerbaijan', 'Austria', 'Australia', 'Armenia', 'Argentina', 'Antigua and Barbuda', 'Angola', 'Andorra', 'Algeria', 'Albania', 'Afghanistan', 'US', 'Taiwan*', 'Singapore', 'Korea, South', 'Vietnam', 'Japan', 'Thailand', 'China']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 643]
2020-01-24
['Azerbaijan', 'Austria', 'Australia', 'Armenia', 'Argentina', 'Antigua and Barbuda', 'Angola', 'Andorra', 'Algeria', 'Albania', 'Afghanistan', 'Vietnam', 'US', 'Korea, South', 'Japan', 'France', 'Taiwan*', 'Singapore', 'Thailand', 'China']
...
此时,您基本上已经获得了绘制条形图所需的所有数据,该条形图显示了每个国家每天的确诊病例数。
绘制条形图
您现在可以显示一个竞赛条形图,显示每个国家的确诊病例数。您不能在 Jupyter Notebook 中显示动态图表,因此让我们编写以下代码,并将其保存为名为 RacingBarChart.py 的文件(保存在您启动 Jupyter Notebook 的同一目录中)。下面以粗体显示的代码是绘制赛车条形图的代码:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime# load the dataset
dataset_path = "./Dataset - 20 July 2020/"
df_conf = pd.read_csv(dataset_path +
"time_series_covid_19_confirmed.csv")# sort the dfs
df_conf = df_conf.sort_values(by=
['Province/State','Country/Region'])df_conf = df_conf.reset_index(drop=True)# extract the dates columns
dates_conf = df_conf.columns[4:]# perform unpivoting
df_conf_melted = \
df_conf.melt(
id_vars=['Province/State', 'Country/Region', 'Lat', 'Long'],
value_vars=dates_conf,
var_name='Date',
value_name='Confirmed')# convert the date column to date format
df_conf_melted["Date"] = df_conf_melted["Date"].apply(
lambda x: datetime.datetime.strptime(x, '%m/%d/%y').date())# group by date and country and then sum up based on country
df_daily = df_conf_melted.groupby(["Date", "Country/Region"]).sum()df_daily_sorted = df_daily.sort_values(['Date','Confirmed'],
ascending=[True, False])# get the list of all countries
all_countries = list(df_conf['Country/Region'])**# plotting using the seaborn-darkgrid style
plt.style.use('seaborn-darkgrid')****# set the size of the chart
fig, ax = plt.subplots(1, 1, figsize=(14,10))****# hide the y-axis labels
ax.yaxis.set_visible(False)****# assign a color to each country
NUM_COLORS = len(all_countries)
cm = plt.get_cmap('Set3')
colors = np.array([cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])**top_n = 20for date, daily_df in df_daily_sorted.groupby(level=0):
**#** print(date)
**#** print(daily_df) # a dataframe
topn_df = daily_df.head(top_n)
# get all the countries from the multi-index of the dataframe
countries = list(map (lambda x:(x[1]),topn_df.index))[::-1]
confirmed = list(topn_df.Confirmed)[::-1] **# clear the axes so that countries no longer in top 10 will not
# be displayed
ax.clear()** **# plot the horizontal bars
plt.barh(
countries,
confirmed,
color = colors[[all_countries.index(n) for n in countries]],
edgecolor = "black",
label = "Total Number of Confirmed Cases")** **# display the labels on the bars
for index, rect in enumerate(ax.patches):
x_value = rect.get_width()
y_value = rect.get_y() + rect.get_height() / 2** **# display the country
ax.text(x_value, y_value, f'{countries[index]} ',
ha="right", va="bottom",
color="black", fontweight='bold')** **# display the number
ax.text(x_value, y_value, f'{confirmed[index]:,} ',
ha="right", va="top",
color="black")** **# display the title
plt.title(f"Top {top_n} Countries with Covid-19 ({date})",
fontweight="bold",
fontname="Impact",
fontsize=25)** **# display the x-axis and y-axis labels**
**plt.xlabel("Number of people")** **# draw the data and runs the GUI event loop
plt.pause(0.5)****# keep the matplotlib window
plt.show(block=True)**
要运行该应用程序,请在您的终端/Anaconda 提示符中键入以下内容:
**$ python RacingBarChart.py**
您现在应该会看到赛车条形图:
如果不想显示这么多国家,调整**top_n**
变量的值:
top_n = **10**
并更改图形的大小:
fig, ax = plt.subplots(1, 1, figsize=(**10,6**))
如果你想加速比赛,改变**pause()**
功能的持续时间:
# draw the data and runs the GUI event loopplt.pause(**0.2**)
摘要
我希望你有机会尝试一下赛车条形图。赛车条形图允许信息动态呈现,是一个非常强大的媒介来推动你的观点。如果您有任何改进,请告诉我,我很乐意收到您的来信!下次见!
用 Azure 函数和 MongoDB 创建 RESTful 无服务器 API
了解如何使用 Azure 函数创建一个使用 MongoDB Atlas 作为后端的无服务器 API。
在本教程中,我们将在使用 MongoDB Atlas 后端的 Azure Functions 中使用 HTTP 触发器构建一个无服务器 API。我们将为一个存储专辑信息的假想音乐库构建一个 API。
我们将使用 C#来构建这个 API,因此您至少需要理解 C#的语法。我不打算深入探究 Azure 函数和 MongoDB 背后的复杂性的大量细节,我只想简单地展示它们如何一起工作来创建一个简单的 API。
如果你想了解更多关于 MongoDB 的知识,我强烈推荐你去看看 MongoDB 大学。他们为希望了解 MongoDB 所有知识的开发人员和 DBA 提供了极好的免费课程。如果你喜欢这篇文章,并且想了解更多关于 Azure 函数的知识,我建议你查看一下文档。
什么是 MongoDB?
MongoDB 是一个 NoSQL 文档数据库,在索引和查询方面为开发人员提供了灵活性。数据存储在 JSON 文档中,这允许我们随时改变数据结构。
MongoDB Atlas 本质上是 MongoDB 的托管服务,我们可以将其用于我们的 MongoDB 集群。我们可以在 Azure、AWS 或 GCP 上托管我们的 MongoDB Atlas 集群。
对于 C#开发,我们可以使用MongoDB 驱动程序连接到我们的 MongoDB 集群。在开发期间使用它时,重要的是使用与您的 Atlas 集群正在使用的 Mongo 版本兼容的驱动程序。
什么是 Azure 功能?
Azure 函数是我们可以在 Azure 中运行的小块代码,而不用太担心应用程序基础设施(这种“担心”因场景而异。真的要看情况和你的要求)。
我们可以使用特定的事件来触发函数中的动作。对于本教程,我们将基于 HTTP 请求触发事件。
设置 MongoDB Atlas 集群
在开始编码之前,我们需要设置我们的 MongoDB Atlas 集群。MongoDB 文档中有一个关于如何设置 Atlas 集群的很棒的指南,所以如果你还没有的话,看看这个指南。
创建 Azure 函数
一旦你创建了你的集群,让我们创建我们的 Azure 函数。我准备用 Visual Studio 2019 开发我的函数。为了使用 Visual Studio 开发 Azure 功能,请确保您已经启用了 Azure 开发工作负载。
创建一个“新项目,点击“ Azure Functions ”。就像我之前说的,我们要用 C#做这个项目,所以要确保这是模板中选择的语言。
现在您已经选择了正确的模板,为您的 API 命名并设置一个容易找到的位置来保存项目。
现在,我们可以选择想要创建的函数类型。由于这是一个 API,我将创建一个带有 HTTP 触发器的函数。创建 HTTP 触发器函数时,可以设置该函数将使用的存储帐户以及授权级别。
我们在这里不会做任何授权的事情,所以只需将它设置为“Anonymous”。关于授权的一个快速注释,如果您创建了一个具有某个级别的函数,但决定以后需要更改它,您可以在代码中这样做,所以不要太担心这个设置。只是帮 Visual Studio 给你生成一个模板。
点击创建并祝贺您!你现在有一个 Azure 函数。虽然这不是很好,但是让我们做点什么吧。
连接到 MongoDB 集群
好了,现在我们已经设置好了函数,让我们获取到 MongoDB Atlas 集群的连接字符串。
前往您的 Atlas 门户网站(如果您尚未登录)并登录。点击连接按钮,如下图所示:
因为我们要将应用程序连接到我们的集群,所以为我们的连接方法选择连接您的应用程序选项:
现在我们必须选择一个驱动程序版本。选择" C#/。NET" 为驱动" 2.5 或更高版本"为版本。复制连接字符串,因为我们以后会用到它。
现在,让我们创建一个名为 Startup.cs 的新类。这个类将允许我们在函数中使用依赖注入。
在我的启动类中,我正在实例化我的 MongoClient 的单例实例,这样所有的函数都可以使用它。这里我们所需要的是获取我们的 MongoDB Atlas 集群的连接设置,并将其作为字符串参数传递。
我已经将这个实际的连接字符串放在我的 local.settings.json 文件中,该文件是在 config 的icon configuration实例中获得的。将连接字符串或设置硬编码到您的代码中并不是一个好主意,因此这是您的代码中可以用来保护应用程序所需的秘密的一种方法。
设置我们的相册类
现在让我们建立我们的相册类。这门课我们不会做什么太惊人的事情。为了让这个模型与 MongoDB 驱动程序一起工作,我简单地强调了我们需要/可以做的几件事情。我们的类的定义如下:
在这个类中,我已经给了我的专辑一个 Id、名称、艺术家、价格、发行日期和流派。很简单。这里我想强调的两件事是 Id 属性和 AlbumName 属性。
对于 Id 属性,我已经将该属性指定为我们的 ObjectId。这充当了我们的 MongoDB 文档的主键。MongoDB 驱动程序会为我们生成这个。
对于 AlbumName 属性,我将该属性装饰为一个名为 Name 的 BsonElement。当这个属性被持久化到 MongoDB 时,它将在我们的文档中被调用。
创建 RESTful 方法
我们已经获得了制作 RESTful API 所需的几乎所有东西,所以让我们开始构建它吧。对于我们所有的函数,我已经创建了一个构造函数,它接受以下参数,这些参数将有助于注入我们的依赖项:
- MongoClient :连接到我们的数据库。
- ILogger :这样我们可以在函数中记录活动。如果你在 Azure 上部署了一个功能并启用了应用洞察,那么你就是日志被发送到的地方。
- IConfiguration :这是我用来管理我们函数中需要的所有秘密的。正如我之前提到的,这些值保存在 local.settings.json 中,用于本地调试。
我还创建了一个 IMongoCollection <相册> ,这样我们就可以在 MongoDB 数据库中使用我们的相册集合了。
对于我们所有的函数,所以我们得到一个异常,我刚刚记录了异常消息,并设置我们的 IActionResult returnValue 抛出一个 500 错误响应。没什么复杂的,但是对于这个例子来说足够简单了。
现在让我们深入了解每个函数😊
创建相册. cs
在这个函数中,我们传递一个 HttpRequest 类型的请求,并读取该请求的正文。我用过 Newtonsoft。Json 将我的请求反序列化到我的 album 对象中,我正在用我们输入的值创建一个新的 Album 对象。
然后我使用。InsertOne() 方法将我们的新相册对象插入到我们的 MongoDB 中,并使用我们的相册对象将我的返回值设置为一个新的 OkObjectResult。
GetAlbum.cs
在这个函数中,我将传递一个 id 来表示我们文档的 id。在我们的 try/catch 语句中,我将尝试使用来查找具有该 id 的相册文档。在我们的 MongoCollection 上找到()方法,并将其返回给用户。如果我们找不到它,我们将发送一个日志消息说我们找不到它,并抛出一个 404。
GetAllAlbum.cs
在这些函数中,我们在这里所做的就是使用 Find() 函数来查找我们的相册集合中的所有相册,并将它们作为列表返回给用户。如果收藏中有专辑,我们会返回一个 404。
UpdateAlbum.cs
UpdateAlbum 略有不同。这里我们传递我们的请求体和一个 id。我将我们的主体反序列化为一个相册对象,然后设置我们传递给 updatedResult 项的 id。
我使用 ReplaceOne() 方法做两件事。首先,使用 id 在集合中查找具有该 id 的相册,然后,我传递 updatedResult 相册对象来替换现有的条目。如果我们找不到这个项目,我会抛出一个 404。
删除相册. cs
在我们的 DeleteAlbum 函数中,我们再次传递我们希望删除的文档的 id,并使用 DeleteOne() 函数找到具有该 id 的相册,然后删除它。
使用 POSTMAN 测试我们的 API
我们所有的方法都已经就绪,所以我们现在可以开始使用 Postman 测试我们的 API 了。让我们依次测试我们的每一个功能。在 Visual Studio 中按 F5 启动我们的函数。
它不应该需要太长时间,但一旦它完成,你应该给每个功能的一些网址如下:
保持这个控制台窗口打开,因为这些 URL 被映射到每个函数,我们将需要使用它们来触发我们的函数。
对于我们的 CreateAlbum 函数,我们需要发送下面的 JSON 有效载荷来将一个相册插入到我们的集合中。这是我发送的有效载荷:
{
"albumName": "No.6 Collaborations Project",
"artist": "Ed Sheeran",
"price": 15.99,
"releaseDate": "2019-12-06T11:00:00Z",
"genre": "Pop"
}
将 URL 粘贴到文本框中,并确保类型设置为 POST 。点击发送,应该会得到如下响应:
看来成功了!回到你的相册集群,导航到你的相册收藏,点击找到。我们应该能够看到作为 JSON 有效负载的一部分发送的带有值的插入文档。
让我们尝试读取我们刚刚创建的项目!复制并粘贴该项目的 _id 字段,然后在我们的 GetAlbum URL 中将它用作 id 参数。将请求类型更改为 GET 并点击“ Send ”。
如您所见,我们应该在主体中看到返回给我们的已创建相册的文档。
快速浏览维基百科后,这张专辑涵盖了更多的流派,所以我们需要更新我们的文档。使用相同的 id,将请求类型更改为 PUT 并更新主体,如下所示:
{
"albumName": "No.6 Collaborations Project",
"artist": "Ed Sheeran",
"price": 15.99,
"releaseDate": "2019-12-06T11:00:00Z",
"genre": "Pop, Rap, Hip-Hop"
}
点击发送更新我们的文档:
在 Atlas 中找到您的收藏,然后点击“查找”。我们应该看到更新后的文档在 MongoDB 中保持不变。
现在让我们测试一下我们的函数,看看我们是否可以删除文档。使用 id 作为 Id 参数,并将请求类型设置为删除。点击发送删除文件。
回到地图,再次点击找到。如果成功了,这个文档应该从我们的相册收藏中删除。
结论
在本教程中,我们学习了如何使用 Azure 函数构建一个真正简单的 API,该 API 使用 MongoDB 作为数据存储。虽然这是一个非常简单的项目,但希望本教程能给你一些如何在 Azure 函数中使用 MongoDB 的想法。
如果你想看完整的样本,请查看 GitHub 上的代码!
如果你有任何问题,请在下面的评论区告诉我!
用 9 个简单的步骤在 Microsoft Azure 中从头开始创建一个无服务器的 Python 聊天机器人 API
学习使用 Azure Function 应用程序创建和部署自己的无服务器聊天机器人应用程序,这些应用程序可用于 Slack、Skype、MS Teams 和其他应用程序
介绍
聊天机器人和无服务器是 2020 年完全主导企业界的两大技术趋势:
为什么不一石二鸟,学习如何构建自己的无服务器聊天机器人?
完成本文后,您将知道如何:
- 在 Azure 中部署无服务器应用程序,
- 建立一个聊天机器人,可以嵌入到一个网站,连接到微软团队,Slack,Skype,Facebook Messenger 和许多其他渠道!
图片作者:皮沙贝的穆罕默德·哈桑
为什么是聊天机器人?
我们现在都知道,在未来几年,聊天机器人将在世界各地的组织中变得越来越突出。从优化公司和客户之间的信息交流到完全取代销售团队。
根据 Juniper Research 发布的一篇论文,我们可以预计,到 2022 年,客户服务领域多达 75 % 的查询将由机器人处理,每年将带来 80 亿美元的业务成本。
为什么没有服务器?
图片来源:Pixabay 的 Nikin
无服务器应用程序是另一个巨大的技术趋势,它们允许在线部署简单的应用程序,而不需要建立或管理任何类型的服务器基础设施。此外,现在有了微软和亚马逊等公司提供的优秀云服务,就有可能(也非常容易)完全根据使用情况来扩展您的应用程序,因此您只需为使用量付费,永远不必担心没有足够的带宽!
教程:
现在,我们将通过几个步骤来建立和设置你的机器人。
你需要的只是一个 Azure 订阅,Visual Studio 代码(VSCode)安装在你的机器上,旁边还有一些 Python 版本!
步骤 1:在 Azure Portal 上注册一个新的 Web 应用程序 Bot 服务
首先登录 Azure 门户网站,在搜索栏上查找 Web 应用程序 Bot,这将允许您导航到 Bot 服务页面,在这里您可以在 Azure 门户网站上创建一个新的 Web 应用程序 Bot 。
您可以通过在 Azure 门户主页上搜索来找到 Bot 服务
单击 add 创建新服务,这将带您进入下一页,在这里您必须选择一系列配置,您可能需要创建一个新的资源组,并确保选择 S1 定价层(这是我们目前需要的)。
选择 Echo Bot C# 模板,我们稍后将稍微修改该模板以将 Bot 连接到我们的 API。
步骤 2:通过 VSCode 设置 Azure 访问
此时,我们将创建我们的 bot 将与之交互的后端。有多种方法可以做到这一点,你可以在 Flask、Django 或任何其他框架中创建一个 API。
我们将使用 Azure Function 应用程序,因为它可以非常简单地建立一个无服务器的 API,可以根据需求进行很好的扩展。
首先,创建一个新文件夹并在 VSCode 上打开它:
现在,我们需要安装一些扩展来帮助我们创建一个功能应用程序,并将其推送到 Azure,即我们需要 Azure CLI 工具和 Azure 功能。
一旦你安装了 VSCode 的扩展,你就可以通过 VSCode 登录你的 Azure 订阅来管理那里的资源!
按 CTRL+SHIFT+P 打开 VSCode 命令托盘,然后输入 Azure Sign in。
登录后,您应该会在 VSCode 的底部栏看到以下内容:
步骤 3:创建一个新的 Azure Function App 项目
导航到 VSCode 中的 Azure function 选项卡并创建一个新项目:
遵循将出现在 VSCode 命令托盘上的提示:
选择要使用的语言(在本例中为 Python)并创建新的虚拟环境:
作为我们的模板,我们将使用 HTTP 触发器:
我们将使我们的安全选项匿名,这意味着任何人都可以访问你的 API 而不需要 API 密钥,你可以在这里阅读更多关于认证设置的信息:
Azure Functions 是一种无服务器的计算服务,让您无需显式地…
docs.microsoft.com](https://docs.microsoft.com/en-us/azure/azure-functions/)
最后,为保存无服务器功能应用程序的文件夹选择一个名称,然后按回车键。
第四步:修改函数应用程序的代码
现在您已经创建了您的函数应用程序,应该已经为您的项目自动生成了一个文件夹结构。您应该会看到一个文件夹,其名称与您在步骤 3 中创建项目时传递的名称相同。
在这个文件夹中,应该有一个名为 init 的 python 文件。py,看起来像这样:
init。py 注释
即使对于有经验的 Python 程序员来说,主函数定义的语法初看起来也有点令人畏惧:
def main(req: func.HttpRequest) -> func.HttpResponse:
这段代码只是指定函数将在接收到请求对象时执行,并将返回 HTTP 响应。
从这里,我们可以很容易地修改这个模板,以添加我们希望通过我们的机器人接口提供的功能,这可能包括例如从 SQL 数据库查询数据,触发其他功能或您可能需要您的机器人做的任何其他工作。
对于这个用例,我们将使用开放的英国政府数据:我们的无服务器函数将获得一个包含英国城市名称的 HTTP 请求,它将返回该城市所有图书馆的列表。
让我们在应用程序中创建一个名为 modules 的文件夹,并在其中创建一个名为 library_finder.py 的新文件。
此时,您的项目应该是这样的:
在 library_finder.py 中粘贴以下代码片段:
您需要在 azure function 为我们创建的虚拟环境中安装 pandas。
如果您不熟悉虚拟环境,请查看以下链接:
[## 12.虚拟环境和包- Python 3.8.2 文档
Python 应用程序通常会使用不属于标准库的包和模块。应用程序…
docs.python.org](https://docs.python.org/3/tutorial/venv.html)
我们的虚拟环境已经设置好了,我们现在只需要激活它。这可以在 vscode 中通过键入
.venv\Scripts\activate
在 VSCode 的终端窗口中。
如果您的环境已经成功激活,您将看到(。venv)在路径的开始。
现在我们可以安装熊猫了,只需在你的控制台中输入“pip install pandas”
(对于您希望通过 pip 安装的任何其他外部库,可以重复相同的过程)。
现在将熊猫添加到您的 requirements.txt 文件中非常重要,它看起来应该是这样的:
现在我们可以回到 init。py (我们的无服务器应用程序的主文件)并修改代码,以便在接收到 HTTP 请求时(a)我们将在 json 上查找附加到请求正文的文本字段,(b)我们将读取文本并解析它,(c)我们将调用我们刚刚定义的函数并返回请求的数据。
以下代码将满足我们的目的:
步骤 5:在本地测试您的 API
我们现在已经使用 Azure 函数创建了一个基本的无服务器 API,是时候测试我们是否一切都按预期工作了!
我们可以在本地启动我们的 Azure 功能应用程序。为此,请在终端中用 VS 代码键入以下命令:
func start
您的功能将被初始化,您应该会看到标志性的 Azure Function 应用程序徽标!
如果一切正常,您应该不会看到任何错误,并且在提示符的末尾会显示如下消息:
该消息包含与我们在本地启动的无服务器应用程序通信的 URL。这可以很容易地用一个叫做 Postman 的免费软件来完成。在 Postman 中,您可以通过发送请求并查看响应来调试 API。
或者,您可以通过在命令提示符窗口中打开 Python 并向指定的 URL 发送请求,然后检查我们是否获得了预期的响应,来测试 API 是否正在工作。
第六步:将你的功能应用部署到 Azure
现在,您的无服务器应用程序正在工作,并且您已经成功创建了一个 HTTP 触发器,是时候将它部署到 Azure 了,这样您就可以从本地网络外部访问它。
要部署它,只需导航到 VScode 中的 Azure 选项卡,然后滚动到函数窗口。
右键点击你的 Azure 订阅,按在 Azure 中创建功能 App。
提示将要求您命名函数,提供位置和 Python 版本。按照要求执行步骤,等待直到 Azure 函数创建完成。你应该可以在 Azure Functions 选项卡中找到它,再次右键单击该函数并选择 Deploy to Function App。
将出现一个提示,要求确认部署,几分钟后,将出现一条消息,表明部署已经成功。
发生这种情况时,您可以通过打开浏览器窗口并导航到以下位置来检查您的部署是否有效:
[https://[name_of_your_functionapp].azurewebsites.net/](/[name_of_your_functionapp].azurewebsites.net/)
如果您在 Azure 门户页面中导航到您的功能应用程序,也可以找到此链接。
访问该链接后,您应该会看到以下页面:
这意味着您已经成功地将您的功能应用程序部署到互联网上,现在任何人都可以访问您的无服务器 API!(取决于您在创建 HTTP 触发器时选择的身份验证设置)
步骤 7:将机器人连接到无服务器功能
对于这一步,我们将回到我们在本教程的步骤 1 中在 Azure portal 中创建的 Bot 应用服务!
导航到 web bot 服务主页,转到 build 选项卡,然后点击“打开在线代码编辑器”。
从在线代码编辑器导航到 Bots > EchoBot.cs
这将允许您对机器人的 C#代码进行一些轻微的修改,这是您最初应该看到的内容:
用下面提供的代码替换此页面上的代码:
记得把我们上一步创建的 Azure Function App 的 URL 访问点的 URL 替换掉!
现在我们只需要构建我们对 C#代码所做的修改,这可以通过右键单击页面上的 build.cmd 文件轻松完成
我们可以测试我们的机器人,检查它是否如预期的那样工作。打开 Azure Portal 并导航到您的 Web 应用程序 Bot 主页。
在网络聊天中选择测试选项:
步骤 8:将你的机器人发布到频道
如果一切正常,你就可以将这个机器人添加到任何支持的频道了。
通过按保存并接受使用条款来配置 Microsoft Teams 频道。
接下来,我们可以通过点击“get bot embed codes”链接并复制 HTML 标记内的 URL,为某人提供与我们的 bot 对话的链接。
访问该 URL 的任何人都将被邀请在 MS 团队中添加机器人,如下所示:
MS 团队将打开,我们的机器人将被列为您的聊天之一。
步骤 9:(可选)将您的机器人应用程序发布到 MSTeams
这可能足以满足你的机器人需求,如果你想发布机器人,那么它可以被添加到 ms 团队的群聊中,你可以通过打开 MS 团队并点击“…”图标,然后打开应用工作室:
点击创建新应用:
你必须填写登记表。重要的是,一旦您填写了所有必需的信息,导航到机器人选项卡并按下设置按钮:
在现有机器人选项卡中,我们刚刚创建的机器人应该列在下拉菜单中
最后一步是导航到清单编辑器上的测试和分发选项卡,然后分组安装你的应用。
您已经配置了您的 MS Teams 应用程序,您需要做的只是邀请机器人加入特定的团队,并享受您的新无服务器机器人应用程序。
创建一个简单的叶子地图-新冠肺炎全球总案例
从数字到互动地图
在 Unsplash 上由 Brett Zeck 拍照
本文中显示的所有代码都可以在我的 GitHub repo 这里获得。
创建交互式地图有很多方法,但我最喜欢的方法是通过叶子;基于 Python 语言的地图可视化库。
在这篇文章中,我想展示如何创建一个很好的交互式地图,以及当前的情况(2020 年 4 月 4 日);代表病情的最佳数据应该是世界上新冠肺炎确诊病例的数量。让我们开始吧。
创建地图
对于不知道叶子是什么的你;它基本上是一个地图可视化库,有许多内置的专门用于创建交互式地图。我们可以使用下面的命令来安装叶库。
#Installation via pip
pip install folium#or, installation via conda
conda install folium -c conda-forge
就像我们已经安装了我们的库。现在,让我们尝试展示一个简单的交互式地图(或底图)。
import folium#Creating a base map
m = folium.Map()
m
使用 leav 的交互式底图示例
现在我们已经有了我们的基本地图。在这种情况下,我想创建一个世界地图,显示截至今天(2020 年 4 月 3 日)新冠肺炎确诊病例的彩色国家部分(这基本上是我们所说的 choropleth 地图)。
要做到这一点,我们首先需要有实际的数据。幸运是,现在有许多现有的 API 提供这种数据。在我的例子中,我会使用来自 thevirustracker.com 的 API。让我们试着从 API 中提取一些数据。下面是从 API 提取数据的步骤。
#We would use the requests module to pull the data from API
import requests#Pulling the Worldwide COVID-19 data, we use the get function from requests module with API endpoint(Basically a URL address to request your data) as the function parameter. To know available endpoint, check the API documentationres = requests.get('[https://api.thevirustracker.com/free-api?countryTotals=ALL'](https://api.thevirustracker.com/free-api?countryTotals=ALL'))#We turn the data into json. It would become dictionary in the Python.
covid_current = res.json()
从 API 中以 JSON 格式提取的数据(字典)
在上面的数据中,我们可以看到有很多可用的数据,但在这种情况下,我只取国家(' title ')和总案例(' total_cases ')的数据。
#Creating the COVID-19 DataFramedf = []
for j in range(1,len(covid_current['countryitems'][0])):
df.append([covid_current['countryitems'][0]['{}'.format(j)] ['title'],
covid_current['countryitems'][0]['{}'.format(j)]['total_cases']])df_covid = pd.DataFrame(df, columns = ['Country', 'Total Case'])
我们的新冠肺炎数据
对于最后一个数据,我们还需要一个世界各国的 GeoJSON 数据。GeoJSON 数据是一种表示地理要素(如国家边界)的数据格式。对于我们的情况,leav 已经拥有了我们可以使用的 GeoJSON 数据。
#Setting up the world countries data URL
url = '[https://raw.githubusercontent.com/python-visualization/folium/master/examples/data'](https://raw.githubusercontent.com/python-visualization/folium/master/examples/data')country_shapes = f'{url}/world-countries.json'
我还将清理新冠肺炎数据,因为在下一步中,我将在地图上使用由 clean 提供的世界国家数据。在这种情况下,我需要在新冠肺炎数据集中使用我的国家名称,以跟随由 leav 提供的国家名称。
#Replacing the country namedf_covid.replace('USA', "United States of America", inplace = True)
df_covid.replace('Tanzania', "United Republic of Tanzania", inplace = True)
df_covid.replace('Democratic Republic of Congo', "Democratic Republic of the Congo", inplace = True)
df_covid.replace('Congo', "Republic of the Congo", inplace = True)
df_covid.replace('Lao', "Laos", inplace = True)
df_covid.replace('Syrian Arab Republic', "Syria", inplace = True)
df_covid.replace('Serbia', "Republic of Serbia", inplace = True)
df_covid.replace('Czechia', "Czech Republic", inplace = True)
df_covid.replace('UAE', "United Arab Emirates", inplace = True)
现在,我们有了数据,可以使用 flour 创建新冠肺炎确诊病例 choropleth 地图。下一步,我们只需要将 choropleth 图层添加到我们的底图中。
#Adding the Choropleth layer onto our base mapfolium.Choropleth(
#The GeoJSON data to represent the world country
geo_data=country_shapes,
name='choropleth COVID-19',
data=df_covid,
#The column aceppting list with 2 value; The country name and the numerical value
columns=['Country', 'Total Case'],
key_on='feature.properties.name',
fill_color='PuRd',
nan_fill_color='white'
).add_to(m)m
新冠肺炎 Choropleth 地图
就这样,我们已经有了一张简单的地图,代表了世界上确诊的新冠肺炎病例。我们可以看到,2020 年 4 月 3 日的美国是最深的颜色。这是因为现在的病例数是最高的。一些国家如意大利、中国、西班牙、意大利的颜色也比其他国家略深,因为他们的确诊病例数仅次于美国。
另外,我会添加一个可点击的标记来显示国家名称。在这种情况下,我们需要这个 csv 文件中提供的每个国家的纬度和经度。
for lat, lon, name in zip(country['latitude'],country['longitude'],country['name']):
#Creating the marker
folium.Marker(
#Coordinate of the country
location=[lat, lon],
#The popup that show up if click the marker
popup=name
).add_to(m)
m
有标记的 choropleth 地图
我们到了。我们只是根据新冠肺炎确诊病例制作了一个简单的互动地图。我们仍然可以看到很多东西,比如恢复的病例、死亡病例、百分比等等。
结论
创建一个简单的交互式地图是很容易的叶库,我们需要的只是数据,现在可以通过 API 拉。Choropleth 是世界上最适合可视化新冠肺炎确诊病例的地图之一。
希望有帮助!
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
如果您喜欢我的内容,并希望获得更多关于数据或数据科学家日常生活的深入知识,请考虑在此订阅我的简讯。
如果您没有订阅为中等会员,请考虑通过我的推荐订阅。
在 PowerBI 中创建风暴跟踪器
并在此过程中帮助菲律宾气象局
菲律宾正处于新冠肺炎疫情的风暴“安博”(黄蜂的国际名称)之中。
我习惯根据我从新闻和时事中听到的内容来思考相关的项目(这很容易)。这有让我保持乐观的副作用。
所以我决定在 PowerBI 中为这场风暴创建一个追踪器。在此过程中,我发现菲律宾大气、地球物理和天文服务管理局(PAGASA)网站上的风暴数据有小错误!
这让它变得更有价值。
概观
跟踪器是这样工作的。
如果你还记得我以前的一篇关于在 PowerBI 中重建 Gapminder 的文章,你会意识到这个风暴追踪器只是那个泡泡地图的另一个版本。但是我们不使用 x 轴和 y 轴,而是使用纬度和经度数据。
此外,数据还应该有一个日期时间列,以动画我们的跟踪。
所以我们的数据要求是
- 追踪风暴的日期和时间栏。
- 地图中的经纬度栏用来绘制风暴
获取数据
在网上搜索时,我找到了两个可以为我们服务的数据源, Wunderground 和 PAGASA (菲律宾气象局)。
风暴安博(黄蜂)的地下数据
PAGASA 关于风暴安博(黄蜂)的数据
查看数据来源,我选择使用 PAGASA 的数据,因为它显示了风暴每小时的移动。这将在以后产生更详细的地图。
导入数据
现在,要在新的 PowerBI 文件中导入它
- 去首页>获取数据>网页。
并选择适当的表。
2.现在,我们需要将日期和时间列合并到一个日期时间列中。为此,我选择了使用合并列
并将结果列转换为日期时间类型。
3.接下来,将 location 列分成纬度和经度部分。您可以使用空格分隔符拆分列。
然后去掉度北和度东字符。
确保将结果列转换为十进制格式。稍后当我们将它们转换成纬度和经度类型时,这是需要的。
得到的数据应该是这样的。
清理的 PAGASA 数据
将列转换为适当的数据类型
为了在地图上实际绘制纬度和经度,我们必须确保 PowerBI 能够识别纬度和纬度数据类型。
应用查询更改后,在数据视图中,单击纬度列并转到列工具>属性。将栏目转换为纬度和不汇总。
对经度做同样的操作。
这些列后面应该有一个球形图标。否则,转换不成功。
如果您之前没有在 PowerQuery 编辑器中将列转换为小数,转换将不会成功。
绘制数据
好了,要在地图中绘制数据,请选择 PowerBI 中的 ArcGIS 地图。
请注意,默认的地图可视化没有使其具有交互性的时间轴,因此我们使用 ArcGIS 版本。
将列拖动到适当的轴上,如下所示
初步的情节应该是这样的
让我们编辑图来改变图例的颜色并放大点。转到图表中的省略号>编辑>符号样式。设置以下设置,我发现这些设置在我们的绘图中效果很好
- 透明度 0%
- 符号大小 50px
- 符号颜色
符号颜色
图表现在应该看起来像这样
现在,上面的图表有一些问题。
这些点应该彼此靠*,并且不应该向后移动,因为这不是暴风雨的通常移动方式!
当你播放图表时,这是显而易见的
回头看看数据,罪魁祸首是晚上 11 点被自动标记为第二天的一部分。
例如,2020 年 5 月 15 日晚上 10 点之后的晚上 11 点时间立即被标记为 5 月 16 日,而不是 5 月 15 日。
要纠正这些,我们必须通过右键单击数据编辑查询>来打开查询编辑器。在电量查询编辑器中,右击单元格>替换数值。
替换数据中的错误值
当然,我也通知了 PAGASA 这件事。
编辑完数据后,图表应该看起来像这样。
结论
好了,我们的教程到此结束。让这个项目对我来说特别的是,我能够指出 PAGASA 网站上的一个错误。
完成的文件在下面的 Github 链接中。
还看我的书PowerQuery 熊猫指南Leanpub。**
也看看我以前在 Medium 上的文章。
如何从头开始创建 TensorFlow 2 模型——一个“无用”的例子。
假设您想要构建一个可以在项目中使用的模型。您选择用 TensorFlow 2 框架来实现它,但是您想知道如何实现它。
TensorFlow 2 (TF2)框架允许创建可以在您的项目中轻松使用的复杂模型。所以让我们带着必需的东西(TF2 和 Python 3)进入主题的深处。
在 TF2,模型是使用框架的 Keras 包装器中的Model
类构建的。但是在写代码之前,试着把你的模型分成可用的部分。让我们以文章中的自动编码器为例,如果你不知道这是什么,请阅读维基百科文章这里。
回到模型,您可以将您的模型构建为一个完整的自动编码器,但是您很可能不会这样使用它。你很可能会单独使用编码器部分或解码器部分。所以让我们把模型分成两部分,Encoder
和Decoder
,相同的名字将会在代码中进一步使用。
自动编码器的概念性表示。编码器和解码器部分用方括号区分,长方体表示模型的不同层,像卷积或全连接。
如何从概念上建立模型
最好的方法是将模型划分为服务于某些特定功能的功能部分,例如,一个对某些东西进行编码,而另一个进行预测或解码。在使用自定义训练循环(您将在本文中进一步了解)的由几个部分(定义为 objects 中的模型类)组成的模型训练期间,这不会产生问题。
建立模型—模板
构建一个模型非常简单,你只需要从tf.keras
库中继承Model
类。第二类必需的方法— __init__
和call
(为什么不__call__
在故事的最后解释)。
您可以在后续工作中使用的模型模板
__init__
方法包含了将在call
方法中执行的所有层和操作。例如,自动编码器的所有神经网络层,或者在执行任何操作之前将要初始化的一些常数。这里需要注意的是,所有的层都必须继承自Layer
类,因此如果你想做一些 TensorFlow 操作(像tf.math.reduce_sum()
),你必须使用Lambda
层(更多在这里)。另一个解决方案是在call
方法内部执行操作。还要记住,如果你在__init__
方法中定义了一些东西,那么你应该指向模型的类的实例——也就是self
关键字。否则,它们在call
方法中不可用,例如self.layer_1 = tf.keras.layers.Dense(5)
。
关于call
方法,您可以使用在__init__
方法中定义的对象来定义对输入数据执行的所有操作。在call
的参数列表中,您可以根据需要定义任意数量的参数。最基本的一个是输入,用于将数据输入模型。另一个论点是training
,它并不总是强制性的,但如果你不知道他,生活就会变得复杂。由于在训练阶段和测试/验证阶段,它们的行为有很大的不同,所以在它们创建之后,应该将争论进一步传递给像BatchNormalization
和Dropout
这样的层。
创建模型后,你只需将其创建为 Python 对象,如model = ExampleModel()
。如果有一些参数,如神经元或层数,你可以在创建模型对象时传递它们,如__init__
方法中所指定的。您将在下面的案例研究中了解到这一点。然后,如果你想做一个预测,你只需调用模型,即model(input)
,它输出在call
方法中定义的处理过的输入。
构建自动编码器模型的案例研究
所以我们一起做个例子来扩展你的知识面。想象一下,你在一家塑料回收公司,大部分时间你都在做一项无聊的工作,检测生产线上的非塑料瓶垃圾。但是你发现了一篇关于用机器学习检测图像的文章,你开始学习它来解决问题。由于非塑料瓶物品种类繁多,很难对它们进行分类——你可以有一个瓶子,也可以有一个锡箔或罐头,也许还有一些蔬菜或其他未指定的东西。所以你要集中精力检测所有不是塑料瓶的东西。因此你决定使用异常检测(更多在这里),你想检测非塑料瓶垃圾作为一个异常。几周之后,你了解了 TF2,并开始思考这个模型。首先,你从互联网上收集了一些塑料瓶垃圾的图片。您获取了 908 个塑料瓶图像,并将它们分成一个训练(827)和一个测试(81)数据集。还有。你发现了一些非塑料垃圾图像,并将其标记为阴性测试(也是 81)。
总结问题,我们必须检测垃圾图像是否是塑料瓶。为此,我们收集了 908 张塑料瓶图片和 81 张非塑料图片进行测试。你可以在我的 Github 知识库中找到专门为这篇文章创建的图片,你可以打开例如 Google Collab。
垃圾异常检测使用一个自动编码器的目的的储存库是教程在走向数据科学…
github.com](https://github.com/DanielWicz/trashanomaly)
或者直接访问 Google colab:
使用自动编码器的垃圾异常检测
colab.research.google.com](https://colab.research.google.com/github/DanielWicz/trashanomaly/blob/master/tutorial_notebook.ipynb)
此外,需要注意的是,你只有 827 张训练图像,这是一个相当小的数字,所以你要对这些图像应用图像增强——这种技术会稍微干扰图像,使它们对于神经网络来说有点不同,但我不会在这里详细介绍,你可以在这里阅读更多的和。
建立自动编码器模型
在此之前,如果你从我的 Github 库(,这里是)打开 Jupyter 笔记本是最好的,但是如果你从手机上阅读它,仍然有代码片段可以理解这个问题。然而,首先让我们在纸上定义模型:
用于异常检测的自动编码器模型。层的参数被省略了,但是你可以在下面的代码或者笔记本中检查它们。在卷积层之后使用完全连接的层的目的是确保来自卷积层的信息的有效聚集,从而更好地编码。然而,我找不到任何支持这一观察的论文,因此对此持怀疑态度。
如您所见,有两个部分——编码器和解码器(见左侧),但让我们转到代码,看看模型是如何构建的。由于堆叠了几层,它有点长,所以你将被迫转移到 Github 的网站上寻找要点或更好的方法——打开 Jupyter 笔记本。
编码器模型要点
代码遵循与ExampleModel
、tf.keras.Model
、__init__
方法和__call__
方法相同的方案。正如你所见,所有层的参数都在__init__
方法中定义。关于call
方法,看一下training=False
,参数的值被进一步传递到BatchNormalization
层。它确保该层在训练期间处于训练模式,而在测试/验证期间处于评估模式。此外,我总是将其默认值设置为False
,以确保 after training 不会错误地将True
传递给 BatchNorm 或 Dropout 层(它们不在模型中)。
让我们也来分析解码器的模型代码:
它看起来几乎一样,但颠倒了。看看__init__
方法中的默认参数,除了重建图像的最后一层,它们与自动编码器相反。
创建模型的对象
现在是创建模型的时候了,这真的很简单——就像示例一样,您只需创建模型类的一个实例:
对last_layer_kernel_shape method
的解释在文章的最后。
此时你可以尝试使用一个图像的编码器和解码器(就像:encode(image_variable)
)。结果应该是随机噪声,因为权重被初始化为随机的。然而,该图像应该具有与将在训练期间使用的图像相同的大小,因为权重的形状是基于第一遍的维度来初始化的。我会在文末深入探讨。
训练模型
为了训练模型,我们将使用一个定制的训练循环。让我们先把代码扔到桌面上:
代码实际上遍历了所有的历元(第一个循环),然后遍历了所有的批次,其中每个批次都被传递给了train_step
函数,如下所示。
简而言之,装饰器@tf.function
以较小的灵活性为代价显著提高了训练步骤的性能。更多关于 TF2 的网页这里。
train_step
函数将一批图像传递给编码器,然后将编码器的潜在变量传递给解码器,计算损失。然后用gradient
评估梯度并用apply_gradient
应用。整个过程是在GradientTape
的环境中进行的,T5 记录了正向传递过程中的所有操作,并在应用gradient
方法后执行反向传播。这里我就不多赘述了,更多关于在 TF2 建立定制训练循环的内容,你可以去谷歌的 Tensorflow 2 网站这里。但是要注意training=True
,如果你不定义它,批量标准化层就不会训练。此外,如果它没有设置为 True,就很难检测到,没有错误也没有任何特定的行为。所以你必须小心这个论点。如果您想玩代码,请查看我前面提到的 Jupyter 笔记本。
好的,但是异常检测在哪里…?看着生产线上所有的塑料瓶,我们还是很无聊。从这里开始!
使用经过训练的自动编码器进行异常检测
由于自动编码器在潜在空间中聚集相似的数据,所以您可以使用潜在向量(从编码器生成的向量)进行异常检测。让我们对测试示例(塑料瓶— 81 张图像)和负面测试示例(排除了塑料瓶的一些垃圾—也是 81 张图像)执行 PCA(主成分分析)。
使用主成分分析将潜在空间向量降低到 2 维的图
大约一半的正例聚集在 PCA 图中的一个点周围,因此它给出了数据实际上以某种方式聚集的线索,但是不打算作为异常检测器工作(它在这里表现不好)。
我决定使用非线性聚类模型——高斯混合模型(简称 GMM——你可以在这里找到更多的)。我在这里不会详细介绍,但是它允许用高斯(正态)分布描述多维数据集群,并用于评估测试示例离集群有多远。在我们的例子中,它允许描述塑料瓶的潜在向量簇(编码器的输出)。然后使用训练集(827 个塑料瓶图像),使用 GMM 模型计算得分,得分描述了一个例子离聚类有多远。此外,如果示例是塑料瓶(低于阈值)或另一个垃圾(高于阈值),则分数被设置为阈值。
在提供的 Jupyter 笔记本中使用了 GMM 模型,所以如果你对它的性能感到好奇。
保存编码器和解码器模型
与 Keras 或 PyTorch 的框架相比,保存模型有点棘手。用来自 Keras 的model.save()
保存模型并不完全有效,因此你必须解决它。此外,to_json()
也不适用于非顺序(比如在 Keras 中)模型,这就有点麻烦了。
首先,为了保存模型,你必须保存权重,这很简单——你只需要在模型的对象上调用save_weights(filepath='path_to_file')
方法。
然而它不包含模型架构,所以您必须以另一种方式保存它。最简单的方法是用 models config 创建一个字典,并将其保存为 JSON 文件,如下所示。
然后加载模型变得很简单,因为对于字典,您可以使用解包操作符(**
用于字典)。解包操作符将字典转换成方法的参数。例如,使用来自 config 的字典将被转换a_method(**encoder_dict)
将被转换成一系列参数a_method(fillters = [32, 64, 128, 196, 256, 1024], kernel sizes = …)
。
加载模型
保存模型的加载分三步进行。第一步是创建模型类的一个实例。在谈论拆包的时候,我们已经迈出了装载模型的第一步。由于你必须使用 json 标准库加载 JSON 格式的序列化字典,并在调用Encoder
和Decoder
的同时用**
解包。
对解码器执行与上面相同的代码。但是在这里停下来一会儿——为什么在文章的开始我们要把自动编码器分成两部分?如果我们想要执行异常检测和检测非塑料瓶垃圾,我们实际上不需要加载解码器!这就是为什么把模型分成更小的部分真的很有用。然而,要使这个模型可行,还有三个步骤。
第二步是初始化权重形状。最简单的方法是传递与原始模型形状相同的输入。由于图像的形状为(batch_size,x_dimension,y_dimension,channel dimension ),因此我只需使用形状为(1,127,127,1)的np.zeros
或tf.zeros
(假设 np 为 NumPy,tf 为 TensorFlow)创建一个零向量,并将其传递给编码器。在这样一个简单的技巧之后,形状被初始化,您可以加载保存的权重,如下所示。
现在你有了检测非塑料瓶垃圾的模型,你可以愉快地做一些比观察生产线上的垃圾更有趣的事情,例如学习机器学习!如果你有任何问题,请写在评论区,我会在 1-2 天内回复你。
总结
- 要创建一个模型,你需要创建一个继承自
tf.keras.Model
的类,并且定义了两个方法__init__
和 call。 - 重要的是在调用方法中定义
training
参数,并进一步传递给批量规格化和 dropout 层,否则会出问题。 - 要使用该模型,您需要创建该模型的类的一个实例。记得过
training=True
,一边训练。 - 您可以使用自定义训练循环来训练这样的模型。
- 在传递第一数据之后,定义权重矩阵的形状。
- 保存模型需要将参数保存为可读格式(例如 JSON)并保存权重矩阵。
- 加载需要使用先前的参数创建模型,传递与先前形状相同的输入(例如
np.zeros
或tf.zeros
),然后加载保存的权重。
其他解释:
- 定义了
call
方法而不是__call__
,因为__call__
在调用call
之前调用了一些额外的东西。这些东西包括初始化权重的方法,以及确保调用中的东西正常工作的所有其他东西。因此,如果你有一个编码器实例,并用一个输入(如encoder(input, training=False)
)调用它,那么首先执行__call__
并完成一些工作。然后在所有那些东西之后,调用你定义的call
方法。 - 如果您想知道为什么在编码器模型和模型实例的创建中有
last_layer_kernel_shape
方法,那么这里是正确的地方。解码器需要关于最后一个卷积层的输出维度的信息,因此last_layer_kernel_shape
方法返回最后一层的维度。
有用的链接:
- 专门为这篇文章创建的包含整个 Jupyter 工作笔记本的存储库,您可以在这里找到和
- 来自 Google 的 Tensorflow 2 自定义层/模型指南,它充满了代码示例,您可以在这里找到它。
- 在 Tensorflow 2 中编写自定义循环你可以在这里找到。
创建一个分析登革热病例的网络应用程序
使用 Tweepy、TextBlob、NewsAPI 和 Geopandas 分析登革热
卢克·切瑟在 Unsplash 上的照片
介绍
信息就是力量。指尖上的信息是超能力。
在此期间,我偶然在 Reddit、Twitter 和脸书上看到了许多关于新冠肺炎的图像。它们中的许多都非常鼓舞人心,能够在如此短的时间内吸收大量信息是如此令人愉快。
我也想在新冠肺炎上建一个仪表板,但我觉得已经够多了。因此,我决定研究另一个问题,登革热,这也是一个紧迫的问题。data.gov.sg 的数据很容易获得,但是有三个主要问题:
- 一些数据没有任何可视化
- 数据到处都是,需要额外的工作来过滤结果
- 没有足够的数据来讲述一个故事
学分: ggscore
这促使我自己制作了一个仪表板。这个项目有 3 个主要部分:
- 分析:我可视化了登革热病例的数量,并创建了一个登革热热区和繁殖点的叠加地图。
- 新闻检索:该选项卡能够从多个来源检索主题为“登革热”的新闻。
- Twitter 分析:该功能构成了该项目的主体。简而言之,我实时检索 twitter 数据,分析他们的情绪,并以图表形式呈现出来。
这个项目的目的是展示构建这样一个强大的应用程序是多么简单。不一定是登革热。可以是任何话题。对于这个项目来说,重要的不是内容,而是技能。这更多的是概念验证,而不是最终产品。我将带你走过项目的所有 3 个不同部分,同时提供代码片段。希望您能够在自己有意义的项目中复制它。请务必滚动到本文的底部,找到完整代码的链接。
概念
在我开始之前,我想谈谈这个项目是如何建立的。
这个项目是用 Dash 构建的,这是一个构建数据科学/ML 项目的 Python 框架。随着 dash 引导组件【DBC】dash 核心组件【DCC】,你将需要最少的 HTML/CSS 技能来构建你的 web 应用。
这三个特性中的每一个都采用了不同的技术(将进一步解释)。然后使用 Heroku 部署所有这些功能,这是一个云*台即服务(PaaS),允许您轻松地将应用程序部署到云。
第 0 部分:仪表板
Dash 应用程序主要由两部分组成。
第 1 部分:应用程序布局
应用程序布局是应用程序的前端部分。然而,多亏了 DBC 和 DCC,你不需要复杂的 HTML 代码来创建你的应用程序。例如,我的整个应用程序是用以下代码创建的:
# Code partially hidden for readability
# Layout of entire app
app.layout = html.Div(
[
navbar,
dbc.Tabs(
[
dbc.Tab(analysisTab, id="label_tab1", label="Visuals"),
dbc.Tab(newsTab, ...),
dbc.Tab(socialMediaTab, ...),
dbc.Tab(infoTab, ...),
],
style={"font-size": 20, "background-color": "#b9d9eb"},
),
]
)
当然,你必须指定什么是 analysisTab,newsTab 等。你可以看到如何用 DBC 和 DCC 抽象你的应用程序。DBC 和 DCC 都提供了按钮、选项卡、表单、导航栏以及许多其他基本组件。
第 2 部分:应用程序回调(可选)
应用程序回调可以被认为是应用程序的后端部分。如果你观察上面的代码,你会注意到我给我的analysisTab
分配了一个id="label_tab1"
,这有助于我们识别哪个回调用于那个特定的组件。
回调的示例如下所示:
[@app](http://twitter.com/app).callback(
Output("sa-graph", "children"), [Input("interval-component-slow", "n_intervals")]
)
def sa_line(n):
children = [...]
return children
这样做的是,它将接受一个带有指定参数n_intervals
的输入组件interval-component-slow
,并将由方法sa_line()
创建的结果children
输出到输出组件sa-graph
。如果您想让您的应用程序具有交互性,这一点尤其有用。例如,你可以允许你的可视化根据用户点击的单选按钮而改变。
但是,如果您希望应用程序是静态的,并不总是需要回调。你会意识到我的前两个标签页没有使用回调,因为我不需要用户的任何输入。我只需要向他们发送信息。第三个标签(twitter 分析)是一个特例。我不得不使用回调,因为我希望我的分析每 30 秒刷新一次。稍后详细介绍!
第一部分:视觉化
地质公园
从data.gov.sg下载的数据以 GeoJSON 格式出现。GeoJSON 文件允许您将地理数据编码到对象中。它具有非常结构化的格式,包括位置的类型、几何图形和属性。由于登革热集群涉及一块土地,我们的位置被编码在一个多边形对象中,这是一个经度和纬度的数组。
读取 GeoJSON 文件很容易。我们简单地使用 Geopandas:
central_data = gpd.read_file("data/dengue-cases-central-geojson.geojson")
一旦数据被加载,操作应该很容易,前提是你精通数据框架。
薄层
follow 允许我们将 GeoJSON 数据可视化在一个活页地图(一个用于交互式地图的 JavaScript 库)中。
首先,我们创建一张地图并放大到我们想要的地点(在我的例子中,我选择了新加坡的经度和纬度):
kw = {"location": [1.3521, 103.8198], "zoom_start": 12}
m = folium.Map(**kw)
接下来,我在地图上添加两个图层(一个用于集群,另一个用于繁殖区):
folium.GeoJson(<FIRST LAYER DATA>, ...).add_to(m)
folium.GeoJson(<SECOND LAYER DATA>, ...).add_to(m)
最后,由于 Dash 不允许我直接显示叶子对象,所以我将生成的地图导出到一个 HTML 文件中,并使用一个 IFrame 来显示它:
m.save("dengue-cluster.html")html.Iframe(id="dengue-map", srcDoc=open("dengue-cluster.html", "r").read(), ...)
第二部分:新闻剪报
本节的主要学习点是 NewsAPI 的用法。NewsAPI 允许我们使用他们的 API 从多个来源搜索新闻文章。在您可以开始使用它之前,您需要注册一个帐户,以便获得您自己唯一的 API 密钥。
请注意,作为一个免费会员,你将受到每天新闻检索数量的限制。
存储密钥(本地系统)
钥匙很重要!就像你不会和你的朋友分享你的房子钥匙一样,你也不会想要分享你的 API 钥匙。当您在本地系统上开发时,我强烈建议您将所有的键保存在一个keys.py
文件中,并将该文件添加到.gitignore
中,这样它就不会被在线推送。
存储密钥(Heroku)
由于你没有将keys.py
推送到 GitHub,Heroku 无法访问它。相反,Heroku 有自己的配置变量。您可以通过 CLI 或它们的 GUI 将您的密钥添加到 Heroku。
Heroku 配置变量
代码!
出于部署的目的,我们让代码根据我们是在本地系统上还是在 Heroku 上来检索密钥:
try:
from keys import newsapikey # retrieve from local system
newsapi = NewsApiClient(api_key=newsapikey)
except:
newsapikey = os.environ["newapi_key"] # retrieve from Heroku
newsapi = NewsApiClient(api_key=newsapikey)
接下来,我们终于可以检索我们的新闻文章了:
all_articles = newsapi.get_everything(
q="dengue singapore",
from_param=date_n_days_ago,
to=date_now,
language="en",
sort_by="publishedAt",
page_size=100,
)
有三个主要功能newsapi.get_everything()
、newsapi.get_top_headlines()
和newsapi.get_sources()
。使用任何你认为合适的!然后我们可以检索我们的内容。检索所有文章标题的示例如下所示:
all_articles_title = [
str(all_articles["articles"][i]["title"])
for i in range(len(all_articles["articles"]))
]
现在您已经有了数据,您可以使用 DBC 添加它们。在这个项目中,我使用了卡片、卡片主体和卡片页脚。
第 3 部分:实时 Twitter 情感分析
这构成了项目的主体。我使用了 Tweepy ,这是一个 Python 库,允许我们访问 Twitter API 来传输推文。我的目标是检索所有与“登革热”相关的推文,预处理文本,分析情感,并可视化结果。从 Twitter 上不断挖掘推文,我的应用程序将每 30 秒刷新一次以显示新数据。
工作流程
在这个阶段,我可能应该向您介绍一下工作流程。
- 推文被从推特中抽出
- 推文正在被清理(翻译成英文,表情符号被移除,链接被移除),推文的情感被 TextBlob 分析
- 建立了与数据库的连接,每次有新的推文进入时,处理过的推文和情感就会被推送到数据库。对于本地系统,我使用 MySQL,对于 Heroku,我使用他们的 PostgreSQL 数据库。该脚本保持运行。
- 同时,我检索超过一天的推文,并从数据库中删除它们。这将确保我们的数据库不会耗尽空间。
- 随着我的应用程序在并行中运行,它将每隔 30 秒从数据库中检索一小时的数据并显示它们。
钥匙钥匙钥匙
就像 NewsAPI 一样,Tweepy 也要求我们生成 API 密钥。前往 Twitter 开发者获取您的!
Twitter 抓取
完整代码在此。 注意注释代码是针对 MySQL 的,未注释代码是针对 PostgreSQL 的。
步骤 1:如果数据库和表不存在,通过为我们创建一个数据库来初始化数据库。
DATABASE_URL = os.environ["DATABASE_URL"]
conn = psycopg2.connect(DATABASE_URL, sslmode="require")
cur = conn.cursor()cur.execute(
"""
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_name = '{0}'
""".format(
parameters.TABLE_NAME
)
)
if cur.fetchone()[0] == 0:
cur.execute(
"CREATE TABLE {} ({});".format(
parameters.TABLE_NAME, parameters.TABLE_ATTRIBUTES
)
)
conn.commit()
cur.close()
步骤 2:初始化 Twitter API 并开始流媒体播放
myStreamListener = MyStreamListener()
myStream = tweepy.Stream(auth=api.auth, listener=myStreamListener)
myStream.filter(track=parameters.TRACK_WORDS)
第三步:处理收到的每条推文。为了实现这一点,你必须超越** tweepy。StreamListener,它是负责从 Twitter 向您推送新推文的类。**
# Override tweepy.StreamListener to add logic to on_status
class MyStreamListener(tweepy.StreamListener):
def on_status(self, status):
# Start modifying
id_str = status.id_str
created_at = status.created_at
clean_text, text = preprocess(status.text)
sentiment = TextBlob(text).sentiment
polarity = sentiment.polarity
...
# SQL statement for pushing tweets to DB
...
# SQL statement for deleting and cleaning DB
...
让它活起来
让你的应用程序每 x 秒刷新一次的诀窍是使用dcc.Interval
。通过在应用程序中添加一个时间间隔作为回调的输入,可以告诉应用程序每隔 x 秒重新生成一次组件。
dcc.Interval(
id="interval-component-slow",
interval=30000, # in milliseconds
n_intervals=0,
),
情绪的折线图+饼图
首先,我们必须检索刮出的数据:
# Loading data from Heroku PostgreSQL
DATABASE_URL = os.environ["DATABASE_URL"]
db_connection = psycopg2.connect(DATABASE_URL, sslmode="require")# Load last 1 hour data from MySQL
...query = "SELECT ... FROM {} WHERE created_at >= '{}' ".format(
"dengue", timenow
)df = pd.read_sql(query, con=db_connection)# Convert UTC into SGT
...
接下来,对数据进行清理和转换,以绘制时间序列:
# Clean and transform data to enable time series
# Bin into 5 minutes
result = (
df.groupby([pd.Grouper(key="created_at", freq="5min"), "polarity"])
.count()
.reset_index()
)
...
最后,我们使用 Plotly Graph 对象将结果可视化。左上角的折线图是用go.Scatter
创建的,而右上角的饼图是用go.Pie
创建的。这就是创建视觉效果的简单之处!
检索实际的推文
推文用仪表板数据表显示。
dash_table.DataTable(id='table', columns=[{"name": i, "id": i} for i in df.columns], data=df.to_dict('records'))
真实推文的文字云
目前还不太支持单词云。传统的方法是创建一个图像,并把它附加到我们的可视化中。我使用的非常规方法是在 X 和 Y 轴上绘制单词,而不是显示坐标,而是显示文本。因此,这个词云是使用go.Scatter
创建的。
go.Scatter(x=wc_data.x,
y=wc_data.y,
mode="text",
text=wc_data.words,
...)
第 4 部分:部署到 Heroku
既然您的应用程序在本地系统中运行良好,那么是时候将它部署到 Heroku 了。我强烈推荐你看看这个 15 分钟教程如何做到这一点。否则,请参考下面的摘要:
- 创建一个 Heroku 帐户并下载 Heroku CLI
- 确保项目的所有依赖项都已下载。此外,运行
pip install gunicorn
- 创建一个 Procfile ,它将告诉 Heroku 运行什么
- 用
pip freeze > requirements.txt
创建 requirements.txt。这对于 Heroku 了解你正在使用的包是很重要的,这样他们也可以下载它。 - 使用部署您的应用程序
heroku login
heroku create "<YOUR APP NAME>"
git add .
git commit -m "<ANY MESSAGE>"
git push heroku master
heroku ps:scale web=1
注意:为了让你的 tweet scrapper 和应用程序同时运行,你需要 2 个不同的应用程序。否则,您可以支付额外的 dynos,这是 Heroku 容器。您的 Procfile 将会不同。要使用相同的数据库,将您的 scrapper 的 DATABASE_URL(配置变量)设置为与您的应用程序的 DATABASE_URL 相同。
此外,请注意,加载应用程序需要相当长的时间。这是因为我正在使用免费的 dynos。如果应用程序在设定的时间内没有被访问,免费的 dynos 将被置于睡眠状态。
结论
这个应用程序简单、轻量、功能强大。由于有限的预算、时间和网页设计技巧,这绝不是一个完美的应用程序。我还需要花时间优化算法,这将大大提高应用程序的加载速度。
通过这篇文章,希望你对 Dash,Tweepy,NewsAPI,Geopandas,和 leav 有了更深入的了解。我强烈要求你为不同的上下文创建类似的应用程序。
链接:
- https://dengue-db.herokuapp.com/
- https://github.com/bensjx/Dengue-dashboard
- https://github.com/bensjx/twitter-scrap-dengue
我如何用 Python 和 AWS 制作一个网站
关于使用 EC2 实例部署 Streamlit 应用程序的分步教程
作为编码员和程序员,我们必须能够在不随身携带笔记本电脑的情况下,向不同的人展示我们创建的项目和应用程序。为了做到这一点,我们可以把我们的应用程序放在我们自己的网站上,让任何能上网的人都能看到。创建我们自己的网站的第一步是找到一个网站托管服务,如亚马逊网络服务(AWS) 。这些主机服务将确保我们的网站在任何时候都可以从任何一台可以上网的电脑上访问。
我们网站的外观将完全取决于我们使用的代码。在我们的例子中,我们实现了 Streamlit Python 库来为基于数据科学的应用程序构建用户界面。要了解我们如何使用 Streamlit 创建我们的 web 应用程序,请查看下面的文章:
[## 我如何使用 Streamlit 构建 Web 应用程序
用于构建 Web 应用的 Streamlit 和 Python
towardsdatascience.com](/how-to-use-streamlit-to-create-web-applications-218af44064f5)
本文深入研究了我们使用 Streamlit 及其各自功能创建的 web 应用程序的开发和设计。
为网站利用亚马逊网络服务(AWS)
让我们开始创建网站的过程。首先,我们需要找到一个适合我们的网站托管服务。有许多虚拟主机服务,但我们现在将利用 AWS。
第一步。创建 AWS 帐户
首先,您必须先 创建一个 AWS 账户 。这是一个独立的帐户,不同于用于购物或销售的普通亚马逊帐户。我们将使用免费层帐户。我们将需要这个帐户,以便创建一个服务器来托管我们的网站。
第二步。创建 AWS EC2 实例
一旦创建了帐户,我们就可以继续创建一个 AWS EC2 实例。AWS EC2 实例或亚马逊弹性计算云用于启动实例,它是云中的虚拟服务器。
要创建一个实例,从主页导航到 My Account 选项卡并点击 AWS 管理控制台。
AWS 管理控制台
一旦您在管理控制台中找到自己,导航到所有服务部分,并点击 EC2 。
EC2 仪表板
在这里你会发现许多不同的资源和选择。然而,我们将关注于启动实例部分。点击启动实例按钮继续。
第三步。准备 EC2 实例
接下来,我们将看到一个页面来定制该实例。这里的第一步是选择一个亚马逊机器映像(AMI) 。
这里我们选择亚马逊 Linux AMI 2018.03.0 (HVM) 。我们选择这一个是因为它已经包含了许多编程语言(例如 Python ),并且它是符合自由层条件的。
选择实例类型
一旦我们选择了合适的 AMI,我们继续下一步,选择正确的实例类型。选择自由层合格选项: t2.micro 。
配置安全组
继续下一步,直到第 6 步。在那里,我们需要配置安全组。点击添加规则。
点击添加规则后,您会看到添加了一个新行。我们需要为我们的 Streamlit 应用程序配置这个新行。
- 在类型下,选择自定义 TCP 。
- 在端口范围下,输入 8501 。
- 在源下,选择任意位置。
点击右下角的查看并启动完成。
启动实例
点击查看并启动后,会看到一个查看页面。确保一切正确,然后点击右下角的发射。
第四步。创建并下载新的密钥对
点击发射后,出现以下弹出窗口。
在这里,您需要创建一个新的密钥对,方法是导航到 选择一个现有的密钥对 并选择 创建一个新的密钥对 。
为密钥对名称输入您想要的任何名称。然后,点击下载密钥对。为了从命令终端访问我们的实例,您将需要这个.pem
文件。(动了一下。pem 文件放入。ssh 文件夹在一个安全的位置,如果你没有一个,你可以创建它。
下载私钥文件(。pem),可以点击启动实例。这将带您进入显示启动状态的下一页。
第五步。从我们的 EC2 实例中查找公共 DNS (IPv4)
现在我们的 EC2 实例已经启动并运行,我们需要找到公共 DNS 地址来在命令终端中访问该实例。为此,导航到管理控制台,点击 EC2 ,点击实例,然后选择我们刚刚创建的实例。您会在右下角找到公共 DNS (IPv4) 地址。 复制地址 因为以后会用到。
已完成启动 EC2 实例
如果遵循了这些步骤,您就已经成功地使用 AWS 启动并创建了 EC2 实例。接下来,您需要 SSH 到实例中,以便建立网站。
SSH 到 EC2 实例
SSH 或安全 Shell 是一个网络协议,它允许我们通过终端使用命令行界面来操作 EC2 实例。如果你有 Mac 或 Linux,那么你已经有 SSH 了。Windows 用户可能需要使用 油灰 。
对于下面的步骤,我们将使用 MacOS 命令终端来访问我们的 EC2 实例。要 SSH 到我们的实例,我们必须首先打开命令终端,然后导航(CD)到包含我们之前下载的.pem
文件的同一个文件夹。(确保。pem 文件位于. ssh 文件夹中,该文件夹存储在您计算机上的安全位置。
进入该文件夹后,运行以下命令:
$ ssh -i key_pair.pem ec2-user@your_public_dns_address
- 使用您创建并下载的
.pem
文件。 - 将“ ec2-user@ ”添加到您的公共 DNS (IPv4)地址的开头的中。
输入此命令后,您应该会在命令终端中看到以下内容:
这意味着我们在 EC2 实例中成功了!
EC2 实例中的安装
现在,我们需要安装我们的 Streamlit 应用程序所需的所有包和文件。即使我们的 AMI 包含一些必需的包,我们仍然需要更多的包。当我们仍然在 EC2 实例的命令终端中时,运行以下命令:
1.安装 Python 3.6
AMI 中包含的 Python 是 Python 2 而不是 Python 3,这是我们的 Streamlit 应用程序运行的基础。所以我们需要安装 Python 3。
$ sudo yum install python36
2.安装 Git
我们还需要安装 Git,以便下载包含我们的 Streamlit 应用程序的存储库。
$ sudo yum install git
3.克隆 Github 存储库
我们将克隆包含我们的 Streamlit web 应用程序的 Github 存储库:
$ git clone your_github_repo
在我们成功地将 Github repo 克隆到 EC2 实例中之后,导航( CD )到 repo 文件夹。
4.安装要求
导航到包含requirements.txt
文件的文件夹。一旦我们进入包含我们的requirements.txt
文件的文件夹,我们就可以安装运行我们的 Streamlit 应用程序所需的所有库。
$ python36 -m pip install -r requirements.txt --user
这将安装在requirements.txt
文件中指定的所有库。--user
命令将确保我们的库以正确的权限安装。
查看/运行应用程序
安装好一切后,我们终于可以运行我们的应用程序了。在包含.py
文件的文件夹中,输入 Streamlit 命令:
$ streamlit run your_app.py
现在,我们的 web 应用程序可以通过使用外部 URL 在我们首选的浏览器中在线查看。但是,当我们注销 EC2 实例时,该链接将不再可用,这将关闭当前会话。
为了让这个 URL 在我们注销时仍然有效,我们需要再安装一个东西: TMUX 。
TMUX
TMUX 让我们能够保持会话运行,即使我们注销。但是首先,使用 ctrl+C 关闭当前的 Streamlit 会话。然后,导航回实例的开头。
要安装,请在 EC2 实例中输入以下命令:
$ sudo yum install tmux
然后,使用以下内容创建一个新会话:
$ tmux new -s name_of_session
这将启动一个新的会话,您可以选择任何名称来运行我们的应用程序。从那里,就像以前一样,移动到你的app.py
文件所在的文件夹。
在那里,您可以再次运行 Streamlit:
$ streamlit run your_app.py
现在,您已经有了在 TMUX 会议中运行的 Streamlit 应用程序。要退出 tmux 会话但仍保持运行,您必须 分离 tmux 会话 。你可以通过按下 ctrl+B,然后 D 来完成。
分离后,您可以通过运行以下命令来检查 tmux 会话的状态:
$ tmux ls
您应该会看到有一个 tmux 会话仍然处于活动状态。现在,您可以从 EC2 实例注销,而不必担心 URL 会关闭。只需按: ctrl+D 。
Web 应用程序启动并运行
我们的 web 应用程序是什么样子的
成功!我们能够通过使用 AWS EC2 实例来激活我们的 Streamlit 应用程序。我们拥有的具体网络应用是我们的约会应用,它利用了 数据科学 和 机器学习 :
为约会应用程序利用无监督机器学习
towardsdatascience.com](/dating-algorithms-using-machine-learning-and-ai-814b68ecd75e)
网站的 URL 可能不漂亮,但可以通过为我们的实例创建自己的自定义域名来解决。然而,这一过程需要更多的步骤,比预期的更加复杂。注册一个域名也可能要花一些钱。
最后,我们希望您能够利用 Streamlit 和 AWS EC2 实例成功创建一个托管您自己的 web 应用程序的网站。请随意查看展示我们的 Streamlit web 应用程序开发或约会应用程序开发的其他文章。
资源
使用无监督机器学习和 NLP - marcosan93/AI-Matchmaker 的匹配简档
github.com](https://github.com/marcosan93/AI-Matchmaker) [## 我如何使用 Streamlit 构建 Web 应用程序
用于构建 Web 应用的 Streamlit 和 Python
towardsdatascience.com](/how-to-use-streamlit-to-create-web-applications-218af44064f5) [## 我用机器学习和人工智能做了一个约会算法
为约会应用程序利用无监督机器学习
towardsdatascience.com](/dating-algorithms-using-machine-learning-and-ai-814b68ecd75e)
用 JAX 创建神经网络的对立例子
在本教程中,我们将看到如何创建使用 JAX 欺骗神经网络的对立例子。
黛比·莫勒在 Unsplash 上的照片
首先,让我们看看一些定义。有哪些对立的例子?简单地说,对立的例子是神经网络的输入,其被优化以欺骗算法,即导致目标变量的错误分类。我们可以通过给目标变量添加“适当的”噪声来对其进行错误分类。下图展示了这一概念。
摘自古德费勒等人的解释和利用对立的例子。
今天,本教程的重点是演示如何创建对立的例子。我们将使用 快速梯度符号方法进行生成。
在这个方法中,如果 x 是一个输入图像,我们将 x 修改为
其中对抗输入是通过将交叉熵损失的梯度的 符号w . r . t 输入图像 x 和加到原始图像上获得的。ε是这里的超参数。
对于本教程,我们将使用流行的 MNIST 数据集。如果你不知道什么是 MNIST 数据集,我建议去下面的链接。
MNIST 数据库(改进的国家标准和技术研究所数据库)是一个大型数据库…
en.wikipedia.org](https://en.wikipedia.org/wiki/MNIST_database)
为了训练我们的模型并生成对立的例子,我们将使用 JAX 模块。JAX 是自动微分(AD)工具箱,在训练 MNIST 这样的大规模数据集时非常方便。有人恰如其分地将 JAX 描述为服用类固醇的笨蛋!由于这不是“JAX 介绍”教程,我不会深入探讨。
你可以参考下面的文档来进一步了解 JAX。
声明:JAX 已经放弃 Python 2 支持,需要 Python 3.6 或更新版本。见 docs/CHANGELOG.rst. JAX 是…
github.com](https://github.com/google/jax)
对于初学者,我建议参考以下关于 JAX 及其用法的教程。
[## 你不了解 JAX
你不知道 JAX 这个简短的教程涵盖了 JAX 的基础知识。JAX 是一个 Python 库,它扩充了 numpy 和…
colinraffel.com](https://colinraffel.com/blog/you-don-t-know-jax.html)
现在让我们深入编码。我提供的代码是建立在下面的 GitHub 库之上的。我已经做了必要的修改,并添加了一些新的功能,使其适合手头的应用程序。可以访问以下链接,以供参考。
一个用于构建攻击、构建防御和基准测试的对抗性示例库…
github.com](https://github.com/tensorflow/cleverhans/blob/master/tutorials/future/jax/mnist_tutorial.py)
首先,我们将导入所有重要的库。
接下来,我们将下载并加载 MNIST 数据。
现在,我们将定义一个函数,该函数将通过迭代所有层来计算全连接神经网络的输出,获取输入/前一层的激活并应用双曲正切激活。
记住,对于我们使用的输出, z=w⋅x+b
在本教程中,我们将使用交叉熵损失。下面的函数将返回我们模型的损失。
下面的单元格定义了我们的模型的准确性以及如何初始化它的参数。
现在我们必须生成批量的训练数据。为此,我们将为数据集创建一个 Python 生成器。它一次输出一批 n 个训练样本。
接下来,我们的工作是使用【StAX】创建一个全连接的神经网络架构。Stax 是一个神经网络规范库。这里,我们将详细说明卷积神经网络中各层的规格。
现在我们必须定义小批量 SGD 优化器。优化器给了我们 3 样东西。
1]方法 opt_init 接受 init_fun 返回的一组初始参数值,并返回初始优化器状态 opt_state,
2]方法 opt_update,它接受梯度和参数,并通过应用一个优化步骤来更新优化器状态,以及
3]获取优化器状态并返回当前参数值的方法 get_params。
接下来,我们将根据训练示例训练我们的模型。在训练结束时,我们将获得“参数”,我们将使用这些参数来计算损失函数相对于测试图像的梯度。
最后,我们定义一个函数,它将返回损失函数相对于测试输入的梯度。此外,这个函数将计算测试损失以及预测我们的目标变量的类别。
现在是时候测试我们的模型了。
首先,我们来看一个测试输入。这里我们选择一个属于类‘7’的图像。
让我们想象一下原始图像。
上面的代码给出了下面的输出。
让我们看看我们训练过的模型是否预测了该图像的准确类别。
运行上面的代码后,我们得到以下输出。
我们看到我们的模型已经正确地预测了我们的输入图像的类别。
现在让我们跳到有趣的东西。让我们用 快速梯度符号法扰动同一个图像。
为此,我们定义了函数。这里我们使用 0.3 作为超参数ε的值。
运行上面的代码后,我们得到以下输出。
我们看到扰动的图像有很多噪声。这个噪声量可以由超参数ε的值来控制。
最后,让我们看看噪声是否对模型分类有任何影响,即我们的模型是否对扰动的图像进行了错误分类。
上面的代码给出了以下结果。
瞧啊。我们的模型将输入图像错误分类为类别‘3’。
因此,我们看到了如何产生对立的例子。您可以对整个测试集重复相同的过程。
如果你对 JAX 感兴趣,我建议你访问下面的页面,看看你还能为 JAX 做些什么。
JAX,杰克斯,杰克斯。如今,Twitter 似乎不知道其他任何东西(除了新冠肺炎)。如果你和我一样,想知道什么…
roberttlange.github.io](https://roberttlange.github.io/posts/2020/03/blog-post-10/)
图像来源包括:
如果你觉得这篇文章有用,请给它鼓掌,它会有很大的帮助!!
此外,欢迎任何建议/更正。
编码快乐!!
创建增强的 NYT 新冠肺炎数据集
在 BigQuery 中添加人口统计、选举和移动信息
美国人口密度(累计人口桶的 1%)随时间变化的 Covid 感染。互动版。
编者注: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
我已经在谷歌云工作了十个月,但对我们的产品没有任何经验,所以当我读到谷歌将 COVID 数据,包括纽约时报的县级数据,添加到他们的公共 BigQuery 数据集时,这似乎是一个学习工具如何工作的好机会。(声明:这是我用自己的时间做的一个项目,与谷歌无关。)
在疫情期间,我一直非常焦虑,我处理焦虑的主要应对机制是试图理解让我焦虑的事情。当我在 3 月中旬开始这个项目时,关于该病毒的深度分析来源要少得多,我希望能够提出原始数据的问题,提出并测试假设,以更好地了解它的传播以及对公共卫生政策和我自己行动的影响。
我也想学习一些新东西。
不要使用这个数据集
如果你正在寻找一个可靠的,经过测试的,高质量的美国新冠肺炎人口统计数据集,这不是那个数据集。我认为这很棒,但这是我自己在业余时间做的,作为学习练习和自己使用。使用由一组人构建的数据集,经过实际训练、自动化正确性测试、更好的度量测试和更多的采用,你会好得多。
我为什么写这篇文章
我认为分享我构建这样的报告模式的新手经验可能对其他人有用或有趣,就像我在这样做的过程中阅读的许多文章对我有帮助一样。
该项目需要学习使用 GIS 和 SQL 几何图元,如何将人口普查局的数据放在一起,以及处理该数据的复杂子集的一些策略,例如城市边界和部分县。我希望这些策略和工具对其他正在学习操作类似数据集的人有用。
如何找到这个你不应该使用的数据集
我已经在 GitHub 上签入了一些文件,可以用来重新创建报告模式,如果你对此感兴趣的话。我故意不进行任何完整性测试或安装自动化。不是我懒,是别的什么原因。
如果你对数据集本身感兴趣,它是通过 BigQuery 公开获得的。我大约每六个小时物化一对表,但是如果您想要最新的数据,您最好查询我的视图并物化您自己的表。
NYT 县数据
有几个由县和州报告的原始数据的数据集。起初,我很快为 COVID 跟踪项目的数据构建了一个 BigQuery 导入器,该项目最初是大西洋的一个项目。这是一个在州一级广泛使用的数据集,跟踪住院和测试以及感染和死亡。
然而,在我看来,状态内的变化比状态间的变化更重要。这是一种疾病,至少在开始时,对人口稠密的城市地区的影响比人口不稠密的农村地区要大得多,而且由于增加其传播的因素,这种疾病似乎有可能继续存在。
除了密度问题,50 个左右的样本数量很少。它代表了更大数量的县报告的集合,并且信息在该集合中丢失。
我想要我能找到的最细粒度的数据,在美国,这意味着分别查看每个县的报告数据。
我希望能够查看关于地理位置的县级数据,以及人口和地理数据,如总人口和土地面积,我希望将这些数据加入到麻省理工学院的选举数据和谷歌的新冠肺炎移动性报告中,以便我可以寻找移动性和疾病传播之间的关系,以及移动性数据的党派关系。
我选择纽约时报的数据有几个原因:
- 它已经在 BigQuery 中可用了。
- 它是基于某种程度的现场报道,我发现这些年来《纽约时报》是一个普遍值得信赖的信息来源。
- 当时大多数仪表盘都使用约翰霍普金斯大学的数据,该大学有更多的指标系列,但当时 BigQuery 中没有县级数据,我也有点逆向思维。
我希望能够做的事情之一是比较来自不同数据集的数据,看看它们有多少一致,所以我的计划是最终与所有这些数据一起工作。我决定先从容易获得的东西开始。
NYT 数据特征
如果你打算使用 NYT 的数据,你应该去 GitHub 上的源,仔细阅读,并完全理解作者对数据集的描述。但简而言之:
NYT 的数据集非常简单。
有三个维度字段,date
、state_name
和county
,用于标识数据适用的美国县。还有另一个字段county_fips_code
,它给出了一个唯一的 FIPS 代码,这是一个广泛使用的用于识别县的标准值。
该数据包含所有 50 个州以及美国领土的数据。
还有县名为“未知”的行。这用于记录在州或地区级别报告的死亡人数,但 Times 无法将其分配到县。这些行没有 FIPS 代码,在我的数据集中,它们没有填充任何来自其他来源的增强数据。
每行只有两个数据值:deaths
和confirmed_cases
。这些给出了该县报告的累计数字。该数据集不提供新的病例或死亡,必须通过与前一天的比较来计算。
这种情况很少发生,尤其是在早期,一个县会一天比一天向下修正对死亡或确诊病例的估计。这导致一天的“新病例”为负。在我的分析中,我通常会过滤掉这些异常情况,或将新案例的下限设置为 0。如果你分析的是日常案例,这不是一个大问题。为了与报告不同情况的其他数据集进行比较,它可能需要一些补救措施。
NYT 地理异常
NYT 数据有几个地理报告例外,这些例外构成了该项目中最具挑战性和最有趣的部分,并为我提供了使用大量基本 GIS 操作和工具的绝佳学习机会:
- 纽约市五个区(纽约、国王、皇后、布朗克斯和里士满县)的所有案件都分配到一个名为纽约市的区域,并且
- 四个县(卡斯、克莱、杰克逊和普拉特)与密苏里州堪萨斯市重叠。我们显示的这四个县的病例和死亡人数只是堪萨斯城以外的部分。堪萨斯市的病例和死亡人数是作为他们自己的一行报道的。
FIPS 电码
从技术上来说,FIPS 代码是一种已被废弃的用于识别县的联邦标准,但仍广泛用于政府之外或旧的数据源中。今天,人口普查局使用一个更新的系统,T2 大地水准面来识别县和其他类型的地方。从大地水准面到 FIPS 码的映射很容易找到。
有两种类型的 FIPS 代码:州和县。
一个州的 FIPS 代码是一个的两位数。加州是 06 年。德州是 48。
一个县的 FIPS 代码是一个三位数字,在州内唯一。许多数据源将这两个代码连接起来,形成一个在全国独一无二的五位数代码。例如,德克萨斯州的特拉维斯县的综合 fips 代码为 48453,因为它位于德克萨斯州(48),而 FIPS 县的代码为 453。
在许多网页和数据源中,这个组合数字被称为“FIPS 县代码”,因此您必须检查该值是带有单独州字段的三位数代码,还是五位数代码。此外,一些源将这些值存储为整数,其他的存储为字符串。编写帮助函数或准备好表达式来在相同数据的这些表示之间进行转换是一个好主意。
人口普查局美国社区调查
每年,人口普查局每年都会调查大约 350 万个家庭。它收集关于祖先、公民身份、教育程度、收入、语言能力、移民、残疾、就业和住房特征的信息,因此它提供了非常丰富的数据源。
ACS 数据特征
结构和内容
像所有人口普查局的数据一样,ACS 调查数据被分成几个依次更小的级别。每个州又分为若干县(或类似县的划分,如教区)。县被分成普查区域,普查区域又被分成区块组,区块组由最小的分区区块组成。街区是一种任意的划分,但在城市中,它通常对应于一个实际的城市街区。
对于每个街区,该局记录他们正在调查的任何事物的实际数量(人、儿童、有色人种、一个年龄组内的人、收入范围内的人、男性、女性等)。)它们还记录了一些无法直接计算的总值,如收入中值、租金中值等。
数据的结构很重要,因为为了解决 NYT 数据中的地理异常,我需要将一些县加在一起以构建一个新的县,并从其他县中减去部分以消除重叠。
缺失数据
另一件需要注意的重要事情是,由于 ACS 数据是一项调查,而不是人口普查,抽样误差在较低的地理层次上成为一个更大的因素。当误差线变得太大时,一些字段将不被包括在内。柱子根本不在那里。举例来说,你无法按种族和年龄对男性进行分类。在堪萨斯城的一些县,我不得不将这些值设置为 NULL,因为它们不存在。
ACS 地理算术
人口普查局的划分有两个非常重要的属性:它们是不相交的,这意味着它们不重叠;它们是综合的,这意味着一个州的所有土地都在一个县中,一个县中的所有土地都在一个人口普查区域中,等等。
总的来说,这些属性意味着,当你把这个县所有街区的总人口加起来,你就得到这个县的总人口。
对我们的目的很重要的是,这也意味着如果你只取一个县(城市的一部分)中的一些区块组,并从该县的总人口中减去它们的人口,那么剩下的就是该县不在所选区块组中的所有部分的人口。
合计计算值(*均值、中位数和四分位数)
有了中值,就有点复杂了。你不能用一个县的收入中值减去一个城市的收入中值来计算这个县其他地方的收入中值:你很可能得到一个负数!
同样,一个县(或一个州的县,等等)中的区块组的中值总和。)不会给出该县的中值。
在这些情况下,为了估计中位数的合计值等。我取了中间值的人口加权*均值。例如,我在纽约州的纽约、国王、皇后、布朗克斯和里士满县使用SUM(median_income*total_pop)/SUM(total_pop)
来估算 NYT 数据中报告的“纽约市”的收入中值。
由于美国的收入分布如此不均匀,这种人口加权*均值不如使用基于“帕累托分布”的估计精确,但根据 randos 在互联网上的说法,它们在大多数情况下似乎相当接*,而且我有比学习如何编写这样的估计函数更有趣的事情要做。如果有人想这样做,欢迎你!
美国县地理度量和形状数据
BigQuery 拥有公共数据集,其中包含我想要的数据的两个县和区块组属性:以*方米为单位的土地面积和该区域的地理边界。每行包含一个geo_id
字段、一个county_fips_code
字段和一个state_fips_code
字段,用于识别县或区块组,对于从 ACS 数据中的geo_id
字段映射到其他数据集使用的 FIPS 代码至关重要。
地理数据是多边形的形式,其中每个点被映射到地球的椭球体模型上的精确纬度和经度。这种数据有几种表现形式。默认情况下,如果您选择一个地理字段,它将显示 WKT 表示:
*POLYGON((-85.852932 32.475392, -85.853041 32.475265, -85.853711 32.475087, -85.853876 32.475043, -85.854102 32.474983, -85.854492 32.474879, -85.854615 32.474846, -85.854879 32.474887, -85.855306 32.474953, -85.855697 32.475028, -85.855797 32.475053, -85.856138 32.475139, -85.856702 32.475297, -85.856804 32.475326, -85.856873 32.47535, -85.857654 32.475617, -85.860029 32.476349, -85.861159 32.476446, -85.862489 32.476404, -85.862771 32.476263, -85.863495 32.475749, -85.86442 32.475496, -85.865332 32.475246, -85.866949 32.47506, -85.86719 32.475012, -85.870686 32.474311, -85.872695 32.473869, -85.874495 32.472924, -85.874783 32.472225, -85.874744 32.471279, -85.874726 32.470837, -85.874521 32.469859, -85.874504 32.469366, -85.874518 32.469039, -85.874609 32.468884, -85.874787 32.468792, -85.874887 32.468809, -85.874957 32.46828, -85.875196 32.468105, -85.875958 32.467548, -85.87736 32.466318, -85.878192 32.465182, -85.878512 32.464157, -85.878597 32.462615, -85.878571 32.460794, -85.87857 32.460749, -85.878545…*
Google 有一个演示工具, BigQueryGeoViz ,它将在地图上呈现查询结果形状,并允许您使用您可以指定的其他属性对它们进行着色和着色。这个工具非常方便,即使有点基础,也是我在构建数据集时唯一使用的 GIS 可视化工具。
BigQuery GIS 功能
我不会在 BigQuery 的 GIS 功能上花太多时间。我建议查阅文档和入门指南。它们足以…让我开始。
但是,我将简要地提到 BigQuery 提供的在构建这个数据集时必不可少的函数:
- ST_UNION 及其聚合函数 ST_UNION_AGG ,获取一个形状列表并返回一个包含所有形状中每个点的新形状。我使用此函数将块组加在一起,以获得密苏里州堪萨斯城的*似版本,并通过将组成纽约市的所有县合并来获得纽约市的形状。
- ST_DIFFERENCE 和 ST_DIFFERENCE_AGG 与 UNION 相反:它接受两个形状,并返回从第一个形状中减去的第二个形状,以便移除任何重叠区域。我用它来“修剪”堪萨斯城的部分区域。
- ST_INTERSECTS 接受一个形状和第二个形状或点,如果第二个参数部分或全部位于第一个参数内,则返回 TRUE。例如,这允许您获取纬度和经度,并运行一个查询来告诉您它在美国的哪个县!理论上很简单,实际上却很神奇。
- ST_COVERS 如果给出的第二个形状完全在给出的第一个形状内,则返回 true。我用这个来确定哪些街区群完全在密苏里州坎萨市的边界内。
纽约市
纽约是一个相当简单的例子。NYT 报道了一个名为“纽约市”的县,它涵盖了五个真正的纽约县——纽约、金斯、昆斯、布朗克斯和里士满。
为了解决这个问题,我只需要汇总这五个县的人口普查数据和几何数据。
纽约、国王、皇后、布朗克斯和里士满县
密苏里州堪萨斯城
修复堪萨斯城更难。《纽约时报》对密苏里州堪萨斯市的报道就好像它是一个县一样,而没有报道发生在那里的死亡和感染病例。)组成自治市。
堪萨斯城都会区横跨两个州:
堪萨斯城市区和它重叠的四个密苏里县。
堪萨斯城的堪萨斯部分报告了它所在县的死亡人数,所以我只需要纠正密苏里部分。
我需要建立一个“虚构的”县,覆盖堪萨斯城的密苏里州部分,而不是聚合多个真实世界的县:
然后我需要"减去"每个县也是堪萨斯城的一部分:
当我完成时,我将有一组五个县:四个“真正的”县,堪萨斯城被剔除,第五个县,“堪萨斯城”。
建造一座堪萨斯城
人口普查局的数据与城市边界不一致,因为这些边界一直在变化。但是它有人口普查区域和街区组,我们可以用它们来做一个大概的比对。
我通过从big query-public-data:utility _ us . us _ cities _ area获取堪萨斯城和密苏里州的big query-public-data:utility _ us . us _ cities _ area行的 ST_INTERSECTION,获得了堪萨斯城 Missiour 部分的轮廓
很难完全自动地找到一组接*堪萨斯城面积的区块组。如果我在块组之间使用 ST_INTERSECT,我会得到大量在城市之外的块组:
ST_INTERSECTS 结果,堪萨斯城的边界用黑色显示。
另一方面,如果我使用 ST_COVERS,我会得到一个欠计数:
堪萨斯城边界为黑色的 ST_COVERS 的结果。
为了得到更好的*似,我从 BigQueryGeoViz 中的 ST_INTERSECT 集合开始;然后,我单击我想要删除的每个块组,并将其粘贴到查询中的排除列表中。这花了一点时间,但最终结果非常接*我想要的:
这种方法的好处是可以很容易地重新加载查询,并随时检查我的工作。
一旦我找到了一组包含我的密苏里州堪萨斯城的虚拟“县”的块组,我就用上面的算法估计 ACS 数据。我通过调用 ST_UNION_AGG 为它创建了一个 GIS 边界,将所有的 blockgroup 形状放在一起。
修剪密苏里郡
一旦我有了一组接*堪萨斯城的区块组,我就用它来调整组成它的四个县,减去也是堪萨斯城一部分的部分。
为此,我先将 ACS blockgroup 数据加入到人口普查局 blockgroup 数据中,其中包含该县的 FIPS 县代码。我通过 FIPS 码对块组进行分组,然后将它们聚合,如上所述。这为我提供了每个县的一行,其中有该县所有区块组的总人口。我使用 ST_UNION_AGG 来计算这些块组聚合的形状。
组成堪萨斯城的街区,按郡分组。
然后,我使用上面详述的技术,从整个县的值中减去该县的堪萨斯城部分,基本上就完成了!
加入数据
现在我已经合成了我需要的县数据,我需要将它们加入到 NYT 数据中。因为其他县是根据 FIPS 代码连接的,而该字段对于合成县是空白的,所以这是自然的选择。
我想避免真实县和我的合成县之间的 FIPS 代码冲突。这很简单:“真正的”fips 代码长度从不超过五位数。只要我的合成 FIPS 码的低五位数字是“0”,它们就不可能与真正的 FIPS 码混淆,因为“00”不是有效的州 FIPS 码,“000”也不是有效的县 FIPS 码。
因此,我将“1000000”分配给“纽约市”,将“2000000”分配给堪萨斯市,并将“000000”添加到我所修剪的每个县的 FIPS 代码中——例如,将“29095000000”添加到密苏里州的安德鲁县。
然后,我可以通过在 NYT 数据上创建一个视图来修复这些县的空白 FIPS 代码。NYT 数据中的县有一个县 FIPS 代码使用该代码,但如果为空,则州是“纽约”,县是“纽约市”,则 FIPS 代码是“1000000”,依此类推。
构建和修复数据集的每一步都会创建一个新视图,该视图构建在旧视图之上。最初,我为中间步骤实现了表,但是这不必要地消耗了空间,并且在查询数量和数据更新数量都很少的环境中会对性能产生负面影响。NYT 数据每天更新,ACS 数据不变,县地理数据每年更新一次。
相反,我每天物化一个大的报告表,在这个过程的最后,使用像 Google 的 Data Studio 和 BigQuery GeoViz 这样的工具。大多数情况下,我实际上并不使用这个表,但稍后会更多地使用它。
添加派生指标
NYT 的原始数据只包括每个县每天报告的累计死亡和病例。我想做的第一件事是计算一些基本的增量和加速度:
- 计算每日增量死亡和确诊病例。
- 计算 7 天内新增死亡和病例数。
- 计算 7 至 14 天的新增死亡和病例数。
- 用 2 除以 3 得到周环比加速度。
我首先计算(1)和(2)使用滞后超过分区,然后在一个单独的视图中划分这些值得到加速度。
我多次执行相同的步骤(2-4 ),时间间隔为一周,这样我就有了 4 周前的新案例,以及 3 个两周一次的案例加速值。
截至 2020/06/18,过去 7 天各州新增病例的旭日图。一个州的楔形大小是它在美国过去 7 天的病例总数中所占的比例。一个州的颜色反映了该州一周内案件的增长速度。互动版。
使用数据
起初,我在谷歌的数据工作室和 Tableau 中使用这个数据集,进行基本的数据探索,并验证我可以在数据中看到我期望寻找的东西。就其本身而言,这是有用的,并且建立了我对数据集的信心,但最终这些工具允许你在不学习编码的情况下快速构建可视化。
不过,问题是,我已经知道如何编码,知道如何编码可以让你节省一些功能和数字处理,以后再回来用有趣的方式组合它们。它还允许您构建更加复杂的可视化。
这些天,我使用 BigQuery 的 iPython Magics 定期将这个数据集刷新到 Jupyter 笔记本中。在那里,我使用 Pandas 来获得新的度量标准并转换数据。
我一直在使用 Pandas 内置的 Matplotlib 集成来进行快速和肮脏的可视化,并使用 Plotly 来创建交互式 Javascript 情节。
使用各县的 GIS 边界,我可以在 Plotly 中创建 chloropleth 地图,以显示各县的各种地理指标。
我不太相信地理地图能达到这个目的。它们是陆地地图,病毒攻击人类。在美国,人口分布非常不均匀——20%的县拥有 80%的人口——地图通常不能很好地反映问题。
显示前一周 0-7 天追踪病例比率的美国氯普图。许多县没有这方面的足够数据,并且是空白的。
预计堪萨斯市和地区的 0-14 天案例加速。
纽约市 synthetic county 的 0-14 天案例加速。
我还很高兴地看到,我的堪萨斯城及其周边地区的几何图形与《泰晤士报》上的克罗珀特地图非常吻合:
纽约时报堪萨斯城地区的地理位置。
我还可以使用 ACS 数据来确定有色人种百分比较高的县,这些人死于该疾病的风险较高,如 5/1 的总病例树图所示:
然而,到目前为止,加入这些数据的最大优势来自两个领域:total_pop
来自 ACS,area_land_meters
来自 GIS 数据。用第一种方法你可以得到人均感染率和死亡率,用这两种方法你可以得到人口密度。
其他数据集
我还创建了映射表,可用于将这些数据连接到谷歌的新冠肺炎移动性报告数据,以及麻省理工学院的县级选举数据。到目前为止,将 NYT 的数据与移动性数据联系起来还不是很有成效,但是将每个数据分别与县地理、选举和 ACS 数据联系起来给了我很多有用的见解!
结论
我希望这对其他人有所帮助。如果是这样,或者如果你有一个项目,我可能会有用的贡献,让我知道!
使用数据科学家工具包创建考试档案系统
利用 R Shiny 更好地准备考试
随着新冠肺炎疫情中关于大学生晋升标准的不确定性的增加,我决定创建一个集中的*台来聚合来自学生自己的考试资源。
动机:
对任何人来说,每天更新的与新冠肺炎相关的新病例和死亡都是令人痛苦的。然而,大学生不得不处理过多的额外问题,从被取消的工作机会到下学期晋升的不确定性。这也是我的队友们越来越焦虑的一个原因。所以,这个项目是我决定尽自己的一份力量来帮助我的同学和大学同学的原因。
向未知领域展示 R Shiny:
我最*被谷歌选中参加一个著名的暑期项目,该项目隶属于统计计算的 R 项目。我的项目基本上包括开发一个 R 包,让你的 R 代码更加高效。因此,我认为用 R Shiny 进行这个项目可以让我对我的夏季项目有更多的优化想法,因此我选择了 R Shinydashboard 。
正如你可能已经意识到的,R Shiny 主要用于创建仪表板,并在数据分析、争论、操作等之后,交互式地交流你的发现。然而,我的项目是一个典型的 web 开发项目,几乎没有任何与数据相关的元素。尽管如此,我还是继续进行下去,以便为我的夏季奖学金项目发现新的优化想法。
使用这种方法,我必然会面临许多挑战,我将在接下来的章节中讨论这些挑战。
编码策略:
通常一个 R Shiny 项目被分成两个文件,一个 ui。r 文件和一个服务器。r 文件。但是,名为 app 的单个文件。也可以使用 r。
ui。稀有
- 在侧栏面板中,我首先列举了该应用程序的所有预期功能,包括上传和下载试卷的 功能,这些试卷的解决方案和课堂笔记 。
- 我还包含了" shinyWidgets "库,这样我就可以在成功上传文件时使用" SweetAlert "以及"downloadbtn"与默认的 R Shiny "download button"相比,它提供了更多的定制选项
- 我使用 fluidRow 框创建了几个输入字段来收集关于正在上传的论文的信息。我还使用了
fileInput()
函数的“ 接受 ”参数,只接受 PDF 文件。 - 我从“字体真棒”网站上选择了图标,并在“关于”部分使用了
class = “fa-spin”
来吸引人们的注意。
代表性代码,完整的代码向下滚动到结论
服务器。稀有
- 为了持久存储上传的 PDF 文件,我不得不使用一个包含“ pdftools ”库的解决方案。
- 选择学期后,我希望科目选项仅限于该学期的科目。这是我用助手函数和
req()
函数实现的。 - 在论文/笔记等成功上传后,我想发送一个成功通知,这是我通过包含“ shinyWidgets ”库和使用
observeEvent()
代码块实现的。 - 为了从我的本地存储器(我的“www”文件夹)开始下载,在“download handler”中,我将
filename
参数转换成一个函数,并用file.copy()
函数调用了content
参数中的那个函数。
代表性的代码,完整的代码向下滚动到结论
完整应用程序的片段:
web 应用程序的登录页面
上传成功后的温馨提示
下载选项以及所选解决方案的可用选项
结论:
所以,如果你对这个特别的 R Shiny web app 的代码感兴趣,点击这里。
如果你有兴趣看到这个网络应用的现场和部署版本,点击这里。
鉴于 Shiny 的内在能力,这款网络应用对移动用户的响应也足够快。如果你喜欢我的这个项目,看看我之前创建的仪表板这里。
在从事这个项目的过程中,我发现了一些棘手的问题,我将在以后的文章中列举这些常见的陷阱以及如何避免它们。所以,请继续关注,稍后见。
你也可以给我买杯咖啡来支持我的工作。
谢谢你,祝你成功。
用 Jupyter 笔记本创建一个交互式仪表板
…以及如何将仪表板部署到 Heroku
随着 PowerBI 和 Tableau 等 BI 工具的出现,当你想创建一个交互式仪表板时,Jupyter Notebook 可能不会是你脑海中出现的第一个工具。然而,由于 Voila,现在可以直接从 Jupyter Notebook 构建交互式仪表板。
在本文中,我想向您展示如何使用 Jupyter Notebook 创建交互式绘图,将它们转换为独立的仪表板,然后使用 Heroku 将其部署在云上,以便其他人可以看到您的仪表板。所以,让我们开始吧!
加载数据
在本文中,我将使用旧金山数据集。该数据集包含 2016 年发生在旧金山的超过 150,000 行犯罪历史。
像往常一样,我们可以用熊猫加载数据集。为了了解数据集的样子,让我们打印出数据集的前五行。
因为这篇文章的目的是展示如何使用 Voila 将 Jupyter 笔记本中的交互式可视化变成独立的仪表板,所以我不打算对这个数据集进行 EDA。让我们开始创建小部件,为这个数据集的可视化添加交互性。
创建交互式小部件
现在我们已经加载了数据,我们可以立即开始创建小部件。这些小部件是为我们的可视化添加交互性的基本要素。在本文中,我们将使用三个部件:一个滑块部件和两个多选部件。要创建这些小部件,我们可以使用 Jupyter Notebook 的ipywidgets
库。
我们要创建的第一个小部件是 slider 小部件。为此,我们可以使用来自ipywidgets
的IntSlider()
属性。这个 slider 小部件将控制熊猫应该加载多少行数据集。例如,如果滑块值是 1000,那么 Pandas 应该加载数据集的前 1000 行。下面是它的代码实现。
import ipywidgets as widgets
import pandas as pdstyle = {'description_width': 'initial'}limit_case = widgets.IntSlider(
value=1000,
min=100,
max=5000,
step=1,
description='Max Number of Case:',
disabled=False,
style=style)
在上面的代码中,我们从ipywidgets
向IntSlider()
属性传递了几个参数。第一个是value
,这是我们运行代码时会显示的默认值。接下来,min
和max
是滑块中可以考虑的最小和最大范围值。同时,step
是我们上下移动滑块时的增量或减量。最后,我们添加了style
参数,这样description
中的单词就不会被截断。
接下来,使用来自ipywidgets
的interactive()
属性,我们可以将小部件与我们想要交互更改其值的变量链接起来。现在,如果我们更改滑块值,我们可以看到数据集的长度也会相应地更改。
如果您运行上面的代码单元,您将得到一个类似这样的交互式滑块小部件。
接下来,让我们创建第二个小部件,它是多选小部件。我们可以通过使用ipywidgets
中的SelectMultiple()
属性来实现这一点。有了这个小工具,我们可以选择只在特定的选区而不是所有的选区可视化犯罪。为了创建这个多选小部件,下面是代码实现。
import pandas as pd
import ipywidgets as widgets
from ipywidgets import Layoutdf = pd.read_csv('SF_crimes.csv')unique_district = df.PdDistrict.unique()district = widgets.SelectMultiple(
options = unique_district.tolist(),
value = ['BAYVIEW', 'NORTHERN'],
description='District',
disabled=False,
layout = Layout(width='50%', height='80px', display='flex')
)
在上面的代码中,我们使用SelectMultiple()
属性来选择区域变量的多个值。我们应该指定的第一个参数是options
,它应该包含变量的可用选项列表(在我们的例子中是不同种类的旧金山地区)。下一个是value
,它应该包含我们希望默认显示的变量值,然后description
是用于描述小部件名称的文本字段。
如果您运行上面的代码单元格,您将得到下面的交互式多选小部件。
最后,我们可以创建第三个小部件,它与前面的多选小部件完全相同。这个小部件的目的是让我们能够选择想要可视化的犯罪类别。下面是这个小部件的代码实现。
import pandas as pd
import ipywidgets as widgets
from ipywidgets import Layoutdf = pd.read_csv('SF_crimes.csv')unique_cat = df.Category.unique()style = {'description_width': 'initial'}category = widgets.SelectMultiple(
options = unique_cat.tolist(),
value = ['VANDALISM', 'ASSAULT', 'ROBBERY'],
description='Criminal Case',
disabled=False,
style=style,
layout = Layout(width='50%', height='80px')
)
我们传递给这个SelectMultiple()
属性的参数与之前相同,只是value
和options
参数是犯罪类别而不是地区。
如果您运行上面的代码单元格,您将得到下面的小部件。
接下来,我们希望将之前创建的所有三个小部件结合起来,创建一个交互式可视化。为了可视化犯罪地点,我们可以用地图来可视化它们,因为我们在数据集中有关于纬度和经度的信息。
为了将数据集可视化成地图,我们可以使用folium
库。如果还没有安装folium
,可以使用 pip 命令安装。
pip install folium
我们可以将地图与小部件集成在一起,这样当我们用小部件做出不同的选择时,可视化就会相应地调整。
首先,slider 小部件的值将决定我们应该在可视化中考虑的数据集中的犯罪数量。接下来,地图应该会根据我们使用多选小部件的选择显示犯罪类别和地区。
为了创建额外的可视化,我们还可以创建两个条形图。一个条形图显示有多少犯罪基于我们使用小部件选择的类别。另一个图表显示了我们使用小部件选择的地区有多少起犯罪。
最后,我们需要定义一个函数,将所有三个小部件与我们的地图可视化和两个条形图集成在一起。
下面是从头到尾完成所有这些工作的完整代码实现。
现在,如果我们调用上面 Jupyter 笔记本的最后一个代码单元中的函数update_map
,我们将根据我们在所有三个小部件中选择的值获得地图和条形图可视化的交互性。
现在我们已经完成了 Jupyter Notebook 的工作,在ipywidgets
库的帮助下创建交互式可视化效果!
用 Jupyter 创建一个仪表板
到目前为止,我们已经在ipywidgets
、matplotlib
和folium
库的帮助下创建了交互式可视化。现在,你可以开始向其他人展示你的 Jupyter 笔记本中的可视化。
然而,在大多数情况下,您将向非技术人员展示可视化,这些人对查看您笔记本中的大量 Python 代码不感兴趣,只想看到清晰的可视化。因此,使用 Jupyter 笔记本来显示交互式可视化并不是最佳选择。
要将 Jupyter 笔记本上的可视化转换为独立的仪表板,我们可以使用 Voila。现在,如果您还没有安装 Voila,您可以使用 pip 命令安装它,如下所示:
pip install voila
一旦你安装了 Voila,创建一个独立的仪表板将变得非常容易。您所需要做的就是进入您的终端或 Anaconda 提示符,然后键入以下格式:
voila path/to/your/notebook.ipynb
或者,您也可以通过单击 Jupyter 笔记本工具栏中的 Voila 图标,直接在笔记本中执行此操作。
现在,如果您执行了上述两个步骤中的任何一个,您将获得以下独立的仪表板。
就是这样!我们已经将 Jupyter 笔记本中的可视化内容转化为一个独立的仪表盘。现在,使用同样的技巧,您可以将 Jupyter Notebook 中任何数据集的可视化转换为独立的仪表板。
使用 Heroku 部署仪表板
到目前为止,您用 Voila 构建的仪表板只能在本地计算机上运行。这意味着你实际上不能和其他人分享,除非你把你的电脑借给他们。
为了让其他人访问您的仪表板,您需要将您的仪表板部署在云上,例如 AWS、GCP、Azure 或 Heroku。在本文中,我们将使用 Heroku 来部署我们的仪表板,以便其他人可以用他们自己的计算机看到您的仪表板。
使用 Heroku 部署您的仪表板的第一步是登录您的 Heroku 帐户。如果你还没有账号,你可以免费注册 Heroku。
下一步是下载 Heroku 命令行界面(CLI) 并安装到您的本地机器上。
Heroku CLI 安装后,现在您需要在 Jupyter 笔记本文件旁边创建两个附加文件。这些文件是:
- requirements.txt: 这个文本文件应该包含您在 Jupyter 笔记本中使用的所有依赖项或库。例如,在本文中,我们使用了
folium
、numpy
、pandas
、ipywidgets
、matplotlib
和voila
库。因此,我们需要在这个文件中列出它们。另外,由于我们使用 Jupyter Notebook,我们需要在这个文件中添加ipykernel
,这样 Heroku 就可以运行我们的 Jupyter Notebook 中的命令。下面是 requirements.txt 内容的样子。
numpy
pandas
folium
ipywidgets
matplotlib
voila
ipykernel
- Procfile: 这个文件告诉 Heroku 如何运行您的 Jupyter 笔记本,以及需要哪些文件。您可以键入以下内容作为该文件的内容。
web: voila --port=$PORT --no-browser --enable_nbextensions=True your_jupyter_notebook_name.ipynb
您可以使用电脑中的任何文本编辑器创建这两个文件,例如 Notepad 或 Notepad++。接下来,将这些文件保存在保存 Jupyter 笔记本文件的同一文件夹中。
现在打开您的终端或命令提示符,然后在其中键入heroku login
。这将引导你进入一个浏览器页面,提示你使用 Heroku 帐户登录。
接下来,使用命令提示符进入保存 Jupyter 文件、requirements.txt 和 Procfile 的工作目录。
在工作目录中,通过键入git init
初始化一个空的存储库。接下来,您可以通过键入以下格式在 Heroku 帐户上创建一个新的应用程序:heroku create your-app-name
。
创建应用程序后,按顺序输入以下命令:
git add .
git commit -m "Your customized message"
git push heroku master
现在,在你输入git push heroku master
之后,Heroku 将开始创建应用程序来托管你的仪表板。大约一分钟后,您会在命令提示符下看到一个指向您的仪表板的链接。该链接将与您的应用程序名称相似。
就是这样!现在,您可以与其他人共享该链接,以便他们可以看到您的交互式仪表板。
您可以在这里看到本文中部署的可视化仪表板。
如果您有兴趣查看本文中使用的 Jupyter 笔记本和 requirements.txt 以及 Procfile,您可以在这里看到它。
使用 D3 . js——数据处理,为“办公室”创建交互式数据可视化
为创建交互式可视化准备数据
编辑描述
cuthchow.github.io](https://cuthchow.github.io/the-office-visualisation/)
我为这个项目创作的一个视觉效果是,一季一季地描绘剧中每个主要角色的情绪得分。
我一直认为,最酷的数据可视化是那些你可以与之互动和玩耍的数据,因为它们给你自己探索数据的自由,并从你自己的角度理解它。我还发现,我在网上看到的许多符合这一描述的可视化是在 D3.js 的帮助下创建的,D3 . js 是由 Mike Bostock 创建的 Javascript 库,其特定意图是为 web 创建高度可定制和交互式的可视化。
因此,我决定学习 D3.js,以便创造我自己的观想。由于我非常支持基于项目的学习,以便更快更深刻地理解一个主题,我决定将电视节目《办公室》中的所有台词可视化,这实际上结合了我最大的两个兴趣。我在这里的目标是记录整个过程,并分享我在这个过程中学到的一些东西,希望也能帮助任何试图学习 D3.js 的人。
数据处理
这第一篇文章将关注我在创建实际可视化之前所做的数据预处理。我做的所有预处理都是在 Jupyter 笔记本中使用 Python 和 Pandas 完成的,还有一些其他用于特定任务(如情感分析)的库。
我开始使用的数据集包含了《办公室》中每一句台词的信息,以及关于季节、剧集、场景、演讲者以及场景是否被删除的信息。
我从一开始就知道我想在我的观想中包含以下信息:
- 口语线长度
- 台词的感悟
- 文本的词汇复杂性
为了得到每一行的长度,我简单地取了 line_text 列,在每个空格处将字符串分成一个列表,并计算结果数组的长度。
import pandas as pddf = pd.read_csv('the-office-lines.csv')
df['word_count'] = df['line_text'].apply(lambda x: len(x.split(' ')))
为了获得每行的情感,我不得不利用自然处理库“NLTK”,它有几个预先训练好的模型,适合分析情感的任务。(对于未来的项目,我的目标是使用“The Office”语料库从头构建一个语言模型,以生成更准确的情感分数,但由于这个项目是关于数据可视化的,我满足于使用 NLTK 的内置模型。)NLTK 提供了 VADER 情绪分析器供我们使用,它提供了一个从-1 到 1 的复合情绪分数,其中-1 表示绝对负面,1 表示绝对正面。
from nltk.sentiment.vader import SentimentIntensityAnalyzer
sid = SentimentIntensityAnalyzer()df['sentiment'] = df['line_text'].apply(lambda x: sid.polarity_score(x)[0]#The 0th element of the polarity scores gives us the compound score, whereas the 1st, 2nd and 3rd element return the negative, neutral and positive scores respectively
最后,我想分析每一句口语的词汇复杂性。谢天谢地,这种测试已经存在了。我选定的一个是 Flesch-Kincaid 可读性测试,它基本上返回可读性或等级分数,对应于大致的学校等级难度。分数是基于每个单词的*均音节数和每个句子的*均单词数,所以这是一个相当初级的工具。
弗莱施-金凯试验
由于它的简单性,它使得实现起来相当简单。然而,应该注意的是,这个测试是针对书面语言的,而电视节目的台词是针对口语的。
此外,我意识到对每一行都进行测试并不是最好的方法,因为测试在更大的文本上效果更好。因此,我为每个关键人物创建了一个单独的语料库,然后对每个单独的语料库应用测试,以确保返回的分数尽可能准确。
创建单独的搜索集:
df = df.groupby('speaker').count().reset_index()
chars = list(df[df['scene'] > 100].speaker)#chars is the list of characters with over 100 scenes, as I wanted to remove the characters with only a handful of appearances from the dataset char_corpi = dict()for char in chars:
corpus = []
for row in df[df.speaker == char]['line_text']:
corpus.append(row)
corpus = ' '.join(corpus)
char_corpi[char] = corpus#char_corpi is a dictionary of corpuses, where the key is each character, and the value is a string of all their lines in the show.
创建和应用 Flesch-Kincaid 测试:
import re def flesch_kincaid_grade(corpus):
words = len(corpus.split(' '))
sents = len(re.split('! |? |. ', corpus))
check = lambda x: 1 if x in 'aeiouyAEIOUY' else 0
sylls = sum(list(map(check, corpus)))
score = 206.835 - 1.015 * (words/sents) - 84.6 * (sylls/words)
return score kincaid_scores = []for char in char_corpi:
score = flesch_kincaid_grade(char_corpi[char])
kincaid_scores.append({'speaker': char, 'score': score})df = pd.toDataframe(kincaid_scores)
df.to_csv('kincaid_score.csv', index = False)## This is the new set of data which I will use directly in the visualisation
结论
这就是我为这个项目做的所有数据预处理。相当简单,对吗?
使用散景和熊猫在 Python 中创建交互式地图
使用 Python 来(相对)容易地创建一个交互式热图,按位置显示鳄梨价格。
作者图片——跟随这篇博文,自己制作。互动版这里。
这篇文章可以看作是我之前处理鳄梨价格数据的文章的延伸(我能说什么呢,我爱鳄梨!)你可以在这里找到。推荐去看看!
作为数据分析师/数据科学家/其他数据操纵者,我们经常发现自己使用可视化来有效地向他人传达我们的发现(并让自己更好地理解它们)。如果使用得当,可视化有很多优势——它们可以总结数据、显示时间趋势、显示地理数据等等。
有时静态的可视化是完美的,但在其他情况下,创建一些交互的东西会更清晰,对最终用户更有用,或者更酷更有趣!
在本文中,我们将介绍创建显示美国鳄梨价格的交互式热图的过程,该热图可以在任何现代 web 浏览器中轻松查看和操作。我们将使用 Python 来完成,主要使用散景和熊猫 数据帧。
在看到纳丁·阿梅西-博尔顿的优秀项目“预测国王郡的房价”后,我受到启发开始使用散景,该项目非常有效地使用了散景。我建议把整本笔记本都看看——里面有一些非常有趣的东西。
入门指南
首先,我们需要一些 Python dict 形式的地理数据、熊猫数据帧或熊猫 GroupBy 对象。方便的是,我碰巧有一个旧的数据框架,我将在这篇文章中使用它。我在我的上一篇文章中生成了这个文件,并使用 pd.to_csv() 将其导出。如果您对我为达到这一点所做的数据争论感兴趣,请查看一下。
这篇文章的所有代码都可以在这个 GitHub repo 中找到,如果你想在不安装任何东西的情况下进行编码,那么使用活页夹 查看这里。如果你只是想边看边读,我推荐你使用 Jupyter 笔记本浏览器。
为什么要用散景?
Bokeh 可以帮助你简单方便地制作好看且有用的交互式可视化效果,然后可以在所有现代浏览器中显示。这意味着你可以在一篇博客文章、一个内部仪表板或者任何你可以查看 HTML 的地方嵌入一些好的有用的东西。几乎每个人都有使用浏览器的经验,这对于让你的视觉呈现在观众面前非常有用。
我的目的的主要卖点是交互性。在我们的示例中,我们今天使用的是地理地图,因此允许用户随意放大和缩小以查看我们的数据点的物理分布,以及当用户悬停在某个点上时选择突出显示哪些数据的能力,让我们创建了一个直观的可视化,我们的观众可以按照自己的节奏进行交互和操作。这反过来会让他们更容易将我们的发现应用到身上,这对任何数据专业人士来说都是一个优势!
如果您有一个工作的 Python 设置,在您的系统上安装散景相对简单。我自己正在使用 Anaconda 生态系统,所以只需要一个简单的conda install bokeh
就可以了。如果你喜欢 pip,那么pip install bokeh
也可以。更多信息可以在他们的快速入门指南中找到。
它是如何工作的?
散景是建立在四个主要概念上的:地块、字形、引导线和注释以及范围。为了获得完整的信息,我强烈推荐阅读文档(总是阅读文档!),但是我会在这里提供一个非常简短的说明。
- 剧情。这是您将在输出中看到的所有内容的容器。您将创建一个呈现给用户的图形。任何使用过 Matplotlib 或 Seaborn 或 Python 中其他可视化库的人都会熟悉这个想法。
- 象形文字。这就是散景如何显示你的观想。这些可以简单到一条线或一个圆,也可以复杂到像一个多边形,图像 url(很适合在 viz 上使用你的 logo!)或者文字。
- 参考线和注释。引导线是像轴、网格线等帮助用户理解比例和进行比较的东西。注释是像标题、图例等东西,它们提供额外的信息来帮助用户理解可视化。
- 范围。这是数据的范围。你会注意到,如果你点击我的可视化,默认情况下,它会打开所有可见的数据点,以美国为中心(我所有的数据都来自那里)。如果你愿意,这可以在创建可视化时修改。
事实真相
好了,言归正传。
首先,我们想要导入我们的库。Bokeh 与您可能熟悉的其他 Python 可视化库(如 Matplotlib 或 Seaborn )略有不同,因为它被排列为许多不同的子模块,您可以从中导入您需要的函数。它没有一个可以导入来访问所有功能的包罗万象的包。
这确实使了解它变得有点困难,但是您可以遵循下面的代码。习惯了就不太恐怖了!
*from bokeh.io import output_notebook, show, output_file
from bokeh.plotting import figure, ColumnDataSource
from bokeh.tile_providers import get_provider, Vendors
from bokeh.palettes import PRGn, RdYlGn
from bokeh.transform import linear_cmap,factor_cmap
from bokeh.layouts import row, column
from bokeh.models import GeoJSONDataSource, LinearColorMapper, ColorBar, NumeralTickFormatterimport numpy as np
import pandas as pd*
好了,现在我们准备开始了。首先,我们需要一些数据。我们将使用我们的 CSV 文件,我们可以使用 pd.read_csv() 将它作为数据帧读入 pandas:
*import pandas as pddf = pd.read_csv('avocado_df.csv', index_col=0)display(df.head())*
这为我们提供了以下输出:
出于他们自己最了解的原因,散景图使用墨卡托坐标。我们的位置以经度和纬度列出。这意味着我们需要将纬度和经度转换成墨卡托坐标。
参与编程和数据分析这样一项有趣而富有挑战性的活动的一大好处是,对于你遇到的大多数困难问题,其他一些聪明人可能以前也遇到过。为了取得进步和提高你的技能,你还需要发展有效谷歌的技能,找到那些聪明人在你之前找到的答案!
在我们的例子中,我们的开拓者 Nadine Amersi-Bolton 已经解决了这个问题,并为我们创建了一个函数来完成这个转换。这非常好,所以我们将更聪明地工作,而不是更努力地使用这个现有的解决方案。谢谢纳丁。
*# Define function to switch from lat/long to mercator coordinates
def x_coord(x, y):
lat = x
lon = y
r_major = 6378137.000
x = r_major * np.radians(lon)
scale = x/lon
y = 180.0/np.pi * np.log(np.tan(np.pi/4.0 +
lat * (np.pi/180.0)/2.0)) * scale
return (x, y)# Define coord as tuple (lat,long)
df['coordinates'] = list(zip(df['latitude'], df['longitude']))# Obtain list of mercator coordinates
mercators = [x_coord(x, y) for x, y in df['coordinates'] ]*
现在我们只需要将这一列添加到我们的数据帧中,并将其分成两个单独的列(一个用于 x 坐标,一个用于 y 坐标)。我们可以用熊猫通过两个简单的步骤做到这一点。
*# Create mercator column in our df
df['mercator'] = mercators# Split that column out into two separate columns - mercator_x and mercator_y
df[['mercator_x', 'mercator_y']] = df['mercator'].apply(pd.Series)*
当我运行这个程序时,我发现散景非常不想与数据帧中的geometry
列合作。这来自我们在之前的可视化中使用的匀称的文件,这里不需要。我们可以用 df.drop() 来摆脱它。
*df = df.drop(columns=['geometry'] # Examine our modified DataFrame
df.head()*
这给了我们:
看那些墨卡托 x 和墨卡托 y 柱!太棒了。
准备我们的阴谋
现在我们有了开始策划的一切。在创建我们的数字之前,我们需要先做一些准备工作。这些包括:选择用于地图的图块;选择我们的调色板;指示散景使用哪个数据帧作为信息源;创建颜色映射器;并设置我们的工具提示。
别担心,我们会边走边检查的!
首先—选择我们的瓷砖提供商。有各种组织提供可以在散景中使用的地图拼图。这些可能是谷歌地图(很棒,但需要一个 API,所以我们不会在这里使用它)或 CartoDB (也很棒)或 Stamen 。这些大多使用 OpenStreetMap 数据,一般可靠性很高。我们将使用雄蕊碳粉,因为它看起来很棒,单色造型将允许我们的彩色数据点很好地显示出来。
使用雄蕊调色剂制作的西欧地图|由雄蕊设计制作的地图拼贴,在 CC 下由 3.0 制作。数据由 OpenStreetMap 提供,在 ODbL 下。
*# Select tile set to use
chosentile = get_provider(Vendors.STAMEN_TONER)*
接下来,我们需要选择一个调色板。当我们在处理鳄梨时,我选择了紫绿色的 PRGn。我们可以使用PRGn[3]
到PRGn[11]
选择 3 到 11 种色调。我们将使用最大化来获得我们视觉化的最大变化。
*# Choose palette
palette = PRGn[11]*
现在我们想通过使用 ColumnDataSource 将散景指向我们的数据帧。
*# Tell Bokeh to use df as the source of the data
source = ColumnDataSource(data=df)*
接下来我们需要定义颜色映射器。在这种情况下,我们想让散景知道是我们的'AveragePrice'
字段决定了数据点的颜色。
*# Define color mapper - which column will define the colour of the data points
color_mapper = linear_cmap(field_name = 'AveragePrice', palette = palette, low = df['AveragePrice'].min(), high = df['AveragePrice'].max())*
在我们构建图形之前,要做的最后一件准备工作是选择哪些字段应该用作工具提示——也就是说,当用户将鼠标悬停在地图上的数据点上时,应该显示哪些字段。在这种情况下,我们将选择'AveragePrice'
字段和'Region'
,因为它们可能是我们的观众最感兴趣的。
*# Set tooltips - these appear when we hover over a data point in our map, very nifty and very useful
tooltips = [("Price","[@AveragePrice](http://twitter.com/AveragePrice)"), ("Region","[@region](http://twitter.com/region)")]*
创建图形
我们快到了!坚持住。
下一步是定义我们的数字。这个图形是我们所有元素的持有者。我们这样设置它:
*# Create figure
p = figure(title = 'Avocado Prices by region in the United States', x_axis_type="mercator", y_axis_type="mercator", x_axis_label = 'Longitude', y_axis_label = 'Latitude', tooltips = tooltips)*
您可以看到,我们已经为图形指定了标题,将轴类型设置为墨卡托,为两个轴都指定了标签,并告诉 Bokeh 在哪里可以找到关于 tootltips 的信息。所有这些都在一行代码中——不错!
现在我们需要添加图块,否则我们的数据点会漂浮在空白背景上。不是如此引人注目的观想!
*# Add map tile
p.add_tile(chosentile)*
下一步是创建数据点本身:
*# Add points using mercator coordinates
p.circle(x = 'mercator_x', y = 'mercator_y', color = color_mapper, source=source, size=30, fill_alpha = 0.7)*
在这种情况下,我们调整了fill_alpha
,这减少了圆圈的不透明度,并允许用户在缩小时更容易理解重叠的数据点。我们还将圆的大小设置为 30。大小在每一级缩放中都保持不变,所以尝试一下,看看什么适合您的数据。
我们的下一步是创建一个颜色条——这将作为我们可视化旁边的一个图例,提供关于我们的数据点上使用的颜色的含义的重要信息。绿色是贵还是便宜?(贵!)通过给我们的用户这些信息,他们一眼就能明白观想在向他们展示什么。
*#Defines color bar
color_bar = ColorBar(color_mapper=color_mapper['transform'],
formatter = NumeralTickFormatter(format='0.0[0000]'),
label_standoff = 13, width=8, location=(0,0))# Set color_bar location
p.add_layout(color_bar, 'right')*
现在只需几个步骤来确保我们可以在 Jupyter 笔记本中看到可视化,并将文件保存为 HTML 文件,然后我们可以将它嵌入到我们想要的任何地方。
*# Display in notebook
output_notebook()# Save as HTML
output_file('avocado.html', title='Avocado Prices by region in the United States')*
我们到了。我们准备赞美我们的劳动成果!只差一步了:
*# Show map
show(p)*
瞧啊。
辉煌的漫威!放大,摆弄一下!
哒哒!现在,我们已经从一个包含一些位置数据的 CSV 文件变成了一个完全成熟的交互式地图,我们可以将它嵌入到我们的仪表盘、博客帖子或任何我们想嵌入的地方。
我希望这篇文章能给你灵感,让你自己用散景做出一些伟大的东西。
非常感谢您抽出时间和我一起探讨这个话题。一如既往,我非常欢迎您的反馈——您可以在 Twitter 上通过@craigdoedata联系我,让我知道我如何才能更有效地完成这项分析。
你可以在我的网站www . craigdoedata . de上看到我还在做什么。
再说一遍,整个 Jupyter 笔记本和相关文件都可以在我的 Github 页面和活页夹上找到,我鼓励你使用它们。
下次见!
更像这样?访问craigdoedata . de
只用你的数据科学技能创建一个类似 iPhone 的应用程序:一键生活日志链接到 Google Sheets
你用的都是 Python 和 GCP 云函数或者 Heroku。
作者图片
我没有把自己培养成一名移动应用程序开发人员,因此在我的 iPhone 上拥有自己的应用程序超出了我的梦想。但是,一些简单的功能,如我将在本文中演示的功能,可以在没有任何软件开发技能和流程的情况下实现,如 Swift 编程和 app store review。
在这里,我想创建一个简单的功能,比如当你点击一个图标时,它会记录你点击它的时间。
你点击的以下生活日志怎么样,其中没有一个是你想为此建立一个电子表格的?
- 到达办公室时:你一到办公室就查看邮件,所以你忘了写下你到达办公室的时间,对吗?(远程工作人员的情况并非如此)
- 离职时:你*时都在跑步,所以忘了写时间,对吧?(远程工作人员的情况并非如此)
- 喂宝宝的时候:你忘了写下来,因为你*时都是在收拾烂摊子,对吧?(每个人都是这样)
- 当宝宝醒来:战争开始了!我没时间做笔记!(每个人都是这样)
让我们停止把它存储在本地设备上,把它放到云中。这就是我们现在所做的一切。
德里克·欧文斯在 Unsplash 上拍摄的照片
我就是这样想出这个主意的:当你在你的 iPhone(或任何其他智能手机)上点击图标时,它会自动记住时间,并记录在 Google Sheets 中。
在这篇文章中,我将写下我是如何做到这一点的,不需要真正构建移动应用程序,甚至只需要数据科学技能!
目录
- 规划解决方案
- API 解决方案演示 1——谷歌云功能
- API 解决方案演示 2—Heroku
- 结尾注释
规划解决方案
现在这里是我如何规划背景架构。
我们想要的是 iPhone 主屏幕上的图标。轻触其中一个图标会触发某种东西,并以某种方式在谷歌表单中记录下来。不使用特殊的编程技巧,怎么做呢??
没有 CS 技能,如何连接你的 iphone 图标和 Google Sheets?(图片由作者提供)
下面是我是如何作弊的:图标不是 app,是一个特定 HTTP 请求的 Safari 快捷方式。中间出现一个 API,接收 HTTP 调用,最后把记录推送到 Google Sheets。
点击图标-> HTTP 请求-> API 触发器->推送到 Google 工作表。(图片由作者提供)
这个技巧已经解决了大部分技术问题,但是仍然有一个问题:谁以及如何托管 API。
谁将主持 API?(图片由作者提供)
有许多可能的 API 解决方案,但是每一个都有优点和缺点。
每个 API 解决方案的优缺点
通过使用个人服务器,你不得不准备许多,但在配置上有更多的自由作为回报。保持机器一直运行以等待 HTTP 请求的维护成本很高,因此这次对于像我们这样的小应用程序来说是大材小用。
PaaS(比如 Heroku) 是一个可行的选择,可以让你的应用一直保持托管和服务状态。正如我们将在后面看到的那样,设置起来相当容易。实际上,我认为这是所有解决方案中最公*的一个;唯一值得注意的是,Heroku 免费帐户有每月使用限制——所有应用程序每月使用 1000 小时。这相当于 41.7 天,因此对于一个应用程序来说足够了,但当你已经有另一个应用程序在 Heroku 上运行时,这可能就很重要了。
像 AWS 上的 Lambda 或 GCP 上的 Cloud Functions 这样的云端无服务器服务是另一个可能的选择。虽然它是最容易部署的,因为几乎每个基础设施都是托管的,并且您只需要运行 Python 代码,但是每个无服务器服务在配置上都有其不可改变的限制:当您使用外部库(包括 Pandas!);而云函数每个函数只接收来自一个 URL 的 HTTP 请求,这意味着当我们有多个生活事件想要记录时,我们必须复制 Python 代码并反复编辑几行。
在本文中,我演示了使用云函数和 Heroku 的情况。从代码的角度来看,本地服务器案例与 Heroku 案例非常相似。
API 解决方案演示 1——谷歌云功能
要让 Google Cloud 函数接收 HTTP 请求并将记录推送到 Google Sheets,请执行以下步骤:
步骤 1 —创建一个新的 GCP 项目
最好创建一个新项目,以便与您已经拥有的其他项目隔离开来。我新建了一个,名字叫“记录 iPhone 点击”。
第二步——首先配置 Google Sheets 来接收来自云功能的数据推送
首先设置 Google Sheets 端,因为我们需要云函数代码的认证信息。
在本页的描述之后,
- 启用“Google Drive API”。
- 启用“谷歌工作表 API”。
- 转到“APIs &服务>凭据”并选择“创建凭据>服务帐户密钥”。
- 填写表格
- 点击“创建密钥”
- 选择“JSON”并点击“创建”。JSON 文件的下载会自动进行。
- 请注意 JSON 文件中“client_email”的地址。
- 将 JSON 文件保存到云函数可以访问的地方。
- 在 Google Sheets 中创建电子表格。在单元格 A1 中写入“事件”,在单元格 B1 中写入“时间”,作为最终日志记录的标题。
- 在工作表文件中,按下右上角的“Share”按钮,将您在上面第 7 步中获得的 client_email 添加为授权用户。
这就是全部!
步骤 3—在云功能中配置新功能
转到“云函数”并创建函数。使用触发器类型作为“HTTP”。
新云功能的配置(作者截图)
步骤 4—将代码输入 main.py 和 requirements.txt
点击左下角的下一步,进入代码输入页面。
新云功能代码
这是我实际使用的代码。
main.py
requirements.txt
请记住,服务帐户 JSON 文件需要下载到本地文件夹中,当您想要在云功能中执行此操作时,您必须将文件保存在/tmp/
子文件夹中。否则,云功能拒绝构建您的应用程序。
步骤 5 —部署功能
点击“部署”按钮,等待成功部署。可能需要几分钟。
第六步——找到网址并检查它是否有效
URL 可以在函数的详细信息中找到。
你可以在那里找到网址。
如果您测试单击它,您将看到类似下面的消息,这意味着代码已成功部署。如果您看到内部错误、服务器错误或任何其他错误,一定是出了问题。
成功消息。
在这里,您还应该在您创建的工作表中看到一条新记录。
工作表中新插入的记录。
第 7 步——将网址发送到你的 iPhone,并在主屏幕上创建一个 Safari 链接
在 iPhone 上打开您的 URL,并将快捷方式添加到主屏幕。
将您的链接添加到主屏幕
然后,您可以在 iPhone 主屏幕上看到新图标。
搞定了。
我们最终得到了什么
第八步——为你的其他生活日志制作 URL 链接,一次又一次地复制函数,设置事件名称,并将每个快捷方式添加到你的主屏幕。
这是这个选项最不方便的地方:云函数的代码只能链接到一个 HTTP 请求 URL。因此,如果你需要一个以上的生活日志(像“喂宝宝”、“宝宝醒来”、“到达办公室”等。),你唯一能做的就是一次又一次地创建函数。
为每个事件设置单独的功能。
当您对 API 设置有更多的控制时,这个过程可以大大减少,就像使用 Heroku 的下一个选项一样!
API 解决方案演示 2—Heroku
在我的另一篇文章中,我已经讨论了很多关于 Heroku 的好处以及如何建立的问题。因此,我将只讨论提交到与 Heroku 同步的 GitHub 存储库的代码。设置完成后,Heroku 可以自动从 GitHub 下载你的应用并进行部署。
Heroku 与 GitHub 存储库同步,全天候提供最新的应用程序。(图片由作者提供)
步骤 1 —准备要与 Heroku 同步的 GitHub 存储库
这是我的 GitHub 库:
requirements.txt 和上面带云函数的 Demo 1 一模一样。
以下是另外两个文件中的内容:
Procfile
app.py
正如你在代码中看到的,在 Heroku 代码中,你需要编写一些额外的代码来通过 Flask 设置 app。另一方面,我们可以设置多个 URL,子目录链接到你想要设置的每个生活日志(像*main domain/feed-baby*
),而不需要为它们准备多个 app,不像上面的云功能案例。
步骤 2 —配置 Heroku 与 GitHub 存储库同步
这一步欠很多我在这里的另一个帖子告诉你怎么做。只要你选择了我们刚刚建立的正确的 GitHub 库,没有什么不同。
第三步——找到网址并检查它是否有效
从 Heroku 设置中找到根 URL,为生活日志添加子目录,如[https://<your subdomain of Heroku app>.herokuapp.com/](https://record-iphone-click.herokuapp.com/)arrive-at-office/
。检查你的浏览器和工作表的 API 是否正常工作。
检查你的浏览器
检查你的工作表
第四步——将网址发送到你的 iPhone,并在主屏幕上创建一个 Safari 链接
这一步和上面云函数的情况完全一样。
我们最终得到了什么
结尾注释
在这篇文章中,我展示了一个例子,结合数据科学家已经知道的技能,可以达到比我们通常预期的更进一步的应用。
iPhone(或任何智能手机)可以发出 HTTP 请求,当我们可以正确设置工作流时,通过 HTTP 请求>启动代码>给出结果,任何事情都可以完成。这篇文章演示了一个非常简单的例子,通过点击 iPhone 图标将生活日志记录到 Google Sheets,但是任何进一步的高级应用程序都可以生成,这取决于您的想象力!
使用 Python 创建和自动化交互式仪表盘
Gif 由你真诚地
在本教程中,我将在 selenium、pandas、dash 和 plotly 的帮助下,使用 python 创建一个自动的、交互式的得克萨斯州新冠肺炎县案例统计仪表板。我假设读者对 python、pandas 和 selenium 有所了解。阅读完本教程后,这就是你创建令人惊叹的交互式仪表盘所需的全部内容!
这些步骤的概述以及您将学到的内容如下:
- 使用 selenium 从 web 下载每日更新的数据
- 使用 shutil、glob 和 os python 库更新数据目录
- 用熊猫简单清理 excel 文件
- 格式化要输入到 plotly 图形中的时间序列数据帧
- 使用 dash 为您的仪表板创建本地网页
在我们开始之前,您需要下载将在本教程中使用的 python 库。这可以在您的终端上使用以下命令来完成:
pip install <package name># package names: selenium, pandas, webdriver_manager, shutil, glob, plotly,and dash
需要注意的一点是,我在步骤 1-4 中使用了 Jupyter 笔记本,然后在步骤 5 中使用了 Spyder。当我解释一个代码块做什么的时候,它通常就在文本的正上方。我在本教程中的所有代码都可以在我的 GitHub 中的 Automate collecting of data notebooks 文件下找到。
好,我们开始吧!
第一步:使用 selenium 从网上下载每日更新的数据
# import packages
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
import shutil
import glob
import os
from unicodedata import *
import time# open a chrome browser using selenium
driver = webdriver.Chrome(ChromeDriverManager().install())# got to web page where excel file links are located
driver.get("[https://www.dshs.texas.gov/coronavirus/additionaldata/](https://www.dshs.texas.gov/coronavirus/additionaldata/)")# these options allow selenium to download files
options = Options()
options.add_experimental_option("browser.download.folderList",2)
options.add_experimental_option("browser.download.manager.showWhenStarting", False)
options.add_experimental_option("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream,application/vnd.ms-excel")
因此,第一个代码块非常简单明了,但是需要注意一些事情。我正在安装一个 selenium chrome web 驱动程序,这样我就不必在新的 chrome 驱动程序更新时重新下载了。这个驱动是用来在线下载我要的 excel 文件的。它会在你的电脑上打开一个新的谷歌 chrome 窗口,然后进入网页,提供每天更新的德克萨斯新冠肺炎各县的案件数量。selenium 的标准选项不允许从 web 下载文件,因此需要修改这些选项以允许这样做。
# initialize an object to the location on the web page and click on it to download
link = driver.find_element_by_xpath('/html/body/form/div[4]/div/div[3]/div[2]/div/div/ul[1]/li[1]/a')
link.click()# Wait for 15 seconds to allow chrome to download file
time.sleep(15)
这段代码使用完整的 Xpath 单击 excel 文件的链接。这可以通过右键单击页面、检查页面、右键单击想要单击的链接并复制完整的 Xpath 来找到。然后驱动程序点击链接,等待 15 秒下载,这对于下一个代码块不出错是至关重要的。
2。使用 shutil、glob 和 os python 库更新数据目录
在继续阅读之前:如果你在家学习,你需要改变我从/Users/tsbloxsom/Downloads/下载目录的路径。xlsx ' to '/Users/
您还需要从我使用的目录中更改要存储数据和 python 笔记本的目录:'/Users/tsbloxsom/Documents/GitHub/Texas-census-county-data-project/Automate collection of data notebooks/。xlsx' to '/Users/ <your_username>/bla/bla/。xlsx '</your_username>
# locating most recent .xlsx downloaded file
list_of_files = glob.glob('/Users/tsbloxsom/Downloads/*.xlsx')
latest_file = max(list_of_files, key=os.path.getmtime)# replace "\" with "/" so file path can be located by python
latest_file = latest_file.replace("\\", "/")
latest_file# we need to locate the old .xlsx file(s) in the dir we want to store the new xlsx file in
list_of_files = glob.glob('/Users/tsbloxsom/Documents/GitHub/Texas-census-county-data-project/Automate collecting of data notebooks/*.xlsx') # need to delete old xlsx file(s) so if we download new xlsx file with same name we do not get an error while moving it
for file in list_of_files:
print("deleting old xlsx file:", file)
os.remove(file)# Move the new file from the download dir to the github dir
shutil.move(latest_file,'/Users/tsbloxsom/Documents/GitHub/Texas-census-county-data-project/Automate collecting of data notebooks/')
这个代码块是大量自动化发生的地方。它使用 glob 和 os.path.getmtime 函数找到我刚刚下载的 excel 文件,删除我想要存储新 excel 文件的目录中的任何旧 excel 文件(可能是前一天的),然后将新 excel 文件移动到 GitHub 目录中。os.path.getmtime()函数返回路径的最后修改时间,因此使用 max()函数,可以找到最*下载的 excel 文件。
第三步:用熊猫简单清理 excel 文件
现在我们已经在我们想要的目录中有了最新的新冠肺炎数据,在绘制之前我们需要做一些清理工作。
import pandas as pd
import repd.set_option('display.max_rows', 500)
pd.options.display.max_colwidth = 150# again we need to locate the .xlsx file
list_of_files = glob.glob('/Users/tsbloxsom/Documents/GitHub/Texas-census-county-data-project/Automate collecting of data notebooks/*.xlsx')
latest_file = max(list_of_files, key=os.path.getctime)
print(latest_file.split("\\")[-1])df = pd.read_excel("{}".format(latest_file),header=None)df.head()
真实的你的形象
因此,我们读取 excel 文件,并将其转换为熊猫数据框(df)…我们有一些清理工作要做。让我们处理文件的第一行和最后几行。
# print out latest COVID data datetime and notes
date = re.findall("- [0-9]+/[0-9]+/[0-9]+ .+", df.iloc[0, 0])
print("COVID cases latest update:", date[0][2:])
print(df.iloc[1, 0])
print(str(df.iloc[262:266, 0]).lstrip().rstrip())#drop non-data rows
df2 = df.drop([0, 1, 258, 260, 261, 262, 263, 264, 265, 266, 267])
首先,我想打印出前两行和最后几行数据的相关信息,然后我想从 df 中删除这些行。
# clean column names
df2.iloc[0,:] = df2.iloc[0,:].apply(lambda x: x.replace("\r", ""))
df2.iloc[0,:] = df2.iloc[0,:].apply(lambda x: x.replace("\n", ""))
df2.columns = df2.iloc[0]
clean_df = df2.drop(df2.index[0])
clean_df = clean_df.set_index("County Name")# convert clean_df to a .csv file
clean_df.to_csv("Texas county COVID cases data clean.csv")
真实的你的形象
在上面的代码块中,我清除了每个日期的行,其中许多日期在单元格中有新行、和回车、字符。然后,我将日期行作为列名,并删除原来的日期行。我们最终得到了一个清晰的 df,其中的行是德克萨斯州的每个县,每列代表每个日期的案例数。最后,我想为下一步将 df 转换成. csv 文件。
第四步:将输入的时间序列数据帧格式化成 plotly 图形
import plotly.express as pxlist_of_files = glob.glob('/Users/tsbloxsom/Documents/GitHub/Texas-census-county-data-project/Automate collecting of data notebooks/*.csv')
latest_file = max(list_of_files, key=os.path.getmtime)
latest_file.split("\\")[-1]df = pd.read_csv(latest_file.split("\\")[-1])
我们再次使用 glob 和 os.path.getmtime 函数来查找最新的。csv 文件,即我们的“德州县 COVID 病例数据 clean.csv”。
# convert df into time series where rows are each date and clean up
df_t = df.T
df_t.columns = df_t.iloc[0]
df_t = df_t.iloc[1:]
df_t = df_t.iloc[:,:-2]# next lets convert the index to a date time, must clean up dates first
def clean_index(s):
s = s.replace("*","")
s = s[-5:]
s = s + "-2020"
#print(s)
return sdf_t.index = df_t.index.map(clean_index)df_t.index = pd.to_datetime(df_t.index)
真实的你的形象
在上面的代码块中,我将数据帧转换为时间序列 df,其中行现在是日期,列是县。我还删除了几个列,其中包含病例总数和报告病例的县数。最后,我将日期列转换成一种可用的格式,这种格式可以转换成 pandas date_time 对象,对此我使用了函数 clean_index。但是我们还没有完成。
# initalize df with three columns: Date, Case Count, and County
anderson = df_t.T.iloc[0,:]ts = anderson.to_frame().reset_index()ts["County"] = "Anderson"
ts = ts.rename(columns = {"Anderson": "Case Count", "index": "Date"})
我们的 plotly 函数将接受 3 列作为输入:日期、案例数和县。所以我们需要把宽 df 转换成长 df。为此,我首先初始化一个包含三列的长 df:一个日期列、安德森县的案例数和一个包含县名的县列。看起来像这样:
真实的你的形象
# This while loop adds all counties to the above ts so we can input it into plotly
x = 1
while x < 254:
new_ts = df_t.T.iloc[x,:]
new_ts = new_ts.to_frame().reset_index()
new_ts["County"] = new_ts.columns[1]
new_ts = new_ts.rename(columns = {new_ts.columns[1]: "Case Count", "index": "Date"})
ts = pd.concat([ts, new_ts])
x += 1#save long form df for dash app
ts.to_csv("time_series_plotly.csv")
上面代码块的注释说明了一切,但我们现在只是循环遍历整个 wide df 并为每个县创建 new_ts long df,看起来像 Anderson 的 df。然后,我们将每个长 df 连接在一起,wallah 我们自己有了可以输入到 plotly 中的长 df,稍后我们的 dash 应用程序显示如下!
真实的你的形象
fig = px.scatter(ts, x='Date', y='Case Count', color='County')
fig.update_traces(mode='markers+lines')
#fig.update_traces(mode='lines')
fig.show()
真实的你的形象
我们最终可以使用简单的三行代码将数据绘制成散点图!我使用了“标记+线条”选项,我认为它看起来比线条更令人愉快,也更容易交互。我已经爱上了 plotly,因为与 seaborn 或 matplotlib 不同,它的图形是交互式的,而且编码非常简单。Plotly 也有出色的文档,可在此处找到。
第五步:使用 dash 为你的仪表板创建一个本地网页
对于没有任何软件开发经验的人来说,这一步可能会变得棘手。大约一个月前我还没有,所以我会慢慢地走完这一步,尽可能保持简单。首先,我们希望在笔记本/数据目录中创建一个虚拟环境。要做到这一点,我们必须进入命令行或 Anaconda 提示符,并使用 cd 进入该目录。大概是这样的:
C:\Users\tsbloxsom>cd C:\Users\tsbloxsom\Documents\GitHub\Texas-census-county-data-project\Automate collecting of data notebooks
然后我们创建虚拟环境:
python3 -m venv venv
然后我们激活虚拟环境:
venv\Scripts\activate
现在让我们进入短跑项目。这里有一个链接,供新手使用。Dash 是由 plotly 的人制作的,所以你可以得到很棒的文档和产品。我学会了如何在不到两个小时的时间内为时间序列仪表板制作这个应用程序,不是想吹牛,就是这么简单。下面是应用程序的代码。我使用了 dash 网站上本教程中的基础应用模板组合。因此,打开您最喜欢的代码编辑器,如 Spyder,将下面的代码作为 app.py 保存在与您的新 venv 文件夹相同的目录中。csv 文件。
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pdexternal_stylesheets = ['[https://codepen.io/chriddyp/pen/bWLwgP.css'](https://codepen.io/chriddyp/pen/bWLwgP.css')]app = dash.Dash(__name__, external_stylesheets=external_stylesheets)colors = {
'background': '#F0F8FF',
'text': '#00008B'
}# assume you have a "long-form" data frame
# see [https://plotly.com/python/px-arguments/](https://plotly.com/python/px-arguments/) for more options
df = pd.read_csv("time_series_plotly.csv")fig = px.scatter(df, x='Date', y='Case Count', color='County')fig.update_layout(
plot_bgcolor=colors['background'],
paper_bgcolor=colors['background'],
font_color=colors['text']
)markdown_text = '''
### Texas COVID-19 DashboardCreator: Truett Bloxsom, [LinkedIn]([https://www.linkedin.com/in/truett-bloxsom/](https://www.linkedin.com/in/truett-bloxsom/)), [github]([https://github.com/tsbloxsom](https://github.com/tsbloxsom))This is my first interactive dashboard using Dash! Hope you like it!This first plot is Texas COVID-19 accumulated cases by county over timeSource for data: [dshs.texas.gov]([https://www.dshs.texas.gov/coronavirus/additionaldata/](https://www.dshs.texas.gov/coronavirus/additionaldata/))'''app.layout = html.Div([
dcc.Markdown(children=markdown_text,
style={
'backgroundColor': colors['background'],
'textAlign': 'center',
'color': colors['text']
}),
dcc.Graph(
id='example-graph',
figure=fig
)
])if __name__ == '__main__':
app.run_server(debug=True)
现在回到激活的环境,下载 dash、plotly 和 pandas:
pip install <package_name>
然后,您可以通过运行以下命令运行您的应用程序:
python3 app.py
它应该是这样的:
现在只需复制 http 链接,粘贴到 chrome 和 boom 中即可!它应该是这样的:
我更进一步,跟随 Elsa Scola 关于如何免费部署 dash 应用程序的教程。如果你只是想玩我的交互式仪表盘,你可以这里!
感谢您关注我的教程,并随时通过 linkedin 联系我。我总是试图获得反馈并加强我的数据科学技能,所以如果有什么我可以改进编码方式或更详细地解释一些东西,请在评论中告诉我。
用 Amazon 的关系数据库服务(RDS)创建和连接 PostgreSQL 数据库
开始使用云中的数据库并不困难。事实上,多亏了亚马逊网络服务(AWS ),事情从来没有这么简单过。
关系数据库服务(RDS)是亚马逊全面的 web 产品套件的一部分,截至今天,RDS 支持 6 种数据库引擎:Amazon Aurora、PostgreSQL、MySQL、MariaDB、Oracle 数据库和 SQL Server。
这里,我们将关注使用 Amazon 的 RDS 和 pgAdmin 创建并连接到 PostgreSQL 数据库实例。
前期步骤:这是检查和确定你有两件东西的地方:1) 亚马逊 AWS 账户;如果你还没有账户,亚马逊已经简化了在这里创建一个账户。2)pg admin;如果您的计算机上没有下载 pgAdmin,可以在此 处 解决。
一旦你有了这两样东西,就正式开始了。我们开始吧!
步骤 1- 登录到 AWS 控制台,找到 RDS 服务。选择 RDS 服务会将您带到 Amazon RDS 仪表板。从仪表板中,选择“创建数据库”在继续配置设置之前,您需要确定选择了“标准创建”和“PostgreSQL”。这个可以直接看下面。
步骤 2-使用下拉菜单,选择 PostgreSQL 的最新版本。在我们的例子中,它是“PostgreSQL 11.5-R1”
步骤 3——对于这一步,给数据库实例一个名称,并创建一个用户名和密码。你可能会比下面看到的更具体。
步骤 4-确保选择了“数据库实例大小”和“存储”默认设置。
第 5 步-再次确保“可用性和持久性”和“连接性”默认设置被选中。
步骤 6-在“可公开访问”下的“附加连接配置”中,选择“是”。这是走向正确的重要一步。
步骤 7-确保选择了“数据库认证”和“附加配置”的默认设置。验证完成后,我们就可以创建数据库了。
重要提示:创建一个数据库实例并供使用通常至少需要几分钟。
在 Amazon RDS 仪表板的“Databases”中,您应该知道您的数据库实例的名称以及正在使用的引擎(PostgreSQL)。
步骤 8——现在是时候解决最后一个问题了,确保实例可以使用。在“连接性和安全性”中,有一个标题为“安全性”的部分单击列出的默认 VPC 安全组。
第 9 步-点击默认 VPC,我们将进入以下屏幕。在这里,使用“操作”下拉菜单,选择“编辑入站规则”
步骤 10-编辑入站规则时,选择“Anywhere”并保存编辑。
步骤 11-完成出站规则的步骤 9 和 10。
第 12 步-返回亚马逊 RDS 仪表板。在使用 pgAdmin 连接数据库实例之前,再次确认该实例可供使用。
第 13 步——很好,现在是时候打开 pgAdmin 并连接 RDS 数据库实例了。打开 pgAdmin 后,您应该会看到左侧显示“服务器”的侧边栏。右键单击“服务器”创建一个新的服务器。
步骤 14-给服务器一个名称。出于演示的目的,我选择将服务器命名为“example”
第 15 步- 接下来,在“连接”选项卡中填写一些信息。通过在 RDS 仪表板中单击您的数据库实例,可以找到主机名/地址。您可以在“连接性&安全性”中的“端点”下找到它我推荐复制粘贴到 pgAdmin 里。最后,输入您在 RDS 中为数据库实例创建的用户名和密码。一旦你输入了每一项,就到了保存的时候了。
服务器现在应该列在左侧栏中,如下图所示。
第 16 步——现在,让我们继续在服务器中创建一个数据库。右键单击“数据库”,选择“创建”,然后选择“数据库…”
第 17 步——给数据库命名,完成后保存。
是的,就这么简单。我们现在看到数据库列在左侧栏中。
主要成就👏
使用 Amazon 的 RDS 创建一个 PostgreSQL 数据库实例。
使用 pgAdmin 连接到数据库实例。
使用 pgAdmin 创建服务器和数据库。
我很乐意连接!找到我最好的地方是在 LinkedIn 上。😃
在 Julia 中创建和拟合回归模型
在 Julia 中根据风速和湿度创建和拟合车床模型。
0.0.9 刚刚被合并到主分支中,随之而来的是一系列令人兴奋的新特性。Julia 的机器学习现在比以往任何时候都容易。Julia 中的机器学习目前面临的唯一问题是缺乏 Python 大小的生态系统,以及缺乏文档。幸运的是,对于车床用户来说,有许多功能和工具可供我们使用,甚至更好:
车床有很好的文档!
我在网上找到了另一个 CSV 数据集。要将这个 CSV 加载到 Julia 中,我们将需要三个依赖项中的第一个,CSV.jl。所以记住这一点,第一步是跳转到 Pkg REPL,首先输入朱莉娅 step,然后按]。从这个 REPL 中,我们可以添加三个依赖项:
julia> ]
pkg> add "Lathe"#Unstable
pkg> add "DataFrames"
pkg> add "CSV"
现在我们的系统上已经有了 CSV.jl,我们可以使用将它引入到 Julia 中,并使用 CSV.read()方法读入我们的数据:
using CSV
df = CSV.read("weatherHistory.csv")
对于连续和分类特征,这个数据集有许多选项,这将首先允许我展示车床预处理的最新方法,
一个热编码
我决定做的第一件事是将数据分成两个数据帧,一个将用于我们的模型,另一个将用于展示一个热编码器。我这样做是通过首先做
show(df, allcols = true)
其次,我构建了两个具有相应特性的新数据帧类型:
using DataFrames
hotdf = DataFrame(:Humidity => df[:Humidity], :Type => df[Symbol("Precip Type")])
df = DataFrame(:Humidity => df[:Humidity],:WindSpeed => df[Symbol("Wind Speed (km/h)")])
仅仅为了这个例子,我将假装我不知道如何使用车床,以便炫耀车床中的代码内文档。首先,我们当然需要使用以下命令加载车床:
using Lathe
然后我们可以用茱莉亚的?()包本身的方法:
?(Lathe)
|====== Lathe - Easily ML =====|= = = = = v. 0.0.9 = = = = = ||==============================|**__**Lathe.stats|**__**Lathe.validate|**__**Lathe.preprocess|**__**Lathe.models|**__**Use ?(Lathe.package) for information![uuid]38d8eb38-e7b1-11e9-0012-376b6c802672[deps]DataFrames.jlRandom.jl
由此,我们看到了一个模块树,其中显示了三个模块:
- 车床.统计
- 车床.验证
- 车床.预处理
- 车床.型号
对于 one hot encoder,我们当然要研究 Lathe.preprocess。(Lathe.package)了解更多信息,所以让我们试试吧!
现在我们可以在模块树中看到 OneHotEncode()函数,让我们对它做同样的操作:
?(Lathe.preprocess.OneHotEncode)
由此我们得到一个简短的描述,参数,甚至用法。看起来这个函数需要一个数据帧,以及一个表示我们想要编码的列的符号。这当然与你的预期有关:
抛开手边的方法,让我们回到我们的原始数据框架,用我们的两个特征,湿度和风速。因为数据集在这个特定的列上没有缺失值,并且我不打算缩放任何特性,所以我决定要做的第一件事是继续并训练 TestSplit:
using Lathe.preprocess: TrainTestSplit
train, test = TrainTestSplit(df)
这将返回两个新的数据帧,train 将占数据的 75%,而 test 将占剩余的 25%。我们可以通过在 df 后面添加一个浮动百分比来对此进行调整:
train, test = TrainTestSplit(df, .5)
这将*均分割数据。
就我个人而言,我喜欢将我所有的数据分配给变量,这些变量在考虑做模型之前就可以很容易地改变。在 Julia 中,你可以这样做:
x = :Humidity
y = :WindSpeed
trainX = train[x]
trainy = train[y]
testX = test[x]
testy = test[y]
接下来,我们只需要导入我们的模型,我们的预测函数,然后我们就可以构建和拟合了!
using Lathe.models: predict, LinearRegression
model = LinearRegression(trainX, trainy)
yhat = predict(model, testX)
这是结果:
真的是太简单了!你怎么会对此感到不安呢?我很期待车床在未来的发展方向,以及这个模块会对 Julia 产生怎样的影响。到目前为止,Lathe 可能是您可以使用的最简单的 ML 包,我认为大部分学习曲线源于使用 DataFrames.jl,而不是 Lathe。总的来说,我对车床的下一个版本,0.1.0 以及更高版本感到非常兴奋!
使用深度神经网络创建艺术直播视频滤镜
将 CoreML 用于 iPhone 的复杂视频滤镜和效果
在之前的一个项目中,我致力于复制快速神经风格转移,通过深度神经网络将一幅图像的艺术风格应用于另一幅图像,从而转换一幅图像。虽然在 python 笔记本中转换图像效果很好,但对于普通用户来说并不容易。我想在 iOS 设备上部署这个模型,类似于几年前流行的 Prisma 应用程序。除此之外,我还想测试生成模型的极限,并在直播视频上转换帧。这个项目的目标是在实时视频上运行一个生成模型,探索在当前技术界限下的可能性。有几件事情使这成为可能——1)缩放输入,2)利用设备的 GPU,3)简化模型。因为这是建立在以前的项目之上的,所以熟悉一下以前的帖子会有所帮助。
缩放输入
今天许多手机可以拍摄令人惊叹的 4k 视频,包括我开发的 iPhone XS。虽然设备中的 A12 芯片功能强大,但要在这种大小的每一帧上使用深度神经网络,速度太慢了。通常视频帧被缩小以用于设备上的图像识别,并且该模型在帧的子集上运行。例如,对象识别应用程序可以在 224 x 244 帧上每秒运行一次模型,而不是在 4096 x 2160 帧上每秒运行 30 次。这适用于对象检测用例,因为对象在帧之间不会改变太多。
这显然不适用于视频帧的风格化。每秒只有一个风格化的帧闪烁对用户来说没有吸引力。然而,这里有一些要点。首先,缩小框架尺寸是完全合理的。视频通常以 360p 的分辨率流式传输,并放大到设备的 1080p 屏幕。第二,也许没有必要以每秒 30 帧的速度运行模型,较慢的帧速率就足够了。
在模型分辨率和帧速率之间有一个权衡,因为 GPU 在一秒钟内可以进行的计算数量有限。您可能会看到一些视频聊天*台在使用卷积实现视频效果(即改变背景)时,帧率较低或缓冲更多。为了了解不同的帧速率和输入形状是什么样子,我用原始的神经网络和 OpenCV 在电脑上制作了几个风格化的视频。我选定的目标帧速率为 15 fps,输入为 480 x 853。我发现这些仍然是视觉上吸引人的,也是基准测试中可识别的数字。
利用 GPU
我使用 tfcoreml 和 coremltools 将 Tensorflow 模型转换为 coreml 模型。完整方法的要点可以在下面找到。这里有几个考虑因素。首先,我转向了批量规范化,而不是实例规范化。这是因为 CoreML 没有现成的实例标准化层,这简化了实现,因为在干扰时间,每批中只有一个帧。还可以在 tfcoreml 中使用自定义方法来转换实例规范化层。
接下来,TensorFlow 和 CoreML 之间的张量形状不同。TensorFlow 模型有一个 out in (B,W,H,CH),而 CoreML 支持图像的(B,CH,W,H)。在将模型转换为 CoreML 模型后,我编辑了模型规格,以使转置层适应形状。只有在将模型输出形状更改为格式(B,CH,W,H)之后,我才将输出类型更改为图像。这必须在模型规格中手动完成;在撰写本文时,tfcoreml 支持图像作为输入参数,但不支持输出。
此外,由于我缩小了图像的尺寸,以便通过网络传递它们,因此我能够使用 coremltools 添加一个放大层,将图像放大到合理的 1920 x 1080 帧大小。一种替代方法是在获得网络结果后调整像素缓冲区的大小,但这将涉及 CPU 的工作或 GPU 上的额外排队。CoreML 的 resize 层具有双线性缩放,并提供了令人满意的放大,几乎没有特征或像素伪像。由于这个调整大小层不是基于卷积,它也增加了模型推断时间的最小时间。
我使用 GPU 的最后一个方法是显示帧。由于我向帧添加了自定义处理,所以我不能将它们直接发送到标准的 AVCaptureVideoPreviewLayer。我使用 Metal Kit 中的 MTKView 来展示框架,它利用了 GPU。虽然金属着色器是一个简单的传递函数(输入作为输出返回),但绘图证明是高性能的,并且视图中的队列在丢帧的情况下也很有帮助。
简化模型架构
原始模型架构有五个剩余卷积层。虽然在标准 GPU 上性能很高,但这对 A12 处理器来说太深了,至少对于典型的帧速率来说是这样。神经网络学习各种纹理的一个主要组成部分是五个残余块。如果纹理很简单,后面的剩余层可能看起来更接*身份过滤器。如果纹理更复杂,所有的层可能有有意义的过滤器。我试着为一个更高性能的网络修剪掉一些这样的块,代价是不能学习一些高度复杂的纹理。此外,我试验了可分离的卷积层,而不是传统的全连接层,就像在其他轻量级架构(如 MobileNets)中使用的那样。
A12 测试中使用的架构。灰色卷积残差块(2–4)是通过移除或使用块中的可分离卷积层进行实验的。
我在一台计算机 GPU 上测试了几种不同的架构,以缩小最具性能的网络,同时将纹理降级降至最低。虽然我只使用了 3 x 3 大小的内核,但我保持了原始架构的缩小和放大在很大程度上的一致性。一些改变(将剩余块减少到 1,将过滤器的数量减少到 64)具有快速的推断时间,但是在质量上有很大的下降。在 GPU 测试之后,我在一台配有 A12 芯片的 iPhone XS 上测试了这些模型。
各种模型架构的结果(毫秒)。
这些是 100 次迭代的基准测试的结果(以毫秒为单位),输入帧大小为 480 x 853。第一帧被省略,因为它是模型“启动”的异常值。从这些结果中得出的一个有趣的结论是,可分离的卷积块并没有提高网络的性能。为了提高效率,可分离的卷积层通常难以实现。我读过各种案例,其中可分离层在不同环境中的表现不尽如人意,这里也可能是这种情况,这值得进一步研究。
我使用了 3 个完整的(不可分离的)剩余块来获得以下结果。这种模式在各种风格和案例上都非常有效。15 fps 是每帧 66 毫秒,这种实现可能是该设备的上限,因为会出现几次丢帧或延迟。
结果和考虑
下面是一些应用了不同风格和纹理的例子。这些结果为移动设备上的视频显示了比当前流行的仅使用着色器或单个滤镜的效果复杂得多的效果。
草图纹理样式
“缪斯”风格
“尖叫”风格
“雨公主”风格
马赛克风格
从这个实验中得到的一个教训是,修整过的神经结构并不能概括所有的风格,因为分解图像的层数较少。这在复杂的样式中最为明显,这些样式更多地变换了图像中的对象。用神经网络进行转换是一个很难解决的问题,所以这并不奇怪。马赛克风格是这个问题的一个很好的例子。与其他实现包含的更复杂的特征相比,我的实现在图像中重复类似的镶嵌,后者在后处理中使用完整的网络。另一方面,更简单的风格,如“尖叫”和普通的草图纹理表现得相当好,与整个建筑的预期相似。
另一个缺点是缺乏广义超参数。通过修剪后的网络,我发现没有一组可靠的超参数适用于大多数风格。再一次,马赛克风格在这里有改进的空间,因为内容组件被牺牲了,希望有更复杂的风格特征。
随着未来硬件的进步,能够使用更大的神经网络是不可避免的。随着更复杂的网络,更复杂的风格,纹理,甚至对象转换都是可能的。目前,这表明可以开发各种过滤器和效果,用于社交媒体等实时视频内容。
相关著作
实时视频神经风格转移,凯特·鲍姆利,丹尼尔·迪亚蒙,约翰·西格蒙——https://towards data science . com/Real-Time-Video-Neural-Style-Transfer-9f6f 84590832
创建 AWS EC2 并将其与 AWS Cloud9 IDE 和 AWS S3 连接
创建亚马逊弹性计算云 (EC2)、将其与亚马逊简单存储服务(S3)链接并连接到 AWS Cloud9 集成开发环境(IDE)的分步教程
亚马逊网络服务(AWS)。
有时,您可能会因为您的桌面功能而无法完成任务。例如,作为一名数据科学家,大部分时间几乎不可能将一个巨大的数据集加载到您的个人桌面上。即使您能够加载这些数据,训练您的模型也可能需要很长时间。一个可能的解决方案是使用亚马逊 Web 服务 ( AWS )。你可以从这里阅读更多关于 AWS 的内容。
使用亚马逊弹性计算云 ( 亚马逊 EC2将有助于解决前面提到的问题。它是可扩展的,并且消除了预先投资硬件的需要。你可以从这里阅读更多关于亚马逊 EC2 的信息。
还有亚马逊简单存储服务 ( 亚马逊 S3 )可以用来随时存储和检索任意数量的数据。你可以从这里阅读更多关于亚马逊 S3 的信息。
为了使与亚马逊 EC2 的通信更容易,可以使用 AWS Cloud9 。它是一个基于云的集成开发环境( IDE ),让你可以使用浏览器编写、运行和调试你的代码。你可以从这里阅读更多关于 AWS Cloud9 的内容。
在本文中,我们将:
1.获取访问密钥和秘密密钥
访问密钥和秘密密钥用于验证您向 AWS 发出的请求。
从这里创建 AWS 账户后,从这里登录 AWS 管理控制台。
登录后显示的 AWS 管理控制台-作者图片
首先,我们需要创建一个访问密钥和秘密密钥。在安全、身份、&合规挑选 IAM (身份和访问管理)。
Pick IAM 按作者分类的图像
按下用户然后选择你的用户名。如果找不到您的用户名,按 添加用户。
按下用户 —图片作者
按下您的用户名后,在安全凭证选项卡内按下创建访问键。然后下载。csv 文件。你会在里面找到你的访问密钥&秘密密钥。把它们存放在安全的地方,因为你以后会需要它们。
按下创建访问密钥然后下载。csv —作者图片
2.构建一个 AWS EC2 实例
回到 AWS 管理控制台,从 compute pick EC2。
选择 EC2 —按作者分类的图像
按下启动实例。
按下启动实例 —图片由作者提供
从显示的选项中选择适合你的。在这个教程中,我将选择 Ubuntu Server 20.04 LTS 版本。按下选择****
搜索或向下滚动到 Ubuntu Server 20.04 LTS,然后按选择—作者图片
选择您需要的实例。对于本教程,我将选择 t2.micro 。有关 Amazon EC2 实例类型的更多信息,您可以按这里的。
选择所需的实例类型-按作者分类的图像
从上面的标签中选择 4。添加存储器。如果需要,您可以增加大小。
如果需要,增加大小—图片由作者提供
选择 6。配置安全组。将分配一个安全组改为选择一个现有的安全组。选择您想要的群组。您的管理员应该已经创建了该策略,或者您也可以创建它。如果不选择安全组,您的计算机将是公共的,任何人都可以访问。如果需要,您可以随意使用任何其他配置。最后,按下查看并发射然后发射。
选择一个安全组,然后按查看并启动 —图片由作者提供
将选择现有的密钥对改为创建新的密钥对。然后为该密钥对添加一个名称,并按下下载密钥对。这将下载一个 pem 文件。然后按下启动实例。最后,按下视图实例。
创建新的密钥对,然后下载 pem 文件。启动实例,然后按查看实例-按作者分类的图像
你会发现你的机器没有名字。将其重命名为您想要的名称,然后点击选择它。复制公共 DNS(IPv4) ,因为我们将在下一步中需要它。
重命名您的机器,然后选择它,并复制公共 DNS 由作者的形象
3.使用 PuTTY 连接到此实例
下一步你需要安装油灰。PuTTY 是一个终端仿真软件,可以在 Windows、Unix 和 Linux 上运行。
打开推杆。选择加载。转到保存 pem 文件的路径(之前在步骤 2 中保存)。确保将类型的文件设置为所有文件(。) 查看 pem 文件。双击 pem 文件。
您应该会得到一条消息,表明您成功导入了外键。按确定,然后保存私钥。
油灰键生成器。媒体负载—作者提供的图像
关闭油灰并打开油灰。
在主机名中写上 ubuntu@ <粘贴公共 DNS(IPv4) >
展开 SSH 然后按下 AUTH 。点击浏览并转到你之前保存的 PPK 然后双击它。
回到会话,然后在“保存的会话中添加一个名称,然后按保存。这样,您就将配置保存到了 PuTTY 中。下次要连接机器时,只需按下 load 即可。
最后按下打开
PuTTY —作者提供的图像
现在你通过终端连接到机器上,可以写任何你想要的 linux 命令。
PuTTY 连接的终端—图片由作者提供
您应该从以下两个命令开始更新环境。
sudo apt-get update
sudo apt-get -y upgrade
4.将 AWS EC2 连接到 AWS S3。
要通过机器与 AWS S3 交互,您可以通过以下命令安装 awscli
sudo apt-get install -y awscli
aws configure
" aws configure "将要求您输入之前在 IAM 中生成的访问密钥和秘密密钥(来自步骤 1)。对于其他 2 个提示,按回车键即可(无需填写)。
从 aws 复制一些东西到你的机器就像在 linux 中使用 cp 命令。示例:
aws s3 cp source destination
源和目的可以是机器上的本地路径,也可以是 s3 上的路径。
对于 s3 路径,只需要在开头加上 s3:// 即可。
将文件从 S3 复制到本地机器的示例:
aws s3 cp s3://FolderName/fileName.txt ./fileName.txt
保持 PuTTY 与实例的连接,并为下一步运行终端。
5.将 AWS EC2 连接到 AWS Cloud9
从 AWS 管理控制台,在开发者工具内选择 Cloud9。
Pick Cloud9 —按作者分类的图片
按创建环境。为该环境添加任何名称,然后按下下一个。
按创建环境-按作者分类的图像
在配置设置中,将环境类型更改为连接并运行在远程服务器(SSH) 。在用户中,写入 ubuntu 。添加实例的公共 DNS IPv4 。然后按下复制密钥到剪贴板。
更改环境类型并填充数据-按作者分类的图像
在连接到机器的 PuTTY 终端中,写入以下命令,将键保存到机器中:
echo <**Paste the Copied Key**> >> ~/.ssh/authorized_keys
sudo apt-get install -y nodejs
Cloud9 需要 nodejs 这就是我们安装它的原因。
在配置设置中按下下一步,然后创建环境。
Cloud9 加载屏幕—作者图片
右键点击 C9 安装和复制链接地址。
按下下一个并勾选所有选项。
右键点击“ C9 安装”和复制链接地址 —图片作者
进入 PuTTY 终端并运行以下命令:
wget <**Paste the copied link address**>
chmod a+x c9-install.sh
sudo apt-get -y install python
sudo apt-get install build-essential
./c9-install.sh
现在你可以关闭油灰并从云 9 继续。
Cloud9 欢迎屏幕—作者图片
现在,您已经准备好在这个 IDE 上开始开发了。你可以使用 Cloud9 通过拖放将文件或数据从你的本地机器上传到 EC2 。您也可以运行 / 调试代码或使用下面的终端与机器交互。
Cloud9 IDE —作者图片
注意:如果实例的 IP 地址改变了(当你停止一个实例,然后重启它的运行时发生),你只需要复制新的 IPv4 地址。
在 Cloud9 中,点击左上角的 Cloud9 图标。
点击“转到您的仪表板”。按下所需的环境,然后从右上角选择编辑。然后向下滚动到主机,只需更改 IPv4 地址。
选择环境,然后按编辑-按作者分类的图像
结论
本教程提供了启动和连接 AWS EC2 到 AWS Cloud9 的分步指南,以及将数据从 AWS S3 移入或移出。这应该有助于设置一个基本的数据科学环境。
我推荐 AWS 文档来获得更多关于亚马逊提供的所有服务的信息。
创建漂亮且信息丰富的文本表示:单词云
一个简单的 Python 教程
单词云经常被误认为是笨重和过时的。事实上,它们可以是优雅和创造性的文本交流方法,既可以作为探索性分析,也可以作为演示。此外,它们很容易用 Python 创建——所以让我们开始吧!
让我们复制粘贴这篇新冠肺炎文章的内容,并将其粘贴到一个名为covid_article.txt
的文本文件中。这个文本文件的内容将被存储到一个名为content
的变量中。
为了确保一个单词与另一个单词相同,我们需要去掉标点符号和大写字母,这样“hello”就与“hello”相同,而“Hello”与“Hello!”相同。我们还需要确保字符都是字母——我们可以通过列表理解(或者正则表达式)来实现这一点。
显然有一些小问题需要解决,但一般来说这只是一串单词,我们现在继续。我们需要导入wordcloud
模块(使用pip install wordcloud
安装)和matplotlib
库来显示图像。
我们可以使用 wordcloud 用简单的参数构建一个 WordCloud。使用了三个参数——max_font_size
,表示一个字的最大大小;max_words
,显示的最大字数;还有background_color
,设置背景的颜色。Matplotlib 可以用来设置图形大小,显示文字云,不显示轴。
不要担心分辨率低,我们稍后会解决这个问题。
它看起来不是很好,但它有很多我们期望看到的信息,每个单词的大小取决于它在文本中出现的次数。请注意,默认情况下,所谓的“停用词”,或者像“a”、“I”或“and”这样的常用词被排除在词云之外。您可以编写额外的过滤器来删除其他单词。
让我们试着改变颜色。我们需要创建一个函数color_func()
,它返回一个 HSL 颜色,这是另一种表示颜色的方法(仅次于 RGB)。这个函数返回介于灰色和红色之间的随机颜色。
你可以在这里熟悉 HSL(色相、饱和度、明度)。供参考,这是hsl(0,50,50)
的数值。
要应用 color 函数,请使用以下代码将 wordcloud 转换为数组,并应用重新着色函数。
或者,我们可以重新定义color_func
函数来接受一个word
参数,并检查它是否在最常见的单词列表中,top_words
。在这种情况下,top_words
是文本中出现超过二十次的所有单词的集合。如果单词是顶部单词,则返回一个随机的红色值。否则,该单词将显示为灰黑色。
让我们把这个词做成病毒形状的云。首先,我们需要找到病毒的图片——你可以在任何搜索引擎上找到它。理想情况下,图像将有一个白色背景来创建一个“面具”。
来源: IconExperience 。图片免费分享。
掩码需要通过 PIL (Python Imaging Library)读取,并通过 NumPy 转换成数组。我们需要进口这些。
一旦mask
被创建,就可以在WordCloud
中使用,包括参数mask=mask
。这个例子也使用了上面例子中的着色函数。
不错!Wordcloud 为我们做了适应面具的大部分工作。对于我们的特定图像,较大的单词被推到中间,较小的单词被推到外面,这提供了一个很好的层次风格的可视化。
或者,我们可以将背景改为‘black’
,将文本颜色改为灰白色,以获得另一种可视化效果。
一些额外的想法/建议:
- 增加字数可以帮助更好地勾勒出面具的轮廓。
- 这些单词云的输出通常是粒状的。要导出高质量图像,请使用
plt.savefig(“filepath/name.png”, dpi=600)
,其中 DPI 为“每英寸点数”。600 DPI 很好地*衡了质量和处理时间。
高分辨率图像!
- 了解到较大的区域通常由较大的单词填充,尝试在 photoshop/PowerPoint 中创建自己的蒙版来表达层次。
- 使用着色功能,您可以突出显示重要的单词,并探索其他提供的
**kwargs
,如font_size
、position
和orientation
。例如,可以根据单词的大小或其在图像中的位置成比例地对单词进行着色(例如,根据单词在图像中的位置对单词进行着色)。
感谢阅读!
[## 您一直在寻找的 5 个以上简单的一行程序来提升您的 Python 可视化水*
最小的努力,最大的收获
towardsdatascience.com](/5-simple-one-liners-youve-been-looking-for-to-level-up-your-python-visualization-42ebc1deafbc)
除非另有说明,图片由作者创作。
使用 Python 创建超越默认设置的美丽地图
超越默认地图定制的初学者指南
用 Geopandas 制作地图非常容易,但我必须承认,它的默认设置不如其他数据可视化软件。在本教程中,我们将研究改变和创建美丽地图的方法。有许多不同的方式来定制您的地图。我们介绍了一些具体的方法,您可以通过参数、选择颜色以及在地图中添加背景图和底图来增强地图制作。
让我们用少而简单的代码制作出光滑美观的地图。
数据和默认地图
让我们读取数据,并用默认值将其可视化。我们使用 Geopandas read_file()
函数读取数据。用 Geopandas 绘制地理数据也很简单,只需调用plot())
函数。
gdf = gpd.read_file(“data/malmo-pop.shp”)
gdf.plot()
看那张地图。尽管长得丑,我们还是要欣赏这种轻松。我们可以用参数来增强和定制地图。绝不接受默认值。
自定义地图参数
上面的地图有几个缺点,我们可以很容易地通过获取和定制参数来解决。在这一节中,我们回顾一些可以帮助我们制作更加美观的地图的基本元素。让我们添加一些基本元素和自定义地图。
fig, ax = plt.subplots(figsize=(16,16)) # 1gdf.plot(ax=ax, color=”grey”, edgecolor=”white”, linewidth=0.2) # 2plt.title(‘Mälmo Neighbourhoods’, fontsize=40, fontname=”Palatino
Linotype”, color=”grey”) # 3ax.axis(“off”) # 4plt.axis('equal') # 5plt.show() # 6
到目前为止我们做了什么?。我们为地图添加了一个标题(#3),并删除了纬度和经度的轴标签(# 4)。在(#5)中,我们还通过确保比例保持固定来确保地图尽可能地代表现实— plt.axis("equal")
。我们也增加了地图的大小(# 1)。
地图的实际颜色作为参数传递给plot()
函数。我们将地图默认的蓝色改为灰色,并使线条边缘变小为白色。
我们在定制地图方面走得更远了。上面的地图比之前默认的地图更令人愉快。在下一节,我们将了解如何有效地使用颜色。
有效地使用颜色
虽然颜色的选择是个人的,但有许多工具可以通过设计指南帮助你有效地选择颜色。一个这样的工具是*板。这些是可口可乐的一些指导颜色选择。
我将在地图上添加街道线,这样我们就可以为地图选择不同的颜色。
streets = gpd.read_file(“data/malmo-streets.shp”)colors = [“#A1E2E6”, “#E6BDA1”, “#B3A16B”, “#678072”, “#524A4A”]
palplot(colors, size=4)
我们可以使用这些颜色来传递函数参数。
fig, ax = plt.subplots(figsize=(20,20))
streets.plot(ax=ax, color = colors[4], linewidth= 0.2)
gdf.plot(ax=ax, color=colors[1], edgecolor=”white”, linewidth=0.3)plt.title(‘Mälmo Neighbourhoods’, fontsize=40, fontname=”Palatino Linotype”, color=”grey”)
ax.axis(“off”)
#plt.axis(‘equal’)
plt.show()
使用您选择的颜色,您可以增强地图的视觉美感。在这个例子中,我们修改了地图的背景颜色,并给街道线赋予了十六进制颜色#678072。
上图显示了颜色的有效使用,以及如何使用十六进制代码选择颜色。为了给我们的地图添加上下文,我们可以添加底图。
添加底图
早先添加背景底图相当复杂,但是随着上下文库的引入,我们可以很容易地将不同的底图添加到我们的地图中。让我们这样做吧。
fig, ax = plt.subplots(figsize=(16, 18))gdf.to_crs(epsg=3857).plot(ax=ax, color=colors[1], edgecolor=”white”, linewidth=0.3, alpha=0.5) # 2 - Projected plot streets.to_crs(epsg=3857).plot(ax=ax, color = colors[4], linewidth= 0.2)ctx.add_basemap(ax, url=ctx.providers.Stamen.TonerLite) # 3plt.title(‘Mälmo Neighbourhoods’, fontsize=40, fontname=”Palatino Linotype”, color=”grey”)
ax.axis(“off”)
#plt.axis(‘equal’)
plt.show()
我们只是将数据投影到网络墨卡托,然后绘制它(# 2),并添加一个底图,在这种情况下,Stamen Toner Lite 设计。这是带底图的地图。根据您的选择,您可以选择许多不同的底图。
上面的地图有一个上下文底图,与 Geopandas 提供的默认地图相比,视觉效果更好。不要接受默认设置,尝试实验和设计你的地图。有了 Matplotlib 的一些知识,您可以用 Python 创建一个美观的地图。
结论
在本教程中,我们介绍了如何在使用 Geopandas 制作地图时超越默认设置。我们已经看到了如何使用参数调整和定制绘图。我们也分享了如何在你的地图中加入色彩设计。最后,我们很容易地添加了底图,为地图提供了上下文。
本教程的代码可以在这个 Github 仓库中获得。
permalink dissolve GitHub 是 4000 多万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/shakasom/esda/blob/master/Plotting.ipynb)
用 floWeaver 创建漂亮的桑基图
用 Python 升级您的 Sankey 游戏
不久前,我写了“桑基图的什么、为什么和如何”在探索 Matplotlib 的 Sankey 库的过程中,我发现了一个可爱的替代品:floWeaver。如果您正在寻找一个不同风格的 Sankey 风格的图来记录流程,这个博客就是为您准备的。
叙利亚难民及其目的地的桑基图(注:性别/年龄数据系捏造)。
老实说,我希望 floWeaver 比 matplotlib sankey 更容易使用。我最终发现它是不同的,而不是更容易/更难。我确实认为默认设置的图表更吸引我,在默认设置中,您必须有一个愿景,并在 matplotlib sankey 中更有意识地进行设计选择。
当 floWeaver 运行良好时
以下是一些您可能选择 floWeaver 而不是 matplotlib sankey 的情况:
- 您希望跟踪跨节点的流(例如,标记原始输入源,即使它经过了几个步骤)
- 您没有连接到箭头和方向端点(例如,指向上或指向下的流)
- 你可以接受好看的默认值,不需要对字体大小之类的东西进行太多的控制
安装 floWeaver
在 floWeaver GitHub 上找到你需要知道的一切。或者这里是给conda
用户的概要:
安装 floWeaver 本身:
conda install -c conda-forge floweaver
安装 ipysankeywidget 以在 Jupyter 笔记本中显示 sankey 图:
conda install -c conda-forge ipysankeywidget
启用 ipywidgets。这可能已经启用,但确认一下也无妨:
jupyter nbextension enable --py widgetsnbextension --sys-prefix
数据格式
看起来floWeaver
需要列/特性“源”、“目标”和“值”。从这里,您可以添加其他特性,使用您想要的任何列名。这些源和目标将成为您的终端节点的一个选项。如果你真的想绘制一个其他的特征作为你的末端节点,那也可以,但是你的末端节点的一个选项仍然应该在“目标”列中(如果我的推断是正确的)
以下是我将在下面使用的示例的数据表:
叙利亚难民数据表
扩大的叙利亚难民数据表(捏造的数据)
家庭财务数据表
使用 floWeaver
首先,在 Rick Lubton 和团队的教程、文档和例子中找到这个和更多。我强烈建议您使用它们( docs , GitHub repo )来进一步调查。
制作桑基图
简单难民数据集的桑基图。
- 从导入和导入数据集开始。这里我们使用上面显示的简单难民数据集。
import pandas as pd
from floweaver import *refugees = pd.read_csv('refugees.csv')
2.设置您的节点、顺序、包和分区
节点是桑基图的聚集点。你的流动将要流入和流出的固定“位置”。在本例中,左侧是叙利亚,右侧是七个目的地。您可以根据需要命名节点组。每个“进程组”将显示为一组垂直排列的节点。注意 ProcessGroup 接受一个列表作为它的参数,但是在节点名称是否重复方面是灵活的。
nodes = {
'start': ProcessGroup(['Syria']), # one (Syria) at the start
'end': ProcessGroup(list(refugees['target'])), # 7 at the end
}
顺序是您希望节点进程组出现的顺序(从左到右)。请注意,这是一个进程组/节点名称列表:
ordering = [[‘start’], [‘end’]]
束是您想要显示的一组连接。这使您可以选择删除任何不必要的连接。包应该是 floWeaver 的列表。捆绑包含连接信息的对象。
bundles = [Bundle('start', 'end')]
即使您已经定义了节点,我们也需要设置分区,以指示我们希望它们被分割的确切方式。使用.unique()
让我们不必单独列出每个目的地。
nodes['start'].partition = Partition.Simple('source', ['Syria'])
nodes['end'].partition = Partition.Simple('target',
refugees['target'].unique())
3.创建并显示您的桑基图
使用上面的节点、排序和捆绑包创建 Sankey:
sdd = SankeyDefinition(nodes, bundles, ordering)
要显示桑基,将 floWeaver 的weave(*sankey_definition, dataset, measures=’value’, link_width=None, link_color=None, palette=None*)
与.to_widget()
结合使用,以显示在 Jupyter 笔记本中。如果要保存图像,请在.to_widget()
后添加.auto_save_png(*filename*)
。
weave(sdd, refugees).to_widget().auto_save_png('syria1.png')
将较小的功能捆绑到一个组合类别中
正如你在上面看到的,我们有一些非常小的流量。除了修改 end nodes 分区之外,您可以使用与上面相同的代码将它们捆绑在一起。不是列出同一级别上的所有节点,而是为您想要进行的任何分组创建一个(名称,节点列表)元组。
nodes[‘end’].partition = Partition.Simple(‘target’,
[‘Turkey’,
‘Lebanon’,
‘Jordan’,
‘Europe’,
(‘Other’, [‘Iraq’, ‘Egypt’, ‘USA’])])
您需要重新创建 Sankey 定义并重新编制 Sankey:
sdd = SankeyDefinition(nodes, bundles, ordering)
weave(sdd, refugees).to_widget().auto_save_png('syria_other.png')
叙利亚难民与伊拉克、埃及和美国捆绑成“其他”的桑基图
突出显示不同的(非目标)特征
为了突出显示一个非目标特性,再次调整分区。在这里,我们按地区划分。
# set end partition to region
nodes[‘end’].partition = Partition.Simple(‘region’,
refugees[‘region’].unique())# create SankeyDefintion, display and save image
sdd = SankeyDefinition(nodes, bundles, ordering)
weave(sdd, refugees).to_widget().auto_save_png('syria_region.png')
按地区分列的锡兰难民目的地桑基图。
添加路点(中间步骤)
突出流程中不同特征或步骤的另一种方式是通过路点(额外的节点集)。
按地区和目的地分列的叙利亚难民目的地桑基图。
要添加此路点,我们需要
- 为我们的航点(区域)创建一个新的分区
- 将航路点添加到我们的节点字典中,提供给
Waypoint
新的区域划分 - 将航点添加到我们的订购列表中(中间)
- 通过添加 waypoints=[ node_name 作为参数,将航点添加到我们的包中。
# create region partition
region = Partition.Simple('region', refugees['region'].unique())# set the "nodes"
nodes = {
'start': ProcessGroup(['Syria']),
'middle': Waypoint(region),
'end': ProcessGroup(list(refugees['target'].unique())),
}# set the order of the nodes left to right
ordering = [['start'], ['middle'], ['end']]# set the "bundle" of connections
bundles = [Bundle('start', 'end', waypoints=['middle'])]# partition the groups for display
nodes['start'].partition = Partition.Simple('source', ['Syria'])
nodes['end'].partition = Partition.Simple('target',
refugees['target'].unique())# set color palette
palette = {'North America': '#ED533B', 'Middle East': '#3CAEA3',
'Europe': '#F6D55C'}# create sankey with flow_partition
sdd = SankeyDefinition(nodes, bundles, ordering,
flow_partition=region)# display sankey (with color palette) and save as png
weave(sdd, refugees, palette=palette).to_widget().auto_save_png('syria_waypt.png')
调整显示功能
要调整颜色,创建一个调色板字典,在 SankeyDefinition 中添加您想要着色的分区作为“flow_partition”,并将您的调色板添加到weave()
。注意:这段代码使用了上面的节点、包、排序和数据集。
# set color palette
palette = {'North America': '#ED533B', 'Middle East': '#3CAEA3',
'Europe': '#F6D55C'}# create sankey with flow_partition for color
sdd = SankeyDefinition(nodes, bundles, ordering,
flow_partition=region)# display sankey (with color palette) and save as png
weave(sdd, refugees, palette=palette) \
.to_widget().auto_save_png('syria_waypt.png')
要调整 Sankey 输出的大小,创建一个大小字典并将其添加为.to_widget()
的参数
# image size
size = dict(width=570, height=300)# create sankey diagram
weave(sdd, refugees).to_widget(**size).auto_save_png('syria_sm.png')
这是最后一个例子,一起展示。
假设家庭资金流动的桑基图
# set the nodes
nodes = {
'start': ProcessGroup(list(household['source'])),
'category': Waypoint(Partition.Simple('category',
household['category'].unique())),
'end': ProcessGroup(list(household['target'])),
}# set the order of the nodes left to right
ordering = [['start'],
['category'],
['end']]# set the "bundle" of connections you want to show
bundles = [Bundle('start', 'end', waypoints=['category'])]# add the partitions
partner = Partition.Simple('source', household['source'].unique())
nodes['start'].partition = partner
nodes['end'].partition = Partition.Simple('target',
household['target'].unique())# add the color palette
palette = {'partner 1': '#027368', 'partner 2': '#58A4B0'}# create the sankey diagram
sdd = SankeyDefinition(nodes, bundles, ordering,
flow_partition=partner)# display the sankey diagram
weave(sdd, household, palette=palette).to_widget().auto_save_png('./images/household.png')
你可以在上面看到,颜色划分并不总是产生我想要的结果,但是在这一点上,我不确定如何调整它。对我来说,根据航路点给流着色看起来最可爱,但不一定能实现桑基图的目标——别忘了把它放在最前面!
更美观的家用桑基图,但不是很好地显示了乐趣、必需品和拯救每个伙伴的贡献。
和往常一样,请查看 GitHub repo 中的完整代码。编码快乐!
叙利亚日落。照片由蒂莫西·卡西斯在 Unsplash 上拍摄
以 scikit-learn 方式创建基准模型
来源: pixabay
了解如何为分类和回归问题创建一系列基准模型
我真正喜欢scikit-learn
的是我经常偶然发现一些我以前没有意识到的功能。我最*的“发现”是DummyClassifier
。虚拟估计器不从特征中学习任何模式,它使用简单的试探法(从目标中推断)来计算预测。
我们可以使用那个简单的估计量作为我们更高级模型的一个简单的健全性检查。为了通过检查,所考虑的模型应该产生比简单基准更好的性能。
在这篇短文中,我将展示如何使用DummyClassifier
,并解释可用的启发式方法。
设置
我们需要导入所需的库:
在本文中,我编写了一个简单的函数,打印一些选择的评估指标来评估模型的性能。除了准确性之外,我还包括了在类不*衡的情况下有助于评估性能的指标(这个列表并不详尽)。
加载数据
对于本文,我使用著名的 Iris 数据集。为了进一步简化问题,我将多类问题转化为二元分类,同时引入类不*衡。这个练习的目的是预测给定的植物是属于杂色类还是“其他”(Setosa 或 Virginica)。
改造后班级比例为 2:1。
训练模型前的最后一步是使用train_test_split
函数将数据分成训练集和测试集:
因为我们有意引入了阶级不*衡
探索DummyClassifier
的变种
DummyClassifier
估计器提供了一些可能的规则(称为策略),我们可以用它们来确定基准类预测。下面我将简要描述它们,并给出相应的代码片段来展示实现。
“持续”战略
可以说是虚拟分类器的最简单的变体。这个想法是用一个值代替所有的标签。这种变体的一个可能的用例是,当我们想要根据 F1 分数来评估一个潜在的评估者时,F1 分数是精确度和召回率的调和*均值。
我使用少数类(Versicolour)来创建天真的预测。同样值得一提的是,这些特性在确定预测值时不起任何作用,它们只是为了匹配scikit-learn
的 fit + predict 风格。在下面的总结中,我们看到我们实现了完美的回忆,F1 得分为 0.5。
{'accuracy': 0.3333333333333333,
'recall': 1.0,
'precision': 0.3333333333333333,
'f1_score': 0.5}
“统一”战略
在这个变体中,从可用的类中随机地(一致地)生成天真的预测。
运行代码会产生以下摘要:
{'accuracy': 0.4,
'recall': 0.4,
'precision': 0.25,
'f1_score': 0.3076923076923077}
此外,我们使用Counter
检查预测标签的分布:
Counter({0: 14, 1: 16})
正如均匀绘制所预期的,目标的分布是随机的,并不反映标签的真实分布。
“分层”战略
为了解决前面提到的缺点,我们可以使用stratified
规则。预测是随机生成的,但是保留了来自训练集的类的分布。正如在分层的train_test_split
中一样。
运行代码会产生以下摘要:
{'accuracy': 0.6333333333333333,
'recall': 0.3,
'precision': 0.42857142857142855,
'f1_score': 0.3529411764705882}
我们再一次查看每一类的观察次数:
Counter({0: 23, 1: 7})
使用stratified
策略会产生类似于我们在观察值中看到的分布。
“最频繁”策略
策略的名称很容易理解——预测值是标签中最常见的值。
运行代码会产生以下摘要:
{'accuracy': 0.6666666666666666,
'recall': 0.0,
'precision': 0.0,
'f1_score': 0.0}
运行代码时,我们收到以下警告:UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples.
原因是我们选择的策略。由于多数类用于所有预测,因此没有少数类的观测值,并且无法计算精度和召回率等指标。
“优先”策略
与‘most_frequent’
策略非常相似的策略。伪分类器总是预测最大化类先验的类。区别在于拟合分类器的predict_proba
方法。在‘prior’
策略的情况下,它返回类别先验(类别概率由训练集中标签的比率决定)。
‘prior’
和‘most_frequent’
策略的结果是相同的,所以为了简洁起见,我不再展示它们。
值得一提的是,对于所有策略,predict
方法完全忽略了输入数据,用作预测的值是在拟合阶段确定的(或在使用‘constant’
策略时由我们提供)。
训练一个实际的评估者
在尝试了创建基准模型的多种策略之后,是时候训练一个简单的分类器并评估它是否优于基准了。为此,我使用带有默认设置的决策树分类器(除了为再现性指定的random_state
)。
通过检查下面的总结,我们可以清楚地表明决策树优于所有的基准。
{'accuracy': 0.9473684210526315,
'recall': 0.9230769230769231,
'precision': 0.9230769230769231,
'f1_score': 0.9230769230769231}
DummyRegressor
我已经展示了如何使用scikit-learn
的DummyClassifier
来评估分类任务的基准模型。自然,回归问题存在一个类比估计量——DummyRegressor
。我就不赘述了,因为用法和DummyClassifier
很像。为了完整起见,我只提到可用的策略:
'mean’
—估计器使用目标(来自训练集)的*均值作为预测'median’
—估计器使用目标(训练集)的中值作为预测'constant’
—估计器使用常数值作为预测值'quantile’
—估计器使用目标(训练集)的指定分位数作为预测
与DummyClassifier
类似,predict
方法忽略计算预测的输入数据。
结论
在本文中,我展示了如何使用简单的试探法使用scikit-learn
中可用的估计器来创建基准模型。我们可以使用这样的模型进行健全性检查,看看我们的评估者是否比天真的基线表现得更好。如果不是,我们应该调查是什么导致了这个问题(也许这些特性没有帮助或者类的不*衡影响了结果)。
您可以在我的 GitHub 上找到本文使用的代码。一如既往,我们欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。
我最*出版了一本关于使用 Python 解决金融领域实际任务的书。如果你有兴趣,我贴了一篇文章介绍这本书的内容。你可以在亚马逊或者 Packt 的网站上买到这本书。
在 Matplotlib 中选择和创建色彩映射表
Elena Mozhvilo 在 Unsplash 上的照片
MATPLOTLIB 简介
从颜色列表中创建和定制自己的色彩映射表的指南
答几乎所有使用 Python 编程语言的程序员都知道 Matplotlib。这是最常用的库之一。它是一个多*台库,可以运行许多操作系统,由 John Hunter 于 2002 年创建。
现在人们开始开发比 Matplotlib 更简单更现代风格的新包,像 Seaborn,Plotly,甚至熊猫都用 Matplotlib 的 API 包装器。但是,我认为 Matplotlib 仍然在许多程序员的心中。
如果你需要学习使用 Matplotlib 的入门知识,你可以看看这个链接。
[## 用 Matplotlib 实现数据可视化——绝对初学者第一部分
这是使用 Matplotlib 和 Jupyter Notebook(一个强大的 Python 模块)可视化我们的数据的教程。
medium.comI](https://medium.com/@bahrulsg/data-visualization-with-matplotlib-for-absolute-beginner-part-i-655275855ec8)
Matplotlib 中的色彩映射表
在可视化 3D 绘图时,我们需要色图来区分 3D 参数并做出一些直觉。科学上讲,人脑是根据看到的不同颜色来感知各种直觉的。
Matplotlib 提供了一些您可以使用的漂亮的色彩映射表,例如顺序色彩映射表、发散色彩映射表、循环色彩映射表和定性色彩映射表。出于实用的目的,我没有更详细地解释它们之间的区别。如果我向您展示 Matplotlib 中每个分类色彩映射表的示例,我想这将会很简单。
以下是一些连续色彩映射表的示例(并非全部)。
由 Matplotlib 提供的连续色彩映射表。
Matplotlib 会给你绿色的作为默认的颜色图。然后,接下来是 Matplotlib 中发散、循环、定性和杂项色彩映射表的示例。
Matplotlib 中的色彩映射表。
你自己的彩色地图
你对所有提供的颜色图都不感兴趣吗?还是需要其他花哨的彩图?如果是的话,你需要看完这篇文章。我将指导你定制和创建你自己的色彩映射表。
但是在定制它之前,我会给你看一个使用色彩映射表的例子。我使用' RdYlBu_r '色彩映射表来可视化我的数据。
Matplotlib 中 RdYlBu_r 色彩映射表的示例(图片由作者提供)
让我们修改你自己的色彩映射表。
首先,我们需要使用下面的代码创建可视化的模拟数据
# import some libraries / modules
import numpy as np
import matplotlib.pyplot as plt# create mock data
data = np.random.random([100, 100]) * 10
数据变量是一个数组,由 100 x 100 个从 0 到 10 的随机数组成。你可以通过写这段代码来检查它。
要可视化的模拟数据(图片由作者提供)
之后,我们将使用下面的简单代码显示带有默认颜色图的模拟数据。
plt.figure(figsize=(7, 6))
plt.pcolormesh(data)
plt.colorbar()
代码将向您显示这样的图形。
Colormesh 使用默认色彩映射表可视化模拟数据(图片由作者提供)
正如我之前提到的,如果你没有定义你使用的色彩映射表,你将得到默认的色彩映射表,命名为' viridis '。
接下来,我将用这段代码将彩色地图从'绿色改为'地狱彩色地图
plt.pcolormesh(data, **cmap='inferno'**)
你会得到这样的结果
Colormesh 使用“inferno”色图可视化模拟数据
修改色彩映射表
现在,要修改色彩映射表,您需要在 Matplotlib 中导入以下子库。
from matplotlib import cm
from matplotlib.colors import ListedColormap,LinearSegmentedColormap
要修改颜色图中颜色类别的数量,您可以使用以下代码
new_inferno = cm.get_cmap('inferno', 5)# visualize with the new_inferno colormaps
plt.pcolormesh(data, **cmap = new_inferno**)
plt.colorbar()
并且会得到这样一个结果
修改后的地狱只有 5 种颜色。
下一步是修改色图中的范围颜色。我会给你一个“hsv”色图的例子。你需要用这个图来了解颜色的范围。
hsv 彩色地图(图片由作者提供)
如果我们只想使用绿色(大约 0.3)到蓝色(0.7),我们可以使用下面的代码。
# modified hsv in 256 color class
hsv_modified = cm.get_cmap('hsv', 256)# create new hsv colormaps in range of 0.3 (green) to 0.7 (blue)
newcmp = ListedColormap(hsv_modified(np.linspace(0.3, 0.7, 256)))# show figure
plt.figure(figsize=(7, 6))
plt.pcolormesh(data, **cmap = newcmp**)
plt.colorbar()
它会给你一个这样的数字
修改“hsv”色彩映射表的范围颜色(图片由作者提供)
创建您自己的色彩映射表
要创建自己的色彩映射表,至少有两种方法。首先,您可以在 Matplotlib 中组合两个连续的色彩映射表。第二,你可以在 RGB 中选择和组合你喜欢的颜色来创建彩色地图。
我们将向您演示如何将两个连续的色彩映射表合并为一个新的色彩映射表。我们想把“橙色”和“蓝色”结合起来。
结合蓝色和橙色的色彩映射表创建一个新的(作者图片)
你可以仔细阅读这段代码。
# define top and bottom colormaps
top = cm.get_cmap('Oranges_r', 128) # r means reversed version
bottom = cm.get_cmap('Blues', 128)# combine it all
newcolors = np.vstack((top(np.linspace(0, 1, 128)),
bottom(np.linspace(0, 1, 128))))# create a new colormaps with a name of OrangeBlue
orange_blue = ListedColormap(newcolors, name='OrangeBlue')
如果您使用‘orange blue’色彩映射表来可视化模拟数据,您将得到如下图。
橙色蓝色地图(图片由作者提供)
下一步是从你喜欢的两种不同的颜色创建一个色图。在这种情况下,我将尝试从黄色和红色创建它,如下图所示
将被组合的两种颜色(图片由作者提供)
首先,你需要创建黄色色图
# create yellow colormapsN = 256yellow = np.ones((N, 4))yellow[:, 0] = np.linspace(255/256, 1, N) # R = 255
yellow[:, 1] = np.linspace(232/256, 1, N) # G = 232
yellow[:, 2] = np.linspace(11/256, 1, N) # B = 11yellow_cmp = ListedColormap(yellow)
和红色地图
red = np.ones((N, 4))red[:, 0] = np.linspace(255/256, 1, N)
red[:, 1] = np.linspace(0/256, 1, N)
red[:, 2] = np.linspace(65/256, 1, N)red_cmp = ListedColormap(red)
下图显示了您创建的黄色和红色色彩映射表的可视化效果
左:红色 _cmp 和右:黄色 _cmp(图片由作者提供)
之后,您可以使用前面的方法组合它。
newcolors2 = np.vstack((yellow_cmp(np.linspace(0, 1, 128)),
red_cmp(np.linspace(1, 0, 128))))double = ListedColormap(newcolors2, name='double')plt.figure(figsize=(7, 6))plt.pcolormesh(data, cmap=double)
plt.colorbar()
你会得到这样一个数字
你自己的彩色地图:D
您还可以使用此代码调整色彩映射表的方向、延伸和焊盘距离。
plt.figure(figsize=(6, 7))plt.pcolormesh(data, cmap = double)
plt.colorbar(orientation = 'horizontal', label = 'My Favourite Colormaps', extend = 'both', pad = 0.1)
你会看到一个这样的图形
修改的色彩映射表
结论
为你的色彩图选择正确的颜色是很重要的,因为这是人类思维的表现。颜色表达思想、信息和情感。Matplotlib 提供了许多颜色图,但是有些人在选择颜色图时有不同的倾向。如果他们想建立自己的品牌,他们需要创建自己的色彩映射表。我希望这个故事可以帮助你在 Matplotlib 中创建和修改你自己的色彩映射表。
如果你喜欢这篇文章,这里有一些你可能喜欢的其他文章:
[## 使用 Matplotlib 可视化数据的 5 个强大技巧
如何使用 LaTeX 字体,创建缩放效果,发件箱图例,连续错误,以及调整框填充边距
towardsdatascience.com](/5-powerful-tricks-to-visualize-your-data-with-matplotlib-16bc33747e05) [## 用于科学绘图的 Matplotlib 样式
为您的科学数据可视化定制 Matplotlib
towardsdatascience.com](/matplotlib-styles-for-scientific-plotting-d023f74515b4) [## 使用 Matplotlib 实现 Python 数据可视化—第 1 部分
完成了从基础到高级的 Python 绘图的 Matplotlib 教程,包含 90 多个示例
towardsdatascience.com](/visualizations-with-matplotlib-part-1-c9651008b6b8) [## 在 Matplotlib 中自定义多个子情节
使用 subplot、add_subplot 和 GridSpec 在 Matplotlib 中创建复杂 subplot 的指南
towardsdatascience.com](/customizing-multiple-subplots-in-matplotlib-a3e1c2e099bc) [## Vaex 大数据简介—读取 12.5 亿行的简单代码
用 Python 高效读取和可视化 12.5 亿行星系模拟数据
towardsdatascience.com](/introduction-to-big-data-a-simple-code-to-read-1-25-billion-rows-c02f3f166ec9)
仅此而已。感谢您阅读这个故事。喜欢就评论分享。我还建议您关注我的帐户,以便在我发布新故事时收到通知。
用 Numpy select()和 where()方法在 Pandas 上创建条件列
一些最有用的熊猫把戏
帕斯卡尔·贝纳登在 Unsplash 上拍摄的照片
Pandas 是一个令人惊叹的库,它包含大量用于操作数据的内置函数。虽然内置函数能够执行有效的数据分析,但是合并其他库中的方法为 Pandas 增加了价值。
在本文中,我们将看看如何用 Numpy select()
和where()
方法在 Pandas 上创建条件列
请查看我的 Github repo 获取源代码
从两个选项中创建条件列
假设我们有一个关于水果店的数据集。
df = pd.DataFrame({
'fruit': ['Apple', 'Banana', 'Apple', 'Banana'],
'supplier': ['T & C Bro', 'T & C Bro', 'JM Wholesales', 'JM Wholesales'],
'weight (kg)': [1000,2000,3000,4000],
'customer_rating': [4.8,3.2, 4.2, 4.3]
})
水果店数据集(作者制作)
我们可以看到,商店从两个供应商处购买了水果“ T & C Bro ”和“ JM 批发”。它们的价目表分别保存如下:
# T & C Bro
**tc_price** = pd.DataFrame({
'fruit': ['Apple', 'Banana', 'Orange', 'Pineapple'],
'price (kg)': [1.1, 2, 2.9, 3.1]
})# JM Wholesales
**jm_price** = pd.DataFrame({
'fruit': ['Apple', 'Banana', 'Orange', 'Pineapple'],
'price (kg)': [1.2, 1.8, 4, 6]
})
我们希望从供应商的价目表中检索价格,将它们组合起来,并将结果添加到新列中。预期的输出是:
作者制作的图像
这个计算中棘手的部分是,我们需要有条件地检索 价格(公斤) (基于供应商和水果),然后将其组合回水果店数据集。
对于这个例子,一个改变游戏规则的解决方案是结合 Numpy where()
函数。一行代码就可以解决检索和合并。
第一步:将fruit
列设置为索引
df = df.**set_index('fruit')**tc_price = tc_price.**set_index('fruit')**jm_price = jm_price.**set_index('fruit')**
通过调用set_index('fruit')
将 果 列设置为所有数据集的索引。这很重要,所以我们可以稍后使用loc[df.index]
来选择用于值映射的列。
步骤 2:将 Numpy where()
与 Pandas 数据框架合并
Numpy where(**condition**, **x**, **y**)
方法[1]根据condition
返回从x
或y
中选择的元素。最重要的是,这个方法可以接受类似数组的输入,并返回类似数组的输出。
df['price (kg)'] = **np.where**(
**df['supplier'] == 'T & C Bro'**,
tc_price**.loc[df.index]['price (kg)']**,
jm_price**.loc[df.index]['price (kg)']**
)
通过执行上述语句,您应该得到如下输出:
作者制作的图像
有点困惑?以下是一些细节:
df['supplier'] == 'T & C Bro'
返回一个布尔数组df.index
返回['Apple', 'Banana', 'Apple', 'Banana']
(由 步骤 1 设定的指标)。并且tc_price.loc[df.index]
和jm_price.loc[df.index]
都基于标签df.index
返回相同长度的数据帧。
np.where()
如何工作
从两个以上的选项中创建条件列
我们已经学习了如何从两个数据集创建一个条件列。如果超过 2 个数据集呢,例如,水果店数据集中有 3 个不同的供应商。
有 3 个供应商的水果店数据集(作者制作)
对于 2 个以上的数据集/选择,我们可以使用 Numpy select()
方法。让我们借助一个例子来看看它是如何工作的。
步骤 1:将价格列表组合在一起,并将fruit
列设置为索引
第一步是将所有价格列表合并到一个数据集中。
之后,设置 果 列为索引。
df_3 = df_3.**set_index('fruit')**df_price = df_price.**set_index('fruit')**
步骤 2:将 Numpy select()
与 Pandas 数据框架合并
Numpy select(**condlist**, **choicelist**)
方法根据condlist
中的条件返回一个从choicelist
中的元素提取的数组。
args = df_price.loc[df_3.index]**conds = [
df_3['supplier'] == 'T & C Bro',
df_3['supplier'] == 'JM Wholesales',
df_3['supplier'] == 'Star Ltd.',
]****choices = [
args['T & C Bro'],
args['JM Wholesales'],
args['Star Ltd.'],
]**df_3['price (kg)'] = np.select(**conds**, **choices**)
基本上,
- 如果条件
df_3['supplier'] == 'T & C Bro'
满足,则从args['T & C Bro']
获取输出元素。 - 如果条件
df_3['supplier'] == 'JM Wholesale'
被满足,它从args['JM Wholesale']
获取输出元素。 - 如果条件
df_3['supplier'] == 'Star Ltd.'
满足,它从args['Star Ltd.']
获取输出元素。
通过执行上述语句,您应该得到如下输出:
np.select()
的输出
好了
感谢阅读。
请在我的 Github 上的笔记本中查看源代码。
如果你对机器学习的实用方面感兴趣,请继续关注。
你可能会对我的其他一些熊猫文章感兴趣:
- 何时使用熊猫变换()函数
- 熊猫数据透视表实用介绍 _table()函数
- 使用熊猫方法链接提高代码可读性
- 在 Pandas 数据框架中处理日期时间
- 熊猫阅读 _csv()你应该知道的招数
- 用 Pandas read_csv()解析日期列应该知道的 4 个技巧
更多可以从我的 Github 中找到
参考
- [1] Numpy 文档— where()
- [2] Numpy 文档— select()
创建用于熊猫分组的自定义聚合
我开始使用带有自定义聚合的 groupby,我想与您分享我所学到的东西。
Pandas groupby
是一个函数,你可以在数据帧上使用它来分割对象,应用一个函数,并组合结果。当您想要将大量数据分组并为每个组计算不同的操作时,此函数非常有用。如果您将聚合函数与您的groupby
一起使用,则每次函数运行时,该聚合将为每个组返回一个值。形成组后,可以对分组的数据运行一个或多个聚合。
我今天使用的数据集是 Kaggle 上的亚马逊 50 大畅销书。这个数据集有一些我们可以使用的很好的数字列和类别。导入数据集后,我们可以快速查看一个使用head(1)
抓取第一行并用.T
转置数据的例子。这里我们可以看到流派是groupby,
的一个很好的分类栏,我们可以汇总用户评级、评论、价格和年份。
df = pd.read_csv("bestsellers_with_categories.csv")
print(df.head(1).T)>>> 0
>>> Name 10-Day Green Smoothie Cleanse
>>> Author JJ Smith
>>> User Rating 4.7
>>> Reviews 17350
>>> Price 8
>>> Year 2016
>>> Genre Non Fiction
现在我们已经快速浏览了这些列,我们可以使用groupby
对 Genre 的数据进行分组。在应用groupby
之前,我们可以在这个数据集中看到两个体裁类别,非小说和小说,这意味着我们将有两组数据要处理。如果我们想考虑作者或书名,我们可以和小组一起玩,但我们现在将坚持流派。
df.Genre.unique()>>> array(['Non Fiction', 'Fiction'], dtype=object)group_cols = ['Genre']
ex = df.groupby(group_cols)
为系列数据创建自定义聚合
在设置我们的组之后,我们可以开始创建自定义聚合。我曾在类似这样的函数中使用自定义聚合,在不同条件下执行计算或聚合之前过滤掉特定的值。当我测试聚合函数时,我喜欢从一个小系列开始来验证输出,如下图所示。
ser = pd.Series([1,10,4,2,10,10])
一旦我们有了一系列要测试的数据,我们就可以开始创建聚合函数了。下面我创建了三个聚合函数。第一个和第二个函数是non_nan_mean
和standard_deviation
,它们验证序列不为空,移除任何 NA 值,并执行*均值或标准偏差计算。最后一个聚合是一个mean_lower_rating
,它消除任何大于 5 的上限值,并计算下限值的*均值。
def non_nan_mean(x):
if x.empty:
return None
else:
x = x.dropna()
return np.mean(x)def standard_deviation(x):
if x.empty:
return None
else:
x = x.dropna()
return np.nanstd(x)
def mean_lower_rating(x):
upper = []
for val in x:
if val > 5:
continue
else:
upper.append(val)
return np.mean(upper)
一旦你定义了你的聚合函数,根据你的需要,你可以应用你的序列来测试。我打印出我的值来查看,如下所示。
print(non_nan_mean(ser))
print(standard_deviation(ser))
print(mean_lower_rating(ser))>>> 6.166666666666667
>>> 3.9334745737353156
>>> 2.3333333333333335
通过 Groupby 应用聚合
在理解了您正在使用的数据集并使用少量数据测试了聚合函数之后,您可以应用使用前面提到的agg
函数创建的聚合函数。这个函数作用于 dataframes,它允许我们聚合指定轴上的数据。应用这些聚合的一个简单方法是创建一个列表,并将该列表作为参数传递。此方法会将您的聚合应用到组数据框架中的所有数字列,如下面的示例一所示。在这个例子中,你可以看到我正在调用ex
,这是前面的分组输出。
示例 1:
aggs = [non_nan_mean, standard_deviation,mean_lower_rating]
ex2 = ex.agg(aggs)
print(ex2)
Jupyter notebook 中生成的字典聚合的输出。
从输出中可以看出,mean_lower_rating
聚合在特定的列上表现不佳,这是由为特定列设计的函数(即用户评级)造成的。考虑到这一点,我们可以看看将聚合参数传递到agg
函数的不同方式,这将清理输出。
将参数传递给agg
的另一种方法是开发一个字典。字典将列名映射到要运行的聚合函数。这种方法的一个例子见例二。
示例 2:
aggs_by_col = {'Reviews': [non_nan_mean],
'Price': [non_nan_mean,standard_deviation],
'User Rating': [mean_lower_rating]}
ex1 = ex.agg(aggs_by_col)
print(ex1)
Jupyter notebook 中生成的字典聚合的输出。
如前所述,如果您不想使用mean_lower_rating
聚合将所有聚合应用于所有列,则首选此方法。定义聚合应用于哪些列的过程对于大型数据集非常有益,因为它清理了输出,只提供了您想要查看的数据。这种应用聚合的方法允许我只为用户评级指定mean_lower_rating
聚合,并将其他聚合指定到它们各自的列。
最后的想法
将 Pandas groupby
与agg
函数一起使用将允许您将数据分组到不同的类别中,并将您的数字列聚合到每个聚合函数的一个值中。如果您有创建自定义聚合函数的用例,您可以编写这些函数来接收一系列数据,然后使用列表或字典将它们传递给agg
。如果希望所有的聚合都应用于所有的数字列,可以传递一个列表;如果要指定哪些聚合应用于哪些列,可以传递一个字典。如果您愿意,还可以使用 lambda 函数来创建聚合,这一点我在本文中没有涉及。
附加阅读
- 熊猫。DataFrame.groupby 文档
- pandas . core . group by . data frame group by . aggregate文档
- 分组依据:分解-应用-合并文档
- 熊猫。DataFrame.agg 文档
如果你想阅读更多,看看我下面的其他文章吧!
当我参加大学讲座时,最常被问到的问题是“我需要具备什么技能?”
towardsdatascience.com](/top-8-skills-for-every-data-scientist-79e6b1faf3e1) [## 停止浪费你的时间,咨询一个主题专家
在从事数据科学项目时,请一位主题专家来审查您的工作可能会有所帮助。
towardsdatascience.com](/stop-wasting-your-time-and-consult-a-subject-matter-expert-f6ee9bffd0fe) [## 采用现有数据科学项目的成功关键
代码本来可能不是你的,但现在是你的了。那么接下来呢?
towardsdatascience.com](/keys-to-success-when-adopting-a-pre-existing-data-science-project-9f1225fb0275) [## 不要太骄傲而不愿寻求帮助
如果你被一个 bug 卡住了或者感到不知所措,你可以寻求你需要的帮助。
towardsdatascience.com](/dont-be-too-proud-to-ask-for-help-76f21d16f318) [## 数据可视化的前 3 篇文章
如果您想更好地构建数据可视化,这些文章很有帮助。
towardsdatascience.com](/top-3-articles-for-data-visualization-956a08a54b04)
为深度学习项目创建自定义图像数据集
下载图像数据集的一些有用的浏览器扩展
作者图片
这个周末,我为我学龄前的孩子制作了一个简单的水果分类器。这是一个简单的图像分类应用程序,可以预测图像中的水果。我把它作为一个游戏呈现给我的儿子,看谁先预测到这个名字——计算机还是人类:)。这是一个应用程序的预览。
水果分类器应用程序(作者视频)
对于这个应用程序,我需要下载许多水果的图像来训练图像分类器。在这个过程中,我发现了一些浏览器扩展,这使得批量下载图像变得非常容易,我已经编译并在本文中展示了它们。
然而,在开始使用扩展之前,有两件重要的事情需要记住:
版权问题
不要下载任何违反版权条款的图片。有时,没有所有者的许可,你不能复制版权图像。本文中下载的图片仅用于教育目的。
下载设置
确保您的下载设置中没有选择“在下载前询问每个文件的保存位置”,否则,下载器会询问您是否允许下载每个文件。不可取。下面的片段演示了访问该选项的过程。
下载设置(按作者分类的视频)
这篇文章是寻找好数据集的完整系列文章的一部分。以下是该系列中包含的所有文章:
第 1 部分 : 为数据分析任务获取数据集——高级谷歌搜索
第 2 部分 : 为数据分析任务寻找数据集的有用站点
第三部分 : 为深度学习项目创建定制图像数据集
第四部分 : 毫不费力地将 HTML 表格导入谷歌表单
第 5 部分 : 使用 Camelot,从 pdf 中提取表格数据变得很容易。
第六部分 : 从 XML 文件中提取信息到熊猫数据框架
第 7 部分 : 磨练您探索性数据分析技能的 5 个真实世界数据集
现在让我们来看看一些方便下载图像的有用工具:
1.Fatkun 批量下载图像
Fatkun 批量下载图像是一个强大和方便的浏览器扩展,从网上下载图像。它的一些功能包括:
- 可以根据分辨率或链接过滤图像
- 创建自定义规则来下载所需的图像,以及
- 能够批量重命名和批量下载图像
使用
现在让我们下载苹果水果的图像,因为我们想要创建一个水果分类检测器。因为展示比写过程更容易,所以我包含了一个简短的视频来一步一步地展示下载过程。
Fatkun 批量下载图像演示(视频由作者提供)
2.Imageye —图像下载器
Imageye 是另一个浏览器扩展,允许你下载网页上的所有图片。Imageye 还为您提供了以下功能:
- 基于像素宽度和高度过滤图像。您也可以根据图像的 URL 过滤图像。
- 像 Fatkun 一样,你可以一次批量下载所有图片,或者手动选择你想要下载的图片。
🔗下载扩展的链接
使用
Imageye —图像下载程序演示(视频由作者提供)
3.下载所有图像
这个 Chrome 扩展从一个网页上下载所有的图片,并把它们打包成一个 zip 文件。它不能根据图片的大小来过滤图片,但对于从 Unsplash 等网站批量下载图片来说却是极好的,因为 Unsplash 只提供图片。它分析当前浏览器页面以识别图像,然后将它们下载到一个 zip 文件中。单击右上角的扩展图标开始下载图像。它会给你一个估计需要多长时间才能完成。
使用
下载所有图像扩展演示(视频由作者提供)
4.ImageAssistant 批量图像下载程序
ImageAssistant 批量图像下载 r 是一个图像提取器,用于从网页中嗅探、分析和批量下载图像。它非常灵活,并提供了许多定制图像下载的方法。例如,您可以提取网页上的图片或预取图像链接,甚至批量提取图像的 URL。此外,图像过滤器还提供了通过图像扩展类型或分辨率大小过滤图像类型显示的选项。
🔗 链接下载扩展
使用
ImageAssistant 批量图像下载程序演示(视频由作者提供)
5.快速方法
最后一种方法不使用任何浏览器扩展。这个方法是我从 Zacchary Mueller 的Practical-Deep-Learning-for-Coders-2.0资源中获得的,他在 Github 上分享了这个资源。这个代码已经由弗朗西斯科·英厄姆和杰瑞米·霍华德的工作给出,而后者又受到 阿德里安·罗斯布鲁克 的启发。
该方法要求你安装fastai—一个深度学习库,因为它利用了它的一些固有功能。要理解幕后发生的事情,您需要一些库的知识,尤其是数据块 API。解释这超出了本文的范围,但是我将快速浏览下载图像所需的步骤:
- 进入谷歌图片搜索你感兴趣的图片。向下滚动,直到找到您想要下载的图像。假设我们对寻找苹果和芒果的图像感兴趣。
- 在 Chrome/Firefox 中打开 Javascript“控制台”,粘贴以下代码行,然后执行。这将获得所有图像的 URL,并将其保存在一个 CSV 文件中。对每个类别重复该过程。现在你会有两个 CSV 文件,即 apple.csv 和 mango.csv。
urls=Array.from(document.querySelectorAll('.rg_i')).map(el=> el.hasAttribute('data-src')?el.getAttribute('data-src'):el.getAttribute('data-iurl'));
window.open('data:text/csv;charset=utf-8,' + escape(urls.join('\n')));
- 接下来,为您要下载的每一类图像创建一个文件夹。
folders = ['Apple','Mango']
files = ['apple.csv','mango.csv')
- 最后,下载图片
classes = ['Apple','Mango']
path = Path('fruits')path.mkdir(parents=True, exist_ok=True)for i, n in enumerate(classes):
print(n)
path_f = Path(files[i])
download_images(path/n, path_f, max_pics=50)
- 验证图像是否正确
imgs = L()
for n in classes:
print(n)
path_n = path/n
imgs += verify_images(path_n.ls())
显示图像
fruits = DataBlock(blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(0.2),
get_y=parent_label,
item_tfms=RandomResizedCrop(460),
batch_tfms=[*aug_transforms(size=224,max_warp=0),Normalize.from_stats(*imagenet_stats)])
dls = fruits.dataloaders(path, bs=32)
dls.show_batch(max_n=9)
下载的图像(作者提供的图像)
下面是一段展示整个过程的视频:
演示(作者提供的视频)
结论
在本文中,我们看到了收集图像数据以创建深度学习模型的各种方法。您可以选择浏览器扩展,也可以通过编码来获得相同的结果。无论您选择哪种方法,请注意限制和版权问题。此外,不要忘记使用这些工具为您的下一个项目收集数据。同时,这里是本系列中的一些其他文章,您可能会觉得有用
用 matplotlib 创建自定义绘图函数
一个简短的教程,学习如何创建模块化函数,包括使用 matplotlib 绘图
TLDR:使用以下语法定义您自己的函数,这些函数涉及绘制到特定的轴上:
def custom_plot(x, y, ax=None, **plt_kwargs):
if ax is None:
ax = plt.gca()
ax.plot(x, y, **plt_kwargs) ## example plot here
return(ax)def multiple_custom_plots(x, y, ax=None, plt_kwargs={}, sct_kwargs={}):
if ax is None:
ax = plt.gca()
ax.plot(x, y, **plt_kwargs) #example plot1
ax.scatter(x, y, **sct_kwargs) #example plot2
return(ax)
你可以在这个链接找到原始代码库。
介绍
在之前的一篇文章中,我向你展示了如何更好地组织你的数据。我们看到了如何使用支线剧情整齐地显示不同的情节,如何添加自由浮动轴,以及如何使用 GridSpec 轻松地创建轴的*铺组织。
因为这篇文章的重点是总体图的一般结构和表示,所以绘图本身非常简单,因为它们只使用了一个预定义的 matplotlib 函数,比如带有默认参数的.plot
或.hist
。通常,在您在前一篇文章中学习的漂亮的*铺结构中,您将需要绘制自己的特定绘图,该绘图结合了来自不同类型的基本绘图函数的信息以及对其他一些数据生成或数据处理函数的调用。例如,在顶部绘制随机样本的分布及其相应的理论密度函数。
在这里,我将向您展示如何创建您自己的自定义绘图函数,可以通过在您组织的绘图中调用这些函数来轻松使用这些函数,如下所示:
fig, axes = plt.subplots(number_of_subplots)
for ax in axes:
my_custom_plotting_function(ax=ax, function_kwargs)
加上支线剧情的良好组织,这将有助于您最大限度地利用 matplotlib 上的静态绘图(预示着动力学绘图后续教程…也许……)并利用来自不同地块的信息来分享您的数据的综合情况。
基本语法
传递轴
在图形中拥有一系列自定义图的第一步是能够将单个自定义图连接到单个轴。第一步是能够将我们想要绘制的轴传递给我们的自定义函数。这可以像这样简单地完成:
def custom_plot(x, y, ax=None, **plt_kwargs):
if ax is None:
ax = plt.gca()
ax.plot(x, y, **plt_kwargs) ## example plot here
return(ax)
我在那里做了什么?这里第一个相关的部分是ax
的论证。如果你以前用过 seaborn,你可能已经知道如何使用它了。本质上,ax
将获取您想要绘制的轴对象。这可以是一个子图轴或一个简单的自由浮动的嵌入轴。这个想法是,情节的组织部分将在这个功能之外处理,可能由另一个功能处理。
为什么ax
会默认为None
?这可以用下面的句子来更好地回答:
if ax is None:
ax = plt.gca()
我们看到,如果在ax
中没有提供 axes 对象,它默认为None
并触发这个if
条件。在这种情况下,由于没有给定轴,默认情况下,该函数将查找当前图形中使用的最后一个轴,或者如果没有可用的轴,则使用函数.gca
(代表获取当前轴)创建一个轴,并将其用作绘图轴。在函数的最后,我们还返回这个 ax,以防我们想用它进行其他定制(在某些情况下不是必需的,但很实用)。
让我们测试一下,首先在不指定轴的情况下绘图,然后提供一个特定的轴:
# Without providing axes (default to None -> gca())
plt.figure(figsize=(10, 5))
custom_plot([1, 2], [10, 20])
plt.title('Our custom plot with no axes provided (defaults to .gca())')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
# Providing the axes
fig, axes = plt.subplots(2, figsize=(10, 5))# Plotting with our function
custom_plot([2, 3], [4, 15], ax=axes[0])
axes[0].set(xlabel='x', ylabel='y', title='This is our custom plot on the specified axes')# Example plot to fill the second subplot (nothing to do with our function)
axes[1].hist(np.random.normal(size=100))
axes[1].set_title('This plot has nothing to do with our function. Just a histogram of some random numbers')plt.tight_layout() #This to avoid overlap of labels and titles across plots
plt.show()
传递图的关键字参数
到目前为止还不错;我们可以创建一个函数来绘制数据,并可以将它连接到我们的绘图的特定轴(如果没有提供轴,它甚至会自己处理)。那么**plt_kwargs
呢?
如果您不习惯在函数中使用**kwargs
(如在关键字参数中)(参数的实际名称并不重要,您可以将其命名为**kwargs
、**plt_kwargs
、**literally_anything_else
,只要您加上双星号“**”),那么首先创建并使用一个没有**kwargs
的新函数会更容易解释。
顺便说一句,如果你以前真的没有见过这种类型的星号符号,那么在 python 中使用单星号*
和双星号**
在很多情况下都非常有用,无论是在函数内部还是外部,绝对值得在 google 上搜索一下(甚至可以写一篇关于它的博文....可能...).无论如何,回到我们没有**kwargs
的custom_plot
的例子:
def no_kwargs_plot(x, y, ax=None):
if ax is None:
ax = plt.gca()
ax.plot(x, y) ## example plot here
return(ax)plt.figure(figsize=(10, 5))
no_kwargs_plot([1, 2], [10, 20])
plt.show()
没有错误,没有问题…但是,如果你想让线条变粗呢?通常在.plot()
中,我们会简单地将参数linewidth
设置为一个更厚的值。我们可以将linewidth
添加到no_kwargs_plot
的输入列表中,然后像这样将其传递给.plot()
:
def no_kwargs_plot(x, y, ax=None, linewidth=1):
if ax is None:
ax = plt.gca()
ax.plot(x, y, linewidth) ## example plot here
那会解决问题。但是所有其他可能的论点呢?剧情()。必须将它们连同它们的默认值一起写在我们的函数中,这将会很长,并且不太实际:
def no_kwargs_plot(x, y, ax=None, linewidth=1, other=1,...):
if ax is None:
ax = plt.gca()
ax.plot(x, y, linewidth, other,....) ## example plot here
这就是使用**
符号(**kwargs
)有用的地方。当用于自由键值元素时,比如我们函数中的孤立输入(在我们的例子中,这些输入与预定义的参数 x、y 和 ax 无关)**name
会将所有这些元素打包到一个字典中,并将它们存储在变量name
中。
例如,如果我们将绘图函数用作custom_plot(x=xdata, y=ydata, ax=axes[0], linewidth=2, c='g')
,那么得到的plt_kwargs
字典将是{'linewidth':2, 'c':'g'}
。如果这还不太清楚,看看下面的示例代码,输出(> >)和下面的模式:
def print_kwargs_only(x, y, ax=None, **plt_kwargs):
print(plt_kwargs) #to print the dictionary with all the orphan kwargsprint_kwargs_only(x=[1, 2], y=[10, 20], not_xyax=1, random_orphan_kwarg='so lonely', linewidth=2, c='g')>> {'not_xyax': 1, 'random_orphan_kwarg': 'so lonely', 'linewidth': 2, 'c': 'g'}
因此,使用**
解决了将所有可能的绘图输入放入我们的函数中的问题,而不需要显式地预定义它们,并让它们准备好在字典中使用。但是,这本补充关键字参数词典是如何使用的呢?
之前,我提到过**
在自由元素上使用时表现得像一个打包函数。当你在一个字典上使用**
时(不管它是否被**
打包过),实际上**
会做与之前相反的动作:它会将字典解包成不同的自由元素。在custom_function
中,当我们在.plot()
、即 ax.plot(x, y, **plt_kwargs)
中编写**plt_kwargs
时,我们实际上是在要求 python 获取字典plt_kwargs
并将它的所有键值对分别解包到.plot()
函数中作为单独的输入。
这样,在不知道将使用多少和哪些绘图定制的情况下,我们可以将它们全部传递给将进行绘图的函数部分。
我们可以再次使用我们最初的custom_plot
函数看到这一点(您可能注意到,这次我使用了该函数返回的轴来展示如何使用它):
plt.figure(figsize=(10, 5))
out_ax = custom_plot([1, 2], [10, 20], linewidth=5, c='g')
out_ax.set(xlabel='xlabel', ylabel='ylabel', title='Testing out the usefulness of **kwargs')
plt.show()
基本语法的扩展
这就是基本的语法。有了这些,你应该已经能够开始创作一些更有趣的情节了。
不过在使用 ham 之前,我们需要注意一个你在使用**kwargs
时可能会遇到的潜在问题。也就是说,如果您在custom_plot
函数中进行多个绘图会怎么样?例如,如果你正在绘制两条线,其中一条应该是虚线,而另一条是实线。**kwargs
怎么知道哪个论点进入了哪个情节?
答案是“**kwargs
包装机”不能再工作了,需要更换,但是“**kwargs
拆包机”可以正常工作。我这么说是什么意思?让我们定义一个名为multiple_custom_plots
的新函数来阐明它:
def multiple_custom_plots(x, y, ax=None, plt_kwargs={}, sct_kwargs={}):
if ax is None:
ax = plt.gca()
ax.plot(x, y, **plt_kwargs)
ax.scatter(x, y, **sct_kwargs)
return(ax)
这里有什么不同,我们应该如何使用它?首先,看看可能的输入列表。现在,我们没有了**kwargs
,而是有了两个新的参数,一个用于我们的一个情节。此外,默认情况下,这些参数是空字典。
如果你听了我之前关于**kwargs
的解释,希望你已经很清楚了。想法是,因为我们不能要求函数自动将所有孤立输入打包到一个字典中(我们现在需要两个独立的字典),我们将不得不自己提供预打包的每个绘图参数字典。
稍后用双星号使用它们与最初的custom_plot
没有什么不同,因为在字典上使用**
仍然意味着我们希望它的值被解包。我们使用空字典作为缺省值,因为如果您没有提供定制字典,我们在试图用**
解包它们(或缺少它们)时会遇到问题。如果没有提供任何内容,空字典本质上是用来将任何内容解包到函数中的。
让我们看看如何使用它:
plot_params = {'linewidth': 2, 'c': 'g', 'linestyle':'--'}
scatter_params = {'c':'red', 'marker':'+', 's':100}
xdata = [1, 2]
ydata = [10, 20]plt.figure(figsize=(10, 5))
multiple_custom_plots(xdata, ydata, plt_kwargs=plot_params, sct_kwargs=scatter_params)
plt.show()
快速应用
因此,当要创建自定义函数来进行绘图时,上一节应该足以让您在一段时间内享受到静态绘图的乐趣。在下一节中,我将简单地给你一个使用自定义函数的绘图示例,希望能启发你去做一些自己的绘图。
随机样本的样本容量和核密度估计
假设您想了解给定随机变量的样本大小如何影响对其潜在概率分布的估计。
假设我们有一个连续的随机变量 X,正态分布,均值为μ(μ),标准差为σ(σ)(即 X∞N(μ, σ ))。我们想知道 scipy 的核密度估计量(kde)如何受到我们随机样本大小的影响(我们从正态分布中随机抽样的次数),方法是将其与潜在的真实概率密度分布(pdf)的估计量进行比较。
我们将通过绘制不同 n 值的样本本身、它们的 kde 和它们的潜在 pdf 来做到这一点。
def sample_plot(mu=0, sigma=1, N=100, sct_kwargs={}, pdf_kwargs={}, kde_kwargs={}, ax=None):
# generate sample
sample = np.random.normal(loc=mu, scale=sigma, size=N)
# generate pdf
xrange = mu + 5*sigma*np.linspace(-1, 1, 100)
pdf = stats.norm.pdf(xrange, loc=mu, scale=sigma)
# generate kde
estimation = stats.gaussian_kde(sample)
kde = estimation(xrange)
#Plotting
if ax is None:
ax = plt.gca()
ax.scatter(sample, np.zeros_like(sample), **sct_kwargs)
ax.plot(xrange, pdf, **pdf_kwargs)
ax.plot(xrange, kde, **kde_kwargs)
return(xrange)
让我们一步一步地解构这个函数:
首先是投入。这里,我们将从高斯随机数生成器创建自己的数据,而不是请求数据数组。所以我们需要询问相关的统计参数μ和σ(分别为高斯分布的均值和标准差)。我们还需要问要取的样本数 N。实际上,我们将在后面迭代 N 的不同值,以查看样本大小对估计的影响。这个想法是将样本绘制成散点,将 pdf 和 kde 绘制成常规线图。因此,我们将为它们各自的绘图参数(线宽、标记大小等)提供一个字典作为输入。).最后,我们将询问我们想要在其上绘制这三样东西的图形的轴。
该函数的第一部分将简单地从所提供的统计参数中生成大小为 N 的随机高斯样本。
代码的第二部分将创建对应于由μ和σ给出的正态分布的 pdf 的线图的 x-y 对。我们将 pdf 的范围限制为 5 个标准偏差,因为任何一边的任何其他值都将非常小。
代码的第三部分首先计算样本的 kde,然后将其应用到与 pdf 相同的 x 轴上的值范围。
最后,在代码的第四部分,我们简单地将 x 轴(高度为 0)上的所有采样值绘制成散点图,并将 pdf 和 kde 绘制成线图。这三个都有各自的绘图关键字参数。
# Sample parameters
sample_sizes = (10, 20, 100, 250, 500, 2_000)
mean = 100
std = 15# Plotting parameters
scatter_params = {'alpha':0.1, 'c':'g', 's':100, 'label':'samples'}
pdf_params = {"linewidth":2, 'c':'k', 'label':'pdf'}
kde_params = {"linewidth":3, 'ls':'--', 'c':'g', 'label':'kde'}# Plotting
fig, axes = plt.subplots(6, figsize=(15, 20))
for ax, n in zip(axes, sample_sizes):
sample_plot(mu=mean, sigma=std, N=n, ax=ax,
sct_kwargs=scatter_params, pdf_kwargs=pdf_params, kde_kwargs=kde_params)
ax.set_title(f'N={n}')axes[0].legend()
axes[-1].set_xlabel('Sample Value', fontsize=13)
plt.tight_layout()
plt.savefig('finalplot')
plt.show()
就是这样!希望您已经学会了如何通过正确传递相应的轴和关键字参数来为函数添加绘图功能。这将有助于您拥有越来越模块化的代码来快速浏览和可视化您的数据。
原载于 2020 年 4 月 28 日https://matic derini . github . io。
为只读数据库创建自定义 SQL Server 复制
我们来讨论一下使用 T-SQL 的 SQL Server 复制。并考虑从源到目的地单向复制表的一般算法。
图片来自 Piqsels (CC0)
通常需要创建 SQL Server 数据库的只读副本。例如,为了将分析任务和操作任务分开,可能需要这样做。第一种情况会导致数据库的高负载,为了降低负载,会创建主数据库的副本来执行分析性只读查询。
通常,这些只读副本可以使用内置的 DBMS 工具创建:
但是,如果您不需要整个数据库,而只需要其中的几个表,该怎么办呢?在这种情况下,您可以自己创建复制。只要数据采样是主要目标,单向的数据库复制(主到从)就足够了。几种方法包括 SSIS 和。NET 可以用来执行这种复制。
在本文中,我们将使用 JobEmpl recruiting service 数据库来演示如何使用 T-SQL 创建主-从方向的数据库复制。
使用 T-SQL 创建 SQL Server 单向复制
首先,让我们描述一下这个复制的主要原理和算法。在每次迭代中,我们需要比较源数据库和目标数据库中所选表的数据。这意味着我们需要输入一个唯一的代理键来比较这些表。为了加快比较过程,我们还需要为那个键创建一个索引。还需要为每个复制表添加一个计算字段,为每一行计算校验和。
选择固定的数据部分也很重要,例如,一次选择一定数量的行(每次迭代)。
因此,我们需要执行以下步骤:
- 在源表上,创建一个 REPL_GUID 列和一个唯一的 REPL_GUID 索引,以强制源表和目标表之间的一对一关系。您还应该创建一个计算校验和列,该列将计算每一行的校验和值。
- 创建一个名为 Target 的新目标数据库。
- 跨源和目标数据库同步复制表的模式,并删除对不存在的对象的所有引用。
- 禁用目标数据库的外键。
- 运行复制并监视源数据库和目标数据库之间有多少行不同。
现在让我们使用为招聘员工而创建的 JobEmpl 数据库来详细回顾每个步骤。
图 1 求职者数据库模式
我们只需要复制雇员和工作历史表。
然后,可以在以下脚本的帮助下执行上述算法的第一步。
从脚本中,您可以看到它必须在源 JobEmpl 数据库上运行,并且您应该相应地在 @src 和 @sch 变量中指定源数据库和模式。构建动态 sql 需要 @sql 变量,同时需要 @name 保存复制表的名称。
首先,我们将复制的表名收集到临时#tbl 表中。接下来,我们用光标遍历每个表名,并将表名提取到@name 变量中。之后,对于每个表,形成一个非 IDENTITY 类型的列列表,并将结果插入到带有“+”符号的@listcols 变量中。
值得一提的是,首先使用 CAST 函数将每个表名转换为 NVACHAR(MAX)类型,然后使用 COALESCE 函数([
接下来,创建计算校验和字段、REPL_GUID 字段及其唯一的 indREPL_GUID 索引。
在我们的例子中,我们得到了下面的脚本。
稍后,您可以借助以下脚本从数据库中删除创建的表和索引。
复制的表也在这里,其中每个表的 indREPL_GUID 索引以及 REPL_GUID 和 CheckSumVal 列都被删除了。
在我们的例子中,创建了下面的 T-SQL 代码。
现在让我们根据上面提到的算法的第二步创建一个新的 JobEmplRead 数据库来接收数据。然后,我们同步复制表的模式。要执行同步,请使用 DbForge 模式比较工具:选择 JobEmpl 作为数据源,jobEmplRead 作为数据目标。
图 2 模式同步的数据库选择
然后按下比较按钮。完成比较的元数据创建过程后,选择所需的表并开始配置数据库同步过程。
图 3 选择表进行模式同步
接下来,我们选择默认值—脚本生成。
图 4 选择脚本生成作为同步输出
现在,让我们清除备份创建选项。
图 5 取消选择备份创建选项
接下来,我们取消选中所有依赖项,因为我们不需要创建其他对象。我们稍后将在生成的模式同步脚本中手动删除外键。
图 6 取消选择所有依赖关系
现在按下同步按钮,忽略摘要选项卡上的警告。
图 7 警告
在生成的脚本中删除以下外键:
- FK _ 工作历史 _ 公司 _ 公司 ID
- FK _ 工作历史 _ 职位 _ 职位标识
- FK _ 作业历史记录 _ 项目 _ 项目标识
我们需要这样做,因为我们没有转移公司、职位和项目表。结果,我们得到了一个移动复制模式表的脚本。
在 JobEmplRead 数据库中运行此脚本。
因此,我们已经完成了算法的第 3 步:在 JobEmpl 和 JobEmplRead 数据库中同步表模式,并删除了对不存在的对象的所有引用。
让我们使用下面的脚本进行监控。
这里我们有完整的外部连接语句创建和表列表的返回,以及不同的行数,包括不存在的和缺少的行。
在我们的例子中,我们得到以下结果。
图 8 复制表中不同的行数
下面的脚本是为了比较而生成的。
值得注意的是,为了减少阻塞,事务隔离级别是脏读。
让我们将算法的第 4 步和第 5 步合并到下面的脚本中。
首先,JobEmplRead 数据库中复制表的所有外键都被禁用。然后,使用 MERGE 语句,数据被部分复制。在我们的例子中,每次迭代有 100 000 行。该脚本包含一次迭代,并执行以下 T-SQL 代码。
这个脚本应该按照预先指定的时间间隔自动运行。例如,它可以每分钟运行一次,甚至更频繁,具体取决于分析需求。
在几次迭代之后,不同行的数量必须更少。
图 9 特殊行数量的变化
请记住,要在禁用的表中启用外键,您应该运行以下脚本。
在我们的例子中,将生成并执行以下脚本。
ALTER TABLE [JobHistory] CHECK CONSTRAINT [FK_JobHistory_Employee_EmployeeID];
请记住,在复制所有数据之前,不能在复制运行时在复制的表上启用外键。
结论
我们已经讨论了实现从源到目的地单向复制表的过程的方法之一。
这种方法和脚本可以应用于任何数据库。但是当然,这些脚本必须根据复制表的具体情况进行修改。例如,如果表中有计算字段,可能需要进行修改。
SQL Complete 是帮助我构建这些脚本的主要工具。该工具还允许代码格式化以及重命名对象及其所有引用。
【https://blog.devart.com】原载于 2020 年 3 月 19 日。
从零开始创建深度神经网络,强化学习导论
第二部分:强化学习和反向传播
一只正在学习捡东西的狗【图片由 Unsplash 上的 Humphrey Muleba 拍摄】
本文是三部分系列的第二部分,将详细介绍 OpenAI gym 上 Cartpole-v1 问题的解决方案——仅使用 python 库中的 numpy。
的第一部分奠定了基础,创建了计划的大纲并构建了前馈功能,以将环境状态传播到其行动值。这一部分将集中讨论强化学习中累积奖励和行动价值背后的理论,以及反向传播机制的建立。这些是我们代理学习过程的基础。
到上一篇文章结束时,我们有了一个简单的程序循环:在每个时间步上,代理观察环境的状态,并通过它的神经网络(随机初始化)来获得每个动作的预测值。然后,它(以概率 1-ε)选择具有最大预测回报的行动。然后在下一个时间步重复这个过程。
然而,此时代理不知道在采取特定动作之后发生了什么,因为没有反馈。例如,当我们训练一只宠物时,根据它们的行为是可取的还是不可取的,我们创建正面或负面强化的反馈回路是很重要的。一只狗记得在过去捡球为它赢得了一次款待(一个有价值的奖励),并且在下一次类似的情况出现时更有可能优先捡球。同样,我们在程序中实现内存也很重要,这样代理可以跟踪奖励和采取行动后的结果状态。
体验回放
我们将通过在代理的记忆中添加体验,并在一个称为体验重放的过程中使用它们来改进代理来做到这一点。照此修改主程序循环,
# The main program loop
for i_episode in range(NUM_EPISODES):
observation = env.reset()
# Iterating through time steps within an episode
for t in range(MAX_TIMESTEPS):
env.render()
action = model.select_action(observation)
**prev_obs = observation** observation, reward, done, info = env.step(action)
# Keep a store of the agent's experiences
**model.remember(done, action, observation, prev_obs)**
**model.experience_replay(20)** # epsilon decay
...
我们还将向 RLAgent 添加相关代码,首先是在 init 函数中,
self.memory = deque([],1000000)
还有记住,的宣言
def remember(self, done, action, observation, prev_obs):
self.memory.append([done, action, observation, prev_obs])
请注意,这个内存实现使用了 dequee,这是一个简单的数据结构,允许我们“附加”内存并跟踪最新的 n 条目,其中 n 是 dequee 的大小。Deque 是一个集合,所以我们还需要添加它的 import 语句,
from collections import deque
最后,我们添加了 experience_replay 方法,这将帮助代理从经验中学习以改进其游戏性,
1 def **experience_replay**(self, update_size=20):
2 if (len(self.**memory**) < update_size):
3 return
4 else:
5 batch_indices = np.random.choice(len(self.**memory**), update_size)
6 for index in batch_indices:
7 done, action_selected, new_obs, prev_obs = self.**memory**[index]
8 action_values = self.**forward**(prev_obs, remember_for_backprop=True)
9 next_action_values = self.**forward**(new_obs, remember_for_backprop=False)
10 experimental_values = np.copy(action_values)
11 if done:
12 *experimental_values[action_selected] = -1*
13 else:
14 *experimental_values[action_selected] = 1 + self.****gamma*****np.max(next_action_values)*
15 self.**backward**(action_values, experimental_values)
16 self.**epsilon** = self.**epsilon** if self.**epsilon** < 0.01 else self.**epsilon***0.995
17 for layer in self.**layers**:
18 layer.lr = layer.lr if layer.lr < 0.0001 else layer.lr*0.995
在这个方法中有很多东西需要解开,但是让我们一步一步地来看。
首先,有一个简单的检查,看看我们是否有足够的经验开始学习。如果没有,我们就等着看。
接下来,我们从所有存储的内存中随机取样,得到 update_size 内存的索引。对于这些索引中的每一个,我们检索(第 7 行)与之相关的内存数据。
然后我们计算三样东西——
- 用 prev_obs 变量计算的 action_values ,
- 用 obs 变量计算的 next_action_values ,用于在下一次计算中计算实验值
- 实验值(在累积奖励和值函数部分说明)
一旦计算出这些值,我们就将实验值和行动值预测反馈给一个自我。 后向 函数将计算这些值之间的差异,并使用它来对权重矩阵进行更改。 向后 功能将在后面关于反向传播的章节中进行检查和实现。
最后,我们更新系统的ε(探索率)和学习率变量。学习速率是反向传播算法使用的一个属性,它决定了学习过程中步长的大小。注意,我们已经将 epsilon 更新从它在主循环中的原始位置移到了这个方法中。
动作和实验值
上面粘贴的代码块在第 8–14 行有 3 个计算。我们现在将逐一介绍这些内容。
第一个, action_values,是代理在给定状态下每个动作的当前估计值(prev _ OBS)——与第一部分的 select_action 函数中计算和使用的值相同。
第二个计算是 next_action_values。这是来自下一个状态(new_obs)的两个动作的一组预测值,即在代理在创建该记忆的情节期间采取动作之后获得的状态。
next_action_values 只是一个临时变量,用于随后的计算——用于实验值。这是我们的代理从这次特殊“经历”中学到的目标值,有两种形式:
- [第 11-12 行]如果在下一次观察中杆子已经翻倒,则该集结束,并且在此状态之后所有未来奖励的总和为(-1)。
- [第 13-14 行]如果极点在下一次观察中没有翻倒,那么预期的未来奖励是观察到的即时奖励加上一个折扣的总和,该折扣是该状态的预期未来奖励的 gamma — 倍。
请注意,这两种形式的实验值仅适用于该集期间选择的动作。其他动作(在横竿问题中只有一个)没有更新,因为我们没有任何关于来自给定状态的那些动作的新的实验知识。
实验值数量至关重要。它捕捉新的经验信息,即代理已经了解在给定状态下采取行动的价值。
累积报酬和价值函数
本节将后退一步,形式化一些强化学习理论,这些理论隐含在我们到目前为止编写的代码中。首先,请注意,到目前为止,我们已经讨论了特定状态下的行为价值,但是这到底意味着什么呢?这个值是如何获得的?为什么变量 experimental_values 是这样计算的?
本节的其余部分将讨论一些强化学习理论。他们自由引用并大量借鉴了 2015 年发表的一篇伟大论文,这篇论文展示了深度 Q-Networks 的力量,并使用一个通用的架构来玩 Atari 2600 游戏。如果您对此不感兴趣,请随意跳到反向传播部分,我们将继续实施。
我们首先将特定时间步长 t 的“累积奖励”或“回报”定义为一个时间步长后直到剧集结束的所有未来奖励的总和。形式上,
其中,rₜ是每个时间点收到的直接奖励,γ是折现因子。我们还定义了一个称为最优行动值函数 Q(s,a)* —
这是所有政策中国家-行动对(sₜ,aₜ)的“预期”累积回报的最大值,其中政策定义了在给定的国家必须采取什么行动。它代表给定状态下行动的“真实”价值,即从特定状态 s、选择行动 a、开始,然后在完全了解环境的情况下进行最优博弈的最大预期回报。期望符号抓住了环境通常可能是随机的这一事实。
上面的最佳动作值函数服从一个称为贝尔曼方程的重要恒等式,
这个恒等式的意思是,如果我们在状态 s 中采取行动 a 后,从结果状态s’中知道所有行动的最优行动值,那么最优行动值函数 Q(s,a)* 是在这个行动后观察到的即时回报的预期和(贴现的)可以从状态s’采取的所有行动a’的最大最优行动值。期望符号捕捉了这样的事实,即s’(因此也是 r ) 可能在概率上由初始状态 s 和动作 a 确定。
大多数强化学习代理通过使用贝尔曼方程作为迭代更新来学习,在我们的例子中是—
当 i 趋向于无穷大时,收敛到最优作用值函数 Q(s,a)的一个量。尽管它与上面代码块中的第 14 行相似,但我们所做的和这个等式所代表的是有区别的。这种迭代更新建议我们保存一个所有可能的状态动作对的表,并递增地更新该表中的每个值。这实际上是不可能的,因为我们有连续的状态空间!相反,我们使用基于神经网络的函数逼*器 Q(s,a;W) 来估计动作值函数,*其中 W 表示我们网络中的权重。这个函数逼*器被称为 Q 网络——在我们的例子中是深度 Q 网络或 DQN。我们确实一直在建设一个 DQN!
代替更新所有状态-动作对,更实际的是迭代地最小化损失函数的期望,定义为:
这里有两点需要注意,
- 给定代理记忆中的所有(s,a,r,s’)组合,我们使用随机梯度下降来计算每步中单个样本(我们在第 7 行检索的记忆数据)的损失,而不是计算并最小化该值的总预期损失。
- 上式中权重 W 的下标(i - 1)和(I)表示我们保存了权重的快照,用于下一次更新。这确实是理想和完整的实现,我们很可能会在第三部分谈到它,但是现在,我们不会实现这个细节。我们只使用一组权重来计算预测值和目标值(即上面代码块中的 experimental_values )。实际上,在没有用于计算目标值的固定网络的情况下,对上述损失函数运行随机梯度下降最终会最小化经验损失和目标值方差之和,这不是我们想要的(我们只想最小化经验损失)。你可以在这里阅读更多关于这个细节【第 9 页】。对于我们的简单用例,幸运的是,这种差异不会破坏我们的解决方案。
有了这些知识,我们现在可以回过头来回答行动价值的意义这个问题。
在训练中的给定时间点,特定状态的动作值(由正向函数计算)就是模型对当时最佳动作值函数的估计。换句话说,它是对代理人期望从一个给定的州获得的每个行为的总折扣奖励的估计。在 cartpole 问题的特定情况下,这是代理期望从特定状态保持存活的时间步数的折扣和。另一方面,实验值是网络在每次迭代中试图接*的目标值。
根据上面提到的几点,损失函数的最后一个等式根据经验可以归结为—
使用实验值和动作值,正如我们在前面章节中经验 _ 回放功能的定义中所定义的。
这是我们将在每次迭代中寻求最小化的数量(对于给定的样本)。实验值和动作值之间的差值将形成更新我们网络权重的基础,在反向功能中实现。
反向传播
强化学习理论帮助我们在最后一节将损失函数定义为实验值和行动值之间的*方差。现在我们有了这些值,优化问题与通过反向传播最小化任何其他神经网络中的误差函数是一样的。
我们现在将实现rl agent . experience _ replay代码的最后一部分,即向后函数:
def **backward**(self, calculated_values, experimental_values):
delta = (calculated_values — experimental_values)
for layer in reversed(self.**layers**):
delta = layer.**backward**(delta)
该函数首先计算计算值(预测值 action_values )与在某个状态下采取某个动作的实验值之差。然后,该误差通过从最后一个隐藏层到输入层的每一层向后“传播”,并且基于该误差更新权重。直观地说,每一层都被告知其输出的估计值与在输出层中生成实验值所需的预期输出相差多少。
此函数调用 NNLayer.backward 函数:
1 def **backward**(self, gradient_from_above):
2 adjusted_mul = gradient_from_above
3 # this is pointwise
4 if self.**activation_function** != None:
5 adjusted_mul = np.multiply( relu_derivative(self.**backward_store_out**),gradient_from_above)
6 D_i = np.dot( np.transpose(np.reshape(self.**backward_store_in**, (1,len(self.**backward_store_in**)))), np.reshape(adjusted_mul, (1,len(adjusted_mul))))
7 delta_i = np.dot(adjusted_mul, np.transpose(self.**weights**))[:-1]
self.**update_weights**(D_i)
return delta_i
首先,(第 4-5 行)如果该层的输出有一个相关的激活函数,我们将激活函数的导数(在我们的例子中是 ReLU 函数)与从上一层接收的误差逐点相乘。得到的值— adjusted_mul — 用于每层中的两个进一步的计算:
- D_i 【第 6 行】:这是损失函数相对于该层权重的实际导数。然后将该值传递给 NNLayer。 update_weights 方法进行实际更新。没有处理矩阵的所有绒毛,这是在这一层接收的(列矩阵)输入和上面计算的(row_matrix) adjusted_mul 值之间的简单点积。
- delta_i 【第 7 行】:这是该层输入的计算“误差”(即前一层的输出)。这被向后传递到前一层,以实现其自己的导数和增量计算。
在这篇指导性的博客中给出了反向传播算法的推导和一步一步实现的完整处理,但我们不会在此详述。虽然我的实现与那篇博客中给出的略有不同,但检查我描述的函数是否正确地实现了反向传播可能是一个很好的练习。
该算法剩下的两部分是 relu_derivative 和 NNLayer 。更新 _ 权重功能。让我们现在过一遍。
ReLU 函数的导数非常简单—如果值大于 0,ReLU 函数返回输入值,否则返回 0。所以这个函数的导数对于 x>0 就是恒等式(即 1)函数,否则就是 0。这是矩阵输入的实现—
def **relu_derivative**(mat):
return (mat>0)*1
最后, NNLayer.update_weights 函数:
def **update_weights**(self, gradient):
self.**weights** = self.**weights** - self.**lr***gradient
太好了,这个简单的实现应该足以让一个收敛算法工作并训练我们的代理!这两部分所有代码的工作实现可以在我的 Github 上找到。
训练有素的特工
当我们运行这个程序时(记得在每个时间步长调用 env.render() !),几百集下来,我们就有了一个相当训练有素的经纪人。一旦代理人在连续 100 集内达到 195 以上的*均奖励,任务就被认为解决了。这通常需要 100-300 个时间步来实现,但是,由于不恰当的初始化,在某些运行中可能需要更长的时间。关于收敛保证和性能的更详细的分析将在下一篇文章中完成,但这里有一个来自该代理的完整运行示例,
训练有素的钢管舞特工!
太好了!看起来我们的代理人能够很好地*衡杆子。将此与我们在上一篇文章中开始的随机代理进行比较。
虽然我们构建的代码足以训练这个代理,但它还可以进行优化,以实现更快的收敛和更高的稳定性。这将是下一个帖子的重点。
咻!我们在这方面做了很多工作。这里有一个总结:
- 实现了一个“体验重放”机制,其中代理将所有(状态、动作、奖励、下一个状态)存储在内存中,并从中随机抽取样本进行权重更新。
- 研究了强化学习理论及其与 cartpole 问题的相关性,并推导出一种更新权重的机制,以最终学习最佳状态-动作值。
- 实现了反向传播算法来实际训练网络。
这里还有一些任务需要完成,我们将在本系列的下一篇也是最后一篇文章中继续讨论。
- 将 update_weights 方法替换为基于 Adam 的优化器,该优化器跟踪所有参数的个体学习率。这将有助于算法更快地收敛。
- 重构全局常量和特定于模型的常量,以获得更好的可配置性。
- 分析该方法在几种不同超参数配置下的性能。
- 实施用于计算目标值的快照网络,该网络会定期更新为网络的当前 Q 值。
下次再见!
参考
- 用深度强化学习玩雅达利,Deep mind Technologies【Mnih et。阿尔,2015】。
- 通过深度强化学习进行人类水*的控制【Mnih et。阿尔,2015】。
- http://www . briandolhansky . com/blog/2013/9/27/人工神经网络-反向传播-第四部分
- 深度 Q 学习的理论分析。阿尔,2019]
从零开始创建深度神经网络,强化学习导论
第三部分:反思和改进
由 Unsplash 上的 bantersnaps 拍摄的照片
这是一系列文章中的第三篇也是最后一篇,旨在全面介绍 OpenAI gym 上的侧翻问题的解决方案,该解决方案是在没有使用 Pytorch 或 Tensorflow 等标准机器学习框架的情况下从零开始构建的。完整的代码可以在这里找到。
第一部奠定了基础。在其中,我们讨论了神经网络体系结构,并实现了前向传播来计算代理动作的值。第二部分深入研究了强化学习理论的细节,形式化了 Q 值和 DQN 的概念。我们还在第二部分中实现了反向传播。
第三部分将包含一些不同配置的代理性能的可视化和反映。这最后一部分还将完成实现并添加增强功能,如 Adam 优化器。在这里,我们不太关注超参数选择背后的严格性,而是更多地探索可以为模型改进而调整的配置。
在上一节的最后,我们完成了 Cartpole 代理的实现。时间来看看结果和代理的表现随着时间的推移!
跟踪代理的改进
让我们进行一次完整的训练,从随机初始化开始跟随代理,直到它学会*衡极点的艺术。这次训练用了 141 集来实现它的目标(连续 100 集的*均分数为 195)。
首先,这是分数的图表—
现在让我们看看代理在培训过程中的 3 个不同阶段的表现。代理的前 5 次运行非常糟糕,
最初几轮
在培训的中途,我们可以看到代理取得了进步,尽管仍有改进的空间。这是第 75 和 76 集,
训练中途
最后,在训练接*尾声时,代理人能够几乎完美地*衡杆子。这是第 138 轮,
最终受训代理人
我们可以看到代理到最后还是挺不错的!
目标网络
在第二部分关于累积奖励和行动值的部分,我们讨论了如何使用 DQN 的完整实施的简化版本,通过使用相同的权重来计算预测的行动值和目标值。相反,我们需要在计算目标动作值(实验 _ 值在 RLAgent 中)时有一个固定的权重网络。经验 _ 回放方法)。让我们开始实施吧。首先,我们在 NNLayer 中添加了 stored_weights 参数的初始化。初始化函数,
def __init__(self, input_size, output_size, activation=None, lr = 0.001):
self.input_size = input_size
self.output_size = output_size
self.weights = np.random.uniform(low=-0.5, high=0.5, size=(input_size, output_size))
**self.stored_weights = np.copy(self.weights)**
self.activation_function = activation
self.lr = lr
记住参数remember _ for _ back prop= False 中传入的 experimental_values 的计算(通过 next_action_values 计算)。这个参数实际上可以被重新使用来告诉网络使用存储的权重而不是当前的网络权重。编辑 NNLayer。前进功能:
# Compute the forward pass for this layer
def forward(self, inputs, remember_for_backprop=True):
# inputs has shape batch_size x layer_input_size
input_with_bias = np.append(inputs,1)
unactivated = None
**if remember_for_backprop:
unactivated = np.dot(input_with_bias, self.weights)
else:
unactivated = np.dot(input_with_bias, self.stored_weights)**
# store variables for backward pass
output = unactivated
...
最后,在每次体验重放后,我们将把存储的权重更新为新的网络权重。将粗体行添加到 experience_replay 方法的最后一位:
...
for layer in self.layers:
**layer.update_stored_weights()** layer.lr = layer.lr if layer.lr < 0.0001 else layer.lr*0.99
最后,添加 NNLayer。更新 _ 存储 _ 权重方法:
def update_stored_weights(self):
self.**stored_weights** = np.copy(self.**weights**)
很好,这个相对简单的修正意味着我们的目标网络计算不依赖于我们当前的权重。
解决方案的*均发作次数
很好,现在我们已经完成了一次典型的跑步,是时候看看代理在多次不同的训练中学习得有多快了。为了做到这一点,我们从头开始初始化一个新的代理,运行多次,看看需要多少集才能达到*均奖励阈值。
这是 50 次运行的数据。
每次运行的解决方案集
除了两次运行长时间陷入局部极小值并花费了超过 2000 个时间步骤来解决之外,几乎所有其他运行都花费了不到 200 集来收敛。在 50 次运行中要解决的*均事件数是 240.84。这包括两次异常运行。
不同的批量
超过 20 次运行的*均发作到解决方案
该图显示了改变批次大小如何影响*均发作次数(每个批次大小超过 20 次)。我测试了 4 个不同的值——5、10、20 和 40。就要解决的*均情节数而言,表现最好的是批量大小为 20,*均有大约 173 个情节要解决。然而,考虑到我们对批量大小为 10 的算法进行了一半的更新,我们仍然能够*均只解决 304 集。这比 double 低 15%左右。在批量大小为 40 的情况下,尽管大多数时候该算法收敛得非常快(超过 50%的解决方案处于最低可能的 100 集标记),但是该算法在某些集内非常不稳定,并且直到远远超过 3000 集时才收敛。
接下来,我们将使用批量大小10 进行其余的增强。
Adam 优化器
到目前为止,在计算了梯度之后,我们的 NNLayer。 update_weights 函数使用随时间不断降低的学习率更新层权重,直到达到最小阈值。
对于权重矩阵中的每个参数,我们当前的权重更新具有相同的学习率。我们现在将使用亚当优化技术,看看是否能改善结果。Adam 的工作方式是跟踪网络中每个参数的个人学习率,使用关于该参数的梯度的一阶和二阶矩的估计值。这通常会导致更快的收敛。
参考这篇文章来了解下面代码的细节。如果您想直接了解超参数配置,可以跳过本节关于 Adam 实现的其余部分。
我们开始吧。我们将更改 NNLayer 中的 update_weights 方法,如下所示:
def **update_weights**(self, gradient):
m_temp = np.copy(self.m)
v_temp = np.copy(self.v)
m_temp = self.**beta_1***m_temp + (1-self.**beta_1**)*gradient
v_temp = self.**beta_2***v_temp + (1-self.**beta_2**)*(gradient*gradient)
m_vec_hat = m_temp/(1-np.power(self.**beta_1**, self.**time**+0.1))
v_vec_hat = v_temp/(1-np.power(self.**beta_2**, self.**time**+0.1))
self.**weights** = self.**weights** - np.divide(self.**lr***m_vec_hat, np.sqrt(v_vec_hat)+self.**adam_epsilon**)
self.m = np.copy(m_temp)
self.v = np.copy(v_temp)
beta_1、beta_2 和 adam_epsilon 参数是在 adam 优化器的实现中使用的常数。它们几乎从未改变。矩阵 m 和 v 以及时间参数是在训练过程中更新的变量。它们都在层的 init 方法中初始化:
def **__init__**(self, input_size, output_size, activation=None, lr = 0.001):
...
self.**lr** = lr
self.**m** = np.zeros((input_size, output_size))
self.**v** = np.zeros((input_size, output_size))
self.**beta_1** = 0.9
self.**beta_2** = 0.999
self.**time** = 1
self.**adam_epsilon** = 0.00000001
我们还用 Adam 的时间参数的增加来代替层学习速率的减少。Adam 使用时间自动降低学习率。像这样更新 experience_replay 方法的最后 3 行:
...
for layer in self.layers:
**layer.update_time()
** layer.update_stored_weights()
update_time()实现只是每次将时间参数增加 1。
将我们的实现与代码开头链接的文章进行比较,以验证它确实是准确的!
太好了,现在是实现的时候了,看看它是否真的表现得更好!以下是 50 次运行(批量为 10 次)后解决方案的集数图表:
用 Adam optimizer 解决情节
虽然这仍然有一些不稳定性,但与我们的旧优化器相比,它的性能提高了大约 17%(261 对 304)。
虽然这不是结论性的,试验的数量也很少,但它表明 Adam 在某些情况下是一种有效的技术。对 Adam 性能的全面分析,以及何时使用这种优化技术与其他优化技术的对比,可以在原始论文中找到。
隐藏层尺寸
隐藏层的大小也有所不同。以下是 4 种不同隐藏层大小(12、24、48 和 96)的*均集数。在第一部分中的神经网络图描述的每个实验中有两个隐藏层,
为隐藏层解决不同大小的剧集
该图呈下降趋势,图层大小为 96 时表现最佳。同样,少量的运行并不能提供确凿的证据,但它表明更多的参数通常会提高代理的性能。当然,代价是训练更大的网络所需的时间和内存通常更大。
隐藏层数
到目前为止,我们所有的实验都有两个隐藏层。相反,尝试使用 1 层和 3 层,在 20 次运行中得到以下结果,每次运行有 96 个隐藏单元—
- 1 层收敛的*均步骤— 198.6
- 收敛到两层的*均步数— 163.75
- 3 层的*均收敛步骤— (>1000)集。在这里,网络需要很长时间才能收敛。此外,更深层次的神经网络会遇到其他问题,比如需要小心处理的消失梯度问题。
不幸的是,硬件的限制使我无法对更深层次的神经网络进行更彻底的分析。
摘要
在这一部分中,我们总结并完成了算法的实现,以训练我们的 cartpole 代理。包括目标网络实现和 Adam 的更新代码可在这里找到。由于普遍性低,我故意没有对这个特殊问题的各种超参数配置进行全面分析,但这里有一篇的信息论文,它非常详细地研究了改变 dqn 配置的影响。
综上所述,这部分我们做了以下工作。
- 从随机初始化到接*完美*衡的最终状态,分析并可视化了代理改进的样本运行。
- 添加了目标网络,完成了实施。
- 增加了一个 Adam 优化器来代替原来的一揽子学习率。
- 探索了一些不同的超参数配置,如批量大小、隐藏层大小和隐藏层数。
进一步阅读
如果您已经完成了这一步,那么您现在已经完成了横竿问题的实现!您可能希望:
- 进一步调整这个程序,找出最佳的超参数配置。你能在 50 次运行中把*均集数降到 110 以下吗?
- 继续讨论在露天健身房环境中的其他问题。 MountainCar 是很好的下一步!
- 看看强化学习和人工通用智能的前沿。OpenAI 在他们的博客上记录了所有的进展。
感谢您的阅读!!!
参考
- 重要的深度强化学习【彼得·亨德森等】
- Adam——深度强化学习的最新趋势。
- Adam 优化技术简介。
从零开始创建深度神经网络,强化学习导论
第一部分:体育馆环境与 DNN 建筑
宠物强化学习![图片鸣谢: 斯蒂芬妮·吉布奥特
本文是三部分系列文章的第一部分,将详细介绍 OpenAI gym 上 Cartpole-v1 问题的解决方案——仅使用 python 库中的 numpy。这个解决方案远非最佳解决方案(你可以在健身房网站上找到),而是专注于从基本原则出发。
运行本文代码的先决条件是 python (3.x ),并安装了 gym 和 numpy 模块。
当我第一次开始在 OpenAI 健身房寻找强化学习时,我无法找到任何关于如何开始自己构建解决方案的好资源。有非常强大的库(如 Tensorflow 和 Pytorch ),允许您构建极其复杂的神经网络,并轻松解决 cartpole 问题,但我想从头开始创建神经网络,因为我相信理解现代机器学习技术的核心构建块是有价值的。我写的是我希望在我努力工作的时候能够找到的东西。所以让我们开始吧。
一、什么是 OpenAI 健身房?在他们的网站上有一个很好的简短介绍,“Gym 是一个开发和比较强化学习算法的工具包。”“ gym 库是一个测试问题——环境——的集合,你可以用它来制定你的强化学习算法。这些环境有一个共享的接口,允许你编写通用算法。”这意味着,围绕构建和渲染模拟真实世界场景的模型的工程已经为我们完成,所以我们可以专注于教代理玩好游戏。
上面的描述也提到了强化学习。那是什么?这里有一个维基百科的总结——“强化学习是机器学习的一个领域,涉及软件代理应该如何在一个环境中采取行动以最大化一些累积回报的概念。”
给你一个类比,想想一只狗是如何被训练的——有利的行为被积极地强化(以款待的形式),而负面的行为被消极地强化。在某种程度上,即使是我们人类,也是复杂的强化学习代理,试图通过选择我们认为在未来会“有益于”我们(以更大回报的形式)的行动来最大化实现我们目标的机会。这张图展示了强化学习的循环,
强化学习周期[Image credit:Mohit Mayank
上图显示了一个代理(我们将构建的程序)将环境的状态和奖励作为输入,从之前的动作中选择一个后续动作并将其反馈给环境,然后再次观察环境。
很好,现在我们已经了解了强化学习中的基本概念,让我们回到我们试图解决的问题——cart pole。首先,具体看一下关于侧翻问题的文档。文档很好地概述了我们正在努力实现的目标。简而言之,我们控制着滑块的底部,顶部有一根垂直*衡的杆子。我们的目标是尽可能长时间地防止杆子脱落。如果极点(就其角度而言)下降到某个点以下,环境将被重置。下面是一个随机代理人在处理横竿问题。
扁担上的随机代理
如你所见,不是很好!但这是意料之中的,因为这个代理会忽略环境的当前状态,并在每个时间步选择一个随机动作。让我们看看我们是否能做得更好。
履行
是写代码的时候了!我们将从导入将要使用的库开始。我们将需要 gym 用于上面讨论的开放人工智能环境,以及 numpy 用于一些数学和矩阵操作。
import gym
import numpy as np
接下来,我们需要导入 gym 为侧翻问题提供的环境。这是如何做到的:
env=gym.make('CartPole-v1')
我们还可以通过打印来观察这个特殊环境空间的一些特征:
print(env.action_space) # Discrete(2)
print(env.observation_space) # Box(4,)
在文档中有更多关于环境及其工作方式的信息,但是上面的值捕捉了定义这个环境的基本元素——可以执行的动作,以及在每个时间步的观察。
动作空间是离散的,包含 2 个值:0 和 1。这些对应于代理能够执行的两个动作,即向左或向右推动滑块。
另一方面,观察空间是连续的,并且有四个组成部分(不要与数据结构框(4)相混淆,对于我们的目的,它仅仅意味着包含四个值的数组)。四个值是什么意思?它们是代表当时环境状态的数字——即大车的位置、大车的速度、杆子的角度、杆子的转速。[https://github . com/open ai/gym/issues/238 # issue comment-231129955]
这里要理解的一个基本事情是,观察和动作空间中的数字的含义只是为了完整性而解释的,我们的目标不是解释(动作空间或观察空间的)值,而是让代理学习这些值在上下文中的含义。让我们回到我们的程序,添加代码来运行一个基本的循环。
# Global variablesNUM_EPISODES = 10
MAX_TIMESTEPS = 1000# The main program loopfor i_episode in range(NUM_EPISODES):
observation = env.reset()
# Iterating through time steps within an episode for t in range(MAX_TIMESTEPS):
env.render()
action = env.action_space.sample()
observation, reward, done, info = env.step(action)
if done:
# If the pole has tipped over, end this episode break
上面的代码为剧集声明了一个主程序循环,并遍历了一集内的时间步长。在内部循环中,程序采取行动,观察结果,然后检查情节是否已经结束(要么是杆子倒下了,要么是滑块脱离了边缘)。如果有,环境被重置,内部循环重新开始。
选择动作的行是从可用的动作中随机抽样的;事实上,它的行为与前面显示的随机代理完全一样。让我们修改一下,定义一个自定义方法来选择给定观察的动作。
现在,我们将定义一个代理(希望如此!)将学会在给定的状态下更聪明地选择行动。我们将在一个类中模拟这个代理:
**class RLAgent:
** env = None def **__init__**(self, env):
self.***env*** = env
def **select_action**(self, observation):
return env.action_space.sample()
我们还需要向全局变量添加一个 RLAgent 实例,并更改操作选择,以便从实例化的类中调用该函数。注意,目前 select_action 函数做的事情和以前一样,但是我们以后会改变这一点。
神经网络
我们现在将创建我们的神经网络的元素。关于神经网络的快速入门:“人工神经网络(ANN)是一个被称为人工神经元的连接单元或节点的集合,它松散地模拟了生物大脑中的神经元。每个连接,就像生物大脑中的突触,可以向其他神经元传递信号。人工神经元接收信号,然后进行处理,并向与之相连的神经元发出信号。”
这就是我们的未来,
神经网络
上图显示了一个神经网络,它有一个输入层、两个“隐藏”层(第 2 层和第 3 层)和一个输出层。该模型提供从观察空间到输入层的输入,这些输入被“前馈”到后续层,直到输出层,其中输出层中的值被用于选择动作。
例如,层 3 中的每个节点都是通过激活函数传递的层 2 的线性组合(加权和)。用于计算第 3 层的权重在矩阵中随机初始化,并通过称为 【随机梯度下降】 的过程逐渐调整,以更好地预测输出。激活函数是一个简单的非线性函数,它允许分类器学习基础观察空间中的非线性规则。
在我们的 CartPole 问题中,有 5 个输入(观察空间的元素+一个偏差项)和 2 个输出(我们可以推车的两个方向)。
神经网络层将被封装在一个 NNLayer 类中,
**class NNLayer:**
# class representing a neural net layer
def **__init__**(self, input_size, output_size, activation=None, lr = 0.001):
self.***input_size*** = input_size
self.***output_size*** = output_size
self.**weights** = np.random.uniform(low=-0.5, high=0.5, size=(input_size, output_size))
self.***activation_function*** = activation
self.***lr*** = lr
这个类包含三个主要内容:
- 图层的尺寸(输入和输出尺寸)。
- 连接输入图层和输出图层的权重。
- 输出的激活函数(默认激活为无,也称为线性)。
我们现在将这个类的用法添加到我们的 RLAgent 中。首先,我们将编辑选择动作函数,
def **select_action**(self, observation):
values = self.forward(observation)
if (np.random.random() > self.epsilon):
return np.argmax(values)
else:
return np.random.randint(self.env.action_space.n)
这个函数不是每次都随机选择一个值,而是将关于环境状态的信息传递给我们的神经网络,并计算每个行为的“预期回报”(values 是一个接受(1,nᵢₙ)数组并返回(1,nₒᵤₜ)数组的函数)。然后,它会选择能带来最大预期回报的行动。注意,该函数仍然选择概率为ε的随机值。ε,也称为“探索率”,是强化学习中一个重要概念的实现:探索和利用之间的权衡。探索有助于模型不陷入局部最小值,通过不时探索明显次优的行动,可能揭示更大的回报。另一方面,利用允许代理使用其对当前状态的了解来选择最有利可图的动作。在大多数 RL 代理中,epsilon 在开始时很高(接* 1.0),并且随着时间的推移逐渐降低到 0,因为代理在给定状态下对动作的学习值变得更有信心。
select_action 函数还调用 self.forward (RLAgent。Forward),下面是这个函数的代码,
def **forward**(self, observation, remember_for_backprop=True):
vals = np.copy(observation)
index = 0
for layer in self.layers:
vals = layer.forward(vals, remember_for_backprop)
index = index + 1
return vals
上面的 RLAgent.forward 函数有一个简单的循环。它将输入(我们试图决定行动过程的观察)传递给网络,并获得每个行动的一组值。它在内部调用 NNLayer.forward 函数,收集每一层的输出并将其传递给下一层。为了完成 select action 函数的实现,下面是最后一部分 NNLayer.forward 函数。remember_for_backprop 参数是一个布尔值,它指定是否需要存储某些计算值,以防止权重更新期间的重复计算(这将在反向传播一节中详细解释)。
# Compute the forward pass for this layer
def **forward**(self, inputs, remember_for_backprop=True):
input_with_bias = np.append(np.ones((len(inputs),1)),inputs, axis=1)
unactivated = np.dot(input_with_bias, self.weights)
output = unactivated
if self.***activation_function*** != None:
output = self.***activation_function***(output)
if remember_for_backprop:
# store variables for backward pass
self.***backward_store_in*** = input_with_bias
self.***backward_store_out*** = np.copy(unactivated)
return output
这个功能—
- 将偏差项追加到输入中。
- 计算该层的输入和权重矩阵的乘积。
- 获取步骤 2 的输出,如果已经为该层定义了一个激活函数(在我们的例子中是 ReLU),则通过该函数发送这个输出。
让我们在 RLAgent 的 init 函数中添加这些层的实例化,
def **__init__**(self, env):
self.***env*** = env
self.***hidden_size*** = 24
self.***input_size*** = env.observation_space.shape[0]
self.***output_size*** = env.action_space.n
self.***num_hidden_layers*** = 2
self.***epsilon*** = 1.0 self.***layers*** = [NNLayer(self.input_size + 1, self.hidden_size, activation=relu)]
for i in range(self.num_hidden_layers-1):
self.***layers***.append(NNLayer(self.hidden_size+1, self.hidden_size, activation=relu))
self.***layers***.append(NNLayer(self.hidden_size+1, self.output_size))
你可以看到上面我们总共有 2 个隐藏层和 1 个输出层。此外,在除了输出层之外的所有层中,我们都使用了一个激活函数,叫做RectivedLlinearUnit(ReLU)。这个函数是一个非常简单的函数,它给我们的神经网络引入了足够的非线性。这是它的实现,
def **relu**(mat):
return np.multiply(mat,(mat>0))
此函数接收一个矩阵,并返回另一个具有相同值的矩阵,其中原始矩阵大于 0,所有其他值都为 0。最后,让我们将代理的初始化和 epsilon decay 添加到主程序循环中。这是新的主程序的样子:
# Global variables
NUM_EPISODES = 10
MAX_TIMESTEPS = 1000
model = RLAgent(env)# The main program loop
for i_episode in range(NUM_EPISODES):
observation = env.reset()
# Iterating through time steps within an episode
for t in range(MAX_TIMESTEPS):
env.render()
action = model.select_action(observation)
observation, reward, done, info = env.step(action)
# epsilon decay
model.epsilon = model.epsilon if model.epsilon < 0.01 else model.epsilon*0.995
if done:
# If the pole has tipped over, end this episode
print('Episode {} ended after {} timesteps, current exploration is {}'.format(i_episode, t+1,model.epsilon))
break
这个模型目前做什么?它随机初始化神经网络的权重,并基于这些权重计算任何给定状态下的动作值。然而,我们需要一种方法来改进网络中的权重值,以便代理能够在任何给定的状态下采取最佳行动。如前所述,这是通过随机梯度下降实现的,并通过称为反向传播的技术实施。我将在下一篇文章中详细介绍反向传播以及强化学习的相关理论。
总而言之,这就是我们迄今为止所取得的成就:
- 编写了与 OpenAI gym 中的 CartPole 的环境空间进行交互的主要程序组件。
- 将强化学习代理及其组件神经网络层封装在各自的类中。
- 为我们的深度学习强化学习代理编码和初始化神经网络架构。
- 实施“前馈”计算,通过神经网络传播对环境的观察,以计算行动值。
在下一篇文章中,我们将致力于实现以下目标:
- 检查并形式化一个“累积奖励”的概念,即一个代理人在特定的状态下对掷球问题的期望。
- 理解代理应如何更新神经网络中的权重,以更接*其想法,即在特定状态下采取特定行动预期的正确累积回报。
- 实现反向传播——实现上面 2 中提到的目标的算法。
下次见!
使用 Tensorflow 从头开始创建更深的瓶颈 ResNet
我们将看到如何使用 Tensorflow 2.0 从头开始实现 ResNet50
图一。剩余块和跳过连接(来源:图片由作者创建)
可以看出,通常较深的神经网络比浅的神经网络性能更好。但是,深度神经网络面临一个共同的问题,称为“消失/爆炸梯度问题”。为了克服这个问题,提出了 ResNet 网络。
原文链接—https://arxiv.org/abs/1512.03385
剩余块:
ResNets 包含剩余块。如图 1 所示,存在激活‘al’,随后是具有 ReLU 非线性的线性层,‘al+1’。接着是另一个线性层,带有另一个非线性,“a l+2 ”。这是正常或普通神经网络的样子。ResNet 增加了跳过连接。在 ResNet 中,来自‘al的信息被快进并复制到‘al+1之后的线性层之后,ReLU 非线性之前。现在具有跳跃连接的整个块被称为残余块。图 2 显示了 ResNet 原始论文中看到的残余块。跳跃连接有助于跳过几乎两层,将其信息传递到神经网络的更深处。
图二。ResNet 论文中显示的残余块(来源:ResNet 论文原文)
使用残余块可以训练更深层次的神经网络。因此,ResNet 论文的作者将这些残差块一个接一个地堆叠起来,形成一个深度残差神经网络,如图 3 所示。
图 3。34 层*面网络与 34 层残差网络的图像片段(来源:原始 ResNet 论文)
可以看出,当使用诸如 SGD 的优化算法来训练深度*面网络时,理论上训练误差应该随着神经网络中层数的增加而不断减小,但实际情况并非如此。在减少到某一层之后,训练误差又开始增加。但是使用残余块可以克服这个问题,并且即使当层数显著增加时,训练误差也保持减小。这可以在图 4 中看到。
图 4。普通网络与 ResNet 的训练误差。X 轴表示层数的增加,y 轴表示训练误差(来源:图片由作者提供)
更深的瓶颈 ResNet 架构:
图 5。普通残余块 vs 更深的瓶颈残余块(来源:原 ResNet 论文)
图 5 显示了普通剩余块与更深瓶颈剩余块之间的差异。在公共残差块中,有两个具有 3×3 滤波器的卷积层。在瓶颈架构中,有 3 个卷积层,而不是 2 个。这三层是 1x1、3x3 和 1x1 卷积,其中 1x1 层负责减少然后增加(恢复)维度,而 3x3 层则成为具有较小输入/输出维度的瓶颈。
图 6。瓶颈残余块—身份块(来源:图片由作者创建)
如图 6 所示,瓶颈残差块有一个 1x1 的 Conv 层,然后是批规范化和 ReLU 激活,接着是一个 3x3 的 Conv 层,然后是批规范化和 ReLU,最后是一个 1x1 的 Conv 层和一个批规范化。这连接到跳过连接输入,然后应用最终 ReLU 激活。这是第一个瓶颈层版本(身份块)。“f”代表每个 Conv 层中滤光器的数量。
图 7。瓶颈剩余块—投影版本(来源:图片由作者创建)
瓶颈层的第二个版本(投影)与第一个版本非常相似,除了它有一个额外的 1x1 Conv 层,然后是在跳过连接上出现的批量归一化。跳过连接上的额外 Conv 层确保残差块左侧的滤波器数量与残差块右侧的滤波器数量相同。这允许将输入与残差块相加,而没有任何误差。此外,如果我们需要将步添加到左侧 Conv 层以减小图像的大小,我们可以通过在跳过连接中在 Conv 层上添加类似数量的步来确保来自前一层的输入也具有相同的大小。
使用 Tensorflow 创建 ResNet50】
图 8。我们将创建一个 50 层的 ResNet。图像显示了网络中使用的所有块(来源:原始 ResNet 文件)
- 在 ResNet 论文中,提到了在每个卷积块之后和激活函数之前使用批量归一化。
- 该网络始于 Conv 层,其具有 7×7 的核形状和 64 个核(滤波器),步长为 2。随后是批量规范化和 ReLU 激活。
- 接下来是最大 3x3 的池层和步幅 2。
- 其后是 ResNet(剩余/瓶颈)块。有 4 个 ResNet 块,每个块中的内核数量和内核大小如图 8 所示。
- 最后,有一个*均池层,一个完全连接的层,然后是 Softmax 激活。
算法:
- 从 Tensorflow 导入所有必要的层
- 为 conv-巴奇诺姆-雷卢块写一个函数
- 为身份瓶颈块编写一个函数
- 为投影瓶颈块编写一个函数
- 为 ResNet 块编写一个函数
- 一起使用所有函数来创建模型
导入 TensorFlow 和所有必需的库。
*#import the libraries*
**from** **tensorflow.keras.layers** **import** Input, Conv2D, BatchNormalization
**from** **tensorflow.keras.layers** **import** MaxPool2D, GlobalAvgPool2D
**from** **tensorflow.keras.layers** **import** Add, ReLU, Dense
**from** **tensorflow.keras** **import** Model
创建 conv-巴奇诺姆-雷鲁块的函数
这是基本的构建模块。它包含一个卷积层,随后是 BatchNormalization,接着是 ReLU 激活功能。
*#Conv-BatchNorm-ReLU block*
**def** conv_batchnorm_relu(x, filters, kernel_size, strides=1):
x = Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding = 'same')(x)
x = BatchNormalization()(x)
x = ReLU()(x)
**return** x
功能创建身份瓶颈块
正如我们前面所看到的,一个身份块包含 3 个卷积层,每一层之后是批量标准化和 ReLU 激活,除了最后一层,它首先添加到跳过连接,然后才应用 ReLU 激活。
此外,前两个卷积层的滤波器数量相同,但最后一个卷积层的滤波器数量是原来的 4 倍。
如图 8 所示,“conv2_x”的前两个卷积模块有 64 个滤波器,最后一个卷积模块有 64*4=256 个滤波器。对所有的标识块重复同样的操作。
*#Identity block*
**def** identity_block(tensor, filters):
x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=1)
x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
x = BatchNormalization()(x)
x = Add()([tensor,x]) *#skip connection*
x = ReLU()(x)
**return** x
创建投影块的功能
投影块的左侧与标识块相似,只是步数不同。
投影块的右侧包含 1×1 卷积层,其滤波器数量是左侧第一层的 4 倍,跨距数量相同。这确保了原始输入和跳过连接具有相同数量的滤波器和相同的步距,否则矩阵加法将会出错。
*#Projection block*
**def** projection_block(tensor, filters, strides):
*#left stream*
x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=strides)
x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
x = BatchNormalization()(x)
*#right stream*
shortcut = Conv2D(filters=4*filters, kernel_size=1, strides=strides)(tensor)
shortcut = BatchNormalization()(shortcut)
x = Add()([shortcut,x]) *#skip connection*
x = ReLU()(x)
**return** x
创建 ResNet 块的函数
如图 8 所示,每个模块“conv2_x”、“conv3_x”等都重复多次。“conv2_x”重复 3 次,“conv3_x”重复 4 次,“conv4_x”重复 6 次,“conv5_x”重复 3 次。
在每种情况下,第一个块是投影块,其余的是单位块。所以对于‘con v2 _ x’,第一个块是投影块,另外两个重复是单位块。
第一块是投影的原因是为了确保来自跳过连接的输入和该块的实际输出的步长和滤波器数量是相同的。在投影块中,第一卷积层的跨距=2。这意味着,如果输入的是一个图像,该层之后的图像的大小将会减小。但是跳过连接中的输入仍然具有先前的图像大小。添加两个不同大小的图像是不可能的。所以 skip 连接也有一个 stride=2 的卷积层。这确保了图像尺寸现在是相同的。
在下面的重复中,步幅被设置为 1,因此图像大小保持不变,并且不需要投影块。因此,除了每个 ResNet 块的第一次重复之外,所有其他重复都使用相同的块。
*#Resnet block*
**def** resnet_block(x, filters, reps, strides):
x = projection_block(x, filters, strides)
**for** _ **in** range(reps-1):
x = identity_block(x,filters)
**return** x
创建模型
既然所有的块都准备好了,那么现在可以按照图 8 创建模型了。
*#Model*
input = Input(shape=(224,224,3))
x = conv_batchnorm_relu(input, filters=64, kernel_size=7, strides=2)
x = MaxPool2D(pool_size = 3, strides =2)(x)
x = resnet_block(x, filters=64, reps =3, strides=1)
x = resnet_block(x, filters=128, reps =4, strides=2)
x = resnet_block(x, filters=256, reps =6, strides=2)
x = resnet_block(x, filters=512, reps =3, strides=2)
x = GlobalAvgPool2D()(x)
output = Dense(1000, activation ='softmax')(x)
model = Model(inputs=input, outputs=output)
model.summary()
输出片段:
绘制模型
**from** **tensorflow.python.keras.utils.vis_utils** **import** model_to_dot
**from** **IPython.display** **import** SVG
**import** **pydot**
**import** **graphviz**
SVG(model_to_dot(model, show_shapes=**True**, show_layer_names=**True**, rankdir='TB',expand_nested=**False**, dpi=60, subgraph=**False**).create(prog='dot',format='svg'))
输出片段:
使用 Tensorflow 从头开始创建 ResNet 50 的完整代码:
***#import the libraries***
**from** **tensorflow.keras.layers** **import** Input, Conv2D, BatchNormalization
**from** **tensorflow.keras.layers** **import** MaxPool2D, GlobalAvgPool2D
**from** **tensorflow.keras.layers** **import** Add, ReLU, Dense
**from** **tensorflow.keras** **import** Model***#Conv-BatchNorm-ReLU block*****def** conv_batchnorm_relu(x, filters, kernel_size, strides=1):
x = Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding = 'same')(x)
x = BatchNormalization()(x)
x = ReLU()(x)
**return** x***#Identity block*****def** identity_block(tensor, filters):
x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=1)
x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
x = BatchNormalization()(x)
x = Add()([tensor,x]) *#skip connection*
x = ReLU()(x)
**return** x
***#Projection block***
**def** projection_block(tensor, filters, strides):
*#left stream*
x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=strides)
x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
x = BatchNormalization()(x)
*#right stream*
shortcut = Conv2D(filters=4*filters, kernel_size=1, strides=strides)(tensor)
shortcut = BatchNormalization()(shortcut)
x = Add()([shortcut,x]) *#skip connection*
x = ReLU()(x)
**return** x***#Resnet block***
**def** resnet_block(x, filters, reps, strides):
x = projection_block(x, filters, strides)
**for** _ **in** range(reps-1):
x = identity_block(x,filters)
**return** x***#Model***
input = Input(shape=(224,224,3))
x = conv_batchnorm_relu(input, filters=64, kernel_size=7, strides=2)
x = MaxPool2D(pool_size = 3, strides =2)(x)
x = resnet_block(x, filters=64, reps =3, strides=1)
x = resnet_block(x, filters=128, reps =4, strides=2)
x = resnet_block(x, filters=256, reps =6, strides=2)
x = resnet_block(x, filters=512, reps =3, strides=2)
x = GlobalAvgPool2D()(x)
output = Dense(1000, activation ='softmax')(x)
model = Model(inputs=input, outputs=output)
model.summary()
参考文献:
- 何、、任、,深度残差学习用于图像识别,2015, arXiv:1512.03385【cs .简历]
使用 TensorFlow 创建 DenseNet 121
在本文中,我们将了解如何使用 Tensorflow 从头开始创建 DenseNet 121 架构。
图 1:DenseNet 中的各种块和层(来源:dense net 原始文件)
DenseNet 论文链接:https://arxiv.org/pdf/1608.06993.pdf
DenseNet(密集卷积网络)是一种架构,专注于通过使用层间更短的连接,使深度学习网络更深入,但同时使它们更有效地训练。DenseNet 是一个卷积神经网络,其中每一层都连接到网络中更深层的所有其他层,即第一层连接到第二层、第三层、第四层等等,第二层连接到第三层、第四层、第五层等等。这样做是为了实现网络层之间的最大信息流。为了保持前馈性质,每一层从所有前面的层获得输入,并将它自己的特征映射传递给它后面的所有层。与 Resnets 不同,它不是通过求和来组合特征,而是通过连接它们来组合特征。因此“第 I”层具有“I”个输入,并且由所有其前面的卷积块的特征图组成。它自己的特征地图被传递给所有下一个“I-i”层。这在网络中引入了'(I(I+1))/2 '连接,而不是像传统深度学习架构中那样仅仅是' I '连接。因此,它比传统的卷积神经网络需要更少的参数,因为不需要学习不重要的特征映射。
DenseNet 由两个重要的模块组成,而不是基本的卷积层和池层。它们是致密块体和过渡层。
接下来,我们看看所有这些块和层是什么样子,以及如何用 python 实现它们。
图 DenseNet121 框架(来源:原始 DenseNet 论文,由作者编辑)
DenseNet 从一个基本的卷积和池层开始。然后是一个密集块后面跟着一个过渡层,另一个密集块后面跟着一个过渡层,另一个密集块后面跟着一个过渡层,最后是一个密集块后面跟着一个分类层。
第一卷积块有 64 个大小为 7×7 的滤波器,跨距为 2。接下来是最大池层,最大池为 3×3,跨距为 2。这两行可以用 python 中的以下代码来表示。
input = Input (input_shape)
x = Conv2D(64, 7, strides = 2, padding = 'same')(input)
x = MaxPool2D(3, strides = 2, padding = 'same')(x)
定义卷积块 —输入后的每个卷积块有如下顺序:BatchNormalization,后面是 ReLU activation,然后是实际的 Conv2D 层。为了实现这一点,我们可以编写下面的函数。
*#batch norm + relu + conv*
**def** bn_rl_conv(x,filters,kernel=1,strides=1):
x = BatchNormalization()(x)
x = ReLU()(x)
x = Conv2D(filters, kernel, strides=strides,padding = 'same')(x)
**return** x
图 3。密集块(来源:DenseNet 纸-由作者编辑)
定义密集块 —如图 3 所示,每个密集块都有两个卷积,内核大小分别为 1x1 和 3x3。在密集块 1 中,重复 6 次,在密集块 2 中重复 12 次,在密集块 3 中重复 24 次,最后在密集块 4 中重复 16 次。
在密集块中,每个 1x1 卷积具有 4 倍数量的过滤器。所以我们使用 4 *滤镜,但是 3 * 3 滤镜只出现一次。此外,我们必须连接输入和输出张量。
使用“for 循环”,每个模块分别重复 6、12、24、16 次。
**def** dense_block(x, repetition):
**for** _ **in** range(repetition):
y = bn_rl_conv(x, 4*filters)
y = bn_rl_conv(y, filters, 3)
x = concatenate([y,x])
**return** x
图 4:过渡层(来源:DenseNet 论文-作者编辑)
定义过渡层 —在过渡层,我们要将通道数量减少到现有通道的一半。有一个 1x1 卷积层和一个 2x2 *均池层,步长为 2。在函数 bn_rl_conv 中已经设置了 1x1 的内核大小,所以我们不需要明确地再次定义它。
在过渡层中,我们必须将通道减少到现有通道的一半。我们有输入张量 x,我们想知道有多少个通道,我们需要得到其中的一半。因此,我们可以使用 Keras backend (K)获取张量 x,并返回一个维度为 x 的元组。我们只需要该形状的最后一个数字,即过滤器的数量。所以我们加[-1]。最后,我们可以将这个数量的滤波器除以 2,得到想要的结果。
**def** transition_layer(x):
x = bn_rl_conv(x, K.int_shape(x)[-1] //2 )
x = AvgPool2D(2, strides = 2, padding = 'same')(x)
**return** x
我们已经完成了密集数据块和过渡层的定义。现在我们需要将密集的块和过渡层堆叠在一起。所以我们写了一个 for 循环来运行 6,12,24,16 次重复。因此循环运行 4 次,每次使用 6、12、24 或 16 中的一个值。这完成了 4 个密集块和过渡层。
**for** repetition **in** [6,12,24,16]:
d = dense_block(x, repetition)
x = transition_layer(d)
最后是 GlobalAveragePooling,接下来是最终的输出层。我们在上面的代码块中看到,密集块是用‘d’定义的,在最后一层,密集块 4 之后,没有过渡层 4,而是直接进入分类层。因此,“d”是应用 GlobalAveragePooling 的连接,而不是“x”上的连接。另一种方法是从上面的代码中删除“for”循环,然后一层接一层地堆叠这些层,而没有最后的过渡层。
x = GlobalAveragePooling2D()(d)
output = Dense(n_classes, activation = 'softmax')(x)
现在我们已经把所有的模块放在一起了,让我们把它们合并起来,看看整个 DenseNet 架构。
完成 DenseNet 121 架构:
**import** **tensorflow** **as** **tf**
**from** **tensorflow.keras.layers** **import** Input, Conv2D, BatchNormalization, Dense
**from** **tensorflow.keras.layers** **import** AvgPool2D, GlobalAveragePooling2D, MaxPool2D
**from** **tensorflow.keras.models** **import** Model
**from** **tensorflow.keras.layers** **import** ReLU, concatenate
**import** **tensorflow.keras.backend** **as** **K***# Creating Densenet121***def** densenet(input_shape, n_classes, filters = 32):
*#batch norm + relu + conv*
**def** bn_rl_conv(x,filters,kernel=1,strides=1):
x = BatchNormalization()(x)
x = ReLU()(x)
x = Conv2D(filters, kernel, strides=strides,padding = 'same')(x)
**return** x
**def** dense_block(x, repetition):
**for** _ **in** range(repetition):
y = bn_rl_conv(x, 4*filters)
y = bn_rl_conv(y, filters, 3)
x = concatenate([y,x])
**return** x
**def** transition_layer(x):
x = bn_rl_conv(x, K.int_shape(x)[-1] //2 )
x = AvgPool2D(2, strides = 2, padding = 'same')(x)
**return** x
input = Input (input_shape)
x = Conv2D(64, 7, strides = 2, padding = 'same')(input)
x = MaxPool2D(3, strides = 2, padding = 'same')(x)
**for** repetition **in** [6,12,24,16]:
d = dense_block(x, repetition)
x = transition_layer(d) x = GlobalAveragePooling2D()(d)
output = Dense(n_classes, activation = 'softmax')(x)
model = Model(input, output)
**return** modelinput_shape = 224, 224, 3
n_classes = 3model = densenet(input_shape,n_classes)
model.summary()
输出:(假设最后 3 节课—模型总结的最后几行)
要查看架构图,可以使用下面的代码。
**from** **tensorflow.python.keras.utils.vis_utils** **import** model_to_dot
**from** **IPython.display** **import** SVG
**import** **pydot**
**import** **graphviz**
SVG(model_to_dot(
model, show_shapes=**True**, show_layer_names=**True**, rankdir='TB',
expand_nested=**False**, dpi=60, subgraph=**False**
).create(prog='dot',format='svg'))
输出—图表的前几块
这就是我们实现 DenseNet 121 架构的方式。
参考文献:
- 1.黄高和刘庄以及劳伦斯·范德马腾和基利安·q·温伯格,密集连接的卷积网络,arXiv 1608.06993 (2016 年)
创建有效的行业-学术人工智能合作伙伴关系
活动讲座
达林·格雷厄姆| TMLS2019
https://torontomachinelearning.com/
关于演讲者
Darin 在领导创新计划方面拥有 20 多年的经验,这些创新计划将创意推向市场。他在领导 R&D 项目和帮助加拿大、美国、新西兰和英国建立创新生态系统方面拥有丰富的国际经验。
作为合作的倡导者,他与各种独特的团体密切合作,让行业与学术和研究机构合作,以增强基于技术的应用成果,特别是与人工智能(AI)相关的成果,通过增加知识和帮助开发人力资本来促进经济增长。
Darin 刚刚担任了一个新角色,负责 LG 电子的 R&D 战略和运营人工智能活动,在多伦多建立了他们的新实验室。最*,作为创始运营团队的一员,Darin 帮助领导了 Vector Institute 的创建和形成,这是加拿大首屈一指的人工智能研究机构。
他还帮助建立和启动了三星在多伦多的人工智能实验室。他曾在多个组织担任主要领导职务,包括 ORION(安大略省研究和创新光纤网络)、NZi3(新西兰 ICT 创新研究所)和 CITO(安大略省通信和信息技术,安大略省卓越中心)。
Darin 在多伦多大学获得了航空航天工程博士学位,他的论文重点是自主机器人控制系统的高级神经网络;多伦多大学航空航天工程硕士,滑铁卢大学计算机科学和应用数学学士。
使用机器学习创建面部识别软件
使用 Keras 和 OpenCV
虽然用机器学习来进行面部识别的想法很早就被人知道了,但是自己创作这个软件还是挺有用的。
概念:
本文的目标是创建一个二元分类面部识别网络,允许一个人访问,而不允许另一个人访问。
我将使用 open-cv 访问计算机中的内置摄像头,并使用 haar-cascade 分类器来检测图像中的人脸。收集数据后,我会使用卷积神经网络对数据进行模型训练。
之后,当 OpenCV 检测到被授予访问权限的人时,它会在他/她的脸部周围画一个正方形,正方形上有“访问已验证”的字样。
代码:
from numpy import unique
from numpy import argmax
import os
import cv2
from PIL import Image
import numpy as np
from tensorflow.keras import Sequential
from tensorflow.keras import optimizers
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Dropout
除了标准的 numpy 和 os 库用于数据访问和数据操作,我还使用 open-cv 和 PIL 进行图像处理。
def data_path():
file = 'XXXXXXXX'
os.chdir(file)
files = os.listdir()
files.remove('.DS_Store')
return file,files
这是访问你计算机中数据的功能。要使此函数工作,请将变量文件更改为数据所在的相关目录。
def data_setup(files,file):
pixels = [0]*len(files)
answers = list()
print(len(files))
for i in range(len(files)):
image = Image.open(files[i])
pixels[i]= np.asarray(image)
pixels[i] = pixels[i].astype('float32')
pixels[i] /= 210.0
if files[i][0] == 'm':
answers.append(1)
elif files[i][0] == 'n':
answers.append(0)
dataset = np.array(pixels)
for i in range(len(dataset)):
dataset[i] = dataset[i].reshape(320,320)
return np.asarray(dataset),np.asarray(answers)
该函数访问数据,并将所有照片重新整形为 320 x 320 的图片。然后,它返回 X 和 y 值,即数据和真值。
def train(data,answers):
x_train = data
y_train = answers
x_train = np.array(x_train)
x_train = x_train.reshape((x_train.shape[0], x_train.shape[1], x_train.shape[2], 1))
print(x_train.shape)
in_shape = x_train.shape[1:]
print(len(data),len(answers))
model = Sequential()
model.add(Conv2D(10, (3,3), activation='relu', kernel_initializer='he_uniform', input_shape=in_shape))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(10, (3,3), activation='relu', kernel_initializer='he_uniform', input_shape=in_shape))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(1,activation ='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy',metrics = ['accuracy'])
model.fit(x_train, y_train, epochs=100, batch_size=100, verbose = 2, validation_split = 0.33)
return model
这个脚本创建、编译和训练卷积网络。使用的损失是二进制交叉熵,度量是准确度,因为我们希望网络以高准确度预测正确的人脸。
def face_recognition(model):
dirx = 'XXXXXXXXXXXXXXXXXXXXXXXXXX'
os.chdir(dirx)
face_cascade = cv2.CascadeClassifier('cascades/data/haarcascade_frontalface_alt.xml')
cap = cv2.VideoCapture(0)
while True:
ret,frame = cap.read()
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray,scaleFactor = 1.05, minNeighbors = 5)
for (x,y,w,h) in faces:
print('Face Detected')
roi_gray = gray[y:y+h,x:x+w]
roi_color = frame[y:y+h,x:x+w]
roi_gray = roi_gray.astype('float32')
roi_gray /= 210.0
classify = cv2.resize(roi_gray,(320,320))
if classify.shape == (320,320):
classify = classify.reshape((1, classify.shape[0], classify.shape[1], 1))
color = (255,0,0)
stroke = 2
end_cord_x = x+w
end_cord_y = y+h
pred = model.predict(classify)
print(pred)
if pred == 1:
cv2.rectangle(frame,(x,y),(end_cord_x,end_cord_y),color,stroke)
cv2.putText(frame, 'Access', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)
elif pred == 0:
cv2.rectangle(frame,(x,y),(end_cord_x,end_cord_y),color,stroke)
cv2.putText(frame, 'Denied Access', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)
if cv2.waitKey(20) & 0xFF == ord('q'):
break
cv2.imshow('frame',frame)
该脚本应用模型并实时进行预测,创建并标记有面的盒子。要使这个函数工作,您必须再次确定 haar-cascade 在哪个目录中。您可能需要将文件夹从外部位置移到正确的目录。
file,files=data_path()
data,answers = data_setup(files,file)
model = train(data,answers)
face_recognition(model)
程序的最后一部分操作所有功能,并启动实时面部识别。
如何改进我的计划:
当我写程序时,我总是分享一个强大的框架,在那里可以添加更复杂的功能。以下是一些可以增加程序功能的方法:
- 多类分类
当给定每个人的一组*衡的照片时,尝试训练模型来预测这个人是谁,而不是二进制分类。
- 添加更多的哈尔级联分类器
我使用的 haar-cascade 是正面人脸 cascade,只能检测正面人脸照片。你可以添加更多的分类器,这将使它工作,不管相机的角度。
我的链接:
如果你想看更多我的内容,点击这个 链接 。
用 OpenAI 的语言模型生成假新闻
GPT 和 NLP 时代的种族和政治
简介
在我们之前的文章中,我们讨论了最*发布的 OpenAI 的 GPT-3 及其在误传时代可能的影响。我们讨论了 GPT 系列的发展、架构和历史,并评估了在原始 出版物中呈现的一些功能和结果。
然而,由于这些是研究小组选择的文献实例,它们不具备使它们与当今紧迫主题相关的必要背景。虽然 GPT-3 尚未向公众发布,但被作者称为“太危险而不能发布”的 GPT-2 已于 2019 年初推出。因此,它为我们的问题陈述充当了这种模型的能力的一个伟大的估计者。
GPT-2 模型重量配置。(阿拉玛等人。艾尔
在我们的上一篇文章中,我们已经介绍了 GPT 系列模型的变压器型架构。最重要的是,该模型以 4 种不同的预设重量配置发布,参数从 1.17 亿延伸到 15 亿。可以想象,模型复杂度的增加伴随着各种 NLP 任务性能的提高。
GPT-2 在 WebText 数据集上接受训练,该数据集由用户在 Reddit *台上发布的 4500 万个出站链接的文本内容组成。网络文本中访问量排名前 15 位的域名是:谷歌、存档、博客、GitHub、纽约时报、Wordpress、华盛顿邮报、维基、BBC、卫报、易贝、Pastebin、CNN、雅虎!,以及《赫芬顿邮报》。
来自 GPT-2 原始出版物的问答示例。
该模型在生成今日最新头条的人类可接受内容方面表现如何?该型号在低重量配置下表现如何?我们能证实基于变压器模型的批评者提出的担忧吗?
让我们来看看 GPT-2 在生成媒体中最*一些相关主题的传真输出方面表现如何。
实施
由于从零开始训练 GPT-2 模型的复杂性,我们将采用 GPT 图书馆的 Lopez-Franco 的操场实现来检查使用条件样本生成的输出差异。你会在 GradientCrescent Github 上找到改编后的代码。
具体来说,我们将比较两种不同的 GPT-2 配置,小型模型(1.17 亿个参数)和大型模型(7.75 亿个参数),通过检查和在一些相关当代主题上产生的一些输出。请注意,GPT-2 有一个公开的超大模型(有高达 15 亿个参数),但由于它的大小,我们选择忽略它。**
笔记本是高度抽象的,条件样本生成的关键方法位于interactive _ conditional _ samples . py:
!python3 src/interactive_conditional_samples.py — model_name=’117M’ — nsamples=2 — top_k=40 — temperature=.80
model_name = '117M'
:型号选择。有 117 米、345 米、774 米和 1558 米型号可供选择。。seed = None
:随机值发生器,用来在将来产生相同的结果。nsamples = 1
:指定输出样本的数量。length = None
:每个样本要打印的令牌数(字数)。batch_size= 1
:你想同时处理多少个输入。temperature = 1
:在 0 和 1 之间浮动。在 softmax 层之前进行采样之前,对输出逻辑进行缩放。更高的温度导致更多的随机完井。top_k = 0
:控制分集的整数值。截断被认为具有最高值的逻辑集合。1 表示每一步只考虑一个单词(标记),导致确定性完成。40 表示每一步考虑 40 个单词。0(默认值)是一个特殊设置,表示没有限制。
这个脚本是用 Tensorflow Core 编写的,它接受用户输入文本,将它们编码成标记化的表示形式,然后将它们输入到模型中,在指定的约束条件下生成输出。
with tf.Session(graph=tf.Graph()) as sess:
context = tf.placeholder(tf.int32, [batch_size, None])
np.random.seed(seed)
tf.set_random_seed(seed)
output = sample.sample_sequence(
hparams=hparams, length=length,
context=context,
batch_size=batch_size,
temperature=temperature, top_k=top_k, top_p=top_p
)saver = tf.train.Saver()
ckpt = tf.train.latest_checkpoint(os.path.join(models_dir, model_name))
saver.restore(sess, ckpt)while True:
raw_text = input(“Model prompt >>> “)
while not raw_text:
print(‘Prompt should not be empty!’)
raw_text = input(“Model prompt >>> “)
context_tokens = enc.encode(raw_text)
generated = 0
for _ in range(nsamples // batch_size):
out = sess.run(output, feed_dict={
context: [context_tokens for _ in range(batch_size)]
})[:, len(context_tokens):]
for i in range(batch_size):
generated += 1
text = enc.decode(out[i])
print(“=” * 40 + “ SAMPLE “ + str(generated) + “ “ + “=” * 40)
print(text)
print(“=” * 80)
由于该模型是在一年多前定型的,因此最*的主题(如最*的新冠肺炎疫情)不会出现在数据集中。对所有生成的样本进行了系统的交叉引用,得出的结论是,这些句子没有一个是逐字逐句从某篇文章中复制来的。所有样本的内容都是合成的,而不是记忆的。
我们来看一些话题。
论特朗普总统
775 米
117 米
虽然在这两个例子中,整体的流程和语法看起来很自然,但是两个模型都显示出不一致,这些问题在较小的模型中更加明显。
特朗普表示,“非美国”媒体正试图转移他所谓的“我们历史上最大的问题”,即美国和英国的“非美国”媒体。
“这家媒体,以色列的媒体,沙特阿拉伯和其他许多国家的媒体,试图把我们所有人都关进监狱,我的意思是,这是一种耻辱。我的意思是,我们在监狱里,”特朗普说。
- 美国传统中东盟友的所有媒体都试图“把他们关进监狱”是不太可能的。不幸的是,由于总统倾向于重复自己的话,区分真实和虚构变得更加困难。然而,大量的重复引起了怀疑。
大模型的文本要连贯得多,除了一句话:
特朗普曾表示,朝鲜应该在 2022 年之前成为“核大国”。
- 这将与美国对朝鲜的外交政策背道而驰。虽然这句话确实有道理,但它没有抓住朝鲜和全球对其核计划的看法之间的隐含关系。
关于奥巴马总统
775 米
117 米
虽然较大模型的输出对于人类输出来说似乎还过得去(除非有人被国税局杀害的指控),但较小模型的输出虽然语法正确,但表现出多个主题不一致:
曾在竞选中承诺“废除并取代”奥巴马医改的奥巴马表示,共和党的税收计划将通过减税 10%来伤害美国人。
- 减税通常被认为不会伤害美国人。
根据凯撒家庭基金会(Kaiser Family Foundation)的一项研究,该法案将影响 140 万居住在美国的人,高于前一年的 220 万。
- 受该计划影响的人数减少了,而不是增加了,所以从开始的一词是不正确的。
共和党人还表示,该计划将在 2030 年前减少 1300 亿美元的联邦赤字
- 减税不太可能减少联邦赤字。
但白宫表示,该法案不会改变该计划的核心税收结构,只会降低年收入超过 25 万美元的人的利率和所得税。
- 白宫减税的收入门槛作为一个可行的税收计划肯定很难实现。
论警察暴行
775 米
117 米
这两种配置之间的差异在这里更加明显,较小模型的输出几乎是无意义的。
这导致了*年来广泛的警察暴力行为,导致了 700 多人在收缴警察枪支时被捕。
- 警察暴力不太可能导致“警察的枪被拿掉”。可能是模型学习了从某人手中拿枪的概念,在这里犯了一个语法错误。
警察局长比尔·布拉顿(Bill Bratton)此前表示,他担心人们会因为最*一连串的枪击事件而离开警察队伍。他补充说,他担心他们将来可能会被警察开枪打死,他希望看到那些不在警察队伍中的人被开枪打死,以便那些不在服务中的人留在警察队伍中。
- 警察局长布拉顿的声明在这里是完全荒谬的。警察怎么会希望看到已经在枪击中丧生的人,或者担心前警察将来会被警察枪杀?
但当案件最终被提交到大陪审团时,大陪审团尚未决定是否起诉该男子,该案件目前正由 NYPD 调查。
纽约警察局一再声明,它从未见过以这种方式实施的“毒品相关或暴力犯罪”案件。
- 很可能 NYPD 在这个案子被带到大陪审团之前就已经在调查了。此外,没有详细说明犯罪的方式。
上黑人的命也是命
775 米
117 米
大模型的输出对于人类输出来说还过得去,除了集会组织者反复提到的。相比之下,较小的模型显示不一致。
警方被指控在 8 月份杀害罗德尼·金的事件中行为残忍,而金*年来在全国范围内一直颠倒黑白。但在当今世界,一名警官有一个暴力、虐待或以其他方式虐待的伴侣并不罕见,媒体通常不愿意报道这些案件。
- 罗德尼·金被杀的悲剧案件是警察暴行的一个很好的例子,但是“黑白颠倒”这句话没有语法意义。此外,最后一段提出了两个看似不相关的案例,这并没有增加论点,似乎不合适。
关于系统性种族主义
775 米
117 米
两个模型都显示出一些不一致。对于更大的模型,托马斯的评论是重复的,并且是不一致的。
托马斯一直是黑人的命也是命运动的倡导者,他认为警察经常成为暴力和种族主义言论的目标。
“他们觉得警方花的时间太长了,而且他们在执法方面做得不够。”
- 警察不太可能认为他们反应迟钝,做得不够。相反,少数民族被指定为种族主义的目标更有意义,这一点没有得到正确的推断。
较小的模型表现出明显更多的不一致性,标点符号也很差。
已故的迈克尔·b·威廉姆斯(Michael B. Williams)在他的新书《我们是谁》(Who We Are)中认为,美国的“种族鸿沟”不是一个“种族”问题。这不是阶级或种族的问题。换句话说,正如威廉姆斯在他的《种族与性别》中所写的,“美国是一个种族体系。
- 虽然可以推断出作者论点的主旨,但种族的重复是多余的。此外,在声称种族划分与种族概念无关后,宣布美国是“一个种族系统”是没有意义的。
结论
很明显,随着 GPT-2 模型复杂性的增加,我们观察到产生涵盖当今媒体头条主题的人工生成内容的能力增加。
相比之下,据报道最大的 GPT-3 模型拥有超过 1750 亿个参数,其性能与其复杂性相匹配。这种模型的扩散的影响,特别是当结合对特定情绪或色调的数据进行微调时,将产生模型,这些模型不仅能够生成特定主题的内容,还能够生成特定论点的内容。这将产生潜在的破坏性后果,因为它能够通过为期望的论点提供现实的来源来改变话语的背景。
幸运的是,这些模型的复杂性和资源密集性将限制其使用,目前只有资源极其丰富的组织,如国家级行为者。最终,解决“假新闻”危险的方法不在于简单地试图限制语言模型的扩散,而在于提高我们作为一个社会的信息素养和批判性思维的技能。
这就结束了我们对变压器类型模型的讨论。下一次,我们回到强化学习,并在 Pytorch 中实现价值学习,以进一步解决我们的代理在 Doom 中的表现。
我们希望你喜欢这篇文章,并希望你查看 GradientCrescent 上的许多其他文章,涵盖人工智能的应用和理论方面。为了保持对 GradientCrescent 的最新更新,请考虑关注该出版物并关注我们的 Github 资源库。
参考
我们已经训练了一个大规模的无监督语言模型,它可以生成连贯的文本段落,实现…
openai.com](https://openai.com/blog/better-language-models/) [## 用无监督学习提高语言理解
我们通过一个可扩展的、与任务无关的系统,在一系列不同的语言任务上获得了最先进的结果…
openai.com](https://openai.com/blog/language-unsupervised/) [## 根据人类偏好微调 GPT-2
我们已经对 774M 参数 GPT-2 语言模型进行了微调,在各种任务中使用了人类反馈,成功地匹配了…
openai.com](https://openai.com/blog/fine-tuning-gpt-2/)
用 Python 创建分形
在这个帖子里自己试试吧!
分形艺术— 来源
首先,什么是几何分形?几何分形是一种在不同尺度上具有重复结构的几何形状:无论你是否靠*图像,你都会看到相同的图案。或者,正如伯努瓦·曼德尔布罗所定义的,“一个粗糙或支离破碎的几何形状,它可以被分割成多个部分,每个部分(至少*似地)是整体的缩小版”。
现在,我们如何在 Python 中构建一个分形?假设我们在不同的尺度上重复一个结构,我们将需要应用一个递归解决方案。此外,我们将使用 turtle 来绘制分形。在这篇文章中,我们将同时绘制分形树和科赫雪花。
绘制分形树
为了创建一棵树,我们将每个分支分成两个子分支(左和右),并缩短新的子分支,直到达到我们自己定义的最小分支长度:
import turtleMINIMUM_BRANCH_LENGTH = 5def build_tree(t, branch_length, shorten_by, angle):
passtree = turtle.Turtle()
tree.hideturtle()
tree.setheading(90)
tree.color('green')build_tree(tree, 50, 5, 30)
turtle.mainloop()
到目前为止,我们只是定义了基础。我们导入了 turtle 并创建了 turtle 的一个实例。Turtle(),它将是在画布上移动并绘制我们的树的对象。然后我们用 setheading() 让它面朝上。我们还定义了递归函数的签名,如下所示:
- t:我们的海龟实例。
- branch_length:分支的当前长度,以像素为单位。
- shorten_by:决定子分支比父分支短多少像素。
- 角度:子分支从父分支出现的角度。
此外,我们已经定义了 MINIMUM_BRANCH_LENGTH(以像素为单位),它设置了创建更多子分支的最小阈值。
现在让我们构建递归函数的主体:
import turtleMINIMUM_BRANCH_LENGTH = 5def build_tree(t, branch_length, shorten_by, angle):
if branch_length > MINIMUM_BRANCH_LENGTH:
t.forward(branch_length)
new_length = branch_length - shorten_by t.left(angle)
build_tree(t, new_length, shorten_by, angle) t.right(angle * 2)
build_tree(t, new_length, shorten_by, angle) t.left(angle)
t.backward(branch_length)tree = turtle.Turtle()
tree.hideturtle()
tree.setheading(90)
tree.color('green')build_tree(tree, 50, 5, 30)
turtle.mainloop()
如您所见,如果 branch_length 小于 MINIMUM_BRANCH_LENGTH,我们就达到了基本情况。否则,我们绘制分支并继续创建子分支,方法是计算它们的长度,向左和向右旋转“角度”度,并用新值再次调用 build_tree。最后,我们向后移动到我们分支的根。
如果您执行该代码,应该会得到以下结果:
我们的分形树
最后,您可以随意使用这里的代码(和参数)!
画科赫雪花
在这篇文章的第二部分,我们将绘制一个更复杂的结构:科赫雪花。
首先,我们需要创建一个递归函数来创建科赫曲线,然后我们将连接其中的 3 条曲线来创建一个雪花。让我们从定义递归函数的参数开始:
- t:我们的海龟实例。
- iterations:表示列表下方图像中 n 的值(注意,n=0 表示一条*坦的线,这是我们递归函数的基本情况)。
- 长度:我们当前(子)雪花中每条边的长度。
- shortening_factor:确定创建新子雪花时边长除以的因子。
- 角度:确定新边出现的角度。
科赫曲线构建— 来源
一旦我们定义了我们的递归函数的基本结构,我们可能会达到以下几点:
import turtledef koch_curve(t, iterations, length, shortening_factor, angle):
passt = turtle.Turtle()
t.hideturtle()for i in range(3):
koch_curve(t, 4, 200, 3, 60)
t.right(120)turtle.mainloop()
此时,我们只需实现递归函数。如果我们已经达到我们的基本情况,我们将只是画一条线。否则,我们将更新我们的参数(特别是迭代次数和长度)并调用我们的递归函数 4 次。在这些函数调用之间,我们会先向左,然后向右,最后再向左。让我们看看完整的实现是怎样的:
import turtledef koch_curve(t, iterations, length, shortening_factor, angle): if iterations == 0:
t.forward(length)
else:
iterations = iterations - 1
length = length / shortening_factor koch_curve(t, iterations, length, shortening_factor, angle)
t.left(angle)
koch_curve(t, iterations, length, shortening_factor, angle)
t.right(angle * 2)
koch_curve(t, iterations, length, shortening_factor, angle)
t.left(angle)
koch_curve(t, iterations, length, shortening_factor, angle)t = turtle.Turtle()
t.hideturtle()for i in range(3):
koch_curve(t, 4, 200, 3, 60)
t.right(120)turtle.mainloop()
如果您执行上面的代码,您应该会获得以下结果:
我们的科赫雪花
同样,您可以随意使用这里的代码(和参数)!
用 Mapbox 和 Python 制作高分辨率卫星影像
超高分辨率卫星和高程图像
对于我的一些个人项目,我想获得地球上特定点的超高分辨率卫星和高程图像。当我试图用谷歌搜索一些现有数据时,没有足够灵活的东西。我最终偶然发现了 Mapbox API,它允许我使用 URL 请求来获取任何位置和缩放级别的图像。除此之外,它还可以选择返回编码为 PNG 图像的高分辨率高程数据(参见上面的第二幅图像)。
获取地图框 API 密钥
第一步是访问https://docs.mapbox.com/api/注册并获得 API 密钥。这里我就不赘述了,因为这非常简单,但我们想要的只是看起来像这样的密钥:
一旦我们有了密钥,它将允许我们解析 Mapbox API,并获得一些漂亮的图像 tilesets 回来!请记住,有很多免费的访问,所以您不需要担心 API 的限制,除非您将它部署给更广泛的受众。
关于 Tilesets 的一个注记
我遇到的最大问题之一是理解 tilesets。基本上,我假设我可以在 API 中使用纬度/经度值,但是全局成像的工作方式是每个缩放级别有一组“tilesets”。缩放级别是从 0 到 15,其中 0 表示整个世界的图片,15 表示您可以看到自己的房子。瓦片集被索引为矩阵(x/y ),该矩阵基本上表示切片的地球图像。在缩放 0 时,在 x=y=1 时只有一个图块,因为它只是一个大图像。想象一下,我们放大到级别 1,实际上需要 4 张图片来覆盖地图。这意味着我们有四种组合(0,0),(0,1),(1,0),(1,1)。正如你可以想象的那样,变焦越深,需要的瓷砖就越多。
我将向您展示为给定的 lat/lng 组合获得一组瓷砖所需的代码,因此您不必担心它!经典的 python 有一个包来包所有的东西。
安装 Python 包和文件夹设置
按照传统,我们必须安装非标准软件包。为了弄清楚 lat/lng 到 tileset 之间的转换,我们使用了 mercantile 软件包(相当肯定是墨卡托和 tile 的组合)。我们还将安装 pillow ,这是一个广泛用于 python 的图像处理库。
pip install mercantile
pip install pillow
我们还将建立一些目录来存储卫星和高程图像的临时图像。
mkdir ./satellite_images
mkdir ./elevation_images
mkdir ./composite_images
合成图像目录是我们的最终图像将去的地方。
从 Lat/Lng 转换为 Tilesets
首先要做的是确定我们要扫描的位置。我住在多伦多附*,所以我想看看超高分辨率的海滨是什么样子!我设置了坐标(只要谷歌一下位置,在地图上点击右键,选择“这里有什么?”以获得坐标)
lat_lng = [43.640918, -79.371478]
接下来,我需要决定我想要查看的扫描范围。我用一个纬度/液化天然气三角洲因子来表示它,并应用它来找到我感兴趣的区域的左上角和右下角的选择。
delta=0.05
tl = [lat_lng[0]+delta, lat_lng[1]-delta]
br = [lat_lng[0]-delta, lat_lng[1]+delta]
z = 15 **# Set the resolution (max at 15)**
提取图像
下一步是从特定的 API URLs 中提取图像数据。Mapbox 有两个我们感兴趣的端点。一个提供高细节卫星图像,另一个提供编码高程映射。端点需要相同的输入,因此我总结如下:
Mapbox API URLs
mapbox . terrain-RGB/**{ z }/{ x }/{ y }@ 2x。pngraw?access _ token = {您的密钥}
mapbox . satellite**/{ z }/{ x }/{ y }@ 2x。pngraw?access _ token = {您的密钥}**
- map box . terrain-RGB:这是高程数据的端点
- 地图框。 卫星:这是卫星数据的终点
- {z}: 这是我们想要选择的缩放级别(0-15)
- {x}: 我们在给定缩放级别下请求的 x *铺
- {y}: 我们在给定缩放级别下请求的 y *铺
- {您的密钥}: 您向 Mapbox API 注册时请求的 API 密钥(看起来像PK . kjsalfjaeii 9789 F7 ah……)
- @2x.pngraw: 这表示我们希望图像是双倍分辨率(512x512)的原始 PNG 文件
如果你进入你的浏览器,用一组有效的输入导航到 URL,你应该看到一个漂亮的图像返回。这就是我们要用 python 实现的自动化。
首先,我们需要使用前几节中定义的 x/y/z 来收集图像数组。
**import requests **# The requests package allows use to call URLS**
import shutil **# shutil will be used to copy the image to the local****# Loop over the tile ranges**
for i,x in enumerate(range(x_tile_range[0],x_tile_range[1]+1)):
for j,y in enumerate(range(y_tile_range[0],y_tile_range[1]+1)) **# Call the URL to get the image back**
r = requests.get('https://api.mapbox.com/v4/mapbox.terrain-
rgb/'+str(z)+'/'+str(x)+'/'+str(y)+'[@2x](http://twitter.com/2x).pngraw?
access_token=pk.eyJ1I....', stream=True) **# Next we will write the raw content to an image**
with open(‘./elevation_images/’ + str(i) + ‘.’ + str(j) + ‘.png’,
‘wb’) as f:
r.raw.decode_content = True
shutil.copyfileobj(r.raw, f) **# Do the same for the satellite data
** r =requests.get('https://api.mapbox.com/v4/mapbox.satellite/'+
str(z)+'/'+str(x)+'/'+str(y)+'[@2x](http://twitter.com/2x).pngraw?
access_token=pk.eyJ1I....', stream=True) with open(‘./satellite_images/’ + str(i) + ‘.’ + str(j) + ‘.png’,
‘wb’) as f:
r.raw.decode_content = True
shutil.copyfileobj(r.raw, f)**
在这一点上,我们有一组图像在它们的文件夹中,并以【x.y.png】的格式命名,这将使我们在稍后合成最终图像时更容易循环。
合成最终图像
我们的小项目快结束了。我们现在想做的是将所有较小的图像合成一个分辨率极高的大图像。为此,我们将使用 Pillow (python 包)创建一个新的 png。
首先,我们制作一个新的空白图像,设置成我们想要的最终尺寸。然后,我们将只是粘贴在有意义的偏移较小的图像。下面是一张 GIF 图片,直观地展示了代码的作用。
显示各个图像如何构成的动画。
****# Import the image, math and os libraries** import PIL
import mathfrom os import listdir
from os.path import isfile, join**# Loop over the elevation and satellite image set**
for img_name in [‘elevation’,’satellite’]: **# Make a list of the image names **
image_files = [‘./’+img_name+’_images/’ + f for f in
listdir(‘./’+img_name+’_images/’)] **# Open the image set using pillow** images = [PIL.Image.open(x) for x in image_files] **# Calculate the number of image tiles in each direction**
edge_length_x = x_tile_range[1] — x_tile_range[0]
edge_length_y = y_tile_range[1] — y_tile_range[0]
edge_length_x = max(1,edge_length_x)
edge_length_y = max(1,edge_length_y) **# Find the final composed image dimensions**
width, height = images[0].size
total_width = width*edge_length_x
total_height = height*edge_length_y **# Create a new blank image we will fill in**
composite = PIL.Image.new(‘RGB’, (total_width, total_height)) **# Loop over the x and y ranges**
y_offset = 0
for i in range(0,edge_length_x):
x_offset = 0
for j in range(0,edge_length_y): **# Open up the image file and paste it into the composed
image at the given offset position**
tmp_img = PIL.Image.open(‘./’+img_name+’_images/’ + str(i) +
‘.’ + str(j) + ‘.png’)
composite.paste(tmp_img, (y_offset,x_offset))
x_offset += width **# Update the width** y_offset += height **# Update the height****# Save the final image**
composite.save(‘./composite_images/’+img_name+’.png’)**
瞧。你现在应该有两张非常非常大并且分辨率惊人的图像。为了给你一个规模的感觉,看看下面的图片,显示了从上面放大的 GIF 版本。
上图的放大图显示了我们惊人的分辨率(图片大约 50Mb)。
男人看起来脆脆的!下面是高程数据的 GIF 图。
高程数据合成的动画。
在这一点上,你可以做你需要的最终图像。这整件事有两个好处。
- 这些图像分辨率极高
- 你可以很容易地选择任何你感兴趣的矩形地图
对于我自己的项目,我真的想要解码的高程数据。Mapbox 实际上告诉你如何解码像素,以获得低至 0.1 米的高程,但我只是在下面放了一个 python 脚本。
解码高程数据
****# Load the elevation image and convert it to RGBA**
elevation_raw = PIL.Image.open(‘./composite_images/elevation.png’)
rgb_elevation = elevation_raw.convert(‘RGBA’)**# Loop over the image and save the data in a list**
elevation_data = []
for h in range(rgb_elevation.height):
for w in range(rgb_elevation.width): **# Extract the pixel values**
R, G, B, A = rgb_elevation.getpixel((w, h)) **# Use Mapbox conversion from pixel to meters**
height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1) **# Append to our elevation data list**
elevation_data.append(height)**# Save to a json file**
import json
with open(‘./elevation.json’, ‘w’) as outfile:
json.dump(elevation_data, outfile)**
全部完成!现在你可以用它对原始图像进行分析或切片。(还可以做等高线,3D 可视化或者一堆东西!)
参考和链接
** [## 使用 Mapbox 和 Three.js 进行高分辨率制图
使用 Kaggle 笔记本探索和运行机器学习代码|使用来自非数据源的数据
www.kaggle.com](https://www.kaggle.com/kapastor/high-resolution-mapping-with-mapbox-and-three-js)**
用 Python 和 Folium 为 Instagram 创建交互式地图
学习使用 Python 的叶库制作漂亮的、定制的和交互式的地图,甚至合并您的 Instagram 帖子。
根据您的喜好创建交互式地图|作者图片
我周末的一个追求就是逛遍我所在的英国牛津郡地区的所有酒吧。随着时间的推移,这些大量的酒吧访问已经变成了一个受欢迎的“牛津郡酒吧”Instagram 页面。因此,当 Instagram 在 2016 年决定移除其“照片地图”功能时,这真是一个耻辱。原因是大多数用户实际上并没有使用它。但是,如果您的帐户可以很好地利用这样一个功能,那会怎么样呢?任何旅游、观光或本地博客页面肯定会从中受益。我花了很多时间摸索谷歌定制的、相当笨拙的“我的地图”功能,但结果从来都不太好,也不可能以任何方式整合 Instagram 帖子。嗯…,对于许多其他数据和 web 相关的任务,Python 有一个答案。
你需要什么
Python 有大量用于不同任务的库,当涉及到快速制作漂亮的交互式地图时,leav库做得非常好。好消息是,您不需要了解太多 Python 就能完成这项工作,因为我将一步一步地带您完成它。
让我们继续导入 Let 和我们将需要的其他依赖项(这些将在我们遇到它们时解释)。
现在,让我们创建一个地图-一个简单明了的底图:
location=[]
参数设置地图中心的坐标(在这里是英国牛津附*)。tiles=
参数告诉叶使用来自 OpenStreetMap (OSM)的免费可用的开源地图切片。有几个缩放设置,最后,control_scale=
增加了一个比例尺。leav 以 HTML 格式保存所有与地图相关的内容。这意味着您可以通过在浏览器中打开.html
文件来快速查看您的地图:
树叶地图的默认 OSM 底图-由一行 Python 生成!|作者图片
就是这样!一行 Python 代码,我们就已经有了一个明亮、多彩的底图。但地图从何而来?Folium 附带了一些内置的地图 tilesets,您可以使用tiles=
参数轻松地指定它们。默认情况下是 OSM tileset,但是 leav 也支持使用来自供应商的公开可用的地图切片,例如 【雄蕊】雷森林StadiaCarto等。这个链接提供了一些地图块之间的快速比较。如果您只是为了个人、非商业用途进行制图,地图切片应该可以免费使用。
对于我自己的“牛津郡的酒吧”地图,我希望对底图有更多的控制。我希望它看起来更环保,并包括所有可用的小径和人行道数据(这样人们就可以在他们选择的酒吧附*找到一个不错的散步场所)。 Mapbox 允许您定制自己的地图,然后通过您的个人 API 和访问令牌访问它,您可以将这些信息传递到 lets:
如果您使用的是第三方图块提供商,请始终记得添加一个attr=
参数,这样他们的工作就能得到适当的认可(这显示在地图的右下角)。
添加地图元素
现在让我们开始添加一些元素到地图上。我想添加的第一件事是显示牛津郡边界的覆盖图。我在 GeoJSON 文件中有这方面的数据。
保存地图,并在浏览器中打开。这是目前为止的情况:
Mapbox 为 Strava 和脸书等公司提供地图切片,现在您也可以使用它们了!|作者图片
现在我们开始更多的定制。首先,我们将加载一个图像,用作地图上标记的自定义图标。然后我们将加载另一个地理边界——这次是牛津城边界。你可以想象,与牛津郡的其他地方相比,牛津市中心的酒吧密度更高。为了避免大量的标记被一个接一个的绘制出来,以及一个看起来混乱的地图,我们将使用 leav 的MarkerCluster
插件(这是在第一步中导入的)。在低缩放级别下,标记聚类将位于牛津市边界内的所有标记组合在一起。只有放大时,各个标记才会分开。为此,我们将利用 Python 的 json 和 Shapely 库(也是上面导入的),它们对于任何类型的空间操作都非常有用。在下面的代码中,我们使用json
库读取文件,然后使用json
文件的geometry
属性定义一个形状:oxford_boundary
。最后,我们创建了MarkerCluster
,它也有许多自定义其行为的选项。
让我们添加一些标记
在一个单独的 Python 字典中,我已经存储了与我想要添加到地图中的每个酒馆相关联的所有数据。对于每个酒吧,都有一对坐标,一个 Instagram 帖子的链接,酒吧的网站和谷歌地图方向。我们将使用这些细节将 Instagram 帖子和网站链接合并到每个标记的弹出窗口中。字典是这样的:
现在是主要的跑腿工作。这里我们使用一个简单的for
循环来遍历我们想要创建标记的所有酒馆。
需要注意的一点是,每个标记都需要自己的CustomIcon
实例,因此我们在循环的每次迭代中都创建一个。标记弹出窗口使用 HTML 来设置样式。HTML 通常用于设计网站,但在这种情况下,它允许我们使用<iframe src={insta_post}
将 Instagram 帖子嵌入到弹出窗口中。同样,我们可以向弹出窗口添加文本,并按照我们想要的方式设置样式。每个标记都是用自己的CustomIcon
、Popup
、coordinates
和tooltip
(悬停)文本实例逐个创建的。for
循环的最后一部分使用oxford_boundary
形状来测试标记是否位于牛津城边界内。如果是,则添加到oxford_cluster
中,如果不是,则单独添加到地图中。最后,使用导入的LocateControl
插件添加一个地理位置按钮,并在 HTML 中设置浏览器标签标题。
最终的交互式地图
下面是它的预览图:
点击此处查看全互动地图|作者图片
牛津上空可以看到MarkerCluster
。如果单击,地图会放大以显示牛津聚类中包含的所有 11 个标记。每个标记都可以点击,以显示一个弹出窗口,并带有嵌入式 Instagram 帖子和网站链接。让我们来看看:
你几乎可以添加任何你想标记的弹出窗口!|作者图片
全功能互动地图可在此查看:https://pubs-of-oxfordshire-map.github.io/。
虽然在浏览器中打开本地的.html
文件可以很容易地查看地图,但是如果您想要在线获取它,您需要将它托管在某个地方。 Github Pages 提供了一个非常快速简单的托管服务,可以从你的.html
文件中创建一个网页,这就是我在酒吧地图中使用的。而且是免费的!
希望这能给你提供一些提示和灵感,让你尝试 Python 和 follow 的定制地图。鉴于 well 与各种第三方地图图块提供商集成得如此之好,其支持 HTML 的大量选项和使其足够强大,几乎可以实现任何您想要的外观。****
如果你对所有代码感兴趣,这里有一个链接,链接到牛津郡 Github Repo 的酒馆,地图就在那里。
如果你对牛津郡周围的酒吧感兴趣,一定要看看牛津郡 Instagram 页面 的 酒吧!🍺
附录-(图标)
我们可以在这里停下来,但是如果你想更进一步,可以通过编辑保存地图的.html
文件来添加 Favicons——也称为 tab——或网站图标。像 RealFaviconGenerator 这样的网站让你上传一张图片或标志,网站会生成一系列大小完全合适的图片,供各种操作系统和浏览器渲染。这些图像需要放在网站所在的根文件夹中。生成器还为您提供了一些 HTML 文本,您需要将这些文本插入到.html
文件的<head>
部分。这个文本将不同的浏览器指向正确的图像,这样你就可以在你的标签上看到一个漂亮的网站图标。
我们将利用 Python 的美汤库(开头导入)来读取.html
文件并找到<head>
部分。这使得为收藏夹图标添加tab_icon
HTML 文本变得很容易。
真的是这样!
我就说到这里,但是如果你有任何想法或问题,请留下你的评论🙂
感谢阅读!🍻
从简历中创建知识图并遍历它们
用数据做很酷的事情!
来源:克林特·王茂林在 Unsplash 上
介绍
知识图作为存储非结构化数据的数据结构越来越受欢迎。在这篇博客中,我们展示了简历中的关键元素是如何被存储和可视化为知识图表的。然后我们通过简历的知识图表来回答问题。我们的代码可以在这个 Github 链接上找到。
知识图是一种通过数据链接将特定领域的知识组织在本体中,从而使我们能够对其进行建模的图。然后,机器学习可以应用于知识图上,以获得洞察力。知识图具有图的所有主要组成部分——节点、边和它们各自的属性。
让我们先看看节点和边的定义:
节点 是图中线条相交或分叉的点。
边 是连接图中两个节点的线。
如下面的知识图所示,斯波克、星际迷航、星球大战、伦纳德·尼莫伊、科幻小说、欧比万·克诺比、亚历克·伊兹高尼是节点,角色、主演和流派是图的边。
图 1:知识图的一个例子。(来源:Maximilian Nickel 等《知识图的关系机器学习综述:从多关系链接预测到自动化知识图构建》)
上面的表示是一个知识图的例子,它存储了电影及其角色以及扮演这些角色的演员的信息。以这种形式存储信息使我们能够对特定领域的复杂知识进行建模,这样我们就可以在需要时通过结构化遍历给定的图来访问特定的信息。
要在知识图中结构化的信息以三元组的形式建模。在三元组中,通常有两个实体和实体之间的关系。例如,在上面的图表中,played(斯波克,伦纳德·尼莫伊)是一个三联体,其中实体是斯波克和伦纳德·尼莫伊,关系是 played。
创建简历技能知识图
在这个博客中,我们将为人们和他们在简历中提到的编程技能创建一个知识图表。这个想法是从简历中提取技能,并以图表格式建模,这样就更容易导航和提取具体信息。
出于这个博客的目的,我们将使用 3 份虚拟简历。
所以,现在让我们开始研究代码吧!完整的代码和简历分享在我的 Github repo 这里。
出于简化的目的,我们将只关注不同候选人知道的编程语言。
从简历中解析数据
首先,我们使用 regex 提取简历中提到的编程语言。下面分享了这方面的代码。
这里的输出是来自 3 个程序员的语言:
**Output:**['JavaScript', 'HTML5', 'PHP OOP', 'CSS', 'SQL', 'MySQL']['Python,R,CSS,PHP,Machine Learning']['HTML5', 'CSS', 'SQL', 'Jquery’, ‘PHP']
这里,为了简化起见,我们假设文件名是候选名,并且编程语言行具有相同的格式。
使用 Networkx 库创建知识图
现在,一旦我们有了这些,我们将使用 networkx 库从这些信息中构建一个知识图。 NetworkX 是一个 Python 包,用于创建、操作和研究复杂网络的结构、动态和功能。这个库包含了一系列的函数来创建不同类型的图形,遍历它们,并在它们的基础上执行一系列的操作和计算。
在我们的例子中,我们将使用 from_dict_of_lists()函数,使用它我们将从一个字典中创建一个有向图,该字典包含作为关键字的名称,以及作为值的相应编程语言。
首先,为了便于理解,让我们为每个候选人创建单独的图表:
这里,在第四行中,我们从前面创建的字典中创建一个 networkx 对象。这里的 create_using 参数指定了我们需要创建的图形类型。
不同类型的图形及其各自的 networkx 类在 networkx 文档中指定如下:
图 2:图形类型—来源:Networkx 文档
我们将使用多点图,因为我们需要在单个图中表示多个简历,并且需要显示有向边以便更清晰地表示。
可视化单个候选人的知识图表
接下来,我们将使用 matplotlib 创建一个图,并使用 networkx 绘制图形。在绘制知识图之前,我们必须首先指定图的布局。networkx 库提供了大约 7 种布局,您可以根据自己的使用情况使用这些布局来绘制图形。你可以在这里探索这些布局。
上述代码将为 Mathew 及其技能创建一个图表,如下所示:
图 3:候选人技能的知识图表。来源:在我的 Github 上分享的代码
同样,我们可以为其他两个候选人创建知识图表。
现在,让我们试着把这些图想象成一个连通图:
这里,我们使用 draw()函数通过指定的参数来绘制图形:
- With_labels —使用此标志,可以指定是否在图形中显示标签。
- Node _ color 使用此参数,可以指定节点的颜色。
- edge _ cmap——使用这个参数,可以指定图形的颜色映射。
- 位置-使用该参数,可以指定图形的布局。
- Node _ size 使用此参数,可以指定节点的大小。
- Font_size —使用该参数,我们定义了节点上使用的文本的字体大小。
结果图将如下所示:
图 4:简历中个人及其技能的关联知识图。来源:在我的 Github 上分享的代码
正如你在上面的图表中所看到的,我们现在已经创建了一个知识图表,包括每个候选人和他们知道的编程语言。
穿越我们的知识图谱
现在我们已经准备好了知识图,我们可以使用这个图通过基于图的操作来提取信息。有许多数学图形操作可用于从知识图中提取数据。
如果从头开始编写代码,遍历知识图可能会很乏味。为了简化这一点,最佳实践是将图形存储在 Neo4j 之类的图形数据库中,并在 SPARQL 中编写查询。出于这个博客的目的,我们将使用我们在上面的图 4 中创建的 networkx 图展示一些简单的提取技术。
假设你是一家公司的招聘人员,你收到了这些简历,并创建了上面的知识图。现在,你想知道候选人拥有的最流行的编程语言。
我们来试试吧!
用图的术语来说,我们可以将获取最流行的编程语言的问题转化为最多边指向的节点。想想看,一个节点连接的边数越多,知道该技能的候选人就越多。因此,我们只需提取连接边数最多的技能节点。那就简单了!
让我们看看如何使用 networkx 库来实现这一点:
**Output:**'CSS'3
这里,我们只是使用了 networkx 库中的一个名为 degree()的函数。该函数返回连接到特定节点的边的数量。在上面定义的函数 get_max_degree_node()中,我们遍历所有的技能节点(除了 name 节点之外的所有节点)并获取技能节点中的最大度数。
类似地,我们可以使用相同的函数来获得知道最多编程语言的候选人,而不是传递名称列表,我们传递技能列表,该列表必须从所有节点中删除。
skill_list = languages_mathew+languages_john+languages_maxmax_languages_degree, max_languages_node = get_max_degree_node(skill_list,G)print(max_languages_node)print(max_languages_degree)**Output:**Mathew Elliot6
这些是我们如何使用基本代码遍历知识图并从中提取信息的几个例子。如前所述,对于更高级的查询,建议将知识图存储在图数据库中,然后使用 SPARQL 等查询语言进行查询。
结论
在博客中,我们解释了什么是知识图,如何构建和遍历知识图。知识图有着广泛的应用,从对话代理,搜索引擎,问答系统等等。它们是需要存储大量互连数据的系统的理想选择。谷歌、IBM、脸书和其他技术公司在他们的系统中大量使用知识图表来将信息联系在一起,并非常有效地进行遍历。
在深度学习分析,我们非常热衷于使用机器学习来解决现实世界的问题。我们已经帮助许多企业部署了创新的基于人工智能的解决方案。如果你看到合作的机会,请通过我们的网站这里联系我们。这篇博客是由 Priya Dwivedi 和 Faizan Khan 写的。
参考
- 网络 x 库—https://networkx.github.io/
- 知识图表介绍—https://towardsdatascience.com/knowledge-graph-bb78055a7884
逐步创建最小的 Dag
开始使用 DAG 的第一个版本的四个建议
dagitty.net 的经典之作
免责声明——这篇文章假设你熟悉因果推理的语言,并且对使用 Dag 来描述因果模型和因果关系没有根本的本体论反对。
如果你在实证/定量领域工作,你可能会发现自己每天都在努力写下因果推理问题。如果你在你的因果冒险中也使用有向无环图(简而言之,Dag)(为了清晰、识别、去偏置或任何其他原因),那么你可能会认识到 Dag 可能会遭受一点“冷启动”问题。
事实上,直接从因果问题或变量列表开始很难画出最小的 DAG——或者,至少,我一直在与这种因果推理版本的文思枯竭作斗争。
在讲授辅助因果推理课程时,我与同样在绘制和连接大量给定变量时遇到困难的学生分享了一些提示和技巧,所有这些都与同一个因果问题有关。然后,我想我可以把这些分享给更广泛的、可能感兴趣的(也同样在挣扎的)观众。
因此,这里有一些实用的建议,可以帮助你开始使用最小 DAG,并更进一步识别和估计你感兴趣的因果效应。
定义因果量
定义因果问题将已经给出了因果效应中涉及的数量的意义。从因果问题开始,为了开始一个最小的 DAG,你需要定义至少三个关键变量:
- 经历某种状态变化的代理、单位或个人。
如果该单位是综合的(例如,*均销售额、*均价格变化),至少在编写 DAG 的第一个版本时,它可能有助于在单个规模上重新定义该单位(例如,不是*均销售额,而是一个客户的销售水*)。 - 一个结果变量。
- 一个状态、干预或治疗变量,当其发生变化时,被认为会导致单元结果的变化。
Pearl (2009)还建议在开始定义我们的因果模型时考虑其他量。这些量被称为因果亲本(PA),是结果变量的所有相关和直接(可观察)原因。如果因果父 PA 还影响系统中建模的其他变量,则它必须包含在变量列表中。如果你排除这样的变量,将会有未被观察到的干扰同时影响几个变量,这将导致许多随后的假设被违反。
写一个最小的 DAG
一旦你有了(1)谁/什么是分析单位,(2)结果,(3)治疗,(4)结果的直接因果父母的列表,你可以开始用几个步骤画 DAG:
- 写下结果变量
- 写下治疗变量,并将其与结果变量联系起来
- 写下结果的原因父母:结果的直接和相关的原因。使用箭头将它们与结果联系起来。
- 确保影响系统中不止一个变量(即不仅仅是结果)的结果的因果父项被明确定义,并且它们与其他变量的联系也被明确定义。
- (摘自 Pearl,2009)思考并列出结果变量的因果双亲的因果双亲(即因果祖父母)。
如果您认为它们与您的因果问题不直接相关,我们可以将它们保存在一个单独的、更广泛的列表中,或者保存在一个单独的、更广泛的 DAG 中,以供将来参考。
检查隐藏的假设
这些步骤将给出代表相关因果问题的第一个最小 DAG 。之后,您可能需要检查更多的东西:
- 缺失箭头:DAG 中缺失的箭头代表您的假设。您隐含地假设了没有用箭头连接的变量之间的独立性。这些假设可信吗?合理吗?它们是否被先前的研究或现有的理论所证明?
- 不可观测的干扰或误差:到目前为止,DAG 只包含可观测的量。每个数量也有一个未观察到的组成部分。例如,变量 X 有一个不可观测的分量 Ux,表示为:Ux → X
每个变量的不可观测分量是如何连接到 DAG 中的变量的?任何变量的 U 与任何其他可观察变量有联系吗?例如,如果我的 DAG 包含 X、Y 和 Z,根据我的直觉、经验或参考理论,Ux 可能会影响 X 和 Z:
X < — Ux → Z
检查偏差的直接来源
最后,通过检查 DAG 中的这些量,您可以初步推断出治疗分配机制(尽管它们应该得到一篇完全独立的文章):
- 有没有什么观察变量直接导致我的治疗变量?
- 是否有任何未观察到的变量影响我的治疗和结果?例如,如果我的处理是 T,结果是 Y,我可能会有这样的情况:
Y < — Ut → T
直觉上,这违反了可忽略性假设 - 是否有任何未观察到的变量影响我的治疗原因和结果?例如,如果我的处理是 T,结果是 Y,我处理的原因是 Z,我可能会有这样的情况:
Y < — Uz → Z
同样,这违反了排他性假设
您可以在 DAG 的不同版本和更新上重复这些步骤。最终,当您对最小版本满意时,您可以继续为您的模型使用更复杂的图形诊断工具,如 d 分离标准或后门标准。但是我会把那些有趣的事情留给下一篇因果导向的文章。
快乐的 minimal-DAG 图!
参考
本帖中使用的唯一参考文献是:
Pearl,J. (2009)。因果关系。剑桥大学出版社。
但是其他非常有用的参考文献还有:
Heckman,J. J. (2008)。计量经济学因果关系。国际统计评论, 76 (1),1–27。
Imbens,g . w .&Rubin,D. B. (2015)。统计、社会和生物医学科学中的因果推理。剑桥大学出版社。珀尔,J. (2009 年)。统计学中的因果推断:概述。统计调查, 3 ,96–146。
珀尔,j .,格里穆尔,m .&新罕布什尔州朱厄尔(2016)。统计中的因果推断:初级读本。约翰威利&的儿子们。
珀尔 j .&麦肯齐 D. (2018)。原因之书:因果的新科学。基础书籍。
利用深度学习创建虚拟角色的 Mo-Cap 面部动画
综述论文“图像动画的一阶运动模型”并探讨其在游戏动画产业中的应用。
运动捕捉(简称 Mo-Cap)是用摄像机记录人的真实运动的过程,目的是在计算机生成的场景中再现那些精确的运动。作为一个着迷于在游戏开发中使用这项技术来创建动画的人,我很高兴看到在深度学习的帮助下这项技术得到了巨大的改进。
在这篇文章中,我想分享一个由 A. Siarohin 等人最*发表的 NeurIPS 论文“ 用于图像动画的一阶运动模型 ”的快速概述。艾尔。并展示其在游戏动画行业的应用将如何“改变游戏规则”。
运动扫描技术
早在 2011 年,游戏《黑色洛杉矶》就推出了绝对令人惊叹的栩栩如生的面部动画,看起来领先于其他所有游戏。现在,将*十年过去了,我们仍然没有看到许多其他游戏在提供逼真的面部表情方面接*它的水*。
RockStar 工作室在 2011 年游戏《黑色洛杉矶》中使用的 MotionScan 技术,用于创建逼真的面部动画。[ 来源 ]
这是因为在开发这款名为 MotionScan 的游戏时使用的面部扫描技术极其昂贵,而且捕捉到的动画文件太大,这就是为什么大多数发行商在游戏中采用这项技术不切实际。
然而,由于深度学习驱动的动作捕捉的最新进展,这种情况可能很快就会改变。
图像动画的一阶运动模型
全文 PDF:https://arxiv.org/pdf/2003.00196.pdf
在这项研究工作中,作者提出了一种深度学习框架,通过跟踪驾驶视频中另一张脸的运动,从人脸的源图像创建动画,类似于 MotionScan 技术。他们提出了一种自我监督的训练方法,可以使用特定类别的未标记视频数据集来学习定义运动的重要动力学。然后,展示如何将这些动态运动与静态图像相结合,生成动态视频。
框架(模型架构)
让我们看看下图中这个深度学习框架的架构。它由运动模块和外观模块组成。驾驶视频是运动模块的输入,源图像是我们的目标对象,它是外观模块的输入。
一阶模型模型框架
运动模块
运动模块由编码器组成,该编码器学习包含相对于对象运动高度重要的稀疏关键点的潜在表示,该对象在该场景中是人脸。这些关键点在驾驶视频的不同帧之间的移动生成了一个运动场,它由一个我们希望模型学习的函数驱动。作者使用泰勒展开到来*似这个函数到一阶来创建这个运动场。根据作者,这是第一次一阶*似被用于模拟运动。此外,这些关键点的学习仿射变换被组合以产生 Dsense 运动场。密集运动场预测帧的每个单独像素的运动,而不是只关注稀疏运动场中的关键点。接下来,运动模块还会生成一个遮挡图,它会突出显示需要内画的帧像素,这些像素是由头部相对于背景的运动产生的。
外观模块
外观模块使用编码器对源图像进行编码,然后将其与运动场和遮挡图相结合,以对源图像进行动画处理。发电机模型用于此目的。在自我监督训练过程中,来自驾驶视频的静止帧被用作源图像,并且所学习的运动场被用于使该源图像动画化。视频的实际帧充当生成的运动的基础事实,因此它是自我监督的训练。在测试/推断阶段,该源图像可以被来自相同对象类别的任何其他图像替换,并且不必来自驾驶视频。
在游戏角色上运行训练好的模型
我想探索这个模型在一些虚拟设计的游戏角色脸上的效果如何。作者分享了它的代码和一个易于使用的谷歌 Colab 笔记本来测试这一点。这是他们训练的模型在游戏《侠盗猎车手》中不同角色身上测试时的样子。
使用一阶运动模型生成的面部动画。(游戏中的虚拟人物 GTA V. 左: Franklin 中: Michael 右: Trevor)
正如你所看到的,使用这个 AI 创建逼真的动画是极其容易的,我认为它将被几乎每个游戏艺术家用于创建游戏中的面部动画。此外,为了用这种技术执行 Mo-Cap,我们现在需要的只是一台相机和任何带 GPU 的普通计算机,这个人工智能将负责剩下的工作,这使得游戏动画师大规模使用这种技术变得非常便宜和可行。这就是为什么我对这个人工智能在未来游戏开发中可能带来的巨大改进感到兴奋。
有用的链接:-
感谢您的阅读。如果你喜欢这篇文章,你可以关注我在媒体、 GitHub 上的更多作品,或者订阅我的 YouTube 频道。
创建我的第一个深度学习+数据科学工作站
我的家庭设置
它很贵💰💰但是,它是一个怪物😈😈
Lambda 和其他供应商提供预建的深度学习工作站,配备新的安培 RTX 3090、3080 和 3070 GPUs
Exxact 还拥有一系列深度学习工作站和服务器,起价为3700 美元,配有几个英伟达 RTX 30 系列 GPU、英特尔酷睿 i9、3 年保修和深度学习软件堆栈。
创建我的工作站对我来说是一个梦想。
我知道这个过程,但不知何故,我从来没有去过。可能是时间或金钱。主要是钱。
但这次我不得不这么做。我只是厌倦了在 AWS 上为任何小型个人项目设置服务器,并摆弄所有的安装。或者我不得不在谷歌 Collab 笔记本上工作,这些笔记本在运行时间和网络连接上有很多限制。所以,我找了些时间,在 NVIDIA 的帮助下,创建了一个深度学习*台。
整个过程包括大量阅读和观看来自 Linus Tech Tips 的 Youtube 视频。因为这是我第一次从零开始组装电脑,所以也有点特别。
按照你的要求建造 DL 钻机需要大量的研究。我研究了各个部分,它们的性能,评论,甚至美学。
现在,我研究的大多数工作站版本都专注于游戏,所以我也想制定一个深度学习装备规范。
我会试着把我用过的所有部件以及我使用这些特殊部件的原因放在一起。
还有,如果你想看我在设置系统使用 Ubuntu 18.04 后是如何设置深度学习库的,可以查看 这个设置深度学习工作站的权威指南。
那么,为什么需要工作站呢?
我想到的第一个答案是,为什么不呢?
我在深度学习和机器学习应用程序方面做了很多工作,每次开始新项目时,制造新服务器并安装所有依赖项总是非常令人头疼。
此外,它看起来很棒,放在你的桌子上,随时可用,并且可以根据你的要求进行重大定制。
除此之外,使用 GCP 或 AWS 的财务方面,我很喜欢建造我的钻机的想法。
我的身材
我花了几个星期才完成最终版本。
我从一开始就知道,我希望拥有强大的计算能力,并且在未来几年内可以升级。目前,我的主要优先事项是获得一个系统,可以支持两个 NVIDIA RTX 泰坦卡与 NVLink。这将允许我拥有 48GB 的 GPU 内存。简直牛逼。
下面的建筑可能不是最好的,也许有更便宜的选择,但是我可以肯定的是,它是未来最不头疼的建筑。所以我顺其自然。我也联系了 Nvidia,得到了很多关于这个特别版本的建议,只有在他们同意后我才继续。
1.英特尔 i9 9920x 3.5 GHz 12 核处理器
英特尔还是 AMD?
是的,我用的是英特尔处理器,不是 AMD 处理器。我这样做的原因(虽然人们可能不同意我的观点)是因为英特尔有更兼容和相关的软件,如英特尔的 MKL,这有利于我使用的大多数 Python 库。
另一个可能是更重要的原因,至少对我来说,是 NVIDIA 的人建议如果我想要双 RTX 泰坦配置,就选择 i9。未来再一次零头痛。
那么,为什么是英特尔系列中的这一款呢?
我从十核的 9820X 和 18 核的 9980XE 开始,但后者让我的预算捉襟见肘。我发现i9–9920 x,* 的 12 核和 3.5 GHz 处理器正好符合我的预算,因为它总是更适合中端解决方案,所以我选择了它。*
现在,CPU 是决定你最终将使用的许多其他组件的组件。
例如,如果您选择 i9 9900X 系列的 CPU,您将不得不选择 X299 主板,或者如果您打算使用 AMD Threadripper CPU ,您将需要 X399 主板。所以要注意选择正确的 CPU 和主板。
2。微星 X299 SLI 加 ATX LGA2066 主板
这一个符合要求*
这是一个特别困难的选择。这里有太多的选择。我想要一个至少支持 96GB RAM 的主板(同样是按照英伟达支持 2 个泰坦 RTX GPU 的规格)。这意味着,如果我使用 16GB RAM 模块作为 16x6=96,我必须至少有六个插槽。我有 8 个,所以它可以扩展到 128 GB 内存。
我还希望能够在我的系统中安装 2 TB NVMe 固态硬盘(未来),这意味着我需要 2 个 M.2 端口,这正是该主板所具备的。否则,我将不得不去买一个昂贵的 2TB 单 NVMe 固态硬盘。
我研究了许多选项,基于 ATX 外形、4 个 PCI-E x16 插槽和主板的合理定价,我最终选择了这个 one 。
3。Noctua NH-D15 chromax。黑色 82.52 CFM CPU 冷却器
气流怪物
液体冷却现在很流行。最初,我还想使用 AIO 冷却器,即液体冷却。
但在与 NVIDIA 的几个人交谈以及在互联网论坛上搜索这两种选择的利弊后,我意识到空气冷却更适合我的需要。所以我选择了 Noctua NH-D15 ,这是市场上最好的空气冷却器之一。所以,我选择了最好的空气冷却系统,而不是普通的水冷系统。这个冷却器是无声的。稍后将详细介绍。
4.Phanteks Enthoo Pro 钢化玻璃盒
**
一个容纳所有部件的极好的大房子
接下来要考虑的是一个足够大的箱子,能够处理所有这些组件,并且能够提供所需的冷却。这是我花了大部分时间研究的地方。
我的意思是,我们将保留 2 个泰坦 RTX,9920x CPU,128 GB 内存。那里会非常热。
再加上 Noctua 空气冷却器的空间要求和添加大量风扇的能力,我只有两个选择,这是基于我糟糕的审美观和我所在国家的可用性。选项分别是— 海盗船 Air 540 ATX和Phanteks Enthoo Pro 钢化玻璃 PH-es 614 ptg _ SWT。**
这两个都是例外情况,但我使用了 Enthoo Pro,因为它是最*推出的产品,具有更大的外形尺寸(全塔式),提供了未来更可定制的构建选项。
5.双泰坦 RTX 带 3 插槽 NVLink
食谱的主要成分**
这两个 泰坦 RTX是目前为止整个建造中最重要也是最昂贵的部分。光是这些就占了 80%的成本,但是他们不牛逼吗?
我想在我的构建中有一个高性能的 GPU,NVIDIA 的好伙计们足够慷慨地给我送来两个这样的测试。
我就是喜欢它们。设计。它们在构建中的外观以及它们可以使用 3 插槽 NVLink 进行组合以有效提供 48gb GPU RAM 的事实。太棒了。如果钱是一个问题,2 个 NVIDIA GeForceRTX 2080 Ti也可以。唯一的问题是,您可能需要在 RTX2080Ti 上进行较小批量的训练,在某些情况下,您可能无法训练大型模型,因为 RTX 2080 Ti 只有 11GB RAM。还有,你将无法使用 NVLink,它结合了 TITANs 中多个 GPU 的 VRAM。
更新:对于新发布的 RTX30 系列,我会选择 RTX3090 GPU,它提供 24GB 内存,价格大约是 RTX 的一半。其他选项 RTX3080(10GB)和 RTX3070(8GB)的 RAM 较低,适合大多数 DL 用途。
6。三星 970 Evo Plus 1 TB NVME 固态硬盘
最快的?存储选项***
储物呢?当然是 NVMe 的固态硬盘,而三星 Evo Plus 是这场固态硬盘竞赛中最受欢迎的赢家。
到目前为止,我买了 1 个,但由于我的主板上有 2 个 M.2 端口,我将在未来获得 2TB 固态硬盘的总存储空间。
您还可以获得几块 2.5 英寸固态硬盘,以获得更多存储空间。
7.海盗船复仇 LPX 128GB (8x16GB) DDR4 3200 MHz
我的第一台电脑有 4 MB 内存。从没想过我会造一台 128 GB 内存的电脑。
正如 NVIDIA 团队所建议的那样,我想要最少 96GB 的内存。所以我说,管他呢,还是用 128 GB 的内存吧,不要掉价。
正如你所看到的,这些 RAM 棒不是 RGB 照明的,这是一个有意识的决定,因为 Noctua 空气冷却器没有为 RAM 插槽提供很多间隙,RGB 的高度略高。所以请记住这一点。此外,我从来没有试图去为一个 RGB 建设反正,因为我想把重点放在那些点燃了我的建设中的巨人。
8.海盗船 1200W 电源
发电站
1200 瓦的电源是一个相当大的电源,但我们需要意识到,我们的组件在全瓦数下的估计瓦数约为 965 瓦。
我有几个其他制造商的电源选项,但由于海盗船的名字,我选择了这个。我本来要带 HX1200i 的,但是没有,而且 AX1200i 在我那里比这个贵多了。但是除了这个,这两个都是很好的选择。
9.更多粉丝
静音散热器
Phanteks 机箱配备了三个风扇,但我被建议将机箱的进气和排气风扇升级为 BeQuiet BL071 PWM 风扇,因为双泰坦可以释放大量热量。我注意到我房间的温度几乎比室外温度高 2-3 度,因为我通常开着机器。
为了获得最好的气流,我买了 5 个。我把两个放在箱子的顶部,还有一个风扇,两个放在前面,一个放在后面。
10.外围设备
必需品——一杯茶和那些扬声器
这一部分是不必要的,但想把它完成。
考虑到我们拥有的所有能力,我不想在外围设备上省钱。所以我给自己买了一台 LG 27UK650 4k 显示器用于内容创作,明基 EX2780Q 1440p 144hz 游戏显示器用于一点点游戏,一个机械樱桃 MX 红色 Corsair K68 键盘和一个 Corsair M65 Pro 鼠标。
我的建造完成了。
定价💰💰💰
我将按照 PCPartPicker 网站的价格,因为我已经从不同的国家和来源获得了我的组件。您也可以在 PCPartPicker 网站查看零件列表:https://pcpartpicker.com/list/zLVjZf
这太贵了
正如你所看到的,这无论如何都是相当昂贵的(甚至在从 NVIDIA 获得 GPU 之后),但我猜这是你为某些痛苦付出的代价。
最后
最终结果证明努力是正确的
在这篇文章中,我谈到了组装深度学习装备所需的所有零件,以及我购买这些零件的原因。
你可能会尝试寻找更好的组件或不同的设计,但这个对我来说已经很好地工作了一段时间,它很快吗?
如果你想看看我在用这些组件设置系统之后是如何设置深度学习库的,你可以查看 这本权威指南,为用 Ubuntu 18.04 设置深度学习工作站
请在评论中告诉我你的想法。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 中 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系到我
创建我的第一个 Julia 可执行文件
用 C 编译 Julia!(这一点也不好玩。)
(茱莉亚标志 src =http://julialang.org)
在我过去的经历中,我抱怨了很多关于 Julia 编程语言目前存在的一些问题。Julia 是一种伟大的语言,但没有一种语言没有它的问题,而且 Julia 的缺点令人印象深刻地罕见,当然不会破坏使用该语言的体验。我抱怨过的其中一个问题是,Julia 对编译后的可执行文件的支持目前肯定不是最佳的。
虽然这并不是说在 Julia 中创建可执行文件是不可能的,但是要从 Julia 中获得一个合适的可执行文件,肯定需要做更多的工作。我在今年早些时候(或去年年底,我不记得了)接触过这个问题,记得花了很多时间努力想得到任何结果。最终,我屈服了,放下了键盘,这对我来说是一次非常沮丧的失败。从那以后,Julia 的编译器有了许多更新,我非常乐观地认为现在可能有一种方法可以获得编译后的可执行文件。据我所知,它仍然肯定不是最佳的,但现在它是可能的。
我们的应用
对于我们的应用程序,我想做一个非常基本的 GUI,它既能让我们知道我们的可执行文件是否与视觉反馈一起工作,又能测试 Julia 编译的可执行文件将如何处理系统范围的依赖性。为此,我选择了 GTK 3+。我选择 GTK 是因为
- 我熟悉 Python、Vala 和 c 语言的 GTK
- 我用 Gnome。
- 朱莉娅有一个固定的 GTK 港。
- GTK 应用程序构建起来相当简单快捷。
- GTK 在 Linux 上使用了一个系统范围的依赖项 gtklib3,这意味着测试依赖项打包非常容易。
所有这些都有很大的好处,会让实际的写作过程变得相当简单。虽然我熟悉 Gtk,以及它的类和函数,但是将库转移到 Julia 中肯定是一件有趣的事情。
用 GTK 制作图形用户界面的第一步是制作一个窗口。
(可预见)
我们可以像在 Python 或 Vala 中那样做。虽然在 Python 中,类当然不会被导出,所以你必须调用 Gtk,例如:
# Python
import Gtk
win = Gtk.GtkWindow(title, width, height)
---------
# Julia
using Gtk
win = GtkWindow(title, width, height)
---------
# Valausing Gtk
var window = new Gtk.ApplicationWindow (title, width, height);
显然,它们都有语法上的差异,但是代码真正变得 julianization 的地方是删除了 add()这样的函数,并用我们都知道和喜欢的经典 Julia 基本函数替换了这些函数。
win = GtkWindow("this program is literally just an audio icon", 1280, 720)b = GtkVolumeButton()
push!(win,b)
此外,我们可以使用通常的 showall()方法来展示我们的创作!:
showall(win)
我从来没有见过更好的用户界面/UX 设计。
收集
为了编译我们的代码,我们必须将它保存为一个. jl 文件。我打开 G-edit 并记下刚才演示的代码,但是现在通过 bash 运行该文件会产生以下结果:
没有任何东西
也许我们什么也没看到,但也许我们应该问问朱莉娅本人,她对这种情况是否有任何意见?
看来我们已经达成共识,什么都没发生。当然,我不知道实际上该怎么做,但幸运的是我们可以咨询我的好朋友和犯罪伙伴:
谷歌。
在谷歌搜索了一会儿后,我遇到了一个堆栈溢出的帖子,最初的发帖人声称他需要一个 Pkg 更新才能工作。
我一直在用朱莉娅的 Gtk 包工作。昨天一切都很好。今天,Gtk 窗口不会出现在…
stackoverflow.com](https://stackoverflow.com/questions/24086363/julia-gtk-windows-do-not-display)
考虑到这一点,我意识到无论如何是时候创建一个虚拟环境了。
现在我将把 Gtk 添加到这个新环境中:
这一次,我还决定尝试从 REPL 运行代码。
所以这肯定是可行的,但是我们如何将这个应用程序打包到它自己的可执行文件中呢?
实际编译
就用 Julia 打包和编译二进制文件而言,我们有两种主要的方法来处理依赖关系。我们的第一个选项包括将加载的包和编译的函数保存到一个文件中,我们可以让 Julia 在启动时执行这个文件。对于这个用例选项,第二个也是更可行的方法是将整个项目编译成一个可重定位的编译应用程序。这会生成一组其他文件以及一个可执行文件,该文件可以在一台可能没有安装 Julia 的计算机上运行(我们将对此进行测试。)
为了让包编译实际工作,我们首先需要重构这些文件“Julia style”,这涉及到创建一个 src 目录,以及一个 Project.toml。
现在我们需要创建一个名为 src 的新目录,并将我们的源文件移到那里。
现在,当然,我们要去获取 Julia Gtk 的 UUID,并将其添加到我们项目文件的[deps]部分(是的,我差点忘了我们有一个依赖项。)之后,我们还需要在 Julia 文件中添加一个“main”函数,最终结果如下所示:
using Gtk
function julia_main()
win = GtkWindow("this is definitely not a window title", 400, 200)
b = GtkButton("DO NOT click this button.")
push!(win,b)
showall(win)
end
下一步,我们将重新激活我们的 Pkg 环境,并向其中添加 Julia 的包编译器。
现在,我们将使用以下命令创建一个系统映像:
julia --startup-file=no --trace-compile=app_precompile.jl src/test.jl
这将在我们的工作目录中创建一个名为 app_precompile.jl 的预编译文件:
我们可能不应该直接编辑它,但是让我们看看文件内部是什么样子的。
这个 Julia 代码正在预编译我们的系统映像,以便创建一个可执行文件。需要注意的是,需要添加一些参数来编译 Windows。当然,我用的是 GNU+Linux,所以我只是设置它为 X11 编译。以下是您可以为 Windows 添加的参数:
-Wl,--export-all-symbols
最后,我们需要写一些 C 语言来为我们特定的 Julia 包构建一个小的“编译器”。所以让我们快速接触一个 C 文件:
touch compile.c
现在让我们在我们选择的文本编辑器中打开该文件,我喜欢 Nano。
nano compile.c
我们要做的第一件事是包含一些 Julia 编译器需要的标准 C 头文件,它们是 string 和 stdin。
#include <string.h>
#include <stdint.h>
接下来我们还将包括 Julia 需要的标题。对于使用 Julia 来说,有两个头是至关重要的,但是如果您使用的是 C-ported 包,您可能还想包含它的头。因为 GTK 就是这种情况,所以我把它也包括在 Julia.h 和 uv.h 中:
#include "uv.h"
#include "julia.h"
#include <gtk/gtk.h>
最终结果看起来有点像这样:
接下来,我们将从 Julia 头运行这个:
JULIA_DEFINE_FAST_TLS()
然后在我们的应用程序中声明 C 入口点的原型:
**int** **julia_main**();
接下来,我们将创建一个名为 main 的新类,它将接受由 Julia main 操作提供的两个参数。我们还将调用 uv_setup_args(),它将告诉 UV Julia 提供的参数:
**int** **main**(**int** argc, **char** *argv[]) {
uv_setup_args(argc, argv);
接下来,我们将初始化 libsupport。这是重要的一步,因为我们希望能够在可执行文件中使用动态链接。
libsupport_init();
然后我们将调整由 Julia.h 头文件提供的 Julia 选项:
jl_options.image_file = JULIAC_PROGRAM_LIBNAME; julia_init(JL_IMAGE_JULIA_HOME);
请注意,JULIAC_PROGRAM_LIBNAME 将是一个参数,我们可以在创建完这个文件后将它添加到 C 编译器中。接下来,我们将使用与 uv 相同的方式设置 Julia 参数:
jl_set_ARGS(argc, argv);
现在我们需要将 PROGRAM_FILE 参数设置为 argv[0]。当然,这也是一个参数,所以我们可以在任何 Julia 文件中重用这个编译器。
jl_set_global(jl_base_module, jl_symbol("PROGRAM_FILE"), (**jl_value_t***)jl_cstr_to_string(argv[0]));
现在我们要设定朱莉娅的基本论点。
**jl_array_t** *ARGS = (**jl_array_t***)jl_get_global(jl_base_module, jl_symbol("ARGS")); jl_array_grow_end(ARGS, argc - 1);
**for** (**int** i = 1; i < argc; i++) {
**jl_value_t** *s = (**jl_value_t***)jl_cstr_to_string(argv[i]); jl_arrayset(ARGS, s, i - 1);
}
这样我们就可以关闭这个类,调用 work 函数:
**int** ret = julia_main();
然后,我们将添加一个带有 Julia 头的退出挂钩:
jl_atexit_hook(ret);
**return** ret;
这是我最后的文件:
(所以不要太 C)
下面是我最后的编译命令:
gcc -DJULIAC_PROGRAM_LIBNAME=\"sys.so\" -o MyApp compile.c sys.so -O2 -fPIE -I'/home/kc/julia/include/julia' -L'/home/kc/julia/lib' -ljulia -Wl,-rpath,'/home/kc/julia/lib:$ORIGIN'
现在我们做到了!
我们有一个编译好的二进制文件,点击它,我们会得到:
结论
我甚至不能对你撒谎,
那是一种痛苦。
朱莉娅编译肯定还没有完全到位。弄清楚这一点肯定不好玩,而且由于缺乏关于如何做到这一点的文档,这种斗争变得更加复杂,但就我所见,它真的没有那么远。我对编译 Julia 文件的潜在未来感到兴奋!我认为 Julia 作为一种通用高级编程语言有很多用途,但由于缺乏易于访问的编译,这些用途还有待探索。当然,这样的事情为什么会如此复杂是有道理的,但是我希望在我完美的未来,我们可以有一种更类似于从不同语言编译可执行文件的体验。
使用 BeautifulSoup 创建我自己的数据集
如何使用 BeautifulSoup 库应用 Web 报废来构建自定义数据集以供将来分析。
由 Pixabay 授权
这个项目的目标是使用网络抓取创建我自己的数据集,从 RentHop 网站提取波士顿公寓租赁数据,并将其保存到 CSV 文件中。
我将遵循 4 个简单的步骤:
- 访问网页
- 查找特定信息
- 检索数据
- 保存数据
加载库
import pandas as pd
import requests
from bs4 import BeautifulSoup
验证请求和 BeautifulSoup 库是否已经安装。如果没有,则需要接下来的步骤来运行项目代码。
pip install beautifulsoup4
pip install requests
访问网页
我将使用库请求来访问 RentHop 站点。
r = requests.get('https://www.renthop.com/boston-ma/apartments-for-rent')
r.content
我将使用 BeautifulSoup 来进行 HTML 解析。我将创建一个 BeautifulSoup 对象,并应用一个过滤器来获取代码中的< div >标签。
# Creating an instance of BeautifulSoup
soup = BeautifulSoup(r.content, "html5lib")listing_divs = soup.select('div[class*=search-info]')
print(listing_divs)
查找特定信息
之所以把所有的< div > 标签都放到我的 list listing_divs 中,是因为包含了 20 套公寓的房源数据。现在我应该找出每个公寓的单独数据点。
这些是我想要锁定的信息:
- 列表的 URL
- 公寓的地址
- 附*
- 卧室数量
- 浴室数量
# Retrieving data from one recordurl = listing_divs[0].select('a[id*=title]')[0]['href']
address = listing_divs[0].select('a[id*=title]')[0].string
neighborhood = listing_divs[0].select('div[id*=hood]')[0].string.replace('\n','')print(url)
print(address)
print(neighborhood)
检索数据
要获得超过 20 条记录的数据,需要在几个页面上迭代。使用网站上的搜索选项,我得到了下面的 URL,它将有助于通过最后一个参数页面在不同的页面导航。
https://www.renthop.com/search/boston-ma?min _ price = 0 & max _ price = 50000 & q = & sort = hop score & search = 0 &page = 0
让我们创建一个简单的代码来生成第 1 页到第 4 页的 URL:
url_prefix = "https://www.renthop.com/search/boston-ma?min_price=0&max_price=50000&q=&sort=hopscore&search=0&page="
page_number = 0for url in *range*(4):
target_page = url_prefix + *str*(page_number)
print(target_page + '\n')
page_number += 1
# Creating a function to retrieve data from a list of <div>s*def* retrieve_data(listing_divs):
listing_list = []
for index in *range*(*len*(listing_divs)):
each_listing = []
current_listing = listing_divs[index] url = current_listing.select('a[id*=title]')[0]['href']
address = current_listing.select('a[id*=title]')[0].string
neighborhood = current_listing.select('div[id*=hood]')[0].string.replace('\n','') each_listing.append(url)
each_listing.append(address)
each_listing.append(neighborhood) listing_specs = current_listing.select('table[id*=info] tr') for spec in listing_specs:
try:
each_listing.extend(spec.text.strip().replace(' ','_').split())
except:
each_listing.extend(np.nan)
listing_list.append(each_listing) return listing_list# Looping and getting data from 350 pages (part of the result of searching)url_prefix = "https://www.renthop.com/search/boston-ma?min_price=0&max_price=50000&q=&sort=hopscore&search=0&page="
page_number = 1all_pages_parsed = []
pages = 350for url in *range*(pages):
target_page = url_prefix + *str*(page_number)
page_number += 1 r = requests.get(target_page)
# Getting a BeautifulSoup instance to be able to retrieve data
soup = BeautifulSoup(r.content, "html5lib") listing_divs = soup.select('div[class*=search-info]')
one_page_parsed = retrieve_data(listing_divs)
all_pages_parsed.extend(one_page_parsed)
将数据保存在 CSV 文件中
df = pd.DataFrame(all_pages_parsed, columns=['url','address','neighborhood','price','rooms','baths','none'])
df.head()
现在,最后一步!
# Writing a comma-separated values (CSV) filedf.to_csv('apartments_leasing.csv', index=False)
结论:
- 应用网络抓取允许我们为将来的分析创建我们自己的数据集。这只是一个例子,但是互联网上有很多网页
- 为了方便提取和定位我们需要的数据,了解要废弃的网页代码是至关重要的
- BeautifulSoup 是一个有用且强大的网页抓取工具,它很容易学习,并且有非常好的文档,你可以在这个链接上查看
- BeautifulSoup 需要一个外部库向网站发出请求,在这种情况下,我使用了 Requests ,这种依赖对于这个特定的项目来说并不代表任何缺点
- 我邀请你在我的 GitHub 库上查看这个项目的完整代码
Python 中从头开始的神经网络
初学者理解和实现神经网络的实用指南。
神经网络已经接管了世界,正在你能想到的任何地方被使用。有很多帖子描述了神经网络是如何工作的,以及如何从零开始实现一个神经网络,但我觉得大多数帖子更面向数学,更复杂,而不太重视实现。我今天的重点将是从头开始实现一个网络,并在这个过程中,了解其内部工作原理。
这篇文章的目标是引导你将神经网络中的数学方程转换成 python 代码。如果你对方程式和数学细节感兴趣,我已经创建了一个 3 部分的系列,详细描述了一切:
神经网络的学习过程
让我们快速回顾一下神经网络是如何从训练样本中“学习”并用于预测的。
神经网络主要由三种类型的层组成——输入层、隐藏层和输出层。
一层中的每个神经元接受输入,乘以一些权重,加上偏置,应用激活函数,并将其传递给下一层。
学习过程
学习过程可以总结如下:
- 前馈:输入被送入网络。它们穿过隐藏层并到达输出层以产生输出。这个过程叫做前馈。它在训练时使用,也用于在网络完成训练后进行预测。
- 计算成本:前馈后产生的输出与期望输出进行比较,我们计算它与原始值的差异。成本基本上告诉我们我们的产出与原始价值有多大的不同。换句话说,它是对我们网络中错误的一种度量。理想情况下,我们希望成本为 0,或者非常接*于 0 的值。
- 反向传播:在这一步,我们回到我们的网络,我们更新每一层的权重和偏差的值。成本值告诉我们要更新多少权重和偏差(这里我们使用梯度下降)。这次更新基本上是调整权重和偏差,以使我们网络的输出变得更接*期望的输出,最终,成本下降到 0。
- 对于固定数量的循环或迭代或时期,重复上述步骤。我们通过查看成本来决定纪元的数量——我们寻找最低的成本值。
当我们到达一个阶段,我们的成本接*于 0,我们的网络正在进行准确的预测,我们可以说我们的网络已经“学习”了。简而言之,学习就是更新权重和偏差的过程,直到网络产生一些期望的输出。
在接下来的几节中,我们将使用 Python 实现上述步骤。
创建图层类并初始化它
首先,我们创建一个层类来表示网络中的每一层。这只是让事情变得更整洁,更容易封装与层相关的数据和功能。
我们引入 numpy——使我们的数学计算更容易。我们还定义了将在网络中使用的激活函数和损失函数(用于计算成本)。
在 layer 类中,我们定义了字典 activationFunctions,它保存了我们所有的激活函数及其派生函数。这只是为了使事情更整洁,避免大量的 if 语句。我们还定义了学习率。如果你愿意,你可以尝试不同的学习速率值。
在 init 函数中,我们将三个参数作为输入:
- 输入:该层的输入数量
- 神经元:这一层中神经元的数量
- 激活:要使用的激活函数
现在我们可以初始化我们的权重和偏差。需要注意的一点是,我们将使用矩阵乘法来执行所有的计算。因此我们所有的变量都是矩阵。
我们的权重是一个矩阵,其行数等于该层中神经元的数量,列数等于该层的输入数。我们使用 np.random.randn 函数创建一个带有随机值的形状(神经元,输入)矩阵。用随机值初始化权重矩阵对于我们的网络正确学习是很重要的。
我们的偏差是一个列向量,包含网络中每个神经元的偏差值。使用 np.zeros 函数将其初始化为 0。
形状参考
这里有一个快速形状参考,以免以后与形状混淆。形状是我们将使用的矩阵的维度。这一点非常重要,因为大多数错误都是由于形状不匹配造成的,这将有助于您进行调试。
神经元数=给定层中的神经元数
输入数=该层的输入数
样本数(或 m) =训练样本数
- 网络的输入,X _ train . shape =(X 的维数,样本)
- W.shape =(神经元,输入)
- b.shape =(神经元,1)
- Z.shape =(神经元,样本)
- 形状= Z 形
- dZ.shape = Z.shape
- 宽形状=宽形状
- 形状=形状
- 形状=形状
前馈功能
前馈功能通过网络的每一层传播输入,直到它到达输出层并产生一些输出。正如我上面提到的,每个神经元接受输入,乘以权重,加上一个偏差,并应用一个激活函数来生成输出。
前馈方程可以总结如下:
这里需要注意的几件事是:
- 𝐴_prev 项是前一层的输出。对于我们的输入层,这将等于我们的输入值 X.
- W 和 A_prev 之间的运算是点运算。因为两者都是矩阵,所以它们的形状匹配很重要(W 中的列数应该等于 A_prev 中的行数)。这是矩阵乘法的一个基本性质。
- 这一层的输出是 A_prev。对于输出图层,这将等于预测输出 Y_bar。
在代码中,我们在 layer 类中编写了这个前馈函数,它只计算当前层的输出。稍后,我们将使用一个循环来迭代层对象,并按顺序生成每个输出。
我们将 A_prev、Z 和 A 的值保存在我们的类中,以便以后在反向传播时使用。
梯度下降
梯度下降使我们的网络学习。我不会在这篇文章中详细讨论梯度下降,因为我已经就此发表了一篇详细的文章。
基本上,梯度下降计算我们的权重和偏差应该更新多少,以使我们的成本达到 0。这是用偏导数来完成的。
梯度下降是基于这样一个事实,在一个函数的最小值,它的偏导数将等于零。这里我们感兴趣的是最小化成本函数。
成本取决于层中的权重和偏差值。所以对于每一层,我们找到该层的成本相对于权重和偏差的导数。这个导数值是我们对权重和偏差的当前值的更新。用于进行这种更新的等式被称为学习等式。
学习方程式
这里的α是我们之前定义的学习率。
反向传播算法
反向传播算法基本上是在我们的网络中往回走,计算每一层中偏导数值的值,并应用学习方程。
在我们得到产出后,我们将计算成本。
成本等式
这里 m 是我们训练集中的样本数。l 是计算单个样本的实际值和预测值之间的误差的任何损失函数。这基本上给出了所有样本的*均误差。
接下来,让我们看看求偏导数的方程。我们只对成本相对于 W 和 b 的偏导数感兴趣。但是为了有效地得到这些值,我们需要计算 C 对 A 和 Z 的偏导数。
反向传播方程
这些等式中需要注意的事项:
- dC/dA 的值是给定的,即在代码中,从上一层返回。在输出层的情况下,这是直接计算,因为成本是 A 的函数,并且输出 Ybar = A。
- d_act 是在该特定层中使用的激活函数的导数。
- A_prev 与我们在前馈部分讨论的 A_prev 相同。我们找到它的转置来匹配 dC/dZ 的形状。还要记住一个变量的导数,比如 Z 和 Z 有相同的形状。
- 最后我们计算 dC/dA_prev 返回到下一层。
- 在代码中,我们忽略 dC 项,只使用分母来表示变量,因为所有变量都有分子 dC。例如,在代码中,变量 dA 实际上表示值 dC/dA。
请注意,在代码中,我们使用了上面讨论的精确等式,但做了一些修改:
- 为了找到 dZ 的值,我们使用了 np.multiply 进行逐元素乘法。这是因为 dZ、d_act(Z)和 dA 的维数是相同的。因此,如果我们使用点积,就会出现形状不匹配,数学上就不正确了。
- dW 是 dZ 和 A_prev 的转置之间的点积,使用 np.dot .但是我们也用 dZ.shape[1]除它,它等于 dZ 中的列数。记住 dZ 中的列数等于样本数(行数等于神经元数)。
在寻找点积时,我们将元素相乘并相加。自从 A_prev。t 有 m 行,在求点积时,W 的每个值是我们数据中所有样本的和。为了标准化,除以样本数,dW 的每个值都是所有样本的*均值。 - db 和 dZ 的尺寸不同。因此,为了匹配维数,我们找到 dZ 的所有列的总和,即所有样本的总和,除以样本数,以进行归一化,就像我们对 dW 所做的那样。
- 最后,我们使用学习方程来更新权重和偏差,并返回 dA_prev 的值,该值作为 dA 传递给下一层。
训练网络
现在我们可以把所有东西放在一起实现网络。我们将建立一个简单的 2 层网络来学习异或函数。
[[0.00435616 0.97579848 0.97488253 0.03362983]]
需要注意的事项:
- m 是样本的数量。epochs 是我们将运行的迭代次数。
- 层列表包含层类的对象。第一层包含 2 个输入和 3 个神经元。这两个输入是我们正在执行 XOR 运算的两个二进制值。第二层由 3 个输入组成,因为前一层具有来自 3 个神经元的 3 个输出。它还包括一个单一的输出,异或的答案。
- 我们通过遍历每一层并将前一层的值作为输入传递给下一层来执行前馈。
- 计算成本是可选的,这里我们这样做只是为了绘制图表。
- 对于反向传播,我们在 For 循环中使用 reversed()函数向后遍历各层。dA 的值被计算并传递到下一层。
- 最后,在所有时期的循环运行之后,我们的网络应该被训练,也就是说,所有的权重和偏差应该被调整。现在,我们可以使用训练时使用的前馈逻辑进行预测。
- 可以看到输出看起来不错。如果您愿意,可以将这些值四舍五入为 0 和 1。
最后,让我们看看我们的损失是如何随着时间的推移而减少的。
这个网络显然不能用来解决现实世界的问题,但我认为它给了我们一个关于神经网络如何准确工作的好主意。从头开始实现一些东西是深入理解它的一个很好的练习。
你可以在这个 Google Colab 笔记本里找到所有的代码。
我还在 YouTube 上做了一个 3 部分系列详细描述了每个方程是如何推导出来的。
参考资料:
https://www . coursera . org/learn/neural-networks-deep-learning/
https://towards data science . com/math-neural-network-from-scratch-in-python-d6da 9 f 29 ce 65
https://towards data science . com/how-to-build-your-own-neural-network-from-scratch-in-python-68998 a08 E4 f 6
https://towards data science。
联系我! 邮箱:adarsh1021@gmail.com
推特:@adarsh_menon_
用强化学习创造下一代视频游戏人工智能
探索强化学习如何被用来颠覆创建视频游戏人工智能的传统方法
在维基共享空间。检索到 2020 年 10 月 16 日wiki.unrealengine.com
强化学习将成为创造智能视频游戏人工智能的新黄金标准。强化学习(RL)相对于传统游戏人工智能方法的主要优势在于,强化学习不是使用复杂的行为树手工制作人工智能的逻辑,而是简单地奖励他们希望人工智能表现的行为,并且代理通过自身学习来执行必要的动作序列以实现期望的行为。本质上,这就是人们如何用食物奖励来教狗表演戏法。
游戏人工智能的 RL 方法可用于训练各种战略行为,包括路径寻找、NPC 攻击和防御,以及人类在玩视频游戏时能够表现的几乎每一种行为。最先进的实现包括那些用于在国际象棋、围棋和多人战略视频游戏中击败一流人类玩家的实现。对于 RL 算法在理论上可以发现什么样的策略行为几乎没有限制,然而在实践中,计算费用和环境复杂性限制了人们想要使用 RL 实现的行为类型。因此,在开始之前,理解强化学习的基础以及它对你想要训练的行为的适用性是很重要的。
强化学习是基于一个代理在他们环境中发生的事情之间建立联系的能力。然而,与经典条件反射等其他形式的学习不同,强化学习更进了一步,它不仅解释了关联是如何形成的,还考察了人们如何根据这些关联的质量和强度来修改行为,以实现某种期望的奖励(或避免惩罚)。换句话说,它涉及一个随着时间优化的战略目标。
以下是在强化学习算法中发挥作用的元素的目录,无论是在人类还是视频游戏代理中。
首先,必须有一个环境——那是学习展开的环境。我们可以将其进一步分解为所谓的“全局环境状态”,它包含关于特定设置和代理的个人环境状态的所有可能的已知信息,这是代理可以访问的全局环境的子集。扑克游戏的环境有助于说明这一点。
扑克游戏的全局环境包括所有的牌,既包括牌面朝上放在桌上供玩家看到的牌,也包括牌面朝下对玩家隐藏的牌。每张卡的每个位置都包含在全局环境中。同时,代理人的环境的特征是通过他们的感官向他们展示的牌,即已经出现在桌子上的牌和他们手中的牌。这是全局环境状态的一个较小的子集。我们每个人只看到地球和宇宙以外的一小部分全球环境状况。在关于强化学习的文献中,这样的环境属于所谓的部分可观测马尔可夫决策过程。“部分可观察”是因为代理可以访问包含在全局环境中的一些但不是全部信息,而“马尔可夫”是因为它满足包含行动者成功学习所需目标行为的所有必要信息的属性。如果我让你和一个住在陶星球上的外星人打扑克,而不看你的牌,也不知道你是赢了还是输了,这显然是一个没有希望的任务,而且完全是“无价值的”。
经常在强化学习中使用的全局环境的另一个子集是所谓的马尔可夫环境状态,它包含关于环境的所有相关信息,这些信息对于针对某个特定目标做出关于未来的最佳决策是必要的。实质上,马尔可夫环境状态总结了环境的所有先前状态,使得不再需要进一步的信息来从该点向前优化决策。
如果你正试图做出如何避开迎面而来的棒球的决定,但无法以任何方式看到或检测到棒球,马尔可夫环境状态将是不完整的。然而,如果你用眼角瞥见了棒球,并能从那个位置计算出它的轨迹,你就不需要知道更多关于球被扔出之前发生了什么来成功躲避它。因此,它将满足马尔可夫状态的定义。在跳棋游戏中,如果你进入游戏的中点,看一眼棋盘,你不需要知道棋子在那个时候是如何到达特定位置的,就可以制定出一个最佳的前进策略。仅仅看到董事会目前的状态就足够了。马尔可夫环境经常出现在强化学习的更正式的数学处理中,但是因为我们在这里关注的是主题的直觉而不是它的数学,所以我们不会详细讨论马尔可夫环境。
在我们的强化传奇中,下一个演员是代理人自己。这是能够通过强化学习的“大脑”。在计算机科学中,这将是包含强化学习算法的机器人或合成角色。在生物学中,人类是强化学习代理的例子。我们必须有一些方法,让代理人在其环境中采取行动,以影响其获得的回报。实验室烧杯中的大脑无法进行强化学习,因为它无法以任何方式影响其环境。因此,必须有某种方法来与环境谈判,使我们的代理人的行动影响环境的状态。
除了媒介本身,在我们采取行动注意到它们的影响之后,我们还必须有一种方法来观察我们的环境。如果你将手臂连接到我们假设的烧杯中的大脑,但手臂没有任何感觉,那么这些手臂可能会假设能够做很多好事,特别是如果它可以构建身体的其余部分供大脑骑行。然而,如果没有任何方法来观察用这些武器采取的行动并衡量其效果,这些武器将仍然是无用的。因此,对环境进行观察的能力是强化学习的另一个关键组成部分。要记住的重要一点是,代理人的观察空间必须包括它需要知道的一切,以创建一个策略来实现它所寻求的回报。 例如,一个必须以特定顺序采取若干行动以获得奖励的人工智能代理必须以某种变量数组的形式访问这些过去行动的记忆。一个只知道自己当前行为的主体,将无法围绕一个需要记住一系列行为的目标进行优化。以同样的方式,在特定环境情况下因采取行动而获得奖励的代理必须有权观察这些相关环境对象中的每一个。 一个很好的经验法则是,无论游戏环境中的什么变量都是代理人奖励功能的一部分,接下来讨论的,也必须是代理人的观察空间的一部分,以便它能学会接收奖励。
这就把我们带到了奖励的话题上。如果没有奖励,或者相反的惩罚,就没有动力去学习一种行为。在任何形式的学习之前,必须有一个大棒或胡萝卜,一些东西来激励行为的改变。如果事情没有任何积极或消极的联系,就没有什么可以推动行为的改变,没有什么可以追逐和避免。这些都包含在代理的奖励函数中,并且是强化学习的基础。如果你在视频游戏中训练 NPC,你的奖励可以是在游戏环境中以特定方式组合的变量的任意组合。我们可以总结这些组件的整个模式,以及它们如何在一个简单的模型中相互关联,如图 1 所示。
作者图片
图 1——强化学习组件和交互的流程图:学习者采取行动,观察环境,接受奖励与否,然后相应地更新策略。这一过程重复进行,随着时间的推移,通过连续的动作逐渐改进代理的策略。
既然我们已经将强化学习的所有组成部分都放在了一起,那么让我们来看看它们是如何相互作用来创造一种战略行为的。总而言之,我们有一个可以采取行动的代理,根据环境的状态和它过去的行动,代理要么得到奖励,要么没有。但是,为了让强化学习的魔力发生,当代理人被奖励时,它必须将这种奖励传播回导致它获得奖励的行为和环境状态。这可以通过多种方式来实现,在状态行动对之间反向传播奖励的公式可能会有点麻烦。为了使用 RL 来创建视频游戏 AI,人们可以理解,最初代理必须采取随机行动,一旦它“偶然”通过机会获得奖励,该奖励可以与将它带到奖励处的行动和观察相关联。当这个过程重复很多次时,代理人会逐渐发现一个模型,这个模型显示哪些行为会带来回报,哪些不会。因此,随机试错训练始终是强化学习的一个关键组成部分,也是代理人学习获得奖励的方法。
人们在编程视频游戏 AI 时会遇到的大多数形式的强化学习将如上所述的传统 RL 与另一类称为神经网络的机器学习相结合。这就是深度强化学习中“深度”的来源,因为深度神经网络与 RL 模型一起烘焙,使其更具可扩展性和鲁棒性。
为视频游戏 AI 实现强化学习的早期问题之一是所谓的组合爆炸。如果一个智能体需要观察它的环境并与之互动来学习获得奖励,那么我们需要多大的表格来跟踪它的环境中可能影响其奖励的所有对象?例如,一个环境中的四个可移动对象可以以至少 24 种不同的方式组合,假设它们能够以不同的顺序排列。8 个物体可以有 40,320 种组合方式!除此之外,我们进入了从人类的角度来看几乎毫无意义的数字——12 个物体可以以 4.79 亿种独特的方式组合。即使对计算机来说,那也是一张相当大的桌子。但是人类经常在涉及这种组合爆炸的任务中取得成功。例如,在玩 Atari 视频游戏的任何给定时间,人类都在从总数为 33,600 个像素的物体中抽象出 4 或 5 个我们正在跟踪的不同物体。
那么,我们如何从所有这些像素组合中提取出少量有意义的特征呢?对人类来说,进化为我们做了所有艰苦的工作。虽然根据计算机的处理能力,可以想象计算机可以比人处理更多的功能,但组合爆炸甚至会加重现代超级计算机的负担。对强化学习来说幸运的是,在一个被称为神经网络的人工智能领域发生的*行发展给出了答案。
深度神经网络最常与称为“监督学习”的人工智能领域相关联,这需要有人为算法提供标记的训练数据,以从中学习模式。深度神经网络的惊人之处在于,它们可以像猫的照片一样获取嘈杂、非线性、非均匀的数据,并将其抽象为对其进行分类至关重要的几个特征。这就是垃圾邮件分类器如何在你的电子邮件收件箱中工作,以及网飞如何根据你喜欢或不喜欢的电影为你创建推荐。这种分类器在软件中越来越常见,并由于深度神经网络而受到了鼓舞。神经网络比早期的统计方法(如逻辑回归和贝叶斯分类器)更强大,因为它们擅长发现隐藏在多层复杂性下的模式。他们就像分类器的大力士,在最大最复杂的训练数据中寻找模式。这种分类器确实是强大的工具,但与强化学习不同,它们需要某种标记数据来进行训练,并且创建这些训练集的过程通常是一个费力和艰苦的过程。
正是使用深度神经网络从雅达利屏幕呈现的大量组合中抽象出少数关键特征的想法,证明了强化学习的革命性。深度神经网络有能力获取非常嘈杂的大型数据集,并检测其中的模式。Atari 视频游戏的屏幕可以被认为是这样一个大而嘈杂的数据集。通过使用 Atari 的屏幕作为 RL 算法与神经网络相结合的观察空间,他们可以将所有像素组合的复杂性降低到与玩家可能做出的不同移动相关的数量,通常只有 4 或 5 (Volodymyr Mnih,2015)。他们只是幸运地认为这是可能的吗?一点也不,相反,因为这些游戏是为人类设计的,而人类只有有限的能力来跟踪他们视野中的多种特征,所以这些游戏在设计时就考虑到了这些限制。为外星人设计的 Atari 视频游戏能够从 10,000 种不同的重要特征组合中学习,这完全是另一回事。然而,在大多数游戏功能只是装饰,对输赢没有任何意义的情况下,深度神经网络可以将复杂性降低到强化学习算法可以管理的程度。
雅达利的情况同样适用于围棋这样的棋类游戏。由于游戏固有的复杂性,这种古老的中国娱乐方式被认为是计算机无法掌握的。正如围棋专家喜欢提醒我们的那样,在一场围棋比赛中,可能的棋盘组合比宇宙有史以来存在的夸克数量还要多。但就像在雅达利电子游戏中一样,围棋游戏中的许多棋盘位置与游戏的任何给定回合都不相关。它们就像屏幕远处角落里的像素,在它表明敌人正向你走来之前并不重要。“深度强化学习”,即深度神经网络和强化学习的结合,被证明在掌握围棋方面与在 Attari 视频游戏中一样有效。2017 年,由 DeepMind 开发的围棋深度强化学习算法 AlphaZero 击败了几位世界领先的人类围棋选手以及最佳围棋人工智能。
当想到像围棋这样的游戏时,一个关键的谬误是假设复杂的游戏需要复杂类型的学习。在分形几何中,看似无限复杂的令人困惑的图案可以从简单的公式中推导出来。进化产生了无数的生命形式,它被一个同样简单的学习规则所指导——错误。事实上,同样的学习方程式可以让你掌握井字游戏,也可以让你掌握像围棋这样的游戏。在这两个游戏中,强化学习可以发现与获胜相关的关键关联。这并不是说没有更复杂的方法来教电脑掌握游戏。1997 年在国际象棋比赛中击败加里·卡斯帕罗夫的 IBM 超级计算机 DeepBlue 是一个庞大的程序,其中内置了数千个由国际象棋专家和程序员编写的场景。但是,这样复杂的程序,最终远不如强化学习这样的简单算法健壮和强大。首先,他们编织了编码他们的人类的经验偏见。当雅达利深度强化学习算法在 Deepmind 开发出来时,它发现了一种在乒乓游戏中增加分数的方法,这种方法使用了一种人类玩家以前不知道的技巧。如果它完全是根据人类的经验编写的,它很可能永远不会做出这种“外星人”的动作。强化学习的优势在于,在与自己对抗的过程中,它可以尝试数百万种游戏历史上从未有人想过要尝试的动作。这也会使校准变得困难。如果一个人的目标是创造一个具有人类专业水*的视频游戏人工智能,这可能涉及到在代理达到超人类水*之前停止训练。
许多所谓的“专家”在研究国际象棋强化学习算法 AlphaZero 时,看到了更高级版本的 DeepBlue,因此未能意识到他们正在研究一种完全不同的人工智能,一种具有完全不同含义的人工智能。因为强化学习模仿了人类学习的方式之一,所以可以用于掌握围棋的相同算法可以用于掌握煎蛋卷或叠衣服。当你第一次开始学习叠衣服时,你会犯错误,袖子不整齐,折痕不精确。通过重复,或者用计算机科学的话说,迭代,你慢慢地学会了让你达到目标状态,完美折叠衬衫所必需的正确动作。以这种方式,许多人类活动可以被“游戏化”并变成强化学习问题。这既是强化学习的希望,也是它的危险。国际象棋超级计算机 DeepBlue 只能在国际象棋上取得成功,而强化学习算法可以很容易地适应任何可以游戏化的任务。
在下一篇文章中,我们将演示如何使用现代游戏开发软件创建你自己的定制 RL 环境。
使用 Python 创建 PDF 文件
如何使用 PyFPDF 和 Python 创建 pdf 文件
威廉·艾文在 Unsplash 上的照片
你可能不明白为什么我们应该使用 PDF 格式作为报告工具,而有更先进的方法,如动态仪表板。你的态度是可以理解的,但这里有一些想法可能会改变你的想法:
- PDF 文件格式是全局的。 它是当今最常用的文件格式之一,广泛应用于各个领域。
- 它是便携式的。 是的!PDF 代表可移植文档格式。你可以移动你的文件而不必担心任何限制。
- 它是一个*台无关的工具。 它独立于硬件和操作系统。您可以在 Windows 中创建 pdf 文件,并在 Macintosh 或 Unix 中查看它们。
- 可以离线使用。你甚至不需要网络连接。
- 安逸之辈。 创建 PDF 文件有许多不同的方法。我们的目的是学习如何用 Python 来做这件事。
使用 PyFPDF
PyFPDF 是 Python 下的一个小巧紧凑的 PDF 文档生成库。不可否认,用 Python 创建 pdf 有很多选择,但是我更喜欢使用 PyFPDF,因为它很简单。
让我们从导入“【FPDF】包开始。如果你还没有,请检查 这个链接来安装它。
*python -m pip install fpdf # installationfrom fpdf import FPDF # fpdf class*
我们从“fpdf”包中导入 FPDF 类。FPDF是一个 PHP 类,允许用纯 PHP 生成 PDF 文件。**
最初,我们创建一个类来使用 FPDF 库。目前,我们使用 pass 语句,这是一个空操作。
***class PDF(FPDF):
pass # nothing happens when it is executed.***
创建一个类之后,是时候创建我们的第一个 pdf 页面了。首先,我们需要创建一个 PDF 类的对象。
***pdf = PDF()#pdf object***
FPDF 构造函数有几个影响结果的参数。其中第一个是 页面方向 。可以使用不同的方向,如“ 【风景】【人像】*** 默认的方向是纵向。如果想改成横向,用“L”作为方向值。(不区分大小写。)***
**pdf=PDF(orientation='L') # landscape**
其次,如果您喜欢“厘米”、“点”或“英寸”的单位,您可以更改度量单位默认值为“毫米:毫米”
**pdf=PDF(unit='mm') #unit of measurement**
最后,最后一个参数是页面格式。正如你注意到的,默认值是“”A4”。其他值有"【A3】【A5】信、合法。"
**pdf=PDF(format='A4') #page format. A4 is the default value of the format, you don't have to specify it.# full syntax
PDF(orientation={'P'(def.) or 'L'}, measure{'mm'(def.),'cm','pt','in'}, format{'A4'(def.),'A3','A5','Letter','Legal')#defaultpdf = PDF(orientation='P', unit='mm', format='A4')**
要了解更多信息,您可以查看原始文档这里。
因为我们有了自己的类,所以我们可以添加一个页面并保存输出。
**pdf.add_page()
pdf.output('test.pdf','F')**
add.page 功能向文档中添加新的页面。我上面提到的 PDF 参数也适用于这个特性。顺便说一句,如果你不添加一个页面,类方法就不起作用。
这是我们的第一个空白 PDF 页面。
第一个 PDF 页面
让我给你一个提示。如果你想检查你的 pdf 文件,现在不要使用 PDF 阅读器。因为当你试图改变 python 代码时,它会给出如下错误:
我们有这个问题的一个原因是你的 PDF 文件正在被另一个程序使用。要补救这种情况,只需使用 chrome 或 firefox 作为 PDF 浏览器。
坐标空间和画线
PyFPDF 是建立在坐标空间(x,y)上的,因此,我们需要了解必要的数学技巧,比如加、减:)
我之前说过,orientation 的默认值是' A4 '。所以我们需要知道 A4 的尺寸(w:210 mm,h:297 mm)。让我们将这些维度分配给变量以备后用。
**pdf_w=210
pdf_h=297**
我们准备做一些不同的事情。让我们在这一页的中间画一条线。但是首先,我们需要在我们的类中创建一个方法。
**class PDF(FPDF):
def lines(self):
self.set_line_width(0.0)
self.line(0,pdf_h/2,210,pdf_h/2)**
lines 方法有两行代码。在第一行代码中,我们将线条宽度设置为 0.0 毫米。在另一行代码中,我们通过“line”函数绘制了一条简单的线条。这个函数有四个参数。
**line(x1,y1,x2,y2)**
x1
和y1
是原点。您可能已经猜到,x2
和y2
是我们需要指定的端点。在我们的例子中,x1
等于零,这意味着我们要从纸的最左边开始。
很简单,对吧?让我们用这些线画一个长方形。这次我们需要四种不同的线函数。
**class PDF(FPDF):
def lines(self):
self.set_line_width(0.0)
self.line(5.0,5.0,205.0,5.0) # top one
self.line(5.0,292.0,205.0,292.0) # bottom one
self.line(5.0,5.0,5.0,292.0) # left one
self.line(205.0,5.0,205.0,292.0) # right one**
尽管 line 函数可用于绘制矩形,但还有另一个函数可用于绘制矩形。
**class PDF(FPDF):
def lines(self):
self.rect(5.0, 5.0, 200.0,287.0)**
“【rect】函数有四个不同的变量。但这和 line 函数不一样。
**rect( x: float, y:float, w:float, h:float, style='')**
x 是左上角的横坐标,y 是左上角的纵坐标,' w' 是矩形的宽度,' h' 是矩形的高度。如果要填充矩形,样式参数是必不可少的。 D '是默认的样式值。 F 代表填充, FD 或 DF 代表填充和绘制。
**class PDF(FPDF):
def lines(self):
self.rect(5.0, 5.0, 200.0,287.0)
self.rect(8.0, 8.0, 194.0,282.0)**
好了,现在你已经学会了如何画一条直线和一个矩形。让我们用 RGB 值填充最外面的矩形。(不要忘记' FD 或' DF '参数的填写。)
**class PDF(FPDF):
def lines(self):
self.set_fill_color(32.0, 47.0, 250.0) # color for outer rectangle
self.rect(5.0, 5.0, 200.0,287.0,**'DF'**)
self.set_fill_color(255, 255, 255) # color for inner rectangle
self.rect(8.0, 8.0, 194.0,282.0,**'FD'**)**
现在我们已经创建了模板。
添加图像
这一次我们将学习如何添加图像到我们的 PDF 页面。让我们从在类中创建另一个函数开始。
**def imagex(self):
self.set_xy(6.0,6.0)
self.image(sctplt, link='', type='', w=1586/80, h=1920/80)
self.set_xy(183.0,6.0)
self.image(sctplt2, link='', type='', w=1586/80, h=1920/80)**
有两种不同的代码行。首先是 set_xy 函数,定义了当前位置的横坐标和纵坐标。这些是图像的起点。另一个是在页面上放置图像的图像函数。如果您对参数有疑问,请查看本 文档 。
狮子图片由openclipbart-Vectors来自 Pixabay
狮子图片来自 Pixabay
我们也可以添加一个情节。
**import plotly.express as px
import plotly
import os
df = px.data.iris()
pltx= px.scatter(df, x="sepal_width", y="sepal_length", color="species",
size='petal_length', hover_data=['petal_width'])plotly.io.write_image(pltx,file='pltx.png',format='png',width=700, height=450)
pltx=(os.getcwd()+'/'+"pltx.png")### define a methoddef charts(self):
self.set_xy(40.0,25.0)
self.image(plt, link='', type='', w=700/5, h=450/5)**
使用文本
添加图片后,让我们创建一个标题。顺便说一下,不要忘记用另一个 set_xy 函数重置索引。定义位置时,它保持不变,直到用另一个 set_xy 函数改变它。
**def titles(self):
self.set_xy(0.0,0.0)
self.set_font('Arial', 'B', 16)
self.set_text_color(220, 50, 50)
self.cell(w=210.0, h=40.0, align='C', txt="LORD OF THE PDFS", border=0)**
我们需要在创建文本之前使用 set_font 来定义字体。这次我们只有三个参数;家庭,“风格”和“大小”默认情况下,标准字体使用 Latin-1 编码。所以你可能会遇到编码错误之类的问题。如果问题仍然存在,请考虑使用 DejaVu 字体系列。
我们可以使用文本文件来代替文本。为此,我们需要使用下面提到的 和 语句。
**def texts(self,name):
with open(name,'rb') as xy:
txt=xy.read().decode('latin-1')
self.set_xy(10.0,80.0)
self.set_text_color(76.0, 32.0, 250.0)
self.set_font('Arial', '', 12)
self.multi_cell(0,10,txt)**
哦,不要忘记设置作者:)
**pdf.set_author('Eser SAYGIN')**
概述
PDF 似乎有点老派,但它仍然是最广泛使用的报告工具,它仍然是商业世界中许多公司的有用工具。在本文中,我们学习了如何在 Python 中使用 PyFPDF 创建一个基本的 pdf 文件。如果您需要生成有用的基本 PDF,那么这个库可能正合您的胃口。
** [## eser saygı客户细分主管—图尔基耶 i̇ş银行| LinkedIn
经验丰富的项目主管,有银行业工作经验。英语熟练…
www.linkedin.com](https://www.linkedin.com/in/esersaygin/)
有用链接
- https://pyfpdf.readthedocs.io/en/latest/index.html
- http://www.fpdf.org/
- https://github.com/reingart/pyfpdf**
使用 Python 创建照片镶嵌
一步一步的教程,让你自己的美丽的图像
由 AmaltasCoder 使用 Tubromosaic.com 创建的图像
代码更新于 2021 年 12 月 8 日
- 不再需要 Image_Tester.py
- 感谢丹·布莱克、约翰内斯·拉·波特和乔丹·莫里斯的编辑和评论
照片马赛克是由许多较小的图像组成的图像。较小的图像拥有与原始图像中的位置相似的颜色和亮度,使得原始图像从远处看是可见的,类似于克洛德·莫内的画。有许多在线工具(付费软件)可以创建照片马赛克,然而,我决定在 python3 中实现一个照片马赛克创建器,并与社区共享。我的妻子正在创建一个新的生活方式博客,我想还有什么比从我们一年多前的婚礼中创造一幅美丽的图像更好的方式来帮助她的项目呢。源代码可以在Data Dolittle GitHub上免费获得。让我们从设置运行脚本所需的环境开始。
工具
照片拼接程序需要和 numpy 分别处理图像和矩阵操作。按照上面文本超链接中的说明安装这两个软件包。
图像
你心中有想制作照片马赛克的图像吗?太好了!图像组成更小的图像怎么样?背景图像越多,镶嵌效果就越好。如果你想快速下载大量图片,我建议你使用 chrome 扩展插件直接从 google images 下载图片。火狐中还有其他批量图片下载程序。把所有的背景图片放在同一个文件夹里。将您的前景图像保存在该文件夹之外。你所有的照片应该是 3 通道图像,如 jpeg 或 jpg。确保运行 Image_Tester.py 脚本来检查背景图像文件夹中的 3 通道图像。移除任何。png,。口角或者。创建照片马赛克之前的 gif。该脚本会将图像移动到名为“不可用的图像”的文件夹中。
python Image_Tester.py --images [Background_Images_Folder]
马赛克创造者
现在,您已经安装了必要的工具并拥有了照片,您已经准备好创建您的照片镶嵌。必需的输入包括目标前景图像、背景图像文件夹和网格的大小,两个整数之间用空格隔开。一个可选参数是输出标志,用于将镶嵌图像保存为您想要的任何名称。
python Mosaic_Creator.py --target [Input_Image] --images [Background_Images_Folder] --grid 50 50 --output [Output_Name]
代码
这一节包含了创建引擎的细节,所以对于那些无聊的伪代码滚动到底部,看看我的演示的最终产品。完整代码可在 GitHub 上获得。
****图像目录:背景图像被加载到内存中,进行整形和处理。该处理包括获取图像中的*均红色、蓝色和绿色通道。这个*均值用于将背景图像放入网格中。
****目标图像:目标图像被分成由用户输入定义的网格。单元尺寸是原始图像尺寸和由于网格尺寸导致的分割数量的乘积。计算每个像元的红色、绿色和蓝色*均值。单元*均值和背景图像*均值之间的最佳匹配用于将背景图像放入单元中。所有单元格都填充有背景图像,生成的镶嵌图保存到文件中。
输出
使用我们的结婚照运行脚本后,创建了以下马赛克。
照片马赛克脚本的产品在右边,原始图像在左边。
这幅镶嵌画看起来像原作,只是颜色更深。我们的婚礼在日落时举行,所以许多照片的背景都很暗,导致马赛克图像变暗。增加照片的数量将提供更多的细节,增亮的照片将增加整体亮度。本文详细介绍了使用 GitHub 上可用的脚本在 python 中创建照片马赛克的工具和过程。你可以使用任何东西来创建你自己的照片马赛克!我的名字是科迪·格利克曼,可以在 LinkedIn 上找到我。请务必查看下面的一些其他文章。
将 MacOS 默认值从 Python 2 升级到 Python 3
towardsdatascience.com](/time-to-make-the-switch-9eb719d434de) [## 使用 GitHub 创建漂亮的静态网页
查找模板和为静态网页创建表单的位置
towardsdatascience.com](/building-a-beautiful-static-webpage-using-github-f0f92c6e1f02) [## 使用 Dash 和 SQL 快速浏览圣诞歌曲
使用 SQL 数据库创建 Dash 仪表板的简单项目
towardsdatascience.com](/dashing-through-christmas-songs-using-dash-and-sql-34ef2eb4d0cb)
这是一幅树木镶嵌画
由维基百科上的树木图片制成的树木马赛克**
使用 Python 创建演示文稿
创建和更新 Powerpoint(。pptx)文件
亚历克斯·利特温在 Unsplash 上的照片
时间是科学家拥有的最宝贵的数据,必须有效利用。如果你经常准备一个演示文稿,那就意味着你在忙着浪费时间。但是如果你可以自动化的话,为什么你要做一些像繁重的工作呢?
这是我关于耗时工作自动化的第二篇文章。你可以点击 这里 找到另一个关于用 python 创建 pdf 文件的。
本文由五部分组成。
1-入门
2-添加图像
3-添加图表
4-实施
现在我将向您展示用 Python 创建 PowerPoint 幻灯片的基础。
入门
**python-pptx**
是一个用于创建和更新 PowerPoint 文件的 Python 库。这篇文章将是对这个包的基本介绍。如果你想了解更多,这个是你应该查看的官方文档页面。现在让我们安装软件包,如果你没有。
**pip install python-pptx**
我建议你使用 pip 安装代码行,因为这个包依赖于Ixml
、Pillow
和XlsxWriter
包。如果您使用 setup.py 安装方法,您将需要自己安装这些依赖项。
用例子来解释一件事总是很容易的。为此,我将首先使用官方的例子。在文章的最后,我将逐页构建一个基本的 PowerPoint 演示文稿。让我们从添加幻灯片开始。为此,我们首先需要导入 pptx 包。
**from pptx import Presentation**
我们将使用**Presentation()**
打开或创建一个演示文稿。让我们把它赋给一个变量。
**prs=Presentation()**
现在我们已经准备好制作我们的第一张幻灯片。这张幻灯片将包含一个标题和一个副标题。
**lyt=prs.slide_layouts[0] # choosing a slide layout**
**slide=prs.slides.add_slide(lyt) # adding a slide**
**title=slide.shapes.title # assigning a title**
**subtitle=slide.placeholders[1] # placeholder for subtitle****title.text="Hey,This is a Slide! How exciting!" # title****subtitle.text="Really?" # subtitle****prs.save("slide1.pptx") # saving file**
好了,我们刚刚制作了第一张幻灯片。让我们逐行解释这个代码块。你可以把幻灯片布局想象成一个模板。共有九种幻灯片布局,我们在本例中使用了第一种。**prs.slide_layouts[0]**
这意味着‘我将使用标题布局’。那么我们怎么知道应该选择哪一个呢?这都是关于 PowerPoint 中的幻灯片母版。如下图所示,幻灯片母版上的第一项是“标题幻灯片布局”。这就是为什么**slide_layouts[0]**
给了我们标题对象。顺便说一下,你可以使用空白布局,而不是标题布局。
其次,我们使用了 add_slide 函数来添加一个继承布局的新幻灯片。**slide=prs.slides.add_slide(lyt)**
通过做这个作业,我们刚刚添加了一个包含标题对象的幻灯片布局(很大的一个!).
在这一步之后,我们已经创建了一个标题变量。**title=slide.shapes.title**
下一排是**subtitle=slide.placeholders[1]**
。这意味着如果你想添加一个副标题,那么你必须添加一个占位符。使用**.text**
属性,我们可以在一个形状上分配一个字符串。
代码块的最后一步是导出 pptx 文件。**prs.save(“slide1.pptx”)**
在我们的路径中将幻灯片保存为 pptx。这就是结果。
添加图像
好了,我们刚刚学习了如何添加幻灯片、标题和副标题。现在是添加图表和图像的时候了。
在添加新元素之前,我应该提到一些重要的东西。长度等级。pptx 包有一个 util 模块,包含实用函数和类,如长度,英寸等。
**from pptx.util import Inches**
在这里你可以找到 pptx.util 的源代码。你可能会想,“为什么它如此重要?”。首先,如你所知,有两种默认的幻灯片大小。标准和宽屏。
如果您想使用宽屏演示,您应该在开始时定义幻灯片大小。
**prs.slide_width = Inches(16)
prs.slide_height = Inches(9)**
除此之外,正如您将在下面看到的,我们需要调整对象以适应我们的工作空间。好吧,我们继续。现在我们需要定义一个图像路径,以便以后使用。
**img_path = '/your-img-path/{your-image}.png'**
好的,进行到一半。在我们的第一个例子中,我们使用了**slide_layout[0]**
。这次我们将选择第六个布局,它是空白的。
**prs = Presentation()
blank_slide_layout = prs.slide_layouts[6]
slide = prs.slides.add_slide(blank_slide_layout)**
嗯,我们得到了我们需要的一切。让我们添加一个图像。
**path='stick2.png'** **prs = Presentation()
blank_slide_layout = prs.slide_layouts[6]
slide = prs.slides.add_slide(blank_slide_layout)**
我们现在要定义两个变量。这些变量允许您根据需要调整位置或移动。
**left=Inches(1)
top=Inches(0.5)**
如果您检查这些值,您会看到 914400。这是动车组,每英寸有 914400 个动车组。 (1 寸= 914400 动车组)
好了,我们准备好添加图像了。
**img=slide.shapes.add_picture(path,left,top)**
他在指出什么。
添加图表
pptx 包支持添加或修改图表。为此,我们将使用**pptx.chart.data**
和**pptx.enum.chart**
。
首先,我们需要定义图表数据。
**chart_data=ChartData()**
我们再补充一些类别和系列。
**chart_data.categories = ['Column 1', 'Column 2', 'Column 3']
chart_data.add_series('Hey', (10.5, 5.5, 17.5))
chart_data.add_series('there', (25.5, 40.3, 30.7))
chart_data.add_series('reader', (5.2, 10.3, 8.4))**
现在我们需要定义一些度量。 (左、上、宽、高)
**x,y,cx,cy=Inches(1),Inches(2),Inches(5),Inches(7)**
我们现在准备添加一个图表。
**chart=slide.shape.add_chart(XL_CHART_TYPE.LINE_MARKERS_STACKED,x,y,cx,cy,chart_data).chart****prs.save('chart-02.pptx')**
刚开始就够了。让我们构建一个端到端的 PowerPoint 演示文稿。
实施
好的,我们是白领,我们的经理想要一个明天的快速演示。是的,如果你愿意,你可以一个一个地建立一个演示文稿。
让我们建立一个自动化的演示程序。
第 1 页:
首先,让我们导入必需的包。
**from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE
from pptx.dml.color import RGBColor
from pptx.util import Inches, Pt
from pptx.enum.dml import MSO_THEME_COLOR****title='Automated Presentation Creating Process\n\
How to Create PowerPoint Presentations with Python '
pylogo='pylogo.png'
pptlogo='pptlogo.png'
prs = Presentation()**
导入包后,我定义了一些变量。(标题和徽标)。现在我们可以创建我们的第一页。我将使用宽屏布局。
**slide = prs.slides.add_slide(prs.slide_layouts[6])
prs.slide_width = Inches(16)
prs.slide_height = Inches(9)****shape = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, Inches(9/1.5),Inches(16),Inches(9/8.5)
)**
**shape.shadow.inherit = False**
**fill=shape.fill
fill.solid()
fill.fore_color.rgb=RGBColor(255,0,0)
shape.text= title
line=shape.line
line.color.rgb=RGBColor(255,0,0)
logo1=slide.shapes.add_picture(pylogo,Inches(13.8),Inches(6.0),height=Inches(1.0),width=Inches(1.0))
logo2=slide.shapes.add_picture(pptlogo,Inches(14.5),Inches(5.8),height=Inches(1.5),width=Inches(1.5))**
我刚刚添加了一个矩形和两个图像。**slide.shapes.add_shape({type of shape},{left spacing},{top spacing},{width},{height})**
。
我已经用**shape.shadow.inherit = False**
去掉了阴影效果。形状对象也有一个**.fill**
属性。这是形状的颜色。**.line**
是一个形状周围的边框,可以有不同的颜色。因此,我为它们分配了相同的 RGB 代码。结果如下:
第 2 页:
让我们添加一个图表。
slide = prs.slides.add_slide(prs.slide_layouts[6])shape = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, Inches(0.5),Inches(16),Inches(0.3))
shape.shadow.inherit = False
fill=shape.fill
fill.solid()
fill.fore_color.rgb=RGBColor(255,0,0)
shape.text= "How to Add a Chart"
line=shape.line
line.color.rgb=RGBColor(255,0,0)
logo1=slide.shapes.add_picture(pylogo,Inches(14.5),Inches(0.4),height=Inches(0.5),width=Inches(0.5))
logo2=slide.shapes.add_picture(pptlogo,Inches(15.0),Inches(0.4),height=Inches(0.5),width=Inches(0.5))**from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.chart.data import ChartData
import numpy as np
import datetime****N = 100****random_x = np.random.randn(N) + 10
random_y = np.random.randn(N)+5
random_z = np.random.randn(N) +20****dte=datetime.datetime.today()
dt_lst=[dte-datetime.timedelta(days=i) for i in range(N)]****chart_data = ChartData()
chart_data.categories = dt_lst
chart_data.add_series('Data 1', random_x)
chart_data.add_series('Data 2', random_y)
chart_data.add_series('Data 3', random_z)****x, y, cx, cy = Inches(1), Inches(2), Inches(14), Inches(6)
chart = slide.shapes.add_chart(
XL_CHART_TYPE.LINE, x, y, cx, cy, chart_data
).chart
chart.has_legend = True
chart.legend.include_in_layout = False
chart.series[2].smooth = True**
我刚刚在空白幻灯片上添加了一个随机变量的折线图。你可以在这里找到图表类型。
**slide.shapes.add_chart({type of chart},{left spacing},{top spacing},{width},{height}, {chart data})**
。
第 3 页:
如果您愿意,您可以添加一个 plotly 图表作为图像。让我们看看如何做到这一点。
slide = prs.slides.add_slide(prs.slide_layouts[6])shape = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, Inches(0.5),Inches(16),Inches(0.3))
shape.shadow.inherit = False
fill=shape.fill
fill.solid()
fill.fore_color.rgb=RGBColor(255,0,0)
shape.text= "How to Add an Image"
line=shape.line
line.color.rgb=RGBColor(255,0,0)
logo1=slide.shapes.add_picture(pylogo,Inches(14.5),Inches(0.4),height=Inches(0.5),width=Inches(0.5))
logo2=slide.shapes.add_picture(pptlogo,Inches(15.0),Inches(0.4),height=Inches(0.5),width=Inches(0.5))**import plotly.graph_objects as go
import pandas as pd****df = pd.read_csv('**[**https://raw.githubusercontent.com/plotly/datasets/718417069ead87650b90472464c7565dc8c2cb1c/sunburst-coffee-flavors-complete.csv'**](https://raw.githubusercontent.com/plotly/datasets/718417069ead87650b90472464c7565dc8c2cb1c/sunburst-coffee-flavors-complete.csv')**)****fig = go.Figure(go.Sunburst(
ids = df.ids,
labels = df.labels,
parents = df.parents))
fig.update_layout(uniformtext=dict(minsize=10, mode='hide'))****fig.write_image("img.png")****imgpth='img.png'****left = top = Inches(1)
pic = slide.shapes.add_picture(imgpth, left, top)**
我们使用了一个巧妙的旭日东升例子。
**slide.shapes.add_picture({image path},{left spacing},{top spacing})**
。
第 4 页:
最后,让我们添加一个文本框。
slide = prs.slides.add_slide(prs.slide_layouts[6])shape = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, Inches(0.5),Inches(16),Inches(0.3))
shape.shadow.inherit = False
fill=shape.fill
fill.solid()
fill.fore_color.rgb=RGBColor(255,0,0)
shape.text= "How to Add an Image"
line=shape.line
line.color.rgb=RGBColor(255,0,0)
logo1=slide.shapes.add_picture(pylogo,Inches(14.5),Inches(0.4),height=Inches(0.5),width=Inches(0.5))
logo2=slide.shapes.add_picture(pptlogo,Inches(15.0),Inches(0.4),height=Inches(0.5),width=Inches(0.5))left = Inches(1)
top = Inches(2)
width = Inches(12)
height = Inches(5)**text_box=slide.shapes.add_textbox(left, top, width, height)****tb=text_box.text_frame
tb.text='Do you know how the Orcs first came into being? They were elves once,\n\
taken by the dark powers, tortured and mutilated. A ruined and terrible form of life.\n\
Now... perfected. My fighting Uruk-Hai. Whom do you serve?'****prg=tb.add_paragraph()
prg.text=" "****prg=tb.add_paragraph()
prg.text="They will find the Ring, and kill the one who carries it."**
有两种不同的文本代码块。第一个是关于文本框的。**slide.shapes.add_textbox(left, top, width, height)**
用***tb.text***
可以在文本框内添加文字。如果你想添加另一个段落,那么你应该使用***tb.add_paragraph()***
。
结果如下:
第 5 页:
最后,我们已经完成了基本的演示。也许你只是想加一个感谢页:)
slide = prs.slides.add_slide(prs.slide_layouts[6])shape = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, Inches(4.0),Inches(16),Inches(1.0))
shape.shadow.inherit = False
fill=shape.fill
fill.solid()
fill.fore_color.rgb=RGBColor(255,0,0)
shape.text= "Thank You"
line=shape.line
line.color.rgb=RGBColor(255,0,0)
logo1=slide.shapes.add_picture(pylogo,Inches(14.5),Inches(4.0),height=Inches(1.0),width=Inches(1.0))
logo2=slide.shapes.add_picture(pptlogo,Inches(15.0),Inches(4.0),height=Inches(1.0),width=Inches(1.0))
不要忘记保存:)
**prs.save('basic_presentation.pptx')**
结论
正如我在本文开头所说,设计一个演示文稿是一个耗时的过程。就像一个妖精说的, “时间就是金钱,朋友!” 。
这是用 Python 创建 PowerPoint 演示文稿的基本示例。您可以定义函数,并在需要时运行它们。欲了解更多信息,请访问pptx文档页面。
感谢阅读!
代码
****参考文献:
创建用于探索性数据分析和数据清理的 Python 函数
自动化数据科学家的枯燥工作
更新(2021–02–05):这篇博文中使用的 Python 库现在发布在 PyPi 上。这个包还包含了新特性:它提供了一个类,这个类包含了简化 Scikit-Learn 模型建模过程的方法。这篇博客文章的第二部分即将发表,将介绍如何在 Python 中利用 OOP 来自动化建模过程。
探索性的数据分析和数据清洗是我们开始开发机器学习模型之前的两个必不可少的步骤,它们可能非常耗时,尤其是对于仍然在熟悉这整个过程的人来说。
EDA 和数据清理很少是一次性的、线性的过程:您可能会发现自己经常回到前面的章节并修改处理数据集的方式。加快这个过程的一个方法是回收一些你发现自己反复使用的代码。这就是为什么我们应该创建函数来自动化 EDA 和数据清理的重复部分。在 EDA 和数据清理中使用函数的另一个好处是消除由代码中的意外差异导致的结果不一致。
在这篇博文中,我将带您浏览几个我为 EDA 和数据清理创建的有用的 python 函数。包含所有这些函数的库可以从我的库[eda_and_beyond](https://github.com/FredaXin/eda_and_beyond/blob/master/eda_and_beyond.py)
中克隆。特别感谢所有为这个小(但正在增长的)图书馆做出贡献的人。
处理缺失值的函数
EDA 中的一个重要步骤是检查缺失值,研究缺失值中是否有任何模式,并相应地决定如何处理它们。
这里的第一个函数是让您大致了解每一列中缺失数据的总数和百分比:
def intitial_eda_checks(df):
'''
Takes df
Checks nulls
'''
if df.isnull().sum().sum() > 0:
mask_total = df.isnull().sum().sort_values(ascending=False)
total = mask_total[mask_total > 0]
mask_percent = df.isnull().mean().sort_values(ascending=False)
percent = mask_percent[mask_percent > 0]
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
print(f'Total and Percentage of NaN:\n {missing_data}')
else:
print('No NaN found.')
在初始检查之后,您可以决定是否要对那些缺失值过多的列进行更仔细的检查。通过指定缺失值百分比的阈值,以下函数将给出缺失值超过该阈值的列的列表:
def view_columns_w_many_nans(df, missing_percent):
'''
Checks which columns have over specified percentage of missing values
Takes df, missing percentage
Returns columns as a list
'''
mask_percent = df.isnull().mean()
series = mask_percent[mask_percent > missing_percent]
columns = series.index.to_list()
print(columns)
return columns
处理缺失值的方法有很多。如果您决定删除缺少太多值的列(超过您指定的某个阈值),您可以使用此函数来完成任务:
def drop_columns_w_many_nans(df, missing_percent):
'''
Takes df, missing percentage
Drops the columns whose missing value is bigger than missing percentage
Returns df
'''
series = view_columns_w_many_nans(df, missing_percent=missing_percent)
list_of_cols = series.index.to_list()
df.drop(columns=list_of_cols)
print(list_of_cols)
return df
但是,从数据集中删除缺失值有许多不利之处,例如降低了统计能力。如果你决定估算缺失值,检查一下 Sklearn 的[SimpleImputer](https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html)
模块,这是一个简单易用的工具,可以根据你的喜好估算缺失值。
此外,如果你想了解更多关于如何处理缺失值的信息,可以看看这张由人口研究中心的梅丽莎·汉弗莱斯制作的幻灯片。
数据可视化功能
人脑非常善于识别模式,这就是为什么在 EDA 过程中可视化数据集并识别模式会非常有益。例如,直方图使得分析数据的分布变得更容易;箱线图非常适合识别异常值;在检查两个变量之间的相关性时,散点图非常有用。谈到数据可视化,Matplotlib 和 Seaborn 是你最好的朋友。但是,如果有大量要素,为每个要素创建单独的地块会变得很乏味。在这一节,我将带你通过几个函数来创建团体图,可以帮助你一箭多雕。
我们经常想看看有数值的列的分布。以下函数将为数据集中的所有数字列创建一组绘图。(这个函数改编自张秀坤·高伟凯的博文,是一篇用真实数据集看完整个 EDA 过程的好读物):
def histograms_numeric_columns(df, numerical_columns):
'''
Takes df, numerical columns as list
Returns a group of histagrams
'''
f = pd.melt(df, value_vars=numerical_columns)
g = sns.FacetGrid(f, col='variable', col_wrap=4, sharex=False, sharey=False)
g = g.map(sns.distplot, 'value')
return g
下面是输出的样子:
另一个有用的可视化工具是热图。当你想检查因变量和自变量之间的相关性时,热图非常方便。如果要素过多,热图通常会显得杂乱无章。避免这种情况的一种方法是只为因变量(目标)和自变量(特征)创建热图。以下功能将帮助您完成这项任务:
def heatmap_numeric_w_dependent_variable(df, dependent_variable):
'''
Takes df, a dependant variable as str
Returns a heatmap of all independent variables' correlations with dependent variable
'''
plt.figure(figsize=(8, 10))
g = sns.heatmap(df.corr()[[dependent_variable]].sort_values(by=dependent_variable),
annot=True,
cmap='coolwarm',
vmin=-1,
vmax=1)
return g
正如您在输出中看到的,因为值是排序的,所以相关性变得更容易阅读。
用于更改数据类型的函数
确保要素具有正确的数据类型是 EDA 和数据清理过程中的另一个重要步骤。Pandas 的.read_csv()
方法解释的数据类型与原始数据文件不同,这种情况经常发生。在这一步中,阅读数据字典很有启发性。此外,如果您计划进行一些功能工程,那么就需要更改数据类型。以下两个函数协同工作,将分类特征转换为数字(序数)特征:
第一个函数是输出一个函数,即转换器,它将把列表中的每个str
转换成一个int
,其中int
是列表中该元素的索引。
def categorical_to_ordinal_transformer(categories):
'''
Returns a function that will map categories to ordinal values based on the
order of the list of `categories` given. Ex.
If categories is ['A', 'B', 'C'] then the transformer will map
'A' -> 0, 'B' -> 1, 'C' -> 2.
'''
return lambda categorical_value: categories.index(categorical_value)
第二个函数有两个部分:首先,它采用以下形式的字典:
categorical_numerical_mapping = {
'Utilities': ['ELO', 'NoSeWa', 'NoSewr', 'AllPub'],
'Exter Qual': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],
'Exter Cond': ['Po', 'Fa', 'TA', 'Gd', 'Ex']
}
使用我们之前定义的函数,它将字典变成这样:
transformers = {'Utilities': <utilties_transformer>,
'Exter Qual': <exter_qual_transformer>,
'Exter Cond': <exter_cond_transfomer>}
函数的第二部分使用.map()
方法将每个转换器函数映射到数据帧上。请注意,在此功能期间,将创建原始数据帧的副本。
def transform_categorical_to_numercial(df, categorical_numerical_mapping):
'''
Transforms categorical columns to numerical columns
Takes a df, a dictionary
Returns df
'''
transformers = {k: categorical_to_ordinal_transformer(v)
for k, v in categorical_numerical_mapping.items()}
new_df = df.copy()
for col, transformer in transformers.items():
new_df[col] = new_df[col].map(transformer).astype('int64')
return new_df
我的博文到此结束。我的目标是创建一个开源库,使 EDA 和数据清理过程更加简化。一如既往,我希望听到您的反馈。如果您有任何更正或希望为这个小型开源项目做出贡献,请提出请求。感谢您的阅读!
资源:看看这个很棒的(而且是免费的!)Python 编程教科书:用 Python 自动化枯燥的东西作者:Al Sweigart。
最初发表于https://github.com。
使用 AWS Amplify 创建 React + GraphQL 无服务器 Web 应用程序
诺德伍德主题公司在 Unsplash 上拍摄的照片
AWS 放大器
AWS Amplify 是由 Amazon Web Services 提供的一项服务,它提供了使用 AWS 服务以更安全和可扩展的方式为移动和 Web *台创建端到端解决方案的能力。AWS Amplify 最初于 2018 年 11 月推出,从那时起,许多开发人员都创建并部署了他们的新应用程序,因为 Amplify 提供了简单性和可靠性。
Amplify 提供了易于配置的功能,称为模块,名为认证,存储,后端 API,托管和部署等。你可以从这里获得更多关于 Amplify 提供的模块的信息。在本文中,我们将使用 3 个模块,身份验证、后端 api 以及托管和部署。
在本文中,让我们尝试使用 GraphQL api 构建和部署 react 应用程序。下面是为了实现我们的目标,我们将在本文中执行的任务。
- 安装和配置 Amplify CLI
- 创建 react 应用程序
- 添加身份验证
- 添加 GraphQL api
- 在 Amplify 上部署和托管
我在本文中使用的完整 react 应用程序代码可以在这里找到。
安装和配置 Amplify CLI
在创建 react 应用程序之前,我们需要做的第一步是在系统中全局安装 Amplify CLI 工具。为此,我们需要在终端中运行以下命令。(我们需要在系统中安装 node.js 才能使用 npm)
npm install -g @aws-amplify/cli
现在,为了用我们的 AWS 帐户配置已安装的 Amplify,发出下面的命令。
amplify configure
这将提示我们需要回答的一系列问题。首先,它会要求我们使用 web 控制台登录我们的 AWS 帐户。下一个重要问题是它将为我们的 Amplify CLI 工具创建的 IAM 用户。在为用户指定一个名称之后,它将再次把我们重定向到 IAM 控制台,在这里我们需要为我们的用户提供必要的权限策略。
在这里确保为用户添加管理员访问策略。然后转到安全凭证选项卡,为该用户创建一个访问密钥,并记住下载和存储凭证细节。回到控制台,作为下一步,它将要求这个 accessKeyId 和 secretAccessKey。现在,我们已经完成了对 Amplify CLI 的配置。
创建 react 应用程序
现在让我们开始创建 react 应用程序。为了创建新的 react 应用程序,发出以下命令。
npx create-react-app amplify-react-sample
cd amplify-react-sample
npm start
如果在 localhost:3000 中一切顺利,它应该显示新创建的 react 应用程序。
下一步是将 aws-amplify 作为依赖项添加到 react 应用程序中。这个包将具有从我们的 react 代码中对 Amplify 进行所有配置的功能。
npm install aws-amplify @aws-amplify/ui-react
接下来,让我们为 react 应用程序初始化一个 Amplify 项目。
amplify init
我们将再次被提示回答一系列问题。对于项目名称,您可以随意命名。对于应用类型,选择 javascript ,对于框架,选择 react 。对于其他问题,我们可以保留默认值,因为我们不会更改应用程序内部的 react 构建和运行脚本。
我们项目中的以下修改将在此步骤中完成。
- 一个名为 amplify 的新文件夹将在项目的根级别创建。它存储了与我们后端相关的所有配置(AWS 资源)。(在本文中,我们将添加身份验证和 GraphQL API)。随着我们添加更多的 amplify 功能,与这些功能相关的配置将作为基础架构代码存储在该文件夹中。
- 将在
src
目录中创建一个名为aws-exports.js
的文件,该文件包含我们将使用 Amplify 创建的服务的所有配置。目前,它只包含地区信息,因为我们还没有使用任何 Amplify 服务。
同时,在 AWS 上将创建一个云项目,我们可以通过 Amplify 控制台访问该项目。控制台还为我们的应用程序提供了一些其他的配置选项。
现在我们的 react 应用程序已经用 Amplify 初始化了,让我们进入下一个任务,添加身份验证。
添加身份验证
对于认证,Amplify 使用 AWS Cognito 作为主要的认证提供者。AWS Cognito 提供了许多功能,如身份验证、用户注册、帐户恢复等等。你可以从点击查看更多可用功能或认知。要在我们的应用程序上添加 Cognito 身份验证,请在终端中发出以下命令。
amplify add auth
我们将再次被提示有关身份验证配置的问题。对于本文,我们将选择默认配置进行安全配置。(如果需要,我们可以稍后通过运行 amplify update auth 来更新它)。对于登录选项,您可以选择用户名或电子邮件。
就这样,现在我们已经为自己创建了身份验证模块。但在我们的应用中测试之前,我们需要将这种配置放大。为此,只需在终端中发出以下命令。
amplify push
该命令将在 AWS 上的 Amplify 和 Cognito 上创建所需的配置,还将使用 Cognito auth 配置编辑我们的aws-exports.js
文件。
下一步是在我们的 react 应用程序中使用这种认知身份验证。首先,我们需要为我们应用程序导入放大器模块。编辑index.js
文件以导入放大器,并使用aws-exports.js
文件中的配置配置放大器模块。
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig);
现在我们可以在应用程序中使用 auth 模块了。转到app.js
,首先导入以下模块。
import { withAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";
接下来,用带身份验证器的包装我们的应用组件。这里 AmplifySignOut 是 amplify-react 提供的一个组件,它充当一个 SignOut 按钮。关于 Amplify 提供的更多预建 UI 组件,你可以在这里查看。
现在我们已经在 react 应用程序中添加了 Amplify 身份验证,让我们启动服务器并测试它是否工作。运行npm start
,进入浏览器。
现在,当我们尝试访问 localhost:3000 时,它应该会显示一个登录页面。如果你能看到上面的页面,这意味着我们的身份验证设置正确。下一步是在我们的 Cognito 用户池中创建一个用户。您可以通过 IAM 控制台或点击创建账户来创建用户。用户也可以通过 Cognito 控制台进行管理。
现在尝试使用我们创建的用户登录,现在应该可以转到我们的主页了。如果一切正常,让我们进入下一个任务。
添加 GraphQL API
Amplify 支持使用 AWS AppSync 创建无服务器后端 API。AppSync 支持 REST 和 GraphQL API 框架。对于本文,我们将使用 AppSync GraphQL api。要了解什么是 GraphQL 以及如何使用 AWS AppSync 创建无服务器的 GraphQL,您可以查看我写的这篇文章。
在添加后端 GraphQL api 之前,让我们对 react 应用程序做一些修改,以便在表格中显示数据。在本文中,让我们假设我们的应用程序将显示与演员相关的数据。
在上面的代码中,我使用 material-ui 来创建表格,您可以使用npm install @material-ui/core
来安装它。代码只显示了一个表,在这个表中,我们已经在 getActors() 方法中定义了一组数据。(确保也在 App.js 中导入 Home 组件)
现在,让我们尝试创建自己的 GraphQL API 来从 API 中检索参与者信息,而不是在代码中获取硬编码的值。要将 GraphQL api 添加到我们的应用程序中,只需在终端中发出以下命令。
amplify add api
对于问题,您将被要求提供选择服务作为 GraphQL 和授权选择亚马逊认知用户池。接下来,为模式模板选择带有字段的单个对象。这将为我们自动创建一个简单的 GraphQL 模式。接下来,当您要求编辑模式时,按下 Y 。
这将打开我们自动创建的 schema.graphql。在这里,我们可以看到已经创建了带有模型 Todo 的模式。
type Todo [@model](http://twitter.com/model) {
id: ID!
name: String!
description: String
}
让我们为我们的用例编辑这个模式,我们的模型将是 Actors。
在这里,如果需要,我们还可以指定模式中允许的查询和变异。在 GraphQL 中,Query 类似于 GET 调用,我们不对数据做任何修改,而 Mutation 类似于 PUT、POST、DELETE 调用,我们对数据做修改。但是如果我们想要的话,这也可以在 AppSync 中自动生成。
下一步是推动这个 GraphQL api 来扩展它在 AppSync 上的创建位置。发出下面的命令来推送 API。
amplify push
这里再次向我们询问一组配置。选择语言目标为 javascript,并将文件模式设为默认。稍后,它将要求为我们的模式生成所有 GraphQL 操作。确保为此输入 Yes ,因为这将为我们的演员模型自动生成查询和变异。
这将在 AppSync 上创建我们的 GraphQL API。我们的数据库也将在 AWS DynamoDB 上自动创建。此外,它在 amplify 文件夹中创建一个名为backend
的新文件夹。我们的 Actor 模式和其他 AppSync 相关配置可以在这个后端文件夹中找到。
在配置的最后,我们将获得使用 actors 模式配置的 GraphQL 端点。现在,如果我们转到 AppSync 控制台,我们可以看到新创建的 Actors api。
现在您可以看到我们的模式已经用查询和变异更新了,因为 AppSync 为我们生成了这些。这里,让我们试着创建一些数据,这样我们就可以从 API 进行查询。转到查询选项卡。在那里,从下拉菜单中选择突变并点击 + 按钮。
突变是我们更新数据库的方式。因为我们需要创建演员,所以单击 createActor 并填写演员的详细信息。现在,让我们添加两个演员。
****
现在让我们测试我们的 GraphQL。为此,我们需要使用一个查询。点击 + 按钮,删除突变并添加查询
让我们使用 listActors 进行查询,只返回 id、firstName、lastName、age 和 movies 字段。
如果我们的查询返回了正确的结果,这意味着我们的 GraphQL api 现在已经正确配置了。现在让我们开始将 GraphQL API 添加到 react 应用程序中。
首先,我们需要在我们的 Home 组件中导入 Amplify 、 API 、 graphqlOperation 模块。
import Amplify, { API, graphqlOperation } from 'aws-amplify'
接下来,我们需要导入将要在应用程序中执行的查询和变异。在我们的例子中,查询是 listActors ,变异是 createActor 。我们可以从 AppSync 控制台或后端/api 文件夹中的aws-schema.graphql
文件中获得这些查询或变异
import { createActor} from './graphql/mutations'
import { listActors} from './graphql/queries'
此外,确保使用正确的配置来配置导入的放大器。
import awsExports from "./aws-exports"; Amplify.configure(awsExports);
下面是我们将用于获取演员的函数。
现在,我们需要做的最后一件事是从 UseEffect 调用这个 fetchActors() 函数。移除之前调用的 getActors() 方法,替换为 fetchActors() 。
现在,如果一切正常,它应该只显示 2 个演员,现在来自我们的 GraphQL api。
接下来,让我们尝试使用 react 应用程序添加参与者。为此,我们将使用已经导入的 addActor 突变。
除了创建 addActor()函数,我们还需要输入字段。下面是 Home 组件的完整代码。
现在让我们试着添加一个 actor,在它出现在桌面上之后。
现在已经完成了,让我们进入最后一项任务,部署和托管
部署和托管
现在,对于最后一部分,让我们通过发出以下命令为我们的应用程序添加托管模块。
amplify add hosting
我们将再次被提示几个问题。对于插件选择带放大控制的主机,对于类型选择手动部署。****
现在唯一剩下的事情就是发布这个模块来放大。对于以下问题命令。
amplify publish
这将生成一个 cloudFormation 堆栈,并将在 AWS 上创建必要的基础设施。
过一会儿,所有的基础设施将被创建,我们将有部署的 URL。你可以继续下去,检查网址,以确保每件事的工作,因为它应该是。
此外,除了手动部署,我们还可以从 Amplify 选择连续部署。
好了,这是如何使用无服务器 GraphQL api 和使用 Cognito 认证部署 react 应用程序的简单指南。如果你想了解更多关于 AWS Amplify 和它的更多特性,请遵循官方文档。谢谢:)
**[## 放大框架文档
编辑描述
docs.amplify.aws](https://docs.amplify.aws/start)**
使用 R Markdown 创建报告
使用 R Studio 和 R Markdown 编写报告的说明
图片由 Mediamodifier 来自 Pixabay
视频版本
介绍
使用 Jupyter notebook 或 Google Colab 很容易将 python 脚本转换成报告。你知道你也可以对 R 脚本做同样的事情吗?
在本教程中,我将演示如何将您的 R 脚本转换为报表。
先决条件
我们需要安装两个软件。
下载编织机包
请务必下载可以将 R Markdown 文件转换成报告的软件包。
您可以在控制台中运行命令(如下所示)来下载该包。
install.packages("knitr")
在 R Studio 中使用 R Markdown 进行编码
在“文件”标签下,点击“新建文件”和“R Markdown”。
命名文件并选择默认输出格式。您随时可以在以后更改输出格式。
一旦你点击了“Ok”,你现在就可以在 R Markdown 上编码和写报告了。
了解如何使用 R Markdown
突出显示的部分(或单元格)是您可以编写代码的地方。
```{r cars}
YOU CAN WRITE YOUR CODE HERE
并且**非突出显示的**部分是你可以写你的**报告**的地方。

R 降价示例
您可以通过单击单元格右上角的绿色运行按钮来运行整个单元格中的代码。
或者,您可以按 Ctrl + Enter 键只运行选定的代码。
# 针织 R 减价检索报告
为了编织 R Markdown 文件,您可能需要安装几个软件包。R studio 会自动检测到您缺少必要的包,并要求您下载它们。
有三种方法可以输出您的报告。
1. 超文本标记语言
2. 便携文档格式
3. MS Word

输出选项
**HTML 输出**

HTML 输出
**PDF 输出**

PDF 输出
**MS 字输出**

MS Word 输出
我希望这篇文章对你有用。感谢您的阅读!
# 使用 Flask 和 Python 创建 RESTful Web APIs
> 原文:<https://towardsdatascience.com/creating-restful-apis-using-flask-and-python-655bad51b24?source=collection_archive---------0----------------------->
## 编程;编排
## 用 Flask 构建 Web APIs 的综合指南

图片来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2682712) 的 [Gerd Altmann](https://pixabay.com/users/geralt-9301/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2682712)
lask 是一个广泛使用的微型 web 框架,用于在 Python 中创建 API。这是一个简单而强大的 web 框架,旨在快速轻松地开始使用,并能够扩展到复杂的应用程序。
从[文档](https://flask.palletsprojects.com/en/1.1.x/foreword/)中,
> “微型”并不意味着您的整个 web 应用程序必须适合一个 Python 文件(尽管它确实可以),也不意味着 Flask 缺乏功能。微框架中的“微”意味着 Flask 旨在保持核心简单但可扩展。

来源:Flask 的[文件](https://flask.palletsprojects.com/en/1.1.x/_images/flask-logo.png)
# 装置
使用 pip 安装烧瓶
pip install Flask
# 最小烧瓶应用程序
from flask import Flask
app = Flask(name)
@app.route('/hello/', methods=['GET', 'POST'])
def welcome():
return "Hello World!"
if name == 'main':
app.run(host='0.0.0.0', port=105)
将该文件保存为`app.py` *(或任何其他你想要的文件名)*,并进入终端并键入`python app.py`(即`python <filename>.py`)
您应该会看到类似这样的内容:
- Running on http://0.0.0.0:105/ (Press CTRL+C to quit)
启动任何网络浏览器,然后前往`http://localhost:105/hello/`查看应用程序的运行情况。
现在,让我们逐行理解代码的工作原理:
`from flask import Flask` →导入烧瓶类
`app = Flask(__name__)` →创建类的一个实例
`@app.route('/hello/', methods=['GET', 'POST'])` →我们使用`route()`装饰器来告诉 Flask 哪个 URL 应该触发这个函数。
`methods`指定允许哪些 HTTP 方法。默认为`['GET']`
`if __name__ == '__main__'` → `__name__`是 Python 中的一个特殊变量,取脚本名的值。这一行确保我们的 Flask 应用程序仅在主文件中执行时运行,而不是在导入到其他文件中时运行
`app.run(host='0.0.0.0', port=105)` →运行烧瓶应用程序
指定我们希望 flask 应用程序运行的服务器。`host`的默认值为`localhost`或`127.0.0.1`
`0.0.0.0`的意思是*“本地机器上的所有 IPv4 地址”。这确保了从所有地址都可以到达服务器。
默认`port`值为`5000`,您可以设置参数`port`以使用您选择的端口号。*
# 可变规则
您可以使用`<variable_name>`向 URL 添加可变部分。该函数接收变量作为关键字参数。
from flask import Flask
app = Flask(name)
@app.route('/int:number/')
def incrementer(number):
return "Incremented number is " + str(number+1)
@app.route('/string:name/')
def hello(name):
return "Hello " + name
app.run()
运行上面的代码来启动 Flask 应用程序。
打开浏览器并转到`http://localhost:5000/Jimit`,您将看到输出为`Hello Jimit`,当您转到`http://localhost:5000/10`时,输出将为`Incremented number is 11`。
# 返回 JSON 可序列化输出
Flask 应用程序中函数的返回值应该是 JSON 可序列化的。您可以使用`jsonify`使您的输出 JSON 可序列化。该函数包装`json.dumps()`以将 JSON 输出转换成带有*application/JSON*mime-type 的响应对象。
## 示例 1:
这个例子展示了如何对字典对象使用`jsonify`:
from flask import jsonify
@app.route('/person/')
def hello():
return jsonify({'name':'Jimit',
'address':'India'})
这将发送如下的 JSON 响应:
{
"address": "India",
"name": "Jimit"
}
## 示例 2:
还可以使用`jsonify`自动将列表和元组序列化为 JSON 响应。
from flask import jsonify
@app.route('/numbers/')
def print_list():
return jsonify(list(range(5)))
这将产生输出:
[
0,
1,
2,
3,
4
]
# 重定向行为
@app.route('/home/')
def home():
return "Home page"
@app.route('/contact')
def contact():
return "Contact page"
在上面的例子中,`home`端点的 URL 有一个尾随斜杠,而`contact`端点的 URL 缺少尾随斜杠。
这会导致两种不同的行为:
1. 对于`home`端点,如果您访问不带结尾斜杠的 URL,那么 Flask 会将您重定向到带结尾斜杠的 URL。
2. 对于`contact`端点,如果您访问带有结尾斜杠的 URL,那么它将导致状态 404 Not Found。
# 返回状态代码
通过按如下方式指定状态代码,可以将状态代码与响应一起返回:
@app.route('/teapot/')
def teapot():
return "Would you like some tea?", 418
对这个 URL 的响应将是带有状态码`418`的`Would you like some tea?`,而不是通常的`200`。
# 请求前
通过使用`app.before_request` decorator,您可以指定一个应该在请求被处理之前一直执行的函数。
@app.before_request
def before():
print("This is executed BEFORE each request.")
@app.route('/hello/')
def hello():
return "Hello World!"
对于这个例子,首先在服务器上打印语句`This is executed BEFORE each request.`,然后执行`hello`端点的功能。当您希望记录请求以进行监控时,这尤其有用。
# 访问请求数据
要访问请求数据,请使用以下命令
from flask import request
您可以使用以下属性来提取随请求一起发送的数据:
`[request.data](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.data)` →以字符串形式访问输入的请求数据
`[request.args](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.args)` →访问解析后的 URL 参数。返回`ImmutableMultiDict`
`[request.form](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.form)` →访问表单参数。返回`ImmutableMultiDict`
`[request.values](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.values)` →返回组合了`args`和`form`的`CombinedMultiDict`
`[request.json](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.json)` →如果 *mimetype* 为`application/json`则返回解析后的 JSON 数据
`[request.files](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.files)` →返回包含所有上传文件的`MultiDict`对象。每个键是文件名,值是`FileStorage`对象。
`[request.authorization](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.authorization)` →返回一个`[Authorization](https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization)`类的对象。它代表客户端发送的一个*授权*头。
# app.run()参数
`app.run()`在服务器上运行应用程序。除了`host`和`port`
之外,还有各种参数可以与`[app.run()](https://flask.palletsprojects.com/en/1.1.x/api/?highlight=threaded#flask.Flask.run)`一起使用,其中包括:
`debug` →如果`debug`参数设置为`True`,那么服务器将在代码更改时自动重新加载,并在出现未处理的异常时显示交互式调试器。默认为`False`
`use_reloader` →当`use_reloader`设置为`True`时,当代码改变时,服务器将自动重启。默认为`False`
`threaded` →当`threaded`设置为`True`时,进程将在单独的线程中处理每个请求。默认为`False`
`ssl_context` →连接的 SSL 上下文。如果服务器应该自动创建上下文,则需要`[ssl.SSLContext](https://docs.python.org/3/library/ssl.html#ssl.SSLContext)`、格式为`(cert_file, pkey_file)`的元组或字符串`'adhoc'`。默认值为`None`,即 SSL 被禁用。当我们想在 HTTPS 而不是 HTTP 上托管 Flask 应用程序时,使用这个。
# 蓝图
蓝图允许我们将不同的端点分成子域。
`home.py`
from flask import Blueprint
home_bp = Blueprint('home', name)
@home_bp.route('/hello/')
def hello():
return "Hello from Home Page"
`contact.py`
from flask import Blueprint
contact_bp = Blueprint('contact', name)
@contact_bp.route('/hello/')
def hello():
return "Hello from Contact Page"
`app.py`
from flask import Flask
from home import home_bp
from contact import contact_bp
app = Flask(name)
app.register_blueprint(home_bp, url_prefix='/home')
app.register_blueprint(contact_bp, url_prefix='/contact')
app.run()
注意,在两个蓝图中,`/hello/`路线都在调用`hello`函数。
当你去`http://localhost:5000/home/hello`时,输出将是`Hello from Home Page`
而当你访问`http://localhost:5000/contact/hello`时,输出将是`Hello from Contact Page`
# 记录
您可以使用以下方法在 Flask 应用程序中记录语句
app.logger.debug('This is a DEBUG message')
app.logger.info('This is an INFO message')
app.logger.warning('This is a WARNING message')
app.logger.error('This is an ERROR message')
## 参考
* [https://flask.palletsprojects.com/en/1.1.x/foreword/](https://flask.palletsprojects.com/en/1.1.x/foreword/)
## 资源
这篇文章的所有代码片段都可以在我的 [GitHub 页面](https://jimit105.github.io/medium-articles/Creating%20RESTful%20APIs%20using%20Flask%20and%20Python.html)上找到。
## 推荐阅读
[用 Flask、Flask-RESTPlus / Flask-RESTX 和 Swagger UI 构建 Python API](https://medium.com/p/7461b3a9a2c8)
## 让我们连接
领英:[https://www.linkedin.com/in/jimit105/](https://www.linkedin.com/in/jimit105/)
GitHub:[https://github.com/jimit105](https://github.com/jimit105)
推特:[https://twitter.com/jimit105](https://twitter.com/jimit105)
# 为数据科学项目创建可重用的代码
> 原文:<https://towardsdatascience.com/creating-reusable-code-for-data-science-projects-740391ec7bad?source=collection_archive---------26----------------------->
## 编写自己的库的路线图
作为有抱负的数据科学家,我们花费大量时间编写代码,但是从更大的角度来看,数据科学的核心不是编写代码,而是理解我们的数据并从中提取价值。编码部分只是完成这个目标的手段。显然,我们无法避免编写代码,这样做可能对过程有害,但是我们可以减少我们花在编写代码上的时间,这使我们能够将大部分时间花在制定策略以实现我们的主要目标上。
对我们大多数人来说,这个过程如下。我们倾向于编写小代码块,然后根据需要进行复制、粘贴和修改。这对于小任务来说是没问题的,但是对于大块的代码多次这样做可能会使你的笔记本杂乱无章,很难找到东西,并且更多的时候,在尝试不同的策略时会破坏笔记本。在多个不同的地方修改代码也是非常烦人的,再次浪费你的时间。
我们中的一些人定义了一些函数来解决这个问题,但是问题是每当我们创建新的函数时,我们必须不断地将这些函数复制到不同的笔记本中。此外,当我们在以后的项目中工作时,可能很难检索我们在早期项目中使用的函数,这使我们花费时间试图在我们所有的项目中找到它,然后只是放弃并再次写出一个自定义函数。

图片来自:[https://imgur.com/gallery/0BpqqmW](https://imgur.com/gallery/0BpqqmW)
# 级别 1 —输入。py 文件
这个问题的最佳解决方案是将您的代码定义在外部 py 文件的函数中。这样,您可以在任何需要的时候在任何笔记本中重用您的代码。你所需要做的就是导入那些函数,就像导入你最喜欢的库并使用它们一样。这使您的代码保持有组织,并专注于任务。不会有大块大块的代码分散你对主要任务的注意力,在你的笔记本上找东西会变得容易得多。当你移动到一个不同的项目时,你所需要做的就是将这些 py 文件复制到项目文件夹中,这样你就万事俱备了。(在文章的最后,我讨论了一些方法来帮助你避免这样做。)
现在,我们如何创建这些文件?让我们用一个简单的例子一步步来。
让我们创建一个名为 custom_mean()的函数,它接收一个列表,将所有值加 2,再乘以 2,然后返回*均值。当然,这对于任何真正有用的函数来说都太简单了,但是您可以根据自己的需要将它变成任何样子。
现在,打开一个文本编辑器,将函数复制到其中。在页面顶部,添加您已经用于自己的函数的任何导入函数。在我的例子中,我使用了 Numpy 的 mean(),所以我的文件看起来像这样。
import numpy as npdef my_mean(list):
list = np.array(list)
return np.mean((list + 2) * 2)
接下来,只需用您希望用一个*调用您的导入的名称来保存您的文件。py* 分机。我就把我的*叫做 custom_means.py* 。
现在把它导入你的笔记本,你可以这样做。
from custom_means import my_mean
现在让我们试着利用它。
list = [34, 54, 46, 57, 86, 34]
print('My Mean:', my_mean(list))# Output
My Mean: 107.66666666666667
这很容易。现在你如何把这带到下一个层次?
# 第 2 级—目录
随着项目的进展,您可以在文件中添加更多的函数。您还可以为 Scikit-Learn 之类的库创建定制类。你可以在这里学习如何做那个。
最终,当你写了更多的函数和类时,你会了解到,根据它们完成的任务的种类,在不同的文件中组织它们是很重要的,就像我们使用的流行的库一样。例如,您可能希望您的科学计算功能是 numpy 的一部分,而您的绘图实用程序是 matplotlib 的一部分。所以,对你的函数做同样的事情是明智的。
随着文件数量的增加,将所有文件放在一个或多个文件夹中会变得更加容易。现在,如果一个文件不在你正在工作的同一个文件夹中,你如何从这个文件中导入函数呢?答案是你一直在做却没有意识到的事情。我可以用上一个例子中的同一个文件来举例说明。让我们将它添加到一个名为 *utilities* 的文件夹中。
在我们的工作目录中,我们现在有了包含 *custom_means.py* 文件的 *utilities* 文件夹。为了导入相同的函数,我们现在可以输入。
from utilities.custom_means import my_mean
看起来很熟悉,不是吗?例如,当我们从 Scikit-learn 中导入 RandomForestRegressor 时,
from sklearn.tree import RandomForestRegressor
我们实际做的是访问 sklearn 安装中的树目录,然后从 *_classes.py* 文件中导入类。你可以在这里看一看。
请注意,这与我们上面的例子略有不同,但想法是一样的。这些差异是由于他们使用额外的方法来加速使用 Cython 的代码,这需要额外的文件。由于项目的规模,他们有一种更复杂但有效的方法来管理他们的文件。 *__init__。py* 文件自动告诉 python 每个类的代码应该查看哪个文件,而不需要我们明确地告诉它
from sklearn.tree._classes import RandomForestRegressor#Note: This produces an error, because of the way the code is #organized
你现在不必担心,但是把它作为将来某一天要达到的目标是很好的。
在文件夹中组织我们所有的自定义实用程序会非常有帮助。现在我们只需将文件夹复制到任何新项目的工作目录中,我们就完成了。随着时间的推移,您的工具在调整后变得更加完善,能够调用这些实用程序而不必每次都复制您的实用程序文件夹最终可能会很有用。
# 级别 3-Python 路径
现在让我们进入下一个阶段。除非你有一套不需要经常编辑的有用工具,否则不值得这么做。如果您的函数和类还没有完成,您可能应该继续将文件夹复制到新的项目目录中。但是现在让我们假设我们有一些完善的工具。
为了理解这一点,我们需要理解当您导入一个包时会发生什么。Python 首先检查你的工作目录,然后检查‘path’变量,看看你要导入的函数是否在那里。path 变量实际上是计算机中安装各种软件包的位置列表。您可以通过运行以下命令来查看您的路径变量
import sys
sys.path
我电脑上的输出如下。我使用的是 Linux 系统,它会根据你使用的操作系统而有所不同,但是不管你用的是什么电脑,这个过程都是一样的。
['/home/anupjsebastian/anaconda3/envs/my-env/lib/python37.zip',
'/home/anupjsebastian/anaconda3/envs/my-env/lib/python3.7',
'/home/anupjsebastian/anaconda3/envs/my-env/lib/python3.7/lib-dynload',
'',
'/home/anupjsebastian/anaconda3/envs/my-env/lib/python3.7/site-packages',
'/home/anupjsebastian/anaconda3/envs/my-env/lib/python3.7/site-packages/IPython/extensions',
'/home/anupjsebastian/.ipython']
如果您将 utilities 文件夹放在这些目录中的一个目录中,您将能够像访问您使用的所有其他包一样访问您的函数和类。我个人更希望是在
/home/anupjsebastian/anaconda3/envs/my-env/lib/python3.7
因为的其余部分主要是这个目录的子目录。
还有一种方法是将其放置在自定义位置。对于 Linux 和 Mac,在终端中键入以下内容
For Linux
nano ~/.bashrc# For Mac
nano ~/.bash_profile
并将下面一行添加到文件的末尾。
For Linux
export PYTHONPATH=/Your/path/here# For Mac
export PYTHONPATH="/Your/path/here"
对于 Windows,你可以在这里找到[。更多关于 Python 路径的细节可以在](https://www.architectryan.com/2018/08/31/how-to-change-environment-variables-on-windows-10/)[这里](https://bic-berkeley.github.io/psych-214-fall-2016/using_pythonpath.html)找到。
## 奖金水*
以简单优雅的方式做同样的事情的一个酷方法是使用 GitHub。你可以把你的代码放在 GitHub 仓库里,然后把它安装到你的电脑上。这确保了软件包会自动与 pip 安装的其他库一起放置在正确的位置。pip 安装 GitHub 存储库的代码如下。
pip install git+(weblink here)# For example pip install git+https://github.com/scikit-learn/scikit-learn
我在上面展示了一个关于如何从其 [GitHub](https://github.com/scikit-learn/scikit-learn) 库安装 scikit learn 的例子,但是我不建议你为 Scikit-learn 或任何专业软件包这样做,除非你知道你在做什么。但是,您可以为自己的包做这件事。
这就是全部内容——这是一个完整的路线图,说明如何通过编写可重用的代码和自动化您最终为每个新项目所做的大量繁琐过程,来逐渐减少您花费在编码上的时间并专注于手头的任务。
总之,首先在代码中编写更多的函数来完成重复的任务,然后将它们转移到单独的 py 文件中,这样它们就可以被很好地组织起来,并且很容易被利用。然后使用目录来管理您的 pip 文件。继续调整和开发您的可重用代码,在您认为它们已经可以在黄金时间使用之后,将它们放在一个您可以从任何地方轻松访问它们的位置。或许,你可以在 GitHub 上分享你的代码,并回馈给开源社区。
这篇文章包含了大量的信息,一次可能要吸收很多。我希望无论你处于学习的哪个阶段,这篇文章都对你有用。即使你不打算做此时此刻提到的每一件事,在你需要的时候回到这里也是有用的。
祝你好运!
# 金融和经济数据的数据管道
> 原文:<https://towardsdatascience.com/creating-smart-etl-data-pipelines-in-python-for-financial-and-economic-data-ad852e8daca7?source=collection_archive---------31----------------------->
## 从 Quandl API 中提取数据,转换并加载到 SQLite DB 中。用 python 动态编程,用 Matplotlib 和 Seaborn 动态加载图表。

构建 ETL 的输出日志
***前置要求*** : Python 3、SQLite、SQLiteStudio(可选)、Quandl 账号
***所需 Python 模块*** : Quandl、pandas、sqlite3、matplotlib、seaborn、numpy
这是一个相当直接的解决方案,只是说明了如何使用 python 和各种可用的模块来实现简单的 ETL。我已经包含了一些代码片段,来说明我是如何把它们拼凑在一起的。
这当然可以通过云*台来实现。然而,对于这个项目,我选择在本地保存和管理我的数据。这也是为云计算资源付费的一种替代方式。即使您可以设法获得免费的计算和存储,也肯定是在有限的时间或有限的能力范围内。在本地运行这将有助于我保持项目的活力。
我将从圣路易斯联邦储备银行的美联储经济数据中寻找一系列数据进行经济研究。包括来自耶鲁大学经济系的数据。所有这些都源自 Quandl 的 API。
我的方法项目的结构如下:通过创建用户定义的函数来处理每个阶段;提取,转换,加载,数据库查询和图表。
第一步是[下载](https://www.sqlite.org/index.html)并在本地机器上安装 SQLite。下一步是创建一个数据库。或者,您可以从命令行使用 SQLite 命令或通过 [SQLiteStudio](https://sqlitestudio.pl/) 创建一个数据库。这将作为数据库管理。

SQLStudioLite
## 构建基块—项目导入
import pandas as pd
import sqlite, quandl
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.dates
from datetime import date, datetime, timezone
import matplotlib.ticker as ticker
import matplotlib.patches as patches
from matplotlib import gridspec
import matplotlib.font_manager as fm
import matplotlib.gridspec as gridspec
import matplotlib.patches as mpatches
## 提取
创建连接到 API 的函数需要两个部分。首先,需要一个 Quandl 帐户。创建一个帐户后,就像收集一个 API 令牌一样简单,可以用它来访问数据。
def data_extract(pair: str, freq: str, key: str):
quandl.ApiConfig.api_key = key
return quandl.get(pair, collapse=freq)
## 改变
转换函数相当简单,因为在这个例子中,数据不需要提炼或清理。从 Quandl 的 API 返回的对象被转换成熊猫数据帧。出于健康原因,在将数据加载到 SQLite 数据库之前,列标题空格被替换为下划线。
def data_tranform(data) -> pd.DataFrame:
df = pd.DataFrame(data)
df.rename(columns=lambda x: x.replace(" ", "_"), inplace=True)
return df
## 负荷
这个阶段非常简单,模块 pandas 提供了到 SQL 数据库的基本连接。commit()函数将提交对数据库的更改。
def data_load(table_name: str, data: pd.DataFrame, conn):
data.to_sql(name=table_name.replace("/", "_"), con=conn, if_exists='replace') # update, append new values only, not full overwrite
conn.commit()
## 将 ETL 部分放在一个函数中
这个函数背后的想法是允许迭代不同表的列表,以提取、转换和加载到数据库中。
def ETL(table_collection: list):
global count
list_len=len(table_collection)
for pair in table_collection:
data = data_extract(pair, freq='daily', key=quandl_api_key)
print("..Loading data..")
print(pair+" extracted from API")data = data_tranform(data)
print(pair+" transformed")data_load(table_name=pair, data=data, conn=conn)
print(pair+" loaded into SQL db")count += 1
output = dict(count=count, list_len=list_len)
print("{count}..of..{list_len}".format(output))
## 主要功能
使用 main 函数执行 ETL,然后从 Quandl 中选择仪器进行分析。在主函数中,我通常只包含我想要执行的行为方面,例如,加载/刷新数据库中的数据集。或者在下一节中选择从我的数据库中提取数据到一个数据框架中,并通过一个函数绘制出来。
df = sql_to_df(conn, 'select MULTPL_SHILLER_PE_RATIO_MONTH.Date, MULTPL_SHILLER_PE_RATIO_MONTH.Value as SP500_PE_RATIO, NASDAQOMX_XQC.Index_Value as NASDAQ_Price from MULTPL_SHILLER_PE_RATIO_MONTH INNER JOIN NASDAQOMX_XQC ON MULTPL_SHILLER_PE_RATIO_MONTH.Date=NASDAQOMX_XQC."Trade Date"')
为了说明我迄今为止的解决方案,您必须知道或在 Quandl 的数据库中搜索指标的名称。此外,熟悉数据代表的内容和实际数据本身也将是一个优势。
## 制图
这是这个过程中更细致的部分,这里花了更多的时间来整合视觉效果。我已经创建了接受多个时间序列数据的函数。不管怎样,这是一个临时的解决办法;制作 Matplotlib 时间序列图,以及 Seaborn 进行相关分析。它被设计得看起来简单而专业,而且不太科学。下图显示了加载到数据库中的几个数据集的最终输出。

图 2—S&P500 市盈率和 Nasdaq100 的自定义时间序列图
上面的图 2 是对数据库的查询结果,该查询返回了一个新的数据帧,该数据帧可以传递给图表功能。用 NASDAQ100 得出 S&P500 的市盈率的时间序列图

图 3-M2V v 贸易加权美元
图 3 只是另一个将数据拉入数据库,然后用与上述相同的函数再次绘制的例子。
货币流通速度 M2 *-货币流通速度是在给定的时间内,一个货币单位被用来购买国内生产的商品和服务的频率。换句话说,就是单位时间内,一美元被用来购买商品和服务的次数。**【1】*
贸易加权美元指数代表许多对美元的外汇对,以表明美元的强势。
**注**:标注与轴标签重叠,需要修改。

图 M2V 和贸易加权美元之间的相关性
图 4 加载了与图 3 中相同的数据摘录。这里的想法是突出特征之间的任何相关性。应用 2 个不同样本大小的滚动相关性,右边的图显示了相关性随时间的变化。左边是 seaborn 图,显示了总的观察周期相关性。
## 然后
该项目的未来迭代计划。
* 我想过集成 python [Dash 库](https://plotly.com/dash/)的可能性。为这些图表创建基于 web 的仪表板。
* 我的目标是寻找更有用的抽象数据源,这些数据源可能与具体的金融资产直接或间接相关。我会在字里行间寻找来创建我自己的数据集。
* 将预测模型作为函数加载。(可能是 ARIMA 或 fbProphet)
* 项目自动化和内务管理正在进行中。
如果有任何问题或建议,请随时联系我。
*【1】。圣路易斯美联储银行,M2 货币存量速度[M2V],检索自圣路易斯美联储银行弗雷德;*[](https://fred.stlouisfed.org/series/M2V,)**2020 年 6 月 30 日。**
# 使用 mkDocs 在 10 分钟内创建软件文档
> 原文:<https://towardsdatascience.com/creating-software-documentation-in-under-10-minutes-with-mkdocs-b11f52f0fb10?source=collection_archive---------46----------------------->
## 为什么以及如何为您的项目快速创建专业文档
当你从事任何项目时,文档都是非常有用的,甚至是至关重要的。幸运的是,mkDocs 创建了一个很好的、高效的方法来创建看起来既专业又易于使用的文档。
# 首先,什么是文档,为什么它很重要?
软件文档是关于你的代码和项目的书写,解释它是关于什么和它是如何工作的。这对开源项目、团队项目,甚至个人项目都很重要。
通过记录您的代码,您可以:
1. 让你的代码对你合作的其他人来说是可解释的(当你回头看你的代码时,对你自己来说也是)。
2. 跟踪软件的所有方面。
3. 使以后的调试更容易。
4. 可以有一个通用的空间来保存你的文件。
用 mkDocs 创建文档的框架最多只需要 10 分钟。现在花 10 分钟编写文档,值得花无数分钟努力调试代码,并在以后向同事和自己解释代码。
# mkDocs 设置
要使用 mkDocs,您需要 pip。你可以在这里找到如何安装 pip [的说明。如果您有 pip,请确保更新它,然后安装 mkDocs。当你安装 mkDocs 的时候,你也应该选择一个主题(查看选项](https://pip.pypa.io/en/stable/installing/)[这里](https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes))。在这个例子中,我们选择了材质主题。
pip install --upgrade pip
pip install mkdocs
pip install mkdocs-material
现在您已经准备好创建您的文档了。运行下面的命令,但是将 PROJECT_NAME 替换为您的项目名称。
mkdocs new PROJECT_NAME
cd PROJECT_NAME
您应该会看到一个名为`mkdocs.yaml`的文件和一个名为`docs`的文件夹。该文件夹将有一个单一的降价文件,`index.md`。
要运行文档,使用`mkdocs serve`,然后在浏览器中转至`[http://127.0.0.1:8000/](http://127.0.0.1:8000/)`。

运行 mkdocs serve 后,在 https://127.0.0.1:8000/会看到类似这样的东西。
打开`mkdocs.yaml`,您应该会看到以下内容:
site_name: My Docs
我们将编辑这个文档。首先,我们将创建一个通用的大纲;随意填写占位符变量。主题是我们过去安装的 pip。
site_name: NAME
nav:
- Home: index.md
- Page2: page2.md
- Section1:
- Subpage1: subpage1.md
- Subpage2: subpage2.md
theme:
name: THEME_DOWNLOADED
例如,假设我想为一个游乐园模拟创建文档。这是我将在`mkdocs.yaml`中写的内容:
site_name: Amusement Park Simulation
nav:
- Home: index.md
- About: about.md
- Games:
- "Ping Pong": games/ping.md
- Balloon: games/balloon.md
- Rides:
- "Scary Coaster": rides/scary.md
- "Drop of Doom": rides/drop.md
theme:
name: material
注意,`mkdocs.yaml`中提到的所有文件夹和目录都在`docs`目录中。这是我的结构看起来的样子:
PROJECT_NAME/
docs/
index.md
about.md
games/
ping.md
balloon.md
rides/
scary.md
drop.md
mkdocs.yaml
Add the rest of your code here
如果你不想要几个文档,也可以用 markdown 头语法创建类别。如果你不熟悉。md 文件,你可以在这里了解更多的语法[。](https://www.markdownguide.org/basic-syntax/)

只有大纲的最终版本。我的下一步是用内容填充我的减价文件!
# 最后,部署。
最后,我们将在 GitHub 页面上托管我们的文档。只需运行`mkdocs gh-deploy`。它应该在您的存储库中创建一个新的分支,在`USERNAME.github.io/REPOSITORY_NAME`托管您的站点。
就是这样!您已经成功地为项目创建了文档。如果您想了解如何进一步定制您的文档或其他 mkDocs 选项,请访问他们的网站[此处](https://www.mkdocs.org/)。点击这里查看我的 GitHub 资源库中的教程[,点击这里](https://github.com/GenericP3rson/mkDocs)查看已部署的网站[。](https://genericp3rson.github.io/mkDocs/)
# 调试需要注意的事项
* 确保您选择使用制表符或空格。mkDocs 不允许两者的组合。
* 如果名称中有空格,请添加引号。
* 如果你得到一个 404 错误,这意味着你可能丢失了一个文件。检查`docs`以确保你的文件存在。
* 请注意,组织不支持 GitHub 页面。
# 使用无监督学习创建 Spotify 播放列表
> 原文:<https://towardsdatascience.com/creating-spotify-playlists-with-unsupervised-learning-9391835fbc7f?source=collection_archive---------36----------------------->
## 聚类在创建推荐中的实际应用

由[马尔特·温根](https://unsplash.com/@maltewingen?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
Spotify 提供的播放列表并不短缺。现在在我的主页上,我看到的播放列表有:Rap Caviar,Hot Country,Pump Pop,以及其他各种音乐类型。
虽然许多用户喜欢浏览歌曲,并根据自己的口味创建自己的播放列表,但我想做一些不同的事情。我使用了一种无监督学习技术来寻找密切相关的音乐,并创建自己的播放列表。
该算法不需要对每首歌曲进行分类,也不需要每个播放列表都完美无缺。相反,它只需要产生我可以审查和创造性命名的建议,节省了我研究不同流派歌曲的时间。
# 数据集
Spotify 的 Web API 允许开发者访问他们庞大的音乐库。因此,从 1921 年到 2020 年的* 170,000 首歌曲的数据被收集并在 [Kaggle](https://www.kaggle.com/yamaerenay/spotify-dataset-19212020-160k-tracks) 上发布。这些数据涵盖了几乎所有的音乐类型,包括晦涩和流行的歌曲。
数据集中的每首歌曲都被几个关键的音乐指标所分解。 [Spotify](https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-features/) 自己定义了每一项措施,但简单来说:
* 声学:一首歌的声学程度。带有柔和钢琴和小提琴的歌曲得分较高,而带有扭曲吉他和尖叫的歌曲得分较低。
* 可舞性:一首歌对舞池的适合程度,基于速度、节奏稳定性、节拍强度和整体规律性。有感染力的流行歌曲得分较高,而呆板的古典音乐得分较低。
* 能量:一首歌给人的感觉有多强烈和活跃。硬摇滚和朋克得分较高,而钢琴民谣得分较低。
* 工具性:一首曲目的工具性如何。纯器乐歌曲得分较高,而口语和说唱歌曲得分较低。
* 活跃度:这首歌在观众面前录制的可能性有多大。
* 响度:音轨的音量。
* 声音:一首歌的声音有多大。没有音乐和只有口语的歌曲得分较高,而器乐歌曲得分较低。
* 效价:一首歌听起来有多积极或快乐。欢快的歌曲得分较高,而悲伤或愤怒的歌曲得分较低。
* 节奏:一首歌曲的速度,单位为每分钟节拍数(bpm)。
# 清理数据
虽然深入分析很有吸引力,但是需要进行一些数据清理。所有工作都将在 python 中完成。
import pandas as pd
import numpy as np# read the data
df = pd.read_csv("Spotify_Data.csv")
在做任何事情之前,熊猫和 NumPy 库被导入。最后一行只是将之前保存的 CSV 文件转换为 DataFrame。
from re import search# Writes function to tell if string is degraded beyond recognition
def is_data_corrupt(string):
# Search for a name with letters and numbers. If no numbers or letters are found, returns None object
found = search("[0-9A-Za-z]", string)
# Return 1 if corrupt, 0 if not
return 1 if found == None else 0
在数据收集过程中的某个时刻,大量文本被破坏。因此,一些艺术家和歌曲名称被列为不可理解的文本字符串,例如“‘a’和‘e’”。尽管我很想知道‘a’的最热门歌曲,但我无法搜索和编译这些值。
为了过滤损坏,编写了一个名为 is_data_corrupt()的函数,它使用正则表达式来检查字符串是否包含数字、大写字母或小写字母。它会将任何只包含标点符号或特殊字符的字符串标记为损坏,这应该可以找到有问题的条目,同时保留合法的歌曲和艺术家姓名。
Create a helper column for artist corruption
df["artists_corrupt"] = df["artists"].apply(lambda x: is_data_corrupt(x))# Create helper column for name corruption
df["name_corrupt"] = df["name"].apply(lambda x: is_data_corrupt(x))# Filter out corrupt artists names
df = df[df["artists_corrupt"] == 0]# Filter out corrupt song names
df = df[df["name_corrupt"] == 0]
在将函数 is_data_corrupt()应用于 artists 和 name 列以创建两个新的辅助列之后,任何标记为损坏的文本字符串都会被过滤掉。
值得注意的是,这只标记了被降级到无法识别的文本。一些文本仍然包含部分降级。比如著名作曲家弗雷德里克·肖邦就被改成了“弗雷德里克·肖邦”。更广泛的数据清理纠正了这些条目,但是这些方法超出了本文的范围。
Gets rid of rows with unspecified artists
df = df[df["artists"] != "['Unspecified']"]
很大一部分艺术家没有被列出,而是被赋予占位符值“未指定”。由于未列出的艺术家给查找歌曲带来了不必要的困难(毕竟歌曲名称不是唯一的),这些也将被过滤掉。
Filter out speeches, comedy routines, poems, etc.
df = df[df["speechiness"] < 0.66]
最后,纯声乐曲目,如演讲,喜剧特辑,诗歌朗诵,提出了一个问题。因为分类是基于声音特征的,所以它们会聚集在一起。
不幸的是,由罗斯福总统的炉边谈话和有线电视员拉里的例行公事组成的播放列表是一种非常糟糕但有趣的收听体验。声道必须根据内容进行分类,这超出了本数据的范围。
因此,我简单地过滤了所有“speechiness”值超过 0.66 的音轨。虽然我可能过滤了一些歌曲,但移除这些曲目的回报是值得的。
# 数据探索
即使在运行无监督模型之前,在数据中寻找有趣的模式也能为如何继续提供见解。所有的可视化都是用 seaborn 完成的。
sns.heatmap(df.corr(), cmap = "coolwarm")

数据中所有相关系数的热图。图由作者制作。
上面的热图显示了不同的数字数据之间的紧密联系。深红色显示强烈的积极关系,深蓝色显示强烈的消极关系。一些有趣的相关性值得一提。
不出所料,流行度和年份之间存在着密切的关系,这意味着越*的音乐越受欢迎。在喜欢流媒体的年轻观众和积极推广新音乐的 Spotify 之间,这种洞察力证实了领域知识。
另一个不足为奇的趋势是,响度与能量相关。直觉上,高能歌曲辐射强度,往往伴随响度而来。
声学有一些有趣的负面关系。它与能量和响度的反比关系抓住了大多数人对民谣的印象。然而,原声歌曲往往不太受欢迎,其数量也随着时间的推移而减少。
虽然令人惊讶的是钢琴和原声吉他曲目顽固地保留在公众意识中,但这一见解更多地说明了失真吉他和合成器在音乐制作中的受欢迎程度。在它们出现之前,每首歌都被认为是原声的,它们的市场份额被现代音乐夺走了。
# 光学聚类
为了创建播放列表,我使用了 Scikit-Learn 的 OPTICS clustering 实现,它本质上是通过数据找到高密度的区域,并将它们分配到一个簇中。低密度区域的观察是不分配的,所以不是每首歌曲都会出现在播放列表中。
df_features = df.filter([
"accousticness",
"danceability",
"energy",
"instramentalness",
"loudness",
"mode",
"tempo",
"popularity",
"valence"
])
在运行算法之前,我提取了想要分析的列。大多数功能都是纯粹基于音乐的,除了流行,流行是为了帮助像艺术家一样的群体在一起。
请注意,光学聚类使用基于欧几里得距离来确定密度,没有添加太多列,因为高维数据会扭曲基于距离的度量。
from sklearn.preprocessing import StandardScaler# initialize scaler
scaler = StandardScaler()# Scaled features
scaler.fit(df_features)
df_scaled_features = scaler.transform(df_features)
接下来,数据被转换为标准比例。虽然其余的功能已经在 0 和 1 之间标准化,但速度和响度使用不同的标度,这在计算距离时会扭曲结果。使用标准定标器,一切都达到相同的比例。
Initialize and run OPTICS
ops_cluster = OPTICS(min_samples = 10)
ops_cluster.fit(df_scaled_features)# Add labels to dataframe
df_sample["clusters"] = ops_cluster.labels_
最后,在数据集上运行光学。请注意参数 min_samples,它表示创建一个聚类所需的最小观察次数。在这种情况下,它规定了制作播放列表所需的最少歌曲数量。
将 min_samples 设置得太小会创建许多只有几首歌曲的播放列表,但将其设置得太高会创建几个有很多歌曲的播放列表。选择 10 个是为了达到合理的*衡。
还要注意,数据集仍然相当大,因此运行算法需要时间。在我的情况下,我的计算机在返回结果之前工作了几个小时。
# 结果
如前所述,我想要一个系统来推荐播放列表,我可以手动检查,以节省自己搜索不同流派艺术家的时间。不是每首歌都需要分类,播放列表也不需要完美。
虽然还有很大的改进空间,但光学满足了我的期望。按照一个总的主题将歌曲分组,我发现了一组有趣的播放列表,涵盖了我一无所知的流派。
不幸的是,大多数歌曲没有聚集在一起,这意味着该算法失去了大量的音乐多样性。我尝试了不同的聚类方法(DBSCAN 和 K-Means),但是我得到了相似的结果。很简单,数据不是很密集,所以基于密度的方法从一开始就是有缺陷的。
然而,播放列表本身通常会提出有趣的建议。虽然偶尔会提出一些奇怪的组合(例如,流行的电子舞曲艺术家蠢朋克发现自己也是古典作曲家),但他们仍然很中肯。因此,通过这个项目,我发现了新的艺术家,也学到了很多关于音乐的知识。
这就是无监督学习的神奇之处。
# 播放列表
虽然我可以写不同的指标来评估播放列表的有效性,但我认为没有比互联网的严峻考验更好的评判标准了。我稍微编辑了一些我最喜欢的,并鼓励任何人听并做出自己的判断。请注意,有些歌曲可能包含明确的歌词。
* [重击节拍](https://open.spotify.com/playlist/0Yk0yqeagGmuFRiRKFLZ1t?si=jwYiWkQ_TrWGepiQO9hkSg):嘻哈音乐中紧凑的歌词和砰砰的基线
* [合成糖](https://open.spotify.com/playlist/1WTPROLNJn5rHoMDyL1jSn?si=r0pd8kKrQP2-WjDDm-oRSQ):一碗五颜六色的流行歌曲
* [快节奏硬摇滚](https://open.spotify.com/playlist/34dMKv4kMMwKPxKNtE9thK?si=xCZc5mooTX24GYj8IfkIyA):当朋克和硬摇滚打在你的脸上
* 弦乐组:小提琴和大提琴之夜
# 结论
虽然还有改进的空间,但 OPTICS 集群满足了要求,并创建了一组多样化的有趣播放列表。考虑到基于密度的方法的问题,我会用层次聚类、余弦相似性或其他方法来克服数据的稀疏性。
然而,更广泛地说,这展示了无监督学习在发现甚至人类都难以量化的模式方面的力量。虽然因为需要审查而不是完全自动化,但使用这种算法展示了人类和机器如何合作创造更令人愉快的最终产品。
# 在 python 中创建利益相关者友好的点状图
> 原文:<https://towardsdatascience.com/creating-stakeholder-friendly-dot-plots-e9e9daae9124?source=collection_archive---------45----------------------->

图片来源:[quantlabs.net](https://quantlabs.net/blog/2017/04/intro-to-scientific-python-with-math-behind-it-matplotlib/)
一个美丽的数据可视化胜过千言万语。受我去年看到的[大卫·斯皮格哈尔特](https://en.wikipedia.org/wiki/David_Spiegelhalter)的演讲的启发,我创建了一个易于使用的 python 包来创建视觉上令人惊叹的点状图。
***编者按:*** [*走向数据科学*](http://towardsdatascience.com/) *是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击* [*这里*](https://www.who.int/emergencies/diseases/novel-coronavirus-2019/situation-reports) *。*
# 可视化新冠肺炎症状
由于全球疫情目前影响着世界的每一个部分,我认为使用新冠肺炎相关数据集是及时的,也是有益的。Kaggle 有这样一个数据集,名为“[新型冠状病毒 2019 数据集](https://www.kaggle.com/sudalairajkumar/novel-corona-virus-2019-dataset)”,其中有许多关于冠状病毒患者的不同信息,包括他们表现出的症状。
你可能听说过很多冠状病毒的主要症状是发烧和干咳,但在这个数据集中它们有多普遍呢?下面是我试图用一种非常容易理解的方式来形象化症状频率。

## 解释点状图
可视化是一个 10×10 的点网格,每个点都有颜色,代表 100 个人中的一个。整整一排代表十分之一的人。与条形图相比,这种可视化人群的方法的好处在于,你不需要担心百分比,也不需要担心非技术型观众一提到它就会走神。我们可以说 10 个人中有 1 个,或者 100 个人中有 1 个,这样更直观。
右手边的标签(由于选择了点的布局)有助于传递顶级信息,因为一行代表十分之一的人。例如,可以清楚地看到*发烧*标签横跨 5 行,表明 10 个人中有 5 个人的唯一症状是发烧。更简单的方法是将发烧和咳嗽这两行加在一起,得出 10 个人中有 7 个人发烧,而不必将 47%和 26%的真实百分比加在一起。
## 顶级带回家
1. 十分之八的人患有 T4,要么是咳嗽,要么是发烧
2. 十分之二的人既不咳嗽也不发烧
3. 1/10 的人会咳嗽**但** **不会发烧**
4. 十分之七的人会发烧
5. 100 个人中有 26 个人既咳嗽又发烧
# 自己生成一个图很简单
我在 matplotlib 中创建了这个可视化,并在这个 [GitHub repo](https://github.com/mattcrooksphd/Medium-Dot-Plot) 中将代码包装成一个易于使用的 python 包。在这篇文章中,我将介绍如何生成图的基础知识,但是有一个 jupyter 笔记本会介绍更多的细节。还有从完整的 [Kaggle 数据集](https://www.kaggle.com/sudalairajkumar/novel-corona-virus-2019-dataset)中提取的特定数据集,用于上述可视化。
## 导入包
我们可以使用下面的代码行导入这个包。注意,如果代码不在您的工作目录中,您将需要使用相对路径。

## 基本情节
我们可视化所需的数据集以字典的形式提供。字典的关键字是每个类的标签,值是该类的人数。百分比是自动计算的。

这个包还需要一个类标签列表,这样它就知道绘制类的顺序。这是以列表的形式提供的,第一项在图的底部,最后一项在顶部。

该包的最基本实现只需要以下内容

这产生了下面的图

## 选择颜色
您可以通过将颜色选择字典传递给关键字参数`color_dict`来指定您自己的颜色。这些可以被命名为颜色或 rgb 颜色。

## 添加标签
默认情况下,标签是关闭的。这允许您在布局上工作,而不必担心如果布局不适合包可能会抛出的一些错误。我将在后面讨论这些错误。为了打开标签,我们可以进入`ignore_labels=False.`

这里使用的标签与`class_labels`中使用的标签和`colour_dict`中的按键相同。
## 标题和题注
同样,标题和题注可以通过将它们的文本作为两个额外的关键字参数传入来添加。

## 修改布局
为了给图形加标签,你需要在右栏下面每种颜色至少有一个点。尽管在本例中这是默认情况,但并不总是这样。您可以做一些修改来改变每一行的顺序。这些被传递到关键字参数`*reversed_rows.*`
> D **默认:** `*reversed_rows=None*` *(或左空)*为所有行从左到右
>
> **Snake:** 传入`reversed_rows='snake'`将每行的顺序与第一行左右、第二行左右、第三行左右等互换
>
> **反转行:**传入一个列表,比如`*reversed_rows=[1, 3]*` *,只交换第二行和第四行从右向左运行,其他行从左向右运行。并不是说这些是从 0 开始的 python 索引。*
交换行也是有用的,这样标签就代表了每一个类中 10 个人的数量。
尝试传入`*reversed_rows=[7]*`来获取错误

## 保存数字
该类具有属性`f`和`axis`,它们是标准的`matplotlib`图形和轴句柄。您可以使用这些选项随意修改图,但尤其可以使用以下选项保存图形

## 更改布局
不是每个数据集都最适合 10x10 的网格。关键字参数`gridsize`可以用来告诉代码使用多少个点。它应该是一个元组,第一个元素是水*点数,第二个元素是垂直点数。例如:

在较小的图中,您可能会得到以下错误

这意味着当把每一类的百分比四舍五入到最接*的点数时,你得到的点数太多了。例如,如果我们尝试生成一个 2 x 5 的图,每个点将值 10%,我们的类将四舍五入到最接*的 10%:
*发热* : 47%到 50%
*发烧和咳嗽* : 26%到 30%
*咳嗽* : 10%到 10%
*都不是* : 17%到 20%
这些加起来是 110%,这是误差的基础。如果需要,您可以通过自己提供所需的类百分比来克服这个问题。

## 最佳实践布局
为了获得最大的视觉效果,有一些关于安排点的布局的最佳实践。以下是我在制作上图时所做的选择:
1. 按大小降序排列类别
2. 重点关注发烧和咳嗽症状,其中*和*这两个类别都不代表不属于其他三个类别的人。该类别出现在顶部,并以中性灰色显示
3. 将*发烧和咳嗽*放在*发烧*和*咳嗽*之间,很像维恩图
4. 将标签上的*发烧*四舍五入至 5/10,将*发烧和咳嗽*四舍五入至 2/10。我希望 100 个人中总共有 73 个人用 7 行来表示。47 比 26 比 30 更接* 50。
# 结论
我已经提供了一个演练和代码,让你用 python 制作出你自己的漂亮的点状图。这里又是 GitHub 回购的链接。尽情享受吧!
# 从头开始创建 Streamlit 仪表板。
> 原文:<https://towardsdatascience.com/creating-streamlit-dashboard-from-scratch-59316a74fa1?source=collection_archive---------22----------------------->
## Streamlit 是一个很棒的工具,可以轻松构建视觉上吸引人的仪表板。

马库斯·温克勒在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
构建仪表板从来都不容易,创建仪表板需要 bootstrap、HTML、CSS 等知识和大量的时间。但是通过使用 streamlit,我们只需几行代码就可以创建高度交互式和视觉上吸引人的仪表板。让我们开始构建我们的控制面板,但首先让我们了解一下 streamlit。
# 什么是 Streamlit?
**Streamlit** 是一个开源的 Python 库,其速度快得惊人,可以轻松地为机器学习和数据科学构建漂亮的定制网络应用。这是一个非常棒的工具,只需要一些 python 知识就可以创建高度交互式的仪表板。我们将从安装 streamlit 开始,看看它是如何工作的。
**安装 Streamlit**
pip install streamlit
查看 streamlit 上的一些演示。为此,我们需要在命令提示符下运行下面给出的命令。此命令允许您浏览预加载的 Streamlit 演示。你一定要看看这些,因为它们真的很有趣。
streamlit hello
现在让我们创建自己的仪表板。在这里,我将创建一个仪表板,用于分析 2019 年 5 月至 2020 年 5 月印度股市的 5 大赢家和输家。为了构建它,让我们导入一些我们需要的库。请注意,您需要为 streamlit 创建一个脚本并运行它,因此我们将使用任何代码编辑器,如 Atom 或 Notepad++并使用。py 格式。
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import matplotlib.pyplot as plt
所有这些库将用于股票数据的数学计算和可视化。现在让我们使用 pandas 导入我们的数据,这里我为赢家和输家创建了单独的文件,所以我们将导入这两个文件。
DATA_URL = ("C:/Users/Divya/gainers.csv")
DATA_UR= ("C:/Users/Divya/losers.csv")
df=pd.read_csv(DATA_URL)
df1=pd.read_csv(DATA_UR)
导入数据文件后,让我们开始设置仪表板的标题。为此,我们将使用 **st.title** 作为主标题,使用 **st.sidebar.title** 作为侧栏标题,如下所示。
st.title("Share Price analysis for May 2019 to May 2020:")
st.sidebar.title("Share Price analysis for May 2019 to May 2020:")
st.markdown("This application is a Share Price dashboard for Top 5 Gainers and Losers:")
st.sidebar.markdown("This application is a Share Price dashboard for Top 5 Gainers and Losers:")
您可以保存此文件并在 streamlit 中运行它来查看更改。您将看到带有您提到的标题的仪表板。我将我的文件存储为 share_new.py,因此我将通过键入下面给出的命令来运行。
streamlit run share_new.py

这是您的仪表板最初的外观。
streamlit 最好的一点是它非常快,也就是说,你可以保存脚本中的更改,应用程序会立即反映这些更改,你不需要一次又一次地刷新应用程序。
在这个仪表板中,我们将为数据集中的所有股票创建蜡烛图。此外,我们将绘制这些股票的移动*均线。首先创建收益者的标题,并创建一个选择框来选择要分析的股票。
st.sidebar.title("Gainers")
select = st.sidebar.selectbox('Share', ['Adani Green Energy', 'GMM Pfaudler', 'AGC Networks', 'Alkyl Amines Chem', 'IOL Chem & Pharma'], key='1')
一旦保存文件,更改将会反映在您的应用程序中。

在这里,您可以看到选择框显示了标题增益。
现在,我们将编写代码来创建一个复选框,以启用或禁用蜡烛图的可视化,并为所有股票创建一个蜡烛图。下面给出的代码将创建“阿达尼绿色能源”的蜡烛图,还将计算和显示该份额的移动*均线。
if not st.sidebar.checkbox("Hide", True, key='1'):
st.title("Gainers")
if select == 'Adani Green Energy':
for i in ['AdaLow', 'AdaHigh', 'AdaClose', 'AdaOpen']:
df[i] = df[i].astype('float64')
avg_20 = df.AdaClose.rolling(window=20, min_periods=1).mean()
avg_50 = df.AdaClose.rolling(window=50, min_periods=1).mean()
avg_200 = df.AdaClose.rolling(window=200, min_periods=1).mean()
set1 = { 'x': df.AdaDate, 'open': df.AdaOpen, 'close': df.AdaClose, 'high': df.AdaHigh, 'low': df.AdaLow, 'type': 'candlestick',}
set2 = { 'x': df.AdaDate, 'y': avg_20, 'type': 'scatter', 'mode': 'lines', 'line': { 'width': 1, 'color': 'blue' },'name': 'MA 20 periods'}
set3 = { 'x': df.AdaDate, 'y': avg_50, 'type': 'scatter', 'mode': 'lines', 'line': { 'width': 1, 'color': 'yellow' },'name': 'MA 50 periods'}
set4 = { 'x': df.AdaDate, 'y': avg_200, 'type': 'scatter', 'mode': 'lines', 'line': { 'width': 1, 'color': 'black' },'name': 'MA 200 periods'}
data = [set1, set2, set3, set4]
fig = go.Figure(data=data)
st.plotly_chart(fig)
类似地,我们可以为数据集中列出的所有份额创建烛台图表,但是重复上面提到的代码,最终的仪表板将显示数据集中提到的所有份额。

显示所有功能和共享的最终仪表板。
视频显示了最终创建的仪表板,烛台图表是使用 Plotly 创建的,因此它们是交互式的,移动*均线可以相应地启用或禁用。这个仪表板在不到两个小时的时间内就创建好了,具有高度的交互性和视觉吸引力。
这只是 streamlit 所能做的一个例子。您可以探索更多信息,了解 streamlit 为创建网络应用和仪表盘提供的无限功能。如果您在创建自己的仪表板时遇到任何困难,请回信并与我分享您的经验。
[](/creating-dataset-using-faker-and-use-it-for-pandas-profiling-6fe26e1b9557) [## 使用 Faker 创建数据集并将其用于熊猫概况分析
### 创建您自己的数据并对其执行操作。
towardsdatascience.com](/creating-dataset-using-faker-and-use-it-for-pandas-profiling-6fe26e1b9557)
# 在你走之前
***感谢*** *的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的* [***LinkedIn 个人资料***](http://www.linkedin.com/in/himanshusharmads) *。也可以在我的*[***Github***](https://github.com/hmix13/streamlit_candlestick)*中查看我在这里使用过的代码和数据集。另外,请随意浏览* [***我的简介***](https://medium.com/@hmix13) *并阅读我写的与数据科学相关的不同文章。*
# 为深度学习创建合成 CT 数据
> 原文:<https://towardsdatascience.com/creating-synthetic-ct-data-for-deep-learning-2dca1e43c4f0?source=collection_archive---------61----------------------->
## 当数据太少而无法训练 GAN 时,如何生成逼真的体积图像
# TL;速度三角形定位法(dead reckoning)
我们描述了一种从一小组样本中创建合成体积医学图像的方法。我们的方法基于随机部分变形,因此无需深度学习(不需要 GANs)。创建的卷看起来非常真实,并且适合于创建用于深度学习的训练数据集。在我们的 [Covid19 胸部 CT 挑战赛](https://www.covid19challenge.eu/)中,我们应用这种方法为开发者创建了一个合成玩具数据集。
**数据隐私是公众质疑医学影像数据的一个重要方面。**患者相关信息的匿名化需要两个主要步骤。第一步是从可识别信息中剥离患者数据。这包括患者的姓名、出生日期、出生地或现居住地。在第二步中,可能需要对图像数据本身进行匿名化。一个突出的例子是从脑 CT/MRI 图像重建人脸的可能性。作为进一步的匿名化步骤,这通常需要去面。
在我们的[**covid 19 挑战赛**](https://www.covid19challenge.eu/) 中,我们处理胸部的放射影像数据。幸运的是,这个数据不像脑成像数据那样敏感,因为病人的头和脸都被裁剪了。剩余的图像数据本身不包含与人相关的信息,因为胸部成像是现实的抽象表示。如果没有公共数据库,去识别信息只有主要护理人员知道。
在我们的[covid 19 挑战](https://www.covid19challenge.eu/)中,我们采取了**多种措施来确保完全匿名化**。我们从合作的放射部门和私人诊所接收预先匿名的数据。在数据传输之后,任何剩余的元数据都将被剥离到最少的一组挑战相关信息。其余的临床元数据不是特定于患者的(年龄、性别、PCR 结果、入院后天数、结果类别、临床事件)。事实上,它可能与许多潜在的医院和患者有关。接下来,受过医学教育的训练团队将图像分割成肺叶和 Covid19 相关的病变。根据预定义的分段协议,这种注释集中发生,并具有后续的质量保证步骤,以确保数据的一致和高质量标记。
由此产生的数据是高度匿名的,但仍然没有向公众公布。由于道德和法律要求,**参赛团队在挑战的任何时候都不能直接访问未经更改的成像数据**。相反,开发者可以通过 [Eisen.ai](https://eisen.ai/) 接口[提交](https://medium.com/analytics-vidhya/deep-learning-on-covid-19-data-with-eisen-7ad5b5657ff9)处理作业,在非公开的图像数据上训练和验证方法。
然而,**作为数据科学家,我们更希望至少有一个最小的本地代表性数据集**可用。这有助于对数据外观和可变性、潜在挑战以及算法的快速原型化有所了解。为了弥合这一差距,我们需要设计一种方法来发布用于本地开发的图像子集,这种子集具有高质量和代表性,并且仍然不包含患者的未更改图像数据。实现这一点的一个非常有前途的研究方向是生成神经网络模型,特别是生成[对抗网络(GANs)](https://arxiv.org/abs/1809.07294) 。然而,这种方法需要来自图像域的非常大的数据集,以便学习数万或数十万幅图像的真实外观。这在医学成像中通常很难实现。此外,这些方法仅在 2D 显示出产生逼真的效果。图像尺寸大约为 512-1024 像素边长。此外,基于 GAN 的方法需要[巨大的计算资源](https://www.fastcompany.com/90244767/see-the-shockingly-realistic-images-made-by-googles-new-ai)用于训练。由于很少带注释的图像、高分辨率的 3D 体积(512 x512 x300–500 体素)以及准备阶段有限的计算能力,**基于 GAN 的方法在我们的挑战中不可行**。
相反,我们依赖于一种更传统的技术: [**测地线插值**](https://github.com/stnava/Morpheus) **经由可变形图像配准**。这个过程的一个更常见的表达是**“图像变形”**。基本想法很简单:我们使用一个强大的、现成的非线性图像配准工具包,用于医学图像,名为 [ANTs](http://stnava.github.io/ANTs/) 。将 ant 应用于我们的胸部 CT 图像,我们将“移动”体积配准到“固定”体积。一旦变形被计算出来,我们不会将“移动的”体积一直变形到“固定的”体积,而是只变形一定的百分比。变形因此没有完成,而只是*部分*。这个概念的一个例子如图 1 所示。

图一。通过部分图像变形生成合成胸部 CT 的概念。(*作者图片*)
由于对可变形配准进行了仔细的参数化,因此**生成的体积看起来非常逼真**,无论是在健康组织还是在病变区域。连同原始图像数据一起,所有黄金标准分割标签和患者元数据被变形(标签)和/或内插(年龄、入院后天数等。).**生成的体积具有完全合成的形态:合成体积中的解剖形状和尺寸与“固定”和“移动”体积非线性不同**。因此,胸部的生物标记(如果存在的话,例如椎骨形状或脊柱弯曲)也是非线性改变的和合成的。示例图像如图 2 所示。

图二。通过四个胸部 CT 体积的中心冠状切片。你可能猜到哪个卷是真实的,哪个是合成的。(*底部解决方案)(*作者图片*)
从这个玩具数据集中恢复原始卷应该是不可能的。为了确保这一点,**我们应用了三种形式的随机化**:首先,玩具数据集是从全部数据的*随机选择的子集*中生成的。第二,考虑到成对配准的全连通有向图,我们仅沿着边缘的随机子集执行可变形配准*。第三,从源到目标的部分变形也被设置为一个*随机百分比*。请记住,源数据本身在任何时候都是不可公开访问的,**开发者数据集中的合成卷不再与任何原始源数据相关联**。*
当然,这种方法有一定的局限性。其中包括:
* **拓扑误配准:**微分同胚配准不能处理数据中的拓扑变化。例子可以是支气管路径的不同分支,或者具有不规则形状和位置的 Covid19 损伤。拓扑差异导致配准错误,这表现为合成体积中的拖尾或压缩伪影。
* **插值伪影:**由于合成体积中的体素强度是通过插值计算的,因此与原始数据相比,图像看起来有些模糊。
* **样本外内插:**用零值内插被共同配准到目标体积的体素网格之外的区域中的运动体积的体素。我们用空气等效体素强度来修补这些体素。
无论如何,所有这些伪像通常发生在神经网络训练的增强期间。考虑到参与团队的本地开发的便利性,这些工件是一个可以接受的折衷。
用合成数据创建一个可公开访问的玩具数据集是挑战准备阶段的一个重要里程碑。我们希望有了这些数据,开发者可以更容易地在本地构建他们方法的原型,同时了解 Eisen 接口。一个[艾森代码初学者工具包](https://gist.github.com/faustomilletari/1c1d9d671641e36e63199d26bb232d58)刚刚推出。试一试,并关注我们的团队成员 [Fausto Milletari](https://medium.com/u/a5d79f344c19?source=post_page-----2dca1e43c4f0--------------------------------) 的更新。
(*)解答:没有一个体积是真实的,所有四个体积都是合成的。事实上,所有四卷都是从相同的源主题合成的,共同注册到四个随机选择的目标主题,证明了可以实现的形态学可变性。
下面,图 3 示出了源和目标体以及合成体的另一个更详细的例子(在两者之间正好 50%的变形/变形)。

图 3。随机选择的源、目标和 50%变形合成体积的详细比较。(*图片作者*)
# 用 Matplotlib 创建 Synthwave
> 原文:<https://towardsdatascience.com/creating-synthwave-with-matplotlib-ea7c9be59760?source=collection_archive---------14----------------------->
## 正确使用 Matplotlib 创建动画复古 Synthwave 视觉效果

synthwave 是一种非常独特的音乐类型,灵感来自 20 世纪 80 年代的怀旧,是技术人员的共同最爱。我喜欢它,我发现它的艺术风格非常迷人。
在 YouTube[上快速搜索该类型可以让任何人欣赏该类型带来的复古科幻美学。](https://www.youtube.com/watch?v=wOMwO5T3yT4)
现在,我想创造这样的视觉效果。然而,我日复一日地与数据打交道,我不是动画师、*面设计师或艺术家。
然后我想,*“我确实创造了视觉效果,我在 Matplotlib 中可视化数据。在 Matplotlib 中创建 Synthwave 视觉效果不是很有趣吗?”*。
所以我们在这里。
# 远景
首先要创建的是透视样式的垂直网格线。为此,我们设置一个原点`(0, 5)`。这些线必须从这里延伸到框架底部的`y = -50`处。每一行唯一要更改的值是 Numpy linspace 函数中的最终 x 值。我们用一个从`x = -500`到`x = 500`的 for 循环来实现,步长为`50`。

现在,没有地*线,没有地*线你不可能有一条无尽的 synthwave 路。所以我们简单地用`np.ma.masked_where(y > 0, y)`屏蔽掉`0`以上的所有 y 值。
最后,让我们来确定配色方案。我们将使用黑色背景,并使用多条半透明线来创建发光效果[1]。

# 移动
这个有点棘手。为了创建看起来向我们走来的水*线,我们使用 Matplotlib 动画来不断更新水*线的 y 位置。我们创建了这些运动线的十个实例,每个实例都分配了一个修改后的指数函数,如下所示:

分别用于第 0、4 和 8 行的 y 位置函数。
对于每一帧,每条运动线被分配相同的 x 值。但是,当我们沿着 x 轴移动每个函数时,我们返回一个不同的 y 值。如果我们画出所有十条运动线,记住这个逻辑,我们可以通过时间(x 轴)可视化线的 y 位置(y 轴):

通过使用指数函数,我们将运动线和透视线合二为一。当运动线离我们越来越*时,通过增加向下的速度来创造三维运动的幻觉。这类似于动画中的缓和[2]。
将此应用于水*网格线的 y 位置,会给我们一种在霓虹紫色 tron 般的世界中不断前进的错觉。

幸运的是,对于我们的输出文件的大小,我们的运动线运动每十分之一的总时间重复一次。这意味着我们可以在动画函数`animation.FuncAnimation(fig, animate, frames=int(frames/10))`中将动画帧减少 10 帧。
# 迈阿密太阳报
如果在我们无尽的霓虹紫色之路的尽头没有超大的复古日落,这就不是 Synthwave 了。
当涉及到渐变时,Matplotlib 可能有点困难。我们使用`imshow()`来创建一个图像,在我们的例子中是`plasma`渐变。然后,我们遮蔽该图像,使其超出中心点的指定半径,得到:

很好,但还没到那一步。迈阿密的太阳需要辉光和几条水*线。对于光晕,我们放置了几个半径稍大、alpha 值较低的圆。线条是在使用简单的黑线图后添加的。

将迈阿密的太阳和霓虹网格放在一起,我们得到:

# 目的地
无尽霓虹紫路需要目的。一个遥远但不太遥远的目的地。想想——一个神秘的都市天堂。
幸运的是,Matplotlib 附带了一个专门构建的复古 skyline 生成器,名为`plt.bar()`!我们简单地使用`np.random.uniform(0, 10)`,通过一些计算来定义酒吧宽度,我们有自己美丽的,随机生成的天际线。

由于我们无法在无云的迈阿密夜空下看到我们的地*线轮廓,我们添加了一个从我们无尽的地*线发出的深紫色光芒。我们将再次使用`plt.imshow()`。我们需要的是初始的`gnuplot`渐变,所以让我们使用前 28 个颜色映射并用`[ListedColormap](https://matplotlib.org/3.1.0/tutorials/colors/colormap-manipulation.html)`创建一个新的渐变。

来自 Matplotlib 的 [**gnuplot** 颜色图参考](https://matplotlib.org/3.1.0/gallery/color/colormap_reference.html)
我还擅自减少了我们的霓虹网格线宽,我认为现在看起来好多了。让我们看看我们的无尽之路是什么样子的:

看起来不错,但现在我们过大的迈阿密日落可能太大了,挤压了我们对远处地*线的完美看法。另外,80 年代迈阿密的天空总是布满星星,而我一颗也没看到。
因此,让我们调整太阳的大小,用`plt.scatter()`和`np.random.uniform()`分别代表`x`和`y`来添加星星。我们还根据 y 位置(越靠*地*线越暗)和一点随机性来改变每颗星星的`alpha`参数。

# 收尾
现在有一些小的调整,我认为将完成可视化。
首先,恒星出现在太阳前面。我们简单地调整恒星的`ax.scatter()`函数中的`zorder`参数。将它们移动到太阳下面——默认为`zorder = 1`【3】。
在这一点上,它看起来很好,但星星只是普通的白点,不是很有说服力。所以我们添加了一些随机生成的闪烁。
最后,就像地*线后面放射出的光芒一样。我们添加另一个紫黑色渐变。这一次沿着我们无尽的路。
就是它,Matplotlib 中的 Synthwave!

虽然这没有明显的用例,但我强烈推荐尝试创建类似的东西,因为在这个过程中你会学到很多东西。而且,说实话,很好玩!
我希望你和我一样喜欢阅读这篇文章。
如果您有任何问题或建议,请随时通过 [Twitter](https://twitter.com/jamescalam) 或在下面的评论中联系我们。
谢谢,
# 参考
[1]: **创建照明线**—[https://public wiki . delta RES . nl/display/~ baart _ f/2012/01/03/创建+照明+线](https://publicwiki.deltares.nl/display/~baart_f/2012/01/03/Creating+illuminated+lines)
[2]: **放松的基础**—[https://developers . Google . com/web/fundamentals/design-and-UX/animations/The-basics-of-easing](https://developers.google.com/web/fundamentals/design-and-ux/animations/the-basics-of-easing)
【3】:**佐德演示**——[https://matplotlib.org/3.1.1/gallery/misc/zorder_demo.html](https://matplotlib.org/3.1.1/gallery/misc/zorder_demo.html)
**项目回购**—[https://github.com/jamescalam/python_synthwave](https://github.com/jamescalam/python_synthwave)
**合成波**——[https://www.youtube.com/watch?v=wOMwO5T3yT4](https://www.youtube.com/watch?v=wOMwO5T3yT4)
# 用 Python 制作生态学经典“风筝图”
> 原文:<https://towardsdatascience.com/creating-the-ecology-classic-kite-diagram-in-python-46989e1310ad?source=collection_archive---------41----------------------->
## 使用 Python 的 matplotlib 库

手绘风筝图(图片由作者提供)
风筝图是生态学和生物学研究中的经典用法,也是英国 A 级生物学课程教学大纲的一部分。尽管如此,在标准软件可视化软件包中创建这些图表的选项很少,大多数似乎仍然是手绘的。这篇短文将解释如何用 Python 3 和 matplotlib 库来自动化这个过程。
**那么什么是风筝图呢?**风筝图提供了沿横断面进行的不同观察的图形总结。横断面是横跨栖息地的一部分或整个栖息地的一条线。这通常是用绳子、绳索等手工完成的。各种物种的数量可以沿着样带定期计算。各种物种的分布会受到各种不同因素的影响,包括捕食者以及其他环境因素,如热、光和湿度。这些被称为非生物(非生命)因素。也可以使用样方收集数据,这涉及到使用沿样带移动的正方形(例如 1m2)框架。然后可以在每一点计算方块中的物种数量。
风筝图是一种观察不同物种在一个样带中数量变化的方式。
这使得研究人员可以在栖息地的不同地方,比如海边,看到某些物种的相对丰富度。例如,可能有许多种类的草、植物和昆虫分布在海岸的不同位置。

沿着海岸可以发现各种各样的物种
这些图表通常是手工制作的,在标准的可视化软件包中似乎很少有对它们的支持。我们在 Excel 中找到了一个例子(Luke,2019)和一个使用 R 产生的例子(Hood,2014),但没有使用 Python 的例子。随着 Python 越来越多地被用于分析数据,我们认为应该尝试使用 Python 实现一个简单的风筝图。这是使用 Python 3 的交互式 Jupyter 笔记本完成的。我们在这里向其他领域的人展示这个过程,这些人可能会发现自动化这样的图是有用的。我们特意尝试保持初学者的基本实现。
例如,我们将使用 Python 的“pandas”库来表示我们将使用的数据集。该数据集被输入 Excel 并保存为逗号分隔值(CSV)文件。我们还将使用 numpy 库从数据集中提取列,并对它们应用操作。按照 Python 的惯例,我们可以使用简写引用来引用这些库(pd 代表 pandas,np 代表 numpy)。
import pandas as pd
import numpy as np
**导入数据**
接下来,我们使用 pandas read_csv()函数将数据集加载到 pandas dataframe 对象中,并提供 csv 文件的路径。我们将这些数据存储在一个名为 kite_data 的变量中,然后可以在笔记本(或其他 Python 环境)中查看该变量。
kite_data = pd.read_csv("./biology/kitedata.csv")
kite_data

数据集(作者提供的图片)
数据的第一列应该代表距离。这将用于水*轴。其余的列表示物种的频率,或者有时表示某些植物的覆盖百分比,它们将在 y 轴上以一定的间隔绘制。
**创建风筝绘图功能**
下一步是创建一个函数来生成风筝图。这建立在 matplotlib 库的基础上,matplotlib 库被广泛用于生成各种各样的可视化。你可以在 https://matplotlib.org/3.1.1/gallery/index.html[的画廊里看到一些例子。我们将导入该库,并将其称为 plt。我们还需要使用多边形函数在图上画出风筝的形状。](https://matplotlib.org/3.1.1/gallery/index.html)
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
导入库后,我们可以创建函数。数据框的第一列应该是区域分割的距离或其他度量(如样方)。这里我们使用 iloc 特性,它代表整数位置。这是一种通过数字(0 到列数)而不是通过列名来引用列的方式。我们可以提取并存储第一列距离,供以后在 x 轴上使用。我们还创建了一个空列表来存储起始点。这用于在 y 轴上定位单个风筝形状。我们还获取列名,并将其存储在 y_values 变量中,以便稍后在 y 轴上绘制物种名称。最后,我们获得数据帧中的列数,并将其存储在名为 num_cols 的变量中,这样我们就知道需要向图表中添加多少物种。
def kite_diagram(df, axis_labs):
"""Function to draw a kite diagram."""
plt.axes()
start_points = []
v1 = np.array(df.iloc[:, [0]])
y_values = df.columns
y_values = np.delete(y_values, 0)
num_cols = len(df.columns) - 1
现在我们需要从数据集(不包括距离列)中获取最大值,这样我们就可以在图上以足够的垂直间距放置风筝形状。我们希望将不同的风筝图隔开最大的距离,这样它们就不会相互重叠,因为这会使它们不可读。为此,我们获取除第一列(距离)之外的所有列,然后使用 max()函数来确定列中的最大值。
df_cols = df.iloc[:, 1:len(df.columns)]
max_val = max(df_cols.max())
因为 Python 使用从 0 开始的索引系统,所以我们选择 1 作为列数(列的长度),以排除存储在位置 0 的第一个(距离)列。接下来,我们将数据中的最大值存储在名为 max_val 的变量中。除了第一列之外的每一列都应该代表不同的物种,所以我们需要循环遍历每一列,并为每一个物种制作一个风筝形状。由于 Python 从 0 开始索引,我们将从 1 开始跳过距离列。
for j in range(1, num_cols + 1):
p1 = []
p2 = []
p1 和 p2 列表将存储每个物种的多边形(风筝形状)的坐标点。有两个列表,因为该图本质上显示了基线上方和下方相同形状的镜像,如下图所示。这是通过将每个值减半并投影一对点来实现的,其中一个值在水*基线之上,另一个值在水*基线之下,这样每对点之间的距离代表原始总值。例如,值 8 将比基线高 4 个单位,比基线低 4 个单位。

风筝多边形形状在基线的上方和下方(图片由作者提供)
为了对此进行编码,我们需要获得每个数据值的中点。这通过将每个值除以 2 来实现。我们可以用 numpy 轻松做到这一点。我们可以将每个列的值转换成 numpy 数组。然后,我们可以将数组中的每个值除以 2。
v2 = np.array(df.iloc[:, [j]]) / 2
应该注意的是,使用标准 Python 列表,操作不能应用于整个列表。下图说明了这一点,显示了当我们试图将列表除以 2 时出现的错误。

尝试将 Python 列表除以 2 以将列表中的每个值减半时出错(图片由作者提供)
但是,如果我们使用 numpy 数组,该操作将应用于列表中的所有值:

使用 numpy 数组,我们可以成功地对数组的所有成员执行操作(图片由作者提供)
接下来,我们要确定这是否是我们添加到图表中的第一个风筝,如果是,我们要将垂直基线定位在数据集中找到的最大值的一半处,这样我们就有足够的空间在基线上方和下方绘制所需的图案。对于所有其他随后的风筝模式,我们将把数据集中的最大值添加到先前的起点,以使它们在垂直方向上均匀分布。
if j == 1:
start_point = max_val / 2
else:
start_point = start_point + max_val
我们还将每个物种基线的起点存储在一个列表中,以便以后标记该地块。此外,我们将多边形的第一个点(线的上方和下方)设置为零,这样当我们开始绘制形状时就不会有任何间隙。我们在整个形状的两端都这样做,以防止不必要的间隙。
start_points.append(start_point)
p1.append([0, start_point])
p2.append([0, start_point])
**生成风筝形状的点**
对于所有随后的点,我们将循环遍历所有的值,并对线以上和线以下的值加上或减去我们计算并存储在 v2 变量中的一半值。上面和下面的图案应该是相同的,所以我们将上面的线点和水*距离(v1)存储在一个名为 p1(多边形 1)的变量中,将线下的点和水*距离(v1)存储在一个名为 p2(多边形 2)的变量中。最后,在检查完所有的值后,我们给两个多边形添加一对额外的值,使线回到起点。同样,这避免了形状末端的图案中的任何间隙。
for i in range(0, len(v1)):
p1.append([v1[i], start_point + v2[i]])
p2.append([v1[i], start_point - v2[i]])p1.append([v1[i], start_point])
p2.append([v1[i], start_point])
我们最终得到的是一系列坐标点。每对中的第一个是 x 轴上的位置(水*位置),在我们的示例中从 0 到 20。p1 中的第二个数字是图上基线(垂直 y 轴)上方的位置,而在 p2 中是基线下方的位置:
p1 = [[0,0],[2,0],[4,0],[6,0],[8,1.5],[10,2],[12,4],[14,4],[16,3.5],[18,2.5],[20,2],[20,0]]
p2 = [[0,0],[2,0],[4,0],[6,0],[8,-1.5],[10,-2],[12,-4],[14,-4],[16,-3.5],[18,-2.5],[20,-2],[20,0]]
**将形状添加到绘图中**
我们现在可以使用 Polygon()函数使用这些点来创建多边形并将其添加到绘图中。
c = np.random.rand(3,)
l1 = plt.Polygon(p1, closed=None, fill=True, edgecolor=c, alpha=0.4, color=c)
l2 = plt.Polygon(p2, closed=None, fill=True, edgecolor=c, alpha=0.4, color=c)
我们为每个风筝形状分配一个随机颜色,并将该值存储在一个名为 c 的变量中。我们使用 matplotlibs 的 Polygon()函数创建了两个多边形,并将它们存储在变量 l1 和 l2 中。第一个参数是数据点(p1 或 p2),接下来我们设置一些其他可选参数,我们希望形状被填充,所以我们设置为真,我们为边缘添加颜色。在这种情况下,我们使用与整个形状相同的颜色。可以调整 alpha 值来增加形状的透明度。如果形状中有任何重叠,或者只是为了使颜色不那么强烈,这将有所帮助。最后,我们添加填充颜色。
现在可以使用 add_line()函数将多边形添加到绘图中。函数的作用是:获取或者创建一个当前的坐标轴。
plt.gca().add_line(l1)
plt.gca().add_line(l2)
**收尾工作**
最后,在循环所有列并添加了物种风筝形状后,我们可以在主循环后为整个情节添加额外的功能。
plt.yticks(start_points, y_values)
plt.xlabel(axis_labs[0])
plt.ylabel(axis_labs[1])
plt.axis('scaled')
plt.show();
回想一下,我们将列名(物种)添加到一个名为 y_values 的变量中,我们可以使用 yticks()函数将这些名称添加到我们存储的 start_points 位置,以便将物种名称与基线对齐。接下来的两行添加了 x 轴和 y 轴的标签,我们将它们传递给一个列表中的函数。“轴缩放”选项改变绘图容器的尺寸,而不是数据限制。另一个可以使用的选项是“相等”,这样 x,y 点具有相等的增量。最后,show()函数将呈现绘图。
最后,为了绘制图表,我们需要调用在列表中提供数据集和 x/y 轴标签的函数。
kite_diagram(kite_data, ['Distance', 'Species']);
此输出可以在手绘版本旁边并排看到:

左,手绘的图;右,用 kite_diagram()函数生成的图(图片由作者提供)
完整功能的代码如下所示:
def kite_diagram(df, axis_labs):
"""Function to draw a kite diagram."""
plt.axes()
start_points = []
v1 = np.array(df.iloc[:, [0]])
y_values = df.columns
y_values = np.delete(y_values, 0)
num_cols = len(df.columns) - 1
df_cols = df.iloc[:, 1:len(df.columns)]
max_val = max(df_cols.max())
for j in range(1, num_cols + 1):
p1 = []
p2 = [] v2 = np.array(df.iloc[:, [j]]) / 2
if j == 1:
start_point = max_val / 2
else:
start_point = start_point + max_val start_points.append(start_point)
p1.append([0, start_point])
p2.append([0, start_point])
for i in range(0, len(v1)):
p1.append([v1[i], start_point + v2[i]])
p2.append([v1[i], start_point - v2[i]]) p1.append([v1[i], start_point])
p2.append([v1[i], start_point]) c = np.random.rand(3,)
l1 = plt.Polygon(p1, closed=None, fill=True, edgecolor=c, alpha=0.4, color=c)
l2 = plt.Polygon(p2, closed=None, fill=True, edgecolor=c, alpha=0.4, color=c) plt.gca().add_line(l1)
plt.gca().add_line(l2) plt.yticks(start_points, y_values)
plt.xlabel(axis_labs[0])
plt.ylabel(axis_labs[1])
plt.axis('scaled')
plt.show();
Python 通过 matplotlib、seaborn、ggplot 等库提供了各种各样的可视化。这些库也可以很容易地扩展,以添加额外的可视化类型,如这里所见。这为多个科学领域的科学可视化提供了丰富的基础。支持数据科学的现代语言,如 R 和 Python,鼓励了这种可视化的发展,提供了使这种绘图相对容易实现的工具。
**参考文献**
[1] Luke,K (2019)最佳 Excel 教程:风筝图[在线]。访问时间:2020 年 7 月 7 日【https://best-excel-tutorial.com/56-charts/267-kite-chart
[2] Hood,D(2014)RPubs:R 中的风筝图[在线]。访问时间:2020 年 7 月 7 日【https://rpubs.com/thoughtfulbloke/kitegraph
感谢维多利亚·戈拉斯,她为这篇文章的写作做出了贡献,并提供了图表的手绘版本。
# 在 python 中使用单词云创建排版
> 原文:<https://towardsdatascience.com/creating-typography-using-word-cloud-in-python-9652bd62fa69?source=collection_archive---------32----------------------->
***一图抵千言。***

这是我系列的第一篇博客——使用计算机视觉和深度学习来增强摄影。
> 21 世纪创造价值的最佳方式是将创造力与技术相结合——史蒂夫·乔布斯
相机最*最大的进步来自人工智能,而不是传感器和镜头。在过去的几年里,技术使得摄影技术取得了惊人的进步。人工智能正在改变我们拍摄照片的方式和编辑照片的方式。
随着“计算机视觉”成为自动驾驶汽车等其他新技术的重要组成部分,人工智能在解释和理解我们图像的内容方面将变得越来越复杂。
作为一名热情的摄影师,我总是努力将手动任务自动化,以便我可以专注于创作创意内容。在这个项目中,我将讨论如何通过几个简单的步骤,使用您自己的图像创建和定制 word cloud。
**目标:使用 python 中的 word cloud 将照片转化为排版艺术。**

> 一张图胜过千言万语。字面意思!这张图有 2200+字。*😱*
字体设计:它是一种以视觉上吸引人的方式排列文字的艺术。它旨在引发特定的情感并传达特定的信息。
在某个时间点,人们实际上是把字母和字符放在物理空间中。在这个项目中,我将展示我们如何利用 python 中的 word cloud 的力量来使这种艺术形式可扩展,并在几分钟内创建它。
**单词云:**单词云是一种用于表示文本数据的数据可视化技术,其中每个单词的大小表示其频率或重要性。词云被广泛用于分析来自社交网络网站的数据以进行情感分析。
为了在 Python 中生成单词云,需要的模块有— matplotlib、OpenCV 和 word cloud。
以下是涉及的步骤:
1.相关数据收集(网络搜集)
2.数据清理和自然语言处理(NLP)
3.从图像创建遮罩并生成单词云
1. **相关数据收集:**
在这个项目中,为了获得摄影领域最流行的单词列表,我从一个流行的摄影网站 KelbyOne 上删除了 836 个摄影课程标题(例如,高级人像编辑技术)。我使用 python 模块 Scrapy 从 70 页中删除了数据。这些课程从 2006 年开始上传。
**2。文本预处理:**
我使用 python 模块“Spacy”来执行自然语言处理(NLP)
* ***标记化***
它是将字符串拆分成其组成标记的过程。这些标记可以是单词或标点符号。
课程名称:“什么是闪光?控制你的光线”
代币:["什么","那个","闪光","什么?"、“控制”、“你的”、“光”]
* ***词汇化***
将 word 转换成其基本形式:
例如,像 reducing、reduces、reduced、reduction 这样的词将被转换为 reduce。
* ***文字清理技巧:***
删除不必要的空格、标点符号、特殊字符(数字、表情符号等)。)和非字母符号(如 D750)
* ***停用词***
出现频率极高且对句子没有多大意义的单词。
例如冠词(a,the 等)。),be 动词(是,am 等。)、代词(他、她等。)
经过处理,我们在数据中总共有 3558 个单词和 1133 个唯一单词,数据中的所有单词都用于创建单词云。
在词云中,最频繁出现的词更突出(频率越高,字体越大)。

**3。从图像中创建遮罩并生成单词云**
我在 photoshop 中创建了两张图片的蒙版,并在每张蒙版中分别应用了文字云。文本填充蒙版的黑色部分。每个面具都充满了所有的 1100+独特的话。
**掩 1 字云:**
我让蒙版 1 的背景为黑色,文字为白色,以突出主体和纪念碑的拱门。

所有 3558 个单词的文本字符串(其中 1133 个单词是唯一的)在 wordcloud 函数中传递。
**代码片段:**
import cv2
from wordcloud import WordCloud
import matplotlib.pyplot as plt#White text Black Background
image = cv2.imread("D:/Photography/Typography/mask1.jpg", 1)
wordcloud = WordCloud(background_color='black', mask=image, mode="RGB", color_func=lambda *args, kwargs: "white",
width=1000 , max_words=100, height=1000, random_state=1).generate(text)fig = plt.figure(figsize=(25,25))
plt.imshow(wordcloud, interpolation='bilinear**')
plt.tight_layout(pad=0)
plt.axis("off")
plt.show()
我创建了多个单词云,并保存了最好的一个。更改参数' **random_state** '的值会生成不同的输出。
您还可以通过更改参数' **max_words** '的值来自定义单词云中的单词数。
参数'**插值=双线性**'用于使图像看起来更*滑。
**掩二字云:**
我为蒙版 2 保留了白色的背景和彩色的文字,以增加排版的细节和趣味。

**代码片段:**
#Colored text white Background
image = cv2.imread("D:/Photography/Typography/mask2.jpg", 1)
wordcloud = WordCloud(background_color='white', mask=image, mode="RGB", max_words=1200, width=1000 , height=1000, random_state=2).generate(text)fig = plt.figure(figsize=(25,25))
plt.imshow(wordcloud, interpolation='bilinear')
plt.tight_layout(pad=0)
plt.axis("off")
plt.show()
我将每个单词云与掩码结合起来,得到了以下结果:

在 photoshop 中合并后的最终结果。

单词 cloud 可以以任何分辨率生成,这使得它非常适合在大尺寸上打印。我在探索如何将诗歌或故事中的词语按顺序插入,这样艺术会更有意义。在我随后的系列博客中,我将谈论艺术风格的转移,3D 图像修复,以及更多。
我在我的摄影作品中应用了这种技术,结果令人惊讶!

华泰!

世界上最小的汽车😜

📸🧕🕌正确的视角让不可能成为可能✨

史上最长镜头!一个好的摄影师知道站在哪里——安塞尔·亚当斯
感谢您的阅读!我希望你喜欢这篇文章。如果你想了解我的文章,请跟我来。

我已经分享了图片(我保留本文中使用的所有图片的权利,它们是由我拍摄的)和面具,以便您可以自己进行实验。
链接:[https://drive.google.com/open?id = 13 cf 8 vna 9 fc 0 vgeh 7h 9 zarxradgtjl 2 ou](https://drive.google.com/open?id=13cf8Vna9Fc0VgEh7H9ZarXRadgTJL2OU)
***阅读我的其他博客:***
[](/art-with-ai-turning-photographs-into-artwork-with-neural-style-transfer-8144ece44bed) [## 人工智能艺术:用神经风格转换将照片变成艺术品
### 有没有希望自己能像毕加索或梵高一样画画?
towardsdatascience.com](/art-with-ai-turning-photographs-into-artwork-with-neural-style-transfer-8144ece44bed) [](/country-wise-visual-analysis-of-music-taste-using-spotify-api-seaborn-in-python-77f5b749b421) [## 使用 Spotify API 和 Python 中的 Seaborn 对音乐品味进行国别可视化分析
### 你知道哪个国家喜欢欢快的音乐,哪个国家喜欢喧闹的音乐吗?
towardsdatascience.com](/country-wise-visual-analysis-of-music-taste-using-spotify-api-seaborn-in-python-77f5b749b421)
参考资料:
[https://www . ka ggle . com/aa shita/word-clouds-of-variable-shapes](https://www.kaggle.com/aashita/word-clouds-of-various-shapes)
[https://www . data camp . com/community/tutorials/word cloud-python](https://www.datacamp.com/community/tutorials/wordcloud-python)
[https://www . plural sight . com/guides/natural-language-processing-visualizing-text-data-using-word-cloud](https://www.pluralsight.com/guides/natural-language-processing-visualizing-text-data-using-word-cloud)
[https://amueller.github.io/word_cloud/generated/wordcloud.WordCloud.html](https://amueller.github.io/word_cloud/generated/wordcloud.WordCloud.html)
# 使用张量流从零开始创建 VGG
> 原文:<https://towardsdatascience.com/creating-vgg-from-scratch-using-tensorflow-a998a5640155?source=collection_archive---------12----------------------->
## 我们将看到如何使用 Tensorflow 2.0 从头开始实现 VGG16

图一。VGG 16 号建筑(来源:图片由作者创作)
LeNet-5 是最古老的卷积神经网络架构之一,由 Yann LeCun 于 1998 年设计,用于识别手写数字。它使用 5x5 过滤器,*均池,没有填充。但按照现代标准,这是一个非常小的神经网络,只有 6 万个参数。如今,我们看到的网络有一千万到几十亿个参数。下一个革命性地使用卷积网络的大型卷积神经网络是 AlexNet,它有大约 6000 万个参数。AlexNet 第一层使用 96 个内核大小为 11x11 的滤镜,步长为 4。下一层使用 3x3 滤镜,依此类推。此外,AlexNet 使用最大池和填充,这在 LeNet-5 中没有使用。AlexNet 与 LeNet-5 非常相似,但它要大得多。还有,AlexNet 用的是 ReLU 激活功能,而 LeNet-5 主要用的是 Sigmoid 激活。这些网络的共同点是,随着我们深入网络,张量的大小不断减小,而通道的数量不断增加。此外,如今在创建神经网络架构时仍在使用的另一个趋势是使用卷积层(一层或多层),然后是一些池层,最后是一些完全连接的层。
下一个大的卷积神经网络是 VGG 网络。关于 VGG,值得注意的是,作者没有使用这么多超参数,而是使用了一个更简单的网络,其中重点是使用具有小尺寸 3×3 滤波器的卷积层,步长为 1,并使用“相同”填充,并使所有 MaxPooling 层 2×2 的步长为 2。VGG 大大简化了以前制作的神经网络结构。
[https://arxiv.org/abs/1409.1556](https://arxiv.org/abs/1409.1556)VGG 纸链接
**使用 Tensorflow 的 VGG 16 架构和实现:**

图二。VGG 建筑。用红色突出显示的 VGG 16 号(来源:图片来自原始论文)
图 2 显示了所有的 VGG 架构。VGG 16 号的建筑用红色突出显示。图 1 给出了该架构的一个简单版本。
VGG 网络使用最大池和 ReLU 激活功能。所有隐藏层使用 ReLU 激活,最后一个密集层使用 Softmax 激活。MaxPooling 是在步长为 2 的 2x2 像素窗口上执行的。
VGG 16 有 5 个卷积块和 3 个全连接层。每个块由 2 个或更多卷积层和一个最大池层组成。
**算法:**
1. 导入所有必要的层
2. 为卷积块编写代码
3. 为密集层编写代码
4. 建立模型
**导入库:**
# import necessary layers
from tensorflow.keras.layers import Input, Conv2D
from tensorflow.keras.layers import MaxPool2D, Flatten, Dense
from tensorflow.keras import Model
**输入:**
# input input = Input(shape =(224,224,3))
输入是 224x224 RGB 图像,所以 3 个通道。
**Conv 第一街区:**
它有两个 Conv 层,每个层有 64 个过滤器,后面是最大池。

# 1st Conv Block
x = Conv2D (filters =64, kernel_size =3, padding ='same', activation='relu')(input)
x = Conv2D (filters =64, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
**Conv 第二街区:**
它有两个 Conv 层,128 个过滤器,然后是最大池。

# 2nd Conv Block
x = Conv2D (filters =128, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =128, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
**Conv 第三街区:**
它有三个 Conv 层,256 个过滤器,然后是最大池。

# 3rd Conv block x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
**Conv 第 4 和第 5 区块:**
Conv 区块 4 和 5 都有 3 个 Conv 层,512 个过滤器,然后是最大池。

# 4th Conv block
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
# 5th Conv block
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
**密集层:**
有 3 个完全连接的层,前两层具有 4096 个隐藏单元和 ReLU 激活,最后一个输出层具有 1000 个隐藏单元和 Softmax 激活。

# Fully connected layers x = Flatten()(x)
x = Dense(units = 4096, activation ='relu')(x)
x = Dense(units = 4096, activation ='relu')(x)
output = Dense(units = 1000, activation ='softmax')(x)
**创建模型:**
# creating the model
model = Model (inputs=input, outputs =output)
model.summary()
输出:

**绘制模型:**
# plotting the model
from tensorflow.python.keras.utils.vis_utils import model_to_dot
from IPython.display import SVG
import pydot
import graphviz
SVG(model_to_dot(model, show_shapes=True, show_layer_names=True, rankdir='TB',expand_nested=False, dpi=60, subgraph=False).create(prog='dot',format='svg'))
输出片段:

# 用 TensorFlow 实现 VGG 16 的完整代码:
# import necessary layers from tensorflow.keras.layers import Input, Conv2D from tensorflow.keras.layers import MaxPool2D, Flatten, Dense from tensorflow.keras import Model# input
input = Input(shape =(224,224,3))# 1st Conv Block
x = Conv2D (filters =64, kernel_size =3, padding ='same', activation='relu')(input)
x = Conv2D (filters =64, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)# 2nd Conv Block
x = Conv2D (filters =128, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =128, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)# 3rd Conv block
x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)# 4th Conv block
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
# 5th Conv block
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu')(x)
x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)# Fully connected layers
x = Flatten()(x)
x = Dense(units = 4096, activation ='relu')(x)
x = Dense(units = 4096, activation ='relu')(x)
output = Dense(units = 1000, activation ='softmax')(x)# creating the model
model = Model (inputs=input, outputs =output)
model.summary()
**结论:**
VGG 网络是一个非常简单的卷积神经网络,由于其简单性,使用 Tensorflow 很容易实现。它只有 Conv2D、MaxPooling 和 Dense 图层。VGG 16 共有 1.38 亿个可训练参数。
VGG 是 CNN 出版期间最深的模型架构,最多有 19 个重量层。它在 ImageNet 挑战中取得了最先进的性能,并表明更深的网络有利于更好的分类准确性。
**参考文献:**
1. 卡伦·西蒙扬和安德鲁·齐泽曼,用于大规模图像识别的极深度卷积网络,[arXiv:1409.1556 V6](https://arxiv.org/abs/1409.1556v6)【cs .CV],2015。
# 创建单词嵌入:使用深度学习在 Python 中编码 Word2Vec 算法
> 原文:<https://towardsdatascience.com/creating-word-embeddings-coding-the-word2vec-algorithm-in-python-using-deep-learning-b337d0ba17a8?source=collection_archive---------1----------------------->
## 用深度学习理解单词嵌入创作背后的直觉

当我在写另一篇展示如何在文本分类目标中使用单词嵌入的文章时,我意识到我总是使用从外部来源(例如[https://nlp.stanford.edu/projects/glove/](https://nlp.stanford.edu/projects/glove/))下载的预训练单词嵌入。我开始思考如何从头开始创建单词嵌入,因此这就是这篇文章的诞生。我的主要目标是让人们通过我的代码片段阅读这篇文章,并深入理解创建单词的向量表示背后的逻辑。
完整的代码可以在这里找到:
https://github.com/Eligijus112/word-embedding-creation
单词 embeddings 的创建的简短版本可以概括为以下流程:
阅读文本 **- >** 预处理文本 **- >** 创建(X,Y)数据点 **- >** 创建一个热编码(X,Y)矩阵 **- >** 训练一个神经网络 **- >** 从输入层提取权重
在这篇文章中,我将简要解释每一步。
来自 wiki: **单词嵌入**是一组[自然语言处理](https://en.wikipedia.org/wiki/Natural_language_processing) (NLP)中的[语言建模](https://en.wikipedia.org/wiki/Language_model)和[特征学习](https://en.wikipedia.org/wiki/Feature_learning)技术的统称,其中来自词汇表的**单词或短语被映射到实数向量。**术语 word2vec 字面翻译为**字到矢量**。举个例子,
“爸爸”= [0.1548,0.4848,…,1.864]
"妈妈" = [0.8785,0.8974,…,2.794]
单词嵌入最重要的特征是语义上相似的单词之间的距离(欧几里德距离、余弦距离或其他距离)比没有语义关系的单词之间的距离要小。例如,像“妈妈”和“爸爸”这样的词应该比“妈妈”和“番茄酱”或“爸爸”和“黄油”更靠*。
使用具有一个输入层、一个隐藏层和一个输出层的神经网络来创建单词嵌入。

在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上由 [Toa Heftiba](https://unsplash.com/@heftiba?utm_source=medium&utm_medium=referral) 拍摄的照片
要创建单词嵌入,首先需要的是文本。让我们创建一个简单的例子,用 12 句话陈述一些关于一个虚构的皇室家族的众所周知的事实:
The future king is the princeDaughter is the princessSon is the princeOnly a man can be a kingOnly a woman can be a queenThe princess will be a queenQueen and king rule the realmThe prince is a strong manThe princess is a beautiful womanThe royal family is the king and queen and their childrenPrince is only a boy nowA boy will be a man
计算机不理解国王、王子和男人在语义上比王后、公主和女儿更接*。它看到的都是编码成二进制的字符。那么我们如何让计算机理解某些单词之间的关系呢?**通过创建 X 和 Y 矩阵并使用神经网络。**
当创建用于单词嵌入的训练矩阵时,超参数之一是上下文(w) 的**窗口大小。最小值为 1,因为没有上下文,算法无法工作。让我们看第一句话,假设 w = 2。**
The future king is the prince
粗体字**称为焦点字,左边的 2 个字和右边的 2 个字(因为 w = 2)是所谓的上下文字。所以我们可以开始建立我们的数据点:**
(The, future), (The, king)
**现在,如果我们浏览整个句子,我们会得到:**
(The, future), (The, king),
(future, the), (future, king), (future, is)
(king, the), (king, future), (king, is), (king, the)
(is, future), (is, king), (is, the), (is, prince),
(the, king), (the, is), (the, prince)
(prince, is), (prince, the)
**从 6 个单词中,我们能够创建 18 个数据点。在实践中,我们对文本做了一些预处理,删除了停用词,如 **is,the,a 等。**通过扫描整个文本文档并添加数据,我们创建了初始输入,然后可以将其转换为矩阵形式。**
**文本预处理功能**
**给定字符串列表**文本**创建(X,Y)单词对的完整管道:**
**数据点的创建**
**创建的数据点的第一个条目:**
['future', 'king'],
['future', 'prince'],
['king', 'prince'],
['king', 'future'],
['prince', 'king'],
['prince', 'future'],
['daughter', 'princess'],
['princess', 'daughter'],
['son', 'prince']
...
**在最初创建数据点之后,我们需要为词汇表中的每个唯一单词分配一个唯一的整数(通常称为 index)。这将在创建**独热编码矢量时进一步使用。****
**创建独特的单词词典**
**对文本使用上述函数后,我们得到字典:**
unique_word_dict = {
'beautiful': 0,
'boy': 1,
'can': 2,
'children': 3,
'daughter': 4,
'family': 5,
'future': 6,
'king': 7,
'man': 8,
'now': 9,
'only': 10,
'prince': 11,
'princess': 12,
'queen': 13,
'realm': 14,
'royal': 15,
'rule': 16,
'son': 17,
'strong': 18,
'their': 19,
'woman': 20
}
**到目前为止,我们所创建的仍然不是神经网络友好的,因为我们所拥有的数据是成对的**(焦点词,上下文词)**。为了让计算机开始计算,我们需要一种聪明的方法将这些数据点转换成由数字组成的数据点。一个聪明的方法是**一键编码**技术。**
**一键编码将一个单词转换成一个向量,该向量由 0 和一个表示字符串的坐标组成,等于 1。向量大小等于文档中唯一单词的数量。例如,让我们定义一个简单的字符串列表:**
a = ['blue', 'sky', 'blue', 'car']
**有三个独特的词:蓝色,天空和汽车。每个单词一个热表示:**
'blue' = [1, 0, 0]
'car' = [0, 1, 0]
'sky' = [0, 0, 1]
**因此,列表可以转换成矩阵:**
A =
[
1, 0, 0
0, 0, 1
1, 0, 0
0, 1, 0
]
**我们将用完全相同的技术创建两个矩阵,X 和 Y。将使用焦点词创建 **X 矩阵,使用上下文词创建 **Y 矩阵。******
**回想一下我们根据版税文本创建的前三个数据点:**
['future', 'king'],
['future', 'prince'],
['king', 'prince']
**python 中的一键编码 X 矩阵(单词 f**future,future,king** )应该是:**
[array([0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]),
array([0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]),
array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.])]
**python 中一键编码的 Y 矩阵(单词 **king,prince,prince** )应该是:**
[array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]),
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]),
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
0., 0., 0., 0.])]
**这些矩阵的最终大小将是**n×m,**其中**
****n** -创建的数据点的数量(焦点词和上下文词对)**
****m**——唯一字的数量**
**创建 X 和 Y 矩阵**
**我们现在有了从焦点单词和上下文单词对构建的 X 和 Y 矩阵。下一步是选择嵌入维度。我将选择维数等于 2,以便稍后绘制单词,并查看相似的单词是否形成簇。**
****
**神经网络体系结构**
**隐层维度就是我们的单词嵌入的大小。输出层激活功能是 **softmax。**隐藏层的激活函数是**线性的。**输入维数等于唯一字的总数(记住,我们的 X 矩阵的维数是 n×21)。每个输入节点将有两个权重将其连接到隐藏层。这些权重就是单词嵌入!在网络的训练之后,我们提取这些权重并去除所有剩余的权重。我们不一定关心输出。**
**对于网络的训练,我们将使用 keras 和 tensorflow:**
**训练和获得重量**
**在网络训练之后,我们可以获得权重并绘制结果:**
import matplotlib.pyplot as pltplt.figure(figsize=(10, 10))for word in list(unique_word_dict.keys()):
coord = embedding_dict.get(word)
plt.scatter(coord[0], coord[1])
plt.annotate(word, (coord[0], coord[1]))
****
**嵌入的可视化**
**我们可以看到,在剧情的各个角落,出现了‘男人’,‘未来’,‘王子’,‘男孩’和‘女儿’,‘女人’,‘公主’这样的字眼,形成一簇簇。所有这些都是通过 21 个独特的单词和 12 个句子实现的。**
**通常在实践中,使用预训练的单词嵌入,典型的单词嵌入维数为 100、200 或 300。我个人使用这里存储的嵌入:\[https://nlp.stanford.edu/projects/glove/](https://nlp.stanford.edu/projects/glove/)。**
# 创建您的第一个机器学习模型
> 原文:<https://towardsdatascience.com/creating-your-first-machine-learning-model-f97f47ee6749?source=collection_archive---------50----------------------->
## 向我展示如何像五岁小孩一样设置我的本地环境

韦斯·希克斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
这是我题为“**创建你的第一个机器学习模型**”系列的第一篇文章我假设你已经知道了机器学习的基础知识,并且想要创建你的第一个机器学习模型。我的目标是尽可能地为你分解步骤。
如果你发现任何不清楚的条款或你不明白的东西,这意味着我没有兑现我的承诺。请注意,你可以自由评论和要求澄清。
在这篇文章中,你将学到:
* 如何为机器学习项目设置硬件?
* 使用 conda 管理机器学习依赖性的可重复过程。
* 如何在新计算机上创建相同的机器学习项目环境
* 本地硬件的限制以及何时使用云服务进行机器学习工作。
* 免费云服务,允许你免费研究、构建和训练模型。
如果你要进行一次公路旅行,在开始旅行之前,你需要做一些事情。否则,你可能会停止你的公路旅行,并可能不得不支付一些意想不到的维修费用,也许,浪费时间。同样,在您开始机器学习之旅之前,您的环境必须完全设置好。
# 配置您的硬件
机器学习工作需要大量的计算能力;你要做的第一件事是确保你的硬件足够强大,能够在本地构建东西。对于硬件要求,您应该满足以下条件:
* 中央处理器
* 英特尔酷睿 i5 或更高版本。你也可以选择 AMD 的同类产品
* 你至少有 8GB 的内存
* 建议使用 GPU (NVIDIA GeForce GTX 960 ),但这是可选的
如果您是一名开发人员,这些步骤对您来说并不陌生。你应该做的第一件事是确保你的操作系统是最新的。
作为个人电脑的拥有者,你可能对“发行版”这个概念很熟悉——为便于使用而构建、组装和配置的软件组件的集合。对于科学任务,你需要一组用于科学计算的软件。我们将在本教程中使用的发行版之一是 Anaconda。
Anaconda 只是众多发行版中的一个。对于一个刚刚进入“condas”世界的初学者来说,你可能会发现术语“anaconda”、“miniconda”和 conda 令人困惑。你并不孤单。刚开始我也是这种感觉。如果你仔细看看这些单词,你会发现“conda”这个词在这三个名字中很常见。
“康达”究竟是什么?
## **康达**
如果在 python 中使用 pip 和 virtual env,那么 conda 会更清晰。Conda 是一个独立于*台的包管理器和环境管理器。包管理器允许你管理依赖关系(你或其他人写的代码)。即安装、更新和删除软件包。另一方面,环境管理器允许您隔离软件环境。
环境隔离是机器学习项目的一个重要方面,因为不同的工具可能具有冲突的需求和包;如果没有环境隔离,将很难管理多个项目。
## **迷你康达**
出于许多原因,您可能不想安装 Anaconda。例如,如果您的任务只需要一个包,为什么要安装 199 个您不会使用的包呢?您可以将 miniconda 视为 Anaconda 的引导版本,其中包括包管理器“conda”、python 以及一些基本包。Miniconda 需要大约 400MB 的磁盘空间,当磁盘空间不足时,这可能是一个更好的选择。
## **蟒蛇**
Anaconda 是一个 python 和 R 发行版,其目标是为您提供开箱即用的数据科学包。在撰写本文时,anaconda 已经预打包了多达 200 个数据科学包。因为 anaconda 拥有大多数包,所以它在您的机器上占用了更大的磁盘空间。对于初学者,您可能想从 Anaconda 开始您的机器学习之旅——这就是为什么我们在本教程的剩余部分使用它。
那么如何安装 Anaconda 呢?
# 安装 Anaconda
要安装 anaconda,请进入 Anaconda 的[下载页面](https://www.anaconda.com/products/individual),下载一个适合您选择的操作系统的二进制文件。
双击二进制发行版进行安装,始终单击“继续”并接受许可协议。在我的 Mac OS 上,Anaconda 的第一个屏幕是这样的:

Anaconda 安装屏幕
请花点时间放松一下;安装可能需要几分钟时间。您可以通过运行以下命令来验证 anaconda 是否已成功安装:
conda list
如果一切正常,您应该会看到软件包和预安装版本的列表,如下图所示。

Conda 列表输出
如果你得到一个错误,不要惊慌,你可能需要杀死你的终端并重新启动它。
## ***使用 conda*** 管理环境
> *科学是基于再现性和人为制造的客观性。这使得它有能力提出关于物质和能量的主张,也使得科学知识不适用于人类生活的存在性和本能,这是独特的、主观的和不可预测的*
>
> *——****保罗·卡兰尼蒂***
你曾经重新运行过你或别人十年前写的代码吗?如果你足够幸运能让它运行起来,这可能会令人沮丧。原因与软件和库确实变化很快这一事实相差不远。所使用的库、操作系统、编译器和解释器的版本可能已经改变或被更新的版本所取代。简而言之,十年前软件运行的环境和条件可能已经发生了变化。
好的代码必须是可重复运行和可复制的。确定您软件可以重新运行的一种方法是确保可再现和可复制的条件和要求是恒定的。我们通过锁定需求和隔离软件环境来做到这一点。
在安装 anaconda 时,conda 会为您创建一个名为“base”的默认环境。如果您将所有代码都放在这个基础环境中,那么隔离代码的目的就失败了。您想要做的是为每个项目创建一个新的环境,以保持您的项目是独立的。
## ***用康达*创造环境**
要创建一个具有特定软件版本的新环境,例如 python 和 scipy 的特定版本,请执行以下命令。
conda create --name example_env python=3.7 scipy=1.4.1
有时,在 YAML 文件中定义所有的环境依赖项会很方便。如果你想和别人分享你的代码,这非常方便。
您可以使用 YAML 文件 environment.yml 将环境定义为:
filename: environment.yml
name: another_example_env
dependencies:
- python=3.7
- scipy=1.4.1
在您的终端中执行下面的代码来创建环境之后
conda env create --file /path/to/environment.yml
## ***用康达*** 更新环境
更新您的环境是您在机器学习过程中的日常任务之一。
库经常变化,旧版本被替换,或者有时您甚至需要用一个库替换另一个库或者删除多余的依赖项。
Conda 提供了一个命令,用于在对环境文件进行更改后更新您的环境。
例如,让我们向环境中添加一个新的依赖项。
filename: environment.yml
name: another_example_env
dependencies:
- python=3.7
- scipy=1.4.1
- pytorch=1.5.0
对环境文件进行更改后,您可以通过运行以下命令来更新环境:
conda env update --file /path/to/environment.yml --prune
## ***列表环境***
当您有多个机器学习项目时,在选择要处理的项目之前,您可能希望先看到您的环境列表。
使用 conda list 命令,您可以看到您的环境列表:
conda info --envs
## ***激活您的环境***
环境激活是应用程序运行的先决条件。当您激活您的环境时,您正在设置您的应用程序工作可能需要的任意环境变量。
要使用 conda 激活环境,请运行以下命令:
conda activate myenv
## ***推出 jupyter 笔记本***
Jupyter notebook 是一个提供基于 web 的交互式界面的工具,允许您交互式地开发和展示数据科学项目。它将您的代码及其输出集成到一个文档中。
一旦您设置并激活了您的环境,您就可以通过执行命令`jupyter notebook`开始开发来启动 Jupyter 笔记本。
jupyter notebook
您将看到一个类似于下图的屏幕:

Jupyter 笔记本
## **分享和复制你的环境**
软件是为多次运行而编写的。通常,我们与同事、朋友、合作者,甚至随机的陌生人共享代码。当我们这样做时,我们应该确保他们能够运行我们与他们共享的代码——没有相同的环境,再现性是不可能的。
您可以通过导出您的环境来共享您的环境,以便您的同事可以构建与您相同的环境来运行您的代码。我们使用以下命令导出环境:
conda env export --file exported_environment.yml
## **打造相同的康达环境**
有时候,您可能希望在一台机器上构建一个相同的环境,而在另一台机器上构建一个相同的操作系统。
例如,如果你购买了新的硬件或者把你的代码从你的工作计算机转移到你的家用计算机,家用计算机有相同的操作系统。Conda 允许您在同一台机器上或者在运行相同操作系统*台的另一台机器上构建一个相同的环境。我们通过将规格列表导出为文件来实现这一点。
conda list --explicit > current/working/directory/spec-file.txt
您可以使用以下命令,使用导出的规范在另一台计算机上创建完全相同的环境:
conda create --name identical_env --file spec-file.txt
# ***本地硬件的限制***
您已经看到了如何为机器学习项目设置本地开发环境。您的本地设置适合需要少量计算能力的小型机器学习项目。
在现实世界中,机器学习项目通常需要大量的数据和巨大的计算能力,远远超出了您的本地机器所能提供的。
如果你正在训练一个模型来识别模式,它可能需要并行计算资源,这在传统处理器上可能需要几天时间。
所有这些限制和更多的限制使我们转向云服务,如[谷歌云*台、](https://cloud.google.com/) [亚马逊 Sagemaker](https://aws.amazon.com/sagemaker/) ,它们的硬件针对机器学习工作进行了优化。它们使我们能够利用无限的计算能力并行训练大型数据集。Google、AWS、Azure 等提供的云服务。拥有针对机器学习作业优化的硬件
# **免费云服务,允许您免费构建模型**
你可能会想,“哦,不,这是一个介绍性的帖子,对于我的项目,我甚至不需要强大的计算能力,我只是想尝试一下。”
我明白,我完全理解。如果你不是在做一个大项目,你可能只需要很少的计算能力来尝试。
这里有一个问题,云已经变得相对便宜,一些服务,如 [Google Colab](https://colab.research.google.com/) 甚至允许你免费训练模型。我的意思是不需要环境设置,大多数机器学习包已经预装。你可以免费快速搜索。
以下是云服务;您可以使用:
* [谷歌合作实验室](http://colab.research.google.com/)
* [朱庇特](http://jupyter.org/try)
* [微软 Azure](https://notebooks.azure.com/)
* [AWS Sagemaker](https://aws.amazon.com/sagemaker/)
* [谷歌人工智能*台](https://cloud.google.com/ai-platform)
* [Cocalc](https://cocalc.com/)
# 结论
在这篇文章中,你看到了如何为机器学习项目设置你的本地机器。我们还看到了一个免费云服务列表,允许您免费构建和训练模型。
同时,在你等待第二个帖子的时候,你可以[订阅我的简讯](http://bit.ly/hubofml)。每月一次,我会发送一份时事通讯,其中包含许多关于数据科学、软件工程和机器学习的令人兴奋的内容。期待快速提示,链接到有趣的教程,意见,论文和图书馆。
# 在 Google 云*台中创建 ETL 以实现自动化报告
> 原文:<https://towardsdatascience.com/creation-of-an-etl-in-google-cloud-platform-for-automated-reporting-8a0309ee8a78?source=collection_archive---------24----------------------->
## 以 PyTrends 为例,了解如何为自动化报告创建自己的无服务器且完全可伸缩的 ETL

基于 PyTrends 数据的最终报告
*首先要提到的是,这篇文章是由* [*阿尔贝托·巴尔加斯*](https://medium.com/@a.vargas.pina) *和亚历克斯·马斯普合著的。Medium 还没有合著选项,所以你应该怪我们俩:)*
我们都是 [Labelium](https://www.labelium.com/) 数据部门的一员。作为一家媒体机构的数据部门,我们经常需要开发和更新各种类型的报告,并且需要选择各种各样的数据源。
因此,我们迫切需要开发一个标准的 ETL 过程来满足以下需求:
* 完全自动化,一旦提交报告,无需数据部门资源的干预。
* 能够接收任何类型的数据源和任何更新条件。
* 把它留在 GCP,因为它是我们的主要工作栈。
我们在本文中解释的是我们如何实现它的一步一步,选择每种不同工具的原因以及一些有用的技巧。将有助于说明整个过程的用例是使用 PyTrends 从 Google Trends 中提取的搜索引擎索引的自动化报告。所选择的品牌和关键词是一个广泛的选择,旨在显示 Covid 危机对西班牙互联网用户搜索习惯的影响。该报告在每周一西班牙时间 08:05(GMT+1)自动更新。
值得注意的是,这个特定的报告可以使用更简单的解决方案自动生成,例如,通过启动一个带有启动脚本的虚拟机来执行所需的 python 代码。但是我们的目标是开发和测试一个可以在任何场景下工作的 ETL,不管具体的更新条件如何。
这是 ETL 的基本模式:

抽取、转换、加载至目的端(extract-transform-load 的缩写)
在下一节中,我们将详细介绍这个特定报告的 PyTrends 实现的所有相关内容。如果你只是对自动化部分感兴趣,可以直接跳到自动化部分。
# PyTrends
你可以在这个 GitHub 上找到所有的代码。
如果你喜欢一步一步的指导,就跟着我们吧。
为了更好地理解 Pytrends 是什么,它是如何工作的,以及它的局限性,我们强烈推荐这些文章:C [分类](https://github.com/pat310/google-trends-api/wiki/Google-Trends-Categories), [Pytrends 文档](https://pypi.org/project/pytrends/),[一个很棒的教程](https://searchengineland.com/learn-how-to-chart-and-track-google-trends-in-data-studio-using-python-329119),[它是如何工作的](https://www.karinakumykova.com/2019/03/calculate-search-interest-with-pytrends-api-and-python/)和/或[这篇文章](/google-trends-api-for-python-a84bc25db88f)。
万岁!既然你已经是 Pytrends 的专家了,那我们就来写脚本吧。
要记住的最重要的事情是,对于这个或任何其他你想通过云函数激活的代码,你是在一个完全无服务器的架构上工作。如果你不习惯典型的软件开发最佳实践,并且想要在基于笔记本的工作流程中应用最佳实践,我们强烈推荐 [Jupyter 笔记本宣言](https://cloud.google.com/blog/products/ai-machine-learning/best-practices-that-can-improve-the-life-of-any-developer-using-jupyter-notebooks)。这些是云功能*强制*架构标准:
* 忽略子文件夹,所有脚本必须在同一个文件夹中。
* 输出文件夹必须名为"**../tmp** ”,所以在开发的时候,在你当前的上面创建一个 tmp 文件夹。你最终不需要它,因为云功能会创建一个。
我们的选择是使用 4 个脚本:
* main.py 通过*runpy 调用工作脚本。*
* download_pytrends.py 向 *pytrends* 请求所需信息。
* upload_gcs.py 通过 *gcsfs 将新信息上传到 gcs。*
* 删除所有收集的数据。
# 剧本
记住创建一个 requirements.txt 文件来指定您需要的库:
以及所有的进口:
main.py
* download_pytrends.py
使用关键字类别代码,我们确保每个关键字都与其类别中的顶级关键字进行比较。创建一个包含几个关键词的列表会给你相对于那个关键词的结果,而不是那些没有列出的。
* 该函数请求 info,基于 [**这一个**](https://searchengineland.com/learn-how-to-chart-and-track-google-trends-in-data-studio-using-python-329119) :
*Unstack(level=-1)* 用于构造同一列中的所有关键字。在 Data Studio 中绘制结果时,这将非常有用。
我们现在可以与谷歌云存储互动。这需要一点时间,因为 gcsfs 库文档中并没有记录所有内容:
* remove_files.py 清除所有收集的数据:
出于我们的目的,我们有几个 CSV,所以我们创建了一个列表。
我们现在有了一个完整的 PyTrends 实现,可以将 csv 推送到 Google 云存储中。
在进入自动化部分之前,有几个快速提示:
* 请记住您通过 PyTrends 请求的数据量以及您使用的超时时间,以免被暂时禁止。
* 谷歌趋势信息是嘈杂的,并不完全稳定。那是有意的;如果相同的查询产生不同的结果,不要感到惊讶。请记住,谷歌趋势信息对于发现全球趋势非常有用,除此之外别无它用。它不是为深度数据分析提供高度依赖的信息而设计的。
# 自动化
## 提取阶段
主管道是:
云调度器>云发布/订阅>云功能
正如我们在开始时所讨论的,对于这个特定的用例来说,它确实是过度工程化了。没有必要使用云发布/订阅,我们可以使用更简单或更传统和紧凑的解决方案。但我们希望创建一个管道,允许我们在非常广泛的场景中触发报告的更新,并且非常容易扩展。
Cloud Pub/Sub 是来自 GCP 的事件和全球消息系统。它提供完全自动化的扩展和配置。这对我们很有帮助,因为它允许我们轻松地将报告自动化集成为任何应用程序的全局工作流的一部分。如果您需要简化任何类型的实时分析,它值得一看。
管道中的另一个关键元素是云功能。在这个特定的用例中,Cloud Functions 执行触发 PyTrends 更新 Google 云存储上的数据的 python 代码,正如我们在上一节中看到的。Cloud Functions 是 GCP 的事件驱动的无服务器计算*台。我相信你现在已经非常清楚地看到了趋势:目标是一个完全无服务器的事件驱动架构。
这些是设置此阶段的分步说明;这非常容易做到:
* 在云调度程序上创建作业:

在云调度程序上创建作业
这里只需要记住两件事:1)对于频率,您需要使用传统的 unix-cron 格式;2)记住将发布/订阅指定为您的目标。
* 根据您在 Cloud Scheduler 上建立的主题,转到 Pub/Sub 并创建一个新主题:

在发布/订阅上创建主题
这将由云调度程序触发,并进而触发云功能。
剩下唯一要做的就是设置云功能。云函数允许我们执行 python、Node.js 或 Go 中的代码。在我们的例子中,我们使用的是 python 脚本,因此我们需要相应地设置选项:

创建云函数
您需要将发布/订阅指定为触发器。选择您之前在 pub/sub 上创建的主题,上传带有 python 代码的 zip 文件,并选择 main 作为您的函数。请记住,还要在 GCS 上指向您要用于暂存的正确暂存存储桶。
还要记住,zip 文件必须包含根文件夹中的所有脚本(没有额外的文件夹!),包括 requirements.txt 文件。此外,main.py 是您的启动器脚本。
顺便提一下,根据其设计的用例,云功能有局限性,这一点很重要。例如,最大超时是 540 秒。如果你需要长时间执行代码,云函数并不是你理想的工具。查看 App Engine 可能是有意义的(如果您需要灵活性,可能由云功能触发)。
您可以在这里看到高级选项:

云功能高级选项
如您所见,您可以更改超时、更改网络选项、添加环境变量等。对于这个特定的用例,默认选项对我们来说很好。
## 为可视化转换和加载数据
随着数据被提取、更新并存储在我们想要的位置,我们可以进入下一个阶段:为报告准备好数据。
我们将使用 Dataprep 从云存储中提取数据,安排其在 BigQuery 上的发布,并从 BigQuery 生成我们的 Data Studio 报告。
Dataprep 是基于 [Trifacta Wrangler](https://www.trifacta.com/products/wrangler-editions/) 的数据准备解决方案,它完全集成在 GCP 上。很直观,很好用。如果您喜欢直接使用 SQL 方法,并且有需要自动化的复杂数据转换需求,我们强烈推荐 [Dbt](https://www.getdbt.com/) 。我们在内部使用它来转换 Google Ads 数据,以便在 BigQuery 中使用,这很棒。
但是对于这个特定的用例,Dataprep 将很好地为我们服务。
首先创建一个新流程:

在 Dataprep 中创建流
然后,导入要使用的数据:

在 Dataprep 上导入数据
你可以直接从你的电脑、云存储或 BigQuery 上传数据。
对于我们的用例,我们将在 GCS 中选择我们的 python 脚本(由云函数执行)正在生成的文件。
这将在 Dataprep 中打开一个新的流。我们将看到一个小预览,并开始添加我们的食谱:

Dataprep 上的导入数据
一旦我们开始编辑食谱,我们就可以开始处理数据。我们将获得包含一些描述性信息的 csv 的不同列,我们可以根据需要编辑数据。这将生成配方(我们转换数据所采取的完整步骤序列)。

Dataprep 上的 csv
当我们准备好的时候,我们可以点击*运行作业*,它将在数据流上执行我们的指令:

默认情况下,它会生成一个 csv,但是我们希望结果在 BigQuery 中。为此,我们只需选择*添加发布动作*并在 BigQuery 上选择一个数据集。
我们还可以根据需要选择创建/替换、追加或截断。因为我们希望在 BigQuery 上连续更新相同的数据集,所以我们将安排作业并选择 append。
我们现在已经自动更新了 BigQuery 上的数据集,我们将使用它作为 Data Studio 报告的基础。剩下的唯一一件事就是在 Data Studio 上添加数据源(Resources 菜单—管理数据源):

在 Data Studio 上添加新的数据源
使用 BigQuery 作为 Data Studio 的数据源的好处是,我们可以使用 BigQuery BI Engine,这是一个内存中的分析服务。它提供了极快的查询响应,而且,对于报表应用程序来说,最重要的是,它允许高并发性。
您可以在这里查看最终报告:[2020–2019 搜索引擎指数对比—西班牙](https://datastudio.google.com/open/1qyJweNLTKll6VLq1h2G8HRYMwHd-oUUC)
在第一页上,您有相关西班牙公司的简短选择,并可以按公司进行筛选。在第二页上,您可以使用代表不同部门的关键字进行同样的操作。我们的目标基本上是让人们快速了解新冠肺炎效应对不同行业搜索习惯的影响。每周一西班牙时间 08:05(GMT+1)自动更新。正如 PyTrends 部分提到的,你必须记住 Google Trends 数据的质量,特别是在一致性方面。它只是为了给出一个主要趋势的非常广泛的概述,在这方面,它是伟大的。
# 结论
我们希望这个小练习对你有所帮助。我们的主要目标是展示 GCP 在报告自动化方面提供的可能性,以及建立一个完全自动化的无服务器和可扩展的架构是多么容易,该架构可以针对各种各样的使用案例和数据源进行定制。
不用说,您可以使用许多不同的方法或工具来完成类似的任务,其中许多方法在架构方面并不复杂。但是在我们看来,这个练习是一个很好的起点,可以帮助你自己探索和设计你的理想解决方案。
# 用 Tableau 制作信息图形动画
> 原文:<https://towardsdatascience.com/creation-of-information-graphic-animations-with-tableau-21cfc7660189?source=collection_archive---------62----------------------->
## 使用关于 COVID19 感染的当前数据的例子
在我之前的文章“[动画信息图形](/animated-information-graphics-4531de620ce7)”中,我讨论了如何使用“Python”和“Plotly”将依赖于时间的数据显示为动画。在这篇文章中,我想展示如何用软件 [Tableau](http://www.tableau.com) 的 2020 年新版本创建信息图形动画。

作者图片
## **数据:**
为了举例,我使用了各个国家的 COVID19 感染数据,这些数据可以从“欧洲疾病预防和控制中心”下载。
可以用免费版的 [Tableau Public](https://public.tableau.com/) 来跟例子。
## **《赛车排行榜》**
在 YouTube 上很受欢迎的动画版条形图是所谓的“赛车条形图”。在这里,动画是由一系列单独的条形图创建的,这些条形图根据一个顺序进行排序,并随着时间的推移遵循这个顺序(“比赛”)。
以下示例显示了每个国家每 100,000 名居民中 COVID19 感染总数的发展情况。在每种情况下,从 3 月 1 日起全球排名前 15 的国家。2020 年,并且只考虑人口超过 100 万的国家。
每个国家 COVID19 感染情况的竞赛条形图
创建这种动画的说明可以在例如
[](/how-to-build-a-bar-chart-race-on-covid-19-cases-in-tableau-4c45b08cdafe) [## 在 5 分钟内建立一个关于新冠肺炎案件的 Tableau 条形图比赛
### 使用新的 Tableau 版本 2020.1
towardsdatascience.com](/how-to-build-a-bar-chart-race-on-covid-19-cases-in-tableau-4c45b08cdafe)
或者
## 动画地图
“Choropleth 地图”是地图的表现形式,其中各个国家根据颜色代码进行着色。这个颜色代码代表一个数值。在我们的例子中,这是每 100,000 名居民中的感染人数。如果现在将单个日期的地图表示连接在一起,则获得动画,该动画不仅显示了时间进程,还显示了感染的地理分布。
COVID19 感染的地图动画
在本报告所述期间(从 2020 年 3 月开始),感染扩散的中心位于欧洲和北美。在所示时间段的末尾(4 月底),可以观察到在南美和东欧的传播。然而,Choropleth 表示法的一个缺点是,人口众多的小国只能稍微看得见,而像俄罗斯、加拿大和美国这样的大国则占主导地位。
在 Tableau 中创建“Choropleth 地图”的介绍可在此处找到:
如果您现在将当天的数据字段放在 Tableau 的“页面架”上,并使用菜单项“格式/动画”打开动画功能,您可以轻松获得所需的动画。然后,对色阶进行单独调整,即可获得所需的外观。
# 创造力和人工智能
> 原文:<https://towardsdatascience.com/creativity-and-artificial-intelligence-46de4326970c?source=collection_archive---------38----------------------->

在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上由 [Franck V.](https://unsplash.com/@franckinjapan?utm_source=medium&utm_medium=referral) 拍摄的照片
## AI 是如何产生新想法的?它会取代人类的创造力吗?
# 创意机器人
人工智能是我们现在随处可见的流行语之一。它可以用来检测信用卡欺诈,下棋,甚至开车。但这些都是我们或多或少愿意放弃的任务,并承认计算机可以比我们做得更好——似乎有理由相信,总有一天,计算机将在我们的城市里四处行驶,协调交通。
但是艺术呢,或者说创造力呢?这一直是我们可以信赖的比电脑更好的东西——例如,当我们与 Siri 交谈时,很难想象它会画出达利这样的东西。然而,多年前,我们对象棋也有同样的感觉。这是人类智慧的巅峰,而且复杂到计算机无法理解,直到 IBM 的深蓝打败了被认为是有史以来最好的棋手加里·卡斯帕罗夫,震惊了世界。从那以后,计算机不仅在国际象棋方面超过了人类,在许多其他游戏方面也超过了人类,包括比国际象棋复杂得多的围棋。创造性艺术也会发生同样的情况吗?
我们可以看到创意领域缓慢但显著的进步:有能够[绘画](https://www.bbc.com/news/business-47700701)、[创作歌曲](https://www.bbc.com/future/article/20140808-music-like-never-heard-before),甚至[写文章](https://www.forbes.com/sites/nicolemartin1/2019/02/08/did-a-robot-write-this-how-ai-is-impacting-journalism/)的计算机。老实说,它们有时可能有点简单,但它们每年都会变得更好,并且更难与人类的工作区分开来。怎么可能呢?AI 如何才能有创造力?
基本上有三种方式可以用人工智能来创造新的想法:
1. 产生新的组合
2. 探索概念空间的潜力
3. 进行转换
目前,计算机(和科学家)更关注前两者。然而,第三个问题最有可能造成破坏,在未来几年可能会得到更多的探索。
# 产生新的组合
产生新的组合可能是三者中最容易被计算机探索的,并且非常接*于进行类比或开玩笑。
> “什么样的凶手有纤维?麦片杀手”。
不太好笑,但你可以看到它有一些逻辑,对不对?1996 年,人工智能博士 Kim Binsted 创造了一个能够产生双关语的计算机程序(这是其中之一)。虽然并不总是有趣,但我们可以通过评估单词之间的语音相似性(例如,*系列*和*谷物*)并交换它们,来理解计算机是如何制造这种笑话的。然而,Jape 所做的大部分是没有意义的,并且必须在以后由人类来修剪,以决定什么是有趣的,什么是不有趣的。
# 探索概念空间的潜力
这就是事情开始变得有趣的地方:想象一下,给一个人工智能输入音乐“语法”,即音乐创作的基本规则,以及贝多芬和斯特拉文斯基等著名作曲家的签名列表:他们的风格、典型的和声和旋律。也就是说,让贝多芬听起来像贝多芬的东西。然后,让这个人工智能写一首新歌,听起来像是贝多芬写的。这是大卫·柯普在 EMI(音乐智能实验)中所做的,你可以在这里听听结果:
这种特殊之处在于,给定一个音乐规则和签名的概念空间,计算机可以在这个空间内找到其他尚未探索的可能组合。不仅可以创作出高质量的音乐,还可以让音乐听起来像特定的作曲家。
# 进行转换
一个成功的发现系统的早期例子是自动数学家,一个 1970 年的项目,它可以生成和转换小代码。这是终极计算机的雏形:能够自我编程的计算机。这种系统被称为[人工开发](https://en.wikipedia.org/wiki/Artificial_development),用于一些特定的领域,并取得了一些显著的效果。
这是人工智能具有创造性的三种方式中探索最少的一种,但它无疑具有巨大的潜力。
# 接下来是什么?
AI 创造力的最大瓶颈之一是对新想法的评估:在探索和转换空间后,计算机如何自动理解和评估其结果?它怎么知道,从它写的所有歌曲中,哪些要保留?特别是对于空间转换应用程序,这可能特别棘手,但甚至更相关。
人工智能的最新进展表明,计算机能够制作高水*的艺术作品,往往能够欺骗人类,让他们认为这是另一个人制作的。我们能让计算机在没有我们干预的情况下自己完成这项工作吗?*期不会。我们会停止消费人造艺术品吗?大概不会。然而,我们可以开始欣赏这两者。
这里介绍的框架不仅与理解和评估人工智能领域的新发现有关,而且与提出新问题和解决这些问题的方法有关。
> “人工智能创造力的最终证明将是一个产生新颖想法的程序,这些想法最初会困惑甚至排斥我们,但它能够说服我们,它们确实是有价值的。我们离那还有很长的路要走。”玛格丽特·博登
本文是对*创造力和人工智能*的总结,由 Margaret A. Boden 发表在*人工智能*上。你可以在这里找到完整的文章。
# 基于自动编码器和 K-means 的信用卡客户聚类
> 原文:<https://towardsdatascience.com/credit-card-customer-clustering-with-autoencoder-and-k-means-16654d54e64e?source=collection_archive---------17----------------------->
## 通过改进模型进一步挖掘客户营销的商业智能

来自 pixabay 的 Img 通过[链接](https://pixabay.com/photos/marketing-customer-polaroid-center-2483867/)
在之前的[文章](/stacked-auto-encoder-as-a-recommendation-system-for-movie-rating-prediction-33842386338)中,我们为电影分级预测创建了一个堆栈式自动编码器模型。但正如我们所知,通过编码器部分,自动编码器模型也可以帮助进行特征提取。因此,在本文中,我们将继续使用自动编码器和 k-means 进行客户聚类。像往常一样,它被分成 4 个部分。
1. 自动编码器简介
2. 自动编码器建模
3. k 均值建模
4. 外卖食品
让我们开始旅程吧🏃♂️🏃♀️!
**1。自动编码器简介**
自动编码器是一种人工神经网络,用于以无监督的方式学习特征表示。它使用相同的数据进行输入和输出。如图 1 所示,通过在网络中添加瓶颈,它迫使网络创建输入数据的压缩版本,这就是编码器的工作方式。同时,解码器将编码特征重构为其原始输入。

图 1 自动编码器模型结构(图片由作者提供)
有不同类型的自动编码器模型。这里我们将创建一个堆栈式自动编码。在堆叠自动编码器模型中,编码器和解码器具有用于编码和解码的多个隐藏层,如图 2 所示

图 2 堆叠式自动编码器模型结构(图片由作者提供)
**2。自动编码器建模**
简单介绍之后,让我们继续创建一个用于特征提取的自动编码器模型。
**2.1 创建模型**
我们将使用 Keras 来创建模型架构。具体来说,
input_df = Input( shape = (17, ))
x = Dense(7, activation = ‘relu’)(input_df)
x = Dense(500, activation = ‘relu’, kernel_initializer=’glorot_uniform’)(x)
x = Dense(500, activation = ‘relu’, kernel_initializer=’glorot_uniform’)(x)
x = Dense(2000, activation = ‘relu’, kernel_initializer=’glorot_uniform’)(x)
encoded = Dense(10, activation = ‘relu’, kernel_initializer=’glorot_uniform’)(x)x = Dense(2000, activation = ‘relu’, kernel_initializer=’glorot_uniform’)(encoded)
x = Dense(500, activation = ‘relu’, kernel_initializer=’glorot_uniform’)(x)
decoded = Dense(17, kernel_initializer=’glorot_uniform’)(x)autoencoder = Model(input_df, decoded)
encoder = Model(input_df, encoded)
autoencoder.compile(optimizer = ‘adam’, loss = ‘mean_squared_error’)
对 mention✨✨:来说有几分价值
* 输入张量是一批 17 维向量,因为我们的数据有 17 个特征。
* 网络瓶颈的输出维度是 10,因为我们的目标是将 17 个特征压缩到 10 个特征。
* 网络的最终输出解码后的维数为 17,与输入维数相同。
* Autoencoder 模型将 input_df 作为输入,解码后作为输出。
* 编码器模型将 *input_df* 作为输入,将*编码后的*作为输出。
* 我们使用 **glorot_uniform** 通过从均匀分布中抽取样本来初始化层权重。
**2.2 拟合&提取**
创建好模型后,让我们来拟合模型。注意,我们对模型输入和输出使用相同的数据!
autoencoder.fit(creditcard_df_scaled, creditcard_df_scaled, batch_size= 120, epochs = 25, verbose = 1)
然后,我们可以使用编码器部分来提取特征表示。
pred = encoder.predict(creditcard_df_scaled)
**3。k 均值建模**
**3.1 找 K**
k-means 的第一步是挑选聚类数。我们将使用肘法。为了实现它,我们对不同数量的聚类应用 k-means。具体来说,
score_2 = []
range_values = range(1, 20)
for i in range_values:
kmeans = KMeans(n_clusters = i)
kmeans.fit(pred)
score_2.append(kmeans.inertia_)
如果您还记得在之前的[文章](https://medium.com/@vistaxjtu/credit-card-customer-clustering-with-k-means-b9ec023a7d6e)中,我们也应用了肘方法,但是使用了原始的特性。图 3 比较了原始数据集和编码数据集的 WCSS 变化。我们发现最佳的 K 是 4。

图 3 WCSS 随聚类数的变化(图片由作者提供)
**3.2 应用 k 均值**
决定了 k 之后,让我们对压缩数据集应用 k-means。
kmeans = KMeans(4)
kmeans.fit(pred)
labels = kmeans.labels_
df_cluster_dr = pd.concat([creditcard_df, pd.DataFrame({‘cluster’: labels})], axis = 1)
**3.3 可视化集群**
有趣的部分来了!
第一个可视化是比较集群特征。由于功能太多,这里只分析 5 个关键功能,*'余额','购买','一次性购买','分期付款 _ 购买','现金 _ 预付'。这里我们将进行一些定性观察。*
仔细看看图 4📣📣。你可以在下面找到:
* 聚类 3 中的客户余额最低,与其他组相比,他们没有进行太多的购买和现金预支。所以,他们对信用卡并不积极或热衷。
* 聚类 2 中的客户是一个余额非常高的群体,与聚类 0 和 1 相比,他们倾向于进行更多的购买,包括一次性购买和分期购买。
* 聚类 1 中的客户的*均余额低于聚类 0。他们的购买模式相似,但聚类 0 中的客户倾向于进行更多的一次性和分期付款购买,尽管他们的*均信用限额低于聚类 1。

图 4 比较聚类特征(图片由作者提供)
这些发现可以使营销策略更有针对性和洞察力📣📣。
**3.4 PCA 可视化**
如果我们想可视化聚类分布,我们的数据集与 10 个特征是不可能做到这一点。但是我们可以使用主成分分析进一步将特征压缩到 2D 空间。具体来说,
pca = PCA(n_components = 2)
prin_comp = pca.fit_transform(pred)
现在我们可以查看如图 5 所示的集群分布。很好,现在我们可以看到 4 个没有太多重叠的集群。

图 5 主成分分析空间上的聚类分布(图片由作者提供)
**4。外卖**
最初,我们使用 k-means 进行客户聚类,创建了 8 个组。这里,我们进一步重新创建了特性,并使用自动编码器模型将特性总数从 17 个压缩到 10 个。
我们发现每一组都有一些有趣的特征。例如,分类 3(上面的黄点)显示没有低余额的频繁购买。根据营销目的的不同,这些客户可以根据他们的习惯很好地定位。
此外,我们只做了一些定性的观察。我们更好地对每个集群的特征进行定量分析,这可以给我们更多有见地的发现。
太好了!这是所有的旅程。希望你喜欢它。如果你需要代码,请访问我的 Github [repos](https://github.com/luke4u/Credit-Card-Prediction/tree/master/customer_clustering) 💕💕。
# 基于 K 均值的信用卡客户聚类
> 原文:<https://towardsdatascience.com/credit-card-customer-clustering-with-k-means-b9ec023a7d6e?source=collection_archive---------16----------------------->
## 基于信用卡数据集的 k-means 聚类和 PCA 的商业智能营销

来自 pixabay 的 Img 通过[链接](https://pixabay.com/illustrations/tent-leaves-camping-icons-5635679/)
在之前的[帖子](/credit-card-fraud-detection-9bc8db79b956)中,我们试图使用各种模型来检测信用卡交易中的欺诈。这里我们将转向另一个热门话题——客户聚类。这个帖子旨在帮助市场部的朋友。像往常一样,它被分成 5 个部分。
1. 问题陈述
2. 数据审查
3. 数据处理
4. k 均值聚类
5. 外卖食品
现在让我们开始旅程🏃♀️🏃♂️.
**1。问题陈述**
众所周知,营销对于任何企业的发展和可持续性都是至关重要的。然而,对于任何营销专业人士来说,一个关键的痛点是了解客户并确定他们的需求。市场部的同事分配给你的任务是创建一个模型来执行客户分组。
**2。数据回顾**
数据集包含 17 个要素和 8,950 条记录。如图 1 所示,它是关于客户的购买和支付习惯,例如客户多久进行一次一次性或分期付款购买,或者他们多久进行一次现金预付,支付多少,等等。通过检查每个顾客,我们可以发现他/她喜欢哪种类型的购买,或者他/她是否喜欢预付现金而不是购买。

图 1 数据集列定义概述(图片由作者提供)
**3。数据处理**
一般来说,为了深入了解任何原始数据并获得初步发现,我们会执行探索性数据分析(EDA)。我在几篇文章中讨论了这个主题和一般步骤。如果你是 EDA 新手,看看我之前的[文章](/demystify-employee-leaving-with-eda-2ed96525f3a7)。在这里,我们将重点关注以下建模所需的一些步骤。
**3.1 填充缺失值**
由于各种原因,原始数据中经常出现缺失值。要检查每列中缺少多少值,
creditcard_df = pd.read_csv(‘Marketing_data.csv’)
creditcard_df.isnull().sum()
如图 2 所示,列*‘信用额度’*有 1 个缺失值,而‘最小支付’有 313 个缺失值。

图 2 缺失值计数(作者图片)
处理缺失值的方法有很多,包括:
* 删除缺少值的列
* 插补,用某个数字填充缺失值,例如每列的*均值。
* 插补的扩展。插补是标准方法。但是,估算值可能高于或低于其实际值。为了帮助 model 做出更好的预测,我们可以添加一个列,将缺失的值标记为 True 或 False。
在这种情况下,我们将填充缺失值列的*均值。具体来说,
creditcard_df.loc[(creditcard_df[‘MINIMUM_PAYMENTS’].isnull() == True), ‘MINIMUM_PAYMENTS’] = creditcard_df[‘MINIMUM_PAYMENTS’].mean()creditcard_df.loc[(creditcard_df[‘MINIMUM_PAYMENTS’].isnull() == True), ‘MINIMUM_PAYMENTS’] = creditcard_df[‘MINIMUM_PAYMENTS’].mean()
**3.2 跌落特性**
数据集包含 17 个要素。现在我们需要考虑是否所有这些都是训练模型所必需的。在我们这种情况下,*‘CUST _ ID’*是无济于事的。所以我们会放弃。
creditcard_df.drop(‘CUST_ID’, axis = 1, inplace = True)
**3.3 秤特点**
有些特征如*‘购买’*变化范围很大,而其他特征如*‘购买 _ 频率’*在 0 到 1 之间变化。我们需要在同一范围内缩放所有功能。这里我们使用来自 **sklearn** 的 **StandardScaler()** 来移除*均值并缩放至单位方差。
scaler = StandardScaler()
creditcard_df_scaled = scaler.fit_transform(creditcard_df)
**4。k 均值聚类**
k-means 聚类是一种无监督的机器学习算法。根据维基百科,它旨在将观测值划分为 k 个集合,以最小化组内*方和(WCSS)。WCSS 表示聚类中所有点到质心的距离的总和。它从一组随机初始化的质心开始,然后执行迭代计算以优化质心的位置,直到质心稳定,或者达到定义的迭代次数。
**4.1 找 K**
k-means 的第一步是挑选聚类数。肘法是最受欢迎的方法之一。为了实现它,我们对不同数量的聚类应用 k-means 并比较它们的 WCSS。具体来说,
score_1 = []
range_values = range(1, 20)
for i in range_values:
kmeans = KMeans(n_clusters = i)
kmeans.fit(creditcard_df_scaled)
score_1.append(kmeans.inertia_)
图 3 显示了 WCSS 随集群数量的变化。为了确定聚类的最佳数量,**我们必须选择“弯头”处的 k 值,即在该点之后失真/惯性开始以线性方式降低**。因此,我们得出结论,最佳聚类数是 8。

图 3 WCSS 随聚类数的变化(图片由作者提供)
**4.2 应用 k 均值**
确定了最佳聚类数后,让我们应用 k-means 并预测每个样本的聚类。具体来说,
kmeans = KMeans(n_clusters = 8, init = ‘k-means++’, max_iter = 300, n_init = 10, random_state = 0)labels = kmeans.fit_predict(creditcard_df_scaled)
注意,我们将 k 均值的运行次数设置为 10。这意味着 k-means 将使用不同的质心种子运行 10 次,最终结果将是 WCSS 方面的最佳输出。此外,我们将单次运行的 k 均值的最大迭代次数设置为 300。
**4.3 可视化集群**
有了预测结果,第一个可视化是检查每个聚类的特征分布。
例如,让我们以集群 4 为例进行分析。**如图 4 所示,与其他聚类相比,我们发现聚类 4 中的客户倾向于具有高账户余额,并且非常频繁地更新余额,但是他们的购买金额非常偏向 0。这表明客户对预借现金更感兴趣,如图 5** 所示。

图 4 将聚类 3 的特征与其余聚类进行比较

图 5 比较集群之间的*‘CASH _ ADVANCE’*(图片由作者提供)
作为一名信用卡用户,我经常购物,但不喜欢预支现金。因此,我发现自己可能属于第 0 类,它代表经常进行一次性购买,但不热衷于分期付款和预付现金的客户。但是如果你分期付款购物,而不是预付现金,你可能属于第二类。
就我个人而言,我发现分析不同客户群体在交易中的表现非常有趣✨✨.也许试一试,看看你适合哪里📣📣?
**4.4 PCA**
显然,直方图对于分析聚类特征很有用,但对于可视化总体聚类分布却没那么有用。因为我们有 17 个特征,所以我们必须压缩自由度的维度,以便直观地观察集群分布。为此,我们使用主成分分析(PCA)将特征压缩到 2D 空间中。
对于那些不熟悉 PCA 的人来说,PCA 通常用于降维,通过将数据仅投影到第一主分量上来获得低维数据,同时保留或最大化沿投影方向的方差(顺便说一句,这来自[维基百科](https://en.wikipedia.org/wiki/Principal_component_analysis)🤭).
所以,为了实现 PCA,
from sklearn.decomposition import PCA
pca = PCA(n_components = 2)
principal_comp = pca.fit_transform(creditcard_df_scaled)
pca_df = pd.DataFrame(data = principal_comp, columns = [‘pca1’, ‘pca2’])
pca_df = pd.concat([pca_df, pd.DataFrame({‘cluster’: labels})], axis = 1)
最后,将 17 个特征压缩为 2 个特征,图 6 显示了 k 均值预测结果的散点图。你可能会注意到红色星团分散在蓝色星团、粉色星团和绿色星团中。这可能表明红色聚类与其他聚类具有特征相似性。也许我们可以用新技术再造一个新的集群🤔🤔?

图 6 主成分分析空间上的聚类分布(图片由作者提供)
**5。外卖**
我们创建了一个 k-means 模型,成功地将信用卡客户分为 8 类。我们发现集群 0、2 和 4 的一些有趣的购买和支付模式,还有更多有待探索。我们还注意到集群之间有一些重叠,表明还有改进的空间。
太好了。希望你觉得这次旅行很有趣。为了继续这个话题,我将在下一篇文章中进一步挖掘,看看我们是否可以改进聚类结果。在那里见💕💕。
# 信用卡欺诈检测
> 原文:<https://towardsdatascience.com/credit-card-fraud-detection-1b3b3b44109b?source=collection_archive---------27----------------------->
## 使用多元高斯分布的欺诈检测

图片由[穆罕默德·哈桑](https://pixabay.com/users/mohamed_hassan-5229782/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=3696073)拍摄,来自[皮克斯拜](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=3696073)
# 动机
如今,大多数交易都在网上进行,这意味着信用卡和其他在线支付系统也参与其中。这种方法对公司和消费者都很方便。消费者节省了时间,因为他们不必去商店购物,公司通过不欠实体店和避免昂贵的租金来节省资金。数字时代似乎带来了一些非常有用的功能,这些功能改变了公司和消费者之间的互动方式,但只有一个成本…公司需要雇用熟练的软件工程师和渗透测试人员,以确保所有交易都是合法的、非欺诈性的。这些人在设计公司的服务器时,让客户无法控制关键的交易部分,如支付金额。通过精心设计,大多数(如果不是全部)问题都可以消除,但即使是用来创建服务器的框架也不是完美的。例如,如果你遵循 django 框架发布说明,你会发现在不同的版本中有许多错误修正,因此一个公司不应该仅仅依赖于它的工程技术。
# Python 中的信用卡欺诈检测
在本文中,我将介绍一种方法来检测是否有人绕过安全墙进行非法交易。你可以在我的 [github repo](https://github.com/christk1/anomaly-detection) 中找到代码和数据集,但是我强烈建议你按照本文的说明来构建它。
数据集由信用卡交易组成,其特征是 PCA 分析的产物,因此除了“金额”、“时间”和“类别”之外,我们不知道它们代表什么。“金额”是每笔交易的价格,“时间”是<<seconds elapsed="" between="" each="" transaction="" and="" the="" first="">>“类别”在其值等于 **1** 时表示一笔**欺诈交易**,在其值等于 **0** 时表示一笔**有效交易**。最初,我们将安装所需的包,以便从我的 GitHub repo 中找到“requirements.txt”文件,创建一个虚拟环境安装它们:</seconds>
pip install -r requirements.txt
最初创建一个“main.py”文件并导入以下内容:
import numpy as np
import pandas as pd
import sklearn
from scipy.stats import norm
from scipy.stats import multivariate_normal
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
import seaborn as sns
现在,我们将读取数据集并检查是否有任何缺失值:
df = pd.read_csv('creditcardfraud/creditcard.csv')# missing values
print("missing values:", df.isnull().values.any())
没有丢失的值,所以我们可以继续进行可视化,以更好地理解我们的数据。让我们画出数据集的*衡程度:
plot normal and fraud
count_classes = pd.value_counts(df['Class'], sort=True)
count_classes.plot(kind='bar', rot=0)
plt.title("Distributed Transactions")
plt.xticks(range(2), ['Normal', 'Fraud'])
plt.xlabel("Class")
plt.ylabel("Frequency")
plt.show()

我们发现我们的数据非常不*衡。因此,我们不能直接使用任何监督学习算法,因为它会基于“正常”的例子过度拟合。此时,我们仍处于数据调查分析阶段,因此让我们绘制热图:
heatmap
sns.heatmap(df.corr(), vmin=-1)
plt.show()

我们似乎没有高度相关的特征,尽管“V2”和“金额”之间存在轻微的负相关(标签*未显示在上面的热图中,但它接*“类别”特征*)特征。“Amount”功能与其他功能也略有关联,这意味着它可以部分地由他们计算,所以我们可以尝试放弃它(从我的测试来看,这给了最终分数一个很好的改善)。“时间”和其他特性之间也有一点关联,但是在这篇文章之后,我们会看到一个更重要的理由来放弃它。
到目前为止,我们做了一些不错的数据分析,但我们没有做最重要的部分,即了解“我们的数据来自哪里”。因此,下一张图将是数据分布图:
fig, axs = plt.subplots(6, 5, squeeze=False)
for i, ax in enumerate(axs.flatten()):
ax.set_facecolor('xkcd:charcoal')
ax.set_title(df.columns[i])
sns.distplot(df.iloc[:, i], ax=ax, fit=norm,
color="#DC143C", fit_kws={"color": "#4e8ef5"})
ax.set_xlabel('')
fig.tight_layout(h_pad=-1.5, w_pad=-1.5)
plt.show()

我鼓励您在本地机器上执行上述代码片段,以便更好地查看结果。蓝线表示实际的高斯分布,而红线表示数据的概率密度函数。我们看到几乎每个特征都来自**正态(或高斯)分布**,除了来自“时间”分布。因此,这将是我们使用多元高斯分布进行**欺诈检测的动机。这种方法只适用于高斯分布的特征,因此如果你的数据不像高斯分布,你可以用我在这篇文章中描述的方法将其转换。例如,你可以在“第 26 版”、“第 4 版”、“V1”上尝试这种技巧,看看你是否能在我们的最终得分上有所提高。“时间”特征来自于**双峰分布**,其不能被转换为类高斯分布,因此我们将丢弃它。放弃“时间”特性的另一个原因是,它似乎不像其他图表中显示的特性那样包含极值**。****
此外,我们将使用的算法对距离度量很敏感,因此如果我们将特征缩放到固定范围,我们将获得更好的结果。添加下面的代码片段,该代码片段将删除上面提到的功能,并缩放其他功能,使它们的值介于 0 和 1 之间:
classes = df['Class']
df.drop(['Time', 'Class', 'Amount'], axis=1, inplace=True)
cols = df.columns.difference(['Class'])
MMscaller = MinMaxScaler()
df = MMscaller.fit_transform(df)
df = pd.DataFrame(data=df, columns=cols)
df = pd.concat([df, classes], axis=1)
**注意**minmax scaler 不会改变分布的形状,因此异常值仍会在正确的位置。
至此,我们完成了数据分析和预处理,并准备深入研究算法的实现细节。该算法的步骤如下:
1. 查找可能包含异常示例的要素(完成)
2. 计算训练集中每个特征的*均值,训练集中通常包含**正常**交易的 **60%**
3. 计算训练集的协方差矩阵
4. 计算训练集上的多元正态 pdf(概率密度函数)(下面给出的*)*
5. *计算验证集上的多元正态 pdf(包含**欺诈**交易的 **50%** ,通常包含**正常**交易的 **20%***
6. *在测试集上计算多元正态 pdf(包含**欺诈**交易的 **50%** ,通常包含**正常**交易的 **20%***
7. *基于来自**验证**集合的 **pdf** 找到一个阈值,该阈值表明比**阈值**小**的 **pdf 值**是**异常值*****
8. *计算测试集上的**异常值**,这些异常值是比先前阈值小**的 **pdf 值**的**和*****
**
**多元正态 pdf**
*其中 **μ** 为均值, **det** 为行列式,**σ**为协方差矩阵, **κ** 为 **x** 取值所在空间的维数。*
> **通常,该 pdf 返回交易正常的< <置信度> >。如果该数字低于阈值,则该交易是异常值。**
*是的,我知道上面的 pdf 伤了你的眼睛(就像我的一样),但它已经在 scipy 包中实现了…*
*继续我们的代码,创建一个名为“functions.py”的文件,我们将在其中添加有用的函数来实现算法的某些阶段,并放置以下函数,该函数将我们的数据集分为训练集、验证集和测试集:*
import pandas as pd
import numpy as npdef train_validation_splits(df):
# Fraud Transactions
fraud = df[df['Class'] == 1]
# Normal Transactions
normal = df[df['Class'] == 0]
print('normal:', normal.shape[0])
print('fraud:', fraud.shape[0]) normal_test_start = int(normal.shape[0] * .2)
fraud_test_start = int(fraud.shape[0] * .5)
normal_train_start = normal_test_start * 2 val_normal = normal[:normal_test_start]
val_fraud = fraud[:fraud_test_start]
validation_set = pd.concat([val_normal, val_fraud], axis=0) test_normal = normal[normal_test_start:normal_train_start]
test_fraud = fraud[fraud_test_start:fraud.shape[0]]
test_set = pd.concat([test_normal, test_fraud], axis=0) Xval = validation_set.iloc[:, :-1]
Yval = validation_set.iloc[:, -1] Xtest = test_set.iloc[:, :-1]
Ytest = test_set.iloc[:, -1] train_set = normal[normal_train_start:normal.shape[0]]
Xtrain = train_set.iloc[:, :-1] return Xtrain.to_numpy(), Xtest.to_numpy(), Xval.to_numpy(), Ytest.to_numpy(), Yval.to_numpy()
*现在添加下面的函数来计算*均值和协方差矩阵:*
def estimate_gaussian_params(X):
"""
Calculates the mean and the covariance for each feature. Arguments:
X: dataset
"""
mu = np.mean(X, axis=0)
sigma = np.cov(X.T) return mu, sigma
*回到我们的“main.py ”,导入并调用上述函数以及我们每组的多元正态 pdf:*
(Xtrain, Xtest, Xval, Ytest, Yval) = train_validation_splits(df)(mu, sigma) = estimate_gaussian_params(Xtrain)# calculate gaussian pdf
p = multivariate_normal.pdf(Xtrain, mu, sigma)
pval = multivariate_normal.pdf(Xval, mu, sigma)
ptest = multivariate_normal.pdf(Xtest, mu, sigma)
*现在是时候参考阈值(或“ε”)了。一般来说,一个好的做法是用 pdf 的最小值初始化阈值,并以一小步递增,直到达到最大 pdf,同时将每个阈值保存在一个向量中。对于我们的问题,我发现来自 pdf 的值可以有效地用于创建阈值向量。创建向量后,我们创建一个“for”循环,并对其进行迭代。在每次迭代中,我们将当前阈值与产生我们预测的 pdf 值进行比较。然后,我们根据我们的**预测**和**地面实况**值计算 **F1 分数**,如果我们发现 F1 分数大于之前的分数,我们将覆盖一个“最佳阈值”变量。在“for”循环结束时,我们得到了产生最佳 F1 分数的ε值。*
***注意**我们不能用准确性作为我们的衡量标准!如果我们确定所有的交易都是“正常”的,我们可能有 99%的准确率和一个无用的算法。*
*要实现上述功能,请将以下函数添加到“functions.py”中:*
def metrics(y, predictions):
fp = np.sum(np.all([predictions == 1, y == 0], axis=0))
tp = np.sum(np.all([predictions == 1, y == 1], axis=0))
fn = np.sum(np.all([predictions == 0, y == 1], axis=0)) precision = (tp / (tp + fp)) if (tp + fp) > 0 else 0
recall = (tp / (tp + fn)) if (tp + fn) > 0 else 0
F1 = (2 * precision * recall) / (precision +
recall) if (precision + recall) > 0 else 0
return precision, recall, F1def selectThreshold(yval, pval):
e_values = pval
bestF1 = 0
bestEpsilon = 0 for epsilon in e_values:
predictions = pval < epsilon (precision, recall, F1) = metrics(yval, predictions) if F1 > bestF1:
bestF1 = F1
bestEpsilon = epsilon return bestEpsilon, bestF1
*最后,在我们的“main.py”文件中导入函数,并调用它们来返回我们的阈值和验证集的 F1 分数,并在我们的测试集上评估我们的模型:*
(epsilon, F1) = selectThreshold(Yval, pval)print("Best epsilon found:", epsilon)
print("Best F1 on cross validation set:", F1)(test_precision, test_recall, test_F1) = metrics(Ytest, ptest < epsilon)
print("Outliers found:", np.sum(ptest < epsilon))
print("Test set Precision:", test_precision)
print("Test set Recall:", test_recall)
print("Test set F1 score:", test_F1)
*结果是:*
*Best epsilon found: 5e-324
Best F1 on cross validation set: 0.7852998065764023
Outliers found: 210
Test set Precision: 0.9095238095238095
Test set Recall: 0.7764227642276422
Test set F1 score: 0.837719298245614*
*哪个挺好的!*
> **本教程已经结束,希望这些信息对您有用。请不吝赐教,并询问您不明白的事情,我会尽快回复您!**
**最初发表于*[Python 中的欺诈检测(devnal.com)](https://devnal.com/articles/7/fraud-detection-in-python/)*。**
# 信用卡欺诈检测
> 原文:<https://towardsdatascience.com/credit-card-fraud-detection-9bc8db79b956?source=collection_archive---------7----------------------->
## 机器学习模型和深度神经网络比较和采样技术来提高性能

来自 Stockvault 的 Img 通过[链接](https://www.stockvault.net/photo/254493/hacking-credit-card-illustration#)
在本文中,让我们带您经历一场关于信用卡欺诈检测的 Kaggle 竞赛。**将建立一个深度神经网络和两个机器学习模型来应对挑战,并比较不同模型的性能。**此外,将实施数据采样技术来改进模型。像往常一样,分成 9 个部分:
1. 商业挑战
2. 数据审查
3. 数据处理
4. DNN 模型建筑
5. DNN 模型评估
6. 决策图表
7. 随机森林
8. 抽样
9. 摘要
现在让我们开始旅程🏃♂️🏃♀️.
**1。业务挑战**
检测欺诈交易对任何信用卡公司都非常重要。一家知名公司让我们负责检测潜在的欺诈行为,这样客户就不会为他们没有购买的商品付费。因此,我们的目标是建立一个分类器,判断一项交易是不是欺诈。
**2。数据回顾**
数据集是 Kaggle 信用卡欺诈检测数据集[这里是](https://www.kaggle.com/mlg-ulb/creditcardfraud)。它包含欧洲持卡人在 2013 年 9 月进行的两天交易。该数据集包含 284,807 笔交易中的 492 笔欺诈。因此,它是高度不*衡的,正面(欺诈)仅占 0.17%。
查看视频中显示的数据,您可能会发现它只包含数字变量。特征 *V1,V2,… V28* 是 PCA 变换得到的主要成分。唯一没有被转换的特征是*‘时间’*和*‘金额’*。*“时间”*是每次交易与第一次交易之间经过的秒数。“金额”是交易金额。 ***【类】*为响应变量,1 为欺诈,0 为非欺诈**。
原始输入数据集的视频简要视图
**3。数据处理**
要快速了解每个变量的分布,下面我们来试试。

Gif 数据分布的简要视图
正如您所注意到的,变量*‘Amount’*的范围是从 0 到 25,691.16。为了缩小其较大的范围,我们使用**标准化**来移除*均值并缩放至单位方差,以便 68%的值位于(-1,1)之间。如果你想了解这个话题的更多细节,请阅读这篇文章。
具体来说,
scaler = StandardScaler()data[‘NormalizedAmount’] = scaler.fit_transform(data[‘Amount’].values.reshape(-1, 1))
注意我们必须使用*" . values . shape(-1,1)"* 将**系列**转换为**数组**,并整形为 2D 数组。尝试删除这一部分,看看你得到的错误。这是一个常见的📣📣!!
现在让我们将数据分成 X 和 y 两部分,具体来说,
data = data.drop([‘Amount’, ‘Time’], axis = 1)
y = data[‘Class’]
X = data.drop([‘Class’], axis = 1)
列 *['Amount ',' Time']* 被删除,因为建模不需要它们。注意,我们在删除列时设置**轴= 1** 。如果您不熟悉 drop 函数,这是另一个常见错误📣📣!
最后,让我们将数据分成训练和测试数据集。具体来说,
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 0)
**4。DNN 模型建筑**
在这里,我们将使用 **Keras** 中的顺序模型构建一个 5 层深度神经网络。具体来说,
model = Sequential([
Dense(input_dim = 29, units = 16, activation = ‘relu’),
Dense(units = 24, activation = ‘relu’),
Dropout(0.5),
Dense(units = 20, activation = ‘relu’),
Dense(units = 24, activation = ‘relu’),
Dense(units =1, activation = ‘sigmoid’),])
对于第一个隐藏层, *input_dim* 是输入变量的数量。*units’的*是每层中节点或神经元的数量。
我们使用校正线性单元( *ReLU* )作为隐藏层的激活函数。 ***ReLU* 在构建深度神经网络时,通常表现优于 Sigmoid 和双曲线正切函数。这是因为当输入值过大或过小时,Sigmoid 和 Tanh 会趋于饱和。此外,它们仅在其中点周围显示高梯度,例如对于 sigmoid 为 0.5,对于 tanh 为 0。如果你想要更多关于 *ReLU* 的细节,请随意阅读这篇[文章](https://machinelearningmastery.com/rectified-linear-activation-function-for-deep-learning-neural-networks/)。**
我们在二进制分类问题的输出层使用 Sigmoid 函数。如果你想了解更多关于如何创建神经网络的知识,请阅读这篇文章。
**5。DNN 车型评测**
有了模型架构,让我们编译和训练模型。具体来说,
model.compile(optimizer = ‘adam’, loss = ‘binary_crossentropy’, metrics = [‘accuracy’])
model.fit(X_train, y_train, batch_size = 15, epochs = 5)
注意,上面我们使用“*二元交叉熵*作为损失函数,并使用“*Adam”*来更新网络权重。*‘Adam’*是深度学习领域快速取得好结果的流行算法。如果你想知道更多关于亚当的细节,请随意阅读这篇文章。
模型权重每 15 个样本更新一次。如果你不清楚 epoch 和 batch 的概念。一个时期代表整个训练集通过网络输入的一个时间。批次定义了在更新内部模型参数之前要迭代的样本数。在批次结束时,将预测与预期输出进行比较,并计算误差。利用这个误差,优化器通过降低误差梯度来改进模型。
太好了。现在我们来评价一下模型。具体来说,

图 2 DNN 评估
请记住,我们使用“**准确性”**作为衡量标准。该模型以 99.94%的准确率找到了✨✨!
*现在有一个问题要问你:如此高的精度是否意味着良好的性能?*如果您还记得的话,准确度是真阴性和真阳性之和除以总数据集大小。如果 95%的数据集是负面的(非欺诈),网络将巧妙地预测所有都是负面的,导致 95%的准确性。**然而,对于欺诈检测来说,检测正面比检测负面更重要。因此,我们需要更好的指标。**
图 3 显示了使用测试数据集的混淆矩阵。DNN 的准确率为 85.3%,召回率为 78.9%,F1 得分为 82.0%。大约 20%的欺诈被错误分类为非欺诈,导致客户额外付费,尽管准确率为 99.94%。因此,有足够的空间来改进 DNN 模式📣📣。

图 3 DNN 混淆矩阵
**6。决策树**
> 正如 [Wikipedia](https://en.wikipedia.org/wiki/Decision_tree) 所述,决策树是一个类似流程图的结构,其中每个内部节点代表对一个特征的测试(例如,天气是晴是雨),每个叶子节点是所有测试后得到的类别标签。决策树旨在学习基于条件分割数据集的方法。所以,它是非参数的。
现在让我们建立一个决策树模型。具体来说,
from sklearn.tree import DecisionTreeClassifier
decision_tree_model = DecisionTreeClassifier()
decision_tree_model.fit(X_train, y_train)
y_pred = decision_tree_model.predict(X_test)
图 4 显示了模型的混淆矩阵。决策树给出的准确率为 82.7%,召回率为 74.8%,F1 值为 78.6%,比 DNN 模型差😥😥!

图 3 决策树混淆矩阵
**7。随机森林**
> 从概念上讲,随机森林(RF)是决策树的集合。每棵树投票给一个类,得到最多投票的类是预测的类。决策树是在整个数据集上构建的,而随机森林随机选择要素来构建多个决策树并对结果进行*均。如果你想了解更多关于射频工作原理和参数优化的信息,请阅读这篇[文章](/comparison-and-optimization-on-ml-models-for-loan-application-prediction-675923d83d04)。
具体来说,
from sklearn.ensemble import RandomForestClassifier
rf_model = RandomForestClassifier(n_estimators = 100)
rf_model.fit(X_train, y_train)
y_pred = rf_model.predict(X_test)
训练模型需要几分钟时间。最终,RF 的准确率为 95%,召回率为 77.5%,F1 值为 85.3%,优于 DNN 和决策树模型🤪!

图 3 随机森林混淆矩阵
**8。采样**
**关于我们这里的数据集,一个问题是阶级不*衡。**284,807 笔交易中只有 0.17%是欺诈。可悲的是,该模型对检测多数类比少数类更敏感。一般来说,有两种技术可以解决类不*衡,欠采样和过采样。
8.1 欠采样
最简单的策略是随机选择多数阶级来*衡少数阶级。但局限性在于,从多数类中随机移除的数据可能有助于创建稳健的模型。
我们先实施这个策略。具体来说,
fraud_ind = np.array(data[data.Class == 1].index)
normal_ind = data[data.Class == 0].index
random_normal_ind = np.random.choice(normal_ind, num_frauds, replace = False)
random_normal_ind = np.array(random_normal_ind)
under_sample_data = data.iloc[under_sample_ind, :]X_undersample = under_sample_data.iloc[:, under_sample_data.columns != ‘Class’]
y_undersample = under_sample_data.iloc[:, under_sample_data.columns == ‘Class’]
上面,我们随机选择了与欺诈相同数量的非欺诈,并创建了一个新的数据集。使用缩减后的数据,重新训练 DNN 模型。最终,欠采样给出的准确率为 100%,召回率为 87.6%,f 1 值为 93.4%。比没有欠采样的 DNN 好得多😇!

图 4 欠采样混淆矩阵下的 DNN
8.2 SMOTE
最简单的过采样方法是复制少数类中的数据,但不会向模型中添加新信息。或者,我们可以从现有数据中合成数据,称为合成少数过采样,简称 SMOTE。
> **SMOTE,最初由 Nitesh Chawla 于 2002 年提出,其工作原理是选择特征空间中接*或相似的数据,并在数据之间画一条线,并在该线上的一点制作新数据。**这是有效的,因为新数据接*特征空间中的少数类。如果你想了解 SMOTE,请随意阅读 Nitesh 的[文章](https://arxiv.org/abs/1106.1813)。
现在,让我们实现 SMOTE 并重新训练模型。具体来说,
from imblearn.over_sampling import SMOTE
X_resample, y_resample = SMOTE().fit_sample(X, y)
上面的代码创建了一个*衡的' *y_resample'* ,其中有 284,315 个欺诈和 284,315 个非欺诈。在重新训练 DNN 模型之后,SMOTE 给出了 99.8%的精确度、100%的召回率和 99.9%的 f 1 分数。无论有无欠采样,都比 DNN 好得多😇😇!

图 5 DNN 与 SMOTE 混淆矩阵
**9。总结**
作为总结,我们创建了 5 个模型,DNN,决策树和随机森林,DNN 欠采样,DNN SMOTE。如下表所示,使用 SMOTE 的 DNN 性能最佳。

**太好了!这是所有的旅程。如果你想看代码,看我的资源库** [**这里**](https://github.com/luke4u/Credit-Card-Fraud-Detection) **🤞🤞。**
# 基于机器学习的信用卡欺诈检测
> 原文:<https://towardsdatascience.com/credit-card-fraud-detection-with-machine-learning-6cf8e7c78639?source=collection_archive---------34----------------------->
## 使用编码器-解码器架构检测欺诈交易

由 [Ales Nesetril](https://unsplash.com/@alesnesetril?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
欺诈检测是异常检测的众多案例之一,是金融市场的一个重要方面。有什么办法可以根据交易历史来预测一笔交易是不是欺诈?让我们探索一个神经网络架构,因为它试图预测案件是否为欺诈。到本文结束时,我们将能够使用 Keras 从头构建一个编码器-解码器架构,并将交易分类为欺诈性或非欺诈性。
# 资料组
我们使用 ULB 机器学习小组的数据集信用卡欺诈检测。该数据集包含 28 个匿名变量、1 个“数量”变量、1 个“时间”变量和 1 个目标变量—类。变量被匿名化,以保护客户的隐私,因为数据集是在公共领域。数据集可以在这里找到[。作为目标变量的“0”对应于非欺诈案例,而目标变量中的“1”对应欺诈案例。](https://www.kaggle.com/mlg-ulb/creditcardfraud)
我们导入所需的包
在执行了极简探索性数据分析并测量了数据集是否偏斜之后,我们得出了以下分布结果。

数据集的分布
从上图可以明显看出,我们面临的最大挑战之一是数据集的偏斜。目标阶层高度不*衡,只有 0.17%的数据属于欺诈交易。
对于我们的用例,为了使我们的生活更简单,我们将只取大约 1000 行非欺诈性交易。你可以从[这里](/top-3-methods-for-handling-skewed-data-1334e0debf45)阅读如何统计偏斜的数据点。
# 可视化数据
为了形象化我们的数据分布,我们使用了 T-SNE。T-SNE 或 T-分布式随机邻居嵌入是一种数据分解技术,它将数据减少到特定的维度,并显示具有最大信息的前 n 个维度,这种技术类似于[主成分分析](https://en.wikipedia.org/wiki/Principal_component_analysis)
下图中的每个点代表一个事务。非欺诈交易用绿色表示,而欺诈交易用红色表示。T-SNE 提取的成分用两个轴表示,如下所示。

二维*面中数据点的表示
从上图中,我们可以观察到许多非欺诈交易与欺诈交易以这种螺旋形状重叠,因此很难使用决策边界进行分类。我们将使用一种称为自动编码器的神经网络架构来解决这个问题。
# 编码器-解码器架构
自动编码器或编码器-解码器架构是一种特殊类型的神经网络,其中我们在输入和输出层中具有相同数量的属性,即在输入和输出层中具有相同数量的神经元的架构。自动编码器使用无监督学习下的回归技术来学习输入数据的低级/潜在表示。这些低级表示或“编码”表示被变形或“解码”回原始形状,以投影实际数据。换句话说,期望网络预测它的实际输入。自动编码器在中间包含一个少数神经元的紧密瓶颈,从那里可以获得编码的表示。实际上,在自动编码器中,数据被压缩成低维代码,从这里被解压缩回输入时的实际维度。
我们将创建一个自动编码器模型,在这个模型中,我们将尝试学习非欺诈案例的潜在表示。同样的模型也将用于欺诈案件,我们希望欺诈数据点与非欺诈案件有所不同。
请注意,输入和输出维度是相等的。该架构试图模拟输入的输出。正如我们所见,编码层和解码层之间存在对称性,这是该架构背后的主要思想。
为了避免任何数据管理不当,我们应用数据标准化,并将数据拟合到我们的神经网络中。
这种方法的美妙之处在于,我们不需要太多的数据样本来学习好的表示。
小样本的概念是基于这样一种直觉,即一个类别随后不同于另一个目标类别。我们向我们的网络显示单一类别的数据,以便我们的网络能够区别其他类别。
# 潜在表象
在训练我们的模型之后,我们对找到输入的隐藏或编码表示感兴趣。这可以通过编码层的权重来获得。我们创建另一个仅包含编码层的顺序模型。我们将只添加训练的权重,直到存在潜在表示的第三层。这是为了确保我们能够获得特定情况下的编码,无论是欺诈/非欺诈作为输出。
我们现在的目标是使用获得的隐藏表示创建一个数据集。
“rep_x”包含由相应的“rep_y”变量表示的目标类的编码表示。
现在让我们试着想象这些编码的表示,不管它们是否被区分,既然我们已经获得了编码的表示。

二维*面中编码数据点的表示
我们可以清楚地观察到欺诈和非欺诈交易是可以区分的,事实上在某种程度上是线性可分的。我们现在甚至可以使用更简单的线性模型来预测所需的类别。
选择一个简单的线性分类器是一个想法,可以根据底层算法、计算效率和内存限制进行调整。
由此,我们已经成功地创建了一种编码器-解码器架构,该架构可以被证明是有效的,并且在采用合适的分类器时具有相当的准确性。您可以跟随[这个](https://github.com/milindsahay/Credit_card_fraud_detection) GitHub 库,获得完整的代码和输出。
你还在等什么?出去抓那些坏人。一定要留意好人(误报),他们可能会被诬告,我们都知道“**权力越大,责任越大**”!
# 基于机器学习的信用风险分析
> 原文:<https://towardsdatascience.com/credit-risk-analysis-with-machine-learning-736e87e95996?source=collection_archive---------10----------------------->

迈克尔·朗米尔在 Unsplash[上的照片](https://unsplash.com/)
## 使用 XGBoost、LightGBM 和 CatBoost 预测客户违约风险
***来自《走向数据科学》编辑的提示:*** *虽然我们允许独立作者根据我们的* [*规则和指南*](/questions-96667b06af5) *发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的* [*读者术语*](/readers-terms-b5d780a700a4) *。*
**信用风险**与客户无法履行合同义务的可能性有关,如抵押贷款、信用卡债务和其他类型的贷款。
最大限度地降低违约风险是金融机构主要关心的问题。出于这个原因,商业和投资银行、风险投资基金、资产管理公司和保险公司等越来越依赖技术来预测哪些客户更有可能停止偿还债务。
机器学习模型一直在帮助这些公司提高信用风险分析的准确性,提供了一种提前识别潜在债务人的科学方法。
在本文中,我们将建立一个模型来预测 [Nubank](https://nubank.com.br/en/about-us/) 的客户违约风险,Nubank 是一家著名的巴西金融科技公司。
# 关于数据
Nubank 是一家巴西数字银行,也是拉丁美洲最大的金融科技公司之一。众所周知,它是一家数据驱动的公司,利用技术来制定决策和改善服务。
数据集可以在[这里](http://dl.dropboxusercontent.com/s/xn2a4kzf0zer0xu/acquisition_train.csv?dl=0)下载。一些私人信息被散列以保持数据匿名。
# 数据分析
我们正在为 45,000 名客户处理包含 43 个特征的数据集。`target_default`是一个真/假特征,是我们试图预测的目标变量。在探索数据集之后,我们发现一些特征具有异常值和缺失值。其他不会给模型增加价值的变量都被删除了(你可以在这里查看代码和完整的解释)。
下面的直方图有助于我们可视化数字特征的分布:

图一。数字特征的直方图
上述要素具有需要处理的缺失值。如我们所见,它们呈偏态分布,这表明我们应该用每个要素的中值来填充缺失值。
是时候处理剩余列中缺失的值了。我们根据每个特性的特殊性填充这些值,如下所示:
* 分类变量将用最常出现的值填充。
* 数值变量将用它们的中值填充。
* 在某些特定的情况下,我们将用零填充缺失的值,因为有理由相信不是每个客户端都有分配给这些变量的值。
清理数据集并处理缺失值后,我们现在使用以下功能:

图二。功能列表
在设置机器学习算法之前,我们需要执行一些预处理。考虑到大多数机器学习算法在处理数字输入时效果更好,我们将使用 Scikit Learn 的`LabelEncoder`对二进制变量和 pandas 的`get_dummies`对其他分类变量进行预处理。
# 机器学习模型
我们正在试验以下 3 种梯度增强算法,以确定哪一种能产生更好的结果:
* XGBoost
* LightGBM
* CatBoost
在建立模型之前,我们需要将数据分成训练集和测试集。之后,由于我们正在处理一个不*衡的数据集,我们将分别使用`StandardScaler`和`RandomUnderSampler`对训练集进行标准化和重新采样。
**评估指标**
关于模型的评估,值得一提的是,我们应该考虑将`Precision`、`Recall`和`F1 Score`作为评估指标,原因如下:
* 精度将会给我们正确的肯定识别的比例。它可以定义为:

* **回忆**将确定被正确识别的真实阳性的比例,它可以被定义为:

* **F1 分数**是一个有用的指标,当我们需要在精确度和召回率之间寻求*衡时。该公式定义为:

由于我们的目标是最小化公司损失,预测客户违约的风险,良好的召回率是可取的,因为我们想要确定确实倾向于停止支付债务的客户的最大数量,因此,我们正在追求少量的*假阴性*。
此外,我们还寻求尽量减少误报的数量,因为我们不希望客户被错误地认定为违约者。因此,良好的精确率也是期望的。
然而,在精确度和召回率之间总是有一个折衷。对于本文,我们选择更加强调回忆,将其作为我们的评估标准。
我们还使用交叉验证来获得更好的结果。`cross_validate`方法不是简单地将数据分成训练和测试集,而是将我们的训练数据分成 k 个折叠,从而更好地利用数据。在我们的例子中,我们将执行 5 重交叉验证,因为我们让默认的 k 值。

图三。回忆价值观
请注意,所有三个模型都产生了相似的结果。我们现在将调整模型上的一些超参数,看看我们是否可以实现更高的分值。这里使用的方法是`GridSearchCV`,它将搜索每个估计器的指定参数值。
每个模型的超参数调整如下:
## XGBoost
对于 XGBoost 模型,我们将根据[官方文档](https://xgboost.readthedocs.io/en/latest/parameter.html)调整以下超参数:
* `n_estimators` -模型中的树木数量
* `max_depth` -一棵树的最大深度
* `min_child_weight` -一个孩子所需实例重量的最小总和
* `gamma` -在树的叶节点上进行进一步划分所需的最小损失减少
* `learning_rate` -更新中使用步长收缩来防止过度拟合
param_grid = {'n_estimators': range(0,1000,50),
'max_depth': [1, 3, 5],
'min_child_weight': [1, 3, 6],
'gamma': [0, 1, 5],
'learning_rate': [0.0001, 0.001, 0.01, 0.1]}
*最佳召回率:{'n_estimators': 50,'* max_depth': 3,' min_child_weight': 6,' gamma': 1,' learning _ rate ':0.0001*}*
## LightGBM
现在,转到 LightGBM 模型,另一个基于树的学习算法,我们将参考[文档](https://lightgbm.readthedocs.io/en/latest/Parameters.html)调整以下超参数:
* `max_depth` -一棵树的最大深度
* `learning_rate` -收缩率
* `num_leaves` -一棵树的最大叶子数量
* `min_data_in_leaf` -一片树叶中的最小数据量
param_grid = {'max_depth': np.arange(5, 75, 10),
'learning_rate' : [0.001, 0.01, 0.1],
'num_leaves': np.arange(20, 220, 50),
'min_data_in_leaf': np.arange(100, 1000, 100)}
*最佳召回率:{*' learning _ rate ':0.69,' max _ depth ':0.01,' num_leaves': 70,' min _ data _ in _ leaf ':400*}*
## CatBoost
最后,我们将搜索 CatBoost 的超参数值,这是我们的第三个梯度增强算法。根据[文档](https://catboost.ai/docs/concepts/parameter-tuning.html),将调整以下超参数:
* `depth` -树的深度
* `learning_rate`——我们已经知道,学习率
* `l2_leaf_reg` -成本函数的 L2 正则化项的系数
param_grid = {'depth': [6, 8, 10],
'learning_rate': [0.03, 0.1],
'l2_leaf_reg': [1, 5, 10]}
*最佳召回率:{*' depth ':0.65,' l2_leaf_reg': 5,' learning_rate': 0.03 *}*
## 在测试集上评估模型
调整超参数后,所有三个模型都显示出更好的结果。值得一提的是,XGBoost 的得分有了很大的提高,而 LightGBM 和 CatBoost 的得分则略有提高。
现在,我们可以检查这些模型在**测试集**上的表现。为了帮助我们将结果可视化,我们为每个人绘制了一个**混淆矩阵**。
**XGBoost**

图 4。XGBoost 混淆矩阵
**灯 GBM**

图五。LightGBM 混淆矩阵
**CatBoost**

图六。CatBoost 混淆矩阵
# 结论
这篇文章的主要目标是建立机器学习算法,能够识别潜在的违约者,从而减少公司损失。可能的最佳模式是能够最大限度地减少假阴性,识别客户群中的所有违约者,同时最大限度地减少假阳性,防止客户被错误地归类为违约者。
满足这些要求可能非常棘手,因为在精确度和召回率之间存在权衡,这意味着增加其中一个指标的值通常会降低另一个指标的值。考虑到最小化公司损失的重要性,我们决定更加重视减少假阳性,搜索可以提高召回率的最佳超参数。
在测试的三个**梯度增强算法**中, **XGBoost** 产生了最好的结果,召回率为 81%,尽管它产生了不希望的 56%的假阳性。另一方面, **LightGBM** 和 **CatBoost** 提供了更好的假阳性计数,分别为 38%和 33%,但它们的假阴性大大高于 XGBoost,导致召回率较低。
本文呈现了一个经典的评估指标困境。在这种情况下,将由公司的决策者在机器学习算法的帮助下分析大局,并决定最佳的后续计划。当然,在未来的文章中,我们可以测试不同的方法来实现更理想的结果,例如利用深度学习模型。
完整代码请参考[笔记本](https://github.com/rmpbastos/data_science/blob/master/Credit_Risk_Analysis.ipynb)。
# 信用风险管理:分类模型和超参数调整
> 原文:<https://towardsdatascience.com/credit-risk-management-classification-models-hyperparameter-tuning-d3785edd8371?source=collection_archive---------22----------------------->
最后一部分旨在向您介绍在我们的转换数据集上应用不同分类算法的过程,以及使用超参数调整生成最佳性能模型的过程。

图片来源:[https://unsplash.com/photos/w7ZyuGYNpRQ](https://unsplash.com/photos/w7ZyuGYNpRQ)
提醒一下,这个端到端项目旨在解决数据科学(尤其是金融行业)中的分类问题,分为 3 个部分:
1. 解释性数据分析(EDA)和特征工程
2. 特征缩放和选择(奖励:不*衡数据处理)
3. **机器学习建模(分类)**
如果您错过了前两个部分,请随意查看这里的[](/credit-risk-management-eda-feature-engineering-81cc34efc428)****和这里的[](/credit-risk-management-feature-scaling-selection-b734049867ea)**,然后再看最后一个部分,它利用它们的输出来生成最佳分类模型。******
# ******A.分类模型******
*******应该使用哪种算法来建立一个模型来处理和解决分类问题?*******
******当谈到分类时,我们有相当多的不同算法可以使用,不像回归。举例来说,逻辑回归、K-邻居、SVC、决策树和随机森林是解决这类问题的最常见和最广泛使用的算法。******
******以下是每种算法的功能以及它与其他算法的区别:******
* ********逻辑回归**:该算法利用回归 ***预测一个数据样本的连续概率*** (从 0 到 1),然后将该样本归类到更可能的目标(0 或 1)。但是,它假设输入和目标之间存在线性关系,如果数据集不遵循高斯分布,这可能不是一个好的选择。******
* ******K-Neighbors** :该算法假设彼此非常接*的数据点属于同一类。特别地,它通过 ***距离其较*的邻居*** 的多个投票来对数据样本的目标(0 或 1)进行分类。****
* ******SVC** :该算法通过 ***定义一个决策边界*** 进行分类,然后通过查看数据样本落在边界的哪一侧,将数据样本分类到目标(0 或 1)。本质上,该算法旨在最大化决策边界和每个类中的点之间的距离,以减少错误分类的机会。****
* ******决策树**:顾名思义,这个算法 ***将树的根*** (整个数据集)拆分成决策节点,每个决策节点将被拆分,直到没有进一步的节点可拆分。然后,该算法通过从根到叶/终端节点沿着树对数据样本进行分类,并查看它落在哪个目标节点上。****
* ******随机森林**:该算法是从决策树发展而来的集成技术,其中涉及到许多协同工作的决策树。特别地,随机森林将该数据样本给予每个决策树,并且 ***返回最流行的分类*** 以将目标分配给该数据样本。该算法有助于避免决策树可能出现的过拟合,因为它从多个树而不是一个树聚集分类。****
****让我们比较一下它们如何处理我们的数据集:****
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifierclassifiers = {
"LogisticRegression" : LogisticRegression(),
"KNeighbors" : KNeighborsClassifier(),
"SVC" : SVC(),
"DecisionTree" : DecisionTreeClassifier(),
"RandomForest" : RandomForestClassifier()
}
****从 sklearn 导入算法后,我 ***创建了一个字典,将所有算法合并到一个地方*** ,这样更容易将它们一次应用到数据上,而不需要手动逐个迭代。****
#Compute the training score of each modelstrain_scores = []
test_scores = []for key, classifier in classifiers.items():
classifier.fit(x_a_train_rs_over_pca, y_a_train_over)
train_score = round(classifier.score(x_a_train_rs_over_pca, y_a_train_over),2)
train_scores.append(train_score)
test_score = round(classifier.score(x_a_test_rs_over_pca, y_a_test_over),2)
test_scores.append(test_score)print(train_scores)
print(test_scores)
****在对训练集和测试集应用算法之后,似乎逻辑回归对数据集不太适用,因为分数相对较低(大约 50%,这表明该模型不能对目标进行分类)。这很容易理解,也证明了我们的原始数据集不是正态分布的。****
********
****相比之下,决策树和随机森林在训练集上产生了非常高的准确度分数(85%)。然而,当分数非常低(超过 50%)时,测试集就不同了。解释大差距的可能原因是(1)过度适应训练组,(2)目标泄漏到测试组。然而,经过反复核对,情况似乎并非如此。****
****因此,我决定研究另一个评分标准,**交叉验证分数,**来看看是否有任何差异。基本上,这种技术将训练集分成 n 层(默认值= 5),然后在 n-1 层上拟合数据,并在另一层上评分。这个过程重复 n 次,从中计算出*均分数。与标准准确度分数相比,交叉验证分数 ***带来了关于模型如何工作的更加客观的分析*** 。****
**from sklearn.model_selection import cross_val_scoretrain_cross_scores = []
test_cross_scores = []for key, classifier in classifiers.items():
classifier.fit(x_a_train_rs_over_pca, y_a_train_over)
train_score = cross_val_score(classifier, x_a_train_rs_over_pca, y_a_train_over, cv=5)
train_cross_scores.append(round(train_score.mean(),2))
test_score = cross_val_score(classifier, x_a_test_rs_over_pca, y_a_test_over, cv=5)
test_cross_scores.append(round(test_score.mean(),2))
print(train_cross_scores)
print(test_cross_scores)**
********
****正如所见,培训和测试分数之间的差距明显缩小了!****
****由于随机森林模型产生了最高的交叉验证分数,我们将根据另一个名为 **ROC AUC 分数**的分数指标对其进行测试,并查看其在 **ROC 曲线**上的表现。****
****本质上, **ROC 曲线**是假阳性率(x 轴)相对于真阳性率(y 轴)在阈值 0 和 1 之间的图,而 **AUC** 代表可分离性的程度或度量(简单地说,区分靶的能力)。****
********
****图片鸣谢:[https://towardsdatascience . com/understanding-AUC-roc-curve-68b 2303 cc9 C5](/understanding-auc-roc-curve-68b2303cc9c5)****
****以下是如何计算 **FPR** (特异性反转)和 **TPR** (也称为灵敏度)的快速汇总表:****
********
****图片来源:[https://towardsdatascience.com/hackcvilleds-4636c6c1ba53](/hackcvilleds-4636c6c1ba53)****
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import roc_curve
from sklearn.metrics import roc_auc_scorerf = RandomForestClassifier()
rf.fit(x_a_train_rs_over_pca, y_a_train_over)
rf_pred = cross_val_predict(rf, x_a_test_rs_over_pca, y_a_test_over, cv=5)
print(roc_auc_score(y_a_test_over, rf_pred))#Plot the ROC Curve
fpr, tpr, _ = roc_curve(y_a_test_over, rf_pred)
plt.plot(fpr, tpr)
plt.show()
********
****ROC AUC 得分= 76%的 ROC 曲线****
****因为我已经证明了交叉验证在这个数据集上有效,所以我应用了另一种交叉验证技术,称为“ **cross_val_predict** ”,它遵循类似的方法,即分割 n 个折叠并相应地预测值。****
# ****B.超参数调谐****
*****什么是超参数调整,它如何帮助提高模型的精度?*****
****在根据每个算法的默认估计量计算出模型之后,我希望看到是否可以进一步改进,这可以归结为超参数调整。本质上,这种技术 ***从每个算法中选择一组最优估计器*** ,该算法(可能)在给定的数据集上产生最高的准确度分数。****
****我把(可能)放在定义中的原因是,在某些情况下,很少或没有改善取决于数据集以及最初做的准备(加上它需要永远运行)。但是,应该考虑超参数调整,以期找到性能最佳的模型。****
#Use GridSearchCV to find the best parametersfrom sklearn.model_selection import GridSearchCV#Logistic Regression
lr = LogisticRegression()
lr_params = {"penalty": ['l1', 'l2'], "C": [0.001, 0.01, 0.1, 1, 10, 100, 1000], "solver": ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']}
grid_logistic = GridSearchCV(lr, lr_params)
grid_logistic.fit(x_a_train_rs_over_pca, y_a_train_over)
lr_best = grid_logistic.best_estimator_#KNearest Neighbors
knear = KNeighborsClassifier()
knear_params = {"n_neighbors": list(range(2,7,1)), "algorithm": ['auto', 'ball_tree', 'kd_tree', 'brutle']}
grid_knear = GridSearchCV(knear, knear_params)
grid_knear.fit(x_a_train_rs_over_pca, y_a_train_over)
knear_best = grid_knear.best_estimator_#SVCsvc = SVC()
svc_params = {"C": [0.5, 0.7, 0.9, 1], "kernel":['rbf', 'poly', 'sigmoid', 'linear']}
grid_svc = GridSearchCV(svc, svc_params)
grid_svc.fit(x_a_train_rs_over_pca, y_a_train_over)
svc_best = grid_svc.best_estimator_#Decision Treetree = DecisionTreeClassifier()
tree_params = {"criterion": ['gini', 'entropy'], "max_depth":list(range(2,5,1)), "min_samples_leaf":list(range(5,7,1))}
grid_tree = GridSearchCV(tree, tree_params)
grid_tree.fit(x_a_train_rs_over_pca, y_a_train_over)
tree_best = grid_tree.best_estimator_
******GridSearchCV** 是在每个算法中找到最优估计量集合的关键,因为它仔细检查并组合不同的估计量以适应数据集,然后返回所有估计量中的最佳集合。****
****值得注意的一点是,我们必须记住每种算法的所有可用估计量,以便能够使用。例如,对于逻辑回归,我们有一组不属于其他算法的“惩罚”、“C”和“求解器”。****
****找到**后。每个算法的 best_estimator_** 使用每个算法的最佳集合来拟合和预测数据。但是,我们需要将新的分数与原始分数进行比较,以确定是否有任何改进,或者继续再次微调估计值。****
# ****奖励:XGBoost 和 LightGBM****
****【XGBoost 和 LightGBM 是什么?与传统算法相比,这些算法的性能有多好?****
****除了我听说过的常见分类算法,我还知道一些源于传统的高级算法。在这种情况下, **XGBoost 和 LightGBM** 可以被认为是*决策和随机森林的继承者。*为了更好地理解这些算法是如何开发出来的,请看下面的时间表:****
****************
****图片鸣谢:[https://www . slide share . net/GabrielCyprianoSaca/xgboost-light GBM](https://www.slideshare.net/GabrielCyprianoSaca/xgboost-lightgbm)****
****我不打算详细说明这些算法在数学上有何不同,但总的来说,它们能够在处理缺失值的同时更好地 ***修剪决策树+同时避免过度拟合。*******
#XGBoost
import xgboost as xgbxgb_model = xgb.XGBClassifier()
xgb_model.fit(x_a_train_rs_over_pca, y_a_train_over)
xgb_train_score = cross_val_score(xgb_model, x_a_train_rs_over_pca, y_a_train_over, cv=5)
xgb_test_score = cross_val_score(xgb_model, x_a_test_rs_over_pca, y_a_test_over, cv=5)print(round(xgb_train_score.mean(),2))
print(round(xgb_test_score.mean(),2))#LightGBM
import lightgbm as lgblgb_model = lgb.LGBMClassifier()
lgb_model.fit(x_a_train_rs_over_pca, y_a_train_over)
lgb_train_score = cross_val_score(lgb_model, x_a_train_rs_over_pca, y_a_train_over, cv=5)
lgb_test_score = cross_val_score(lgb_model, x_a_test_rs_over_pca, y_a_test_over, cv=5)print(round(lgb_train_score.mean(),2))
print(round(lgb_test_score.mean(),2))
****经过计算,每个模型的训练和集合分数分别为 72% & 73% (XGBoost)和 69% & 72% (LightGBM),与上面计算的随机森林模型相对相同。然而,我们仍然能够通过对这些高级模型进行超参数调整来进行进一步优化,但要注意,这可能需要很长时间,因为 XGBoost 和 LightGBM 由于其算法的复杂性而具有更长的运行时间。****
****瞧啊。这就是这个端到端项目关于分类的总结!如果您热衷于探索整个代码,请随时查看我下面的 Github:****
****知识库:[https://github.com/andrewnguyen07/credit-risk-management](https://github.com/andrewnguyen07/credit-risk-management)领英:[www.linkedin.com/in/andrewnguyen07](http://www.linkedin.com/in/andrewnguyen07)****
****关注我的媒体,关注即将到来的未来项目!****
# 信用风险管理:EDA 和特征工程
> 原文:<https://towardsdatascience.com/credit-risk-management-eda-feature-engineering-81cc34efc428?source=collection_archive---------20----------------------->
## 这一部分从如何使用 EDA 和特征工程技术对数据进行清理和预处理开始,特别是在接触和不接触“目标”变量的情况下。

图片来源:[https://unsplash.com/photos/g7NfqV6C074](https://unsplash.com/photos/g7NfqV6C074)
# 语境
*在金融行业中,数据科学可以提供巨大帮助的常见用例有哪些?*
信用评分卡是金融行业中常见的风险控制方法之一,它使用个人信息和交易记录来识别和评估现有和潜在客户的信誉。有许多不同的用例利用这种方法,如贷款管理、信用卡审批、信用额度扩展等。
也就是说,这个项目的适用性因金融机构面临的问题而异。使该项目可用的核心引擎是输入数据的处理和转换,以从现有/新输入中产生高度可预测性的输出,从而最好地解决问题。
# 目录
该端到端项目分为 3 个部分:
1. 解释性数据分析(EDA)和特征工程
2. 特征缩放和选择(奖励:不*衡数据处理)
3. 机器学习建模(分类)
***注* :** 由于该项目旨在提高我在数据科学方面的能力,简而言之,为了自学和自我提高,该项目将把数据集分成 2 个更小的子集来测试哪个产生更好的结果,而不是只应用性能最好的技术。
因此,让我们从项目的第一部分开始:EDA &特性工程
# A.解释性数据分析(EDA)
让我们导入必要的库和两个数据集:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as snsapplication = pd.read_csv("application_record.csv")
credit = pd.read_csv("credit_record.csv")

应用数据集

信用数据集
如上所述,应用数据集提供现有银行客户提交的*个人信息中的所有数据点(如身份证、性别、收入等。),信用数据集将每个对应的 id 与他/她的 ***贷款偿还状态*** 进行映射(例如,X 表示当月无贷款,C 表示已还清,> 0 表示逾期还款月数)。*
*为了更好地使用信用信息,我清理了数据集,将“Status”列转换为数字,并按客户 ID 和最*一个月进行分组:*
credit.status = credit.status.replace({'X':-2, 'C': -1})
credit.status = credit.status.astype('int')
credit.status = credit.status.apply(lambda x:x+1) credit_month = credit.groupby('id').months_balance.max().reset_index()record = pd.merge(credit_month, credit, how="inner", on=["id", "months_balance"])record.head()
*当一切都设置好后,我就利用“内部合并”将新处理的数据集与应用程序结合起来。在此之上,如果你回头参考原始数据集,“出生日期”和“就业”是从今天开始向后算的天数,这在最初有点难以理解。因此,我决定将这些变量转换成正数和年份。*
df['age'] = df.birth_date.apply(lambda x: round(x/-365,0))
df['year_of_employment'] = df.employment.apply(lambda x: round(x/-365,0) if x<0 else 0)df = df.drop(columns=["birth_date","employment"])
*继续,我建议你永远不要忽略的每个 EDA 的两个亮点是(1) ***检查空值*** 和(2) ***处理异常值*** 。前者确保我们在处理和插入建模之前有一个 100%干净的数据集,而后者有助于避免您的数据集因边缘极端异常值而过度倾斜。*
df.isnull().sum()
df.occupation_type = df.occupation_type.fillna("Others")
*“职业类型”是唯一具有空值(NaN)的变量,所以我继续用“其他”填充这些值。*
*使用 **df.describe** 和 **sns.boxplot** ,我能够发现“年收入”和“Fam 成员”是数据集中有异常值的两个变量,直观显示如下:*
**
*为了消除异常值,我编写了一个函数,它可以很容易地应用于具有类似问题的变量:*
def remove_outlier(col):
q25 = col.quantile(0.25)
q75 = col.quantile(0.75)
iqr = q75 - q25
cutoff = iqr1.5
lower = q25 - cutoff
upper = q75 + cutoff
return lower, upper#Remove outliers for Annual Incomelower_1, upper_1 = remove_outlier(df.annual_income)
df = df.loc[(df.annual_income > lower_1) & (df.annual_income < upper_1)] #Remove outliers for Fam Memberslower_2, upper_2 = remove_outlier(df.fam_members)
df = df.loc[(df.annual_fam_members > lower_2) & (df.fam_members < upper_2)]*
**
*我们几乎已经完成了 EDA,接下来要定义“目标”变量,您可能在大多数分类课程中听过这个变量。*
*回到这个项目的主题,信用风险管理,我们需要确定我们应该如何处理客户的贷款偿还状态。有了这个数据集,我为那些当月没有任何贷款或没有还清贷款的人定义了“target = 0”,而剩余的数据(任何逾期贷款)被映射到“target = 1”。*
df['target'] = None
df.loc[df.status < 1,'target']=0
df.loc[df.status >= 1,'target']=1
df.target = pd.to_numeric(df.target)
# *B.特征工程*
**什么是特征工程,它在建模前对数据预处理有什么帮助?**
*根据[维基百科](https://en.wikipedia.org/wiki/Feature_engineering),*
> *特征工程是利用领域知识通过数据挖掘技术从原始数据中提取特征的过程。这些特征可以用来提高机器学习算法的性能。*
*事实上,特征工程不仅需要领域知识,还需要对数据集的理解和实现的目标。特别地,对于我们的数据集,有相当多的不同特征,我们称之为**自变量**,它们与还款状态相关,这就是**目标变量(0 或 1)** 。因此,为了调整出一个有洞察力和可操作的模型,我们需要通过转换现有的和/或添加支持数据来“工程化”那些特性,这使得特性工程不同于 EDA。*
*正如我从一开始就提到的,我们永远不会知道哪种方法更好,直到我们进行测试。也就是说,我决定测试两个场景,有的**和没有**接触目标变量的**,然后看看以后产生的结果是否有任何显著的不同。***
*在深入实现之前,我们应该意识到,对于特征工程来说,没有“一刀切”的技术,因为它取决于您如何处理您的特征。在这个项目中,我在我的数据集中利用了“**类别编码**,因为大多数数据都是分类的,应该转换为数字,以便在大多数机器学习模型中更容易处理。*
df_a = df #for encoding without target
df_b = df #for encoding with targetx_a = df_a.iloc[:, 1:-1]
y_a = df_a.iloc[:, -1]from sklearn.model_selection import train_test_splitx_a_train, x_a_test, y_a_train, y_a_test = train_test_split(x_a, y_a, test_size=0.3, random_state=1)
*我们为两个场景创建了两个独立的数据集,这样我们就可以操作每个数据集,而不用担心会混淆。*
*此外,**在处理之前需要注意的一个重要亮点是,强烈建议我们**将数据集**拆分为训练集和测试集**,以避免数据泄露**。本质上,如果我们在处理后进行分割,测试集的数据就会暴露出来,因此在建模阶段与训练集进行比较时不够客观。***
# *1.无目标分类编码*
**根据变量类型,我们将对每个变量应用合适的技术。**
*如果你回头参考数据集,有 3 种类型的变量:(1)二元,(2)名义和(3)连续。虽然二进制和连续变量是不言自明的,但名义变量指的是一组不同的类别,它们之间没有内在的顺序。*
## *1.1.二元变量*
*对于我们数据集中的二进制变量(如性别、汽车、财产),我们可以从 sklearn 库中选择**标签编码器**或**标签二进制化器**,这将 ***将原始数据映射为 0 或 1:****
#Option 1: Label Encoder (applied to >2 categories per variable)
from sklearn.preprocessing import LabelEncoder, LabelBinarizerle = LabelEncoder()
gender_le = le.fit_transform(x_a_train.gender)#Option 2: LabelBinarizer (applied to 2 categories per variable)bn = LabelBinarizer()
gender_bn = np.array(x_a_train.gender).reshape(-1,1)
gender_bn = bn.fit_transform(gender_bn)
## *1.2.名义可变因素*
*名义变量(如收入类型、教育、家庭状况、住房类型、职业类型)是分类变量,需要在建模前转换为数字变量。对类别进行编码的两种常见方法是(1) **虚拟编码**和(2) **一个热编码器**,它基本上创建 n 列作为该变量内的 n 个唯一类别,并根据每列中每个类别的存在与否分配 0 或 1。*
**
*图片来源:[https://deepai . org/machine-learning-glossary-and-terms/dummy-variable](https://deepai.org/machine-learning-glossary-and-terms/dummy-variable)*
*这些方法之间的区别在于伪编码转换成 n-1 个子变量,而一个热编码器转换成 n 个子变量。*
#Option 1: Dummy Encoding: kn - k variablesincome_type_dummy = pd.get_dummies(x_a_train.income_type)#Option 2: OneHotEcnoder: kn variablesfrom sklearn.preprocessing import OneHotEncoderonehot = OneHotEncoder(sparse=False, drop='first', handle_unknown='error')income_type_onehot = onehot.fit_transform(x_a_train.income_type.to_numpy().reshape(-1,1))
income_type_onehot = pd.DataFrame(income_type_onehot, columns=onehot.get_feature_names(['income_type']))
*虚拟编码可以通过 **pd.get_dummies()** 轻松完成,因为它已经是 pandas 库的一部分。对于一个 Hot 编码器,我们需要从 sklearn 库中导入它,并单独或同时转换每个变量。*
*一个热编码器被设计为保持跨训练和测试集的类别数量的一致性(例如,处理没有出现在任何一个集合中的类别),因此它比哑编码更值得推荐,因为它更容易用“ **handle_unknown= "error"** ”来控制。*
*然而,一个热门编码器的缺点之一是**多重共线性**,它指的是变量或子变量之间高度线性相关,因此降低了我们模型的准确性。这可以通过分配参数“**drop =‘first’**”来纠正或避免,这有助于在编码后删除其中一个子变量。*
## *1.3.连续变量*
*连续变量是在任意两个值之间有无限个值的数值变量。从本质上来说,它需要永远计数!让我们直观地看看数据集中每个连续变量的分布情况:*
**
*左图显示了客户的年龄范围,而右图显示了不同收入群体的分布情况。处理这类变量的两种常用方法是(1) **固定宽度宁滨**和(2) **自适应宁滨**。特别是,前者从预定义的箱(例如年龄-10-20、20-30、30-40 等)创建子类别,而后者依赖于数据的分布。*
*固定宽度宁滨的优点和缺点是:对变量进行编码**容易且简单**但是相对主观而不考虑数据本身。因此,我建议选择密切关注数据分布的自适应宁滨。根据我的观察,我决定采用“**分位数**”,而不是转换成 2-bin 类别,因为原始分布范围很广,**之后应用了标签编码**。*
#Convert each variable into 5 equal categories/eachx_a_train['age_binned'] = pd.qcut(x_a_train.age, q=[0, .25, .50, .75, 1])
x_a_train['annual_income_binned'] = pd.qcut(x_a_train.annual_income, q=[0, .25, .50, .75, 1])#Apply Label Encoder to assign the label to each category without biasx_a_train['age'] = le.fit_transform(x_a_train['age_binned'])
x_a_train['annual_income'] = le.fit_transform(x_a_train['annual_income_binned'])
*Tada!我们已经设计了所有必要的变量,但从未触及目标变量!现在,让我们在将每一个拟合到建模阶段之前,移动到另一个。*
# *2.带目标的类别编码*
*由于在这种方法中利用了与目标变量的相关性,为了更好的客观性,我们应该以相同的方式对所有自变量进行编码。*
*同样,必须遵循先决条件:处理前的训练测试分割*
x_b = df_b.iloc[:, 1:-1]
y_b = df_b.iloc[:, -1]from sklearn.model_selection import train_test_splitx_b_train, x_b_test, y_b_train, y_b_test = train_test_split(x_b, y_b, test_size=0.3, random_state=1)
*在涉及目标的类别编码技术中,我发现了 3 个普遍使用的选项:(1) **证据权重编码器(WOE)** , (2) **目标编码器**,( 3)**留一编码器(LOO)** 。*
*简而言之,*
* ***WOE 编码器**:证据权重编码是信用风险建模中广泛使用的一种技术,它可以获得与目标相关的每个变量中不同类别之间的最大差异。这很容易理解,其数学计算如下——好的百分比(在这种情况下,目标= 0) /坏的百分比(目标= 1)的自然对数:*
**
* ***目标编码器& LOO 编码器**:前一种方法用数据集中所有行的目标变量的*均值替换分类值,后一种方法做了同样的事情,但排除了行本身。原因是为了避免在建模前使用太多信息而导致直接目标泄漏。*
#Option 1: WOE Encoder
import category_encoders as cewoe = ce.WOEEncoder()def woe_encoder(col, target):
for i in range(len(x_b_train.columns)):
col.iloc[:,i] = woe.fit_transform(col, target)
return coldf_woe_train = woe_encoder(x_b_train, y_b_train) #Option 2: Target Encoder
from category_encoders import TargetEncoderte = TargetEncoder()def target_encoder(col, target):
for i in range(len(x_b_train.columns)):
col.iloc[:,i] = te.fit_transform(col, target)
return coldf_te_train = target_encoder(x_b_train, y_b_train)
*在测试了这两种方法之后,两者之间新编码的数据集似乎没有太大的区别。因此,对于这个项目的后续步骤,我选择了带有 WOE 编码器的数据集。**但是**,请在其他数据集上进行测试,这可能会由于不同的数据分布而产生不同的结果。*
**
*WOE 编码器*
**
*目标编码器*
*瞧啊。这是这个项目第一部分的总结!*
*如上所述,该项目是作为学习笔记和提高我的数据科学技能的过程中的亮点的存储库而创建的。因此,我在每一部分都测试了多种方法,以便找出最好的执行技术。*
*请务必注意接下来的两个部分,它们涵盖了**特征缩放/选择**和**机器学习建模**!与此同时,我们来连线:*
*github:[https://github.com/andrewnguyen07](https://github.com/andrewnguyen07)
领英:[www.linkedin.com/in/andrewnguyen07](http://www.linkedin.com/in/andrewnguyen07)*
*谢谢!*
# 信用风险管理:特征缩放和选择
> 原文:<https://towardsdatascience.com/credit-risk-management-feature-scaling-selection-b734049867ea?source=collection_archive---------26----------------------->
在特征工程之后,这一部分进入数据准备过程的下一步:特征缩放和选择,在建模之前将数据集转换为更易消化的数据集。

图片来源:[https://unsplash.com/photos/JG35CpZLfVs](https://unsplash.com/photos/JG35CpZLfVs)
提醒一下,这个端到端项目旨在解决数据科学(尤其是金融行业)中的分类问题,分为 3 个部分:
1. 解释性数据分析(EDA)和特征工程
2. **特征缩放和选择(奖励:不*衡数据处理)**
3. 机器学习建模(分类)
如果你错过了第一部分,在阅读接下来的第二部分之前,请在这里查看[](/credit-risk-management-eda-feature-engineering-81cc34efc428)**,以便更好地理解上下文。**
# **A.特征缩放**
***什么是特征缩放,为什么我们在建模之前需要它?***
**根据维基百科,**
> **特征缩放是一种用于**标准化**独立变量或数据特征范围的方法。在数据处理中,它也被称为数据规范化,通常在数据预处理步骤中执行。**
**如果您还记得第一部分,我们已经在两个数据集(A & B)上完成了所有特征的工程设计,如下所示:**
****
**数据集 A(无目标编码)**
****
**数据集 B(用目标编码)**
**如上所述,所有特征之间的数据范围和分布彼此相对不同,更不用说一些带有异常值的变量了。也就是说,强烈建议我们对整个数据集一致地应用特征缩放,以使其更容易被机器学习算法消化。**
**事实上,市场上有许多不同的方法,但我将只关注我认为相对有特色的三种:**标准缩放器**、**最小最大缩放器**和**鲁棒缩放器**。简而言之,**
* ****StandardScaler** :该方法移除*均值,并将数据缩放至单位方差(*均值= 0,标准差= 1)。然而,它受异常值的影响很大,特别是那些边缘极端的异常值,这些异常值会将缩放后的数据范围扩展到超过 1 个标准偏差。**
* ****MinMaxScaler** :该方法减去特征中的最小值,再除以范围(即原始最大值和最小值之差)。本质上,它将数据集重新调整到 0 和 1 的范围内。然而,这种方法相对有限,因为它将所有数据点压缩到一个狭窄的范围内,并且在存在异常值的情况下帮助不大。**
* ****RobustScaler** :该方法基于百分位数,减去中位数,除以四分位数范围(75% — 25%)。它通常比其他两种缩放器更可取,因为它不会受到大的边缘异常值(如果有的话)的很大影响。**
**让我们看看三个定标器在我们的数据集中有何不同:**
from sklearn.preprocessing import StandardScaler, RobustScaler, MinMaxScaler#StandardScaler
x_a_train_ss = pd.DataFrame(StandardScaler().fit_transform(x_a_train), columns=x_a_train.columns)#MinMaxScaler
x_a_train_mm = pd.DataFrame(MinMaxScaler().fit_transform(x_a_train), columns=x_a_train.columns)#RobustScaler
x_a_train_rs = pd.DataFrame(RobustScaler().fit_transform(x_a_train), columns=x_a_train.columns)
****
**标准缩放器**
****
**最小最大缩放器**
****
**鲁棒定标器**
**如上所述,RobustScaler 的缩放数据范围看起来比其他两个缩放器更容易理解,这可能有助于机器学习算法更快、更有效地推动处理运行时间。然而,这是我在建模之前的假设,但是我们可以在那个阶段进行试验。**
# **B.不*衡数据处理**
***什么是不*衡数据,我们应该如何处理?***
**简而言之,不*衡数据集是指目标分布存在严重偏差的数据集,这对于建模来说可能并不理想。作为一个更清楚的例子,让我们看看我们的数据集是否不*衡:**
a_target_0 = df_a[df_a.target == 0].target.count() / df_a.target.count()
a_target_1 = df_a[df_a.target == 1].target.count() / df_a.target.count()
**结果是 76%的数据被归类为目标 0,而剩余的 24%被归类为目标 1。**
**事实上,没有一个清晰的基准,我们可以依靠它来准确地确定我们的数据集是否不*衡。有些人说是 9:1,而有些人说是 8:2,这实际上取决于数据集的性质以及您正在解决的环境/问题。在我的例子中,我认为上述结果是不*衡的,并将对数据集进行“重新采样”以使其相对*衡。**
*****只是一个免责声明*** ,我在这里采取的所有预处理步骤并不意味着它们都是必须要做的,并且对我们以后的模型的准确性有积极的影响。这意味着我的目标是测试所有可能有助于改进我的模型的场景。**
**回到重采样,我们可能听说过两种常见的方法:过采样和欠采样。简而言之,**
* ****过采样**:该方法从少数类中复制样本,并将它们添加到数据集(训练集)。**
* ****欠采样**:这与上面从多数类中删除一些样本相反。**
**视觉上,你可以想象成这样:**
****
**图片鸣谢:[https://towards data science . com/having-an-unbalanced-dataset-here-is-how-you-can-solve-it-1640568947 EB](/having-an-imbalanced-dataset-here-is-how-you-can-solve-it-1640568947eb)**
**让我们测试一下这两者:**
Under-sampling
from imblearn.under_sampling import RandomUnderSamplerundersample = RandomUnderSampler()x_a_train_rs_under, y_a_train_under = undersample.fit_resample(x_a_train_rs, y_a_train)
print(Counter(y_a_train_under))# Over-sampling
from imblearn.over_sampling import SMOTE
from collections import Counteroversample = SMOTE()x_a_train_rs_over, y_a_train_over = oversample.fit_resample(x_a_train_rs, y_a_train)
print(Counter(y_a_train_over))
**对于每种方法,都有多种选择来对数据集进行重采样,但我选择了最常用的一种,即 **RandomUnderSampler** (欠采样)和 **SMOTE** (过采样)。重采样后的类分布为:**
* **随机欠采样:0: 5814,1: 5814**
* **SMOTE: 1: 18324,0: 18324**
**两者各有利弊,但是顾名思义,RandomUnderSampler 从多数类中“随机”选择要移除的数据,这可能会导致信息丢失,因为不是整个数据集都被纳入建模。也就是说,我选择了 SMOTE。**
# **C.特征选择**
**什么是功能选择,有哪些选项可供我们参考?**
**简而言之,特征选择是在数据集中选择与目标变量有很大关联/影响的变量的过程。特别是,当涉及到更大的数据集时,我们可能会面对数百个特征,其中一些可能不相关,甚至与输出无关。因此,我们需要在建模之前进行特征选择,以达到最高的精度。**
**事实上,有一些不同的技术可以归为两大类:(1) **特征选择**和(2) **维度缩减**。我相信这些名字对你来说听起来很熟悉,但本质上它们是相同的,只是为每一个做的技术相对不同。**
## **1.特征选择**
**如果你正在寻找一个完整的技巧列表,请随意参考[这篇博客文章](https://www.analyticsvidhya.com/blog/2018/08/dimensionality-reduction-techniques-python/),它列出了所有可能的试验方法。在这个项目中,为了简单起见,我将只应用两个:(a) **特征重要性**和(b) **相关矩阵**。**
**对于**特征重要性**,顾名思义,我们将选择与目标变量相关率最高的前几个特征(如前 10 或 15,取决于特征总数)。具体来说,该技术部署了 ExtraTreeClassifier 算法的属性: **feature_importances_****
from sklearn.ensemble import ExtraTreesClassifierfi = ExtraTreesClassifier()
fi_a = fi.fit(x_a_train_rs_over, y_a_train_over)df_fi_a = pd.DataFrame(fi_a.feature_importances_,index=x_a_train_rs_over.columns)
df_fi_a.nlargest(10,df_fi_a.columns).plot(kind='barh')
plt.show()
****
**。功能 _ 重要性 _**
**如你所见,年收入是最重要的特征,其次是年龄和就业年份。事实上,这真的取决于你要选择的特性的数量。**
**转到特征选择的第二种方法,**相关矩阵**是显示数据集中变量之间相关系数的表格。本质上,数字越高,任何两个变量之间的相关性越强。**
**为了更好地展示,让我们更直观地看看:**
df_b_train_processed = pd.concat([x_b_train_rs_over, y_b_train_over], axis=1) #combine processed features with their targetcm_b = df_b_train_processed.corr()print(cm_b.target.sort_values().tail(10))plt.figure(figsize=(20,20))
sns.heatmap(cm_b, xticklabels=df_b_train_processed.columns, yticklabels=df_b_train_processed.columns,annot=True)
****
**df.corr()**
**如上所述,我们只需要考虑表中的最后一列,即所有自变量与目标的相关性。看起来所有的特征与相同色调的目标共享相似的系数。这可能与我们刚刚提到的(1)特性重要性略有不同。然而,没有明确的对错答案,因为每种技术的设计和功能都不同。**
## **2.降维**
**降维基本类似于特征选择,但有自己的技术。我经常使用的常见选项可以分为基于组件的(PCA)和基于投影的(t-SNE 和 UMAP)。**
* ****基于组件(PCA)** :顾名思义,它基于数据集中的原始特征,这些特征被转换成一组与目标具有更好相关性的新变量。**
* ****基于投影(t-SNE 和 UMAP)** :这种技术背后的数学概念很复杂,但简而言之,它指的是将多维数据投影到一个更低维的空间,这有助于减少数据集中的特征数量。**
**请记住,使用这些技术时需要进行特征缩放!**
from sklearn.decomposition import PCApca = PCA(.95)pca_a_train = pca.fit(x_a_train_rs_over, y_a_train_over)
print(pca_a_train.n_components_)plt.plot(np.cumsum(pca_a_train.explained_variance_ratio_))
plt.show()x_a_train_rs_over_pca = pd.DataFrame(pca_a_train.transform(x_a_train_rs_over))
x_a_train_rs_over_pca.head()
****
**至于 PCA,当我调用语法时,我将 PCA 的解释方差设置为. 95,这意味着我希望得到一组新的变量,它与原始变量相比有 95%的方差。在这种情况下,在我们将训练数据拟合到 PCA 之后,计算出我们只需要 46 个特征中的 24 个。此外,查看解释方差比率图,在 24 个特征之后,线停止增加,这可能是应用 PCA 后的理想特征数。**
****
**pca.fit_transform()**
**至于基于投影的,t-SNE 对大数据集很好,但它被证明有局限性,特别是计算时间低和大规模信息丢失,而 UMAP 在运行时方面表现更好,同时保留了信息。**
**简而言之,UMAP 的工作原理是,它先计算高维空间中的点与点之间的距离,投影到低维空间中,再计算这个低维空间中的点与点之间的距离。然后,它使用随机梯度下降来最小化这些距离之间的差异。**
import umapum = umap.UMAP(n_components=24)umap_a_train = um.fit_transform(x_a_train_rs_over)
x_a_train_rs_over_umap = pd.DataFrame(umap_a_train)
x_a_train_rs_over_umap.head()
****
**umap。UMAP.fit_transform()**
**为了在性能方面比较 PCA 和 UMAP,它依赖于我们的数据集的规模和复杂性,以便确定使用哪一个。然而,为了简单和更好的运行时间,我选择在数据集之间应用 PCA,并在建模阶段利用它。**
**瞧啊。我们已经完成了这个端到端项目的第二部分,重点是功能缩放和选择!**
**我真的希望你能发现它内容丰富且易于理解,所以请在这里留下你的评论吧!请注意这个项目的第三个最后部分,它利用所有的数据准备步骤来应用**机器学习算法**。**
**与此同时,我们来连线:**
**github:[https://github.com/andrewnguyen07](https://github.com/andrewnguyen07)
领英:[www.linkedin.com/in/andrewnguyen07](http://www.linkedin.com/in/andrewnguyen07)**
**谢谢!**
# 基于机器学习的信用风险建模
> 原文:<https://towardsdatascience.com/credit-risk-modeling-with-machine-learning-8c8a2657b4c4?source=collection_archive---------0----------------------->
## 真实世界的机器学习解决方案是什么样的——不需要背景知识
***注来自《走向数据科学》的编辑:*** *虽然我们允许独立作者根据我们的* [*规则和指导方针*](/questions-96667b06af5) *发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的* [*读者术语*](/readers-terms-b5d780a700a4) *。*
**信用风险建模**——估计某人偿还贷款的概率的过程——是现代世界最重要的数学问题之一。在本文中,我们将从头开始探索如何将**机器学习**应用于信用风险建模。你不需要了解任何关于机器学习的知识就能理解这篇文章!
为了用机器学习解释信用风险建模,我们将首先发展关于信用风险建模的领域知识。然后,我们将介绍四种可用于信用风险建模的基本机器学习系统:
* k-最*邻
* 逻辑回归
* 决策树
* 神经网络
到本文结束时,您将理解这些算法如何应用于信用风险建模的现实世界问题,并且您将在总体上理解机器学习领域!
让我们从一个简单的例子开始学习什么是信用风险建模。
# 形势
假设你的朋友泰德需要十块钱。你会想要回那些钱的,所以他保证明天你再见到他时会还你的。

作者创建的图像
但是你听说过 Ted 有一段难以回报他人的历史。结果,你知道有可能你再也看不到你的十美元了。
你会借钱给他吗?
要做这个决定,你需要另一条信息。
泰德过去有多困难?
如果他只是忘了还钱给一个不该还钱的人,那么你可能是个好人。另一方面,如果他有深陷债务和逃往其他国家的习惯,那么你可能应该把你的钱留给自己。
泰德的历史告诉我们一些关于借钱给他的风险。
如果他过去值得信任,那么借钱给他的风险相对较低。如果他过去不值得信任,那么借钱给他的风险就相对较高。
风险的概念给了你一个合理的方法来决定是否借钱给泰德。首先,确定你可以接受多大的风险。然后,弄清楚泰德的历史让借钱给他有多大风险。如果借钱给泰德的风险不超过你的最大承受风险,那么你可以继续借钱给他。
如果只是 ted 向你要钱,你可能会依靠你的直觉和感觉来判断借钱给 Ted 的风险。这对一个人来说已经足够了。

作者创建的图像
但是如果有上百个 ted 向你要钱呢?

作者创建的图像
你不可能了解这些人中的每一个人,这样你就可以决定借钱给他们是不是一个好主意。但是如果你只是随便借给他们,你可能会借钱给一些永远不会还你钱的人。你也可能拒绝一些非常负责任的人,他们会很快还你钱。相反,你会想用一个更科学的过程。
这个科学过程被称为**信用风险建模**,这也是我们将在本文中探讨的内容。
从形式上来说,**信用风险建模**是利用一个人的数据来确定这个人偿还贷款的可能性的过程。根据流程的名称,信用卡公司一直在进行信用风险建模就不足为奇了。但是信用风险建模并不一定与信用卡有任何关系,即使“信用”在名称中。信用风险建模适用于*任何*贷款,而不仅仅是与信用卡相关的贷款。
无数组织使用信用风险建模,包括保险公司、银行、投资公司和政府财政部。有时,个人利用信用风险模型战略性地借出他们自己的钱来谋生。信用风险建模在人们借钱的任何地方都是极其重要的。
更重要的是,理解信用风险建模的过程将使理解任何涉及概率的建模变得更加容易。
但是现在,让我们回到 Ted,这样我们就能对贷款的基本原理有一个坚实的把握。
# **什么是利率?**
如果你只是出于好意借钱给泰德,你可能会要求他还你同样多的钱。但是你也可以借此机会赚点外快。
你可以告诉泰德,他必须还你和你给他一样多的钱,外加百分之十。
这意味着如果你给泰德 10 美元,他必须还你 11 美元。他多给你的钱叫做利息。在这种情况下,Ted 付给你 1 美元的利息,而**利率**是百分之十。如果你把利率提高到 20%,特德将不得不付给你 2 美元的利息,这意味着他总共要付给你 12 美元。
如果泰德真的需要钱,那么他可能会同意支付一些利息。*支付贷款利息基本上是购买你未来花钱的能力。*

作者创建的图像
把未来的钱花在现在是一件方便的事情,就像商业世界中所有方便的事情一样,它是有代价的。
关于利率,你可以看到 500 个泰德同时向你要钱是多么有用。如果你向他们收取 20%的利息,那么借给他们每个人 10 美元可以让你赚 1000 美元。
但我们通过一个相当大的——也是无效的——假设得出了 1000 美元的数字:每个人都会还你钱。
如果有人不还你钱,你就永远失去了你给他们的 10 美元。你也错过了他们会付给你的 2 美元利息。这意味着,每有一个人不还你钱,你就少了 12 美元。
如果 500 人中只有 84 人不还你钱,那么实际上你会在整个借贷过程中赔钱。令人惊讶的是,只有不到 17%的人把整个系统搞砸了。
为了以此为生,你需要一个更好的系统。
# **什么是违约风险?**
幸运的是,信用风险模型可以让你估计每个人无法偿还你的概率。**拖欠**贷款意味着无法偿还,所以每个人无法偿还贷款的概率被称为他们的**违约风险**。
确定某人的违约风险很重要。一旦你知道某人的违约风险,有一种方法可以校准他们的利率,以减轻借钱给他们的风险。
但在我们理解如何利用违约风险来校准某人的利率之前,理解一个基本的统计学概念是很重要的:大数定律。
# **大数定律**
先说一个简单的情况:抛硬币。
我们都听说过有 50%的机会得到正面和 50%的机会得到反面。这叫做**理论概率**。
如果你抛一次硬币,那么你要么是正面要么是反面。如果你掷硬币 10 次,你会得到 5 个正面和 5 个反面(50%正面)。但也完全有可能你得到 6 头 4 尾(60%正面),甚至 9 头 1 尾(90%正面)。
这些观察到的结果被称为**实验概率**。实验概率不一定等于理论概率。
那么,50%的正面机会到底意味着什么呢?
这意味着如果你抛硬币很多很多次,总的结果是 50%的硬币都是正面朝上。
让我们一步步来看这个。
让我们掷硬币五次。我们得到的正面数量是红点的数量,我们得到的反面数量是蓝点的数量。

作者创建的图像
到目前为止,这看起来相当不*衡,我们 80%的翻转都是正面朝上。这是 80%的实验概率,与我们 50%的理论概率相差甚远。但是让我们继续翻转。这是我们抛了 15 次硬币后得到的结果。

作者创建的图像
现在我们总共有 60%的结果是正面的,接* 50%。如果我们继续抛硬币,直到我们已经抛了 50 次,这就是我们得到的结果。

作者创建的图像
这是我们总失误数的 46%,接* 50%。如果我们一遍又一遍地抛硬币,那么我们会看到这个正面百分比越来越接*理论概率的 50%。它可能不会每次翻转都接* 50%,但通常会接* 50%。
如果我们继续翻转,直到我们达到 100 次翻转,蓝线代表一段时间内头的累积百分比。红线是 50%。

作者创建的图像
蓝线的起点非常高,因为我们的前四次投掷都是正面,这意味着我们 100%的累积投掷都是正面朝上。但是当我们在混合中得到一些尾部时,蓝线倾向于靠*红线。
正如你所看到的,当我们抛硬币的次数越来越多时,代表我们实验概率的蓝线接*红线,也就是我们的理论概率。
这就是我们如何定义一个理论概率,比如在掷出六面骰子时,有 50%的机会得到正面,或者有 16.7%的机会得到 1。
**大数定律就是当我们做大量试验的时候,实验概率会趋向于接*理论概率。**我们做的试验越多,实验概率就越接*理论概率。
为了说明这个概念,这里有一个五万次抛硬币的模拟图:

作者创建的图像
最后,实验概率与理论概率如此接*,以至于蓝线和红线几乎在彼此之上。
这意味着当我们处理大量的试验时,我们可以假设实验概率基本上等于理论概率。在这种情况下,如果我们掷硬币五万次,实验概率将超级接* 50%。
重要的是要强调,每次你抛硬币,*得到正面的概率总是 50%,不管在它之前的是什么*。即使你已经连续得到 10 个头像,下一次得到另一个头像的概率仍然是*50%。*
这有助于解释为什么上图中的蓝线并不总是直对红线。即使接*翻转 30,000,您也可以看到蓝线仍有一点摆动。但随着时间的推移,蓝线一般倾向于接*红线。
从大数定律中学到的重要的一点是,大量的试验得到的实验概率非常接*理论概率。
如果你认为这个想法有点道理,你就已经准备好理解基于违约风险校准利率的过程了。
# **利用违约风险校准利率**
假设你正在考虑向很多人,比如 10,000 人,发放 10 美元的贷款。使用信用风险模型,你发现*均来说,每个人的理论违约风险是 15%。大数定律告诉你,大量试验,实验概率接*理论概率。鉴于你这里有大量的人(10,000 人),可以有把握地假设,你贷款的人中大约 15%真的会违约,85%会还你钱。
如果你知道大约 15%的人可能不会还你钱,那么你就知道你将永远失去你借的 10 美元贷款中的 15%。因为 10,000 人中的 15%是 1500 人,你借给每个人 10 美元,你知道你可能会损失 15,000 美元。
但你不需要对失去 15000 美元无动于衷。这里有几件事你可以做。
## **1。提高每个人的利率来弥补那 15000 美元。**
这样,85%真正还贷的人最终将多付 15,000 美元。因为 10,000 人中的 85%是 8,500 人,这相当于每人多得 1.76 美元的利息。在这种情况下,这相当于将利率提高了 17.6 个百分点。
问题来了:如果利率已经定在 20%了,这样你就可以获利,那么再加息 17.6 个百分点,总利率就是 37.6%。这是一个很大的增幅,像这样提高利率可能会阻止一些潜在的借款人。
## **2。只接受最安全的借款人。**
信用风险建模让你可以分别看到每个人的违约风险。有些人可能有 40%的违约风险,而其他人可能只有 1%的违约风险。如果你只把贷款给违约风险低于某个阈值的人,比如说 2%,那么愿意还你钱的人的比例会高得多。这意味着违约的人会更少,你损失的钱也会更少。
因此,你可以收取较低的利率,因为你不必弥补因违约而损失的钱。这种策略的问题是,你可能不得不拒绝一大部分想要贷款的人,这将减少你的利润。
## **3。收取与违约风险成比例的利率。**
这是现实世界中最重要的策略。当我们检查所有的借款人时,我们称每个借款人为“借款人 x”。**对于每个借款人 x,你向借款人 x 收取一定的利率,这样如果每个借款人都有借款人 x 的违约风险,你也不会损失任何钱。**
这是一个密集的想法,所以让我们打开它。
在分析第一个策略时,我们发现,如果每个借款人有 15%的违约风险,我们应该给每个借款人的利率增加 17.6 个百分点。(这相当于 15%的*均违约风险。)这样,那 85%不违约的人多付的利息,就弥补了那 15%违约的人。
如果你借钱给的每个人都有相同的违约风险,那么每个借款人的利率总会增加一定的金额,以抵消那些违约的人。

作者创建的图像
事实上,每个借款人的利率应增加的金额可以用以下公式计算:

作者创建的图像
如果你不想的话,没有必要去钻研这个公式,但是如果你想知道它是如何工作的,这一段将会告诉你。上面的风险项代表将要违约的人的比例。违约是一种二元结果;这意味着每个人要么会违约,要么不会违约。没有中间地带。数字 1 代表 100%的人,所以术语 1-风险(一减去风险)是没有违约的人的比例。你用违约者除以非违约者来确定非违约者需要偿还每个违约者贷款的百分比。
即使每个借款人的违约风险不同,你也可以用这个公式准确计算出你应该给每个借款人增加多少利率来*衡他们的风险。如果你把这个金额加到每个借款人的利率上,你有大量的借款人,大数定律告诉我们,从长期来看,你几乎肯定不会亏损。
简单回顾一下,这是你借钱给某人后,他可能会还你的钱的明细。

作者创建的图像
在现实世界中,这些策略是结合在一起的。按照策略 1,贷款人通常以小额固定利率提高每个人的利率,而不考虑违约风险,以弥补其模型中的不确定性。按照策略二,贷款人往往是有风险门槛的,所以不会接受所有人。最后,根据策略 3,贷款人利用每个借款人的违约风险给每个人的利率增加一定数量的百分点。
如果你掌握了这三个策略,那么你就已经牢牢掌握了贷款的基本原则。
在这一点上,我们已经探索了为什么发现某人的违约风险如此有用。现在,我们准备进入信用风险建模中使用的机器学习模型来计算这些违约风险。
# **什么是机器学习?**
现在人们经常讨论“机器学习”这个术语,有时很难知道它是什么意思。
简单来说,机器学习模型是一种数学预测模型,当它看到更多数据时会变得更好。
要做机器学习,你需要两样东西:一个模型,和数据。
有大量不同类型的机器学习模型。机器学习模型是用于根据数据进行预测的一组步骤的名称。下面,我们将探索在信用风险建模中很重要的四个基本机器学习模型。
你还需要大量的历史数据。这些数据需要包含你试图预测的事物(称为**目标**)和与你试图预测的事物相关的其他特征(称为**特征**)。在信用风险建模的情况下,你需要许多个人的历史数据。这里的目标数据是每个人是否违约。这里的特征数据由每个人的统计数据组成,比如他们的收入、年龄和就业状况。
一旦你有了数据,你需要**通过输入历史数据来训练**模型。建立机器学习模型是为了将特征数据与目标数据联系起来。训练后,模型可以查看已知要素数据和未知目标数据的情况,并且模型可以进行预测。
这种情况在现实生活中很常见。当你试图决定是否给某人贷款时,你通常有他们所有的特征数据,比如收入和年龄。你只是不知道目标数据:他们拖欠贷款的概率。
计算这个概率是机器学习模型的闪光点。
让我们从一个简单但功能惊人的模型开始。
# **K *邻模型**
假设我们有一堆具有两个特征的历史贷款数据:每个人的收入和年龄。我们也知道每个人是否拖欠贷款。如果他们违约了,那么他们的点在图表上就会变成红色。如果他们偿还了贷款,他们的点在图上是蓝色的。

作者创建的图像
现在让我们假设特德再次要求贷款。我们想知道他偿还贷款的可能性有多大,现在我们有了一个比我们的感觉更强大的工具。如果我们知道泰德的收入和年龄,那么我们可以在同一张图上用绿色标出他。

作者创建的图像
然后,我们选择一些我们要查看的“最*邻居”。这个数字称为 K。如果 K = 5,那么我们将选择最接* Ted 点的 5 个点,并计算出它们的违约百分比。

作者创建的图像
在这种情况下,80%离 Ted 最*的人偿还了贷款(4 人),20%的人违约(1 人)。这意味着我们暂定 Ted 的违约风险为 20%。
当然,您可能认为将 K 设置为等于 5 似乎很武断。有一种方法可以优化我们的 K 值,那就是**测试**模型。
为了测试模型,我们将历史数据分成两部分:T2 的训练数据和 T4 的测试数据。然后,我们将 K 值设置为一个任意值,比如 10,并将我们的训练数据提供给模型,以生成一个如上图所示的图表。然后,对于测试数据中的每个人,我们让训练好的模型猜测这个人是否拖欠贷款。模型看不到测试数据的目标列;它只看到特征。在测试过程中,模型必须仅根据特征做出预测,就像在现实生活中一样。
每次模型做出预测时,我们都会记录模型的预测是否正确。然后,我们通过找到它正确预测的案例的百分比来计算模型的**准确度分数**。
为了优化我们的 K 值,我们用不同的 K 值一遍又一遍地测试这个模型,直到我们找到一个效果最好的。有时候理想 K 值是 1,而其他时候理想 K 值是巨大的。(当然,对于信用风险建模,K 值为 1 是愚蠢的,因为这会给我们带来 0%或 100%的违约风险。)
在上面的 2D 图表中,我们只有两个特征变量:收入和年龄。但是 k-最*邻算法对于 2 个以上的特征变量也非常有效。如果我们有 3 个特征变量,我们可以在一个 3D 框中而不是 2D 方块中绘制这些点。

作者创建的图像
对于 3 个变量,同样的步骤也适用。训练模型给了我们一个 3D 图,上面有一堆蓝点和红点。当我们试图预测一个人的违约风险时,我们会把他们画在图上。为了计算他们的违约风险,我们找到这个人的 K 个最*的邻居,我们计算这些邻居违约的百分比。我们说这个百分比就是新人的违约风险。
一旦我们有了 4 个变量,图表就变得难以想象了。但是计算机可以处理几乎无限多的变量。事实上,使用毕达哥拉斯定理,用 4 个或更多变量手动用代数方法计算最*邻并不太困难。(有兴趣可以谷歌一下!)
k-最*邻可能看起来是一个非常简单的模型,但是由于有数千个数据点和许多功能,它可能非常强大。
然而,在某些情况下,其他模型更有效。我们来讨论另一个经典的机器学习模型:逻辑回归。
# **逻辑回归模型**
最简单的逻辑回归形式是包含一个目标列和一个要素列的数据集。例如,包含借款人收入和他们是否拖欠贷款的历史数据集将非常容易用于逻辑回归。
对于逻辑回归,目标数据必须是**二进制**,这意味着它只能有两种结果。在信用风险建模中,目标数据实际上是二元的:一个人可以拖欠或不拖欠贷款。
为了制作一个图表,我们可以将这个二元结果表示为*一个人在过去*偿还贷款的概率。如果有人在过去拖欠贷款,那么我们可以说他们偿还贷款的概率为 0。如果有人真的偿还了贷款,那么我们可以说他们偿还贷款的概率是 1。给过去的事件分配概率可能会感觉怪怪的,但 0%或 100%的概率只是另一种说法,即我们知道某事要么肯定没有发生,要么肯定发生了。
以下是收入与历史违约概率的关系图:

作者创建的图像
因为我们正在处理明确的过去的数据,所有的点不是在顶部,就是在底部。顶部的蓝点代表偿还贷款的人,而底部的红点代表违约的人。
这是事情变酷的地方。逻辑回归算法使用一些有趣的数学方法(我们不会在这里深入探讨)计算出一条类似如下的直线:

作者创建的图像
这条线告诉我们一个给定收入水*的新人偿还贷款的概率。
用逻辑回归预测一个新人的违约风险非常简单。你只需找到收入轴上与新人收入相对应的点,与该点相对应的逻辑回归线的 y 值就会给出他们偿还贷款的概率。从 1 中减去这个值就是他们的违约风险。
假设 Ted 的收入就在这里:

作者创建的图像
为了找出泰德偿还贷款的概率,我们向上走,遇到逻辑回归趋势线。

作者创建的图像
根据这个模型,看起来他有大约 85%的机会偿还贷款。这意味着他的违约风险是 15%。
值得注意的是,这个版本的逻辑回归将 Ted 的违约风险设为 15%,而我们的 k *邻模型将 Ted 的违约风险设为 20%。从不同的模型中获得不同的预测在机器学习的世界中是完全正常的。为了决定使用哪个模型,我们必须使用历史数据进行一系列训练/测试循环,以查看哪个模型在这种情况下始终表现最佳。
逻辑回归模型的 2D 版本非常简单,就像 2D k *邻一样。但是就像 K *邻一样,一台电脑(或者一个拿着铅笔的人!)可以毫无问题地将逻辑回归推广到无穷多个维度。
k-最*邻和逻辑回归都是很好的*似模型,尤其是当数据遵循线性模式时。但有时,有一个模型来捕捉数据中微妙的非线性模式也不错。决策树模型是一个简单的模型,在寻找这种模式方面非常出色。
# **决策树**
为了理解决策树,让我们回到我们用来说明 k *邻如何工作的图。

作者创建的图像
正如我们所见,理解该图的一种方法是使用 k-最*邻算法。然而,我们也可以制定一些简单的规则,根据点的位置对它们进行分类。例如,我们可以画一条线将图表分成两半,如下所示:

作者创建的图像
画这条线会将数据分成两半。表示这一点的一种方法是使用树形图,如下图所示。

作者创建的图像
然后,我们可以再次分割这些分割,这样它们在图上看起来就像这样:

作者创建的图像
这是第二次分裂的树形图:

作者创建的图像
我们可以像这样不断进行越来越小的拆分,直到我们在每个分支的底部只剩下一个结果——违约或不违约。另一种看待这个问题的方式是,我们不断地把图上的方框变得越来越小,直到每个方框只包含一种颜色的点。
对于每一次分割,计算机通过使用一个有趣的数学过程找到分割的最佳值,我们在这里不会深入研究。
要使用决策树算法测试一个新案例,您只需从头开始,沿着分支向下。
假设 Ted 一年挣 5 万美元,30 岁。我们从树的顶端开始。

作者创建的图像
然后,我们向右下方移动,因为 Ted 每年挣 2 万多美元。

作者创建的图像
从这里开始,我们向右移动,因为 Ted 大于 28 岁。

作者创建的图像
这个过程一直持续,直到我们到达树的底部,在那里一片叶子上只有一种可能的结果。在真正的决策树中,会有比这里显示的两层更多的分支。
这个算法的问题是它返回一个二元结果:要么 Ted 违约,要么 Ted 不违约。鉴于任何模型本质上都是不完美的,这种二元结果对于信用建模来说并不那么有用。我们不能用一个二进制的结果来设定 Ted 的利率。
解决方法是使用**随机森林**。随机森林只是决策树的集合,所有的结构都稍有不同。为了进行预测,森林中的每棵树都“投票”决定它是否认为 Ted 会拖欠贷款。泰德的信用风险是说他会违约的树的百分比。
像我们讨论的其他算法一样,决策树算法可以扩展到任意多的维度。决策树算法的一个优点是它非常擅长捕捉非线性数据。然而,决策树有时会对有噪声的数据看得太远,认为它们看到了在统计上不存在的重要模式。
还有一种机器学习算法,我们将在这里简单介绍一下。
# **神经网络**
神经网络比我们已经讨论过的三种算法要复杂得多。它们是模仿人脑的迷人算法,也是大多数可被视为“人工智能”的系统的核心。
对于简单的信用风险建模问题,神经网络通常是多余的,但在某些情况下,它们可能非常有用。
如果你想了解更多关于神经网络的知识,你可以在这里阅读它们的详细解释[。](https://medium.com/@a.jeremymah/behind-the-eyeballs-of-ai-how-neural-networks-work-207c31e0b1ff)
# **风险建模流程总结**
这四类算法(k *邻、逻辑回归、决策树和神经网络)只是信用风险建模中使用的机器学习的开始。但是理解这些算法的基础知识会让你对整个信用风险建模过程有一点了解,这种理解会给你提供一个完美的跳板来学习更多关于机器学习的知识。
首先,我们开发了一些关于贷款和利率在现实世界中如何使用的领域知识。然后,我们将数据分成训练集和测试集来分析各种模型。最后,我们分析了几个可以测试的模型,我们知道我们会使用最有效的模型输出来进行预测。
类似的过程适用于几乎任何涉及机器学习的问题。
首先,发展一些关于你正在处理的问题的领域知识是很重要的,这样你就知道如何提出正确的问题。然后,您必须清理和准备您的数据(这个主题我们在这里没有深入探讨)。接下来,您将训练和测试各种模型,完善模型以最大限度地提高预测准确性。最后,你向他人展示你的结果,并将它们应用到现实世界的问题中,不断重复这个过程。
如果你对这个一般过程有了更好的理解,那么你就可以更好地理解信用风险建模和机器学习作为一个整体!
# 适时给予的应得的赞扬
> 原文:<https://towardsdatascience.com/credit-where-credit-is-due-ff9d3c38c940?source=collection_archive---------34----------------------->
## 导航学术信用和作者

来源:[谷歌艺术与文化](https://artsandculture.google.com/asset/card-game-the-game-of-authors-illustrated-parker-brothers/4QEi7RBrTSOkFA)
学分分配和关于论文作者的决定是一个令人惊讶的难以驾驭的话题,尤其是对初级研究人员来说。我清楚地记得在我的第一篇论文上非常努力地工作,本能地将自己列为最后一名作者——鉴于我的名字缩写,我习惯于在名单上最后一名——并通过我的合著者的间接评论发现,论文上的姓名排序实际上是一件事。那个想法当时对我来说似乎很滑稽,但我很快了解到,奇怪的是,[真的很关心这个问题。](http://phdcomics.com/comics/archive.php?comicid=562)
# 总则
大多数专业组织都有作者身份的标准(例如 [ACM](https://www.acm.org/publications/policies/authorship) 、 [IEEE](https://journals.ieeeauthorcenter.ieee.org/become-an-ieee-journal-author/publishing-ethics/definition-of-authorship/) 、 [APA](https://www.apa.org/science/leadership/students/authorship-paper.pdf) 、 [ICMJE](http://www.icmje.org/recommendations/browse/roles-and-responsibilities/defining-the-role-of-authors-and-contributors.html) )。在我自己的机构,黄金法则是:
> 公*,倾向于包容性。
这种宽泛的、故意不具体的政策让研究人员决定遵守哪些标准,这些标准通常是由他们自己的社区设定的。我们还为研究人员提供了寻求建议和处理升级的机制。标准以微妙的方式变化,从作为论文的活跃作者是否是作者身份的必要条件,到围绕致谢和引用的实践。
我对初级研究人员的主要建议是谈论学分分配:尽早谈论它,并根据需要经常重复对话。
> “成功与你愿意进行多少尴尬的对话成正比。”——蒂姆·费里斯
我遇到的大多数问题都与不匹配的期望有关:‘你说我会成为合著者/但你什么都没做/但你说…’,或者‘你提交了这篇文章却没有承认我?!'。谈论它是最好的解药。关于作者身份的早期对话不需要理解为合同:事情会发生变化,人们会忙于其他事情,也会有新人加入。这就是为什么养成定期进行这些对话的习惯是有帮助的,只要参与的每个人都愿意参与,正如他们应该做的那样。有一件事非常有助于奠定这些对话的基础,那就是共享的进度文档,其中每个人对项目的投入都被记录下来。
而且这些都是很难的对话!有人会说“[至关重要的对话](https://www.amazon.com/Crucial-Conversations-Talking-Stakes-Second/dp/1469266822)”拖延拥有它们是非常诱人的,因此错过了早期发现问题的机会。我见过这样的情况,作者名单只有在准备好的论文到期的那天才能确定,这给合作者带来了过度的压力,尤其是那些初级角色。
关于如何进行这些对话,没有通用的模板,因为这在很大程度上取决于参与者各自的角色以及当时工作的成熟程度。一个好的起点是,一旦研究的叙述开始成形。当合作者开始讨论潜在出版物的“内容”和“时间”时,这是一个将对话引向“谁”的机会,同时设定这将是一个持续过程的预期。
不要轻易将你的名字与任何作品联系在一起。慷慨地使用致谢——对某人的工作表示感谢通常是一种赞赏的认可,但在没有询问他们的情况下,永远不要在论文中承认或添加某人为合著者。有很多人采取措施不在网上公开他们的名字或关系,尊重他们的隐私选择是很重要的。我也拒绝成为名义上由我投稿的论文的合著者,因为我不同意该成果的质量或作为独立出版物的价值。还要注意的是,专利申请的作者标准往往比学术出版物更为严格,因为一项专利的共同发明人要证明没有参与该发明,这可能是该专利完全无效的理由。
# 常见模式
有一些常见的场景往往是围绕信贷问题的丰富来源。一个是团队经理、领导或其他学术顾问的角色。简单地说,在我的团队中,我们不实行“荣誉作者”制度。线索不应被添加为提供人数、资金、参加会议或仅仅是有脉搏的作者。有一些无形的贡献,如指导研究,以特定的方式提供建议或使其有组织地进行,这些都值得认可,但我不认为在每篇论文上添加实验室领导的常见做法是健康的,特别是因为它在某种程度上贬低了高级团队成员的贡献。
另一个经常出现的问题是如何认识到软件工程师的影响,他们为支持研究贡献了基础设施。任何专门为研究项目服务的工程工作都是被邀请投稿的明确理由。当有相应的出版物时,更为横向的工作,以及在没有工具和框架等专门化的情况下对多个项目做出贡献的工作,通常是值得承认或引用的。值得注意的是,对于那些没有围绕研究成果建立职业生涯的人来说,学术学分并没有得到一致的重视。尝试并理解如何以对自己重要的方式奖励自己的工作是很重要的。
现有技术的问题是另一个常见的问题,特别是当它建立在你的同事的工作之上时,他们可能会感到某种程度的所有权。这里的一般标准是,任何发表的作品都是被引用的理由,而不是合著。要求成为自己作品的任何衍生作品的合著者是一种常见的冲动,但除非涉及未发表的作品,否则会受到抵制。许多社区试图通过非自我引用的数量来衡量影响,从而实现标准化,尽管该标准并未统一应用。
另一方面,使用具体的、可操作的、以前未发表的想法保证了提出想法的个人被邀请共同撰写论文。当人们寻求对他们共享的一般概念的信任时,问题往往会出现,但不仅仅是在高层次上讨论它们。很容易写下并传播一个想法,而不需要限定或试图发表它,希望也许有人会从事类似的工作,并且现在有义务信任你。但这是一种非常有害的动态,尤其是在绿地领域,那里有许多“显而易见”的想法可以尝试,而艰难的工作是验证它们。对此,我的解决办法是将任何关于一般的、非特定的想法的现有技术的主张视为承认的理由,而不是合著。这是一种微妙的*衡,因为你仍想激励思想的自由流动,但又不想让一个人口中的每一个概念胚胎都成为插在研究领域的一面旗帜,上面写着“不进则退”。
在大型团队中,长时间的工作涉及到许多人和问题的各个方面,一个非常常见的问题是优化整个团队的信誉,而不是希望更快地发布他们自己的问题。经常有一种“独家新闻”的诱惑:迅速发表一篇论文,要么与更大的努力竞争,解决其中的一部分,而不解决项目的更大范围,要么使用为项目建造的新基础设施,但未发表。这是为数不多的完全可靠和可发表的研究可能需要被阻止的情况之一,而且更大项目的利益可能会理所当然地优先于少数人的利益。
# 无意识的偏见和激励
我发现在项目合作中,尝试并理解潜在的偏见和动机是非常有用的,因为很容易出于良好的意图而无意中造成困难的局面。例如图片:
1. 一个高级贡献者向项目添加第二个初级贡献者,威胁“第一作者身份”,
2. 相反,初级成员向另一个高级贡献者寻求建议,威胁“最后作者身份”,
3. 一位合作者为项目添加了#名人,很高兴有机会与他们合作撰写论文。其他团队成员不高兴,因为现在这是一个“名人项目”,他们自己的贡献将会黯然失色。
4. 作者在某些期望的基础上预先就作者顺序达成一致,但是项目不断发展,
5. Aaron Aappy 建议按字母顺序排列,Zoe Zzzywk 并不觉得有趣。
在许多领域,博士是由第一作者的论文组成的,终身职位案例是由最后作者的论文组成的,一个人建立个人档案的机会是有限的,这就产生了支持或反对某些类型合作的强烈动机。一个人的个人经历还有许多其他方式可以影响到某人对公*的看法:如果你来自一个在你的领域中代表性不足的群体,也许“*等作者身份”对你来说并不*等。或者,如果你是一名工程师,其对研究工作的贡献在过去被忽视了,仅仅承认你的工作,而不是合作,可能会让人觉得又是一种轻视。
这就是为什么我强调从一开始就在工作组中公开谈论这些问题,因为只要这些偏见仍然是无意识的,无论是对你自己还是对他人,它们甚至会妨碍定义公*对每个人意味着什么。无论我们多么希望公*可以被客观地定义,在这种情况下,它在很大程度上仍然是一种主观的衡量标准。
> 信用不是一个附加的数量
我经常遇到的最后一个常见的认知偏见是,在论文中增加另一位作者总是会稀释一个人的贡献,好像荣誉是一个固定的馅饼,要在合著者之间瓜分。这是完全不真实的:没有人会在意你是 4 人还是 5 人论文的合著者——在单作者论文的背景下很可能完全相反。事实上,如果你有一位非常知名的研究人员作为合著者,在某种程度上,他们可能会提高你论文的可信度,有效地增加整体信用,而不是拿走。
# 疑难病例
事情也有很多不太好的方式变得复杂。想象一下这些:
1. 爱丽丝在团队会议上提出了一些想法。几个月后,Bob 提交了一篇非常相似的论文。爱丽丝没有意识到。
2. 顾问建议一组项目。Alice 和 Bob 独立地获得了相同的想法,并且彼此不说话,独立地执行它。
3. Bob 开始与 Alice 合作,但是没有时间继续工作。论文是在他有机会腾出足够的时间来投稿之前写的。
4. 鲍勃提出了一个想法。Alice 说“几个月前我也有同样的想法,让我把它写下来”,然后迅速拿出一份描述这个想法的文档。
5. 爱丽丝和鲍勃,分别是第一和第二作者,向会议提交了一篇论文,但被拒绝。Alice 有其他事情要做,但是 Bob 和 Chris 一起工作,对它进行实质性的修改,重新提交,它被接受了。
6. Bob 为这个项目做了很多工作,但是这些工作最终没有被包括在报告中。
在每一种情况下,都有相互冲突的观点的自然设置,或实际指责的空间,正确的做法可能取决于许多因素。这可能是坦诚对话最重要的地方,包括事实和人们对这些事实的解释和感受。这也是某种形式的独立裁决特别有用的地方。
作者的问题也可能最终成为决策点之一,在这种情况下,达到每个参与者都能接受的封闭程度比实际结果更重要。到了紧要关头,如果真到了那一步,抛硬币并不是做出决定的最糟糕方式。特别是,它没有“包袱”来解释为什么做出一个特定的决定,并使每个人都能继续前进。
# 我应该在乎吗?
许多人会认为围绕着作者身份的问题微不足道,不值得他们关注。我承认,在我职业生涯的大部分时间里,我都是在那个阵营中度过的,我把更多的注意力放在了推动科学发展上,而不是信贷拨款的细节上。但随着我的角色逐渐变成一名研究负责人,我开始意识到,不想关心并不能免除我关注的责任,因为这对我周围的人来说确实很重要,原因也是我不能轻易忽视的。尽管我很希望自我和职业发展仅仅是非凡研究成果的次要驱动力,但我明白它们本身也推动了很大一部分科学进步。
人们会对各种各样的内在和外在激励因素做出反应,但他们的自我形象往往总是他们动机的核心,不可回避的事实是,看到自己的名字在公共领域得到认可是其中的一个重要部分。所以我转过身来,决定拥抱人们对适当和公*的认可的渴望,并帮助促进学术信用的公*和透明是值得关注的,只要这样人们的注意力可以自信地转向更重要的问题,即共同产生良好的科学。如果好篱笆造就好邻居,公*和包容的信贷造就好的研究,这是值得关注的理由。
*感谢* [*亚历山大·浮士德*](https://www.afaust.info) *帮助整理本文所依据的材料,并感谢乔伊斯·诺亚-万霍克进行大量编辑。*
# 克莉玛,嗯,它有什么用?
> 原文:<https://towardsdatascience.com/crema-huh-what-is-it-good-for-f7bf4fa97897?source=collection_archive---------42----------------------->
## 完全没有
浓缩咖啡的经典属性之一是克莉玛。许多咖啡师将克丽玛质量视为好的拍摄质量的关键指标。然而,我没有。
我不是克莉玛的狂热粉丝,我的浓缩咖啡之旅一直是减少克莉玛的用量,因为它的份量更短(更低的输出输入比),所以我并没有特别注意它。一个朋友曾经在看了我没有典型的克莉玛后问我所有的克莉玛在哪里。所以我把更多的水从冰球中推入另一个杯子,以证明它的存在,但我不打算喝它。
然而,作为一个数据人,我想研究一下克莉玛,看看我是否能理解它与其他咖啡的不同之处。需要说明的是,我主要是从重量、可溶物和味道来看克莉玛。我承认可以做更好的分析,但考虑到我还没有看到任何关于 crema 的分析,我想我应该从这里开始。
# 超级自动克莉玛
我开始用超级自动相机进行测试,因为它比我手动拍摄的照片产生了更多的克莱沫。克莉玛被一些人认为是人造的,因为它来自一个加压的移动式过滤器。

杯子里的二氧化碳很快就会冒出来。

从这里,我分离出克莉玛。

我用两把勺子刮下克莉玛并进行比较。

我用 Atago 测量 TDS。

我确保在测量前混合。

我原以为克莉玛的 TDS 会比其他镜头低,但实际上几乎一样。然而,由于重量原因,它仅占所有提取咖啡的 3%。

# 拉帕沃尼·克雷马

我把拉帕沃尼的一个镜头拉进了镜头的前半段和后半段。大部分克莉玛在后半部分,后半部分提取的咖啡少得多。

只看下半年的克莉玛,它只贡献了总咖啡可溶物的 2.8%,占总量的 4.9%。克莉玛的 TDS 比秒高。

我打了两枪,作为检查克丽玛的快速测试。我没有做更多的测试来了解克莉玛中有哪些可能与杯子不同的颗粒。大部分的克莉玛来自于镜头的后半部分,我已经不喝那部分了,因为我没有发现味道上的好处。我不喜欢后半部分单独存在或者和前半部分混在一起。我宁愿淡化一个镜头的前半部分。所以,不管克莱沫对味道有什么潜在的好处,我都不感兴趣。
然而,我并不热衷于去除克莉玛。有些人完全去除了克莉玛,但是我坚定地站在“喝之前搅拌一杯”的阵营。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 [LinkedIn](https://www.linkedin.com/in/robert-mckeon-aloe-01581595?source=post_page---------------------------) 上找到我。
# 我的进一步阅读:
[解构咖啡:分割烘焙、研磨、分层以获得更好的浓缩咖啡](/deconstructed-coffee-split-roasting-grinding-and-layering-for-better-espresso-fd408c1ac535)
[浓缩咖啡的预浸:更好的浓缩咖啡的视觉提示](/pre-infusion-for-espresso-visual-cues-for-better-espresso-c23b2542152e)
[咖啡的形状](/the-shape-of-coffee-fa87d3a67752)
[搅拌还是旋转:更好的浓缩咖啡体验](https://towardsdatascience.com/p/8cf623ea27ef)
[香辣浓缩咖啡:热磨,冷捣以获得更好的咖啡](/spicy-espresso-grind-hot-tamp-cold-36bb547211ef)
[断续浓缩咖啡:提升浓缩咖啡](https://medium.com/overthinking-life/staccato-espresso-leveling-up-espresso-70b68144f94)
[用纸质过滤器改进浓缩咖啡](/the-impact-of-paper-filters-on-espresso-cfaf6e047456)
[浓缩咖啡中咖啡溶解度的初步研究](/coffee-solubility-in-espresso-an-initial-study-88f78a432e2c)
[断奏捣固:不用筛子改进浓缩咖啡](/staccato-tamping-improving-espresso-without-a-sifter-b22de5db28f6)
[浓缩咖啡模拟:计算机模型的第一步](https://towardsdatascience.com/@rmckeon/espresso-simulation-first-steps-in-computer-models-56e06fc9a13c)
[更好的浓缩咖啡压力脉动](/pressure-pulsing-for-better-espresso-62f09362211d)
[咖啡数据表](https://towardsdatascience.com/@rmckeon/coffee-data-sheet-d95fd241e7f6)
[工匠咖啡价格过高](https://towardsdatascience.com/overthinking-life/artisan-coffee-is-overpriced-81410a429aaa)
被盗咖啡机的故事
[浓缩咖啡过滤器分析](/espresso-filters-an-analysis-7672899ce4c0)
[便携式浓缩咖啡:指南](https://towardsdatascience.com/overthinking-life/portable-espresso-a-guide-5fb32185621)
克鲁夫筛:一项分析
# 利用脸书先知预测犯罪率
> 原文:<https://towardsdatascience.com/crime-rate-prediction-using-facebook-prophet-5348e21273d?source=collection_archive---------39----------------------->
## 充分利用 FB Prophet 进行时间序列预测的指南

[I](https://unsplash.com/photos/4dKy7d3lkKM) mg 通过[链接从 unsplash】](https://unsplash.com/photos/Q2-EQDwxFtw)
> 时间序列预测是任何数据科学家都必须知道的技术之一。像预测天气、产品销售、购物中心的客户访问或要维护的库存数量等问题都与时间序列预测有关,这使它成为数据科学家技能组合的重要补充。
在这篇文章中,我将介绍**如何使用脸书预言家预测芝加哥的犯罪率。**分成 5 部分:
1.先知游戏攻略
2.电子设计自动化(Electronic Design Automation)
3.数据处理
4.模型预测法
5.外卖食品
让我们开始旅程吧🏃♀️🏃♂️.
**1。先知介绍**
2017 年,脸书核心数据科学团队开源 Prophet🎉🎉。正如其 [Github](https://github.com/facebook/prophet) 页面所述,Prophet 是:
* 预测时间序列数据的程序;
* 基于附加模型;
* 用每年、每周和每天的季节性以及假日效应来拟合非线性趋势。
Prophet 使用一个可分解模型,该模型有三个主要部分,包括趋势、季节性和假期,组合如下:

其中:
* *g(t)* 是对非周期性变化建模的趋势函数;
* *s(t)* 表示周期性变化(例如,每周和每年的季节性);
* *h(t)* 表示节假日对潜在不规则时间表的影响;
* 误差项表示模型不适应的任何特殊变化。
> 因此,使用时间作为回归变量,Prophet 试图将时间的线性和非线性函数拟合为组件。实际上,Prophet 将预测问题框定为曲线拟合练习,而不是查看每个观察的基于时间的依赖性,这带来了灵活性、快速拟合和可解释的参数。
> Prophet 最适用于具有强烈季节效应的时间序列和几个季节的历史数据。
**2。EDA**
这里使用的数据是来自 [Kaggle](https://www.kaggle.com/currie32/crimes-in-chicago) 的芝加哥犯罪数据集。它包含了 2001 年至 2017 年发生在芝加哥市的已报告犯罪的汇总。
快速查看下面的数据,您会注意到数据集有 23 列和 7,941,282 条记录,包括 ID、案例号、块、主要类型、描述等。
芝加哥原始犯罪数据集一览
首先,让我们删除未使用的列。具体来说,
df.drop([‘Unnamed: 0’, ‘ID’, ‘Case Number’, ‘IUCR’, ‘X Coordinate’, ‘Y Coordinate’,’Updated On’,’Year’, ‘FBI Code’, ‘Beat’,’Ward’,’Community Area’,‘Location’, ‘District’, ‘Latitude’, ‘Longitude’],
axis = 1, inplace=True)

图 1 删除列后的数据视图
如图 1 所示,*【日期】*栏为日期格式。让我们将它转换成熊猫可以理解的日期格式,并将其设置为索引。具体来说,
df.Date = pd.to_datetime(df.Date, format = ‘%m/%d/%Y %I:%M:%S %p’)
df.index = pd.DatetimeIndex(df.Date)
df.drop(‘Date’, inplace = True, axis = 1)
现在,数据可以进行可视化了。**首先,让我们看看每年的犯罪分布。**具体来说,
plt.plot(df.resample(‘Y’).size())
plt.xlabel(‘Year’)
plt.ylabel(‘Num of crimes’)
注意上面的 *df.resample('Y ')。size()* 产生年度犯罪计数。
如图 2 所示,从 2002 年到 2005 年,犯罪率开始下降。但是从 2006 年开始,犯罪率开始上升,在 2009 年达到顶峰,然后一直下降到 2018 年。这条曲线可能反映了经济对社会犯罪的影响。金融危机前后,犯罪率逐年下降,但金融危机导致的经济不景气导致犯罪率上升。

图 2 犯罪率的年度分布
**其次,我们来看一下季度犯罪率分布。如图 3 所示,犯罪率呈周期性起伏的下降趋势。**

图 3 犯罪率的月度分布
同样,如图 4 所示,月度犯罪率显示出与季度分析相同的模式。

图 4 犯罪率的季度分布
**3。数据处理**
*Prophet 的输入始终是包含两列的数据帧:“ds”和“y”。“ds”(datestamp)列应该是 Pandas 所期望的格式,最好是 YYYY-MM-DD 表示日期,或者 YYYY-MM-DD HH:MM:SS 表示时间戳。“y”列必须是数字,代表我们希望预测的测量值。*
具体来说,
df_m = df.resample(‘M’).size().reset_index()
df_m.columns = [‘Date’, ‘Monthly Crime Count’]
df_m_final = df_m.rename(columns = {‘Date’: ‘ds’, ‘Monthly Crime Count’: ‘y’})
**4。模型预测**
*从 EDA 分析中,我们发现有***的月度和季度季节性,而没有* ***的年度季节性。*** *默认情况下,如果时间序列超过两个周期,Prophet 适合每周和每年的季节性。用户可以使用“添加季节性”方法添加季节性,如每小时、每月和每季度。**
*要进行预测,请实例化一个新的 Prophet 对象,并调用 fit 方法对数据进行训练。具体来说,*
m = Prophet(interval_width=0.95, yearly_seasonality=False)
m.add_seasonality(name=’monthly’, period=30.5, fourier_order=10)
m.add_seasonality(name=’quarterly’, period=91.5, fourier_order=10)
m.fit(df_m_final)
*注意 *'interval_width=0.95'* ,产生一个围绕预测的置信区间。Prophet 使用部分傅立叶和来*似周期信号。傅立叶级数决定了季节性变化的速度。*
*预测是在一个数据帧上进行的,该数据帧有一列*‘ds’*包含要进行预测的日期。例如,要预测接下来的 24 个月,请尝试以下方法:*
future = m.make_future_dataframe(periods = 24, freq = ‘M’)
pred = m.predict(future)
*如图 5 所示,预测值' *yhat'* 被分配给具有下限和上限的每个日期。*
**
*图 5 预测结果*
*如图 6 所示,黑点是历史数据,深蓝线是模型预测。浅蓝色阴影是预测值周围 95%的置信区间。**蓝线显示与图 3 中的模式匹配良好,表明对历史数据的预测良好。😇😇***
**
*图 6 预测图*
*最后,图 7 显示了✨✨.犯罪率模式的非周期性趋势以及月度和季度季节性成分*
**
*图 7 预测模式组件图*
***5。外卖***
*我们介绍了如何充分利用脸书先知。具体来说,*
* *使用 EDA 来探索历史数据模式,帮助创建最合适的模型*
* *使用数据处理为建模准备数据*
* *利用 Prophet 拟合历史数据,预测未来犯罪率*
***如果你觉得这篇文章有帮助,请随意点击👏s!如果想看用 Python 实现的代码,看我的仓库** [**这里**](https://github.com/luke4u/Time_Series_Forecasting/tree/main/Forecasting-FB-Prophet) **。🤞🤞🤞***
***参考:***
*1.[脸书先知官方文件](https://facebook.github.io/prophet/docs/quick_start.html)*
*2.先知论文:Sean J. Taylor,Benjamin Letham (2018)大规模预测。美国统计学家 72(1):37–45([https://peerj.com/preprints/3190.pdf](https://peerj.com/preprints/3190.pdf))。*
# CRISP-数据挖掘和大数据领域的数据挖掘方法领导者
> 原文:<https://towardsdatascience.com/crisp-dm-methodology-leader-in-data-mining-and-big-data-467efd3d3781?source=collection_archive---------3----------------------->
## 机器学习方法的一步一步的简短指南

图片来自[Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=4503157)【1】的[二面体](https://pixabay.com/users/algedroid-9286107/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=4503157)
2015 年 3 月,我与 Alberto Cavadia 和 Juan Gómez 合作撰写了一篇名为“大数据项目开发的方法论商业建议”的论文[2]。当时,我们意识到大数据项目通常有 7 个部分。
不久之后,我在论文中使用了 CRISP-DM 方法,因为它是一个开放的标准,[在市场上广泛使用](https://www.kdnuggets.com/2014/10/crisp-dm-top-methodology-analytics-data-mining-data-science-projects.html) [3],而且(感谢之前的论文)我知道它与其他方法非常相似。

论文结论与 CRISP-DM 步骤之间的比较 [Israel Rodrigues](https://medium.com/@lwjirl)
随着我的数据层职业生涯的发展,我不可避免地注意到 CRISP-DM 方法仍然非常相关。实际上,数据管理单元和 IT 配置文件是围绕这种方法的步骤构建的。所以我决定,写一个小故事,来描述长期成功方法的步骤。

[肯尼斯·简森的图片](https://creativecommons.org/licenses/by-sa/3.0/)描述了步骤【4】
CRISP-DM 代表数据挖掘的跨行业标准过程,是 1996 年为塑造数据挖掘项目而创建的方法。构思一个数据挖掘项目包括 6 个步骤,并且可以根据开发人员的需要进行循环迭代。这些步骤是业务理解、数据理解、数据准备、建模、评估和部署。
第一步是**业务理解**,其目标是给出目标和数据的上下文,以便开发人员/工程师了解特定业务模型中数据的相关性。
它包括会议、在线会议、文档阅读、特定的现场学习,以及他们帮助开发团队的一长串方法,提出关于相关上下文的问题。
这一步的结果是开发团队理解了项目的上下文。项目的目标应该在项目开始前定义。例如,开发团队现在应该知道目标是增加销售,在这一步结束后,了解客户销售什么以及他们如何销售。
第二步是**数据理解**,其目标是了解从数据中可以预期和实现什么。它从几个方面检查数据质量,如数据完整性、值分布、数据治理合规性。
这是项目的关键部分,因为它定义了最终结果的可行性和可信度。在这一步,团队成员就如何提取信息的最佳价值进行头脑风暴。如果开发团队不清楚某些数据的用途或相关性,他们可以暂时后退一步,了解业务以及它如何从这些信息中受益。
由于这一步骤,数据科学家现在知道,就数据而言,结果应该满足项目的目标,什么算法和过程产生该结果,数据的当前状态如何,以及它应该如何,以便对所涉及的算法和过程有用。

[伊斯罗德里格](https://medium.com/@lwjirl)的一些数据理解技巧
第三步是**数据准备**,涉及 ETLs 或 ELTs 过程,通过算法和过程将数据片段转化为有用的东西。
有时,数据治理策略在组织中没有得到尊重或设置,为了赋予数据真正的意义,标准化信息成为数据工程师和数据科学家的工作。
同样,一些算法在某些参数下表现更好,一些算法不接受非数字值,另一些算法在值的方差很大的情况下不能正常工作。话又说回来,标准化信息是开发团队的责任。
大多数项目在这一步花费了大部分时间。这一步,我相信,是有一个 IT 概况称为数据工程师的原因。由于这非常耗时,在处理大量数据时会变得非常复杂,因此 IT 部门可以利用专门的资源来执行这些任务。
第四步是**建模**,是任何机器学习项目的核心。这一步负责应该满足或帮助满足项目目标的结果。
虽然这是项目中最吸引人的部分,但也是时间最短的部分,就好像之前的一切都做得很好,几乎没有什么需要调整的。如果结果是可改进的,该方法将返回到数据准备并改进可用数据。
一些算法,如 k-means、层次聚类、时间序列、线性回归、k-最*邻等,是该方法中这一步的核心代码行。

以色列罗德里格斯的一些建模技巧
第五步是**评估**,由它来验证结果是否有效和正确。如果结果是错误的,该方法允许回顾第一步,以了解为什么结果是错误的。
通常,在数据科学项目中,数据科学家将数据分为训练和测试。在这一步中,使用测试数据,其目的是验证模型(建模步骤的产品)是否与现实相符。
根据任务和环境的不同,有不同的技术。例如,在监督学习的环境中,对于分类项目的任务,一种验证结果的方法是使用混淆矩阵。对于无监督学习,进行评估变得更加困难,因为没有静态值来区分“正确”和“不正确”,例如,对项目进行分类的任务将通过计算(一些)聚类中元素之间的内部和内部距离来评估。
在任何情况下,指定一些误差测量源都是很重要的。这个误差度量告诉用户他们如何对结果有信心,或者是“肯定这将工作”或者“肯定它不会”。如果在所有情况下,误差度量恰好为 0 或没有,这将表明模型过拟合,而现实可能表现不同。
第六步也是最后一步是部署,它包括以有用和可理解的方式呈现结果,通过实现这一点,项目应该实现其目标。这是唯一不属于循环的步骤。
根据最终用户的不同,有用和可理解的方式可能会有所不同。例如,如果最终用户是另一个软件,就像销售网站程序询问其推荐系统向购买者推荐什么一样,一种有用的方式是 JSON 携带对特定查询的响应。在另一种情况下,就像一位高层管理人员需要计划的信息来进行决策一样,呈现这些发现的最佳方式是将其存储在分析数据库中,并作为商业智能解决方案的仪表板来呈现。

一些部署**的**例子由[以罗德里格](https://medium.com/@lwjirl)
我决定写这篇简短的描述/解释,因为我对这种方法的长期相关性感到惊讶。这种方法已经存在很长时间了,而且看起来它会流行更久。
这种方法非常符合逻辑,并且向前推进了一步。因为它评估数据挖掘项目的所有方面,并允许对其执行进行循环,所以是健壮的和可信的。毫不奇怪,大多数开发人员和项目经理都选择它,而且备选方法非常相似。
我希望这篇简短的介绍,能够帮助 IT 专业人员对他们的任务的方法开发进行论证。信息学的其他几个领域可以阅读这个故事,并基本了解数据科学家正在做什么,以及它与数据工程师和商业智能等其他方面的关系。
我希望你喜欢它,因为这是我的第一个故事:)。
参考资料:
[1] algedroid,Team,Work,Business,Cooperation (2019),URL:[https://pix abay . com/photos/Team-Work-Business-Cooperation-4503157/](https://pixabay.com/photos/team-work-business-cooperation-4503157/)
[2] Alberto Cavadia,Juan Gómez,e . Israel rodríguez,大数据项目发展的方法建议(2015 年),《数据科学论文》
[3] [Gregory Piatetsky](https://www.kdnuggets.com/author/gregory-piatetsky) **,** CRISP-DM,仍然是分析、数据挖掘或数据科学项目的顶级方法论(2014),URL:[https://www . kdnugges . com/2014/10/CRISP-DM-top-methodology-analytics-data-mining-data-science-projects . html](https://www.kdnuggets.com/2014/10/crisp-dm-top-methodology-analytics-data-mining-data-science-projects.html)
[4] Kenneth Jensens,显示 CRISP-DM (2012)不同阶段之间关系的流程图,URL:[https://es . Wikipedia . org/wiki/Cross _ Industry _ Standard _ Process _ for _ Data _ Mining #/media/Archivo:CRISP-DM _ Process _ diagram . png](https://en.wikipedia.org/wiki/Cross-industry_standard_process_for_data_mining#/media/File:CRISP-DM_Process_Diagram.png)
# 基于可视化理论的清晰 python 绘图
> 原文:<https://towardsdatascience.com/crisp-python-plots-based-on-visualization-theory-5ac3a82c398e?source=collection_archive---------15----------------------->
## 是时候从 Matplotlib 升级到 **Plotly** 了——下面是方法和原因。
P 实际上,这个帖子是我们都可以从经典中学习到的一大块东西,*量化信息的可视化展示* —爱德华·塔夫特。我还介绍了如何用 Python 实现它。
# 展示信息的支柱
## 卓越的图形
这就是演示文稿的**质量**。通过删除多余的形状、分散注意力的颜色和不一致的字体,您的数据将更容易被查看。根据我的经验(以及爱德华·塔夫特的经验),优秀的图形来自于许多小变化的累积收益。你地块上的每一滴墨水都很重要。
## 图形完整性
这就是演讲的**真相**。通过确保您的坐标轴正确缩放并反映趋势,您的数据将得到更真实的解释。从长远来看, ***真相总是更有价值*** 。每个人都见过其中一个用非线性 y 轴使假设看起来更强的图——发誓永远不这样做。
*本教程是你需要的几行代码,可以清晰真实地显示你的数据。*

如果你对你的数据感兴趣,我会拿一份。
对于所有的数据科学家和信息书呆子来说, [*定量信息的可视化显示*](https://www.goodreads.com/book/show/17744.The_Visual_Display_of_Quantitative_Information) 是最终的茶几书。它是展示信息书籍中无可争议的王者,这是有原因的,而且阅读起来也很愉快。
我一直在使用其中概述的方法,在我的机器人和机器学习研究论文中创建强大的情节。下面是几个摘录,由于 Medium 缺乏渲染 pdf 图像(目前),请进行缩减采样。

左)散点图具有内在的密度感。右图)通过线条+标记绘制的彩色轨迹分散对象。
这个职位有三个目标。
1. 我想确保每个人都理解他们在使用默认绘图功能时犯的一些关键错误。
2. **向我的听众介绍 Plotly** ,他与数据打交道,并在人工智能和数据领域探索自我完善的文章。
3. 涵盖**可视化理论**的基础知识,以及在拥挤的数字领域中让你的作品吸引眼球的先进技术。
包含的代码摘自我的教程报告中的`plot.py`,我将扩展它以包含 3d 绘图、动画等的最佳实践。
[## NATO Lambert/绘图-基础
### Plotly 和 Matplotlib 中干净地块基础的代码行集合— natolambert/plotting-basics
github.com](https://github.com/natolambert/plotting-basics)
T *utorial 从这里开始*。本教程将同时使用两种绘图工具— Matplotlib 和 Plotly。

左— Matplotlib 徽标,右— Plotly 徽标。
1. [**Matplotlib**](https://matplotlib.org/) :老旧的绘图引擎驱动着那么多遗留的实验代码,工程师的拐杖卡在了过去。
2. [**plottly**](https://plotly.com/):数据科学、分析学以及我的职业生涯的未来。
自始至终,我认为 plotly 为用户提供了更多的工具来保持图形的优秀和完整性。
# 0.设置

这是我们要建立的情节。小杂乱,简单的数据。更高级表现的*台。
我在这篇文章的最后包括了设置(导入和数据加载),所以人们可以根据需要复制它。依赖性的障碍很低,所以我们可以很容易地获得顶层绘图和安全保存机制。更少的依赖意味着更多的人会借鉴、复制和构建你的作品。
* 易于开发的绘图工具:`matplotlib`、`matplotlib.pyplot`、`plotly`、 `plotly.graph_objects`
* 数学和数据工具:`numpy`,`pandas`
* 系统工具:`os`,`sys`
## 初始化绘图
创建新数据可视化的第一步可能会给用户带来失败。 ***总是创建一个轴或一个特定的图形对象*** 。这允许完全控制数据以何种方式发送到哪里。
Plotly 已经领先一步。使用**子图**时,图形会根据每一行和每一列进行索引,而不是根据 matplotlib 中必须跟踪的轴列表进行索引(当 n=1 时,`plt.subplots`调用有效)。
Init plot / subplots
mpl
fig_mpl, ax = plt.subplots()# plotly
fig_plo = plotly.subplots.make_subplots(rows=1, cols=1)###### add data ######
for i in range(0, 4):
# mpl
ax.plot(...)
# plotly
fig_plo.add_trace(go.Scatter(...))
# 1.删除额外信息
## 网格线应该消失
数字和印刷混乱的最终来源是网格线。即使渲染成 PDF 格式,网格线看起来也不是很好(缩小来看看网格线是什么样子),而且它们很少帮助专注的读者理解。让趋势自己说话。
mpl
ax.grid(False)#plotly
fig.update_layout(xaxis_showgrid=False, yaxis_showgrid=False)
## 多余的框线是没有用的
使用空白。在任何媒介中,空间都是有限的。将您的数据装箱会从页面中移除宝贵的空间,而您可以在这些空间中呈现数据。右边和上面的线应该去掉,但是有时候左边和下面的线很漂亮。
mpl
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.set_xlabel("Year")
ax.set_ylabel("Market Share (%)")# plotly
fig_plo.update_xaxes(title_text="Year", linecolor='black',
row=1, col=1, zeroline=True,)
fig_plo.update_yaxes(title_text="Market Share", linecolor='black',
row=1, col=1, zeroline=True,)
## 掌控你的传奇
总会有快速读者在剧情中跳跃。总是有一个传奇人物来回答他们的问题。Plotly 拥有令人难以置信的图例工具,如分组、始终可见的隐藏项目,以及显示所选图例条目子集的交互式绘图。
使用**交互式 plotly 仪表板,让您的用户看到完整的数据,并了解他们想要的内容。**
mpl
ax.legend()# plotly
fig_plo.update_layout(showlegend=True,)

plotly 仪表盘的一个例子— [来源](https://plotly.com/python/figurewidget-app/)。
# 2.一致的字体
## 尺寸很重要&与你的媒介相匹配
字体往往被忽视。将 Arial 字体的字体放入 Times 字体的复杂报表中,看起来总是不合适。缩放字体大小以匹配文本(几乎)并始终匹配字体。
mpl
font = {'size': 24, 'family': 'serif', 'serif': ['Times']}
matplotlib.rc('font', **font)
matplotlib.rc('text', usetex=True)# plotly
fig_plo.update_layout(font=dict(
family="Times New Roman, Times, serif",
size=24,
color="black"
),
)
# 3.可印刷的
## 移除背景并使用高分辨率
总是在你的图上提高分辨率。默认情况下,它们是低 dpi 和小尺寸的,所以把它们投影到屏幕上几乎没有用。 *Plotly 允许您设置查看和保存的分辨率,因此您可以在显示时准确地看到将要保存的图形,*这是一个成功之处,当您尝试它时就会明白。
Matplotlib 将保存和查看分开,这使您生成与使用`savefig()`不同的图`with show()`,并导致头痛。
mpl
ax.set_facecolor((1, 1, 1))
no resolution tool until saving 😦# plotly
fig_plo.update_layout(plot_bgcolor='white',)
fig_plo.update_layout(width=1000, height=1000,)
# 4.在头脑中使用屏幕
## 另存为 PDF
可移植文档格式(PDF)将您的绘图保存为一系列彩色矢量对象,因此当它在屏幕上移动或移动到新设备时,它会被再次渲染。 *PDF 将使你在演示文稿或手稿中再也不会有像素化的情节。*
mpl
plt.savefig(os.getcwd()+"plop_mpl.pdf", dpi=300)
plt.show()# plotly
fig_plo.write_image(os.getcwd()+"plot_plotly.pdf")
fig_plo.show()
## 低对比度颜色

屏幕上漂亮的颜色。柔软,独特,由我独特设计。
屏幕绘图的一个微妙之处是使用哪种颜色。颜色应该是 1)可区分的和 2)悦目的。这最终被*淘汰,*核心色。看看下面可用的颜色图,但首先我有一些经过测试的颜色。
我的颜色**天蓝色**T2、**红苹果**T3、**苔绿**、**芥末黄**T5。下面是查看 matplotlib 和 plotly 中一些颜色的链接。
[](https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html) [## 在 Matplotlib - Matplotlib 3.1.0 文档中选择色彩映射表
### Matplotlib 有许多内置的色彩映射表,可以通过。还有外部库,如[palettable]和…
matplotlib.org](https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html) [](https://plotly.com/python/discrete-color/) [## 离散颜色
### 同样,笛卡尔坐标中标记的 X 或 Y 位置可以用来表示连续值…
plotly.com](https://plotly.com/python/discrete-color/)
# 输出
在继续滚动之前,仔细看看这些图。看哪个更一致,更无 bug。然后你就可以决定用哪个*台了。**其中一个里面的 bug 来自源头,无法避免**,这对于我来说是不成立的立场。

左)Matplotlib,右)Plotly。Matplotlib 版本的 auto-legend location 是一个彻底的失败,一些字体没有得到正确的转换,并且总的来说不够清晰。
我是 team plotly(右上方),无 bug 且敏锐。
## Plotly 的改进版本
[Plotly 的 API](https://plotly.com/python-api-reference/) 有一个很容易访问的工具,几乎可以用于绘图中的每个设置,你可以通过一两行代码批量传递它们。这里是我喜欢的情节中的调整(偏好)的集合。它允许您 a)控制图例形状和位置,b)删除绘图周围的空白。试一试,看看 API——您会发现大量的工具。
advanced
fig_plo.update_layout(legend_orientation="h",
legend=dict(x=.6, y=0.07,
bgcolor='rgba(205, 223, 212, .4)',
bordercolor="Black",
),
margin=dict(r=10, l=10, b=10, t=10),
)
[## plotly - 4.6.0 文档的 Python API 参考
### 这是 plotly 的 API 的参考。另请参见 plotly 的文档网站。
plotly.com](https://plotly.com/python-api-reference/) 
通过更好的可视化向前迈出一步。来源—作者。
不太花哨的细节:
## 进口
下面是我如何为绘图脚本/模块构建我的导入。
file tools
import os
import sys# plotting tools
import plotly
import plotly.graph_objects as go
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt# Core
import numpy as np
## 数据
我从本教程中使用的数据是从一个 plotly 线绘图的例子。你可以在这里查看。值得注意的一点是,plotly 有很好的色彩科学——*柔和的颜色在数字观看时更容易被眼睛看到*(较低的总颜色数 r+g+b)。
Frome https://plotly.com/python/line-charts/
title = 'Main Source for News'
labels = ['Television', 'Newspaper', 'Internet', 'Radio']
colors = ['#1f77b4', # muted blue
'#ff7f0e', # safety orange
'#2ca02c', # cooked asparagus green
'#d62728', # brick red
]mode_size = [8, 8, 12, 8]
line_size = [2, 2, 4, 2]x_data = np.vstack((np.arange(2001, 2014),)*4)y_data = np.array([
[74, 82, 80, 74, 73, 72, 74, 70, 70, 66, 66, 69],
[45, 42, 50, 46, 36, 36, 34, 35, 32, 31, 31, 28],
[13, 14, 20, 24, 20, 24, 24, 40, 35, 41, 43, 50],
[18, 21, 18, 21, 16, 14, 13, 18, 17, 16, 19, 23],
])
敬美丽的情节!
更多?订阅我关于机器人、人工智能和社会的时事通讯!
[](https://robotic.substack.com/) [## 自动化大众化
### 一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)
# 从数据科学的角度看促成秋粘虫传播的关键因素
> 原文:<https://towardsdatascience.com/critical-factors-contributing-to-the-spread-of-fall-armyworms-from-a-data-science-perspective-a1fec38cff32?source=collection_archive---------43----------------------->
## Shravani Basu 博士、Á·安赫尔·德贾恩·戈塔雷多纳博士、Sébastien Foucaud 博士和 Mukti Sadhan Basu 博士——SBSF 咨询公司
# 研究的目标
尽管在美国本土的热带和亚热带地区已经存在了几十年,秋粘虫(*草地夜蛾*);一汽)在开始传播到非洲和世界其他地方之前,并没有被深入研究。因此,从这种害虫的爆发中获得的信息非常有限,这种害虫正在各大洲迅速变得难以根除。减轻虫害带来的巨大损失的唯一方法是详细了解虫害,并制定多管齐下的作物保护和虫害控制策略。
这项研究试图通过结合三个不同的数据集,提供 FAW 潜在爆发的补充信息,阐明有利于 FAW 传播的条件。
由于粮农组织使用的数据收集方法的局限性,综合数据集不允许建立预测模型。因此,这项研究的重点是提取推动一汽在非洲传播的特征,因为大部分数据都是在非洲收集的。
虽然基于非洲数据集,但我们进行分析的方式使得研究结果可以扩展到全球任何一汽事故。
# 秋季粘虫:全球威胁
FAW 蛾的幼虫期是一种害虫,以 350 多种植物物种为食,对重要的经济作物如玉米、水稻、高粱等造成广泛损害;棉花、甘蔗、花生等经济作物;水果作物,如苹果和橘子,以及蔬菜作物等。然而,玉米仍然是优选的宿主。因为毛虫吃了太多的植物,它们对作物的存活和产量非常有害。
据粮农组织称,非洲每年损失多达 1800 万吨玉米,足以养活数千万以玉米为主要作物的人,这意味着非洲大陆的经济损失高达 46 亿美元。此外,针对具体国家的研究表明,一汽暴露与杀虫剂使用强度之间存在[正相关](https://academic.oup.com/erae/advance-article/doi/10.1093/erae/jbz048/5698630)。有几种控制一汽虫害的措施,但并没有显著减少损失。
一汽疫情于 2016 年在非洲发现,此后蔓延至包括中东和大洋洲在内的亚洲。这种昆虫繁殖迅速,每年繁殖几代,饮食多样化,可以通过迁移到不同的地方(跨洲)或躲藏起来,在条件更有利的时候跳回来,在恶劣的条件下生存。这种蛾每晚能飞 100 公里。雌蛾在产卵前可以迁移 500 公里。然而,这种昆虫不适应低温。它们不能在低于 0℃的温度下迁徙和繁殖。由于全球变暖,暖冬为一汽大规模繁殖创造了良好的条件。

**图 1:** 秋粘虫生活史图。节选自 [FAO 的一汽指南](http://www.fao.org/fao-stories/article/en/c/1104446/)。

**图 2:** 一汽虫害的早期在植物上留下白色斑块(照片由印度班加罗尔农业科学大学 G. Keshavareddy 博士提供)。

**图 3:** 晚熟幼虫破坏轮纹,使其外观粗糙(照片由印度班加罗尔农业科学大学 G. Keshavareddy 博士提供)。

**图 4:** 相对早期阶段玉米棒的发病率(照片由印度班加罗尔农业科学大学 G. Keshavareddy 博士提供)。
FAW 造成的全球虫害和国家损害的程度详见粮农组织 2020 年 1 月至 3 月的《食物链危机预警公报》 [№34](http://www.fao.org/3/ca7582en/ca7582en.pdf) 。这里简要介绍了风险最大的国家的情况以及所报告的破坏规模。
## **在非洲**:
**安哥拉** -据报道,2017 年,一汽有超过 19,000 公顷的玉米、小米和高粱作物被毁,造成约。180 万美元的损失。
**埃塞俄比亚**——FAW 攻击四季种植的玉米:主雨季、短雨季、灌溉玉米。在该国,超过 458 个玉米种植区受到一汽的影响。
**马拉维**——在 2016/17 年主雨季(11 月-3 月)首次报道了一汽的存在。这种害虫对全国的玉米、反季节灌溉玉米(4 月-10 月)和小麦等其他作物造成了严重损害。在 2017/18 年准备收获季节(11 月至 3 月),政府宣布因虫害而进入灾难状态。
乌干达——乌干达所有 121 个地区(100%的领土)都证实了这种害虫的存在。
**南苏丹** -全国各地(全国所有前 10 个州)都证实了一汽的存在。实地观察和农民报告表明,当作物受到水分胁迫时,FAW 虫害严重。
**斯威士兰**——2016/17 赛季首次报道了一汽的存在。这种害虫对全国各地的高粱、小米和玉米造成了严重损害,一直持续到 2017/18 赛季。
**埃及**(北非)- 一汽自 2019 年 5 月起在埃及南部玉米地正式报道。法奥得以穿越撒哈拉沙漠的天然屏障。因此,北非国家面临风险。
**苏丹-** 自 2017 年以来,已有来自苏丹的 FAW 报告,尼罗河流域可能被认为是传入埃及的可能途径。埃及和苏丹的气候允许许多寄主植物连续种植,这增加了害虫传播和破坏的可能性。
**坦桑尼亚、赞比亚和津巴布韦**——在 2016/17 年度首次报告了一汽的存在,该虫害在 2017/18 年度生产季节(11 月至 3 月)继续对玉米造成损害。
## 在亚洲:
**孟加拉-** 一汽于 2018 年 8 月首次被检出。从那以后,它传播到了全国的几个地方。
**柬埔寨-** 截至 2019 年 6 月 11 日,柬埔寨共有 11,142 公顷的玉米作物被销毁,其中拜林省 2,544 公顷,马德望省 3,033 公顷,班迭棉吉省 4,715 公顷,以及 Tboung Khmum 省 850 公顷。
**缅甸** -根据农业、畜牧业和灌溉部的数据,自 2019 年 1 月的第一周以来,一汽入侵了该国的玉米田。FAW 在伊洛瓦底地区得到确认,然后在同一年的短时间内传播到 9 个邦/地区。伊洛瓦底省大约有 4 046 公顷的土地受到影响。
**印度尼西亚-**2019 年 3 月在西苏门答腊首次检出一汽。在四个月内,这种害虫已经蔓延到苏门答腊、爪哇和加里曼丹部分地区的 12 个省。
**菲律宾-** 到 2019 年 6 月,东内格罗斯省马比奈当地政府宣布,一汽袭击了这个山城 32 个镇中的 28 个。
**斯里兰卡** - FAW 主要在阿努拉德普勒、莫纳拉加拉和安帕赖地区感染玉米,但在该国几乎所有地区的农场中都发现了。FAW 在上述三个地区种植玉米的总面积分别为 61,010 公顷和 34,856 公顷。此外,据报道,一汽已蔓延到水稻、番茄、小米、绿豆和一些牧草品种,如甘蔗。在斯里兰卡种植的 82,000 公顷中,据报道有 43,037 公顷被 FAW 侵染。该国的作物总损失估计在 10%到 25%之间。
**也门**(西亚)-自 2018 年以来,一汽的传入和存在一直被报道,这增加了传入阿曼和沙特等邻国的风险。
**中国**——未列入粮农组织报告,但据[新闻报道](https://www.taiwannews.com.tw/en/news/3880336)2020 年 3 月 5 日,中国政府在新闻发布会上承认一汽于 2018 年首次入侵中国。南部和西南部省份受影响最大。中国当局发现,从 11 月到 2019 年 1 月,中国南部和西南部的小鹿数量正在增加。受灾面积达 4 万公顷,是去年同期的 90 倍。由于这种害虫已经侵袭了邻国老挝的 80,000 多公顷土地,当局预测中国可能会出现更糟糕的情况。
根据中国农业和农村事务部发布的指导方针,一汽北移将比去年提前一个月。一旦他们向北移动,黄河和淮河周围地区 50%的玉米地将受到威胁。
一汽也从 2019 年 6 月开始在[台被举报。根据动植物卫生检验检疫局(BAPHIQ)的数据,截至 2019 年 7 月 10 日,台湾已有](https://www.taipeitimes.com/News/taiwan/archives/2019/06/17/2003717082) [199 起确认的一汽目击事件](https://www.taiwannews.com.tw/en/news/3745382),超过 50 公顷的玉米地受到影响。
**印度**——不是来自粮农组织的报告,而是独立核实,根据国家农业昆虫资源局(NBAIR)2018 年 7 月进行的一项调查,第一次报道 FAW 是在卡纳塔克邦(印度南部)的 Chikkaballapur 区的田地里。这种害虫已经摧毁了卡纳塔克邦 70%以上的农作物,现在已经蔓延到印度南部、西部、北部和东北部。玉米种植面积约为 930 万公顷,年产量接* 2800 万吨。
**澳大利亚**——FAW 没有被包括在粮农组织的报告中,它已经在北领地和西澳大利亚北部的多个地区被检测到,并可能很快威胁昆士兰州 Wide Bay 地区的农作物。农业和渔业部表示,自今年 2 月在澳大利亚发现 FAW 以来,仅在玉米、高粱和大豆作物中发现过 FAW。如果成立,一汽有可能成为澳大利亚北部棉花的害虫。
# 数据集
我们为这项研究合并了三个互补的、公开的数据集。
1. 本研究中使用的核心数据集是在联合国粮食及农业组织(FAO)为**全球秋季粘虫控制行动(** [秋季粘虫](http://www.fao.org/fall-armyworm/en/))发起的项目下收集的数据,该项目对 FAW 爆发的病例进行了分类,主要发生在非洲国家。**一汽监测预警系统(FAMEWS)** 由一个分发给农民用于数据收集的移动应用程序和一个用于绘制当前情况的全球*台组成。这些数据大多由农民自己直接在 FAMEWS 应用程序中收集,使用两种检测技术:使用信息素陷阱收集昆虫([一汽指导说明 3](http://www.fao.org/3/I8322EN/i8322en.pdf) )或侦察田地([秋粘虫侦察](http://www.fao.org/3/I8321EN/i8321en.pdf))。本研究使用的数据集版本涵盖了 2018 年 2 月 27 日至 2019 年 9 月 30 日之间登记的病例。所选数据集包括 39013 个案例,每个案例测量了 44 个变量。不幸的是,粮农组织的这个数据集不再在他们的网站上免费提供了。
2. 结合 FAW 疫情数据集,我们使用了饥荒预警系统网络(FEWS 网)陆地数据同化系统(FLDAS)中 VIC 模型的再分析天气数据。这些数据的分辨率为 0.25 度,覆盖了从 2001 年 1 月到现在的整个非洲大陆(在这项研究中,我们使用了 2018 年 2 月到 2019 年 10 月之间的可用数据)。时间分辨率是每日的。有关数据集的更多信息,请访问: [FLDAS:项目目标](https://ldas.gsfc.nasa.gov/fldas)。从模型中总共提取了 21 个变量,包括降水量、温度和风速,并在 [FLDAS 模型数据描述| LDAS](https://ldas.gsfc.nasa.gov/fldas/model-data-description) 中描述。
3. 使用的最后一个数据集是来自统一的世界土壤数据库(HWSD)的土壤数据。这是一个 30 角秒的栅格数据库,有 15 000 多个不同的土壤制图单元,结合了世界各地现有的区域和国家土壤信息更新(SOTER、ESD、中国土壤图、WISE)。数据可通过粮农组织门户网站获取:[世界土壤数据库 1.2 版|粮农组织土壤门户网站](http://www.fao.org/soils-portal/soil-survey/soil-maps-and-databases/harmonized-world-soil-database-v12/en/)。数据集包括 58 个变量,描述了土壤单位的组成和土壤参数的特征(有机碳、pH 值、蓄水能力、土壤深度、土壤和粘土部分的阳离子交换能力、总可交换养分、石灰和石膏含量、钠交换百分比、盐度、结构等级和粒度)。变量的更多细节可以在:[http://www.fao.org/3/aq361e/aq361e.pdf](http://www.fao.org/3/aq361e/aq361e.pdf)找到。
这三个数据集根据 FAMEWS 数据中提供的作物田地的地理坐标进行合并。
# 方法
## A.选择
在 FAMEWS 数据集中使用了两种不同的检查方法:侦察和信息素陷阱。不同的检测方法,无论是单独使用还是组合使用,都可能产生意想不到的偏差。因此,我们将研究限于使用 Scouting 检测的样本,因为它代表了最大的样本(69%的病例,即 26901 例——见图 5)。

**图 5:**fame ws 数据集中不同检查方法的份额。
如图 6 所示,在 FAMEWS 的搜索样本中,当观察 FAW 阳性和阴性检测的相对分布时,阳性病例占样本的 85.9%。数据集中非常强的阳性检测偏倚实际上表明,通过移动应用程序收集数据是在 FAW 实际爆发后开始的。如果数据收集来自给定地区的系统调查(调查期间该地区所有农民的输入,不考虑 FAW 的发生率),我们预计在数据集中发现的比例会小得多。

**图 6:**FAMEWS 数据集中阳性和阴性检查的分布。
这一观察结果实际上被侦察检查随时间的分布所证实,如图 7 所示:缺乏时间一致性是反应模式的症状,而不是系统的数据收集。

**图 7:**fame ws 数据集中过去几个月检查次数的总体分布。
关注图 8 中所示的非洲特定区域,支持类似的结论:检查数量的分布因位置而异(对于图 8 中红色的选定区域- *左*,2019 年的检查数量接*于零)。

**图 8: *左* :** 侦察检查的空间分布;红色区域中的检验用于相邻地块。 ***右侧*** :所选区域当月检验次数分布。
正如所展示的,基于侦察检查的数据集高度偏向于阳性检测。这很可能是由于疫情爆发后数据收集的反应性质(农民只在他们的田地被感染后才使用该应用程序)。这样一个不*衡的数据集不允许我们在给定的日期为给定的领域建立一汽爆发的预测模型。为了实现这一目标,有必要进行更加系统和公正的数据收集,以提供疫情病例的现实表现。
因此,我们决定将我们的研究重点放在一汽在玉米作物中传播的驱动因素和加重因素上。在实践中,我们正在使用机器学习建模来提取一组具有预测能力的特征,以识别一汽的存在。我们希望确保这些特征的预测重要性可以通过位置和时间来推断,以便我们从非洲过去的疫情中获得的见解在未来对其他国家(如印度)也有效。
## B.模型、预处理和验证策略
我们决定训练一个极端梯度增强( [XGBoost](https://github.com/dmlc/xgboost) )优化来预测,作为一个目标变量,在一个给定的检测点被一汽侵染的植物的百分比。如前一节所述,害虫传播的预测(例如,田地是否会受到影响?)是不可能的,因为检测样本中存在偏差。因此,我们选择将重点放在虫害程度的预测上(即,知道田地已经虫害,预计受影响的比例是多少?)
图 9 显示了每次侦察检查的潜在侵扰程度范围,表明二元目标变量(阳性,检测到,与阴性,未检测到)无法完全掌握问题。我们还从训练集中删除了虫害为 0%和 100%的案例,因为它们似乎与调查信息的缺乏更相关,因为输入是由农民键入的。

**图 9:** 每次检查中受侵染植物的比例(%)分布(基于侦察)
在任何模型训练之前,我们对数据集进行适当的预处理:
* 将数据集限于非洲大陆和玉米作物(因为其他作物的信息也存在于数据集中)——将总样本减少到 16705 个案例;
* 删除重复的行和常量列;
* 从 FLDAS、FAMEWS 和 HWDS 数据集中进行领域驱动的特征选择(仅关注相关信息,放弃不相关或重复的特征,例如数据库 IDs
* 某些字段的标准化(例如“cropFieldSize”,因为根据应用程序的用户使用不同的测量单位);
* 手动特征工程,为我们的研究以更相关的方式组合特征;
* 汇总天气数据以反映每周*均水*。
鉴于 FAMEWS 数据集的性质,以及所使用的空间和时间要素(土壤和天气数据)的混合,我们必须实施特定的验证策略,以确保最重要的要素将在时间和位置上正确地概化。我们的验证策略基于时间分离,其中 2018 年的数据用于训练,2019 年的数据用于验证。然而,为了消除任何空间影响,我们对这一基本策略进行了如下改进:
* 对于 2019 年的验证集,我们定义了 3 个特定区域作为经度区间:A (-16,-1),B (26,33),C (35.5,46)。这些区域经过精心设计,以最大限度地提高培训和验证实例的数量。
* 然后,我们在训练/验证中分割我们的检查数据集三次,每次在 2018 数据集上训练模型,排除验证区域中的数据(例如,区域 A 外的所有 2018 数据),并用验证区域内的 2019 数据集进行验证(例如,A 内的所有 2019 数据)。
通过这种验证策略,我们优化了最重要的超参数,首先是全局网格搜索,然后是更局部的网格搜索以进行微调。使用上述三个分割,在微调模型的超参数时,我们可以计算验证集之间的模型*均绝对误差(MAE)。MAE 越低,模型通过位置和时间进行概化的能力越高。
## C.特征选择
一旦模型被训练,我们可以为每个特征计算 XGBoost 目标函数中的增益,该增益是当模型的决策树之一使用该特征在数据集中进行分割时获得的。然后,我们将 XGBoost 模型中一个特性的重要性定义为所有这些收益的总和。
然而,特征的重要性可以通过隐藏的相关性或掩盖关于位置或时间的信息来人为地提高。为了确保所选择的特征正确地概括,对于每个特征,我们再次微调和训练模型,而不考虑该特征。然后,我们将排除该特性的新模型的 MAE 与包含该特性的旧模型进行比较。如果在不考虑该特征时 MAE 显著增加(大于 0.005),我们丢弃该特征。在这种情况下,再次重新计算重要特征的集合,将被丢弃的特征从我们的预测器集合中去除。
被丢弃的功能示例:
* instant _ yearly:MAE 中的 **0.7934** 移除后的改进
* 第一阶段:MAE 中的 **0.5037** 移除后的改进
* cropFieldSize:对 MAE 中的 **0.0863** 的改进
从 FLDAS、HWSD 和 FAMEWS 数据集的原始组合变量集中选出的最终 14 个特征对 FAW 虫害具有最高的预测能力,它们是:

# 分析和结果
为了直观显示所选特征区分严重和轻微虫害作物的能力,我们使用二元目标构建了一个决策树,如果作物的 FAW 植物百分比超过中位数(大约 25%的虫害),则该目标被定义为真,否则为假,如图 10 所示。
决策树从上到下如下所示:
* 每个单元格代表基于所述条件的样本分成两部分(例如,顶部单元格‘Psurf _ f _ tavg _ mean < 96627.656’);如果条件得到满足(True ),则对照左边的下一级单元格检查相应的拆分样本,如果不满足(False ),则在右边检查;
* 在每个细胞中,阴性(在我们的情况下较少侵染)和阳性(在我们的情况下较多侵染)的分数在括号中表示(例如在顶部细胞上‘值=[0.513,0.487]’,因此 51.3%的阴性和 48.7%的阳性);
* 每个单元所代表的完整样本的比例也同样被指示(例如在顶部单元中“样本= 100%”);
* 阳性细胞比例越高(感染越多),细胞越蓝,阴性细胞比例越高(感染越少),细胞越红。
为了更好地理解如何读树,让我们来看两个最极端的例子:
1 -第一个实例对应于具有最暗蓝色阴影的单元,其占用于本研究的样本集的 5.7%(在非洲对玉米作物的侦察检查),其中每个田地(在该子集中)的 85.4%受到侵害(“值=[0.146,0.854]),有利于以下条件:
* 低地面气压(' Psurf _ f _ tavg _ mean≤96627.656 ');
* 粘土的低重量分数(尽管不极端)(' 10.5<t_clay></t_clay>
* high fraction of Organic Content (‘T_OC>0.955 ');
* 而没有下雨(' Rainf_f_tavg_mean≤0 ')。
2 -第二个实例对应于具有最暗红色阴影的单元,其占用于本研究的样本集的 5.5%(在非洲对玉米作物的侦察检查),其中每个田地(在该子集中)只有 17.1%受到侵害(“值=[0.829,0.171]),有利于以下条件:
* 中间地面大气压力(' 96627.656<psurf_f_tavg_mean></psurf_f_tavg_mean>
* high Humidity Rate (‘Qair_f_tavg_mean>0.015 ');
* 田间施肥情况(“crop fertilizer _ no≤0.5”);
* 和较老的作物(“年龄> 54.5”)。

**图 10:** 决策树模型基于我们的 14 个高度预测特征来预测虫害程度。
尽管决策树对所选特征之间的重要性和相互作用提出了有趣的见解,但现在让我们尝试解释为什么这些所选特征中的一些具有高预测能力。我们将重点放在一些选定的分析上,并不打算进行详尽的研究。同样,我们的目标是证明所使用的方法使我们能够确定一汽传播背后最重要的驱动因素。
## 土壤密度的影响
在这里,我们需要强调的是,其中一些特征只有在与其他特征相结合时才具有真正的预测能力。例如,如图 11 所示,粘土的分数“T_CLAY”独立地与我们的目标具有相当弱的相关性。然而,图 11 中的第二个图显示,当与地面压力“Psurf_f_tavg_mean”结合时,其影响更大。这个事实已经可以通过决策树推导出来了。

**图 11:** 对于所有检查(**左侧**)和在较低表面大气压力下进行的检查(**右侧**)(拟合三阶回归函数),作为粘土重量分数函数的侵染率百分比。
一个初步的解释是,在较高的地面大气压力下,不管土壤的成分如何,土壤的密度都较大。在较低的大气压力下,土壤密度较低,那么粘土的质地将对密度产生更大的影响。由于土壤密度较低(因此大气压力较低,粘土含量较低),一旦蛹羽化,FAW 成虫蛾更容易从土壤中出来,增加了感染的风险。

**图 12:** 作为有机物含量的重量分数的函数的侵染率百分比(拟合三阶回归函数)。
类似的解释也可以用图 12 所示的有机物含量部分来提出:有机物含量部分越高,土壤的密度越小,因此虫害的风险就越高。
请注意,在图 11 和图 12 中,我们使用了三阶回归。在粘土或有机物含量重量分数的极端情况下,测量中的低统计数据阻止了任何此类测量的解释,并且可能不够可靠。
## 作物生长阶段和作物健康的影响

**图 13:** 作为作物年龄函数的侵染率百分比(拟合二阶回归函数)。
FAW 毛虫主要以玉米叶子和轮叶的嫩部分为食,这解释了幼龄作物感染风险较高,而老龄作物感染风险迅速降低的原因,如图 13 所示。虫害的高峰期大约是 30-80 天。玉米在种植后 130-135 天成熟,这解释了超过这个年龄的统计数据(和虫害案例)迅速减少的原因,因为这些案例可能主要与数据收集中的错误有关(因为年龄是根据农民直接收集的数据计算的)。

**图 14:** 作为土壤温度函数的侵害率百分比(拟合二阶回归函数)。
在图 14 中,我们研究了土壤温度对侵染率的影响(空气温度和土壤辐射温度与土壤温度密切相关,因此我们只关注这一变量)。在 20-25℃(293-298k)附*有一个明显的峰值,在 17℃ (290K)以下和 27℃ (300K)以上有强烈的下降。这些温度对应于玉米根有效生长的理想土壤温度,这意味着该变量与作物的内在健康相关(更健康的作物显然对应于更高的侵染率)。
## 天气条件的影响

**图 15:** 作为*均风速的函数的侵害率百分比(拟合三阶回归函数)。
当显示风对虫害的影响时,如图 15 所示,一开始,当风更大时(高达 2 米/秒),虫害会明显增加,但当风越来越大时,虫害会减少(高达 4 米/秒)。一种潜在的解释是,一些风增加了 FAW 蛾在农田的不同区域传播和恢复循环的机会,但更强的风阻止了 FAW 毛虫留在叶子上继续进食。

**图 16:** 作为空气湿度的函数(**左**)和作为前一周降雨量的函数(**右**)(拟合一阶回归函数)的侵害率百分比。
图 16 显示了虫害与空气比湿度和降雨量之间的负相关关系。这些结果表明,尽管湿度和降雨量有利于植物的发育,但过多的雨水会将幼虫从叶子上冲走,事实上降低了侵染率。这是一个有争议的结果,因为文献中的一些研究倾向于表明相反的情况。
## 灌溉的影响

**图 17: *左:*** 作为土壤湿度函数的侵染率百分比(拟合一阶回归函数); ***中心:*** 基于灌溉类型(雨养/灌溉/未知)的感染率百分比; ***右:*** 基于灌溉类型的土壤湿度(雨养/灌溉/未知)。
如图 16 所示,降雨似乎减缓了虫害。我们进一步调查浇水的影响,看看降雨是否真的对一汽有害。
在图 17- *左图*中,土壤湿度与虫害发生率呈现出与空气湿度和降雨量相似的负相关关系。然而,在图 17- *中部*观察不同类型灌溉的效果时,很明显,虽然灌溉似乎有利于虫害,但雨水灌溉确实降低了虫害发生率,证实了上述结果。如图 17- *右图*所示,降雨条件下的土壤湿度*均高于灌溉条件下的土壤湿度,这意味着雨水也有利于植物的生长(事实上,土壤湿度还取决于土壤成分,这是由我们数据集中的有效水容量‘AWC _ CLASS’提供的信息。)
从这一分析中可以清楚地观察到,任何模拟降雨的灌溉系统(如洒水器)都可以再现雨养的效果(降低感染率)。
还值得注意的是,我们的分析中没有证据表明土壤水分缺乏(水分胁迫)会加剧 FAW 的侵扰,正如上述粮农组织报告中来自南苏丹的报告所述。要理解这种差异,需要更多的数据和更深入的调查。
# 结论和建议
所进行的工作,除了提供一些可操作的见解(例如,关于灌溉),还证明了采用基于数据科学的方法来使用各种信息来源的重要性,超出了有限调查的范围,以支持全面和以结果为导向的农业项目的发展。
1. 我们强调整体方法的重要性,通过结合不同但高度互补的数据集(来自农民“FAMEWS”、天气“FLDAS”和土壤数据“HWSD”的输入),得出一致和可靠的图像。正如在研究中所看到的,所有重要的特征都是从三个数据集中提取的,并且大多数洞察都是基于来自各种数据源的特征的组合而提供的(从决策树中可以看到)。
2. 这整个工作是基于开放存取数据的可用性。我们要感谢构建这些数据集的团队所做的工作(数据收集、数据分析、建模和模拟)。我们要再次强调自由分享此类数据的重要性,并对粮农组织最*从其网站上移除对 FAMEWS 数据集的访问感到难过(根据最新状态,截至 2019 年底仍可下载 csv/excel 格式)。
3. 我们已经确定了一组明确的重要特征(14 个特征),可用于更好地了解一汽传播背后的驱动因素。虽然我们建议聚合尽可能多的数据,但是这些特性可以用作未来数据收集策略的指导原则。当然,这里进行的大多数分析结果都有以前的研究和常识的支持,但这种分析提供了可量化的信息,可用于建立预测模型和确定可操作的措施来限制害虫的传播。
4. 数据采集是任何研究的关键要素,FAMEWS 所做的出色工作值得在此强调。向农民提供一个应用程序来通知他们农田的状况,是防止 FAW 等害虫传播的一个主要武器。然而,由于农民的“非正式”输入,数据集存在自身的局限性,导致一定程度的准确性缺失,并产生不可控制的偏差。FAMEWS 采取的收集策略意味着对疫情爆发后的测量存在重大偏差,因为农民大多在虫害发生后使用该应用程序,这使得无法建立一汽传播的预测模型。
我们建议采用系统的调查策略,直接在农场一级收集信息,独立于几个季节的虫害,并将农民的投入与独立的天气和土壤信息相结合。调查的设计需要有预测性和规范性的模型作为目标,在此基础上可以采取措施尽可能消除偏见。这类项目从一开始就需要数据科学家和机器学习专家的参与。
# Cronbach 的 Alpha:理论及其在 Python 中的应用
> 原文:<https://towardsdatascience.com/cronbachs-alpha-theory-and-application-in-python-d2915dd63586?source=collection_archive---------13----------------------->
## 理解并计算秤可靠性的最重要衡量标准。

图片来自 [bongkarn thanyakij](https://www.pexels.com/de-de/@bongkarn-thanyakij-683719)
*克朗巴赫的 Alpha* 是心理学和社会科学中衡量量表可靠性的主要标准。它的重要性怎么强调都不为过,你会在你读到的几乎所有定量实证研究中发现它。令人惊讶的是,还没有一篇关于 Medium 的文章报道它在 Python 中的应用。最重要的是,像 NumPy、Pandas 或 Sklearn 这样的公共数据科学库都没有 Cronbach alpha 度量。这篇文章将告诉你如何去做。
首先,我将解释克朗巴赫的阿尔法背后的一些理论和数学。然后,我们将快速着手动手编程。
# 克朗巴赫的阿尔法值是多少?
## 心理测验理论
每当我们使用多个项目来测量一个**潜在(不可观察)结构**,我们就称这套项目为**量表**。例如,在商业环境中,我们可能想要了解我们的客户对产品 p 的态度,这对营销人员来说非常有帮助。然而,与体重(例如 50 公斤)、收入(例如 100 万英镑)等概念相反。$)或出勤(真/假),我们无法直接衡量态度。
简单地问他们“你对产品 P 的态度是什么?”一开始看起来是个不错的解决方案。然而,因为这是由你的客户来定义什么是真正的态度,你不会得到可靠的结果。相反,您应该自己定义该结构,并尝试使用多种客观且可观察的方法来度量它。如果一个顾客把一个产品评价为“好”、“有趣”、“令人兴奋”和“有用”,这可能是一种态度的衡量,尽管这个词本身并没有被使用。可以说,你可以用这 4 个问题作为衡量态度的尺度。
为了确保我们在构建量表方面做得很好,我们需要使用一些质量度量。总的来说,我们要测试一个量表的有效性**和可靠性**和**。如果秤实际测量了它应该测量的结构,那么它就是有效的。如果每次测的都是一样的,那就是可靠的。**
## ****克朗巴赫的阿尔法如何帮助?****
**克朗巴赫的阿尔法是对可靠性的一种衡量。确切地说,它告诉我们**内部如何一致**我们的规模。这是量表中所有项目测量相同结构的程度。如果“好的”、“有趣的”、“令人兴奋的”和“有用的”都有助于衡量同一件事,克朗巴赫的阿尔法就会很高。然而,如果我们试图用形容词“美味”、“丰富”、“沮丧”和“寒冷”来衡量态度,我认为克朗巴赫的阿尔法值会很低。这些项目看起来确实不一致。这正是克朗巴赫的阿尔法帮助我们的。**它回答了这样一个问题:我选择用来度量一个结构的项目排成一行了吗?****
**克朗巴赫的阿尔法值介于 0 和 1 之间。较高的值表示较高的内部一致性。一般来说,0.7 或更高的克朗巴赫α被认为是可接受的。现在,让我们来看看这些数字是如何产生的。如果你想直接进入编码领域,可以跳过这一部分。**
# **我们如何计算克朗巴赫的阿尔法值?**
**这是我们需要的公式:**
****
**这里,N 是项目(问题)的数量,r 是项目之间的*均相关性。让我们回到例子来理解这意味着什么。假设我们询问了 6 位顾客对产品 p 的看法,然后我们计算了“好”、“有趣”、“令人兴奋”和“有用”之间的相关性。假设有结果:**
****
**我们可以看到所有的相关性都很高。这应该会导致高克朗巴赫阿尔法值。下图说明了如何将公式应用于数据。**
****
**我们得到了一个**克朗巴赫的阿尔法值为. 965** ,这是异常的!**
**我们已经(在某种程度上)算完了!**让我们开始编码吧!****
# **我们如何在 Python 中应用这一点?**
**让我们编写自己的函数来计算克朗巴赫的阿尔法。首先,我们来进口熊猫和 numpy。**
import pandas as pd
import numpy as np
**现在,我们需要什么来“教授”这个函数呢?**
1. **将数据帧输入转换成相关矩阵。**
2. **计算 N 和 r。**
3. **使用公式计算克朗巴赫的阿尔法值**
**你可以这样做:**
def cronbach_alpha(df): # 1. Transform the df into a correlation matrix
df_corr = df.corr()
# 2.1 Calculate N
# The number of variables equals the number of columns in the df
N = df.shape[1]
# 2.2 Calculate R
# For this, we'll loop through the columns and append every
# relevant correlation to an array calles "r_s". Then, we'll
# calculate the mean of "r_s"
rs = np.array([])
for i, col in enumerate(df_corr.columns):
sum_ = df_corr[col][i+1:].values
rs = np.append(sum_, rs)
mean_r = np.mean(rs)
3. Use the formula to calculate Cronbach's Alpha
cronbach_alpha = (N * mean_r) / (1 + (N - 1) * mean_r)
return cronbach_alpha
**如果我们对样本数据使用该函数,我们将得到以下输出:**
****
**结果是 0.966。用手工计算,我们得到 0.965。这很可能是由于舍入。**
# **总有另一种方法…**
**听着,我要告诉你一些事。有另一种方法来计算克朗巴赫的阿尔法,更适合 numpy 计算。不过,有个原因我还没告诉你。您刚刚学习的方法更加直观,也更容易理解。最重要的是,除非您使用非常大的数据集,这在 Cronbach 的 Alpha 主要应用的调查统计中并不常见,否则您使用上面的函数不会有任何问题。我不会在本文中讨论这种方法。然而,我鼓励你利用你的新知识,自己做一些进一步的研究。**
**无论如何,我希望这篇文章对你有所帮助,教会你一个新的统计概念和/或改进你的工作流程!**
**非常感谢您的阅读!**
# 使用机器学习在浏览器中裁剪和识别图像或视频中的特定对象
> 原文:<https://towardsdatascience.com/crop-and-identify-specific-objects-in-an-image-or-video-in-the-browser-using-machine-learning-9b68780b2bb7?source=collection_archive---------33----------------------->
## 利用客户端机器学习的力量

视频来源:[https://www.youtube.com/watch?v=Yi_kDT5L5s4](https://www.youtube.com/watch?v=Yi_kDT5L5s4)
# 简介—浏览器中的机器学习
Tensorflow.js 允许我们在浏览器中完全在客户端训练模型和运行推理。这开启了一大堆新的机会!
在本文中,我们将构建一个工具,允许用户裁剪图像的特定部分,并对裁剪后的图像进行推理。我们将使用一个图像分类模型来帮助我们将图像分类到一个特定的类别,但理论上你可以使用任何模型。
# 演示
[**应用的现场演示**](https://vigorous-leavitt-67d07f.netlify.app/) 如上图 gif 所示。下面的*注释*中提到了使用说明。
[**Github 库**](https://github.com/wingedrasengan927/Identify-Specific-Objects-in-an-Image-or-Video-using-machine-learning-in-the-browser)
## **注:**
1. 要使用实时工具,单击*实时演示*链接,这将打开应用程序。接下来上传你选择的任何视频剪辑。此时屏幕仍将保持空白。要播放视频,点击*播放*按钮,视频将开始播放。接下来,在感兴趣的视频实例上点击*暂停*按钮。这将初始化框架上的裁剪器。选择感兴趣的区域进行裁剪,点击*裁剪*按钮。这将弹出一个显示裁剪图像的模态。点击*进行预测*按钮,对裁剪后的图像进行推断。结果将显示在模态中。
2. 演示中显示的工具是我们将在本文中构建的工具的扩展版本。您可以查看 github repo 以获取源代码。
# 我们开始吧
## 目录结构
我们将创建一个简单的目录结构,如下所示。
|-- index.html
|-- index.js
|-- style.css
|-- |-- Images
| `-- image.jpg
## 超文本标记语言
这就是我们的 index.html 的样子
在 *head* 标签中,我们使用 cdn 导入 css 样式表和某些库来帮助我们。我们将使用的库是:
[**cropper . js**](https://fengyuanchen.github.io/cropperjs/)**:**一个Javascript 图像裁剪库。
[**tensor flow . js:**](https://www.tensorflow.org/js)JavaScript 中用于机器学习的库。
在主体中,我们有三个容器。第一个容器是我们将要测试的图像。第二个容器是我们放置按钮的地方,第一个按钮用于初始化图像的裁剪工具,第二个按钮用于裁剪选定的部分,并对图像的裁剪部分进行推理。第三个容器是空的,但是一旦我们运行推理,我们将使用 JavaScript 动态地填充结果。最后,我们导入 cropper.js 库和 index.js 脚本。
## 半铸钢ˌ钢性铸铁(Cast Semi-Steel)
我们的风格. css
接下来,我们添加一些 css 来限制边界内的图像,并使一切看起来很好。
html 和 css 完成后,页面看起来会像这样:

图片来源:[http://www . new thinktank . com/WP-content/uploads/2010/05/Animals _ 311 . jpg](http://www.newthinktank.com/wp-content/uploads/2010/05/Animals_311.jpg)
## Java Script 语言
这是最重要的部分。让我们了解一下代码是做什么的。
首先,我们抓住所有的元素,以便我们可以操纵它们。这包括图像、按钮等。
接下来,我们运行 *loadModel* 函数。该函数下载 MobileNet 预训练模型,该模型已经在具有数千个类别的大型数据集上进行了训练。MobileNet 指的是神经网络的特定架构。
我们给这两个按钮添加了事件监听器,这样它们就可以在*点击时做出响应。*
当我们点击第一个按钮——“初始化裁剪器”时,我们运行函数 *initializeCropper。*这个函数在我们的图像上创建一个*裁剪器*的新实例,并且清空我们的*结果*容器。
第二个按钮——“裁剪图像并进行预测”运行*预测*功能。该函数获取从 cropper 获得的裁剪后的图像数据,并将其提供给我们的 MobileNet 模型。然后,该模型输出裁剪图像所属的类别或种类以及置信度概率。然后,我们动态地创建一个 *img* 元素和一个 *h1* 元素,并将它们添加到我们的结果容器中以显示结果。
流程是:
1. 点击“初始化裁剪器”按钮
2. 点击“裁剪图像并进行预测”
3. 将显示结果

在上面的例子中,我裁剪了右边的北极熊,并对裁剪后的图像进行了推理。正如我们所见,该模型以相当高的可信度正确预测了它的类别。
# 结论
在本文中,我们构建了一个简单的工具,允许用户裁剪图像的特定部分,并对裁剪后的图像运行图像分类模型。
希望你喜欢它!尽管这是一个基本的例子,但它可以扩展到构建令人惊叹的东西。一个例子是在视频中使用它,正如我在演示中展示的( [github](https://github.com/wingedrasengan927/Identify-Specific-Objects-in-an-Image-or-Video-using-machine-learning-in-the-browser) )。
客户端的机器学习已经打开了许多可能性,我鼓励你去创造令人敬畏的东西。
> “如果你不创造东西,就没有东西”——埃隆·马斯克
如果您有任何反馈或想取得联系,请在 ms.neerajkrishna@gmail.com 的*上给我留言*
# 参考
1. 【tensorflow.org/js
2. [https://fengyuanchen.github.io/cropperjs/](https://fengyuanchen.github.io/cropperjs/)
3. [https://code labs . developers . Google . com/code labs/tensor flow js-teacheble machine-code lab/index . html # 0](https://codelabs.developers.google.com/codelabs/tensorflowjs-teachablemachine-codelab/index.html#0)
# 交叉注意力才是你需要的!
> 原文:<https://towardsdatascience.com/cross-attention-is-what-you-need-fusatnet-fusion-network-b8e6f673491?source=collection_archive---------13----------------------->
## [新成绩](http://cvpr2020.thecvf.com/) / CVPR 2020
## FusAtNet:用于高光谱和激光雷达分类的基于双注意的光谱空间多模态融合网络
> FusAtNet:用于高光谱和激光雷达分类的基于双注意的光谱空间多模态融合网络
>
> ***萨蒂扬·莫拉*等。al,**IEEE/CVF 计算机视觉和模式识别会议(CVPR)研讨会,2020 年,第 92–93 页
如今,随着传感技术的进步,多模态数据正变得易于用于各种应用,特别是在遥感(RS)中,其中许多数据类型如多光谱(MSI)、超光谱(HSI)、激光雷达等。都是可用的。

今天,多模态数据很容易获得!
这些多源数据集的有效融合变得越来越重要,因为这些多模态特征已被证明能够生成高度精确的土地覆盖图。然而,考虑到数据中涉及的冗余和多种模态之间的大范围差异,RS 环境下的融合并不是微不足道的。此外,不同模态的特征提取模块之间很难交互,这进一步限制了它们的语义相关性。
> 为什么**单一融合表示很重要?**
组合多模态图像的几个优点包括:
1. 生成丰富、融合的表示有助于选择任务相关的特征
2. 改进分类,提高可信度,减少歧义
3. 补充缺失或有噪声的数据
4. 减少数据大小
有趣的是,今天大多数常见的方法往往只是使用早期连接、CNN 提取的特征级连接或多流决策级融合方法等方法,完全忽略了跨域特征。视觉**一个*注意*** *,*深度学习研究人员的工具箱中最*增加的一个是 ***在多模态领域中基本上未被探索。***
> 一个问题出现了:**如何最好地融合这些模态,形成一个联合的、丰富的表示,可以用于下游的任务?**

基于多模态融合的分类任务的一般示意图。目标是有效地组合两种模态(这里是 HSI 和 LiDAR ),使得结果表示具有丰富的、融合的特征,这些特征对于精确分类来说是足够相关和鲁棒的。
一个理想的融合方法是将两种模态协同地结合起来,并确保结果反映输入模态的显著特征。
# 一个新概念:交叉注意
在这项工作中,我们提出了“交叉注意”的新概念,并在土地覆盖分类的背景下提出了基于注意的 HSI-LiDAR 融合。

多通道融合中的自我注意与交叉注意。自我注意模块(左)仅在单一通道上工作,其中隐藏表征和注意屏蔽都来自同一通道(HSIs)。另一方面,在交叉注意模块(右)中,注意掩模来自不同的模态(LiDAR ),并且被利用来增强来自第一模态的潜在特征
交叉注意是一种新颖且直观的融合方法,其中来自一种模态(此处为 LiDAR)的注意掩模被用于突出显示另一种模态(此处为 HSI)中提取的特征。注意,这不同于自我注意,在自我注意中,来自 HSI 的注意屏蔽被用来突出它自己的光谱特征。
## **FusAtNet:在实践中使用交叉注意**
本文提出了一个用于 HSIs 和 LiDAR 数据的集体土地覆盖分类的特征融合和提取框架 FusAtNet。所提出的框架有效地利用了 HSI 通道,使用“自我注意”机制来生成强调其自身光谱特征的注意图。类似地,同时使用“交叉注意”方法来利用激光雷达导出的注意图,该注意图强调 HSI 的空间特征。然后,这些注意的光谱和空间表示与原始数据一起被进一步探索,以获得特定于模态的特征嵌入。由此获得的面向模态的联合光谱-空间信息随后被用于执行土地覆盖分类任务。

**fusat net 示意图(呈现在休斯顿数据集上)。**首先,高光谱训练样本 XH 被送到特征提取器 FHS 获取潜在表征,并送到光谱注意模块生成光谱注意掩模。同时,相应的激光雷达训练样本 XL 被发送到空间注意模块 AT 以获得空间注意掩模。注意屏蔽被单独乘以潜在 HSI 表示以获得 MS 和 MT。MS 和 MT 然后与 XH 和 XL 连接并被发送到模态特征提取器 FM 和模态注意模块 AM。然后将两者的输出相乘得到 FSS,然后将其发送到分类模块 C 进行像素分类。
# 结果
在三个 HSI-LiDAR 数据集上的实验评估表明,所提出的方法达到了最先进的分类性能,包括在现有最大的 HSI-LiDAR 基准数据集 Houston 上,为多模态特征融合分类开辟了新的途径。
> **休斯顿**

**带有分类地图的休斯顿超光谱和激光雷达数据集。**(a)HSI 的真彩色合成,(b)激光雷达图像,(c)地面实况。(d) SVM (H),(e) SVM (H+L),(f)两个分支的 CNN (H),(g)两个分支的 CNN (H+L),(h) FusAtNet (H),(i) FusAtNet (H+L)

休斯顿数据集上的精度分析(单位为%)。‘H’仅代表 HSI,而‘H+L’代表融合的 HSI 和激光雷达
> **特伦托**

**带有分类地图的 Trento 高光谱和激光雷达数据集。**(a)HSI 的真彩色合成,(b)激光雷达图像,(c)地面实况。(d) SVM (H),(e) SVM (H+L),(f)两个分支的 CNN (H),(g)两个分支的 CNN (H+L),(h) FusAtNet (H),(i) FusAtNet (H+L)

**Trento 数据集上的精度分析(以百分比表示)。**‘H’仅代表 HSI,而‘H+L’代表融合的 HSI 和激光雷达
> **MUUFL**

**带有分类地图的 MUUFL 高光谱和激光雷达数据集。**(a)HSI 的真彩色合成,(b)激光雷达图像,(c)地面实况。(d) SVM (H),(e) SVM (H+L),(f)两个分支的 CNN (H),(g)两个分支的 CNN (H+L),(h) FusAtNet (H),(i) FusAtNet (H+L)

**mu ufl 数据集的精度分析(单位为%)。** H 仅代表 HSI,而 H+L 代表融合的 HSI 和激光雷达
可以清楚地看到,对于所有情况,我们的方法在所有途径中以显著的优势优于所有现有技术方法,无论是 OA(休斯顿、特伦托和穆弗勒数据集的相应准确度为 89.98%、99.06%和 91.48%)、AA(相应值为 94.65%、98.50%和 78.58%)还是κ。很容易观察到,在类/生产者的准确度的情况下,对于大多数类,我们的方法的性能优于其他方法,而对于少数类,我们的方法仅略微超过其他方法。对于休斯顿数据集,可以注意到,与其他方法相比,我们的方法对于“商业”类(92.12%)的准确性有显著提高。这可以归因于这样的事实,即商业区域通常具有可变的布局,并且频繁的海拔变化被基于 LiDAR 的注意力地图有效地捕捉到。在休斯顿分类地图中也观察到,诸如 SVM 和双分支 CNN 的方法倾向于将阴影区域分类为水(在地图的右边部分),因为它们的色调较暗。我们的方法也在很大程度上缓解了这个问题。

**从 FusAtNet 获得的分类图往往噪声较小,并且具有*滑的类间过渡(此处为 MUUFL 数据集)**HSI(左上)、激光雷达图像(左下)、分类图(右)的真彩色合成图。
类似地,在 Trento 数据集的情况下,“道路”类显示出显著的准确性提高(93.32%)。该增量也是由于道路轮廓相对于其高度的变化。
此外,例如在 MUUFL 中,可以从视觉上验证从 FusAtNet 获得的分类图往往噪声更小,并且具有*滑的类间过渡。
> **消融**
我们进一步进行了不同的消融研究,以突出我们模型的各个方面。

**表 4:** 通过改变所有数据集上的注意力层进行消融研究(精确度为%)
表 4 表示在网络中描述的各种注意层存在的情况下的性能,证明了对所有三个注意模块的需要。

**表 5:** 有和无数据增强的训练消融研究(准确度百分比)
表 5 显示了数据扩充的性能。

**表 6:** 通过改变 MUUFL 数据集上训练样本的分数来对性能建模(精度百分比)
表 6 表示训练数据减少后的性能。
除了明显的观察结果,即数据扩充&训练数据的增加增加了准确性,有趣的是注意到 ***我们的网络仅用 50%的训练数据就开始胜过现有的 SOTA 方法。***
## 结论
总之,我们的工作是在土地覆盖分类的背景下为 HSI-LiDAR 融合引入注意力学习概念的**首批方法之一。在这方面,**我们引入了基于“交叉注意”**的概念,在模态间进行特征学习,这是一种新颖直观的融合方法,它利用来自一个模态(此处为激光雷达)的注意来突出另一个模态(HSI)中的特征。我们**在三个基准 HSI-LiDAR 数据集上展示了最先进的分类性能**,优于所有现有的深度融合策略。**
****发表为*** *(点击下方)**
> *[***FusAtNet:用于高光谱和激光雷达分类的基于双注意的光谱空间多模态融合网络***](http://openaccess.thecvf.com/content_CVPRW_2020/html/w6/Mohla_FusAtNet_Dual_Attention_Based_SpectroSpatial_Multimodal_Fusion_Network_for_Hyperspectral_CVPRW_2020_paper.html)*
>
> ****萨蒂扬·莫赫拉*,**希瓦姆·潘德,比普拉·班纳吉,苏哈西斯·乔杜里;IEEE/CVF 计算机视觉和模式识别会议(CVPR)研讨会,2020 年,第 92–93 页*
# Python 中基于跨列的数据操作
> 原文:<https://towardsdatascience.com/cross-column-based-data-manipulation-in-python-dfa5d8ffdd64?source=collection_archive---------57----------------------->
## 基于单个命令和 ETL 过程中的多列清理和特征工程数据
假设您的经理给了您一个随机数据集,并告诉您进行“基本”清理:“只保留在 A、B 和 C 列中有值的记录,或者在这三列中没有任何值的记录”。你会怎么做?

在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的 [ThisisEngineering RAEng](https://unsplash.com/@thisisengineering?utm_source=medium&utm_medium=referral)
# 介绍
特征工程可以是非常基本的,例如缩放和编码,但有时很神秘。最*,我遇到了这种基于跨列的需求,这让我想到,**为什么清理逻辑听起来如此简单,但在数据清理和特性工程中却是内在复杂的?**
## 用词直白,用代码不那么直观
我们从现有课程中学到的大多数数据操作技能都集中在行方式应用程序上。这种类型的功能在数据分析包(如 Pandas)中写得很好,这意味着分析师或数据科学家不必从头开始创建新的应用程序。然而,当涉及到基于列的应用时,事情就没那么简单了,**尤其是涉及到多个列的时候**。如果我们想执行我在引言中提到的简单请求,我们必须在流程中混合一些更高级的东西,比如逻辑关系。
在这篇文章中,我将演示一个基本的用例以及一个关于跨列操作的更复杂的情况。我还将包括完整的操作函数,展示如何在常规 ETL 过程中应用这些函数。以下文章中分享的技术包括:
1. **计算数值的跨列聚合指标**
2. **用跨列逻辑运算验证记录**
# 数据集介绍
去年春天,我和我的朋友参加了布朗大学举办的 2020 年数据马拉松比赛。主题是根据信用局数据和邮政编码级别的人口统计数据预测潜在的购房者在哪里。这些特征包括诸如银行卡限额、打开的银行卡数量和最*抵押贷款的余额等指标。
所提供的数据集涵盖 2019 年 4 月至 9 月,CSV 文件也相应拆分。总共有 36,000,000 多行和 300 多列,它们的总大小约为 10GB。我会用这个来展示我的想法。

数据集的片段
# 问题 1:使用聚集方法减少跨数据集聚集指标
简单来说就是数据一塌糊涂。在对不同时间的数据集进行组合后,只要进行简单的计算,数据的总大小就可以冻结一台普通的笔记本电脑。因此,我想提出一种方法,将相同的指标推广到不同的月份(例如,每个月都有一个“未结银行卡数量”指标。我喜欢减少功能的总数,但保留这些信息)。
各种机器学习技术(例如,PCA、聚类)可以实现类似的效果。然而,我会用最直接的方式转换特征,这就是*均。
# 解决方案 1:谨慎使用熊猫内置计算
这只是说,我们需要在一段时间内对指标进行*均。我们可以通过`pd.DataFrame.mean()`轻松实现这一点。熊猫包还提供了`sum()`、`std()`等各种聚合功能。

预期的结果
这是一项相对容易的任务。然而,诀窍是设置正确的管道来组织新的列并构建新的数据帧。这里有几个值得一提的提示:
* `pd.DataFrame()`不允许你像`pd.read_csv()`那样单独指定列的数据类型(我知道这很奇怪)。为了适应这些限制,建议在构建新的数据框后分配`astype()`功能。
* 此外,如果您同时要分配不同类型的数字数据,那么在使用值列表构建一个`pd.DataFrame()`时,要小心可能的信息丢失。整数和浮点之间的自动转换会在没有任何警告的情况下损坏值!(更不用说数值型数据转换成字符串值也会在这个过程中丢失几个数字)
整个功能如下所示。注意,“df”是要处理的数据,“df_col”只是用于构建列名的列表。
# 问题 2:清理跨列逻辑关系的数据
直到这一点,它没有太多的麻烦。但是困难来了。虽然我们缩小了数据范围,但还是有些不对劲。
一般来说,如果一条记录(一个区域)有一笔抵押贷款,那么在描述第一笔抵押贷款的每一列(*均贷款额、*均余额、比例)中都应该有一个值。如果一条记录根本没有抵押贷款,那么这三列中应该根本没有值(因为该区域没有抵押贷款)。然而,我只看了几行就发现了差异。有些行有*均贷款额但没有*均余额,有些行只有第二笔贷款的比例而没有其他两笔对应的贷款。

三个共存列的描述
如果模型不能识别高维数据的正确模式,这种类型的错误会严重损害模型。为了使模型或分析更加稳健,应该从数据集中排除有问题的记录。
## 用文氏图思考
为了理解这个问题,文氏图可能有助于理解。对于我在这里尝试做的,以及大多数一般类似的特别情况,我正在查看子集完全不相交部分和完全相交部分,它们分别是 000 (All - (A∪B∪C))和 111 (A⋂B⋂C)。

来源:https://en.wikipedia.org/wiki/Venn_diagram
有了这个想法,因为 Pandas 支持逻辑操作符(&,|,~),我可以这样配置逻辑:
111
((df[col_1].astype(bool) & df[col_2].astype(bool) & df[col_3].astype(bool))## 000
(~df[col_1].astype(bool) & ~df[col_2].astype(bool) & ~df[col_3].astype(bool)))
> 注:如果你想知道,不,这不是你在做否定逻辑条件时需要把 AND 转换成 OR 的情况(因为这里的元素都是二进制的,真或假)。另一个例子见这个[螺纹](https://stackoverflow.com/a/52786447/12170001)。
然后,我将这些逻辑运算符整合成一行,形成一个系列:
111 or 000
s_bool = ((df[col_1].astype(bool) & df[col_2].astype(bool) & df[col_3].astype(bool))|(~df[col_1].astype(bool) & ~df[col_2].astype(bool) & ~df[col_3].astype(bool)))
在将所有的布尔序列连接成一个数据帧之后,我使用`all()`和`apply()`以及`lambda`创建一个短函数,同时沿着列轴应用它。更具体地说,`all(x)`将执行另一个逻辑操作来确保所有不同的部分(列组;例如,6 月份第三次贷款的一组列,8 月份第五次抵押贷款的一组列)遵守我首先设定的规则(在维恩图中是 111 或 000 关系)。这最终归结为一系列布尔函数:
s_agg_bool = df_bool.apply(lambda x: all(x), axis = 'columns')
We can then use this series of booleans to subset the right records out of the raw data.
这是最后一个 ETL 函数,我对数据集进行了清理。请注意,它被设计为应用于多组列,例如来自不同月份或不同类别的指标:
> 在进行逻辑工作之前,我们必须将所有 NaN 值转换为 0,并将所有数值转换为布尔数据类型,这样我们就可以利用 Pandas 逻辑的机制(所有零值将为假,所有非零值将为真)。
# 结论
我快速浏览了一些基于交叉列的数据操作的技巧和例子。然而,我知道这很难成为类似用例的全面指南。
数据操作异常困难,因为我们经常面临动态问题,即在现实世界中,单个函数不足以估算缺失值或识别有偏差的数据。在试图找到相关资源后,我意识到没有系统的指导或讨论彻底覆盖实际的东西,因为它的本质是很难归类和非传统的。因此,这是分享可能的解决方案的一种尝试,我希望这篇文章能够激发新的想法,发现更多非常规的方法。

克里斯蒂安·埃斯科瓦尔在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
祝贺并感谢你的阅读!欢迎回复任何想法和意见,或者在我的 [Linkedin 页面](https://www.linkedin.com/in/jefflu-chia-ching-lu/)上留言!
附:如果你对新冠肺炎的一些州级信息感兴趣,一定要访问这个简洁的个人[仪表盘](https://coronavirus-in-us.herokuapp.com/)来跟踪曲线。
杰夫
# 交叉熵去神秘化。
> 原文:<https://towardsdatascience.com/cross-entropy-demystified-f0886a64883f?source=collection_archive---------35----------------------->
## 深入研究交叉熵,其必要性和实用性背后的直觉和推理。
# 引言。
很长一段时间,我没有完全理解交叉熵损失。**我们为什么要取指数(softmax)?那我们为什么要拿走木头?我们为什么要拍下这个日志?我们是如何以必须最小化的正损失结束的?**
这些问题越来越让我困惑,以至于我只是接受了我必须使用交叉熵进行多标签分类,并没有想太多。
最*我开始浏览 fastai 的 2020 课程,Jeremy 在解释交叉熵,尽管我认为他做得很好,但我之前的问题没有得到很好的回答。于是我开始琢磨和摆弄笔记本,终于弄明白了。
在这篇文章中,我会问一些简单的问题,然后我会回答这些问题,希望之后你不会再有任何关于交叉熵的令人难以置信的问题。
## 损失函数的目标是什么?
损失函数的目标是简单地输出一个数字(我们如何得到这个数字很快就会清楚),这个数字有 2 个要求。
1. 数字必须是正数。
2. 我们的模型越不准确,这个数字就应该越大。
我们的目标是最小化这个数字,我们的损失越接*零,我们的模型就越精确。
> 注意:我不会探究为什么会这样,我假设你知道神经网络的目标是最小化损失。如果你想了解更多,请访问 fast.ai,跟随杰里米的惊人深度学习课程。这适用于所有与交叉熵无关的问题,但是我愿意在评论中回答它们。
## 第一站,激活。
我们的第一站是激活。这些是神经网络的最后一层输出,是我们的网络对我们图像标签的猜测。激活离标签越*,我们的模型就越精确。
让我们考虑一个网球的图像,我们试图找出这个图像是否是以下三个事物之一:
1. 篮球
2. 网球
3. 足球
网球的标号将是[0,1,0]。这些预测是 100%准确的。猜测分别对应 0%篮球,100%网球,0%足球。
现在假设我们的激活是[0.001,4.334,2.90]。这些只是我凭空想象出来的随机数。然而,如果我们假设这些是激活,我们可以清楚地看到网球的激活是最高的,然而,我们如何量化呢?或者用百分比概率怎么说呢?
此外,如果我们有一组不同的激活,比如[4.334,2.90,0.01],现在最高的激活是篮球,但我们的图像是一个网球,如何量化我们的模型有多错误?
# 这就是交叉熵的用武之地。
交叉熵是一个损失函数,它量化了我们的模型有多错误。
这种疯狂主要有 3 个步骤(尽管一旦你理解了它的超级简单和直观)。
## 1.Softmax
第一步是使我们的激活值在 0 和 1 之间。我们这样做,所有激活值对应于图像属于某个类别的概率,现在我们可以简单地最大化对应于正确类别的一个值,这将自动减少对应于错误类别的值。如果我们使用原始激活,这个过程会困难得多。
> 注意:当我说我们想要最大化激活时,不要混淆。我们确实想最大化激活,我们只想最小化损失。激活是我们的模型做出的猜测,损失是我们模型错误的度量。
> 我们想减少错误(丢失),增加正确预测(正确标签对应的激活)。
***我们怎么做 softmax?***
对于每一项,取每一项的指数,除以所有项的指数之和。
对于我们的激活[4.334,2.90,0.01],让我们计算每个项目的指数。
acts = [4.334,2.90,0.01]
exp_acts = [76.2487,18.1741,1.0101]
sum(exp_acts) = 95.4329
acts_softmax = [0.7990,0.1904,0.0106]
看看最高激活是如何对应最高概率的,这就是为什么我们做 softmax
> 侧边栏:我们为什么不干脆做 acts = acts/sum(acts)呢?
> 让我们试试:
> acts/sum(acts) = [0.5983,0.4003,0.0014]
> 将此与 softmax 进行比较,较高的激活对应于使用 softmax 的概率比使用归一化的概率高。在分类中,这个特性在大多数时候是需要的,因此我们使用 softmax。
## 2.原木
考虑 2 个交叉熵值,一个是 0.99,第二个是 0.999。他们看起来比较接*,如果我们根据 softmax 计算我们的损失,两者的损失不会相差太远。然而,这是不可取的。
考虑 10000 个项目,第一个 softmax 为 0.99,100 个项目被误分类,而第二个 softmax 为 0.999,只有 10 个项目被误分类。本质上 0.999 的 softmax 好了 10 倍,而不仅仅是好了一点点。这种质量上的差异最好用测井曲线来表示。
log(0.01)=-4.6051
log(0.001)=-6.9077
尽管 softmax 值接*,但 log 值中的 cast 差异非常有用。
也 log(1) = 0,所以当 softmax 概率为正确类的 100%时,损失(如果只是 log(softmax)为 0)。
我们计算损耗的方法是,其中 label ==1,我们取-log(softmax),这就是损耗。
## 3.为什么是负的对数损失?
在我们的例子中,softmax 为 1,log softmax 为 0,因此损失为零。这是正确的。
但当我们的 softmax 为 0.01 时,我们的 log softmax 为- **4.6,**但我们知道我们的损失必须是一个必须最小化的高正数,所以我们取它的负值,使我们的-log(softmax)为正,这是损失的正确值。
由于曲线的性质,Log 为负。

许可证 [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/) 下 x < 1 are negative. Image by [Saylor 学院](https://saylordotorg.github.io/text_intermediate-algebra/s10-03-logarithmic-functions-and-thei.html)的记录值
因为 softmax 值总是小于或等于 1,所以 log 总是负的,因此-log(softmax)是正的。
**重申一下:-Log(softmax)是我们的损失值。我们取数据集或小批量中每个损失值的*均值,以获得总损失,我们试图将其最小化。**
# 外卖想法
1. 我们采用激活指数使预测概率之间的差距更大。
2. 我们取 softmax 的对数,将 softmax 的小差异等同于损失的大差异,这是应该存在的。**在某种意义上,我们使用 softmax 对激活进行处理,以帮助预测,但我们使用 log 对激活进行反处理,以准确评估损失。**
3. 我们对 log 取负值以得到正损失,因为 log 对小于 1 的值返回负值(softmax 值就是这种情况)。
# 信用
1. 这里的许多想法来自杰瑞米·霍华德的 Fastai 课程,我只是试图解释我更好地理解的内容,并添加了我的一些想法。
2. [https://saylordotorg . github . io/text _ intermediate-algebra/S10-03-logarithus-functions-and-the I . html](https://saylordotorg.github.io/text_intermediate-algebra/s10-03-logarithmic-functions-and-thei.html),就是我得到对数图的地方。
# 用于分类的交叉熵
> 原文:<https://towardsdatascience.com/cross-entropy-for-classification-d98e7f974451?source=collection_archive---------0----------------------->

[李建刚](https://unsplash.com/@lijiangang?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照
## 二元、多类和多标签分类
**TL;博士在最后**
**交叉熵**是分类任务常用的损失函数。让我们看看为什么要使用它,在哪里使用它。我们将从一个典型的多类分类任务开始。
## 多类分类
图片上是哪一类——狗、猫还是熊猫?只能是其中之一。让我们想象一只狗。

预测是一个**概率向量**,这意味着它表示所有类别的预测概率,总计为 1。
在神经网络中,通常通过 softmax 函数激活最后一层来实现这种预测,但任何事情都可以,它只是必须是一个概率向量。

让我们计算这张图片的交叉熵损失。
> 损失是对模型性能的一种度量。越低越好。学习时,模型的目标是获得尽可能低的损失。
目标表示所有类别的概率—狗、猫和熊猫。
> 多类分类的目标是一个热点向量,这意味着它在单个位置上为 1,在其他位置上为 0。
对于 dog 类,我们希望概率为 1。对于其他类,我们希望它是 0。
我们将开始分别计算每个类别的**损失,然后将它们相加。每个单独类别的损耗计算如下:**

不要太担心公式,我们马上会谈到它。请注意,如果目标的职业概率是 0,那么它的损失也是 0。

最后——狗类的损失:

那个数字是什么意思?
让我们看看,如果预测的概率不同,损失会如何表现:

* 预测为 1 时损失为 0(与目标相同)。
* 如果预测值为 0(与我们的目标完全相反),损失就是无穷大。
* 我们永远不会预测小于 0 或大于 1 的东西,所以我们不用担心这个。
如果我们预测中间的东西呢?
我们离目标越远,损失越大。
你可以把它想成一个类似于*方误差的概念——我们离目标越远,误差增长越快。
**为什么猫和熊猫类的损失为 0?**
看起来我们是在奖励低损失的模型,即使它预测了图像中不存在的类别的高概率。
我们不介意模型预测有一只猫有 80%的概率,如果没有,因为它只剩下 20%的概率来预测正确的类别。在那里,损失会大得多。换句话说,我们不关心模型在哪些类上浪费了预测的概率,只关心它如何正确地识别唯一的当前类。
该图像的总损失是每类损失的总和。

它可以被公式化为所有类的总和。

这就是**交叉熵**公式,可以作为任意两个概率向量的损失函数。这是我们损失了一张图片——我们一开始展示的一只狗的图片。如果我们想要我们的批次或整个数据集的损失,我们只需合计单个图像的损失。
假设我们有两个不同的模型给出以下预测:

在交叉熵的眼中,模型 B 更好——它的交叉熵损失更低。如果你能明白为什么——干得好!有一只熊猫。

照片由[杰瑞米 C](https://unsplash.com/@zuoanyixi?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
通过惩罚大错误比惩罚小错误多得多来训练模型,在机器学习中被证明是一个好主意。
如果大多数类别的损失为 0,为什么要对所有类别求和?
如果我们的目标是一个热点向量,我们确实可以忽略所有其他类别的目标和预测,只计算热点类别的损失。这是我们预测的负自然对数。

这被称为**分类交叉熵**——交叉熵的一个特例,其中我们的目标是一个热点向量。
事情是这样的——交叉熵损失甚至适用于非热点向量的分布。
即使对于这个任务,损失也会起作用:

有了交叉熵,我们仍然能够计算损失,如果所有的类都是正确的,损失将是最小的,并且仍然具有惩罚更大错误的属性。
在我们的单热目标例子中,熵是 0,所以最小损失是 0。如果你的目标是一个**而不是**的概率向量,熵(最小损失)将大于 0,但是你仍然可以使用交叉熵损失。
如果你更好奇熵是什么,我推荐你看这个[视频](https://www.youtube.com/watch?v=ErfnhcEV1O8)。
## 二元分类
**二进制交叉熵**是交叉熵的另一个特例——如果我们的目标是 0 或 1,就使用它。在神经网络中,通常通过激活 sigmoid 来实现这种预测。
目标是**而不是**一个概率向量。我们仍然可以用一点小技巧来使用交叉熵。
我们希望预测图像中是否包含熊猫。

这就好像我们将目标转换为一个热点向量,将我们的预测转换为一个概率向量——熊猫的概率将与预测相同,而非熊猫的概率将是 1-预测。换句话说,如果我们预测 0.6,这意味着我们说 60%是熊猫,40%不是熊猫。
这种损失可以用交叉熵函数来计算,因为我们现在只比较两个概率向量,或者甚至用分类交叉熵来计算,因为我们的目标是一个热点向量。它也可以在没有使用**二进制交叉熵**进行转换的情况下进行计算。

我们只是将自然对数应用于我们的预测和目标之间的差异。
这就是二元交叉熵的全部内容。
## 多标签分类
交叉熵也可以用作多标签问题的损失函数,方法很简单:

注意我们的目标和预测是**而不是**一个概率向量。有可能图像中有所有的类,也有可能没有。在神经网络中,通常通过激活乙状结肠来实现这一点。
我们可以把这个问题看作多个二元分类子任务。假设我们只想预测有没有狗。
我们知道我们的目标是 1,而我们预测的是 0.6
我们将计算这个子任务的二元交叉熵:

对其他班级也这样做。
对于猫,我们的目标是 0,所以二进制交叉熵的另一部分抵消了:

并合计每个子任务的损失:

这就是多标签分类的交叉熵损失。
# 结论/TL;速度三角形定位法(dead reckoning)
**交叉熵** —通用公式,用于计算两个概率向量之间的损失。我们离目标越远,误差就越大——类似于*方误差。

**多类分类** —我们使用多类交叉熵—交叉熵的一种特殊情况,其中目标是一个热点编码向量。它可以用交叉熵公式计算,但可以简化。

**二进制分类** —我们使用二进制交叉熵——交叉熵的一种特殊情况,我们的目标是 0 或 1。如果我们将目标分别转换为像[0,1]或[1,0]这样的单热点向量和预测,则可以使用交叉熵公式来计算。即使没有这种转换,我们也可以用简化的公式来计算。

**多标签分类** —我们的目标可以一次表示多个(甚至零个)类。我们分别计算每一类的二元交叉熵,然后将它们相加得到完全损失。

# 交叉熵、对数损失及其背后的直觉
> 原文:<https://towardsdatascience.com/cross-entropy-log-loss-and-intuition-behind-it-364558dca514?source=collection_archive---------42----------------------->
## 在这篇博客中,你会对交叉熵和对数损失在机器学习中的应用有一个直观的了解。

[absolute vision](https://unsplash.com/@freegraphictoday?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/compass?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片。
在浏览这个博客之前,你不需要知道任何事情,因为我将从基础开始。以下内容将在本博客中详细介绍和解释。
1. **随机变量**
2. **信息内容**
3. **熵**
4. **交叉熵和 K-L 散度**
5. **日志丢失**
## **随机变量:**
这被定义为一个获取随机事件输出的变量。例如,我们可以定义一个变量 X,它取掷骰子的输出值。这里 X 可以取 1 到 6 的值。
我们有时也会计算随机变量取特定值的概率。例如,P(X=1)是 1/6,这对于其他值也是一样的,因为它们同样可能发生。
## **信息内容(IC):**
当我们讨论信息内容时,我们是在随机变量的背景下进行的。你可以这样想,如果你知道一个事件将要发生,而且这个事件经常发生,那么信息量就非常少。现在,如果你知道一个事件将要发生,而这个事件很少发生,那么信息量就很大。比如有两个随机变量 X 和 Y,这里 X 告诉太阳会不会升起,Y 告诉今天会不会有地震**(惊喜元素)**。你很容易得出 Y 包含更多信息的结论。
> 信息含量与惊喜元素成正比。
因此,我们可以得出结论,一个随机变量的信息含量取决于一个事件发生的概率。如果概率很低,信息量就很大。我们可以用数学方法写成如下:

信息内容
因此 IC 是概率的函数。这里 P(X=S)表示 X 取值 S 的概率,这将用于博客的其余部分。信息内容显示以下属性。

如果 X 和 Y 是独立事件,IC 的性质。
上图中的第二个性质见于对数函数族。这给了我们用对数作为函数来计算 IC 的直觉。因此,现在我们可以用数学方法定义 IC 如下:

IC 公式。
## **熵:**
随机变量的熵被定义为随机变量的期望信息量。我们会用电子学分支的信息论来更直观的解释。
在信息论中,发送值 X=某个值的比特数称为信息量。如果随机变量有 N 个值,那么我们说它是一个信号。事实上,我们将信号定义为取 N 个值的随机变量。一个信号在不同的时间间隔取不同的值,因此我们可以定义一个随机变量,它取信号可以取的 N 个可能的值。现在我们找到发送信号所需的比特数,这就是随机变量的预期信息内容。换句话说,我们称之为熵。下面的例子将使它更清楚。

举例说明如何计算熵。
使用以下公式计算熵/预期 IC

计算熵/预期 IC 的公式
使用这个公式我们得到熵= (1/2*1)+(1/4*2)+(1/4*2) = 3/2。因此,*均而言,我们将使用 1.5 位来发送该信号。
## **交叉熵和 K-L 散度:**

数据传送
在接收端,我们不知道随机变量的实际分布。我们将看到接收到 100 个信号,然后估计随机变量的分布。现在让我们假设发送方的实际分布是“y ”,估计是'y^'.在这里,分布意味着随机变量取特定值的概率。以下是发送数据所需的位数。

计算发送方和接收方位数的公式。
现在我们已经知道位数和熵是一样的。发送方的熵称为熵,接收方的估计熵称为**交叉熵**。现在,这被称为交叉熵,因为我们使用实际分布和估计分布来计算接收端的估计熵。你一定会想到的另一个问题是,为什么我们在接收端使用实际分布(y)。答案是,我们正在估计接收到的随机变量(-log(yi^).)的每个值所需的比特数所使用的比特数将取决于接收机接收到的随机变量的分布。如果在这之后还不清楚,那么让我们举个例子。
我们估计 P(X=A)是 1/4,而实际是 1/8,那么估计的比特数将是-(log(1/4)) = 2,但是对最终答案的贡献将是 1/8*(2) = 1/4,因为我们将在接收器接收这个值 1/8 次。现在我想这已经很清楚了。
**K-L 散度**等于交叉熵和熵之差。

K-L 散度。
## **日志丢失:**
现在我们将转到机器学习部分。我想我们知道 y^是什么意思。如果你不知道,那么 Y^就是给定数据点属于特定类别/标签的预测概率。例如,我们可以在机器学习中有一个模型,它将判断一个文本是否是滥用的。计算 y^的公式如下

数据点属于某一类的概率公式。
这里**‘w’是权重向量**,**‘x’是**数据点**的 d 维表示**,**‘b’是偏差项**。
> **我们在所有机器学习中的主要目标是正确估计训练数据集中数据点的分布**。
在这里,训练集可以被视为发送者,而模型可以被视为试图估计分布的接收者。
当 K-L 散度最小时,将出现最佳估计。因此我们将找到对应于最小 K-L 散度的(w,b)。在更新(w,b)时,我们忽略熵项,因为它是一个常数,只有交叉熵项变化。因此,我们的损失方程如下。

失败
这是损失项,我们通常称之为对数损失,因为它包含对数项。
对于**二进制分类**,其中‘yi’可以是 0 或 1。这个损失看起来将类似于**loss =-(y * log(y)+(1-y)* log(1-y))**。这是我们大多数人都熟悉的。暂时就这些了。我希望你都记下了。
如果你喜欢这个内容,请在下面的评论中告诉我。
参考资料:
[https://en.wikipedia.org/wiki/Cross_entropy](https://en.wikipedia.org/wiki/Cross_entropy)
[https://en.wikipedia.org/wiki/Information_content](https://en.wikipedia.org/wiki/Information_content)
[https://en.wikipedia.org/wiki/Random_variable](https://en.wikipedia.org/wiki/Random_variable)
如果你想了解什么是校准,那就去看看这个 [**博客**](https://medium.com/analytics-vidhya/calibration-in-machine-learning-e7972ac93555) 。
[](https://medium.com/analytics-vidhya/calibration-in-machine-learning-e7972ac93555) [## 机器学习中的校准
### 在这篇博客中,我们将学习什么是校准,为什么以及何时应该使用它。
medium.com](https://medium.com/analytics-vidhya/calibration-in-machine-learning-e7972ac93555)
想学习如何防止自己的模型欠拟合、欠拟合,那就去翻翻这篇 [**博客**](/overfitting-and-underfitting-in-machine-learning-89738c58f610) 。
[](/overfitting-and-underfitting-in-machine-learning-89738c58f610) [## 机器学习中的过拟合和欠拟合
### 在这篇文章中,你将了解什么是过度拟合和欠拟合。您还将学习如何防止模型…
towardsdatascience.com](/overfitting-and-underfitting-in-machine-learning-89738c58f610)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步