TowardsDataScience-博客中文翻译-2020-一百零一-

TowardsDataScience 博客中文翻译 2020(一百零一)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

Python 首次马拉松之路

原文:https://towardsdatascience.com/road-to-the-first-marathon-with-python-c78b2eee4e47?source=collection_archive---------49-----------------------

一位数据科学家讲述了他与 Strava API、熊猫等的马拉松之旅。

跑步对我来说不仅仅是一种锻炼。这让我成为一个更快乐的人。2019 年 3 月,我在洛杉矶第一次跑马拉松。那时,我写了第一篇关于那次经历的媒体文章。

作为一名数据科学家,我知道我可以做更多的事情来记住这段旅程。但是,我拖了一年多。由于新冠肺炎,我们外出跑步和参加比赛的能力非常有限。这让我更加怀念过去的时光。多亏了 Strava,我记录了我第一次马拉松前的所有跑步。我决定埋头研究这些数据。

这篇文章不是关于什么的?

这不是关于如何更好地跑步。为此,我推荐《丹尼尔的跑步公式》这本书。

这篇文章是关于什么的?

这是一本技术入门书,介绍了如何使用 Python 连接 Strava 的 activities API,并使用常见的 Python 库对我在 2019 年洛杉矶马拉松之前的跑步进行探索性分析。这揭示了数据是如何偏离直觉的。还提供了代码片段。

Strava API

在过去的 5 年里,我一直在使用 Strava。我喜欢这个应用程序的简单性和功能性。直到最近,我才开始探索 Strava API 。在高层次上,Strava API 要求开发人员使用 OAuth 2.0 身份验证来访问用户的数据。并且每次用户发起认证时,都会创建一个认证码来交换临时访问令牌。开发人员可以使用这个访问令牌来获取用户的数据。

例如,我首先发送一个 post 请求来获取我的访问令牌。这个访问令牌允许我访问我的活动数据。

import pandas as pd
import requests
import configparser
import os.path
import json
from collections import defaultdict
import datetime
import matplotlib.pyplot as pltconfig = configparser.ConfigParser()
config.read('/Users/LG/Documents/key_files/strava/strava.cfg')
client_id = config.get('USER','CLIENT_ID')
client_secret = config.get('USER','SECRET')
auth_url = config.get('OAUTH','AUTH_URL')
auth_code = config.get('OAUTH','AUTH_CODE')
params = {'client_id':client_id,'client_secret':client_secret,'code':auth_code,'grant_type':'authorization_code'}
headers = {'content-type': 'application/json'}
r =requests.post(auth_url,data=params)

然后,我使用访问令牌发送 get 请求,从 Strava 提取我的所有活动数据。

# retrieve access token
access_token = "access_token=" + str(r.json()['access_token'])
# url for getting activities
activity_url = "[https://www.strava.com/api/v3/activities](https://www.strava.com/api/v3/activities)"
# only select variables of interest
cols = ['average_heartrate','average_speed','distance','moving_time',\
        'start_date','start_latitude','start_longitude','suffer_score','total_elevation_gain',
       'type'
       ]
# write a loop to retrieve all the activities and store in a dictionary indexed by activitiy id.
page = 1
d = defaultdict()
while True:
    string = activity_url + '?'+ access_token + '&per_page=50' + '&page=' + str(page)
    # get the page of activities
    r = requests.get(string)
    r = r.json()
    if not r:
        break
    for i in range(len(r)):
        if r[i]['id'] not in d:
            d[r[i]['id']] = defaultdict()
            for key,value in r[i].items():
                if key in cols:
                    d[r[i]['id']][key] = value
        else: 
            continue
    page += 1

嘣,我把我所有的活动数据都保存在一个 Python 字典里了。现在,让我们做一些数据管理。

  • 仅过滤正在运行的活动。我有时也骑自行车。
  • Strava 记录的跑步距离是米,我需要把它转换成英里。
  • Strava 记录的速度是米每秒,我需要把它转换成分钟每英里。
  • 我过滤了 2017-12 和 2019-03 之间的日期,当时我正在为马拉松比赛进行实际训练。
my_strava = pd.DataFrame.from_dict(d,orient='index')
# filter only run activities
my_strava = my_strava[my_strava['type']=='Run']
# meter to mile translation
def meter_to_mile(meter):
    return meter * 0.000621371192
# average speed meter per second to Minutes Per Mile
def speed_converter(speed):
    return 1 / (meter_to_mile(speed) * 60)# translate my distance into miles, and moving time into hours/minutes, 
# and start_date into date/time, speed into Minutes per Mile
my_strava['distance'] = my_strava['distance'].apply(meter_to_mile)
my_strava['moving_time'] = my_strava['moving_time'].apply(lambda x:str(datetime.timedelta(seconds=x)))
my_strava['start_date'] = my_strava['start_date'].apply(lambda x:datetime.datetime.strptime(x,'%Y-%m-%dT%H:%M:%SZ'))
my_strava['average_speed'] = my_strava['average_speed'].apply(speed_converter)
# filter dates between 2017-12-01 and 2019-03-24
first_run_date = '2017-12-01'
last_run_date = '2019-03-25'
mar = (my_strava['start_date'] > first_run_date) & (my_strava['start_date'] < last_run_date)
my_marathon = my_strava.loc[mar]
my_marathon.head()

数据如下表所示,似乎可以进行一些探索性分析。

分析

这绝对是关键时刻之一。只有当我看数据的时候,我才意识到我跑的比我想象的要少得多。在洛杉矶马拉松之前的一年里,我的跑步距离实际上是下降的。它不符合任何在线马拉松训练计划。反而是长达一年多的训练之旅,建立了我的信心和抵抗力。

fig,ax = plt.subplots(figsize=(10,7))
ax.set_title('Running Distance (Miles)')
ax.plot('start_date','distance','bo--',data=my_marathon)
arrowprops=dict(arrowstyle='<-', color='red')
# add annotations for 2018-07-29 for SF half, 2019-03-24 for LA full.
ax.annotate(s='SF half marathon',xy=((datetime.datetime.strptime('2018-07-29','%Y-%m-%d')),13.1),\
           xytext=((datetime.datetime.strptime('2018-08-01','%Y-%m-%d')),15.0),\
           arrowprops=arrowprops);
ax.annotate(s='LA marathon',xy=(datetime.datetime.strptime('2019-03-24','%Y-%m-%d'),26.2),\
           xytext=(datetime.datetime.strptime('2019-01-15','%Y-%m-%d'),20.0),\
           arrowprops=arrowprops);

我的跑步大多集中在 3-7 英里,每英里 10 分钟。这种水平的表现就是所谓的“轻松步伐”。偶尔,我会跑个门槛。洛杉矶马拉松是我唯一一次跑超过 15 英里。

fig,ax = plt.subplots(figsize=(10,7))
ax.set_title('Speed (Minutes Per Mile) vs Distance (Miles)')
ax.scatter('average_speed','distance',data=my_marathon,color='blue',marker='D')
ax.set_xlabel('Minutes Per Mile')
ax.set_ylabel('Miles')
plt.show()

我的巅峰月份是 2018–07 年训练和参加旧金山半程马拉松时的 50 多英里。事件发生后,情况急转直下。然而有趣的是,低于 15 英里的最低表现出现在 2019 年 2 月洛杉矶马拉松比赛之前。同样,只有当我们看数据时才会感到惊讶!

fig,ax = plt.subplots(nrows=1,ncols=2,figsize=(15,5))
ax[0].set_title('Training Frequency: Number of Runs Per Month')
ax[0].plot('month','count','bo--',data=monthly_run)
ax[1].set_title('Training Intensity: Total Miles Per Month')
ax[1].plot('month','total_distance','ro--',data=monthly_mile)
plt.show()

给你。简而言之,这是我第一次用 Python 请求、字典、pandas 和 matplotlib 进行马拉松式的体验。

道路交通网络可视化

原文:https://towardsdatascience.com/road-transportation-optimization-with-python-part-1-visualisation-costing-698eadcdce0b?source=collection_archive---------14-----------------------

构建您的 FTL 网络性能的可视化:交付/路线每吨成本卡车尺寸

道路交通网络可视化—(图片由作者提供)

在一系列仓储作业优化之后,我们将使用相同的方法通过以下方式提高道路 运输效率

  1. 处理数据:提取非结构化的运输记录,并对其进行处理,以构建您的优化模型
  2. 提高可见性:使用 Python 可视化库来清楚地了解当前路线和卡车装载率
  3. 模拟场景:建立一个模型,模拟多种路线方案,估计对平均每吨成本的影响

💌新文章直接免费放入你的收件箱:时事通讯

一、如何用 Python 做运输计划?

问题陈述

零售店配送用 满载卡车(FTL)

  • 1 仓库使用三种型号的卡车
    (3.5T、5T、8T)运送物料
  • 第 49 家店铺交付
  • 12 个月的历史数据,其中 10,000 次交付
  • 一周 7 天的运营
  • 23 个城市
  • 您车队中的 84 辆卡车

2 辆卡车的运输路线,覆盖 2 条路线中的 5 家商店—(图片由作者提供)

目标:降低每吨成本

方法:装运合并

在这种情况下,您使用第三方承运商,按目的地对整辆卡车收费:

上表显示了承运人针对每种类型的卡车交付的每个城市所适用的费率。观察到较大卡车的每吨成本较低,一个改进措施是在建造路线时最大化装运整合

因此, 路线运输计划优化的 主要目标是在每条路线上覆盖最大数量的商店。

[## 萨米尔·萨奇

数据科学博客,专注于仓储,运输,数据可视化和机器人流程自动化…

samirsaci.com](http://samirsaci.com)

二。数据处理:了解当前情况

1.导入数据集

在开始考虑优化模型之前,你的首要任务是了解目前的情况。

从来自多个来源的非结构化数据开始,我们需要构建一组数据框架来模拟我们的网络,并提供每条路线的装载率和交付商店列表的可见性。

单店发货记录

店面地址

运输成本

2.每条路线交付的商店列表

让我们处理初始数据帧,以列出每条路线交付的所有商店。

1 条路线= 1 个卡车 ID + 1 个日期

3.添加每条路线覆盖的城市

现在让我们来计算运输公司为每条路线开具的运输成本发票:

可视化:每辆卡车的交付量百分比

每辆卡车(3.5T、5T、8T)的路线百分比(百分比)——(图片由作者提供)

平均卡车尺寸(吨)对每吨总成本(人民币/吨)的影响(图片由作者提供)

见解

  • 平均卡车尺寸:大多数小型卡车
  • 每吨成本:每吨成本与平均卡车尺寸成反比

在本教程中找到更多智能可视化的灵感,

二。理解现状:观想

1.运输计划可视化

目标:获得每天所有交付的简单可视化,重点关注不同路线的数量

运输计划:2017 年 1 月—(图片由作者提供)

解决方案 : Python 的 Matplotlib 网格函数

  • 列: 1 列= 1 家商店
  • 行: 1 行= 1
  • 颜色=白色: 0 发货
  • 颜色: 1 种颜色= 1 条路线(1 辆卡车)

视觉洞察力

  • 交货频率:每周 n 次交货
  • 路线数量:一天有多少种不同的颜色?
  • Color = White: 不发货
  • 路线:我们是否每天都有相同的商店分组?

12 个月概述

12 个月概述—(图片由作者提供)

优化后,此图表将帮助我们轻松直观地了解新路由的影响。更好的路线意味着每天更少的路线,所以每条线的颜色会更少。

2.商店交付的地理可视化

目标 将同一路线上的地理位置可视化

所有商店位置(蓝点)——(图片由作者提供)

解决方案 OpenStreet 地图+ Matplotlib 散点图

要获得这种可视化效果,你可以使用免费工具 OpenStreet Map。详细的解释可以在艾哈迈德·卡西姆写的这篇伟大的文章(在地图上绘制地理数据的简单步骤链接)中找到。

如果你需要支持来获得 GPS 坐标,看看这个简短的教程

每天不同路线的可视化

不同路线的可视化(1 种颜色= 1 条路线)——(图片由作者提供)

三。后续步骤

关注我的 medium,了解更多与供应链数据科学相关的见解。

1.衡量环境影响

除了降低成本,您还可以通过优化您的运输网络来减少二氧化碳排放量。

供应链可持续性报告—(图片由作者提供)

在这篇文章中,我介绍了一个简单的方法来使用 Python 和 PowerBI 报告你的分销网络的二氧化碳排放量。

[## 使用 Python 进行供应链可持续性报告

自动化和报告构建 ESG 报告的 4 个步骤,重点关注分销网络的二氧化碳排放 4 个步骤…

www.samirsaci.com](https://www.samirsaci.com/supply-chain-sustainability-reporting-with-python/)

2。路线优化:每条路线的交货数量

  • 处理了历史记录的数据框架
  • 当前运输计划
  • 基于配送城市的每条路线运输成本计算模型
  • 每天不同路线数量的可视化
  • 每条路线交付的地理位置可视化

接下来的步骤是

  • 路线:增加每条路线交付的店铺数量
  • 车队分配:确保工作量分配均匀
  • 发货频率:减少每周发货次数,增加每次发货数量
  • 模拟影响:我们可以从上面列出的优化中获得节约

关于我

让我们在 LinkedinTwitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运营并降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

[## Samir Saci |数据科学与生产力

专注于数据科学、个人生产力、自动化、运筹学和可持续发展的技术博客

samirsaci.com](https://samirsaci.com)

参考

[1] Ahmed Qassim ,在地图上绘制地理数据的简单步骤,链接

[2]用 Python 进行供应链可持续性报告, Samir SaciLink

计算机视觉路线图

原文:https://towardsdatascience.com/roadmap-to-computer-vision-79106beb8be4?source=collection_archive---------18-----------------------

介绍组成计算机视觉系统的主要步骤。从图像预处理、特征提取和预测开始。

恩尼奥·迪贝利在 Unsplash 上拍摄的照片

介绍

计算机视觉是当今人工智能的主要应用之一(如图像识别、目标跟踪、多标记分类)。在本文中,我将带您了解组成计算机视觉系统的一些主要步骤。

计算机视觉系统工作流程的标准表示为:

  1. 一组图像进入系统。
  2. 为了预处理和从这些图像中提取特征,使用了特征提取器。
  3. 机器学习系统利用提取的特征来训练模型并进行预测。

现在,我们将简要介绍一些主要流程,数据可能会经历这三个不同的步骤。

图像进入系统

在尝试实现 CV 系统时,我们需要考虑两个主要组件:图像采集硬件和图像处理软件。部署 CV 系统的主要要求之一是测试其健壮性。事实上,我们的系统应该能够不受环境变化的影响(比如光照、方向、比例的变化),并且能够重复执行设计的任务。为了满足这些要求,可能有必要对我们系统的硬件或软件应用某种形式的约束(例如,远程控制照明环境)。

一旦从硬件设备获得图像,在软件系统中有许多可能的方法来用数字表示颜色(颜色空间)。两个最著名的颜色空间是 RGB(红、绿、蓝)和 HSV(色调、饱和度、值)。使用 HSV 颜色空间的一个主要优点是,通过只取 HS 分量,我们可以使我们的系统光照不变(图 1)。

1: RGB 与 HSV 色彩空间[1]

特征提取器

图像预处理

一旦图像进入系统并使用颜色空间来表示,我们就可以对图像应用不同的操作符来改善其表示:

  • 点操作符:我们使用图像中的所有点来创建原始图像的变换版本(为了明确图像内部的内容,而不改变其内容)。点算子的一些例子是:强度归一化、直方图均衡化和阈值化。点操作符通常用于帮助人类视觉更好地可视化图像,但不一定为计算机视觉系统提供任何优势。
  • 组操作符:在这种情况下,我们从原始图像中取出一组点,以便在图像的变换版本中创建一个点。这种类型的运算通常通过使用卷积来完成。可以使用不同类型的核与图像进行卷积,以获得我们的变换结果(图 2)。一些例子是:直接平均,高斯平均和中值滤波。因此,对图像应用卷积运算可以减少图像中的噪点数量并提高平滑度(尽管这也可能会导致图像略微模糊)。因为我们使用一组点来在新图像中创建单个新点,所以新图像的尺寸必然会比原始图像的尺寸小。这个问题的一个解决方案是应用零填充(将像素值设置为零)或在图像边界使用较小的模板。使用卷积的一个主要限制是在处理大模板时的执行速度,这个问题的一个可能的解决方案是使用傅立叶变换。

图 2: 核卷积

对图像进行预处理后,我们可以应用更先进的技术,通过使用一阶边缘检测(例如 Prewitt 算子、Sobel 算子、Canny 边缘检测器)和 Hough 变换等方法,尝试提取图像中的边缘和形状。

特征抽出

一旦预处理了图像,有 4 种主要类型的特征形态可以通过使用特征提取器从图像中提取:

  • 全局特征:将整幅图像作为一个整体进行分析,从特征提取器中提取出一个特征向量。全局特征的一个简单例子可以是面元像素值的直方图。
  • 基于网格或块的特征:将图像分割成不同的块,从每个不同的块中提取特征。为了从图像的块中提取特征,使用的主要技术之一是密集 SIFT(尺度不变特征变换)。这种类型的特征被广泛用于训练机器学习模型。
  • 基于区域的特征:将图像分割成不同的区域(例如,使用阈值或 K-Means 聚类等技术,然后使用连通分量将它们连接成片段),并从这些区域中的每一个提取特征。可以通过使用诸如矩和链码的区域和边界描述技术来提取特征)。
  • 局部特征:在图像中检测多个单个兴趣点,通过分析兴趣点附近的像素提取特征。可以从图像中提取的两种主要类型的兴趣点是角点和斑点,这些可以通过使用诸如哈里斯&斯蒂芬斯检测器和高斯的拉普拉斯算子的方法来提取。通过使用诸如 SIFT(尺度不变特征变换)的技术,可以最终从检测到的兴趣点提取特征。通常使用局部特征来匹配图像以构建全景/3D 重建或从数据库中检索图像。

一旦提取了一组区别特征,我们就可以使用它们来训练机器学习模型进行推理。使用像 OpenCV 这样的库,可以很容易地在 Python 中应用特性描述符。

机器学习

计算机视觉中用于对图像进行分类的主要概念之一是视觉单词包(BoVW)。为了构建一个视觉单词包,我们首先需要通过从一组图像中提取所有特征来创建一个词汇表(例如,使用基于网格的特征或局部特征)。随后,我们可以计算提取的特征在图像中出现的次数,并根据结果构建频率直方图。使用频率直方图作为基本模板,我们可以通过比较它们的直方图来最终分类一幅图像是否属于同一类(图 3)。

这个过程可以总结为以下几个步骤:

  1. 我们首先通过使用特征提取算法(如 SIFT 和 Dense SIFT)从图像数据集中提取不同的特征来构建词汇表。
  2. 其次,我们使用 K-Means 或 DBSCAN 等算法对词汇表中的所有特征进行聚类,并使用聚类质心来总结我们的数据分布。
  3. 最后,我们可以通过计算词汇中的不同特征在图像中出现的次数,从每个图像中构建一个频率直方图。

然后,可以通过对我们想要分类的每个图像重复相同的过程来分类新图像,然后使用任何分类算法来找出我们的词汇表中哪个图像与我们的测试图像最相似。

图 3:视觉单词包[2]

如今,由于人工神经网络架构的创建,如卷积神经网络(CNN)和递归人工神经网络(RCNNs),已经有可能构思出计算机视觉的替代工作流(图 4)。

图 4:计算机视觉工作流程[3]

在这种情况下,深度学习算法结合了计算机视觉工作流程的特征提取和分类步骤。当使用卷积神经网络时,在将特征向量提供给密集层分类器之前,神经网络的每一层在其描述中应用不同的特征提取技术(例如,第 1 层检测边缘,第 2 层发现图像中的形状,第 3 层分割图像等等)。

机器学习在计算机视觉中的进一步应用包括多标记分类和对象识别等领域。在多标记分类中,我们的目标是构建一个模型,能够正确地识别一幅图像中有多少个对象以及它们属于哪一类。相反,在对象识别中,我们的目标是通过识别图像中不同对象的位置,将这一概念向前推进一步。

联系人

如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

文献学

[1]用作海滩清洁工的模块化机器人,Felippe Roza。研究之门。访问:https://www . research gate . net/figure/RGB-left-and-HSV-right-color-spaces _ fig 1 _ 310474598

[2]OpenCV 中的视觉词汇包,视觉与图形组。扬·昆德拉克。访问:https://vgg . fiit . stuba . sk/2015-02/bag-of-visual-words-in-opencv/**

【3】深度学习 Vs 传统计算机视觉。哈里塔·蒂拉卡拉恩,纳迪斯人。访问网址:https://naadispeaks . WordPress . com/2018/08/12/deep-learning-vs-traditional-computer-vision/

自然语言处理(NLP)路线图

原文:https://towardsdatascience.com/roadmap-to-natural-language-processing-nlp-38a81dcff3a6?source=collection_archive---------27-----------------------

介绍自然语言处理(NLP)中最常用的一些技术和模型

凯利·西克玛在 Unsplash 上的照片

介绍

由于过去十年大数据的发展。组织现在每天都面临着分析来自各种来源的大量数据。

自然语言处理(NLP)是人工智能的研究领域,专注于处理和使用文本和语音数据来创建智能机器和创造洞察力。

当今最有趣的自然语言处理应用之一是创造能够与人类讨论复杂话题的机器。迄今为止,IBM Project Debater 代表了该领域最成功的方法之一。

视频 1: IBM 项目辩手

预处理技术

为了准备用于推理的文本数据,一些最常用的技术是:

  • 标记化:用于将输入文本分割成其组成单词(标记)。这样,将我们的数据转换成数字格式就变得更容易了。
  • 停用词移除:用于从我们的文本中移除所有介词(如“an”、“the”等……),这些介词仅被视为我们数据中的噪声源(因为它们在我们的数据中不携带额外的信息)。
  • 词干化:最后使用是为了去掉我们数据中的所有词缀(如前缀或后缀)。通过这种方式,我们的算法实际上可以更容易地不将实际上具有相似含义的单词(例如,insight ~ insightful)视为区分单词。

使用标准的 Python NLP 库,例如 NLTKSpacy ,所有这些预处理技术都可以很容易地应用于不同类型的文本。

此外,为了推断我们文本的语言语法和结构,我们可以利用诸如词性(POS)标记和浅层解析等技术(图 1)。事实上,使用这些技术,我们用词汇类别(基于短语句法上下文)显式地标记每个单词。

1:词性标注示例[1]。

建模技术

一袋单词

单词包是一种用于自然语言处理和计算机视觉的技术,目的是为训练分类器创建新的特征(图 2)。这种技术是通过构建一个统计文档中所有单词的直方图来实现的(不考虑单词顺序和语法规则)。

图 2:单词袋[2]

限制这种技术有效性的一个主要问题是在我们的文本中存在介词、代词、冠词等。事实上,这些都可以被认为是在我们的文本中频繁出现的词,即使在找出我们的文档中的主要特征和主题时不一定具有真正的信息。

为了解决这类问题,通常使用一种称为“术语频率-逆文档频率”(TFIDF)的技术。TFIDF 旨在通过考虑我们文本中的每个单词在大样本文本中出现的频率来重新调整我们文本中的单词计数频率。使用这种技术,我们将奖励那些在我们的文本中经常出现但在其他文本中很少出现的单词(提高它们的频率值),同时惩罚那些在我们的文本和其他文本中频繁出现的单词(降低它们的频率值)(如介词、代词等)。

潜在狄利克雷分配

潜在狄利克雷分配(LDA)是一种主题造型手法。主题建模是一个研究领域,其重点是找出聚类文档的方法,以便发现潜在的区分标记,这些标记可以根据它们的内容来表征它们(图 3)。因此,主题建模在这个范围内也可以被认为是一种维度缩减技术,因为它允许我们将初始数据缩减到一个有限的聚类集。

图 3:主题建模[3]

潜在狄利克雷分配(LDA)是一种无监督学习技术,用于发现能够表征不同文档的潜在主题,并将相似的文档聚集在一起。该算法将被认为存在的主题的数量 N 作为输入,然后将不同的文档分组为彼此密切相关的文档的 N 簇。

LDA 与其他聚类技术(如 K-Means 聚类)的区别在于,LDA 是一种软聚类技术(基于概率分布将每个文档分配给一个聚类)。例如,可以将一个文档分配给聚类 A,因为该算法确定该文档有 80%的可能性属于该类,同时仍然考虑到嵌入到该文档中的一些特征(剩余的 20%)更有可能属于第二个聚类 b

单词嵌入

单词嵌入是将单词编码为数字向量的最常见方式之一,然后可以将其输入到我们的机器学习模型中进行推理。单词嵌入旨在可靠地将我们的单词转换到向量空间,以便相似的单词由相似的向量表示。

图 4:单词嵌入[4]

如今,有三种主要的技术用于创建单词嵌入: Word2VecGloVefastText 。所有这三种技术都使用浅层神经网络,以便创建所需的单词嵌入。

如果您有兴趣了解更多关于单词嵌入是如何工作的,这篇文章是一个很好的起点。

情感分析

情感分析是一种 NLP 技术,通常用于理解某种形式的文本是否表达了对某个主题的积极、消极或中性的情感。例如,当试图了解公众对某个话题、产品或公司的普遍看法(通过在线评论、推文等)时,这可能特别有用。

在情感分析中,文本中的情感通常表示为介于-1(消极情感)和 1(积极情感)之间的值,称为极性。

情感分析可以被认为是一种无监督的学习技术,因为我们通常不会为我们的数据提供手工制作的标签。为了克服这一障碍,我们使用预先标记的词典(一本单词书),它被创建来量化不同上下文中大量单词的情感。情感分析中广泛使用的词汇的一些例子是文本块VADER

变形金刚(电影名)

Transformers 代表当前最先进的 NLP 模型,用于分析文本数据。一些广为人知的变形金刚模型的例子有伯特GTP2

在创建变压器之前,递归神经网络(RNNs)代表了顺序分析文本数据以进行预测的最有效的方法,但是这种方法发现很难可靠地利用长期依赖性(例如,我们的网络可能会发现很难理解在几次迭代之前输入的单词是否可能对当前迭代有用)。

由于一种叫做注意力的机制,《变形金刚》成功地克服了这种限制(这种机制用于确定文本的哪些部分需要关注并给予更多的权重)。此外,Transformers 使得并行处理文本数据比顺序处理更容易(因此提高了执行速度)。

多亏了拥抱人脸库,变形金刚如今可以很容易地用 Python 实现。

文本预测演示

文本预测是可以使用诸如 GPT2 之类的转换器轻松实现的任务之一。在这个例子中,我们将引用 Carlos Ruiz Zafón 的" The Shadow of the Wind "作为输入,然后我们的转换器将生成其他 50 个字符,这些字符在逻辑上应该遵循我们的输入数据。

A book is a mirror that offers us only what we already carry inside us. It is a way of knowing ourselves, and it takes a whole life of self awareness as we become aware of ourselves. This is a real lesson from the book My Life.

从上面显示的示例输出中可以看出,我们的 GPT2 模型在为输入字符串创建可重密封的延续方面表现得非常好。

点击此链接,您可以运行一个示例笔记本来生成自己的文本。

希望您喜欢这篇文章,感谢您的阅读!

联系人

如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

文献学

[1]使用 python 中的 NLTK POS tagger 提取自定义关键字,Thinkinfi,Anindya Naskar。访问位置:https://www . thinkinfo . com/2018/10/extract-custom-entity-using-nltk-pos . html

[2]字袋模型弓和字集模型播的比较,程序员求。访问地址:http://www.programmersought.com/article/4304366575/;jsessionid = 0187 f8e 68 a 22612555 b 437068028 c 012

[3]话题建模:NLP 中讲故事的艺术,
TechnovativeThinker。访问地址:https://medium . com/@ magesh dominator/topic-modeling-art-of-story-in-NLP-4d c83 e 96 a 987

[4] Word Mover 的嵌入:来自 Word2Vec 的通用文本嵌入,IBM 研究博客。访问网址:https://www . IBM . com/blogs/research/2018/11/word-movers-embedding/

机器人已经为现实世界做好了准备

原文:https://towardsdatascience.com/robots-are-ready-for-the-real-world-32808cb1a4eb?source=collection_archive---------34-----------------------

来自加州大学伯克利分校人工智能研究所的新创业公司 Covariant.ai 展示了机器人可以在大型游戏(真实世界)中做到这一点,人工智能社区很高兴看到这一点。他们解决了泛化鲁棒性的问题。

那么什么是co variant . ai——一家隐形机器人学习初创公司,它希望将功能性机器人大规模带到现实世界。这是一个广泛的使命,但迹象表明这是一个准备充分的使命。即将播出的新闻片段只是即将到来的许多成功的第一瞥——团队和机器人才刚刚开始学习。首先是机器人配送,其次是范式转变。

我在机器人会议上的对手。

抓住(新)任务

近年来,随着电子商务的主导地位,仓储物流出现了爆炸式增长,自动化需求也随之增长。重复的,看似简单的任务对机器人来说太难代替了。

来源肯·戈德堡,自然特稿。

抓取任务是机器人研究的一个非常热门的领域。有很多研究小组或多或少地关注于开发可靠的方法来获得新的“区块”为什么机器人还没有取代人类?纵观文献( 123 ),显而易见:

  1. 机器人失败的案例太多了。拿起一个新物体的简单动作需要 a)在空间中定位它,b)规划一条路径以在它要去的地方遇到一个物体,以及 c)知道末端的手臂可以拿起它。当环境中的其他变量(如照明、对象位置或温度)发生变化时,角点案例的数量会呈指数增长。解决把握会来自于概括。
  2. 人类在这方面非常非常擅长。说实话。机器人抓取的最先进技术在他们接受训练的物体上的成功率仍然保持在 80-90%(123 )。人们认为人类的错误少一个数量级,因此成功率下限为 99%,或者是机器人每小时挑选次数的两倍多。 协变是如何弥合差距的?

最终,人类可以以极高的速度和可靠性完成这些任务,而机器人在过去十年里一直在努力解决这些问题。令人印象深刻的机器人展示背后的潜在力量是强化学习的(巨大)进步。

免责声明:经过进一步的研究,以及与伯克利研究人员的讨论,结果是 Covariant.ai 推出的产品尚未使用 RL。他们正致力于整合它,但目前它是强大的深度学习。

来源:协变的极简网站

协变通过在性能上的巨大飞跃弥合了这一差距(我想看看这些结果在多大程度上取代了该领域最近的学术基线)。他们通过 行业研究、 尖端科学、实际工程 解决的关键问题有:

1.谁需要这些机器人让他们去尝试。

Covariant 的新闻稿称,他们花了大约一半的年轻时间询问数百家需要机器人的公司,因此他们选择了物流。是聪明的计划,而不是运气,让他们在以聪明的计划而闻名的德国经销商 Knapp 引起轰动。

2.具有通用性和鲁棒性的模型结构。

他们聚集了业内最优秀的人才来解决机器人学习中的通用性和鲁棒性问题。这是大多数研究没有触及的两个领域——学生们选择一个他们想要解决的问题,然后只需要让它工作一次。在两年或更短时间内将一个疯狂的研究生想法变成一个完美的产品并不常见。他们因此获得了领先于时代的产品。

3.我们如何把它变成一个产品。

该网站暗示相机的改进,严格的实验室测试,等等。这就是橡胶肉的来源——协变公司的工程师们想出了如何在实验室中惩罚他们的机器人,所以这是现实世界中的 T2 傻瓜证据。这太令人印象深刻了。他们似乎不需要在线学习期(微调模型)或试用期(收集真实数据)。协变机器人出现了,准备改变世界。

来源:连线篇 —来自本视频

机器学习的一大步?

在过去的帖子中,我总是回到当前的人工智能系统如何需要结构化学习数据特异性。看看 Covariant 的机器人能多快地完成新任务就知道了。这里有真正的一般化吗,或者只是一个独一无二的数据集。如果这些算法可以在许多环境中工作,这将是一个明显的进步,这将在 2020 年初启动一波机器人自动化转型。

就我个人而言,我认为某种更聪明的方法正在推动这一进程走向普遍化。这些结果展示了性能的提升,远远超过了研究论文的增量改进——这通常伴随着方法的彻底改变。有一点是肯定的,物流公司 Knapp 已经感受到了这项技术的好处。纳普的创新副总裁将这些机器人的年轻生命概括为:

“我们对这个机器人进行了三四个月的测试,它几乎可以处理我们扔给它的任何东西。我们真的要把这些推向市场。我们希望有大量这样的机器。”

少数人的团体

迄今为止,在机器人和机器人任务的机器学习方面很少有经济上的成功— 任何带有与世界互动的智能体的东西 —才刚刚开始进入公众的视线。科幻小说中已经预测了几十年的时间,但现实总是不尽如人意。

来源— 罗伯托·尼克森,Pexel。

特斯拉是第一家致力于自动驾驶系统的公司,该系统使用端到端的卷积神经网络进行控制。自动驾驶仪之所以受到如此多的关注(前几个月的 12345 ),是因为它是此类的第一款,而且它拥有高调的股权。

协变是下一个让机器学习机器人进入现实世界的产品。许多其他创业公司正在研究抓取问题,包括软机器人影子机器人公司双手灵巧机器人等等。更不用说亚马逊、谷歌和其他科技巨头投入的资金了。

现在我们有了特斯拉和 ,下一个 是谁?促成这一成功的另一个因素是产品推出的时机,我不会深入探讨。协变公司在这里抓住了时机,因为自动化正在积极发展,并且物流对公司来说是一项巨大的成本(亚马逊的股票在盘后交易中上涨了 12%,此前他们降低了物流成本的增长率——是的,成本仍在上升)。

机器人不再需要成为桌面玩具。(图片由作者提供)

我想以机器人研究人员的身份来写,但我可以把主流印象留给媒体。该公司于 1 月 29 日退出黑暗模式,迎来了一系列正面报道——协变出现在纽约时报IEEE SpectrumThe VergeWired麻省理工科技评论华尔街日报中。

[## 面向真实世界的人工智能机器人

“人工智能机器人技术正迅速与每一个仓库运营相关联,并对 KNAPP 的战略至关重要……

协变. ai](https://covariant.ai/)

尽管如此,该公司目前不太可能需要更多资金,因为它得到了一个大胆的机器学习创始人名单的支持(来自《连线》的文章):杰弗里·辛顿和扬·勒村(深度学习的联合创始人)、杰夫·迪恩(谷歌人工智能负责人)费-李非丹妮拉·鲁斯(分别是斯坦福大学和麻省理工学院人工智能研究部门的负责人)。

我和他们在一起。我迫不及待地想看看协变和人工智能社区的下一步。

我去本德旅行的照片。

使用 Pandas 1.1.0 进行可靠的 2 数据帧验证

原文:https://towardsdatascience.com/robust-2-dataframes-verification-with-pandas-1-1-0-af22f328e622?source=collection_archive---------48-----------------------

使用最近添加的 DataFrame.compare()进行可靠的资格检查。

库纳尔·卡拉在 Unsplash 上拍摄的照片

Pandas 是数据科学家和数据工程师最常用的 Python 库之一。今天,我想分享一些 Python 技巧来帮助我们在两个数据帧之间进行资格检查。

注意,我用了这个词:资格,而不是完全相同。相同很容易检查,但是资格是一个松散的检查。它是基于商业逻辑的。因此,更难实现。

不是重新发明轮子

1.1.0 版本中——发布于 2020 年 7 月 28 日,8 天前——Pandas 引入了内置的 比较功能。我们接下来的所有步骤都是基于它构建的。

用于比较的官方文档截图

提示:如果您正在使用 Anacondas 发行版,您应该使用下面的命令行来升级您的 Pandas 版本。

低垂的果实

总是先检查两帧之间的列数。在某些情况下,这种简单的检查可以发现问题。

在某些情况下,如浓缩变化,我们可以有不同数量的列。合格的定义可以是:对于在 2 个数据帧之间具有相同值的所有前面的列。因此,我们将确定要检查的列,并将它们保存在变量中,以备后用。

开锁的钥匙

在实际应用中,我们会有不同的 id 来标识一个记录,比如 user-id、order_id 等。为了进行唯一的查询,我们可能需要使用这些键的组合。最终,我们希望验证具有相同关键字的记录具有相同的列值。

第一步是编写组合键。这就是数据框架应用的亮点所在。我们可以使用 df.apply(lambda: x: func(x), axis = 1 )进行任何数据转换。在轴= 1 的情况下,我们告诉熊猫一排排做同样的操作。(轴= 0,逐列)

处理值错误

对于新的 DataFrame.compare 函数,下面的错误是最令人困惑的。让我解释一下。

值错误:只能比较标签相同的 DataFrame 对象

出现此错误的原因是两个数据框之间的列的形状和顺序不相同。是的。DataFrame.compare 仅适用于相同检查,不适用于资格检查。

解决问题的方法是:使用之前创建的 keyColumn ,比较具有相同 keyColumn 值的数据帧之间的子集。对每个 keyColumn 值都这样做。

如果两个数据框架中键列的尺寸不同,则提出问题并跳过检查。

带走:

使用最新的 Pandas 1.1.0 DataFrame.compare 进行可靠的 DataFrame 资格检查。为了处理 ValueError,我们使用 keyColumn 进行多个子数据帧检查,并返回最终决定。

遮挡倾斜人脸的面部标志检测

原文:https://towardsdatascience.com/robust-facial-landmarks-for-occluded-angled-faces-925e465cbf2e?source=collection_archive---------11-----------------------

Dlib 提供了一个很好的面部标志检测器,但是当面部处于陡峭的角度时,它不能很好地工作。了解如何使用一个可以?

照片由特伦斯伯克Unsplash 拍摄

面部标志检测或面部关键点检测在计算机视觉中有很多用途,例如面部对齐、睡意检测、Snapchat 过滤器等等。这项任务最广为人知的模型是 Dlib 的 68 关键点界标预测器,它能实时给出非常好的结果。但当人脸被遮挡或与相机成一定角度时,问题就开始了。获得精确的角度结果对于头部姿态估计等任务至关重要。所以在这篇文章中,我将介绍一个鲜为人知的 Tensorflow 模型,它可以实现这一使命。

Dlib 的问题

装置

现在,这只是因为我想找到缺点,但本质上,在 Windows 中安装 Dlib 有点困难,需要安装 Cmake 和其他一些应用程序来完成。如果您正在使用 Anaconda 并尝试使用 conda install,那么 Windows 的版本也是过时的,不支持 Python>3.5。如果你使用任何其他操作系统,那么你会没事。

倾斜的面孔

我不写错了什么,直接给你看。

Dlib 的结果

好了,诽谤 Dlib 已经够了,让我们来看看真正的东西。

要求

我们将需要 Tensorflow 2 和 OpenCV 来完成这项任务。

# pip install
pip install tensorflow
pip install opencv# conda install
conda install -c conda-forge tensorflow
conda install -c conda-forge opencv

人脸检测

我们的第一步是在图像中找到面部标志。对于这个任务,我们将使用 OpenCV 的 DNN 模块的 Caffe 模型。如果你想知道它与其他模型如 Haar Cascades 或 Dlib 的正面人脸检测器相比表现如何,或者你想深入了解它,那么你可以参考这篇文章:

[## 人脸检测模型:使用哪种模型,为什么?

一个关于用 Python 实现不同人脸检测模型的完整教程,通过比较,找出最好的…

towardsdatascience.com](/face-detection-models-which-to-use-and-why-d263e82c302c)

你可以从我的 GitHub 下载需要的模型。

import cv2
import numpy as npmodelFile = "models/res10_300x300_ssd_iter_140000.caffemodel"
configFile = "models/deploy.prototxt.txt"
net = cv2.dnn.readNetFromCaffe(configFile, modelFile)
img = cv2.imread('test.jpg')
h, w = img.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0,
(300, 300), (104.0, 117.0, 123.0))
net.setInput(blob)
faces = net.forward()#to draw faces on image
for i in range(faces.shape[2]):
        confidence = faces[0, 0, i, 2]
        if confidence > 0.5:
            box = faces[0, 0, i, 3:7] * np.array([w, h, w, h])
            (x, y, x1, y1) = box.astype("int")
            cv2.rectangle(img, (x, y), (x1, y1), (0, 0, 255), 2)

使用cv2.dnn.readNetFromCaffe加载网络,并将模型的层和权重作为参数传递。它在大小调整为 300x300 的图像上表现最佳。

面部标志检测

我们将在这个 Github repo 中使用尹提供的面部标志检测器。它还提供了 68 个地标,这是一个在 5 个数据集上训练的 Tensorflow CNN!预先训练好的模型可以在这里找到。作者还写了一系列解释背景、数据集、预处理、模型架构、训练和部署的帖子,可以在这里找到。我在这里提供了他们的概要,但是我强烈建议你去读一读。

在第一个系列中,他描述了视频中面部标志的稳定性问题,然后列出了现有的解决方案,如 OpenFace 和 Dlib 的面部标志检测以及可用的数据集。第三篇文章是关于数据预处理和准备使用的。在接下来的两篇文章中,工作是提取人脸并在其上应用面部标志,以使其准备好训练 CNN 并将它们存储为 TFRecord 文件。在第六篇文章中,使用 Tensorflow 训练了一个模型。在最后一篇文章中,模型被导出为 API,并展示了如何在 Python 中使用它。

我们需要从图像中提取的作为感兴趣区域的人脸坐标。然后,它被转换为大小为 128x128 的正方形,并被传递给模型,该模型返回 68 个关键点,然后这些关键点可以被归一化为原始图像的尺寸。

张量流模型的结果

本作品使用的完整代码:

现在,你知道它可以很好地表现侧面,它可以用来做一个头部姿态估计器。如果你想做你可以参考这篇文章:

[## Python 中的实时头部姿态估计

使用 Python 和 OpenCV 创建一个头部姿态估计器,它可以告诉你头部朝向的角度

towardsdatascience.com](/real-time-head-pose-estimation-in-python-e52db1bc606a)

速度

Dlib 的主要卖点之一是它的速度。让我们看看它们在我的 i5 处理器上是如何比较的(是的😔).

  • Dlib 给出大约 11.5 FPS,并且界标预测步骤花费大约 0.005 秒。
  • 张量流模型给出大约 7.2 FPS,并且界标预测步骤花费大约 0.05 秒。

因此,如果速度是主要关注点,并且遮挡或倾斜的人脸不太多,那么 Dlib 可能更适合你,否则我觉得张量流模型占主导地位,而不会在速度上有太大的妥协。

你可以在 GitHub 上的这里找到面部检测和地标检测的正确记录的代码,我曾试图在这里为在线监督制作一个人工智能。

非线性异方差数据的稳健线性回归模型

原文:https://towardsdatascience.com/robust-linear-regression-models-for-nonlinear-heteroscedastic-data-14b1a87c1952?source=collection_archive---------8-----------------------

图片由来自 PixabayPixabay 许可诺贝特·瓦尔德豪森拍摄

Python 的分步教程

我们将讨论以下内容:

  1. 线性回归模型假设的简要概述,其中包括关系的线性和同方差(即恒定方差)残差。
  2. 逐步指导将回归线性模型拟合到真实世界数据,而真实世界数据通常是非线性且非均方的
  3. 段涂抹估计量介绍:减少预测误差的必要工具。
  4. 一个基于 Python 的教程:一直以来,我们将使用来自美国 FRED 的黄金出口价格指数的真实世界数据集,我们将使用 Python、Pandas、Numpy、Patsy 和 Statsmodels 详细说明每个步骤。

这篇文章有一种“边学边写”的风格。

我将从第一点的一点理论开始,然后我们将直接进入第二到第四点的动手部分。

线性回归模型的假设

线性回归模型,如线性回归模型,如线性回归模型,对线性关系的建模非常有效。它们的运行特性已经被很好地理解,并且它们得到了数十年研究的支持,导致了可解释的、可辩护的和高度可用的结果。

线性回归模型有几个假设,即:

  • 线性:因变量和解释变量之间的关系被假定为线性的,即可以用下面的等式来表示:
    y=βx+ϵ* 其中 y 是因变量向量 β 是(常值)回归系数的向量,【ϵ】是误差项的向量,即y【x无法解释的部分。
  • 独立同分布残差:回归模型的残差【ϵ】被假设为 i 相互独立(即残差之间没有相关性)并且IId*同分布。此外,我们更喜欢(但不要求)残差是正态分布的。*
  • 同方差残差:残差中的方差被假定为常数,特别是方差应该而不是是因变量(或解释变量×时间** )的函数,或者是时间的函数(在时间序列数据的情况下)。**

在真实世界的数据集中,数据通常是非线性的和异方差的(即非异方差的)。模型的残差也可能不是完全同分布或正态分布的。

在本文中,我们将了解如何将线性模型拟合到数据中,尽管存在这些实际障碍。

让我们开始吧。

真实世界的数据集

我们将使用以下黄金价格指数数据集(此处可用):

出口价格指数(最终用途):非货币黄金 (来源:美国弗雷德)

步骤 1:加载数据集

让我们将数据加载到一个熊猫数据框架中:

**import pandas as pdimport numpy as npfrom matplotlib import pyplot as pltdf = pd.read_csv('monthly_gold_price_index_fred.csv', header=0, infer_datetime_format=True, parse_dates=[0], index_col=[0])print(df.head(10))**

以下是前 10 行:

(图片由作者)

我们将添加一个名为Time _ Period的新列,其取值范围为 1 到 132。

***df['Time_Period'] = range(1, len(df)+1)***

打印前 10 行:

***print(df.head(10))***

(图片由作者提供)

步骤 2:检查数据

绘制因变量出口价格黄金指数时间段的关系图:**

****#Create a new pyplot figure to plot into** fig = plt.figure()**#Set the title of the plot** fig.suptitle('Export Price Index of Gold')**#Set the X and Y axis labels** plt.xlabel('Time Period')
plt.ylabel('Price Index')**#plot the time series and store the plot in the *actual* variable. We'll need that later for the legend.** actual, = plt.plot(df['Time_Period'], df['Export_Price_Index_of_Gold'], 'bo-', label='Gold Price Index')**#Set up the legend. There is only one time series in the legend.** plt.legend(handles=[actual])**#Show everything** plt.show()**

剧情是这样的:

从 2001 年 1 月到 2011 年 12 月连续 132 个月的黄金出口价格指数(来源: 美国弗雷德 ) (图片由作者

价格数据显示 异方差 即非常数方差,以及 非线性增长

为了确认异方差性,让我们绘制出口价格黄金指数相对于时间段的第一个差值:

****#create a time lagged column** df['LAGGED_Export_Price_Index_of_Gold'] = df['Export_Price_Index_of_Gold'].shift(1)**#Do a diff between the Export_Price_Index_of_Gold column and the time lagged version** df['DIFF_Export_Price_Index_of_Gold'] = df['Export_Price_Index_of_Gold']-df['LAGGED_Export_Price_Index_of_Gold']**#Plot the diff column using Series.plot()** df['DIFF_Export_Price_Index_of_Gold'].plot()**#Display the plot** plt.show()**

这是一阶差分图,显示方差随时间增加:

显示异方差的出口价格指数的第一个差异(图片由作者提供)

第三步:消除非线性

我们将首先处理数据中的非线性。

取因变量的 对数 平方根 具有使数据线性的效果,同时减弱其中的异方差。

我们将使用出口价格指数的自然对数,因为对数函数比平方根函数增长得更慢,所以它的转换效果比平方根更强。

在使用这两种变换(对数和平方根)时,我们也应该记住它们的缺点。

对数和平方根变换的缺点:

  • ****负值:两种变换都产生负 y 值的未定义值。数据集的负部分需要以不同的方式处理,例如通过两部分模式。
  • ****零值:对于 y=0,对数变换未定义。这可以通过在每个 y 值上增加一个微小的正值来解决,代价是引入一个偏差。更好的方法是使用两部分模型,其中逻辑回归模型学习区分零数据和非零数据,而 OLSR 模型适用于正值数据的对数转换 y。另一种方法是使用一个可以容许零值的 G 一般化 L 线性 M 模型(GLM)。

对数和平方根转换最适用于正值数据。

还有其他转换可供选择,例如双指数数据的双对数转换。

那么,为您的数据选择哪种正确的变换(也称为标度)呢?

对数变换的基本假设是原始数据呈现指数趋势。使用平方根变换,您假设原始数据呈现幂趋势。

在他们的书“广义线性模型”中,McCullagh 和 Nelder 先生提供了一条关于选择正确变换的简洁建议,我将解释如下:

一个好的数据转换函数应该能够实现以下三个目标:

它使方差或多或少地保持恒定,即它减弱了数据中的异方差。

它使得模型的残差几乎呈正态分布。

它确保了线性模型的解释变量和因变量之间的线性加性关系。

让我们继续进行对数变换的变换练习。

让我们向名为LOG _ Export _ Price _ Index _ of _ Gold的数据框添加一个新列,其中包含。我们将使用 numpy.log() 来完成这项工作。

**df['LOG_Export_Price_Index_of_Gold'] = np.log(df['Export_Price_Index_of_Gold'])**

原始变量和记录变量的并排比较揭示了对数变换已经使时间序列线性化:

****#Create a (2 x 1) grid of subplots**
ax = plt.subplot(1, 2, 1)**#Set the title of the first sub-plot**
ax.set_title('Export_Price_Index_of_Gold versus Time_Period', fontdict={'fontsize': 12})**#Plot the RAW scale plot**
plt.scatter(x=df['Time_Period'].values, y=df['Export_Price_Index_of_Gold'].values, color='r', marker='.')**#Setup the second subplot**
ax = plt.subplot(1, 2, 2)ax.set_title('LOG(Export_Price_Index_of_Gold) versus Time_Period', fontdict={'fontsize': 12})**#Plot the LOG scale plot**
plt.scatter(x=df['Time_Period'].values, y=df['LOG_Export_Price_Index_of_Gold'].values, color='b', marker='.')**#Display both subplots**
plt.show()**

黄金价格指数原始比例图和对数比例图的比较(图片由作者提供)

步骤 4:将线性回归模型拟合到经过对数变换的数据集

由于log(y)相对于 Time_Period 是线性的,我们将使用以下线性回归方程来拟合log(y)的 OLS 回归模型:

log(Export _ Price _ Index _ of _ Gold)β_ 0+β_ 1Time _ Period***

其中,β_ 0&β_ 1分别为回归截距和回归系数。

导入回归包:

***import** statsmodels.api **as** sm
**import** statsmodels.formula.api **as** smf
**from** patsy **import** dmatrices*

patsy 语法中形成模型表达式。我们告诉 Patsy黄金的 LOG _ Export _ Price _ Index _ of _ Gold依赖于 Time_Period 。Patsy 将自动包含截距 β_0 :

*expr = 'LOG_Export_Price_Index_of_Gold ~ Time_Period'*

创建训练集和测试集:

***#We'll train the model on the first 120 time periods i.e. 120 months of data and we'll test its predictions on the last 12 time periods i.e. 12 months** split_index = 119**#Get the indexed date at the split position** split_date = df.index[split_index]**#time periods 0 to 119 is the training set** df_train = df.loc[df.index <= split_date].copy()**#time periods 120 to 131 is the testing set** df_test = df.loc[df.index > split_date].copy()**print**('Model will train on the first ' + str(len(df_train)) + ' months and make predictions for the final ' + str(len(df_test)) + ' months.')*

我特意选择了最后 12 个时间段作为维持(测试)集。如果你仔细观察黄金价格指数图,你会发现在过去的 10-20 个时间段内,数据变得更加非线性,从预测的角度来看,这个区域对于线性模型来说尤其具有挑战性。

让我们在训练集上建立并训练一个普通的最小二乘回归(OLSR)模型:

*olsr_results = smf.ols(expr, df_train).fit()*

打印培训总结:

*print(olsr_results.summary())*

OLSR 模式总结(图片由作者

让我们检查模型的性能:

R 平方:该模型能够解释 log(y)中 97.7%的方差,这是一个非常好的拟合。

F 统计量:6.42 e-99 的 p 值小得令人难以置信——远小于一个连 0.1% (0.001)都没有的临界值。因此,我们拒绝 f 检验的零假设,即模型不比仅截距模型好,并且我们接受 f 检验的替代假设,即模型的系数联合显著。

模型系数的显著性:模型系数的 p 值表明它们分别具有统计显著性

总体而言,该模型似乎在训练数据集上实现了高拟合优度。

让我们获取模型对训练集和测试集的预测。

注意,我们是预测 log(),而不是 raw**

****#Predict log(y) on the training data set**
olsr_predictions_training_set = olsr_results.predict(df_train['Time_Period'])**#Predict log(y) on the testing data set**
olsr_predictions_testing_set = olsr_results.predict(df_test['Time_Period'])**

在对数标度上绘制预测值和实际值:

****#Create the pyplot figure for plotting**
fig = plt.figure()fig.suptitle('Predicted versus actual values of LOG(price index)')**#Plot the log-scale PREDICTIONS for the training data set**
predicted_training_set, = plt.plot(df_train.index, olsr_predictions_training_set, 'go-', label='Predicted (Training data set)')**#Plot the log-scale ACTUALS fpr the training data set**
actual_training_set, = plt.plot(df_train.index, df_train['LOG_Export_Price_Index_of_Gold'], 'ro-', label='Actuals (Training data set)')**#Plot the log-scale PREDICTIONS for the testing data set**
predicted_testing_set, = plt.plot(df_test.index, olsr_predictions_testing_set, 'bo-', label='Predicted (Testing data set)')**#Plot the log-scale ACTUALS for the testing data set**
actual_testing_set, = plt.plot(df_test.index, df_test['LOG_Export_Price_Index_of_Gold'], 'mo-', label='Actuals (Testing data set)')**#Set up the legends**
plt.legend(handles=[predicted_training_set, actual_training_set, predicted_testing_set, actual_testing_set])**#Display everything**
plt.show()**

训练和测试数据集上【y】的预测值与实际值(图片由作者提供)

步骤 5:将模型的预测从对数比例转换回原始比例

这是一个需要小心的地方。我们的本能可能是简单地将对数标度预测指数化回原始标度 y

但是我们的直觉是错误的。让我们看看这是为什么。

如果你喜欢,你可以跳过下面的一点点数学,向下滚动到段的涂抹估计器 的章节。

log(y**)X 的条件期望通过以下线性方程与拟合模型的预测相关:**

e(log(y)|x)=β_ fittedx+…(1)*

其中:

  • E(log(y)|X)log(y)y*的值*****
  • β_fitted 是训练模型系数的向量,包括截距的占位符。
  • X 是回归变量的矩阵+一列为回归截距。
  • ϵ 是拟合模型的残差向量。ϵ=predictions减去 实际值

为了将预测转换回原始比例,我们对等式(1)的两边取幂:

exp(e(log(y)|x)= exp(β_ fittedx+*

我们要的是不是exp(E(log(y)|X)。相反我们要的是E(exp(log(y)|X))。注意这两者之间微妙但重要的区别。**

因此,我们将 E() 运算符应用于等式(2)的两边:

e(exp(e(log(y)|x))= e(exp(β_ fittedx+【ϵ***

经过一些简化后,它变成了:

e(y|x)= e(exp(****β_ fitted*x)【t90)

这最终简化为:

e(y)|x)= exp(β_ fittedx) e**

其中:

  • E(X)|y)是原始标度中的条件期望。正是我们想要的**
  • exp(β_ fitted***X)是模型的对数标度预测,取幂后转换为原始标度。
  • e(exp()是模型的对数标度模型残差的期望值,在取幂以便将它们转换成原始标度之后。**

段涂抹估计量

总之,为了正确地将模型预测从对数标度重新转换回原始标度,我们需要执行以下操作:

  1. 对回归模型的预测求幂,
  2. 将指数化预测乘以指数化对数标度误差的期望值,即e(exp(【ϵ))。

指数化残差的期望 e())称为段涂抹估计量,也称为涂抹因子。

E 的值(exp(ϵ)取决于对数标度残差、* 的分布如下:*

**情况一:对数尺度残差 ϵ 正态分布:**t35】如果 ϵ ~ N(0,σ )ϵ 正态分布,均值=0,方差 σ,则涂抹因子 E(exp()这就是 N(0,σ ) 的一阶矩,即。 exp(0 + 0.5σ ) = exp(0.5σ )

情况二:对数尺度残差 ϵ 同分布但不正态分布: 如果对数尺度残差是In 独立,I*d 同分布随机变量但不是 N(,σ ) ,则需要找出它们的分布并然后我们取该分布的一阶矩来得到涂抹因子的值e(exp(【ϵ)。*

情况 3:对数尺度残差分布未知: 在这种情况下:

(图片由作者)

其中 ϵ_i =第 I 个样本的误差,n=样本数。

从上述三个案例中,我们可以看出:

当误差分布较大时,即方差 σ 较大时,涂抹因子较大,或者在情况#3 中,涂抹因子的值较大。

让我们为我们的例子计算涂抹因子。

步骤 5A:计算段的拖尾因子

让我们看看残差是否是正态分布的(上面的情况 1)。

我们的对数标度模型的残差存储在olsr_results.resid中。下面是最上面的几行:

OLSR 的残差(图片由作者提供)

为了知道误差是否正态分布,我们将关注 OLSR 模型输出底部的 4 个证据:

OLSR 模型剩余误差的特性(图片由作者提供)

偏斜度:残差的偏斜度(-0.025)几乎为零。相比之下,正态分布的偏斜度为零。

峰度:残差的峰度(2.984)几乎等于 3.0。正态分布的峰度是 3.0。

正态性的综合 K 检验:综合检验极高的 p 值(0.96)使我们接受了检验的零假设,即残差呈正态分布。

Jarque-Bera 正态性检验:JB 检验极高的 p 值(0.993)再次验证了 JB 检验的残差正态分布的零假设。

所有证据表明残差呈正态分布。

这一结论也从视觉上得到证实:

*plt.hist(olsr_results.resid, bins=20)
plt.show()*

残差直方图(图片由作者提供)

让我们使用pandas.core.series.Series对象的 mean()var() 函数打印出残差的均值和方差 σ :

*print('Mean of residual errors='+str(olsr_results.resid.mean()))
print('Variance of residual errors='+str(olsr_results.resid.var()))Mean of residual errors=-3.841371665203042e-15
Variance of residual errors=0.005319971501418866*

我们可以看到,平均值实际上是零。这完全可以从 OLSR 模型中预料到。方差为 0.00532。

由于我们已经证明残差是 N(0,σ ) 分布,根据情形#1 ,段的涂抹因子为:

**E(exp(****ϵ****)) = exp(0.5σ²) = exp(0.5**0.00532) = **1.00266***

我们的抹黑因素到位了。我们准备好使用它了吗?还没有。我们还需要检查最后一件事:

残差是齐次的吗?

步骤 5B:检查残差是否是同方差的

如果残差不是异方差的,即它们是异方差的,则段的涂抹因子产生有偏的结果。**

ϵ 为异方差时,方差(ϵ)不是常数。事实上方差()可以是模型的解释变量×t58】的函数。在我们的例子中,X=Time _ Period********

例如,如果**为 N( 0,σ(x)分布,即残差服从零均值正态分布,并且有一个方差函数:【σ(x),那么根据前面提到的案例#1 😗*

涂抹因子= e(exp()= exp(0.5 σ(x*)

当回归模型的残差在对数尺度上是异方差时,为了计算涂抹因子,我们需要知道如何计算方差函数σ (X)。

然而在实践中,计算方差函数σ(X)可能不是一件容易的事情。

当残差在对数尺度上是异方差的时,通常最好执行以下操作:

处理异方差残差的方法

  • 验证通货膨胀和季节性的影响已经通过通货膨胀调整和季节性调整被抵消。这与货币数据尤其相关。
  • 检查模型中是否缺少任何重要的解释变量,并将它们添加进来。
  • 代替使用原始残差,使用 异方差调整残差 (也称为“白化”残差)来计算段的涂抹估计量 Statsmodels 通过变量RegressionResults.wresid使白化残差在回归模型的训练输出中可用。
  • 切换到 a G 通用 L 线性 M 模式( GLM )。GLM 假设方差均值的函数,而均值本身是解释变量 X 的函数。
  • 切换到一个 W 八个 LS 方( WSS )或一个 G 一般化 L 东 S 方( GLS )模型,该模型不假设方差是同质的。
  • 最后,如果残差中的异方差很小,并且你的 OLSR 模型在其他方面表现良好,就照原样接受你的 OLSR 模型吧!****

让我们测试一下我们的模型的残差是否是同方差的。如果是,我们是清白的,否则我们应该考虑上述 4 个补救措施之一。

Statsmodels 包含一个异方差的怀特测试的实现,它可以很容易地应用于我们的残差,如下所示:****

****#Using patsy, pull out the *X* matrix containing the Time_Period and the intercept columns from the pandas Data Frame.**expr = 'LOG_Export_Price_Index_of_Gold ~ Time_Period'y_train, X_train = dmatrices(expr, df_train, return_type='dataframe')**

导入测试包并运行 White 的测试:

**from statsmodels.stats.diagnostic import **het_white****from** statsmodels.compat **import** lzipkeys = ['**Lagrange Multiplier statistic:**', '**LM test\'s p-value:**', '**F-statistic:**', '**F-test\'s p-value:**']test = **het_white**(olsr_results.resid, X_train)lzip(keys, test)**

这将打印出以下输出:

**[('**Lagrange Multiplier statistic:**', 3.2148975951052883), 
 ("**LM test's p-value:**", 0.20039821889655918), 
 ('**F-statistic:**', 1.610406682366166), 
 ("**F-test's p-value:**", 0.2042032693339592)]**

****LM 检验:LM 检验的统计量遵循卡方分布,自由度=模型-1 的 DF =(3–1)= 2。p 值 0.2 使我们接受检验的零假设,即残差中没有异方差

f 检验:f 检验的统计量遵循 f 分布。同样,0.204 的高 p 值证实了检验的零假设,即残差中不存在异方差。

总体而言,我们的结论是残差是同方差的。

让我们进行回归分析的最后一步,将预测值转换回原始标度,并使用段的模糊因子进行修正。

步骤 5C:将预测从对数标度转换为原始标度

回想一下,训练集和测试集的对数标度预测和对数标度实际值存储在以下变量中:

  • olsr_predictions_training_setdf_train['LOG_Export_Price_Index_of_Gold']
  • olsr_predictions_testing_setdf_test['LOG_Export_Price_Index_of_Gold']

让我们对所有四个变量进行指数运算,将它们转换回原始比例。为此,我们将使用 numpy.exp() :

**olsr_predictions_raw_scale_training_set = **np.exp**(olsr_predictions_training_set)actuals_raw_scale_training_set = **np.exp**(df_train['LOG_Export_Price_Index_of_Gold'])olsr_predictions_raw_scale_testing_set = **np.exp**(olsr_predictions_testing_set)actuals_raw_scale_testing_set = **np.exp**(df_test['LOG_Export_Price_Index_of_Gold'])**

将原始比例预测值乘以我们之前计算的段涂抹系数 1.00266

****adjusted**_olsr_predictions_raw_scale_training_set = olsr_predictions_raw_scale_training_set***1.00266****adjusted**_olsr_predictions_raw_scale_testing_set = olsr_predictions_raw_scale_testing_set***1.00266****

最后,绘制所有四个(调整后的)原始比例值:

****#Create the pyplot figure for plotting**
fig = plt.figure()fig.suptitle('Predicted versus actual values of Gold Price Index')**#Plot the raw scale predictions made on the training data set**
predicted_training_set, = plt.plot(df_train.index, adjusted_olsr_predictions_raw_scale_training_set, 'go-', label='Predicted (Training data set)')**#Plot the raw scale actual values in the training data set**
actual_training_set, = plt.plot(df_train.index, df_train['Export_Price_Index_of_Gold'], 'ro-', label='Actuals (Training data set)')**#Plot the raw scale predictions made on the testing data set**
predicted_testing_set, = plt.plot(df_test.index, adjusted_olsr_predictions_raw_scale_testing_set, 'bo-', label='Predicted (Testing data set)')**#Plot the raw scale actual values in the testing data set**
actual_testing_set, = plt.plot(df_test.index, df_test['Export_Price_Index_of_Gold'], 'mo-', label='Actuals (Testing data set)')**#Set up the legends**
plt.legend(handles=[predicted_training_set, actual_training_set, predicted_testing_set, actual_testing_set])**#Display everything**
plt.show()**

我们得到如下的情节:

训练和测试数据集上的 原始标度 y 的预测值与实际值(图片由作者提供)

人们不应该对测试数据集上的实际值(洋红色)和预测值(蓝色)之间的较大偏差感到太失望,原因有两个:

  1. 请注意,在维持期间,原始数据变得明显非线性,因此我们的维持集对于线性模型尤其具有挑战性,
  2. 我们要求我们的模型预测未来整整 12 个月的价格指数!实际上,我们会做n 步滚动预测,其中 n 最多可以是 1 到 6 个月。

摘要

我们在本文中讨论了几个主题。我们来总结一下:

  1. 线性回归模型假设概述。
  2. 非线性数据线性化技术,这些技术的缺点以及如何处理这些缺点。
  3. 如何将线性模型拟合到线性化数据,以及如何评估其性能。
  4. 概述段的涂抹因子以及如何使用它来提高拟合模型的预测精度。
  5. 如何在模型的剩余误差中发现异方差以及你处理它的选择。

参考

段,N. (1983),“涂抹估计:一种非参数重变换方法,《美国统计学会杂志》,78,605–610。

曼宁,w .,穆拉希,J. (2001),“估计对数模型:转变还是不转变,《卫生经济学杂志》,20,461–494

相关阅读

** [## 线性回归的假设

以及如何使用 Python 测试它们。

towardsdatascience.com](/assumptions-of-linear-regression-5d87c347140) [## 异方差没有什么可怕的

使用 Python 的原因、影响、测试和解决方案

towardsdatascience.com](/heteroscedasticity-is-nothing-to-be-afraid-of-730dd3f7ca1f) [## 使用偏度和峰度检验正态性

…以及使用综合 K 平方和 Jarque–Bera 正态性检验的分步指南

towardsdatascience.com](/testing-for-normality-using-skewness-and-kurtosis-afd61be860) [## 回归分析的 f 检验

如何使用它,如何解释它的结果

towardsdatascience.com](/fisher-test-for-regression-analysis-1e1687867259)

感谢阅读!如果您喜欢这篇文章,请关注我的Sachin Date以获得关于回归和时间序列分析主题的提示、操作方法和编程建议。**

机器学习算法实例 ROC 分析

原文:https://towardsdatascience.com/roc-analysis-with-practical-example-f899cd10dd47?source=collection_archive---------33-----------------------

用混淆矩阵逐步引导受试者工作曲线

米利安·耶西耶在 Unsplash 上拍摄的照片

在机器学习应用中,分类算法用于获得对数据流的预测,以便标记对象供进一步分析。在这种应用中,不仅分类算法的准确性很重要,而且算法的灵敏度或真阳性率(检测/识别率)也很重要。机器学习模型的训练阶段的数据不平衡的例子可以显示准确性和检测率的重要性。具有大量样本的任意类别(例如,汽车)将给出良好的准确性,因为分类器在训练期间已经看到该类别的许多示例,然而,分类器在具有少量样本的类别(人类)上将表现不佳。这意味着系统的整体识别率会更低。准确度、精确度和识别率可以用受试者工作特性(ROC)曲线来衡量。

ROC 最初用于第二次世界大战期间的雷达信号分析。目前,ROC 分析用于信号检测理论、机器学习、测量系统和医疗诊断应用。

在本文中,我们将讨论 ROC 分析如何用于计算机视觉算法的分类准确性。ROC 分析的基本概念可以通过混淆矩阵(也称为误差矩阵)很容易地理解。

混淆矩阵

混淆矩阵显示了分类模型相对于实际结果/基本事实做出的正确与不正确预测的数量。通用混淆矩阵有四个类别,如表 1 所示。我们用一个简单的例子来解释,如图 1 所示的黑色背景中人体的检测。

  • TP =真阳性,当物体的边界被正确分类(白色像素)为真时。预期结果。
  • FP =假阳性,当黑色背景被分类为人类时,这是错误的分类(红色像素)。不是预期的结果。
  • TN = True Negative,当背景没有被分类为人类时为真。预期结果。
  • FN =假阴性,当一个人被归类为假背景(绿色)的一部分时。不是预期的结果。

困惑矩阵(来源:作者)

给定混淆矩阵,很容易在 ROC 或精确回忆空间中构建一个点。在 ROC 空间中,假阳性率 ( FPR )标绘在 x 轴上,真阳性率 ( TPR )标绘在 y 轴上。

FPR 是误分类样本(假阳性)与总阴性样本(假阳性+真阴性)的比率。简单来说,当答案实际上是否定的时候,有多少次答案被报为是。它包括图 1 中的黑色背景区域。

TPR 是正确分类的样本(真阳性)与总阳性样本(真阳性+假阴性)的比率。简单来说,答案是肯定的时候,有多少次答案被报为肯定。它包括图 1 中的白色人体部分区域。

在 PR 空间中,召回绘制在 x 轴上,精度绘制在 y 轴上。召回与 TPR 相同,而精度是真正分类的样本(真阳性)与总阳性样本的比率。换句话说,精确度就是如果答案是肯定的,它被报告为肯定的频率[2]。

示例:作为二元分类问题的人体检测

嵌入式智能摄像机系统在许多现实世界的监控应用中越来越受欢迎。然而,当在户外环境中使用视觉算法时,仍然存在挑战,即照明、阴影、遮挡和天气条件的变化。为了应对这些挑战,为了减少背景噪声,创建了成像流水线。背景减除是成像流水线中提高信噪比的重要步骤。在这项工作中,将使用 ROC 曲线评估低复杂性背景建模和减法技术。三种背景减除算法的性能:将比较低通无限脉冲响应滤波器、渐进生成和混合技术。为简单起见,减影图像被分割,以便将研究限制为二元分类问题。

在图 1 中示出了参考分割图像相对于地面真实图片(手动计算/提取)的正片和底片。在图中,TP 代表被正确标记为阳性的像素,FP 代表被错误标记为阳性的像素,TN 代表被正确标记为阴性的像素,FN 代表被错误标记为阴性的像素。

图片来源:作者

通过使用 TP、FP、TN 和 FN 的四种组合,我们可以绘制 ROC 曲线,该曲线可以绘制为精确度对召回率或真阳性率(TPR)对假阳性率(FPR)。

精确或召回:被正确识别的实际阳性病例的属性。

(来源:作者)

根据定义,一个好的算法应该同时产生少量的误报和漏报。这意味着精确度和召回率都应该有很高的值。精度和召回产生具有一个阈值的单个值,因此我们使用一系列阈值来产生曲线。这也有助于确定合适的阈值[1]。

三种背景减除算法的比较:包括低通无限脉冲响应滤波器、渐进生成和混合技术的三种背景减除技术的 ROC 曲线如图 2 所示。很明显,与其他两种方法相比,混合技术具有高精度和高召回率。

图 3 显示了假阳性率(FPR)对真阳性率(TPR)的 ROC 曲线。在该曲线中,与其他两个公开的系统相比,对于我们提出的算法,TPR 高,FPR 低。

图二。LPIIR、混合和渐进背景减除算法的 ROC 曲线。(来源:作者)

图 3。LPIIR、混合和渐进背景减除算法的 ROC 曲线。(来源:作者)

结论:

在机器学习中,由于复杂性的降低,分类问题通常被简化为二元决策问题。评估分类算法的性能非常重要,因为整个系统的检测性能不仅取决于准确性,还取决于检测率。ROC 曲线显示了分类器在所有可能阈值上的性能,用户可以为算法选择合适的阈值。

ROC 曲线是通过沿 y 轴绘制真阳性率相对于沿 x 轴绘制假阳性率而生成的。给出的用 ROC 曲线比较三种背景扣除方法的性能的例子表明,通过观察三种算法的精度和召回率,很容易量化结果。

参考资料:

1- Yannick Benezeth,Pierre-Marc Jodoin,Bruno Emile,H_el_ene Laurent,Christophe Rosenberger。背景减除算法的比较研究。《电子成像杂志》,摄影光学仪器工程师学会(SPIE),2010 年。

2- J .戴维斯和 m .戈德里奇。精确回忆与 roc 曲线的关系。1551 号技术报告,威斯康星大学麦迪逊分校,2006 年 1 月。

相关文章

要不要把 ROC 分析应用到实时检测分类系统中?请按照指南使用 Nvidia Jetson Nano 制作一个工作系统

[## 入门:Nvidia Jetson Nano,对象检测和分类

边缘计算的未来

towardsdatascience.com](/getting-started-nvidia-jetson-nano-object-detection-and-classification-161ad566d594)

你想用 OpenCV 和 QT 在 windows 电脑上创建一个计算机视觉设置吗?请按照教程

[## 在 Windows 上使用 GStreamer 和 QT 的 OpenCV

使用 OpenCV 的 GStreamer 管道的分步指南和实例

towardsdatascience.com](/opencv-with-gstreamer-and-qt-on-windows-6f0fdb075993)

有用的链接

链接中混淆矩阵的一个简单例子

用新冠肺炎假设的例子解释 ROC 曲线:二元和多类分类教程

原文:https://towardsdatascience.com/roc-curve-explained-using-a-covid-19-hypothetical-example-binary-multi-class-classification-bab188ea869c?source=collection_archive---------8-----------------------

在这篇文章中,我清楚地解释了什么是 ROC 曲线以及如何阅读它。我用一个新冠肺炎的例子来说明我的观点,我也谈到了混淆矩阵。最后,我提供了用于绘制多类分类案例的 ROC 和混淆矩阵的 Python 代码。

作者做的手工素描。

1.介绍

在使用机器学习分类 模型的 99%的情况下,人们报告其 ROC 曲线图(以及 AUC:ROC 下面积)以及其他指标,如模型的准确性混淆 矩阵

但是什么是 ROC 曲线它告诉我们什么?为什么大家都在用它是如何连接到混淆矩阵的?继续阅读,你将能够回答所有这些问题。****

1.1.ROC 定义

受试者工作特性曲线(ROC)曲线是显示二元 分类器在其判别阈值变化时的诊断能力的曲线图。

在我深入研究细节之前,我们需要理解这个区分阈值在不同的模型中并不相同,而是特定于模型的。例如,如果我们有一个支持向量机( SVC ,那么这个阈值只不过是决策 边界方程的偏差项。通过改变 SVM 模型中的偏差,我们实际上只是改变了决策边界的位置。看看我之前发表的 SVM 的文章,了解更多关于 SVM 车型的细节。****

通过在各种阈值设置下绘制真阳性率 ( TPR )对假阳性率 ( FPR )来创建 ROC 曲线。真阳性率也称为敏感度回忆或机器学习中的检测概率。假阳性率也称为虚警概率,可以计算为(1特异性)。它告诉我们,我们的模型能够很好地区分这些类别。

很多术语吧?等一下,我将在下一节用一个例子解释所有这些术语,这个例子会让你永远记住所有这些术语。

如果你想在交互式路线图和活跃的学习社区的支持下自学数据科学,看看这个资源:https://aigents.co/learn

1.2.术语解释清楚(TP、TN、FP、FN)

新冠肺炎测试示例

让我们想象一下,我们有一个新冠肺炎测试,它能够在几秒钟内告诉我们一个人是否被病毒感染而不是。所以测试输出可以是(受影响)或(不受影响)——我们有一个二进制分类情况。****

让我们也假设我们知道地** 真相并且我们有两个种群:**

  • a) 认为确实受到影响的人 ( TP:真阳性,蓝色分布在下图中)以及
  • b) 人即不受影响 ( TN : 真底片,下图中红色分布)——**二元分类情况。**

作者做的手工素描。假设我们真的知道真相,两个人群的例子,一个受新冠肺炎影响,另一个不受影响。

因此,如前所述,我们假设我们知道基本事实,即我们真的知道谁生病了,谁没生病。

接下来,我们使用我们的 covid 测试并定义一个阈值(假设为 20)。如果测试值如果高于阈值,那么将此人表示为受影响(阳性,covid-受影响)。另一方面,如果测试值如果低于阈值,那么我们将此人表示为非** - 受影响(阴性,无 covid)。因此,基于测试的输出,我们可以将一个人表示为受影响** ( 阳性蓝色人群)或受影响 ( 阴性、红色人群)。********

要消化所有这些,请看下图。

作者做的手工素描。假设我们真的知道真相,两个人群的例子,一个受新冠肺炎影响,另一个不受影响。此外,基于测试的输出,我们可以将一个人表示为受影响的(蓝色人群)或不受影响的(红色人群)。

但是有一个问题!我们的测试不可能完美!

有些人会错误地将** 误归类阳性 (covid-affected,我们称此为假阳性 ( FP )或阴性 (covid-free,我们称此为 阴性 ( FN )。**

总结:

  • 真阳性 ( TP蓝色分布)是真正拥有新冠肺炎病毒的人**。**
  • 真阴性(TN,红色分布 ) 是真正没有新冠肺炎病毒的人
  • 假阳性(FP) 是指确实没有患病根据检测,他们被 ( )表示为患病 ( 阳性)。
  • 假阴性(FN) 是指真正患病根据检测,他们被 ( )表示为 患病 ( 阴性)。
  • 对于完美** 情况,我们会想要 TPTN FP 和**FN——这将是具有完美 ROC 曲线的完美模型。****

1.3.ROC 曲线、AUC 和混淆矩阵

中华民国

既然我们已经理解了术语 TP、TN、FP、FN 让我们再回头看看 ROC 曲线的定义。

在不同的阈值设置下,绘制真阳性率 ( TPR )相对于假阳性率 ( FPR )的 ROC 曲线。换句话说,ROC 曲线显示了对于基础模型的不同阈值设置TPR 和 FPR** 的权衡。**

作者做的手工素描。ROC 曲线显示了基础模型不同阈值设置的 TPR 和 FPR 之间的权衡。如果曲线在对角线之上,则模型是好的,并且高于概率(对于二元情况,概率是 50%)。如果曲线在对角线以下,模型就真的不好。

  • 如果曲线在对角线上方,则型号为上方的几率(二进制情况下几率为 50%)。如果曲线对角线下方,则型号不良。****

联合自卫军

如果曲线在之上或在之下对角线(机会水平),则 AUC(曲线下面积)表示AUC 的值范围0 到 1 。预测 100%错误的模型的 AUC 为 0.0,预测 100%正确的模型的 AUC 为 1.0。

混乱矩阵

使用上述所有术语,我们还可以构建由这些指标组成的著名的混淆矩阵,然后我们可以计算出真阳性率假阳性率,如下图所示为二进制分类情况。****

图来自维基百科

估计了真阳性率假阳性率(使用上表中的公式)后,我们现在可以绘制 ROC 曲线。但是等一下!

真阳性率假阳性率只是 2 个标量如何才能在 ROC 剧情里真的有曲线?

这是通过改变一些阈值设置来实现的。ROC 曲线显示了 TPR 和 FPR 对于不同阈值的权衡。

例如,在支持** 矢量 机器 ( SVC )的情况下,这个阈值只不过是决策边界等式中的偏差项。因此,我们将改变这个偏差(这将改变决策边界的位置)并且偏差**的给定值估计TPR

要了解关于支持向量机的一切,请看这篇帖子。

1.4.多类分类问题的 ROC 曲线和混淆矩阵

ROC 曲线仅二元** 分类 问题定义。但是有一种方法可以整合到多类分类问题中。为此,如果我们有 N 个类,那么我们将需要定义几个模型。******

例如,如果我们有 N=3 个类别,那么我们将需要定义以下案例:类别 1 对类别 2 的案例/模型 1 ,类别 1 对类别 2 的案例/模型 2 ,以及类别 1 对类别 3 的案例/模型 3 。****

****记住在我们的新冠肺炎测试例子中,我们有两种可能的结果,即受病毒影响(阳性)和不受影响(阴性)。类似地,在多类情况下,我们再次必须定义积极和消极的结果。

在多类情况下,对于每种情况,类是第第二类:

  • 1:1 类 vs 类* 类为 2 类***
  • 2:类 2 vs 类 3**
  • 3:1 vs 类 3

换句话说,我们可以这样想:我们问分类器“这个样本是正的还是负的?”并且分类器将预测标签()。将独立估计每种情况的 ROC,2,3。

这同样适用于混淆矩阵。对于每种情况,我们都有一个混淆矩阵

2。Python 工作示例

现在,你应该对 ROC 曲线和混淆矩阵了如指掌,也应该熟悉 TP、TN、FP、FN、TPR、FPR 等术语。现在让我们构建一个 python 工作的例子

2.1 分类模型

在之前的帖子中,我解释了什么是 SVC ,所以这里我们将使用这样一个模型。

2.2 数据集

在虹膜数据集中,我们有 3 类花和总共 4 个特征。所以分类问题不再是二元的了,因为我们有 3 个类。然而,下面的代码将为我们的多类分类问题估计和绘制 ROC 曲线。

为此,该模型将用于1 级对 2 级、2 级对 3 级以及 1 级对 3 级。因此,我们在末端有 3 种情况,在每种情况下有,偏置将变化,以便得到给定 情况ROC 曲线—因此,3 条 ROC 曲线作为输出。

2.3 使用 Iris 数据和 scikit-learn 的示例

ROC 曲线和 AUC 指标

**import matplotlib.pyplot as plt
from sklearn import svm, datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import label_binarize
from sklearn.metrics import roc_curve, auc
from sklearn.multiclass import OneVsRestClassifier
from itertools import cycle
plt.style.use('ggplot')**

让我们加载数据集,将标签二进制化,并将数据分成训练集和测试集(以避免过度拟合):

**# Load the iris data
iris = datasets.load_iris()
X = iris.data
y = iris.target# Binarize the output
y_bin = label_binarize(y, classes=[0, 1, 2])
n_classes = y_bin.shape[1]# We split the data into training and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y_bin, test_size= 0.5, random_state=0)**

最后,我们建立我们的模型(SVC)并估计 3 种情况的 ROC 曲线:1 类对 2 类、2 类对 3 类和 1 类对 3 类。

每次类是第二个 一个 1:1 vs****

**#We define the model as an SVC in OneVsRestClassifier setting.
#this means that the model will be used for class 1 vs class 2, #class 2vs class 3 and class 1 vs class 3\. So, we have 3 cases at #the end and within each case, the bias will be varied in order to #get the ROC curve of the given case - 3 ROC curves as output.
classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True, random_state=0))y_score = classifier.fit(X_train, y_train).decision_function(X_test)# Plotting and estimation of FPR, TPR
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_score[:, i])
    roc_auc[i] = auc(fpr[i], tpr[i])
colors = cycle(['blue', 'red', 'green'])
for i, color in zip(range(n_classes), colors):
    plt.plot(fpr[i], tpr[i], color=color, lw=1.5, label='ROC curve of class {0} (area = {1:0.2f})' ''.format(i+1, roc_auc[i]))
plt.plot([0, 1], [0, 1], 'k-', lw=1.5)
plt.xlim([-0.05, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic for multi-class data')
plt.legend(loc="lower right")
plt.show()**

输出图产生了我上面介绍的 python 代码。

提醒:ROC 曲线显示交易 - TPRFPR不同 阈值 设置的底层模型。如果曲线高于对角线对角线,则模型为高于几率(二进制情况几率为 50%)。如果曲线低于对角线对角线,则型号不良

如果曲线高于的或低于对角线(机会水平),则 AUC(曲线下面积)表示

混乱矩阵

现在我们也来估算一下混淆矩阵。

**from sklearn import svm, datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix# Load the iris data
iris = datasets.load_iris()
X = iris.data
y = iris.target# We split the data into training and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.5, random_state=0)# the model
classifier_svc = svm.SVC(kernel='linear',random_state=0)# fit the model using the training set
classifier_svc.fit(X_train, y_train)# predict the labels/classes of the test set
y_pred = classifier_svc.predict(X_test)**

有了预测 y_pred基础真相标签 y_test,估计混淆矩阵:

**# build the confusion matrix
cnf_matrix = confusion_matrix(y_test, y_pred)print(cnf_matrix)
#[[21  0  0]
# [ 0 29  1]
# [ 0  1 23]]**

我们可以看到,我们可以真的 **预测所有 3 组的标签/类别(对角线上的值大多表示高真的** 正的 ( TP )比率)。****

提醒 :混淆矩阵给你看的是 TN,TP,FN,FP。对角线上的值是 TP 的计数,因此这些值越高,模型的预测能力越好。

那都是乡亲们!希望你喜欢这篇文章!

最新帖子

**** [## 时间序列预测:用脸书的先知模型预测股票价格

使用可从《先知脸书》公开获得的预测模型预测股票价格

towardsdatascience.com](/time-series-forecasting-predicting-stock-prices-using-facebooks-prophet-model-9ee1657132b5) [## 支持向量机(SVM)解释清楚:分类问题的 python 教程…

在这篇文章中,我解释了支持向量机的核心,为什么以及如何使用它们。此外,我还展示了如何绘制支持…

towardsdatascience.com](/support-vector-machines-svm-clearly-explained-a-python-tutorial-for-classification-problems-29c539f3ad8) [## PCA 清楚地解释了——如何、何时、为什么使用它以及特性的重要性:Python 指南

在这篇文章中,我解释了什么是 PCA,何时以及为什么使用它,以及如何使用 scikit-learn 在 Python 中实现它。还有…

towardsdatascience.com](/pca-clearly-explained-how-when-why-to-use-it-and-feature-importance-a-guide-in-python-7c274582c37e) [## 关于 Python 中的最小-最大规范化,您需要知道的一切

在这篇文章中,我将解释什么是最小-最大缩放,什么时候使用它,以及如何使用 scikit 在 Python 中实现它

towardsdatascience.com](/everything-you-need-to-know-about-min-max-normalization-in-python-b79592732b79) [## Scikit-Learn 的标准定标器如何工作

在这篇文章中,我将解释为什么以及如何使用 scikit-learn 应用标准化

towardsdatascience.com](/how-and-why-to-standardize-your-data-996926c2c832)

敬请关注并支持这一努力

如果你喜欢并发现这篇文章有用,请关注我!

有问题吗?把它们作为评论贴出来,我会尽快回复。

参考

[1]https://en.wikipedia.org/wiki/Confusion_matrix

https://en.wikipedia.org/wiki/Support_vector_machine

[3]https://en . Wikipedia . org/wiki/Receiver _ operating _ character istic

[4]https://sci kit-learn . org/stable/modules/generated/sk learn . SVM . SVC . html

和我联系

ROC 与 TOC

原文:https://towardsdatascience.com/roc-vs-toc-bccb7d70ef07?source=collection_archive---------36-----------------------

可视化分析二元分类器的两种方法

开始了。(来源:作者)

最近,在撰写一篇关于如何优化配置二元分类器的文章时,我遇到了一个很有前途的替代方法,称为 TOC 分析。在这篇文章中,我将向您介绍这两种可视化工具背后的概念,并讨论它们的相似之处和不同之处。

动机

当谈到评估二元预测器的性能时,接收器操作特性 (ROC)曲线几十年来一直是一种主要形式,实际上早在机器学习时代之前就已经存在了(关于 20 世纪 50 年代的早期示例,请参见 Peterson 等人[1])。它允许我们评估和比较分类器的性能,因此是模型选择的有用工具。

一个分类器完全可以用一条在二维空间中画出的曲线来描述,也就是说,在你的监视器或一张纸上画出的曲线,这是基于这样一个事实:对于任何给定的测试集,2×2 混淆矩阵只有两个自由度。当且仅当你知道你的测试集的组成时,ROC 曲线上的每个点决定了相应的混淆矩阵,也就是说,你知道它包含多少阳性和阴性样本。然而,ROC 图本身包含作为视觉信息的成分。

鉴于 ROC 分析的悠久历史,这一缺点最近通过 2014 年 Pontius 和 Si 引入的(TOC)得以解决[2]。TOC 图包含完整的 ROC 信息,并允许您读取曲线上每个点的全部信息,即测试集的组成和混淆矩阵的所有四个条目。

在我们了解其工作原理之前,让我们快速回顾一下二进制分类的基本概念和符号。

二元分类基础

二进制分类中,模型性能通常通过将其对来自测试集的数据点的预测与已知的正确结果进行比较来评估,即与测试集的标签进行比较。每个数据点的预测分为四类:真阳性(TP)、真阴性(TN)、假阳性(TP)或假阴性(FN ),每类中的样本数在 混淆矩阵 中表示:

尽管混淆矩阵有 4 个分量,但实际上只有两个分量是独立的。这是因为每个实际测试集都有一定数量的 P 个实际为正的样本和一定数量的 N 个实际为负的样本。因此,不管特定分类器的性能如何,最终,我们总是得到 TP + FN = P 和 TN+FP = n。
这就产生了两个含有四个未知数的方程,并留给我们两个自由度。参数化这些自由度的一种常见方式(但不是唯一的方式)是借助于
真阳性率* (TPR)和假阳性率 (FPR)。数学上,它们被定义为*

从这些定义出发,你可以很容易地用很少的代数说服自己,TPR 和 FPR 足以确定所有四个 TP、FP、FN 和 TN,注意

**

在许多情况下,分类器基于概率分类。这意味着他们根据数据点 x 的特征来计算每个类别的概率。在二元分类的情况下,这是两个概率 p(1|x)和 p(0|x)。由于样本必须属于这两类中的任何一类,即 p(1|x)+p(0|x)=1,所以只需查看两个概率中的一个,比如 p(1|x)。为了获得二元预测,使用判别阈值将连续概率 p(1|x)离散化为两类中的任一类:

因此,TPR 和 FPR 以及混淆矩阵中的所有四个数字都依赖于阈值。这种依赖性就是 ROC 和 TOC 图的设计目的。

ROC 和 TOC 图

ROC 图和 TOC 图都是在单个图中可视化所有可能阈值选择的分类器性能的工具。但是,它们基于两个不同的坐标系。

ROC 曲线被绘制到 FPR-TPR 坐标系中,也就是说,您为 0%和 100%之间的所有阈值绘制(FPR(阈值),TPR(阈值))。另一方面,TOC 图绘制在(TP+FP)-TP 坐标系中,也就是说,您绘制每个阈值的点(TP(阈值)+FP(阈值),TP(阈值))。

1: 两个分类器的 ROC 曲线(蓝色和绿色线条)和代表不知情(随机)分类器的曲线(橙色圆点)。

TOC 图的主要吸引力在于,您可以读出 TOC 空间中每个点的完整混淆矩阵。这不仅可以通过绘制曲线来实现,还可以通过用角(0,0)、(N,0)、(N+P,P)、(P,P)来绘制周围的平行四边形框来实现。

我们将在下面更详细地讨论这个平行四边形的性质,但是现在,我们注意到从 TOC 曲线上的任何点到盒子左边界的距离对应于 FP,到右边界的距离对应于 TN,到顶部的距离对应于 FN,到底部的距离对应于 TP。

图 2: 对于 TOC 曲线上的每个点,您都可以读出混淆矩阵的所有四个分量,TP、FP、FN 和 TN,同时还可以获得 ROC 图中的所有信息。(来源:作者)

与 ROC 图不同,ROC 图不允许在不知道 P 和 N 的情况下重建混淆矩阵,P 和 N 不包含在图本身中,TOC 图包含每个给定阈值选择的混淆矩阵。此外,您可以轻松地读取测试集的大小和组成,例如,揭示数据中的偏斜度,这些偏斜度在 ROC 图中是“隐藏”的。**

ROC 空间与 TOC 空间

ROC 曲线被绑定到一个被称为 ROC 空间的正方形区域,其点对应于 TPR 和 FPR 在 0 和 1 之间的所有可能值【3】。这个正方形被从 TPR=FPR=0 到 TPR=FPR=1 的对角线切成两半。
这条对角线上的点代表所谓的
未知分类器。
这种分类器只是对数据点进行随机分类,完全不顾其实际特征值。例如,在 TPR=FPR=0.7 时,不知情的分类器会将 10 个数据点中的 7 个数据点分类为阳性,将 10 个数据点中的 3 个数据点分类为阴性。

这条对角线的两端是两个非常特殊的决定者的家。
在左下方,当 TPR=FPR=0 时,我们有一个分类器,它简单地将每一个数据点归类为负面。ROC 空间右上角的分类器将每个数据点分类为阳性。

ROC 空间的另外两个角落同样有趣。在左上角,我们有完美的分类器。它将正确的类别分配给测试集的每个单个数据点,对应于一个对角混淆矩阵,其中 TP=P,TN=N,FP=FN=0,也就是说,

通过翻转它的每个响应,你可以使完美的分类器变得完全不完美。这种最差的分类器给每个数据点分配了错误的类别,导致混淆矩阵 TP=TN=0,FP=N,FN=P,即

在 ROC 空间中,这对应于右下角,TPR=0,FPR=1

图 3:ROC 曲线(蓝色)与无信息(随机)分类器性能的比较(橙色对角线)。ROC 空间的四个极端角是:(1)完美分类器,(2)全正分类器,(3)最差可能分类器,和(4)全负分类器。(来源:作者)

TOC 空间在两个基本方面不同于 ROC 空间。首先,它不是正方形而是平行四边形,其次,它的形状取决于测试集的组成。这是选择轴的直接结果,因为与速率 TPR 和 FPR 不同,绝对值 TP 和 FP 不包含在从 0 到 1 的范围内。为了理解平行四边形,选择 TP 的任意固定值。然后,您的选择自动成为 TP+FP 的下限,TP+FP 是 TOC 图的横坐标(也称为 x 轴),因为 FP 是一个计数,因此永远不会为负。TP+FP 的上限也由您的选择决定。就是 TP+N,因为 FP 不能大于 N,TP+FP 的这些上下界形成了平行四边形的倾斜的左右边界。TP 本身也是一个计数,并且限制在从 0 到 P 的范围内,分别确定平行四边形的上下边界。因此,整个 TOC 空间可以嵌入一个 N+P 乘 P 的矩形中。

尽管有这些差异,TOC 空间只是 ROC 空间的一个(非均匀)缩放剪切版本。ROC 空间中的任何点都可以使用线性变换映射到 TOC 空间

这种转换是缩放的组合

和剪切

因此,我们可以通过使用

由于这种变换的简单性质,ROC 空间的基本几何结构被带到 TOC 空间。因此,就像 ROC 空间一样,TOC 空间也被表示无信息分类器的对角线减半。TOC 空间的角也保留了它们在 ROC 空间中的含义:左上角是完美的分类器,右下角是最差的分类器,右上角是全肯定的分类器,左下角是全否定的分类器(见图 4)。

图 4:TOC 空间的四个极端角分别是(1)完美分类器,(2)全正分类器,(3)最差可能分类器,(4)全负分类器。在第(5)点,患病率(阳性数)被正确估计。(5)(灰色区域)左边 TP+FP < P,这样患病率被低估,右边(5) TP+FP > FP,这样患病率被高估。(来源:作者)

其他派生属性在转换过程中也保持不变。例如,让我们考虑 ROC 曲线下的面积(AUROCC ),这是一个使用单个数字描述 ROC 曲线整体的典型指标。TOC 曲线下的面积(AUTOCC)与 ROC 曲线下的面积(AUROCC)成正比,可以用同样的方式解释。我们有

因此 ROC 曲线下的 ROC 空间部分(11 的正方形)与 TOC 曲线下的 TOC 空间部分(N 乘 P 的平行四边形)相同。

在 Pontius 和 Si [2]的原始出版物中有这种关系的冗长证明,但我发现更直接的考虑是只有
ϕₛcₐₗₑ影响该区域(ϕₛₕₑₐᵣ是面积保持的)并且它的雅可比矩阵是 N⋅P,因此很明显,在变换下,AUROCC 被相应地放大。

图 5:TOC 曲线下的平行四边形(蓝色区域)相对于整个平行四边形面积(蓝色+橙色部分)的分数等于 ROC 曲线下的面积。比较图 3 所示 ROC 图中的彩色区域。(来源:作者)

TOC 空间有一个值得注意的点是 ROC 空间无法识别的。它是点(P,TP),如图 4 中的点(5)所示。它是曲线上平行四边形左边界上端正下方的点。在这一点上操作的分类器产生与测试集中的阳性和阴性样本完全相同的阳性和阴性预测比率。这样的分类器可以说是正确地代表了实际的流行度。该点左侧的所有分类器都低估了阳性率,而其右侧的所有分类器都高估了阳性率。

TOC 空间的形状

虽然 ROC 空间在测试集组成变化的情况下保持几何静态,但是 TOC 空间变化强烈。Pontius 和 Si 建议通过重新调整图形的比例来“提高视觉清晰度”,这样在纸上(或你的显示器上),TP 轴和 TP+FP 轴具有相同的长度[2]。然而,用相同的比例绘制 TP 和 TP+FP 轴,并观察 TOC 空间如何随着测试集组成的变化而展开和折叠,有助于理解 TOC 空间的概念。

对于任何非空测试集,有五种可能的情况:

1.0=N

2。0 < N < P
3。0 < P=N
4。0 < P < N
5。0=P < N

为了说明,让我们考虑一个在“成人”数据集[4]上训练的玩具分类器,并用同样多的阳性和阴性样本编译一个主测试集。然后,我们可以通过控制这个集合的子集来创建所有五种可能的情况。具体来说,我们从 N=0,P=1552(情况 1)开始,增加 N(情况 2),直到达到 N=P=1552(情况 3)。从那里,我们减少 P(情况 4),直到我们最终达到 P=0,N=1552(情况 5)。图 6 说明了 TOC 空间如何由于测试集组成的这些变化而改变其形状。

图 6: 显示各种测试集构成的 TOC 空间形状的动画。请注意,在 N=0 和 P=0 这两种极端情况下,从二维坍缩到一维。(来源:作者)

图 7: 对于 0=N < P(情况 1),TOC 空间是一维的。它对应于从(0,0)到(P,P)的对角线。(来源:作者)

让我们更详细地讨论情况 1 到 5。

对于 N=0 < P, TOC space is one-dimensional! Its left and right boundaries collapse into a single diagonal line from (0, 0) to (P, P) (see Figure 7).

Although this might seem strange at first glance, it is sensible, since in absence of negatives, the performance of a classifier is in fact one-dimensional, completely determined by a single parameter, its TPR.

Going away from this extreme situation, we increase the number of negatives in the test set.

图 8 :对于 0 < N < P(情况 2),TOC 空间是一个相当窄的对角线带。(来源:作者)

对角线分成左右边界,打开了一个有限的区域,即“正常的”二维 TOC 空间。

然而,TOC 空间保留了一个相当窄的对角线形状,因为它的右边界从 TP+FP=N 开始,在 TP+FP=P 的左边很远的地方,它的左边界在这里结束(见图 8)。

图 9: 对于 0 < N=P 左边界在右边界起点的正上方结束。(来源:作者)

进一步增加否定的比例,然后我们接近 P=N 的情况,也就是说,在测试集中否定和肯定是完全平衡的。

现在,右边的 TOC 空间边界直接从左边界的端点下面开始(参见图 9)。

图 10: 对于 0 < P < N,TOC 空间相当宽广。(来源:作者)

当我们增加负数的数量超过这一点时,我们会遇到这样一种情况,左边界和右边界的对角线不再在彼此之上,在(P,0)、(N,0)、(N,P)和(P,P)之间有一个矩形区域。

因此,TOC 空间现在显得相当大(参见图 10)。

最终,我们接近了另一个极端:整个测试集都充满了否定。同样,TOC 空间的两个边界将两个折叠成一条线。

这一次,是上限和下限,因此 TOC 空间变成了从 0 到 N 的水平线段(参见图 11)。

图 11: A 0=P < N,TOC 空间是从 0 到 N 的一维线段(来源:作者)

同样,这种崩溃是合理的,因为分类器的结果现在完全由 TN 和 FP 组成,其性能完全由一个参数定义,即其 FPR。

我们通过注意到对于 N=P=0 的完全空的测试集的情况(实际上不相关,但是病理上有趣), TOC 空间将进一步折叠,成为单个点,来结束我们的 TOC 空间的往返行程。

讨论

TOC 图是统计师工具箱的有用补充。习惯于 ROC 分析的读者可以很快学会解释 TOC 图,因为这两种表示共享许多属性。

TOC 曲线比 ROC 曲线包含更多的信息。然而,ROC 图的信息稀疏性也可以被看作是有利的。正如 Fawcett 在他的ROC 分析简介【3】:ROC 图的一个优点是,它们能够可视化和组织分类器性能,而不用考虑类别分布或错误成本。当研究具有偏斜分布的学习或成本敏感学习时,这种能力变得非常重要。研究人员可以绘制一组分类器的性能图,并且该图相对于操作条件(类偏斜和错误成本)保持不变。随着这些条件的改变,感兴趣的区域可能会改变,但是图形本身不会改变。**

因此,ROC 曲线在 TOC 不是合适替代品的某些情况下仍然适用。

当涉及到库支持时,ROC 曲线具有明显的优势。尽管在任何主要的机器学习或统计库中都有现成的用于 ROC 分析的库函数,但由于 TOC 出现的时间相对较短,对它的支持仍然相当有限。有一个由最初 TOC 出版物的作者策划的 TOC R 包。然而,如果你愿意多做一点,一个根据概率绘制 TOC 图的函数可以很快用你最喜欢的编程语言实现。

最后,我们必须考虑到,图表最终是一种交流的手段。在统计学和机器学习社区,ROC 分析是一个众所周知的标准,ROC 图是直接理解的。相比之下,TOC 分析仍然相对年轻,可能仍然会困扰你的一部分听众。如果你在一个重要的会议上有 10 分钟的时间,你不会想花 5 分钟来解释一个不寻常的图表类型(TOC),特别是当有一个可供选择的(ROC ),你的听众会马上得到。

尽管 TOC 分析还远未被广泛采用,但我希望这篇文章能让你相信,在分析分类器的性能时,这是一个值得尝试的工具。

参考

[1] W. Peterson、T. Birdsall 和 W. Fox,信号可检测性理论(1954),信息理论 IRE 专业组汇刊, 4 (4),第 171–212 页。

[2] R. G. Pontius Jr 和 K. Si,测量多阈值诊断能力的总操作特性 (2014),国际地理信息科学杂志 28 (3),第 570-583 页

[3] T. Fawcett,ROC 分析介绍 (2006),模式识别字母 27 ,861–874 页

[4] 成人数据集 (1996),通过 Dua,d .和 Graff,C. (2019), UCI 机器学习知识库提供

建造一个人工智能机器人来玩石头、剪子、布

原文:https://towardsdatascience.com/rock-paper-scissors-ai-bot-janken-ee2d3089b778?source=collection_archive---------43-----------------------

了解如何做同样的事情

AI Bot 名称:Janken
使用的技术 : TensorFlow、Keras、Numpy、SqueezeNet &打开 CV

都是通过 python 脚本执行的。通过键盘敲击进行游戏设置。

TL;博士

如果你想开始运行,你可以直接到我的 GitHub 和克隆。
https://github.com/DarrenBro/rock-paper-scissors-ai
自述文件上有详细的说明你可以遵循。

在 Github 项目中,我包含了模型示例、项目的依赖项、SqueezeNet 模型(稍后会提到)和测试图像。

即使你以前从未使用过 ML 或 python,我也尽量使入门变得简单。

本文的其余部分将集中在成就 Janken 的 4 个步骤上;

本地网络摄像头——Janken 并不总是做对

  1. 收集数据
    (什么看起来合适)
  2. 挤压网络和训练神经网络
  3. 测试模型
  4. 玩游戏!

收集数据——什么是合适的

我们希望收集 4 种类型的图像(石头、布、剪刀和背景/噪声),并将它们映射为我们的输入标签。我们只需要将它们编入索引。

在机器学习中,当具有正确答案的数据集可用时,它可以被视为一种超级监督学习的形式。这种形式的训练是“图像分类”,我们知道正确的标签,Janken 属于监督图像分类

因此,我们希望每个类别都有数百张图片,这需要我们做一些工作,但我们通过一个简单的开放 CV 脚本在几秒钟内收集所有这些图片,减少了繁重的工作。

这在 Github 自述文件中有解释

需要注意的事项。

保持所有输入图形一致 如果数据不一致,你很容易陷入如下错误。形状。

ValueError:检查时出错:预期 squeezenet_input 具有 shape (None,300,300,3),但得到的数组具有 shape (1,227,227,3)。

所以有了我上面的图,捕捉然后存储你的数据,你可以在几秒钟内自动收集 100 张图像。

本地网络摄像头——尝试获得尽可能多的不同角度

移除偏置

本地网络摄像头——我戴着手套的手扩大了数据范围

ML 中的一个巨大领域是拥有一个具有良好通用性的模型。

数据的多样性足以用来训练吗?100 多张我自己的手的图像可能很难识别除了我以外的任何东西。

我的临时解决方案是用一只戴着手套的手来鼓励 CNN 关注手势的特征。

然后我扩展了这一点,伸出手从量子单元收集图像,并训练进一步降低我的偏见。

一个改进的想法 —保持所有训练图像为黑白,并将游戏图像也转换为 BW 彩色。

最后需要注意的一点是:当我在抓图时用手调整角度,一些照片在运动中变得模糊了。重要的是通过移除来清除它们,因为它们只会混淆模型来识别静态图像,就像在游戏中一样。

噪音污染 /灯光
噪音’的意思是背景或者除了手势以外的任何东西。关于这一点的一点是,我有麻烦让 Janken 认识到还没有播放,它会在预测之间跳跃。所以在训练中保持一个稳定的背景不是最好的选择,我可以在数据中混合更多的对比、阴影、海报、门,只是更多的自然房屋和办公室物品。

挤压网络和训练神经网络

这里有很多要说的,我在我的 GitHub 里给代码加了很多注释(上下链接),这里要重点关注的文件叫做“train_model.py”。

Keras

让我们的标签输入映射到索引值,因为这是神经网络识别它们的方式。

INPUT_LABELS = {
    "rock": 0,
    "paper": 1,
    "scissors": 2,
    "noise": 3
}

在那之后,我决定走“顺序模式”。

一种允许简单的 1-1 映射的设计,在 ML 术语中,它允许层接受 1 个输入张量并创建 1 个输出张量。例如,顺序允许神经网络(NN)将纸张图像识别为值 1

model = Sequential([
    SqueezeNet(input_shape=(300, 300, 3), include_top=False),
    Dropout(0.2),
    Convolution2D(LABELS_COUNT, (1, 1), padding='valid'),
    Activation('relu'),
    GlobalAveragePooling2D(),
    Activation('softmax')

以上是我们的模型定义,Janken 的核心,这是我们需要做的所有架构设计,大部分工作是在我们编译和拟合模型之前收集、清理和整形数据。

我很快会谈到“挤压网”。下面的所有东西都被认为是一个层(keras.layers),它们都很重要,所以让我们花一分钟来解释每一个。

退出添加以减少过拟合,0.2 = 20%,因此在整个训练过程中,退出结果的 1/5,以保持模型学习新方法,而不变得陈旧。这种高达 50%的情况并不少见。

Convolution2D 通过第一个参数 LABELS_COUNT 控制图层的大小,总共 4 个,(3 个手势+噪波)标签。它被附加到已经定义的神经网络‘SqueezeNet’上。

激活(ReLU) 整流线性单元将负值变为 0 并输出正值。
为什么是?激活函数负责获取输入并为输出节点分配权重,模型不能很好地与负值协调,它在我们期望模型如何执行的意义上变得不一致。

𝑓(𝑥) = max{0,𝑥} = > relu 单元的输出为非负。返回 x,如果小于零,则 max 返回 0。

ReLU 在正端产生一条直线,负端是一个平坦的零点

ReLU 已经成为许多类型的神经网络的默认激活函数,因为使用 ReLU 的模型更容易训练,并且通常实现更好的性能和良好的模型收敛性。

这并不是所有的结束,它可能会导致一个问题,称为“死亡 ReLU”。如果你返回太多的负数,你不会希望它们都变成 0,而是以负的线性方式返回。如果您有兴趣了解更多信息,请搜索“ Leaky ReLU ”。

globaveragepool2d进行分类,计算上一层各特征地图的平均输出。
即数据简化层,为激活(“softmax”)准备模型。

激活(softmax) 给出每个手势的概率。
我们有一个 4 图像类(问题),‘soft max’处理多类,任何超过 2 的。

挤压网

这是一个用于图像分类的预建神经网络,这意味着我们可以专注于它的扩展,以实现我们构建 Janken 的目的,这本身就足够了,而不是从头开始创建神经网络。光是训练时间就可能是几天。

你用 SqueezeNet 得到的奖金;

  1. 较小的卷积神经网络(CNN)在分布式训练中需要较少的跨服务器通信
  2. 导出新型号所需的带宽更少。
  3. 更小的 CNN 更适合部署并且使用更少的内存。

重新访问培训脚本中的代码行。

SqueezeNet(input_shape=(300, 300, 3), include_top=False)

input_shape 是一张尺寸为 300 x 300 像素的图片。3 代表 RGB 颜色范围。

include_top 让您选择是否需要最终的密集层。

密集层能够解释发现的模式,以便对图像进行分类。这张图片包含了岩石。

设置为因为我们已经标记了岩石数据的样子。

卷积层作为特征提取器工作。他们识别图像中的一系列图案,每一层都可以通过看到图案的图案来识别更精细的图案。

重量说明:

  • 卷积层中的权重是固定大小的。卷积层不关心输入图像的大小。它只是进行训练,并根据输入图像的相同大小呈现结果图像。
  • 密集图层中的权重完全取决于输入大小。它是每个输入元素的一个权重。因此,这要求你的输入总是相同的大小,否则你不会有适当的学习权重。

因此,将最终密集图层设置为 false 可让您定义输入大小(300 x 300)。(并且输出大小会相应增加/减少)。

测试模型

在脚本“ test_model ”中,您可以在您已经处理过的图像或模型从未见过的新图像上看到模型预测。

有时这是正确的

有时候你很幸运

其余 90%的时间你只是叹息

该脚本处理您想要提供的任何新图像,因为它将使用 open CV 重新整形为 300x300。

玩游戏!

对 Janken 游戏结果的预测
我想象 Janken 将做出的预测会“闪烁”很多,因为移动的摄像机图像将总是提供不同的输入来分析和运行模型。

照明将发挥很大的作用,所以我试图分割我的数据集,并在一天的不同时间收集图像。

静态背景或控件冻结图像将有助于做出更稳定的手势预测。

结果如何?

Janken 并没有考虑到锁定,所以一个“优雅”的解决方案是通过 mac 的网络摄像头将其他人播放到显示器上与另一个玩家共享的屏幕上。

我知道你印象深刻

然而,Janken 只能不断地击败我,它能够通过显示器从其他玩家那里赢得 50%的手势,但我怀疑相机图像和处理的性质使 Janken 很难可能做出所有的手势。

为了改进模型,我应该通过我的网络摄像头收集来自用户的图像,给 Janken 更多的概括。

总的来说,我真的很喜欢我学到的一切,这个项目中没有一项技术是我熟悉的。把学习变成游戏是很棒的。

最后,我添加了一些用户控件,希望能增加三局两胜的游戏体验。
主要说明位于灰色标题上。

安静点。

截图的时候总是忘记微笑:)

资源

全代码 GitHub 链接

[## 达伦布罗/石头剪子布人工智能

人工智能机器人和人类玩石头剪刀布

github.com](https://github.com/DarrenBro/rock-paper-scissors-ai/blob/master/collect_image_data.py)

Keras API

[## Keras 文档:Keras API 参考

Keras 文档

:Keras API 参考 Keras 文档 keras.io](https://keras.io/api/)

SqueezeNet 信息

[## 回顾:挤压网(图像分类)

AlexNet 级精度,参数减少 50 倍

towardsdatascience.com](/review-squeezenet-image-classification-e7414825581a) [## SqueezeNet: AlexNet 级别的精度,参数减少了 50 倍,并且<0.5MB model size

Recent research on deep neural networks has focused primarily on improving accuracy. For a given accuracy level, it is…

arxiv.org](https://arxiv.org/abs/1602.07360)

O'Reilly (TensorFlow 和 Keras)

[## 使用 TensorFlow 2 和 Keras 进行深度学习-第二版

使用最新发布的 TensorFlow 2 和 Keras 构建机器和深度学习系统,用于实验室、生产和…

www.oreilly.com](https://www.oreilly.com/library/view/deep-learning-with/9781838823412/)

使用 PyTorch 的 Ax 包进行摇摆超参数调谐

原文:https://towardsdatascience.com/rocking-hyperparameter-tuning-with-pytorchs-ax-package-1c2dd79f2948?source=collection_archive---------13-----------------------

使用贝叶斯和土匪优化较短的调谐,同时享受难以置信的简单设置。它非常有效。

雅罗斯瓦夫·米奥

对于许多机器学习任务,超参数调整是必须的。我们通常努力为我们的问题选择正确的算法和架构,然后严格训练以获得一个好的模型。在这两次之后进行超参数调整(HPT)可能看起来没有必要,但事实上,这是至关重要的。HPT 应该定期进行,并可能帮助我们以较小的努力实现性能的巨大改进。

我在这里建议的是用新发布的 python 包实现 HPT 的一种简单方法。它将完美地工作,并且花费不超过半个小时来设置你在你自己的计算机上训练的任何东西。对于其他模型,尤其是那些需要部署培训或在生产中运行的模型,这将通过一些小的调整来实现,我们将在本文的 B 部分进一步讨论。

Ax 是什么?

Ax 是 PyTorch 的一个开源包,它可以帮助您在您定义的参数范围内找到任何函数的最小值。机器学习中最常见的用例是找到训练模型的最佳超参数,这些参数将使您的总体损失最小化。该软件包通过运行多轮训练来实现这一点,每轮训练使用一组不同的参数,并返回损失最小的参数。诀窍在于,它不需要对这些参数进行网格搜索或随机搜索,而是使用更复杂的算法,因此节省了大量训练和运行时间。

Ax 可以找到连续参数(比如学习率)和离散参数(比如隐藏层的大小)的最小值。它对前者使用贝叶斯优化,对后者使用 bandit 优化。即使这个包来自 pytorch,它也可以用于任何函数,只要它返回一个您想要最小化的值。

在我们开始之前,安装说明可以在这里找到。

第一部分——“你好,世界!”

Ax 有几种操作模式,但我们将从最基本的一种开始,用一个小例子来说明。如前所述,我们通常会使用 Ax 来帮助我们找到最佳的超参数,但在其核心,这个包可以帮助我们找到一个函数关于某些参数的最小值。这就是为什么对于这个例子,我们将运行 Ax 来寻找一个复杂的二次函数的最小值。为此,我们将定义一个名为 booth 的函数,该函数在字典 p 中接收其参数 {x1,x2} :

# Sample Quadratic Function
def booth(p): 
 # p = dictionary of parameters 
 return (p[“x1”] + 2*p[“x2”] — 7)**2 + (2*p[“x1”] + p[“x2”] — 5)**2print(booth({“x1”:6, “x2”: 7}))

这将返回:365。

为了找到“booth”的最小值,我们将让 ax 运行该函数几次。它为每次运行选择的参数取决于以前的运行——它运行的参数以及对这些参数的函数的结果是什么(在机器学习中,“结果”==“损失”)。运行下一个代码位执行 20 次连续的函数运行,具有 20 组不同的参数 {x1,x2} 。然后打印出【最佳参数】。作为比较,如果我们想用网格搜索来运行这个,对于 x1x2,的跳跃为 0.5,我们将需要 1600 次运行,而不是 20 次!

from ax import optimize
best_parameters, best_values, _, _ = optimize(
 parameters=[
 {“name”: “x1”,
  “type”: “range”,
  “bounds”: [-10.0, 10.0],},
 {“name”: “x2”,
  “type”: “range”,
  “bounds”: [-10.0, 10.0],},],
 evaluation_function=booth,
 minimize=True,)print(best_parameters)

这会打印出来(真实最小值是(1,3)):

{'x1': 1.0358775112792173, 'x2': 2.8776698220783423}

相当接近!在笔记本电脑上,这可能不到 10 秒钟。您可能会注意到,返回的“最佳参数”并不完全是真正的最小值,但是它们使函数非常接近实际的最小值。您对结果中的边距有多宽容是一个您稍后可以使用的参数。

这里我们需要做的唯一一件事是确保我们有一个以字典作为输入返回单个值的函数。这是运行 ax 时大多数情况下需要的。

B 部分—使用部署运行

A 部分非常简单,可以在你的电脑上运行的任何东西上很好地工作,但是它有几个缺点:

  1. 它一次只运行一个“训练”,如果一个训练会话超过几分钟就不好了。
  2. 它只在本地运行,如果您的常规模型运行在云/部署等上,这是很糟糕的。

为此,我们需要一些更复杂的东西:最好是某种神奇的方法,只需要几组参数就可以运行,这样我们就可以部署培训,然后耐心等待,直到他们得到结果。当第一批训练完成时,我们可以让 ax 知道损失是什么,获得下一组参数并开始下一批。用 ax 的行话来说,这种运行 ax 的方式就是所谓的“ax 服务”。短语和运行都很简单,几乎和我们在 a 部分看到的一样简单。

为了适应“ax 范式”,我们仍然需要某种运行训练并返回损失的函数,可能类似于下面这样:

def training_wrapper(parameters_dictionary): 1\. run training with parameters_dictionary (this writes the loss value to somewhere in the cloud)2\. read loss from somewhere in the cloud3\. return loss

在定义了适当的包装器之后,我们可以运行 ax 服务 HPT 代码。因为我希望您能够在您的计算机上运行这个演示代码,所以我将坚持使用我们在 A 部分中介绍的二次函数——“booth”。 {x1,x2} 的参数范围将保持不变。对于运行部署,我可以用我的“training_wrapper”版本替换下一个示例代码中的 booth。示例代码如下所示:

from ax.service.ax_client import AxClientax = AxClient(enforce_sequential_optimization=False)ax.create_experiment(
 name=”booth_experiment”,
 parameters=[
 {“name”: “x1”,
 “type”: “range”,
 “bounds”: [-10.0, 10.0],},
 {“name”: “x2”,
 “type”: “range”,
 “bounds”: [-10.0, 10.0],},],
 objective_name=”booth”,
 minimize=True,
)for _ in range(15):
 next_parameters, trial_index = ax.get_next_trial()
 ax.complete_trial(trial_index=trial_index, raw_data=booth(next_parameters))best_parameters, metrics = ax.get_best_parameters()

这与 a 部分有一点不同,我们不再仅仅使用“优化”功能。相反:

  1. 我们初始化一个 ax 客户端。
  2. 我们建立了一个“实验”,并选择了一系列我们想要检查的超参数。
  3. 我们用 get_next_trial()得到下一组我们想要运行函数的参数。
  4. 等待函数完成,用 complete_trial() 运行

也就是说,我们把获取下次运行的参数和实际运行分开了。如果您想并发运行,您可以一次获得 N 个 get_next_trial(),并异步运行它们。如果您想这样做,请确保不要忘记将“enforce _ sequential _ optimization”标志设置为 false。如果您想知道您可以同时运行多少次,您可以使用get _ recommended _ max _ parallelism(在这里阅读这个函数的输出)。

差不多就是这样。这个包是非常可定制的,并且可以处理您想要为您的特定环境所做的任何改变。文档可读性很强,尤其是教程。它还有各种各样的可视化效果来帮助你弄清楚发生了什么。

摘要

您必须手动设置多次运行或使用网格搜索的日子已经一去不复返了。的确,在一个非常大的超参数集上,ax 可能导致类似随机搜索的结果,但是即使它没有减少您最终运行的训练数量,它仍然为您节省了编码和多次运行的处理。设置非常简单——我强烈建议您亲自尝试一下!

链接:

  1. 通用文档:https://ax.dev/docs/why-ax.html
  2. 教程:https://ax.dev/tutorials/
  3. 可视化:https://ax.dev/tutorials/visualizations.html

权力 BI 中的角色扮演维度

原文:https://towardsdatascience.com/role-playing-dimensions-in-power-bi-185dc58f90f1?source=collection_archive---------17-----------------------

检查这个简单的建模技术,以避免数据模型冗余

在解释了如何通过简单地遵守一些简单的规则来将 Power BI 数据模型的大小减少 90% ,并理解了如何通过 VertiPaq 引擎在后台优化您的数据模型之后,现在是学习维度建模的核心概念之一的最佳时机。

Marko Blazevic 在 Pexels 上拍摄的照片

角色扮演维度并不是 Power BI 独有的概念。这是一种通用的数据建模技术,来自金博尔的方法论

简而言之,这就是当您使用一个相同的维度为您的事实表创建多个关系时的情况。角色扮演维度概念的典型用法是用表示日期维度,因为在许多情况下,您的事实表将包含多个日期字段。例如,在博彩行业中,有 DateBetPlaced 和 DateBetProcessed 字段,它们不需要相同(在大多数情况下也不需要相同)。因此,假设业务请求是分析 DateBetPlaced 和 DateBetProcessed 上的数据。

一维多重引用的解决方案

第一种解决方案是创建两个完全相同的日期维度的副本,并在第一种情况下将 DateKey 关联到 DateBetPlaced,在第二种情况下关联到 DateBetProcessed。大概是这样的:

如您所见,事实表中的每个日期字段都与其自己的日期维度相关。我们说这些引用中的每一个都在模型中“扮演”它的角色。

当然,这种模型并不是最佳的,因为我们基本上是在没有有效理由的情况下使数据冗余。此外,为了获得有效的结果,我们需要为日期维度的每个引用使用单独的过滤器。

优化模型

我们可以将一个维度多次关联到一个事实表,而不是保留同一个维度的多个引用。这个概念在不同的工具中有不同的表现(例如,在 SSAS 多维数据库中,如果数据源视图有适当的外键,您可以在维度和事实表之间定义多个活动关系。但是,这超出了本文的范围),所以我将重点讨论 Power BI。

因此,我将从我的数据模型中删除冗余的 Date 维度,并将 Date 维度中的 DateKey 连接到事实表中的 DateBetProcessed 字段。

这里发生了什么?Power BI 创建了一个关系,但是正如你注意到的,这个关系是用虚线标记的。这是因为 Power BI 只允许两个表之间有一个活动关系,在我的例子中,是在 DateKey 和 DateBetPlaced 之间。所以,当我把它放在报告画布上时,我会得到这样的结果:

我可以看到每月下注的总数,但是由于我的活动关系是在 DateKey 和 DateBetPlaced 之间,所以我看到的总数是基于下注日期的!

如果我想看,而不是下了多少注,每月有多少注被处理呢?这里涉及到 DAX 功能 用户关系 。此功能使我们能够定义对于特定计算哪个关系应该是活动的。

所以,当我写出下面的度量时:

Bets Processed = CALCULATE(
                    COUNT('Fact Bets'[BetID]),
                    USERELATIONSHIP(DimDate[DateKey],'Fact Bets'[DateBetProcessed])
)

我在明确的对 Power BI 说:在这里,我不希望你使用默认的主动关系(DateKey — DateBetPlaced)。相反,我希望您切换到使用另一个关系(DateKey — DateBetProcessed ),并使该关系仅对该计算有效!

结果如下:

您可能会注意到,根据计算中使用的关系,线条是不同的。

结论

使用这种技术,我们使我们的用户能够从不同的角度分割数据,并给予他们基于多种场景分析数据的灵活性,从而保持我们的数据模型整洁而不冗余。

成为会员,阅读媒体上的每一个故事!

订阅这里获取更多有见地的数据文章!

追踪石油泄漏

原文:https://towardsdatascience.com/rolling-in-the-deep-589f3460960f?source=collection_archive---------20-----------------------

我从漏油到应用人工智能预测设备故障的旅程

一)动机

在求职过程中,我发现我所在地区的许多公司都在寻找具有石油和天然气知识的数据科学家。我的背景大部分是关于数学的,所以我决定沿着石油管道去冒险。

我注意到的一件事是,石油和天然气是一个庞大的行业,基础设施陈旧。2014 年,Inside Energy 报告称,美国 45%的原油管道已经超过 50 年。有些管道甚至在 20 世纪 20 年代以前就铺设好了,而至今仍在运行。这种古老、老化的基础设施可能会导致灾难,如漏油,从而影响公司收入、环境和周围地区的人们。

那么,所有这些石油泄漏的罪魁祸首是什么呢?让我们潜入油中!

二)2010-2017 年石油管道事故

数据包括从 2010 年到 2017 年向管道和危险材料安全管理局报告的每个石油管道泄漏或溢出的记录。在 7 年多的时间里,发生了 2795 次泄漏。有 46 个特征描述每个事件。其中值得注意的有:事故日期和时间、操作者姓名、事故原因、危险液体类型、损失数量、伤亡人数以及相关成本

数据丰富,描述性强。我选择了一些有趣的特性,并从这里开始

1)事件是如何按年、月和一天中的小时分布的?

每年的病例数。资料来源:Huy Bui

观察 2017 年有未完成的数据。最高峰是 2015 年的 462 例

按日期的小时数列出的案例数

这里没有调整时区,但是我可以有把握地假设大多数情况发生在营业时间。

每月病例数。资料来源:Huy Bui

有趣的是,12 月份只有几起案件(212 起)。但在 1 月份,它急剧增加(275)。我想知道,由于运营从旧的一年过渡到新的一年,这里是否有一种趋势,或者,这可能只是一月份发生的一系列随机泄漏?

2)这些事故发生在哪里?

2010-2017 年所有事故的位置。资料来源:Huy Bui

根据我由plotly创建的地理图,在我看来泄漏发生在管道沿线。德克萨斯州是所有州中事故最多的州(1004 起)。这并不奇怪,因为德克萨斯州是美国最大的国内石油生产商。我以为德州会损失最多的钱,但事实并非如此…

3)每个状态的净损失是多少?

各州净损失(2010–2017)。资料来源:Huy Bui

让我们来看看赔钱最多的三个州:

第三名 得克萨斯发生 1004 起事故,损失 135580 桶,净亏损 18475 万桶

第二名加州,事故 153 起,损失 3390 桶,净亏损 192 桶。密耳

冠军是…密歇根州 29 起事故,5355 桶损失,834 百万桶

我的假设是完全错误的,事故数量与净损失并不相关。此外,桶损失的数字没有考虑到钱去了哪里。

拼图中缺失的部分是其他特征,我天真地认为它们不重要。我应该考虑的其他特征是:

  • 管道关闭
  • 财产损失成本
  • 商品成本损失
  • 应急响应成本
  • 环境补救费用

4)那么……密歇根到底发生了什么?

密歇根净亏损时间序列(百万美元)。资料来源:Huy Bui

你看到的 2010 年的高峰占了密歇根州总净亏损的 95%,占了 7 年间美国总净亏损的 36%!稍加研究表明,这一事件就是 7 月 25 日的卡拉马祖河漏油事件。当安桥(6B 线)运营的一条管道爆裂并流入塔尔马奇溪时,长达 18 个小时无人察觉。工作人员误解了异常压力数据,认为这是由管道中的气泡引起的。因此,他们重新启动了输油管两次,使得漏油速度大大加快。

密歇根十大事件列表将让你更好地了解卡拉马祖河漏油事件对该州造成了多大的损害。

密歇根十大事故(按净损失排名)。资料来源:Huy Bui

5)漏油的罪魁祸首是什么?

设备故障是这个问题的简答。当涉及到解释具有许多参数的数据时,人的能力是非常有限的。手动读取数据会导致忽略异常情况,并对业务造成严重损害。事实上,材料/焊接/设备故障占所有原因损失的 53%。

原因类别和不同特征之间的关联。资料来源:Huy Bui

幸运的是,随着机器学习的增长,许多大型石油公司已经开始将人工智能应用到他们的基础设施中,以帮助防止设备故障,并使他们的业务受益。

III)预测性设备故障

回到 2019 年 10 月,我参加了德克萨斯 A&M 大学主办的数据马拉松。挑战之一是使用由康菲石油公司赞助的传感器 数据 预测井下设备故障。我没有做这个挑战,但是我对这个问题很感兴趣,并且仍然记得它。之前的分析给了我足够的动力,让我回头再看一遍这个数据集。

传感器数据的样本。资料来源:Huy Bui

训练集由 60000 个观察值和 170 个传感器值组成。这些传感器分为两种类型:

  • measure:传感器的单次测量。
  • histogram bin:一组 10 个柱,它们是传感器的不同柱,显示它们随时间的分布。

工作是对观察结果是故障还是非故障进行分类。下面的直方图显示了列车组分布:

列车组中“故障”和“未故障”的分布。资料来源:Huy Bui

数据包含许多空值。我必须创建一个函数,接受不同的阈值,逐个处理丢失的值,并超调这个函数以获得最佳结果。我用xgboost来做预测,用f1-score作为度量。

我将我的模型应用于测试集上的 16001 个不同的观察值。提交预测后,我在第一次尝试中获得了 0.99383。

四)结论

如果石油工业投资研究人工智能,他们会更快地发现有缺陷的设备,更有效地防止灾难,并节省大量资金。具体来说,Kalamazoo 河漏油事件本来可以用机器学习来避免。

我希望你喜欢阅读我的文章,并跟随我踏上这一旅程。感谢任何反馈。如果你想看看数据,玩玩plotly交互图,判断一下我的技术知识,这里是链接

是什么让一首歌伟大?第一部分

原文:https://towardsdatascience.com/rollingstone-for-medium-f1f638b9568e?source=collection_archive---------49-----------------------

什么让一首歌变得伟大

使用 Selenium 和 BeautifulSoup 在 Python 中动态抓取生成的内容

Natalie Cardona 在 Unsplash 上拍摄的照片

【这是 系列三篇文章中的第一篇

Web 抓取、可视化、列表理解、正则表达式、熊猫!这个项目拥有一切——包括泡菜!

【2020 年 9 月 1 日更新:自本文首次发表以来,《滚石》已将源代码改为其页面。因此,我已经更新了代码,允许检索动态生成的内容。】

一旦你学会了一些编码和基本的数据科学技能,标准的建议是去做项目,做很多项目。不幸的是,我们中的许多人很难找到这样的项目。

前几天我在翻滚石有史以来最伟大的 500 首歌 的榜单。我开始问自己:‘谁的歌在榜单上最多?或者:“会偏向于过去几十年,因为这些评论家可能还不到 20 多岁?”?。在我开始这个项目的第二天,我就一直在寻找一个能够让我将一些网络搜集技术与一些探索性数据分析( EDA )结合起来的项目。我从中获得了很多乐趣:我希望通过分享它,你也能学到一些东西并从中获得乐趣。

我将在这里展示的是许多库、工具和技能集:这是一个端到端的项目,从数据检索开始,到可视化结束,涉及解析、清理和分析数据。我们将涉及的一些内容:

  • 网页抓取(使用BeautifulSoupSelenium
  • 正则表达式(使用 Python 的re模块)
  • API(即 Spotify 的)(使用spotipy)
  • 数据分析和可视化(用pandasmatplotlib)。

我会认为这个项目适合高级初学者到中级程序员。它本身并不复杂:但它确实涉及许多不同的领域。注意,HTML 和 CSS 的一些非常基础的知识对于第一部分可能是有用的。

网络搜集:获取数据并清理数据

首先,让我们导入我们需要的库。

# webscraping libraries
import urllib # to retrieve web pages
from bs4 import BeautifulSoup # to parse web pages
from selenium import webdriver # to retrieve dynamically generated content
import time  # allows us to wait before scraping or interacting with web pages# data, cleaning, analysis and visualization
import pandas as pd # the goto python library for data cleaning and analysis
import matplotlib.pyplot as plt # the goto python library for data visualization
import seaborn as sns # data visualization (but nicer!)
import re # Python's library for regular expressions (see more below)# to interact with Spotify's API
import spotipy # to query Spotify's API
from spotipy.oauth2 import SpotifyClientCredentials # for API login # display charts in jupyter notebook
%matplotlib inline

第一步:用硒和美丽的汤刮动态生成的内容

打开浏览器,导航至我们的列表。首先,请注意,该列表是以 50 为一组进行“分批”的。这告诉我们,一旦我们开始抓取,我们可能需要迭代不同的地址来获取我们的数据——稍后将详细介绍。

向下滚动我们找到我们的歌曲。我们要检索五类数据:

  • 艺术家
  • 歌曲名称
  • 作者
  • 生产者
  • 发布日期

如果你ctrl-click(在 Mac 上)点击页面的一个元素,并从弹出的菜单中选择检查,你会看到相应的 HTML 高亮显示。在这种情况下,要获得艺术家和歌曲的标题,我们需要查找标签为<h2>的元素,该元素属于类c-gallery_vertical-album__title

检查 web 元素

通常,我们会使用urllib检索页面,并使用参数html.parser将结果传递给beautiful soup:BeatifulSoup 会解析我们检索到的 HTML,并允许我们找到使用find_all方法识别的元素。
然而,事实证明页面是动态生成的(如果你查看源代码,你不会发现任何这些元素)。因此,首先我们将使用[Selenium](https://selenium-python.readthedocs.io)来模拟浏览器打开页面,然后才检索内容。

def get_page_source(url):
    """
    Input: a str (the target url)
    Returns: a Beautiful Soup object (the parsed page source)
    -----------------------------------------------------------------------
    Retrieves the target page's contents and passes them to Beautiful Soup.
    -----------------------------------------------------------------------
    """
    options = webdriver.ChromeOptions()
    options.add_argument('--ignore-certificate-errors')
    options.add_argument('--incognito')
    options.add_argument('--headless')
    driver = webdriver.Chrome(options=options)
    time.sleep(10) # sleep for 10s to allow the page to load
    target_page = url
    driver.get(target_page) 
    page_source = driver.page_source #get the contents
    soup =  BeautifulSoup(page_source)
    driver.quit() # close the driver
    return souptarget_url ="https://www.rollingstone.com/music/music-lists/500-greatest-songs-of-all-time-151127/smokey-robinson-and-the-miracles-shop-around-71184/"
soup = get_page_source(target_url) # pass the HTML contents to Beautiful Soup for parsing.

现在使用 Beautiful Soup 的find_all方法,并传递适当的 CSS 标识符,我们就可以检索到我们定位的所有项目。

song_all = soup.find_all('h2', {'class':'c-gallery-vertical-album__title'})
song_all[0]<h2 class="c-gallery-vertical-album__title">Smokey Robinson and the Miracles, 'Shop Around'</h2>

好吧,我们有进展了。让我们进一步挖掘。

第二步:用正则表达式清理

列表项不仅包含我们正在寻找的数据,还包含 HTML 标签。为了只获取数据,我们使用get_text()如下:

song_all[0].get_text()"Smokey Robinson and the Miracles, 'Shop Around'"

可恶。很多格式,空白,额外的引号。我选择通过re模块使用正则表达式Regex 来清理数据。

RegEx 是一种功能强大的微编程语言,对于搜索字符串中的字符模式非常有价值。它有一个相当陡峭的学习曲线,所以我尽量经常使用它:正如他们所说:练习,练习,练习!(点击下面的正则表达式介绍)。

** [## 正则表达式简介

使用 Python 逐步介绍正则表达式

medium.com](https://medium.com/better-programming/introduction-to-regex-8c18abdd4f70)**

def strip_extra_chars(line):
    """
    Strips non-alphanumerical, whitespace and newline characters away from string
    """
    line = line.get_text()
    line = re.sub("\A\S[^a-zA-z0-9]", "", line) # remove any non-whitespace non-alphanumeric character from the beginning of the line
    line = re.sub("[’‘]", "", line).strip() # get rid of extra quotes and remove whitespace with .strip()
    line = re.sub("\n", "", line) # get rid of newlines
    return line.strip()

函数strip_extra_chars将从我们的数据中提取一行,去掉所有的垃圾,包括那些讨厌的引号。更多细节见函数中的注释,试试这个是测试和学习 RegEx 的好资源。

步骤 3:遍历列表

我们差不多完成了。还记得一开始我们注意到我们的网页只包含 50 首歌曲,因此我们需要迭代内容吗?如果我们通过顶部导航菜单上的ctrl-clicking再次检查页面,我们会发现它指向我们需要的 URL。这里我们定义了一个get_links函数,它将 URL 存储在一个列表中。

然后,我们可以轻松地遍历列表并检索所有数据。

def get_links(url):
    options = webdriver.ChromeOptions()
    options.add_argument('--ignore-certificate-errors')
    options.add_argument('--incognito')
    options.add_argument('--headless')
    options.add_argument("--start-maximized");
    driver = webdriver.Chrome(options=options)
    # note that in order to get all the urls we need to set the browser's window to max size
    driver.set_window_size(1024, 768) 
    time.sleep(10) # sleep for 10s to allow the page to load
    target_page = url
    driver.get(target_page) 
    header = driver.find_element_by_id('pmc-gallery-list-nav-bar-render')
    links = header.find_elements_by_tag_name('a')
    urls = [link.get_attribute("href") for link in links]
    driver.quit()
    return urlsurls = get_links(target_url)

现在我们有了 URL,下一步是将数据存储在列表中。对于每个 URL,我们将启动我们的get_page_source功能,提取相关数据,并存储它。我们将每个页面的数据存储到两个列表中:song_all,其中包含艺术家和歌曲的标题,以及other_all,其中包含关于作者、制作人和发行日期的数据。通过对它们进行迭代,我们提取相关的文本,通过调用我们的strip_extra_chars函数去掉多余的字符和格式,并将结果附加到我们之前初始化的三个空列表中:一个用于艺术家,一个用于标题,一个用于其他信息(接下来我们将对其进行解析)。

songs = []
artists = []
other_data = []for url in urls:
    print(f"retrieving data from {url}")
    soup = get_page_source(url)
    song_all = soup.find_all('h2', {'class':'c-gallery-vertical-album__title'})
    other_all = soup.find_all(attrs={'class': 'c-gallery-vertical-album__description'})
    for song in song_all:
        song = strip_extra_chars(song)
        title = song.split(',')[1]
        title_inner = title[2:len(title)-1]
        songs.append(title_inner)
        artists.append(song.split(',')[0])
    for other in other_all:
        other = strip_extra_chars(other)
        other_data.append(other)
driver.quit()

借助正则表达式和一点字符串切片将清理和分割包含在other中的数据。我们在一个split_others函数中这样做,然后我们在一个循环中调用这个函数来产生三个作者、制作人和发布日期的列表。

def split_others(line):
    x = "(Writers:|Writer:)"
    y = "(Producer:|Producers:)"
    z = "Released:"
    a = re.split(x, line)
    a = re.split(y, a[2])
    writers = a[0].strip()
    b = re.split(z, a[2])
    producers = b[0]
    released = b[1].split(',')[0]
    released
    return writers, producers, releasedwriters = []
producer = []
release_date = []
for item in other_data:
    w, p, r = split_others(item)
    writers.append(w.strip())
    producer.append(p.strip())
    release_date.append(r[-2:].strip())

第四步:把所有的东西放在一起!

就是这样!我们已经取回了我们的数据。我们现在可以使用字典并将我们的数据传递给熊猫,以便将其存储在数据帧中。

d = {'Artist':artists, 'Song title': songs, 'Writers': writers, 'Producer': producer, 'Year': release_date} 
df = pd.DataFrame(data=d)

(可选:因为抓取相当耗时,我将把我们检索到的数据存储在一个pickle中,这个是一个很好的教程。要将其加载回来,您需要用三重引号取消对该部分的注释。

 import pickle

filename = 'ROLLING_STONE_DATA'
outfile = open(filename,'wb')
pickle.dump(d,outfile)
outfile.close()
'''
filename = 'ROLLING_STONE_DATA'
infile = open(filename,'rb')
d = pickle.load(infile)
infile.close()
'''df = pd.DataFrame(data=d)

既然我们都是好奇的动物,那就稍微尝一尝,看看谁是唱歌最多的艺人,用matplotlib来剧情结果。

top_10 = df['Artist'].value_counts()[:10]plt.barh(top_10.index, top_10)<BarContainer object of 10 artists>

Romulus:理解并发性和弹性范围锁的案例研究

原文:https://towardsdatascience.com/romulus-a-case-study-for-understanding-concurrency-elastic-range-locks-9aa9f93bed80?source=collection_archive---------58-----------------------

超越一维串行程序,开发多核世界

下面的文章希望通过构建一个调度器(名为 Romulus)的案例研究,将读者带入一个并发性、弹性和现代计算趋势的旅程,该调度器可以自动将任何串行数据结构转换为高性能的并发键值存储。通过将分布式计算和数据库设计的概念引入本地内存,范围锁的分析和可扩展性方面的新范例将形成一系列简单而强大的治理原则,这些原则根据目标争用的功能对数据进行分区和保护。

来源:迈克尔·克里姆根 via Baeldung

介绍

正如 ASP los’17 的最佳论文所描述的:

并发数据结构被用在软件栈中的任何地方,从内核(例如,用于调度的优先级队列),到应用程序库(例如,尝试存储器分配),到应用程序(例如,用于索引的平衡树)。这些数据结构效率低下时,会降低性能。”Calciu et。铝

直到最近,由于单核机器的大规模改进,计算机的能力才得以复合。性能界限每两年翻一番,由于当时的应用程序还不够先进,无法满足现代高性能计算的要求,因此串行程序主宰了应用层。渐渐地,摩尔定律的经典形式消亡了。为了重振它的趋势,程序员开始将他们的工作负载分布在同步的资源上,并分配到专门的计算单元中(你好,GPU)。今天,计算不再由一维的串行程序来定义。相反,软件已经超越了本地内存寄存器的逻辑,进入了多个内核,在这些内核中,它们必须在可变的位置和时间下,在它们的并发事务中遵循某种形式的一致性和治理。

来源:卡尔·鲁普

简单的同步技术,比如粗粒度锁,提供了一种简单但不可扩展的方法来管理共享内存。另一方面,细粒度的异步技术对于普通开发人员来说可能过于复杂。开发这些内核对于构建任何高性能计算环境的基础至关重要。但是为什么不为所有的应用程序开发商用硬件的全部功能呢?希望这个案例研究可以为日常开发人员提供一个新的理解,从基本原则的角度来构建并发系统并分析其中的性能。对于更有经验的开发人员,该研究提出了一种更强大的范围锁方法,该方法利用基本的启发式算法,在任何不平衡的工作负载下主动地追逐和收敛到争用级别。如果你还在阅读,系好安全带准备长途旅行。

形式摘要

以下摘要将构成开发和表征 Romulus 的指南:

Romulus 在每个事务中注入一个调度器,以消除一系列弹性分区之间的争用,通过自适应读取-复制-更新(RCU)来同步这些库,并为所有工作负载提供稳定的执行状态。Romulus 的设计灵感来自 MapReduce:一个简单的框架,它并行地编排各种任务的处理,利用状态空间的全局视图来分布和管理跨资源的通信。Romulus 的主要目的是自动将任何串行 API 翻译成并发数据结构,同时达到手工制作的最先进算法的性能。然而,它最重要的贡献是一组竞争试探法,为系统提供了向目标竞争级别收敛的能力。更详细地说,Romulus 通过采用一种新颖的锁设计,根据实时线程冲突来拆分和合并分区,从而快速适应不平衡的工作负载。因此,这种方法可以扩展到其他应用程序中,在这些应用程序中,当不对称的工作负载超越其系统时,稀缺资源必须在线适应。此外,Romulus 通过提供一种在有序键值存储中进行有效范围查询的算法,将数据库设计中的概念引入本地内存。总的来说,Romulus 提出了一种调度算法,通过即插即用部署模型实现可预测的并行性。

有三个基本目标将推动该调度器架构中的决策:

(1)可扩展到任何有序键值存储的简单方法,也接近最先进的手工解决方案的性能(2)保证跨所有操作(包括范围查询)的可序列化性
(3)通过冲突试探法可预测的争用
(4)对不对称工作负载的强大容忍度

最终设计预览

箱子/球的理论原理

眼前的基本问题是一个调度问题:如何将输入连接到计算资源的公平分布(类似于 MapReduce ),该计算资源以预定义的争用级别为目标,并通过数百个线程进行扩展。摘要暗示 Romulus 将把数据分成一系列分区。因此,在进入 Romulus 的设计之前,对需要多少个分区来达到冲突率的第一原理分析可以(I)明确所追求的平衡/行为(ii)快速揭示性能预期。此外,它将提供一个(非常松散的)正式模型,通过将 Romulus 上的预期冲突映射到单个分区中的测量来证明争用的收敛性。正如后续章节所揭示的,当线程确定一系列离散测量违反了分区的预期阈值时,它们会将分区分成两半,以消除争用并适应不平衡的工作负载。因此,以下部分对我们的第三和第四个目标至关重要。

数学分析是通过对球中的经典箱问题的先前工作的垂直整合形成的:将 M 个球放入 N 个箱中的概率结果。这个模型以前已经在计算机科学中以多种形式应用于负载平衡。

箱柜/球数学模型

在最基本的模型中,M 个球通过一个随机函数——产生一个均匀分布——并被放入 N 个箱子中的一个。以下部分集中于理解三个重要现象:(I)观察不到碰撞的概率(ii)在单个箱中观察到大于 K 的高度的概率(iii)在所有 N 个箱中观察到大于 K 的高度的概率。这将有助于我们理解与线程相比,如何扩展分区的数量。

第一种情况:观察不到碰撞的概率

生日悖论再次上演

如果我们让 X 表示在所有箱中没有观察到碰撞的概率,并假设 M ≤ N,那么通常 M 个球必须放置在 N 个唯一的箱中。通过采用泰勒级数近似,并将其限制在一个可忽略的误差范围内(已知 u = 1/N 很小),我们可以这样估计 Pr[X]:

对于常数 Pr[X] = C,可以看出 C = O(m/n);也就是说,为了保持恒定的无冲突概率,箱的数量必须以 n = 0(m)的比率缩放

第二种情况:一个箱内的碰撞

设 Pr[Xi ≥ K]代表在单个 Xi 中至少 K 次碰撞的概率。这可以表示为:

对于常数 Pr[xi ≥ K] = C,如果 N ≥ M,则保持恒定期望高度所需的面元必须按比例缩放到 N = O(M)。无论输入的绝对值如何,对于 N = M,该预期高度保持相对恒定。

第三种情况:N 个箱的 K 次碰撞

设 Pr[X ≥k]表示在所有 N 个面元上至少发生 K 次碰撞的概率。假设 M= O(N ),并且每个面元彼此独立,则通过并集规则形成上界:

我们估计 M:N 的比值为 1。虽然在 M:N = 0.1 的未来讨论中可能不会出现这种情况,但上限仍将在这种情况下成立,并为进一步的数值分析提供足够的背景。应用这个比值,可以发现当 N(因此 M)一起向更大的值增加时,K 如何变化:

为什么 K 的预期值在单个容器中保持不变,但随着 N 和 M 一起缩放,一系列容器的预期值会增加?从逻辑的角度来看,随着我们增加出现差异的机会,我们增加了 K 在任何单个桶中远远超出其预期高度的机会。

结果

这些推导对罗慕路斯的三个期望很重要:

(I)为了保持不发生冲突的恒定概率,必须以 N = O(M)的比率相对于球的数量指数地缩放容器的数量。Romulus 不会遵循这种方法,因为这对于高线程数是不切实际的,因此,调度器不会试图消除所有冲突

(ii)如果将容器的数量与球的数量成线性比例,则每个容器中球的预期平均高度保持不变。但是,对于较大的 N 和 M 值,由于方差的性质,在热点中任何离散时间点的冲突可能更高。注意 Romulus 将遵循这个模型,因此,它将试图保持一个目标冲突率

(iii)当从单个仓构建整个系统的视图时,假设工作负载一致,可以创建关于系统状态的置信区间。更具体地,通过测量单个箱柜上的 K(即球队列的高度),可以确定其值是否持续违反阈值并超过概率。如果是这样的话,调度器应该预测一个偏斜的工作负载已经超越了系统并违反了均匀分布假设

毫无疑问,这是一个超级粗糙的模型(带有许多未言明的假设),但对于理解未来的总体趋势很有帮助。

数值分析

让我们将这个旧的视图转换成一个线程对库执行更新操作的新视图

下一节在线程在一系列分区上执行更新操作的理论环境中,证实了箱成球问题的理论趋势,并揭示了并发环境中两个明显的锁现象:(1)将锁分区减少到更细粒度的范围(即增加箱的数量)对于消除争用具有递减的回报(2)作为结果,给定无限资源的最佳理论配置是将锁的数量最大化到尽可能最小的分区中。然而,在许多情况下,系统只有少量的资源可以消耗。因此,利用范围锁来实现适当的并行性,而没有 40 字节 pthread_lock_t 的内存开销。然而,最重要的是,由于向更细粒度的范围减少锁分区具有递减的回报,Romulus 理论上可以利用更少数量级的锁,同时在许多场景中仍然接近手工制作的性能。

为了将经典的箱柜转化为球的问题建模,我们形成了值域锁的概念;存在一个 N 个仓的锁表——代表一系列 N 个抽象分区——它独立于底层存储层的实际大小。线程的数量代表了球的数量,它们通过即时插入/移除来跨这些分区操作。为了观察争用超时,我们对一个无限的迭代游戏建模如下:

1)在游戏开始时,线程被随机放入一个分区/箱(即锁)

2)每个分区都有一个等待完成的操作队列。对于游戏中的每一轮,一个线程充当系统中的并行代理。队列前面的所有线程都被清空,将自己回收到另一个分区中。他们通过遵循均匀分布的随机数生成来选择下一个分区。这表示一个线程获得一个分区的所有权,执行一个操作,释放该区域,然后继续下一个操作。所有不在队列前面(即在队列中其他线程后面)的线程不执行任何动作,而是等待,直到它们在未来的循环中移动到队列的前面。

3)游戏永远重复自己,类似于真实的系统

该模型有许多重要的假设,最明显的是:所有操作都是瞬时的,每轮完成一次,工作负载具有均匀分布,并且消除了内存访问、线程间通信和其他特定于体系结构的开销的可变性。当然,真实的系统有资源限制,比如内存和 I/O,它们会改变价值等式。然而,正如未来的测试床所揭示的,总体趋势保持不变,并为分析奠定了基础。实验在 Python 中以 10K、100K 和 1M 回合进行。无论游戏时间长短,结果都趋向于相同的值

左边的两个图描绘了在两种极端设置下游戏每一轮的队列高度,希望提供高争用与低争用环境中冲突变化的定性说明。第一张图片展示了一个有 64 个线程和 48 个分区的游戏。第二个图像表示一个游戏,有 64 个线程和 640 个分区。

在第一个游戏中,我们可以看到当分区数量太少时可能发生的灾难性影响。在第二场比赛中,虽然存在许多争夺点,但绝大多数操作都没有冲突。下面的实验将计划第二种情况,Romulus 试图模仿这种情况以获得更好的并行性/性能。

在该图中,我们将活动队列的平均高度定为 1.05,并绘制了在不同线程速率下达到该高度所需的分区数量。这是通过以恒定的 100K 回合玩一些游戏并发现产生适当平均高度的最接近的分区数目来计算的。本实验的目的是了解如何在线程间保持目标争用级别;从结果和先前的分析可以确认,对于 M / N <1,必须将分区的数量缩放为线程数量的线性函数。

在该图中,我们重申了这一点,并假设分区数量为 10 *个线程,将平均高度表示为线程数量的函数。包含了锁的最佳数量(底层结构的大小,表示为 1M ),以展示两种方法的一致差异

最后,这最后一个实验说明了增加分区在消除争用方面的好处在减少。从逻辑上讲,这是有道理的。有一个箱子和 64 个球,一个箱子可以装 64 个球。随着两个箱子的增加,球平均分裂,平均高度达到 32。有了四个容器,球再次均匀分裂,但体积仅从 32 个容器减少到 16 个容器。因此,随着时间的推移,分离的边际量继续减少。

这些图表揭示了罗穆卢斯争用计划的首要原则中的重要期望。首先也是最重要的,Romulus 在理论上永远无法击败手工制作的经典读/写工作负载,它表现出两个特征:( 1)无等待遍历;( 2)每个元素一个锁;( 3)搜索成本相当于转换层。这是因为 Romulus 并不试图消除其系统中的所有竞争;它在某种程度上限制了并行性,即在一系列元素上获取所有权,将粗粒度的锁分布在更小的分区上。因此,它将遭受更多的争用,导致冲突线程的性能下降一个数量级。然而,实际上,达到合理的并行水平所需的范围数量将比元素数量小一个数量级(假设 10 *线程< <映射大小)

回到设计罗慕路斯:方法论

Romulus 将代表一个调度器,它将一个抽象数据结构分割成一系列弹性分区。这些容器不限于任何大小,而是动态地适应来自应用程序线程的不相等的力。对于有序的键值存储,这些分区代表一系列键。假设程序员总是为有序的键值存储提供 API。

罗慕路斯的抽象方法论

塑造 Romulus 需要三个重要的层:适应性翻译层、数据层和同步层。在翻译层,算法必须为给定的输入键产生适当的分区。但是,它不应该表示类似于哈希映射的静态范围集,因为人们必须反射性地修改分区以展平倾斜的输入。在数据层,程序员为添加、包含和删除提供了一个抽象 API 来存储和访问一个元素。同步层确保操作的严格可串行化,并且通常跨转换层和数据层集成自身;这对于在每个分区中创建隔离和其他可序列化保证非常重要。对于图中描述的方法,我们利用读取-拷贝-更新(RCU)并创建两个数据拷贝。写入者视图(数据的活动部分)是写入者独立执行更新操作的状态空间,并且需要一个互斥锁。读者视图(数据的副本部分)使读者能够继续无等待地遍历系统。因此,Romulus 中的争用只来自更新分区的写线程。这就是为什么我们没有把读者包括在上面的数字分析中。

罗穆卢斯的方法论为其具体实现产生了许多变体;也就是说,它可以被视为一种抽象和模块化的并发方法(但最适合于有序的键值存储)。因此,从程序员的角度来看,此后的算法集中于优化性能,尽管仍然存在许多其他的适应,并提供正确的语义。在定义 Romulus 的具体实现之前,调度算法必须首先应用优先竞争分析的原则来决定如何最初划分范围,并求解阈值,使 Romulus 能够动态适应偏差输入。

应用竞争试探法

在 Romulus 中,偏斜的输入分布可以被描述为导致争用的输入操作的不平等加权。这意味着不对称是通过两种现象形成的:(I)不同权重的操作(ii)非均匀的访问分布。在基于违反竞争试探法来分割范围时,罗穆卢斯方法对歪斜的原因是不可知的,只是作为一种反射力

为了将锁队列的平均高度定为 1.05(理论上),系统分成 10 * t 个分区,其中 t 是应用程序中的线程数(这在实践中证明效果很好,尽管常数会根据机器的具体情况而变化)。在执行过程中,Romulus 遵循两个简单的启发式方法来分割/合并范围:

(1)如果应用程序线程测量到竞争高于阈值 K,则它们在应用它们的更新操作之前将一个范围分成两个新的分区。k 是通过将 95%的置信区间应用于先前的理论分析和新的假设 N = 10 * M 来确定的,其中 N =分区的数量,M =线程的数量:

在队列中等待的线程数量的测量有很大的差异,特别是当系统中线程和分区的数量增加时,正如之前所看到的那样。因此,Romulus 通过强制线程投票赞成拆分来减少误报的数量,并且在投票被批准后,拆分继续进行

(2)在获得一系列范围的访问分布之后,当两个分区的邻域被访问的次数低于每个分区的平均访问次数的 2 倍时,后台线程将合并这两个分区。虽然合并很重要,但它可以被视为节省资源的一种方式,而不是提高任何操作的吞吐量(未来的分析表明它不会影响范围查询性能)

Romulus 实现

这描绘了 Romulus 的标准实现的快照,而一个不对称的工作负载在一段时间内超越了系统。Romulus 的目标不是创建一个每个元素都有一个锁的散列映射,而是节省资源并在面对范围查询时表现良好。

转换层被实现为定制的散列树。线程将散列到的树的级别是具有完整的内部分区集的最底层。

数据层是有序的键值存储;有效支持范围查询的要求。

同步层利用 RCU 的自定义适应,特别是在提交协议中,以便在范围查询和单个元素操作中为读者进行优化。同步层深入到转换层,在转换层中存储了一个原子计数器,以便向写线程通知执行一个范围的分区的范围查询的数量。在 Romulus 的抽象需求中,翻译层中的上层树——从分区集向上构建——是不必要的。然而,为了跨分区同步范围查询,该模型扩展了 Romulus 的要求,并形成了一个称为 CRRQS(跨区域范围查询)的上层。接下来的几节从工作负载的要求和对堆栈的更深层次的影响开始,详细介绍了每一层的重要方面。

批量操作(范围查询)

为了提供可序列化的范围查询,Romulus 通过集成多粒度同步从数据库中引入概念。有两种极端的优化方法:基于锁的查询和无锁范围查询。对于跨许多单独分区的大范围查询,基于锁的实现会导致显著的性能下降,同时在各种类型的操作之间实现了公平性。例如,系统可以在活动分区中实现读写锁,阻止更新发生,直到完成它们的操作。获取一系列这些锁的开销对于实际应用来说太高了。因此,在实验开始时,Romulus 在分区的顶部构建了一个上层树层,其中表示要同步的范围的粒度。当范围查询想要在一个范围内操作时,它从较高的树层的根向下搜索,直到找到符合其界限的最小范围。此后,范围查询自动递增节点中的计数器,并继续执行其操作。这向系统中的所有其他代理发出正在执行范围查询的信号,通过将必要的状态集中到单个位置,以恒定成本在无锁机制中同步这些操作。下面讨论提交操作对写线程的影响。尽管如此,范围查询线程的两个附加方面是,它们必须在操作开始时读取指示时间戳的全局纪元,并且它们必须使用该时间戳来指示是否用分区上的最近操作来更新它们的结果。不过,这一需求的成本是不变的,不会对大型查询产生显著影响。

RCU

在 Romulus 中使用读取-复制-更新(RCU)作为同步机制具有重要的二阶效应。首先,它使读操作能够无锁地访问分区。其次,它需要额外的存储空间和操作开销。Romulus 的翻译层中的一个分区通过以下元数据表示(不包括指向树中左右子节点的指针):

因此,调度程序将数据复制到称为副本和活动的两种结构中。复制指针为读线程提供进入底层存储层的入口,而活动指针为写线程提供进入另一层的入口。在经典的 RCU 算法中,人们会将手头的内存复制到一个线程本地地址,并在提交之前隔离执行操作。在此实现中,已经提供了数据,以避免每次更新时复制大范围内存所带来的存储和计算成本。相反,为了保持操作的可序列化性,编写器线程遵循不同的操作序列。首先,写线程通过自定义互斥体获得分区的所有权。这个互斥体包含一个“无效”状态,它表示先前发生了一个合并/拆分操作,以及争用锁的写入程序的数量。假设没有任何东西被无效,并且不需要发生合并/拆分,写线程在活动数据结构上执行其更新操作。此后,当且仅当提交操作有效时,写线程执行原子比较和交换(CAS)以用新鲜状态替换副本指针。这表示更新操作的线性化点。由于调度器知道活动结构上的更新操作的结果,当且仅当操作返回真(即,成功地更新了元素)时,线程然后将等待所有读取器线程离开旧的副本,然后在执行相同的操作时将其陈旧的结构更新到新的状态。完成后,线程完成两个分区到较新版本的复制,并释放锁。RCU 操作中的一个关键点是确定提交操作是否有效。在经典的 RCU 算法中,写线程不需要等待系统中的其他代理提交。相反,提交操作以非阻塞方式进行。当范围查询被启用时,写线程必须确保当它认为范围查询可能在它的分区上操作时,它没有执行更新操作。为了确定范围查询是否依赖于分区,编写器向上遍历翻译层,并检查每个父级没有范围查询,如下所示:

如果它继续通过这个依赖路径,并且似乎没有范围查询发生,那么写线程将认为提交操作是有效的。当更新线程遍历从叶到根的路径中的元素时,出现了一个问题,并且范围查询此后在写入者的路径视图中以较低级呈现陈旧状态开始:

这个问题有一个简单的解决方案。在提交更新之前,范围查询会将操作记录到分区的 MRO 中,并带有相应的全局时间戳。在范围查询记录了它对分区的读操作之后,它将检查时间戳。如果发现节点的时间戳违反了范围查询的时间戳,那么它将撤销其本地记录中最近的操作。因此,范围查询要么不会遇到更新冲突,要么能够在没有太多开销的情况下解决它。重要的是要注意,在与上述相同的条件下,如果另一个写线程希望更新该分区,它将不会提交另一个操作(这将覆盖 MRO ),直到范围查询完成,因为它肯定会在路径的更新版本中看到范围查询的信号。因此,可以说写线程将一直延迟自己,直到不再需要 MRO,并且它可以替换 MRO,而没有进一步的影响。毫无疑问,上面的实现针对大量读取的实验进行了优化,在这些实验中,范围查询跨一系列分区执行。写操作不仅要花更长的时间通过从叶到根的路径来提交操作,而且还会因为阻塞范围查询而被饿死。

哈希树

散列树代表 Romulus 中的转换层:给定一个输入键,它提供存储该键的分区。对于读/写操作,调度器首先散列关键字,以便为线程提供要遍历的初始起始级别。哈希层的位置不会影响正确性,但会影响性能,并希望通过从较低的节点开始来降低转换层搜索成本。从这里开始,线程必须向下搜索,直到到达对应于保存其键的分区的叶节点。树叶代表数据层,包含指向结构的正确指针;它们还被连接起来,以使范围查询能够跨分区线性继续。读取操作无锁进入副本,而写入操作遵循上面的自定义 RCU 算法。但是,在获取锁之前,写线程会增加一个变量,该变量表示分区中写线程冲突的数量。

步骤 1:读取等待队列的大小

然后,写入方记录分区中写入方冲突数量的测量结果。如果度量值高于预先计算的阈值(如前面的分析所示),那么他们投票决定分割分区。如果获得了足够的投票,一旦任何写线程获得了锁的所有权,它们将执行拆分操作

第 2 步:通过 fork(K) API 执行一个 split 操作,其中 K 表示要在范围内进行拆分的值。

在活动数据结构内完成拆分操作后,它会通过向当前分区添加两个新叶而传播到哈希树中。两个新的活动分区将实际上被放入叶子的副本指针中,因为这代表了 RCU 原语中预先的提交。从这里开始,由于所有新的读取器都被重定向到更新的结构,写入器线程将等待所有旧的读取器完成,然后对陈旧的副本结构执行相同的操作。最后,线程在自定义互斥体上指示它不再有效,所有竞争的线程重新开始它们的操作,慢慢进入更新的状态空间,并希望均匀地分散在两个新的区域。

翻译层的自反递归性质

这个过程可以根据需要重复多次,以消除阈值以下的争用。

合并/拆分

近年来,在设计跨公共数据结构的合并/拆分操作方面已经开展了许多工作。SkipLists、红黑树、链表和许多其他结构可以执行分割操作,其时间复杂度与它们的搜索操作相同。因此,当程序员在这种假设下提供 fork(K)的实现时,Romulus 中的 split 操作在算法上使其中一个线程完成更新操作的时间加倍(排除范围查询的存在)。上面提供的用于合并/拆分的实现利用应用程序线程来自己识别争用,并通过同步方法在它们的关键路径内执行操作。这在算法中产生了错误分类热点的风险,并且需要精确的阈值模型来衡量。有人可能会说,将这项工作卸载到后台线程可能有意义;然而,进一步的分析表明并非如此。通过助手线程对争用做出异步、迟缓的反应可能有助于系统完全准确地达到稳定状态,但是用单个助手线程识别大型数据结构的热点的时间和复杂性将无法立即解决不平衡的工作负载。干草堆里的一根针的故事。结果,由于持续的冲突,这些线程的并行性会显著降低。因此,在大多数情况下,解决问题的时间可能比应用程序线程自己处理拆分花费的时间更多。此外,更重要的一点是,如果不对称继续变化,那么异步线程将很可能获得冲突的误报,并且可能永远无法解决动态不对称。这扩展到异步估计的更一般的问题,因为竞争的近似变得更加难以为动态系统建模。这种对不平衡工作负载的忽视会给需要跨不同场景弹性的系统带来灾难,因此会阻碍 Romulus 的主流应用。

其他注释

从前面的图和未来的分析中可以看出,转换层的形状反映了穿过罗穆卢斯的偏斜分布;也就是说,对于经常发生冲突的热点,Romulus 将深化翻译层,以消除更多范围内的争用。一个要考虑的有趣问题是,给定一个偏斜分布,Romulus 的翻译层是否收敛到一个稳定状态。非常宽松的形式分析和实验结果证明,Romulus 在给定偏斜分布的情况下确实收敛到稳定状态,并且它通过主动适应冲突中的应用程序线程本身的争用来快速收敛。因此,Romulus 将在翻译层实现顺序行为,直到新的偏斜超越系统并强制分区更新。在线系统经常困扰性能的一个方面是内存回收行为:安全地释放内存以供重用。Romulus 已经将内存回收的开销融入了它的分区隔离中;也就是说,不需要向 Romulus 添加任何新的元数据来保护内存的释放。此外,可以同步回收内存,而不是异步回收内存(这是一种常见的方法,它会延迟回收,直到其他线程无法在通往退休元素的路径上继续前进)

结果呢

作为一种调度算法,Romulus 中性能的一般趋势应该事先知道:
(1)没有其他瓶颈,如内存,Romulus 将很好地从单线程基线扩展到更高的线程数
(2)倾斜的输入将很快得到解决,并将争用分散到以前的级别

这些在下面的图表和分析中得到证实。另一方面,与竞争对手的相对绩效和其中的垄断案例很难量化;一个人必须遵循一个发现过程。为了准确了解 Romulus 的性能,应该部署一个串行跳表,并与两个手工制作的、最先进的并发跳表解决方案进行比较:NUMASK 和 Herihly。这分别代表了两种最快的无锁和基于锁的结构(至少目前如此)。以下实验中使用的测试平台由一台配备 4 枚英特尔至强白金 8160 处理器的服务器组成,总共提供 192 个线程。有 4 个插槽通过 4 个 NUMA 区(一对一)和 768 GB 内存托管 4 个处理器。所有数字是十次试验的平均值。在实验中,应用程序线程平均分布在 NUMA 区域。注意:竞争对手不提供可序列化的范围查询解决方案,因此我们根据他们的上限进行基准测试,因为他们不安全地遍历结构,并在其批量操作中显示陈旧的结果。尽管如此,对于 200 万大小的数据结构,10%的 500 个预期元素的范围查询、70%的读取和 20%的更新操作,可以观察到以下性能:

synchrobench 中的吞吐量比较

该结构在手工制作的解决方案的性能上限方面有很好的伸缩性,直到 60 个线程。尽管如此,罗穆卢斯在即插即用加速器方面做得非常好。它比 Node Replication(ASPLOS ' 17 的最佳论文,从一开始就是报价的来源)更好的一个领域是他们订购的关键价值商店;不过,在堆栈/队列/其他结构中,它会比较差,因为在这些结构中,操作只是从结构的前面和后面弹出来——它们在那里找到了自己的最佳位置。

在跳过列表键值存储中,NR 由于其复制方法中的单个编写器要求而不能很好地扩展

基准链接列表是一个有趣但无效的实验,因为翻译层卓越的算法性能降低了搜索成本,导致了超线性加速。

既然我们已经知道了在 Skip Lists 中手工制作的方法的性能,那么 Romulus 的其余声明必须针对手边的数据结构进行研究;具体来说,(Romulus 在偏斜分布下的表现如何?(ii)不同的存储桶如何影响更新/RQ 性能?

确定合并/拆分算法成功与否的两个最重要的衡量标准是它适应争用的速度(即解决争用的时间)以及与均匀分布相比对性能的总体影响。罗穆卢斯在这两方面都表现出色。下图拍摄了实验中每毫秒吞吐量的快照,以创建吞吐量随时间变化的视图。应用程序线程在~5 秒钟引入一个偏斜分布,以便观察(1) Romulus 如何适应采用分叉/合并操作的工作负载(2)Romulus 未采用分叉/合并的天真版本的性能增益

这证实,即使罗穆卢斯牺牲了几毫秒的短期性能,系统的长期性能也会因为即将到来的偏差而迅速恢复到接近先前的水平。

对于相同的工作负载,系统的自反性质可以通过在实验结束时查看 Romulus 叶节点中的三个访问分布来描述。下图绘制了(1)输入偏斜(即无分叉/合并时的访问分布)(2)具有 48 个线程和 200 万大小的 100%更新操作的实验结果(即启用分叉/合并时的访问分布),以及(3)给定 Romulus 部署的试探法集,该实验的目标理论结果(K = 3)。它通过查看每个分区的访问百分比(单个分区中观察到的访问/跨分区的总访问)来实现这一点

Romulus 低于理论目标,并且在分区数量上的开销比预期的多,很可能是由于悲观的宽松竞争启发式算法(K = 3)。具有强保证的明确定义的阈值对于 Romulus 收敛到分区的目标最终平衡状态是至关重要的。阈值太高,启发式算法无法正确适应不平衡的工作负载。阈值太低,系统消耗的资源超过初始目标,并且对输入分布进行错误分类。

最后,一个有趣的观察结果是,在分别希望合并和分割范围时,范围查询和更新会产生冲突。下图显示了当增加纯范围查询和更新工作负载的存储桶时,吞吐量如何变化

范围查询 v 根据存储桶更新吞吐量增长。(i) 64 个线程(ii)100 万个大小(iii)完整数据结构的范围查询(iv)对数轴(v)标准化意味着将每个数字除以最大获得值,以限制 0 和 1 内的所有点

总的来说,Romulus 可以用手工制作的最先进的解决方案很好地扩展,并且由于合并/分割算法的自反性质和竞争的概率方法,在偏斜分布下表现得相当好。我希望在这项工作中发现的更多的想法将被正式化,并使其成为现代系统中寻找一种简单的并发方法。

//

新手数据科学错误使十几项医学研究无效

原文:https://towardsdatascience.com/rookie-data-science-mistake-invalidates-a-dozen-medical-studies-8cc076420abc?source=collection_archive---------6-----------------------

克里斯蒂安·鲍文在 Unsplash 上的照片

母鸡吉勒·范德维勒注意到大量研究报告在预测准妈妈是否会早产方面近乎完美的准确性,他惊讶得目瞪口呆。这是巨大的。

婴儿难以忍受的高死亡率从一开始就一直困扰着人类。早产是这些过早死亡的主要原因,它困扰着美国十分之一的新生儿。如果有可能肯定地说一名妇女是否会提前分娩,就可以做好准备以减少并发症的风险。

然而,预测早产被证明是难以捉摸的。对于妇科医生来说,要确定一名女性是否提前分娩,他们必须考虑大量的风险因素,包括空气污染、家庭暴力和压力等难以理解的因素。到目前为止,专家们还没有弄清楚如此众多的潜在煽动者之间的复杂相互作用。

现在,在人工智能的帮助下,研究人员似乎已经成功解决了这个难题。兴奋之余,根特大学机器学习专业的博士候选人范德维尔招募了他的同行,并着手复制令人难以置信的结果。他一点也不知道他们即将踏上一场彻底的科学毁灭之旅,导致十几篇同行评议的文章被宣布无效。

任何机器学习系统的基础都是数据。为了让算法学会做出准确的预测,需要用大量相关的例子来教授它们。这些示例的集合称为数据集。

范德维尔遇到的所有看起来非同寻常的研究都是基于流行术语——早产 EHG 数据库。它包含了几百条记录,每条记录对应一次怀孕。每份记录依次包含临床变量,如母亲在产科医生就诊期间的年龄和体重,距离实际分娩的周数,以及由放置在腹部的电极测量的电信号。

通常情况下,医学数据集的敏感性使得原始研究之外的第三方研究人员无法访问它们。这使得繁殖变得极其复杂,如果不是不可能的话。因此,当 Vandewiele 的团队发现必要的数据集可以公开获得时,他们只能想象他们如释重负的叹息。只要按一下按钮,数据就是他们的了。

下载完数据后,是时候开始将其输入论文中概述的预测模型了。理想情况下,科学家们会开源他们的代码库,这样这一步就相当于仅仅运行一些现有的脚本。不幸的是,在我们生活的世界里,将研究代码库保密的做法在人工智能社区很常见。

这群人不是在困难面前退缩的人,他们卷起袖子开始工作。他们采用了显示最佳结果的文章,并完全复制了它的设置。但是当他们最终进行分析时,奇怪的事情发生了——获得的结果明显比报道的差。这些预测比随机预测好不了多少!

“我们肯定犯了一个错误,”范德维尔想。然而,在花了几天时间反复检查他们的每一行代码后,似乎没有任何问题。最终,他们的好奇心被挫败感所取代,团队放弃了第一个实验,并试图复制下一篇论文。同样的事情。该系统的运行情况比宣传的要糟糕得多。发生了什么事?他们偶然发现了一个阴谋吗?

现在完全疯狂,团队处于野兽模式。文章被重新实现左和右。尽管如此,没有一个复制品能达到承诺的近乎完美的预测精度。这就好像他们被困在一场希腊悲剧中,被一块无情的巨石折磨着,在他们即将把它送上山顶时,这块巨石掉头滚下山坡。

几个月过去了,毫无结果,在重复 11 项研究的巨大努力下,这个团队再也受不了了。但是就在他们准备认输的时候……一个突破。在将数据输入机器学习模型之前,只需对数据的组织方式进行简单的改变,Vandewiele 和他的合作者最终能够获得与原始研究相同的结果。唯一的问题是:这样的数据处理方案存在根本性的缺陷。

为了理解这个难题,我们需要更仔细地看看构建机器学习系统背后的方法论。

泛化的概念是人工智能的核心。为了有任何用处,一个根据输入训练的模型——例如母亲年龄和体重的组合——期望的输出——怀孕前的周数——是已知的,它应该能够推广到新的、以前看不到的输入组合。这种算法就像一个学生在考试中死记硬背一些相似但不完全相同的问题。

相应地,机器学习中使用的数据集被一分为二。第一部分——训练集——用于教授算法,而第二部分——测试集——用于衡量模型对任务本质的理解程度。显然,正如任何称职的校长都可以证明的那样,这两套系统不能重叠是至关重要的。

除了对数据集进行分区,研究人员还需要确保它包含不同类型输出的可比数量的记录。继续类比,一个学生要在考试中取得好成绩,他需要同样多的练习不同类型的问题。一个只研究积分的初露头角的数学家是不会在求导这一节中胜出的。

然而,众所周知,医学数据集是不平衡的。足月-早产 EHG 数据库也不例外,包含的足月分娩记录几乎是早产记录的 7 倍。为了补偿这种不均衡,科学家将对应于少数类的数据点的副本添加到原始的不平衡数据集中。这个过程称为过采样。

让他们惊讶的是,范德维尔的团队发现,这些好得令人难以置信的研究的作者在将数据集一分为二之前进行了过采样。因为这种分割是随机进行的,所以在训练集和测试集中出现相同的数据点会产生毁灭性的副作用。实际上,在考试之前,模特们就已经看到了要被评估的问题。难怪他们的结果惊人。

虽然很明显,但这个错误在数据科学家中并不罕见。机器学习竞赛平台 Kaggle 的联合创始人本·哈姆纳(Ben Hamner)将信息从训练设备意外“泄露”到测试设备称为该公司的“头号挑战

“我们很多人都犯过同样的错误,包括我自己。我想一个优秀的机器学习研究者的与众不同之处在于,你应该永远对近乎完美的结果持怀疑态度,”范德维尔说。

在这里,范德维尔也触及了一个更好的点。随着算法开始管理我们生活中越来越重要的方面,我们需要确保管理这些系统的专业人员无愧于他们的职责。虽然参加一些在线课程足以开始优化广告收入,但应该要求处理我们医疗数据的分析师接受更多培训。否则,我们就是在拿无辜的生命冒险。

避免机器学习中的新手错误

原文:https://towardsdatascience.com/rookie-errors-in-machine-learning-bc1c627f2789?source=collection_archive---------9-----------------------

或者,如何不搞砸

来源:约翰·T 在 Unsplash

这是我 2019 年 1 月在麦吉尔大学演讲的书面版本。随后,安德烈·卡帕西写了一篇很好的帖子,更具技术性和深度学习的具体内容,但内容重叠——在这里查看

你可以通过你所犯错误的质量和数量来跟踪你对某个主题的专业知识的加深。这篇文章列出了我和我周围的人在机器学习中犯的一些更重复的错误,希望能加速你走出不熟练的浅水区,进入犯真正有趣错误的黑暗深水区。这不是一篇介绍性的文章:你需要在 Pytorch 或 Tensorflow 中破坏一些模型才能跟上。

我将尝试从中间分布中提取,但一些黄色和紫色的样本也可能会出现在那里。图片来源。

机器学习中常见的错误

我试图将我们在 ML 中看到的错误分为三大类,严重程度逐渐增加。

1.浪费你时间的错误

这种错误是令人遗憾的,但也是可以忍受的。在深度学习中,可怕的形状错误是最常见的,当你试图将大小不兼容的矩阵相乘时就会出现。

我不打算多谈这些错误,因为它们通常是显而易见的:程序失败了,你必须找出原因。你知道你犯了一个错误。你修复它,发现一个新的错误,然后循环重复。没人会受伤。

2.导致实验不准确的错误

这是一种代价更高的错误,因为你最终会做出糟糕的决定。

想想亚航飞行员的故事,他因为 GPS 不可靠而没有去马来西亚,而是去了墨尔本。如果你的导航有问题,你会很快到达你不想去的地方。

例如,如果您实现了一个添加了一堆参数的新特性,并将其与现有模型的性能进行比较,而没有重新进行超参数搜索,您可能会错误地认为您的新特性使事情变得更糟。事实上,您可能需要更加规范化,以揭示更具表现力的模型的好处。

随着时间的推移,导致实验不准确的错误会越来越多,因此尽早发现这些错误是非常有价值的。

3.让你相信你的结果比实际要好的错误

错误的大老板:犯了一个导致你高估表现的错误。这些很难发现,因为我们对看到它们抱有偏见:当模型表现得令人惊讶地糟糕时,我们倾向于再看一眼,但当它表现得令人惊讶地好时,我们更有可能祝贺自己高超的直觉(本质上是一种形式的确认偏见)。

高估的常见原因是过度适应您的测试集,您的数据不代表真实世界,或者只是搞砸了您的度量。(下面将详细介绍这些内容)。

如果你从这篇文章中只学到一样东西,那应该是这个。没有什么比意识到你的模型比你想象的更糟糕更令人尴尬或沮丧的了。

机器学习生命周期

大多数机器学习都被上面的香肠制作图所捕获。你获取一些数据,通过一个模型,用一些指标量化输出。我们来看看每个阶段都存在哪些让自己看起来很傻的机会。

结果是什么:度量

机器学习可以归结为试图降低你的损失函数值。

更在https://lossfunctions.tumblr.com

然而,损失函数从来就不是你想要优化的:它只是一个近似值。在训练分类任务的普通情况下也是如此:你在为训练或验证集交叉熵进行优化,但你实际上关心的是测试集准确性(或 F1/AUC)。在实际目标是一些松散的、不可微的东西的情况下(比如“制造一个听起来像人类的聊天机器人”),低保真度近似的问题甚至更加尖锐。

无论你的环境如何,如果你的损失函数不能代表模型的真实表现,你就有麻烦了。这里有一大堆搞乱这里的方法:

1.将一些训练集混合到测试集中

来源

将训练数据混合到您的验证或测试集中很容易做到,并且将产生极好的模型性能(由您的不可靠的测试集评估),以及在现实世界中糟糕的性能。

当然,你的 train/val/test 应该是disjoint——包含不同的例子。有时你需要仔细考虑不相交集合的确切构成。这可以决定通过测试集的性能来量化哪种类型的归纳。例如,如果我们试图从收据中提取总价值,显然测试集应该包含从未见过的收据。但它是否也应该包括从未见过的商家,以向我们保证我们不会过度适应特定的商店?

有时你需要仔细考虑不相交集合的确切构成。这可以决定通过测试集的性能来量化哪种类型的归纳。

一种好的做法是将您的数据一次分成训练集、验证集和测试集,然后将它们放在文件系统中的不同文件夹中。无论在哪里读入数据,都要使命名超显式:例如, TrainDataLoaderTestDataLoader,

2.错误指定损失函数

这很难做到,因为大多数框架会为你处理损失函数规范。

然而,有很多方法可以滥用给你的坚如磐石的实现。我在这里看到的两个最常见的错误是混淆损失函数是否期望接收概率分布或对数(即,是否需要添加 softmax),以及混淆回归和分类。

后者出奇的普遍,甚至在学术界也是如此。例如,亚马逊评论数据集包含评论和星级评定,经常被顶级实验室用作分类任务。这显然不太正确,因为 5 星评价更像 4 星评价,而不是 1 星评价(数据是有序的)。

错误指定您的测试功能

虽然深度学习的损失函数必须是可微的,但我们经常使用一套不同的不可微指标来表达测试时的性能。例如,在机器翻译和摘要中,我们分别使用 BLEUROUGE ,对于其他任务,我们可能使用准确度、精确度或召回率。

通常,这些比你的损失函数值更容易理解,损失函数值可能很难理解(方便的提示:如果数据集是平衡的,当你开始训练时,交叉熵应该是 -log[n_classes] )。因此,在培训期间尽可能多地记录测试集指标是个好主意,而不是等到测试时才使用它们。这将让你更好地了解训练的进展情况,防止你在结束训练后才发现问题。

选择测试函数时要小心。例如,您不能通过计算匹配字符的数量来使用准确性来描述序列模型的性能,因为序列之间的任何不对齐都将产生零准确性。因此需要使用编辑距离代替。选择错误的指标来评估你的模型是一次痛苦而难忘的经历。

继续使用序列建模示例:确保您理解特殊字符—通常是序列开始(SOS)、序列结束(EOS)和填充。如果你忘记将它们从你的计算中排除,你可能最终得到看起来很好的模型——但只是真正擅长预测充满填充的长序列。

我曾经做过一些语义解析的工作,目的是将自然语言语句转换成数据库查询,这些查询可以被评估以回答诸如“明天从蒙特利尔到亚特兰大有多少趟航班?”。为了表征其准确性,我们将候选数据库查询发送到数据库,并检查返回的内容是否与我们发送的真实数据库查询相匹配。在我犯的一个更严重的错误中,我设置了这样一种情况:如果您向数据库发送无意义的查询,数据库会无声地失败,返回一个“错误”字符串——然后我会发送预测的和真实的数据库查询的损坏版本。这两个函数都返回了字符串“error”——然后被计算为 100%准确。

这就引出了一个指导原则,那就是尝试设置事情,这样你犯的任何错误都只会使性能变得更差,并且总是查看你的模型实际做出的预测,而不仅仅是指标。

使用指标避免错误

  1. 在步骤 1 中运行所有指标

这就是 random 的样子。如果你的模特在没有任何训练的情况下表现得出奇的好,那你就搞砸了。

2。记录一切

ML 是一门定量学科,但统计学是骗人的。你的眼睛很少这样。是的,你应该记录你能想到的所有数字,但是你也应该以人类可读的方式记录模型的预测。

在 NLP 中,这通常意味着反转你的符号化,这可能会很痛苦——但 100%值得。它还能让你对模型训练的定性方面有宝贵的见解。例如,语言模型通常从学习输出像eeeeeeeeee<PAD><PAD><PAD>,这样的字符串开始,因为这些是数据中最常见的字符。

如果您处理图像,记录东西可能更麻烦,因为您不能只将图像输出到文本文件或终端。我的同事使用 ASCII art 来克服这一点,使他们能够在训练过程中可视化光学字符识别模型的输入:

在培训过程中,尽一切可能可视化模型输入和输出!来源

3。研究您的验证集

使用您的测试指标来识别集合中表现最好和最差的样本。去了解他们。它们是否符合你的直觉,即模型应该在哪里表现良好,何时应该挣扎?如果你有一些量化信心的方法,比如 softmax,你可以探索模型超级自信的情况——以及错误的情况。

回归中,学习残差是有好处的。请记住,平均值可能会产生误导,正如安斯科姆的四重奏所阐述的那样:

安斯科姆的四重奏:4 个都有相同的均值和方差,用同一条回归线拟合最好。不要过于依赖统计数据:要贴近数据。来源

如果你有一个多维的问题,试着画出误差和单个特征。输入空间中是否有你做得特别差的区域?如果是这样,您可能需要收集更多的数据或在该地区进行扩充。

考虑烧蚀或扰动某些特性,并检查它如何影响性能。像 LIMEEli5 这样的工具让机器学习模型变得简单明了。这篇精彩文章描述了扰动分析如何揭示 CNN 的 x 光分类使用 x 光机本身引入的标签来决定患者是否患有肺炎,因为使用的 x 光机类型与肺炎患病率之间存在相关性。

模型

你参加的大多数课程和面试都将关注机器学习的建模方面。事实上,作为一名机器学习实践者,你的职业生涯将主要花在担心数据和指标上,而不是关注机制的奇异实现。

绝大多数深度学习错误是形状错误,如上所述,这是一件好事:它们会导致相对容易修复的显式故障。

打破你的模型的更微妙的方法包括:

  1. 包括不可微运算

在深度学习模型中,一切都必须是端到端可区分的,backprop 才能工作。因此,你可能会期望像 Tensorflow 这样的深度学习框架中的不可微操作有明确的路标。你错了。我已经看到了与 Keras Lambda 层的特别混乱,它有一种打破背投的天赋。这种特殊毒药的解药是用model.summary()来验证你的大多数参数是可训练的——如果你看到具有不可训练参数的层,你可能已经破坏了自动分化。

如果你看到带有不可训练参数的层,你可能已经破坏了自动分化。

2。测试时未能关闭漏失

测试时需要关闭 Dropout,否则,你会得到随机的结果。这可能非常令人困惑,特别是对于部署您的模型并试图为它编写测试的人来说。

在大多数框架中,这是通过将模型模式设置为eval来处理的。还要注意,在培训期间,退出会产生一个令人惊讶的现象,你的验证损失比你的培训损失更好,因为当评估前者时,你已经退出了。当你第一次看到这一点时,它看起来像是过度拟合的反义词,并可能导致一些头部划痕。

3。维度混乱

不同的框架在安排诸如批量大小、序列长度和通道数量等公共维度方面提供了不同的约定。有些给你选择翻转的机会,而其他的如果你做错了就会默默的失败。

未能正确排序你的维度会产生奇怪和微妙的行为。例如,如果你混淆了批次大小和序列长度,你将会在你的批次中的例子之间丢失信息,并且不能随着时间的推移保存信息

避免模型错误

  1. 模块化、可测试的代码

建模错误主要可以通过结构良好的代码和单元测试来避免,这两者相辅相成。

通过将您的模型分解成具有明确定义的角色的离散块,您将能够有效地测试它们。您的测试应该集中在验证维度是否与您在不同批次和输入大小的条件下所预期的一样。我真的很喜欢【Chase Roberts 关于单元测试 ML 代码的这篇文章。

2。对维度有自信

我喜欢在我的 ML 代码中加入关于维度的断言。这让读者非常清楚哪些维度应该改变,哪些不应该改变——当然,如果发生意外,它会抛出一个错误。

漂亮的表达张量流代码,由 Keith Ito 提供。请注意模块化和形状注释。

至少,试着养成向代码中添加关于维度的注释的习惯,以限制读者的工作内存负荷。查看 Keith Ito 的这个漂亮的 Tacotron 实现,这是一个超级注释 Tensorflow 代码的例子。

3。用小数据过度拟合简单模型

这是另一个 Karpathy 技巧,我很早就知道了它的价值。您应该确保您的模型只适合数据集的一小部分。

为了加分,通过配置文件使您的模型易于配置,并指定一个参数数量最少的测试配置。然后在 CI/CD 中添加一个步骤,检查这个小模型是否能够适应非常小的数据集,并自动运行它。这将有助于捕捉对代码库的任何更改,这些更改会破坏模型或培训管道。

输入什么:数据

首先,了解你的数据

在你开始建模之前,你应该已经厌倦了看数据。

我们做的大多数人工智能都试图复制人脑的一些模式识别能力。在开始写代码之前,通过练习这些能力,让你的生活变得简单!理解您的数据集将有助于您思考架构和指标,并迅速了解性能问题可能出现在哪里。

通常,它还会标记数据本身的问题:类别不平衡、文件类型问题或偏见。后者可能很难用算法来评估,因为要发现它们,你需要一个与你试图训练的模型一样聪明的模型。例如,您将需要查看您的数据来注意类似“我所有关于猫的照片都是在室内拍摄的,而我所有关于狗的照片都是在室外拍摄的,所以也许我正在训练一个室内/室外分类器,而不是一个猫/狗分类器?”。

Andrej Karpathy 为 ImageNet 构建了一个标签平台,以评估自己的表现,并加深对数据集的理解。来源

正如安德烈·卡帕西在这里讨论的一样,投入大量精力开发软件来帮助你查看、分割和切割数据是值得的。2018 年,当我在伦敦 KDD 听他演讲时,他强调优步的许多 ML 工程师并没有编写代码来优化模型;他们正在编写代码来优化数据标签。

为了理解您的数据,您需要获得关于三种分布的直觉:

  • 输入分布,例如平均序列长度、平均像素值、音频持续时间
  • 输出的分配——等级不平衡是一个大问题
  • 输出|输入的分布(这通常是您正在建模的内容)

选择加载数据的方式

有效地加载和预处理数据是机器学习工程中比较痛苦的部分之一。我总的来说发现在效率和透明度之间有一个折衷。

一方面,像 Tensorflow Records 这样的专用数据结构允许您将数据序列化为大数据包,并防止频繁读写磁盘。然而,你在效率上的收获是在透明度上的损失:这些结构很难询问,如果你决定要添加或删除文件,你必须重新序列化。

另一方面,简单地从磁盘直接读入一个列表并遍历它不会赢得任何速度上的奖励,但它完全清楚发生了什么。

现在我发现 Pytorch DatasetDatasetLoader实用程序是控制和效率之间的一个很好的折衷。有专门用于处理文本( torchtext )和图像( torchvision )数据集的包,它们提供了相对高效的方式来加载、填充和批量处理每个域中的数据。我也听说过 Pytorch Lightning 的好消息,但我还没有用过它。

加快数据加载的方法

这里有一些有趣的方法,是我过去曾经尝试过的。

  1. 没有装载你认为你正在装载的东西

令人惊讶的是,很容易丢失数据,或者重复加载相同的数据。以下是我处理这件事的一些方法:

  • 编写正则表达式从文件夹中加载某些文件,然后在添加新文件时没有更新这些正则表达式,这意味着我没有加载这些文件
  • 误算一个历元中的步数,因此跳过了一些数据集
  • 在一个文件夹中有递归符号链接(是的,真的),这样相同的数据被多次加载(在 Python 中达到 1000 的递归限制)
  • 不完全遍历文件层次结构,因此无法加载子文件夹中的数据

2。数据结构不正确

不要把所有的数据放在一个目录中。

如果你有 1,000,000 个文本文件,并把它们都放在一个文件夹里,你的生活将会很痛苦,因为对那个文件夹的任何操作都要花费很长时间。很多时候,当你只想获取一些文件来查看或计算一些东西时,你会因为等待加载大量的文件夹而大大降低工作流程的速度。如果你的数据远程存储在一个数据中心,并且你已经使用sshfs安装了目录,这种情况会更加严重。

第二个陷阱是在应用昂贵的预处理步骤时,无法制作数据的副本。将耗时的预处理结果保存到磁盘是一个好主意,这样您就不必在每次运行模型时重做工作,但是重要的是不要覆盖原始数据,并且能够跟踪哪个预处理代码在什么数据上运行

类似下面的内容通常很有效:

raw [your raw data lives here, in whatever format you received it] processed [any preprocessing results in a new folder here] 2018jun14_b9f0c41 [date & commit stamped for reproducibility]
      batch-1 [data split into manageable batches of ~1000]
      batch-2
      ... 2017jun18_c760c52 
      batch-1
      batch-2
      ...

3。预处理不当

特别是在 NLP 中,很容易在预处理中滥用数据。

对非 ASCII 字符的不正确处理可能是一个很大的问题,而且它们经常很少出现,很难被发现。

标记化带来了很多出错的可能性。如果您正在执行基于单词的标记化,很容易在一个数据集上形成您的词汇,然后在另一个数据集上使用它(或者在一些预处理之前或之后的同一个数据集),从而导致大量的词汇外单词。这是一个致命的无声错误——你的模型不会崩溃,它只是不能很好地工作。

训练集和测试集之间在词汇上的巨大差异在这里会产生问题,因为对于测试集中的单词,而不是训练集中的单词,您将无法了解任何信息。同样,充分了解您的数据并尽早发现这些问题是非常值得的。

避免数据处理中的错误

  1. 记录一切

确保每次转换数据时都在训练过程中记录示例。你不应该只是记录你的模型输出了什么,你应该记录输入了什么。

2。了解你的数字

您应该非常熟悉以下统计数据:

  • 你有多少例子
  • 对于给定的批量大小,对应于多少批
  • 多少批次构成一个时期(即通过整个数据集)

同样,你可以也应该记录这些事情,或者你可以加入一些assert语句来确保每件事都有意义。

3。预处理期间保存状态

一些预处理步骤需要或创建工件,您需要努力保存这些工件。例如,如果您通过训练集的meanvariance对数字数据进行规范化,那么您需要保存那个meanvariance,这样您就可以在测试时应用相同的转换。同样,如果你不保存你的训练词汇,你将无法在测试时以同样的方式进行标记——在测试中形成一个新的词汇并重新标记将产生无意义的结果,因为每个单词将获得完全不同的标记。

4。如果可以的话向下采样

当您拥有由大型文件(如图像和音频)组成的数据集时,很容易通过最少的预处理将它们输入到您的神经网络中,希望网络能够学习到预处理效果最好的方法。

如果你有无限的时间和计算,这可能是一个好办法,但在现实世界中,一些明智的下采样可以走很长的路。你可能不需要全高清图像来训练狗/猫分类器,尽管你可以使用扩张卷积来学习下采样器,但如果你在进行任何学习之前只是以传统方式进行下采样,那么你的时间和梯度下降会更好地利用。

向下采样允许您更快地完成模型拟合、模型评估和模型摆弄的循环,因此是一项很好的时间投资。

结论

总结一下,在你的机器学习冒险中要遵循的 5 条指导原则:

  1. 从小处着手,这样你的实验会进行得很快。限制你的循环时间能让你更早发现问题,更快检验假设。
  2. 贴近数据。如果你不理解数据,你就无法做好数据建模工作。不要被诱惑而把时间花在玩弄花哨的模型上,而不是做看数据的无聊工作。
  3. 记录比你认为需要的更多的信息。你对学习过程的了解越多,就越容易发现异常并提出改进措施。
  4. 比起效率,更喜欢简单和透明。少量的时间节省并不能证明代码或数据结构变得更复杂是合理的。浪费在试图理解不透明代码上的人力时间比浪费在低效算法上的计算时间要糟糕得多。
  5. 如果事情好得令人难以置信,那就是真的。在机器学习中有很多方法可以愚弄自己:成为一名优秀的科学家意味着毫不留情地发现和消除这些机会。

请在下面的评论中分享你犯下的任何有趣或奇特的错误,如果你有任何你认为我应该记录的好习惯,请告诉我!

要避免的菜鸟饭桶错误

原文:https://towardsdatascience.com/rookie-git-mistakes-to-avoid-45919c0058f8?source=collection_archive---------17-----------------------

https://towards data science . com/how-to-think-about-data-28 EC 05 a 75 CD 2

使用 Git 时,做一些简单的事情来避免沮丧和浪费时间

数据工程师通常比数据分析师、数据科学家和 ML 工程师更熟悉 Git 这样的开发工具。在过去的几年里,随着越来越多的非工程工作涉及到编写代码,像 Git 这样的源代码控制系统已经被广泛采用。尽管收养率有所上升,但没有足够多的新收养者对此感到满意。

当雇主没有足够重视培训他们的团队,或者当他们没有从他们的工程同行那里得到足够的支持时,非工程团队经常面临使用 Git 的问题。人人自立的模式是不可持续的。培训是团队发展和成长的一个非常重要的部分。

非工程团队在编写代码时有不同的思维方式。我试图在的另一篇文章中总结这些观点。在这里,我们将讨论的不是运行错误的 Git 命令可能会犯的错误,而是犯更基本的错误,比如直接在主分支上工作、进行非常大的提交等等。

当师父是你唯一的分支

如果你是团队的一员,并且你唯一的分支是 master,那么你基本上错过了 Git(或者一般的版本控制系统)背后的整个概念。在这种情况下,每个人都有一个本地主分支,并提交到该分支,然后将更改推送到远程主分支,发现有无数的冲突。避免这种情况,使用分支——这就是乐趣所在。

StackOverflow 上有一篇 knittl 写的关于为什么不使用 master branch 进行开发的很好的总结。

主分支应该代表代码的“稳定”历史。使用分支来试验新特性,实现它们,当它们足够成熟时,您可以将它们合并回 master。

这样,master 中的代码几乎总是可以顺利构建,并且可以直接用于发布。

不遵循分支方法

一旦您理解了分支的基本概念,并且能够创建分支并将它们合并到 master 中,您将遇到的下一个问题更多的是从分支管理的角度来看。所有分支方法中最流行和最有价值的是 gitflow。我从一个数据工程师的角度写了这篇文章。同样的原则也适用于任何使用 pandas、numpy、scipy 等工具用 SQL 或类似的 Python 脚本编写查询的人。

[## SQL 的 Git 最佳实践

使用 GitHub、GitLab、BitBucket 等来存储和组织 SQL 查询,以实现发现和重用

towardsdatascience.com](/git-best-practices-for-sql-5366ab4abb50)

Gitflow 包括三个层次的分支,其中masterdevelopfeature是三个不同的层次。这三种情况也有例外,但你不必一开始就陷入其中。点击此处了解更多信息—

[## GitFlow 简介

GitFlow 是 Git 的一个分支模型,由 Vincent Driessen 创建。它吸引了很多关注,因为它是…

datasift.github.io](https://datasift.github.io/gitflow/IntroducingGitFlow.html)

在一次提交中提交一千个文件

从技术上讲,这不是一个错误,只是一个非常糟糕的做法。除了 repo 中的初始提交之外,您的提交应该只包含可以在单行提交消息中明确定义的更改。提交所有未提交的更改对于一千个新文件来说不是一个好的提交消息。

小投入,多投入。

按照这条规则去做,你会没事的。这个想法是推动最小的完整工作单元。有一篇很棒的文章深入探讨了这个问题。

[## 为什么应该编写小的 Git 提交

易于理解的小提交是伟大软件开发的支柱

medium.com](https://medium.com/better-programming/why-you-should-write-small-git-commits-c9a042737aa6)

这里有几篇我喜欢并找到的其他好文章

结论

就像 SQL、Python、Javascript 等其他广泛采用的技术一样,如果你想写代码的话,Git 可能是你不可或缺的东西——不管是以什么身份。你肯定要处理版本控制系统。因此,如果你花一些时间去理解它是什么以及它是如何工作的,那是最好的。

[## 对 Git 最大的误解

你可能误解了 git。

medium.com](https://medium.com/@gohberg/the-biggest-misconception-about-git-b2f87d97ed52)

最初,您可能不需要复杂的 Git 命令,因为只需要基本的命令就可以了。有几篇类似于 this 的文章讨论了运行错误的 Git 命令所犯的错误。只要把这几条( 12345 )过一遍,就应该排序了。

使用 Python 解决调度问题

原文:https://towardsdatascience.com/roster-optimization-using-python-85b26d58e806?source=collection_archive---------7-----------------------

实践教程

通过使用简单的 Python 编程应用混合整数模型来公式化和解决复杂的排班问题

扬·克朗斯Unsplash 上拍摄的照片

这篇文章说服读者用定量的方法来做决策。它阐明了我是如何从优化的角度识别和思考问题的。最优化不仅仅是一个数学研究课题。如果运用得当,它可以用来解决不同学科的实际问题。

使用正确的技术,可以对问题进行建模,以最大化/最小化某个结果。

动机

管理科学是一种基于科学方法的决策方法。它大量使用定量分析。涉及定量决策方法的知识体系有各种各样的名称;除了管理科学,另外两个广为人知和接受的名称是 运筹学决策科学

二战后的两个发展导致了管理科学在非军事领域的发展和应用。

  1. 首先,持续的研究导致了许多方法上的发展。也许最重要的发展是乔治·丹齐格于 1947 年发现了求解线性规划问题的单纯形法。
  2. 与此同时,这些方法的发展正在发生;数字计算机引发了计算能力的虚拟爆炸。计算机使从业者能够使用先进的方法来解决各种各样的问题。

解决问题是一个过程,它能识别实际情况和期望情况之间的差异,然后采取行动解决这种差异。决策是指构建问题,然后分析它以选择一个替代方案。当我们经历很多成功时,很难理解使用数据做决策的必要性。我们的直觉(理解为自我)实际上淡化了运气在你做出的产生有利结果的决定中的作用。

数据科学教会了我在决策过程中量化的重要性。添加数字或量化结果的概率有助于减少情况的模糊性,并增加决策的一致性。如果没有适当的数据驱动分析,作为决策者,你很容易受到各种偏见和一厢情愿的想法的影响。

分析领域正以指数级的速度增长;AI(人工智能)、ML(机器学习)和深度学习等术语在科技行业很常用。就个人而言,在所有这些实践领域中,有一个经常不被重视的学科— 优化。我最近在研究一个优化问题,这是我们在计划一个活动或安排一个会议时经常遇到的问题。

排班问题

今年早些时候,我父亲被任命为我们地区一个本地演讲会的会长。国际演讲会是一个非营利性的教育组织,在全球范围内运营俱乐部,以促进交流、公众演讲和领导力。他的任务之一是为俱乐部的每个成员分配未来会议的角色。

在他担任主席期间,他将主持 27 次会议,俱乐部目前有 26 个成员(包括他自己)。在每次会议中,成员可以扮演不同的角色,例如“发言评估员”、“发言人”或“桌面主题主管”。为了成功地召开会议,必须给与会的不同成员分配 17 个角色。

照片由 NeONBRANDUnsplash 上拍摄

我父亲创建了一个 excel 电子表格,在表格中,他尽自己最大的努力以符合逻辑的方式给每个成员分配角色。在几次令人沮丧的尝试后,他让我看看他正在进行的杰作。从计划的角度来看,这不是一个不可能完成的任务。通过多次反复试验,我们将能够解决这个问题。然而,我的基本问题是,如果有另一个成员在他的任期内加入或有人退出。这个耗时的过程不得不重复。

我告诉他,我将热衷于开发一个模型,以改善他的俱乐部的名册。数学模型是任何定量决策方法的关键部分。

例如,如果构建一个自定义表需要 10 个小时, 10x 就是数学模型,它定义了构建 x 表所花费的总小时数。

模型

对我来说,这个练习的主要目的是开发一个公平的方法来分配每个人的角色,并最大限度地提高他们在赛季中的参与度。数学模型还必须遵循其他几个要求:

  1. 每个会议在给定的一天只能分配 17 个角色。
  2. 为了使这成为一个公平的模式,每个成员都必须有机会在赛季中至少扮演一次每个角色。
  3. 每个成员在会议中只能扮演一个角色。
  4. 会议中的每个角色都必须被分配。

下面给出的是制定演讲会最高参与度花名册问题的公式化。

陈述模型的决策变量

最优化问题的表述

用 Python 公式化模型

  1. 初始化决策变量 从上面的符号可以看出,决策变量是二进制的(即只能保存 0 或 1 的值)。每个变量决定了成员、日期(会议)和角色的不同组合的值。

在 Python 中初始化决策变量

例如,如果 x_10_2_3 的值为 1,则意味着第二次会议中的 10 号成员将执行 3 号角色。

2。定义目标函数 如前所述,该模型试图最大化俱乐部中每个成员的参与度

在 Python 中定义目标函数

3。设置约束 与任何优化问题一样,模型的约束通常需要最多的分析推理。我更喜欢将问题分解成一个玩具例子,并测试模型在应用特定约束时的行为。

在 Python 中设置约束

报表生成

定量分析过程的一个重要部分是根据模型的解准备报告。决策者必须容易理解报告的结果。它应该包括建议的决定和其他可能对决策者有帮助的有关结果的相关信息。

在解决了上面的排班模型后,我决定创建一个 excel 报表,打印出每个决策变量的值。在这个模型中有11934 个决策变量,这再次加强了使用编程解决这个问题的论点。

为了编程,我给变量“角色”分配了一个数值,并使用下表对其进行了映射。

表格显示角色 5 对应于会议中的第一个发言人

俱乐部中各种角色的映射表

打印模型的结果

从上面的 excel 报表快照中可以看到,值为 1激活了决策变量。这意味着成员 10 已经被指派为会议 10发言评估员 4 的角色。

为了让决策者更容易理解,我在 Excel 中创建了一个简单的数据透视表。

在这个数据透视表中,

  1. 被映射到成员。
  2. 映射到(会议)
  3. 单元格值映射到角色。

即将召开的会议的俱乐部名单

从上面的报告中可以看出,每个成员都有机会至少扮演一次每个角色。单元格值为 0 意味着该特定会议上的相应成员没有被分配角色。

结论

当我接近这篇文章的结尾时,下面给出了我在研究过程中获得的一些重要知识,我想总结一下,以供大家参考。

  1. 从上面的公式可以看出,类似的排班问题可以通过改变几个关键决策变量来建模和轻松解决。我强烈建议你自己去修改/测试代码。
  2. 我们可以添加额外的约束,比如由于一些个人原因,在特定的会议上给成员分配一个角色。或者,我们也可以将问题建模为没有成员在连续 3 次会议中处于空闲状态。
  3. 一个数学模型只取决于你的专业水平。编程的真正力量在于分析的可重复性和可扩展性。对我来说,手动创建另一个花名册会非常耗时。我现在可以在未来的任何时候重用这些代码,节省我的时间、精力和精力。

GitHub 链接,谢谢

谢谢你一直读到最后。我希望这篇文章能激励你用更定量的视角分析生活中的不同问题。

对于任何感兴趣的人,可以在我的 GitHub 资源库中找到代码。请随意下载并分析您的用例信息。

[## 有线服务/数据科学

使用 Python 中的纸浆包来制定和解决一个简单的排班问题打开 jupyter 笔记本安装…

github.com](https://github.com/wiredtoserve/datascience/tree/master/Rostering)

参考

[1]安德森、斯威尼、威廉姆斯、卡姆、科克伦、弗莱、奥尔曼。管理科学导论:决策的定量方法。2015 年第 14 版。Cengage 学习。第 2-8 页

Unsplash 上由Courtney hedge拍摄的照片

Rotoscoping:好莱坞的视频数据分割?

原文:https://towardsdatascience.com/rotoscoping-hollywoods-video-data-segmentation-7ca7c02b6ccc?source=collection_archive---------38-----------------------

在好莱坞,视频数据分割已经做了几十年。简单的技巧,如绿色屏幕的颜色键控可以大大减少工作。

2018 年末,我们开发了一个视频分割工具箱。视频剪辑中常见的一个问题是拍摄场景时,天空过饱和或太亮。电影中的大部分天空都被 VFX 专家取代了。这个任务叫做“天空替换”。我们认为这是引入自动分割来掩盖天空以进行进一步替换的完美起点。基于收集的经验,我将解释 VFX 和数据注释的相似之处。

您会发现我们构建的解决方案与当时被认为是最佳图像分割模型的 Deeplab v3+进行了比较。我们的方法(左)产生了更好的建筑物周围的细节,并大大减少了帧之间的闪烁。

我们的天空分割模型和 Deeplab v3+的比较

好莱坞的视频分割技术

在这一节中,我们将更仔细地看看彩色键控,例如绿色屏幕和旋转镜。

什么是色彩键控?

我很肯定你听说过彩色键控或绿色屏幕。也许你自己在使用 Adobe After Effects、Nuke、Final Cut 或任何其他软件编辑视频时也使用过这样的技巧。

我小时候自己做过很多视频剪辑。与朋友一起制作有趣的视频,并使用 after-effects 等工具添加酷炫的效果。没日没夜的看 videocopilot.com和 creativecow.com的教程。我记得我和一个朋友在我家后院玩木棍,只是为了几个小时后把它们换成光剑。

如果你不知道绿色屏幕是如何工作的,你可以在下面找到一个视频,它会给你一个比我用文字更好的解释。

解释绿屏如何工作的视频

本质上,绿屏使用的是颜色键控。镜头中的“绿色”被屏蔽了。这个遮罩可以用来混合另一个背景。美妙的是,我们不需要烧你的 GPU 的花哨的图像分割模型,而是一个相当简单的算法,寻找具有所需颜色的相邻像素进行遮罩。

什么是 rotoscoping?

你可以想象在许多好莱坞电影中,特效需要更复杂的场景,而不是简单地使用彩色背景来掩盖元素。想象一个场景,动物可能不喜欢强烈的颜色,或者有很多头发在风中飘动。简单的颜色键控方法是不够的。

但也是针对这个问题,好莱坞在多年前就找到了一种技术: Rotoscoping
为了让你更好地了解什么是 rotoscoping,我在下面嵌入了一个视频。该视频是一个关于如何使用 after effects 进行旋转观测的教程。使用一个特殊的工具箱,你可以在整个视频中的物体周围绘制样条线和多边形。工具箱允许帧之间的自动插值,为您节省大量时间。

旋转观测的后效教程

这项技术于 2003 年在 After Effects 中推出,已经问世近 20 年,并被许多 VFX 专家和自由职业者使用。

剪影与 After Effects one 工具完全专注于旋转观测形成对比。在这个视频中,你可以了解他们最新的产品更新

我为你挑选了一个例子来展示 rotoscoping 的结果有多详细。MPC Academy 的以下视频中的三个元素让我大吃一惊:运动模糊(T7)、头发的精细细节(T9)、帧一致性(T11)。当我们为 VFX 编辑开发一个产品时,我们了解到这个行业的质量要求超出了我们在图像分割方面的要求。在计算机视觉中,既没有数据集,也没有符合好莱坞标准的模型。

MPC 学院的旋转观测演示卷

在 YouTube 上搜索“roto showreel”,你会发现更多的例子。

VFX 的工具和工作流程以及数据标注惊人地相似。

质量检验比较

我们如何训练我们的天空分割模型

此外,我们在简单的背景前使用了公开的和无许可证的树、人和其他移动元素的视频。为了获得地面真实信息,我们简单地使用彩色键控。它非常有效,我们在几个小时内就完成了 5 分钟镜头的像素级精确分割。为了增加样本的多样性,我们使用了视频编辑工具,在移动摄像机的同时,裁剪掉部分视频。4k 原始视频有一个平滑移动的全高清帧。对于一些镜头,我们甚至打破了典型的二进制分类,使用平滑的边缘,在全黑和全白之间插入,作为我们的遮罩。通常,分割总是二进制的,黑色或白色。当场景模糊时,中间有 255 种颜色。

颜色键控允许我们获得复杂场景的地面真实数据,如树叶或头发。下面的棕榈树图片已经使用简单的颜色键控进行了遮罩/标记。

这适用于所有种类的树。甚至帮助我们获得了整个视频的良好效果。我们能够在剪辑过程中简单地调整颜色键控参数。

为了让你对我们的色彩键控实验的时间结果有一个概念,请看下面的 gif。注意有一点苦涩。我们特意添加了这个来“模拟”用你手中的相机进行录制。摄像机本身的移动是整个场景上作物的简单线性插值。所以你在下面看到的只是全景的一部分。

训练模型

时间一致性的对抗性训练

因此,对于您的下一个视频数据分割项目,您可能想看看是否可以使用这些技巧来收集数据并节省大量时间。在我的其他帖子中,你会发现一个数据注释工具列表。如果你不想在手工注释上花费时间,这里还有一个数据注释公司的列表

我要感谢和我一起做这个项目的莫莫和黑基。另外感谢所有 VFX 艺术家和工作室的反馈和富有成效的讨论。

原载于 2020 年 4 月 23 日 https://data-annotation.com**

在 R 中映射地理空间数据的 3 个简单步骤

原文:https://towardsdatascience.com/route-66-revisited-mapping-geospatial-data-in-r-371dd406cde0?source=collection_archive---------56-----------------------

1)获取 API 2)安装库 3)图表

我总是想知道人们是如何用 r 语言创建漂亮的地理空间可视化的。我会瞪大眼睛看着完美的渐变,这些渐变可以让仪表板或演示文稿流行起来,我只是想知道人们到底是如何用我与 p 值和回归线相关联的编程语言来做到这一点的。

令人惊讶的是,您实际上只用几行代码就可以完成这一点——今天我将向您展示如何做到这一点。

第一步:访问美国社区调查(ACS)API

首先,你需要访问人口普查局的 API。前往此链接请求 API 密钥。大约 10 秒钟后,你应该会收到一封电子邮件,里面有你的密钥和一个链接,可以确认你确实是人类。把你的 API 密匙藏在某个地方——你每次重新打开 r 时都需要调用它。

为了正确读取 API,我们需要安装 3 个库:“acs”、“choroplethr”和“choroplethrMaps”。我们还将安装“RColorBrewer”和“ggplot2”用于稍后的绘图。

choropleth 地图(源自希腊语χῶρος的“区域/地区”和πλῆθος的“大众”)是一种专题地图,其中区域按照统计变量的比例进行阴影化或图案化,该统计变量表示每个区域内地理特征的汇总,如人口密度或人均收入。— 维基百科

下面是您需要输入到 R 中以使 API 工作的代码:

第二步:选择表 ID

一旦你安装了 API 密匙,你就可以在data.census.gov找到可用的数据集进行实验(我在教程中使用的是表格 B19301,各县的人均收入)。当您找到想要使用的数据时,记下表 ID。创建一个漂亮的 choropleth 图只需要表 ID。

确定数据集后,让我们开始绘图吧!

步骤 3:映射地理空间数据

下面我有按县绘制美国人均收入图表的代码。这只是可以切换的一瞥;如果你有一个我没有举例说明的修改,关于 county_choropleth_acsggplot2 Colors 的 R 文档将带你完成剩下的部分。

  • 一些 ACS 数据集包含空值;最丰富多彩的图表将是那些他们的大部分观察呈现!

……以及相关图表:

\ 1:默认值\ 2:自定义标题\ 3:渐变+自定义标题\ 4:颜色变化+自定义标题\

奖金:州&地区仅限

改变我们的图表来关注一个州或地区就像添加一个额外的输入一样简单,即state_zoom()函数。为了一次绘制多个州(而不是整个国家)的图表,我们将把我们想要一起查看的所有州连接起来。

对于最后两张图,我使用了 ggplot2 提供的一些有趣的调色板。这个库的scale_fill_brewer()函数包含了一组预定的调色板,你可以在这里找到。

…。这里有四个相关的图表:

结论

现在您已经知道如何在 r 中创建简单的地理空间可视化。)你想调查。您可以按国家、州或部分州来绘制图表。最后,您可以更改调色板、渐变和图形标题。

这只是地理空间的冰山一角,但我希望它可以让你开始。感谢您的阅读,祝您工作愉快!❤️

GIF 来自 GIPHY

行或列—我应该将索引放在哪里?

原文:https://towardsdatascience.com/rows-or-columns-where-should-i-put-my-index-on-65d429692dee?source=collection_archive---------12-----------------------

为 SQL Server 工作负载选择最佳索引策略是最具挑战性的任务之一。了解与传统的 B 树结构相比,在哪些场景中使用列存储索引会使您受益

照片由 Nick Fewings 在 Unsplash 上拍摄

为 SQL Server 工作负载选择最佳索引策略是最具挑战性的任务之一。您可能知道,索引可以极大地提高查询的性能,但同时,当涉及到维护时,它们会导致额外的开销。

老实说,我永远不会称自己为索引专家。然而,我想分享我从最近的项目中获得的经验,因为它为我打开了一个全新的视角,我认为它也可以对其他人有益。

首先,直到几个月前,我还从未使用过列存储索引,因为我公司的工作环境是基于 SQL Server 2008R2 的。我有关于列存储索引的理论知识,也知道它们和传统 B 树索引的区别,但我从未在现实中尝试过。

但是,首先要做的是…

什么是列存储索引?

与 rowstore 类型的数据存储相反,columnstore 在表的列级别上操作,rowstore 类型的数据存储在物理上以行的格式存储数据。它最初是在 SQL Server 2012 中引入的,后来在 SQL Server 的每个新版本中都得到了改进。传统的行存储索引(我将它们称为 B 树索引)存储每一行的键值,以便 SQL Server 引擎可以使用这个键来检索行数据,而列存储索引则分别存储每个表列!

使用 Columnstore 索引的主要原因是它的高压缩率!这在内存占用方面带来了显著的好处,因此,如果使用得当,性能会更好。

网上确实有很多很棒的资源供学习关于列存储索引的架构,并且微软的文档在这个主题上也相当全面,但是我想展示一些使用列存储索引有意义的真实例子。

只是强调一下,我将专门使用聚集列存储索引(非聚集列存储索引不在本文讨论范围之内)。

对于所有的例子,我都使用堆栈溢出数据库。

爬上一棵 B 树

让我们首先在 Posts 表上运行几个简单的查询,这个表有 1700 多万条记录,只是为了对数据有一个感觉。一开始,我在这个表上没有任何索引,除了主键列上的聚集索引。

我的目标是找到 2010 年上半年所有浏览量超过 3000 的帖子:

SELECT * 
FROM dbo.Posts P
WHERE CreationDate >= '20100101'
    AND CreationDate < '20100701'
    AND ViewCount > 3000

这个查询返回了 88.547 行,执行起来花了一分多钟!

由于该表上不存在索引,SQL Server 必须扫描整个表来满足我们的请求,执行大约 420 万次逻辑读取。让我们稍微帮助一下我们可怜的 SQL Server,在 CreationDate 列上创建一个非聚集索引:

CREATE NONCLUSTERED INDEX [ix_creationDate] ON [dbo].[Posts]
(
    [CreationDate] ASC
)

现在,当我再次运行完全相同的查询时,我在 9 秒钟内得到了我的结果,但是逻辑读取的数量(560 万)表明这个查询还远远不够好。SQL Server 感谢我们的新索引,因为它用于缩小初始搜索的范围。但是,选择所有列显然不是一个好主意,因为 SQL Server 必须从聚集索引中选取所有其他列,执行大量的随机读取。

现在,我会问自己的第一个问题是:我真正需要什么数据?我需要正文、关闭日期、最后编辑日期、等吗??好了,我将重写查询,只包含必要的列:

SELECT P.Id AS PostId
    ,P.CreationDate
    ,P.OwnerUserId AS UserId
    ,P.Score
    ,P.ViewCount
FROM dbo.Posts P
WHERE P.CreationDate >= '20100101'
    AND P.CreationDate < '20100701'
    AND P.ViewCount > 3000

我们得到了完全相同的执行计划,逻辑读取次数减少了(400 万次),因为返回的数据量减少了。

SQL Server 建议在我们的谓词列(WHERE 子句中的列)上创建索引,并在索引中包含其余的列。让我们遵从 SQL Server 的意愿,修改我们的索引:

CREATE NONCLUSTERED INDEX [ix_creationDate_viewCount] ON [dbo].[Posts]
(
    [CreationDate],
    [ViewCount]
)
INCLUDE ([OwnerUserId],[Score])

现在,当我运行我的查询时,它在不到一秒的时间内执行,仅执行 3626 次逻辑读取!哇!因此,我们创建了一个很好的“覆盖”索引,它非常适合这个查询。我特意将“针对此查询”部分加粗,因为我们无法为针对数据库运行的每个查询创建覆盖索引。在这里,它可以用于演示目的。

列存储索引开始发挥作用

好了,我们不能再优化之前的查询了。现在让我们看看 columnstore index 将如何执行。

第一步是创建 dbo 的副本。Posts 表,但是我将在这个新表(dbo)上创建一个聚集列存储索引,而不是使用 B 树索引。Posts_CS)。

CREATE CLUSTERED COLUMNSTORE INDEX cix_Posts
ON dbo.Posts_CS

您可能注意到的第一件事是这两个相同的表在内存占用方面的巨大差异:

dbo 的内存占用。帖子表

dbo 的内存占用。帖子 _CS 表

因此,一个包含聚集列存储索引的表比一个包含 B 树索引的表消耗的内存要少 4 倍!如果我们也考虑非聚集索引,这种差异只会变得更大。正如我已经提到的,数据在列级别上压缩要好得多。

现在,让我们在新创建的 columnstore 索引表上运行完全相同的查询。

SELECT P.Id AS PostId
    ,P.CreationDate
    ,P.OwnerUserId AS UserId
    ,P.Score
    ,P.ViewCount
FROM dbo.Posts_CS P
WHERE P.CreationDate >= '20100101'
    AND P.CreationDate < '20100701'
    AND P.ViewCount > 3000

列存储索引中的数据存储在段中。因此,根据表中的数据分布,SQL Server 必须读取更多或更少的数据段才能检索到请求的数据。

如上图所示,为了返回 88.547 条记录,SQL Server 遍历了 26 个数据段,跳过了 72 个数据段。这是因为我们的列存储索引中的数据没有按照任何特定的顺序排序。比方说,我们可以按 CreationDate 对其进行排序(假设我们的大多数查询将使用 CreationDate 作为谓词),在这种情况下,性能应该会更好,因为 SQL Server 将确切地知道在哪些数据段中查找数据,哪些数据段可以被跳过。

现在,让我们一起运行这两个查询,并比较查询成本:

传统的 B 树索引查找成本为 3.7,而列存储扫描成本为 10.7。很明显,因为我们有一个完美匹配的非聚集索引,它覆盖了我们需要的所有列。还是那句话,差别没那么大。

添加更多配料…

但是,假设过了一段时间后,我们需要扩展我们的输出列表,并为 LastActivityDate 检索数据。让我们看看会发生什么:

哎呀!!!通过只添加一列,结果完全变得有利于 columnstore 索引。现在,B 树非聚集索引没有所有必要的数据,它需要从聚集索引中提取last activity date——这使得这个查询的成本上升到 236!另一方面,columnstore index 变得稍微贵了一点,现在要 14!

当然,正如您可以在上面的图片中注意到的,SQL Server 要求另一个索引(或扩展现有的索引),但这就是我在上面强调的——您不应该盲目地服从 SQL Server 的所有愿望,否则您将完成“过度索引”的表!

运行分析查询

根据定义,在运行分析查询时,列存储索引应该处于领先地位。因此,让我们在下面的场景中检查一下:我想检索在 2010 年上半年注册、在 2010 年和 2011 年发帖的用户,并且用户的总体声誉大于 3000,各个帖子的浏览量超过 3000……我还需要查看用户的位置显示名称。听起来很复杂,但实际上并不复杂:)

以下是查询:

SELECT U.Id
    ,U.Location
    ,U.DisplayName
    ,P.CreationDate
    ,P.Score
    ,P.ViewCount
FROM dbo.Users U
    INNER JOIN dbo.Posts P ON U.Id = P.OwnerUserId 
WHERE U.CreationDate >= '20100101'
    AND U.CreationDate < '20100701'
    AND U.Reputation > 3000
    AND P.ViewCount > 3000
    AND P.CreationDate >= '20100101'
    AND P.CreationDate < '20120101'

该查询返回 37.332 行,我们希望对 SQL Server 有所帮助,在 Users 表的 CreationDate 列上创建一个非聚集索引。

CREATE NONCLUSTERED INDEX [ix_creationDate] ON [dbo].[Users]
(
    [CreationDate]
)

当我运行查询时,SQL Server 附带了以下执行计划:

因为我们的索引没有覆盖所有必要的列,所以 SQL Server 认为对 Users 表执行扫描比执行索引搜索和昂贵的键查找更便宜。这个查询花费 58.4。

现在,我将创建一个 Users 表(Users_CS)的副本,并在其上创建一个聚集列存储索引:

CREATE CLUSTERED COLUMNSTORE INDEX cix_Users
ON dbo.Users_CS

现在让我们同时运行我们的查询的 bot,并比较性能:

同样,带有 columnstore 索引的表很容易胜过带有 B 树索引的原始表。第二次查询的费用是 9.2!请记住,我们甚至没有优化 columnstore 索引本身(在插入过程中,我们没有对数据进行排序)!

最后一个例子来自我的真实项目,我们在实际工作负载上比较了 columnstore 和 B 树索引的性能。查询本身非常简单:我想汇总今年 11 日到 7 月底每个客户的存款总额:

SELECT customerID
    ,SUM(amount) total
FROM factDeposit
WHERE isSuccessful = 1
    AND datetm >='20200101'
    AND datetm < '20200801'
GROUP BY customerID

SELECT customerID
    ,SUM(amount) total
FROM factDeposit_cs
WHERE isSuccessful = 1
    AND datetm >='20200101'
    AND datetm < '20200801'
GROUP BY customerID

结果如下:

同样,columnstore 索引以 4.6 比 26 的查询开销令人信服地“胜出”!

那么,我为什么要使用 B 树索引呢??!!

在你陷入不再需要传统的 B 树索引的陷阱之前,你应该问自己:陷阱在哪里?很明显,问题就在这里,因为 B 树索引在大多数数据库中仍然被大量使用。

列存储索引的最大缺点是更新/删除操作。被删除的记录并没有真正被删除,它们只是被标记为已删除,但是它们仍然是列存储索引的一部分,直到索引被重建。更新的性能甚至更差,因为它们是作为两个连续的操作执行的:删除,然后插入…插入“本身”不是问题,因为 SQL Server 将它们保存在名为 Deltastore 的结构中(顺便说一下,该结构具有 B 树结构),并对列存储索引执行大容量加载。

因此,如果您经常执行更新和/或删除,请注意您不会从列存储索引中获得最大的好处。

所以,正确的问题应该是:

什么时候应该使用 B 树索引,什么时候应该使用列存储索引?

答案是,正如 SQL Server 内部 99%的争论一样——这要看情况!

寻找合适的工作负载

关键的挑战是确定最适合使用列存储和行存储索引的场景,或者更好地说是工作负载

以下是针对每种索引类型的最佳实践用法的一些建议:

  • 对不经常更新/删除的大型表(至少有几百万条记录)使用列存储索引
  • 列存储索引在静态数据上表现最好,例如在 OLAP 工作负载中,有许多查询只是从表中读取数据,或者定期大容量加载新数据
  • 列存储索引在扫描和执行大数据范围的聚合(执行求和、AVG、计数等)方面表现出色,因为它们能够一次处理大约 900 行,而传统的 B 树索引是逐个处理的(直到 SQL Server 2019,它为基于行的工作负载添加了批处理模式)
  • 当表被频繁修改(更新、删除、插入)时,在高事务性工作负载上使用 B 树索引
  • b 树索引通常在具有高选择性的查询中性能更好,例如,当您返回单个值或少量值时,或者如果您查询小范围的值( SEEK ing for a value)

如果说应该在 OLAP 工作负载中使用列存储索引,而在 OLTP 环境中使用 B 树索引,这未免过于简单化了。为了得到这个问题的正确答案,你应该问自己: 什么样的查询最常用于特定的表? 只要你得到这个问题的答案,你就能为自己的工作量定义合适的索引策略。

最后,如果你想知道是否可以从两个世界中取长补短:答案是——可以!从 SQL Server 2016 开始,可以在同一个表上结合 columnstore 和传统的 B 树索引!

然而,这是一个独立而复杂的主题,需要认真的规划和各种考虑,超出了本文的范围。

感谢阅读!

RPA 趋势:这是我们今年可以期待的

原文:https://towardsdatascience.com/rpa-trends-this-is-what-we-can-expect-this-year-58ad6ac0e84?source=collection_archive---------15-----------------------

2020 年,方向将朝着数字化、自动化、机器人过程自动化(RPA)和超自动化,即 RPA 与人工智能(AI)和机器学习的结合。这意味着激动人心的变化正等待着我们,可能是由 RPA 和全球经济环境驱动的。

许多公司将会发现,只有在人类劳动和辅助软件机器人之间的智能交互中,业务增长才有可能。此外,与以往任何时候相比,政治家们将不得不解决关于工作日益自动化的经济和社会后果的讨论。

因此,我们有必要更仔细地了解一下自动化的各个趋势。2020 年的总体长期趋势和短期发展是有区别的。

超自动化和机器人仍然是长期的潮流引领者

考虑到软件行业的全球发展,在我看来,目前有一个明显的领导者。根据 Gartner 的数据,2018 年机器人过程自动化(RPA)软件的销售额增长了 63.1%,达到 8.46 亿美元,成为全球企业软件市场增长最快的领域。

Gartner 最近发布了 2020 年前 10 大战略技术趋势,其中三个-超自动化、自主事物和人工智能安全-与自动化领域直接相关。

超自动化

根据 Gartner 的说法,“没有任何单一的工具可以取代人类。今天的超自动化是工具的组合,包括自动化流程(RPA)、智能企业管理软件(iBPMS)和人工智能——目标是人工智能驱动的决策。虽然这不是主要目标,但高度自动化通常会导致创建组织的数字双胞胎(DTO),使组织能够可视化功能、流程和关键绩效指标如何相互作用以增加价值。然后,DTO 成为高度自动化过程中不可或缺的一部分,提供有关组织的连续、实时信息,提供重要的商业机会。

自主的事物

虽然今天的物联网倾向于由静态对象组成,但明天它也将由自主行动和在很大程度上独立移动的机器人组成。“自主事物,包括无人机、机器人、船只和设备,使用人工智能来执行通常由人类执行的任务。这项技术的智能范围从半自动到全自动,可用于各种环境,包括空中、海上和陆地。虽然自主事物目前主要存在于受控环境中,如矿井或仓库,但它们最终将演变为开放的公共空间,”Gartner 继续说道。

人工智能安全

自动化带来的不断扩大的机会也带来了新的风险。根据 Gartner 的说法,“高度自动化和自主化等不断发展的技术为商业世界的转型提供了机会。但是它们也产生了新的潜在安全漏洞。安全团队必须应对这些挑战,并了解 AI 如何影响安全领域。对于人工智能安全来说,有三个重要的观点:

  • 人工智能驱动系统的保护:人工智能训练数据、训练管道和 ML 模型的保护。
  • 使用人工智能来提高防御能力:通过使用人工智能,可以理解模式,检测攻击,部分网络安全流程可以自动化。
  • 预测攻击者对人工智能的恶意使用:检测攻击并防御它们。"

今年我们可以期待什么

除了这些中期趋势之外,还可以预测进一步的短期变化,这些变化很可能在 2020 年发生。

全球经济衰退成为自动化的驱动力

与去年相比,经济信号没有改善。因此,今年的特点也将是低利率和贸易战的威胁,资本投资下降和多重政治不稳定。如果这些信号传到消费者那里,新一轮的全球衰退可能很快就会到来。以前只是简单地裁员,现在公司的反应更加明智。特别是在经济困难时期,他们可以利用自动化,扩大软件机器人的使用。

RPA 正在成为“自动化的 YouTube”

RPA 有可能在今年成为中央自动化平台。正如 YouTube 成为视频内容的焦点一样,RPA 可以成为中央自动化存储库。与此同时,自动化程序的有用性和可重用性不断增加,使得它们更容易转移到新的领域。例如,不同行业和地区的公司将使用相同的代码。这一趋势预计将大大简化和扩展 RPA 的总体使用。

机器人的标准化和组合

公司还会发现,标准化的机器人适合不同部门的不同用途,但也可以用于不同行业和跨公司边界。机器人将因此离开它们以前孤立的操作区域。它们的使用将变得更加可预测和可扩展。只有这样,RPAs 的优势才能得到充分发挥。然而,越是简单的标准任务被自动化,因此越不容易出错,消除剩余问题就变得越复杂。公司应该用站点可靠性工程来抵消这一点,在站点可靠性工程中,IT 操作被认为是用软件工程解决的软件任务。

年轻学者将给自动化带来新的动力

现在进入就业市场的年轻学者对传统流程的转型和现代化有着更广泛的兴趣和理解,而在过去,这些老员工一次又一次地失败了。他们会问一些让人不舒服的问题,比如为什么某些事情多年来都是用这种方式解决,而不是用其他任何方式,意思是更好。他们可能已经在学校学到了某些过程的自动化,但肯定是在学习期间,并且已经将这些融入到他们的私人生活中。因此,他们也将开始实现部分工作流程的自动化。他们这样做的效果和效率越好,旧的担忧就能越快得到克服,公司也能越快相信变革的必要性。这些年轻的、积极的自动化专家可以在公司内部形成一种任务团队,定义公司范围的自动化战略,并将其贯彻到各个部门。同时,它们将是 RPA 提供商的理想界面。

机器智能会让所有人大吃一惊

人工智能和机器学习的潜力远远没有被耗尽。昨天显然只能靠人类智慧解决的问题,明天将由软件解决。正如电子电路的复杂性在给定的时间内增加了一倍(摩尔定律),人工智能也将呈指数增长,这将带来许多惊喜。特别是因为这种增长将补充量子计算机领域的进一步创新。量子比特的数量正在有规律地增长,并开辟了许多新的应用可能性。看似坚定的边界会倒下。

爱国军正在进入世界政治和经济舞台

自动化的后果将在今年影响到社会的大部分。RPA 将成为纽约联合国和达沃斯世界经济论坛的一个议题。它还关系到工作和公平的报酬。各个国家也可能对自动化的社会后果产生更大的兴趣。最晚当越来越多的机器人在经济衰退的情况下接管许多工作时,RPA 将成为政治议程的重中之重。在当前的分析中,Forrester 还预测了劳动力市场的重组。根据这项分析,不到 4%的现有办公室工作将会消失。相反,全球可能会创造 30 万个新工作岗位,这些岗位的所有者应该具备直觉、同情心和相当大的灵活性。机器人无法替代的素质。

RPA 行业将继续整合

2020 年,大型和已有的 RPA 供应商将合并新的、较小的竞争对手。像 UiPath 这样的供应商将不会被收购,因为它们的市场估值很高。这也适用于它们被大公司俘获的情况。这些公司将通过收购规模较小的供应商,将其 RPA 专业知识带入集团内部。已经可以观察到,全球软件服务提供商和技术提供商正在收购自动化行业的公司,这一趋势可能会持续下去。

为 2020 年及以后做出正确的决策

这些趋势的总体情况表明,跟上 RPA 和 AI 领域的发展步伐是多么复杂。因此,为了获得决定性的竞争优势,公司了解这些趋势并相应地调整其战略就变得更加重要。RPA 让各行各业各种规模的公司都有可能制定自己的数字化转型战略。UiPath 使用大量案例研究来证明“自动化第一”理念对于公司生存的重要性。即使今年许多有形的自动化趋势是可预见的,但决定性因素将是每个企业为做出充分反应所能贡献的创新力量。

图片经由 Pixabay

RPubs:添加到简历中的快速项目

原文:https://towardsdatascience.com/rpubs-fast-projects-to-add-to-your-resume-778d69064493?source=collection_archive---------43-----------------------

如果你以前没有数据科学方面的工作,而你正在试图获得一份工作,那么在招聘人员眼中证明你自己的最好方式就是有一个前端工作的项目。虽然一篇研究论文或另一个 Jupyter 笔记本可能有复杂和/或技术上有趣的工作,但它并没有真正吸引人们的注意力。如果你的工作是交互式的,那么敬业度会在你的简历上大大提升。不管你是 10 岁还是 50 岁的博士,当你遇到一个交互式的图表时,你都会点击它。

如果你对 R 有点熟悉,这篇文章会对你有用,让你的作品具有交互性,并发布到网上,让其他人也能看到。

r 降价

R Markdown 是 Markdown 的一个扩展,它可以帮助您创建格式整洁的 pdf、html 文档、word 文档等等。Markdown 的原理相对简单,通过查看这个链接,你可以在大约 5 分钟内学会如何创建表格、插入图片和链接、设置文本样式以及设置标题。

R Markdown 稍微复杂一点。要在 R Studio IDE 中创建 R Markdown 文档,请转到文件>新建文件> R Markdown。这将把我们带到下面的窗格,在这里您可以命名您的文档,并选择您的文档类型。现在,把它作为一个 HTML 文档,然后点击“OK”按钮。

您现在应该有一个类似下图的脚本。在文件的顶部是一些 YAML,它给出了文件的一些信息。你可以在空白处写下你的减价,你也可以用普通的 HTML 来写,因为我们将呈现一个 HTML 文档。灰色区域用反斜杠括起来,它与大多数键盘上的波浪号共用同一个键。在第一组反勾号之后,你写下你正在使用的编程语言(在我们的例子中,我们使用的是 R,但是也可以选择另一种),然后是代码块的名称。

可以为代码块配置附加选项。常见的有:

  • include阻止代码和结果出现
  • echo阻止结果出现
  • warning阻止代码生成的警告出现
  • background改变块的背景颜色
  • fig.widthfig.height以英寸为单位控制数字的大小

最后,为了在代码顶部呈现生成的文档,在 Knit > Knit to HTML 上单击下拉箭头。这将为您提供所创建文档的视图。接下来,我们将为这个文档创建交互组件,然后在线发布它。

Plotly

Plotly 将帮助我们用最少的代码渲染具有很多特性的迭代图。要开始使用 plotly,请在您的控制台中运行install.packages("plotly"),并将library(plotly)放在您的某个代码块中(我推荐第一个代码块)。要创建一个图,您将使用 plot_ly 函数。下面我运行了plot_ly(data = iris, x = ~Sepal.Length, y = ~ Petal.Length, color = ~Species)并生成了下面的图表,它能够创建截图,使用工具提示,突出显示数据点,并调整轴。

虹膜数据集的散点图

尝试重新渲染文档,您可以在输出中看到绘图。如果您已经在使用 ggplot,但不想转换为 plotly,安装 ggplotly 包,并将 ggplots 包装在ggplotly()函数中,以获得现有 plotly 功能。要查看更多您可能想要生成的示例,请查看plotly.com/r/

高价租船合同

尽管 highcharter 不能免费用于商业目的,但它可以用来创建许多不同风格的交互式图表。Highcharter 有助于开发比 plotly 更干净、不那么繁忙的地块。

使用如下的 hchart()函数,我们可以看到一个条形图形式的 diamonds 数据集。

hchart(diamonds$clarity, colorByPoint = TRUE, name = "Clarity")

钻石数据集的高查特条形图

像 plotly 一样,highcharter 的工具提示使其更有用,因为您可以看到这些条形图的准确高度或散点图上某个点的准确位置。有关 highcharter 的更多示例,我建议您查看 highcharter 展示区

传单

传单是我个人最喜欢的映射包,开始你需要一个地理空间数据集和对 r 中管道操作符的理解。管道操作符把左边的对象作为左边函数的第一个参数。因此,以下内容是相同的:

data %>% func1() %>% func2()

func2(func1(data))

这实际上是我更喜欢 R 代码的编写方式,因为从左到右阅读要容易得多。您的代码采取了明确的步骤,并且您不必花费大量时间来查看特定参数处的嵌套函数。

活页地图是通过向管道链添加额外的功能来创建的,最终形成非常可定制的地图,这些地图可以具有多层影像、迷你地图、定制标记、路径和定制区域。如果您想快速浏览一下,我的文章用不到 15 行代码用 R 语言制作交互式地图是一个很好的起点,下面我将展示如何生成地图。

数据表

在大多数情况下,您希望以可视的格式呈现数据,因为这样更容易理解。如果您想要显示一个表,那么您应该使用 DT 包以交互方式来完成。下面的代码生成了下面的屏幕截图。

datatable(iris)

DT 包很棒,因为它使您能够在页面中显示表格数据(最小化空间),允许您搜索数据,并允许您根据特定的列对数据进行排序。这些是查看你的表的人可能想要的特性,DT 给了他们这些功能,并且是同一个表的截图的一部分。

RPubs

现在您已经对可以制作的交互式可视化类型有了一些概念,让我们来看看如何将您的文档发布到 RPubs。首先编织您的文档,就像您在过去的步骤中可能一直在做的那样来呈现它。渲染完成后,单击窗格右上角的发布按钮,这将生成下面的弹出窗口。

点击 RPubs,然后注册一个账户。注册后,再次单击发布按钮。值得注意的是,RPubs 上的每一个文件都是公开可见的,所以不要分享任何不应该分享的东西。

建议

虽然这不是你项目的一个非常重要的前端,但它比一个没有交互性的纯文本项目要好。希望这能在很短的时间内给你的投资组合增加一些东西。由于这是一个容易创建的项目,我的建议(如果你正在找工作)是对与你想工作的行业或公司相关的数据集进行分析。例如,如果你对房地产感兴趣,搜索住房数据集,如果你对为 Spotify 工作感兴趣,使用他们的 API 来获取数据。

RStudio addins,或者如何让您的编码生活更简单

原文:https://towardsdatascience.com/rstudio-addins-or-how-to-make-your-coding-life-easier-6b627c7b2240?source=collection_archive---------10-----------------------

发现最好的 RStudio 插件,如何在实践中使用它们,以及它们如何在用 R 或 R Markdown 编写代码时帮助您

萨法尔·萨法罗夫拍摄的照片

什么是 RStudio addins?

答虽然我已经使用 RStudio 好几年了,但我最近才发现 RStudio 插件。从那以后,我几乎每次使用 RStudio 都在使用这些插件。

什么是 RStudio addins?RStudio 插件是一些扩展,它们为从 RStudio 中执行高级 R 函数提供了一种简单的机制。更简单地说,当执行一个插件(通过单击插件菜单中的一个按钮)时,相应的代码被执行,而您不必编写代码。如果仍然不清楚,请记住,对于在 RStudio 中导入数据集的,您有两种选择:

  • 通过编写代码导入它(例如,感谢read.csv()函数)
  • 或者,您可以通过单击环境窗格中的“导入数据集”按钮来导入它,设置导入设置,然后单击“导入”

RStudio 加载项与“导入数据集”按钮非常相似,但具有其他常见功能。因此,您可以编写代码,就像您可以通过编写代码来导入数据集一样,但是由于 RStudio 插件,您可以在不实际编写必要代码的情况下执行代码。通过使用 RStudio 插件,RStudio 将为您运行所需的代码。RStudio 插件可以简单到插入常用代码片段的函数,也可以复杂到接受用户输入以绘制图形的闪亮应用程序。RStudio 插件的优势在于,与您自己编写代码相比,它们允许您更轻松地执行复杂和高级的代码。

我相信 addins 值得所有 R 用户去尝试。初学者将有可能使用他们本来不会使用的功能,因为代码太复杂,而高级用户可能会发现它们在某些情况下有助于加快代码的编写。有关 R 中的其他提示,请参见文章“R studio 和 R Markdown 中的提示和技巧”。

装置

RStudio 加载项作为 R 包分发。所以在能够使用它们之前,你需要安装它们。你可以像安装软件包一样安装一个插件:install.packages("name_of_addin")。一旦你安装了包含插件的 R 包,它将通过顶部的插件菜单立即在 RStudio 中可用。

RStudio 加载项工具栏

艾丁斯

如果您仍然不相信,请参见下面我认为最有用的插件列表,以及下面几节中的具体示例。

请注意,此列表并不详尽,根据您在 RStudio 上进行的分析类型,您可能会发现其他列表也很有用。欢迎在文章末尾发表评论,让我(和其他读者)知道你认为值得使用的插件。

埃斯奎塞

是一个由法国 dreamRs 公司开发的插件。他们是这样定义的:

该插件允许您通过用 ggplot2 包可视化数据来交互式地浏览数据。它允许您绘制条形图、曲线、散点图、直方图、箱线图和 sf 对象,然后导出图形或检索代码以再现图形。

有了这个插件,你可以很容易地从[{ggplot2}](https://www.statsandr.com/blog/graphics-in-r-with-ggplot2/) 中创建漂亮的图形,对我来说最好的部分是你可以检索代码来复制图形。与默认的{graphics}包相比,{ggplot2}包的图形看起来确实更好,但是代码也更长更复杂。有了这个插件,您可以通过在一个用户友好的交互式窗口中拖放感兴趣的变量来从{ggplot2}包中绘制图形,然后在您的脚本中使用生成的代码。

为了便于说明,假设我们想要创建数据集iris的变量Sepal.LengthPetal.Length的散点图,并用变量Species给点着色。为此,请遵循以下步骤:

  1. 加载数据集并重命名为: 1
dat <- iris

2.安装组件{esquisse}。这必须只做一次

install.packages("esquisse")

3.从 RStudio Addins 菜单中打开“ggplot2”生成器:

步骤 3:从 RStudio 插件菜单中打开“ggplot2”构建器

4.选择您想要处理的数据集(在本例中为dat,并在检查观察值和变量的数量是否正确后点击“验证导入的数据”(绿框):

步骤 4:选择数据集并验证导入的数据

5.将感兴趣的变量拖放到相应的区域。在这种情况下,我们将绘制变量Sepal.LengthPetal.Length以及基于变量Species的色点的散点图:

步骤 5:将变量拖放到相应的区域

6.点击窗口右下方的“>导出&编码”。您可以复制代码并将其粘贴到脚本中您想要放置的位置,也可以单击“在脚本中插入代码”将代码放置到脚本中光标所在的位置:

步骤 6:检索代码,以便在脚本中使用

如果选择第二个选项,代码应该出现在光标所在的位置。许多不同的选项和定制是可能的(例如,轴标签、颜色、图例位置、主题、数据过滤等。).为此,使用位于窗口底部的按钮(“标签和标题”、“绘图选项”和“数据”)。您可以在窗口中立即看到更改,当情节符合您的需要时,将代码导出到您的脚本中。我不会详细讨论不同类型的图和定制,但是一定要通过移动变量和定制来尝试其他类型的图,看看有什么可能。

提问者

{questionr}插件在调查分析和处理因素变量时非常有用。有了这个插件,你可以很容易地重新排序和重新编码因子变量。得益于cut()函数,该插件还允许轻松地将数字变量转换为因子(即,对连续变量进行分类)。像其他插件一样,安装完{questionr}包后,你应该会看到它出现在顶部的插件菜单中。从加载项下拉菜单中,选择是否要对因子变量进行重新排序或重新编码,或者对数值变量进行分类。

记录因素

我们可以使用{questionr}插件,而不是从{dplyr}包中编写recode()函数。

对于这个例子,假设我们想要重新编码Species变量以缩短因子的长度,然后将这个新变量存储为Species_rec:

步骤 1:选择要重新编码的变量和重新编码设置

步骤 2:指定新因素的名称

步骤 3:根据列联表检查结果,并使用顶部的代码

重新排序因素

类似于重新编码,我们可以通过{questionr}插件对因子进行重新排序。假设我们想对Species变量的 3 个因子重新排序,顺序是versicolor然后是virginica最后是setosa。这可以通过以下方式完成:

步骤 1:选择要重新排序的变量和新的变量名

第二步:指定你想要的顺序

步骤 3:使用脚本顶部的代码

对数字变量进行分类

{questionr} addin 也允许将一个数字变量转换成一个分类变量。这通常是为年龄而做的,例如,当年龄被转换成年龄组时。对于这个例子,假设我们想要创建变量Sepal.Length的 3 个类别:

步骤 1:选择要转换的变量和新的变量名

步骤 2:将休息次数设置为 3

(自己尝试其他切割方法,直接在窗口中查看结果。)

第三步:根据底部的柱状图检查结果

步骤 4:使用脚本中的代码

治疗

如果你经常用 R Markdown 写作,那么{remedy}插件将会极大地方便你的工作。该插件允许您添加粗体,创建列表,网址,斜体,标题(H1 到 H6),脚注等。以一种有效的方式。我认为直接使用代码可以更快地完成这些任务,而不是通过 addins 菜单然后选择您想要的转换。然而,就我个人而言,我不可能记住所有转换的代码,通过菜单应用它比在 Google 或 Markdown cheat sheet 上搜索答案更快。

Styler

{styler}插件允许通过运行styler::tidyverse_style()将你的代码重新格式化成更可读的格式。它对 R 脚本和 R Markdown 文档都有效。您可以重新格式化选定的代码、活动文件或活动包。我发现在共享或发布我的代码之前,这个插件特别有用,因此它尊重最常见的代码样式准则。

例如,像这样的一段代码:

1+1
#this is a comment
  for(i in 1:10){if(!i%%2){next}
print(i)
 }

变得更加简洁易读:

1 + 1
# this is a comment
for (i in 1:10) {
  if (!i %% 2) {
    next
  }
  print(i)
}

蛇皮箱

{snakecaser} addins 将一个字符串转换成蛇形样式。Snake case 样式是将由空格分隔的几个单词组成的字符串写成单词用下划线(_)分隔的字符串的做法。此外,它用小写字母代替大写字母。例如,以下字符串:

This is the Test 1

将被转换为:

this_is_the_test_1

snake case 样式对于变量、函数和文件名等特别有用(甚至被许多 R 用户推荐)。

视图管道步骤

感谢这篇文章的读者,我发现了ViewPipeSteps插件。此加载项允许在每个步骤后打印或查看管道链的输出。

例如,下面是一个包含数据集diamonds的链:

library(tidyverse)diamonds %>%
  select(carat, cut, color, clarity, price) %>%
  group_by(color) %>%
  summarise(n = n(), price = mean(price)) %>%
  arrange(desc(color))## # A tibble: 7 x 3
##   color     n price
##   <ord> <int> <dbl>
## 1 J      2808 5324.
## 2 I      5422 5092.
## 3 H      8304 4487.
## 4 G     11292 3999.
## 5 F      9542 3725.
## 6 E      9797 3077.
## 7 D      6775 3170.

如果您不确定您的管道链或想要调试它,您可以在每个步骤后查看输出,方法是突出显示您的整个管道链,然后单击 addins 菜单中的“查看管道链步骤”:

从 addins 菜单中,您可以选择将结果打印到控制台,或者在新的窗格中查看结果(就好像您在管道的每一步之后都调用了函数View())。点击查看管道链步骤将打开一个新窗口,显示每个步骤的输出:

请注意,您必须使用以下命令来安装ViewPipeSteps addin:

devtools::install_github("daranzolin/ViewPipeSteps")
library(ViewPipeSteps)

现在,你再也没有借口使用这个管道操作符了!

这是

yml 这是一个容易写 YAML 标题的附加程序

[ymlthis](https://ymlthis.r-lib.org/) addin 使得为 R Markdown 和相关文档编写 YAML front matter 变得很容易。该插件将为您创建 YAML,并将其放在一个文件中,比如一个.Rmd文件,或者放在您的剪贴板上。

如果你想写(更复杂的) YAML 标题,这个插件特别有用。

Reprex

如果你经常向 R 社区求助,这个 addin 可能会很有用!

重要的是要记住,当你向某人寻求帮助时,你必须通过准确清晰地概述你的问题来帮助他们。这有助于社区快速了解您的问题,从而减少响应时间。

在大多数情况下,这涉及到提供一个可再现的例子,也就是说一段代码(尽可能小和可读)再现所遇到的问题。

{reprex}插件允许你转换你的可重复的例子,这样它就可以很容易地在平台上共享,比如 GitHub,Stack Overflow,RStudio community 等等。您的可重复示例的布局将适应平台,您甚至可以包含关于您的 R 会话的信息。 2

以下是如何一步一步地使用它:

  1. 首先在 R 中创建可重复的例子(记住尽可能保持简短易读,以节省潜在帮助者的时间):

最小可重复示例

2.在插件列表中选择{reprex}插件:

选择{ reprex }加载项

3.选择您将发布问题的平台(如果您希望在可重现示例的末尾显示您的会议信息,请选中“附加会议信息”):

在{ reprex }加载项中设置选项

4.现在,您可以看到可重现示例的输出(右图),但更重要的是,它已被复制到您的剪贴板中,现在可以粘贴到您选择的平台上了:

您的 reprex 被复制到您的剪贴板中

5.将您的 reprex 粘贴到您选择的平台上(这里,它作为一个问题发布在 GitHub 上):

粘贴您的代表

6.检查可重现示例的最终结果:

决赛成绩

(访问本期 GitHub 看最终结果。)

博客城

我把这个插件放在列表的最后,因为只有有限数量的 RStudio 用户会对它感兴趣:维护用 R 编写的博客的人(比如这个博客)。

此加载项中最有用的功能如下:

  • 新帖子:用blogdown::new_post()创建一个新帖子。它还可以用来创建新页面,而不仅仅是文章
  • 插入图像:在博客文章中插入外部图像
  • 更新元数据:更新当前博客文章的标题、作者、日期、类别和标签
  • 服务站点:运行blogdown::serve_site()在本地实时预览你的网站

感谢阅读。我希望你会发现这些插件对你将来的 R 相关项目有用。参见 RStudio 和 R Markdown 中的其他提示和技巧。

和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。

  1. 您实际上不需要重命名它。然而,我经常用相同的通用名称dat重命名我处理的数据集,这样当我在新项目中重用以前项目的代码时,我就不必在代码中更改数据集的名称。 ↩︎
  2. 感谢 Josep 的建议↩︎

相关文章:

https://statsandr.com】原载于 2020 年 1 月 26 日

毁灭数独——一个数据科学项目

原文:https://towardsdatascience.com/ruining-sudoku-a-data-science-project-part-1-introduction-and-project-design-160a3c5caee5?source=collection_archive---------57-----------------------

1 部分:介绍和项目设计

这就是你在本系列结束时的观感。不过,山羊胡子可不能保证。
照片由范明Unsplash 拍摄

当我年轻而愚蠢的时候(我只保留了这两个特征中的一个),我想象与人工智能一起工作包括花大量的时间设计和实现一个显示某种智能的软件,不管这意味着什么。《黑客帝国》于 1999 年上映,当时我 15 岁,很容易被那些视觉效果所打动:可以说,这部电影对我未来的生活方向产生了相当大的影响。

在我的衣柜上,但那是以后的事了。

到目前为止,我已经作为一名数据科学家工作了大约 7 年,尤其是在过去的两年半时间里,在离开学术界进入行业工作之后,我对这个主题的看法发生了重大变化。如果我必须描述我每天都在做什么,我会说我基本上花时间试图解决与数据有关的问题,这些问题通常需要某种人工智能魔法才能解决。

然而,我意识到,从外部的角度来看,接受一个请求、一个想法并将其转化为实际产品的复杂性可能并不明显。我认为人们很容易专注于更有趣的部分,如花哨的神经网络和奇异的数据集,而忘记数据科学项目中对其成功至关重要的所有其他方面:我指的是像部署数据预处理以及最重要的项目设计,即制定从零到最终产品工作所需的路线图。

所以我认为把这个过程作为一个整体来写会很有趣:我看到了很多关于各个部分的教程(特别是关于前面提到的深度学习相关的东西),但没有多少关于如何把这些部分放在一起,更没有多少花合理的时间来解释一些选择背后的原因或探索为什么有些道路不应该走。请允许我填补这个空白。

介绍

做或不做【一个从零开始的完整的数据科学项目】,没有尝试(其实我鼓励尝试)。
照片由吉米·阮Unsplash 上拍摄

我是大卫·西尔弗关于强化学习的工作的超级粉丝,我经常开玩笑说 DeepMind 的目标是通过创造在特定游戏中比任何人都好的人工智能来破坏我们其余人的游戏。

所以我想沿着这条路走下去会很有趣,尽管是从一个非常不同的角度。

想象一下,你有一个非常种族主义的老阿姨,她将气候变化归咎于移民(她否认这一点:逻辑高于她),通过她的 facebook 账户传播各种错误信息,并且是韦斯特博罗浸信会的铁杆会员。当她在的时候,鸟儿停止歌唱。如果你不必去想象,只要把注意力放在愉快的想法上,时间最终会为你解决这个问题。

凯伦阿姨的生活中只有一个快乐的来源:每周二,每周一期的“数独 eXXtreme 4000”被送到她的邮箱里,她珍惜在那些谜题上度过的每一刻。

我们的目标是摧毁那种幸福。

为了做到这一点,我们将设计并实现一个系统,允许你从包含数独网格的杂志页面上拍摄一张照片,并在几秒钟内自动解决它。

摘要

本文是系列文章的一部分:

你应该有个计划

就像玩乐高一样,第一步是弄清楚你需要哪些部件。
照片由哈维·卡夫雷拉Unsplash 上拍摄

当我开始一个新项目时,我会花相当多的时间来确保几个问题有明确的答案(或者至少尽可能清楚):

  1. 我在做什么?这个项目的最终目标是什么?
  2. 我怎样才能做到这一点?

这是一个迭代过程,从任务的高级描述开始(在我们的例子中,它类似于“给定一张数独的图片,返回另一张完成的拼图的图片”),然后将其分解为更小的任务,直到它们变得清晰、原子化,这些任务可以简化为一些特定的问题,这些问题有算法形式的解决方案。

它不一定是完美的:不管你在计划方面有多好,肯定会有你忽略的东西,你必须在飞行中调整你的轨迹,在这个阶段没有必要专注于细节。只要原计划足够好,提供了一个好的起点,那就可以了。

让我们画一些形状。

项目分解

我很确定他在某个时候说过。
来源:照片由米尔乔兰flickr 上拍摄,由 2.0 授权 CC

技术说明:我用米罗来画图表,坦白地说,这是一个非常好的发明,让青霉素看起来像四年级学生的苏打火山。抱歉,亚历山大,这就是事实。

如我之前所说,你的出发点是问题的定义,仅此而已。所以从一张白纸开始,你应该得到这个:

作者图片

这并不多,当然你很有可能真的知道下一步该去哪里,但是让我们一步一步来。

顺便说一下,我将使用颜色来编码任务的状态:

作者图片

下一件自然的事情当然是将任务分成第一组更小、更易管理的任务。当我开始考虑这个项目需要什么组件时,我用自下而上的方法提出了四个主要任务。

作者图片

  1. 在某种程度上,我希望有一个数独的结构化表示,这是一个代表 81 个单元格的 9 x 9 数组,其中包含 1 到 9 之间的一个数字或者为空。有了这种格式的信息,即使我不一定知道如何做到这一点(这就是为什么那个块有一个红色的边框),我希望一个算法应该能够解决这个难题。
  2. 然而,为了获得该信息,需要扫描网格并识别每个单元格中存在的数字(或者检测出它是空白的)。幸运的是,如果任何数据科学都需要做一些事情,那就是臭名昭著的手写数字识别教程,所以这一部分应该被覆盖(绿色边框)。但真的是这样吗?🧙
    第三部剧透(如果一切顺利,我将在两周后出版):实际上比那要复杂一点,但是我找到了一个优雅的解决意外挫折的方法,如果我可以这么说的话,🧐.
  3. 我们希望我们的系统能够处理在“自然”环境中拍摄的数独网格图像,而没有任何太强的约束。因此,我们必须对输入图像进行某种预处理,以便从杂志的整页中整齐地裁剪旋转网格。加上促进负责数字识别的组件的工作所需的潜在的任何其他调整。
  4. 最后,如果我们能够以一种良好的格式呈现作品,比如说一个网络应用程序,你上传你的图像,它会与另一张图像一起显示在一边,显示谜题的解决方案,这将是一个不错的选择。边框是黄色的,因为虽然我过去做过类似的事情,但这不是我认为自己是专家的项目的一个方面,所以我想记住,这一部分可能比我更熟悉的部分需要更多的努力或研究。

进一步扩展第 3 点描述的活动,我们得到最终(可以这么说)的细分版本:

这甚至不是我的最终形式!
作者图片

目前这已经足够好了。在整个项目中,你可能会发现有些任务比你预期的复杂或多或少,或者它们必须被进一步分割成更小的任务。你甚至可能忘记了一些你在计划时没有想到的事情。这很正常,当你从一个产品的想法实际实现它时,这是必然会发生的。

接下来是什么

暂时就这样了。我们仍然只写了 0 行代码,但是可以说任何项目中最重要的部分之一已经完成了。计划就像乐队中的贝斯手,只有当它不见了的时候你才会注意到它。在接下来的文章中,我将介绍如何实现上图中列出的所有任务,从数据接收/预处理开始。一周后见!

你的快乐很快就会成为遥远过去的记忆,凯伦阿姨。

1】向这些年来与我同台的所有贝斯手道歉。你知道这是事实。

破坏数独——一个数据科学项目(第 2 部分:数据预处理)

原文:https://towardsdatascience.com/ruining-sudoku-a-data-science-project-part-2-data-preprocessing-27a290803c48?source=collection_archive---------58-----------------------

在数据预处理中,您可以对输入图像进行调整,以便在后续步骤中使用。万一你觉得还没准备好,害怕把事情搞砸,记住这个发生的
Ecce Home,前、后、后After byCea。由 2.0 在 CC 下授权

欢迎来到本系列文章的第二篇,该系列文章致力于如何利用数据科学作为惹恼一位不愉快的亲戚的借口!

摘要

本文是系列文章的一部分:

在第 1 部分中,我们经历了项目计划,也就是通过迭代地将它们分割成更小、更易管理的子任务,列出完成项目所需的所有任务。在本文中,我们将讨论在输入数据(在本例中是图像)被传递到管道的下一步,即数字分类器之前,需要对其进行什么处理。

图像预处理

数据预处理任务的分解。
作者图片

为了决定图像必须经过什么样的转换,有必要了解下一个组件(数字分类器)将需要什么样的格式。对于我们的例子,因为我们计划使用基于 MNIST 数据集的图像分类器,所以格式是 28 x 28 灰度图像,只包含一个数字。

如果输入图像只包含数独网格(我们将它分成 9 行 9 列),识别 81 个单元格将是一个相对琐碎的任务。然而,事实并非如此:理想情况下,我们希望用户能够用手机拍一张照片,然后传给应用程序。

因此,我们需要一个额外的步骤,将网格从页面的其余内容中分离出来,补偿旋转,并裁剪边界。然而,要做到这一点,我们首先必须对图像进行一些标准调整,以便于下面的步骤。

原始输入图像。
作者图片

对图像的初始调整包括使用高斯滤波器阈值的一些基本去噪,以去除大部分缺陷并获得二进制图像。我们还将反转颜色,因为这是用于执行几何操作的算法的要求,我们将使用该算法来检测网格并扭曲图像,使其最终成为正方形。

基本预处理(高斯滤波和阈值处理)后的输入图像。
作者图片

然后,我们必须首先确定网格所在的区域,然后应用所需的变换将其扭曲成正方形。这分两步完成,最终结果是这样的:

扭曲和裁剪后从图像中提取的网格。
作者图片

此时,将图像分割成 81 个单元格是微不足道的。任务完成[几乎]!

能偷多少就偷多少

关于时间管理的重要说明:上面的大部分代码不是我写的,而是取自各种来源,要么是 stackoverflow 评论,要么是 quadrillions 之一“用深度学习解决数独!”在媒体上或其他地方的教程。见鬼,我甚至不完全理解它的某些部分,我只是玩玩它,直到我足够信任它可以工作,并且在这个过程中不会烧坏我的电脑。

在这个星球上生活了 36 年,看了无数集《神秘博士》,我明白了一件事:时间是一种资源,通常情况下,你没有足够的时间。所以请允许我尽可能明确地说:

不要重新发明该死的轮子。

如果一个任务足够具体,很可能在某个黑暗的房间里已经有人写了一段代码来解决它。所以去抓住它,而不是花几个小时自己实现它。

最后一笔

快好了。我们仍然有一个问题:如果你只是通过将图像水平和垂直划分为 9 个部分来分割图像,其中一些可能会包含部分边界,如果你的数字分类器不是最擅长的,这可能会是一个问题。

看着你,字面上任何训练有素的分类器对 MNIST

因此,在我们进入有趣的部分之前,我们消耗了大量的精力,只是为了看到 Karen 阿姨脸上的笑容消失,花几行代码来识别并从网格中删除剩菜是一个好主意。

最后的预处理步骤:从原始单元格(左)中,我们首先从网格中删除可能保留的块(中),最后将其大小调整为 28 x 28 像素(右),因为这是我们将要使用的分类器的预期图像大小。
作者图片

就这些了,伙计们

暂时就这样了。你可以找到我在这个仓库中使用的 jupyter 笔记本,然而上面的要点涵盖了所有必要的(和有趣的部分)。

下周,是时候我们打开图形处理器,让神经元滚动:没有数字会保持不被识别。

[1]我们最终将使用与略有不同的东西,但在我计划的时候,这似乎是一个合理的选择,所以我暂时这么称呼它。

[2]我开始尝试复杂的东西,结果每边只剪了 10%左右。很管用。

破坏数独——一个数据科学项目(第三部分:数字识别和数独求解器)

原文:https://towardsdatascience.com/ruining-sudoku-a-data-science-project-part-3-digits-recognition-and-sudoku-solver-5271e6acd81f?source=collection_archive---------46-----------------------

数字五。或者,根据每一个训练有素的 MNIST 分类器,一个 6。或者八,或者蝙蝠侠的标志,为什么不呢。瑞安·约翰斯在 Unsplash 上拍摄的照片

好吧,这是你真正关心的一篇文章,其中 GPU 升温和损耗曲线是平坦的:今天我们将讨论该项目的两个核心部分,即如何识别九个数字中的哪一个出现在每个数独单元中,我们事先识别并分离了这些数字(查看本系列的第二篇文章中的细节),并从那里着手解决这个难题。哦,我们还会用 2020 年的魔法 : AI

但首先,快速回顾一下我们是如何走到这一步的。

之前(和下周),在这个系列中

弗兰克·维西亚在 Unsplash 拍摄的照片

我们从一个相对简单的想法开始:设计并实现一个系统,该系统将一张数独网格清晰可见(但不一定对齐/居中)的图片作为输入,并返回该谜题的

我们花了相当多的时间来组织我们的工作,以便我们至少对各种任务的难度有一个粗略的想法(因此我们必须分配给每一项任务多少时间),并且我们处理了第一个主要的工作包,数据预处理。

现在,我们相信我们的系统正确地处理了输入图像,并把网格中的 81 个单元干净整洁地分开了:是时候做有趣的事情了。

第二步:解决数独

如果你正在读这篇文章,你可能很擅长数学,因此会注意到我把第二步放在第一步之前,尽管 2 被一致认为大于 1。我发誓这是有原因的。

如果你还记得的话,在计划的时候,我给每一个子任务分配了一个估计的难度等级,红色代表我所知甚少或者一无所知的子任务。解算器就是其中之一。虽然我几乎可以肯定在 Github repo 中有一种算法形式的解决方案,但我还没有正确地搜索它,所以我不愿意想当然地在专注于数字识别任务之前研究这一部分,另一方面,我认为我知道如何解决这个问题。哦,讽刺的是。

最终项目组织结构图。颜色表示估计的难度。
作者图片

无论如何,一个简短的搜索就足以找到一个片段,它确实做到了这一点,所以这一整节的目的只是为了强调关注你对首先不太有信心的地方的重要性,以便通过找到一个解决方案(比如在这种情况下)或找到一些变通办法来尽早摆脱这种不确定性。

第一步:识别数字

在这一点上唯一缺少的是数字分类器,一旦插入我们的系统,这个项目基本上就结束了。

我预计这是一个相对容易的部分:每个接触深度学习的人,特别是 CNN 的人,都必须经历在臭名昭著的手写数字识别数据集 MNIST 上训练模型的初始仪式,并且可以在睡梦中产生这样的分类器,所以我确实这样做了。

不幸的是,虽然在 MNIST 的测试集上表现得相当好,但在流水线的前一步中产生的实际图像(包含一位数字的孤立单元网格)上的结果至少可以说是相当令人失望的。当然,大多数时候它仍然是正确的,但是请记住,为了让这个系统工作,即使一个错误也会导致不正确的结果。在这个项目中,我遇到了第一个真正的障碍,不得不去处理它。

两步故障排除

糟透了,但这种事时有发生。为了解决这样的问题,我尝试回答以下两个问题:

  1. 为什么它不起作用?
  2. 我能做些什么呢?

为了回答第一个问题,并找出为什么我的表现如此糟糕(相对而言),我分析了被错误分类的图像,注意到大多数错误发生在数字 1 上,它经常(错误地)被标记为 7 。在其他情况下,图像有某种噪声(小块非黑色像素),预处理步骤没有去除这些噪声,这显然足以混淆模型并产生错误的预测,尽管数字本身在图像中显示得很清楚!

左侧是来自 MNIST 数据集的数字 1 的样本。在右边,从实际的数独网格中提取的 1。尽管非常相似,但在 MNIST 上训练的分类器可能无法正确分类右边的那个,而是预测为 7。作者图片

老实说,这并没有真的让我吃惊:我对 MNIST 数据集有严重的问题,我写了一篇> 1000 字的长篇大论,为什么它是着火的垃圾。简而言之,虽然它是对图像分类问题的一个很好的介绍,但当应用于一种与大多数在线教程中呈现的问题略有不同的问题时(数字打印的数字而不是手写的数字),它并不真正起作用。

我设计的解决方案基于一个神圣的原则,即你应该尽最大努力训练一个数据模型,这个数据与你希望模型工作的数据尽可能相似。如果你在为你的下一个额头纹身寻找报价,请考虑前面的句子。

我所做的是使用 Python PIL 包生成一个合成数据集,在一个正方形画布上以略微不同的大小和位置绘制各个数字。对于每张图片,都从与杂志中使用的字体最相似的列表中选择一种字体,以提高模型的泛化能力。此外,这些图像是在模型训练时动态创建的因此不需要磁盘空间,因为它们没有被保存。

这种生成数据的方式,加上一些标准的数据扩充(实际上只是旋转,加上一些定制的椒盐噪声,以使其对图像中的缺陷具有抵抗力),产生了一个模型,尽管并不完美,但产生了明显更好的结果。

还有一个小细节需要讨论:数字分类器可以识别从 0 到 9 的数字,但是数独单元格要么包含从 1 到 9 的数字要么为空。我发现处理这种情况的最简单的方法是首先使用一些试探法来查看单元格中是否有任何东西(通过简单地计数有多少像素与背景颜色不同,并确定一些合理的阈值),并且只有在实际分类中有一些东西在进行时。这有一个额外的好处,即避免在无用的计算上浪费大量的时间和资源(计算白色像素比在 CNN 上运行推理要便宜得多)。

无聊(但是有用!)工具/物流 blabla

在我回到我的 Factorio megabase 之前,我想多说几句我在这个项目中使用的工具。

我真的不想卷入整个文本编辑器/IDE 帮派战争,已经流了太多的血。我个人使用 Vim(有大量插件,检查我的)。vimrc 如果你感兴趣的话)因为我超级老了但是我对这个话题没有强烈的看法,用什么让你开心。

另一方面,最近我一直在使用一个实验跟踪/簿记工具来跟踪正在运行/已经完成的实验。即使对于像这样的超小型项目,我也建议使用它,因为它会以增加几行代码为代价,使您的生活变得更加简单。有几个工具,我个人很喜欢,也想建议使用神圣的(加上综合可视化)。

最后,这是一个简单的问题,Jupyter 笔记本非常适合数据探索和快速原型制作。使用它们!

搞定了。

就是这样!这次是真的!流水线现在完成了,我们有了从原始输入图像到谜题解决方案的清晰路径。下周,我们将通过使用 Docker 将其打包成 web 应用程序的形式来完成该项目。

你可以在 this Github repo 中找到这一系列文章的笔记本和代码。

七天后见!

[## matteobarbieri/数独

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/matteobarbieri/sudoku)

破坏数独——一个数据科学项目(第 4 部分:部署和恢复)

原文:https://towardsdatascience.com/ruining-sudoku-a-data-science-project-part-4-deployment-and-retro-27620bbeca75?source=collection_archive---------53-----------------------

这不是我。我希望我也这么可爱。
Photo by 大家好,我是尼克🎞号上的 Unsplash

如果你偶然看到这篇文章,我强烈建议你从这个系列的第一部分开始,原因很明显。

这是他们旅程的终点(谁?)说。我们的计划是记录从一个想法到一个产品所需的活动,尽管这是一个简单且范围有限的活动,我们也这样做了。我尊重时间表!

别谢我,真正的英雄不需要那个。

本系列的其他文章

简单回顾一下,现在我们已经完成了从图像到数独谜题答案的数据管道。今天,我们通过设置一个漂亮的基于 web 的前端来包装我们的产品,从而结束了这个循环。

然而,在我们开始之前,一个快速的前提。

没有独角兽这种东西

我所说的独角兽是指在非常优秀的人,拥有数据科学项目所需的所有技能,即项目管理、前端和后端开发、机器学习工程,甚至更多。

我认为自己在 Python 编程和机器学习相关的东西方面相当熟练,但是至少可以说,我对于如何最好地部署一个应用程序(比如我在本系列中所做的那个)的知识是有限的。这意味着,当然,我可以把一些可能有用的东西放在一起,而不会让整个服务器机房着火,但是很可能任何熟练的都会发现我的实现充其量是次优的。

因此,不要期望这个特定的部分是你可能找到的关于如何部署产品的最好的指南,把它作为一个快速原型,以一种整体体面的方式完成工作。

【不那么】动态二人组:前端+后端

尽管不想在这一部分花太多时间(主要是因为我在这个特定任务中缺乏专业知识,所以效率不是特别高),但我仍然想将负责显示内容的前端,与负责繁重工作的后端(基本上是我到目前为止描述的所有代码)分开。当然这里有点矫枉过正,但它符合“真实开发应用程序”的模拟场景。最终的系统将如下所示:

用户上传一张与前端交互的图片,前端将图片传递给后端进行处理。然后,结果被传回前端并显示给用户。因为我不会画画,所以人类在这里就像钻石,因为他们都是由碳构成的。图片作者。

我选择使用 flask 作为后端,因为我已经很熟悉它了,它可以很好地与用 Python 编写的其余代码配合使用,我选择使用 Node.js 作为前端,因为我从来没有借口玩它,这似乎是一个很好的选择。

我真的不想太详细地介绍我是如何做的,因为这里真的没有什么特别聪明的地方,只是你通常对前端的 HTML/JS/CSS ( bootstrap )和异步请求的混合,以及后端的超级标准推理功能,加上一点点 PIL 魔法来绘制原始网格上的解决方案(即缺失的数字)。它看起来是这样的:

一个来自前端的解决了的难题的截屏。请注意,输入图像实际上是一本杂志的一整页,但是为了便于比较,我只包括了裁剪过的网格。作者图片

像往常一样,你会在这个项目的回购中找到代码。注意,为了让系统工作,你必须自己训练一个模型(按照本系列的前一篇文章中的说明)或者下载我训练的那个模型(在 repo 中有一个叫做download_data.sh的脚本可以做到这一点)。您还需要用于在输出图像上书写数字的.ttf字体文件(这也由脚本负责)。

将所有的事情归档

如果我是负责人,我会把不通过 docker 容器或类似的东西来部署东西的任何方式都定为非法。我不是在说“你被罚款”是违法的,我说的是最起码的无期徒刑。

强制容器图片。
照片由马克西姆·霍拉维尔Unsplash 拍摄

因此,为了使部署尽可能不痛苦,我为两个服务(后端和前端)中的每一个创建了一个映像,并用 docker-compose 编排它们。同样,这里没有什么特别的,只有标准的设置来显示正确的端口并安装包含所需数据(模型和字体)的文件夹。

就这样,我们结束了!不过,还有一件事。

重新流行

Sebastiano Piazzi 在 Unsplash 上拍摄的照片

这是一个很好的实践,一旦一个项目(或者甚至是它的一部分)已经完成,回顾一下什么做得好,什么做得不好,这样下次你就可以改进和做得更好。这被称为“进行追溯”(“追溯”的简称)。

什么进展顺利

我真的觉得我应该受到表扬。花大量时间做计划最终得到了回报:一旦决定了要做的事情,就只需要使用你觉得舒服的任何任务管理解决方案来组织工作了(我是一个超级酒鬼),当然还要找时间去做。

除了我们将在下一段讨论的一个小例外,一切都进行得或多或少很顺利。

什么不太顺利

唯一出现一些意外挑战的部分是数字识别组件的实现。无需过多讨论细节(您可以在专门讨论该部分的文章中找到更深入的讨论),我最初计划使用的方法并不像预期的那样有效,因此有必要找到一条替代路线。

老实说,我认为这更像是一个“学习机会”,而不是规划时的一个实际错误,因为提前预见数据或给定方法的问题是不可能的,也因为它迫使我找到一个优雅的解决方案,否则我不会想到,这是一个我将带到未来项目中的经验。

作者的最后一句话

我写这个系列来挑战我自己,以产生我希望你会发现是高质量的内容,带有相当数量的幽默,使它可以忍受。我试过了(显然成功了,耶,我!)每周一写一篇新文章,以避免势头减弱。

请随时通过 LinkedIn 与我联系,我总是渴望与任何对此感兴趣的人讨论数据科学相关的话题。

此外,如果你是一名数据科学家新手或想成为数据科学家的人,却不知道该怎么做才能在这个神奇的行业找到一份工作,给我写封短信:我非常乐意给你这方面的建议🙃

感谢阅读!

[## matteobarbieri/数独

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/matteobarbieri/sudoku) [## 马特奥·巴比耶里-数据科学家-佩尔塔里昂| LinkedIn

数据科学家,深度学习爱好者,Python 爱好者。我获得了计算机科学博士学位,主要研究分析…

www.linkedin.com](https://www.linkedin.com/in/barbierimatteo/)

Python 中基于规则的 App Store 评论情感分析

原文:https://towardsdatascience.com/rule-based-sentiment-analysis-of-app-store-review-in-python-94d8bbfc48bb?source=collection_archive---------34-----------------------

利用“VADER”来识别基于用户评论的用户情绪

照片由威廉·胡克Unsplash 上拍摄

通过阅读这篇文章,您将学会用 Python 分析和执行基于规则的情感分析。我们将使用名为VADERPython包,并在一款名为 Clash of Clan 的手机游戏的 app store 用户评论数据集上测试它。

基于官方文档,VADER (Valence Aware 字典和情感推理器)是:

…一个基于词汇和规则的情绪分析工具,专门针对社交媒体中表达的情绪

与传统的机器学习方法不同,VADER使用的方法是针对社交媒体帖子的。它可能不太适合文学或新闻文章。然而,一个主要的优点是推理时间非常快,您可以在简单的笔记本电脑上顺利运行。

本教程有 3 个部分:

  1. 设置
  2. 履行
  3. 结论

让我们继续下一部分,开始安装必要的模块。

1.设置

Python 包

你可以通过pip install在 Python 中轻松安装VADER。强烈建议您在继续安装之前创建一个虚拟环境。激活您的虚拟环境并运行以下命令。

pip install vaderSentiment

在读取数据集时,我们还需要另一个名为pandas的模块。通过以下命令安装它:

pip install pandas

资料组

转到下面的链接,将整个数据集下载到项目的根目录下。您应该有一个名为clash-of-clans.csvcsv文件,其中包含以下数据。

在下一节中,我们将编写一些 Python 代码。

2.履行

在本教程中,我将使用Jupyter Notebook。话虽如此,您也可以在一个Python文件中正常运行它。

导入

在文件的顶部添加以下导入声明。

from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import pandas as pd

初始化

接下来,初始化SentimentIntensityAnalyzer类的一个实例。我们将在每次计算或分析文本的情感时重用这个实例。

analyzer = SentimentIntensityAnalyzer()

数据帧

确保clash-of-clan.csv和你的文件在同一个目录下。通过read_csv函数加载数据集。如果您将文件存储在其他位置,请修改文件的路径。

df_coc = pd.read_csv("./clash-of-clans.csv", encoding="utf-8")
df_coc.head()

您应该在控制台上看到以下输出。如果您运行的是 Python 文件而不是 Jupyter Notebook,格式可能会略有不同。

情感分析

你可以通过这个模块提供的polarity_scores函数来计算单个文本的情感。

vs = analyzer.polarity_scores('I felt terrible today')
print(vs)

您将得到一个包含 4 个键/值对的字典,如下所示:

{'neg': 0.508, 'neu': 0.492, 'pos': 0.0, 'compound': -0.4767}

供你参考,

posneuneg分数是属于每个类别的文本的比例(因此这些应该加起来都是 1...或者用浮动操作接近它)。如果你想对一个给定的句子进行多维度的情感测量,这些是最有用的指标。

compound分数是通过对词典中每个单词的化合价分数求和来计算的,根据规则进行调整,然后归一化到-1(最极端的负面)和+1(最极端的正面)之间。如果你想对一个给定的句子进行单一的一维情感测量,这是最有用的度量。称之为“标准化加权综合得分”是准确的。

通常,阈值设置如下:

  • positivecompound得分≥ 0.05
  • negativecompound得分≤ -0.05
  • neutralcompound在-0.05 和 0.05 之间

创建一个新函数,并在其中插入以下代码。这个函数接受一个输入文本,并基于复合分数返回文本的情感。情感标签包括:

  • positive — 2
  • neutral1
  • negative — 0
  • junk——-1
def calc_vader_sentiment(text):
    sentiment = 1 vs = analyzer.polarity_scores(str(text))
    compound = vs['compound'] if(compound == 0):
        sentiment = -1
    elif(compound >= 0.05):
        sentiment = 2
    elif(compound <= -0.05):
        sentiment = 0 return sentiment

一旦创建了上面的函数,创建一个新的由三列组成的DataFrame:

  • uid —数据的唯一标识符。我们将使用索引作为 uid
  • text —用户评论的文本
  • label —情感分析得分

运行列表理解中的calc_vader_sentiment函数如下

df = pd.DataFrame({
    'uid': list(df_coc.index.values),
    'text': df_coc['Content'],
    'label': [calc_vader_sentiment(x) for x in df_coc['Content']]
})

它应该需要几秒钟来计算分数,因为我们有大约 50000 行数据。通过运行下面的代码,您可以简单地查看前 10 个结果。

df.head(10)

您应该会得到以下结果。

保存到文件

如果你想把它保存到一个文件中,你可以通过 pandas 模块提供的to_csv函数来完成。根据您的偏好修改文件的名称。

df.to_csv("sentiment_result.csv", encoding='utf8', index=False)

3.结论

让我们回顾一下今天所学的内容。

我们首先简要介绍了VADER及其功能。然后,我们通过 pip install 安装了必要的模块,并从Kaggle下载了clash-of-clan.csv数据集。

之后,我们继续实现我们自己的代码,通过使用由VADER计算的复合分数来计算用户评论的情感分数。

除此之外,我们还学会了将最终输出保存到一个 csv 文件中,以供将来参考。

感谢你阅读这篇文章。希望在下一篇文章中再见到你!

参考

  1. Vader perspection 官方 Github 页面
  2. 来自 Kaggle 的 Clash of Clans 应用商店用户评论数据集

使用 Supervisor 在单个 Docker 容器中运行多个服务

原文:https://towardsdatascience.com/run-multiple-services-in-single-docker-container-using-supervisor-b2ed53e3d1c0?source=collection_archive---------12-----------------------

您是否曾经遇到过这种情况,您希望在同一个容器中运行两个或更多的轻量级服务?

虽然 Docker 提供了一个 Docker-compose 工具,用于在多个容器中构建和运行多服务应用程序。Docker-compose 需要一个 YAML 文件来配置您的多个服务。但是有时我们希望在同一个容器中运行两个或更多的轻量级服务。

在本文中,我将解释如何使用“Supervisor”工具在同一个 docker 容器中启动多个服务。

那么什么是主管呢?

管理程序是一种工具,它允许我们像操作系统一样在 Linux 中同时管理多个不同的进程。监督工具需要一个。conf 文件,我们在其中指定进程和与该进程相关的不同选项,如输出日志位置、自动启动、自动重启等。

我将使用的示例服务

我将在同一个容器中运行两个不同的服务“运行 Django app 的 Gunicorn 服务器”和“Redis 服务器”。因此,在我创建的示例项目中,包含一个简单的 Django REST API,用于从 Redis 服务器添加和获取数据。在生产环境中,这两个服务在具有生产级配置的独立容器中使用。为了便于演示,我将在一个 docker 容器中运行这两个服务。

项目目录结构

开始构建项目的一个好方法是拥有一个整洁清晰的项目结构。因此,考虑到这一点,我们将使用下面的代码目录结构:

multi_service
├── django_app
│   ├── db.sqlite3
│   ├── django_app
│   ├── manage.py
│   ├── redis_test
│   └── requirements.txt
├── Dockerfile
└── supervisor
    └── service_script.conf

在本地测试我们的服务(Django App 和 Redis)

在 docker 中发布服务之前,首先,让我们在本地测试它们。您可以使用下面的链接从 GitHub 下载完整的项目。
网址:https://github.com/aakash-rathore/docker_multi_services.git

要测试 Redis 服务器,只需使用以下命令下载它:

Using apt package installer

$ sudo apt-get install redis-server

# Use apk add (For alpine linux)

# apk add redis

现在使用以下命令运行服务器:

$ redis-serve

您将看到服务正在运行:

本地 redis 服务器测试

为了在本地测试 Django 应用程序,我们将使用 Gunicorn 服务器。使用以下命令在 gunicorn 服务器中运行应用程序:

$ gunicorn --bind 0.0.0.0:8000 django_app.wsgi

该命令的输出如下所示:

在本地运行 gunicorn

主管配置文件

按照上面指定的目录结构创建一个文件【service _ script . conf】。按照下面的配置添加 django 和 redis 服务:

## service_script.conf

[supervisord]  ## This is the main process for the Supervisor    
nodaemon=true  ## This setting is to specify that we are not running in daemon mode

[program:redis_script] ## This is the part where we give the name and add config for our 1st service
command=redis-server  ## This is the main command to run our 1st service
autorestart=true ## This setting specifies that the supervisor will restart the service in case of failure
stderr_logfile=/dev/stdout ## This setting specifies that the supervisor will log the errors in the standard output
stderr_logfile_maxbytes = 0
stdout_logfile=/dev/stdout ## This setting specifies that the supervisor will log the output in the standard output
stdout_logfile_maxbytes = 0

## same setting for 2nd service
[program:django_service] 
command=gunicorn --bind 0.0.0.0:8000 django_app.wsgi
autostart=true
autorestart=true
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes = 0
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes = 0

docker 文件创建

现在我们将开始制作 docker 文件。添加图层的步骤如下:
基础镜像- >安装所需工具- >添加源代码- >添加配置文件- >启动服务
最终 Dockerfile 如下:

# Base Image
FROM alpine

# Installing required tools
RUN apk --update add nano supervisor python3 redis

# Adding Django Source code to container 
ADD /django_app /src/django_app

# Adding supervisor configuration file to container
ADD /supervisor /src/supervisor

# Installing required python modules for app
RUN pip3 install -r /src/django_app/requirements.txt

# Exposing container port for binding with host
EXPOSE 8000

# Using Django app directory as home
WORKDIR /src/django_app

# Initializing Redis server and Gunicorn server from supervisord
CMD ["supervisord","-c","/src/supervisor/service_script.conf"]

构建和测试

最后,使用下面的命令构建 docker 映像:

$ docker build -t multi_service:1 .

成功构建操作后的输出:

docker 构建成功

现在使用上面构建的映像运行 docker 容器:

$ docker run -it -p 8000:8000 multi_service:1

这里 docker 容器是在非守护模式下运行的如果你想在守护模式下运行它使用 '-d' 选项,上面命令的输出:

运行 docker 的输出

让我们在 API 测试工具中测试 django 应用程序,我使用的是一个轻量级的“高级 REST 客户端”,可以在 Google Chrome 扩展商店中找到。您可以使用 Postman、Curl 或任何其他工具进行测试:

测试添加用户功能:

测试添加用户功能

测试获取用户:

测试提取用户功能

结论

在本文中,我解释了如何在一个 docker 容器中运行多个服务(Django app 和 Redis server)。最后,我们还做了一些测试来检查这两个服务是否都在运行。

请在下面留下您对本文的评论,如果您在上面指定的任何步骤中遇到问题,您可以通过 InstagramLinkedIn联系我。

使用 Python 原生编译 Julia 代码

原文:https://towardsdatascience.com/run-native-julia-code-with-python-92d3e1079385?source=collection_archive---------12-----------------------

通过切换语言来加速 Python

我经常讨论使用 Julia 的好处,因为有很多好处。Julia 是一种可扩展的、高性能的高级语言,易于学习,几乎可以完成任何工作。对于数据科学家来说尤其如此,因为 Julia 的重心是统计计算和函数程序。在 Julia 的核心,它是一种为科学家用数据做统计而创造的语言,这种直接的意图使它在特定的事情上非常棒。

虽然我可以花很多年谈论 Julia 如何更快,具有更好的浮点精度,并且打字非常简单,但这不是我今天在这里的原因。虽然我已经表达了我的担忧,也可能夸大了我对朱莉娅的喜爱,但有一个大话题我还没有和朱莉娅谈过:

多才多艺

使用 Python 语言的一个巨大好处是 Python 是通用的。如果你不想使用 web 请求,可能有一个包装器,如果你想把时间戳从 Unix 转换成 UTC,有一个库。这是一个很大的好处,因为这意味着你的语言将永远支持你的工具。我已经提到过 Julia 的包是它最大的缺陷,但尽管如此,Julia 最初是在 2012 年发布的,也就是 8 年前。朱莉娅反驳说:

可翻译。

首先,我们有令人惊奇的包 PyCall.jl,它允许调用 Python 包,甚至在虚拟环境中。在数据科学领域,能够调用数百万个 Python 包是一件大事。

布丁中真正的水果是,反过来也是可能的。没错,从 Python 本地运行 Julia 非常容易。这很重要,因为我们都知道 Python 有时会相当慢。我承认,在大多数情况下,Python 很棒,但是当 Python 让你失望时,它真的会让你进退两难,不知道下一步该做什么。

PyJulia 或 julia.py 允许您发送类似 SQL 查询的代码行,并为实际返回标识一个返回变量。简而言之,你可以通过向 Julia 编译器发送字符串来运行 Python 中的 Julia。当然,第一步是从您的包管理器中获取 Julia,我将使用 APT。

sudo apt-get install julia

现在它就像进入朱莉娅 REPL,然后安装 PyCall 与]或使用 Pkg 一样简单。

julia
julia> using Pkg; Pkg.add("PyCall")
(or)
julia> ]
pkg> add "PyCall"

对于一些 Julia 包,包括 PyCall,我推荐构建代码:

julia> ]
pkg> build "PyCall"

现在,用退格键退出 Pkg,按 ctrl/command+D 退出朱莉娅 press。我们现在可以用 pip 安装我们的 Python 包了,我将使用 pip3。

sudo pip3 install julia

既然我们所有的依赖关系都得到了满足,我们就可以跳到笔记本上进行尝试了!第一步是使用 Julia 库中的安装方法:

import julia as jl
jl.install()

现在我们可以导入 Julia 可执行文件,并用它运行字符串代码!

from julia import Julia
jl = Julia()
average = jl.run("using Lathe.stats: mean; array = [1,5,4,5,6]; m = mean(array); return(m)")

所以…酷!

但这意味着什么呢?嗯,任何用 Julia 开发的包都可以用 Python 开发。不仅如此,该包还可以在 R 中与 JuliaCall.R 一起使用。我已经开始为 Julia 的车床机器学习模块开发包装器,这些包装器将在 native Julia 下与 Python 和 R 一起使用。我真的很高兴 Julia 代码既更快,又同时使该包在最流行的统计语言中通用。这对 Julia 开发者来说是一个很大的好处,因为我们不必为了维护一个 Python 和 R 版本而重写代码。当然,随之而来的是在编译时使用 Julia 而不是 Python 的实际好处。

下降趋势

在计算中没有什么是如此简单的,所以用这种了不起的方法翻译代码,也有一些不幸的问题。虽然很少,但我承认必须安装 Julia 和 PyCall 依赖项有点乏味,特别是对于一个以前从未使用过 Julia 的 Python 包的最终用户来说。此外,Julia 的版本控制和兼容性给这个包带来了很大的问题,让 PyJulia 运行起来可能会很困难。我遇到的问题主要是在 Unix 所有权中,编译器在我的非所有目录(由 root 所有)中,由于权限被拒绝,我将得到退出状态 1

这当然是通过在我的 Python 位置上使用 chown 并在 Julia 的情况下做同样的事情来解决的。

除了这个问题,还有它的写作方式,有点乏味。诚实地说,通过字符串发送单行代码并不是最理想的,如果你打算在代码中使用引号,并使用字符串,那么在 Python 中的“、”和”之间导航将会非常困难。没什么大不了的,但绝对是需要注意的。

结论

总之,多才多艺是朱莉娅的一大优势。Julia 代码不仅速度快,而且扩展性极强,还可以写成其他语言。这是一个很大的优势,因为您的 mill Julia 包可以在一个包装器中轻松运行,并且当它在本地系统上更新时,可以与原始的 Julia 包一致地更新,所以…酷!我非常期待能让 R 和 Python 都了解 Lathe,这样更多的人就能使用 Lathe,并希望能喜欢上它。

在网站上运行 Python 代码:探索 Brython

原文:https://towardsdatascience.com/run-python-code-on-websites-exploring-brython-83c43fb7ac5f?source=collection_archive---------2-----------------------

Python 中的 JavaScript 等效脚本

作为 Python 的长期追随者,我从未停止对这种语言的探索,每天都在寻找它的实现。在一个晴朗的日子里,我了解了旨在取代 Javascript 的 web 脚本的 Brython。它允许你在网页上直接使用 Python 脚本来处理 DOM(文档对象模型)元素和事件,全部使用我们最喜欢的语言 Python。让我们深入了解如何为你的下一个项目实现这样的脚本,以及我是如何使用这项技术赢得比赛(第二名)的!

在此之前,如果你想探索 Python 的 android 方面,那么一定要看看我在 Medium 上的 Python 系列中的 Android 应用程序。

[## 用 Python 构建 Android 应用程序:第 1 部分

使用 Python 构建 Android 应用程序的分步指南

towardsdatascience.com](/building-android-apps-with-python-part-1-603820bebde8)

卢卡·布拉沃Unsplash 上拍摄

什么是 Brython?

Javascript 被认为是支持网页交互性的网络语言。没有人想浏览可以用 HTML 和一点 CSS 构建的静态页面。Brython 提供了一种使用 Python 与网页元素进行交互的直观方式。在过去,python 也在使用 Django/Flask 的后端脚本中得到应用,允许用户快速开发可扩展的网站。而 Bython 也专注于在客户端应用这一点。从 Brython 开始,您有两种选择:

您在本地下载 Brython 包,然后将它包含在您的文件中。这可以使用 pip 来完成:

pip install brython

然后在一个空文件夹中,

brython-cli --install

或者,

直接使用 Brython CDN,它会在新的更新发布后立即更新,让您不必手动更新软件包。

<script src="https://cdn.jsdelivr.net/npm/brython@3.8.10/brython.min.js"> </script> <script src="https://cdn.jsdelivr.net/npm/brython@3.8.10/brython_stdlib.js"> </script>

使用 Brython 做一些基本的事情

让我们复习一些基础知识,开始开发一些很酷的项目。请注意,这要求您对 DOM 有一个基本的了解,熟悉基本的 Javascript 将是一个加分项,尽管不是必需的。在您的 IDE 中,填充一个基本的 HTML 代码,放置一个输入字段和一个带有 id 的 span 标记。另外,使用 script 标记添加 Brython CDN。它可能应该是这样的:

作者代码

现在,使用 Python 脚本将这个输入事件绑定到 span 标记。为此,我们将使用浏览器中的文档。任何类型的脚本都将封装在 text/python 类型的脚本标签中,这也可以在 body 标签下定义。需要做的另一个更改是在 body 标记()中包含 onload="brython()"。这是一个脚本,它将使我们能够实时显示文本:

作者代码

这里,我们使用了浏览器模块中的文档对象将输入(文本)的 id 绑定到函数 show_text()。这个函数使用它们的 id 将输入标记的值分配给 span 标记。这里有一个 GIF 来展示这个 HTML 页面在加载浏览器时做了什么:

作者 GIF

再举个例子!

让我们看另一个例子,看看如何使用 Brython 进行 API 调用,这是最令人兴奋的部分。考虑一个场景,您希望主持一个基于 web 的机器学习项目。您创建了一个模型,现在将它作为 API 在任何云平台上使用,比如 Heroku、GCP 或 AWS。作为一名 python 开发人员,您可能不太熟悉 web 开发,并且发现很难处理网站的延迟响应或缓慢加载。你可以使用 GitHub 页面轻松托管你的网站,但它不支持 flask 或 Django 框架(目前)。

为了处理这种情况,您可以轻松地创建一个基本的 HTML 网页(或者使用一点 CSS 从 bootstrap 获得帮助),要求用户输入所需的参数,然后您的 Brython 脚本向您的模型主机发出请求,您在网页上显示结果。这将有利于网站托管在 GitHub 上,使管理代码库更容易,而且它将一直运行。让我们来看一下创建一个 API 调用来返回一个随机笑话的脚本:

作者代码

首先,查看该文件的输出:

作者 GIF

这里,get joke 按钮被绑定到 API 调用函数,该函数对服务器进行 AJAX 调用,返回结果,然后呈现给显示标签。

获胜的剧本

我得到的问题陈述是从给定的 API 中获取歌词,并在网络上显示出来。它需要音乐艺术家作者和我们想要歌词的标题/歌曲名。下面是同样的 GIF:

作者 GIF

如果你想探究这个的源代码,那么这里有 GitHub 库链接

结论

我们看到了如何使用 Brython 将基于 python 的脚本直接实现到网页中。毫无疑问,就速度、可用性、代码的可维护性和理解性而言,目前 Javascript 是不可替代的。这些 python 脚本需要熟悉 DOM,而只有使用 JS 才能更好地练习和理解 DOM。这最适合那些希望在 web 上展示其后端和前端 python 技能的人。很多人都用 Pygame 和海龟库做过游戏、图形。你可以在谷歌上了解更多。

这就是我的观点,在 medium 上关注我可以收到更多类似这些有趣文章的通知。说完了,合十礼!

更新:Brython 的高级使用案例—

[## Python 在前端:使用 Brython 的 ML 模型 Web 界面

本文作为数据科学博客简介的一部分发表,机器学习是一个迷人的领域…

www.analyticsvidhya.com](https://www.analyticsvidhya.com/blog/2021/07/python-on-frontend-ml-models-web-interface-with-brython/)

一些链接:

[## 自动化项目组合生成的 GitHub 动作

使用 Python 和基本前端的 Dockerized GitHub 动作。

towardsdatascience.com](/github-action-that-automates-portfolio-generation-bc15835862dc) [## 家庭团体有那么糟糕吗?结果会让你震惊

分析 WhatsApp 群聊&构建网络应用

towardsdatascience.com](/is-family-group-that-bad-results-will-shock-you-573f64e194be) [## Kaustubh Gupta -机器学习作家- upGrad | LinkedIn

嗨,我是一名 Python 开发人员,能够进行 Web 抓取、Selenium 自动化、数据科学、后端 Web 开发…

www.linkedin.com](https://www.linkedin.com/in/kaustubh-gupta-612767ab/)

在 Sublime Text (Mac)上运行 Python 3

原文:https://towardsdatascience.com/run-python3-on-sublime-text-5949e55450b2?source=collection_archive---------0-----------------------

只需要一分钟

弗洛里安·奥利佛在 Unsplash 上拍摄的背景照片

Sublime Text 是编程中使用最广泛的轻量级文本编辑器之一。如果你是一个 Python 程序员,你可能没有运行你喜欢的 Python 版本。本教程解释了如何运行 Python 3.7 获得 Sublime 文本。

如果你喜欢视频教程,这是我的 YouTube 视频:

检查您的 Sublime 文本使用的是哪个版本

创建新的 Python 文件。我把我的命名为 scratch.py。在运行 Python 代码之前,将文件保存为. py 扩展名是很重要的。否则,你将无法执行 Python 代码。此外,拥有一个临时 Python 文件来运行快速代码总是好的。

现在键入以下代码:

import sys
print(sys.version)

要运行代码,请按下命令 B 或转到工具- >构建。

如你所见,我的崇高文本运行的是 Python 2.7。要将此更改为 Python 3.7,我们必须添加一个“构建系统。

添加 Python 3 作为构建系统

转到工具->构建系统->新建构建系统..

注意,在我的构建系统列表中,我有 Python 和 Python 3。Python 自动来,运行 2.7。Python3 是我们要添加的。

一旦你点击新建构建系统… 你将被带到一个名为 untitled.sublime-build 的新窗口。

我们需要更改花括号之间的文本。首先删除括号中的代码。接下来复制并粘贴以下代码:

"cmd": ["python3", "-i", "-u", "$file"],     
"file_regex": "^[ ]File \"(...?)\", line ([0-9]*)",     
"selector": "source.python"

您可能需要按 Tab 键来缩进第二行和第三行。现在按下命令 S 保存文件。将该文件重命名为您希望称为构建系统的名称。简明易懂的东西。我把我的命名为 Python3注意,不要改扩展名,必须是。宏伟的建筑。也不要改变路径。 Sublime Text 默认会把它放在正确的路径上。

一旦你点击保存,关闭文件,这样你就回到了你的 scratch.py 文件。现在转到 Tools - > Build System,选择 Python3(或任何您命名的构建系统)。如果你没有看到你的新构建系统,你可能需要退出 Sublime Text 并重新打开它。

现在运行相同的代码来测试您使用的 Python 版本。

这就对了,Python 3.7 开始运行了。编码快乐!

如果你想看我在 Sublime Text 中运行 python 3 的视频教程,请点击这个链接:https://youtu.be/IprbE2C_rsEv

立即在 AWS Spot 实例上运行 StyleGAN2 ADA

原文:https://towardsdatascience.com/run-stylegan2-ada-on-an-aws-spot-instance-in-no-time-d2022fc1e119?source=collection_archive---------33-----------------------

在 AWS 上运行 StyleGAN2,与预训练的模型一起玩,或者从头开始训练一个模型。

示例使用有限数量的训练数据为几个数据集生成图像,使用 StyleGAN2 ADA 进行训练(图像来自“使用有限数据训练生成式对抗网络”论文)。

介绍

最近 NVIDIA 发表了一篇名为“用有限数据训练生成对抗网络的论文,并发布了代码。他们提出了一种自适应鉴别器增强(ADA)机制,可以稳定 StyleGAN2 训练,并在小数据集上取得明显更好的结果。

在本文中,我们将展示如何在 AWS Spot 实例上快速运行这段代码。

启动 AWS Spot 实例

“Spot 实例是未使用的 EC2 实例,其可用价格低于按需价格。因为 Spot 实例使您能够以大幅折扣请求未使用的 EC2 实例,所以您可以显著降低 Amazon EC2 的成本。”

现场实例,AWS 文档

为了启动一个 Spot 实例并在环境中运行 Docker 容器,我们将使用 Spotty 。Spotty 是一个开源工具,旨在简化云中深度学习模型的开发。

使用 pip 安装最新版本:

pip install -U spotty

Spotty 只需要在项目的根目录中有一个描述实例和容器参数的spotty.yaml配置文件。而且我们已经在原回购的这个叉里给你准备好了。

使用以下命令克隆项目:

git clone [https://github.com/spotty-playground/stylegan2-ada](https://github.com/spotty-playground/stylegan2-ada)

默认情况下,Spotty 将在us-east-1区域启动一个p2.xlarge Spot 实例,但是,如果需要,您总是可以在spotty.yaml文件中更改这些参数。

在启动实例之前,请确保您已经安装并配置了 AWS CLI。更多信息,请参见安装 AWS CLI配置基础知识

要启动实例,请从项目根目录运行以下命令:

spotty start aws-1

等待实例启动:

Bucket "spotty-stylegan2-ada-la4gun6uy92a-eu-central-1" was created.
Syncing the project with the S3 bucket...
Creating IAM role for the instance...
Preparing CloudFormation template...
  - volume "stylegan2-ada-aws-1-workspace" will be created
  - availability zone: auto
  - on-demand instance
  - AMI: "Deep Learning AMI (Ubuntu 16.04) Version 35.0" (ami-01955a821cfdfaf73)Volumes:
+-----------+------------+------------+-----------------+
| Name      | Mount Path | Type       | Deletion Policy |
+===========+============+============+=================+
| workspace | /workspace | EBS volume | Retain Volume   |
+-----------+------------+------------+-----------------+Waiting for the stack to be created...
  - launching the instance... DONE
  - preparing the instance... DONE
  - mounting volumes... DONE
  - syncing project files... DONE
  - building Docker image... DONE
  - starting container... DONE+---------------------+---------------+
| Instance State      | running       |
+---------------------+---------------+
| Instance Type       | p2.xlarge     |
+---------------------+---------------+
| Availability Zone   | us-east-1e    |
+---------------------+---------------+
| Public IP Address   | 100.26.50.220 |
+---------------------+---------------+
| Purchasing Option   | Spot Instance |
+---------------------+---------------+
| Spot Instance Price | $0.2700       |
+---------------------+---------------+Use the "spotty sh" command to connect to the container.

就是这样!只需连接到容器并使用代码:

spotty sh aws-1

Spotty 使用 tmux ,所以如果你的互联网连接断开了,你也不会丢失你的进度。有关 tmux 的更多信息,请阅读官方文档Tmux 备忘单

例子

生成 MetFaces 图像

met faces 模型生成的图像。

连接到容器后,使用以下命令通过预先训练的模型生成图像:

python generate.py — outdir=out/metfaces — trunc=1 — seeds=85,265,297,849 \
 — network=[https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada/pretrained/metfaces.pkl](https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada/pretrained/metfaces.pkl)

该命令将在out/metfaces/目录中生成 4 幅图像。您可以使用以下命令将它们下载到您的本地计算机上:

spotty download aws-1 -i 'out/*'

使用 FFHQ 模型将图像投影到潜在空间

在这个例子中,我们将展示如何使用自定义的 Spotty 脚本。

要找到一个潜在向量并生成一个进展视频,将一个目标图像target.jpg放到本地机器上的data/目录中,并运行以下命令:

spotty run aws-1 projector-ffhq -p TARGET=data/target.jpg -p OUTPUT_DIR=out/projection

该脚本将在一个 tmux 会话中运行。因此,即使您的互联网连接断开,该进程仍将运行,您可以在以后的任何时间重新连接它。

脚本完成后,使用Ctrl+b, x组合键关闭 tmux 窗口,并将结果下载到您的本地机器:

spotty download aws-1 -i 'out/*'

完成后,不要忘记停止实例!使用spotty stop命令。如果不再需要这些数据,请删除 EBS 卷。

像海军上将一样管理您的数据科学团队…

原文:https://towardsdatascience.com/run-your-data-science-team-like-an-admiral-f2f313a837fe?source=collection_archive---------36-----------------------

管理一个数据科学团队很难!我们需要从任何可能的地方获取灵感,纳尔逊海军的文化就是一个起点。

勒穆埃尔·弗朗西斯·阿博特的《纳尔逊肖像》

“如果把自己的船放在敌人的船旁边,任何一个船长都不会做错什么” 霍雷肖·纳尔逊上将,特拉法尔加战役前,1805 年

现在请容忍我。我承认这种比较听起来有点牵强。事实上,对西洋镜迷来说,这听起来有点像“法老的商业秘密” [1]。然而,我想说服你,关于领导力的一些事情是基本的,作为管理(或渴望管理)数据团队的人,我们应该从更远的地方寻找如何建立团队文化的灵感。追随胜利者通常是值得的,对吗?

首先,我们应该明白纳尔逊既聪明又勇敢。他在特拉法尔加战役前夕的胜利计划是将他的战船排成两行,向法国人发起冲锋。差不多就是这样。他把击沉法国船只的决定权留给了各个指挥官,只要他们认为合适。最终,英国赢得了一场令人震惊的胜利,确保了英国是世界上占主导地位的海上力量——直到大约一个世纪后美国真正开始行动。

这些都很有趣,但这和数据科学有什么关系呢?

首先,让我们看看隐含的领导风格。尼尔森没有为他的队长们提供一份需要完成的任务清单,也没有试图对他们的时间进行微观管理。相反,他告诉他们“只要你在和敌人战斗,你就是在做正确的事情,我会支持你”。换句话说,他提供了一个广泛的目标,并允许他们做任何他们需要做的事情来实现它。

现在,如果你雇佣了合适的人,你的数据科学家将会变得聪明、积极和独立。也许你决定效仿纳尔逊的做法,为自己制定一个远大的目标:

“如果数据科学家将模型投入生产,他们不会犯很大的错误” 一些数据科学负责人,在 2019 年的某个时候

听起来很棒,对吧?发布指令,然后让一群聪明人想出如何做实际工作。与此同时,你可以早点离开,去高尔夫球场(美国)或酒吧(英国)。

要理解为什么这种方法本身行不通,我们可以回到纳尔逊和他的海军。纳尔逊在海军文化的背景下发布了他的命令,这种文化基于一套共享的技能、经验和信仰。他知道他可以相信他的队长会实现他的目标,因为他知道他们如何思考和战斗。

那么这种共享文化是由什么组成的呢?这些与数据科学相关吗?这是我看到的七个相似之处:

  1. 技能很重要

在纳尔逊的时代,成为一名海军军官的训练要从 12 岁开始。这些孩子在现役战斗舰上服役,在那里他们与一名更有经验的下级军官(他自己通常是青少年)搭档,后者会向他们展示真正的诀窍。

海军军官候补生亨利·威廉·拜恩顿 13 岁(1780 年)。是的,像这样的孩子被放在了火线上。

我找不到这些海军军官候补生的伤亡率,很大程度上是因为似乎没有人对记录它感兴趣。可以说,当幸存者到达指挥位置时,他们从很小的时候就已经积累了大量的技术知识。

你的数据科学团队必须了解大量技术和事实材料。我们将在另一篇文章中回到技术到底是什么。现在让我们说它是软件技能、统计学和机器学习的某种结合。数据科学负责人的部分工作是确保团队拥有这些技能。传统上,这意味着从组织外部雇佣人员。然而,随着数据科学在大型组织中的地位越来越稳固,它也成为毕业生轮换计划或类似计划中年轻内部候选人的热门选择。确保合适的人得到合适的培训是这项工作的一个重要部分。最后,就像尼尔森的队长一样,你必须确信你团队中的每个人都知道并理解一个共享的知识体系。

2。练习,练习,练习

特拉法尔加战役之前,法国被封锁。这就是英国对拿破仑大陆封锁的回应——你们没有封锁我们,是我们封锁了你们!英国船只连续几个月滞留在海上,扰乱了法国的贸易。这实际上是把法国军舰困在港口里。到特拉法尔加战役时,英国水手只是更擅长航海,并不是因为他们有什么天生的优势,而仅仅是因为他们做得更多。

数据科学团队的成员必须通过研究数据科学来提高数据科学水平。这听起来微不足道,但它提出了平衡的难题。你必须推动人们获得适合他们的体验。有些任务是人们想做的,因为他们以前做过,并且擅长做。然后是人们想做的任务,因为他们以前从未做过,希望 擅长它们。最后,有些任务没人想做,因为它们很无聊。不管怎样,你必须确保合适的人得到合适的体验,并且所有必要的工作都完成了。最后,就像英国船上的船长站在海上一样,你需要对你团队的经验充满信心。

3。扁平层级

纳尔逊用莎士比亚的短语“一帮兄弟”来形容他和他的高级队长的关系。他似乎认为自己是平等者中的第一人,历史学家认为这表明了“为国家服务的社会平等”。纳尔逊总是在重大交战前咨询一群船长,以检查和改进他的计划。

作为数据科学的负责人,你必须与团队的高级成员保持良好、开放的关系。他们会知道你不知道的事情,会有你没有的经历。你需要利用这些知识。然而,扁平层级的危险在于,你最终会得到“偶然的层级”,在这种层级中,有人最终会占据主导地位。如果这是一个# iamverysmart【3】的人,他肯定,肯定知道所有的答案,那么你试图培养的开放文化正在被摧毁。在这种情况下,你只需要让某个人成为临时领导,这个人必须具备维持开放性的必要个性。这可能不是那个认为应该是他们的人!我们要达到的目的是尼尔森成功实现的。没有人怀疑纳尔逊拥有舰队的最终指挥权,船长对他们的船只负有责任。然而,这种层级是足够扁平的,以至于计划可以被共同讨论和改进。

4。成功孕育成功

胜利很重要。在特拉法尔加战役之前,英国海军赢得了一系列战役:1794 年 6 月 1 日的辉煌战役、1795 年的格罗依战役、1797 年的圣文森特角战役、1797 年的坎伯唐战役、1798 年的尼罗河战役和 1801 年的哥本哈根战役。到了 1805 年,他们预计会赢,而且他们确实赢了。

作为个人,你的数据科学家应该已经有了“精英”心态。他们可能已经在大学、学术界,或者更好的是在工作场所取得成功,发展了这一点。你的工作是将这转化为精英 团队 的心态。最后,当项目开始出错时,你会看到人们做非凡的事情,因为“交付是我们的工作”。在这种文化中,一个失败的项目是不可想象的。然后,这种成功反馈到团队是成功团队的共同信念中,这产生了更多的努力工作以带来更多的成功,如此循环。正如纳尔逊的海军一样,成功的文化孕育着更多的成功。

5。精英管理规则

好吧,我们不应该过分强调这一点。按照现代标准,纳尔逊和他同时代的人都是“男性,脸色苍白”。然而,以下是对他们背景的描述:

“作为一个乡村牧师的儿子,他(纳尔逊)来自从下层绅士到中产阶级的广泛社会阶层,正是这些阶层为皇家海军提供了如此多的军官。他可能会与他同时代的国旗名单圣文森特,邓肯,康华里,基思,加德纳,甘比尔,科林伍德,和兄弟 Bridport 和胡德;圣文森特的父亲是一个省律师,邓肯的邓迪教务长,加德纳的一名军官,甘比尔的巴哈马副总督,科林伍德的一个商人,和胡德的另一个牧师。【5】**

当大陆海军由贵族子弟领导的时候,英国海军是根据功绩从中产阶级中提拔的。

您的数据科学团队必须保持不变。不管你喜不喜欢,你正处于一场激烈的资源争夺战中。如果你是一个面向内部的团队,会有一堆人想要外包你。如果你是对外面对,会有几十个饥渴的竞争对手。像尼尔森一样,你需要你能得到的最好的人,不考虑任何其他因素。

6。卡在

1757 年 3 月 14 日,海军上将约翰·宾被行刑队处决。根据军事法庭的说法,面对敌人,他“没有尽最大努力”。历史学家认为,Byng 被处决是为了解决启蒙运动观点和更古老、更血腥的战争观点之间的争论,启蒙运动认为战争现在是关于科学的兵力部署(在这种情况下,按照命令在优势敌人面前撤退是合理的行动过程)。更简洁地说,按照伏尔泰的说法,他被处决是为了“鼓励其他人”。不管是什么原因,从 Byng 时代到 Trafalgar 时代及以后,皇家海军的行动经常带有极端侵略性。宁可死在敌人手中,也不要被行刑队羞辱。

当然,你不希望你的数据科学家表现出极端的攻击性。你可能也不会被允许执行其中的任何一个——尽管这听起来很诱人。然而,在每个数据科学项目的某个时候,你会将已经完成的工作抛在身后,不得不依靠你的能力去做一些新的事情。这可能很可怕,但这是工作的一部分!你的团队文化必须鼓励大胆的“能行”精神,鼓励人们迈出未知的第一步。就像纳尔逊的海军一样,当事情看起来很困难时退缩是不能容忍的。

7。资源决定成败

资源对纳尔逊海军的影响可以用下面的图表来概括:

事实证明,钱很重要。详细讨论见[4]。

实际上,到特拉法尔加时代,英国人已经比他们的大陆对手多花了两个半世纪。他们有更多的船,更多的枪和更多的战士。这是他们不断获胜的一个重要原因。

在数据科学的旧时代,传说你可以给一个聪明人一支圆珠笔,一个新的记事本和一台计算机。然后你可以退一步,期待他们创造奇迹。那个世界已经结束了——如果它曾经存在过的话。你的团队在一个由大型科技巨头、经验丰富的外部咨询公司和专注的初创企业提供的 API 组成的宇宙中工作。它将受到可支配资源的限制,受限于工时、计算资源以及花费在数据质量和数量上的资源。因此,你需要对什么是可能的持现实态度,并将你要解决的问题与你必须投入的资源相匹配。最后,就像尼尔森一样,比你的直接竞争对手规模更大会有所帮助——如果这不可能,至少在相同的支出水平上。

这就是为什么你的数据科学团队应该像尼尔森的海军一样的七个原因。归根结底,这不是你为团队设定的广泛目标。这是容易的部分。难的是建立你设定目标的文化和环境,所以你可以相信你的团队会成功实现。

脚注

1如果你不是一个西洋镜迷,你几乎肯定应该是。这是一部英国情景喜剧,在 channel4.com 和网飞播出。《法老的商业秘密》这一集的精彩之处在于,它讲述了一个主角试图让他的白痴商业书籍出版

请看弗朗兹·库切的《为了政党还是国家:爱德华时代英格兰的民族主义和流行保守主义的困境》。是的,我知道纳尔逊不是爱德华七世时代的人,但第一章涵盖了之前的几年。毕竟是历史书。

【3】#我很聪明的人们的闪避诱发震中当然是https://www.reddit.com/r/iamverysmart/

【4】皇家海军从 1653 年特克萨尔战役胜利到 1805 年特拉法尔加战役胜利的全要素生产率。帕特里克·卡尔·奥布莱恩

【5】纳尔逊与英国海军:航海术、领导力、独创性。北美罗杰**

【6】男子气概、军国主义和十八世纪文化,1689-1815。朱莉娅·巴尼斯特

运行你的第一个交易算法!

原文:https://towardsdatascience.com/run-your-first-trading-algorithm-46dbd5b1cb7c?source=collection_archive---------24-----------------------

编码或选择一个算法,回测它,并使用羊驼中的纸张或货币帐户运行它。

菲利普·格利克曼在 Unsplash 上的照片

免责声明:本文内容不构成理财建议。这只是一个关于如何为交易机器人建立基础设施的教程。

动机

一张图片胜过千言万语。请看下面 S&P500 指数和一些组成 ML 401k 投资组合的共同基金的表现比较,并做出你自己的结论。

(来源:https:/finance . Yahoo . com)

S&P500 vs FSELX

S&P500 对雷西斯

S&P500 对 S&P500?

正如你所看到的,这些共同基金的表现与 S&P500 相比并没有那么好。除此之外,获取这些基金的业绩/风险指标并不简单。夏普比率,最大下降,阿尔法,贝塔——祝你好运。

我们不应该盲目地依赖公司来管理我们的财务。归根结底,这是我们的未来,我们对此最感兴趣。

但是谁有时间管理投资组合呢?这就是我们需要自动化的原因。

让我们看看如何部署我们的第一个交易算法。

建筑

你需要:

  • 量子密码。自己写或者在社区搜索。那里有很多好东西,但鉴于目前新冠肺炎市场的波动性,我建议采取市场中性策略(小市场相关性,小贝塔)。
  • PythonAnywhere 账户(你需要 5 美元/月的计划):https://www.pythonanywhere.com/
  • 羊驼账号(免费!)
    https://alpaca . markets

我应该成为一名艺术家。

建立基础设施

  1. 羊驼

一旦您创建并验证了您的羊驼账户,点击“转到纸质账户”并点击“您的 API 密钥”中的“查看”按钮。记下 key_id、secret_key 和 base_url。

  1. PythonAnywhere

创建虚拟环境

《时代》杂志 2015 年虚拟现实封面催生了一个经典迷因。Pic: @PellyNV

我们先用 python 3.6 创建一个虚拟环境,安装 pylivetrader。

Pylivetrader 是让羊驼理解量子算法的软件包。这个包允许您获得 Quantopian 和 Alpaca 的算法订单执行的回溯测试功能,所有这些都与 PythonAnywhere 粘合在一起,同时在云中不间断地运行。

在创建和升级你的账户(最便宜的计划)后,你需要用 Python 3.6 创建一个虚拟环境并安装 pylivetrader。

在 PythonAnywhere 上打开 bash 并键入以下命令:

$ python3.6 -m venv venv
$ source venv/bin/activate
(venv)$ pip install pylivetrader

注意 pylivetrader 将只与 Python 3.6 一起工作。

安装在虚拟环境中的所有包只能在同一个虚拟环境中访问,这里称为' venv '。' venv '内部的 Python 版本也将被限制为 3.6(在这种特殊情况下);然而,您仍然可以使用' venv '之外的其他 python 版本。为此,只需输入 deactivate。

(venv)$ deactivate

您可以通过键入以下内容直接跳回虚拟环境:

$ source venv/bin/activate

上传量子算法

关闭 bash 并转到“文件”,创建一个名为 algo.py 的新空文件,并粘贴以下代码:

from pylivetrader.api import order_target, symbol

def initialize(context):
    context.i = 0
    context.asset = symbol('AAPL')

def handle_data(context, data):
    # Compute averages
    # data.history() has to be called with the same params
    # from above and returns a pandas dataframe.
    short_mavg = data.history(context.asset, 'price', bar_count=100, frequency="1m").mean()
    long_mavg = data.history(context.asset, 'price', bar_count=300, frequency="1m").mean()

    # Trading logic
    if short_mavg > long_mavg:
        # order_target orders as many shares as needed to
        # achieve the desired number of shares.
        order_target(context.asset, 100)
    elif short_mavg < long_mavg:
        order_target(context.asset, 0)

保存并关闭它。我们需要一个配置文件来访问羊驼;还记得 api_key 和 secret 吗?我们现在需要它们。

创建一个名为 config.yaml 的新文件,并添加以下定义 :

key_id: 'your key_id'
secret: 'your secret'
base_url: [https://paper-api.alpaca.markets](https://paper-api.alpaca.markets)

保存并关闭它。我们差不多完成了:最后要做的事情是安排一个将在云中不间断运行的任务。

转到“任务”,一直向下滚动到“始终在线任务”,然后键入以下命令:

source venv/bin/activate && pylivetrader run -f algo.py --backend-config config.yaml

稍后,任务将开始运行:

最后,进入你的羊驼纸账户,检查订单是否通过。

一旦你准备好用真钱开始,为你的钱帐户获得 api 密钥和秘密,并更新 config.yaml 文件。

结论

在本文中,我们使用 PythonAnywhere 和 Alpaca 完成了运行 Quantopian 算法的步骤。

根据您的算法,您可能需要包含更多的导入以使其工作。您可以在这里找到每种情况下需要导入的包的列表:

https://alpaca . markets/docs/alpaca-works-with/zip line-to-pylivetrader/

最后,如果你决定运行你的羊驼货币账户,我强烈建议使用市场中性算法(空头和多头头寸比例相等)。我的苹果股票布林线交易算法一直很好地应对了冠状病毒危机引起的波动。可以在这里查看:https://towards data science . com/back testing-bollinger-bands-on-apple-stock-using-quanto pian-6be 2e 4824 f 43

我希望你喜欢这篇文章!请不要犹豫评论或分享您的结果。

谢谢

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

在 Docker 中运行您的本地数据库!🐳

原文:https://towardsdatascience.com/run-your-local-database-in-docker-3e7ed68a50f3?source=collection_archive---------7-----------------------

用于本地开发的 Docker 数据库映像:

如何使用 docker 建立本地开发数据库?快速简单。

照片由 Ishant MishraUnsplash 拍摄

当建立一个新项目时,有时会有一个全面的先决条件列表,以便能够运行一个项目。在这个简短的教程中,我们将使用 docker 映像建立一个本地数据库。例如,我们将使用 PostgreSQL,但是您可以自由选择您的数据库。

先决条件:你首先也是唯一需要的就是 Docker。我下载了 Docker 桌面以便在我的 mac 上开始使用(你可以自由使用你喜欢的任何 Docker 安装)。

我们开始吧!🙌

✌️My 3 个主要原因

我使用 docker 数据库映像进行本地开发的三个主要原因,以及我认为您也应该这样做的原因:

  1. 它使本地开发设置变得容易。一个简单的抽象,使用一行设置来设置数据库!
  2. 它不要求您在计算机上安装任何数据库(或数据库的特定版本)。
  3. 它隔离了数据库环境。它允许我同时运行多个版本和实例,而不会遇到麻烦。

我通常将它作为选项添加到我的README文件的项目设置中。然后人们可以决定做什么——要么用手动方式做,要么用简单的方式做😄

您并不局限于使用 PostgreSQL 映像docker 拥有所有常见数据库的数据库映像,如 Redis、MySQL、Oracle 等。找到你的最爱。

让我们装运那个集装箱吧!

我们将使用 Postgres 映像启动一个新的 docker 容器来运行数据库:

$ **docker run --name my_postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres**

这就对了。我们基本上完成了——在一行中,您已经设置好了所有的东西😄 🤷‍♀(如果你没有安装任何图像,docker 会问你是否要下载)。让我们看看我们刚刚创建的内容:

  • --name my postgres创建一个名为my_postgres的 docker 容器。
  • -p是设置端口的标志。5432:5432将内部 docker 端口映射到外部端口,这样我们可以有一个从外部到 docker 映像的门。端口5432是一个 Postgres 特定的端口——如果使用另一个数据库映像,您可能需要更改它。
  • -e是环境变量的标志。我们加上了POSTGRES_PASSWORD
  • 最后一部分postgres是我们将使用的 docker 图像(如果您使用另一个数据库,您应该更改它)

现在您有了一个名为my_postgres的 docker 容器,它在端口 5432 上运行 PostgreSQL 数据库。数据库将nameusernamepassword设置为 postgres

👩‍💻一些细节

好了,让我们继续看一些 docker 标志和命令:

  • -d如果你想在后台运行你的数据库作为守护进程。如果没有d标志,您需要让您的容器一直在一个单独的终端中运行,以访问您的数据库。您可以通过运行docker start <docker_name>docker stop <docker_name>来启动和停止容器。
  • docker postgres:latest如果你想确保使用最新的 Postgres 镜像。
  • docker pull postgres拉最新的 Postgres 图片。
  • docker images列出您电脑上的所有图像。
  • docker ps -a查看所有创建的容器(具有指定端口的容器是运行中的容器)。
  • docker rm <docker_ID>docker rm <docker_name>移除码头集装箱。
  • 从 docker 容器中清除所有数据库数据。即使您有几个 docker 容器在运行,您仍然会访问相同的数据,因为它被写入相同的位置。

💅哪里可以欣赏我的新数据库?

您可以选择您喜欢的数据库 GUI 工具并连接到您的数据库。我喜欢用 DBeaver 使用以下命令连接到我们在 Docker 容器中运行的新创建的数据库:

  • 数据库名称: postgres
  • 数据库用户: postgres
  • 数据库密码: postgres
  • 端口: 5432

如果您喜欢在终端中使用数据库,您可以在 docker 容器中输入以下命令来访问数据库(我们的 docker 在本教程中被命名为 my_postgres ):

$ **docker exec -it <DATABASE_NAME> bash**

🎉快乐编码
🐳你会开始使用 docker 数据库镜像来进行本地设置吗?

如何将你的 ML 模型预测速度提高 50 倍?

原文:https://towardsdatascience.com/run-your-machine-learning-predictions-50-times-faster-3ad2f4ee5819?source=collection_archive---------9-----------------------

图片由来自 Pixabay斯蒂芬·凯勒拍摄

使用蜂鸟只用了 2 行代码

随着这么多计算和服务框架的出现,开发人员将一个模型投入生产的压力与日俱增。如果哪种模型在我的数据上表现最好的问题还不够,现在的问题是选择什么框架来服务于用 Sklearn 或 LightGBM 或 PyTorch 训练的模型。每天都有新的框架加入进来。

那么,对于一个数据科学家来说,是否有必要学习一个不同的框架,因为一个数据工程师对此很熟悉,或者相反,一个数据工程师是否需要学习一个数据科学家喜欢的新平台?

再加上这些不同框架提供的速度和性能因素,问题突然变得更加复杂。

所以,当我最近在 Github 上偶然看到蜂鸟项目时,我感到惊喜,这个项目旨在回答这个问题,或者至少朝着正确的方向迈出了积极的一步。

那么,蜂鸟是什么?

根据他们的文件:

Hummingbird 是一个用于将训练好的传统 ML 模型编译成张量计算的库。蜂鸟允许用户无缝地利用神经网络框架(如 PyTorch )来加速传统的 ML 模型。

得益于蜂鸟,用户可以受益于:

(1)在神经网络框架中实现的所有当前和未来的优化;

(2)原生硬件加速;

(3)具有支持传统和神经网络模型的独特平台,并且具有所有这些

(4)无需重新设计他们的模型。

更简单地说:现在,您可以将用 Scikit-learn 或 Xgboost 或 LightGBM 编写的模型转换成 PyTorch 模型,并在推理时获得 Pytorch 的性能优势。

到目前为止,这里是蜂鸟支持的 操作者的列表还会有更多。

简单的例子

我们可以从安装蜂鸟开始,简单如:

pip install hummingbird-ml

为了使用 hummingbird,我将从一个小型随机分类数据集的最小示例开始。我们首先创建一个包含 100,000 行的样本数据集,并在此基础上使用 RandomForestClassifier。

import numpy as np
from sklearn.ensemble import RandomForestClassifier
from hummingbird.ml import convert# Create some random data for binary classification
from sklearn import datasetsX, y = datasets.make_classification(n_samples=100000, n_features=28)# Create and train a model (scikit-learn RandomForestClassifier in this case)skl_model = RandomForestClassifier(n_estimators=1000, max_depth=10)
skl_model.fit(X, y)

hummingbird 帮助我们的是通过使用简单的命令将这个 sklearn 模型转换成 PyTorch 模型:

# Using Hummingbird to convert the model to PyTorch
model = convert(skl_model, 'pytorch')
print(type(model))
--------------------------------------------------------
hummingbird.ml._container.PyTorchBackendModel

现在,我们可以使用以下代码将新的 Pytorch 模型加载到 GPU:

model.to('cuda')

这太棒了。因此,我们可以从 sklearn 模型转换为 PyTorch 模型,这应该在 GPU 上运行得更快。但是增加多少呢?

让我们来看一个简单的性能对比。

比较

1.成批处理方式

我们将首先使用 sklearn 模型来预测整个训练数据集,并检查它所花费的时间。

我们的新 PyTorch 型号也可以做到这一点:

那就是 9580/195 ~ 50x 的加速。

2.单实例预测

我们在这里预测一个例子,看看这个模型在实时环境中的表现。sklearn 模型:

vs. Pytorch 模型

这也是 79.6/1.6 ~ 50 倍的加速比。

小警告

我经历的一个小警告是,sklearn 模型和蜂鸟 PyTorch 模型的预测并不完全相同。

例如,下面是我从两个模型中得到的预测:

是的,有时,它们在第 7 位数字上不同,这可能是转换过程的一个函数。我认为它不会改变最终的 1 或 0 预测太多。我们还可以检查:

scikit_1_0 = scikit_preds[:,1]>0.5 
hb_1_0 = hb_preds[:,1]>0.5 
print(len(scikit_1_0) == sum(scikit_1_0==hb_1_0))
------------------------------------------------------------
True

因此,在这种情况下,两个模型对 100,000 行的整个数据集给出了完全相同的 1 或 0 预测。

所以我想没关系。

结论

微软的开发人员仍在致力于添加更多的操作符,从模型到特性工程,如代码中的MinMaxScalerLabelEncoder,我希望他们能进一步开发和改进这个项目。如果你感兴趣,这里是发展的路线图。

虽然 Hummingbird 还不完美,但它是第一个能够运行经典 ML 推理 DNN 框架的系统,并证明它们足够成熟,可以用作通用编译器。当涉及到在高吞吐量或延迟时进行预测时,我会尝试将它包含在我的开发工作流中。

你可以在我的 GitHub 库找到这篇文章的代码以及我所有的文章。

继续学习

如果你想了解更多关于构建机器学习模型并将其投入生产的知识,这个关于 AWS 的课程可以满足你的要求。

谢谢你的阅读。将来我也会写更多初学者友好的帖子。在媒体关注我,或者订阅我的博客了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系

此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。

使用 OpenDataHub、OCS 和外部 Ceph 集群运行 Spark 数据处理工作负载

原文:https://towardsdatascience.com/run-your-spark-data-processing-workloads-using-opendatahub-ocs-and-an-external-ceph-cluster-8922f166f884?source=collection_archive---------32-----------------------

evUnsplash 上的照片

Kubernetes 已经成为事实上的标准容器编排平台。通过这种方法,组织试图围绕 Kubernetes 收集所有的应用程序和平台,以利用其稳定性、敏捷性和简单性。在 Kubernetes 中运行您的整个堆栈将允许您拥有一个单一的 API 和一种通用语言,无论是用于需要部署的应用程序、数据库还是存储引擎。

几年前,人们认为,为了获得更高的大数据工作负载性能,您的应用程序需要主要基于闪存介质的高性能本地磁盘。将计算和存储放在一起带来了自身的挑战,主要是在出现故障和升级时,因为组织必须将它们作为一个整体来对待。

今天,我们看到许多数据处理引擎,如 Spark、Presto 等使用 S3 作为存储后端。使用 S3 处理大数据工作负载有多种原因:

  • S3 是一个面向吞吐量的存储系统,可以支持来回传输大量数据
  • S3 是基于 HTTP 的,当必须开发一个连接器来访问它时,它是非常可移植的
  • S3 拥有自己的智能数据管理功能,如存储桶生命周期、存储类别、版本控制等,可以减轻最终用户的数据管理负担

今天,我想和你重点谈谈如何使用 S3 存储后端在 Kubernetes 上运行你的数据处理引擎。为此,让我们简单介绍一下我们将要使用的平台:

  • Ceph —将用作 Openshift 集群外部的 S3 网关
  • Openshift 集装箱存储—将为我们提供类似 Kubernetes 的功能来处理我们的 S3,将在稍后讨论
  • 开放式数据中心——将用作我们的 spark 集群和 Jupyter 笔记本电脑的数据处理供应器,以运行我们的 Spark 工作负载

先决条件

  • 正在运行的 Openshift 4.5 集群
  • 正在运行的 RHCS 4.1 集群
  • 这两者之间的网络连接

我们要部署的第一个组件是 OCS (Openshift Container Storage ),它将成为我们 S3 存储后端的 Kubernetes 管理平台。使用昨天发布的 OCS 4.5,您可以将外部 Ceph 集群连接到 OCS 管理平面,以利用其资源。这种部署方法称为“独立模式”,主要用于正在处理的工作负载和容量超过 OCS 内部模式处理能力的情况。(内部模式将在 Openshift 上运行您的 Ceph 集群,这对于中小型数据工作负载非常有用)。

让我们连接到 Openshift 集群,进入Operator Hub选项卡,并安装 OCS 操作符。该运营商将部署我们的 OCS 管理平台,并将连接到外部 Ceph 集群:

安装好操作员后,让我们创建一个独立的集群,为此我们点击Create Storage Clusterindependent mode。这将要求在我们的 Ceph 集群上运行一个脚本,该脚本将收集将 OCS 连接到我们的外部 Ceph 集群所需的所有信息。该脚本将抛出一个 JSON 文件到 STDOUT,请将它粘贴到代码片段中。

创建集群后,我们应该让它处于Ready状态:

现在我们已经准备好了 S3 后端,让我们创建一个Object Bucket Claim,这样我们的 Spark 应用程序就可以使用它来处理数据。OBC(对象桶声明)是一种将桶视为 Kubernetes 持久卷声明的方式。这个对象对于 OCS 是唯一的,并且减轻了开发人员跟踪其凭证、bucket 名称和端点 URL 的需要。要创建 bucket claim,只需转到 Openshift 控制台中的Object Bucket Claims(存储选项卡下),创建一个 OBC 并选择 RGW 存储类作为目标。

这种自动化将在我们的外部 Ceph 集群中创建一个用户和一个存储桶,并将所有信息存储在 ConfigMaps 中,并将机密存储在我们的 Openshift 集群中。凭据将存储为机密,而存储桶名称、存储桶端口和端点 URL 将存储为 ConfigMap。

为了验证确实创建了 bucket,让我们使用radosgw-admin命令访问我们的 Ceph 集群并列出我们拥有的 bucket:

$ radosgw-admin bucket list | grep spark
    "spark-bucket-1143d1c8-e321-496a-821c-9c1b89297685"

我们看到我们有一个由 OBC 创建的存储桶,现在让我们尝试获取有关我们创建的存储桶的更多信息:

$ radosgw-admin bucket stats --bucket spark-bucket-1143d1c8-e321-496a-821c-9c1b89297685
{
    "bucket": "spark-bucket-1143d1c8-e321-496a-821c-9c1b89297685",
    "num_shards": 11,
    "tenant": "",
    "zonegroup": "c6f894d0-256a-425f-92ec-b5c41366c1cb",
    "placement_rule": "default-placement",
    "explicit_placement": {
        "data_pool": "",
        "data_extra_pool": "",
        "index_pool": ""
    },
    "id": "9cdf5d28-ceb4-4629-b507-13509f8c99ab.84164.2",
    "marker": "9cdf5d28-ceb4-4629-b507-13509f8c99ab.84164.2",
    "index_type": "Normal",
    "owner": "ceph-user-bbX0Qdrn",
    "ver": "0#1,1#1,2#1,3#1,4#1,5#1,6#1,7#1,8#1,9#1,10#1",
    "master_ver": "0#0,1#0,2#0,3#0,4#0,5#0,6#0,7#0,8#0,9#0,10#0",
    "mtime": "2020-09-17 14:15:12.993277Z",
    "max_marker": "0#,1#,2#,3#,4#,5#,6#,7#,8#,9#,10#",
    "usage": {},
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    }
}

我们看到还创建了一个新用户(在Owner部分下)。现在,让我们验证我们的信息是否如保证的那样位于我们的 Openshift 集群中。让我们描述一下我们称为spark-bucket的 OBC 对象:

$ oc describe secret spark-bucket

Name:         spark-bucket
Namespace:    amq-streams
Labels:       bucket-provisioner=openshift-storage.ceph.rook.io-bucket
Annotations:  <none>
Type:  Opaque
Data
====
AWS_ACCESS_KEY_ID:      20 bytes
AWS_SECRET_ACCESS_KEY:  40 bytes

我们看到,我们将访问密钥和秘密密钥作为秘密存储在 Openshift 集群中。现在让我们做同样的事情,描述配置图,看看我们是否有其余的信息:

$ oc describe cm spark-bucketName:         spark-bucket
Namespace:    amq-streams
Labels:       bucket-provisioner=openshift-storage.ceph.rook.io-bucket
Annotations:  <none>Data
====
BUCKET_NAME:
----
spark-bucket-1143d1c8-e321-496a-821c-9c1b89297685
BUCKET_PORT:
----
8080
BUCKET_REGION:
----
us-east-1
BUCKET_SUBREGION:
----BUCKET_HOST:
----
10.32.0.3
Events:  <none>

太好了!我们已经获得了所需的信息,这样我们的 Spark 应用程序就可以到达我们的 S3 后端。让我们创建一个名为odh的新项目,它将存储Open Data Hub工作负载。

$ oc new-project odh

之后,我们将安装 Open Data Hub operator,这样我们就可以启动和配置我们的 Spark 集群:

在我们成功安装了 ODH 操作符之后,我们将创建一个Open Data Hub定制资源,它将提供所有需要的对象供我们使用。创建 CR 后,将为您的Jupyter Hub笔记本创建一条路线,从s2i-spark-minimal-notebook:3.6映像创建一个新笔记本。

创建这个笔记本将创建一个 spark 集群,其中的每一个 pod 都充当一个 Spark executor。这还将创建一个笔记本数据库,用于存储笔记本中保存的所有信息。这是与您的用户 1:1 的关系,所以下次您登录时,您将看到相同的笔记本。

现在让我们看看豆荚是否真的被创造出来了:

$ oc get podsNAME                                    READY   STATUS      RESTARTS   AGE
jupyterhub-1-2bglz                      1/1     Running     0          17m
jupyterhub-1-deploy                     0/1     Completed   0          17m
jupyterhub-db-1-72fbr                   1/1     Running     0          17m
jupyterhub-db-1-deploy                  0/1     Completed   0          17m
jupyterhub-nb-kube-3aadmin              2/2     Running     0          14m
opendatahub-operator-6c96795b8b-kmhhh   1/1     Running     0          19m
spark-cluster-kube-admin-m-9w69r        1/1     Running     0          14m
spark-cluster-kube-admin-w-wb54g        1/1     Running     0          14m
spark-cluster-kube-admin-w-x5zn9        1/1     Running     0          14m
spark-operator-74cfdf544b-mrdzf         1/1     Running     0          17m

太好了!我们有自己的基础设施。现在让我们验证我们的Jupyter Notebook是持久的:

$ oc get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                STORAGECLASS                              REASON   AGE
pvc-6ec75973-a17a-44d6-b308-42cc4c4664fd   1Gi        RWO            Delete           Bound    odh/jupyterhub-db                    ocs-independent-storagecluster-ceph-rbd            43m
pvc-b3064182-ef7c-434f-a3f3-10c8f198a7d8   2Gi        RWO            Delete           Bound    odh/jupyterhub-nb-kube-3aadmin-pvc   ocs-independent-storagecluster-ceph-rbd            39m

作为奖励,ODH 运营商将为我们的笔记本电脑数据库连接一个 PVC,该 PVC 取自存储在我们外部 Ceph 集群中的 RBD 池,我们在一个系统中获得了两个存储协议,多么令人兴奋!

让我们简单介绍一下我们的 Spark 工作负载。在这个工作负载中,我们将上传一个包含学生成绩的 CSV 文件到我们的 S3 存储桶。这个 CSV 文件包含了学生的名字,以及他们在 4 个年级的分数,也就是最后的成绩。我们的目标是使用火花处理为每个等级收集频率。

让我们来看看我们的 Jupyter 笔记本:

对我们在这里看到的做一点解释:

  • 在第一阶段,我们检查 spark 集群中的连通性,输出以 Spark 执行器的名字打印,可以与 pods 运行相关联。
  • 稍后,我们使用wget将 CSV 文件下载到我们的本地笔记本中,并将其保存在我们的 DB 中
  • 我们使用boto库,以便使用从我们的 bucket claim 收集的信息将 CSV 文件上传到我们的 S3 bucket
  • 然后,我们使用相同的变量来设置 Spark s3a连接器使用的配置
  • 我们从我们的 S3 桶中读取 CSV 文件并打印分数。注意了!我们有一个错误的值,即第 9 个单元格中的“A ”,这会影响我们数据的可靠性
  • 我们清理这个值,最后建立一个等级频率图

结论

我们看到了如何利用 Openshift 平台,以一种简单、可访问的方式运行我们的 Spark 处理工作负载。利用 OCS 的优势,我们简化了处理 S3 存储后端的方式,并获得了双存储协议存储引擎解决方案来存储我们的数据集和笔记本电脑的数据库。希望你喜欢这个演示,下次再见:)

使用 Argo 和 Singer.io 在 Kubernetes 上运行数据复制管道

原文:https://towardsdatascience.com/running-a-data-replication-pipeline-on-kubernetes-with-argo-and-singer-io-2fab5b0bad?source=collection_archive---------21-----------------------

近年来,数百个数据团队已经迁移到 ELT 模式,利用 Stitch 或 FiveTran 等 SaaS 工具将数据可靠地加载到他们的基础设施中。根据我的经验,这些 SaaS 产品非常出色,可以显著加快您的生产时间。然而,许多人没有预算,或者没有需要适应的定制应用程序,或者只是喜欢使用自己的工具。

在这些情况下,一个解决方案是部署singer.iotap 和 targets——可以在任意源和目的地之间执行数据复制的 Python 脚本。Singer 规范是流行的 Stitch SaaS 的基础,它也被许多独立顾问和数据项目所利用。

Singer 管道是高度模块化的。您可以通过管道将任何分支连接到任何目标,以构建符合您需求的数据管道。尽管这使得它们非常适合 Docker 化管道,但我发现很难找到通过 Kubernetes 或 Docker 部署 Singer 管道的例子。最终,我们利用 Argo 工作流和容器化的歌手抽头和目标建立了一个管道。

没有 Argo 和 Kubernetes 的容器编排。图片来自 UnsplashCHUTTERSNAP

本文从较高的层面介绍了工作流,并提供了一些示例代码来启动和运行一些共享模板。我假设你对 DockerKubernetesSinger 规范有些熟悉。即使你是这些技术的新手,我也会试着指出一些有用的资源,为你指明正确的方向。

为什么要自己卷?

ETL 不是任何人进入数据科学或工程的原因。几乎没有创造力,大量的维护,直到出了问题才得到认可。幸运的是,像 StitchFiveTran 这样的 SaaS 工具已经将数据复制变成了小团队可以利用的商品。

数据复制的“已解决”性质使数据科学家更容易拥有端到端的项目,从而使数据工程师能够考虑“平台”而不是单点解决方案。StitchFix 在这方面有一个很棒的帖子。)这个市场的参与者认识到,真正与众不同的是“围绕”集成脚本的东西:例如,GitLab 的 Meltano 项目已经找到了一个合适的位置,成为集成过程的“跑步者”,而不是数据复制服务的逻辑。

歌手轻拍目标

当不存在受支持的连接器时——比方说,对于内部应用程序——第三方服务不会为您做太多事情。这时候 Singer.io 规范就派上用场了。Singer 管道有两个组成部分:

  • tap 连接到服务,提取数据,然后使用 JSON 发出标准化的模式和记录流
  • 目标读取 tap 的记录流并将其加载到仓库中

这种 tap 和 target 的分离分离了“提取”步骤和“加载”步骤。因此,一个拥有 10 个数据源和 1 个数据仓库的组织需要管理 10 个从 tap 到 target 的管道。但是,如果他们迁移这个数据库,他们只需要“换出”目标;他们不会对任何水龙头做任何改动。

Kubernetes 和 Argo 工作流程

然而,如果没有编排层,这些管道一文不值。我们致力于在 Kubernetes 上运行管道,这使我们来到了 Argo,一个用于编排并行“工作流”的开源引擎。它是其他编排工具(如 Airflow 或 Prefect)的替代工具,一个关键的区别在于它对容器的固有关注。这个数据委员会的演讲提供了一个与 Airflow 的很好的比较,特别是:Kubernetes——Argo 的本地工作流编排

我们对我们的解决方案非常满意,但它并不适合所有人。调试 Kubernetes 作业可能比在专用 EC2 实例上调试 Python 脚本更具挑战性。然而,这种方法的回报是三重的:

  1. 部署的简易性和可移植性
  2. 容器内脚本/环境的标准化
  3. 其他数据基础设施也可以很容易地进行容器化和部署

说到这里,让我们试一试吧!

教程

在本教程中,我们将首先使用 Argo 和 MinIO 存储为工件和配置文件设置一个本地 Kubernetes 集群,然后我们将部署一个 Singer tap-to-target 工作流。最后,我们将讨论增强和扩展。

我们可以深入讨论这些领域中的任何一个,但是我会尽量让它变得浅显易懂,把对“生产”考虑的进一步讨论留到最后。本教程有以下先决条件:

  1. Docker 桌面。安装在本地,并打开 Kubernetes 功能。(如果您有另一个集群,也应该没问题。)
  2. 掌舵。我们将安装 Argo 和 MinIO 来运行本教程。

首先,我们将使用两个最简单的 singer.io 包——tap-exchangeratesapitarget-csv——来演示这是如何工作的。下面的图片会让你对我们的发展方向有所了解:

完整的工作流模板。图片作者。

设置 Kubernetes、Argo 和 MinIO

在第一部分中,我们需要设置一个 Kubernetes 集群、Argo 工作流、一个工件存储库和几个存储桶。最简单的方法是使用 Docker for Desktop,它是多平台的,可以在其中部署一个 Kubernetes 集群。这也使本地开发变得更加容易,因为您可以构建容器并部署到本地集群,而无需推到外部容器存储库。

安装 Docker 桌面并启用 Kubernetes

你需要有一个 Kubernetes 集群,你有管理权限,可以用kubectl部署资源。如果你已经有了,那么你可以跳过这一节。如果你没有,最简单的方法就是使用 Docker for Desktop。你会想要遵循 Docker 网站上的文档。

  1. 为桌面安装 Docker。[ Mac
  2. 启用 Kubernetes。[ Mac
  3. 将 Docker 的资源分配提升到至少 12 GB,以确保 MinIO 部署有足够的空间。(见附近图片。)
  4. 测试kubectl命令。[ Mac
  5. 安装最新的 Argo CLI。[ Mac

您可能需要增加默认资源来容纳 MinIO。图片作者。

安装 Argo 工作流程

要安装 Argo,您可以遵循他们的[ 快速入门指南 ],或者简单地使用下面的命令在您的集群中创建一个argo名称空间并部署“快速入门”资源。

kubectl create ns argokubectl apply \
   -n argo    \
   -f https://raw.githubusercontent.com/argoproj/argo/stable/manifests/quick-start-postgres.yaml

您现在应该有一个 Argo 服务器和一些 Argo 使用的新 Kubernetes 资源类型,包括“Workflow”和“CronWorkflow”。要查看 Argo Workflows UI,您可以将 Kubernetes 端口转发到您的本地主机,并在浏览器中查看它。

kubectl -n argo port-forward deployment/argo-server 2746:2746

Argo 工作流为检查作业提供了一个有用的用户界面。图片作者。

设置 MinIO 存储器

Argo 工作流可以通过使用“工件”将文件传入或传出容器。对于本地部署,配置工件传递的一个简单方法是通过 Kubernetes 部署 MinIO 。Argo 有很多关于如何与亚马逊 S3 等其他服务建立这种关系的指南,但是你可以跟随下面的内容快速部署 MinIO。

注意,您需要安装helm(本质上是一个 Kubernetes 包管理器)。在 Mac 上,你可以使用 Homebrew 命令:brew install helm。然后,运行以下命令来添加 MinIO helm 图表:

helm repo add minio [https://helm.min.io/](https://helm.min.io/)helm repo update

接下来,您需要将 MinIO 部署到 Argo 名称空间中。这可能需要一两分钟,请耐心等待。

helm install argo-artifacts minio/minio \
    -n argo                             \
    —-set service.type=LoadBalancer     \
    --set defaultBucket.enabled=true    \
    --set defaultBucket.name=artifacts  \
    --set persistence.enabled=false     \
    --set fullnameOverride=argo-artifacts

您现在应该有一个工件服务器在运行,但是它是空的!我们必须创建一个工件桶,以及一个用于 singer 配置和输出的桶。要使用下面的命令,您需要 MinIO CLI 工具,您可以使用:brew install minio/stable/mc安装该工具。

mc config host add argo-artifacts-local [http://localhost:9000](http://localhost:9000) YOURACCESSKEY YOURSECRETKEYmc mb argo-artifacts-local/artifacts
mc mb argo-artifacts-local/singer
mc mb argo-artifacts-local/outputs

如果您喜欢通过 UI 创建 bucket,那么您可以转到端口 9000 — kubectl -n argo port-forward service/argo-artifacts 9000:9000 —然后创建一个artifactssinger bucket。(请注意,默认凭据是 YOURACCESSKEY 和 YOURSECRETKEY。)

将 Argo 和 MinIO 一起映射

最后,我们需要告诉 Argo 默认的工件存储库在哪里,这样它就知道工件映射到哪个存储桶,并有适当的秘密进行认证。你可以遵循其他地方的说明但是为了简单起见,我建议将这个“补丁”应用到我整理的资源中。

wget -o patch.yml [https://raw.githubusercontent.com/stkbailey/data-replication-on-kubernetes/master/argo/argo-artifact-patch.yml](https://raw.githubusercontent.com/stkbailey/data-replication-on-kubernetes/master/argo/argo-artifact-patch.yml)kubectl patch configmap workflow-controller-configmap \
    -n argo \
    --patch “$(cat patch.yml)”

为了确保第一部分中的所有内容都能正常工作,请尝试运行 Argo 示例库中的“工件传递”示例。

argo submit -n argo [https://raw.githubusercontent.com/argoproj/argo/master/examples/artifact-passing.yaml](https://raw.githubusercontent.com/argoproj/argo/master/examples/artifact-passing.yaml) --watch

蓉城工作流程

你已经得到了库贝内特斯,你已经得到了阿尔戈:让我们不要再等了!运行以下命令:

argo submit \
    -n argo \
    --watch \
    [https://raw.githubusercontent.com/stkbailey/data-replication-on-kubernetes/master/argo/tap-exchange-rate-workflow.yml](https://raw.githubusercontent.com/stkbailey/data-replication-on-kubernetes/master/argo/tap-exchange-rate-workflow.yml)

您应该会在终端中看到一个工作流启动。(或者,您可以转到该链接,复制文本并将其粘贴到 Argo Workflows 的“新工作流”UI 中。)如果您收到类似于failed to save outputs: timed out waiting for condition的错误消息,请尝试再次运行该工作流程——可能是 MinIO 设置未完成。

寻找那些漂亮的绿色对勾。图片作者。

您应该看到一个两步工作流创建和完成。现在,检查“输出”存储桶,看看有什么可用。

mc ls argo-artifacts-local/singer/outputs/tap-exchange-rates/

在大多数 Singer 工作流中,管道的输出实际上是加载到数据库中的数据——但是在这里,我们将 tap 输出导出到一个文件中,该文件由目标压缩并输出到 MinIO 中。

剖析工作流程

现在,让我们从头开始,看看 Argo 工作流刚刚发生了什么。

  1. 它启动了一个tap-to-target多步骤工作流,由两个子步骤组成:taptarget步骤。
  2. 它启动了tap子步骤,并传入一些配置文件,这些文件被映射到/tmp/config.json。当 tap 步骤完成时,/tmp/tap_output.txt处的输出文件存储在默认的 MinIO 工件库中。
  3. 然后启动target子步骤,将配置和 tap 输出映射到容器文件系统中。当目标运行时,输出(来自 CSV 目标)被映射到outputs MinIO 桶。

模板逻辑。虽然“target-csv”目标会输出一个表,但大多数目标会将数据加载到数据库中。图片作者。

我不会深入到工作流文件本身,因为 Argo 上有很多很棒的文档。关于数据复制,有趣的是,这个过程是高度标准化的——无论您的 tap 或目标是什么,您基本上都是交换配置文件或容器地址。

码头歌手

在 Argo 级别模板化工作流也需要在容器级别模板化 Singer taps。让我们暂时把 Kubernetes 放在一边,专注于一个典型的歌手管道。这是一个线性过程:

  1. 点击输入:点击配置文件、目录文件、状态文件
  2. 点击输出:提取数据的文本输出(Singer 格式)
  3. 目标输入:目标配置文件,tap 的文本输出
  4. 目标输出:加载或导出的数据(例如,到数据库或 CSV 文件)和一个“状态”文件,其中包含有关上次提取的元数据

我们可以将容器本身视为黑盒,只要我们知道如何输入适当的输入和输出。为了使本教程简单,我预先创建了taptarget容器供我们使用。要进行测试,请运行以下命令:

docker run -e START_DATE=2020-08-01 stkbailey/tap-exchange-rates

不涉及太多细节,我们将 singer tap-abctarget-xyz命令封装在一个 Runner 对象中,该对象定义了在哪里查找必要的文件。(它可以在 Github 的 singer-container-utils 获得。)剥那只猫皮的方法有很多种,但重要的是要让它在多次轻拍中容易重现。

我们不会深究细节,但是每个 Docker 容器在初始化时都在运行一个 Python entrypoint.py脚本。入口点脚本的要点是:

  1. 确定适当的输入文件所在的路径。
  2. 基于文件可用性构建可执行命令。
  3. 运行命令并将输出写入文件。

比直接运行 tap 的开销更大,但是当你有多个容器要运行时,这种抽象会让生活变得更容易。

模板化工作流程

总而言之:这种方法有价值的原因是因为您可以轻松地将“工作流”模板化,这样任何可以输出 Singer 格式数据的容器都可以替换为一行模板代码(以及一个配置文件),这样您就有了一个新的工作流。

希望不难看出,通过一些额外的调整和一些保护良好的 S3 桶,这可以变成一个相当健壮的架构。一旦设置完成,您只需更改图像位置来添加新的 tap 容器。有一个很好的 UI 来检查日志和重新启动作业,并且很容易用 Slack 通知和附加作业来扩展模板。

我在附带的 GitHub repo 中创建了几个例子,包括带有tap-covid-19 Singer tap 的例子、WorkflowTemplate例子和可以每小时或每天运行的CronWorkflow规范。

只需对模板进行最少的编辑,即可同时运行多个拍子和目标。图片作者。

讨论

尽管建立这种架构对我来说很愉快,但它需要对基础设施、容器和 Singer 规范本身非常熟悉。你可能会发现 PipelinewiseMeltano 可以用更少的开销和更多的附加功能为你做同样的工作。或者,您可能会发现您可以利用 Argo 和这些工具来管理数据复制。

作为一名数据科学家,有很多考虑因素。这些问题包括:

  • 我是否有足够的预算来支付这笔费用(Stitch / Fivetran)?这些工具能满足我所有的数据复制需求吗?
  • 更简单的(单节点)架构是否可行,比如 Meltano?我有其他需要管弦乐队的工作吗?
  • Kubernetes 架构是否适合公司基础设施的其他部分?
  • 为什么不用气流或者提督?
  • 我是否拥有(或可以借用)设置和管理 Kubernetes 集群的基础设施专业知识?

我们的团队选择构建这种架构是因为:

  • 我们(还)没有企业服务的预算,但发现我们需要运行定制的 tap。
  • 我们已经习惯了容器化的应用程序。
  • 我们公司基础设施的其余部分在 Kubernetes 上运行,并利用其他 Argo 产品,使协作更加容易。
  • 我们有其他项目,如数据质量工作,我们需要一个平台来运行,我们以前没有与气流或完美的专业知识。

感谢阅读。乐于在 LinkedIn歌手 Slack 频道与他人联系。

其他资源

  • 歌手入门[ 博客
  • 打造歌手 Tap —信息图[ 博客 ]
  • 记录歌手抽头的参考脚本[ GitHub

用 Visual Studio 代码运行 Jupyter 笔记本

原文:https://towardsdatascience.com/running-a-jupyter-notebook-in-visual-studio-d5a4ede0c509?source=collection_archive---------33-----------------------

数据科学家如何运行 Jupyter 笔记本的指南。ipynb 文件与您的。py 文件

Jupyter 笔记本引用中的虚拟数据示例。Visual Studio 中的 py 文件。作者在 Visual Studio 代码 [1]上截图。

目录

  1. 什么是 Visual Studio 代码?
  2. 什么是 Jupyter 笔记本?
  3. 为什么对数据科学家有好处?
  4. 关于数据集
  5. 我该如何开始(教程)?
  6. 导入类
  7. 结论
  8. 参考文献

什么是 Visual Studio 代码?

Visual Studio Code(VS Code)[1]是一个免费的、简单的平台,开发者使用它来存放、编辑和执行他们的代码。但是最近,越来越多的数据科学家已经利用这个文件和代码管理平台来处理他们的。py 和。ipynb 文件。

什么是 Jupyter 笔记本?

Jupyter Notebook 是数据科学家测试 Python 代码、模型和结果的研究环境和平台。它以扩展名保存。ipynb”,并且可以被执行,包含在其中执行代码的单独的单元。

为什么对数据科学家有好处?

拿着数据科学的理学硕士学位,以及体验各种数据科学和机器学习课程,我已经在我熟悉的 Jupyter 笔记本里变得太舒服了。以我的经验来看,Visual Studio 代码已经成为我在编程 Python 和实现机器学习模型时提高效率的工具。以下是数据科学家应该使用 Visual Studio 代码的主要原因:

  • 更加擅长面向对象编程
  • 在一个位置管理您的文件夹、文件和数据
  • 查看你的笔记本。py 文件的同时
  • 从数据科学家过渡到机器学习工程师
  • 转换你的研究。ipynb 文件生产就绪。py 文件

关于数据集

数据集是在谷歌表单中创建的虚拟数据。它是为了模拟 2020 年冠状病毒疫情数据集的类似领域而开发的。这些字段包括日期、区域、记录计数和虚拟类别。遵循本教程时,不同的数据集,如真实的冠状病毒数据集 [2]只能用于教育和学术研究目的。该数据包含可以更好地表示的日期特性,这是本教程中实现的代码的目标。

我该如何开始(教程)?

启动 Visual Studio 代码的第一步是从这里1】下载必要的包。它将是您计算机上的一个应用程序,是一个可视化和执行代码的平台。从这里开始,如果您选择使用 Python 作为您的主要编程语言,请在市场中搜索该扩展,如下所示。您还可以从这里克隆您的 GitHub 库,并开始发出 pull 请求和终端命令。对于我的例子,我选择创建一个新文件夹,其中包含三个 Python 文件的子文件夹和一个独立的 Jupyter 笔记本文件。我写出来的文件系统是:

visual-studio-code - >示例->(_ _ py cache _ )-> _ init _ _。py、introduction_class.py 和 read_df.py

visual-studio-code->visual _ studio _ code . ipynb

为您的 VS 代码选择 Python 扩展。作者在 Visual Studio 代码1】上截图。

导入类

在我的例子中,我有帮助描述和显示如何设置你的数据科学算法的类。这些类的目标只是返回一个带有转换日期时间特性的 pandas 数据帧,以及一个可以被算法接收的数据帧。而我的 init。py 文件为空,不出所料,my introduction_class.py 包含一个类' IntroductionClass ':

来源于GitHub【2】上的作者

下面是我的 read_df.py。如你所见,我使用 pandas 从一个. csv 文件中读取数据帧。我也从pandas【3】执行了一个简单的日期时间转换。“年/月”列被转换为包含月、月、年中某一天的“日期”。然后,我从 head 函数返回数据帧的前 5 行。这个类作为一个简单的例子来说明什么可以被导入到你的 Jupyter 笔记本中。

来源于GitHub【2】上的作者

导入的最后一部分包含在 Jupyter 笔记本本身中,“visual_studio_code.ipynb”。这个文件导入了两个 python 类。py 文件,其中包括“IntroductionClass”和“testClass”。下面,这些类在导入后被调用。它们返回预期的数据帧,第一个新的 Jupyter 笔记本代码由一个 pandas 函数操作,该函数描述了一个带有常用统计信息的数据帧列,在本例中是“确认恢复”字段。

从第一个例子开始,然后练习从。py 文件,您可以为生产创建更好的可伸缩且高效的代码。您可以跨职能工作,用面向对象的代码实现您的算法,然后以 Jupyter 笔记本的形式更好地测试和可视化您的数据框架,两全其美。

从继承的 visual_studio_code.ipynb 文件。py 文件。作者在 VIsual Studio 代码 [1]上截图。

结论

成为你的数据科学游戏的顶端可能是压倒性的和复杂的,但我相信通过新的和熟悉的工具,你可以提高你的技能,也可以变得更加擅长机器学习工程。我相信 Visual Studio 代码就是这些工具之一。

它帮助我导入 Python 类和函数,这些类和函数用于为可能的机器学习模型塑造我的数据框架。它可以作为一个平台,将您的 GitHub 存储库代码与您的 research Jupyter 笔记本并行设计。它是通向数据科学、数据工程和机器学习的优秀桥梁。

参考

我的 Visual Studio 代码设置是使用虚拟数据集创建的。要素/字段/列也是虚拟属性。图片是我自己对 VS 代码主文件管理平台的截图。我的 VS 代码截图中可视化的数据和指标不得用于任何医疗目的。它是静态的、非真实的,并且不依赖于准确性。它纯粹是为了教程的目的而开发的。

[1] M.Przybyla,视觉工作室代码 (2020)

[2] M.Przybyla, GitHub (2020)

[3]熊猫-发展,熊猫 (2020)

在 Azure Databricks 上运行 Apache Beam 数据管道

原文:https://towardsdatascience.com/running-an-apache-beam-data-pipeline-on-azure-databricks-c09e521d8fc3?source=collection_archive---------27-----------------------

简要介绍如何在 Databricks 上执行 Apache Beam 管道

Github 资源库链接到本文

介绍

当我们想到数据并行管道时,Apache Spark 立即浮现在脑海中,但也有一些更有前途、更新颖的模型能够实现相同的结果和性能。

这就是 Apache Beam 的情况,它是一个开源的统一模型,用于定义批处理和流数据并行处理管道。它提供了以一种方便的方式定义数据管道的可能性,使用其分布式处理后端之一作为运行时( Apache ApexApache FlinkApache SparkGoogle Cloud Dataflow 以及许多其他)。

作者图片

Apache Beam 的强大功能在于更高层次的抽象,这可以防止程序员学习多个框架。
目前,Apache Beam 的使用主要局限于谷歌云平台,尤其是谷歌云数据流。

然而,当转移到其他平台时,可能很难找到一些有用的参考资料和例子来帮助我们运行 Apache Beam 管道。

这就是为什么我想告诉你我如何在 Databricks 上运行 Apache Beam 管道的经验。

让我们建立我们的阿帕奇光束管道

注意:这个演练的代码可以在 this Github repository 获得。

我决定从官方的 Apache Beam 的 Wordcount 例子开始,改变一些细节,以便在 Databricks 上执行我们的管道。

官方代码只是从 Google 云存储中读取一个公共文本文件,对输入文本进行字数统计,并将输出写入给定的路径。为了简化这个过程,我们将通过简单地从代码内模拟的字符串中读取输入文本来代替这些操作,最后将字数统计结果打印到标准输出中。

输入字符串将被定义为列表:

然后我们创建一个简单的光束自定义 DoFn 变换来打印我们的结果:

我们最终的管道将是这样的:

我们现在有了一个可以在本地模式下执行的工作射束管道。
如果您尝试运行它,您应该看到它打印成您的标准输出:

20/06/01 13:14:13 INFO transforms.PrintFN: against: 1
20/06/01 13:14:13 INFO transforms.PrintFN: and: 1
20/06/01 13:14:13 INFO transforms.PrintFN: of: 2
20/06/01 13:14:13 INFO transforms.PrintFN: troubles: 1
20/06/01 13:14:13 INFO transforms.PrintFN: nobler: 1
20/06/01 13:14:13 INFO transforms.PrintFN: arrows: 1
20/06/01 13:14:13 INFO transforms.PrintFN: suffer: 1
20/06/01 13:14:13 INFO transforms.PrintFN: sea: 1
20/06/01 13:14:13 INFO transforms.PrintFN: The: 1
20/06/01 13:14:13 INFO transforms.PrintFN: Or: 1
20/06/01 13:14:13 INFO transforms.PrintFN: not: 1
20/06/01 13:14:13 INFO transforms.PrintFN: slings: 1
20/06/01 13:14:13 INFO transforms.PrintFN: that: 1
20/06/01 13:14:13 INFO transforms.PrintFN: is: 1
20/06/01 13:14:13 INFO transforms.PrintFN: arms: 1
20/06/01 13:14:13 INFO transforms.PrintFN: Whether: 1
20/06/01 13:14:13 INFO transforms.PrintFN: a: 1
20/06/01 13:14:13 INFO transforms.PrintFN: fortune: 1
20/06/01 13:14:13 INFO transforms.PrintFN: take: 1
20/06/01 13:14:13 INFO transforms.PrintFN: question: 1
20/06/01 13:14:13 INFO transforms.PrintFN: To: 1
20/06/01 13:14:13 INFO transforms.PrintFN: mind: 1
20/06/01 13:14:13 INFO transforms.PrintFN: to: 3
20/06/01 13:14:13 INFO transforms.PrintFN: outrageous: 1
20/06/01 13:14:13 INFO transforms.PrintFN: or: 1
20/06/01 13:14:13 INFO transforms.PrintFN: tis: 1
20/06/01 13:14:13 INFO transforms.PrintFN: in: 1
20/06/01 13:14:13 INFO transforms.PrintFN: the: 2
20/06/01 13:14:13 INFO transforms.PrintFN: be: 2

现在,让我们转到数据块

现在,我们想在 Databricks 实例上执行我们的管道。为了实现这一点,我们需要修改代码中的一些东西。首先,我们修改我们的 WordCountOptions,,它必须扩展 SparkContextOptions 类。为了操作光束的 SparkContext ,这些光束选项是必要的。Databricks 集群有自己的 SparkContext,这对检索至关重要,以便扩展应用程序。一旦我们检索到 SparkContext,我们可以直接将其注入到 Beam 的 SparkContextOptions 中,如下所示:

有了这个 Beam 代码的最终版本,我们现在可以在 Azure 中启动我们的 Databricks 工作区,并继续创建一个新的作业。
我们将我们的项目打包到一个 fat jar 中(在本例中,我将使用标准的 maven 生命周期来打包我的应用程序),并通过单击“Upload Jar”将它上传到我们的作业中。

请注意,如果您的 pom.xml 文件中有任何 Spark 依赖项,请记住将它们标记为" provided " ,因为我们的 Databricks 集群将通过执行上下文将它们提供给我们的应用程序。

Jar 上传

指定主类后,使用这些将由 SparkContextOptions: 解析的参数是很重要的

--runner=SparkRunner --usesProvidedSparkContext

最后,我们可以通过点击编辑来设置将与我们的作业相关联的集群:

集群配置

这样,我们在 Databricks 运行时版本 6.4 上定义了一个“ 新的自动化集群 ”和 2 个工人。如果您愿意,您还可以创建一个“交互式集群”,这样您可以更好地控制集群的执行。

现在,我们可以走了!
如果您的工作看起来与下图相似,只需点击“立即运行”,然后等待其终止。

运行 Apache Beam 管道的 Databricks 作业示例

终止后的执行状态

请注意,如果您已经用 python 编写了您的 Beam 管道,那么让它在数据块上工作的过程应该看起来或多或少是相同的:
只需记住将数据块' SparkContext 注入到 Beam 中,并使用正确的参数集执行您的管道。

我希望你喜欢我关于如何在 Azure Databricks 上运行 Apache Beam 管道的演练,如果你找到了关于这个主题的更多有用的见解,请随时联系我!

在 Azure Kubernetes 服务上使用 RocksDB 运行 Apache Flink

原文:https://towardsdatascience.com/running-apache-flink-with-rocksdb-on-azure-kubernetes-service-904181d79f72?source=collection_archive---------29-----------------------

最近,我在研究如何部署一个使用 RocksDB 作为后端状态的 Apache Flink 集群,发现缺少关于这个主题的详细文档。我能够从 Flink 文档和一些堆栈溢出的帖子中拼凑出如何部署它,但是没有一个清晰的指南。我写这篇文章的目的是展示一个从 Flink 1.11.0 开始如何做到这一点的逐步指南。对于 AWS 部署,只需稍加修改即可遵循本指南。我会注意到这些变化。

先决条件

在开始阅读本指南之前,您应该已经订阅了 Azure,并且拥有一个正在运行 blob store 的存储帐户。如果你需要帮助来设置和运行这些服务,请参见本文底部的链接部分,那里有关于如何设置这些服务的 Azure 指南的链接。

如果你还没有安装 flink、azure-cli 和 docker,也可以下载它们的链接。

步骤 1:构建自定义 Docker 映像

为 Flink 后端状态使用 Azure Blob 存储或 AWS S3 存储需要一个额外的插件 jar,该插件 jar 没有内置到基本 Flink docker 映像中。要添加所需的 jar,首先需要从 GitHub 下载 apache/flink-docker repo。

[## apache/flink-docker

github.com](https://github.com/apache/flink-docker)

下载完 repo 后,导航到包含您要部署的 Flink 版本的文件夹。在我的例子中,我想使用 1.11,然后如果你计划使用 scala 代码,导航到你正在使用的 scala 版本的目录。我使用 scala 2.12,所以对我来说这个文件夹是scala_2.12-debian。如果你使用 java 或者 python,我相信你可以使用任何一个版本。在这个文件夹中,你应该会看到两个文件:Dockerfiledocker-entrypoint.sh。我们将要编辑的文件是 Dockerfile。在文本编辑器中打开它,并在COPY docker-entrypoint.sh行的正上方添加以下几行。

对于 Azure Blob 存储:

# install Flink Azure FS Hadoop plugin
RUN mkdir ./plugins/azure-fs-hadoop
COPY ./flink-azure-fs-hadoop-1.11.0.jar ./plugins/azure-fs-hadoop/

对于 AWS S3,您有两个选项 Presto 和 Hadoop:

转眼间:

# install Flink S3 FS Presto plugin
RUN mkdir ./plugins/s3-fs-presto
COPY ./flink-s3-fs-presto-1.11.0.jar ./plugins/s3-fs-presto/

Hadoop:

# install Flink S3 FS Hadoop plugin
RUN mkdir ./plugins/s3-fs-hadoop
COPY ./flink-s3-fs-hadoop-1.11.0.jar ./plugins/s3-fs-hadoop/

接下来从 Flink 1.11.0 文件夹导航到/opt/目录,将后端所需的 jar 复制到你编辑的 docker 文件所在的目录。对于 Azure,这个 jar 是 flink-Azure-fs-Hadoop-1 . 11 . 0 . jar

现在,使用以下命令构建 Docker 映像并将其推送到 DockerHub,用您的 DockerHub 用户名替换 hunterkempf,并更改标签以反映 flink、scala 和您添加到其中的插件的版本:

docker build --tag hunterkempf/flink:1.11.0_scala_2.12_fs_azure .
docker push hunterkempf/flink

步骤 2: Kubernetes 部署

[## Flink Kubernetes 设置

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/deployment/kubernetes.html#appendix)

首先,转到 Flink Kubernetes 设置页面并创建以下内容。yaml 文件,并从附录中复制/粘贴。

  • flink-configuration-config map . YAML
  • jobmanager-service.yaml
  • 作业管理器-会话-部署. yaml
  • 任务管理器-会话-部署. yaml

flink-configuration-config map . YAML

您需要在flink-conf.yaml部分添加以下几行:

此示例针对名为:flinkstorage 的存储帐户中名为:flinkblob 的 blob 存储区。您还需要获取存储帐户的访问密钥,并将其粘贴到 fs . azure . account . Key . flink storage . blob . core . windows . net 之后:

state.backend: rocksdbstate.checkpoints.dir: wasbs://flinkblob@flinkstorage.blob.core.windows.net/flink/fs.azure.account.key.flinkstorage.blob.core.windows.net: 

job manager-session-deployment . YAML 和 task manager-session-deployment . YAML

image: flink:1.11.0-scala_2.11替换为您在第 1 部分中为 jobmanager-session-deployment 和 taskmanager-session-deployment 创建的 docker 映像的名称。

连接到 AKS 集群

运行这两个命令来连接到 AKS 集群

az aks install-cli
az aks get-credentials --resource-group myResourceGroup --name myAKSCluster

将 Flink 部署到 AKS

从创建 yaml 文件的目录中,运行以下命令

kubectl create -f flink-configuration-configmap.yaml
kubectl create -f jobmanager-service.yaml
kubectl create -f jobmanager-session-deployment.yaml
kubectl create -f taskmanager-session-deployment.yaml

现在你应该有一个 Flink 集群运行在 AKS 上,使用 Azure Blob 存储作为 RocksDB 后端。

查看 Flink 仪表盘

要访问 Flink 仪表盘,请运行以下命令:

kubectl get pods

这将返回类似下面的内容

NAME                               READY   STATUS    RESTARTS   AGEflink-jobmanager-5f857bf45d-n4mbt    1/1     Running   0        38sflink-taskmanager-68854b7998-2zkd6   1/1     Running   0        31sflink-taskmanager-68854b7998-vvm5m   1/1     Running   0        31s

复制 flink-jobmanager pod 的名称,并将其用于以下命令,用您的值替换5f857bf45d-n4mbt

kubectl port-forward flink-jobmanager-5f857bf45d-n4mbt 8081:8081

现在打开 http://localhost:8081 ,您应该会看到 Flink 仪表盘

要检查您的配置,请单击左侧的作业管理器选项卡

您应该会看到类似的东西,您的设置在键值标记中列出。

为了测试部署,您可以从位于examples/streaming/文件夹中的 Flink 1.11.0 文件夹上传一个测试 jar

运行流式字数统计作业花了 1 秒钟,没有为我返回任何错误。如果出现内部服务器错误,请检查作业管理器日志,查看错误堆栈跟踪。

链接:

这里有一些有用的链接供进一步阅读

Azure 文档:

[## 安装 Azure CLI

Azure CLI 可安装在 Windows、macOS 和 Linux 环境中。它也可以在 Docker 中运行…

docs.microsoft.com](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) [## 在 portal - Azure Kubernetes 服务中创建一个 AKS 集群

Azure Kubernetes Service (AKS)是一个托管的 Kubernetes 服务,可以让您快速部署和管理集群。在…

docs.microsoft.com](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal) [## 创建存储帐户- Azure Storage

Azure 存储帐户包含所有 Azure 存储数据对象:blobs、文件、队列、表和磁盘。的…

docs.microsoft.com](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?tabs=azure-portal) [## 快速入门-使用 Azure 门户创建 blob-Azure 存储

在这个快速入门中,您将学习如何使用 Azure 门户在 Azure 存储中创建容器,以及上传和…

docs.microsoft.com](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal)

Flink 文档:

[## Flink Kubernetes 设置

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/deployment/kubernetes.html#deploy-session-cluster) [## 本地 Flink 安装

按照以下几个步骤下载最新的稳定版本并开始使用。为了能够运行 Flink,唯一的…

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-release-1.11/try-flink/local_installation.html) [## Azure Blob 存储

Azure Blob Storage 是微软托管的服务,为各种用例提供云存储。

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-stable/ops/filesystems/azure.html) [## 状态后端

部署&操作状态&容错状态在数据流 API 中编写的后端程序通常保持状态…

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/state/state_backends.html#the-rocksdbstatebackend) [## 配置

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-release-1.11/ops/config.html)

码头工人:

[## 用于 Mac 和 Windows 的 Docker 桌面

5 月 28 日上午 9 点 PDT / GMT -7 了解更多关于 Docker 的…

www.docker.com](https://www.docker.com/products/docker-desktop)

“大规模”运行谷歌的云数据融合批处理管道

原文:https://towardsdatascience.com/running-googles-cloud-data-fusion-batch-pipelines-at-scale-f1064b869dff?source=collection_archive---------30-----------------------

理解大数据

为数据科学工作流测试云数据融合

将数据从遗留 sqlserver 转移到 BigQuery 的典型 CDF 管道。作者图片

TLDR:当通过 REST api 大规模提交批量云数据融合(CDF)管道时,在每次调用之间暂停几秒钟,让 CDF 跟上进度。

背景:作为我们参与的迁移的一部分,我们的数据科学团队正在将数百个遗留的 MS Sqlserver ODS 表迁移到 BigQuery 中。虽然我们的工程团队正在处理实际的迁移,但我们(DS 团队)希望我们自己能够快速地在 GCP 构建、原型制作和迁移我们的模型,而无需等待我们的工程团队所承担的所有质量和范围广泛的要求。进入谷歌的云数据融合。基于 CDAP ,这是对我们团队问题的一个很好的解决方案:我们希望定期将遗留表复制到 BigQuery 中,以服务于我们的模型。我们,数据科学团队,完全控制调度和范围,CDF“无代码”接口使所有团队成员可以直接利用 spark 的能力。遵循一些简单的标准,很容易添加额外的管道。

问题:在迁移开始时,我们确定了要复制到 BigQuery 的所有表和源——基本上是迁移的范围。我们有大约 100 个表,我们构建了 1 个管道来迁移 1 个表,所以总共有大约 100 个管道。管道运行的节奏在每日和每月之间变化,大多数表介于两者之间。有一段时间一切都很好,直到我们构建了一个需要 60 个表同时加载的模型。我不会解释为什么我们要构建一个需要 60 个表的模型,而是强调这个需求是如何对云数据融合的可伸缩性进行有趣的测试的。

在这一点上,我们都同意 CDF 不是同时执行 60 个管道的最佳工具,每个管道迁移一个表,其中许多在 10 分钟内完成,但这是我们发现自己所处的情况。CDF 不太适合这项任务,原因有很多,其中一个原因是,它需要 60 个小型 MR/spark 集群来完成大部分 10 分钟的迁移任务。实际上,这些 MR/spark 集群(GCP 的 dataproc,AWS 的 e MR)的配置时间只有 2-3 分钟,成本实际上并不多。我们首先构建了一个管道列表,遍历该列表,并从 GCE VM 通过 CDAP 的 REST api 在 bash 脚本中执行它们。虽然我们最终异步调度了这些任务,但是我们想证明我们可以同时执行这些任务,并且 CDF 可以根据我们的需求进行扩展。

我们最初的发现令我们失望。我们的脚本通过 REST api 在一秒左右的时间内执行了启动管道命令,在 60 个管道中,有 25 个失败了,几乎没有日志。当我们解析 CDF 的 App Fabric 服务日志时,我们遇到了一个有趣的错误:

2020–10–30 22:27:34,715—WARN[pool-10-thread-1:I . c . c . I . a . s . runrecordcorrectorservice @ 148]—修复了 25 个状态为[正在启动、正在运行、已暂停]的运行记录,但程序实际上并未运行

2020–10–30 22:27:34,716—INFO[pool-10-thread-1:I . c . c . I . a . s . runrecordcorrectorservice @ 103]—更正了 25 条状态为[正在启动、正在运行、已暂停]的运行记录,这些记录没有实际运行的程序。这类程序很可能已经崩溃或被外部信号杀死

一个 dataproc 集群,它托管了我们的一个失败的管道,成功的和失败的管道的集群在日志方面是相同的。作者图片

纱线容器数与时间。在下午 6 点以后,可以看到成功创建了 dataproc 集群。作者图片

虽然 CDF 收到了 60 个“执行管道”api 调用,但它错过了 25 个运行记录。有趣的是,当我们查看 dataproc 日志时,我们发现它成功地创建了 60 个集群。也就是说,我们观察到成功创建的集群,甚至是托管失败管道的集群——这帮助我们认识到,我们在创建 dataproc 集群时没有可伸缩性问题,而是在创建 CDF“主”时有问题。问题在于 CDF 的簿记/跟踪,而不是 dataproc 集群的创建。作为数据科学家,而不是工程师或架构师,我们对这是如何工作的知识有限,但我们假设当我们在大约 1 秒钟内用 60 个 api 调用淹没系统时,我们过载了使用的任何跟踪数据库。我们再次尝试在每个 api 调用之间添加一些填充。我们发现,等待大约 10 秒钟会导致所有 60 个管道执行调用被成功提交和执行。虽然不是最优雅的解决方案,但它为我们完成了工作,因此我们可以考虑其他事情,这在数据科学中非常重要。

更新:我们已经探索并正在我们的工程团队的 Cloud Composer 实例上执行和调度这些带有气流的管道,但是当昂贵而复杂的编排资源不可用时,上面概述的方法仍然是一个可靠的选择。

在 15 分钟内在云上运行 Jupyter 笔记本电脑#Azure

原文:https://towardsdatascience.com/running-jupyter-notebook-on-the-cloud-in-15-mins-azure-79b7797e4ef6?source=collection_archive---------9-----------------------

文章做到了标题所说的。在 Azure 笔记本电脑(免费或付费)上运行 Jupyter 笔记本电脑,成本很低。

为了更便宜、更快、更好地运行最好的数据科学模型,我寻找了多种途径来编译复杂的代码。我经常在我的 Github 上运行深度学习、神经网络(RNN,CNN)、自然语言处理(NLP)和高级机器学习(ML)的模型。在我以前的文章中,我比较了数据科学家在云上运行笔记本的多种选择,现在我正在深入研究我最喜欢的一种选择——Azure 笔记本

在云上运行 Jupyter Notebook 的最大优势之一是,你可以拥有一台十年前的 PC/MAC ,它无法在本地运行最新的深度学习算法,但仍然能够在云上部署最先进的机器学习模型,只要你在本文的帮助下设置它。请继续阅读!

通过 Azure 云的计算有无限的潜力

在线云计算的核心问题是我们对云感到不舒服。配置、IP、存储选项、计算选择和连接的变化让我们不知所措。

我要为一个数据科学家破译云。

云是一个难以驾驭的空间,但是,通过一点点帮助,你将得到你想要的东西——一种运行 Jupyter 笔记本电脑的极其廉价和高效的方法。我将努力做到以下几点:

  1. 在 Azure 笔记本上设置 Jupyter 笔记本
  2. 激活并导航 Azure 门户
  3. 设置 Azure 数据科学虚拟机(DSVM)
  4. 将 DSVM 连接到 Azure 笔记本电脑

这可能是一个漫长的阅读,但是,如果你的意图与我的一致,你会满意的。

免费获得高级 Azure 服务:

快速补充说明:大多数追求相同目标的人都在一个知名教育机构的项目中工作。所以,我建议你利用 Azure 的学生福利,这样你就可以免费获得 100 美元的学分——只需使用你所在学院提供给你的电子邮件。我喜欢我们生活的这个时代,云计算比一罐可乐还便宜,所以去 Azure for Students 免费激活你的学生账户吧。这是一个简单的过程,不会超过几分钟。感谢我以后

1.在 Azure 笔记本上设置 Jupyter 笔记本

我在这里 为我的数据科学家同事 写了一个详细的分步过程,你可以按照它来设置并理解在线设置 Jupyter 笔记本的细微差别。

[## Jupyter 笔记本崩溃了,所以我在 15 分钟内就把我的数据科学项目部署到了云中(对于…

你可以用一个很老很老的系统免费进行奇特的数据科学研究——只要动动脑筋,从这个博客中获得帮助。

medium.com](https://medium.com/datadriveninvestor/jupyter-notebook-crashed-so-i-deployed-my-data-science-project-in-the-cloud-in-15-minutes-for-56edc0ac4d0d)

它的本质如下所述:

  1. 打开 Azure 笔记本,登录并开始一个新项目
  2. 打开一个新笔记本并选择运行它的内核
  3. 选择自由层选项作为计算
  4. 导入所需数据并在 Jupyter 笔记本上运行

你现在应该有一个可以在云上运行的 Jupyter 笔记本了。上述空闲层中的基本配置是 1GB 存储和 4GB RAM。在这篇博客的后面,我们将致力于增加存储、内核、内存和计算能力。

2.激活并导航 Azure 门户

您用来制作 Azure 笔记本的帐户可以在此处登录 Azure 门户。你现在应该可以看到 Azure 仪表盘和门户了。现在让我们快速浏览一下我们可能关心的元素和术语:

  • 微软 Azure 是微软创建的云计算服务,用于通过微软管理的数据中心构建、测试、部署和管理应用程序和服务。
  • 虚拟机(VM) :虚拟机是计算机系统的仿真。这本质上就像在云中拥有一台计算机,但是,您不是管理硬件,而是根据自己的需要租用系统。为什么?
    这大大降低了计算成本。正如我在之前的博客中提到的,这意味着总成本约为 1500 美元——2000 美元的硬件可以在你十年前的笔记本电脑上运行,而成本只是它的一小部分。我说的分数是指固体构型低于 30/小时。

通过 Azure 云服务( Pixabay )释放您电脑的真正潜力

  • 云服务:云计算是计算机系统资源的按需可用性,尤其是数据存储和计算能力,无需用户直接主动管理。本质上,你只需在服务被使用时付费。
  • 弹性:云适应性强。
    -运行简单的数据清理和特征工程步骤?
    →太好了!你可以使用免费的或较低层次的(更便宜的)作为计算。
    -运行需要更多计算的复杂模型?
    →又棒了!只需升级系统规格,运行模型一个小时,然后再次转移到较低层。
  • Blob 存储:Azure 存储的奇特术语。这是一个针对存储结构化和非结构化数据而优化的文件系统,您可以在 Jupyter 笔记本中使用它进行分析。

请记住,这个博客并没有教你如何利用 Azure 的所有功能,它专注于帮助数据科学项目,特别是通过 Azure 笔记本部署在云上的 Jupyter 笔记本。如果你感兴趣,我强烈建议你花些时间阅读微软的伟大文档。

3.设置 Azure 数据科学虚拟机(DSVM)

导航到 Azure 门户网站,你应该会看到一个类似这样的屏幕:

Azure 门户主页

我们现在将创建一个虚拟机来连接 Azure 笔记本。该接口将是一个 Jupyter 笔记本,其中的计算将出现在 Azure 服务上。如下所示创建一个资源。

创建资源

搜索 Data Science Virtual Machine for Linux(Ubuntu)并选择它。

搜索Data Science Virtual Machine for Linux(Ubuntu)

您现在应该会看到一个类似于下图的屏幕。

继续创建一个基于 Linux 的数据科学虚拟机

您可以根据需要配置设置。我已经填写了一个基本的样本,你可以遵循。不要被价格吓倒,因为如果你决定整个月都运行这个系统,价格是被提到的。我们将运行虚拟机几个小时,而不是几天,因此,这将是一个非常小的一部分成本,您可以在继续之前查看。如果你使用 Azure 的学生订阅,那么你可以免费获得一年 100 美元。就算你不是,也是极其实惠的。

示例配置— 1

示例配置— 2

现在选择审查+创建。最重要的是,您应该看到 Data Science 虚拟机每小时的成本。对于上面的示例配置(这与 2020 年的一些最新笔记本电脑一样好),设置成本仅为 1/小时。难以置信!根据你的需求,你甚至可以使用 GPU 进行深度学习建模,大约 50 英镑/小时。

所选样本配置的每小时价格(印度卢比)

如果你对所有提到的规范感到满意,只需点击创建按钮,我们就可以开始了。否则,返回并重新配置规范。

该页面应该导航到一个新页面,这需要几秒钟或几分钟的时间(取决于设置虚拟机的配置)

部署正在进行中

部署后,您可以看到以下屏幕。

部署成功!

在这里,我建议您设置自动关闭并在本地计算机上下载部署细节。我还建议保留管理员用户名和密码的备份,以备将来参考。

注意:运行一个虚拟机是要花钱的。不使用时,选择停止按钮停止。如果虚拟机运行不正常,您可以重新部署它。

如果您点击转到资源按钮,您可以看到相关选项。

示例虚拟机,您可以在其中重启、停止和删除 Data Science 虚拟机

干得好!现在,您已经使用想要使用的配置设置了 Data Science 虚拟机。

4.将 DSVM 连接到 Azure 笔记本电脑

将数据科学虚拟机与 Jupyter 笔记本( Pixabay )连接

让我们总结一下你迄今为止做得非常出色的事情:

  • 我们有运行在云上的 Jupyter 笔记本: Azure 笔记本设置好了,我们已经可以在自由层上运行 Jupyter 笔记本了

您有一个项目准备好在 Azure 笔记本电脑上计算

  • 我们已经建立了一个数据科学虚拟机,具有我们需要的存储和计算能力。

如果你前往 Azure 门户网站,你应该会看到最近资源下的资源,如图所示。

现有资源下的 SampleVirtualMachine

到目前为止你做得很好!我们现在将继续进行我们已经进行了这么久的步骤:将我们的 Azure 笔记本与我们的 Azure Compute 连接起来。

转到您的 Azure 笔记本,选择在 Direct Compute 上运行的选项,如下所示。

选择直接计算

您现在应该会看到一个选项来验证您的 Azure 虚拟机凭据,如下所示。

只需输入您之前为虚拟机提供的管理员用户名和密码

对于 IP,您可以返回到虚拟机的设置页面,并复制公共 IP,如下所示。

使用给定的公共 IP 来运行 Jupyter 笔记本

我看到的一个常见错误如下所示。

DSVM 出错

只需选择突出显示的链接并将其粘贴到您的浏览器中。我相信这个错误的发生是因为我们没有一个安全的连接,但是没关系,因为我们是唯一使用它的人,所以个人部署不需要太多的安全性。但是,如果您正尝试为企业部署这一功能,我建议您联系您的 IT 部门,以确保遵循信息安全实践。

粘贴链接应该会将您带到一个要求您提供凭据的页面。您可以输入 DSVM 凭据并登录。

部署成功!现在,只需点击运行

现在,刷新 Azure 笔记本页面,再次尝试直接计算选项,点击运行

您现在已经成功地在云端部署了您的 Jupyter 笔记本电脑。

干得好。( Pixabay )

结论:

[## Anish Mahapatra -数据科学家-穆适马公司| LinkedIn

我正在努力理解数学、商业和技术如何帮助我们在未来做出更好的决策…

www.linkedin.com](https://www.linkedin.com/in/anishmahapatra/)

正如所承诺的,您已经在 15 分钟内成功地在云上运行了 Jupyter Notebook。我花了很多时间研究,并且非常喜欢写这篇文章。如果这对你有帮助,给我点爱!😄我也写关于千禧一代的生活方式咨询聊天机器人金融!如果您对此有任何问题或建议,请随时通过 LinkedIn联系我或关注我这里,我很想听听您的想法!

在 WSL 上运行 Jupyter 笔记本,同时在 Windows 上使用浏览器

原文:https://towardsdatascience.com/running-jupyter-notebook-on-wsl-while-using-firefox-on-windows-5a47ebfae4c1?source=collection_archive---------6-----------------------

体验 Ubuntu 或 Mac 的指南

如果您像我一样是一名数据科学家,最近刚刚迁移(回到)Windows,您可能会发现启动并运行系统很困难,尤其是当您想要使用 bash terminal 的强大功能时。你会立刻意识到你为苹果电脑支付的价格可能真的是值得的。你得到了两个世界的好处:基于 GUI 的应用程序可以正常工作,而基于 CLI 的应用程序可以加快速度。

虽然使用 Windows subsystem for Linux (WSL)可以帮助我让好的 ol' bash 终端工作,但它不能在运行 Jupyter Notebook 时提供相同的现成体验。我也错过了好的终端,比如 Mac 上的 iTerm2 或者 Ubuntu 上的 Terminator 。你可以找到人们使用类似 Hyper 的变通方法,甚至可以回到旧的 tmux ,但我发现前者很迟缓,而后者没有 iTerm2 或终结者那么容易。在本帖中,我将向您介绍 Windows 终端

它很简单,相当快,类似 iTerm2。很好。

设置 Windows 终端

您可以从 Microsoft Store 安装它。您可能需要首先更新您的 Windows。别担心,等待是值得的。

最低要求是 Windows 10 build 1903 | Image by author

完成了吗?

免责声明:我假设你已经用你最喜欢的发行版安装了 WSL。如果你还没有,那就按照 这个指南 。这种情况下,我用的是 Ubuntu 18.04。

接下来,打开您的 Windows 终端。默认情况下,您应该会看到 Windows PowerShell。使用 Windows 终端,你可以打开多个标签,从 Windows PowerShell、命令提示符、你选择的 WSL 甚至 Azure Cloud Shell 切换。如果你想让 bash 成为你的默认设置,点击 Ctrl+,或者点击向下的 v 形图标,然后点击“设置”。

找到“defaultProfile”并将值更改为“profiles”>“defaults”>“list”中的“guid”。

{
    "defaultProfile": **"{c6eaf9f4-32a7-5fdc-b5cf-066e8a4b1e40}"**,
    ...
    "profiles":
    {
        "defaults":
        {
            ...
        },
        "list":
        [
            {
                "guid": "**{c6eaf9f4-32a7-5fdc-b5cf-066e8a4b1e40}**",
                "hidden": false,
                "name": "Ubuntu-18.04",
                "source": "Windows.Terminal.Wsl",
                **"startingDirectory": "//wsl$/Ubuntu-18.04/home/<username>"**
            },
            ...
        ]
    },
    ...
}

注意,我还向 WSL 添加了“starting directory”

使用默认设置,您可以通过按 Alt+Shift+D 打开一个新的窗格。现在你不再需要 Hyper 了,是吗?

配置 Jupyter 笔记本

我假设你已经在这里安装了 Python 和 Jupyter Notebook。如果没有,我推荐使用 Anaconda 来安装它们。提醒您,这将安装在您的 WSL 中。

接下来,使用以下命令生成笔记本配置:

jupyter notebook --generate-config

然后您将在中看到一个 Python 文件。朱庇特文件夹。使用您最喜欢的文本编辑器编辑它们。

code ~/.jupyter/jupyter_notebook_config.py

通过更改该行来禁止通过 redicect 文件启动浏览器(默认值为 True ):

c.NotebookApp.use_redirect_file = False

WSL 的好处是可以直接从 bash 打开 Windows 程序。因此,要让您的 Jupyter 笔记本在浏览器中打开一个选项卡,您可以在 bash 中将它们添加为$BROWSER。我这里用的是火狐,但是你可以换成你自己喜欢的浏览器。也就是说,可以编辑~/。bashrc,并添加以下行:

export BROWSER='/mnt/c/Program Files/Mozilla Firefox/firefox.exe'

瞧吧!

从 WSL 运行 Jupyter 将会在 Windows | Image by author 上调用您的浏览器

/mnt/c 意味着您可以直接从 bash 访问 Windows 上的文件!很整洁不是吗?因此,在这种情况下,我们在 Windows 上使用 Firefox。没有必要在 WSL 上安装 Firefox 甚至 X11。现在,你可以像在 Ubuntu 或 Mac 上一样启动你的笔记本了。

希望你觉得这有用!

Java 中的 Tensorflow SavedModel

原文:https://towardsdatascience.com/running-savedmodel-in-java-1351e7bdf0a4?source=collection_archive---------23-----------------------

如何将您的 TF SavedModel 与当前版本的 Tensorflow Java 一起使用

来源:https://unsplash.com/@ricaros

介绍

机器学习最流行的编程语言是 Python。大多数数据科学家和 ML 工程师用 Python 来构建他们管道。尽管 Python 是一个广泛使用的解决方案,但它可能不是所有栈或用例的最佳选择。在本文中,我将向您展示如何在当前版本的 Tensorflow Java 中使用您的 SavedModel,这可能会有用。

注:如你所见tensor flow Java还在建设中。因此,可能会发生一些意想不到的行为。请记住这一点。

设置 maven 项目

首先创建一个 Maven 项目并打开 pom.xml。我用 Java 11,因为它是 LTS。

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>1.11</maven.compiler.source>
  <maven.compiler.target>1.11</maven.compiler.target>
</properties>

接下来,将 Tensorflow Java 工件添加到项目中。在撰写本文时,它们还没有托管在 maven 上。因此,您必须手动添加 Sonatype 的 OSS 库。你总能在他们的 Github 资源库 Tensorflow/Java 中找到最新的指令。

<repositories>
    <repository>
        <id>tensorflow-snapshots</id>               <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

<dependencies>
    <dependency>
       <groupId>org.tensorflow</groupId>
       <artifactId>tensorflow-core-platform</artifactId>
       <version>0.2.0-SNAPSHOT</version>
    </dependency>
</dependencies>

准备 SavedModel

出于演示的目的,我将使用 GPT-2 模型。但是任何其他 SavedModel 都可以。下载/创建 SavedModel 文件后,我将整个文件夹移动到 Java 项目的 resource 文件夹中。然后转到终端,输入以下行:

saved_model_cli show --dir PATH/TO/SAVEDMODEL/FOLDER --all
# my output:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:signature_def['predict']:
  The given SavedModel SignatureDef contains the following input(s):
  inputs['context'] tensor_info:
    dtype: DT_INT32
    shape: (1, -1)
    name: Placeholder:0
  The given SavedModel SignatureDef contains the following          output(s):
  outputs['sample'] tensor_info:
    dtype: DT_INT32
    shape: (1, -1)
    name: sample_sequence/while/Exit_3:0
Method name is: tensorflow/serving/predict

这为您提供了关于模型的所有相关信息,例如标签集、输入和输出节点等。执行推理时需要这些信息。

运行 SavedModel

创建一个新的 Java 类,创建一个 main 方法,并添加以下代码行来加载带有特定标记集的 SavedModel:

SavedModelBundle model = SavedModelBundle.load(modelPath, "serve");

此时,我们需要为模型创建输入张量。所有可用转换的概述可在TensorCreation.java中找到。在我的例子中,GPT-2 取一个带有形状(1,-1)和 int 值的张量。所以我用一些随机手写的 int 值创建了一个形状为(1,9)的张量

IntNdArray input_matrix = NdArrays.ofInts(Shape.of(1, 9));
input_matrix.set(NdArrays.vectorOf(1, 2, 3, 5, 7, 21, 23, 43, 123), 0);
Tensor<TInt32> input_tensor = TInt32.tensorOf(input_matrix);

然后创建一个映射,它有一个字符串键和一个张量作为值。关键是输入张量的名称和您将用作输入的张量的值。这应该会让你想起以前的 Tensorflow 1。x 天,您必须创建一个 feed_dict,并在会话期间将其插入到图表中。在 Java 中,我们可以简单地创建一个散列表,并将我们创建的输入传感器插入其中:

Map<String, Tensor<?>> feed_dict = new HashMap<>();
feed_dict.put("context", input_tensor);

剩下要做的就是调用 signature_def 函数。

model.function("predict").call(feed_dict)

并且你完成了,所有的代码都可以在这个要点中找到。

在 Docker 上运行、保护和部署弹性堆栈🐳

原文:https://towardsdatascience.com/running-securing-and-deploying-elastic-stack-on-docker-f1a8ebf1dc5b?source=collection_archive---------4-----------------------

在 Docker 上为小规模生产部署和开发构建和配置弹性堆栈(ELK)

什么是弹性叠加?

Elastic Stack(又名 ELK )是您组织的集中式结构化日志记录的当前首选堆栈。它收集吸收存储您的服务日志(以及指标),同时使它们可搜索&可聚集&可观察。然后,基于这些数据构建警报和仪表板。

活动中的弹性堆栈。

运行弹性堆栈

在本帖中,我们将使用 Docker&Docker-Compose编写配置安全部署弹性堆栈。我们将构建的内容可用于 docker 主机上的开发小规模生产部署

  • 为每个组件构建一个映像。
  • 参数化配置&避免硬编码凭证。
  • 将 Elasticsearch 设置为可扩展的生产单节点集群。
  • 设置安全性和加密。
  • 在容器外保存秘密、证书和数据。
  • 在 Docker-Compose 中将所有内容组合在一起。

要跳过逐步教程你可以查看我的 Elastdocker 模板库,用一个命令在 Docker 上旋转弹性堆栈。

[## sherifabdlnaby/elastdocker

用像 Curator,Rubban,ElastAlert 这样的工具进行预警。弹性堆栈(又名 ELK) Docker 组成,预配置…

github.com](https://github.com/sherifabdlnaby/elastdocker)

git clone [https://github.com/sherifabdlnaby/elastdocker.git](https://github.com/sherifabdlnaby/elastdocker.git)
cd elastdocker
make setup & make elk

Elastdocker template 具有更完善的配置和更好的安全性,以及经常与 Elastic Stack 一起使用的监控和工具。为了简单起见,本教程中省略了这些额外的内容。

步伐

  1. 为我们的堆栈创建 Docker 映像(使用我们的配置)。
  2. 创建一个设置容器来生成 Elasticsearch 密钥库和证书。
  3. 使用 Docker-Compose 将堆栈放在一起。
  4. 开始运送日志!🎉

1.为我们的堆栈创建 Docker 图像。

让我们从制作根目录elk开始,创建五个独立的文件夹;elasticsearchlogstashkibanasetupsecrets目录。

另外,在根目录下创建一个.env文件来保存我们的参数。

弹性搜索

./elasticsearch目录中创建Dockerfile并添加以下内容:

然后我们还将elasticsearch.yml添加到./elastichsearch/config/ 目录中,这个文件将在容器启动时加载以配置 elasticsearch。

  • 注意${ELASTIC_XXX}配置将从容器的环境变量中设置,我们稍后将在运行时传递这些变量。

Logstash./logstash目录下创建Dockerfile并添加以下内容:

logstash.yml添加到logstash/config目录中。

logstash.conf添加到logstash/pipeline目录中。

从 Beats = to => Elasticsearch 转发数据的简单管道。

基巴纳

kibana目录中创建Dockerfile并添加以下内容:

kibana.yml添加到kibana\config目录:

2.创建一个设置容器来生成 Elasticsearch 密钥库和认证

我们需要创建一个容器来生成 Elasticsearch 密钥库,其中包含 Elasticsearch 的用户(和超级用户)密码,以及其他凭证(例如,如果您使用 S3 插件,就像 AWS 密钥一样)。
该密钥库由 Elasticsearch docker 映像中的*elasticsearch-keystore* 创建。

2.1 增加setup-keystore.sh/setup

这个脚本创建一个 Keystore 并将其写入/secrets/elasticsearch.keystore,然后将$ELASTIC_PASSWORD添加为默认超级用户elastic,稍后我们将使用docker-compose$ELASTIC_PASSWORD传递给安装容器。

2.2 增加setup-certs.sh/setup

该脚本创建一个自签名的单个 PKCS#12 密钥库,其中包括节点证书、节点密钥和 CA 证书。

2.3 在根目录下创建docker-compose.setup.yml将启动安装容器。

这个docker-compose创建了一个临时容器,它使用我们之前编写的脚本生成密钥库和证书。

使用以下命令运行:

docker-compose -f docker-compose.setup.yml run --rm keystore
docker-compose -f docker-compose.setup.yml run --rm certs

这将创建容器,并在完成时删除它们以节省空间。将其输出保存到/secrets目录。

3.使用 Docker-Compose 将堆栈放在一起。

最后,创建主docker-compose.yml文件,将所有内容放在一起。

Docker-Compose 文件中有什么?

  1. 我们声明 Elasticsearch 的容量将持久存储数据。
  2. 我们声明传递给 Elasticsearch 的秘密密钥库,它包含凭证(目前只有超级用户密码,但是以后可以通过扩展setup-keystore.sh脚本来保存许多其他凭证)
  3. 我们声明 ELK 的 3 个服务(Elasticsearch、Logstash 和 Kibana)
    传递构建图像和运行容器所需的环境变量,召回所有包含${ELASTIC_XXX}配置的配置文件,您必须在这里看到它们被传递。注意,我们也没有在Docker-Compose文件中硬编码它们的值,而是在.env文件中声明所有内容,这样它就保存了所有参数。
  4. 我们还声明了 Elasticsearch 的推荐设置,如ulimitES_JAVA_OPTS,并为堆栈组件设置了堆大小。

将所有参数添加到 **.env** 文件

启动堆栈

运行以下命令:

设置(仅运行一次)

docker-compose -f docker-compose.setup.yml run --rm keystore
docker-compose -f docker-compose.setup.yml run --rm certs

启动弹性堆栈

docker-compose up -d

前往localhost:5601
用户名 : elastic
密码 : changeme

登录弹性基巴纳

4.开始运送日志!🎉

您现在可以开始将日志和指标发送到 Elastic Stack。
1使用 Filebeat 从服务中发送日志
2。使用 Metricbeat 从服务中发送指标。
直接到 Elasticsearch 或者用 Logstash 摄取。并在 Kibana 的发现窗口中浏览它们。

有许多指南可以帮助你将原木运送到弹性堆上。

从这里去哪里?

我们还可以进一步增强这种组合,我们可以启用自我监控,或添加 Prometheus Exporters 以使用 Grafana 监控堆栈,除了我们启用的内部节点传输加密之外,还可以为 HTTP 层启用 SSL,为映像添加健康检查,使用 ElastAlert 或 Curator 等工具来利用堆栈,向集群添加更多节点。

使用 Elastdocker 代替。

Elastdocker 是一个模板库,拥有我们刚刚制作的更复杂版本的弹性堆栈设置,包括上面提到的所有要点。

[## sherifabdlnaby/elastdocker

用像 Curator,Rubban,ElastAlert 这样的工具进行预警。弹性堆栈(又名 ELK) Docker 组成,预配置…

github.com](https://github.com/sherifabdlnaby/elastdocker)

缩放弹性堆栈

这种设置可以处理少量生产工作负载,但最终需要扩展。

  • 您需要将栈组件解耦,以便独立地部署到不同的 docker 主机上。
  • 您可能需要添加额外的 Elasticsearch 节点。
  • 您可能需要横向扩展 Logstash。
  • 将当前设置转换为 Kubernetes 设置,改为使用窗格图表。(前提是你的组织里有 k8。)

分解成吊舱的弹性堆栈的可能 K8S 部署

您可以看看托管弹性堆栈产品。

本帖使用的代码:https://github.com/sherifabdlnaby/medium-elastic-stack

使用机器学习和 Strava 智能运行

原文:https://towardsdatascience.com/running-smart-with-machine-learning-and-strava-9ba186decde0?source=collection_archive---------19-----------------------

基于你和其他运动员的体育观察数据,你的最大潜在训练收获的有序列表。

聪明跑步?

作为一名跑步者,我总是希望提高我的个人最好成绩,我经常想,如何优化我跑步的时间,以获得最好的成绩,同时尽可能地懒惰。有时候我一周跑七次,但是几个月都没有提高速度。其他时候,在几周的徒步旅行、饮酒和每周只跑一两次之后,我跑得非常好。这违背了经营论坛和培训计划的智慧。幸运的是,我已经在 Strava 上记录了五年多的训练,许多人也是如此。这些数据中一定有某种模式。

所以我建立了一个服务,在其他运动员允许的情况下,从运动员的社交网络 Strava 获取数据。机器学习算法从所有运动员的数据中确定哪些因素对改善他们的跑步最重要。通过这种算法,有可能给运动员一个有序的训练改进列表,他们可以进行这些改进以获得更快的速度。

这篇文章很长。

这个问题不容易解决,解决方案也不容易解释,简而言之,因为跑步教练和机器学习工程师之间的重叠可能相当小。出于这个原因,我在顶部放了一个总结部分,并让每个技术部分对于那些研究细节的人来说都是相当独立的。我解释了整个过程,从开始到结束,包括生产“产品”

摘要

从多个跑步者那里获得数据后,我建立了两个模型:

  • 一个模型,可以预测,高达 89%的准确性(R),什么时候运动员将运行在一场比赛(5k 至 42k)完全基于他们的训练计划。换句话说,根本不用他们的步速数据来确定他们的步速。这个模型并不总是对普通跑步者有用,因为在你的速度和你跑的频率或距离之间有很多关联。当与模型比较时,普通运动员可以看到他们相对于顶级运动员是如何训练的,但看不到如何到达那里。
  • 一个模型,它可以预测运动员根据他们的训练计划将会提高多少。这个模型对跑步者更有用,因为它可以告诉普通跑步者怎样做才能跑得更好。然而,我觉得我需要更多的训练数据来达到可接受的 70%。

整个系统现在可以在的一个简单网站中获得【更新:我已经关闭了该网站,因为该系统很快就用完了我的主办预算】,运动员可以在那里注册并深入了解他们的训练计划。

第一个模型的系统输出的原型可视化如下所示。这里,蓝色标记显示了运动员相对于前 10%和后 10%运动员的训练情况。该列表是按照运动员训练中最重要的因素排序的,是每个因素的重要性和运动员在其中的缺点的产物。

在这里,很明显,运动员需要每周跑更多次,跑更多的距离。在此过程中,他们应该花更多的时间在心率 2 区。然而,有一些事情他们做得很好,甚至相对于最好的运动员。例如,在他们跑步前的三个月,他们减少了花在非跑步活动上的时间,因为他们更专注于跑步,并且他们增加了长跑的比例。

深入!

从现在开始,事情变得更加技术性:

为问题设定一些界限

  • Strava API 不会返回他们拥有的所有数据(细粒度的心率数据)或见解。返回的数据是每公里的平均值,这意味着人们无法看到在几分钟内均匀分布的因素。例如,由于非常短的短跑训练计划导致的心率或节奏的峰值不能被考虑。
  • 这种分析着眼于“宏观”的训练计划——你在长期训练中的表现。例如,你如何安排你的周数,增加距离,平均跑步的轻松程度。幸运的是,这是跑步者面临的最热门的问题之一。选择一个有效的培训计划很难。
  • 有许多方法可以解决这个问题,导致许多模型和特征工程决策更多的是艺术而不是科学。例如,我选择只关注个人最佳成绩(PB)前三个月训练的因素。这个决定背后有一些科学依据,但选择两个月或六个月可能会更有效。其他类似的决定包括为相对于其他跑步的“长”跑或“快”跑设置阈值。
  • 很多我想拥有的因素都不见了。这些模型永远不会完美,因为 Garmin 和 Strava 对他们的体重、饮食、压力和睡眠统计数据掌握得非常紧密。
  • 类似地,这些模型平均来说表现不错,但是每个运动员的效果可能不同。一些运动员比其他人给模型添加更多的噪音。随着更多的数据,这种噪声将在模型中考虑。但当涉及到个性化的可视化时,建议会有所不同:例如,你可能是一个非常健康的人,训练非常完美,但需要减掉 10 公斤的上身肌肉才能改善。
  • 同样,这个模型是为普通跑步者设计的。有了更大的数据集,我相信我可以在相似跑步者的子集上运行该模型,以便为跑步者提供更相关的建议,例如显示排名后 10%的跑步者,喜欢他们的人是如何改进的。

掌握技术——首先是一些跑步科学

我在这里解决的问题是长跑:5 公里到 42 公里之间。虽然这些仍然是非常不同的比赛,但动力是相似的,有一个公式和常数变量控制着运动员在这个范围内任何距离的速度之间的映射。假设我经常跑 25 公里到 30 公里的快速(速度或门槛)跑和长距离跑,从我的 5 公里时间推断出我的马拉松时间是相当简单的。这是通过传奇跑步教练杰克·丹尼尔开发的常数和公式“vdot”来完成的。在我的整个模型中,假设 vdot 是您整体跑步健康状况的准确代表,我们处理的是 5-42 公里距离范围内的健康状况。

然而,vdot 并不是健身的完美同义词,与其说它是速度的度量。对于健康的整体测量,人们必须考虑 OBLA、V02max、肌肉效率和肌肉功率输出与足部划水持续时间的关系。我们无法用腕表数据测量那些。但是我们可以衡量是什么帮助人们提高最终目标——速度。

大多数跑步者使用运动员社交网络 Strava 来上传他们的数据,这也是我获取数据的地方。Strava 确实在他们自己的模型中估计你的适合度(vdot 或他们自己的度量),但并不通过他们的 API 提供这一点。出于这个原因,我建立的模型只能在你跑个人最好成绩(PB)时推断你的健康状况,Strava 确实将它作为“估计的最佳努力”提供有可用的选项,例如根据心率和速度之间的关系来估计健身,但这将在最小可行产品(MVP)的模型中添加太多噪声。

我最大的努力

为了达到这些最佳效果,人们普遍认为,你必须在努力训练和恢复之间保持微妙的平衡,并通过大量轻松跑步来保持你的机器运转良好。前者由 trainingpeaks 所谓的脉冲反应模型定义——你让你的身体处于压力之下,它通过变得更强来回应这种压力。后者只是因为科学表明,我们的身体在有氧心率区获得最有效的表现,这对应于“区域 2”中相对轻松的跑步速度。

这将我们带到心率区:教练和运动员根据每位运动员的最大心率定义了一组区域或范围,为我们提供了基于心率数据进行训练的目标区域,从而简化了我们的工作。这些区域从 Z1 到 Z5,从最小努力到最大努力。许多专业运动员完全按照心率范围进行训练,而不是按照速度,因为我们的心脏是我们身体对训练反应的最佳指标。下图给出了区域的指示。请注意,根据您的年龄来估计您的心率范围是一种相对糟糕的做法,因为这是根据遗传而不是健康状况来变化的。例如,克里斯·弗鲁姆的最大心率是 161 ,根据这个模型应该是 186。幸运的是,Strava 不做这种假设,而是随着时间的推移来确定这一点。

心率区(维基共享)

我们可以在心率较高的区域花较少的时间训练,但在较高的区域花一点时间会受益匪浅。不同的教练有不同的看法,一般的建议是花 80%的时间在“轻松”区 Z1-Z3,20%的时间在“困难”区 Z4 和 Z5。然而,这也有所不同,取决于你是在训练季节的早期,准备为一场比赛“达到顶峰”,还是为一场比赛“逐渐减少”你的努力。

为了管理你在这些区域的时间,并进一步简化训练,教练建议几种类型的跑步:

  • 轻松跑步
  • 长距离跑
  • 间歇训练
  • 节奏跑步
  • 用大步跑或法特莱克跑轻松跑,快速冲刺而没有太多相应的心率增加
  • 以上的任何组合。

如果这还不够,还有关于每周增加多少里程的科学,一个宏观类型的脉冲响应模型,在这里你的身体慢慢适应更多的努力,因此变得更健康。过度增加这一点的代价当然是受伤或倦怠。

简化的跑步科学

综上所述,对于每一个运动员来说,都存在着一个最佳的但又难以找到的训练平衡

  • 每项活动的轻松和努力
  • 跑步和休息,取决于你之前的活动
  • 轻松跑、长跑、间歇训练、速度跑
  • 跑步和交叉训练
  • 每周增加你的努力

如果没有对自己或教练的深入了解,即使是经验丰富的跑步者,也很难找到这种平衡。有可用的工具,如 TrainingPeaks 的性能管理器和 Strava 的高级产品。他们很棒,尤其是他们告诉你,你训练太辛苦了,需要休息。然而,我发现他们都专注于试图估计你当前的训练负荷,因此依赖于大量不准确的每日心率数据。我需要一些东西来建议我在几个月的时间里可以做哪些不同的事情,而不是每天都做。

所以,作为一名跑步者,我建立了这个分析作为一个辅助工具:告诉我在更长的几个月里,相对于其他运动员,我可以做些什么不同。

系统的高级概述

像这样的系统有几个部分,需要收集数据,训练模型,运行模型,并为前端用户提供见解。由于 API 访问或处理所需的时间,人们可以将它们视为如下交互的系统,绿色线条表示同步流,橙色线条表示异步流:

获取数据:Strava API

Strava 对他们的 API 的使用有很大的限制。没有运动员的允许,它不能被查询,然后被限制速度,每天只允许大约 10 个跑步者的数据被提取。这降低了数据采集的速度。

除此之外,Strava API 为每位运动员提供:

  • 运动员的心率区——他们体验 Z1-Z5 的心率
  • 一个活动列表,根据活动类型进行标记,包括跑步、自行车或运动员选择上传的任何内容
  • 每次跑步的距离、心率、配速和“圈数”——通常以公里为单位
  • 每圈的平均心率、配速和节奏
  • 对于每一次运行,无论它是否是“最大努力”——换句话说,一个 PB

我的 20 个朋友非常友好地通过 Strava 向我提供了他们所有的数据。鉴于他们每个人的数据平均有 10 个 Pb,这为我的模型提供了近 200 个输入,足以建立一个精确的模型来估计 vdot,但不足以建立一个模型来估计 vdot 的改善,这是我稍后将解释的问题。

  1. 遍历所有运动员活动,确定哪些是 PBs。对于每个 PB,如果它在前一个 PB 之后超过一个月,并且超过前一个记录 0.5 vdot,则成为一个数据点。这是为了确保每个数据点都是足够大的独立 PB。
  2. 根据运动员的所有活动,构建一个回归变量,将该运动员在一个月的时间间隔内的步速映射到心率。这是因为许多运动员活动是在没有心率监测器的情况下进行的(一般来说,心率监测器只是在过去两三年才被广泛使用)。然而,用这个回归量仍然可以相当准确地估计心率,R 为 0.9。这在训练机器学习模型时非常有用:它将整个模型的 R 提高了 0.1
  3. 对于每个 PB,使用杰克·丹尼尔公式确定该 PB 的 vdot。这是第一个机器学习模型的“y”:模型被训练的特征。对于第二个模型,使用 vdot 的变化或 delta-y。同样对于每个 PB,提取 PB 之前 3 个月的所有活动。根据最终模型的 R 选择 3 个月。
  4. 对于每个活动,提取有用的特征,例如活动的类型、长度、总体努力等。虽然我不会对模型特征进行太深入的讨论,但诸如心率的标准差之类的东西可用于确定活动是否是间歇训练,相对努力和活动的长度可用于确定它是一种节奏还是一次长跑。类似的逻辑也适用于加快步伐、爬山重复等。
  5. 特别是在几年前的活动的情况下,步速数据用于推断活动的预期心率,以使得能够从心率中提取一些以上和以下特征。
  6. 活动按周分组,这种方法我还是有点不舒服,因为有些运动员可能以 10 天或 14 天为周期进行训练。对于每周,提取更多的特征,例如每周里程、跑步和其他活动之间的分割以及跑步类型之间的比率。
  7. 将周分组为训练块也是如此。在这里,整个三个月被考虑,并且特征更符合三个月中里程、努力和整体行为变化的相对增加。最后,比赛前的锥度也被分析为一个因素。

所有上述特征都存储在每个运动员的数据库中。都是用来训练最终的机器学习模型。需要注意的是,节奏不是一个特征,因为用它来预测是很愚蠢的..步伐。

机器学习模型

对于机器学习专家来说,这一部分将是相当乏味的。根据所选择的特性,性能最好的模型是决策树,紧随其后的是普通的 XGBoost。调整这些模型显示,相对于通过一些特征工程来改善数据,收益甚微,例如在没有心率数据的情况下,从心率外推心率。

直觉上,并且基于模型性能,问题是近似线性的。

正如本文前面提到的,该模型分为两部分:

  • 一个模型是在 vdot 上训练的:确定解释运动员 vdot 的特征,本质上是马拉松配速。这在最好的情况下有 0.89 的 R,但是根据运动员的不同可以低至 0.6。
  • 另一个模型,真正的金罐,在运动员最后一次 PB 之后,在 vdot 的变化上被训练。这更有价值,因为它不容易受到相关性和混杂因素的影响。然而,在这里,最好的情况 R 只有 0.5。

每个的特征(基于我的 n=200 的小数据集)如下。随着数据库的增长,我将对此进行更新:

解释 vdot(健身)的功能

在这里,我们有一个相当健康的 SHAP 情节。在一个简单的系统中,该图的健康状况通常由红点和蓝点之间的清晰分隔来表示。

  • 所有功能都是针对 PB 之前的 3 个月培训期。
  • 普通特征(常数)以 f_ 为前缀。例如,f_avg_weekly_run_distance 是运动员跑步的平均距离(km)。
  • 相对特征(相对于这个运动员的其他数据)以 r_ 为前缀。例如,“r_proportion_yoga”是运动员在这三个月相对于之前训练的剩余时间做了多少瑜伽。

请记住,这些特征被认为对非线性模型很重要:随机森林回归算法,所以我们不能把它作为跑步者的重要特征清单。我会对“r_proportion_yoga”这样的功能持保留态度。例如,该模型可能已经确定,如果运动员不做很多简单的跑步,那么他们花在做瑜伽上的时间就格外重要。但是由于这个模型的 R 相当好,并且考虑到我们有一个几乎的线性问题,以这种方式看特征是安全的,或者运行一个线性 XGBoost 特征分析。

关于跑步者在 PBs 前的表现,我们可以得出几个相当可靠的结论:

  • 他们比跑得慢的人跑得多,总次数也多
  • 他们在心率区 1、2、4 和 5 的时间较长,在心率区 3 的时间较短。换句话说,他们要么跑得很轻松,要么跑得很艰难。
  • 他们和更多的运动员一起跑步(r _ mean _ athlete _ count)——这肯定是相关的,因为他们可能会在社交生活中关注自己的主要爱好
  • 他们跑得更高了
  • …等等。

解释相对 vdot(适应性改善)的特征

作为一个快速的回顾,我没有看健康的运动员做了什么,而是训练了一个模型,这个模型是关于运动员通常做了什么来最大程度地提高 vdot。这可能是一个拥有足够数据的异常强大的分析,因为它会将相关性从等式中剔除。我们会看到普通人是如何变得更好的。

在这里,我们的 SHAP 情节并不健康。有许多对模型有很大影响的无关特性。

该模型的最佳 R 为 0.5。因此,就目前的数据集规模而言,即使对于最容易解释的运动员表现,它也只能解释 50%的提高速度的因素。

同样重要的是,该模型比预测纯 vdot 的模型更不线性。一些运动员可能因为是这项运动的新手而跑 PBs,因此该模型可能会决定“那些平均每周跑不到 30 公里的人需要比平时跑得更多才能达到他们的 PB,但那些每周跑超过 120 公里的人需要跑得相对更少但跑得更快。”然而,让我们来看一看有意选择的功能是什么:

  • 快跑。很多。
  • 骑自行车的次数相对比以前少了(我住在荷兰,数据集中的许多运动员也住在荷兰,所以当我们终于在我们热爱的夏天停止骑自行车,专注于跑步时,我们变得更快了)
  • f _ slope _ time _ before _ taper:这是在运动员开始减量之前,锻炼时间“增加”的指示。它被认为是重要的,要么增加很多(对一些运动员来说),要么减少一点负荷(更多运动员)
  • f _ slope _ distances _ before _ taper:这里有一个相当明确的迹象表明运动员需要跑更多的距离。所以把这个和前一个变量考虑进去,可能是他们需要跑的更少,但是跑的更远。
  • f _ proportion _ distance _ activities:这支持了前面的两点:在跑 PB 之前跑更大比例的长距离显然是很重要的。请记住这种相关性的潜在危险——运动员经常在有意尝试马拉松 PB 之前进行几次长跑。

可视化

那么这对个人有什么用呢?有了上述模型,就有可能将某个运动员的训练与理想情况进行比较。如下所示,在此处有关于阅读图表的解释。

这些特性按照潜在增益排序:模型中特性的能力,乘以你在这方面的训练有多差。除了明显的“跑得更多”和“跑得更频繁”,该模型清楚地指导运动员“以 Z2 的心率跑得更多”、“跑更多的山”和“少游泳”。

生产

这种小规模的生产相当简单,我使用了完全的谷歌云设置。结果是一切都非常快,除了 Strava API 查询的速度限制在每秒 1 次左右。

  • 由于特征工程和机器学习后端是在 Python 上运行的,所以我用了 Flask 前端
  • 该解决方案托管在谷歌云应用引擎上
  • 该数据库托管在谷歌云 SQL 上
  • 一旦新运动员注册,Cron jobs 就会运行特征工程和 ML 训练算法
  • 当加载可视化页面时,Flask 查询后端以生成 matplotlib 图像

下一步是什么?

有许多改进可以做,特别是对可视化及其解释。然而,首先,我想获得更多的数据,以便建立一个更好的模型来解释如何获得更快的速度。这种更具因果关系的模型对运动员个人来说会更加有用。

在 Docker 容器中运行 Spark NLP,用于命名实体识别和其他 NLP 特性

原文:https://towardsdatascience.com/running-spark-nlp-in-docker-container-for-named-entity-recognition-and-other-nlp-features-8acdb662da5b?source=collection_archive---------28-----------------------

在 Docker 环境下使用 Spark NLP 和 Jupyter notebook 进行自然语言处理

作者照片

如[1]所述,自然语言处理(NLP) 是语言学、计算机科学、信息工程、人工智能等许多研究领域共有的一个共同研究子领域。NLP 通常关注计算机和人类自然语言之间的交互,特别是如何使用计算机来处理和分析自然语言数据(例如,文本、语音等)。).NLP 中的一些主要挑战包括语音识别、自然语言理解(例如,文本理解)和自然语言生成。

机器学习在文本理解中的早期应用之一是电子邮件和垃圾消息检测[1]。随着深度学习的推进,许多新的高级语言理解方法已经问世,如深度学习方法 BERT (使用 MobileBERT 进行问答的例子见[2】)。

NLP 中另一个流行的方法是命名实体识别(NER) 。NER 的主要目的是提取命名实体(例如,个人姓名、组织名称、地点名称、产品名称等。)来自非结构化文本。有许多支持 NER 的开源 NLP 库/工具,如 NLTK 和 SpaCy [3]。最近,Spark NLP [4]得到了越来越多的关注,因为它提供了更完整的受支持的 NLP 特性列表[5][6]。

在我看来,Spark NLP [4]的开发是基于 Ubuntu Linux 和 OpenJDK 的。因此,由于 Colab 使用 Ubuntu 操作系统,所以在 Colab 中直接设置 Spark NLP 的环境(参见指令和代码示例)。然而,我注意到很难在 Mac 上为 Spark NLP 设置一个本地环境,原因如下:

为了避免这个问题,本文演示了如何建立一个 Docker 环境[7]来运行 NER 的 Spark NLP 和 Docker 容器中的其他 NLP 特性。这样的 Docker 环境可以作为建立 Spark NLP 微服务平台的基础。

1.Docker 简介

如[7]中所述,Docker 是一种工具,它允许我们在沙箱(称为容器)中轻松部署应用程序(例如 Spark NLP),以在任何 Docker 支持的主机操作系统(即 Mac)上运行。

Docker 的基本概念是:

  • Dockerfile:
  • Docker 图像
  • 码头集装箱

1.1 文档文件

一个Docker file【7】是一个简单的文本文件,包含一个用于创建 Docker 映像的命令列表(类似于 Linux 命令)。这是一种自动化 Docker 图像创建过程的方法。

1.2 Docker 图像

一个 docker 映像【7】是一个只读模板,包含一组用于创建 docker 容器的指令,该容器可以在 Docker 平台上运行。它提供了一种打包应用程序和预配置服务器环境的便捷方式。

Docker 映像是从 Docker 文件构建的。

1.3 码头集装箱

容器是包含代码及其所有依赖项的标准软件包,因此应用程序可以从一个计算环境快速可靠地运行到另一个计算环境。一个 Docker 容器【7】是一个轻量级的、独立的、可执行的软件包,它包含了应用程序的一切,比如代码、运行时、系统工具、系统库和设置。

Docker 容器是从 Docker 映像构建的。

2.用 Jupyter 笔记本为 Spark NLP 设置 Docker 环境

设置 Docker 环境以使用 Jupyter notebook 运行 Spark NLP 的过程包括以下步骤:

  • 安装 Docker
  • 在 Docker Hub 注册
  • 创建 Dockerfile 文件
  • 建立码头形象
  • 启动码头集装箱
  • 推送 Docker 图像
  • 拉动 Docker 图像

2.1 安装 Docker

不同平台安装 Docker 的说明网上有: MacLinuxWindows

一旦 docker 安装完成,我们可以使用以下 Docker 命令和相应的输出来验证安装:

docker --version
Docker version 19.03.8, build afacb8b

2.2 在 Docker Hub 注册

类似于 Github 分享源代码文件, Docker Hub 就是分享 Docker 镜像。为了共享本地机器上的 docker 映像,需要将本地机器上的 docker 映像推送到 Docker Hub 服务器,以便其他人可以从 Docker Hub 获取 Docker 映像。

需要先去 Docker Hub 注册才能使用 Docker Hub 服务。

2.3 创建 Dockerfile

为了构建新的 Docker 映像,首先需要创建一个 Docker 文件。

为了简化运行 Spark NLP workshop 的过程, John Snow LABS 提供了一个Spark NLP workshop Docker file,用于在 Docker 容器中运行 workshop 示例。

为了在 Docker 容器中构建一个新的 Docker 映像来运行 Spark NLP 和 Jupyter notebook,我基于 Spark NLP workshop Dockerfile 文件创建了一个新的Docker 文件【8】,并做了以下修改:

  • 删除了教程和相关的笔记本和数据文件
  • 用 Spark NLP 2.5.1 替换 Spark NLP 2.4.5
  • 已调整 docker hub 用户名
  • 调整了 docker 容器中的主目录名
  • 添加了命令行选项,将主机上的当前工作目录映射到 Docker 容器中的主目录
  • 移除 Jupyter 笔记本配置

2.4 建立码头工人形象

使用新的 Docker 文件[8],可以按如下方式构建新的 Docker 映像:

docker build -t zhangyuefeng123/sparknlp:1.0 .

构建完成后,应该会显示以下 docker 命令和 Docker 图像标记:

2.5 启动码头集装箱

一旦新的 Docker 映像准备就绪,就可以使用下面的命令启动一个新的 Docker 容器,使用 Jupyter notebook 运行 Spark NLP:

docker run -it --volume $PWD:/home/yuefeng -p 8888:8888 -p 4040:4040 zhangyuefeng123/sparknlp:1.0

如果一切顺利,应该会显示以下输出:

2.6 推送 Docker 图像

为了与他人共享本地主机上的 Docker 映像(如zhangyue feng 123/spark NLP:1.0),需要将映像推送到 Docker Hub,如下所示:

docker push zhangyuefeng123/sparknlp:1.0

以下是 Docker Hub 推送 Docker 图片的结果:

2.7 拉动 Docker 图像

如果具有预期功能的 Docker 映像(例如,zhangyue feng 123/spark NLP:1.0)已经存在于 Docker Hub 中,则它可以被拉到本地主机上以供重用,而不是从 Dockerfile 构建新的 Docker 映像,如下所示:

docker pull zhangyuefeng123/sparknlp:1.0

3.使用 Docker 容器中的 Jupyter 笔记本运行 Spark NLP

一旦一个新的 Docker 容器开始运行(详见第 2.5 节),我们可以复制生成的 URL,如下所示,并将其粘贴到 Web 浏览器中,以启动 Jupyter notebook Web 界面:

http://127.0.0.1:8888/?token=9785e71530db2288bc4edcc70a6133136a39c3f706779554

一旦 Jupyter 笔记本启动,我们就可以像往常一样使用它(详见下一节)。

4.将 Spark NLP 用于 NER 和其他 NLP 功能

为了验证 Docker container 中运行的 Jupyter notebook 具有相同的预期功能,我创建了一个新的 Jupyter notebookSpark-nlp-Docker-demo . ipynb,并使用它来执行[6]中的主要代码片段,以将 Spark NLP 应用于 ner 和其他 NLP 功能。

首先,下面的代码导入所需的 pyspark 和 spark NLP 库,然后启动一个 Spark 会话,在 Spark 上运行 Spark NLP:

from pyspark.sql import SparkSession
from pyspark.ml import Pipelineimport sparknlp
from sparknlp.annotator import *
from sparknlp.common import *
from sparknlp.base import *spark = sparknlp.start()

官方 CoNLL2003 数据集的训练和测试数据集被下载用于演示目的;

from urllib.request import urlretrieveurlretrieve('[https://github.com/JohnSnowLabs/spark-nlp/raw/master/src/test/resources/conll2003/eng.train'](https://github.com/JohnSnowLabs/spark-nlp/raw/master/src/test/resources/conll2003/eng.train'),
           'eng.train')urlretrieve('[https://github.com/JohnSnowLabs/spark-nlp/raw/master/src/test/resources/conll2003/eng.testa'](https://github.com/JohnSnowLabs/spark-nlp/raw/master/src/test/resources/conll2003/eng.testa'),
           'eng.testa')

下面的代码用于读取定型数据集并显示前 500 条记录。训练数据集遵循训练集中用于训练 NER 模型的注释的标准格式。

with open("eng.train") as f:
    c=f.read()print (c[:500])

训练数据集可以以更易读的格式加载:

from sparknlp.training import CoNLLtraining_data = CoNLL().readDataset(spark, './eng.train')
training_data.show(3)
training_data.count()

以下代码加载预训练的 BERT 嵌入模型,并使用它将测试数据集转换为 BERT 嵌入格式(即,将每个单词编码为 768 维向量)。

bert_annotator = BertEmbeddings.pretrained('bert_base_cased', 'en') \
 .setInputCols(["sentence",'token'])\
 .setOutputCol("bert")\
 .setCaseSensitive(False)\
 .setPoolingLayer(0)test_data = CoNLL().readDataset(spark, './eng.testa')test_data = bert_annotator.transform(test_data)
test_data.show(3)

下面的代码显示了句子的标记、相应的 BERT 嵌入和相应的带标签的 NER 标签。

test_data.select("bert.result","bert.embeddings",'label.result').show()

以下代码首先将测试数据集中的 1,000 条记录保存到一个 Parquet 文件中,然后创建一个基于 Tensorflow 的字符级 CNN-DLSTM 模型 NerDLApproach ,使用经过训练的 BERT 嵌入模型 bert_annotator 和 NerDLApproach 模型形成一个管道,最后使用训练数据集中的 1,000 条记录和 Parquet 文件中保存的 1,000 条测试记录来训练管道。

test_data.limit(1000).write.parquet("test_withEmbeds.parquet")nerTagger = NerDLApproach()\
  .setInputCols(["sentence", "token", "bert"])\
  .setLabelColumn("label")\
  .setOutputCol("ner")\
  .setMaxEpochs(1)\
  .setLr(0.001)\
  .setPo(0.005)\
  .setBatchSize(8)\
  .setRandomSeed(0)\
  .setVerbose(1)\
  .setValidationSplit(0.2)\
  .setEvaluationLogExtended(True) \
  .setEnableOutputLogs(True)\
  .setIncludeConfidence(True)\
  .setTestDataset("test_withEmbeds.parquet")pipeline = Pipeline(
    stages = [
    bert_annotator,
    nerTagger
  ])ner_model = pipeline.fit(training_data.limit(1000))

然后,经过训练的管道可用于预测测试数据集的 NER 标签(参见下面的前 20 行结果):

predictions = ner_model.transform(test_data)
predictions.select('token.result','label.result','ner.result').show(truncate=40)

标记为 NER 标签的前 20 行标记和相应的预测 NER 标签可以以更可读的格式显示:

import pyspark.sql.functions as Fpredictions.select(F.explode(F.arrays_zip('token.result','label.result','ner.result')).alias("cols")) \
.select(F.expr("cols['0']").alias("token"),
        F.expr("cols['1']").alias("ground_truth"),
        F.expr("cols['2']").alias("prediction")).show(truncate=False)

以下代码显示了如何使用预训练的管道为给定的句子生成 NER 标记。

from sparknlp.pretrained import PretrainedPipelinepretrained_pipeline = PretrainedPipeline('recognize_entities_dl', lang='en')text = "The Mona Lisa is a 16th century oil painting created by Leonardo. It's held at the Louvre in Paris."result = pretrained_pipeline.annotate(text)list(zip(result['token'], result['ner']))

不同的预训练模型可用于形成新的管道:

import json
import os
from pyspark.ml import Pipeline
from sparknlp.base import *
from sparknlp.annotator import *
import sparknlpdef get_ann_pipeline ():

    document_assembler = DocumentAssembler() \
        .setInputCol("text")\
        .setOutputCol('document') sentence = SentenceDetector()\
        .setInputCols(['document'])\
        .setOutputCol('sentence')\
        .setCustomBounds(['\n']) tokenizer = Tokenizer() \
        .setInputCols(["sentence"]) \
        .setOutputCol("token") pos = PerceptronModel.pretrained() \
          .setInputCols(["sentence", "token"]) \
          .setOutputCol("pos")
    embeddings = WordEmbeddingsModel.pretrained()\
          .setInputCols(["sentence", "token"])\
          .setOutputCol("embeddings") ner_model = NerDLModel.pretrained() \
          .setInputCols(["sentence", "token", "embeddings"]) \
          .setOutputCol("ner") ner_converter = NerConverter()\
          .setInputCols(["sentence", "token", "ner"])\
          .setOutputCol("ner_chunk") ner_pipeline = Pipeline(
        stages = [
            document_assembler,
            sentence,
            tokenizer,
            pos,
            embeddings,
            ner_model,
            ner_converter
        ]
    ) empty_data = spark.createDataFrame([[""]]).toDF("text") ner_pipelineFit = ner_pipeline.fit(empty_data) ner_lp_pipeline = LightPipeline(ner_pipelineFit) return ner_lp_pipeline

以下代码使用上述函数创建一个新管道,然后使用它为给定的句子生成各种注释/标记:

conll_pipeline = get_ann_pipeline ()
parsed = conll_pipeline.annotate ("Peter Parker is a nice guy and lives in New York.")
parsed

5.摘要

Spark NLP [4]越来越受欢迎,因为它在一个系统中支持更多的 NLP 功能。Spark NLP 是在 Ubuntu Linux 系统上用 OpenJDK 开发的。根据我的经验,我注意到很难为 Spark NLP no Mac 设置一个本地环境,这是由于一个已知的异常“异常:Java 网关进程在发送其端口号之前退出”。

为了避免这个安装问题,在本文中,我演示了如何设置一个 Docker 环境来运行 Spark NLP 和 Jupyter notebook for NER 以及 Docker 容器中的其他 NLP 功能。

我使用[6]中的代码示例验证了 Mac 上 Spark NLP 的 Docker 环境。

Spark NLP 的 Docker 环境有潜力作为建立 Spark NLP 微服务平台的基础。

Docker 文件和 Docker 的 Jupyter 笔记本都可以在 Github [8]中获得。

参考

  1. Y.张,利用 word2vec-keras 进行自然语言处理的深度学习
  2. Y.张,面向移动设备的自然语言处理深度学习
  3. 南李,利用 NLTK 和 SpaCy 进行命名实体识别
  4. 火花 NLP
  5. 动词 (verb 的缩写)科贾曼,Spark NLP 简介:基础和基本组件
  6. 动词 (verb 的缩写)科贾曼,命名实体识别(NER)与 BERT 在 Spark NLP 中
  7. 页(page 的缩写)适合初学者的 docker
  8. Y.张,Github 中的 Dockerfile 和 Jupyter 笔记本

使用 Monk AI 的俄语字母分类

原文:https://towardsdatascience.com/russian-alphabets-classification-using-monk-ai-4df7d1ad8542?source=collection_archive---------54-----------------------

让计算机视觉应用变得简单有效

鸣谢:图片来自 FlickerPolyrus

目录:

  1. 介绍
  2. 关于数据集
  3. 设置 Monk 和先决条件
  4. 下载数据集
  5. 创建项目和实验
  6. 方法
  7. 选择最佳模型
  8. 结论

简介:

对我们来说,对手写信息进行分类是一件容易的事情,但对计算机来说,这是一项令人不安和令人畏惧的工作。手写字符分类通常是一项具有挑战性的任务,因为任何字符都有无数种书写方式。

尽管神经网络已经重新定义了这项任务,但它在开发这种分类器方面也发挥了巨大的作用,前提是为它提供了大量的数据来进行训练。

在这个特别的博客中,我们将探索俄语字母表,并以手写形式对它们进行分类。

关于数据集:

在数据集中,我们有 14190 个彩色图像分布在 3 个图像文件夹中,包括所有 33 个类别的俄语字母表。

第一个文件夹具有条纹背景(具有很少的水平线和/或垂直线),第二个文件夹具有白色背景,第三个文件夹具有图形类型背景(具有许多有序的水平线和垂直线)。

以下是数据集中的一些图像:

数据集的样本图像

以下是用于预测目的的俄语字母及其相应的数字:

а=>1, б=>2, в=>3, г=>4, д=>5, е=>6, ё=>7, ж=>8, з=>9, и=>10, й=>11, к=>12, л=>13, м=>14, н=>15, о=>16, п=>17, р=>18, с=>19, т=>20, у=>21, ф=>22, х=>23, ц=>24, ч=>25, ш=>26, щ=>27, ъ=>28, ы=>29, ь=>30, э=>31, ю=>32, я=>33

所以不要担心,即使我们没有得到这篇文章,我们也会留下一些俄罗斯字母的印象!
完整的数据集由 Olga Belitskaya 准备并上传,可以在这里找到:

[## 手写信件的分类

俄罗斯字母的图像

www.kaggle.com](https://www.kaggle.com/olgabelitskaya/classification-of-handwritten-letters)

现在让我们开始设置僧侣和一些先决条件。

设置 Monk 和先决条件

我们从安装 monk 库开始。

1.)我们在这里使用过 colab,所以这里是同样的安装过程。

#Installation process for colab
pip install -U monk-colab

虽然我们可以在僧库探索其他的安装方式。

2.)将它添加到系统路径(每个终端或内核运行都需要)

import sys
sys.path.append("monk_v1/");

下载数据集

接下来,我们将从 Kaggle 直接获取数据集到 colab。为此,我们必须首先从 Kaggle 创建一个 API 令牌。

转到您的 Kaggle 个人资料>>我的帐户>>向下滚动到 API 部分>>单击创建新的 API 令牌(记住,如果以前使用过,请使所有其他令牌过期)

点击这个之后,kaggle.json 文件将被安装到您的系统中。
接下来,再次进入数据集页面,在新笔记本选项的右侧,我们可以找到一个图标来复制 API 命令。此命令将用于下载数据集。
现在,在 google colab 中上传 kaggle.json 文件,如下所示。

! pip install -q kaggle
from google.colab import files
files.upload()
#Upload the kaggle.json file here

此外,使用 API 命令下载数据集。

! mkdir ~/.kaggle
! cp kaggle.json ~/.kaggle/
! chmod 600 ~/.kaggle/kaggle.json#Download the full dataset zip file to Colab
! kaggle datasets download -d 'olgabelitskaya/classification-of-handwritten-letters'

现在,解压缩这个文件并创建一个新文件,将所有数据集存储在一个地方。

#Unzip the downloaded zip file and put it under a new files section! unzip -qq sample.zip
import zipfile
zip_ref = zipfile.ZipFile('classification-of-handwritten-letters.zip', 'r')
zip_ref.extractall('files')
zip_ref.close()

完成后,我们导入这些库和适当的后端。

import os
import sys
sys.path.append("monk_v1/");
sys.path.append("monk_v1/monk/");#Importing MXNet Gluon API backend
from monk.gluon_prototype import prototype

我在这里使用了 MXNet Gluon API 作为后端,只用一行代码我们就可以选择我们想要的后端,不用担心以后会遇到不同的语法。使用 monk 库可以做到这一点,只需使用一种语法,我们就可以跨不同的框架工作,如 PyTorch、MXNet、Keras、TensorFlow。

创建项目和实验

为了创建项目名和实验名,我们使用了 prototype 函数。我们现在可以在一个项目下创建多个实验,并在各个方面进行比较。

#Setup Project Name and Experiment Namegtf = prototype(verbose=1);
gtf.Prototype("Pilot", "Densenet_121");

新项目形成!

方法

模型训练的方法:

我们没有连接三个图像文件夹,这将允许我们一起训练整个数据集。
我们构建鲁棒模型的方法是首先通过使用受背景影响最小的图像(即第二图像文件夹)来训练模型。
此外,该文件夹相当大(包含大量图像),因此它可以在开始时有效地训练模型,并且我们可以通过更简单的数据了解模型的表现。

这将在一定程度上确保模型是否学会提取所需的特征,并且我们可以通过分析相同的图来验证这一点。
如果观察到模型已经学会正确地提取特征,那么我们可以用包含图形类型背景图像的下一个文件夹继续训练模型,即使这个文件夹足够大,因此希望它使模型学会忽略背景。

最后,我们用包含剥离背景的图像来更新和训练模型。

这种方法使我们能够找出数据集的哪一部分没有被正确分类,如果结果不令人满意,我们可以分析所有图,确定模型在哪里表现不佳,并专注于该部分。

开发模型的方法:
1。)首先,选择后端(MXNet 胶子),选择一些可能适合这个特定任务的基本模型。
2。)选择不太密集的变体,并比较它们的性能。

#Analysing basic models
analysis_name = "analyse_models";models = [["resnet34_v2", False, True],["densenet121", False, True],["vgg16", False, True],["alexnet",False,True],["mobilenetv2_1.0",False , True]];epochs=5
percent_data=15
analysis = gtf.Analyse_Models(analysis_name, models, percent_data, num_epochs=epochs, state="keep_all");

3.)为模型调整参数。这是开发一个好模型最关键的部分。

#Shuffle the datagtf.update_shuffle_data(True);#For densenets batch size of 2 and 4 did not work well, batch size of 8 worked just fine , after which validation loss starts to increasegtf.update_batch_size(8);#learning rate 0.1 and 0.05 were not working well ,lr lesser than that didn't vary much w.r.t val and training loss .0.01 best choice#Though optimizers like Adam and its variants are very fast at converging but the problem with them are they sometimes get stuck at local optima's.#Whereas famous optimizers linke SGD and SGD plus momentum are slower to converge , but they dont get stuck at local optima easily.#Here after analysing all these optimizers , sgd worked better.#sgd was the best optimizer even for densenetsgtf.optimizer_sgd(0.01);gtf.Reload();

在调整参数时,这种分析非常重要。

4.)使用相同的调整参数,尝试相同模型的更密集的变体。

#Comparing DenseNets for different depth of densenet variants.analysis_name = "analyse_models";
models = [["densenet121", False, True], ["densenet161", False, True], ["densenet169", False, True],["densenet201", False, True]];
epochs=10
percent_data=15
analysis = get.Analyse_Models(analysis_name, models, percent_data, num_epochs=epochs, state="keep_none");

如果我们直接选择密集网络,可能会因为梯度爆炸/递减而表现不佳,最终我们可能根本不会考虑这个选项。

选择最佳模型

在所有这些努力之后,当暴露于看不见的数据时,很容易找出哪个模型能给你最好的结果。在这种情况下,结果是 Densenet,所选择的 dense net 的适当深度是 121,这足以给出期望的结果。
Resnets 架构在经过调优后的整体性能上排名第二。Mobilenets 可能会表现得非常好,尽管这需要花费大量的训练时间和空间来完成这个特殊的任务。

这里的三个图是相同的模型,但是具有更新的数据。Densenet_121_3 在完整数据
上进行训练。该图表明该模型在所有三种数据集上都学习得相当好。
我们从我们的模型中获得了难以超越的验证性能。

最终模型

该模型看起来不像是过度拟合的,并且在对新图像进行分类时表现良好,确保该模型可以运行。
如果我们可以对现有模型进行任何改进的话,那就是集中精力改进/增加带有图形类型背景的图像。如果我们仔细观察,在那些图像被更新和模型被训练之后,模型的性能下降了一点。
这是另一个可以探索的工作领域。

结论

我们已经使用各种架构执行了迁移学习,使用定义明确的方法构建了一个可靠的分类器。尽管这里有一点需要注意,创建项目和实验使我们很容易管理和比较实验/模型。

此外,我们可以使用很少的代码行执行如此多的复杂操作。如果我们采用传统的方法,在模型之间进行这样的比较将会花费我们不希望的代码,这可能很难调试。当我们使用 monk 库时,更新数据集和重新训练模型也是一项基本任务。

还提到在某些领域可以做更多的工作,进一步提高模型的性能和可靠性。

我们还使用一些测试图像进行了推断,请访问下面的代码链接查看它们。

对于整个代码:

[## sanskar 329/俄语字母

这个项目是关于分类俄语字母。总共有 33 类俄语字母。的目标是

github.com](https://github.com/Sanskar329/Russian-Letters)

或者可以在图像分类动物园查看代码。

对于更多此类应用:

[## 镶嵌成像/monk_v1

Monk 是一个低代码深度学习工具,是计算机视觉的统一包装器。— Tessellate-Imaging/monk_v1

github.com](https://github.com/Tessellate-Imaging/monk_v1/tree/master/study_roadmaps/4_image_classification_zoo)

Monk 教程:

[## 镶嵌成像/monk_v1

模块 1:入门路线图第 1.1 部分:Monk 入门第 1.2 部分:Monk 的基本特性第 1.3 部分…

github.com](https://github.com/Tessellate-Imaging/monk_v1/tree/master/study_roadmaps)

如果这篇文章能帮助你学到新的东西,请分享!

感谢阅读!

面向 STT 和 TTS 的俄语文本规范化

原文:https://towardsdatascience.com/russian-text-normalization-for-stt-and-tts-a6d8f03aaeb9?source=collection_archive---------49-----------------------

包括 STT(语音到文本)和 TTS(文本到语音)在内的许多语音相关问题都需要将抄本转换成真实的“口语”形式,即说话者所说的准确单词。这意味着在一些书写的表达式成为我们的抄本之前,它需要被规范化。换句话说,文本必须分几个步骤进行处理,包括:

  • 转换数字:1984 год->тысячадевятьсотвосемьдесятчетвёртыйгод
  • 展开缩写:2 мин. ненависти->двеминутыненависти
  • 拉丁符号的转写(多为英文):Orwell - > Оруэлл
    等等。

在这篇文章中,我想总结一下我们在俄语语音数据集 Open_STT 中的文本规范化方法,简单介绍一下我们使用的思想和工具。

作为锦上添花,我们决定与社区共享我们的 seq2seq 标准化器。它实际上是通过一行程序调用的,只要看看我们的 github 就知道了。

norm = Normalizer() 
result = norm.norm_text('С 9 до 11 котики кушали whiskas')>>> 'С девяти до одиннадцати котики кушали уискас'

关于任务的更多信息

那么,怎么了?理论上,如果很难将缩写扩展为完整的形式,缩写就不会如此常用:每个人都知道“Dr .”代表“医生”。事实上,这并不简单,一些母语人士的直觉是理解所有细微差别的必备属性。

想知道兔子洞有多深,请看下面的例子:

  • - второе(ые)/the 第二但是t5】-двае/两个 e;
  • 2 части - две части/two 部分,-нетвторойчасти/no 第二部分;
  • длиной до 2 км-длинойдодвухкилометиов/长达两公里,-ееемдовто
  • = 2/5-равнодвепятых/等于五分之二,但是д. 2/5-домдвадробьпять/house二建五甚至-двапять/二建五。

缩写和首字母缩略词也是一个问题:同一个缩写可以根据语境(г - городгод )或说话人(БЦ - б цбизнес центр ?).想象一下一个令人头痛的音译是什么:不知何故,它相当于学习另一种语言。上述所有问题对口语形式来说都是特别具有挑战性的。

统计管道

人们很容易迷失在这无尽的变化中:你沉浸在搜索和处理越来越多新案件的无尽循环中。在某些时候,最好停下来回忆一下帕累托原则或 20/80 法则。我们可以处理大约 20%的最流行的案例,覆盖大约 80%的整个语言,而不是解决一般的任务。

第一个 Open_STT 版本的方法更加残酷:如果你看到一个数字,就把它改成一个默认的基数。作为对 STT 应用程序的攻击,这个决定甚至是合理的。通过将2020 год转换为 две тысячи двадцать год ,你只丢失了 1-2 个字母,而忽略一个完整的数字会导致三个单词的错误。

渐渐地,我们添加了某种形式的上下文依赖。现在год这个词前面的数字变成了序数2020最后变成了 две тысячи двадцатый 。因此,我们的“手动”统计管道出现了——我们找到最流行的组合,并将它们添加到规则集中。

序列到序列网络

在某种程度上,序列到序列(seq2seq)架构显然非常适合我们的任务。事实上,seq2seq 与手动管道工作得一样好,甚至更好:

  • 将一个序列转换为另一个序列—选中;
  • 模型上下文依赖关系—检查;
  • 找到最合适的规则来转换序列—检查;

序列“5 января".”的注意力得分图为了生成“пятого”模型的结尾,不仅考虑了单词“5 ”,还考虑了单词“января".”的后续字符

作为基础,我们从这里的开始,在 PyTorch 上实现 seq2seq。要了解更多关于这个架构的信息,请阅读原版指南,它非常棒。更详细地说:

  • 我们的模型是 Char 级的;
  • 源词典包含俄语字母+英语+标点+特殊记号;
  • 目标词典—仅俄语字母+标点符号。

你拥有的多样化和高质量的数据越多,你的模型训练得就越好——这是常识。如果你曾经试图为英语语言找到这样的数据,那么你知道这是非常简单的。此外,甚至还有数字规范化的开源解决方案。至于俄语,一切就更复杂了。

因此,训练数据是以下各项的组合:

  • 开源规范化数据— 俄语文本规范化 —大部分是书籍,部分用规则集规范化;
  • 使用我们的手动管道处理的随机网站的过滤数据;
  • 一些使模型更加健壮的扩展——例如,任意位置的标点和空格、大写、长数字等。

火炬报

除了解决这个问题,我们还想测试一些新的有趣的工具,比如 TorchScript。
TorchScript 是 PyTorch 提供的一个很棒的工具,可以帮助你从 Python 中导出你的模型,甚至可以作为 C++程序独立运行。

简而言之,PyTorch 中有两种使用 TorchScript 的方式:

  1. 硬核,那需要完全沉浸到 TorchScript 语言中,一切后果自负;
  2. 轻柔,使用开箱即用的torch.jit.script(和torch.jit.trace)工具。

事实证明,对于比官方指南中介绍的更复杂的模型,您必须重写几行代码。然而,这不是火箭科学,只需要一些小的改变:

  • 打字要多注意;
  • 想办法重写不支持的函数和方法。

有关更详细的指南,请查看 a 通道柱

例子

综上所述,查看几个归一化结果。以下所有句子均取自测试数据集,即该模型未见过这些示例中的任何一个:

  • norm.norm_string("Вторая по численности группа — фарсиваны — от 27 до 38 %.")

'вторая·численности·фарсиваны·двадцати·тридцативосьмипроцентов.'

  • norm.norm_string("Висенте Каньяс родился 22 октября 1939 года")

'висентеканьясродилсядвадцатьвторогооктябрятысячадевятьсоттридцатьдевятогогода'

  • norm.norm_string("играет песня «The Crying Game»")

песнязэ

  • norm.norm_string("к началу XVIII века")

‘началувосемнадцатоговека’

  • norm.norm_string("и в 2012 году составляла 6,6 шекеля")

тысячидвенадцатом·составлялашестьцелых·шестьдесятыхшекеля'

原载于 2020 年 3 月 4 日https://spark-in . me

Rust-Powered 命令行实用程序可提高您的工作效率

原文:https://towardsdatascience.com/rust-powered-command-line-utilities-to-increase-your-productivity-eea03a4cf83a?source=collection_archive---------9-----------------------

您腰带下的现代快速工具

作者图片

**Table of Contents**[**Introduction**](#74de)1\. [du alternatives: dust and dutree](#d923)
  ∘ [dust](#97b0)
  ∘ [dutree](#9641)
2\. [time alternative: hyperfine](#bb52)
3\. [A fuzzy finder: skim](#e9fc)
4\. [sed alternative: sd](#5960)
5\. [top/htop alternatives: ytop and bottom](#0dd0)
6\. [A Bonus Tip](#69a8)[**Conclusion**](#1fb3)

介绍

上个月,我写了一篇文章分享七个 Rust 驱动的命令行实用程序。

这些都是现代化的快速工具,您可以每天使用您的终端。

自从发表那篇原始文章以来,我一直在寻找更多的 Rust 驱动的命令行实用程序,并且我发现了更多的精华,今天我很高兴与大家分享。

这些工具将帮助您高效完成终端工作。

我推荐安装 Rust。当你安装 Rust 时,它会把 Cargo 作为工具链之一安装。你可以在货物上安装生锈的动力工具。

如果你想开始学 Rust,这篇文章可以帮你入门。

让我们开始吧。

du替代品:dustdutree

dust

在 Linux 中,du命令估计文件空间的使用情况。dustdutree是铁锈动力du的替代品。

对我来说很难使用du命令。例如使用du,列出文件和文件夹,并计算包括我使用的子文件夹在内的总大小:

$ du -sk * | sort -nr

Dust 给你一个目录的即时概览,它的磁盘空间和命令更简单。

例如

$ dust
$ dust <dir>
$ dust <dir>  <another_dir> <and_more>
$ dust -p <dir>  (full-path)

你可以在这里找到所有的用法。

~/的灰尘结果。货物目录。作者图片

在上图中,可以看到 app 目录占用了 57MB(29%),目标目录占用了 139 MB (71%)的磁盘空间。

可以带货安装dust

$ cargo install du-dust
$ dust --help

你可以在这里找到其他装置

dutree

[dutree](https://github.com/nachoparker/dutree)是分析磁盘使用情况的另一个du选择。

可以带货安装dutree

$ cargo install dutree

可以通过dutree --help找到用法。

dutree也有简单的命令选项。例如,您可以使用-d选项显示不同目录深度的目录。

$ dutree -d // the default is 1
$ dutree -d2 // show directories up to depth 2

dutree 结果。作者图片

time备选:hyperfine

在 Linux 中,time命令显示给定命令运行需要多长时间。它对于测试脚本和命令的性能非常有用。

假设您有两个脚本或命令在做同样的工作。您可以使用time命令来比较这些脚本所需的时间。

hyperfine 是一款 Rust 驱动的、time替代命令行基准测试工具。

用货物安装:

$ cargo install hyperfine

如果使用--warmup N运行hyperfine,超精细将在实际测量前执行 N 次预热。

// warmup run
$ hyperfine --warmup 3 'dutree -d' 'dutree -d2' 'dust'
// actual run
$ hyperfine 'dutree -d' 'dutree -d2' 'dust'

超精细结果。作者图片

一个模糊查找器:skim

模糊查找器帮助您快速搜索和打开文件。是grep的互动版。

[skim](https://github.com/lotabout/skim)是铁锈中的模糊查找器。([fzf](https://github.com/junegunn/fzf)是一个用 Go 语言编写的模糊查找器,也是一个很好的选择。)

可以用 Cargo 安装。

$ cargo install skim

其他安装请参见本页。

您可以搜索文件。

$ sk
$ file-name

使用 sk 命令进行文件搜索

或者使用grep在文件中查找一行。

sk --ansi -i -c 'grep -rI --color=always --line-number "{}" .'

使用 sk 命令进行线搜索

sed备选:sd

在 Linux 中,可以使用[sed](https://www.gnu.org/software/sed/manual/sed.html)进行基本的文本替换。

可以用货物安装sd

cargo install sd

sd 使用更简单的语法。

使用标清:

$ sd before after

而使用 sed:

$ sed s/before/after/g

sd替换换行符和逗号:

$ sd '\n' ','

并且用sed:

$ sed ':a;N;$!ba;s/\n/,/g'

sd是不是sed

图像来自标清

top/htop替代品:ytopbottom

htop在 Linux 中是一个交互式的进程查看器。[ytop](https://github.com/cjbassi/ytop)[bottom](https://github.com/ClementTsang/bottom)是图形过程监视器。

您可以将它们与货物一起安装:

$ cargo install ytop
$ cargo install bottom

您可以使用btm运行bottom

底层在行动。作者图片

额外的小费

当您使用另一台计算机、服务器或系统时,您将使用 Linux 命令。即使您正在使用 Rust 提供的替代工具,继续使用 Linux 命令也是一个好主意。

我建议创建别名。

在我的.zshrc中,我有以下内容:

作者在 my .zshrc. Image 中的别名

如果您是 Powershell 用户:

Powershell 中的别名

结论

我希望你开始使用这些生锈的工具。你会发现它们非常快,看起来很现代。如果你知道更多,请告诉我!

通过 成为 的会员,可以完全访问媒体上的每一个故事。

https://blog.codewithshin.com/subscribe

[## 通过将 Python 转换成 Rust 来学习 Rust

Rust 基础入门教程

towardsdatascience.com](/learning-rust-by-converting-python-to-rust-259e735591c6) [## 7 个强大的 Rust 驱动的命令行工具

适合每个开发人员的现代 Linux 命令

towardsdatascience.com](/awesome-rust-powered-command-line-utilities-b5359c38692) [## 你想学 Rust 但是不知道从哪里开始

Rust 初学者的完整资源

towardsdatascience.com](/you-want-to-learn-rust-but-you-dont-know-where-to-start-fc826402d5ba)

生锈的强盗

原文:https://towardsdatascience.com/rusty-bandit-724a9a7a3606?source=collection_archive---------48-----------------------

用铁锈建造多臂强盗

为什么不像其他数据科学家一样使用 Python 呢?Python 适合前端工作,但是所有强大的数据科学算法在后端使用更快的东西。为什么不是 C++?很多人避免使用 C++的原因是一样的;做一些严重不安全的事情太容易了。为什么不是 Java 或者 Scala?JVM 上没有任何东西赢得任何速度竞赛。

但是为什么会生锈呢?因为它几乎和 C++一样快,而且没有很多缺点。Rust 对内存和线程安全近乎偏执。可变性必须显式指定,这鼓励程序员谨慎使用。一个变量在任何时候最多只能有一个可变引用,编译器会积极地检查这一点。线程也以类似的细节进行检查。最终的结果是这种语言很难做傻事(至少对于 IDE 来说),而且几乎所有的错误都在编译时被发现。作为一个额外的好处,编译器的错误消息是非常有用的,这是编程世界中几乎独一无二的特性。

但是 Rust 不是数据科学语言,也不打算是。C++也不是,它正在愉快地支撑 Tensorflow、XGBoost、PyTorch 和 LightGBM 的后端。Rust 可能会有类似的未来,运行 Keras 优雅的语法所隐藏的所有杂乱的矩阵计算。

除此之外,它就在那里。学习生锈是一个挑战,这是一个足够好的理由。

随着预备工作的结束,本文的剩余部分将由三部分组成。第一部分将快速介绍多臂土匪问题。第二篇将描述我是如何设计和编码 Ratel ,一个用于执行多臂强盗实验的 Rust 程序。第三部分将讨论其中一个实验的结果。

(注意程序名。多臂强盗式学习包括探索(做出新的选择)和利用(做出相同的、明智的选择)的结合。我想不出有哪种动物比蜜獾或南非荷兰语 ratel 更擅长探险或开发。

CT Cooper /公共领域

多臂土匪

独臂强盗是老丨虎丨机的一个古老俚语。对于独臂强盗,用户拉动杠杆或手臂,希望获得奖励,同时让机器偷一点自己的钱作为特权。一个多臂强盗有几个可以拉的 am,每一个都按照某种概率分布产生一个奖励。好消息是,在大多数应用程序中,多臂匪徒比拉斯维加斯的胜算更大。坏消息是,每只手臂的奖励分配是秘密的,而且很可能会保持秘密。

多臂 bandit 学习的目标不是找到每个臂的分布,而仅仅是具有最高平均奖励的臂。概括地说,只有一种方法可以做到这一点;拉所有的手臂很多次,直到你有一个合理的准确测量他们的平均值。这不完全是一个算法。该算法决定如何选择臂。我们有几个选择。

贪婪算法(完全利用)

对每个臂进行初始猜测。这通常是可能值的已知范围的高端。随机选择初始臂,并基于输出更新对该臂的猜测。随后的猜测选择迄今为止平均回报率最高的那只手臂,任何平局都是随机打破的。

这是一个不错的策略,但如果最初几次猜测不走运,它确实有选择次优手臂的风险。我将在第三部分量化这种风险;现在举个例子就够了。

假设有两臂,一臂支付 1 25%的时间,另一臂支付 1 75%的时间。其余时间他们支付 0。每只手平均支出的最初猜测都是 1。偶然地,第二只手臂的前四次拉动支付 0,而第一只手臂的前四次拉动支付 3 次 0 和 11。我们对第二只手臂的平均回报率 0.2 的估计,现在低于第一只手臂的实际平均回报率 0.25。遵循一个贪婪的策略,我将继续拉臂 1,几乎没有机会纠正我最初的错误。

这似乎是一个极端的例子;二号臂连续出现四个 0 的几率不到 1%。在现实生活中,bandit arms 的返回之间的分布通常要小得多,并且贪婪算法找到坏的局部最大值的机会相应地更大。

ε-贪婪算法(探索与剥削)

贪婪算法陷入局部最大值的唯一方式是次优 arm 的估计回报高于最优 arm 的估计回报。实际上,这只会在实验的相对早期发生;给定足够的时间,大数定律保证最佳的手臂将有最好的估计。“足够的时间”这个短语在数学中是一个有趣的短语;这可能是一段非常非常长的时间。此外,多种武器的匪徒通常在“生产”阶段接受训练,需要及时达到最佳或接近最佳的性能。我们没有“足够的时间”

进入ε-贪婪算法。它与贪婪算法只有一点不同。在某种概率ε(0.01 和 0.1 是流行的,但它因问题而异)下,ε-贪婪算法将拉出一个随机臂。如果贪婪算法做出了错误的选择,这允许最佳手臂回到游戏中,同时在大多数时间仍然基于当前知识做出最佳猜测。

要想变得更复杂,就要逐渐降低ε的值。

乐观算法

到目前为止,我讨论的两种算法都只使用了 bandit arm 支出的平均值。如果我们想用方差的信息。最佳非参数估计来自霍夫丁不等式,并给出了我们的估计:

理查德·萨顿和安德鲁·巴尔托,《强化学习:导论》,第二版。

在该公式中,Q_t(a)是臂 a 值的当前估计平均值,t 是试验次数,N_t(a)是臂 a 被拉动的次数,c 是用户定义的常数。该算法挑选具有最高置信上限的臂。这具有类似于ε值递减的ε贪婪算法的效果,只是不需要手动选择初始ε或衰减规则。仍然需要选择 c 的值。

Ratel

一个多兵种武装土匪模拟器可以拆分成两部分;土匪本身和球员,或代理人。在 Python、C++或 Java 中,这两个组件都可以编码为类。Rust 的面向对象设置本身没有类,而是依赖于结构,它携带有组织的数据集合,以及特征,它包含可以为结构实现的方法。Structs 非常类似于 C++ structs ,而 traits 类似于 Java 接口。与通常的编码概念一样,这将通过一个例子变得更加清晰。

强盗

我的基本强盗特征如下,包括一个好强盗需要的所有功能。

*/// A trait for common members of the Bandits* pub trait Bandit<T: ToPrimitive> {
    */// The number of arms of the Bandit* fn arms(&self) -> usize;

    */// The arm with the highest average reward.* fn best_arm(&self) -> usize;

    */// The maximum average reward of all the arms.* fn max_reward(&self) -> f64 {
        self.mean(self.best_arm())
    }

    */// The average reward of a given arm.* fn mean(&self, arm: usize) -> f64;

    */// The average rewards of all the arms.* fn means(&self) -> Vec<f64> {
        (0..self.arms()).map(|arm| self.mean(arm)).collect()
    }

    */// The reward from a pull of a given arm.* fn reward(&self, arm: usize) -> T;

    */// The standard deviation of a given arm.* fn std(&self, arm: usize) -> f64;

    */// the standard deviations of all the arms.* fn stds(&self) -> Vec<f64> {
        (0..self.arms()).map(|arm| self.std(arm)).collect()
    }
}

可以定义的方法有。那些需要来自特定结构类型的数据的将在实现中定义。一个土匪需要的数据,简单来说就是每个手臂的概率分布。下面是每个臂都具有二项分布的情况。

use rand_distr::Binomial;*/// A bandit whose arms distribute rewards according to binomial distributions.* pub struct BinomialBandit<*'a*> {
    */// Vector of number of trials of a* `yes-no` *experiment.* nums: &*'a* Vec<u32>,

    */// Vector of experiment success probabilities.* probs: &*'a* Vec<f64>,

    */// The bandit arm with highest reward.* best_arm: usize,

    */// Distributions of the arms.* distributions: Vec<Binomial>,
}

该结构需要是公共的,因为它将在其当前模块之外被引用。Rust 中的对象有多个隐私级别;使用最严格的限制几乎总是一个好主意。

这段代码中可能让新手感到困惑的其他部分是&符号&'a的使用。前者表示值是引用,类似于指针。在这个结构中,numsprob不是向量本身,而仅仅是向量的内存地址。奇怪的小'a符号控制着这些引用的寿命,并确保它们不会比它们所指向的向量活得更长。Rust 不允许悬空指针。

二项式分布由试验次数和每次试验的成功概率定义。存储定义参数和分布向量似乎是多余的,事实也的确如此。按照目前的设计,这些发行版不能返回它们的参数,所以值得额外的空间来单独保存它们。

定义自定义构造函数通常会有所帮助。为此,请实现结构。

impl<*'a*> BinomialBandit<*'a*> {
    */// Initializes a new Bandit where each arm distributes rewards according to a binomial
    /// distribution.* pub fn *new*(nums: &*'a* Vec<u32>, probs: &*'a* Vec<f64>) -> BinomialBandit<*'a*> {
        assert_eq!(nums.len(), probs.len());
        assert!(probs.val_max() <= 1.0);
        assert!(probs.val_min() >= 0.0);
        assert!(nums.val_min() > 0);
        let dist = nums
            .iter()
            .zip(probs)
            .map(|(&n, &p)| Binomial::*new*(u64::*from*(n), p).unwrap())
            .collect();
        let best_arm = nums
            .iter()
            .zip(probs)
            .map(|(&n, &p)| f64::*from*(n) * p)
            .collect::<Vec<f64>>()
            .arg_max();
        BinomialBandit {
            nums,
            probs,
            best_arm,
            distributions: dist,
        }
    }
}

这大部分是不言自明的。一些基本的断言语句确保我不会加载垃圾。然后,这两个向量被压缩并迭代,以创建分布向量。可能需要注释的一位代码是u64::from(n)。现代编程语言,至少是编译过的各种语言,似乎正在脱离自动类型转换的思想,Rust 也不例外。用一个整型数除以一个浮点型数是行不通的。这可能对类型安全有好处,但是也有强制使用难看的显式类型转换语句的缺点。

在这一点上,我只是有一个土匪坐在周围不能做太多。为了解决这个问题,我需要为 BinomialBandit 结构实现 Bandit 特征。这包括在 Bandit 中定义依赖于结构数据的五个方法。

impl<*'a*> Bandit<u32> for BinomialBandit<*'a*> {
    *///Returns the number of arms on the bandit.* fn arms(&self) -> usize {
        self.nums.len()
    }

    *///Returns the arm with highest average reward.* fn best_arm(&self) -> usize {
        self.best_arm
    }

    */// Computes the expected return of each arm.* fn mean(&self, arm: usize) -> f64 {
        f64::*from*(self.nums[arm]) * self.probs[arm]
    }

    */// Determines the reward for pulling a given arm.* fn reward(&self, arm: usize) -> u32 {
        self.distributions[arm].sample(&mut thread_rng()) as u32
    }

    */// Computes the standard deviations of each arm.* fn std(&self, arm: usize) -> f64 {
        (f64::*from*(self.nums[arm]) * self.probs[arm] * (1.0 - self.probs[arm])).sqrt()
    }
}

最初的 Bandit 特性将输出类型留给了单独的实现。在这里,我指定 BinomialBandit 的业务是给出无符号整数奖励。如果你看一下高斯-班迪特,你会看到它在漂浮。

代理人

正如有多种类型的强盗一样,也有多种类型的代理。我为三种算法中的每一种设计了一个代理结构,用于寻找最优策略和一个包含它们都需要的功能的通用代理特征。和强盗一样,我将首先描述代理特征。

*/// A trait for common members of the Agents.* pub trait Agent<T: ToPrimitive> {
    */// The action chosen by the Agent.* fn action(&self) -> usize;

    */// The number of arms in the Bandit the Agent is playing.* fn arms(&self) -> usize {
        self.q_star().len()
    }

    */// The Agent's current estimate of the value of a Bandit's arm.* fn current_estimate(&self, arm: usize) -> f64 {
        self.q_star()[arm]
    }

    */// The Agent's current estimate of all the Bandit's arms.* fn q_star(&self) -> &Vec<f64>;

    */// Reset the Agent's history and give it a new initial guess of the Bandit's arm values.* fn reset(&mut self, q_init: Vec<f64>);

    */// Update the Agent's estimate of a Bandit arm based on a given reward.* fn step(&mut self, arm: usize, reward: T);

    */// Calculate the update of the Agent's guess of a Bandit arm based on a given reward.* fn update(&mut self, arm: usize, reward: T) -> f64 {
        self.stepper().step(arm) * (reward.to_f64().unwrap() - self.q_star()[arm])
    }

    */// Returns a reference to the Agent's step size update rule.* fn stepper(&mut self) -> &mut dyn Stepper;
}

与 bandit 特性一样,大部分功能已经被填充。唯一需要讨论的方法是update。所有的代理都通过一个类似于随机梯度下降的规则来更新他们的估计。取当前奖励和估计的平均奖励之间的差,并通过该值的某个倍数(类似于学习率)来调整估计值。究竟是什么规则决定了“学习率”取决于用户;一个好的选择是调和递减率,它使估计的平均值成为样本平均值。可用的步进器类别在utilsstepper模块中。

在 trait 的开头,有必要明确声明代理使用的任何类型T都必须可转换为浮点型。

看一看 EpsilonGreedy 结构将有助于向我们展示如何使用代理特征。

*/// Agent that follows the Epsilon-Greedy Algorithm.
///
/// A fixed (usually small) percentage of the
/// time it picks a random arm; the rest of the time it picks the arm with the highest expected
/// reward.* pub struct EpsilonGreedyAgent<*'a*, T> {
    */// The current estimates of the Bandit arm values.* q_star: Vec<f64>,

    */// The Agent's rule for step size updates.* stepper: &*'a* mut dyn Stepper,

    */// The fraction of times a random arm is chosen.* epsilon: f64,

    */// A random uniform distribution to determine if a random action should be chosen.* uniform: Uniform<f64>,

    */// A random uniform distribution to chose a random arm.* pick_arm: Uniform<usize>,
    phantom: PhantomData<T>,
}

该结构最重要的组件是q_star,它包含了每条臂平均值的估计值。其余的大部分都有助于更新。我已经讨论过stepperepsilon仅仅是选择随机臂的频率,两个均匀分布决定何时以及如何选择随机臂。最后一个参数phantom是一个必需的引用,以确保该结构被视为使用了T类型的东西,否则该结构只由实现特征使用。

我们如何实现 EpsilonGreedyAgent 的代理?见下文。

impl<*'a*, T: ToPrimitive> Agent<T> for EpsilonGreedyAgent<*'a*, T> {
    */// The action chosen by the Agent. A random action with probability* `epsilon` *and the greedy
    /// action otherwise.* fn action(&self) -> usize {
        if self.uniform.sample(&mut thread_rng()) < self.epsilon {
            self.pick_arm.sample(&mut thread_rng())
        } else {
            self.q_star.arg_max()
        }
    }

    */// The Agent's current estimate of all the Bandit's arms.* fn q_star(&self) -> &Vec<f64> {
        &self.q_star
    }

    */// Reset the Agent's history and give it a new initial guess of the Bandit's arm values.* fn reset(&mut self, q_init: Vec<f64>) {
        self.q_star = q_init;
        self.stepper.reset()
    }

    */// Update the Agent's estimate of a Bandit arm based on a given reward.* fn step(&mut self, arm: usize, reward: T) {
        self.q_star[arm] += self.update(arm, reward)
    }

    */// Returns a reference to the Agent's step size update rule.* fn stepper(&mut self) -> &mut dyn Stepper {
        self.stepper
    }
}

游戏

为了进行实验,代理人和强盗必须相互作用。交互由一个Game结构管理,该结构包含一个Agent、一个Bandit,以及使它们交互的计数器和方法。

*///Structure to make the Agent interact with the Bandit.* pub struct Game<*'a*, T: AddAssign + Num + ToPrimitive> {
    */// Agent learning about bandit.* agent: &*'a* mut dyn Agent<T>,
    *///Bandit used by agent.* bandit: &*'a* dyn Bandit<T>,
    */// Records wins and losses from each arm pull. Win means pulling the best arm.* wins: RecordCounter<u32>,
    */// Records rewards from each arm pull.* rewards: RecordCounter<T>,
}

基本结构不言自明。除了AgentBandit之外,它还包含计数器来记录wins(挑选最佳手臂)和rewards(所有手臂拉动的结果)。)

实现更有趣一点。

impl<*'a*, T: AddAssign + Copy + Num + ToPrimitive> Game<*'a*, T> {
    */// Initializes a Game with an Agent, Bandit, and new counters.* pub fn *new*(agent: &*'a* mut dyn Agent<T>, bandit: &*'a* dyn Bandit<T>) -> Game<*'a*, T> {
        assert_eq!(agent.arms(), bandit.arms());
        Game {
            agent,
            bandit,
            wins: RecordCounter::*new*(),
            rewards: RecordCounter::*new*(),
        }
    }

    */// Returns the number of bandit arms.* pub fn arms(&self) -> usize {
        self.bandit.arms()
    }

    */// Agent chooses an arm to pull and updates based on reward.* fn pull_arm(&mut self) {
        let current_action = self.agent.action();
        self.wins
            .update((current_action == self.bandit.best_arm()) as u32);
        let reward = self.bandit.reward(current_action);
        self.rewards.update(reward);
        self.agent.step(current_action, reward);
    }

    */// Resets Game. Resets Agent with new initial guess and resets counters.* pub fn reset(&mut self, q_init: Vec<f64>) {
        self.agent.reset(q_init);
        self.rewards.reset();
        self.wins.reset();
    }

    */// Returns vector of rewards.* pub fn rewards(&self) -> &Vec<T> {
        self.rewards.record()
    }

    */// Run game for a certain number of steps.* pub fn run(&mut self, steps: u32) {
        for _ in 1..=steps {
            self.pull_arm()
        }
    }

    */// Returns vector of wins.* pub fn wins(&self) -> &Vec<u32> {
        self.wins.record()
    }
}

通常,大多数类方法都是简单的 setters 和 getters。最有趣的两个方法是pull_armrun。第一个选择一个动作,检查它是否是最好的,然后返回奖励和它是否是最好的。第二个通过拉手臂进行完整的实验,拉的次数是实验需要的次数。

这些都是进行多臂强盗实验所需要的基础。Ratel 代码的其余部分要么是将实验放在一起的胶水,要么是产生输出的代码。任何人在自己的工作中使用这个模块都必须编写自己的版本,所以我不会详细讨论我的代码。在下一部分,我将讨论我的一个实验。

两两伯努利盗匪

当考虑任何复杂的系统时,从简单开始是值得的。最简单的多臂强盗只有两臂,最简单的概率分布可以说是伯努利分布。一个以某个概率 p 返回,零以概率 1-p 返回。给定两条臂,一条左臂和一条右臂,具有不同概率的伯努利输出(否则它将是微不足道的),使用 a)贪婪算法,b)ε-贪婪算法找到最佳臂的可能性有多大?(乐观算法更适合连续分布;这里就不考虑了。)随着概率的变化,这种可能性如何变化?

我以 0.01 为增量,从 0.01 到 0.99 取概率的全范围。用概率 p 测试臂 A,用概率 q 测试臂 B,然后反过来用概率 q 测试臂 A,用概率 B 测试臂 B,这是没有好处的,所以我可以通过总是给左臂更小的概率来将情况的数量减半。我还有 4851 种组合可以尝试。同时,我还将尝试不同的初始值,从 0.11.0,增量为 0.1。现在有 48510 种组合。

这就是在 Rust 工作真正开始有回报的地方。如果我一直从事 Python 的工作,这将是我一个月以来最后一次给你写信。Rust 的速度大约快 100 倍,这意味着我甚至可以在一夜之间运行大型实验。

贪婪算法收敛非常快;200 次迭代足以获得所有组合的精确结果。这让我可以将每个实验运行 100000 次。对于六核 i7 处理器(总共 12 个线程),贪婪算法实验运行大约 3.5 小时。

选择初始猜测值为 0.1 的最佳臂的概率。斧头是每只手臂奖励 1 的几率。

实验中最低可能的初始猜测是双臂 0.1。如果我以此开始,那么当只有一个臂具有高概率时,贪婪算法选择正确臂的概率是高的,在这种情况下,问题是非常容易的,或者两个臂都具有低概率,在这种情况下,它们的相对差异仍然是显著的。当左臂达到 0.7 的概率时,选择最佳手臂的机会几乎等于零。

随着最初猜测的增加,情况会有所改善。对于臂概率的所有选择,每次初始猜测增加 0.1,挑选最佳臂的可能性增加,或者至少不减少。当最初的猜测达到 0.7 时,低概率成功的区域是一个小长条,其中两臂的概率都很高,并且它们的概率差很低(三角形对角线的较低部分)

为每个初始猜测值选择最佳臂的概率。随着最初猜测的增加,选择最佳手臂的可能性也会增加。

寓意是,对于贪婪算法,初始猜测是重要的,应该尽可能地高。在这种情况下,这很容易,因为已知分布是伯努利分布。随着分布信息的减少,一个好的初步猜测将会变得更加困难。

另一个教训是,即使有很好的猜测,也有贪婪算法不那么好的情况。在这个玩具的例子中,很容易限定,如果不是完全量化,坏的区域,但随着更多的武器或更复杂的分布,奢侈品将不再可用。

一个显而易见的问题是,是否有可能通过更长的运行获得更高的准确性;200 次迭代不算多。事实证明这已经足够了。绘制实验的子集,其中两臂的概率仅相差 0.01(取左臂为 0.1、0.2、0.3 等),很明显,在第 100 次迭代之前达到了收敛。这是贪婪算法得到的最好结果。

ε贪婪

有没有更好的策略?是的,尽管这是有代价的。除了最极端的情况之外,选择一个概率较低的随机臂几乎可以得到最佳结果。然而,ε-贪婪算法有两个缺点。首先,成功的概率有一个硬上限,小于 1.0。如果以概率ε选择随机臂,则可以以最多(1-ε)+ε/(臂数)的概率选择最佳臂。随着臂的数量变大,这接近 1-ε,这是保持ε小的动机。

第二个问题是ε-greedy 需要更长的时间来收敛,ε值越小,收敛时间越长。

ε= 0.1

我从ε= 0.1 开始。对于两个手臂,选择最佳手臂的概率应该最大为大约 0.95。和贪婪算法一样,我尝试了 10 个不同的初始值,范围从 0.11.0。每次运行由 4000 次迭代组成,我运行了 10000 次(是贪婪算法的十分之一)。实验进行了大约 7.5 个小时。

为每个初始猜测值选择最佳臂的概率。选择最佳臂的可能性很大程度上与最初的猜测无关。

首先要注意的是,对最初猜测的依赖几乎已经消除。仅这一点就成为对 epsilon-greedy 有利的重要论据。第二,除了对角线上最窄的细长条,所有的臂组合都有接近其最大值的精度。第三,正如预期的那样,最大精度没有贪婪算法那么高。即使有一个警告,这仍然是一个比以前的结果有所改善。

在代理商仍然难以选择正确手臂的地区,有更多的好消息。学习还没有饱和。进一步的迭代可能会产生更好的结果。绘制与上面相同的实验子集表明胜率还没有稳定下来。再重复 4000 次,我会得到更好的结果。

长期胜率。

ε= 0.01

如果我尝试更低的ε值会发生什么?当ε的值等于 0.01 时,我得到的结果具有贪婪算法和ε-贪婪算法的大部分优点,并且ε更大。除了ε的变化之外,该实验的所有参数与上述相同。

再次,有最大可能的精度,但现在是 0.995。同样,结果在很大程度上独立于最初的猜测。再一次,除了两臂的概率相似的相当窄(尽管稍微宽)的子集之外,对于所有的子集都达到了这种精度。

那么窄的范围呢?有没有可能更多的迭代会带来更好的结果。是的,但是它将需要更多。精度仍在提高,但速度比ε= 0.1 时慢得多。

长期胜率

结论

Ratel 可以做许多改进。目前,只有两种概率分布支持土匪,高斯和二项式,但增加其他将是微不足道的。类似地,还可以添加更复杂的代理算法。一个稍微小一点的改进是允许来自多个发行版家族的 bandit arms,尽管我所知道的大多数应用程序并不要求这样。

在实现的代理中,似乎最成功的整体是ε贪婪的,ε值较低。贪婪算法在臂可以被容易地区分的情况下工作得最好。ε-greedy 在除了最难区分的情况之外的所有情况下都是成功的。

也许最重要的结论是,Rust 作为建模和仿真语言表现良好。

SaaS 收入倍数、利率和 R 中的建模

原文:https://towardsdatascience.com/saas-revenue-multiples-and-modeling-in-r-29ce10905488?source=collection_archive---------35-----------------------

证明利率不能完全解释 SaaS 估值的 6 个简单步骤

莎伦·麦卡琴在 Unsplash 上的照片

软件公司似乎每年都变得更有价值,在股票市场中也变得更加重要。就在本周,典型的软件即服务 (SaaS)公司 sales force加入道琼斯工业平均指数。它现在是指数的第三大组成部分。自 Covid 危机以来,这些公司的价值增长甚至加速了。

看看 Salesforce 的数据,很明显,其不断增长的估值很大一部分来自于不断增加的收入。Salesforce 和其他 SAAS 公司只是不断赚更多的钱。

数据位于https://github . com/taubergm/SaaS _ interest _ rates/blob/master/sales force . CSV

然而,这不是唯一的因素。看公司的价格销售比,或者股票“倍数”。在过去的 5 年里,它徘徊在 8 左右,今年飙升至近 12。这种变化让我们许多人想知道,SAAS 的估值是否超前了?

要回答这个问题,才华横溢的风险投资家查马斯·帕里哈皮蒂亚(Chamath Palihapitiya)有一个答案——低利率。

这让我想知道,仅仅使用利率我们能在多大程度上模拟科技公司的估值?如果我们将所有 SAaS 公司放在一起平均,我们能忽略收入增长、利润、客户获取、终身价值、留存以及其他构成经典 SaaS 模型的指标吗?

我试图用 r 中的一些基本建模技术来回答这个问题。该项目的代码和数据都可以在这里找到。这个练习在简单模型的陷阱和我们对软件公司估值的假设方面提供了一些很好的教训。

1 —收集数据

首先,我收集了一份美国所有最大的 SaaS 上市公司的名单。Mike Sonders 有一个很棒的列表,我不得不补充一些他漏掉的。接下来,我收集了这些公司的历史价格销售比数据(又名“倍数”)。这方面的最佳来源是宏观趋势,它有大量的金融信息。

最后,我记录了同期的几个利率指标。我第一次从圣路易斯联邦储备银行网站获得有效联邦基金利率。然后,我决定用 10 年期和 30 年期美国国债的收益率来衡量可能更好,因为这将反映对未来利率的预期。使用 marketwatch 很容易收集这些信息。数据收集的最终结果是一个大矩阵。

数据位于https://github . com/taubergm/SaaS _ interest _ rates/blob/master/SaaS _ mt2 . CSV

每一列代表一个收益季度,每一个值代表一个价格销售比。底部是利率,以百分比来衡量。

1 —数据探索和目测

首先,我想看看利率和 SaaS 倍数之间是否有任何明显的视觉关系。由于数据已经在 Excel 中,很容易绘制出平均 SaaS 公司 P/S 比率(蓝色)与 10 年期收益率、30 年期收益率和有效联邦基金利率的关系。

数据在https://github . com/taubergm/SaaS _ interest _ rates/blob/master/SaaS _ averages . CSV

看起来确实有很好的相关性,但是不同的尺度很难区分。使用标准化数据的相同曲线图如下。

现在这种关系已经不那么明显了。快速检查一下,SaaS 倍数似乎与 30 年期国债收益率呈显著负相关,与 10 年期国债的负相关程度较低。倍数似乎也与联邦基金利率有一定关系,但不是很明显。在 R 中确认这一点很容易。

saas_averages = read.csv(‘saas_averages.csv’)cor(saas_averages)

最令人惊讶的是,倍数膨胀与时间的简单推进有多么密切的关系。将 SaaS 倍数与一个基本递增序列进行比较,我得到的相关性为 0.86!我生成了简单的图表,将这些变量相互比较,看看我是否遗漏了什么。

plot(time, multiples)
plot(ten_yr, multiples)
plot(thirty_yr, multiples)
plot(fed_rate, multiples)

2 —归一化其他输入

直接比较平均值的一个问题是它们隐藏了一些信息。在这种情况下,较年轻的 SaaS 公司往往会以非常高的市盈率进行交易,这可能是由于炒作和较高的收入增长的综合作用。这些公司偏向于最近几个季度的平均倍数。

为了避免这种偏差,我对每个公司的 P/S 数据进行了标准化,以便对所有行进行同等加权。

normalize <- function(x) {
 return ((x — min(x, na.rm = TRUE)) / (max(x,na.rm = TRUE) — min(x,na.rm = TRUE)))
}saas_multiples_normalized = t(apply(saas_multiples_data, 1, normalize))# average quarters and reverse order to increasing in time
saas_multiples_normalized_average = rev(colMeans(saas_multiples_normalized, na.rm = TRUE))

现在,有了干净的数据和一些关于我在寻找什么的直觉,是时候建立一些基本的模型了。

3 —构建模型

R 语言通过“lm”函数使构建线性模型变得轻而易举。使用“lm ”,很容易构建简单的公式,以查看他们如何密切跟踪真实的 SaaS 数据。我决定构建各种利率指标之间的各种多项式关系,看看哪一个表现最好。下面是一些模型。

model_time = lm(multiples_normalized ~ time)model_fed_cubic = lm(multiples_normalized ~ poly(fed_rate,3))model_tenyr_cubic = lm(multiples_normalized ~ poly(ten_yr,3))model_thirtyyr_cubic = lm(multiples_normalized ~ poly(thirty_yr,3))model_tenyr_thirtyyr_fed = lm(multiples_normalized ~ thirty_yr + ten_yr + fed_rate)model_time_tenyr_thirtyyr_fed = lm(multiples_normalized ~ thirty_yr + ten_yr + time + fed_rate)model_thirtyyr_squared_fed_squared_tenyr_squared = lm(multiples_normalized ~ poly(thirty_yr,2) + poly(fed_rate,2) + poly(ten_yr,2))model_time_thirtyyr_squared_fed_squared_tenyr_squared = lm(multiples_normalized ~ time + poly(thirty_yr,2) + poly(fed_rate,2) + poly(ten_yr,2))

这些模型中的每一个都代表了时间、30 年期收益率、10 年期收益率、联邦基金利率和标准化 SaaS 倍数的某种多项式组合

4 —测试模型

我构建的许多模型都是不必要的复杂。例如,没有明显的理由为什么联邦基金利率、30 年期收益率和 10 年期收益率的多项式之和应该表现良好。不过,这是我观察到的。也许这是一个过度拟合的经典案例,或者也许在短期和长期利率预期之间存在某种更深层次的关系。

使用 R“预测”函数可以很容易地计算出这些模型的质量。然后使用“rmse”函数计算预测值和实际 SaaS 平均值之间的均方根误差。

predicted_model_time = predict(model_time)
predicted_model_fed_cubic = predict(model_fed_cubic)
predicted_model_tenyr_cubic = predict(model_tenyr_cubic)
predicted_model_thirtyyr_cubic = predict(model_thirtyyr_cubic)
predicted_model_tenyr_thirtyyr_fed = predict(model_tenyr_thirtyyr_fed)predicted_model_time_tenyr_thirtyyr_fed = predict(model_time_tenyr_thirtyyr_fed)predicted_model_thirtyyr_squared_fed_squared_tenyr_squared = predict(model_thirtyyr_squared_fed_squared_tenyr_squared)predicted_model_model_time_thirtyyr_squared_fed_squared_tenyr_squared = predict(model_time_thirtyyr_squared_fed_squared_tenyr_squared)error_model_time = rmse(multiples_normalized, predicted_model_time)error_model_fed_cubic = rmse(multiples_normalized, predicted_model_fed_cubic)error_model_tenyr_cubic = rmse(multiples_normalized, predicted_model_tenyr_cubic)error_model_thirtyyr_cubic = rmse(multiples_normalized, predicted_model_thirtyyr_cubic)error_model_tenyr_thirtyyr_fed = rmse(multiples_normalized, predicted_model_tenyr_thirtyyr_fed)error_model_tenyr_thirtyyr_fed = rmse(multiples_normalized, predicted_model_tenyr_thirtyyr_fed)error_model_time_tenyr_thirtyyr_fed = rmse(multiples_normalized, predicted_model_time_tenyr_thirtyyr_fed)error_model_thirtyyr_squared_fed_squared_tenyr_squared = rmse(multiples_normalized, predicted_model_thirtyyr_squared_fed_squared_tenyr_squared)error_model_time_thirtyyr_squared_fed_squared_tenyr_squared = rmse(multiples_normalized, predicted_model_model_time_thirtyyr_squared_fed_squared_tenyr_squared)

复杂模型表现最好。同样,这可能是由于过度拟合。真正的考验是使用这些模型来预测未来的数据。

5 —目视验证

我从上一节中选择了一些表现较好的模型,并查看了它们的公式

*模型 _ 时间= 0.01568 时间+ 0.22692

T5【模型 _ fed _ 立方= 0.2984 * fed+0.2884 * fed—0.2989 * fed+0.4073

model _ thirtyyr _ squared _ fed _ squared _ tenyr _ squared

***=-1.80371 *三十 _ 年+0.22576 *三十 _ 年+0.02209 *年+0.29625 年+1.72330 十 _ 年—0.23045 十 _ 年+ 0.40729

记住这些公式后,我将它们与实际的 SaaS 数据进行对比,看看它们看起来如何。归一化的 Saas 倍数是红点,模型预测是蓝点。

代码位于https://github . com/taubergm/SaaS _ interest _ rates/blob/master/SaaS _ mt . R

6 —结论和最终想法

我认为公平地说,科技股和利率之间的关系并不完全清楚。虽然以较低的速率扩展 SaaS 倍数有直观的意义,但我们需要一些非常复杂的多项式模型来很好地拟合数据。我曾听金融大师说过类似“降息 0.5 个百分点会导致 2 个百分点的倍数扩张”之类的话。虽然这在某些情况下是正确的,但它肯定不是一条绝对的定律。

现在,有许多可能的原因导致我没能在利率和倍数之间找到一个好的、清晰的关系。也许没有足够多的公司来进行汇总,所以企业的个体差异使事情扭曲得太多了。也许这种关系并不是线性的。在联邦基金利率接近于零的情况下,市盈率非常高,但在经济繁荣、利率接近 2%时,市盈率也很高。也许我的分析遗漏了更大的宏观层面的变量,包括投资者情绪。事实上,从这些粗略数据来看,SaaS 多次波的最佳单一预测因子是时间。牛市的每一个季度都会扩大倍数,超出利率本身的预测。技术人员都希望有一些数量上的原因,让他们变得如此富有如此之快。也许就像我们的动物精神一样,真正的原因是无法量化的。

为疲软的经济牺牲生命

原文:https://towardsdatascience.com/sacrificing-lives-for-a-weak-economy-e2aae2115ee1?source=collection_archive---------51-----------------------

美国如何在科维德战争中犯下大错

在美国,新冠肺炎疫情战争已经进行了七个月,距离总统大选还有两周。我们现在有大量数据来评估和评价各国在抗击病毒方面的表现。美国对疫情采取了随意的应对措施,没有明确的国家计划,州和地方领导人采取了不一致的策略。这种领导力的缺乏导致了疲软的经济和令人难以置信的高死亡率。

下图显示了疫情到目前为止的时间表。欧洲首次出现重大疫情,死亡率高峰出现在 4 月 10 日,美国的高峰出现在两周后的 4 月 24 日,加拿大的高峰出现在两周后的 5 月 7 日。拉丁美洲在夏季晚些时候经历了高峰期。在可怕的初始爆发后,疫情在欧洲和加拿大基本平息。然而,美国制定了不同的路线。

美国许多州没有阻止病毒的传播,而是选择保持经济开放。政治领导人淡化了病毒的风险,鼓励人们不受干扰地生活。结果是,美国的死亡率远高于其他富裕国家,其曲线类似于较贫困的拉丁美洲地区。美国的人均国内生产总值甚至比最富裕的拉丁美洲国家还要高四倍——我们本应该拥有抗击这种病毒的资源。

COVID 怀疑论者认为减缓病毒传播的努力从根本上被误导了。他们说这种病毒是就像流感(它不是),即使它是致命的,经济比拯救生命更重要。选择对抗病毒的州长的批评者喜欢指出瑞典是一个在对抗病毒方面无所作为的国家的光辉榜样,然而人们似乎仍然乐在其中。数字显示宽松的方法并没有奏效。

下图显示了新冠肺炎的死亡率,以及 COVID 对 30 个最富裕国家的经济影响。经济影响是 2020 年的预计经济增长与没有 COVID 的情况下可能发生的情况之间的差异。国际货币基金组织预计,美国经济遭受的损失将比中位数国家少 22%,但我们的死亡率比中位数高 457%。这种死亡率/增长权衡至少是不利的,特别是因为这些国家中有四分之一既有较低的死亡率,又有较好的经济结果。如果死亡率达到中值,美国本可以挽救超过 170,000 人的生命,这需要更早的行动和更好的协调

最好的方法是积极的早期反应。考虑三个成功国家的行动:南韩日本德国。每个国家在早期都暴露于源于中国和意大利爆发的一连串病例。坚持戴口罩、社交距离/活动限制、大量检测以及隔离和接触者追踪的综合措施使得这些国家能够阻止病毒的传播。结果显示,韩国以第二低的死亡率和第二好的经济结果高居榜首。尽管人口老龄化,但日本的死亡率排名第三,财政状况排名第五。德国的死亡率和增长率高于中值,表现远好于其西方邻国。

另一方面,对数据的合理解读表明,瑞典的宽松政策并不是一个值得效仿的好榜样。下表显示,瑞典及其北欧邻国拥有相似的经济和人口统计数据。区别在于瑞典对 COVID 的反应最小,最大严格指数比其邻国低 35%。结果是,瑞典的死亡率是邻国挪威、芬兰和丹麦的七倍,而牺牲却没有带来经济效益。

瑞典的邻国在财富、典型的经济增长、贸易开放度以及城市中心人口比例方面都不相上下。最大严格程度反映了政府对 COVID 采取卫生相关和经济措施的程度。瑞典奉行宽松的政策,这是最低的严格性。

毫不奇怪,新冠肺炎疫情成为一个热门的政治问题,因为努力减缓疾病将有经济成本。理性的人可能不同意这种平衡行为,但公共话语很快变成了兜售阴谋论和 T2 坏科学的怀疑论者。像德国这样的国家向我们展示了本来可以怎样,用科学和理性的辩论推动更好的结果。今年 11 月,美国需要让其领导人保持同样高的期望。

本文使用的代码和数据可以在 这里找到

编者注: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

SAEMI:电子显微镜图像的尺寸分析

原文:https://towardsdatascience.com/saemi-size-analysis-of-electron-microscopy-images-1-4bb9cd47ad3c?source=collection_archive---------33-----------------------

来源: CNR-IOM ( 抄送)

1 部分,开发电子显微镜图像定量分析工具

本文是我的系列文章的第 1 部分,详细介绍了 SAEMI 的使用和开发,这是我创建的一个 web 应用程序,用于执行电子显微镜图像的高通量定量分析。你可以点击这里查看应用,点击这里查看其 github 。此外,查看第 2 部分这里(在这里我给出了如何使用该应用程序的步骤)和第 3 部分这里(在这里我回顾了我的图像分割模型的训练过程)。在本文中,我将讨论开发 SAEMI 背后的动机、它在科学研究中的用途以及如何使用该应用程序的快速概述。

电子显微镜

20 世纪初,法国物理学家路易·德布罗意(Louis de Broglie)提出了一个革命性的命题:电子很像光,既可以充当粒子,也可以充当波。这种波粒二象性的想法现在是当代量子力学的基础,并导致了许多现代技术的发展,如个人电脑和智能手机,磁共振成像和 x 光等医疗设备,以及光纤互联网。

然而,从德布罗意强有力的陈述中得出的一项鲜为人知的技术是电子显微镜。它的相对默默无闻有很多原因,尽管这可能是因为它们过于昂贵,并且只在实验室环境中使用。尽管如此,它们在实验室环境中的价值是不可低估的。

与光学显微镜类似,电子显微镜允许研究人员观察世界上肉眼看不到的微小部分。然而,由于电子的波长比光短得多,研究人员可以使用电子显微镜来解析比传统光学显微镜更小数量级的特征。光学显微镜可以放大 20,000 倍,而电子显微镜可以放大 100 万倍以上。

在图 1 中,我们可以看到与传统光学显微镜相比,电子显微镜可以实现不同的长度范围。虽然光学显微镜可分辨的特征确实令人印象深刻(导致我们对细菌、疾病和细胞生物学的现代理解),但电子显微镜可获得的特征将我们对世界的理解提高到了一个全新的水平。

图一。光学和电子显微镜长度标尺。作者制作的图像。

在化学和材料研究中,电子显微镜(EM)已被证明对于理解新型纳米材料的组成和结构至关重要,其用途从氢燃料电池发电到微芯片制造。在生物医学领域,EM 也允许研究人员探测光学显微镜无法观察到的生物物体的特征,如蛋白质、DNA 结构或树突细胞。随着科学研究越来越关注长度尺度越来越小的物质世界,EM 作为纳米尺度物体的表征工具变得越来越有价值。

那么问题出在哪里?

尽管 EM 拥有令人惊叹的能力,但这并不意味着它没有问题。虽然 EM 可以在前所未有的长度范围内快速提供颗粒的定性分析,但对 EM 图像进行定量分析可能会变得繁琐而耗时。例如,考虑下图:

图 2 一组纳米颗粒的电子显微镜图像。来源: CNR-IOM ( 抄送)

要定量确定像图像中粒子的平均大小这样简单的事情,您必须首先手动标记所有的粒子(或者至少是它们的良好代表)。然后你必须计算每个粒子的像素数,从这个数中可以计算出每个粒子的平均面积(以像素为单位)。

一种常用于帮助定量分析 EM 图像的工具是 ImageJ 。ImageJ 是一个开源平台,使用 javascript 来帮助研究人员分析科学图像。有了它,研究人员可以使用多边形选择工具来定义图像的特定区域,然后单击一个按钮,就可以计算出定义区域内的像素数量。虽然这些工具很有帮助,但必须手动定义图像内的区域仍然妨碍了一次分析许多 EM 图像的高通量方法。

缺乏用于定量图像分析的自动化工具是因为传统上,很难将图像输入计算机,然后让计算机理解它在看什么。虽然一个人可以看到一幅图像并立即从背景中识别出前景物体,但计算机只能得到一个代表特定点颜色强度的像素值矩阵。这如图 3 所示,其中图像显示为像素值的三个堆叠 2D 阵列(或单个 3D 阵列)。第一 2D 阵列通常代表红色通道,而第二和第三阵列分别代表蓝色和绿色通道。正是这些 2D 阵列中的像素值之间的密集复杂关系层通知了前景对象与背景的分离。因此,开发一种方法来分割 EM 图像中的不同粒子(也可以很好地推广)不是一件容易的事。

图 3 表示图像的像素阵列。作者制作的图像。

作为解决方案的深度学习

幸运的是,并不是所有的希望都破灭了。深度学习是一类机器学习算法,近年来,它推动了计算机视觉的极限。通过使用基于深度学习的模型,研究人员已经能够在自动驾驶汽车、面部识别系统和 3D 模型构建等技术上取得突破。特别是,深度学习模型已经允许计算机不仅可以自动对图像进行分类,还可以将它们分割成不同的组成部分。

对于 EM 图像,深度学习可用于分割图像的组成部分,以便像素可被分类为背景像素或粒子像素。这在下面的图 5 中示出,白色表示背景像素,黑色表示粒子像素。

图 5 纳米粒子的 EM 图像及其通过深度学习的分割。来源: CNR-IOM ( 抄送)

注意,深度学习模型将图像从范围从 0 到 255 的像素值的 3D 阵列(对于具有 8 位像素值的图像)转换为像素值为 0 或 1 的 2D 阵列。通过降低阵列的复杂性并对其进行限制,以便只保留最相关的特征,可以明确地进行更多的定量分析,而不用担心“噪声”。例如,如果我们希望量化 EM 图像中有多少像素被粒子占据,我们只需计算 1(粒子像素的值)在我们的分段 2D 阵列中出现的频率。

相比之下,如果我们使用一个更简单的方法,如二值化。对图 5 中的 EM 图像进行二值化的结果将产生如图 6 所示的图像。与使用深度学习来分割图像不同,在图 6 中简单地计算 1(黑色像素)的频率将导致非常不准确的分析。因此,需要更复杂的分析模式来获得准确的结果。

图 6 使用二值化分割图 5 中的 EM 图像的结果。作者制作的图像。

尺寸分布的测量

现在我们有了一个很好的分割图像,我们想要进行什么样的定量分析呢?

量化 EM 图像以促进科学研究的最简单但也是最有效的方法之一是找出样品中颗粒的尺寸分布。例如,我们为了健康而服用的许多药物必须具有一定的尺寸,以便靶向特定的器官并穿透我们的细胞壁。同样,对于许多化学合成来说,反应物必须在一定的尺寸范围内,否则它们会太大而不能发生反应。

其他更复杂的过程也可能在纳米尺度上独特地依赖于尺寸。金属和金属氧化物纳米颗粒中的量子尺寸效应通常决定了它们的光子和光电性质。由于金属纳米粒子的大小与其吸收特定波长光的能力有着错综复杂的联系,研究人员经常会调整他们的光电设备(如太阳能电池、led、光纤激光器等)。)通过简单地改变这些设备中使用的纳米颗粒的尺寸。

然而,为了成功地调整所有这些有趣的属性,必须首先确定样本的大小。在上面的图 5 中,计算分割图像阵列中 1(粒子像素)的频率对于计算机程序来说是一项微不足道的任务。由于大多数 EM 图像也有相关的比例尺,因此可以通过简单地将该频率乘以像素到纳米/微米的转换比率来确定颗粒的面积。这将导致图 5 中纳米颗粒尺寸的定量测量。

然而,当图像中有许多粒子时,确定尺寸分布需要稍多的步骤。简单地计算图像中 1 的频率将会发现所有粒子的大小都是一个单独的斑点,而不是单个的实体。为了确保保持每个粒子的个性,您可以给图像中每个 1 的连接区域一个唯一的标签。这被称为连通分量标记。例如,考虑下面的 2D 阵列:

[ [ 1,0,0,11 ],
,【0,0,0,11】,

,【1,0,0,0,0】,
,【0,0,0,11】]

1 的每个连接区域分配一个唯一的标签将产生以下数组:

[[ 1,0,0,2,2 ],
,[ 0,0,0,2,2 ],
,[ 3,3,0,0,0 ],
,[ 3,0,0,0,0,0 ],
,[ 0,0,0,4,4 ] ]

获得大小分布仅仅是计算每个独特的标记在阵列中出现的频率。因此,在上面的例子中,粒子 1 具有一个像素的大小,粒子 2 具有四个像素的大小,粒子 3 具有三个像素的大小,粒子 4 具有两个像素的大小。

把所有的放在一起

为了给研究人员提供一种获得 EM 图像中纳米颗粒尺寸分布的定量测量方法,我利用深度学习开发了 SAEMI ,这是一款利用深度学习计算 EM 图像中纳米颗粒尺寸分布的 web 应用程序。

为了使用该应用程序,首先上传一张图像,随后调整其大小,以确保输入像素的数量与我用于训练深度学习模型的像素数量相匹配。这是使用双线性插值来完成的,双线性插值使用沿两个轴的线性插值来将一个网格值(在这种情况下是像素值)映射到另一个不同大小的网格值上。

一旦完成,图像就被输入到我的深度学习模型中,该模型是根据从 NFFA-欧洲拍摄的一组 EM 图像进行训练的。由于这个图像集只包含原始的 EM 图像(我们的 x 值),我不得不提供我自己的基本事实标签( y 值)。这是使用上述的 opencv-python 算法和 ImageJ 图像分析工具的组合来完成的。在下面的图 6 中,您可以看到一个原始 EM 图像及其对应的地面实况标签的示例。

图 6 a)原始电磁图像。来源:CNR-IOM(CC-BY)b)EM 图像的地面真实标签。

经过我训练好的模型,结果是一个二元预测。这个二进制预测是由 0 和 1 组成的 2D 值数组,其中 0 表示背景像素,1 表示粒子像素。这种预测的一个例子如图 7 所示。

图 8 a)原始 SEM 图像。来源: CNR-IOM ( CC-BY ) b)通过深度学习模型馈入后的预测二值图像。金色代表像素值 0,紫色代表像素值 1

最后,通过计数 2D 阵列中 1 的每个连接区域并报告它们的频率来进行定量分析。大小分布随后显示为直方图,并可以下载为. csv 文件,以供研究人员在认为合适时进行进一步分析。下图 9 显示了图 8 所示 EM 图像的直方图。

图 9 图 8 的纳米颗粒尺寸分布直方图。截图来自 SAEMI

所有这些计算,从调整图像大小到使用深度学习分割图像,再到确定颗粒尺寸分布,都是通过点击按钮和最少的输入来执行的。通过利用深度学习的能力,可以以高效和高通量的方式进行 EM 图像的定量分析。

在我系列的第 2 部分中,我将更详细地介绍如何使用该应用程序,在我系列的第 3 部分中,我将更深入地研究训练深度学习模型的过程,以及如何使用深度学习来分割图像。敬请关注,如果你碰巧参与了纳米尺度的研究,请查看我的应用,亲自测试一下。随时欢迎反馈!

SAEMI:电子显微镜图像的尺寸分析

原文:https://towardsdatascience.com/saemi-size-analysis-of-electron-microscopy-images-36c9f61d52ed?source=collection_archive---------54-----------------------

来源: CNR-IOM ( 抄送)

第三部分,训练电子显微镜的分割模型

本文是我的系列文章的第 3 部分,详细介绍了 SAEMI 的使用和开发,这是我创建的一个 web 应用程序,用于执行电子显微镜图像的高通量定量分析。你可以点击这里查看应用,点击这里查看其 github 。此外,请查看第 1 部分这里(我在这里谈论创建该应用程序背后的动机)和第 2 部分这里(我在这里给出如何使用该应用程序的演示)。在这篇文章中,我将讲述我如何训练一个深度学习模型来分割电子显微镜(EM)图像,以便对纳米颗粒进行定量分析。

选择数据集

为了训练我的模型,我使用了来自NFFA-欧洲的数据集,其中包含来自意大利的里雅斯特 CNR-IOM 的 20,000 多张扫描电子显微镜(SEM)图像。这是唯一一个我可以免费访问的大型 EM 图像数据库,正如你所想象的,大多数 EM 图像都有很强的版权问题。该数据库包括 20,000 多张 SEM 图像,分为 10 个不同的类别(生物、纤维、薄膜涂层表面、MEMS 器件和电极、纳米线、颗粒、图案化表面、多孔海绵、粉末、尖端)。然而,对于我的训练,我将训练图像限制为仅取自粒子类别,该类别包含不到 4000 张图像。

其中一个主要原因是,对于几乎所有其他类别,实际上没有办法从图像中获得有用的尺寸分布。例如,考虑图 1a 所示的纤维的 EM 图像。分割图像后,我可以计算图像中每根纤维的大小,但你也可以清楚地看到纤维延伸出图像。因此,我计算的尺寸仅限于电子显微镜图像,我无法单独从图像中提取纤维的长度。

1 a)来自数据集的纤维的 EM 图像 b)来自数据集的颗粒的 EM 图像。来源: CNR-IOM ( 抄送)

将其与图 1b 中颗粒的 EM 图像进行比较,其中图像中显示的尺寸显然是整个颗粒的尺寸。不仅如此,这一类别中的图像往往具有最小程度的遮挡,这使得标记和训练更加容易。

在粒子类别中,所有的图像都是 768 像素高,1024 像素宽。大多数粒子的形状大致为圆形,一些图像中有数百个粒子,而另一些图像中只有一个粒子。由于放大倍数的不同,像素中颗粒的尺寸也有很大差异,范围从 1 微米到 10 纳米。下图 2 显示了数据集中一些粒子图像的示例:

图 2 4 在数据集中发现的颗粒的电子显微镜图像。来源: CNR-IOM ( 抄送)

移除横幅

为了开始训练过程,首先必须对原始图像进行预处理。在很大程度上,这意味着删除包含图像元数据的横幅,同时保留尽可能多的有用图像数据。为了做到这一点,我使用了一种叫做“反射填充”的技术。实际上,我用顶部和底部的倒影替换了横幅区域。这方面的一个例子如图 3 所示,反射以红色突出显示。

图 3 反射填充的例子。来源:CNR-IOM(CC-BY)

然而,为了执行这个转换,我首先必须使用 OpenCV 检测横幅区域。首先,我用我的图像创建了一个二进制蒙版,其中所有高于 250 的像素值都变成了 255,所有低于阈值的像素值都变成了 0。下面显示了用于执行此操作的代码以及图 4 中的结果。

import cv2# convert image to grayscale
grayscale = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# create binary mask based on threshold of 250
ret, binary = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)

图 4 a)原始图像 b)二值化后的图像

虽然阈值处理在寻找横幅区域方面已经相当有效,但我仍然可以检测到图像中不属于横幅的一些元素。为了确保只检测到横幅区域,而没有其他区域残留,我在阈值图像上使用了腐蚀和膨胀来找到图像中垂直线和水平线的位置。侵蚀是一种图像处理技术,其中给定大小的核在图像上扫描。当内核沿着图像行进时,找到最小像素值,并且中心的像素值被该最小值替换。膨胀是一种类似的操作,但使用的是最大像素值。

下面显示了检测垂直线和水平线的代码,以及它们在图 5 中的结果。

# create tall thin kernel
verticle_kernel = cv2.getStructuringElement(
    cv2.MORPH_RECT, 
    (1, 13)
)# create short long kernel
horizontal_kernel = cv2.getStructuringElement(
    cv2.MORPH_RECT, 
    (13, 1)
) # detect vertical lines in the image
img_v = cv2.erode(thresh, verticle_kernel, iterations = 3)
img_v = cv2.dilate(img_v, verticle_kernel, iterations = 3)# detect horizontal lines in the image
img_h = cv2.erode(thresh, horizontal_kernel, iterations = 3)
img_h = cv2.dilate(img_h, horizontal_kernel, iterations = 3)

图 5 a)垂直侵蚀/膨胀 b)水平侵蚀/膨胀

然后我把两个结果加在一起,并对反转的数组进行了最终的腐蚀+二值化。代码和结果如下面的图 6 所示。

# add the vertically eroded and horizontally eroded images
img_add = cv2.addWeighted(img_v, 0.5, img_h, 0.5, 0.0)# perform final erosion and binarization
img_final = cv2.erode(~img_add, kernel, iterations = 3)
ret, img_final = cv2.threshold(
    img_final, 
    128, 
    255, 
    cv2.THRESH_BINARY | cv2.THRESH_OTSU
)

图 6 a)添加垂直腐蚀和水平腐蚀的图像 b)执行最终腐蚀和二值化

有了图 6b 所示的结果,我终于可以安全地检测到横幅并将其删除。首先,我找到了最终结果中像素值为 0 的地方,并将左上角和右下角的坐标放入单独的变量中。同样,我计算了横幅区域的高度和宽度。

import numpy as npbanner = np.argwhere(img_final == 0)# coordinates of the top left corner
banner_x1, banner_y1 = banner[0, 1], banner[0, 0]# coordinates of the bottom right corner
banner_x2, banner_y2 = banner[-1, 1], banner[-1, 0] # calculate width and height of banner
banner_width = banner_x2 - banner_x1
banner_height = banner_y2 - banner_y1

接下来,我用这些坐标和横幅的高度/宽度找到它的上下边缘的“倒影”。

# finding the reflection below the banner
bot_reflect = img[
    banner_y2:banner_y2 + banner_height // 2, 
    banner_x1:banner_x2, 
    :
]
bot_reflect = np.flipud(bot_reflect)# finding the reflection above the banner
top_reflect = img[
    banner_y1 - (banner_height - len(bot_reflect)):banner_y1,    
    banner_x1:banner_x2, 
    :
]
top_reflect = np.flipud(top_reflect)

我找到的区域在下面的图 7 中用绿色突出显示。

图 7 图像中用于反射填充的区域(以绿色突出显示)。来源: CNR-IOM ( 抄送)

最后,我把上面和下面的倒影连接起来,用倒影替换了横幅区域。

reflect_pad = np.concatenate((top_reflect, bot_reflect), axis = 0)
imgcopy = img.copy()
imgcopy[banner_y1:banner_y2, banner_x1:banner_x2] = reflect_pad

最终结果(imgcopy)如下面的图 8 所示。

图 8 反射填充的最终结果。来源: CNR-IOM ( CC-BY )

基本事实标签

移除横幅后,我使用 OpenCV 和 ImageJ 的组合创建地面真相标签。为了帮助创建地面真实标签,使用 OpenCV 使用了分水岭算法

分水岭算法是一种分割算法,它将图像视为地形表面,其中高亮度像素值为丘陵,低亮度值为山谷。该算法背后的想法是,你用水填充“山谷”,随着水位上升,你沿着水汇合的边缘建造屏障。这些障碍代表了图像的分割边界。

虽然分水岭算法非常有用,但它也往往对噪声非常敏感。通常,您需要对图像执行更多的预处理,如阈值处理和 T2 距离变换,以获得良好的分割效果。这使得基于分水岭的分割算法难以很好地推广到各种图像。下面的图 9 显示了一个应用分水岭算法(带有一些预处理步骤)的例子。

# remove noise using a mean filter
shifted = cv2.pyrMeanShiftFiltering(imgcopy, 21, 51)# threshold the image 
graycopy = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(
    graycopy, 
    0, 
    255, 
    cv2.THRESH_BINARY | cv2.THRESH_OTSU
)

# apply adistance transform and find the local maxima
dt = cv2.distanceTransform(thresh, 2, 3)
localmax = peak_local_max(
    dt, 
    indices = False, 
    min_distance = min_distance_bt_peaks, 
    labels = thresh
) # apply the watershed algorithm
markers, num_seg = label(localmax, structure = np.ones((3, 3)))
labels = watershed(-dt, markers, mask = thresh)

图 9 使用分水岭算法的结果

如上所述,分水岭算法本身不足以创建地面真实标签。然而,作为第一遍近似,它的表现并不太差。因此,我决定使用上面的代码,首先为尽可能多的图像提供标签,然后用 ImageJ 再次检查标签,以清理标签,使它们更准确地匹配图像。

ImageJ 是一个用 Java 编写的图像处理和分析工具,专为科学图像设计。下面的图 10 显示了 ImageJ 菜单的一个例子。

图 10 图像 j 菜单

我使用菜单左侧的五个选择工具,用鼠标选择图像的区域,然后选择:编辑→选择→创建遮罩,以清理从分水岭算法创建的第一遍地面真实标签。为了标记尽可能多的图像,我试图限制标记,以便只要图像中有足够多的粒子可以给出尺寸分布的良好近似,地面真实标记是可以接受的。这方面的一些例子如图 9 所示。

图 9 不充分的事实标签。来源: CNR-IOM ( CC-BY )

起初,我用这种方法标记了大约 750 张图片。然而,我在训练中很快发现,使用这种质量的标签并不能很好地训练,我不得不更加谨慎地用 ImageJ 对它们进行第二次检查。这一次,我尽可能确保图像中的每一个粒子都以接近像素的精确度被标记出来。

为了确保数据质量,地面实况标签覆盖了相应的图像,并用肉眼检查,以确定它们相互反映的程度。然后,继续使用 ImageJ 的选择工具,我会用放大 200-300 倍的覆盖图来修饰标签。这些新的基本事实标签的例子如图 10 所示。

图 10 最终地面真相标签。来源: CNR-IOM ( CC-BY )

总共有大约 250 张图片被第二次完全贴上标签。这些被放在单独的目录中,一个目录用于原始图像(从现在开始称为path_img),另一个目录用于地面真相标签(从现在开始称为path_lbl)。虽然这是一个比以前小得多的数据集,但由于影像和地面实况标签之间不存在不准确性,因此在跟踪训练损失时,它表现出了更好的性能。

创建数据集群

一旦创建了足够多的地面真相标签来形成我的训练集,我就开始训练我的深度学习模型。为了做到这一点,我使用了 fastai ,这是一个基于 PyTorch 构建的强大库,可以使使用深度学习的培训更加直观和简化。第一步是确保我的训练集以适合 fastai 框架的方式组织。为此,需要将训练数据和基础事实标签放入一个 DataBunch 对象中。我将展示下面的代码,并在下面的段落中更详细地解释它。

# use smaller image sizes for to deal with memory issues
size = (192, 256)# batch size
bs = 24# create DataBunch
data = (SegmentationItemList.from_folder(path_img)
        .split_by_rand_pct(0.2)
        .label_from_func(lambda x: path_lbl/f'{x.stem}.png')
        .transform(
              get_transforms(flip_vert=True, max_warp=None),    
              tfm_y=True, 
              size=size
         )
         .databunch(bs=bs)
         .normalize())

这段代码的主要部分从“创建数据集中”部分开始,在这里变量data变成了数据集中对象。它首先从path_img目录中收集原始图像,并将数据分成训练集和验证集。这是在.split_by_rand_pct(0.2)行中完成的,其中随机选择 20%的数据作为验证集,以防止训练集过度拟合。

每个图像的相应基本事实标签(即损失函数中使用的 y 值)然后在.label_from_func中收集。这将一个函数应用于在path_img中收集的每一项,并将所有输出放入一个列表中。我应用的函数lambda x: path_lbl/f'{x.stem}.png'获取图像文件名,并在path_lbl目录中寻找匹配的文件名(扩展名为. png)。

然后将一系列图像变换应用于训练和验证集,作为数据扩充的一种形式。我使用的变换包括(但不限于)翻转任一轴上的图像,旋转图像和放大图像。关于我使用的转换的完整列表,请点击这里的。请注意,我还应用了tfm_y=True,这意味着我们应用于原始图像的任何变换也会应用于相应的标签。这对于进行图像分割时的训练至关重要。

此外,对所有图像应用了调整大小转换,确保它们都是 192x256 像素。这个转换应用于所有图像的原因很不幸,因为如果图像再大一点,我在训练它们的时候就会耗尽 CUDA 内存。出于类似的原因,用于训练的批量大小也是一次只有 24 个图像。

最后,像素值在结束时都被归一化,以防止大的异常值影响训练过程。

骰子系数

训练前的最后一步是确定一个度量标准,以确定模型的表现如何。用于图像分割模型的一个常见度量是骰子系数。它与 F1 的分数相同,尽管表达方式略有不同。

骰子系数通过以下公式计算:

其中 TP 是真阳性,FP 是假阳性,FN 是假阴性。在这种情况下,真正值指的是由预测确定的所有正值,这些值也出现在基础真值标签中的相同位置。例如,如果 7x7 图像的基本事实标签是:

预测是:

真阳性的数量将是 11 ie。有 11 个出现在预测和基本事实标签的相同位置。假阳性的数量将是 1(即在预测中出现一个不存在于基本事实标签中的 1)并且假阴性的数量是 2(即两个 1 存在于预测没有获得的基本事实标签中)。骰子系数为:

培训— U-Net

既然已经创建了 DataBunch 对象并选择了度量标准,我终于可以开始训练了。用于图像分割任务的常见架构是 U-Net 架构。U-Net 主要由两个主要部分组成,下采样路径(也称为编码器)和上采样路径(也称为解码器)。它具有自动编码器的许多功能,但在编码和解码部分之间明显增加了跳跃连接。稍后我将解释跳过连接,但首先,让我们讨论它作为自动编码器的属性。下面的图 9 是取自原始 U-Net 论文的 U-Net 架构的一个例子。你可以在这里罚款原纸

图 9 U-Net 架构。来源:https://arxiv.org/abs/1505.04597

网络的左半部分是标准的 CNN,具有卷积层,后面是 ReLU 和 max pool 层。这是一个经典的 CNN,通常用于图像分类问题,目标是输入图像像素值并输出单个分类。然而,在图像分割中,目标是对图像中的每个像素进行分类。因此,您的最终张量应该与原始图像的大小和形状相同(在我们的例子中,为 192x256)。这是通过在架构的上采样部分使用上卷积层来实现的。

您可以将向上卷积(也称为转置卷积)视为在像素值之间额外填充零的卷积。图 10 显示了一个例子。

图 10 a)用于上采样的输入 b)在像素值之间用零填充的输入 c)与 3×3 内核卷积后的输出矩阵

请注意,填充后,执行卷积会产生比输入维数更大的输出。这使得我们最终的输出与输入的大小和形状相同。

现在让我们来谈谈跳过连接。回到图 9,注意连接从下采样路径到上采样路径的层的灰色箭头。这些被称为跳过连接,它们将下采样层的输出连接到相应的上采样层。这些跳跃连接的目的是允许将在下采样路径中学习到的特征和空间信息传输到上采样路径。这允许上采样路径在执行分段时记住“在哪里”以及“是什么”。

幸运的是,fastai 库提供了一个函数,可以在一行代码中创建整个架构。

learn = unet_learner(data, models.resnet18, metrics=dice)

在我的训练中,我使用 resnet18 CNN 作为下采样路径。data参数是我们之前创建的 DataBunch,而用于评估模型的指标是 Dice 指标(如果您不知道 Dice 指标是什么,我会在讨论如何确定模型的有效性时详细介绍)。

然后,通过整个 U-Net 后的最终输出是一个二进制遮罩,其像素值被分类为背景像素(0)或粒子像素(1)。

培训—超参数调整

当训练一个模型时,你总是要经历大量的超参数调整。最重要的超参数之一是学习率,这是获得正确结果的关键。幸运的是,fastai 提供了许多资源,使得优化学习率变得更加容易。

创建 U-Net 架构后,进行了模拟训练,使用从 1e-7 到 10 的各种学习率计算损失。这是使用 fastai 库中的 LR 查找器完成的。结果如图 11 所示。

lr_find(learn)
learn.recorder.plot()

图 11 学习率探测器图

确定学习率时,通常希望在曲线达到最小值之前选择一个值。但是,请记住,这只是一个起点,您可能仍然需要根据训练过程调整学习速度。例如,如果您的损失在后续迭代后开始增加,您可能仍然需要选择较低的学习速率。用于训练模型的最终学习速率是 2e-5 和 5e-5 之间的循环学习速率

另一个要考虑的超参数是冻结或解冻图层组的程度。当通过 fastai 创建 U-Net 时,权重参数已经在 ImageNet 数据库中进行了训练。这背后的想法是,由于许多图像通常彼此相关,使用一个数据集确定的权重通常可以转移到另一个数据集,并获得相当好的结果。这允许您使用较少数量的图像来训练相对较好的模型。在我的训练中,我冻结了三层组中的前两层。

learn.unfreeze()
learn.freeze_to(2)

最后,要确定的最后一个超参数是时期数和重量衰减。在多次测试和实验之后,发现这些超参数的最佳值是 50 个时期和 0.1 的重量衰减。

learn.fit_one_cycle(50, slice(2e-5, 5e-5), wd=1e-1)

图 12 显示了培训期间培训和验证损失的图表。

图 12 培训期间的培训和验证损失

估价

最后一步是评估模型的有效性。训练之后,创建了最终的第三个测试集,该测试集根本不参与训练。Dice 系数是使用该测试集上的预测和基本事实标签计算的,发现为 97.5%。

此外,最终预测与原始图像重叠,并通过肉眼检查,以确定模型在给定 EM 图像上的表现如何。下面的图 13 显示了这样一个例子:

图 13 预测覆盖在原始图像上。截图来自 SAEMI

虽然该模型在预测粒子的位置方面做得很好,但当粒子在图像中非常接近时,它很难分离粒子。目前,该应用程序允许您执行一些后处理,以避免这一问题,但在未来,我想尝试通过在更平衡的数据集上训练实例分割模型来改进我的模型,同时增加训练图像的数量。

摘要

由从 NFFA-欧洲获得的 SEM 图像组成的训练集,并进行相应的处理以获得良好的数据。地面实况标签是使用 OpenCV 和 ImageJ 的组合制作的。通过利用 U-Net 和 Dice 度量,使用 fastai 库进行训练。由单独的测试组确定的最终骰子得分为 97.5%。

如果你想了解更多关于这个培训背后的动机,请点击这里查看我的系列的第一部分。如果你想了解如何使用我创建的部署这个模型的应用程序,请查看我的系列文章第 2 部分这里。非常感谢你阅读这篇文章,我希望你能从中获得许多有价值的信息!

电子显微镜图像的尺寸分析,

原文:https://towardsdatascience.com/saemi-size-analysis-of-electron-microscopy-images-7ab55bd979ac?source=collection_archive---------62-----------------------

来源: CNR-IOM ( 抄送)

第 2 部分,如何从 EM 图像获得尺寸分布

本文是我的系列文章的第 2 部分,详细介绍了 SAEMI 的使用和开发,这是我创建的一个 web 应用程序,用于执行电子显微镜图像的高通量定量分析。在这里查看第 1 部分(在这里我回顾了应用背后的动机)和第 3 部分这里(在这里我回顾了我如何训练我的图像分割模型)。你也可以在这里查看 app ,在这里查看它的 Github 。在这篇文章中,我给出了一个如何使用该应用程序并获得最佳结果的演练。

所以你有一个电子显微镜图像

从这篇文章开始,我假设你以前看过/拍过电子显微镜(EM)图像,或者至少熟悉电子显微镜。如果没有,请查看我的系列文章第 1 部分中的,在那里我详细描述了开发这个应用程序背后的动机。不过,让我们假设你是一名研究人员,已经进行了一些电磁测量,现在想对你的图像进行一些定量分析。更具体地说,您想要确定图像中颗粒的平均尺寸以及该分布的标准偏差。例如,假设您有一个 EM 图像,如图 1 所示。

1 是一个电磁图像的例子。来源:CNR-IOM(CC-BY)

这里首先要注意的是图像底部显示的横幅,其中包含测量信息,如比例尺、电子高压(EHT)和放大倍数。许多电子显微镜及其配套软件会将这类信息(至少会有一个比例尺)添加到图像中。不幸的是,由于这是图像本身的一部分,因此保留它可能会影响深度学习模型的最终分割。

为了减少出错的可能性,应该尽可能从图像中删除附加的“元信息”。但是,请注意在其他地方保留比例尺的记录,因为在最终步骤中,需要将最终计算结果从像素转换为物理尺寸。目前,“元信息”可以通过许多不同的方法去除。

最简单的方法,也是我个人推荐的方法,就是把它从图像中剔除。它引入更多伪像的可能性最小,并且需要图像处理中最少的技术能力来执行。唯一的缺点是裁剪图像可能会丢失重要的数据。

为了展示其他选项,您还可以使用一些更复杂的图像处理技术,使用 Python 中的 open-cv 或 scikit-image 库来移除横幅。这些方法包括使用反射填充、最近邻填充和常数填充。这三种方法的示例如图 2 所示。在所有三个示例中,横幅原来所在的区域都用红色突出显示。

图 2 a)使用反射填充去除标语的示例 b)使用最近邻填充去除标语的示例 c)通过用常数填充替换标语来去除标语。来源: CNR-IOM ( 抄送)

可以看出,这些方法中的每一种都可能导致图像出现非预期的伪像,这取决于您决定如何处理“元信息”。

使用应用程序

现在,假设您决定通过裁剪来移除横幅。然后你可以使用这个链接进入应用程序,然后使用顶部的“上传图片”按钮上传图片。或者,你可以按照我的 github 页面上的说明这里在你的电脑上本地使用这个应用程序。无论哪种方式,一旦打开应用程序,你会看到如图 3 所示的主页。“上传图像”按钮以红色突出显示。如果您手头没有 EM 图像,您也可以使用旁边的“尝试演示”按钮。这将上传一个已经准备好的演示 EM 图像,作为应用程序如何工作的例子。

图 3 赛米主页。截图来自 SAEMI

一旦图像上传到服务器,它将通过我的深度学习模型进行馈送,并使用连通分量标记来计算粒子的大小分布。整个过程大约需要 20-30 秒。我会继续尽我所能加快这个过程。然而,即使有这么长的运行时间,它仍然比手动标记 EM 图像快得多,因为这些图像中一次可以有数百个粒子。计算完大小分布后,您将看到一个类似于图 4 所示的网页。

图 4 图片上传后的 SAEMI。截图来自萨米

让我们更详细地看一下每一部分。

粒度分布

你会注意到的第一个也可能是最重要的部分是尺寸分布直方图。这在图 5 中更详细地示出。

图 5 粒度分布直方图。截图来自萨米

顶部是原始 EM 图像,图像文件的名称显示在它的上方。应用程序计算的大小分布显示在直方图下方,x 轴为大小(以像素为单位), y 轴为计数。因此,有 3 个粒子占据图像中大约 1300 个像素,1 个粒子占据大约 500 个像素。在应用程序中,你可以将鼠标悬停在数据点上来查看它们的确切值。

平均值、中值和标准偏差也已计算出来,并显示在直方图的左上角。在直方图的正上方,你还可以看到一个输入框,上面写着“输入箱子尺寸”。这可用于改变用于定义直方图的仓尺寸。默认的媒体夹大小是 25 像素。例如,图 6 显示了一个 75 像素的直方图。

图 6 箱尺寸为 75 的尺寸分布。截图自萨米

最后,点击底部的“下载数据”将下载尺寸分布作为一个单独的列。csv 文件。然后,您可以执行其他计算,例如确定其他统计模式,或者按照您希望的方式对尺寸分布数据进行建模。

不过,需要注意的重要一点是,所有这些尺寸都是以像素为单位计算的。一个粒子在 EM 图像中占据多少像素。为了获得物理相关的测量值,您必须自己将尺寸转换回物理单位。例如,在图 1 的原始图像中,比例尺显示 2 微米占据大约 110 个像素。因此,500 像素大小的颗粒实际上具有大约 0.9 平方微米的面积。

用片段覆盖原件

您应该查看的下一部分是图 7 中右下方的部分。

图 7 覆盖了分段预测的原始 EM 图像。截图自萨米

这一部分是为了让你仔细检查预测,并确保应用程序能够正确地分割你的 EM 图像。正如你所看到的,这个应用程序在预测图像中每个粒子的位置方面做得非常好。为了更加清晰,您还可以使用图像底部的滑块来更改叠层的不透明度。如果您希望原始图像更清晰,可以将滑块滑动到较低的值。这如图 8 所示。

图 8 显示了原始图像。截图来自萨米

或者,您可以将滑块滑动到更高的值,以便更清楚地显示预测。这如图 9 所示。

图 9 示出了预测的分割。截图来自萨米

从覆盖图来看,似乎这个模型在分割原始 EM 图像的粒子方面做得相当不错。然而,请注意在分段预测的左下方,有两个非常小的预测粒子,它们在原始 EM 图像中不存在。这在图 10 中更详细地示出。

图 10 原始 EM 图像和预测不匹配的区域。截图自萨米

不幸的是,由于它们的尺寸非常小,它们可能会使计算出的平均值偏向较低的值。在这种情况下,中值可能是 EM 图像中平均颗粒尺寸的更好量度。

处理结果图像

为了确保直方图显示尽可能准确,您可以使用“处理图像”部分编辑掉一些不准确的预测。这在图 11 中有更详细的显示。

图 11 后处理图像画布。截图来自 SAEMI

在图 11 中,您可以看到图像下方显示了几个不同的项目,这将帮助您编辑预测。前两个是撤销和重做按钮(分别用左右箭头表示),应该非常简单。在它们旁边的右边是保存按钮,它将保存你对图像所做的更改,然后根据你的更改重新计算尺寸分布。

它们的正下方是“黑白”和“颜色”两个选项按钮。如果选择“黑白”,上面显示的图像将只是二进制掩码。如果你选择“颜色”,图像实际上会显示应用程序看到的单个粒子。换句话说,对于阵列中每个连接的组件,显示的图像将具有唯一的标签。两个选项的比较如图 12 所示。

图 12 a)黑白选项 B)颜色选项。截图自萨米

当你不确定应用程序将两个非常接近的粒子视为一个粒子还是两个粒子时,显示“颜色”选项会很有用。请记住,这两个选项都不会编辑图像本身。它只是为您提供对原始预测的不同看法。

在底部,有一个下拉菜单,允许您选择如何编辑图像。有三个选项可供选择:绘制、移除和擦除。选择这些选项将改变画笔的颜色分别为白色,红色和黑色。

使用“绘制”笔刷编辑图像会将您绘制的任何内容添加到预测中。例如,如果在左上角有一个应用程序不知何故漏掉的粒子,你可以用白色画笔在左下角的画布上绘制来填充它。图 14 显示了一个例子。

图 14 a)原始分割 b)使用 Draw 函数在左上角添加粒子。截图来自萨米

然而,对于我们的图像,有些斑点被误认为是粒子,最好使用移除(红色)或擦除(黑色)选项。通过使用红色画笔,您可以移除任何被红色笔画标记的粒子。图 15 显示了一个例子。

图 15 a)示出了在按下保存并应用移除选项之后,在原始分段 b)上的移除(红色)画笔。截图来自萨米

使用黑色画笔将简单地从预测中擦除该区域。虽然乍一看,红色和白色的画笔是一样的,但它们确实有两种不同的功能。红色画笔移除整个粒子,而白色画笔仅移除已标记的部分。图 16 显示了两种画笔的不同用法。

图 16 a)在粒子上使用红色笔刷的结果 b)在粒子上使用黑色笔刷的结果。截图自萨米

最后,在两个显示选项下面有一个滑块,可以改变“笔刷”的宽度。图 13 显示了不同画笔宽度的示例。

图 13 a)在宽度尺寸为 5 的画布上绘图 b)在宽度尺寸为 30 的画布上绘图。截图来自萨米

恭喜你!你有一个尺寸分布

在执行了令您满意的后处理步骤后,您最终会得到一个准确反映原始图像的尺寸分布。最终的直方图如图 18 所示。

图 18 最终的直方图。截图来自萨米

与我们在图 5 中获得的原始直方图相比,差异并不大,因为该模型已经在预测图像中的粒子方面做得非常好。此外,图像中有许多粒子,所以即使是它犯的小错误也被它识别的阳性粒子淹没了。

但是,如果仔细观察,您会发现平均值从 1.17E3 变为 1.23E3,分布的标准偏差从 5.73E2 变为 5.23E2。同样,您可以将此分布下载为. csv 文件,以便稍后执行其他计算。的。csv 文件包含由我的深度学习模型确定的所有单个粒子的计算尺寸(以像素为单位)。

下载后要执行的计算示例。csv 文件,您可以计算您的大小分布的模式和偏斜,如果您想要更多的统计信息。您也可以从同一样本的多个图像中汇总数据,以获得更大的样本量。此外,您可以绘制多个直方图,反映样品大小分布如何随时间、温度、pH 值等变化。

如果你想在你自己的 EM 图像上尝试 SAEMI,你可以现在就去那里并遵循本文中概述的步骤。祝你好运,请查看本系列的最后部分,在那里我会更深入地了解我是如何训练深度学习模型的,以及它的架构实际上是什么。谢谢!

安全的城市自行车运动第 1 部分:使用历史骑行数据创建随机的城市自行车路线并制作动画

原文:https://towardsdatascience.com/safe-city-biking-part-1-creating-randomized-citibike-routes-using-historical-ride-data-136224bbd955?source=collection_archive---------76-----------------------

马克斯·本德在 Unsplash 上的照片

端到端项目

在本文中,我将勾勒出我的项目路线图,从 Arduino 到数据科学/分析再到增强现实,并从第一步开始。

我一直是一个狂热的自行车爱好者:从每天骑自行车在数英里的山上往返于高中,到现在每天骑着 Citibike 在布鲁克林和时代广场之间跑 12 英里去上班。以下是我成为会员两年来的 Citibike 统计数据截图(在此之前,我会在城市里玩滑板):

这是横穿美国大约一倍半的距离

如果你熟悉在纽约骑自行车,你就会知道骑自行车并不总是安全或平稳的。这些年来我看到了进步,但也有相当多的事故,比如被打开的车门撞上,或者撞到一个很大的坑洞,我被从自行车上摔了下来。除了伤害/事故统计数据之外,很难找到这个城市实际骑自行车情况的数据,所以我决定收集自己的数据。

以下是项目的主要步骤:

  1. 使用 2019 年夏季的 Citibike 历史骑行数据规划骑行路线。
  2. 创建具有 GPS/LTE、陀螺仪和加速度计功能的 Arduino 传感器。
  3. 路线上超过 100 次骑行,从安装在自行车头盔上的传感器收集数据。使用 HTTP post 请求发送数据,并将这些数据存储在数据库中。
  4. 在 Python 中分析这些数据后,构建一个模型,该模型可以预测从 A 点到 B 点的最稳定乘坐,同时还可以提供路线间平均速度和稳定性的热图。
  5. 使用 Mapbox 将所有这些整合到 Unity 中,并创建 AR 世界范围的应用程序和 VR 可探索的数据体验。

首先,我从 Citibike 的月度骑行数据库下载了 2019 年 6 月和 7 月。我想找出其他骑手最常走的路线,并围绕这些路线规划我的路线。为此,我创建了一个名为pathfinder的函数,它将创建一条从离我最近的自行车站开始的路线,并不断选择每英里最受欢迎的自行车站,直到它到达五英里。第一步是使用哈弗森公式,过滤数据帧,只保留距离起点站 1 英里半径内的终点站。这个公式考虑了地球的曲率,这比只采用欧几里得距离更准确。

我使用斯科尔斯街&曼哈顿大道和 S 4 街&威斯大道,函数返回 1.14 英里,而谷歌地图给我 1.3 英里。我添加了一个 1.15 倍的乘数,以说明从鸟瞰图到实际路线长度的转换,从而得到 1.31 英里。

我们将使用 OSMNX 在未来的计算中使用最短的路线

然后,我使用 rand 从该点选择三个最受欢迎的终端站之一。一旦我离开我的第一个出发站 5 英里,我将结束探路者循环,使用osmnx包走所有的路线。

我们的 Pathfinder 函数通过 while 循环输出以下路径:

station number 0: Scholes St & Manhattan Ave
station number 1: Bayard St & Leonard St
station number 2: 49 Ave & 21 St
station number 3: 1 Ave & E 18 St
station number 4: 1 Ave & E 44 St

我们可以用它来绘图:

fig, ax = ox.plot_graph_routes(G, route, route_color='b', node_size=0)

经过一些 Geopandas 数据帧处理后,我用 plotly.express scatter_mapbox制作了路线动画。

我将在每次外出时为自己生成一条随机路径,尽管随着时间的推移,我将不得不考虑覆盖范围以及与先前路线的重叠。

如果我们有车站自行车数量/可用性数据,我们可以使用它来运行蒙特卡罗马尔可夫链(MCMC)模型(如果在每个时间点从车站租赁自行车,则根据会员/骑手总数有额外的车站激活概率),以计算每天结束时自行车在城市中的分布。我可能会在另一篇文章中做这个练习,因为在运行模型之前,它需要更多的数据收集和更多的预处理步骤。

下一次,我将介绍如何设置 Arduino 设备,并将其连接到数据库以使用 Python 进行查询。如果你对这个项目感兴趣,请随时联系我,就你希望我收集的任何数据或你可能有的想法提出建议。

你可以在这个 GitHub 库里找到所有的代码。

Power BI 中的安全点击技巧

原文:https://towardsdatascience.com/safe-click-trick-in-power-bi-d3e2296ade04?source=collection_archive---------45-----------------------

想要避免恼人的书签“点击”行为?检查这个简单的“安全点击”技巧!

JESHOOTS.com 在 Pexels 上拍摄的照片

书签是 Power BI 中最常用的功能之一。就我个人而言,这是我的最爱之一,因为它们让您有可能创建“类似应用程序”的功率 BI 报告,并使它们看起来更具交互性。

我已经写过关于使用书签将静态视觉效果转换成动态视觉效果的文章。你也可以使用书签构建一个你的用户会喜欢的专业切片器面板。老实说,确实有许多可以应用书签的用例。

基本上,书签所做的是,它捕捉报告页面上视觉效果的当前状态,因此您可以通过按钮、图像、形状等在您的操作中使用它作为参考。

讨厌的书签

然而,书签有一个非常令人讨厌的行为,当您处理形状时会出现这种行为。让我们转到 Power BI 中的一个示例,我将解释问题是什么以及如何轻松解决它。

我有一个大的切片器面板,作为多个切片器的占位符。然而,如果我点击矩形区域的某个地方,在切片器本身之外,这个窗格将覆盖所有切片器!对于我的用户来说,这不是一个好的体验,因为他们中的大多数人不知道他们可以点击形状以外的任何地方,一切都会恢复正常。而我想改变这种行为…

安全点击技巧!

我们需要做的第一件事是打开 Power BI desktop 中视图选项卡下的书签窗格:

展开切片器窗格后,我将创建一个新的书签,取消选中所有内容,因此它变成灰色:

现在,让我们为我们的形状定义一个动作,以便它指向我们新创建的书签:

由于我们已经禁用了一切,这个形状变得不可点击,这意味着它不会出现在我们的切片器,如果用户点击它!

让我们看看这是否可行:

厉害!正如你所注意到的,当我点击形状内的区域时,在切片器之外,什么也没有发生——正如我们所预期的!

结论

小细节可以对用户体验产生很大影响。在设计 Power BI 报告时请记住这一点,您的用户会喜欢您的:)

订阅这里获取更多有见地的数据文章!

安全!击球手对到达一垒的影响

原文:https://towardsdatascience.com/safe-the-effect-of-batting-hand-on-making-it-to-first-base-b89ac3a1888?source=collection_archive---------43-----------------------

实践教程

左撇子或右撇子击球手更容易吗?

照片由 Tim GouwUnsplash 上拍摄

几周前,我和家人一起看了 2020 年世界职业棒球大赛的第一场比赛。我不确定为什么会出现这个问题,但有人问客厅对左撇子或右撇子击球手在接到球后是否更容易上一垒的看法。这是一个有趣的问题,没有人有充分的答案。我的父亲是闪耀着智慧光芒的灯塔,他说我应该用科学的方法来解决这个问题,建议我召集大约 30 个朋友和同事在当地的 diamond 进行一个下午的跑步训练。这不是一个坏主意,但让 30 个人一起在全球疫情中可能是一个糟糕的决定。代替直接的科学观察,通过已经收集的数据可能会有用。记住这一点,为了纪念刚刚结束的 2020 年世界职业棒球大赛(祝贺道奇队的球迷!),我决定围绕这个主题来撰写我的首次数据项目文章博客。

假设:距离还是加速度?

在起居室的辩论中,出现了两种假设,各持一方。EASY-L 假说的支持者认为,左手击球手(lhb)更容易,因为从他们的本垒板到一垒的距离比那些右击球手小。EASY-R 的支持者说,因为右撇子面对一垒时转弯较少,所以他们加速更有效率,也更容易到达 1B。图 1 给出了这些假设背后的逻辑的图形表示。两个都是好主意,但只有一个能成为冠军。让我们看看数据是怎么说的。

图一。击球几何图;(左)左手面糊几何。θ' L '是击球手的法线矢量和朝向一垒的矢量之间的角度。-X 表示左手击球手在 X 轴上比本垒板的原点更靠近 1B;(右)右手面糊几何。θ' R ';是击球手的法线向量和朝向一垒的向量之间的角度。+X 表示右手击球手在 X 轴上比本垒板的原点离 1B 更远。作者图片

首先,在这个项目中,我使用了从 MLB 的一个统计网站 BaseballSavant [1]下载的 2020 年统计数据。它有一个伟大的用户界面,这意味着没有必要刮什么…?事情简单时真好。我使用的数据集(以及大多数棒球数据集)的唯一问题是,它没有将击球手作为每个球员条目的一个特征。考虑到击球手是整个分析的阶级区分基础,没有这些数据是一个小问题。幸运的是,在做了一些挖掘后,我能够在 BaseballSavant 上找到另一个数据集,它确实包括击球手作为一个特征(所以肯定没有刮擦!)【2】。

在添加击球手分类变量后,我将主数据集分成三个子集:(1)右手击球手,(2)左手击球手,以及(3)交换击球手统计数据。我将交换击球手从两个单边击球手组中分离出来,因为没有任何容易获得的统计数据来描述交换击球手在本垒板两侧的表现。无论如何,一旦这种分离完成,就该进入数据了。

一个很好的初始统计数据是球员平均击球率(BA 或 AVG ),我们的数据显示 RHBs(2020 年)比 LHBs 表现好约 2%(图 2。).这是一个有趣的结果:常识告诉我们,当击球手面对投掷臂与击球臂相反的投手时,他们会表现得更好。考虑到目前大多数投手都是用右手投球,一般来说,左投手应该比右投手表现更好,但是我跑题了。

图二。每类击球手的平均击球率分布;作者图片

RhB 相对于其左对手 BA 的 2%的优势可能是 EASY-R 假说的一个要点,但它绝对是一个次要的要点。BA 是总安打数与每场击球数的比率;在计算这个指标时,单打、双打、三垒打和全垒打都被认为是命中。这使我们的分析变得混乱,我们的分析集中在去 1B 的容易程度上。击球手成为一个好的跑垒员可能是达到高击球率的一个组成部分,但在这方面,成为一个好的击球手是一个更重要的属性。

用于这种特定分析的更好的修正击球率度量将省略非单次击球。我们可以计算一个新的平均打击率,一个“单次平均”,这将是一名球员的总记录单打与该球员的非三振出局数(高飞球、滚地球、边线球和弹出球)的比率。然而,这个标准在描述击球手到达一垒的能力方面仍然有问题。一个球员的单打平均包括所有记录的单打,这意味着线驱动器和飞球被计算在内。同样,总输出数包括弹出型输出和直线驱动输出。包含这些类型的安打/出局的问题是击球手一开始没有机会被出局。如果击球手不能被扔出,那么这个标准就不能准确描述击球手到达 1B 的效率。一个更好的衡量标准是一名球员的地面击球与地面事件的比率(击球+出局)或 H/(H+O)G。

如果你看过一两场棒球比赛,你可能在 1B 看到过击球手击出快速滚地球后的千钧一发。在这些情况下,在跑垒上处于劣势的球员应该比他们的优势对手更经常被淘汰出局;优势跑步者应该获得更高的 H/(H+O)G 数。因此,我计算了每个玩家的 H/(H+O)G,并将结果绘制在图 3 中。同样,右手击球手的表现似乎比左手击球手略好,相差约 5%。EASY-R 机组的另一点。

图 3。各类别地面事件总数中地面命中率的分布;右手击球手的表现似乎比左手击球手好 5%。作者图片

到目前为止,显示的数据表明,RhB 平均比 lhb 更容易上垒。然而,右撇子击球手是否有跑动优势或者只是更好的击球手还不清楚。我查看了击球手的跑步指标,找出了真相。

我使用的具体指标是跑步者速度(RS)和跑步者从本垒到 1B (HP-1B)的平均时间。由于速度/速率和位移/距离之间的关系,您会期望这些度量具有某种线性关系。这正是图 4 中观察到的情况,因为来自所有三个类别的数据显示了跑步者的速度和到达 1B 的时间之间的反比关系。这是有道理的——你走得越快,就能越快到达目的地。虽然这个结果是意料之中的,但仍然有很多信息需要从这些数据中解开。

首先,让我们看看对每个类别的数据运行线性回归的结果。右手击球员线和左手击球员线的 R = 0.85,显示出时间和运动员速度之间相对较强的关系。左手线的 R = 0.77,关系明显较弱。左手边的线具有较低的 R 值有一个很好的解释,但我们很快会回到这个问题上。所有三条线都具有相对相似的斜率(L = -0.1345,R = -0.1341,S = -0.140),并且具有 L=8.04,R=8.14 和 S=8.2 的 y 截距。为了解释这些数字,我们必须理解描述数据的线性函数的含义。

我们来谈谈 y 轴截距。我们知道我们的 y 变量是以秒为单位测量的,这意味着我们的 y 截距也是如此。在这种情况下,y 轴截距是一个物体在无摩擦真空中以给定速度从本垒板行进 90 英尺左右所需的理想时间与跑步者到达那里所需的实际观察时间之差;我们称之为“时间成本”让我们假设,从空气动力学的角度来说,玩家相对相似(一个令人震惊的提议)。如果我们的假设是正确的,lhb 和 RhB 之间的时间成本差异可能源于它们必须运行的实际距离的微小差异(图 1。) .

坡呢?同样,我们知道 y 变量以秒为单位,x 变量以英尺/秒为单位。如果我们想把后者转换成前者,我们需要把 x 变量乘以单位秒/英尺。这个斜率单位是我们用来测量加速度的单位(英尺/秒)的倒数,这意味着计算出的斜率代表“加速度倒数”在这种情况下,斜率越负意味着所代表的组加速越慢。这里的解释表明,平均而言,lhb 和 RhB 的加速速率相同。切换击球手的加速速度比 L 和 R 的稍低。

图 4。HP 到 1B 时间对跑步者速度;作者图片

数据显示加速度/斜率相对相同,左撇子和右撇子有 0.1 秒左右的时间成本差异。这实际上意味着,一般来说,一个 LHB 人会比一个右撇子以同样的速度提前十分之一秒到达 1B。然而,这只是一般情况。数据还显示,一些左撇子可以在相同的速度下比右撇子多花将近半秒的时间!在 90 英尺的冲刺中,这是一个很大的优势。称之为 EASY-L 的大满贯!

这怎么可能呢?回想一下,回归的左撇子曲线的 R 值明显比右撇子或转换击球手曲线的 R 值差。从视觉上看,这是有意义的,因为对于给定的 x 轴值,y 轴值有更大的分布。这种蔓延的原因是什么?投机时间!我把对回归线的偏离解释为击球手转身效率的一种衡量。回想一下,lhb 必须转过一个大角度来面对 1B。我的猜测是,一些击球手能够比其他人更好地转过这个更大的角度,并因此获得甜蜜的时间奖励。我假设转身的能力,以及延伸的时间奖励,可能与玩家的体能有关系,并绘制了每个玩家的时间差与身体质量指数,以测试这一点(图。5).用于计算身体质量指数的体重和身高数据取自查德威克棒球局数据库[3]。

图 5。时差对身体质量指数;时间差,通过计算球员观察到的 HP-1B 时间与该球员的击球组对应的最佳拟合线值之间的差异来计算。作者图片

不幸的是,对我来说,没有这样的关系。事后看来,这并不十分令人惊讶,因为学术界对身体质量指数作为健康指标的有效性存在明显的分歧。这就是说,有趣的是,根据疾病预防控制中心[4],MLB 的大多数球员都超重(身体质量指数> 25 岁)。这里还有更多的分析要做,但是为了我们两个,让我们结束吧。这是 tl。博士带回家的消息。

酱外带

  1. 平均来说,左撇子击球手在到达 1B 时比右撇子多 0.1 秒的优势。轻松赢了。
  2. 根据玩家的转身效率,这个优势可能会增加到大约 0.5 秒。
  3. 2020 年,惯用右手的击球手比惯用左手的击球手获得更好的奖励。
  4. 额外收获:根据疾病预防控制中心的说法,大多数被抽样的球员都超重,理由是身体质量指数高[5]。

如果你做到了这一步,我希望你喜欢我的第一篇博客/文章。我用于数据分析和图形生成的所有代码和数据都可以在我的 github [6]中找到。感谢阅读!

-乔丹

参考文献:

[1]https://baseballsavant.mlb.com/leaderboard/

https://baseballsavant.mlb.com/visuals/batter-positioning

[3]https://raw . githubusercontent . com/chadwickbureau/baseball databank/master/core/people . CSV

[4]https://www . medical news today . com/articles/265215 #腰围与糖尿病风险相关,不考虑身体质量指数

[5]https://www . CDC . gov/healthy weight/assessing/BMI/adult _ BMI/index . html # interpreted adults

[6]https://github.com/Jordan-M-Young?tab=repositories

将 ML 模型安全地推广到生产中

原文:https://towardsdatascience.com/safely-rolling-out-ml-models-to-production-13e0b8211a2f?source=collection_archive---------16-----------------------

理解大数据

轻松部署机器学习模型和版本的最佳 CI/CD 实践

指挥不同的(非 ML)乐器。资料来源:联合国人类住区规划署

对于任何数据科学家来说,将模型的新版本投入生产的那一天都是百感交集的一天。一方面,你们正在发布一个新的版本,这个版本旨在产生更好的结果和更大的影响;另一方面,这是一个相当可怕和紧张的时刻。你新的闪亮版本可能包含一些缺陷,只有在它们产生负面影响后,你才能发现这些缺陷。

ML 就像指挥不同的乐器一样复杂

将一个新版本替换或发布到生产环境会触及到业务流程的核心决策逻辑。随着人工智能采用率的上升,自动发布和更新模型的必要性正在成为一项常见和频繁的任务,这使得它成为数据科学团队的首要关注问题。

在这篇文章中,我将回顾是什么使新版本的推出如此敏感,需要什么预防措施,以及如何利用监控来优化您的持续集成(CI)管道,以及您的持续部署(CD)管道,以安全地实现您的目标。

ML 系统由软件的多个移动且独立的部分组成,这些部分需要相互协调工作:

“ML 管弦乐队”。作者图片

培训管道:包括利用您的历史数据集生成工作模型的所有处理步骤:数据预处理,如嵌入、缩放、特征工程、特征选择或降维、超参数调整,以及使用交叉验证或保留集的性能评估。

  • 模型注册中心:一个部署的模型可以采取多种形式:特定的对象序列化,比如 pickle,或者跨技术序列化格式,比如 PMML。通常,这些文件保存在基于注册表的共享文件存储中(S3、GCS 等),保存在标准版本存储库(git)或专用服务中,如 MLFlow 模型注册表
  • 服务层:这是实际的预测服务。这些层可以与依赖于模型预测的应用程序的业务逻辑嵌入在一起,也可以分离出来充当与其支持的业务流程分离的预测服务。在这两种情况下,核心功能都是使用模型注册中心的模型,根据新的输入请求检索相关的预测。这种服务可以以批处理或流的方式工作。
  • 标签收集:为监督学习案例收集基础事实的过程。可以手动、自动或使用主动学习等混合方法来完成。
  • 监控:一个集中的服务,监控整个过程和你的活体模型的健康状况,从进入服务的输入的质量,到检测漂移、偏差或完整性问题的收集标签。

鉴于这种相对高级和复杂的编排,许多事情可能会失去同步,并导致我们部署一个性能不佳的模型。一些最常见的罪魁祸首是:

1.缺乏自动化

大多数组织仍然手动更新他们的模型。无论是培训管道还是它的一部分,例如特征选择,或者服务层对新创建的模型的交付和推广;手动这样做可能会导致错误和不必要的开销。

2.多个利益相关方

由于涉及到多个利益相关者和专家,会有更多的移交,因此会有更多的误解和整合问题的空间。当数据科学家设计流程时,ML 工程师通常进行编码-并且没有完全一致(例如,使用不同的缩放方法),这可能导致无意的行为。

3.真实生活数据的超动态性

实证研究是在离线实验室环境中进行的,使用历史数据集,并通过模拟举行测试。不用说,这些环境与生产环境非常不同,对于生产环境,实际上只有部分数据可用。这可能会导致数据泄漏或错误的假设,从而在模型投入生产并需要为实时数据流提供服务时,导致糟糕的性能、偏差或有问题的代码行为。例如,新部署的版本无法处理分类特征中的新值,同时在推理阶段将其预处理为嵌入向量。

4.ML 模型的无声故障与传统 IT 监控

监控 ML 比监控传统软件更复杂——整个系统在工作的事实并不意味着它实际上做了它应该做的事情。如果没有适当的专用 ml 监控服务,此类故障可能会“悄无声息地”消失,直到业务损失已经造成。你可以阅读更多关于如何监控 ml 模型是否应该自己建造

生产中 ML 的 CIֿֿֿ/CD

CI 实践是关于频繁地测试每个软件单元的代码基础,以及通过使用单元/集成/系统测试一起工作的不同软件工件的完整性。

新的模型版本应被视为软件工件

新模型的创建需要一组单元和集成测试,以确保新的“候选”模型在提交到模型注册中心之前是有效的。但是在 ML 领域,CI 不仅仅是关于测试和验证代码和组件,还包括测试和验证数据、数据模式和模型质量。虽然 CI 的焦点是维护有效的代码库和模块的工件,但是在为每个模块构建新的工件之前,CD 过程处理将工件(在我们的例子中,是新的模型版本)实际部署到生产中的阶段。

CI 阶段的最佳实践

以下是 CI 阶段的一些主要最佳实践,它们会影响模型/新版本实现的安全展示:

数据有效性

使用历史数据重新训练/生成模型。为了使模型与生产相关,训练数据集应该充分表示当前出现在生产中的数据分布。这将避免选择偏差或干脆不相关。为此,在开始训练管道之前,应该测试训练集的分布,以确保它适合任务。在这一阶段,可以利用监控解决方案提供关于最后生产案例分布的详细报告,通过使用数据测试工具,如 deequTDDA ,这种类型的数据验证约束可以自动添加到 CI 流程中。

使用监控服务从生产中提取数据分布,并将它们与您的训练数据集进行比较(图片来自 superwise.ai 系统)

模型质量验证

在执行培训管道时,在将新模型作为“候选”模型提交到注册表之前,请确保新的培训流程经过了健康适合度验证。

即使培训管道是完全自动化的,它也应该包括一个保留/交叉验证模型评估步骤。

给定所选的验证方法,应该应用测试来验证拟合的模型收敛不指示过度拟合,即:在训练数据集上看到减少的损失,而在验证集上看到增加的损失。性能还应该高于某个最低速率——基于硬编码的阈值、作为基线的原始模型,或者通过利用监控服务并在使用的验证集期间提取生产模型的速率来动态计算。

交叉验证评估设置示例。作者图片

测试案例—生产数据假设的稳健性

一旦模型质量验证阶段完成,就应该执行集成测试,以查看服务层如何与新版本集成,以及它是否成功地为特定边缘情况的预测服务。例如:处理可能为空的要素中的空值,处理分类要素中的新分类级别,处理不同长度的文本输入,甚至处理不同的图像大小/分辨率……这里的示例也可以手动合成或取自监控解决方案,其功能包括识别和保存存在数据完整性问题的有效输入。

模型压力测试

更改模型或更改其预处理步骤或包也会影响模型的操作性能。在许多用例中,比如实时竞价,增加模型服务的延迟可能会极大地影响业务。因此,作为模型 CI 流程的最后一步,应该执行压力测试来测量操作方面,如平均响应时间。这种度量可以相对于业务约束或者相对于由监控解决方案计算的当前生产操作模型等待时间来评估。

CD 阶段的最佳实践

CD 阶段是实际部署代码以替换先前版本的过程。在传统的软件工程中,这通常是通过“蓝绿色或“金丝雀”部署来实现的——一种逐步推出版本的方法。首先,变更被部署到案例的一个小的子集,通常逐渐扩展到服务器的一个子集。一旦确定功能运行良好,就可以将更改推广到其余的服务器。

将 CD 过程应用于 ML 模型,基本上应该是一个渐进的过程,在让它发挥作用并做出真正的自动化决策之前,使用生产系统验证实时数据的模型正确性和质量。这种评估通常被称为“在线评估”,与 CI 阶段完成的测试相反,CI 阶段完成的测试基于历史数据集,可被视为“离线评估”。

完整的 CI/CD 模型生命周期。作者图片

这种模型在线评估可能涉及一些基本策略:

影子评估

影子评估(或通常称为“黑暗发射”)是一种非常直观和安全的策略。在影子模式中,新模型作为“候选模型”添加到注册表中。任何新的预测请求都由模型版本(当前在生产中使用的版本)和影子模式中的新候选版本来推断。

在影子模式下,使用新的生产数据流测试新版本,但只有最新“生产模型”版本的预测被使用并返回给用户/企业。

有了影子模式,在新版本确实被批准并按预期工作之前,没有暴露的风险。影子模式的好处有两个:

  1. 新版本的性能在它“运行”之前被评估,
  2. 您确保新版本在生产管道中运行良好

使用这种方法,监控服务应该在两组预测中持续存在,并持续监控两个模型,直到新模型足够稳定,可以提升为新的“生产版本”。一旦新版本准备好了,监控服务就向服务层发信号通知它需要升级模型版本。从技术上讲,这种“提升”可以通过为新版本更新“最新”标签并使服务层始终与“最新”模型版本标签一起工作来完成(类似于 docker 图像中的“最新”概念)。或者通过使用显式模型版本标签,但是这将要求服务层总是首先检查什么是最新的生产就绪版本标签,然后才重新加载相关版本并使用它来执行预测。

监控服务负责决定何时提升影子模型。作者图片

我们应该使用统计假设将新的“影子”版本与当前最新的生产模型进行比较

这种测试应该验证新版本的性能具有所需的效果大小,也称为:在特定的统计功效下,两个版本性能指标之间的差异,也称为:当存在一个效果时,正确识别该效果的概率。例如:如果最新版本在过去一周的精度水平为 91%,则新的候选版本必须具有 0 或更大的效果大小,因此新的精度率将至少为 91%。

整体绩效水平不是唯一的衡量标准

不过,在提升模型之前,监控其他因素可能也很重要:性能稳定性,即确定我们上一个示例中每日精度性能的变化,或者检查特定子群体的性能约束。例如,对于我们的“VIP 客户”,该模型必须至少具有 95%的精确度。当标签收集需要时间或可能完全丢失时,可以使用其他 KPI 来测试新模型,作为其质量的代理,即:在贷款审批用例中,此类反馈循环可能需要 3-6 个月以上的时间。在这些情况下,可以使用两个版本的预测之间的相关水平,因为新版本相对于其前一版本应该具有相对较高的相关水平。另一种选择是测试生产数据相对于其训练数据集的分布偏移水平,以确保新版本是在相对于当前生产分布的相关数据集上训练的。

除了所有的好处之外,影子评估也有局限性,只是部分实用。这就是我们将在下面分析的内容,以及审查替代解决方案。

A/B 评估

在许多 ML 场景中,模型的预测实际上会影响它运行的周围环境。让我们以一个推荐模型为例,该模型负责排列要提供的前 5 个电影建议。在大多数情况下,标签取决于用户是否确实观看了建议的电影之一。在这里,模型的预测影响了用户的行为,从而影响了收集的数据。如果候选阴影模型输出 5 部非常不同的电影,用户没有选择它们的事实并不一定意味着它们是糟糕的推荐——只有模型处于阴影模式并且没有采取任何现实生活中的行动的事实使得用户不可能实际看到这些电影。在这种情况下,性能成功是无法比较的。这时需要进行在线 A/B 测试评估。

在 A /B 设置中,当前最新版本和新的候选版本(可称为模型 A/“控制”组和模型 B /“处理”组)实际上都在生产中活动。根据特定的逻辑,每个新请求被路由到其中一个模型,并且只有所选的模型用于预测。

A/B 路由示例。作者图片

这些评估可以进行一段时间,预测,或者直到统计假设——新版本是否至少和以前的版本一样好——被验证。与影子策略类似,要测试的 KPI 可以是性能、稳定性或一些其他代理,……但需要注意的是,监控服务应该负责管理和评估 A/B 测试。

这种方法的成功很大程度上取决于正确仔细地划分 A/B 测试组的能力。

一个好的做法是对一个特定的部分做一个随机的抽样来得到“治疗”,意思是从新的版本中预测。例如,对于电影推荐用例,任何新的请求应该有 X(例如 10%)的机会被新的模型版本服务,直到测试完成。通常,暴露系数相对较低,因为这种策略实际上甚至在定性测试完成之前就“暴露”了新模型。

这种 A/B 配置也可以推广到 A/B/C/…设置中,即同时比较多个模型。请记住,由于测试次数较多,同时测试几个模型会增加假阳性结果的几率。此外,考虑到我们在每组中测试的样本数量较少,因此“小效应大小”的统计功效会降低,因此应谨慎考虑。

多臂土匪

多臂土匪(MAB)实际上是一种更“动态”的 A/B 测试实验,但也是一种更复杂的测量和操作。这是一种经典的强化学习,人们试图“探索”(尝试新的模型版本),同时“利用”和优化已定义的绩效 KPI。这种配置是基于监控服务内部的探索/开发权衡的实现。根据收集的性能 KPI(或前面描述的其他代理),流量在两个(或更多)版本之间动态分配。

在这种情况下,新版本会遇到一定数量的案例。它越是等于或超过之前的版本,基于它的性能 KPI,越来越多的流量会被导向它。

基于监控结果的动态多臂 bandit 路由。作者图片

这样的配置看起来很理想,因为我们试图在优化主要指标的同时动态地优化“曝光度”。但是,它需要高级监控和自动化功能来实施多臂 bandit 解决方案,并根据其结果动态调整流量。特别是考虑到与系统要利用的一个明确的 KPI(性能、稳定性,...

监控安全部署的好处

通过查看传统软件中使用的示例,并将其实施到我们自己的 ML 范例,CI/CD 最佳实践中,我们了解到要有一个更好的“推出日”需要采取许多步骤。所有这些策略都依赖于强大的监控组件来使流程更加数据驱动。

安全地推出模型是为了监控 ML 基础设施的所有部分的协调

下面我们可以看到新版本的安全交付(V2,标记为蓝点),在生产中产生改进的 ROC AUC 性能。

superwise.ai 监控的模型截图

展望未来,您的模型在生产中的健康还需要一个全面的、数据驱动的再培训策略。虽然 CI/CD 范例解决了新模型推出的“什么”和“如何”问题,但 CT(持续培训)范例涵盖了“何时”问题。这将是我的同事或 Itzahary 下一篇帖子中的重点,敬请关注!

从 CI/CD 到 CT(持续培训)。CT 是一个新的属性,是 ML 系统独有的,它涉及自动重新训练和服务模型

我希望这篇文章对你有所帮助。如果你对本文的内容有任何问题、反馈或者想集思广益,请在这里发表评论或者通过 LinkedIn 联系我

我要感谢我的同事们珀尔·利伯曼或伊扎哈里奥里·科恩他们有见地的评论和想法

柳文欢·拉松 super wise . AI公司的联合创始人兼首席技术官,该公司让数据科学&运营团队能够利用先进的人工智能监控平台了解和控制他们的人工智能活动。柳文欢是一位经验丰富的 ML 从业者,在过去的 15 年里,他为初创公司和企业的大型 ML 活动提供领导和咨询。

我总是在找借口阅读、写作、谈论和分享关于 MLOps 主题的想法。

处理数据的安全注意事项

原文:https://towardsdatascience.com/safety-considerations-for-working-with-data-5442b23ce0af?source=collection_archive---------49-----------------------

(图片来源:pexels.com)

介绍

在处理数据项目时,科学家/分析师通常会关注许多不同的元素,因此很容易忘记安全性。然而,当安全方面被遗忘,它可能会适得其反,大时间。安全漏洞会导致许多不良后果,如数据丢失、云资源被劫持、法律问题、黑客攻击、客户离开等等。

这听起来有点戏剧性,但幸运的是,有一些非常简单的步骤可以实施,已经可以防止一系列的问题。由于我收到了很多关于这方面的问题,我决定把一些关键点放在一个帖子里。

一些考虑

始终使用双因素身份验证。

双因素身份认证(或 2FA)是一个简单的原则,其中您的凭证不限于“您知道的东西”(您的密码),而是扩展到“您是什么东西”(生物数据)和/或“您拥有的东西(电话/令牌/……)。

2FA 通常很容易实现,鉴于目前的技术(FaceID、智能手表等),它相当容易实现。实施 2FA 将极大地限制非法访问的风险,因为如果没有第二个因素,即使密码被盗,它也是无用的。如果您还没有为您最重要的客户(电子邮件、云存储等)制定 2FA。)我强烈建议你今天就去买一个。

不要在本地机器上存储数据

我明白了,在本地机器上存储数据既简单又快捷。但是,数据的所有者(客户、业务伙伴……)会不高兴您将数据存储在本地。IT 安全领域有这样一句话:“物理访问=游戏结束。”虽然现实中有更多的细微差别,但被盗或丢失的笔记本电脑/硬盘可能会带来严重的后果。因此,实施了适当安全措施的云存储是首选。

除了像 AWS 和 Azure 这样的主要平台之外,还有很多将数据存储在云上的可能性。

如果除了在本地存储数据之外别无选择,除了锁定计算机之外,您应该始终加密您的驱动器。

物理访问应该受到高度限制。(图片来源:pexels.com)

使用密码管理器

我经常惊讶于许多人仍然对密码管理器持怀疑态度,尽管它们通常是更安全的选择。密码管理器允许你存储数以千计的复杂密码,自动填充它们,并随时生成新的密码。

除了数据专业,使用密码管理器也大大提高了我的个人便利性。例如,现在你需要一个几乎任何网店的用户账户,我的个人经理只是简单地为这个网站建议一个困难的和唯一的密码,并会在我每次访问时填写。万一网站被黑了,我的信息被泄露了,这也没什么大不了的,因为密码只分配给那个网站访问。不要忘记添加 2FA 来访问您的密码管理器。

对您的数据库或笔记本电脑应用 IP 限制

这一简单的技巧阻止了数据被盗,黑客们恨他!

玩笑归玩笑,当在服务器上托管像 MySQL 或 Jupyter 笔记本这样的数据库时,你可以很容易地添加 IP 限制。这意味着数据库只能从某些地方访问,如你的家,办公室,客户等。这个非常简单但有效的措施对您的数据安全有很大的积极影响。尽管这不是万无一失的,但它是防止不必要的访问的非常简单和非常有效的方法。

不过要小心,大多数非专业的互联网计划都有一个动态的 IP。对动态 IP 应用 IP 限制可能会导致您自己被锁在外面。

注意动向

“以量为知”。跟踪通常是数据安全中被忽视的一个方面,但它的重要性不容忽视。我所说的跟踪是指:

  • 谁知道密码?
  • 最后一次更改密码是什么时候?
  • 谁有 IP 访问权限?
  • 记录登录(以及尝试)

跟踪不仅可以防止数据丢失(例如,限制不再需要数据的人访问数据),还可以帮助诊断数据丢失时的问题所在。

不要硬编码密码

我重复一遍,不要不要硬编码密码。一次也没有,不是短暂的,不是内部的,从来没有。尽管当您实现前面提到的步骤(即 2FA、IP 限制)时,硬编码的风险是有限的,但您很可能没有这样做,这很可能会适得其反。通常是这样的:

  1. 编码器硬编码数据库密码,因为它更容易。目前,没有风险,因为它只供内部使用,以后会被删除。
  2. 编码器忘记删除它。
  3. 代码最终会出现在一个私有的 Github 或一些 webscript 上
  4. 一只爬虫捡起它
  5. 混乱随之而来…

我很幸运自己没有经历过这些,但我听过很多故事。然而,硬编码密码有一个好处:你只需要做一次。因为一旦爬虫抓取了它并对您的数据库/云实例/web 服务器造成严重破坏,您就不太可能重复您的错误。

了解风险

最后一部分,我会稍微宽松一点。谈到安全,最重要的是了解风险。当然,理想情况下,您会实现所有这些措施以及更多措施,但这可能不值得付出努力。你需要区分一个简单的个人项目和一个有机密数据的大客户的脚本。风险应该与措施相匹配,高估风险比低估风险更好。

最后,你还需要理解数据和洞察力之间的区别。大部分时间(但不总是!)原始数据需要最高程度的保护,而见解可以较少限制地共享。我之所以强调这种差异,是因为非技术客户通常很难遵守您的安全标准。然而,如果这些见解不像数据本身那样具有同样的风险,你可以自己评估是否可以简单地通过电子邮件或其他渠道分享它们。一旦行被聚合,数据通常会失去其机密性。

结论

“数据是新的石油,”作为一名数据专业人员,您将经常负责存储和保管大量这种宝贵的石油。虽然承担这一责任听起来令人生畏,但有很多简单的方法可以降低这种风险。实施本帖中描述的部分或全部步骤,将使那些不应该访问你的数据的人很难访问你的数据。但是,请记住:

  • 没有任何措施是 100%有保证的。
  • 你的安全系统有多强就有多弱。

本文列出的考虑事项并不详尽。您可以(或许应该)实施许多其他措施,如加密、注入漏洞、哈希等等。

(图片来源:【https://xkcd.com/327/】T2)

当您在处理真正非常重要或保密的数据时,我建议您向认证 it 审计员咨询,他们可以指导您完成应该实施的步骤。

关于我:我叫 Bruno,是一名数据科学家,目前专攻电力 BI。可以通过我的网站与我联系:https://www . zhongtron . me

销售和数据科学:使用 Python 分析竞争对手的分步指南

原文:https://towardsdatascience.com/sales-data-science-a-step-by-step-guide-to-competitor-analysis-using-python-7dc03461d284?source=collection_archive---------18-----------------------

是什么让卖家在亚马逊或易贝这样的在线市场上获得成功?数据科学有助于回答这个问题。

像亚马逊或易贝这样的在线市场到处都是卖家,每一步都在互相竞争。当其他人提供的产品与你的非常相似时,增加销售并不容易。

你有没有想过,如果卖家提供的东西都差不多,是什么让买家选择一个卖家而不是另一个?

你可能会认为这都是价格的问题。

我也是,但后来我禁不住诱惑,想看看这里是否有其他因素在起作用。

在本文中,我站在一个在线卖家的角度,来看看使用 Python 编程工具的数据驱动方法如何帮助回答这个问题。

来源:我的团队在Sunscrapers提供。

为此,我采取了两个步骤。

第一步:找到一个有丰富销售历史内容的在线市场。这篇文章是为教育目的而写的,所以我想对市场名称保密。

第二步:想出一个可以比较不同报价的方法。这里的诀窍是找到一种产品,它有许多相似的变体,并由大量的卖家出售。我为这项研究挑选的在线市场上最常出售的产品之一是我们都在使用的:

一款智能手机屏幕保护器

在这篇文章中,我要问和回答 4 个问题:

Q1。我们产品类型的平均价格是多少?

Q2。定价如何影响销售?

Q3。最受欢迎的屏幕保护器品牌有哪些?

Q4。哪些品牌最赚钱?

贸易工具

python——我们为数据科学选择的编程语言。

Python 工具:

  • scrapy——这个 web 抓取/爬行框架提供了一些便利的特性,比如字段值的编组和预处理。你可以通过在线清理平台运行它,这有助于减轻你的抓取过程。
  • Pandas —我将使用它将数据加载到表中(然后清理、处理和分析它)。
  • Seaborn 和 Matplotlib——这是 Python 中一些方便的数据可视化库。

顺便说一句。我还写了两篇文章来帮助你研究熊猫,看看吧:

如何在熊猫小组中运用分-用-合策略

如何用熊猫和 Dask 在 Python 中处理大型数据集

数据挖掘和准备

1.获取数据

第一步是找到数据源。我需要选择一个能够提供某种销售业绩指标的在线市场——这样我就可以根据其他产品特性对其进行评估。我选的平台提供了最近 100 笔交易的信息。

注意:网络爬虫代码内容相当广泛,并且会因不同的市场网站而异,所以我决定不在本文中包含抓取代码的例子。用于抓取和网页抓取的 Python 框架 Scrapy 提供了大量文档和易于理解的教程,所以如果需要的话可以参考它们。

以下是我如何获得本文数据的简短描述:

首先,我在网上市场手动搜索智能手机屏幕保护器,并从那里开始爬行过程。通常,搜索模式从某种商品列表开始,每个列表指向一个包含更多信息的商品专用页面,甚至可能是过去购买的列表。

请注意,您通常可以在搜索结果列表中获得关于产品展示的有价值信息(如卖家状态、积分、过去购买次数)。毕竟,这是客户第一次接触该优惠,这可能会影响他们的决策过程。

爬行之后,我得到了两张熊猫桌子。主表(df)包含所有的产品信息,每一行对应一个商品。另一个表(sale_history)存储了关于过去购买的信息,每个产品包含许多行单独的销售事件数据。

稍后我将向您展示表格示例。

2.处理数据

在数据提取步骤之后,是时候做一些清理和数据准备了。除了所有通常的步骤(删除空值、将列转换成正确的数据类型等。),这里有几个有趣的步骤我想提一下——同样,不用深入细节。

作为第一步,我倾向于用熊猫unique()的方法浏览各个专栏。这就是我如何能看到价值观是否一致和明智——并捕捉任何潜在的问题。然后,我通过按列对行进行分组来检查数据重复,该列是特定项目的惟一标识符——在本例中,我使用了product_id

我注意到的第一件事是,一些产品页面链接到搜索结果页面中的多个列表(巧合?我不这么认为!).我去掉了重复的,但是决定保留这些信息用于分析。因此,我创建了一个新列,首先列出每件商品的列表数量,然后删除了副本,只留下一个:

df[‘same_offer_count’] = df.groupby(‘product_id’)[‘product_id’]\
    .transform(‘count’)
df = df.drop_duplicates(subset=’product_id’, keep=’first’)

另一个有趣的问题是处理市场上使用的多种货币。我的原始数据表包含裸价格字符串值以及带引号的货币符号(例如,“US $1.09”或“C $2.42”),因此我需要提取数值,并通过将它们转换为 USD 来统一所有价格货币。以下是转换前的几个示例行:

下面是我用来转换它的代码:

import refrom currency_converter import CurrencyConvertercc = CurrencyConverter()
currency_shortcuts = {‘C’:’CAD’, ‘US’:’USD’, ‘AU’:’AUD’} # first I checked only these occur…
regx_str=r’(\w+\s*)\$[ ]?(\d+[.|,]?\d+)’ # note the two ‘re’ groups!df[[‘currency’, ‘quoted_price’]] = df[‘current_price’]\
    .str.extract(pat=regx_str)df[‘currency’] = df[‘currency’].str.replace(‘ ‘, ‘’)
df[‘currency’] = df[‘currency’].map(currency_shortcuts)
df[‘price_USD’] = df[‘quoted_price’].copy()for currency in [ c for c in df[‘currency’].unique() 
                  if c not in [‘USD’]]:
    fltr = df[‘currency’].isin([currency]) 
    df.loc[fltr, ‘price_USD’] = df.loc[fltr, ‘quoted_price’]\
        .apply(lambda x: cc.convert(x, currency, ‘USD’))

这导致了:

接下来,我处理了销售历史表(sale_history)。我执行了一些基本的类型修复,提取并转换了价格和货币,并填充了空值(代码未显示)。我最终得到了这个表(同样,它只是行的快照):

为了让它对我的分析和绘图有用,我按日期(当然还有product_id)汇总了条目,并计算了售出商品的数量和每日销售率。将所有这些打包到一个函数中,可以将它按行应用于数据框:

def calculate_sale_history_stats(df):
    *“””Calculates statistics on sale history, 
    returns new dataframe”””* delta = df[‘purchase_date’].max() — df[‘purchase_date’].min()
    days = int(delta.days)
    values = list(df[‘quantity_sold’])
    earnings = list(df[‘total_price’])
    sold_count = sum(values)

    if len(values) < days:
        values.extend([0]*(len(values) — days))
        earnings.extend([0]*(len(earnings) — days))

    res = pd.Series(
        [ sold_count, np.mean(values), 
          np.std(values), np.mean(earnings), 
          np.std(earnings)
        ], 
        index=[‘Sold_count’, ‘Mean_daily_sold_count’,
               ‘Sold_count_St.Dev’, ‘Daily_earnings’,
               ‘Daily_earnings_St.Dev’]
    )

    return round(res, 2)

并将其应用于sale_history数据帧:

sale_history_stats = sale_history.groupby(‘brand’)\
    .apply(calculate_sale_history_stats)

导致了:

最后,我将汇总的销售统计数据(sale_history_stats)合并到主 df 表中:

df = pd.merge(
    how=’left’,
    on=’product_id’,
    left=aggreg_sale_history,
    right=df[[‘product_id’,’shipping_cost’, ‘shipping_from’,
              ‘top_rating_badge’, ‘seller_feedback_score’, 
              ‘seller_feedback_perc’,]]
)

下面是生成的df表(同样,只显示了选择的列):

现在我们可以走了。因此,让我们开始我们的竞争对手分析。

Q1:我们这种产品的平均价格是多少?

让我们看看我们能从屏幕保护装置中获得多少利润。这类产品卖家一般收费多少?

我可以分析市场上的价格,看看客户通常会为这样的智能手机屏幕保护器支付多少钱:

import matplotlib.pyplot as plt
import seaborn as snsprices = df[‘price_USD’]sns.distplot(df[‘price_USD’], ax=ax, bins=200)paid_prices = sale_history[‘sell_price’]sns.distplot(paid_prices, ax=ax, bins=100)

下面是两个叠加了附加信息的直方图(代码未显示):

如你所见,大多数屏幕保护器的价格约为 1.15 美元(平均约为 3.9 美元)。然而,顾客似乎更喜欢在购买时额外投入一些钱(平均约 5 美元,中位数约 3.8 美元)。“越便宜越好”的规则在这里不适用。

基于这种认识,我们可以假设选择将我们的产品定价在 4 美元左右就可以了。

Q2:定价如何影响销售?

定价可能是客户决策过程中最重要的因素。销售者通常认为高价格会阻止消费者购买他们的产品。在盈利性和可承受性之间取得恰当的平衡可能会成为一项挑战。

让我们看看售出商品的数量和每日收入如何与单价相匹配(作为每日平均值):

*# The daily earnings vs price:*
sns.lmplot(
    x=’sell_price’, 
    y=’Daily_earnings’, 
    data=df[[‘sell_price’, ‘Daily_earnings’]]
)*# Plot the sales frequency vs price:*
sns.lmplot(
    x=’sell_price’, 
    y=’Mean_daily_sold_count’, 
    data=df[[‘sell_price’, ‘Mean_daily_sold_count’]]
)

不出所料,更高的价格意味着平均销量的下降。但是当我们看每天的收入时,似乎利润倾向于随着更高的价格而增加。

在这一点上,发现定价是否反映了产品的质量和/或声誉是很有意思的。不幸的是,这超出了本研究的范围。

Q3。最受欢迎的屏幕保护器品牌有哪些?

让我们仔细看看品牌名称。有一系列的价值观指向“没有品牌”

所以,让我们先把这些乱七八糟的东西收拾干净,给它们都贴上“无品牌”的标签:

ubranded_values = [ 
    ‘Does not apply’, 
    ‘Does Not Apply’, 
    ‘Unbranded/Generic’, 
    ‘unbranded’
]df[‘brand’] = df[‘brand’].apply(
    lambda s: ‘Unbranded’ if s in ubranded_values else s
)

现在,我可以将数据输入到工作中,并得到一个显示品牌名称的饼图。这表明我们在线市场上提供的大多数产品(约 60%)根本没有品牌(或没有标明品牌)。

消费者可能希望坚持一个可识别的名称,所以让我们暂时忽略未命名的产品,而是专注于市场上每天卖出最多产品的前 20 个品牌。

为此,我将使用包含所有事务数据的sale_history表。

让我们创建一个包含市场上提供的品牌信息的表格:

sold_brands = sale_history\
    .groupby(‘brand’)\
    .apply(calculate_sale_history_stats)

接下来,让我们来看看迄今为止销量最高的 10 个品牌——创建一个表格并绘制成这样:

top_sold_brands = sold_brands.sort_values(
    by=[‘Sold_count’, ‘Daily_earnings’, ‘Mean_daily_sold_count’], 
    ascending=False
).reset_index()sns.barplot(
    data=top_sold_brands.iloc[1:21], 
    x=’brand’, 
    y=’Sold_count’)

一眼就足以看出,所有未命名的品牌加起来已经累积了最高的销售数量。然而,Spigen 似乎是这一类别的亚军,并在命名品牌产品中占据市场主导地位。

Q4。哪些品牌最赚钱?

哪些屏幕保护器品牌在最短的时间内给卖家带来了最高的收益?让我们回到无品牌产品的话题上来,因为当谈到收益时,情况可能会略有不同:

most_profitable_brands = sold_brands.sort_values(
    by=[‘Daily_earnings’, ‘Mean_daily_sold_count’, ‘Sold_count’],
    ascending=False
).reset_index()most_profitable_brands = most_profitable_brands[[
    ‘brand’, ‘Daily_earnings’, 
    ‘Daily_earnings_St.Dev’,’Sold_count’,
    ‘Mean_daily_sold_count’, ‘Mean_Sold_count_St.Dev’]]

我得到了这张表:

让我们把它形象地画在柱状图上,就像这样:

plt.bar(x, y, width=0.85, yerr=y_err, 
        alpha=0.7, color=’darkgrey’,  
        ecolor=’black’
)

这应该会创建以下图形:

很明显,没有品牌的产品利润并不高。请注意,当我们比较收入而不是销售商品总数时,品牌的顺序发生了很大变化。中档价位的品牌现在排在榜首(比如售价约 9.5 美元的 PureGear)。尽管他们的日销售率相对较低(每天 1-2 英镑)。

回答这四个问题向我们展示了“质量重于数量”可能是制定在线市场销售策略的最聪明的方法。

在本文中,我将重点带您完成数据挖掘和准备过程,最终回答我在本次研究中选择的关于在线市场销售趋势的四个关键问题。

以下是我可以从数据中回答的一些其他问题:

  • 运输成本会影响客户决策吗?
  • 有多少卖家对自己的产品打折?
  • 打折会带来更高的销售额吗?
  • 顾客从哪里购买产品会有什么不同吗?
  • 卖家反馈分数如何影响销量?
  • 拥有最高评级的徽章会促进产品销售吗?

幸运的是,我已经做到了。你可以在我的研究中找到所有答案,如何用数据科学增加在线市场的销售额。在这里免费下载

原载于 2020 年 3 月 16 日 https://sunscrapers.com。****

销售预测:从传统时间序列到现代深度学习

原文:https://towardsdatascience.com/sales-forecasting-from-time-series-to-deep-learning-5d115514bfac?source=collection_archive---------1-----------------------

图片由我可爱的女朋友 Beatriz Belbut 提供

从传统时间序列模型到现代深度学习,直观地进行销售预测。

介绍

在任何一家公司,都有一种预测未来收入和未来销售的内在愿望。基本配方是:

收集与以前销售相关的历史数据,并使用它来预测预期销售。

照片由马库斯·斯皮斯克Unsplash 拍摄

在过去的十年里,深度学习作为所有可想象的机器学习基准背后的驱动力的兴起彻底改变了这个领域:无论是在计算机视觉、语言还是其他许多领域。最近,有人可能会说,深度学习通过允许模型在单个模型中编码多个时间序列以及解释分类变量,重构了销售预测的潜在未来。我今天的目标是:

从时间序列的角度带您了解销售预测的主要概念和模型背后的基本直觉,并讨论最近的深度学习模型可以带来什么样的功能。

阅读建议

如果你觉得你需要温习销售预测和时间序列的基础知识,我推荐这三本书:

销售预测问题

销售预测就是利用历史数据为决策提供信息。

一个简单的预测周期如下所示:

作者图片

就其核心而言,这是一个[时间序列](https://en.wikipedia.org/wiki/Time_series#:~:text=A time series is a,sequence of discrete-time data.)问题:给定一些时间上的数据,我们希望预测这些数据在未来的动态。为了做到这一点,我们需要一些可训练的动态模型。

根据亚马逊的时间序列预测原则,预测是一个难题,原因有二:

  • 合并大量历史数据,这可能会导致丢失有关目标数据动态过去的重要信息。
  • 整合相关但独立的数据(假期/事件、位置、营销推广)

除此之外,销售预测的一个重要方面是准确性非常关键:

  • 如果预测过高,可能会导致过度投资,从而亏损。
  • 如果预测过低,可能会导致投资不足,从而失去机会。

纳入天气、时间和空间位置等外部因素可能有利于预测。在柳德米拉·塔拉年科的这篇中型文章中,她提到了一个很好的例子,讨论了优步、Lyft 或滴滴出行等按需乘车服务如何必须考虑天气条件(如湿度和温度)、一天中的时间或一周中的某一天等因素来进行需求预测。因此,好的预测模型应该具有能够解释这些因素的机制。

总而言之,到目前为止我们知道些什么?

  • 我们知道预测是一个难题,准确性非常重要。
  • 我们知道有一些难以解释的外部因素在起作用。

我们还不知道的是:

  • 什么是传统的预测方法,为什么他们可能会屈服于这些挑战。
  • 深度学习方法如何有所帮助,以及取代传统模型的前景如何。

预测模型的类型

根据《哈佛商业评论》的这篇文章,有三种预测技巧:

  • 定性技术:通常涉及专家意见或关于特殊事件的信息。
  • 时间序列分析和预测:涉及历史数据,在数据的动态中寻找结构,如周期模式、趋势和增长率。
  • 因果模型:这些模型涉及相关的因果关系,可能包括库存或市场调查信息等渠道因素。它们可以包含时间序列分析的结果。

我们将重点介绍时间序列分析方法,这是传统预测方法背后的驱动力,它可以给出预测前景的综合布局。

时间序列方法

时间序列是在连续的、等距的时间点获取的数据点序列,可用于预测未来。时间序列分析模型涉及使用历史数据来预测未来。它在数据集中查找趋势、周期性波动、季节性和行为模式等特征。

在处理从时间序列角度解决的销售预测问题时,需要考虑的三个基本要点是:

  • 重复模式
  • 静态模式
  • 趋势

现在,我们将研究这些因素中的每一个,并编写代码让我们直观地理解它们。在那之后,我们将看到现代深度学习模型可以带来什么。

重复模式

当查看时间序列数据时,我们寻找的一个元素是在时间上重复的模式。与这个想法相关的一个关键概念是 [自相关](https://en.wikipedia.org/wiki/Autocorrelation#:~:text=Autocorrelation, also known as serial,the time lag between them.) 。

直观地说,自相关对应于观测值之间的相似性,作为它们之间时滞的函数。

那是什么意思?它指的是通过查看不同时间点的观测值之间的相关性(即“自动”)来发现时间序列中观测值动态的结构。这是寻找重复模式的主要工具之一。

为了澄清这一点,让我们来看看 kaggle 公开发布的天气数据集,并绘制其原始温度数据和自相关图。

这些步骤将是:

  • 加载我们的数据集
  • 清除日期列
  • 绘制原始天气数据
  • 使用statsmodels绘制自相关图
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
from statsmodels.graphics.tsaplots import plot_acf

green = sns.color_palette("deep", 8)[2]
blue = sns.color_palette("deep", 8)[0]

# Loading the dataset
df_weather = pd.read_csv("data/weatherHistory.csv")

#Cleaning the dates column
df_weather['Formatted Date'] = pd.to_datetime(df_weather['Formatted Date'])

#Plotting the raw weather data
fig = plt.figure(figsize=(17,8))
ax1 = fig.add_subplot(121)

plt.scatter(df_weather["Formatted Date"],df_weather["Temperature (C)"], color=green,s=20)
plt.title("Weather Data Time Series",fontsize=15)
plt.xlabel("Date",fontsize=15)
plt.ylabel("Temperature (ºC)",fontsize=15)

# Plotting the autocorrelation plot
ax2 = fig.add_subplot(122)
plot_acf(df_weather["Temperature (C)"], ax=ax2,color=blue)
plt.title("Autocorrelation Plot for Weather Data", fontsize=15)
plt.ylabel("Correlation",fontsize=15)
plt.xlabel("Lag",fontsize=15)
plt.show()

我们可以清楚地看到左边有一个重复的图案,似乎是正弦曲线形状。在右边,我们可以看到自相关图:线条的大小表示给定滞后值的相关程度。当我们考虑天气的季节性和重复性时,该图似乎表明了一种相关的循环模式。

然而,我们能从销售数据集的自相关图中得到什么呢?它会呈现出和这个简单的天气数据集一样清晰的重复模式吗?

让我们用这个零售销售数据集绘制与上面相同的信息。

这里的步骤将是:

  • 加载数据集
  • 获得 45 家商店的总销售额
  • 绘制 2010 年至 2013 年间的总销售量
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
from statsmodels.graphics.tsaplots import plot_acf
import matplotlib.dates as mdates
import datetimegreen = sns.color_palette("deep",8)[2]
blue = sns.color_palette("deep",8)[0]

retail_sales = "./sales_dataset.csv' df_sales  = pd.read_csv(retail_sales)fig = plt.figure(figsize=(17,8))
ax1 = fig.add_subplot(121)
df_sales_sum = df_sales.groupby(by=['Date'], as_index=False)['Weekly_Sales'].sum()
df_sales_sum["Date"] = pd.to_datetime(df_sales_sum["Date"])
df_sales_sum.plot(x="Date",y="Weekly_Sales",color="g",ax=ax1, fontsize=15)
plt.xlabel("Date",fontsize=15)
plt.title("Total Sales Volume", fontsize=15)
plt.ylabel("Sales", fontsize=15)
date_form = mdates.DateFormatter("%Y-%m-%d")
year_locator = mdates.YearLocator()
ax1.xaxis.set_major_locator(year_locator)ax2 = fig.add_subplot(122)
plot_acf(df_sales_sum.Weekly_Sales,ax=ax2)
plt.title("Autocorrelation", fontsize=15)
plt.xlabel("Lag",fontsize=15)
plt.ylabel("Correlation", fontsize=15)
plt.show()

在这里,我们看到在lag = 5的观察中有一个相对高相关性的点。我们在前面的图表中看到的相同结构的缺乏是销售的偶然性的结果:考虑到预测销售的因素的数量,我们不应该期望数据像在天气数据集中那样具有完全清晰的相关性。然而,有趣的是观察到可能与所涉及产品类型相关的因素相关的相关性峰值。例如,对于一个销售圣诞礼物的商店,我们应该期望看到从圣诞节开始相隔一年的观察结果之间的高相关性,因为人们更有可能在这个特定时期购买更多的礼物。

静态模式

顾名思义,静态模式的概念与不变的事物相关。

在时间序列中,这个概念最著名的代理是,平稳性是指一个时间序列保持静态的统计性质:一个平稳时间序列中的观测值不依赖于时间。

趋势和季节性会影响时间序列在不同时间的值。传统上,我们会随着时间的推移寻找一致性,例如通过使用观察值的平均值或方差。当时间序列是平稳的时,建模会更容易,统计建模方法通常假设或要求时间序列是平稳的。

如果你想更深入地了解平稳性,我推荐 Shay Palachy 的这篇作品

检查数据集是否稳定的标准过程包括使用一种称为 Dickey-Fuller 测试的测试,该测试检查数据是否具有静态统计属性的置信度。要了解更多细节,请查看这篇文章

from statsmodels.tsa.stattools import adfuller

adf_test_sales = adfuller(list(df_sales_sum["Weekly_Sales"]))

adf_test_weather = adfuller(list(df_weather["Temperature (C)"]))

print("Weather Results:")
print("ADF = " + str(adf_test_weather[0]))
print("p-value = " +str(adf_test_weather[1]))

print("Retail sales results:")

print("ADF = " + str(adf_test_sales[0]))

print("p-value = " +str(adf_test_sales[1]))Weather Results:
ADF = -10.140083406906376
p-value = 8.46571984130497e-18
Retail sales results:
ADF = -2.6558148827720887
p-value = 0.08200123056783876

在这里,我们可以看到天气数据集的测试结果指向平稳,这是一个我们应该持保留态度的结果,因为它在很大程度上取决于我们如何对数据进行采样,通常气候数据是循环平稳的。然而,在我们的零售销售数据集上,p 值表明数据将是稳定的,这种置信度并不显著。

趋势

趋势代表在我们的数据中识别的趋势。在股票市场的情况下,这可能是给定股票的趋势,看起来是上涨或下跌。对于销售预测来说,这是关键: 确定一个趋势可以让我们知道我们的时间序列前进的方向,这是预测销售未来的基础。

我们将使用[fbprophet](https://facebook.github.io/prophet/docs/quick_start.html)包来确定我们两个数据集的总体趋势。这些步骤将是:

  • 选择天气数据的范围(在 2007 年和 2009 年之间)
  • 将数据作为具有两列的 dataframe 提供给fbprophet.Prophet对象:“ds”(表示日期)和“y”(表示数据)
  • 运行模型
  • 用上限和下限绘制趋势图
from fbprophet import Prophet
from datetime import datetime

start_date = "2007-01-01"
end_date = "2008-12-31"
df_weather["Formatted Date"] = pd.to_datetime(df_weather["Formatted Date"], utc=True)

date_range = (df_weather["Formatted Date"] > start_date) & (df_weather["Formatted Date"] < end_date)

df_prophet = df_weather.loc[date_range]

m = Prophet()

ds = df_prophet["Formatted Date"].dt.tz_localize(None)
y = df_prophet["Temperature (C)"]
df_for_prophet = pd.DataFrame(dict(ds=ds,y=y))
m.fit(df_for_prophet)

future = m.make_future_dataframe(periods=120)

forecast = m.predict(future)
forecast = forecast[["ds","trend", "trend_lower", "trend_upper"]]
fig = m.plot_components(forecast,plot_cap=False)
trend_ax = fig.axes[0]

trend_ax.plot()
plt.title("Trend for Weather Data", fontsize=15)
plt.xlabel("Date", fontsize=15)
plt.ylabel("Weather Trend", fontsize=15)
plt.show()INFO:fbprophet:Disabling yearly seasonality. Run prophet with yearly_seasonality=True to override this.
C:\Users\lucas\.conda\envs\env_1\lib\site-packages\pystan\misc.py:399: FutureWarning:

Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.

我们可以看到,对于天气来说,趋势正如我们所预期的那样遵循常规季节,在夏季上升,在冬季下降。

现在,让我们对零售数据集做同样的事情。这些步骤与上面的图类似,唯一的区别是我们将从零售数据集中选择一家商店。

from fbprophet import Prophet

m = Prophet()
# Selecting one store
df_store_1 = df_sales[df_sales["Store"]==1]

df_store_1["Date"] = pd.to_datetime(df_store_1["Date"])
ds = df_store_1["Date"].dt.tz_localize(None)
y = df_store_1["Weekly_Sales"]
df_for_prophet = pd.DataFrame(dict(ds=ds,y=y))
m.fit(df_for_prophet)
future = m.make_future_dataframe(periods=15)
forecast = m.predict(future)
forecast = forecast[["ds","trend", "trend_lower", "trend_upper"]]
fig = m.plot_components(forecast,plot_cap=False)
trend_ax = fig.axes[0]
trend_ax.plot()
plt.title("Trend for Retail Data", fontsize=15)
plt.xlabel("Date", fontsize=15)
plt.ylabel("Sales Trend", fontsize=15)

plt.show()C:\Users\lucas\.conda\envs\env_1\lib\site-packages\ipykernel_launcher.py:8: SettingWithCopyWarning:

A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

INFO:fbprophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.
C:\Users\lucas\.conda\envs\env_1\lib\site-packages\pystan\misc.py:399: FutureWarning:

Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.

从 2010 年到 2013 年,所选商店的销售业绩呈现出近乎完美的线性上升趋势,总销量增幅超过 1%。对这些结果的实际解释需要其他指标,如流失率和潜在的成本增加,因此上升趋势不一定意味着利润增加。然而,一旦考虑了所有因素,趋势是整体性能的良好指标。

传统时间序列模型在销售预测中的应用

到目前为止,我们已经介绍了销售预测问题的基本知识,并从时间序列的角度确定了它的主要组成部分:重复模式、静态模式和趋势的概念。如果你想更深入地了解时间序列,我推荐@Will Koehrsen 的这篇文章。

现在,我们将研究处理销售预测问题的传统时间序列方法:

  • 移动平均数
  • 指数平滑法
  • ARIMA

移动平均数

该模型假设下一个观察值是所有过去观察值的平均值,它可用于识别数据中有趣的趋势。我们可以定义一个窗口来应用移动平均模型平滑时间序列,并突出显示不同的趋势。

让我们用移动平均线模型来预测天气和销售。这些步骤将是:

  • 选择一个范围
  • 定义移动平均窗口的值
  • 计算平均绝对误差
  • 画出滚动平均值的上下界
  • 绘制真实数据
from sklearn.metrics import mean_absolute_error

green = sns.color_palette("deep", 8)[2]
blue = sns.color_palette("deep", 8)[0]

start_date = "2007-01-01"
end_date = "2008-12-31"
df_weather["Formatted Date"] = pd.to_datetime(df_weather["Formatted Date"], utc=True)
date_range = (df_weather["Formatted Date"] > start_date) & (df_weather["Formatted Date"] < end_date)
df_weather_ma = df_weather.loc[date_range]
series = df_weather_ma["Temperature (C)"]

window=90
rolling_mean = series.rolling(window=window).mean()
fig,ax = plt.subplots(figsize=(17,8))

plt.title('Moving Average Model for Weather Dataset',fontsize=15)
plt.plot(rolling_mean, color=green, label='Rolling mean trend')
#Plot confidence intervals for smoothed values
mae = mean_absolute_error(series[window:], rolling_mean[window:])
deviation = np.std(series[window:] - rolling_mean[window:])
lower_bound = rolling_mean - (mae + 2 * deviation)
upper_bound = rolling_mean + (mae + 2 * deviation)
plt.plot(upper_bound, 'r--', label='Upper bound / Lower bound')
plt.plot(lower_bound, 'r--')
plt.plot(series[window:],color=blue, label="Actual Values")

plt.legend(loc='best')
plt.grid(True)
plt.xticks([])

plt.show()

这个模型似乎捕捉到了一些天气的动态变化。让我们看看该模型如何处理零售数据集。

series = df_sales_sum.Weekly_Sales
window=15

rolling_mean = series.rolling(window=window).mean()
fig,ax = plt.subplots(figsize=(17,8))

plt.title('Moving Average Model for Retail Sales',fontsize=15)

plt.plot(rolling_mean, color=green, label='Rolling mean trend')

#Plot confidence intervals for smoothed values
mae = mean_absolute_error(series[window:], rolling_mean[window:])
deviation = np.std(series[window:] - rolling_mean[window:])
lower_bound = rolling_mean - (mae + 1.92 * deviation)
upper_bound = rolling_mean + (mae + 1.92 * deviation)

plt.plot(upper_bound, 'r--', label='Upper bound / Lower bound')
plt.plot(lower_bound, 'r--')

plt.plot(series[window:], color=blue,label='Actual values')

plt.legend(loc='best')
plt.grid(True)
plt.xticks([])
plt.show()

对于销售数据集,拟合看起来没有那么有希望,但是与天气数据集相比,零售数据集的数据也少得多。此外,设置我们的平均大小的窗口参数对我们的整体性能有很大的影响,我没有做任何额外的超参数调整。在这里,我们应该明白的是,复杂的销售数据集需要比简单的一维时间序列所能提供的更多的信息。

指数平滑法

指数平滑类似于移动平均,但在这种情况下,每个观察值的权重会递减,因此,随着我们远离当前,观察值的重要性会降低。这种假设有好有坏:降低时间序列动态中过时信息的权重可能是有益的,但当过去的信息与数据的动态有某种永久的因果关系时,这种假设可能是有害的。

让我们在上面使用的天气数据集中使用指数平滑,我们将:

  • 符合数据
  • 预报
  • 对照实际值绘制预测图
from statsmodels.tsa.api import ExponentialSmoothing
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
sns.set()
import pandas as pd

fit1 = ExponentialSmoothing(df_weather["Temperature (C)"][0:200]).fit(smoothing_level=0.1, optimized=False)
fit2 = ExponentialSmoothing(df_weather["Temperature (C)"][0:200]).fit(smoothing_level=0.5, optimized=False)
forecast1 = fit1.forecast(3).rename(r'$\alpha=0.1$')
forecast2 = fit2.forecast(3).rename(r'$\alpha=0.5$')
plt.figure(figsize=(17,8))

forecast1.plot(color='blue', legend=True)
forecast2.plot(color='red', legend=True)
df_weather["Temperature (ºC)"][0:200].plot(marker='',color='green', legend=True)
fit1.fittedvalues.plot(color='blue')
fit2.fittedvalues.plot(color='red')
plt.title("Exponential Smoothing for Weather Data", fontsize=15)
plt.xticks([])

plt.show()

现在,对于零售数据集:

# Put the correct dataframe here!

fit1 = ExponentialSmoothing(df_sales_sum["Weekly_Sales"][0:200]).fit(smoothing_level=0.1, optimized=False)

fit2 = ExponentialSmoothing(df_sales_sum["Weekly_Sales"][0:200]).fit(smoothing_level=0.5, optimized=False)

forecast1 = fit1.forecast(3).rename(r'$\alpha=0.1$')
forecast2 = fit2.forecast(3).rename(r'$\alpha=0.5$')
plt.figure(figsize=(17,8))

forecast1.plot(color='blue', legend=True)
forecast2.plot(color='red', legend=True)
df_sales_sum["Weekly_Sales"][0:200].plot(marker='',color='green', legend=True)
plt.ylabel("Sales", fontsize=15)

fit1.fittedvalues.plot(color='blue')
fit2.fittedvalues.plot(color='red')

plt.title("Exponential Smoothing for Retail Data", fontsize=15)
plt.xticks([], minor=True)
plt.show()

这里,我们使用平滑因子(最近一段时间的权重)的两个值 alpha = 0.1 和 alpha = 0.5 进行平滑,并用绿色绘制实际温度和零售数据。

正如我们在这里看到的,平滑因子越小,时间序列就越平滑。这具有直观的意义,因为随着平滑因子接近 0,我们接近移动平均模型。第一个似乎很好地捕捉了两个数据集的动态,但它似乎未能捕捉某些峰值活动的幅度。

从概念上讲,在给定数据集的性质的情况下,思考模型的假设如何影响其性能是很有趣的。我认为新信息对销售更重要,因为影响商店销售产品可能性的因素可能会不断变化和更新。因此,与假设动态以某种方式保持不变的模型相比,具有降低过去信息重要性的能力的模型将更准确地捕捉这种变化动态。

有马

ARIMA 或自回归综合移动平均是一种时间序列模型,旨在描述时间序列数据中的自相关性。它适用于短期预测,并且对于为用户指定的时间段提供预测值非常有用,可以显示需求、销售、计划和生产的良好结果。

ARIMA 模型的参数定义如下:

  • p:模型中包含的滞后观测值的数量
  • d:原始观测值被差分的次数
  • 问:移动平均线窗口的大小

现在,我将使用 ARIMA 模型来模拟天气数据和零售销售。这些步骤将是:

  • 将数据分为训练和测试
  • 符合数据
  • 打印均方差(我们的评估指标)
  • 用真实值绘制模型拟合图
from statsmodels.tsa.arima_model import ARIMA
from sklearn.metrics import mean_squared_error
import pandas as pd

X = df_weather["Temperature (C)"].values

train_size = 600
test_size = 200

train, test = X[0:train_size], X[train_size:train_size+test_size]

history = [x for x in train]
predictions = []
for t in range(len(test)):
	model = ARIMA(history, order=(5,1,0))
	model_fit = model.fit(disp=0)
	output = model_fit.forecast()
	yhat = output[0]
	predictions.append(yhat)
	obs = test[t]
	history.append(obs)

mse = mean_squared_error(test, predictions)
print(f"MSE error: {mse}")
plt.figure(figsize=(17,8))

plt.plot(test)
plt.plot(predictions, color='red')
plt.title("ARIMA fit Weather Data")
plt.xticks([])
plt.show()MSE error: 3.105596078192541

在这里,我们看到了 ARIMA 模型与天气数据集的预期良好拟合,因为之前我们看到该数据集具有非常高的自相关性。让我们将此与模型在销售数据集上的表现进行比较:

from statsmodels.tsa.arima_model import ARIMA
from sklearn.metrics import mean_squared_error
import pandas as pd

import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

X = df_sales_sum["Weekly_Sales"].values

split = int(0.66*len(X))
train, test = X[0:split], X[split:]

history = [x for x in train]
predictions = []
for t in range(len(test)):
	model = ARIMA(history, order=(5,1,0))
	model_fit = model.fit(disp=0)
	output = model_fit.forecast()
	yhat = output[0]
	predictions.append(yhat)

	obs = test[t]
	history.append(obs)
mse = mean_squared_error(test, predictions)

print(f"MSE error: {mse}")

plt.figure(figsize=(17,8))
plt.plot(test)
plt.plot(predictions, color='red')
plt.title("ARIMA fit to Sales Data",fontsize=15)
plt.xticks([])
plt.show()MSE error: 47664398980324.34

在这里,拟合度远不如在气象数据集中的拟合度好,考虑到 ARIMA 模型通常适用于高度稳定的数据集,这是意料之中的。

让我们记住,这里的结果只是为了展示模型,并不代表准确的估计。数据集是有限的(求和后的零售数据集小于 200 个数据点),我没有执行任何复杂的超参数调整。这里的目标只是演示这些模型是如何工作的,以及如何在 python 中实现它们。我们可以证实,零售数据集似乎提出了传统模型无法克服的挑战。

我们可以看到,对于有明确模式的数据集,传统模型工作得很好。然而,在缺乏这种结构的情况下,这些模型似乎不具备适应的灵活性,因为它们依赖于关于目标时间序列动态的强有力的假设。

现在,我们将讨论当前用于销售预测的深度学习方法,并尝试了解它们可以带来什么,这将有利于在传统模型不足以满足需求的情况下提高预测准确性。

现代销售预测和深度学习

在这里,我想概述一下我认为最适合销售预测的深度学习候选人的主要候选人。

亚马逊的 DeepAR 模型

我们今天在这里讨论的模型对每个单独的时间序列都适用一个模型。然而,在销售中,通常有多个时间序列与您试图建模的动态相关。因此,能够在所有相关时间序列上联合训练模型是非常有益的。

进入亚马逊预测 DeepAR+,这是一种使用递归神经网络预测一维时间序列的监督学习算法。它允许在一个模型上训练多个时间序列特征,并且在标准时间序列基准上优于传统模型。

该模型的主要特点是,它克服了传统模型只能在单一时间序列上训练的局限性。此外,该模型使用概率预测,其中,该模型预测不同未来情景的可能性分布,展示一组预测区间,而不是传统的对我们在给定日期或期间预期销售多少的点预测。这些预测分位数可以用来表示预测中的不确定性,从而为我们提供每个预测的置信区间。当涉及到点预测用处不大的下游使用决策时,这些类型的预测特别重要。

销售预测的 NLP

一种乍一看似乎非常规但很有希望的方法是使用自然语言处理模型进行预测。我想提到两种方法:

实体嵌入

在 LotusLabs 的这篇文章中,他们描述了使用分类数据(彼此不相关的数据)并利用该数据的嵌入表示来进行预测的想法。为了建立这种表示,传统的神经网络用于将输入映射到嵌入空间。

通过识别相似的输入并将它们映射到相似的位置,他们能够识别出原本很难看到的模式。使用这种方法的一个优点是,您不必执行任何特性工程。

关于这种方法的一个有趣的细节是,它克服了简单的一热编码表示中的稀疏性等问题。

NLP 对产品描述进行销售预测

这篇论文采取了不同的方法。他们使用了日本电子商务市场 Rakuten 上超过 90,000 个产品描述的数据,并确定了可操作的写作风格和词汇用法,这些风格和用法高度预测了消费者的购买行为。

该模型综合使用了词向量、LSTMs 和注意力机制来预测销售额。他们发现,季节性的、礼貌的、权威的和信息丰富的产品描述会带来最好的结果。

此外,他们表明,即使你考虑到品牌忠诚度和商品身份等其他因素,产品描述的嵌入式叙述中的词语也是销售的非常重要的决定因素。他们使用神经网络的新特征选择方法具有良好的性能,并且该方法本身指出了在使用执行销售预测时必须考虑的数据集环境的异质性。

用于销售预测的 WAVENET】

Corporacion Favorita 杂货销售预测比赛中获得第二名的选手使用了改编版的 Wavenet CNN 模型。Wavenet 是一个生成模型,可以在给定一些条件输入的情况下生成实值数据序列。根据作者的说法,这里的主要思想在于扩张因果卷积的概念。

WaveNet 被构造为完全卷积神经网络,其中卷积层具有各种膨胀因子,允许其感受野指数增长,并使用能够保持特征图大小的上采样滤波器覆盖许多时间点。这种方法可以扩大内核的视野,并捕捉输入的整体全局视图。要了解更多,我推荐 DeepMind 的这篇文章。

生成模型似乎是深度学习中销售预测的一个明显趋势,因为它们已经证明有能力对分布进行建模,因此可以预测不同场景的可能性,在销售预测的偶然背景下,当人们可以访问足够的数据时,这似乎是比传统模型更好的方法。

关于元学习的快速说明

在今年 5 月发表的这篇论文中,邵会·马和罗伯特·菲尔德斯开发了一种销售预测的元学习方法。这个想法是使用元学习者利用潜在的预测方法池,而不是一个模型的方法。

他们的方法使用元学习器来提取数据的相关特征,这些元学习器使用 1-D 卷积的堆叠序列和最后合并的校正线性单元。在集合阶段,他们使用密集层和 softmax 将来自多个预报的预测结合起来。

他们的方法点表明该领域趋向于更混合的自学方法,而不是单一模型解决方案。

结论

传统的方法只能解释它们被训练的一维数据的动态。然而,像这样的方法指出了混合模型的未来,其中多个时间序列可以被考虑,分类变量可以被包括在预测管道中。

通用性和灵活性似乎是渗透成功销售预测模型的关键因素。深度学习能够开发复杂的定制预测模型,这些模型包含非结构化零售数据集,因此只有当数据足够复杂时,使用它们才有意义。

如果你想看看这篇文章的笔记本,你可以在这里找到它。

如果你喜欢这种类型的内容,请查看我的 Youtube 频道:

作者

如果您想更深入地了解 Python 预测,请查看 Udemy 的这些课程:

这些是附属链接,如果你使用它们,我会得到一小笔佣金,干杯!😃

这里有一些程序员必备的附属链接:)

如果你喜欢这篇文章,请在 LinkedInTwitter 上联系我。

谢谢,下次再见!😃

参考

具有价格和促销效果的销售预测

原文:https://towardsdatascience.com/sales-forecasting-with-price-promotion-effects-b5d70207b128?source=collection_archive---------0-----------------------

图片来源:Pixabay

促销分析,时间序列预测,干预分析

许多生意都是季节性的,有些生意会在假期赚钱,比如超级碗、劳动节、感恩节和圣诞节。此外,他们在一年中的几个星期里利用促销来增加对产品或服务的需求或知名度。

在本帖中,我们将使用时间序列分析技术来分析历史数据,具有推广效果。

我们将要使用的数据是 9 家商店和 3 种产品的每周销售和价格数据。最后,我们将预测其中一家商店三种产品之一未来 50 周的销售情况。你可以在这里找到数据集

数据

  • 商店:商店代码。我们总共有 9 家店。
  • 产品:产品代码。我们总共有三种产品。
  • Is_Holiday:该周是否包含假日的指标:0 =否,1 =是。
  • 基价:无折扣的基本价格或日常价格。
  • 价格:每周的实际价格。它们要么是促销时的促销价格,要么是日常价格。
  • 周销售量:周销售量。

新功能. py

在上面的数据预处理中,我们添加了新的功能,如以美元、年、月、日、周为单位的周销售额。

电子设计自动化(Electronic Design Automation)

为了获得关于数据中连续变量的第一印象,我们将绘制 ECDF

ECDF.py

1

  • 虽然在最好的一周,一家商店销售了 2500 多台,但大约 80%的时间,周销售量都没有超过 500 台。
  • 尽管最高周销售额超过 25K 美元,但超过 90%的数据的周销售额低于 5K 美元。
df.groupby('Store')['weekly_sales'].describe()

图 2

df.groupby('Store')['Weekly_Units_Sold'].sum()

图 3

  • 很容易看出,在所有 9 家商店中,商店 10 的平均周销售额最高,商店 10 的总周销售量也最高。
  • 而 5 号店的平均周销售额最低。
  • 显然,10 号店是最畅销和最拥挤的一家。
g = sns.FacetGrid(df, col="Is_Holiday", height=4, aspect=.8)
g.map(sns.barplot, "Product", "Price");

图 4

g = sns.FacetGrid(df, col="Is_Holiday", height=4, aspect=.8)
g.map(sns.barplot, "Product", "Weekly_Units_Sold");

图 5

  • 产品 2 是所有三种产品中最便宜的产品,它卖得最多。
  • 产品 3 是所有三种产品中最贵的产品。
  • 此外,产品价格在节假日期间没有变化。
g = sns.FacetGrid(df, row="Is_Holiday",
                  height=1.7, aspect=4,)
g.map(sns.distplot, "Weekly_Units_Sold", hist=False, rug=True);

图 6

sns.factorplot(data= df, 
               x= 'Is_Holiday',
               y= 'Weekly_Units_Sold',
               hue= 'Store');

图 7

sns.factorplot(data= df, 
               x= 'Is_Holiday',
               y= 'Weekly_Units_Sold',
               hue= 'Product');

图 8

  • 假期似乎不会对业务产生积极影响。对于大多数商店来说,假日期间的周销售量与平时相同,而 10 号商店在假日期间有所下降。
  • 产品 1 的周销售量在假日期间略有增加,而产品 2 和产品 3 在假日期间有所下降。
g = sns.FacetGrid(df, col="Product", row="Is_Holiday", margin_titles=True, height=3)
g.map(plt.scatter, "Price", "Weekly_Units_Sold", color="#338844", edgecolor="white", s=50, lw=1)
g.set(xlim=(0, 30), ylim=(0, 2600));

图 9

  • 每个产品都有不止一个价格,在节假日和平时都是如此。我猜一个是正常价格,另一个是促销价格。
  • 产品 3 的价格差距是巨大的,在促销期间它被削减到将近 50%。
  • 产品 3 在非节假日期间销量最大。
g = sns.FacetGrid(df, col="Store", hue="Product", margin_titles=True, col_wrap=3)
g.map(plt.scatter, 'Price', 'Weekly_Units_Sold', alpha=.7)
g.add_legend();

图 10

这 9 家商店都销售这 3 种产品。他们似乎都有类似的打折促销活动。但是,在商店 10 的促销活动中,产品 3 的销量最高。

g = sns.FacetGrid(df, col="Store", col_wrap=3, height=3, ylim=(0, 1000))
g.map(sns.pointplot, "month", "Weekly_Units_Sold", color=".3", ci=None, order = [1,2,3,4,5,6,7,8,9,10,11,12]);

图 11

每个商店都有一定的季节性,10 号店的季节性最明显。

g = sns.FacetGrid(df, col="Product", col_wrap=3, height=3, ylim=(0, 1000))
g.map(sns.pointplot, "month", "Weekly_Units_Sold", color=".3", ci=None, order = [1,2,3,4,5,6,7,8,9,10,11,12]);

图 12

每个产品都有一定的季节性,产品 2 每年有两个旺季,产品 3 有一个旺季。

g = sns.FacetGrid(df, col="Store", col_wrap=3, height=3, ylim=(0, 2000), hue='Product', palette="Set1")
g.map(sns.pointplot, "month", "Weekly_Units_Sold", ci=None, order = [1,2,3,4,5,6,7,8,9,10,11,12], alpha=.7)
g.add_legend();

图 13

  • 总的来说,在每家商店,产品 2 每周的销售量都比其他产品多。
  • 在商店 10,产品 3 偶尔会超过产品 2。
g = sns.PairGrid(df, y_vars=["Weekly_Units_Sold"], x_vars=["Price", "Is_Holiday"], height=4)
g.map(sns.regplot, color=".3");

图 14

  • 价格越便宜,每周售出的数量就越多。
  • 是不是放假跟单位卖不卖没关系。

这里我们增加了一个新的栏目,叫做“促销”,这个栏目是从“底价”和“价格”派生出来的。

推广. py

sns.factorplot(data= df, 
               x= 'promotion',
               y= 'Weekly_Units_Sold',
               hue= 'Store');

图 15

在促销期间,每家商店都卖得更多,没有例外。

sns.factorplot(data= df, 
               x= 'promotion',
               y= 'Weekly_Units_Sold',
               hue= 'Product');

图 16

促销期间,每种产品都卖得更多,尤其是产品 2 和产品 3。

g = sns.FacetGrid(df, col="Store", hue="promotion", palette = 'plasma', row='promotion')
g = (g.map(plt.scatter, "Price", "Weekly_Units_Sold")
     .add_legend())

图 17

所有的商店都有相似的价格促销模式,出于某种原因,商店 10 在促销期间卖得最多。

g = sns.FacetGrid(df, hue="promotion", col="Product", height=4)
g.map(qqplot, "Price", "Weekly_Units_Sold")
g.add_legend();

图 18

每个产品都有正常价格和促销价格。在促销期间,产品 3 的折扣最高,销量也最大。

观察值

  • 销售最多、最拥挤的是 10 号店,最不拥挤的是 5 号店。
  • 就售出的单位数量而言,全年最畅销的产品是产品 2。
  • 商店不一定在假期进行产品促销。节假日似乎不会对商店或产品的表现产生影响。
  • 产品 2 好像是最便宜的产品,产品 3 是最贵的产品。
  • 大多数商店都有一些季节性,他们每年有两个旺季。
  • 产品 1 在 2 月份的销量比其他月份稍高,产品 2 在 4 月和 7 月左右的销量最高,产品 3 在 7 月至 9 月的销量最高。
  • 每种产品都有其正常价格和促销价格。产品 1 和产品 2 的正常价格和促销价格之间没有明显的差距,但是,产品 3 的促销价格可以削减到其原价的 50%。虽然每个商店都对产品 3 进行这种降价,但在降价期间,商店 10 的销售额最高。
  • 促销期间比平时卖得多并不稀奇。商店 10 使产品 3 在 7 月至 9 月期间成为最畅销的产品。

先知的时间序列

我们将为商店 10 中的产品 3 构建一个时间序列分析,并以美元为单位预测每周销售额。

timeseries_viz.py

图 19

商店 10 中产品 2 的季节性很明显。在学校放假期间,销售总是在七月和九月之间达到高峰。

下面我们正在实现 prophet 模型,预测未来 50 周的周销售额。

图 20

该模型能够捕捉到季节性。

model.plot_components(forecast);

图 21

metric_df = forecast.set_index('ds')[['yhat']].join(store_10_pro_3.set_index('ds').y).reset_index()
metric_df.dropna(inplace=True)
error = mean_squared_error(metric_df.y, metric_df.yhat)
print('The RMSE is {}'. format(sqrt(error)))

季节性效应

关于 Prophet 的一个伟大的事情是,我们可以添加我们自己的定制季节。这里我们将添加从七月初到九月初的学校假期。

add _ 季节性. py

图 22

metric_df = forecast.set_index('ds')[['yhat']].join(store_10_pro_3.set_index('ds').y).reset_index()
metric_df.dropna(inplace=True)
error = mean_squared_error(metric_df.y, metric_df.yhat)
print('The RMSE is {}'. format(sqrt(error)))

RMSE 下降了一点。

model.plot_components(forecast);

图 23

Jupyter 笔记本可以在 Github 上找到。享受这周剩下的时光吧!

参考资料:

[## 季节性、假日效应和回归因素

如果您有假期或其他想要建模的周期性事件,您必须为它们创建一个数据框架。它有…

facebook.github.io](https://facebook.github.io/prophet/docs/seasonality,_holiday_effects,_and_regressors.html)

https://seaborn.pydata.org/generated/seaborn.FacetGrid.html

Tableau 中的销售与利润(象限)分析

原文:https://towardsdatascience.com/sales-vs-profit-quadrant-analysis-in-tableau-aeafd1b757dc?source=collection_archive---------16-----------------------

使用 Tableau 中的象限分析快速识别任何销售数据集中表现最佳的类别。

Tableau 中的销售与利润象限分析(图片由作者提供)

使用 Tableau 中的象限分析快速识别任何销售数据集中表现最佳的类别。

详细信息:象限图只不过是一个由四个相等部分组成的散点图。每个象限支持具有相似特征的数据点。以下是如何使用超市销售数据集在 Tableau 中绘制象限分析图的分步方法,以确定在销售和利润方面表现最佳的品类:

步骤 1: 打开一个新的 Tableau 文件,将“超市”公共数据集导入工作簿。如果你以前没有用过 Tableau,请从以下网址下载“Tableau Public”:https://public.tableau.com/en-us/s/download

Tableau 公开下载(图片由作者提供)

第 2 步:我们的目标是根据销售和利润指标评估各个子类别的表现。因此,将“订单”表导入到工作区中;将汇总的“销售”和“利润”指标拖放到行&列中。

导入订单表(作者图片)

总销售额和利润(作者图片)

第 3 步:通过将“子类别”字段拖放到“标签”和“分析”窗格中,引入子类别。

导入子类别(按作者分类的图片)

步骤 4: 为参考行创建计算字段:

Reference Line for Profit = WINDOW_AVG(SUM([Profit]))Reference Line for Sales = WINDOW_AVG(SUM([Sales]))

将参考线计算拖至“详细信息”和“分析”窗格。编辑两个参考行计算,一个用于销售,另一个用于利润。

创建计算字段(作者图片)

利润参考线(图片由作者提供)

销售参考线(图片由作者提供)

统一参考线部件(图片由作者提供)

带圆圈的参考线(图片由作者提供)

步骤 5: 为“象限(颜色指示器)”创建计算字段:

Quadrant (Colour Indicator)=IF [Reference Line for Profit]>= WINDOW_AVG(SUM([Profit]))
AND [Reference Line for Sales]>= WINDOW_AVG(SUM([Sales]))
THEN 'UPPER RIGHT'ELSEIF[Reference Line for Profit]< WINDOW_AVG(SUM([Profit]))
AND [Reference Line for Sales]>= WINDOW_AVG(SUM([Sales]))
THEN 'LOWER RIGHT'ELSEIF[Reference Line for Profit]> WINDOW_AVG(SUM([Profit]))
AND [Reference Line for Sales]< WINDOW_AVG(SUM([Sales]))
THEN 'UPPER LEFT'ELSE 'LOWER LEFT'END

拖动“象限(颜色指示器)”来着色和编辑表格计算;选择“子类别”作为特定维度。

象限颜色指示器(图片由作者提供)

选择子类别(按作者分类的图片)

步骤 6: 此外,为等级创建一个计算字段,并将它拖到工具提示中:

Sales Rank = RANK(SUM([Sales]))Profit Rank = RANK(SUM([Profit]))Count of Sub-Category = WINDOW_COUNT(COUNTD([Sub-Category]))

对于每个计算字段:编辑表格计算&选择“子类别”作为特定维度。

销售排名(作者图片)

利润排名(作者图片)

子类别计数(按作者分类的图片)

第七步:给标题,格式化坐标轴,添加参考线。

Y 轴格式(图片由作者提供)

X 轴格式(图片由作者提供)

在两个轴上添加参考线(图片由作者提供)

y 轴参考线配置。(图片由作者提供)

x 轴参考线(图片由作者提供)

第 8 步:固定 BI 小工具的大小,设置工具提示。

BI 部件大小(图片由作者提供)

工具提示:

<Sub-Category>Sales:<SUM(Sales)>Sales Rank:<AGG(Sales Rank)>/<AGG(Count of Sub-Category)>Profit:<SUM(Profit)>Profit Rank:<AGG(Profit Rank)>/<AGG(Count of Sub-Category)>

工具提示配置。(图片由作者提供)

第九步:产生洞察力:

销售与利润象限分析(图片由作者提供)

(1)我们可以从象限图中清楚地看到,通过为超市创造最大的销售额和利润,手机、储物件和活页夹销量最大。因此,让销售团队知道这些产品对我们的年收入有多重要。

(2)另一方面,我们可以看到销售了大量的桌子和机器,但它们的利润似乎出人意料地低于同行。我们在抛售吗?我们应该提高产品的价格吗?

(3)超市出售的电器不多,而它们的利润率似乎真的很高。因此,让销售团队知道,如果他们能想出更多创新措施来推动这些设备的销售,那就太好了。

(4)最后,重新审视我们的销售和营销策略,以确保有足够数量的家具、书架和艺术品以修订后的价格售出。

结论:

因此,在本文中,我们简要地研究了 Tableau 中交互式象限分析的基本原理,并从相似和不相似的数据点中获得见解。从这里,我们可以继续用 R 或 Python 进行探索性数据分析,以识别数据集中真正推动销售的其他重要组件。

GitHub 库

我已经从 Github 的许多人那里学到了(并且还在继续学到)。因此在一个公共的 GitHub 库 中分享我的 Tableau 文件,以防它对任何在线搜索者有益。此外,如果您在理解 Tableau 中数据可视化的基础知识方面需要任何帮助,请随时联系我。乐于分享我所知道的:)希望这有所帮助!

关于作者:

[## Sreejith Sreedharan - Sree

数据爱好者。不多不少!你好!我是 Sreejith Sreedharan,又名 Sree 一个永远好奇的数据驱动的…

srees.org](https://srees.org/about)

使用 Docker 和 Nginx 的负载平衡解决方案示例

原文:https://towardsdatascience.com/sample-load-balancing-solution-with-docker-and-nginx-cf1ffc60e644?source=collection_archive---------0-----------------------

当今大多数业务应用程序都使用负载平衡在不同资源之间分配流量,避免单个资源过载。

负载平衡架构的一个明显优势是提高了应用程序的可用性和可靠性,因此,如果一定数量的客户端向后端请求一定数量的资源,负载平衡器会留在它们之间,并将流量路由到满足大多数路由标准(不太忙、最健康、位于给定区域)的后端..等等)。

有很多路由标准,但我们将把本文的重点放在固定的循环标准上——这意味着每个后端接收固定量的流量——我认为这很少被记录下来:)。

为了简化,我们将基于 flask Python 文件创建两个后端“应用程序”。我们将使用 NGINX 作为负载平衡器,将 60%的流量分配给应用 1,40%的流量分配给应用 2。

让我们开始编码,以下是我们项目的完整架构:

负载平衡项目树

app1/app1.py

from flask import request, Flask
import jsonapp1 = Flask(__name__)
@app1.route('/')def hello_world():
return 'Salam alikom, this is App1 :) 'if __name__ == '__main__':
app1.run(debug=True, host='0.0.0.0')

app2/app2.py

from flask import request, Flask
import jsonapp1 = Flask(__name__)
@app1.route('/')def hello_world():
return 'Salam alikom, this is App2 :) 'if __name__ == '__main__':
app1.run(debug=True, host='0.0.0.0')

然后,我们必须通过添加 requirements.txt 文件来对两个应用程序进行 dockerize。它将只包含 flask 库,因为我们使用的是 python3 映像。

app 1/requirements . txt
您可以为 app2 创建相同的。

Flask==1.1.1

app1/Dockerfile
你可以为 app2 创建相同的。

FROM python:3
COPY ./requirements.txt /requirements.txt
WORKDIR /
RUN pip install -r requirements.txt
COPY . /
ENTRYPOINT [ "python3" ]
CMD [ "app1.py" ]

我们将使用 NGINX 作为负载平衡器,路由标准将由循环法权重参数保证:

nginx/nginx.conf

upstream loadbalancer {
server 172.17.0.1:5001 weight=6;
server 172.17.0.1:5002 weight=4;
}
server {
location / {
proxy_pass [http://loadbalancer;](http://loadbalancer;)
}} 

然后,我们将通过使用 Nginx 映像创建 Dockerfile 来对我们的负载平衡器进行 dockerize。它会在启动时将上述 conf 文件复制到容器内的相关路径中。

nginx/Dockerfile

FROM nginx
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf

现在我们将创建 docker-compose 文件,该文件将加速我们的完整架构,以便我们可以从浏览器访问它。

docker-compose.yml

version: '3'
services:
app1:
build: ./app1
ports:
- "5001:5000"
app2:
build: ./app2
ports:
- "5002:5000"
nginx:
build: ./nginx
ports:
- "8080:80"
depends_on:
- app1
- app2

关于 docker-compose.yml 的一些要点:

  • 它将基于我们的 docker 文件为 app1、app2、Nginx 构建映像,然后根据这些映像构建容器。
  • app1 和 app2 容器中打开的端口是 5000(flask 使用的默认端口),这些端口将被映射到 5001 和 5002。
    负载平衡器会根据该端口将流量路由到适当的应用程序。
  • 负载平衡器(Nginx)会将其内部 80 端口暴露给 8080,这样我们就可以从 http://localhost:8080 访问应用程序

最后,我们将通过运行下面的命令来加速我们的架构,并在浏览器中享受结果:)

docker-compose up

您还可以在下面的 GitLab repo 中找到完整的代码:

[## Abdelilah OUASSINI /负载平衡

GitLab.com

gitlab.com](https://gitlab.com/ouassini/load-balancing)

一如既往,我希望你学到了新的东西。萨拉姆:)

抽样分布——样本均值

原文:https://towardsdatascience.com/sampling-distribution-sample-mean-fcf69484535e?source=collection_archive---------33-----------------------

用 Python 模拟和例子

在推理数据分析中讨论的最重要的概念之一是抽样分布的概念。了解抽样分布有助于我们更好地理解和解释描述性和预测性数据分析调查的结果。在不确定性和假设检验的情况下,抽样分布也经常用于决策。

Marcin JozwiakUnsplash 上拍摄的照片

什么是抽样分布?

你可能已经熟悉了概率分布的概念。概率分布让我们了解与随机变量可能假设的值(或值的范围)相关的概率和可能性。随机变量的值(结果)是随机确定的。随机变量的一些例子包括零售商店的月收入、在任何给定的一天到达洗车场的顾客数量、在任何给定的一天在某条高速公路上的事故数量、零售商店的周销售量等。虽然随机变量的结果是随机的,但概率分布允许我们获得和了解结果中出现不同值的可能性和概率。抽样分布是我们附加到样本的样本统计上的概率分布。

样本均值作为样本统计量

样本统计量(也简称为统计量)是从样本中获得的值。这里有一个例子,假设你收集了居住在某个街区的 250 个随机选择的个人填写的调查结果。根据调查结果,您意识到该样本中个人的平均年收入为 82,512 美元。这是一个样本统计,用x̅= 82,512 美元表示。样本均值也是一个具有概率分布的随机变量(用 X̅表示)。X̅的概率分布称为样本均值的抽样分布。可以为其他类型的样本统计定义样本分布,包括样本比例、样本回归系数、样本相关系数等。

你可能想知道为什么 X̅是一个随机变量,而样本均值只是一个单一的数字!理解这一点的关键在于样本间可变性的概念。这个想法指的是从同一人群中抽取的样本并不完全相同。这里有一个例子,假设在上面的例子中,我们不是只对居住在某个特定社区的 250 个人进行一次调查,而是在那个社区进行了 35 个同样大小的样本。如果我们为 35 个样本中的每一个计算样本均值 ,你将得到 35 个不同的值。现在假设,假设,我们在那个社区进行了许多相同规模的调查。我们会得到许多许多(不同的)样本均值。由这些样本平均值得到的分布就是我们所说的样本平均值的抽样分布。从这个角度考虑样本均值,我们可以想象 X̅(注意大写字母)是如何代表样本均值的随机变量,而(注意小写字母)只是该随机变量的一种实现。

样本均值的抽样分布

假设 X 代表数据(总体),若 X 具有均值μ和标准差σ的分布,且若 X 近似正态分布或样本量 n 较大,

上述分布仅在以下情况下有效,

  • x 近似正常样本量 n 大,
  • 数据(总体)标准差σ已知。

如果 x 是正态的,那么 X̅也是正态分布的,与样本大小 n 无关。中心极限定理告诉我们,即使 x 不正态,如果样本量足够大(通常大于 30),那么 X̅'s 分布近似正态(Sharpe,De Veaux,Velleman and Wright,2020,PP . 318–320)。如果 X̅是正态的,我们可以很容易地将其标准化并转换为标准正态分布 Z

如果总体标准差σ是而不是已知的,我们不能假设样本均值 X̅是正态分布的。如果满足某些条件(下面解释),那么我们可以将 X̅变换为另一个随机变量 t ,

随机变量 t 据说遵循 t 分布,具有 n-1 个自由度,其中 n 是样本大小。t 分布呈钟形且对称(就像正态分布一样),但与正态分布相比,它的尾部更厚。这意味着与正态分布相比,离平均值越远的值出现的可能性越大。

对随机变量 t 使用 t 分布的条件如下(Sharpe 等人,2020 年,第 415-420 页):

  • 如果 X 是正态分布,即使对于小样本量(n<15),也可以使用 t 分布。**
  • 如果样本量在 15 到 40 之间,只要 X 是单峰的并且合理对称,就可以使用 t 分布。
  • 对于大于 40 的样本量,可以使用 t 分布,除非 X 的分布严重偏斜。

用 Python 模拟

让我们从正态分布中抽取一个大小为 n=250 的样本。这里我们假设我们的数据是正态分布的,参数μ = 20,σ = 3。从这个群体中收集一个样本

运行这段代码一次,我就会得到随机变量 X̅.的一个实例(或实现)下面是我运行这段代码 10 次后, 的 10 个值。

但是如果我运行这段代码 10,000 次,记录下 的值,并绘制出这些值的频率(或密度),我会得到下面的结果。

样本均值的分布(图片由作者提供)。

如您所见,该分布近似对称且呈钟形(就像正态分布一样),平均值约为 20,标准误差约等于 3/sqrt(250) = 0.19。

用不同的样本量从相同的人群中取样将导致不同的结果分布的分布测量。正如我们所预期的,增加样本量将减少标准误差,因此,分布将在其平均值附近变窄。请注意,即使样本量非常小,X̅分布也是正态分布。这是因为 X 是正态分布的。

样本大小对样本均值分布标准误差的影响(图片由作者提供)。

人口(数据)不正常怎么办?

别担心!即使您的数据不是正态分布,如果样本量足够大,X̅的分布仍然可以使用正态分布来近似(根据中心极限定理)。下图显示了 x 严重向左倾斜时的 X̅分布。正如你所看到的,对于小样本,X̅'s 分布倾向于模拟 x 的分布。然而,随着样本量的增长,X̅的分布变得更加对称和钟形。如上所述,如果样本量很大(通常大于 30),不管 x 的分布是什么,X̅'s 分布都是近似正态的。

对于大样本量,X̅'s 分布是正常的,即使 x 有一个偏斜的分布。

示例和应用

了解 X̅的分布可以帮助我们解决问题,在不确定的情况下,我们需要使用推断数据分析来做出决策。许多商业问题需要能够解决随机事件的随机性和概率性的决策工具。假设测试 是许多不同商业领域经常使用的工具之一,包括零售运营、市场营销、质量保证等。

例如,假设一家零售店开展了一项大型营销活动,并有兴趣调查该活动对该店平均销售额的影响。假设管理层想要调查平均日销售额现在是否大于 8000 美元。以下假设证明了这个研究问题:

请注意,我们正在对总体平均销售额进行测试,因此μ。为了解决测试问题,假设我们记录了 40 天的销售量(样本为 n=40 )并计算所需的统计数据。假设日销售量的平均值和标准差分别计算为 x̅=$8,100s=$580,。由于σ的值未知,并且假设上述假设检验正在进行,我们可以将 X̅转换为自由度为 n-1=39 的随机变量 t ,其中,

为了解决测试问题,我们需要找到与测试相关的 p 值。该属性计算如下:

随机变量 t 的概率密度函数以及测试的 p 值如下所示。

测试的 p 值在图片中突出显示(图片由作者提供)。

下面将找到测试的 p 值。

计算得出的 p 值约等于 0.14。根据大多数标准(显著性水平),这是一个大的 p 值,表明我们未能拒绝零假设。换句话说,根据 X̅的分布和收集的样本,我们不能得出零售店的平均日销售量μ大于 8000 美元的结论。这个计算是可能的,只是因为我们知道 X̅的分布。

可以为其他样本统计(例如,样本比例、回归预测系数等)定义采样分布。)也用于其他环境,如置信区间和预测区间或对回归结果的推断分析。

使用 Python 实现的样本分布

原文:https://towardsdatascience.com/sampling-distributions-with-python-implementation-3f4555e1d450?source=collection_archive---------29-----------------------

抽样分布、中心极限定理和用 Python 例子解释的自举

Unsplash 上由 Jametlene Reskp 拍摄的照片

抽样分布是统计数据的分布(可以是任何统计数据)。你可能会问为什么抽样分布很重要?嗯,它们是推断统计学的关键,推断统计学的目标是根据从一个群体的个体样本中收集的数据得出关于该群体的结论。

在本文中,感兴趣的群体是 10,000 只澳大利亚牧羊犬幼犬。我一直很好奇澳洲狗有蓝眼睛的比例是多少,淡褐色的比例是多少(假设这是它们唯一能拥有的两种眼睛颜色现实中并不是这样!).所以我们要估计的参数是蓝眼睛小狗在我们人口中所占的比例。假设我能找到 20 只小狗作为我们的样本参与实验。那 30 只小狗中蓝眼睛的比例(比例是 0,1 值的平均值)是我们的统计。下面你可以看到这个例子是模拟的

采样分发服务器有以下两个属性:

  1. 采样分布以原始参数值为中心。
  2. 样本量越大,抽样分布的方差越小。

从上面我们可以看到,原始样本的平均值为 0.75,标准偏差和方差分别为 0.433 和 0.187。让我们在示例中探索上述属性:

我们看到样本的比例以原始平均值(0.75)为中心。对于较大的样本量(20,如图中橙色所示),这一点更容易观察到。我们还可以看到,与 5 个样本相比,20 个样本的方差在减小。实际上,每个样本的方差就是原始数据的方差除以样本量。

在处理抽样分布时,经常会讨论两个重要的数学定理:

  1. 大数定律
  2. 中心极限定理

大数定律

大数定律表明,随着样本量的增加,样本均值将越来越接近总体均值。让我们用我们的例子来验证一下。

我们可以看到,在一个规模为 100 的样本中,平均值更接近原始平均值。

中心极限定理

该定理指出,在样本量足够大的情况下,均值的抽样分布将呈正态分布。这个定理并不适用于所有的统计学。它适用于以下情况:

  1. 样本均值
  2. 样本均值之间的差异
  3. 样本比例
  4. 样本比例之间的差异

为了验证这一点,让我们将我们的示例更改为非正态分布的样本。

我们看到,对于大小为 3 和 5 的样本,分布向右倾斜,对于大小为 30 的样本,分布更接近正态分布。

拔靴带

依赖数学定理,如中心极限定理,会导致缺口。我们可能不总是能够获得足够大的样本(收集 5 只小狗已经够难了!正如我们在中心极限定理一节中看到的,5 不是一个足够大的样本)或者我们可能在这些定理不适用的地方使用样本统计。

在这些情况下,我们可以使用自举。统计学中的自举意味着替换抽样,它允许我们模拟抽样分布的创建。np.random.choice(sample, size, replace=True可用于自举。事实上,在前面的所有章节中,我们已经从 20 只小狗的样本中进行了引导。通过自举,然后计算我们统计数据的重复值,我们可以了解我们统计数据的抽样分布。

采样是不够的,取而代之的是分析您的 ML 数据

原文:https://towardsdatascience.com/sampling-isnt-enough-profile-your-ml-data-instead-6a28fcfb2bd4?source=collection_archive---------25-----------------------

人工智能和数据管道的生产测井方法

艾萨克·巴克斯和伯尼斯·赫尔曼

照片由 Unsplash 上的 Petri R 拍摄

现在是 2020 年,我们大多数人仍然不知道我们的模型在生产中何时、何地、为何或如何出错。虽然我们都知道“什么会出错,什么会出错”,或者“老鼠和[数据科学家]精心制定的计划经常出错”,但复杂的模型和数据管道往往被推向生产,而很少关注诊断不可避免的不可预见的故障。

在传统的软件中,日志记录和工具已经被作为标准的实践来创建透明性和理解复杂系统的健康状况。当涉及到人工智能应用时,日志记录通常是不完整的。在这篇文章中,我们概述了 ML 日志记录的不同方法,并对它们进行了比较和对比。最后,我们提供了一个名为 WhyLogs 的开源库,只需几行代码就可以实现数据记录和分析。

传统软件中的登录是什么?

日志是开发和操作健壮软件系统的重要工具。当您的生产系统进入错误状态时,使用工具来更好地定位和诊断问题的来源是非常重要的。

对于许多软件工程学科来说,堆栈跟踪有助于定位执行路径并确定程序在失败时的状态。但是,堆栈跟踪并不能提供故障发生前状态如何变化的信息。日志记录(及其相关术语,软件跟踪)是将程序执行和事件信息存储到一个或多个文件中的一种实践。日志对于诊断各种软件的问题至关重要;生产系统的必备。

图片由monkeyuser.com免费使用

数据记录有何不同?

统计应用程序,如数据科学和机器学习中的应用程序,是需要日志记录的主要候选对象。然而,由于这些应用程序的复杂性,可用的工具仍然有限,它们的采用远不如标准软件日志广泛。

统计应用通常是不确定的,并且需要许多状态变化。由于需要处理广泛分布的状态,必须避免严格的逻辑断言,并且机器学习软件通常永远不会到达错误状态,而是静静地产生差的或不正确的结果。这使得错误分析变得更加困难,因为维护人员在问题发生时不会被提醒。

当检测到错误状态时,诊断问题通常很费力。与显式定义的软件相比,数据集对于内省来说尤其不透明。软件完全由代码指定,开发人员可以轻松地包含精确的日志记录语句来查明问题,而数据集和数据管道则需要大量的分析来进行诊断。

虽然 ML 中有效的日志实践可能难以实现,但在许多情况下,它们甚至比标准软件更有必要。在典型的软件开发中,在部署到生产环境之前,大量的问题可能会被编译器、ide、类型检查、逻辑断言和标准测试所捕获。有了数据,事情就没那么简单了。这激发了在 ML 操作中改进工具和最佳实践的需求,以推进统计记录。

软件开发中对好的测井工具的一般要求同样适用于 ML 操作领域。这些要求可能包括(但当然不限于)以下内容。

记录要求

  1. 易于使用
    良好的日志记录有助于开发,因为它可以尽早地、经常地向开发人员公开内部功能。如果日志是笨重的,没有人会使用它。软件开发中常见的日志模块可以像打印语句一样简单易用。
  2. 轻量级
    日志记录不应该干扰程序执行,因此必须是轻量级的。
  3. 标准化便携
    现代系统庞大复杂,我们必须能够调试。日志需要多语言支持。输出格式应该是标准的,易于从多个来源进行搜索、过滤、消费和分析。
  4. 我们必须能够在不修改代码的情况下修改所有服务的详细程度、输出位置,甚至可能是格式。对于开发人员或数据科学家来说,详细程度和输出要求可能与生产服务非常不同。
  5. 接近代码
    日志记录调用应该存在于它们所引用的代码/服务中,日志记录应该让我们非常快速地查明服务中的问题发生在哪里。日志记录提供了一种生成系统内部逻辑功能跟踪的系统方法。

谈到 ML 日志记录,有哪些方法可用?

标准代码内日志记录

在数据科学中,标准日志模块可以而且应该做很多事情。我们可以记录数据访问、正在执行的步骤(培训、测试等)。还可以记录模型参数和超参数以及更多细节。专注于 ML 用例的服务和库(比如 CometML)可以扩展这种日志的效用。

虽然标准日志记录可以提供很大的可见性,但它很少甚至不提供对数据的内省。

优点

  • 灵活且可配置
  • 可以跟踪中间结果和低复杂度的数据
  • 允许重用现有的非 ML 测井工具

缺点

  • 高存储、I/O 和计算成本
  • 数据科学家可能不熟悉或不适合日志记录格式
  • 日志处理需要计算量很大的搜索,特别是对于复杂的 ML 数据
  • 由于昂贵的存储成本,数据保留率较低;对过去问题的根本原因分析不太有用

抽样

监控 ML 特有的大量数据的一种常见方法是记录数据的随机子集,无论是在训练、测试还是推理过程中。随机选择数据的某个子集并存储它以供以后参考,这可能是相当直接和有用的。基于采样的数据记录不能准确表示异常值和罕见事件。因此,诸如最小值、最大值和唯一值等重要指标无法准确测量。保留异常值和不常见值非常重要,因为它们通常会影响模型行为,导致模型预测出现问题,并且可能表明存在数据质量问题。

优点

  • 易于实施
  • 比其他日志解决方案需要更少的前期设计
  • 日志处理等同于对原始数据的分析
  • 数据科学家熟悉的数据输出格式

缺点

  • 高存储、I/O 和计算成本
  • 信号嘈杂,覆盖范围有限;小样本要求可扩展且轻便
  • 没有统计分析处理步骤是不可读或不可解释的
  • 抽样往往会遗漏罕见事件和异常值
  • 无法精确计算依赖于异常值的指标,如最小值/最大值和唯一值
  • 输出格式依赖于数据,这使得与监控、调试或自省工具集成更加困难

数据剖析

记录数据的一种有前途的方法是数据剖析(也称为数据草图或统计指纹)。这个想法是捕捉给定数据集的人类可解释的统计特征,以提供对数据的洞察。已经存在广泛的有效流算法来生成可扩展的、轻量级的数据集统计特征,并且文献非常活跃并且正在增长。然而,在实践中实现这些算法存在重大的工程挑战,特别是在 ML 日志记录的环境中。一个项目正在努力克服这些挑战。

优点

  • 易用性
  • 可扩展且轻量级
  • 通过基于文本的配置文件灵活配置
  • 准确表示罕见事件和离群值相关指标
  • 无需进一步处理即可直接解释的结果(如直方图、平均值、标准差、数据类型)

缺点

  • 没有现有的普遍解决方案
  • 解决方案背后涉及数学和工程问题

让数据记录变得简单而不妥协!介绍 WhyLogs。

数据分析解决方案 WhyLogs 是我们对 ML 的现代、简化数据记录的贡献。WhyLogs 是一个开源库,其目标是通过提供近似的数据分析和满足上述五个日志记录要求(简单、轻量级、可移植、可配置、接近代码)来弥补 ML 日志记录的不足。

估计的统计配置文件包括每个要素的分布近似值,这些近似值可以提供直方图和分位数、总体统计数据(如最小/最大/标准偏差)、唯一性估计值、空计数、频繁项目等。所有的统计数据都是可合并的,这使得算法变得非常并行,并且允许将多个数据集的数据合并在一起,以便以后进行分析。这对于实现灵活的粒度(因为您可以更改聚合级别,例如,从每小时更改为每天或每周)和登录分布式系统非常关键。

WhyLogs 还支持适用于生产环境的特性,如标记、小内存占用和轻量级输出..标记和分组功能是实现细分级别分析以及将细分映射到核心业务 KPI 的关键。

便携轻巧

目前,有 PythonJava 实现,提供 Python 与 pandas/numpy 的集成,以及与 Spark 的可扩展 Java 集成。生成的日志文件很小,并且跨语言兼容。我们在以下数据集上测试了 WhyLogs Java 的性能,以验证 WhyLogs 的内存占用和输出二进制文件的大小。

我们在每个数据集上运行我们的配置文件,并收集 JMX 指标:

易于使用、可配置且接近代码

WhyLogs 可以很容易地添加到现有的机器学习和数据科学代码中。Python 实现可以“pip”安装,除了具有可访问 API 的库之外,还提供交互式命令行体验。

WhyLogs jupyter 笔记本示例

更多示例

更多使用 WhyLogs 的例子,请查看 WhyLogs 入门笔记本

强大的附加功能

当与实时数据的监控和其他服务相结合时,可以看到 WhyLogs 的全部威力。要探索这些功能如何与 WhyLogs 配合,请查看 WhyLabs 平台的实时沙盒,该沙盒运行在 Lending Club 数据集的修改版本上,每天接收 WhyLogs 数据。

WhyLabs 平台截屏捕捉了模型运行状况仪表板和功能运行状况视图。作者图片

让我们让数据记录成为生产 ML 系统的黄金标准!

数据科学、机器学习以及围绕它们的技术正以极快的速度发展,这些操作的规模以及参与其中的人数也是如此。伴随这种快速增长而来的是不可避免的问题激增。最佳实践在 ML 中仍处于萌芽状态,但正如软件和系统工程的情况一样,最佳实践必须继续成长和发展。有效的日志记录肯定在操作健壮的 ML/AI 系统的最佳实践中扮演主要角色。需要 WhyLogs 等项目来应对这些统计系统的独特挑战。

点击这里查看 Python 和 Java 的 WhyLogs,点击这里,或者开始阅读文档。我们喜欢反馈和建议,所以请加入我们的 Slack 频道或发送电子邮件至 support@whylabs.ai

感谢我在 WhyLabs 的队友 Bernease Herman 合作撰写了这篇文章。关注 Bernease 上的

PyTorch 几何图形中的大型图形采样

原文:https://towardsdatascience.com/sampling-large-graphs-in-pytorch-geometric-97a6119c41f9?source=collection_archive---------24-----------------------

大型图上的图形深度学习技术

有时,我们会遇到大型图形,迫使我们超出 GPU 或 CPU 的可用内存。在这些情况下,我们可以利用图形采样技术。 PyTorch Geometric 是一个图形深度学习库,允许我们轻松实现许多图形神经网络架构。该库包含许多标准的图形深度学习数据集,如 Cora、Citeseer 和 Pubmed。但是最近图形开放数据集中出现了使用大规模网络的趋势,如开放图形基准 (OGB) [3]。在 OGB,各种数据集从 ogbn-arxiv (169,343 个节点)这样的“小型”网络一直到 ogbn-papers100M (111,059,956 个节点)这样的“大型”数据集。也许 ogbn-arxiv 可以适合内存,如果你只是用一个小的 GCN 或其他东西做一个节点分类,但是尝试任何超出这个范围的东西,或者在 OGB 使用一个中到大的数据集,你可能不得不求助于对图形进行采样。

有多种方法可以对大型图表进行采样,我将尝试介绍两种主要的方法。

  1. 邻居采样器

来自 3 层邻域采样器的二部图草图

2.GraphSAINTSampler

来自 GraphSAINTSampler 小批量的子图采样器的草图

neighborhood sampler类来自 GraphSAGE paper, 大型图形上的归纳表示学习【2】如果你之前没有使用过 SAGEConv,你可以把它想象成学习一个基于节点的邻域输出节点嵌入的函数,而不是直接学习所有的节点嵌入。这使得它对归纳任务特别有用,因为你可以通过它从未见过的 GNN 节点。

我画了一个 3 层的 GNN 邻居样本的抽象视图,每个层都被分成一个二分图邻居样本。

邻域采样的三层 GNN 示意图

我试图用蓝色画出源节点,用红色画出目标节点。如您所见,目标节点也有自循环,因此它们出现在二分图的左侧和右侧。下面是代码的样子:

*train_loader = NeighborSampler(data.edge_index, node_idx=train_idx, 
                               sizes=[15, 10, 5], batch_size=1024, 
                               shuffle=**True**, num_workers=12)*

sizes指定每个源节点要采样的邻居数量。想象一下,从我们想要计算嵌入的节点集开始(这些是上图第 3 层中最后的红色节点)。然后,我们计算超参数指定的所有样本,最终返回给我们——相反。这意味着当我们遍历所有层时,我们最终只得到我们感兴趣的节点嵌入。

**注意:每个小批量中的节点 id 是大图中的原始节点 id。该采样器本身不对子图进行采样,而是对邻域进行采样,以学习聚合函数。

从 ogbn-products 数据集上 PyTorch Geometric 中的 GraphSAGE 示例中,我们可以看到train_loaderbatch_sizen_idadjs组成。

***for** batch_size, n_id, adjs **in** train_loader:
    ...
    out = model(x[n_id], adjs)
    ...*

n_id是采样过程中使用的每个节点的所有节点 id,包括采样的邻居节点和源节点。通过传递我们的模型x[n_id],我们只隔离了在这一批计算中使用的那些节点的节点特征向量。adjs中有 3 个adj,由一个edge_indexe_idsize组成。因此,在 PyTorch 几何模型的 SAGE 模型中,我们有:

***def** forward(self, x, adjs): 
    **for** i, (edge_index, _, size) **in** enumerate(adjs): 
        x_target = x[:size[1]] 
        x = self.convs[i]((x, x_target), edge_index)            
        **if** i != self.num_layers - 1:                
            x = F.relu(x) 
            x = F.dropout(x, p=0.5, training=self.training) 
    **return** x.log_softmax(dim=-1)*

现在你可以看到adjs中的三个二分图分别被传递到三个卷积层。

这种方法的一个可能的缺点是,我们实际上并没有从训练数据中为每一批抽取子图。采样器试图模仿在训练数据集网络上的 GNN 卷积,而不是在每次迭代中获取实际样本。这可能是有益的,这样就不会使你的训练循环产生偏差,但是如果你正在做一些简单的分类或者链接预测之外的事情,我会遇到一些关于索引的问题。然而,从另一个角度来看,与子图采样相比,这种方法可能是有益的,因为我们减少了训练数据的偏差。

GraphSAINTSampler 允许您处理原始训练数据集的实际子图,并重写从 0 到 n 的节点 id,其中 n 是子图中的节点数。

GraphSAINTSampler 父类有三个子类:GraphSAINTNodeSampler、GraphSAINTEdgeSampler 和 GraphSAINTRandomWalkSampler。这些类中的每一个都使用它们各自的采样方案来计算节点的重要性,这转化为采样的概率分布[5]。这个初始采样器就像一个预处理步骤,估计被采样的 V 中的节点 vE 中的边 e 的概率。该概率稍后被用作子图的归一化因子[4]。

下面是 PyTorch Geometric 中的 graph_saint 示例中的 GraphSAINTRandomWalkSampler 示例。

*loader = GraphSAINTRandomWalkSampler(data, batch_size=6000, 
                                     walk_length=2, num_steps=5, 
                                     sample_coverage=100, 
                                     save_dir=dataset.processed_dir, 
                                     num_workers=4)*

请记住,根据您设置超参数的方式,加载器可能不会加载整个数据集。batch_size超参数是每批抽样的行走次数。例如,使用 Citeseer 数据集和batch_size = 1walk_length = 1num_steps = 1,我们得到 1 个包含 2 个节点的数据样本。使用batch_size = 10,我们得到 1 个有 20 个节点的数据样本。使用batch_size = 100,我们得到大约 200 个节点——在每次迭代中可能会改变,例如 189、191 等。num_steps超参数是每个时期的迭代次数。因此,如果我们将num_steps增加到2,那么节点的数量将增加到大约 380,其中有一个batch_size = 100和一个walk_length = 1walk_length超参数指的是采样器每次随机行走的长度,返回节点数量的结果将根据网络的分类性而有很大的不同。这实际上编译了 PyTorch Geometric 的torch_sparse库中稀疏张量表示上的随机行走的 C++实现(以及随后的 cuda 实现)。请参阅关于使用自定义 C++操作符扩展 TorchScript 的 PyTorch 教程了解更多信息。

Citeseer 数据集上各种超参数的 GraphSAINT 数据加载器中的节点数

GraphSAINT 论文最重要的方面之一是计算归一化统计量,以减少每个子图样本中的偏差。GraphSAINTSampler 计算这些统计数据,这部分由sample_coverage超参数控制。得到的统计数据作为data对象的一个edge_norm属性返回。我们可以在用edge_norm属性正向传递我们的图形神经网络之前修改edge_weight属性。

*edge_weight = data.edge_norm * data.edge_weight            
out = model(data.x, data.edge_index, edge_weight)*

[1]费伊先生。 PyTorch 几何。图形深度学习库。

[2] W. Hamilton 等人,大型图上的归纳表征学习 (2017)。

[3] W .胡等著开图基准 (2020)。

[4] H. Zeng 等, GraphSAINT:基于图抽样的归纳学习方法 (2020)

5 布朗斯坦先生。简单可扩展图形神经网络 (2020)。

数据科学的抽样方法

原文:https://towardsdatascience.com/sampling-methods-for-data-science-ddfeb5b3c8ed?source=collection_archive---------30-----------------------

梅根·麦克莱恩在 Unsplash 上的照片

从人群中获取样本的最好方法是什么?

语境

在大多数研究中,分析整个人口是相当困难的(或者有时是不可能的),所以研究人员使用样本来代替。在统计学中,调查抽样是为了进行调查而从我们的总体中获取样本的过程。作为数据科学家,我们通常使用以前收集的数据,所以我们不会花太多时间考虑如何实际做到这一点。然而,正如我们将在本文中看到的,我们的数据可能会有不同的偏差,这取决于它是如何采样的,因此您可以更好地理解每个采样设计的含义。绘制这些样本的方法有很多种,根据上下文的不同,有些方法可能比其他方法更好。

概率 x 非概率

抽样设计有两大类:概率和非概率。在概率抽样中,人口中的每个元素都有一个已知的非零的概率存在于样本中。这种方法通常是优选的,因为其属性,例如偏差采样误差,通常是已知的。在非概率抽样中,人口中的一些元素可能没有被选取,样本在总体上不具有代表性的风险很大。然而,在某些情况下,概率抽样有时是不可能的,或者非随机抽样可能更便宜。

现在让我们来看看每个类别中的一些不同的抽样设计及其特性。

概率抽样

无替换的简单随机抽样

这可能是最明显的抽样方法:如果你有 1000 个人,而你只能分析 100 个人,那么你将一次随机选择一个人,直到你有 100 个样本。这将使每个个体在样本中具有相同的概率。

SRSWR 是一个无偏抽样设计,这意味着我们期望从样本中计算出的参数是无偏的。这通常是更好的抽样设计,但有一个小小的警告:你可能会得到一个非常糟糕的样本,完全是运气不好,得到的结果根本不能代表你的总体。在这种情况下,对你的样本进行分层可能会有所帮助(我们稍后会谈到这一点)。

然而,在实践中,获得一个真正简单的随机样本并不那么简单。例如,对于选举投票,你是怎么做的?你不可能真的有一份全国所有人的名单来随机选择。例如,您可以列出所有可用的个人电话号码,并从中进行选择。我的观点是,要做到这一点,你可能需要一份全部人口的名单——如果你在街上随机采访人们,这实际上不是完全随机的:根据你选择去的地点,你的样本可能会产生不同的结果。

泊松采样

在泊松抽样设计中,你的总体中的每个元素都将经历一个伯努利试验,以确定它们是否会出现在样本中。如果总体中每个元素的概率都相同,这就是所谓的伯努利抽样的特例。这也取决于你是否有一份人口中所有元素的清单。假设您有一份您所在国家的所有公司的列表,并且您想要对它们进行调查。举例来说,你可以根据它们的规模,为它们中的每一个分配一个概率 p ,或者甚至为每一个分配一个不同的概率(你可能想给更大的公司更大的权重)。注意,在这种情况下,你无法事先知道样本的确切大小——这就是我们所说的随机大小抽样设计

分层抽样

在某些情况下,根据某些特征对人口进行分层可能会非常有用。假设你想对你公司的 1000 名员工做一个调查,看看他们对自己的工作有多开心,但是你只有时间去采访其中的 100 人,所以你取了一个样本。有了 SRSWR,你可能会有 50 个会计人员,却没有数据科学家。这会让你认为你公司的员工比他们实际上更不快乐,因为数据科学家是他们工作中最快乐的人,而会计师……嗯,他们是会计师。在这种情况下,您可以做的是将您的人口划分为几个部门,然后从每个部门中随机抽取样本,抽取与部门规模成比例的样本。

这种方法在某些情况下非常有用:

  1. 阶层之间的差异很小(你知道,从以前的研究来看,同一部门的人在工作幸福感方面感觉差不多)
  2. 阶层之间的差异很大(你在工作中的快乐程度很大程度上取决于你所在的部门)

然而,在实践中,实现起来可能是昂贵且复杂的。因为它需要你人群的先前信息,当你在更广泛、更昂贵的研究之间进行更小的研究时,它可能是有用的。:如果您的国家每 10 年进行一次人口普查,并且您需要每 5 年一次的中间信息,您可以使用您的人口普查数据来帮助进行较小的中间研究。

非概率抽样

志愿者抽样

这是一种广泛使用的方法:当你在一个脸书小组上发布一份调查表格,并要求人们为你填写时,你会得到这种结果。这很容易,也很便宜,但它会导致很多偏见,因为你实际上是在脸书取样的人,看到了你的帖子,最重要的是:他们愿意为你填写表格。这可能会对喜欢你的人或有足够时间填写表单的人进行过度采样。

它可以作为第一个验证步骤,看看以后是否有兴趣追求更昂贵的方法。

抽样断定法

在这个抽样设计中,您将根据您现有的领域知识选择您的样本。如果你想为一门新的编程在线课程调查潜在客户,你可能已经知道什么样的人会喜欢这门课程,并开始在 LinkedIn 上寻找他们。

不言而喻,这种方法容易产生你自己的偏差,你不应该根据它的结果得出确定的结论。它可以在与志愿者取样相同的情况下使用。

结论

现在你知道了一些最常见的抽样设计,何时使用它们以及它们的注意事项。调查抽样本身是一个完整的专业领域,对于那些为政府机构工作的统计人员来说特别有用,但数据科学家了解基础知识以了解他们的收集方法的含义或自己进行调查是有好处的。

一旦你采样了你的数据,然后呢?嗯,你需要应用一些特征工程来理解它。此外,您可能会喜欢这篇关于数据科学家项目管理工作流程的文章。

随时联系我关于LinkedIn如果你想进一步讨论,这将是一种荣幸(老实说)。

旧金山阿片类药物危机和毒品问题及其对公共安全的影响

原文:https://towardsdatascience.com/san-franciscos-opioid-crisis-and-drug-problem-and-effects-on-public-safety-1ffad5040e0f?source=collection_archive---------43-----------------------

使用机器学习和分类技术的旧金山毒品和犯罪分析

禁毒战争是一个阶段,指的是政府主导的行动,旨在通过加大和加强对违法者的惩罚来阻止非法毒品的使用、分销和交易。这场运动始于 20 世纪 70 年代,至今仍在发展。因此,近年来美国许多州正在经历阿片类药物危机。有一个正在进行的辩论,阿片类药物危机是墨西哥和中美洲移民的产物,而不是大型制药公司的放松管制和私人医疗保健系统的失败。因此,在这种情况下,旧金山正面临着严重的毒品问题和阿片类药物危机。我与卡提克亚舒克拉郝舒一起选择了这个数据集来执行 EDA,并应用数据科学分类算法,如 XGBoost、KNN、回归、朴素贝叶斯和随机森林,以从可视化结果中获得洞察力。旧金山(SF)在推动进步的公共卫生解决方案方面有着悠久的历史,包括医用大麻和针头交换,在这两者都合法或被广泛接受之前。这是如此不成比例,以至于加州通过了一项法案,允许旧金山开设安全注射点(SIS)。

旧金山阿片类药物危机

旧金山(SF)在推动进步的公共卫生解决方案方面有着悠久的历史,包括医用大麻和针头交换,在这两者合法或被广泛接受之前。这是如此不成比例,以至于加州通过了一项法案,允许旧金山开设安全注射点(SIS)。

安全注射点(SIS):

安全注射场所是受医疗监督的设施,旨在提供一个卫生和无压力的环境,在这个环境中,个人能够静脉注射非法娱乐性药物,并减少公共药物使用造成的滋扰。它们是针对毒品问题的减少危害方法的一部分。北美的第一家 SIS 网站于 2003 年在温哥华市中心东区(DTES)附近开业。

潜在问题:

比较不同社区的犯罪类型。你可能会被袭击的前五个街区是哪里?某些“成对”犯罪是否经常在某个街区同时发生?

为旧金山政府确定安装 SIS 的潜在社区

目标变量:

  1. 2003 年至 2018 年犯罪类型与邻里之间的相关性
  2. 某些类型的犯罪是经常一起发生,还是特别一起发生
  3. 2013 年至 2018 年使用的药物类型与社区之间的相关性
  4. 确定旧金山政府建设安全注射点的潜在社区/区域
  5. 根据提供的空间和时间特征预测犯罪的类型/类别

数据:

我们的数据来自旧金山警察局的数据库。这是从 2003 年 1 月到 2018 年 5 月的犯罪历史数据。数据集有 13 列和 2215024 行。

数据集元数据

分析和推论:

  1. 我们统计了每一类犯罪的发生次数,并绘制了图表。由于分布是偏斜的,我们通过取对数来标准化它。以下是标准化的犯罪类别分布。

2.有 915 个不同的犯罪描述,这些描述决定了犯罪是否与毒品有关。因此,我们计算了每种犯罪描述的发生率,并过滤掉低于 97%的犯罪描述,剩下的用于创建聚类图。

3.创建了一个聚类图,以探索不同类型的犯罪分布(即每个 PD 区(即警区)的犯罪分布)。同样,由于这种分布是偏斜的,它影响了我们的模型(如下所示)。人们可以观察到侠盗猎车手是一个离群值,除此之外,我们没有获得任何信息,因此需要规范化。

4.因此,使用最小-最大归一化进行归一化,因为记录日志不会保留一个特征相对于另一个特征的大小比例。下面是标准化的聚类图:

在这里,我们可以观察到以下情况:

(一)南部:极高的盗窃发生率,包括从汽车中盗窃
(二)湾景:重大的暴力和威胁事件

(iii) Tenderloin: 似乎是一个异常值,拥有毒品用具的发生率极高。Tenderloin 似乎是安装 SIS 的潜在候选人,尽管它可能是假阳性(即这些可能是由于大麻)。因此,人们需要更深入地研究。

5.接下来,我们使用一些正则表达式和字符串模式匹配过滤了与毒品相关的犯罪,并统计了每个与毒品相关的描述的出现次数。同样,分布是倾斜的,它影响了下面显示的聚类图。人们可以观察到 Tenderloin 是一个异常值,我们没有获得其他信息。

6.下面是标准化的聚类图,显示了各县与毒品相关的犯罪的分布情况。

推论:

从上面的聚类图,我们可以清楚地得出结论,田德隆,南部,使命和北部是安装 SIS 的最佳候选人

7.此后,进行时间序列分析,以分析阿片类药物随时间的变化趋势。首先,我们压缩了大量与毒品相关的犯罪描述,以创建阿片类药物组/特征(即巴比妥酸盐特征、可卡因特征、大麻特征、冰毒特征等)。然后,我们为每个组创建了一个 30 天的窗口,并统计了从 2003 年 11 日到 2018 年 5 月 15 日每个月的 30 天窗口内每个组的事件数量。

为了去除月份的周期性特征,我们将它们从 0 到 187 进行索引。下面是一个堆积直方图,代表这些趋势。如你所见,与冰毒和海洛因相关的事件显著上升。

8.为了使趋势更加清晰,以下是 2003 年至 2018 年期间类阿片趋势的标准化分布。

人们可以观察到,与裂纹相关的事件在此期间有所减少。同样,大麻相关事件在 2016 年合法化后有所下降。但是与冰毒和海洛因相关的犯罪显著上升——这是断定这是一种流行病的实质性证据。

型号选择:

  1. 我们从一个二元逻辑回归模型开始,该模型预测了犯罪是否与毒品有关的可能性,给出了某些地理坐标。这将有助于旧金山政府根据预测向某些地区分配资源。我们最初的准确率是 94%,高得“令人怀疑”。因此,我们检查了偏见——我们发现我们的目标阶层是不平衡的——也就是说,与毒品相关的犯罪相比,与毒品无关的犯罪要多得多。因此,我们使用 SMOTE 对目标类进行了过采样。在此之后,我们的准确率为 77%,这是有意义的,尽管 AUC 从 0.68786 上升到 0.69875。

目标类别分布(过采样前)

目标类别分布(过采样后)

这种不平衡的分布可以通过欠采样或过采样来解决。选择过采样的原因是,尽管欠采样可能有效,但在此过程中会丢失许多有用的数据。这些数据对逻辑回归算法是有意义的。

过采样技术是 SMOTE ,代表合成少数过采样技术。原因有二。首先, SMOTE 是一种非常常见的过采样技术。其次,当对不平衡数据集中的少数类进行过采样时,可能发生的情况是,模型最终会学习到少数几个示例的太多细节,通常是通过随机添加少数类数据这样的简单方法。另一方面,SMOTE 学习少数数据点的邻域的属性。这样,模型可以更好地泛化。

过采样前的 AUC

过采样后的 AUC

推论

我们意识到二元逻辑回归(即二元 LR)不足以解决我们的问题。例如,如果我们使用这些预测来分配政府资源,那么我们可能会有很高的假阳性率。由于仅仅找到非毒品/毒品相关犯罪高发的地理坐标/区域是不够的,我们需要更深入地研究,例如,我们希望在谋杀率高于纵火率的地方分配更多的资源。于是我们转而使用其他多类模型,如多项逻辑回归(多项 LR)XGBoostKNN随机森林。

项目:

结论:

XGBoost 表现最好。上述模型预测了每一类犯罪的可能性,给出了地理坐标,以及犯罪是在白天还是在晚上发生。我们可以使用这些可能性/概率&在给定的地理坐标上聚合,来识别社区并相应地分配政府资源。

权衡:

  1. 特征缩放:我们每次归一化数据时都使用最小-最大归一化,虽然它不能很好地处理异常值,但另一方面它保留了原始的缩放比例。在对数/z 分数归一化的情况下,异常值得到很好的处理,但不会保留原始比例。对我们的分析来说,保留比额表更重要。因此,总会有权衡。
  2. AUC/ROC 与 Precision/Recall (PR): 我们使用 PR 作为我们的评估指标。由于我们的目标阶层不平衡,我们希望我们的模型考虑到这一点。

在现实世界中,由于正负样本非常不均匀,所以更多地使用 PR 曲线。ROC/AUC 曲线不能反映分类器的性能,但是 PR 曲线可以具有更好的可解释性。

AUC(ROC 下面积)是有问题的,尤其是当数据不平衡时。正面例子的出现率相对较低。用 AUC 来衡量分类器的性能,问题是 AUC 的增加并不能真正反映一个更好的分类器。这只是太多反面例子的副作用。由于我们更关心真阳性的实际预测,而不是模型的“整体”性能,因此 PR & F-1 评分似乎更合适。

代码:

您可以在我们的 github 资源库查看整个项目的代码和 jupyter 笔记本:

[## Gandalf 1819/SF-阿片样物质-危机

旧金山(SF)在推动进步的公共卫生解决方案方面有着悠久的历史,包括医疗…

github.com](https://github.com/gandalf1819/SF-Opioid-Crisis)

除此之外,您还可以在这里进一步查看 Tableau 分析:

[## Tableau 公共

编辑描述

public.tableau.com](https://public.tableau.com/profile/chinmaynw#!/vizhome/SFDrugOpioidCrisis/Sheet1)

参考资料:

[1]https://www . kqed . org/news/11766169/旧金山-芬太尼-死亡-几乎-150

[2]https://www . SF chronicle . com/Bay area/article/Bay-Briefing-芬太尼-流行病-恶化-in-San-14032040.php

[3]https://www . business insider . com/San-Francisco s-dirty est-street-has-a-drug-market-and-pills-of-poop-2018-10

[4]https://www . SF chronicle . com/bayarea/article/California-bill-allowing-San-Francisco-safe-13589277 . PHP

[5]https://data . SF gov . org/Public-Safety/Police-Department-Incident-Reports-Historical-2003/tmnf-yvry/data

https://data.sfgov.org/d/wkhw-cjsf

[7]https://www . quora . com/What-the-meaning-of-min-max-normalization

基于 Python 的 Plotly 的桑基图基础

原文:https://towardsdatascience.com/sankey-diagram-basics-with-pythons-plotly-7a13d557401a?source=collection_archive---------1-----------------------

可视化流量和比较比例的绝佳选择

在本文中,我将介绍使用 Plotly 和 Python 绘制桑基图的基础知识。

它们是一个方便的图表,用于可视化任何一种可测量的流量——一些例子是旅行者拼写者金钱的流量。

桑基图显示了蒸汽机的能效,1898 年

这张图表的制作归功于爱尔兰船长马修. H. P. R .桑基,他用它来形象化蒸汽机的能源效率。

桑基图的思想类似于网络图,其中链接连接节点。

主要区别在于,在桑基的模型中,链接具有不同的宽度,它们共同编码一个可测量的变量。

亲自动手

对于下面的例子,我将结合 Jupyter Lab 使用 Plotly 来探索如何创建一个 Sankey。

import plotly.graph_objects as go

用 Plotly 构建图表有不同的方法;我将使用图形对象,用 Jupiter 小部件可视化图形,并为最终的可视化导出一个 HTML。

逻辑

构建一个 Sankey 可能相当困难,特别是如果您有太多的节点和连接,我将使用列表作为例子来简化它,但是您可以用 JSON 文件或 Pandas 数据帧来修改这个逻辑。

我们将使用go.Sankey来构建图表,这需要一个link

那个link是一本包含我们想要绘制的连接数据的字典。

source = [0, 0, 1, 1, 0]
target = [2, 3, 4, 5, 4]
value = [8, 2, 2, 8, 4]

源和目标是 Plotly 将连接的节点的索引列表,值是将定义这些连接的宽度的数字列表。

link = dict(source = source, target = target, value = value)
data = go.Sankey(link = link)
print(data)

我们将 Sankey 对象保存到一个名为 data 的变量中,现在我们可以将该数据传递给一个图形;

fig = go.Figure(data)

并显示图表。

fig.show()

就是这个想法。让我们试着识别这些节点,这样我们就能更清楚地看到什么在连接什么。

我们需要一个包含节点数据的字典,它应该包含一个带有标签的列表。我们还可以添加更多的参数,比如pad来定制节点之间的距离,或者thickness来定义它们的句柄的大小。

# data
**label = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE"]**
source = [0, 0, 1, 1, 0]
target = [2, 3, 4, 5, 4]
value = [8, 2, 2, 8, 4]# data to dict, dict to sankey
link = dict(source = source, target = target, value = value)
**node = dict(label = label, pad=50, thickness=5)**
data = go.Sankey(link = link, **node=node**)# plot
fig = go.Figure(data)
fig.show()

那更好理解。我们可以阅读列表,并想象它们之间的联系。

用例

现在让我们用一些真实的数据来尝试一下,我将尝试改变温哥华 2020 年预算中的这种可视化。

温哥华 2020 年预算,第。6

我希望看到资金从运营支出流向地区和服务的比例。

我将从总支出的一个节点开始,它将连接到四个领域,然后将链接到它们的细分领域——让我们快速勾画一下这个想法。

用 sketch.io 绘制

我已经使用了百分比来查找值,并为链接构建了以下列表。

source = [0, 0, 0, 0,       # Op Expeditures
          1, 1,             # Public Safety
          2, 2,             # Eng n Util
          3, 3, 3, 3, 3, 3, # Community Serv
          4, 4, 4]          # Corp Supporttarget = [1, 2, 3, 4, 
          5, 6,
          7, 8, 
          9, 10, 11, 12, 13, 14, 
          15, 16, 17]value = [484500, 468350, 355300, 306850, 
         339150, 145350, 
         371450, 96900, 
         129200, 80750, 48450, 48450, 32300, 16150, 
         113050, 129200, 64600]

让我们也为节点定义标签。

label = ['Operating Expeditures', 
         'Public Safety', 
         'Engineering and Utilities', 
         'Community-Related Services', 
         'Corporate Support',
         'Police', 
         'Fire',
         'Utilities', 
         'Engineering Public Works',
         'Parks and Recreation', 
         'Arts, Culture, and Community Services',
         'Library', 
         'Development, Buildings, and Licensing',
         'Planning, Urban Design, and Sustainability', 
         'Other',
         'Corporate Support', 
         'Debt and Capital (Non-Utility)', 
         'Contingencies and Transfers']

一旦我们准备好数据,剩下要做的就是定义我们的图形和绘图。

很好,越来越好了。

我们可以在图表中使用另一种编码,即颜色。

我们可以定义节点和链接的颜色;为此,我们需要一个每个元素有一种颜色的列表。

color_node = [
'#808B96', 
'#EC7063', '#F7DC6F', '#48C9B0', '#AF7AC5',
'#EC7063', '#EC7063',
'#F7DC6F', '#F7DC6F',
'#48C9B0', '#48C9B0', '#48C9B0', '#48C9B0', '#48C9B0', '#48C9B0',
'#AF7AC5', '#AF7AC5', '#AF7AC5']color_link = [
'#EBBAB5', '#FEF3C7', '#A6E3D7', '#CBB4D5',
'#EBBAB5', '#EBBAB5',
'#FEF3C7', '#FEF3C7',
'#A6E3D7', '#A6E3D7', '#A6E3D7', '#A6E3D7', '#A6E3D7', '#A6E3D7',
'#CBB4D5', '#CBB4D5', '#CBB4D5']

在这里,我试图突出每个支出的区域,我对节点和链接使用相似的颜色,一个比另一个亮一点。

几乎准备好了,剩下要做的就是添加标题、调整细节和导出 HTML 文件。

我们可以在绘图之前更新图形的布局,这允许我们更改字体、背景颜色、定义工具提示上的信息等等。

fig.update_layout(
    hovermode = 'x',
    title="Vancouver Operating Expeditures by Area of Service",
    font=dict(size = 10, color = 'white'),
    paper_bgcolor='#5B5958'
)

一旦可视化准备就绪,我们就可以将其导出为 HTML。这可能是最自然的部分,我们所要做的就是调用.write_html(‘filename.html’)

这里你可以看到另一个例子,我增加了营业收入。

https://thiagobc23.github.io/plotly-sankey/van.html

我试图移除白色的文字阴影,但是我在 Plotly 中找不到解决方案,我找到的最好的答案是覆盖 CSS

你可以在这里找到这篇文章的代码,在这里找到带有交互式 Sankey 的网页。

感谢你花时间阅读我的文章,我希望你喜欢它。

更多资源: Plotly —图形对象
Plotly —桑基图

桑坦德银行客户满意度——使用 Python 的自我案例研究

原文:https://towardsdatascience.com/santander-customer-satisfaction-a-self-case-study-using-python-5776d3f8b060?source=collection_archive---------35-----------------------

目录

  1. 商业问题
  2. 使用机器学习来解决业务问题
  3. 评估指标(曲线下面积)
  4. 探索性数据分析
  5. 特征工程
  6. 现有解决方案
  7. 我的模型实验
  8. 摘要、结果和结论
  9. 未来的工作
  10. 链接到我的个人资料— Github 代码和 Linkedin
  11. 参考

加勒特·帕克在 Unsplash 上拍摄的照片

1.商业问题

本案例研究基于 2016 年开展的 Kaggle 竞赛。客户满意度是当今每个公司最重要的关键绩效指标之一,被视为公司成功的关键因素。不开心的顾客不会留下来。更重要的是,不开心的顾客很少在离开前表达他们的不满。桑坦德银行是一家西班牙跨国银行和金融公司,业务遍及欧洲、北美、南美和亚洲。在桑坦德银行举办的这场 Kaggle 竞赛中,我们需要根据公司提供的功能,提前预测客户是否对他们的服务不满意。这将有助于他们在客户离开之前采取主动措施提高客户满意度。

2.使用机器学习来解决业务问题

这个问题是一个分类建模任务,以确定客户(数据点)是否不满意。

我们有两个文件:test.csv 和 train.csv,其中包含大约 370 个匿名的特征和一个作为目标的依赖特征。每个数据点代表一个客户,如果客户对公司的服务不满意,TARGET 中的值为 1,如果客户满意,TARGET 中的值为 0,并且数据集严重失衡。

3.评估指标(曲线下面积)

这里使用的度量是 ROC (接收器工作特性)曲线下的面积,ROC(接收器工作特性)曲线是在不同阈值下真阳性率(灵敏度或召回率)(TPR)和假阳性率(FPR)之间的图下的面积。AUC 有助于确定模型是否擅长区分类别。AUC 越高,模型预测 0 为 0 和 11 的能力越强。AUC 为 0.5 意味着模型进行随机猜测(随机模型),而 AUC 为 0 意味着模型预测 0 为 11 为 0,而 AUC 为 1 是理想的分类器。

来源 — AUC 是 ROC 曲线下的面积

这里有一篇关于 AUC 的非常好的文章。

[## 理解 AUC - ROC 曲线

在机器学习中,性能测量是一项基本任务。所以说到分类问题,我们可以…

towardsdatascience.com](/understanding-auc-roc-curve-68b2303cc9c5)

4.探索性数据分析

我们将首先深入研究数据集,并通过可视化观察所有特征,这将有助于我们获得结论以及特征工程的想法。总共有 370 个特征都是匿名的(不包括“目标”特征)。我们能够从文献综述中以及通过观察值的分布来获得匿名化特征所传达的信息。

由于有许多特性,我们将检查具有零方差的特性(不包含任何信息,即仅存在 1 个唯一值),如果发现,将删除它们。

发现 34 个特征具有零方差,并且这些都被移除。

我们现在将检查重复的特征(即它们在数据点上共享相同的值,不需要相同的特征名称)。

发现 12 个特征是重复的,其中 6 个被移除。

我们现在将移除所有的稀疏特征(具有很少信息的特征)。设置一个条件,使得如果特征具有为 0 的 99%的值,则它被认为是稀疏特征。

也没有发现缺失值。

去除不必要的特征后的最终特征数是 142。

现在,我们将探索每一项功能。

4.1 目标

我们可以看到数据集非常不平衡,只有 3.96%的客户不满意,96.04%的客户满意。

4.2 var3(地区)

通过查看其他文献以及当前唯一值的数量(=208),此功能可能包含客户所在地区的信息。此功能中还有一个异常值-999999,它可能缺少值。除了异常值,这些值的范围从 0 到 208。发现最常见的唯一值是 2(对于训练和测试数据,大约 97%)。首先,我们将把所有-999999 转换为-1 值,并且只考虑不包括“2”的值来研究值计数。

“var3”的值计数,不包括最常见的值“2”

由于查看值计数,我们可以看到-1 仅代表整个数据集的一小部分(即使在排除 2 之后)。因此,我们将用此功能最常用的值“2”替换缺失的值。

4.3 var15(年龄)

在训练数据中发现 var15 的最小值为 5,最大值为 105。因为 var15 的值是从 5 到 105,所以可以有把握地认为该特征暗示了客户的年龄。文献综述也证明了这一点。

训练和测试数据具有相似的分布,两者都由最年轻的客户组成。

不满意的顾客的最小年龄是 23 岁。似乎每个年轻顾客(23 岁以下)总是很满意。因此,我们可以在特征工程期间创建一个新的特征,它本质上是告诉客户是否在 23 岁以下。

4.4 var38(抵押价值)

从 kaggle 的各种文献可以推断,var38 可能是抵押价值。

var38 的值计数

这里我们不能得到任何信息,因为一个值有很高的频率。我们将打印出每个百分点值。

以百分位数表示的 var38 值

我们可以看到,0 百分位值和 10 百分位值之间有着巨大的差异。这与 90 百分位值和 100 百分位值的情况相同。现在,我们将检查低于 97.5 百分位的所有值的 var38 分布图(高于 97.5 百分位的值 1 占主导地位,无法提取可用信息)

我们可以看到上面的图是右偏的,峰值在 100,000 到 150,000 之间。我们可以应用对数变换并检查结果分布。

应用对数变换后

我们可以看到它看起来类似于正态分布。由于机器学习模型在正态分布上工作得更好,因此我们可以为该特征创建具有对数变换和不具有对数变换的新数据集,并在特征工程阶段比较性能。

4.4 带有关键字的功能

有几个特征有特定的关键字,它们是:“imp”、“ind”、“num”、“saldo”。这些关键字被发现是从一些文献中获得的一些西班牙语单词的缩写形式。

“num”要素在数据集中具有最高的制图表达。

现在我们要做的是通过为每个关键字随机选择 2 个特征来逐个探索每个关键字的特征,然后我们将得出结论。

4 . 4 . 1‘imp’特性

imp' likey 是 importe 的缩写词,在西班牙语中表示数量(从文献综述中推断)。带有“imp”关键字的特征的总数是 14。通过查看包含“imp”关键字的两个随机选择的特性的值计数,我们可以看到这两个特性最常出现的值都是 0。0 在这两个特征中占了 87%以上。

随机选择的“imp”特征训练和测试数据的频率图

对数变换后不包括 0 的随机选择的“imp”特征的分布

同样在对数变换之后,分布(不包括 0)变得很像高斯分布。因此,我们可以为所有“imp”功能制定一个策略。

imp 特性的策略:

  1. 我们可以创建一个具有对数变换(不包括 0) imp 特征的新数据集。
  2. 我们还可以创建另一个没有应用转换的数据集(保持原样)。

4 . 4 . 1“saldo”功能

具有 saldo 关键字的特征的数量是 26。现在,我们将像处理“imp”特征一样处理数据。通过查看这两个特征的值计数,我们可以看到 0 也是最常见的值(大约占整个训练数据集的 95%)。

训练和测试数据的频率图

发现所有“saldo”特征都是连续的。

对数变换后的随机“萨尔多”特征(不包括零)

同样在这里(类似于“imp”功能),我们可以创建新的数据集,将对数变换应用于 saldo 功能,除了 0 值将保持不变。

“萨尔多”特色战略:

  1. 我们可以使用经过对数变换(不包括 0)的 saldo 要素创建一个新数据集。
  2. 我们还可以创建另一个没有应用转换的数据集(保持原样)。

4.4.3“数量”特征

“num”可能是数字的缩写形式。探索所有“num”特征的值计数,我们可以说每个具有“num”关键字的特征本质上都是分类的。

一些带有“num”关键字的特性及其对应的唯一值的数量

为带有“num”关键字的功能找到的唯一值的最大数量是 172(总数据点约为 70k),最小数量是 2。我们将为“数字”功能采用一种策略:

针对“数字”功能的战略

  1. 我们将对允许的唯一值的最大数量设置一个阈值,以便将一个特征定义为分类特征。我们将阈值设置为 10。
  2. 我们可以使用响应编码、一个热编码创建新数据集,并保留“num”个要素的要素不变。(这只适用于根据上述规则定义的分类特征)

4 . 4 . 3“ind”特征

“ind”功能可能指指示器。查看值计数后,所有具有“ind”特征的特征只有 2 个唯一值。

针对“ind”特性的策略

我们保留这些功能不变。

5.特征工程

从文献回顾中可以理解,对于特定数据点,不同特征上零的出现次数是识别客户是否不满意的一个重要特征。因此,我们将为预处理后的数据创建要素,如下所示:

no_zeros: 一个数据点的不同特征中出现的零值数量

no_nonzeros: 一个数据点的不同特征中存在的非零值的数量

现在,我们将添加一个特性,该特性计算不同关键字特性(' saldo ',' ind ',' num ',' imp') 中的个零和非零值:

现在,我们将对具有 50(不含)到 210(含)个唯一值的所有特性的每个唯一值取所有“saldo”和“imp”特性的平均值,并将其添加为特性。

K 均值聚类:我们将在对这个特定步骤应用标准化之后,添加 k = 2,4,6,8,10 的 K 均值聚类值作为特征。注意:我们在这个步骤之后获得的特征是不标准化的,因为我们需要应用对数变换。我们应用标准化来获得 K-means 聚类特征。

移除与目标高度相关的特征和低相关的特征:我们将移除彼此高度相关的所有特征,保留一个。我们将移除与“目标”特征具有低相关性的所有特征。对于移除高相关特征,阈值保持为任何高于 0.95 相关值的特征,而对于移除与“目标”的低相关值,阈值是任何低于 10**-3 相关值的特征。

创建新数据集:

现在,我们将使用日志转换创建新的数据集。我们将对功能' var38 ',所有' saldo '和' imp '功能应用对数变换,零值除外,零值将保持不变。

现在我们有两个数据集,一个是对数转换的,另一个是普通数据集(没有任何对数转换)。现在,我们将对这些应用一个热编码响应编码,并创建新的数据集。这里,编码应用于唯一值的数量在 3 和 10 之间(包括 3 和 10)的那些特征。这里有一篇关于如何计算 响应编码 的文章。对于响应编码,我使用拉普拉斯平滑来平滑数据(假设在测试数据中有一个在训练数据中没有看到的类别值,我们不希望该唯一值的概率为零,所以我们应用平滑)。通过手动检查从一组阿尔法中随机选择的特征的响应编码值,根据结果编码值的变化程度来找到最佳阿尔法。

最后,创建了 6 个数据集,它们是:

  1. 正常
  2. 普通带一个热编码
  3. 正常带响应编码
  4. 测井转换
  5. 用响应编码转换的日志
  6. 用一个热编码转换的日志

现在我们将标准化所有数据集。标准化后,我们将应用主成分分析(这里 n=2)并将其作为特征添加到每个数据集。

最初,我们只有 142 个用于训练和测试数据的特征。现在我们已经创建了 6 个数据集,要素的最终数量如下:

  1. 正常:359 特性
  2. 普通带一个热编码:446 个特征
  3. 正常带响应编码:374 特征
  4. 测井转换:359 个特征
  5. 响应编码转换的日志:374 个特征
  6. 一次热编码转换的日志:446 个特征

6.现有解决方案

  1. 德里克·范登艾森。(2017).桑坦德客户满意度 : 在预处理中,他去除了方差为零的特征、稀疏特征以及重复的列条。现在,在标准化之后,他移除了具有 0.99 或更高的高相关值的所有第一对相关特征。特征工程:从 var3 (Nationality)特征中创建新的特征,其本质上携带了客户是否来自最普通的国家或具有最普通的价值的信息。var15(年龄)、var36 和 var38(抵押价值)也是如此,var15 的值为-999999,这可能意味着缺少值。创建的另一个特性告诉我们数据点的值是否为-999999。创建此功能后,var3 中的所有-999999 值都被替换为最常用的值。对 var36 也是如此,因为它的值为 99,与其他唯一值(包括 0、1、2 和 3)相差甚远。Var36 是一个热编码,因为其值表明它是分类的。另一个功能是计算数据点中 0 的数量(dmi3kno,2015)。此外,还为数据点功能创建了多个 1。作者创建了 3 个模型:逻辑回归、随机森林和 XGBoost ,具有超参数调整的 10 折分层交叉验证。发现度量 AUC 对于 XGBoost 是最好的。
  2. kweonwooj 用 Python : 重新实现 pjpan 的第 34 位解(用 R 代码),在这次重新实现中,用户删除了所有有常量(只有 1 个唯一值)的特性,删除了所有重复的特性,保留了一个。将 9999999999 和-99999 等所有极值转换为-1。移除稀疏要素,条件是如果要素的 99%值为 0,则该要素被声明为稀疏。对于这组列“num_meses_var8_ult3”、“num_meses_var13_largo_ult3”、“num_op_var40_comer_ult1”,这组列中的所有 meses 列的值都被更改为 int 类型。而集合中的所有其他特征的值除以 3(这些特征的唯一值为 3 的倍数)。数据在 XGBoost 上用 10 个分层的 k 折叠进行训练。预测的测试值是所有这些模型测试预测值的平均值,这些预测值符合 10 个分层的 k 倍。

对于我的方法,我已经创建了 6 个数据集,这在特征工程部分进行了详细说明,并将在这 6 个不同的数据集上建模。

7.我的建模实验

我有 6 个数据集,所以我要做的是,对每个数据集,我将在逻辑回归、决策树、随机森林、XGBoost 和 LightGBM 上建模。首先,对数据集进行随机分割,对“目标”进行分层(确保训练和测试中唯一“目标”值的比例相同),分割比例为 85:15。在数据集上建模后,我基于随机森林模型选择了个顶级特征,然后为每个数据集创建了包含前 250 个特征的新数据集。然后基于具有前 250 个特征的数据集创建随机森林、XGBoost 和 LightGBM 模型。使用随机搜索 CV 进行逻辑回归、决策树和随机森林找到最佳超参数。

用于随机搜索 cv 的不同超参数

这里 class_weight = 'balanced '处理了类的不平衡。对于决策树随机森林,在拟合具有最佳超参数的模型之后,在这些模型之上拟合校准的分类器(以获得概率值)。

这里的模型不是决策树分类器就是随机森林分类器

对于 XGBoostLightGBM,我手动更改了每个参数,并根据以下文章找到了最佳参数。对这两个模型使用 RandomSearchCV 耗费了大量时间。

[## 像老板一样在 Python 中微调 XGBoost

XGBoost(或极端梯度推进)不再被引入,只是在太多的数据科学中被证明是相关的…

towardsdatascience.com](/fine-tuning-xgboost-in-python-like-a-boss-b4543ed8b1e)

下面是 XGBoost 的代码,类似的还有 LightGBM (除了物镜在 LightGBM 中变成‘二进制’)。

不同数据集的组合 AUC 图如下所示:

日志数据集的 AUC 分数(已排序)

正常数据集的 AUC 分数(已排序)

基于验证 AUC 的每个数据集的最佳模型

然后,我们根据集合堆叠的验证 AUC,从每个数据集中选择最佳模型。我们不从所有模型中取出最好的模型,因为对于堆叠和集成,模型之间的差异越大(这确保我们从不同的数据集取出模型,否则在相同数据集上训练的模型将出现),最终集成/堆叠分类器的性能将越好(每个模型将是不同主题的专家,即例如:如果我们建立狗分类器模型,我们可以建立分类器,使得一个模型将检测尾部, 另一个模型将检测人脸等,因此结合这些模型将提供更好的性能,这就是我所说的不同模型的意思)。 所以数据集之间的差异越大,模型之间的差异就越大。

然后,获得的最佳模型在提供的整个训练数据集上进行训练(我们之前分割了数据),然后用于预测测试“目标”概率值。然后,获得的 6 个“目标”被用作创建新数据帧的特征。在该数据帧的顶部训练分类器(堆叠)。这里使用的分类器是逻辑回归(通过随机搜索 CV 找到的超参数),并且在 Kaggle 中提交“目标”的测试概率值。还提交了从 6 个最佳模型获得的 6 个测试概率值,并记录了它们的公开 AUC 分数。然后,基于获得的公开 AUC 分数,选择最佳的 2 个模型(这里是“log re(top 250) xgb”和“normal re(top 250)”),然后对概率值进行简单的平均集合。然后在 Kaggle 上提交获得的值,并记录公开的 AUC 分数。

8.摘要、结果和结论

带有 kaggle 公共 AUC 分数的模型

我们可以从上面的结果数据框中看到,拟合于对数变换响应编码(前 250 个特征)数据集的 XGB 模型和拟合于正常响应编码(前 250 个特征)数据集的 XGB 模型表现非常好。这两个模型的简单平均组合能够获得 0.82746 的 kaggle 公共 AUC 分数,仅比公共排行榜中的最高分数(0.84532)低约 2%。在 Log transformed(所有特征)上训练的 xgb 模型表现很差,因为它具有最差的公开分数。对分类特征进行响应编码极大地改进了模型,因为与在响应编码数据集上训练的模型相比,在其他数据集模型上训练的模型不会产生很好的结果。

日志响应编码(前 250 个功能)xgboost 模型(前 20 个功能)

正常响应编码(前 250 个功能)xgboost 模型(前 20 个功能)

此外,从所使用的两个集合模型的特征重要性图中,我们可以看到,在这两个模型中,具有最高重要性的共同特征是“var15”(代表从文献综述中推断的“年龄”),这意味着年龄是决定客户是否满意的重要因素。

9.未来的工作

  1. 我们可以使用贝叶斯优化来找到每个模型的最佳超参数。
  2. 我们也可以尝试实施深度学习模型来解决问题。

10.链接到我的个人资料— Github 代码和 Linkedin

你可以在这个 github 链接 上找到这个案例研究的完整代码。你可以在 Linkedinashishthomas7@gmail.com联系我。

11.参考

  1. https://www . Applied ai course . com/course/11/Applied-Machine-learning-course
  2. 桑坦德银行客户满意度调查(2017 年)
  3. kweonwooj 用 Python 重新实现 pjpan 的第 34 位解决方案(用 R 代码实现)
  4. https://medium.com/r/?URL = https % 3A % 2F % 2f towards data science . com % 2f fine-tuning-xgboost-in-python-like-a-boss-b 4543 ed 8 B1 e
  5. https://towards data science . com/mercari-price-re commendation-for-online-retail-sellers-979 C4 d07 f 45 c?gi=5873f2d314af
  6. https://medium . com/@ thewingdwolf . winterfell/response-coding-for-categorical-data-7bb 8916 c6dc 1
  7. https://towards data science . com/understanding-AUC-roc-curve-68b 2303 cc9 C5

如何发现财务数据中的欺诈和异常

原文:https://towardsdatascience.com/sap-data-analytics-examples-for-internal-audit-financial-tax-accounting-and-controlling-analysis-1fbc81d01eac?source=collection_archive---------47-----------------------

内部审计、财务和税务会计及控制等领域的一些数据分析示例

莎伦·麦卡琴在 Unsplash 上的照片

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

像 SAP 这样的软件,用于处理公司的所有业务流程,如记账、控制、销售等。托管大量数据,尤其是财务数据,这些数据可能会带来重要的见解,需要由商业智能、会计或内部审计等部门进行控制。下面对常见检查的概述应该提供了一个实用的分析用例列表。

可疑的变化

在 CDHDR 和 CDPOS 表的帮助下,您可以分析表中的变化,还可以识别可疑的过程,如订单中的不同值,该值从 20.000 €变为 19.999 €,刚好低于现有的限制。您需要的是对提到的表的访问(至少是读取),对这些表如何工作的理解和一些 SQL。

示例:SQL 结果—值的变化:

UDATE       |CHANGENR |VALUE_NEW |VALUE_OLD01/01/2020  |1234     |20.000    | 
02/03/2020  |1234     |19.999    |20.000

另一个例子是查看客户的信用限额更改的频率——在某个订单之前的许多更改或更新也值得查看。

示例:SQL 结果—计数变化[1]:

OBJECTCLAS  |OBJECTID  |FNAME    |Count_ChangesKLIM        |543       |KLIMK    |6

检查重复项

为了监控主数据的质量,同时防止不正确的预订甚至欺诈,检查重复数据总是一个好主意—一个著名的例子是 SAP 数据中的客户数据。对于像这样的更深入的分析,您可能需要 SQL 之外的其他方法,并且您可能更喜欢 python 笔记本。

示例:使用 Python 进行字符串相似性检查[2]:

import distance
distance.levenshtein("customer_abc", "customer_abcd")

双重支付

重复支付意味着赔钱,因此你可以检查 BSEG 表中的财务记录是否有重复。以下 SQL 连接 BSEG 的 BSEG,以标识具有相同公司代码、金额等的记录。但是具有不同文档编号。

示例:SQL 检查重复项[3]:

SELECT B1.MANDT,B1.BUKRS,B1.GJAHR,B1.BELNR BELNR1,B1.BUZEI BUZEI1,B2.BELNR BELNR2,B2.BUZEI BUZEI2,B1.DMBTR FROM BSEG BSEG_1 JOIN BSEG BSEG_2 ON (BSEG_1.MANDT=BSEG_2.MANDT AND BSEG_1.BUKRS=BSEG_2.BUKRS AND BSEG_1.DMBTR=BSEG_2.DMBTR AND BSEG_1.SHKZG=BSEG_2.SHKZG) WHERE B1.BELNR!=B2.BELNR 

周末和节假日交易

由于权责发生制会计(年度账目、流转税的提前返还等),在不寻常的日期过账可能会有风险。)或者诈骗。要获取日期为创建日期或更改日期的记录,您可能需要来自 getfestivo 等开放 API 的假日日期,并调用类似以下的 API:

示例 API 调用[4]:

[https://getfestivo.com/v2/holidays?api_key=**[**](https://getfestivo.com/v2/holidays?api_key=[) **YOUR API KEY HERE]**&country=DE&year=2020

不寻常的预订文本

要确定不应该存在的费用,您可以在 BKPF 表中使用以下内容搜索不寻常的预订文本:

SQL 文本搜索示例:

... FROM BKPF
WHERE UPPER(BKTXT) LIKE = “Cancellation”
OR UPPER(BKTXT)= “Credit note”
UPPER(BKTXT)= “fee”
UPPER(BKTXT)= “Switzerland”
.....

结论

这些只是 SAP 系统中少数可能的分析问题,但由于我们讨论的是财务数据,这是一个重要的话题,您和您的公司应该意识到这些问题,并渴望培养分析能力。在本文中,示例与 SAP 相关,但是其他具有财务模块的 ERP 系统,如 Oracle 或 DATEV,也将以相同的方式工作,并且案例也是相似的。

进一步的资料和阅读

[1]石,董,(2015)使用 CAATs 对 SAP 进行审计。

[2]pypi.org,https://pypi.org/project/Distance/

[3]DAB-GmbH,https://www . da b-Europe . com/file admin/public data/Downloads/2016 _ 04 _ DAB _ Gesamtkatalog _ es . pdf

[4]费斯蒂沃,https://getfestivo.com/documentation

谷歌云中的 SAP 数据分析

原文:https://towardsdatascience.com/sap-data-analytics-in-the-google-cloud-8f1a8a662355?source=collection_archive---------41-----------------------

如何将 SAP 与谷歌云平台(BigQuery、Data Studio 或 recently looker 等强大数据分析工具的提供商)相结合,以获得强大的数据分析平台和宝贵的见解。如果您对 SAP 数据的实用数据分析方法感兴趣,这篇文章可能也会让您感兴趣。

云是数据分析的推动者——图片由 Alex MachadoUnsplash 上提供

体系结构

SAP HANA 已经内置了 SAP 数据服务,因此您可以轻松地将数据从 SAP 应用程序或其底层数据库导出到 BigQuery。然而,GCP 和思爱普的结合对于那些仍然生活在 ERP 世界中的公司来说也是可能的。

以下几点显示了将数据从 SAP 加载到 Google Services 的可能性:

  • 使用 talend/Google data flow/等数据集成工具,通过 RFC(远程函数调用)接口提取数据。
  • 编写 ABAP 程序
  • fivetran 等第三方连接器
  • 使用 SAP 复制服务器

因此,可能的解决方案如下所示:

SAP 到大型查询—图片来自 Google [1]

实现增量负载

获取数据是第一步。之后,您将处理如何实现一个 delta 逻辑的问题,因为您可能不想通过满载定期加载所有数据。这只会带来加载时间长、没有历史数据等缺点。尤其是控制、内部审计等部门。通常基于历史数据视图构建他们的数据分析。

虽然 SAP 数据服务已经内置在 CDC 向导中,但其他数据集成方法可能需要一些额外的工作。要实现这一步,非常了解 SAP 表和表结构是很重要的。在自己的逻辑上实现增量加载尤其可以借助两个表来实现: CDHDR (改变标题)和 CDPOS (改变位置)。这些表跟踪主数据或事务数据的变化。

数据转换

随着基于列的数据仓库工具(如 BigQuery)的出现,由于性能以及源系统和目标系统之间不同的表结构或数据类型,数据转换通常是必要的。

一个例子是,在 SAP 中,你有 BKPF(会计凭证标题)和 BSEG(会计凭证段)表。由于其基于列的结构,对 BigQuery 中的数据进行反规范化是有意义的。解决方案可以是连接 BKPF 和 BSEG,如果需要,还可以连接其他主数据和参考表——例如借助 BigQuery DTS(数据传输服务)中的内置功能。因此,您可以得到一个非规范化的数据对象体系结构,如下图所示:

插图:从原始表格到非规范化数据对象-作者图片

另一个获得性能和成本效率的转换是使用嵌套数据结构。BigQuery 或 Amazon 的 Redshift 等较新的数据仓库技术确实能更好地处理这种数据结构。使用嵌套数据的用例有,例如标题/段表,如 BKPF 和 BSEG,或者如果一个表包含的列变化不大,如:

  • name
  • lastname

结合历史上经常变化的列,如[2]:

  • address(一个嵌套重复的字段及其后续嵌套字段):
  • address.address
  • address.city

实施(自助)申报

最后一部分将实现报告和数据分析的可能性。有了 BigQuery,数据分析师已经有了一个很好的分析工具。数据科学家可以使用数据实验室或 GCP 内众多 ML 服务之一。Data Studio 可能是业务用户共享 KPI、仪表板等的合适解决方案。为了在 BigQuery 中实现角色/用户访问模型,通常使用 BUKRS 属性(公司代码),这样用户就只能看到分配给他的公司代码的数据,这也是 SAP 处理用户权限的方式。

结论 SAP 和 GCP 的结合将为贵公司带来巨大的数据分析能力和可能性。SAP 系统托管了大量想要分析的有趣数据,而 GCP 提供了许多数据分析/科学家工具和服务,可以实现这一点。

资料来源和进一步阅读:

[1] Google,https://cloud . Google . com/solutions/sap/docs/big query-replication-from-sap-apps?hl=de

[2]谷歌,https://cloud.google.com/bigquery/docs/nested-repeated

https://blogs.sap.com/2018/04/08/hana-sda-google-bigquery/

https://cloud . Google . com/solutions/sap/docs/big query-sap-export-using-SDS

讽刺分类(使用快速文本)

原文:https://towardsdatascience.com/sarcasm-classification-using-fasttext-788ffbacb77b?source=collection_archive---------36-----------------------

我们将使用 FastText python 模块为新闻标题构建一个讽刺分类器。

FastText 是由脸书研究团队创建的一个库,用于高效学习单词表示法和句子分类。它在 NLP 社区中吸引了很多人,特别是作为一个强大的单词表示基线,取代了 word2vec,因为它在获取单词向量时考虑了字符 n 元语法。这里我们将使用 FastText 进行文本分类。

数据收集自https://www . ka ggle . com/RMI SRA/news-headlines-dataset-for-sneach-detection。这里讽刺的是来自TheOnion而非讽刺的是来自HuffPost。现在让我们跳到编码。**

首先,让我们检查数据来决定方法。可以从https://www . ka ggle . com/RMI SRA/news-headlines-dataset-for-stranship-detection/data 下载数据?select =挖苦 _ 头条 _ 数据集 _v2.json

***#load data*
**import** **pandas** **as** **pd**
df = pd.read_json("Sarcasm_Headlines_Dataset_v2.json", lines=**True**)
#shuffle the data inplace
df = df.sample(frac=1).reset_index(drop=**True**)
*# show first few rows*
df.head()**

基本上,在阅读带有 pandas 的表格形式的 json 时,数据集包含 3 列,其中‘headline’包含新闻的标题文本,‘is _ anticate’包含分别表示讽刺和非讽刺的 1 和 0。如果我们看到讽刺和非讽刺的例子的代表性是

0 14985
1 13634
Name:is _ sniptic,dtype: int64

现在来看看课文是如何寻找讽刺和非讽刺的例子的:

**#word cloud on sarcastic headlinessarcastic = ‘ ‘.join(df[df[‘is_sarcastic’]==1][‘headline’].to_list())plot_wordcloud(sarcastic, ‘Reds’)**

***#word cloud on sarcastic headlines* sarcastic = ' '.join(df[df['is_sarcastic']==0]['headline'].to_list()) plot_wordcloud(sarcastic, 'Reds')**

现在,在构建分类器模型之前,我们需要对文本进行一些清理,以消除噪声。既然这些都是新闻标题,就不包含太多废话。我想到的清理是把所有的字符串都变成小写,去掉所有不是字母数字的内容,用一个特定的标签替换数字。

**df['headline'] = df['headline'].str.lower()
df['headline'] = df['headline'].apply(alpha_num)
df['headline'] = df['headline'].apply(replace_num)**

现在,我们已经准备好清理文本和它们相应的标签来构建一个二元讽刺分类器。如前所述,我们将使用 FastText python 模块构建模型。

首先,需要在环境中安装 FastText python 模块。

***#Building fasttext for python\* 
!git clone https://github.com/facebookresearch/fastText.git 
!cd fastText
!pip3 install .**

我们需要以 FastText api 可以理解的格式准备好训练和测试文件。我们希望用来训练模型的文本文件的默认格式应该是 __ label __

***#data preparation for fasttext*
**with** open('fasttext_input_sarcastic_comments.txt', 'w') **as** f:
    **for** each_text, each_label **in** zip(df['headline'], df['is_sarcastic']):
        f.writelines(f'__label__**{each_label}** **{each_text}\n**')**

文件中的数据如下所示:

**!head -n 10 fasttext_input_sarcastic_comments.txt__label__1 school friends dont find camp songs funny
__label__0 what cutting americorps would mean for public lands
__label__0 when our tears become medicine
__label__1 craig kilborn weds self in private ceremony
__label__1 white couple admires fall colors
__label__1 mom much more insistent about getting grandkids from one child than other
__label__0 diary of a queer kids mom
__label__1 sephora makeup artist helping woman create the perfect pink eye
__label__1 kelloggs pulls controversial chocobastard from store shelves
__label__0 winston churchills grandson introduces a new nickname for donald trump**

这里 __label__0 表示非讽刺,__label__1 表示讽刺。现在我们已经准备好开始训练分类器模型了。为此,将把数据集分为训练(90%)和测试(10%)数据集。用于监督二元分类的 FastText 函数是 train_supervised

***''*
*For classification train_supervised call will be used:*

*The default parameters to it:*
 *input             # training file path (required)*
 *lr                # learning rate [0.1]*
 *dim               # size of word vectors [100]*
 *ws                # size of the context window [5]*
 *epoch             # number of epochs [5]*
 *minCount          # minimal number of word occurences [1]*
 *minCountLabel     # minimal number of label occurences [1]*
 *minn              # min length of char ngram [0]*
 *maxn              # max length of char ngram [0]*
 *neg               # number of negatives sampled [5]*
 *wordNgrams        # max length of word ngram [1]*
 *loss              # loss function {ns, hs, softmax, ova} [softmax]*
 *bucket            # number of buckets [2000000]*
 *thread            # number of threads [number of cpus]*
 *lrUpdateRate      # change the rate of updates for the learning rate [100]*
 *t                 # sampling threshold [0.0001]*
 *label             # label prefix ['__label__']*
 *verbose           # verbose [2]*
 *pretrainedVectors # pretrained word vectors (.vec file) for supervised learning []*
*'''*
model = fasttext.train_supervised('sarcasm_train.bin', wordNgrams=2)**

要测量测试数据集中的性能:

***#measuring performance on test data*
**def** print_results(sample_size, precision, recall):
    precision   = round(precision, 2)
    recall      = round(recall, 2)
    print(f'{sample_size=}')
    print(f'{precision=}')
    print(f'{recall=}')

print_results(*model.test('sarcasm_test.bin'))***sample_size=2862 
precision=0.87 
recall=0.87*****

结果虽然不完美,但看起来很有希望。我们现在保存模型对象,以便将来进行推理。

***#save the model*
model.save_model('fasttext_sarcasm.model')**

FastText 还能够压缩模型,以便通过量化牺牲一点点性能来获得更小的模型文件。

**# with the previously trained `model` object, call
model.quantize(input='sarcasm_train.bin', retrain=True)\
# results on test set
print_results(*model.test('sarcasm_test.bin')) ***sample_size=2862 
precision=0.86 
recall=0.86*****

如果你看到精度和召回似乎遭受 0.01 分,但是,看看模型文件大小:

**!du -kh ./fasttext_sarcasm****98M ./fasttext_sarcasm.ftz
774M ./fasttext_sarcasm.model*****

压缩模型只有基本模型的 1/12。因此,这是模型大小和性能之间的权衡,用户必须根据用例来决定。既然分类器模型已经训练好并准备好了,现在是准备好推理脚本的时候了,这样您就可以计划部署了。

**def predict_is_sarcastic(text):
    return SarcasmService.get_model().predict(text, k=2)if __name__ == '__main__':
    ip = 'Make Indian manufacturing competitive to curb Chinese imports: RC Bhargava'
    print(f'Result : {predict_is_sarcastic(ip)}')***Result : (('__label__0', '__label__1'), array([0.5498156 , 0.45020437]))*****

从上面可以看出,最大概率的结果标签是 __label__0,这意味着根据训练的模型,所使用的标题是非讽刺性的。在 model.predict()调用中,k 的值表示您希望输出的类的数量以及它们各自的概率分数。由于我们使用了 softmax 激活(FastText 中默认的一个),两个标签的概率之和为 1

总之,在进行任何 NLP 分类时,FastText 可以是一个强大的基线,并且它的实现非常容易。这篇文章的所有源代码可以在我的 git repo 找到。FastText python 模块没有得到官方支持,但对于技术人员来说,这不应该是一个实验问题:)。在以后的文章中,波斯特将尝试讨论如何将训练好的模型转移到生产中。

** [## Facebook 研究/快速文本

fastText 是一个用于高效学习单词表示和句子分类的库。在本文档中,我们…

github.com](https://github.com/facebookresearch/fastText/tree/master/python) [## sambit 9238/深度学习

深度学习技术在自然语言处理、计算机视觉等领域的实现。-sambit 9238/深度学习

github.com](https://github.com/sambit9238/Deep-Learning/tree/master/sarcasm_classifier)**

自然语言处理中的讽刺检测

原文:https://towardsdatascience.com/sarcasm-detection-with-nlp-cbff1723f69a?source=collection_archive---------3-----------------------

一个高度复杂现象的研究综述

梅勒妮·德雷维克在 Unsplash 上的照片

⚠️读了我在⚠️博客中的原帖

当你试图知道某人是否在讽刺你时,你可能会做出这样的表情。对人类来说不易察觉,机器呢?

注意:为了简洁起见,这篇文章将只考虑使用 tweets 和使用深度学习模型的讽刺检测。

讽刺检测在 NLP 中是一个非常狭窄的研究领域,它是情感分析的一个特例,它不是检测整个光谱中的情感,而是关注讽刺。因此,这个领域的任务是检测给定文本是否是讽刺性的。

我们遇到的第一个问题是,与情绪分析中情绪类别的定义非常明确(爱客观上有积极的情绪,恨消极的情绪,无论你问谁或你说什么语言)不同,讽刺的边界没有那么明确。在开始发现讽刺之前,先了解一下什么是讽刺是至关重要的。

什么是讽刺?

牛津词典提供了如下定义:

讽刺是使用通常表示相反意思的语言来嘲笑或表达轻蔑。

有些人可能不同意它的目的,但有一个惯例,人们使用积极的话来传达消极的信息。当然,它因人而异,高度依赖于文化、性别和其他许多方面。例如,美国人和印度人对讽刺的理解是不同的。

此外,有人讽刺并不意味着其他人认为这是说话人的意图。这种主观性将对 DL 模型的性能产生影响。

1.数据:推特数据集

首先,两个 Twitter 数据集被用来进行研究:

里洛夫等人,2013 年

他们的数据集由自动提取的推文组成:35k 包含标签#挖苦和 140k 随机推文。

里洛夫数据集的一个例子

与其他数据集相比,该数据集很大,但与 DL 模型中使用的数据集相比,该数据集非常小。这是不平衡的,这意味着非讽刺性的推文比讽刺性的多,这是现实的,因为讽刺在我们的日常互动中非常罕见,数据集应该尽可能地代表现实。关于讽刺的类型,这个数据集捕捉了有意的讽刺,这条推文被贴上讽刺的标签是因为作者希望它是这样,它没有考虑人们的看法。

Ptáek 等人,2014 年

他们的数据集由手动标注的推文组成:他们将 7k 条推文标注为讽刺,并添加了另外 7k 条随机推文。

Ptáek 数据集的一个例子

这个数据集要小得多,因为手动标注更加耗时。这些研究人员还选择使其平衡,增加比通常更多的讽刺例子,以帮助模型更好地概括。在这种情况下,被标记为讽刺的推文是注释者认为是讽刺的(认为是讽刺),作者的意图没有被考虑。

这条推文在 2013 年疯传,这名女子在登机前写了这条推文,当她抵达她所访问的非洲国家时,推特爆炸了,她收到了许多指责她的各种消息,她甚至失去了工作……当有人最终让她发言时,她说她是在讽刺和开玩笑,她无意冒犯任何人。但在那个时候,她的意图无关紧要。

2.特征

历史上,原始推文添加了许多层功能:

  1. 清理:删除其他标签,链接,图片。
  2. 词法功能:删除停用词、标记化、弓形、词性标注
  3. 实用特征:表情符号、提及
  4. 上下文不一致:上下文不一致。可以是显式的,也可以是隐式的
  5. 用户嵌入 : 风格和个性特征。这种嵌入以两个相似用户具有相似嵌入的方式编码一个 Twitter 用户的信息。因为他们在推特上谈论同一个话题,用相似的风格写作,或者有可比的行为模式。

用户嵌入的想法是,如果在训练数据中有一个具有某种个性的用户,他们碰巧发出讽刺性的推文,那么当我们获得新数据时,有一个新用户具有类似的风格,因此与前一个用户类似的嵌入,无需查看新用户的推文,我们就可以预测这个用户是否会讽刺。仅仅通过观察嵌入的相似性。

最后两个特征是在撰写本文时才出现的,已经取代了早期研究中使用的纯语言和词汇特征。

3.模型

有无数的 DL 模型和架构的组合,这篇文章的目的不是深入分析模型。

直到 2017 年,大多数模型都是机器学习模型、逻辑回归、SVM 和 CNN 的一些尝试。

大约在 2017 年,LSTMs 开始有牵引力,由于它们的顺序性质和更容易处理长期依赖性,它们接管了 ML 模型。像变形金刚这样的注意力模型也很快取代了它们,现在变形金刚、密集网络和 CRF 层的组合正在被使用。

4.分析

不同的论文有不同的评价方法,但在几乎所有的情况下,人的评价很差,模型的评价更差。可能的原因是少量的数据和人类本身无法察觉讽刺。

分析当前研究立场的主要论文之一来自 2019 年 Oprea 和 Magdy,他们提出了 3 个旨在回答的问题:

  1. 用户嵌入是否预示了推文的讽刺性质?
  2. 用户嵌入在 Riloff 和 Ptacek 数据集之间扮演什么角色?
  3. 这两个数据集之间的性能差异是什么?

换句话说,第一个问题是,我们是否可以在不看推文的情况下,仅通过了解用户过去的行为和个性来预测讽刺。概括一点,讽刺是随机的,还是有某些性格特征使某人更可能是讽刺的?

如果是,它们对这两个数据集的影响是什么?了解某人,他们的爱好和兴趣,他们的个性,是否有助于我们发现他们是否在讽刺我们?

至于性能,在这两个数据集上训练模型,DL 模型在哪里表现得更好?

我鼓励读者停止阅读一段时间,根据你的直觉和你目前所读的内容,尝试猜测答案并思考原因。

这只猴子正在努力思考用户嵌入,并阻止你马上看到答案,鼓励你花一分钟思考它们:)

保罗·尼科勒洛在 Unsplash 上的照片

该论文表明,用户确实有一种基于他们的历史行为来决定是否讽刺的倾向。对我们来说,有些人比其他人更讽刺似乎是很自然的,但这是第一次有人在推特上发表这样的观点:是的,个性和讽刺是相关的。

为了回答第二个问题,用户嵌入在 Ptacek 数据集上是很好的预测器,但在 Riloff 数据集上不是。这意味着当察觉到别人的讽刺时,了解他们是有帮助的。但是当检测到他们有意的讽刺时,添加用户嵌入似乎不会增加任何价值。

关于第三个问题,DL 模型在 Riloff 上表现相当不错,在 Ptacek 上表现不佳。这可能有很多原因,平衡/不平衡,他们捕捉的不同类型的讽刺,但在我看来,在这一点上,它只是大小的问题(Riloff 的 35k 讽刺性推文,Ptacek 的 7k)。我相信这个模型在 Riloff 上有更好的结果,只是因为有更多的推文可以训练。如果数据集具有相同的大小,那么观察它们之间的差异将会非常有趣。

这篇论文也是第一次正式表示,由于 Riloff 和 Ptacek 之间的结果缺乏一致性,他们得出结论,这些数据集是不等价的,它们捕捉了不同的现象,即有意和感知的讽刺。

这篇论文的意义是巨大的,我希望看到它对 2020 年以后发表的论文的影响。

5.当前立场和未来研究

目前,一般来说,人们使用的主要是文本数据,一般来自 Twitter 和 Reddit,但也来自其他论坛帖子。

至于特征,他们使用了一些词汇特征、单词的上下文嵌入(BERT)和用户嵌入。

每个月都有新的发布模型,通常是一些密集层的变形金刚组合。

现在常见的一些错误有:

  • 假阴性:模型没有检测到讽刺性的推文,很可能是因为它们非常特定于特定的情况或文化,并且它们需要 DL 模型所不具备的高水平的世界知识。最有效的讽刺是专门针对人、情境和说话者之间的关系的讽刺。
  • 以非常礼貌的方式写的讽刺性的推文不会被发现。有时候,人们用礼貌作为讽刺的方式,用非常正式的词来形容与随意的谈话不相称。以非常正式的方式称赞某人是一种常见的讽刺方式。

未来的研究

作为未来的研究,我希望看到在数据集上做更多的工作,更大,更完整,更多样化的数据集,而不是不断增长的复杂的 DL 模型。一个模型和它的数据一样好,而且迄今为止使用的数据集都有它们的偏见和局限性。

实际上,在 2019 年底发布了一个新的数据集,Oprea 和 Magdy 的 iSarcasm,用户可以贡献自己讽刺的推文,并包括对为什么讽刺的解释,以及一些关于他们的元数据。不幸的是,它不是很大(大约 1k 条推文),但在我看来,这是朝着正确方向迈出的一小步。

DL 的最大挑战之一似乎是将世界知识添加到模型中,这将加速训练,并极大地有助于泛化和偏向。但问题是怎么做。截至目前,没有答案。

最后,有些人正在超越文本,使用图像和音频的多模态讽刺检测,根据电视节目如《生活大爆炸》和《老友记》的数据训练模型。它仍处于早期阶段,但考虑到讽刺的多模态性,它看起来很有希望。很多时候讽刺的不是文字,而是语调或者面部表情。

这是截至 2020 年初讽刺检测的概述,我希望它足够信息量,以便任何具有最低限度机器学习知识的人都可以理解并了解最新的研究。

你可以在我的 Github 中找到发表在《讽刺检测》上的大多数论文的注释,以及我的演示文稿中的幻灯片。我已经把我在帖子中直接引用的具体论文的链接放了出来。

我很乐意讨论发现讽刺的想法和方法,或者解释一些不清楚的概念。欢迎在 Twitter 上和我聊天。感谢阅读!

SARS 基因组符合本福特定律,具有核碱基的笛卡尔乘积

原文:https://towardsdatascience.com/sars-genome-fits-benfords-law-with-the-cartesian-product-of-nucleobases-534876aff947?source=collection_archive---------45-----------------------

~Python 代码~

恒星也以多种方式遵循本福德定律。(图片作者)

许多人可能听说过来自网飞的书呆子和娱乐纪录片 本福德定律连接 章节“数字”老实说,那是我第一次听说它的地方。1 到 9 中较小数字的第一位优势…这是一种隐藏的模式,我们都以某种方式融入其中,无法摆脱。

鉴于目前的事件,我想知道本福特定律如何适用于病毒基因组序列,因为本福特定律需要大量的实例才能恰当地适用。SARS 物种基因组(包括新型冠状病毒,通常称为新型冠状病毒)的核苷酸或核碱基长度约为 29,800(也称为基因组的组成部分:A,T,G,C)。出版物已经显示了本福德对特定基因的分析趋势,但是仅仅看基因组序列似乎不能说明生物信息,除了提供一些有趣的“玩弄”法律。今天,我将玩转法律,揭开 SARS 序列的“本福特性”。首先,我导入了序列文本文件,去掉了换行符。然后,我将字符串分割成单个字母,并创建了一个函数“count_pattern”用于计数(代码如下所示)。在这篇文章中,我展示了 2003 年非典2019 年新型冠状病毒的例子(点击查看基因组信息)。

创建更多实例的一种方法是查看特定的核碱基(我称之为 bp )排列在基因组中出现了多少次(即 AG、at、GC、ATG、GGG、CTA、ATGC、CGTT……被发现了多少次)。为了穷尽这个列表,我们只取 bp: A,T,G,c 的笛卡尔积【简而言之,什么是笛卡尔积?这是一种重复的排列:比方说,把苹果、梨和香蕉按照一定的顺序放进四个口袋#1、2、3、4,每个口袋一个水果。]为了不出现大多数个位数,我从单个 BP(A、T、G、C 分别出现的次数)到 5 bp(即 AGTGC)进行了计数。

值得一提的一个“bug”是 Python 3 中的 count 函数不会多次重复计算排列中的同一个字符。例如,如果遇到'..AAAA..,‘我们知道技术上有 3 个‘AA’在里面,但是函数会回答 2。同样,如果遇到..TTTTT..,'在这种安排下,该函数将只记录 1 个“TTT”,而我们将记录 3 个。为了快速解决这个问题(暂时忘记优雅),我简单地半手工将 repeat=3 & 5 的计数添加到 2 bp 的计数中,将 repeat=4 & 5 的计数添加到 3 bp 的计数中,将 repeat=5 的计数添加到 4 bp 的计数中。我没有深究,但数学就是这样解决的。这仅适用于 bp 一致的计数排列。

有趣的是,似乎有一种奇偶依赖或交替趋势:**偶数BP 排列的计数产生了一种更像本福特的趋势——这意味着第一位数更有可能是 1 和 2,,而奇数BP 排列的计数似乎大大违反了本福特定律。**注意 y 轴是自然对数而不是线性刻度,以更好地显示下计数值。

笛卡尔乘积 bp 数的 sar(作者图片)**

新型冠状病毒按笛卡尔积的 bp 数(作者图片)**

然而,如果将所有的数据汇总在一起,我们会得到一个基于曼-惠特尼检验的类似本福特的曲线(SARS 和新型冠状病毒序列的 P > 0.86)。虽然曼-惠特尼检验可能不是检测任何细微差别的最佳方法,但由于趋势很明显,我将暂时搁置它。黑色条代表实际数据,而紫色曲线是公式 log(1+1/d)得出的本福特曲线。

SARS 样本结果(图片由作者提供)**

新型冠状病毒样本结果(图片由作者提供)**

我们能从这个练习中学到什么?我被本福德定律的流行所震惊——它似乎以某种方式潜伏在这个世界的计数之下。有许多方法可以创建要计数的实例(就像这里介绍的一样)。虽然我不是遗传学专家,但我敢打赌这篇文章的结论对其他基因组也适用。我也想知道随机生成的序列是否符合这个结论。本福特定律是所有可测量事物的基础,还是仅仅是“自然发生”的事物?“自然发生”到底是怎么定义的?也许研究人员可以致力于建立更有意义的标准和指导方针,利用本福特定律来检测欺诈行为,并提供可操作的见解。一篇显示人类大脑电活动也遵循这一普遍规律的文章可以在这里找到。我将把进一步的见解和解释留给任何感兴趣的人——但我对探索这个话题很感兴趣。

来自 FASTA 的文字截图

🛰️从卫星探测海岸线变化的四个步骤

原文:https://towardsdatascience.com/satellite-coasts-detection-model-with-python-and-opencv-28d1b4b8474e?source=collection_archive---------10-----------------------

Python 和 OpenCV 来检测海岸线随时间的变化

介绍

海岸是一个非常动态的系统 在这个系统中,海岸线的侵蚀、后退或前进现象受到众多气象、地质、生物和人为因素的控制。

在海洋磨蚀的作用大于沉积物的情况下,有明显的海岸侵蚀的情况,字面上导致地球表面 解体和破坏

来源: 弗林德斯大学 (CC0)

本文的目标是

在本文中,我们将使用一种叫做 的算法对 Landsat 8 平台上的【OLI】传感器*** 获取的两幅卫星图像进行 Canny 边缘检测*** 。

通过这种方法, 我们将能够可视化和评估一个特定的欧洲地区遭受强烈侵蚀作用的海岸线的进程:霍尔德内斯海岸。

这里的是拟定的工作流程 :

整个管道:从进口到产品。

开始吧!但是之前…

0.陆地卫星 OLI 数据简介

Landsat 8 是一个轨道平台,安装在一个名为 OLI(实用陆地成像仪)的 11 波段多光谱传感器 上。

具体来说,在本文中,我们将仅使用分辨率为 30 米的波段(即前 7 个波段)。

美国地质调查局地球资源卫星 8 号

资料可以免费下载 ,注册后通过USGS:https://earthexplorer.usgs.gov/提供的平台。

此外,正如通常使用的那样, 我们将使用反射率 ,即从地球表面反射的阳光量【0–1】,而不是使用入射阳光的原始数据。

1.包导入

在各种常见的软件包中,本文我们将使用来轻松处理 光栅 图像,使用 OpenCV 来应用 Canny 算法和 Scikit-Learn

2.数据导入

让我们定义一个变量,它告诉我们要保留的波段数以及先前在 JSON 中输入的辅助数据:

这个 Json 是来自 Landsat OLI 成像仪(我创造的)的信息集合。一种说明书。看起来是这样的:**

bands.json 文件包含了我们将要使用的波段的所有有用信息。

请记住,我们将只使用 30 米分辨率的波段,所以只有前 7 个。如果你愿意有较低的分辨率(100m),你可以嵌入 重要的 TIRS 1 和 TIRS 2 波段以及

正如上面几行已经提到的,我们将使用来自 OLI 陆地卫星 8 号的两个不同的采集数据:

  • 2014/02/01
  • 2019/07/25

为了方便和加速两个采集 的所有操作,我们将定义一个 Acquisition()类 ,它将允许我们封装所有必要的函数。

在代码执行期间,这将允许我们执行一些支持功能,例如:

  • 在指定路径中搜索 GeoTIFF
  • 加载 采集;
  • 收购 登记 (对齐);
  • 采集子集采集**

好了, 我们现在可以使用 : 来启动整个代码

结果应该是这样的:

**Searching for 'tif' files in Data/2014-02-01
Found 7 'tif' files
Loading images
Done
Searching for 'tif' files in Data/2019-07-25
Found 7 'tif' files
Loading images
Done**

我们的 14 幅 OLI 图像(7 个波段中的 2 次采集)现已加载。

2.1.子集化多光谱立方体

在这个阶段,在两个多光谱立方体 的 的“对齐”(或更正式的注册)之后,我们负责剪切掉我们不感兴趣的采集部分。**

让我们使用函数sub estimates()来“剪切”不需要的数据。

因此,让我们 定义 AOI ( 感兴趣区域)并使用 【采集() 类中的函数subestimages()进行子集化:

搞定了。

3.数据探索

3.1.可视化多光谱立方体

让我们尝试查看 2019/07/25 收购的所有乐队。 纯粹出于审美原因 ,在制作图像剧情之前,我们先用standard scaler()对图像进行标准化处理。

这应该是结果。

正如你所看到的,有些波段比其他波段更亮。这很正常。

3.2.以复合 RGB 显示多光谱立方体

现在让我们尝试在使用波段 4(红色)、3(绿色)和 2(蓝色)获得的 RGB 合成 中可视化这两个采集。

偏置和增益被定义 只是为了得到一个漂亮的 viz

这就是结果!有趣的是,这两种采集在反射率方面有着显著的不同。

好了,继续进行 海岸线检测

4.自动海岸线检测

在本段中,我们将使用 Canny 的方法 进行边缘检测。

在进行真正的检测之前,有必要准备数据集,尝试通过聚类算法 对其进行分割,以区分海洋和陆地

4.1 数据准备

在这个阶段,我们应该为聚类操作重塑两个多光谱立方体。

4.2 使用 K 均值的图像分割

让我们通过 k-means 对这两个收购 进行分段(使用你更喜欢的模型)。

4.3.收购细分结果

这里有两个集群确定代表浮现的土地和水体。

4.4.Canny 边缘检测算法

Canny 的传统钥匙技术分为以下几个阶段:

  1. 降噪 通过与高斯滤波器卷积;
  2. 图像 四个方向 (水平、垂直、2 斜)的渐变计算;
  3. 提取 梯度局部极大值
  4. 阈值带迟滞 用于边缘提取。

让我们立即开始,通过 将聚类结果转换成图像 ,然后 通过具有 15×15 内核的高斯滤波器降低噪声 :

稍微模糊图像后,我们可以使用OpenCVCanny()模块继续执行真正的 Canny 技术:

在一行代码中,我们获得了梯度,提取了局部最大值,然后对每次采集应用了具有滞后的阈值。

注意:使用 Canny()参数探索不同的结果。

4.5.结果呢

这是结果。

以下是一些细节:

5.结论

从结果中可以看出,Canny 的算法在其原始管道中工作得相当好,但其性能像往常一样取决于所涉及的数据。

事实上,所使用的聚类算法允许我们分割初始的多光谱立方体,其性能当然可以改进。并行使用几个聚类模型可以全面改善结果。

🤝如有任何疑问、反馈或合作要求,请随时* 联系我Linkedin*。我会很高兴和你聊天!**

👉要获得更多类似的内容,并关注即将发布的文章,请不要忘记在 Medium 上 关注我。

👉 如需参考本文,请联系我。谢谢你。

土星云托管已推出:为每个人的 GPU 数据科学

原文:https://towardsdatascience.com/saturn-cloud-hosted-has-launched-gpu-data-science-for-everyone-c3e1ae21b1b5?source=collection_archive---------48-----------------------

数据科学的未来&机器学习支持 GPU

GPU 计算是数据科学的未来。RAPIDS、TensorFlow 和 PyTorch 等包实现了数据科学所有方面的闪电般快速处理:数据清理、特征工程、机器学习、深度学习等。利用 GPU 计算的挑战在于,它需要对内部硬件或基础设施进行投资,以便在云上利用 GPU。

今天,土星云宣布推出土星云托管,这是一个端到端 GPU 数据科学的云托管解决方案,适合所有初创公司、小型团队、学生、研究人员和修补数据科学家的需求。

鸣谢:土星云

TL;DR:Saturn Cloud Hosted是实时的,任何人只需点击一个按钮,就可以注册并启动支持 GPU 的数据科学机器。您可以立即开始免费试用!

鸣谢:土星云

注册后几秒钟内,您就可以启动 JupyterLab 实例,该实例具有针对最流行的 GPU 数据科学包的预配置环境,由英伟达 T4 或 V100 GPU 提供支持。当您的数据大小超过单个 GPU 时,您可以轻松地扩展到由多个 GPU 机器组成的集群。几百甚至!Saturn Hosted 负责所有硬件供应、环境设置和集群通信挑战,因此数据科学家可以直接投入工作。

土星云托管的 GPU 改变世界。鸣谢:土星云

由 NVIDIA GPUs 托管的土星云的愿景是将世界上最快的数据科学和机器学习能力带给每个人,无论预算、资源和时间如何。虽然 GPU 加速工具在以前是一种奢侈品,但随着时间的推移,价格下降,加上云可用性和 Saturn Cloud 提供的基础设施,使其成为日常用户的强大工具。

GPU 上更快的随机森林

让我们探索使用 Apache Spark 在 CPU 机器集群上实现分布式随机森林训练,并将其与使用 RAPIDS 和 Dask 在 GPU 机器集群上训练的性能进行比较。

TLDR :我们使用 3 亿个实例训练了一个随机森林模型:Spark 在 20 节点 CPU 集群上耗时 37 分钟,而 RAPIDS 在 20 节点 GPU 集群上耗时 1 秒。这比 GPU 快了 2000 多倍。

您可以在此阅读关于该基准测试的更多内容。我们在 Spark (CPU)和 RAPIDS (GPU)集群上对纽约市出租车数据的 300,700,143 个实例训练了一个随机森林模型。两个集群都有 20 个工作节点,小时价格大致相同。以下是工作流程每个部分的结果。

Spark 是 37 分钟,而 RAPIDS 是 1 秒钟!

GPU 粉碎了它——这就是为什么你现在拥有它们会如此激动。想想当你不需要为一次拟合等待超过 30 分钟时,迭代和改进模型的速度会有多快。一旦您添加了超参数调整或测试不同的模型,每次迭代很容易增加到几个小时或几天。

需要看到才相信?你可以在这里找到笔记本!或者继续阅读,看看如何在 Saturn Cloud Hosted 中建立一个项目,并为自己运行它。

借助土星云托管的 GPU 加速数据科学

在 Saturn Cloud Hosted 上使用 GPU 很容易上手,我们将使用一个数据样本完成上面的随机森林模型训练练习。该示例使用纽约市出租车数据来训练随机森林模型,该模型将乘坐分为“高小费”或“低小费”乘坐。当你创建一个帐户时,这些笔记本会被预加载到一个“examples-gpu”项目中,或者你也可以自己点击这里获取这些笔记本。

我们首先将一个 CSV 文件加载到一个数据帧中,但是因为我们使用的是 RAPIDS cudf 包,所以数据帧被加载到 GPU 内存中:

然后经过一些特征处理,我们训练我们的随机森林模型!

Saturn Cloud Hosted 的伟大之处在于这段代码“能够正常工作”。环境已经给你设置好了,GPU 也接好了,现在你可以专注于训练一个模型了。

如果数据集很大,使用单个 GPU 可能不够,因为数据集和后续处理必须适合 GPU 内存。这就是土星云上的 Dask 星团发挥作用的地方!您可以从 UI 或笔记本中定义集群,确保为 Dask 工作人员选择 GPU 大小:

接下来就是为分布式 GPU 处理导入适当的 RAPIDS 模块和子模块:

你想要一个简单的方法来获得超快的 GPU 数据科学吗?

是啊!有了土星云托管,您可以在几秒钟内启动 GPU 集群。Saturn Cloud 处理所有工具基础设施、安全性和部署问题,让您立即开始使用 RAPIDS。点击这里免费试用土星云主持的!

如果您所在的公司需要虚拟私有云解决方案,土星云也提供企业解决方案,您可以在这里找到。

作者:
亚伦·里希特,土星云高级数据科学家

米(meter 的缩写))塞巴斯蒂安·梅蒂,土星云的创始人

使用记录聚合节省 Amazon Kinesis 流成本

原文:https://towardsdatascience.com/save-amazon-kinesis-stream-costs-using-records-aggregation-79c8fd9ea748?source=collection_archive---------42-----------------------

使用 KPL 记录聚合降低 AWS 成本的案例研究

图片来自 Pixabayxresch

几年前,我们为一个客户开发了一个流管道,使用 Kafka 代理在他们自己的数据中心运行。流媒体管道是使用 Kafka Producer API 开发的。由于业务前所未有的增长,他们决定将其基础架构迁移到 AWS 云。作为迁移的一部分,我们决定实现 Kinesis Streams 来替代 Kafka。

在我们参与之前,客户内部团队已经做了一些调查并得出了一些数字。这是他们想出的办法:

平均记录大小: 77 字节
记录格式: JSON

[centos @ IP-172–31–70–220 ~]$ python 3 single _ record _ producer . py
向 Kinesis 流发送这些数量的记录:1

(图片作者)

(作者图片)

(作者图片)

假设每秒 25000 条记录,Kinesis 计算器估计的账单为$ 1196.82/月——相当高

由于明显的原因,客户对价格感到担忧,所以他们联系了我们。这篇文章的目的是展示你如何利用运动聚合来获得优势。你可能知道 Kinesis 流的成本是基于 2 个因素:

  1. 碎片成本/小时(固定-根据所需碎片数量计算)
  2. PUT 有效负载(根据 PUT 有效负载而变化)—PUT 有效负载单元以 25KB 有效负载区块计算。这就是使用 Kinesis 记录聚合可以节省成本的地方。

什么是 Kinesis 记录聚合?

在大量情况下,数据工程师使用 Kinesis 生产者库(KPL)将记录写入 Kinesis 流。 聚合 是 KPL 的一部分,允许客户增加每个 API 调用发送的记录数量。

数据工程师通常按如下方式构建他们的代码:

从源
循环中取出数据记录,用于记录:
调用 kinesis_client.put_record 方法发送一条
78 字节 记录
结束循环

相反,这是他们应该做的事情:

从源
循环中取出数据记录进行记录:
调用 Kinesis 聚合器—一次放入 12 条记录 请求
调用 kinesis_client.put_record 方法发送 984
字节 记录
结束循环

[centos @ IP-172–31–70–220 ~]$ python 3 aggregate _ record _ producer . py
向 Kinesis 流发送这些数量的记录:12

(作者图片)

现在是成本节约…

(图片作者)

(作者图片)

95.75 美元/月,相比之下1196.82 美元/月—** 对于节省来说还不错**

我希望您获得了一些关于如何为客户节约成本的宝贵见解。Kinesis 记录聚合当然是一个非常有效的方法,但是我建议你应该从开发周期的一开始就实现它。否则,您将需要重构大量代码来实现它。

我希望这篇文章是有帮助的。亚马逊 Kinesis** 是由 Datafence 云学院提供的 AWS 大数据分析课程的一部分。课程是周末自己在网上教的。**

保存日期

原文:https://towardsdatascience.com/save-the-date-ad964b2397d0?source=collection_archive---------22-----------------------

如何在 SQL 查询中生成缺失日期

在本文中,我们将讨论:

  1. 如何使用 Presto SQL 生成一个包含日期范围的表
  2. 连接应该从另一个表中补齐缺失数据的表的经验法则。

在对数据进行计算之前,数据完整性是我们需要解决的最重要的事情之一。即使有正确的意图,我们有时也会忽略数据中的错误。当错误不在我们拥有的数据中,而是在我们没有的数据中时,这将变得非常困难。

当执行考虑数据中样本数量的计算(计算平均值或中值)时,我们需要处理值为 NULL 或零的行。

让我们假设我们经营一家网上商店,想要查看一个客户一个月内的平均日购买量。在客户没有购买的日期,我们的数据中不会有这种迹象。如果我们忽略这个问题,计算每个客户的平均购买量,我们会得到一个过高的估计。

 customer_id    | order_date | purchase_amount |
10000100005411274 | 2020-04-11 |        1        |
10000100005411274 | 2020-04-16 |        1        |
10000100005411274 | 2020-04-18 |        2        |
10000100005411274 | 2020-04-21 |        2        |
10000100005411274 | 2020-04-24 |        1        |

如果我们在不看我们的原始数据的情况下计算客户的日均购买量,我们会认为他的平均购买量是 1.4(真是个客户!).

为了解决这个问题,我们必须为所有客户生成并匹配所有日期。通过 Presto SQL,我们可以在一个简单的查询中做到这一点:

SELECT
     *CAST*(date_column AS DATE) date_column
 FROM
     (VALUES
         (SEQUENCE(*date*('2020-04-01'),
                   *date*('2020-04-30'),
                   INTERVAL '1' DAY)
         )
     ) AS t1(date_array)
 CROSS JOIN
     UNNEST(date_array) AS t2(date_column)

使用 SEQUENCE,我们将创建一个日期在我们范围内的数组,并在数组中的每个元素与数组本身之间执行一个交叉连接。结果是每个不同日期对应一列。

一种快速的替代方法是从我们的初始数据中提取所有不同的日期,而不考虑客户,并将其存储为 WITH AS 语句。

接下来,我们将执行另一个交叉连接,以匹配我们的客户和不同的日期,从而填充缺失的日期:

with all_dates as (
SELECT
     *CAST*(date_column AS DATE) date_column
 FROM
     (VALUES
         (SEQUENCE(*date*('2020-04-01'),
                   *date*('2020-04-30'),
                   INTERVAL '1' DAY)
         )
     ) AS t1(date_array)
 CROSS JOIN
     UNNEST(date_array) AS t2(date_column)
)
select distinct customer_id
               ,date_column as order_date
from customer_purchases
cross join all_dates

最后,我们将把客户和日期之间的新匹配加入到包含我们数据的初始表中。

重要通知

表应该是我们用客户和日期创建的新表,而连接表应该是我们的初始数据。如果我们执行内部连接,我们将丢失所有没有购买的日期。此外,我们需要确保不要在 where 子句中放置处理左连接表中的列的条件,这将把左连接变成内连接。

with all_dates as (
SELECT
     *CAST*(date_column AS DATE) date_column
 FROM
     (VALUES
         (SEQUENCE(*date*('2020-04-01'),
                   *date*('2020-04-30'),
                   INTERVAL '1' DAY)
         )
     ) AS t1(date_array)
 CROSS JOIN
     UNNEST(date_array) AS t2(date_column)
),customers_dates as (
select distinct customer_id
               ,date_column as order_date
from customer_purchases
cross join all_dates)

select u.customer_id
     , u.order_date
     , *coalesce*(p.purchase_amount,0) purchase_amount
from customers_dates u
left join customer_purchases p
on u.customer_id = p.customer_id
and u.order_date = p.order_date
order by customer_id asc-------------------------------------------------------------------- customer_id    | order_date | purchase_amount|
................. | .......... | .............  |
10000100005411274 | 2020-04-13 |       0        |
10000100005411274 | 2020-04-14 |       0        |
10000100005411274 | 2020-04-15 |       0        |
10000100005411274 | 2020-04-16 |       1        |
10000100005411274 | 2020-04-17 |       0        |
10000100005411274 | 2020-04-18 |       2        |

现在我们可以用正确的方法计算平均每日购买量。

总结(TL;博士):

1.使用 SEQUENCE,我们可以创建一个包含一系列日期的数组,并将它们转换成表格。这种方法对于填充数据中缺失的日期非常有效,可以确保没有发生的日期仍然会出现。

2.当在两个表之间执行左连接时,如果 where 子句中的条件寻址来自表的列,则将左连接转换为内连接。人们很容易忽略这一点,当在同一个查询中使用表间连接应用条件时,应该三思而行。

通过更聪明地做事来节省时间

原文:https://towardsdatascience.com/save-the-time-by-doing-things-smarter-3cc26b9e5b9f?source=collection_archive---------81-----------------------

提高工作效率的 5 个简单技巧

西蒙·米加吉在 Unsplash 上的照片

时间是每个任务、每个项目的主要限制因素。如果你有无限的时间,你基本上可以做任何事情,取得任何成就。因此,明智的做法是优化你处理日常任务的方式,让它们花费更少的时间。当然,我们希望完成的工作量和质量保持不变。如果你在一个地方节省了一些额外的时间,你可以把它花在其他事情上。

在本文中,我将分享 5 个关于如何优化工作方式的技巧,并在处理数据科学项目时节省一些额外的空闲时间。实际上,这些技巧中的大部分都非常通用,不仅适用于数据科学,也适用于其他领域。

1.使用脚本自动化小型重复性任务

我相信这个想法对你来说并不新鲜。但是在现实中,你有多经常遵循这个建议呢?我见过许多人一次又一次地输入相同的行,每天在容易避免的地方重复完全相同的代码、文本和命令。

不要写两遍相同的代码。

我打赌你听说过这样一句话,甚至可能在为一些开源或商业产品编写代码时遵循这条规则。但是这条规则不仅仅对源代码有效。你的日常小任务呢?我将提供一个小例子让你更好地理解这个想法。

对于我的数据科学项目,我通常需要相当多的工具。像朱庇特笔记本,有的。txt 文件,用于存储临时笔记和链接,控制台终端打开我的项目的主目录,文件浏览器,互联网浏览器,等等。在我的互联网浏览器中,我通常喜欢打开一些预定义的标签,如我的电子邮件、我的日历、Kaggle、Matplotlib 函数参考等等。

因此,当我启动电脑时,我首先要做的是启动许多程序和应用程序。我已经测量过了——我可以设法在大约 2 分钟内开始。没有那么多,因为我一天只需要做一次,对吗?但后来我计算了一下,需要多长时间。每个工作日 2 分钟等于每周 10 分钟。总计每月 40 分钟。总计每年 480 分钟。顺便说一下,这正好是 8 小时或 1 个工作日。所以基本上,每年我都要花一整天的时间打开电脑。

然后有一天我写了一个大约 20 个命令的小脚本,它启动 Jupyter notebook,打开终端,打开浏览器,以及我需要的所有其他东西。除此之外,它甚至检查我的互联网连接,因为有时我的电脑无法捕捉无线网络,我需要重启网络接口几次才能让它工作。我的脚本替我做了。

现在我可以把这 2 分钟花在不同的任务上。例如,我可以在电脑启动时用手机查看电子邮件,或者做一些其他的小事情。我知道,那只是每天 2 分钟。但是事情总结起来。

如果你每个工作日只花 2 分钟做一些重复性的工作,那么到年底,你将会每天工作 8 小时。

当你在电脑上工作时,有很多事情可以实现自动化。甚至你在浏览器中的操作也可以自动化,使用一些工具,比如 Selenium,它可以移动鼠标并执行点击。我很确定在你典型的日常工作流程中有几个自动化的机会,你只需要发现它们。

2.开发时避免大量数据

这个我再举个例子解释一下。

您对新数据集进行探索性数据分析(称为 EDA)。这不是一个巨大的数据集,但却是一个相当大的数据集,比如说,大约有 1000 万行。在如此大的数据上,一些操作可能会非常慢。在 EDA 的过程中,你需要一次又一次地运行相同的代码片段——修复一些 bug 和错别字,调优一些参数,检查一些想法。如果每次运行只需要几秒钟,那么整个过程会非常慢。

您可以通过在开始时对整个数据集的某个随机子集进行采样来优化这一点。这个子集应该足够小,以允许代码快速运行,但也应该足够大,以代表全部数据。

只是要小心——如果你只抽取了很小一部分数据,那么就要避免对数据下结论。那就在这个阶段写一个无 bug 的代码吧。由于切换到完整版本的数据后,结论可能会发生变化。因此,如果采样数据部分足够大,效果会更好。“足够大”的确切含义可能因情况而异,但例如,大约 30-50 万行足够快,并且在大多数情况下仍然代表表格数据。

如果你使用 Pandas Python 库,那么sample函数可以帮你做到这一点。如下例所示,我们将包含完整数据的熊猫数据帧df_full采样为仅包含原始数据 20%的较小数据帧df_used:

df_used = df_full.sample(frac=0.2)

当您完成 EDA 并修复了所有错误后,现在可以将代码行从前面的示例切换到下面的示例:

#df_used = df_full.sample(frac=0.2)
df_used = df_full

现在,重新运行您的笔记本,只需等待一次,就可以在完整的大数据集上执行缓慢的操作。

这不仅适用于 EDA,也适用于涉及大数据规模的大多数任务,如训练模型、执行 SQL 查询等。首先,让它没有错误地工作,然后在完整数据上运行。

3.使用复制粘贴模板

也许有人告诉过你,复制粘贴总是不好的?如果是,不要相信他们——有些情况下,复制粘贴是最有效的做事方式。

考虑一个任务,比如绘制一些变量来呈现 EDA 结果。作为一名数据科学家,您很可能已经多次完成这样的任务。我用的是 Matplotlib Python 库。

下面给出了一些用 Matplotlib 在 Jupyter 笔记本中绘制两个数组值的简单示例代码,以供参考。

import matplotlib.pyplot as plt
plt.figure(figsize=(10,6))
plt.plot(df.index, df['value1'], label='Value 1')
plt.plot(df.index, df['value2'], label='Value 2')
plt.title('Results of my EDA')
plt.legend()

每次从头开始写所有这些行不是很有效率。即使过一段时间后,您将学会在不查看 Matplotlib 文档的情况下编写那些行和必要的参数,它仍然会很慢,并且会有引入一些错别字的风险。

另一方面,也不值得创建自己的自定义库函数作为 Matplotlib 函数的包装,因为您将需要许多不同的绘图,通常有轻微但重要的变化。因此,这种定制的包装器代码会增长得非常快,以支持所有需要的绘图变量,过一段时间后,您会发现自己要花很多时间来维护该包装器并修复其中的错误。

对于这种情况,有一个更好的解决方案——准备一个基本模板,您可以根据需要对其进行复制粘贴和编辑。包括关于最重要参数的注释,这样您就不需要每次都在文档中查找它们。

抄写文本比书写快。

我有一个。txt 文件,包含许多常用的代码片段模板(通常每个模板有 5-10 行)。用于绘制折线图、绘制饼图、应用宁滨将连续变量转换为分类变量等的模板。每当我需要一些常用的功能时,这让我节省了一些时间。

4.写你自己的 StackOverflow 式的笔记

你遇到过一些不平凡的任务吗?为了完成这项任务,你很可能不得不在 Google 上搜索信息,阅读工具文档,在 YouTube 上观看演示视频,或者做一些其他耗时的事情。当你经过几个小时的奋斗成功完成任务后,你会愉快地忘记它。有时…直到下一次你需要再次做完全相同的事情。

几年前,当我刚刚开始从事数据科学项目时,我必须安装 XGBoost 库,这是当时用于表格数据预测的最先进的库。在那时,在我的 Windows 上安装 XGBoost 确实是一项重要的任务,因为要获得最新版本,我必须从源代码中编译它。为了编译它,我必须先安装 Visual Studio。此外,我必须安装一些依赖项才能让一切正常工作。基本上,我花了整个晚上的时间从头开始设置一切。

在我的 XGBoost 在 Windows 上运行之后,我高兴地开始使用它…直到大约 4 个月后我的硬盘崩溃。然后,我花了几乎完全相同的时间再次安装它,因为我几乎不记得我到底做了什么让它工作。现在,这次我花了额外的 5 分钟来记下关键点、主要信息来源的链接以及最棘手部分的解决方案。

大约 6 个月后,我买了一台新的笔记本电脑,这为我节省了很多时间。然后当我咨询我的朋友在他的机器上安装 XGBoost 时。

每当你遇到一个不简单的棘手任务时,就值得考虑记下一些关于解决方案的简短笔记。因为有可能你将不得不再次做同样的事情。可以用简单的。txt 文件,一些应用程序的笔记或任何你喜欢的工具。我建议您保持在线访问,以防您可能需要从其他设备上查看它。

5.使长程序步骤独立

考虑一个典型的机器学习流水线。您读取一些输入数据,执行清理,转换数据,计算要素,训练模型,然后进行预测。比方说,你有一个程序,一个接一个地完成所有这些步骤。现在,您获得了一些额外的输入数据,并希望重新计算您的预测。这很简单——你只要运行你的程序,过一段时间它就会给你新的预测。

现在,让我们说,你想检查是否改变你的模型中的一些参数可以给你更好的准确性。你改变程序中的参数,重新运行程序,再次得到新的预测。但是等等——这一次您实际上不需要重新读取输入数据。所有的数据转换和特征计算也不是必须的。你只是想用新的参数重新训练模型。

因此,如果有可能从某个预定义的点重新运行程序,效率会更高。在这个特殊的例子中,只是模型训练和预测。

通过不必要的执行代码,你只是在浪费时间和电力。

这就是我非常喜欢使用 Jupyter 笔记本来完成数据科学任务的原因。它们内置了将整个程序分割成小步骤(单元)的支持。每个单元都可以独立执行。要记住的主要技巧是不要修改先前单元格中定义/计算的变量。否则,从中间的某个地方重新运行笔记本时,可能会导致错误的结果。

实际上,使用传统的 Python 脚本也可以实现类似的独立步骤,只需要一些额外的小工作。执行此操作的示例伪代码:

step = read_input_argument()
if step==0: #read and clean initial input data data = read_and_clean_data() save_to_disk(data, 'step1.csv')
if step<=1: #transform data
  if step==1:
    data = read_from_disk('step1.csv') data = transform(data) save_to_disk(data, 'step2.csv')
if step<=2: #calculate features
  if step==2:
    data = read_from_disk('step2.csv') data = get_features(data) ...

这样的程序接收step参数。如果它的值为 0,将执行整个程序。如果该值等于 1,程序将跳过清理输入数据,但将使用一个预先保存的临时文件与已经清理的数据。通过这种方式,您可以指定开始程序执行的步骤,从而节省大量时间,不必花费无数次清理和转换不需要的数据。

另外一个建议是保持足够大的步长。否则,您可能会遇到这样的情况:在每一步之后,写入/读取中间临时文件的成本都要高于在步骤中完成的实际工作的成本。

一些最后的话

约书亚·厄尔Unsplash 拍摄的照片

不是所有的东西都适合每个人。所以,可能不是所有的建议都像对我一样对你有用。但希望你能得到一些改进工作方式的新想法。

请随意留下你关于节省时间和更有效做事的建议和技巧的评论。

效率高,事半功倍,给自己多留点时间!

感谢阅读!

节省成为职业棒球手的时间:轻松且免费

原文:https://towardsdatascience.com/save-time-becoming-a-ml-pro-easily-and-free-of-charge-a8ff2f683b1b?source=collection_archive---------32-----------------------

阿里·沙阿·拉哈尼在 Unsplash 上拍摄的照片

通过使用现成的 AWS SageMaker 笔记本

如果你熟悉大数据、云计算和机器学习的基础知识,并希望获得新的知识和经验,AWS SageMaker 及其现成的笔记本是一个不错的选择。

首先什么是 aws:

亚马逊网络服务(AWS)是世界上最全面、最广泛采用的云平台,从全球数据中心提供超过 175 种全功能服务。从计算、存储和数据库等基础设施技术到机器学习和人工智能、数据湖和分析以及物联网等新兴技术,AWS 的服务和这些服务中的功能远多于任何其他云提供商。这使得将您现有的应用程序迁移到云中变得更快、更容易、更具成本效益,并构建几乎任何您可以想象的东西。 *

其次,什么是 SageMaker:

亚马逊 SageMaker 是一个完全托管的机器学习服务。借助亚马逊 SageMaker,数据科学家和开发人员可以快速轻松地构建和训练机器学习模型,然后直接将其部署到生产就绪的托管环境中。它提供了一个集成的 Jupyter 创作笔记本实例,可以方便地访问您的数据源进行探索和分析,因此您不必管理服务器。它还提供了常见的机器学习算法,这些算法经过优化,可以针对分布式环境中的海量数据高效运行。 *

那么,我说的熟悉是什么意思呢?如果你能勾选以下所有方框,你应该很熟悉:
——你知道回归和分类的区别以及使用的领域。
-你听说过一些算法,比如 k 近邻、随机森林、极端梯度推进和主成分分析。你可以处理像 Keras,Tensorflow,PyTorch 或者 Chainer 这样的框架。
-你了解 AWS 的概念。
——你知道创造和使用 Jupyter 笔记本。

准备好了吗?好了,让我们直入主题,攀登 ML 阶梯:
众所周知,aws 是一家全方位服务的云提供商。当人们听到“云”这个词时,有些人可能只会想到存储和数据库解决方案。但这仅仅是开始。不幸的是,他们提供各种各样的服务。比如图像和物体识别、BI 和 CRM 工具、区块链网络或虚拟现实应用。
他们的大多数服务和工具都可以通过 API 或使用 aws 管理控制台来访问。

AWS 管理控制台的登录页面

专业提示:如果你是 AWS 新手,还没有账户,你可以免费获得一个。这还不够:有了 AWS 免费层,你还可以访问几乎所有的 AWS 服务。存储大小和计算时间有一些限制。即使是免费的,你也必须在注册时提供你的信用卡信息。最好是设定一个预算限额,这样就不会被收费。如果你是学生,那就更好了:你可以获得一个免费教程的 AWS 教育账户,预算 100 美元,而且不需要提供你的信用卡或银行账户。

1。开始
登录 AWS 管理控制台,在搜索框中输入 Sagemaker 并点击结果。

专业提示: AWS 有几个数据中心。如果您必须遵守特殊的治理政策,您可以选择适合您的区域。例如,如果您必须遵守 GDPR,您可以在五个 EU-地点之间进行选择。位置对网速也很重要。注意:目前只有 US-East-1 (N. Virginia)支持学生帐户。

支持的 aws 区域

单击左侧的笔记本实例,然后单击创建笔记本实例:

如何创建新的笔记本实例

提示:通过设置生命周期配置,您可以设置每次启动或创建笔记本时运行的默认脚本。通过使用这种方法,您的工作效率会更高,因为您不需要每次都指定经常使用的库、工具或存储位置。

SageMaker 生命周期配置

2。创建新笔记本时的选项

2.1。笔记本实例类型 Sagemaker 提供了广泛的实例类型选择,经过优化以适应不同的机器学习(ML)用例。他们做工作的方式不同。通过混合使用 CPU、GPU、内存和网络容量。选择正确的实例类型取决于您。甚至可以动态地更改实例类型。如果你的项目变得越来越大,你就可以把资源变得越来越大。

实例示例

举例:

ml.t2.medium =机器学习标准二代中等大小

ml.r5.2xlarge =机器学习内存优化第五代超大

2.2。弹性推断

为了加快你的学习进度,你可以选择 GPU 驱动的实例类型。另一方面,GPU 驱动的实例比 CPU 或 RAM 驱动的实例更昂贵。但是对于实时操作来说,它们的延迟通常太高。如果你不想妥协,你应该看看弹性推理。弹性推理允许您将适量的 GPU 驱动的推理加速附加到 Sagemaker 模型上。为您的应用选择最佳的 CPU 实例类型,附加适量的 GPU 加速,获得两全其美。

2.3。权限和加密

IAM 代表身份和访问管理。IAM 是一项服务,可以帮助您安全地控制对 AWS 资源的访问。您使用 IAM 来控制谁被验证(登录)和授权(有权限)使用资源。
注册您的第一个帐户时,您将拥有一个全访问单点登录帐户(也称为 root 用户)。AWS 强烈建议不要使用 root 用户执行日常任务,甚至是管理任务。相反,您应该从单个 IAM 角色开始。你也可以加密你的笔记本。更多细节请看 AWS 文档。

权限和加密

2.4。可选定制

亚马逊虚拟私有云(亚马逊 VPC)使您能够将 AWS 资源启动到您定义的虚拟网络中。这个虚拟网络非常类似于您在自己的数据中心运行的传统网络,具有使用 AWS 的可扩展基础架构的优势。

可选定制

将 Git 存储库与您的 notebook 实例相关联,以便在源代码控制环境中保存您的笔记本,即使您停止或删除您的 notebook 实例,该环境也会持续存在。您可以将一个默认存储库和最多三个附加存储库与一个笔记本实例相关联。存储库可以托管在 AWS CodeCommit、GitHub 或任何其他 Git 服务器上。它有助于坚持、合作或学习。

2.5。完了!

如果您完成了参数设置,您可以点击创建笔记本实例。如果您的笔记本处于活动状态,可能需要一段时间。通常几分钟。休息一下,喝杯咖啡,为下一步做准备。

3。启动你的笔记本

当您的笔记本处于使用状态时,您可以使用 Jupyter 或 JupyterLab 启动它。在这个演示中,我们使用常规 Jupyter。

亲提示: 离开时一定要把笔记本停好。即使没有模型,也有成本。

笔记本正在使用中

然后点击 SageMaker 示例

SageMaker 笔记本列表

正如你在上面的图片中看到的,有大量不同的例子。它们按主题分组。每个话题都是可扩展的。选择一个最符合你需求的。由于我们正在使用大数据和机器学习,我们选择:“使用 XGBoost 的客户流失预测-
使用梯度提升树来预测移动客户离开
”。点击使用,笔记本启动。

机器学习笔记本

笔记本讲的是什么: 笔记本描述了使用机器学习(ML)来自动识别不开心的客户,也称为客户流失预测。失去客户对任何企业来说都是代价高昂的。尽早发现不满意的客户,让你有机会激励他们留下来。

关于使用 XGBoost 进行客户流失预测的 SageMaker 笔记本

现成的笔记本带你经历一个典型的 ML 循环的每一步。这是一个公共数据集,所以很容易按照说明。现在,您可以看到并学习如何使用 XGBoost 算法来构建、训练和学习。玩得开心,享受吧!

专业提示:
为了最大限度地利用本教程并获得完整的理解,阅读一下关于 AWS S3 会很有好处。

当停止或完成工作时,请始终记住移除托管端点并避免被收费。

本笔记本展示了如何使用 aws 现成的 SageMaker jupyter 笔记本来增加您的 ML 知识!我希望你喜欢它。如果您有任何问题或建议,请随时使用下面的评论功能。敬请关注更新!

拯救之旅:动力、深度参与和动态细分市场

原文:https://towardsdatascience.com/saver-journeys-momentum-deep-engagement-dynamic-segments-63fb24aafd1b?source=collection_archive---------56-----------------------

我们再看一眼 Jupiter 的早期数据,对用户进行细分,并试图预测是什么让一个人从低倾向储蓄者变成高倾向储蓄者

版权所有木星储蓄。知识共享署名 4.0 国际许可。

合成:

  • 应用程序参与度、储蓄势头和奖励能够以约 90%的准确率预测储蓄者的细分动态。
  • 储蓄者抵制简单的分类,但是关注他们的旅程,而不是他们的静态点,导致可以采取行动的结构和意义。
  • 关于固有用户特征或“大数据”的毫无根据的先验可能是无益的,尤其是在早期应用数据科学时。

寻找既健壮又有用的片段

之前的一篇文章中,我们看了一下 Jupiter 关于促使用户存更多钱的数据。在这一期节目中,我们将着眼于木星首批约 1000 名储户的总体行为,或者说细分和聚类。

第一步是传统的“RFM”细分——“新近度”(某人最近储蓄了多久)、“频率”(他们储蓄的频率),以及“货币量”(他们的余额是多少)。

我们将所有储蓄者分成四分之一,然后组合标签,因此“111”意味着“前四分之一最近,前四分之一频率,前四分之一货币”,等等。通常当你这样做的时候,一个或两个部分会占主导地位(如果事情进展顺利,111;如果你遇到麻烦,444)。这是我们的情节:

标准分割技术中的高度异质性

至少我们最大的细分市场是 111,但这是一个很大的分散。我们可以将 111 和 112 归类为“频繁,高价值”,然后将 443 和 442 归类为“不频繁,低价值”,但这最多只能吸引 30%的用户。

储户不会落入一个漂亮的桶或几个角色

在这之后,我们吵了很久。我们添加了行为数据并运行了一系列分析( K-MeansHDBScant-SNE 等)。大多数出现了和 RFM 一样的结果——大量的分散,没有直观的结构。不过,最终我们结合了两种方法,并开始取得进展:

(1)应用主成分分析( PCA )来找到用户事件流中变化的主要来源。我们可以通过他们打开 Jupiter 应用程序和参与“片段”(应用程序内的金融知识片段)的频率、以及他们保存和赎回 boost 的频率来解释用户之间的差异。交叉相关性加强了这一发现。参与是一个有意义的变化轴

(2)将储蓄者分为每月储蓄一次以上的储蓄者和储蓄一次或一次以下的储蓄者,合并每个用户的事件计数,并在每个较大的池中运行聚类算法(HDBScan)。这种划分使我们能够在两个池中的每个池中找到仅 2-3 个聚类点的良好匹配,总共有五个细分市场。

当重新结合在一起并根据余额记录绘制时,我们最终找到了一种聚类方法,这种方法既代表了高维行为数据,又显示了值的清晰级数。这五个部分来自数据,而不是我们的先验,所以我们不能给它们起一个可爱的名字,但是我们可以开始问一些关于它们的好问题。

储蓄者按储蓄和余额的频率绘制,按价值分类着色

使用丰富的数据使分段动态化

细分只有在导致行动时才有用。所以我们问,我们能不能找到过去储户行为的一个方面,来预测他们现在属于哪一类?从那以后,我们的哪一个杠杆将最有效地影响这种行为,从而将储户转移到更有价值的细分市场?

首先,我们运行了各种模型,使用每个用户的事件计数作为输入特征,并将他们的最终细分作为目标标签。不幸的是,没有一个模型的精度远远超过 0.5——这是抛硬币的结果。考虑到一个内部结构复杂的小数据集,这并不奇怪。

但在这里,拥有一个丰富的数据集,构建和结构化,以方便流畅地进行这些分析,变得非常宝贵。对于我们来说,储蓄器的每个方面——事件、余额、行为——都是一系列实时事件的集合。通过一点跑腿工作,我们可以滑动分段事件几乎任意地沿着时间线计数

从 6 月初到 9 月底,我们使用这种能力在 3.5 个月的时间内每 3 天收集一次数据。这让我们从几百个数据点增加到接近 15000。

当我们把自己局限在单一时间点的问题上时,我们的模型仍然没有得到很大的提升,“我们能预测用户当前的细分市场吗?”这在某种程度上并不令人惊讶。如果数据增加接近重复,那么数据增加的帮助要比“大数据”的天真想法小得多。关于静态横截面结构的问题,仅移动横截面就非常接近于复制(参见下面的投影图)。

有限数据与增强数据的 t-SNE 图:二维空间已经填满,但没有清晰的结构出现

所以我们改变了问题,变成了关于在时间上的进化,一个更有价值也更容易处理的问题

我们问:什么能预测储户是否会在下个月“升级”他们的细分市场? 从一个关于用户在哪里的问题,我们产生了一个关于用户要去哪里的问题。在我们扩展的数据集中,每次我们都要向前看一个月,并检查用户群的变化。基本面数据点成了储户旅程中的一个阶段。

这并没有使扩增完全没有损失,但确实使它明显更好。对于理解静止的你这个问题,今天的你和一个月前的你或多或少是一样的;对于理解你的储蓄习惯是如何演变的问题,今天的你和一个月前的你有很大的不同,足以找到额外的意义。

参与+推动价值增长的动力

证据就在布丁中——模型度量。根据我们增加的数据,我们运行模型来预测某一天的某个特定储户,在当时对他们的一切都了解的情况下,是否会在下个月增加一个细分市场。仅使用简单的模型,准确率立即跃升至 70%,具有良好的假阳性/假阴性率。

由于数据集相当平衡,我们决定引入一些重型机器,并将数据输入谷歌云的 AutoML。它拟合了一个相当复杂的梯度增强树,并达到了 90%的准确度。我们担心标签泄露,但是交叉相关性、训练曲线和特征重要性分数都让我们放心了。

那么哪些特性最重要呢?

第一,气势。最重要的两个特征(25%和 15%)是储蓄者的余额四分位数和最近四分位数。因此,能够存很多钱(按原始金额计算)确实很重要——如果不重要的话,坦率地说,这很可疑——但这只能解释一个人晋升的可能性的四分之一。紧随其后的是近期。将这些放在一起作为对“动力”的粗略衡量,可以解释储户未来 40%的动力。

二、订婚,尤其是深度订婚。下一个重要的特性是用户打开应用程序的频率。然后——这让我们吃惊——用户查看应用内信息存档的频率。紧接着是用户点击屏幕中央的金融知识“金块”的频率。

这些可以解释为用户不只是打开应用程序,而是觉得有理由去探索它。每一个都占总特征重要性的 10%。当结合其他一些相关功能(如共享推荐代码或探索“拯救伙伴”)时,深度参与总共解释了另外 30%的变化

最后,情感奖励和储蓄习惯。与我们前一篇文章中的发现相呼应,用户存钱的频率和他们是否已经兑现了一次提升占了另外 15%的特征重要性。最后的 10%分散在一系列其他功能中。

最终模型中的特征聚类和重要性

如何防止储户将下移到价值段?动力和参与因素同样重要。但是他们加入了出站通信。也就是说,如果我们最近给用户发了信息或让他们提高报价,用户就不太可能降级(通过退出或进入休眠状态),即使他们没有深度参与应用程序。这是一种解脱,因为我们曾经想知道,打扰人们,或者只是过于频繁地提醒他们有这个储蓄罐,有时会导致提款。注意:并不是说我们认为这是垃圾邮件的许可证。

结论:使用(或不使用)旧的心智模型和大数据与丰富数据

总的来说我们能说什么?首先,我们第一篇文章中的发现得到了一些强化——行为是可塑的,情感和参与很重要,不仅仅是消费应用,也不仅仅是交易和加密。

还有另外两个更广泛的观点。有时我们会惊讶地经常被问到,“你的目标人群是什么?”一方面,如果只是最初的捷径,这是一个可以理解的问题。在某种程度上,年龄会对储蓄产生影响——职业道路只是让可用于储蓄的收入和不同年龄的储蓄压力有所不同。

但是仔细观察,这个问题可能代表一个没有根据的先验。更糟糕的是,这可能是一条糟糕的捷径。讽刺地说:如果你能在年龄和性别上做一个 Excel 支点,并认为它是连贯的,为什么要试图理解储蓄者行为的细微差别?过分简单的数据工具导致过分简单的想法,这些想法成为主导未来分析的先验知识。

我们有木星拯救者的年龄数据。我们还没有发现它能有效地解释储蓄者的行为。我们不需要它来达到预测节段运动 90%的准确性,或诱导反应率 70%以上的准确性,或在抵制任何简单分割的数据中寻找意义。

如果人口统计学很重要,他们会以结构的形式出现——所以只需要寻找结构。如果他们不这样做,他们就是一种虚假的一致性,是由简单的工具或大而有限的数据造成的。

一个相关的注释是关于如何判断数据。其他都等于,数据越多越好(好得多)。但是其他的几乎都不等于

我们 Jupiter 团队中的一些人使用新的纯数字银行(“neobanks”)。今年搬家国的时候开的账户(非常【2020 年初)。我用它作为我的主要银行账户。仅仅是交易数量就足以说明这一点。但是这个应用程序一直在使用宝贵的屏幕空间来帮助我“迁移到我们这里作为你的主要账户”。鉴于我交易的参考,它还不断为我提供我显然已经拥有的产品和服务的折扣。

这个 neobank 拥有 500 多万用户,因此它拥有“大数据”,但这些数据要么非常差,要么使用非常糟糕,要么两者兼而有之,因此这些数据不会比他们拥有 50 个数据点更有实际用途

因此,这将是我们一段时间内的最后一次数据发布。我们希望这篇文章和上一篇文章能让我们更好地了解我们正在构建的东西。希望他们也清楚地表明了丰富的数据可以做多少事情,即使是在早期——只要你抛开过去,充分利用现代数据方法的力量。

最重要的是,我们希望他们已经表明,一些关于储蓄的旧观念——都是关于违约的,你不能让它变得有趣,只是减少摩擦,等等——至少对严肃的问题是开放的。即使这给我们带来了竞争,我们也希望其他人将很快超越已经厌倦的现代金融科技储蓄工具包(这里一个目标,那里一个总结),更接近理解储户的行为,并帮助他们改变这种行为。

使用 AWS Lambda 进行 scikit-learn 预测可节省 95%的基础设施成本

原文:https://towardsdatascience.com/saving-95-on-infrastructure-costs-using-aws-lambda-for-scikit-learn-predictions-3ff260a6cd9d?source=collection_archive---------36-----------------------

用于实时机器学习推理的 AWS Lambda 与 AWS SageMaker

声明:我是 模型动物园 的开发者,一个专注于易用性的模型部署平台。

https://modelzoo.dev/lambda-vs-sagemaker-cost/试用我们的工具

如果你是一个需要部署机器学习模型进行实时推理的 AWS 客户,你可能会考虑使用 AWS SageMaker 推理端点。然而,模型部署还有一个选项,这个选项有时会被忽略:直接在 AWS Lambda 上部署。虽然它有一些警告,但是 Lambda 的简单性和成本效益使得它值得考虑 SageMaker 端点用于模型部署,特别是当使用 scikit-learnxgboostspaCy 时。在本文中,我们将回顾使用 AWS Lambda 进行 ML 推理的一些好处和注意事项,并深入一些相关的基准测试。我们表明,在低使用率的情况下(每月< 2M 预测),当将模型从 SageMaker 迁移到 Lambda 时,您可以节省高达 95%的基础设施成本。我们还将展示 scikit-learn-lambda ,这是我们的开源工具包,用于在 AWS Lambda 上轻松部署 scikit-learn。

什么是 AWS SageMaker 推理端点?

通过 SageMaker 端点进行实时 ML 推理的 AWS 基础设施图

SageMaker 推理端点是 AWS 提供的令人印象深刻的端到端机器学习工具包的许多部分之一,从数据标记( AWS SageMaker 地面真相)到模型监控( AWS SageMaker 模型监控器)。SageMaker 推理端点提供了围绕 GPU 加速、自动缩放、AB 测试、与培训管道集成以及与离线评分集成的功能( AWS 批量转换)。这些特性的代价很高——最便宜的推断端点(ml.t2.medium)将花费您$ 50/月来运行 24/7。下一个最佳终点(ml.t2.xlarge)是$ 189.65/月

什么是 AWS Lambda?

AWS Lambda 是一个通用的无服务器计算平台

AWS Lambda 是无服务器计算运动的先驱,让您无需提供或管理服务器即可运行任意功能。它只在需要的时候执行你的代码,并且自动伸缩,从每天几个请求到每秒几百个请求。Lambda 是一个通用的函数执行引擎,没有任何机器学习的特定功能。它激发了一个不断增长的工具社区,一些来自 AWS 本身(无服务器应用模型)和一些外部附属的(无服务器框架)。

AWS Lambda 对实时机器学习推理的好处

  • Lambda 有一个按请求付费模式,可以随着你的成长而扩展。这是赞成还是反对取决于你的使用水平,但特别是成本效益服务在 2M 预测每月。在撰写本文时,SageMaker 不支持在低活动期自动调整到零。
  • Lambda 需要一个更简单的实现计算模型,其中并发/自动伸缩可以在逻辑之外透明地处理。
  • Lambda 要求降低 维护工作量,无需管理任何底层服务器或逻辑。
  • Lambda 有一个丰富的开发者生态系统(开源和 SaaS),用于监控、记录和测试无服务器应用,比如无服务器应用。有了 SageMaker,你就依赖于 AWS 特有的资源,比如用于工具的 SageMaker 兼容容器SageMaker Python SDK

AWS Lambda 用于实时机器学习推理的缺点

导致冷启动延迟的请求生命周期。

  • Lambda 对可用资源有限制,最大 3,008 MB 物理内存不支持 GPU 加速。对于使用具有严格延迟要求的大型模型的基础架构来说,这可能是一个障碍。
  • 当在 5-10 分钟的不活动期后调用该函数时,Lambda 会导致冷启动延迟AWS Lambda上的冷启动是一篇很棒的博文,深入探讨了影响冷启动延迟的一些因素。
  • AWS Lambda 中的包大小限制非常严格,压缩后为 50 MB,解压缩后为 250 MB。有变通办法( AWS Lambda Layers ),但是这对于捆绑常见的 ML 依赖项(如 TensorFlow (~400 MB)和 PyTorch (~250 MB)来说可能是一个恼人的障碍。
  • Lambda 不能直接使用 Docker 容器作为依赖,而是有自己的依赖管理系统 AWS Lambda Layers 。这有时需要用户在打包时做一点额外的工作。

案例研究:scikit-learn

pharm assesse是一个模型动物园用户,其任务是更自信、更高效地治疗更多患者。他们开发了一个专有模型,可以使用 scikit-learn 自动进行小病评估、诊断和治疗&文档,现在正在与一组初始用户一起测试它。他们的模型大小约为 50 MB,是 Lambda 部署的理想选择。

设置

通过 Model Zoo,PharmAssess 能够使用简单的 Python API 部署其 scikit-learn 模型。在幕后,Model Zoo 将把 scikit-learn 模型打包并部署到 AWS Lambda 函数中(参见我们的 quickstart 获取完整示例)。

import modelzoo.sklearnmodel = ...  # train model
modelzoo.sklearn.deploy(model)

如果您喜欢维护自己的模型存储、部署和监控堆栈,您可以使用我们的开源包 scikit-learn-lambda 通过无服务器框架将模型部署到您自己的 AWS 云上:

$ git clone [https://github.com/model-zoo/scikit-learn-lambda](https://github.com/model-zoo/scikit-learn-lambda)
$ cd scikit-learn-lambda
$ cp <model file> scikit-learn-lambda/model.joblib
$ serverless deploy

延迟基准

AWS Lambda 的一个重要警告是“冷启动”,即在 5-10 分钟不活动后发生的初始化开销。为了了解严重性,我们生成了具有三个隐藏层的 MLPClassifier 模型,每个隐藏层使用 100、250、500、750 和 900 的层大小。每个模型都在 iris 数据集上训练,并使用 joblib 库序列化到磁盘。这导致模型文件的大小分别为 0.68 MB、4.08 MB、16.17 MB、36.25 MB 和 52.13 MB。我们使用 scikit-learn-lambda 将每一个部署到具有 1024 MB 内存的 lambda 函数,并测量三个样本的平均冷启动延迟,以生成上面的基准。

我们还在下面绘制了十五个样本的平均“热”延迟,每个模型的平均延迟在 2-5 毫秒左右。

有一些解决冷启动延迟的方法,比如每隔几分钟就触发你的函数来保持它的热度或者使用提供的并发。但是,如果针对 p90+延迟进行优化是一项重要的业务需求,那么无服务器模式可能不适合您的使用案例。

成本基准

AWS Lambda 的使用情况很难结合每个请求的时间、内存使用情况和每月的总请求量来衡量。我们创建了这种交互式可视化,以帮助我们了解各种使用场景下的每月成本,以 SageMaker 端点为基准。

[## AWS Lambda 与 AWS SageMaker 成本计算器

modelzoo.dev](https://modelzoo.dev/lambda-vs-sagemaker-cost/)

成本差异不言自明:使用上面观察到的平均热延迟和 1024 MB 的分配内存,Lambda 比来自最低成本层的单个 SageMaker 端点便宜一个数量级。按请求付费的模型也使其非常适合 AB 测试和原型开发——您可以部署您训练的每一个模型,而不必担心利用不足。

TL;dr:什么时候应该使用 AWS Lambda?

一般来说,AWS Lambda 对较小的模型(<50MB) that can use CPUs for inference and for which you expect a usage pattern of <2M requests/month. This makes it ideal for building 原型新产品有意义,它们使用轻量级模型库【2】,如 scikit-learnxgboostspaCy

使用 JSON 和 YAML 文件保存和加载 Keras 模型

原文:https://towardsdatascience.com/saving-and-loading-keras-model-42195b92f57a?source=collection_archive---------7-----------------------

使用 JSON 和 YAML 文件保存和加载深度学习模型的快速简单的方法

维克托·弗雷塔斯在 Unsplash 上的照片

你如何与在世界不同地方工作的其他团队成员分享你训练的深度学习模型,或者如何在训练期间或之后保存深度学习模型及其训练的权重,或者如何从你停止的地方恢复模型的训练?

要回答以上问题我们需要保存一下

  • 模型建筑
  • 模型的训练权重或参数

共享保存的文件将允许其他人重新创建模型来进行推理或从您停止的地方继续进一步的训练。可以通过加载模型来重新创建模型,并从包含模型架构和预训练权重的保存文件中训练权重。

使用不同的方法来保存和加载深度学习模型

  • JSON 文件
  • YAML 档案
  • 检查点

在本文中,您将学习如何将 Keras 开发的深度学习模型保存为 JSON 或 YAML 文件格式,然后重新加载该模型。

为了保存模型,我们首先创建一个基本的深度学习模型。我使用了时尚 MNIST 数据集,我们用它来保存,然后用不同的方法重新加载模型。

我们需要安装两个库:**pyyaml and h5py**

**pip install pyyaml
pip install h5py**

我用的是 Tensorflow 1.14.0

#Importing required libararies
**import os
import tensorflow as tf
from tensorflow import keras**#Loading Fashion MNIST dataset
**(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.fashion_mnist.load_data()**#creating a smaller dataset 
**train_labels = train_labels[:1000]
test_labels = test_labels[:1000]**#Normalizing the dataset
**train_images = train_images[:1000].astype('float32') / 255
test_images = test_images[:1000].astype('float32') / 255**# Reshaping the data for inputing into the model
**train_images = train_images.reshape((train_images.shape[0],  28, 28,1))
test_images = test_images.reshape((test_images.shape[0],  28, 28,1))**#Defining and compiling the keras model
**def create_model():
    model = tf.keras.Sequential()
    # Must define the input shape in the first layer of the neural network
    model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=(28,28,1))) 
    model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(256, activation='relu'))
    model.add(tf.keras.layers.Dropout(0.5))
    model.add(tf.keras.layers.Dense(10, activation='softmax'))** #Compiling the model **model.compile(loss='sparse_categorical_crossentropy',
             optimizer='adam',
             metrics=['accuracy'])

    return model**# Create a basic model instance
**model = create_model()****model.summary()**

使用 JSON 文件保存和加载模型架构

将模型保存和加载到 JSON 文件的步骤

  • 将训练数据拟合到模型中
  • 使用 to_json() 将模型架构保存到 JSON 文件中。返回的字符串将保存在一个 JSON 文件中
  • 使用 save_weights() 保存训练好的重量
  • 通过读取 JSON 文件从 JSON 文件中重新加载模型架构,然后使用 model_from_json() 将模型架构重新加载到新模型中
  • 将使用 load_weights() 重新加载已训练的重量

将列车数据拟合到模型中

#Fit the train data to the model **model.fit(train_images, 
          train_labels,  
          batch_size=64,
          epochs=100,
          validation_data=(test_images,test_labels))**

将模型架构和权重保存到 JSON 文件

使用 to_json() 将模型架构写入 JSON 文件,并使用save _ weights()保存训练好的模型的权重

**from keras.models import model_from_json**# serialize model to json
**json_model = model.to_json()**#save the model architecture to JSON file
**with open('fashionmnist_model.json', 'w') as json_file:
    json_file.write(json_model)**#saving the weights of the model
**model.save_weights('FashionMNIST_weights.h5')**#Model loss and accuracy
**loss,acc = model.evaluate(test_images,  test_labels, verbose=2)**

原始模型的损失和准确性

将模型架构从 JSON 文件重新加载到新模型

我们已经创建了一个新的模型: model_j ,它将与保存的模型具有相同的架构

我添加了 glorot_uniform 库,因为我得到了初始化器的警告

**from keras.initializers import glorot_uniform**
#Reading the model from JSON file
**with open('fashionmnist_model.json', 'r') as json_file:
    json_savedModel= json_file.read()**#load the model architecture 
**model_j = tf.keras.models.model_from_json(json_savedModel)
model_j.summary()**

新模型与先前保存的模型具有相同的架构

将训练好的重量重新加载到新模型中

使用 load_weights() 将预训练的权重加载到新模型中

**model_j.load_weights('FashionMNIST_weights.h5')**

只有模型架构和预训练的权重被加载到新模型中,但是缺少模型编译细节,所以我们需要编译模型。

我们可以指定不同的优化器来编译模型,比如将优化器从 Adam 改为“Rmsprop”或“SGD”

#Compiling the model
**model_j.compile(loss='sparse_categorical_crossentropy',
         optimizer='SGD',
         metrics=['accuracy'])**

我们可以检查新模型的损失和准确性,以确认是否加载了相同的训练重量

loss,acc = model_j.evaluate(test_images,  test_labels, verbose=2)

使用 JSON 文件的新模型的测试数据的模型损失和准确性

使用 YAML 文件保存和加载模型架构

将模型保存和加载到 YAML 文件的步骤

  • 将训练数据拟合到模型中
  • 使用 to_yaml() 将模型架构保存到 YAML 文件中。返回的字符串将保存在 YAML 文件中
  • 使用 save()将训练好的重量保存在 H5 文件中。
  • 通过读取 YAML 文件从 YAML 文件中重新加载模型架构,然后使用 model_from_yaml() 将模型架构重新加载到新模型中
  • 使用 save() 保存训练后的重量,并使用 load_model() 加载到新模型中

我们将使用之前的模型保存到 YAML 文件。我们不会再次拟合该模型,因为它已经被训练过了。

将模型架构保存到 YAML 文件

使用 to_yaml() 将模型架构写入 YAML 文件

#saving the model to a YAML file
**yaml_model= model.to_yaml()**# writing the yaml model to the yaml file
**with open('fashionmnist_yamlmodel.yaml', 'w') as yaml_file:
    yaml_file.write(yaml_model)**

将模型架构从 YAML 文件重新加载到新模型

从 YAML 中读取模型架构,并使用model _ from _ YAML()将其恢复为新模型 model_y

我们得到了与原始模型相同的模型架构

**#Read  the model architecture from  YAML file**
**with open('fashionmnist_yamlmodel.yaml', 'r') as yaml_file:
    yaml_savedModel=yaml_file.read()**# Load the saved Yaml model
**from keras.models import model_from_yaml
model_y= tf.keras.models.model_from_yaml(yaml_savedModel)
model_y.summary()**

使用 HDF5 格式的*Save()*将权重保存在单个文件/文件夹

  • 模型的架构,以便我们可以随时随地重新创建它
  • 模型的训练权重
  • 训练配置如损失函数、优化器等。
  • 优化器的状态

Save()有助于导出模型并重新创建整个模型,或者从您离开的地方继续训练。

可以使用 load_model()、 恢复模型,这也负责使用保存的训练配置编译模型。

#saving the smodel's architecture, weights, and training configuration in a single file/folder.**model.save('fashionmnist.h5')**

恢复模型

# loading the model from the HDF5 file
**model_h5 = tf.keras.models.load_model('fashionmnist.h5')**

让我们在测试图像上评估新恢复的模型。注意,我们在这里没有编译模型

**loss,acc = model_h5.evaluate(test_images, test_labels, verbose=2)**

使用 YAML 文件的新模型的测试数据的模型损失和准确性

我们得到大致相同的损失和精度,因为我们没有在这里设置种子。

保存模型并使用时尚 MNIST 重新加载模型的代码

结论:

我们现在可以保存权重,并使用 JSON 文件或 YAML 文件将它们重新加载到一个新的模型中,这将允许您与世界分享您出色的深度学习工作,或恢复您的训练以获得更好的表现。

使用检查点保存和加载模型将在下一篇文章中讨论

参考资料:

[## 保存和加载模型| TensorFlow 核心

您看到了如何将权重加载到模型中。手动保存它们与模型一样简单

www.tensorflow.org](https://www.tensorflow.org/tutorials/keras/save_and_load)

https://keras . io/getting-started/FAQ/# how-can-I-save-a-keras-model

保存雅加达洪水数据以备不时之需

原文:https://towardsdatascience.com/saving-jakarta-flood-data-for-a-rainy-day-d44695e487d6?source=collection_archive---------85-----------------------

Python 如何帮助清理混乱的数据集,以便对未来的洪水监测进行潜在的改进

乔丹·罗兰Unsplash 上的照片

新的一年新的开始。嗯,对雅加达来说不是,它很快将成为印度尼西亚的非首都城市。当我们通常在那天晚上燃放烟花庆祝时,雅加达的人们却在忙着游泳。不过,方式不对。这场洪水破坏了雅加达的庆祝活动,而这几乎是一个传统。到 2020 年 2 月底,已经发生了 6 次。

关于洪水灾害数据,雅加达政府已经与 petabencana.id 和灾害对策机构(BPBD 雅加达)合作,通过他们的官员或 petabencana.id 平台内的社区来监控洪水事件。最终产品将在一个名为 Jakarta Satu 的平台上展示。最热门的产品是洪水地图,从 2020 年 11 日开始。问题是,给出的数据有多好?嗯,我不抱太大希望。我去过那里。

雅加达 Satu 平台,显示 2020 年 11 日的洪水事件

雅加达的洪水数据有点问题。好吧,这里没有糖衣。太可怕了。从平台上看,这张地图显然很漂亮,但在它背后?相反,这是一项有些草率的工作。尽管有一些有价值的信息,但数据管理从一开始就没有真正计划好,尤其是在 1 月 2 日更新之后。

洪水测绘数据可通过中的 REST 服务获得,此处为。此处的目的是将自 11 日以来发生的许多洪水事件的数据合并,并将其整理成更整洁、更易读的数据。数据管理是使用本笔记本中的 Pandas 和 Geopandas 完成的。

1.数据和信息管理

初始条件

第一批数据和其他数据明显不一致。原始数据(11 日)仅关注洪水分类,用数字表示严重程度(1 表示低,5 表示非常高)。与此同时,1 月 3 日和其他日期的数据发生了重大变化,变得更加详细。意图是好的。但是,仍然有许多缺失值和空白列。

11 日有史以来第一次洪水列单:

[‘OBJECTID_1’, ‘OBJECTID’, ‘KAB_NAME’, ‘KEC_NAME’, ‘KEL_NAME’, ‘RW’, ‘ID’, ‘KETERANGAN’, ‘GENANGAN’, ‘TINGGI’, ‘FLOOD’, ‘F01012020’, ‘SHAPE.AREA’, ‘SHAPE.LEN’, ‘geometry’]

次日至最近(1 月 3 日-1 月 7 日)的栏目列表:

[‘OBJECTID_1’, ‘OBJECTID’, ‘KAB_NAME’, ‘KEC_NAME’, ‘KEL_NAME’, ‘RW’, ‘ID’, ‘KETERANGAN’, ‘GENANGAN’, ‘TINGGI’, ‘FLOOD’, ‘F01012020’, ‘KECAMATAN’, ‘KELURAHAN’, ‘ID_RW’, ‘KEL_DAN_RW’, ‘RW_1’, ‘RT’, ‘JUMLAH_TER’, ‘JUMLAH_T_1’, ‘JUMLAH_T_2’, ‘JUMLAH_T_3’, ‘JUMLAH_T_4’, ‘JUMLAH_T_5’, ‘JUMLAH_PEN’, ‘JUMLAH_P_1’, ‘LOKASI_PEN’, ‘MULAI’, ‘KONDISI_AW’, ‘KETINGGIAN’, ‘KETERANG_1’, ‘WAKTU_SURU’, ‘PENANGANAN’, ‘PENANGAN_1’, ‘PENANGAN_2’, ‘PENANGAN_3’, ‘BANTUAN’, ‘TINGGI_MAX’, ‘Shape__Are’, ‘Shape__Len’, ‘geometry’]

提高可读性

因此,为了整理出 11 日洪水的全面信息,必须提高可读性。以下是我目前所知道的,并相应地重新命名:

Kab_name to ID 栏 : 行政 信息| mulai:洪水监测时间| keterang_1:可能的洪水原因| kondisi_aw:初始条件| f 010102020:11 日洪水| tinggi:洪水深度| keting gi:洪水严重程度指数|tinggi_max:最大深度

# Get the only valuable column for the analysis
flood_period_one = flood_0301_1800[[‘KAB_NAME’, ‘KEC_NAME’, ‘KEL_NAME’, ‘RW’, ‘RT’, ‘ID’, ‘MULAI’, ‘KETERANG_1’, ‘KONDISI_AW’, ‘F01012020’, ‘TINGGI’, ‘KETINGGIAN’, ‘TINGGI_MAX’, ‘geometry’]]# Rename them with proper naming to improve readability
flood_period_one.columns = [‘KAB_NAME’, ‘KEC_NAME’, ‘KEL_NAME’, ‘RW’, ‘RT’, ‘ID’, ‘START_TIME’, ‘CAUSE’, ‘INITIAL_CONDITION’, ‘FLOOD_CLASS_0101’, ‘FLOOD_CLASS_0301_1800’, ‘DEPTH_0301_1800’, ‘MAX_DEPTH_0301_1800’, ‘geometry’]

合并数据帧

后来,洪水泛滥被证明持续到 1 月 7 日左右。为了给出一个更广阔的视角,我将它们结合起来(从 1 月 3 日午夜到 1 月 7 日晚上)来看洪水每天的发展,从最高到最低的状态。将它们组合在一起的关键在于“ID”列。

# List of data that need to merge
flood_event = [flood_0301_2400, flood_0401_0600, flood_0401_1200, flood_0401_1800, flood_0401_2400,flood_0501_0600, flood_0501_1200, flood_0501_1800, flood_0501_2400,flood_0601_1800, flood_0601_2400,flood_0701_1800]#Join all the dataframe
from functools import partial, reduce
flood_merge = partial(pd.merge, on=['ID'], how='left')
flood_result = reduce(flood_merge, flood_event)

为了简化必要的数据,我将数据整理成关于洪水深度的具体数据。

# Take the ID before filtering commenced
flood_ID= flood_result[['ID']].copy()# Filtering the column list with Regex to ease up the job
flood_nextday = flood_result.filter(like=’TINGGI’,axis=1).copy()#Rename the column list
flood_nextday.columns = [‘FLOOD_CLASS_0301_2400’, ‘DEPTH_0301_2400’, ‘MAX_DEPTH_0301_2400’,‘........', ‘FLOOD_CLASS_0701_1800’, ‘DEPTH_0701_1800’, ‘MAX_DEPTH_0701_1800’]# Then join them together with concat()
flood_join  = pd.concat([flood_ID, flood_nextday], axis=1).reindex(flood_result.index)

最后,我们用这个把它们粘在一起。

# Merge from the first our clean dataframe called period_one
flood_1Jan = flood_period_one.merge(flood_join, how=’left’, on=’ID’)# Move the geometry to the first column index
flood_1Jan = flood_1Jan[[col for col in flood_1Jan.columns if col != 'geometry' ] + ['geometry']]#Show the result
flood_1Jan.sort_values(by='FLOOD_CLASS_0101', ascending=False).head()

但是,有许多关于内容和缺失数据的担忧,因此需要采取进一步的行动来实现近乎完美。

洪水可能原因

该信息还包括导致该事件的可能因素的原因。尽管有这样的意图,但数据提供得并不好,缺少很多值。这些信息几乎包含了这样的文字,例如:'Penyebab:Lua pan Kali Krukut'表示洪水来自 kru Kut 海峡。所以我只打算删除文本‘Penyebab’(cause),因为它与进一步的处理无关。

flood_1Jan[‘CAUSE’] = flood_1Jan[‘CAUSE’].str[10:]

日期和时间

洪水监测时间在 START_TIME 列中给出。但是,该列是在字符串列上形成的。我建议这应该是一个日期时间对象。大部分初始数据看起来是这样的。

拉布,2020 年 11 日,普库尔 05:00 WIB

我们需要消除日名(拉布/星期三)、普库尔(时间)和 WIB(当地时间)

#Split between the comma and whitespace
date_split = flood_1Jan[‘START_TIME’].str.split(‘, ‘ , expand = True)#Get the index 1 column which consist of date (1 Januari 2020) and map in dictionary.
date_split[1] = date_split[1].map({’01 Januari 2020': ‘01–01–2020’, ’02 Januari 2020': ‘02–01–2020’})
date = date_split[1]#Assign the time data from index 2 of data split and split again then replace the '.' with ':' 
time = date_split[2].str.split(‘ ‘ , expand = True)
time = time[1].str.replace(‘.’, ‘:’)

然后,用转换成 DateTime 对象。strptime

from datetime import datetime
date_time = date + ‘, ‘+ time
date_time = date_time.astype(str)
flood_1Jan[‘START_TIME’].update(date_time)
flood_1Jan[‘START_TIME’] = flood_1Jan[‘START_TIME’].apply(lambda x: datetime.strptime(x,’%d-%m-%Y, %H:%M’ if x != ‘nan’ else x))

2.错误的管理数据

使用的管理数据是不同的。1 月份的洪水数据使用的是 2013 年数据的底图,而 2 月份的数据使用的是 2019 年的最新数据。这是相当独特的,因为这是一个提供准确性的问题。2013 年的数据在某些时候很麻烦。

ax = boundaries_january.plot(figsize=(15,15), facecolor=’none’, edgecolor=’red’)
boundaries_february.plot(ax=ax, facecolor=’none’, edgecolor=’black’)

基于 2013 年(红色)和 2019 年(黑色)的行政空间数据使用不当

是的,他们对自己的产品感到困惑。是的,还在学习…

因此,为了解决这个问题,我需要在这两个数据之间匹配相似的数字。我最大的猜测是 ID 栏。但不幸的是,事实并非如此。ID 已经完全用不同的编码改变了。所以,目前,我只是让它保持原样。也许在本文的下一部分。

3.空白和缺失数据

有一些关于不同位置的深度范围的信息。虽然我认为这可能会造成一些混乱和无用的由于广泛的差距范围。例如,10–150cm在这种情况下没有发现帮助。但是在某些情况下可能会有帮助,所以我决定稍微整理一下数据。

# The flood depth range located on column started with 'DEPTH_' so just use filterdepth = list(flood_1Jan.filter(regex=’^DEPTH’,axis=1))
depth.append('INITIAL_CONDITION')#Replace certain text to maintain similarity between the datafor depth_range in depth:
flood_1Jan[depth_range] = flood_1Jan[depth_range].str.replace(‘ cm’, ‘’)
flood_1Jan[depth_range] = flood_1Jan[depth_range].str.replace(‘s.d’, ‘s/d’)
flood_1Jan[depth_range] = flood_1Jan[depth_range].str.replace(‘s/d’, ‘-’)

此外,flood 类列需要转换为数字,并将零值替换为 NaN。稍后,它将对时间序列分析有用。相信我。

flood_class = list(flood_1Jan.filter(regex=’^FLOOD’,axis=1))for floods in flood_class:
flood_1Jan[floods] = pd.to_numeric(flood_1Jan[floods])
flood_1Jan[floods] = flood_1Jan[floods].replace(0, np.nan)

因此,除 11 日洪水数据(F0101)外,其他数据没有最大深度值,仅给出了洪水等级。在我们之前的行动之后,洪水的深度范围大部分应该是这样的:‘10–150’。因此,我利用数据的长度捕捉到了最大值。这是因为有些数据的数量是固定的。

# Get the maximum depth of 1st January flood and convert to numeric
add_max_depth_0101 = flood_1Jan[‘INITIAL_CONDITION’].apply(lambda x: x.split(‘-’)[-1] if len(x) > 3 else x)
flood_1Jan[‘MAX_DEPTH_0101’] = pd.to_numeric(flood_1Jan[‘MAX_DEPTH_0101’])# Change column position
flood_1Jan.insert(10, ‘MAX_DEPTH_0101’, add_max_depth_0101)

因此,我用 11 日数据中使用的大部分类别范围来映射字典。这是根据一个已经填写好的值做出的假设。但是,它考虑许多方面来提供一条尽可能准确的信息。

class_dict = {5: 200, 4: 150, 3: 80, 2: 40, 1: 20}flood_1Jan[‘MAX_DEPTH_0101’] = flood_1Jan[‘MAX_DEPTH_0101’].fillna(flood_1Jan[‘FLOOD_CLASS_0101’])flood_1Jan[‘MAX_DEPTH_0101’] = flood_1Jan[‘MAX_DEPTH_0101’].map(class_dict).fillna(flood_1Jan[‘MAX_DEPTH_0101’])

4.错误的分类

所使用的分类不时会有所不同。例如,在同一个地方,洪水深度为 150 厘米。然而,从 11 日起使用的分类是 4 级,而 1 月 3 日的数据显示是 5 级。这里发生了一些不一致。

洪水事件之间不一致的分类

flood_1Jan[‘MAX_DEPTH_0101’] = flood_1Jan[‘MAX_DEPTH_0101’].fillna(flood_1Jan[‘FLOOD_CLASS_0101’])flood_1Jan[‘MAX_DEPTH_0101’] = flood_1Jan[‘MAX_DEPTH_0101’].map(class_dict).fillna(flood_1Jan[‘MAX_DEPTH_0101’])

根据 2020 年 1 月 3 日及其他日期的数据分类,最好遵循这种方法。分类是这样的:

11-30 厘米的 1 级<10 cm
2 级
31-70 厘米的 3 级
71-150 厘米的 4 级
150 厘米的 5 级>

为了从数据中重新分类所有的洪水分类,我做了这个魔术。

verify_flood = [
(flood_1Jan[‘MAX_DEPTH_0101’] >= 150),
(flood_1Jan[‘MAX_DEPTH_0101’] < 150) & (flood_1Jan[‘MAX_DEPTH_0101’] >= 80),
(flood_1Jan[‘MAX_DEPTH_0101’] < 80) & (flood_1Jan[‘MAX_DEPTH_0101’] >= 40),
flood_1Jan[‘MAX_DEPTH_0101’] < 40) & (flood_1Jan[‘MAX_DEPTH_0101’] >= 20),
(flood_1Jan[‘MAX_DEPTH_0101’] < 20),
]classifications = [5,4,3,2,1]flood_1Jan[‘FLOOD_CLASS_0101’] = np.select(verify_flood, classifications, default=np.nan)

问题解决了。数据现在可以交付了。

结论

大多数政府数据在数据准备和管理方面都很糟糕,没有未来的考虑。我知道在这些数据中可以利用很多东西,如果这些数据能够以良好的方式得到充分的处理,这些东西可能会在以后被证明是有益的。这就是我这么做的原因。

你知道吗,这个工作最终会有回报的。这只是第一次互动。这是接下来将要发生的剧透。这是雅加达洪水分析和可视化的下一步。

用机器学习拯救蜜蜂

原文:https://towardsdatascience.com/saving-the-bees-with-machine-learning-9b2ec7189680?source=collection_archive---------34-----------------------

实践教程

开发一个系统来帮助蜜蜂研究人员分析视频

图片来自 weter78/Shutterstock

W 如果没有蜜蜂,世界粮食供应链将会崩溃——大约三分之一的世界粮食产量依赖于蜜蜂。FDA 估计,在美国,蜜蜂授粉给农作物增加了 150 亿美元的价值(T4)。

蜜蜂在自然生态系统中也至关重要,交叉授粉的植物不能自我繁殖,间接为无数其他物种创造食物和住所。

鉴于蜜蜂在工业和环境中的巨大价值,越来越多的科学研究正在进行以了解它们的蜜蜂行为。

我有机会与蜜蜂产品合作研究中心合作,这是一个旨在促进澳大利亚蜜蜂产业发展的项目。CRCHBP 收集研究数据的主要方式之一是记录蜜蜂在其自然栖息地的高清视频。一段 20 秒的视频片段:

你看到右下角的蜜蜂了吗?

为了将这些视频转化为有用的统计数据,比如蜜蜂会拜访哪些花,会逗留多长时间,研究人员需要手动观看数百个小时的这些视频。这个过程可能需要!机器学习能帮助我们吗?

YOLO——别浪费时间了!

我的任务是开发一个系统,可以帮助识别视频中的蜜蜂,以加快分析过程。我得到了几个小时之前带注释的视频,所以我可以马上开始训练神经网络!

YOLOv4 是一个超级快速准确的物体检测框架,我用它创建了一个可以检测蜜蜂的模型。目前,它被认为是最先进的,所有技能水平的开发人员都可以使用。你只需要提供你自己的数据来训练一个定制的目标检测网络。

我将训练数据上传到了谷歌 Colab 笔记本电脑上,它提供了免费的 GPU——非常适合训练神经网络。编译 YOLO 就像把它粘贴到 Colab 实例中一样简单(我在这个项目中的所有代码都是这里是):

神经网络被训练的照片的例子

该网络在超过 40 万张蜜蜂图像上进行训练,总共花费了大约 2 周的时间。

在训练过程之后,网络可以以大约 90%的准确率识别静止图像中的蜜蜂。

为了检测视频中的蜜蜂,我们可以在每一帧上运行网络,然后将所有帧拼接在一起 Python OpenCV 库使这变得超级快速和简单。这个项目的所有代码都可以在这个 Google Colab 链接找到。您可以将它克隆到您自己的驱动器中,并将其用于您自己的数据集!

这是上一个视频的输出:

很好,但是这对研究人员有什么帮助呢?

我们可以编写一些自定义代码来跟踪屏幕上是否有蜜蜂。研究人员将从每一个处理过的视频中得到这个图表的副本。他们可以跳到视频中看到蜜蜂的地方。根据我的估计,蜜蜂只在大约 20%的时间里出现在视频中。

研究人员收到的输出图

有了这样的图表,任何分析视频的人都可以跳到最有趣的部分!由于视频通常长达数小时,这就节省了大量时间。

外卖食品

这是一个需要解决的有趣问题,看到这个系统部署在 CRCHBP 收集的万亿字节的数据上将是有益的。我从中得到的一点是有导师的重要性——西澳大利亚大学的马克·雷诺兹博士和杜惠恩博士在这个项目的整个完成过程中提供了巨大的帮助。

你成功了!非常感谢。如果你喜欢这篇文章,考虑把你的电子邮件放在下面我的邮件列表里!

如果你想和我谈谈,我的电子邮件是 gmail.com 的 chebthony。

告别 Excel?使用新冠肺炎数据对 Python Grid Studio 进行简单评估

原文:https://towardsdatascience.com/say-goodbye-to-excel-a-simple-evaluation-of-python-grid-studio-using-covid-19-data-90624f322b81?source=collection_archive---------1-----------------------

xreschPixabay 拍摄的照片

具有 Python 编程语言技能的数据分析师/数据科学家的替代工具。

最近发现了一个优秀的开源项目“Grid Studio”。这个库结合了电子表格和 Python 在数据分析方面的优势。

你有没有想过

  • 当您使用 MS Excel 时,您希望使用您的 Python 技能和库,如 Numpy、Pandas、SciPy、Matplotlib 和 Scikit-learn 来生成和操作数据
  • 当您使用 Python 时,您可能认为需要数据的表格视图来实时了解当前数据集,但您能做的只是手动输出df.head()

好的,这个库可以满足你的所有要求。

在一切之前,我们先来看看它是什么样子的。Grid Studio 是一个基于 Web 的应用程序。这是网络用户界面。

用户界面分为 3 个主要面板。

  1. 电子表格,与流行软件如 Excel 和 Google Sheets 相同。
  2. 代码区,您可以在其中编写 python 代码。
  3. File/Plots/Terminal/Stdout 窗口,将这 4 个窗口聚合为不同的选项卡。

因此,有了这个库,你就可以像 Jupyter/iPython 一样使用代码区编写你的 Python 代码并逐行运行,而“Python out”窗口会显示结果。此外,您还可以将您的熊猫数据框同步到电子表格,以便立即查看。

装置

Bret Kavanaugh 在 Unsplash 上拍摄的照片

先说 Grid Studio 的安装。您需要在本地机器上安装 docker 来运行源代码。如果你现在没有 docker 桌面,你可以从这里下载:

[## 用于 Mac 和 Windows 的 Docker 桌面

在你的桌面上容器化应用程序的最快方法是 MacOS 和 Windows 的应用程序…

www.docker.com](https://www.docker.com/products/docker-desktop)

之后,在 GitHub 克隆 repo:

[## ricklamers/gridstudio

Grid studio 是一个基于 web 的电子表格应用程序,完全集成了 Python 编程语言。它打算…

github.com](https://github.com/ricklamers/gridstudio)

git clone [https://github.com/ricklamers/gridstudio](https://github.com/ricklamers/gridstudio)

然后,只需转到它的根文件夹并运行启动脚本:

cd gridstudio && ./run.sh

等待 docker 获取所有组件可能需要几分钟时间。此后,您将能够访问 Web 用户界面,网址为

[http://localhost:8080/](http://localhost:8080/dashboard/)

新冠肺炎数据的分析示例

WebarooUnsplash 上拍摄的照片

我不喜欢为了举例而写例子。所以,我们用一些真实的数据,用 Grid Studio 做一些基础的数据分析。

我们可以在这里得到新冠肺炎确诊病例的数据:

[## 下载今天全球新冠肺炎病例地理分布的数据

每天在欧洲中部时间 6:00 到 10:00 之间,一组流行病学家筛选多达 500 个相关来源,以收集…

www.ecdc.europa.eu](https://www.ecdc.europa.eu/en/publications-data/download-todays-data-geographic-distribution-covid-19-cases-worldwide)

下载 CSV 文件格式的数据,其中包含世界上所有国家的新冠肺炎数据。

将数据加载到电子表格

# Read all data
df = pd.read_csv("[https://opendata.ecdc.europa.eu/covid19/casedistribution/csv](https://opendata.ecdc.europa.eu/covid19/casedistribution/csv)").dropna()
print(df.head())

我们可以通过链接直接阅读在线 CSV 文件。这里我觉得 Grid Studio 有一个改进。也就是说,它不像 Jupyter 笔记本那样可以即时打印你的变量。如果你想打印你的变量,你必须使用print方法。

另一个限制是,看起来电子表格不太支持datetime类型。在测试过程中,我发现它不能显示datetime64[ns]类型的熊猫栏。所以,我想把dateRep列转换成整数。

# Convert date to integer (because of Grid Studio limitation)
df.dateRep = pd.to_datetime(df.dateRep, format='%d/%m/%Y').dt.strftime('%Y%m%d').astype(int)

转换数据

首先,让我们按国家过滤数据。例如,我只对澳大利亚的数据感兴趣。

# Get Australia data
df_oz = df[df.countriesAndTerritories == 'Australia']

然后,我们将只选择dateRepcasesdeaths列。

# Retain only date, cases and deaths columns
df_oz = df_oz[['dateRep', 'cases', 'deaths']]

之后,按日期对数据框进行排序,这样我们就可以计算累计病例和死亡人数。

# Calculate cumulative cases & deaths
df_oz = df_oz.sort_values('dateRep')
df_oz['cumCases'] = df_oz.cases.cumsum()
df_oz['cumDeaths'] = df_oz.deaths.cumsum()

将数据呈现到电子表格中

现在,我们的熊猫数据框中应该有 5 列,分别是日期、新病例、新死亡、累计病例和累计死亡。让我们将数据框呈现到电子表格中。

# Show in sheet
sheet("A1", df_oz)

Grid Studio 使这一点变得非常容易。通过调用它的 API sheet,我们简单地指定将呈现数据框的左上角单元格,然后传递数据框变量。

如果您想显示标题,您也可以在sheet方法中指定header=True

电子表格中的更多功能

当数据在电子表格中时,我们可以像使用其他常规软件如 Excel 和 Google Sheets 一样使用它。我就不演示SUMAVG等公式特性了。每个人都很熟悉。

最有用的功能之一是您可以轻松地将电子表格导出为 CSV 格式。这意味着我们可以使用 Pandas data frame 的强大功能轻松下载和转换数据,然后导出以使用其他软件做进一步的分析。

另一个我认为非常有用的方法是通过点击使用matplotlib绘制数据。例如,如果我们想要绘制每天的新案例,只需简单地选择“新案例”列并右键单击它,如下图所示。

然后,在右下角,您可以在“绘图”选项卡中找到该绘图。

事实上,Grid studio 已经通过自动生成代码完成了这个情节。下面是为上面的图表生成的代码。

data = sheet("B1:B106")
data.plot()
show()

因此,如果有必要,我们可以添加一些注释。例如,我们可以给这个图表添加一个标题:

data = sheet("B1:B106")
data.plot(title='Daily New Cases')
show()

类似地,我们可以使用相同的程序分别绘制 4 列。接下来的 3 个图表是通过简单的点击和添加标题生成的,总共花了我 30 秒!

很明显,使用 Grid Studio 来执行一些简单的数据分析会非常快速和方便。感谢作者里克·拉默斯提出了这个惊人的想法。

局限性和未来的改进

照片由桑嘎日玛罗曼塞利亚Unsplash 拍摄

虽然我很喜欢这个结合了电子表格和 Python 的应用程序的想法,但我不得不说它离成熟还很远。在我看来,至少需要解决和实施一些限制和潜在的改进:

  1. 应该支持所有的熊猫数据类型
  2. 应该实现更多的电子表格的功能,大多数类似的应用程序会有,如拖动填充
  3. 电子表格中有一些需要修复的错误,例如,加载的数据似乎停留在内存中,可能无法从电子表格中删除。
  4. 建议:如果我们能把这张纸和熊猫的数据框装订在一起就太好了。也就是说,当您修改图纸时,数据框会更新,反之亦然。
  5. 建议:如果 Python 的 stdout 输出可以转换成带有[1]:行号的 iPython 样式就太好了,这样调试起来会容易很多。

[## 通过我的推荐链接加入 Medium 克里斯托弗·陶

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@qiuyujx/membership)

如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)

基于云的高吞吐量低延迟大数据管道架构

原文:https://towardsdatascience.com/scalable-efficient-big-data-analytics-machine-learning-pipeline-architecture-on-cloud-4d59efc092b5?source=collection_archive---------0-----------------------

来源:图片由 JuraHeep 来自 Pixabay

数据工程

可扩展和高效的数据管道对于分析、数据科学和机器学习的成功非常重要,就像可靠的供应线对于赢得战争一样重要。

更新免费版: 可扩展的高效大数据管道架构

对于在现实世界中部署大数据分析、数据科学和机器学习(ML)应用程序,分析调整和模型训练只占工作的 25%左右。大约 50%的工作用于为分析和 ML 准备数据。剩下的 25%的努力用于使洞察力和模型推论易于大规模使用。大数据管道将所有这些整合在一起。这是一条铁路,曼梯·里的重型货车在这条铁路上行驶。长期的成功取决于正确的数据管道。

本文通过以下四个部分介绍了数据管道并概述了大数据架构的替代方案:

  • 视角:通过了解所有利益相关者的视角,你可以增强工作的影响力。本节解释了各种观点,并研究了数据管道所需的工程特性。
  • 管道:在本节中,您将了解大数据管道经过数据湖数据仓库的概念性阶段
  • 可能性:在这一部分,您将了解用于平衡规模和速度的 lambda 架构,以及用于大数据架构关键组件的技术选择。你还会在 AWSAzureGoogle Cloud 上瞥见无服务器管道。
  • 生产:本节提供了在生产中成功部署大数据管道的技巧。

远景

视角:视角取决于有利位置。

构建数据分析或机器学习应用程序涉及三个利益相关方:数据科学家、工程师和业务经理。

从数据科学的角度来看,目标是 使用可用数据为给定问题找到最健壮和计算最便宜的模型。

工程的角度来看,目的是 建造 东西,让别人可以依赖;通过创造新的东西或者寻找更好的方式来创造现有的东西,这些东西可以 24x7 运行而不需要太多人工干预。

业务角度来看,目的是 向顾客传递 价值;科学和工程是实现这一目标的手段。

在本文中,我们将重点关注工程视角,特别是处理 ML 应用程序中所需的大量数据的方面,同时考虑其他视角。数据管道的理想工程特性是:

  • 可访问性:数据科学家可以方便地访问数据以进行假设评估和模型实验,最好是通过查询语言。
  • 可伸缩性:随着数据量的增加而伸缩,同时保持低成本的能力。
  • 效率:数据和机器学习结果在指定的延迟内准备就绪,以满足业务目标。
  • 监控:关于数据和管道健康状况的自动警报,需要对潜在的业务风险做出主动响应。

管道

管道:运转良好的大数据管道是机器学习成功的必备条件。

数据的价值只有在转化为可操作的洞察力后才能释放出来,并且这种洞察力必须及时交付。

一条数据管道将端到端的操作缝合在一起,包括收集数据、将* it 转化为洞察力、培训模型、交付洞察力、应用模型,无论何时何地都需要采取行动来实现业务目标。*

数据是新的石油。它很有价值,但如果不提炼,就不能真正使用。必须把它变成气体、塑料、化学物质等。创建一个有价值的实体,推动盈利活动;因此,必须对数据进行分解和分析,使其具有价值。
—克莱夫·亨比,英国数学家,乐购俱乐部卡的设计师

数据管道有五个阶段,分为三个部分:

  • 数据工程:捕获、摄取、准备(大约 50%的工作量)
  • 分析/机器学习:计算(大约 25%的工作量)
  • 交付:演示(约 25%的工作量)

抓取:数据源(手机 app、网站、web apps、微服务、物联网设备等。)被装备以捕获相关数据。

接收:测量源将数据泵入各个入口点(HTTP、MQTT、消息队列等)。).也可能有从谷歌分析等服务导入数据的工作。数据可以有两种形式:blobs 和 streams。所有这些数据都被收集到一个数据湖中。

准备:提取、转换、加载(ETL)操作是对数据湖中的数据块和流进行清理、符合、整形、转换和编目;使数据准备好供 ML 使用,并将其存储在数据仓库*中。*

计算:这是分析、数据科学和机器学习发生的地方。计算可以是批处理和流处理的组合。模型和洞察(结构化数据和流)被存储回数据仓库*。*

展示:洞察通过仪表盘、电子邮件、短信、推送通知和微服务交付。ML 模型推理被公开为微服务。

大数据管道中的阶段。

数据湖与数据仓库

数据湖 包含通常以 blobs 或文件形式接收的所有自然/原始形式的数据。 数据仓库 存储清理和转换后的数据以及目录和模式。湖和仓库中的数据可以是各种类型的:结构化(关系)、半结构化、二进制和实时事件流。

湖和仓库是物理地保存在不同的商店中,还是通过湖上的某种接口(例如 Hive 查询)来物化仓库,这是一个选择的问题。这种选择是由速度要求和成本限制决定的。

无论采用哪种方法,保留用于审计、测试和调试目的的原始数据都很重要。

探索性数据分析

探索性数据分析(EDA)的作用是分析和可视化数据集,并制定假设。它可能会暴露收集的数据中的差距,导致新的数据收集和实验,并验证一个假设。

您可以将它们视为小规模的 ML 实验,以瞄准一小组有前途的模型,这些模型在完整的数据集上进行比较和调整。

拥有一个维护良好的数据仓库,包括目录、模式和通过查询语言的可访问性(而不需要编写程序),有助于加快 EDA 的速度。

可能性

可能性:架构是性能和成本的权衡。有六个选项播种在这个形象,使一个三角形帐篷形状。从左上到右下,制作帐篷所需的胶水量逐渐减少。在 prod 中你会选择做哪一个?注意三角形的底边比另一边小,浅蓝色的那块是长方形而不是正方形。

数据管道、数据湖和数据仓库并不是新概念。过去,数据分析是使用批处理程序、SQL 甚至 Excel 表来完成的。现在发生变化的是促进机器学习的大数据的可用性,以及对实时洞察的需求不断增长。

有几种架构选择提供了不同的性能和成本权衡(就像随附图片中的选项一样)。我了解到技术上最好的选择不一定是生产中最合适的解决方案。您必须仔细检查您的需求:

  • 需要实时洞察还是模型更新?
  • 您的应用程序的过时容忍度是多少?
  • 成本限制是什么?

根据这些问题的答案,您必须在 Lambda 架构中平衡批处理和流处理,以满足您的吞吐量和延迟需求。Lambda 架构由三层组成:

  • 批处理层:提供高吞吐量、全面、经济的 map-reduce 批处理,但延迟较高。
  • 速度层:提供低延迟实时流处理,但成本较高,当数据量较大时可能会超出内存限制。
  • 服务层:当准备好时,来自高吞吐量批处理的输出与流处理的输出合并,以预先计算的视图或特别查询的形式提供综合结果。

lambda 架构中的基本假设是源数据模型是仅追加,即摄取的事件被打上时间戳并追加到现有事件中,永远不会被覆盖。

大数据架构:您对云堆栈的选择

下图显示了一个使用开源技术实现大数据管道所有阶段的架构。准备和计算阶段经常被合并以优化计算成本。

使用开源技术的大数据架构

大数据架构和技术选择的关键组件如下:

  • HTTP / MQTT 端点用于接收数据,也用于提供结果。这方面有几种框架和技术。
  • 发布/订阅消息队列,用于接收高容量流数据。卡夫卡是目前事实上的选择。经过实战证明,它可以扩展到高事件摄取率。
  • 用于数据湖(和数据仓库)的低成本大容量数据存储Hadoop HDFS 或云 blob 存储,如 AWS S3
  • 查询和目录基础设施为了将数据湖转换成数据仓库,Apache Hive 是一种流行的查询语言选择。
  • Map-Reduce 批量计算引擎,用于高吞吐量处理,例如 Hadoop Map-Reduce ,Apache Spark
  • 流计算用于延迟敏感处理,例如 Apache Storm ,Apache Flink 。Apache Beam 正在成为编写数据流计算的选择。它可以部署在火花批流道或 Flink 流道上。
  • 数据科学和 ML 的机器学习框架Scikit-LearnTensorFlowPyTorch 是实现机器学习的热门选择。
  • 低延迟数据存储器用于存储结果。根据数据类型和用例,有许多成熟的 SQL 与 NoSQL 数据存储选择。
  • 部署编排选项有 Hadoop YARNKubernetes/kube flow

规模和效率由以下杠杆控制:

  • 吞吐量取决于摄取的可伸缩性(即 REST/MQTT 端点和消息队列)、数据湖存储*容量和 map-reduce 批处理。*
  • 延迟取决于消息队列流计算用于存储计算结果的数据库效率*。*

大数据架构:无服务器

随着无服务器计算的出现,可以通过避免 DevOps 来快速启动。体系结构中的各种组件可以由所选云服务提供商提供的无服务器组件替代。

亚马逊网络服务微软 Azure* 、谷歌云平台(GCP) 上大数据管道的典型无服务器架构如下图所示。每一个都与上一节讨论的大数据架构紧密相关。你可以把这些作为参考,入围适合你需求的技术。*

亚马逊网络服务( AWS )、微软 Azure 、谷歌云平台( GCP )上的大数据管道架构。图片由作者提供,并在Creative Commons BY-NC-ND 4.0 International许可下发布。

生产

在制作中,简单常常胜过聪明。你可能会注意到,制作三角形帐篷的选择并不需要最少的胶水。所需零件的生产方式以及整体操作的简单性对于减少出错的可能性非常重要。

生产可能是不可操作的分析和机器学习的墓地。如果您不投资对管道的健康状况进行 24x7 的监控,每当一些趋势阈值被突破时就会发出警报,那么它可能会在没有人注意到的情况下失效。

请注意,工程和运营支出不是唯一的成本。在决定架构时,也要考虑时间、机会和压力成本。

操作数据管道可能会很棘手。这里有一些我从惨痛的教训中学到的技巧:

  • 在扩展数据科学团队之前,扩展数据工程。 ML 车皮不先铺铁路就跑不了。
  • 在干净的数据仓库中勤奋工作。 ML 只和数据一样好。在定义正在收集的数据的模式、对其进行编目时要有纪律。如果没有这些,不要惊讶有多少数据在存储中永久腐烂,变成仅仅字节
  • 开始简单。从无服务器开始,尽可能少地凑合。只有在投资回报合理的情况下,才转向成熟的渠道或您自己的部署。在计算阶段投资最少的 Bootstrap。通过调度一系列 SQL 查询和云函数来实现计算,甚至可以实现“无计算”。这将使整个管道准备得更快,并让您有充足的时间专注于将数据策略、数据模式和目录准备就绪。
  • 仅在仔细评估后构建。业务目标是什么?你有什么手段来影响业务成果?哪些见解将具有可操作性?收集数据并在此基础上构建 ML。

摘要

关键要点是:

  • 调整分析和机器学习模型只需要 25%的努力。
  • 尽早投资数据管道,因为分析和 ML 只和数据一样好。
  • 确保勘探工作的数据易于访问。
  • 从业务目标出发,寻求可行的见解。

我希望这篇文章对你有用。对于在生产中构建一个健壮的数据管道,你有什么技巧和诀窍?请在评论中分享。

如果您喜欢,请:

**原载于ML4Devs.com

基于谷歌云的 Dask 可扩展机器学习

原文:https://towardsdatascience.com/scalable-machine-learning-with-dask-on-google-cloud-5c72f945e768?source=collection_archive---------27-----------------------

Dask 为您的数据科学工具库增添了一大利器,它为您提供了大规模计算的高级并行性

Paul Pasieczny 在 FreeImages.com 拍摄的照片

于 2021 年 11 月 13 日更新,以反映在谷歌云 上设置 Dask 集群的最新步骤

许多人对 ask 进行了评估,并将其与其他工具进行了比较,包括 Spark、Ray 和 Vaex。它是与 Numpy、Pandas 和 Scikit-Learn 等其他社区项目合作开发的,绝对是扩展机器学习的一个很好的工具。

因此,本文的目的不是比较 Dask 的优缺点(为此,您可以参考本文末尾的参考链接),而是添加到现有的关于 Dask 在云上部署的文档,特别是 Google Cloud。谷歌云为新注册用户提供了免费试用,所以你可以免费试用。

在 Google Cloud 上部署 Dask 的步骤

我们首先列出要采取的一般步骤,然后用截图详细说明每个步骤(随意点击每个步骤进行导航)。拥有一个 Google Cloud 帐户是关注本文的唯一前提。

  1. 创建 Kubernetes 集群
  2. 设置舵
  3. 部署 Dask 进程和 Jupyter
  4. 连接到 Dask 和 Jupyter
  5. 配置环境
  6. 移除你的集群

1.创建 Kubernetes 集群

我们的第一步是通过谷歌 Kubernetes 引擎(GKE)建立一个 Kubernetes 集群。

a)登录谷歌云控制台后,启用 Kubernetes 引擎 API

b)启动谷歌云壳

您应该会在控制台页面的右上角看到一个类似于下面红框中按钮的按钮。点击它,会弹出一个终端。这个终端后面的虚拟机预装了各种工具,最重要的是 kubectl ,这是一个控制 Kubernetes 集群的工具。

点击红框中的按钮,启动谷歌云外壳

c)创建一个受管理的 Kubernetes 集群

在 Google Cloud Shell 中键入以下内容,创建一个托管的 Kubernetes 集群,将 < CLUSTERNAME > 替换为以后可以引用的名称。

gcloud container clusters create \
  --machine-type n1-standard-4 \
  --num-nodes **2** \
  --zone us-central1-a \
  --cluster-version latest \
  <CLUSTERNAME>

上面代码中参数的简要描述:

  • machine-type 指定每个节点的 CPU 和 RAM 的数量。您可以从这个列表中选择其他类型。
  • num-nodes 确定要旋转的节点数量。
  • 区域是指您的集群所在的数据中心区域。你可以选择在离你的用户不太远的地方

当您的集群正在初始化时,您还可以在 Kubernetes 集群页面上看到它正在旋转:

  • 在控制台页面顶部的搜索栏中键入 kubernetes clusters

  • 从下拉列表中选择 Kubernetes 集群。

  • 可以看到指定了 < CLUSTERNAME > 的集群正在旋转。等到绿色勾号出现,集群就准备好了。

或者,您也可以通过运行以下命令来验证您的群集是否已初始化:

kubectl get node

部署集群时,您应该看到状态为就绪

d)向群集提供帐户权限

kubectl create clusterrolebinding cluster-admin-binding \
  --clusterrole=cluster-admin \
  --user=<GOOGLE-EMAIL-ACCOUNT>

替换为您用于登录 Google Cloud 的 GOOGLE 帐户的电子邮件。

2.设置舵

我们将使用 Helm 在 Kubernetes 集群上安装、升级和管理应用程序。

a)通过在 Google Cloud Shell 中运行安装程序脚本来安装 Helm

curl [https://raw.githubusercontent.com/helm/helm/HEAD/scripts/get-helm-3](https://raw.githubusercontent.com/helm/helm/HEAD/scripts/get-helm-3) | bash

b)验证舵安装正确

helm version

确保版本至少为 3.5。

3.部署 Dask 进程和 Jupyter

我们就快成功了……在我们开始运行机器学习代码之前,还有几个步骤。

a)使用 Dask 的 Helm 图表库添加和更新包信息

helm repo add dask [https://helm.dask.org/](https://helm.dask.org/)
helm repo update

b)在 Kubernetes 集群上启动 Dask

helm install my-dask dask/dask --set scheduler.serviceType=LoadBalancer --set jupyter.serviceType=LoadBalancer

默认情况下,这会部署一个 dask 调度器、三个 dask 工作器和一个 Jupyter 服务器。

根据您的使用情况,您可以修改上述代码中的选项:

  • my-dask 用于引用您的 dask 设置,您可以更改为其他名称。
  • — set 会将参数调度器.服务类型调度器.服务类型设置为值负载平衡器。这对于我们使用外部 IP 地址来访问 Dask dashboard 和 Jupyter 服务器是必要的。如果没有此选项,默认情况下将只设置集群 IP,如本堆栈溢出帖子中所述。

4.连接到 Dask 和 Jupyter

在上一步中,我们在集群上启动了 Dask。但是,部署可能需要一分钟的时间,过一会儿您可以用 kubectl 检查状态:

kubectl get services

一旦准备就绪,外部 IPs 将为您的 Jupyter 服务器( my-dask-jupyter )和 dask 调度程序( my-dask-scheduler )显示。如果您在 EXTERNAL-IP 下看到 <挂起> ,请在再次运行上述代码之前多等一会儿。

在您的网络浏览器中输入 my-dask-jupytermy-dask-scheduler 的外部 IP 地址将允许您分别访问您的 jupyter 服务器和 dask 仪表板。

对于 Jupyter 服务器,您可以使用默认密码 dask 登录。要更改此密码,请参见下一节。

恭喜你!您现在可以开始运行您的 Dask 代码:)

单击笔记本下的按钮开始:)

注意:如果您在访问 Jupyter 时遇到 404 错误,只需点击顶部的 Jupyter 徽标即可进入登录页面。

5.配置环境

在步骤 4 之后,您也许能够执行一些基本的 Dask 代码,但是如果您想运行 dask-ml 呢?默认情况下不会安装。如果您想启动三个以上的默认工作人员,该怎么办?改一下你的 Jupyter 服务器密码怎么样?

因此,我们需要一种方法来定制我们的环境,我们可以通过创建一个 yaml 文件来配置它。该 yaml 文件中的值将覆盖标准配置文件中相应参数的默认值。

为了便于说明,我们将使用下面的值。一般来说,配置分为三个主要部分:调度、工人和 Jupyter 各一个。

Dask Helm 部署更新的配置文件模板

要更新配置,只需执行以下操作:

  • 在您的 Google Cloud Shell 中,运行nano values.yaml 来创建文件 values.yaml
  • 复制粘贴上面的模板(随意修改)并保存。
  • 更新您的部署以使用此配置文件:
helm upgrade my-dask dask/dask -f values.yaml
  • 请注意,您可能需要等待一段时间,以便更新准备就绪。

配置概述

我们还在下面提供了模板中常用配置的一般描述。

a)安装库

在 Worker 和 Jupyter 下,你可以找到 env 的子部分。请注意,安装可以通过 conda 或 pip 进行,包之间用空格隔开。

env:  # Environment variables.
  - name: EXTRA_CONDA_PACKAGES
    value: dask-ml shap -c conda-forge
  - name: EXTRA_PIP_PACKAGES
    value: dask-lightgbm --upgrade

b)工人人数

工人数量可以通过副本参数指定。在我们的例子中,我们要求 4 个工人。

worker:
  replicas: 4  # Number of workers.

c)分配的资源

根据您的需要,您可以通过 resources 子部分增加分配给调度程序、工作程序和/或 Jupyter 的内存或 CPU 数量。

resources:
  limits:
    cpu: 1
    memory: 4G
  requests:
    cpu: 1
    memory: 4G

c) Jupyter 密码

Jupyter 密码是密码参数下的哈希值。您可以通过替换此字段来更改您的密码。

jupyter:
  password: 'sha1:aae8550c0a44:9507d45e087d5ee481a5ce9f4f16f37a0867318c'

要生成新密码的哈希值,

  • 首先在你的 Jupyter 发射器中发射一个终端。

  • 在命令行中运行jupyter notebook password并输入您的新密码。哈希后的密码将被写入一个名为jupyter _ notebook _ config . JSON的文件。
  • 查看和复制哈希密码。

  • 替换 values.yaml 中的密码字段。

6.移除集群

要删除您的 Helm 部署,请在 Google Cloud Shell 中执行:

helm del --purge my-dask

请注意,这不会破坏 Kubernetes 集群。为此,您可以从 Kubernetes 集群页面中删除您的集群。

通过上面的指南,我们希望您现在能够在 Google Cloud 上部署 Dask。

感谢阅读,我希望这篇文章是有用的:)也请随时评论您可能有的任何问题或建议。

参考

[## 攀登熊猫:达斯克 vs 雷 vs 摩丁 vs 瓦克斯 vs 急流

Python 和它最受欢迎的数据争论库 Pandas 人气飙升。与竞争对手相比,如…

www.datarevenue.com](https://www.datarevenue.com/en-blog/pandas-vs-dask-vs-vaex-vs-modin-vs-rapids-vs-ray) [## Kubernetes 和 Helm - Dask 2.22.0+11.gb5156d66 .脏文档

使用 Kubernetes 和 Helm 很容易在云资源上启动 Dask 集群和 Jupyter 笔记本服务器。这是…

docs.dask.org](https://docs.dask.org/en/latest/setup/kubernetes-helm.html)

利用气流和 Kubernetes 扩展您的数据管道

原文:https://towardsdatascience.com/scale-your-data-pipelines-with-airflow-and-kubernetes-4d34b0af045?source=collection_archive---------9-----------------------

完美的气流设置

不管你是在运行后台任务、预处理作业还是 ML 管道。编写任务是容易的部分。最难的部分是流程编排——管理任务之间的依赖关系、安排工作流并监控它们的执行是一件非常繁琐的事情。

输入气流。您的新工作流管理平台。

为什么是气流?

几年前,在有效扩展:当 Kubernetes 遇到 Celery 时,我写了我自己使用 Flask、Celery 和 Kubernetes 实现的工作流引擎。我考虑了可用的解决方案——包括气流。由于看不到令人满意的解决方案,我决定实现自己的框架。从那时起,气流已经走过了漫长的道路。以下是我改用气流的原因:

可攀登的

当使用正确的设置时,也就是我们将要看到的设置,气流既可扩展又具有成本效益。

含电池

虽然 UI 并不完美,但它是 Airflow 的核心竞争力之一。在这种情况下,一张图片胜过千言万语-

气流 UI

无论是以操作符的形式还是以执行器的形式,Airflow 都有大量的集成。

以及一个实验性但不可或缺的用于工作流的 REST API,这意味着你可以动态地触发工作流。

经过战斗考验

有这么多公司使用气流,我可以放心知道它会不断改善。

正在使用气流

完美的气流设置

🔥一次性基础设施

使用 helm 和一些预制的命令,我们可以轻松地破坏和重新部署整个基础架构。

🚀经济高效的执行

我们使用 kubernetes 作为任务的引擎。Airflow scheduler 将在新的 pod 上运行每个任务,并在完成后将其删除。允许我们使用最少的资源根据工作负载进行扩展。

🔩解耦编排

使用 Kubernetes 作为任务运行器的另一个巨大优势是——将编排与执行分离。你可以在中了解更多信息,我们都在错误地使用气流,以及如何修复它

🏃动态更新的工作流程

我们使用 Git-Sync 容器。这将允许我们单独使用 git 来更新工作流。无需在每次工作流程变更时重新部署气流。

气流执行选项

CeleryExecutor+KubernetesPodOperator(推荐)

更多 Pods |指定 Docker 映像中的执行代码

编排和执行的➕解耦。
➖为芹菜工人提供额外的豆荚和花卉监测。

kubernetexecutor+whatever operator

更少的窗格| Dag 中定义的代码

➕没有多余的豆荚。
➖弱解耦。我们必须在 Dag 中定义执行代码和依赖关系。

kubernetexecutor+KubernetesPodOperator

➕没有多余的豆荚。
➕将编排和执行解耦。
不支持 —当前导致 pod 启动递归。

让我们设置它

先决条件

brew install kubectl
brew install helm

确保你有:

还建议设置 Kubernetes 仪表板

设置

cookiecutter https://github.com/talperetz/scalable-airflow-template

要填写 cookiecutter 选项,请查看可扩展气流模板 github repo

make deploy

瞧🎉

作为 Docker 图像的任务

我使用 docker 图像,因为我可以将气流从它运行的实际任务中分离出来。我可以在不改变气流配置、代码或部署的情况下改变底层任务。

当构建图像时,我从 python-cli-template 开始,它提供了快速而直观的 cli 体验。

Github 上的 Python CLI 模板

工作流程示例

from datetime import datetime, timedelta

from airflow import DAG
from airflow.contrib.operators.kubernetes_pod_operator import KubernetesPodOperator

default_args = {
    "owner": "airflow",
    "depends_on_past": False,
    "start_date": datetime(2015, 6, 1),
    "email": ["airflow@airflow.com"],
    "email_on_failure": False,
    "email_on_retry": False,
    "retries": 1,
    "retry_delay": timedelta(minutes=5),
}

example_workflow = DAG('kube-operator',
                         default_args=default_args,
                         schedule_interval=timedelta(days=1))
with example_workflow:
    t1 = KubernetesPodOperator(namespace='airflow',
                               image="ubuntu:16.04",
                               cmds=["bash", "-cx"],
                               arguments=["echo", "hello world"],
                               labels={'runner': 'airflow'},
                               name="pod1",
                               task_id='pod1',
                               is_delete_operator_pod=True,
                               hostnetwork=False,
                               )

    t2 = KubernetesPodOperator(namespace='airflow',
                               image="ubuntu:16.04",
                               cmds=["bash", "-cx"],
                               arguments=["echo", "hello world"],
                               labels={'runner': 'airflow'},
                               name="pod2",
                               task_id='pod2',
                               is_delete_operator_pod=True,
                               hostnetwork=False,
                               )

    t3 = KubernetesPodOperator(namespace='airflow',
                               image="ubuntu:16.04",
                               cmds=["bash", "-cx"],
                               arguments=["echo", "hello world"],
                               labels={'runner': 'airflow'},
                               name="pod3",
                               task_id='pod3',
                               is_delete_operator_pod=True,
                               hostnetwork=False,
                               )

    t4 = KubernetesPodOperator(namespace='airflow',
                               image="ubuntu:16.04",
                               cmds=["bash", "-cx"],
                               arguments=["echo", "hello world"],
                               labels={'runner': 'airflow'},
                               name="pod4",
                               task_id='pod4',
                               is_delete_operator_pod=True,
                               hostnetwork=False,
                               )

    t1 >> [t2, t3] >> t4

高级气流

如果你喜欢这篇文章,一定要关注我:

中:https://medium.com/@talperetz24 推特:https://twitter.com/talperetz24 领英:https://www.linkedin.com/in/tal-per/

扩展人工智能:困难的 5 个原因

原文:https://towardsdatascience.com/scaling-ai-5-reasons-why-its-difficult-6ea77b9f7d48?source=collection_archive---------39-----------------------

如何确保您的人工智能解决方案能够处理真实的数据、真实的客户和真实的商业环境。

扩展人工智能解决方案以处理真实数据、业务用户和客户充满了风险和困难。即使是经验丰富的人工智能组织也与生产规模不断增长的人工智能解决方案发生冲突。

这篇文章强调了为生产而扩展人工智能可能存在问题的 5 个领域。如果你想将人工智能引入你的业务,关键是要做好这一点。理解它为什么棘手是实现这一目标的第一步。

图经 iStock 下许可给曾拉赫曼(iStock.com/Dekdoyjaidee)

面向生产的人工智能扩展涉及哪些内容

将人工智能从开发或测试扩展到真正的商业领域并不适合胆小的人。真实数据、业务用户和客户可能比他们的测试对手要求更高,更令人难以招架。即使是很小的错误也会很快升级为实质性的损害。

人工智能系统给传统 IT 带来了额外的风险,主要是所涉及的数据和处理规模的大规模增长。无论是单个组织内部还是整个行业,可供借鉴的经验也较少。

与许多其他人工智能风险和陷阱一样,避免将人工智能用于生产的问题主要是技术工作。但业务和项目管理仍然可以发挥关键作用,确保问题得到充分考虑。

如果在使用 IT 系统时出现严重问题,会有许多高调的提醒:付出代价的可能是业务主管。

缩放难度 1: 技术性能

人工智能算法通常需要密集的计算机处理。这些通常涉及矩阵操作、线性代数和统计分析。它还需要一遍又一遍地重新计算,直到结果收敛到一个有效的结论。

大多数人工智能系统需要至少数十万个数据点才能有意义地执行,通常是数百万或更多。这种组合意味着人工智能系统的计算机处理和存储需求是巨大的。这远远超过了过去大多数企业使用的任何东西。

当建立一个人工智能解决方案时,早期的工作是在小的数据子集上。这需要的计算机资源比后来所需的数量级要小。正是这种处理能力的提升,可能是指数级的,使得性能成为人工智能设计的一个关键领域。

对大规模性能关注不够可能会产生在测试期间看似工作良好,但业务却无法使用的人工智能解决方案。

例如:

  • 一个人工智能系统,可以在测试过程中实时即时标记可能的欺诈交易。如果用实时数据做同样的事情需要几分钟,那将是不可接受的。
  • 不同营销活动的可能绩效报告可能需要几分钟才能在测试中运行。但是,如果需要几天或几周的时间来运行实时数据,它就没那么有用了。
  • 产品推荐对客户有帮助。但是,如果为真实客户生产这些产品的成本大于利润,它们就没有商业价值。

这方面最著名的例子可能是(现已解散的)网飞竞赛。参赛者被要求找到一种更好的方法来确定观众接下来要观看的推荐。获胜的解决方案工作得非常好,但是不能被使用。原因之一是运营计算成本。即使对网飞来说,扩展人工智能的成本也超过了答案的商业价值。

缩放难度二: 数据量&准确性

复杂的技术含义和选择

处理 100 倍或更大的数据量增长的技术问题非常复杂。这些会导致架构和技术的基本问题。例如,一个错误的数据库选择会导致一个有效的测试系统无法大规模使用。

关系数据库已经统治商业 IT 几十年了,并且运行得非常好。但是人工智能使用的大量数据挑战了它们的物理限制。许多人工智能专家推荐其他更好扩展的技术、架构和技巧。

其中一些是尖端的,即使它们的起源可以追溯到几十年前。例子包括 NoSQL (包括图形数据库)时间序列数据库MapReduce 编程甚至无共享架构

这些领域的技能和经验比成熟的技术更少。技术成本和人力成本都更高了。

繁重的数据清理和准备任务

在扩展人工智能时,除了海量数据的技术挑战之外,还有另一个数据问题。这是确保所有数据可靠所需的工作。

清理和准备原始数据所需的步骤在规模上是不同的。例如,人工干预(如人工检查)对于准备测试数据可能是有效的。但是它们需要被明智地用于生产,假设它们甚至是可行的。

“数据管道”和“ETL”(提取、转换&加载)等术语涵盖了这种工作。这些标签之间有语义上的区别。这些条款还可能排除其他也需要扩展人工智能的数据准备步骤。但不管标签如何,如果设计得不好,这个过程会阻止一个完美的人工智能系统工作。

缩放难度 3: 业务流程&人

(组织的)内部变化

随着任何新技术的引入,最大的问题很可能是使用它的人。AI 也不例外。除非一家企业让员工做好使用人工智能解决方案的适当准备,否则它向生产的过渡可能会导致它的灭亡。

这不仅仅是培训用户。这也是关于修正流程、更新政策和提供正确的业务支持。(不仅仅是技术支持)。

面向客户的变革

此外,人工智能工作通常会对客户体验产生影响。这有时是显而易见的,有时是间接的。例子包括产品推荐、聊天机器人、个性化活动和新的客户服务脚本。

影响客户的扩展人工智能解决方案的一部分是确保面向客户的渠道准备好处理客户反应。这可能只是一个暂时的高峰,但这样的事件可能会非常引人注目。一个常见的例子可能是引入人工智能聊天机器人。也许与直觉相反,这可能会导致最初的电话数量增加,特别是如果它没有按计划进行。

许多人工智能工作都是为了补充和增强人类的工作。但是实现商业价值需要人类的工作做出相应的改变。这可能比人工智能工作本身更难。

例如,在网站上添加智能常见问题应该可以节省联络中心员工的时间。但是,从额外的时间中创造商业利益需要思考、准备,也许还要做出艰难的决定。人工智能项目经理不会认为这是他们职权范围的一部分。事实上,它甚至可能不会出现在专注于技术的人工智能项目计划中。

缩放难度 4: 意想不到的行为

对新人工智能解决方案用户的支持包括帮助他们使用该技术。但是一个更棘手的问题是支持业务问题,尤其是那些测试中没有出现的问题。为生产而扩展人工智能应该考虑到没有设计或计划的情况。

最初,这主要是关于预期结果的例外。例如,如果欺诈检测分数应小于或大于 5 分,流程将满足每个分数的要求。但用户需要知道,如果分数以其他形式返回,比如“未知”或“错误”,该怎么办。

然而,随着时间的推移,由于人工智能系统内的变化,可能会出现新的情况。如果人工智能结合了机器学习,并且被设计成随着时间的推移自我改进,这种可能性更大。通常,这是为了提高算法的准确性。但可以更加开放,比如识别客户行为的新模式。

还有人工智能带来意想不到的商业后果的风险——或许是可能性。团队可以在试点项目和“试运行”中清除这些,但并不总是如此。

因此,扩展人工智能的一个重要部分包括处理困难或关键的场景。一个重点应该是确保应急方案在技术上和操作上都是可行的。例如,暂时“关闭”人工智能解决方案是否简单?

根据定义,意想不到的行为很难预测。这并不意味着他们不可能准备,只是更困难。

扩展难度 5: 数据安全&治理

围绕将人工智能扩展到生产的最后一个问题是低估了安全影响。也许这是第一次,大量不同的数据将全部集中在一个地方。这就产生了潜在的新漏洞,并代表了一种需要降低的新型业务风险。这类问题可能会被宣传放大,而且安全问题可能会对品牌产生重大影响。

数据被盗或直接被利用的风险显而易见。这些都要求常规的网络和 IT 安全性很高。但它不仅仅适用于 AI 计算机和数据库。通信基础设施和云设施也存在新的潜在漏洞。

安全和隐私考虑也需要考虑人工智能解决方案的构建和处理方式。敏感数据的副本或摘录存在于不如真实环境安全的地方。传统的 IT 测试和生产环境可能不适合 AI 数据验证和实验活动。

人工智能也创造了一个新的不同的安全风险,因为它的“智能”。从不完整和不同的数据中推断意义,或者智能地填补数据中的空白的能力带来了新的挑战。例如,以前“安全”的数据——可能是匿名的或不完整的——如果与其他数据混在一起,可能会变得敏感。更糟糕的是,这些“其他数据”可能位于人工智能系统之外,甚至是业务之外。这意味着在规划安全措施时需要考虑所有数据,而不仅仅是敏感数据。

企业接受严格的控制、政策和系统是保护财务资源和资产所必需的。许多人现在将数据视为也需要强有力保护和监督的资产。人工智能系统将这带到了另一个层面。

当扩展人工智能时,数据保护、安全和治理不再是获得监管信息。

攀登人工智能:外卖

构建有效的人工智能系统和解决方案是困难的。要克服的复杂性涉及技术、数据和业务。将人工智能扩展到生产环境会进一步限制这种复杂性,并增加额外的挑战。

但商业中的人工智能需要产生业务改进,如果没有实现它们,它可能是不完整的。因此,围绕扩展人工智能的问题在某种程度上与选择正确的算法一样重要。

在你的组织中评估、规划和预算人工智能时,请记住这一点。本文中描述的这些因素应该包含在你的思考中,成为你风险意识的一部分。即使您还没有准备好解决它们,它们也应该作为占位符存在,以便以后详细考虑。

本文最初发表于【www.aiprescience.com】

使用 Apache Airflow 扩展 DAG 创建

原文:https://towardsdatascience.com/scaling-dag-creation-with-apache-airflow-a7b34ba486ac?source=collection_archive---------19-----------------------

活动发起人Unsplash 上的照片

数据科学社区中更困难的任务之一不是设计一个结构良好的业务问题的模型,或者开发在可伸缩环境中运行的代码库,而是在 ETL 或数据科学管道中安排任务,定期执行模型,并自动化中间的一切。

这就是阿帕奇气流前来救援的地方!借助以图形形式显示任务的 Airflow UI,以及以编程方式定义工作流以提高可追溯性的能力,在生产中定义和配置 Data Science 工作流变得更加容易。

然而,还有一个困难。在某些情况下,相同的建模、整体流程会被用于不同的数据源。为了提高性能,最好让这些进程同时运行,而不是将它们添加到同一个 dag 中。

没问题,让我们简单地为每个进程创建一个 dag,所有进程都有相似的任务,并安排它们同时运行。如果我们遵循 DRY 软件开发原则,有没有一种方法可以用相同类型的任务创建多个不同的 Dag,而不必手动创建它们?

有没有一种方法可以创建具有相同类型任务的多个不同 Dag,而不必手动创建它们?

最初的方法

首先,让我们假设我们的初始 dag 如下所示:

from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from time import sleep

from airflow import DAG
from airflow.operators.python_operator import PythonOperator# the default arguments for the DAG
default_args = {
    "depends_on_past": False,
    "start_date": datetime.now() - relativedelta(days=1),
    "retries": 1,
    "retry_delay": timedelta(seconds=120),
    "email_on_failure": False
}

dag = DAG("dummy", default_args=default_args, schedule_interval=None)# python function we are to use that only sleeps for 60 seconds.
def tmp():
    sleep(60)

    print("Dag passed!")
    return None

t1 = PythonOperator(python_callable=tmp, task_id="time", dag=dag)

这个 dag 的目的是简单地休眠,然后打印一个语句。这是一个非常简单的 dag,但是让我们假设这将代表我们的数据科学工作流。如果这是我们部署到生产中的唯一数据科学工作流,我们很可能会感到满意。

但是,如果像前面提到的那样,我们想要创建多个具有相同类型任务的 Dag,而不必手动创建它们,该怎么办呢?例如,以我们上面的初始 dag 为例,如果我们想要配置在打印语句之前我们想要休眠的秒数会怎么样呢?这就是我们需要理解流程的哪些部分是可配置的,然后允许这些部分成为流程中的变量。让我们再次尝试我们的初始 dag:

from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from time import sleep

from airflow import DAG
from airflow.operators.python_operator import PythonOperator

default_args = {
    "depends_on_past": False,
    "start_date": datetime.now() - relativedelta(days=1),
    "retries": 1,
    "retry_delay": timedelta(seconds=120),
    "email_on_failure": False
}# this function will create a dag, define the python callable and add it to the PythonOperator
def create_dag(dag_id, seconds=60):
    dag = DAG(dag_id, default_args=default_args, schedule_interval=None)

    def tmp():
        sleep(seconds)

        print("Dag passed!")
        return None

    t1 = PythonOperator(python_callable=tmp, task_id="time", dag=dag)

    return dag# here we create a global variable called dummy dag, which will store the DAG object here.
globals()["dummy_dag"] = create_dag("dummy", 60)

当一个变量被添加到 python 脚本中的全局字典时,它被渲染并被视为在全局范围内创建的变量,即使它最初是在函数范围内创建的。现在,您可以轻松创建多个任务相似的 Dag。

现在,如果某些 Dag 的任务分配只有微小的变化,会怎么样呢?

一般来说,大多数人会简单地用相似的 DAG 创建过程创建多个 python 脚本。

但是,有更好的办法。

特定 Dag 的可配置任务分配

考虑这个例子,我们最初在一个名为 task_assigner.py 的 python 模块中定义它

from functools import reduce

from airflow.operators import *

# this will be just a small subset of operators that we will define
task_dict = {
    "external_task": ExternalTaskSensor,
    'python_operator': PythonOperator,
    'trigger_dag': TriggerDagOperator,
    "bash": BashOperator
}

class TaskAssigner:

    def __init__(self, parent_dag):
        self.parent_dag = parent_dag

    def define_tasks(self, task_params):
    """
    This will use the task_dict to help map the task define to the appropriate task with the correct parameters

    :param tsk_name: The unique name of the task
    :type: str
    :param task_params: The parameters for all the task
    :type: dict
    """

        defined_tsk_dict = {}

        for tsk in task_params.keys():
            dag_type = task_params[tsk]["type"]
            params = task_params[tsk]["params"]
            params["dag"] = self.parent_dag
            params["task_id"] = tsk

            defined_tsk_dict[tsk] = task_dict[dag_type](**params)

        return defined_tsk_dict

    def map_task(self, tsk_names, defined_task_dict):
    """
    This will help map the sequence of the tasks to the appropriate defined task

    :param tsk_names: The sequence of the tasks to run
    :type tsk_names: str
    :param defined_task_dict: The dictionary of defined tasks
    :type defined_task_dict: dict
    """

        res_lst = []

        for tsks in tsk_names:
            if isinstance(tsks, list):
                res_lst.append(self.map_task(tsks, defined_task_dict))
            elif isinstance(tsks, str):
                res_lst.append(defined_task_dict[tsks])
            else:
                raise ValueError

        return res_lst    

    def run(self, task_sequence, task_params):
    """
    This will assign each task in the sequence, with the right parameters, to the parent dag

    :param task_sequence: This will be a list of, or a list of list, of strings
    :type task_sequence: List[List[str]] or List[List[List[str]]], or something in between.
    :param task_params: The parameters for all the task
    :type task_params: dict
    """

        defined_task_dict = self.define_tasks(task_params)

        # this is a List[List[Tasks]] or a List[List[List[Tasks]]]
        run_lst = self.map_task(task_sequence, defined_task_dict)

        def tmf(tsks):
            reduce(lambda tsk1, tsk2: tsk1 >> tsk2, tsks[1:], tsks[0])

        list(map(tmp, run_lst))

        return None

然后在另一个脚本中使用 TaskAssigner,比如说在 dag.py 中

import json

from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from time import sleep

from airflow import DAG
from airflow.operators.python_operator import PythonOperator

from task_assigner import TaskAssigner

default_args = {
    "depends_on_past": False,
    "start_date": datetime.now() - relativedelta(days=1),
    "retries": 1,
    "retry_delay": timedelta(seconds=120),
    "email_on_failure": False
}# now what we do is create a dag, pass it into the TaskAssigner
# with the dag sequence and the params for each task in the dag.def create_dag(dag_id, dag_sequence, dag_params):
    dag = DAG(dag_id, default_args=default_args, schedule_interval=None)

    tsk_assign = TaskAssigner(dag)

    tsk_assign.run(dag_sequence, dag_params)

    return dag# we read in our configuration file with our parameters in the dag
with open("dag_configs.json") as f:
    config = json.load(f)# we loop through each dag id in the JSON file, and then create our dag and add it as a global variable here.for dg_id in config.keys():
    globals()[dg_id] = create_dag(dg_id, config[dg_id]["dag_sequence"], config[dg_id]["dag_params"])

这里使用的 dag_configs.json 是:

{
  "dummy": {
    "dag_sequence": [["dummy"]],
    "dag_params": {
      "dummy": {
        "type": "bash",
        "params": {"bash_command": "sleep 60"}
      }
    }
  }
}

把所有的放在一起

现在,我们讨论了如何利用 OOP 定义多个 Dag,这是 DRY 兼容的。但是,这是假设每个 dag 都是相互独立的,实际情况通常不是这样。有些情况下,每个 dag 都可能依赖于另一个 dag。

你认为解决办法是什么?

如果您有任何问题,请随时联系我们。

此外,我正在创建的内容将只在 blog.pyscale.net 完全可用。请访问该网站获取更多内容,尤其是如果您想了解更多关于面向初学者的数据科学工具的信息!

一如既往,#happycoding!

通过多 GPU/TPU 训练扩展逻辑回归

原文:https://towardsdatascience.com/scaling-logistic-regression-for-multi-gpu-tpu-training-b4898d5049ff?source=collection_archive---------41-----------------------

了解如何使用带有 PyTorch 闪电的 GPU 和 TPU 将逻辑回归扩展到大规模数据集。

这种逻辑回归实现旨在利用大型计算集群(来源)

逻辑回归是一种简单但强大的分类算法。在这篇博文中,我们将看到我们可以 将逻辑回归视为一种神经网络

将其框架化为神经网络允许我们使用 PyTorch 和 PyTorch Lightning 这样的库在硬件加速器上进行训练(比如 GPU/TPU)。这使得分布式实现可以扩展到大规模数据集。

在这篇博文中,我将通过将 NumPy 实现连接到 PyTorch 来说明这个链接。

我已经将这个高度可扩展的逻辑回归实现添加到了 PyTorch Lightning Bolts 库中,您可以轻松地使用它在自己的数据集上进行训练。

来源: PyTorch 闪电

跟着走

如果您想自己运行每个步骤中的代码,我们将浏览的所有代码示例都可以在这个配套的 Colab 笔记本中找到。

为什么我需要可伸缩的逻辑回归?

使用 8 个 GPU 拟合逻辑回归的示例。

就像其他 ML 库一样(例如 Sci-kit Learn ),这个实现将允许你在几行代码中建立一个逻辑回归模型。与其他库不同,您可以在许多机器上的多个 GPU、TPU 或 CPU 上训练大量数据集。

除了具有十几个要素的玩具数据集之外,真实数据集可能具有数万个要素和数百万个样本。这种规模,CPU 根本不行。相反,我们可以利用 GPU 和 TPU 将几天的训练变成几分钟。

例如,在本教程结束时,我们在几秒钟内在 1 个 GPU 上对包含 70,000 幅图像和 784 个特征的完整 MNIST 数据集进行训练。事实上,我们甚至尝试过 ImageNet。我们的逻辑回归实现可以在大约 30 分钟内在 2 个 GPU(v100)上循环遍历所有 130 万个图像,每个图像具有 150,528 个像素(输入特征)。

作为基线的逻辑回归

不管您的分类问题有多复杂,使用逻辑回归分类器设置一个强基线总是一个好的实践。即使您打算使用更复杂的方法,如神经网络。

从 PyTorch 中实现的逻辑回归基线开始的优点是,它使得用神经网络替换逻辑回归模型变得容易。

逻辑回归概述

如果你熟悉逻辑回归,可以跳过这一节。

我们使用逻辑回归来预测一个离散的类别标签(如猫对狗),这也称为分类。这与回归不同,回归的目标是预测一个连续的实值量(如股票价格)。

在最简单的逻辑回归案例中,我们只有 2 个类别,这被称为二元分类

二元分类

我们在逻辑回归中的目标是从输入值或特征矩阵X中预测二元目标变量Y(即 0 或 1)。例如,假设我们有一群宠物,我们想根据耳朵形状、体重、尾巴长度等特征找出哪只是猫或狗(Y)。(X)。设 0 代表猫,1 代表狗。我们有n样品或宠物,以及关于每只宠物的m特征:

我们要预测宠物是狗的概率。为此,我们首先对输入变量进行加权求和——让w表示权重矩阵。特征X和权重w的线性组合由向量z给出。

接下来,我们将 sigmoid 函数应用于z向量的每个元素,从而得到向量y_hat

S 形函数,也称为逻辑函数,是一个 S 形函数,将z的值“挤压”到范围[0,1]内。

Sigmoid 函数(作者自己的图像)

由于y_hat中的每个值现在都在 0 和 1 之间,我们将此解释为给定样本属于“1”类(与“0”类相对)的概率。在这种情况下,我们将把y_hat解释为宠物是狗的概率。

我们的目标是找到w参数的最佳选择。我们希望找到一个w,使得当x属于“1”类时P(y=1|x)的概率大,当x属于“0”类时P(y=1|x)的概率小(在这种情况下P(y=0|x) = 1 — P(y=1|x)大)。

请注意,每个型号都是通过选择w完全指定的。我们可以使用二元交叉熵损失或对数损失函数来评估特定模型的表现。我们希望了解我们的模型预测与训练集中的真实值有多“远”。

二元交叉熵损失

注意,对于每个训练示例,求和中的两项中只有一项是非零的(取决于真实标签y是 0 还是 1)。在我们的例子中,当我们有一只狗(即y = 1 ) 时,最小化的损失意味着我们需要使y_hat = P(y=1|x)变大。如果我们有一只猫(即y = 0),我们想把1 — y_hat = P(y=0|x)做大。

现在我们有了损失函数来衡量给定的w与我们的训练数据有多吻合。我们可以通过最小化L(w)来学习分类我们的训练数据,以找到w的最佳选择。

我们可以搜索最佳w的一种方式是通过迭代优化算法,如梯度下降。为了使用梯度下降算法,我们需要能够针对w的任何值计算损失函数相对于w的导数。

注意,由于 sigmoid 函数是可微的,损失函数相对于w是可微的。这允许我们使用梯度下降,也允许我们使用自动微分包,如 PyTorch,来训练我们的逻辑回归分类器!

多类分类

二元对多类分类(作者自己的图像)

我们可以将上面的内容推广到多类设置,其中标签y可以有K个不同的值,而不是只有两个。注意,我们从 0 开始索引。

现在的目标是估计类别标签采用每个K不同可能值的概率,即每个k = 0, …, K-1P(y=k|x)。因此,预测函数将输出一个 K 维向量,其元素总和为 1,以给出 K 个估计概率。

具体来说,假设类现在采用以下形式:

这也被称为多项式逻辑回归softmax 回归

关于维度的注释——上面我们只看了一个例子,x是一个m x 1向量,y是一个在0K-1之间的整数值,让w(k)表示一个m x 1向量,它代表第 k 个类的特征权重。

输出向量的每个元素采用以下形式:

这被称为 softmax 功能。softmax 将任意的真实值转化为概率。softmax 函数的输出始终在[0,1]范围内,分母中的求和意味着所有项的总和为 1。因此,它们形成一个概率分布。它可以被视为 sigmoid 函数的推广,在二进制情况下,softmax 函数实际上简化为 sigmoid 函数(试着自己证明这一点!)

为了方便起见,我们指定矩阵W来表示模型的所有参数。我们将所有的w(k)向量连接成列,使得矩阵W具有维度m x k

与二进制情况一样,我们训练的目标是学习使交叉熵损失函数最小化的W值(二进制公式的扩展)。

现在来看神经网络

我们可以认为逻辑回归是一个完全连接的单层神经网络,后面跟着 softmax 函数。

逻辑回归可以看作是一个单层神经网络(作者自己的图像)。

输入层为每个特征包含一个神经元(可能为一个偏差项包含一个神经元)。在二进制的情况下,输出层包含 1 个神经元。在多类情况下,输出层包含每个类的一个神经元。

NumPy 与 PyTorch 代码

事实上,传统的逻辑回归和神经网络公式是等价的。这在代码中最容易看到——我们可以证明原始公式的 NumPy 实现等价于在 PyTorch 中指定一个神经网络。为了说明这一点,我们将使用 Iris 数据集中的例子。

虹膜数据集是一个非常简单的数据集,用于演示多类分类。有 3 个类别(编码为 0,1,2)代表鸢尾花的类型(setosa,versicolor 和 virginica)。植物萼片和花瓣的长度和宽度有 4 个实值特征。

虹膜数据集(作者自己的)

下面是我们如何使用 sci-kit learn datasets 库中的工具加载和混洗 Iris 数据集:

让我们检查数据集维度:

有 150 个样本——事实上我们知道每个类有 50 个样本。现在,让我们挑选两个具体的例子:

上面我们看到,x有维度2 x 4,因为我们有两个例子,每个例子有 4 个特征。y2 x 1值,因为我们有两个例子,每个例子都有一个标签,可以是 0,1,2,其中每个数字代表类的名称(versicolor,setosa,virginica)。

现在我们将创建我们想要学习的权重参数w。因为 3 个类中的每一个都有 4 个特征,所以w的尺寸必须是4 x 3

注意:为了简单起见,我们只看特性,这里不包括偏见术语。现在我们有了,

在 NumPy 中学习 w:

根据逻辑回归公式,我们先计算z = xwz的形状是 2 x 3,因为我们有两个样本和三个可能的类。这些原始分数需要被标准化为概率。我们通过对z的每一行应用 softmax 函数来实现这一点。

因此,最终输出y_hat具有与z相同的形状,但是现在每行总和为 1,并且该行的每个元素给出了该列索引作为预测类的概率。

在上面的示例中,y_hat的第 1 行中的最大数字是 0.6,这给出了作为预测类的第二个类标签(1)。对于第二行,预测的类别也是 1,但概率为 0.5。事实上,在第二行中,标签 0 也有 0.41 的高概率。

在 PyTorch:

现在让我们用 PyTorch 实现完全相同的东西。

首先,我们需要将 NumPy 数组转换成 PyTorch 张量。

然后定义神经网络的线性层和 softmax 激活函数。

PyTorch 自动初始化随机权重,所以我用上面使用的相同权重矩阵显式替换权重。

现在我们计算这个一层神经网络的输出,看到它和上面完全一样。

当训练神经网络学习最优W时,上述步骤被称为正向传递(计算输出和损失)。接着是向后传球。在反向传递过程中,我们使用反向传播算法(本质上是链式法则)(link)计算每个权重参数的损失梯度。最后,我们使用梯度下降算法来更新每个权重的值。这构成了一次迭代。我们重复这些步骤,迭代地更新权重,直到模型收敛或者应用了一些停止标准。

如您所见,无论您使用 NumPy 还是 PyTorch 之类的神经网络库,输出都是相同的。然而,PyTorch 使用张量数据结构而不是 NumPy 数组,后者针对硬件加速器(如 GPU 和 TPU)上的快速性能进行了优化。因此,这允许逻辑回归的可扩展实现。

使用 PyTorch 的第二个优点是,我们可以使用自动微分来有效地自动计算这些梯度。

使用 PyTorch 闪电

虽然像我刚才做的那样实现一个简单的逻辑回归示例很简单,但是在许多机器上正确地在多个 GPU/TPU 上分发数据批次和训练变得非常困难。

然而,通过使用 PyTorch Lightning,我实现了一个处理所有这些细节的版本,并将其发布在 PyTorch Lightning Bolts 库中。这种实现使得在任何数据集上定制和训练该模型变得很简单。

1.首先,安装螺栓:

pip install pytorch-lightning-bolts

2.导入模型并实例化它:

我们指定输入要素的数量、类的数量以及是否包含偏差项(默认情况下设置为 true)。对于虹膜数据集,我们将指定in_features=4num_classes=3。您还可以指定学习率、L1 和/或 L2 正则化。

3.加载数据,可以是任何 NumPy 数组。

让我们继续以虹膜数据集为例:

上面你看到的是你如何使用一个叫做数据集数据加载器的东西在 PyTorch 中加载数据。数据集只是 PyTorch 张量格式的示例和标签的集合。

数据加载器有助于高效地迭代数据集的批次(或子集)。数据加载器指定了诸如批量大小、无序播放和数据转换等内容。

如果我们在 DataLoader 中迭代一个批处理,我们会看到xy都包含 10 个样本,因为我们指定了 10 个批处理大小。

数据模块:

然而,在大多数方法中,我们还需要对数据进行训练、验证和测试。有了 PyTorch Lightning,我们有了一个极其方便的类叫做 DataModule 来为我们自动计算这些。

我们使用SklearnDataModule——输入任何 NumPy 数据集,定制您希望如何分割数据集,它将返回数据加载器供您输入到您的模型中。

一个数据模块只不过是一个训练数据加载器、验证数据加载器和测试数据加载器的集合。除此之外,它还允许您完全指定和组合所有数据准备步骤(如拆分或数据转换)以实现重现性。

我已经指定将数据分成 80%的训练、10%的验证、10%的测试,但是如果您想要使用自己的自定义拆分,也可以传入您自己的验证和测试数据集(作为 NumPy 数组)。

假设您有一个包含 20 个样本的自定义测试集,并希望使用 10%的训练集进行验证:

分割数据是很好的做法,但完全是可选的——如果不想使用验证或测试集,只需将val_splittest_split中的一个或两个设置为 0。

4.训练模型

现在我们有了数据,让我们在 1 个 GPU 上训练我们的逻辑回归模型。当我呼叫trainer.fit(第 6 行)时,训练将开始。我们可以调用trainer.test来查看模型在我们的测试集上的性能:

我们最终的测试集准确率是 100%——这是我们使用 Iris 这样完全可分离的玩具数据集所能享受到的。

由于我是在一台配有 1 个 GPU 的 Colab 笔记本上进行这方面的训练,所以我将gpus=1指定为Trainer的一个参数——然而,在任何类型的多个硬件加速器上进行训练都非常简单:

PyTorch Lightning 在幕后处理分布式培训的所有细节,以便您可以专注于模型代码。

基于完整 MNIST 数据集的训练

在大型数据集上运行时,GPU 和 TPU 上的培训非常有用。对于像 Iris 这样的小数据集,硬件加速器并没有太大的作用。

例如,手写数字的原始 MNIST 数据集包含 70,000 张 2828 像素的图像。这意味着输入特征空间的维数为 784,输出空间中有 K = 10 个不同的类。事实上, Sci-kit Learn 中的实现并不使用原始数据集,而是使用仅 88 像素的降采样图像。

MNIST 数据集(来源)

给定 785 个特征(偏差为 28*28 + 1)和 10 个类,这意味着我们的模型有 7850 个我们正在学习的权重(!).

Bolts 方便地提供了一个[MNISTDataModule](https://pytorch-lightning-bolts.readthedocs.io/en/stable/api/pl_bolts.datamodules.mnist_datamodule.html#pl_bolts.datamodules.mnist_datamodule.MNISTDataModule)来下载、分割和应用标准转换,比如对 MNIST 数字图像进行图像归一化。

我们再次使用 1 个 GPU 进行训练,并看到最终的测试集准确率(对 10,000 个看不见的图像)为 92%。相当令人印象深刻!

规模更大

鉴于 PyTorch 的效率和 PyTorch Lightning 的便利,我们甚至可以扩展这个逻辑回归模型,在包含数百万样本的大规模数据集上进行训练,如 ImageNet。

如果您有兴趣了解更多关于闪电/闪电的信息,我建议您查看以下资源:

如何开始

希望这份指南能准确地告诉你如何开始。最简单的开始方式是用我们已经看过的所有代码例子运行 Colab 笔记本

你可以自己试试,只要装上螺栓,随便玩玩!它包括不同的数据集,模型,损失和回调,你可以混合和匹配,子类化,并在你自己的数据上运行。

pip install pytorch-lightning-bolts

或者查看 PyTorch 闪电

来源: PL 螺栓 Github

扩展机器学习

原文:https://towardsdatascience.com/scaling-machine-learning-fc5e0a36f220?source=collection_archive---------21-----------------------

活动讲座

Razvan Peteanu | TMLS2019

来自多伦多机器学习峰会的演讲:【https://torontomachinelearning.com/

关于演讲者:

Razvan Peteanu 目前的职位是 TD Securities 的首席架构师-机器学习。他有 25 年的软件开发经验,主要是在金融行业。在过去几年中,他的重点一直是在云中或内部构建可扩展的机器学习解决方案。

关于演讲:

一些在数据科学中非常普遍教授和使用的库并不是为大规模机器学习而设计的,因此扩大计算可能是一个挑战,特别是许多课程倾向于专注于算法,而不涵盖 ML 工程。从积极的一面来看,现在有很多方法可以解决这个问题,为一个给定的项目选择正确的方法是一个重要的决定,因为改变架构可能是昂贵的。该演讲将讨论几种扩大机器学习的方法的利弊,包括最近的发展。

YouTube

从零到英雄的机器学习

原文:https://towardsdatascience.com/scaling-machine-learning-from-zero-to-hero-d63796442526?source=collection_archive---------42-----------------------

通过使用 AWS Lambda、无服务器框架和 PyTorch 来扩展您的机器学习模型。

凯尔·格伦在 Unsplash 上的照片

原载于 2020 年 5 月 8 日https://www . philschmid . de

介绍

构建机器学习模型的工作流程往往在评估阶段就结束了:你已经达到了一个可以接受的准确度,你可以在你的“研究环境”和“哒哒!任务完成。”但这还不是全部!机器学习工作流程中最后也是最重要的一步是部署你的模型用于生产。

在生产中不工作的模型是没有价值的

部署的模型可以定义为无缝集成到生产环境中的任何单元,它可以接收输入并返回输出。但公司面临的机器学习的主要问题之一是找到一种方法在这样的环境中部署这些模型。

据报道,大约 40%的失败项目停滞在开发阶段,没有投入生产 来源

在这篇文章中,我将一步步向您展示如何使用 AWS Lambda 部署您自己定制的 Pytorch 模型,并使用 API 将其集成到您的生产环境中。我们将大规模利用简化的无服务器计算方法。

什么是 AWS Lambda?

AWS Lambda 特性

AWS Lambda 是一种计算服务,让你无需管理服务器就能运行代码。它只在需要的时候执行你的代码,并且自动伸缩,从每天几个请求到每秒几千个请求。您只需为您消耗的计算时间付费,当您的代码不运行时,则不收费。

要求

本文假设您已经安装并配置了用于部署 AWS Lambda 功能的无服务器框架,以及一个工作 Docker 环境。无服务器框架帮助我们开发和部署 AWS Lambda 功能。它是一个 CLI,开箱即可提供结构、自动化和最佳实践。它还允许您专注于构建复杂的、事件驱动的、无服务器的架构,由函数和事件组成。

无服务器框架徽标

如果你不熟悉或者还没有设置无服务器框架,看看这个无服务器框架快速入门

通过修改无服务器的 YAML 文件,你可以连接 SQS,比如说,创建一个深度学习管道,或者甚至通过 AWS Lex 将其连接到聊天机器人。

辅导的

在我们开始之前,我想给你一些关于我们将要使用的模型的信息。我在 google colab 中训练了一个 Pytorch 图像分类器。如果你想知道 Google Colab 是什么,看看这里的。我为分类汽车损坏检测创建了一个数据集,并微调了一个 resnet50 图像分类器。在本教程中,我们将使用Python3.8Pytorch1.5

数据集中的示例图像

我们要做什么:

  • 使用无服务器框架创建 Python Lambda 函数
  • 将 Pytorch 添加到 Lambda 环境
  • 写一个预测函数来分类图像
  • 创建一个 S3 存储桶,用于保存模型和上传模型的脚本
  • 配置无服务器框架以设置 API 网关进行推理

我们正在构建的架构将会是这样的。

项目的架构

现在让我们开始学习教程。

创建 AWS Lambda 函数

首先,我们通过使用带有aws-python3模板的无服务器 CLI 创建 AWS Lambda 函数。

这个 CLI 命令将创建一个包含handler.py.gitignoreserverless.yaml文件的新目录。handler.py包含一些基本的样板代码。

添加 Python 需求

接下来,我们将 Python 需求添加到 AWS Lambda 函数中。为此,我们使用无服务器插件serverless-python-requirements。它自动捆绑来自requirements.txt的依赖项,并使它们可用。serverless-python-requirements插件允许你捆绑非纯 Python 模块。更多关于的信息,请点击这里

安装插件

要安装插件,请运行以下命令。

这将自动把插件添加到你的项目的package.jsonserverless.yml中的插件部分。

requirements.txt添加要求

我们必须在根级别创建一个requirements.txt文件,包含所有需要的 Python 包。但是您必须小心,解压缩后的部署包大小不能超过 250MB。你可以在这里找到所有 AWS Lambda 限制的列表。

如果我们用pip install torch安装 Pytorch,这个包大约有 470 MB,这对于部署在 AWS Lambda 环境中来说太大了。因此,我们在requirements.txt中直接添加到 python wheel 文件(.whl)的链接。所有 PyTorch 和 torchvision 包的列表请参考此列表。

requirements.txt应该是这样的。

为了使依赖性更小,我们将在serverless-python-requirements插件中使用三种技术:

  • zip -将requirements.txt中的依赖项压缩到一个附加的.requirements.zip文件中,并将unzip_requirements.py添加到最终的包中。
  • slim -删除不需要的文件和目录,如*.so*.pycdist-info等。
  • noDeploy -从部署中省略某些包。我们将使用标准列表,排除那些已经内置到 Lambda 中的包,以及 Tensorboard。

您可以在“配置我们的serverless.yaml文件”部分看到它的实现。

预测功能

我们的 Lambda 函数实际上由 4 个函数组成。

  • load_model_from_s3()用于将我们的模型从 S3 加载到内存中,创建我们的 PyTorch 模型和一个名为classes的列表,它包含可预测的类。
  • transform_image()用于将输入图片转换成 PyTorch 张量。
  • get_prediction(),使用变换后的图像作为输入来获得预测。
  • detect_damage()是我们 Lambda 环境的主要功能。

伪代码

工作程序代码看起来像这样。

将训练好的模型添加到我们的项目

如前所述,我在 colab 笔记本中训练了一个汽车损坏检测模型,它以一幅图像作为输入,并返回所描绘的汽车是01-whole还是00-damaged。我还添加了一些代码,为您完成所有的捆绑魔术:如果您运行笔记本,它将创建一个名为cardamage.tar.gz的文件,准备部署在 AWS 上。记住,Lambda 函数解压缩后的大小只能是 250MB。因此,我们不能将我们的模型直接包含到函数中。相反,我们需要用load_model_from_s3()从 S3 下载它。

为此,我们需要一个 S3 桶。您可以使用管理控制台或此脚本创建一个。

在我们创建桶之后,我们可以上传我们的模型。您可以手动或使用提供的 python 脚本来完成。

配置serverless.yaml

下一步是调整serverless.yaml并包括custom Python 需求配置。我们将编辑serverless.yaml的四个部分,...

  • 保存我们的运行时和 IAM 权限的provider部分。
  • 我们配置serverless-python-requirements插件的custom部分。
  • package部分,我们将文件夹排除在生产之外。
  • function部分,我们创建函数并定义调用 Lambda 函数的事件。

看一下完整的serverless.yaml。不要担心,我将在一分钟内详细解释所有四个部分。

供应者

在无服务器框架中,我们在provider部分定义我们的功能部署在哪里。我们使用aws作为我们的提供商,其他选项包括googleazure等等。您可以在此找到完整的提供商列表

此外,我们定义我们的运行时、环境变量和 Lambda 函数拥有的权限。

作为运行时,我们使用python3.8。为了让我们的函数工作,我们需要两个环境变量S3_BUCKETMODEL_PATHS3_BUCKET包含我们之前创建的 S3 存储桶的名称。MODEL_PATH是我们的cardamage.tar.gz文件在 S3 桶中的路径。我们仍然缺少将模型从 S3 导入 lambda 函数的权限。iamRoleStatements处理我们的 lambda 函数的权限。我们从 S3 获取模型所需的许可是s3:getObject,将我们的 S3 桶的 ARN ( 亚马逊资源名)作为资源。

习俗

serverless.ymlcustom部分,我们可以定义插件或其他脚本的配置。更多详情,请参考本指南。如前所述,我们使用serverless-python-requirements来同时安装和减少依赖项的大小,这样我们就可以将所有东西打包到 Lambda 运行时中。如果你想知道它是如何工作的,你可以在这里阅读。

包裹

package部分可用于从最终包中排除目录/文件夹。这为包装过程提供了更多的控制。你可以exclude特定的文件夹和文件,比如node_modules/。更多细节请看这里。

功能

第四个也是最后一个部分——function——包含了 Lambda 函数的配置。我们在这里定义分配的内存大小、超时和events。在functionevents部分,我们可以定义一些事件,这些事件将触发我们的 lambda 函数。对于我们的项目,我们使用http,它将自动创建一个指向我们函数的 API 网关。您还可以为sqscrons3等定义事件。你可以在这里找到完整的列表。

部署功能

为了部署该功能,我们在package.json中创建一个deploy脚本。要部署我们的功能,我们需要启动并运行 docker。

之后,我们可以运行yarn deploynpm run deploy来部署我们的功能。这可能需要一段时间,因为我们正在用 docker 创建一个 Python 环境,并在其中安装我们所有的依赖项,然后将所有内容上传到 AWS。

这个过程完成后,我们应该看到类似这样的东西。

部署的 lambda 函数

测试和结果

为了测试我们的 lambda 函数,我们可以使用失眠症、邮差或任何其他 rest 客户端。只需在请求中添加一张受损或整辆汽车的图片作为多部分输入。让我们用这张图片试试。

测试图像

失眠请求

作为我们对红色汽车测试的结果,我们得到了01-whole,这是正确的。此外,您可以看到完整的请求花费了 319 毫秒,而 lambda 执行时间大约为 250 毫秒。老实说,这相当快。

如果您打算重新构建分类器,您必须小心,第一个请求可能需要一段时间。首先,Lambda 正在解压和安装我们的依赖项,然后从 S3 下载模型。完成一次后,lambda 需要大约 250 毫秒-1000 毫秒,这取决于分类的输入图像大小。

最棒的是,如果有几个传入请求,我们的分类器会自动扩展!

您可以毫无顾虑地扩展到数千个并行请求。

感谢阅读。你可以在这里找到 GitHub 库的完整代码,在这里找到 colab 笔记本。如果你有任何问题,随时联系我。

Lance Miles 提到,您可能必须在 API 网关的设置中包含“多部分/表单数据”作为二进制媒体类型,以便网关能够以 base64 编码通过失眠症发送的图像。

在现实世界中扩展机器学习

原文:https://towardsdatascience.com/scaling-machine-learning-in-real-world-cb601b2baf4a?source=collection_archive---------39-----------------------

图片来源:Unsplash @Nasa

机器学习正以惊人的速度在平台、工具、技术、堆栈和操作化的范围内发展。然而,现实世界中的扩展仍然存在差距,这种差距开始在处于解决可重复性、测量、版本问题的后期阶段的玩家或正在尝试一些模型或算法并受到运营方面阻碍的新玩家中变得明显。即使在更大或更成熟的参与者中,由于现实世界正在变化或处于不断变化的状态,这也是数据和相关模型的本质,因此扩展问题也很明显。在数据科学领域,这被称为隐藏的技术债务,它是发展的活跃领域之一。

在这篇文章中,我将分享我在应对这些挑战时的学习和经验,并列出一些架构原则或指导来解决这些问题,并构建在现实世界中扩展的能力

数据科学工作流程

以一个典型的数据科学工作流程为例,如 R Olson 的一篇论文中所述,我们可以看到这与通常的软件开发在实验和反馈决策循环的使用上有什么不同。允许扩展该工作流程的关键原则或因素是:再现性、测试和监控、测量和可观察性

图片来源:评估用于自动化数据科学的基于树的管道优化工具

再现性

图片来源:Unsplash @Kara Eads

再现性不是一个容易或简单的问题。需要考虑的方面有很多,其中比较重要的是数据、领域、外部依赖、结果测量和评估标准等。但是,有一些选项可以帮助您开始使用解决方案。这里的关键原则是版本控制,包括数据、模型和所有与执行相关的工件,如代码、管道、评估指标、接受和拒绝的结果、实验或 AB 测试需求等。

图片来源: NeurIPS 重现性清单

在软件栈中,可以捕捉这些类型需求的选项之一是 DVC,数据版本控制dvc.org的缩写。这是一个开源框架,位于 Git 之上,集成了云存储服务,如 AWS S3GCP GCS 。这允许 ML、NLP、DL 管道被版本化、跟踪和复制。摘自 dvc 文件:

“数据处理或 ML 管道通常从大型原始数据集开始,包括中间的特征化和训练阶段,并产生最终模型,以及准确性指标

为数据科学对大型数据文件和目录进行版本控制很好,但还不够。数据是如何过滤、转换或用于训练 ML 模型的?DVC 引入了一种机制来捕捉数据管道——产生最终结果的一系列数据过程。

DVC 管道及其数据也可以很容易地版本化(使用 Git)。这使您可以更好地组织您的项目,并在以后准确地再现您的工作流程和结果,就像它们最初构建时一样!"

图片来源:https://dvc.org/doc/command-reference/dag

在 DVC 中,流水线阶段和命令、它们的数据 I/O、相互依赖性和结果(中间或最终)在dvc.yaml中指定,可以手动编写或使用助手命令[dvc run](https://dvc.org/doc/command-reference/run)构建。这使得 DVC 可以在以后修复一条或多条管道(见[dvc repro](https://dvc.org/doc/command-reference/repro))。

DVC 为此构建了一个依赖图(DAG)。

在现实世界中,该解决方案跨环境层进行映射,从具有较少限制的构建配置的本地开发开始,到使用 Jenkins、Travis 等 CICD 工具构建的集成测试环境,再到具有严格限制的 prod 部署环境。

测试和监控

图片来源:Unsplash @cdc

世界的货币是变化,这表现在数据中。如果 ML 或 ML、NLP、DL 模型的任何组合提供的结果与当前模式不同步,则会对相关性、准确性、偏差以及收入、参与度、忠诚度和信任度等业务和企业指标产生负面影响。最近的一个例子是在 Covid 环境下客户行为和互动的变化。适应了这种变化的车型增加了客户参与度,加深了品牌信任度,建立了忠诚度,而由于数据变化而漂移的车型则相反。

机器学习的测试领域从根本上不同于软件测试。数据的组合、具有多个步骤的管道、离线在线培训服务、跨层的故障点等等,这些是一些关键因素。谷歌发布了一项重要的研究ML 测试分数:ML 生产准备和技术债务减少的指标为测试制定了指导方针

图片来源:ML 测试分数:ML 生产准备和技术债务削减的一个指标

有了这个指导,我们就可以使用这些边界来构建测试框架:数据、基础设施、模型、集成、监控等等,并有一定程度的重叠。在更高的层次上,这种划分可以被看作是数据+模型+代码以及生产/操作需求,比如监控。在软件方面,这转化为测试脚本(以 PyTest 为例)、执行模型(Docker)、跟踪框架(MLFlow)、日志记录(ELK stack Elastic Search/LogStash/ki Bana)、监控(Prometheus、Grafana)的组合

考虑到边界的数量和测试的类型,以及执行和反馈循环,这是一个非常复杂的任务。这里的方法是在 ML 技术债务分析、技术能力分析、基本需求分析和一些架构、路线图视图的指导下提出一个计划。这里的关键成果是开始构建由计划指导的框架,开始从团队获得现场使用,并开始反馈循环

测量和可观察性

图片来源:Unsplash @Dan Lohmar

测量对于建立任何类型的基线来衡量准确性和相关性都是至关重要的。度量有很多方面,如原始度量、聚合度量、基线、偏差、已解释的、未解释的等等。这是 ML 中快速发展并与运营和业务高度重叠的领域之一。测量和测试监控之间有很大的重叠,主要区别在于测量紧密关注可观察性,即通过观察外部来确定系统内部情况的能力。推特在他们的科技 博客帖子中提到了可观察性。辛迪·斯里哈兰的文章是一个很好的参考。引用她的文章:

“监控”最适合报告系统的整体健康状况。因此,监控最好局限于从基于时间序列的仪器、已知故障模式以及黑盒测试中获得的关键业务和系统指标。另一方面,“可观察性”旨在提供对系统 行为的高度精细的洞察,以及丰富的上下文 ,非常适合调试目的。

图片来源:普罗米修斯文档

度量标准可以利用数学建模(采样、聚合、汇总、关联)和预测的力量来获取系统在当前和未来的时间间隔内的行为知识。由于 numbers 针对存储、处理、压缩和检索进行了优化,因此指标可以延长数据的保留时间并简化查询。这使得指标非常适合构建反映历史趋势的仪表板,这些趋势描述了运营问题(延迟、内存/CPU 使用)以及预测监控(中值和平均值、最小值/最大值、标准偏差)

度量显示服务或应用程序的趋势,而日志则关注特定的事件。日志的目的是尽可能多地保存特定事件的信息。日志中的信息可用于调查事件并帮助进行根本原因分析。

图片来源:https://www.elastic.co/elastic-stack

就映射到软件堆栈而言,非生产或开发环境更适合诸如 MLFlow 等解决方案,而生产级环境需要 ELK、Prometheus、Grafana 类型堆栈的规模、速度和可靠性。

我们将何去何从

任何围绕扩展或生产数据科学的对话,都需要谈论持续集成/持续部署或 CICD,简而言之,在 DevOps、MLOps 领域众所周知。这是一个值得单独讨论的话题。简单地说,当我们整合并自动化再现性、测试、监控、测量、可观察性时,最终结果或成果是 CICD 执行模型或框架。在以后的文章中,我将探讨这些核心元素是如何组合在一起形成 CICD 管道的。

我希望这篇文章能提供一些关于机器学习的真实世界的有用信息,并有助于实现规模化。我期待听到您的反馈。

使用 Tensorflow Serving & Kubernetes 扩展机器学习模型

原文:https://towardsdatascience.com/scaling-machine-learning-models-using-tensorflow-serving-kubernetes-ed00d448c917?source=collection_archive---------28-----------------------

将 ML 模型投入生产和规模的开发人员指南

来源:哈里特,转自 Unsplash

Tensorflow serving 是一个神奇的工具,可以将您的模型投入生产,从处理请求到有效地使用 GPU 处理多个模型。当请求数量增加,系统很难跟上请求时,问题就出现了。这就是 Kubernetes 可以帮助编排和扩展多个 docker 容器的地方。

大纲:

  1. 设置对接
  2. 获取型号
  3. 集装箱化模式
  4. 设置谷歌云集群
  5. 使用 Kubernetes 部署型号

让我们开始吧:

1.设置 Docker

Docker 是什么?— Docker 提供了在松散隔离的环境(称为容器)中打包和运行应用程序的能力。(详情)

要安装docker 你可以在这里勾选它支持多个平台。

如果你使用的是 ubuntu,你可以使用:

***# Install docker community edition* curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) | sudo apt-key add -sudo add-apt-repository "deb [arch=amd64] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) $(lsb_release -cs) stable"sudo apt-get update
sudo apt-get install -y docker-ce**

要在 Ubuntu 中使用 docker,我们通常需要添加前缀 sudo,但是如果你不想每次都添加 sudo,你可以这样做:

***# Remove sudo access needed for docker* sudo groupadd docker
sudo gpasswd -a $USER docker**

我们将需要一个 DockerHub 账户,以便以后我们可以推动我们的 docker 形象。如果您没有帐户,请创建一个帐户。

***# Once your Dockerhub account is setup, login to docker* docker login**

2.获取模型

TensorFlow serving 仅支持 SavedModel 格式,因此我们需要将任何 TensorFlow 模型或 Keras 模型转换为 SavedModel 格式。这里有一个关于如何保存到保存的模型格式的例子https://www.tensorflow.org/guide/saved_model

为简单起见,我们将从 Tensorflow/models 下载一个预先训练好的 ResNet 保存模型。

***# Downloading ResNet saved models* mkdir /tmp/myresnetcurl -s [https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz](https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz) | tar --strip-components=2 -C /tmp/myresnet -xvz**

3.集装箱服务模式

原始来源:期限

接下来,我们需要制作一个 Tensorflow 服务图像。幸运的是 Tensorflow 服务图片已经在 Dockerhub 中建立并可用。它有 GPU 和 CPU 两个版本。让我们下载它。

***# Downloading the CPU version*
docker pull tensorflow/serving:2.1.0*# To download the GPU version you can just
# docker pull tensorflow/serving:2.1.0-gpu***

' tensor flow/serving:2 . 1 . 0 '*容器图像或任何其他 TF 服务图像的默认入口点为'/usr/bin/TF _ serving _ entry point . sh'。我们将创建自己的TF _ serving _ entry point . sh,我会在下面告诉你为什么:***

tf_serving_entrypoint.sh

上面的脚本运行 Tensorflow 服务,从' /models/resnet/ '加载模型,为 gRPC 打开端口 8500,为 REST-API 打开端口 8501。

***# Download the tf_serving_script.sh* curl -s [https://gist.githubusercontent.com/bendangnuksung/67c59cdfb2889e2738abdf60f8290b1d/raw/918cfa09d6efcc200bb2d617859138fd9e7c2eb4/tf_serving_entrypoint.sh](https://gist.githubusercontent.com/bendangnuksung/67c59cdfb2889e2738abdf60f8290b1d/raw/918cfa09d6efcc200bb2d617859138fd9e7c2eb4/tf_serving_entrypoint.sh) --output tf_serving_entrypoint.sh*# Make it executable* chmod +x tf_serving_script.sh**

建议创建我们自己的服务脚本,因为您将可以控制 模型名称、端口模型路径 。如果您有多个模型,默认的“tf_serving_entrypoint.sh”将抛出一个错误。为了服务多个模型,您需要为您的多个模型创建一个models . config并更新您的脚本。您的服务模型看起来有点像这样:

***# Just an example of running TF serving with models.cofig
# tensorflow_model_server --port=8500 --rest_api_port=8501 
# --model_config_file=/path/to/models.config***

要了解更多关于用 docker 上菜的 TF,请参考 tfx/serving/docker

RestNet 保存的模型tf_serving_script.sh 移动到 docker 镜像中并运行:

***# Run the tf/serving containerimage* docker run -d --name=servingbase tensorflow/serving:2.1.0*# copy the saved model* docker cp /tmp/resnet/ servingbase:/models/*# copy tf_serving_script.sh* docker cp tf_serving_entrypoint.sh servingbase:/usr/bin/tf_serving_entrypoint.sh*# commit* docker commit servingbase myresnet:latest*# kill the container* docker kill servingbase*# running new created image* docker run -d --name=myresnet -p 8500:8500 -p 8501:8501 myresnet:latest*# list running container and see whether its running* docker ps**

让我们测试一下码头工人是否响应我们的请求。将下载一个客户端脚本,使用 gRPCRESTAPI 进行推理。

***# Download the Restnet client script* curl [https://gist.githubusercontent.com/bendangnuksung/8e94434a8c85308c2933e419ec29755a/raw/0a52618cdce47d16f2e71c900f2a1ee92063933f/resnet_client_restapi_grpc.py](https://gist.githubusercontent.com/bendangnuksung/8e94434a8c85308c2933e419ec29755a/raw/0a52618cdce47d16f2e71c900f2a1ee92063933f/resnet_client_restapi_grpc.py) --output [resnet_client_restapi_grpc.py](https://gist.github.com/bendangnuksung/8e94434a8c85308c2933e419ec29755a)**

测试客户端脚本:

***# Test using GRPC* python [resnet_client_restapi_grpc.py](https://gist.github.com/bendangnuksung/8e94434a8c85308c2933e419ec29755a) -p 8500 -ip localhost*# Test using RESTAPI* python resnet_client_restapi_grpc.py -p 8501 -ip localhost*# We will see that GRPC has faster response time than RESTAPI
# Once its deployed in cloud the difference is much higher**# Stop running container* docker stop myresnet**

将图像推送到docker hub:****

***# Push image to Dockerhub | replace* ***DOCKERHUB_USERNAME*** with yourA/c name
docker tag myresnet:latest **DOCKERHUB_USERNAME**/myresnet:latest
docker push **DOCKERHUB_USERNAME**/myresnet:latest**

4.设置 Google 云集群

我们将使用谷歌云平台(GCP) ,因为它提供 Kubernetes 引擎,并提供一年 300 美元的免费试用,这对我们来说是一个惊人的资源。您可以在这里 免费试用您的 。您需要启用您的帐单来激活您的 300 美元免费试用。

GCP 允许你使用 Gcloud SDK 通过 CLI 处理资源。UbuntuMac安装 Gcloud SDK。

Kubectl 也需要控制 Kubernetes 集群。从T21 这里 安装 Kubectl。

****设置 GCloud 项目并实例化集群:

*# Proceed once Gcloud and Kubectl is installed**# Gcloud login* 
gcloud auth login*# Create unique Project name | Replace* ***USERNAME*** *with any unique name*
gcloud projects create **USERNAME**-servingtest-project*# Set project* gcloud config set project **USERNAME**-servingtest-project

激活 Kubernetes 引擎 APIhttps://console . cloud . Google . com/APIs/API/container . Google APIs . com/overview?project =USERNAME-serving test-project(将链接中的 USERNAME 替换为您之前提供的唯一名称)

创建并连接集群:

*# Creating a cluster with 2 nodes*
gcloud beta container --project "**USERNAME**-servingtest-project" clusters create "cluster-1" --zone "us-central1-c" --disk-size "30" --num-nodes "2"*# You can change the zone and disk size. More Details at* [*https://cloud.google.com/sdk/gcloud/reference/container/clusters/create*](https://cloud.google.com/sdk/gcloud/reference/container/clusters/create)*# Connect to cluster*
gcloud container clusters get-credentials cluster-1 --zone us-central1-c --project **USERNAME**-servingtest-project*# Check whether the 2 Nodes are ready:*
kubectl get nodes*# Sample output:* # *NAME                STATUS   ROLES    AGE   VERSION* # *gke-cluster-1-...   Ready    <none>   74s   v1.14.10-gke.36* # *gke-cluster-1-...   Ready    <none>   75s   v1.14.10-gke.36*

5.使用 Kubernetes 部署模型

什么是 Kubernetes?—它是一个容器编制器。你可以认为 Kubernetes 是一个很好的俄罗斯方块玩家,所以每当不同形状和大小的新容器进来时,Kubernetes 都会找到放置容器的最佳位置。

务必查看此视频以便更好地理解。

分两个阶段使用 Kubernetes 部署容器模型:

  1. ****部署:部署负责保持一套吊舱运行。我们为 pod 定义了所需状态的列表,部署控制器将实际状态更改为所需状态。

2.服务:将运行在一组pod上的应用程序公开为网络服务的抽象方式。我们定义了一个状态列表,比如端口应该监听哪里或者应该监听哪个应用程序。

service.yaml

****让我们下载这两个文件:

# Download deployment & service yaml files
curl [https://gist.githubusercontent.com/bendangnuksung/f1482aa9da7100bc3050616aaf503a2c/raw/7dc54db4ee1311c2ec54f0f4bd7e8a343d7fe053/deployment.yaml](https://gist.githubusercontent.com/bendangnuksung/f1482aa9da7100bc3050616aaf503a2c/raw/7dc54db4ee1311c2ec54f0f4bd7e8a343d7fe053/deployment.yaml)--output [deployment.yaml](https://gist.githubusercontent.com/bendangnuksung/f1482aa9da7100bc3050616aaf503a2c/raw/5634d69757aad3d392343bfbe15a85badcdf76c9/deployment.yaml)curl [https://gist.githubusercontent.com/bendangnuksung/5f3edd5f16ea5bc4c2bc58a783b562c0/raw/f36856c612ceb1ac0958a88a67ec02da3d437ffe/service.yaml](https://gist.githubusercontent.com/bendangnuksung/5f3edd5f16ea5bc4c2bc58a783b562c0/raw/f36856c612ceb1ac0958a88a67ec02da3d437ffe/service.yaml) --output service.yaml

我们将需要对' deployment.yaml '进行更改

*line 16: Change bendang to Your Dockerhub account name
from: image: bendang/myresnet:latest
to  : image:* **DOCKERHUB_USERNAME***/myresnet:latest*

****开始部署:

**kubectl get deployment** *#Output:No resources found in default namespace.***kubectl apply -f deployment.yaml** *#Output: deployment.extensions/myresnet-deployment created**# Wait until the depoyment is ready: 2/2***kubectl get deployment**
*#Output: 
# NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
# myresnet-deployment   2/2     2            2           1m*

这将把“ myresnet:latest”映像加载到“deployment . YAML”文件中定义的两个窗格中。

****启动服务:

**kubectl get service** *# output:
#NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
#kubernetes   ClusterIP   10.7.240.1   <none>        443/TCP   28h***kubectl apply -f service.yaml** *#output: service/myresnet-service created**# wait until it allocate external IP for LoadBalancer***kubectl get service**
*#output
#NAME         TYPE          CLUSTER-IP   EXTERNAL-IP     PORT(S)                         
#kubernetes   ClusterIP     10.7.240.1   <none>          443/TCP                       
#myresnet-s.  LoadBalancer  10.7.252.203* ***35.192.46.666*** *8501 & 8500*

运行‘service . YAML’后,我们将获得一个外部 IP,在本例中,它是 35.192.46.666。这个 IP 现在将成为我们的单一访问点,我们可以在这里调用我们的模型,所有的负载平衡都在内部处理。

测试:

我们仍将使用相同的脚本'resnet _ client _ restapi _ grpc . py',唯一的变化是提供我们创建的服务的'外部 IP '。

*# Test using GRPC* python [resnet_client_restapi_grpc.py](https://gist.github.com/bendangnuksung/8e94434a8c85308c2933e419ec29755a) -p 8500 -ip ***35.192.46.666****# Test using RESTAPI* python [resnet_client_restapi_grpc.py](https://gist.github.com/bendangnuksung/8e94434a8c85308c2933e419ec29755a) -p 8501 -ip ***35.192.46.666***

如果你有任何问题,请在下面的评论中告诉我。

敬请关注我下一篇关于 在谷歌云功能上部署机器学习模型

动手 PCA 数据预处理系列。第二部分:异常值处理

原文:https://towardsdatascience.com/scaling-outliers-handling-categorical-data-encoding-the-pca-hands-on-skills-series-part-ii-46e712e240eb?source=collection_archive---------51-----------------------

尝试使用 PCA 但停留在处理阶段?看看这个。

王思韵(本文合著者)。树枝上毛茸茸的小鸟【水彩】(2020)。我们用低聚艺术来表示 PCA 的感觉,就是降维。但是 PCA 能做的不止这些。

系列介绍

在本系列中,我们将探索缩放数据和 PCA 的结合。我们希望看到,每当我们遇到新的数据集时,我们如何才能更好地为机器学习任务准备数据。旅程由三部分组成。

  • 第一部分:定标器和 PCA
  • 第二部分:认识离群值
  • 第三部分:分类数据编码

我们将在这篇文章中做什么

  1. 介绍/回顾要处理的数据集和任务
  2. 向原始数据集添加合成异常值
  3. 对修改后的数据集执行缩放变换
  4. 对缩放变换后的数据集进行主成分分析并评估性能

你将学到什么

  • 理解定标器的重要性及其与 PCA 的密切关系
  • 明智地选择定标器,尤其是当存在异常值时
  • 使相关和漂亮的可视化:)

在你开始阅读之前,我们强烈建议你先玩玩这个笔记本(在 Colab 和 Github 上都有。请在这篇文章的末尾找到链接。)

遇到离群值

通过 scaler + PCA 组合继续我们的旅程,我们现在到达站 II。这一次,我们将用一些现实生活中经常遇到的障碍,比如离群值,来挑战缩放工具。

我们将向 wine 数据集添加几个合成异常值,并展示我们工具包中的每个定标器将如何反应。

请注意,异常值处理背后有丰富的理论。然而,探究科学的细节并不是我们这里的重点。

像往常一样,让我们通过导入 Python 包和定义我们的助手函数来做准备。你可能想看看这个系列的第一部分。

[## 缩放变压器。动手 PCA 数据预处理系列。(第一部分)

第一部分:定标器和 PCA

medium.com](https://medium.com/coffee-sucre/pca-a-practical-journey-preprocessing-encoding-and-inspiring-applications-64371cb134a)

第一眼

我们继续使用葡萄酒数据集。这些数据是对意大利同一地区三个不同种植者种植的葡萄酒进行化学分析的结果。对于三种类型(产地)葡萄酒中的各种成分,有十三种不同的测量方法。数据集仅包含数字要素。我们的目标是使用 13 种不同的测量方法找出目标标签,例如,原点在哪里。

winds 数据集是一个相当友好的数据集,如此友好,以至于使用任何缩放器进行预处理都几乎不会出错。但是,如果我们在集合中添加一些合成噪声/异常值来折磨我们的定标器会怎么样呢?所以我们随机选择三个特征,分别加入十个离群值。对于其余的特征,我们合成了与真实数据“相似”的十个值。但是首先,让我们在添加合成数据之前回顾一下原始数据集。

df_wine.describe()g = sns.pairplot(data=df_wine, 
                 vars=df_features.columns, 
                 hue='target_original',            
                 corner=True, palette=customer_palette,   
                 hue_order=target_order)

(提示:在单独的窗口中打开以获得更好的视图)

现在,修改的数据集(添加合成噪声/异常值)。

df_wine_new.describe()g = sns.pairplot(data=df_wine_new, vars=df_features.columns, hue='target_original',
                 corner=True, palette=customer_palette, hue_order=target_order)

(提示:在单独的窗口中打开以获得更好的视图)

由于人工痕迹,合成数据往往太容易被发现,有时使用此类数据得出的结论可能不太适用于真实数据集。(博客的作者之一非常不喜欢他们;p)。但是出于实验的目的,我们仍然使用 Numpy 的选择函数来生成异常值。我们尽最大努力使合成数据尽可能真实。

思考时间

想知道哪三个是被选中的吗?留下评论或玩提供的笔记本检查。;P

让我们画出协方差矩阵。

X_train_o = df_wine_new.iloc[:,:-4].copy()
y_train_o = df_wine_new.iloc[:,-4:].copy()# the following are customer functions. 
# refer to the notebook to see what the do.X_trans_dict_o, X_pca_dict_o = transformer_bundle(X_train_o)trans_heat_plot(X_trans_dict_o, y_axis_labels=df_features.columns)

回想一下,在本系列的第一部分,我们提到过:“经验法则是,热图越丰富多彩,PCA 结果越好。通常情况下,主成分分析不喜欢简单的热图,会输出不太有趣的主成分。这一次,我们邀请您特别关注鲁棒的定标器。请注意,热图的颜色条上升到最大值 17.5,添加了异常值的三个选定变量从图中突出,压倒了其余变量。这是因为鲁棒定标器采用每个变量的中间 50%部分(第 1 至第 3 个四分位数)来计算方差,并根据中值将值居中。

虽然健壮的定标器与标准定标器一样是线性定标器,但它排除了异常值的影响。因此,它保留了数据“正常”部分的结构,并强调了异常值,因此三个扰动变量的方差很大。热图的其余部分看起来很枯燥,这表明缩放器将所有变量的非异常值信息保持在相似的比例,而不管异常值。

另一方面,它的标准 scaler 表兄弟几乎破坏了三个所选列的非异常值的方差,使得三个列的信息部分与其他列相比不可用。然而,主成分分析会被这种转换所迷惑,对这三个变量给予了错误的关注(理论上,超过了应该给予的关注)。

即使我们试图描述舞台背后发生的事情,但最好还是玩玩笔记本,确认你的直觉,尤其是检查转换前后每个特征的方差。

开始任务

接下来,我们对缩放变换后的数据执行 PCA。我们将了解不同的定标器如何影响 PCA 结果。

让我们想象一下关于每个定标器的前两个主要部分。

# the following are customer functions. 
# refer to the notebook to see what the do.X_trans_dict_o, X_pca_dict_o = transformer_bundle(X_train_o)
pca_scatter_plot(X_pca_dict_o, y_train_o.iloc[:,1:],    
                 highlight_list=np.arange(-10,0))

由于异常值的存在,不同变压器的性能比较变得更加令人兴奋。;)

在多类分类的情况下,我们希望结果少受离群点的影响。(即,离群值不会独立于现有的三个类而形成新的类,并且“自然地”融入到这些类中。)两个转换器通过了这个测试:分位数转换器和最小-最大转换器。

另一方面,如果我们要做某种异常检测(比如说目标是识别不合格的葡萄酒,这是无监督学习的一个分支),我们会更喜欢让异常值更加突出的变压器,像标准缩放器和鲁棒缩放器。

或者,如果我们希望转换后的数据不被粉碎,换句话说,保持球形,那么我们还应该包括一个电源变压器。

请注意,这不是一个普遍的结论,因为我们所做的非常有限,我们没有涵盖合成异常值的其他可能方法。我们可以有把握地建议测试一大堆变压器(线性或非线性的),找到更适合问题的那一个。

在接下来的部分中,我们使用真实标签和 K-means 聚类标签来可视化增强的数据集。

首先,让我们把它看作一个分类问题,因为它本来就是这样的。

# set n_clusters=3, plot using customer function
kmeans = KMeans(n_clusters=3, random_state=RANDOM_STATE)
pca_cluster_contour_plot(X_pca_dict_o, y_train_o, kmeans)

等高线是每个聚类的区域,彩色点是带有真实标签的数据点。

从这个测试来看,几乎所有的线性转换器都会导致一个“坏”的结果:真实的数据点被异常值挤压得太厉害,以至于 K-means 甚至找不到 3 个聚类。相反,它只返回 2。在这个测试中,我们可能更喜欢分位数转换器、幂转换器和最小最大值转换器。

详细说明:像 K-means 这样的聚类方法是无监督的学习模型,不需要地面真实(即真实标签)的信息来学习。这样做的副作用是模型看不到绝对标签值的差异。例如,预测结果[0,0,1,2,2]与[11,2,0,0]基本相同。当我们使用指数评估结果时,这不是问题,比如 NMIAMIARIV-measure score 。但是这对于可视化来说就成了一个问题,因为……好吧,标签[0,0,1,2,2]肯定不等同于[11,2,0,0]当每个标签都与某种视觉辅助相关联时,比如颜色或形状。

资源:https://i.imgur.com/

在这里,我(饰演柯飞)试图以一种工程化的方式将预测标签与事实标签相匹配。我不得不承认,这种做法远非“科学”的方法。事实上,我不认为有一种“科学”的方法可以做到这一点,特别是当唯一的预测标签的数量与唯一的真实标签的数量不同时。但我对匹配的结果已经足够满意了。如果你对我如何匹配标签感到好奇,去玩笔记本吧。如果你想出了更复杂的标签匹配方法,请在评论中分享你的想法。

现在,让我们考虑一个异常检测问题。注意,我们通常不使用 K-means 来解决异常检测任务。相反,我们可以使用 DBSCAN、孤立森林或其他算法来检测数据集中的噪声。但是由于我们先验地知道有三个类,在这种情况下,可以通过增加 K-means 中的 K 值来检测离群值(例如,原始目标标签的三个聚类,离群值的额外的一个或多个)。下面是相同的图,但是 K=4。

# set n_clusters=4, plot using customer function
kmeans = KMeans(n_clusters=4, random_state=RANDOM_STATE)
pca_cluster_contour_plot(X_pca_dict_o, y_train_o, kmeans)

在评估性能之前,让我们先解决这个问题。

为什么剧情里有诡异的标签?(例如,' pred_1001.0 ',' pred_999.0')

如前所述,我们试图将 k-means 预测标签值与地面真实标签值进行匹配。其方法是找出在预测的标签区域(轮廓)内关联的真实标签(聚类)是什么。例如,查看 QuantileTransformer 图,在红色轮廓区域内,红色散射占主导地位,因此该轮廓将被命名为与红色相关联的“0”。

但是‘pred _ 1001.0’标签怎么了?你可能注意到,在紫色的“pred_1001.0”轮廓中,蓝色散射占主导地位。但是由于相关联的标签值“1”已经被蓝色轮廓“pred_1”占用,我们不能将紫色和蓝色轮廓都命名为“pred_1 ”,否则它们将合并为一个。(你可能已经猜对了:‘pred _ 1001.0’来自‘pred _ 1000+1’,dummy 来自‘pred _ 1’;' pred_999.0 '来自' pred_1000+-1 ',一个来自' pred_-1 '的哑元。)

那么,我们如何从剧情上分析表演呢?

如果我们最关心的是异常值,那么焦点就在“pred_-1”轮廓上。例如,‘pred _-1’轮廓是否包含大多数离群值。以异常值检测为目的,我们更喜欢标准的定标器、max-abs 定标器和电源变压器(甚至规格化器也不错;)).

请注意,电源变压器的性能非常出色。它不仅检测到了所有的异常值(虽然得到了一个额外的非异常值),而且在非异常值中实现了非常高的准确性。正因如此,这次测试的赢家是 PowerTransformer。

好的,分析中有很多信息。我会给你一些时间来评估情节。

资源:mem-arsenal.ru

备注:值得一提的是,由于我们是通过进一步推取一些变量的最大值来生成异常值的,因此异常值会在原始数据的一侧形成一组,如上图所示。然而,如果我们将合成数据的方式从单调改变为多方向,也就是说使一些最小值变得更小,我们将会看到异常值遍布整个特征空间。在这种情况下,K-means 不再适合,因为我们不知道离群值会形成多少个组。

摘要

我们已经使用合成异常值测试了我们的定标器;每个定标器对异常值的反应方式各不相同。根据您的任务来判断每个缩放器的性能会更加谨慎。

正如您所看到的,变压器区分异常值(即,不像使用 minMaxScaler 时那样混合异常值和非异常值)和保持非异常值方差(即,不像使用 standardScaler 时那样被粉碎)的能力是一种权衡。很多时候,根据个人喜好和具体情况来决定哪种转换器最合适。

扩展知识

对于离群值来说足够了。下一次,我们将测试 PCA 的极限,看看它对分类数据的反应。(是的,PCA 可以处理分类数据。)

我希望你喜欢这篇文章。请随时留下评论。

声明:该系列由莫克菲和王思韵合作。第一次发布:2020 年 6 月 9 日。

笔记本链接:

[## blog _ PCA _ part II-1 _ kefei _ 0605 . ipynb

编辑描述

drive.google.com](https://drive.google.com/file/d/15EWUv-h9qlEg86Q7GGg6la-mzs31xEV4/view?usp=sharing) [## kefeimo/数据科学博客

github.com](https://github.com/kefeimo/DataScienceBlog/blob/master/1. PCA/blog_PCA_part II-1_kefei_0605.ipynb)

攀登熊猫:比较达斯克、雷、摩丁、瓦克斯和拉皮兹

原文:https://towardsdatascience.com/scaling-pandas-comparing-dask-ray-modin-vaex-and-rapids-c74c85a4e59c?source=collection_archive---------31-----------------------

如何更快地处理更多数据

加速与 Dask、Ray、Modin Vaex 和 RAPIDS 的数据争论——来源:作者

Python 和它最受欢迎的数据争论库 Pandas 人气飙升。与 Java 等竞争对手相比,Python 和 Pandas 使得数据探索和转换变得简单

但是众所周知,Python 和 Pandas 都有关于可伸缩性效率的问题。

Python 立刻失去了一些效率,因为它是一种解释型的动态类型语言。但更重要的是,Python 一直关注简单性和可读性,而不是原始能力。同样,Pandas 专注于提供一个简单的高级 API,很大程度上忽略了性能。事实上,熊猫的创造者写了“我讨厌熊猫的 10 件事”,总结了这些问题:

性能问题和缺乏灵活性是熊猫自己的创造者不喜欢这个库的主要原因。(来源)

性能问题和缺乏灵活性是熊猫自己的创造者不喜欢这个库的主要原因。(来源)

因此,许多开发人员试图以各种方式为 Python 和 Pandas 增加更多功能也就不足为奇了。一些最著名的项目是:

  • Dask:一个低级的调度器和一个高级的部分 Pandas 替换,面向计算集群上的运行代码。
  • Ray: 跨处理器或集群并行化 Python 代码的底层框架。
  • 摩丁 : 熊猫的替代者,由达斯克驱动。
  • Vaex:部分熊猫替代,使用惰性评估和内存映射,允许开发人员在标准机器上处理大型数据集。
  • 急流 : 一个运行在 GPU 上的数据科学库集合,包括 cuDF ,部分替代了熊猫。

还有其他人。以下是 Python 数据争论的概况:

Dask、Modin、Vaex、Ray 和 CuDF 经常被认为是彼此的潜在替代者。来源:使用工具创建

那么,如果您正在处理大量数据,并且需要更快的结果,您应该使用哪一种呢?

告诉我该试哪一个

在决定使用哪种工具之前,最好对每种方法有更多的了解。我们将仔细比较它们,但您可能希望按照以下顺序尝试它们:

  • 摩丁,以射线作为后端。通过安装这些程序,只需更改一行代码(将' import pandas as pd '改为' import modin.pandas as pd ')就可以获得显著的好处。与其他工具不同,摩丁的目标是与熊猫完全兼容。
  • 一个更大更复杂的项目。但是 Dask 也提供了 Dask.dataframe,这是一个更高级的类似熊猫的库,可以帮助你处理核外 T21 数据集。
  • Vaex,它旨在帮助您在标准笔记本电脑上处理大量数据。它的 Pandas replacement 包含了一些 Pandas API,但它更侧重于探索和可视化。
  • 激流、如果有 NVIDIA 显卡的话

快速比较

我们研究的每个库都有不同的优势、劣势和扩展策略。下表对这些进行了概述。当然,和很多事情一样,下面的大部分分数很大程度上取决于你的具体情况。

Dask 和 Ray 比较成熟,但是摩丁和 Vaex 比较容易上手。如果你有 GPU 的话,Rapids 是有用的。

Dask 和 Ray 比较成熟,但是摩丁和 Vaex 比较容易上手。如果你有 GPU 的话,Rapids 是有用的。

这些都是主观的分数,根据你的具体情况可能会有很大的不同。在分配这些等级时,我们考虑了:

  • 成熟度:自第一次提交以来的时间和提交次数。
  • 人气:GitHub 明星数量。
  • 易于采用:用户期望的知识量、假定的硬件资源和易于安装。
  • 扩展能力:每个工具的广泛数据集大小限制,取决于它是主要依赖于单个机器上的 RAM、硬盘空间,还是可以扩展到机器集群。
  • 用例:无论这些库是被设计来加速 Python 软件的速度( General )、专注于数据科学和机器学习(数据科学)、还是被限制为简单地替换 Pandas 的“数据框架”功能(数据框架)。

CPU,GPU,集群,还是算法?

如果您的数据集太大,无法在单台机器上有效地工作,那么您的主要选择是跨…

  • …多线程或处理器:现代的 CPU 都有几个独立的内核,每个内核都可以运行很多线程。通过跨内核并行化来确保您的程序使用所有潜在的处理能力通常是最容易的起点。
  • …GPU 核心:显卡的设计初衷是高效地并行执行数百万像素的基本运算。然而,开发人员很快就看到了这种能力的其他用途,“GP-GPU”(图形处理单元上的通用处理)现在是一种流行的方法,可以加速严重依赖矩阵操作的代码。
  • …计算集群:一旦你达到单台机器的极限,你就需要一个联网的机器集群,协同工作。

除了增加更多的硬件资源,巧妙的算法也能提高效率。像 Vaex 这样的工具严重依赖于(在确定需要结果之前不做任何计算)和 内存映射 (将硬盘上的文件视为加载到 RAM 中)。

这些策略没有一个天生就比其他策略好,您应该选择适合您的特定问题的策略。

并行编程(无论您使用线程、CPU 内核、GPU 还是集群)提供了许多好处,但它也非常复杂,并且使调试等任务变得更加困难。

现代图书馆可以隐藏一些——但不是全部——增加的复杂性。无论你使用哪种工具,你都有可能期望一切都井井有条(左下),但结果却是一片混乱(右下)。

并行处理并不总是像你期望的那样完美。( Source Reddit )

并行处理并不总是像你期望的那样完美。(来源

达斯克 vs 雷 vs 摩丁 vs 瓦克斯 vs 激流

虽然并非所有这些库都是彼此的直接替代,但在决定为项目使用哪个库时,直接比较它们是有用的。

在进入细节之前,请注意:

  • RAPIDS 是一个图书馆集合。为了进行比较,我们只考虑 cuDF 组件,它相当于熊猫的急流。
  • Dask 最好被认为是两个项目:一个低级 Python 调度程序(在某些方面类似于 Ray)和一个高级 Dataframe 模块(在许多方面类似于 Pandas)。

达斯克对雷

Dask(作为一个底层调度器)和 Ray 在目标上有很多重叠,都是为了让 Python 代码更容易在机器集群上并行执行。Dask 更专注于数据科学领域,提供更高级别的 API,进而为 Pandas、NumPy 和 scikit-learn 提供部分替代,此外还有一个低级别的调度和集群管理框架。

Dask 和 Ray 的创建者在中讨论了这些库如何与 GitHub 线程进行比较,他们得出的结论是调度策略是关键区别之一。Dask 使用集中式调度器在多个内核之间共享工作,而 Ray 使用分布式自下而上的调度。

达斯克对摩丁

Dask(更高层次的数据框架)承认 Pandas API 的局限性,虽然它为了熟悉而部分地模拟了这一点,但它并不打算完全兼容 Pandas。如果你有复杂的现有 Pandas 代码,你不太可能简单地把 Pandas 换成 Dask。数据框架,让一切按预期工作。相比之下,这正是摩丁努力的目标:100%报道熊猫。Modin 可以运行在 Dask 之上,但最初是为了与 Ray 一起工作而构建的,这种集成仍然更加成熟。

Dask 对 Vaex

Dask (Dataframe)并不完全兼容熊猫,但是已经很接近了。这些紧密的联系意味着达斯克也背负着熊猫固有的一些包袱。Vaex 与 Pandas 的差异更大(尽管对于基本操作,如读取数据和计算汇总统计数据,它非常相似),因此也更少受到它的约束。

最终,Dask 更侧重于让您扩展代码以计算集群,而 Vaex 使在单台机器上处理大型数据集更容易。Vaex 还提供了一些功能来帮助您轻松地可视化和绘制大型数据集,而 Dask 则更专注于数据处理和争论。

达斯克对拉皮兹(cuDF)

Dask 和 RAPIDS 通过由 RAPIDS 提供的集成很好地合作。如果您有一个计算集群,您应该使用 Dask。如果你有 NVIDIA 显卡,你应该用 RAPIDS。如果你有一个 NVIDIA GPUs 的计算集群,你应该两者都用。

雷 vs .摩丁或瓦克斯或急流

把雷和摩丁,Vaex,或者激流比,没那么有意义。与其他库不同,Ray 不提供高级 API 或 Pandas 等价物。相反,雷为摩丁提供动力,而以类似于 Dask 的方式与急流整合。

摩丁 vs. Vaex

与 Dask 和 Vaex 相比,Modin 的目标是提供一个完整的 Pandas 替代品,而 Vaex 与 Pandas 的差异更大。如果您正在寻找一种快速的方法来加速现有的 Pandas 代码,那么 Modin 应该是您的第一选择,而 Vaex 更可能对新项目或特定用例感兴趣(尤其是在单台机器上可视化大型数据集)。

摩丁对急流(cuDF)

摩丁通过 Ray 或 Dask 使用许多 CPU 内核来扩展熊猫代码。RAPIDS 通过在 GPU 上运行熊猫代码来扩展它。如果您有可用的 GPU,请尝试一下 RAPIDS。但最容易的胜利可能来自摩丁,你可能应该先尝试过摩丁后再转向急流。

Vaex vs. RAPIDS (cuDF)

Vaex 和 RAPIDS 的相似之处在于,它们都可以在一台机器上提供性能提升:Vaex 通过更好地利用计算机的硬盘驱动器和处理器内核,而 RAPIDS 通过使用计算机的 GPU(如果可用且兼容的话)。RAPIDS 项目的整体目标是比 Vaex 更广泛,让你端到端地进行机器学习,而数据不会离开你的 GPU。Vaex 更适合原型开发和数据探索,允许您在消费级机器上探索大型数据集。

最后一句话:过早的优化是万恶之源

玩新的专业工具很有趣。也就是说,许多项目都存在过度工程化和过早优化的问题。如果你还没有遇到规模或效率问题,单独使用 Python 和 Pandas 也没什么问题。它们被广泛使用,提供了成熟性、稳定性和简单性。

一旦你达到了 Python 和 Pandas 自身的极限,你就应该开始研究这里讨论的库。否则,您可能会花费太多时间来选择和配置库,而不是在项目上取得进展。

我们已经用这些库构建了许多项目,并且知道何时以及如何使用它们。如果你需要第二种意见,请联系我们。我们很乐意帮忙。

利用在线回归等功能扩展实时推理

原文:https://towardsdatascience.com/scaling-up-real-time-inference-with-online-regression-and-more-902a939c171c?source=collection_archive---------53-----------------------

根据数据做出正确的决策意味着成功或失败。这就是为什么谷歌的团队试图实现“更多、更好、更快的实验”,也是为什么他们和许多其他公司投资开发内部实验平台(微软的 ExP网飞的闪亮优步的 XPBooking.com 的 ET ,不胜枚举)。基础是 A/B 测试,现在这种测试被大规模实时地广泛进行。

这些天来,我们的胃口已经超出了 A/B 测试。科学家们面临着挑战,要在越来越复杂的情况下运用更先进的技术来改进决策。作为一个个人例子,我帮助将微软的 CUPED 引入 Booking.com 的 ET 以增强实验能力。这有助于更好地做出决策,但我并没有快速或廉价地做出决策。它是每天使用整个数据集计算的,随着数据的增长,成本会越来越高。另一个例子是网飞的 XP 允许数据科学家部署任意的统计函数。同样的规模挑战也适用,因此他们有一个专门的数学工程团队来编写高性能代码并减少代价高昂的瓶颈(例如,分位数自举和 OLS 回归)。

巧合的是,这篇文章讨论了回归(CUPED 也基于此)对决策的价值,如何将结果扩展到大量数据并实时显示,以及一个简单的基础如何为实时统计方法的宝库铺平道路。下面是我们将涉及的技术部分的总结。

实时回归方法概述

附注:我正在分享这个问题统计方面的一个解决方案。首先,虽然我找不到它们,但其他解决方案可能已经存在了(我的意思是,网飞团队说它是可用的)。如果你知道并且有能力,我很乐意在评论中与你分享!另一件事是,这并没有涵盖问题的工程方面。为了大规模成功实施,您需要将了解这里发生的事情的数据科学家与知道如何适当地传输和存储数据的工程师配对。

回归

普通最小二乘回归(从现在开始我就称之为“回归”)是建模连续数据最广泛使用的方法之一。它在机器学习中被大量使用,但这不是我们在这里的目的。我们在这里用它进行统计推断。如果你不太熟悉这种使用方式,它应该会让你兴奋地了解到回归涵盖了许多有用的统计技术测试,从简单的 t 测试到结构方程建模。最后会有更多的介绍。基本上,在你的推理工具包中加入回归可以极大地扩展你做出正确决策的范围。

警告:我在故意推销回归的价值。然而,这种价值来自于对数据和结果的假设,这些假设对未经培训的用户构成了严重的风险。如果你对使用基于回归的方法进行推断感兴趣,一定要和一个了解风险的统计学家讨论。如果你不这样做,你最终可能会做出更糟糕的决定!

问题是,如何扩展回归并不像使用简单的技术那样显而易见。这当然是它没有被大规模利用的一个原因。因此,让我们开始研究如何扩展我们的回归工具包,以便大规模、实时地工作!

让它发挥作用的洞察力

压缩是关键

有效处理大规模数据通常涉及数据压缩。例如t——测试不需要每个数据点。他们只需要跟踪几个数字:组均值、方差和样本量(个人演示贴)。类似地,分位数自举可以用分桶数据来完成(网飞邮报)。如果您可以使用可以实时更新的压缩数据集,那么您就成功了!

事实证明我们可以利用这个来回归。无论您的总数据有多大,回归只需要一些汇总统计数据,这些统计数据可以随着新数据的到来而更新。

回归只需要协方差和 n

所有一个回归需要的是 协方差矩阵 和样本大小。

拥有 25 个指标和 1 亿个数据点。见鬼,10 亿个数据点?!没问题。你只需要 326 个数字。指标的 25x25 协方差矩阵(嗯,它的各种变换),它是对称的,因此有 325 个唯一的数字,一个数字代表样本大小。

现在我们知道了这一点,这篇文章的其余部分将介绍如何实现实时回归,然后讨论这种方法可能带来的其他好处。

让它工作

用于回归的实时数据压缩

注意:我将使用 r 来演示这项技术。你可以在这里 找到我的 GitHub 上的完整代码

我们需要协方差矩阵,但是随着数据的流入,使用中间对象更容易:偏差叉积矩阵。“偏差”指的是“与平均值的差异”,“叉积”是数值相乘的总和。因此,当一个新单元(例如,用户)的数据出现时,我们更新三样东西:样本大小、度量平均值(用于计算偏差)和偏差叉积矩阵。这里有一个总结:

当新实验单元的数据到达时,如何更新压缩数据

对于那些渴望代码的人来说,我写了一个 R 函数update_compression(),当新单元的数据到达时,它会这样做。你可以在 GitHub repo 中找到它。除了一个矩阵转置(如果你愿意可以避免),这都是基本的数学。

让我们通过一个简单的数据集来看看它的实际应用:

# A simple data set of 10 observations
xs#>              x1       x2        x3
#>  [1,] 0.1366358 5.719901 11.807291
#>  [2,] 2.0323437 4.750903  6.992279
#>  [3,] 1.2030423 5.903466  8.910174
#>  [4,] 1.9078714 5.649198  7.488551
#>  [5,] 2.6487667 7.465745 11.340035
#>  [6,] 4.5719305 7.744037 10.077654
#>  [7,] 0.7048246 4.347754  7.146864
#>  [8,] 2.5375564 6.234913  9.964397
#>  [9,] 1.2603890 4.258484  7.630200
#> [10,] 1.4273975 7.329029 12.639481

假设第一个单元(第一行)到达,我们运行我们的函数。结果是:

*# Compress first row of data*
compressed_d <- update_compression(xs[1,])
compressed_d#> $n
#> [1] 1
#> 
#> $means
#>         x1         x2         x3 
#>  0.1366358  5.7199010 11.8072912 
#> 
#> $dcrossp
#>      [,1] [,2] [,3]
#> [1,]    0    0    0
#> [2,]    0    0    0
#> [3,]    0    0    0

这表明我们的样本大小为 1,每个指标的平均值就是数据点本身。叉积都是 0,只有 1 个数据点。

现在每一行都按顺序显示。最后的结果是什么?

# Iteratively update the compression with remaining data **for** (i **in** 2:nrow(xs)) {
  compressed_d <- update_compression(xs[i,], compressed_d)
}compressed_d#> $n
#> [1] 10
#> 
#> $means
#>       x1       x2       x3 
#> 1.843076 5.940343 9.399693 
#> 
#> $dcrossp
#>           [,1]      [,2]      [,3]
#> [1,] 13.747618  8.726897  1.679592
#> [2,]  8.726897 14.509862 18.319109
#> [3,]  1.679592 18.319109 38.730283

样本量为 10。滴答!手段呢?

apply(xs, 2, mean)#>       x1       x2       x3 
#> 1.843076 5.940343 9.399693

滴答!

以及偏差叉积矩阵?我在回购协议中留下了一些更简单的检查方法。如果你知道你的矩阵代数,我们可以这样验证它:

xs_d <- sweep(xs, 2, apply(xs, 2, mean)) # all deviation scores
t(xs_d) %*% xs_d#>           x1        x2        x3
#> x1 13.747618  8.726897  1.679592
#> x2  8.726897 14.509862 18.319109
#> x3  1.679592 18.319109 38.730283

完美!

我们现在可以在单位进入我们的实验时更新高度压缩的数据。这是让事情实时、大规模运转的关键。

从压缩数据回归

我们现在需要的是根据请求从压缩数据中获得回归结果。下面是如何发生这种情况的总结。

从压缩数据(离差叉积矩阵和样本大小)到最终回归结果的进展总结

我为此写了另一个 R 函数,compressed_regression(),你也可以在这里的 repo】中找到它。请注意,这比数据压缩更复杂。你需要非常了解矩阵形式的回归,这样才容易理解。存在矩阵运算(例如,%*%的乘法运算、t()的变换运算和solve()的求逆运算)以及从 t 分布中获取 p 值的需求(使用pt())。不过,对于数据科学家和工程师来说,实现这些应该不是问题。

无论如何,让我们来测试一下。假设我们有之前的压缩数据,并想在 x2 和 x3 上回归 x1:

compressed_regression(
  compressed_d$dcrossp,
  compressed_d$n
)#>   coefficient standard_error   t_value     p_value
#> 1   1.3571198      0.2666634  5.089261 0.001416338
#> 2  -0.5985403      0.1632186 -3.667107 0.007994773

这与 R 的内置回归模型相比如何?

summary(lm(x1 ~ x2 + x3))$coefficients[-1,]#>      Estimate Std. Error   t value    Pr(>|t|)
#> x2  1.3571198  0.2666634  5.089261 0.001416338
#> x3 -0.5985403  0.1632186 -3.667107 0.007994773

完美!对于实现来说,这就差不多了。

注意:我的方法要求在计算中使用截距,但计算不准确。为什么?计算它需要额外的工作,但我从未见过截距在推理/假设测试(相对于预测)中有用。如果您想试着弄清楚它,您将想要开始修改代码 在这里 添加占位符值。

回归和超越!

随着实时回归成为一种选择,它还打开了什么?下图显示了一些统计技术,这些技术可以从这里讨论的完全相同的压缩数据中进行。我不打算解释它们或它们的用途,但我相信有统计学背景的读者会很兴奋。不过,如果你有问题,请提出来,因为这些答案可能会成为一篇好的后续文章。

只是一些统计技术,可以使用我们的压缩数据

对于推理和数据探索,大规模使用这些方法是非常令人兴奋的事情!如果你想探索一些更复杂的方法,我可以推荐用接受协方差矩阵作为输入的 lavaan 包在 R 中进行测试。

嗯,我就知道这么多了。感谢阅读,我希望这对你有用。如果你有问题,有改进的想法,或者你想更进一步,请评论!

扩展您的时间序列预测项目

原文:https://towardsdatascience.com/scaling-your-time-series-forecasting-project-9b7cb31a75dc?source=collection_archive---------30-----------------------

这篇博客基于我在国际预测研讨会 ISF2020 上的演讲,题目是— 扩大销售预测的最佳实践。

照片由埃里克·麦克莱恩Unsplash 上拍摄

我们预测什么,为什么?

Wix.com,我们一直在使用时间序列预测模型作为我们预测 Wix 未来产品系列的数据科学项目的一部分。这让该公司做了两件重要的事情:(1)基于未来的收款进行更好的预算规划;(2)对股市的准确引导。

预测收藏是一项具有挑战性的任务(每一项预测任务都是如此),但我们一直在不断改进,并取得了惊人的成绩。在这篇博客中,我想分享一些我们对扩展预测项目的见解和实践。

从底层开始扩展—设计的重要性

我们的设计基于独立的构建模块协同工作

为了使我们的系统能够扩展并容纳更多的特征、更多的模型以及最终更多的预测者,我们将它划分为上述构件。每个构件都独立存在,但也知道如何与他人沟通和合作。每个问题的简短解释(希望我将来能更深入地描述它们,请在下面评论/DM with requests):

  • 数据源—用于访问和查询我们所有不同的表格和数据库。
  • 特征存储 —使用来自数据源的数据创建时序特征。给定所请求要素的列表,它将返回一个时序数据集。
  • 模型— 为清晰起见,本文引用了时间序列的开源模型。
  • 模型库 —模型的包装器,具有我们选择的超参数,并与源自特征库的数据集兼容。
  • 预测器 —从特征存储中获取数据集,从模型库中获取模型,并为我们所需的任务创建时间序列预测器。
  • 气流 DAGDAG%20as%20code.)在我们的系统中扮演着几个角色,在这种情况下(后面会有更多)我们指的是它安排预报员的运行(在我们的情况下是每天)

如上所述,这种设计支持扩展,同样重要的是,它支持快速实验,这是每个成功的数据科学项目的基础。

让您的朋友和数据更亲密

数据始终是每个数据科学项目中最重要的部分。当预测一个源于你的业务的时间序列时,你自然会想要添加你可用的“内部”信息。当你想给时间序列预测增加一些特性时,你必须确保你知道这些特性的未来值是多少(或者你也必须预测它们,这会增加很多复杂性)。也就是说,这些要素可能会随着时间的推移而发生变化,并且可能会出现不同类型的错误,从而极大地影响您的结果。

从下游错误到 上游 解决方案 —密切关注您的结果和数据是发现下游问题的关键(下一节将详细介绍),这里的重点是将这些转化为上游解决方案——基于我们的设计,我们能够在不同层上进行数据验证:

  • 数据源验证我们正在获取符合我们预期的最新数据。
  • 特征库验证特征是否符合我们对数据集的要求。
  • 气流 DAG 在运行预测器之前验证表格是否已更新以及之前的流程是否成功完成。

监控——我们如何适应业务变化

我们可以将业务变更分为两类:

  • 内部变化 —例如,新销售、新产品、价格变化等..
  • 外部变化——比如一个新的竞争对手,甚至是一个遥远的全球性疫情。

尽管变化可能非常不同,但适应这些变化需要经历相似的阶段:

  1. 发现变化 —这是我们进行监控的地方,我们不断跟踪我们的结果,并通过分析报告和电子邮件提醒(再次使用 DAG)将它们与过去的结果进行比较。
  2. 了解变化— 变化有趋势吗?我们能量化影响吗?它从哪里来的?这在内部和外部变化之间是非常不同的,它总是一个非常具有挑战性的任务。有时甚至理解我们并不完全理解这种变化也可能是我们的结论。
  3. 帮助模型理解变化— 这很大程度上取决于我们在前一阶段得出的结论,如果我们对变化及其未来行为有很好的理解,我们可能会通过某个特征将其引入模型。我们也可以决定看看这个模型是否会自己发现这个变化。

何时以及如何改进您的车型

micha Parzuchowski 在 Unsplash 上拍摄的照片

如果你仔细遵循,现在你已经有了一个相当好的系统,但是现在呢?你应该在什么时候改进你的模型,如何改进?请做好准备,这一部分会包括一些棘手的问题,而不是很多决定性的答案。

也就是说,一个答案可以解决所有问题——实验是处理这些问题的关键。

如果它没坏,就不要修理它?

预测器运行良好,但是您开始考虑一个应该非常相关的新功能——您应该添加它吗?

如果它开始发出吱吱声,然后修理它?

预测者的误差略有增加,你要回到上一节,标出一些可能的变化。但是你不确定预测者的这种变化会是什么结果——你应该添加它们吗?

什么时候改变是好的改变?

改变预测器或增加一个功能会增加复杂性和未来可能的技术债务(尽管我们的系统旨在减少这种情况)。这项新功能的影响是什么?随着时间的推移,这种影响会增加吗?改进是否足够显著,足以证明增加新功能或改变预测工具的成本是合理的?

追踪变化,改写历史

假设我们已经添加了新的变更,现在是时候回去监控变更并查看其效果了。但从某种意义上来说,我们现在是在比较两种不同的预测者(苹果和橙子?).一方面,我们希望看到我们的新预测者与旧预测者的对比,但另一方面,我们希望看到我们的新预测者与新预测者的对比,就好像它在一周前运行一样(据此是苹果与苹果的对比)。

为此,我们有两个表:

  • 真实历史基本上是当天生产的模型的实况结果。
  • 更新历史历史,就好像我们当前的模型和当前的特征和数据一直伴随着我们(我们为此运行所有以前的日期)。

结论

当业务快速变化时,扩展并保持预测系统的准确性是一项非常具有挑战性的任务,但是使用这些实践对我们来说已经走了很长的路,希望它也能为您服务。

使用计算机视觉的扫描文档分类

原文:https://towardsdatascience.com/scanned-document-classification-using-computer-vision-33a42d9e01f9?source=collection_archive---------6-----------------------

一种解决扫描文档分类问题的深度学习方法

在数字经济时代,银行、保险、管理、医疗和法律等部门仍然需要处理各种手写笔记和扫描文档。在业务生命周期的后期,手动维护和分类这些文档变得非常繁琐。对这些非机密文档进行简单而有意义的自动化宁滨将使维护和利用这些信息变得容易得多,并大大减少人工工作量。

扫描的文档

本案例研究的目标是开发一个基于深度学习的解决方案,可以自动对文档进行分类。

数据:对于本案例研究,我们将使用 RVL-CDIP(瑞尔森视觉实验室复杂文档信息处理)数据集,该数据集由 16 类 400,000 幅灰度图像组成,每类 25,000 幅图像。有 320,000 幅训练图像、40,000 幅验证图像和 40,000 幅测试图像。调整图像大小,使其最大尺寸不超过 1000 像素。该数据集的大小超过 200 GB。

业务-ML 问题映射:我们可以将业务问题映射为多类分类问题。当前数据集中有 16 个类别。我们需要仅基于扫描文档的像素值来预测文档的类别,这使得问题变得困难。 但是等等,为什么不能用 OCR 提取文字,应用 NLP 技术呢?是的,我们也对这个想法感到兴奋,但是低质量的扫描导致了较差的文本提取质量。在实际的商业场景中,我们也无法控制扫描的质量,因此依赖于 OCR 的模型即使经过适当的预处理也可能会泛化能力差。

KPI 和业务约束:数据集相当平衡。因此,我们选择准确性作为主要指标,微观平均 F1 分数作为次要指标,以惩罚错误分类的数据点。我们还使用混淆度量来验证模型的性能。对延迟的要求适中,对可解释性没有具体要求。

我们能从文档的像素强度和大小中得到什么吗?

让我们尝试使用一个方框图来显示文档的平均像素强度和大小

从方框图中,我们可以观察到某些类型的扫描文件的尺寸与其他文件大不相同,但也有重叠。例如,类别 13 和类别 9 的文件大小差别很大,但是类别 9 的大小与类别 4 和类别 6,7 重叠。

我们可以观察到,对于 75%的情况,类别 4 的平均像素强度位于 160–230 像素之间。但是对于大约 50%的情况,它也与类别 6 的平均像素值重叠。对于其他类,平均像素值重叠。

分析方法

为了解决眼前的问题,我们在扩充的数据上训练了卷积神经网络(CNN)。我们已经尝试在有和没有数据增强的情况下训练模型,结果是可比较的。

高级分析工作流程图

太好了!但是如何决定网络架构呢?你是如何训练网络的,因为数据一次装不下?

从头开始训练神经网络需要大量的时间和计算资源来收敛,为了避免这种情况,我们采用了迁移学习的帮助。我们从在 ImageNet 数据集上训练和在我们的数据集上重新训练的预训练网络的权重开始。针对这类问题的当前 SOTA 模型使用域间和域内迁移学习,其中图像被分成四个部分:页眉、页脚、左体和右体。首先使用预训练的 VGG16 模型来训练整个图像(域间),然后使用该模型来训练部分图像(域内)。

在这个实验中,我们采用了一种稍微不同的方法。我们没有使用 VGG16 进行域内迁移学习,而是训练了两个并行模型 VGG16 和 InceptionResNetV2,并使用这些模型的堆栈作为我们的最终模型。我们的假设是,由于这两个模型的不同架构,他们将学习图像的不同方面,并将它们堆叠将导致良好的概括。 但是我们如何选择这些型号呢? 这基本上来自交叉验证的结果。 我们尝试了各种网络架构,像 VGG16、VGG19、DenseNet、ResNet、InceptionNet,选出了最好的两个。

我们使用 keras 的 ImageDataGenerator 类来预处理和加载训练数据,而不是将整个数据一次性加载到内存中。

VGG16 网络的最终培训阶段

好的。但是如何处理超参数呢?

对于任何 CNN 的超级参数是:学习率,池大小,网络大小,批量大小,优化器的选择,正则化,输入大小等。

学习速率对神经网络的收敛起着重要的作用。深度学习问题中使用的损失函数是非凸的,这意味着在存在几个局部最小值和鞍点的情况下,找到全局最小值不是一件容易的事情。如果学习率太低,它将缓慢收敛,如果学习率太高,它将开始振荡。在本案例研究中,我们使用了一种称为“循环学习率”的技术,其目的是以这样一种方式训练神经网络,即对于每个训练批次,学习率以循环方式变化。

但是它为什么会起作用呢?在 CLR 中,我们在一个阈值内改变学习率。周期性的较高学习率有助于克服它是否停留在鞍点或局部最小值。

对于其他超参数,我们开发了定制的效用函数来检查哪种配置效果更好。假设在 10 个时期之后,我们得到了 47%的准确度。我们将在那时使用该模型作为测试基线,并使用效用函数来检查哪个配置集(即 batch _ size/optimizer/learning _ rate)将在未来时代产生更好的准确性。

结果

我们使用 VGG16 模型实现了 90.7%的准确率,使用 InceptionResNetV2 实现了 88%的准确率。上述两个模型的比例叠加模型获得了 97%的训练准确率和 91.45%的测试准确率。

你可以在这里找到完整的实现。

引用:

  1. A.W. Harley,A. Ufkes,K. G. Derpanis,“用于文档图像分类和检索的深度卷积网的评估”,ICDAR,2015 年。
  2. https://arxiv.org/abs/1506.01186
  3. https://www . research gate . net/publication/332948719 _ Segmentation _ of _ Scanned _ Documents _ Using _ Deep-Learning _ Approach

使用 DeepAI 扫描旧的黑白胶片底片并将其转换为彩色图像

原文:https://towardsdatascience.com/scanning-and-converting-old-black-and-white-film-negatives-to-color-images-using-deepai-5eb9c549d34a?source=collection_archive---------27-----------------------

封面照片由Grzegorz Mleczek

这篇文章与我之前关于(生物)医学的文章无关,但是使用了我在博士研究期间学到的一些 Python 技巧。它没有深入研究深度学习背后的理论,而是展示了一个如何使用该技术的实际例子。我在本教程中使用 Adobe Photoshop 和 Lightroom,如果你成功地使用了开源软件,请在评论中告诉我。

世界大部分地区再次陷入困境,许多人又回到了家中。如果你正在寻找一件不涉及任何其他人的事情,你可能会在本教程中找到一些灵感。

我的祖父母给了我一些旧的黑白底片,这篇博客描述了我用 DeepAI 扫描、数字化和着色的过程:

之后 vs 之前

扫描胶片底片

我用我的 DSLR(尼康 D3500 和 Samyang 135 mm f/2.0 镜头)、一个三脚架、我的手机作为背光和一个纸箱扫描了胶片底片,我剪下这个纸箱以便底片可以放进去。如果你没有 DSLR,你可以用你的手机作为相机,笔记本电脑屏幕作为背光,但不要指望惊人的效果(使用一个捕捉原始文件的应用程序,最好是一个三脚架,以保持尽可能低的 ISO。我在下面添加了 DSLR 与小米 9T 相机的对比。

DIY 底片扫描仪,智能手机放在盒子背面。胶片和智能手机之间需要有一些距离,否则你最终会得到图像中的像素模式。

我的相机设置是 ISO100,f/4 和 1/8 秒快门速度(确保你拍摄的是 RAW 而不是 JPG),结果是这样的图像:

反转负像

每张照片都是不同的,所以你必须尝试什么是有效的。我是从亚历克斯·伯克的指导开始的。

扫描、裁剪和编辑 B&W

lightroom 预设可以在 my Github 上找到。

给 B&W 图像着色

现在我们进入最有趣的部分:使用深度学习将 B&W 图像转换为彩色图像。

互联网上有很多选择,但大多数要么很贵,要么没那么好(或者两者兼而有之)。我开始在 Photoshop 上使用新的神经过滤器的“着色”功能,但结果很糟糕。

在更多的谷歌搜索之后,我找到了 DeepAI。不幸的是,每个大于 1200x1200 像素的图像都会被缩小,如果您在工作流程的其余部分使用 RAW 文件和高分辨率图像,这是一个遗憾。据我所知,deepAI 的付费版本也不支持更大的图像,如果他们支持,请告诉我。

幸运的是,DeepAI 允许使用 API 提交作业。换句话说,如果我们将图像分割成 1200x1200 像素的重叠拼贴,给这些拼贴着色,然后将它们拼接在一起,应该不会有任何质量损失。分割图像可以用这个代码来完成:

一旦所有图像被分割,我们可以使用 Python 将每个单独的图像块提交给 DeepAI:

在 Photoshop 中自动合并单个帧:“文件”>“自动”>“Photomerge ”,并根据图像执行一些额外的调整(对比度、鲜明度等):

用我的智能手机代替我的 DSLR 得到的结果要差得多,但是我没有优化这个过程:

所有代码都在我的 GitHub 上。如果你发现 DeepAI 使用的模型有任何偏见,请发推文或电子邮件告诉他们。

玩得开心,呆在家里,保持社交距离,阻止冠状病毒的传播。

近红外光谱中的散射校正和异常值检测

原文:https://towardsdatascience.com/scatter-correction-and-outlier-detection-in-nir-spectroscopy-7ec924af668?source=collection_archive---------22-----------------------

人工智能领域的最新进展已经引起了 NIR 传感器数据和机器学习技术的集成,以实现这些结果。在这篇文章中,近红外传感器与电脑接口和样品进行扫描。我们对每个样本进行了十次扫描,扫描时间为十秒钟,以减少误差并包括样本的所有部分。在我们的案例中,扫描结果是分散的,并且由于光线、工作距离和扫描过程中的人为错误的变化,我们有异常值。为了减少散射和消除异常值,我们对近红外(NIR)数据实施了乘性散射校正(MSC)和标准正态变量(SNV)。

数据预处理是在近红外光谱和大多数光谱分析中建立大多数类型校正模型的基本步骤。通过精心设计的预处理步骤,可以大大提高模型的性能。

原始数据;样本 1 和样本 2 包含重复读数

1 .具有 10 个重复读数的样品 1 的原始吸收光谱。x 轴:波长,y 轴:吸光度单位

图 2 .具有 10 个重复读数的样品 2 的原始吸收光谱。x 轴:波长,y 轴:吸光度单位

Python 中的乘法散射校正

MSC 需要一个参考光谱作为开始。理想的参考光谱是没有散射效应的光谱。在实践中,我们将样本数据的平均值作为参考。这是 MSC 和 SNV 最重要的区别。

现在,正如你可以收集到的,得到一个没有不必要的散射效应的光谱并不容易,不是在我们感兴趣的所有波长上。因此,如果数据表现良好,我们可以将平均光谱作为我们所追求的理想光谱的近似。颗粒尺寸和路径长度的影响会因样品不同而随机变化,因此平均值会合理地减少这些影响,至少在这些影响真正随机的近似值中是如此。这是 MSC 背后的主要假设。

让我们认为 Xm 是平均谱。我们首先相对于平均光谱回归每个光谱 Xi。这是通过普通最小二乘法 Xi ≈ ai + biXm 完成的,然后我们计算校正的频谱 Xmsc = (Xi-ai)/bi

def msc(input_data, reference=None):
    """
        :msc: Scatter Correction technique performed with mean of the sample data as the reference. :param input_data: Array of spectral data
        :type input_data: DataFrame :returns: data_msc (ndarray): Scatter corrected spectra data
    """ eps = np.finfo(np.float32).eps
    input_data = np.array(input_data, dtype=np.float64)
    ref = []
    sampleCount = int(len(input_data))

    # mean centre correction
    for i in range(input_data.shape[0]):
        input_data[i,:] -= input_data[i,:].mean()

    # Get the reference spectrum. If not given, estimate it from the mean # Define a new array and populate it with the corrected data    
    data_msc = np.zeros_like(input_data)
    for i in range(input_data.shape[0]):
        for j in range(0, sampleCount, 10):
            ref.append(np.mean(input_data[j:j+10], axis=0))
            # Run regression
            fit = np.polyfit(ref[i], input_data[i,:], 1, full=True)
            # Apply correction
            data_msc[i,:] = (input_data[i,:] - fit[0][1]) / fit[0][0]

    return (data_msc)

此外,我们可以绘制 MSC 值

def linegraph(df,name="data"):
    """
        :linegraph: Plot absorbance unit vs wavelength number graph :param df: Spectral data containing absorbance units and wavelengths
        :type df: ndarray :returns: Absorbance unit vs wavelength graph
    """ # Read the spectral data file
    neospectraDataFile= neoSpectraSensorData.getNeospectraDataFile() # Get the wavelengths
    wavelengths =
    np.array(neospectraDataFile.columns).astype(np.float)
    x = wavelengths
    y = df
    ys = [i for i in y] #put into the format for LineCollection # We need to set the plot limits, they will not autoscale
    fig, ax = plt.subplots(figsize=(10,10), dpi=150)
    ax.set_xlim(np.min(x), np.max(x))
    ax.set_ylim(np.min(ys), np.max(ys)) # Make a sequence of x,y pairs
    line_segments = LineCollection([np.column_stack([x, y]) for y in ys],linewidths=(0.5),linestyles='solid')
    ax.add_collection(line_segments)
    ax.set_title('Absorbance unit graph of '+name, fontsize = 15)
    ax.set_xlabel(r'Wavenlength', fontsize=15)
    ax.set_ylabel('Absorbance', fontsize=15)

    return(plt.show())

理学硕士的结果:

图 3 .样品 1 的 MSC 校正数据图;x 轴:波长,y 轴:吸光度单位

图 4 .样品 2 的 MSC 校正数据图;x 轴:波长,y 轴:吸光度单位

Python 中的标准普通变量

与 MSC 不同,SNV 校正是在每个单独的光谱上进行的,不需要参考光谱。SNV 修正也可以分为两个概念步骤。

通过去掉其均值来对每个 Xi 频谱进行平均。将每个以平均值为中心的谱除以它自己的标准偏差:Xisnv =(Xi-Ximean)/sigma

def snv(input_data):
    """
        :snv: A correction technique which is done on each
        individual spectrum, a reference spectrum is not
        required :param input_data: Array of spectral data
        :type input_data: DataFrame

        :returns: data_snv (ndarray): Scatter corrected spectra
    """

    input_data = np.asarray(input_data)

    # Define a new array and populate it with the corrected data  
    data_snv = np.zeros_like(input_data)
    for i in range(data_snv.shape[0]): # Apply correction
    data_snv[i,:] = (input_data[i,:] - np.mean(input_data[i,:])) / np.std(input_data[i,:])

    return (data_snv)

通过调用上面的线图函数,进一步绘制 SNV 图

SNV 的结果:

图 5 是样品 1 的 SNV 校正数据图;x 轴:波长,y 轴:吸光度单位

图 6 样品 2 的 SNV 校正数据图;x 轴:波长,y 轴:吸光度单位

对原始数据应用 MSC 和 SNV 后的结果:

图 7. a .原始光谱 b. SNV 光谱 c .样品 1 的 MSC 光谱

图 8. a .原始光谱 b. SNV 光谱 c .样品 2 的 MSC 光谱

现在,结果被分散校正。MSC 中的异常值可以通过使用简单的 z 分数技术来消除。

分散校正数据的异常值检测

MSC(原始数据)的异常值相当低。因此,如果光谱中有超过 40%的点是异常值,我们就认为它是异常值

zscore =(individual sample-individual sample . median())/individual sample . STD()

定义 z 得分函数:

def zscorefunction(arrayMatrix, threshold=1):
    """
        :zscorefunction: Compute the z score of arrayMatrix     
        (Individual sample), relative to the sample median and   
         standard deviation. :param arrayMatrix: Array file containing spetral data of   
        one sample
        :type arrayMatrix: array :returns: The coordinates of the points which are 
        considered to be outliers. We are interested in x coordinate of the results. 
        Here, In our case, the x coordinate is the spectra number. - Example of the output:: output:(array([1,2,1,2,3,4]), array([1,5,8,70,85,143]))

            Read as, (spectra number, point in the spectra) 
            (1,1),(2,5),(1,8),(2,70),(3,85) and (4,143). 

            [1,2,1,2,3,4] are the spectra number of the sample 
            and [1,5,8,70,85,143] are the point in the spectra

            The 1st and 8th wavelength point in the 1st spectra 
            are outliers.similarly, The 5th and 70th wavelength   
            point in the 2nd spectra are outliers """
    # A z-score is the number of standard deviations away from a  
    mean for a data point. 
    zscore = (arrayMatrix - np.median(arrayMatrix))/   
    arrayMatrix.std()

    return (np.where(np.abs(zscore) > threshold))

z 得分函数返回被视为异常值的点的坐标。

def deleteOutliersSummary(X,Y,summary = True):
    """
        :deleteOutliersSummary: Calls the z score function to get   
        the outlier spectra numbers.We are interested in x 
        coordinate of the results. In our case, the x coordinate is 
        the spectra number.So, we apply the counter function on the 
        result to get the total count of outlier points for 
        spectra.and delete the spectra if the spectra has 75% of 
        its points as outliers :param X: Training spectral file (usually MSCTrain)
        :type X: array :param Y: Training target file      
        :type Y: array :returns: individualX (list) and y (list), 
        New spectral & target train files with outliers eliminated """ # A z-score is the number of standard deviations away from a 
    mean for a data point. 

    # We define a deleteSpectra where we store the Spectra number 
    with more than 75% (you can change this based on your MSC data)
    points as outliers deleteSpectra = []
    individualX = neoSpectraSensorData.getIndividualSamples(X)
    y= getMeanData.getMeanTarget(Y)
    out = 0
    noOut = 0
    sampleCount = len(individualX) for i in range(0,sampleCount):
        # call the function
        x = zscorefunction(individualX[i])[0]
        # print sample number and spectra number with its   
        corresponding number of outlier points

        if summary == True:
            # Counter gives the spectra number(index): number of  
            outlier points
            print("\nSAMPLE",i+1)
            print(Counter(x)) threshold = 0.75*X[1].shape[0]
        for j in range(0,individualX[i].shape[0]):
            # If the sepctra contains more than 75% of points as   
            outliers, delete the spectra

            if (Counter(x)[j] > threshold):
                deleteSpectra.append(j) # Delete the outlier spectra from the sample
        individualX[i] = np.delete(individualX[i], deleteSpectra, 0)
        y[i] = np.delete(y[i], deleteSpectra, 0)

        # If the sample has no outliers in all it's spectra then 
        display "No outliers detected"
        if deleteSpectra != []:
            out +=1
        else:
            noOut +=1

        if noOut == sampleCount:
            print("No outliers detected")

        if summary == True:
            print ("Delete Spectra:",deleteSpectra)
        del deleteSpectra[:]

    return(individualX,y)

输出:

计数器给出光谱数(指数):异常点的数量

从 MSC 数据中剔除异常值后绘制数据:

图 9 .从样品 1 的 MSC 数据中消除异常值后的图

图 10 .消除样本 2 的 MSC 数据上的异常值后的图

这里您可以看到,在经过散射校正的数据中,异常值被消除了。现在,我们可以使用这些数据来建立模型。

最终结果:

原始数据—原始数据的异常值剔除

图 11 .原始数据— MSC 数据—样本 1 的 MSC 异常值消除

图 12 .原始数据— MSC 数据—样本 2 的 MSC 异常值消除

谢谢你的时间。希望对你有帮助。

参考:https://nirpyresearch . com/two-scatter-correction-techniques-NIR-spectroscopy-python/

散点图:用 matplotlib、seaborn 和 pandas 绘制实验结果

原文:https://towardsdatascience.com/scattered-boxplots-graphing-experimental-results-with-matplotlib-seaborn-and-pandas-81f9fa8a1801?source=collection_archive---------9-----------------------

利用 python 可视化库绘制散点箱线图

Ciaran 库尼使用 Matplotlib 绘制。

Matplotlib、Seaborn 和 Pandas 的联合力量为数据科学家和工程师提供了丰富的资源,用于数据可视化和结果呈现。然而,对于初学者来说,将可用的工具操作成他们想象的美丽图形并不总是容易的。我在自己的工作(脑机接口博士学位)中发现,我获取的数据并不总是以直接适用于使用某些功能的方式进行组织,定制可能很困难且耗时。此外,选择最佳类型的图表来显示您的结果可能需要深思熟虑,并且经常需要反复试验。

散点图是一种非常有效的交流结果的方式,既能吸引观众的眼球,又能给观众提供信息。箱线图显示了结果的分布,显示了中间值、四分位距和其他与数据的偏斜度和对称性相关的因素。你可以在这里找到了解 boxplots 的有用教程:https://towards data science . com/understanding-box plots-5e 2 df 7 bcbd 51

使用散点图将特定的数据点叠加在箱线图上,是以一种吸引人的方式描绘结果的一种极好的方式,这种方式可以使读者很容易理解所呈现的内容。在这篇文章的剩余部分,我将逐步完成创建散点图的过程,提出一些定制的想法,并展示如何同时绘制多个图形。

Scatted box plots

在导入所需的 Python 包之后,我创建了一个小型数据集,表示可能从包含结果(例如,使用不同算法的分类结果)的 excel 文件中读入的数据类型。这里,我有四列数据,每一列包含从 65 到 95 的二十个值(类似于分类精度)。每列还有一个基本标题。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsdataset = np.random.default_rng().uniform(60,95,(20,4))
df = pd.DataFrame(dataset, columns=['data1','data2','data3','data4'])
df.head()

excel 格式的随机样本数据。

加载数据后,我通常喜欢做的一件事是将 dataframe 列重命名为更具描述性的名称,以便在绘制数据时使用。在这里,我修改了标题以表示实验编号。

for n in range(1,df.columns.shape[0]+1):
    df.rename(columns={f"data{n}": f"Experiment {n}"}, inplace=True)
df.head()

带有编辑过的列名的示例数据。

编辑完列名后,生成一个初始的散点图就非常容易了。首先,我为结果(val)、要绘制的数据名称(names)和要添加到散点图数据点的抖动(xs)创建列表变量。注意:抖动被添加到数值中,以提供将覆盖在箱线图顶部的数据点的分隔。然后,我使用 for 循环来遍历数据帧中不同数据列的范围,以组织绘图所需的数据。

vals, names, xs = [],[],[]for i, col in enumerate(df.columns):
    vals.append(df[col].values)
    names.append(col)
    xs.append(np.random.normal(i + 1, 0.04, df[col].values.shape[0]))  # adds jitter to the data points - can be adjusted

使用这些数据容器来绘制初始的散点图是非常简单的。matplotlib boxplot 函数只需要上面收集的 val 和 names 数据。散点图稍微复杂一点,但是只需要一个带有 python zip 关键字的 for 循环来遍历抖动值、数据点和调色板。

plt.boxplot(vals, labels=names)palette = ['r', 'g', 'b', 'y']for x, val, c in zip(xs, vals, palette):
    plt.scatter(x, val, alpha=0.4, color=c)plt.show()

只需几行简短的 python 代码,就可以生成一个描述结果分布的散点图:

最简风格的箱线图(来源:恰兰·库尼使用 Matplotlib)。

然而,这并不是一个非常漂亮的图形,所以我通常会尝试做一些事情来定制它,使它更有吸引力,希望更具描述性。我相信你们很多人都知道,seaborn 提供了一些主题,可以用来概括你的情节风格。目前,我更喜欢“白色网格”——但这种情况经常改变。此外,boxplot 函数接受多个可自定义的属性参数,以帮助您完善演示文稿。我在下面插入了一些选项,包括调整框的颜色、线条的粗细和中值标记的样式。

我要提醒你的是,分散的箱线图可能会做得过火,所以不要让图表过多,也不要使用太多冲突的颜色。

##### Set style options here #####
sns.set_style("whitegrid")  # "white","dark","darkgrid","ticks"boxprops = dict(linestyle='-', linewidth=1.5, color='#00145A')
flierprops = dict(marker='o', markersize=1,
                  linestyle='none')
whiskerprops = dict(color='#00145A')
capprops = dict(color='#00145A')
medianprops = dict(linewidth=1.5, linestyle='-', color='#01FBEE')

用吸引人的配色方案定制您的图是以引人入胜的方式呈现结果的一个非常重要的方面。你可以在上面的代码片段中看到,我使用十六进制颜色代码来定制属性。虽然 Matplotlib 提供了颜色选项,但我最近开始使用网站https://htmlcolorcodes.com/来高精度地选择我想要的颜色。在这里,我选择了 4 个十六进制颜色代码用于散点图点。

palette = ['#FF2709', '#09FF10', '#0030D7', '#FA70B5']plt.boxplot(vals, labels=names, notch=False, boxprops=boxprops, whiskerprops=whiskerprops,capprops=capprops, flierprops=flierprops, medianprops=medianprops,showmeans=False) 

带有附加样式的箱线图(来源:恰兰·库尼使用 Matplotlib)。

现在我可以给剧情添加一些可选的特性(注:这只是一个演示。在实践中,尽量避免过度填充图表,因为这会降低可读性)。让我们添加一条对应于 y 轴上某点的水平线。这种东西可以用来表示某个阈值或者某个模型的概率分类精度。

plt.xlabel("Categorical", fontweight='normal', fontsize=14)
plt.ylabel("Numerical", fontweight='normal', fontsize=14)sns.despine(bottom=True) # removes right and top axis lines
plt.axhline(y=65, color='#ff3300', linestyle='--', linewidth=1, label='Threshold Value')
plt.legend(bbox_to_anchor=(0.31, 1.06), loc=2, borderaxespad=0., framealpha=1, facecolor ='white', frameon=True)

增加了 x/y 标签、消旋和图例(来源:Ciaran 库尼,使用 Matplotlib)。

现在你有了它,一个快速简单的方法来产生一个分散的箱线图,它可以帮助你的结果向观众展示。下面我将这个功能扩展到同时绘制多个箱线图。

正在策划

有些情况下,单个箱线图不足以传达你想要呈现的结果。也许一个实验有多个条件,或者在几个不同的数据集上评估了几个独立的机器学习分类器。在这种情况下,我们可以使用 matplotlib 的子绘图函数来生成理想的图形。

事实上,实现这一点所需的大部分代码只是我们上面实现的代码的简单复制,但是完成这些步骤仍然是有用的。第一件事是创建第二个数据集。这里,我有效地使用了和以前一样的代码,但是我调整了数据点的范围,这样我们可以在两个图中看到这个效果。

dataset = np.random.default_rng().uniform(50,86,(20,4))
df_1 = pd.DataFrame(dataset, columns=['data1','data2','data3','data4'])
df_1.head()

然后,只需像以前一样完成这些步骤,将数据处理成我们需要的格式。唯一的区别是,现在我们是针对两个数据集进行的。

for n in range(1,df.columns.shape[0]+1):
    df.rename(columns={f"data{n}": f"Experiment {n}"}, inplace=True)
    df_1.rename(columns={f"data{n}": f"Experiment {n}"}, inplace=True)namesA, valsA, xsA = [], [], []
namesB, valsB, xsB = [], [], []for i, col in enumerate(df.columns):
    valsA.append(df[col].values)
    namesA.append(col)
    xsA.append(np.random.normal(i + 1, 0.04, df[col].values.shape[0]))for i, col in enumerate(df_1.columns):
    valsB.append(df_1[col].values)
    namesB.append(col)
    xsB.append(np.random.normal(i + 1, 0.04, df_1[col].values.shape[0]))

有了形状中的数据,我们就可以用两个轴对象创建一个 matplotlib 子图对象。接下来,我们分别在 ax1ax2 上绘制来自两个数据集的方框和数据点。

fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(5, 5))bplot1 = ax1.boxplot(valsA, labels=namesA, notch=False,     showmeans=False)bplot2 = ax2.boxplot(valsB, labels=namesB, notch=False, 
            showmeans=False)palette = ['#33FF3B', '#3379FF', '#FFD633', '#33FFF1']for xA, xB, valA, valB, c in zip(xsA, xsB, valsA, valsB, palette):
    ax1.scatter(xA, valA, alpha=0.4, color=c)
    ax2.scatter(xB, valB, alpha=0.4, color=c)

多个分散的箱线图同时绘制(来源:Ciaran 库尼使用 Matplotlib)。

你可以看到数据已经被正确地绘制出来,稍加努力,你甚至可以看出两者之间的差异。当然,如果我们试图描述两个图之间数据的一些重要方面,添加一点颜色来区分这两个图会很有帮助。下面,我修改了箱线图属性,加入了独特的蓝色和红色方案,以帮助区分数据。

boxpropsA = dict(linestyle='-', linewidth=1, color='#33B3FF')
flierpropsA = dict(marker='o', markersize=10,
                  linestyle='none', markeredgecolor='g')
whiskerpropsA = dict(color='#33B3FF')
cappropsA = dict(color='#33B3FF')
medianpropsA = dict(linewidth=1, linestyle='-', color='#33B3FF')  # colors median lineboxpropsB = dict(linestyle='-', linewidth=1, color='#FF4533')
flierpropsB = dict(marker='o', markersize=10, linestyle='none', markeredgecolor='g')
whiskerpropsB = dict(color='#FF4533')
cappropsB = dict(color='#FF4533')
medianpropsB = dict(linewidth=1, linestyle='-', color='#FF4533')  # colors median line

用颜色区分你的数据(来源:Ciaran 库尼使用 Matplotlib)。

最后,让我们看一两个小的补充,使图表更可读,更有吸引力。和以前一样,我将样式设置为 seaborn whitegrid。然后我使用 for 循环来设置两个支线剧情的某些属性。其中之一是移除内部 x 标签,只留下外部标签。我还设置了一个常见的 y 轴范围和一条指示某个阈值的水平线以及一个 y 轴标签。

sns.set_style("whitegrid")
for ax in fig.get_axes():
    ax.label_outer()
    sns.despine(ax=ax)
    ax.set_ylim(50, 100)
    ax.axhline(y=65, color='#ff3300', linestyle='--', linewidth=1,      label='Threshold')fig.text(0.04, 0.5, 'Classification accuracy (%)', ha='center', va='center', rotation='vertical', fontsize=12)

双图散点图的最终版本(来源:希兰·库尼使用 Matplotlib)。

你可以看到这些增加的效果。特别是,y 轴界限和水平线的结合使绘制的数据分布存在显著差异变得非常明显。如果你将它与第一个双盒图进行比较,你会发现现在已经做出了这些改变,推断信息要容易得多。

用于编写这篇文章的代码可在此处获得:https://github . com/cfcooney/medium _ posts/blob/master/scattered _ box plots . ipynb

我希望它能帮助你们中的一些人更好地展示你们的成果。

在 Windows 平台上调度 Python 脚本

原文:https://towardsdatascience.com/schedule-your-python-scripts-on-windows-platform-4c0b756b81e9?source=collection_archive---------38-----------------------

触手可及的生产力

法比安·阿尔伯特在 Unsplash 上的照片

在开始本教程之前,让我们想一个场景:

您的经理给你打电话,告诉你,“客户希望在 IST 时间每天早上 6 点之前刷新数据。请更改您的轮班时间,并确保客户端批处理完成后脚本执行”。这是什么意思?这意味着,从今天开始,你将开始失去宝贵的睡眠、与家人和朋友在一起的时间,最重要的是,你将在一天中效率最低的时候执行一些随机脚本。

如果以上听起来很熟悉,你想把自己从这样的噩梦中拯救出来,请继续阅读。在本教程中,我们将学习在 Windows 平台上调度 Python 脚本。

首先要做的事情

要实现我们的目标,我们需要三样东西:

  • Python 脚本—对于本教程,我已经编写了一个小的 Python 代码,让从我的 Windows 文件夹位置读取一个“CSV”文件。这个“CSV”文件包含 2 列,每列都有随机数。代码添加两个列以创建一个新的列并且将 CSV 文件的这个新版本保存在相同的文件夹位置。
**#### Importing the required library**
import pandas as pd**#### Reading csv file**
df = pd.read_csv("C:\\Ujjwal\\New_File_V1.csv")**#### Adding 2 columns**
df["Third_Column"] = df["Randome Numbers 1"] + df["Random Numbers 2"]**#### Exporting the data to same location**
df.to_csv("C:\\Ujjwal\\New_File_V2.csv",index = False)
  • 批处理文件 —批处理文件是一个带有“.”的脚本文件。蝙蝠”扩展。这些文件通常保存为简单的文本文件,包含可以在命令行界面(命令提示符)上执行的命令。当命令提示符执行这些文件时,它通读文件中写入的命令,然后逐行执行它们
  • 调度器 —最后但同样重要的是,我们需要一个调度器,它能够读取批处理文件并且在设定的时间执行其中写入的命令。为此,我们将使用 Windows 的任务调度器会派上用场。

准备批处理文件

在上一节中,我们已经分享了我们计划安排的示例 Python 代码。所需的第二个组件是一个批处理文件。出于我们的目的,批处理文件将包含 2 个命令:

  • Python 应用的位置——这是 Python 应用()。exe”扩展名,用于执行脚本。在我们的批处理文件中,我们将提供这个应用程序的位置作为第一个命令。在我的系统上,这个位置如下:
**#### Location of Python Executable**
C:\Users\Ujjwal\Anaconda3\python.exe
  • Python 脚本的位置——这是您想要调度的脚本。我们将提供这个脚本的位置作为批处理文件的第二个命令。鉴于我们正在使用 Python 脚本,请确保文件夹位置中的反斜杠被正斜杠替换。我的脚本在 Windows 中的位置如下。
**#### Windows Location**
C:\Ujjwal\Executable.py**#### Backslash replaced with forward slash**
C:/Ujjwal/Executable.py
  • 最终脚本 —最终的批处理脚本如下所示。在任何 Windows 位置,将该脚本保存在扩展名为. BAT 的文本文件中。
**#### Final Batch Script**
C:\Users\Ujjwal\Anaconda3\python.exe "C:/Ujjwal/Executable.py"

设置时间表

在这一步中,我们将设置计划来定期执行我们的任务。逐步过程如下:

  • 打开 Windows 任务计划程序 —进入 Windows 搜索,搜索任务计划程序,并将其打开。任务计划程序界面如下所示:

任务计划程序界面(图片由作者提供)

  • 创建新任务 —点击右窗格中的创建基本任务,创建新任务。将弹出一个新窗口,提示您填写任务名称和描述。界面将如下所示:

创建基本任务的新窗口(作者图片)

  • 选择周期 —填写任务名称和描述详细信息后,点击下一步。现在,系统会提示您选择触发选项,在这里我们可以设置任务的周期。出于我们的目的,我们选择每日执行。触发器选项的完整列表如下:

计划周期(图片由作者提供)

  • 选择计划时间 —选择任务频率后点击下一步。现在,系统将提示您选择希望触发计划的特定时间。界面屏幕如下所示:

选择计划时间(作者图片)

  • 创建动作 —选择执行时间后点击下一步。在这一步中,调度器将提示您选择想要调度的动作。选择启动程序。界面将如下所示:

选择动作(作者图片)

  • 选择批处理文件 —选择所需动作后点击下一步。现在,应用程序接口将询问您想要调度的节目的位置。选择我们在上一节中创建的批处理文件。应用程序屏幕将如下所示:

选择批处理文件位置(按作者排列的图像)

  • 最后 —就这样,点击下一步,完成。您的 Python 脚本将在每天的特定时间执行。

结束语

我相信有了上面的解决方案,你可以自动完成所有重复的任务。

我希望这篇教程是有启发性的,并且你学到了一些新的东西。请评论并分享您的反馈。

下次请继续关注更多有趣的话题。在此之前:

快乐学习!!!!

Django 和 Heroku 安排的网页抓取

原文:https://towardsdatascience.com/scheduled-web-scraping-with-django-and-heroku-e832e1363c7a?source=collection_archive---------3-----------------------

创建一个 django 应用程序,每天抓取求职公告板

我们经常需要大量的训练数据来进行机器学习,网页抓取可以是获取这些数据的一种方式。

但在过去,有一家公司是我非常想去工作的。他们目前没有数据科学的职位,但他们一有,我就想申请。

解决办法?每天查看他们的工作公告板,这样每当有新工作发布时,我都会得到通知。

让我们构建一个简单的 django 应用程序,部署到 Heroku,并每天创建一个工作公告板。

设置应用程序

为应用程序创建目录,并将cd放入其中。

mkdir jobs && cd jobs

在您喜欢的任何代码编辑器中打开它。我用的是 Sublime。

创建并启动我们的虚拟环境。然后安装我们需要的软件包。

python -m venv env
source env/bin/activate
pip3 install django psycopg2 django-heroku bs4 gunicorn

创建项目(django 版本的 web 应用程序)。

django-admin startproject jobs

cd进入项目,创建一个 app 进行抓取。

cd jobs
django-admin startapp scraping

创建工作模型

我们只需要在这个应用程序中定义一个模型,一个作业模型。这代表我们将收集的工作。

用以下内容覆盖/scraping/models.py

from django.db import models
from django.utils import timezoneclass Job(models.Model):
    url = models.CharField(max_length=250, unique=True)
    title = models.CharField(max_length=250)
    location = models.CharField(max_length=250)
    created_date = models.DateTimeField(default=timezone.now) def __str__(self):
        return self.title class Meta:
        ordering = ['title'] class Admin:
        pass

/scraping/admin.py中注册您的模型。这允许我们在 Django 的默认管理面板中查看记录(我们很快会谈到这一点)。

from scraping.models import Job
admin.site.register(Job)

scraping添加到/jobs/settings.py中已安装的应用中。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'scraping'
]

设置数据库

设置数据库。我喜欢 postgres,所以我们要用它。

此时,确保您之前已经在 mac 上用brew安装了 postgres 并启动了它(这超出了本文的范围)。

在命令行上为此项目创建一个数据库。您可以使用下面的命令打开 postgres 控制台。

psql -d template1

创建用户和数据库,然后退出。

create user django_user;
create database django_jobs owner django_user;
\q

/jobs/settings.py中,更新DATABASES。在其他框架中,您可能希望专门针对开发环境来确定范围,但是在这里我们不会担心这个问题。不管怎样,它在 Heroku 上会起作用的(我们很快就会到达那里)。

注意,这些是我们在上面创建的用户名和数据库名。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'django_jobs',
        'USER': 'django_user',
        'HOST': '',
        'PORT': ''
    }
}

创建迁移,并从命令行迁移数据库。

python manage.py makemigrations
python manage.py migrate

这将创建一个名为scraping_job的表。这是一个 django 名称空间约定,因为它属于scraping应用程序。

现在创建一个超级用户,并在命令行中给它一个密码。

python manage.py createsuperuser --email admin@example.com --username admin

测试当前应用

到目前为止,我们已经做了一些工作,但我们不知道是否有任何工作。我们先测试一下,然后再进一步。

在命令行上。

python manage.py runserver

然后在浏览器中导航至http://127 . 0 . 0 . 1:8000/admin。使用您刚刚创建的超级用户登录。

登录后,点击“抓取”下的“作业”,然后点击右上方的“添加作业”。现在填写一些编造的信息,然后点击“保存”。如果您可以看到您创建的作业,那么到目前为止一切都正常!

自定义 django-admin 命令

我们将设置一个定制的 django-admin 命令来抓取工作板。这是我们将在基础架构级别自动安排的内容,以便实现自动抓取。

/scraping模块中,创建一个名为/management的目录,在/management中创建一个名为/commands的目录。然后在/commands中创建 2 个 python 文件,_private.pyscrape.py

不在此处显示所有内容

将此代码放入scrape.py

from django.core.management.base import BaseCommandfrom urllib.request import urlopen
from bs4 import BeautifulSoup
import jsonfrom scraping.models import Jobclass Command(BaseCommand):
    help = "collect jobs" # define logic of command
    def handle(self, *args, **options): # collect html
        html = urlopen('[https://jobs.lever.co/opencare'](https://jobs.lever.co/opencare')) # convert to soup
        soup = BeautifulSoup(html, 'html.parser') # grab all postings
        postings = soup.find_all("div", class_="posting") for p in postings:
            url = p.find('a', class_='posting-btn-submit')['href']
            title = p.find('h5').text
            location = p.find('span', class_='sort-by-location').text # check if url in db
            try:
                # save in db
                Job.objects.create(
                    url=url,
                    title=title,
                    location=location
                )
                print('%s added' % (title,))
            except:
                print('%s already exists' % (title,)) self.stdout.write( 'job complete' )

将代码放在这样的目录结构中,然后用一个handle函数定义一个Command类,告诉 django 这是一个定制的 django-admin 命令。

在这里,我们使用美丽的汤刮杠杆工作板,然后保存在我们的数据库中。我没有附属机构,但我选择的工作板是为一家名为 Opencare 的出色公司设计的——如果他们发布了什么,你应该申请!

您现在可以从命令行运行它,如下所示。

python manage.py scrape

你会看到这个输出。

再运行一次,你会看到这个。

这是因为我们已经防止在上面的scrape.py代码中添加重复的作业记录。

如果您有一个数据库管理程序设置(如 dbeaver ,您还可以检查数据库中的行。我们不会在这里深入讨论,但它应该看起来像下面这样。

部署到生产

现在让我们把这个放在 Heroku 上。

冻结您的需求,以便 Heroku 知道在部署时安装什么。

pip3 freeze > requirements.txt

在命令行上,运行nano .gitignore并添加如下。

.DS_Store
jobs/__pycache__
scraping/__pycache__

然后ctrl+xyenter保存并关闭(mac 上)。这可以防止部署不必要的文件。

在根目录下创建一个名为Procfile的文件,并将下面的内容粘贴到里面。这告诉 heroku 启动一个 web dyno,第二个命令迁移 db。

web: gunicorn jobs.wsgi
release: python manage.py migrate

文件树将看起来像这样。

确保你有一个 [Heroku](http://heroku create flask-ml-api-123) 账号,然后在命令行上用heroku login登录。

用你想要的任何名字创建一个应用程序。但它需要在 Heroku 上的所有应用程序中是唯一的。我跑了heroku create django-scraper-879,这里的 app 名字是django-scraper-879。但是你需要自己选择。

现在将这几行添加到 settings.py 的最底部。

import django_heroku
django_heroku.settings(locals())

在设置中更新DEBUG。不想在调试模式下部署到 prod。

DEBUG = False

用下面的添加文件到 git。

git init
git add . -A
git commit -m 'first commit'

现在用这个把我们的 app 推给 Heroku。

git push heroku master

计划作业

您可以使用heroku run python manage.py scrape从本地命令行手动运行该作业,但是每天都必须手动运行它会很烦人。

让我们自动化这一点。

登录你的 Heroku 控制台,点击“资源”,然后“查找更多附加组件”。

现在找到并点击这个附加组件。尝试用“ctrl+f”来输入“schedule ”,以帮助找到它。Heroku 有一大堆潜在的附件。看起来像下面。

现在将它添加到您的应用程序中,这样您就有了。

点击它,创建一个作业,everyday at…12am UTC。不必要的 ping 网站是不好的!

输入您的命令并保存。

保存,我们就完成了!

现在只需等到 UTC 时间 12 点(或者你选择的任何时间),你的数据库就会被填充。

结论

我们在这里接触了很多东西。Django,Heroku,日程安排,网页抓取,Postgres。

虽然我用了想知道一家公司何时发布新工作的例子,但是有很多理由你可能想抓取一个网站。

  • 电子商务公司希望监控竞争对手的价格。
  • 高管招聘人员可能想知道公司何时发布职位空缺。
  • 你或我可能想知道 Kaggle 上什么时候增加了一个新的比赛。

本教程只是为了说明什么是可能的。让我知道你是否已经用网络抓取在评论中建立了任何酷的东西。

SCIEM:数据预处理的操作顺序

原文:https://towardsdatascience.com/sciem-an-order-of-operations-for-data-preprocessing-e3af0bf101b8?source=collection_archive---------39-----------------------

通过最小化调试时间来提高数据清理的效率。

CRISP-DM 流程的前 5 个步骤的循环最终看起来有点像上面的时间线。由于预处理可能会占用数据科学过程中的大部分时间,这可能会挤出用于业务理解、建模或评估结果的时间。建模只占项目时间的很小一部分,尽管它通常是人们想到数据科学时首先想到的事情。预处理会带来很大的时间负担,特别是对于大型数据集,或者从不同格式的许多来源编译的数据。

如何最大限度地减少预处理时间,使流程更加高效?

作者图片

在获取和导入数据之后,需要在建模之前进行预处理步骤,否则会导致错误。按照正确的顺序做这些事情可以通过减少错误来加速这个过程。就像数学中的 PEDMAS 一样,数据预处理的步骤有一个自然的顺序。下面是使用 Python 和 Pandas 预处理数据的操作顺序的新缩写。

PEDMAS 之于数学,正如 SCIEM 之于数据科学。

安托万·道特里的照片

  1. 拆分:列车试拆分
  2. 清洁:杂清洁
  3. 输入:输入缺失值
  4. 编码和缩放/标准化/规范化/转换/平衡:对分类数据进行编码;根据需要缩放、标准化和转换数字数据。平衡是指纠正阶级不平衡。
  5. 模型:训练机器学习算法

作者图片

为什么这些步骤会以这种顺序出现?

训练-测试-分割 在以任何方式操作数据之前,最好分割训练和测试数据。通常这是通过 sklearn 来完成的,对于大数据也可以用 Spark 来完成。如果可能的话,最好在查看数据之前就将其拆分。这防止了数据泄露,即来自测试数据的信息被用于训练模型。

清洗 这可能包括多种操作,例如:

  • 删除不适用的列
  • 删除重复项
  • 基于筛选条件删除行
  • 移除异常值
  • 删除错误值
  • 更改数据类型
  • 拆分或组合字符串格式的数据
  • 宁滨(将要素转换成组)
  • 特征聚合
  • 离散化
  • 重采样

如果在编码之前没有进行清理,调试过程将一次又一次地返回到这个步骤来解决错误,直到每个清理任务完成。

输入缺失值 输入缺失值是指用选择的替代值如均值、中值或众数填充 NAN(非数字)值。输入必须在编码前完成,因为编码器不喜欢南的。

One-Hot-Encoding
这需要是最后一步,因为如果您试图对 NAN 值进行编码,或者如果列数据类型不一致,将会导致错误。这里有一个关于 one-hot-encoding标签编码的资源。

作为数据理解的一部分,涉及基于领域知识手动缩小特征范围的特征选择应该在所有这些步骤之前进行。使用算法的特征选择应在预处理后进行。

如果您对预处理的顺序或如何有一个更有效的预处理工作流有任何进一步的见解,请不要犹豫留下评论。还有其他可能影响订单的潜在步骤。SCIEM 是一个可以遵循的通用框架。

参考文献:

  1. Maksym Balatsko, 关于预处理你想知道的一切:数据准备 ,2019,走向数据科学。
  2. Jason Brownlee, 如何用 Scikit 在 Python 中为机器学习准备你的数据——学习 ,2016,机器学习掌握。
  3. Syed Danish, 使用 Scikit 学习 Python 中数据预处理实用指南 ,2016,Analytics Vidhya。
  4. Robert R.F. DeFilippi, 用 Python 为数据科学清理和准备数据—最佳实践和有用的包 ,2018,中。
  5. 萨尔瓦多·加西亚,朱利安·卢恩戈和弗朗西斯科·埃雷拉,《数据挖掘中的数据预处理 ,2015,施普林格。
  6. Tarun Gupta,Python 中的数据预处理 2019,走向数据科学。
  7. Rohan Gupta,面向数据科学的数据科学家离散化技术介绍,2019。
  8. Ihab 易勒雅斯储旭数据清理,2019,计算机械协会。
  9. 问:伊森·麦卡勒姆(Ethan Mccallum), 《不良数据手册:清理数据,以便你可以重新开始工作》 ,2012 年,奥赖利。
  10. Jason Osborne, 数据清理最佳实践:收集数据前后需要做的所有事情的完整指南 ,2012 年,SAGE 出版社。
  11. Pranjal Pandey, 数据预处理:概念,2019 ,走向数据科学

数据科学——一个“古老”的故事

原文:https://towardsdatascience.com/science-of-data-an-old-story-d9db5e6cb00?source=collection_archive---------51-----------------------

为数据科学增添视角

Johannes Plenio 在 Unsplash 上拍摄的照片

我们(智人)与其他动物物种的主要区别之一是我们能够 想象识别模式 并基于这种理解做出 可操作的决策 。这帮助我们度过了最艰难的环境,征服了这个星球。我们通过有效地“处理”我们可用的“数据来做到这一点。在早期,人类通过识别掠食者攻击他们的方式衍生出新的生存策略。我们的祖先以此为“训练数据”,发现了躲避捕食者攻击甚至反击的方法。同样,他们从自然过程中观察数据,如“天气变化”、“草药的药性”、“用火烹饪”等,并获得可操作的见解,以提升进化链。因此,数据总是以某种形式存在。人类一直在利用数据造福人类。“数据科学”可能是今天的流行语,但人类从史前时代就开始使用这种数据科学。事实上,数据科学让我们达到了今天的水平。用简单明了的英语来说,如果你需要定义“数据科学”,它的意思无非是“利用数据得出可操作的见解以使事情变得更好的科学”。

在现代,“让事情变得更好”可能意味着“以更好的方式购物”、“更好地了解客户的行为”、“提出更好的建议”、“预测结果”等等。使用数据并在其上应用数据科学原理,有助于我们实现所有这一切,甚至更多。

为了对任何领域或概念建立牢固的理解,我们应该努力掌握该领域中使用的基本术语的含义。在数据科学领域,以下 3 个术语被过度使用:统计学、数据挖掘和机器学习。

M. B. M.Unsplash 上拍摄的照片

统计:

比方说,你经营一家杂货店,在那里销售一些产品。为了更好地了解你的企业是如何运作的,你记录下每天售出的产品数量。因为你有销售数据,你可以找出你每月的总销售额,比别人卖得多的前 10 个产品的列表,顶级客户的列表,一周的平均销售额或某一天的平均销售额等等。这些测量会让你知道你的生意做得怎么样。这就是简单统计学的意义。

统计学就是收集、组织、总结、分析和解释数据的科学。

让我们用一个例子来理解它。 UCI 机器学习库提供了一个数据集,其中包含一家总部位于英国的注册无店铺在线零售商在 2010 年 12 月 1 日至 2011 年 12 月 9 日之间发生的销售交易。(根据 UCI 机器学习知识库,这些数据是由公共分析小组主任陈大庆博士提供的。chend '@' lsbu.ac.uk,伦敦南岸大学工程学院,伦敦 SE1 0AA,英国。)

使用 统计 方法可以回答的一些有趣问题有:

  • 完成交易的独立客户总数是多少?
  • 订单数量最多的前 10 名客户是谁?
  • 消费最高的前 10 名客户是谁?
  • 每年每个月的平均销售额是多少?

这些问题的答案可能会让你对业务有所了解

Python 3/Jupyter Notebook 已经用于探索数据和回答上述问题。(最后已经提供了完整笔记本的 github 链接。然而,我们将在下面详细讨论代码的每一部分。)

让我们导入所需的 Python 库并将数据加载到 Pandas 数据框架中

***#Import data
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns 
color = sns.color_palette()** **df_retail_sales = pd.read_csv(‘./input/data.csv’,encoding="ISO-8859–1")***

检查关于该数据帧的信息

***df_retail_sales.info()***

输出是

数据由 8 个属性组成,定义如下:

InvoiceNo: 代表唯一的订单号或交易号。
库存代码:代表已售出产品的唯一产品代码。
描述:描述已售出的产品
数量:其在交易中售出的产品数量
发票日期:交易日期
单价:其售出的 1 件产品的售价
客户 ID: 其代表进行交易的客户
国家:国家名称

让我们执行基本的数据清理任务——删除属性为空值的行,删除数量列中为负值的行(负值对数量列没有任何意义)

***df_retail_sales['InvoiceDate'] = pd.to_datetime(df.InvoiceDate, format='%m/%d/%Y %H:%M')****df_retail_sales = df_retail_sales[df_retail_sales.Quantity > 0]****retail_sales = df_retail_sales.dropna()***

现在让我们做一些基本的 特征工程——这是一种有趣的说法,即我们在数据中添加一个新列或修改一个现有列,这样会更有用。例如,在我们的例子中,我们可以添加一个新列“amount spend”,它将是数量单价的乘积。我们还应该为添加单独的列,这些列可以从 InvoiceDate 派生。这些将有助于我们计算年、月和日销售额的数据。(记住…..这就是基本统计学!)

***retail_sales['AmountSpent'] = retail_sales['Quantity'] * retail_sales['UnitPrice']****retail_sales.insert(loc=6, column='YearMonth', value=retail_sales['InvoiceDate'].map(lambda x: 100*x.year + x.month))****retail_sales.insert(loc=7, column='Month', value=retail_sales.InvoiceDate.dt.month)****retail_sales.insert(loc=8, column='Day', value=(retail_sales.InvoiceDate.dt.dayofweek)+1)****retail_sales.insert(loc=9, column='Hour', value=retail_sales.InvoiceDate.dt.hour)***

我们的新数据框架“retail_sales”如下所示:

***retail_sales.info()***

现在让我们试着回答我们的问题。

完成交易的唯一客户总数是多少?

***retail_sales['CustomerID'].nunique()***

4339

订单数量最多的前 10 名客户是谁?

***orders = retail_sales.groupby(by=['CustomerID'], as_index=False)['InvoiceNo'].count()****orders.rename(columns={'InvoiceNo': 'Cnt_Invoices'}, inplace=True)****orders=orders.sort_values(by='Cnt_Invoices', ascending=False).head(10)****orders.plot(x="CustomerID", y=["Cnt_Invoices"], kind="bar")***

按订单数量排名的前 10 名客户

消费最高的前 10 名客户是谁?

***spending = retail_sales.groupby(by=['CustomerID'], as_index=False)['AmountSpent'].sum()****spending.sort_values(by='AmountSpent', ascending=False).head(10)****spending.plot(x="CustomerID", y=["AmountSpent"], kind="bar")***

按消费金额排名的前 10 名客户

每个月的平均销售额是多少?

***ax = retail_sales.groupby(‘YearMonth’)
[‘AmountSpent’].mean().sort_index().plot(‘bar’,color=color[1],figsize=(15,6))****ax.set_xlabel(‘Month’,fontsize=10)****ax.set_ylabel(‘Avg Sales’,fontsize=10)****ax.set_title(‘Sales (01-Dec 2010–09-Dec-2011)’,fontsize=14)****ax.set_xticklabels((‘Dec10’,’Jan11',’Feb11',’Mar11',’Apr11',’May11',’Jun11',’July11',’Aug11',’Sep11',’Oct11',’Nov11',’Dec11'), rotation=’horizontal’, fontsize=11)****plt.show()***

每月平均销售额

以上是一些基本统计如何帮助理解和分析数据集的例子。更进一步,我们还可以通过研究各种属性的分布,找出它们之间的关系,从数据中发现有用的信息。

伊万·班杜拉在 Unsplash 上的照片

数据挖掘:

以零售销售数据为例,我们可以发现:

  1. 忠诚的客户(经常购物)
  2. 休眠客户(不经常购物)
  3. 每个客户最早和最晚的订单日期。
  4. 特定人群在一年中的特定日子购买的一组产品。

数据挖掘是在大型数据仓库中自动发现有用信息的过程。

让我们为客户构建一个新的数据框架,并尝试找到一些此类信息

*customer_data=retail_sales.groupby(‘CustomerID’).agg(
 # Get count of orders column for each customer
 TotalOrders=(‘InvoiceNo’, ‘count’),
 # Get total amount spent for each customer
 TotalSpending=(‘AmountSpent’, ‘sum’),
 # Get earliest order date for each customer
 EarliestInvoice=(‘InvoiceDate’, ‘min’),
 # Get latest order date for each customer 
 LatestInvoice=(‘InvoiceDate’, ‘max’)
)customer_data.head()*

两个新属性:最早发票最新发票可用于确定客户是忠诚的还是休眠的。如果这两个日期的差异很小,则顾客频繁购物,否则他处于休眠状态。

“总支出”列可用于挖掘大买家的信息。该信息可以与库存代码(产品)的计数相结合,以找出哪些产品销售更频繁。

Clarisse CrosetUnsplash 上拍摄的照片

机器学习:

比方说,您的客户向您提供他们购买的反馈。为了从这种反馈中获得可操作的见解,你编写一个计算机程序,将它作为输入,并给你一个客户对你的产品的感觉或看法的分数。这些观点可以帮助你了解客户的需求,你可以采取一些措施来满足客户。随着不同客户的购买倾向于增长,新的反馈不断出现,你的程序在感知客户情绪方面变得更好。这是机器学习。

机器学习是计算机科学和人工智能的一个分支,旨在开发从数据中学习的系统,并在学习的基础上帮助做出决策和预测。

简单明了地说, 统计 技术被用来训练你的机器(或者你的电脑程序)去学习,从而帮助你挖掘你的数据,发现有用的模式和可操作的见解。

所讨论的示例代码可从 Github 获得

从 A 到 Z 用 NumPy 进行科学计算

原文:https://towardsdatascience.com/scientific-computing-with-python-daaaaddfa122?source=collection_archive---------47-----------------------

数据科学

关于 Numpy 你需要知道的

背景由乔·塞拉斯Unsplash

Numpy数值 PYthon 是 python 最强大的数学库之一。它使我们能够高效和有效地进行计算。由于其惊人的能力,它比普通的 python 更好。****

  • numpy 模块主要基于ndarray类,它允许面向向量的编程
  • ndarray类允许实例化多维向量并用本地 python 指令处理它们,以简化科学计算。
  • 该模块完全是用 C 语言编写的调用函数的执行不通过虚拟机(就像 Python 的情况一样),这允许我们进行高性能的密集计算。****
  • 提供两种外观,一种是过程外观(函数和变量),另一种是面向对象外观(类、属性、方法、特性……)。

认识“ndarray”:

实例化 ndarray:

可以调用类 a ndarray的构造函数来构造 n 维向量的实例。主要参数是:

  • 形状:正整数元组,表示每个维度的元素数量****
  • dtype: 表示数组元素的类型。因此,ndarray 是包含相同类型值的 n 维向量****

重要提示:你也可以使用numpy.ndarray创建一个数组,但这不是推荐的方式(数组的构造应该如下段所示)。

****import numpy as np
np.ndarray((4,2), np.int)****

输出结果: Google Colab

****a = np.ndarray(shape= (4,2), dtype = np.int)****

输出结果: Google Colab

ndarray 的功能:

生成 numpy 数组:

np.array:这个函数允许你创建并从 python iterable 初始化一个多维向量并返回np.ndarray

  1. 一维 向量构造:
****a = np.array([1,2,3, 3+3j])** 
*# L =[1,2,3, 3+3j]
# a = np.array(L)***

输出结果: Google Colab

2.矩阵构造:

现在,参数是一个列表的列表,其中子列表具有相同的大小表示结果矩阵的行

**m = np.array([[1,2,5], [3,2,5], [0, 3, 8]])**
*#L=[[1,2,5],
#  [3,2,5],
#  [0,3,8]]
#m = np.array(L)*

输出结果: Google Colab

3.三维矢量构造:

列表的列表的列表…

**a3 = np.array([[[1,2],[2,0]], [[0,1],[0,5]]])**

输出结果: Google Colab

离散化函数:

离散化是我们将连续数据转换成离散形式的过程。这些函数返回一个一维向量,该向量是将一个区间(连续的)划分为一组有限的点而得到的。

  1. 功能:“arange”:

返回给定间隔内间隔均匀的值。

**np.arange(5)**# *a vector of integers ranging from 0 to 4\. (the upper bound is excluded)***np.arange(0,1, 0.2)** *#division of the interval [0,1 [according to step 0.2*
**np.arange(1, 0, -0.1)** *#Negative step*

输出结果: Google Colab

numpy.arangerange python 内置功能有什么区别?

Python 内置函数“range”只接受整数作为参数。

**range(0, 1, 0.2)** *#Error*

输出结果: Google Colab

2.功能:“linspace”😗***

返回指定间隔内等间距的数字。

**a = np.linspace(0,1)** *#by default divides the interval into 50 equidistant points*
**b = np.linspace(0, 1, num = 10)**

输出结果: Google Colab

3.功能:“日志空间”😗***

返回在对数刻度上均匀分布的数字(默认情况下以 10 为基数)。

**np.logspace(0,1)**

输出结果: Google Colab

齐次 n 维向量:

  1. 功能‘0&1】:

返回给定形状和类型的新数组,用零填充。

**np.zeros((2,3), dtype = np.bool)
np.zeros(shape = (10,))**
**np.ones((2,5,2))** *#3D*
**np.ones((3,3), dtype = np.complex)**

输出结果: Google Colab

2.功能:“满”😗***

返回给定形状和类型的新数组,用相同的值填充。

*#np.full(shape****,*** *fill_value****,*** *dtype=None****)* np.full((5,5), 3, np.int)
np.full((2,4), fill_value=3+2j, dtype=np.complex)**

输出结果: Google Colab

我们可以用np.ones作为np.full的等价物。

**np.ones((2,4)) * (3+2j)
np.full((2,4),(3+2j))**

输出结果: Google Colab

矩阵函数:

  1. 单位矩阵:

线性代数中,大小为 n 的单位矩阵就是主对角线上有 1,其他地方有 0 的 n × n 方阵矩阵

**np.identity(5)** #I5
**np.identity(5, dtype = np.int)**
**np.identity(4, dtype = np.bool)**

输出结果: Google Colab

2.功能‘眼’😗***

返回一个对角线上为 1,其他地方为 0 的二维数组。

*#**np.eye****(****N****,*** *M=None****,*** *k=0****,*** *dtype=<class 'float'>)* #**N** : *int :*Number of rows in the output.
#**M** : *int, optional :*Number of columns in the output. If None, #defaults to *N*.
#**k** : *int, optional :*Index of the diagonal**np.eye(3)** *#np.identity(3)*
**np.eye(3,3,-1)** *#fill the first diagonal below the main diagonal with ones.*
**np.eye(3,3,1)** *#fill the first diagonal above the main diagonal with ones.*
**np.eye(5,10, 3, dtype = np.int)**

输出结果: Google Colab

3.功能‘诊断’😗***

diag 函数有两个参数:

  • ndarray
  • 整数 k(默认值= 0)。如果‘v’的维数是 1,则该函数构造一个矩阵,其中它的对角线数 k 由向量‘v’的元素构成。如果 a 是一个矩阵(维数为 2 ),那么该函数提取一维向量中第 k 条对角线的元素。

提取一条对角线或构造一个对角线数组。

**np.diag([1,5,7])
a = np.ones((3,3)) * 5
np.diag(a)**

输出结果: Google Colab

3.函数‘from Function’😗***

通过对每个坐标执行一个函数来构造一个数组。
让我们创建一个基于其索引的向量。

#*numpy.fromfunction****(****function****,*** *shape)*
**np.fromfunction(lambda i,j : i-j, (3,3))
np.fromfunction(lambda i, j : i == j, (5,5))**

输出结果: Google Colab

ndarray和原生 python 迭代器有什么区别(和优势)?

******

输出结果: Google Colab** ****

输出结果: Google Colab

ndarray支持本地 Python 操作符(+、-、 …),以及 numpy 模块中可用的一组“矢量化”数学函数(numpy.cosenumpy.sinanumpy.exp …)。*

4.功能“矢量化”。****

numpy.vectorize的目的是将不支持 numpy 的函数转换成可以操作(并返回)numpy 数组的函数

让我们构建一个函数sign,允许我们计算一个值的符号。

如果你对在 Jupyter 笔记本、Google Colab 或任何其他支持 LaTeX 数学写作的交互式笔记本中用 LaTex 写数学感兴趣,请查看这篇文章。

def sign(x):
   if x == 0:
      return 0
   elif x > 0:
      return 1
   else:
      return -1

输出结果: Google Colab

vectorized_sign = np.vectorize(sign)

输出结果: Google Colab

再比如:

我们来构建一个函数xlogx

如果你对在 Jupyter 笔记本、Google Colab 或任何其他支持 LaTeX 数学写作的交互式笔记本中用 LaTex 写数学感兴趣,请查看这篇文章。

import math
def xlogx(x):
   if x==0:
      return x
   else:
      return x * math.log(x)

输出结果: Google Colab

ndarrays 的“有趣”特性:

  • 属性(只读),告诉我们向量元素的类型。

输出结果: Google Colab

  • numpy.size返回数组中元素的个数。

输出结果: Google Colab

  • numpy.ndim返回数组维数。

输出结果: Google Colab

  • numpy.shape包含每个维度的元素数量的元组。该属性是可读/写的。

提醒: 元组元素的乘积应该等于数组的大小。

⟹形状属性允许你改变向量的形状,但不能改变它的总大小!!!

a.shape = (5,4)
a.shape = (10,2)

输出结果: Google Colab

两个不变关系:

如果你对在 Jupyter 笔记本、Google Colab 或任何其他支持 LaTeX 数学写作的交互式笔记本中用 LaTex 写数学感兴趣,请查看这篇文章

使用复杂系统:

**z = np.array([[1, 3+3j],[2+1j,3+5j],[5j,5]])**
**z.real** *#Return the real part of the complex argument.*
**z.imag** *#Return the imaginary part of the complex argument.* **np.conj(z)** *#Return the complex conjugate.*

输出结果: Google Colab

ndarray 的一些预定义方法:

ndarray.trace:

trace方法:允许计算尺寸为> 1ndarray的轨迹。
提醒:线性代数中,方阵 A 的迹(常缩写为 tr)定义为主对角线上元素的和。

数组维数应大于 1

输出结果: Google Colab

ndarray.reshape:

它返回包含新形式的源数组的ndarray类的新实例。

a = np.arange(10)
b = a.reshape((2,5))

输出结果: Google Colab

reshape返回的向量不是初始向量的独立副本,而是与源共享相同的内存大小写。

输出结果: Google Colab

如何创建的独立副本?

我们使用.copy()方法。

**c = a.reshape((2,5)).copy()***#create an independent copy of a*

ndarray.dot

点积的计算(对于一维向量)。维数≥ 2 的向量的矩阵乘法。

v1 = np.array([1,2,3,4])
v2 = np.array([1,0,1,0])
v1.dot(v2) #dot productm = np.diag([1,2,3,4])
n = np.ones((4,4))
m.dot(n) #Matrix multiplication

输出结果: Google Colab

np.cross

返回两个向量(数组)的叉积。

v1 = np.array([1,2,3])
v2 = np.array([1,0,1])v3 = np.cross(v1,v2)
v3.dot(v1)

输出结果: Google Colab

ndarray.astype

允许创建一个新实例,该实例包含与源 vector 相同的值,但被转换为参数指示的类型。

输出结果: Google Colab

聚合方法:

  • ndarray.sum:返回给定轴上数组元素的和。
  • ndarray.sum:返回给定轴上数组元素的乘积。
  • ndarray.max:返回给定轴的最大值。
  • ndarray.min:返回给定轴的最小值。
  • ndarray.mean:返回给定轴上数组元素的平均值。
  • ndarray.cumsum:返回给定轴上元素的累积和。
  • ndarray.cumprod:返回给定轴上元素的累积积。
  • ndarray.var:返回数组元素沿给定轴的方差。
  • ndarray.std:返回数组元素沿给定轴的标准偏差。
  • ndarray.argmin:返回给定轴上最小值的索引。
  • ndarray.argmax:返回给定轴上最大值的索引。

****重要提示:列→轴=0。行→轴=1

a = np.array([[10, 11, 11], [20, 12, 21], [30, 22, 22], [40, 32, 23] , [50, 24, 24]])

输出结果: Google Colab

ndarrays 上的操作:广播

Numpy 解释形式为a 操作符 b的操作,其中a和/或b是类darray的实例,如下所示:具有最小尺寸的向量在最大向量中广播。让我们从例子开始:

B = np.arange(1,4)
A = np.array([Agenerator + i for i in range(10,31,10)])#or we can simply create them with np.array
#A = np.array([ [11, 12, 13], [21, 22, 23], [31, 32, 33] ])
# B = np.array([1, 2, 3])

输出结果: Google Colab

与广播相乘:

这张图说明了广播的工作方式:来源

输出结果: Google Colab

另一个例子:

来源

b = np.arange(1,4)
a = (b * 10).reshape((3,1))

输出结果: Google Colab

广播规则:

  • Numpy 用左边的 1 填充较小维度的向量的形状,以便有两个相同维度的向量。
  • Numpy 从右边开始比较 a.shape 和 b.shape 中的整数对(index -1) →较小的公维。
  • 二维 a.shape [i]和 b.shape [i]是相容的,如果:

如果你对在 Jupyter 笔记本、Google Colab 或任何其他支持 LaTeX 数学写作的交互式笔记本中用 LaTex 写数学感兴趣,请查看这篇文章。

索引数组:

简单索引:

索引数≤维度数。

  • 1D :
a = np.arange(5)
print("a[0]= ", a[0]) #first element
print("a[-1]= ", a[-1]) #last element
print("a[a.size]= ", a[a.size]) #error

输出结果: Google Colab

  • 2D :

输出结果: Google Colab

切片:

**#A[start:stop-1:step]**a = np.arange(5)
print(a[0:3])
print(a[::-1]) #reverse the order of the elements of a.

1D 切片。输出结果: Google Colab

多维切片。输出结果: Google Colab

****示例:

如果你对在 Jupyter 笔记本、Google Colab 或任何其他支持 LaTeX 数学写作的交互式笔记本中用 LaTex 写数学感兴趣,请查看这篇文章。

  1. 创建a
  2. 应用切片从a中提取深绿色标记的部分。

a = np.fromfunction(lambda i, j : (i+1)*10 + (j+1), (5,5))
a[:3, 2:]

a[3:]

****注:更多例子请看 Google Colab 笔记本

屏蔽:

当您希望根据某种标准提取、修改、计算或操作数组中的值时,就会出现屏蔽。让我们生成两个数组并使用布尔掩码。

a = np.arange(25).reshape((5,-1)) # -1 >> python will automatically calculate ( or simply type .reshape((5,5))
c = a%2b = c == 0 #b is a boolean vector it can be used as a mask for the vector a
a[b] #gives the values which are overlayed with the elements which are True in b

输出结果: Google Colab

我们可以使用~not广播到数组元素中。

  • ~不是
  • &
  • |
  • %mod

输出结果: Google Colab

线性代数

进口

from numpy import linalg as alg#Let's generate some arrays:
d1 = np.diag([1,2,3])
d2 = np.array([[1,0,0],[2,0,0],[3,0,0] ])
d3 = np.array([[0,0,1], [0,1,0], [1,0,0]])

功能

  1. 函数' det': 计算一个数组的行列式。
alg.det(d1)
alg.det(d2)

输出结果: Google Colab

2.函数' matrix_rank'😗* 计算一个数组的秩。**

alg.matrix_rank(d1)
alg.matrix_rank(d3)

输出结果: Google Colab

3.函数‘inv’😗* 计算一个数组的逆矩阵。**

print(d1)
print(alg.inv(d1))

输出结果: Google Colab

4.函数‘matrix _ power’😗* 将方阵提升到(整数)次幂n。**

alg.matrix_power(d1,2)
alg.matrix_power(d1,-1) # >> equivalent of alg.inv(d1)

输出结果: Google Colab

5.函数‘eigvals’😗* 计算一个数组的特征值**

alg.eigvals(d1)

输出结果: Google Colab

6.函数‘EIG’😗*
计算一个方阵的特征值和右特征向量。**

print(d1)
eigVal, eigVect = alg.eig(d1)

输出结果: Google Colab

7.函数‘求解’😗* 用两种不同的方法求解下列线性方程组:**

如果你对在 Jupyter 笔记本、Google Colab 或任何其他支持 LaTeX 数学写作的交互式笔记本中用 LaTex 写数学感兴趣,请查看这篇文章。

#First method (using alg.solve):
x = alg.solve(a,b)#Second method (using alg.inv):
inv_a = alg.inv(a)
x = inv_a.dot(b)

输出结果: Google Colab

资源:

感谢阅读!😄

**

查看我的其他文章,关注我的 中型**

哈利菲艾哈迈德阿齐兹

带 Lambda 的科学 Python

原文:https://towardsdatascience.com/scientific-python-with-lambda-b207b1ddfcd1?source=collection_archive---------16-----------------------

Python Lambda 函数的正确用法:Python 科学编程的最佳语法。

(图片由作者提供)

介绍

在过去的十年里,Python 编程语言已经进入了科学计算领域的许多人的脑海。这是有意义的,因为 Python 是一种动态的、易于理解的编程语言,其背后有一个重要的生态系统。尽管 Python 有能力担当这个角色,但是,Python 并不是为科学计算甚至统计而设计的。因此,与其他科学编程语言相比,Python 的语法往往有很大不同。

虽然有很多例子表明 Python 在语法上可能不是应用数学的好语言,但是 Python 确实有一些有趣和令人兴奋的技巧来抵消这种天生的劣势。这方面最酷的函数之一是λ函数。

Julia 编程语言让我喜欢的一点是语法表达式的使用。在 Julia 语言中使用这些表达式允许您在一行中非常容易地将不同的类型分派给不同的操作。这使得数学表达式非常简单,写起来就像在论文里一样。举例来说,这对于微分方程,尤其是积分方程来说非常有用。虽然在我看来,Julia 的实现当然更好,但 Python 中的 lambda 函数可以被认为是一个等价的函数。如果您也想了解 Julia 的实现,这里有一篇关于它的文章:

[## 如何在 Julia 中使用语法表达式和调度

朱莉娅的面包和黄油:句法表达和多重调度

towardsdatascience.com](/how-to-use-syntactical-expressions-and-dispatch-in-julia-67ce155e4af9)

lambda 是什么意思?

lambda 函数是一个匿名函数,它将 Pythonic 表达式作为参数。将 lambda 视为应用程序是一个很好的方式。每当使用 lambda 时,它通常意味着我们希望将这个表达式应用于这个构造的类型。Lambda 还可以用来映射数据,在 for 循环中非常有价值。

在 Python 中,lambda 函数用于创建 Python 表达式,无需元解析。这很好,因为这也意味着我们避免了通常与执行随机代码行相关的危险——尤其是在 Python 中。然而,这个表达式也可以返回到一个变量,并在运行时环境中定义为一个函数。

使用λ

lambda 函数的用法类似于字典键。将键视为提供的变量或参数,将键下的数据视为调用键时要计算的表达式。

例如,让我们考虑 f(x) = x + 5:

x = [5,10,15]f = lambda x : [z + 5 for z in x]

我们想用 x 评估的表达式是[z + 5 for z in x].在这个循环中,我们简单地给列表 x 的每个元素加 5。每当我们把 x 放在冒号 lambda x :之前,我们就声明这些是将被提供来执行表达式的参数。最后,我们设置 f 等于 lambda 函数的返回——这将是一个接受参数 x 并执行算术[z + 5 for z in x]的函数。也就是说,f 现在被定义为这个函数:

print(f(x))
[10, 15, 20]

此外,我们可以创建一个带有多个参数的 lambda 函数。下面的函数将两个列表按元素相加:

f = lambda x, y : [w + i for w, i in zip(x, y)]

(图片由作者提供)

调度/λ

最后,在与我最喜欢的编程语言的比较中,Python 的 lambda 函数本质上扮演了与 dispatch 相同的角色,在一行代码中快速创建函数。尽管如此,我认为在代码之间画一个快速的比较可能是令人兴奋的。让我们使用两种语言中的两种方法创建一个正态分布,看看哪种方法更有效:

计算机编程语言

norm = lambda x : [i = (i-mean(x)) / std(x) for i in xt]norm(x)

朱莉娅

norm(x) = [i = (i-mean(x)) / std(x) for i in xt]norm(x)

从这两个例子中可以清楚地看出,这种方法在两种语言中的应用几乎没有什么不同。然而,Julia 版本的一个显著优点是,类型也可以被不同地分派和计算,例如,如果我们要通过 Julia 中的这个函数传递一个整数,我们会得到一个错误,因为我们不能遍历整数。对于 Python 来说也是如此。这两种语言的不同之处在于,当 x 的类型不是数组时,Julia 能够运行另一个完全独立的函数:

norm(x::Array) = [i = (i-mean(x)) / std(x) for i in xt]
norm(x::Int64) = (x - x) / std(x)

结论

尽管 Python 并不是一门在创建时就必须考虑科学计算的语言,但它确实非常适合这个角色。这种语言通过实现一些工具来做到这一点,这些工具使得利用语言内部的科学和数学语法变得更加容易。虽然 Python 的版本可能没有 Julian 实现好,但它表明 Python 是一种强大而通用的语言,可以在科学计算领域超越大多数其他语言。此外,lambda 是一个很好的工具,无论你是不是科学家,在用 Python 编程时牢牢掌握它是一件很好的事情。

posted @ 2024-10-16 09:01  绝不原创的飞龙  阅读(190)  评论(0)    收藏  举报