TowardsDataScience 博客中文翻译 2022(一百六十六)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

使用线性规划的车队和劳动力计划

原文:https://towardsdatascience.com/fleet-and-workforce-planning-with-linear-programming-16db08c7f91d

线性编程解决一些商业问题的能力预览

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pop &斑马Unsplash 上拍照

B 企业(应该)花费大量的时间和精力来预测他们的产品/服务的需求,或者完成不同任务所需的精力/资源,以使他们的回报最大化,同时避免两种不希望的结果:

  • 缺乏资源或工人来应对需求,这可能导致流程失败或销售和客户损失;
  • 有太多未使用的资源(库存过多)或失业工人,这意味着储存货物的额外成本和员工的机会成本(也许他们可以被分配到其他任务)。

假设一家公司已经成功开发了产品/服务需求的准确预测模型,那么下一步可能是组织产品补给背后的物流,并根据未来需求分配任务。例如,这可能意味着找到以下问题的答案:

i. 从哪个仓库向我的每个商店发送预计在下周/下个月内售出的产品“P”件比较便宜?

二。考虑到每个仓库的可用单位数量,我如何才能做到这一点?

**三。**可利用的交通资源如何?我如何利用现有的货车/卡车将库存箱子从仓库运到商店?考虑到每辆货车运输的箱子数量有限,哪种选择成本更低?

**四。**考虑到我的一些员工在上周的国际象棋锦标赛中打架,如果他们被分配到同一辆车上,可能会再次打架,我如何将可用劳动力分配到每辆货车?

接下来的问题是,如何回答这些看起来非常具体且性质不同的问题?嗯,我们需要一个框架,允许我们从头开始构建一个完全可定制的解决方案,在这个框架中,我们可以指定我们的目标和现实世界的约束,这些约束定义了这个目标的可行结果。把现实生活中的问题写成方程组怎么样?

在本文中,我们将模拟这些场景,并使用一种称为线性规划的数学建模方法,对提出的 4 个问题给出一个简单的解决方案。虽然不在数据科学的聚光灯下,但这是一种著名的优化技术/框架,用于求解线性方程组,该方程组由一个目标函数(我们希望最大化/最小化什么,例如最大化利润同时最小化成本)和一组线性约束组成,这些约束指定了我们需要满足的不同条件,以便获得可行的解决方案。

基本上,我们用线性术语写下我们想要解决的问题,指定目标和约束,然后使用优化算法(求解器)以智能和有效的方式浏览数千或数百万个可行和不可行的结果,使我们至少接近最优解。但是,我们所说的最优是什么意思呢?在这个框架中,最优意味着构成最终解决方案的参数的最佳可行配置。请注意,由于这个原因,“最佳”可能指的是完全不同的东西,这取决于企业的偏好/规则。例如,对一个公司来说,满足客户的需求可能比节省运输成本更重要,而对其他公司来说可能不是这样。最后,这一切都归结于业务目标及其约束的正确规范。

一个问题,一个框架,一个解决方案

为了回答前面的问题,我们先介绍一个虚构的场景。上下文将保持简单,因为本文的主要目的只是介绍框架以及我们如何使用它通过开发定制解决方案来解决现实生活中的问题。在开始之前,我想澄清一下,从这里开始出现的图像/等式/代码块都是来自我个人的阐述。好了,我们直奔主题吧。

1.上下文和示例结果

假设你拥有一家公司,拥有一个目录,其中有 5 种产品( P=5 )、 3 个仓库( W=3 )、3 个商店( S=3 )、4 辆货车( V=4 )用于运输产品,以及 8 名员工( E=8 ),这些员工可以被分配到每辆货车上(成对)正如开始提到的,你已经对预期需求有了一个估计。此外,我们将假设由于你已经估计了需求,你也储存了足够的库存来满足它,因此知道你在每个仓库有多少库存。

为了简化问题,我们不考虑产品的单位,而是讨论产品的箱数,即产品的需求和库存是以产品的箱数来衡量的(在现实生活中,在预测以箱/包形式交付的产品需求后,我们需要将原始数字转换为所需的箱数或分组单位数,因为这是运输它们的方式)。

接下来,我们假设您也知道使用任何货车将每种产品的一箱从您的任何仓库运送到您的任何商店的成本。除了可变成本之外,每辆车的使用成本也是固定的(你可以把它看作折旧/维护成本)。

此外,我们假设每辆货车可以行驶的次数是有限的。在这种情况下,限制是 5 次旅行。此外,每辆货车都有一个默认的箱子数量。最重要的是,每辆车(如果使用的话)都需要两人一组(一名司机和一名助手)来操作。此外,我们只能给每个员工分配一辆货车,如果我们这样做,他将获得 1500 美元的固定工资。

最后,在上周举行的国际象棋锦标赛期间,一些员工发生了争吵,因此希望避免将两个有冲突的工人分配到同一辆货车上;事实上,我们有 23 对相互冲突的员工( J=23 )。如果我们最终把他们分配到同一辆货车上,我们将不得不处理后果,即罚款 500 美元。

总之,我们的上下文变量如下:

  • “P”产品= 5
  • “W”仓库= 3
  • “S”商店= 3
  • “V”型货车= 4 辆
  • “E”员工= 8 人
  • “J”对相互冲突的雇员= 23

这些假设是:

  • 我们知道每个商店对每种产品的需求;
  • 我们知道每个仓库里有多少箱我们的产品;
  • 所有的盒子都有相同的大小(为了简化问题);
  • 我们知道每辆车能装多少箱子;
  • 根据产品-仓库-商店-货车的组合,发送一个箱子的成本不同;
  • 每辆货车的使用都有固定成本;
  • 没有运输至少 1 个箱子,货车不能从仓库到商店;
  • 货车最多只能行驶 5 次。我们可以假设它们都发生在同一天;
  • 每辆货车能运送多少箱子是有限制的;
  • 没有一辆货车可以重复同样的行程;
  • 我们需要为每辆货车分配 2 名员工,以便能够成行;
  • 如果我们指派一名员工,我们必须支付他这项任务🙂;
  • 每个员工只能被分配到一辆货车上;
  • 希望避免将冲突的员工分配到同一辆车上。

然后,我们要解决的问题是找到最优(成本更低)的方式将产品从仓库运输到商店,使用不同的货车和工人,同时满足需求、库存、货车和工人分配的约束。

结果看起来像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此表说明了在某些参数(冲突罚金、货车固定成本和员工人数)存在部分差异的不同情况下获得的结果(最终成本)。注意,试验不同参数的可能性是这个框架的主要优点之一。当然,如果我们同时尝试几项改变,比如允许货车进行更多的旅行,同时减少处罚成本和增加冲突数量,分析会更丰富。事实上,我鼓励你以后尝试这些改变。

好的,从现在开始,事情会变得更技术性一些…

2.变量、目标和约束

我们需要建立的第一件事是,我们将在定义问题的方程组中使用的变量。以下是它们的列表,以及它们的描述和值的范围:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

既然我们已经指定了变量,我们就来写问题。用数学术语来说,要解决的问题如下:

i) 客观

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(1)这是用四项之和表示的目标函数: a) 单位成本之和乘以从仓库***【w】发送到仓库【s】使用货车v***;b)F所用面包车的固定费用总和; c) 员工工资之和 e 分配到面包车v**; d) 不遵守不将冲突的一对雇员 j 分配到同一辆货车 v 的可选约束的惩罚成本。

ii) 约束

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)第一个限制规定必须满足每个商店的每个产品 p 的需求,即使用任何车辆v【从每个仓库 w 发送到每个商店 s 的所有产品箱组合的总和**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)规定不能寄没有的箱子。换句话说,从仓库发出的产品箱数总和必须低于或等于其可用箱数;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(4)规定我们不能超过货车所能容纳的箱数限制,因此每辆车运输的箱数总和必须低于或等于货车每次行程的容量(【capacity _ v】)。有了这个约束,我们用辅助变量 Z_wsv 来计算货车的行程数,因为 capacity_v 是车辆 v 在每次行程中可以运输的最大箱子数,我们可以将几个产品的运输算作一次行程。此外,该约束隐含地阻止了重复相同行程的可能性;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

(5)发出信号,表明没有一辆货车可以行驶超过 5 次,同时检查每辆货车是否被使用过。请注意,这个约束与前一个约束是链接在一起的。怎么会?好吧,一旦我们通过使用约束(4)知道是否进行了一次旅行,我们简单地将 Z_wsv 的数目相加,并要求总数小于或等于 5(旅行限制)。这里,由于等式的规定,除非货车闲置,否则 T_v 将等于 1;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

(6)指定每辆货车将被分配 2 个或零个雇员;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

(7)要求每个员工只能分配到一辆货车上;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(8)该约束说明了对冲突雇员对的期望约束 j 是可选的。当一对相互冲突的员工j =(E1e2 )被分配到同一辆面包车(A _ ve1+A _ ve2= 2)时,惩罚被激活( G_vj =1)如果只有一个成员被分配到货车,那么 H_vj =1。如果对 j 的冲突雇员中没有一个被分配到货车 v 中,则所有元素都为零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(9)最终约束规定了每个变量的上限和下限。这里我们声明哪些变量是二进制的,哪些是整数。

完整的问题被表达为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

既然我们已经设法写出了问题,我们可以继续使用谷歌的 或 Python 的工具的来编码这些例子的解决方案。但是,在继续之前,重要的是要强调花时间完成前面的步骤是非常重要的,因为这将使您更好地掌握手头的问题,同时潜在地避免代码中的一些未来错误,并帮助您向数据科学的同事解释您的推理。

3.解决办法

你可以在这个笔记本里查看整个解决方案。

首先,我们导入将在本例中使用的包。

*import numpy as np
import pandas as pd
from ortools.linear_solver import pywraplp*

该解决方案由以下步骤组成:

  1. 设置一个能够复制模拟的种子;
  2. 申报仓库、产品、商店、货车、员工和行程限制的数量;
  3. 为模拟数据设置一些阈值,并为固定工资和将一对有冲突的雇员分配到同一辆货车的处罚设置值;
  4. 生成成本矩阵(每种产品 1 个)、库存向量(每种产品 1 个,显示每个仓库的可用库存)、需求向量(每种产品 1 个,显示每个商店的需求)、每辆货车的容量列表(每辆货车每次行程可以运输多少箱子)以及最终的冲突雇员对列表;
  5. 调用一个求解器的实例,可以用来寻找手头问题类型的解(整数规划或混合整数规划);
  6. 创建变量;
  7. 定义约束条件;
  8. 定义目标函数和问题(最大化/最小化);
  9. 求解并验证结果是否符合约束。

我们从步骤 1-4 开始。当我们模拟这个问题时,成本和数量是用一个随机变量创建的,但是在实际场景中,我们需要使用企业提供的输入。该过程如下所示:

现在,要完成第 5 步,我们需要实例化一个求解器。本例中,我们使用的是来自 Google 的 pywraplp 或者-Tools 。注意在 pywraplp 中有几个可用的解算器,比如 GLOPSCIPGUROBIIBM CPLEX 。由于 GUROBICPLEX 需要许可证,而 GLOP 是为简单线性编程设计的,但是我们手头的问题需要求解器来处理整数或混合整数线性问题,我们将使用SCIP(可以处理整数和混合整数线性问题的最快的非商业求解器之一)。

*solver = pywraplp.Solver.CreateSolver('SCIP')*

之后,我们继续第 6 步,即定义构成线性方程组的变量。

首先,对于产品、仓库、商店和货车的每个组合,我们需要创建一个索引为 x 的变量 p (产品) w (仓库) s (商店) v (货车),它告诉我们产品 p 的整数箱数这些整数变量(求解器。IntVar)被限制为正数(下限 = 0 和上限=solver . infinity())。此外,为了跟踪每个特定的变量和未来的约束,我们仔细地给它们命名。其中一个变量的例子是 x_1_1_1_1**

*x = {}
for p in range(P_products):
  for w in range(W_warehouses):
    for s in range(S_stores):
      for v in range(V_vans):
        x[p,w,s,v] = solver.IntVar(lb=0,
                                  ub=solver.infinity(),
                                  name=f"x_{p+1}_{w+1}_{s+1}_{v+1}")*

其次,我们生成布尔变量(解算器。BoolVar)T _ v它告诉我们是否使用了 van v 它需要能够为其使用分配成本。

*T = {}
for v in range(V_vans):
  T[v] = solver.BoolVar(name=f"T_{v+1}")*

第三,我们创建变量 A_ve ,如果 employee e 被赋值给 van v ,则发出信号。我们需要这样做,以便能够考虑员工工作的成本。

*A = {}
for v in range(V_vans):
  for e in range(E_employees):
    A[v,e] = solver.BoolVar(name=f”A_{v+1}_{e+1}”)*

第四,我们生成变量 Z_wsv ,这是一个辅助布尔变量,用于计算货车的行程次数。他们每个人都会告诉我们从仓库 w 到商店 s 的行程是否被分配给货车

**Z = {}
for v in range(V_vans):
  for w in range(W_warehouses):
    for s in range(S_stores):
      Z[w,s,v] = solver.BoolVar(name=f”Z_{w+1}_{s+1}_{v+1}”)**

最后,我们生成变量 H_vjG_vjH_vj 表示冲突对 j 中只有一个冲突员工被分配到 van v 。变量 G_vj 表示一对冲突雇员 j 中的两个成员都被分配到 van v 中。

**H = {}
G = {}
for v in range(V_vans):
  for j in range(len(J_employees_conflicts)):
    H[v,j] = solver.BoolVar(name=f"H_{v+1}_{j+1}")
    G[v,j] = solver.BoolVar(name=f"G_{v+1}_{j+1}")**

创建变量后,我们继续生成线性约束(我们使用解算器方法。添加来完成此操作)。关于需求约束,我们声明 sum ( 求解器。每个仓库发出的库存总和必须等于商店的需求。在库存约束的情况下,我们指定发送到每个商店的库存总和必须低于或等于每个仓库的可用库存。在这两种情况下,我们都使用来自库 itertools 的函数 product 来获得可能组合(仓库、货车)和(商店、货车)的笛卡尔乘积。

***# Demand constraint** for p in range(P_products):
  for s in range(S_stores):
    solver.Add(
    solver.Sum(
    [x[p, j[0], s, j[1]] for j in itertools.product(
                                  range(W_warehouses),
                                  range(V_vans))]) == demands[p][s],
                                  name='(2) Demand')**# Stock constraint** for p in range(P_products):
  for w in range(W_warehouses):
    solver.Add(
    solver.Sum(
    [x[p, w, j[0],j[1]] for j in itertools.product(
                                   range(S_stores),
                                   range(V_vans))]) <= stocks[p][w],
                                   name='(3) Stock')*

注意,我们使用参数“name”为每个限制添加了一个名称。这将允许我们使用函数解算器在标准问题表示(LP 格式)中识别它们。ExportModelAsLpFormat* ,我强烈建议你用的 :*

*print(solver.ExportModelAsLpFormat(obfuscated=False))*

下面是约束条件的快照:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是需求和库存约束的一个例子(记住 x 采用的形式是 x_pwsv )。第一个,按照要求,显示了使用任何货车从任何仓库 w 发送到商店s = 3v 的产品的箱数 p=5 必须等于商店3 **,**的 需求=3 库存约束表明,从仓库 w=1 发送到任何商店、使用任何货车的产品 的箱数 p = 1p = 1w = 1**必须低于或等于库存=11 仓库 1 随机生成的产品 1 的库存。这些特定约束的其余部分遵循相同的逻辑。

接下来,我们添加行程和货车的使用约束。对于第一个问题,我们要求每辆运货车每次运输的箱子不能超过其容量,同时还要检查从仓库*【w】到仓库【s】的行程是否分配给了运货车。第二个约束帮助我们验证是否使用了 van v*

****# Trip verification constraint**
for v in range(V_vans):
  for s in range(S_stores):
    for w in range(W_warehouses):
      solver.Add(
      solver.Sum(
      [x[p, w, s, v] for p in range(P_products)])
      <= capacities[v]*Z[w, s, v],
      name='4) TripVerification')**# Van use and trip limit constraint** for v in range(V_vans):
  solver.Add(
  solver.Sum(
  [Z[j[0], j[1],v] for j in itertools.product(
                            range(W_warehouses),
                            range(S_stores))]) <= trip_limit*T[v],
                            name='5) TripLimit')**

让我们来看看这些约束的示例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一方面,约束 TripVerification_63 检查 van v=4 是否被分配从仓库 w=3 到商店 s=3 。注意,术语 Z_wsv 乘以 4,就是面包车 v 的容量。这意味着,为了符合约束条件,运输数量的最高总和不能大于 4。同样,在任何情况下,对于所有大于 0 的量, Z_wsv 必须等于 1。这就是我们如何核实一次旅行是否被分配完成。

另一方面,三极限约束的例子意味着货车 v=1 的所有可能行程路径的总和( w_s )必须低于或等于 5(我们在开始时设置的行程极限),因为我们有术语 -5T_1 。请注意,这最后一项也将告诉我们是否会使用 van v=1 。相同的逻辑适用于该类剩余约束中的其余货车。

在这之后,我们跟进最终的约束:(6)employee requirement(7) JobLimit(8)conflict verification。关于员工要求我们要求使用货车时,必须指派两名员工。接下来, JobLimit 意味着我们不能将一名员工分配到一辆以上的货车上。最后,构建 ConflictVerification 来验证每对 J 员工冲突是否被分配到同一辆货车。

****# Number of employees per van**
for v in range(V_vans):
  solver.Add(
  solver.Sum(
  [A[v,e] for e in range(E_employees)]) == 2*T[v],
  name='6) EmployeeRequirement')**# Number of vans an employee can be assigned to** for e in range(E_employees):
  solver.Add(
  solver.Sum([A[v,e] for v in range(V_vans)]) <=1,
  name='7) JobLimit')**# Verification of the constraint compliance** for v in range(V_vans):
  for idx,j in enumerate(J_employees_conflicts):
    solver.Add(
    solver.Sum([A[v,j[0]-1]])==-A[v,j[1]-1]+H[v,idx]+2*G[v,idx],
    name='8) ConflictVerification')**

前两者的一个例子是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这两个约束都很简单。 EmployeeRequirement_69 告诉我们,要使用货车 v=4 ,我们至少需要为其分配 2 名员工。 JobLimit 指定雇员 e=1 只能被分配到一辆货车。

关于最后一个约束,可能是最难理解的一个,我们有两个例子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

conflict verification _ 152检查如果冲突的一对员工 j=8 ,由员工 e=2e=3 组成的 v=4 。如果这些雇员中只有 1 个被分配给它,那么 H_4_8 必须等于 1,等式才能等于 0。如果两个雇员都被分配到这辆货车,那么 G_4_8 必须等于 1,等式才能等于 0。请注意,只有第二种情况会对总成本产生影响,因为 500 美元的罚金将被激活。在 ConflictVerification_152 的情况下,我们可以直接看到,它检查的是完全相同的东西,但对于由雇员 e=2e=4 组成的冲突对 j=9

既然我们已经写完了一系列约束条件,我们准备继续第 8 步,即目标函数的定义和问题的类型(最大化/最小化)。为此,我们首先创建一个列表来保存前面描述的每一项,因为目标函数就是所有项的总和。然后,在添加了所有的项之后,我们指定我们想要解决一个最小化问题( solver)。最小化

****# Objective Function** objective_function = []**# First term -> Transportation variable costs** for p in range(P_products):
  for w in range(W_warehouses):
    for s in range(S_stores):
      for v in range(V_vans):
        objective_function.append(costs[p][w][s][v] * x[p, w, s, v])**# Second term -> Transportation fixed costs** for v in range(V_vans):
  objective_function.append(costs_v[v]*T[v])**# Third term -> Salary payments** for v in range(V_vans):
  for e in range(E_employees):
    objective_function.append(fixed_salary*A[v,e])**# Fourth term -> Penalties for not avoiding conflicts** for v in range(V_vans):
  for j in range(len(J_employees_conflicts)):
    objective_function.append(conflict_penalty*G[v,j])**# Type of problem** solver.Minimize(solver.Sum(objective_function))**

这是整个目标函数,其中所有变量都是二进制的,除了 x ,这是一个整数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后,我们使用 Solve 方法运行优化算法。

**# Call the solver method to find the optimal solution
status = solver.Solve()**

剩下的就是检查解决方案了。为了做到这一点,我们称之为解决方案状态,如果它是最优的( pywraplp)。我们打印目标函数的值,如果不是这样,我们应该检查我们以前的工作,寻找问题定义中的不一致。

**if status == pywraplp.Solver.OPTIMAL:
  print(
   f'\n Solution: \n Total cost = ${solver.Objective().Value()}'
   )
else:
    print(
    'A solution could not be found, check the problem specification'
    )**

对于此模拟,解决方案是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这意味着使用货车将产品从仓库运送到商店的最佳运输成本最终为 22,499 美元。现在我们必须决定这是否足够好。如果我们认为可以通过修改问题的定义找到更好的解决方案,我们应该考虑如何添加/删除或修改一些约束,或者如果可能的话,如何改变上下文变量。如果我们认为这是可以的,那么下一个自然的步骤将是检查模型变量的最优值,因为从计划的角度来看,能够辨别哪些是将要使用的货车,哪些是将要在这些任务中工作的雇员是非常相关的;他们中的哪些人被分配到哪辆货车;哪些是需要计划的行程以及在每个行程中运输的产品的数量和类型。

为了完成这篇文章,我们假设没有什么可做的了,这样我们就可以通过提取解决方案的细节来检查相关变量的最优值。

4.车队和劳动力计划详细信息

为了开始提取有用的细节,我们遵循一个简单的程序:I)提取每个变量的最佳值;ii)预处理并将数据重新排列到表格中;iii)使用不同的过滤器查询表格。这里是我)和 ii):

**result_list = []**# Extract the solution details and save them in a list of tables** for var in [x,Z,T,A,H,G]:
  variable_optimal = []
    for i in var.values():
      variable_optimal.append(i.solution_value())

      var_result=list(zip(var.values(),variable_optimal))

      df=pd.DataFrame(var_result,columns=['Name','Value'])

      result_list.append(df)**# Concatenate the tables and extract the variable names** results=pd.concat(result_list)
results['Name']=results['Name'].astype(str)
results.reset_index(drop=True,inplace=True)
results['Variable']=results['Name'].str.extract("(^(.)\d?)")[0]
results['Variable']=results['Variable'].str.upper()
results['Value']=results['Value'].map(int)**# Create a mapping of variables and indices to simplify the analysis** variable_indices={'X':'X_product_warehouse_store_van',
                  'A':'A_van_employee',
                  'T':'T_van',
                  'H':'H_van_pair',
                  'G':'G_van_pair',
                  'Z':'Z_warehouse_store_van'}results['Indices']=results['Variable'].map(variable_indices)**# Order the columns** results=results[['Variable','Indices','Name','Value']].copy()**

这是主表的一个示例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来,在创建我们的主数据框架之后,我们通过用“T”过滤列变量(表示车辆使用情况的二进制变量)和用 1 过滤列(这意味着最优解意味着我们需要使用这些货车)来寻找我们要使用的货车:

**list(results[(results[‘Variable’]==’T’)&(results[‘Value’]==0)].Name)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果显示将使用 vans 1、2 和 3。

对于下一部分,我们将只搜索与 van 1 相关的变量,从回答使用该车辆进行哪些旅行开始。这里重要的是要记住,van v=1 对应于索引 v=0 :

**trips_van_1=[]
for w in range(W_warehouses):
  for s in range(S_stores):
    for v in range(V_vans):
      if v==0:
        trips_van_1.append(str(Z[w,s,v]))trips_df=results[(results['Variable']=='Z')&(results['Value']>0)]display(trips_df[trips_df['Name'].isin(trips_van_1)])**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,货车 1 被分配了以下 5 次行程:

  • 仓库 1 存放 1 和 3;
  • 仓库 2 到商店 2;
  • 仓库 3 存放 2 和 3;

接下来,我们需要找到将负责货车 1 的交付操作的员工:

**employees_van_1=[]
for v in range(V_vans):
  for e in range(E_employees):
    if v==0:
     employees_van_1.append(str(A[v,e]))

employees_df=results[(results['Variable']=='A')&(results['Value']>0)]display(employees_df[employees_df['Name'].isin(employees_van_1)])**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该表告诉我们,雇员 5 和 6 被分配到货车。现在,关于货车 1 的最后一个问题是,在每次行程中,它必须运输多少箱子和哪些产品。让我们在从仓库 2 到商店 2 的行程中这样做:

**transport_df = results[(results['Variable']=='X')&(results['Value']>0)]transport_trip_2_2 = []for p in range(P_products):
  for w in range(W_warehouses):
    for s in range(S_stores):
      for v in range(V_vans):
        if w==1 and s==1 and v==0:
          transport_trip_2_2.append(str(x[p,w,s,v]))display(transport_df[transport_df['Name'].isin(transport_trip_2_2)])**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,在从仓库 2 到商店 2 的行程中,货车 1 将运输 5 个箱子(其最大容量),4 个产品 2 和 1 个产品 3。请注意,在这些示例中,我们验证了模型的预期效果,即考虑了约束条件以获得最佳解决方案。最后,我们只需要检查冲突的两个雇员的约束条件发生了什么:

**results[(results['Variable']=='G')&(results['Value']!=0)]**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

嗯,看起来这一对相互冲突的雇员 j=14 (雇员 3 和 4)被分配到最优解中的货车 2。如果是这样,那么 A_2_3 和 A_2_4 应该等于 1,我们来查一下:

**results[(results['Variable']=='A')&(results['Value']!=0)]**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

就这样,我们终于完成了例子。

结束语

总而言之,我们已经看到,将现实生活中的问题写成线性方程组确实是可能的。更重要的是,我们也能够解决它们。当然,找到一个最佳解决方案严格地依赖于环境变量、目标和约束集。然而,真正复杂的设置可能会使找到最佳解决方案变得更加困难,因为需要分析更多的参数组合,这反过来需要更多的计算能力。

最后,尽管本文中提供的示例与车队和劳动力规划问题有关,但该框架的应用范围要广得多。严格地说,我已经用这种方法解决了与物流供应链管理定价和收入管理相关的问题,但是可能的应用更进一步,因为它也经常用于解决与医疗保健优化城市设计管理科学体育分析相关的问题。

对于那些到达这一步的人,我希望你能够获得一些见解来构建你自己的解决方案。别担心,尽管可以说最难的部分是定义问题,但是一旦你做了,接下来的步骤就很简单了。感谢阅读!

别忘了喜欢和订阅更多与解决真实商业问题相关的内容🙂。

参考

[1]线性规划:基础与扩展(运筹学国际丛书&管理科学第 285 册)

飞行影响:将碳排放加入旅程

原文:https://towardsdatascience.com/flight-impact-adding-carbon-emissions-to-the-itinerary-9ebdf7ad5b1c

构建一个交互式应用程序,为关注气候的旅行者提供支持

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

克里斯·莱佩尔特在 Unsplash 上的照片

对许多美国人来说,航空旅行是生活中根深蒂固的一部分。无论是去看望家人度假,参加会议,还是快速休假,我们中的许多人在准备起飞时都没有考虑到我们的飞行可能会有非货币成本。我们的旅行计划中经常缺少的是对旅行碳排放的调查。虽然大多数旅行者没有考虑到环境影响,但欧洲运输和环境联合会警告说,随着航空排放到 2050 年增加一倍或两倍,该行业可能会消耗全球碳预算的 1/4。

关于为什么关注个人的“碳足迹”不是遏制气候危机的有效方法,有许多非常有见地的论点。然而,航空业确实提供了一个有趣的机会。2018 年,只有 11%的全球人口乘坐过飞机,而仅仅 1%的人却承担了 50% 的航空排放。这意味着这个小群体中的个体有一种独特的能力,可以通过相对较小的行为变化产生影响。

带着这个想法,我着手我的最后一个项目 METIS 数据科学训练营,希望建立一些东西,让消费者能够自我教育,并在旅行中做出明智的决定。就这样,飞行冲击诞生了!(点击本文底部链接查看!)

这项任务

在这个项目中,我和我的同学们面临着建立一个完整的数据管道的挑战,从 API 到交互式 web 应用程序。Flight Impact 背后的管道如下:调用 Climatiq API (CC BY-SA 4.0),清理 Google Colab 中的数据,存储在 MongoDB 中,最后在一个. py 文件中操纵它,该文件用于从 Github 启动我的 Streamlit 应用程序。

Climatiq API 提供了从客运到航运等各种活动的排放量计算。当给定两个机场的组合时,API 返回目的地之间单程的单个经济舱乘客的二氧化碳排放量(以千克为单位)。我能够指定我想要使用的计算标准,并且我选择了美国环保局对短程、中程和远程飞行的计算方法,以确保可重复性。使用世界大型机场(每年服务数百万乘客)的列表和 Python 请求库,我自动化了 180,000 对全球目的地的 API 请求。

接下来,我在 Google Colab 中清理了我的数据,添加了一些对前端用户体验有用的功能,如机场的完整国家名称(而不仅仅是一个代码)和航班排放量的计算(以吨为单位)。在 Google Colab 上,我使用 pymongo 将清理后的数据插入 MongoDB 数据库,该数据库托管在 CleverCloud 上。

在 Streamlit 中构建应用

管道的最后一步是在一个. py 文件中构建飞行影响的用户体验。 Streamlit ,一个将 Python 数据脚本转化为 web 应用的平台,允许你直接从 Github repo 中的文件启动应用。你只需要在代码中添加一些 Streamlit 命令来指定特性,比如下拉菜单和地图(这里有一个链接指向我的 Streamlit。py 文件,如果你正在寻找一个例子)。在这个过程中,我学会了不要低估 Streamlit 的缓存特性的重要性。每当用户改变页面上的某些内容时,Streamlit 都会重新运行脚本,但是在函数之前添加一个简单的@st.cache 命令会告诉应用程序仅当输入或函数本身发生变化时才重新运行该函数。这有助于应用程序运行更快,使用更少的内存。

简介:飞行冲击

当用户访问 Flight Impact 时,我们的目标是让他们能够探索自己的选择,并以更明智的消费者身份离开。用户可以:

  • 查看所有从其所在城市出发的全球航班,以及每个航班的相关碳排放量
  • 按出发国家或城市、目的地国家或城市或排放限制进行过滤,以比较各种路线
  • 根据他们的路线是否已经确定,查看影响较小的替代方案或使他们选择的航班购买更省油的方式,例如经济舱、避免中途停留、只乘坐满员航班

当个人在寻找参与解决气候危机的方法时,我们的旅行习惯可以发挥作用。展望未来,让碳排放成为旅行计划过程中与获得靠窗座位同样重要的一部分。

自己试试这个应用这里!注意:此链接使用 Streamlit 的免费版本,一段时间后可能会超过使用限制。如果是这样的话,下面的视频也展示了飞行撞击体验。

要深入了解我对这个项目的代码或演示,请参见 项目回购

floWeaver 将流数据转换成 Python 中的 Sankey 图

原文:https://towardsdatascience.com/floweaver-turn-flow-data-into-a-sankey-diagram-in-python-d166e87dbba

实践教程

floWeaver 将流数据转换成 Python 中的 Sankey 图

用几行代码创建和定制一个 Sankey 图

动机

假设您想要显示两个域(例如,公司和工作)之间的映射,或者从一个位置到另一个位置的路径。

如果可以用图表把这种关系可视化不是很好吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这时桑基图就派上用场了。桑基图是描述流量的图表,其中线条的宽度与流量成比例。

流程可以是:

  • 从一个地区到另一个地区的运动
  • 能量从源头到目的地的运动
  • 政党之间选民的流动

还有更多。

在本文中,我们将学习如何使用 floWeaver 在 Python 中创建一个 Sankey 图。

floWeaver 是什么?

floWeaver 是一个 Python 库,允许您轻松创建和定制 Sankey 图。

要安装 floWeaver,请键入:

pip install floweaver

要在你的 Jupyter 笔记本上显示桑基图,安装 ipysankeywidget

pip install ipysankeywidget
jupyter nbextension enable --py --sys-prefix ipysankeywidget

确保 ipywidgets 已启用:

jupyter nbextension enable --py --sys-prefix widgetsnbextension

酷!现在我们已经准备好试用 floWeaver 了。

准备数据

让我们从从 Kaggle 加载 Cruise_Travels 数据集开始。

接下来,按照EMBARK_PORTDISEMBARK_PORTCRUISE_REGION对数据帧进行分组。

floWeaver 根据它们的名称将不同的列用于不同的目的。具体来说,

  • source指定流程开始的位置
  • target指定流程结束的位置
  • type指定流的类型
  • value指定流量

因此,我们将重命名一些列,以便它们符合 floWeaver 的格式。我们将使用:

  • EMBARK_PORTsource
  • DISEMBARK_PORTtarget
  • CRUISE_REGIONtype
  • PRICE_PAIDvalue

创建桑基图

开始

首先创建两个组:embark_portdisembark_port

接下来,指定图表中每个组的顺序。列表中的第一组将放在最左侧。列表中的最后一组将放在最右侧。

为了指定哪些连接应该出现在图中,我们将使用Bundle:

将所有内容放在一起,创建一个桑基图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这看起来不是很有用,因为所有端口都被归入各自的类别。让我们使用Partition来分隔这些端口:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

不错!这样更好看!在上图中:

  • 左边的节点代表不同的上船港口。
  • 右边的节点代表不同的卸载端口。
  • 这条线显示了从一个港口到另一个港口的航行路线。
  • 线越粗,旅行就越贵。

组合不同的类别

上面的图表看起来有点乱。我们还可以将不同的类别组合成一组,使图形看起来更清晰。让我们试着根据港口所属的大洲对不同的港口进行分类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

不错!图表现在看起来更清晰了。

彩色亮度

除了线条的粗细,我们还可以用颜色强度来表示一个流的值有多大。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我们也可以使用palette属性来改变调色板。让我们用Blues_9调色板来表现海洋的颜色。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

找到所有调色板的列表这里

现在更容易看出哪些旅行最贵。似乎最昂贵的旅行是:

  • 从大洋洲到欧洲
  • 从澳大利亚到欧洲
  • 从欧洲到欧洲
  • 从欧洲到亚洲

区分不同的流动类型

type代表巡航区域。为了根据线的类型给线着色,我们将使用SankeyDefinition中的flow_partition属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

对相同类型的线进行分组

为了对相同类型的线进行分组,我们将在图的中间添加节点。这些节点被称为路点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

有意思!从图中可以看出,大多数穿越西部和东南亚的旅行似乎比穿越其他地区的旅行更贵。

结论

恭喜你!您刚刚学习了如何使用 Python 从流数据集创建桑基图。我希望这篇文章能给你提供使用 Sankey 图分析你自己的流量数据所需的知识。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/Data-science/blob/master/visualization/floweaver_example/travel.ipynb

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。

这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:

https://python.plainenglish.io/find-the-top-bootcamps-for-data-professionals-from-over-5k-profiles-92c38b10ddb4

参考

伊万·佩雷斯。2020–02–16.游轮旅行。
CC0:公共领域。从https://www.kaggle.com/ivanpv/cruise-travels 取回 2021–01–12

荧光神经元细胞数据集—第一部分

原文:https://towardsdatascience.com/fluorescent-neuronal-cells-dataset-part-i-ac123196b963

一种新的目标分割、检测和计数基准

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

国家癌症研究所Unsplash 上拍摄的照片

在过去十年中,计算机视觉成功的关键因素之一是大量标记数据的可用性。

事实上,收集智能手机、网络摄像头和其他现代设备图像的可能性产生了前所未有的大量数据,描述了广泛的日常生活环境。随着可用性的提高,对所描绘场景的熟悉程度使得求助于未经培训的操作员来注释图像成为可能,因此需要相对较低的成本。反过来,这允许研究人员和从业人员通过利用监督学习技术来解决几个学习任务,如人脸识别关键点检测自动驾驶等。

不幸的是,这并不适用于更多的利基应用,在这些应用中,数据更少,标记需要一些专业知识。例如,考虑像生物学或生命科学这样的领域,由于缺乏适当规模的精选数据,这些领域的技术水平仍然落后于更主流的应用。

在本系列的第一篇文章中,我们将呈现 荧光神经元细胞数据集 :收集了小鼠脑切片283 张高分辨率图片 (1600x1200 像素)和相应的地面真相蒙版

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图一。**数据集-图片由作者提供

数据

荧光神经元细胞数据集(莫雷利等人,2021) 是 283 张高分辨率(1600x1200 像素)小鼠脑切片图片的集合,可在此免费下载

这些图像是通过成像技术荧光显微镜获得的,以研究啮齿动物麻木的机制( Hitrec 等人,2019 )。在实践中,在小鼠接受受控的实验条件后,一种标记被注射到它们的大脑中,以“突出”一些感兴趣的神经元细胞。因此,这些结构在合成的、通常较暗的背景上表现为亮度和饱和度不同的黄色斑点(图 1,顶行)。

虽然图片的数量有限,特别是与计算机视觉应用中通常涉及的大规模数据集相比,但它们的高分辨率允许将它们分成更小的块。通过这种方式,再加上典型的增强管道,数据总量可以增加数百倍,从而保证有足够的信息可供学习。

真相面具

除了图像,数据还包含用于语义分割的相应地面实况注释,即二进制掩码,其中如果每个像素属于一个细胞,则标记为 255(白色),否则标记为 0(黑色)(见图 1,底部一行)。

为了减轻标记的工作量,开发了半自动程序,包括自适应阈值和领域专家的手动分割。

特别是,大多数图像(252 张图片)首先通过自适应阈值自动注释。这意味着根据每个图像的像素强度分布来选择亮度截止值,所有高于该阈值的像素都被视为细胞。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图二。**自适应阈值处理—图片作者

然后,这些草稿被手动修改以排除假阳性和/或增加假阴性。

相反,剩下的 31 幅图像是由领域专家手工分割的。后一组中包括了特别相关的例子,目的是为最具挑战性的观察收集高度准确的注释。

学习任务

荧光神经元细胞数据集可用于研究不同的学习任务。

有了二进制基础事实掩码,更自然的方法是在语义分段设置中按原样利用标签。或者,通过在分割对象周围绘制边界框,可以很容易地将其扩展用于对象检测。此外,可以通过仅考虑每个图像中的细胞总数来关注对象计数,从而将数据作为回归问题进行分析。

在第一篇文章中,我们介绍了荧光神经元细胞数据集:一组精选的荧光显微镜图像和相对真实的遮罩。

我们介绍了它的起源、数据格式,并简要提到了一些可以用这些数据来研究的学习问题。

在下一篇文章中,我们将更详细地研究这些数据,强调与这些数据相关的一些特殊特征和挑战。

如果你喜欢这个话题,你可以在[ 12 中阅读更详细的讨论。另外,你可以继续下载数据集,用原始论文的代码做实验,用数据玩自己。
让我知道你在评论里发现了什么!

参考

[1] L. Clissa,通过机器和深度学习支持科学研究:荧光显微镜和操作智能用例 (2022), AlmaDL [2] R .莫雷利等人,通过 c-ResUnet 的深度学习实现荧光显微镜中的细胞计数自动化 (2021),科学报告

荧光神经元细胞数据集—第二部分

原文:https://towardsdatascience.com/fluorescent-neuronal-cells-dataset-part-ii-e1ac27e26d7

独特的特征和挑战

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

JESHOOTS.COMUnsplash 上拍照

在本系列的第二篇文章中,我们将更详细地浏览 荧光神经元细胞(【FNC】)的数据,强调它们的一些特有特性和挑战。

如果您错过了第一部分,请查看更多有关数据收集方式及其含义的详细信息:

独特的特征

荧光神经元细胞数据集带有一些特殊的特征,可能值得一提,以帮助分析这些数据。

RGB 通道

由于有意选择了特定波长的光线,图片主要由两种流行的色彩构成:

  • 荧光标记发出的黄色调
  • 背景的暗色调。

因此,要填充的颜色通道只有红色和绿色,它们的组合产生黄色,而蓝色通常是空的(图 1 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图一。**像素强度分布— 作者图片

因此,通过 3D 色彩空间表示 FNC 数据可能是多余的,并且较低维度的表示可能是足够的(例如灰度)。

另一方面,分散在额外维度中的剩余信息可能有助于从背景中辨别细胞的像素——为什么要丢弃它呢?然而,RGB 并不是唯一可用的色彩空间,所以人们可能会想,对于 FNC 数据的分析来说,其他表示法是否可能更方便

图 2 将一幅图像的像素显示为三维色彩空间中的点。颜色反映了图像中像素的外观,而位置是使用 RGB(左)或 HSV (右)编码确定的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图二。 RGB 与 HSV 色彩空间— 作者图片

在 RGB 表示中,点是杂乱的,并且几乎是对齐的。相反,云在 HSV 空间中分散得多,这可以促进细胞像素和背景之间更容易的分离。

这一观察表明不同的色彩空间对于特定的学习任务来说或多或少都是方便的,因此选择一个色彩空间可能会影响结果。出于这个原因,FNC 数据集的作者在他们为这些数据提议的 c-ResUnet 架构中插入了一个学习过的色彩空间转换。

细胞特征

除了技术规格,考虑要分割/检测/计数的对象的特征也很重要。

例如,我们可以检查像面积最大值 费雷特直径 这样的量的分布,以了解不同神经元细胞之间大小的可变性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3。**细胞面积和费雷特直径— 图片由作者提供

这两个指标表明,大多数细胞尺寸较小,第 75 百分位分别约为 150μm 和 21μm。尽管如此,这两种分布呈现长尾效应,延伸到更高的值,达到上述度量值的三倍以上。

另一个重要的方面是每张图片中对象的数量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 4。**计数分布— 图片作者

大多数图像呈现低数量的神经元,中值为 21,第一个峰值为 56 个图像,没有细胞 ( # cells=0 )。剩下的那些反而形成了一条非常长的尾巴——事实上是分布的一半——在一幅图像中,较高的值挤在几个局部峰值周围,最多可达 68 个单元。

总而言之,这些细胞在大小、形状和数量方面表现出相当的异质性。因此,这需要一个足够灵活的模型来处理这种可变性。

挑战

FNC 数据集提出了一些在训练期间必须解决的特定挑战。

阶级不平衡

其中之一肯定是背景和细胞像素之间的极度不平衡。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5。**阶层失衡— 作者图片

几乎 90%的图像包含不到 1%的细胞像素。更重要的是,这个百分比在最好的情况下不超过 5%,最大值为 4.86%。

就绝对值而言,在 50%的图像中,背景像素大约是细胞像素的 20 到 300 倍,分布的重要部分延伸到超过 1000 倍。

当然,学习任务通常会受到班级比例失调的影响,因此对 FNC 数据的分析应该考虑必要的补救措施来处理它。

确凿的例子

除了班级不平衡之外,一般来说,FNC 和荧光显微镜成像也面临一些特殊的挑战。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 6。**挑战— 作者图片

例如,有时多个单元靠得很近,甚至相互重叠。在这种情况下,精确的分割对于正确处理细胞团块至关重要。因此,可能需要一些技巧来帮助模型分离不同的细胞实例(例如分水岭后处理)。

嘈杂的标签

此外,复杂性的一个重要来源是由于细胞识别中固有的任意性而导致标签中存在噪声。

事实上,有时甚至人类专家也不同意是否应该将标记染色视为细胞。因此,可能会发生类似的例子在图像上被稍微不同地标记的情况。

不幸的是,这个问题很难解决。然而,在评估结果时,人们可能希望将它考虑在内,或者至少在查看纯度量时意识到这个问题。

史前古器物

最后,学习任务受到偶然存在的荧光团积累的阻碍,这些荧光团产生与细胞非常相似的发射。

当这种情况发生时,图片可能包含虚构的物体或在形状、大小或颜色方面类似神经元细胞的无趣结构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 7。**生物文物— 作者图片

这些伪像可能从细丝和点状伪像(图 7 和图 8 )的小区域变化到更大的结构,如图 7 中的条纹或图 8 中的“马卡龙”形物体。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 8。**技术神器— 作者图片

因此,该模型确实需要理解细胞的形态,并将其与颜色信息一起考虑,以便识别它们。

干得好,走到了这一步!

在本文中,我们介绍了在荧光神经元细胞数据集上执行的一些 EDA,强调了在分析这些数据时要解决的一些特殊特征和挑战。

在本系列的下一篇也是最后一篇文章中,我们将讨论一些建议的度量标准,以评估特定于这些数据的检测和计数性能。

如果你喜欢这个话题,你可以在[ 12 中阅读更详细的讨论。另外,你可以继续下载数据集,用原始论文的代码做实验,用数据玩自己。
让我知道你在评论里发现了什么!

参考

[1] L. Clissa,通过机器和深度学习支持科学研究:荧光显微镜和操作智能用例 (2022), AlmaDL [2] R .莫雷利等人,通过 c-ResUnet 的深度学习实现荧光显微镜中的细胞计数自动化 (2021),科学报告

荧光神经元细胞数据集—第三部分

原文:https://towardsdatascience.com/fluorescent-neuronal-cells-dataset-part-iii-287c2a4f1a22

评估的基准指标

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

斯科特·格雷厄姆Unsplash 上拍照

在本系列的第三篇也是最后一篇文章中,我们检查了一个特定于任务的指标列表,它非常适合对 荧光神经元细胞 ( FNC ) 数据的分析。

如果您错过了第一部分,请查看更多关于 i) 数据是如何收集的以及它们代表了什么的详细信息:

与***ii)***FNC 数据相关的具体挑战:

模型评估和性能评估是数据分析管道中的关键步骤。当然,有各种方法可用于此目的。

鉴于这种多样性,需要记住的关键方面是每种策略都强调模型的不同功能。因此,性能可能会因参考指标的不同而有很大差异。

由于这个原因,我们必须明智地选择评估计划,以反映我们模型的最终用途。

在下文中,我们将讨论一些适用于 FNC 数据的指标。具体来说,我们根据学习任务考虑 3 种场景:语义分割、对象检测对象计数。

细分指标

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

扎克里·纳尔逊Unsplash 上的原始照片。作者添加的注释。

对于语义分割,我们可以采用标准的度量,例如 骰子系数平均交集超过并集

然而,它们可能被对边界单元的主观识别和注释中潜在的不准确性所破坏。因此,我们在解释这些指标时需要考虑到这一点。

噪声的主要来源来自注释过程。事实上,地面实况标签是用半自动方法产生的,包括自适应阈值和手动注释。前者生成具有锯齿状单元轮廓的遮罩,而后者呈现具有更平滑边界的对象。

因此,即使大部分细胞被正确识别,我们也可能在边界分割中观察到微小的重复错误。

因此,单一指标值不足以进行真实的评估。相反,一个彻底的评估需要一个更大的画面,并且必须适合分析的最终目标。

在实践中,当目标是精确细分时,建议追求更高的性能。相反,当最终的兴趣更多地在于识别对象时,我们可以放宽要求。

检测指标

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

阿诺·塞纳尔在 Unsplash 拍摄的照片

关于对象检测度量,可以采用常用的指标,如 F1 得分、精度和召回率 。确定的关键要素是真阳性、真阴性和假阳性的定义。事实上,这必须根据我们数据的具体特征来定制

在 FNC 的例子中,设计了一个专用算法。这使得预测对象和目标对象之间的关联具有合理的灵活性。

具体来说,将每个预测对象与相应基本事实标签中的所有像元进行比较,并与最接近的像元进行唯一链接。如果它们的质心距离小于平均像元直径(50 像素),则预测的元素被视为匹配。因此,它增加了真正的正计数(TP)。

在这个过程的最后,所有没有匹配的真实对象都被认为是假阴性(FN)。同样,剩余的检测到的不与任何目标相关联的项目被认为是假阳性(FP)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

对于检测指标,我们不会遇到之前描述的分割指标的相同缺陷。

尽管如此,边界细胞的存在使得我们的评估容易受到一些注释的主观性的影响。在这种情况下,目标和预测对象之间的不一致通常在操作员主观解释的范围内。

然而,这种一致性并没有被度量所捕捉。因此,我们观察到较低的性能,尽管结果与人类的判断完全一致。

总之,我们可以综合考虑所有指标,以全面了解我们模型的优势和劣势。

计数指标

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

克里斯·贾维斯在 Unsplash 上的照片

有几种评估模型计数能力的方法,各有利弊。建议的策略是综合利用不同的指标从多个互补的角度评价结果

一种方法是简单地考虑基本事实掩膜中的像元数与预测像元数之间的差异。例如,我们可以考虑绝对误差来了解目标计数和预测计数之间的实际距离。

然而,根据目标单元的总数,给定的余量表示或多或少的严重误差。为此,我们可以添加百分比误差作为额外的评估要素。此外,这提供了我们是否高估/低估计数的信息。

虽然上述数量是直观的,但当计数分布的可变性较低时,它们可能会隐藏较差的性能。因此,我们可以通过查看 R 决定系数来补充评估。这可以理解为模型解释的方差部分。因此,它给出了我们的模型如何很好地捕捉现象的可变性的感觉。

总之,建议将这三个指标结合起来看,以更全面地了解我们模型的优势和劣势。

在本文中,我们检查了几个基准,用于评估使用荧光神经元细胞数据集训练的模型。

当然,最终的选择还是要看你分析的具体要求。此外,请记住,由于数据的自然干扰,纯指标值会受到限制。

现在我真的很想知道你的拍摄!

你认为这个列表是详尽的吗?你能想到更好的或互补的度量标准吗?

在评论里告诉我吧!

如果你喜欢这个题目,你可以在[ 1 2 ]中阅读更详细的讨论。此外,您还可以下载 数据集 ,用原论文的 代码 数据 进行实验。

参考

[1] L. Clissa,通过机器和深度学习支持科学研究:荧光显微镜和操作智能用例 (2022), AlmaDL [2] R .莫雷利等人,通过 c-ResUnet (2021),科学报告

MNIST 上的 Flux.jl 性能分析

原文:https://towardsdatascience.com/flux-jl-on-mnist-a-performance-analysis-c660c2ffd330

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

布拉登·科拉姆在 Unsplash 拍摄的照片

在上一篇文章中定义了各种模型和训练算法后,我们现在来看看它们在应用于 MNIST 数据集时的表现。

介绍

在之前的一篇文章(flux . JL on MNIST——主题变奏曲 )中,我们定义了三种不同的神经网络(NN),目的是使用 MNIST 数据集识别手写数字。此外,我们实现了梯度下降算法(GD)的三个变体来训练这些模型,它们可以与不同的成本函数和不同的优化器结合使用。

因此,我们有相当多的积木,它们可以以各种方式组合,以实现我们识别手写数字的目标。具体来说,我们有以下组件(要了解更多信息,请查看上面引用的前一篇文章):

  • 型号: 4LS,3LS,2LR
  • GD 变体:批量 GD、随机 GD小批量 GD
  • 成本函数:均方误差( mse )和交叉熵( ce )
  • 优化者:下降亚当

在本文中,我们将分析这些组件的不同配置对于图像识别的表现如何,以及需要多少努力(以 GD 的迭代次数和用于该目的的时间表示)来训练它们。

学习曲线

我们不仅对模型在用 GD 训练后的性能感兴趣,而且我们还想通过在每次迭代后查看成本函数(或损失函数的同义词)的值来了解 GD 在执行过程中的表现。

绘制这些值与迭代次数的关系,我们得到所谓的学习曲线。这条曲线很好地描述了 GD 收敛的速度,以及在训练过程中是否存在问题(例如,如果 GD 陷入平稳状态)。

为了在培训期间收集这些值,我们必须在 GD 实现中添加几行代码:

  • 由于计算损失在计算上可能是昂贵的,我们不希望总是在每次迭代时都这样做,而是只在每次到第 n 次迭代时才这样做。这由新的(可选的)关键字参数logstep(第 1 行)控制。1 的logstep意味着我们在每次迭代后计算并存储损失,例如 100 的值意味着我们仅在每次第 100 次迭代后才这样做。
    仅举例说明差异有多大:使用随机 GD 对 4LS 模型进行 4000 次迭代,不记录损失需要 3.8 秒,每次迭代计算损失需要 262.9 秒。
  • 在第 2 行中,用于存储损失历史的数组hist被初始化为零。
  • 在第 9 行中,我们检查是否已经到达应该记录损失的迭代,并将相应的损失值存储在hist数组中(第 10 行)。
  • 在函数的末尾(第 13 行),我们返回完整的损失历史。

注意 : Flux 提供了一种回调机制,可以根据时间间隔来实现这种日志记录。即,使用该机制,损失值不会在每 n 次迭代之后被记录,而是在每 n 秒之后被记录。

准确(性)

训练完模型的参数后,我们想知道这组参数在用于识别手写数字时的表现如何。因此,我们将测试数据集上的模型预测与测试数据集的实际标签(test_y_raw)进行比较,并计算正确识别数字的比例。这个分数叫做模型的精度

例如,我们通过调用model4LS(test_x)获得 4LS 模型的预测。结果是一个由 10,000 个热点向量组成的数组。为了将它们与test_y_raw中的标签进行比较,我们必须将独热向量转换成数字。这可以使用函数Flux.onecold()来完成,它简单地返回一个热向量中 1 位的索引。也就是说,如果第一位是 1,函数返回 1,如果第二位是 1,我们得到 2,依此类推。由于我们的模型产生一个第一位设置为 1 的独热向量,如果“数字 0”被识别,我们必须将onecold()的结果减 1 以获得正确的标签。

因此,精度计算如下:

function accuracy(y_hat, y_raw)
    y_hat_raw = Flux.onecold(y_hat) .- 1
    count(y_hat_raw .== y_raw) / length(y_raw)
end

例如,调用accuracy(model4LS(test_x), test_y_raw)将获得测试数据集上 4LS 模型的准确性。

accuracy功能使用 btw。.-算子广播朱莉娅的机制。第一个公式中的运算.-从表达式左侧数组的每个元素的中减去 1。第二个公式中的.==y_hat_raw每个元素与y_raw中的每个对应元素进行比较(产生一个布尔数组)。

培训和分析

以下所有的训练都是在一台 8 GB 内存的苹果 M1 上使用 Julia 1.7.1 和 Flux 0.13.3 完成的。

列出的所有运行时间都是使用等于批量的logstep完成的。即损失函数值仅在运行结束时取一次,因此对运行时间没有显著影响。

给那些想要自己重复分析的人一个提示:如果你想要用相同的配置分析不同数量的迭代,例如,比较 1000 次迭代和 2000 次迭代的结果,你不必运行 1000 次迭代和 2000 次迭代。相反,这可以以相加的方式完成(即,首先运行 1000 次迭代,然后再运行 1000 次),因为每次迭代都应用于同一组模型参数。

分析

批量梯度下降

我们从一个“经典”组合开始分析:批处理 GD 变体和Desc-优化器。对于 4LS-和 3LS-模型,我们将使用 mse 损失函数和 2LR 交叉熵。正如前一篇文章中所讨论的,这些模型和成本函数的配对应该工作得最好。

一些实验表明,0.2 的学习率在这种情况下效果很好。

4LS 和 3LS 的结果 使用 500 次迭代,我们可以看到 4LS 模型在大约前 200 步快速收敛,然后明显变慢。在 3LS 模型上,这种减速在不到 100 次迭代时就已经开始了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–500[图片由作者提供]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–500[图片由作者提供]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

批次 GD — 4LS、3LS[图片由作者提供]

训练 3LS 模型需要更长的时间,因为它有更多的参数。但是我们可以看到:并不是随着参数数量的线性增加。更高的努力也导致了更好的结果,在大约 2000 次迭代后显示:4LS 保持在大约 11%的精度,而 3LS 增加到几乎 20%。

但是在这两种情况下,结果都不是压倒性的(至少在评估的迭代范围内)。

2LR 的结果
2LR-型号的结果完全不同。通过 500 次迭代,我们在大约 200 秒内获得了几乎 93%的准确率。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–500[图片由作者提供]

因此,在将迭代次数增加到 4000 次后,这些数字进一步提高,达到 96%以上的准确度。

放大迭代 8000 次到 16000 次之间的学习曲线,我们可以看到在损失方面仍有良好的进展。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–16,000[图片由作者提供]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 8000–16000 次[图片由作者提供]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

批量 GD—2LR[图片由作者提供]

进行更多的迭代仍然会减少损失:从 16,000 次迭代到 32,000 次迭代会将其值从 0.033 减少到 0.013。但是准确性没有明显的变化(它保持在 96%和 97%之间,甚至随着迭代次数的增加而有所下降)。所以这可能是我们从这个配置中能得到的最好的了(真的不差!).

在这一点上请注意:损失是根据训练数据计算的,而准确性是基于测试数据的。因此,这里描述的情况可能是对训练数据的过度拟合(因此损失进一步减少),这不会导致对测试数据的更好预测(即准确性没有提高)。

随机梯度下降

现在我们来看看 GD 的下一个变种。使用随机 GD,我们应该期望大大降低运行时间,但可能也会降低质量。

4LS 和 3LS
的结果,实际上,4LS-model 在不到一秒的时间内完成 500 次迭代,达到 0.090 的损耗和 10.28%的精度。对于 3LS 模型来说,同样数量的迭代需要一秒多一点的时间,而我们得到的准确率接近 14%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–500[图片由作者提供]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–500[图片由作者提供]

仔细观察学习曲线,我们还可以看到它们不像批次 GD 中的曲线那样平滑。曲线上的小突起是由于随机 GD 并不总是以最佳方式达到最小值。

这里出现了与上面相同的问题:我们能通过更多的迭代获得(显著)更好的结果吗?用 4LS 做 1000 次迭代,精度(至少有一点)提高到 11.35%。但是在 2,000 次迭代时,我们再次变差(10.28%),在 8,000 次迭代时,我们回到 11.35%。发生了什么事?

放大学习曲线(迭代 2,000 到 4,000 次之间)很有启发性。在这里,我们可以在一个具体的例子上看到我们之前在理论上描述的:随机 GD 不以最直接的方式移动到最小值,而是以之字形路径移动。而在这里,它似乎根本没有朝着正确的方向前进。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 2000–4000 次[图片由作者提供]

对于 3LS 模型,同样的情况似乎会发生,因为我们在 500 次迭代时达到 13.94%的精度,在 1000 次迭代时回落到 13.12%。但是学习曲线表明这里的情况不同:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 500–2000 次[图片由作者提供]

曲线也是振荡的,但是有一个明显的下降趋势。因此,经过 16,000 次迭代后,我们的准确率接近 80%,经过 512,000 次迭代后,我们的准确率达到 96.64%。

除此之外,学习曲线以一种有趣的方式发展:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–16,000[图片由作者提供]

在最初几次迭代中迅速降低之后,损耗在大约 4,000 次迭代之前没有太大变化。有一个平稳期。但是我们又一次看到了显著的进步。

所以也许 4LS 显示了同样的现象,我们不应该这么早放弃?事实上,在 15,000 次和 20,000 次迭代之间的范围内,损耗开始下降得更快,在大约 60,000 次迭代以上的范围内,学习曲线再次变得更陡:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 4001–54,000 和 4001–154,000[图片由作者提供]

通过超过 1 M 的迭代,我们在 4LS 的情况下实现了 94.78%的准确度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机 GD — 4LS,3LS[图片由作者提供]

2LR 的结果 与 4LS 和 3LS 相比,2LR 在 500 次迭代时的性能明显更差:我们仅获得 9.75%的精度(但与使用批处理 GD 的大约 200 秒相比,只需要 2.7 秒)。不幸的是,如果我们做更多的迭代,结果不会变得更好。即使迭代超过 200 万次,我们也只能得到 10.1%的准确率。学习曲线显示了与这一发现一致的清晰画面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 8,001–2,048,000[图片由作者提供]

为什么我们用这种配置会得到这么差的结果?答案大概是:太无知了。随机 GD 忽略 60,000 个实例中的 59,999 个(这是一个很大的数目!).然后我们将剩下的少量信息输入损失函数。 mse 以同样的方式使用该信息的每一位来计算损失,但是 ce 本身有一些内置的“无知”:它只考虑产生的唯一热向量的一个值(最大值)。也就是说,它非常重视那个值,而忽略了其他值。从许多实例来看,这可能是一个好策略,但是在我们的情况下(尤其是从一个未经训练的模型开始),这可能只会导致随机的无意义。

小批量梯度下降

由于小批量 GD 是其他两种变体之间的折衷,它应该在我们到目前为止考虑的所有配置上都工作良好,但是比随机 GD 运行需要更长的训练时间。让我们看看,如果训练运行符合我们的期望。

4LS 和 3LS 的结果 实际上,我们对 4LS 和 3LS 模型都获得了极好的结果:在大约 50 万次迭代时,可分别实现 94.56%的准确度,在 200 万次迭代时,可分别实现 97.68%的准确度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小批量 GD — 4LS,3LS[图片由作者提供]

2LR 的结果
同样适用于 2LR 模型:这里我们在 64,000 次迭代中有 96.98%的准确度。例如,学习曲线比带有 mse 的 4LS 模型的学习曲线更加振荡,但它明显收敛:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 4,001–512,000[图片由作者提供]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小批量 GD—2LR[图片由作者提供]

结论

当我们查看每种测试配置所能达到的最佳精度时,我们会看到下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练时间的最高准确度[图片由作者提供]

每个模型都有一个 GD 变体,这并没有带来好的结果(至少在测试的迭代范围内)。剩下的两个 GD 变量导致所有模型的精度值远远超过 90%,这是非常好的。但是在训练时间上的差异是惊人的:3LS 和 mini-batch 用了将近一个小时,而 2LR 和 mini-batch 用了不到 50 秒。

在下表中,我们计算了一个名为“努力”的绩效指标,它是训练时间和准确性之间的比率。所以你可以看到这方面的佼佼者:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按“努力”排名[图片由作者提供]

另一个有趣的观察是,更多的模型参数不一定导致更好的准确性。

因此,我们可以看到我们的“构建模块”的不同配置如何导致行为和 KPI 的有趣变化。…但是我们是不是忘了什么?亚当怎么办?嗯,那是另一篇文章的素材:-)。

MNIST——主题变奏曲

原文:https://towardsdatascience.com/flux-jl-on-mnist-variations-of-a-theme-c3cd7a949f8c

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

詹姆斯·奥尔在 Unsplash 上拍照

Flux.jl 是一个 ML-stack,提供轻量级组件来创建模型并训练它们。使用 MNIST 数据集,我们将看到通过将这些组件组合在一起,构建不同的数据集分类方法是多么容易。

概观

Flux.jl

Flux.jl 是一个 100%用 Julia 写的包。它旨在建立模型,这些模型通常使用基于自动微分的迭代方法进行训练。这类模型中最常见的可能是神经网络(NN ),它使用梯度下降算法(GD)的一种变体来训练。

与其他一些包含现成的“机器”来构建和训练模型的 ML 库相比,Flux 提供了一套轻量级的乐高积木,您可以根据自己的特定需求将它们组装在一起。

在本文中,我将展示这种类似 lego 的方法如何应用于构建几个 NN 模型(准确地说是 多层感知器 )来对图像进行分类,并实现 GD 算法的不同变体来训练它们。

MNIST 数据集

为此,我们将使用众所周知的 MNIST 数据集,它由 70,000 张手写(和标记)数字图像组成。这些图像中的 60.000 个用作训练数据,剩余的 10.000 个实例将用于测试模型。每个图像由 28×28 灰度像素组成。

使用MLDatasets包可以轻松加载整个数据集:

第一个表达式(第 3 行)自动获取带有相应标签的 60.000 个训练图像,第 4 行的变体使用split = :test获取其余的用于测试。所以train_x_raw是一个 28x28x60000 元素的数组,test_x_raw经过这次运算后是一个 28x28x10000 元素的数组。标签存储在train_y_rawtest_y_raw中,它们分别是大小为 60.000 和 10.000 的数组,包含 0 到 9 之间的整数。

函数convert2image允许我们显示这样的图像。我们通过调用convert2image(MNIST, train_x_raw[:,:,10])得到例如第 10 个训练图像:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

手写数字 4 的灰度图像[图片由作者提供]

相应的标签(4)在train_y_raw[10]

这张图片(在train_x_raw[:,:,10]中)的数据只是一个 28x28 元素的矩阵,包含 0 到 1 之间的数字,每个数字代表一个灰色阴影。

模型

如上所述,我们的目标是创建能够对这些图像进行分类的不同模型。也就是说,我们的模型的输入将是一个手写数字的图像(以 28x28 矩阵的形式),输出是一个 0 到 9 之间的数字,告诉我们图像包含哪个数字。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基本处理流程[图片由作者提供]

数据

不幸的是,神经网络既不直接处理矩阵,也不以我们想要的方式输出数字。所以我们必须对我们的数据做一些调整。

神经网络的输入必须是包含所有值的(列)向量。因此,我们必须转换我们的 28x28 矩阵,并将每张图片的 28 列堆叠在一起。结果是一个 784 元素的向量(28 x 28 = 784)。通量函数flatten()就是这样做的:

train_x = Flux.flatten(train_x_raw)
test_x  = Flux.flatten(test_x_raw)

这分别产生一个 784x60000 和一个 784x10000 元素的数组。

用于这种分类问题的 NN 的输出通常是所谓的独热向量。这是一个位向量(在我们的例子中有 10 个元素,因为我们有 10 个不同的类),其中正好有一位是 1,所有剩余的位都是 0。值为 1 的元素表示相应的类别,即如果第一位为 1,则表示“数字 0”,如果第四位为 1,则表示“数字 3”,依此类推。

通量函数onehotbatch()对整个标签阵列进行这种转换:

train_y = Flux.onehotbatch(train_y_raw, 0:9)
test_y  = Flux.onehotbatch(test_y_raw, 0:9)

自变量0:9告知必须由结果独热向量表示的数字的(可能)范围。这些语句的结果分别是 10x60000 和 10x10000 位数组。

因此,我们为图像预测调整的处理管道看起来像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

改编的处理流程[图片由作者提供]

注意:在现实世界的应用中,神经网络很少会产生“真正的”单热点向量。相反,将产生包含概率的向量,并且如果 NN 工作良好,这些值中的一个将接近 1,而所有其他值将接近 0。

多层感知器网络

我们想要使用的模型是所谓的多层感知器网络 (MLP)。这些是“经典”神经网络,由几层神经元组成,其中一层的每个神经元连接到下一层的所有神经元(也称为全连接密集网络):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个三层感知器[图片由作者提供]

不同的 MLP 在

  • 层数,
  • 每层中神经元的数量
  • 所谓的激活函数,其在传递到下一层之前应用于神经元的结果;这种激活功能可能因层而异。

MNIST 分类模型

在我们的例子中,第一层中输入值的数量已经由输入数据固定(到 784),最后一层必须有 10 个神经元(产生 10 个输出值),因为我们有 10 个类。但是我们可以自由选择模型的其他特征。

为了简单起见,我选择了三个可以在互联网上其他地方找到的模型:一个来自格兰特·桑德森在他的 YouTube 频道“3Blue1Brown”上关于神经网络的精彩视频(“T10”在这里你可以找到第一个及其精彩的可视化),第二个(改编自)来自我们主题的这个深度处理,第三个来自 Flux 文档

具体来说,我们有以下型号(我根据它们的主要特征命名)

  • 4LS :一个 4 层模型,使用 16 个内层节点和s 形激活函数
  • 3LS :一个三层模型,使用 60 个内层节点和 sigmoid 激活函数
  • 2LR :使用内层 32 个节点和 relu 激活功能的 2 层模型

旁注:为了更接近 Flux 中的以下模型定义,我没有将输入和输出作为一个单独的层(在别处是这样做的)。

使用通量的方法,我们可以将这三个模型定义如下(在注释中,您可以看到每个模型的参数数量):

第三种模式(model2LR)仅在第一层使用relu-激活功能。在第二层上,没有指定这样的功能。这意味着使用默认流量(身份)。因此,该层可能会产生[-∞,∞]范围内的值,这不是我们想要的。因此,softmax-函数应用于结果,将它们标准化到从 0 到 1 的范围,并确保它们的总和为 1。

使用sigmoid-功能激活的前两个型号是相当“传统”的 NNs,而现在sigmoid已经大部分被relu取代。

梯度下降训练

训练这样的模型意味着找到最佳的参数值,以便该模型能够以高精度进行预测。

价值函数

为了测量一组特定的参数相对于这个目标的表现有多好,我们需要一个所谓的成本函数。该成本函数 C 将利用特定参数集做出的预测 ŷ 与训练数据中的实际类别 y 进行比较,并在此基础上计算该“距离”(= C(ŷ,y) 的指标。即成本函数的结果越小,选择的参数越好。所以我们的目标是找到一组使成本函数最小的参数。

在这种情况下使用的典型成本函数是:

mse 通常与具有sigmoid激活功能的型号一起使用,而 ce 通常与relu激活功能一起使用。这些配对背后的基本原理是,在这些星座中,可以保证成本函数是凸的,即它恰好有个最小值。因此,用于寻找(全局)最小值的算法将不会被可能的其他(局部)最小值分散注意力。

话虽如此,我不会隐瞒“损失函数中局部最小值的存在在实践中似乎不是一个主要问题”(这是“某种程度上的理论谜团”),正如 LeCun 等人在他们关于“ 基于梯度的学习应用于文档识别 ”的论文中所写的那样。IEEE 的,1998 年 11 月)。因此,也使用其他配对。

梯度下降

用于训练模型的梯度下降算法的基本思想(即,为最小化成本函数 C 的模型找到一组参数)如下:

  1. 从任意选择的一组参数开始 W₀
  2. 在位置w₀(∇c(w₀):
    w₁=w₀–α∇c(w₀)向 C梯度的反方向移动一小步(α),计算出一组新的“改良”参数 W₁

由于位置 W₀ 处的 C (矢量)的梯度指向 C 最陡的方向,我们必须向相反方向移动以达到最小值(因此公式中为负)。

重复第 2 步,直到 C 向最小值收敛(或者直到我们有一组参数 Wi 为我们想要的应用提供足够的精度):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

梯度下降算法的迭代步骤

步长α称为*学习率。*启发式选择。如果太大,算法可能会“超调”。如果太小,算法收敛非常慢。在实践中,用 0.1、0.01 和 0.001 这样的值进行实验并根据结果进行调整是一个好主意。

Flux.jl 中的实现

那么,如何使用 Flux.jl 在 Julia 中实现这些概念呢?模型(model4LSmodel3LSmodel2LR)已经定义,训练数据已经可以使用(在train_xtrain_y)。

剩下的是成本函数的定义(Flux 称之为损失函数)和梯度下降算法的实现。Flux 中每个 GD 实现的核心是Flux.train!()函数。它在 GD 的每次迭代中被调用,并计算模型参数的(稍微)改进版本(如上所述)。必须将以下参数传递给train!():

  • 成本函数 loss
  • 模型参数 params ( Wi 来自最后一次迭代步骤或第一次运行时的初始参数)
  • 训练数据 data(在train_xtrain_y中)
  • 一个所谓的优化器opt;这是用于产生模型参数的新(改进)版本的公式,如上所示(梯度下降算法的迭代步骤)。上面的公式是最基本的变体。近年来,已经开发出了更复杂的变体。与基本变体的主要区别在于,它们在每个迭代步骤上动态地调整学习速率α。
    除了我们在Descent(α)中得到的基本变体,我们将应用一个最先进的版本: ADAM (=自适应矩估计),它在ADAM(α)中可用。

我们可以使用Flux.params()功能访问每个模型的参数:

params4LS = Flux.params(model4LS)
params3LS = Flux.params(model3LS)
params2LR = Flux.params(model2LR)

成本函数 msece 也在 Flux 中预定义(如Flux.Losses.mse()Flux.Losses.crossentropy())。由于train!()期望一个可以直接传递训练数据(train_xtrain_y)的成本函数,而两个预定义的损失函数期望基于train_x的模型预测作为它们的第一个参数,我们必须定义一些“转换”:

loss4LSmse(x,y) = Flux.Losses.mse(model4LS(x),y)
loss4LSce(x,y)  = Flux.Losses.crossentropy(model4LS(x),y)
loss3LSmse(x,y) = Flux.Losses.mse(model3LS(x),y)
loss3LSce(x,y)  = Flux.Losses.crossentropy(model3LS(x),y)
loss2LRce(x,y)  = Flux.Losses.crossentropy(model2LR(x),y)

由于 mse 成本函数不能很好地与使用relu的神经网络一起工作,我们只为 2LR 模型定义了基于 ce 的损失函数。

现在,我们可以通过调用这些函数来查看模型的初始(随机)参数损失有多大。例如,loss4LSmse(x_train, y_train)在我的环境中提供 0.2304102 的值。但是由于初始参数是随机选择的,如果你在电脑上尝试,你会得到另一个值。因此,这个值只是用于(相对)比较,以便查看在 GC 的一些迭代之后它会发生多大的变化。

实现梯度下降的变体

批量梯度下降

然后我们准备使用 Julia 和 Flux.jl 实现 GD 的第一个版本:

我们梯度下降的第一个版本train_batch()将训练数据Xy、损失函数loss、优化器opt、模型参数params和我们想要训练的迭代次数epochs作为参数。GD 的这个实现本质上是在每次迭代中调用train!()的 for 循环。

由于train!()期望训练数据被包装在数组的元组中,我们在第 2 行准备了这个结构。就是这样!

例如,为了使用 mse 成本函数和标准 GD 公式训练我们的 4LS 模型,在 100 次迭代中学习率为 0.1,我们必须写出:

train_batch(train_x, train_y, loss4LSmse, 
            Descent(0.1), params4LS, 100)

执行此语句后,我们应该看到损失值有所改善。在我的环境中,我通过调用loss4LSmse(x_train, y_train)得到一个值 0.12874180。所以我们可以在这 100 次迭代中减少一半的损失。

GD 的这种变体被称为批次梯度下降,因为在每次迭代中,损失函数的梯度在完整的训练数据集上进行评估。即所有 60.000 幅图像都用于计算该值(在每次迭代中!).

显然,这在计算上相当昂贵!因此,开发了 GD 的其他变体。

随机梯度下降

随机梯度下降算法在每次迭代中仅使用训练数据的单个(随机选择的)实例(即一个单个图像)来评估损失函数的梯度。这显然成本更低。

考虑到仅仅一个实例(而不是 60.000)当然不会产生损失函数的实际梯度。即合成向量不会指向该函数最陡的方向。但实际上,产生的矢量并没有那么差。

使用这种方法,我们不会得到到达最小值的最直接的路径,而是到达(或接近)那个点的曲折路径。即使我们没有直接达到最小值,结果通常也足够接近,并且我们以相当低的成本达到最小值。所以这通常是首选的变体。

实现如下所示:

小批量梯度下降

GD 的第三个变体是我们看到的前两个变体之间的一个很好的妥协。它既不作用于整个数据集,也不作用于每次迭代的单个实例,而是选择例如 100 个实例的随机样本,并在此基础上计算梯度。这个样品被称为小批量

这个小批量的大小可以自由选择。因此,为此我们需要一个额外的参数batchsize:

我们在这里实现了一个简单的策略来提取小批量:在第 4 行中选择整个数据集内的一个随机位置,然后我们从该位置(第 5+6 行)开始提取一个大小为batchsize的小批量。

更高级的策略是随机选择小批量的所有实例。这可以通过使用焊剂[DataLoader](https://fluxml.ai/Flux.jl/stable/data/mlutils/#DataLoader)来实现。

GD 的第三个变体当然是最通用的实现,我们可以用train_minibatch()来表示前两个变体,如下所示(因此我们实际上只需要小批量 GD 的实现):

train_batch(X, y, loss, opt, params, epochs) = 
        train_minibatch(X, y, loss, opt, params, epochs, size(X)[2])train_stochastic(X, y, loss, opt, params, epochs) = 
        train_minibatch(X, y, loss, opt, params, epochs, 1)

结论

我们已经看到 Flux.jl 如何用于预处理数据和定义模型。此外,使用构建模块,如

  • 损失函数
  • 优化者
  • train!()-功能

我们可以很容易地实现 GD 算法的不同变体。

在后续文章中,我将结合不同的损失函数、优化器和其他参数来评估这里提出的模型,以了解需要花费多少努力来训练它们,以及从 MNIST 数据集中识别手写数字可以达到什么精度。所以,敬请期待!

MNIST——亚当呢?

原文:https://towardsdatascience.com/flux-jl-on-mnist-what-about-adam-fb78e7f02acd

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

西蒙·李Unsplash 上的照片

到目前为止,我们已经看到了使用标准梯度下降优化器的性能分析。但是,如果我们使用像亚当这样更复杂的方法,我们会得到什么结果呢?

在关于 MNIST 的flux . JL——主题的变体中,我展示了三种用于识别手写数字的神经网络,以及三种用于训练这些网络的梯度下降算法(GD)。

关于 MNIST 的后续文章flux . JL——性能分析显示了使用不同参数的每个模型的性能以及训练它们需要付出的努力,这取决于所应用的 GD 变体。GD 算法使用标准的 GD 学习公式,在该分析中利用恒定的学习速率。

现在,我们将使用高级学习公式(现在称为“优化器”)执行此分析,并将结果与使用标准公式实现的性能进行比较。我们将使用的优化器名为 ADAM (= 自适应矩估计),由 Diederik P. KingmaJimmy Ba2015 年发表。它适应每次迭代的学习速率,是目前该领域最复杂的方法之一。

与最后的分析类似,我们将应用所有三种 GD 变量进行训练:批量 GD、随机 GD 和小批量 GD。

批量梯度下降

4LS-型号

使用 batch GD 将 ADAM 应用于 4LS 模型,导致了显著的改进:仅用 500 次迭代,我们就获得了 74.16%的准确率,提高到 91%以上。相比之下,标准的Descent优化器停留在 11.35%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

批量 GD—4LS[图片由作者提供]

学习曲线说明了这种行为:左边是标准优化器,迭代 500 次,右边是 ADAM,迭代 1000 次。ADAM 非常快地将损失降低到低于 0.1 的值,然后稍微停滞,直到大约迭代 300,在那里它再次开始降低。Descent也相当快,直到低于 0.1(请注意 y 轴上的不同缩放比例),然后停滞不前。除此之外,使用Descent减少损失并不能转化为准确性的提高(在测试数据集上)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1-500(下降)和 1-1000(亚当)[图片由作者提供]

3LS 型号

对于 3LS 模型,我们得到了类似的结果。ADAM 在这里也显著地改进了结果:在 500 次迭代时,我们获得了 95.66%的准确度,在 1000 次迭代时,准确度可以略微提高到 96%以上。另一方面,标准优化器只能产生低于 20%的精度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

批量 GD—3LS[图片由作者提供]

学习曲线也显示了这一点:两个优化器都非常快地将损耗降低到低于 0.1 的值(再次注意 y 轴上的不同缩放),ADAM 甚至比Descent更快,然后降低得更多。

与 4LS-model 相比,ADAM 在这里停滞得更快,但在这一点上已经取得了比 4LS 更好的准确性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1-500(下降)和 1-1000(亚当)[图片由作者提供]

2LR 型号

在 2LR-model 上,我们已经用标准优化器得到了很好的结果,并且我们用 ADAM 达到了相同的准确度水平(超过 96%)。但是 ADAM 已经迭代了 500 次(然后略有下降),而标准优化器需要 4,000 次迭代才能达到这个水平。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

批量 GD—2LR[图片由作者提供]

在这种情况下,除了 ADAM 在开始时下降较快之外,学习曲线非常相似:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1-1,000 次(下降和亚当)[作者图片]

随机梯度下降

对于随机 GD,标准优化器在 4LS-和 3LS-模型上提供了相当好的结果。让我们看看,亚当是否能做得更好。

4LS-型号

对于两个优化器,4LS 模型的学习曲线具有相同的基本形状,但是 ADAM 导致更强的振荡。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–1,024,000 次(下降和亚当)[图片由作者提供]

ADAM 用更少的迭代给了我们更高的精度,但最终我们没有得到从Descent得到的质量(在 200 万次迭代中,94.16%对 92.12%)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机 GD—4LS[图片由作者提供]

3LS 型号

对于 3LS 模型,我们得到了几乎相同的情况:基本上相似的曲线,其中 ADAM 在开始时更快地收敛到较低的损失值(并且以这种方式振荡得更多):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–1,024,000 次(下降和亚当)[图片由作者提供]

这反映在我们能够达到的精度上。同样,ADAM 也没有达到我们用标准优化器达到的水平(在 200 万次迭代中,93.73%对 97.64%):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机 GD—3LS[图片由作者提供]

2LR 型号

用标准优化器训练 2LR 模型是失败的。我们从来没有达到 20%以上的准确率。在这种情况下,ADAM 是一个真正的改进,在大约 50 万次迭代后达到了 87%以上的精度(然后又变得更差)。但是我们不能像其他配置那样达到 95%以上。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机 GD—2LR[图片由作者提供]

使用 ADAM 的学习曲线显示出剧烈的波动,它并不真正符合上表中的数字,因为我们做的迭代次数越多,损失似乎越大。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 1–1,024,000 次(下降和亚当)[图片由作者提供]

前 128,000 次迭代的 100 移动平均线更好地显示了该部分发生的情况。至少在最初的 40,000 次迭代中,损耗显著降低。但是这仍然不能解释为什么精度在高达大约 50 万次迭代的范围内提高。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

移动 100-平均值-迭代 1–128,000(ADAM)[图片由作者提供]

小批量梯度下降

小批量 GD 在所有三种模型上都取得了非常好的结果。所以留给亚当的空间并不多。让我们来看看不同之处。

4LS-型号

正如上面在其他配置中看到的,ADAM 在开始时收敛得更快(500 次迭代时 92.46%的准确性),但没有达到标准优化器的水平(200 万次迭代时 94.49%对 93.28%):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小批量 GD—4LS[图片由作者提供]

学习曲线反映了这些发现(同样,亚当导致了更多的振荡):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 4,001–512,000(下降)和 1–512,000(亚当)[图片由作者提供]

3LS 型号

3LS 型号也是如此。ADAM 在 500 次迭代时已经达到了 94.47%的准确度,但是在 200 万次迭代时最终稍差(97.68%对 96.17%)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小批量 GD-3LS[图片由作者提供]

以类似的方式,这反映在学习曲线中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 4,001–512,000(下降)和 1–512,000(亚当)[图片由作者提供]

2LR 型号

对于 2LR 型号,情况有点不同。亚当在开始时并没有收敛得更快。同样,它也达不到与标准优化器相同的准确性水平(97.0%对 91.33%)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小批量 GD—2LR[图片由作者提供]

学习曲线在这里显示了(与随机 GD 一样)显著的振荡:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代 4,001–512,000(下降)和 1–512,000(亚当)[图片由作者提供]

结论

血统 vs .亚当

下图概述了使用标准优化器(左)和 ADAM(右)获得的最佳结果。它显示了与训练时间相关的准确性。在这里,我们可以看到,亚当做得很好:几乎所有的结果都在左上角。即以相对较小的努力实现了高精度。标准优化器提供了更广泛的结果。但最终它产生了一些最好的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

准确度与训练时间(下降和亚当)[图片由作者提供]

顶级表现者

下图显示了表现最好的人(准确率> 90%)。最好的结果是 3LS/下降(亮蓝色),然后是 2LR/下降(亮黄色)。4LS 变异体(绿色)通常不太好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

准确性与训练时间——最佳表现者[图片由作者提供]

努力

最后,在最近的分析中,我计算了一个名为“努力”的性能指标,这是训练时间和准确性之间的比率(描述了达到一定水平的准确性需要多少训练时间)。所以你也可以看到这方面的佼佼者:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按“努力”排名[图片由作者提供]

摘要

有趣的是,在标准优化器失败的三种情况下,我们都可以通过 ADAM 获得明显更好的结果。但在所有其他情况下,亚当都没有达到我们用Descent获得的水平。在这些情况下,迭代次数越少,收敛速度越快,但最终还是于事无补。因此,我们可以得出结论,这两种优化器可以很好地互补,但没有一种优化器可以适用于所有情况。

焦点损失:交叉熵的更好选择

原文:https://towardsdatascience.com/focal-loss-a-better-alternative-for-cross-entropy-1d073d92d075

据说焦点损失在许多情况下比交叉熵损失表现得更好。但是为什么交叉熵损失会失败,焦点损失如何解决这些问题,让我们在这篇文章中找到答案

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

梯度下降,由 Unsplash 上的 Rostyslav Savchyn 拍摄

损失函数是计算预测值与实际值偏离程度的数学方程。较高的损失值表明模型存在重大误差,而较低的损失值表明预测相当准确。目标是尽可能减少损失函数。模型使用损失函数来学习可训练的参数,例如权重和偏差。因为参数的权重更新方程具有损失函数相对于权重或偏差的一阶导数,所以该函数的行为将对梯度下降过程产生显著影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

权重更新公式,图片来源:作者

现在有很多损失函数可用。他们每个人都有不同的数学公式和不同的惩罚模型错误的方法。每种方法都有优点和缺点,在决定使用哪种功能之前,我们必须权衡利弊。

现在我们已经定义了损失函数,让我们来看一下分类交叉熵损失引起的问题以及焦点损失如何解决它们。

分类交叉熵损失

分类交叉熵损失传统上用于分类任务。顾名思义,这个的基础就是熵。在统计学中,熵是指系统的无序性。它量化了变量的模型预测值的不确定程度。所有概率估计的熵之和就是交叉熵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

交叉熵的表达式,图片来源:作者

其中 Y 是真实标签,p 是预测概率。

注意:上面显示的公式是针对离散变量的。在连续变量的情况下,求和应该用积分代替。

对数图清楚地表明总和将是负的,因为概率范围从 0 到 1。因此,我们添加一个减号来反转求和项的符号。log(x)和-log(x)的图形如下图(x)所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

红色为 log(x ),蓝色为-log(x ),图片来源:作者。使用 Desmos 绘制的图表。

交叉熵损失表现不佳的情况

  • 阶级不平衡继承过程中的偏见。多数类示例将主导损失函数和梯度下降,导致权重在模型的方向上更新,在预测多数类时变得更有信心,同时不太强调少数类。平衡交叉熵损耗处理这个问题。
  • 分不清的例子。困难的例子是那些模型反复出现巨大错误的例子,而简单的例子是那些容易分类的例子。因此,交叉熵损失未能更多地关注硬例子。

平衡交叉熵损失

平衡交叉熵损失为每个类别添加一个加权因子,用希腊字母 alpha,[0,1]表示。α可以是逆类频率或由交叉验证确定的超参数。alpha 参数取代了交叉熵方程中的实际标签项。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

平衡交叉熵的表达式,图片来源:作者

尽管这个损失函数解决了类不平衡的问题,但是它不能区分难的和容易的例子。通过焦点丢失解决了问题。

焦点损失

焦点损失侧重于模型出错的例子,而不是它可以自信地预测的例子,确保对困难例子的预测随着时间的推移而提高,而不是对简单例子过于自信。

这到底是怎么做到的?焦损失通过所谓的向下加权来实现。向下加权是一种减少简单示例对损失函数的影响的技术,导致对困难示例的更多关注。这种技术可以通过向交叉熵损失添加调制因子来实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表情为焦损,图片来源:作者

其中,γ (Gamma)是使用交叉验证调整的聚焦参数。下图显示了不同γ值下焦损失的表现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

向下加权随着γ的增加而增加,图片来源:焦损失研究论文

伽玛参数是如何工作的?

  • 在错误分类样本的情况下,pi 很小,使得调制因子近似或非常接近 1。这使得损失函数不受影响。因此,它表现为交叉熵损失。
  • 随着模型的置信度增加,即 pi → 1,调制因子将趋向于 0,从而降低了分类良好的示例的损失值。聚焦参数γ ≥ 1 将重新调整调制因子,使得简单的例子比困难的例子权重更低,减少它们对损失函数的影响。例如,假设预测概率为 0.9 和 0.6。考虑到γ = 2,为 0.9 计算的损耗值为 4.5e-4,向下加权系数为 100,为 0.6 计算的损耗值为 3.5e-2,向下加权系数为 6.25。从实验来看,γ = 2 对于焦损失论文的作者来说效果最好。
  • 当γ = 0 时,焦损相当于交叉熵。

在实践中,我们使用聚焦损失的α平衡变体**,其继承了加权因子 α和聚焦参数 γ的特征,产生比非平衡形式稍好的精度。**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

α平衡焦损失的表达式,图片来源:作者

震源损失自然地解决了类别不平衡的问题,因为来自多数类别的样本通常容易预测,而来自少数类别的样本则很难预测,这是因为缺少来自支配损失和梯度过程的多数类别的数据或样本。由于这种相似性,焦点损失可能能够解决这两个问题。

感谢您阅读这篇文章😃。祝你有愉快的一天。

关注这五个领域,进入数据科学领域

原文:https://towardsdatascience.com/focus-on-these-five-areas-to-break-into-data-science-4a7ad39818f8

有抱负的数据科学家们,为了获得第一份数据科学工作,以下是你们必须知道的事情

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

杰基·霍普Unsplash 上的照片

这是我在网上找到的一个问题的答案:

作为一名有志于进入 AI/ML 和数据科学领域的学生,我应该关注什么?

数据科学是一个复杂且快速发展的领域。这是一项艰巨的任务,尤其是像我这样自学成才的人。我没有结构化的课程,所以确定重点领域对我来说有点像猜谜游戏。

因此,为了让任何人都容易,下面是你应该关注的五个领域,以获得你的第一份数据科学工作。

想进入数据科学领域吗?参加我的免费数据科学基础课程,开始回归和分类项目!

数据操作

首先,你需要知道如何获取和操作你的数据。

您可能必须访问数据库来提取数据集。了解 SQL 基础知识是最基本的。至少,你应该知道如何:

  • 查询两个日期之间的数据
  • 将表格连接在一起
  • 过滤您的表格

当然,掌握窗口函数和公共表表达式(cte)会更好。

然后,一旦你有了数据,你必须知道如何操作它。现实生活中的数据集令人讨厌。它们有缺失值、异常值,并且列有模糊的标签。因此,你必须知道如何处理这些不同的问题,并提取有意义的信息。

对于任何数据操作,熊猫和 NumPy 都是必不可少的。

了解你的算法

第二,你必须知道你的算法。

回归、分类和聚类任务对于数据科学家来说很常见。你必须知道如何实现每个任务的算法。您还应该对哪种算法通常工作得最好有一些直觉。

这里列出了你必须知道的基本算法:

回归

  • 线性回归
  • 套索和岭回归
  • 决策树(随机森林、装袋、助推)

分类

  • 逻辑回归
  • 决策树
  • SVM

聚类

  • k 均值聚类
  • 分层聚类

我没有在这个列表中包括任何深度学习,因为入门级的工作很少需要它。我的建议是,如果需要的话,在进入深度学习之前,先关注基础知识。

解释您的评估指标

第三,你必须对你的评价指标有深刻的理解。

知道如何解释你的评估标准是获得第一份工作的关键。我们必须认识到,作为数据科学家,我们与非技术人员交流。对他们来说,回忆、精确、F1 分数或平均寿命没有任何意义。把我们的科学成果翻译成通俗易懂的语言是我们工作的一部分。

此外,您必须知道如何为您的问题选择合适的指标。例如,我曾经采访过一些候选人,他们使用精确度来评估不平衡数据集上的分类模型。在这里,使用绝对指标是一个巨大的错误。

因此,您必须训练自己的技能,为正确的情况选择正确的指标。

磨练科学方法

职位名称是这样写的:我们是科学家。重要的是表明我们遵循科学的方法,我们进行实验。

这个领域有很多方面。首先,让我们考虑创建一个健壮的测试集。首先,你的测试集应该在整个实验中保持不变。它还应该代表您的解决方案的实际应用。

例如,假设您正在开发一个聊天机器人。然后,你的测试集不应该只包含完美的句子,没有语法或标点符号错误。在现实中,人们会犯错误,使用缩写,有时根本不用标点符号!您的测试集必须反映您的算法必须处理的数据类型。

另一种表明你遵循科学方法的方式是一次修改一个变量。例如,不要在设计新功能的同时改变算法。如果您的评估指标提高或降低,您将无法判断这是由于新特性还是新算法。

永远保持一切不变,一次改变一个变量。当然,跟踪你的实验,并评估每一个实验。

准备好解释你的工作

最后,你必须能够解释你工作的每一步。

同样,我面试的候选人不能证明他们在项目中采取的步骤是正确的!做出正确的决定是不够的,你必须能够证明它是正确的。

在项目的每一步,你都应该问自己:我为什么要这样做?

这将有助于你深入理解你的工作,并证明你的想法。

你必须解释为什么放弃那个专栏。

你必须解释为什么用平均值而不是中值来填充缺失值。

你必须知道你为什么设计这个新变量。

你必须解释你为什么选择这个评估标准。

你必须解释你是如何选择冠军模特的。

如果你发现自己在没有数据支持的情况下,出于直觉采取了一个步骤,那么这可能是个坏主意。

实验失败并知道原因,好过实验成功却不知道原因。

另外,你可以肯定这种类型的问题会在面试中出现!我知道我喜欢问他们,因为这将优秀的数据科学家与平庸的数据科学家区分开来。

你有它!专注于这五个领域,保证你能打入数据科学领域。

我希望这篇文章对你有帮助!

确保订阅更多文章、免费赠品、课程通知和 VIP 活动邀请!

另外,查看我的免费课程,开始学习数据科学!

叶和 Choropleth 地图:从零到专业

原文:https://towardsdatascience.com/folium-and-choropleth-map-from-zero-to-pro-6127f9e68564

使用带有图层控制和自定义工具提示的 follow 创建交互式 Choropleth 地图的分步教程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源: Pixabay

介绍

Choropleth 地图可能是最流行的地理空间数据可视化之一。它是一种专题地图,在地图上的预定义区域使用不同的阴影图案或颜色来表示与变量或指标相关的地理特征。

它可以绘制在世界地图、美国地图上,或者按照不同的地理区域(如州、县、邮政编码等)进行划分。下图展示了一些来自维基百科的 choropleth 地图的例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:维基百科

在 Python 中,有几个图形库可以用来绘制 choropleth 地图,如 follow、Plotly、Matplotlib 等。其中,follow 是一个高度专业化的地理空间可视化库。它是健壮的和可定制的,可以在任何地理区域或国家以各种风格和设计绘制 choropleth 地图。对于想要开始使用 Python 进行地理空间可视化的初学者来说,这是一个理想的选择。

在本教程中,我们将学习 learn 的核心概念,并使用它创建一个 choropleth 地图,该地图通过自定义工具提示和图层控件将美国县级的新冠肺炎案例可视化。我们将要创建的 choropleth 地图如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

先决条件

#1:安装叶片:

您可以使用下面的命令,根据 folio 的文档页安装 folio:

$ pip install folium

或者

$ conda install folium -c conda-forge

#2:安装 GeoPandas:

根据 GeoPandas 的文档页面,建议使用 Anaconda / conda 安装 Geopandas。首先按照指令下载并安装 Anaconda,然后使用以下命令安装最新版本的 GeoPandas:

conda install geopandas

下载数据

制作 Choropleth 地图需要两种主要的输入:

  1. 包含地理区域标识符(例如,国家名称、县 fips 代码、邮政编码等)的表格数据集。)和我们希望在地图中可视化的指标(例如,人口、失业率等。)
  2. GeoJSON 文件,包含几何信息并定义预定义地理区域的边界(例如,国家、美国县、邮政编码等)。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

现在,我们已经了解了数据输入要求和 GeoJSON 文件的作用,我们可以继续下载教程所需的两个数据集。这两个文件都是开放的数据集,可以免费下载和使用。

数据集 1 : 美国新冠肺炎县一级的社区传播

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据来源:疾病预防控制中心

数据集 2 : 美国各县的 GeoJSON 文件

你可以去public.opendatasoft.com,向下滚动到“地理文件格式”部分,下载美国各县的 GeoJSON 文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

导入库并读取数据

让我们导入所有必要的库并将数据读入 Python。请注意,为了读取 GeoJSON 文件,我们需要使用 GeoPandas,这是一个开源库,专门用于处理地理空间数据类型,使使用 python 处理地理空间数据变得更加容易。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

接下来,让我们仔细看看 covid_df 数据帧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

该数据集跟踪每个 report_date 在美国县一级的新冠肺炎指标。由于我们正在创建一个静态 choropleth 地图,因此我们将数据框限制为最近的 report_date(即我们示例中的 12/29/2021)。

我们还需要做一些数据争论来清理一些数据字段。我们会将“fips_code”的数据类型更改为 string,如果缺少前导零,则填充前导零。我们还将创建两个具有正确数据类型和格式的新列“new_cases_7days”和“pct_positive_7days ”,并删除原始列。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清理后的 covid_df(图片由作者提供)

最后,让我们将 geojson 与 covid_df 结合起来,以创建最终数据集,为可视化做好准备。通过连接这两个数据集,我们确保 covid_df 数据框中的 fips_code 与 GeoJSON 文件中的 coty_code 完全匹配,这在稍后使用 follow 绘制 choropleth 地图时至关重要。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可视化的最终数据框(图片由作者提供)

使用 leav 创建 Choropleth 地图

第一步:启动一个基础叶子地图

要使用 leav 创建 choropleth 地图,我们需要首先使用 leav 启动一个基础地图。映射(),然后向其中添加图层。我们可以通过使用 location 参数将起始坐标传递给地图。我们在这里选择的起始坐标(40,-96)大致代表美国地图的中心。

我们可以从内置图块集列表中选择所需的地图图块(例如 tiles="Stamen Terrain "),默认为 OpenStreetMap,或者只需将图块选项保留为“无”。我们还可以使用 zoom_start 参数设置地图的初始缩放级别。

步骤 2:将 Choropleth 地图图层添加到底图中

基础地图最初是空的。我们可以使用 leav 创建 choropleth 地图图层。Choropleth()并使用 add_to()方法将其添加到基本地图中。在叶子里。Choropleth()函数,有几个导入参数我们需要指定:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

请注意,在上面的代码中,我们可以使用 quantile()和 tolist()创建一个自定义色标,并通过 threshold_scale 参数轻松传递我们的自定义色标,而不是使用固定值范围。仅用几行代码,我们就用 Folium 创建了一个带有自定义色阶的基本 choropleth 地图!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

步骤 3:向地图添加自定义工具提示

我们上面创建的基本地图看起来相当不错!然而,目前,当用户悬停在地图上时,没有工具提示显示每个县的信息,如县名、Covid 病例数、阳性率等。这将是一个很好的添加到地图的功能,以使可视化更具交互性和信息量。

要添加自定义工具提示,我们需要使用 follow . features . geo JSON()方法和 GeoJsonTooltip 函数来设置“字段”和“别名”参数。这些字段将是我们想要在悬停工具提示中显示的 df_final 中的任何列,而别名将是我们给予这些字段的标签/名称。

请注意,我们还可以将 HTML 元素传递给“aliases”参数来格式化工具提示。例如,我们可以使用
将长格式文本分成两行,如下面的代码所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

添加层控制以在指标之间切换

到目前为止,我们的 choropleth 图仅可视化了来自 df_final 数据帧的一个指标——“新病例 _ 7 天”。如果我们也有兴趣了解不同县之间“pct_positive_7days”指标的变化情况会怎样?

我们可以按照上一节中显示的相同步骤,为“pct_positive_7days”创建另一个映射。但是有没有什么方法可以在同一个地图上用一个切换按钮显示多个指标,这样我们就不需要分别显示多个地图了?

答案是肯定的,解决问题的秘密武器是使用叶子。FeatureGroup()和 leav。LayerControl()。

叶子。FeatureGroup()方法创建要素组图层。我们可以把东西放在里面,把它们作为一个单独的层来处理。我们可以创建多个要素组图层,每个图层代表一个我们希望在地图上显示的度量。然后,我们可以添加 LayerControl 来勾选/取消勾选 FeatureGroup 图层,并在不同的指标之间切换。

让我们看看如何通过下面的代码实现这一功能:

步骤 1: 我们启动一个底图,创建两个名为 fg1 和 fg2 的特征组图层,并将它们添加到底图中。我们为将在 LayerControl 中显示的每个 featureGroup 图层命名。“覆盖”参数允许您将层设置为覆盖层(勾选图层控制中的复选框)或基础层(勾选单选按钮)。

步骤 2: 我们将第一个 choropleth 地图图层(用于“新病例 7 天”指标)添加到 fg1:

**第三步:**我们将第二个 choropleth 地图图层(用于“pct_positive_7days”指标)添加到 fg2。除了与“pct_postive_7days”指标相关的一些更改之外,代码结构与步骤 2 中的相同。

步骤 4: 我们添加图层控制到地图中。请注意,在代码中,我还添加了一个平铺层来选择暗模式和亮模式。我们还可以将交互式地图保存到 HTML 文件中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Choropleth 地图(图片由作者提供)

收尾思路

Folium 是我学习并用于地理空间可视化的第一个地理空间绘图工具,我非常喜欢它!它易于使用,但非常强大和灵活。这是一个很棒的 python 库,可以让您开始了解地理空间数据可视化领域中使用的概念和技术,也是添加到您的数据科学家工具箱中的一个很好的工具。感谢阅读,我希望你喜欢这篇文章!

参考和数据来源:

  1. Folium 的 Github 文档页面:https://python-visualization.github.io/folium/
  2. 数据来源:美国新冠肺炎县一级社区。这是一个开放的数据集,无需许可即可公开使用。
  3. 数据来源:美国县界。这是一个由public.opendatasoft.com提供的开放数据集,你可以免费下载。

你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!

树叶映射:在地图上显示标记

原文:https://towardsdatascience.com/folium-mapping-displaying-markers-on-a-map-6bd56f3e3420

Python 中向叶子地图添加标记的简短指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 GeoJango 地图Unsplash 上拍摄

是一个强大的 Python 库,可以很容易地可视化地理空间数据。它利用了 Leaflet.js 的强大功能,这是一个流行且领先的 JavaScript 库,可用于创建可跨桌面和移动平台使用的交互式地图。

该库的设计使其易于使用,同时保持良好的性能。

有多种方法可以将数据显示在一张树叶地图上,包括:

  • 标记(点和圆)
  • 热图
  • 氯派思

在这个简短的教程中,我们将看到如何在一个叶子地图上显示单个和多个标记。

在 follow Python 教程中显示标记

安装叶片

如果您尚未安装 leav,您可以通过 pip 安装它:

pip install folium

或者蟒蛇:

conda install folium

进口叶子

第一步是导入我们将要使用的库。

在本教程中,我们将使用 pandas 加载 CSV 文件中的数据,并使用 follow 在地图上显示我们的数据。

从 CSV 加载井位数据

我们在这个例子中使用的数据来自挪威石油管理局的网站,包含了在挪威大陆架上已经钻探的所有油井的位置。

数据可在此下载:

https://fact pages . NPD . no/en/井筒/表视图/勘探/全部

这些数据得到了挪威政府的 NOLD 2.0 许可,详细信息可以在这里找到:挪威开放政府数据许可(NLOD) 2.0

下载完数据后,我们现在将使用 pandas 函数 [read_csv()](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html)加载数据。

当我们调用数据帧(df)时,我们看到以下内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到有 5 列:

  • 井名—井的名称
  • 目的——钻井的目的是什么
  • 完井年份——油井完井的年份
  • 纬度—以十进制单位表示的井的纬度位置
  • 经度-以十进制单位表示的井的经度位置

显示空白树叶地图

要用 leav 显示基本地图,我们需要调用folium.map()。在这个类方法中,我们可以传入许多参数。

  • location —地图将居中的位置
  • zoom_start —地图的初始缩放级别
  • control_scale —比例尺控件是否显示在地图上

我们还可以使用许多其他参数。如果您想了解更多信息,请查看关于leave . map类的帮助文档。

这将返回以下以上面指定的位置为中心的地图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以挪威为中心的底图。图片由作者提供。

向树叶地图添加单个标记

在地图上显示数据的最简单方法是使用标记。

我们可以通过调用folium.Marker并传递该点的位置来给地图添加一个标记。在这个例子中,我们将显示数据的平均纬度和经度。

为了让它出现在地图上,我们需要应用.add_to()函数并传入我们的地图对象。

当我们调用map对象时,我们得到下面的地图,在所有数据点的平均位置(地图的中心)有一个标记。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显示我们所有油井平均位置的叶图。图片由作者提供。

更改树叶地图上标记的颜色

我们还可以通过调用 Marker 方法中的[**icon**](https://python-visualization.github.io/folium/modules.html?highlight=icon#folium.map.Icon)参数来改变标记的颜色,并将其设置为:

folium.Icon(color='red', icon='')

标记颜色可以是以下任何一种:

*‘red’, ‘blue’, ‘green’, ‘purple’, ‘orange’, ‘darkred’, ’lightred’, ‘beige’, ‘darkblue’, ‘darkgreen’, ‘cadetblue’, ‘darkpurple’, ‘white’, ‘pink’, ‘lightblue’, ‘lightgreen’, ‘gray’, ‘black’, ‘lightgray’*

要删除叶子标记中的任何符号,我们可以将icon设置为一个空字符串。

一旦我们选择了颜色,我们就需要使用.add_to(map)将它添加到我们的地图中。

完整的代码如下所示:

这将生成下面的地图,我们的标记颜色改为红色。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

改变颜色和移除符号后的叶子标记。图片由作者提供。

更改树叶地图上标记上的图标

在上面的例子中,我们移除了图标,但是如果我们想要使用我们自己选择的图标,我们可以从下面的 Bootstrap 网站传入一个命名的符号。

https://getbootstrap.com/docs/3.3/components/

例如,我们可以将图标设置为图钉

然后,当我们运行代码并查看地图时,我们可以看到图标已经更新为图钉。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

更改符号后的叶子标记。图片由作者提供。

当我们放大时,我们可以更清楚地看到图标的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

放大我们的标记和所选符号的视图。图片由作者提供。

向树叶地图添加多个标记

我们可以继续一个接一个地添加标记,这对于小数据集来说可能是好的。但是,当您有一个像这样的超过 2000 行的数据集时,我们需要考虑一种替代方法。

在下面的代码中:

  • 我们调用我们的地图并将zoom_start改为 3,这将比之前的地图更大程度地缩小地图
  • 然后我们迭代数据帧中的每一行
  • 然后我们创建一个 iframe 对象,它允许我们对弹出窗口的外观和内容有更多的控制。在这种情况下,我们只显示井的名称
  • 接下来,我们创建弹出窗口,并传递 iframe 对象和弹出窗口的尺寸
  • 最后,我们获取每一行的纬度和经度,并将它们添加到地图中

当我们运行这个并调用我们的 map 对象时,我们得到了下面的地图,其中显示了挪威近海油井的所有位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加多个标记后的叶子图。图片由作者提供。

我们可以单击任何一个孔来查看弹出窗口和孔的名称。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在包含井名的 iframe 中弹出。图片由作者提供。

按类别控制树叶标记颜色

我们可以根据数据中的属性来改变颜色,而不是用单一颜色来表示叶子标记。

在本例中,我们将根据井的用途更改标记的颜色。

为此,我们需要用目的和相关颜色建立一个字典。在这种情况下,我们有三个类别:野猫,评估和野猫-CCS,分别分配给红色,绿色和蓝色。

由于该列包含一些缺失值(nan ),我们需要在第 15 到 19 行设置一个简短的 try-except 块。

为了调用这些颜色,我们在对folium.Icon()的调用中改变颜色参数的参数。

当我们呼唤地图时,我们现在有了根据目的着色的标记。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

带有按类别着色的标记的树叶地图。图片由作者提供。

我们可以放大并检查一个标记,看看弹出窗口显示什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

带有按类别和自定义弹出窗口着色的标记的树叶地图。图片由作者提供。

摘要

Folium 为显示地理空间数据提供了一个很好的平台。一旦你理解了它的基本原理,使用起来就很简单了。如果你有时间,值得探索一下叶库中可用的其他地图类型。

感谢阅读。在你走之前,你一定要订阅我的内容,把我的文章放到你的收件箱里。 你可以在这里做!或者,您可以 注册我的简讯 免费获取更多内容直接发送到您的收件箱。

其次,通过注册会员,你可以获得完整的媒介体验,并支持我和其他成千上万的作家。每月只需花费你 5 美元,你就可以接触到所有精彩的媒体文章,也有机会通过写作赚钱。如果你用 我的链接报名,你直接用你的一部分费用支持我,不会多花你多少钱。如果你这样做了,非常感谢你的支持!

遵循以下高质量数据接收的最佳实践

原文:https://towardsdatascience.com/follow-these-best-practices-for-high-quality-data-ingestion-deb9e2a7f6cb

如何选择正确的工具并将其集成到您的数据管道中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乔恩·泰森在 Unsplash 上的照片

数据摄取可能是 ETL/ELT 过程中最重要的一步。毕竟没有数据是做不了任何转化的!数据接收管道将您的工具和数据库连接到您的数据仓库,即整个数据堆栈的中枢。

您为将数据接收到仓库中而设置的流程为您的数据团队中的所有其他流程设定了标准。您随后的转换和分析取决于您接收的数据的质量。这就是为什么您必须从一开始就遵循最佳实践,并在整个过程中记录下来。

当您从一开始记录最佳实践时,您被迫遵循一个固定的结构。没有这种结构,事情就会变得一团糟。您创建了一个新的数据连接器,它没有遵循您想要的命名约定,但是您已经为要接收的数据付费了,现在要更改它已经太晚了。您与一家摄取公司签订了合同,该公司不提供对您的业务最重要的连接器集成。这些都是你应该避免的现代数据堆栈的噩梦。

那么,创建数据接收管道的最佳实践是什么呢?在本文中,我将分享我从零开始构建数据堆栈时学到的最佳实践。这些实践包括选择摄取工具、记录来源、编排、测试和监控。通过分享我过去所做的事情,你可以在开始自己的旅程之前,从我的成功和失败中学习。

‍Compare 数据接收工具

当我第一次着手决定我们的数据堆栈将使用的工具时,我当时的经理已经准备好了一些非常棒的系统。我是一名新的分析工程师,从未在初创公司做过全职工作。我只是在学习诀窍!幸运的是,他在决定合适的工具和组装框架方面有丰富的经验。如果不是他制作了一个精美的 Excel 电子表格来帮助比较我们正在选择的数据摄取工具,谁知道我们今天的摄取会是什么样。

那么,这个决定摄取工具的框架到底是什么呢?它基本上是一个记分卡,上面有我在标题中提到的三个不同的类别——必须拥有的、最好拥有的和交易破坏者。然后,在每个类别下都有某些品质/特性,我们根据工具的功能给它们打了 1-3 分。

在设计数据接收管道时,有许多重要的事情需要考虑,下面是我们包括的几个:

  • 可用的数据连接器(Shopify、Azure Blob、脸书、NetSuite 等。)
  • 您团队的能力(建立时间、维护时间、技能组合等。)
  • 预算(接收预期数据量的每月成本)
  • 支持(松散的社区、专门的支持代理等。)

这些都是为您的团队决定合适的摄取工具的关键。你可能找不到能满足你所有需求的工具,但是你确实想找到最能满足你需求的工具。如果你在一个工具中找不到你想要的东西,那就策划一下如何使用几个不同的工具,并且仍然能获得相同的结果。例如,也许你有一个分析工程师,他可以用 Airbyte 为你的大多数来源设置和维护连接器。然而,有一些没有集成,所以你需要让你的工程团队建立一些东西。

记录您的数据摄取管道来源

我怎么强调文档的重要性都不为过,尤其是当你在一个小型的数据团队中工作的时候。当出现问题,文档的所有者生病、休假或离开公司时,文档就是您的生命线。您的现代数据堆栈中的所有内容都应该记录在案,以便团队中的任何其他人都可以在需要时接管。数据摄取也是如此。

确保您保留了您正在使用的各种摄取工具以及您在该工具中设置的连接器的文档。如果不这样做,就很容易忘记原始数据的来源。此外,请注意为使该连接器工作,您必须做出的任何特殊要求或更改。例如,对于我们的一个连接器,我必须在数据库上启用变更数据捕获,以便正确地捕获数据。把这些事情写下来,这样对你的团队来说就不是猜谜游戏了。

随时在你的仓库里保存一份所有原始数据的副本

我在雪花数据仓库架构:如何组织数据库、模式和表中详细地描述了这一点,所以如果你还没有,一定要去看看。数据摄取的一个最基本的最佳实践是保护您的原始数据。原始数据应该始终保存在数据仓库中的一个单独的数据库中。任何转换工具或人员都不应该对此数据库拥有写访问权限。它应该是严格只读的。

这是一个从不同来源接收所有原始数据的数据库。这个数据库将作为您所有数据的“备份”,以防您的数据建模出现问题。假设您意外运行了一个数据模型,该模型删除了表中的随机行并混合了列值。你不能再相信那些数据了。你不想删除它,因为那样它就永远消失了,但是你不能用它。您可以删除数据模型中保存的所有数据,并通过引用原始数据源重新运行模型。

原始数据的救援!

‍Run 同步建模

根据您运行数据模型的频率,您的原始数据应该总是在您的模型运行之前同步到您的仓库。无论你是像我一样使用 dbt 进行转换,还是其他,一个转换应该紧接着另一个发生。这确保了相互依赖的 dbt 模型不会并行运行,从而在不同的分析中给出不准确的结果。通过同步运行 syncs 和 dbt 模型,您还可以对数据进行更准确的验证。

幸运的是,像 Airbyte 这样的数据摄取工具一直在与 Airflow、Prefect 和 Dagster 这样的强大部署工具合作。就我个人而言,我使用 Prefect 来部署我的数据模型。他们通过提供“Airbyte 连接任务”使同步变得容易,这允许你直接在你的数据管道内同步你的数据连接器。通过在管道中直接同步数据,您可以在模型和这些同步之间创建下游依赖关系。这意味着您的数据模型中总是有最准确和最新的数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

您只需指定 Airbyte 服务器的托管位置、post、api 版本和连接 id。就这么简单!然后,在完美流中使用这些任务,并将它们设置为 dbt 模型运行的上游依赖项。您可以阅读本教程,用 Airbyte、Prefect 和 dbt 创建一个数据接收和传输管道。

数据源上的‍Create 警报

您必须在源头创建数据警报和测试。许多人认为测试和警报应该在最终的数据模型上进行,但实际上恰恰相反。当你专注于下游而不是上游时,它就变成了一个你永远无法解决的一万块拼图。您必须做大量的挖掘工作,才能发现问题实际上从来都不是数据模型。

相信我,我知道因为我也经历过。当我们在数据可视化平台中编写所有的数据模型时,就不可能调试中间模型。我不得不花数周时间调查一个问题,结果发现是数据源本身造成的。您构建的第一个警报应该在数据源级别。如果您的数据源有问题,您需要在它影响任何下游数据模型之前立即知道。

更不用说,修复数据源的问题要比检查数据模型、测试每一行代码以查看其性能是否符合预期容易得多。数据源中的错误可以简单到修复电子表格中的人为错误、更新凭证或重新触发 cron 作业。与涉及大量代码的情况相比,它们要简单得多。

我最喜欢在源代码中设置测试和警报的方式是使用 dbt tests 和 re_data。使用 dbt 测试,您可以检查空值、主键和预期值。您可以使用这些简单的测试来确保您的数据符合预期。 re_data 是一个 dbt 包,允许您监控数据的新鲜度、行计数和描述性统计等指标。您甚至可以设置松弛警报,在指标超出其典型 z 值时提醒您。

松弛警报本身就很强大。我们每天都在 Slack 上跟公司和团队沟通。将数据提醒直接发送到 #data-alerts 频道,这样你就不需要每天早上解析电子邮件或手动访问仪表盘。如果有问题,你的工具会通过 Slack 告诉你。确保您总是设置它们!

结论

从数据堆栈的初始阶段开始遵循数据接收最佳实践,将为您的团队未来的成功做好准备。这些最佳实践的文档将从一开始就使需求变得清晰,几乎不给出错留下空间。当为拼图的一部分设定了高标准时,它必然会渗透到其他部分。

再次重申,在决定一个接收工具并创建一个数据接收管道到您的堆栈时,请记住以下几点:

  1. 写下所有对你的业务最重要的连接器,你的团队必须提供的技能和时间,以及在决定合适的摄取工具时你的预算。
  2. 记录所有数据源以及它们是如何被接收到数据仓库中的,包括任何特殊的设置。
  3. 始终保持一个包含原始数据的数据库。
  4. 同步运行数据同步和模型,以便数据中没有间隙。
  5. 在数据源而不是下游数据模型上创建警报。

虽然这可能会增加您的工作量,但现在遵循这些最佳实践只会为您省去麻烦。您正在主动解决数据接收管道中的某些问题,在对业务产生负面影响之前找到解决方案。从一开始就考虑这些事情总是比对出错的事情做出反应要好。

订阅我的分析工程简讯以了解最佳实践、新工具和所有现代数据堆栈的最新信息。

用 SQL 提取字符串的简单公式

原文:https://towardsdatascience.com/fool-proof-formula-to-extracting-strings-with-sql-9b35c57de224

解决讨厌的字符串问题的分步示例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上由Bozhin karivanov拍摄的照片

你有没有遇到过一个字符串提取问题,几个小时后却发现自己快疯了?人们很容易迷失在所有潜在的字符串函数中。

字符串是我最不喜欢使用 SQL 的数据类型。对于模式匹配,您需要了解许多不同的函数和字符。但是,当你正确使用它们时,我在本文中向你展示的方法是相当简单的。

我写这篇文章的目的是让字符串不那么烦人。从我的错误和许多小时的挫折中吸取教训!我将向您介绍我正在研究的问题、我的解决方案中的关键功能以及解决方案本身。到本文结束时,您将成为使用 SQL 提取字符串的专家。

问题是

我最近受命为营销团队重新创建一个数据模型。很大一部分逻辑是从活动链接中提取 utm 信息。当您在 Google 上做广告时,会使用 utm 信息创建特殊链接,如媒体、出版商、活动和来源。这些都组合在一起形成一个链接,看起来像这样:

[https://www.etsy.com/market/lamp_shades?utm_source=google&utm_medium=cpc&utm_term=etsy%20lamp%20shades_p&utm_campaign=Search_US](https://www.etsy.com/market/lamp_shades?utm_source=google&utm_medium=cpc&utm_term=etsy%20lamp%20shades_p&utm_campaign=Search_US_Brand_GGL_ENG_Home_General_All&utm_ag=Lamp+Shades&utm_custom1=_k_Cj0KCQiAt8WOBhDbARIsANQLp94WN__lDzNNnwS6yptN8pqbeU09mUzcKN9-5hHMFTWbS4msnQJqh4YaAtaOEALw_wcB_k_&utm_content=go_6518959416_125883546672_536666915699_aud-459688891835:kwd-308153672827_c_&utm_custom2=6518959416&gclid=Cj0KCQiAt8WOBhDbARIsANQLp94WN__lDzNNnwS6yptN8pqbeU09mUzcKN9-5hHMFTWbS4msnQJqh4YaAtaOEALw_wcB)

关键是识别模式。不只是一种模式,而是所有可能的模式。你可能认为你已经找到了一个有效的模式。但是,随后会出现一个特殊的用例,您也需要将它构建到您的逻辑中。找出您正在处理的字符串列中的所有模式。相信我,越早找到他们越好。将这些模式写在列表中,如下所示:

  • utm_source 位于“utm_source”之后和“&”之前
  • utm_medium 位于“utm_medium”之后和“&”之前
  • utm_campaign 位于“utm_campaign=”之后

一旦你找到了字符串中的模式,你就可以把它们翻译成代码。开始用文字而不是代码编写逻辑要容易得多。有了字符串提取,你的代码会变得非常混乱,非常快。几乎到了无法阅读的程度…

**提示:**边走边注释你的代码。这不仅有助于你在编写代码时更好地理解它,也有助于阅读你的代码的人。

SQL 字符串函数

CHARINDEX()

在处理字符串时,我喜欢使用两种不同的函数。第一个是 CHARINDEX() 函数。这会返回您提供给它的字符串的索引。它有两个输入,您正在搜索的字符串和您在中搜索的列*。因此,如果我在campaign_link列中寻找‘UTM _ medium ’,我会将其写成:*

charindex('utm_medium', campaign_link)

这段代码将返回“utm_medium”在该列值中所处位置的索引。如果它不在列值中,函数将返回 0。

子字符串()

我要使用的下一个函数是 SUBSTRING() 函数。这需要您想要定位字符串的列和两个索引——一个用于字符串的开头,一个用于结尾。它返回所提供的列中这两个索引之间的字符串。

如果我知道字符串“utm_medium”在索引 5 和 10 之间,我会编写如下所示的代码:

substring(campaign_link, 5, 10)

这将返回campaign_link列中索引 5 和 10 之间的字符串。

其他有用的 SQL 命令

选择语句

CASE 语句总是很方便,但在处理字符串时更是如此。它帮助我们处理我们在数据中发现的所有奇怪的一次性场景。 CASE 语句遵循一个简单的“当这种情况发生时,则执行这个模式。

CASE 
    WHEN utm_medium='Facebook' THEN 'Social'
    ELSE 
END AS marketing_category

您还可以在 CASE 语句中添加一个ELSE来处理其他可能破坏代码的场景。并且,用END结束你的案例陈述!

LIKE 运算符

LIKE 操作符对于在字符串中搜索模式特别有用。虽然它不能像CHARINDEX()SUBSTRING()那样帮助提取那些模式,但它在 CASE 语句中特别有用。它们通常由我写的案例陈述中的“ when 部分组成。

当像使用一样使用时,你指定一个你正在搜索的字符串。根据您希望它在要搜索的列中的位置,您需要使用特殊的运算符。

  • _ 代表单个字符
  • %代表一个、一个或多个字符

我通常在要查找的字符串的开头和结尾使用%。只要字符串出现在列值中的任何位置,这将被证明是正确的。

CASE 
    WHEN campaign_link LIKE '%facebook%' THEN 'Social'
    ELSE 
END AS marketing_category

请注意,我用单引号括起了要搜索的字符串,并用%号括起来。

解决方案

现在,让我们进入解决问题的解决方案,并利用我刚才提到的这些功能。请记住,我是在反复试验之后才得出这个结论的。重要的是,如果你发现有什么不对劲的地方,就要不断地检查你的代码,并反复强调。

当我写字符串提取代码的时候,我是分几部分来做的。首先,我使用了CHARINDEX()函数来查找我正在寻找的字符串部分的索引。这将返回 utm_medium 中“u”的索引,所以我们要将这个字符串的长度加到 index 函数中。这将返回=之后的字符串的索引。

charindex('utm_medium=', campaign_link) + 11

然后,我将它与SUBSTRING()函数结合使用,以获取字符串索引右侧的所有内容(我通过在函数 100 中创建第二个索引来实现这一点)。

substring(campaign_link, charindex('utm_medium=', campaign_link) + 11, 100)

之后,我添加了另一个CHARINDEX()函数来查找下一个字符左边的字符串。为此,从索引中减去 1,因为它是字符串的结尾,只有 1 个字符长。

substring(campaign_link, charindex('utm_medium', campaign_link) + 11, charindex('&', campaign_link) - 1)

最后,我在这个逻辑中添加了一个 CASE 函数。但是,这只是在我意识到并非每个活动链接都有 utm_source、utm_medium、utm_publisher 等时才添加的。我需要一个案例来处理这些字符串后面没有“&”的情况,就像我逻辑中的最后一个要点。

substring(campaign_link,           # starting with the index of utm_medium in the link
          charindex('utm_medium', campaign_link) + 11,           # if this string contains an & then return that index
          # if not, return 100 as the ending index
          CASE 
              WHEN substring(campaign_link, charindex('utm_medium', campaign_link) LIKE '%&%' THEN charindex('&', campaign_link) - 1
              ELSE 100 
          END
)

看到事情变得多混乱了吗?在逻辑中添加 CASE 语句会使代码变得难以阅读,这就是为什么注释代码很重要的原因。阅读它的人需要很长时间才能意识到它在做什么。

让事情变得更加复杂的是,我们需要将它添加到另一个 case 语句中,如果 utm_medium 出现在campaign_link中,这个语句将只提取它。这看起来像这样:

CASE
# check if the link contains a utm_medium
    WHEN campaign_link LIKE '%utm_medium%' THEN 
# return the string after utm_medium=
substring(campaign_link, charindex('utm_medium=', campaign_link) + 11,
# if this extract link contains an &, find the index of that
CASE 
    WHEN substring(campaign_link, charindex('utm_medium=', campaign_link) LIKE '%&%' THEN charindex('&', campaign_link) - 1
# if it doesn't, return 100 as the ending index
    ELSE 100 
END
)
# if it doesn't contain utm_medium, return null 
    ELSE NULL
END AS utm_medium

结论

我发现字符串提取的关键是要有耐心。不要试图一口气写完整段代码。编写各个部分,验证它们是否如您所期望的那样工作,然后将它们集成在一起。虽然这看起来像是更多的工作,但从长远来看,它最终会花费更少的时间。如果你一头扎进去,肯定会出错,你会花更多的时间来调试你的代码。

如果您的工具箱中有这些函数和操作符,您将很快用 SQL 提取字符串。记住,注释你的代码!下次再来看看“ 3 个 SQL 交换以写出更好的代码”和“如何使用这 6 个不寻常的 SQL 函数”。

被统计意义所迷惑

原文:https://towardsdatascience.com/fooled-by-statistical-significance-7fed1bc2caf9

不要让诗人欺骗你

看看世界上最短的关于#统计学的讲座,以及人们对待它的方式的所有错误:

或者说: p =0.042

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自 thesaurus.com 的截图。我的另一个词库很糟糕,很糟糕,也很糟糕。

统计上的不重要

与普遍的看法相反,术语“”并不意味着某件重要的*、重大的令人信服的发生了。如果你认为我们在这里使用的单词 significant 会让你的词典感到自豪,那么你就成了一个狡猾花招的受害者。不要让诗人欺骗你。*

“你不应该让诗人骗你。”比约克

对于那些喜欢尽量少接触统计数据的人来说,以下是你需要知道的关于“统计显著性”这个术语的所有信息:

  • 这并不意味着发生了什么重大的事情。
  • 这并不意味着结果是“大的”或值得注意的。
  • 这并不意味着你会对数据感兴趣。
  • 意思是某人声称对某事感到惊讶。
  • 如果你不太了解问题中的 某人某事 ,它不会告诉你任何有用的东西。

对于除了正在讨论的决策者之外的所有人来说,统计上有意义的结果很少是重要意义上的重要*——它们偶尔很适合提出有趣的问题,但通常它们是不相关的。*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安德鲁·乔治在 Unsplash 上拍摄的照片

当非专家使用这个术语时,要格外警惕,尤其是当它伴随着令人窒息的旺盛时。有时,特别厚颜无耻的江湖骗子更进一步,去掉“统计”一词,挖掘诗歌的全部力量。“嘿瞧”,他们告诉你,“我们所谈论的在宇宙的眼中是有意义的。”**

不,不是的。

最糟糕的可能罪犯是那些把“统计显著性”发音为“确定的”或“确定的”或“完美无瑕的知识”的同义词的人——这里有一些讽刺被忽略了。该术语来自一个处理不确定性的领域,因此(根据定义!)只属于我们的知识是 而不是 完美无瑕的设定。

行话预警!

对于那些喜欢用行话来对付行话的人,我将在下一节中帮助自己使用更正式的语言。你可以不去想那一点,但是如果你同时对这里的新事物感到好奇,那就走一条小弯路在短短 8 分钟内浏览所有统计学中最重要的观点:

*

我的文章中的大部分链接会将你带到博客文章,在那里我会给你重点主题的更深入的概述,所以你也可以将这篇文章作为选择你自己的冒险关于数据科学的迷你课程的发射台。

统计显著性

“统计显著性”仅仅意味着一个 p 值 低到足以改变决策者的想法。换句话说,我们用这个术语来表示一个无效假设拒绝。**什么是无效假设呢?测试有多严格?_(ツ)_/*

欢迎来到统计学,这里的答案是 p = 0.042 但是你不知道问题是什么。

从技术上来说,设定假设检验条件的决策者唯一的 人,对他来说,检验的结果可能具有统计学意义。

统计是个人的

统计学为你提供了一套决策工具,但如何使用它们取决于你自己——它将像任何其他决策一样是个性化的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Towfiqu barbhuiyaUnsplash 上拍摄

这个过程包括非常仔细地措辞你的决策问题,挑选你愿意接受的假设,对你的答案可能错误的不同方式做出一些风险权衡**(因为随机性是一个混蛋),然后使用数学来获得你特定问题的风险可控的答案。*

作为一种修辞欺凌的支撑,它的流行有些反常和滑稽。

这就是为什么真正的专家永远不会用统计数据像锤子一样敲打敌人。两个决策者可以在相同的数据上使用相同的工具,然后得出两个不同的——完全有效的——结论……这意味着它作为修辞欺凌的一个支柱,其受欢迎程度既反常又滑稽。

统计意义是个人的。仅仅因为我对这些数据感到惊讶,足以改变我的想法,并不意味着你也应该如此。

当我理解了统计是如何工作的,我不禁惊叹于在不了解统计决策局限性的人面前宣布某件事具有统计学意义是多么的傲慢——几乎是粗鲁。这个术语听起来太普遍了,对任何人都没有好处;这就像是“闭嘴,相信我,因为我的方法很奇特”的修辞手法。我希望你能和我一起给这种修辞方式以它应得的“ pffft ”。

借用别人的统计意义

等等,难道我们就不能从别人的统计结果中学到什么吗?

这就有点哲学了,所以我需要一篇单独的文章来阐述我对这个问题的看法:

*https://kozyrkov.medium.com/why-do-we-trust-scientists-98c24e3b9f0e

简而言之,我的建议是,把你的一些决策权委托给其他人是没问题的,只要你相信他们是有能力的,并且把你的最大利益放在心上。当他们被说服时,你会借用他们的意见,这样你就不用自己重做他们所有的工作了。

通过使用别人的统计结论,你的决定不是基于数据,而是基于你对个人的信任。

请注意,使用别人的结果,你的决定不是基于数据,而是基于你对个人的信任。选择信任他人没有问题,所以你不需要从零开始凭经验建立你的整个世界观——知识共享是人类如此成功的一部分——但值得注意的是,你可能是你认为你正在收听的任何“知识”的几个回合的断电话下游。

如果你让某人站出来代表你做决定——这意味着消耗别人的 p 值和决策结论——那么确保这个人是你认为足够胜任和值得信任的。

你不应该让诗人欺骗你

如果那个对你大放厥词的人是你不信任的人怎么办?往山上跑!

无论何时,只要有一丝关于统计意义的声明的说服力,就要格外小心的发言人兜售的任何商品。如果你信任和你谈话的人,你不需要他们的呼吁有统计学意义。你只需要知道他们相信。如果你不信任他们,你就不会信任他们的统计术语,就像你不会信任他们的爵士乐手一样。

如果你连问题是什么都不明白,那么回答有什么用呢?

如果有一件事我想让你从这篇博文中学到的话,那就是:如果你不太了解决策者,不太了解他们如何着手弄清楚他们是否应该改变他们的想法(确切地说是关于什么),那么他们关于统计显著性的主张 对你来说完全没有意义 。如果你连问题是什么都不明白,那么回答有什么用呢?

感谢阅读!人工智能课程怎么样?

如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:

在这里欣赏课程播放列表,它被分成 120 个单独的一口大小的课程视频:bit.ly/machinefriend

https://kozyrkov.medium.com/membership

喜欢作者?与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 TwitterYouTubeSubstackLinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格取得联系。

寻找动手 ML/AI 教程?

以下是我最喜欢的 10 分钟演练:

脚注

*如果你很想知道什么是 p 值,我制作了一个视频来帮助你:

这是我 YouTube 播放列表上的第一个视频,你可以在 http://bit.ly/quaesita_p1 找到

  • 要了解假设检验的解释,请查看我关于该主题的博客文章,或者看看这段视频:*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值