TowardsDataScience-博客中文翻译-2022-八-

TowardsDataScience 博客中文翻译 2022(八)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

用 25 个例子来解释熊猫群

原文:https://towardsdatascience.com/all-about-pandas-groupby-explained-with-25-examples-494e04a8ef56

探索性数据分析的有效工具

莎伦·皮特韦在 Unsplash 上的照片

groupby 是数据分析中最常用的 Pandas 函数之一。它用于根据给定列中的不同值对数据点(即行)进行分组。然后,我们可以计算生成的组的聚合值。

如果我们有一个包含汽车品牌和价格信息的数据集,groupby 函数可用于计算每个品牌的平均价格。

(图片由作者提供)

在本文中,我们将通过 25 个例子来尝试发现 groupby 函数的全部潜力。即使你习惯使用这个函数,我还是建议你继续读下去,因为我们还会介绍一些不常用但对各种任务都很有用的操作。

我用模拟数据创建了一个销售数据集。让我们首先用这个数据集创建一个数据帧。

import pandas as pdsales = pd.read_csv("sales_data.csv")sales.head()

(图片由作者提供)

例 1:单个聚合

我们可以计算每个商店的平均库存数量,如下所示:

sales.groupby("store")["stock_qty"].mean()**Output:**store
Daisy      1811.861702
Rose       1677.680000
Violet    14622.406061
Name: stock_qty, dtype: float64

示例 2:多个聚合

我们可以在一次操作中完成多个聚合。下面是我们如何计算每个商店的平均库存数量和价格。

sales.groupby("store")[["stock_qty","price"]].mean()**Output:**

(图片由作者提供)

确保在 Python 列表中写入要聚合的列。

示例 3:多个聚合— 2

我们还可以使用 agg 函数来计算多个聚合值。

sales.groupby("store")["stock_qty"].agg(["mean", "max"])**Output**

(图片由作者提供)

示例 4:命名聚合

在前两个例子中,不清楚聚合列代表什么。例如,“平均值”并没有告诉我们它是库存数量的平均值。在这种情况下,我们可以使用命名聚合。

sales.groupby("store").agg(

    avg_stock_qty = ("stock_qty", "mean"),
    max_stock_qty = ("stock_qty", "max"))**Output**

(图片由作者提供)

要聚合的列和函数名写在一个元组中。

示例 5:多个聚合和多个函数

sales.groupby("store")[["stock_qty","price"]].agg(["mean", "max"])**Output**

(图片由作者提供)

示例 6:使用不同列的命名聚合

我们可以对不同的列和函数使用命名聚合。

sales.groupby("store").agg(

    avg_stock_qty = ("stock_qty", "mean"),
    avg_price = ("price", "mean"))**Output**

(图片由作者提供)

例 7: as_index 参数

如果 groupby 操作的输出是 DataFrame,则组值显示在索引中。我们可以使用 as_index 参数使它们成为数据帧中的一列。

sales.groupby("store", as_index=False).agg(

    avg_stock_qty = ("stock_qty", "mean"),
    avg_price = ("price", "mean"))**Output**

(图片由作者提供)

示例 8:用于分组的多列

就像我们可以聚合多个列一样,我们可以使用多个列进行分组。

sales.groupby(["store","product_group"], as_index=False).agg(

    avg_sales = ("last_week_sales", "mean")

).head()**Output**

(图片由作者提供)

为每个商店-产品组组合生成一个组。

示例 9:对输出进行排序

我们可以使用 sort_values 函数根据聚合列对输出进行排序。

sales.groupby(["store","product_group"], as_index=False).agg( avg_sales = ("last_week_sales", "mean")

).sort_values(by="avg_sales", ascending=False).head()**Output**

(图片由作者提供)

这些行根据平均销售额按降序排序。

例 10:最大 n 值

max 函数返回每组的最大值。如果我们需要最大的 n 个值,我们可以使用 nlargest 函数。

# largest 2 values
sales.groupby("store")["last_week_sales"].nlargest(2)**Output** store      
Daisy   413    1883
        231     947
Rose    948     883
        263     623
Violet  991    3222
        339    2690
Name: last_week_sales, dtype: int64

我们可以看到它们的行的值和索引,这可用于访问整行。

例 11:最小的 n 值

nsmallest 函数返回每个组的 n 个最小值。

# smallest 2 values
sales.groupby("store")["last_week_sales"].nsmallest(2)**Output**
store      
Daisy   23     12
        52     12
Rose    304    12
        320    12
Violet  20     12
        21     12
Name: last_week_sales, dtype: int64

例 12:第 n 个值

我们也可以求出一个组中的第 n 个值。让我们首先按照商店和上个月的销售列对销售数据框架进行排序。

sales_sorted = sales.sort_values(by=["store","last_month_sales"], ascending=False, ignore_index=True)

我们可以找到每个商店上个月销售额第五高的产品,如下所示:

sales_sorted.groupby("store").nth(4)**Output**

(图片由作者提供)

输出包含每个组的第 5 行。因为行是根据上个月的销售额排序的,所以我们得到上个月销售额第五高的行。

例 13:索引为负的第 n 个

我们也可以使用带有负值的第 n 个。例如,“nth(-2)”返回从末尾开始的第二行。

sales_sorted.groupby("store").nth(-2)**Output**

(图片由作者提供)

例 14:唯一值

唯一函数可用于查找每个组中的唯一值。例如,我们可以在每个组中找到唯一的产品代码,如下所示:

sales.groupby("store", as_index=False).agg( unique_values = ("product_code","unique"))**Output**

(图片由作者提供)

示例 15:唯一值的数量

我们还可以使用 nunique 函数找到每个组中唯一值的数量。

sales.groupby("store", as_index=False).agg( number_of_unique_values = ("product_code","nunique"))**Output**

(图片由作者提供)

例 16: Lambda 表达式

我们可以在 agg 函数中使用 lambda 表达式作为聚合。

sales.groupby("store").agg(

    total_sales_in_thousands = (
        "last_month_sales", 
        lambda x: round(x.sum() / 1000, 1)
    )

)**Output**

(图片由作者提供)

示例 17:带有 apply 的 Lambda 表达式

Lambda 表达式可以使用 apply 函数应用于每个组。例如,我们可以计算每个商店上周销售额与上月销售额四分之一之间的平均差额,如下所示:

sales.groupby("store").apply( lambda x: (x.last_week_sales - x.last_month_sales / 4).mean())**Output** store
Daisy     5.094149
Rose      5.326250
Violet    8.965152
dtype: float64

例 18:drop na paramater。

默认情况下,groupby 函数忽略缺少的值。因此,如果用于分组的列中有缺失值,它将不会包含在任何组中,也不会单独显示。我们可以使用 dropna 参数来改变这种行为。

让我们首先添加一个缺少存储值的新行。

sales.loc[1000] = [None, "PG2", 10000, 120, 64, 96, 15, 53]

我们将计算有和没有 dropna 参数的每个商店的平均价格,以查看差异。

# without dropna
sales.groupby("store")["price"].mean()**Output**
store
Daisy     69.327426
Rose      60.513700
Violet    67.808727
Name: price, dtype: float64------------------------------------------------------------------
# with dropna
sales.groupby("store", dropna=False)["price"].mean()**Output**
store
Daisy     69.327426
Rose      60.513700
Violet    67.808727
NaN       96.000000
Name: price, dtype: float64

注意 :为了使用 groupby 函数的 dropna 参数,需要有 pandas 版本 1.1.0 或更高版本。

例 19:多少组

我们有时需要知道生成了多少个组,这可以使用 ngroups 方法找到。

sales.groupby(["store", "product_group"]).ngroups**Output**
18

“商店”和“产品组”列中的不同值有 18 种不同的组合。

示例 20:获取特定的组

get_group 函数可以用来获取一个特定的组作为数据帧。使用用于分组的列中的值来定义组。在使用多个列进行分组的情况下,值被写入一个元组中。

例如,我们可以获得属于商店“Daisy”和产品组“PG1”的行,如下所示:

daisy_pg1 = sales.groupby( ["store", "product_group"]).get_group(("Daisy","PG1"))daisy_pg1.head()**Output**

例 21:分配等级

rank 函数用于根据给定列中的值为行分配等级。我们可以使用 rank 和 groupby 函数分别对每个组中的行进行排序。

sales["rank"] = sales.groupby("store")["price"].rank( ascending=False, method="dense")sales.head()**Output**

(图片由作者提供)

例 22:累计和

我们可以计算每组内的累积和。让我们用简单的时间序列数据创建一个新的数据框架。

import numpy as npdf = pd.DataFrame(
   {
     "date": pd.date_range(start="2022-08-01", periods=8, freq="D"),
     "category": list("AAAABBBB"),
     "value": np.random.randint(10, 30, size=8)
   }
)df**Output**

(图片由作者提供)

我们可以创建一个包含值列的累积和的列,如下所示:

df["cum_sum"] = df.groupby("category")["value"].cumsum()df**Output**

(图片由作者提供)

例 23:展开的累加和

扩展函数提供扩展变换。我们仍然需要一个函数来进行聚合,例如均值和求和。如果它与 sum 函数一起使用,结果将与 cumsum 函数相同。

df["cum_sum_2"] = df.groupby(
    "category"
)["value"].expanding().sum().valuesdf**Output**

(图片由作者提供)

例 24:累积平均值

我们还可以通过使用 expanding 和 mean 函数来计算累积平均值。

df["cum_mean"] = df.groupby(
    "category"
)["value"].expanding().mean().valuesdf**Output**

(图片由作者提供)

示例 25:当前最高值,扩展

扩展和最大值功能可用于记录组中的当前最大值。

df["current_highest"] = df.groupby(
    "category"
)["value"].expanding().max().valuesdf**Output**

(图片由作者提供)

最终想法

groupby 函数和聚合函数一起构成了一个高效的数据分析工具。它们帮助我们从数据集中提取信息。

我们在本文中所做的例子涵盖了 groupby 函数的大多数用例。

你可以成为 媒介会员 解锁我的全部写作权限,外加其余媒介。如果你已经是了,别忘了订阅https://sonery.medium.com/subscribe如果你想在我发表新文章时收到电子邮件。

感谢您的阅读。如果您有任何反馈,请告诉我。

关于 Python 的全部内容——100 多个代码片段、技巧、概念和重要模块

原文:https://towardsdatascience.com/all-about-python-100-code-snippets-tricks-concepts-and-important-modules-9a9fda489b6b

Pablo HeimplatzUnsplash 上拍摄的照片

Python 是目前最流行的语言。它在从网站建设到人工智能的所有领域都被大量使用。

作者图片

一些在日常工作中使用 python 的人是数据分析师、数据科学家、数据工程师、机器学习工程师、web 开发人员等。在这篇文章中,我分享了一些 python 中的代码片段、概念和重要模块,我觉得它们非常有用,希望对你有用。其中大部分是我从 python 文档、stackoverflow 和 kaggle 中找到的。如果你想了解数据结构和算法,那就练习 leetcode 或 hackerrank 类型的问题。

来自 excalidraw 库的图像

我使用了以下工具

  • carbon.now.sh 为代码片段。
  • excalidraw 为图纸
  • Gitmind 为流程图。

我们开始吧。

1.将两个列表转换成字典:

图片由作者使用 carbon.now.sh

{‘CLIPPERS’: 3, ‘GSW’: 1, ‘LAKERS’: 2}

2.使用 ZIP 和 ZIP(*)

图片由作者使用 carbon.now.sh

[(‘car’, 10), (‘truck’, 20), (‘bus’, 30)]
(‘car’, ‘truck’, ‘bus’)
(10, 20, 30)

3.平铺列表:

图片由作者使用 carbon.now.sh

[1, 2, 3, 3, 7, 8, 9, 12, 17]

4.使用 Pickle 保存和加载机器学习模型:

图片由作者使用 carbon.now.sh

此外,查看这篇 Kaggle 文章,了解更多信息

5.融化功能。

Pandas melt()函数用于将数据帧的格式从宽改为长。

图片由作者使用 carbon.now.sh

df:

df1:

另一个例子:

图片由作者使用 carbon.now.sh

有关更多信息,请检查此堆栈溢出

6.Faker:

Faker 是一个为你生成虚假数据的 Python 包。无论您需要引导您的数据库、创建好看的 XML 文档、填充您的持久性以对其进行压力测试,还是匿名化来自生产服务的数据,Faker 都适合您。

图片由作者使用 carbon.now.sh

Jeremy Craig
1302 Brittany Estate
Lake Diamondburgh, HI 31774
Level president life time follow indicate size should. Consumer ability this perform write. Oil wait left tough product.
Need out per third most job special. Good gas star build blood.

7.串并分割:

在给定的分隔符/分隔符周围拆分字符串。

图片由作者使用 carbon.now.sh

输出:

8.大小写-大写和小写

图片由作者使用 excalidraw 制作

图片由作者使用 carbon.now.sh

warriors is the best team
WARRIORS IS THE BEST TEAM
wARRIORS IS THE BEST TEAM

9.魔法命令:

  • 运行命令%lsmagic 查看所有可用的魔术命令。
Available line magics:
%alias %alias_magic %autocall %automagic %autosave %bookmark %cat %cd %clear %colors %config %connect_info %cp %debug %dhist %dirs %doctest_mode %ed %edit %env %gui %hist %history %killbgscripts %ldir %less %lf %lk %ll %load %load_ext %loadpy %logoff %logon %logstart %logstate %logstop %ls %lsmagic %lx %macro %magic %man %matplotlib %mkdir %more %mv %notebook %page %pastebin %pdb %pdef %pdoc %pfile %pinfo %pinfo2 %pip %popd %pprint %precision %profile %prun %psearch %psource %pushd %pwd %pycat %pylab %qtconsole %quickref %recall %rehashx %reload_ext %rep %rerun %reset %reset_selective %rm %rmdir %run %save %sc %set_env %shell %store %sx %system %tb %tensorflow_version %time %timeit %unalias %unload_ext %who %who_ls %whos %xdel %xmodeAvailable cell magics:
%%! %%HTML %%SVG %%bash %%bigquery %%capture %%debug %%file %%html %%javascript %%js %%latex %%perl %%prun %%pypy %%python %%python2 %%python3 %%ruby %%script %%sh %%shell %%svg %%sx %%system %%time %%timeit %%writefileAutomagic is ON, % prefix IS NOT needed for line magics.

10.反转字符串:

图片由作者使用 carbon.now.sh

图片由作者使用 carbon.now.sh

11.Colab:可以用表格格式显示数据框:

图片由作者使用 carbon.now.sh

12.交换变量:

在 python 中,你不需要临时变量。

图片由作者使用 carbon.now.sh

13.合并词典:

图片由作者使用 carbon.now.sh

{‘apple’: 2, ‘banana’: 3, ‘orange’: 2}

14.打印表情符号

图片由作者使用 carbon.now.sh

你可以在这个链接中查看表情符号的完整列表,或者你可以谷歌一下。使用 CLDR 简称。

15.TQDM:进度条

tqdm源自阿拉伯语单词 taqaddum (تقدّم),意为“进步”,是西班牙语中“我如此爱你”的缩写( te quiero demasiado )。立即让你的循环显示一个智能进度条——只需用包装任何 iterable,你就完成了。trange(N)也可以作为tqdm(range(N))的便捷快捷方式。

图片由作者使用 carbon.now.sh

查看他们的 GitHub 库以获得更多信息。

16.打开列表:

图片由作者使用 carbon.now.sh

输出:

1 2 3[1, 2, 3, 4] 5 61 [2, 3, 4, 5] 6

17.删除句子中的标点符号:

图片由作者使用 carbon.now.sh

使用字符串模块。你可以像上面那样去掉标点符号。

!”#$%&’()*+,-./:;<=>?@[\]^_`{|}~The wellknown story I told at the conferences about hypocondria in Boston New York Philadelphiaand Richmond went as follows

18.找出两个列表中的共同元素:

图片由作者使用 carbon.now.sh

输出:

[‘g’, ‘f’, ‘e’, ‘h’]

19.向列表追加值:(追加或连接)

图片由作者使用 carbon.now.sh

输出:

[‘apple’, ‘oranges’, ‘bananas’, ‘grapes’]

20.从列表中删除子列表:

图片由作者使用 carbon.now.sh

输出:

[10, 20, 60]

21.使用负索引[-1]或[~0]:

图片由作者使用 carbon.now.sh

60 
50 
60 
50

要理解切片符号,请查看这个精彩的 stackoverflow 线程。

22.切片分配:

切片赋值是操纵列表的另一种方式。查看此堆栈溢出以了解更多信息。

图片由作者使用 carbon.now.sh

输出:

[10, 21, 31, 40, 50, 60, 70, 80, 90]
[10, 40, 50, 60, 70, 80, 90]
[10, 10, 20, 30, 40, 50, 60, 70, 80, 90]

23.变量名:

  • 变量名必须以字母或下划线开头。
  • 变量名不能以数字开头。
  • 变量名不能以符号开头。
  • 变量可以包含字符、数字和下划线。
  • 变量名区分大小写
  • 变量不能包含 python 关键字的名称。

图片由作者使用 carbon.now.sh

24.可变与不可变:

图片由作者使用 excalidraw 制作

25.内置函数:

为了检查 python 中的内置函数,我们可以使用 dir()

图片由作者使用 carbon.now.sh

要了解任何函数的功能,我们可以使用内置函数帮助。

图片由作者使用 carbon.now.sh

这同样适用于模块。

例如,你使用熊猫,找出熊猫中可用的功能,然后帮助。

import pandas as pd
dir(pd)
help(pd.util)

26.评论:

  • 单行注释。

  • y = a + b #计算内联注释的总和。
  • """这是在功能模块、类等文档中使用的多行注释" " "
  • docstring 是一个多行注释,用于记录模块、类、函数和方法。它必须是它所描述的组件的第一个语句。

27.突破 vs 继续 vs 回传 Vs 传球:

  • Break 语句——控制流立即退出循环。
  • Continuos-该语句跳到下一次迭代,并且不执行当前迭代中 Continuos 之后的命令。
  • Return :该语句退出函数,不执行其后的代码。
  • Pass:Pass 语句为空语句。当传递被执行时,什么都不会发生。它导致无操作(NOP)。这对于尚未编写的代码来说是一个有用的占位符。

图片由作者使用 carbon.now.sh

输出:

0 1 2 3 4 Break

继续:

图片由作者使用 carbon.now.sh

输出:

0 1 2 3 4 Continue 5 Continue 6 7 8 9 Continue

返回:

图片由作者使用 carbon.now.sh

输出:

1
2
3
2
4
3

过关:

图片由作者使用 carbon.now.sh

28.列举:

如果您想要循环遍历索引,并且拥有元素以及元素的索引,则使用枚举函数。

图片由作者使用 carbon.now.sh

输出:

0 : sanjose 1 : cupertino 2 : sunnyvale 3 : fremont

请检查此堆栈溢出讨论

29.弹出、移除和反转:

Pop:移除并返回索引处的项目。如果没有参数,它将移除并返回列表的最后一个元素。

l1 = [10,20,30,40,50,60,70,80]
pop(2)
returns 30

移除:移除指定值的第一个匹配项。如果找不到提供的值,将引发 ValueError。

l1 = [10,20,30,40,50,60,70,80]
l1.remove(30)
l1
[10, 20, 40, 50, 60, 70, 80]

反转:反转列表。

l1 = [10,20,30,40,50,60,70,80]
l1.reverse()
print(l1)
[80, 70, 60, 50, 40, 30, 20, 10]

30.检查列表是否为空:

使用 len(list)== 0 或不列出。

图片由作者使用 carbon.now.sh

31.检查列表中是否存在该元素:

只需在如下列表中使用。

图片由作者使用 carbon.now.sh

32.所有和任何:

  • 使用 all()确定 iterable 中的所有值是否都计算为 True。
  • any()确定 iterable 中的一个或多个值的计算结果是否为 True。

图片由作者使用 carbon.now.sh

33.要查找列表中 n 个最大和 n 个最小的数字:

图片由作者使用 carbon.now.sh

要找到最大的 n 项和最小的 n 项,请使用 heapq。

[800, 500, 320, 200]
[10, 25, 40, 59]

34.检查文件是否存在:

图片由作者使用 carbon.now.sh

输出;

True

35.检查文件是否为空:

图片由作者使用 carbon.now.sh

输出:

True

36.复制文件和目录:

图片由作者使用 carbon.now.sh

37.os.path:

  • 检查当前目录。
  • 检查父目录。
  • 检查路径是否是目录。
  • 检查路径是否是文件。
  • 检查路径是否是装载点。

图片由作者使用 carbon.now.sh

38.Python 的生成器和迭代器的区别:

  • iterator:其类有一个__next__方法和一个做return self__iter__方法的任何对象。
  • 每个生成器都是迭代器,但不是反过来。生成器是通过调用一个函数构建的,这个函数有一个或多个yield表达式,并且是一个满足iterator定义的对象。

请查看这个关于迭代器和生成器区别的 stackoverflow 问题

39。具有任意数量参数和任意数量关键字参数的函数:

图片由作者使用 carbon.now.sh

图片由作者使用 carbon.now.sh

输出:

200 400 500 700
apple : 1 orange : 2 grapes : 2

40.λ函数:

  • Lambda 创建一个包含单个表达式的函数。
  • 写 lambda 函数不用用 return。
  • 返回:(冒号)后面的值。
  • Lambda 也可以接受参数。
  • Lambdas 用于短函数。

图片由作者使用 carbon.now.sh

查看此 stackoverflow 问题以了解有关 Lambda 的更多详细信息。

41.地图功能:

  • Map 采用一个函数和一组项目。它创建一个新的空集合,对原始集合中的每个项目运行函数,并将每个返回值插入到新集合中。它返回新的集合。

图片由作者使用 carbon.now.sh

42.过滤功能:

过滤器接受一个函数和一个集合。它返回函数返回 True 的每个项目的集合。

图片由作者使用 carbon.now.sh

43.Python 装饰器:

查看我关于 python decorator 的详细帖子。

  • 装饰器是在 Python 2.4 中引入的。Python 装饰器函数是一个修改另一个函数并返回一个函数的函数。
  • 它接受一个函数作为它的参数。它返回一个闭包。Python 中的闭包只是一个由另一个函数返回的函数。
  • 装饰函数中有一个包装函数。
  • 它向现有函数添加了一些额外的功能,而不改变现有函数的代码。这是装修工可以做到的。
  • 装饰器允许你在函数前后执行代码;他们装饰而不修改功能本身。
  • 在 Python 中,装饰器以@ 符号开始,后面是装饰器函数的名称。装饰者减慢函数调用。

44.f 弦:

  • f 字符串在 3.6 版本中引入。

图片由作者使用 carbon.now.sh

查看这个博客,了解更多的字符串格式。

45.字符串模块:

图片由作者使用 excalidraw 制作

46.Timeit —测量小代码片段的执行时间:

这个模块提供了一种简单的方法来计时小部分的 Python 代码。它既有命令行界面,也有可调用界面。

图片由作者使用 carbon.now.sh

47.模块与封装:

  • 模块是可以导入的单个 Python 文件。
  • 一个包由多个 Python 文件(或模块)组成,甚至可以包括用 C 或 C++编写的库。它不是一个文件,而是一个完整的文件夹结构。

看看这个关于模块和封装差异的栈溢出问题

48.复制和深层复制:

默认赋值“=”将原始列表的引用赋给新名称。也就是说,原始名称和新名称都指向同一个列表对象。

图片由作者使用 carbon.now.sh

所以这样做

图片由作者使用 carbon.now.sh

49.收藏-计数:

图片由作者使用 carbon.now.sh

50.有序字典:

Python 字典中键的顺序是任意的。这将导致混乱,当你不断添加关键,也在调试中。例如常规词典

图片由作者使用 carbon.now.sh

使用 orderedDict frm 集合。

51.JSON 文件:

  • 使用 json 包下载 json 文件,读取 json 文件

图片由作者使用 carbon.now.sh

阅读和打印

图片由作者使用 carbon.now.sh

52.过滤单行代码中的列表:

图片由作者使用 carbon.now.sh

53.随机种子-为什么它很重要:

要查看更多关于随机的信息,请参考堆栈溢出问题。Python 中的 random()函数用于生成伪随机数。它为一些称为种子值的值生成数字。Python 中的 random.seed()函数用于初始化随机数。默认情况下,随机数生成器使用当前系统时间。如果你使用相同的种子值两次,你得到相同的输出意味着随机数两次。

如果不使用 see,每次执行的输出都是不同的。

图片由作者使用 carbon.now.sh

当使用种子时,你可以一次又一次地得到同样的结果。

图片由作者使用 carbon.now.sh

54.读写文本文件:

图片由作者使用 carbon.now.sh

55.读取和写入 CSV 文件:

图片由作者使用 carbon.now.sh

56.颠倒字典:

图片由作者使用 carbon.now.sh

57.合并两个词典:

请检查这个堆栈溢出。

图片由作者使用 carbon.now.sh

58.对词典进行排序:

图片由作者使用 carbon.now.sh

{‘apple’: 1,
 ‘bananas’: 3,
 ‘grapes’: 4,
 ‘oranges’: 2,
 ‘strawberries’: 5,
 ‘watermelon’: 6}

59.将字符串转换为单词:

图片由作者使用 carbon.now.sh

60.列表 1 和列表 2 的区别:

图片由作者使用 carbon.now.sh

61.将多个 CSV 文件加载到数据帧中:

使用 glob

图片由作者使用 carbon.now.sh

62.解压缩文件:

有 python 模块可以解压文件

  • 目标文件
  • zipfile
  • gzipfile

zipfile

图片由作者使用 carbon.now.sh

目标文件:

图片由作者使用 carbon.now.sh

gzip

图片由作者使用 carbon.now.sh

63.dict.get(key) Vs dict[key]哪个更好:

请检查这个堆栈溢出一个

  • 如果键不在,dict[key]将会产生一个错误。
  • 如果您使用 dict.get() 并且没有找到一个键,代码将返回 None (或者一个自定义值,如果您指定了一个)。

图片由作者使用 carbon.now.sh

64.==和之间的差是:

  • 如果两个变量指向同一个对象(在内存中),is将返回True
  • ==如果变量所指的对象相等。

看看这个 stackoverflow 的。

图片由作者使用 carbon.now.sh

65.将字典转换为元组列表:

图片由作者使用 carbon.now.sh

66.将字典键转换为列表:

图片由作者使用 carbon.now.sh

67.找出列表中元素的索引:

*使用枚举

图片由作者使用 carbon.now.sh

68.使用 Numpy 和 itertools 拼合列表:

有许多方法可以做到这一点。也可以使用 numpy 或 itertools。

图片由作者使用 carbon.now.sh

69.删除前导和尾随空格:

图片由作者使用 carbon.now.sh

70.两个日期之间的天数和月数差异:

图片由作者使用 carbon.now.sh

几个月后?

图片由作者使用 carbon.now.sh

71.检查日期是工作日还是周末:

图片由作者使用 carbon.now.sh

72.操作系统模块:

图片由作者使用 excalidraw 制作

73.处理异常-使用 Try、except 和 finally:

图片由作者使用 carbon.now.sh

图片由作者使用 excalidraw 制作

74.找出 python 对象的内存:

  • 使用 sys.sizegetinfo()

图片由作者使用 carbon.now.sh

75.使用正则表达式从文本中提取电子邮件 ID:

图片由作者使用 carbon.now.sh

76.常规导入与从模块导入与*:

  • 常规进口-进口熊猫作为 pd
  • Python 风格指南建议将每个导入放在一行中。
import pandas,numpy,sys

不建议这样做。

import pandas as pd
import numpy as np
import sys

以上推荐。

  • 从模块导入:有时你只想导入一部分导入。Python 允许使用
from tensorflow import keras
from tensorflow.keras import layers
from keras.models import sequential

例如,如果您只导入了 tensorflow,那么要使用 keras,您必须导入 tensorflow.keras。

  • 从模块导入:不允许或不建议使用通配符()导入。原因是它会导致名称空间共谋。简言之,不要使用通配符导入。下面是不允许的。
from tensorflow import * 
  • 你也可以使用本地导入来代替本地导入。但是最好使用 glocal import。

77.类型提示:

在其他语言中,您需要在使用之前定义数据类型。在 python 中,你不需要定义数据类型。python 3.5 引入了类型提示。请查看文档了解更多信息。

图片由作者使用 carbon.now.sh

78.使用 del 删除变量:

图片由作者使用 carbon.now.sh

也可以看看这个 stackoverflow 的讨论。

79.当你试图改变一个不可变的对象时会发生什么:

图片由作者使用 carbon.now.sh

请查看 stackoverflow 对此的讨论。

80.三重引号:

图片由作者使用 carbon.now.sh

  • 你可以用三重引号
  • 你也可以在引号前使用像\这样的反斜杠,或者使用双引号。

请查看 stackoverflow 讨论。

81.urllib:

  • urllib.request

图片由作者使用 carbon.now.sh

也检查一下

  • urllib.parse
  • urllib.error
  • urllib . robot parser

查看 python 文档

82.链图-集合:

根据文档——提供了一个[ChainMap](https://docs.python.org/3/library/collections.html#collections.ChainMap)类来快速链接多个映射,因此它们可以被视为一个单元。这通常比创建一个新字典并运行多个[update()](https://docs.python.org/3/library/stdtypes.html#dict.update)调用要快得多。

图片由作者使用 carbon.now.sh

83.全局变量与非局部变量:

  • 场景 1——使用全局变量:尝试在函数中使用全局变量,你会得到下面的错误。

图片由作者使用 carbon.now.sh

要修复此错误,请在函数-global glb_var 中将该变量声明为全局变量

图片由作者使用 carbon.now.sh

  • 场景 2 :使用非 nonlocalvariablese。不要更改全局变量值。

图片由作者使用 carbon.now.sh

84.海象运营商:

根据 python 文档——有了新的语法:=,将值赋给变量作为更大表达式的一部分。它被亲切地称为“海象操作员”,因为它与海象的眼睛和长牙相似。python 3.8 中引入了 Walrus 操作符。

图片由作者使用 carbon.now.sh

另一个例子

图片由作者使用 carbon.now.sh

查看文档

85.Python 3.10 中更好的错误消息:

例如在以前的版本中

图片由作者使用 carbon.now.sh

在 Python 3.10 版本中

图片由作者使用 carbon.now.sh

还有很多更好的错误消息,如下所示

SyntaxError: multiple exception types must be parenthesized
SyntaxError: expression expected after dictionary key and ':'
SyntaxError: ':' expected after dictionary key
SyntaxError: invalid syntax. Perhaps you forgot a comma?
SyntaxError: did you forget parentheses around the comprehension target?
SyntaxError: expected ':'
IndentationError: expected an indented block after 'if' statement in line 2
AttributeError: module 'collections' has no attribute 'namedtoplo'. Did you mean: namedtuple?

图片由作者使用 carbon.now.sh

86.Python 3.10 中新的 match-case 语句:

以下示例来自 python 文档。

图片由作者使用 carbon.now.sh

另一个例子

请查看文档了解更多信息。

87.对于编码和解码,您可以使用加密技术:

图片由作者使用 carbon.now.sh

请查看加密文档。

88.Why if name == "main ":

  • if name == "main "条件下的代码将仅在 python 脚本作为独立脚本执行时执行。
  • 如果您在另一个脚本或模块中导入了上述模块,则不会执行 if name == "main ":下的代码。

请查看这个 stackoverflow 问题的详细回答。

89.为什么我们在 python 类中需要 self:

图片由作者使用 excalidraw 制作

Please refer to this [stackoverflow discussion](https://stackoverflow.com/questions/2709821/what-is-the-purpose-of-the-word-self).

90.为什么我们需要 python 类中的 init:

stackoverflow 中还有一个关于我们为什么需要 init 的大讨论。看看这个。根据文档,init 方法是面向对象方法中 C++构造函数的 Python 等价物。每次从类中创建对象时,都会调用 init 函数。init 方法让类初始化对象的属性,没有其他用途。它只在类内使用。

91.系统模块:

  • sys 模块提供系统特定的参数和功能。更多信息请查看文档
  • 我们可以在下面看到一些 sys 方法

图片由作者使用 carbon.now.sh

如何使用 sys 模块列出一个目录中的所有文件?

9 2.Python 分析:

根据 python 文档——cProfileprofile提供了 Python 程序的确定性剖析概要文件是一组统计数据,描述了程序的各个部分执行的频率和时间。这些统计数据可以通过[pstats](https://docs.python.org/3/library/profile.html#module-pstats)模块格式化成报告。很简单。只需导入 cprofile 并开始使用它。

图片由作者使用 carbon.now.sh

93.将表格数据从 PDF 读入 dataframe,并保存为 CSV 或 JSON 格式:

  • Tabula-py 是一个将 PDF 表格转换成 pandas 数据框架工具。tabula-py 是 tabula-java 的一个包装器,在你的机器上需要 java。tabula-py 还能让你把 PDF 格式的表格转换成 CSV/TSV 文件。

图片由作者使用 carbon.now.sh

请查看本 colab 笔记本了解完整实施。

94.使用 PyMuPDF 从 PDF 文件中提取图像:

图片由作者使用 carbon.now.sh

查看他们的 github 库以获取更多信息。

95.合并 PDF 文件:

您可以使用 PyPDF2 或 PyPDF4。代码片段是针对 PyPDF2 的。

图片由作者使用 carbon.now.sh

查看 stackoverflow 了解更多信息。

96.如何获取列表中元素的索引:

图片由作者使用 carbon.now.sh

更多信息请参考。

97.向现有字典添加关键字:

图片由作者使用 carbon.now.sh

请查看关于向字典添加键的这个 stackoverflow 讨论

98.追加和扩展的区别:

图片由作者使用 carbon.now.sh

  • 将它的参数作为单个元素添加到列表的末尾。列表本身的长度将增加一。
  • extend遍历其参数,将每个元素添加到列表中,扩展列表。无论 iterable 参数中有多少元素,列表的长度都会增加。
  • 请查看此堆栈溢出讨论了解更多信息。

99.如何在 python 脚本中设置时间延迟:

  • 使用时间模块和 time.sleep

图片由作者使用 carbon.now.sh

100.将字符串转换为日期:

  • 有许多方法可以做到这一点。

图片由作者使用 carbon.now.sh

101.数据预处理库:

数据科学家、数据分析师和所有数据专业人员经常使用的重要数据预处理库有

  • 熊猫
  • 数字价格

102.数据可视化库:

以下是一些重要的数据可视化库

  • Matplotlib
  • Seaborn
  • 阴谋地
  • 散景
  • 牛郎星

103.Web 抓取 Python 模块:

一些重要的 web 抓取库是

  • 刺儿头
  • 美汤
  • 请求
  • Urllib

104.机器和深度学习库:

一些流行的机器学习和深度学习库是。

  • Sci-Kit 学习
  • Keras
  • 张量流
  • Pytorch
  • Mxnet

105: Python Excel 库:

  • xlwings
  • XLsxwriter
  • xlrd
  • pyexcel

查看链接了解更多信息。

例如使用 xlwings 在 excel 中查看数据帧。

图片由作者使用 carbon.now.sh

106.常用文档字符串:

  • reStructuredText (剩余)格式:

图片由作者使用 carbon.now.sh

  • 谷歌格式:例如

图片由作者使用 carbon.now.sh

  • Numpy 格式:

图片由作者使用 carbon.now.sh

107.可迭代 vs 迭代器 vs 迭代:

图片由作者使用 excalidraw 制作

108.删除字符串中的空格:

使用剥离()、替换()和拆分()

图片由作者使用 carbon.now.sh

109.查找目录中的所有文本文件或目录中的任何文件类型:

图片由作者使用 carbon.now.sh

请查看堆栈溢出了解更多信息。

110.PIP 与 Conda:

图片由作者使用 excalidraw 制作

请检查这一个为更多关于康达的信息

111.删除文本中的敏感信息,如姓名、电子邮件、电话号码等:

  • 使用 scrubadub 隐藏敏感信息,如电话号码、姓名、信用卡详情等。

图片由作者使用 carbon.now.sh

查看文档了解更多信息。

112.使用 Modin 的更快的熊猫数据帧:

  • 在 pandas 中,当你进行任何计算时,你一次只能使用一个内核。使用 Modin,您可以使用机器上的所有 CPU 内核。请查看他们关于安装和入门指南的文档
**import** **modin.pandas** **as** **pd**
df = pd.read_csv("my_dataset.csv")

113.使用 Mercury 将 python 笔记本转换为 web 应用程序:

pip install mljar-mercurymercury watch my_notebook.ipynb

watch命令将监控您笔记本的更改,并自动将它们重新加载到 Mercury web 应用程序中。查看他们的 github 库以获取更多信息。

114.Apache Spark-Pyspark- Pandas API:

熊猫 API 在去年 10 月发布的 Spark 3.2 版本中可用。PySpark 用户可以通过调用**DataFrame.to_spark()**来访问完整的 PySpark APIs。熊猫星火数据帧和星火数据帧实际上是可以互换的。

结论:

希望这篇文章对你有用。python 现在再次变得非常流行,已经被应用到了各个领域。这篇文章的主要目的是作为你的参考。例如,如果您想知道如何合并两个 PDF 文件,那么来查看指向 python 模块 PyPDF2/PyPDF4 的代码片段,然后深入了解 github 库。请随时在 linkedin 与我联系。谢谢!

参考资料:

  1. https://docs.python.org/3/whatsnew/3.10.html
  2. https://docs.python.org/3/whatsnew/3.9.html
  3. https://docs.python.org/3/whatsnew/3.8.html
  4. https://www.kaggle.com/
  5. https://stackoverflow.com/
  6. https://farid.one/kaggle-solutions/

使用 Google DataStudio 的一体化仪表盘

原文:https://towardsdatascience.com/all-in-one-dashboards-using-google-datastudio-9fba7b6d6e7d

聚合过滤器被低估的能力

照片由 Unsplash 上的 Aziz Acharki 拍摄

如果谷歌的数据可视化工具 DataStudio 在公司内部的使用仍在增加,我发现它的一个功能特别被低估了——老实说,我一开始也低估了它。聚合过滤器的概念可能适用于其他数据可视化工具,但 DataStudio 是我发现并广泛使用它的工具。作为一名数据分析师,在过去的几年里,这是迄今为止我使用最多的数据可视化工具。

让我先给你介绍一下我称之为“聚合过滤器”。当我在网上浏览时,我找不到我在这篇文章中所指的确切措辞。聚合过滤器是功能“下拉列表”和功能“参数”的组合。在您想知道应该在多大程度上使用这些神秘的聚合过滤器(在这种情况下,您应该直接跳到第 3 部分)之前,让我们借助一个示例来明确这个概念。

在上一篇文章中,我构建了一个示例仪表板,显示给定产品的销售量:

  • 总计(第一张图)
  • 和每个区域(第二个图表)

玛丽·勒费夫尔—图 1

在本文中,让我们想象一下,除了显示每个地区的销售量分割,我们还需要显示每个国家的销售量分割(在每个现有地区内)。这一修改的原因可能是每个国家经理都需要数字来监控他们自己的销售。州经理呢?一旦他们听说了我们出色的仪表板,他们可能会想要自己的仪表板来显示每个州的销售量。

从一个利益相关者的基本情况,我们切换到我们必须为几个利益相关者构建类似的仪表板的情况,他们中的每一个都像他们的同事一样监控相同的指标,但是在不同的范围内。我希望在我现有的仪表板上建立这些调整,而不是为每个利益相关者复制相同数量的工作。

这就是聚合过滤器发挥作用的地方。

抱歉,聚合什么?

我在本文中提到的聚合过滤器可以添加到任何 DataStudio 报告中,方法是使用顶部栏中的“添加控件”功能,然后单击“下拉列表”。

玛丽·勒费夫尔—图 2

与常规过滤器一样,聚合过滤器是通过定义右侧面板上的“控制字段”来设置的:这是由报表用户自己通过创建的过滤器来控制的维度。

常规过滤器和聚合过滤器的主要区别是,常规过滤器使用来自数据源的一个维度(绿色字段)作为“控制字段”,而聚合过滤器使用来自数据源的一个参数(紫色字段)作为“控制字段”。

玛丽·勒费夫尔—图 3

如何在报表中创建聚合过滤器?

1.创建参数

在您之前设置的数据源中,或者直接在您的报表右侧,点击【添加参数】。在这里,您可以命名和定义将要创建的参数类型。

玛丽·勒费夫尔—图 4

在我们的示例中,我们希望创建一个名为“聚合过滤器”的参数,因为我们将只创建一种类型的聚合过滤器。如果您计划使用几个聚合过滤器,我建议您在命名参数时更加具体,以避免在设置不同过滤器时出现混淆。

这将是一个值列表,代表我希望在同一图表中显示的几个聚合级别:

  • 每个地区
  • 每个国家,考虑到一个世界区域由几个国家组成
  • 每个州,考虑到一个国家由几个州组成

玛丽·勒费夫尔—图 5

2.配置过滤器

既然已经在数据源中创建了参数,您必须定义该参数将引用哪些维度。换句话说,我们希望在这里设置如下:当用户选择“按国家”而不是“按地区”时,DataStudio 应该调用并显示原始数据源中的哪个字段?

这是在数据源部分完成的——正是我们在步骤 1 结束时停止的地方。点击右下角的“保存”后,点击“所有字段”。您会看到出现了一个新的条目类别:参数,其中有一个是我们刚刚创建的名为“聚合过滤器”的条目。

玛丽·勒费夫尔—图 6

为了完成配置,我们通过在数据源中单击“添加字段”来创建一个字段。我是这样设置的:

玛丽·勒费夫尔—图 7

现在保存并返回到您的仪表板。

3.将过滤器应用于相关图表

在我们的示例中,我们希望当最终用户将聚合过滤器设置为“每个国家”或“每个州”时,第二个图表会根据用户的选择进行更新并显示数据。

为此,请选择要应用聚合过滤器的图形。在右侧面板上,将“分解维度”设置为“聚合过滤器(作为字段)”。确保您选择了刚刚在步骤 2 中创建的维度字段(绿色)。这将根据参数默认设置自动更新您的图形(在我们的示例中是每个区域,因此视觉上没有任何变化)。

玛丽·勒费夫尔—图 8

4.微调 UX

基于您的聚合的过滤器现在已经准备好了,您只需要通过点击顶部栏菜单中的“添加控件”和“下拉列表”将它添加到仪表板(参见图 2)。在右侧面板中,将“控制字段”设置为“聚合过滤器”。确保您选择了参数(而不是我们在步骤 2 中创建的字段):您将通过它的紫色来识别它。

玛丽·勒费夫尔—图 9

我想和你分享的最后一个技巧是关于这个聚合过滤器的设计。对于任何用户来说,名为“聚合过滤器”的过滤器与过滤器“区域”的行为不同可能并不明显。然而,它们的行为确实不同:

  • 常规过滤器“区域”将选择应用于仪表板中显示的数据范围
  • 而默认设置为“Per region”的“Aggregation filter”会影响数据在第二个图表中的显示方式

如果我将常规过滤器“region”设置为“region A ”,将聚合过滤器设置为“Per country ”,这就是我的最终输出,这里考虑到 Region A 由 country A1 和 country A2 组成(图 10)。这是典型的仪表板视图,区域 A 的国家经理将使用,而最初的仪表板仅用于区域经理(见图 1)。

玛丽·勒费夫尔—图 10

为什么您也应该开始在仪表板中使用聚合过滤器?

如果你达到了这一步,恭喜你!—您必须预见支持在各种仪表板中使用聚合过滤器的论点。无论如何,让我强调一下我所经历的聚合过滤器的两个主要优点。

原因 1:节省自己的时间

如果实现聚合过滤器在开始时看起来工作量很大,那么正确配置它们所花费的时间很快就会与未来工作中节省的时间相平衡。事实上,如果几个利益相关者需要类似的仪表板,聚合过滤器的替代方案是…实际上构建几个仪表板。这表示通过使用聚合过滤器技术可以节省一些时间。

除了在仪表盘构建过程中节省时间外,仪表盘的维护也因聚合过滤器而变得更加容易。是否需要更改显示的指标?没问题,我会换一次的!需要改变字体或颜色吗?没问题,您可以在自己创建的独特仪表板中一劳永逸地完成这项工作。

原因 2:将不同的利益相关者聚集在同一个仪表板上

从利益相关者的角度来看,为几个人构建一个仪表板可能被视为懒惰的表现(可能吗?)或者是一种误解,认为不同的人有不同的需求。我认为,如果度量是相同的,并且显示可以被合并以适合所有相关方,没有理由不这样做。

此外,将具有相似需求的利益相关者聚集到同一个仪表板上可能会使他们更紧密地合作,并在当前和未来指标的定义上一起工作。这有利于公司职能和/或部门之间更好的协调,我认为这对每个人都有好处。

结论

正如我之前所说的,我的结论是明确的:只要有意义,我们就使用聚合过滤器!

如果您知道其他数据可视化工具中类似的功能,我会有兴趣知道类似的技巧。不要犹豫,在评论区分享你的仪表板习惯。

你喜欢阅读这篇文章吗? 成为 的一员,加入一个不断成长的充满好奇心的社区吧!

https://marie-lefevre.medium.com/membership

所有的蘑菇都可以食用,但有些只能吃一次

原文:https://towardsdatascience.com/all-mushrooms-are-edible-but-some-only-once-aed96aa3b9cf

混淆矩阵和分类度量的可视化介绍。

Igor Yemelianov 在 Unsplash 上拍摄的照片。

问题是

前段时间,一位生物学教授告诉我“所有的蘑菇都可以吃,但有些只能吃一次。”抛开科学家的黑色幽默不谈,蘑菇是一个很好的例子来说明分类问题的复杂性,并介绍在这种情况下通常应用的性能指标

情况是这样的:众所周知,一些蘑菇品种是美味佳肴,而另一些是有毒的,甚至可能导致死亡。作为专家数据科学家,训练一个机器学习模型自动识别毒蘑菇岂不是很棒?最后只是一个二元分类问题:有毒(阳性情况)还是可食用(阴性情况)。

我们首先需要的是一个带标签的数据集。在 UnsplashKalineri 拍摄的照片。

首先,我们需要一个标记的数据集,它包含足够多的蘑菇,我们已经知道它们的正确分类。雇佣一个专家真菌学家在这个项目阶段肯定会派上用场。

下一步是将数据集分成训练测试集。合理的划分应该是 70%的培训和 30%的测试。不过其他的划分也可以,取决于可用的观测数量和每类的比例。

训练一个模特并不容易。在这种情况下,输入变量是每种蘑菇的特征,而输出或目标变量是期望分类为有毒可食用。在这一点上,必须强调模型必须只在训练集上进行训练,并在测试集上进行评估。由于设计和装配模型不在本文讨论范围内,所以我们跳过这一部分,假设… 瞧!模型准备好了。

还需要一个单独的测试集。Andrew Ridley 在 Unsplash 上拍摄的照片。

现在我们进入激动人心的部分。模型有多好?为了回答这个问题,我们用它来预测测试集中的蘑菇是否有毒。虽然我们已经知道了答案,但这个模型以前从未见过。因此,通过比较预测的实际的值,我们可以测量分类性能一般化的能力。

二元分类有四种可能的结果。图片作者。

测试模型和测量性能

这里我们有一个由 12 个蘑菇组成的测试集。它们相应的特征在左边,最右边一栏表示它们是有毒的还是可以食用的。接下来,我们用我们的模型进行预测。比较中间的预测值和右边的实际值,我们发现该模型正确地对一些实例进行了分类,而在另一些实例中出现了错误。

当模型准确地将一个毒蘑菇分类(阳性情况)时,称为一个真阳性。同样,正确识别一个食用菌(阴性)是真阴性

那些是正确的答案,然后有一些错误。当模型将一种食用菌标记为有毒时,就会出现假阳性。相反,假阴性的 T21 是有毒的,被误认为是可食用的。这些也分别被称为类型 I类型 II 错误。

然而,并非所有的错误都是一样的。严重性取决于手头问题的具体细节。例如,在我们的情况下,假阴性比假阳性更糟糕。为什么?

camilo jimenezChristine SiracusaUnsplash 拍摄的照片。

一个假阴性意味着一个毒蘑菇被错误地识别为可食用。这是一个严重的健康危害,因为它可能会产生有害甚至致命的后果。相反,一个假阳性、或者将一种可食用蘑菇归类为有毒,除了丢弃完好的食物并扔进垃圾桶之外,没有任何实际影响。

混乱矩阵

这些结果可以显示在具有特定布局的表格中,称为混淆矩阵。水平的表示观察到的类,而显示预测的类。在二元分类问题中,它们相交于四个单元格,这四个单元格概括了每一种可能的结果。

正确的分类显示在对角线上,而错误显示在外面。这允许找到模型混淆两个类的地方(因此得名)。要知道这个矩阵在很多文档和软件包中可能会出现转置的情况,也就是行中的预测,列中的实际值。这两种变体在文献中都很常见。

混乱矩阵。图片作者。

另一种方法:维恩图

我们可以用文氏图来说明这些结果。实际类别出现在背景的矩形区域。接下来,我们用虚线包围预测阳性。该区域内的个体是模型识别为有毒的个体。最好的情况是这个区域与红色区域完全重叠,因为这意味着模型对每个蘑菇都进行了正确的分类。不幸的是,在这个例子中没有发生这种情况。

文氏图。图片作者。

虽然混淆矩阵和文氏图是可视化模型性能的好工具,但是将性能综合成一个单一的数值是很棒的。因为分类是一个多方面的问题,所以有许多度量标准,每一个都侧重于一个特定的方面。让我们仔细看看其中的一些。

拯救度量。在 Unsplash 上由 Miikka Luotio 拍摄的照片。

敏感度、回忆率或真阳性率(TPR)

该模型能多好地检测毒蘑菇?

换句话说,灵敏度是真阳性除以观察阳性的比率。

Sensitivity 是当训练模型的首要任务是捕捉尽可能多的正面信息(我们的例子)时的选择指标。

敏感。图片作者。

特异性或真阴性率(TNR)

该模型能多好地检测可食用蘑菇?

同样,特异性是真阴性除以实际阴性的比率。

当不希望出现假阳性时,特异性是一个合适的选择,就好像我们希望密切监控有多少食用蘑菇被扔掉。

特异性。图片作者。

精确度或阳性预测值(PPV)

被预测有毒的蘑菇中有多少是真的有毒?

虽然灵敏度和特异性集中于观察到的类别,但是一些度量标准测量预测的性能。例如, precision 是真阳性与预测阳性的比率。

Precision 是一个应该被监控的指标,如果你希望对预测的积极结果有信心的话。

精准。图片作者。

准确(性)

正确分类的比例是多少?

一个独特的度量标准,同时衡量真正的积极和消极应该是有用的。不幸的是,精度不平衡等级的问题中提供了误导性的结果。

如果您需要在单个值中综合分类器的整体性能,请查看诸如平衡准确度F1 得分曲线下面积(AUC) 等指标。

准确性。图片作者。

天真模型:多数法则

我们已经不遗余力地构建了一个模型,但是我们如何确保它是值得的呢?这些预测有价值吗?使用模型比没有模型好吗?

要回答这个问题,我们必须确认模型提供了足够的信息来证明它的存在。型号选择是一项非常复杂的任务,需要一个专门的岗位;然而,让我们粗略地看一下这个主题,并简要地介绍一下天真模型的概念:一个不复杂的模型,它在不利用任何输入数据的情况下提供预测。

对于一个简单的分类模型,一个合理的选择是多数原则。它忽略输入变量,并将每个个体标记为训练集中最频繁观察到的类(在我们的示例中为负或可食用)。这个模型构建起来并不昂贵,而且应该是正确的。具体来说,这个模型的准确率,也称为无信息率,是训练集中多数类的比例。

每个模型都必须超过这个基准才是显著的。否则,运营 it 毫无意义,我们应该要么坚持卓越的天真模式,要么回到设计桌上重新思考我们的方法。

基于多数原则的天真模型。图片作者。

Unsplash 上由亚历山大·巴甫洛夫拍摄

结论

二元分类是每个数据科学家都应该掌握的一项常用技术,因为它是许多商业和科学问题的核心。当多类分类扩展和概括这些概念时,很好地掌握这个基础也是至关重要的。您的数据科学工具箱中的工具越多,您就能更好地应对新的挑战性问题。

我希望这篇文章对你有用…小心蘑菇!

延伸阅读

你能做的所有游戏自助餐:与气候相关的物理风险中的模型风险

原文:https://towardsdatascience.com/all-you-can-game-buffet-model-risks-in-the-climate-related-physical-risks-8445d5f96c18

与 CRPRs 相关的模型风险清单

迈克·纽伯瑞在 Unsplash 上的照片

在气候变化时代,数据科学家可以在追求可靠的数据驱动决策以促进可持续性方面发挥重要作用:稳健地建立气候相关风险(crr)模型,并为决策者提供有用的信息,以减轻风险带来的负面后果。因此,crr 与数据科学家高度相关。与 crr 建模相关的风险范围很广。在这种情况下,参与气候相关风险建模的数据科学专业人员需要对这些风险有扎实的了解。有鉴于此,我决定将我过去关于这个主题的研究著作(如杉尾,2022 )汇编起来,在 TDS 上提出我个人的观点。我希望这篇文章将为数据科学专业人员提供一个清单的基础,以解决与 crr 相关的模型风险。

介绍

气候相关风险具有路径依赖性和高度不确定性。crr 源于高度复杂的气候系统,因此很难建模。

模型风险和尾部风险

通常,有两种类型的风险与模型使用相关:模型风险和尾部风险。

  • 模型风险是"因模型使用不当而导致估值错误的风险。当组织使用错误的模型或错误地使用正确的模型时,就会出现这种风险。(Chance &艾德莱森,2019 年,第 21 页)
  • 当我们在实际分布的尾部发现比使用的概率模型预期的更多的事件时,尾部风险就出现了。(Chance & Edleson,2019 年,第 21 页)

在很大程度上,2007/2008 年的金融危机可能就是这两种风险的一个例子。在广泛使用先进的投资组合风险模型——如风险价值——的同时,金融系统扩大了负债(账面上和账面下),超出了它们的能力。这些复杂的模型反而鼓励了金融行业证明过度冒险的合理性。这些模型的使用最终导致了知识错觉和控制错觉。它助长了 T2 系统性金融危机的起因,并悲惨地未能保护系统免受肥尾事件的影响。(诺切拉,2009 年)

如果 crr 得不到缓解,风险变量之间的相关性可能会逐渐增加,并出现前所未有的非线性发展。(气候变化工作组,2020 年,第 9 页)它们可能会在气候系统的平衡中推动不可逆转的系统性范式转变。(施耐德公司,美国大学,摘要)

CRRs 可能对我们人类的生活造成有害的肥尾风险。鉴于金融危机的历史教训,数据科学专业人员必须解决模型风险和尾部风险( Schneider,2003,p5 ),并从数据驱动的风险管理角度稳健地构建 crr 模型。

气候相关风险的物理风险和转移风险

与气候相关的财务披露特别工作组(TCFD)-一个由行业领导的特别工作组,其任务是制定自愿与气候相关的财务披露指南-将 crr 分为两类风险:过渡风险和物理风险。

  1. 在向低碳经济转型的过程中,可能会出现转型风险。这些风险与“政策、法律、技术和市场变化相关,以解决与气候变化相关的缓解和适应要求”。
  2. 物理风险可分为急性(事件驱动)或慢性风险。
  • 急性风险是"事件驱动的,包括极端天气事件严重性的增加,如气旋、飓风或洪水。例如恶劣的气候。
  • 慢性风险"指气候模式的长期变化":例如,海平面上升或持续高温引起的慢性热浪。

( TCFD,2017 年,第 5–6 页)

下面,根据 TCFD 的分类,这篇文章将集中讨论 crr 的物理风险——称之为与气候相关的物理风险,并探讨与模拟 crpr 相关的潜在风险。并且,我希望这篇文章将为数据科学专业人员提供一个检查表的基础,以解决 crr 中涉及的各种模型风险。

讨论

过去,财产和意外伤害保险行业——财产和意外伤害保险公司和再保险公司——开发了自己的灾难/危险模型来评估与灾难风险相关的保险风险。在这种背景下,通常应用他们的框架来模拟与气候相关的物理风险。(环境署金融倡议,2019 年,第 8 页)

首先,A 节探讨了财产和意外保险公司的巨灾模型中潜在的不确定性来源。然后,B 节将探讨在应用财产和意外保险公司的巨灾模型框架来减轻与气候相关的物理风险时存在的潜在风险。

A. 保险人巨灾模型涉及的不确定性来源

a) 2 个不确定性的系统来源

除了模型风险和尾部风险之外,伦敦劳埃德发表的一篇论文还关注与气候相关的物理风险的不确定性的以下两个系统来源。(图米&雷斯泰尔,2014 )

  • 气候系统本身的内部变化
  • 人类引起的变化的影响。

除了人类引起的变化,如人类活动引起的 GHG 排放量的指数上升,气候系统本身已经有了内在的可变性。我们经常观察到一些地区发生干旱,而另一些地区同时发生洪水。气候系统固有的内部可变性,称之为 自然可变性 ,这使得科学家很难概括有无人为引起变化的气候相关风险的后果。

b)趋势

此外,自然变化和人为引起的变化结合在一起,正在展示新出现的 趋势 。( Toumi & Restell,2014 年,第 32 页)劳埃德的论文阐述了将观察到的趋势纳入模型的必要性,如下所示:

“如果一个灾难模型确实量化了一个地区/危险的损失,那么这个过程是复杂的,并且取决于许多假设,这自然会导致围绕该损失的一定程度的不确定性。对于更极端的事件,这种不确定性会增加,在这种情况下,经验很少,客户输入到灾难模型中的风险数据质量很差。在决策过程中,有效传达模型的局限性及其输出中固有的不确定性是至关重要的。为了让灾难模型有助于预测风险,它们必须包含观察到的趋势。”(图米&雷斯泰尔,2014 年,第 9 页)

总之,这两种可变性的混合将使科学家很难对 CRPRs 的影响进行建模。

换句话说,没有一个单一的模型可以从今天的气候系统中消除这两个固有的不确定性,仅仅因为它们是系统性的。因此,模型的任何输出都会传达这些不确定性。简而言之,我们无法使用单一值的模型对 CRPRs 做出任何精确的预测,比如点估计。当我们处理 crr 时,任何单点估计都是欺骗性的。

尽管如此,我们仍然可以探索一系列潜在的假设情景,为未来的不确定性做准备。我们需要用概率范围(如置信区间)来捕捉场景路径的风险,以测试现有策略的弹性。

c)特定于供应商的偏见

此外,劳埃德的论文包括另一个不确定性来源,当我们使用供应商模型时,可能会出现这种不确定性:特定于供应商的偏差。简而言之,任何供应商模型都基于一组特定的假设,以使其能够分析 CRPRs 的复杂性。这些假设不可避免地成为模型偏差的来源,并设定了模型使用的范围和限制。

总体而言,在劳埃德的论文中,Toumi & Restell 在评估财产和意外保险公司的灾难/危险模型时确定了一些不确定性来源( Toumi & Restell,2014,第 9 页):

  1. 模型风险和尾部风险
  2. CRPRs 不确定性的系统来源:
  • 气候系统的自然/内部可变性
  • 人类引起的变化的影响

3.特定于供应商的偏见

到目前为止,我们在财产和意外保险公司的风险管理框架的背景下讨论了嵌入在财产和意外保险公司的灾难模型中的不确定性的来源。

不可避免的是,财产和意外保险公司的模型框架固有地受到其业务偏见的影响。

换句话说,在更广泛的背景下应用他们的灾难/灾害模型框架来缓解 CRPRs 有一些潜在的限制。

B. 使用保险人的巨灾模型对气候相关物理风险建模的潜在风险

财产和意外伤害保险公司的业务偏见导致在以下三个因素上与 CRPRs 不匹配:即,

  • 风险状况不匹配,
  • 时间范围不匹配,以及
  • 方案基础不匹配。

我们将在本节中逐一讨论这些不匹配。

a)风险状况不匹配:

一般来说,常规保险业务的运作原理是https://link.springer.com/chapter/10.1007/978-94-011-1378-6_1大数法则。( Smith & Kane,1994 )从概念上来说,假设可分散的非系统性风险——这种风险预期以稳定的已知概率实现(平稳性范式)——财产&意外险承保人可以将风险集中起来,分散给大量的被保险人。**

下面简单解释一下大数定律在传统保险业务中是如何工作的。

  • 首先,财产和意外保险公司为保险产品制定了一个足够大的目标销售量方案;然后将保险索赔频率的期望概率应用于目标被保险人,以便估计假设的目标被保险人之间的期望总保险损失(索赔)。
  • 此后,在预期总保险损失(索赔)的基础上,他们计算总运营成本和利润率,以估计特定保险产品所需的总收入。
  • 最后,他们用目标被保险人的总收入除以保险产品的保险费。

这是保险费定价(精算定价)的简化版本。

简单地说,财产保险公司通过分散特定被保险人的非系统性风险来获利。在这种机制下,有一个关键的平稳性假设。平稳性是一种统计属性(如均值和方差)不会随时间而改变的状态。财产和意外保险公司可以期望利润,因为他们假设保险风险是平稳的。

相比之下,CRPRs 正不可逆转地偏离过去的平衡。由于我们处于不可逆的瞬态,CRPRs 的统计特性将会改变。因此,一开始就以稳定的范式评估民事登记和报告制度是错误的。下一段引言阐明了在处理气候系统时区分平稳性和非平稳性行为的重要性。

此外,由于 crpr 在所有地理位置都在加强,它将越来越系统化,因此很难在地理上使其 crpr 的覆盖范围朝着未来多样化。

为了应对传统保险风险管理框架的这一特殊限制,出现了一类新的产品,称为替代风险转移(ART) 。艺术的概念基本上是一个零和游戏。巨灾债券(CAT bonds)是 ART 的一个例子。财产&意外险保险公司可能会出售 CAT 债券,将系统风险转移给买家,以换取一系列类似息票的分期付款。不可避免的是,在气候变化的时代,随着对 CRPRs 的预期上升,CAT 债券将变得越来越难以销售。

总体而言,传统保险风险管理框架中的非系统性和平稳性假设与 CRPRs 的系统性和非平稳性风险状况不一致。从这个意义上说,简单地照搬财产和意外伤害保险公司的灾难/灾害风险管理框架来评估 CRPRs 可能会遭遇风险状况不匹配的问题:系统性与非系统性;平稳性与非平稳性。

b)时间跨度不匹配

CRPRs 在发展和时间上都是前所未有的和不确定的。因此,它们是多时间范围:短期、中期和长期范围。

相反,在许多情况下,财产保险项目的期限通常是一年。不可避免的是,这种商业实践使他们倾向于将其灾难/危害风险管理框架的时间范围定得很短。

从主要的一般保险公司之一美国国际集团与气候相关的财务披露摘录中,我们可以看到一般保险公司风险管理框架的一个例子:

“我们的大部分普通保单每年都会续保,这让我们有机会定期对风险进行再保险和重新定价。在一般保险及人寿和退休业务的战略制定和资产负债管理决策中都考虑了中长期影响。长期的基本趋势和重大变化更具挑战性,因为很难做出精确的预测。”( AIG2,2020,第 8–9 页)

他们对财产和意外保险公司业务的短期偏见与 CRPRs 的多时间范围行为不匹配。这很可能解释了简单地复制财产和意外保险公司的灾难风险管理框架以减轻 CRPRs 的不确定性的来源。换句话说,当应用财险公司的巨灾风险模型来管理 CRPRs 时,使用者需要适当地调整时间范围。

此外,如下所述,它们的短期偏差还会导致额外的不匹配。

c)情景基础不匹配(数据集偏差)

基于可以基于历史数据预测短期未来的假设,保险业使用历史数据并做出一组调整来评估短期范围内的巨灾风险。

重复地说,过去没有关于 CRPRs 将显现的前所未有的未来的信息。

历史模拟法的优点是将实际发生的事件结合起来,不需要指定分布或估计参数,但只有在未来与过去相似的情况下才有用(吉斯&麦卡锡·贝克,2021 年,第 49 页)

任何采用历史数据集的模型都需要在用户端进行仔细检查,以确定所嵌入的使用假设是否合适。

特别是,在历史数据上天真地应用数据驱动的机器学习算法,如深度学习,可能会使模型欺骗性地过度适应过去的模式,并导致模型不稳定(方差)。因此,我们最终可能会低估未来的风险。尽管它们善于从数据集中发现历史模式,但它们并不是为了预测风险变量之间未来模式的前所未有的非平稳性变化而设计的。

CRPRs 是路径依赖的,非常不确定。因此,CRPRs 在我们面前有许多潜在的未来道路。当然,没有人知道哪条路会展开。在这种情况下, TCFD 建议采用探索性情景分析来预测 CRPRs,因为我们可以将跨多个时间范围的多个假设情景纳入模型。

然而,场景分析也有其固有的设计限制。它依赖于主观的假设情景。最近,博士托尼·休斯在 LinkedIn 上发帖阐明了情景分析所涉及的内在不确定性:假设的有效性没有保证;统计模型的错误设定会导致误导性的结果。(休斯,2022 )

截图:摘自托尼·休斯博士 2022 年 10 月 20 日的 LinkedIn 帖子

结论

我们讨论了财产和意外保险公司的灾难模型中涉及的一些不确定性来源。

  1. 模型风险和尾部风险
  2. CRPRs 不确定性的系统来源:
  • 气候系统的自然/内部可变性
  • 人类引起的变化的影响

3.供应商特有的风险

换句话说,在选择评估与气候相关的物理风险的模型时,必须尽职调查以下清单。

  • 尾部风险是否包含在使用中的供应商模型中:供应商模型如何在模拟前所未有的极端事件时包含增强的复杂性和不确定性
  • 模型如何使用户能够将观察到的趋势纳入 CRPRs 评估中
  • 模型规格如何传达模型的局限性以及决策过程输出中固有的不确定性。

此外,我阐述了我个人的观点,即应用财产和意外保险公司的灾难模型评估 CRPRs 的常见做法可能会因以下 3 种类型的不匹配而遭受模型风险:

  • 风险状况不匹配:系统性风险与非系统性风险;非平稳性与平稳性行为
  • 时间跨度不匹配:多跨度与短期
  • 场景基础不匹配(数据库偏差):未来场景与历史数据

尽管如此,我无意指责财产和意外保险公司的灾难/风险模型。相反,我更想呼吁用户注意与应用财产和意外保险公司的风险管理框架在更广泛的意义上缓解 CRPRs 相关的潜在风险。

总的来说,如果用户选择使用财产和意外保险公司的模型,用户可以通过将 CRPRs 的以下三个特征结合到他们在 CRPRs 管理中的使用分析中来定制财产和意外保险公司的风险管理框架:

  • 不可分散的系统性和非平稳性风险状况
  • 多时间范围
  • 潜在的多种未来情景

这可能不是一个全面的列表,无法涵盖在更广泛的背景下使用财产和意外伤害保险公司的灾难模型来缓解 CRPRs 的所有相关风险。

除了所有这些风险之外,一些怀有政治目的的最终用户可能会玩主观/有偏见的游戏,做出低估的预测,以操纵公众对 CRPRs 影响的看法。场景分析可以服务于 尽你所能游戏 式——就像尽你所能吃自助餐——的虐待模式。

无论我们使用什么模型,我们都是我们腐败和/或不稳定人性的囚徒。没有一种模式能把我们从自我幻想和政治操纵中解放出来。

总的来说,数据科学专业人员需要提高对这些人类缺陷和任何被玩弄/滥用的模型的脆弱性的自我意识,并就所使用的任何模型的有限范围与受众进行良好的沟通:特别是其假设和模型输出的影响。(气候变化工作组,2020 年,第 10 页)

最后,我想用英国统计学家乔治·博克斯的一句精辟的话来结束这篇文章:

“所有模型都是错的,但有些是有用的”(维基百科,2022)

我希望这篇文章将为数据科学专业人员提供一个解决与 crr 相关的模型风险的检查表基础。

感谢阅读。

参考

关于“注意力”和“变形金刚”你需要知道的一切——深入理解——第 1 部分

原文:https://towardsdatascience.com/all-you-need-to-know-about-attention-and-transformers-in-depth-understanding-part-1-552f0b41d021

注意力、自我注意力、多头注意力和变形金刚

图一。来源:阿瑟尼·托古列夫在 Unsplash 上拍摄的照片

这是一篇很长的文章,谈论了人们需要知道的关于注意力机制的几乎所有事情,包括自我注意、查询、键、值、多头注意、掩蔽多头注意和变形金刚,包括伯特和 GPT 的一些细节。因此,我将文章分为两部分。在本文中,我将介绍所有的关注点,在下一篇文章中,我将深入探讨变压器网络架构。

内容:

  1. rnn 面临的挑战以及变压器模型如何帮助克服这些挑战
  2. 注意机制

2.1 自我关注

2.2 查询、键和值

2.3 注意力的神经网络表征

2.4 多头注意

3.变形金刚(下一个故事继续)

简介

注意力机制于 2014 年首次用于计算机视觉,试图理解神经网络在进行预测时正在看什么。这是试图理解卷积神经网络(CNN)输出的第一步。2015 年,注意力首次用于对齐机器翻译中的自然语言处理(NLP)。最后,在 2017 年,注意力机制被用于 Transformer networks 进行语言建模。自那以后,变压器已经超越了递归神经网络(RNNs)的预测精度,成为 NLP 任务的最新技术。

1.rnn 面临的挑战以及变压器模型如何帮助克服这些挑战

1.1 RNN 问题 1——遭受长期依赖的问题。rnn 不能很好地处理长文本文档。

变压器解决方案—变压器网络几乎只使用关注模块。注意力有助于在序列的任何部分之间建立联系,因此长程相关性不再是问题。对于变压器,长程相关性与任何其他短程相关性一样有可能被考虑在内。

1.2。 RNN 问题 2 —遭遇渐变消失和渐变爆炸。

变压器解决方案—几乎没有渐变消失或爆炸问题。在变压器网络中,整个序列是同时训练的,在此基础上只需增加几层。因此渐变消失或爆炸很少是一个问题。

1.3。 RNN 问题 3—rnn 需要更大的训练步长才能达到局部/全局最小值。RNNs 可以被想象成一个展开的非常深的网络。网络的大小取决于序列的长度。这就产生了许多参数,而这些参数中的大多数都是相互关联的。因此,优化需要更长的训练时间和许多步骤。

变压器解决方案 —比 RNN 需要更少的训练步骤。

1.4。 RNN 问题 4—RNNs 不允许并行计算。GPU 有助于实现并行计算。但是 rnn 是作为顺序模型工作的,即网络中的所有计算都是顺序发生的,不能并行化。

变压器解决方案 —变压器网络中的无递归允许并行计算。所以每一步都可以并行计算。

2.注意机制

2.1 自我关注

图二。解释自我关注的例子(来源:图片由作者创建)

想想这句话——“巴克很可爱,他是一只狗”。这个句子有 9 个单词或标记。如果我们只考虑句子中的单词“he ”,我们会看到“and”和“is”是与它非常接近的两个单词。但是这些词没有给“他”一词任何上下文。相反,单词“Bark”和“dog”在句子中与“he”更相关。从这一点上,我们明白,在一个句子中,邻近并不总是相关的,但上下文更相关。

当这个句子被输入计算机时,它将每个单词视为一个标记 t,并且每个标记都有一个单词嵌入 V 。但是这些单词嵌入没有上下文。因此,这个想法是应用某种加权或相似性来获得最终的单词嵌入 Y ,这比初始嵌入 V 具有更多的上下文。

在嵌入空间中,相似的单词看起来靠得更近或者具有相似的嵌入。例如,单词“king”将更多地与单词“queen”和“royalty”相关,而不是与单词“zebra”相关。同样,“斑马”将更多地与“马”和“条纹”相关,而不是与“情感”这个词相关。要了解更多关于嵌入空间的信息,请访问吴恩达的视频( NLP 和单词嵌入)。

所以,直觉上,如果‘国王’这个词出现在句首,而‘女王’这个词出现在句尾,他们应该会给对方提供更好的语境。我们使用这种思想,通过将单词嵌入相乘(点积)来获得更多的上下文,从而找到权重向量 W,。因此,在句子树皮很可爱,他是一只狗,中,我们没有使用单词嵌入,而是将每个单词的嵌入彼此相乘。图 3 应该能更好地说明这一点。

图 3。寻找权重并获得最终嵌入(来源:图片由作者创建)

正如我们在图 3 中看到的,我们首先通过将第一个单词的初始嵌入与句子中所有其他单词的嵌入相乘(点积)来找到权重。这些权重(W11 到 W19)也被归一化为总和为 1。接下来,这些权重乘以句子中所有单词的初始嵌入。

W11 V1 + W12 V2 + …W19 V9 = Y1

W11 到 W19 都是具有第一个单词 V1 的上下文的权重。因此,当我们将这些权重乘以每个单词时,我们实际上是朝着第一个单词的方向重新加权所有其他单词。所以从某种意义上来说,单词'吠叫'现在更倾向于单词''和'可爱',而不是紧随其后的单词。这在某种程度上提供了一些背景。

对所有单词都重复这一过程,这样每个单词都可以从句子中的其他单词中获得一些上下文信息。

图 4。上述步骤的图示(来源:图片由作者创建)

图 4 使用图示更好地理解了获得 Y1 的上述步骤。

这里有趣的是,没有训练权重,单词的顺序或接近度对彼此没有影响。此外,这个过程与句子的长度无关,也就是说,句子中单词的多与少并不重要。这种给句子中的单词添加一些上下文的方法被称为自我关注

2.2 查询、键和值

自我关注的问题是什么都没有被训练。但是,如果我们添加一些可训练的参数,网络就可以学习一些模式,给出更好的上下文。该可训练参数可以是其值被训练的矩阵。因此引入了查询、键和值的概念。

我们再来考虑一下前面的句子——“树皮很可爱,他是狗”。在图 4《自我关注》中,我们看到最初的单词嵌入( V )被使用了 3 次。1st 作为第一个单词嵌入和句子中所有其他单词(包括本身,2nd)的点积得到权重,然后再乘以(第 3 次)权重,得到最终的带上下文的嵌入。这三个 V 的出现可以由三个术语查询来代替。

假设我们想让所有的单词都与第一个单词 V1 相似。然后我们发送 V1 作为查询词。然后,这个查询单词将与句子中的所有单词(V1 到第 9 版)进行点积,这些就是关键字。所以查询和键的组合给了我们权重。这些权重然后再次与作为值的所有单词(V1 到 V9)相乘。我们已经有了查询、键和值。如果您仍然有一些疑问,图 5 应该能够消除它们。

图 5。表示查询、键和值(来源:作者创建的图片)

但是等等,我们还没有添加任何可以训练的矩阵。这很简单。我们知道,如果一个 1×k 形状的向量乘以一个 k×k 形状的矩阵,我们得到 1×k 形状的向量作为输出。记住这一点,让我们用形状为 k×k 的矩阵 Mk(键矩阵)乘以从 V1 到 V10 的每个键(形状为 1×k 的每个键)。类似地,查询向量乘以矩阵 Mq(查询矩阵)。,并且值向量乘以值矩阵 Mv。这些矩阵 Mk、Mq 和 Mv 中的所有值现在都可以被神经网络训练,并且给出比仅仅使用自我注意好得多的上下文。为了更好地理解,图 6 展示了我刚才解释的内容的图示。

图 6。键矩阵、查询矩阵和值矩阵(来源:图片由作者创建)

现在我们知道了键、查询和值的直觉,让我们看一个数据库分析和 Attention 背后的官方步骤和公式。

让我们通过研究一个数据库的例子来理解注意力机制。因此,在一个数据库中,如果我们想要基于一个查询 q 和 k i 和来检索某个 v i ,我们可以使用一个查询来识别对应于某个值的键,这样就可以完成一些操作。注意力可以被认为是一个类似于这种数据库技术的过程,但是以一种更加概率化的方式。下图展示了这一点。

图 7 显示了在数据库中检索数据的步骤。假设我们向数据库发送一个查询,一些操作会找出数据库中哪个键与查询最相似。一旦找到键,它将把对应于该键的值作为输出发送出去。在图中,操作发现查询与键 5 最相似,因此输出值 5。

图 7。数据库中的值检索过程(来源:作者创建的图片)

注意力机制是一种模仿这种提取过程的神经结构。

  1. 注意机制测量查询 q 和每个键值 k i. 之间的相似性
  2. 这种相似性返回每个键值的权重。
  3. 最后,它产生一个输出,这个输出是我们数据库中所有值的加权组合。

数据库检索和注意力在某种意义上的唯一区别是,在数据库检索中我们只得到一个值作为输入,但在这里我们得到的是值的加权组合。在注意机制中,如果一个查询与关键字 1 和关键字 4 最相似,那么这两个关键字将获得最大的权重,并且输出将是值 1 和值 4 的组合。

F 图 8 显示了从查询、键和值获得最终关注值所需的步骤。下面详细解释了每个步骤。

(键值 k 为向量,相似度值 S 为标量,权值(softmax)值 a 为标量,值 V 为向量)

图 8。获得关注值的步骤(来源:图片由作者创作)

第一步。

步骤 1 包含关键字和查询以及各自的相似性度量。查询 q 影响相似度。我们有查询和键,我们计算相似度。相似性是查询 q 和键 k 的某个函数。查询和键都是一些嵌入向量。相似性 S 可以使用各种方法计算,如图 9 所示。

图 9。计算相似度的方法(来源:作者创建的图像)

相似性可以是查询和关键字的简单点积。可以定标点积,其中 qk、的点积除以每个键的维数的平方根、 d、这是最常用的两种寻找相似性的技术。

通常,通过使用权重矩阵 W,将查询投影到新的空间,然后使用关键字 k 进行点积。内核方法也可以作为一种相似。

第二步。

第二步是找到重量。这是通过使用' SoftMax '完成的。公式如下所示。(exp 是指数型的)

相似性像完全连接的层一样连接到权重。

第三步。

步骤 3 是 softmax ( a )的结果与相应值( V )的加权组合。 a 的第一个值与 V 的第一个值相乘,然后与 a 的第二个值与值 V 的第二个值的乘积相加,依此类推。我们获得的最终输出是所期望的关注值。

三个步骤的总结:

W 在查询 q 和关键字 k 的帮助下,我们获得关注值,这是值 V、的加权和/线性组合,权重来自查询和关键字之间的某种相似性。

2.3 注意的神经网络表征

图 10。注意块的神经网络表示(来源:图片由作者创建)

图 10 显示了注意块的神经网络表示。单词嵌入首先被传递到一些线性层中。这些线性层没有“偏差”项,因此只是矩阵乘法。其中一层称为“键”,另一层称为“查询”,最后一层称为“值”。如果在键和查询之间执行矩阵乘法,然后归一化,我们就得到权重。然后将这些权重乘以这些值,并求和,得到最终的注意力向量。这个模块现在可以用于神经网络,被称为注意模块。可以添加多个这样的关注块来提供更多的上下文。最好的部分是,我们可以得到一个梯度反向传播来更新注意块(键、查询、值的权重)。

2.4 多头关注

为了克服使用单一注意力的一些缺陷,使用了多头注意力。让我们回到那句话——“树皮很可爱,他是狗”。在这里,如果我们用“狗”这个词,从语法上来说,我们理解“吠”、“可爱”和“他”应该与“狗”这个词有某种意义或关联。这几个字说的是狗的名字叫 Bark,是公狗,是可爱的狗。仅仅一个注意机制可能不能正确地识别这三个词与“狗”相关,我们可以说三个注意在这里更好地用单词“狗”来表示这三个词。这减少了寻找所有重要单词的注意力负担,也增加了容易找到更多相关单词的机会。

因此,让我们添加更多的线性层作为键、查询和值。这些线性层是并行训练的,并且彼此具有独立的权重。所以现在,每个值、键和查询给我们三个输出,而不是一个。这三个键和查询现在给出了三种不同的权重。这三个权重然后用矩阵与这三个值相乘,以给出三个倍数输出。这三个注意块最终被连接起来,以给出一个最终的注意输出。这种表示如图 11 所示。

图 11。具有 3 个线性层的多头注意力(来源:图片由作者创建)

但 3 只是我们选的一个随机数。在实际场景中,这些可以是任意数量的线性层,这些被称为头部( h )。也就是说,可以有 h 个线性层给出 h 个注意力输出,然后将它们连接在一起。而这也正是它被称为多头注意力(multi-head)的原因。图 11 的更简单版本,但是图 12 中示出了具有 h 数量的头部。

图 12。具有“h”层的多头注意力(来源:图片由作者创建)

现在,我们已经了解了注意力、查询、键、值和多头注意力背后的机制和思想,我们已经涵盖了变压器网络的所有重要构建模块。在下一个故事中,我将讲述所有这些模块如何堆叠在一起形成变压器网络,并讲述一些基于变压器的网络,如伯特和 GPT。

参考资料:

Ashish Vaswani、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan N. Gomez、ukasz Kaiser 和 Illia Polosukhin。2017.你需要的只是关注。《第 31 届国际神经信息处理系统会议录》(NIPS'17)。美国纽约州红钩市柯伦联合有限公司,邮编 6000–6010。

关于“注意力”和“变形金刚”你需要知道的一切——深入理解——第二部分

原文:https://towardsdatascience.com/all-you-need-to-know-about-attention-and-transformers-in-depth-understanding-part-2-bf2403804ada

注意、自我注意、多头注意、蒙面多头注意、变形金刚、伯特和 GPT

在前面的故事中,我已经解释了什么是注意力机制,以及与变形金刚相关的一些重要关键字和块,如自我注意力、查询、键和值以及多头注意力。

要了解关于这些话题的更多信息,请访问本故事的第一部分— 【你需要知道的关于‘注意力’和‘变形金刚’的一切—深入了解—第一部分

在这一部分,我将解释这些注意力块如何帮助创建变压器网络,并详细讨论网络中的所有块。

内容:

  1. rnn 面临的挑战以及变压器模型如何帮助克服这些挑战(在第 1 部分中讨论)
  2. 注意机制 —自我注意、查询、键、值、多头注意(在第 1 部分中讨论)
  3. 变压器网络
  4. GPT 基础知识(将在第 3 部分中介绍)
  5. BERT 的基础知识(将在第 3 部分讨论)

3.变压器网络

论文— 关注是你所需要的全部 (2017)

图一。变形金刚网络(来源:图片来自原论文)

图 1 显示了变压器网络。这个网络已经取代 RNNs 成为 NLP 甚至是计算机视觉中的最佳模型( 视觉变形金刚 )。

该网络包含两部分——编码器和解码器。

在机器翻译中,编码器用于对初始句子进行编码,解码器用于产生翻译后的句子。转换器的编码器可以并行处理整个句子,比 RNNs 更快更好,RNNs 一次处理一个单词。

3.1 编码器模块

图二。变压器网络的编码器部分(来源:图片来自原论文)

编码器网络从输入开始。在这里,整个句子是一次输入的。然后,它们被嵌入到“输入嵌入模块。然后将一个“位置编码加到句子中的每个单词上。这种编码对于理解每个单词在句子中的位置至关重要。如果没有位置嵌入,该模型会将整个句子视为一个装满单词的袋子,没有任何顺序或意义。

详细:

3.1.1 输入嵌入 —句子中的单词‘dog’可以利用嵌入空间获得一个向量嵌入。嵌入只是将任何语言中的一个单词转换成它的向量表示。图 3 显示了一个例子。在嵌入空间中,相似的词具有相似的嵌入,例如,单词' cat '和单词' kitty '在嵌入空间中会落得很近,而单词' cat '和' emotion '在空间中会落得更远。

图 3。输入嵌入(来源:作者创建的图像)

3.1.2 位置编码

一个词在不同的句子中可能有不同的意思。比如 a 中的狗这个词,我拥有一只可爱的狗(动物/宠物—位置 5)b .你是一只多么懒的狗啊!(不值钱——位置 4),有不同的含义。来帮助使用位置编码。它是一个向量,根据单词在句子中的上下文和位置给出信息。

在任何一个句子中,单词都是一个接一个地出现,具有重要的意义。如果句子中的单词杂乱无章,那么这个句子就没有意义。但是当转换器加载句子时,它不是按顺序而是并行加载。由于 transformer 架构不包括并行加载时单词的顺序,因此我们必须明确定义单词在句子中的位置。这有助于转换者理解句子中一个单词在另一个单词之后。这就是位置嵌入派上用场的地方。这是一种定义单词位置的矢量编码。这种位置嵌入在进入注意力网络之前被添加到输入嵌入中。图 4 给出了输入嵌入和位置嵌入在输入到注意力网络之前的直观理解。

图 4。对位置嵌入的直观理解(来源:图片由作者创建)

有多种方式来定义这些位置嵌入。例如,在最初的论文中,作者使用正弦和余弦交替函数来定义嵌入,如图 5 所示。

图 5。原始文件中使用的位置嵌入(来源:原始文件中的图像)

尽管这种嵌入在文本数据上工作得很好,但在图像数据上却不行。所以一个物体(文本/图像)的位置可以有多种嵌入方式,可以固定,也可以在训练中学习。基本思想是,这种嵌入允许 transformer 架构理解单词在句子中的位置,而不是通过混淆单词来弄乱意思。

在字/输入嵌入和位置嵌入完成之后,嵌入然后流入编码器的最重要部分,该部分包含两个重要的块——一个'多头关注块和一个'前馈网络。

3.1.3 多头关注

这是奇迹发生的主要街区。要了解多头注意力,请访问此链接— 2.4 多头注意力

作为输入,这个模块接收一个包含子向量(句子中的单词)的向量(句子)。多头注意力然后计算每个位置与向量的每隔一个位置之间的注意力。

图 6。成比例的点积注意力(来源:图片来自原论文)

上图显示了成比例的点积注意力。这与自我关注完全相同,只是增加了两个模块(比例和遮罩)。要详细了解 Sef-Attention,请访问此链接— 2.1 自我关注

如图 6 所示,比例注意力完全相同,只是在第一次矩阵乘法(Matmul)后增加了一个比例。

缩放比例如下所示,

缩放后的输出进入遮罩层。这是一个可选层,对机器翻译很有用。

图 7。关注区块(来源:图片由作者创建)

图 7 显示了注意块的神经网络表示。单词嵌入首先被传递到一些线性层中。这些线性层没有“偏差”项,因此只是矩阵乘法。其中一层称为“键”,另一层称为“查询”,最后一层称为“值”。如果在键和查询之间执行矩阵乘法,然后归一化,我们就得到权重。然后将这些权重乘以这些值,并求和,得到最终的注意力向量。这个模块现在可以用于神经网络,被称为注意模块。可以添加多个这样的关注块来提供更多的上下文。最好的部分是,我们可以得到一个梯度反向传播来更新注意块(关键字、查询、值的权重)。

多头注意力接收多个键、查询和值,通过多个按比例缩放的点积注意力块进行馈送,最后将注意力连接起来,给出最终输出。这如图 8 所示。

图 8。多头关注(来源:图片由作者创作)

更简单的解释是:主向量(句子)包含子向量(单词)——每个单词都有位置嵌入。注意力计算将每个单词视为一个'查询,并找到与句子中其他一些单词相对应的一些',然后取对应的'的凸组合。在多头注意力中,选择多个值、查询和键,这提供了多重注意力(更好的单词嵌入上下文)。这些多重关注被连接以给出最终关注值(来自所有多重关注的所有单词的上下文的组合),这比使用单个关注块要好得多。

在 simple words 中,多头关注的想法是采用一个单词嵌入,使用关注(或多重关注)将其与其他一些单词嵌入(或多个单词)相结合,以产生该单词的更好嵌入(嵌入周围单词的更多上下文)。

想法是用不同的权重计算每个查询的多个关注度。

3.1.4 增加&定额和前馈

下一个块是' Add & Norm ,它接受原始单词嵌入的剩余连接,将其添加到来自多头关注的嵌入,然后将其归一化为具有零均值和方差 1。

这被输入到一个“前馈模块,该模块在其输出端也有一个“添加&标准”模块。

在编码器块中,整个多头注意力和前馈块重复 n 次(超参数)。

3.2 解码器模块

图 9。变压器网络的解码器部分(来源:图片来自原论文)

编码器的输出也是一系列嵌入,每个位置一个嵌入,其中每个位置嵌入不仅包含原始单词在该位置的嵌入,还包含关于其它单词的信息,它使用注意力学习这些信息。

然后馈入变压器网络的解码器部分,如图 9 所示。解码器的目的是产生一些输出。在论文中,注意力是你所需要的,这个解码器被用于句子翻译(比如从英语到法语)。所以编码器会接收英语句子,解码器会把它翻译成法语。在其他应用中,网络的解码器部分是不必要的,因此我不会对此进行过多阐述。

解码器模块中的步骤—

  1. 在句子翻译中,解码器模块接收法语句子(用于英语到法语的翻译)。像编码器一样,这里我们添加了一个单词嵌入和一个位置嵌入,并将其馈送到多头注意块。

2.自我关注模块将为法语句子中的每个单词生成一个关注向量,以显示句子中一个单词与另一个单词的相关程度。

3.然后将来自法语句子的注意力向量与来自英语句子的注意力向量进行比较。这是英语到法语单词映射发生的部分。

4.在最后一层,解码器预测将英语单词翻译成最可能的法语单词。

5.整个过程重复多次,以获得整个文本数据的翻译。

图 10 显示了用于上述每个步骤的模块。

图 10。不同解码块在句子翻译中的作用(来源:图片由作者创建)

解码器中有一个新的模块——掩蔽多头注意力。所有其他模块,我们之前已经在编码器中看到过。

3.2.1 掩蔽多头注意

这是一个多头注意力块,其中一些值被屏蔽。屏蔽值的概率无效或不被选择。

例如,在解码时,输出值应该只取决于以前的输出,而不是将来的输出。然后我们掩盖未来的输出。

3.3 结果和结论

图 11。结果(来源:图片来自原始论文)

本文比较了英语到德语和英语到法语的语言翻译,以及其他最先进的语言模型。BLEU 是一种用于语言翻译比较的度量标准。从图 11 中,我们看到 big Transformer 模型在两种翻译任务中都获得了较高的 BLEU 分数。他们还显著改善了培训成本。

总之,变压器模型可以降低计算成本,同时仍能获得最先进的结果。

在这一部分,我解释了变压器网络的编码器和解码器模块,以及每个模块在语言翻译中是如何使用的。在下一个也是最后一个部分(第 3 部分),我将讨论一些最近变得非常著名的重要变压器网络,如 BERT(变压器的双向编码器表示)和 GPT(通用变压器)。

参考资料:

阿什什·瓦斯瓦尼、诺姆·沙泽尔、尼基·帕尔马、雅各布·乌兹科雷特、利翁·琼斯、艾丹·戈麦斯、祖卡斯·凯泽和伊利亚·波洛苏欣。2017.你需要的只是关注。《第 31 届国际神经信息处理系统会议录》(NIPS'17)。美国纽约州红钩市柯伦联合有限公司,邮编 6000–6010。

关于单词包和 Word2Vec —文本特征提取,您需要知道的一切

原文:https://towardsdatascience.com/all-you-need-to-know-about-bag-of-words-and-word2vec-text-feature-extraction-e386d9ed84aa

数据科学

Word2Vec 为什么更好,为什么不够好

塔玛拉·比利斯Unsplash 上拍摄,由作者编辑

W 虽然图像数据可以直接用于深度学习模型(RGB 值作为输入),但文本数据却不是这样。深度学习模型只对数字起作用,而不是像文本一样的符号序列。所以,你需要一种方法从文本中提取有意义的数字特征向量。这被称为特征提取

从现在开始,我们将通过文档来调用单个文本观察,通过语料库来调用文档集合。

**Table of Contents****·** [**Bag of Words**](#38a3)
  ∘ [The Basics](#0e83)
  ∘ [Example on Data](#c38c)
  ∘ [Advantages & Limitations](#f0dd)**·** [**Word2Vec**](#263a)
  ∘ [The Basics](#6400)
  ∘ [Creating Train Data](#b396)
  ∘ [Continuous Bag of Words & Skip-Gram](#2c27)
  ∘ [Advantages & Limitations](#b3bc)**·** [**Summary**](#b0f4)

一袋单词

基础知识

要创建的最直观的特性之一是每个单词在文档中出现的次数。所以,你需要做的是:

  1. 对每个文档进行标记化,并给每个标记一个整数 id。标记分隔符可以是空格和标点符号。
  2. 统计令牌在每个文档中的出现次数。

记号出现的次数称为词频 (tf)。尽管简单,术语频率不一定是最好的语料库表示。根据 Zipf 定律 ,像“the”、“a”、“to”这样的常用词几乎总是文档中出现频率最高的术语/标记。如果你将频率这个词直接输入到分类器中,那些非常频繁的记号将会掩盖那些更罕见但更有趣的记号的频率。

为了解决这个问题,一种最流行的“标准化”术语频率的方法是用文档频率 (idf)的倒数对每个标记进行加权,其计算公式如下

其中m是语料库中的文档总数,df( t )是语料库中包含标记 t 的文档数。加权 tf 称为 tf-idf,由下式给出

对于语料库中文档 d 的令牌 t欧几里德范数 然后归一化所得到的 tf-idf 向量,即,

对于任何 tf-idf 矢量 v

数据示例

作为一个具体的例子,假设您有以下语料库。

corpus = [
    'This is the first document.',
    'This is the second second document.',
    'And the third one.',
    'Is this the first document?',
]

然后,m = 4。经过标记化,语料库中共有 9 个标记:“和”、“文档”、“第一”、“是”、“一”、“第二”、“the”、“第三”、“this”。因此,术语频率可以表示为大小为 4×9 的矩阵:

词频|图片作者作者

df( t )然后可以通过计算每个令牌的非零值的数量,根据术语频率来计算,idf( t )使用上面的公式来计算:

文档频率和文档频率的倒数|图片作者作者

tf-idf( td )通过将上面的 tf 矩阵乘以每个令牌的 idf 来获得。

tf-idf |图片作者作者

对于每个文档,tf-idf 的欧几里德范数分别显示如下。

2.97, 5.36, 4.25, 2.97

然后,通过将原始 tf-idf 除以每个文档的适当欧几里德范数来计算归一化的 tf-idf。您可以获得标准化的 tf-idf,如下所示。

归一化的 tf-idf |图片由作者

这些是输入模型的最终特征。

优势和局限性

因为每个单词都由一个标量表示,所以文本的单词包表示是非常轻量级的,并且容易理解。然而,它至少有两个明显的缺点:

  1. 特征维度线性依赖于唯一标记的数量(姑且称之为vocab_size),当你有一个大的语料库时,这是一个坏消息。您可以丢弃一些最少出现的标记以节省空间,但这样您就从数据中丢弃了一些潜在有用的信息。
  2. 如果您查看上面关于数据的示例中的第一个和最后一个文档,您会发现它们是不同的文档,但是具有相同的特征向量。这是因为单词包没有保留标记之间的关系。

为了解决限制 2,您可以添加 n-grams 作为新特性,它捕获 n 个连续的标记(以及它们之间的关系)。然而,这又导致了限制 1,您需要为额外的功能节省额外的空间。

Word2Vec

基础知识

Word2Vec 同时解决了单词包表示的两个限制:

  1. 不是每个文档都有一个长度等于vocab_size的特征向量,现在每个标记都变成了一个长度由您决定的数字(通常为 100–1000,姑且称之为embed_dim)的向量。
  2. Word2Vec 通过考虑邻近的标记来对标记的上下文进行矢量化,而不是对标记本身进行矢量化。

结果是一个vocab_size × embed_dim矩阵。那么,Word2Vec 是如何学习一个令牌的上下文的呢?

注意:在继续之前,最好知道什么是密集神经网络和激活函数。这里有一个故事。

</5-most-well-known-cnn-architectures-visualized-af76f1f0065e>

Word2Vec 采用了具有单个隐藏层的 密集神经网络 的使用,该隐藏层没有激活函数,在给定另一个独热码编码令牌的情况下预测一个独热码编码令牌。

输入层有vocab_size个神经元,隐藏层有embed_dim个神经元,输出层也有vocab_size个神经元。输出图层通过 softmax 激活函数传递,该函数将问题视为多类。

下面是网络的架构,其中xᵢ∑{ 0,1}对令牌进行一热编码后,∑表示上一层输出的加权和,s 表示 softmax。

Word2Vec 训练的密集神经网络架构|图片由作者

与来自输入层的隐藏层相关联的权重矩阵被称为字嵌入,并且具有维度vocab_size × embed_dim。当您在 NLP 任务中使用它时,它充当一个查找表,将单词转换为向量(因此得名)。在训练 Word2Vec 的最后,你把除了嵌入这个词以外的东西都扔掉了。

你有神经网络模型。现在,火车数据怎么样?

创建列车数据

创建数据来训练神经网络包括将每个单词分配为中心单词和其相邻单词作为上下文单词。相邻单词的数量由窗口(超参数)定义。

具体来说,让我们回到之前的例子。我们将使用 window = 1(中心单词左右各一个上下文单词)。生成列车数据的过程如下所示。

创建列车数据|图片作者作者

绿色单词是中心单词,橙色单词是上下文单词。一次一个单词,你正在创建(中心,上下文)对。对语料库中的每个文档重复这个步骤。

Word2Vec 的想法是,相似的中心词将出现在相似的上下文中,您可以通过使用(中心,上下文)对重复训练您的模型来了解这种关系。

连续的单词包和跳格

Word2Vec 有两种方法学习令牌的上下文。两者的区别在于输入数据和使用的标签。

1。连续单词包(CBOW)

给定上下文单词,CBOW 将取其独热编码的平均值,并预测中心单词的独热编码。下图解释了这一过程。

CBOW 如何学习|图片作者作者

2。跳过程序(SG)

给定一个中心单词,SG 将对其进行一次性编码,并在输出时最大化上下文单词的概率。为每个上下文单词计算误差,然后求和。下面是训练过程。

SG 如何学习|图片作者作者

由于 softmax 用于计算输出层中所有单词的概率分布(可能是数百万或更多),因此训练过程在计算上非常昂贵。

为了解决这个问题,您可以将问题重新表述为一组独立的二进制分类任务,并使用 负采样 。新的目标是对于任何给定的(单词,上下文)对,预测单词是否在中心单词的上下文窗口中。

负采样只更新正确的类和少数任意(一个超参数)不正确的类。我们之所以能够做到这一点,是因为在大量的训练数据中,我们会多次看到同一个单词作为目标类。

优势和局限性

SG 可以很好地处理少量的训练数据,并且可以很好地表示不常用的单词或短语。但这是以增加计算成本为代价的。

CBOW 的训练速度比 SG 快几倍,对常用词的准确率略高。如果训练时间是一个大问题,并且您有足够大的数据来克服预测不常用词的问题,CBOW 可能是一个更可行的选择。

由于输入层和输出层之间只有线性关系(在 softmax 之前),Word2Vec 产生的特征向量可以是线性相关的。比如 vec(国王)vec(男人)+ vec(女人)≈ vec(女王),这种对于我们这种小糊状人脑来说是有道理的。

然而,Word2Vec 仍然有一些限制,其中四个是:

  1. Word2Vec 依赖于关于单词的本地信息,即单词的上下文仅依赖于其邻居。
  2. 单词嵌入是训练神经网络的副产品,因此特征向量之间的线性关系是一个黑盒(某种)。
  3. Word2Vec 无法理解词汇外(OOV)单词,即不存在于训练数据中的单词。您可以分配一个用于所有 OOV 单词的 UNK 令牌,或者您可以使用对 OOV 单词稳定的其他模型。
  4. 通过给每个单词分配不同的向量,Word2Vec 忽略单词的https://en.wikipedia.org/wiki/Morphology_(linguistics)。比如、吃被 Word2Vec 认为是独立不同的词,但它们来自同一个词根:。这个信息可能有用。

在下一个故事中,我们将提出并解释理论上可以解决这些限制的嵌入模型。敬请期待!

摘要

感谢到达终点!

Markus SpiskeUnsplash 上拍摄的照片

在这个故事中,向您介绍了两种可以从文本数据中提取特征的方法:

  1. ****单词包标记每个文档并统计每个标记的出现次数。
  2. Word2Vec 使用具有单个隐藏层的密集神经网络来从一个热编码的单词中学习单词嵌入。

虽然单词包很简单,但它没有捕捉到标记之间的关系,并且对于大型语料库来说,所获得的特征维数变得非常大。Word2Vec 通过使用(中心,上下文)单词对解决了这个问题,并允许我们定制特征向量的长度。

然而,Word2Vec 并不完美。它不能理解 OOV 的文字,忽略了文字的形态学。

🔥你好!如果你喜欢这个故事,想支持我这个作家,可以考虑 成为会员 。每月只需 5 美元,你就可以无限制地阅读媒体上的所有报道。如果你注册使用我的链接,我会赚一小笔佣金。

🔖想了解更多关于经典机器学习模型的工作原理,以及它们如何优化参数?或者 MLOps 大型项目的例子?有史以来最优秀的文章呢?继续阅读:

**Albers Uzila

艾伯斯·乌兹拉**

从零开始的机器学习

**View list8 stories****Albers Uzila

艾伯斯乌兹拉**

高级优化方法

**View list7 stories****Albers Uzila

艾伯斯乌兹拉**

MLOps 大型项目

**View list6 stories****Albers Uzila

艾伯斯乌兹拉**

我最好的故事

**View list24 stories****Albers Uzila

艾伯斯·乌兹拉**

R 中的数据科学

View list7 stories

[1]托马斯·米科洛夫、程凯、格雷戈·科拉多、杰弗里·迪恩(2013): 向量空间中单词表征的有效估计https://arxiv.org/abs/1301.3781v3

[2]拉迪姆·řehůřek(2022):教程:面向学习的课程Gensim 上的 Word2Vec 模型

[3]悟空·莫汉达斯(2021): 嵌入——用 ML 制作https://madewithml.com

[4] Eric Kim (2019): 揭秘 Skip-Gram 语言建模中的神经网络https://aegis 4048 . github . io

关于梯度提升算法,您只需知道第 1 部分。回归

原文:https://towardsdatascience.com/all-you-need-to-know-about-gradient-boosting-algorithm-part-1-regression-2520a34a502

用例子、数学和代码解释算法

卢卡·布拉沃在 Unsplash 上的照片

梯度推进是表格数据集最流行的机器学习算法之一。它功能强大,足以找到模型目标和要素之间的任何非线性关系,并且具有强大的可用性,可以处理要素上的缺失值、异常值和高基数分类值,而无需任何特殊处理。虽然您可以使用一些流行的库(如 XGBoostLightGBM )来构建准系统梯度增强树,而无需了解算法的任何细节,但是当您开始调整超参数、定制损失函数等时,您仍然想知道它是如何工作的。,以获得更好的模型质量。

本文旨在为您提供关于该算法的所有细节,特别是它的回归算法,包括从头开始的数学和 Python 代码。如果你对分类算法更感兴趣,请看 Part 2

算法与实例

梯度增强是集成方法的一种变体,在这种方法中,您可以创建多个弱模型并将它们组合在一起,以获得更好的整体性能。

在本节中,我们使用下面的样本一步一步地构建梯度推进回归树,该样本在*x**y*之间具有非线性关系,以直观地理解它是如何工作的(下面的所有图片都是作者创建的)。

回归问题的示例

第一步是对目标*y*做出非常天真的预测。我们将初始预测*F₀*作为*y* : 的整体平均值

初始预测:F0 =平均值(y)

你可能会觉得用平均值来预测很傻,但是不要担心。当我们添加更多的弱模型时,我们将改进我们的预测。

为了改进我们的预测,我们将重点关注第一步的残差(即预测误差),因为这是我们希望最小化以获得更好预测的内容。残差*r₁*在下图中显示为蓝色垂直线。

为了最小化这些残差,我们正在建立一个回归树模型,以*x*为特征,残差*r₁ = y − mean(y)* 为目标。背后的原因是,如果我们可以通过建立附加的弱模型找到*x**r₁*之间的一些模式,我们就可以利用它来减少残差。

为了简化演示,我们正在构建非常简单的树,每个树只有一个分支和两个终端节点,称为“stump”。请注意,梯度推进树通常有一个稍微深一点的树,比如有 8 到 32 个终端节点的树。

这里我们创建第一棵树,用两个不同的值*γ₁ = {6.0, −5.9}*预测残差(我们用*γ* (gamma)来表示预测)。

该预测*γ₁*被添加到我们的初始预测*F₀*中,以减少残差。事实上,梯度推进算法并不是简单地将*γ*加到*F*上,因为它使模型过度适应训练数据。相反,*γ*按比例缩小学习率 *ν*,范围在 0 和 1 之间,然后加到*F*

在这个例子中,我们使用了一个相对较大的学习率*ν = 0.9*来使优化过程更容易理解,但是它通常应该是一个小得多的值,例如 0.1。

更新后,我们的组合预测*F₁*变为:

现在,更新的残差*r₂*看起来像这样:

下一步,我们将使用相同的*x*作为特征,更新的残差*r₂*作为目标,再次创建回归树。下面是创建的树:

然后,我们用新的树预测*γ₂*更新我们先前的组合预测*F₁*

我们重复这些步骤,直到模型预测停止改进。下图显示了从 0 到 6 次迭代的优化过程。

您可以看到,随着我们向组合模型中添加更多的树,组合预测*F𝑚* 越来越接近我们的目标 y。这就是梯度增强如何通过组合多个弱模型来预测复杂目标。

数学

在这一节中,我们将深入算法的数学细节。这是数学公式中的整个算法。

来源:改编自维基百科弗里德曼的论文

让我们一行一行地解开这个谜团。

第一步

第一步是创建一个初始常数值预测*F₀**L*是损失函数,在我们的回归案例中是平方损失。

*argmin* 的意思是我们在寻找最小化*ΣL(y*ᵢ*,γ)*的值*γ* 。让我们通过使用实际损失函数来计算值*γ*。为了找到最小化*ΣL**γ* ,我们对*γ**ΣL*的导数。

我们发现*γ* 使得*∂ΣL/∂γ*等于 0。

原来最小化*ΣL*的值*γ*就是*y*的平均值。这就是为什么我们在上一节中使用*y*平均值进行初始预测*F₀*

第二步

从 2–1 到 2–4 的整个步骤 2 过程重复*M*次。*M*表示我们正在创建的树的数量,小的*m*表示每棵树的索引。

第 2 步–1

我们计算残差*rᵢ𝑚*的方法是对损失函数相对于之前的预测*F𝑚-₁*求导,然后乘以 1。正如您在下标索引中看到的,计算每个单个样本*i**rᵢ𝑚*。你们中的一些人可能想知道为什么我们称之为*rᵢ𝑚*残差。该值实际上是负梯度,为我们提供方向(+/)和幅度方面的指导,使损失函数最小化。你很快就会明白为什么我们称之为残差。顺便说一下,这种使用梯度来最小化模型损失的技术非常类似于通常用于优化神经网络的 g 梯度下降技术。(其实两者略有不同。如果你有兴趣,请看看这篇文章详述的那个话题。)

让我们在这里计算残差。方程中的*F𝑚-₁*表示上一步的预测。在第一次迭代中,它是*F₀*。我们正在求解残差方程*rᵢ𝑚*

我们可以去掉 2,因为它只是一个常数。剩下我们*rᵢ𝑚 = yᵢ − F𝑚-₁*。你现在可能明白为什么我们称之为残差了。这也给了我们有趣的见解,即负梯度为我们提供了方向和大小,使损失最小化,实际上只是残差。

步骤 2–2

*j*表示树中的末端节点(即叶子)*m*表示树索引,大写*J*表示叶子总数。

第 2-3 步

我们正在寻找使每个终端节点*j*上的损失函数最小化的*γⱼ𝑚**Σxᵢ∈Rⱼ𝑚 L*表示我们正在合计属于终端节点*Rⱼ𝑚*的所有样本*xᵢ*的损失。让我们把损失函数代入方程。

然后,我们找到使σ(*)的导数等于零的*γⱼ𝑚*

请注意,*nⱼ*表示终端节点*j*的样本数。这意味着最小化损失函数的最优*γⱼ𝑚*是终端节点*Rⱼ𝑚*中残差*rᵢ𝑚*的平均值。换句话说,*γⱼ𝑚*是回归树的常规预测值,其是每个终端节点中目标值(在我们的情况下,残差)的平均值。

第 2-4 步

在最后一步,我们正在更新组合模型*F𝑚*的预测。*γⱼ𝑚1(x ∈ Rⱼ𝑚)*意味着如果给定的*x*落在终端节点*Rⱼ𝑚*中,我们选择值*γⱼm*。由于所有的终端节点都是排他的,任何给定的单个*x*只落入一个终端节点,相应的*γⱼ𝑚*被添加到先前的预测*F𝑚-₁*中,并进行更新预测*F𝑚*

如前所述,*ν*是范围在 0 和 1 之间的学习率,其控制附加树预测*γ*对组合预测*F𝑚*的贡献程度。较小的学习率降低了额外的树预测的效果,但是它基本上也降低了模型过度适应训练数据的机会。

现在我们已经完成了所有步骤。为了获得最佳的模型性能,我们希望迭代第 2 步*M*次,这意味着向组合模型添加*M*树。实际上,您可能经常想要添加超过 100 棵树来获得最佳的模型性能。

你们中的一些人可能会觉得所有这些数学都是不必要的复杂,因为前面的部分以一种简单得多的方式展示了基本的思想,而没有那些复杂的东西。其背后的原因是梯度推进被设计成能够处理任何损失函数,只要它是可微的,并且我们所回顾的数学是具有这种灵活性的梯度推进算法的一般化形式。这使得公式有点复杂,但这是算法的美妙之处,因为它在处理各种类型的问题时具有巨大的灵活性和便利性。例如,如果您的问题需要绝对损失而不是平方损失,您可以只替换损失函数,整个算法就像上面定义的那样工作。事实上,流行的梯度增强实现如 XGBoostLightGBM 有各种各样的损失函数,因此您可以选择适合您问题的任何损失函数(参见 XGBoostLightGBM 中提供的各种损失函数)。

密码

在这一节中,我们将把刚刚复习过的数学知识转化为可行的 python 代码,以帮助我们进一步理解算法。代码主要来自 Matt Bowers 的实现,所以所有的荣誉都归于他的工作。我们使用 scikit-learn 的DecisionTreeRegressor来构建树,这有助于我们只关注梯度推进算法本身,而不是树算法。我们正在模仿 scikit-learn 风格的实现,其中您使用fit方法训练模型,并使用predict方法进行预测。

请注意,所有经过训练的树都存储在self.trees列表对象中,当我们使用predict方法进行预测时,会检索到这些树。

接下来,我们将通过查看他们在我们数据上的 RMSE 来检查我们的CustomGradientBoostingRegressor是否与 scikit-learn 的GradientBoostingRegressor表现相同。

正如您在上面的输出中所看到的,两种型号都有完全相同的 RMSE。

结论

我们在这篇文章中回顾的算法只是梯度推进算法的一个选项,它专用于平方损失的回归问题。如果您也对分类算法感兴趣,请查看第 2 部分。

如果您想了解该算法的更多细节,还有一些其他的好资源:

  • StatQuest,Gradient BoostPart 1 Part 2
    这是一个 YouTube 视频,以初学者友好的方式解释了 GB 回归算法,具有很好的视觉效果。
  • 特伦斯·帕尔和杰瑞米·霍华德, 如何解释渐变助推本文还重点介绍了 GB 回归。它解释了平方损失和绝对损失之间的算法差异。
  • 杰罗姆弗里德曼, 贪婪函数逼近:一个梯度推进机这是弗里德曼的论文原文。虽然有点难以理解,但它确实展示了算法的灵活性,他展示了一个通用算法,可以处理任何类型的具有可微损失函数的问题。

你也可以在 Google Colab 链接或者下面的 Github 链接中查看完整的 Python 代码。

https://colab.research.google.com/drive/1mWKDoEtUo82bcxLlwA7YNuEoFzlIY-GG

参考

关于梯度提升算法,您只需知道第 2 部分。分类

原文:https://towardsdatascience.com/all-you-need-to-know-about-gradient-boosting-algorithm-part-2-classification-d3ed8f56541e

用例子、数学和代码解释算法

作者图片

在第 1 部分文章的中,我们详细学习了梯度推进回归算法。正如我们在那篇文章中所评论的,只要损失函数是可微的,算法就足够灵活来处理任何损失函数。这意味着,如果我们只是用处理分类问题的损失函数替换用于回归的损失函数,特别是均方损失,我们就可以在不改变算法本身的情况下执行分类。尽管基本算法是相同的,但我们仍然想知道一些不同之处。在这篇文章中,我们将深入分类算法的所有细节。

算法与实例

梯度推进是集成方法的一种变体,其中您创建多个弱模型(它们通常是决策树)并组合它们以获得整体上更好的性能。在本节中,我们将使用非常简单的示例数据构建一个梯度推进分类模型,以便直观地理解其工作原理。

下图是样本数据。它有二进制类*y* (0 和 1)和两个特性*x₁**x₂*

分类问题示例(图片由作者提供)

我们的目标是建立一个梯度推进模型来分类这两个类。第一步是对所有数据点的第 1 类概率进行统一预测(我们称之为*p*)。统一预测的最合理值可能是第 1 类的比例,这只是一个*y*的平均值。

这是数据和初始预测的 3D 表示。这时预测的只是一个在*y*轴上一直有统一值*p = mean(y)*的平面。

预测平面(图片由作者提供)

在我们的数据中,*y*的均值是 0.56。因为它大于 0.5,所以所有事物都被分类到具有该初始预测的类 1 中。你们中的一些人可能会觉得这个统一值预测没有意义,但是不要担心。当我们添加更多的弱模型时,我们将改进我们的预测。

为了提高我们的预测质量,我们可能希望关注初始预测的残差(即预测误差),因为这是我们希望最小化的。残差定义为*rᵢ = yᵢ − p* ( *i*代表每个数据点的索引)。在下图中,残差显示为棕色线,即从每个数据点到预测平面的垂直线。

残差(图片由作者提供)

为了最小化这些残差,我们正在建立一个回归树模型,以*x₁**x₂*为特征,以残差*r* 为目标。如果我们可以构建一个树,找到*x**r*之间的一些模式,我们就可以通过利用那些找到的模式来减少初始预测*p*的残差。

为了简化演示,我们正在构建非常简单的树,每个树只有一个分支和两个终端节点,称为“stump”。请注意,梯度推进树通常有一个稍微深一点的树,比如有 8 到 32 个终端节点的树。

在这里,我们创建第一棵树,用两个不同的值*r = {0.1, -0.6}*预测残差。

创建的树(作者图片)

如果你已经阅读了关于回归算法的文章,你现在可能会认为我们想要将这些预测值添加到我们的初始预测*p*中,以减少其残差,但是分类的情况略有不同。我们添加到初始预测中的值(我们称之为*γ* gamma)在以下公式中计算:

*Σxᵢ∈Rⱼ*意味着我们正在聚合属于终端节点*Rⱼ*的所有样本*xᵢ*上的 sigma *Σ*中的值。*j*代表每个终端节点的索引。您可能会注意到,分数的分子是终端节点*j*中残差的总和。我们将在下一节讨论给出这个公式的所有计算,但现在让我们只使用它来计算*γ* 。下面是*γ₁**γ₂*的计算值。

这个*γ* 并不是简单的加在我们最初预测的*p* 上。相反,我们将*p*转换成对数概率(我们将把这个对数概率转换值称为*F(x)*),然后加上*γ*。对于那些不熟悉对数优势的人,其定义如下。你可能在逻辑回归中见过它。

对数概率

预测更新的另一个调整是,在添加到对数几率转换预测*F(x)*之前,通过学习率 *ν*缩小*γ*,学习率范围在 0 和 1 之间。这有助于模型不过度拟合训练数据。

在这个例子中,我们使用了一个相对较大的学习率*ν = 0.9*来使优化过程更容易理解,但它通常应该是小得多的值,如 0.1。

通过用实际值替换上式右侧的变量,我们得到了更新的预测值*F₁(x)*

如果我们将 log-odds *F(x)*转换回预测概率*p(x)*(我们将在下一节介绍如何转换),它看起来像下面的一个楼梯状物体。

更新的预测平面(图片由作者提供)

紫色平面是初始预测*p₀* 和它被更新为红色和黄色平面*p₁*

现在,更新后的残差*r*看起来像这样:

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

在下一步中,我们使用相同的*x₁**x₂*作为特征,使用更新的残差*r*作为目标,再次创建回归树。下面是创建的树:

创建的树(作者图片)

我们应用相同的公式来计算*γ*。计算的*γ*和更新的预测*F₂(x)*如下。

同样,如果我们将对数赔率*F₂(x)*转换回预测概率*p₂(x)*,它看起来像下面这样。

更新的预测平面(图片由作者提供)

我们重复这些步骤,直到模型预测停止改进。下图显示了从 0 到 4 次迭代的优化过程。

通过迭代进行预测更新(图片由作者提供)

你可以看到组合预测*p(x)*(红色和黄色平面)越来越接近我们的目标*y*,因为我们在组合模型中添加了更多的树。这就是梯度增强如何通过组合多个弱模型来预测复杂目标。

下图总结了算法的全过程。

算法的过程(图片由作者提供)

数学

在本节中,我们将通过查看数学细节来学习一个更一般化的算法。这是数学公式中的整个算法。

来源:改编自维基百科弗里德曼的论文

让我们一行一行地仔细看看。

第一步

第一步是创建初始常数预测值*F₀**L*是损失函数,我们使用对数损失(或者更一般地称为交叉熵损失)来表示。

原木损失

*yᵢ*是我们的分类目标,它不是 0 就是 1。*p*是 1 类的预测概率。您可能会看到*L*根据目标类别*yᵢ*采用不同的值。

由于*−log(x)**x*的递减函数,预测越好(即*yᵢ=1*增加*p*,我们的损失就越小。

*argmin* 是指我们在寻找使*ΣL(y*ᵢ*,γ)*最小的值*γ* (gamma) 。虽然假设*γ*是预测概率*p*更简单,但我们假设*γ*对数几率为,这使得接下来的所有计算更容易。对于那些忘记我们在上一节中回顾的对数优势定义的人,它被定义为*log(odds) = log(p/(1-p))*

为了能够根据对数优势解决*argmin*问题,我们将损失函数转换成对数优势函数。

现在,我们可能想用对数概率表示的东西来代替上面等式中的*p*。通过转换前面所示的对数优势表达式,*p*可以用对数优势表示:

然后,我们用这个值代替前面的*L*方程中的*p*,并简单地应用它。

现在我们正在寻找最小化*ΣL**γ* (请记住我们假设它是对数概率)。我们对*ΣL*的对数几率求导。

在上面的等式中,我们用*p*替换了包含对数优势的分数,以简化等式。接下来,我们设置*∂ΣL/∂log(odds)*等于 0,并求解*p*

在这个二元分类问题中,*y*不是 0 就是 1。所以,*y*的均值其实就是 1 类的比例。你现在可能明白为什么我们在最初的预测中使用了*p = mean(y)*

由于*γ*是对数概率而不是概率*p*,我们将其转换成对数概率。

第二步

从 2–1 到 2–4 的整个步骤 2 过程重复*M*次。*M*表示我们正在创建的树的数量,小的*m*表示每棵树的索引。

第 2 步–1

我们计算残差*rᵢ𝑚*的方法是对损失函数相对于之前的预测*F𝑚-₁*求导,然后乘以 1。正如您在下标索引中看到的,针对每个单个样本*i*计算*rᵢ𝑚*。你们中的一些人可能想知道为什么我们称之为*rᵢ𝑚*残差。该值实际上是负梯度,为我们提供方向(+/)和幅度,使损失函数最小化。你很快就会明白为什么我们称之为残差。顺便说一下,这种使用梯度来最小化模型损失的技术非常类似于通常用于优化神经网络的梯度下降技术。(其实两者略有不同。如果你有兴趣,请看看这篇文章详述了那个话题。)

让我们在这里计算残差。方程中的*F𝑚-₁*表示上一步的预测。在第一次迭代中,它是*F₀*。和上一步一样,我们对*L*的对数比进行求导,而不是 p,因为我们的预测*F𝑚*是对数比。下面我们使用在上一步中得到的用对数几率表示的*L*

在上一步中,我们也得到了这个等式:

所以,我们可以用*p*代替*rᵢ𝑚*方程中的第二项。

你现在可能明白为什么我们称之为*r*残差了。这也给了我们有趣的见解,即负梯度为我们提供了方向和大小,使损失最小化,实际上只是残差。

步骤 2–2

*j*表示树中的末端节点(即叶子)*m*表示树索引,大写*J*表示叶子总数。

第 2-3 步

我们正在寻找使每个终端节点*j*上的损失函数最小化的*γⱼ𝑚**Σxᵢ∈Rⱼ𝑚 L*表示我们正在合计属于终端节点*Rⱼ𝑚*的所有*xᵢ*的损失。让我们把损失函数代入方程。

*γⱼ𝑚*解这个方程将会非常困难。为了更容易解决,我们使用二阶泰勒多项式来近似*L*。泰勒多项式是一种将任何函数近似为具有无限/有限项数的多项式的方法。虽然我们在这里不研究它的细节,但是如果你感兴趣的话,你可以看看这篇教程,它很好地解释了这个想法。

下面是使用二阶泰勒多项式的*L*的近似值:

我们用这个近似值代替等式*γⱼ𝑚*中的*L*,然后找到使σ(*)的导数等于零的*γⱼ𝑚*的值。

正如我们在上一步中已经计算出的*∂L/∂F*,如下所示:

我们用这个代替*γ* 等式中的*∂L/∂F*

最后,我们得到了上一节中使用的*γⱼ𝑚*值的简化公式。

第 2-4 步

在最后一步,我们正在更新组合模型*F𝑚*的预测。*γⱼ𝑚1(x ∈ Rⱼ𝑚)*意味着如果给定的*x*落在终端节点*Rⱼ𝑚*中,我们选择值*γⱼm*。由于所有的终端节点都是排他的,任何给定的单个*x*只落入一个终端节点,相应的*γⱼ𝑚*被添加到先前的预测*F𝑚-₁*中,然后进行更新的预测*F𝑚*

如前所述,*ν*是范围在 0 和 1 之间的学习率,其控制附加树预测*γ*对组合预测*F𝑚*的贡献程度。较小的学习率降低了额外的树预测的效果,但是它基本上也降低了模型过度适应训练数据的机会。

现在我们已经完成了所有步骤。为了获得最佳的模型性能,我们希望迭代第 2 步*M*次,这意味着向组合模型添加*M*树。实际上,您可能经常想要添加超过 100 棵树来获得最佳的模型性能。

如果你读了我关于回归算法的文章,你可能会觉得分类算法的数学计算比回归复杂得多。虽然*argmin*和对数损失函数的导数计算复杂,但本节开头所示的基本数学算法完全相同。这实际上是梯度推进算法的优雅之处,因为完全相同的算法适用于任何损失函数,只要它是可微分的。事实上,流行的梯度增强实现如 XGBoostLightGBM 有各种各样的损失函数,因此您可以选择适合您问题的任何损失函数(参见 XGBoostLightGBM 中提供的各种损失函数)。

密码

在这一节中,我们将把刚刚复习过的数学知识转化为可行的 python 代码,以帮助我们进一步理解算法。我们使用 scikit-learn 的DecisionTreeRegressor来构建树,这有助于我们只关注梯度推进算法本身,而不是树算法。我们正在模仿 scikit-learn 风格的实现,其中您使用fit方法训练模型,并使用predict方法进行预测。

请注意,所有经过训练的树都存储在self.trees列表对象中,当我们使用predict_proba方法进行预测时,会检索到这些树。

接下来,我们检查我们的CustomGradientBoostingClassifier是否与 scikit-learn 的GradientBoostingClassifier表现相同,方法是查看它们在我们数据上的日志损失。

正如您在上面的输出中看到的,两个模型有完全相同的日志损失。

推荐资源

在这篇博文中,我们回顾了梯度推进分类算法的所有细节。如果您也对回归算法感兴趣,请查看第 1 部分的文章。

如果您想了解该算法的更多细节,还有一些其他的好资源:

  • StatQuest,Gradient boostingPart 3 Part 4
    这些是 YouTube 上的视频,以初学者友好的方式解释了具有很好视觉效果的梯度提升分类算法。
  • 特伦斯·帕尔和杰瑞米·霍华德, 如何解释梯度推进虽然本文的重点是梯度推进回归而不是分类,但它很好地解释了算法的每个细节。
  • 杰罗姆弗里德曼, 贪婪函数逼近:一个梯度推进机这是弗里德曼的论文原文。虽然有点难以理解,但它确实展示了算法的灵活性,他展示了一种通用算法,可以处理任何类型的具有可微损失函数的问题。

你也可以在 Google Colab 链接或者下面的 Github 链接中查看完整的 Python 代码。

https://colab.research.google.com/drive/13p46IFhg3h6BIdjxUcfXPco13jIOCV6I?usp=sharing

参考

关于在 Bioconda 上发布软件,您需要知道的是

原文:https://towardsdatascience.com/all-you-need-to-know-about-publishing-software-on-bioconda-680d48e52868

在 Bioconda 上发布与生物信息学相关的 Python 包

你在安装包和运行大量命令来安装依赖项时遇到过困难吗?如果您幸运的话(大多数情况下您不会幸运),您将在没有任何依赖问题或版本冲突的情况下完成软件包的安装。在跨学科领域的工作让我意识到让这些工具运行起来有多难,除非你知道从编程的角度来看实际上发生了什么。您希望这些工具与所有依赖项捆绑在一起,并且可以在不与您已经安装的工具冲突的情况下安装/运行。

不要害怕——我亲爱的读者们!包装经理将前来救援!这些是软件工具,像 【康达】pip ,它们以一致的方式自动化安装、维护和删除程序的过程。将您的软件包放在这样的存储库中有助于您的用户轻松安装它。它也增加了你的包裹的可见性和可及性。在本文中,我将通过示例代码片段、模板和最佳实践向您介绍如何在 bioconda 上发布与生物信息学相关的 Python 包。

bioconda-recipes GitHub 知识库(作者截图)

Bioconda 和配方

我的社区(包括我自己😃)爱 bioconda !Bioconda 允许您使用 conda 软件包管理器安装与生物医学研究相关的软件包。Bioconda 基本上是一个 通道 (它是存储软件包的位置),其中有 配方 ,这些配方包含已发布软件包的元数据。我们必须创建一个类似的配方,并将其添加到 bioconda 频道。假设你有一个 GitHub 账号,并且已经安装了 Miniconda 或者 Anaconda ,那就让我们开始吧。

步骤 1:设置 bioconda 配方库

如果这是你第一次在 bioconda 上发布,你必须建立你的bio conda-recipes库。

https://github.com/bioconda/bioconda-recipes

点击 此处 创建 bioconda-recipes 库的分支。

现在使用以下命令创建存储库的本地克隆。确保用你的 GitHub 用户名替换<USERNAME>

git clone https://github.com/<USERNAME>/bioconda-recipes.git

然后添加主 bioconda-recipes repo 作为上游遥控器,这样就可以很容易地更新所做的更改。

cd bioconda-recipes
git remote add upstream https://github.com/bioconda/bioconda-recipes.git

如果在制作副本后对原始存储库进行了更改,您可以使用以下命令更新您的本地副本。

git checkout master
git pull upstream master
git push origin master

您可以访问 GitHub 存储库,检查您的 fork 是否与原始存储库保持一致。

检查你的叉子是否是最新的(作者截图)

步骤 2:创建一个分支

建议您创建自己的分支进行工作。假设您的工具名称是mytool,让我们使用下面的命令创建一个分支。随意用你的工具名替换mytool

git checkout -b mytool

第三步:制作你的食谱

配方将包含一个包含配方中所有元数据的meta.yaml文件。通常,对于一个纯 Python 包来说,拥有这个文件就足够了。如果您需要编译更多的包(例如 C/C++包)或导出路径,这些应该添加到 macOS 和 Linux 的build.sh文件或 Windows 的bld.bat文件中。

让我们从食谱目录开始。

cd recipes

使用conda skeleton创建配方

如果您已经在 Python 包索引(PyPI) 中有了您的包,您可以使用如下的conda skeleton命令自动创建一个模板。以下命令将创建一个名为mytool的文件夹,其中包含一个meta.yaml文件。

conda skeleton pypi mytool

创造你自己的食谱

如果您的代码应该从 GitHub 库中下载并编译,那么您应该在 GitHub 上发布一个版本。这里有一个模板,假设你的包遵循基本 Python 包结构。请确保更改<USERNAME>mytool和版本号,以匹配您的 GitHub 帐户和软件包详细信息。

{% set name = "mytool" %}
{% set version = "0.1.0" %}

package:
  name: "{{ name|lower }}"
  version: "{{ version }}"

source:
  url: "https://github.com/<USERNAME>/{{ name }}/archive/v{{ version }}.tar.gz"
  sha256: 8e87ae23bd81b842b95d778291d9379b2d89936c14dbb8216ac9cb2d7104c87a

build:
  number: 0
  noarch: python
  entry_points:
    - myutil=mytool_utils.myutil:main
  script:
    - {{ PYTHON }} -m pip install . --no-deps -vv

requirements:
  build:
    - {{ compiler('cxx') }}
  host:
    - pip
    - python >=3.8
  run:
    - python >=3.8
    - biopython
    - pysam
    - networkx
    - scipy
    - numpy
    - tqdm

test:
  commands:
    - mytool --help

about:
  home: "https://github.com/<USERNAME>/mytool"
  license: MIT
  license_file: LICENSE
  summary: "mytool: This is a test"
  doc_url: "https://mytool.readthedocs.io/"
  dev_url: "https://github.com/<USERNAME>/mytool"

extra:
  recipe-maintainers:
    - <USERNAME>
  identifiers:
    - doi:10.10943/myjournal

package下的包的名称和版本开始。

然后你要指向source下的发布。确保添加具有正确 SHA256 校验和的正确 URL。您可以使用以下命令获得源代码的 SHA256 校验和。

name=<your_repo_name>
version=<release_version>
url=https://github.com/<USERNAME>/$name/archive/v$version.tar.gz
wget -O- $url | shasum -a 256

build下,您必须提供构建号(从 0 开始)、平台和构建命令(这里我为setup.py添加了构建命令)。这里我定义了noarch: python,意思是这是一个纯 python noarch,它不包含任何操作系统特定的文件。如果您想从命令行调用定制脚本,您可以将它们添加到entry-points下,这将通过识别要运行的 Python 函数来定义 shell 命令(在我们的示例中是main)。

然后,您必须在requirements下指定编译器、预处理器、Python 版本和依赖项(如果需要,可以固定特定的版本)。固定包版本时,包及其版本约束必须用空格分隔(例如,python >=3.8)。

接下来,您可以在tests中的commands下指定一个命令来测试安装。我通常调用我的工具的帮助命令。确保这些测试命令快速而简单。

然后,您可以在about下添加关于软件包的详细信息,如主页 URL、文档 URL、许可类型、许可文件和工具摘要。

最后,您可以在recipe-maintainers中添加您的 GitHub 用户名,在extra下的identifiers中添加出版物的 DOI。

如果你想阅读更多关于meta.yaml文件的细节,请查看官方 bioconda 文档

https://bioconda.github.io/tutorials/gcb2020.html#meta-yaml-sections

建议:看看别人的食谱

如果你不知道如何制作食谱,可以看看其他的食谱,比如你可以使用的代码,然后复制到你自己的食谱目录中。这里有一些很棒的食谱示例。

您可以使用以下命令检查您的更改。

git status

步骤 4:提交和推送更改

您可以提交您的更改,并将它们从您的mytool分支推送到您分叉的 GitHub 存储库。

git add mytool
git commit -m 'Add mytool v0.1.0'
git push --set-upstream origin mytool

步骤 5:创建一个拉取请求

现在转到你分叉的 GitHub 库,你会看到一条消息说This branch is 1 commit ahead [...] bioconda:master。您将看到一个名为拉动请求的按钮,点击它并按照说明操作。您应该添加描述您的拉动式请求。这是我为了添加我的一个工具而创建的一个拉请求。如果您的包有出版物,请确保也添加这些详细信息。

创建信息性拉取请求(作者截图)

一旦您创建了拉取请求,bioconda 构建系统将开始测试您的更改。如果你非常幸运,你不会有任何问题,你的构建会通过。如果没有,请编辑您的配方来解决问题,并将更改再次推送到您的分支(一次又一次)。

当您的构建“变绿”并且所有检查都通过时,您可以发出@BiocondaBot please add label命令。

步骤 6:测试构建

同时,您可以发出命令@BiocondaBot please fetch artifacts来获取到 CI 构建的包/容器的链接。您可以下载这些构建,并使用它们在本地测试包。下载完构建之后,创建一个新的 conda 环境(不要安装在您的base环境中),只需运行下面的命令来安装您的包。确保将<build_file_name>替换为您下载的.tar.bz2文件的名称。

conda install -c packages <build_file_name>

现在,您可以使用以下命令测试您的安装。

# show the location of the executable
which mytool

# print the version
mytool --version

# print the help message
mytool --help

如果您已经设置了测试用例,您可以运行它们来确保您的包端到端地正确工作。我通常做这个测试,并在 pull 请求上发布输出,以显示一切正常。这是我在一个拉请求中发布的一个测试运行

如果一切顺利,来自 bioconda 团队的某人将批准您的拉取请求,并且更改将被合并到主存储库中。

最后的想法

Bioconda 仅支持来自defaultsconda-forgebioconda渠道的依赖。因此,如果您有来自任何其他通道的依赖项,就没有办法在meta.yaml文件中指定它们,并且您的配方将无法通过构建(仍然有未解决的问题)。在这种情况下,您将不得不在您自己的频道上发布您的包,我们将在以后的文章中讨论这一点。

希望这篇文章对你在 bioconda 上发布自己的生物信息学软件有用。请随意浏览 bioconda 教程了解更多详情。

快乐包装-出版!

干杯!

参考

[1] 初始设置— Bioconda 文件可在https://bioconda.github.io/contributor/setup.html获得

[2] 投稿工作流程— Bioconda 文档可在https://bioconda.github.io/contributor/workflow.html获得

[3] 教程— Bioconda 文档可在 https://bioconda.github.io/tutorials/index.html获得

[4]我在发布包时遇到的个人问题😁

关于在代码中编写有效的注释,您需要知道的是

原文:https://towardsdatascience.com/all-you-need-to-know-about-writing-effective-comments-in-your-code-41134a5acb60

这和写源代码一样重要

托马斯·博曼斯在 Unsplash上的照片

注释是程序员可读的简短解释或注释,直接写在计算机程序的源代码中。尽管计算机在执行程序时会忽略它们,但在源代码中编写有效的注释可能与实际代码本身一样重要,因为软件总是不完整的。

总有一些事情可以做来改进软件产品或服务,这意味着代码库必须不时更新。对你不理解的代码进行修改或添加新功能几乎是不可能的,因此重要的是代码总是被构造成能被人类阅读。

"程序必须是为人们阅读而写的,并且只是附带地为机器执行而写的."

——哈罗德·艾贝尔森

注释被用来通知、警告和提醒那些没有编写代码的人[和你未来的自己]代码正在做的重要事情。在这篇文章中,我们将关注用 Python 写注释。

**Table of Contents** 
- [**5 Unwritten Rules about comments in Python**](#bfad)
    - [Rule #1 Comments are complete sentences](#d842)
    - [Rule #2 Comments should obey line limits](#c779)
    - [Rule #3 Comments must be on the same indentation level as the code it comments](#792e)
    - [Rule #4 Put a space after the #](#4d4c)
    - [Rule #5 Links don’t replace explanations](#db99)
- [**The different types of comments**](#8f41)
    - [Inline comments](#389e)
    - [Explanation comments](#bf3a)
    - [Summarization comments](#8385) 
    - [Legal comments](#9085)
    - [Code tag comments](#3a69) 

关于 Python 中注释的 5 条不成文的规则

我们可以用 Python 写单行或多行注释。使用#符号定义单行注释,并在行尾结束。Python 不一定有专门的多行注释语法,所以 Pythonistas 可以决定是使用多个单行注释(完全称为块注释),还是使用三重引号多行字符串。

# This is a single line comment. # This is an example 
# of multiple single-line
# comments that make up a 
# multi-line comment."""This is an example
of triple quotes multi-line
strings that make up a
multi-line comment."""

这三种情况都有效,但是您会注意到三重引号多行字符串比使用多个单行注释来定义多行注释可读性更好。因此,如果需要的话,最好使用三重引号多行字符串来定义多行注释——记住,“程序是为人类阅读而编写的。”

你可能会遇到这样的人,他们认为评论是事后的想法,或者告诉你“评论不重要”,忽略它们。去阅读任何流行的库的源代码,看看它们的重要性。如果你希望你的代码 1)更易读和 2)更专业,注释当然是必需的。

然而,有 5 条黄金法则可以确保你写的代码是有效的。

规则 1 注释是完整的句子

注释代码的全部目的是让其他程序员(以及您未来的自己)阅读它,以便更好地理解它所注释的代码中发生了什么。因此,评论应该遵循正确的语法规则,包括标点符号,以确保它向读者提供清晰的信息。

# data from train dir               **<-- Not good** 
data = load_data()# Load data from train directory.        **<-- Good**
data = load_data() 

规则 2 注释应该遵守行的限制

Python 的风格指南 PEP 8 是作为 Python 编程的一套最佳实践而创建的。其中一个指导方针建议行数应该限制在 79 个字符以内:这个指导方针同样适用于源代码和注释。

# This comment is so long that it doesn't even fit on one line in the code cells provided by medium.   **<-- Not good** # This comment is much shorter and within the limits.   **<-- Good**

程序员总是打破 PEP 8 建议的行限制,这没什么——这毕竟只是一个指南。但是你的评论仍然应该遵守你和你的团队(或者你自己,如果你是一个人的话)达成的任何行限制。

规则 3 注释必须和它所注释的代码在同一缩进层次上

缩进是指某些代码开头的空格数;Python 使用四个空格作为默认缩进来对代码进行分组。在不同的缩进层次上写注释不会导致你的程序崩溃,但是当它在同一层次上的时候就容易理解多了。

**def** example_function(): 
# Perform a random calculation.        **<-- Not good**
    random = 24 + 4
    **return** random**def** example_function(): 
    # Perform a random calculation.       ** <-- Good**
    random = 24 + 4
    **return** random

规则#4 在#后面加一个空格

#后面加一个空格有助于可读性。

#This is valid but not easy to read.        **<-- Not good**
# This is valid and easy to read.           **<-- Good**

规则 5 链接不能代替解释

有时我们可能需要链接到外部页面来进一步解释我们的代码在做什么。仅仅留下一个到页面的链接不如在链接到外部资源之前写下为什么要实现代码有效。这样做的原因是,页面可以被删除,然后你会留下一个无法解释的链接,导航到任何地方。

: 下面的示例代码摘自Scikit-learn code base

**# Not good**
**def** _check_input_parameters(**self**, X, y, groups): 

    --- snip ---
    # see [https://github.com/scikit-learn/scikit-learn/issues/15149](https://github.com/scikit-learn/scikit-learn/issues/15149)
 **if not** _yields_constant_splits(self._checked_cv_orig): 
        **raise ValueError**(
            "The cv parameter must yield consistent folds across "
            "calls to split(). Set its random_state to an int, or "
            " set shuffle=False."
        )
    --- snip ---**# Good
def** _check_input_parameters(**self**, X, y, groups): 

    --- snip ---     # We need to enforce that successive calls to cv.split() yield 
    # the same splits: 
    # see [https://github.com/scikit-learn/scikit-learn/issues/15149](https://github.com/scikit-learn/scikit-learn/issues/15149) **if not** _yields_constant_splits(self._checked_cv_orig): 
        **raise ValueError**(
            "The cv parameter must yield consistent folds across "
            "calls to split(). Set its random_state to an int, or "
            " set shuffle=False."
        )
    --- snip --- 

对于不热衷于遵循规则的开发人员,你听到的第一个理由是“你想太多了。”在某种程度上,他们有一个观点:如果你决定违背上面提到的每一条规则,你的程序不会崩溃。

然而,编程的主要部分是协作。尽管你努力成为一名伟大的程序员,创造出非凡的东西,但你也应该记住,你的大部分工作将作为团队的一部分来完成(在大多数情况下)。因此,考虑如何让别人更容易理解你的代码是有帮助的,其中一个方面就是你如何写你的注释。

你的评论可读性越强,其他开发者(和你未来的自己)就越有可能阅读它们:你的评论只有在被阅读时才有益处。确保你的评论被阅读的一部分是知道如何有效地放置它们。

不同类型的评论

注释是源代码文档的一种额外形式。因此,它们的一般目的是通知其他人[包括您未来的自己]为什么代码中的某些功能是以某种方式实现的,但是有几种不同的方式来部署注释以实现这一目标。

让我们来看看其中的一些方法:

内嵌注释

行内注释出现在一行代码的末尾。使用行内注释有两个主要原因:

#1 如果一个变量已经被定义了,但是使用一个特定对象的原因还不清楚,你可以使用一个行内注释来证明你的决定。

AGE = 18 # The legal drinking age in the UK. 

内联注释的另一个很好的用途是通过为定义的内容提供更多的上下文来减少歧义。

day = 3 # Days in a week range from 0 (Mon) to 6 (Sun)
height = 1.75 # Height is given in meters 

您可能还会看到一些代码库使用注释来指定变量的数据类型。

"""Code taken from my fraud detection model project.
See: [https://github.com/kurtispykes/fraud-detection-project/blob/main/IEEE-CIS%20Fraud%20Detection/packages/fraud_detection_model/fraud_detection_model/train_pipeline.py](https://github.com/kurtispykes/fraud-detection-project/blob/main/IEEE-CIS%20Fraud%20Detection/packages/fraud_detection_model/fraud_detection_model/train_pipeline.py)"""**from** config.core **import** config  # type: ignore
**from** pipeline **import** fraud_detection_pipe  # type: ignore
**from** processing.data_manager **import** load_datasets, save_pipeline  # type: ignore
**from** sklearn.model_selection **import** train_test_split  # type: ignore

我在我的项目中这样做的唯一原因是我使用了一个名为 typechecks 的自动化工具来验证数据类型——我假设这也是它可能出现在其他代码库中的原因。

通常情况下,您不需要指定数据类型,因为从赋值语句中可以明显看出这一点。

解释注释

注释的主要目的是解释为什么一个特定的部分以某种方式被实现。如你所知,做事情有几种方法,所以一个解释能让你更深入地了解程序员的意图。

例如,看看这个评论:

number_of_words *= 0.2 # Multiply the number of words by 0.2\. 

上面的场景是一个无意义评论的完美例子。不需要火箭科学家就能算出你在用0.2乘以number_of_words,所以没有必要在注释中再次声明。

这是一个改进的版本:

number_of_words *= 0.2 # Account for each word valued at $0.20\. 

这个评论提供了对程序员意图的更多洞察:我们现在知道0.2是每个单词的价格。

: 如果你已经阅读了 7 种你应该知道并避免的代码气味 ,你就会知道我们本可以通过给它分配一个常数(即 *PRICE_PER_WORD = 0.2* )来省略这个幻数,从而更好地改进这个代码。

总结评论

有时我们别无选择,只能使用几行代码来实现某些功能。给你的同事(和你未来的自己)一个你的代码正在做什么的概要是非常有益的,因为它允许他们快速浏览你的代码。

"""This is a small piece of functionality extracted from 
the Scikit-learn codebase. See: [https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/impute/_knn.py#L262](https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/impute/_knn.py#L262)"""# Removes columns where the training data is all nan
**if not** np.any(mask):
    **return** X[:, valid_mask]row_missing_idx = np.flatnonzero(mask.any(axis=1))non_missing_fix_X = np.logical_not(mask_fit_X)# Maps from indices from X to indices in dist matrix
dist_idx_map = np.zeros(X.shape[0], dtype=**int**)        dist_idx_map[row_missing_idx] = np.arange(row_missing_idx.shape[0])

总结注释只是对代码中发生的事情的一个高级概述。将它们放在代码库中的不同位置,可以让队友(和你未来的自己)非常容易地快速浏览代码,以便更好地理解——这也表明你知道代码是如何工作的。

法律意见

根据您的工作地点,您可能需要在脚本的顶部包含版权、软件许可和作者信息——这更像是一项内部政策,而不是所有程序员的必需品。

这里有一个来自 Scikit-learn 的[_knn.py](https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/impute/_knn.py#L262)源文件的例子:

# Authors: Ashim Bhattarai <ashimb9@gmail.com>
#          Thomas J Fan <thomasjpfan@gmail.com>
# License: BSD 3 clause

代码标签注释

散布在各种源文件中的简短提醒注释并不少见。开发人员这样做是为了提醒自己,他们还没有抽出时间去做,但打算在未来做的事情。

最常见的标记是 TODO 标记。

"""*This is an example of a code tag comment in the
Scikit-learn codebase. The full code can be found here:* [*https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/metrics/pairwise.py*](https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/metrics/pairwise.py)"""**def** pairwise_distances_argmin_min(
X, Y, *, axis=1, metric="euclidean", metric_kwargs=None
):
    --- snip ---
    **else**:
        # TODO: once PairwiseDistancesArgKmin supports sparse input 
        # matrices and 32 bit, we won't need to fallback to 
        # pairwise_distances_chunked anymore. Turn off check for 
        # finiteness because this is costly and because array
        # shave already been validated.
    --- snip ---

注意上面的例子是如何清楚地描述了要做什么——如果你使用代码标签,这是必不可少的。

这些评论不应该用来代替一些正式的跟踪工具或错误报告工具,因为如果你没有阅读它们所在的部分,很容易忘记它们的存在。

需要注意的是,Python 并没有严格执行所有这些约定。团队可能有他们自己关于注释代码的约定,在这些情况下,最好遵循提供的结构。但是要记住,你的代码是要被阅读的,所以尽可能地让它可读会让这个过程变得更容易,不管这个人是谁。

我错过了什么类型的评论吗?留下评论。

感谢阅读。

联系我:
LinkedIn
Twitter
insta gram

如果你喜欢阅读这样的故事,并希望支持我的写作,可以考虑成为一名灵媒。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。如果你使用我的注册链接,我会收到一小笔佣金。

已经是会员了?订阅在我发布时得到通知。

**https://kurtispykes.medium.com/subscribe **

Python 中的下一件大事:数据类

原文:https://towardsdatascience.com/all-you-need-to-start-coding-with-data-classes-db421bf78a64

全面了解 Python 中的数据类基础知识

照片由 J GowellUnsplash 上拍摄

什么是数据类?

数据类是 Python 版内置的一项新功能。它们提供了装饰器和函数来创建更简单、更方便、更安全的类,这些类主要用于处理数据,因此得名。

数据类的一个主要好处是,它们会自动为你生成几个特殊的方法,比如__init____repr____eq__。当定义主要用于处理数据的类时,这可以为您节省大量时间和冗余代码。

数据类的另一个主要好处是使用了强类型,这可以确保定义实例的属性。这是通过使用类型注释来实现的,类型注释允许您在定义类时指定每个属性的类型。这可以防止由于类型不明确而导致的错误,也使代码更容易被其他开发人员理解。

如何使用数据类

要使用数据类,首先需要从dataclasses模块导入 dataclass 装饰器。这个装饰器是 Python 3.7 和更高版本中固有的。

使用数据类非常简单。只需用@dataclass装饰器装饰你的类定义来定义一个数据类。

下面是一个带有默认参数的简单数据类的示例:

from dataclasses import dataclass, field

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
print(p)  # Output: Point(x=1.0, y=2.0)

我们定义了一个带有两个字段xyPoint数据类,这两个字段都是浮点数。当您创建一个Point类的实例时,您可以指定xy的值作为构造函数的参数。

默认情况下,数据类将为您生成一个__init__方法,该方法将类的字段作为参数。它们还将生成一个返回对象的字符串表示的__repr__方法,这是在上面的例子中调用print(p)时输出的内容。

您可以通过传递额外的参数来自定义由装饰器生成的方法,例如 repr=False 来停用__repr__方法。

场函数

您还可以通过使用field()函数的defaultdefault_factory参数来指定数据类字段的默认值。

例如:

@dataclass
class Point:
    x: float = 0.0
    y: float = field(default=0.0)

p1 = Point(x=1.0, y=2.0)
print(p1)  # Output: Point(x=1.0, y=2.0)

p2 = Point(1.0)
print(p2)  # Output: Point(x=1.0, y=0.0)

在这个例子中,我们定义了一个具有两个字段xyPoint类。x 字段的默认值为 0.0,直接在字段定义中指定,而 y 字段的默认值为 0.0,使用field()函数指定。

当我们创建一个Point类的实例时,我们可以指定xy的值作为构造函数的参数。如果我们没有为y指定一个值,它将使用默认值 0.0。

在数据类内部,指定默认值的两种用法对于不可变的属性是等效的。

然而,field()函数允许更多的灵活性和额外的选项来定义属性。

字段 repr 和 init 参数

事实上,field()函数将默认参数__repr____init__设置为。我们可以将这些设置为来修改它们的行为。

@dataclass
class Point:
    x: float = field(default=0.0, repr=False)
    y: float = field(default=0.0, init=False)

p1 = Point(3.0) 
print(p1) # Output: Point(y=0.0)
p2 = Point(3.0, 2.0) # TypeError: Point.__init__() takes from 1 to 2 positional arguments but 3 were given

在第一个例子中,实例p1没有在其字符串表示中显示x的值,这对于隐藏临时或敏感变量(至少从字符串表示中)是有用的。

在第二个例子中,实例p2不能被创建,因为 init 参数被设置为 False,我们试图初始化y变量。这对于只应由方法返回的变量很有用。让我们向我们的数据类添加一个方法,该方法仅使用y作为函数compute_y_with_x中计算的输出:

@dataclass
class Point:
    x: float = field(default=0.0, repr=False)
    y: float = field(default=0.0, init=False)
    def compute_y_with_x(self):
        self.y = self.x ** 2

p2 = Point(x=2.0)
p2.compute_y_with_x()
print(p2) # Output: Point(y=4.0)

这里,我们注意到p2使用y作为未初始化的变量( init=False ),这是我们的输入变量x转换的结果。我们不关心初始化后的x,所以我们移除了它的字符串表示 (repr=False)。

默认工厂字段

你还记得我们讨论过使用类的默认参数或者用默认参数设置一个字段对不可变的对象有同样的影响,但是对可变的对象没有影响吗?

让我们看一个例子,我们试图初始化一个可变对象的属性,比如一个列表。我们还将创建一个方法,允许我们将元素添加到名为add_a_dimension的列表中:

@dataclass
class Points:
    coord: list = field(default=[])
    def add_a_dimension(self, element):
        self.coord.append(element)
# Output: ValueError

这个类不能被构造,因为如上所述,“不允许对字段使用可变默认值(比如列表)。”这是由数据类添加的安全措施,对于防止我们在常规类中可能遇到的错误非常有用。

事实上,如果我们想使用一个常规类来定义Points数据类,它将完全等同于:

class Points:
    coord = []
    def __init__(self, coord=coord):
        self.coord = coord
    def add_a_dimension(self, element):
        self.coord.append(element)

而且我们在定义这个类的时候不会有任何错误!然而,如果我们使用一个常规类以这种方式定义该类,我们会看到意想不到的行为:

p1 = Points()
p2 = Points()
p1.coord, p2.coord # Output: ([],[])
p1.add_a_dimension(3)

好了,我们实例化了一个空列表,并将 3 添加到实例p1中。你认为现在p1.coordp2.coord的价值会是多少?

p1.coord, p2.coord # Output: ([3], [3])

难以置信,实例p2也受到了列表附加 3 的影响!

这是因为在 Python 中,当你创建一个类的实例时,这个实例将共享类属性的同一个副本。因为列表是可变的,所以p1p2将共享coord列表的相同副本。

为了正确地实现这个类以避免这种意外的结果,数据类为被称为default_factoryfield()提供了参数。

该参数允许您指定一个函数,每次创建一个新的类实例时,都会调用该函数为字段创建一个新的默认值。这确保了类的每个实例都有它自己唯一的字段副本,而不是共享同一个可变对象。

@dataclass
class Points:
    coord: list = field(default_factory=lambda: [])
    def add_a_dimension(self, element):
        self.coord.append(element)
p1 = Points()
p2 = Points()
p1.coord, p2.coord # Output ([], [])

值得注意的是,即使我们将一个可变列表定义为coord的默认值,也没有 ValueError

让我们只在一个实例上调用add_a_dimension方法后检查输出:

p1.add_a_dimension(3)
p1.coord, p2.coord # Output ([3], [])

啊,我们终于有了想要的结果!实例p2未被实例p1调用的方法更改。正是因为每个实例都有其字段coord的唯一副本

正如我们所见,数据类通过正确处理可变和不可变对象来提供安全性。

遗产

要考虑的最后一点是数据类如何处理继承。默认情况下,数据类没有一个__init__方法,所以需要一个允许您覆盖继承属性的方法。这个方法叫做__post_init__,在实例初始化之后调用。

这里有一个例子来帮助说明这个概念:

@dataclass
class Point:
    x: float = field(default=0.0)
    y: float = field(default=0.0)

    def __post_init__(self):
        self.x = self.x ** 2
        self.y = self.y ** 2

@dataclass
class ColoredPoint(Point):
    color: str = field(default='black')

    def __post_init__(self):
        self.color = self.color.upper()

在这个例子中,Point类有一个__post_init__方法,它对xy的值求平方。ColoredPoint类继承自Point,也有自己的__post_init__方法,该方法大写color属性的值。

让我们创建一个Point的实例:

p0 = Point(2.0,2.0)
print(p0) # Output: Point(x=4.0, 4.0)

我们从输出中注意到,调用了__post_init__方法,并且xy的值都是平方的。

现在让我们创建一个ColoredPoint的实例:

p1 = ColoredPoint(2.0, 2.0, 'red')
print(p1) # Output: ColoredPoint(x=2.0, y=2.0, color='RED')

创建ColoredPoint的实例时,调用了__post_init__方法,并且color的值是大写的,但是xy的值不是平方的,知道为什么吗?

这是因为我们ColoredPoint__post_init__方法中没有调用Point__post_init__方法!

要调用基类的__post_init__方法,可以使用super()函数,该函数返回对基类的引用。以下是ColoredPoint的修正版:

@dataclass
class ColoredPoint(Point):
    color: str = field(default='red')

    def __post_init__(self):
        super().__post_init__()
        self.color = self.color.upper()
p2 = ColoredPoint(2.0, 2.0, 'red')
print(p2) # Output: ColoredPoint(x=4.0, y=4.0, color='RED')

随着这一改变,当创建ColoredPoint的实例时,将调用Point类的__post_init__方法,并且xy的值将如预期的那样平方。

我很高兴能够帮助您了解数据类。如您所见,数据类提供了常规类的增强版本,更加安全并强调强类型。我希望您对自己在未来项目中使用数据类的能力更有信心。

无需额外费用,您可以通过我的推荐链接订阅 Medium。

https://medium.com/@arli94/membership

或者你可以在收件箱里收到我所有的帖子。**在这里做

关于 AB 测试你应该知道的

原文:https://towardsdatascience.com/all-you-should-know-about-ab-testing-599724c34773

正确运行 AB 测试的所有步骤。

在快速变化的数据世界中,AB 测试是一种工具,它帮助产品团队测试假设,并做出数据驱动的决策,而不是直觉。

Joyce McCown 在 Unsplash 上的照片

先说 AB 测试的定义。

我们可以测试每一个变化:

  • 登陆页面设计,
  • 行动号召按钮的颜色和位置,
  • 登记表,
  • 邮件营销,
  • 网站广告文字,
  • 等等。

任何 AB 测试的目标都是找到哪种解决方案能够为期望的目标行动提供最佳对话。例如,它可能是——在这种情况下,将有更多的用户关注该链接,在网站或应用程序中注册,订阅时事通讯,并填写反馈表。在更复杂的测试中,我们可以研究长期指标,例如,平均发票的变化或产品变化对利润的影响。

我们为什么需要 AAB 测试?

测试最好用对照样本进行,以免怀疑结果的可靠性。我们将测试选项的受众分成三组(A1、A2 和 B),而不是像往常一样分成两组(A 和 B)。对于 A1 和 A2,我们将展示一个版本,而对于 B,我们将展示另一个版本。这种方法将揭示外部因素是否影响了测试结果,以及在度量标准的收集中是否存在错误。

交通分组方法(图片由作者提供)

示例:
我们想在我们的网站上测试新的新闻稿形式。与此同时,市场部开展了一场大横幅大折扣的销售活动。所以,我们会有很多网站访问者想现在购买,但他们对时事通讯一点也不感兴趣,所以他们会歪曲实验结果。

上面的方法将显示 AB 测试的参与者是否均匀地分布在各组中,以及广告活动是否影响其结果。如果 A1 组和 A2 组的转化率没有差异,我们可以信任数据并相信外部因素不会影响结果。

任何 AB 测试的主要步骤:

AB 测试管道(图片由作者提供)

  • 准备假设

假设将有助于确定实验的目标,选择度量标准,并解释结果。下面是我们可以使用的模式:

假设的模式(作者图片)

举例:

如果 用户注册会出现通迅订阅建议

那么 用户会订阅的更频繁,未来会更投入(比如,有 here 留存率)。

因为 一条关于时事通讯的消息不仅会促使他们订阅,还会促使他们更频繁地回到网站。

  • 定义指标

AB 检验度量是一个用来估计假设是否被证实的指标。在上面的例子中,它们是——订阅率和回报率。

重要的是要考虑实验可能影响的所有指标。否则,选择一个将改进一个度量标准,但同时使产品整体恶化的选项是有风险的。

  • 建立成功标准和后续行动

成功标准是一个预期的结果,可以用来决定测试是否成功。

例如:

我们对新注册用户的订阅有 7%的转化率。我们添加了一个弹出窗口,显示不同订阅的信息,预计订阅的转化率将增加到 12%。12%是成功的标准。

如果带有小部件的版本的订阅量增加,那么假设已经得到证实——实验是成功的,这种变化可以成为所有用户的午餐。

如果指标会下降,那么就需要制定其他解决方案。

如果指标保持在同一水平或变化在统计上不显著,则可能有不同的进一步行动选项,例如:

  • 改变测试条件;
  • 选择另一个功能;
  • 测试不同的观众群。

不同结果的行动计划在实验准备阶段确定。否则,之后,诸如“下一步做什么?我们为什么要做 AB 测试?”

  • 交叉审查

准备大型重要实验时要做交叉评审。同事们会帮助评估所有有争议的观点。他们将检查假设是如何制定的,是否所有可能受实验影响的指标都被考虑在内,以及计划根据结果做出的决策有多正确。同事在复习时提出的每一个多变的问题都可以帮助你在考试中避免错误。

  • 准备实验

在此阶段,将创建一个包含要测试的更改的版本,并定义以下参数:

  1. 对照和实验样品
  2. 最小样本量
  3. 估计结果统计显著性的指标
  4. 测试需要多长时间?

****对照实验样品是将参与测试的用户。您既可以检查所有用户,也可以检查由特定指标(位置、性别、年龄等)联合起来的组。

****统计显著性的指标是对照和实验样本的度量之间的差异,在该差异下,这不太可能是随机结果。

测试持续时间 —实验运行的天数。该指标取决于总样本量和当前流量。公式:每天样本/流量=天数。

  • 运行实验

启动时要做的主要事情是检查 AB 测试是否正确运行。例如,正在测试的一个按钮工作正常,测试的用户是随机选择的。如果在运行之后版本 A 和 B 的转换中出现了很大的差异,或者相反,则根本没有差异—这是有问题的信号。

如果测试工作正常,那么在测试结束前不要试图分析结果,也不要在测试过程中改变测试设置。第一天,赢家可能是一个选项,第二天——又是另一个。你需要等到测试结束才能得到可靠的结果,否则我们可能会面临偷窥的问题。这里有一篇很好的论文解释一下——链接

  • 分析结果并得出结论

我们应该将收到的度量数据与之前设置的成功标准进行比较。然后,我们可以对实验结果做出结论,并参考测试开始前制定的行动计划。

  • 准备一份关于测试结果的报告

与同事分享 AB 测试的结果将有助于他们理解用户行为,即使乍一看,假设与他们的任务无关。

存储报告以便公司的所有员工都可以轻松找到它们是有意义的。这将有助于节省团队的时间和资源,并避免重复的 AB 测试。

H0 假说、p 值和 ab 群

(H0) 是样本 A 和样本 b 的均值之间没有差异的默认假设。因此,在可以证明相反情况之前,零假设为真。

****P 值是 H0 被拒绝的最低显著性水平。

用户需要被随机地分成A 组和 B 组,例如,偶数和奇数用户 id。但最主要的是根据你的标准检查样本的同质性:性别、年龄、地点等。

样本量

在我们处理 A/B 测试的结果之前,我们需要确保测试达到了统计显著性。这是你获得 95%或更高的置信度的点。许多测试工具已经具有内置的统计显著性,所以当测试准备好进行解释时,我们会立即得到通知。还有,网上有很多计算器,比如,计算器 _1计算器 _2 ,或者计算器 _3

还有哪些错误值得害怕?

  • ****历史效应:外界发生的一些事情对你的测试结果产生了负面影响。
  • ****工具效应:测试软件的一个 bug 扭曲了测试结果。

为什么 AB 考不正好 50/50 通过?

有时在 AB 测试期间,您可能会注意到每个变量上的流量并不相同。这并不意味着测试有问题,只是随机样本工作…偶然。通过你网站的流量越多,这个数字就越接近 50/50 的比例。

我如何以及何时可以解释分割测试结果?

测试的早期阶段并不是开始解释结果的最佳时机。我们应该等到测试达到统计显著性再回到原来的假设。当测试最终证实或否定它时,我们就可以开始下结论了。当回顾一个测试时,通过将你的结果与特定的变化联系起来,试着保持公正。确保所做的更改和结果之间有明确的联系,并且没有受到任何第三方(任何外部)力量的影响。

并行 ab 测试

我们可以平行进行两项测试,但样本不能混在一起。否则,一个变化可能会影响第二个变化的感知。

并行 ab 测试示例(图片由作者提供)

通过为不同版本的接口用不同的标签标记事件,可以在前端配置并行测试。你可以使用特殊的工具来简化这个过程,比如谷歌优化。

使用 AB 测试时?

通常,AB 测试失败是因为目标不明确,所以我们不知道我们在测试什么。我们可以用 AB 测试来测试,比如说理论,给新闻页面添加一个图片会增加流量吗?如果您更改标题以强调报价的临时性质,会发生什么情况?上述所有变化都很容易量化。当目标模糊时,人们会在 AB 测试中遇到麻烦:例如,如果我们想比较两个完全不同的设计和不同的选项。虽然这样做是可能的,但是如果没有明确的赢家,那么就有可能搞错或者曲解了点击量增加的原因。

什么可以取代 A/B 测试?

****可用性测试。该方法检查界面的用户友好程度。

研究不需要开发人员参与,就像 A/B 测试一样。我们需要在布局层面创建一个新的界面,构建一个交互原型,观察用户如何与之交互。然后找出可能的问题并找到解决方案。

****假门试验。当开发一个特性比较困难且耗时时,可以用这种方法来检查用户是否需要。

为了做到这一点,在界面上添加了一个按钮,在这个按钮后面没有任何东西——一个假门——并跟踪有百分之多少的用户会按下它。在假门后面通常会放一条信息,说明该部分正在开发中。我们还可以添加一个调查链接,从而为未来的产品收集更多的数据。

向有限的受众发布新产品。如果有足够的时间,我们可以针对一个城市、地区或其他选定的部分用户推出产品,而不是进行测试。当产品是本地的,并且你想测试商业模式的大变化或者尝试一个全新的产品时,这种方法是合适的。

经典与贝叶斯方法

在 frequentist 方法中,概率是事件发生的频率。我们用这种方法来检验统计假设,结果我们会得到一个 p 值。这种计算比较繁琐,我们需要满足许多条件,以便考虑到这种方法的所有特点。

****在贝叶斯方法中,概率是确定的程度(主观概率),在结果中,我们得到每个选项的成功概率。它对初始数据的要求较低,这意味着我们不必检查分布,我们需要更少量的数据,但这样做的代价是贝叶斯计算的复杂性。

最后,无论你选择哪种方法,都要记住 A/B 测试的潜力。这是一种几乎通用的探查受众的方式,任何形式都会给你带来很多有用的数据。

结论

A/B 测试是一个很好的工具,但是它不能帮助我们得到“哪种变体更好?”这个问题的明确答案,只能让你在寻找最优解的路上减少不确定性。在 AB 测试准备的所有阶段,每个细节都很重要,每个不准确之处都会耗费资源,并会对结果的可靠性产生负面影响。

几乎任何图像都只有 8k 矢量

原文:https://towardsdatascience.com/almost-any-image-is-only-8k-vectors-c68c1b1aa6d2

一种图像表示,与生成性任务中的单词非常相似

图一。不仅自动编码器的一些变体家族在图像的重建保真度方面有了显著提高,而且编码器现在还可以压缩输入图像并将其映射到固定的学习向量词汇表。这使得能够使用这种固定的词汇将图像表示为令牌流,用于图像的自我监督学习,就像我们在 NLP 中所做的那样。此外,这种降维表示还减轻了下游模型的负担,以便在没有信息损失的情况下进行比特压缩以及语义特征提取。然而,输入图像的固定词汇映射主要用于生成任务。迄今为止,还没有人尝试用同样的方法来完成区别性任务。如果这被证明是成功的,这样的词汇将有资格作为图像的模拟词。这篇文章研究了两种将输入映射到固定词汇的候选方法,其中一种非常有前途——它将输入图像(左边的鸟)映射到 8192 个学习向量的固定词汇(分布式表示)。这 8192 个向量的组合然后被馈送到解码器,该解码器几乎没有感知损失地重建原始图像(右边的鸟)。左边一只鸟的图像来自的 DIV2k 数据集。这篇文章使用了来自 DIV2K 数据集的图片,并得到了作者的明确许可。谢谢拉杜蒂莫夫特

概述

寻找问题“图像中文字的模拟是什么?”似乎大体上沿着两条路径前进(有一些例外)由多种因素驱动—

  • 正在解决的任务的性质(区别性或生成性)
  • 如何训练模型(自我监督与被监督)
  • 训练过程的性质(例如伯特风格屏蔽输入和预测屏蔽输入)等。

这个问题最近变得引人注目,这在很大程度上是因为 transformers 在区分任务(分类、分割、对象检测)和生成任务的视觉方面取得了成功,在标准基准中即使没有超过卷积架构,也与其相当。特别是,在视觉上模仿像 BERT 这样的自我监督变压器模型的成功的愿望带来了这个问题,因为像素似乎不是单词的模拟物,它们更接近于字符而不是单词。甚至将像素比作字符也感觉有点夸张——一个像素的颜色组合数量使其成为一个非常大的字符集——2 个⁴独特符号(每个像素由 3 种颜色组成,每种颜色有 256 个可能值)

这种搜索的两条主要途径是

第一种方法主要用于解决识别任务,如分类、分割、目标检测等。,而后者用于生成图像。有些方法是两种途径的混合——比如将输入视为输入到转换器中的补丁,并使用模型的固定令牌词汇在输出端进行预测 ( 拜特 )

这两种方法之间的一个关键区别是,在基于面片的方法中,在大多数情况下没有输入的维度减少(有例外,如* 感知者 模型,以及 随机移除编码器 75 %的输入面片的方法,如 MAE ) —从输入图像空间的整个维度学习的负担落在接收这些面片作为输入的变换器模型上,以便解决其辨别任务。尽管事实上输入图像被聚集成碎片(例如,在VIT中,调整大小后的输入图像张量是[3,224,224]并且投影张量是包含 16×16 碎片的[768,14,14]——两者都具有 150,528 的相同展平形状,但情况仍然如此。同 拜特 )。与像素级输入图像相比,补片降低了变形器的二次计算成本,但对于补片和像素而言,扁平形式的输入维度是相同的另一种方式是,转换器必须执行感知和语义压缩,这通常是通过将模型的前几层专用于前几层并将后续层专用于后几层(Swin transformer)完成的,它只对补丁进行操作,在一定程度上减轻了这种情况—在参考部分注释中进行了检查。*

相比之下,第二种方法不仅显著降低了输入图像的维度(在下面详细检查的特定情况下降低了 16 倍),而且更重要的是,还将任何输入图像映射到一组固定的学习词汇向量,并根据该学习词汇重建原始图像,而不会对自然出现的对象造成任何感知损失(文本除外)。有趣的是,下面讨论的模型中的固定词汇学习不是为了表示下游变换器模型的输入,而是为了具有交叉注意力的扩散模型,该模型可以无条件地或有条件地合成关于文本、语义图、图像(图像-图像翻译)等的高分辨率图像。然而,先前已经尝试将输入映射到固定词汇,用于输入到自回归变压器模型。在这种情况下,自动编码器风味在降维空间中学习固定词汇(【dVAE】在 DALL-E ) ,但是具有感知损失(例如像猫毛一样的精细细节的模糊重建)用于输入到图像中使用的自回归变换器模型(连同描述图像的文本令牌)

这篇文章重点介绍了最近的一项工作,即对输入图像进行降维,同时映射到一个已学习的固定词汇集,在重建图像中几乎没有感知损失——由以下未回答的问题引发:

  • 我们是否可以利用这种方法(已经证明对生成性任务很有效),将任何图像映射到一组固定的学习词汇向量,并使用这些向量(令牌)来预训练像 BERT 这样的模型?即通过屏蔽由这个学习词汇(在预训练期间冻结?可能需要位置嵌入?还是冻结向量+学习到的位置编码的组合?)并在输出端预测它们(不像 BEiT 输入补丁并在输出端从固定词汇预测记号;或者像MAE那样,不在固定的学习词汇上而是在输入补丁上进行掩蔽)。**
  • 那么,我们能否微调这样一个模型来解决歧视性任务呢?
  • 可以利用这一点来解决我们在高分辨率大图像方面面临的挑战吗?

一个固定的词汇(用于表示维度减少的图像)在实践中对上述挑战有效,这将使我们比当前最接近单词的词汇(用于辨别任务)* —图像补丁,更接近视觉中的单词模拟。*

图像生成的两阶段方法

以下检查的两种图像生成方法 (DALL-E vs 扩散模型),具有以下共同要素

  • 两者都将图像生成作为两个阶段的过程来执行(这种方法似乎在 2017 年的* *和 2019 年的的改进版本中首先完成了https://arxiv.org/pdf/1711.00937.pdf
  • 第一阶段利用一种自动编码器,将任何图像转换成学习向量的固定词汇表。DALL-E 使用离散 VAE 作为第一级,而扩散模型使用 VQGAN 作为第一级。编码器减小了输入图像的尺寸。然后,这被紧密映射(量化)到一组固定向量的码本,该码本然后被馈送到解码器以重建原始图像。**
  • 对于阶段 1,DALL-E 使用离散 VAE (dVAE) ,其编码器将输入图像[3,256,256]映射到[8192,32,32]。本质上,32×32 个向量中的每一个都是 8192 维的独热向量(在以下与 VQGAN 模型奇偶校验的示例中,编码器将 DALL-E 的 dVAE 的输入图像张量[3,384,384]减少到[8192,48,48])。相比之下,扩散模型的阶段 1 自动编码器是 VQGAN,其将输入图像张量[3,384,384]映射到[3,96,96]向量,这些向量然后被紧密映射到 3 维的 8192 个学习词汇向量。这个码本在本质上几乎像一个颜色空间——编码器将 RGB 空间输入映射到这个缩减的颜色空间——RGB 中的 2 个⁴颜色组合被映射到映射空间中的 8192 (2)个颜色——缩减因子为 2048。这种色彩空间缩减伴随着 16 倍的维度缩减(384x384 → 96x96)。这两种减少的组合将输入空间像素组合降低了几个数量级——2⁸(下图 1a)。此外,与模型的隐藏空间维度的正常大小形成鲜明对比的是,这个降维空间的大小为 3,这是一个低维的分布式表示。虽然两个码本都将输入 2 ⁴色彩空间映射到 8192 个向量的固定词汇表,但是从变换器输入的角度来看,VQGAN 码本优于 dVAE 码本——与由大小为 8192 的相互正交的向量组成的 dVAE 码本相比,VQGAN 表示是大小为 3 的分布式表示。

图 1a。给定一个编码器,其输入具有 4∶1 的维数缩减,伴随有 8192 个向量的学习码本,每 4 个像素的输入像素颜色组合的缩减因子是 2⁸。除了减少变换器的计算开销之外,输入符号空间组合中的 2⁸减少因子可能产生更好的模型性能。它已经证明了在生成任务中的收益。辨别任务的增益有待确定。图片由作者提供。

  • 自动编码器训练的结果产生可以将输入图像映射到固定矢量码本的编码器和可以从矢量码本重建图像的解码器。编码器用于阶段 2 训练,以将图像映射到码本,而解码器用于图像生成。DALL-E 在文本和图像标记上训练自回归模型,用于根据文本生成图像。扩散模型根据文本、语义图或其他图像进行图像生成(图像-图像转换任务,如修补)

比较 dVAE 和 VQGAN

dVAE 和 VQGAN 的以下特征比较如下

  • 图像重建质量(阶段 1 输出)
  • 图像生成质量(阶段 2 输出)
  • Vocab 载体

图像重建质量

下图是比较 DALL-E 的 dVAE 和扩散模型的 VQGAN 的重建质量的示例。第一幅图像是原始图像,随后是 dVAE (中)和 VQGAN (右)的重建图像。

图 2a。左边的图像是来自的 DIV2k 数据集的原始图像。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

图 2b。左边的图像是来自 DIV2k 数据集的原始图像。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

图 2c。左边的图像是来自DIV2k 数据集的原始图像。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

图 2d。左边的图像是来自 DIV2k 数据集的原始图像。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

图 2e。左边的图像是来自DIV2k 数据集的原始图像。中间是 dVAE 重建,右边是 VQGAN 重建。请注意原始图像缩放到 384x384(由作者生成)时面部的变形

图 2f。左边的图像是来自DIV2k 数据集的原始图像。中间是 dVAE 重建,右边是 VQGAN 重建。当输入未被缩放时,重构接近原始重构。该图像是图 2e 的裁剪后的 384×384 的区域,没有缩放。(由作者生成)

图 2g。左图为作者原图。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

图 2h。左图为作者原图。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

图 2i。左图为作者原图。中间是 dVAE 重建,右边是 VQGAN 重建(作者生成)

从上面的图像中可以观察到以下情况

  • VQGAN 重建比 dVAE 重建更接近原始图像
  • 这两种模型都与文本重建(图 2h-2i) 有矛盾,特别是对于某些字体。然而,VQGAN 重建比 dVAE 更接近原始图像。
  • 缩放图像会导致重建失真,即使在 VQGAN 中也是如此,这可以从面部特征(图 2e) 中看出。然而,没有缩放的原始图像在重建时几乎没有感知损失(图 2f)。

图像生成质量

下图是比较 DALL-E 的 dVAE 和扩散模型的 VQGAN 的生成质量的示例。第一幅图(图 3a)是使用 DALL-E (利用阶段 1 dVAE) 生成的图像,带有文本提示输入。随后的都是由扩散模型(利用阶段 1 VQGAN) 无条件地(图 3b) 或者有条件地利用布局输入(图 3c) 、图像输入(修补-图 3d-f) 和文本输入(图 3g) 生成的图像。

图 3a。图像来自 DALL-E 纸零拍摄文本到图像生成。

图 3b。扩散模型的无条件图像采样。图片由作者生成

图 3c。从扩散模型到图像合成的条件图像生成。来自扩散模型纸的图像

图 3d。基于扩散模型的条件图像生成。修补。作者生成的图像****

图 3e。基于扩散模型的条件图像生成。修补。作者生成的图像****

图 3f。基于扩散模型的条件图像生成。修补。作者生成的图像****

图 3g。扩散模型纸的图像用户自定义提示的文本到图像合成。这些图像没有我们在重建图像中发现的同样的细节水平。例如面部的细节。然而,无条件的人脸图像生成具有非常精细的细节,如上面图 3b 所示。

很少定性观察

  • VQGAN 的图像生成质量总体上似乎优于 DALL-E 的 dVAE,尤其是精细细节的生成。生成的图像质量与我们在重建中看到的几乎相同。
  • 然而,VQGAN 的文本到图像生成的生成质量错过了面部的细节。不清楚这是否是该任务/培训集等的结果。从布局描述、修补和 VQGAN 的无条件图像生成中生成的图像看起来更逼真。
  • 这种定性评估表明,仅利用 VQGAN 的潜在空间码本向量的阶段 2 生成模型在照片级真实感质量方面几乎与使用阶段 1 解码器直接重建的图像一样好(文本到图像任务除外)。然而,直接作用于像素的扩散模型的生成能力甚至已经优于 GANS (更不用说 DALL-E 中用于生成图像的自回归模型的生成能力)。因此,有人可能会认为这可能与码本或潜在空间无关——只是已经在像素空间中建立的扩散模型的生成能力。有趣的是,该论文不仅报告了重建质量的改进 (FID 分数 1.6x) ,而且还报告了计算时间的加速(2.7x),这是通过扩散模型作用于潜在空间而不是像素空间实现的。进一步支持前面的陈述,利用离散码本的潜在空间的扩散模型的照片真实感的定性检查似乎比直接作用于像素的扩散模型更好。
  • 作为题外话,值得注意的是 DALL-E 的 dVAE 在 BEiT 的输出端用于令牌预测。然而,该模型的输入将图像视为补丁,而不是码本向量。尚不清楚为什么做出这一特定决定——是因为 dVAE 的重建质量,还是因为 dVAE 码本矢量的正交特性不适合作为输入表示?

词汇向量

dVAE 将任何输入图像映射到 8192 个正交向量(一键向量),而 VQGAN 将任何输入图像映射到 8192 个学习到的线性无关但非正交向量。具体来说,如前所述,任何输入图像张量[3,384,384]在 dVAE 中映射到[8192,48,48]张量,在 VQGAN 中映射到[3,384,384]到[3,96,96],然后用作解码器的输入张量。**

我们可以通过简单地向解码器提供与解码器输入的形状相匹配的张量中复制的 vocab 向量中的一个来可视化两个模型的固定词汇向量。下图显示了 dVAE 词汇向量(本质上是一个热点向量)和 VQGAN 词汇向量的可视化。

图 4a。构成 DALLE 的 dVAE 离散码本的 8192 个向量的可视化。8192 个值中的每一个的解码器输出图像在上面形成一个正方形小块。8192 解码器输出排列在宽度为 96 的网格中。最后一行仅部分填充了 8192%96 颜色值。如图 4c(顶行)所示,除了一小部分沿着边界具有网格状线条之外,颜色基本上是单调的。即使颜色看起来是多余的,它们也是不同的。作者图片****

图 4b。构成扩散模型中使用的 VQGAN 码本的 8192 三维向量的可视化。8192 个值中的每一个的解码器输出图像在上面形成一个正方形小块。8192 解码器输出排列在宽度为 96 的网格中。最后一行仅部分填充了 8192%96 颜色值。如图 4c(顶行)所示,除了一小部分沿着边界具有非常柔和的网格状线条之外,颜色基本上是单调的。另一个值得注意的区别是灰色代码占主导地位,这往往是非常低的幅度向量,在图像锐度中起作用,在下面的一节中讨论。即使颜色看起来是多余的,它们也是不同的。作者图片****

图 4c。两种自动编码器共有的两种风格的码本可视化的缩放再现。单调颜色主导两种码本可视化,与 VQGAN 中较柔和的网格线(边界行)相比,dVAE 具有更独特的网格线(顶行)。除了这些网格线,从色彩映射的角度来看,两个自动编码器都将色彩空间从 2 ⁴减少到 8192 距离值。作者图片****

关于可视化的一些定性观察

  • 除了在两个码本可视化中的少数代码中存在网格线(它们所起的作用尚不清楚),从色彩空间映射的角度来看,两个自动编码器的编码器在两个自动编码器中执行从 2 种⁴色彩组合到 8192 种色彩的色彩空间映射。与几何特征提取器相比,它们似乎都主要是色彩空间压缩器(网格线在少数代码中的作用未知)
  • 这些模型在映射颜色空间中的颜色表示上有所不同——dVAE 表示是一种稀疏表示 (one-hot) ,比 VQGAN 的密集分布式表示大 682 倍。此外,VQGAN 码本矢量的很大一部分(~66%)是非常低的幅度矢量,几乎接近于零。它们确实扮演着重要的角色(下文将会讨论)尽管其机制尚不清楚。

VQGAN 码本的显著特性

  • 与 dVAE 正交码本向量不同,当用欧几里德距离和余弦距离度量检查时,VQGAN 的向量揭示了不同的属性。
  • 每个“非灰色”码本向量的欧几里德距离邻域(图 5a、5c、5e) ,当被可视化时,示出了由相似颜色支配的顶部邻域,不同颜色被主要由接近零的幅度向量组成的灰色带分开。此外,每个码本向量的欧几里德距离邻域属于两组 (5a,5c,5e 对 5g) 中的一组,这两组在视觉上和在捕获欧几里德距离分布的直方图(5i)中都是明显的——这仅仅是由于接近零的幅度向量的邻域和其他的差异。
  • 余弦距离向量邻域,当被可视化时,具有与灰色混合的颜色——前面提到的接近零的幅度向量。这种视觉模式非常类似于任何输入图像的码本向量的可视化(图 6a 和 6b) 。此外,余弦距离分布的直方图(5i)示出了邻居几乎均匀地分布在余弦值的整个范围内,与可视化一致。更重要的是,接近零的幅度向量的邻域几乎均匀地分布在余弦值的整个范围内。人们很容易将这些接近零的向量和文本中的空格字符/分隔符的作用相提并论,尽管这种表面上的比较并没有揭示这些向量真正在做什么,除了边缘化它们可能扮演的更深层次的角色。将这些近零向量与 JPEG 压缩中通过量化 DCT 系数实现的稀疏矩阵进行比较也很有吸引力(这对压缩有很大贡献),但这是错误的,原因如下——即使近零幅度,它们也起作用。
  • 例如,仅用一个近零幅度矢量替换所有近零幅度矢量会使整个图像模糊(图 7) 。这提供了这些接近零的向量的重要性的间接证据,尽管如上所述,还不清楚它们实际上是做什么的。
  • 码本向量似乎具有局部和全局影响(图 8a 和 8b) 。如果我们用不同颜色的几个代码替换输入到解码器的码本中的几个代码,这表现为如图 8a 所示的软补丁。在图 8b 中,代码的全局影响是明显的。这是一个黑色输入图像的解码器输出的可视化。黑色在码本空间中表示,在左边和上边有一个彩色带,图像的其余部分为灰色。它们似乎对整个图像的颜色有全局影响。此外,假设一个代码无论在解码器输入矩阵中的哪个位置都是相同的,那么该代码在输入矩阵中的位置可能隐含地作为解码器的位置信息。
  • 从重构的角度来看,码本向量形成退化码。由不同码本向量组合组成的两个不同张量在解码时产生相同的输出。当用作下游任务的字模拟时,下游模型将不得不学习不同的代码是等效的,就像解码器通过将其映射到相同的输出图像(图 8b) 所做的那样。
  • 将码本图像编码大小与同一图像的不同级别的 JPEG 压缩进行比较,显示码本编码的图像大小(对于所有图像都是常数~ 16KB-2 * 8192 字节)小于最低质量的 JPEG 版本(21KB),其中感知损失显著(图 9a 和 9b)

图 5a。VQGAN 码本向量的欧几里德距离邻域的可视化。紧邻的邻域显示相似的颜色,随后是构成大约 66%的码本向量的灰色。其余的颜色跟随在这条灰色带之后。作者图片

图 5b。图 5a 中相同码本向量的余弦距离邻域的可视化。注意,这个邻域模仿了图 5a 中的邻域颜色排序,除了灰色被分散,而不是被限制在单个带中。作者图片

图 5c。另一个码本向量的欧几里德距离邻域的可视化。紧邻的邻域显示相似的颜色,随后是构成大约 66%的码本向量的灰色。其余的颜色跟随在这条灰色带之后。作者图片

图 5d。图 5c 中相同码本向量的余弦距离邻域的可视化。注意,这个邻域模仿了图 5b 中的邻域颜色排序,除了灰色被分散,而不是被限制在单个带中。作者图片

图 5e。另一个码本向量的欧几里德距离邻域的可视化。紧邻的邻域显示相似的颜色,随后是构成大约 66%的码本向量的灰色。其余的颜色跟随在这条灰色带之后。作者图片

图 5f。图 5e 中相同码本向量的余弦距离邻域的可视化。注意,这个邻域模仿了图 5b 中的邻域颜色排序,除了灰色被分散,而不是被限制在单个带中。作者图片

图 5g。灰色码本向量的欧几里德距离邻域的可视化。所有颜色都遵循这条灰色带。作者图片

图 5h。与上面检查的所有向量一致,图 5g 中相同向量的余弦距离邻域到处都是灰色。作者图片

图 5i。这些图表总结了码本向量的特征。灰色的欧几里德距离显示了 0 附近的一个大尖峰,随后是所有的颜色。余弦距离的邻域几乎是均匀的。非灰色向量的欧几里得邻域显示了将颜色分成两部分的大量灰色。余弦距离也模拟了这一点。作者图片

图 6a。上面显示的余弦距离可视化的一个奇怪属性是,它非常类似于图像输入的码本可视化。这是图 1 所示的鸟的码本可视化。作者图片

图 6b。余弦距离可视化的奇怪属性的另一个例子。这是松鼠的码本可视化,如下图 9a 所示。作者图片

图 7。用单个格雷码替换所有的格雷码使得整个重建图像模糊(右图)。左边的图像是原始的,中间是编码器输出映射到码本向量的重构。这一替换突出了这些近震级矢量的重要性,尽管它们的确切作用尚不清楚。DIV2k 数据集左边的鸟的图像。作者图片

图 8a。如果我们用不同颜色的几个代码替换输入到解码器的码本中的几个代码,这表现为如上所示的软补丁。这说明了代码的本地化影响。作者图片

图 8b。该图说明了一项准则的全球影响。这是一个黑色输入图像的解码器输出的可视化。黑色在码本空间中表示,在左边和上边有一个彩色带。它们似乎对整个图像的颜色有全局影响。另一个事实是,多个码本矢量组合在输出空间中产生相同的颜色。输出空间中的黑色可以用这个代码或仅仅一个代码来实现(如图 4b 中的平铺图像所示的产生黑色的代码之一)。作者图片

图 8c。这个图是代码的局部和全局影响的另一个例子。名称和光标在码本空间中由非灰色表示,这是码的局部影响。白色背景似乎被左侧和顶部的带所捕获——代码的全局影响。

图 9a。左边的原始图像来自 DIV2k 数据集。转换/重建图像由作者提供。质量度量是在 JPEG 中以不同压缩率保存 JPEG 文件时使用的度量。顺便说一句, 扩散模型正在作为一种可能的手段被检查 以减少在被高度压缩(低质量因子)的 JPEG 图像的解压缩期间的可见伪像。

图 9b。由作者创建/生成的图像

最后的想法

给定一组固定的学习向量不仅可以用于表示任何具有非常小的感知损失的图像,而且可以生成照片般逼真的图像,有证据表明,码本包含足够的信息来忠实地表示来自图像底层分布的任何样本(文本除外)。这意味着码本向量可以用作图像上任何下游辨别任务的单词模拟,特别是当重建质量随着更新的码本学习设计而提高时。

最后,关于 VQGAN 和扩散模型的其他想法,与本文的主题无关…

VQGAN autoencoder 通过零特征工程实现的压缩可以超过 JPEG 等压缩方案,这至少是违反直觉的,如果不是引人注目的话——可以说是人类在特征工程方面独创性的展示范例。特别是,导致 VQGAN 压缩方案的某些可观察属性(在上一节中检查)的底层机制不清楚,除了它们正在执行的模糊结论之外,在精神上** ,JPEG 的所有关键“特征工程”思想**

  • 通过 CbCr 颜色下采样和子采样在 YCbCr 空间中实现的 jpeg 中的颜色空间缩减(在 JPEG 压缩中约 50%的贡献)——***vqgan 中从 2 ⁴到 8192(2)颜色的颜色空间映射似乎实现了这一点。***
  • JPEG 中剩余的约 40-45%的压缩是通过将输入表示为高频和低频分量的组合(由离散余弦变换的频率系数表示)并使用精心选择的量化表来丢弃这些系数的大部分(与低频分量相比,我们的眼睛对高频分量不太敏感,因此 JPEG 丢弃这些高频分量而没有感知损失)从而导致压缩。
  • 最后,利用霍夫曼编码/游程编码,通过由 VQGAN 编码器的量化— 降维产生的稀疏矩阵的对角遍历来实现 JPEG 中的无损压缩,这似乎实现了上述 40–45%压缩的效果,以及该步骤的压缩。

扩散模型不仅在生成能力上取代了 GANs,而且还被视为一种可能的手段,以减少高度压缩的 JPEG 图像解压缩过程中的可见伪像(低质量因子≤10)——这是一个长期存在的图像增强问题。尽管事实上 GAN 在生成能力上已经被像素空间中的扩散模型所取代,但它是一种“GAN 风味”,将输入压缩到扩散模型的潜在空间以供消耗,这进一步增强了扩散模型生成中的照片真实感,正如我们在上面看到的。特别是作者声称,GANs 的对抗性损失有助于学习产生现实重建的表示(通过选择一个可能的重建)超越重建损失最小化(LPIPS),没有这些重建将是模糊的(平均可能的重建)。

最后,这个想法(来自两所德国大学)——结合 VQGAN 和扩散模型来同时增强和利用两者的优点(离散密集表示码本, 照片级真实感图像)虽然通过组合它们克服了一个关键的限制(直接在像素空间上工作的扩散模型的长训练时间)似乎很难得到应有的关注 ( 无论是在引文 还是社交媒体 中尽管他们之前的工作非常有名,并用于创建抽象图像形式以及剪辑——VQGAN+剪辑组合 ) 相比之下,一些受欢迎的同行甚至无法接近这部作品的写实主义【6】【15】。 它可能只是相对较新,最终将随着时间的推移(它已被接受为【CVPR】22)

链接到 Github 上的码本比较

参考资料和附加注释

  1. 使用残差量化的自回归图像生成,2022 年 3 月 本月早些时候发表的一篇论文提出将图像表示为离散码的堆叠图。
  2. 使用潜在扩散模型的高分辨率图像合成,2021 年 12 月 在上面的帖子中检查了使用 VQGAN 的论文。最近大量的论文倾向于使用离散码本,使用 VQVAE/VQGAN 变体作为第一阶段,使用扩散模型作为第二阶段,以根据文本、图像、布局元素等生成图像。扩散模型不是直接从图像开始并递增地添加噪声( 【滑动】 ),而是从离散码本表示开始并递增地添加噪声。在反向去噪路径中——从噪声开始(可能用条件文本/图像/布局等引导,或者仅用这些条件输入的级联表示引导),模型输出去噪的离散表示,然后传递给解码器以重建生成的图像。作者声称两阶段方法是有利的,原因有几个——(1)高频的不易察觉的细节被抽象掉了,(2)该模型可以专注于数据中的重要语义信息,以及(3)在低维空间中的训练在计算上更有效。此外,这项工作的一个有趣的方面是在第一阶段使用 GAN 来生成码书,并使用扩散模型(与 GAN 相反)来生成真实感图像。这是最近一个趋势的结果,扩散模型在生成真实感图像方面已经超过了 GANs。
  3. ImageBART:用于自回归图像合成的具有多项式扩散的双向上下文,2021 年 8 月 本文使用与下文提到的驯服变压器论文中提到的相同的自动编码器设计。
  4. 屏蔽自动编码器是可扩展视觉学习器,2021 年 12 月 它使用屏蔽自动编码器,其中编码器仅对构成输入的面片子集进行操作,而解码器根据编码器的潜在表示输出以及在去除面片的地方之间引入的屏蔽标记来预测屏蔽面片。解码器的目标是减少像素级的重建损失。这种工作在精神上非常接近于固定的学习词汇所实现的——通过随机移除输入补片来降低像素空间中的冗余,编码器被迫专注于在其输出中捕捉图像的重要方面( 【理想地语义 ),以帮助解码器在重建整个图像时预测在像素级别丢失的补片是什么。虽然这种减少输入(75%)并迫使编码器学习有助于解码器重建整个图像的表示的方法可以赋予编码器(在该预训练任务之后仅使用编码器)学习丰富表示的能力,以用于随后对下游辨别任务的微调——重建图像 的模糊 性质不仅表明学习的表示对生成任务不起作用, 但是,学习到的表示也不像本帖中讨论的阶段 1 VQGANs 的学习到的表示那样完整,它可以用来重新生成原始图像而没有感知损失。 这表明,至少在原则上,一个在第一阶段对辨别任务的输出进行微调的模型应该表现得更好——然而,这需要得到验证。在这一点在工作模型中得到验证之前,人们可能会认为,区别性任务与生成性任务不同,可能不一定需要能够重建图像而没有知觉损失的表征。
  5. 关于《视觉变形金刚》大家应该知道的三件事,2022 年 3 月最近的这篇论文基于视觉变压器的易于实施的变体提供了见解。其中之一是增加了一个基于 MLP 的补丁预处理,以改善伯特风格的自监督学习。然而,即使进行了变换/缩小,该模型仍然在像素级上运行。
  6. GLIDE:使用文本引导扩散模型实现照片级真实感图像生成和编辑,2022 年 3 月 这项工作不采用两阶段方法来生成图像。相反,训练扩散模型以逐步向图像添加噪声,然后去噪以在像素级重新生成图像,从反向步骤以噪声开始的地方开始。去噪过程可选地由来自单独分类器的标签的文本或梯度来指导。还尝试了另一种对文本进行调节的方法,称为 【分类器自由引导】 ,其中文本/标签在 20%的时间内被替换为空文本/标签,并使模型在相反的过程中预测下一个时间步长,使用该时间步长的具有文本/标签的预测来推动没有文本/标签的预测。
  7. Swin Transformer:Hierarchical Vision Transformer using shift Windows,2021 年 8 月 本文也没有采用两阶段的方法,而是提供了一种创新的方法来学习仅仅来自补丁的表示,这种方法比普通的 Transformer 更有效,并且可以捕获更高分辨率的信息。它以分层的方式训练视觉变换器,其中以小的块尺寸和较小的注意跨度开始,一层的输出的相邻向量被合并,然后下一层具有具有较大注意跨度的移位窗口。这种方法在处理补丁时解决了普通变形器中的至少两个问题:( 1)低分辨率(16x16)补丁——相比之下,Swin 变形器从更小的补丁大小 4x4 开始,以及(2)导致二次复杂度的全局关注——相比之下,Swin 变形器在非重叠的局部窗口上进行自关注(但也允许具有移位窗口的跨层的跨窗口连接),因此计算效率高。实际上,Swin transformer 完成了“卷积+最大池”组合所完成的工作。
  8. 驯服用于高分辨率图像合成的变压器,2021 年 6 月 本文介绍了使用卷积 VQGANs 作为生成离散码本的手段。本文中的几项创新,其中关键的是学习码本,该码本在没有感知损失的情况下重建图像,这部分是由于 GAN 的对抗损失而成为可能,该 GAN 迫使模型输出可能的重建候选之一,而不是对由 LPIPS 损失驱动的可能输出候选进行平均。以前有过学习离散码本的尝试,如下面的参考文献 16 和 17。这个看起来是第一个使用 VQGAN 的。
  9. 感知者:迭代注意的一般感知,Jun 2021 本文介绍了一种利用交叉注意机制降低输入图像维数的方法。这种方法用于不同的模态。
  10. BEIT:2021 年 6 月影像变形金刚 BERT 预培训如前所述,这使用了一种将图像输入视为补丁的混合方法,但使模型预测由 DALLE 的 dVAE 学习的输入图像的离散表征。
  11. 一张图片抵得上 16X16 的文字,Jun 2021这是通过输入图像作为补丁来使用图像的变形金刚的原纸。
  12. LeViT:一个穿着 ConvNet 服装的视觉转换器,用于更快的推理,2021 年 5 月 本文提出了一种计算高效的方法来处理第一层中的补丁。
  13. ConViT:利用软卷积电感偏置改善视觉变压器,2021 年 6 月 本文提出了一种提高视觉变压器采样效率的方法
  14. 训练数据高效的图像转换器&通过注意力提取,2021 这表示输入为小块。该架构不使用卷积。
  15. 零镜头文本到图像生成,2021 年 1 月本文介绍了 DALL-E,它使用了本文中讨论的离散 VAE。
  16. 用 VQ-VAE-2 生成多样的高保真图像,2019 。这是对 17 版 VQVAE 的改进。
  17. 神经离散表示学习,2018 这篇论文似乎是第一个介绍图像生成的两阶段方法——第一阶段是一种自动编码器 VQVAE 的味道。
  18. 链接到参考本文中引用的高质量图像数据集的论文。
  19. 制作场景:具有人类先验的基于场景的文本到图像生成最近的另一项工作使用 VQGAN 来学习离散码本。
  20. 扩散模型在图像合成上击败 GANs,Jun 2021 本文论证了扩散模型在无条件和类条件图像生成上可以超越 GANs。它还检查了对用于扩散模型的原始 UNet 模型 的架构改进。
  21. Palette:Image-to-Image Diffusion Models,November 2021这篇论文认为,在非常低的质量因子值(非常高的压缩率)下使用 JPEG 压缩引入的伪像的移除(在解压缩期间)是其图像到图像翻译任务之一。
  22. 去噪扩散概率模型,2020。 这项工作为扩散概率模型创造了短语“扩散模型”。还展示了使用这些模型的高质量图像合成。
  23. 利用非平衡热力学的深度无监督学习,2015 引入扩散模型(最初称为扩散概率模型)的原始论文
  24. 用变形金刚进行高分辨率复杂场景合成,2021 年 5 月VQGAN 用于学习离散码本,该离散码本然后被用作自回归变换器的输入,该自回归变换器学习根据在训练期间与图像码本表示一起馈送的布局描述来预测潜在空间表征。
  25. 用改进的 VQGAN 进行矢量量化图像建模,2021 年 10 月。 本文采用两阶段方法。第一阶段使用基于视觉转换器的 VQGAN 进行离散码本学习。第二种状态是自回归变换器,其输入由阶段 1 编码表示。该模型用于图像生成任务和表征学习。
  26. 扩散自动编码器:走向有意义和可解码的表示。马克 2022。最近的工作描述了一种使用扩散自动编码器学习语义表示的方法。代码尚未发布。

每个人的 AlphaFold

原文:https://towardsdatascience.com/alphafold-for-everybody-c73d40d4b56d

丹尼尔·利维斯·佩鲁西在 Unsplash 上的照片

每个人的 AlphaFold

一个新的谷歌云博客展示了如何使用 Vertex AI 来运行 DeepMind 开创性的 Alphafold 蛋白质结构预测系统

2021 年初我花了几个月的时间面试数据科学的候选人。在每次面试中,我都要求候选人说出 2020 年以来两项主要的人工智能成就。这是我一直在寻找的两个标题:

  1. GPT-3——由 OpenAI 于 2020 年 6 月限量公测发布

2.alpha fold—由 DeepMind 于 2020 年 12 月公布[ 正式论文

随着时间的推移,我开始觉得问这个问题有点像一个骗局。我很乐意询问 GPT-3,因为我已经用它做了一些实验,包括生成 git 命令在伦敦地铁导航,以及回答电影琐事问题。然而,当谈到 Alphafold 时,我真的不知道我在说什么。我没有任何生命科学背景来帮助我理解为什么 AlphaFold 很重要,我对 AlphaFold 也没有任何实践经验。

通过 Vertex AI 实际操作 AlphaFold

自从这些面试以来,我一直想弥补我在 AlphaFold 方面缺乏实践经验的不足,所以我很高兴地得知,我的一位同事为 Shweta Maniar 的谷歌云博客帖子做出了贡献,该帖子描述了一种使用 Vertex AI 运行 AlphaFold 的简单方法。

在这篇文章中,我描述了我在谷歌云的顶点人工智能环境中,在笔记本电脑上建立简化版 AlphaFold 的经历。我将描述我是如何进行设置的,并提供一些资源进行进一步的探索。

请注意:

  • 我是谷歌员工,但本文内容为本人观点,不代表谷歌官方立场。
  • 我不是生命科学家,所以请原谅任何有问题的术语。

呈现你的第一个三维蛋白质结构预测

博客清晰地解释了端到端的过程,从开始使用谷歌云到运行笔记本获得蛋白质结构预测。预算至少一个小时来设置环境并完成一次运行。一旦你遵循了博客中的说明,你将在 Vertex AI 中拥有一个笔记本,它使用 AlphaFold 的简化版本来预测与氨基酸序列对应的三维蛋白质结构。

开箱即用,当您运行笔记本电脑时,它会根据以下氨基酸序列进行预测:

MAAHKGAEHHHKAAEHHEQAAKHHHAAAEHHEKGEHEQAAHHADTAYAHHKHAEEHAAQAAKHDAEHHAPKPH

笔记本完成后,它会向您展示 AlphaFold 对与输入氨基酸序列相对应的三维蛋白质结构的预测:

AlphaFold 对笔记本中初始氨基酸序列的蛋白质结构预测

渲染中的颜色表明了模特的自信:

使用“金发女孩”示例练习 AlphaFold

为了更好地了解该设置可以做什么,我决定看看我是否可以从笔记本中获得与成熟的 AlphaFold 提供的预测相匹配的预测。也就是说,给定一个氨基酸序列和 AlphaFold 为该序列预测的三维蛋白质结构,我会从 Vertex AI 笔记本使用的 AlphaFold 的简化版本中获得匹配的蛋白质结构吗?

现在,如果你的科学更多的是在“数据”方面,而不是“生活”方面,你可能想知道如何做这样的实验。好消息是网上有令人难以置信的、可获得的资源,特别是 www.uniprot.org 的。用它自己的话说:

通用蛋白质资源(UniProt)是蛋白质序列和注释数据的综合资源

我们可以使用www.uniprot.org找到一个氨基酸/蛋白质结构组合,我们可以用它作为我们的基线。

对于我们的基线,我们想要一个“金发女孩”的例子:

  • 不太简单 —笔记本要求输入字符串中至少有 16 个氨基酸,所以这个例子不会起作用,因为它只有 13 个氨基酸:
GACLGKACGRKRK
  • 不太复杂 —我们想获得一个“眼球”感,看看我们笔记本的预测与 UniProt 中的 AlphaFold 预测有多接近。这意味着我们不想要一个如此复杂的结构,以至于非生命科学家都无法理解。例如这只,它的氨基酸序列中有 750 个条目,其三维结构如下,太复杂了,不能作为一个好的基线:

对于非专业人士来说太复杂了(【https://www.uniprot.org/uniprot/P58215】T4

  • 恰到好处 : 这个例子有一个合理的氨基酸序列,长到可以在笔记本上工作:
MASPLRSLLFLLAVLAVAWAATPKQGPRMLGAPEEADANEEGVRRALDFAVSEYNKGSND
AYHSRAIQVVRARKQLVAGVNYFLDVEMGRTTCTKSQTNLTDCPFHDQPHLMRKALCSFQ
IYSVPWKGTHSLTKFSCKNA 

ProtInt 条目中的 3d 蛋白质结构的呈现非常简单,应该可以直观地将其与笔记本创建的预测进行比较:

金发女孩的例子(https://www.uniprot.org/uniprot/P21460)

要运行这个实验,我们所要做的就是将笔记本中 sequence_1 变量的值更新为本例的氨基酸序列。要获得该序列,在 UniProt 条目中,向下滚动并点击 FASTA 按钮:

该序列将出现在一个新的选项卡中:

MASPLRSLLFLLAVLAVAWAATPKQGPRMLGAPEEADANEEGVRRALDFAVSEYNKGSND
AYHSRAIQVVRARKQLVAGVNYFLDVEMGRTTCTKSQTNLTDCPFHDQPHLMRKALCSFQ
IYSVPWKGTHSLTKFSCKNA

只需删除换行符,您就有了需要分配给笔记本中 sequence_1 的字符串:

*# Input sequences (type: str)*
sequence_1 **= "**MASPLRSLLFLLAVLAVAWAATPKQGPRMLGAPEEADANEEGVRRALDFAVSEYNKGSNDAYHSRAIQVVRARKQLVAGVNYFLDVEMGRTTCTKSQTNLTDCPFHDQPHLMRKALCSFQIYSVPWKGTHSLTKFSCKNA"

使用 sequence_1 的新值重新运行笔记本,您将从 AlphaFold 的简化版本中获得蛋白质结构预测:

笔记本的蛋白质结构预测为来自https://www.uniprot.org/uniprot/P2146的氨基酸序列

将 ProtInt 的蛋白质结构与笔记本生成的结构并排比较,在非专业人士看来,它们非常相似:

蛋白质结构预测的并排比较

关于使用 UniProt 的一些附加提示

作为一名非专业人士,我面临的一个挑战是确定 3d 效果图的方向,这样我就可以对它们进行比较。UniProt 通过包含一个小部件来帮助您,该小部件可以在您旋转三维结构时显示 x-y-z 轴的位置,如左下方突出显示的:

显示蛋白质结构渲染中 x-y-z 轴方向的小部件

当您旋转图像时,小部件会向您显示轴的位置。

如果您想在 ProtInt 中显示的蛋白质结构和笔记本中的预测之间进行更多的比较,该怎么办?ProtInt 中的许多条目不包含蛋白质结构,那么如何找到包含它们的例子呢?

以下是在 ProtInt 中搜索“弹性蛋白”的结果,按顺序显示了长度最短的条目。具有蛋白质结构渲染的示例在第三列中有一个金色文档,如下所示:

蛋白质搜索结果显示一个具有蛋白质结构渲染的条目

下面是高亮条目的蛋白质结构渲染图:

结论

通过关注这个谷歌云博客,即使是非专业人士也可以很容易地利用 Vertex AI 笔记本来练习简化版的 AlphaFold。使用这种装置,你可以得到氨基酸序列,并预测它们的蛋白质结构。使用 UniProt ,你可以得到更多的尝试序列的想法。由于这种设置,我们真的为每个人提供了 AlphaFold。

下面是本文描述的实验的视频摘要:【https://youtu.be/y90-mZGL6AQ

引文

****uni prot:uni prot 联盟
uni prot:2021 年通用蛋白质知识库
核酸研究 49:D1 (2021)

AlphaFold : 利用深度学习进行高精度蛋白质结构预测

约翰·跳线、理查德·伊文思、亚历山大·普里策尔、蒂姆·格林、迈克尔·菲格诺夫、凯瑟琳·图亚苏武纳库尔、奥拉夫·龙内贝格、拉斯·贝茨、奥古斯丁·伊迪克、亚历克斯·布里德格兰、克莱门斯·迈耶、西蒙·科尔、安娜·波塔彭科、安德鲁·巴拉德、安德鲁·考伊、贝尔纳迪诺·罗梅拉-帕雷德斯、斯坦尼斯拉夫·尼科洛夫、里舒布·贾恩、乔纳斯·阿德勒、特雷弗·贝克、斯蒂·彼得森、戴维·雷曼、马丁·施泰纳格、米查林娜·帕霍尔斯卡、戴维·西尔弗

2020 年 11 月 30 日—12 月 4 日蛋白质结构预测技术的第十四次重要评估(摘要书)

AlphaFold2 第一年:它改变了世界吗?

原文:https://towardsdatascience.com/alphafold2-year-1-did-it-change-the-world-499a5a38130a

DeepMind 承诺给我们一场革命。发生了吗?

图片由 Greg Rakozy 在 unsplash.com 拍摄

一年前, AlphaFold2 发表在《自然》上。AlphaFold 是在 CASP13(一项旨在预测已获得但尚未发表的蛋白质结构的竞赛)期间推出的,它的表现优于其他竞争对手,但没有引起轰动。在随后的 CASP14 比赛中,AlphaFold2 不仅击败了所有其他竞争对手,而且算法预测的结果与实验获得的结果相似。

在接下来的几天里,研究人员声称 AlphaFold2 意味着蛋白质结构预测的问题终于可以结束了。其他人声称这是一场科学革命,将打开令人难以置信的新视角。

本文提出这个问题:AlphaFold2 一年后,发生了什么?最近有什么进展?AlphaFold2 在哪些应用中使用过?

**Index of the article**
- **The protein: a self-assembling origami
- Does AlphaFold2 have its Achille’s heel?
- Again on the ring: AlphaFold2 will keep the belt?
- No protein left behind
- How men used the fire that Prometheus gave them
- What course does the future take?**

蛋白质:一张自我组装的折纸

照片由约书亚·迪克森unsplash.com拍摄

简而言之,我们如何从序列中获得蛋白质结构的问题一直是二十世纪生物学的核心。蛋白质是生命的引擎;如果信息储存在 DNA 中,那就是蛋白质在每个生物体内执行所有功能。它们的顺序决定了它们的结构,而结构决定了它们的功能。

生物学是有史以来最强大的技术。DNA 是软件,蛋白质是硬件,细胞是工厂。—阿尔温德·古普塔

了解蛋白质的结构有助于更好地理解它在健康和疾病状态下的功能。该结构对于设计能够与蛋白质相互作用的分子也至关重要(实际上所有药物都是在知道蛋白质结构的情况下设计的)。然而,预测结构然后预测功能开启了几乎无限的可能性(从纳米技术到医学,从农业到制药,等等)。

然而,由于各种原因,预测结构是困难的:

  • 一个序列有大量的潜在结构,但只有一个是正确的
  • 蛋白质如何组装的生物物理规则是未知的
  • 测试或模拟所有可能的组合在计算上过于昂贵
  • 各种化学基团之间既有局部的相互作用,也有远距离的相互作用

在之前的一篇文章中,我讲述了为什么这很难,以及了解 AlphaFold2 的结构和更详细的介绍对于进一步学习的重要性:

alpha fold 2 有它的阿奇利之踵吗?

经过一次试验,发现了一些局限性。Unsplash.com 的图片由阿伦视觉提供

AlphaFold2 在蛋白质预测这样复杂的挑战中当然取得了卓越的成绩。AlphaFold2 的影响力已经可以从它获得的引用数量上看出来。 Pubmed 标注为总引用次数 1935 次(2021 年 380 次,2022 年 1639 次)。而根据谷歌学术的说法,这个数字甚至超过了 5000:

谷歌学术作者截图。

今年,一些研究人员也解决了 AlphaFold2 的局限性。在讨论新的应用和最近的发展之前,DeepMind 模型的局限性是什么,这很有趣。

目前,AlphaFold2 无法预测与其他蛋白质的相互作用。事实上,这是一个重要的方面,因为几乎所有由蛋白质执行的功能都是与其他几个伙伴一起执行的。这是一个复杂的问题,因为蛋白质可以在同源二聚体(即与自身)或异源二聚体(与其他不同的蛋白质)中相互作用。这些相互作用可以改变蛋白质本身的结构,从而改变其构型。

AlphaFold2 的作者自己发表了一篇后续论文,其中他们提出了 alpha fold-多聚体,这是一种专门为模拟蛋白质相互作用和组装而设计的模型。虽然结果令人鼓舞,但仍有改进的余地。

用α折叠多聚体预测的结构实例。可视化的是地面真实结构(绿色)和预测结构(用链着色)。”图片来源:原纸

此外,目前 AlphaFold2 没有预测蛋白质结构的其他重要方面:金属离子、辅因子和其他配体。此外,蛋白质的结构不仅仅由氨基酸结构决定。蛋白质可以经历调节其功能和半衰期的修饰(糖基化磷酸化磷酸化泛素化等等)。这些翻译后修饰是重要的,并且在不同的疾病中经常改变。

比如 AlphaFold2 正确预测了血红蛋白的结构。然而,它预测没有血红素辅因子的结构,但生理条件下的血红蛋白总是与血红素辅因子结合。在另一种情况下,CENP-E 驱动蛋白,AlphaFold2 正确地预测了所谓的分子马达,但没有预测盘绕圈区域。事实上,AlphaFold2 在蛋白质的内在无序区域(如卷曲螺旋区域)方面存在困难。

AlphaFold2 的局限性:蛋白质血红蛋白和 CEMPE 的结构与相应的 AlphaFold2 预测。图片来源:此处,牌照:此处

此外,氨基酸的侧链并不总是精确定位的。例如,这些信息对于确定蛋白质的活性位点以及如何设计能够调节它的分子非常重要。

另一个问题是,蛋白质可以有不同的构象(它们不是静态的,而是动态的实体),AlphaFold2 只返回其中的一种。在疾病条件下,蛋白质序列可能会发生改变蛋白质结构的突变,AlphaFold2 目前无法预测这一点。

一个重要的例子是人钾电压门控通道亚家族 H 成员 2 (hERG) 蛋白,在心跳过程中可以以三种构象存在(开放、关闭、无活性)。该通道的突变或与特定药物的相互作用导致长 QT 综合征,因此预测三种不同的构象结构非常重要。

正如麻省理工学院的研究人员所指出的,AlphaFold2 目前只在药物发现的一个步骤中有用:对蛋白质的结构进行建模。事实上,该模型不允许模拟药物如何与蛋白质发生物理相互作用。事实上,研究人员试图模拟细菌蛋白质如何与抗生素相互作用(与蛋白质结合更紧密的分子可能是更好的抗生素),,但 AlphaFold2 不是很有效

“利用这些标准的分子对接模拟,我们获得了大约 0.5 的 auROC 值,这基本上表明你并不比随机猜测做得更好,”麻省理工学院研究员科林斯·JJ 谈到使用 AlphaFold2 对接抗生素和蛋白质时说道(来源:此处)。

然而,一如既往,用户考虑到该方法的局限性是至关重要的。如果结构预测被天真地使用和解释,它会导致错误的假设或明显错误的机械模型。—alpha fold 的欢乐与危险,EMBO 报道

AlphFold2 无疑代表了蛋白质分子结构预测的一个突破。然而,另一方面,它显示了几个局限性,正如麻省理工学院的研究人员指出的那样,在做出假设和用于某些应用时,都必须考虑到这些局限性。

无论模型的目的是什么,模型成功实现的目标都是由数据的性质决定的。AlphaFold2 使用 PDB (蛋白质结构数据库,一个实验确定的蛋白质分子结构的储存库)中的数据进行训练,从而预测蛋白质结构,就好像它是另一个 PDB 条目一样。问题是,许多蛋白质在生理条件下只与辅因子、其他蛋白质结合,或以大的复合物形式存在,但人们不能总是在生理条件下使蛋白质、辅因子或其他蛋白质结晶。

此外,对于超过 40%的所有 UniProt 家族来说,没有蛋白质结晶,而当观察超家族(一个更广泛的类别)时,超过 20%。

因此,尽管在与蛋白质结构相关的出版物中(当这是通过结晶学获得时)解释了如何以及为什么获得该结构(以及该模型的可能限制),AlphaFold2 在其预测中并不考虑这种背景。因此,尽管 AlphaFold2 是一款出色的工具,但我们仍必须考虑其局限性。

再上擂台:AlphaFold2 会留腰带?

图片由内森·杜姆劳在 Unsplash.com 拍摄

AlphaFold2 表明,使用深度学习可以很好地预测单个蛋白质的结构。另外,鉴于 AlphaFold2 最近的成绩, CASP 决定增加挑战的复杂度。如前所述 CASP 是一个致力于蛋白质结构预测算法的挑战现在在 其第 15 版 (第一个 CASP1 在 1994 年,最后两个 CASP13 和 CASP14 被 DeepMind 拿下)。

因此,CASP15 将关注几个类别,包括/

  • 单个蛋白质和结构域建模。同样在这个版本中,将有几个单一的蛋白质结构域,但将强调细粒度的准确性(局部主链基序和侧链)。
  • 组装。这一类别考虑蛋白质相互作用(不同蛋白质结构域之间、不同亚基之间的相互作用等等)
  • RNA 结构和复合物。事实上,RNA 分子也可以组装成 3D 结构,这方面的知识要少得多。
  • 蛋白质-配体复合物。蛋白质可以有生理配体如各种代谢产物(甚至其他蛋白质)或治疗性分子也可以理解为配体。这是药物发现的关键一步,mecate 中的大多数药物实际上都是通过结合来调节目标(蛋白质)的小分子。CASP15 也是推动药物设计研究的一个挑战。
  • 蛋白质构象集合。如上所述,蛋白质不应被视为静态实体,而是可以有几种构象变化。这些变化可以发生在特定的条件下,如结合到特定的底物,磷酸化,或结合到药物。事实上,在实验条件下获得这些构象的数据也很困难(通常通过冷冻电镜和核磁共振获得)。

可以注意到,虽然以前的版本侧重于蛋白质的静态观点,但这里的重点是蛋白质的动态观点(溶液中的行为,与其他蛋白质伴侣或其他生物大分子的相互作用)。因此,鉴于上述限制,这些类别对 AlphaFold2 构成了挑战。

CASP15 的提交于 8 月 19 日截止,结果将于 12 月在安塔利亚大会上公布。因此,我们很快就会知道 DeepMind 是否会保住冠军腰带。与此同时,对于那些喜欢剧透的人来说 CAMEO (一个由不同机构支持的社区项目)在恒定的时间内监控来自不同来源(不同服务器,其中存在不同竞争对手的预测)的预测质量。CAMEO 提供不同的预测分数,你也可以选择时间间隔。

《同样的目标》中的 CAMEO 也使用了来自同一个 AlphaFold2(去年发布的版本)的预测。从图中可以看出,AlphaFold2 naive 版本的表现并不差(越靠近右上方越好)。

作者截图来自 CAMEO 网站

无蛋白质残留

谷歌趋势上关键词“Alphafold”的趋势,作者截图

仅仅 12 个月后,AlphaFold 已经被超过 50 万名研究人员访问,并用于加速从塑料污染到抗生素耐药性等重要现实问题的进展— DeepMind 新闻稿

一年后,AlphaFold2 仍然出现在谷歌搜索中,最近有一些发展,我们将在这里讨论。

文章发表后,DeepMind 立即在 GitHub 上提供了源代码,还提供了一个谷歌实验室,在那里可以输入蛋白质的序列并获得结构的图像。

例如,只需访问 Uniprot 网站,检索一种蛋白质的序列,将其插入谷歌 Colab 细胞,并运行它以获得预测。下面是人类血红蛋白的结构:

MVHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPK
VKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFG
KEFTPPVQAAYQKVVAGVANALAHKYH

这里有一个 AlphaFold2 预测的例子,颜色代表作者使用提供的 Google Colab 得到的模型的置信度(也可以使用 GitHub 源代码但是下载重量是 2.2 TB,需要 GPU 来运行)

如前所述,同一个小组已经提出了针对多聚体复合物的 AlphaFold2 的特定版本,源代码也已公布( GitHubGoogle Colab )。

此外,DeepMind 与 EMBL 的欧洲生物信息研究所(EMBL-EBI) 合作,最初发布了大约 20 个物种(35 万人)的 2000 万个蛋白质的结构。

最近,他们发布了数据库中几乎所有分类的蛋白质的结构(他们在已经收集的蛋白质序列上使用了 AlphaFold2)。DeepMind 建立了一个数据库,所有这些预测的结构都可以在这里访问( AlphaFold DB )或者批量下载这里。该网站包含 2 亿个结构(最初发布了 100 万个结构)。

最近,DeepMind 宣布他们因 AlphaFold 的开发获得了 2023 年生命科学突破奖。

人类如何使用普罗米修斯给他们的火

AlphaFold 1 已经被证明对科学家有用。AlphaFold 的第一个版本不能准确地模拟氨基酸侧链,但科学家发现该模型在主链方面做得非常好。例如,AlphaFold1 在这篇科学论文中被用于确定一种大型谷氨酸脱氢酶的结构(他们利用该模型来指导对这种蛋白质结构的研究)。

大型谷氨酸脱氢酶的结构。图片来源(原创文章)

这阐明了如何将 AlphaFold 与实验工作相结合。事实上,蛋白质结构实验测定中发展最快的技术之一是冷冻电子显微镜。这项技术可以用于未能结晶的蛋白质(剧透:许多重要的蛋白质),并允许我们在更多的生理情况下观察蛋白质(构象、相互作用等……)。另一方面,这种技术产生具有低信噪比的图像,并且不产生原子分辨率。在这些情况下,AlphaFold2 可以帮助预测结构,并促进实验验证。

Science 最近出了一期特刊来解析原子核的孔隙结构。核孔是一种复杂的蛋白质结构,调节生物大分子从细胞核到细胞质的进出。核孔复合物(NPC)由 1000 多个蛋白质亚单位组成,并参与几种细胞过程(除了调节运输之外),使它们在几种疾病和与病原体的相互作用中至关重要(例如,它是几种需要进入细胞核的病毒的靶标)。

另一方面,能够获得超过 30 种 NPC 蛋白的高分辨率 3D 图像被证明是极其困难的。今天,研究人员在 AlphaFold 的帮助下构建了这个模型,因此,三篇文章已经发表,以接近原子的分辨率展示了 NPC 的结构。

人类 NPC 的脚手架建筑。图片来源:这里

2004 年,研究人员发现 PINK1 基因的突变导致了帕金森病的早期发展。能够获得人类 PINK1 的结构很困难(蛋白质不稳定),而他们已经成功获得了昆虫 PINK1 蛋白质的结构。然后,研究人员使用 cryo-EM 和 AlphaFold2 获得了人类 PINK1 的结构。正如研究人员所说,这为研究可用于治疗帕金森病的分子开辟了可能性:

我们可以开始思考,“我们必须开发什么样的药物来修复蛋白质,而不仅仅是处理蛋白质被破坏的事实”——大卫·科曼德(来源)

正如belt Rao教授所强调的,我们可以将该结构用于许多研究目的。从古生物学到药物发现,凡是讨论生物的地方,蛋白质结构都会有用。

我们现在可以追溯蛋白质进化的更长时间——Pedro belt Rao(此处)

AlphaFold2 不仅为药物发现开辟了工业应用的可能性,事实上,蛋白质也用于各种工业过程(洗涤剂中的酶、食品工业等……)。此外,对蛋白质的研究可以用于环境方面的重要挑战:例如,AlphaFold2 已经被用于研究卵黄蛋白原(一种对蜜蜂的免疫力很重要的蛋白质)的结构。蛋白质结构也可以用于设计蛋白质,使 T2 降解塑料或其他污染物。

一只感恩的蜜蜂,高兴我们试图帮助它们免于灭绝。图片来源:【Unsplash.com】德米特里·格里戈列夫

我们在上面讨论了 AlphaFold2 如何在不同的应用中使用。另一方面,该模型是开源的这一事实意味着它可以被使用并合并到其他模型中。例如,这就是 META 对 ESM-IF1 所做的,其中的想法是从结构中检索序列。

另一个最近的应用 Making-it-rain ,对描述蛋白质的动态很感兴趣。在动力系统中,通过施加或改变条件,分子的原子沿着可以遵循的轨迹运动。造雨模拟了蛋白质原子在水中或其他分子(核酸、小分子)存在时的行为。文章中描述的 Making-it-rain 除了几个型号之外,还集成了 AlphaFold2。此外,代码可以在 GitHub 和几个谷歌实验室中找到(链接在资源库中)。

MD 模拟运行在 Google Colab 的“造雨”笔记本上

未来何去何从?

图片由 Torsten Dederichs 在 Unsplash.com 拍摄

EMBL 在一份报告中列出了 AlphaFold2 的一些可能应用,简单来说:

  • 加速结构研究。 AlphaFold2 将能够在蛋白质难以结晶时提供帮助,并在整个蛋白质序列不可结晶时帮助解析通过低分辨率实验方法获得的结构。
  • 填充蛋白质复合物的成分。 AlphaFold2 可以帮助研究大型蛋白质复合物,与其他生物分子(RNA,DNA)的相互作用,甚至帮助提出关于蛋白质功能的假设。
  • 为蛋白质动力学分析产生假说。结构是研究蛋白质动力学的第一步。
  • 预测 RNA 结构。我们可以从预测蛋白质结构中学习,并致力于这一新的前沿领域。
  • 预测蛋白质的进化或序列突变的影响。

我们已经看到了不同的应用,以及将 AlphaFold2 集成到不同模型中的可能性。其他团体关心的是如何使获取信息的途径民主化。 ColabFold 是一个项目,旨在使 AlphaFold2 和其他结构预测算法如 RosettaFold 更容易和更有用。

此外,其他小组已经开始努力克服 AlphaFold2 的局限性,例如预测转运蛋白和受体的替代构象状态。本文中的研究人员已经证明了这是可能的,并且也发布了代码( GitHub )。

预测分子的不同构象。来源:此处

更具挑战性的是估计配体与某个口袋结合的强度(计分问题)。这是药物发现的圣杯,存在多种方法以不同的准确度描述蛋白质-配体结合。— 化学博士。Inf。模型。2022,62,3142-3156

当然,制药行业将从 AlphaFold2 的发布中受益最大。蛋白质结构是设计分子的第一步。事实上,市场上的大多数药物都是“小分子”,即插入蛋白质“口袋”并阻断或激活蛋白质的小分子。AlphaFold2 可以帮助研究人员识别新的口袋,从而实现小分子的设计。

另一个新领域是从零开始提取蛋白质。去年六月,韩国批准了一种从零开始制造蛋白质的疫苗。这一成果是经过 10 年的紧张工作才取得的。

AlphaFold2 的出现给这个领域带来了新的宣传。几家公司已经决定专注于人工智能在蛋白质设计中的应用。毕竟,设计蛋白质在医药、垃圾清理、纳米技术等各个领域都是有用的。

例如,一些建议的方法,如现有结构的变化或组装局部结构,使用 AlphaFold2 要容易得多。还有更特殊的方法,如“蛋白质幻觉”,其中你从随机氨基酸开始,同时预测结构(尽管这种方法似乎效率低下,但它在 100 种肽上进行了测试,其中五分之一达到了预测的形状)。

正如本文中所描述的,这个领域正在快速发展。此外,一些工具和算法正在开发中,以帮助绘制蛋白质( ProteinMPNN 就是一个例子,它有助于在绘制蛋白质时进行一种“拼写检查”)。另一个有趣的方法是使用 GPT-3 而不是生成文本,它用于生成蛋白质序列

如何利用深度学习进行蛋白质设计的例子。图片来源:此处

离别的思念

图片由在 Unsplash.com 的蝠鲼拍摄

AlphaFold2 获得的结果令人印象深刻,以至于 CASP 协会取消了一些类别,因为它们被认为对可用的算法来说太简单了。
然而,我们看到的 AlphaFold2 有几个局限性(不同的构象、与其他生物分子的相互作用、配体等……)。在某种程度上,这些问题源于实验数据库的不完整性,一种算法依赖于它在训练过程中看到的数据来进行预测。

尽管有这些限制,AlphaFold2 还是被用于不同的目的和不同的出版物中。AlphaFold2 还是一个起点,毕竟结构预测只是药物发现的步骤之一。无论如何,在没有实验证据和仅仅使用算法预测的情况下开始药物开发还为时过早。

此外,添加的结构越多,这些预测模型的改进就越多。AlphaFold2 使用类似序列的比对,但即将推出的模型将更加复杂,包括其他信息、物理和生物学原理等。

关于构象态、动力学和瞬态相互作用的更复杂的问题仍然需要注意和实验来回答。因此,重要的是,资助者和同行评审者不要相信“折叠问题已经解决”。— EMBL 报道

也就是说,结构生物学不会消失,而是利用算法,让它进行得更快。多年来,想获得所有蛋白质的结构是一个梦想:光靠实验方法取得成功代价太高。另一方面,许多蛋白质家族没有被涵盖,需要填补空白以帮助模型学习,并且折叠过程的许多方面仍然不清楚

正如我们所看到的,获得结构是第一步,在未来,我们将利用这些信息来设计新的蛋白质,获得它们的序列,并为各种应用生产它们。

迄今为止,来自 190 个国家的 500,000 多名研究人员访问了 AlphaFold 数据库,查看了超过 200 万个结构。— DeepMind 新闻稿

研究人员已经利用 AlphaFold2 进行了几个研究项目,创造了新的工具,许多新的应用程序也即将出现。要看到 AlphaFold2 开启的所有新的可能性,一年的时间仍然很短。

然而,在药物发现方面,我们只能在未来几年内看到 AlphaFold2 和这些新方法的结果。不幸的是,在一种新疗法上市之前,它必须通过临床前和临床试验的几个步骤。从构思到到达临床,也可能需要长达十年、数十亿美元的时间(更不用说很多临床试验失败)。人工智能对于缩短时间和费用至关重要,但这是另一个故事了(可能会在另一篇文章中讲述)。

如果你觉得有趣:

你可以寻找我的其他文章,你也可以 订阅 在我发表文章时得到通知,你也可以在LinkedIn上连接或联系我。感谢您的支持!

这是我的 GitHub 知识库的链接,我计划在这里收集代码和许多与机器学习、人工智能等相关的资源。

**https://github.com/SalvatoreRa/tutorial

或者随意查看我在 Medium 上的其他文章:

附加资源

AlphaGo:人工智能如何掌握围棋

原文:https://towardsdatascience.com/alphago-how-ai-mastered-the-game-of-go-b1355937c98d

应用强化学习

AlphaGO 背后的机器学习架构解释了

图片由 David Silver 等人提供,AlphaGo 论文[1]

2016 年,AlphaGo 击败了世界上最好的围棋选手。然后,这似乎是不可能的;现在,它作为机器学习历史上的一个关键里程碑而被铭记。

围棋游戏与机器学习

游戏,无论是棋盘游戏还是视频游戏,都是测试、评估和改进机器学习模型的完美平台。游戏通常有一个非常清晰的评分系统,因此提供了一个清晰有效的方法来量化和衡量进展。

在过去,其他棋盘游戏标志着科技史上的重要里程碑。当深蓝在 1997 年击败国际象棋世界冠军加里·卡斯帕罗夫时,这被视为计算世界的一项不可思议的成就。当时,许多人认为这还需要几十年,有些人认为这永远不会发生。

这个模型的成功要归功于人类的输入(由其他国际象棋大师)和原始的计算能力。它能够预见未来的许多行动,击败人类。一些人批评深蓝的方法,比如加来道雄,在他 2008 年的书《不可能的物理学》中说,这个事件是“原始计算机能力的胜利,但是这个实验没有教会我们任何关于智力的东西”。他补充道,“我过去认为国际象棋需要思维”。他将机器对人类的胜利归功于深蓝可以在未来计算移动位置,而不需要创造力智能

围棋比国际象棋复杂得多。在国际象棋中的任何给定位置,预期的合法走法是 35 步左右,在围棋中是 250 步。事实上,围棋棋盘中可能的布局比宇宙中的原子还要多。未来的计算走法不是一个可行的策略,因此要解决围棋游戏,真正的思维创造力智力是必需的。

这就是为什么在围棋比赛中击败世界上最好的人类是机器学习世界的一个如此重要的里程碑,在这篇文章中,我想讲述谷歌的 DeepMind 是如何掌握这场比赛并击败历史上一些最好的选手的。

围棋的规则

作者提供的图片(已捕捉到褪色的黑色部分)

在我进入大联盟之前,先简单介绍一下围棋。围棋很简单。每个玩家在棋盘上放置一个棋子,一次一个。目标是用你的作品包围空白的空间。你通过包围空白区域来赢得分数。如果你包围了对手的棋子,这些会被捕获(见上图左下方褪色的黑色棋子)。游戏结束时,控制最多空间的玩家获胜。在上面的最后一个位置,白棋控制了更多的空位,所以白棋获胜。

我试着玩在线围棋,结果很糟糕。好消息是,你不需要理解围棋或擅长围棋,就能理解 AlphaGo 背后的机器学习。

强化学习,基础

Deep Mind 的联合创始人兼首席执行官戴密斯·哈萨比斯说:“即使你把世界上所有的计算机都拿来运行一百万年,也不足以计算出(围棋比赛中)所有可能的变化。”。

强化学习是机器学习中通过玩来学习的模型类别。这些模型的学习方式有点像人类,它们通过多次迭代来玩游戏,随着输赢不断改进。我还有一篇文章更详细地解释了强化学习是如何工作的。你可以在这里查看。

简单回顾一下,强化学习的核心可以通过以下定义来理解:

代理是正在玩游戏的算法。

环境是游戏进行的平台,对于围棋来说是棋盘。

状态是环境中所有棋子的当前位置。

动作是代理在环境的给定状态下可能采取的动作。

表示在给定状态或动作/状态对的情况下赢得游戏的可能性。

策略是代理根据下一个状态的预测值选择下一个动作的方法(根据价值函数,你总是选择你认为最好的动作吗?你应该不时地探索以学习新的东西吗?).

RL 算法的目标是学习最优值函数,这将允许它在任何给定的状态下确定将导致它赢得游戏的最高可能性的动作。

用深度神经网络和树搜索掌握围棋

大卫·西尔弗、阿贾·黄、克里斯·j·马迪森、阿瑟·古兹、洛朗·西夫尔、乔治·范登德里斯切、朱利安·施利特维泽、约安尼斯·安东诺格鲁、韦达·帕内尔·谢尔瓦姆、马克·兰托特、桑德·迪耶曼、·格雷韦、约翰·纳姆、纳尔·卡尔希布伦纳、伊利亚·苏茨基弗、蒂莫西·利利拉普、马德琳·利奇、科拉伊·卡武克库奥卢、托雷·格雷佩尔&戴密斯·哈萨比斯。发表于 2016 年《自然》杂志。

在这里我将介绍 AlphaGo 的架构。在阅读本节的同时,请随意参考上面的定义。

AlphaGo 由两种类型的神经网络组成,策略网络和网络。

政策网络

BLUF: 策略网络的目标是确定给定状态下下一个动作的概率。政策网络的培训分为两个步骤。首先,通过在专业游戏数据上的监督学习(SL 策略网络)来训练策略网络。然后,该模型的权重被用作使用强化学习训练的策略网络(RL 策略网络)的初始化。

图片由 David Silver 等人提供,AlphaGo 论文[1]

监督学习(SL)策略网络:策略网络的输入是状态。如上所述,状态代表各部分的位置。板子是 19x19。为了改善输入,他们使用 48 个特征地图对其进行扩展,使输入形成 19x19x48 的形状。这些特征图帮助算法捕捉棋盘的位置,这可以通过突出棋子周围的空白空间、棋子的聚集等来实现。这种扩展有助于算法学习。

SL 策略网络是一个监督学习分类模型(到目前为止,没有强化学习)。它是使用职业玩家游戏来训练的。策略网络然后被给予任务来预测在给定状态 p(a|s) 之后下一个最可能的动作。策略网络是一个卷积神经网络(CNN),它输出下一个动作应该是什么的一系列概率(19x19 输出)。

作者图片

策略网络 (p) 的权重 (σ) 可以与上面所示的项成比例地更新。这基本上是监督学习分类设置中的交叉熵损失。这里,我们正在更新权重,以便在给定状态 (s) 的情况下,最大化人员移动 (a) 的可能性。

推广政策是一个小得多的网络,以同样的方式训练。它是一个线性层,并且在较少的数据上进行训练。计算未来位置要快得多,因此在以后执行树搜索时会很有用。

图片由 David Silver 等人提供,AlphaGo 论文[1]

RL 策略网络:到目前为止,策略网络都是监督学习模型,它们所做的只是使用职业选手的训练数据来模仿他们。这些模型仍然没有学会自己玩游戏。为此,通过强化学习来改进 SL 策略网络。

RL 策略网络用 SL 策略网络的权重初始化。然后通过自我游戏来训练它,你把这些模型放在一起,让它们互相游戏。在游戏结束时,模型采取赢的模型的行动的概率应该增加,而输的模型的行动则相反。通过让模型自己玩,我们现在正在教模型赢得游戏,而不仅仅是模仿大师。

作者图片

权重的更新功能类似于 SL 策略网络的更新功能。然而这一次,我们等到游戏结束时才执行更新。 z 是基于游戏结果的奖励。如果代理赢了,那么 z 就等于+1,如果它输了就等于-1。通过这种方式,我们可以让模型更喜欢它赢的时候玩的棋,而不太可能去玩它输的时候玩的棋。

价值网络

BLUF: 价值网络预测给定状态下代理人赢得游戏的概率。有了一个完美的价值网络,你可以通过选择导致最高价值状态的行动来确定每个位置的最佳移动。实际上,作者使用树搜索方法来确定下一个最佳行动,仍然使用这个近似值函数。

为了训练价值网络,3000 万个游戏由自我游戏产生(让 SL 策略网络自我游戏)。然后,价值网络被训练为来自这些游戏中的每一个的随机状态的回归,以确定该位置是赢还是输。这又是一个监督学习任务,尽管这次是一个回归任务。

作者图片

价值函数的梯度更新如上所示。右侧的比例项是结果 z 和值函数 v(s) 的输出的函数。如果当给定一个状态时,价值函数正确地预测代理将获胜,则比例项保持为正。然而,如果价值函数预测代理将失败,但最终获胜,则比例项为负,我们以相反的方向更新梯度。这个比例项的大小随着价值函数对赢得游戏的可能性的预测的错误程度而增加。这基本上是监督回归任务中的 L2 损失。

树形搜索

为了确定下一个最佳移动,执行树搜索。

图片由 David Silver 等人提供,AlphaGo 论文[1]

树搜索从给定状态 s 开始。基于 Qu(P)选择一个动作。

作者图片

Q动作值函数,决定一个动作而不是一个状态的值。

u(s|a) 是树搜索算法的输出,其中输出与先验成比例(上面等式中的分子,先验取自 SL 策略网络)。 u(s|a) 也与给定一个动作下对树上叶子的访问次数 (N(s,a)) 成反比。在开始时,该算法探索新的移动,并且不太信任 Q 值函数(取 argmax 的 u 部分,因为 N(s,a) 会很小),但是在搜索算法多次收敛于同一叶子之后,它开始更多地依赖于 Q,允许它更深入地探索已经被经常访问的有希望的分支。

作者图片

为了计算 Q (动作值函数),他们首先使用 V 评估每片叶子,这是结果状态的值函数 (v) 、和展示结果 (z) 之间的加权平均值。展示结果是应用展示策略网络并从叶状态开始进行游戏时游戏的结果(赢或输)。因为首次展示策略网络很小,所以评估很快,因此它可以用作树搜索的一部分,以便对每个叶子多次执行整个游戏,并在选择最终动作时考虑这些结果。最后,要从叶子评价 V 得到 Q ,你只需将 V 除以那个边的访问次数。

结果和进一步研究

AlphaGo 模型实际上是一系列模型,每一个新的迭代都比前一个有所改进。发表了两篇论文,第一篇是 2016 年的《用深度神经网络和树搜索掌握围棋博弈》(我经历过的那篇),第二篇是 2017 年的《掌握没有人类知识的围棋博弈》。第二篇论文介绍了 AlphaGo Zero,它没有使用先验知识(没有专业棋手训练数据),只是简单地依赖于 RL 策略网络、价值网络和树搜索。该模型完全基于强化学习,并推广到其他棋盘游戏,如国际象棋,它能够击败国际象棋中最好的引擎 stockfish。

David Silver 等人的 AlphaGo Zero Paper [2]

AlphaGo Lee 是 2016 年以 4 比 1 击败 Lee Sedol 的模型,是围棋历史上最强的选手之一。AlphaGo Master 随后以 60 比 0 的比分击败了一系列世界象棋冠军。最后,AlphaGo Zero 是一个更好的迭代,没有使用专业的训练数据(见上面的 Elo 评级)。

结论

在这篇文章中,我描述了 AlphaGo 的架构,这是一个击败了有史以来一些顶级围棋选手的机器学习模型。我首先经历了强化学习的一些基础,然后我分解了模型的架构。

现在你可能会奇怪,为什么要花这么大的力气去解决一个棋盘游戏呢?毕竟,这对任何人都没有帮助。研究是为了从整体上加深我们的知识,而像围棋这样的游戏可以让我们非常容易地量化进展。我们在这些棋盘游戏上取得的进展可以用来解决更大的挑战。DeepMind 致力于节省能源、识别疾病和加速全球科学发展的模型。这种研究至关重要,因为它加深了我们对人工智能的知识和理解,在未来,它很可能成为许多改变生活的技术的催化剂。

支持我

希望这对你有所帮助,如果你喜欢,可以 跟我来!

您也可以使用我的推荐链接成为 中级会员 ,并访问我的所有文章及更多:https://diegounzuetaruedas.medium.com/membership

你可能喜欢的其他文章

参考

[1]大卫·西尔弗、阿雅·黄、克里斯·j·马迪森、阿瑟·盖兹、劳伦特·西弗、乔治·范·登·德里斯切、朱利安·施利特维泽、约安尼斯·安东诺格鲁、韦达·潘妮尔·谢尔瓦姆、马克·兰托特、桑德·迪耶曼、张秀坤·格雷韦、约翰·纳姆、纳尔·卡尔施布伦纳、伊利亚·苏茨基弗、蒂莫西·利利拉普、马德琳·利奇、科雷·卡武克库奥卢、托雷·格雷佩尔和戴密斯·哈萨比斯,《用深度神经网络和树搜索掌握围棋博弈》,自然,2016 年。可用:https://www.nature.com/articles/nature16961

[2] David Silver、Julian Schrittwieser、、Ioannis Antonoglou、Aja Huang、Arthur Guez、Thomas Hubert、Lucas Baker、Matthew Lai、Adrian Bolton、Yutian Chen、Timothy Lillicrap、Fan Hui、Laurent Sifre、George van den Driessche、Graepel & amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;可用:https://www.nature.com/articles/nature24270

AlphaZero 国际象棋:它是如何工作的,是什么让它与众不同,它能告诉我们什么

原文:https://towardsdatascience.com/alphazero-chess-how-it-works-what-sets-it-apart-and-what-it-can-tell-us-4ab3d2d08867

深入探讨 21 世纪计算机象棋中最具革命性的现象

由福托梅克。来源:Istockphoto

介绍

对于那些对国际象棋感兴趣的人,或者那些一直在关注人工智能最新发展的人来说,“AlphaZero”这个名字会立刻被认出来;它战胜了当时世界领先的国际象棋引擎 Stockfish,彻底改变了计算机和人类下棋的方式。

然而,如果你不是国际象棋爱好者,或者错过了几年前的新闻,你可能会想知道 AlphaZero 到底是什么,是什么让它值得写一整篇博文。为了你,我会解释的。

简而言之,AlphaZero 是一个游戏程序,通过自我游戏和神经网络强化学习的结合(稍后会有更多介绍),它能够学习玩国际象棋等游戏,并从头开始──也就是说,在只被灌输了这些游戏的规则之后。事实上,AlphaZero 的一个较新的衍生产品叫做 MuZero ,它不仅限于国际象棋等棋盘游戏,还可以学习玩雅达利系列的一系列简单视频游戏。AlphaZero 和 MuZero 都是由谷歌母公司 Alphabet Inc .的子公司 DeepMind 设计的。

雅达利经典吃豆人是穆泽罗学会玩的游戏之一。来源:截图,中途游戏。

好的,所以我们手上有一个被称为普通游戏玩 (GGP)人工智能的东西。我们以前见过这些;例如,斯坦福大学已经举办了十多年的年度 GGP 竞赛,像 2016 年的伍德斯托克这样的参赛作品表现尤为出色。那么是什么让 AlphaZero 如此特别呢?它非常擅长它所做的。事实上,它好到连 AlphaZero 负责学习的游戏专业人员,甚至是专门为玩其中一种游戏而开发的最先进的专用计算机程序,都比不上它的表现。是百事通,全的高手。作为对 AlphaZero 的 GGP 能力的一个案例研究,我将专注于国际象棋,我自己也是一名狂热的国际象棋选手。

这篇博文背后的前提是,作为一名大师级棋手,我准备从基于象棋的角度提供一些人工智能领域的外部见解。为此,我将首先详细介绍传统引擎和 AlphaZero 的工作方式,然后解释这两种引擎的播放风格可以告诉我们什么,并探索这对更广泛的人工智能领域的影响。

*在国际象棋方面,截至 2022 年,较新的引擎被普遍认为已经超越了 AlphaZero 的打法实力;然而,在当时,它的性能是前所未有的,尽管围绕 AlphaZero 运行的改进硬件存在一些争议。

传统发动机

自从 20 世纪 70 年代计算机国际象棋领域出现以来,直到 AlphaZero 的诞生,几乎所有国际象棋引擎的基本架构都几乎相同。

在每一步棋中,对博弈树进行深度优先搜索(DFS ),直到指定的深度:也就是说,计算从当前位置开始的选定数量的每一步棋的合法序列。由于没有遗漏合法的移动,这类搜索算法被称为强力搜索

一个在短游戏树上表演的 DFS 动画。来源:维基共享资源。

然后,树的终端节点(或每个计算序列中的最后一步棋)被分配一个数值,粗略地说,该数值表示该节点的位置对白棋(拿白棋的玩家)有多有利;这个值被称为静态 评估。它是使用由一群强大的国际象棋选手制定的许多启发法计算出来的,旨在代表人类在评估任何给定位置时考虑的因素。这些试探法包括明显的指标,如材料计数(双方棋子的原始总值)和棋子方格表读数(棋子方格表详细说明了棋子有多好,一般来说,每个棋子在 64 个方格中的每一个上),但也包括更微妙的考虑,如国王安全和棋子结构。截至 2022 年领先的国际象棋引擎 Stockfish 14*使用的试探法的完整列表,可以在这里找到。

微型博弈树终端节点的静态评估。来源:作者。

因此,称为最小最大算法的东西从终端节点一直运行到博弈树的根,其代表当前位置:对于每个先前没有被评估的节点,如果轮到白棋在该节点移动,则评估被计算为该节点的子节点的评估集的最大值(或对该节点所代表的移动的回复);并且如果轮到黑棋在该节点移动,则它被计算为该集合的最小。这确保了评估过程需要双方在启发式估计中的最佳发挥,并输出对当前位置的粗略评估。

极大极小算法在先前图像的博弈树上运行。在这种情况下,由于引擎显示为黑色,因此该评估在引擎转动时被最小化。对该职位的评估是-4。来源:作者。

最后,应用称为α-β修剪的修剪算法。该算法工作如下:两个值α和β存储在每个节点上,它们分别代表任何祖先节点的白色和黑色的最差可能评估。直观地说,这些值代表阈值,超过该阈值,由于比一些先前评估的移动更差,节点不能对祖先之一有任何影响。因此,由于白棋考虑让黑棋走与白棋已经计算好的棋步一样多或更多的棋步是没有价值的,反之亦然,阿尔法大于或等于贝塔的节点被跳过,或者被修剪。这个聪明的技巧节省了大量的计算能力,而不影响准确性。

阿尔法贝塔剪枝算法适用于游戏树从早期。来源:作者。

注意,在上图中,31 个节点中只有 3 个被修剪。这是因为很多次优的走法,比如黑棋的第一步棋(+1),是在最优走法之前评估的,比如黑棋的第一步棋(-4)。如果我们改变评估移动的顺序,以便更频繁地首先评估更有希望的移动,我们可以大大增加修剪的节点数量:

来源:作者。

因此,传统引擎的最后一个核心组件是另一组试探法,它决定了计算移动的顺序。这一类的一个常见的启发是捕捉的优先级,例如,因为捕捉是将对手的棋子从棋盘上拿走从而削弱他们力量的唯一直接方式。

可以对这个简单的模型进行大量的添加和改进,例如各种附加的修剪算法、开放书、残局表库等等。然而,这是计算机第一次击败业余选手,最佳最多产的大师和大师,并最终在 1997 年著名的深蓝 vs 卡斯帕罗夫比赛中一劳永逸地取代人类成为最佳下棋实体的中坚力量。经过一些修改,它继续被用于当今最强大的国际象棋引擎。

结合本节中描述的所有内容,我们可以为传统引擎的外观生成一些 Python 代码。请注意代码如何将所有概述的步骤合并到一个简化的流程中:

  • Stockfish 14 以一种叫做 NNUE 的方法,将一个简单的神经网络整合到其静态评估函数中,从而偏离了传统架构;然而,试探法仍然被用于向神经网络提供由一组位置和它们各自的试探法评估组成的训练数据。

阿尔法零

因此,在最后一节中,我们看到了国际象棋引擎在本质上几十年来几乎是一样的,只是在很大程度上有所改进,尽管这些改进很重要,但不是种类。这一切都在 2018 年发生了变化,当时 DeepMind 公布了 AlphaZero 的内部工作原理,此前 alpha zero 在对抗当时最强的引擎时表现出色,震惊了国际象棋界。2018 年论文发表后不久,一个名为 LeelaChessZero 的开源 AlphaZero 克隆项目启动,该引擎迅速上升到计算机象棋的巅峰,成为 2019 年全球最强引擎

那么与传统引擎相比,AlphaZero 和 LeelaChessZero 到底有什么不同呢?让我们深入探究一下 AlphaZero 的大脑来寻找答案。

首先,卷积神经网络(在视觉分类任务中表现特别好的一类神经网络)被初始化,以返回两个随机生成的输出:v(s),它稍后将估计玩家从位置 s 开始的获胜概率,以及 p(s) ,它稍后将估计从位置 s 开始的每个合法移动的前景如何。该网络随后将被训练以接收位置 s 并从中预测 v(s)和 p(s) 。称这个初始化的神经网络为 nnet0

虽然 nnet0 并不出色,但我们仍然可以使用它来构建一个游戏代理,目的是使用来自该代理所玩游戏的数据来训练另一个神经网络 nnet1 ,并使用该网络来构建一个更好的游戏代理。最终的策略是重复这个过程足够多次,使我们最终的神经网络收集到足够的数据,使我们最终的游戏代理高度胜任。

AlphaZero 使用的游戏代理设计是基于一种版本的蒙特卡罗树搜索,它不像 DFS 那样尽可能深入地探索每个可能的分支,而是探索少量分支,直到游戏结束。MCTS 背后的基本前提是,它不需要探索像 DFS 一样多的分支,额外的计算能力可以被重定向到其他地方。

在 AlphaZero 的案例中,它使用的 MCTS 的确切版本如下:

  • 在每一步中,都会运行大量的模拟。把模拟想象成一个迷你游戏,代理人和自己对弈,看哪一步会产生最有利的结果。
  • 每个模拟都需要执行以下算法,直到遇到任何先前模拟都没有访问过的第一个位置:
  • 在当前位置的所有合法移动中,选择最有效的一个。最有成效的一步棋被评估为在探索利用之间达到完美平衡的一步棋:也就是说,一步棋有足够好的记录值得探索(利用),但另一方面还没有被探索太多以产生有用的信息(探索)。更严格地说,最有成效的行动 a 是最大化以下表达式的行动,称为置信上限:

Q(s,a) 是移动 a 在所有探索它的模拟中的平均评价(稍后有更多细节),并表示开发分数,或估计移动 a 的客观良好程度。 c 是一个超参数常数,代表勘探相对于开采的优先程度。 P(s,a) 表示一步 a 有多有希望,或者值得探索的程度(同样,稍后会有更多细节)。最后, N(s,a) 是从位置 s 开始移动 a 的次数。注意,随着 N(s,a) 变大,等式中的最后一项变小,因此已经被访问太多次的移动将减少该项的值;因此,人们可以认为它代表了探索分数。

  • 玩选定的移动并再次运行算法,直到游戏结束,在这种情况下,如果玩家赢得游戏,模拟的移动 a 的评估被记录为 1,如果玩家没有赢得游戏,则被记录为-1,并且剥削分数 Q(s,a) 被重新计算,或者,如上所述,遇到任何先前模拟都没有访问过的位置 r 。在这种情况下,将用于模拟的 r 的评估记录为v(r),并将 P(r) 设置为P(r)──这就是我们的 P(s,a) 的来源,因为我们仅在当前位置被访问过的情况下运行该算法,所以 P(s) 将已经被访问过**
  • 一旦预先选定的模拟次数( AlphaZero 使用 1600 )已经完成,我们现在就有了一个分布,称为策略,表示位置 s 的每个合法移动已经被访问了多少次。回想一下,在每个模拟中只有最有希望的移动被访问,所以策略是代理认为每个移动有多有希望的直接反映。因此,我们可以用它来选择我们在游戏中的下一步。

唷,那可真够难的。好吧,幸运的是,从现在开始,事情会变得简单一点。不过,在我开始之前,让我们为上述版本的游戏代理编写一些 Python 代码:

现在我们已经建立了我们的游戏代理,我们希望让它自己玩很多次(AlphaZero 使用 25,000 次),这样我们就可以使用游戏中的数据,并将其输入到我们新的神经网络中。很简单,对吧?没那么快。如果我们总是玩最有希望的棋,代理人会一遍又一遍地重复玩同一个游戏或非常相似的游戏,这不会给我们提供多少数据。因此,一个更好的解决方案是随机选择由政策向量加权的合法举措──这些举措越有希望,就越有可能被采用。

好吧,但是新的神经网络需要什么数据呢?好吧,回想一下,神经网络的目的是输出一个给定位置的获胜概率,以及每个合法移动有多大希望的分布。前者可以根据自我博弈的结果进行训练,后者可以根据政策向量进行训练,政策向量应该能够准确地反映出这些举措的前景。因此,每场游戏中的每一步棋都会产生一个训练示例,其中包含作为输入的棋步之前的位置和游戏的最终结果以及作为输出的棋步策略。

注意,我们的第一个神经网络将具有完全随机的输出,而我们的第二个神经网络将输出完全随机的策略估计器,因为它已经被训练了随机策略。然而,从我们的第三个神经网络开始,所有的输出应该是有意义的,并且应该随着每一代的更新而变得更加精确。

但是如果他们没有呢?为了防止这种情况,我们启动了训练过程中最令人兴奋的部分──让旧的神经网络和新的神经网络相互对抗!我们让他们玩一定数量的游戏(AlphaZero 使用 400),如果新网络的胜率高于预先选择的阈值(AlphaZero 使用 55%),我们就使用新网络来构建我们新的玩游戏代理,并继续训练过程;否则,我们使用旧网络。

我们将整个过程重复设定的次数(AlphaZero 使用 200),瞧!这里我们有一台前所未见的革命性的象棋机器。我的意思是,你仍然需要令人难以置信的硬件和数小时的培训时间来启动,但不幸的是,我不能帮助你。

含义

好了,现在我们知道了 AlphaZero 出现之前的情况,以及 AlphaZero 是如何改变它的。至少在理论上是这样。那么实际上呢?在我深入研究一些具体的例子之前,让我们先想想我们可能会期望什么。

我们现在知道,AlphaZero 选择棋步的核心是基于哪一步棋产生最高的估计最终获胜概率,这与传统引擎相反,传统引擎选择的棋步是一个最有利的位置,一个固定的量。因此,我们可能会看到,与传统引擎不同,AlphaZero 更喜欢不会产生最有利的短期头寸,但会增加游戏结束时获胜概率的举动。

此外,我们知道传统引擎的评估功能是基于相当简单的试探法,而 AlphaZero 的评估功能是高度复杂的神经网络在数百万场游戏中积累的经验的产物。因此,有人可能会怀疑 AlphaZero 更有可能识别抽象概念,比如那些由 moremovers 的解所包含的概念。

最后,可以说我们可以做出的最直接(也可以说是最令人兴奋)的预测是,由于 AlphaZero 不利用任何人类知识,不像传统引擎(不仅使用人类构建的试探法,还使用开卷机,有时还使用残局桌面),我们可以期待它提出人类以前不知道的全新想法。人们可能会对游戏的开场阶段抱有特别高的期望,这几乎完全被依赖于人写的开场书的传统引擎所绕过。

那么这些预测是如何累积起来的呢?我将用 AlphaZero 玩的一个真实游戏中的例子来演示性地回答这个问题。

在 AlphaZero 和 Stockfish 8 之间移动 41 步。来源:作者。

在这个位置,从表面上看,白方的 h 卒给黑方的 g 卒施加压力,破坏了黑方国王周围的防御。然而,AlphaZero 决定将它的 h 兵推进一格,放弃了对黑方防御的压力,也扼杀了白方进攻的希望。

位置在 42 之后。h6。来源:作者。

为什么会这样?它的意图在 8 步后暴露了:通过一系列的动作,它几乎迫使黑方皇后一路退回到 h8,之后它巧妙地用一辆车关闭了航路,从而将它锁在了游戏之外。

位置在 49 之后。Rf6。来源:作者。

突然之间,h 兵推动背后的原因似乎很清楚:黑皇后很想去 g7,然后去 f8,但 h 兵阻止它这样做。

Stockfish 8 是一个传统的引擎,也是当时世界上最强的引擎,它低估了这个想法:从远处看,它认为这个位置是有利的,因为黑棋有更多更好的棋子。另一方面,AlphaZero 能够看得更远,明白在游戏的剩余时间里黑皇后就像不存在一样。

在一个人的国王面前把侧翼兵一直推到棋盘上是一个从未被大师们认真对待过的想法;然而,自从 AlphaZero 展示了概念验证之后,这个想法就经常出现在人类的最高层,尤其是被五次世界冠军芒努斯·卡尔森所利用。

虽然这只是一个例子,但 AlphaZero 在与 Stockfish 8 的比赛中始终表现出卓越的长期理解,并引入了许多顶级大师迄今为止从未使用过的想法。

至于抽象概念,结果是好坏参半。一方面,如上所述的策略清楚地表明了比以前看到的更高水平的抽象理解:例如,在上面的场景中,如果不认识到棋子被永久锁定在游戏之外的抽象概念,就不可能正确地评估位置。另一方面,AlphaZero 和它的克隆体 LeelaChessZero 仍然在与已经被人类解决的谜题作斗争,并且有一些位置对于即使是弱小的人类玩家来说也是微不足道的,他们都错误地评估了这些位置。

那么,从这项以国际象棋为中心的研究中,我们能从总体上对人工智能得出什么结论?我们已经看到,通过使用神经网络,可以训练机器掌握过去只属于人类的概念,例如非常长期的规划。我们还看到,基于神经网络的架构比竞争对手的架构更好,而人类也比它们更好:在我们的情况下,这是创造力和长期思维。由此,我们可以假设神经网络可能会在人类仍然比机器有一些优势的领域开辟新天地,即使机器在所述领域总体上比人更有能力。因此,也许像国际象棋这样更多由机器主导的领域应该被神经网络重新审视。然而,尽管如此,我们也看到,尽管神经网络令人印象深刻,但在一些更普遍的智能方面,如抽象思维方面,它们仍然与人类相去甚远。

因此,我的结论将是:人工智能显然正以令人难以置信的速度前进;然而,在它充分发挥潜力之前,我们还有很长的路要走。

如何用 Python 编写易读、优雅的正则表达式模式

原文:https://towardsdatascience.com/alternative-regex-python-library-1d15daad8b13

用友好的声明性语法编写更容易理解的正则表达式

来自佩克斯大卫·巴图斯的照片

毫无疑问,Regex 是有史以来最有用的文本处理工具。它帮助我们找到文本中的模式,而不是精确的单词或短语。正则表达式引擎也明显更快。

然而,困难的部分是定义一个模式。有经验的程序员可以随时定义它。但是大多数开发人员将不得不花时间搜索和阅读文档。

不管经验如何,每个人都发现阅读别人定义的模式很困难。

这就是 PRegEx 解决的问题。

PRegEx 是一个 Python 库,它使得正则表达式模式更加优雅和可读。现在它是我最喜欢的清理器 python 代码库之一。

您可以从 PyPI 库安装它。

pip install pregex# [Poetry](https://www.the-analytics.club/virtualenv-alternative-for-python-dependency-management) users can install
# poetry add pregex 

开始编写可读性更强的正则表达式。

这里有一个例子来说明 PRegEx 有多酷。

从地址中提取邮政编码的需求非常普遍。如果地址是标准化的,这并不困难。否则,我们需要使用一些巧妙的技术来提取它们。

美国的邮政编码通常是五位数。此外,一些邮政编码的扩展名可能是由连字符分隔的四位数。

例如,88310 是新墨西哥州的邮政编码。有些人更喜欢使用带有分机 88310–7241 的地理段。

下面是寻找这种模式的典型方法(使用 re 模块)。

使用正则表达式在 Python 中查找我们的邮政编码

这些步骤似乎很简单。然而,如果你要向一个编程新手解释你是如何定义这个模式的,你将不得不做一个小时的演讲。

我也不打算解释了。因为我们有 PRegEx。这是它的 PRegEx 版本。

使用 PRegEx 模块在 Python 中查找我们的邮政编码。

如您所见,这段代码定义和理解都很简单。

该模式有两个部分。第一段应该正好有五个数字,第二段是可选的。此外,第二段(如果有)应该有一个连字符和四个数字。

理解子模块以创建更令人兴奋的正则表达式模式。

这里我们使用了 PRegEx 库的几个子模块——类和量词。“类”子模块确定匹配什么,量词子模块帮助指定执行多少次重复。

您可以使用其他类,比如 AnyButDigit 来匹配非数字值,或者 AnyLowercaseLetter 来匹配小写字符串。要创建更复杂的正则表达式模式,您还可以使用不同的量词,比如 OneOrMore、至少、AtMost 或不定。

这是另一个有更多精彩比赛的例子。我们需要找出短信中的电子邮件地址。这很简单。但是除了匹配模式之外,我们还对捕获电子邮件地址的域感兴趣。

使用正则表达式匹配 Python 中的电子邮件地址,并捕获电子邮件域。

在上面的例子中,我们使用了“groups”子模块中的 Capture 类。它允许我们在一场比赛中收集片段,这样你就不必做任何后处理来提取它们。

您经常需要的另一个子模块是操作符模块。它帮助您连接模式或选择一组选项中的任何一个。

这是上面同一个例子的一个稍微修改的版本。

在上面的例子中,我们已经将顶级域名限制为。我们已经使用了 operator 子模块中的“要么”类来构建这个模式。如你所见,它与 thanos@wierdland.err 不匹配,因为它的顶级域名是。呃,“不是”。' com '或' . org '

最后的想法

对于有经验的开发人员来说,定义正则表达式可能不是一项艰巨的任务。但即使对他们来说,阅读和理解别人创造的模式也是困难的。对于初学者来说,这两者都令人望而生畏。

此外,正则表达式是一个优秀的文本挖掘工具。任何开发人员或数据科学家几乎肯定会遇到正则表达式的使用。

如果您是 Python 程序员,PRegEx 涵盖了复杂的部分。

感谢阅读,朋友!在LinkedInTwitterMedium上跟我打招呼。

还不是中等会员?请使用此链接 成为会员 因为,不需要你额外付费,我为你引荐赚取一小笔佣金。

亚马逊业务分析师面试问题

原文:https://towardsdatascience.com/amazon-business-analyst-interview-questions-bcb57f7768c3

提高你编写 SQL 查询的技能,最大化你在亚马逊获得业务分析师工作的机会

作者在 Canva 上创建的图片

亚马逊每天运送超过一百万个包裹。即使是物流和商业运作效率的微小提高也能节省数百万美元。业务分析师可以分析硬数据,获得洞察力,并将他们的发现传达给公司的决策者。

在本文中,我们将讨论业务分析师如何融入亚马逊的组织结构。了解他们未来的责任可以帮助有抱负的商业分析师获得工作,并推进他们在亚马逊的职业生涯。

亚马逊业务分析师的职责

亚马逊的业务分析师利用数据来获得关于业务运营、物流、客户行为和习惯的见解,并利用它们来增加利润。有两种方法可以做到这一点:削减不必要的成本,增加收入。

和今天的其他科技企业一样,亚马逊收集了大量的用户数据。商业分析师通过寻找增加用户支出的方法来增加收入。另一方面,查看数据是发现和消除组织内低效率的最安全的方法之一。

这个角色的面试反映了商业分析师在亚马逊的职责。候选人必须表现出很强的商业知识和处理数据的能力。编写 SQL 查询是业务分析师的日常任务之一。

潜在的业务分析师必须回答从业务案例到实际 SQL 挑战的各种问题。SQL 问题通常涉及业务流程,消除低效并增加收入。

分析数据只是业务分析师工作的一部分。他们的主要职责之一是与公司的决策者交流见解。

亚马逊的面试官在寻找什么?

业务分析师在 SQL 方面做了很多工作,所以面试官希望他们对这种语言非常精通。与其他数据科学专业不同,业务分析师通常只需要 Python 的基础知识。这也有助于很好地理解 Excel。

专注于任务和检测数据模式的能力也能帮助你在工作中取得成功。这样的技能是很难衡量的,所以面试官通常会在回答问题的过程中观察应聘者。

有抱负的商业分析师也需要展现出深厚的商业知识。分析和汇总数据的能力很重要,但如果不能在业务环境中获得洞察力并做出正确的结论,这还不够。为了在这些面试中理解非技术性问题,请阅读一篇关于业务分析师面试问题的一般性文章。

亚马逊的文化专注于改善客户体验,让他们的网站成为网上购物最便捷的选择。同样关注改善客户体验可以帮助你在亚马逊找到一份工作。

解决亚马逊业务分析师面试问题的框架

1。理解问题

应对 SQL 挑战的最佳方式是仔细阅读描述。尤其是问题有很多概念和条件的情况下。新问题一开始可能会令人困惑。多读几遍,以保持正确的方向。

有些问题是明确而具体的。其他的则很宽泛,可以有不同的解释。当问题在任何方面都不清楚时,最好直接要求面试官具体说明。

问题通常会引入“唯一用户”之类的概念或“计算每月销售额差异”之类的操作。有些问题对这些概念和任务有具体的定义,而有些则没有。

通常,任务描述还会指定在返回数据之前如何安排数据,如何格式化输出,或者类似的事情。

2。分析数据

查看可用数据是解决 SQL 问题的第二重要步骤。如果幸运的话,可用数据将包含在一个表中,但情况并非总是如此。

有些问题有多个表,每个表可以有十几列。开发一种系统的方法来轻松理解可用的数据是很重要的。

第一步是查看问题描述中提到的值,并确定哪些表和列包含这些值。

查看每一列的数据类型会有所帮助。它可以帮助您决定是否需要对值进行转换或格式化。日期值经常是这种情况,它需要格式化或转换成文本值。

此外,数据中每个值的唯一性和限制也很重要。所以看实际数据是值得的。

3。规划你的方法

一旦你理解了问题和可用的数据,就该为找到解决方案制定合理的步骤了。

写下你的计划可以帮助你专注于解决挑战。你可以从最直接的方法开始,最终你可能会对最初的想法有所改进。在编写查询时,写下的逻辑步骤很容易遵循。

养成计划你的方法的习惯是非常有益的。这是一个在面试中展示你想法的机会。想出多种方法来处理一个问题可以证明你的 SQL 技能和作为数据科学家的总体敏锐度。

这些步骤应该描述从初始数据到最终答案你将执行什么操作(赋值、乘法、除法、格式化)。

您不必为每个步骤编写特定的代码,但是您可以编写伪代码,或者在编写实际代码时引用您将使用的 SQL 特性。

4。编写代码

将您的逻辑大纲转换成有效的 SQL 查询。从最基本的查询开始,逐渐增加复杂性。

例如,如果需要筛选某一列中的值,首先选择该列中的所有值。逐步添加过滤、格式化规则、聚合等条件。

添加这些复杂要素时,请检查查询的输出。这样,您将知道什么在起作用,以及额外的代码是否有预期的效果。您还可以检测代码中的问题,并轻松地修复它们。

亚马逊商业分析师面试问题#1:

找出没有订单的客户数量

作者在 Canva 上创建的图像

业务分析师的日常任务之一是了解客户行为。在这个角色中,您可能需要分析没有下订单的用户的行为。也许你需要理解他们为什么不下订单。或者亚马逊员工如何鼓励用户订购更多。

分析用户行为是亚马逊成功的秘诀之一。因此,我们从这个 SQL 挑战开始列出潜在的面试问题。

理解问题

这个问题看起来很简单,很清楚候选人需要做什么。在 StrataScratch 平台上标记为‘中等’难度。

在这个 Amazon business analyst 采访问题中,我们必须处理两个表——一个包含订单信息,另一个包含客户信息。

似乎合乎逻辑的是,为了找到答案,我们将不得不交叉检查两个表以得到答案。

事不宜迟,我们开始吧。

分析数据

如前所述,我们可以从两个表中提取所有必要的信息。首先,让我们看看订单表:

如果我们看一下实际的表格,现有的数据可能更容易理解:

让我们看一下每一栏:

  • id 列包含一个唯一的整数值来标识每个订单。
  • cust_id 列包含一个整数值,用于标识下订单的客户。由于问题要求查找没有订单的客户,我们将不得不查找不在该列中的用户。
    在现实生活中,一个客户可以下多个订单。 cust_id 值描述了许多订单与一个客户的关系,因此它们不一定是唯一的。
  • 这个问题没有提到订单的时间顺序,所以可以安全地忽略 order_date 列中的值。
  • 此外,该问题没有要求我们查找客户订购了什么,因此可以忽略 order_details 列。
  • 我们不必计算每个用户的订单量。也不需要跟踪 total_order_cost 列中的值。

客户表是拼图的另一个重要部分:

现在我们已经知道了列和列中包含的值的类型,让我们来看看表本身:

让我们看一下每一栏:

  • id 列包含一个唯一的整数值来标识每个客户。每行只描述一个客户,所以 id 的值是唯一的。

本质上,我们有一个所有用户的列表,不管他们是否下了订单。我们必须用订单表中的 cust_id 列交叉检查这个列表。

  • 该问题没有要求我们通过用户的全名来识别用户。因此,我们可以忽略列中的值。
  • 客户的位置不是兴趣点,所以我们可以忽略城市地址列中的值。
  • 我们也不需要包含在 phone_number 列中的用户联系信息。

首先,我们需要识别重要的列。在这种情况下,这将是客户表中的 id 列和订单表中的 cust_id 列。

常见的错误是从订单表中选择 id 列。我们需要 cust_id 列,因为在这个表中, id 列标识订单,而不是客户。

从数据中可以明显看出,每一笔订单都与一个客户相关联。有时一个客户有多个订单,但是没有一个订单没有 cust_id 值,该值标识谁下了订单。

规划你的方法

1。合并两个表格中的数据

在这种情况下,显而易见的选择是使用联接来合并两个表中的数据。我们只需要选择正确的连接类型。

为了执行连接,我们需要定义共享维度——客户的身份。在订单表中,客户标识符位于 cust_id 列。在客户表中,这些值存储在 id 列中。

该问题要求我们找出满足特定标准的客户数量。第一张表是订单,第二张是客户。我们使用 RIGHT OUTER JOIN 来确保第二个表中第一个表中没有的客户 id 不会被删除。

两个表之间的共享维度是客户的身份。在客户表中,这些值存储在 id 列中。在订单表中,它们存储在 cust_id 列中。

2。过滤记录以查找没有订单的用户

接下来,您需要找到特定客户 id 没有订单的情况。换句话说,在任何订单的 cust_id 列中都找不到该客户的 id

我们执行一个连接,但是不能保证客户表的 id 列中的每个值都会出现在订单表的 cust_id 列中。在没有匹配的情况下, cust_id 列将为空。

在这一阶段,我们有了包含相应订单数据的所有客户的列表。我们可以使用 WHERE 语句只保留没有订单的用户。

3。获取过滤结果中的行数

一旦我们有了所有没有 cust_id 值的记录,我们就差不多找到了答案。

最后一步是使用 COUNT() 聚合函数来获得被过滤的行数。这是没有订单的客户数量。

4。输出结果并重命名列

使用作为关键字给输出列一个更具描述性的标签是一个很好的实践。

写代码

  1. 合并两个表中的数据
SELECT *
FROM orders o
RIGHT OUTER JOIN customers c ON o.cust_id = c.id

2.筛选记录以查找没有订单的用户

SELECT *
FROM orders o
RIGHT OUTER JOIN customers c ON o.cust_id = c.id
WHERE o.cust_id IS NULL

3.获取筛选结果中的行数

SELECT COUNT(*)
FROM orders o
RIGHT OUTER JOIN customers c ON o.cust_id = c.id
WHERE o.cust_id IS NULL

4.输出结果并重命名该列。

SELECT COUNT(*) AS n_customers_without_orders
FROM orders o
RIGHT OUTER JOIN customers c ON o.cust_id = c.id
WHERE o.cust_id IS NULL

有经验的 SQL 开发人员有时会更改表的顺序,使用左连接而不是右外连接。

如果你准备好迎接挑战,尝试使用左连接来解决这个亚马逊商业分析师面试问题。

亚马逊商业分析师面试问题#2:寻找员工少于 5 人的部门

作者在 Canva 上创建的图像

业务分析师有时需要检测组织中的结构性低效。由于这个原因,亚马逊面试官可能会要求他们解决这个问题,候选人需要找到有一定数量工人的部门。

理解问题

要回答这个亚马逊业务分析师面试问题,候选人必须找到每个部门的工人数量,然后过滤列表以满足标准。

最终输出只包含两列——部门和相应的员工人数。

分析数据

解决这个问题所需的所有信息都包含在一个工人表中。这是桌子的模型:

我们有六列不同类型的值。除了注意值的类型之外,查看表中的实际数据也很有帮助:

让我们检查每一列中的值:

  • worker_id 列包含一个唯一的整数值来标识每个雇员。我们将使用这些值来计算每个部门的员工人数。
  • 我们不应该使用 first_namelast_name 列中的值来标识每个用户。可能有同名的雇员,所以最好使用唯一的 worker_id 值来标识他们。因此,可以忽略这些值。
  • 这个问题没有问我们关于补偿的问题。因此薪资列可以忽略。
  • 候选人不必按时间顺序跟踪每个员工的加入日期。我们不需要处理 joining_date 列中的值。
  • 最后,部门列包含每个员工的工作地点信息。该问题要求我们返回部门信息以及雇员人数。

工人表中最重要的列是工人 id部门。我们必须得到每个部门的雇员人数。获得雇员数量的最安全的方法是计算每个部门的唯一值。

规划你的方法

1。选择要使用的数据

我们需要编写一条语句来从 workers 表中选择部门worker_id 列。这将为我们提供员工名单及其工作地点。

我们没有把员工人数单列出来。我们需要用其他方法找到工人总数。

2。合计每个部门的员工人数

我们可以通过对 worker_id 值使用 count() 聚合函数来获得工人的数量。我们需要使用 GROUP BY 语句来输出每个部门的员工总数。

默认情况下, count() 聚合函数给列一个通用的 count 标签。我们可以使用作为关键字来给列一个描述性的名称。

在这个阶段,我们应该只有唯一的部门值和每个值的员工数量。

3。过滤出员工人数超过 5 人的部门

我们不能使用 WHERE 语句来过滤聚合函数的结果。SQL 开发人员使用 HAVING()子句丢弃不符合标准的组(部门)。在这种情况下,条件是每个部门必须有 5 名以上的员工。

写代码

  1. 选择要处理的数据
SELECT 
    department,
    worker_id
FROM worker

2.合计每个部门的员工人数

SELECT department,
       count(worker_id) AS num_of_workers
FROM worker
GROUP BY department

3.筛选出拥有超过 5 名员工的部门

SELECT department,
       count(worker_id) AS num_of_workers
FROM worker
GROUP BY department
HAVING count(worker_id) < 5

亚马逊商业分析师面试问题 3:薪水最高的员工

作者在 Canva 上创建的图像

要解决这个“中等”难度的问题,考生必须分析 worker 表中的数据。它包含像工人的工资和他们的部门的信息。

理解问题

这项任务相当简单——我们有关于工人、他们的工资和他们所属部门的信息。我们需要为每个部门找到收入最高的人。问题描述还指定需要返回哪些值。

你必须决定如何解决在同一个部门工作的多名员工分享最高工资的边缘情况。最有可能的是,你应该回复他们所有人的信息,但是你可能想和面试官澄清这一点。

分析数据

潜在的业务分析师需要编写 SQL 查询来处理 workers 表中的数据。这是问题 2 中的表格。但是,在这种情况下,我们将使用不同的值来获得最终答案:

让我们再次看看可用数据的具体例子:

让我们看一下表格中的列:

  • 对于这个亚马逊业务分析师面试问题,我们不需要跟踪 worker_id 列中的值。我们只需在first _ name列中找到工资和产值最高的行。
  • 我们的最终输出包括我们确定的每个部门中工资最高的个人的 first_name 值。因此,我们需要从该列中选择值。
  • 我们根本不需要处理姓氏的值。因此,可以忽略该列。
  • 要解决这个问题,候选人需要找到每个部门薪资最高的员工。因此,我们需要根据员工的工作地点定义不同的员工群体,并为每个群体找到最高的工资
  • 每个员工加入的时间并不重要,因此可以忽略加入日期列。
  • 我们必须在每个部门找到最高的薪水。因此,我们必须在部门列中寻找具有相同值的多个工人,并输出工资最高的一个。

为了回答这个亚马逊商业分析师的采访问题,我们需要使用薪水部门列。最后,输出还需要包括工人各自的名字值。

规划你的方法

1。找出每个部门员工的最高工资

在查看了现有的数据之后,很明显有许多员工被分配到仅仅几个部门。我们需要根据员工工作的部门对他们进行分类,并找出每组中收入最高的人。

为此,我们最有可能使用 MAX()聚合函数以及 GROUP BY 语句将它们放在不同的组中。

2。组合主表和临时表中的数据

为了访问所有列中的值,我们需要创建一个包含最高收入者的临时表。然后,我们必须将它与主表连接起来,以访问两个表中的所有值。

我们可以执行一个内部连接来剔除那些不是收入最高的员工的记录。为此,我们需要使用 on 语句指定共享维度。内部联接将完成剩下的工作。

3。输出三列中的值

我们将临时表与主表连接起来的主要原因是为了能够访问主表的所有列。

该问题要求我们找出每个部门中工资最高的人,并在他们的工资部门名字列中输出值。

编写代码

  1. 找出每个部门员工的最高工资
SELECT 
      max(salary) AS TotalSalary,
      department 
FROM worker 
GROUP BY department

2.合并两个表中的数据

SELECT *
FROM
  (SELECT max(salary) AS TotalSalary,
          department
   FROM worker
   GROUP BY department) AS TempNew
INNER JOIN worker t ON TempNew.department = t.department
AND TempNew.TotalSalary = t.salary

3.输出三列中的值

SELECT t.department,
       t.first_name,
       t.salary
FROM
  (SELECT max(salary) AS TotalSalary,
          department
   FROM worker
   GROUP BY department) AS TempNew
INNER JOIN worker t ON TempNew.department = t.department
AND TempNew.TotalSalary = t.salary

摘要

业务分析师的角色是独特的,因为它需要技术和业务专长,两者同等重要。为了得到一份工作,候选人经常会面临 SQL 挑战。他们编写 SQL 查询的能力将影响他们成功的机会。

在这篇文章中,我们讨论了亚马逊业务分析师职位的三个面试问题。您可以在 StrataScratch 上探索其他 SQL 问题,在这个平台上,任何级别的数据科学家都可以练习和提高他们的 SQL 技能。

最初发表于【https://www.stratascratch.com】

亚马逊理解自定义分类

原文:https://towardsdatascience.com/amazon-comprehend-custom-classification-d91d68d169cc

利用 AutoML 定制数据集

图片来自苏伦兰议员Unsplash

亚马逊理解是庞大的 AWS AI/ML 堆栈的一部分。这是 AWS 提供的首要自然语言处理(NLP)服务,它提供了很大的灵活性。在过去,我解释了如何使用理解 API 来自动化情感分析和实体识别

然而,对于大多数用例,您会有一个特定的数据集来训练您的模型。对于一个自定义分类的例子,我们可以使用 intensive 的 AutoML 功能,并用我们自己的数据集对它们进行微调。在本文中,我们将采用一个示例垃圾邮件数据集,并使用 intensive API 来启动一个自定义分类训练任务。利用这一点,我们可以从我们训练过的理解模型中创建一个端点,它可以执行实时推理。****

注意:对于刚接触 AWS 的人来说,如果你想继续学习,请确保在下面的 链接 中注册账户。培训和部署过程中会产生费用,尤其是在您的终端保持运行的情况下。本文还假设您对 AWS 和使用一个 AWS SDK 有基本的了解。

设置

首先,我们将从以下 Kaggle 链接中提取样本数据集。您可以在任何环境中使用这个数据集,我将使用一个经典的 SageMaker 笔记本实例 (ml.c5.xlarge)来获得进一步的计算能力。您也可以在本地的 Anaconda 环境中工作,或者使用任何您喜欢的环境。

首先,我们下载数据集,并使用 Pandas 获得一些初步信息。对于训练数据,我们可以有两种类型的分类问题:多类多标签模式。对于这个例子,我们有一个多类类型的问题,因为我们有两个类:垃圾邮件和火腿。

读取数据集

垃圾邮件数据集(作者截图)

为了使用 understand,我们将使用 Boto3 Python SDK ,你也可以使用 AWS SDKs 支持的任何语言。

我们需要注意的一些日常工作是上传数据集到 S3 并且确保理解权限到 S3 进行模型训练。首先,我们创建一个 S3 存储桶,确保它有一个唯一的名称,然后我们可以将数据集上传到这个存储桶。

创建存储桶并上传文件

接下来转到 IAM,确保为 Comprehend 创建一个附加了comprehension dataaccessrolepicy的角色。

IAM 角色理解(作者截图)

请确保掌握该角色的 arn,因为稍后您将需要为模型培训提供它。

模特培训

对于模型训练,我们首先需要创建一个文档分类器,它将为训练指定我们的训练数据位置。这将提交并启动一个培训作业。

创建文档分类器

随着培训作业启动,我们可以监控其状态,直到它成功完成。

跟踪培训状态

我们还可以在控制台中监控并看到该培训作业成功执行。此步骤可能需要 30-60 分钟,具体取决于数据集的大小、类/标注的数量以及其他因素。

培训工作(作者截图)

在这里,我们还可以看到跟踪的不同指标,如准确性和 F1 得分。使用这个经过训练的文档分类器,我们可以获取作业 arn,然后使用它直接部署到实时端点或进行批量推理。在这个例子中,我们将查看一个实时端点,但是可以在下面的链接中查看关于异步批处理作业的文档。

端点创建和调用

为了创建端点,我们可以继续使用理解 API。我们将首先获取经过训练的文档分类器 arn,并将其输入到一个 create_endpoint 调用中,我们可以对其进行监控,直到创建端点。这一步大约需要 10-15 分钟。

端点创建

这里需要注意的一个因素是参数杀菌单位,这是您可以调节生产量的旋钮。每个推理单元代表每秒 100 个字符的吞吐量,所以请确保选择一个足以满足您的用例的值。一旦端点处于状态,我们就可以获取一个样本数据点,并将其提供给端点进行推断。

来自数据集的样本数据点(作者截图)

使用 classify_document 调用,我们可以输入这个样本点进行推断。

端点调用

推论(作者截图)

如果不想让端点启动并运行,请确保使用以下调用删除端点。

删除端点

其他资源和结论

https://github.com/RamVegiraju/aws-comprehend-custom-classifier

整篇文章的代码请访问上面的链接。关于 Amazon understand 的更多示例/资源,请查看如何在没有任何代码的情况下直接在 AWS 控制台上重复整个过程。为了进一步理解代码示例,请查看这个 AWS 示例库

我希望这篇文章很好地介绍了 AWS 提供的核心 AI/ML 服务之一。在以后的文章中,我们将探索如何将这个服务与 AWS ML 堆栈中的其他服务集成在一起。

如果你喜欢这篇文章,请在LinkedIn上与我联系,并订阅我的媒体 简讯 。如果你是新手,使用我的 会员推荐 报名。

亚马逊数据科学面试问题

原文:https://towardsdatascience.com/amazon-data-science-interview-questions-bb217eb040f6

准备一次面试,以获得亚马逊的数据科学家职位

作者在 Canva 上创建的图片

亚马逊是电子商务巨头,也是世界上最有价值的品牌之一。数据收集和分析是亚马逊商业模式的关键。这家电子商务巨头使用数据来个性化用户体验,创建和设计产品,甚至提高其业务运营的效率。

考虑到数据对其商业模式的重要性,亚马逊一直在寻找有前途的数据科学家加入其行列。

在面试过程中,你很可能不得不写一个 SQL 查询来解决一个业务问题。面试官通过观察你处理问题的方式来评估你的分析、理解能力以及关注细节的能力。

在本文中,我们将解决一个亚马逊数据科学面试问题,以展示如何以正确的方式处理这些问题。

亚马逊数据科学访谈中测试的基础到中级概念

作者在 Canva 创建的图像

亚马逊每天都要处理海量数据,所以面试官都在寻找能写出高效代码的候选人。对 SQL 的全面了解也很重要。

除了在面试中展示这些技能,你还需要它们在日常工作中脱颖而出。一旦你得到了它,你需要拿出好的成绩来脱颖而出,推进你的数据科学事业。

编写高效的 SQL 查询归结于使用该语言提供的最合适的工具。

让我们来看看亚马逊数据科学采访中测试的一些最重要的概念:

自连接

连接是 SQL 的一个重要特性,具有广泛的潜在应用。关于自联接的知识将使您能够处理单个表的多个引用,这对于解决本指南后面部分概述的问题是必要的。

精通自连接包括知道如何使用别名两次甚至三次引用同一个表。一个好的候选人还知道如何根据上下文给出别名来提高代码的可读性。

JOIN 和 ON 语句是并行的,所以知道如何编写后者也很重要。ON 语句描述两个表之间的关系,或者在自联接的情况下,描述同一表的两个引用之间的关系。

您可以使用 ON 语句来过滤记录,以满足特定的条件。查看“ SQL 连接面试问题 ”中使用连接的一些例子。了解如何设置条件对于获得预期结果至关重要。设置条件通常包括检查等式或进行比较。

间隔

许多 SQL 问题都与日期和时间有关,因此您需要精通日期格式和对日期值执行算术运算。INTERVAL 函数允许您将日期值增加一年或十天。

在 SQL 中,间隔值对于对日期和时间值执行算术运算至关重要。理想的候选人应该能够使用 interval 关键字创建一个基本的间隔值,以指定值和时间单位,如“10 天”。知道间隔值不区分大小写也是有好处的。

一般来说,考生至少应该具备加减日期值的基本知识,知道这些运算之后日期值会发生什么变化。

文章“ 基于 SQL 场景的面试问题 ”大体描述了日期-时间值的区间函数和算术运算。

SQL 逻辑运算符

要设置复杂的条件,必须熟练掌握逻辑运算符。它们允许您找到 SQL 问题的答案,其中您必须找到满足特定标准的记录。精通逻辑运算符意味着能够将它们链接起来以获得想要的结果。

在本指南的后面部分,我们将解决一个要求您在逻辑运算符之间使用 and 和 AND 的问题。然而,这只是冰山一角,因为 SQL 中还有更多类型的逻辑运算符。

为了最大化你获得数据科学工作的机会,学习所有不同类型的逻辑运算符,并理解在 SQL 中使用逻辑运算符的可能性。

SQL 中的数据类型

数据类型是 SQL 中最重要的概念之一。所有参加亚马逊数据科学面试的候选人都应该对使用每种数据类型的可能性和每种数据类型可能使用的功能有所了解。

所有有抱负的数据科学家都应该能够找出 SQL 中值的数据类型。除此之外,他们应该能够解释人类读取值的方式与计算机读取值的方式之间的差异。例如,是什么让 SQL 将一些数值视为数字,而将其他数值视为文本,即使它们看起来像数字?

了解使用每种数据类型的规则会很有帮助。例如,知道数值不能包含空格或逗号这一事实可以帮助您避免错误。

铸造值

数据科学面试问题旨在具有挑战性。通常,您需要将一种值类型转换为另一种值类型来使用它。

了解用于转换值的函数及其语法是非常重要的。有些函数有简写语法,这对代码的可读性很有用。我们的问题的正确解决方案之一是使用双冒号语法对日期值执行算术运算。

亚马逊数据科学面试问题演练

让我们来看一个简单的问题,亚马逊面试官用这个问题来测试应聘者的 SQL 熟练程度。

寻找用户购买

这个问题被标记为“中等”难度,并给候选人一个相当简单的任务:返回活跃用户的列表。它还给出了什么是活跃用户的定义。在着手解决问题之前,最好多读几遍这个问题。

截图来自 StrataScratch

问题链接:https://platform . stratascratch . com/coding/10322-finding-user-purchases

要回答该问题,申请者必须找到满足指定标准的记录。问题最具挑战性的部分是在问题描述中设置一个反映标准的条件。

第一步应该是分析成为“活跃用户”意味着什么,并将其转化为 SQL 代码。然后你应该决定你解决问题的方法。你应该以效率为目标,用最少的代码找到解决方案。

可用数据集

数据假设

试图分析这个问题的可用数据的候选人有一个简单的任务:只有一个包含五列的表。

让我们检查唯一可用的 amazon_transactions 表的每一列:

  • 我们使用订单本身的 id 值来确保我们比较两个单独的订单来确定它们之间的时间间隔,而不是将一个订单与其自身进行比较。
  • 为了识别下订单的用户,我们必须处理来自 user_id 列的值。
  • 产品种类不是重要因素,所以栏可以忽略。
  • 两个订单之间的时间间隔是一个重要的因素,所以我们必须使用来自 created_at 列的值。
  • 收入列并不重要,因为这个问题并没有以任何方式提到或暗示需要计算销售量。

尽管如此,可能还有一些细节需要注意。例如,查看可用数据,您会注意到 created_at 列只包含日期值,没有时间。在这种情况下,当一些用户在同一天下了两个订单时,您可能会感到困惑。

截图来自 StrataScratch

在这种情况下,很难确定哪个订单应该先来,哪个应该后来。要找到解决这一困境的办法,你需要消化问题的提法。

活动用户的定义对订单顺序没有任何重要性。只要它们发生在任何给定的 7 天窗口内,哪个订单先出现并不重要。重要的是两者之间的时间间隔在 0 到 7 天之间。

当对数据中的异常情况感到困惑时,你应该做的第一件事是仔细阅读问题的措辞。如果问题包含帮助你做出决定的关键词,你应该继续你的方法。

有些情况下,你必须善于沟通,并与面试官反复核对,以确保你没有偏离正确的轨道。尽管如此,你不应该走极端;展现独立性和分析思维能力很重要。

解决方案逻辑

在编写任何 SQL 代码之前,候选人应该理解这个问题,并从逻辑上阐明她的方法。仔细阅读每一句话,因为有时只有一个词可以改变任务的意义。通过问这个问题,面试官不仅测试你的 SQL 技能,也测试你倾听和理解任务的能力。

在 SQL 中,连接对于处理两行之间的时差很有用。考虑到我们只有一个表,我们可能需要使用一个自连接。您的整体方法应该包括以下步骤:

  1. 创建对该表的两个引用,并用自联接将它们连接起来
  2. 使用 ON 语句设置条件,以查找符合条件的行
  3. 处理边缘情况

这个问题要求我们返回满足条件(作为一个活动用户)的各个行的值。在考虑这个问题的解决方案时,关注活跃用户的定义是非常重要的。

在 7 天窗口内的任何两次购买都足以将其中一次视为“活跃用户”。为了找到符合标准的订单,我们将交叉检查所有订单,以找到符合标准的订单对。

这个亚马逊数据科学面试问题最具挑战性的部分是设置一系列条件。

第一个条件是订单必须由同一个人下。换句话说,我们应该遍历表的两个引用,找到具有相同 user_id 值的订单记录。

下一步是比较订单本身的 id 。这是必要的,以避免当我们将订单与其在另一个表中的副本进行比较,并将其作为“活动用户”的另一个实例时,仅仅因为订单的两个副本具有相同的日期。

最后,在比较两个订单时,我们必须检查其中一个订单是在同一天发生的,还是在第一个订单之后的七天内发生的。为此,我们可以使用简单的比较操作符、=。

为了比较两个订单的日期,我们必须访问它们的 created_at 值。假设我们有日期值 x 和 y。在 SQL 中,检查 x 是否发生在 y 之后(换句话说,比 y 更近)。

为了检查某个日期值是否比另一个日期值旧,我们使用小于(< ) sign. The condition x < y 来检查 x 是否出现在 y 之前。

我们将使用等号(=)和大于号(>)运算符来检查第二个订单是否发生在第一个订单的 created_at 值的同一天或之后。但是我们如何检查第二个订单是否发生在第一个订单之后不超过 7 天?

假设 x 是当前日期。您可以使用 INTERVAL 函数将 7 天添加到当前日期,然后使用它进行比较。

我们将不得不使用 AND 逻辑操作符将上面列出的多个条件连接起来。

请记住,我们必须找到活动用户的 user_id 值。这很简单,因为这些记录已经包含了 user_id 列。我们只需找到满足这些要求的两个订单记录,并从其中一个引用中输出 user_id 值。

最后,我们还必须处理用户不止一次激活的情况。问题描述不要求我们找出用户变得活跃的次数,所以我们不需要跟踪用户变得活跃的多个实例。我们可以使用 DISTINCT 关键字来确保最终的列表只包含唯一的用户 id。

解决方法

步骤 1:创建对表的两个引用

因为我们试图找到在某段时间内发生的两个订单,所以我们需要对同一个表进行两次引用。为了简单起见,我们称它们为 a 和 b。

SELECT *
FROM amazon_transactions a
JOIN amazon_transactions b

步骤 2:设置条件

现在我们到了困难的部分。我们必须建立条件以确保:

  1. 订单是由同一用户下的,
  2. 我们不是在比较订单本身,
  3. 第二个参考的订单创建时间晚于第一个参考的订单,但不晚于 7 天。

我们将使用 AND 逻辑运算符,因此 SQL 只返回满足所有三个条件的行。

我们将使用 INTERVAL 函数的简单语法来检查两个订单是否在 7 天的窗口内。

SELECT *
FROM amazon_transactions a
JOIN amazon_transactions b ON a.user_id = b.user_id
AND a.id != b.id
AND b.created_at >= a.created_at
AND b.created_at <= a.created_at + INTERVAL '7 day'

现在,如果我们运行代码并查看输出,您将看到所有满足条件的行:

截图来自 StrataScratch

步骤 3:输出 user_id 值,处理边缘情况

这个问题要求我们输出活动用户的 user_id 值,而不是整行。我们必须修改我们的 SELECT 语句,从一个引用中返回 user_id 值。

一些用户订购很多,所以他们可能会多次满足我们成为“活跃用户”的条件。我们可以使用 DISTINCT 语句只显示每个用户一次。

SELECT DISTINCT a.user_id
FROM amazon_transactions a
JOIN amazon_transactions b ON a.user_id = b.user_id
AND a.id != b.id
AND b.created_at >= a.created_at
AND b.created_at <= a.created_at + INTERVAL '7 day'

运行这段代码,您将看到活动用户的唯一值的列表。

截图来自 StrataScratch

另一个正确的解决方案

有多种方法可以设置条件来检查 7 天内是否发生了两个订单。这种方法与 StrataScratch 平台略有不同:

SELECT DISTINCT(a1.user_id)
FROM amazon_transactions a1
JOIN amazon_transactions a2 ON a1.user_id=a2.user_id
AND a1.id <> a2.id
AND a2.created_at::date-a1.created_at::date BETWEEN 0 AND 7
ORDER BY a1.user_id

在这种情况下,我们使用 BETWEEN and 和逻辑运算符来检查两个日期之间的时间差是否在 0 到 7 天之间。

最后的话

在本文中,我们浏览了在亚马逊面试中向数据科学家候选人提出的一个有趣的问题。

通过研究问题为数据科学工作面试做准备是一个良好的开端,但真正做好准备取决于对所有 SQL 概念的透彻理解,以便提出高效的解决方案。

查看我们的其他帖子,如“ 亚马逊 SQL 面试问题 ”和“ 亚马逊数据分析师面试问题 ”来提高你的 SQL 技能,最大限度地增加你在亚马逊找到工作的机会。

Ameca 证明超现实机器人不会太久

原文:https://towardsdatascience.com/ameca-is-proof-hyper-realistic-robots-wont-be-long-916241e1d344

它的表现力会让你起鸡皮疙瘩。

Ameca — 工程艺术有限公司

阿梅卡不是最聪明的。它不是最有帮助或最有技巧的。它不能进行引人入胜的对话,甚至不能移动。尽管看起来毫不相干,但这个英俊的机器人最近在网上疯传——因为它看起来像你和我。

关于人工智能和机器人的对话通常围绕着智能、灵巧、语言和其他容易识别的人类特征。我们经常忘记研究外貌的分支(有时被称为人类机器人学)。建造模仿人类表情、手势和面部情绪的机器人是其核心任务之一。

大多数人工智能研究都专注于构建超级智能的无实体代理(想想 GPT-3、AlphaZero 和机器学习术语下的任何其他虚拟模型)。智能是人类独特性的缩影——大概也是灌输给机器人的最有用的特征。然而,人工智能系统并不是孤立存在的。无论我们是否意识到,人工智能已经根植于我们生活的各个方面,最终,我们可能会面对一个现实,即我们与这些人工智能控制的物理机器人进行交互。

考虑我们是否希望这些机器人看起来像我们,这是人工智能未来的一个根本问题。提高机器人与人交流、联系和互动的能力,对于缩小它们与我们之间的差距至关重要。

CES 2022 中展示的 Ameca 是一个由英国公司 Engineered Arts 制造的人形超现实机器人,它于 2021 年末诞生,并随着这个视频而走红:

Ameca 的“觉醒”——工程艺术频道( Youtube

Ameca 睁开眼睛,惊讶地眨着眼睛,朝各个方向看,寻找它醒来的来源,这种方式非常有关联——当有人突然叫醒我们时,我们正是这样做的。机器人看着它的手臂检查和测试它,就好像它刚刚变得有生命一样。敬畏、好奇和惊奇是复杂的人类情感,我们看到 Ameca 表现得很好。感觉很熟悉。

尽管如此,它显然不是人类:灰色半透明的皮肤,机器人的四肢,由金属和塑料制成……工程艺术运营总监摩根·罗告诉 CNET 他们故意让阿梅卡不像人类,这样它就不会那么可怕。该公司还有另一系列机器人(梅斯麦),它们被设计成尽可能的人形。那些正好落在恐怖的山谷里,这使它们变得异常可怕。

阿梅卡不仅仅像人类。它感觉很友好——你会让你的孩子玩它。

也许关键不是让机器人 100%像人,而是让它们在某些方面(如面部动作)非常像人,同时保持其他特征是人工的(如肤色)。Ameca 准确地实现了平衡的最佳点,使其成为与卓越的非语言交流互动的完美之选。像这样的机器人可以担任客户支持职位,或者帮助自闭症儿童,还有许多其他用途。

不过,Ameca 现在属于一个陈列柜,这可能是我们如此喜欢它的一个很好的部分原因。如果这些人在街上闲逛,我会有不同的想法。梅斯麦版本会更糟。想象一下,晚上 10 点,在家里一条黑暗偏僻的街道上,你会遇到这样的人。Ameca 是我们现在拥有的最好的产品,但它还远远没有准备好进行大规模生产。

然而,从长远来看,这些机器人可能会成为跨越人类不接受的深渊的桥梁。我之前曾说过我们应该让人工智能和机器人更像我们。如果我们最终不得不与机器人生活在一起,我们最好让它们不仅让我们的大脑感到愉快,也让我们的眼睛感到愉快。

但是我们先不要担心这个。我们离这些情景还很远。阿梅卡看起来像人类,但却极其愚蠢。与 AlphaZero 或 GPT-3 不同,它不是为了显示智力而建造的——无论是狭义的还是广义的。它的语言功能非常有限,这与几年前成为第一个拥有合法公民身份的人形机器人索菲亚形成了鲜明对比。而且它不能动。你不会发现 Ameca 像波士顿动力的 Atlas 一样跳来跳去。

如果我们将 Ameca 的外观与 GPT-3 的语言能力、AlphaZero 的学习过程、Atlas 的身体控制以及自动驾驶汽车的视觉意识结合起来,那么我们将更接近索尼的 I,机器人。正是在所有这些人工智能和机器人技术的交汇点上,我们将发现未来的机器人。类似人类的面部表情和手势;平滑无界的运动;顶尖的语言能力,以及许多其他我们还没有接近实现的特性,甚至不是独立的例子。

Ameca 离实现目标还很远,工程艺术的道路也不是没有障碍的。

一个潜在的问题是众所周知的拟人化偏见。人们倾向于将完整的人性归于展示了足够多人类特征的机器人。例如,如果我们发现机器人的面部表情和我们的完全一样,人们会认为它也有产生这些表情的精神状态。语言模型也是一样。一篇写得好的、连贯的、有说服力的文章可以让读者推测出文字背后的意图。

艾的散文没有意图,阿梅卡的微笑背后也没有心理状态。

我们的大脑从我们的期望中产生预测。如果我们做出与现实相差甚远的假设,假设一块有着弯曲的嘴唇和闪亮的眼睛的金属有情感和感觉,这可能会产生重要的后果。我们最终可能会将关键任务,如照顾孩子或与孩子玩耍,委托给机器人(这是一个极端的例子,但不会发生什么)。

关于人形机器人的未来,最重要的政策是大力教育人们什么能做,什么不能做。这不足以消除不必要的后果,但这是我们这些从事先锋人工智能教育的人应该做的最起码的事情。

不过,现在,我们可以欣赏 Ameca 的怪癖,并在社交媒体上与他人分享视频。

如果你喜欢这篇文章,可以考虑订阅我的免费周报 明日之心 !每周都有关于人工智能和技术的新闻、研究和见解!

您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!😃

AMTA 2022 亮点

原文:https://towardsdatascience.com/amta-2022-highlights-4802372b53ed

面向用户和研究人员的机器翻译技术现状

佛罗里达州奥兰多——照片由 Unsplash 上的科迪董事会拍摄

每两年,机器翻译(MT)社区在国际机器翻译协会(IAMT)北美分会 AMTA 会议上会面并交流该领域的最新进展。对于参与机器翻译的人来说,这总是一个非常有趣的事件,在这里,研究人员、用户、行业甚至政府组织都可以发表研究论文或展示他们的工作。2022 年版的 AMTA 于 9 月在佛罗里达州的奥兰多举行。

在这篇文章中,我强调并总结了我认为最有创意和最有趣的论文。我从用户(见会议记录)和研究(见会议记录)轨道中挑选论文。

选择最佳机器翻译模型——人类评价的方法论

作者斯捷潘·科罗塔耶夫(effect ff)和安德烈·里亚布契科夫(effect ff)

本文中的关键假设是,如果两个或多个相同长度的翻译文本是从不同但相同的源文档翻译而来的,那么它们在后期编辑时应该花费大致相同的精力。

在以下情况下,两份文件被视为“同类”:

  • 它们属于相同的领域和类型。
  • 它们具有相似的复杂性和/或可读性分数,这些分数是用一些选定的度量标准计算出来的。
  • 它们在专业术语的密度上很接近。
  • 它们应该只有很少的重叠专业术语。

他们将后期编辑的“努力”定义为:

  • 花费的时间
  • 编辑距离
  • 已更改段的百分比

然后,如果我们翻译了同类文档,并且其中一个翻译需要较少的后期编辑工作,我们可以得出结论,该翻译是由更好的机器翻译系统生成的。

这是非常直观的,作者证明了他们的假设在英语到俄语的翻译任务中是正确的。

他们也承认自己工作的局限性,例如,“花费的时间”从来不是一个非常可靠的衡量标准,因为后期编辑自己负责衡量。

你所需要的是来源!基于源的神经机器翻译质量评估研究

乔恩·坎布拉·几内亚(Welocalize)和玛拉·努恩齐亚蒂尼(Welocalize)

这是会议用户追踪的另一个原创作品。它提出了一种不同的方法来评估 QE 山的质量(QE ),即自动评估翻译质量的过程,而不使用任何人工翻译。你可以说这是一个无人监督的评估任务。这是一个研究得非常透彻的问题,但所提出的方法的独创性在于,它可以在翻译完成之前执行 QE!

事实上,这种方法仅利用源文本进行翻译,并利用训练数据来训练机器翻译系统。这里的假设是,如果我们知道机器翻译系统使用的训练数据,我们应该能够猜测它将翻译给定的源文本有多好。

在实践中,该论文表明,这种方法与最先进的 QE 度量标准相关联,如彗星-QE 。当然,标准的 QE 度量仍然更加准确,但是所提出的方法具有几个优点,使得它在各种情况下都是有用的。例如,它可以用来评估翻译一个给定的源文本的难度,甚至在编辑开始之前就优先考虑和更好地计划后期编辑,等等。

这项工作的主要限制之一是,我们实际上需要知道机器翻译系统的训练数据。它不适用于黑盒 MT 系统。

用相似的翻译提升神经机器翻译

作者徐(希斯特兰,利姆西),约瑟夫克雷戈(希斯特兰),让塞内拉特(希斯特兰)

神经机器翻译需要大量的训练数据,即人类在目标领域和语言对中创建的翻译。对于大多数用例,我们没有足够的训练数据来训练目标领域中精确的机器翻译系统。

缓解训练数据缺乏的一种方法是利用“翻译记忆”:以前由人类在相同领域和语言对中产生的翻译。然后,在翻译一个句子时,我们可以检查内存中是否已经有这个句子的翻译。这是最理想的情况,但大多数时候我们翻译的新文本并不在记忆中。在这种情况下,我们可以利用“模糊匹配”模糊匹配被定义为与翻译记忆库中的另一个句子相似的新句子。

尽管模糊匹配可能与我们要翻译的实际句子有很大的不同,但本文提出了几种利用模糊匹配来提高翻译质量的方法。他们展示了如何向神经模型提供模糊匹配的源端和目标端的信息。这在下表中针对英语到法语的翻译进行了说明:

图 2 的截图由徐(Systran,LIMSI)、Josep Crego (Systran)和 Jean Senellart (Systran) 拍摄。

他们提出了 3 种利用模糊匹配的方法。FM+方法是提供最佳结果的方法。它保持整个模糊匹配不变,但增加了标签:

  • S 为源词;
  • R 为不相关的目标词;
  • 以及用于相关目标单词的 T

我发现 FM*表现出奇的低。与我在 NAACL 2019 论文中提出的有一些相似之处:神经机器翻译的部分翻译的无监督提取。在我的工作中,我称之为“部分翻译”,而不是“模糊匹配”,我屏蔽(或丢弃)了未翻译的标记。这里,Systran 用标记“∨”来屏蔽它们。我不知道他们为什么选择这个标记,这个标记也被用来分隔源句子和目标句子。我预计模型会对该标记是宣布目标句子还是屏蔽不相关的文本感到困惑。

FM+的表现看起来让人印象深刻,即使只拿 BLEU 评价过。这项工作的一部分是开源的:https://github.com/SYSTRAN/fuzzy-match.

神经机器翻译中数据过滤方法的比较

作者:弗雷德·贝恩(Transperfect),西莉亚·索勒·乌格特(Transperfect),维克特·斯特里比泽(Transperfect),安娜·扎雷茨卡娅(Transperfect)

在噪声数据上训练的机器翻译系统可能表现不佳。过滤训练数据以去除最嘈杂的句子对几乎总是必要的。本文对不同的现有滤波方法进行了评估,这些方法可识别由 Khayrallah 和 Koehn (2018) 定义的噪声类型:

  • MUSE :从源句子和目标句子的 MUSE 单词嵌入中计算句子嵌入,然后用余弦相似度对句子对评分。
  • Marian Scorer :用神经机器翻译模型给句子对打分。
  • XLM-R :计算源句子和目标句子的多语言句子嵌入,然后用余弦相似度对句子对评分。
  • LASER :获取 LASER 给出的多语言句子嵌入,然后对句子对进行余弦相似度评分。
  • 彗星:使用 wmt-20-qe-da 模型进行质量估计,对句子对进行评分。

他们发现玛丽安记分员是过滤句子的最佳工具。这对我来说并不奇怪,因为这个计分器是唯一一个利用根据他们自己的数据训练的模型的工具。尽管如此,这篇论文非常有说服力,这要归功于一份远高于机器翻译研究标准的评估:

  • 他们使用不同的自动度量标准:BLEU、TER 和 chrF。
  • 由于使用了 SacreBLEU,计算出的分数可以在将来的工作中引用。
  • 他们进行了统计显著性测试。
  • 他们用 MQM 框架进行了人体评估。

按照我在我的 ACL 2021 论文中提出的量表,他们的评估将得到最高 4 分的元评估分数。

在神经机器翻译中,对词汇外单词进行字节对编码有多有效?

作者:阿里·阿拉比(阿姆斯特丹大学),克里斯托夫·蒙兹(阿姆斯特丹大学),弗拉德·尼库莱(阿姆斯特丹大学)

本文介绍了一项逾期的研究,关于 BPE 如何缓解翻译训练数据中不存在的单词的困难(OOV)。

从技术上讲,当使用 BPE 时,不存在 OOV,因为单词被分解成更小的 BPE 记号,这些记号都在 MT 模型词汇表中。尽管如此,形成 OOV 单词的 BPE 记号的序列在训练数据中仍然不可见。

在各种有趣的发现中,我首先认为,由于使用了 BPE,某些类型的 OOV 词翻译得更好,尤其是命名实体。对于其他类型的 OOV,BPE 也有帮助,但不显著。此外,在他们试图更好地理解 BPE 是如何帮助的过程中,作者证明了 OOV 单词的翻译质量与他们获得的转化者的注意力密切相关。

本文强调了 BLEU 在评估翻译质量方面的另一个弱点。正如 Guillou 等人(2018)在 WMT18 所展示的,BLEU 对局部误差几乎不敏感。因此,当一个 OOV 单词没有被正确翻译并且对翻译的剩余部分没有任何影响时,它将仅对 BLEU 分数有非常小的影响。代替 BLEU,作者推荐人工评估来准确地评估 OOV 单词的翻译。

跨语言对机器翻译的一致人工评估

作者:(META AI),Cynthia Gao (META AI),Janice Lam (META AI),Francisco Guzman (META AI),Mona Diab (META AI),Philipp Koehn (META AI,约翰·霍普金斯大学)

我强调这篇论文是因为它提出了一个非常全面和简单的人类评估框架。它设计得如此之好,以至于它可以包含在一个页面中,并带有示例,如下所示:

图 1 截图作者:(META AI),辛西娅·高(META AI),贾尼斯·林(Janice Lam)(META AI),弗朗西斯科·古兹曼(META AI),莫娜·迪亚卜(META AI),菲利普·科恩(META AI,约翰·霍普金斯大学)。

更具体地说,用该框架获得的评分(表示为 XSTS)集中在获得对 MT 系统排序有意义的分数上。该框架已经在大量的语言对上进行了评估。

结论

我只把最原创/最有趣的论文突出给我看。我鼓励你们仔细看看会议的进程。还要注意的是,有几个专门讨论非常特殊的机器翻译主题的研讨会,我在本文中根本没有涉及到。

如果你想支持这项工作,跟我上媒

人工智能项目的敏捷框架——开发、质量保证、部署和维护

原文:https://towardsdatascience.com/an-agile-framework-for-ai-projects-development-cbe115ba86a2

多年来,软件开发社区已经达成了广泛的共识,即软件应该以迭代生命周期进行开发——根据用户反馈对产品进行持续的改进。

今天,许多人工智能团队在采用迭代生命周期之前,面临着与传统软件团队相似的困难。虽然人工智能并没有改变构建符合产品市场的优秀产品的本质,但它确实有细微差别和复杂性,使得应用现有的方法很困难。这一系列文章的目标是提出一个迭代生命周期框架,它适应于以人工智能为中心的软件

这篇文章是独立的,不需要阅读系列中的前几篇文章。在之前的帖子中,我们已经讨论了(1) 这个框架的动机,并提供了一个概述,为很好地执行它奠定了基础;(2) 框架的需求阶段。在这篇文章中,我们将讨论框架的开发和部署阶段。

发展

作者图片

在这个阶段,我们通常有四个子阶段。这四个步骤应该以交替的方式执行,在它们之间来回循环

请注意,本系列的目的是通过几个核心示例来指定流程中的步骤——即“什么”(例如评估算法设计的可行性)。我们将不描述“如何”(例如,为算法执行有效 POC 的原理)。

以下是这一阶段的总结:

作者图片

1.设计数据收集和注释方法

1.1.彻底考虑收集和注释方法,以及它们的成本效益

请记住:

  • 通常,这些方法有几种可能的变体,并且这些变体在成本效益谱上有所不同。有些注释会更快,但对于算法开发来说可能不太有效或不太灵活(例如,代表性数据较少、类的粒度较小、注释粗糙、所需的 QC[质量控制]量),而其他一些会更慢但更有效。
  • 想清楚这些取舍,做出明智的决定。
  • 很多时候,为注释者开发更好的工具可以使最有效的注释方法也最快。

1.2.记录方法(但要灵活应对变化)

  • 该文档最好由注释团队创建,并由算法开发人员审查。
  • 该文档有几个目的:验证每个人都真正在方法上保持一致,培训收集者\注释者,创建可靠性和一致性,以及能够在几个月后轻松地恢复注释。

1.3.定期检查注释质量

  • 要求注释者首先注释一个小的子集,并在让他们完成工作之前监控它。
  • 请注释者标记所有用当前方法难以标记的样本,并定期检查这些样本以改进方法(有时添加一些注释会有助于过滤掉有问题的案例)。

2.数据收集、注释和评估

2.1.了解模型性能如何随更多数据而扩展

在收集更多数据之前-测量并绘制模型性能,作为您研究的基线模型的数据集大小增加的函数。根据结果迭代确定获取更多数据的成本效益

2.2.构建灵活的数据接收管道(也称为 ETL)

这将允许您像乐高一样改变不同类型的预处理,并检查它们对模型的影响。

2.3。选择数据存储和版本控制方法

存储和版本控制有许多不同的方法和实现方式——从理解概念和它们之间的区别开始。

下面的方法只是示例,了解您项目的需求(例如,可能版本控制对您来说根本不是一个需求,因为数据不会改变)使用这些示例作为参考,并根据您的需求设计相关的方法

在早期阶段的项目会使用更基本的方法,这是很自然的,同时,欠冲会导致重现性问题,甚至数据丢失,修复这些问题的成本很高。

  1. 数据存储在一个目录\桶中。
  • 优点:实现简单。
  • 风险:无法(\难以)恢复以前版本的数据以实现可再现性,缺乏结构增加了因误用而造成损坏的风险,根据元数据进行查询的速度很慢。

2.通过训练时的快照进行版本控制(即将使用的数据和注释复制到实验工件目录中)

  • 优点:实施简单,有利于再现性。
  • 风险:大型数据集不可扩展,存在快照丢失的风险,因为工件目录丢失。

3.专用数据库:

a.存储在桶中的大量文件具有唯一的 id。

b.存储在受版本控制的数据库中的元数据和注释指向重文件。

c.用于训练集\验证集的数据 id 是单独跟踪的(例如,使用 git)。

d.对于中型数据集,一切都可以使用简单的机制来实现,比如 git。

e.对于非常复杂的数据集,考虑使用专用机制,如 DVC。

  • 优点:支持版本控制,支持通过元数据进行高效查询,结构显著降低了误用和数据丢失的风险。
  • 风险:更难实现和维护。

4.货架解决方案的:

  • 风险:你依赖于外部框架。

2.4。验证数据质量

收集期间和之后\注释—持续确保数据是根据商定的方法收集和注释的。如果有任何差异,决定如何处理它们。

2.5.对每个数据集执行统计探索

使用聚合和统计(整体和每个类)来更好地了解您的数据,量化代表性,识别异常值,并识别数据覆盖范围问题。

3.算法开发和培训

3.1.建立和维护一个结构良好的代码库

a.代码库应该结构良好,包括:

  • ****模块化组件用于数据处理、模型定义、模型训练、模型评估和实验管理。
  • 这种模块化的设计方式将使您能够在研究和生产中最大程度地重用相同的组件。这将使您能够避免生产缺陷。
  • 避免研究中的“手动”步骤——你的代码库应该简化并记录(通过代码)你研究的所有步骤。缺乏重现性的最常见原因之一是不记得手动执行的步骤的细节。

b .流线实验再现性 -

  • 播种
  • 快照:代码版本、数据版本和分割、脚本、超参数、配置、种子值、实验结果、实验工件等。

3.2.调试并执行健全性检查

参考 Andrej Karphaty 的博客文章中的第 2 和第 3 部分:神经网络训练的诀窍。

3.3.建立性能基线

针对您的问题建立性能基线。基线对于建立预期性能的下限(简单的模型基线)很有用,这通常与部署的需求相差不远。

  1. 找到一个非常相似的问题的 SOTA 模型(如果有的话,并且容易适应)并重现结果。如果不可用——构建一个相对简单的模型,它不需要任何在代码库中没有简化的复杂方法。
  2. 在您的数据集上进行训练,并执行简单的超参数调整—这是您的基线。

3.4.将您的绩效提高到期望的要求

  1. ****了解您的噪声,了解哪些结果具有统计学意义(例如,多次训练基线并估计方差)。
  2. ****执行受控研究(包括新的研究方向和对现有模型的简单超参数扫描)——在导数方法中:一次只改变一件事,并测量该方向的“部分”导数。合并所有已经证明偏导数有所改善的方向,并考虑放弃其他方向。
  3. ****尽可能实际——把每一个新的 SOTA 当做你的新基线,并且(可能的话)回到这一节的开头(测量噪音,用新的 SOTA 作为偏导数的参考)。
  4. ****尽可能多地评估和比较“下游”指标的实验(例如,影响用户感受的整个算法包的指标)。很多时候,“模型”指标的改进不会影响下游指标,意识到这一点很重要。

3.5.执行研究同行评审

  • 让至少和你一样有经验的人(最好比你更有经验)——真正回顾你所做的研究工作(比如问最难的问题)。
  • 为了成本效益,在长时间运行的昂贵的实验过程中——在运行之前,让别人检查你的实验(包括代码)。
  • 用简洁的方式记录你的实验、扫描和结果,以及你从中获得洞察力的主要图表。

3.6.在审查完代码后,尽可能多地将变更合并到主分支中

4.测试和评估

人工智能系统往往有许多高度复杂的移动部件,随着你(和其他人)给算法包增加更多的复杂性,这些部件不断变化和发展。为了确保有效性和可扩展性,必须确保:

  1. ****通过评估:我们新的 algo 包的性能确实比当前部署的版本更好(或者至少不会更差)。
  2. ****通过测试:
  • 您的代码没有被破坏——这将提高开发速度。
  • 定期确保培训结果可以用最新版本的代码库重现。

****在以下阶段进行评估和测试很重要:

  1. 在您的研究过程中不断地,以确保您真正地提高性能(评估)并保持开发速度(测试)。
  2. 在合并到共享开发分支之前。
  3. 在合并到生产分支之前(即,在部署到生产之前)。

4.1.评估算法包

执行可靠的比较,这将使您能够决定我们是否应该在生产中部署新版本的算法包(单个组件\模型中的任何微小变化也被视为包的新版本)。

  • 在将新版本的算法部署到产品中之前,以及在对共享分支进行任何重大更改之前,这种比较将作为一个强制步骤来执行。
  • ****用于评估的推理应该通过“生产管道”——生产中用于推理的同一管道。
  • 这将是一个新算法和当前最先进的算法之间的比较,以了解性能是否确实有所改善。
  • 依赖性的变化(例如内部词典、基本事实)会导致较差的算法包被认为是较好的,反之亦然。一个苹果对苹果的比较意味着在完全相同的依赖关系上比较两个算法包——相同的内部词汇,基础事实,等等。

4.2.添加完整的管道集成测试(“烟囱清扫”)

最常见的错误不仅会导致性能问题,还会“破坏”管道。发生这种情况是因为格式(例如形状、类型等。)将会改变,管道中跟随它的下一个组件将会崩溃。

这实际上太容易实现了,对开发速度的影响是严重的。团队成员无休止地将破碎的组件合并到共享的分支中,团队中的每个人都在无休止地修复其他人的 bug。同样常见的是,3 个人同时独立地修复同一个 bug。

你应该渴望对你的每一个主要“用户故事”(例如,每一个 SOTA)进行全面的集成测试。在许多情况下——SOTA 测试非常慢(有时会运行几天),因此它们不能在每个到主分支的合并中运行。全管道集成测试必须足够快,以便在每次合并时运行,因为每次运行它对你的开发速度至关重要。

4.3.最终测试整个培训管道(“研究系统”)— SOTA 测试

测试完整的培训管道(从原始数据到培训再到评估),并断言最新版本的代码达到了 SOTA。这些测试应该每晚/每周运行一次,因为它们需要几个小时到几天的时间。****

自动化这一点至关重要,因为最终同一个代码库支持多个项目,每个项目都有其下游的 SOTA。很有可能一个人在一个项目上工作——会降低另一个项目的下游度量,因为为您执行的每个变更手动测试每个项目是不可行的。

4.4.为您创建/更改的每个新功能编写单元测试

单元测试是一种确定从更大的代码库中隔离出来的单个行为(即单元)的正确性的方法(使用依赖注入或其他需要时的隔离机制)。这些测试应该在每次提交时运行,因此应该很快(每秒数百次)。拥有一个大范围的单元测试能够在开发过程中快速发现错误,并将它们定位到一个特定的行为(即单元),这极大地加速了调试。****

上面描述的端到端测试和集成测试,对于缺乏单元测试覆盖来说,是一个相对较好的补充。然而,它们要慢得多(因此它们不能在开发过程中持续使用)并且更少本地化(因此它们需要调试时间来调查源代码)。另一个免费的备用方案是“记录的功能测试”(如下)。

4.5.使用“记录的”功能测试

你应该有一个在几个重要例子上运行的快速功能测试,这样你就可以快速运行(<7 minutes) to increase confidence that you haven’t broken any functionality during development. These tests should be created for any important high-level functionality (e.g. production pipeline inference). Be careful of creating too many of these as they are relatively hard to maintain.

4.6. Add system integration tests

The algorithm package performance can be degraded by a change in its external dependencies (e.g. a DB/service the algorithm is querying). There are two types of dependencies in that aspect — dependencies that are allowed to affect the algorithm performance (e.g. internal lexicons. Should be validated that they only improve the performance through monitoring [described in a later section]), and dependencies that are not allowed to change the performance at all (The second type should be verified using integration tests whenever a new version of the dependency is deployed, which will compare the actual outputs to the expected [recorded] outputs and assert the output doesn’t change at all.

****QA,部署和维护)

作者图片

1.在用户体验时评估算法包

执行可靠的比较,这将使您能够决定我们是否应该在生产中部署新版本的算法包。

  • 在将新版本的算法部署到生产中之前,这种比较将作为一个必不可少的步骤。
  • 比较将被管理和执行,理想地,算法开发者不参与测试的定义,作为盲的和客观的测试。
  • 这将是一个新算法与当前最先进算法之间的比较,以了解性能是否确实有所提高(在这种情况下,我们应该部署新算法)。

2.为部署设置一些自动流程

当团队成长时,很难相信每个人都知道/记得/遵循流程——对许多人来说,这是不自然的,手动流程可能很慢/容易出错。而且规模很大,很难管理。

最好在这个过程中加入一些自动化机制来保证质量。

3.保持将模型回滚到以前版本的能力

4.监控生产中的算法包

所需的监控类型:

  • 停机时间和错误—建议通过警报进行监控。
  • 计算性能没有下降。
  • 在标准数据集上定期评估在生产中部署的算法包的性能。降低的算法性能可能由各种原因引起,例如:数据分布的变化、新的错误、算法包依赖性的变化。
  • 数据分布与预期相比的变化(被降低的算法性能所掩盖,但是直接监控可以更好地定位问题的来源)

**信用:凯利·西克玛——【https://unsplash.com/photos/JRVxgAkzIsM **

这个帖子系列是与伊丹·巴苏克合著的。

我将很高兴听到你的想法和经验。

基于 PCL 的自动驾驶激光雷达感知栈的算法实现

原文:https://towardsdatascience.com/an-algorithmic-implementation-of-an-autonomous-driving-lidar-perception-stack-with-pcl-4d5928bcebc7

基于激光雷达感知的 C++管道示例

Unsplash 上的 CHUTTERSNAP 拍摄

介绍

自动驾驶是一个相对较新且非常迷人的现代技术领域。自 2004 年 DARPA Grand Challenge 期间公开展示并在 2007 年的城市挑战赛期间转向更具挑战性的城市环境以来,自动驾驶一直受到业界和学术界的追捧。这些应用一直在个人自动驾驶汽车、自动出租车车队、卡车运输、送货等等之间徘徊,但技术仍未出现。自动驾驶陷入幻灭低谷的原因之一是软件驱动栈的感知组件是一个非常复杂的问题。虽然大多数团队采用基于激光雷达的感知,但特斯拉和 Wayve 仍试图建立摄像头优先的感知。依赖激光雷达的解决方案也可以分为两类:处理点云的传统计算机视觉算法和基于深度学习的方法。神经网络承诺以高平均精度解决感知问题,然而,如果我们想在最坏的情况下证明合理的精度,这是不够的。在汽车行业,当软件必须满足 ASIL D 级可靠性时,最好有一个独立的传统非 ML 感知堆栈在车辆上运行,并提供与汽车标准的高度一致性。在本文中,我们将看看在 PCL(一个开源的点云库)的帮助下实现的广告栈。

首先,我们将坚持系统级的测试驱动开发(TDD ),以确保我们的整个代码在第一次现场部署发生之前得到彻底的测试。为此,我们需要一个数据集来运行我们的代码。卡尔斯鲁厄理工学院和芝加哥丰田理工学院的经典 2012 数据集 Kitti 非常适合这个目的。这是第一批大规模高质量数据集之一,旨在作为自动驾驶领域计算机视觉算法的基准。Kitti 具有包括检测在内的几个子数据集,但我们对跟踪子数据集更感兴趣,因为我们将利用数据的时间属性以及 GNSS 定位数据。

谷歌地图定位的凯蒂跟踪序列 0000

Kitti 跟踪由 21 个同步的 PNG 图像序列、威力登激光雷达扫描和来自 RT3003 GPS-IMU 模块的 NMEA 记录组成。数据集的一个重要特征是传感器之间的彻底相互校准,包括矩阵“Tr_imu_velo ”,它是从 GPS-IMU 坐标到威力登激光雷达坐标的转换。

感知管道的架构如下。

点云感觉形态的感知管道。图片由作者提供。

让我们分别讨论每个组件,并深入研究它们的 C++实现。

点云抽取

为什么我们可能需要抽取来自深度传感器(可能是一个或几个激光雷达)的点云?自动驾驶软件最重要的要求是满足实时操作约束。第一个要求是处理流水线跟上激光雷达扫描采样的速率。在实际情况下,扫描速率可能从每秒 10 次扫描到每秒 25 次扫描不等,这导致任何处理阶段的最大延迟从 100 毫秒到 40 毫秒不等。如果对于任何可能的输入点云,任何阶段(例如集群化)滞后超过 100 毫秒(对于每秒 10 次扫描的速率),将会发生丢帧,或者流水线的总延迟将开始任意增长。这里的一个解决方案是丢弃激光雷达点,而不是丢弃整个帧。这将逐渐降低准确性指标(召回率和精确度),并保持管道实时运行。第二个要求是系统的整体延迟或反应时间。同样,总延迟应限制在至少 100 或 200 毫秒,500 毫秒甚至 1 秒左右的反应时间对于自动驾驶来说是不可接受的。因此,通过首先应用抽取来处理少量的激光雷达点来开始算法设计是有意义的。

抽取的标准选项有:

  1. 规则的
  2. (伪)随机
  3. 体素网格

常规缩减采样速度很快,但可能会导致点云上出现锯齿图案。随机或伪随机下采样也很快,但可能会导致小对象不可预测地完全消失。像 PCL 的 pcl::VoxelGrid <>类这样的体素网格过滤是智能和自适应的,但需要额外的计算和内存。一如既往,选择一种方法而不是另一种方法是一种设计选择。

原始点云。请注意,此处显示的 RGB 图像仅用于清除。RGB 数据不用于激光雷达管线。图片由作者提供。

抽取的点云。图片由作者提供。

多扫描聚合

多扫描聚合是在自我车辆相对于地面移动时将若干历史激光雷达扫描配准到公共坐标系的过程。公共坐标系可以是本地导航框架(也称为 ego-master 坐标)或当前激光雷达传感器坐标。我们将以后者为例。这个阶段理论上是可选的,但在实践中非常重要。问题是下一阶段的聚类取决于激光雷达点的密度,如果密度不足,过度聚类的影响可能会发挥作用。过度集群化意味着任何对象(汽车、公共汽车、建筑墙壁等)都可能被分割成几个部分。就其本身而言,对于检测障碍物来说,这可能不是问题,但是,对于感知-跟踪-过度集群化的下游模块来说,这是一个巨大的挑战。跟踪器可能不准确地关联物体的碎片,并产生将由预测模块在与自我车辆路径交叉的方向上外推的轨迹,这又使得自我车辆突然刹车。我们绝对不希望集群化中的小错误在下游组件中造成大量错误。

几次连续扫描(5 到 10 次)的聚合成比例地增加了落在每个物体上的激光雷达点的密度,并有助于精确的聚类。自我车辆运动的一个很好的特点是,汽车能够从不同的角度观察同一物体,并且激光雷达扫描模式覆盖物体的不同部分。

自我载体(灰色方框)从不同的角度观察同一个物体。图片由作者提供。

让我们看看执行聚合的代码。第一阶段是用历史点云以及随后的扫描仪姿态之间的姿态变换来保持约束长度的队列。请注意,我们如何使用从 RT3003 GPS-IMU 模块获得的平移速度[Vx,Vy]和旋转速度 Wz 来构建姿态变换。

在第二阶段,我们从时间上向后的最近扫描开始遍历队列,将扫描到扫描的转换累积到集合转换中,并将集合转换应用于每个历史帧。通过这种方法,我们使计算成本为 O(N*D ),其中 N 是点数,D 是历史的深度(扫描次数)。

在聚集之后,如果移动的物体看起来有点模糊,那么点云会变得模糊。这是一个挫折,可以在集群化阶段进一步处理。在这个阶段,我们需要的是一个更密集的点云,从多个帧中积累信息。

5 帧聚合点云。为了便于观察,地面被移开了。图片由作者提供。

地面移除

感知堆栈的目的是提供有关动态对象和静态障碍物的信息。汽车应该在道路上行驶,通常路面不被认为是障碍。因此,我们可以移除从路面反射的所有激光雷达点。为了做到这一点,我们首先将地面检测为平面或曲面,并移除表面周围约 10 厘米或以下的所有点。有几种方法可以检测点云上的地面:

  1. 用 Ransac 探测一架飞机
  2. 用霍夫变换检测平面
  3. 具有填色的非平面表面检测

让我们借助 Eigen 和 PCL 库更深入地了解 Ransac 的 C++实现。

首先,我们来定义一下我们的候选平面。我们将使用基点加法向量的形式。

然后,我们定义一个辅助函数,它允许我们在点云转换到平面坐标后,在 Z 坐标上找到满足条件的所有点的索引。条件是由 lambda 函数给出的,因为我们希望这个帮助器尽可能通用。请按照代码中的注释来了解实现的细节。

最后,主要的 Ransac 实现如下所示。第一步是根据 Z 坐标对点进行粗略过滤。此外,我们需要再次抽取点,因为我们不需要聚合云中的所有点来验证候选平面。这些操作可以一次完成。

接下来,我们开始迭代。借助 C++标准库的 std::mt19937 伪随机生成器,每次迭代采样 3 个随机点。对于每个三元组,我们计算平面并确保其法线向上。然后我们使用同一个帮助函数 find _ inlier _ indices 来计算内联器的数量。迭代结束后,我们剩下的是最佳平面候选,我们最终使用它来复制点云中索引不在列表中的所有点。请注意 std::unordered_set <>的使用。它允许执行索引的常数时间 O(1)搜索,而不是我们对 std::vector <>的线性 O(N)搜索。

让我们看看地面消除的结果。

在移除地面之前。图片由作者提供。

移除地面后。图片由作者提供。

去除了背景之后,我们准备对剩余的点进行聚类,并通过凸包提取来压缩对象元数据。这两个阶段值得他们自己的文章。我将在即将到来的第二部分中介绍它们的实现。同时下面是聚类的最终结果——凸包提取。

使用凸包可视化的最终聚类对象。图片由作者提供。

凸包绝对是任何追踪器都渴望接受的元数据类型。它们在 RAM 使用方面更紧凑,并且比定向边界框更精确地表示对象的边界。这是另一个可视化,取自 Kitti 序列 0003。

来自 KITTI 追踪序列 0003 的集群化点云。图片由作者提供。

结论

我相信,自动驾驶将是人类在生活质量和整体生产力方面的一次飞跃。它将最终使“自动”-“移动”真正成为一个最终自动化的移动设备。自动驾驶需要最高水平的软件工程,我希望许多刚开始职业生涯的工程师在阅读这篇文章后会受到鼓励,以完善他们的技能。

参考资料:

[1] 作者在 Github 上的项目源代码(星星不胜感激)

[2] 基蒂跟踪页面

[3] PCL 点云处理库

A/B 测试中 p 值的替代方法

原文:https://towardsdatascience.com/an-alternative-to-p-values-in-a-b-testing-44f1406d3f91

在 A/B 测试中,总变异距离的高概率下界(HPLBs)如何能产生一个综合的有吸引力的测试统计量

图 1:来自原始论文的图(作者)

撰稿人:洛里斯·米歇尔杰弗里·纳夫

一般 A/B 测试的经典步骤,即决定两组观察值是否来自不同的分布(比如 P 和 Q),是:

  • 假设一个零假设和一个备择假设(这里分别为 P=Q 和 P≠Q);
  • 定义重要性水平α;
  • 构建一个统计测试(拒绝或不拒绝空值的二元决策);
  • 导出测试统计量 T;
  • 从 t 的近似/渐近/精确零分布获得 p 值。

然而,当这样的检验拒绝零,即当 P 值是显著的(在给定的水平上)时,我们仍然缺乏对 P 和 Q 之间的差异有多强的度量。事实上,测试的拒绝状态在现代应用中可能是无用的信息(复杂数据),因为有足够的样本量(假设水平和功效固定),任何测试都倾向于拒绝空值(因为它很少完全为真)。例如,了解有多少数据点支持分布差异是很有趣的。

因此,基于来自 P 和 Q 的有限样本,一个比“P 不同于 Q 吗?”可以表述为“实际支持 P 和 Q 之间分布差异的观测值λ的概率下限是多少?”。这将正式转化为以高概率(比如 1-α)满足λˇ≤λ的估计值λˇ的构造。我们称这样的估计为λ上的高概率下界 (HPLB)。

在这个故事中,我们想鼓励在 A/B 测试中使用 HPLBs,并给出一个论点,说明为什么λ的正确概念是 P 和 Q 之间的总变化距离,即 TV(P,Q)。我们将在另一篇文章中解释和详细描述这种 HPLB 的构造。您可以随时查看我们的paper了解更多详情。

为什么总变异距离?

总变异距离是概率的一个强(细)度量。这意味着如果两个概率分布不同,那么它们的总变化距离将是非零的。它通常被定义为集合上概率的最大不一致。然而,它更直观地表现为概率 P 和 Q 之间的离散传输度量(见图 2):

概率度量 P 和 Q 之间的总变化距离是需要从 P 改变/移动以获得概率度量 Q 的概率质量的分数(反之亦然)。

实际上,总变化距离代表 P 和 Q 之间的点的分数,这正是λ的正确概念。

图 2:左上方表示 TV(P,Q)的可能质量差。右上角通常定义为 TV(P,Q)最大概率不一致(在 sigma 代数上)。底部离散的最佳运输公式,质量分数不同于 P 和 Q(作者)。

如何使用 HPLB 及其优势?

估计值λˇ对 A/B 测试很有吸引力,因为这个单一的数字既包含了统计显著性(如 p 值一样)又包含了效应大小估计值。它可以按如下方式使用:

  • 定义置信水平(1-alpha);
  • 基于这两个样本构造 HPLBλˇ;
  • 如果λˇ是零,则不拒绝该空值,否则如果λˇ> 0,则拒绝该空值,并推断λ(不同的分数)至少是概率为 1-α的λˇ。

当然,要付出的代价是λˇ的值取决于所选的置信水平(1-α),而 p 值与之无关。然而,在实践中,置信水平变化不大(通常设置为 95%)。

考虑医学中效应大小的例子。与没有接受药物治疗的安慰剂组相比,新的药物治疗需要在实验组中具有显著的效果。但影响有多大也很重要。因此,我们不应该只谈论 p 值,还应该给出一些效应大小的度量。这一点现在在优秀的医学研究中得到广泛认可。事实上,一种使用更直观的方法计算 TV(P,Q)的方法已被用于单变量设置,以描述治疗组和对照组之间的差异。我们的 HPLB 方法提供了显著性和效应大小的度量。让我们用一个例子来说明这一点:

举个例子

我们模拟二维的两个分布 P 和 Q。因此,P 将只是一个多元正态分布,而 Q 是 P 和具有移动平均值的多元正态分布之间的一个混合物

library(mvtnorm)
library(HPLB)set.seed(1)
n<-2000
p<-2#Larger delta -> more difference between P and Q
#Smaller delta -> Less difference between P and Q
delta<-0# Simulate X~P and Y~Q for given delta
U<-runif(n)
X<-rmvnorm(n=n, sig=diag(p))
Y<- (U <=delta)*rmvnorm(n=n, mean=rep(2,p), sig=diag(p))+ (1-(U <=delta))*rmvnorm(n=n, sig=diag(p))plot(Y, cex=0.8, col="darkblue")
points(X, cex=0.8, col="red")

混合权重增量控制两个分布的不同程度。delta 从 0 到 0.9 变化,如下所示:

模拟 delta=0(右上)、delta=0.05(左上)、delta=0.3(右下)和 delta=0.8(左下)的数据。来源:作者

然后,我们可以计算每种情况下的 HPLB:

#Estimate HPLB for each case (vary delta and rerun the code)
t.train<- c(rep(0,n/2), rep(1,n/2) )
xy.train <-rbind(X[1:(n/2),], Y[1:(n/2),])
t.test<- c(rep(0,n/2), rep(1,n/2) )
xy.test <-rbind(X[(n/2+1):n,], Y[(n/2+1):n,])
rf <- ranger::ranger(t~., data.frame(t=t.train,x=xy.train))
rho <- predict(rf, data.frame(t=t.test,x=xy.test))$predictionstvhat <- HPLB(t = t.test, rho = rho, estimator.type = "adapt")
tvhat

如果我们用上面的种子集来做,我们

不同增量的估计值。

因此,HPLB 设法(I)检测何时两个分布确实没有变化,即当δ为零时它为零,(ii)当δ仅为 0.05 时已经检测到极小的差异,以及(iii)检测到差异越大,δ越大。同样,需要记住的关于这些值的重要事情是,它们确实有意义——值 0.64 将是真实 TV 的下限,概率很高。特别是,每一个大于零的数字都意味着 P=Q 在 5%的水平上被拒绝。

结论:

当涉及到 A/B 测试(双样本测试)时,重点通常是统计测试的拒绝状态。当检验拒绝零分布时,在实践中有一个分布差异的强度度量是有用的。通过构建总变化距离的高概率下限,我们可以构建预期不同的观测值部分的下限,从而提供分布差异和偏移强度的综合答案。

免责声明和资源: 我们意识到我们遗漏了许多细节(效率、HPLBs 的构建、功率研究……),但希望能够开阔思路。在我们的 页面R中可以找到 M 矿石的详细信息以及与现有试验的比较,并查看起重机上的 R-package HPLB。

数据网格的体系结构

原文:https://towardsdatascience.com/an-architecture-for-the-data-mesh-32ff4a15f16f

数据网格正在革新企业数据管理。但是实际的数据网格架构是什么样的呢?

照片由 Unsplash 上的 Dariusz Sankowski 拍摄

数据网格正在革新企业数据管理

数据是新的黄金,至少他们是这么说的。但是最近挖掘这些数据价值的努力经常失败。在某些情况下,令人沮丧地失败了。我们尝试了数据仓库,但是整个企业中不一致的数据将它们变成了数据丛林。因此,我们尝试了“数据湖”,但是陈旧的数据将它们变成了数据沼泽!

因此,对数据网格持怀疑态度是合理的。它是最新的时尚还是提供了实用和持久的价值?嗯,我想这个问题还没有定论,但是我最初的观点是——在一家大型金融服务公司建立了一个现代数据网格——数据网格是实用的、可行的,而且最重要的是,它可以显著加快解决方案的交付。

但是有许多文章论述了为什么数据网格将改变企业数据格局,我不会试图再现这些见解(但是如果您希望阅读更多内容,请查看此处此处,以及此处)。但是,鉴于我对概念感兴趣,我认为解释一下如何实现数据网格会更有用。更具体地说,本文讨论了几个主题:

  • 数据网格架构,展示了如何使用基础技术来创建数据产品,而这些数据产品又可以组合成企业数据网格
  • 数据网格实时同步场景,展示了数据网格/数据产品如何允许数据在整个企业内近乎实时地迁移,这可能首次解决“陈旧数据”问题
  • 数据网格人工智能/人工智能场景,展示数据网格/数据如何产生关键数据谱系、可追溯性和可再现性,这将允许高级人工智能/人工智能模型更轻松地处理任务关键型工作负载
  • 数据网格治理和可审计性场景,展示数据网格/数据产品如何捕捉有望彻底改变数据治理的数据变化

数据混乱—陈旧和不一致的数据在企业中激增

企业数据领域充斥着新数据技术的尸体。企业的“数据仓库”为分析数据提供了一个家。但它们从未解决数据一致性问题,我们很快发现,用一两天前的旧数据填充它们会使重要的分析数据变得陈旧。数据仓库变成了数据丛林。

因此,我们将数据仓库整合到“数据湖”中,并了解到查找数据并保持其一致性非常困难,并且在许多情况下不太实际,这导致用户(和高管)质疑数据的有用性。数据湖变成了数据沼泽。

图 1:现状:数据混乱

可以肯定地说,我们在企业中的移动方式(图 1)是一项重大挑战:

  • 陈旧数据:批处理过程在一夜之间移动记录簿数据,留下许多系统至少 24 小时的数据;即使使用在线流程来更新数据,它们通常也是定制的,构建起来既昂贵又耗时,这可能导致它们很少被构建!
  • 不一致的数据:批处理过程会产生不一致的数据,因为下游数据是在已经过时的记录簿批量更新后的一天(或两天)处理的;同样,用定制的解决方案来应对这一挑战是不现实的,也是不可行的。

简而言之,我们当前的实践——尤其是那些与跨企业的数据移动相关的实践——已经造成了数据混乱!

数据网格是组件的组合

我们遗留的数据混乱不会很快消失。然而,根据我的实际操作经验,“数据网格”将大大简化企业的数据环境,并为准确和一致的数据提供基础。

据最初概念的创造者 Zhamak Dehghani 称,数据网格是基于几个原则:

  • 面向领域、分散的数据所有权和架构:数据网格的核心是将责任分散和分配给最接近数据的人,以支持持续变化和可伸缩性
  • 数据即产品:数据网格支持多种功能:包括可发现性、安全性、可探索性、可理解性、可信赖性,同时定义了负责建立这些功能的角色—域数据所有者。
  • 作为平台的自助式数据基础架构:数据网格允许数据所有者以简单易用的自助式方式提供数据
  • 联合计算治理:数据网格实现了一个联邦治理模型,该模型包含去中心化和领域自治、通过全球标准化实现的互操作性、动态拓扑以及最重要的平台决策的自动执行

图 2:数据网格原则

但最重要的是,这些原则允许数据网格提供引人注目的好处和结果。首先,它们不仅允许跨分析领域集成数据,还允许跨运营(交易)和参与(为与客户交互而优化)领域集成数据。

其次,数据网格提供了一种使用一组一致的可发现和自助服务接口连接企业的方法,从而提供了对整个企业中数据的无缝访问。现在,数据确实可以很容易地找到、使用和管理。

如今,这些原则并没有体现在任何单一供应商的产品中。但是,假以时日,供应商将提供真正的数据网格功能。然而,今天的数据网格必须由组件组装而成。数据网格是由许多较小的乐高积木搭建而成的乐高飞船(图 2)。

那么,这些构建块是什么,它们看起来像什么,以及如何将它们组装成数据网格?

数据网格/数据产品架构

数据网格体系结构中有几个关键组件(图 3 ):一个数据产品和一个数据网格,该数据网格集合了整个企业中的数据产品并使其可访问。

图 3:数据产品(概念架构)

先说一个数据产品。根据前面提到的数据网格原则,企业中会有许多数据产品,但每个产品都由相同的组件组成:

  • 数据,包括

  • ****数据事件,定义和描述与数据产品相关的任何状态变化、命令或数据传输;事件从许多来源生成,包括数据产品 API(每个请求都可以是一个事件)、变更数据捕获(数据的每个变更都是一个事件),以及数据目录变更(元数据的变更是发布给感兴趣的订户的事件)

  • 数据产品目录 ,描述驻留在数据产品中的数据,同时提供用户界面和 API,使用户和机器都能轻松消费数据产品;数据产品目录集成到企业数据目录中,为所有数据产品提供一致的企业视图

  • 数据产品不可变变更/审计日志 ,它跟踪并记录对数据产品的所有变更,以支持联合治理以及审计需求

  • 事件流骨干 ,使数据产品内部以及整个企业之间的数据事件通信变得简单

数据网格/数据产品场景:数据网格和 AI/ML

人工智能/人工智能模型的好坏取决于训练它们所用的数据。因此,我们最好确保我们用来训练这些模型的数据是准确和一致的。但是,如何在运营和分析(或参与)系统/数据库之间及时移动数据呢?

在图 4 中,我们展示了数据网格如何解决这种情况:

  1. 运营系统更新客户数据
  2. 变更数据捕获 (CDC)读取操作数据库事务日志,获取客户数据变更,然后创建一个事件并发布到由事件流主干(通常是 Kafka)管理的“主题”中
  3. 订户——在我们的例子中是客户分析系统——会收到客户数据变更事件的通知
  4. 分析系统用收到的事件中的信息更新其数据库
  5. AI/ML 算法使用最新和一致的数据来训练它们的模型,从而获得最佳结果

图 4:数据网格/数据产品场景:AI/ML

数据网格/数据产品场景:数据沿袭

企业对模型可再现性、可追溯性和可验证性的需求迫使组织重新思考传统的 AI/ML 交付生命周期。特别是,金融服务、监管机构要求明确的模型可再现性、可追溯性和可验证性(详见欧盟美国加拿大)。但是从医疗保健和生物技术到政府安全的许多其他行业也有类似的要求。事实上,现在即使是适度监管行业的企业也发现可重复性、可追溯性和可验证性的好处远远超过了它们的成本。

但是今天模型的可再现性、可追溯性和可验证性是如何解决的呢?企业退回到两种情况。一些企业使用端到端分析包(例如 SAS )将这些功能嵌入到它们的生命周期中,但这种情况使企业受制于专有的供应商产品,这对那些希望利用强大的开源 AI/ML 组件的企业来说是一个不利因素。

第二种选择是企业构建定制的组件来建立数据血统,以支持可再现性、可追溯性和可验证性。不幸的是,这种方法既昂贵又耗时,因此只适用于最关键的人工智能/人工智能模型,最终限制了人工智能/人工智能模型的机会和价值。

图 5:数据网格/数据产品场景:数据谱系

在图 5 中,我们展示了数据网格如何解决这种情况。该场景几乎与前一个场景(图 4)相同,只是增加了一些关键的内容。在步骤 4 中,“变更/审计日志”服务订阅被写入(在通知变更时)不可变的仅附加“变更/审计日志”的数据变更(注意,这是可配置的,使得它可以应用于任何/所有更新)。接下来,在步骤 7 中,监管机构可以检查(通过工具/实用程序)来自工具和实用程序的报告/证据,这些报告/证据说明了再现性、可追溯性和可验证性所需的数据谱系。

数据网格企业架构

数据网格是使用企业数据产品目录支持的事件流主干连接的一组数据产品。一个企业有许多数据产品,每一个都是围绕与企业相关的业务线或组来组织的。通过捕获数据更改并将其作为事件发布在企业流主干网上,数据产品之间共享数据,从而允许订户(实际上是任何应用程序或系统)获得他们感兴趣的数据更改的通知。

尤其重要的是企业数据目录,它与数据产品目录同步(使用数据网格功能),以跟踪整个企业中的数据元素(即驻留在每个数据产品中的数据)。借助强大的搜索/查找功能,企业数据目录可以轻松地在整个企业中查找数据。

图 6:数据网格企业架构

数据网格提供了工具和机制来简化数据产品的连接:

  • ****数据网格使数据共享变得容易:数据在域之间共享,并且很可能跨域重复。数据网格域被明确设计为在域之间共享数据。所有数据产品访问和更改都被视为事件,这些事件在数据产品内部共享,但也在使用事件流主干的数据产品之间共享。事件被发布到一个“主题”上,任何订阅者都可以得到任何事件的通知。
  • ****数据网格支持近实时数据共享:使用“变更数据捕获”(CDC)机制在域内近实时传输数据,从而保持域操作、参与和分析数据同步,同时通过捕获本地域数据 CDC 事件并使用 Kafka 等事件流主干(通过“主题”)将它们传输给感兴趣的订户,在域间近实时传输数据。
  • ****数据网格与企业数据目录集成:每个数据产品都与企业数据目录近乎实时地集成在一起,允许任何用户(或机器)访问企业中任何数据产品的相关信息。这种可见性、透明性和即时性是联合治理的基础。

企业数据网格场景:实时数据同步

简而言之,数据网格使得在企业内部共享和同步数据产品变得简单。大多数企业有许多数据产品,其中一些可能反映业务领域(“零售银行”、“支付”、“商业银行”),而其他可能反映数据领域(“客户”、“账户”、“交易”)。

图 7:企业数据网格场景:实时数据同步

在图 7 中,我们看到了如何在整个企业中移动和同步数据:

  1. 操作系统更新数据
  2. 数据网格 CDC 捕获数据产品中的数据更改
  3. 订阅者——实际上是企业中的任何应用程序/系统——接收数据更改通知并更新其本地存储库。

总结想法

数据网格使正确的数据在正确的时间可用,使企业能够自由地推动当今市场所需的创新、速度和敏捷性。它允许一个真正的联合数据访问、交付和管理模型,该模型不受官僚流程的约束,而是允许任何企业团队在任何时间以自动化的方式使用他们的项目、小组或业务线所需的数据。

旧的数据管理模型就是这样。老了。他们的时代已经过去了。

数据网格确实是更好的方法!

NumPy 数组的简单介绍

原文:https://towardsdatascience.com/an-easy-introduction-to-numpy-arrays-6ed950edd389

什么,怎样,为什么。

米卡·鲍梅斯特在 Unsplash 上的照片

**Index:**
1.Introduction
2.Indexing an array
3.Slicing array
4.Operations on a array
5.Arithmetic functions in Numpy
6.Concatenation of array
Splitting of an array

1.介绍

NumPy 代表数字,Pandas 代表数据

NumPy 是一个免费的开源 Python 库,可以帮助你操作数字和应用数值函数。熊猫擅长处理数据框架,NumPy 擅长应用数学。

数组是一个由相同类型的值组成的网格,由一个正整数索引。在 Numpy 中,维度称为轴(0 和 1 表示二维数组),轴是秩。

数组的秩就是它拥有的轴(或维度)的数量。简单列表的秩为 1:二维数组(有时称为矩阵)的秩为 2:三维数组的秩为 3。

过多地投入数组的海洋可能是危险的,也是技术性的。因此,对于本文,我们将把自己限制在一个或两个数组(1 维或 2 维)上,因为它们是数据分析中最常见的。

  • NumPy 数组对比列表

更好的做法是使用 Numpy 数组而不是列表。它们分别是更快,消耗更少内存,更方便

https://medium.com/@LVillepinte/introduction-to-list-a49a4a73f8ce

2.索引数组

  • 一维步进

索引数组类似于索引列表。第一个值是 0,需要用方括号来调用你的数据。

my_array = np.arrange(5)print(my_array)
[0,1,2,3,4]print(my_array[0])0
  • 二维分度

如上所述,一个数组可以有几个维度,索引数组的方法保持不变。我们可以使用选择行和列的两个索引来检索元素。

my_array = np.array([[0,1,2],[3,4,5]])
my_arrayarray([[0,1,2],
      [3,4,5]])my_array[0][1]
1
  • 选择一行或一列

您也可以选择一行或一列:

my_array
array([[0,1,2]
      ,[3,4,5]])my_array[0]
[0 1 2]my_array[:,1]
[1 4]

3.切片数组

  • 切片 1D 阵

切片系统是这样做的[ start:end ],其中 start 是包含性的,end 是排他性的。

my_array = [1,2,3,4,5,6,7,8] 
my_array[1,3]
[2, 3]my_array[1:]
[2,3,4,5,6,7,8]my_array[:]
[1,2,3,4,5,6,7,8]
  • 切片 2D 阵

多维数组也是如此。

my_array = np.array([[1,2,3,4]
                     ,[5,6,7,8]
                     ,[9,10,11,12]])print(my_array[:,1:3])
[[2,3]
 [6,7]
 [10,11]]

4.数组上的操作

  • 将两个数组相加

可以对数组进行操作。我们可以把它们加在一起。请注意,如果大小不同,该操作将不起作用:

array_1 = np.arrange(5)
array_2 = np.array([100,101,102,103,104])array_3 = array_1 + array_2
print(array_3)
array([100,102,104,106,108])

您也可以将数组相乘或应用任何类型的数值运算,逻辑保持不变。

  • 使用带有比较表达式的 Numpy】

比较数组时,NumPy 应用布尔逻辑。

my_array = [0,1,2,3,4,5]
new_array = my_array > 1
array([False, False, True, True, True, True)my_array[new_array]
array([2,3,4,5])my_array[my_array > 1]
array([2,3,4,5])

5.NumPy 中的算术函数

在内置的 Python 函数之上,NumPy 提供了算术函数。它们遵循前面看到的相同的数组操作规则:

np.add() 
np.subtract()
np.negative()
np.multiply() 
np.divide()
np.floor_divide()
np.power()

6.数组的串联

  • 连接一个 1D 数组

只有当数组具有相同的形状时,它们才能连接在一起。您可以一次添加多个阵列。

array_a = np.array([1,2,3])
array_b = np.array([4,5,6])
array_c = np.concatenate([array_a, array_b])
array([1,2,3,4,5,6])
  • 连接一个 2D 数组

2D 阵列对矩阵特别感兴趣。默认情况下,串联设置在axis=0上。这将返回更多的行,但列数相同。如果我们希望数组彼此相邻,我们需要设置axis=1

如果数组不匹配,比如,1 上的行数和轴数不相同,或者 0 上的行数和轴数不相同,那么它将不会工作并返回一个错误消息。

7.数组的拆分

可以将一个阵列分成多个子阵列。让我们把这看作串联的反义词。如果你简单地将一个数字与split()函数联系起来,NumPy 将会很聪明地将你的数组分成大小相同的子数组。

如果不可能创建相同大小的子阵列。必须使用array_split()功能。

my_array = [1,2,3,4,5,6]
split_array = np.split(my_array, 2)
print(split_array)
[array(1,2,3)),[array(4,5,6)]my_new_array = [1,2,3,4,5,6,7]
mna = np.split_array(my_new_array)
[array(1,2,3)),[array(4,5,6)], [array(7)]

你有可能去定制的结果,并在指定的位置分裂 1D 数组。

my_array = [1,2,3,4,5,6,7]
split_array = np.split(my_array, [2,4])
print(split_array)
[array([1,2])],array([3,4]),array([5,6,7])]

您也可以使用vsplit()hsplit()功能决定垂直或水平分割阵列

结论

NumPy 提出了一种操作数据的简便方法。数组比列表更好、更快、更有效,是一种广泛使用的解决方案。这个介绍展示了它是如何工作的,以及一些有用的操作和可能的查询。

在你走之前👋

感谢您阅读本文。这是一份关于数据科学的每周时事通讯。免费订阅,不要错过下一篇:https://medium.com/subscribe/@LVillepinte

📚先前发布:

https://faun.pub/how-to-become-the-best-data-scientist-6b1334f53244 https://faun.pub/this-one-thing-is-causing-the-next-black-swan-e734e6e710c1

理解 GIT 的简单方法

原文:https://towardsdatascience.com/an-easy-way-to-understand-git-80717c4b98e9

中间 git 命令及其含义解释

和许多程序员一样,我一直在使用 GIT 对我的工作库进行版本控制,我想回顾一下最常用的命令及其逻辑,以便有更深入的理解。这是我在一些在线教程和课程后收集的,我想和你分享一下!

图片来自 unsplash.com

  1. 创建新分支
  • git branch new_branch_name git checkout new_branch_name(或git switch new_branch_name)或
  • git checkout -b new_branch_name
  • git switch -c new_branch_name

2.用旧的提交创建一个新的分支,从那里继续工作

git log  (to check the commit id)
git checkout id (to pass related commit)
git checkout -b new_branch_name commit_id (create a new branch with that commit) 

3.舞台机制

您已经暂存了一些文件(在您的跟踪区域中),并且您在这些文件中有一些未暂存的更改,您想要删除这些更改。

  • git restore filename
  • git checkout filename删除特定文件中的修改

  • git restore .或者
  • git checkout .删除这种情况下的所有修改。

您有未暂存的文件,并且您想从添加这些文件的地方返回

  • git clean -dn (to check which files you are going to delete) git clean -df (to delete these files)

您想取消转移一些已经转移的文件,告诉 git 不要跟踪它们

  • git restore --staged filenamegit reset filename

检查哪些文件在您的暂存区域中

  • git ls-files

4.删除提交

  • git reset HEAD~n
    git reset --SOFT HEAD~n返回 n 次提交(删除 n 次提交),不删除变更
  • git reset --HARD HEAD~n通过从您的工作存储库中删除变更来删除 n 个提交。

5.隐藏机制

您已经进行了一些更改,但是没有提交,因为它还没有准备好提交。你想要改变你的分支,但是你不希望这些阶段性的改变进入你刚刚传递到的分支。您必须将这些更改保存在内存中,以免丢失,不会带到另一个分支,并且能够在需要时保留它们。

  • git stash把它们保存在记忆中,让它们暂时消失
  • git stash apply收回更改
  • git stash apply n仅获取隐藏存储器中 id n 的变化
  • git stash pop取回更改并将其从存储存储器中删除(n 参数也适用,如在 apply 中)
  • git stash list查看你的收藏区有什么
  • git stash clear删除你所有隐藏的记忆
  • git stash drop n删除 id 为 n 的 stash(你可以用上面提到的“git stash list”命令检查 id!!)

6.带来丢失的数据

您希望恢复已删除的提交或分支:

  • git reflog查看过去 30 天内发生的所有事件及其哈希 id。因此,与 git log 命令相比,这里我们不仅可以看到提交历史,还可以看到我们执行的每个步骤,如切换分支、转移、存储、删除提交或分支等。
  • git reset --hard hash_id恢复已删除的提交
  • git checkout hash_id git checkout -b new_branch_name恢复已删除的分支

7.合并类型

合并操作主要有两种类型:快进非快进,分别有 1 和 4 种不同的应用方式。在本教程中,对于非快进类别,我只提快进合并和递归合并。

合并类型—按作者排序的图像

快进:在您为您的新特性创建了一个新的分支(姑且称之为特性分支)之后,您提交到这个分支,同时,主分支中没有任何改变。在这种情况下,合并过程非常容易应用,没有任何冲突,我们称这种情况为快进合并。

快进合并结构—按作者排序的图像

快进并没有添加任何额外的提交来提到这个合并操作,它只是把提交从新分支放到主分支。

为了应用这种合并,

  • git checkout master git merge new_branch

现在,当您在主分支中检查您的提交历史时,您将看到来自特性分支的提交也被列出。

递归非快进:在你为你的新特性创建了一个新的分支(姑且称之为特性分支)之后,你向这个分支提交,同时,一些新的提交也在主分支中进行。在这种情况下,我们讨论的是非快进合并。当这两个分支合并时,提交按日期排序。与快进合并相反,在非快进情况下,进行合并操作的提交,并作为主分支的最后一次提交添加。

应用非快速前进合并的命令与快速前进合并相同,很明显快速前进合并是不可能的,并且会导致非快速前进合并。

递归非快进结构—作者图片

在一个非快速向前的合并中,可能有一些冲突需要修复,因为在两个分支中都有一些变化。如果您在特征分支中更改了一个文件,同时在主分支中也更改了该文件,您必须决定接受哪个更改(或两个都接受)来解决冲突并完成合并。另一方面,如果在这两个分支中只更改了不同的文件,则在合并期间不会有冲突。

要修复合并,我建议您使用 Visual Studio 代码接口,因为它可以让您看到差异并更容易地决定做什么:

Visual Studio 合并冲突—按作者排序的图像

8.重置基础机制

我提到过,在非快进合并中,由于在两个分支中进行了不同的提交,所以提交是按日期对齐的。例如,在上图中,不能确定 master 中的提交顺序是 m1 → m2 → m3 → f1 → f2 还是 m1 → m2 → f1 → f2 → m3 还是 m1→m2→f1→m3→m3→F2。

如果您希望按照主分支提交→功能分支提交的顺序排列这些提交,您可以使用 rebase 机制用主分支的最新版本来更改功能分支的基础,并在这个新基础之后添加功能分支提交。该过程还允许您应用快速合并,因为在这种重新组织的情况下,我们将所有在 master 中进行的提交放在 feature 分支中,这意味着当我们合并它们时,master 中没有任何变化。

重置基础和快进合并结构—作者图片

要应用于 rebase,请执行以下操作:

git checkout feature
git rebase master (rebase feature branch using the master branch)
git checkout master
git merge feature (merge the master branch with feature branch via fast forward) 

9.樱桃采摘

有时,您可能希望挑选一些提交添加到主分支,而不是特性分支中的每个提交。在这种情况下,cherry-pick 命令就是您要找的命令!

git checkout feature
git log (check the ids of commits you wanna apply to the master)
git checkout master
git cherry-pick commit_id

Cherry-pick 操作只在您的主分支上添加 1 个提交,作为您挑选的提交的 zip 版本。

本教程是关于重要的 git 命令及其逻辑的。不要忘记 Git 是用来控制你本地计算机上的工作流的,如果你想在云上运行这个库,并在这些本地云库之间进行通信,你必须在 Github 上开一个账户。Git 和 GitHub 之间的一些基本命令如下:

  • git remote add origin URL将 GitHub 云存储库连接到您的本地工作存储库
  • git push origin master将文件从本地存储库加载到云中,如果不是第一次推送,则更新更改(合并)!

基础词汇:

  • Staging 表示用下面的命令
    git add filename (or) git add .将变更添加到 git 跟踪区
  • 提交意味着使用下面的命令
    git commit -m "commit message"将阶段性变更保存为项目的更新版本
  • 分支表示从主目录创建的附加工作目录。主要目标是在不影响主存储库的情况下,在额外的分支上开发新特性,并在特性被测试并准备好实时使用时,通过将您的分支与您的主分支合并来添加这些新特性
  • 主分支意味着默认的主分支附带了工作存储库的 git 初始化。Main 是一些人喜欢使用的另一个常用名称,您可以使用git branch -M branch_name命令更改您的主分支名称

感谢您对本教程的兴趣,我希望它是有帮助的!在学习了 Git 命令及其用法之后,我建议您看一看 GitHub 命令,以便更好地控制您在云和本地存储库之间的版本控制。熟悉 GitHub 总是一个好主意,因为在协作中,云存储库是最常见的方式,而不是本地方式。💥

一种有效的图像异常检测方法

原文:https://towardsdatascience.com/an-effective-approach-for-image-anomaly-detection-7b1d08a9935b

利用来自英特尔 OpenVinoToolkit 的 Anomalib 来基准测试、开发和部署基于深度学习的图像异常检测

图片来源: Pixabay ( Pixabay 许可:免费商业使用)

检测图像异常甚至比检测结构化数据集或来自时间序列数据的异常更加困难。这部分是因为在结构化数据集中,视觉特征比数值特征更难捕捉。这就是深度学习(DL)技术的用武之地,因为深度学习模型可以对图像等非结构化数据进行自动特征提取。

为了方便执行图像异常检测任务,英特尔 OpenVino 推出了 Anomalib ,这是一个 DL 框架,提供了最先进的异常检测算法,用于公共和私有数据集的基准测试。该框架提供了最近文献中描述的异常检测算法的许多现成可用的实现,以及加速定制 DL 模型的开发和实现的工具集合。该框架重点关注无监督的基于图像的异常检测,其目标是识别图像中的异常值或数据集中图像内的异常像素区域。这个框架被开发者(https://openvinotoolkit.github.io/anomalib/)很好地维护着,并且不断地用新的算法和训练/推理插件进行更新。

工业应用领域

现在,如果您想知道为什么 Anomalib 会对您有用,那么我将从我的经验中提到这个框架可以产生巨大影响的以下工业应用领域:

  • 制造过程质量检测:金属螺母、齿轮、容器、密封件等小零件和设备的缺陷检测。该库可以检测不同于正常样品的异常和有缺陷的图案。
  • 从医学图像中检测医学状况:从放射医学图像中检测和定位肿瘤、出血、胸部感染等医学状况是另一个大规模使用案例,在这种情况下,该框架将非常有用

为什么是 Anomalib?

如果你想知道为什么 anomalib 用于无监督图像异常检测,我会列出以下主要原因:

  1. 很难捕捉和注释大量的异常图像数据。这就是为什么无人监管的异常检测是当务之急。
  2. 传统的计算机视觉算法是无效的,因为正常样本和异常样本之间的差异可能非常小。然而,可能存在多种类型的变化,因此很难预先预测所有类型的图像异常。因此,基于 DL 的方法可以自动学习正常和异常特征之间的差异。
  3. 您可以使用 Anomalib 在全球和局部范围内进行检测和定位。因此,您不仅可以识别不同类型的异常,还可以突出显示局部像素级异常区域
  4. 为您自己的用例编写您自己的异常检测器的简单抽象。
  5. 支持高速用例的实时推理过程!

anomalib 可视化工具产生的示例性输出(来源: ArXiv Anomalib 论文

理论——😦

是的,我知道太多的理论会令人厌烦。但重要的是要知道 Anomalib 是如何工作的!我保证我不会抛出太多的技术信息和花哨的术语来使这篇文章变得非常专业。相反,我将介绍这个框架的高层次直观理解和机制。详细了解请阅读关于 Anomalib 的研究文献原文——Anomalib:Akcay 等人的异常检测深度学习库:【https://arxiv.org/abs/2202.08341】T4

架构图

anomalib 框架架构图(来源: ArXiv Anomalib 论文)

上图总结了 anomalib 库从开发到部署的整个端到端流程。

支持的型号

Anomalib 为无监督异常检测和定位方法提供了以下判别和生成方法:

培训用数据

该框架还为越来越多的公共基准数据集提供了数据集适配器,这些数据集来自广泛用于文献中的图像和视频领域。

  • 图像。Anomalib 支持 CIFAR-10 进行快速原型制作,支持 MVTec、BTAD 和 Kolektor 进行真实世界的缺陷检测应用。
  • 视频。该库支持视频数据集,如 ShanghaiTec [10]。目前,视频数据集仅在帧级基础上受支持,因为现有的 anomalib 模型针对图像域进行了优化。作者计划在未来版本中支持视频异常检测模型来解决这个问题。
  • 自定义。除了前面提到的公共数据集,anomalib 还为用户提供了一个数据集接口,以实现定制数据集,在其上可以训练新的和现有的 anomalib 模型。

关于这些数据集的参考、引用和许可,我建议你阅读全文—https://arxiv.org/pdf/2202.08341.pdf

超越基准

这篇论文的作者(https://arxiv.org/pdf/2202.08341.pdf)报告了他们在图像级和像素级得分上的表现,这似乎令人印象深刻!

使用 Anomalib 的 MVTec 数据集类别的图像级 AUROC 分数(来源: ArXiv Anomalib 论文

使用 Anomalib 的 MVTec 数据集类别的像素级 AUROC 分数(来源: ArXiv Anomalib 论文)

对于任何工业应用来说,结果看起来都非常惊人!

代码示例在哪里???

不要担心!在我自己运行了几个用例之后,我确保提供了代码示例!然而,我将在另一篇文章中解释代码的完整工作方式。但是完整的工作代码已经可以在我的 GitHub repo 上找到:https://GitHub . com/adib 0073/unsupervised-anomaly-detection/blob/main/unsupervised-anomaly-detection . ipynb。该算法超级快,非常容易运行和复制的结果!

图像异常检测为可解释的人工智能

你有兴趣了解更多关于图像分类和分割模型的可解释人工智能吗?然后,Anomalib 中提出的方法可能是解释建立在图像数据集上的模型的非常有用的方法。如果你有兴趣了解更多关于可解释 AI 的知识,我推荐阅读这本书: 【应用机器学习可解释技术】 并探索 GitHub 知识库以获得实际操作的代码示例。

https://www.amazon.com/Applied-Machine-Learning-Explainability-Techniques/dp/1803246154?_encoding=UTF8&pd_rd_w=Wr6SJ&content-id=amzn1.sym.716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_p=716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_r=6P2PM599T97MRG7NZD9J&pd_rd_wg=m4qUW&pd_rd_r=6e349d93-5ba0-4bfe-9055-905c0153fe58&linkCode=li3&tag=adib0073-20&linkId=35506e1847de5c011fc57aa66c2b1d8e&language=en_US&ref_=as_li_ss_il

摘要

在本文中,我们讨论了 Anomalib ,这是一个用于培训、基准测试、部署和开发基于深度学习的无监督异常检测模型的综合框架。Anomalib 提供了一组工具,允许对任何图像数据集上的不同无监督异常检测模型进行快速和可重复的比较。开源框架可在:https://github.com/openvinotoolkit/anomalib获得,完整文档可在:https://openvinotoolkit.github.io/anomalib/获得。希望你喜欢这篇文章!在 MediumLinkedIn 上关注我,了解更多关于计算机视觉、可解释的人工智能和机器学习的信息。

https://www.amazon.com/Applied-Machine-Learning-Explainability-Techniques/dp/1803246154?_encoding=UTF8&pd_rd_w=Wr6SJ&content-id=amzn1.sym.716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_p=716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_r=6P2PM599T97MRG7NZD9J&pd_rd_wg=m4qUW&pd_rd_r=6e349d93-5ba0-4bfe-9055-905c0153fe58&linkCode=li3&tag=adib0073-20&linkId=35506e1847de5c011fc57aa66c2b1d8e&language=en_US&ref_=as_li_ss_il

参考

使用 Pytest 测试数据科学管道的简明指南

原文:https://towardsdatascience.com/an-elegant-guide-to-testing-your-data-science-pipeline-using-pytest-4859b0c32591

数据科学项目 Pytest 综合指南

安妮·斯普拉特在 Unsplash 上的照片

动机

今天的数据科学团队面临的最大障碍之一是将他们的数据驱动管道从 jupyter 笔记本过渡到可执行、可再现和有组织的文件,包括函数和类。

对于我们大多数人来说,在数据科学项目的生命周期中,在 jupyter 笔记本上工作是一项有趣而愉快的任务。

然而,构建一个没有错误的、经过充分测试的、可靠的、在生产中不会中断的数据管道却不是这样。

例如,设想这样一种情况,您在数据管道中编写了一个函数,将两个数相除:

在 jupyter 笔记本中工作时,作为程序员,您知道应该在方法中放入什么,以及期望输出什么。

想想在这个简单的划分中到底会有什么地方出错,你在没有测试的情况下把这个方法推向生产,你猜怎么着?

分母为零时管道崩溃!

因此,为了减少数据管道中的错误,并确保函数按照预期的方式工作,在将代码投入生产之前,根据不同的示例输入(或数据情况)对它们进行测试是非常重要的一步。

换句话说,这里的目标是为管道创建一个可靠的自动化框架。

首先,自动化框架被定义为一种有组织的方法来编写测试用例,执行它们,并报告自动化测试的结果。

测试框架概述(图片由作者提供)

通常,您应该始终测试数据科学项目中的函数,因为它:

  • 确保代码端到端按预期运行,从而提高可靠性。
  • 帮助您检测不同方法可能失败的边缘情况。
  • 允许其他团队成员在测试的帮助下理解您的代码——降低代码复杂性。
  • 在不干扰其他功能的情况下,帮助用优化/改进版本交换现有代码。
  • 支持生成关于代码的可操作见解,如运行时、预期输入/输出等。

谢天谢地,Python 提供了少数开源库,如 unittestnosebehavior等。,但在 Python 开发者社区中脱颖而出的是 Pytest

因此,在本文中,我将详细介绍如何利用 Pytest 为您的数据科学项目构建测试套件。

我们开始吧🚀!

Pytest 入门

作为一个快速介绍,Pytest 是一个测试框架,用于编写测试代码,在 Python 中的编码管道上执行它们,并生成相应的测试报告。

在我看来,Pytest 最大的优点是它与数据管道集成的直观和简单的过程。

测试已经是一项数据科学家们不太感兴趣的工作了。

在这方面,Pytest 真正使编写测试套件的任务变得简单而有趣,这反过来极大地有助于确定数据科学项目的可靠性。

要安装 Pytest,请执行以下命令:

接下来,您应该导入 Pytest 来利用它的核心测试功能。

这样,您就可以编写测试用例并执行它们了。

将 Pytest 投入使用

我们已经在project.py中编写了divide_two_numbers()方法。

接下来,让我们为测试创建一个单独的文件(test_file.py)。

project
├── project.py
└── test_file.py

对于我们上面定义的方法(divide_two_numbers()),我们来写一个测试函数。

这将根据不同的输入检查该方法返回的输出的数据类型。

要执行这个测试功能,打开当前工作目录中的命令行并运行命令pytest:

正如所料,该方法在第二次断言时失败,分母为零,这允许您更正函数。

所有这些都很好,但现在你应该有两个问题:

  1. 在上面的pytest命令中,我们从未指定测试文件的名称。然而,在结果中,我们只看到了对test_file.py的提及。Pytest 是如何推断出test_file.py是我们列出所有测试用例的特定文件的?
  2. 接下来,pytest 是如何找到它需要执行的方法的?换句话说,pytest 是如何将divide_two_numbers()test_divide_two_numbers_method()区分开来,并指出测试用例是在后一种方法中列出的?

答案在于 Pytest 中的“测试搜索”(也称为“测试发现”)技术。

Pytest 中的测试搜索

Pytest 实现了以下测试发现方法来查找测试函数、文件和 Python 类:

  1. 如果在 Pytest 执行期间没有指定目录(或文件), Pytest 将从当前目录开始发现,并递归到子目录中。
  2. 在每个目录中,它在文件名中搜索一个模式:test_*.py*_test.py。这就是为什么在上面的测试中,project.py文件没有被选中。
  3. 从模式匹配的文件中,它收集:
  • 一个 Python 类的所有"test”前缀方法之外。例如,从下面的方法中,只有test_method1()会被收集,而method2_test()不会。

  • 所有"test"前缀的方法内,一个"Test”前缀的 Python 类没有__init__()方法。

例如,在上面的代码块中,从Test_Class1开始,只有test_method1()会被收集,因为它以关键字"test"为前缀。

此外,不会从Test_Class2收集任何方法,因为它有__init__()方法。

最后,不会从Class3_Test收集任何方法,因为类名没有以关键字"Test"为前缀。

自定义测试搜索

虽然 Pytest 的默认测试发现机制直观明了,但是在某些情况下,您可能需要定制测试搜索模式。

为此,您可以在项目根目录中声明的配置文件(pytest.ini)中配置不同的命名约定。

Pytest 还支持对文件扩展名.ini的替代,你可以在这里阅读

该目录现在的结构如下:

project
├── project.py
├── test_file.py
└── pytest.ini

我们在配置文件中设置了三个变量来定制搜索模式。这些是python_filespython_classespython_functions。这里有一个例子:

使用这种搜索模式,Pytest 将在以"check_"为前缀的文件中搜索测试函数。

此外,测试函数也应该以"check_"为前缀。

最后,测试类(如果有的话)应该以关键字"_Check"结尾,以便考虑进行测试。

现在,如果我们运行pytest命令,我们会得到以下输出:

Pytest 未识别出任何测试,因为新定义的测试搜索模式与目录中的任何方法和文件都不匹配。

要纠正这一点,您应该将**test**_file.py重命名为**check**_file.py,将**test**_divide_two_numbers_method()重命名为**check**_divide_two_numbers_method()

project
├── project.py
├── check_file.py
└── pytest.ini

遵循测试搜索模式后,我们重新运行该命令并获得以下输出:

在这里,我删除了被零除测试用例。

要获得详细的测试输出,请在运行pytest命令时使用-v选项。

标记测试

假设您的管道中有各种模块,它们一起工作来完成一个共同的目标。

具有多个模块的代码管道(图片由作者提供)

当然,每个模块也可能有自己的测试套件。

假设您对一个特定的模块(比如说模块 1)做了一些修改,您希望在提交修改之前对其进行测试。

在这种情况下,运行整个测试套件将是多余和耗时的,因为您没有对管道的其他模块进行任何更改。

此外,其他模块可能已经在之前的测试运行中彻底测试过。

这就是 Pytest 中的测试标记派上用场的地方。

顾名思义,标记允许您将测试组织成不同的类别。

在测试时,您可以使用这些标记通过 Pytest 运行特定的测试——避免不必要的计算和运行时使用。

从技术上讲,您可以使用@pytest.mark.{marker-name}装饰器来标记一个测试。

实现标记

在上面的划分示例中,让我们定义四种不同的测试方法,如下从check_function1()check_function4():

要标记测试函数,您应该导入标记,如上所示。

接下来,我们用mark.non_zero_input decorator 标记前三个方法,用mark.zero_input decorator 标记最后一个方法。

当您定义一个自定义标记时(就像我们上面所做的),在配置文件中指定它是一个好的实践(pytest.ini)。

它有两个主要好处:

  1. 它避免了 Pytest 可能抛出的任何不必要的警告。
  2. 如果有人想了解您的管道及其测试套件,在配置文件中声明自定义标记可以帮助他们了解他们的角色,这将有助于他们毫无麻烦地使用您的代码。

您可以在配置文件中声明自定义标记,如下所示:

接下来,假设您只想运行那些对应于non_zero_input标记的测试函数。

您可以使用命令:pytest -m {marker-name}来做到这一点。下面演示了这一点:

在上面的测试输出中,我们注意到 Pytest 忽略了check_function4()进行测试,只采用了在non_zero_input标记下定义的前三个方法。

关于 Pytest 中的标记要知道的事情

除了我们上面讨论的关于 Pytest 标记的概述之外,还有一些关于它们的事情你应该知道。

→ #1 一个功能可以有多个标记

在 Pytest 中,测试函数不一定属于单个标记。相反,您可以将多个标记定义为单个函数的装饰者,如下所示:

→ #2 您可以为整个班级定义一个标记

对于编写模块化代码,Python 中的类通常是测试代码管道的首选,因为一个类通常有多个方法。

要为类中定义的函数声明一个标记,可以为整个类指定一个标记,Pytest 将其扩展到类中的每个方法。

但是,如果希望不同的方法属于不同的标记,也可以为类中的每个方法单独声明标记。

→# 3 py test 中的一些预定义标记

除了上面讨论的自定义标记,Pytest 还提供了一些标记,涵盖了开发人员在测试过程中可能需要的一般功能。

  • **mark.skip**:顾名思义,在一个方法上声明这个标记,是为了在测试过程中有意跳过它。例如,考虑下面的测试:

现在,如果您执行pytest命令,我们会得到以下输出:

在上面的输出中,我们注意到一个跳过了测试,以及在为方法定义skip装饰器时指定的原因。

  • **mark.xfail**:Pytest 提供的另一个有用的固有装饰器是xfail(简称 expected-failure )。

假设您意识到一个特定的测试函数没有被正确编码。因此,您预期它在某些情况下会失败。

使用xfail标记,您可以在您的测试套件中将测试功能标记为预期的失败。

现在,如果您执行pytest命令,我们会得到以下输出:

在上面的输出中,我们注意到一个 XFAIL 测试,以及在为该方法定义xfail装饰器时指定的原因。

您可以使用以下命令在 Pytest 中找到预定义标记的完整列表:

书写固定装置

编写模块化代码是开发大型项目的关键步骤。良好的开发实践表明:

如果你写同样的代码两次,你就做错了!

这同样适用于设计测试套件。

Pytest 中的 Fixtures 类似于项目中的通用函数,可以在管道中的任何地方使用。

唯一的区别是函数应该显式导入,而 fixtures 则不需要。

Fixtures 不仅减少了您编写的代码量,还使您的测试套件简明易懂。

创建夹具

通常,在 Pytest 中,您应该在一个独立于测试用例的文件中声明您的 fixtures。

出于演示目的,让我们创建一个文件conftest.py

project
├── project.py
├── test_file.py
├── conftest.py
└── pytest.ini

fixture装饰器的帮助下,夹具被声明如下:

在这个例子中,我们的目标是针对相同的输入测试加法和减法模块。

一种简单的方法是在两个函数中定义测试输入。

然而,使用 fixtures,您可以将测试输入作为参数传递给测试方法。

测试函数收到的参数应该与夹具名称test_inputs相同

此外,如果你注意到,我们从来没有进口设备。

永远记住,您在 fixtures 文件中创建的任何 fixture 都可以被当前目录和每个子目录中的每个测试函数递归地访问。

现在,如果我们执行pytest命令,我们会得到以下输出:

总而言之,在pytest.fixture装饰器的帮助下,我们在一个单独的文件中定义了一个夹具。

当 fixture 的名称作为任何测试方法中的参数传递时,Pytest 获取 fixture 返回的对象。

固定装置的范围

在上面夹具的定义中,我们在装饰器中传递了scope参数。

从本质上讲,设备的主要目的是分发信息。

在 fixture 声明的上下文中,Scope 定义了当被测试函数调用时,fixture 应该多长时间返回一次对象的新副本。

例如,在上面的例子中,scope=’function’表示 fixture 将为每个函数创建一个新的输入列表副本。

换句话说,fixtures 是在测试第一次请求时创建的,并根据它们的scope销毁,T1 可以取以下五个值之一:

  • scope = 'function':这是 fixture 的默认范围,每个测试功能创建一个新的副本。
  • scope = 'class’:夹具的单个副本可用于一个类中的所有测试方法。
  • scope = 'module':每个模块调用一次夹具。
  • scope = 'package':夹具的作用范围在包级别。
  • scope = 'session':测试套件的每次运行只创建一次 fixture。

结论

至此,我们结束了关于使用 Pytest 开发测试套件的介绍性博客。恭喜你!

我希望这篇文章让您详细了解了为什么测试是必不可少的,以及如何利用 Pytest 来测试您的数据科学项目。

还有几件事,比如参数化,生成测试报告等。我很快会在另一篇博客中发表:)

感谢阅读!

🧑‍💻成为数据科学专家!获取包含 450 多个熊猫、NumPy 和 SQL 问题的免费数据科学掌握工具包。

✉️ 注册我的电子邮件列表 不要错过另一篇关于数据科学指南、技巧和提示、机器学习、SQL、Python 等的文章。Medium 会将我的下一篇文章直接发送到你的收件箱。

我喜欢写基本的数据科学概念、工具、技巧和诀窍。你可以在 LinkedIn 上和我联系。

用于发布 Python 包的 GitHub 库

原文:https://towardsdatascience.com/an-end-to-end-guide-to-publish-your-python-package-bdb56639662c

这是一个端到端的演示,旨在促进共享开源软件或内部工作的过程

照片由西格蒙德Unsplash 拍摄

这是一个共享代码的端到端演示。该项目旨在帮助您理解如何在 PyPI 上正确地对外发布您的 python 模块/包,并在内部与您的同事分享您的工作。我还为你准备了 GitHub 回购,让你的生活更轻松。

更多详情可以直接去这个回购: GitHub 回购

第一步

分别去下面两个网站注册自己的账号。

注意:如果您不想公开发布您的软件包,可以跳过这一部分。此外,我强烈建议首先在测试网站上尝试您的包,以避免上传过程中的错误。因为你在 PyPI 上对你的包所做的任何改变都是不可恢复的,上传错误可能会导致你的包补丁出现故障。你要避免这种情况!!

第二步

选项 1:将这个库分支到您自己的 GitHub 帐户,并在您的本地。您可以在 GitHub 上进行大部分的修改,但是您需要通过 cmd 发布您的包,并且这些文件在本地可用。

选项 2:手动创建必要的文件。这里是您需要的核心文件列表:

作者图片

我知道这很难接受。但是请原谅我。您只需要对其中一些进行必要的修改,其余的将保持默认设置。

可选—多个模块

假设您有多个类,它们的函数在不同的文件中创建。您希望使文件夹(或子文件夹)遵循以下约定:

作者图片

在每个文件夹中,更新“init”。py”通过这样做:

在主文件夹中,更新“init”。py”通过这样做:

然后,用户将能够像这样正确地导入您的库:

如果你想从主(前一个)文件夹中导入一些东西,你应该这样做:

请考虑在您的“init”中添加一个列表。py ”,以便用户可以检查哪些功能可用:

第三步

在 cmd 中安装以下 python 包:

第四步

按照您想要的任何顺序进行以下更改:

  1. 用自己的 python 包替换 src 文件夹中的 your_main_code.py,并保留“init”。如果你只是发布一个模块。
  2. 对 setup.py 进行更改,说明包含在该文件中。
  3. 挑选你自己的执照。打开许可证文件,单击“编辑”,单击“选择许可证模板”,然后选择满足您需求的许可证。如果你不知道哪个许可证适合你,你可以使用麻省理工学院的许可证,这是最常见的选择之一。或者,你可以用这个链接来挑选一个:https://choosealicense.com/
  4. 更新 CHANGELOG.md 以反映版本信息
  5. 可选:创建一个“test.py”并将该文件放在 tests 文件夹中。或者,如果您确信您的模块一切正常,您可以删除整个文件夹。
  6. 删除这个“README.md”文件中的所有内容,并用您的软件包的详细描述更新该文件。

第五步

对于步骤 5,您有多种选择来执行其余步骤。这里有两个例子:

  1. 在 cmd -命令提示符下执行此操作
  • 在您的本地中,打开 cmd,导航到您的软件包所在的目录,然后键入以下内容:

2.在 Jupyter 笔记本终端中完成:

第六步

在这一步中,我们将使用 cmd/terminal 中的以下代码来构建您的包:

运行代码后,您将在当前目录中看到以下两个文件夹:

  • 建设
  • 距离

在 dist 文件夹下,你会看到一个名为“TheNameofYourPackage-the versionofyourpackage . tar . gz”的‘tar’文件。到目前为止,如果您不需要公开发布您的代码;相反,如果您只是想与您的朋友或同事共享您的代码,您可以只与他们共享这个文件。他们只需执行“pip 安装”即可使用您的代码:

可选—在 PyPI 上测试您的包

现在,您将要向 PyPI 发布您的包。在你公开它之前,你可能想做的另外一件事是测试一旦人们下载了它们,你的包是否会像预期的那样工作。您可以做的是创建一个名为“test”的文件夹,并创建一个 test.py,其中包含您的包的一些示例实现。然后,在你的 cmd/终端中输入“pytest”。如果一切正常,它将自动运行 test.py 并通过测试。否则,它会引发错误,您应该在进入下一步之前相应地修复错误。

还有一件事你可能想试着测试一下你的包的架构是否适合。在 cmd/terminal 中,键入以下代码:

您应该会看到类似这样的内容:

Checking distribution dist/TheNameofYourPackage-TheVersionofYourPackage-1.0.0-py3-none-any.whl: PassedChecking distribution dist/TheNameofYourPackage-TheVersionofYourPackage.tar.gz: Passed

第七步

将您的包上传到 TestPyPI:

然后,您会在 TestPyPI 上看到一个指向您的包的测试版本的链接。看看吧!如果有任何打字错误或不兼容的错误,在把你的包上传到真正的 PyPI 之前修复它们。

现在,这是最激动人心的时刻,将您的包上传到 PyPI,帮助我们社区中成千上万的人:

至此,您的软件包应该已经正式上线,可以由任何人在任何时间、任何地点进行“pip 安装”。重大时刻!我仍然记得当我看到我的第一个包裹时的感觉。我告诉自己,这就是我编码的原因!

恭喜恭喜!!!

一些提示

  • 每当你想更新你的软件包时,你应该删除“build”和“dist”文件夹,修改你的代码,编辑“CHANGLOG.txt”文件,并修改“setup.py”中的版本号。并重复步骤 5–7。
  • 您可以在更新后通过以下方式升级您的软件包: pip 安装您的软件包名称—升级
  • 你总能在 PyPi 上找到你的包裹:http://pypi.org/project/YOURPACKAGENAME/
  • 不要随意发布包。尽管对你能发表什么或不能发表什么没有硬性限制,但要确保你上传的东西实际上是有意义的,并且有人会从你的工作中受益。

端到端机器学习项目:心力衰竭预测,第 2 部分

原文:https://towardsdatascience.com/an-end-to-end-machine-learning-project-heart-failure-prediction-part-2-4518d2596421

web 应用程序中的模型部署

欢迎来到端到端机器学习项目系列的第二部分!在第一篇文章中,我们训练、验证、调整并保存了一个使用患者信息来预测心力衰竭概率的机器学习模型。在本文中,我们将开发一个 web 应用程序,任何人都可以通过它与我们的模型进行交互。应用程序的前端将由 HTML、CSS 和 JavaScript 处理,后端将使用 Flask 用 python 编写。和往常一样,文章中使用的所有代码都可以在 GitHub 上找到。应用程序的部署版本可以在这里找到。

心力衰竭预测应用程序。图片作者。

目录结构

让我们首先检查这个应用程序的目录结构:

-/ flaskapp
   -/ models
     - heart_disease_model_2021-12-28
   -/ services
     - model_utils.py
   -/ static
     -/css
     -/ fonts
     -/ img
     -/ js
   -/ templates
     - index.html
   - api_routes.py
   - app.py 
-/ notebooks
requirements.txt

这个项目的两个主要目录是“flaskapp”和“notebooks”。“笔记本”目录包含我们在上一篇文章中用来训练模型的代码。“flaskapp”目录包含将托管心力衰竭模型的 web 应用程序的所有代码。我们可以通过将“flaskapp”目录分成前端和后端组件来考虑它的内容。所有的前端组件都包含在“静态”和“模板”目录中。目录的其余部分包含后端代码和文件。

前端

模板

“模板”目录的目的是存储应用程序中使用的所有 HTML 文件。这个特殊的应用程序只有一个页面,这个页面的结构是由 index.html 决定的。让我们从主要标题开始,浏览一些主要部分:

主标题。图片作者。

为了显示这一点,我们简单地将“h1”标记包装在“nav”标记中,如下所示:

<!-- Navbar --><nav class="navbar navbar-main navbar-expand-lg px-0 mx-4 shadow-none border-radius-xl" id="navbarBlur" navbar-scroll="true"> <h1>Heart Failure Prediction</h1></nav>

在‘nav’标签中,我们首先指定一个决定导航条样式的类——我将在下一节解释这个类的来源。接下来,我们指定一个 id,允许我们通过 JavaScript 或 HTML 的其他部分访问 navbar 的任何属性。最后一个参数“navbar-scroll”决定了当用户滚动时,navbar 是否固定在屏幕上。通过将 navbar-scroll 设置为 true,我们可以确保用户在滚动时总能看到 navbr。

向下移动页面,我们有接受用户输入的部分:

用户输入部分。图片作者。

这可以说是 HTML 文件最关键的部分,因为这些输入将通过我们的机器学习模型运行。为了创建这个部分,我们将利用Bootstrap——一个前端框架,允许开发人员轻松创建现代响应应用程序。特别是,我们将实现自举网格系统,它允许我们将页面布局组织成行和列。为了说明这一点,让我们看看第一行输入:

第一个输入行。图片作者。

<div class="row"> <div class="col-lg-4"> <p style="color: white;">Sex</p> <select class='form-control' name="sex" id="sex" style="width:100%;"> <option value="M">Male</option> <option value="F">Female</option> </select> </div> <div class="col-lg-4"> <p style="color: white;">Resting ECG</p> <select class='form-control' name="restingECG" id="restingECG" style="width:100%;"> <option value="Normal">Normal</option> <option value="ST">ST</option> <option value="LVH">LVH</option> </select> </div> <div class="col-lg-4"> <p style="color: white;">Chest Pain Type</p> <select class='form-control' name="chestPainType" id="chestPainType" style="width:100%;"> <option value="ATA">ATA</option> <option value="NAP">NAP</option> <option value="ASY">ASY</option> <option value="TA">TA</option> </select></div></div>

我们首先用类“row”创建一个 div。紧随行 div 之后,我们创建第一个列 div,它包含第一个下拉输入。请注意,该类是“col-lg-4”。每个引导行最多可以包含 12 列;通过将类命名为“col-lg-4”,我们确保了这个 div 将使用父行中 12 个可用列空间中的 4 个。在该列中,我们指定输入的名称,然后用该特性的所有惟一值创建一个 select 标记。例如,以下 HTML 对应于“胸痛类型”下拉列表:

<p style="color: white;">Chest Pain Type</p><select class='form-control' name="chestPainType" id="chestPainType" style="width:100%;"> <option value="ATA">ATA</option> <option value="NAP">NAP</option> <option value="ASY">ASY</option> <option value="TA">TA</option></select>

图片作者。

图片作者。

需要注意的是,我们给每个“select”标签一个名称和一个 id。我们稍后在 JavaScript 中访问下拉列表的值时会用到这些字段。

我们可以通过简单地使用“input”标签并将类型指定为“number”来创建数字输入。

<div class="col-lg-4"> <p style="color: white;">Age</p> <input class='form-control' type="number" id="age" name="age"     value="40" min="0" max="120" style="width:100%;"></div>

输入患者年龄。图片作者。

请注意,我们将默认年龄设置为 40,最小年龄设置为 0,最大年龄设置为 120。对于年龄来说,这些是非常容易的选择,但是,一般来说,我们可以使用每个数字输入的分布来确定默认值、最小值和最大值。

index.html 文件中最后一个主要组件是“运行模型”按钮。

<div class="col-lg-4"> <p style="color: white;">Calculate Heart Failure Probability</p> <button id='makePred' class="btn btn-outline-primary btn-md mb-0" style="width:100%; background-color: white;">Run model</button></div>

运行模型按钮。图片作者。

同样,我们必须给这个按钮一个 id,以便它可以在以后用来触发事件。

静态— CSS

层叠样式表(CSS)是一个设计网页样式的框架,这也是为什么我们有漂亮的网页,看起来不像是在 1990 年写的。我不会假装自己是 CSS 专家,但我知道的足够多,足以给出这个应用程序的 CSS 要求的概述。

这款应用的风格改编自创意蒂姆提供的开源 HTML、CSS 和 JavaScript 模板;所有文件都可以在“static/css”目录下找到。为了说明样式是如何工作的,让我们看一下 soft-ui-dashboard.css 中的“表单控制”类:

.form-control {display: block;
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.4rem;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #d2d6da;
appearance: none;
border-radius: 0.5rem;
transition: box-shadow 0.15s ease, border-color 0.15s ease;}

“form-control”类负责进行这种史前输入:

史前的 HTML 输入。图片作者。

看起来像来自现代网络应用程序的输入:

风格化输入。图片作者。

假设我们感觉很狂野,希望我们的输入有一个橙色的背景。只需在 CSS 文件中进行以下更改:

.form-control {display: block;
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.4rem;
color: #495057;
***background-color: rgb(250, 186, 11);*** /* Change the background color */
background-clip: padding-box;
border: 1px solid #d2d6da;
appearance: none;
border-radius: 0.5rem;
transition: box-shadow 0.15s ease, border-color 0.15s ease;}

我们得到了喷雾鞣制的投入:

用橙色背景色输入。图片作者。

任何浏览过该项目的 repo 的人都会注意到 CSS 文件相当广泛。对于任何有兴趣学习更多 CSS 知识的人来说,这里有大量的资源。

静态— JavaScript

JavaScript 是世界上最流行的编程语言,负责管理网页上发生的所有操作。JavaScript 在这个项目中的主要应用是充当前端(所有 HTML、CSS 和 JavaScript 代码)和后端(用 Flask 编写的服务器端代码)之间的桥梁。我们将使用 jQuery ,一个流行的经过时间考验的 JavaScript 库,向 Flask 发送请求。这些请求将包含心力衰竭模型进行预测所需的所有输入。该模型将使用请求输入进行预测,Flask 将把预测(以及我们需要的任何其他数据)发送回 jQuery。JQuery 将解析预测,并将它们发送回 index.html 文件供用户查看。

在深入研究 jQuery 代码之前,我们必须在 index.html 文件中进行三次导入:

<!-- Jquery CDN -->
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script><!-- JS file to get predictions from server -->
<script src="../static/js/api.js"></script><!-- Plotly CDN-->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

第一次导入允许我们通过 jQuery 内容交付网络(CDN)使用 jQuery。这是一个包含 jQuery 库的公共 JavaScript 文件。接下来,我们导入将与 Flask 交互并返回预测的本地 JavaScript 文件。最后,导入 plotly.js CDN,这样我们就可以在页面上显示图形。

现在是有趣的部分。所有的操作都使用了 50 行 jQuery:

$(document).ready(function(e) { $('#makePred').click(function() { $('#hfProb').empty();
      $('#hfPred').empty();

      var sex = $('#sex').val(); var restingECG = $('#restingECG').val(); var cpt = $('#chestPainType').val(); var exerciseAngina = $('#exerciseAngina').val(); var sts = $('#stSlope').val(); var age = $('#age').val(); var bp = $('#restingBP').val(); var chol = $('#cholesterol').val(); var bs = $('#fastingBS').val(); var maxHR = $('#maxHR').val(); var oldpeak = $('#oldpeak').val();

      var inputData = { 'sex': sex, 'restingECG': restingECG, 'cpt': cpt, 'exerciseAngina': exerciseAngina, 'sts': sts, 'age': age, 'bp': bp, 'chol': chol, 'bs': bs, 'maxHR': maxHR, 'oldpeak': oldpeak }; $.ajax({ url: 'main/api/make_prediction', data: inputData, type: 'post', success: function(response) { $('#hfProb').append(`<p style="color:white;">Patient has a   ${response['pred']} probability of heart failure</p>`) var figure = JSON.parse(response['plot']); Plotly.newPlot('hfPlot', figure.data, figure.layout, { displayModeBar: false, responsive: true });      } }) });});

首先,我们必须告诉 jQuery 在执行任何操作之前让整个网页呈现出来。我们使用下面的代码行来实现这一点:

$(document).ready(function(e) {   });

接下来,我们创建一个事件监听器来监视“运行模型”按钮。

$('#makePred').click(function() {});

回想一下“#makePred”是 index.html“运行模型”按钮的 id。每次单击“运行模型”时,这个事件监听器将执行花括号内的任何代码。在我们的例子中,我们希望将患者数据发送到服务器并获得预测结果。为此,我们必须首先访问输入的值。例如,下面的块将把病人的年龄和血压存储为 JavaScript 变量:

var age = $('#age').val();
var bp = $('#restingBP').val();

我们重复这个过程,然后将所有变量存储在一个类似于字典的 JavaScript 对象中:

var inputData = {'sex': sex,'restingECG': restingECG,'cpt': cpt,'exerciseAngina': exerciseAngina,'sts': sts,'age': age,'bp': bp,'chol': chol,'bs': bs,'maxHR': maxHR,'oldpeak': oldpeak };

“inputData”变量是将被转换为 JSON 并发送给服务器的对象。最后一步是通过 AJAX 请求将患者的数据发送到服务器。通俗地说,AJAX 允许我们在不刷新网页的情况下与服务器进行交互。让我们分解我们的 AJAX 请求:

$.ajax({url: 'main/api/make_prediction',data: inputData,type: 'post',success: function(response) {$('#hfProb').append(`<p style="color:white;">Patient has a   ${response['pred']} probability of heart failure</p>`)var figure = JSON.parse(response['plot']);Plotly.newPlot('hfPlot', figure.data, figure.layout, {displayModeBar: false,responsive: true });}})});

AJAX 主体中的“url”参数告诉 jQuery 我们试图访问的资源在服务器上的位置;我们将在下一节在 Flask 中对此进行定义。“数据”参数是包含患者信息/模型输入的对象。通过将“type”指定为“post”,我们告诉服务器我们正在向它发送数据以进行处理(患者数据)。“url”、“数据”和“类型”参数都是我们发出请求所需要的。

一旦服务器处理了请求并返回了结果,我们就可以告诉 jQuery 如何处理返回的数据。这是通过在 AJAX 主体中填充“success”参数来实现的:

success: function(response) {$('#hfProb').append(`<p style="color:white;">Patient has a   ${response['pred']} probability of heart failure</p>`)var figure = JSON.parse(response['plot']);Plotly.newPlot('hfPlot', figure.data, figure.layout, {displayModeBar: false,responsive: true });}

请注意,我们正在传入一个函数,该函数接受一个变量“response”。该变量包含从服务器发回的数据,可能如下所示:

来自服务器的响应对象。图片作者。

在我们的例子中,响应对象有一个用于模型预测的条目和一个用于风险因素图的条目(这将在下一节中解释)。现在我们可以对数据做任何我们想做的事情!

要在我们的网页上显示患者心力衰竭的概率,我们可以使用“附加”方法:

$('#hfProb').append(`<p style="color:white;">Patient has a   ${response['pred']} probability of heart failure</p>`)

大概是这样的:

图片作者。

最后,我们可以使用 plotly.js 呈现风险系数图:

var figure = JSON.parse(response['plot']);Plotly.newPlot('hfPlot', figure.data, figure.layout, {displayModeBar: false,responsive: true });

这给了我们:

图片作者。

后端

Flask 是驱动这个应用后端功能的主力。在我们的上下文中,后端的主要目的是从前端接收患者数据,转换数据,通过心力衰竭模型运行数据,并将预测返回给前端。作为复习,让我们看一下 Flask 应用程序的目录结构:

-/ flaskapp
   -/ models
     - heart_disease_model_2021-12-28
   -/ services
     - model_utils.py
   -/ static
     -/css
     -/ fonts
     -/ img
     -/ js
   -/ templates
     - index.html
   - api_routes.py
   - app.py

经训练的心力衰竭模型存储在“模型”目录中;这不要与来自 MVC 框架的模型概念相混淆。“服务”目录包含允许我们模块化心力衰竭模型使用的代码。实际的烧瓶代码包含在“api_routes.py”和“app.py”中。

让应用程序运行

我们先来看看“app.py”:

from flask import Flask, render_template
from api_routes import bp1app = Flask(__name__)
app.register_blueprint(bp1)@app.route('/')
def index():
  return render_template('index.html')if __name__ == "__main__":
  app.run(debug=True)

幸运的是,Flask 抽象出了服务器端代码的许多复杂性。这意味着我们只需要几行代码就可以让我们的应用程序运行起来!我们从导入一些基本的依赖关系开始:

from flask import Flask, render_template
from api_routes import bp1

导入“flask”类是为了创建一个 app 实例,而“render_template”函数是 Flask 用来定位和显示 HTML 文件的。“bp1”导入是一个烧瓶蓝图。诚然,蓝图可能有点难以把握。然而,对于这个应用程序,蓝图是一种隔离和模块化 API 代码的方法。当更多的页面和功能被添加到一个应用程序时,这就很方便了。接下来的两行代码创建 Flask 应用程序并注册 API 蓝图:

app = Flask(__name__)
app.register_blueprint(bp1)

接下来,我们定义一个名为“index”的函数,该函数将“index.html”文件呈现到主目录(“/”)中。这意味着“index.html”是我们在访问基本 url 时看到的页面。

@app.route('/')
def index():
  return render_template('index.html')

“app.py”的最后一步是运行应用程序:

if __name__ == "__main__":
  app.run(debug=True)

这就是我们让应用程序运行所需的一切!只需在终端中运行“python app.py ”,应该会出现类似如下的内容:

运行终端的 Flask 应用程序。图片作者。

在这种情况下,当我们点击“http://127.0.0.1:5000/”时,我们可以看到我们的应用程序正在运行:

Flask 呈现的网页。图片作者。

与前端交互

这是橡胶与路面真正相遇的地方。最后两个文件决定了数据如何流经后端并返回前端。从“model_utils.py”开始,我们导入依赖项并将心力衰竭模型加载到 Catboost 对象中:

import numpy as np
import pandas as pd
from catboost import CatBoostClassifier, Pool
import plotly.graph_objects as go# Global variable that stores trained model instance
MODEL = CatBoostClassifier().load_model('models/heart_disease_model_2021-12-28')

然后,我们创建一个字典,将前端特征名称转换为模型能够理解的特征名称:

# Dictionary that converts frontend feature names to names understood by modeldisplay_to_model = {'age':'Age', 'sex':'Sex', 'cpt':'ChestPainType', 'bp':'RestingBP','chol':'Cholesterol', 'bs':'FastingBS', 'restingECG':'RestingECG','maxHR':'MaxHR', 'exerciseAngina':'ExerciseAngina', 'oldpeak':'Oldpeak','sts':'ST_Slope'}

另外,在大多数生产软件中,字符串映射是用枚举处理的;对我们来说,字典映射完成了工作。在此之后,我们定义一个接受患者数据、重命名特征并返回模型的心力衰竭概率预测的函数:

def predict_hf(data:pd.DataFrame): # Make sure column names are correct
   data_predict = data.rename(display_to_model, axis=1) # Make sure columns are in the correct order
   data_predict = data_predict[MODEL.feature_names_] return MODEL.predict_proba(data_predict)

接下来的两个函数用于根据 SHAP 值生成风险因素图:

def get_shap_df(data:pd.DataFrame): # Make sure column names are correct
   data_predict = data.rename(display_to_model, axis=1) # Make sure columns are in the correct order
   data_predict = data_predict[MODEL.feature_names_] data_pool = Pool(data_predict,  cat_features=MODEL.get_cat_feature_indices()) shap_values = MODEL.get_feature_importance(data_pool,  type='ShapValues') shap_values = shap_values[:,:-1].reshape(shap_values.shape[0], len(MODEL.feature_names_)) shap_df = pd.DataFrame(shap_values, columns=MODEL.feature_names_).T shap_df.columns = ['feature'] shap_df['AbsVal'] = np.abs(shap_df['feature']) shap_df.sort_values('AbsVal', ascending=False, inplace=True) return shap_dfdef plot_shap_values(data:pd.DataFrame): shap_df = get_shap_df(data) fig = go.Figure()
   fig.add_trace(go.Bar(x=shap_df.index, y=shap_df.feature))
   fig.update_layout(title='Patient Risk Factors') return fig.to_json()

最后,我们转到“api_routes.py”文件:

import pandas as pd
from flask import Blueprint, request
from services.model_utils import predict_hf, plot_shap_valuesbp1 = Blueprint('main', __name__, url_prefix='/main')@bp1.route('/api/make_prediction', methods=['POST'])
def make_prediction(): # Read in the patient data from the frontend and convert to df
   form_df:pd.DataFrame = pd.DataFrame(request.form, index=[0]) # Make heart failure prediction  
   pred = predict_hf(form_df)
   pred_class_1 = pred[0][1] # Get risk factor plot
   plot = plot_shap_values(form_df)

   # Return prediction and plot back to frontend 
   return {'pred':round(pred_class_1, 2), 'plot':plot}

该文件导入依赖项,包括我们编写的用于进行预测和风险因素绘图的函数:

import pandas as pd
from flask import Blueprint, request
from services.model_utils import predict_hf, plot_shap_values

然后,我们创建前面讨论过的蓝图:

bp1 = Blueprint('main', __name__, url_prefix='/main')

最后,我们创建一个对应于“API/make _ prediction”URL 的 Flask API 路由:

@bp1.route('/api/make_prediction', methods=['POST'])
def make_prediction(): # Read in the patient data from the frontend and convert to df
   form_df:pd.DataFrame = pd.DataFrame(request.form, index=[0]) # Make heart failure prediction  
   pred = predict_hf(form_df)
   pred_class_1 = pred[0][1] # Get risk factor plot
   plot = plot_shap_values(form_df)

   # Return prediction and plot back to frontend 
   return {'pred':round(pred_class_1, 2), 'plot':plot}

“request.form”对象包含从前端传递的所有患者信息。这个对象被转换成一个数据帧,并通过预测和绘图功能。预测和情节作为字典传递回前端(Flask 将其转换为 JSON)。

最后的想法

本文涵盖了心力衰竭应用程序的所有组件。我们展示了数据如何从前端流向后端,以及机器学习模型如何嵌入到 web 应用程序中。我希望这篇文章给读者一个机器学习模型部署的高层次概述,我完全意识到这还远远不够全面。感谢您的阅读!https://hf-predictor.herokuapp.com/

利用 Streamlit 从 ECG 信号中检测异常的端到端 Web 应用

原文:https://towardsdatascience.com/an-end-to-end-web-app-to-detect-anomalies-from-ecg-signals-with-streamlit-e00c177305ff

本教程着重于用 MLflow、Sagemaker 和 Streamlit 构建一个 web 应用程序

迈克尔·芬顿在 Unsplash 上的照片

这篇文章是关于如何使用 DagsHub+ML flow+AWS Lambda 部署你的 ML 模型的故事。在前面的故事中,我展示了如何训练和部署一个模型来从 ECG 信号中检测不规则的心律。基准数据集是 ECG5000 数据集,它包含从一名心力衰竭患者身上随机选取的 5000 次心跳。它经常被用在研究论文和教程中。

这一次,我将使用另一个真实世界的数据集,它更嘈杂,因此也更具挑战性。该数据包含时间戳、患者 id 和心率等字段。除了这些特征,还有一个标签告诉你心律是否异常,是通过人工标注得到的。

在本教程中,我们将构建一个检测心脏异常的 web 应用程序。在创建应用程序之前,我们将进行一些探索性分析,建立和比较不同的机器学习模型。我们开始吧!

我们试图解决的问题是什么?

作者插图。

我们将再次关注从 ECG 信号中检测异常。上面是患者的 ECG 信号,其中 x 标记代表异常。从这个例子中,你可以猜到它不仅是异常检测,而且是峰值检测。因此,我们需要识别峰值,并确定这些峰值是否异常。

为了遵守异常检测公式,训练集仅包含正常 ECG 信号,而测试包含正常和异常信号。还有一个重要的考虑因素:异常构成少数类,并对应于峰值。事实上,测试集包含不到 1%的异常

基于这些原因,单靠心率不足以解决这个问题。除了心率,我们还需要创建两个新特性。首先,我们构建一个变量来计算心率的当前值和先前值之间的差值。另一个至关重要的特性是峰值标签,如果有峰值,它的值等于 1,否则返回 0。

目录

  • 第 1 部分:模型训练和物流跟踪
  • 第 2 部分:使用 Amazon SageMaker 部署 MLflow 模型
  • 第 3 部分:用 Streamlit 创建一个 Web 应用程序

主要要求

  • 创建 GitHub 存储库。
  • 登录 DagsHub,点击“+ Create”按钮,选择“Connect A Repo”选项。它允许你通过 GitHub 和 DagsHub 来管理你的库。
  • 这创建了一个 DagsHub 存储库,需要它来跟踪 ML 实验和版本化数据。
  • 在本地 PC 上克隆存储库。建议将 Visual Studio 代码作为 IDE。
  • 使用pip install dvc安装 DVC。
  • 在我们的项目中运行dvc init

第 1 部分:模型训练和 MLflow 跟踪

就像在之前的教程中一样,我们将使用一个令人敬畏的开源平台来跟踪实验,打包机器学习模型,并在生产中部署它们,称为 MLflow。它与 DagsHub 一起使用,dag shub 允许您在存储库上找到所有结果实验,并有效地对数据和代码进行版本控制。除了这些特性,您还可以在 DasgHub repository 上可视化整个管道的交互式图形。

这次,我考虑了两种检测心脏异常的模型:自动编码器和隔离森林。这一选择是因为这项任务非常具有挑战性,而且与正常观测相比,异常现象的数量非常少。由于其特有的假设:异常代表少数类,并且在隔离树上具有短的平均路径长度,隔离林被证明比自动编码器具有更好的性能。

此外,在训练算法之前,它设置超参数的值,该值对应于数据集中异常的比例,也称为污染。隔离林表现最好,污染率小于 1%,如 0.4%和 0.5%。autoencoder 发现这项任务更成问题,这并不奇怪,因为它没有这些类型的假设。

在脚本中,我们训练两个可用模型中的一个,并记录超参数和指标。我们还对将模型记录为工件并在模型被训练后注册它感兴趣。这两个操作可以使用mlflow.sklearn.log_model(sk_model=,artifact_path,registered_model_name)合并。如果还是不想注册模型,可以避免指定registered_model_name参数。

你可以在这里 找到 train.py 的完整代码。

我们可以运行代码并尝试超参数的不同组合,例如 model_name (if 或 Autoencoder)、使用隔离林时的污染率、epochs 数以及切换到 Autoencoder 时的批量。

python src/train.py

我们可以在实验框中访问实验的结果,它存在于 DagsHub 存储库中。从测试集上的模型评估中获得的结果,您可以注意到自动编码器获得了非常小的精度和 f1 分数值,而召回率很高,等于 88%。这意味着假阳性的数量增加,然后,自动编码器发现异常甚至正常的模式。

与 autoencoder 不同,Isolation Forest 获得了更好的分数,63%的准确率和 85%的召回率,导致 f1 分数等于 73%。即使仍有一些误报,但考虑到这个问题具有挑战性,结果超出了预期。

为了更好地理解所获得的评估方法,让我们也将真实值与预测值进行对比。这是使用 Autoencoder 对患者的 ECG 信号进行编码后得到的曲线:

这与隔离林获得的相同地块相比较:

绿点代表预测标签,而红叉代表地面真相。很明显,自动编码器似乎发现了异常的大量观察值,而隔离森林考虑了最异常的样本。

第 2 部分:使用 Amazon SageMaker 部署 MLflow 模型

由于隔离林实现了最佳性能,我们将只关注此算法。它可以分为两步:

  • MLflow 模型的更新阶段
  • 创建 AWS 帐户并设置 IAM 角色
  • 将 MLflow 模型部署到 Sagemaker 端点

1.MLflow 模型的更新阶段

从 DagsHub 页面,可以访问 MLflow 服务器用户界面。我们只需点击右上角的远程按钮,然后选择“转到 MLflow UI”。之后,我们可以按下菜单中的型号选项,进入注册型号页面。然后,我们单击您注册的模型的最新版本,并将 Stage 参数从 None 设置为 Staging。

除了手动执行此操作,您还可以直接使用 python 脚本:

现在,我们更新了模型的阶段,我们准备切换到下一步!

2.创建 AWS 帐户并设置 IAM 角色

在部署模型之前,需要考虑一些要求:

  • AWS 中注册账户
  • 进入 IAM →用户→ 添加用户。选择用户名,并选择访问密钥-编程访问作为 AWS 凭据类型。
  • 如果您没有 SageMaker 组,请点击创建组。选择组名,添加两个策略:AmazonSageMakerFullAccessAmazonEC2ContainerRegistryFullAccess
  • 之后,我们可以进入角色→ 创建角色。选择 AWS 服务和 SageMaker。Sagemaker 策略将自动附加到角色。

作者截图

  • 设置 AWS CLI 界面。在终端上运行aws configure,它会询问您 AWS 访问密钥 ID、AWS 秘密访问密钥、默认区域名称和默认输出格式。欲了解更多信息,请点击查看。这是 AWS 文档中显示的一个示例:
$ **aws configure** 
AWS Access Key ID [None]: ***AKIAIOSFODNN7EXAMPLE*** 
AWS Secret Access Key [None]: ***wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY*** 
Default region name [None]: ***us-west-2*** 
Default output format [None]: ***json***

我想强调的是,这些已配置的凭据对于下一步至关重要。

3.将 MLflow 模型部署到 Sagemaker 端点

第一步是构建mlflow-pyfunc映像,并使用 MLflow CLI 将其推送到 SageMaker:

mlflow sagemaker build-and-push-container

你可以在 AWS 账户和 Docker 桌面上找到生成的图片。

之后,我们可以使用 python 脚本通过mlflow.sagemaker.deploy将 MLflow 模型部署到 Sagemaker。您可以在 mlflow 文档中找到更多信息。

这将导致创建一个 Sagemaker 端点。

您应该获得这个最终输出。如果出现错误,请检查是否正确指定了 image_url、model_uri 和 arn。

我们可以快速检查端点是否在工作。选择患者的 ECG 信号,并将其传递到部署的模型,该模型会将观察结果分类为正常或异常。

需要注意的是,当观察异常时,隔离林返回-1,否则返回 1。为了与地面实况进行比较,我们需要映射这些值,1 表示异常,0 表示正常。如果您运行该脚本,您应该获得如下输出:

第 3 部分:创建 Web 应用程序

让我们最终构建一个 web 应用程序来检测 ECG 信号中的异常!我们将使用 Streamlit,这是一个免费的开源框架,允许使用 Python 用几行代码构建应用程序:

  • 我们建立了到通过 AWS Lambda 获得的 API 服务的链接,其中已经部署了模型。
  • 使用st.markdown显示网络应用的主标题。
  • 在上传一个 CSV 文件来评估模型的性能时,使用st.sidebar创建一个左侧面板侧栏。
  • 按钮“检查异常!”需要单击以显示部署模型的结果。

如果上传文件并选择按钮,将出现一个代表患者 ECG 信号的散点图。像以前一样,它将真实值与预测值进行比较。除了这些特性之外,您还可以通过以下代码更改患者 ID 的值

在 GitHub 中推送所有更改后,我们可以使用 Streamlit 部署应用程序。这非常简单明了。如果你想了解更多信息,点击这个 YouTube 视频的链接。我部署的应用程序的链接是这里

最终想法:

恭喜你!您已经完成了这个专注于检测 ECG 信号异常的项目。当从一个工具切换到另一个工具时,一开始可能会感到不知所措,但是当你达到结果时,它会给你带来满足感!特别是,一个网络应用程序可以是一个可爱和直观的方式来与其他人分享你的工作。感谢阅读。祝您愉快!

查看我的 DagsHub 存储库中的代码:

https://dagshub.com/eugenia.anello/anomaly_detection_ecg_part2

你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都会收到电子邮件!

智力的方程式

原文:https://towardsdatascience.com/an-equation-for-intelligence-f522055675fd

unsplash

出自弗朗索瓦·乔莱:《论智力的尺度》

纵观历史,智力的衡量标准已经发生了巨大的变化,以适应不断变化的思想和对不同人群和动物的态度。近几十年来,智能的可能性正在向机器延伸。

许多人试图用语言来阐明智力的标准:这里有滑动的空间,智力的应用必须由一个外部因素来评估,而不是仅仅由被测量者来证明。其他人做出了令人信服的哲学主张,即智力以及代理、自由意志、人格等下游标签不是人类首先要强加的(参见冈克尔,《机器问题)。更进一步,一些哲学家断言投射在机器和其他人的地位上的存在主义焦虑是一个虚假的形象:一个掩盖内部冲突和紧张的面具(参见齐泽克,绝对反冲性和失败的绝对)。

尽管如此,许多人还是希望有一个客观的智力衡量标准。在具有里程碑意义的论文《论智能的度量》中,Francois Chollet 可能推进了这项事业:Chollet 提出了一个系统智能的表达式。他在论文中提供了许多其他有趣的观点,但这篇文章的目标是提供一个(相对)简短,(相对)平易近人,(相对)简化的等式所代表的内容的概述。请阅读这篇文章,更深入地了解这里的动态。

智力的属性

  • 聪明在于适应性和概括性而不是技能本身。仅仅为了下棋而构建的程序是不智能的,但是能够有效地学习下棋的通用代理可以被认为是智能的。
  • 智力的测量必须控制先验,并量化概括/适应性的强度。从强先验(初始信息)开始的模型比从弱先验开始的模型更容易学习任务,但这并不意味着它更聪明。智能必须被语境化——它不仅必须理解系统的当前状态,还必须理解它来自哪里。
  • AI 将军必须以人类智能为基准。它本质上是最低限度的人类中心主义。没有“通用的”智力标准。但这并不意味着我们不能测量智力:我们只需要理解参考点。同样,在物理学中,我们可以测量速度,即使它在相对参考系中运行。

形式化组件

出自《论智力的衡量》

一个智能系统负责生成技能程序——这些是‘静态’决策程序,接受来自任务情况(输入信息)并发出相应的响应。响应的适当性用于确定任务分数并向智能系统提供反馈。反馈用于改进进一步技能程序的生成。

考虑深度学习:技能程序是神经网络的前馈通道,是一系列静态的计算矩阵运算。情况是输入的数据;响应就是输出。反馈是损失,它被发送回智能系统——在这种情况下,是反向传播。这里,反向传播是用来创建适应性程序的“智能”算法。

但也有其他配置:一些神经架构搜索系统使用控制器神经网络来提出候选神经网络架构;在这种情况下,控制器神经网络就是智能系统。或者,一些语言模型可以编写函数脚本;语言模型是智能系统(技能程序是文字生成的字符串输出)。

但重要的是要理解,这是一个通用框架,用于形式化智能系统如何与环境交互,以解决环境中的任务并从中学习。它可以应用于经典的机器学习、人类和动物心理学,甚至是我们尚未探索的未来形式的协同学习。

一个智力方程式,用语言来说

引用自 Chollet 的话:“系统的智能是对其在任务范围内的技能获取效率的度量,涉及先验、经验和概括难度。”

详细说明:如果一个系统是智能的,它能够…

  • 获取技能(在不熟悉的领域学习新问题)
  • 有效地(最佳利用资源,例如零/一/少量拍摄)
  • 在任务范围内(不是一组受限的域)
  • 关于先验(系统开始时有多少信息)
  • 经验(系统通过培训获得了多少信息)
  • 和推广难度。(任务的“难度”)

让我们开始构建我们的正式方程式!

原子

我们将从定义关键变量开始,首先呈现 Chollet 的符号,然后是描述。这是一个沉重的符号负担,但为了理解最终的结果,它值得熟悉。

一个任务

一个课程——在训练过程中智能系统和任务之间的一系列交互。

足够技能阈值 —系统被认为具有“足够技能”的某项任务的掌握程度 T 。学习如何骑自行车有一个足够低的技能门槛;学习如何进行脑外科手术有一个更高的。

在 T 达到足够技能水平的值——系统在一项任务中获得足够技能的“令人印象深刻”或“重要”程度。执行 CPR 具有相对较低的足够技能水平,但是在拯救人类生命的背景下具有高价值。

来自智能系统的课程空间,为一项任务生成足够技能的解决方案。在其他方面,一个系统在与信息(即课程)交互方面可以采取的所有可能的途径,以允许它最终达到或超过给定任务的足够技能阈值。

给定课程发生的概率;即智能系统将被呈现给的概率,即环境/任务对信息的特定顺序和选择。

系统解——达到技能阈值的任务 T 的最短解。

给定一门课程的 T 最短最优培训时间解。也就是说,在给定一系列呈现给模型的数据的情况下,任务的最优解决方案,而系统解决方案是任务整体的最优解决方案。这两种解决方案可能不一样。

字符串的算法复杂度。通用图灵机上输出字符串的最短程序的长度。点击查看更多关于 Kolmogorov 复杂度的内容。

一个字符串 s2 拥有的相对复杂度约为 s1 。给定 s2 ,产生 s1 的通用图灵机上最短程序的长度。这表面上是程序间相似性的度量。

分子

我们将开始把原子组装成更复杂的组件。方程式很快就要出来了——坚持住!

以系统为中心的泛化难度。由最短训练时间解决方案解释的系统解决方案的算法复杂性的比例。这代表了从训练经验中归纳出解决一项任务中以前没有的部分是多么困难。如果训练集和真实任务有天壤之别,那么泛化难度就非常大;如果它们是相同的,那么一般化是微不足道的。换句话说:“训练制度的解决方案和任务的‘真实’解决方案有多相似?”在损失最小化的狭义背景下——“培训损失情景的全局解决方案离真实损失情景的全局解决方案有多远?”

开发者意识到的泛化困难。由训练解系统初始状态解释的系统解的算法复杂度的比例。与以系统为中心的开发人员不确定性相同,除了智能系统的初始状态也被考虑在内:这允许我们考虑先验知识——系统在学习开始时可能已经具有哪些信息或优势。如果系统的初始状态与最优训练解非常相似,那么开发者意识到的泛化难度相当低。

智能系统的先验。由初始系统状态解释的最短解决方案(在技能阈值θ的任务 T 的)的算法复杂度的比例。如果最优解与初始系统状态非常不同,那么分子(和整个表达式)将被最小化。另一方面,如果最优解与初始系统状态非常相似,那么分子(和整体表达式)将最大化。

步骤 t 的经验。在步骤 t 由智能系统接收的相关信息和新信息的数量。计算为{最优解和智能系统的当前状态之间的相似性}和{最优解和智能系统的当前状态以及在步骤 t 遇到的新数据之间的相似性}之间的差。如果新数据补充了现有状态,使其更接近最优解,则第二项将最小化,整体表达式将最大化。

课程经历。智能系统在课程中接收的相关信息的度量;时间 t 时所有时间步的经验的简单总结。

智力方程式

我们将只看 Chollet 的充分情况方程(相对于最优情况方程)。智能是相对于智能系统(is)、足够技能阈值(θ-T)和任务范围来定义的。

让我们把这个等式分解成几个组成部分。

这是关于问题的总系统暴露信息:先验(系统初始化时它已经拥有的任何信息)加上经验(它在学习/适应过程中获得的任何信息)。

因此,下面的内心表达…

…在导致智能系统获得足够技能水平的所有可能的课程中,取该路径的情境化难度的加权平均值(按每个课程的概率加权, P_C )。难度计算为概括难度除以总系统信息暴露:如果概括难度非常高,但总信息暴露非常低,则课程路径具有高情境化难度:它必须用更少的资源克服更高的障碍以获得足够的技能。

总的来说,上面的表达式测量了在给定任务 T 中获得足够技能的总难度。

这是在 T 中达到足够技能等级的主观值。回想一下,第二项表示足够技能阈值,第一项表示获得这种足够技能水平的值。将它们相乘可以让我们理解在给定的任务中达到足够的技能水平作为一个产品是多么有价值(字面意思!)这两个维度。

现在,我们将在任务 T 中达到足够技能水平的主观值乘以总难度和范围内所有任务的平均值,以获得整体系统智能。

以这种方式,高智能系统高效地为高概括难度的任务生成高技能的解决方案,即,具有相对较低的信息量(先验、经验或两者)。智力,这样表述,就是经验转化为技能的转化率

智能形式化的性质

在这里,我们将注意到一些相关的观察和 Chollet 的特殊智能方程形式化的含义。

  • 智力与范围相关,即被评估的任务的广度。
  • 技能是模型输出的一个属性,而不是模型本身的一个内在属性。
  • 智能必须包括学习和适应。
  • 智力不能仅仅在最高的抽象层次上进行插值。
  • 智能与课程优化(即学习过程)无关。
  • 计算依赖于智能的合成程序,但不依赖于程序本身是如何合成的。

对研究的影响

  • 智能可以作为一个优化问题来处理,就像 ImageNet 上的 top- k accuracy 在 2010 年代作为计算机视觉的一个刺激优化程序一样。参见抽象推理文集挑战
  • 关注更广泛的能力和适应性,而不是狭隘的技能。
  • 激发对程序合成的兴趣,而不仅仅是程序输出(图灵式模型不可知论)。允许我们适应更高层次的抽象。

感谢阅读!

而且,如果你对最新的文章感兴趣,可以考虑订阅。如果你想支持我的写作,通过我的推荐链接加入 Medium 是一个很好的方式。干杯!

隐私保护联邦学习的探索(附代码)

原文:https://towardsdatascience.com/an-exploration-of-a-privacy-preserving-federated-learning-system-7944fb284061

为了更好地理解解决一些机器学习问题的思路

杰森·登特在 Unsplash 上拍摄的照片

机器学习虽然是一项非常强大的技术,但似乎也知道一些缺点。例如,对数据的巨大需求和对神经网络的可能攻击,将在后面讨论,有望用一些隐私保护的联邦学习来回答。

题目是什么意思?

首先,我们快速讨论一下什么是联邦学习。今天,机器学习被任何想要更深入地理解他们的数据集并对一些分类/回归任务建模的人广泛使用。然而,要实现可能的最佳模型,需要大量数据,而这些数据可能很难集中在一个地方。因此,如果拥有自己的数据切片的多个用户可以将所有东西组合起来模拟一个大数据集,然后获得一个非常好的模型,会怎么样?这是联邦学习的主要思想。

现在,如果这些用户不想分享他们自己的数据,但仍然希望使用每个人的数据聚合来建立一个不错的机器学习模型,该怎么办?例如,一个用户可能是一家医院,癌症检测模型所需的数据不能按原样发送,因为患者显然会不同意。即使是非匿名化的数据也可能存在隐私风险。

一种想法是不发送准确的数据,而是添加一些噪声,这样接收者就不会获得真实的数据。然而,如果我们添加太多的噪声,我们的数据可能会变得无关紧要,学习算法可能找不到任何有用的东西。联合学习系统不是直接共享数据,而是每个用户可以使用自己的数据计算的梯度或权重。

如果你对权重或梯度的概念感到不舒服,这里有一个神经网络世界的快速介绍。

我们现在有一个多用户的系统,在将模型发送给其他人之前,用户可以单独更新他们的模型。有一个中央服务器,它首先发送模型的初始参数,然后接收每个用户的结果。此时,服务器可以通过对所有接收到的梯度求和,将不同的模型组合成一个模型,并用平均的新值更新权重。

图片来自https://sparkd.ai/archives/4444,联合学习原理

这样够隐私吗?

第一部分介绍了保护每个用户对数据的访问的解决方案。然而,敌对的机器学习领域知道一些攻击(白盒,黑盒),当用户发送其更新的权重时,这些攻击再次引发问题。事实上,简而言之,向世界展示我们的模型也展示了我们的数据,并且由于服务器和每个其他参与者都知道架构,发送我们的权重允许他们尝试白盒攻击来检索我们的数据。

为了解决这个新问题,我们现在可以转向密码学,更确切地说,转向同态加密。事实上,这种加密不仅将我们的纯文本(或权重)转换为密码文本,还允许对其进行一些基本计算。因此,服务器仍然可以将所有接收到的权重聚集到主模型的一个全局更新中,而无需访问这些值。服务器完成的聚合结果将被初始发送者读取。

同态加密需要一些数学知识才能完全理解,所以让我们试着抓住要点。考虑一个函数 f(x) = 2x。该函数保留加法,使得 f(x+y) = f(x) + f(y) ,但不保留乘法。我们希望我们的加密方案是完全同态的,因此它必须至少保持加法和乘法。

存在各种同态加密方案,但是我们稍后将在我们的实现中使用 CKKS [1],因为它允许使用浮点数,并且权重通常被描述为在 0 和 1 之间。我们还想将总数除以参与人数 n ,或者乘以 1/n 。首先,加密使用公钥和私钥。评估密钥也用于评估对密文的操作。明文和密文空间被定义在多项式环上,允许一些重要的操作性质。

为了允许多个用户对系统和模型进行训练,我们需要使用先前加密方案的多方同态加密版本。

总之,使用当前我们的解决方案,数据仅由所有者使用,并且模型权重总是被加密,因此服务器的扫视或窃取是不可能的,并且我们的数据是保密的。

真的管用吗?

嗯,根据这篇论文【2】,是的。最终的模型获得了接近几种常见问题的公共方法的精确度。

事实上,我们遵循并行随机梯度下降(SGD)算法[3]。这相当于批量更大的单节点 SGD。它不应该与“模型平均”混淆,后者一次使用多个模型进行预测。在这里,我们只用一个模型完成了训练阶段。

但是,我们应该注意,联合学习容易受到中毒攻击。事实上,如果网络中的一个节点受到损害,并由于错误或可能的攻击而决定发送错误的结果,最终的模型将产生不好的结果。一个简单的防御措施是删除“异常值”权重更新,并可能禁止负责节点参与剩余的学习阶段。这个对手节点可以通过对输入权重的组合进行反复试验来找到,因为权重是加密的,并且只有一些或所有权重的聚合是可能的。然而,这种技术显然不是完美的,因为坏的梯度可能仍然适合界外边界,或者大多数节点可能是恶意的,导致梯度的错误的假定平均值。

本文 [4]通过首先在服务器上引导信任,提出了一种拜占庭健壮的联邦学习系统。这是通过服务器收集参与者的每个数据集的一部分并维护其自己的模型来完成的,就像其他节点训练它们的权重一样。对于每次迭代,服务器然后基于节点变化与服务器变化的相似性,为每个节点分配“信任分数”。然而,遗憾的是,访问用于比较的权重将意味着它们不再被加密,因此这个解决方案不适用于我们的情况。此外,服务器训练的本地模型需要我们不想共享的数据。因此,我们需要确保每个节点都是认真的,不会以我们自己的方式妥协。

并行化的 SGD 及其聚合是与经典机器学习的唯一真正区别,因为项目的其余部分是关于加密数据的传输。我们可能会经历更长的训练时间,因为我们正在使用的非对称加密非常慢,但作为回报,我们使用的数据比我们自己可以拥有的多得多(假设其他节点正在带来相关数量的数据)。

怎么才能实现呢?

这里有一份 Golang 的概念证明,你可能会想要。 Golang 允许您简单地将 Github 存储库作为库导入,然后随心所欲地自由使用这些功能。

整个系统是密码学、机器学习和分布式系统的混合体。我们将需要一些代表用户的节点(和聚集值的服务器),能够通过 TCP 或 UDP 发送和接收消息。请注意,UDP 的消息大小限制约为 65000 字节,加密的消息可能会比这个大。如果通过 TCP 和安全通道发送,消息会更安全。目前,它们以加密块包的形式发送,或者当服务器响应时,以结果包的形式发送。

该项目的加密部分将通过使用 Golang 库 lattigo 来重用早先发现的方案(CKKS)。它允许我们的节点简单地加密和解密多项式中定义的权重。

最后,神经网络不太重要,因为该系统可以连接到我们最喜欢的 Python 机器学习库,该库只需要解密的权重来建立模型并对其运行反向传播步骤,然后将其重新传输到 Go 程序,该程序将重新连接到网络并加密权重。我们主要需要一个好的权重表示,允许节点在其上处理它们的梯度。

当然,在学习阶段开始之前,所有节点都必须检查它们的数据是否与其他节点的数据相匹配,再加上所决定的模型架构的输入层,否则它们将无法训练它们的模型。当使用分别由参与者和服务器发送的加入参数数据包时,我们可以观察到这个准备阶段。

有了这样一个系统,我们现在可以启动一个服务器和多个其他节点,这些节点将加入到服务器中进行联合学习过程。一旦服务器决定了超参数加上初始权重,它就将其发送给所有参与者。他们每个人都必须遵循梯度下降步骤并计算新的权重。这些重量在通过网络之前必须被加密,因此没有人能够读取它们并推断出所使用的数据。服务器接收所有不同的权重,并在处理它们的平均值之前聚合这些值,为我们提供一个具有轻微训练过的权重的模型。此时,我们已经完成了机器学习算法的一个步骤,我们可以尽可能多地重复它,直到我们获得我们希望的模型。

[1] J. Hee Cheon,A. Kim1,M. Kim 和 Y. Song。近似数算术的同态加密。2016.

[2]s . Sav、A. Pyrgelis、J . r . Troncoso-帕斯托里萨、D. Froelicher、J . P . boss UAT、J. Sa Sousa 和 J . P . Hubaux。海神号:保护隐私的联邦神经网络学习。2021.

[3] M. Zinkevich,M. Weimer,L. Li 和 A. Smola。并行随机梯度下降。2010.

[4] X .曹,m .方,j .刘,n .龚振强。FLTrust:通过信任引导的拜占庭健壮联邦学习。2021.

感谢阅读!

我希望你喜欢探索联合学习,如果你有任何问题和/或建议,请在评论中告诉我。

图像嵌入和潜在空间

原文:https://towardsdatascience.com/an-extensive-introduction-to-image-embedding-and-auto-encoders-0-6-5c5d9a18fcaa

对潜在空间的广泛介绍

图像嵌入和潜在空间的理论和实践介绍。

克里斯·劳顿Unsplash 上拍摄

他们说一幅图像胜过千言万语。看看上面的图片,想象一下它告诉我们的关于树叶、树叶的颜色以及它们的生活的故事。让计算机为我们讲述其中的一些故事会很有趣,但计算机能告诉我们图像中有树叶吗?他们的颜色?还是说秋天要来了?这些对人类来说很容易,因为我们只需要看图像。但这对计算机来说更具挑战性,因为它们看到的都是数字,这对人类来说也很难理解。

好的方面是我们可以帮助计算机理解。计算机可以把图像变成更容易理解的东西。它可以将图像转换成变量,告诉它图像中物体的纹理、颜色和形状。有了这些,计算机就可以开始告诉我们图像中的故事。为了讲述这些故事,图像必须首先被嵌入到计算机中,然后通过嵌入潜在空间中而转换成变量。我们将后者称为潜在空间嵌入以区分两者。

本书旨在从理论和实践上介绍图像嵌入、潜在空间嵌入以及不同应用所使用的技术。这本书从基础开始,向现代方法发展,所有都有例子和代码支持,使它更容易理解。

该系列包括 8 章,每一章将在以下章节中介绍。关注我,以便在我发布新章节或帖子时收到电子邮件。

  1. 嵌入、聚类和相似性介绍
  2. 图像嵌入和精度介绍
  3. 图像潜在空间嵌入简介
  4. 潜在空间中的聚类
  5. 实用:人脸检测中的潜在空间
  6. 自动编码器简介
  7. 使潜在空间适应相似性
  8. 实用:潜在空间中的产品推荐

第 1 章:嵌入、聚类和相似性介绍

在坐标系中用数字表示的知识。

第一章解释了什么是嵌入,以及如何用它来表示变量中的真实世界。每个变量都是一个问题,因为它向世界寻求知识;挑战在于提出正确的问题。问正确的问题可以影响计算机理解变量的难易程度,以及我们是否可以在应用程序中成功使用变量。

计算机很难理解嵌入的图像,这就是为什么我们现在还不去看它们。相反,我们将着眼于这些问题的简单表示,以及如何使用正确的问题来嵌入这些内容。目标是在深入研究图像之前理解嵌入的潜在机制。

第二章:图像嵌入和精度介绍

将图像转换成数字的过程是机器学习所必需的。由杰斯·贝利Unsplash 拍摄的图书照片

现在是时候将嵌入的概念从第 1 章扩展到图像嵌入了。在许多使用机器学习的应用中,图像是一种流行的选择。图像由小方块组成,每个小方块显示一种颜色。这些颜色放在一起就形成了整个图像。所有的颜色都可以用一个数字来表示,并被视为一个变量。

第二章尝试使用第一章中教授的方法制作一个可以识别图像中动物的应用程序。引入精确度是为了衡量这些方法识别正确动物的能力。结果表明,第一章的方法不足以实现可靠的应用,而潜在空间嵌入可以帮助我们提高精度。

第三章:图像潜在空间嵌入简介

通过询问图像看起来更像一只狗还是一只鸡来转换图像,并绘制在一个坐标系中。 Valeria BoltnevaPexel 上拍摄的狗狗照片。由 Erik KaritsPexel 上拍摄的小鸡照片。

第三章利用第二章的优点和缺点,开始探索潜在的空间嵌入。

使用第一章中关于图像的方法具有挑战性,因为相同的颜色不一定会产生相同的对象;一只狗和一只鸡都可以是棕色的,但不一样。转换可以使用标准的潜在空间嵌入器来减轻这些挑战,例如主成分分析(PCA)线性判别分析 (LDA)和神经网络分类器。PCA 和 LDA 帮助我们将 2D 的图像可视化,但是对于使图像更容易理解没有什么帮助。另一方面,神经网络分类器对计算机理解图像的能力有很大的贡献。

神经网络分类器是不完善的,在实际应用中有其自身的挑战。对神经网络进行简单的调整来应对这些挑战。没有调整是没有后果的;在第 3 章的结尾,讨论了权衡以及何时使用什么。

第四章:潜在空间中的聚类

目标是明确区分不同肤色的人群。

第四章解释了如何进行改进的潜在空间嵌入以更好地区分新动物。

第 3 章中提到的一些挑战是由如何训练神经网络分类器来学习一组定义的类(例如,动物)之间的差异引起的。在了解差异之后,我们可以通过询问神经网络如何得出结论来减轻这些挑战。它可能会根据动物的颜色、皮毛、腿的位置、牙齿等特征做出决定。自动编码器背后的想法是使用这些功能来伪装未来引入的新动物。

但是为什么一定要先学会识别动物,再问用什么特征呢?为什么不从一开始就学习最好的特性来区分它们呢?第 4 章介绍了结合这些想法的现代方法,以更有效地分离相似但不同的实例,如人脸。

第五章:实践:潜在空间中的人脸检测

著名人物的图像,作为您的系统可能想要识别的人的例子。来源:野外贴标签的脸

现在是时候在实践中使用我们的新知识了。第一个实际案例是我们建立一个系统来识别你的哪个朋友在一张照片里。我们将看到专注于分类的潜在空间嵌入如何创建一个可扩展的系统,在这个系统中,你可以轻松地添加新朋友,而不必担心将陌生人识别为你的朋友。

第 6 章:自动编码器简介

在用编码器将图像变换到潜在空间之后,自动编码器通过训练神经网络用解码器重建输入图像来自动创建潜在空间嵌入。来自 Pexel 的瓦莱里娅·博尔特涅娃的狗狗照片

到目前为止,重点一直是如何通过用图像和标签(例如,动物或人脸)训练神经网络来进行潜在空间嵌入。但是,有没有可能让过程更简单,创造一个没有标签的潜在空间嵌入?这就是自动编码的用武之地!

在用编码器将图像转换到潜在空间之后,自动编码器通过使神经网络用解码器重建输入图像来自动化标记过程。这个想法是,不同的维度倾向于代表不同的特征,因为相似的物体在潜在空间中彼此更接近。代表特征的尺寸帮助解码器在基于特征创建原始图像时重新创建原始图像。因此,自动编码器可以帮助自动创建潜在空间。

第七章:让潜在空间适应相似性

使用神经网络分类器的潜在空间嵌入专注于在类别之间创建清晰的分离,使得更容易确定图像属于哪个类别。问题是,在寻找相似性时,这些方法是无效的。来自同一类的相似图像更接近,但是跨类的相似图像被降低优先级,因为神经网络试图将每个图像分开。

第 7 章使用第 6 章中的自动编码器的概念,并通过使用正则化和社会数据来指导该过程,使它们适应相似性。

第八章:实践:潜在空间中的产品推荐

左边的图片是用户上传的,左边的图片是系统的推荐。所有图片均来自 Unsplash ,从左上至右下依次为:瑞安·赫里斯托多洛瑞恩·赫里斯托多洛亚历克斯·舒比兹阿里·乔宾阿里·乔宾阿什坎·弗鲁扎尼奥莱娜·谢尔坚科斯文·布兰德斯玛

第二个实际案例是,用户可以上传他们喜欢的产品的图片,并以此作为寻找下一件家具的灵感。系统寻找相似的产品并推荐给用户。我们将看到关注相似性的自动编码器如何创建一个可扩展的系统,您可以在其中轻松添加新产品。

参考

[1] Casey Cheng,用零数学直观解释主成分分析法 (2022),

[2]杨,,线性判别分析, (2020)解释

[3] Victor Zhou,初学者的机器学习:神经网络导论 (2019),

[4] G. Huang 等野外标记人脸:研究无约束环境下人脸识别的数据库(2007)

[5]杰森·布朗利(Jason Brownlee),生成性对抗网络(GANs)的温和介绍 (2019),machinelearningmastery.com

所有图片和代码,除非另有说明,均为作者所有。

感谢你阅读这本关于潜在空间的书!当分享我们的想法时,我们学得最好,所以请分享一个评论,无论是一个问题,新的见解,还是一个分歧。任何建议和改进都非常感谢!

如果你喜欢这本书,并且对机器学习和数据科学的新见解感兴趣,注册一个中级会员,可以完全访问我的内容。关注我,以便在我发布新章节或帖子时收到电子邮件。

https://medium.com/@mathiasgronne/membership

假设检验的图解说明

原文:https://towardsdatascience.com/an-illustrated-explanation-of-hypothesis-tests-3594ffff34dc

一劳永逸地学会什么是假设检验。Python 中的代码。

正态分布。图片作者 bby。

声明:嘿,先把事情说清楚,这不是一个赞助的帖子。我非常喜欢这本书。

假设检验

我最近读了这本好书统计学的卡通介绍。它带来了对基本统计概念的有趣而流畅的介绍,所有介绍都像一本漫画书。

引起我注意的一点,也是我今天要揭示的一点,是作者 Grady Klein 如何解释假设检验的概念。用他的话说…

假设检验是一个有趣的名称,用于猜测总体平均值,并将该猜测与我们已有的样本平均值进行比较。

样本均值和我们的新猜测。我们来测试一下。图片由作者提供。

现在,假设我们收集了另一个样本,我们不知道该样本是否来自我们的分布。我们能做什么?我们通过计算在“星形”点上具有平均值的样本与在“圆形”点上具有平均值的样本来自同一个总体的概率值(也称为 p 值)来检验它们都来自同一个分布(总体)的假设。

在我们的测试中,每个人都是无辜的,除非另有证明。也就是说,测试将假设样本 1 和样本 2 来自同一总体,并将该假设称为零假设(Ho)。但如果我们错了,他们不是来自同一个人群,这种假设就被命名为替代假设(Ha)。

让我们确定一个测试的门槛。在这种情况下,5%是经验法则。如果两个样本来自同一人群的概率(p 值)小于 5%,那么我们可能是错的,所以我们有强有力的证据拒绝 Ho。但如果我们的 p 值高于 5%,我们的机会告诉我们没有足够的证据来拒绝 Ho,所以我们保持样本来自同一人群的假设。也许这只是一个奇怪的样本,有着奇怪的平均值。

# Imports
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import f_oneway
from statsmodels.stats.weightstats import ztest# Create df
df = pd.DataFrame({'id': np.arange(1,51),'val': np.random.randn(50)})# Mean
df.val.mean()
**[OUT] -0.054089553629615886**#----# Create df2
df2 = pd.DataFrame({'id': np.arange(1,51),
'val': np.random.randint(40,50, size=50)/10})# Mean 2
df2.val.mean()
**[OUT] 4.513333333333334**# Test
"Can a distribution with mean sample 2 come from the same population as sample 1?
Ho : p-value >= 0.05 == Yes, same population
Ha : p-value < 0.05 == No, different population"
sample1 = df.val
mean_sample2 = df2.val.mean()# Z-test
stat, p = ztest(sample1, value=mean_sample2)
print(f'Test statistics: {stat}')
print(f'p-Value: {p}')**[OUT]
Test statistics: -26.21106515561983 p-Value: 1.9876587424280803e-151**

正如我们所看到的,零假设(Ho)被拒绝,因此我们有强有力的证据得出结论,两个样本来自不同的人群。
实际上,这意味着,如果我们认为真实总体平均值为-0.054(与样本 1 相同),那么很难找到平均值为 4.51 的样本。实际上,它被发现的几率几乎为零。

这是我们刚刚测试过的。橙色样品几乎没有接触到蓝色样品。图片由作者提供。

来自同一群人

让我们试试另一个测试。现在我将从样本 1 中抽取一个样本 3,并测试它们来自同一人群的概率是真是假。

# Create df3
df3 = pd.DataFrame({'id': np.arange(1,31),'val': np.random.choice(df['val'].values, size=30)})# Mean 3
df3.val.mean()
**[OUT] 0.04843756603887838**

酷,均值更接近样本 1 均值( -0.054 )。这是个好兆头。

sample1 = df.val
mean_sample3 = df3.val.mean()stat, p = ztest(sample1, value=mean_sample3)
print(f'Test statistics: {stat}')
print(f'p-Value: {p}')**[OUT]** 
**Test statistics: -0.3488669147011808 p-Value: 0.727189224747824**

哇!这两个随机正态样本来自同一总体的概率为 72%。有道理。3 是从 1 中提取的!

# Plot
sns.kdeplot(data=df, x='val', fill=True)
sns.kdeplot(data=df3, x='val', fill=True)
plt.legend(['Sample1', 'Sample3']);

两种分布重叠很多!图片由作者提供。

在你走之前

我想创建这个快速的帖子来展示假设检验背后的东西。我们实际测试的是什么。这是这里的目标,所以你可以围绕这个重要的统计概念建立你的直觉。

  1. 我们有一个平均值为-0.05 的样本
  2. 我们有一个平均值为 4.51 的样本 2
  3. 假设检验:考虑到真实总体均值为-0.05 或非常接近,是否有可能两个样本来自同一个总体?
  4. 我们开始假设,是的,他们来自同一个群体。然后我们计算 p 值,或概率值。
  5. 如果概率小于 5%,我们拒绝它,支持 Ha,说统计证据表明这两个样本来自不同的分布。如果超过 5%,那么我们没有统计证据表明他们不是来自同一人群,因此我们假设他们是。

GitHub 代码:【https://tinyurl.com/3csp5ejm

如果你喜欢这个内容,请关注我的博客。

https://gustavorsantos.medium.com/ https://en.wikipedia.org/wiki/Statistical_hypothesis_testing

回归分析中使用的方差-协方差矩阵图解指南

原文:https://towardsdatascience.com/an-illustrated-guide-to-the-variance-covariance-matrices-used-in-regression-analysis-3eb5a5dd2cff

系数估计的概率分布(图片由作者提供)

使用真实世界的数据集,说明回归建模的基础工件是如何构建的

方差-协方差矩阵形成了回归模型的梯形失真。回归模型误差的方差-协方差矩阵用于确定模型的误差项是否为 【同方差】 (常方差)。拟合回归模型系数的方差-协方差矩阵用于导出拟合模型系数估计值的标准误差置信区间。两个矩阵都用于形成模型预测的预测区间

方差-协方差矩阵是一个方阵,即它具有相同数量的行和列。沿着矩阵主对角线的元素,即从左上到右下的元素,包含方差,而所有其他元素包含协方差。因此,回归模型的拟合系数的方差-协方差矩阵包含拟合模型的系数估计的方差和系数估计之间的成对协方差。

类似地,回归模型的误差项的方差-协方差矩阵包含每个误差项沿其主对角线的方差以及所有误差项对之间的协方差。

话虽如此,为什么拟合模型的系数或误差项首先会有方差,以及这些矩阵在回归建模中扮演什么角色是我们将在本文中深入探讨的主题。

我们将使用经典的线性回归模型作为我们的范例模型。我们将学习的概念同样适用于各种常用的回归模型。

本文是以下两部分系列的第 1 部分:

第 1 部分:回归分析中使用的方差-协方差矩阵的图解指南
第 2 部分:深入探究经典线性回归模型的方差-协方差矩阵

汽车数据集作为我们的样本数据集

以下数据包含 205 辆汽车的规格,摘自 1985 年版的沃德汽车年鉴。每行包含一组 26 个关于单个车辆的规格。

汽车数据集(来源:加州大学欧文分校)

我们将考虑这个数据集的一个子集,它只包含三个变量:
City _ MPG
Engine _ Size
Curb _ Weight

三变量版本可从这里 下载

以下是三个变量数据集的前几行:

汽车数据集的子集(来源:加州大学欧文分校)

回归目标

我们的回归目标是使用线性回归模型回归发动机尺寸整备质量城市行驶里程。模型方程为:

city _ mpg =β_ 1+**β_ 2 发动机 _ 尺寸+β_ 3 整备 _ 重量+ ϵ

其中 ϵ 是模型的误差项。回归模型的误差项 ϵ 代表建模者没有或不能测量的所有因素的影响。

上述方程的矩阵形式如下:

y=+ϵ

凡,

  • y 是一个【n×1】大小的列向量,包含 City_MPG 的观测值。我们假设
  • 是一个【3×1】大小的回归模型系数列向量 β_1、β_2、β_3 对应的截距、引擎 _ 大小和整备 _ 重量。****
  • 是一个【n×3】大小的矩阵,包含回归变量的值。该矩阵的第一列是一列 1,它充当截距 β_1 的占位符。**
  • 是模型的回归误差的一个【n×1】大小的列向量。

当模型拟合在尺寸为 n 的样本上时,拟合模型的方程可以写成:

y=xβ_ cap+e******

在哪里,

  • yX 的含义与之前相同。
  • β_cap 是一个【3×1】大小的列向量,包含了回归模型的系数 β_1,β_2,β_3 的估计值 β_cap 有时被称为拟合系数。****
  • e 是拟合模型的残差【n×1】大小的列向量。残差是 y 的观测值与预测值之差。e=y——xβ_ cap****

回归策略

通常,我们会在 75–85%的数据集上训练这个模型,并在剩下的 15–25%的数据集上测试它。因此,给定的数据集可能是从一个大得多(理论上无限大小)的群体中抽取的样本。

为了说明方差-协方差矩阵是如何构建的,我们将遵循一个稍微不同的策略,称为自举。我们将假设 205 辆车的数据集是人口,我们将遵循以下回归策略:**

  1. 我们将从该人群中随机抽取 100 辆大小为 50 的车辆,替换。“替换”意味着在使用样本后,我们将把它放回总体中,以便在进行下一次抽取时可以使用这些数据。with-replacement 策略在许多现实环境中可能不太现实,但是它使数学变得非常简单,并且在汽车数据集的情况下不会导致任何实际困难。**
  2. 我们将使用 OLS 技术对这 50 个车辆样本中的每一个样本训练(也称为拟合)一个线性回归模型。这种模型被称为副OL 东 SR 出( OLSR )模型。**
  3. 在对每个样本进行训练后,我们将记下拟合模型的系数值 *β_0_cap、β_1_cap 和β_2_cap。‘cap’表示这些是相应系数的总体水平值的估计值。***
  4. 我们还将为每个样本记下回归的残差' e '。回归的残差是因变量( City_MPG )的观测值与拟合回归模型预测值之差。

下面是实现上述自举算法的 Python 代码:**

让我们来看看我们的引导实验的结果。

培训结果

让我们打印出使用上述程序对 100 个数据样本运行 OLSR 模型得出的回归系数表(包括截距)。

***print**(df_sample_beta)*

拟合 OLSR 模型的回归系数表,对模型进行 100 次拟合,每次对随机选择的 50 辆车进行采样(图片由作者提供)

该表中的每一行都对应于回归系数的拟合值,这些回归系数是通过在随机选择的 50 辆汽车样本上拟合 OLSR 模型而获得的。

从该表可以看出,拟合模型的回归系数表现得像随机变量。的确,它们随机变量。每个估计的系数遵循某种概率分布,它有一个均值方差**

使用上表中的数据,让我们画出三个回归系数的概率密度函数。下面是它的 Python 代码:

***def** draw_pdf(data, min_X, max_X, var_name):
    hist = np.histogram(data)
    hist_dist = scipy.stats.rv_histogram(hist)
    X = np.linspace(min_X, max_X, 20)
    fig = plt.figure()
    fig.suptitle(**'PDF of '** + var_name)
    plt.plot(X, hist_dist.pdf(X), label=**'PDF'**)
    plt.xlabel(var_name)
    plt.ylabel(**'Density'**)
    plt.show()*#Plot the PDFs of the three coefficient estimates* draw_pdf(df_sample_beta[**'Intercept'**], 0, 100, **'estimated Intercept '**)data=df_sample_beta[**'Curb_Weight'**]
draw_pdf(data, min(data)*0.9, max(data)*1.1, **'estimated coefficient for Curb_Weight'**)data=df_sample_beta[**'Engine_Size'**]
draw_pdf(data, min(data)*0.9, max(data)*1.1, **'estimated coefficient for Engine_Size'**)*

我们得到以下三幅图:

回归系数估计值的 pdf(图片由作者提供)

构建回归系数的方差-协方差矩阵

我们可以使用回归系数值表来计算每个系数的方差以及三个系数的成对协方差。

让我们回忆一下方差和协方差的公式。

给定在大小为 n 的样本上实现的随机变量 x ,以下 x 的样本方差形成了对 a x 的总体方差的无偏估计:

随机变量的总体方差的无偏估计量 x (图片由作者提供)

这里, x_barx 的均值,分母中的 -1 代表因包含均值而损失的单自由度。

两个随机变量 xz 之间总体水平协方差的类似无偏估计量如下:

总体的无偏估计量随机变量的协方差 x z (图片由作者提供)****

使用这些公式,我们将计算回归系数值的方差和成对协方差。

系数的含义如下:

回归模型系数的平均估计值(图片由作者提供)

方差和协方差如下:

回归系数的方差和协方差(图片由作者提供)

以下 Python 代码可用于计算系数估计值的平均值和回归系数的方差-协方差矩阵:

****#Calculate the mean estimate for each coefficient***coeff_means = df_sample_beta.**mean**()

***#Calculate the variance-covariance matrix for each coefficient***coeff_covs = df_sample_beta.**cov**()*

我们会把它们打印出来:

*print(coeff_means)*

系数的平均估计值(图片由作者提供)

***print**(coeff_covs)*

回归系数的方差-协方差矩阵(图片由作者提供)

在上面的矩阵中,由红框指示的沿主对角线的元素包含相应系数估计的方差,而非对角线元素包含成对协方差。

发动机尺寸和整备质量之间的负系数可能看起来是反直觉的,但是这个值是如此之小,以至于人们应该忽略这个符号。事实上,考虑到抽样技术的性质,每次我们进行 1000 个样本的实验时,我们将得到方差和协方差的稍微不同的值。

上述推导模型系数协方差矩阵的过程被称为 bootstrap 技术。重要的是要记住,回归系数的协方差是相应真实总体水平协方差的有限样本估计值(通常假设未知)。

当使用 bootstrap 技术时,当抽取次数很少时,需要使用因子(D+1)/D 向上调整估计的协方差,其中 D 是抽取次数。在我们的实验中,D=100,这个调整因子只有 1.01,可以忽略不计。

模型系数的标准误差

系数估计值的标准误差就是代表系数估计值的随机变量的标准偏差。

符号方面,

SE(【β_ cap | X】= STDEV(β_ cap | X)=SQRT(Var(β_ cap|X)

回想一下方差-协方差矩阵的对角元素包含系数的方差。因此,每个系数的标准误差可以通过取协方差矩阵的相应对角元素的平方根来计算。

让我们计算并打印出标准偏差:

*coeff_std_errors = np.**sqrt**(coeff_covs)
**print**(coeff_std_errors)*

以下是红框中显示的三个系数的标准误差:

使用 bootstrapping 技术计算的系数估计的标准误差(图片由作者提供)

上述计算系数标准误差的技术被称为标准误差的自举,并且如此获得的标准误差被称为系数估计的自举标准误差**

模型系数的置信区间

使用以下公式计算每个系数的(1-α)100%置信区间:*

回归系数置信区间公式(图片由作者提供)

在上面的公式中:

  • β_cap_i 是模型对数据样本拟合后上报的第 I 个系数的拟合值。
  • 方括号内的 t 值是从具有 (n-k) 自由度的双边 t 分布返回的临界值,其中 n 是样本大小, k 是包括截距在内的回归系数的数量。
  • se_i_iβ_cap 的方差-协方差矩阵中第对角元素的平方根。**

为了计算置信区间,我们需要在随机抽取的 50 个样本上拟合 OLSR 模型:

****# Select a random sample of size SAMPLE_SIZE***df_sample = df.**sample**(**n**=SAMPLE_SIZE)***# carve out the X and y matrices using Patsy***y_train, X_train = **dmatrices**(model_expr, df_sample, **return_type**=**'**dataframe**'**)***# Build an OLS regression model using Statsmodels***olsr_model = sm.**OLS**(**endog**=y_train, **exog**=X_train)***# Fit the model on (y, X)*** olsr_results = olsr_model.**fit**()***#Print the training summary of the fitted model* print**(olsr_results.**summary**())*

我们得到以下培训总结输出:

OLSR 模式的培训总结(图片由作者提供)

拟合系数 β_cap 如下:

***print**(olsr_results.params)**Intercept** 49.987734
**Curb_Weight** -0.009330
**Engine_Size** -0.001189*

样本量 n 为 50,包括截距在内有 3 个回归变量。所以,(n-k)= 50–3 = 47。在 α=0.05 处的双边 t 值为 2.012。**

整备质量的系数估计值的 95%置信区间计算如下:

整备质量的 95%置信区间
=(-0.009330)+/-(2.012 * 0.002586)
=(-0.009330)+/-(0.005203032)
=
[-0.014533032,-0.004126968】******

这与 statsmodels 在培训总结中报告的整备质量的 95%置信区间非常接近:**

95%的 CI 由 Statsmodels 报告(图片由作者提供)

让我们也比较一下 Statsmodels 报告的标准误差和我们使用 Bootstrapping 技术计算的相应误差:

以下是 Statsmodels 报道的几个:

Statsmodels 报告的标准误差(图片由作者提供)

以下是我们已经引导的一些:

使用 bootstrapping 技术计算的系数估计的标准误差(图片由作者提供)

我们可以看到,虽然整备质量发动机尺寸的标准误差与 Statsmodels 报告的误差非常接近,但是截距的标准误差相差很小(3.731 比 3.915)。那么我们应该使用自举的标准误差还是应该使用统计软件包报告的误差呢?毕竟,如果方差-协方差矩阵未指定,系数估计的标准误差将是不正确的,置信区间也将是不正确的。**

我将在下周的第 2 部分中解决这个重要的问题:深入探究经典线性回归模型的方差-协方差矩阵。****

敬请期待,造型快乐!

参考文献、引文和版权

数据集

汽车数据集 引用: Dua,d .和 Graff,C. (2019)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。 下载链接

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

一个深入的 Python 装饰者教程,你可以实际使用

原文:https://towardsdatascience.com/an-in-depth-tutorial-to-python-decorators-that-you-can-actually-use-1e34d3d2d305

深入 Python 的内部

照片由菲奥娜艺术

介绍

使用 Python 和许多其他语言可以做的一件神奇的事情是修饰函数。装饰者可以修改函数的输入、输出以及函数本身的行为。最棒的是,您只需一行代码就可以完成所有这些操作,根本不需要修改函数语法!

要了解 decoratorss 如何工作以及如何为自己创建一个 decorator,您需要了解一些重要的 Python 概念。

因此,在我们开始编写 decorators 之前,我们将深入学习一些 Python 的内部知识,比如作用域和闭包。如果你熟悉这些概念,请跳过它们,到第 5 部分,所有的乐趣开始了!

https://ibexorigin.medium.com/membership

获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:

https://alphasignal.ai/?referrer=Bex

函数是对象

您喜欢 Python 的一个原因是它能够将任何东西表示为对象,函数也不例外。对于第一次阅读这篇文章的人来说,将一个函数作为参数传递给另一个函数可能看起来很奇怪,但是这样做是完全合法的:

作为对象,函数与以下内容完全相同:

  • 用线串
  • 整数和浮点数
  • 熊猫数据帧
  • 列表、元组、字典
  • osdatatimenumpy这样的模块

您可以将函数赋给新变量,并使用它来调用函数:

>>> new_func = my_func
>>> new_func()
Printing the function's argument

现在这个变量也包含了函数的属性:

您还可以将每个函数存储在其他对象中,如列表和字典,并调用它们:

范围

考虑鲍勃和约伯之间的对话:

  • 鲍勃:“乔恩,你昨天为什么没来上课?”
  • 乔恩:“我感冒了……”

不是最好的故事,但当鲍勃问乔恩昨天缺席的原因时,我们知道他指的是站在他旁边的乔恩,而不是某个在另一个国家的随机乔恩。作为人类不难注意到这一点,但是编程语言使用一种叫做 scope 的东西来告诉我们在程序中引用的是哪个名字。

在 Python 中,名字可以是变量、函数、模块名等。

考虑这两个变量:

>>> a = 24
>>> b = 42
>>> print(a)
24

在这里,print毫不费力地告诉我们,我们指的是我们刚刚定义的a。现在考虑一下这个:

>>> def foo():
...     a = 100
...     print(a)

如果我们运行foo,你认为会发生什么?它会印 24 张还是 100 张?

>>> foo()
100

Python 如何区分我们在函数开头定义的a?这就是作用域变得有趣的地方,因为我们引入了不同的作用域层:

作者图片

上图显示了这个小脚本的范围:

全局范围是您的脚本/程序的整体范围。与ab具有相同缩进级别的变量、函数、模块将在全局范围内。例如,foo函数在全局范围内,但是它的变量afoo的局部范围内。

在一个全局范围内,可以有许多局部范围。例如,for循环和列表理解和函数中的变量在它们的代码块中是局部的,不能从全局范围访问。

global之外还有一个更大的范围级别:

作者图片

内置范围包含您用Pythonpipconda安装的所有模块和包。

现在,让我们探讨另一种情况。在我们的foo函数中,我们想要修改全局a的值。我们希望它是一个字符串,但是如果我们在foo里面写a = 'some text',Python 将会创建一个新的变量而不改变全局a

Python 为我们提供了一个关键字,让我们指定我们引用的是global范围内的名称:

编写global <name>将让我们修改global范围内名称的值。

顺便说一句,坏消息😁上图中,我省略了一个级别的范围。在globallocal之间,还有一层我们没有覆盖:

作者图片

当我们有嵌套函数时,作用域开始发挥作用:

在嵌套函数outer中,我们首先创建一个名为my_var的变量,并将其赋给字符串Python。然后我们决定创建一个新的inner函数,并想给my_var赋一个新值——Data Science,并打印出来。但是如果我们运行它,我们会看到my_var仍然被分配给‘Python’。我们不能使用global关键字,因为my_var不在全局范围内。

对于这种情况,您可以使用nonlocal关键字来访问外部函数(非本地)范围内的所有名称,而不是global:

总之,作用域告诉 Python 解释器在程序中何处寻找名字。在单个脚本/程序中可以有四个级别的范围:

  • 内置:Python 安装的所有包名,pipconda
  • 全局:一般范围,脚本中没有缩进的所有名称
  • Local:包含代码块中的局部变量,如函数、循环、列表理解等。
  • 非局部:在嵌套函数的情况下,在globallocal之间的一个额外的范围级别

关闭

在我解释装饰者是如何工作的之前,我们还需要谈谈闭包。让我们从一个例子开始:

我们在foo中创建一个嵌套函数bar并返回它。bar尝试打印数值of x:

当我们写var = foo()的时候,我们把bar的功能分配给了var。现在var可以用来调用bar。当我们调用它时,它打印出 42。

>>> var()
42

但是等一下,var怎么会知道x的事情呢?x是定义在foo的范围内,而不是bar的范围内,你会认为xfoo的范围外是无法访问的。这就是闭包的由来。

闭包是一个函数的内置内存,它包含函数运行所需的所有非本地名称(在一个元组中)!

所以,当foo返回bar时,它附加了所有非本地变量bar需要在foo的范围之外运行。您可以使用.__closure__属性访问函数的闭包:

一旦您以元组的形式访问函数的闭包,它将包含名为cells的元素,该元素具有单个非局部参数的值。闭包内可以有函数需要的任意多个单元格:

在上面的例子中,变量x, y, zchild的非局部变量,所以它们被添加到函数的闭包中。任何其他名字如valueoutside都不在闭包中,因为它们不在非局部范围内。

现在,考虑这个更棘手的例子:

我们创建一个parent函数,它接受一个参数和一个嵌套函数child,后者打印传递给parent的任何值。我们用var(‘dummy’)调用parent,并将结果赋给func。如果我们称之为:

>>> func()
dummy

不出所料,它打印出了‘dummy’。现在让我们删除var并再次调用func:

>>> # Delete 'var'
>>> del var>>> # call func again
>>> func()
dummy

它仍然打印出“dummy”。为什么?

你猜对了,它被添加到闭包里了!因此,当一个来自作用域外层的值被添加到闭包时,它将保持不变,即使我们删除了原始值!

>>> func.__closure__[0].cell_contents‘dummy’

如果我们不删除var并改变它的值,闭包仍将包含它的旧值:

当我们在下一节讨论装饰者时,这个概念将会很重要。

让我们回顾一些概念,以确保您理解:

  • 闭包是嵌套函数的内部内存,它包含存储在元组中的所有非局部变量。
  • 一旦一个值存储在闭包中,它就可以被访问,但是如果原始值被删除或修改,它就不能被覆盖
  • 嵌套函数是在另一个函数中定义的函数,遵循以下一般模式:
>>> def parent(arg):

...     def child():
...        print(arg)
...    
...    return child

最后,装修工

装饰器是修改另一个函数的函数。他们可以改变函数的输入、输出甚至行为。

让我们从一个非常简单的装饰开始:

现在,我们创建一个函数,它对传递的任何参数求平方,我们用add_one来修饰它。add_one将传递函数的参数加 1:

要将函数用作装饰器,只需将@ symbol放在函数定义的正上方,后面跟着装饰函数的名称。当我们将 5 传递给修饰过的square函数时,它没有返回 25,而是产生了 36,因为add_one接受了square的参数,即 5,并给它加 1,然后将其插回square:

>>> square(10)
121

现在,让我们仔细看看add_one.

首先,让我们从add_one开始,它只返回传递给它的函数:

为了让我们的装饰器返回一个修改过的函数,定义一个嵌套函数来返回通常是有帮助的:

我们的装潢师仍然无所事事。在add_one内部,我们定义了一个嵌套的child函数。child只接受一个参数并调用传递给add_one的任何函数。然后,add_one返回child

在这个嵌套的*child*函数的例子中,我们假设传递给*add_one**func**child*的参数数量完全相同。

现在,我们可以让所有的魔法都发生在child函数内部。我们不是简单地调用func,而是想修改它的参数,给它们加 1:

通知func(a + 1)?它调用传递给add_one的任何参数,并在参数中加 1。这一次,我们将覆盖square,而不是创建一个新变量来存储child:

>>> square = add_one(square)
>>> square(5)
36

现在,当我们经过 5 时,它返回 36 而不是 25。

即使我们覆盖了它,它如何使用square功能?好在我们学习了闭包,因为旧的square现在在child的闭包里面:

>>> square.__closure__[0].cell_contents
<function __main__.square(a)>

至此,我们的add_one函数已经可以用作装饰器了。我们可以把@add_one放在square的正上方,看看神奇的事情发生了:

装饰者的真实例子

我想如果我不向你展示如何创建一个timer装饰器,那将是一个遗憾:

这一次,注意我们是如何使用*args**kwargs的。当我们不知道函数中位置参数和关键字参数的确切数目时,就使用它们,这种情况下很好,因为我们可以在任何类型的函数上使用timer

现在,您可以在任何函数上使用这个装饰器来确定它运行多长时间。无重码!

下一个非常有用的装饰器是缓存装饰器。缓存装饰器对于计算量大的函数非常有用,你可能会用相同的参数多次调用这些函数。缓存闭包中每个函数调用的结果将使我们能够在修饰函数被调用时立即返回结果:

在 main,cache函数中,我们希望创建一个字典,将元组中的所有参数存储为键及其结果。缓存字典将如下所示:

cache = {
    (arg1, arg2, arg3): func(arg1, arg2, arg3)
}

我们可以使用参数元组作为键,因为元组是不可变的对象。

现在,让我们看看如果我们用cachetimer装饰我们的睡眠功能会发生什么:

首先,让我们试着睡 10 秒钟:

>>> sleep(10)
sleep took 10.0001 seconds to run!

不出所料,运行了 10 秒。现在,如果我们再次以 10 作为参数运行sleep,你认为会发生什么:

>>> sleep(10)
sleep took 0.0 seconds to run!

用了 0 秒!我们的缓存装饰工程!

接受参数的装饰者

到目前为止,我们对装饰师的了解相当扎实。然而,当你让装饰者接受参数时,他们的真正力量就来了。

考虑这个装饰器,它检查函数的结果是否是类型str:

我们在一个虚拟函数上调用它来检查它是否工作:

它正在工作。然而,如果我们有办法检查任何数据类型的函数的返回类型,这不是很酷吗?来,看看这个:

使用这种类型的修饰器,您可以为所有的函数编写数据类型检查。让我们一起从头开始建造它。

首先,让我们创建一个简单的装饰器,它调用传递给它的任何函数:

我们如何调整这段代码,使它也接受自定义数据类型并对func的结果进行检查?我们不能添加额外的参数decorator,因为 decorators 应该只接受一个函数作为参数。

我们定义了一个更大的父函数,它返回一个装饰器来解决这个问题。这样,我们可以将任何参数传递给父函数,而父函数又可以在装饰器中使用:

注意我们是如何将装饰器包装在一个更大的父函数中的?它将一个数据类型作为参数,传递给我们的装饰器,然后返回它。在wrapper中,我们写了type(result) == dtype,不管数据类型是否匹配,它都计算为TrueFalse。现在,您可以使用此函数对任何函数执行类型检查:

保留修饰函数的元数据

在这之前,我们从来没有检查过一件事——被修饰的函数是否以所有方式被保留?例如,让我们回到我们的sleep函数,我们用timer来修饰它:

让我们调用它并检查它的元数据:

我们检查函数的三个元数据属性。前两个回归了None,不过应该是出了点什么。我的意思是,sleep有一个长 docstring 和一个等于 5 的默认参数。他们去哪里了?我们在调用__name__得到函数名wrapper时得到了答案。

如果我们研究一下timer的定义:

我们可以看到,我们实际上并没有返回传递的函数,而是在wrapper内部返回它。显然wrapper没有 docstring 或者任何默认参数,这就是为什么我们得到了上面的None

为了解决这个问题,Python 为我们提供了一个来自functools模块的有用函数:

wrapper函数上使用wraps让我们保留所有附加到func的元数据。注意我们是如何在函数定义上方将func传递给wraps的。

如果我们使用timer的这个修改版本,我们会看到它如预期的那样工作:

使用wraps(func)对于编写装饰者来说是一个很好的实践,所以把它添加到我们今天定义的所有装饰者中吧!

结论

看完这个帖子,你对打造装修工有了很强的认识。更重要的是,你知道它们是如何工作的,以及它们工作的方式。

作为最后一点,我建议每当你有重复的代码在你的函数上执行类似的任务时,就使用 decorators。装饰者可以是使你的代码变得枯燥的另一个步骤(不要重复你自己)。

感谢您的阅读!

https://ibexorigin.medium.com/membership https://ibexorigin.medium.com/subscribe

阅读更多我的故事:

https://ibexorigin.medium.com/6-sklearn-mistakes-that-silently-tell-you-are-rookie-f1fe44779a4d

Python 中假设检验的交互式指南

原文:https://towardsdatascience.com/an-interactive-guide-to-hypothesis-testing-in-python-979f4d62d85

t 检验、方差分析、卡方检验及示例

Python Cheatsheet 中的统计测试(图片来自作者的网站

什么是假设检验?

假设检验是推断统计学中的一个重要部分,在推断统计学中,我们使用样本中的观察数据来得出关于未观察数据(通常是总体)的结论。

假设检验的含义:

  • 临床研究:广泛用于心理学、生物学和医疗保健研究,以检验临床试验的有效性
  • A/B 测试:可应用于商业环境,通过测试不同版本的活动激励、网站设计等来提高转化率
  • 机器学习中的特征选择:基于过滤器的特征选择方法使用不同的统计测试来确定特征的重要性
  • 学院或大学:嗯,如果你主修统计学或数据科学,它很可能会出现在你的考试中…

假设检验的 4 个步骤

(如果你喜欢视频演练,也请查看我的 YouTube 视频。)

第一步。定义无效假设和替代假设

零假设(H0) 根据统计检验,可以有不同的表述,但可以概括为两个或多个变量之间不存在差异、不存在关系或不存在依赖关系。

替代假设(H1) 与零假设相矛盾,它声称关系存在。这是我们想要证明正确的假设。然而,在统计学中更保守的方法更受青睐,我们总是假设零假设是真的,并试图找到证据来拒绝零假设。

第二步。选择适当的统计检验

常见的统计检验类型包括 t 检验、z 检验、anova 检验和卡方检验

选择适当的统计测试(图片由作者提供)

T 检验:比较两组/两类小样本数值变量

Z 检验:用大样本量比较两组/两类数值变量

ANOVA 检验:比较两组或多组/多类数值变量之间的差异

卡方检验:检验两个分类变量之间的关系

相关性检验:检验两个数值变量之间的关系

第三步。计算 p 值

p 值的计算方式因统计测试而异。首先,基于观察到的样本数据的平均值和标准偏差,我们能够导出检验统计值(例如 t 统计)。然后计算得到这个检验统计量的概率给定零假设的分布(如学生 t 分布),我们就求出 p 值。我们将使用一些例子来更详细地说明这一点。

第四步。确定统计显著性

然后将 p 值与显著性水平(也称为α值)进行比较,以确定是否有足够的证据来拒绝零假设。显著性水平是预定的概率阈值,通常为 0.05。如果 p 值大于阈值,则意味着该值很可能出现在零假设为真的分布中。另一方面,如果低于显著性水平,这意味着它不太可能出现在零假设分布中,因此拒绝零假设。

除了显著性水平,功效分析也常用于假设检验,查看我关于统计功效的文章。

用示例进行假设检验

Kaggle 数据集“客户个性分析”用于本案例研究,展示不同类型的统计测试:T 检验、ANOVA 和卡方检验。它们对大样本量很敏感,当样本量很大时,几乎肯定会产生非常小的 p 值。因此,我从原始数据中随机抽取了一个样本(大小为 100 ):

sampled_df = df.sample(n=100, random_state=100)

t 检验

t 测试(图片来自作者的网站)

当我们想要测试一个数字变量和一个分类变量之间的关系时,使用 T-test。t 检验有三种主要类型。

  1. 一个样本的 t-检验:根据一个常量值检验一个组的平均值
  2. 双样本 t 检验:检验两组间均值的差异
  3. 成对样本 t 检验:检验同一受试者两次测量值的均值差异

例如,如果我想测试“最近”(客户上次购买后的天数)是否有助于预测“响应”(客户是否在上次活动中接受了报价),我可以使用双样本 t-检验。

第一个样本是接受报价的客户的“最近”:

recency_P = sampled_df[sampled_df['Response']==1]['Recency']

第二个样本是拒绝该提议的客户的“最近”:

recency_N = sampled_df[sampled_df['Response']==0]['Recency']

为了直观地比较这两组的“近期性”,我们可以使用直方图(或 distplot)来显示分布。

t 检验的距离图(图片由作者提供)

与消极反应相比,积极反应似乎具有较低的新近性。为了量化差异,让我们按照假设检验中的步骤进行 t 检验。

第一步。定义无效假设和替代假设

  • null:在上次活动中接受报价的客户和不接受报价的客户在最近度上没有区别
  • 替代方案:与不接受报价的客户相比,接受报价的客户具有更低的新近度

第二步。选择合适的测试

要检验两个独立样本之间的差异,双样本 t 检验是最合适的统计检验。当零假设为真时,检验统计量遵循学生 t 分布。t 分布的形状由自由度决定,计算方法是两个样本大小之和减 2。

导入 Python 库 scipy.stats 并创建如下所示的 t 分布。

from scipy.stats import t
rv = t(df=100-2)

第三步。计算 p 值

Python 中有一些方便的函数可以计算分布中的概率。对于分布范围内覆盖的任意 x,pdf(x)为 x 的概率密度函数——可表示为下面的橙色线, cdf(x) 为 x 的累积密度函数——可视为累积面积。在本例中,我们正在测试另一个假设,即肯定回答的最近次数减去否定回答的最近次数小于 0。因此,我们应该使用单尾检验,并将我们得到的 t 统计量与该分布中的最低值进行比较,因此在这种情况下,p 值可以计算为CDF(t _ statistics)

t 统计和 t 分布(图片由作者提供)

【ttest _ ind()是 python 中一个方便的独立 t 测试函数,它已经自动为我们完成了所有这些工作。传递两个样本 recency _ P 和 recency_N 作为参数,选择备选项=“less”,我们得到 t 统计量和 P 值。

Python 中的 t-test(图片作者提供)

这里我用 Plotly 来形象化 t 分布中的 p 值。将鼠标悬停在线上,查看点概率和 p 值如何随着 x 的移动而变化。填充颜色的区域突出显示了我们在这个特定示例中获得的 p 值。

t 分布与 t 统计的交互可视化(查看 代码 )

如果你想自己构建这个,可以查看我网站上的 代码片段

第四步。确定统计显著性

常用的显著性水平阈值是 0.05。由于这里的 p 值(0.012)小于 0.05,所以我们可以说,基于收集的样本,它具有统计学意义。接受该报价的客户的近期发生率较低可能不是偶然发生的。这进一步表明特征“响应”可能是目标变量“新近性”的强预测器。如果我们要为预测“最近”值的机器学习模型执行特征选择,“响应”可能具有很高的重要性。

方差分析检验

方差分析测试(图片由作者提供)

既然我们知道 t 检验是用来比较一个或两个样本组的平均值的。如果我们想要测试两个以上的样本呢?使用 ANOVA 测试。

ANOVA 通过计算组间方差与组内方差的比率来检查组间差异。较大的比率表明组间的差异是组间差异的结果,而不仅仅是随机的。

作为一个例子,我使用特征“Kidhome”来预测“NumWebPurchases”。“Kidhome”有三个值——0、1、2,自然形成三组。

*kidhome_0 = sampled_df[sampled_df['Kidhome']==0]['NumWebPurchases']
kidhome_1 = sampled_df[sampled_df['Kidhome']==1]['NumWebPurchases']
kidhome_2 = sampled_df[sampled_df['Kidhome']==2]['NumWebPurchases']*

首先,将数据可视化。我发现箱线图是方差分析测试最一致的视觉表现。

方差分析检验的箱线图(图片由作者提供)

三个组之间似乎有明显的差异。所以让我们进行 ANOVA 测试来证明是否是这样。

1。定义假设:

  • 零:三组之间没有差异
  • 备选方案:至少两组之间存在差异

2。选择适当的检验: ANOVA 检验是检查两个以上组的数值与分类值之间关系的首选方法。方差分析检验中零假设的检验统计量也遵循由自由度定义的分布,即 f 分布。自由度通过总样本数(n)和组数(k)来计算。

  • dfn = n — 1
  • dfd = n — k
*from scipy.stats import f
dfn = 3-1
dfd = 100-3
rv = f(dfn, dfd)*

3。计算 p 值:为了计算 f 统计量的 p 值,我们用 f 分布的右尾累积面积,也就是1-rv.cdf(f_statistics)

f 统计和 f 分布(图片由作者提供)

*x = np.linspace(rv.ppf(0.0001), rv.ppf(0.9999), 100000)
y = rv.pdf(x)
pvalue = 1 - rv.cdf(x)*

f 分布与 f 统计的交互可视化(查看 代码 )

为了使用 Python 轻松获得 f 统计数据和 p 值,我们可以使用函数stats.f_oneway()返回 p 值:0.00040。

*f_stat, pvalue = stats.f_oneway(kidhome_0, kidhome_1, kidhome_2)*

4。确定统计显著性:将 p 值与 0.05 的显著性水平进行比较,我们可以推断有强有力的证据反对零假设,并且很可能至少两组之间的“NumWebPurchases”存在差异。

卡方检验

卡方检验(图片来自作者的网站

卡方检验用于检验两个分类变量之间的关系。基本原则是,如果两个分类变量是独立的,那么当一个分类变量改变时,另一个分类变量应该具有相似的组成。我们来看“教育”和“反应”是否独立的例子。

首先,使用堆积条形图和列联表汇总每个类别的数量。

*ed_contingency = pd.crosstab(sampled_df['Education'], sampled_df['Response'])*

**

卡方检验的堆积条形图(图片由作者提供)

如果这两个变量彼此完全独立(零假设成立),那么所有教育组中正面回答和负面回答的比例应该是相同的。看起来组成略有不同,但这是否足以说明存在依赖性——让我们进行卡方检验。

1。定义假设:

  • 空:“教育”和“反应”是相互独立的。
  • 备选案文:“教育”和“反应”是相互依存的。

2。选择适当的检验:选择卡方检验进行分类 vsl 分类统计检验。卡方分布由自由度决定,计算方法为(行-1)x(列-1)。

*from scipy.stats import chi2
r = 5
c = 2
dof = (5-1) * (2-1)
rv = chi2(df= dof)*

3。计算 p 值: p 值计算为右尾累积面积:1-rv.cdf(chi2_statistics)

χ2 统计和χ2 分布(图片由作者提供)

*x = np.linspace(rv.ppf(0.0001), rv.ppf(0.9999), 100000)
y = rv.pdf(x)
pvalue = 1 - rv.cdf(x)*

Python 还提供了一个有用的函数来获取给定列联表的 chi 统计数据和 p 值。

*chi2_stat, pvalue, dof, exp = chi2_contingency(ed_contingency)*

chi 分布与 chi 统计的交互可视化(查看 代码 )

4。确定统计显著性e:p 值为 0.41,表明不具有统计显著性。因此,我们不能拒绝这两个分类变量是独立的零假设。这也表明“教育程度”可能不是“反应”的强预测因素。

感谢到目前为止,我们已经涵盖了本文中的许多内容,但仍然有两个重要的假设检验值得在即将到来的帖子中分别讨论。

  • z 检验:检验两类数值变量之间的差异——当样本量较大时
  • 相关性:测试两个数值变量之间的关系

如果你想阅读更多这样的文章,我将非常感谢你的支持,注册中级会员😃

带回家的信息

在本文中,我们交互式地探索和可视化三种常见统计检验之间的差异:T 检验、ANOVA 检验和卡方检验。我们还使用示例来演练假设检验中的基本步骤:

1.定义无效假设和替代假设

2.选择合适的测试

3.计算 p 值

4.确定统计显著性

更多这样的文章

* Destin Gong

德斯坦贡

机器学习实用指南

View list10 storiesPrincipal Component Analysis for MLTime Series Analysisdeep learning cheatsheet for beginnerDestin Gong

德斯坦贡

开始学习数据科学

View list8 storiesStatistical Tests in Python

原载于 2022 年 4 月 14 日 https://www.visual-design.net**的 *

帕斯卡三角形:一种方法

原文:https://towardsdatascience.com/an-interesting-approach-a78941855ffe

利用数学概念用 C 语言打印出帕斯卡三角形。

老实说,关于编程语言,我不喜欢用 C 写代码。然而,由于一个大学模块(在 C 中),一个练习是实现将打印出 Pascal 的第 n 行的代码。

尽管 C 对更大的整数(例如,20!),我决定实现一组计算的简单版本。在尝试使用组合学之前,我已经经历了各种方法:临时变量和加法,2D 数组等。所有这些我都不知道如何实际实现。因此,我知道组合学可能是获得答案的最快方法。

组合:是什么?

计数可能是一个非常琐碎的任务。手里拿着 4 块巧克力,你数 1,2,3,4。然而,一旦选择元素的子集。

假设你是一名足球教练,正在挑选一支 11 人的球队。你有 40 个感兴趣的申请人,你想用不同的组合测试他们(让我们忽略这个演示的形式)。

自然地,你会有 40 个选择给第一个玩家,39 个给第二个,38 个给第三个,以此类推,直到你填满了测试队的 11 个位置。以同样的方式,你最终又选择了 11 个人,(所以剩下的 18 个人还没有被选择)。然而,即使你重复这个过程,你会观察到有一组确定的球员没有发挥彼此和/或对彼此。

因此,为了避免这种错误计数,您需要使用组合的概念。帕斯卡三角形中显示的数字就是建立在这个概念上的。

帕斯卡三角:简介

前一行中的两个元素递归相加形成下一行的一个元素,从而形成一个模式。同样,每行也有一个模式。就是给定行中的每个元素都可以写成“n 选 k”的形式(如图 1)。

数学上,“n 选择 k”用下面给出的公式描述。(在本文中,我不会深入讨论证明的细节,但是,它可以用数学归纳法来展示)

图 1: n 选 k(图片由作者提供)

使用 nCk 的概念及其数学公式,我们可以继续计算给定行的单个元素。

图 2:帕斯卡三角形样本(第 1-4 行)(图片由作者提供)

回头看看第一、二、三、四排。可以看出,在每一行中都有一个图案(不仅在各行之间,而且在各行之间)。此外,可以看到 k 的值(如果元素以 n 选择 k 的形式书写)从 0 增加到比行数少 1。例如,如果查看第 4 行,则第 4 行中的最后一个元素是 3C3。由此,看到了另一个细节;n 的值(在 n 中选择 k)比行号小 1。

这可以推广到第 n 行(n 选择 k)

图 3:帕斯卡三角形的第 n 行

重温问题:用 C 打印三角形

最初,我尝试了简单的方法,将第 n 行的公式直接实现到 c 中。我进行了一些测试,一切都很好,直到我尝试计算阶乘(12)。剧透提醒:这是一个巨大的数字;很自然地,代码出现了溢出错误,并开始打印出对我来说毫无意义的数字。我还尝试使用十几个临时变量,认为递归地重复加法过程就可以完成这项工作(不幸的是没有成功)。以下是我在找到有效方法之前对这个问题的尝试。

作者图片

作者图片

作者图片

作者图片

作者图片

好吧…这些都没用,但是有一个用了。

在这些尝试之后,我改变了对三角形整体的看法。相反,我注意到每一行都是在受控环境中产生的模式。这样,我可以避免内存分配和内存泄漏。

以这种方式思考,我探索了同一行中两个连续数字之间的关系。我试着看看是否有一个 nCk + b = nC(k+1)没有让我去任何地方。经过尝试,nCk * b = nC(k+1)让我发现了一些非常有用的结果。

作者图片

下面是我的结果的(更清晰的)总结

作者图片

结束语

我成功地打印出了 20 行帕斯卡三角形。虽然在这种情况下,我使用了上面的公式来避免溢出错误,但是同样的公式也可以在其他语言中实现,比如 Python,它不会为更大的数字返回溢出错误。在这种情况下,这可以被视为打印帕斯卡三角形的内存优化方法。

机器学习的离散化技术介绍

原文:https://towardsdatascience.com/an-intro-to-discretization-techniques-for-machine-learning-93dce1198e68

概述和 Python 实现

照片由 Vladislav Vasnetsov 拍摄:https://www . pexels . com/photo/mished-color-plastic-trash-bins-2682683/

许多机器学习算法在用离散变量训练时表现更好。因此,特征工程程序通常会包含离散化。你可能会发现,机器学习竞赛中的许多获奖作品都利用了这一技术。

不要被 5 音节词吓倒。离散化只需要将连续值转换成离散的类别。这是统计学中的一个常见概念,通常被称为“宁滨”或“木桶理论”。

离散化在机器学习中有许多优点,并且在 Python 中易于执行,这将详细解释。

利益

乍一看,将连续变量转换成离散变量的概念似乎是不必要的,但将这一步骤包括在特征工程过程中有许多好处。

  1. 降低噪音

连续变量倾向于存储具有微小波动的信息,这对感兴趣的机器学习任务没有提供附加值。处理这样的值将导致模型产生大量噪声,这不可避免地会影响性能。

离散化解决了这一问题,使用户能够通过创建包含更少唯一值的分布来限制这种噪声。

2。提供直观的功能

根据不同的用例,离散变量可能比连续变量更直观。

例如,如果要建立一个比较年轻人和老年人的因果模型,可能值得将年龄记录为离散变量(例如,“儿童”、“成人”和“老年人”),而不是连续变量。

这将使模型的结果更容易解释,因为特性是为应用程序定制的。

3。最小化异常影响

最后,离散化减轻了离群值的影响。无论异常值对值分布的倾斜程度如何,在转换后,它都将被转换为较高或较低的组。

缺点

尽管有很多好处,离散化有一个明显的缺点:信息丢失。

当然,这是特征工程中许多转换的特征。像归一化和主成分分析(PCA)这样的技术自然会导致一些信息损失。

总的来说,离散化中的信息损失不一定很大,但是在考虑转换后应该有多少个离散值时,值得记住。

离散化的类型

有许多方法可以在 Python 中实现离散化。

可以促进这种转换的两个最突出的 Python 包是: scikit-learnfeature_engine

为了展示这些包提供的一些转换器,让我们使用下面的虚构数据,这些数据由预测特征“年龄”和目标标签“糖尿病”组成。

代码输出(由作者创建)

目标是使用各种可用的离散化方法将年龄特征中的值转换成 5 个离散组。

我们将使用以下函数来可视化转换,并突出显示每种方法的特性。

1。等频离散化

等频率离散化需要将连续数据转换成箱,每个箱具有相同(或相似)数量的记录。

为了在 Python 中实现这个方法,我们可以使用 scikit-learn 包的 KBinsDiscretizer ,其中的strategy超参数被设置为“分位数”。

代码输出(由作者创建)

就观察数量而言,这 5 个箱相对接近。然而,为了获得这种均匀性,每个箱的宽度必须是不均匀的。

我们也可以用 feature_engine 包中的equalfrequencydiscreditser来执行这个转换。

2。等宽离散化

顾名思义,等宽离散化将数据转换为具有相同宽度的条块。与等频率离散化非常相似,我们可以将这种技术与 sci-kit learn 包的 KBinsDiscretizer 一起使用。但是,strategy超参数应设置为“统一”。

代码输出(由作者创建)

如输出所示,所有创建的条块的宽度为 15.4。

我们也可以通过使用 feature_engine 包的 EqualWidthDiscretiser 来执行这个转换。

3。k-均值离散化

k-均值离散化需要使用 k-均值聚类算法将数据点分配给箱。

也可以用 scikit-learn 包的 KBinsDiscretizer 来执行,其中的strategy超参数被设置为‘k means’。

代码输出(由作者创建)

4。决策树离散化

决策树离散化与之前的方法不同,它是一种监督的学习技术,这意味着它需要使用目标标签来转换连续变量。

顾名思义,它使用决策树算法寻找理想的分割点来分割观察值。

我们可以用 feature_engine 包中的decision tree discrete ser来实现这一技术。

代码输出(由作者创建)

5。自定义离散化

最后,用户可以选择根据自定义规则将其连续变量转换为离散值。具有一定领域知识的用户可以受益于创建具有预定义宽度的箱子。

在我们的示例中,假设我们希望将年龄特征转换为预定义的组,包括年龄组“1–12”、“13–18”、“19–30”、“31–60”和“60–80”。

我们可以用 Python 中的 feature_engine 包的arbitrary discretizer来实现这一点。

代码输出(由作者创建)

关于 feature_engine 包的说明

值得注意的是,feature_engine 转换器与 scikit-learn 工具兼容,这意味着它们可以与其他转换器一起用于无缝的特征工程过程。

为了展示这一点,我们可以将 feature_engine 包的 EqualWidthDiscretiser 合并到 sci-kit 学习管道中,以生成预测作为示例。

结论

照片由 Prateek KatyalUnsplash 上拍摄

有时候,最简单的转换比最复杂的机器学习算法能获得更多的好处。

像噪声和极值这样的因素在数据集中普遍存在,很难通过增加模型的复杂性来克服。

长期以来,离散化被证明是一种将数据转换为更易于管理的有效方法,从而改善了任何后续建模的结果。这就是为什么这种技术通常包含在机器学习竞赛的获奖作品中。

我祝你在数据科学的努力中好运!

R 中的情感分析介绍 Twitter 对贝克·梅菲尔德有何看法?

原文:https://towardsdatascience.com/an-intro-to-sentiment-analysis-in-r-how-does-twitter-feel-about-baker-mayfield-cda513ed0b78

使用 Twitter 开发者 API 和 R 库分析受众情绪

全国的体育迷定期讨论他们喜欢的球队、球员、教练等。贯穿每个运动赛季。毕竟,共同的热情将人们联系在一起——而体育运动经常将人们聚集在一起。不同的球队有如此多的观点,如果我们想更好地了解某个球队、教练、比赛或球员的情绪会怎么样呢?

一些 R 包使我们能够进行情感分析,以确定社交媒体帖子是积极的、消极的还是中立的。当我们将这些与 Twitter 开发人员 API(或用于数据提取的 Netlytic)结合起来时,我们能够解析我们选择的主题的数千条推文。

桑德罗·舒赫在 Unsplash 上的照片

在本赛季的大部分时间里,继上赛季季后赛之后,克利夫兰·布朗队的四分卫贝克·梅菲尔德受到了来自许多不同角度和观点的巨大批评。随着早期过多的伤病和受欢迎的布朗队球员家属在社交媒体上的批评,本赛季对于崭露头角的布朗队来说无疑是一个巨大的调整。然而,在 1 月 3 日对阵匹兹堡钢人队的糟糕表现(单场比赛连续 10 次未完成比赛和 9 次助攻)之后,在 Twitter 上与当地媒体记者接触,并在本赛季剩余时间里因急需的手术而缺席,许多人在 Twitter 上表达了他们对自 1999 年以来围绕布朗队第 31 位四分卫的争议的看法。

使用 R,我们可以执行情感分析来回答问题, “那么,人们对贝克·梅菲尔德的真实感受是什么?”

使用 Twitter API 进行数据拉取

首先,我们需要引入一些 R 库来帮助我们进行文本清理、可视化和情感分析。

library(rtweet)
library(stopwords) 
library(dplyr) 
library(tidyr) 
library(tidytext) 
library(wordcloud)
library(devtools)
library(tidyverse)      
library(stringr)
library(textdata)

有各种各样的选项可用于提取社交媒体数据,如 Netlytic(基于云的文本分析器和社交网络可视化工具,包括数据导出)和 Twitter 开发人员 API。对于我们的用例,我们将使用 Twitter API 来提取包含各种关键字的推文。(注意:此 API 仅允许我们提取前 6–9 天的数据)。

你可以在这里阅读更多关于创建开发者帐户、使用 Twitter API 以及创建供你使用的访问令牌的信息:https://cran . r-project . org/web/packages/rtweet/vignettes/auth . html。完成后,您可以从您的开发者门户访问更多信息。

一旦您有了应用程序密钥、消费者密钥和访问令牌,您就可以使用 create_token()来生成授权令牌,这样您就可以将您的推文拉过来。

create_token(app = app_name, 
 consumer_key = consumer_key, 
 consumer_secret = consumer_secret, 
 access_token = access_token, 
 access_secret = access_secret)

现在,您已经准备好开始提取数据了!使用 rtweets 库中的 search_tweets(),我们可以使用一些关键字。在使用布尔运算符时,我们可以搜索任何包含至少 1 个给定搜索词的推文。默认情况下,这只返回 100 条 tweets,最大限制为 18,000 条。但是,您可以设置一个参数,在“retryonratelimit”设置为 true 的情况下返回更多推文(基于您的帐户访问级别,您可以拉取的推文总数是有限制的,所以我建议只在访问级别较高的情况下使用该参数)。还有其他选项来抓取最受欢迎的推文或混合最近和受欢迎的推文,而默认设置是返回最近的推文。出于一般情绪分析和时间的目的,我选择不包括转发。

有关查询格式/灵活性/选项的更多信息可在此处获得:https://www . rdocumentation . org/packages/rtweet/versions/0 . 7 . 0/topics/search _ tweets

tweets_data <- search_tweets('Baker Mayfield OR Mayfield OR Cleveland Browns OR Stefanski', include_rts = FALSE, lang = 'en', n = 18000)

观察返回的推文数量

nrow(tweets_data) 

照片由陈伶俐·林恩(右输出)

Twitter API 返回许多关于 Tweets 的详细信息,包括标签长度、url 模式等。出于分析的目的,我们感兴趣的是前 16 列的信息,范围从 user_id 到 reply_count。

tweets_data <- tweets_data[,1:16]
summary(tweets_data)

照片由陈伶俐·林恩(右输出)

现在,我们可以看看一些推文及其内容…

head(tweets_data$text)

照片由陈伶俐·林恩(右输出)

我们还可以观察哪一天关于我们的话题的推文最多。

照片由陈伶俐·林恩(右输出)

我们现在可以看到,绝大多数推文发生在 1 月 4 日星期二,这是布朗队对钢人队比赛(也是梅菲尔德本赛季最后一场比赛)后的一天。在我们继续分析时,记住这一点很重要,因为这可能会影响我们数据中的情绪。

一些初步探索和数据预处理

我们可以使用 tidytext 库中的 unnest_tokens()函数将我们的 tweets 扩展为单个单词,将单词格式化为小写,并删除任何标点符号,然后过滤掉不需要的单词(the、to、and、is 等)。)使用预定义的停用词进行分析。

words_data <- tweets_data %>% select(text)  %>% 
              unnest_tokens(word, text)words_data %>% count(word, sort = TRUE)

照片由陈伶俐·林恩拍摄(R 输出——我们数据中的热门词汇)

在删除停用词之前,使用 anti_join(stop_words),我们可以看到一些最常见的词将出现在停用词中,如 https 和 t.co,因此我们可以过滤掉这些词,过滤掉停用词,然后再次检查。

words_data <- words_data %>% filter(!word %in% c('https', 't.co', 'he\'s', 'i\'m', 'it\'s'))words_data2 <- words_data %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE)head(words_data2, n = 10)

陈伶俐·林恩拍摄的照片(R 输出—清洗后数据中的热门词汇)

现在,我们可以看到我们的单词变得更干净了,我们可以在检查情感之前在单词云中检查我们清理过的数据。

照片由陈伶俐·林恩拍摄

基于 Bing 词典的词级情感分析

words_data2 %>%
      inner_join(get_sentiments("bing")) %>%
      count(sentiment, sort = TRUE)

照片由陈伶俐·林恩拍摄(R 输出用于单词级情感分析)

我们可以看到,大多数单词被认为是负面的。如果我们想知道数据中的哪些词被归类为正面或负面,我们可以使用比较词云(并使用 sentimentr 库排除任何亵渎的词)来获取峰值。

profanity_list <- unique(tolower(lexicon::profanity_alvarez))words_data %>% filter(!word %in% c('https', 't.co', 'he\'s', 'i\'m', 'it\'s', profanity_list)) %>%
    inner_join(get_sentiments("bing")) %>%
    count(word, sentiment, sort = TRUE) %>%
    acast(word ~ sentiment, value.var = "n", fill = 0) %>%
    comparison.cloud(colors = c("red", "blue"),
                     max.words = 50)

照片由陈伶俐·林恩拍摄

这让我们对我们的类别有了更深入的了解。我们可以看到像“更好”、“粉丝”、“赢”和“最好”这样的词是积极的,而像“冒犯”、“伤害”、“糟糕”或“问题”这样的词是消极的。然而,根据上下文,使用 bing 词典,一些单词可能同时适用于负面情绪和正面情绪。积极分类的“进步”一词可能是指四分卫的一系列进步广告,而消极分类的“损失”可能只是指布朗队输给钢人队。如果没有进一步的检查,这些词的分类可能会被误解,因为它可能取决于完整推文或句子的上下文。

使用 Sentimentr 在完整 Tweet 级别进行情感分析

使用 sentimentr 库,我们可以分析完整的推文,并检查平均情绪得分,而不是逐字分类。

library(sentimentr)
tweet_sentences_data <- sentiment(get_sentences(tweets_data$text)) %>% 
  group_by(element_id) %>% 
  summarize(meanSentiment = mean(sentiment))head(tweet_sentences_data)

照片由陈伶俐·林恩拍摄(R 输出每条推文的情感评分)

平均情绪告诉我们情绪是积极的还是消极的。如果是正数,则为正面情绪,反之为负面情绪。如果是 0,那简直就是神经。

我们还可以观察最积极的推文相对于最消极的推文有多积极,我们可以在每组中进行计数。有了这些计数,我们可以使用可视化来可视化我们数据中的情感平衡!(谁不喜欢好的 viz)

print(paste0("Most negative tweets sentiment: ", min(tweet_sentences_data$meanSentiment)))
print(paste0("Most positive tweets sentiment: ", max(tweet_sentences_data$meanSentiment)))print(paste0("# of Negative Tweets: ", sum(tweet_sentences_data$meanSentiment < 0)))
print(paste0("# of Neutral Tweets: ", sum(tweet_sentences_data$meanSentiment == 0)))
print(paste0("# of Positive Tweets: ", sum(tweet_sentences_data$meanSentiment > 0)))

照片由陈伶俐·林恩(右输出)

我们可以看到,我们最负面的推文实际上是非常负面的,接近-1,反之亦然。为了更有力地展示我们的发现,我们可以使用我们的情感计数来创建一个显示每个情感平衡的可视化。

slices <- c(sum(tweet_sentences_data$meanSentiment < 0), sum(tweet_sentences_data$meanSentiment == 0),
            sum(tweet_sentences_data$meanSentiment > 0))
labels <- c("Negative Tweets: ", "Neutral Tweets: ", "Positive Tweets: ")pct <- round(slices/sum(slices)*100)
labels <- paste(labels, pct, "%", sep = "") #customize labeling#add in appropriate colors for positive, neutral, negative
pie(slices, labels = labels, col=c('red', 'yellow', 'green'), 
   main="Tweet Sentiment Percentages")

照片由陈伶俐·林恩(右输出)

在推文层面,我们可以看到我们推文中的观点比文字层面更加平衡。

用户层面的情感分析

这种分析的另一个有趣的扩展是显示每个用户的情绪,因为一些用户可能有多个情绪不同的推文。然而,我们的数据集中有超过 4000 个用户。为了一个更清晰的视觉和更容易的初步探索,我们将我们的数据限制在前 50 个最受欢迎的推文及其各自的用户。

n_distinct(tweets_data$user_id)

照片由陈伶俐·林恩(右输出)

#selecting top 50 tweets by favorites
user_sentiment <- tweets_data %>% select(user_id, text, favorite_count) %>% arrange(desc(favorite_count)) %>% slice(1:50)
head(user_sentiment)

照片由陈伶俐·林恩拍摄(R 输出-热门推文的文本内容)

陈伶俐·林恩的照片(R 输出——热门推文的“收藏夹”数量)

现在,我们已经将数据按收藏数量降序排列,并限制在前 50 条推文中,我们可以轻松地对每个用户的情绪进行分组,并再次使用 sentimentr 库中的 sentiment_by()更好地了解这些用户的情绪。

out <- sentiment_by(get_sentences(user_sentiment$text), 
                    list(user_sentiment$user_id))plot(out)

照片由陈伶俐·林恩拍摄(R 用户级情感输出)

这使我们能够更好地了解每个用户的情绪,因为一些用户在其所有推文中有广泛的情绪评分,而其他用户则完全中立(可能只有一条推文或多条完全中立的推文)。

..现在你知道了!情绪分析是一种非常有用的方法,可以通过社交媒体数据、调查数据、书籍等更好地了解公众的看法或情绪。然而,我们也必须意识到这种方法的局限性。使用来自 Twitter 的社交媒体数据,我们在使用的数据方面受到限制,因为可能是这样的情况,当人们有负面情绪时,他们更有可能发推特,而不是中性或积极的情绪。此外,使用 bing 词典,一些单词可能同时适用于消极情绪和积极情绪,因此探索您的数据以观察类似这些情况并在必要时做笔记是有用的。最重要的是,在探索数据和进行情感分析时,永远记得提出越来越多的问题,我建议在开始学习时使用你感兴趣的数据。

使用社交媒体数据进行体育情感分析的潜在扩展

在这种情况下,体育数据情感分析还有许多其他应用,您可以进一步扩展您的分析。直接从顶级/受欢迎的体育分析师那里收集推文,并根据他们对每个团队的社交媒体输入进行情绪分析,并进一步分析任何偏袒/偏见,这可能是有趣的。在一个给定的赛季进行情绪分析,并衡量情绪如何随着每场比赛或组织的变化而变化,这可能是很有趣的。可能性无穷无尽,感兴趣的话花时间去探索吧!

照片由妮可·沃尔夫Unsplash 拍摄

使用 scitkit-learn 介绍套索和岭回归

原文:https://towardsdatascience.com/an-introduction-lasso-and-ridge-regression-using-scitkit-learn-d3427700679c

UCL 数据科学学会 12a 研讨会:偏差-方差权衡、套索实施、山脊实施及其差异

迈克·考克斯在 Unsplash 上的照片

今年,作为 UCL 数据科学学会的科学负责人,该学会将在整个学年举办一系列 20 场研讨会,涵盖的主题包括数据科学家工具包 Python 和机器学习方法简介。每个人的目标是创建一系列的小博客文章,这些文章将概述主要观点,并为任何希望跟进的人提供完整研讨会的链接。所有这些都可以在我们的 GitHub 资源库中找到,并将在全年更新新的研讨会和挑战。

本系列的第十二次研讨会是对高级回归方法的介绍,包括 Lasso 和 Ridge 回归的理解和实现。虽然亮点将在这篇博文中呈现,但完整的研讨会可以在我们的 GitHub 账户这里找到。

如果您错过了之前的任何研讨会,可以在这里找到:

偏差与方差的权衡

在建模阶段,有必要在您创建的模型中尽可能多地决定偏差和方差的水平。当构建监督机器学习模型时,目标是实现最准确预测的低偏差和方差。这意味着在处理训练数据时,我们必须处理欠拟合和过拟合的问题。

我们可以认为偏差是模型的精确度(即与目标的接近程度),而方差是预测与目标的相关程度。我们的目标是同时具有低偏差(即准确)和低方差(即始终准确),但在训练模型时,这两者之间往往存在权衡。例如,在过度拟合的情况下,我们可能对训练数据非常准确,因此具有较低的偏差,但我们可能对看不见的数据具有较高的方差,因为它已经过度拟合。因此,两者之间存在一定程度的权衡,我们可以这样设想:

作者图片

在建模中,我们试图在新数据集上实现尽可能低的验证误差的中间点,而不会过度接近与过度拟合相关联的低训练误差。就回归而言,可以对此进行控制的两个模型是 Lasso 和 Ridge 回归,如下所示。

套索回归

Lasso regression 是最小绝对收缩和选择运算符的缩写,是一种执行方差选择和正则化的回归方法,能够提高回归模型的预测准确性和可解释性。

早期的线性回归研讨会展示了什么是方差选择,在这种情况下,我们根据变量之间的相关性或它们在回归中缺乏显著性来移除变量。这样做的目的是通过仅使用对目标变量重要的变量来提高模型的有效性,并从模型中去除噪声。因此,这可以提高模型的有效性和效率。

相反,正则化试图通过引入惩罚或约束来解决过度拟合的问题,以避免模型过度拟合训练数据。拉索回归能够做到这一点使用“L1 正则化”。这里,将罚项添加到普通最小二乘回归目标(减少平方和),使得回归目标变为:

作者图片

这里引入的惩罚,由等式的第二部分给出,相当于系数绝对值的。这意味着系数的子集被强制为零,充当自动特征选择。因此,只有最重要特征的子集在模型中具有非零权重,从而更容易解释。

模型中这种正则化的程度由𝛼参数控制,这意味着我们可以控制欠拟合和过拟合之间的权衡,以找到一个舒适的中间地带。这是因为随着𝛼增加,它会降低系数值并减少方差(过拟合),但是在某个点之后,模型开始失去对数据的重要理解,并导致偏差增加(导致欠拟合)。因此,应使用超参数调整或交叉验证技术仔细选择𝛼的值。

现在,我们可以以类似于基本回归的方式实现一个简单的套索回归。为此,我们使用了来自 UCI 机器学习数据库的关于建筑能效的数据集。这在训练数据集上实现如下:

#import the linear model class
from sklearn import linear_model#initiate the model with an alpha = 0.1
lasso_model = linear_model.Lasso(alpha = 0.1)#fit the model to the data
lasso_model.fit(X=X_train, y=y_train)

我们在这里可以看到,实现 lasso 回归模型遵循了与实现线性回归模型几乎相同的语法。主要区别在于,我们必须为实现指定 alpha 值(默认值为 0.1),这会影响应用于系数的惩罚。

在这种情况下,如前所述,我们可以通过从模型实现中提取系数来检查模型中的系数,如下所示:

#extract the coefficients
df_coef_lasso = pd.DataFrame({"var": X_train.columns.values, 
                              "coef":lasso_model.coef_})#show the dataframe
df_coef_lasso#out:
   var   coef
0  X1    -0.000000
1  X2    0.001935
2  X3    0.050211
3  X4    -0.000000
4  X5    4.823518
5  X6    -0.000000 
6  X7    14.491866
7  X8    0.291431

由此我们可以看到,这些系数似乎与原始回归系数略有不同,其中一些系数实际上已经减少到 0。这意味着引入的惩罚意味着 X1、X4 和 X6 在回归方程中不再重要。

考虑到这一点,即模型旨在降低模型的复杂性以避免过度拟合,我们可以检查模型在训练数据集上的得分:

lasso_model.score(X_train, y_train)#out:
0.9068673512124443

这个分数低于原始模型,但是它已经设法用模型中更少的显式变量实现了这个分数。在某些情况下,由于降低了达到相同预测能力水平所需的模型复杂性,这可能导致改进的调整后 R。

像以前一样,我们可以使用模型来预测测试数据集,并查看它在那里的表现:

#extract model predictions
model_predictions = lasso_model.predict(X_test)#R2 score on test data
lasso_model.score(X=X_test, y=y_test)#out:
0.9036176174398927

虽然在这种情况下,与线性回归模型相比,该模型没有提高测试数据的 R,但我们可以开始看到,当我们在未来处理更复杂的数据集时,该模型可能会做得更好。当我们有彼此相关的独立方差或者我们有大量的独立变量要处理时,尤其如此。

另一件重要的事情是,我们最初选择的alpha = 0.1不一定是最佳值。我们可以使用交叉验证或超参数调整来检查这一点,但就我们的目的而言,我们可以简单地看到惩罚如何改变变量的重要性,例如随着 alpha 增加,越来越多的系数减少到 0,如下所示:

#import the necessary modules
from itertools import cycle
from sklearn.linear_model import lasso_path
import numpy as np
import matplotlib.pyplot as plt#run the lasso path
alphas_lasso, coefs_lasso, _ = lasso_path(X_train, y_train.values.reshape(-1),
                                          alphas = [.0001, .001, .01,.1, .5, 1, 10, 100, 1000, 10000], 
                                          fit_intercept=False)#plot the coefficients over the path
log_alphas_lasso = np.log10(alphas_lasso)
for index, coef_l in enumerate(coefs_lasso):
    l1 = plt.plot(log_alphas_lasso, coef_l,
                 label = X_train.columns.values[index])#add labels
plt.xlabel('Log(alpha)')
plt.ylabel('coefficients')
plt.title('Lasso Path')
plt.axis('tight')plt.legend(bbox_to_anchor = (0.7, 0.3, 0.5, 0.5))
#sho the model
plt.show()

作者图片

这显示了模型的系数如何在可能的 alpha 值范围内变化,由此小的 alpha 值意味着几个变量仍然对模型有影响,而当它增加时,较少的变量对模型有影响。

里脊回归

岭回归还能够对数据集执行正则化,以便能够控制模型中的过拟合和欠拟合。这通过使用以下等式来实现:

作者图片

岭回归的强大之处在于,它通过强制降低系数来最小化残差平方和(RSS )(实际上引入了一种惩罚),但它不会强制系数为零(如上面的 lasso 回归)。这种惩罚的效果实际上是系数幅度的平方。这利用了被称为 L2 正规化的另一种正规化形式。

这又留下了𝛼的选择,它可以用几种方法来完成。更传统的统计方法是选择𝛼,这样可以减少一些信息标准,如 AIC 或 BIC。备选方案是对𝛼值执行交叉验证和选择,这将最小化交叉验证的残差平方和(或一些其他度量)。虽然前一种方法强调模型对数据的拟合,但后一种方法更关注其预测性能。

我们可以用和以前几乎一样的语法实现岭回归:

from sklearn.linear_model import Ridgeridge_model = Ridge(alpha = 1.0)
ridge_model.fit(X_train, y_train)

然后,我们可以用与之前类似的方式检查系数,如下所示:

df_coef_ridge = pd.DataFrame({"var": X_train.columns,
                              "coef":ridge_model.coef_[0]})
df_coef_ridge#out:
   var   coef
0  X1    -3.648339
1  X2     0.012609
2  X3     0.035442
3  X4     -0.011416
4  X5     5.307185 
5  X6     -0.008634
6  X7     18.218816
7  X8     0.269722

由此我们可以看出岭回归和原始套索回归之间的主要区别是,尽管一些系数接近于 0,但它们实际上并没有达到 0。这是因为惩罚不会完全减少它们,事实上也永远不会。因此,尽管引入了惩罚,但是在该模型中没有执行完整的特征选择。

然后,我们可以看到模型在测试数据集上的表现:

ridge_model.score(X_train, y_train)#out:
0.9128636836305432

我们可以看到,该模型比 Lasso 回归表现得更好,但仍然比基本线性回归表现得更差(至少在这个度量方面)。

当然,重要的是它能够在看不见的数据上执行得如何,我们可以检查:

ridge_predictions = ridge_model.predict(X_test)print(ridge_model.score(X_test, y_test))#out:
0.9085287497226727

其中,像以前一样,知道模型正在处理看不见的数据,模型的性能下降,尽管这是可以预料的。

然而,我们知道我们只为原始模型选择了一个单一的 alpha 值,而我们可以使用RidgeCV找到最佳的 alpha 值。这是通过执行留一交叉验证来实现的,以确保我们通过一系列值获得模型的最佳 alpha 值。这要求我们输入一系列的值来训练它,我们可以这样实现它:

#import the model
from sklearn.linear_model import RidgeCV#create the model
ridgeCV_model = RidgeCV(alphas = np.logspace(-6, 6, 13))
#implement it on training data
ridgeCV_model.fit(X_train, y_train)

我们可以通过以下方法提取最佳 alpha 值:

optimal_alpha = ridgeCV_model.alpha_
print(optimal_alpha)#out:
0.001

在这种情况下,我们可以重做模型,看看性能如何变化:

#implement the model with the new alpha
ridge_model = Ridge(alpha = 0.001)
#fit the model
ridge_model.fit(X_train, y_train)#check the score on the original data
print(ridge_model.score(X_train, y_train))#check the score with the new data
print(ridge_model.score(X_test, y_test))#out:
0.9173454869664193
0.9128137902284486

我们现在可以看到,使用 R 度量,具有最佳 alpha 的训练岭模型现在实际上优于基本回归模型(至少只是略微)。因此,某种程度的惩罚提高了模型的通用性,以确保模型中过拟合的机会或程度更小。

拉索 v 型山脊

重要的问题是,在什么情况下应该使用套索或岭回归。这将从根本上取决于所讨论的数据,因为:

  1. Lasso 通过将一些系数设置为零来执行有效的变量选择,而在岭回归中并非如此。
  2. 如果有少量独立特征或重要参数(仅当少数预测因素影响响应时),Lasso 的性能会更好。
  3. 如果有大量独立特征或重要参数(当大多数预测器影响反应时),岭很好地工作。
  4. 通常最好通过交叉验证来使用这两种模型,以选择最适合该案例的模型。

这就是脊和套索回归的介绍,以及如何在您的数据上实现它们!

如果您想了解我们协会的更多信息,请随时关注我们的社交网站:

https://www.facebook.com/ucldata

insta gram:【https://www.instagram.com/ucl.datasci/

领英:【https://www.linkedin.com/company/ucldata/

如果你想了解 UCL 数据科学协会和其他优秀作者的最新信息,请使用我下面的推荐代码注册 medium。

https://philip-wilkinson.medium.com/membership

或者看看我写的其他故事:

H2O 波表介绍

原文:https://towardsdatascience.com/an-introduction-of-h2o-wave-table-95a91da3672e

互动。简单。有用。

克里斯·格拉夫顿在 Unsplash 上拍摄的照片

H2O Wave 是一个 Python 包,用于为各种数据科学工作流和行业用例创建实时 ML/AI 应用。

数据科学家以表格形式查看大量数据。运行 SQL 查询、在 Excel 中旋转数据或分割熊猫数据框架是非常基本的任务。

随着低代码工具的使用越来越多,拥有一个表格组件是必不可少的。H2O Wave 的表格组件有一些奇妙的功能,如原生过滤、降价单元格、行触发器、分页甚至实时搜索,这使得 Wave 应用程序对用户来说非常有用,交互&现代。

H2O 波表

让我们看看其中的一些特性及其相应的实现。下表提供了一个简单的 Wave 应用程序结构:

app.py可以在安装 H2O 波后使用wave run app.py运行,并在 http://localhost:10101 上查看

P.S. 作为 WaveTon 系列的一部分,还可提供一个全面应用的桌面展示柜

创造

创建表包括定义数据的列和行。

ui.table用于定义表格元素。

ui.table_column用于定义每一列的数据。
ui.table_row用于定义数据中每行的内容。

**ui.table(
    name='table_name',
    columns=[
        ui.table_column(name='id', label='Id'),
        ui.table_column(name='user', label='User'),
        ui.table_column(name='product', label='Product')
    ],
    rows=[
        ui.table_row(name='0', cells=['0', 'Adam', 'Coffee']),
        ui.table_row(name='1', cells=['1', 'Sarah', 'Balloons'])
    ]
)**

波表(创建)

在一个csv文件中有数据是很常见的,表的创建可以扩展到从一个上传的文件中创建。

来自 WaveTon 系列的示例 CSV 加载器应用展示了这是如何实现的。

大小

可以通过多种方式调整表格的大小,以充分利用空间。

width用于设置表格的总宽度。
height用于设置表格的总高度。
它们可以设置为像素值、百分比或自定义计算。

min_width用于设置一列的最小宽度。
max_width用于设置一列的最大宽度。
列宽也可以直接在表格上交互调整。

ui.table(
    name='table_name',
    columns=[
        ui.table_column(
            name='id',
            label='Id',
            **min_width='20px'**
        ),
        ui.table_column(
            name='user',
            label='User',
            **min_width='100px',
            max_width='200px'
**        ),
        ui.table_column(
            name='product',
            label='Product',
            **min_width='100px'**
        )
    ],
    **width='100%',**
    **height='calc(100vh - 60px)',** ...)

波表(尺寸)

类型

每列的默认列类型为string

data_type用于改变一列的类型。
当使用numeric列正确处理排序和/或使用timestamp列显示简洁的日期/时间格式时,必须进行设置。

ui.table(
    name='table_name',
    columns=[
        ui.table_column(
            name='id',
            label='Id',
            **data_type='number'**
        ),
        ...,
        ui.table_column(
            name='quantity',
            label='Quantity',
            **data_type='number'**
        )
    ],
    ...
)

波表(类型)

分类

通过单击表格标题,可以对表格进行排序。

sortable用于启用一列的排序。

提供resettable选项将表格恢复到原始状态非常有用。

ui.table(
    name='table_name',
    columns=[
        ...,
        ui.table_column(
            name='user',
            label='User',
            **sortable=True**
        ),
        ui.table_column(
            name='product',
            label='Product',
            **sortable=True**
        ),
        ui.table_column(
            name='quantity',
            label='Quantity'
            **sortable=True**
        )
    ],
    **resettable=True,**
    ...
)

波表(排序)

过滤器

可以通过从表格标题中选择值来过滤表格。

filterable用于启用对某一列的过滤。

提供resettable选项将表格恢复到原始状态非常有用。

ui.table(
    name='table_name',
    columns=[
        ...,
        ui.table_column(
            name='user',
            label='User'
            **filterable=True**
        ),
        ui.table_column(
            name='product',
            label='Product'
            **filterable=True**
        ),
        ...
    ],
    **resettable=True,**
    ...
)

波表(过滤器)

表可以被视为特定列的组。

groupable用于启用表格的分组。

提供resettable选项将表格恢复到原始状态非常有用。

ui.table(
    name='table_name',
    columns=[
        ...
    ],
    **groupable=True,**
    **resettable=True,**
    ...
)

波表(组)

搜索

可以基于全部或部分关键字搜索来过滤表格。

searchable用于启用对列的搜索。当在搜索栏中输入文本时,将显示至少在一个可搜索列中包含匹配项的每一行。

ui.table(
    name='table_name',
    columns=[
        ...,
        ui.table_column(
            name='user',
            label='User',
            **searchable=True**
        ),
        ui.table_column(
            name='product',
            label='Product',
            **searchable=True**
        ),
        ...
    ],
    ...
)

波表(搜索)

图标

可以将一列图标添加到表格中。

cell_type用于用ui.icon_table_cell_type定义图标列。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='icon',
            label='Icon',
            cell_type=ui.icon_table_cell_type(),
            min_width='30px'
        ),**
        ...
    ],
    rows=[
        ui.table_row(name='0', cells=[..., **'CoffeeScript'**, ...]),
        ui.table_row(name='1', cells=[..., **'Balloons'**, ...]),
        ui.table_row(name='2', cells=[..., **'TVMonitor'**, ...]),
        ui.table_row(name='3', cells=[..., **'CoffeeScript'**, ...]),
        ui.table_row(name='4', cells=[..., **'Balloons'**, ...])
    ],
    ...
)

波表(图标)

支持的图标列表可在此处获得。

百分率

百分比列可以以循环进度图标的形式添加到表格中。

cell_type用于用ui.progress_table_cell_type定义百分比栏。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='discount',
            label='Discount',
            cell_type=ui.progress_table_cell_type(),
            sortable=True,
            min_width='80px'
        )
**    ],
    rows=[
        ui.table_row(name='0', cells=[..., **'0.09'**]),
        ui.table_row(name='1', cells=[..., **'0.66'**]),
        ui.table_row(name='2', cells=[..., **'0'**]),
        ui.table_row(name='3', cells=[..., **'0'**]),
        ui.table_row(name='4', cells=[..., **'0.15'**])
    ],
    ...
)

波表(百分比)

标签

可以将一列单个或多个标签添加到表中。

cell_type用于定义标签列,用ui.tag_table_cell_type定义标签列表。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='tags',
            label='Tags,
            cell_type=ui.tag_table_cell_type(
                name='',
                tags=[
                    ui.tag(label='Beverage', color='$brown'),
                    ui.tag(label='Home', color='$blue'),
                    ui.tag(label='Retail', color='$green'),
                    ui.tag(label='Sale', color='$red')
                ]
            )
        )
**    ],
    rows=[
        ui.table_row(name='0', cells=[..., **'Beverage,Sale'**]),
        ui.table_row(name='1', cells=[..., **'Home,Sale'**]),
        ui.table_row(name='2', cells=[..., **'Retail'**]),
        ui.table_row(name='3', cells=[..., **'Beverage'**]),
        ui.table_row(name='4', cells=[..., **'Home,Sale'**])
    ],
    ...
)

波表(标签)

降价

列可以被格式化为表格中的标记文本。

cell_type用于通过ui.markdown_table_cell_type定义降价列。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='description',
            label='Description',
            cell_type=ui.markdown_table_cell_type(),
            searchable=True
        ),**
        ...
    ],
    rows=[
        ui.table_row(name='0', cells=[..., **'**Product**: *Coffee*\n**Category**: *Beverages*'**, ...]),
        ui.table_row(name='1', cells=[..., **'**Product**: *Balloons*\n**Category**: *Home*'**, ...]),
        ui.table_row(name='2', cells=[..., **'**Product**: *Television*\n**Category**: *Retail*'**, ...]),
        ui.table_row(name='3', cells=[..., **'**Product**: *Coffee*\n**Category**: *Beverages*'**, ...]),
        ui.table_row(name='4', cells=[..., **'**Product**: *Balloons*\n**Category**: *Home*'**, ...])
    ],
    ...
)

波表(降价)

图像

减价格式可以扩展到表格中的图像列。

cell_type用于用ui.markdown_table_cell_type定义图像列。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='picture',
            label='Picture',
            cell_type=ui.markdown_table_cell_type(),
            searchable=True
        ),**
        ...
    ],
    rows=[
        ui.table_row(name='0', cells=[..., **'<center><img src="https://images.unsplash.com/photo-1587049016823-69ef9d68bd44" width="70%">'**, ...]),
        ui.table_row(name='1', cells=[..., **'<center><img src="https://images.unsplash.com/photo-1574276254982-d209f79d673a" width="70%">'**, ...]),
        ui.table_row(name='2', cells=[..., **'<center><img src="https://images.unsplash.com/photo-1552975084-6e027cd345c2" width="70%">'**, ...]),
        ui.table_row(name='3', cells=[..., **'<center><img src="https://images.unsplash.com/photo-1587049016823-69ef9d68bd44" width="70%">'**, ...]),
        ui.table_row(name='4', cells=[..., **'<center><img src="https://images.unsplash.com/photo-1574276254982-d209f79d673a" width="70%">'**, ...])
    ],
    ...
)

波表(图像)

声音的

减价格式可以被扩展到表格中的音频列,在该音频列中可以播放嵌入的音频文件。

cell_type用于用ui.markdown_table_cell_type定义图像列。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='audio',
            label='Audio',
            cell_type=ui.markdown_table_cell_type(),
            searchable=True
        ),**
        ...
    ],
    rows=[
        ui.table_row(name='0', cells=[..., **'<center><audio controls><source src="https://media.merriam-webster.com/audio/prons/en/us/mp3/c/coffee01.mp3" type="audio/wav">'**, ...]),
        ui.table_row(name='1', cells=[..., **'<center><audio controls><source src="https://media.merriam-webster.com/audio/prons/en/us/mp3/b/balloo01.mp3" type="audio/wav">'**, ...]),
        ui.table_row(name='2', cells=[..., **'<center><audio controls><source src="https://media.merriam-webster.com/audio/prons/en/us/mp3/t/televi03.mp3" type="audio/wav">'**, ...]),
        ui.table_row(name='3', cells=[..., **'<center><audio controls><source src="https://media.merriam-webster.com/audio/prons/en/us/mp3/c/coffee01.mp3" type="audio/wav">'**, ...]),
        ui.table_row(name='4', cells=[..., **'<center><audio controls><source src="https://media.merriam-webster.com/audio/prons/en/us/mp3/b/balloo01.mp3" type="audio/wav">'**, ...])
    ],
    ...
)

波表(音频)

菜单

可以在表格的一行上执行多个自定义操作。

ui.menu用于提供动作列表。

ui.table(
    name='table_name',
    columns=[
        ...,
        **ui.table_column(
            name='menu',
            label='Menu',
            cell_type=ui.menu_table_cell_type(
                commands=[
                    ui.command(name='view_transaction', label='View Transaction', icon='Shop'),
                    ui.command(name='view_image', label='View Image', icon='ImageSearch')**
                **]
            )
        )**
    ],
    ...
)

波表(菜单)

引发

可以将行触发器设置为对表中的一行执行单个操作。

link用于定义触发列。对于要单击的触发器,只能启用一列(通常是 id 或 key 列)。双击一行中的任意位置也会触发对该行的相同操作。

ui.table(
    name='table_name',
    columns=[
        ui.table_column(
            name='id',
            label='Id',
            **link=True**
        ),
        ...
    ],
    ...
)

波表(触发器)

多选

可以对表中的多行同时执行操作。

multiple用于表中行的多选。

ui.table(
    name='table_name',
    columns=[
        ...
    ],
    **multiple=True,**
    ...
)

波表(多选)

标页数

通过对表进行分页,可以有效地处理大型数据集。

pagination用于定义要分页的行数。rows需要根据page_change事件进行过滤。

ui.table(
    name='table_name',
    columns=[
        ...
    ],
    **rows=[
        ...
    ],**
    **pagination=ui.table_pagination(total_rows=5, rows_per_page=2),
    events=['page_change']**
    ...
)

波表(分页)

[计] 下载

可以下载csv格式的表格。

downloadable用于下载表格。

ui.table(
    name='table_name',
    columns=[
        ...
    ],
    **downloadable=True,**
    ...
)

波表(下载)

你可以做很多与 H2O 波的表组件!它使数据科学家和分析师能够构建更具生产力和效率的东西。
这些代码片段有助于开始为不同的数据集和用例扩展它。

桌面陈列柜

作为 WaveTon 系列的一部分,一个成熟的综合应用表格展示也是可用的,该系列还提供了处理交互的附带代码,建议作为定制表格的起点。

桌面陈列柜

资源和参考

贝叶斯推理导论

原文:https://towardsdatascience.com/an-introduction-to-bayesian-inference-2cee9416504c

贝叶斯推理方法的数学+代码介绍-马尔可夫链蒙特卡罗和变分推理。

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

在之前的博客文章中,我介绍了贝叶斯统计的世界。它检查了贝叶斯统计如何提出一种方法,将证据纳入我们的模型,以获得更好的模型。这个过程被称为推理,这是获得/逼近后验概率的一种奇特的数学说法。

然而,贝叶斯统计的一个大问题是,除了最简单的模型,通常不可能找到分析或精确的解决方案。因此,更常用的近似推断方法侧重于寻找后验估计值。在这篇博文中,我将介绍两种方法:变分推理马尔可夫链蒙特卡罗

问题

让我们首先用贝叶斯推理来重新审视这个问题。

贝叶斯公式告诉我们如何将似然性 P(X|θ)与先验 P(θ)相结合以获得后验。P(X)观察到 X 的概率被称为证据,可以通过对所有可能的θ值取一个期望值来计算。这将导致必须计算多个变量的积分或总和,这是非常困难的。

这两种方法不是计算 P(X ),而是变分推理和 MCMC 使用来自分子的信息来直接估计后验概率,完全绕过这个积分。

让我们看看这两种方法在一个玩具示例的上下文中是如何工作的:对 2-混合高斯分布建模。

目标分布-按作者分类的图像

变分推理

变分推理(VI)是一种逼近特定概率分布的技术。VI 通过从尽可能接近目标分布 p 的分布族 Q 中找到概率分布 q 来实现这一点。

KL 散度公式

这是通过使用 Kullber-Lieback 散度(KL-Divergence)作为我们的损失函数来实现的。KL 散度是一个量化两个分布之间差异的函数。由于函数是可微的,我们可以使用基于梯度的方法来优化函数。执行这种优化将返回后验分布的局部最优值。

尽管 KL 散度衡量两个分布之间的差异,但它不是距离度量。KL(q||p) ≠ KL(p||q)除非 p=q 其中 KL 散度= 0

然而,我们仍然面临一个大问题,首先获得后验 p(x)的函数!假设我们可以找到一个函数 p^ ,它与 p 非常相似,具有相同的最小值。如果是这样的话,我们可以使用梯度下降来优化 KL(q|p^),并且我们的近似 q 应该是对 p 的最佳可能估计。

这个函数 p^ 就是原始贝叶斯公式中的 p(x |θ)\乘以 P(θ)。这本质上是后验的,没有难以计算的归一化常数/证据。两个函数 p(x)和 p^(x 通过下面的等式相关。

让我们看看当我们取 KL-散度(q||p)时会发生什么

∑ q(x)和 Z(θ)是与 x 无关的常数

KL( q || p )和 KL( q || p^ )之差是一个常数 log Z(θ) ,两个函数将具有相同的最小值。

这允许通过调整 q 的参数使用梯度下降的优化程序,以最小化 KL 损失。

既然我们知道 KL-散度 KL(q|P)总是≥0,我们就可以推导出 log Z(θ) 或证据 P(X)的附加性质。

如上所述,KL(q|p^)将总是大于 log Z(θ) ,这也是来自等式 2 的 log P(X) 。具体地,这种损失可以被解释为证据 P(X)的下限。正因为如此,之前我们描述为 KL(q|p^)的函数也被称为证据下界(ELBO)。我们的函数代表对数似然 对数 P(X) 的下界。最大化这将意味着增加在所有可能的θ值上看到数据的可能性。

代码实现

Pyro 和 Tensorflow Probability 是两个可以相对快速地执行变分推理的包,但是对于本教程,我们将使用 JAX 来执行一个实现。JAX 本质上是具有自动微分的 numpy,它将帮助我们更好地理解在我们简单的玩具例子上执行 VI 时在引擎盖下发生了什么。

本质上,JAX 允许对给定函数的梯度进行数值计算。为了使用它,我们只需要定义返回 KL 散度损失的函数。我们的函数有 3 个参数,数据(X)以及我们试图拟合的高斯分布的均值和标准差参数。

获得 KL 散度的函数

一旦写好函数,我们就可以使用jax.grad自动获得梯度。argnums参数告诉 Jax 我们想要哪个参数的梯度。由此,我们创建了两个函数mu_grad_klsigma_grad_kl,它们将给出各自的梯度更新。

变分推理梯度下降

之后,我们可以使用简单的梯度下降算法来获得拟合分布的参数。注意,由于 KL 散度函数不是凸的,所以所获得的参数只能保证是局部最优,而不是全局最优。

双高斯目标的单高斯拟合—作者提供的图像

正如所料,估计并不完美,仅仅是因为我们的变分模型(单高斯分布)对于目标(混合分布)来说不是一个好的模型。使用混合分布模型作为拟合,我们将能够更好地估计目标。在更复杂的情况下,找到一个好的变分模型是非常困难的。

MCMC

一种替代方法是从分布中取样,而不是用变分推断来近似分布。如果我们可以很容易地从后验分布中抽取无偏样本,我们就可以获得均值和方差,并使用这些信息进行估计。

为什么直接取样不起作用?

不幸的是,对复杂且未知的分布进行采样并不总是那么容易。因为我们有一个非标准化的概率分布,普通分布(指数,高斯)的抽样算法不起作用。因此,我们求助于使用其他方法。

马尔可夫链蒙特卡罗(MCMC)描述了一组特定的采样方法,用于在没有精确概率密度函数的情况下逼近分布。在深入研究细节之前,让我们首先了解 MCMC 算法的两个组成部分。

算法的蒙特卡罗部分简单地指随机抽样。蒙特卡罗方法是一类基于重复随机抽样的方法。假设我们有一枚硬币,想估计它正面朝上的概率。蒙特卡罗方法是抛硬币很多次,并使用观察结果来预测参数。

马尔可夫链是一种控制一系列事件之间行为的数学模型。链中的每个节点代表一个状态,并通过链接连接在一起,链接是状态之间的转移概率。马尔可夫链的独特之处在于,链中的下一个状态只取决于前一个状态。

来自马尔可夫链的样本—作者图片

在我们的例子中,马尔可夫链中的每个节点都是一个样本。每个样本仅依赖于它之前的样本,并且转移概率告诉我们如何从链中导出样本,并且重复进行(蒙特卡罗)以逼近目标分布。

给定正确的转移概率,可以证明当样本数量接近无穷大时,样本的分布将是我们从中采样的目标分布。

捷径解释:所有的 MCMC 算法都建立了一个平稳分布为后验的马尔可夫链。给定足够的时间,马尔可夫链将收敛到这个平稳分布,并产生无偏样本。

因为它只有在一段时间后才能达到正确的估计值,所以在使用 MCMC 时,我们必须执行额外的步骤。通常,我们看样本的痕迹,观察它们如何随时间变化。理想情况下,样本应该停止波动,并开始向某个值/范围收敛。

随后,我们在开始时丢弃一部分样本。第一组样本是“预烧”期,在此期间,算法尚未向稳定分布收敛。

大都会黑斯廷斯算法

现在,我们对 MCMC 如何工作有了一些简单的直觉,让我们看看如何才能得到“正确的跃迁概率”。一种常见的 MCMC 算法 Metropolis Hastings 算法被用作例子。

为此我们要做两件事。非标准化概率密度(P)和转移函数(Q)。p 返回一个描述状态概率的值,而 Q 描述到达一个新状态 x '的概率,假设我们处于前一个状态 x。

Metropolis-Hastings 算法的接受概率

这个转移函数可以是简单对称的,就像正态分布一样。 Q(x'|x) ~ N(x,3) 。本质上,在每个实例中,我们从一个法线(0,3)采样,我们得到的值被加到 x 以获得下一个点。

由于正态分布是对称的,转移函数项 Q(x|x')Q(x'|x) 将在我们的接受概率中抵消。

这样我们就可以为 MH 算法构造伪代码了

算法:

1\. Start from random sample poiint
2\. Sample from Transtion function to get next point
3\. Evaluate the function at th new point
4\. Use the criteria to accept/reject the sample
   a. If accept: Add new sample
   b. Else: Add previous sample
5\. Reject earlier samples for the burn-in period.

接受概率有助于算法调整获得的样本。仍将获得来自较低概率密度的样本,但是与高概率样本相比,速率较低。

Metropolis Hastings 算法的代码实现

一旦收集了所有的样本,可以丢弃其中的一半作为老化期,以获得下面的样本

比较两种方法

从所示的两个例子来看,MCMC 似乎是更好的推断方法

  • 不需要任何关于后验的事先假设
  • 可以收敛到全局最优
  • 算法很简单

然而,MCMC 背后的一个大缺点是,对于大量的维度,它的性能比 VI 差。虽然可以保证收敛到最佳分布,但这可能需要非常长的时间。当使用 MCMC 训练大型贝叶斯网络时,这可能是一个巨大的问题,其中变量的数量可以是几百/几千。

另一方面,变分推理使用梯度下降进行训练,并可以利用现代深度学习框架来使用 GPU 并行化计算。此外,寻找全局最优可以被认为是不必要的,因为大多数最先进的神经网络在没有达到全局最优的情况下表现得非常好。

如果你喜欢这篇文章,请在 Medium 上关注我!
在 LinkedIn 上连接:https://www.linkedin.com/in/reo-neo/

数据泄漏介绍

原文:https://towardsdatascience.com/an-introduction-to-data-leakage-f1c58f7c1d64

对数据的粗心处理会破坏你的机器学习模型

来自佩克斯路易斯·金特罗的照片

在执行机器学习任务时,维护数据卫生至关重要。

人们对这个话题给予了很多关注,重点放在处理过时、不完整或不正确的数据的重要性上。毕竟,忽视数据的整洁会毁掉你建立可靠模型的机会。

然而,尽管对这一主题进行了大量的报道,却缺乏对数据卫生的一个巨大威胁的认识:数据泄漏。

在这里,我们探讨什么是数据泄漏,它是如何发生的,以及如何防止它。

数据泄露

首先,让我们快速回顾一下训练集和测试集之间的关系。

训练集是用于训练机器学习模型的数据子集,而测试集是用于测试模型的数据子集。简单吧?

对这种关系强调不够的是,训练数据需要完全独立于测试数据。测试集中的值应该与训练集中的值无关。

数据泄露是使用训练数据以外的信息训练模型时出现的现象。它本质上违反了训练数据的独立性,并允许它被来自外部来源的信息所改变。

反过来,这可以通过提供误导性的评估指标来掩盖模型性能中的缺陷。如果您在这种情况下未能识别数据泄漏,您可能会误以为您的模型是健壮的,但在部署后发现它完全不可靠。

因此,确保在处理数据时不会无意中导致任何数据泄漏是非常重要的。

数据泄露的例子

就像厨师小心处理肉类和农产品以避免交叉污染一样,数据科学家必须正确处理他们的培训和测试集以避免数据泄露。

好消息是,只要你小心谨慎,数据泄露是很容易避免的。消除它不需要编写额外的代码行。

坏消息是,您可能会犯一些看似无关紧要的错误,从而使您的项目暴露在数据泄露的风险之下。您的模型不会通知您此类错误,因此您需要保持警惕以避免它们。

让我们来介绍几个可能导致数据泄露的错误。

1.不删除重复项

这是从一开始就危及你的项目的一个相当简单的方法。

如果包含重复项,您将冒在定型集和测试集中出现相同记录的风险,从而消除两个数据子集之间的独立关系。

一个简单的解决方法是在将数据分成训练集和测试集之前删除重复的记录。

2.分割数据前的要素缩放

特征缩放是预处理中的一个重要步骤,因为它可以确保模型不会偏向于某个特定的特征。

不幸的是,在将数据分成训练集和测试集之前,有时会错误地应用诸如标准化和规范化之类的特征缩放技术。这是一个错误,因为它允许测试数据的值影响训练数据的缩放方式。

特征缩放需要在数据被分割后的发生。在适当的特征缩放中,需要仅基于训练集中的值来缩放训练数据。之后,需要基于用于缩放训练数据的参数来缩放测试集。

3.拆分数据前的数据扩充

数据扩充是解决数据不平衡和增强模型性能的一种好方法。

但是,它的有效性取决于您在训练模型时如何应用它。

数据增强不只是凭空创造数据;它依靠真实数据和某些算法来生成人工记录。

如果你对算法如何被用来创建合成数据感到好奇,请查看我的文章,其中我给出了 SMOTE 的概要,这是一种更流行的数据增强方法。

自然,创建的人工数据的类型取决于输入这种算法的数据。在这个步骤中包括来自测试集的数据将不可避免地影响添加到训练集中的数据。

因此,数据扩充只能在拆分数据后进行。为了正确执行数据扩充,您为训练模型而生成的人工数据必须仅来自训练数据。

个案研究

为了巩固到目前为止所学的一切,让我们通过一个快速的案例研究来应用我们所学的一切。我们将使用一个提供保险公司客户信息的数据集(无版权)。数据可以在这里获得。

这是数据集的预览。

代码输出(由作者创建)

目标变量是“响应”,它代表一个客户是否有兴趣申请保险。

首先,我们可以删除所有缺失值的记录,并用 one-hot-encoding 对分类变量进行编码。

接下来,我们可以用 drop_duplicates()方法删除任何重复项,这样所有的客户机都是惟一的。请注意,这一步是在将数据分成训练集和测试集之前进行的。

既然已经处理了重复数据,我们可以将数据分成训练集和测试集。

接下来,我们可以使用 sklearn 的 MinMaxScaler 通过归一化来缩放特征。为了避免数据泄漏,最小最大缩放器应该首先转换训练数据,然后基于用于缩放训练数据的参数来转换测试数据。

最后,我们可以通过应用 SMOTE 来处理任何数据不平衡。在此之前,让我们看看当前的训练集和测试集有多不平衡。

代码输出(由作者创建)

在训练集和测试集中有相当大的不平衡。

这可以通过使用 SMOTE 为训练数据生成人工记录来解决。

让我们看看现在的训练集和测试集有多不平衡。

代码输出(由作者创建)

由于该步骤是在训练测试分割后执行的,SMOTE 仅使用训练数据创建人工数据。测试集在整个过程中未被触及,并且保持不平衡。

自始至终,我们对数据集应用了许多技术。然而,由于对训练集和测试集的小心处理,我们能够避免任何数据泄漏。

结论

照片由普拉蒂克·卡蒂亚尔Unsplash 拍摄

最后,避免数据泄露是非常简单的。

如果您注意到了,所有提供的示例都演示了当一个步骤在错误的时间执行时,而不是一起执行时,数据泄漏是如何发生的。

与其随意执行预处理步骤而不注意顺序,不如事先规划好管道。这将使您能够检查每个步骤,并确保在整个过程中训练数据独立于测试数据。

我祝你在数据科学的努力中好运!

参考

  1. 莫比乌斯。(2022).从不平衡的保险数据中学习,第 4 版。2022 年 2 月 13 日从https://www.kaggle.com/arashnic/imbalanced-data-practice.检索

面向数据科学家的数据库介绍

原文:https://towardsdatascience.com/an-introduction-to-databases-for-data-scientists-d6eb686b0dc

您需要了解的关于数据库的一切都在一篇文章中

照片由叶小开·克里斯托弗·古特瓦尔德Unsplash 上拍摄

数据库是通常存储在计算机系统中的有组织、有结构的信息集合(来源:甲骨文)。数据库的操作和管理通常发生在数据库管理系统(DBMS)中。

什么是数据库?

在数据库中,大量数据通常以结构化的方式存储,并可供检索。这几乎总是一个电子系统。然而,从理论上讲,模拟信息收集,如图书馆,也是数据库。

早在 20 世纪 60 年代,就出现了对集中式数据存储的需求,因为像数据访问授权或数据验证这样的事情不应该在应用程序中完成,而应该与应用程序分开。

什么是数据库管理系统?

数据库由两个主要部分组成。一个是实际的数据存储,一个是所谓的数据库管理系统(简称 DBMS)。简单地说,它充当数据和最终用户之间的接口。MySQL 是 Oracle 数据库管理系统的一个具体例子。

数据库管理系统的中心任务包括,例如:

  • 数据的存储、修改和删除
  • 数据模型的定义和符合性
  • 添加用户并创建相应的权限

该管理系统进一步确保所谓的 ACID 属性在数据存储中得到维护。其中包括以下几点:

  • 原子性(A) :数据事务,例如新数据记录的输入或旧数据记录的删除,要么完全执行,要么根本不执行。对于其他用户,事务只有在完全执行后才可见。例如,在金融机构的数据库中,从一个帐户到另一个帐户的转帐只有在两个表中的交易都完全执行时才可见。
  • Consistency (C) :当每个数据事务将数据存储从一致状态移动到一致状态时,满足该属性。
  • 隔离(I) :当多个事务同时发生时,最终状态必须与事务分别发生时相同。也就是说,数据库应该通过压力测试。换句话说,它不应该由于过载而导致不正确的数据库事务。
  • 持久性(D) :数据只能因交易而改变,不能因外部影响而改变。例如,软件更新不得无意中导致数据更改或可能被删除。

数据库有哪些类型?

有许多不同类型的数据收集,这也主要取决于组织或公司内的使用类型。各种影响因素都在发挥作用,例如潜在用户和数据查询的数量,以及要存储的数据类型:

  • 关系数据库:这是存储数据的地方,这些数据可以以表格的形式存储,即以行和列的形式存储。
  • 分布式数据库:如果数据要存储在几台不同的计算机上,这就叫做分布式数据库。这很有用,例如,如果您想使数据收集无故障,或者如果您需要处理大量的数据查询。
  • 数据仓库 :如果数据要在公司内部集中访问,这就称为数据仓库。在这里,来自不同源系统的数据被存储并形成统一的数据形式。
  • NoSQL 数据库 :如果要存储的数据不对应于关系模式,例如在非结构化数据的情况下,它被存储在所谓的 NoSQL(“不仅是 SQL”)数据集合中。

这些只是一些最常见的数据库类型。随着时间的推移,出现了更多的类型,但是我们不能在本文中详细讨论它们。最常见的数据库类型是关系数据库和 NoSQL 数据库。

SQL 和 NoSQL 的区别是什么?

关系数据库存储数据,这些数据组织在具有列和行的表中。通常,它用于组织中的许多应用程序,如存储销售数据、客户信息或仓库中的当前库存。这些数据库可以通过 SQL 语言查询,并且它们满足所介绍的 ACID 属性。然而,该数据库只能在一台设备上实现,这意味着如果需要更多的存储,则必须改进该计算机的硬件,这通常更昂贵。

NoSQL 的原理(“不仅是 SQL”)最早出现在 2000 年代末,泛指所有不在关系表中存储数据、查询语言不是 SQL数据库。除了 MongoDB 之外,NoSQL 数据库最著名的例子还有 Apache CassandraRedisNeo4j

由于其结构的原因,NoSQL 数据库的可伸缩性远远高于传统的 SQL 解决方案,因为它们还可以分布在不同的系统和计算机上。此外,大多数解决方案都是开源的,支持关系系统无法覆盖的数据库查询。

有关 NoSQL 数据库的更多信息,请查看我们关于该主题的文章:

数据库挑战

如果在组织中引入大型数据仓库,管理员将面临各种各样的挑战。创建数据集合时,应考虑以下几点:

  • 增加数据量的能力:由于公司内部生成和存储的数据量不断增加,系统必须有足够的资源来扩展数据量。
  • 数据安全:当部分机密信息存储在中央位置时,自然会成为未经授权访问的目标。这不仅包括防止外部访问,还包括为组织内的用户分配权限。
  • 可扩展性:随着公司的发展,信息量自然也会增长。数据库解决方案应该为此做好准备,并且能够处理更多的用户查询和数据。
  • 数据时效性:当今世界,我们习惯了无延迟地接收信息,同样的道理自然也适用于数据存储。因此,必须构建能够尽快处理和提供信息的体系结构。

谁使用结构化查询语言?

结构化查询语言(SQL) 是处理关系数据库时最常用的语言。不管它的名字是什么,这种语言不仅仅可以用于简单的查询。它还可以用于执行创建和维护数据集合所需的所有操作。

SQL 提供了许多读取、修改或删除数据的函数。它实际上用在所有常见的关系数据库系统中,并且应用广泛。此外,非关系系统还提供了扩展,因此即使数据没有排列在表中,也可以使用查询语言。这可能是因为 SQL 提供了许多优势:

  • 它在语义上非常容易阅读和理解。即使是初学者也能在很大程度上理解这些命令。
  • 这种语言可以直接在数据库环境中使用。对于信息的基本工作,数据不必首先从集合转移到另一个工具。
  • 简单的计算和查询可以直接在数据集合中进行。
  • 与其他电子表格工具(如 Excel)相比,使用结构化查询语言的数据分析可以很容易地复制和拷贝,因为每个人都可以访问集合中的相同数据。因此,相同的查询总是导致相同的结果。

在我们的博客中,我们提供了一篇关于结构化查询语言的详细文章:

https://databasecamp.de/en/data/sql-definition

为什么数据库对数据科学家如此重要?

如果您读到这里,您可能会想,既然有像数据工程师这样的同事在做数据库方面的工作,为什么数据科学家应该了解数据库呢?然而,这只是部分正确。在大多数公司中,不可能填补两个单一的职位,即数据科学家和数据工程师。因此,即使作为一名数据科学家,您也应该具备数据库的基础知识。

然而,另一点更重要:几乎所有用作评估来源的数据都来自数据库。因此,数据库决定了数据科学家如何获得他需要的信息。例如,查询语言、数据结构,以及数据是否已经准备好以及如何准备好。所有这些信息对数据科学家来说或多或少都很耗时,因此对他来说至关重要。

这是你应该带走的东西

  • 数据库是一个系统,用于以有组织和结构化的方式收集信息。
  • 关系存储系统仍然是最常见的。然而,NoSQL 解决方案或数据仓库也越来越受欢迎。
  • 在创建这样的数据集合时,需要考虑许多不同的挑战,比如可伸缩性或数据安全性。
  • 对于查询和维护数据库,结构化查询语言(SQL)在许多情况下仍然被使用。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

* *

弹性研究导论

原文:https://towardsdatascience.com/an-introduction-to-elasticsearch-19f081380d14

从弹性搜索开始你需要知道的一切

UnsplashMarten Newhall 拍摄的照片

Elasticsearch 是一个基于 Apache Lucene 的分布式搜索引擎。它是一个流行的搜索引擎,用于全文搜索或日志分析,因此被许多大公司使用,如网飞、Slack 和优步。

Elasticsearch 是如何工作的?

这个搜索引擎基于这样一个事实,即原始数据和要搜索的文档被组合在一个索引中。为此,它们在索引步骤中被规范化和解析,以便最终的搜索可以运行得更快。这种预处理的索引可以比原始文档和数据更快地被搜索。

假设我们有一个网上商店,里面有各种各样的家具,我们想让我们的顾客通过搜索就能找到它们。对于每件家具,都有在搜索中应该考虑的信息。其中包括产品属性(如尺寸、颜色或特殊功能)和产品描述。为了确保可以快速搜索这些文本属性,我们使用全文搜索 Elasticsearch。

为此,我们必须将数据和文档存储在其索引中。这可以被认为是一个存储所有信息的数据库。在索引中,有几个所谓的类型,可与数据库中的表相比较。

在我们的示例中,只有产品属性可以存储在一种类型中,而产品描述存储在另一种类型中。在这些类型中,最后还有保存单个数据记录的文档。尽管索引不是一个严格的关系数据库,但仍必须保持一定的结构,以便快速搜索。

在我们的例子中,每件家具都在文档中准备好,并存储在一个结构中,以便在最后的搜索中更容易找到它们。为此,所谓的属性是在文档中定义的,它最接近表中的列。

你用 Elasticsearch 做什么?

任何需要搜索功能的地方都可以使用 Elasticsearch。此外,它因其高可伸缩性和快速搜索过程而脱颖而出。可以考虑的应用有:

  • 在网站上搜索
  • 应用程序中的搜索引擎
  • 企业数据搜索引擎
  • 搜索日志文件
  • 在地理数据中搜索
  • 在安全和监控文件中搜索

Elasticsearch 有哪些组成部分?

由于它的广泛使用和许多好处,围绕弹性搜索已经形成了一整套工具,不仅仅是搜索。

Logstash 的帮助下,可以收集和准备数据,以便更好地适用于后续索引。开源程序可以被理解为 Elastic 的 ETL 工具,它通过将不同来源的数据汇集在一起,进行转换,并将其带到最终的存储位置来提供类似的功能。

弹性搜索的下游工具是 Kibana。它提供了可视化和分析来自搜索索引的信息的可能性。因此,这种所谓的 ELK (Elastic,Logstash,Kibana)堆栈提供了覆盖从获取数据到分析指数的完整范围的可能性。

Elasticsearch 有什么好处?

Elasticsearch 是当今非常流行的搜索引擎,因为它有很多优点。其中一些是:

  • 速度:由于索引的原因,它比同类算法要快得多,尤其是在全文搜索方面。此外,预备索引也不需要很长时间,这意味着从包含在索引中到在搜索中可找到的整个过程非常快。这对于搜索速度是重要标准的应用来说是非常有利的。
  • 分布式架构:索引分布在不同的物理机器上,称为碎片。还会创建单个文档的副本,以弥补单台机器的故障。这种集群结构允许扩展搜索的性能。
  • 其他功能 : Elasticsearch 还提供许多其他功能,有助于确保搜索性能非常高。例如,这些包括数据汇总或索引生命周期管理。
  • 业务分析:已经描述的组件提供了可视化和处理索引或已处理数据的可能性。这提供了一个整体的方法。

Elasticsearch 有什么缺点?

尽管 Elastic 的搜索算法有巨大的优势,但在实施之前,也有一些问题需要考虑和权衡:

  • 搜索并非与所有商店系统和基础设施兼容。
  • 使用自托管服务器,实现 Elasticsearch 会变得非常昂贵和复杂。
  • 索引在整个集群中的分布有利于扩展,但也可能很快成为缺点。如果使用了太多所谓的主碎片,并且索引分布在许多机器上,就会出现这种情况。因此,当索引一个新文档时,所有这些机器都必须处于活动状态,这导致仅索引一项就给系统带来很高的负载。

这是你应该带走的东西

  • Elasticsearch 是一个针对各种应用程序的流行全文搜索。
  • 基本原则是索引数据,从而使搜索算法更容易、更快地找到数据。
  • 这种搜索算法的特点是处理速度快,并且索引可以划分到一个计算机集群中,因此是可伸缩的。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,请不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

* *

超越概率预测导论

原文:https://towardsdatascience.com/an-introduction-to-exceedance-probability-forecasting-4c96c0e7772c

用 Python 预测时间序列中极值出现的概率

让-丹尼尔·卡拉姆在 Unsplash 上拍摄的照片

超越概率预测是估计一个时间序列在预定义的未来时期内超过预定义阈值的概率的问题。

在前一篇文章中,我简要描述了时间序列数据出现的 6 个问题,包括超越概率预测。在这里,我将更深入地研究这项任务。在一些基本的定义之后,我将解释为什么这个问题很重要,以及如何使用 Python 在你自己的时间序列中应用它。

图 1:海浪高度时间序列。预测它是否超过预定义的阈值(红色虚线)对于确保海上作业的安全非常重要。图片作者。

超越概率预测与许多领域相关,例如经济学。央行依赖于对通胀率是否会超过某个阈值的估计。如果这种可能性很高,那么央行可能会通过提高利率来应对。

本质上,这个问题在极端值(即分布的尾部)高度相关的情况下很重要。另一个例子如图 1 所示,与海洋数据分析相关。短期预测海浪高度对于估算发电量非常重要。但是大浪会对海上作业的安全造成威胁(例如船只的安全通行),因此预测大浪非常重要。

概率输出是可取的,因为它携带更多的信息来支持决策,特别是当风险很高时。虽然很可能不会下雨,但预报“有 10%的可能性会下雨”比简单地说“不会下雨”更能提供信息。厌恶风险的人可能热衷于拿一把雨伞。

把手放在某物或者某人身上

有两种主要方法可用于解决超越概率问题:二元分类或预测集合。让我们对这两个选项进行概述和编码。

二元分类

超越与二元事件有关。因此,模拟超越概率最自然的方法是将问题框架化为二元分类问题。解释变量代表最近过去的观察,以及你可能有的任何额外变量。目标变量是二进制的,指示事件是否发生,即是否超过阈值。然后,我们可以使用概率分类器(如逻辑回归)来估计超标概率。

这里有一个 Python 模型可以做到这一点。我将使用一个与波高相关的时间序列,你可以在这里得到。您可以在图 1 中看到部分时间序列。为了简洁起见,我将跳过基本的时间序列预处理。

脚本 1:为超标概率预测创建二元分类模型的代码模型

我们从读取数据和转换 pd 的时间序列开始。Series type)转换成一组我们可以输入学习算法的观察值(第 8-21 行)。然后,我们将数据分为训练和测试(第 24 行),并将阈值设置为第 95 个百分点(第 27 行)。我们任意设置阈值,但是这个值强烈依赖于数据集。使用阈值创建二元响应变量(第 30-31 行),然后训练分类器(第 34-35 行)。最后,我们在第 38 行中检索检验观测值的超越概率。

预测集合

照片由 kazuendUnsplash 上拍摄

分类方法的替代方法是使用预测集合。本质上,我们训练几个预测模型来预测时间序列的未来值。然后,我们可以通过计算预测值超过阈值的模型的比率来估计超过概率。

基于集合的方法在与环境科学相关的领域中很常见,例如气象学或水文学。在这些领域中,通过改变初始条件来建立不同的模型。在机器学习中,我们可以做到这一点,例如,通过使用不同的学习算法或不同的训练集。

下面是一个创建预测集合的模型。在这种情况下,我们将使用随机森林,它在不同的训练集上训练几个决策树。这是上一个脚本的延续,所以我将直接开始创建模型。

剧本 2:剧本 1 的延续。为超越概率预测创建回归预测集合的代码模型

首先,我们创建预测集合(第 1-8 行)。请注意,我们现在使用数字响应变量来表示序列的下一个值。然后(第 11-12 行),我们检索每个系综成员的数值预测(在这种情况下,是一棵树)。最后,在第 15 行计算超越概率。

评估预测

我们可以使用 AUC(ROC 曲线下面积)或 Brier 评分等指标来评估每种方法。这些衡量概率预测的准确性。请看下面的脚本。

脚本 3:评估概率预测的准确性

每种方法的优缺点

根据输入数据集,这些方法中的一种可能会执行得更好。但是,除了性能之外,还有其他方面需要考虑,使这些策略中的一个相对于另一个更可取。

相对于分类器,通过集成采用回归方法具有两个主要优点。第一个是集成模型,因为集合的预测不仅可以用于估计超越概率,还可以用于估计时间序列的未来值。第二个优点是阈值 灵活性因为这个参数不是事先固定的。分类方法在训练期间固定阈值,并且不能为了推断而改变。另一方面,回归模型遵循关于阈值的懒惰方法。因此,在给定时刻,我们可以使用相同的模型来估计不同阈值的超越概率。这使我们能够绘制如图 2 所示的超越概率曲线。

图 2:超越概率曲线,给出了不同阈值的超越概率。图片作者。

另一方面,众所周知,集成方法的计算量很大,这阻碍了它们在许多领域的应用。

扩展ˌ扩张

峰值超过阈值(POT)

超越概率预测的一个相关问题是峰值超过阈值分析。但是,峰值超过阈值方法的目标不是试图预测超出事件,而是研究超出阈值的观测值的统计特性。

超越一个门槛

虽然超越概率预测通常指二元事件(无论超越是否发生),但也可以定义多个阈值。

这方面的一个例子是在定量交易中使用的三重屏障方法。股票市场交易者可能对根据价格变动预测买入、卖出或持有信号感兴趣。在预测价格回报方面超过正阈值可以用作买入信号;相反(预测价格回报低于负阈值)可以代表卖出触发。如果没有一个阈值被满足,那么交易者应该持有当前的头寸。

外卖

  • 超越概率预测是估计一个时间序列在一个预先定义的未来时期内超越一个预先定义的阈值的概率的过程;
  • 这个问题与极端值对预测很重要的情况有关;
  • 解决这一任务有两种主要方法:二元分类模型或预测集合。各有利弊。

进一步阅读

[1]泰勒、詹姆斯·w .和余克明。"利用自回归 logit 模型预测金融风险管理的超越概率."《英国皇家统计学会杂志:A 辑(社会中的统计学)179.4(2016):1069–1092。

[2] Méndez,Fernando J .等人,“使用与时间相关的峰值超过阈值(pot)模型对极端显著波高的长期可变性进行估计。”地球物理研究杂志:海洋 111。C7 (2006 年)。

机器学习算法中的梯度下降和反向传播导论

原文:https://towardsdatascience.com/an-introduction-to-gradient-descent-and-backpropagation-in-machine-learning-algorithms-a14727be70e9

机器学习算法利用反向传播和梯度下降算法来模拟学习。理解神经网络中学习的计算模拟所涉及的算法和过程的基本原理。

照片由 Unsplash 上的 ThisisEngineering RAEng 拍摄

人工神经网络 (ANN)是人工智能技术的基本构件。人工神经网络是机器学习模型的基础;它们模拟了和人脑一样的学习过程。简而言之,人工神经网络赋予机器在特定任务中完成类似人类(甚至更高)表现的能力。本文旨在为数据科学家提供基本的高级知识,帮助他们理解在训练人工神经网络时调用的函数和方法中涉及的低级操作。

作为数据科学家,我们的目标是通过揭示数据中的模式来解决业务问题。通常,这是通过使用机器学习算法来识别模式和表示为模型的预测来完成的。为特定用例选择正确的模型,并适当地调整参数,需要对问题和底层算法有透彻的理解。考虑对问题域和算法的理解,以确保我们恰当地使用模型,并正确地解释结果。

本文介绍和解释梯度下降和反向传播算法。这些算法有助于人工神经网络从数据集学习,特别是在涉及数据点和神经网络预测的操作导致网络参数值发生修改的情况下。

建立直觉

在我们进入这篇文章的技术细节之前,让我们看看人类是如何学习的。

人类大脑的学习过程是复杂的,研究几乎没有触及人类如何学习的表面。然而,我们所知道的一点点对构建模型是有价值和有帮助的。与机器不同,人类不需要大量数据来理解如何解决问题或进行逻辑预测;相反,我们从经验和错误中学习。

人类通过突触可塑性的过程来学习。突触可塑性是一个术语,用来描述在获得新信息后,新的神经连接是如何形成和加强的。与我们经历新事件时大脑中的连接被加强和形成的方式相同,我们通过计算神经网络预测的误差,并根据这些误差加强或削弱神经元之间的内部连接,来训练人工神经网络。

梯度下降

梯度下降是一种标准的优化算法。它经常是引入来训练机器学习的第一个优化算法。让我们剖析一下术语“梯度下降”,以便更好地理解它与机器学习算法的关系。

梯度是对直线或曲线的陡度进行量化的度量。数学上,它详细描述了直线上升或下降的方向。

下降是向下的动作。因此,梯度下降算法基于这些短语的两个简单定义来量化向下运动。

为了训练机器学习算法,你努力识别网络中的权重和偏差,这将帮助你解决正在考虑的问题。例如,您可能有一个分类问题。当查看图像时,您想要确定图像是猫还是狗。要构建模型,您需要使用带有正确标记的猫狗图像数据样本的训练数据来训练您的算法。

虽然上述示例是分类,但问题可能是定位或检测。尽管如此,一个神经网络对一个问题的表现如何被建模为一个函数,更具体地说,是一个成本函数;成本或有时被称为损失函数,衡量一个模型的错误程度。成本函数的偏导数影响最终模型的权重和选择的偏差。

梯度下降是一种便于搜索参数值的算法,该算法使成本函数朝着局部最小值或最佳精度最小化。

神经网络中的成本函数、梯度下降和反向传播

神经网络令人印象深刻。同样令人印象深刻的是,计算程序能够在没有被明确告知要检测什么特征的情况下,区分图像和图像中的对象。

将神经网络视为接受输入(数据)以产生输出预测的函数是有帮助的。这个函数的变量是神经元的参数或权重。

因此,解决提交给神经网络的任务的关键任务将是以接近或最好地代表数据集的方式调整权重和偏差的值。

下图描绘了一个接收输入(X1,X2,X3,Xn)的简单神经网络,这些输入被前馈到包含权重(W1,W2,W3,Wn)的层内的神经元。输入和权重经过乘法运算,结果由加法器()相加,激活函数调节该层的最终输出。

图 1:作者创建的浅层神经网络图像

为了评估神经网络的性能,需要一种用于量化神经网络预测和实际数据样本值之间的差异或差距的机制,产生影响神经网络内的权重和偏差的修改的因子的计算。

通过成本函数来促进神经网络的预测值和数据样本的实际值之间的误差差距。

图 2:描绘的神经网络内部连接和预测

上图展示了一个由密集连接的神经元组成的简单神经网络架构,它对包含数字 0-3 的图像进行分类。输出层中的每个神经元对应一个数字。神经元连接的激活程度越高,神经元输出的概率就越高。该概率对应于通过网络前馈的数字与激活的神经元相关联的可能性。

当一个“3”通过网络被前馈时,我们期望负责分类“3”的连接(由图中的箭头表示)具有更高的激活,这导致与数字“3”相关联的输出神经元的概率更高。

几个组件负责神经元的激活,即偏置、权重和先前层激活。对于神经网络,这些指定的组件必须被迭代地修改,以在特定数据集上最佳地执行。

通过利用诸如“均方误差”的成本函数,我们获得与网络误差相关的信息,该信息用于通过网络的权重和偏差向后传播更新。

为了完整起见,下面是机器学习中使用的成本函数的例子:

  • 均方误差
  • 范畴交叉熵
  • 二元交叉熵
  • 对数损失

我们已经介绍了如何通过测量网络预测的技术来提高神经网络的性能。本文的其余内容集中在梯度下降、反向传播和成本函数之间的关系。

图 3 中的图像展示了一个绘制在 x 轴和 y 轴上的成本函数,该函数的参数空间中包含值。让我们来看看神经网络如何通过将成本函数可视化为在可能的权重/参数值的参数空间内的图表上绘制的不平坦表面来学习。

图 3:梯度下降可视化

上图中的蓝点表示搜索局部最小值的一个步骤(将参数值评估到成本函数中)。建模成本函数的最低点对应于导致成本函数最低值的权重值的位置。成本函数越小,神经网络的性能越好。因此,可以根据收集的信息修改网络的权重。

梯度下降是一种算法,用于引导在每一步选择的值对向最小值移动。

  • 局部最小值:在成本函数的指定范围或扇区内的最小参数值。
  • 全局最小值:这是整个成本函数域内的最小参数值。

梯度下降算法通过计算可微函数的梯度并沿梯度的相反方向移动,来指导在局部/全局最小值处最小化函数的值的搜索。

让我们换个话题,回顾一下反向传播。

反向传播是迭代调整神经元输出(偏差、权重、激活)以降低成本函数的方法。在神经网络架构中,神经元的输入(包括与前一层中的神经元的所有先前连接)决定其输出。

嵌入在反向传播中的迭代数学过程计算成本函数相对于权重、偏差和先前层激活的偏导数,以识别哪些值影响成本函数的梯度。

通过计算梯度最小化成本函数导致局部最小值。在每个迭代或训练步骤中,网络中的权重通过计算的梯度以及学习速率进行调整,学习速率控制对权重值进行修改的因子。在神经网络的训练阶段,对要采取的每一步重复该过程,目标是在每一步之后更接近局部最小值。

图 4:错误的向后传播(反向传播)。Gif 来源:3Blue1Brown,第三章,深度学习

“反向传播”这个名字来源于这个过程的字面意思,即“错误的反向传播”梯度的偏导数量化了误差。通过在网络中反向传播误差,最后一层(离输出层最近的层)的梯度的偏导数用于计算第二层到最后一层的梯度。

误差通过各层传播,并且在当前层中利用来自前一层的梯度的偏导数,直到到达网络中的第一层(最接近输入层的层)。

梯度下降算法的类型

神经网络预测的成本是通过评估训练集中的预期结果和数据样本来确定的。使用反向传播和梯度下降将数据传送到神经网络以进行权重修改采取了各种形式。

本节介绍了基于训练数据和训练数据集中的数据样本计算梯度下降的三种常用方法。

  • 批量梯度下降(BGD)
  • 随机梯度下降法
  • 小批量梯度下降

批量梯度下降

我们了解梯度下降是如何工作的,现在可以将其应用于我们的训练数据。梯度下降算法对训练数据的应用有多种形式。一种形式叫做批量梯度下降(BGD)

在图 3 中,我们朝着局部最小值的方向前进。当使用 BGD 时,我们应用每一个训练数据来决定我们朝最小的方向走了多远。在每个阶段,我们都会利用所有的训练数据。

对于大量数据,训练时间可能会延长。尽管如此,它在计算上是高效的,因为我们不像其他梯度下降变体那样频繁地修改我们的模型参数。因为在训练模型时我们需要所有的数据集,所以 BGD 不是内存高效的。

随机梯度下降

随机梯度下降(SGD)的工作方式是从训练集中选取单个数据点,并基于该单个数据点计算梯度,而不是遍历训练集中的每一个数据,然后移向局部最小值。

在 BGD 和随机梯度下降之间,后者明显更快,因为您是在计算单个数据实例的梯度,而不是整个数据集。这有什么影响?

随机梯度下降的缺点在于,当利用梯度下降时,参数空间内的更新可能有噪声。当从训练集中选择数据点来计算每一步的梯度时,SGD 的随机性质使它变得有噪声。

为了适应随机梯度下降的噪声并保证我们达到最佳参数值,我们必须对训练数据迭代特定次数,并确保训练数据在梯度下降开始时被混洗。

噪声导致求解成本函数的模糊参数值。然而,给定足够的时间,随机梯度下降将收敛于局部最小值。随机梯度下降的噪声和随机性也是有利的。当算法变得“停滞”时,这对于摆脱不是全局最小值的局部最小值是有用的

与 BGD 相比,SGD 在每一步分配参数值时,由于其随机和不稳定的特性,具有避开局部最小值和找到全局最小值的优点。

在每一步分配参数值时,随机梯度下降的随机和不可预测的性质使其在避免局部最小值和定位全局最小值方面优于 BGD。

但是与随机梯度下降相比,BGD 参数值更接近全局最小值和最优值。当在梯度下降算法的两个变型之间进行选择时,在速度和最优性之间有一个折衷。

小批量梯度下降

考虑采取一种独特的方法来结合新加坡和 BGD 的优势。

小批量梯度下降算法使用从训练集中随机选择的多个数据样本计算梯度,就像 SGD 一样。然而,当计算梯度时,它不包括整个数据集,所以它不完全是 BGD。你可以把它描述为两种方法的混合。

与 BGD 相比,Mini Batch GD 在计算梯度时使用最少量的数据;但是,比 SGD 慢。

与 SGD 相比,小批量 GD 的一个关键优势是小批量梯度下降减少了参数空间中的噪音。因此,与单批次 GD 相比,使用小批次梯度下降可获得更可行的最佳参数值。

摘要

这只是梯度下降和反向传播主题的初级读本。有一整个世界的数学和微积分与梯度下降的话题。

诸如 TensorFlowSciKit-LearnPyTorch 之类的软件包通常会抽象出实现训练和优化算法的复杂性。然而,这并没有解除数据科学家和 ML 从业者理解这些智能“黑盒”背后发生的事情的需求

想探索更多与反向传播相关的数学吗?以下是一些有助于您探索的资源:

通过探索 Nvidia 深度学习学院提供的各种课程和实践教程,深入深度学习的世界。

感谢阅读。

本文最初发表于 Nvidia AI 博客

我希望这篇文章对你有用。

要联系我或找到更多类似本文的内容,请执行以下操作:

  1. 成为推荐媒介会员,支持我的写作
  2. 订阅我的 YouTube 频道 获取 AI 播客和即将推出的内容。
  3. 订阅我的 邮件列表 获取我的简讯

图划分算法和社区检测简介

原文:https://towardsdatascience.com/an-introduction-to-graph-partitioning-algorithms-and-community-detection-29e7c962d10e

D 锦鲤Unsplash 上的照片

图划分是一个长期存在的问题,有着广泛的应用。这篇文章分享了图划分的方法,包括一些流行的图划分算法的理论解释和 python 代码的实际实现。

澄清

“聚类”在不同的语境下可能会令人混淆。在本文中,集群意味着节点集群,即将图划分为集群(或社区)。我们交替使用图划分、(节点)聚类和社区检测。换句话说,我们在本文中没有考虑重叠社区。(请注意,社区检测的更广泛定义可以包括重叠社区)

三言两语…

图划分通常是一个无监督的过程,我们定义期望的质量度量,即聚类评估度量,然后我们采用一些算法来基于定义的评估度量找到最佳划分解决方案。在余下的内容中,我们将首先介绍两个最常用的评估指标。然后,我们介绍两种方法,可以有效地找到每个评估指标的(近似)解决方案。

聚类评估指标

由于聚类通常是一项无人监督的任务,因此重要的是要有一个质量度量或目标函数来评估聚类解决方案。其他领域中的聚类通常在数据点上使用不同的基于距离的函数来测量聚类的质量。然而,由于图是非欧几里得数据,并且数据点的距离远不如图中的连接重要,所以我们需要对图中的聚类进行不同的测量。换句话说,质量度量需要在图的连通性上定义。

切割和规格化切割

通常,如果一部分节点和边彼此紧密连接,则它们被认为是一个社区(或一个集群),否则被认为是不同的社区。因此,很自然地对图进行聚类,使得节点在一个聚类中具有最大边,而在不同的聚类中具有最小边。跨越不同聚类的边的权重之和被定义为图的“切割”。换句话说,“切割”是为了将图完全分离成单独的子图而移除的边的总权重。

切割的简单说明。作者图片

在数学上,将图 G‘切割’成两个不相交的集合 A 和 B 可以计算为:

求图 G 聚类的最佳方式,问题等价于求“割”的最小值,即 min-cut 。然而,也不难看出,最小割的一种可能的退化解决方案是从整个图中切掉少量节点,从而产生仅移除几条边的平凡聚类。一种退化的解决方案如所示

min-cut 提供的琐碎解决方案示例。图来自【1】。

为了克服这个问题,[1]提出了一种方法来归一化每个聚类内的边的体积的测量的切割,定义为关联(Assoc)。

归一化切割(n-切割)有效地惩罚了最小切割中的退化解,使其在包括图像分割和图形社区检测在内的许多应用中成为一种鲁棒且流行的聚类度量。

模块性

图模块性是在[2]中作为一个质量函数引入的,用于评估社区的紧密性。模块化 Q 定义为:

其中 2m 是边的体积, A 是图邻接矩阵, k_i 和 k_j 是节点 i 和节点 j 的度,s_i 和 s_j 是社区指示器。

图形模块性的解释:

A_ij 是每两个节点之间的实际边数,因为图的邻接矩阵定义了图的连通性。并且表达式( k_ik_j)/2m* 给出了每两个节点之间的期望边数(假设随机放置边),或者换句话说,节点 i 和节点 j 之间存在边的概率(如果随机放置边)。

因此,模块性可以解释为一个社区(或一个集群)中每对节点的实际边数与期望边数(假设边是随机放置的)之差。

模块性取[-0.5,1]之间的值,并且当社区与图的剩余部分断开时最大,当社区包含完全断开的节点时最小。

图划分算法

我们可以很容易地使用前面提到的两个评估指标来测试一个图划分解决方案。然而,寻找(最佳)图划分解决方案是 NP 完全的。换句话说,没有已知的有效算法能比暴力算法更有效地解决这个问题。通俗地说,保证得到最佳解决方案的唯一可能方式是尝试每一种可能的组合……由于图形的大小,它几乎对图形聚类解决方案的所有组合进行了详尽的测试(例如强力测试)。因此,多年来,已经提出了不同的有效算法来寻找图聚类的近似解。

谱聚类

众所周知,最小割问题可以通过 Ford-Fulkerson 算法高效解决。然而,当施加尺寸平衡条件(即规格化切割)时,这个问题就变成了 NP 完全问题。谱聚类是一种在归一化的图拉普拉斯上逼近归一化的特征值/特征向量分解的解的方法。

  • 通过下式获得图拉普拉斯算子:L=D-A
  • 执行图拉普拉斯的特征分解。Lv=λv
  • 将图形投影到对应于 k 个最小特征值的特征向量,每列投影的特征向量是每个节点的特征特征
  • 对本征特征执行 k-均值聚类

特征值表示图的连通性。当我们向下遍历一个图时,图拉普拉斯的特征值给我们洞察变化。最小的特征值总是零。零特征值的数量表示图中连通分量的数量。从下面的例子可以看出这一点。

具有两个相连组件的图形(左)。相应的特征值(右)。作者图片

有一个连通分量的图形(左)。相应的特征值(右)。作者图片

如果你仔细观察特征值,你会注意到光谱中有一个突然的变化。我通常称之为“本征能隙”(尽管我注意到对此有不同的定义)。间隙表示图中自然存在的簇的数量。这可以从下面的例子中观察到:

有三个自然集群的图表(左)。相应的特征值(右)。作者图片

具有两个自然集群的图形(左)。相应的特征值(右)。作者图片

请注意,“间隙”出现的位置从“3”变为“2 ”,正好对应于图中的聚类数。

这种观察为我们提供了使用特征值来查找图中聚类数的直觉,特别是当我们不知道数据中实际或预期的聚类数时(这通常是真实世界的用例)。

接下来,我们需要做的就是将特征向量投影到图的每个节点上。在下面的示例中,我们之前已经在特征值图中确定了三个集群。因此,我们只取最小的三个特征值对应的前三个特征向量。注意,每个特征向量正好包含 N 个数字,其中 N 是图中节点的数量。我们将前三个特征向量投影到每个节点,每列作为特征特征。执行 K-means 聚类(或您喜欢的对数据点的任何其他聚类方法)来基于本征特征找到聚类。以下示例中的颜色表示已识别的分类。注意,在计算中仅使用了前三行(即前三个特征向量)。您可以通过查看每个聚类中本征特征的距离来进行快速验证。请注意,我特意在图中的一个集群中制作了{1,5,6},以表明节点索引无关紧要。

特征向量聚类的图示。作者图片

好了,这就是谱聚类背后的数学。在真实的情况下,除非您是一名研究人员或者您想要完全理解您正在做的事情,否则您并不真的需要经历所有这些复杂的实现。使用谱聚类的一个更简单的方法是使用 sklearn 库中的实现。下面的代码块中显示了一个示例:

import networkx as nx
from sklearn.cluster import SpectralClustering
from sklearn.metrics.cluster import normalized_mutual_info_score
import numpy as np# Here, we create a stochastic block model with 4 clusters for evaluation
sizes = [150, 150, 150, 150]        
probs = [[0.20, 0.05, 0.02, 0.03], [0.05, 0.30, 0.07, 0.02],                 [0.02, 0.07, 0.30, 0.05], [0.03, 0.02, 0.05, 0.50]]G = nx.stochastic_block_model(sizes, probs, seed=0)adj = nx.adjacency_matrix(G)
n_clusters = 4
node_labels = [G.nodes[n]['block'] for n in np.sort(G.nodes)]spectral_clusters = SpectralClustering(n_clusters=n_clusters, assign_labels="discretize", affinity='precomputed').fit_predict(adj)# Get the result
nmi = normalized_mutual_info_score(spectral_clusters, node_labels)
print("nmi:", nmi)

限制:

  1. 大型矩阵的特征分解在计算上非常昂贵。这展示了谱聚类在大型图上的应用。
  2. 谱聚类只是最佳聚类解决方案的一种近似。

鲁文聚类

Louvain 的方法[3]是一种快速的图模块优化算法。它在两阶段迭代过程中优化了图的模块性。在阶段 1 中,首先为图中的每个节点分配一个单独的社区。在那之后,对于每个节点 i ,当:

  1. 节点 i 从其原始团体中移除
  2. 节点 i 被插入到其相邻节点 j 的社区中

重复阶段 1,直到模块性没有增加并且达到局部最大值。

在阶段 2 期间,通过替换相同社区中的所有节点来创建新的图,将所有节点合并成代表社区的单个节点。社区内的边由到节点的自环代替,社区外的边由到其他节点的加权边代替。一旦创建了新图,它就在新图上重复阶段 1。

我们将使用 sknetwork 中的实现来测试 Louvain 的方法。下面的代码块中显示了一个示例:

import networkx as nx
from sknetwork.clustering import Louvain
from sklearn.metrics.cluster import normalized_mutual_info_score
import numpy as np# Here, we create a stochastic block model with 4 clusters for evaluation
sizes = [150, 150, 150, 150]        
probs = [[0.20, 0.05, 0.02, 0.03], [0.05, 0.30, 0.07, 0.02],                 [0.02, 0.07, 0.30, 0.05], [0.03, 0.02, 0.05, 0.50]]G = nx.stochastic_block_model(sizes, probs, seed=0)adj = nx.adjacency_matrix(G)
n_clusters = 4
node_labels = [G.nodes[n]['block'] for n in np.sort(G.nodes)]louvain = Louvain()    
clusters = louvain.fit_transform(adj)# Get the result
nmi = normalized_mutual_info_score(clusters, node_labels)
print("nmi:", nmi)

结论

在本文中,我们简要介绍了图划分、图划分的两个评估指标,以及分别优化 n-cut 和图模块化的两种算法。这些算法是早期的方法,可以追溯到 2000 年代,但由于其巨大的效率和可行性,仍然广泛用于许多图划分应用程序。

然而,在最近的应用中,图通常在节点特征中包含丰富的信息。因此,尽管这些方法功能强大且高效,但它们越来越不适用于现代图形应用程序。这些方法的局限性在于,它们仅基于图的连通性来划分图,而没有考虑节点特征中的任何信息。尽管已经做了一些工作将部分节点特征编码到边权重中并在加权图上进行划分,但是边权重所能表示的信息量仍然有限。最近有使用 图神经网络 进行图划分的方法,该方法可以联合考虑图的连通性和节点特征来检测图中的社区。

参考资料:

[1] J. Shi 和 J. Malik,“归一化切割和图像分割”, IEEE 模式分析和机器智能汇刊,第 22 卷第 8 期,第 888–905 页,2000 年。

[2] M. E. J .纽曼,《网络中的模块化与社区结构》,Phy。2006 年修订版

[3] 布隆德尔,文森特 D.纪尧姆,让-卢普朗比奥特,雷诺列斐伏尔,艾蒂安,《大网络中社区的快速展开》,统计力学杂志,2008

Python 程序员的 Ibis 介绍

原文:https://towardsdatascience.com/an-introduction-to-ibis-for-python-programmers-2112ea32370d

更 Pythonic 化的处理数据库的方式

照片来自图片编辑

几周前,我正致力于建立一个关系数据库,以探索来自 DataSF 的公民艺术收藏的记录。每当我参加一个技术会议,我都会试着花一两天的时间在这个城市看看它的文化场景,所以这看起来像是有用的信息!我决定使用 MySQL 作为我的数据库引擎。作为一个熊猫出身的人,我对自己编写原始 SQL 查询时的低效和受限感到惊讶。我还花了大量时间来解决查询中的错误,这些错误适用于一种风格的 SQL,但不适用于 MySQL。在整个过程中,我一直在想,如果有一个更 Pythonic 式的方法就好了!!!几周后,我被介绍给了宜必思。在这篇文章中,我将向您简要介绍 Ibis,并带您浏览一些代码示例,展示 Ibis 如何为我的问题提供解决方案。

Ibis 提供了一种更 Pythonic 化的方式来与多个数据库引擎进行交互。对于我们这些喜欢在 SQL 字符串上写 python 代码的人来说,这尤其有用。可以在 select 语句中编写的任何东西都可以在 Ibis 中编写。在我自己的冒险中,我总是遇到栖息在大象身上的朱鹭(鸟类版本)。如果你在现实生活中从未见过大象,我可以肯定它们是巨大而复杂的生物。这是我站在两个人旁边的照片。

作者图片

一只小鸟坐在一只大象上的图像隐喻了 Ibis 如何为用户提供一种不太复杂、更具性能的方式来与多个大数据引擎进行交互。事实上,数据越大越复杂,使用 Ibis 的争议就越大。它简化了您的工作流程,消除了对复杂 SQL 查询的需求。相反,你可以编写类似熊猫的代码,无论你想扩大规模还是缩小规模,代码都保持不变。

比方说,您已经开始在 jupyter 笔记本中编写访问 SQLite 数据库的代码。如果您决定扩大规模并使用 Dask,您可以在同一个笔记本中用一行代码实现这一点。如果需要,Ibis 可以让您自由地切换后端,并在相同的上下文中使用更强大的引擎继续工作。这意味着你的工作保持在一个地方,不容易出错,你的认知负荷也减少了。

目前,Ibis 支持相当多的后端,包括:

传统 DBMS:PostgreSQL、MySQL、SQLite
分析 DBMS:OmniSciDB、ClickHouse、Datafusion
分布式 DBMS:Impala、PySpark、BigQuery
内存分析: pandas、Dask

无论您想要与 SQL 数据库还是分布式 DBMSs 进行交互,Ibis 都可以让您用 Python 来实现。对于 Python 程序员来说,Ibis 提供了一种用 Python 编写 SQL 的方法,允许对特定查询引擎(例如 BigQuery)进行单元测试、可组合性和抽象!你可以用一种熟悉的、类似熊猫的语法对你的数据进行连接、过滤和其他操作。

在这篇文章中,我将带你了解使用 SQLite 后端的一些基本知识,但是如果你想尝试其他的,请查看 Ibis 后端页面的例子。Ibis 目前支持超过 12 个后端,其中一些编译成 SQL,一些直接执行。

让我们使用我之前提到的公民艺术收藏数据集,看看 Ibis 能做什么!

装置

可以用 Pip,Conda 或者 Mamba 安装 Ibis。

为了安全起见,我建议建立一个虚拟环境。您可以将这些命令复制并粘贴到您的终端中,以安装带有 Pip 或 Conda 的 Ibis。

Pip:

如果您使用 pip 来安装 Ibis,您将需要运行pip install 'ibis-framework[sqlite]'而不是通常的pip install ibis-framework来运行 sqlite。如果您喜欢使用另一个后端,请在 Ibis 文档中查找具体的命令。

Mac:

窗口:

康达:

如果你正在使用 Conda,你可以使用下面的命令,sqlite 框架将默认包含在内。

用 SQLite 创建数据库

接下来,我们将创建一个数据库和一个表,其中包含来自 DataSF 的关于旧金山周围艺术的数据。这些数据是开源的,所以你可以直接从网站下载 CSV 文件。Ibis 还不支持将 CSV 文件加载到 SQL 表中,所以我们将按照这些命令来创建一个 SQLite 数据库和一个包含我们的数据的表。

创建一个名为 civic_art 的文件夹

为了使接下来的步骤更容易,我将 CSV 文件从我的下载文件夹移动到我们刚刚创建的 civic_art 文件夹中。当我们在 civic_art 文件夹中时,我们用下面的命令创建一个新的数据库。

创建一个名为 civicArt.db 的数据库

这个命令应该可以在命令行中打开 SQLite。您可以键入.databases来再次检查数据库是否被正确创建。这是我看到的照片。

太好了!数据库已成功创建!SQLite 已经在civicArt database中打开了,所以我们接下来要做的是创建一个表,并将 CSV 文件中的数据加载到其中。

**civicArt.db**中创建一个表名为 **civicArtTable**

检查表格是否已按预期创建

如果您的表已经按预期创建,您现在可以通过键入.quit退出 SQLite。这将带您回到命令行。我们现在准备开始用 Ibis 探索我们的数据!

连接到数据库

Ibis 的优势之一是它允许您使用自己最熟悉的工具工作。我喜欢用 IPython 编写 Python 代码,但是您也可以使用 jupyter 笔记本或任何您喜欢的 Python shell。无论您选择什么,我们都将使用相同的命令连接到我们之前创建的 civicArt 数据库。

连接到我们的数据库(我们将使用 Ibis 的交互模式进行惰性评估)

这里需要注意的是, pandas.read_sql 将数据加载到内存中并自己执行计算。 Ibis 不会加载数据或执行任何计算。相反,它将数据留在数据库中,并要求后端在那里执行计算。这意味着您可以以后端的速度执行,而不是本地计算机的速度。思考这个问题的一个好方法是回到我们的大象比喻。如果你需要一些重物,与朱鹭相比,让大象来做可能是个好主意。我们优雅的 Ibis 可以将较重的工作传递给较大的数据库引擎,并观察工作完成的速度比它本身快得多。

照片由存放照片

如果一切顺利,你应该连接到数据库。接下来我们来看看我们的数据!

查看表格详细信息

让我们使用 Ibis 来查看一下 civicArt 数据库中的表。

列出数据库中的所有表格

为表格指定一个更易读的名称,并列出其中的列

与 Ibis 争论数据

现在让我们使用 Ibis 在数据集上执行一些常见的 SQL 命令,并在这个过程中找到一些关于旧金山艺术的有用信息。

询问

您可以在 SELECT 语句中编写的任何内容都可以在 Ibis 中编写。让我们来测试一下!我将使用下面的代码来找出哪些艺术家的作品目前在这个城市展出,以及他们的作品的标题是什么。

从表格中选择列

太好了!如果您以前使用过 Pandas,这应该感觉类似于从 Pandas 数据框架中获取列。但是请记住,数据库正在为我们做所有的工作,所以这样效率更高!

我也很好奇总共有多少艺术家展出他们的作品。为了找出答案,让我们找出不同的艺术家,然后计算他们的数量。

独特和计数

您应该已经在表格底部看到了列表的长度,但是为了向您展示计数方法,我们确认在旧金山展出一件或多件作品的艺术家数量为 386 位!

过滤数据

接下来让我们挑选一位艺术家,并找出他们所有的艺术作品的确切位置。阿德里安·科尔伯恩斯的展览标题《地质幽灵》引起了我的注意,所以让我们选择它们吧!我使用以下命令来完成这项工作。

按艺人名称过滤

原来 Adriane 有两件展品,一件在 4 Guy Place,另一件在 Dagget 街& 16 街。这太好了,我们已经有一些地方可以添加到我们的旅游行程中了!

分组依据

会议结束后,我通常不会在一个城市停留超过一两天。为了在最短的时间内做最多的事情,知道哪些地方展出的艺术品最多可能是件好事。我们将使用下面的groupby表达式来获取我们需要的信息。

获取旧金山最具艺术气息的地点!

第一站是 Potrero 大街 1001 号,这里展出了 59 本书!!

出于好奇,我搜索了波特雷罗大道 1001 号,以了解更多关于我所期望的艺术画廊的信息。令人惊讶的是,它竟然是扎克伯格旧金山总医院。他们显然在艺术上投入了大量资金,并拥有令人难以置信的收藏。数据能让你大吃一惊!艺术品数量第二多的地方是位于电报山大道 1 号的 Coit Tower。它不是医院,看起来是一个探索的好地方!

照片来源 Flikr

连接

非常好。在这一点上,我们知道,下次我们在旧金山参加技术会议时,我们有一些很好的选择。现在我们知道去哪里了。下一步是什么?

嗯,通常美术馆里都是潮人,他们可能会问你展出的艺术家的情况。好在我们可以提前做好这方面的准备!让我们使用一个内部连接来帮助我们找到哪些艺术家有最多的艺术展示,这样我们就可以查找他们,并通过良好的响应感到很酷;)

使用 inner_join 查找最受欢迎的艺术家

( I bis 版本 2.1.1 有时会遇到视图注释被具体化,然后在连接操作中具体化不解析列名的问题。以下代码片段仅适用于版本 2.1.1,并使用 mutate()函数来更改其中一个列标题和 materialize(),以确保可以显示创建的视图。这个应该是在3 . 0 . 0版本中修复的。)

使用 inner_join 查找当地最受欢迎的艺术家

太棒了。我们现在知道 Mary (McChesney) Fuller 在 Potrero 大道 1001 号展出的艺术品最多,可以在网上搜索了解更多!在我看来,我们已经做好了探索科幻艺术的准备!

我们在相对较短的时间内学到了很多信息。如果你喜欢 Python 代码,这可能比写出原始的 SQL 字符串更有效。这也意味着如果你在 IPython 或 jupyter 笔记本上开始你的工作流程,你可以留在那里。这应该感觉更顺畅,并有望提高生产率。使用有助于提高效率的工具意味着你有更多的时间在工作中进行创新和探索!感谢你的阅读,希望我能在世界各地的某个美术馆里看到你看大象。

另外,我还做了一个 jupyter 笔记本,里面有这篇文章中列出的所有命令!这里可以找到

长短期记忆网络导论(LSTM)

原文:https://towardsdatascience.com/an-introduction-to-long-short-term-memory-networks-lstm-27af36dde85d

理解长短期记忆的概念和问题

照片由 Soragrit WongsaUnsplash 上拍摄

长短期记忆(简称:LSTM)模型是循环神经网络 (RNN)的一个亚型。它用于识别数据序列中的模式,例如出现在传感器数据、股票价格或自然语言中的模式。rnn 之所以能够做到这一点,是因为除了实际值之外,它们还在预测中包括其在序列中的位置。

什么是递归神经网络?

为了理解递归神经网络如何工作,我们必须再看一下常规的前馈神经网络是如何构造的。其中,隐藏层的神经元与前一层的神经元和后一层的神经元相连。在这样的网络中,一个神经元的输出只能向前传递,而绝不会传递给同一层甚至上一层的神经元,因此得名“前馈”。

这对于递归神经网络是不同的。神经元的输出可以很好地用作前一层或当前层的输入。这比前馈神经网络的构建方式更接近我们大脑的工作方式。在许多应用中,我们还需要在改善整体结果之前立即了解计算的步骤。

RNNs 面临哪些问题?

递归神经网络是深度学习领域的一个真正突破,因为第一次,最近过去的计算也包括在当前计算中,显著提高了语言处理的结果。尽管如此,在训练过程中,他们也带来了一些需要考虑的问题。

正如我们在关于梯度法的文章中已经解释过的,当用梯度法训练神经网络时,梯度可能会呈现非常小的接近 0 的值或者非常大的接近无穷大的值。在这两种情况下,我们都不能在反向传播过程中改变神经元的权重,因为权重要么根本不变,要么我们不能用这么大的值乘以数字。由于递归神经网络中的许多互连以及用于它的反向传播算法的稍微修改的形式,这些问题发生的概率比正常的前馈网络高得多。

常规 rnn 非常擅长记忆上下文,并将其纳入预测。例如,这允许 RNN 认识到在句子“clouds are at the _ _”中,需要单词“sky”来在该上下文中正确完成句子。另一方面,在一个较长的句子中,保持上下文变得困难得多。在稍加修改的句子“部分流入彼此并低悬的云在 __”中,一个递归神经网络推断“天空”这个词变得困难得多。

长短期记忆模型是如何工作的?

递归神经网络的问题在于,它们有一个短期记忆来保留当前神经元中以前的信息。然而,对于较长的序列,这种能力下降得非常快。作为对这一点的补救,LSTM 模型被引入,以便能够更长时间地保留过去的信息。

递归神经网络的问题在于,它们只是将之前的数据存储在它们的“短期记忆”中。一旦其中的内存耗尽,它就简单地删除保留时间最长的信息,并用新数据替换它。LSTM 模型试图通过在短期记忆中只保留选定的信息来避免这个问题。

为此,LSTM 架构总共包括三个不同的阶段:

  1. 在所谓的遗忘门中,决定哪些当前和先前的信息被保留,哪些被丢弃。这包括上次运行的隐藏状态和当前状态。这些值被传递到一个 sigmoid 函数中,该函数只能输出 0 到 1 之间的值。值 0 意味着所有以前的信息都被遗忘,1 意味着所有以前的信息都被保留。
  2. 输入门中,决定当前输入对解决任务有多大价值。为此,当前输入乘以隐藏状态和上次运行的权重矩阵。
  3. 输出门中,计算 LSTM 模型的输出。取决于应用,例如,它可以是补充句子意思的单词。

这是你应该带走的东西

  • LSTM 模型是递归神经网络的一个亚型。
  • 它们用于识别数据序列中的模式,例如出现在传感器数据、股票价格或自然语言中的模式。
  • 一种特殊的结构允许 LSTM 模型决定是在短期记忆中保留先前的信息还是丢弃它。因此,序列中更长的依赖性也被识别。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你想让无限制地访问我的文章和数以千计的精彩文章,不要犹豫,通过点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5获得会员资格*

** **

数据科学的数学思维导论

原文:https://towardsdatascience.com/an-introduction-to-mathematical-thinking-for-data-science-f1214d2cc61d

作为一名数据科学家,具备数学思维能力为何如此重要,以及如何做到这一点的详细示例

迈克尔·泽兹奇在 Unsplash 上的照片

当我在大学上第一堂数学课时,我绝望地迷路了。好像教授在用一种不同的语言说话,我发现自己对作业和考试总是感到困惑。我疯狂地记下笔记,尽可能多地参加办公时间的活动,并连续几个小时学习——但都无济于事。

后来我意识到问题不在于课程材料。不管我在科目的细节上多么努力(这是多变量微积分),因为我缺少的基础知识更广泛。

我不知道如何进行数学思考。在高中,我只学过如何将数字插入公式或求解遵循相同模式的基本方程。我大学数学课上的问题——以及相关的研究生职位上的问题——要难得多。

我最终参加了一个从头开始教授数学思维的课程,它极大地影响了我对技术问题的看法。在本文中,我将介绍三件事:1)数学思维的意义,2)讨论为什么它对数据科学很重要,以及 3)应用这种思维方式解决问题的详细示例。

什么是数学思维?

数学思维与许多数学家所说的“T4”数学成熟度“T5”密切相关。用加州大学伯克利分校教授阿南特·萨海[1]的话来说,数学成熟度是指“一步一步解决问题的舒适感,即使你向前迈出一步,也能保持对工作的信心。”

在大多数数学问题中,解决方案并不是一目了然的。一个数学上成熟的人发现不断进步并最终达成解决方案是合理的,甚至是令人满意的,即使他们一开始并不知道会是什么。

从广义上讲,数学思维是一种解决问题的方法,你必须学会应用,以获得数学上的成熟。这通常可以归结为发展证据,这是一个花哨的术语,指的是建立一个声称的事实为真的一系列逻辑步骤。

例如,早在小学时,勾股定理就在数学课上被普遍教授给学生:

这里, abc 分别指直角三角形的第一条边、第二条边和斜边。标准的家庭作业和考试问题包括用这个方程来求解三角形的缺边。

然而,一个更复杂的问题——一个需要数学思维的问题— 可能会要求某人通过一系列逻辑步骤来证明这个陈述是正确的。这将被称为勾股定理的证明。

在高层次上,你可以把做证明看作是解决一种难题。这需要一定程度的聪明和独创性——与流行的观点相反,这是可以通过实践培养的。还有一些常用的证明技巧[2],可以学习入门数学思维。

学习这些技巧可以帮助你提高数学成熟度。我不会特别关注任何一种技术,但是我将在后面的部分演示一个介绍性的证明。我用这个证明的目的是强调,数学思维不需要高深的数学知识,也不需要参考一套特定的技巧——它是任何人都可以学习的通用技能。

但是首先让我们花点时间来谈谈为什么你,一个数据科学家,首先应该关心数学思维。

为什么数学思维对数据科学很重要?

数据科学的数学基础来自理论统计学。现代数据科学中常用的许多技术——如机器学习、假设检验和代表性数据采样——都基于统计方法。

诚然,数据科学是一个广阔的领域,并不是每个数据科学家都需要知道如何在理论上开发一种前沿的机器学习算法才能做好自己的工作。这不是我的论点。

然而,对于任何从事数据科学工作的人来说,至少对解决手头问题的方法有一些了解是很重要的。

作为一个类比,考虑一个项目经理和软件开发人员为一个崭露头角的技术创业。项目经理的工作不是为产品编写实际的代码,而是作为消费者和开发者之间的接口,从而在高层次上管理项目。然而,具有一些软件开发知识的项目经理将能够更好地理解项目,并在平衡消费者需求和开发者约束的复杂性中导航(这是许多项目经理在学校也学习计算机科学的原因)。

同样,你可能不是一个理论数据科学家。也许您的天赋在于编码,所以您将现有的数据管理包合并到您的整体工作流中,而没有研究技术细节。或者也许你是一个领域专家,不编码模型或进行统计测试,但是帮助程序员和统计学家理解数据来源的上下文。

在这两种情况下,数学思考的能力会让你更深入地了解数据的复杂性,以及问题实际上是如何解决的。你可能会发现自己在逻辑上重新安排一个程序以使其更有效,或者推荐一种特定的数据收集技术来获得与现有统计方法相匹配的样本。这样做,您将扩展您的技能,从而为更好的数据科学工作流做出贡献。

现在,让我们看看数学思维实际上是如何进行的。

数学思维的一个应用例子是什么?

让我们通过一个简单的问题来说明如何通过数学思维来解决问题。这是我在本科数据科学课程中布置给我们的第一批问题之一。除了标准的代数运算之外,它不会涉及任何数学知识,并且专注于一步一步解决问题的逻辑过程。

我们将证明下面这个基本的统计陈述:对于任何一个数的集合,所有与其平均值的偏差之和是 0 [3]。这适用于任何数字列表,我们可以表示如下:

我们可以从数学上写出平均值的偏差总和开始:

这里, xbar (上面有横线的 x 代表平均值,每个下标 i 区分我们列表中的不同数字。

首先,让我们更仔细地考虑一下 xbar 的值。回想一下,上面我们指定使用 xbar 来引用数字列表的平均值。平均值是如何计算的?取所有数字的和,然后除以列表的长度。因此,根据定义,我们可以将 xbar 写成如下形式:

现在,让我们开始对原始方程(我们希望它等于 0)做一些小的操作,看看我们是否能得到什么。我们可以从编写以下内容开始:

我们上面所做的是在求和中分离出两项。等式的左边表示将括号中的所有内容相加 n 次。然而,请注意变量 xbar 没有下标,这意味着我们实际上只是将 -xbar 总共加了 n 次。因此,我们可以手动写出总和的这一部分,并将其从总和中移除,得到右侧的等式。

下一步,我们可以用扩展版的 xbar 来代替。将除以 n 改写为乘以(1/ n ,我们可以进一步扩展我们的等式:

在这一点上,很容易记住我们最初的目标,这有时会在步骤的海洋中迷失(特别是当问题变得更高级时):我们想证明这个方程等于 0。我们快到了,你看到解决办法了吗?我鼓励你在这里暂停一会儿,看看你是否能完成证明。

注意 n 乘以它的倒数(1/ n )正好是 1。因此,它们相互抵消,我们得到如下结果:

瞧,我们完成了!在这一点上,一个好的练习将是一个接一个地通过上面的步骤,看看你是否能不用看就重做它们。在下一节中,我将讨论一些提高数学思维技能的高级技巧。

一些分手小贴士

如果你以前从未处理过这类问题,那么当你第一次开始时,可能会感到困惑和沮丧。有时,这感觉像是一种天生的技能,根本无法学习。

然而,这不是真的——听听经历过的人是怎么说的。当我第一次学习这项技能时,下面是一些对我很有帮助的提示,我希望它们对你也有帮助。

  1. 翻阅一本离散数学教材。我袖手旁观我早先的论点,即数学思维不一定特定于数学的任何一个分支,甚至数学本身——它更多的是一种解决问题的范式或框架。也就是说,离散数学是一个很好的起点——它将教会你一些基本的数学概念和符号,以及一些你可以在任何地方应用的证明技术。这是数学入门课,学生们通过它来磨练自己的数学成熟。
  2. 模仿图案。如果你完全不知道如何处理一个问题,最好的学习方法之一是找到一个解决方案,深入理解它,并尝试在不看的情况下从头重写。这将使您接触到更多种类的技术,并识别表面上看起来不同,但需要相似的底层技术的问题。
  3. 练,练,练。迄今为止,培养数学思维的最大贡献者是真诚的决心和努力。你必须解决大量的问题并努力解决,就像其他技能一样。这是关于小片段的持续练习,而不是大块的不经常学习。

如果你应用这些技巧,你可以提高你的数学思维能力,并在成为更深入的数据科学家的道路上前进。祝你好运,下次再见!

想擅长 Python? 获取独家,免费获取我简单易懂的攻略 。想在介质上无限阅读故事?用我下面的推荐链接注册!

https://murtaza5152-ali.medium.com/?source=entity_driven_subscription-607fa603b7ce---------------------------------------

我叫穆尔塔扎·阿里,是华盛顿大学研究人机交互的博士生。我喜欢写关于教育、编程、生活以及偶尔的随想。

参考

[1]https://www . Reddit . com/r/Berkeley/comments/671 bxl/anant _ sahai _ learning _ Mathematical _ Mathematical _ maturity _ is/
【2】https://en . Wikipedia . org/wiki/Mathematical _ proof # Methods _ of _ proof _ proof
【3】https://SPH web . bumc . bu . edu/otlt/mph-modules/bs/bs 704 _ summaryzingd

混合整数线性规划导论:背包问题

原文:https://towardsdatascience.com/an-introduction-to-mixed-integer-linear-programming-the-knapsack-problem-1445452a9fe9

了解如何使用 scipy 和 pyomo 解决 Python 中的优化问题

照片由丹尼斯·莱昂Unsplash 上拍摄

背包问题可能是学习整数规划、最优化或运筹学时首先面临的问题之一。在这个问题中,从给定的一组物品中,人们必须选择最有价值的组合,以适合特定容量(重量、体积或两者)的背包。

在整篇文章中,我们将使用 scipy 实现背包问题的多维变体,使用 pyomoGLPK 解算器实现整数形式。学习如何使用这两个框架对将来解决更复杂的问题有很大帮助,背包问题是一个惊人的开始。

多维背包是经典问题的变体,其中考虑了一个以上的约束。它增加了复杂性,这有助于探索连续域和离散域结果之间的差异,如下一节所述。

感兴趣的人可以跟随本示例笔记本中的完整代码。

线性规划和松弛公式

当公式化优化问题时,必须定义一个目标,该目标是向量决策变量 x 的函数,并且可能受到一些等式和不等式约束,这些约束也是 x 的函数。该目标通常在最小化意义上定义,因此目标是在遵守约束的同时找到其最低值。最大化目标可以通过简单地将相应的最小化目标乘以-1 来表达。在公式中, x 的每个分量的上下边界可能是明确的,这减少了搜索空间。按照惯例,由于求解技术的原因,决策变量的下限通常默认等于零。

由于线性问题的目标和约束都是其决策变量的线性组合,因此该问题可以表述如下。

线性问题。(图片由作者提供)。

当定义一个宽松的公式时,意味着原始问题有一些整数决策变量,宽松的形式将这些变量转换成连续变量。这正是第一部分将要发生的事情。因此,让我们首先定义背包问题的要素。

决策变量:

  • x:C添加到背包中的各物品数量的列向量。在这个例子中,它将以[0,1]为界。

固定参数:

  • c : 与每个决策变量相关的成本(其值为负)。
  • 一个 _{ub}: 矩阵的不等式约束条件。
  • a _{1,i}: 物品单位重量 i
  • a _{2,i}: 项目单位体积 i
  • b _{ub}: 带有背包重量和体积容量的列向量。

让我们用 Python 创建这些元素。首先,使用字典,这将在后面的 pyomo 中有用。在这里,我使用一个固定的随机种子来获得相同的结果。

*# Import numpy
import numpy as np

# Set of items
I = set(range(1, 11))

# Random seed
np.random.seed(12)

# Weight associated with each item
w = dict(zip(I, np.random.normal(loc=5.0, scale=1.0, size=10).clip(0.5, 10.0)))

# Volume associated with each item
v = dict(zip(I, np.random.normal(loc=6.0, scale=2.0, size=10).clip(0.5, 10.0)))

# Price associated with each item
price = dict(zip(I, np.random.normal(loc=10.0, scale=1.0, size=10).clip(0.5, 20.0)))

# knapsack capacity
kw, kv = 21.0, 22.0*

然后在 numpy 样式中使用 scipy

*# Costs
c = -np.array(list(price.values()))

# Inequality constraints matrix
A_ub = np.array([
    np.array(list(w.values())),
    np.array(list(v.values()))
])

# Upper bounds for linear inequality constraints
b_ub = np.array([kw, kv])

# Bounds (one quantity of each item)
bounds = [(0, 1),] * 10*

现在我们已经拥有了使用来自 scipylinprog 来解决这个问题的所有必要元素。

*from scipy.optimize import linprog

# Obtain solution
sol = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds)

print(sol)*

它返回以下内容。

*con: array([], dtype=float64)
fun: -44.817244893700625
message: 'Optimization terminated successfully.'
nit: 6
slack: array([-1.68047194e-08, -2.36582025e-08])
status: 0
success: True
x: array([9.99999999e-01, 8.65571091e-01, 7.40355899e-10, 1.00000000e+00, 2.62434803e-10, 2.98795062e-09, 2.33299681e-10, 8.80512141e-01, 9.99999997e-01, 2.05849974e-10])*

注意 x 中的一些项目是分数,这在某些情况下是不可能的。因此,对于这些情况,我们必须找到一种方法,使用决策变量的整数值来找到最佳可行解。

从版本 1.9.0 开始, scipy 有了一个混合整数线性规划解算器。因此,我们可以通过将完整性关键字参数解析为 linprog 来将松弛背包问题转化为整数版本。全整数变量应该被赋予 1,而连续变量应该被赋予 0,其形状与决策变量向量相同。

*# Assign ones to integer variables
integrality_vector = np.full(c.shape[0], 1)

# Obtain solution
sol_int = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, integrality=integrality_vector)*

有些问题用矩阵形式表述更复杂,因为它们包含几组在大集合上定义的约束和决策变量。在这些情况下,数学建模语言有利于创建和转换求解器可以成功解释的代数表达式。在下一节中,我们将看到如何使用数学建模 Python 包 pyomo 将背包问题公式化。

整数背包问题

为了使用整数变量来表述这个问题,我们将使用 pyomo 来创建代数整数线性模型,这些模型可以由通常的代数优化解算器来解释。所以让我们从进口 pyomo 开始。

*import pyomo.environ as pyo*

pyomo 中有两种建模方法:抽象具体模型。在第一种方法中,问题的代数表达式是在提供一些数据值之前定义的,而在第二种方法中,模型实例是在定义其元素时立即创建的。你可以在库文档或者拜纳姆等人的书(2021)中找到更多关于这些方法的信息。贯穿本文,我们将采用具体的模型表述。

*model = pyo.ConcreteModel()*

在代数数学优化语句中,通常在集合上定义变量和/或表达式。在背包问题中,我们有与每个项目相关的决策变量和参数。因此,我们将创建一个项目集,每个项目都有一个相关的键。

*# Set of items (previously defined)
I = set(range(1, 11))

# As an attribute of the problem
model.I = pyo.Set(initialize=I)*

第二步,我们将定义这个问题的一些固定参数。下面列出了它们。

  • kw: 背包的重量容量。
  • kv: 背包的体积容量。
  • w _I:集合 I 中每个项目 i 关联的权重。
  • v _I:与集合 I 中每一项 i 关联的体积。
  • c _I:集合 I 中每一项 i 关联的成本(值)。

与背包相关联的参数是固定标量。因此,我们可以通过下面的代码实例化它们。

*# knapsack capacity
kw, kv = 21.0, 22.0

# Parameters of the knapsack
model.kw = pyo.Param(initialize=kw)
model.kv = pyo.Param(initialize=kv)*

*然而,必须为集合 I 中的每个元素定义与每个项目相关的参数。这可以通过将集合作为第一个参数在 pyo 中传递来实现。参数定义。如果定义的元素(变量、参数、表达式或约束)由多个集合索引,则可以在此语句中传递多个集合。这个定义是 Python args 风格的。

*# Parameters of the items
model.v = pyo.Param(model.I, initialize=v)
model.w = pyo.Param(model.I, initialize=w)
model.c = pyo.Param(model.I, initialize=price)*

记住 vwprice 是之前定义的 Python 字典,其中 key 是 I 中的元素。

现在让我们来定义这个问题的决策变量:对于集合 I 中的每个物品,添加到背包中的物品数量。请注意,我在[0,1]中定义了宽松公式的界限。因此,另一种说法可能是在 pyo 中定义这些变量。二进制

*model.x = pyo.Var(model.I, within=pyo.Integers, bounds=(0, 1))*

太好了!现在我们已经定义了决策变量和固定参数,我们可以定义问题的约束和目标。

这些约束可以用下面的等式来表示。

背包问题的约束。(图片由作者提供)。

Python 中的意思是。

*def weight_constraint(model):
    return sum(model.x[i] * model.w[i] for i in model.I) \
        <= model.kw

model.weight_constraint = pyo.Constraint(rule=weight_constraint)

def volume_constraint(model):
    return sum(model.x[i] * model.v[i] for i in model.I) \
        <= model.kv

model.volume_constraint = pyo.Constraint(rule=volume_constraint)*

注意,在创建与每个约束相关的函数时,模型是一个强制参数。当我们将它传递给函数时,我们可以引用之前定义的属性 xwvkwkvI

现在,让我们定义目标函数。由于 c 被定义为与每个物品相关联的正值,我们的目标将是最大化背包中运输的价值。

背包问题的目标函数。(图片由作者提供)。

Python 中是这样的。

*def obj_function(model):
    return sum(model.x[i] * model.c[i] for i in model.I)

model.objective = pyo.Objective(rule=obj_function, sense=pyo.maximize)*

为了解决这个问题,我们必须实例化一个求解器。在这个例子中,我将使用 GLPK ,它是开源的,因此任何用户都可以下载并执行。在“您的 _ 路径 _ 到 _GLPK”字段中,添加到glpsol.exe文件的路径。例如,我的路径是“C:\glpk-4.65\w64\glpsol.exe”。

最新可用的 GLPK 版本可在这里找到,Windows 可执行文件可在这里找到。

*opt = pyo.SolverFactory('glpk', executable=YOUR_PATH_TO_GLPK)

# You can add a time limit by using the following command line
opt.options['tmlim'] = 120

solution = opt.solve(model)*

现在,我们可以使用显示方法来检查模型的元素。对于目标函数,请参见下面的代码。

*model.objective.display()*

它应该返回以下输出。

*objective : Size=1, Index=None, Active=True
Key  : Active : Value
None :   True : 39.88187183116921*

因此,人们可能会注意到我们的整数解(39.88)比使用松弛公式(44.82)得到的结果更差。这发生在整数问题中,因为与松弛问题中的相应空间相比,决策空间必然减少。

通过研究决策变量的差异,人们可能会注意到:

  • 在两种情况下都添加了项目 1。
  • 仅在放松问题中部分添加了第 2 项—值 0.87。
  • 仅在整数题中增加了第 3 项。
  • 在两种情况下都添加了第 4 项。
  • 第 5 项在任何情况下都没有添加。
  • 第 6 项在任何情况下都没有添加。
  • 第 7 项在任何情况下都没有添加。
  • 第 8 项在松弛问题中被部分添加—值 0.88 —但在整数版本中被完全添加。
  • 第 9 项仅在放松问题中完全添加。
  • 在任何情况下都没有添加项目 10。

进一步阅读

线性规划的更多细节感兴趣的可以参考 luen Berger&Ye(2008);整数规划见 Wolsey(2020);对于运筹学,温斯顿&戈德堡(2004)。

只有一个约束和二元变量的整数规划问题通常被称为背包问题* (Winston & Goldberg,2004)。这些问题可以通过动态编程有效地解决,这可能是另一篇文章的重点。*

在另一个示例笔记本中可以找到带有多个背包和一些附加规则的背包问题的扩展公式。

分枝定界算法是整数和混合整数问题求解中最常用的算法。对其机制的介绍感兴趣的人可以参考我的另一篇文章:

* *

结论

在本文中,我们看到了混合整数线性规划的介绍。使用 scipy 获得多维背包问题的松弛形式的解,使用 pyomoGLPK 解算器获得整数形式的解。这些示例中使用的完整代码可供进一步使用。

参考

拜纳姆,马丁等人,2021。pyo mo-python 中的优化建模。斯普林格。

卢恩伯格,D. G .和叶,2008 年。线性和非线性规划。第三版。斯坦福:斯普林格。**

温斯顿,W. L .和戈德堡,J. B .,2004 年。运筹学:应用与算法。4 版。加州贝尔蒙特:汤姆森布鲁克斯/科尔贝尔蒙特。

2020 年洛杉矶沃尔西。整数编程。第二版。约翰·威利的儿子们。

命名实体识别简介

原文:https://towardsdatascience.com/an-introduction-to-named-entity-recognition-3cd48fbcd1fc

为什么你应该知道命名实体识别

泽克瑞亚·森在 Unsplash 上的照片

命名实体识别是自然语言处理中的一个用例,其中模型学习标记属于特定组的某些单词。

什么是命名实体识别?

当我们人类试图理解一个句子时,我们会很快识别出属于特定类别的单个单词,例如位置、时间或识别一个人的单词。命名实体识别指的就是这种模型,它标记句子或段落中的特定单词,并将它们分配到正确的类别。

这些信息对于正确理解句子的内容是必不可少的,应该被正确识别。词和句子成分的分类是在不同的阶段发现的。

NER 的挑战是什么?

自然语言处理的问题是,我们都是从小就能流利地使用一种自然语言,并且不用思考就能理解它。这使得我们在文本中如何识别实体变得更加困难。对于该模型,这涉及到克服一些对我们来说不言而喻的挑战:

  • 识别变体:姓名、地名或公司名称可以出现在不同的变体中。一个人既可以用全名来称呼,也可以只用姓来称呼。该模型必须认识到,这两个时间可能指的是同一个人。这同样适用于“纽约”、“NYC”和“纽约市”的称谓,它们都是指同一个美国主要城市。
  • 规范化:时间或金钱引用可以以不同的格式出现,但仍然表示相同的东西。一个 NER 模型还必须了解这些差异,例如,要理解“€10.000”和“€10,000”是同一个意思,只有在英语中,逗号才用于分隔千位。
  • 实体的定界:最后,实体之间的定界也必须被识别。一个实体可能只由一个单词组成,而另一个实体在大多数情况下有四个单词。

命名实体识别使用什么级别?

如果我们想要训练命名实体识别,我们需要足够的训练数据来馈入模型。要自动获得这些信息,而不必手动对实体进行分类,我们可以使用以下步骤:

  1. 名词识别:我们的命名实体必须是名词,所以我们过滤给定的文本,只保留名词。在许多语言中已经有了这方面的训练模型,例如词性标注。
  2. 单词分类:我们把名词过滤掉之后,就可以把它们归类到我们想要的类别中。为此,可以使用各种免费数据库来尽可能自动化这一步骤。例如,我们可以通过 API 查询谷歌地图数据库,对位置信息进行分类。

NER 是如何工作的?

在 Python 模块 Spacy 和 NLTK 中,您可以轻松地加载经过训练的命名实体识别模型,这些模型已经可以很好地用于标准语言。然而,您可能还需要训练您自己的 NER 模型,以便更好地为您自己的用例进行调整。

在开始实际训练之前,我们需要一个训练数据集,其中包含足够多的文本示例和文本中的实体。如果您想在特殊情况下训练模型,通常无法创建数据集本身并手动命名单词或短语。

随后,所谓的条件随机场(CRF)可以被训练用于命名实体识别。它是一种统计模型,特别适合于模式的识别,并且在预测中还包括上下文信息。

简单来说,条件随机场为单个序列训练逻辑回归。以下值用作输入变量:

  • 输入向量集
  • 当前要预测的单词的位置
  • 前一个单词的标签
  • 当前单词的标签

例如,这可以用来学习动词经常跟在名词后面,以及学习关于可能标签的结论。

你用 NER 做什么?

命名实体识别可用于许多领域。因此,以下示例只是可能使用案例的摘录:

  • 人力资源:特殊的模型可以用来从申请人的简历中快速提取信息。
  • 搜索算法:例如,在产品搜索中,可以以差异化的方式识别和搜索产品特征和产品名称。例如,通过在产品标题中的搜索词“iPhone 12”中搜索数字 12,而不是将其作为产品属性进行搜索,这可以改善搜索结果。
  • 客服:客户的询问也可以通过识别关键词更好的分类筛选。这减少了员工的响应时间。

这是你应该带走的东西

  • 命名实体识别模型学习将单个单词或序列分配给一个组。
  • 为此,训练所谓的条件随机场,其根据序列执行分类。
  • 一个好的 NER 模型的特点是对变量的识别和对实体的良好划分。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

* *

组织网络分析导论

原文:https://towardsdatascience.com/an-introduction-to-organizational-network-analysis-cfa91a0e2fda

使用图论通过发现影响者和关键人物来提高员工参与度和沟通

阿德里安·匡威Unsplash 上的照片

在过去的几个月里,我一直在研究旨在帮助人力资源领域日常工作的数据科学解决方案。为什么?好吧,让我们说这是一个真正有趣的领域,在我看来,没有得到足够的重视。不管是什么组织,一个公司未来的成功很大程度上依赖于他们现在和未来的员工,那么为什么不关注他们呢?在这方面,我发现大多数标准解决方案都围绕以下想法:

  • CV 解析器;
  • 用于招聘的聊天机器人;
  • 员工流失模型;
  • 培训课程的推荐系统。

如你所见,除了流失模型,这些解决方案无法为公司提供战略价值,也无法为员工带来真正的利益。但是,那时能做什么呢?好吧,另一套没有被过多讨论的相关解决方案属于组织网络分析的主题。在这篇介绍中,我们将通过 Python 中的一些模拟例子来介绍它背后的关键概念,以使事情更容易理解。那么,什么是组织网络分析?为了能够回答这个问题,我们首先需要理解组织网络的概念。

a)网络:了解组织的工具

正如 Barabási (2013)所提到的,组织网络是由组织成员定期相互互动形成的社会网络。根据 Tichy,n .,Fombrun,C. (1979),该网络的特征在于根据社会对象(人或群体)来表示社会交互,其中由于缺乏交互,不一定每个对象都连接到整个网络,并且它们中的一些可以共享不止一个连接。此外,通过这些互动,网络的参与者通过分享信息、情感、影响和商品或服务建立联系。换句话说,我们有一个员工网络(节点),这些员工通过他们的互动(边)联系在一起。这种组织的功能性表示有助于理解一些工作动态,否则这些动态会被隐藏起来。

从这个角度来看,刘,j .,莫斯科维纳 a .,M. (2017)将组织网络分析 (ONA)正式定义为通过使用取自图论的技术对组织网络的系统探索。其目的是更好地理解管理结构、人际关系和组织内部的信息流动。在这方面,这种分析的范围可以非常广泛:从对检测到的影响者、非正式领导、辞职人员(退出网络)和高度/不积极参与的员工的特征的描述性分析,到旨在通过对这些类型的内部行为进行更稳健的解释来提供实际价值的预测模型。但是,请记住,与任何数据科学项目一样,在提供任何这些解决方案之前,都需要考虑组织的数据成熟度(您应该从收集和处理具有明确目标的数据开始,然后提供分析,最后转向预测性和规范性模型)。

现在你可能认为所有这些听起来不错,但为了进行任何类型的分析,你首先需要掌握组织网络,这可能相当棘手。要做到这一点,可以遵循主要方法,即进行调查,要求所有员工提名其他同事(来自组织的任何领域)作为其工作生活不同方面的参考。这些调查可以在网上进行,也可以通过任何流行的资源管理工具进行,比如 Workday。有些问题可能是:

  • 当你想知道公司未来的重要信息时,你会找谁?(在 Barabási 的网络科学书中有一个很好的详细例子)。
  • 给你情感支持的人是谁?
  • 当你需要解决技术问题(例如,使用软件 A)时,你依赖谁?
  • 谁是最能激励你的人?
  • 你一天中经常互动最多的人是谁?

正如你所看到的,网络的正确表现取决于员工的参与率和诚实的回答,他们知道他们的答案不是匿名的。在这种情况下,人力资源部门在整个过程中发挥积极作用至关重要(作为一项建议,他们可以强制进行调查,并努力宣传其好处(稍后会提到),以获得更好的结果)。

完成数据收集步骤后,我们可以构建有向图(网络),其中的节点是雇员,他们的有向链接代表一个到另一个的任命。作为一个模拟的例子,我将在后面解释,对于一个有 40 个成员的组织,这看起来像这样:

作者图片

在此图中,颜色代表节点的一些分组属性,这有助于我们对它们进行分类(在本例中,它指的是每个员工被分配“工作”的区域)。请注意,箭头指向提名的方向,在没有收到或发出提名的情况下,我们有孤立的节点。

一旦我们完成了网络的构建,是时候寻找组织的影响者和关键角色了。但是,到底什么是影响者或关键人物呢?我们怎么知道?这并不是一成不变的,因为有几种方式可以让某人落入这些类别中的任何一个,而且它们比仅仅考虑最多提名的人“稍微”复杂一些。例如,如果我们考虑上面列出的一系列问题,至少有 4 种有影响力的人。其中一些可能是组织信息流的关键,而另一些可能是必要的激励因素、情感支持或技术参考。此外,我们可以找到帮助其他人接触这些有影响力的人的成员(这反过来也使他们有影响力)。然而,至少我们可以说它们都有一个共同的特点:在网络内部某种程度或类型的中心性。在这里,我们需要引入一些来自网络科学的单独指标,这些指标对于这种分析是必不可少的。为了简单起见,我们将只提及其中一些想法背后的主要思想:

  • : 一个节点的度定义为其它节点指向这个节点的链接数。当网络有向时,有必要区分指向一个节点的链路(入度)和从一个节点指向其他节点的链路(出度)。以这篇文章为例,你可以把它想成提名你的人数与你提名的人数之比。
  • 接近中心性 :这个度量(在 0 和 1 之间)测量网络中一个节点到其余节点的平均距离(根据到达它们所需的步数)。这使我们能够对与网络其余部分的平均接近度有一个概念。从这个角度来看,您可以将影响者或关键人物确定为其亲密度指数高于某个阈值的人(或人群)。
  • :这个度量(在 0 和 1 之间)可以看作是一种量化一个节点对网络信息流的影响的方式。它主要用于查找充当节点组之间桥梁的节点。它是通过计算所有节点对之间的最短路径来计算的,这允许测量一个节点位于其他节点的最短路径之间的频率。
  • 枢纽和权威评分 : 这些评分(0 到 1 之间)帮助我们将节点分为三种类型:常规、枢纽和权威。它们的特征可以总结如下:一些节点(hub)有许多链接指向一些其他的(authorities ),这些链接不经常链接到其余的节点(regular)。这种行为的一个最常见的例子发生在论文引用中,其中主要知识集中在基础论文(权威论文)中,而基础论文又被一系列论文(枢纽论文)引用,这些论文被其他论文(常规论文)更频繁地引用。在组织网络的情况下,当局可以被视为主要与枢纽交流的领导人,枢纽是当局和“正式”雇员之间的桥梁。因此,我们可以说,中心和当局都可以被视为专业网络中的关键行为者。

如果我们要为一个网络(如上图所示)计算这些指标,我们将获得一个类似于下表的表格:

作者图片

接下来,剩下的就是决定一些规则和阈值,以定义我们将考虑作为影响者的指示的行为类型。一种过于简单的方法是,采用六个指标中每个指标的前 5 分,并为每个指标定义一个不同的参与者类别。例如,那些在中拥有最高学位的人可以被称为“寻求成员”,拥有学位的顶级员工可以被称为“积极成员”,那些拥有最高亲密度中间度分数的人可以分别被称为“中心成员”和“连接成员”,最后我们有了网络的“中枢”和“权威”。注意,这些是我刚刚想到的一些随机的名字,你可以自由选择你喜欢的名字。**

到目前为止,除了检测到的影响者之外,我们还有一个“非正式”组织的近似值,每个员工有一组网络统计数据,这本身就很有价值。问题是,你能用这些信息做什么?正如本文标题中提到的,你可以尝试提高员工的参与度和沟通。怎么会?我既不是社会学家,也不是心理学家,所以我参考了已发表的论文,并与该领域的专业人士进行了非正式交谈,为你提供了以下总结观点:

  • 您可以通过关键节点传播信息来改善信息流,这有助于提高组织的透明度,从而提高员工的参与度;
  • 你可以通过将新员工与一些关键节点配对来改善他们的入职情况;
  • 需要选择变革领导者的变革管理流程可以利用这种方法检测到的领导者。同样,在需要跟踪新工具实现前后成员交互的变化的情况下,它也很有用;
  • 在合并和收购过程中,可以调整整个方法,以找到知识专家和领导人,帮助减轻这一紧张过程的负担,同时保持业务尽可能平稳运行;
  • 注意孤立的或很少被提名的节点可能是一个好主意,因为与组织联系不良可能表明糟糕的工作生活体验、低工作满意度和敬业度(Kahn,W. A .,1990 年)以及有缺陷的沟通系统。如果你综合所有这些因素,你可能会面对一个更容易辞职的成员;
  • 最后,也是更有趣的是,你可以超越组织网络分析的描述性统计(而不是 ML 或 AI ) ,开始考虑实现预测模型,试图使用网络提供的额外信息作为特征来回答一些常见的问题。例如,如果有足够的时间,你可以用通过几次调查获得的网络数据来补充你的客户流失模型。

在结束对组织网络分析主题的一般性介绍之前,需要强调的是,这种方法并不严格局限于专业组织,因为它也可以适用于教育机构(针对员工和校友)或任何其他可以创建人际网络的环境。

接下来,对于那些对“幕后”感兴趣的人,我将描述如何通过一个模拟场景来建立一个基本的网络,该场景假设您能够进行一项调查,该调查只是要求您提名与您互动最多的组织成员。

b)模拟网络

我们从导入将要使用的库开始:

*# General
import itertools as it
import numpy as np
import pandas as pd# Network
import networkx as nx*

在继续创建网络之前,我们首先需要考虑将使用哪些设计参数。因为目的是创建一个虚构的组织,在那里可以找到不同类型的“有影响力”的人,所以我们需要指定:

  • 员工人数;
  • 员工将被分配到的区域的数量和名称;
  • 区域的大小,以了解每个区域将有多少员工“工作”;
  • 在每个领域中能够有较高概率提名/被他人提名的人的比例;
  • 高关系人提名/被提名的概率;
  • 不属于高度关联类别的员工被提名/被提名的最大概率;
  • 区域 A 的成员提名区域 B 的成员的概率,因为一些区域比其他区域更有可能互动。

鉴于此,我们现在指定将定义网络的全局参数:

*# GLOBAL PARAMETERS
# Set a seed to replicate the experiment
np.random.seed(1)# Specify the number of employees in the network
n_employees = 40# Specify the areas
m_areas = ['HR', 'IT', 'Finance', 'Marketing', 'Sales']# Specify the area sizes as percentage of total
area_sizes = [0.5/5, 2/5, 1/5, 0.5/5, 1/5]# Set the percentage of highly connected people per area
influencer_ratio=0.1# Give a higher probability of connection to a type of employee
influencer_link_probability=0.85# Give an upper bound to the link probability of a non "influencer" employee
max_non_influencer_link_probability=0.6# Create all pairs of areas to define their interaction probability
areas_links_combinations = list(it.combinations(m_areas,2))# Specify the probability of each pair
areas_links_probability = [0.1, 0.2, 0.3, 0.4,
                           0.4, 0.2, 0.7, 0.7,
                           0.6, 0.9]*

接下来,我们创建员工,并将他们分配到一个区域:

*# Generate a list of employees
employees = [f'Employee_{n}' for n in range(n_employees)]# Generate an array of employee's areas
# using the area sizes as assignment probabilitiesemployees_areas=np.random.choice(a=m_areas,
                                 size=n_employees,
                                 replace=True,
                                 p=area_sizes)# Join the data into a pandas DataFrame
df=pd.DataFrame({'Employee':employees,'Area':employees_areas})*

一旦我们有了员工和区域的数据框架,我们就可以定义每个人的提名概率:

*# For low link probability people a random uniform distribution
# is usedlower_proba_assignment=lambda x : np.random.uniform(
non_influencer_link_probabilities[0],
non_influencer_link_probabilities[1])# Assign the link probabilities per area and combine the data
dfs=[]
for area in m_areas: df_area=df[df['Area']==area].copy() area_employees = df_area.shape[0] n_influential = round(area_employees*influencer_ratio) influ_employees=df_area['Employee'].sample(n_influential).values df_area['Link_Probability']=np.where(
                          df_area['Employee'].isin(influ_employees),
                          influencer_link_probability,
                          df_area['Employee'].apply(
                                            lower_proba_assignment))
  dfs.append(df_area)df=pd.concat(dfs,ignore_index=True)*

我们的数据帧看起来像这样:

作者图片

既然我们已经有了提名/被提名的节点和个体概率,我们可以继续创建它们的链接。为此,我们将首先生成所有可能的链接组合(不包括他们自己命名的情况)。

*# Create the links DataFrame
possible_links=list(it.product(df['Employee'],df['Employee']))
df_links=pd.DataFrame(possible_links, columns=['From','To'])
df_links=df_links[df_links['From']!=df_links['To']].copy()*

在获得所有对之后,我们有以下数据结构:

作者图片

我们现在需要做的是,根据每个员工所属的领域(有些领域更有可能互动),以及他们被分配的提名概率(有些人比其他人更容易被提名/提名),来定义我们将保留哪些链接。为此,我们需要将数据框架与雇员合并,并将概率与链接数据框架联系起来:

*# Merge links DataFrame with employee data based on the 'From' side
df_links=pd.merge(df_links,
                  df[['Employee','Area','Link_Probability']],
                  left_on='From',
                  right_on='Employee',
                  how='left')# Drop the employee column as we'll need to do another merge
df_links.drop(['Employee'],axis=1, inplace=True)# Rename columns to know to what employee the data belongs to
df_links.rename(columns={'Area':'From_Area',
                        'Link_Probability':'From_Link_Probability'},
                        inplace=True)# Merge links DataFrame with employee data based on the 'To' side
df_links=pd.merge(df_links,
                  df[['Employee','Area','Link_Probability']],
                  left_on='To',
                  right_on='Employee',
                  how='left')# Rename the columns
df_links.rename(columns={'Area':'To_Area',
                         'Link_Probability':'To_Link_Probability'},
                          inplace=True)# Drop the employee column as we don't need it anymore
df_links.drop(['Employee'],axis=1, inplace=True)df_links.rename(columns={'Area':'To_Area',
                         'Link_Probability':'To_Link_Probability'},
                         inplace=True)*

在前面的步骤中,我们加入了个人链接概率,但我们仍然需要添加不同地区成员之间的提名概率。特别是,由于有两种方式来显示一对区域(A,B)和(B,A ),我们需要反映关联的概率:

*# Start by creating a DataFrame with the first way
external_proba_a=pd.DataFrame(areas_links_combinations,
                              columns=['From_Area','To_Area'])external_proba_a['Area_Pair_Probability']=areas_links_probability# Mirror the previous transformation but renaming the columns
external_proba_b=external_proba_a.copy()external_proba_b.rename(columns={'From_Area':'To_Area',
                                 'To_Area':'From_Area'},
                                 inplace=True)# Concatenate both DataFrames
external_proba=pd.concat([external_proba_a,
                          external_proba_b],
                         ignore_index=True)# Link the DataFrame of links with the "external" probabilities
df_links=pd.merge(df_links,
                  external_proba,
                  on=['From_Area','To_Area'],
                  how='left')# In the cases where the area is the same
# we'll keep a probability close to 1 (0.9)
# meaning the nomination depends mainly on the individual
# probabilitydf_links['Area_Pair_Probability']=np.where(
  df_links['From_Area']==df_links['To_Area'],
  0.9, df_links['Area_Pair_Probability'])# For the majority of the cases, the final pair probability
# will equal the average between individual probabilities
# times the area probabilitydf_links['Pair_Probability']=df_links['Area_Pair_Probability']*  (df_links['From_Link_Probability']+
 df_links['To_Link_Probability'])/2# In case an employee has the highest link probability
# we'll make him more prone to receive nominations than to make
# nominations by halving the pair probability df_links['Pair_Probability']=np.where(
df_links['From_Link_Probability']==influencer_link_probability,
df_links['To_Link_Probability'],df_links['Pair_Probability']/2)df_links['Pair_Probability']=np.where(
df_links['To_Link_Probability']==influencer_link_probability, df_links['From_Link_Probability'],
df_links['Pair_Probability']/2) # Finally, use the Pair_Probability to flip a coin
# and decide if the pair will be considered (Active_Link=1) or notdf_links['Active_Link']=df_links.apply(lambda x: np.random.binomial(n=1,p=x['Pair_Probability']),axis=1)*

由于这一步,我们能够定义我们的节点和链接:

*nodes = employees# We create a list of tuples of pairs of nodes to define the links
links = df_links[df_links['Active_Link']==1][
                 ['From','To']].to_records(index=False)*

现在,我们已经准备好构建网络并计算指标:

*# Create an instance of a Directed Graph using networkx
G = nx.DiGraph()# Add the nodes and links to the graph
G.add_nodes_from(nodes)
G.add_edges_from(links)# Calculate all the metrics mentioned in the previous section:# DegreeOut
degree_df_out=pd.DataFrame(G.out_degree,
                           columns=[“Employee”,”DegreeOut”])# DegreeIn
degree_df_in=pd.DataFrame(G.in_degree,
                          columns=[“Employee”,”DegreeIn”])# Closeness
closeness_df=pd.DataFrame.from_dict(nx.closeness_centrality(G),
                                    orient=”index”).reset_index()closeness_df.columns=[“Employee”,”Closeness”]# Betweenness
betweenness_df=pd.DataFrame.from_dict(nx.betweenness_centrality(G),orient=”index”).reset_index()betweenness_df.columns=[“Employee”,”Betweenness”]# Hubs and Authorities
hubs, authorities = nx.hits(G, normalized=True)hubs_df=pd.DataFrame.from_dict(hubs, orient="index").reset_index()
hubs_df.columns=["Employee","HubScore"]authorities_df=pd.DataFrame.from_dict(authorities,
                                      orient="index").reset_index()authorities_df.columns=["Employee","AuthorityScore"]# Merge the results
results=pd.merge(df, degree_df_in, on="Employee",how='left')
results=pd.merge(results, degree_df_out, on="Employee",how='left')
results=pd.merge(results, closeness_df, on="Employee",how='left')
results=pd.merge(results, betweenness_df, on="Employee",how='left')
results=pd.merge(results, hubs_df, on="Employee",how='left')
results=pd.merge(results, authorities_df, on="Employee",how='left')results=results.sort_values(by='Closeness', ascending=False)*

通过这最后一步,获得了如本文第一部分所示的表格。正如您所看到的,修改代码以使用真实数据是相当简单的(您只需要在根据提名定义节点和链接之后运行最后一个块)。

结束语

在整篇文章中,我们讨论了组织网络分析背后的主要思想,同时提出了一些用例,并提供了在您的组织/机构中开始一个简单项目所必需的工具。从这个意义上说,我鼓励你尝试运行你自己的模拟场景,如果可能的话,采取下一步措施,开发基于网络指标的预测模型。从我的经验来看,这些指标确实为预测模型增加了有价值的信息(至少对客户流失和参与度是如此)。

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

参考

蒂希,n .丰布伦,C. 1979。组织环境中的网络分析。人伦,32(11),923–965。

巴拉巴希,2013 年。网络科学。英国皇家学会哲学汇刊 A:数学、物理和工程科学,371(1987),20120375。

刘,2017,m。使用多重人际关系的组织网络分析决策支持系统。多智能体和复杂系统。斯普林格,33–47 岁。

w . a . Kahn,1990。工作中个人参与和脱离的心理条件。管理学院学报,33(4),692–724。

卡兰格斯,e .,约翰斯顿,k .,比森,a .,林,I. 2015。内部沟通对员工敬业度的影响:一项初步研究。公关评论,41。

Matplotlib 用户的 Plotly 介绍

原文:https://towardsdatascience.com/an-introduction-to-plotly-for-matplotlib-users-9f4f0d2113bc

Python 科学绘图

使用类似于 matplotlib 的自定义功能,轻松地将交互性构建到您的绘图中

KOBU 机构Unsplash 上拍摄的照片

我在这个平台上写的第一批文章是一系列关于matplotlib的教程,这是一个令人难以置信的 Python 可视化库,我在研究生院广泛使用,可以无限定制以创建美丽的图形。作为一名数据可视化爱好者,我一直在寻找更好的方法来创建图形和信息图,随着时间的推移,我越来越多地遇到了plotlyplotly最吸引人的特点是交互性是最重要的——虽然与matplotlib的交互性肯定是可能的(事实上,我有另一篇关于这个主题的文章),来自plotly的数字可以直接导出到 HTML 和 Javascript,web 浏览器的自然语言,使我们的交互式可视化易于与他人共享。

这篇文章的目的是从一个重度matplotlib用户的心态出发,给出在plotly中开始探索可视化的工具。希望在本教程结束时,你已经可以制作漂亮的交互式图表了!

安装 Plotly

首先也是最重要的,我们需要确保我们已经安装了plotly——我们可以用pip非常简单地做到这一点:

pip install plotly

Plotly Express 与 Plotly Graph 对象

plotly中有几个开发图的接口——最简单的是使用plotly.express。使用plotly.express的好处是你可以用最少的代码行从一个数据帧到一个图表。一个例子如下:

**# Import plotly express** import plotly.express as px
import pandas as pd**# Import iris dataset**
iris = pd.read_csv("[https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv](https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv)")**# Plot with plotly.express**
fig = px.scatter(iris, x="sepal_length", y="sepal_width", color="species", height=600, width=600)
fig.show()

plotly express 的输出—由作者生成的图形

正如我们所看到的,px.scatter()可以直接接收一个数据帧,如果需要,您可以使用color参数来选择如何分割数据(在这种情况下,我们为species的每个唯一值指定不同的散点图颜色)以立即生成多系列散点图。我们只需要使用一行代码就可以做到这一点!

另一方面,Plotly 图形对象要求您一条一条地构建图形。不过对我来说,来自matplotlib,这感觉更自然,所以我个人喜欢用 **graph_objects** API 来生成我的 **plotly** 人物。

plotly中的主要构建模块与matplotlib中的非常相似,是一个Figure()。我们从创造我们的形象开始:

**# Import plotly graph objects** import plotly.graph_objects as go**# Create figure**
fig = go.Figure()

现在,我们可以为数据框架中的每个物种添加踪迹。此外,我们可以使用fig.update_layout()固定高度和宽度(我们将在后面更详细地讨论):

**# Add traces** for species, group in iris.groupby("species"):
    fig.add_trace(
        go.Scatter(
            x=group["sepal_length"],
            y=group["sepal_width"],
            name=species,
            mode="markers"
        )
    )fig.update_layout(height=600, width=600)
fig.show()

plotly 图形对象的输出—由作者生成的图形

您会注意到的第一件事是没有轴标签或图例标题——当我们使用plotly.graph_objects时,因为我们没有为xy传递列名,所以没有列名用作轴标签的默认值(同样也没有列名用作图例标题)。虽然这会导致代码稍微多一点,但我认为将轴标签保留为sepal_lengthsepal_width看起来也不太好,尤其是如果这个图要出版的话。因此,无论如何您都必须进行更新,所以让我们现在就做:

fig.update_layout(
    xaxis_title_text="Sepal Length",
    yaxis_title_text="Sepal Width",
    legend_title_text="Species"  
)

您会注意到的另一件事是,悬停文本在plotly.expressplotly.graph_objects的结果之间是不同的,如下所示。后者只有每个分散点的(x,y)坐标,而前者有特征的标签。这是因为我们在调用plotly.express时传递了列名。

默认悬停文本的差异—由作者制作的图形

再一次,我认为默认的悬停文本看起来仍然不太好,所以无论如何我都会重新做,所以我不介意在我们的plotly.graph_objects实现中添加一些额外的代码来做得更好。我们可以使用 HTML 来定义悬停工具提示的格式——我将把物种名称加粗,然后在下面的单独行中显示萼片长度和萼片宽度。要访问数据点值,我们可以使用%{x}%{y}(因为我使用的是 f 字符串,所以我需要通过将它们加倍来避开花括号)。最后,为了删除对比色中包含物种名称的额外文本(上图中的versicolor,我们使用 HTML <extra>标签并将其留空。

**# Add traces** for species, group in iris.groupby("species"):
    fig.add_trace(
        go.Scatter(
            x=group["sepal_length"],
            y=group["sepal_width"],
            name=species.title(),
            mode="markers",
            hovertemplate=f"<b>{species.title()}</b><br>Sepal Length: %{{x}}<br>Sepal Width: %{{y}}<extra></extra>"
        )
    )

格式化的最终 plottly plot—作者制作的图形

我们走吧!轴标签格式正确,我们现在有了一个图例标题,我们的悬停工具提示看起来比默认的plotly.express要好很多。在我看来,额外的几行代码是一种非常值得的权衡。

剧情定制

plotly生成的默认图形看起来已经很不错了,但是我们也可以像在matplotlib中一样对它们进行高度定制。与我们如何使用plt.rcParams["key"] = value定制matplotlib图形类似,在plotly中,我们将使用以下内容:

fig.update_layout(
    key1=val1,
    key2=val2,
    keyn=valn
)

首先,让我们从绘图中删除所有默认样式,这可以通过在创建 figure 对象时将layout_template设置为None来实现。

fig = go.Figure(layout_template=None)
fig.update_layout(height=600, width=600)
fig.show()

没有布局模板的无样式打印图形-由作者制作的图形

我们现在有了一个空白画布,可以在上面添加我们自己的样式定制。让我们首先使用 Python 中的 Yahoo Finance 包加载一些财务数据,然后在我们的非样式轴上绘制这些数据。注意— go.Scatter()通过使用mode关键字参数用于散点图和折线图:

**# Import packages** import yfinance as yf**# Load stock data** aapl = yf.Ticker("AAPL").history(period="3mo")
aapl["ticker"] = "AAPL"msft = yf.Ticker("MSFT").history(period="3mo")
msft["ticker"] = "MSFT"nvda = yf.Ticker("NVDA").history(period="3mo")
nvda["ticker"] = "NVDA"df = aapl.append(msft).append(nvda).reset_index()**# Plot data** fig = go.Figure(layout_template=None)
for ticker, group in df.groupby("ticker"):
    fig.add_trace(
        go.Scatter(
            x=group["Date"],
            y=group["Close"],
            name=ticker,
            mode="lines",
            hovertemplate=f"{ticker}<br>%{{x}}<br>$%{{y}}<extra></extra>"
        )
    )fig.update_layout(height=600, width=600)
fig.show()

未定型的财务情节——作者制作的图表

现在,让我们进行一些定制——在本文中,我将创建一个以学术为主题的情节风格,并从《金融时报》的一些情节中获得灵感。

学院派剧情

你在学术论文中发现的一个典型特征是,轴线出现在所有四个面上。做这个的相关布局参数是showlinemirror,我们可以分别用linewidthlinecolor调整线宽和颜色。注意,这些都是由 JSON 对象表示的,因此您有两个选项来输入布局参数:嵌套字典或由下划线分隔的键。例如,如果我们想在 x 轴上显示这条线,我们可以执行以下任一操作(我更喜欢后者):

fig.update_layout(
    {"xaxis": {"showline": True}}
)fig.update_layout(
    xaxis_showline=True
)

现在,让我们围绕我们的图创建一个方框——ticksmirror选项值将轴线和刻度镜像到图的另一侧:

fig.update_layout(
    xaxis_showline=True,
    xaxis_mirror="ticks",
    xaxis_linewidth=2,
    yaxis_showline=True,
    yaxis_mirror="ticks",
    yaxis_linewidth=2
)

地块周围的方框—作者制作的图形

从上面的图中,我们仍然缺少记号,缺省情况下不显示。我们可以使用ticks键选择将刻度置于图的内部或外部,并分别使用tickwidthticklen键设置刻度的宽度和长度。

fig.update_layout(
    xaxis_ticks="inside",
    xaxis_ticklen=8,
    xaxis_tickwidth=2,
    yaxis_ticks="inside",
    yaxis_ticklen=8,
    yaxis_tickwidth=2
)

作者制作的图

开始看起来更像我们在研究论文中看到的一般学术风格的情节!我们要做的最后一些修饰性的改变是移除网格,添加轴标签,改变字体和字体大小。

fig.update_layout(
    font_family="Avenir",
    hoverlabel_font_family="Avenir",
    xaxis_title_text="Date",
    xaxis_title_font_size=18,
    xaxis_tickfont_size=16,
    xaxis_showgrid=False,
    yaxis_title_text="Closing Price ($)",
    yaxis_title_font_size=18,
    yaxis_tickfont_size=16,
    yaxis_showgrid=False
)

最终学术风格图—作者制作的图形

看起来棒极了!最后一项簿记工作——注意我们如何在update_layout()的关键输入中反复使用xaxisyaxis?这是我会考虑使用嵌套字典的时候之一。我们可以更有效地写出所有的布局更改,如下所示:

fig.update_layout(
    font_family="Avenir",
    hoverlabel_font_family="Avenir",
    xaxis=dict(
        showline=True,
        mirror="ticks",
        linewidth=2,
        ticks="inside",
        ticklen=8,
        tickwidth=2,
        title_text="Date",
        title_font_size=18,
        tickfont_size=16,
        showgrid=False
    ),
    yaxis=dict(
        showline=True,
        mirror="ticks",
        linewidth=2,
        ticks="inside",
        ticklen=8,
        tickwidth=2,
        title_text="Closing Price ($)",
        title_font_size=18,
        tickfont_size=16,
        showgrid=False
    ),
    height=600,
    width=600
)

金融时报启发的情节

我们现在可以做一个与上面类似的练习,制作一个类似于金融时报的图表。从刚才检查的情节来看,下面是我们需要做的一系列事情:

  1. 更改网格、脊线、绘图背景和文本的颜色
  2. 在底部添加 x 轴脊椎
  3. 把字体改成类似的
  4. 为线条的终点添加标记,并为每个股票代码添加文本注释,而不是图例

让我们逐一实现这些步骤。我们将从完全未定型的身材开始。

改变颜色

使用谷歌 Chrome 众多颜色选择器扩展中的一个,我能够从图中获得重要的颜色,它们如下:

网格颜色: #E6D86D

X 轴书脊颜色: #66605B

剧情背景颜色: #FFF0E4

文本颜色: #66605B

fig.update_layout(
    xaxis=dict(
        zeroline=False,
        gridcolor="#e6d8cd",
        tickfont_size=16,
        tickfont_color="#66605b"
    ),
    yaxis=dict(
        zeroline=False,
        gridcolor="#e6d8cd",
        tickfont_size=16,
        tickfont_color="#66605b"
    ),
    plot_bgcolor="#fff0e4",
    paper_bgcolor="#fff0e4",
    height=600,
    width=800
)

作者制作的图

添加 X 轴脊椎

我们通过将 x 轴的showline设置为True并改变线的颜色来反映我们的示例图。

fig.update_layout(
    xaxis=dict(
        showline=True,
        linecolor="#66605b"
    )
)

作者制作的图

改变字体

在这个例子中,我将使用一种叫做 Metric 的字体,它看起来与 FT 使用的字体相似。我们可以将它添加到我们的绘图和悬停信息中,如下所示:

fig.update_layout(
    font_family="Metric",
    hoverlabel_font_family="Metric"
)

作者制作的图

添加标记和文本注释

这是最复杂的更改—为了在最终点创建标记,我们将添加另一个散布追踪,其中只有一个点对应于最终值。此外,我将根据 FT 绘图示例中的颜色定义 3 种颜色,并将其用于三条轨迹。最后,我们可以向这些分散点添加文本注释。注意,我们还必须确保隐藏这些分散点的悬停信息,这样我们就不会对同一个点有两个悬停工具提示。

colors = ["#7f042e", "#2090ce", "#eb5e8d"]i = 0
for ticker, group in df.groupby("ticker"):
    fig.add_trace(
        go.Scatter(
            x=[group["Date"].iloc[-1]],
            y=[group["Close"].iloc[-1]],
            mode="markers+text",
            marker_color=colors[i],
            marker_size=8,
            hoverinfo="skip",
            text=ticker,
            textposition="middle right",
            textfont = dict(color=colors[i], size=16)
        )
    )
    fig.add_trace(
        go.Scatter(
            x=group["Date"], 
            y=group["Close"], 
            name=ticker, 
            mode="lines",
            line_color=colors[i],
            hovertemplate=f"<b>{ticker}</b><br>Date: %{{x}}<br>Closing Price: $%{{y}}<extra></extra>"
        )
    )
    i+=1

现在,我们只需要删除图例,我们将手动添加一个 x 轴范围,以确保我们的注释在稍微偏离图表区域时不会被剪切。我们还可以在我们的情节顶部添加一个标题。

fig.update_layout(
    xaxis=dict(range=["2021-12-01", "2022-03-15"]),
    showlegend=False,
    title="Stock Prices in the Past Three Months",
    title_font_size=24,
    title_x=0.1,
    title_xanchor="left",
)

《金融时报》风格的最终股票走势图——由作者制作

非常酷!现在你可以看到默认的plotly图表的可定制程度。

保存和加载布局

最后,一旦你完成调整你的地块到它的最终形式,你可能想要保存你的布局,以便你可以很容易地加载和重用它在未来。最简单的方法是保存代表绘图布局的 JSON 对象。我们可以这样做:

**# Import JSON package** import json**# Get layout JSON from figure** layout = fig.layout.to_plotly_json()**# Save layout**
with open("layout.json", "w", encoding="utf-8") as f:
    json.dump(layout, f, ensure_ascii=False, indent=4)
f.close()

现在,稍后,添加我们保存的布局很简单,加载 JSON 文件并用它更新布局。

**# Load layout** layout = json.load(open("layout.json"))**# Add layout**
fig.update_layout(layout)

输出地块

我们可以用两种方式导出最终的图形——像我们在matplotlib中做的那样导出光栅化或矢量化的图像,或者导出为保持交互性的 HTML 文件。为了保存图像,我们需要安装一个名为kaleido的依赖项:

pip install kaleido

一旦我们这样做了,我们可以用fig.write_image()保存一个光栅图。我们可以保存为各种格式,如 JPEG、PNG 或 PDF,还可以提供一个scale值,以确保我们的应用程序在最终图像中有足够的 DPI。

fig.write_image("plot.png", scale=2)

现在,如果我们想保存交互情节,我们需要导出到 HTML。我们可以使用fig.write_html()来做到这一点——这个函数的一个可选参数是include_plotlyjs,它设置是否在 HTML 文件本身中包含整个 Plotly javascript。这通常会增加大约 3.5 MB 的整体文件大小,因此如果您希望收件人只使用 CDN 来获取 Plotly JS,而不是将其包含在文件中,您可以在此处设置。

fig.write_html("plot.html", include_plotlyjs="cdn")

结论

这是使用plotly创建和定制交互式人物的基本介绍。你可以进入plotly 布局文档查看使用fig.update_layout()时的所有可能性。本文中的plotly.ipynb笔记本将在这个 Github 资源库中提供。

感谢您的阅读!我感谢任何反馈,你可以在 Twitter 上找到我,并在 LinkedIn 上与我联系,以获取更多更新和文章。

机器学习的数据预处理导论

原文:https://towardsdatascience.com/an-introduction-to-preprocessing-data-for-machine-learning-8325427f07ab

了解如何以及何时应用常见的预处理技术

马库斯·克里斯蒂亚在 Unsplash 上的照片

机器学习算法学习存在于一组特征中的模式,并使用这些模式来预测新的未知数据的给定目标变量。得到的训练模型本质上是一个数学函数,它成功地将 X(特征)的值映射到 y(目标)的未知值。

与所有数学计算一样,机器学习算法只能处理用数字表示的数据。此外,由于每个算法都在各种不同的约束和假设下工作,因此以反映算法如何理解数据的方式来表示这些数字非常重要。

假设我们有一个用红色、蓝色和灰色来表示汽车颜色的特征。如果我们将每种颜色表示为一个数字,比如红色= 1、蓝色= 2 或灰色= 3,机器学习算法在不理解颜色概念的情况下,可能会将红色解释为更重要,因为它由最大的数字表示。

在机器学习术语中,预处理是指将原始特征转换为机器学习算法可以理解和学习的数据。

如图所示,为机器学习预处理数据是一种艺术形式,需要仔细考虑原始数据,以便选择正确的策略和预处理技术。

在下面的教程中,我将主要使用 Scikit-learn 库,用代码示例介绍常见的预处理步骤。本文并不打算详尽地概述所有可用的预处理方法,而是旨在提供最常用策略的良好基础知识。如果您对本文感兴趣的话,我在文章末尾提供了一些链接,以便更深入地研究预处理。

安装

出于本教程的目的,我将使用来自openml.org的“汽车”数据集。该数据集由许多与汽车特征相关的特征和代表其相关保险风险的分类目标变量组成。也可以下载数据集,并使用下面的代码将其转换成熊猫数据帧。

在开始预处理之前,理解每一列的数据类型是很重要的。如果我们运行df.dtypes,我们可以看到数据集混合了分类和数字数据类型。

汽车数据集的数据类型。作者图片

编码分类特征

正如文章开头提到的,机器学习算法需要数字数据。因此,在用于模型训练之前,任何分类特征必须首先被转换成数字特征。

用于处理分类变量的最常见的技术是一种热编码,有时也称为虚拟编码。该技术为要素中包含的每个唯一值创建一个新列。新列是二进制要素,如果值不存在,则包含 0,如果存在,则包含 1。

Scikit-learn 库提供了一种预处理方法,可以执行一次热编码。以下代码将数据集中的分类特征转换为一个热编码列。

当使用这种方法转换分类列时,必须特别注意特性的基数。

基数是指给定列中唯一值的数量。例如,如果我们有一个包含 50 个唯一值的特性。执行一次热编码将导致创建 50 列。

这可能导致两个问题:

  1. 非常大的训练集导致训练时间明显变长。
  2. 稀疏的训练集会导致过度拟合的问题。

我们可以通过运行下面的df[categorical_cols].nunique()来了解数据集中的特征基数。

我们可以看到 make 列具有相当高的基数。

分类特征的基数。作者图片

处理高基数类别的一种方法是将不常出现的值聚集到一个新的类别中。OneHotEncoder方法为此提供了两种选择。

第一个选项是将min_frequency参数设置为一个选定的数字。这将导致频率小于该值的任何值被添加到不频繁类别中。

请注意,该选项目前仅适用于 Scikit-learn 1 . 1 . 0 及以上版本。

第二个选项是将max_categories参数设置为任何大于 1 的数字。这将生成的列数限制在该数量或更少。

输入缺失值

大多数真实世界的数据集都会有一些缺失值。这可能有多种原因。生成数据的系统可能出现错误,导致观察值缺失,或者某个值可能因为与特定样本无关而缺失。

无论什么原因,大多数机器学习算法都不能解释空值,因此,有必要以某种方式处理这些值。

一种选择是删除包含缺失值的行。但是,这通常是不实际的,因为它会将训练数据集的大小减少太多,或者算法的应用可能需要为所有行生成预测。

如果无法删除丢失的值,则有必要用一个合理的值来替换它们。这是一种被称为插补的技术。

有许多策略可以用来计算缺失值。从用特征的中值、平均值或最频繁值替换缺失值的非常简单的选项。到使用机器学习算法来确定插补的最佳值的更复杂的情况。

在选择策略之前,我们首先需要了解我们的数据集是否有任何缺失值。为此,请运行以下命令。

从结果中我们可以看到,5 个特征有缺失值,除了“归一化损失”列,所有特征的缺失值百分比都很低(低于 2%)。

缺失值的百分比。图片作者。

通常,我们会对每个特征进行一些探索性分析,以便为插补策略的选择提供信息。然而,出于本教程的目的,我将简单地展示一个使用简单策略和一个更复杂策略的例子。

下面显示的代码使用了 Scikit-learn 方法,称为SimpleImputer。由于我们混合了带有缺失值的分类和数字特征,我们将使用两种不同的简单策略来估算它们。对于数字特征,我们将用该列的平均值替换缺失值,对于分类特征,我们将使用最常出现的值。

当数据用于训练时,简单地用简单的统计(如平均值)填充所有缺失值可能不会产生最佳性能。一种更复杂的插补方法是使用机器学习算法来告知要插补的值。

一种常用的技术是K-最近邻算法。该模型使用距离度量,如欧几里德距离,来确定一组指定的最近邻,并估算这些邻的平均值。

特征缩放

训练集中的数字特征通常具有非常不同的比例。例如,特性“价格”的最小值为 5,118。而“压缩比”的最小值仅为 7,最大值为 23。机器学习模型可能会错误地将“价格”特征中的较大值解释为比“压缩比”特征中的值更重要。

与缩放相关的另一个预处理步骤是在变换要素的位置居中,以便它们形成正态分布。许多机器学习算法假设特征是正态分布的,除非以这种方式表示特征,否则它们不会如预期那样表现。

Scikit-learn StandardScaler方法通过移除平均值并将每个特征缩放至单位方差来执行居中和缩放。下面的代码执行这些步骤。

扔掉

宁滨或离散化是一种用于将连续变量转换成相似值的组或桶的技术。当一个变量有大量不经常出现的值时,这种技术特别有用。在这种情况下,离散化可以减少特征中的噪声,并减少训练期间模型过拟合的风险。

在我们的数据集中,价格变量的取值范围非常大。最频繁出现的价格只有 2 次。这是一个特别受益于宁滨的特征的例子。

一旦执行了离散化,该特征就必须被视为分类的,因此必须执行额外的预处理步骤,例如一个热编码。

Scikit-learn 库有一个名为KBinsDiscretizer的方法,它在一个步骤中执行宁滨和分类编码。以下代码将价格功能转换为 6 个箱,然后对新的分类变量执行一次热编码。结果是一个稀疏矩阵。

把所有的放在一起

到目前为止,在本教程中,我们已经独立执行了所有预处理步骤。在真正的机器学习应用中,我们总是需要对训练集和任何测试或验证数据集进行预处理,然后在对新数据进行推断的过程中再次应用这些预处理。因此,编写能够一步完成所有这些转换的代码是最有效的。

Scikit-learn 有一个被称为管道的有用工具。Scikit-learn 管道使预处理步骤能够与估计器链接在一起。下面的代码创建了一个管道,该管道执行本教程中概述的所有预处理步骤,并且还适合随机森林分类器。

可以重用管道来预处理测试数据集并生成预测,如下所示。

机器学习算法的学习方式与人类不同。一个算法不可能像你和我一样理解车门数量和汽车之间的关系。为了让机器学习,必须将数据转换为适合算法学习方式的表示形式。

在本文中,我们介绍了以下预处理技术。下面是对这些方法的简要总结以及它们有用的原因。

  1. 编码分类特征:大多数机器学习算法只能处理数字数据,因此分类变量必须转换为数字表示。
  2. 输入缺失值:大多数机器学习算法无法解释空值。输入用合理的替换来替换丢失的值。
  3. 特征缩放:机器学习算法只理解数值关系。因此,不同比例的要素可能会被错误地解释。缩放可确保连续要素中的值都在相同的比例上。
  4. 宁滨:具有许多不经常出现的值的连续变量可能包含大量噪声,这可能导致训练期间的过拟合。宁滨将这些值聚集成桶或相似值的组,从而产生新的分类特征。

本教程给出了应用于机器学习数据的最常见预处理技术的介绍性概述。这里描述的方法有许多不同的选项,还有更多可能的预处理步骤。

一旦你对所描述的技术有了全面的了解,如果你想更深入地研究更多的技术,《用 Scikit-learn 和 Tensorflow 进行机器学习》这本书是一个很好的资源。这本书可以通过这个链接获得免费阅读的 PDF 格式。

关于 Scikit-learn 的更多文章,请参见我之前的帖子。

</10-things-you-didnt-know-about-scikit-learn-cccc94c50e4f>

感谢阅读!

引用

Autos 数据集: Jeffrey,C. Schlimmer。https://archive.ics.uci.edu/ml/datasets/Automobile 网络数据仓库[https://archive.ics.uci.edu/ml/datasets/Automobile]。在开放科学许可证下使用。

Python 中正则表达式的介绍

原文:https://towardsdatascience.com/an-introduction-to-regular-expressions-in-python-23baebfa3ac

照片由 Unsplash 上的尼克·费因斯拍摄

PYTHON 基础

探索 Python 中正则表达式的基本概念

"……这本书是关于什么的?”——我问

他告诉我

什么?我从来没有听说过他们”——我困惑地回答

他说:“哦,如果你读了这本书,你会发现它们非常有用。”

我打开书,翻了翻索引,直接到了 Python 部分

我必须承认。我当时超级迷茫。我一点也不明白这本书在说什么。所以我关了它。

在那之后的某个时候,我正在做一个与自然语言处理相关的项目。我必须解析 pdf 文件,这简直成了一场噩梦。

我绝望地看着我的书柜,我看到那本书就在那里。我告诉自己,我必须试一试。所以我再次打开它,决心理解正则表达式。

我花在学习它们上的每一分钟都是值得的!

正则表达式非常强大和快速。一旦你掌握了这个概念,一个新的世界就展现在你面前。它们允许你搜索复杂的模式,否则很难找到。

注:除特别注明外,所有图片均为作者所有。

基础知识

正则表达式(Regex)是包含普通字符和特殊字符组合的字符串,用于描述在文本中查找文本的模式。

什么???这听起来很复杂…让我们分解一下,以便更好地理解。

正则表达式(Regex)。所有图像,除非另有说明

上图展示了正则表达式的样子。在 Python 中,开头的 r 表示一个原始字符串。不强制使用,但建议使用。

正则表达式包含匹配自身的普通字符。tr 匹配一个 t 后跟一个 r

我们说过一个正则表达式包含普通字符,或者换句话说,我们已经知道的文字字符。正常字符匹配自己。在图中所示的情况下, tr 与后面跟有 rt 完全匹配。

正则表达式包含匹配字符类型、位置或数量的元字符

正则表达式也包含元字符。这些特殊字符本身不匹配。相反,它们是在正则表达式中具有“特殊含义”的字符。特别地,它们可以代表:

1。字符类型

在这种情况下,元字符表示字符类或特殊序列。例如, \d 代表一个数字, \s 表示空格,【A-Za-Z】从 A 到 Z,或 A 到 Z 的任何字母。

2 。想法,如位置重复

元字符可以指示位置。此外,它们还可以充当量词来指定位于其左侧的字符需要匹配多少次。

在本例中,花括号中的 1 和 2 表示紧邻左侧的字符,在本例中为/d,应该出现 1 到 2 次。此外,加号* (+)表示从 A 到 Z 或 A 到 Z 的任何字母应该出现 1 次或更多次。*

在表中,我们可以看到一个支持的元字符及其含义的列表。

下表显示了 Python 支持的一些最常见的元字符。改编自 Python 正则表达式教程。

我们说过正则表达式描述了一种模式。模式是映射到单词或标点符号的一系列字符。

数据科学家或软件工程师使用模式匹配来查找和替换特定的文本。它们的用例非常广泛,从验证字符串(如密码或电子邮件地址)、解析文档、执行数据预处理到帮助进行 web 抓取或数据提取。

为什么正则表达式?他们非常强大和快速。它们允许我们搜索复杂的模式,否则很难找到。

re模块

Python 有一个有用的库,re 模块,用来处理 regex。

import re

这个库为我们提供了几个使模式匹配更容易的函数。让我们看看其中的一些。

为了在中搜索一个模式,我们可以使用.search()功能。它接受正则表达式和字符串。该函数扫描整个字符串,寻找正则表达式给出匹配的第一个位置。如果字符串中没有与模式匹配的位置,则返回 match 或None

> re.**search**('\w{4}\d{4}', 'My password is abcd1234.')<re.Match object; span=(15, 23), match='abcd1234'>

在代码中,我们希望找到一个重复四次的单词字符,然后是一个重复四次的数字。.search()函数找到匹配:abcd1234

另一个帮助我们找到模式匹配的函数是.match()。它还接受正则表达式和字符串。

我们已经有了 .search() ,为什么还需要另一个函数?

.match()函数锚定在字符串的开头。这意味着只有在字符串的开头找到模式匹配,它才会返回相应的匹配。

> re.**match**('\w{4}\d{4}', 'My password is abcd1234.')

在前面的例子中,我们将使用.match()而不是.search()。我们会发现没有匹配,因为在字符串的开头没有一个单词字符重复了四次,后跟一个重复了四次的数字。

> re.**match**('\w{4}\d{4}', 'abcd1234 is my password.')<re.Match object; span=(0, 8), match='abcd1234'>

让我们改变我们的字符串。我们在开头使用一个带有我们的模式的字符串。现在,.match()函数能够找到匹配。

为了找到一个模式的所有匹配,我们可以使用.findall()函数。它有两个参数:正则表达式和字符串。

> re.**findall**(r'\d{1,3}', 'My 3 cats have 15 kittens')['3', '15']

在代码中,我们希望找到在指定字符串中重复 1 到 3 次的任何数字的所有匹配。findall()函数返回找到的两个匹配项的列表:“3”和“15”。

注意,不一定是同一个数字,只是“数字类”要重复 1 到 3 次

要用另一串替换任何模式匹配,我们可以使用sub()功能。它有三个参数:正则表达式、替换和字符串。****

> re.**sub**('\d', ' ', 'My1house2has3white4walls')'My house has white walls'

在这个例子中,我们用空格替换了十进制数字的每一个匹配。

正则表达式在运行

现在我们已经介绍了正则表达式的基本概念,让我们看看正则表达式的实际应用。

想象一下,我们正在清理一些从网络上提取的文本。我们遇到一些字符串(例如:我的名字是#约翰·史密斯。I%live$in#London) 包含不应该出现的符号。我们如何清理这些琴弦?

我们将使用正则表达式和.sub()函数。我们如何构建正则表达式?

我们将表明我们想要搜索符号#$%&,并将它们放在方括号[#$%&]中。这将表明方括号之间的任何单个字符都可以匹配。我们将把它们替换为空白区域“ “。因此代码如下所示:

> my_string = "My&name&is#John Smith. I%live$in#London."
> re.sub(r"[#$%&]", " ", my_string)'My name is John Smith. I live in London.'

现在,假设我们想要验证一个密码。该密码需要满足某些要求。因此,让我们编写正则表达式来帮助我们验证它们:

  1. 它必须以最少 4 个但最多 8 个数字开头:\d{4, 8}.因为我们必须匹配开头,所以我们使用.match()
  2. 数字后面必须有最少 2 个最多 6 个字母,大写或小写[a-zA-Z]{2,}
  3. 之后可以包含任意字符.*
  4. 它不能以下列符号结尾!,@,\(,%,&: `[^!@\)%&]\(`。注意这里我们在方括号中使用`^`来否定符号的出现。`\)`将模式锚定到字符串的末尾。

所以我们定义了一个函数来验证密码:

> def validate_password(password): 
>     if re.match(r"\d{4,8}[a-zA-Z]{2,}.*[^!@$%&]$", password):
>         print(f"Valid Password {password}")
>     else:
>         print(f"Invalid Password {password}")

我们可以使用一个无效的密码来测试它:4390Abac!以一个符号结尾。

> validate_password("4390Abac!")Invalid Password 4390Abac!

还有 4390Abac!1 符合要求。

> validate_password("4390Abac!1")Valid Password 4390Abac!1

最后,假设我们必须从文档中提取日期。人们写日期的方式非常不同。月份可以与数字或名称一起出现。这一天可能在一个月之后,也可能在一个月之前。如此等等。

在下面的例子中,我们需要将日期提取为:*ordinal_number* of *month_name* *year* *hh:mm.* 所以让我们构建正则表达式:

  1. 序数可以有 1 或 2 位数。后面是 st,th,或者 rd (so 2 小字母):\d{1,2}[a-z]{2}。在那之后我们有空白:\s然后是单词of和空白:\s
  2. 然后,我们将表明我们想要匹配任何字母(大写或小写)至少一次:[a-zA-Z]+。然后,空格:\s
  3. 然后,必须跟随一个 4 位数:\d{4}和空格:\s
  4. 然后,我们希望匹配一个 1 或 2 位数的小时数\d{1,2},后跟一个冒号:和一个两位数的分钟数\d{2}

最后,我们将有下面的正则表达式:

r”\d{1,2}[a-z]{2}\sof\s[a-zA-Z]+\s\d{4}\s\d{1,2}:\d{2}”

因此,我们的代码和输出将如下所示:

> my_date = 'Your appointment has been confirmed for 1st of september 2022 18:30'
> regex = r"\d{1,2}[a-z]{2}\sof\s[a-zA-Z]+\s\d{4}\s\d{1,2}:\d{2}"> re.findall(regex, my_date)['1st september 2022 18:30']

一个强大的工具,对吗?

在本文中,我们了解到正则表达式可以匹配普通或文字字符,以及可以表示字符类、数量或位置的元字符。我们探索了re模块,它允许我们在字符串中查找、匹配、搜索和替换模式。最后,我们看到了一些如何使用正则表达式提取数据或验证表达式的例子。

然而,我们只是讨论了正则表达式的基本概念。而且还有更多!

如果你想了解更多关于如何掌握 Python 中的 Regex,可以点击图片,看看我的课程。

此外,查看这些有助于理解和测试正则表达式的资源:

正规化介绍

原文:https://towardsdatascience.com/an-introduction-to-regularization-ec14a269018c

这就是让你训练的模型实际可用的原因

Nicholas Githiri 摄:https://www . pexels . com/photo/person-holding-volume-knob-1345630/

过度拟合是任何数据科学家的克星。

没有什么比精心构建一个在训练数据上得分很高的模型更令人沮丧的了,因为它在测试数据上使用时表现不佳。

用户可以通过使用正则化来避免这种结果,正则化是阻止过度拟合的最有效的技术之一。

在这里,我们深入研究正则化做了什么,以及如何利用它来训练模型在高水平上执行。

过度拟合问题

在讨论正则化之前,值得讨论一下过度拟合以及它在机器学习项目中如此不受欢迎的原因。

过度拟合是一个术语,用于描述过度适应训练数据的模型,从而使其无法在看不见的数据上表现良好,尽管在训练数据上表现良好。

这乍听起来可能很奇怪。一个模型从训练数据中“学到太多”意味着什么?

答案在于,这些模型通过使用损失函数来衡量性能来学习,损失函数量化了模型的预测值与训练数据中的实际值之间的差异。自然,损失函数的目标是最小化这种差异。

然而,仅仅最小化损失并不一定对应于更好的模型性能。

毕竟,一个只关心训练数据的实际值和预测值之间的差异的模型也会考虑噪声等不需要的元素,因此会增加对看不见的数据不适用的复杂性。

用这种安排训练的模型不能一般化,并且将不能针对看不见的数据充分地执行,这是模型的全部目的。

正规化

那么,正则化如何帮助避免令人讨厌的过度拟合问题呢?

简单来说,它给损失函数增加了一个“惩罚”。

正则化确保损失函数不仅考虑预测值和实际值之间的差异,还考虑赋予特征的重要性。

通过这种技术,用户可以限制模型的复杂性,并训练模型使用看不见的数据做出准确的预测。

当然,有多种方法来惩罚一个包含太多复杂性的模型。

两种主要的正则化技术是套索正则化(也称为 L1 正则化)和岭正则化(也称为 L2 正则化)。

总体而言,Lasso 正则化基于模型要素系数的大小对模型进行惩罚,而 Ridge 正则化基于模型要素系数的平方值对模型进行惩罚。

实施正规化

既然我们已经探索了正则化的好处,我们如何在我们的机器学习模型中利用这种技术呢?

嗯,Python 的机器学习包里很多算法已经默认实现正则化了。例如,如果用户没有明确指定正则化技术,Scikit-learn 模块中的线性分类器使用 L2 正则化。

那么,既然正则化已经是机器学习算法中的一个嵌入特性,那么用户是否正确地利用了这项技术呢?

简短的回答是:可能不会。

这是因为分配给模型参数的默认值很少是最佳的。

毕竟,不同的机器学习任务在正则化的类型和强度方面有所不同,正则化是确保训练好的模型在看不见的数据上表现良好所需的。

这就引出了一个问题:在用感兴趣的数据训练模型时,如何确定最佳的正则化类型?

个案研究

为了演示用户如何确定和实现最有效的正则化方法,我们可以使用 Scikit-learn 模块中的玩具数据集构建一个线性分类器。

首先,我们准备数据,创建训练集和测试集。

Scikit-learn 模块中的大多数线性分类器允许用户使用penaltyC参数修改正则化技术。

penalty参数指的是算法包含的正则化技术。

C参数定义分类器使用的正则化强度。分配给该参数的值与正则化的强度成反比。换句话说,C 的值越大,正则化越弱。

对于这个案例研究,我们将使用 LinearSVC 类构建一个模型。

默认情况下,模型分别给penaltyC参数赋值‘L2’和 1。让我们看看一个基线模型在测试集上的表现如何,这些测试集是基于 f1 分数度量的。

代码输出(由作者创建)

基线模型相对于测试集产生大约 0.9012 的 f-1 分数。正则化的使用在使模型相对于测试数据表现良好方面发挥了重要作用。

也就是说,分配给模型超参数的默认值可能不是给定数据集的最佳值。不同类型或强度的正则化可能会产生更好的性能。

因此,通过考虑penaltyC参数值的其他组合并识别产生最佳性能的组合来微调模型将是有益的。这可以通过执行超参数调谐程序来实现。

对于这种情况,Scikit-learn 模块提供了 GridSearchCV 类,允许用户测试超参数的不同组合。该工具最好的特性之一是其内置的交叉验证分割策略,进一步降低了过度拟合的可能性。

让我们使用 GridSearchCV 来确定模型的最佳超参数。

代码输出(由作者创建)

优化的模型仍然使用 L2 正则化。但是,它也为C参数赋值 0.0001。这意味着当使用比基线模型强度更高的正则化时,模型性能提高。

让我们看看具有这些参数的模型在测试集上的表现。

代码输出(由作者创建)

优化后的模型对数据集的 f-1 得分为 0.9727,比基线模型有了相当大的改进。

关键要点

照片由 Prateek KatyalUnsplash 上拍摄

正则化通过限制模型的复杂性来帮助克服过度拟合。

用户可以通过了解他们的模型的特征来充分利用这种技术,这些特征允许他们选择最佳的正则化方法。

如果您不确定是否在机器学习任务中充分利用了正则化,请随意浏览感兴趣模型的文档,并查看提供了哪些关于优化正则化的工具。

我祝你在数据科学的努力中好运!

SageMaker 管道简介

原文:https://towardsdatascience.com/an-introduction-to-sagemaker-pipelines-4018a819352d

如何在 Amazon SageMaker 上开始您的 MLOps 之旅

图片来自 Unsplash 作者 JJ 英

MLOps 是生产过程中经常被忽视的一个重要途径。很容易分离出您想要训练和部署的单一模型。在现实中,将会有大量的模型供您使用。您需要确保您有适当的基础设施来管理培训、模型跟踪和部署。MLOps 有助于将这些过程构建并简化为一个清晰的工作流,为机器学习量身定制。

SageMaker Pipelines 是亚马逊 SageMaker 支持的首要 MLOps 特性。使用管道,您可以创建和管理大规模的端到端 ML 工作流。在今天的文章中,我们将介绍如何设置 SageMaker 管道。

注意:对于 AWS 的新用户,如果你想跟进,请确保在下面的 链接 中注册账户。本文将假设一个新手对 AWS 和 SageMaker 有中级的了解。

目录

  1. SageMaker 管道是如何工作的?
  2. SageMaker 项目设置
  3. 可视化 SageMaker 管道
  4. 其他资源和结论

1.SageMaker 管道是如何工作的?

在高层次上,SageMaker 管道是通过一系列协调的 步骤 构建的。您可以定义不同的步骤类型,如 训练步骤注册模型步骤 。使用 SageMaker SDK,您可以在 Python 中构建这些步骤,一旦执行,就会创建一个显示您的工作流的可视化 DAG。

SageMaker 管道的另一个关键部分是 管道参数 。通过参数,您可以将变量注入到管道中。通过变量,我们可以从实例类型到实例计数等等。使用这些,您可以在工作流程的不同部分调用这些参数。

2.设置

SageMaker Pipelines 是由 SageMaker 工作室最有效地编排的。SageMaker Studio 是由 SageMaker 提供的一个 IDE,在这里你可以在一个与 JupyterLab 非常相似的环境中工作,但是拥有 SageMaker 的所有功能。您应该能够在 SageMaker 控制台的左侧访问 Studio。

SageMaker 工作室(作者截图)

在 Studio 控制台上,你应该可以点击添加用户,在这里你可以创建一个 SageMaker 域。在你点击了你已经创建的用户之后,你应该能够启动一个工作室应用程序。在这里,您应该能够看到开始使用 Studio 的一般设置。

工作室(作者截图)

现在我们可以立即开始工作,使用 SageMaker Python SDK 从头开始构建管道。然而从头开始建立一个管道可能是一件乏味的事情并且许多基础设置可以自动化。 SageMaker 项目帮助提供现成的模板,您可以根据自己的特定 ML 用例进行修改和构建。我们可以在 SageMaker Studio UI 中找到项目。

创建 SageMaker 项目(作者截图)

如果我们点击创建项目,我们可以看到提供的不同模板。

项目模板(作者截图)

在这里,我们可以从简单的模型构建和培训模板开始。选择后,我们可以命名和创建项目,此过程需要几分钟时间。

创建项目(作者截图)

项目已创建(作者截图)

现在我们可以更深入地了解管道实际上是由什么组成的。

3.可视化 SageMaker 管道

在 Repositories 选项卡的右边,您应该能够看到 Pipelines 选项卡。在这里,我们可以看到我们的项目模板已经为我们创建了一个预构建的管道。如果我们点击管道,我们应该能够看到正在进行的执行。

管道正在执行(作者截图)

为了理解工作流程,如果我们单击“Graph ”,我们可以看到构建此管道的不同步骤。

DAG(作者截图)

我们讨论的另一个主要部分是我们注入到管道中的参数或变量。这在右侧的参数选项卡中可见。

参数(作者截图)

现在你可以在哪里编辑这条管道后面的代码呢?如果我们返回到 randomforest-pipeline 项目的 SageMaker 项目选项卡,您应该能够克隆此存储库,并且它将在 Studio IDE 中本地显示。

克隆项目回购(作者截图)

如果您单击显示的本地路径,那么您应该能够看到用于编排和构建该管道的代码。

管道代码(作者截图)

默认情况下,Pipelines 使用鲍鱼数据集构建一个示例,但是您可以为您想要构建/训练的数据和模型编辑该代码。如果我们转到 Pipelines 目录,您应该能够看到许多可以用来构建的样板代码。根目录下还有一个笔记本,帮助执行您在 pipelines 目录中提供的 Python 脚本。

管道设置(作者截图)

定义管道的 Python 脚本(作者截图)

您应该关注的主要 Python 脚本是 pipeline.py 。这是一个脚本,它将您的所有步骤打包在一起,并捕获您定义的整个工作流。您可以在 Python 文件的末尾看到这一点。

然后,这些脚本会在已经为您预先准备好的笔记本单元中执行。

执行管道

4.其他资源和结论

这是一个关于如何利用 SageMaker 管道进行 MLOps 之旅的温和介绍。利用项目,您可以获得预构建的模板,您可以根据自己的用例轻松调整这些模板。管道提供了大量的步骤和可能性,以及 SageMaker 作为一个整体提供的计算规模和能力。

额外资源

SageMaker 管道端到端示例

管道 Youtube 演示

带 SageMaker 的 MLOps】

如果你喜欢这篇文章,请在 LinkedIn 上与我联系,并订阅我的媒体 简讯 。如果你是新手,使用我的 会员推荐 报名。

使用 REST APIs 和 Python 在 LinkedIn 上共享内容的介绍

原文:https://towardsdatascience.com/an-introduction-to-sharing-content-on-linkedin-using-rest-apis-and-python-d82355a06178

了解如何使用 REST APIs 在 LinkedIn 上共享内容

来自像素的图像

LinkedIn 是一个受欢迎的社交媒体平台,允许用户相互联系并维护一个职业网络。您可以管理您的个人资料,也可以为您的组织创建公司个人资料。LinkedIn 的一个有趣的功能是能够在你的订阅源或你的组织的订阅源上分享帖子。你可以在帖子中嵌入网址,提供标签和表情符号,还可以分享图片和视频。虽然在 LinkedIn 上分享内容的主要方式是使用 web 应用程序,但是您也可以使用 LinkedIn 提供的REST API和您选择的编程语言以编程方式来完成。

在本文中,我们将学习如何使用LinkedIn REST API并分享来自您的个人档案以及您管理的组织档案的内容。如果你没有组织,不要担心,因为我会从头开始指导你。

先决条件

为了更进一步,我们需要具备以下先决条件。

  1. LinkedIn 账户 —你需要一个 LinkedIn 账户,如果你还没有,请前往https://www.linkedin.com/创建一个。
  2. REST API—使用 REST API 的一些基础知识。
  3. python —使用 Python 和在 Python 中使用 REST APIs 的一般编程知识。

除了上面提到的,我将创建代码库,并使用 GitHub 使其可用。在任何时间点,你都可以选择参考来自微软的 LinkedIn 的官方文档,然而,我将尝试用简单的术语解释这个过程。

假设您已经创建了 LinkedIn 帐户,现在我们将创建以下两项资产。

  1. LinkedIn 公司页面 —也可以称为公司页面,我们需要使用您的个人帐户创建它。您将成为您创建的公司页面的管理员。
  2. 开发人员门户上的应用程序 —为了访问 LinkedIn REST APIs,我们需要创建一个应用程序,它将代表我们获得权限,并在您的个人资料或组织的个人资料上共享内容。

创建 LinkedIn 公司页面。

让我们从您的 LinkedIn 帐户创建一个公司页面。你可以为你的公司想出任何一个假名来继续发展。我将把我的公司作为“ DataScholar ”。

第一步 :开通 LinkedIn 账户。点击工作并选择创建公司页面。点击公司并输入详细信息,点击创建页面

图 1 —创建 LinkedIn 公司页面

第二步 :您需要为您要创建的公司页面提供以下详细信息。

  1. 名称 —贵公司的名称。我正在使用数据选择器
  2. 公司 网址 —贵公司的 LinkedIn 用户名。
  3. 网站 —贵公司所在的网站。我准备用“”https://thedatascholar.com”。
  4. 行业 —选择为软件
  5. 公司
  6. 徽标 —您公司徽标的任何自定义图像。
  7. 标语——对贵公司的简短描述。

一旦您提供了所有这些详细信息,您的 LinkedIn 公司页面就创建好了。

图 2 — LinkedIn 公司页面

在开发者门户中创建 LinkedIn 应用程序。

在本节中,我们将深入探讨并在 LinkedIn Developer 平台上创建一个应用程序,它将允许我们以编程方式与 LinkedIn 资产进行交互。

第一步 :导航到https://developer.linkedin.com/。点击我的应用,然后选择创建应用。输入申请的详细信息。

  1. 应用程序名称 —应用程序的名称。您可以使用与您公司相同的名称。
  2. LinkedIn 页面 —应用程序将关联的公司页面。
  3. 隐私政策 URL —可选字段。
  4. 应用徽标 —为您的应用上传一个徽标。

同意条款,然后点击创建应用

图 3——创建 LinkedIn 应用

第二步 :点击验证验证您的申请。

图 4 —验证 LinkedIn 应用程序

第三步 :点击生成网址复制网址后点击我完成

图 5 —为 LinkedIn 应用程序生成验证 URL

第四步 :在浏览器上复制并点击网址。它会要求您验证页面的应用程序。

图 6 —验证 LinkedIn 应用程序和公司关联

第五步 :点击验证

图 7- LinkedIn 应用程序和公司协会已验证

太好了!现在,您已经成功地创建了一个 LinkedIn 公司页面,还创建了一个使用 REST APIs 与 LinkedIn 交互的应用程序。

让我们配置我们在上一节中创建的 LinkedIn 应用程序。我们将添加访问 LinkedIn REST APIs 和存储应用凭证所需的产品。

为您的 LinkedIn 应用程序添加产品。

一旦你创建了 LinkedIn 应用程序,你需要定义你的应用程序将使用什么产品。基于此,将为您的应用程序提供您需要的必要权限。有三种产品可供选择。

  1. 在 LinkedIn 上分享 —这允许您发布个人资料中的内容。
  2. 使用 LinkedIn 登录 —这允许您在网页上使用 LinkedIn 社交登录。
  3. 营销开发者平台——这允许你发布你页面上的内容。

作为本文的一部分,我将把这三个产品都添加到我的应用程序中。

图 8 —向 LinkedIn 应用添加产品

市场开发人员平台需要额外的访问权限。你会收到一份表格。填写表格并等待批准(2 个工作日)。点击查看访问表单并填写相关详细信息。

图 9 —填写市场开发平台评审表

提交表格后,可能需要 2 个工作日才能通过电子邮件获得答复。然后,您会注意到该应用程序已添加到您的产品列表中。

💡 营销开发人员平台 允许您发布公司页面上的内容。不添加它,你仍然可以从你的个人 LinkedIn 档案发布内容。

获取并存储应用凭据。

第一步 :在你的 LinkedIn app 上,点击 Auth 标签。您可以找到稍后需要的客户端 ID客户端机密

图 10 —存储 LinkedIn 应用程序凭据

第二步 :为你的 App 添加重定向网址。这可以仅仅是你的网页网址加上“/auth/LinkedIn/callback”。

图 11 —设置应用程序重定向 URI

第三步 :从公司 URL 获取组织 ID

图 12 —获取组织 ID

现在让我们开始构建允许我们与 LinkedIn REST APIs 交互的 python 程序。

设置静态变量和库

为了在 python 中使用 REST APIs,我们将利用requests库。您可以使用 python pip管理器来安装它。除此之外,我们还将创建变量来存储我们在上一节中获得的client_idclient_secretorganization_id

在接下来的步骤中,我们需要获得一个访问令牌,用于共享 LinkedIn 概要文件中的内容。访问令牌可以通过以下两个步骤获得。

  1. 一个 GET 请求,将要求用户允许你的应用程序获得所需的权限,并将你重定向到一个新的 URL。从这个 URL 中,您需要提取参数代码中的值。
  2. 一个 POST 请求将返回你的全新访问令牌。

创建 GET 请求以获取验证码

设置以下详细信息以构建 URL。

  • base_url:LinkedIn 端点获取授权码。
  • redirect_uri:您在创建应用程序时设置的重定向 URL。
  • scope:发布个人资料内容所需的范围。
  • state:授权后会返回给您的随机文本。这允许您验证来自 CSRF 攻击的响应。为了简单起见,我们现在不要太在意它。

当您运行上述代码时,它将在您的控制台上打印 URL。复制网址并在你的浏览器中点击。您将被要求使用 LinkedIn 凭据登录。一旦您的登录被验证,它会将您重定向到redirect_uri中提到的地址。在这个 URL 中,您还会收到两个参数,codestate。复制code的值并存储,以处理下一个请求。

💡要从您的公司页面发布内容,您需要在您的范围内拥有更多权限。一旦您收到 LinkedIn 团队对营销开发人员平台的验证,您就可以获得此信息。

创建 POST 请求以获取访问令牌

现在已经获得了验证码,我们可以通过向 LinkedIn 发送 POST 请求来获得访问令牌。您需要在 POST 请求中提供以下内容作为有效负载。

  • grant_type:授权类型应为'授权 _ 代码'。
  • code:上一步获取的授权码。
  • redirect_uri:你的应用的重定向 URI。
  • client_id:我们之前存储的客户端 ID。
  • client_secret:我们之前存储的客户端秘密。

如果 POST 请求成功,您将收到一个包含access_token的 JSON 响应。我把它提取出来并存储在一个单独的变量中。

获取您的 LinkedIn 个人资料 ID

为了从您的 LinkedIn 个人资料发布内容,您需要获取您的个人资料 ID。这可以通过直接查询 LinkedIn REST API 来实现。

请注意,我已经在授权头中将access_token作为不记名令牌进行了传递。此外,我已经将 LinkedIn 概要 ID 存储在一个名为person_id的变量中。

分享 LinkedIn 个人资料中的内容

让我们从 LinkedIn 个人资料中准备想要分享的内容。我从 Redhat 中随机选择了一篇文章来分享。此外,我想给共享的内容添加一个缩略图。为此,您可以选择任何公开可用的图像。请注意,有效负载包含键owner,其值是我们在前面的步骤中获得的概要文件 ID。此值决定是从个人资料还是从公司页面共享帖子。

如果你想更深入的了解有效载荷,请参考官方文档。有效载荷中的每个字段都有清晰的解释。请求成功后,内容将从您的个人资料中共享,并出现在 feed 中,如下所示。

图 13——LinkedIn 上个人资料中分享的帖子

分享 LinkedIn 公司页面的内容

从组织页面共享内容与从个人简档共享内容非常相似。决定帖子内容是否从个人或组织的个人资料中共享的唯一关键是owner。以下值可用于owner键。

  • 个人简介 : urn:li:person:{person_id}
  • 组织 : urn:li:organization:{organization_id}

您可以从公司页面的 URL 获取 organization_id。有了它,现在可以从组织的个人资料中修改有效负载和共享内容。

这将发布您组织的个人资料中的内容,并且您将在订阅源中看到这些内容。

图 14 —从 LinkedIn 上的组织档案中分享的帖子

💡要从组织的个人资料中发布内容,您需要等到收到 LinkedIn 的电子邮件,这可能需要 2 个工作日。

结论

在本文中,我们重点讨论了如何使用 LinkedIn REST APIs,以及如何从个人档案和组织档案中共享内容。这只是使用 LinkedIn REST APIs 的初级介绍。除了分享内容,你还可以使用其他产品与 LinkedIn 广告和活动管理工具互动。

资源

如果你想了解更多关于 LinkedIn REST APIs 的知识,我推荐你阅读下面的内容。

原载于 2022 年 3 月 13 日【https://datacloudmag.com】

随机过程导论(2)

原文:https://towardsdatascience.com/an-introduction-to-stochastic-processes-2-bdcb894aeeef

概率测度的连续性、Radon-Nikodym 导数和 Girsanov 定理

图片来自 Unsplash

Girsanov 定理和 Radon-Nikodym 定理在金融数学中经常用于金融衍生品的定价。而且他们关系很深。这些定理的用法也可以在机器学习中找到(尽管非常理论化)。文献[9]中给出了一个例子,其中 Girsanov 的理论被应用于一种用于强化学习的新策略梯度算法中。

然而,那些定理远非直观的(甚至是符号)。因此,这个帖子试图向他们提供一个清晰的解释,这是很难找到的。为了正确理解它们,我们需要首先理解概率分布的不同类型的连续性,这就是为什么这个主题也包括在内,并占了这篇文章的很大一部分。

另外,注意这篇文章是这个的延续,比如过滤、鞅、itprocesses 等等。已经讨论过了。所以如果有不清楚的地方,可以参考那个帖子,或者我另外两个关于测度论和概率论的帖子,或者给我留言。

对概率论的再认识

随机变量和分布

随机变量是概率论中一个非常基本的概念,也是理解随机过程的基础。随机变量在我关于测度论和概率的其他文章中略有提及,但在这里我们将更严格地看待它。首先,我们在测度论的背景下给出了随机变量的形式定义:

给定一个概率三元组(ω,𝓕,P) ,一个随机变量是从ω到实数 的函数 x,这样

Cond。1.1

我们可以看到ωX 的定义域,条件 1.1 与说 X⁻ ((-∞,x))∈𝓕.相同还有,我们知道,如果设 A ={(-∞,x];x ∈ ℝ} ,那么 σ(A) = 𝓑 ,其中 𝓑 是 Borelσ-代数。(这一点很容易证明,利用σ-代数的性质证明 σ(A) 包含所有区间。)因此,对于所有的 B ∈ 𝓑 ,条件 1.1 也与 X⁻ (B) ∈ 𝓕 相同。[1]

概率三元组(ω,𝓕,P) 上随机变量的分布是 p(回想一下它是定义在上的函数),概率空间的度量。分布由定义(或者换句话说,概率由随机变量定义)

随机变量 x 的分布。

注意,有时候你也能看到一个随机变量的术语“概率定律”或“定律”,但它们是一回事。

连续性

我们已经知道,随机变量或分布可以是离散的,连续的,或两者的结合。但是仍然缺少一些东西,在这一节中,我们将回顾我们已经知道的东西并填补空白。

(1)离散:如果一个概率测度 μ 的所有质量都在每个单独的点上,那么它就是离散的。最简单的例子是掷硬币,其图形如下所示:

图 1.2 离散随机变量的一个例子。(抛硬币)

(2)绝对连续:这个性质既可以指随机变量,也可以指测度,定义不同。然而,它们有着内在的联系,所以我们将在这里讨论这两个问题,记住概率测度和随机变量之间的联系是很重要的。

a.首先我们看一下措施的案例。它是相对于作为参考的另一个度量来定义的。我们说ν相对于 μ 绝对连续,如果 ν(A) = 0 对于任意 A ∈ 𝓑 使得 μ(A) = 0νμ 之间的这种关系通常用 ν ≪ μ 来表示。[2]我们也可以说 μ 支配 ν。如果适用于 ν ≪ μμ ≪ ν ,那么我们说这两个测度是等价的,通常记为 ν ~ μ 。更直观的说,我们可以把 ν 看作一个更“精致”的测度,如果 μ 告诉我们一个集合的测度为零,那么 ν 就不会输出更大的结果。

b.在随机变量的情况下,随机变量x:ω→ℝ绝对连续的如果存在一个可积的非负函数 fₓ: ℝ → ℝ⁺ (这是概率密度函数)使得对于所有 x ∈ ℝ,

绝对连续随机变量的条件。

有时我们也可以看到 fₓ 在所有任何 Borel 集合上是可积的(元素在 𝓑 中),根据我们在随机变量一节中所讨论的,这两个版本的定义是相同的

出现了两个问题:1 .「连续性」和「绝对连续性」有什么关系?2.连续性和度量有什么关系?(现在我们只说随机变量)

首先我们来回答第二个问题:如这里所讨论的,如果 P(X = x) = 0 ,对于所有的 x ∈ ℝ,那么 X 是一个连续变量。(直观上,当随机变量连续时,它可以取实直线上的任何值,在这种情况下,测度给了我们“点的长度”)。现在我们来看第一个问题:从绝对连续性的定义,我们可以推导出

情商。1.3

为什么等式 1.3 适用是显而易见的:对单例集的积分为零。因为我们有 P(X = x) = 0 对于所有的 x ∈ ℝ ,如上所述,x 是连续的。因此,我们有这样的说法:

任何绝对连续的随机变量也是连续的。

而下面的事实在绝对连续性和度量之间架起了桥梁:

一个随机变量是绝对连续的当且仅当每个零测度集的概率为零。

(3)奇异连续:这里又有“奇异连续测度”和“奇异连续分布”,又有联系。并且实际上比之前的情况(连续性)彼此更加接近。我们将首先看一下这两个概念的定义,然后给出一个具体的例子。这是一个话题,很少包括在本科学习材料中。但是在我们了解这些之后,所有类型的概率分布都包括在内了。

如果你只是想理解吉尔萨诺夫定理,就没有必要去研究奇异连续性的细节,因为“绝对连续性”是非常重要的。然而,我相信学习概率论对理解所有这些类型的分布是有帮助的。

a.上的一个奇异连续分布是集中在一个勒贝格测度零的集合上的概率分布,但这个集合中每一点的测度也是零。

b.并且一个测度 μ 关于测度 λ奇异连续的如果 μ{x} = 0 对于所有的 x ∈ ℝ,但是有 s 个⊆ 其中 λ(S) = 0μ(S) = 0,这意味着【T34 也可以遇到另一个名称,奇异测量,并且可以看起来具有稍微不同的定义,但是这里为了简单和清楚,我们使用[1]中的版本,并且将避免术语“奇异测量”。

一些例子对于理解奇异连续性是有用的。在一维情况下(涉及一个随机变量),一个例子是康托尔函数。由于其形状,它也被称为“魔鬼楼梯”。(见图 1.4)这样的累积分布函数(CDF)是连续的但几乎处处零导数(这个词我们在之前就见过了)。离奇的是,它不是按导数增长,而是确实按构造增长。

图 1.4 康托尔函数。(图片来自交叉验证。)

较少病态的例子存在于更高维度(想想这个:我们要构造一个勒贝格测度为零的集合,在集合中,每一个点也会有测度为零——自然,这将是类似于“一条线的面积”或者“一个平面的体积”的东西,在一维空间中是找不到的。),这里会考虑几个随机变量的联合分布。这里我们将展示一个例子,其中两个随机变量取自[4]:

有两个设备,它们产生从 0 到 1 的随机数。但是,其中一个坏了,总是给 0。统计学家连续两天每天使用其中一个。他从这两个设备中随机选择(当然,有可能他两天都使用同一个设备)。设 X 表示第一天获得的数量,Y 表示第二天获得的数量。X 和 Y 的联合分布是什么?

我们很容易看出,这种分布既不是绝对连续的,也不是离散的,因为好器件的输出分布是均匀的,而坏器件的输出分布是离散的。为了分析它实际上是什么,我们也将通过这个例子来讨论概率分布的分解。

为了更直观的理解,我在这里提供了一个蹩脚的分布函数:

图 1.5 和 y 的联合概率密度函数(图片由作者提供。)

注意 f(0,0) = 1/2,这意味着一半的质量集中在点(0,0)上。它对应的事件是,在这两天,统计学家得到的输出为零。而另一半质量均匀地分布在一个接一个的单位正方形上(体积为 1 × 1 × 1/2 = 1/2)。我们还可以在 CDF 中找到奇异分量。不失一般性,我们先试着算出 X 的累积分布。根据勒贝格分解定理,任何概率测度 μ 都可以分解为

等式 1.6 测度分解。

由于 CDF 唯一确定随机变量的分布(考虑定义),我们也可以将 CDF 分解为等式 1.6 给出的形式,即

方程 1.7 CDF 的分解。

X 的 CDF 没有任何奇异部分。并且可以容易地确定离散部分和绝对连续部分:

方程 1.8,1.9 x 的 CDF 的离散部分和绝对连续部分。

其中 Iᵣ(x)表示以下函数

因此,我们有

方程 1.10 的 CDF。

根据我们之前讨论过的独立同分布(i.i.d .)假设,Y 与 X 具有完全相同的 CDF,由等式 1.10 给出。此外,由于 i.i.d .假设,X 和 Y 的 CDF 简单地为 F(x,y) = F(x)F(y),这样我们就得到

方程 1.11 和 y 的联合 CDF。

X 和 Y 的联合 CDF 具有预期的所有三个部分。奇异连续部分对应的是其中一个器件坏了而另一个器件好的事件,对应的是图 1.5 中给出的 X 和 Y 的联合概率密度函数图中的阴影区域——它们在三维空间中有勒贝格测度零,在这个空间上定义的测度对应的是方程 1.11 中的奇异连续部分。但是在这个区域里,每个点的测度都是零。

现在我们回头看看奇异连续测度的定义,显然,我们可以称阴影区域上的测度为 μ ₁(假设其中一个器件坏了——子样本空间),另一个为 3d 空间(原始样本空间)中的 μ ₂,我们很容易看到 μ ₁相对于 μ ₂.是奇异的

Radon-Nikodym 定理和测度变换

Radon-Nikodym 衍生物

Radon-Nikodym 导数实际上是两个度量的比值,与实分析中的“导数”无关,因为实分析中的导数描述了函数变化的速度,但 Radon-Nikodym 导数与此完全不同。然而,它们确实有一些相似之处。我们可以从 Radon-Nikodym 导数的以下性质看出这一点:设 ν 是测度空间(ω,𝓕)【7】上的σ-有限的测度,(t14)ν是σ-有限的意思是ω是具有有限测度的集合的可数并)

(1)若μ是测度, μ ≪ νf ≥ 0μ 可积函数,则

右侧的 dν不知何故“取消”了。

(2)如果 μᵢ,i= 1,2 都是测度并且 μᵢ ≪ ν ,那么 μ₁ + μ₂ ≪ ν

(3)(链式法则)如果𝜏是测度, μσ-有限测度, 𝜏 ≪ μ ≪ ν ,那么

链式法则。

如果 μν 相等,则

氡-尼科代姆钍射气

Radon-Nikodym 定理展示了一种通过某种线性算子用另一个度量来表示一个度量的方法。在这种情况下,表示是通过积分算子(积分器——这个术语在这里使用)。Radon-Nikodym 定理说

给定一个可测空间(ω,𝓕)* ,若(ω,𝓕) 上的σ-有限测度ν相对于(ω,𝓕) 上的σ-有限测度μ绝对连续,则ω上存在一个非负可测函数 f,使得*

对于任何可测集 e。

为什么这个定理很重要?一点是可以用来说明条件期望的存在性。在我们给出条件期望的定义之前,我们需要知道这样一个事实,即给定σ代数𝓕,用 E[X | 𝓕]表示的随机变量 x 的条件期望本身是一个(𝓕-measurable) 随机变量。定义如下:

我们称随机变量 y 为 x 的条件期望,即 Y = E[X | 𝓕].需要满足两个条件

y是𝓕-measurable.

②对于所有的 A ∈ 𝓕 ,我们有

1_A 表示字符(指示器)功能。更直观的说,这意味着给定信息 𝓕 (参考这个看为什么叫“信息”和什么意思), YX 的最佳预测。并且可以使用 Radon-Nikodym 定理证明的以下定理显示了条件期望的存在性和唯一性:

考虑一个概率空间(ω,𝓕,P* ),其上的一个随机变量 X ,以及一个子σ代数𝓖 ⊆ 𝓕.如果 E[X] 是明确定义的,那么存在一个𝓖-measurable 函数e[x|𝓖】对于p-零集合是唯一的(度量 P 在那些集合上给出零),使得*

我们在这里提到 Radon-Nikodym 定理的最重要的原因是,它与度量的变化密切相关,而度量的变化是 Girsanov 定理中的基础。现在我们提出下面的定理:

如果 PQ 是等价的概率测度,并且 X(t) 是一个 𝓕ₜ 适应的过程,设 dQ/dP 是 Radon-Nikodym 导数并且l(s)=eₚ[dq/dp|𝓕ₛ*,那么对于 s < t*

第二个等式很难证明,所以这里不包括它,但是第一个等式非常容易用 Radon-Nikodym 导数的性质(1)来表示:

并且 Radon-Nikodym 定理保证 Radon-Nikodym 导数 dQ/dP 存在,也就是所谓的 X密度

吉尔萨诺夫定理

首先,我们简要介绍什么是维纳测度,因为下面我们将讨论维纳过程和带有漂移的维纳过程。维纳测度是定义在维纳空间上的测度,维纳空间是区间【0,1】上连续实值函数 x 的空间C【0,1】。我们可以用σ代数和测度来构造一个可测空间。而维纳测度是这个空间上的唯一测度,它给每一个有限路径分配一个概率。[5]直观上,集合的维纳测度是维纳过程轨迹是该集合的成员的概率。

Girsanov 定理描述了随机过程如何随着度量的变化而变化。更准确地说,它通过给出它们之间的似然比(Radon-Nikodym 导数)的显式公式,将 Wiener 测度 P 与连续路径空间上的不同测度 Q 联系起来。还需要应用的是 PQ 是等价的,这在连续性一节中讨论。以及更多的洞察力:

Girsanov 定理指出,具有不同漂移的新过程可以从具有等效度量的过程中构造出来——随机过程的漂移变化不会导致度量的剧烈变化。事实上,度量值如何变化,可以明确地计算出来。

Girsanov 定理的一个稍微不同的版本指出,可以为随机过程找到一个新的表示,该随机过程具有不同的漂移和测量。

吉尔萨诺夫定理还有一个相反的版本,它陈述了如果我们有一个带有测度 P 的过程 X(t) ,和另一个带有测度 Q 的过程 Y(t) ,其中 Q ~ P 。那么 X(t)Y(t) 可以用漂移变化的相互关系来表示。我们将在本节中介绍这三个版本。[6]

吉尔萨诺夫定理 I

设 W(t)是滤波概率空间上的维纳过程

Exp。3.0 维纳过程的过滤概率空间。

Y(t) ∈ ℝⁿ 为形式的 n 维itprocess

其中 T ≤ ∞ 为常数 W(t) 为 n 维维纳过程。让我们测量

等式 3.1 测量值的变化。

假设 a(s,ω) 满足诺维科夫条件,这是过程 M(t) 为鞅 : 的充分条件

诺维科夫的情况。

其中 E = Eₚ 是关于 p 的期望。定义概率空间 Y(t)(ω,𝓕_T)* 上的测度 q,通过*

方程 3.2 测度的 Girsanov 变换。

那么 Y(t) 是关于概率测度 Q 的 n 维维纳过程,对于 t ≤ T

在这个定理中, Y(t) 是维纳过程的漂移移动后的新过程。我们可以看到, a(t,ω) 就是漂移,它随时间变化。 Y(t) 的度量是通过经由等式 3.2 中给出的过程 Girsanov 变换来变换原始度量(维纳度量) P 而给出的。等式 3.1 给出的M(t)是过程 W(t)Y(t) 之间的度量变化的显式公式,这意味着它允许我们直接计算 Radon-Nikodym 导数。我们可以从等式 3.2 中看出这一点:

吉尔萨诺夫定理 II

这个版本的 Girsanov 定理的公式非常冗长,因为它对于证明是有用的。但是这篇文章并没有给出证明,因为文章的目的是为了理解 Girsanov 定理(如此多不同的公式)是怎么回事,这已经够难的了。第二个版本显示了当过程的漂移改变时(或者我们如何找到过程的不同表示,如我们之前提到的),度量如何改变,给出如下:

Y(t) ∈ ℝⁿ 是形式的一个过程

其中 W(t) ∈ ℝᵐβ ∈ ℝⁿθ ∈ ℝⁿˣᵐ 。假设存在过程 u(t,ω)α(t,ω) 使得

并假设 u(t,ω) 满足诺维科夫条件

情商。3.3

等式 3.4

度量值 PQ 的定义与吉尔萨诺夫定理 I 中的定义相同,这意味着等式 3.3 中的 E = Eₚ ,等式 3.2 也适用。然后

等式 3.5

是关于测量值 Q 的维纳过程,并且过程 Y(t) 具有关于 W_hat(t) 的另一种表示

在吉尔萨诺夫定理 II 中,我们可以看到 Y(t) 的漂移从 β(t,ω) 变为 α(t,ω) 。此外,作为旁注,进程 u(t,ω)α(t,ω) 应该满足某些条件,这里省略这些条件是为了使大图清晰。

吉尔萨诺夫定理的逆定理

在逻辑中,陈述 p → q 的逆命题是 q → p 。在吉拉诺夫定理中,简单来说, p 是新的测度, q 是新的过程。然后,相反的情况被公式化(关于 Girsanov I,因为它更简单)为:

设 p 是维纳过程的维纳测度 W(t)Q~P(ω,𝓐)* 上的等价测度。然后,存在一个随机过程 α(t,ω) ∈ ℝⁿ0 ≤ t ≤ T ,根据 W(t) 的历史进行调整(见表达式 3.0),使得*

是概率空间上的维纳过程(ω,𝓐,Q)* 。并且相应的 Radon-Nikodym 由等式 3.1 给出。为了方便参考和这个公式的重要性,我们再次粘贴到这里*

Radon-Nikodym 导数。

摘要

在这篇文章中,我们从概率分布的连续性开始——离散、绝对连续和奇异连续。然后介绍了 Radon-Nikodym 导数和理论,这是 Girsanov 定理的基础。最后一节介绍了 Girsanov 定理。Girsanov 定理的两个公式被包括,一个是从 Wiener 测度到另一个测度的变化,另一个是在两个不同的测度之间切换。最后,我们证明了 Girsanov 定理的逆定理。Girsanov 定理的一个非常有趣和有用的应用是用 Black-Scholes 公式鞅来建模资产价格。这篇文章不包括它,因为它已经够长了。我们将在即将发布的关于金融衍生品和布莱克-斯科尔斯公式的文章中看到它。😃

参考资料:

[1]罗森塔尔,J. S. (2006 年)。先看严谨的概率论,一个。世界科学出版公司。

绝对连续措施数学百科全书。**

[3]戴夫·纽霍夫, 连续和绝对连续随机变量 (2003)。2022 年 7 月 2 日访问。

[4]库普曼斯,L. H. (1983 年)。 给本科生讲授奇异分布美国统计学家37 (4a),313–316。

[5] 维纳测度 (2013)。2022 年 7 月 5 日访问。

[6]ksen dal,B. (2003 年)。https://link.springer.com/chapter/10.1007/978-3-642-14394-6_5?noAccess=true随机微分方程。在随机微分方程(第 65–84 页)。斯普林格,柏林,海德堡。**

[7]谢尔·多克萨姆。(2008). Radon-Nikodym 衍生物。于 2022 年 7 月 9 日访问。

[8] 拉东-尼科代姆定理 (2010)。于 2022 年 7 月 9 日访问。

[9]e . a .泽奥多洛和 e .托多洛夫基于 Girsanov 的直接政策梯度方法

更改日志

  1. 条件 1.1 下的文字右:我写的不是“ωX 的定义域”,而是“ω是 X 的像”。

随机过程导论(1)

原文:https://towardsdatascience.com/an-introduction-to-stochatic-processes-8c0b51ca73a9

伊托引理:定义和应用

在这篇文章中,主要话题是 ITM 引理,它在金融数学中起着重要的作用,并且是处理随机过程的有用工具。可以找到很多关于这个话题的文章和文献,但是很少包括背景的介绍,比如维纳过程,滤波等等。他们通常假设读者已经熟悉这些概念。当然,搜索相关的上下文是很容易的,但是把所有的东西放在一起要花相当长的时间。这就是这篇文章试图帮助解决的问题——将 ITM 引理与随机过程的相关概念联系起来。

维纳过程的 100 个模拟轨迹(图片由作者提供)

先决条件

维纳过程

我们引入的第一个重要概念是维纳过程,它是 it4 过程的一部分(我们将看到,在 it4 过程中,它充当积分器)。它被定义为一个随机过程(或随机过程,由一个指数集[4]排序的随机变量的集合),具有以下四个性质:

  1. 初始值 W(0) = 0
  2. 维纳过程几乎必然连续(但不可微):以概率 1 ,函数 t → W(t)t 连续。(我们说“几乎”,因为可以有一组 t 的勒贝格测度为 0 ,在此处函数不连续。)它适用于绝对连续性意味着几乎肯定的连续性。关于几乎肯定是连续但不是连续的维纳过程的例子,参见这个 StackExchange 答案
  3. 进程 {W(t)} ₜ≥₀ 具有固定的、独立的增量。
  4. 增量 W(t+s)-W(s) 具有正态分布 N(0,s) (我们用这个来表示均值 μ=0 方差 σ =t 的正态分布)。重要的是要注意增量是独立的,只有当它们不与重叠时。并且可以很平凡的推导出一个维纳过程 W(t) (从 W(0) 开始)的分布是 N(0,t)

过滤和鞅

过滤的定义非常直观。考虑一个概率空间(ω,𝓕,P) (参见此处,关于概率空间的基础知识和符号的意义)过滤是一个σ-代数的序列,𝓕₀,,…,𝓕ₙ,具有性质 𝓕₀ ⊂ 𝓕₁ ⊂ … ⊂ 𝓕ₙ 。但是,好像很抽象。直观地说,过滤包含到时间 t 为止的所有信息。为了更好地理解它,我们可以使用下面的例子(它很长,但绝对值得一读,因为不理解什么是过滤,就不可能理解基于它的其他定义):

我们掷两个骰子,设 S = {1,2,3,4,5,6} ,设样本空间为ω= S×S,这是两次投掷所有可能结果的集合(每个结果都是有序对)。在时间零点,我们不掷任何骰子,σ-代数是𝓕₀={∅,ω},因为,在这种状态下,没有额外的信息(单词“信息”指的是在这一步“可能发生的事情”)被添加到 σ- 代数中,我们只知道 (1) 任何结果都是可能的——ω∈𝓕₀,其中ω是事件(我们知道事件是中的一个元素 (2) 无一胜负——,这是ω的补数(我们知道在一个 σ- 代数中如果a∈ω,那么ā∈ω)。因为如果掷骰子,肯定会有一些结果,事件∅的概率为零, P(∅) = 0

在第一次投掷时,第一次投掷的信息将被添加到包含第一次投掷后所有可能事件的 σ- 代数-𝓕₁中。比如 {2,4,6}×S ∈ 𝓕₁ ,因为可能是第一次抛的结果是偶数,对应事件 {2,4,6} ,至于第二次抛,可以是 S 中的任何东西。同理,也是 {1,3,5}×S ∈ 𝓕 ₁,在这种情况下,第一次抛硬币的结果是奇数,对应事件 {1,3,5} 。同理可以得出结论,𝓕₁=**𝒫(s)×s(2⁶元素)其中 𝒫(S) 表示 s 的幂集,表示第一次抛的所有可能事件。

在第二次投掷时,第一和第二个脚趾上都有信息。𝓕₂包含了第二次投掷后所有可能的事件。这就意味着𝓕₁=**𝒫(s×s)(2⁶元素) 【5】

与随机过程 X(t) 相关的自然过滤或生成过滤是一种过滤,使得在每个时间 t,随机过程 X(t)是 𝓕 ₜ-measurable.形式上,

𝓕1.0 道具 ₜ-measurable

这里使用 Borel 代数,因为随机过程的状态参数通常是实数线,其 σ 代数是 Borel 代数。并且我们说随机过程 X(t)是适应过滤 𝓕 ˣ.一个重要的事实是

随机过程 X 总是适应其自然过滤。

此外, 𝓕 ₜ是最小的σ-代数,从而满足性质 1.0。直观上, 𝓕 ₜ代表了 t 时刻的所有可用信息,这意味着过滤 𝓕 ˣ代表了随机过程的信息(这已经通过上面的例子说明了)随时间的演化, X(t) 的值只取决于在 t 之前的演化。

既然我们知道了什么是过滤和适应过程,现在就很容易理解什么是鞅了。考虑一个适应的随机过程 X(t),0 ≤ t≤ T,过程 X(t)是一个如果它没有上升或下降的趋势。形式上,

等式 1.1 鞅

我们有如下观察结果:

维纳过程是关于其自然过滤的鞅。

这可以显示如下

维纳过程是鞅的证明。

证明过程是鞅的另一种可能方式是通过 ITT 引理,我们将在后面讨论 ITT 引理的应用时看到这一点。

二次变分

二次变化是随机过程的一种变化(不同变化的一个例子是线性变化)。设 X(t) 为任意过程,则 X(t) 的二次变化又为随机过程,定义为

等式 1.2 二次变差。

其中p ={0=t₀<t₁<…<tₙ= t }是一个分区,其范围超过时间间隔【0,t】的分区,并且 ||p|| 表示该分区中最大间隔的网格:

Def 1.3 目。

可以证明维纳过程的二次变差 W,W = t 几乎必然。

It 积分

首先,我们将介绍什么是“积分”以及它是如何构造的。它就像正态积分一样,但是积分是随机的。它推广了 Riemann-Stieltjes 积分,后者是 Riemann 积分的推广。

更确切的说:黎曼积分的推广发生在“积分器”被一个函数代替的方式,这个函数原本是一个无穷小的数(我其实没见过“积分器”这个术语被用在黎曼积分的上下文中,但图 2.1 会把事情说清楚);it0 积分是黎曼-斯蒂杰斯积分的随机推广。

图 2.1 被积函数和积分器。

积分的定义

积分实际上是什么,如下定义所示:

f ∈ 𝒱(S,T) 。那么 f 的 it0 积分(从 ST )定义为

情商。2.2 积分的定义。

其中 {ϕₙ} 是一系列简单函数使得

Exp。2.3 等式中应满足的{ϕₙ}的性质。2.1.

注意,表示维纳过程的 Wₜ有时被表示布朗运动的 Bₜ所代替。[1]我们不需要担心这个,因为它们是一回事。在本文的其余部分,我们将采用 S=0 ,这是通常的情况—该过程从 t=0 开始。本文中提到的简单函数,有时也被称为“初等函数”。简单函数的定义在[2]中给出:

一个函数 ϕ∈ 𝒱 被称为简单,如果它具有以下形式

Def 2.4 初等函数。

χ 表示特性(指标)功能。不完全清楚,“简单函数”和“初等函数”是否相同。根据[3],它们是同一个东西;根据这个对于初等函数来说,集合上的条件更宽松,在集合上定义 χ :有可数无穷多个,但对于简单函数来说,只能有有限个。但这并没有给我们带来任何麻烦,因为在 Def 2.4 中,有可数无穷多的集合【tⱼ,tⱼ₊₁】两个术语都可以,我们将坚持使用“简单函数”。

定义中的 𝒱 代表函数类

满足以下条件:

  1. (t,ω) → f(t,ω)𝓑×𝓕-measurable,其中 𝓑 表示 [0,∞) 上的 Borelσ-代数。
  2. f(t,ω)𝓕ₜ-适应的。
  3. f(t,ω) 是一个L-函数:它是平方可积的,积分是无穷的。

f(t,ω) 应该是一个 L 函数

表达式 2.3 可能看起来很奇怪,但它直观地意味着 {ϕₙ} 是对 f(t, ω ) —想象 ϕₙ 是一个阶跃函数,当阶跃变得无穷小时, ϕₙf(t, ω 相同。数学上,表达式 2.3 意味着it4 积分定义为简单函数的 it4 积分序列的 L 极限。**

it4 过程和扩散过程

过程或随机积分是(ω,𝓕,p)上适应于𝓕ₜ的随机过程,可以写成以下形式

情商。3.1 It 流程。

其中函数 U,V ∈ 𝓛₂ 。我们可以看到第一部分——函数 U 的积分是确定性的。这是一个黎曼积分。第二部分——函数 V 的积分是随机的,并且是过程 Xₜ 中唯一的噪声源,它是一个it4 积分。

等式 3.1 也可以根据随机微分以简化形式书写:

情商。3.2 随机微分过程。

随机过程 X(t)随机微分方程* (SDE)由下式给出***

方程 3.3 随机微分方程的公式。

以方程 3.3 的形式给出的 SDE 被称为 It 型 SDE。(当然也有其他不同类型的 SDE,如 SDE。)并且这种 SDE 的解决方案被称为 it扩散,它是扩散过程的一个特例。一个过程是一个扩散过程,如果

  1. 时间变量 t 是连续的。通常我们假设 t 是为所有非负实数定义的。
  2. 它是一个具有转移累积分布函数的马尔可夫过程

方程 3.3.1 过渡累积分布函数。

和转移概率密度,其被定义为等式 3.3.1 的偏导数(如果存在)

方程 3.3.2 转移概率密度函数。

3. Xₜt 的连续函数。

扩散过程的性质完全由两个特征决定:无穷小均值和无穷小方差,以及可能的边界条件。无穷小平均值定义如下

方程 3.3.3 无穷小均值(第一个无穷小矩)

无穷小方差定义为

等式 3.3.4 无穷小方差(第二个无穷小矩)。

这两个特性都可以从公式 3.3 中直接读出。

SDE 的例子

SDE 的一个非常重要的例子如下

方程 3.4 几何布朗运动的公式。

方程 3.4 给出的 X(t) 的解是几何布朗运动。它是由 ITT 引理解决的,这将在 ITT 引理应用的第二个例子中说明。具有初始条件 X(0) = x₀ 的方程 3.4 的解由下式给出

方程 3.5 几何布朗运动的显式表达。

SDE 的另一个重要例子是奥恩斯坦-乌伦贝克过程,由下式给出

奥恩斯坦-乌勒贝克过程的方程 3.5.1 SDE。

其中 m,α,σ∈ℝ是一些常数。方程 3.6 的直观物理意义是一个质点的运动,该质点随着强度 σ的噪声向 m 级漂移。Xₜ >米时漂移为正,当 Xₜ <米时漂移为负。由于这个特征,Ornstein-Uhlebeck 过程也是均值回复过程。在金融中,奥恩斯坦-乌勒贝克过程用于瞬时利率的瓦西切克模型,其中 σ 是波动率, α 是逆转速度, m 是长期平均水平。解决方法是

方程 3 . 5 . 2 O-U 过程的解。

假设过程从 t=0 开始。

It 等距图

itô integral 定义的思想是首先使用一个简单的函数类来定义积分,然后将其扩展到整个类 𝒱 。为此,我们可以使用等距法。

ITO 等距的引理表述如下:如果 ϕ(t, ω ) 是简单函数且有界

等式 3.6 是等距图。

构造的步骤在[2]中有详细的论述,但这里我们只说明证明的思路:我们要证明简单函数的随机积分序列

简单函数的随机积分序列

在具有有限二阶矩的随机变量的 L-空间中形成一个柯西序列(随着序列的进行,元素变得彼此任意接近)。由于 L-空间是完备的(我们提到这一点是因为,在一个完备的度量空间中,每个柯西序列收敛于那个度量空间中的一个元素),方程 3.7 给出的序列收敛于 L-空间中的一个随机变量。现在我们试图证明极限是柯西的:

等式 3.8

从这里,财产

我们可以立即看到

等式 3.9

结合方程 3.8 和方程 3.9,证明了方程 3.7 给出的序列是柯西序列。[7]

等角变换的另一个重要应用是计算随机变量的方差,它以积分的形式给出。我们将在这里展示一个例子:求下列过程的方差:

等式 3.10 这个的方差是多少?

利用维纳过程的性质,很容易看出方程 3.10 给出的 X(t) 的期望值为零。注意,因为我们在计算每次 t 的方差, t 在这里被认为是一个常数。使用“等距”计算差异,如下所示:

结果。

伊托引理

it’s 引理给了我们一种寻找随机方程的微分的方法(注意我们用“微分”代替“导数”是因为随机过程是不可微的),在一些文献中,我们可以看到它被描述为“微积分中链式法则的随机对应物”。动机是对于时间的非随机合成函数 u = f ∘ g = f(g(t))t ≥ 0 ,其中 fg 可微,计算 u 的导数很容易,我们只需要应用链式法则:

情商。4.1 链式法则。

然而,这对随机方程不起作用。在这种情况下,我们需要的是 It 的引理,其给出如下:

{X(t)} 为随机微分方程(SDE)方程 3.2 给出的 it4 过程。设 g(t,x):[0,∞)×ℝ为两次连续可微函数。然后随机过程

是一个具有随机微分的 it4 过程

情商。Y(t)的 4,2 SDE 表示

以下规则适用:

在等式 4.2 中,前两项来自普通链式法则,如果 X(t) 是二阶可微函数,则该法则适用。同时,最后一项是修正项,是随机过程的新项, (dX(t)) 是过程 X(t) 的二次变量。

应用伊托引理的例子

为了理解 ITM 引理是如何工作的,我们可以看看下面的例子。

  1. 寻找随机过程的 SDE。考虑随机过程

求导非随机函数的方法是行不通的。我们需要在这里应用它的引理。我们怎么做呢?第一步,构造辅助函数(这是一个很不正式的术语) g(X(t),t) 。这里我们使用

情商。4.2“助手功能”。

我们写 g(x,t) 而不是 g(X(t),t) ,只是为了看起来更好,因为当我们计算 g(x,t) 的偏导数时,我们认为它是一个包含两个变量 X 和 t 的函数,并暂时忘记了 X 是 t 的函数这一事实。下一步如下

方程 4.2 的导数。

其中 gₓ表示 g(x,t) 关于参数 x 的一阶导数,gₓₓ是 g(x,t) 关于 x 的二阶导数,以此类推。而在这种情况下,x 扮演的角色实际上是 W(t) 。将导数代入方程 4.2,我们很容易得到

ITT 的公式。

这意味着

2.将一个 It 流程转变为另一个流程。这个例子比上一个稍微复杂一点,因为我们需要做一些替换。假设股票价格 S(t) 遵循几何布朗运动:

Eq 4.3 股票价格模型

找到 Sₜ 的的 SDE。

这里我们应该选择哪个辅助函数是显而易见的,我们让 g(x,t) = ln x 并计算必要的导数

应用 ITT 的引理,我们得到

方程 4.4 在将伊托引理应用于方程 4.3 之后

我们可以用 dS(t) 来处理红色方块中的部分。从方程 4.3 我们可以很容易地得到

等式 4.5

将等式 4.5 代入等式 4.4,我们关注红色方块中的部分:我们将其展开,然后可以使用 ITM 引理中陈述的规则:

将这个表达式代入方程 4.4,我们得到

等式 4.6 结果。

其中蓝色方块中的表达式直接来自等式 4.3。(这种替代的一些步骤被简化了)。在积分方程 4.6 的两边之后,我们得到几何布朗运动的解。此外,根据等式 4.3,假设初始股票价格为 S₀,则我们有

等式 4.7

其中 d ln S(t) 由时间 t 处的对数返回和初始值之间的差值代替。(回想一下“ d ”的意思是不同。)我们可以看到,一个几何布朗运动的对数是一个带漂移的维纳过程。等式 4.7 告诉我们对数收益的条件分布(在初始价格的条件下)是正态的。利用维纳过程的性质,我们可以很容易地读出分布的条件均值和方差

等式 4.8 对数收益的条件分布。

这意味着收益服从对数正态分布。

3.利用 ITM 引理识别鞅。考虑过程

等式 4.9

S(t)是鞅吗?第一步是找到 SDE 和它的积分形式。这里的辅助函数是

其具有与 S(t)相同的形式,并且是一个直接的选择。然后,我们计算必要的导数,并插入公式:

方程 4.10 方程 4.9 的 SDE

积分形式是

方程 4.11 方程 4.10 的积分形式。

方程 4.11 中,右边的积分是鞅,左边的不是(原因见此处)。因此随机过程 S(t)不可能是鞅。

总结

在这篇文章中,我们展示了 ITT 引理的定义和上下文:ITT 积分,ITT 过程,随机微分方程,以及一些先决条件,如过滤,适应过程,鞅和二次变差。最后给出了引理的三个应用:1 .寻找随机过程的 SDE;2.将一个随机过程转换成另一个随机过程;3.检验随机过程是否是鞅。

参考文献

[1]埃瑟里奇和巴克斯特(2002 年)。金融微积分课程。剑桥大学出版社。

[2]ksen dal,B. (2003 年)。随机微分方程 。在随机微分方程(第 65–84 页)中。斯普林格,柏林,海德堡。

[3]弗洛雷斯库本人(2014 年)。 概率与随机过程 。约翰·威利的儿子们。

【4】随机过程及其分类 。2022 年 5 月 20 日访问。

概率论中过滤的例子 。2022 年 6 月 12 日访问。

[6]惠特,W. (2007 年)。 随机微积分快速入门 。2022 年 6 月 13 日访问。

【7】克森达本 中的 It“积分”的构造。2022 年 6 月 16 日访问。

https://folk.ntnu.no/jarlet/ST2101-2008v/diff.pdf【8】扩散理论。6 月 18 日访问。

https://www.math.cmu.edu/~gautam/sj/teaching/2016-17/944-scalc-finance1/pdfs/ch3-int.pdf随机积分。6 月 19 日访问。**

监督编码方法介绍

原文:https://towardsdatascience.com/an-introduction-to-supervised-encoding-methods-add83390686f

探索使用标记数据转换分类特征

luis gomes 的照片:https://www . pexels . com/photo/close-up-photo-of-programming-of-codes-546819/

像 one hot 编码和 ordinal 编码这样的编码技术目前主导着数据科学领域。它们是无监督的方法,这意味着它们仅基于分类特征的值来转换分类特征。

也就是说,还有一个不太知名的编码方法分支:监督编码方法。监督编码方法需要使用目标标签来导出分类数据的数字表示。

目前有大量的文献支持某些监督编码方法,因此值得掌握使用目标标签对分类特征进行编码的概念。

使用目标标注对要素进行编码

对于那些习惯于只使用像一个热编码这样的无监督编码方法的人来说,使用目标标签来编码值可能看起来像一个陌生的概念。

作为介绍,我们可以进行一种最简单的监督编码方法:目标编码。目标编码需要用相应目标标签的平均值来表示类别。

让我们使用下面的虚拟数据。

由作者创建

在这里,每种水果都有相应的目标。在这种情况下,我们将数据按水果分组,并找出每种水果的平均价格。这些平均值将用于表示水果类别。

由作者创建

利益

使用标记数据对分类特征进行编码的优势一目了然。

首先,编码方法直观且易于执行。

它还使用户能够在不增加数据集中列数的情况下转换分类特征。在这方面,监督编码方法优于像热编码这样的方法,热编码容易受到维数灾难的影响。

缺点

像任何技术一样,监督编码方法也有自己的缺点。

首先,用这种方法建立的模型容易过度拟合。毕竟,分配给分类特征的数值完全取决于目标标签在训练集中的分布,因此存在用此数据训练的模型不能用看不见的数据正确概括的风险。

其次,监督编码方法可能不足以代表少数类别。

最后,如果监督编码方法执行不当,随后的模型将遭受数据泄漏,这可能发生在数据被分割之前的应用变换时。

潜在的解决方案

自然,那些寻求解决监督编码方法风险的人有许多工具和技术可供他们使用。

用户可以将某种形式的正则化合并到他们的转换中。他们可以应用平滑技术,这需要在类别平均值和全局平均值之间指定一个值。另一种选择是将高斯噪声添加到训练数据中,以减轻模型过拟合。

用户也可以选择使用更复杂的编码方法。毕竟,像目标编码这样的技术是建立在过于简单的逻辑之上的,可能不适合编码特性。替代目标编码的一个例子是留一个编码,这需要取除当前行之外的所有行的目标标签的平均值。

Python 中的监督编码

现在的大问题是:我们如何将监督编码方法纳入我们的特征工程程序?

对于 Python 用户来说, category_encoders 包提供了各种监督和非监督转换器,可用于对分类特征进行编码。

此外,该包中的变压器与 Scikit Learn 管道兼容,因此它们可以与其他功能工程工具结合使用!

总的来说,这个包是那些希望使用监督编码方法来转换分类特征的人的天赐之物。

现在,我们将使用他们的一个转换器来执行目标编码。让我们使用相同的虚拟数据。

由作者创建

首先,我们可以创建一个TargetEncoder对象。

注意smoothing超参数当前设置为 0。这意味着在变换中不会应用平滑。

然后,我们可以使用这个对象来转换虚拟数据。

代码输出(由作者创建)

现在,我们可以使用这个转换器为看不见的数据编码。假设我们正在使用下面的测试集。

由作者创建)

TargetEncoder对象可以使用来自训练数据的参数为测试数据中的每个类别分配数值。

代码输出(由作者创建)

请注意,“Apple”和“Orange”类别的赋值与它们在训练数据中的赋值相同,而“Grape”类别的赋值是目标标签的全局平均值。

结论

照片由 Prateek KatyalUnsplash 上拍摄

虽然本文没有探索监督编码方法的能力,但是它已经让您熟悉了使用目标标签来转换分类特征的想法。

最终,像热编码这样的无监督技术可以完成这项工作,但它们并不总是理想的选择。一般来说,最好不要依赖某些技术,总是愿意探索相同问题的新解决方案。

我祝你在数据科学的努力中好运!

张量流简介

原文:https://towardsdatascience.com/an-introduction-to-tensorflow-fa5b17051f6b

了解机器学习框架、其架构以及与 PyTorch 的比较

照片由 Unsplash 上绘制的迪兹·格拉汉姆

TensorFlow,简称 TF,是谷歌开发的一个用于 D eep 学习人工智能的框架,最初只在内部使用。然而,几年来,它一直是开源的,可以用于许多编程语言,如 Python

什么是张量流?

TensorFlow 是谷歌的一个开源框架,用于创建机器学习模型。虽然该软件是用 C++编写的,但它是独立于语言的,因此可以很容易地用在各种编程语言中。对于许多用户来说,这个库现在已经成为机器学习的标准,因为通用模型可以相对简单地构建。此外,最先进的 ML 模型也可以通过 TF 使用,如各种变压器。

通过 TensorFlow Keras(高级 API ,可以额外构建单独的神经网络,而无需手动编程各个层。这使得 TF 可用于各种各样的应用程序并可定制。此外,它在自己的网站上提供了各种免费的入门课程和示例,这进一步促进了框架的使用。

张量是什么?

TensorFlow 这个名字乍一看可能有点奇怪,因为它与机器学习没有直接联系。然而,这个名字来自于所谓的张量,张量用于训练深度学习模型,因此构成了 TF 的核心。

张量是线性代数中的一个数学函数,它将选择的向量映射为一个数值。这个概念起源于物理学,随后被用于数学。可能使用张量概念的最突出的例子是广义相对论。

不同种类的多维数组|来源:作者

机器学习领域,张量被用作许多应用的表示,例如图像或视频。这样,许多信息,其中一些是多维的,可以在一个对象中表示。例如,图像由大量单独的像素组成,这些像素的颜色值又由三个颜色层的叠加组成(至少在 RGB 图像的情况下)。这种复杂的结构可以用张量简洁地表示出来。

TensorFlow 是如何工作的?

现在我们已经了解了什么是张量,张量在机器学习中有什么影响,我们现在来处理名字的第二部分,即“流”。在 TF 中,构建的模型被表示为数据流,更准确地说是在有向图中。这意味着我们在 TensorFlow 中建立的每个模型都可以转换为有向图,即每个箭头只能在一个方向上遍历。

在图中的每个节点执行计算操作。在神经网络的例子中,这意味着,例如,节点是计算操作发生的单独层。另一方面,边是已经描述过的张量,从一个节点移动到下一个节点。

https://medium.com/illumination/intuitive-guide-to-artificial-neural-networks-5a2925ea3fa2

然而,自 2019 年以来,这种结构发生了一些变化,因为第二个版本于当年发布,改变了一些甚至是基本的功能。此后,使用高级 API Keras,它在第一个版本中仍然是一个单独的模块。这提供了一个相当简单的方法来建立一个神经网络,只需调用单独的层,从而使 TF 更加用户友好。

# Building a neural network by defining the single layers 
import tensorflow as tf
import tf.keras.layers as layersmodel = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))

此外,更新还使 TF 在处理更复杂、更现代的机器学习模型方面明显更具性能。但是,这两个版本之间不兼容,因此必须重写旧代码才能在 TF 2.0 中工作。但是,仍然支持第一个版本,所以您也可以在这个版本中运行代码,尽管没有新的特性。

如何安装 TensorFlow Python 版本?

TensorFlow 可以相对容易地安装在 Python 中,就像许多其他模块一样,只需一个终端命令。在 Jupyter 笔记本上,多一个“!”必须放在命令前面,以便将其识别为终端调用:

!pip install tensorflow

然而,TF 仍然有一些特性,这就是为什么这个通用的安装命令可能不工作或者不是最佳的。

马科斯

在苹果操作系统 macOS 上,正常安装可能会有问题,特别是如果安装了新的苹果处理器 M1 或 M2。要安装 TF 以便它针对这些 Mac 处理器进行优化,可以使用以下命令:

!pip install tensorflow-macos

此外,还需要另一个插件。在 macOS 上安装 TF 的详细说明可以在苹果网站上找到。

不带 GPU 的 Windows 和 Ubuntu

在 Windows 和 Ubuntu 上,已经描述过的基本安装可以工作。然而,这个版本的 CUDA 已经针对专用(外部)显卡进行了优化。如果您不想使用这些 GPU 或者只是没有安装外部 GPU,您也可以直接只安装 TF 的 CPU 版本:

!pip install tensorflow-cpu

然而,与强大的 GPU 相比,用 CPU 训练神经网络自然会有性能损失。

TF 的架构是什么?

TensorFlow 的架构支持许多系统和应用程序,因此模型也可以用于 web 或移动环境。

在训练中,TF 提供了读取你自己的数据集并将它们转换成优化的数据类型的可能性。此外或可选地,准备好的数据集也可以从 TensorFlow Hub 获得,或者整个准备好的模型已经被加载。在构建模型时,您可以使用 Keras 高级 API 构建自己的模型,也可以使用所谓的 premade estimators,它为特定用例提供预定义的模型。

然后,培训本身可以相对灵活地分配给可用的组件。TensorFlow 支持在处理器(CPU)上训练神经网络,即使这在大多数情况下性能不是很好。如果可能的话,还可以在图形处理单元(GPU)上训练模型,以尽可能缩短训练时间。如果你想在 Google Colab 中训练机器学习模型,也有可能使用所谓的张量处理单元(TPU)。这些特殊的处理器已经针对张量计算进行了优化,因此非常适合用于机器学习

TensorFlow 架构|来源: TensorFlow 博客

模型定型后,可以保存并用于新的预测。因此,TensorFlow 提供了最多样化的部署选项,可用于多种用例。此外,它还可以用于其他编程语言,如 C 或 Java。TensorFlow 提供了一个强大的系统,可以选择在云中或本地服务器上运行模型。此外,还可以通过 TensorFlow Lite 在移动设备上创建一个可用的模型,我们将在下一章详细介绍这一点。

TensorFlow Lite 可以用来做什么?

移动设备很少有足够的能力根据已经训练好的神经网络来计算预测。狭小的安装空间无法安装强大的处理器,当然也无法安装外部 GPU。然而,如今许多用户使用手机的频率几乎超过了笔记本电脑或电脑,因此许多公司也不可避免地为移动设备部署他们的机器学习模型。

例如,智能手机的指纹传感器或面部识别,使用机器学习模型来执行分类。这些功能还必须在没有互联网接入的情况下工作,以便手机甚至可以在飞行模式下使用。因此,制造商被迫在设备上执行模型的计算。

这就是 TensorFlow Lite 的用武之地,它提供了针对移动设备优化的特殊机器学习模型。这是通过照常在 TF 中训练任何模型,然后使用 TensorFlow Lite 将其转换为移动设备友好的版本来完成的。这样做,模型的大小和复杂性也可以减少,以确保它可以快速计算。虽然这种简化会导致精度下降,但在许多情况下,这是可以接受的,因为这样可以缩短计算时间。

TensorFlow 有哪些模型?

除了其他一些优势,TensorFlow 也经常被使用,因为它带有许多最先进的机器学习模型,只需几行代码就可以使用。其中一些已经被预先训练,并且可以用于预测而无需训练(即使这很少有意义)。

其中最著名的型号有:

  • 各种深度神经网络层:Keras API 提供了最常用的层来快速轻松地构建多种类型的深度神经网络。这些包括,例如,卷积神经网络长短期记忆(LSTM) 模型。
  • Transformer 模型:对于自然语言处理来说,目前还没有办法绕开 Transformer 模型,比如 BERT 等..然而,从头开始构建这些需要大量的数据和大量的计算能力。通过 TensorFlow 已经可以获得这些模型中的大量模型。可以用相对较少的数据和相当少的工作量对应用程序进行“微调”。

https://medium.com/nerd-for-tech/easy-guide-to-transformer-models-6b15c103bfcf

  • 残差网络(RESNet) :这些模型用于图像识别,也可通过 TensorFlow 进行预训练。
  • Big Transfer :类似于 Transformer ,这些都是复杂的模型,已经根据大量数据进行了预训练,然后用少得多的数据来适应特定的应用。这允许在图像处理的各个领域中实现非常好的结果。

张量流 vs PyTorch

TensorFlow 和 PyTorch 是 Python 中两个可能的机器学习框架,它们在某些方面有所不同,但提供了基本相似的功能。PyTorch 是由脸书开发和使用的,而 TensorFlow 来自谷歌。这也是为什么在许多情况下,两种选择之间的选择更多的是个人喜好的原因。

我们将在另一篇文章中详细比较这两个框架。然而,简而言之,TensorFlow 与 Pytorch 的选择可以分为以下三个要点:

新型号的可用性

在许多领域,如图像识别或自然语言处理,从零开始建立一个模型已经过时了。由于应用的复杂性,必须使用预先训练的模型。在研发方面,PyTorch 非常强大,多年来为研究人员提供了训练模型的良好框架。因此,他们的新模型和发现大多在 PyTorch 上分享。所以 PyTorch 在这一点上领先于游戏。

部署

然而,在工业环境中,重要的不是新模型可能提取的最后几个百分点的精度,而是该模型可以轻松快速地部署,然后提供给员工或客户。

在这一点上,TensorFlow 是更好的选择,特别是由于额外的组件 TensorFlowLite 和 TensorFlow Serving,并提供了许多轻松部署训练模型的可能性。在这个框架中,重点是端到端的深度学习过程,即从初始数据集到可用和可访问模型的步骤。

生态系统

TensorFlow 和 PyTorch 都提供了不同的平台,可以在其中共享和评估包含工作模型和预训练模型的存储库。不同的平台主要通过模型的主题来区分。总的来说,在这一点上的比较非常接近,但 TensorFlow 在这方面有一点领先,因为它为几乎所有的主题领域提供了端到端的解决方案。

对于这三点更详细的概述,我们推荐 AssemblyAI 的这篇文章。

TensorFlow 可以用于哪些应用?

TensorFlow 已经是许多行业训练机器学习模型的标准,这些模型是专门为一个用例训练的。在他们的网站上,你已经可以看到许多使用 TF 的公司,一些案例研究解释了该框架的具体应用:

  • Twitter :社交网络使用机器学习框架来填充用户的时间表。必须确保只显示最相关的新推文,即使用户关注大量账户。为了做到这一点,TF 被用来建立一个只推荐最佳推文的模型。
  • Sinovation Ventures :这家医疗公司使用 TensorFlow 产品来训练图像分类器,这些图像分类器在视网膜图像上诊断不同类型的疾病。图像的这种分类在许多应用中都是需要的,包括医学以外的应用。
  • Spotify :流媒体服务提供商使用 TensorFlow (TFX)的高级版本向其客户提供个性化的歌曲推荐。与 Twitter 应用程序相比,输入尤其是一个重大挑战,因为它还必须确保歌曲的类型、节奏或速度等参数匹配。这些值比 tweets 的文本更难用数字表示。
  • PayPal :支付服务提供商已经建立了一个复杂的模型,可以在早期发现欺诈性支付。尤其重要的是,该模型应该尽可能少地将合法支付归类为虚假支付,以免恶化用户体验。

这是你应该带走的东西

  • TensorFlow,简称 TF,是 Google 开发的深度学习和人工智能框架,最初只在内部使用。
  • 它为开发新的机器学习模型或使用现有模型提供了一个全面而强大的平台。
  • 在 TensorFlow Lite 或 Serving 等各种组件的帮助下,模型的部署特别容易。
  • 许多大型知名公司都依赖 TensorFlow 的功能。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

*https://medium.com/codex/why-you-should-know-big-data-3c0c161b9e14 https://medium.com/@niklas_lang/understanding-mapreduce-with-the-help-of-harry-potter-5b0ae89cc88 *

广义线性模型(GLM)介绍

原文:https://towardsdatascience.com/an-introduction-to-the-generalized-linear-model-glm-e32602ce6a92

它是什么?模型是如何拟合的&在房价预测中的应用

图片来自 Unsplash

在经典的线性模型中,通常需要正态性。这显示在图 0.1 中,在随机变量 X 固定的情况下,Y 的分布是正态的(由每个小的钟形曲线说明)。回归曲线穿过每个正态分布的平均值。

然而,在广义线性模型中,这一要求不再必要,因为我们可以根据我们对数据的了解,为这些观察值选择一个分布模型。这是通过链接函数实现的,它将这些观察值的平均值e[yᵢ】转换成线性形式。此外,也不再需要同质性。 Y 中的误差方差不必是常数。[5]

广义线性模型的组成部分

GLM 有三个主要组成部分,链接功能是其中之一。这些组件是

1.一个随机分量 Yᵢ,它是每次观测的响应变量。值得注意的是,这是一个条件分布的反应变量,这意味着 Yᵢ是 Xᵢ.的条件

Yᵢ 的分布属于指数族,这意味着 Yᵢ具有形式,定义为【2】

不同的是,规范形式中的 θᵢ 没有被转换,这使得规范形式更容易处理。此外,请注意,总是可以将指数族转换为标准形式。或者,我们也可以将指数函数写成下面的形式[1]

等式 1.2 指数族

在 GLM 使用。在等式 1.2 中, θᵢϕᵢ 分别是位置(与均值相关)和比例参数(与。另外,我们用 μᵢ 来表示 Yᵢ 的平均值。注释:在等式 1.2 中, yᵢ 也可以简单地写成 y ,就像等式 1.1 中一样。我们只需要记住一个 yᵢ 或者 y 代表一次观察的结果。

2.线性预测器,其具有普通线性模型的常见形式

等式 1.3 线性预测值

我们将用这个来预测 Yᵢ.的均值注意,在等式 1.1 中, ηᵢ 不是线性预测器,而是 θᵢ 的变换函数。在本文中,我们将只使用等式 1.2 中给出的形式。

3.一个链接函数 g(∙),将 yᵢ(e(yᵢ)的平均值转换成等式[linear]中的线性形式,这意味着

等式 1.4 链接功能

要求链接的函数是光滑可逆的(可逆表示函数是单调的)。

指数家庭为什么好?

指数族有许多好的性质。

1.在多种来源中(为什么指数家庭如此令人敬畏?指数族的优点Wiki:指数族,提到指数族在贝叶斯统计中非常可行,因为那些分布总是有共轭先验

2.另一个非常重要的性质是,在等式 1.1 中,tᵢ(x充分统计量。简单来说,一个充分的统计量就是一个包含变量 x 关于未知参数的所有信息的函数,在这里是 θ 。这里的“充分”与逻辑学中“充分条件”中的“充分”含义相同。

更正式地说,一个统计量 T(X₁、…、Xₙ) 被说成足以求 θ ,如果条件分布 X₁、…、Xₙ ,给定 T=t ,不依赖于求的任何值

3.除了上述两个属性,指数族还将多个不同的分布结合在一起。这允许我们将多个不同的分布放到一个模式中。在这里,我们将展示使用 abφ 可以获得指数族分布的均值和方差的一般表达式。

我们将使用最大似然来实现这一点:当似然函数被优化时,我们想要 E[Y]首先,我们计算指数族分布的一般形式的对数似然(等式 1.2)(当然,如果对数似然被优化,则似然也被优化)。

等式 2.1

然后我们取它关于 θ 的偏导数。注意零件 c(y,ϕ) 不包含 θ ,所以消失。我们得到了

等式 2.3

窍门是我们可以把 l 作为一个随机变量,用它的期望值e【y】代替 y ,让∂ l/θ 的期望值为 0

情商。2.4

这给了我们一个非常简单的公式 E(Y)

等式 2.5

有一个非常重要的事实值得一提。诀窍(将一阶导数设置为 0 以获得最大值)是由于指数族的对数似然函数的一个属性——它相对于θ凹的。[3]否则,这个方法就简单的断了。(这是指数家族的又一个优点)

现在我们试着计算 YVar(Y) 的方差。取方程 2.3 的导数,我们得到对数似然函数的二阶导数

等式 2.6

我们可以用一般的结果

等式 2.7

它显示了对数似然函数的一个特性。证明是技术性的,既不困难也不有趣。因此,在本文中,我们将省略这一点。将等式 2.6 代入等式 2.7,我们得到

等式 2.8

使用我们已经得到的 Y 的平均值(等式 2.5),以及等式 2.8 的一些代数运算,我们立即得到 Y 的方差

等式 2.9

a(ϕ) 可以是 ϕ 的任何函数,但是为了更容易地使用 GLM,我们通常让

其中 w 为已知常数。那么我们可以将等式 2.9 写成

情商。2.10

属于指数族分布的一个例子

GLM 最简单的例子是具有身份链接功能的 GLM。这将 GLM 简化为一个普通的线性模型。虽然很简单,但这个案例让我们了解了 GLM 的工作。

我们知道,一个普通的线性模型假设每个观测值都有一个正态分布。既然是 GLM 的特例,当然正态分布属于指数族。这里我们展示了如何将正态分布转换为等式 1.1 的形式:

等式 3.1 正态分布属于指数族

我们可以看到,这非常简单——只需将常数移入指数部分,并扩展平方即可。等式 3.1 告诉我们

等式 3.2

使用我们在上一节中得到的结果(等式 2.5 和等式 2.10),我们现在可以检查正态分布的均值和方差

等式 3.3、3.4

拟合模型

为了拟合模型,我们使用似然估计。前面说过,指数函数的对数似然函数是的,所以我们可以通过寻找一阶导数为零的点来求它的最大值。我们现在在解决什么?简单回顾一下这个问题:我们有一个独立响应变量的 n 维向量 Yᵢ ,其中μ=e[yᵢ】,它通过

等式 4.1、4.2

θᵢ 是正则参数我们想要找到 β ,它最大化了对数似然函数。再次, Yᵢ 是独立的,这使得 β 的 MLE 成为可能。类似于等式 2.1, β 的对数似然为

等式 4.3

θᵢβ 的关联方式如下: θᵢYᵢ 的均值相关(这取决于具体的分布函数,在上一节的例子中, θᵢ=μᵢ ), β 也通过 link 函数与 Yᵢ 的均值相关。所以 θᵢβ 是通过【μᵢ连接起来的,这一点我们在后面的偏微分中会看到。

现在我们要对 Eq 求导。4.3 关于 β 中的每一个元素(它们将由索引 j 下标)。这给了

等式 4.5

因为链式法则,我们有

等式 4.6 链式法则

然后在微分方程 2.5 后,我们有

等式 4.7 对 Yᵢ 的平均值进行微分

我们这样做是为了得到θᵢ/μ**因为 E[Yᵢ] = μᵢ.下一步确实很容易,我们将等式 4.7 代入等式 4.5,然后**

等式 4.8

其中红色部分来自等式 4.7。方程 4.8 可以进一步简化。正如我们谈到的,在 GLM 中,【var[yᵢ】不是常数。因此,我们可以把var[yᵢ】看作e[yᵢ】的函数,这样我们就可以定义

等式 4.9

使得(参考等式 2.10)

等式 4.10

将等式 4.10 代入等式 4.8,并将其设置为零(我们感兴趣的是对数似然函数的一阶导数为零的点),我们得到

等式 4.11 将等式 4.8 设置为 0 并代入。

等式 4.11 给我们一个非线性方程组β——如果 j 从 1 到 m,那么有 m 个这样的方程。当未知数的数量不一定等于方程的数量,并且方程可能非常复杂时,如何求解这样的非线性方程组?数值方法在这种情况下发挥作用。这里我们将应用迭代加权最小二乘法(IRLS)。在这篇文章中,我们不会深入细节。一般来说,这种方法迭代地逼近解。而当我们知道 V(μᵢ) 独立于 β 时,那么最小二乘目标就是

Eq 4.12 的目标

换句话说,从现在开始的问题就是找到使 Eq 4.12 最小化的 β 。这样,我们得到了方程 4.11 的最佳解。

GLM 在房价预测中的应用

数据集“房价”来自 Kaggle 上的一个预测竞赛。整个分析的代码可从以下网址获得

**https://github.com/ZhangXichu/House-Price/blob/main/glm.ipynb

使用均方根偏差(RMSD)评估结果。GLM 方法给出了最高 33%的位置。当然,这不是最理想的方法——排行榜上有 0 个错误的结果。但它在实践中很好地展示了 GLM。

我要说的是,关于实现没有太多要谈的,因为为什么要做每一步,在笔记本上都有解释。大部分代码是数据探索、预处理、模型比较和模型诊断。建模部分归结为一行(当然,不要忘记**import** statsmodels.api **as** sm):

model_full **=** sm**.**formula**.**glm(formula**=**formula, family**=**sm**.**families**.**Gamma(link**=**sm**.**genmod**.**families**.**links**.**log()), data**=**train)**.**fit()

其利用对数链接函数将数据拟合到广义伽马分布。

摘要

这篇文章主要是关于广义线性模型(GLM)的定义,何时使用它,以及如何拟合该模型。许多文本都是关于指数族的,因为它是 GLM 的基础,了解指数族的性质有助于我们理解为什么模型拟合变得最小化等式 4.12。(解决这个问题的细节被省略了,因为它值得一整篇文章的决定。)

事实上,这些都不是程序运行所必需的,正如我们在上一节提到的,建模只是一行代码。我们需要从零开始实施 GLM,这几乎不可能发生。然而,了解理论总是有助于决定选择哪个模型,以及诊断和解释模型。**

****参考文献:

[1]赫尔曼·罗德里格斯。 广义线性模型理论 。于 2022 年 2 月 17 日访问。

[2]斯蒂芬·贝茨,安迪·曹。指数族。于 2022 年 2 月 18 日访问。

[3]肯普索恩博士,评估方法 II 。于 2022 年 2 月 23 日访问。

[4]哈斯蒂,T. J .,&蒂布拉尼,R. J. (2017 年)。 广义可加模型 。劳特利奇。

[5]大学习团队(2021), 广义线性模型|什么意思? 。于 2022 年 4 月 7 日访问。

外壳介绍

原文:https://towardsdatascience.com/an-introduction-to-the-shell-676ee5b899df

解释外壳的用途和基本功能

加布里埃尔·海因策在 Unsplash 上的照片

介绍

我最近开始了一份新工作,我发现自己比以前用得更多的一个关键东西是 外壳

在大学期间,我在编写 FORTRAN 代码(是的,没错,是 FORTRAN)时接触到了 shell。然而,我并不真正理解 shell 是什么或者我使用的命令实际上在做什么(我更关心的是不要让任务失败!).

在这篇文章中,我想阐明 shell 的用途,并展示它的一些基本功能。

注意:这将是在 MacOS shell 上,因此大多数命令可能无法在 windows 机器上运行。但是,原理都是一样的!

外壳是什么?

让我们从讨论外壳实际上是什么开始。

如今,我们使用 图形用户界面(GUI) 与计算机进行交互,以访问文件、程序等。然而,图形用户界面有一定的局限性,不允许您使用计算机的全部潜力。

为了充分利用你的电脑,我们需要使用基于文本的界面,外壳。这个 shell 也被称为【CLI】并且运行在一个叫做 终端 的东西里面。终端只是一个运行 shell 的接口/应用程序。

请参考栈交换线程,全面描述终端与外壳的区别。

在坚果壳(没有双关语)中,shell 允许您使用文本与操作系统进行通信。老办法!

贝壳的类型

每台计算机都有某种外壳,有些有多个外壳。它们之间的语法可以不同,但基本上它们执行相同的过程。

最广为人知的 shell 是 Bourne Again Shell (Bash) ,它通常是 Linux 和 MacOS 等类似 Unix 的https://en.wikipedia.org/wiki/Unix-like系统的默认 Shell。然而,MacOS 最近在 2019 年将其默认设置更改为 Z-Shell ,这只是 Bash 的一个扩展,因此具有许多相同的语法和功能。

基本命令和功能

访问外壳

如果您在 MacOS 系统上,您可以通过终端应用程序访问 shell,当您启动它时,它应该看起来像这样:

图片作者。

您需要了解一些功能:

  • 在顶部栏中,您可以看到表示我们在 Z 外壳中的 zsh
  • egorhowell@Egors-MBP 表示我在 Egors-MBP (Macbook Pro)上以 egorhowell 的用户身份登录
  • % 符号是 Z Shell 的命令提示符。对于 Bash shell,它将是一个美元符号 $

查找、更改和创建目录

我们可以使用 shell 来浏览我们的计算机,就像您使用

  • 命令 ls (列表)可以让你看到你当前所在的所有文件夹和目录
  • 我们可以通过 pwd (打印工作目录)看到自己在什么目录
  • 我们可以通过调用mkdir(make directory)命令,后跟我们想要创建的目录的名称,来创建一个目录
  • 命令 cd (改变目录)后跟你要进入的目录。您也可以使用 cd.. 回到上一个目录

让我们看一个例子。我们将使用 lspwd 查看我们当前的目录,然后使用 mkdir 创建目录【egor _ test】并使用 cd 移入其中。

图片作者。

请注意,现在它在命令提示符前显示“egor_test ”,以指示我们所在的目录。

创建、编辑和查看文件

像目录一样,我们也可以从 shell 中创建、编辑和查看文件:

  • 触摸 允许我们创建一个给定的文件
  • 回显 允许我们将数据和变量写入屏幕或文件
  • 我们可以用'>>''追加到文件中,用''覆盖****
  • 我们可以使用 cat 命令查看该文件

让我们再来看一个简单的例子。我们将创建一个名为 'test.txt' 的文件,然后向其中添加字符串 'hello' ,并查看该文件以确保它存在。然后,我们将使用‘hello world’覆盖它,并检查它是否确实覆盖了该文件。最后,我们将追加‘hello world round 2’:

图片作者。

其他有用的命令

  • 手动 命令给你手动命令
  • curl(客户端 URL)** 让您将数据从某个服务器传输到您的 shell,这在您想要安装 家酿诗歌 等软件包时很有用**
  • chmod (更改模式)让您更改文件的权限和特权
  • 【复制】复制文件
  • rm (删除)删除文件和目录
  • 【移动】移动和重命名文件

这只是触及了 shell 可用命令和功能的皮毛。我在这里链接了一些可爱的夜间阅读命令的完整列表!

引擎盖下发生了什么?

当我们在 shell 中键入一个命令时,如果它不是 shell 固有的,它会通过一个叫做 的路径 来查看。这包含了 shell 搜索的目录,以查看是否可以匹配与您的命令相关的可执行文件。我们可以通过键入 echo $PATH 来查看 shell 查询的目录:

图片作者。

注意:我已经安装了 Anaconda 发行版,所以我的路径看起来可能和你的略有不同。

因此,当我们键入一个命令时,shell 会按照给定的顺序查看上述目录,试图找到执行我们输入的命令的二进制文件。

我们可以通过运行命令来检查 shell 执行的文件在哪里:**

图片作者。

这里我们看到 mkdir 的可执行文件在 /bin/mkdir 中,然后我们调用这个可执行文件来制作一个新的目录叫做‘egor’

数据科学家的目的

你可能在想,我们什么时候才能把 shell 当成数据科学家?我们可能不像软件工程师那样经常使用它,但是有很多例子:

  • 使用 pyenvasdf 等包为生产代码管理项目 python 依赖关系
  • 安装工具来改善你的编码体验,例如 自制软件
  • 使用 Anacondapip 下载软件包

在您的数据科学职业生涯中,还有很多时候您需要使用 shell 来实现某些目标!

结论

在这篇文章中,我们回顾了 shell is 和它的一些基本功能。这是一个非常强大的工具,你可能会在职业生涯的某个时候用到它。因此,这是值得舒适的!

和我联系!

(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。执照: CC BY-SA 4.0

话题噪音模型介绍

原文:https://towardsdatascience.com/an-introduction-to-topic-noise-models-c48fe77e32a6

学习如何使用话题噪音模型(1/3)

秩序问题。如今,很难从噪音中找到最重要的词。在这一系列文章中,我们将介绍一种新的模型——话题噪音模型,并向您展示如何在社交媒体文本数据集上使用这些模型来生成更多可解释的话题。社交媒体数据可以以多种不同的形式出现,例如文本、音频、图像和视频等。在本文中,当我们使用短语社交媒体数据时,我们指的是在帖子和/或个人资料中找到的文本数据。本文解释了主题噪声模型与传统主题模型的不同之处。然后介绍了 gdtm python 包,并展示了如何使用原始主题噪声模型、主题噪声鉴别器(TND)和无噪声潜在狄利克雷分配(NLDA)的例子。本系列的第二篇和第三篇文章介绍了主题噪声模型的半监督和时间变体,并展示了如何有效地将它们与社交媒体数据集结合使用,以构建更高质量的主题。

注意:本系列旨在对主题噪声模型进行高层次的解释,并演示如何在实践中使用它们。对于所有的事实细节,你可以在这里阅读研究论文。

介绍

在社交媒体数据集,特别是与新冠肺炎或 2020 年总统选举有关的特定领域数据集,大多数传统的话题模型都难以有效地过滤话题中的噪音。虽然很容易预处理掉停用词(常见的词,如“if、ands 和 buts”)和其他常见类型的干扰,但我们必须依赖底层模型来消除不太明显的干扰类型,如不属于该领域的词(垃圾邮件词),或在该领域中太常见的词(泛滥词)。传统的主题模型,如潜在的狄利克雷分配(LDA) [Blei et al .,2003],很难消除社交媒体数据中固有的噪音。因此,生成的主题通常是不连贯的,换句话说,人类很难理解。

发明主题-噪音模型是为了在使用来自社交媒体等噪音领域的文本时,产生比传统主题模型更连贯、更可解释的主题。话题-噪声鉴别器(TND) [Churchill 和 Singh,2021 (2)]是最初的话题-噪声模型。TND 联合近似数据集上的主题和噪声分布,以允许更准确的噪声去除和更连贯的主题。它可以作为一个独立的模型来产生主题,或者与其他模型相结合来创建基于其他主题分布的主题。我们提倡将 TND 与你最喜欢的主题模型结合起来,因为经验表明,TND 与更传统的主题模型结合时效果最佳。在这一系列文章中,我们将描述我们如何集成 TND 和 LDA 来创建一个称为无噪声潜在狄利克雷分配(NLDA)的模型,我们将向您展示如何有效地使用它。

什么是主题模型?

在我们描述主题噪声模型之前,我们应该对什么是主题模型有相同的理解。图 1 显示了在非常高的层次上确定文档主题的过程。一个主题模型(黑盒)取一组文档(最左边),返回一组总结文档(最右边)的 k 主题(图 1 中四个)。一个题目是一组相关的单词。在图 1 中,我们看到我们的示例数据集由奇幻类书籍组成。我们说数据集的是奇幻书籍。对于这些文章的其余部分,我们将处理特定于领域的数据集,即由引用相同广泛主题领域的文档/帖子组成的数据集。

图一。什么是主题模型?

由黑框表示的主题模型依次扫描文档,找到在同一上下文中重复出现的单词。主题模型通过观察哪些单词在同一文档中频繁出现来工作。当单词一起出现在同一文档中时,它们出现在同一主题中的概率会增加。在观察了数以千计(或更多)的文档后,该模型收敛到一组主题,每个主题都以高概率分配给频繁出现在一起的单词。关于主题模型更完整的解释,请参考主题建模的演变【Churchill and Singh,2021 (3)】。

在图 1 中,主题模型分别返回关于魔法邪恶旅行生物的四个主题。通常话题不会像这里一样有标题。研究人员需要自行确定这些主题类别。大多数主题模型还提供了文档集合中每个单词的概率。在图 1 中,我们向您展示了每个主题中出现概率最高的单词。比如魔杖魔法题目中概率最高。这些主题可以由人类来解释,以更好地理解整个数据集,或者它们可以用于根据最可能的主题对文档进行分类,如图 1 的最右侧所示。在这个例子中,我们看到旅行霍比特人指环王的主导话题,而不是哈利波特。一般来说,主题模型非常擅长识别传统文档中的主题,如书籍、报纸文章和研究论文,因为它们的噪音更少。

主题模型有什么问题?

社交媒体文档(帖子)比书籍短得多。因此,当主题模型寻找在文档内和文档间连续出现的重复单词时,它找不到。推文特别短,由于发布到 Twitter 上很容易,推文经常编辑得很差。单词在十个单词的文档中不能像在书中一样重复出现。社交媒体文档对主题模型提出了独特的挑战。属于同一主题的单词经常没有足够的重复来区分主题单词和干扰单词。同样,关于同一主题的两篇文章可能根本不包含任何相同的单词。考虑两句话,“这不是火箭科学,清洁你的手指和手掌,以避免感染冠状病毒,”和“这就像魔术一样!Covid 可以通过洗手来遏制!”两者表面上都是关于新冠肺炎清洁的相同话题,但包含的内容却完全不同。

这个问题表现在话题嘈杂,难以解读(语无伦次)。为了证明这一点,我们创建了一个由虚构的推特组成的虚假数据集,这些推特是从哈利·波特人物的角度写的,关于新冠肺炎·疫情(这里的域是新冠肺炎·疫情)。这个虚假数据集中的一个文件的例子是,“冠状病毒爆发导致霍格沃茨魁地奇赛季取消。”使用这些虚构的 tweets 生成主题后(图 2),我们可以看到特定领域的噪声如何影响主题。在图 2 中,每一列都是一个主题。

图二。嘈杂的话题。

首先,我们可以看到其中三个主题包含了单词新冠肺炎的变体。这些词属于该领域,但它们在该领域内是如此普遍,以至于它们无助于主题之间的描述。如果每个话题都包含同一个词,那么我们就更难理解如何区分每个话题。第二,题目里有一堆和哈利波特有关的词(狼人斗篷隐身魁地奇)。虽然这些词可能是奇幻书籍领域的相关主题词,但它们不会增加我们对疫情的了解(除非我们有一个不寻常的社交媒体数据集)。

图 3。更好的话题。

图 3 显示了主题的一个更好的版本,其中干扰词被过滤掉,并替换为更有意义的主题词。在图 3 中,每个主题是一个列。主题噪声模型的目标是识别和去除主题中的噪声词。关于图 3 中的主题的另一个有趣的观察是,一些单词实际上是预期短语或 ngram 的一部分。例如,社交距离戴面具虚拟学习都是与新冠肺炎疫情相关的常见短语。这些短语和其他类似的短语可以直接合并到主题模型中,但是由于短文档中的稀疏性问题,关注单个词并不罕见。两个单词的短语在文档集合中出现的频率往往低于单个单词。这意味着当你有一个包含 unigrams 和 ngrams 的主题模型时,ngrams 自然不会上升到主题单词列表的顶部。

让我们回头看看来自我们的假数据集的示例 tweets,看看短文档的最后一个重要方面。

"这不是火箭科学,清洁你的手指和手掌以避免感染冠状病毒."

“这就像魔法一样!Covid 可以通过洗手来遏制!”

因为社交媒体文档比传统文本短得多,所以它们很少包含主题集中的所有主题。第二条虚构的推文包含的词主要指的是新冠肺炎清洁话题,其中一个词( magic )可能指的是另一个话题。给定一个更大的文档集,我们可能能够找到其他经常与 magic 一起出现的单词,并围绕它形成一个主题。现在让我们看看主题噪声鉴别器是如何从主题中去除噪声的。

话题噪音鉴别器(TND)

主题-干扰词模型不是仅仅从一组文档中生成主题,而是试图通过生成过程从概率上分离主题和干扰词。主题噪音鉴别器(TND)通过概率性地将每个文档中的每个单词分配给主题或噪音分布,基于单词在每个分布中的先验概率。结果是一个全局噪声分布,它近似于一个词成为噪声词的可能性,以及一个主题-词分布,它近似于每个词出现在每个主题中的概率。图 4 显示了我们如何看待 TND 的建设。

图 4。主题-噪声模型沿着主题分布生成噪声分布。

当我们最终确定主题时,我们会单独查看每个主题,并决定每个单词是属于该主题还是噪音。我们根据一个词在主题中的出现频率和噪音分布来决定。我们可以把它想象成一个标尺,一边用话题频率加权,一边用噪音频率加权。图 5 显示了规模类比。一边的频率越高,意味着我们更有可能将这个词归类到一边的权重。

图 5。TND 的天平。

我们已经描述了主题噪音鉴别器的新的重要方面是如何工作的,但是它们是如何一起产生主题和噪音分布的呢?让我们来看看高级算法:

给定文档和超参数 k、α、β₀和β₁:的数据集 d

对于 D 中的每个文档 d :

1.从 d 的话题分布中概率性地选择一个话题 zᵢ

2.对于 d 中的每个单词 w ,根据 zᵢHw 的概率,将 w 赋值给 zᵢ 或噪声分布 H (图 5)。

3.给定新的主题词分配,重新近似 d 的主题分布

重复上述 X 次迭代(实践中通常是 500 或 1000 次)

超参数 k 定义了主题集合中主题的数量。超参数α控制每个文档有多少主题。较高的α设置允许每个文档中有更多的主题。在社交媒体文档中,我们将α设置为一个较低的值,因为文档的大小很少允许每个文档有多个主题。超参数β₀控制一个词可以有多少个主题。我们把β₀定得越高,一个词可以传播的话题就越多。最后一个超参数,β₁,控制单词向主题的倾斜(远离噪声分布)。我们设置的β₁越高,任何给定的词就越有可能是主题词而不是干扰词。你可以认为β₁ 在图 5 的天平的主题频率一侧增加了额外的重量。

经过大量的迭代,每个单词都有可能成为主题单词,也有可能成为干扰词。然后,我们可以看看主题和噪声分布,以更好地理解我们的数据。

图 6 描述了一旦我们知道了主题和噪声分布,我们如何查看一些社交媒体文档。我们可以在图 6 中看到每个文档中的单词如何影响该文档的主题分布。第一个文档由绿色和蓝色主题组成,带有一些杂色(紫色),而第二个文档由黄色主题和一些杂色组成。如果干扰词出现在主题中,而不是出现在它们自己的分布中,我们可能会将这两个文档归类为由相同的通用主题组成,从而导致更难解释的主题和文档分类。

图 6。社交媒体文档中主题和噪音之间的相互作用。

无声的潜在狄利克雷分配(NLDA)

虽然 TND 识别高度一致的主题,但从质量上讲,它的主题并不总是像使用其他主题模型生成的主题那样直观。因此,我们建议将来自 TND 的噪声信息与传统的主题模型相结合。通过这种方式,TND 提供了准确的噪声消除,同时保持了人们对用于更传统的文档集合的最新主题模型所期望的相同的主题质量和性能。我们创建了无声的潜在狄利克雷分配(NLDA),TND 和 LDA 的集合,以证明这种类型的方法的有效性。我们采用 TND 的噪声分布、LDA 的主题分布,并将它们结合起来,以创建比单独使用 TND 或 LDA 更连贯、更可解释的主题(图 7)。

图 7。NLDA 乐团。

我们组合 TND 和 LDA,就像我们选择一个词是属于噪声分布还是属于 TND 本身的一个主题一样。在 NLDA,我们使用图 5 所示的相同标度方法,将 TND 噪声分布中的单词频率与 LDA 主题分布中的单词频率进行比较。缩放参数 φ 允许通过将基础频率缩放到相关值来比较噪声和主题分布,即使它们不是使用相同数量的主题 k 生成的。

主题-运行中的噪声模型

既然我们已经更好地理解了什么是主题模型,为什么需要改进它们,以及主题-噪音模型如何解释社交媒体数据的噪音性质,现在是时候使用它们了。在本节中,我们将介绍乔治敦数据实验室主题模型包( gdtm ),并启动和运行 LDA、TND 和 NLDA!

数据集

我们认为展示模型在不同数据集上的表现非常重要。如果只对示例数据集有效,那还有什么意义?在本文的其余部分,我们将把基于哈利·波特的数据集的神奇世界抛在脑后,专注于两个真实世界的数据集——一个关于新冠肺炎·疫情,另一个关于 2020 年美国总统大选。这些数据集分别是在 2020 年 3 月至 2021 年 3 月之间,以及 2020 年 1 月至 2020 年 11 月之间,使用与各自领域相关的关键词,使用 Twitter 的 API 收集的。它们分别包含 100 万和 120 万个文档,这对于主题模型标准来说是相当大的。我们对新冠肺炎和选举数据集进行了以下预处理:标记化、URL 删除、标点删除、小写和停用词删除。关于社交媒体预处理的更多细节,我们建议您参考 textPrep [Churchill and Singh,2021 (5)]。

由于 Twitter 的隐私政策,这两个数据集是不公开的,所以对于您自己的实验,我们提供了一个由 196 条预处理推文组成的小样本数据集,这些推文取自上面的选举数据集(为了遵守 Twitter 的政策,元数据被删除),以及一个链接,链接到由 Kaggle 提供的更大的公共领域数据集,该数据集涵盖了与之前的选举数据集略有不同的时间段内的 2020 年选举。

安装

首先,让我们确保我们有正确的代码。主题噪声模型是用 Java 实现的(基于 LDA 的mallet【McCallum,2002】实现【Blei 等人,2003】,我们认为这是可用的最好的主题模型实现),但是为了简单起见,我们基于 Gensim 的旧 mallet LDA【řehůřek 和索伊卡,2010】构建了 Python 包装器。

注意:gdtm 适用于 Python 3.6 及更高版本。我们还没有在旧版本的 Python 上测试它。本教程假设您使用的是 MacOS 或 Linux。

在您的终端中导航到您的工作目录,启用您计划使用的任何一个虚拟环境,然后 pip 安装 gdtm 。你可以在 gdtm 的文档中找到你需要了解的一切。

> pip install gdtm

一旦安装了 Python 包,您所需要的就是您将要使用的任何主题噪声模型的 Mallet (Java)实现。您可以在 Topic-Noise Models Source 资源库中找到 TND 的实现。从存储库中下载 mallet-tnd 文件夹,并记下它的路径,无论它在您的计算机上的最终位置是哪里(path/to/tnd)。Mallet LDA 可以在这里找到,但是我们也在我们的源代码的 mallet-lda 文件夹中提供了一个精简版本。从任一位置下载它,并记下它在计算机上的路径(path/to/lda)。现在我们已经有了所有需要的代码,我们可以开始玩主题噪音模型了!

加载数据集

选择并下载您的数据集进行实验。您可以使用自己的一个,或者上面的数据集小节中提供的两个选项之一。

注意:主题噪音模型最适用于数万或数十万条推文或其他社交媒体帖子的数据集。噪声分布的训练是使用随机算法完成的。对于较小的数据集,TND 并不总是能够得到准确的噪声分布,所以不要期望在样本数据集上看到很好的结果!如果您想使用更大的数据集来查看其真实效果,我们建议使用我们上面描述的 Kaggle 数据集。美国选举 2020 Kaggle 数据集包含 172 万条关于 2020 年 10 月 15 日至 2020 年 11 月 8 日之间选举的推文,这些推文是使用 Twitter API 收集的。它被发布到公共领域,这意味着你可以随意使用它。在将更大的数据集放入模型之前,您需要做一些预处理和数据争论。我们建议从更大的数据集中的几十万条推文的子集开始。

数据集可以以您认为方便的任何方式加载,但是传递到模型中的最终数据结构应该由一个文档列表组成,其中每个文档本身就是一个单词列表。

您可以使用 gdtm 的内置函数加载样本数据集:

图 8。使用内置函数加载数据

样本数据集是一个由空格分隔的 CSV 文件。如果您自己制作这种类型的文件,不要意外地包含多余的空格,这一点很重要。正如您在图 8 中看到的,有一个参数传递您喜欢的分隔符,因此您可以在自己的数据集中使用您喜欢的任何一个。

管理 TND 和 NLDA

既然我们已经将数据加载到适当的格式中,我们就可以运行我们的模型并生成主题,只需要几行代码。在下面的要点中,我们在第 4 行指定 Java 实现的路径,在第 7 行实例化并运行模型,在第 9 行和第 10 行获得我们的主题和噪声。我们所要担心的是为我们的数据选择正确的参数。

注意:对于特定领域的社交媒体数据,*k*应始终至少为*20**beta1*应介于*9**25*之间。我们喜欢用*k = 30**beta1 = 25*表示 Twitter,用*beta1 = 9**16*表示 Reddit 评论。当数据集更嘈杂时,我们增加*beta1*来试图在真正属于那里的主题中保留更多的单词。有关该参数的完整解释,请参见研究论文(第 IV.B 节)。

图 9。在我们的样本数据集上运行 TND。

运行 NLDA 类似于运行 TND,除了这一次我们运行两个模型的集合,TND 和 LDA。谢天谢地,我们不用担心自己做任何组装。gdtm 帮我们搞定。我们只是提供到 TND 和 LDA 的路径,并指定我们的参数。得到最终的题目和噪音是一样的。

图 10。在我们的样本数据集上运行 NLDA

保存结果

现在,当我们调用get_topics()get_noise_distribution()时,我们实际得到了什么?

正如我们所料,get_topics()返回每个主题最可能出现的单词。我们传递给NLDATNDtop_words参数决定了每个主题返回多少个单词。

get_noise_distribution()功能类似。它获取噪声分布中最有可能的噪声词,并在一个列表中返回它们,从最有可能到最不可能排序。每个单词都在一个元组中,其频率在噪声分布中(word, frequency)。来自TND(或来自 NLDA 的tnd_noise_words_max)的noise_words_max参数决定了应该从分布中返回多少个单词。

top_wordsnoise_words_max参数是必需的,因为主题词和噪声分布是词汇表中所有词的分布。这些参数纯粹是为了方便,并在分析结果时节省一点时间。

我们可以使用gdtm轻松地将主题保存到 CSV 文件中。

图 11。使用 gdtm 保存主题很简单。

解释结果

现在我们已经保存了我们的主题,让我们最后来看看它们。以下主题来自 2020 年完整的选举数据集,样本推文来自该数据集。在 CSV 中,每一行都是一个主题(主题没有标题,由用户决定如何称呼它们)。

图 12。使用 NLDA 在关于 2020 年选举的大型推特语料库上发现的话题。每行都是一个主题。

为了简洁和篇幅的原因,我们展示了题目集中前七个题目的前十个单词。我们可以看到,大多数主题都相当连贯。主题包括 G 联合国暴力(第 1 行)政党(第 2 行),一个一般的推特话题(第 3 行)新冠肺炎(第 4 行),亲拜登的短语和标签(第 5 行)民主党选民(第 6 行),以及亲特朗普的短语和标签(第 7 行)。

判断主题模型的方法有很多种,它们并不总是彼此一致。像困惑主题连贯这样的一些方法依靠纯数学来确定主题是良构的,而其他的则依靠人类的判断。我们发现,就我们而言,主题连贯性和基于人类判断的方法是主题质量的更好指标。虽然这些方法在本文中没有涉及,但是你可以在主题建模的发展【Churchill and Singh,2021 (3)】中找到更多的细节。

这些主题当然不是完美的和包罗万象的。然而,大多数是高度可解释的,主题的趋势很容易识别。在第三行,包含一般 Twitter 单词的主题还包含干扰词,如 lets、you 和 hey 。这些词太笼统,不能考虑作为话题词,而是漏入话题集中最笼统的话题。更具体的话题如枪支暴力包含很少的噪音,如果有的话。主题噪声模型的噪声过滤特性要感谢这种可解释性和连贯性。

主题噪声模型与传统主题模型的比较

作为本文的总结,我们将看看几个使用 LDA、TND 和 NLDA 在新冠肺炎和选举数据集上生成的主题示例。如果你还记得,这些是我们在 2020 年和 2021 年不同时期收集的大型 Twitter 数据集。我们喜欢在不同的数据集上展示模型的性能,以证明这些模型在不同的领域是一致的。

如图 13 所示,第一个主题来自新冠肺炎数据集。这个话题是关于面具和社会距离,并且是基于关于新冠肺炎·疫情的一百万条推文的数据集产生的。绿色单词是我们认为属于该主题的单词。

注意:主题是主观的。我们所看到的好的和坏的主题词,以及你所看到的好的和坏的主题词,将会根据我们在领域内的经验和我们的观点而有所不同。你可以不同意我们对以上或以下主题的分析。

图 13。题目是由 LDA,TND 和 NLDA 发现的。域:新冠肺炎

正如我们所看到的,LDA 包括像中国城市武汉这样的词,它们中的每一个都可能属于领域中的主题,但是它们不属于这个主题中的。TND 包含像战斗消息这样的词。这些词可能属于这个主题,但是我们觉得它们太笼统了,在这个上下文中不是好的主题词。正如我们所看到的,NLDA 的主题包含了更直观的符合主题的单词。**

最后,让我们看一个由 LDA、TND 和 NLDA 在选举数据集上产生的话题。图 14 显示了主题,关于邮件投票。由于许多州都实施了疫情措施,这个话题与此次选举尤为相关。正如我们所看到的,LDA 和 TND 都有合理的话,但对于多个主题结合起来。这些词中有许多在该领域内很常见,但并不专门与邮寄投票相关,如 maga、trump2020、kag、republican、political 。然而,NLDA 再一次给了我们一个更容易理解,更连贯的话题。虽然有些词是噪音,但我们看到许多词是指邮寄投票系统以及与该系统在不同州的实施相关的戏剧(佛罗里达是邮寄投票最受热议的州之一)。一般来说,大多数单词都与一个主题相关。

图 14。题目是由 LDA,TND 和 NLDA 发现的。域:选举 2020

对两个不同领域的主题的简要评估强调了不同的单词集合可以代表特定的主题。没有一个正确的答案。因此,从数量和质量上评估题目的质量是很重要的。

结论

在本文中,我们了解了主题模型如何工作,主题噪音模型如何工作,以及它们如何提高我们从社交媒体数据中获得的主题的质量。我们学习了如何设置我们的编码环境来使用 gdtm 包中的主题噪声模型,如何运行主题噪声模型,以及如何保存和解释我们的结果。我们展示了我们可以用话题噪音模型生成的话题的质量,并且我们展示了由 NLDA 和 TND 生成的话题与由 LDA 生成的话题的比较。

在下一篇文章中,我们将研究一个半监督的主题噪声模型,该模型允许用户通过利用用户先前的领域知识将模型引导到一组更好的主题。

本文由乔治敦大学计算机科学教授兼海量数据研究所所长 Lisa Singh 合著。

除非另有说明,所有图片均为作者所有。

参考

[1] D. Blei,A. Ng 和 M. Jordan,潜在狄利克雷分配 (2003),机器学习研究杂志 3,993–1022。

[2] R. Churchill 和 L. Singh,话题-噪声模型:对社交媒体帖子集合中的话题和噪声分布进行建模 (2021),数据挖掘国际会议(ICDM),71–80。

[3] A.K. McCallum,MALLET:一个用于语言的机器学习工具包(2002 年),http://mallet.cs.umass.edu

[4] R. Řehůřek 和 p .索伊卡,利用大型语料库进行主题建模的软件框架 (2010),LREC 2010 年关于 NLP 框架新挑战的研讨会。45–50.

[5] R. Churchill 和 L. Singh, textPrep:用于社交媒体数据主题建模的文本预处理工具包 (2021),数据科学、技术和应用国际会议。

[6] R. Churchill 和 L. Singh,主题建模的演变 (2021),ACM Comput。Surv。(CSURV)

变形金刚和拥抱脸介绍

原文:https://towardsdatascience.com/an-introduction-to-transformers-and-hugging-face-13052ec9d72d

从自上而下的方法利用 NLP 的力量

Joshua Hoehne 在 Unsplash 上拍摄的照片

虽然大多数机器学习的灌输都是从简单的计算机视觉或表格数据问题开始的(我们现在都熟悉 MNIST 和 iris 数据集),但深度学习的最新发展已经将焦点转移到基于自然语言的问题上。虽然这可能看起来只是 ML 应用的另一个子集,但我相信变压器的出现及其几乎不可避免地扩展到许多应用领域,应该是该领域有抱负的参与者的首要关注点之一。

从 rnn 到变压器

NLP 的大部分进展都是通过广泛应用架构实现的。以下是用于自然语言任务的模型的一些主要发展:

  • 展开的 rnn
  • 编码器-解码器
  • RNNs 的注意机制
  • 第一变压器架构

自然语言中使用的原始模型是循环的:它们在每一步都保持着某种状态,这种状态随着新的输入被输入到模型的下一部分。你可以在很多地方读到这些 rnn 和 LSTMs,尤其是在 fastai book 的第 12 章。关键在于,在每一步都保持这种“状态”,可以让模型记住许多步骤之前的信息,并将其用于预测。如果我们展开香草 RNN,这就更有意义了。

及时展开 RNN(图片由作者提供)。

然后聪明的人把 rnn 用于机器翻译。这是一个特别复杂的问题,因为将一种语言中的单词映射到另一种语言涉及将任意长度的序列映射到另一个任意长度的序列。编码器-解码器架构就是针对这类问题开发的。随着新的输入被馈入,编码器更新状态,直到最后的输入,在最后的输入,最后的隐藏状态被用数字表示。解码器被馈送这个表示,并使用它来产生输出序列。想象一下解码器逐渐“压缩”每个输入,直到每个输入都整齐地处于最终隐藏状态。然后解码器“解包”,一次一个输出字。

具有一对 rnn 的编码器-解码器架构(图片由作者提供)。

尽管这无疑是朝着正确方向迈出的一步,但是由于只使用一个隐藏状态而导致的信息瓶颈是一个问题;解码器只能访问非常简化的序列表示。这对于长文本来说尤其成问题,因为在压缩到最终表示时,序列中很久以前的记忆和表示信息可能会丢失。结果,实践者开始让解码器访问编码器的所有隐藏状态。这就是所谓的注意力。

您可能想知道解码器是如何处理所有这些输入的。这就是注意力这个名字的由来;它提出了一些方法来区分编码器正在查看的状态的优先级。聪明的解决方案是在每个时间步长为每个编码器状态分配可学习的参数(或权重,或注意力)。在训练过程中,解码器了解在每个时间步长对每个输出的关注程度。这个过程如下所示。

一种具有针对一对 rnn 的注意机制的编码器-解码器架构(图片由作者提供)。

然而,即使这样也有一个问题:顺序计算,要求一次输入一个,阻止了输入序列的并行化。有几个原因可以解释为什么这不太理想,但其中一个原因是它太慢了。为了解决这个问题,的变形金刚向自由形式的注意力模型又迈进了一步。为了做到这一点,它删除了循环网络块,并允许关注网络同一层中的所有状态。这就是所谓的自我关注,如下图所示。这两个模块都有自我关注机制,允许它们查看所有状态,并将它们馈送到常规的神经网络模块。这比以前的注意力机制(就训练而言)要快得多,并且是许多现代 NLP 实践的基础。

原始转换器的编码器-解码器架构(图片由作者提供)。

自然语言处理中的迁移学习

迁移学习在 NLP 中是一件大事。有两个主要原因:(1)组装一个大的文本语料库进行训练通常是困难的(我们通常只有几个例子);(2)我们没有足够强大的 GPU(除非我们是 OpenAI 这样的人)来训练这些模型。迁移学习包括采用一个具有预训练权重的模型(其他人已经为我们完成了繁重的工作)并根据新数据对其进行微调。也就是说,我们采用旧模型的主体,根据我们的特定任务数据训练头部,并将它们拼接在一起。主体负责广泛的一般知识表示,模型的负责人根据实际任务和特定领域的数据对预测进行细微的调整。这意味着我们实际上可以用最少的数据和一个普通的 CPU 产生有用的语言模型。

迁移学习类似于计算机视觉,其中模型在大规模数据集上进行预训练,以教授模型视觉的基本特征。然后,我们对特定的数据集进行微调,比如对杂草的类型进行分类。与我们只是根据杂草数据从零开始训练模型相比,使用这种微调模型几乎总能获得更好的结果。

NLP 中的一个关键迁移学习方法是 ULMFiT:用于文本分类的通用语言模型微调。这个想法很简单:预训练一个模型来预测给定单词序列的下一个单词,你可能已经注意到,这不需要标记数据。在这种无监督的预训练之后,对你的特定数据进行相同的训练(预测下一个单词)。最后,在分类任务上训练这个新模型的头。

这一突破孕育了两个结合了自我注意和迁移学习的变形金刚:GPT 和伯特。两者都在许多 NLP 基准测试任务上取得了最先进的结果。

为什么需要拥抱脸?

为了标准化训练和使用语言模型的所有步骤,拥抱脸应运而生。他们正在通过构建一个允许轻松访问预训练模型、数据集和标记化步骤的 API 来民主化 NLP。下面,我们将用最少的代码,在最高的抽象层次上演示拥抱脸如何让任何程序员能够立即将 NLP 的前沿应用到他们自己的数据上。

展示变形金刚

Transformers 有一个分层的 API,允许程序员在不同的抽象层次上使用这个库。这些层中最抽象的是pipeline,它为我们处理一切,即将原始文本转换为来自微调模型的一组预测。

在这次演示中,我们将模拟杰克的女朋友发来的一条短信。(这个例子很好地反映了使用变形金刚的自然语言处理中对变形金刚的介绍,这是该领域的一本很好的参考手册。)她对他拒绝在周末陪她看《暮光之城》感到不满:

情感分类

在 HF Transformers 中,我们通过调用pipeline()函数并提供我们感兴趣的任务的名称来实例化管道。在这里,我们也提供模型;不要太担心这个,因为如果你没有传入一个模型,高频变压器将默认为你给它的任务的一个合理的模型。

现在我们有了自己的管道,让我们来做一些预测吧!每个管道接受一个文本字符串(或字符串列表)作为输入,并返回一个预测列表。每个预测都是一个 Python 字典,所以我们可以使用熊猫来很好地将它们显示为一个Data⁠Frame:

这给了我们以下情绪预测:

该模型已经非常有把握地预测到索菲的文本消息是负面的。这很好——她显然对杰克和他拒绝看好电影感到沮丧。

命名实体识别

情感分类是很好的第一步,但挑选出索菲谈论的特定事物会很有帮助。NLP 中对象的另一个词是命名实体。从原始文本中提取这些的过程是命名实体识别。同样,我们在这里需要做的就是将我们的任务加载到pipeline()函数中,并向它提供我们的文本消息:

图片作者。

我们可以看到,管道检测到了实体,并为它们分配了一个类别,例如PER表示人,等等。它也可以使用其他类别如LOC进行定位。分数代表模型对预测实体的信心。

问题回答

这个任务包括向模型输入我们的文本(称为上下文)以及我们希望模型回答的问题。我们来问问模特索菲想让杰克看什么电影(以防他自己还没想明白):

图片作者。

模特很自信杰克没看的电影是暮光之城。希望杰克也意识到了这一点。

总结

总结的目的是显而易见的:我们想从文本中提取意义并浓缩它。这个任务显然比前面的任务更复杂:它不仅需要提取或映射到一个数字,还需要生成文本。这是 NLP 神奇的一部分,我们的模型不仅学习解释文本,而且产生文本。就像我们免费获得额外的功能一样。

您会注意到,我们已经开始向这些管道传递一些参数,以便更好地控制输出。学习拥抱脸是关于向下移动抽象层次,直到我们进入代码的深度。

文本生成

假设杰克是一个糟糕的男朋友,并且刚刚发现拥抱脸。假设他想使用一个转换器来处理对 Sophie 文本的响应,因为他懒得自己动手。(在过去的几年里,文本生成肯定比写道歉文本要花费更多的精力。非常值得注意的是,在拥抱脸中生成一些文本可能真的比键入回复花费更少的时间。)

图片作者。

听着,我不认为这会让索菲感觉更好,但仍然值得注意的是,在大约一分钟内,我们根据一些上下文和提示对短信做出了(某种)连贯的响应。

结论

阅读完这篇文章后,显而易见的下一步是探索拥抱脸生态系统,它允许你做三件主要的事情:

  • **模特:HF Hub 拥有超过 20,000 名预先训练好体重的免费模特。
  • **记号化器:每条管道都由一个称为记号化的预处理步骤驱动,该步骤将原始文本分割成常见的记号,然后将这些记号映射成数字表示用于训练。由于这一步非常重要(还有一些子步骤,如标准化输入和将模型输出从标记转换回文本),HF 提供了标记化器来为我们做这件事。
  • **数据集:大多数自然语言处理数据集都很大——大到笔记本电脑的内存都装不下。HF 为数据集提供了一个标准接口,并使用智能缓存和内存映射来避免 RAM 限制。

要获得更多资源,一个很好的起点是拥抱脸文档。打开笔记本,写下你自己的示例文本,重新创建上面生成的 NLP 应用程序。祝你的 NLP 之旅好运!

参考

[1] L. Tunstall、L. Werra 和 T. Wolf,用转换器进行自然语言处理 s (2022),奥赖利媒体

[2] J. Howard 和 S. Ruder,面向文本分类的通用语言模型微调 (2018),arxiv

[3] A. Vaswani 等人,注意力是你所需要的全部 (2017),谷歌大脑

将 TigerGraph 与 Go 结合使用简介:探索新冠肺炎患者案例

原文:https://towardsdatascience.com/an-introduction-to-using-tigergraph-with-go-exploring-covid-19-patient-cases-f2c0e45849e4

通过 TigerGraph REST 端点和 TigerGo 使用 TigerGraph 和 Golang 查询图形数据库

图片来自 Pixabay

概观

介绍

最近,我学习了 Go (Golang)的基础知识,并决定用我的新知识构建一个名为 TigerGo 的 TigerGraph Go 包。在这篇博客中,我将介绍使用新库的基础知识,以及如何使用 Go 创建查询 TigerGraph 图形数据库。

工具

  • TigerGo (v0.0.2):今天创建了一个新的 TigerGraph Go 包装器,通过 Go 与 TigerGraph 图形数据库进行交互。
  • TigerGraph Cloud :免费创建和托管一个 TigerGraph 解决方案
  • Go :谷歌创造的语言。

第一部分:创建并准备一个 TigerGraph 解决方案

第一步:创建解决方案

首先,在 TG Cloud 上创建一个 TigerGraph 解决方案。在那里,导航到“我的解决方案”选项卡,然后按蓝色的“创建解决方案”按钮。

选择“我的解决方案”,然后按“创建解决方案”

在第一页上,选择任何初学者工具包。对于这个例子,我将使用新冠肺炎分析初学者工具包。

选择初学者工具包

第二页保持原样;这将建立一个免费的 TigerGraph 实例。在第三页上,适当地配置解决方案。

适当配置设置

注意:请记住您的子域和初始密码!

最后,在最后一页,验证所有信息都是正确的,然后按“提交”!您需要给解决方案几分钟时间来启动。

第二步:加载数据

解决方案启动并运行后,单击“应用程序”按钮启动 GraphStudio,然后从下拉菜单中选择“GraphStudio”。一旦启动,您可能需要登录。

打开 GraphStudio

在 GraphStudio 中,单击左上角的“MyGraph ”,导航到加载数据,然后按播放按钮加载所有数据。

加载所有数据

完美!这样,您就可以在 Go 中与解决方案进行交互了!

第二部分:创建一个 Go 项目并导入 TigerGo

第一步:导入包

有了创建的信息,接下来您需要创建一个新的 Go 项目,然后导入 TigerGo 包。首先,创建一个新的 Go 项目:

mkdir GoProject && cd GoProjectgo mod init GoProject

接下来,为代码创建一个主文件并导入库,github.com/GenericP3rson/TigerGo

package mainimport(
   "fmt"
   "github.com/GenericP3rson/TigerGo"
)func main() { // Main code will be here}

注意:当使用 Visual Studio 代码时,上面的代码会自动删除导入,因为它们没有被使用。

然后,您可以通过两种方式导入该库。您可以通过以下方式手动获取软件包:

go get github.com/GenericP3rson/TigerGo

或者,您可以使用以下命令让 go mod 自动导入它:

go mod tidy

注意:TigerGo 正处于实验阶段,在这篇博客之后将经历发展。然而,一般语法应该保持不变,镜像 pyTigerGraph

第二步:建立联系

首先,通过创建一个 TigerGraphConnection 开始。这将需要您的解决方案的用户名(默认为“tigergraph”)和密码、图形名称和您的主机。这也需要一个令牌,但是您可以将其留空,创建一个名为GetToken的令牌,然后填充这个令牌。

package mainimport(
   "fmt"
   "github.com/GenericP3rson/TigerGo"
)func main() { conn := TigerGo.TigerGraphConnection{ Token: "", // Leaving it empty for now
      Host: "https://SUBDOMAIN.i.tgcloud.io",
      GraphName: "GRAPHNAME",
      Username: "tigergraph",
      Password: "PASSWORD" } fmt.Println(conn.GetToken())}

对于我的例子,代码看起来像这样,密码被编辑:

package mainimport(
   "fmt"
   "github.com/GenericP3rson/TigerGo"
)func main() { conn := TigerGo.TigerGraphConnection{ Token: "", // Leaving it empty for now
      Host: "https://golang.i.tgcloud.io",
      GraphName: "MyGraph",
      Username: "tigergraph",
      Password: "PASSWORD" } fmt.Println(conn.GetToken())}

生成令牌后,在 token 下替换它,因为它是所有其他函数工作所必需的。

package mainimport(
   "fmt"
   "github.com/GenericP3rson/TigerGo"
)func main() { conn := TigerGo.TigerGraphConnection{ Token: "TOKEN",
      Host: "https://golang.i.tgcloud.io",
      GraphName: "MyGraph",
      Username: "tigergraph",
      Password: "PASSWORD" }}

完美!这样,您就可以开始运行命令了。

第三部分:命令

顶点命令

有几个 TigerGo 函数用于检索和操纵顶点。例如,使用 TigerGo,您可以获得某种类型的所有顶点。例如,如果您想要抓取所有患者顶点,您可以使用:

conn.GetVertices("Patient")

REST 等价查询/graph/{graph_name}/vertices端点。

client := &http.Client{ Timeout: time.Second * 10, } req, err := http.NewRequest("GET", fmt.Sprintf("%s:9000/graph/%s/vertices/%s", HOST, GRAPHNAME, vertex_type), nil) if err != nil { return err.Error() }req.Header.Set("Authorization", "Bearer "+TOKEN) 
response, err := client.Do(req)if err != nil { return err.Error() }

边缘命令

同样,TigerGo 提供了涉及边缘的函数。例如,假设我想看看病人如何与周围环境互动。为此,我可以抓住所有与病人相连的边。

conn.GetEdges("Patient", "SOME_PATIENT_ID")

REST 等价物将向/graph/{graph_name}/edges端点发出请求。

client := &http.Client{ Timeout: time.Second * 10, }req, err := http.NewRequest("GET", fmt.Sprintf("GET", fmt.Sprintf("%s:9000/graph/%s/edges/%s/%s/_", HOST, GRAPHNAME, source_vertex, source_vertex_id), nil)if err != nil { return err.Error() }req.Header.Set("Authorization", "Bearer "+TOKEN) 
response, err := client.Do(req)if err != nil { return err.Error() }

查询命令

最后,使用 TigerGo 包,您可以运行已安装的查询。新冠肺炎初学者工具包带有许多预安装的查询。例如,让我们运行ageDistribution,一个返回患者年龄分布的查询。

注意:请确保在运行查询之前安装了 ageDistribution。

conn.RunInstalledQuery("ageDistribution")

REST API 版本向/graph/{graph_name}/{query_name}发送 GET 请求。

client := &http.Client{ Timeout: time.Second * 10, }req, err := http.NewRequest("GET", fmt.Sprintf("%s:9000/query/%s/%s", conn.Host, conn.GraphName, queryName), nil)if err != nil { return err.Error() }req.Header.Set("Authorization", "Bearer "+TOKEN) 
response, err := client.Do(req)if err != nil { return err.Error() }

第四部分:结论、资源和后续步骤!

这就是 TigerGo 的快速介绍!想做点贡献?随时向回购提出拉取请求!

https://github.com/GenericP3rson/TigerGo [## GitHub-generic 3 rson/tiger go

github.com](https://github.com/GenericP3rson/TigerGo)

对围棋感兴趣?我之前写了一篇关于进入 Go 和创建一个包的博客,你可以看看!

https://shreya-chaudhary.medium.com/creating-and-deploying-your-first-go-package-eae220905745

有兴趣创建自己的 TigerGraph 包装器吗?点击查看官方端点的完整文档。

https://docs.tigergraph.com/tigergraph-server/current/api/built-in-endpoints

最后,如果您在开发项目时有任何问题或想与 TigerGraph 开发人员聊天,请加入 the Discord

https://discord.gg/DMHabbX3BA

注:除特别注明外,所有图片均由作者创作。

自然语言处理中的 Word2Vec 介绍

原文:https://towardsdatascience.com/an-introduction-to-word2vec-in-nlp-854e1c288894

Word2Vec 的直观数学解释

斯文·布兰德斯马在 Unsplash 上的照片

裁缝向她展示如何在她的夹克上缝制一颗纽扣。与此同时,她的朋友在外面等着看电梯按钮。

作为人类,本能的会注意到上面两句话中“按钮”这个词的不同含义。但是机器学习模型如何实现这一点呢?

任何使用自然语言的人的首要任务是用数字来表示单词。在过去的十年中,已经使用了许多技术,例如一键编码、TF-IDF、N-grams。但是这些技术没有包含单词语义,并且通常是稀疏表示。本文介绍了一种名为 Word2Vec 的单词嵌入技术。

Word2Vec

“从一个人和什么样的人交往,你就可以知道他说了什么。”——约翰·鲁珀特·弗斯

Word2Vec 是一种最先进的算法,用于在庞大的语料库中生成所有单词的固定长度分布式向量表示。Word2Vec 的有效性有两个原因——第一,使用固定大小的向量,这意味着向量大小不依赖于语料库中唯一单词的数量。第二,在向量表示中加入语义信息。Word2Vec 向量在将相似的单词组合在一起时非常有效。该算法可以基于单词在语料库中的位置做出强有力的估计。例如,“Kid”和“Child”是相似的,因此它们的向量表示将非常相似。

Word2Vec 可以在两种架构中实现— 连续单词包(CBOW)和 Skip-Gram。word 2 vec 的主要思想围绕着在固定大小的窗口中基于中心单词预测上下文(外部)单词,反之亦然。

例如,考虑语料库的以下部分:

…。做梦的可能性 来了 真的让生活变得有趣…..

在上面的例子中,固定窗口为 3,让“”为中心词,“ ”和“ ”为外词。给定中心词,CBOW 预测做梦的概率,给定上下文词做梦,Skip-Gram 预测中心词来。

  • CBOW —根据上下文(外部)单词预测中心单词。
  • skip-Gram-根据中心词预测上下文词

CBOW 和 Skip Gram 体系结构。例句——“想法可以改变你的生活”。图片作者。

目标函数

目标函数也称为误差函数或成本函数。在反向传播期间,神经网络使用梯度下降计算目标函数的最小值。下面是固定大小窗口中每个单词的概率的示例表示。

大小为 1 的示例窗口。使用中心单词“come”来预测上下文单词。图片作者。

考虑大小为 t 的语料库。给定中心单词 Wₜ,对于 t (1,2…,t)的每个位置,预测固定窗口大小 m 内的上下文单词。可能性由下式给出:

似然函数。图片作者。

似然函数取决于参数θ。θ是所有要优化的变量,它是 word 的向量表示。目标函数(也称为成本函数或损失函数)是对数似然的平均负值。

目标函数(损失函数)-平均负对数损失。图片作者。

该函数对所有中心单词进行平均,因此该函数不依赖于语料库中唯一单词的数量。使用称为梯度下降的方法最小化目标函数。

如何计算 P(Wₜ₊ᴊ /Wₜ)?

每个单词有两个向量:

  • v 当单词是中心词时
  • u 当单词是上下文(外部)单词时

对于中心词 c 和上下文词 o、

给定作者的中心词 c. 图像,外部词 o 的概率。

因为我们需要正值,所以使用指数。点积给出了 o 和 c 的相似性,在这种情况下,更大的点积意味着更大的概率。分母在整个语料库中归一化以给出概率分布,即分子除以语料库中每个词的相似度。事实上,上述函数是一个 SoftMax 函数,它是有意义的,因为:

  • Max:放大最大可能变量的概率。
  • 软变量:给最小的可能变量赋值。

我们希望上下文单词肯定与中心单词一起出现,也就是说,具有高概率。因此,SoftMax 函数是一个不错的选择。

最终向量是如何形成的?

在用上述目标函数训练神经网络之后,为每个窗口获得权重。这些权重存储在一个矩阵中。为了获得每个单词的最终密集向量,将权重矩阵乘以每个单词的对应的独热向量。

获得经训练的神经网络的权重矩阵。图片作者。

结论

单词嵌入是自然语言处理的关键步骤。使用 Word2Vec 获得单词的向量表示是非常高效的,因为这样形成的向量是密集的,并且携带对任何 NLP 应用程序都至关重要的语义信息。

感谢您阅读这篇文章!如果你有任何问题,请在下面留言。请务必关注我,获取我关于机器学习和数据科学的最新媒体帖子的更新:)。有问题可以在 LinkedIn 联系我。

贝叶斯统计入门

原文:https://towardsdatascience.com/an-introductory-primer-to-bayesian-statistics-3415ffa28488

贝叶斯统计世界的数学介绍

照片由媒体修改器Unsplash 上拍摄

1748 年,哲学家大卫·休谟写了一篇名为《论奇迹》的文章,认为过去写的不可信的奇迹故事不应该被相信,因为它们违背了自然法则。15 年后,托马斯·贝叶斯牧师的遗作提出了一个更重要的问题,“我们需要多少证据来支持任何主张?”

虽然他的论文确实有神学目的,但它也有许多数学含义,可以说在今天甚至更相关。

250 年后,他的简单公式仍然广泛适用,甚至创造了一个全新的统计学分支。它不仅是任何概率入门课程中的一个基本概念,也是我们今天拥有的许多重要算法和技术的动力。

贝叶斯公式的数学直觉

让我们以一个简单的参数估计问题为背景来看看贝叶斯公式。我们收集一些数据 x 并且我们确定这些数据是基于某个参数 θ生成的。

贝叶斯公式

在这种情况下,概率不是发生的概率,而是确定的程度。如果概率为 0,我们确定该主张为假,而如果概率为 1,我们完全确定该主张为真。

随着视角的改变,P(x|θ)是我们对θ值的确定性。请注意,这是一个以 x 为条件的概率。因此,后验概率本质上陈述了“假设我们已经收集了数据 x,我们对θ的确定性如下”

这个后部由 3 部分组成

  • P(x|θ) —似然:在给定当前模型的情况下,观察到证据的概率。这个模型可以看作是对数据设置了某些假设。例如,如果我们假设山洪暴发的速率为泊松分布,速率参数为 λ ,则可能性将基于泊松分布的概率质量函数
  • P(θ) —先验:我们参数的初始分布。使用前面的泊松例子,我们可以近似地得出速率参数λ遵循从 1 到 3 的均匀分布
  • P(X) —模型证据:所有参数观察到证据的概率。在大多数情况下,它的主要目的是作为一个标准化常数的后验。有效的概率分布的总和必须等于 1,而归一化常数有助于确保这一点。

这个公式的有用之处在于,当额外的证据被引入时,我们可以将我们的后验概率作为新的先验概率。这就产生了一个反复的过程,在这个过程中,我们慢慢地使我们的后验概率适应证据,以评估我们对某一主张的确定性程度。

由于 P(X) 是一个归一化常数,在大多数情况下,我们可以将后验概率表示为一个更简单的比例方程。这允许我们将贝叶斯公式改写如下:

贝叶斯公式——比例

现在我们已经理解了贝叶斯公式,让我们重新回顾一下 frequentist 线性回归,看看它与贝叶斯方法有什么不同。

频率主义者线性回归

在普通(简单)线性回归中,我们有一个模型,可以表述为:

线性回归模型

我们的目标变量 y 可以分解成确定性分量 β x +α 和被建模为随机噪声的随机分量ϵ。

由此,我们还可以得到 y 的条件分布,即 P(Y|X,β) ~ N(β x +α,ϵ)

在频率统计中,我们只关心可能性。在这种情况下,我们已经标记了数据,它略有不同,因为我们关注的是条件似然 P(Y|X,β)

由于似然性是以 X 和β为条件的,这个条件似然性就是正态分布的概率密度函数。

条件似然的概率分布

像往常一样,我们将其转换为更容易优化的对数似然。

条件对数似然

现在我们有了对数似然函数,我们可以执行优化,并找到使该似然函数最大化的β。注意,前两项与β无关,可以省略。同样,由于 1/2σ 是常数,所以不会影响优化,可以省略。剩下的就是下面的优化目标了。

如果这个目标看起来很熟悉,那是因为它是我们都非常熟悉的最小二乘目标。

对线性回归数据集执行最小二乘法就是进行最大似然估计。本质上,这给了我们一个线性回归的统计解释!

贝叶斯线性回归

在频率统计中,我们相信每个未知参数都有一个“真值”或“正确答案”。然后使用最大似然估计来获得这些参数的最佳估计。在贝叶斯统计中,参数本身是一个随机变量,我们试图从观测值中获得这个随机变量的分布。

一般线性回归方程

对于贝叶斯回归,我们将展示一般情况,从方程 Y = Xβ 开始。对于一个有 k 特征和 n 数据点的回归问题,β是一个 k+1 大小的向量(包括截距),X 是一个 n × k+1 矩阵。

然而,这与前一种情况有一个关键的区别。我们的参数 β 不再是点估计,但是有自己的分布。我们首先将其初始化为先验分布或 P(θ)。为了简化我们的计算,我们将从正态分布开始,以均值向量 μ₀ 作为我们的先验,我们的协方差矩阵将是单位矩阵 I

由此,我们现在可以求解后验分布。这是通过对模型中的所有变量应用贝叶斯公式来求解后验概率来实现的。

为后路求解

由于我们先前的假设,我们可以推导出这个特殊问题的解析解。回想一下,我们能够将后验概率表示为一个比例方程,它将先验概率和似然概率结合在一起。

我们的似然和先验都是多元正态分布,可以这样表示

似然和先验分布

现在,让我们把重点放在指数项上,把可能性和先验结合起来。

我们可以去掉不包含β的项,因为它们是常数,可以被吸收到归一化常数项中

从这里我们看到后验可以表示为一般形式的
C exp(-Q(β)) 其中 Q(β) 是二次多项式。这意味着后验概率必须是正态分布。现在我们的目标是将指数部分转换成(β—μ)ᵀσ⁻(β—μₙ).)的形式

二次部分可以分解成 βᵀ(XᵀX+I)β 。这给了我们一定是【βᵀ(xᵀx+i)β】

下一部分是-2μₙᵀσ⁻β。这对应于 2(YᵀX + μ₀)β 。通过一些操作和匹配,我们也可以导出一个表达式。

注意:使用这个因式分解会有一个μₙᵀσ⁻μₙ项,但是它可以被去掉,因为它不是β的函数

这给了我们最后的 PDF

其中 μₙσ分别是均值向量和协方差矩阵。

有趣的是,如果我们将初始均值 μ₀ 设为 0 向量,我们会得到一个非常熟悉的解。如果我们添加一个正则项, μₙ 的值将是( XᵀX + I)⁻ XᵀY ,这是岭回归解。这意味着,从贝叶斯的角度来看,岭回归可以被视为具有以 0 为中心的先验!

贝叶斯统计告诉我们如何在证据面前调整我们先前的信念。如果我们的观察值不是正态分布的,或者我们想要不同的起始参数,我们可以自由地这样做。贝叶斯统计为我们提供了一种计算(或至少近似)参数 θ 分布的方法

为什么要贝叶斯学习?

  1. 合并先前信息

先验是非常有用的,因为它帮助我们根据数据之外的一些知识来引导我们的模型达到不同的目标。贝叶斯统计为我们提供了一种整合这些信息的数学方法。事实上,流行的正则化技术,如 L2 权重惩罚,可以从贝叶斯的角度来看,具有特定类型的先验。

2。获得参数值的分布 贝叶斯技术的一个巨大优势是这些模型返回参数值的分布。然后,这些参数值可用于计算预测的置信区间。在错误预测会产生巨大后果的情况下,拥有这些置信区间可以帮助决策者更好地估计错误预测的概率。

3。数据不足 虽然 MLE 被证明是给定足够大数量样本的最佳估计量,但在许多情况下,我们没有足够的数据点。

一种可能性是,由于难以获得样本,数据很少。另一个更常见的可能性是数据是高维的。随着维数的增加,我们需要的样本数呈指数增长,导致我们的 MLE 估计不够精确。

对参数进行点估计会将所有的概率分布折叠成一个点,这会导致模型对错误的预测非常有把握。

使用贝叶斯方法有助于模型在观察外来数据点时降低可信度,并降低产生高可信度错误预测的可能性。

然而,贝叶斯技术有一个很大的弱点,那就是它们很难计算。在许多情况下,获得 P(X)在数学上是困难的,我们不能解析地求解后验概率。在这些情况下,贝叶斯推理是通过逼近后验来完成的。在下一篇博文中,我将深入研究这些方法,并展示它们是如何工作的。

如果你喜欢这篇文章,请在 Medium 上关注我!
在 LinkedIn 上连接:https://www.linkedin.com/in/reo-neo/

参考

I .古德费勒、y .本吉奥和 a .库维尔,2016 年。深度学习。剑桥(EE。UU。):麻省理工学院出版社,pp .第 5.6 节贝叶斯统计。

珀尔朱迪亚和丹娜麦肯齐。为什么之书。企鹅图书,2019。

理解 LSTMs & GRUs —直观的方式

原文:https://towardsdatascience.com/an-intuitive-approach-to-understading-of-lstms-and-grus-c2191611a37d

我们将继续证明 LSTMs & GRU 比你想象的要容易

虽然当你听到自然语言处理或序列模型时,RNNs 可能是你脑海中首先闪过的东西,但该领域的大多数成功并不归功于它们,而是(在很长一段时间内)归功于解决其消失梯度问题的改进版本。即,LSTM(长短期记忆)网络或不太常见的 GRU(门控循环单元)网络。

之前的故事中,我们展示了 rnn 如何工作,以及为什么它们会受到消失梯度的影响,这限制了它们处理序列数据中长相关性的能力。我们还暗示,这样的问题可以通过设计一个更复杂的循环层来解决。这正是 LSTM/GRU 网络所做的事情!它用一个更复杂的版本取代了标准的递归层,该版本可以用数学方法表示,以克服消失梯度问题;从而解决了 rnn 的长期依赖性问题,提高了 rnn 的整体性能。

但是什么是轮回层呢?

正如我们在前面的故事中所描述的,这是一个

1-接收前一层的激活并乘以一个权重矩阵( W ),就像 FFNNs 中使用的密集层一样

2-获取其自身由于序列中的先前输入而产生的激活,然后将其乘以权重矩阵( W ₕₕ).对于序列中的第一个字,激活向量通常被初始化为零,并且通常被称为“隐藏状态”。

3-将偏差添加到前面两个结果中

4-对其应用激活函数并提供结果(hₜ)as 输入到其他层,就像 ffnns 中使用的密集层一样

这在数学上等同于写作

递归层使用的激活函数 f 一般是【tanh(x)。它可能比 ReLU(和 Sigmoid)具有更好的收敛特性,如这里的

天舒刘Unsplash 上拍照

“门”的概念

我们之前已经讨论过,LSTM 或 GRU 网络改进 RNNs 的方式是用 LSTM 或 GRU 层代替标准的循环层;这两种方法都是经过精心设计的,用来处理渐变消失的问题。但是在我们揭示每一层的结构之前,我们需要理解“门”的概念,它在两种类型的层中都被用作构建块。

数学上,我们可以把门表示为

这意味着它只是一个循环层,但有两个根本的区别

1-我们不需要跟踪它的状态;它将始终使用其所在的 LSTM/GRU 图层的先前隐藏状态。这就是为什么我们稍后会删除 g 中的下标。

2-它使用乙状结肠激活。

事实上,它使用 Sigmoid 是什么使它的功能作为一个门; gₜ 的值将总是从 0 到 1,因此当它按元素乘以另一个向量比如 v 时,就好像 vgₜ* 是 v 经过门 gₜ 的结果;例如,如果 gₜ 全为 1,则 v 将完全通过,如果全为 0,则 gₜ 不会通过,并且如果需要,根据 gₜ 的值, v 的一些元素可能通过,而其他一些元素不会通过,程度不同。

最后要注意的是,从现在开始,一个门将用γ表示,无论何时使用多个门,它们都是相互独立的;这意味着每一个都与其自身的权重和偏好相关联。

汤姆陈Unsplash 上的照片

GRU 层(门控循环单元)

GRU 网络用 GRU 层代替 RNNs 中的循环层。GRU 层使用两个门,一个称为关联门(γᵣ),另一个称为更新门(γᵤ)

一旦计算出两个门,GRU 层以下面的方式产生它的隐藏状态。

1-通过递归层计算一个候选隐藏状态 ( ĥ ),该递归层在通过关联门(γᵣ)后使用该层的先前隐藏状态。

2-使用更新门(γᵤ)计算 GRU 层的隐藏状态,如下所示

为了获得一些直觉,我们先假设没有关联门(设置为全 1)。在这种情况下,每次迭代,更新门决定 GRU 层是否将作为标准递归层(基于输入学习新的隐藏状态)或者跳过学习新的隐藏状态并保留在前一次迭代中所学习的。你可以通过代入上式,确认前者成立是γᵤ全 1,后者成立是γᵤ全 0。

这意味着,如果我们有一个具有长期相关性的序列,例如“我今天早上看到的猫****非常饥饿】,那么网络有一种方法来保留它已经学习了“猫”,方法是从该点开始将更新门设置为低,这样当它必须在是/是之间做出决定时,它会记住它。当然,在现实中,更新门采用连续的值(它不必 100%保留或 100%更新它所学习的内容),你可以认为它只是简单地决定我们应该在多大程度上保留旧信息,以及我们应该在多大程度上用新信息替换旧信息。

关联门有一个更简单的角色,它回答“在生成候选状态时,应该在多大程度上依赖于最后一个隐藏状态?我们应该强烈依赖它吗?或者,即使更新门决定它需要涉及当前令牌的新信息,那么先前的状态是不重要的,并且我们可以假装当前令牌是序列中的第一个(即,通过将γᵣ设置为 0 来重置),这是真的吗?”

最后,值得强调的是,虽然人们可以很容易地从方程中看出,门有助于 GRU 改善 RNN 的短期记忆,但网络必须自己找出答案。有了足够的训练数据和最小化损失的目标,神经网络将有希望能够学习如何设置权重,以便在给定输入序列的情况下,使用门,例如通过改善短期记忆问题来改善网络的性能。在我们进行修改之前,RNN 没有办法通过解决这个问题来提高其性能。

德里克·汤姆森在 Unsplash 上拍摄的照片

LSTM 层(长短期记忆)

您可以将 LSTM 图层视为 GRU 图层的更复杂版本。主要区别在于

1-除了隐藏状态之外,它还有一个“单元格状态”

2-它放弃了相关性门,而是使用一个输出门用于类似的目的。

3-除了更新门之外,它还有一个遗忘门

一旦更新, 忘记输出门被计算,LSTM 以如下方式进行

1-通过使用层的先前隐藏状态的递归层来计算候选单元状态

2-决定应该通过更新门保留多少候选状态,以及应该通过忘记门忘记多少先前的单元状态

这可以被认为是对 GRU 的改进,在 GRU,如果网络决定保留%的先前隐藏状态,那么这意味着将引入(100-A)%的候选隐藏状态。在这里,网络有单独的更新和遗忘门,所以它不会被迫做出这样的决定。如果它愿意,它可以更新 0%的候选小区状态,并完全忘记之前的小区状态。

3-决定有多少单元状态应该通过输出门激活该层

这很像 GRUs 中的关联门。事实上,我们可以消除这个等式,但是将三个门、单元状态和进一步的层输入中的 h ₜ_₁ 的每个实例替换为(γₒhₜ_₁).*不同之处在于,它不仅适用于候选隐藏状态计算,还适用于所有其他计算。

让我们结束吧!

以下是针对 LSTM 和 GRU 图层中的每个令牌执行的公式:

虽然你可能会认为 lstm 总是做得更好,因为它们的复杂性和更多的可训练参数,有一些情况下 gru 往往比它们更好,但总的来说 lstm 是更占优势的选择。为了将大量的参数和计算包含在一个 LSTM 图层中,我绘制了下面图层的低层次表示。看一看它,以确保你理解流程。

作者绘图。实际上,x 和 h 只有一个来源

在我们结束之前,有一件小事我想弄清楚。尽管我们上面提供了所有的直觉,无论是 LSTM 还是 GRU,你总是可以通过时间进行反向投影,以显示他们解决/改善了消失梯度问题。你甚至可以假设,研究人员想出的方法包括通过时间进行反向投影,并思考如何修改方程以缓解梯度消失的问题。

我们的故事到此结束。希望它能帮助你更好地理解 LSTMs 和 GRUs 是如何工作的。下次见,再见。

MCMC 与变分推理的直观比较

原文:https://towardsdatascience.com/an-intuitive-comparison-of-mcmc-and-variational-inference-8122c4bf37b

估计未观测变量的两种巧妙方法

Yannis Papanastasopoulos 在 Unsplash 拍摄的照片

我最近开始研究“概率编程和黑客贝叶斯方法”,这在我的任务清单上已经有很长时间了。作为一个参加过统计学和机器学习课程(包括贝叶斯统计)的人,我发现我正在通过这种编码优先的方法理解一些以前从来不清楚的事情。我强烈推荐这本书!

一些慷慨的人更新了代码以使用 TensorFlow 概率库,这是我在工作中经常使用的。因此,我的大脑终于开始通过反向传播在贝叶斯潜在变量分析和机器学习之间建立联系。我正在将马尔可夫链蒙特卡罗(MCMC)的世界与神经网络联系起来。回想起来,这似乎是我应该早点明白的事情,但是,好吧。如果你和我一样,脑子里没有这种联系,让我们现在就解决它。

贝叶斯潜在变量分析

“潜变量”=隐藏;没有被直接观察或者测量。

在这种情况下,“分析”是建立概率模型来表示数据生成过程的过程。一个常见的例子是开关点分析。假设我们有一段时间间隔内的统计数据(例如,一个世纪以来每年发生的起矿难)。我们想估计新的安全条例实施的年份。我们预计事故率将在这一点后下降,之前给出一个事故率,之后给出一个不同的(希望更低的)事故率。

矿难发生率变化的例子。图片作者。

这是与神经网络的第一个联系:我们用来拟合数据的模型在某种程度上是任意的。“任意”可能是一个强烈的词——你可以说模型结构是合理选择的,带有一些艺术许可。

卡通模特。图片作者。

我们观察的是计数数据,因此泊松分布对于生成我们观察的数据是有意义的。但是,实际上,有两个泊松分布,或者至少有两个利率参数——一个在转换点之前,一个在转换点之后。然后是切换点本身,它是我们数据范围内的一个时间值。

但是像任何建模一样,这是一个任意的模型结构。我们希望它足够简单,具有有用的表达能力,但仅此而已。这非常类似于聚类(想想 K-means 或者 EM 算法,所以你可以想象添加更多的开关点(即更多的聚类)。你可以像对待线性函数而不是固定比率一样对待之前和之后的事故率,把它变成一个在转换点斜率发生变化的回归问题。使这个模型变得复杂或丰富的选择是无穷无尽的。神经网络也是如此。选择正确的模型架构和大小是一个反复试验的问题,艺术许可,避免过度拟合,但允许足够丰富的模型表达。

下一个相似性,我们有我们想要估计的隐藏变量。从某种意义上来说,我们希望为数据“拟合一个模型”。我们希望选择最大化观察数据可能性的参数。我们有一个模型,允许我们计算 p(X|Z) (给定一些参数 Z ,数据 X 的概率),我们想要 p(Z|X) (也就是“后验概率”)。这可以用一种经典的统计方法来完成,即受人喜爱的 MCMC。等效地,可以在称为变分推理的过程中使用梯度下降来学习参数。我们将从较高的层面来讨论这两个问题。

MCMC:采样(非“拟合”)模型参数

MCMC 是一种抽样方法。对于从潜在(未观察到的)模型参数的分布中取样,这是一个非常聪明的算法。重要的是,它不是一种估计参数化分布的方法。MCMC 只是生成参数样本,然后您可以将其绘制成直方图,并以分布图的形式查看。但是你没有“拟合”数据。您正在对可能生成数据的模型参数进行采样。

这种工作方式非常巧妙。更多细节,请查看 RitVikMath 的视频或阅读大都会算法。你从产生一些随机的候选人开始。一般来说,这不是完全随机的,尽管它可能是。通常你是使用先验分布来生成这些值的(现在是贝叶斯)。例如,为了成为有效的泊松参数,速率参数必须是非负的,并且它们应该生成看起来类似于我们的输入数据的计数,因此一个好的起点是以观察到的计数数据的平均值为中心的指数分布。但是,最终,它只是一种生成速率的开始猜测的方法。开关点也是如此。它可以由我们数据的起始年和结束年之间的均匀分布产生。

我们设置了一些合理的先验,并为模型中的每个参数随机抽取了一个候选样本(在我们的例子中是 3 个)。这允许我们估计给定这些随机生成的值 p(X|Z) 的数据的可能性。在下面的漫画中,图中的红线表示事故率转换点,如果您沿着这条线在每个数据点可视化泊松分布,您可以想象如何在此模型下获得数据的可能性。在这种情况下,第二个速率(切换点右侧)看起来太高,数据将被评定为不太可能。

生成模型的动画。有两个速率参数,分布为指数分布(因为它们必须为正)和切换点,分布为均匀分布。每个分布下的小红线显示当前的样本。散点图中的红线显示了这些参数如何转化为计数数据。在这种情况下,模型不能很好地解释数据,这意味着样本还不是很好。图片作者。

这就是事情变得有趣的地方。从最后一个样本(完全随机的)开始,我们将一个核函数(通常是正态分布)集中在这些点上,并再次采样,但现在我们从核分布中采样。如果数据的可能性比上次猜测的高,我们立即接受新的参数样本。如果数据的可能性等于或低于我们当前的步骤,我们按比例接受概率。换句话说,我们绝对、永远、永远接受能更好地解释我们数据的可变样本。我们只是有时会接受更差的。

随着时间的推移,你可以看到马尔可夫链是如何从最合理的参数分布开始采样的,即使我们不知道那个分布是什么。因为我们可以评估数据的可能性,所以我们能够越来越接近对参数的良好猜测。因为我们会在 100%的时间里接受更好的猜测,只是有时会接受不太好的猜测,所以总的趋势是更好的猜测。因为我们仅基于最后的猜测进行采样,所以趋势是样本向相对于数据更可能的参数漂移。

具有更好的可变样本的卡通模型。这些数据比第一张图中的数据更能说明问题。图片作者。

因为进入“合理猜测”的空间需要几个样本,通常 MCMC 在使用前要被采样很多次。这就是所谓的“老化”,老化的适当长度也是一种猜测(就像许多建模是…叹息 )。您还可以看到核函数的选择如何影响最终结果。比如你的核函数是超分散的,那么采样的参数也会更分散。

最终结果是,您可以对合理的参数进行采样,这些参数与您的数据非常“吻合”。你可以将这些采样参数绘制成直方图,估计它们的平均值或中值,以及所有这些好东西。你要去比赛了!

变分推理:新算法,同样的目标

如果你花时间在神经网络,反向传播领域,那么像我一样,你可能会看到各种各样的相似之处。向损失函数的最优值迈进的一小步?听起来像梯度下降!事实上,同样的模型可以在一个叫做变分推理 (VI)的过程中使用梯度下降来“拟合”。

同样,如前所述,我们有一个模型可以用来计算 p(X|Z) ,我们想要的是逆运算: p(Z|X) 。后验描述了使观察数据的可能性最大化的参数。VI 背后的思想是通过最大化证据下限(ELBO) 损失函数来拟合 p(Z|X) 的代表性分布。我们将依次讨论这些作品。

当然,我们不知道后验概率, p(Z|X) ,提前,所以我们用一个足够灵活的分布来表示它。在本教程中,他们展示了几个选项,包括独立正态分布(模型中的每个潜在变量都有一个),多元正态分布(分布及其协方差是可学习的),以及最奇特的选项,作为替身的神经网络,也称为自回归流。关键是,很多分布可以用来近似后验概率。

厄尔巴岛本身就是一个话题,值得关注。维基百科的文章挺好的,还有机器学习&模拟的系列 YouTube 视频。从发展直觉的角度来看,ELBO 是 Z 的先验估计值和最优点估计值之间的平衡行为,可最大化观测数据 x 的似然性。这是通过损失函数同时激励高数据似然性和惩罚偏离先验的大偏差来实现的。

通过最大化 ELBO,我们可以从后面采样,类似于 MCMC 方法。

示例

现在你已经知道 MCMC 和 VI 是什么了,这里有一个使用张量流概率的例子。我将在这里强调一些片段。完整的例子是 GitHub 上的

我坚持使用上面描述的灾难计数模型。在 MCMC 示例中,模型是这样建立的:

disaster_count = tfd.JointDistributionNamed(
    dict(
        early_rate=tfd.Exponential(rate=1.),
        late_rate=tfd.Exponential(rate=1.),
        switchpoint=tfd.Uniform(low=tf_min_year, high=tf_max_year),
        d_t=lambda switchpoint, late_rate, early_rate: tfd.Independent(
            tfd.Poisson(
                rate=tf.where(years < switchpoint, early_rate, late_rate),
                force_probs_to_zero_outside_support=True
            ),
            reinterpreted_batch_ndims=1
        )
    )
)

结果是一个联合分布,在给定一些参数的情况下,它可以评估我们的数据的概率。我们根据一组特定的参数评估数据的对数概率,如下所示:

model.log_prob(
  switchpoint=switchpoint,
  early_rate=early_rate,
  late_rate=late_rate,
  d_t=disaster_data
)

接下来,我们建立一个 MCMC 对象,如下所示:

tfp.mcmc.HamiltonianMonteCarlo(
    target_log_prob_fn=target_log_prob_fn,
    step_size=0.05,
    num_leapfrog_steps=3
)

我们用一个TransformedTransitionKernel包装 MCMC 对象,这样我们就可以在连续的空间中进行采样,同时将学习限制在支持我们的模型(例如,没有负的年份值)。我们定义了烧入步骤的数量,以及我们希望抽取的样本数量。然后,不到一分钟后,我们就有了样品。

MCMC 抽样的模型变量的分布。图片作者。

MCMC-根据灾难数据绘制的估计开关点。图片作者。

在 VI 示例中,我们再次设置了一个联合分布:

early_rate = yield tfd.Exponential(rate=1., name='early_rate')
late_rate = yield tfd.Exponential(rate=1., name='late_rate')
switchpoint = yield tfd.Uniform(low=tf_min_year, high=tf_max_year, name='switchpoint')
yield tfd.Poisson(
    rate=tf.where(years < switchpoint, early_rate, late_rate),
    force_probs_to_zero_outside_support=True,
    name='d_t'
)

这次它被设置为一个生成器(因此有了yield语句)。我们确保模型通过锁定来评估数据的对数似然性:

target_model = vi_model.experimental_pin(d_t=disaster_data)

我们建立了一个灵活的分布,可以优化,以代表后验。我选择了自回归流(一种神经网络),但正如所讨论的,有许多选项。

tfb.MaskedAutoregressiveFlow(
    shift_and_log_scale_fn=tfb.AutoregressiveNetwork(
        params=2,
        hidden_units=[hidden_size]*num_hidden_layers,
        activation='relu'
    )
)

同样,我们使用双投影器将连续的数值优化转化为我们模型的支持。最后,我们像这样拟合我们的模型:

optimizer = tf.optimizers.Adam(learning_rate=0.001)
iaf_loss = tfp.vi.fit_surrogate_posterior(
    target_model.unnormalized_log_prob,
    iaf_surrogate_posterior,
    optimizer=optimizer,
    num_steps=10**4,
    sample_size=4,
    jit_compile=True
)

VI 抽样的模型变量的分布。图片作者。

VI-灾难数据上绘制的估计切换点。图片作者。

在这种情况下,VI 不像 MCMC 那样适合切换点,我也不完全确定为什么。如果您有任何想法,请对本文进行评论或在 GitHub 上提出问题!但是,无论如何,你明白了。

结论

我希望这个简短的练习有启发性。MCMC 用于从潜在变量中取样。VI 用于使用梯度下降来拟合潜在变量。两者都可以用张量流概率来完成。现在我们知道了。

句子的直观解释——伯特

原文:https://towardsdatascience.com/an-intuitive-explanation-of-sentence-bert-1984d144a868

使用暹罗伯特(SBERT)网络的句子嵌入的直观解释以及如何编码

哈米什·道森在 Unsplash 上拍摄的照片

在这篇文章中,我将解释你需要知道的关于句子背后的基本机制——BERT 模型。我还将详细介绍其独特的架构如何用于特定的、独特的任务,如语义相似性搜索,以及如何对此进行编码。

如今,许多广泛用于大量 NLP 任务的转换器,如问题回答、语言建模和摘要,都是单词级的转换器。例如,像 BERT、RoBERTa 和原始 transformer 这样的 transformer 通过计算单词级嵌入来解决许多不同的任务,它们还被训练来执行像掩蔽语言建模这样的任务。然而,对于像语义搜索这样需要强大的句子级理解的任务,仅使用单词级转换器在计算上变得不可行。

语义搜索是一项任务,包括找到与目标/给定句子在意义上相似的句子。例如,在包含 10 个句子的段落中,语义搜索模型会返回前 k 个彼此意思最接近的句子对。使用像 BERT 这样的转换器需要将两个句子都输入到网络中,当我们比较大量的句子(几百个/几千个)时,所需的计算使 BERT 成为不可行的选择(需要几个小时的训练)。

这篇文章旨在通过句子-BERT (SBERT)来克服这一挑战:这是标准预训练 BERT 网络的一种修改,它使用连体和三元网络为每个句子创建句子嵌入,然后可以使用余弦相似度进行比较,使大量句子的语义搜索变得可行(只需要几秒钟的训练时间)。

伯特是做什么的?

BERT 以成对的方式解决语义搜索。它使用一个交叉编码器:将两个句子传递给 BERT,并计算相似性得分。然而,当被比较的句子数量超过数百/数千个句子时,这将导致总共进行 (n)(n-1)/2 次计算(我们本质上是将每个句子与其他每个句子进行比较;即强力搜索)。在现代 V100 GPU 上,这将需要几个小时的训练时间(具体来说是 65 个小时)。下图显示了 BERT 的交叉编码器网络:

作者图片

BERT 交叉编码器由一个标准的 BERT 模型组成,它将两个句子 A 和 B 作为输入,由一个[SEP]标记分隔。在 BERT 的顶部是前馈层,它输出相似性得分。

为了克服这个问题,研究人员试图使用 BERT 来创建句子嵌入。最常见的方法是向 BERT 输入单个句子——记住 BERT 计算单词级嵌入,所以句子中的每个单词都有自己的嵌入。在句子被输入到 BERT 之后,由于 BERT 的单词级嵌入,生成句子嵌入的最常见方式是通过平均所有单词级嵌入或者通过使用第一个标记(即[CLS]标记)的输出。然而,这种方法经常导致糟糕的句子嵌入,通常平均比平均手套嵌入更差。

什么是暹罗网络?

连体神经网络是包含两个(或更多)相同子网/模型的特殊网络,其中(通常)两个模型共享/具有相同的参数/权重。参数更新反映在两个子模型中。连体网络最常用于计算输入的相似性得分,因此有许多应用。

暹罗网络如此强大和多功能的一个原因是,它们主要通过计算两个事物(通常是图像,但在我们的情况下是句子)之间的相似性来训练。因此,添加新的类(用于分类任务)或比较两个事物(例如,两种类型的图像)变得容易,其中一个项目具有在训练期间不存在的分类。这是一个表示暹罗网络架构的图表。该模型使用暹罗网络/共享权重/2 个 CNN 来计算两个图像的编码,然后使用相似性函数和激活函数来计算两个编码之间的相似性。

作者图片

那么具体怎么训练暹罗网络呢?

你可能会想,如果暹罗网络也是以成对的方式训练的,它们的计算效率不也应该很低吗?

这是不正确的。虽然暹罗网络是以成对的方式训练的,但它们不像 BERT 那样用项目组中每个可能的成对组合来训练。本质上,SBERT 网络使用称为三重损失的概念来训练其连体结构。为了解释三重损失,我将使用比较/找到相似图像的例子。首先,网络随机选择一个锚图像。此后,它找到一个正对(属于同一类的另一个图像)和一个负对(属于不同类的另一个图像)。然后,网络使用相似性得分计算每对图像的 2 个图像之间的相似性得分(这因您使用的函数而异)。使用这两个相似性分数,网络然后将计算损失分数,然后基于该损失更新其权重/运行反向传播。数据集中的所有图像被分配到这些三元组中,因此网络能够在数据集中的每个图像上被训练,而不必运行计算效率低的强力/在每个图像组合/对上被训练。

SBERT 论文的作者使用了类似的东西,但不是确切的机制。毕竟不像图像分类,在语义搜索中,句子并没有一个可以关联的类。作者使用了一种独特的技术,包括找到并最小化句子之间的欧几里德距离,并使用这种度量来找到哪些句子对被认为是积极的,哪些句子对被认为是消极的。你可以在他们的论文这里中读到更多。

SBERT 是做什么的,它是如何工作的?

如果你看看 BERT 最初的交叉编码器架构,SBERT 与此类似,但去掉了最后的分类头。与 BERT 不同,SBERT 使用连体架构(正如我在上面解释的那样),其中包含两个基本相同并共享相同权重的 BERT 架构,SBERT 在训练期间将两个句子成对处理。

假设我们在 SBERT 中把句子 A 喂给了 BERT A,把句子 B 喂给了 BERT B。每个 BERT 输出汇集的句子嵌入。虽然最初的研究论文尝试了几种汇集方法,但他们发现均值汇集是最好的方法。池化是一种概化网络中要素的技术,在这种情况下,意味着池化通过对 BERT 中的要素组进行平均来工作。

在池化完成后,我们现在有 2 个嵌入:1 个用于句子 A,1 个用于句子 b。当模型正在训练时,SBERT 连接这 2 个嵌入,然后它们将通过 softmax 分类器运行,并使用 softmax-loss 函数进行训练。在推断时——或者当模型实际开始预测时——然后使用余弦相似度函数来比较两个嵌入,这将输出两个句子的相似度得分。这里是 SBERT 在微调和 at 推断时的示意图。

作者图片

你如何使用 SBERT?

SBERT 有自己的 Python 库,你可以在这里找到。使用它就像使用拥抱脸变形库中的模型一样简单。例如,要计算句子嵌入,您可以这样做:

**from** **sentence_transformers** **import** SentenceTransformer
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')sentences = ['The quick brown fox jumps over the lazy dog', 
             'Dogs are a popular household pet around the world']embeddings = model.encode(sentences)
for embedding in embeddings:
  print(embedding)

在这里,我使用了“释义-MiniLM-L6-v2”预训练模型,但还有许多其他预训练模型可以使用,你可以在这里找到。

使用 SBERT 计算句子相似度也很容易。下面是你如何计算/找到句子列表中句子之间的最高相似度分数。

**from** **sentence_transformers** **import** SentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')sentences = ['The cat sits outside',
             'A man is playing guitar',
             'I love pasta',
             'The new movie is awesome',
             'The cat plays in the garden',
             'A woman watches TV',
             'The new movie is so great',
             'Do you like pizza?']#encode the sentences 
embeddings = model.encode(sentences, convert_to_tensor=**True**)#compute the similarity scores
cosine_scores = util.cos_sim(embeddings, embeddings)#compute/find the highest similarity scores
pairs = []
**for** i **in** range(len(cosine_scores)-1):
    **for** j **in** range(i+1, len(cosine_scores)):
        pairs.append({'index': [i, j], 'score': cosine_scores[i] 
                                                             [j]})#sort the scores in decreasing order 
pairs = sorted(pairs, key=**lambda** x: x['score'], reverse=**True**)**for** pair **in** pairs[0:10]:
    i, j = pair['index']
    print("**{}** **\t\t** **{}** **\t\t** Score: **{:.4f}**".format(sentences[i],
                                  sentences[j], pair['score']))

这些代码片段取自 SBERT 图书馆网站。你可以在这里探索更多。上面的代码输出:

The new movie is awesome The new movie is so great    Score: 0.8939 The cat sits outside    The cat plays in the garden    Score: 0.6788 I love pasta    Do you like pizza?    Score: 0.5096 
I love pasta    The new movie is so great    Score: 0.2560 
I love pasta    The new movie is awesome    Score: 0.2440 
A man is playing guitar The cat plays in the garden   Score: 0.2105 The new movie is awesome    Do you like pizza?    Score: 0.1969 
The new movie is so great    Do you like pizza?   Score: 0.1692 
The cat sits outside    A woman watches TV    Score: 0.1310 
The cat plays in the garden    Do you like pizza?    Score: 0.0900

我希望您觉得这些内容很容易理解。如果你认为我需要进一步阐述或澄清什么,请在下面留言。

参考

使用连体伯特网络的句子嵌入:https://aclanthology.org/D19-1410.pdf

SBERT 库:https://www . SBERT . net/#:~:text = sentence transformers % 20 is % 20a % 20 python % 20 framework,for % 20 more % 20 than % 20100% 20 languages

使用 MLflow 跟踪您的 ML 实验的直观指南

原文:https://towardsdatascience.com/an-intuitive-guide-to-track-your-ml-experiments-with-mlflow-7ac50e63b09

帮助您改进 Sklearn 模型的重现性、管理和实验跟踪

Artem Maltsev 在 Unsplash 拍摄的照片

我已经浪费了很多时间来运行我的 Jupiter 笔记本,以建立一个具有最佳性能的机器学习模型。最佳解决方案主要取决于超参数的特定组合,在走到隧道尽头之前,很难记住我尝试过的所有实验。

最近,我发现了 MLflow,这是一个非常有效的开源平台,用于完整管理机器学习生命周期,其中的主要阶段是收集数据、数据准备、创建模型、模型训练和模型部署。它有非常好的功能,我将在下一节解释。

在这篇文章中,我将重点关注 MLflow 跟踪训练跑步指标和每次跑步实验的模型超参数的能力。我将通过一个用 Sklearn 训练回归器的简单例子向您展示如何利用这个强大的工具。这些步骤如下:

  1. 简介
  2. 先决条件
  3. 在 train.py 上训练一个 MLflow 模型
  4. 在本地主机上可视化结果

介绍

作者插图

如前所述,MLflow 是一个管理机器学习生命周期的平台。它提供了四个组件,可以一起工作,也可以单独工作,这取决于应用程序。

  • MLflow Tracking 用于跟踪/记录实验。首先,在运行代码时存储日志记录参数、指标和输出文件,然后可以在本地主机上可视化所有实验的结果。在本文中,我们将重点关注使用 Python 的日志记录和查询实验。
  • MLflow 项目是一个组件,用于以可重用和可复制的方式打包数据科学代码。
  • MLflow Models 用于封装机器学习模型。有多种方法可以保存和加载 MLflow 模型。例如,我们训练一个 Sklearn 模型,然后使用函数log_model将其记录为当前运行的 MLflow 人工制品
  • Model Registry 是专门用于将模型添加到模型注册表中的工具。它允许提供从试生产到生产的模型的年表。它还允许在每次运行时向模型添加描述。

先决条件

作者插图

在本教程中,我使用 Visual Studio 代码作为 IDE。它是免费开源的,支持多种语言,每种语言都需要安装扩展。此外,它能够处理 Jupyter 笔记本,这对于操纵和分析代码中的数据是不可或缺的。它还集成了一个终端,需要这个终端来编写一些命令行来使用 MLflow。除了这些优点之外,它还可以管理项目文件夹中的文件和文件夹。我建议你查看这个简单的教程,开始使用 VSCode 和 Python。主要步骤是:

  • 如果您的本地机器上没有来自 python.org 的 Python,请安装它
  • 在 VSCode 中添加扩展名“Python”
  • 创建新的 Python 环境

配置完 Python 环境后,就该安装 MLflow 了:

pip install mlflow

为了确保安装运行良好,您应该检查是否在项目的文件夹中找到了名为“mlruns”的文件夹。

作者插图

无论何时运行用 Python 编写的程序,MLflow Python API 日志都会运行到 mlruns 目录中的文件中。

在 train.py 上训练 MLflow 模型

在本例中,我们使用自行车共享数据集,根据温度、风速和湿度等特征来预测每小时租赁自行车的数量。数据集存储在 UCI 的机器学习知识库【2】。此外,我们将训练一个随机森林回归器,它采用三个超参数,n 估计量,最大特征和最大深度。所有代码都保存在一个带有格式的文件中。py,名为 train.py,如下所示:

当您右键单击代码选项“在终端中运行 Python 文件”时,您应该得到类似的输出:

作者插图

我们应该关注的最基本的部分是从with mlflow.start_run()行开始,它允许在程序块结束时自动结束运行。方法mlflow.log_parammlflow.log_metric提供了在当前运行下记录参数和度量的方法。

为了试验超参数的其他值,我们可以在下面的命令中将它们作为参数传递:

python train.py 200 8 6

我建议你试探一下其他价值观,对 MLflow 的效率有更好的认识。

本地主机上可视化结果

为了比较超参数和模型的不同组合,我们可以在终端上运行命令:

mlflow ui

它返回一个 URL,您可以在其中找到一个记录所有超参数和相应的结果指标的表( http://localhost:5000 )。

作者插图

您可以注意到,您所有的实验现在都存储在这个表中。如果您有相同试验的副本,您也可以删除其中一个。此外,您可以将所有结果下载到一个 CSV 文件中,以便快速分析模型的性能。

最终想法:

在这篇文章中,我们看到了 MLFlow 是什么,以及如何利用它来跟踪和存储我们的试验。我希望这篇教程对你开始使用它有所帮助。如果你有兴趣发现其他平台来跟踪实验,请查看本指南,其中介绍了 Neptune.ai,感谢阅读。祝您愉快!

参考文献:

[1]https://www.mlflow.org/

https://www.capitalbikeshare.com/data-license-agreement

[3] 训练、服务和评分线性回归模型

你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都可以收到电子邮件!

NLP 电影史

原文:https://towardsdatascience.com/an-nlp-movie-history-44487ec0144f

绘制电影潮流的起伏

自由剧院在新奥尔良,路易斯安那州,加利福尼亚州。1936.来自纽约公共图书馆

超级英雄电影会过时吗?漫威的粉丝可能会怀疑是否能在票房上看到黑寡妇或雷神炸弹这样的电影——他们的态度肯定是有道理的,因为像 【蜘蛛侠:英雄无归】【蝙蝠侠】 这样的电影取得了巨大成功。尽管如此,电影历史充满了曲折:类型上升到流行,保持在聚光灯下一段时间,然后悄悄地离开舞台。例如,想想 20 世纪 30 年代诙谐的古怪喜剧,20 世纪 40 年代复杂的黑色电影,或者 20 世纪 70 年代轰动一时的灾难电影。

数据科学能否帮助我们理解电影品味的这些突然变化,并预测什么类型的电影可能很快就会出现在我们身边的屏幕上?

为了回答这个问题,我们可以求助于自然语言处理——对于这种分析来说,这是一个非常有用的策略。在本文中,我将使用 expert . ai NL API——一个返回一套 NLP 分析的 API——来探索用于描述电影的语言如何根据电影制作的年份而变化。

作者图片

在接下来的内容中,我将向你展示如何创建如上图所示的情节。这张线图展示了“音乐”作为电影概要中一个主题的受欢迎程度的变化,它与音乐电影(1930-1950)的所谓黄金时代整齐地对齐,伴随着由 【西区故事】 (1961)预示的百老汇改编的短暂复兴,以及由 红磨坊开创的这种类型的当代复兴! (2001)和 芝加哥 (2002)。

我用来生成该图的数据和代码以及下面的数据和代码可在本 Jupyter 笔记本中获得。请随意跟随。

1.设置

1.1.数据集

我们以前都读过电影情节:或多或少详细的电影回顾,作为评论、杂志特写或维基百科文章的一部分写下来。为了开始用 NLP 探索电影趋势,我使用了一个 Kaggle 数据集,它包含了来自维基百科的大约 35,000 个电影情节。这些概要被安排在一个. csv 文件中,该文件还列出了上映年份、原产国以及每部电影的类型——从一分钟长的无声讽刺片 堪萨斯沙龙粉碎者 (1901 年)到费尔赞·奥兹皮特克的土耳其-意大利游记 红色伊斯坦布尔 (2017 年)。

作者图片

为了保持一致,我决定只考虑标记为“美国”的 17,377 个情节,这是数据集中最大的电影份额。同样,由于这个数据集中的前三十年包含的情节(平均而言)明显少于随后的三十年,我只选择了 1930 年至 2017 年之间制作的电影。

1.2.该工具

由于我打算一次检查几个特性(包括,例如,主要词条和主题的列表,在每个电影情节中检测到的主要概念和感觉的分解,以及情感分析分数),我已经决定为这个项目使用 expert.ai NL API 。这套文本处理工具可以从 Jupyter 笔记本中调用,以便接收一组分析,然后可以使用当前的 Python 库对其进行操作和可视化。

1.3.预处理数据集

我们可以从按发行年份分组开始。在此步骤中,我们还应该对文本本身进行快速清理,删除圆括号和方括号之间的所有字符串(通常用于脚注引用或与图形本身无关的信息)。

2.分析

2.1.调用 API

要对您的数据调用 expert.ai NL API,您需要在 expert.ai 开发者网站上注册(该工具免费使用,注册过程只需几秒钟)。我建议将您的凭证存储在一个. env 文件中,并使用 Python-dotenv 将它们加载到您的环境中。一旦你这样做了,处理文本和检索你需要的分析只需要几行代码。

例如,下面两个函数中的第一个将允许您对一个文本块执行完整的分析(包括语义和形态学解析、主要标记和实体的提取、情感分析等等)。第二个函数将根据 API 提供的四个分类法中的一个来执行分类分析(“T8”IPTC 媒体主题“T9”在这里是默认的,但是在这个实验中我们还将执行“情感特征分析)。

处理单个文本块来检索这些统计信息需要的时间相对较少;然而,对整个数据集这样做将非常耗时。为了加速这个过程,我创建了一组函数来处理数据集中的随机文本样本。对于我们感兴趣的范围内的每一年,这些函数随机选择 100 个电影情节,然后从每个情节中随机提取 500 个字符的样本。通过使用这种方法,我已经处理了来自 8,800 个电影情节的样本——约占该分析可用样本总数的 53%。

这个函数将每个样本发送给 API 三次:一次用于完整分析,一次用于 IPTC 主题分类,一次用于情绪特征分类。API 每年返回的 300 个 JSON 对象存储在三个单独的列表中,然后收集在一个专用的字典中。

2.2.提取数据

我们主要对 expert.ai NL API 考虑的几个特征中的七个感兴趣:主要引理、主要概念(或同步子)、主要主题、情感分析得分、IPTC 媒体主题分类和情感特征分类。一组简单的函数可以帮助我们从 API 返回的 JSON 对象中提取所有这些特性。例如,下面两个函数提取情感得分和与每个绘图样本相关的主要词条列表。

2.3.绘制数据

有了按年收集和汇总的所有数据,我们现在可以继续将这些值绘制到图表上,以发现电影情节可能如何随着时间的推移而变化。

人们当然应该记住,这些价值并不是指电影本身,也不是指它们发行或制作的实际时刻。我们看到的结果来自于关于这些电影的文字,但是是在不同的时间,由不同的人写的;所以,他们应该半信半疑。然而,尽管是间接的,他们可能会让我们了解他们描述的电影的整体情绪、主题和主题可能发生了怎样的变化。

我创建了三个函数来可视化 expert.ai NL API 返回的不同类型的特征:特定词条、主题或概念的得分;唯一词条、主题或概念的数量;和情感分析分数。为了增加结果图的可读性,并最小化任何潜在异常值的影响,我使用了 SciPy 的 UnivariateSpline 函数来归一化和平滑这些值,在结果图中绘制原始值和归一化值。

3 结果

3.1 从马到航天飞机

这些函数生成的一些图显示了可以很容易解释的趋势。

作者图片

例如,在上面的图表中,我们看到“马”的 syncon 在数据集中变得越来越不频繁,这与几十年来西部片的成功下降以及汽车的优势增加相呼应。

其他情节突出了特定电影和特许经营的成功可能决定了某些主题和背景的出现或复兴。

作者图片

在上图中,我们可以看到高中一直是一个相对不受欢迎的话题,直到 20 世纪 80 年代初,波奇的《T2》和《T3》(1981)开创了猥亵青少年喜剧的类型。“高中”电影似乎在 20 世纪 90 年代末(继 1999 年的 【美团】 )和 2000 年代末至 2010 年代初(继 2006 年的 高中音乐剧 )再次达到顶峰。

其他趋势可能受到历史事件的影响。

作者图片

例如,在上面的图表中,我们可以看到在 1969 年第一次登月之前的几年里,以及 20 世纪 80 年代后期——1986 年挑战者号灾难之后,“宇宙航行”的话题是如何流行起来的。

3.2 未婚情节

其他图表就不那么容易解读了。

作者图片

例如,在上面的图表中,我们看到“婚姻”这个话题在电影情节中逐渐变得不那么突出,特别是在 20 世纪 30 年代到 80 年代之间。在 20 世纪 90 年代,这个话题似乎有了短暂的复苏——也许是由以婚姻为中心的喜剧 《当哈利遇见莎莉……】(1989)的成功引领的。

作者图片

更令人惊讶的是,上面的图表记录了“爱”从我们的屏幕上消失的轨迹。API 在许多电影情节中发现,这种情感特征似乎在几十年间变得不那么普遍了——尤其是在 20 世纪 60 年代之后。如果有的话,是什么让“爱情”和“婚姻”对电影制作人不那么有吸引力?

3.3 美国电影已经变得无聊了吗?

我们还可以考虑每年记录的唯一令牌的数量。这一指标可能会让我们了解电影主题的多样性是随着时间的推移而增加还是减少。电影年复一年处理的话题和概念是多了还是少了?

作者图片

你在上面看到的图表显示了一个清晰的模式。数据集中独特同步的数量稳步增长:20 世纪 50 年代制作的电影与 20 世纪 30 年代制作的电影相比,与更多概念相关联。这种趋势一直持续到 20 世纪 70 年代中期,然后在 20 世纪 80 年代和 21 世纪 10 年代之间趋于平稳,甚至略有下降。如果这些短文以任何方式代表了它们所讲述的电影,我们可以说,在 1930 年至 1975 年之间,被搬上银幕的主题、人物和环境的多样性稳步增加,但这种增加已经放缓。在过去的几十年里,美国制作的电影是否变得不那么多样化或冒险了?

3.4 感伤的过山车

我想分享的最后一个结果可能是最令人费解的。

作者图片

上面的图表显示了 API 处理的每年收集的样本的平均情绪分析分数。这个分数反映了一篇文章所谓的消极或积极——消极的评价与较低的分数相关,而积极的陈述与较高的分数相关。该数据集中的值变化很大,平滑线的形状可能是归一化过程的产物。

然而,有两个趋势非常突出。首先,中位数分数似乎几乎周期性地下降和上升,每二十年下降一次——在 20 世纪 30 年代中期和 50 年代中期,然后在 70 年代初和 90 年代初。第二,总体模式似乎是一条曲线——2010 年的电影回到了 20 世纪 30 年代电影的积极向上。在世纪之交,电影普遍变得更轻松了吗?我们会迎来新一波的阴郁电影吗?

电影史上的下一件大事是什么?超级英雄大片的日子算吗?我们是否应该期待一些其他旧类型的电影重新出现——也许是对霸气电影的新一轮,或者是期待已久的全球经典怪物的回归?这些情节中的一些给了我们一个电影可能走向的想法——并提醒我们,无论如何,“这将是一个颠簸的夜晚”

[1]:贾斯汀·罗比雄,维基百科电影剧情 (2019),第一版,卡格尔
数据集通过知识共享署名-共享 4.0 国际许可获得许可。

F-stings 颂歌

原文:https://towardsdatascience.com/an-ode-to-f-stings-dcb0fb4fc67a

格式化字符串的显而易见的(也是最好的)方法

应该有一种——最好只有一种——显而易见的方法来做这件事。

Python 的禅

照片由Tj·霍洛韦丘克Unsplash 上拍摄

所有字符串格式化方法概述

当我第一次学习编码时,我犯了许多错误。像任何新手一样,我专注于输出,而不是担心最佳实践。因此,我的指纹中不乏类似这样的代码:

如果我需要格式化某些变量,它看起来像这样:

以这种方式连接字符串是我打印语句、绘图标题甚至文件路径的常用方法。这很有效,为什么要质疑呢?我有太多破碎的脚本要修复,知识空白要填补。我没有花时间去考虑是否有更好的办法。

通过教程和堆栈溢出问题,我了解了%-formattingstr.format()方法。但是我发现这些方法很难读懂。它们需要来回移动。我想要像我的连接方法一样的东西,可以从左到右阅读的东西。

最终,我偶然发现了格式化字符串的最好也是最明显的方法:f-strings。Python 3.6 中引入的 f 字符串(又名格式化字符串)允许嵌入表达式。所有需要做的就是在字符串前加上一个fF,并用括号将表达式分组。f 字符串可以用单引号或双引号初始化。这里有一个简单的例子:

爱上 F 弦的 5 个理由

1.可读性📚

可读性很重要。Python 的禅

在所有的字符串格式化方法中,f-strings 的可读性最强,因为它使用了最少的语法和嵌入式表达式。例如,考虑这些例子:

所有的打印语句产生相同的输出。然而,f 弦将三个变量嵌入到弦中,因此眼睛不需要来回扫视。你可以像读日常散文一样从左到右读 f 弦。

2.速度⚡️

f 字符串是许多字符串格式化方法中最快的。Jupyter 笔记本上的一个简单测试证实了这一点:

图片作者。

这是它的图像:

图片作者。

3.嵌入表达式和函数💯

除了变量,还可以嵌入数学表达式。它们被视为常规 Python 表达式。例如:

Lambda 函数也适用于 f 字符串。下面是上面重写为 lambda 函数的示例:

4.格式精度和 Type✂️

f 字符串使得格式化嵌入的表达式变得容易。只需在表达式后添加‘:’和一个格式说明符。格式规范是一种确定如何表示表达式的小型语言。除了其他属性之外,它还控制表达式的精度和类型。

要控制表达式的精度,请在格式说明符中的‘.’后添加所需的位数。例如:

精度不需要存储在变量中,然后用大括号嵌入。上面的例子是为了清楚起见。

格式规范还通过添加类型代码定义了表达式类型。例如,‘e’是科学记数法的类型代码。默认表达式类型是字符串或‘s’

对于定点符号(‘f’‘F’类型),精度值控制小数点后的位数。对于通用格式(‘g’‘G’类型),精度值决定小数点前后显示的位数。

以下是演示不同表达式类型的一些示例:

百分比

科学符号

不带尾随零

带尾随零

5.间距和对齐🗂

漂亮总比难看好。Python 的禅

格式规范还定义了 f 字符串中的间距和对齐方式。这在输出列表或格式化模板时非常有用。首先,通过使用对齐选项之一来定义对齐。‘<’左对齐,‘>’右对齐,‘=’居中对齐。对齐选项位于精度和类型参数之前。

接下来,使用整数指定嵌入表达式的宽度。宽度定义了表达式的字符长度。如果宽度比表达式本身长,那么表达式将用空格填充。如果宽度较短,则没有填充。如果没有定义宽度,则表达式确定字符串的长度。没有宽度参数,就没有理由定义对齐。

输出。图片作者。

阅读文档了解更多使用格式规范的方法这里

结论

感谢您阅读我的文章。如果您喜欢我的内容,请考虑关注我。此外,欢迎所有反馈。我总是渴望学习新的或更好的做事方法。请随时留下您的评论或联系我 katyhagerty19@gmail.com。

https://medium.com/@katyhagerty19/membership

众所周知,这是一个改变数据验证的开源工具

原文:https://towardsdatascience.com/an-open-source-tool-to-change-data-validation-as-you-know-it-7d6fcb5e9301

data-diff 如何节省您验证数据模型的时间

莱娜·科勒在 Unsplash 上的照片

如果您曾经不得不将您的数据仓库迁移到一个新的平台或帐户,那么您就会知道这是多么耗时和痛苦。最近,我不得不将我的数据从一个雪花帐户迁移到另一个帐户,重新配置所有的数据接收管道和编排。您很快就会意识到一个小小的变化可能(并且将会)破坏您的数据管道中的一切。

随着整个管道的迁移,需要检查迁移是否正确。

  • 您如何知道所有的源数据都作为原始数据被接收到您的数据仓库中呢?
  • 您如何知道您的数据模型配置正确?
  • 你的表格中的数据看起来和其他仓库中的一样吗?

问题层出不穷。回答这些问题的唯一方法是通过数据验证。

如果你曾经验证过任何东西,那么你应该很清楚要花多少时间。这是乏味的,不用动脑筋的,而且相当烦人。作为分析工程师,验证是我们最不想做的事情。我们希望把时间花在解决棘手的问题、构建有洞察力的数据模型以及优化我们的数据管道上。没有验证一切看起来像预期的那样。

验证数据通常看起来像编写一堆计算行数和某些值的基本查询。有时它甚至意味着用相同的主键比较特定的行。我不知道你怎么样,但我只能坚持几个小时,然后我的眼睛就会疲劳,数字也会变得模糊。那么,如果有更好的方法呢?一个既能节省您宝贵时间,又能完成您需要的验证的解决方案?终于有了!而且,完全免费。

Datafold 的开源 data-diff 是什么?

作为比较数据模型中代码变化的 Datafold 的超级粉丝,我非常兴奋地了解到这个团队正在创建的开源工具。data-diff 是一个开源命令行工具和 Python 库,用于比较两个不同数据库中的行。它允许您在同一个数据仓库平台内或跨两个不同平台比较数据库,使其成为用于任何类型迁移的理想工具。

以下是您可以比较的几个数据库:

  • 一种数据库系统
  • 关系型数据库
  • 雪花
  • 神谕
  • BigQuery
  • 红移
  • 数据砖

如果你想看完整的名单,你可以在这里查看

这个工具的工作原理是将表分成更小的块,然后对正在比较的两个数据库中的每个块进行校验和检查。它在表级别工作,允许您比较相同或不同数据库中的两个表。校验和检查会继续,直到工具找到不同的行。然后,它将输出两个数据库之间不同的行,节省您手动查找它们的时间。

该工具不仅可以用于在数据迁移过程中比较数据库,还可以用于几乎所有的数据验证场景。在使用 Airbyte 和 Fivetran 等工具时,您可以使用它来比较从关系数据库和数据仓库(如 Snowflake)获取的数据。当数据丢失时,您可以使用它来创建数据质量检查。您甚至可以使用它来帮助跟踪使用硬删除删除的数据行。

如何使用数据差异

Datafold 有一个 Github repo ,里面有如何使用这个命令行工具的所有说明。我将向您介绍我做了什么,在这个过程中我遇到了一些问题,以及我是如何解决这些问题的。请记住,我是在比较两个不同雪花账户的表格。幸运的是,我在每个帐户中都设置了相同的用户、角色和仓库,这使得配置变得更加容易。

创造 URI

最大的障碍是编写在命令行工具中使用的连接字符串。这些连接字符串包含关键信息,如数据库用户、密码、帐户、数据库、模式、仓库和角色。在 Snowflake 以外的数据库中,您只需要您的用户名、密码和主机名。

雪花 URI 看起来像这样:

snowflake://user:password@account/database/SCHEMA?warehouse=WAREHOUSE&role=role

您将运行的命令是:

data-diff DB1_URI TABLE1_NAME DB2_URI TABLE2_NAME [OPTIONS]

如您所见,这非常简单。您只需要安装 data-diff 包以及适用于您的数据库类型的相应驱动程序。

pip install ‘data-diff[snowflake]’

完成后,您就可以为每个数据库编写 URI 了。

以下是我用在自己身上的:

用户名和密码

我使用我的 dbt 用户和密码进行测试,但是我强烈建议只为 data-diff 包或任何类型的开源工具创建一个用户。

账户名称

当你点击雪花账户右上角的用户名时,账户应该是下拉列表中粗体显示的 id 或昵称。确保包含您所在的地区和提供商。我没有包括这一点,我调试 URI 的时间太长了。因此,如果您的帐户是 madisonprod.central-us.azure,您可以使用madisonprod . central-us . azure作为您的帐户。

数据库和模式

这只是您要比较的表所在的数据库和模式的名称。请记住,这些可能会因帐户或数据库而异。在我的例子中,我的两个表都位于数据库 base 和模式 WEB_DBO 中。

仓库和角色

最后,您需要指定希望 Datafold 使用的仓库和角色。请记住,必须将角色分配给用于连接的用户。我还建议选择该用户和角色的默认仓库。在我的例子中,我使用的是 TRANSFORMER_WH 和 TRANSFORMER 角色,我也用它来运行我的 dbt 模型。

当我把这些放在一起时,我的 URI 看起来会像这样,但有两个不同的帐户:

snowflake://dbt_user:password@madisonprod-central-us.azure/BASE/MADISON_DBO?warehouse=TRANSFORMER_WH&role=TRANSFORMER

策划整个命令

一旦你创建了你的 URI,你就快完成了!您只需要指定想要比较的两个表的名称。

如前所述,data-diff 使用以下命令格式:

data-diff DB1_URI TABLE1_NAME DB2_URI TABLE2_NAME

用你上面写的 URIs 代替 URI DB1 和 URI DB2。在我的例子中,我想比较来自两个不同雪花帐户的订单表。我将这样写命令:

data-diff “snowflake://dbt_user:password@madisonprod-central-us.azure/BASE/MADISON_DBO?warehouse=TRANSFORMER_WH&role=TRANSFORMER” ORDERS “snowflake://dbt_user:password@madisondev-central-us.azure/BASE/MADISON_DBO?warehouse=TRANSFORMER_WH&role=TRANSFORMER” ORDERS

需要注意两件事:

  • 请务必大写您的表名
  • 包括你的两个 URIs 的引用

当我没有做这些事情时,我得到了一个帐户错误,然后是一个表错误。一定要注意小细节!

指定主键

如果您正在比较的表的主键与id的名称不同,那么您需要指定一个“键列”。这将告诉 data-diff 哪个列充当表的主键,并允许它准确地比较两个表。我的表的主键是order_id,所以我这样指定:

data-diff “snowflake://dbt_user:password@madisonprod-central-us.azure/BASE/MADISON_DBO?warehouse=TRANSFORMER_WH&role=TRANSFORMER” ORDERS “snowflake://dbt_user:password@madisondev-central-us.azure/BASE/MADISON_DBO?warehouse=TRANSFORMER_WH&role=TRANSFORMER” ORDERS — key-column ORDER_ID

其他选项

还有一些类似于key-column标志的选项,您可以添加到 data-diff 命令中以优化它。下面是一些我觉得有用的例子:

—列(允许您指定要比较的特定列,从而加快查询速度)

—交互式(向您展示幕后发生的每一步)

—二等分因子(增加数据集分割成的线段数)

尝试这些不同的选项,看看什么最适合你!根据您要比较的内容和数据表的大小,您可能需要使用更多的选项。

运行该命令后,data-diff 将打印出两个不同数据库中的主键。它应该是这样的:

这里 data-diff 打印存在于一个表中但不存在于另一个表中的主键。当键前面有一个+时,这表示这些行存在于第二个表中,而不存在于第一个表中。如果有-符号,则表示该行存在于第一个表中,而不存在于第二个表中。这里,正在打印的订单 id 出现在我的 URI 的第二个表中,而不是第一个表中。

调试您的 URIs

如果您收到与您的帐户相关的错误,建议您使用诗歌运行 data-diff。这将允许您打印您正在向 data-diff 提供的凭据,确保凭据输入正确。

为了做到这一点,您首先要克隆数据差异报告。

接下来,您可以安装诗歌及其依赖项:

pip install poetry

poetry install

在您克隆了 repo 并安装了 poem 之后,导航到 snowflake.py 文件并打印您的凭证。您的 init 函数应该如下所示:

def __init__(self,account: str,_port: int,user: str,password: str,*,warehouse: str,schema: str,database: str,role: str = None,**kw,):snowflake = import_snowflake()logging.getLogger(“snowflake.connector”).setLevel(logging.WARNING)print(account)print(user)print(password)print(warehouse)print(schema)print(database)print(role)logging.getLogger(“snowflake.connector.network”).disabled = Trueassert ‘“‘ not in schema, “Schema name should not contain quotes!”self._conn = snowflake.connector.connect(user=user,password=password,account=account,role=role,database=database,warehouse=warehouse,schema=f’”{schema}”’,**kw,)self.default_schema = schema

现在,您可以像前面一样运行相同的 data-diff 命令,但要以poetry run python3 -m开始。这将打印出 data-diff 在产生错误之前从您的 URI 注册的值。

让我们将其与典型的验证过程进行比较。

以前,当比较两个数据集时,您需要做这样的事情:

  • 求 2021 年订单数:

select count( distinct order_id) from orders where year(created_at)=2021;

  • 求 2021 年 5 月订单数:

select count( distinct order_id) from orders where year(created_at)=2021 and month(created_at)=5;

将订单差异缩小到 5 月份后,您可能需要下载数据集并在 Excel 中比较这两个数据集(除非您碰巧在同一个雪花帐户中有这两个数据集)。Excel 加载时间很长,如果导入大量数据,速度会明显变慢。在这里,您可以使用函数来比较哪些 order _ ids 在一个数据集中不存在,而在另一个数据集中不存在。

我这样做了太多次,这是一种痛苦。分析工程师有更好的事情要做!我们不想花时间去大海捞针。data-diff 消除了寻找数据集之间差异的整个手动过程。现在,您有了额外的时间来研究为什么订单 id 在一个表中,而不在另一个表中。这是我们喜欢做的调查工作,实际上有的影响

结论

data-diff 是一个强大的验证数据的开源解决方案。它消除了一部分可能需要几个小时的手动验证。无论您是像我一样在帐户迁移后使用它来比较帐户间的表,还是使用它来验证您的编排管道,它都将为您腾出时间来完成真正重要的任务。data-diff 使我能够成功地验证我的帐户迁移,所用的时间只是比较每个模式中每个表所需时间的一小部分。

开源工具是现代数据栈的未来。但是,如果你想使用复杂的用户界面比较数据模型,一定要看看 datafold 的付费工具

更多关于分析工程和现代数据堆栈的信息,订阅我的免费每周时事通讯。

数据产品的运营模式

原文:https://towardsdatascience.com/an-operating-model-for-data-products-fba6b268f698

您的运营模式是数据网格之旅的速度和成功的主要指标。下面是在您的企业数据网格中为数据产品设置一个操作模型所需了解的内容。

János Venczák 在 Unsplash 上拍摄的照片

迈向有效的数据产品运营模式

实现数据网格所需的组织变化比技术更难解决。简单明了地说,改变一个人的行为比引进新技术更难,更不用说改变一个团体或一个企业了。

在本文中,我将介绍数据产品团队运营模型的关键要素,然后将其扩展为数据产品生态系统的运营模型,也称为数据网格。沿着这些思路,我将提出几个有助于您建立数据产品和数据网格运营模型的考虑因素,并希望使您组织的数据网格之旅更容易、更快:

  • 数据产品微观世界(一小组数据产品)中的团队类型和关键交互,
  • 数据产品的操作模型,
  • 更广泛的数据产品生态系统的运营模式(即数据网格),以及,
  • 管理企业内数据网格可能发展的运营模式选择的含义。

我以一些关于康威定律对大型企业中数据网格发展的影响的观察来结束这篇文章:

  • 数据网格将从本地实现开始,然后发展到区域实现,可能在同一时区内(2+/-)。
  • 在可预见的未来,区域性数据网格实现将会盛行。
  • 本地数据产品所有者将定义由通用区域技术平台支持的他们自己的技术足迹
  • 企业数据治理任务将变得“轻量级”,大部分数据网格治理将根据本地和区域问题进行优先排序

数据产品团队的拓扑结构

在之前的文章中,我总结了有远见的作者 Skelton、PaisDehghani 所提倡的独特的数据产品团队结构。总而言之,有几个不同的团队:

  • 与流程一致的团队对软件产品或服务的交付负有端到端的责任。
  • 平台团队通过提供有用的工具、实用程序和技术服务,使与流程一致的团队更容易完成他们的工作。
  • 赋能团队充当“顾问”,帮助流程一致的团队克服障碍。
  • 复杂的子系统团队拥有帮助流程一致的团队消费或集成复杂的企业系统所需的深厚专业知识。

图 1,数据产品团队拓扑

在大多数现代企业中,数据产品团队是与流程一致的团队。这个团队有责任、权力和技能来实现数据产品。团队通过建立清晰的数据边界来识别由数据产品管理的数据。数据产品团队由授权所有者运营,拥有本地自主权、资金和决策权来创建和运营数据产品。此外,该团队还与生产者和消费者合作,为数据产品建立合同和 SLA,并确保数据产品提供价值。

每个数据产品团队通常由一个或多个“平台团队”支持,其目标是使数据产品团队更容易使用组织内的通用技术能力。平台团队的例子包括:

  • 云平台团队,该团队提供符合企业要求的云平台即服务(PaaS ),使得以企业规定的安全、可操作和可观察的方式消费云能力变得容易;云的复杂性,尤其是当企业考虑“多云”环境(以及有效执行该功能所需的各种技能和人员)时,使得容纳在单个数据产品团队中是不切实际的。
  • Interfaces Platform Team ,提供标准模板框架和运行时环境,使数据产品能够轻松构建支持数据产品摄取、消费、发现、可观察性和可操作性需求所需的互操作接口。大型企业通常有团队来标准化和简化(并使 X 即服务)API、事件流、数据管道和联合查询技术。
  • 网络和安全平台团队,这使得与企业网络上的其他系统(和人员)的交互变得更加容易,同时还提供服务使数据产品中的数据更容易保护(静态和动态)。

每个数据产品通常还由一个或多个“支持团队”支持,这些团队提供咨询和建议来帮助数据产品团队。我在企业中见过几种类型的赋能团队:

  • 指导小组,根据需要提供行政级别的监督和资金批准;我发现这个团队在影响高级管理人员、解决组织惰性和为数据网格建立势头方面非常有价值。
  • 主题专家,他们在数据产品团队可能需要的特定主题方面具有丰富的经验;在许多情况下,这不是一个正式的团队,而是基于数据产品团队成员可能拥有的松散的同事网络。
  • 企业数据治理团队,为数据产品团队提供所需的指导,以保持符合企业政策和要求。
  • 培训团队,帮助教育和建立企业内部对数据网格和数据产品的认识;最初,我认为培训是由第一批数据产品团队提供的,但随着数据产品数量的增长和培训需求的增长,最终这个团队会发展成一个独立的团队。

虽然“复杂子系统团队”不像其他团队那样常见,但他们在数据产品生态系统内提供专业技能方面也发挥着非常重要的作用。以下是大型企业中常见的这种团队的几个例子:

  • 主数据管理团队,他们拥有所需的深厚技能,例如,执行所需的数据匹配,以确保重要数据资产的准确性、统一性和语义一致性。
  • 大型机团队,比如那些管理 IMS 或 CICS 大型机数据库技术的团队。

数据产品团队运营模式

维基百科运营模型定义为“一个组织如何运行自身的可视化表示(模型)。”运营模型中有许多组件,但对于我们的目的来说,运营模型描述的是“谁在组织中做什么”。

现在,让我们详细阐述我们之前的数据产品团队结构,以确定典型企业中普遍存在的核心交互模式(图 2)。

图 2,数据产品团队运营模式

在我们的示例企业(图 2)中,我们有几种类型的团队,包括流对齐的、平台的、使能的和复杂的子系统团队,并且每个团队分别使用连续的正式的长期的、“X 即服务”、协作的短期的和依赖于子系统的交互模型。

每一个深蓝色的点代表一个主要的责任,每一个都是数据产品团队的责任,这并不奇怪。每个浅灰点代表数据产品团队和另一个团队之间的实质性交互。

如您所见,有几个交互集群:

  • 核心数据管理,显示数据产品团队和其他团队(源系统团队、分析团队或其他数据产品团队)之间需要的广泛互动,以安全、可靠地允许数据产品团队接收和消费数据。
  • 技术服务,构成支持核心数据管理能力的基础服务。
  • 深厚的专业知识,由主题团队和我们复杂的子系统团队提供,如主数据管理和大型机 IMS 团队,两者在大型企业中都很常见。
  • 监督,我们的指导小组提供指导、战略建议和资金援助,企业数据治理团队提供隐私、监管和内部企业要求。

数据网格操作模型

但是,建立高性能运营模型的关键是了解重要的交互发生在哪里,并使用这些信息来优化和简化这些交互。那么,我们该怎么做呢?

要理解的第一点有点反直觉。

虽然数据流以及数据网格中数据产品之间的交互决定了数据网格的 技术前景 ,但行为动态和决策层级决定了数据网格的实际 组织结构

这意味着数据网格操作模型不是数据产品的随意组合。更确切地说,数据网格运营模式——企业网格中的团队结构和数据产品——将围绕企业内的组织动态而结合起来

康威定律对此做了最好的阐述,解释为“你的系统和数据将遵循你的组织结构”。康威定律是“基于这样的推理,为了使产品发挥作用,其组成部分的作者和设计者必须相互沟通,以确保组件之间的兼容性。”

换句话说,系统——在我们的情况下是数据产品——将在定义的组织边界内构建,在该边界内,它促进最简单的通信、最清晰的责任、最大的资金效率和最快速的决策。

简而言之,每个数据产品团队,以及它的支持、平台和复杂的子系统团队合作伙伴,将自然地形成——很大程度上靠自己——一个有些自给自足的微观世界(一个小的生态系统),由围绕共同业务目标的通信模式和交互联系在一起。换句话说,数据产品集群将在一个企业内的单个群体的边界内形成。

这些不同的数据产品团队微观世界之间的交互形成了一个更大的业务单元或区域组的生态系统,它们被更高级别的组织单元(地理区域或业务线)所规定的通信模式和交互所约束。

数据网格——松散耦合的区域生态系统

简而言之,这些相互依赖的生态系统被实际的组织动态所束缚。其中最常见的是将大型企业按国家和区域分组,以适应每个市场不同的客户偏好或监管限制。

图 3,数据网格——松散耦合的区域生态系统

例如,许多大型组织都有北美、欧洲和亚洲小组,这些小组反映了每个地区独特的法律和市场需求。甚至在一个国家内的较小组织中,通常也有一些集团或业务线拥有本地(省、州等)业务单位。因此,总而言之,在可预见的未来,数据网格很可能由松散耦合的区域生态系统组成。但这有着深远的影响。

含义# 1——在可预见的未来,区域性数据网格实施将会盛行。

仅康威定律就强烈表明,纯粹的企业数据网格可能是不切实际的。我认为大多数交互(人员和数据流)都发生在“紧密”的实体之间(从组织的角度来看。接近度。我认为,实际的决策和资金通常局限于业务线或地理单元(毕竟,这就是为什么组织有一个层次结构),这种动态自然会创建数据产品集群(又名。数据网格)。

此外,以我的经验,我没有见过多少真正的企业计划。事实上,除了重大危机情况(疫情、收购等。)很少有情况具有全球利益、结果或必要性,足以保证更广泛的全球协调。即便如此,当我经历过这些事情时,事情通常都是从小事开始发展的。我怀疑,即使在这些情况下,第一个数据网格也可能是一组区域性的交互数据产品。

事实上,我怀疑任何试图促进跨区域决策的人(例如,有一个跨越北美和欧洲业务单位的数据网格)都知道决策是困难和艰巨的,有时几乎是不可能的。

但是其他情况也支持这个结论。监管环境,尤其是与安全和隐私相关的监管环境,因地区而异,对地区间的数据共享造成了限制。

不同的业务优先级也是如此。快速增长的地区可能有必要快速引入新产品,而成熟地区可能专注于保护现有的市场份额,每种产品都推动不同的投资和决策,这可能不利于推动不同地区的数据产品之间的一致性。

技术成熟度和变化速度也很重要。一些地区可能背负着难以发展的旧系统,从而难以建立新的数据产品,而其他地区拥有更新的技术,可以轻松快速地创建数据产品。

相反,数据产品微观世界和区域数据网格生态系统将成为企业内部数据网格增长的主要机制。

含义# 2——数据网格将从本地实施开始,然后发展到区域实施,可能在共同时区内(2+/-)。在可预见的未来,真正的企业级数据网格实现可能是一个令人向往的目标。

一个简单的事实是,较小的团队(5-10 人,最多 15 人)合作得更好,行动更快,并且更有效地建立创建软件所需的信任。显而易见的是,企业将利用这种由少量敏捷的数据产品团队组成的动态的、更小的数据网格实现。并且为了简化通信,很有可能第一个数据网格实现将被局限于相似/接近的时区。

现在,可以有把握地说,远程工作文化和工具可能为建立跨时区的可靠沟通提供机会。但即使这样也有实际的局限性。除非人们真正准备好鼓励全天工作,否则大多数数据产品将被构建到 2 +/-时区内的区域数据网格集群中,以促进跨公共营业时间的通信。

这意味着企业范围内的同构数据网格实现可能不会成为规范。更有可能的是,在可预见的未来,企业可能会采用由较少相关数据产品组成的独立区域数据网格实现。

影响# 3——企业数据治理任务将变得“轻量级”,大部分数据网格治理将根据本地和地区问题进行优先排序

如前所述,监管环境因地区而异。考虑隐私:欧盟有 GDPR (一般数据保护条例),而北美的隐私条例更宽松。同样,不同地区、不同国家的医疗保健数据法规也有很大差异,在许多情况下,甚至州与州或省与省之间也是如此。

然而,这确实导致了一个困惑。传统的数据治理不一定会考虑这种地区差异。在由Mohammad Syed(Caruthers and Jackson 的首席策略师)和 Scott Hirleman (播客采访者)在 Data Mesh 广播播客上的陈述也许是最好的,当前的企业数据治理实践“以牺牲地区或本地市场微观需求为代价来解决宏观问题”。

Syed 和 Hirleman 继续强调了非常重要的一点:数据治理,以及与数据质量相关的核心元素,是根据当地市场的需求“情境化”的。因此,有理由期待数据产品治理也将认识到其运营所在的当地市场的背景。

简单来说,没有放之四海而皆准的办法。这也意味着数据产品治理(如果不是更广泛的数据治理)必须包含更高程度的本地化。那么,数据治理如何才能本地化呢?

首先,主要在当地市场运营的数据产品所有者必须能够响应当地的治理需求。他们必须敏捷、灵活,并且有选择技术平台和治理方法的自由。

其次,企业数据治理将会变得更加轻松。除了少数强制性的企业治理规则之外,区域数据产品所有者可能会领导、指导和验证治理。

含义# 4——本地数据产品所有者将定义由通用区域技术平台支持的他们自己的技术足迹

现代“实时”企业是由数据推动的,而数据现在在推动企业发展中起着核心作用。过去,企业 IT 团队在很大程度上针对成本进行了优化。如今,敏捷性和速度是重中之重。

换句话说,地方自治带来的灵活性胜过了中央集权的指挥控制结构。整体架构变得分布式。一刀切的治理变得联合和本地化。

为了应对这些变化,数据产品所有者将定义自己的技术前景。这最大限度地提高了当地的自主权、决策权和对当地市场的反应能力。

因此,技术足迹很有可能由当地或区域数据产品所有者决定。是的,显而易见的企业平台应该在适当的地方加以利用,但是除非绝对必要,否则不应该强制使用。

总结想法

希望在阅读完本文后,您现在能够更全面地了解在数据网格生态系统中实现数据产品所需的组织考虑。现在,希望您已经获得了开始构建数据产品团队和数据网格运营模型的洞察力,这是加速您的数据网格之旅所必需的。


本文假设您对数据网格有很高的理解。如果您需要一些关于数据产品的背景信息,有许多文章可供参考:此处(数据产品)、此处(数据网格模式)、此处(数据网格架构)、此处(数据网格原则)和此处(经验教训)。对于感兴趣的读者,全套数据网格模式可在此处获得。


除非另有说明,本文中的所有图片均由 Eric Broda(本文作者)创作。图像中使用的所有图标都是普通的 PowerPoint 图标和/或不受版权保护。

本文表达的观点仅代表我个人,不一定代表我的客户的观点。

预测绩效指标概述

原文:https://towardsdatascience.com/an-overview-of-forecasting-performance-metrics-ef548dad0134

时间序列预测的一些基本性能指标概述

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

介绍

对于您的机器学习或统计模型,有许多错误和性能指标。更不用说每年都在开发新的了。

一个指标并不比另一个更好,毕竟它们只是一个单一的数字。然而,为您的模型选择最合适的模型非常重要,这样您就可以正确地优化模型。

在这篇文章中,我想回顾一下经典指标和一些特定的预测指标,以及它们的优缺点。这将有助于您更好地理解在工作中何时何地使用某些指标。

经典作品

平均绝对误差

作者用 LaTeX 写的方程式。

平均绝对误差就是预测值 F 和实际值 A. 之差的平均值

优点:

  • 容易理解
  • 误差存在于数据和预测的单位中

缺点:

  • 不惩罚异常值(如果你的模型需要的话)
  • 尺度相关,因此无法与使用不同单位的其他时间序列模型进行比较

均方误差

作者用 LaTeX 写的方程式。

均方误差与 MAE 类似,但这次我们对预测值和实际值之间的差异进行平方。

赞成者:

  • 离群值会受到严重惩罚(如果您的模型需要的话)

缺点:

  • 误差不会出现在时间序列的原始单位中,因此可能更难解释
  • 尺度相关,因此无法与使用不同单位的其他时间序列模型进行比较

均方根误差(RMSE)

作者用 LaTeX 写的方程式。

均方根误差与 MSE 相同,只是最后我们对结果求平方根。

优点:

  • 仍然严重惩罚异常值(如果你的模型需要的话)
  • 误差将以时间序列的原始单位表示
  • 算是 MSE 和 MAE 两个世界中最好的

缺点:

  • 不太好解释,因为你还在纠正错误
  • 尺度相关,因此无法与使用不同单位的其他时间序列模型进行比较

特定预测

平均绝对百分比误差(MAPE)

作者用 LaTeX 写的方程式。

平均绝对百分比误差是实际值和预测值之间的百分比差。这通常是用于测量大多数预测模型的基线指标。

优点:

  • 容易理解
  • 规模独立,因此可以跨不同的时间序列进行比较

缺点:

  • 如果实际值接近或为零,则误差无限
  • 较低的预测必然会有 100%的误差,但较高的预测可能会有无限的误差,因此它会被低估

对称平均绝对百分比误差

作者用 LaTeX 写的方程式。

对称平均绝对百分比误差是 MAPE 的扩展,但考虑到其对负误差的惩罚多于正误差的限制。

优点:

  • 不再倾向于预测不足,因为输出现在完全限制在 0 到 200%之间

缺点:

  • 仍然有无限值的机会,因为分母仍然可以接近或为零
  • 0 到 200%之间的百分比误差很难解释
  • 实际上并不是对称的,因为它没有平等地对待低估和高估的错误(见此处)

平均绝对标度误差

无季节性:

作者用 LaTeX 写的方程式。

带有季节性:

作者用 LaTeX 写的方程式。

这是季节性和非季节性时间序列的平均绝对比例误差,可能是最好也是最公平的使用指标。

如果你想了解更多关于季节性的知识,可以看看我以前的博客:

如果误差小于 1,则该预测优于对长度为的训练数据的平均简单预测。另一方面,如果它大于 1,则预测比平均的简单预测差。上式中 m 指季节性指数。

一个简单的预测是将你的预测设定为最新的实际观察值。对于简单的季节性预测,您可以将其设置为预测季节的前一个观测值。

优点:

  • 比例无关,因为分子和分母有相同的单位
  • 当实际值接近或为零时的可预测行为
  • 预测不足和预测过度同样会受到惩罚

缺点:

  • 可能很难向利益相关者解释

均方对数误差(MSLE)

作者用 LaTeX 写的方程式。

均方对数误差衡量预测值与实际值之间的比率或相对差异。

优点:

  • 比过度预测更多地惩罚预测不足(如果您的模型需要的话)

缺点:

  • 不容易解释
  • 可能会有被等于或接近于零的值除的问题

总结和进一步的思考

在这篇文章中,我们回顾了经典的误差指标:MAE、MSE 和 RMSE,以及一些特定的预测指标:MAPE、SMAPE、MASE 和 MSLE。所有这些指标都有不同的利弊,无论是独立于规模,能够被零除还是惩罚预测不足。要记住的关键是,不要一刀切,你最好使用一系列指标来评估你的预测模型。

参考资料和进一步阅读

和我联系!

(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0

嵌套和非嵌套回归模型的模型选择测试概述

原文:https://towardsdatascience.com/an-overview-of-model-selection-tests-for-nested-and-non-nested-regression-models-5edfa00a12bc

图片由 Tumisu 发自 Pixabay ( Pixabay 牌照)

如何比较所有或部分变量相同的回归模型

通常,回归建模者面临着在两个或多个竞争模型中选择相同的问题。诸如 R 平方 (或 伪 R 平方 )和用于回归分析的 F 检验 之类的拟合优度测量可用于指示单个模型与训练数据集的拟合程度,而 AIC 或 BIC 这两个模型的分数可进行比较,以查看哪一个更好地拟合数据集。

然而,这种测试本身不能用来决定在两个模型 A 和 B 之间,其中一个是否明显优于另一个。

例如,如果模型 A 的调整后 R 平方值为 0.67,而模型 B 报告的调整后 R 平方值为 0.76,那么 0.09 的差异是否大到足以具有统计学意义?或者模型 A 只是不幸地在一个不合适的数据样本上被训练?

模型选择测试就是为了解决这个问题。具体来说,给定两个回归模型 A 和 B,模型选择测试将帮助您决定 A 更好还是 B 更好。正如我们将看到的,从一对候选模型中选择一个合适的模型可能是一个有趣的练习,有时会以两个模型都被接受或拒绝而告终!

型号选择程序概述

模型选择的过程基于 H1 和 H2 两个假设的形成,使得 H1 倾向于模型 A 和模型 b,而 H2 倾向于模型 b。选择测试由测试统计量 X (随机变量)的公式组成,如果两个假设中的一个假设(比如 H1)为真,则该统计量服从某个已知的概率分布。这个分布给了我们对于不同的观察值XH1 为真的概率。一旦计算出这个检验统计量 X 的值,我们就可以使用 X 的概率分布来“读出”H1 为真的概率。然后,我们根据这个概率是大于还是小于某个临界概率 α 来接受或拒绝 H1。

嵌套与非嵌套回归模型

模型选择测试的性质取决于两个模型 A 和 B 是否嵌套。

如果模型 B 包含模型 A 的所有回归变量(以及参数)加上至少一个,那么 A 被认为嵌套在 B 中。模型 A 被称为限制(或限制)模型,而模型 B 被称为非限制(或非限制)模型。下图说明了这种情况:

受限制的模型 A 嵌套在不受限制的模型 B 中(图片由作者提供)

如果模型以上述方式嵌套,诸如用于回归分析的 F 检验似然比检验之类的检验可用于在约束和非约束模型之间进行选择。

f 检验和 LR 检验的好处在于,与调整后的 R 平方相比,它们还可以用于比较嵌套的非线性模型。还有一点需要注意的是(这也适用于调整后的 R 平方和 AIC),候选模型必须具有相同的响应变量 y 。不能使用这些测试中的任何一个来比较两个模型,其中模型 A 将 y 作为响应变量,而模型 B 将logysqrt(y)作为响应变量。

当模型 A 和 B 仅共享一些回归变量或它们不共享任何回归变量时,即当它们不嵌套时,f 检验和 LR 检验不能用于模型选择。我们需要查看一组不同的模型选择测试来比较这种非嵌套模型。

有大量关于非嵌套模型的模型选择测试的研究文献。我们将检验一个由 Davidson 和 MacKinnon (1984)设计的称为 J 检验的检验,并且我们将说明它在现实世界数据集的两个非嵌套回归模型之间进行选择的用途。

型号选择程序的图示

为一个问题选择一个合适的模型的过程最好用一个例子来描述。在本文中,我们将在真实数据集上比较两个固定效应回归模型的性能。我们将使用的真实数据集是一个面板数据集,包含了从 1992 年到 2014 年测量的七个国家人均 GDP 的同比增长百分比。除了 GDP 增长数据,该面板还包含每个国家总资本形成和人均二氧化碳排放量的同比增长数据。数据集可以从这个 下载链接 下载。

a 面板数据集(来源:世界发展指标数据 CC BY 4.0 license )(图片由作者提供)

如果你是固定效果模型的新手,我建议你回顾一下我关于固定效果模型的文章。对于考虑中的数据集,您可以将 FE 模型视为包含虚拟变量(即 1/0 值变量)的线性回归模型,数据集中的 7 个国家/地区各有一个虚拟变量。虚拟模型应该能够捕捉特定国家的“效应”。

我们来看看上面数据集的两个竞争有限元模型。

模型 A 是一个 FE 模型,在该模型中,我们假设每年人均 GDP 的变化受到总资本形成变化的高度影响(“解释”)。

模型 B 也是一个有限元模型。模型 B 认为人均国内生产总值的变化与其说是由资本形成总额的变化来解释,不如说是由二氧化碳排放量的年度变化来解释。

作为固定效应模型的 A 和 B 都同意使用 6 个虚拟变量来表示数据面板中 7 个国家的效应。我们使用少一个变量来避免完美的多重共线性。

模型 A 和 B 的方程可以表述如下:

对于型号 A:

固定效果模型 A 的模型方程(图片由作者提供)

对于型号 B:

固定效果模型 B 的模型方程(图片由作者提供)

在这两个等式中,下标 i 指的是数据集的第I行。特定于国家的变量是虚拟变量,其值为 1 或 0,取决于第行与第行是否与该国家相关。β=【β_ 0,β_1,…β_ 7】是模型 A 中回归系数的向量,而γ=【γ_ 0,γ_1,…γ_ 7】是模型 b 中系数的向量 βγ 在这两种模型中, ϵ_i 是第行和第行的误差项。误差项是 GDP_PCAP_GWTH_PCNT 的观测值与其模拟值之差。**

使用 Python 和 statsmodels ,让我们使用经典的 OLS 技术在同一个数据集上训练 A 和 B。

让我们从导入我们将需要的所有包开始:

**import** pandas **as** pd
**import** statsmodels.formula.api **as** smf
**from** patsy **import** dmatrices
**import** scipy.stats **as** st

让我们在固定效应模型中定义感兴趣的单位:

unit_names = [**'Belgium'**, **'CzechRepublic'**, **'France'**, **'Ireland'**, **'Portugal'**, **'UK'**, **'USA'**]unit_names.sort()

标记数据集中包含单位名称的列:

unit_col_name=**'COUNTRY'**

对于模型 A,定义 yX 变量。 X 尚不包括假人。我们将很快添加它们。

y_var_name = **'GDP_PCAP_GWTH_PCNT'** X_var_names = [**'GCF_GWTH_PCNT'**]

将世界银行公布的发展指标的面板数据载入熊猫数据框架:

df_panel = pd.read_csv(**'wb_data_panel_4ind_7units_1992_2014.csv'**, **header**=0)

打印出前 30 行:

**print**(df_panel.**head**(30))

我们看到以下输出:

世界银行数据集的前 30 行(图片由作者提供)

创建虚拟变量,每个国家一个虚拟变量:

df_dummies = pd.**get_dummies**(df_panel[unit_col_name])

将虚拟数据框与数据集的其余部分连接起来:

df_panel_with_dummies = df_panel.**join**(df_dummies)

让我们看看扩充数据集是什么样子的:

**print**(df_panel_with_dummies.**head**())

世界银行数据集增加了虚拟变量(图片由作者提供)

现在让我们构建模型 A 的回归方程。回想一下,模型 A 使用总资本形成作为回归变量之一。请注意,我们在回归方程中遗漏了一个虚拟变量,以避免 7 个虚拟变量之间的多重共线性。回归模型的截距将包含被忽略的虚拟变量的系数值(在我们的例子中,为美国):

model_a_expr = y_var_name + **' ~ '**i = 0**for** X_var_name **in** X_var_names:
    **if** i > 0:
        model_a_expr = model_a_expr + **' + '** + X_var_name
    **else**:
        model_a_expr = model_a_expr + X_var_name
    i = i + 1**for** dummy_name **in** unit_names[:-1]:
    model_a_expr = model_a_expr + **' + '** + dummy_name

打印出回归方程(用 Patsy 语法):

print(**'Regression expression for model A=\n'** + model_a_expr)

我们看到以下输出(截距在模型创建期间由 statsmodels 静默包含):

**Regression expression for model A=** GDP_PCAP_GWTH_PCNT ~ GCF_GWTH_PCNT + Belgium + CzechRepublic + France + Ireland + Portugal + UK

建立和训练固定效果模型 A:

fe_model_a = smf.**ols**(**formula**=model_a_expr, **data**=df_panel_with_dummies)fe_model_a_results = fe_model_a.**fit**()

打印出培训总结:

print(fe_model_a_results.**summary**())

我们看到以下输出:

固定效果模型 A 的模型训练总结(图片由作者提供)

让我们快速记下模型报告的调整后的 R 平方为 0.639,以及 F 检验 的 p 值为 2.57e-32。p 值几乎为零,这意味着该模型在解释 y 的方差方面比均值模型做得好得多,均值模型基本上是一条穿过 y 均值的平线。这是好消息。

现在,让我们对固定效果模型 B 重复上述所有步骤:

***#Construct the regression equation.*** X_var_names = [**'CO2_PCAP_GWTH_PCNT'**]model_b_expr = y_var_name + **' ~ '** i = 0
**for** X_var_name **in** X_var_names:
    **if** i > 0:
        model_b_expr = model_b_expr + **' + '** + X_var_name
    **else**:
        model_b_expr = model_b_expr + X_var_name
    i = i + 1**for** dummy_name **in** unit_names[:-1]:
    model_b_expr = model_b_expr + **' + '** + dummy_name

print(**'Regression expression for model B=\n'** + model_b_expr)

***#Build and train model B*** fe_model_b = smf.**ols**(**formula**=model_b_expr, **data**=df_panel_with_dummies)fe_model_b_results = fe_model_b.**fit**()***#Print the model training summary*** print(fe_model_b_results.**summary**())

我们看到以下输出:

固定效果模型 B 的模型训练总结(图片由作者提供)

请注意,模型 B 报告的调整后 R 平方为 0.272。同样,f 检验的 p 值(8.5e-10)实际上为零,这意味着模型 B 在解释 y 的方差方面也比均值模型做得更好。

模型 A 和模型 B 的拟合优度比较

模型 B 的调整后 R 平方(0.272)远小于模型 A 的(0.639)。这似乎是模型 A 优于模型 B 的必然结果。其他拟合优度指标似乎也倾向于模型 A 优于模型 B。模型 A 的对数似然性(-292.91)高于模型 B (-349.43)。模型 A 的 AIC 得分(601.8)低于模型 B 的得分(714.9)。对于 AIC 分数,分数越低越好。

在这种情况下,A 和 B 之间看起来没有太大的竞争。但想象一下,模型 B 的 R 平方、Log-LL 和 AIC 分数碰巧与 A 的相似。在这种情况下,如何比较 A 和 B 的拟合度呢?

回想一下,模型 A 和 B 只有一些共同的回归变量,具体来说,它们只有共同的虚拟变量。因此,A 没有嵌套在 B 中,B 也没有嵌套在 A 中。因此,不能使用 f 检验或似然比检验在 A 和 B 之间进行选择。

在这种情况下,我们可以用 J 检验。我们来举例说明它的用法。

J 检验

J 测试基于组合所谓的综合模型的原理,具体如下:

  1. 我们假设模型 A 和 B 预计能够单独解释响应变量 y 中的一些方差。
  2. 模型 a 由方程描述:y=+*ϵ*****
  3. 模型 b 由方程描述:y=+*ϵ*****
  4. 我们创建一个综合模型 c,它使用按比例加权和来组合模型 a 和 b,如下所示:
    y=(1-λ)+ϵ,其中λ从 0 变化到 1
  5. 现在这里的关键是:如果模型 A 能够解释模型 B 能够解释的 y 中的所有差异,加上一些更多的差异,那么模型 B 实际上包含在模型 A 中。我们不需要模型 B。在这种情况下,当你在数据集上训练综合模型 C 时,系数 λ 的拟合值应该计算为零
  6. 另一方面,如果模型 B 恰好能够解释模型 A 能够单独解释的 y 中的所有方差,那么模型 A 就是多余的,拟合系数 λ 应该算出为 1。
  7. 在所有其他情况下,拟合的 λ 既不是零也不是一,这意味着每个模型 A 和 B 都能够单独解释y 中总方差的一部分,而另一个模型无法解释。任何一种模式都不能为了另一种模式而被彻底抛弃。**
  8. 有趣的是,还有第四种情况,在综合模型 C 中交换 A 和 B 之后, λ仍然计算为零。在这种情况下,我们最终拒绝了型号 A 和型号 B!

在最后两个场景中,测试在 A 和 b 之间的选择是不确定的。

综合模型方法有一个严重问题。鉴于 λ 的指定方式,它会乘以模型 A 和 b 的系数。因此,不可能将 λ 与模型系数的相互作用隔离开来,从而使 λ 实际上不可测量。

Davidson 和 MacKinnon 提出的 J 检验解决了这个问题,同时仍然保留了综合模型方法的精神。我们在 J 测试中所做的是用模型 B 的预测值 y 【拟合】值代替模型 B 对综合模型的贡献。这样做为模型 C 提供了以下更新的等式:

******y=+zγ_ capλ+ϵ,其中λ从 0 到 1 不等

我们将综合模型中的 替换为 Zγ_cap 其中 γ_cap 为 B 在数据集上自行拟合后模型 B 的拟合系数向量。

使用 J 测试的过程如下:

  1. 将模型 B 单独拟合到数据集上。这是 y的回归,从中我们得到拟合系数 γ_cap
  2. 通过拟合的模型运行训练数据集 Z 以产生拟合模型的预测y _ cap=Zγ_ cap。******
  3. 现在,构建模型 C 作为模型 A 的扩充版本,其中一个回归变量是 y_cap 。由此,我们对xy _ capy=*+***********
  4. 如果在这个扩充的模型中, y_cap 的系数 λ 被发现为零,那么模型 B 已经无法解释任何超出模型 A 所能解释的y 的方差。因此,模型 A 包含模型 B,我们选择 A 而不是 B。**
  5. 如果增强模型中的 λ 不为零,那么模型 B 有能力解释 y 中超出 A 所能捕捉的一些差异。在这种情况下,我们不能选择 A 而不是 b。我们很快就会看到在这种情况下该怎么做。
  6. 我们也可以通过如下方式构造模型 c 来逆向测试:
    y
    =
    xβ_ cap
    λ++【ϵ** 即我们在【t3t 上倒退 y 从这个模型中得到预测y _ cap=xβ_ cap并将这些预测作为一个变量加入到 yZ 的回归中。 如果 y_cap 的拟合系数为零,我们得出结论,A 无法解释任何超出 B 所能解释的y 的方差。所以我们选择 B 而不是 a。********

在 J 测试中,在两种情况下,拟合的 λ 都可能为零,从而表明 A 和 B 都不适合数据集。

J 测试的图示

让我们对之前构建的两个固定效应模型进行 J 测试。在 J 测试的语言中,我们将 XZ 定义如下:

X 是固定效应模型 A 的回归变量矩阵:

固定效应模型 A 的回归变量矩阵(图片由作者提供)

Z 是固定效应模型 B 的回归变量矩阵:

固定效应模型 B 的回归变量矩阵(图片由作者提供)

yGDP_PCAP_GWTH_PCNT

在前面的部分中,我们已经在数据集上拟合了模型 B。让我们在 Z 得到它的预测。为此,我们需要首先从 Pandas 数据帧中切出 Z 。Patsy 的dmatrices类将使用模型 B 的回归方程帮助我们做到这一点:

**y_train, Z_train = **dmatrices**(model_b_expr, df_panel_with_dummies, **return_type**='dataframe')**

Z_train 上运行拟合的模型 B,以获得预测值:

**y_cap = fe_model_b_results.**predict**(Z_train)**

通过添加来自模型 B 的预测来扩充数据集:

**df_panel_with_dummies[**'**y_cap**'**] = y_cap**

现在构建综合模型(模型 C)的回归方程。回想一下,模型 C 的表达式如下:

y=y _ capλ++ϵ******

其中 X 是模型 a 的回归矩阵, y_cap 是来自模型 b 的预测, ϵ 是回归的误差项。

**X_var_names = [**'y_cap'**, **'GCF_GWTH_PCNT'**]comprehensive_model_expr = y_var_name + **' ~ '** i = 0
**for** X_var_name **in** X_var_names:
    **if** i > 0:
        comprehensive_model_expr = comprehensive_model_expr + **' + '** + X_var_name
    **else**:
        comprehensive_model_expr = comprehensive_model_expr + X_var_name
    i = i + 1**for** dummy_name **in** unit_names[:-1]:
    comprehensive_model_expr = comprehensive_model_expr + **' + '** + dummy_nameprint(**'Regression expression of the comprehensive model=\n'** + comprehensive_model_expr)**

我们看到以下输出:

****Regression expression of the comprehensive model=**
GDP_PCAP_GWTH_PCNT ~ y_cap + GCF_GWTH_PCNT + Belgium + CzechRepublic + France + Ireland + Portugal + UK**

让我们在扩充数据集上构建和训练这个模型:

**comprehensive_model = smf.**ols**(formula=comprehensive_model_expr, data=df_panel_with_dummies)comprehensive_model_results = comprehensive_model.**fit**()print(comprehensive_model_results.**summary**())**

我们看到以下结果:

综合模型训练总结(图片由作者提供)

在训练总结中,我们看到除了 y_cap 和 GCF_GWTH_PCNT 的系数外,所有变量的系数在统计上均不显著(即为零)。

综合模型中代表模型 B 的系数 y_cap 非常显著。这突出了一个重要的发现:模型 B 具有模型 A 所没有的解释力。我们不应该为了 a 模式而完全抛弃 B 模式

这一决定与我们之前通过简单比较两个模型的调整后 R 平方值以及 Log-LL 和 AIC 分数所做的决定背道而驰。

但它回避了一个问题,B 中的哪些变量具有超出模型 A 所能提供的解释力?我们可以这样回答这个问题:

在综合模型中包含y _ cap——B 的代表——导致 A 和 B 共有的每一个变量的系数在拟合的综合模型中变得统计上不重要。因此,B 中唯一可能具有额外解释力的变量是那些与 A 不常见的变量,即 CO2_PCAP_GWTH_PCNT**

为了检验这一假设,我们将在模型 A 中加入 CO2_PCAP_GWTH_PCNT 以构建如下新模型(我们称之为模型 D) :

固定效应模型 D 的回归方程(图片由作者提供)

让我们在训练数据集上拟合模型 D:

*****#Formulate model D by adding CO2_PCAP_GWTH_PCNT to model A*** X_var_names = [**'GCF_GWTH_PCNT'**, **'CO2_PCAP_GWTH_PCNT'**]fe_model_d_expr = y_var_name + **' ~ '**i = 0**for** X_var_name **in** X_var_names:
    **if** i > 0:
        fe_model_d_expr = fe_model_d_expr + **' + '** + X_var_name
    **else**:
        fe_model_d_expr = fe_model_d_expr + X_var_name
    i = i + 1**for** dummy_name **in** unit_names[:-1]:
    fe_model_d_expr = fe_model_d_expr + **' + '** + dummy_name

print(**'Regression expression of model D=\n'** + fe_model_d_expr)

***#Build and train FE model D*** fe_model_d = smf.**ols**(formula=fe_model_d_expr, **data**=df_panel_with_dummies)fe_model_d_results = fe_model_d.**fit**()
**print**(fe_model_d_results.**summary**())**

我们看到以下输出:

****Regression expression of model D=**
GDP_PCAP_GWTH_PCNT ~ GCF_GWTH_PCNT + CO2_PCAP_GWTH_PCNT + Belgium + CzechRepublic + France + Ireland + Portugal + UK**

固定效果模型 D 的训练总结(图片由作者提供)

我们来回忆一下 A 型的训练总结:

固定效果模型 A 的模型训练总结(图片由作者提供)

以下是固定效应模型 A 和 D 的拟合优度的比较:

固定效应模型 A 和 D 的拟合优度的比较(图片由作者提供)

我们看到,模型 A 和 D 之间的三个拟合优度非常相似,尽管 D 似乎比 A 略胜一筹。

我们还注意到,模型 A 嵌套在 D 中,因为模型 D 包含模型 A 的所有变量,再加上一个变量。因此,为了知道模型 D 是否在统计意义上优于 A,我们可以使用 F 检验进行回归分析

使用 f 检验

我们将使用 f 检验来检验假设,即模型 D 比模型 a 在数据集上产生了更好的统计拟合。

具体来说,我们对 f 检验的两个假设如下:

H1:D 型并不比 A 型更合适 H2:D 型比 A 型更合适

如果发现 H1 是正确的,我们将得出结论,将二氧化碳 _PCAP_GWTH_PCNT 添加到模型 A 不会导致更好的拟合,因此我们接受模型 A 并拒绝 d

如果 H1 被证伪,我们将得出结论,将二氧化碳 _PCAP_GWTH_PCNT 添加到模型 A 中会导致拟合优度的显著改善。因此,我们将拒绝 A 而支持 d。

f 检验的检验统计如下:

f 检验的检验统计量(图片由作者提供)

在上面的公式中,

RSS_a =拟合模型 A 的残差平方和(也称为残差误差).
RSS_d =拟合模型 b 的残差平方和.
k_a =模型 A 的参数个数(k_a=8)
k_d =模型 D 的参数个数(k_d=9)
N =训练数据集中的行数(N=161)

让我们计算 F 统计量的值,并将其值与在 α =.05 时的临界 F (1,152)值进行比较:

**k_a = **len**(fe_model_a_results.**params**)
k_d = **len**(fe_model_d_results.**params**)
N = **len**(df_panel_with_dummies)
rss_a = fe_model_a_results.**ssr**
rss_d = fe_model_d_results.**ssr**f_statistic = ((rss_a-rss_d)/(k_d-k_a))/(rss_d/(N-k_d))

print(**'F-statistic='**+str(f_statistic))

alpha=0.05f_critical_value=st.**f**.**ppf**((1.0-alpha), (k_d-k_a), (N-k_d))print(**'F test critical value at alpha of 0.05='**+str(f_critical_value))**

我们看到以下输出:

****F-statistic=**14.12366072014461
**F test critical value at alpha of 0.05=**3.9033664964061314**

由于统计值(14.12)的计算值远大于临界值(3.90),我们拒绝 H1,接受临界α. 05 的 H2,即(1 — .05)100%=95%的置信水平。******

因此,我们确定,对于所考虑的数据集,固定效应模型 D 确实比固定效应模型 A 更适合。

似然比检验

由于模型 A 嵌套在 D 中,我们也可以使用 LR 测试来比较两个模型的拟合优度。让我们使用 LRT 检验,并用它的结果来交叉验证 f 检验的结果。

似然比检验的检验统计量如下:

似然比检验的检验统计量(图片由作者提供)

在上面的公式中:

受限模型是具有较少参数的模型。在我们的例子中,它是模型 a。

无限制模型是带有附加参数的模型,在我们的例子中,模型 d。

对于这两个模型,我们计算最大化的对数似然,从限制模型的对数似然中减去非限制模型的对数似然,并取负 2 倍的差值作为统计量。

这两个竞争假设与 f 检验相同,即:

H1:D 型不比 A 型更合适
H2:D 型比 A 型更合适

在假设 H1 为真的情况下,LR 检验统计量是卡方分布的,其自由度等于两个模型之间的参数数之差。在我们的例子中,这个差值是(k _ d—k _ a)=(9–8)= 1。

让我们计算测试统计量,并将其值与. 05 的 α 处的临界卡方(1)值进行比较:

*******#Get the maximumed log likelihoods of models A and D*** log_ll_a = fe_model_a_results.**llf**
log_ll_d = fe_model_d_results.**llf*****#Calculate the LR test statistic*** lrt_statistic = -2*(log_ll_a - log_ll_d)
print(**'LRT-statistic='**+**str**(lrt_statistic))***#Calculate the critical Chi-squared(1) value at alpha=0.05*** alpha=0.05chi2_critical_value=st.**chi2**.**ppf**((1.0-alpha), (k_d-k_a))print(**'Chi-squared critical value at alpha of 0.05='**+**str**(chi2_critical_value))****

我们看到以下输出:

******LRT-statistic**=14.305161418177136
**Chi-squared critical value at alpha of 0.05**=3.841458820694124****

α =.05 时,测试统计值(14.31)远大于临界卡方(1)值 3.84,这导致我们拒绝 H1,接受 H2,即有限元模型 D 比有限元模型 a 产生更好的拟合优度。LR 测试与 f 检验一致。

摘要

我们检验了两个相互竞争的非嵌套固定效应模型 A 和 B 解释 7 个国家人均 GDP 增长差异的能力。

在 FE 模型 A 中,我们假设人均 GDP 增长很大程度上是由人均总资本形成的增长(以及特定国家的影响)来解释的。在 FE 模型 B 中,我们采用了一个不同的假设,假设人均 GDP 的增长是由人均二氧化碳排放量的增长来解释的(除了特定国家的影响之外)。

我们在面板数据集上训练了两个模型,发现模型 A 似乎产生了明显更好的拟合。

然而,当我们使用 J 检验在模型 A 和 B 之间进行选择时,我们发现模型 B 也能够解释模型 A 所不能解释的回归因变量中的一些方差。因此,不能为了 a 而完全抛弃 B。

对 J 检验结果的分析表明,用模型 b 中的人均二氧化碳排放量变量来扩充模型 A 可能对我们有益。

我们继续这样做,从而构建了一个新的模型 D,我们继续在数据集上训练 D。

D 的拟合优度,通过其调整后的 R 平方最大化对数似然AIC 得分来证明,被发现与 A 非常相似,尽管比 A 稍有优势。这提出了一个问题:D 是否可能偶然受益于一个有益的数据样本,即一个恰好被调整到 D 的优势的数据面板?****

由于 A 嵌套在 D 中,我们能够通过使用用于回归分析的 F 检验似然比检验来客观地比较 A 和 D 的拟合优度,并得出 D 确实是比 A 更好的拟合模型的结论。****

下面是本文中使用的完整源代码:

参考文献、引文和版权

数据集

世界发展指标世界银行数据 CC BY 4.0 license下载链接

纸质和图书链接

佩萨兰博士和韦克斯博士(1999 年)。非嵌套假设检验:概述剑桥经济学工作论文9918 ,剑桥大学经济学院。 下载链接

戴维森和麦金农(1984 年)。基于人工线性回归的模型规格测试。国际经济评论25 (2),485–502。https://doi.org/10.2307/2526210**

形象

本文中的所有图片版权归 CC-BY-NC-SA 所有,除非图片下面提到了不同的来源和版权。

Python 对象可变性概述

原文:https://towardsdatascience.com/an-overview-of-mutability-in-python-objects-8efce55fd08f

一个重要的编程概念,在不是正式的“计算机科学”的课程和教程中经常被忽略

帕卡塔·高Unsplash 上的照片

理解对象可变性是一个重要的概念,几乎肯定会在计算机科学课程或训练营中涉及到。然而,在专注于数据科学、统计学、用户体验、生物工程或任何其他以某种方式使用代码的相关领域的培训中,它经常被忽略。

这是一个问题,因为这是一个影响程序员如何编写和开发代码的重要概念。对可变性如何工作的错误理解或缺乏认识会导致令人困惑的错误,甚至是看似正常工作的代码在以后会崩溃。

在本文中,我将给出可变性的概述以及一些详细的例子。

什么是可变性?

在技术编程术语中,可变对象是一种在定义后其状态可以被修改的对象。与可变对象相反的是不可变对象,它的状态在最初定义后不能改变。

Python 中不可变对象的例子包括整数、浮点、字符串和元组。在用 Python 编程时,您可能最常处理的两种类型的可变对象是列表字典

让我们看一个具体的例子来说明可变性和不可变性之间的区别。假设我定义了一个歌曲列表。如果我想通过替换其中一首歌曲来改变列表,这完全没问题,因为列表是可变的:

>>> my_songs = ["All Too Well", "No Tears Left to Cry", "Cruel Summer"]
>>> my_songs[1] = "Champagne Problems"
>>> my_songs
['All Too Well', 'Champagne Problems', 'Cruel Summer']

相比之下,假设我定义了一个字符串,它是一个不可变的对象,但是我不小心拼错了。如果我试图修改字符串,Python 会报错:

>>> my_song = "All Too Wall"
>>> my_song[9] = 'e'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

一旦定义了字符串,就不能更改其内容。如果我想修改拼写,我需要定义一个新的字符串。

如果你考虑列表和字典的功能,你会意识到你一直在利用它们的可变性,即使你可能没有意识到。像appendextendupdate这样的函数都可以修改它们引用的列表或字典,对于跟踪数据或完成某个编程任务非常有用。

然而,尽管可变对象在某些情况下很有用,但是如果程序员不知道它们是如何工作的,它们通常会导致错误。在下一节中,我将探索一个详细的例子来解释如果可变性被误解会导致什么问题。

为什么这很重要的详细例子

在这一节中,我将编写两个函数——一个操作整数,一个操作列表。通过探索每个函数的输出并解释幕后发生的事情,我将说明为什么在处理可变对象时必须小心。

第一个函数将数字6加到一个整数上,第二个函数将数字6追加到一个列表中:

# Add 6 to a number
def add_6_to_number(num):
     num = num + 6# Append 6 to the end of a list
def append_6_to_list(lst):
     lst.append(6)

现在,我将利用上面的函数编写一小段代码。请密切注意初始整数的值,并在调用函数前后列出变量:

>>> num = 10
>>> add_6_to_number(num)
>>> print(num) # num remains unchanged
10>>> list_num = [10]
>>> append_6_to_list(list_num)
>>> print(list_num) # list_num, however, changes
[10, 6]

就我个人而言,我第一次学习时就发现这很有趣。变量num保持不变,但是变量list_num发生了变化。为什么会这样?它与如何将可变对象传递给函数有关。这个更容易目测。

下面,我从 Python Tutor 复制了三张图片,这是一个很棒的在线工具,可以帮助你可视化你的 Python 代码。它们显示了在我调用函数add_6_to_num之前、期间和之后发生的事情。

作者图片

作者图片

作者图片

这里关键的观察是,当调用add_6_to_num时,它创建了变量num副本,并且该副本是函数内部被更改的内容。但是我们定义的初始变量num在函数之外保持不变。所有不可变对象都是如此:当它们被传递到函数中时,Python 会复制它们。

现在,让我们来看看列表对应的三个图表:

作者图片

作者图片

作者图片

看到最重要的区别了吗?因为列表是可变的,所以 Python 在将它们传递给函数时不会复制它们。因此,在上面,对相同的列表的引用(用箭头表示)被传递到append_6_to_list函数中。函数内部和函数外部的变量引用的是同一个链表,所以当它在函数内部发生变化时,函数外部的变量num_list的值也会发生变化。

所有可变对象都是如此——当它们被传递到一个函数中时,Python 不会复制它们,而只会传递一个对已经存在的对象的引用。这是一个人在编写代码时应该特别注意的事情,因为它甚至会绊倒最有经验的程序员。如果您的代码中有一堆对列表、字典或其他可变对象的引用,这可能会导致许多问题并导致意外的值。

最后的想法

因为数据科学是在统计学的基础上发展起来的,所以相应的数据科学教育场所有时会忽略教授计算机科学中的重要概念。然而,这可能导致难以维护的低质量代码。

列表和字典是 Python 中数据科学的重要组成部分——它们都是 Python 的数据科学模块 Pandas 广泛使用的数据结构。因此,在编写利用和依赖这些对象的代码时,理解 Python 如何处理这些对象是很重要的。了解本文中的概念对我很有帮助,我希望对你也有帮助。

下次见,伙计们!

想擅长 Python? 获取独家,免费获取我简单易懂的攻略

请考虑使用我下面的推荐链接注册成为正式的媒体会员。你一个月就能看无限的故事,你的会员费直接支持我和其他作家。

https://murtaza5152-ali.medium.com/?source=entity_driven_subscription-607fa603b7ce---------------------------------------

快速排序算法综述

原文:https://towardsdatascience.com/an-overview-of-quicksort-algorithm-b9144e314a72

排序是以结构化的方式组织元素的过程。Quicksort 是最流行的排序算法之一,它在典型情况下使用nlogn比较来对 n 个元素的数组进行排序。快速排序基于分治策略。我们将在本教程中看看快速排序算法,看看它是如何工作的。

快速排序算法概述|作者图片

快速排序的想法

快速排序是一种快速排序算法,它通过将一个大的数据数组拆分成更小的子数组来工作。这意味着每一次迭代都是通过将输入分成两个部分,对它们进行排序,然后再重新组合来实现的。对于大型数据集,该技术非常高效,因为其平均和最佳情况的复杂度为O(n*logn).

它是由东尼·霍尔在 1961 年创建的,至今仍是最有效的通用排序算法之一。它的工作方式是递归地将子列表排序到给定中枢的任一侧,并围绕该中枢动态地移动列表中的元素。

因此,快速排序方法可以概括为三个步骤:

  • 选择:选择一个元素。
  • Divide: 拆分习题集,将较小的部分移到枢轴的左边,将较大的项目移到右边。
  • 重复合并:重复上述步骤,合并之前已经排序的数组。

快速排序的优势

让我们来了解一下使用快速排序的几个主要好处:

  • 它工作迅速有效。
  • 与其他排序算法相比,它具有最好的时间复杂度。
  • 快速排序的空间复杂度为O(logn),对于空间有限的情况,这是一个很好的选择。

快速排序的局限性

尽管快速排序是最快的算法,但它也有一些缺点。让我们来看看快速排序的一些缺点。

  • 这种排序技术被认为是不稳定的,因为它不能保持键值对的初始顺序。
  • 当枢轴元素最大或最小时,或者当所有组件具有相同的尺寸时。快速排序的性能会受到这些最坏情况的显著影响。
  • 这很难实现,因为这是一个递归过程,尤其是在递归不可用的情况下。

运行中的快速排序算法

让我们通过一个例子来更好地理解快速排序算法。在本例中,数组(如下图所示)包含未排序的值,我们将使用 Quicksort 对其进行排序。

未排序和排序的数组|按作者排序的图像

1)。选择枢轴

该过程从从列表中选择一个元素(称为枢纽)开始;这可以是任何元素。枢轴可以是:

  • 任意元素
  • 第一个或最后一个元素
  • 中间分子

对于这个例子,我们将使用最后一个元素4,作为我们的枢纽。

2)。重新排列阵列

现在,这里的目标是重新排列列表,使得所有小于枢轴的元素都朝向它的左边,所有大于枢轴的元素都朝向它的右边。

  • 从第一个索引开始,枢轴元素与所有项目进行比较。如果元素大于 pivot 元素,则追加第二个指针。
  • 当与其他元素比较时,如果找到一个比 pivot 元素更小的元素,这个更小的元素将与之前识别的更大的元素进行交换。

重新排列元素|按作者排列图像

让我们简化上面的例子,

  • 7开始的每一个元素都将与轴心(4)进行比较。因为74大,所以第二个指针将放置在7处。
  • 下一个元素,元素2现在将与枢轴进行比较。由于2小于4,所以会被之前找到的更大的数字7所代替。
  • 数字72互换。现在,pivot 将与比4小的下一个元素1进行比较。
  • 所以再一次,7会和1对调。
  • 该过程继续,直到到达倒数第二个元素,并且在最后,枢轴元素然后被第二个指针替换。此处,编号4(枢轴)将替换为编号6

重新排列元素|按作者排列图像

由于元素213少于 4 个,它们位于枢轴的左侧。元素可以按任何顺序排列:‘1’,’2’,’3’, or ‘3’,’1’,’2’, or ‘2’,’3’,’1’。唯一的要求是所有的元素必须少于主元素。同样,在右侧,不管它们的顺序如何,所有组件都应该大于轴心。

在已排序的位置进行透视|按作者排序的图像

简而言之,该算法搜索每一个小于中枢的值。小于 pivot 的值将放在左边,而大于 pivot 的值将放在右边。一旦值被重新排列,它将把轴心设置在它的排序位置。

③。划分子阵列

一旦我们划分了数组,我们可以将这个问题分解成两个子问题。首先,将数组的段排序到枢轴的左边,然后将数组的段排序到枢轴的右边。

按作者对子数组|图像进行排序

  • 与我们在步骤 2 中重新排列元素的方式相同,我们将分别为左右子部分中的每一个选择一个枢轴元素。
  • 现在,我们将重新排列子列表,使所有元素都小于轴心点,轴心点向左。例如,元素3在三个元素中最大,满足条件,因此元素3处于其排序位置。
  • 以类似的方式,我们将再次处理子列表,并对元素21进行排序。当我们最后得到一个元素时,我们将停止这个过程。
  • 对右侧子列表重复相同的过程。子阵列被细分,直到每个子阵列只包含一个元素。
  • 至此,数组已排序:)

快速排序算法

快速排序函数算法

//start –> Starting index,  end --> Ending index
Quicksort(array, start, end)     
{  
   if (start < end)     
   {  
      pIndex = Partition(A, start, end)
      Quicksort(A,start,pIndex-1)
      Quicksort(A,pIndex+1, end)
   }
}

分区函数算法

使用分区方法以一定的顺序重新排列子阵列。你会发现各种各样的分区方法。这里我们将看到一个最常用的方法。

partition (array, start, end)
{
    // Setting rightmost Index as pivot
    pivot = arr[end];  

    i = (start - 1)  // Index of smaller element and indicates the 
                   // right position of pivot found so farfor (j = start; j <= end- 1; j++)
    {
        // If current element is smaller than the pivot
        if (arr[j] < pivot)
        {
            i++;    // increment index of smaller element
            swap arr[i] and arr[j]
        }
    }
    swap arr[i + 1] and arr[end])
    return (i + 1)
}

快速排序实现

让我们看看用 JavaScript 和 Python 编程语言编写的快速排序程序。

Javascript

让我们从创建一个允许你交换两个组件的函数开始。

function swap(arr, i, j) 
{    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

现在,让我们添加一个函数,该函数使用最后一个元素(最后一个值)作为透视,将所有较小的项目移动到透视的左侧,将所有较大的元素移动到透视的右侧,并将透视放在排序后的数组中的适当位置。

function partition(arr, start, end) {

    // pivot
    let pivot = arr[end];

/* Index of a smaller element that specifies the pivot's correct position so far. */

 let i = (start - 1);

    for (let j = start; j <= end - 1; j++) {

        // If current element is smaller than the pivot
        if (arr[j] < pivot) {

            i++;
            swap(arr, i, j);
        }
    }
    swap(arr, i + 1, end);
    return (i + 1);
}

接下来,让我们添加 main 函数,它将对元素进行分区和排序。

function quickSort(arr, start, end) {
    if (start < end) {

        // The partitioning index is represented by pi.

        let pi = partition(arr, start, end);

        // Separately sort elements before and after partition
        quickSort(arr, start, pi - 1);
        quickSort(arr, pi + 1, end);
    }
}

最后,让我们添加一个函数来打印数组。

function printArray(arr, size) {
    for (let i = 0; i < size; i++)
        document.write(arr[i] + " ");

    document.write("");
}

// Let's start by sorting the unsorted.

let arr = [7, 2, 1, 6, 8, 5, 3, 4];
let n = arr.length;

document.write("Orginal array:" + arr);
quickSort(arr, 0, n - 1);
document.write("Sorted array:"+arr);

这里是快速排序实现(Js)的完整代码

JavaScript 中的快速排序实现

代码输出(Javascript) |作者截图

Python

让我们首先创建一个函数,它负责对数组的第一个和最后一个元素进行排序。

def find_pviot_index(A,start,end):
    pivot=A[end]
    p_index=start
    for iter in range(start,end):
        if A[iter] <= pivot:
            A[p_index],A[iter]=A[iter],A[p_index]
            p_index+=1
    A[p_index],A[end]=pivot,A[p_index]
    return p_index

接下来,我们将添加实现 QuickSort 的主函数。

def quick_sort(A,start,end):
    if start < end:
        pivot_index=find_pviot_index(A,start,end)
        print("--------------",A)
        quick_sort(A,start,pivot_index-1)
        quick_sort(A,pivot_index+1,end)

最后,让我们添加一个函数来打印数组。

A=list()
n=int(input("Set of numbers you want to enter: "))
for x in range(0,n):
    num=int(input("Enter Num:"))
    A.append(num)
print("Orignal array:",A)     
quick_sort(A,0,n-1)
print("Sorted array :",A)

这里是用 Python 实现 Quicksort 的完整代码。

Python 中的快速排序实现

代码输出(Python) |作者截图

快速排序复杂性

让我们看看快速排序在最好、一般和最坏情况下的空间和时间复杂度。一般来说,QuickSort 消耗的时间可以写成如下。

T(n) = T(k) + T(n-k-1) + O(n)

这里,T(k)T(n-k-1)指的是两次递归调用,而最后一项O(n)指的是分区过程。小于 pivot 的项目数用k表示。

时间复杂度

1)。最佳情况复杂度:当划分算法总是选择中间元素或中间元素附近作为支点时,最佳情况场景发生。快速排序的最佳时间复杂度是 O (n*logn)。以下是最佳情况下的重现。

T(n) = 2T(n/2) + O(n)//solution O(nLogn)

2)。平均情况复杂性:当数组元素处于无序序列中,没有适当地增加或减少时,就会出现这种情况。Quicksort 的平均案例时间复杂度为O(n*logn)。以下是平均情况下的重现。

T(n) = T(n/9) + T(9n/10) + O(n)//solution O(nLogn)

3)。最差情况复杂性:最差情况是分区算法每次都选择最大或最小的元素作为枢纽元素。快速排序最坏情况下的时间复杂度是O (n2)。以下是最差情况下的复发。

T(n) = T(0) + T(n-1) + O(n)//solution O(n2)

时间复杂度|快速排序|作者图片

空间复杂性

快速排序的空间复杂度为O(log n).

空间复杂性|快速排序|作者图片

快速排序应用程序

排序算法用于查找信息,因为快速排序是最快的,所以它经常被用作更有效的搜索方法。

  • 它适用于不需要稳定排序的地方。
  • 因为它是尾递归的,所以每次调用优化都可以进行。
  • 它在事件驱动的模拟和运筹学中是有用的。

快速排序是否优于其他算法?

快速排序可能有一些缺点,但它是最快、最有效的排序算法。快速排序具有O (logn)空间复杂度,这使得它成为空间受限情况下的绝佳选择。

虽然最坏情况下的运行时间总是一样的,但是 Quicksort 往往比 HeapSort (nlogn)要快。快速排序比堆排序占用更少的空间(因为堆是一个几乎满的二叉树,有指针开销)。所以,在对数组进行排序时,quicksort 是首选。

结论

快速排序可能有一些缺点,但它是最快的排序算法。快速排序是一种有效的算法,在实践中表现良好。

在本文中,我们学习了什么是快速排序,它的优点和缺点,以及如何实现它。如果你想要任何算法的详细解释或者算法对比,欢迎在下面随意评论。

参考资料和推荐读物

Python 中使用 JSON 数据的各种方法概述

原文:https://towardsdatascience.com/an-overview-of-various-ways-to-work-with-your-json-data-in-python-772b53c9d06a

Python 提供了许多不同的方法来处理这种常见的数据存储格式。

Unsplash 上的 Shubham Dhage 拍摄的照片

如果你从事技术工作——特别是作为一名软件工程师或数据科学家——你可能经常听到 JSON 这个术语。事实上,我敢打赌,你自己也曾在某个时候不得不这样做。

什么是 JSON 数据?

JSON 代表 JavaScript 对象符号。从技术上讲,它是由一位名叫道格拉斯·克洛克福特的程序员(他实际上仍在从事 JavaScript 的开发工作)[1]从专注于数组和文字的 JavaScript 子集派生出来的。

然而,这个名字可能有点误导。尽管 JSON 起源于 JavaScript,但它是一个独立于语言的实体。它本质上只是一种指定和传输数据的便捷方式。它也是人类可读的,尤其是与 XML 等替代格式相比。

好了,介绍够了。如果你对更多的历史细节感兴趣,可以随时查看官方网站:【json.org

在本文中,我们将专门讨论 Python 领域中的 JSON。JSON 无处不在——因此,无论是作为一名进行服务器端开发的软件工程师,还是作为一名试图将信息读入表格的数据科学家,您都很有可能在某个时候不得不处理它。

在我们开始以不同的方式使用 Python 中的 JSON 之前,我们需要知道它实际上是什么样子。在其核心,JSON 数据只是一个大的键值对数组。如果你不熟悉这个术语,它实际上意味着数据是通过给单个值一个引用名或来组织的。

这通过例子更容易看出。例如,假设我们想要存储学生期末考试成绩的信息。我们的 JSON 对象可能如下所示(可怜的 Kaylee):

{
  "John": 92,
  "Kaylee": 44,
  "Akshay": 78,
  "Zahra": 100
}

这种结构可以更复杂和嵌套(有可能使值数组甚至它们自己的对象具有更多的键-值对),但这是基本的思想。一旦你掌握了它,剩下的只是运用和一些精神上的奉献。

这里的关键问题是:这看起来熟悉吗?如果你熟悉 Python 中的数据结构,那么你大脑的神经通路现在应该已经疯狂了。

这基本上只是一个 Python 字典【2】,有一个重要的警告:JSON 中的字符串必须包含在双引号中,而 Python 字典允许单引号或双引号。

方法 1:用熊猫

事实上,JSON 的结构与 Python 字典完全相同,这对像我们这样的熊猫程序员来说是一个很好的礼物。回想一下直接从字典中制作熊猫数据框的最简单方法之一:

my_dict = {
           "Name": ["Alisha", "Ariel", "Aditi", "Tatiana", "Juan"],
           "Degree": ["Nursing", "Biomedical Engineering", "History", "Psychology", "Mathematics"]
          }
df = pd.DataFrame(my_dict)
df

作者图片

正如我们在上面看到的,JSON 的结构实际上和字典一样。因此,Pandas 带有一个实用方法,使得将 JSON 文件读入数据帧变得非常简单,这一点也不奇怪。

如果您习惯于使用 CSV 文件,那么您可能以前运行过类似下面的内容:

my_df = pd.DataFrame('path/to/data.csv')

使用 JSON 遵循同样的模式。如果我们将上面的数据保存为 JSON 文件,我们可以将它读入数据帧,如下所示:

my_json_df = pd.DataFrame('path/to/data.json')
my_json_df

作者图片

仅此而已。请记住,JSON 数据的格式与 Python 字典相同,是一组键值对。因此,这个数据帧与我们之前使用原生 Python 展示的示例相同。

方法 2:原生 Python

如果你不熟悉熊猫,但仍然需要一种使用 JSON 的方法,不用担心。Python 有一个很棒的库可以帮助完成这个任务:json(我知道很聪明)。

你将需要这个库中的两个主要函数:loaddump

load函数让您从 JSON 文件中读入数据。然而,它不会像熊猫的read_json功能那样将其转换为字典,而是将数据作为 Python 字典提供给你。在对文件调用load函数之前,我们还需要使用 Python 内置的open函数打开它:

>>> import json
>>> my_file = open('path/to/example.json', 'r') # 'r' for read mode
>>> data_dict = json.load(my_file)
>>> my_file.close() # Don't forget to close the file
>>> data_dict
{
 "Name": ["Alisha", "Ariel", "Aditi", "Tatiana", "Juan"],
 "Degree": ["Nursing", "Biomedical Engineering", "History", "Psychology", "Mathematics"]
}

dump函数的作用正好相反:它让您用 Python 定义一个字典,然后将其写入 JSON 文件。请注意,如果您以写模式打开文件,用“w”表示,您将覆盖任何现有的内容。如果您想要添加到文件中,请使用 append 模式(用“a”表示)。

>>> data_dict = {
             "Name": ["Alisha", "Ariel", "Aditi", "Tatiana", "Juan"],
             "Degree": ["Nursing", "Biomedical Engineering", "History", "Psychology", "Mathematics"]
            }

>>> my_file = open('path/to/example.json', 'w') # 'w' for write mode
>>> json.dump(data_dict, my_file)
>>> my_file.close() # Don't forget to close the file

这里有两个相关的函数值得一提:loadsdumps。令人困惑的是,这些应该被理解为“load-s”和“dump-s”。末尾的“s”代表“string”

这些函数的工作方式与它们的对应函数类似,只是它们没有被配置为处理文件。很多时候,在编程时,我们直接从某个服务器接收 JSON 字符串形式的数据,而不需要文件作为中间人。要将这些数据读入 Python 字典,我们可以使用loads函数。或者,要将字典转换成我们需要发送给服务器的 JSON 字符串,我们可以使用dumps

回顾+最终想法

这里有一个小型 JSON 备忘单:

  1. 在熊猫中,使用read_json功能。
  2. 如果你正在使用 JSON 模块,如果你正在处理 JSON 文件,使用loaddump,如果你正在直接处理 JSON 字符串,使用loadsdumps

至此,您应该已经准备好在野外处理 JSON 了。下次再见,节日快乐!

想擅长 Python? 获取独家、免费获取我简单易懂的指南点击 。想在介质上无限阅读故事?用我下面的推荐链接注册!

https://murtaza5152-ali.medium.com/?source=entity_driven_subscription-607fa603b7ce---------------------------------------

参考

[1]https://blog.sqlizer.io/posts/json-history/
【2】https://medium . com/forward-data-science/whats-in-a-dictionary-87 f9b 139 cc 03

Python 中探索性数据分析的可视化技术概述

原文:https://towardsdatascience.com/an-overview-of-visual-techniques-for-exploratory-data-analysis-in-python-d35703d43faf

探索性数据分析

关于如何在 seaborn 和 matplotlib 中为 Visual EDA 绘制最常见图形的教程

Firmbee.com 在 Unsplash的照片

在我们开展数据科学项目之前,我们必须首先尝试理解数据,并问自己一些问题。探索性数据分析(EDA) 是数据科学项目的初步阶段,它允许我们从数据中提取重要信息,了解哪些问题可以回答,哪些问题不能回答。

我们可以使用不同的技术进行 EDA,例如可视化和定量技术。在这篇文章中,我们关注视觉技术。许多不同类型的图表可以用来直观地分析数据。它们包括折线图、条形图、散点图、面积图、表格图、直方图、棒棒糖图、地图等等。

在可视化 EDA 阶段,我们使用的图表类型取决于我们想要回答的问题类型。在此阶段,我们不关注美学,因为我们只对回答我们的问题感兴趣。美学将在最后的数据叙述阶段得到关注。

我们可以执行两种类型的 EDA:

  • 单变量分析,一次只关注一个变量
  • 多变量分析,一次关注多个变量。

执行 EDA 时,我们可以有以下类型的变量:

  • 数字——一个可以量化的变量。它可以是离散的,也可以是连续的。
  • 分类 —一个只能假设有限数量的值的变量。
  • 序数 —可以排序的数值变量

在本文中,我将通过一个实际例子向您展示一些最常见的 EDA 可视化技术,这个例子使用了matplolibseaborn Python 库。所描述的概念是通用的,因此您可以轻松地将它们应用于其他 Python 库或编程语言。

文章组织如下:

  • 场景的设置
  • 单变量分析的可视化技术
  • 多元分析的可视化技术

1 场景的设置

此场景的目的是说明 Visual EDA 的主要图形。作为一个样本数据集,我们使用了欧盟地区的 IT 薪资调查,该调查在 CC0 许可下提供。我要感谢帕鲁尔·潘迪,他为 EDA 写了一篇关于 5 个真实世界数据集的精彩文章。我在那里发现了本文中使用的数据集。

首先,我们将数据集加载为熊猫数据帧:

import pandas as pddf = pd.read_csv('../Datasets/IT Salary Survey EU  2020.csv', parse_dates=['Timestamp'])
df.head()

数据集包含 1253 行和以下 23 列:

'Timestamp', 
'Age', 
'Gender', 
'City', 
'Position ',
'Total years of experience', 
'Years of experience in Germany',
'Seniority level', 
'Your main technology / programming language',
'Other technologies/programming languages you use often',
'Yearly brutto salary (without bonus and stocks) in EUR',
'Yearly bonus + stocks in EUR',
'Annual brutto salary (without bonus and stocks) one year ago. Only answer if staying in the same country',
'Annual bonus+stocks one year ago. Only answer if staying in same country',
'Number of vacation days', 
'Employment status', 
'Сontract duration',
'Main language at work', 
'Company size', 
'Company type',
'Have you lost your job due to the coronavirus outbreak?',
'Have you been forced to have a shorter working week (Kurzarbeit)? If yes, how many hours per week',
'Have you received additional monetary support from your employer due to Work From Home? If yes, how much in 2020 in EUR'

2 单变量分析的可视化技术

单变量分析一次只考虑一个变量。我们可以考虑两种类型的单变量分析:

  • 分类变量
  • 数字变量

2.1 分类变量

我们可以绘制的第一个图表是计数图,它统计了每个类别的频率。在我们的示例中,我们可以通过只考虑频率大于 10 的位置来绘制位置列的频率。首先,我们创建遮罩:

mask = df['Position '].value_counts()
df_10 = df[df['Position '].isin(mask.index[mask > 10])]

然后,我们建立图表:

import matplotlib.pyplot as plt
import seaborn as snscolors = sns.color_palette('rocket_r')
plt.figure(figsize=(15,6))
sns.set(font_scale=1.2)
plt.xticks(rotation = 45)
sns.**countplot**(df_10['Position '], palette=colors)
plt.show()

作者图片

我们可以绘制的第二种图形是饼图,它显示了计数图的相同信息,但它也添加了百分比:

values = df_10['Position '].value_counts()
plt.figure(figsize=(10,10))
values.**plot(kind='pie', colors = colors,fontsize=17, autopct='%.2f')**
plt.legend(labels=mask.index, loc="best")
plt.show()

作者图片

2.2 数值变量

在这种情况下,我们可能对数据分布感兴趣,所以我们可以绘制一个直方图。直方图将所有可能的值分解成多个仓,然后计算出一个值属于哪个仓。在我们的示例中,我们可以绘制前 10 名薪金的直方图,因此我们构建如下掩码:

mask = df['Yearly brutto salary (without bonus and stocks) in EUR'].value_counts()
df_10 = df[df['Yearly brutto salary (without bonus and stocks) in EUR'].isin(mask.index[mask > 10])]

然后,我们可以构建直方图:

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
plt.xlabel('Yearly brutto salary (without bonus and stocks) in EUR')
plt.ylabel('distribution')
plt.**hist**(df_10['Yearly brutto salary (without bonus and stocks) in EUR'], bins=20, color='#AD1759')
plt.show()

作者图片

另一个类似于直方图的图形是 dist 图,它也绘制了内核密度估计值(KDE):

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
sns.**distplot**(df_10['Yearly brutto salary (without bonus and stocks) in EUR'], color='#AD1759')
plt.show()

作者图片

另一种用于绘制数字变量的图形是盒形图,它允许您可视化中心和分布,以及其他有用的信息,包括最小、最大值。我们还可以使用箱线图来检测异常值:

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
sns.**boxplot**(df_10['Yearly brutto salary (without bonus and stocks) in EUR'], color='#AD1759')
plt.show()

作者图片

有趣的是,在上图中有一个 120,000 欧元的异常值。

与箱形图相似的是小提琴图,它也显示了数据分布:

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
sns.**violinplot**(df_10['Yearly brutto salary (without bonus and stocks) in EUR'], color='#AD1759')
plt.show()

作者图片

3 多变量分析的可视化技术

多元分析一次考虑更多的变量。在本文中,我们关注的是双变量分析,它一次只关注两个变量。

对于双变量分析,我们可以考虑三种类型的可视化技术:

  • 数字到数字
  • 数字到范畴
  • 绝对到绝对

3.1 数值对数值

在这种情况下,我们考虑两个数值变量,我们想比较它们。最常见的表示它们之间关系的图形是散点图,它将两个变量之间的交叉点绘制成一个点。在我们的例子中,我们可以绘制工资与年龄的散点图:

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
sns.**scatterplot**(df_10['Yearly brutto salary (without bonus and stocks) in EUR'],df_10['Age'],color='#AD1759')
plt.show()

作者图片

3.2 数值到分类

代表数字变量和分类变量之间关系的最常见的图形是条形图。在我们的示例中,我们可以绘制工资与性别的柱状图:

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
sns.**barplot**(df_10['Yearly brutto salary (without bonus and stocks) in EUR'], df_10['Gender'],color='#AD1759')
plt.show()

作者图片

3.3 绝对到绝对

在这种情况下,我们比较两个分类变量。我们可以使用热图,通过颜色绘制两个变量之间的相关性。在我们的示例中,我们可以绘制资历级别与性别的关系图:

plt.figure(figsize=(15,6))
sns.set(font_scale=2)
sns.**heatmap**(pd.crosstab(df_10['Seniority level'], df_10['Gender']), cmap='rocket_r')
plt.show()

作者图片

摘要

在本文中,我通过两个 Python 库:matplotlib 和 seaborn,展示了一些基本的图形来执行可视化的探索性数据分析。在某些情况下,我使用 matplotlib,在其他情况下使用 seaborn。

可以绘制许多其他的可视图形。在这里,我描述了最常见的图表来开始。

我想提醒你,EDA 只是为了数据探索。如果你想构建精彩的可视化,你应该把重点放在数据叙事(讲故事)技术上。

如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的信息。

相关文章

https://pub.towardsai.net/are-data-journalism-and-data-science-the-same-thing-77ba7ec794d4 https://alod83.medium.com/how-to-design-a-data-journalism-story-b2e421673b6e

人工智能偏见的“无偏见”指南

原文:https://towardsdatascience.com/an-unbiased-guide-to-bias-in-ai-3841c2b36165

概念概述

AI/ML 模型中的伦理与统计偏差

图 1——人工智能中的偏见……它是如何渗透进来的,有哪些不同的类型?—作者图片

每当在人工智能的背景下提到伦理,偏见、公平和公正的话题就会接踵而至。

类似地,每当提到训练和测试机器学习模型时,偏差&方差之间的权衡就显得很重要。

但是这两次提到偏见是指同一件事吗?在某种程度上很好,但不完全是…

图片 2 — 统计(模型/算法)与伦理(公平/歧视)偏见— 作者图片

一些基本定义..

在我继续解释之前,我怀疑本博客的大多数读者已经至少对机器学习和相关概念有了基本的了解,所以我将只浏览一些主要供参考的关键定义:

机器学习: 计算机(即机器)使用算法从数据中“学习”模式的过程,不需要人类明确定义要学习哪些特定模式—作者

为了让机器学习这些模式,特别是在“监督学习”中,它们要经历一个训练过程,通过这个过程,算法通常以迭代的方式从训练数据集中提取模式。然后,它在一个看不见的(样本外)测试数据集上测试其预测,以验证它从训练数据集学习的模式是否有效。

通俗地说什么叫偏见?

剑桥词典对偏见的教科书定义如下:

偏见:以不公平的方式支持或反对某个特定的人或事的行为,因为允许个人观点影响你的判断。

一个相关的例子是,体育迷支持一个特定的球队,由于他们的偏见,总是预测他们的球队将赢得每场比赛,即使实际上他们的胜率可能不到 50%,这清楚地表明了他们的幻灭(又名偏见)!

图片 3——曼联 2022/2023 英超赛季前三场比赛——预测vs现实——从一个球迷“有偏见”的角度——作者图片

什么是统计偏差?

在训练机器学习模型中,在偏差和方差之间存在权衡,其中偏差说明模型捕捉数据中的模式有多好,而方差说明这些模式应用于不同数据片段的有多好(即,训练对测试对验证)。

  • 欠拟合:具有高偏差的模型被认为欠拟合训练数据集,即它没有学习到足够的模式来捕捉输入和输出/目标之间的相关关系。在这种情况下,模型在训练数据集和测试/验证数据集上的表现差异往往很小,即在所有数据集上表现同样差。
  • 过拟合:具有低偏差的模型被称为过拟合训练数据集,即它在训练数据集中学习了太多的粒度模式,这使得它在针对训练数据集进行测试时表现得非常好,但在针对测试/验证数据集进行测试时表现得非常差,从而具有高方差。

当部署到生产中时,欠拟合和过拟合都会导致较差的性能,并表现出这里所谓的统计偏差统计偏差可以总结为模型预测与现实的平均误差(即模型试图预测的正确输出)。

统计偏差的根本原因是什么?

统计偏差的主要根源通常是因为模型通过训练过程学习的模式不能反映输入数据和目标/输出之间的真实关系。因此,该模型要么需要更多的优化和训练,要么需要更多/更好的数据和/或特征来学习更多相关的模式。

现在,假设我们正在训练一个 ML 模型来预测一支足球队赢得比赛的可能性。为此,我们可以获得 20 年的历史比分,并利用这些数据来预测特定比赛的比分。这个模型很可能会有很大的偏差,因为我们都知道过去的成绩并不总是未来表现的良好指标(特别是在曼联的情况下…!).

在这种情况下,可以包括额外的预测特征以减少模型的偏差,例如:球员的健康状况、明星球员的可用性、使用中的阵型、球员和教练的相对经验、比赛中的统计数据,例如控球量、传球次数、黄/红牌数、最近的成绩等等。

那么道德偏见呢?

统计偏差和伦理偏差可以被认为是独立的。你可以有一个在统计偏差方面近乎完美的模型(即具有较低的偏差/平均误差),但可能表现出极大的伦理偏差。道德偏见可定义为导致不道德(如非法、不公平或不道德)结果的偏见,通常不利于特定群体的个人权利。

统计偏差与伦理偏差——备忘单

我是所谓的“一页纸”的忠实粉丝,并创建了下面的备忘单,以浓缩的方式抓住了这个博客的精髓。它是一个有用的工具,可以评估 AI/ML 模型中统计和伦理偏见的潜在根本原因,以及解决这些问题的可能缓解措施的非详尽列表:

表 1 —人工智能/人工智能备忘单中的偏差 —统计与伦理偏差—由作者创建

上表的前半部分涵盖了 AI/ML 模型中不同类型的“统计偏差,其中大部分在数据科学社区中相对成熟。欠拟合和过拟合可以被认为是帮助检测 AI/ML 模型中的统计偏差的主要症状,而其他 7 项可以被认为是导致这种统计偏差的根本原因。
这 7 个根本原因是: 1) 用于训练的样本不具代表性, 2) 类别不平衡, 3) 缺乏足够的数据和/或预测特征, 4) 无效的算法和/或超参数, 5) 通过模型的无效动态再训练和/或强化学习代理中的“有偏见的”奖励函数来强化偏见, 6) 不一致的标记或错误标记

表格的后半部分涵盖了 AI/ML 模型中“道德偏见”的潜在根本原因以及可能的缓解措施,这是数据科学社区中一个不太成熟的话题,仍在不断发展。因此,在下一节中,我将借助“真实”的例子/用例,更深入地探究道德偏见的每一个潜在根源。

通过真实例子解释伦理和统计偏见

让我们考虑一个在金融服务行业很受欢迎的例子,一家银行开发了一个 ML 模型来预测个人的信用价值,以帮助他们决定是否发放贷款。

让我们假设这个模型以与个人家庭收入相关的历史数据作为输入,预测的目标是他们是否能够成功地全额偿还贷款。

在这种情况下,很有可能该模型学习了一种模式,即个人收入越高,全额偿还的可能性就越高。虽然这种模式在许多情况下可能是正确的,但它可能会有高统计偏差(即其预测的高平均误差),因为它没有考虑其他重要因素,如生活成本、受抚养人数量、行业和工作类型等。

通过包含受保护的特征的伦理偏见

让我们继续同一个信誉预测场景。在意识到这种统计偏差后,银行向模型中输入额外的数据,包括上面强调的一些因素(如生活成本等)以及一些个人数据,如性别和种族。这导致模型产生近乎完美的预测,且统计偏差较低。然而,由于包含了个人数据,同一模型现在可能表现出高度的道德偏见。

从统计学上来说,这可能是“真实的”,例如,某些种族在历史上平均比其他种族更成功地偿还贷款,然而,尽管这可能是真实的,但在大多数国家,1) 非法(基于平等法案,如欧盟的《欧洲商品和服务平等待遇指令》】和 2) 不道德、在决定一个人的信贷价值时将该人的种族考虑在内。

这是一个很好的例子,说明了一个模型如何具有较低的统计偏差,但同时表现出较高的伦理偏差。

图片 4 —通过包含受保护特征的人工智能中的伦理偏见—作者提供的图片

代理人的道德偏见

即使性别或种族等特征没有明确包含在输入数据中,模型也有可能通过所谓的代理特征以某种方式学习它们。代理特征是与所讨论的个人特征多少有些关联的特征,如男性占多数的某些工作(如建筑工人、飞行员等),或特定种族更受欢迎的某些邮政编码。

因历史偏见决定而产生的道德偏见

伦理偏见还会以另一种方式蔓延到机器学习模型中,那就是在模型试图优化自身的训练数据中使用历史偏见决策。

在相同的信用价值场景中,即使您从输入数据中删除了所有个人特征,如性别和种族,如果模型的输出是基于人类贷款官员以前做出的决定,则由于某些官员可能表现出的任何性别或道德偏见,另一种形式的道德偏见可能仍然存在。如果这些有偏见的决策有些系统化(例如,已经发生了很多次),ML 模型可以很容易地发现它们并学习相同的有偏见的模式。

因目标选择不当而产生的道德偏见

如果模型试图优化自身的目标选择不当,就会导致伦理偏见。例如,在股票投资组合选择模型中,设定一个纯粹寻求增加利润的目标,而不考虑公司的环境或可持续性影响或任何其他道德/法律因素,可能会导致不道德的投资组合选择。

同样,在信誉的例子中,有一个纯粹寻求利润最大化的目标可能会导致某些服务不足的社区的利率增加,从而使他们更加困难,并强化导致他们贫困状况的任何社会偏见。

语言模型中的伦理偏见

语言模型通常在大型文本语料库上训练,因此容易学习文本可能包含的任何不适当的语言或不道德的观点。例如,在非结构化文本中包含种族主义语言或脏话会导致模型学习这种模式,并在聊天机器人、文本生成或其他类似上下文中应用时引起问题。

减轻这种风险的一种方法是从语料库中删除任何不道德的部分,以避免模型学习有问题的模式/语言。人们还可以从文本中删除脏话,以确保训练数据更加“干净”。另一方面,完全排除这样的脏话也会对模型产生负面影响,因为可能需要识别和移除仇恨言论/脏话,如果模型在其训练期间没有遇到这些数据,这是不可能的。

我们如何评估/测试一个模型是否表现出伦理偏见?

评估一个模型是否表现出伦理偏见迹象的一个好方法是执行预测奇偶检验。简而言之,预测奇偶校验检查预测的分布是否等同于相关子群(如性别、种族等)。).

有不同类型的预测性奇偶检验,如偏差保持:例如,在 CV 筛选模型中,尽管在我们的数据分布中男性可能比女性多,但两性接受 CV 的比率应该相似;和偏见转化:例如,不管男女分布是否倾斜,目标都是实现两性接受人数相等。

例如,为了测试道德偏见的信用价值模型,可以在模型被训练后包括个人的性别或种族,以便检查其预测是否偏向某一性别或种族。如果存在异常,那么 SHAPLEY 值之类的可解释性技术可以帮助识别哪些特征充当受保护特征的潜在代理,并采取适当的行动。

然而,应该注意的是,有时除了接受偏见之外,没有什么可以做的。例如,可能存在这样一种模式,即某一职业类别的人被评估为比其他人更有信誉,但是,由于该领域妇女的分布较多,均等测试突出了对女性的积极偏向。这种情况有时可能需要接受,但理解和记录仍然非常重要。

这些偏见测试的细节超出了本博客的范围,但最好的来源之一是回顾牛津大学桑德拉·沃希特教授关于这个主题的工作(相关链接见最后的“进一步阅读”)。

为什么在机器学习模型中,伦理偏见被认为是更大的“风险”?

有人可能会说,基于规则的模型同样容易受到伦理偏见的影响,因为它们也可能包含与个人的种族、性别和其他受保护特征有关的规则。然而,主要区别在于,在机器学习中,1)有时这些特征被错误地包括或仅通过代理存在,以及 2)人类没有对模型学习的特定模式的直接输入(当然,除了选择训练数据、工程特征、选择算法、调整超参数等),因此,模型可能无意中学习有偏见的模式,这些模式保持隐藏,直到执行奇偶校验或其他类似测试来发现它们。

另一方面,在基于规则的系统中,所有这些规则都需要有人明确定义,除非开发人员有特定的恶意或无知,否则这些不道德的模式很难“潜入”。

总之…

偏见是人工智能中一个非常重要且不断发展的子领域,需要成为每个数据科学专业人员的首要考虑。这仍然是一个相对较新且不断发展的话题,对于行业来说,从一开始就统一一套通用的定义和术语是非常重要的。

正如在这篇博客中所提出的,统计偏差和伦理偏差是两种不同类别的偏差,具有不同的根本原因和缓解措施(参见表 1 的总结)。

大多数经验丰富的数据科学家已经很好地掌握了管理统计偏差,因为它涉及到 ML 中偏差与方差之间的既定权衡,但是,当涉及到管理 AI/ML 应用中的道德偏差时,需要更多的意识,特别是考虑到它可能会无意中歧视平等和隐私等基本人权。

延伸阅读…

这里有一些建议给那些有兴趣阅读更多关于这个话题的人:

你遇到过的 AI/ML 模型中有偏差的例子有哪些?期待大家的评论!

在 DAX 中计算货币转换的意外教训

原文:https://towardsdatascience.com/an-unexpected-lesson-with-calculating-currency-conversions-in-dax-58014260a98

货币兑换是常见的报告要求。在实现该解决方案时,我意外地学到了 Power BI 中的数据建模。

约翰·麦克阿瑟在 Unsplash 上的照片

介绍

在进行货币换算时,您必须有一个特定时期的换算率列表。该期间可以是每日或每月的平均汇率或每月的最后(收盘)汇率。

你可以从你的国家银行网上获得这样的名单。

就我而言,我住在德国,在瑞士工作;我观察了欧洲中央银行瑞士国家银行 (SNB)。

在那里,你可以免费下载一套完整的汇率 Excel 文件或 CSV 文件。

根据要求,我必须从 SNB 获取汇率。不幸的是,导出的数据只包含月平均和每月收盘汇率。

因为我需要每日汇率,所以我使用了我以前的一篇文章中描述的技巧来填补时间序列数据中的空白:

https://medium.com/codex/fill-gaps-in-time-series-with-this-simple-trick-in-sql-81ac655e5ad7

将费率表导入 Power BI 后,我编写了计算结果的方法。

以下三种方法从简单到复杂。

在我的开发工作中,我遇到了意想不到的困难,这使我在研究满足特定需求的最后一种方法时获得了新的知识。

案例 1 —一种来源货币兑换多种货币

在本例中,我们有一个 Transaction- (Fact-)表,其中包含以美元表示的值。然后,我们有一个货币兑换率表,它包括从美元到所有其他货币的每日兑换率。

数据模型如下:

图 1 —简单转换的数据模型(由作者提供)

我将使用同样的原理来模拟第三个也是最后一个解决方案。

因为我总是需要每期一个兑换率,所以在查看月度或年度数据时,我必须汇总每日兑换率。

出于这个原因,我需要首先创建一个平均比率指标:

Average Rate = AVERAGE(‘Exchange Rate’[Average_Rate])

现在,我可以使用以下方法来计算转化率:

Online Sales Currency =VAR SalesUSD =
    CALCULATE([Online Sales (By Order Date)]
              ,REMOVEFILTERS(‘Currency’)
              )VAR ExchangeRate = SELECTEDVALUE(‘Exchange Rate’[Average_Rate])RETURN
     SalesUSD * ExchangeRate

我必须在 CALCULATE()中使用 REMOVEFILTERS('Currency ')来确保目标货币选择不会过滤在线销售表。

或者,可以禁用货币和在线销售表之间的关系。

这种简单的方法有一些缺点:

  1. 该度量计算所选期间的平均汇率。这在结果的计算中引入了误差
  2. 该指标不考虑缺失率
  3. 这种方法只适用于一个方向和一种货币。

案例 2 —多种货币对多种货币

SQLBI 的人写了一篇关于货币兑换作为 DAX 模式的文章。

他们的解决方案涵盖了第一种情况的所有问题:

  • 将每日交易映射到相应的每日汇率
  • 考虑缺失率
  • 使用多种来源和目标货币的能力

在此重复细节毫无意义,因为文章和视频包含非常详细的解决方案描述。

您可以在下面的参考资料部分找到这篇文章和相应的视频。

案例 3 —使用过渡货币

在我的情况下,我有以下数据和要求:

  • 我的数据是欧元货币
  • 我有一个以瑞士法郎为基础货币的兑换率表
  • 我需要主要以瑞士法郎报告我的数据
  • 我也必须能够用美元分析数据

因为输入货币是固定的,所以我不需要两个货币汇率表,如第二个案例中的 DAX 模式解决方案之一所示。

当我想计算结果时,我首先要用欧元乘以欧元的汇率,得到瑞士法郎的值。然后,我可以将结果除以目标货币的汇率,得到最终结果。

我用与第一种情况略有不同的逻辑实现了下面的中间方法。嵌入了对逻辑的解释作为注释:

Cost (Currency) =// Step 1: Get the last date with an Exchange-Rate in the actual period
VAR LastExchRate = CALCULATE(
         LASTNONBLANK(‘Date’[Date], MIN(‘Exchange Rates’[Value]) )
         )// Step 2: Get the last Exchange-Rate for Euros
VAR ExchRateEUR = CALCULATE(AVERAGE(‘Exchange Rates’[Value])
                              ,REMOVEFILTERS(‘Currencies’)
                              ,’Currencies’[CurrencyCode] = “EUR1”
                              ,’Date’[Date] = LastExchRate
                              )// Step 3: Convert the Cost in EURO to CHF
// The Reason for the REMOVEFILTERS() is the same as explained in the first case
VAR CostCHF = CALCULATE([Cost (EUR)] * ExchRateEUR
                         ,REMOVEFILTERS(‘Currencies’)
                         )// Step 4: Get the current Currency Code
VAR SelectedCurr = SELECTEDVALUE(‘Currencies’[CurrencyCode])// Step 5: Calculate the Result using the Cost in CHF and the Rate of the current Currency Code
// The Formula to calculate the result is: (CostEuro * RateEUR => CHF) / RateSelectedCurrency
VAR Result = CALCULATE(DIVIDE ( CostCHF
                                , AVERAGE(‘Exchange Rates’[Value])
                                )
                          ,’Currencies’[CurrencyCode] = SelectedCurr
                          ,’Date’[Date] = LastExchRate
                          )RETURN
// Return the Result from Step 5 only when one Currency is selected.
// If no specific Currency is selected, the return Blank
IF ( HASONEVALUE(‘Currencies’[CurrencyCode])
                  ,SWITCH ( SelectedCurr
                            ,”EUR1", [Cost (EUR)]
                            ,”CHF1", CostCHF
                            ,Result)
                  , BLANK()
                  )

正如你在评论中看到的,我得到的是当期的最后一个汇率,而不是计算实际期间的平均汇率。

这样做的原因是,用这种方法验证结果很简单,因为我只需获得特定时期的最新汇率,就可以检查汇总结果(例如月和年)是否正确。

无论如何,我必须在步骤 2 中聚合汇率,因为 CALCULATE 只能使用一个聚合表达式作为第一个参数。但是,由于我将汇率限制在实际期间的最后一天,因此平均值()没有影响。

我需要 IF()中的开关()来检查:

  • 当前货币是欧元,返回以欧元表示的值
  • 当前货币为瑞士法郎,返回以瑞士法郎表示的值
  • 否则,返回计算结果

下一步是通过实现 DAX 模式解决方案中描述的方法来改进代码。

最后的措施如下:

Cost (Currency) =// Step 1:
// Get the Rates for each row in the Data table for CHF (The bridge/intermediary) currency
// As the Data currency is EURO, I have to take the rate for EURO to be able the calculate the value in CHF
VAR AggregatedValue_CHF =
                  CALCULATETABLE (
                      ADDCOLUMNS(
                        SUMMARIZE(‘Azure_UsageDetails’
                                          ,‘Date’[Date])
                        ,“RateEUR”
                        ,CALCULATE(
                          SELECTEDVALUE( ‘Exchange Rates’[Value] ))
                        , “Cost_EUR”, [Cost (EUR)]
                        ),
                      ‘Currencies’[CurrencyCode] = “EUR1”
                      )// Step 2:
// Get the Rates for each row in the Data table for the selected currency
VAR AggregatedValue_EUR =
                 ADDCOLUMNS(
                     SUMMARIZE(‘Azure_UsageDetails’
                       ,‘Date’[Date])
                       ,“Rate”
                        ,CALCULATE(
                          SELECTEDVALUE( ‘Exchange Rates’[Value] ) ) ,“Cost_EUR”, [Cost (EUR)]
                     )// Step 3:
// Combine both tables into one to be able to perform the correct currency conversion
VAR Cost_Rates = NATURALINNERJOIN(AggregatedValue_CHF,
                                  AggregatedValue_EUR)// Step 4:
// Perform the actual currency conversion with the use of the intermediary currency
VAR Result = SUMX(Cost_Rates
                   ,DIVIDE(([Cost (EUR)] * [RateEUR]), [Rate])
                   )// Step 5:
// Get the CurrencyCode of the actual currency
VAR SelectedCurr = SELECTEDVALUE(‘Currencies’[CurrencyCode])RETURN
// Step 6:
// Return the Result from Step 5 only when one Currency is selected.
// If no specific Currency is selected, the return is the Value in EURO
IF ( HASONEVALUE(‘Currencies’[CurrencyCode])
                  // Step 7:
                  // Check if the Currency is either CHF or EURO
                  // if the selected curreny is one of these, use the existing measures to get the result
                  ,SWITCH ( SelectedCurr
                             ,”EUR1", [Cost (EUR)]
                             ,”CHF1", [Cost (CHF)]
                             ,Result)
                  , [Cost (EUR)]
                  )

前两个步骤生成两个表,其中包含数据表中每一行的相应汇率。一个表格包含将欧元转换为瑞士法郎(CHF)的兑换率。另一个从实际的过滤上下文中获取转换率(例如 USD 或 AUD)。

第三步将两个表合并成一个表,如下所示:

图 2 —样本组合数据(非实际数据)(作者提供的数据)

现在,我可以使用步骤 4 中的公式来计算结果。

第四步和第五步与第一步相同,用于解决这种情况。这一步控制输出。

成本(瑞士法郎)衡量标准具有以下代码:

Cost (CHF) =
    VAR AggregatedValue_CHF =
            CALCULATETABLE (
               ADDCOLUMNS(
                    SUMMARIZE(‘Azure_UsageDetails’
                               ,‘Date’[Date])
                    , “Rate”
                    ,CALCULATE(
                          SELECTEDVALUE( ‘Exchange Rates’[Value] ) )
                    ,“Cost_EUR”, [Cost (EUR)]
               ),
               ‘Currencies’[CurrencyCode] = “EUR1”
             )VAR Result = CALCULATE(
                    SUMX(AggregatedValue_CHF
                         ,[Cost (EUR)] * [Rate]
                         )
                    ,’Currencies’[CurrencyCode] = “CHF1”
                    )RETURN
     Result

乍一看,这种方法效率很低,因为它几乎只使用公式引擎:

图 3 —货币兑换的服务器计时(由作者提供)

我将它用于一个只有 32,000 行的小型数据集。

我检查了由 SQLBI 提供的解决方案中的计时。当您使用月费率而不是日费率时,性能会稍好一些。当你可以忍受不太精确的月汇率时,用月汇率计算可能是一个可行的解决方案。

在任何情况下,你都需要用你的数据来评估性能,以了解性能是否对你有利。

180 ms 的响应时间对于我的数据集来说已经足够好了。

然后,我在拥有 6400 万行的大型数据集上测试了相同的度量。结果令人惊讶:

图 4 —带有大型事实表的货币兑换的服务器计时(由作者提供)

存储引擎执行 71.8 %的工作,并行度为 3.4(在四核笔记本电脑上)。测量在不到 1.5 秒后返回结果。
这一测量得出的结论是,该解决方案可以很好地适应数据量。

结果如下所示,仅选择了瑞士法郎、欧元和美元:

图 5 —货币转换的结果(作者提供的数字)

正如开始提到的,在解决这个挑战时,我学到了一些意想不到的东西。

当您查看开始时使用的数据模型时,在货币表和事实表之间有两条路径:

图 6 —不明确的路径(作者提供的图片)

这两条路给我的测量带来了问题。

这种不确定性来自于在货币表中选择一种货币来过滤事实表中的数据。

该筛选器导致一个空结果,因为事实表只包含欧元货币的值。

我无法将 remove filters(' currences ')添加到度量中(步骤 1 和 2),因为我需要选定的货币来获得正确的兑换率。

解决这个问题的唯一方法是禁用货币和事实表之间的关系。这种改变并没有改变结果:

图 7 —修正的数据模型(由作者提供)

这种行为是出乎意料的,因为我在其他货币转换项目中使用了这种建模技术。

结论

货币兑换的计算可能是一个棘手的挑战。

第一种情况下的解决方案很简单,但是它有一些缺点,因为它通过使用平均转换率返回不精确的数据。

DAX Pattern 网站上显示的解决方案非常好,因为它们使用每日或每月汇率来计算正确的结果。此外,这些解决方案还包含对缺失比率等的检查。

在大多数情况下,您可以使用这里显示的解决方案。

我根据自己的具体需求和可用数据创建了自己的解决方案。

但是,尽管这个解决方案很特别,我还是学到了一些宝贵的经验:

  • 确保数据模型不包含任何潜在的模糊关系
  • 根据不同值的数量和许多其他因素,解决方案在处理大量数据时会表现得更好
  • 当创建一个复杂的解决方案时,从一个易于测试的解决方案开始。然后,使用第一个版本来验证后续版本的结果,这需要复杂的查询或对照源数据进行检查

任何方法的下一步都是创建一个计算组。但是,不可能为整个数据模型开发一个计算项,因为第二种和第三种情况的解决方案在一个事实表上迭代。因此,您必须为每个事实表创建一个计算项。

照片由蒂姆·莫斯霍尔德Unsplash 上拍摄

参考

你可以在这里找到上面提到的 SQLBI 文章:货币转换— DAX 模式

相应视频在 SQLBI YouTube 频道:

我在第一个示例中使用了 Contoso 示例数据集,就像我在以前的文章中一样。你可以从微软这里免费下载 ContosoRetailDW 数据集。

Contoso 的数据可以在麻省理工学院的许可下自由使用,正如这里的所描述的

第三个示例中使用的数据集包含来自我的 Azure 云订阅的实际使用数据,这些数据被导入到 Power BI 中。

https://medium.com/@salvatorecagliari/membership

分析机器学习中的公平性(用 Python)

原文:https://towardsdatascience.com/analysing-fairness-in-machine-learning-with-python-96a9ab0d0705

进行探索性的公平性分析,并使用平等机会、均等优势和不同影响来衡量公平性

(来源: flaticon )

仅仅建立做出精确预测的模型已经不够了。我们还需要确保这些预测是公平的

这样做会减少预测偏差的危害。因此,你将在建立对你的人工智能系统的信任方面走很长的路。为了纠正偏见,我们需要从分析数据和模型的公平性开始。

衡量公平很简单。

理解一个模型为什么不公平更复杂。

这就是为什么我们将:

  • 首先做一个探索性公平分析——在你开始建模之前识别潜在的偏见来源。
  • 然后,我们将继续衡量公平—应用不同的公平定义。

您可以看到我们将在下面介绍的方法的摘要。

公平性分析方法概述(来源:作者)

我们将讨论这些方法背后的理论。我们也将使用 Python 来应用它们。我们将讨论关键的代码片段,你可以在 GitHub 上找到完整的项目。

资料组

我们将使用成人数据集 建立一个模型。你可以在表 1 中看到这个的快照。经过一点功能工程,我们将使用前 6 列作为模型功能。接下来的 2 个,种族和性别,是敏感属性。我们将基于这些来分析对群体的偏见。最后是我们的目标变量。我们将尝试预测一个人的收入是高于还是低于 5 万美元。

表 1:成人收入数据集快照(图片来源:作者)(数据集: UCI )(许可证:CC0:公共领域)

在加载这个数据集之前,我们需要导入一些 Python 包。我们用下面的代码做到了这一点。我们使用 NumPy熊猫进行数据操作(第 1-2 行)。 Matplotlib 用于一些数据可视化(第 3 行)。我们将使用 xgboost 来构建我们的模型(第 5 行)。我们还从 scikit-learn 导入了一些函数来评估我们的模型(第 7–9 行)。确保你已经安装了这些。

我们在下面导入数据集(第 7 行)。我们还会删除任何缺少值的行(第 8 行)。注意这里加载了一些额外的列。查看列名(第 1-4 行)。在此分析中,我们将只考虑我们在表 1** 中提到的那些。**

算法公平性的探索性分析

当你有了最终的模型时,评估公平性并没有开始。这也应该是你探索性分析的一部分。一般来说,我们这样做是为了围绕数据集建立一些直觉。所以,当谈到建模时,你对预期的结果有一个很好的想法。具体来说,为了公平,你想了解你的数据的哪些方面可能导致不公平的模型。

由于不公平的不同原因,你的模型可能变得不公平。在我们的探索性分析中,我们将关注与数据相关的 3 个关键来源。这些是历史偏差代理变量不平衡数据集。我们想了解这些在我们的数据中存在的程度。了解原因将有助于我们选择解决不公平的最佳方法。

不平衡的数据集

我们将从查看我们的数据集是否不平衡开始。具体来说,我们指的是敏感属性方面的不平衡。请看图 1,我们有按种族和性别划分的人口。你可以看到我们确实有一个不平衡的数据集。第一张图表显示我们 86%的人口是白人。同样,68%的人口是男性。****

图 1:按种族和性别分列的人口(资料来源:作者)

您可以看到我们是如何为下面的种族属性创建饼图的。我们从按种族统计人口开始(第 2 行)。我们使用索引定义标签(第 3 行)。这些是我们在图 1 中看到的不同种族的名称。然后,我们使用 matplotlib 中的 pie 函数绘制计数(第 6 行)。我们还使用标签创建了一个图例(第 7 行)。性别属性饼图的代码非常相似。

不平衡数据集的问题是模型参数可能会偏向大多数。例如,女性和男性的趋势可能不同。所谓趋势,我们指的是特征和目标变量之间的关系。一个模型将试图最大化整个群体的准确性。这样做可能有利于男性人口的趋势。因此,我们对女性人口的准确性可能较低。

定义受保护的功能

在我们继续之前,我们需要定义我们的保护特性。我们通过使用敏感属性创建二进制变量来做到这一点。我们定义变量,使 1 代表一个特权组,0 代表一个非特权组。通常,弱势群体在过去会面临历史不公。换句话说,这个群体最有可能面临来自一个有偏见的模型的不公平的决定。

我们使用下面的代码定义这些特性。对于种族,我们定义了受保护的特征,因此“白人”是特权群体(第 4 行)。也就是说,如果这个人是白人,这个变量的值为 1,否则为 0。对于性,“男性”是特权群体(第 5 行)。接下来,我们将使用这些二进制变量来代替原来的敏感属性。

在上面的代码中,我们还定义了一个目标变量(第 8 行)。其中,如果个人收入超过 5 万美元,则该值为 1,如果收入低于 5 万美元,则该值为 0。在第 1 行中,我们用原始的敏感属性创建了 df_fair 数据集。我们已经将目标变量和受保护的要素添加到该数据集中。它将被用作剩余公平性分析的基础。

流行

对于目标变量,患病率是阳性病例占全部病例的比例。其中肯定的情况是当目标变量的值为 1 时。我们的数据集的总体患病率为 24.8% 。在我们的数据集中,大约有 1/4 的人收入超过 5 万美元(T21)。我们也可以用流行度作为一个公平的衡量标准。

我们通过计算不同特权(1)组和非特权(0)组的患病率来做到这一点。你可以在下面的表 2 中看到这些值。注意,特权群体的患病率要高得多。事实上,如果你是男性,你挣 5 万美元以上的可能性几乎是女性的三倍

表 2:受保护特征的流行率(资料来源:作者)

我们可以通过计算受保护特征的交叉点的普遍性来更进一步。你可以在表 3 中看到这些数值。如果你属于两个特权群体,左上角给出了患病率(即性别= 1 &种族= 1)。同样,右下方给出了不属于任何特权群体的患病率(即性别= 0 &种族= 0)。这告诉我们,白人男性收入超过 5 万美元的可能性是非白人女性的 4 倍。****

表 3:受保护特征交叉的流行率(来源:作者)

我们使用下面的代码计算这些值。你可以看到总体患病率只是目标变量的平均值(第 1 行)。同样,我们可以对不同的受保护功能组合取平均值(第 3-5 行)。

在这一点上,你应该问自己为什么我们在患病率上有这么大的差异。该数据集是利用 1994 年美国人口普查数据建立的。这个国家有基于性别和种族歧视的历史。最终,目标变量反映了这种歧视。从这个意义上说,患病率可以用来理解历史不公正在多大程度上嵌入了我们的目标变量。

代理变量

我们可以分析偏差潜在来源的另一种方法是通过寻找代理变量。这些是与我们的受保护特征高度相关或关联的模型特征。使用代理变量的模型可以有效地使用受保护的特性来做出决策。

我们可以找到代理变量,就像在特征选择过程中找到重要特征一样。也就是说,我们在特征和目标变量之间使用一些关联的度量。除了现在,我们使用受保护的特性,而不是目标变量。我们将研究两种关联的度量方法——互信息特征重要性

在此之前,我们需要做一些功能工程。我们从创建一个目标变量开始,就像前面一样(第 2 行)。然后我们创建 6 个模型特征。首先,我们让的年龄的教育编号的每周工作时间保持不变(第 5 行)。我们从婚姻状况本国创建二元特征(第 6-7 行)。最后,我们通过将原始职业分成 5 组来创建职业特征(第 9-16 行)。在下一节中,我们将使用这些相同的特性来构建我们的模型。

互信息是两个变量之间非线性关联的度量。它表明一个变量的不确定性通过观察另一个变量减少了多少。在图 2 中,您可以看到 6 个特征和受保护特征之间的互信息值。注意婚姻状况和性别之间的高值。这表明了这些变量之间可能的关系。换句话说,婚姻状况可能是性别的一个替代变量。****

图 2:具有受保护特征的交互信息(来源:作者)

我们使用下面的代码计算互信息值。这是使用 mutual_info_classif 函数完成的。对于种族,我们传递我们的特征矩阵(第 2 行)和种族保护特征(第 3 行)。我们还告诉函数 6 个特征中哪些是离散的(第 4 行)。性的代码是相似的(第 5 行)。

我们可以采取的另一种方法是使用受保护的特征来构建模型。也就是说,我们尝试使用 6 个模型特征来预测受保护的特征。然后我们可以使用来自这个模型的特征重要性分数作为我们的关联度量。你可以在图 3 中看到这个过程的结果。

图 3:预测受保护特性时的特性重要性(来源:作者)

这个过程的另一个结果是我们有了模型的准确性。这些可以给我们一个整体关联的度量。预测种族的准确率为 72.7% ,预测性别的准确率为 78.9% 。如果我们回到图 2 中的互信息值,这种差异是有意义的。你可以看到性的价值通常更高。最终,我们可以预期代理变量对于性别来说比种族更成问题。

您可以在下面看到我们是如何计算种族指标的。我们从获得一个平衡的样本开始(第 2-7 行)。这就是为什么我们的数据集中有相同数量的特权和非特权。然后,我们使用这个数据集来构建一个模型(第 10 -11 行)。请注意,我们使用竞争保护特性作为目标变量。然后我们得到模型预测(第 12 行),计算准确性(第 15 行)并得到特征重要性分数(第 18 行)。

因此,我们已经看到数据集是不平衡的,T2 的患病率比 T4 的高。我们还发现了一些潜在的代理变量。然而,这一分析并没有告诉我们我们的模型是否会不公平。它只是强调了可能导致不公平模式的问题。在接下来的部分,我们将建立一个模型,并表明它的预测是不公平的。最后,我们将回到这个探索性的分析。我们将看到它如何帮助解释是什么导致了不公平的预测。

系统模型化

我们使用下面的代码构建我们的模型。我们正在使用xgbclassifier函数(第 2 行)。我们使用前面在代理变量一节中定义的特性和目标变量来训练模型。然后我们得到预测(第 6 行)并将它们添加到我们的 df_fair 数据集(第 7 行)。最终,这个模型的准确率为 85%。准确率为 73% 召回率为 60%。我们现在想衡量一下这些预测有多公平。

在我们继续之前,如果你想的话,你可以用你自己的模型替换这个模型。或者您可以尝试不同的模型特征。这是因为我们将使用的所有公平性度量都是模型不可知的。这意味着它们可以用于任何型号。他们通过将预测与原始目标变量进行比较来工作。最终,您将能够在大多数应用程序中应用这些指标。

公平的定义

我们通过应用不同的公平定义来衡量公平。大多数定义涉及将人口分为特权群体和非特权群体。然后,您使用一些度量标准(例如准确性、FPR、FNR)来比较各组。我们将会看到,最佳指标显示了谁从该模型中受益于

通常,一个模型的预测要么会给一个人带来好处,要么没有好处。例如,一个银行模型可以预测一个人不会拖欠贷款。这将导致获得贷款的收益。另一个好处的例子是得到了一份工作。对于我们的模型,我们将假设 Y = 1 会带来收益。也就是说,如果模型预测这个人的收入超过 5 万美元,他们就会以某种方式受益。

准确(性)

首先,让我们讨论一下准确性,以及为什么它不是公平的理想衡量标准。我们可以根据图 4 中的混淆矩阵进行精度计算。这是用于比较模型预测和实际目标变量的标准混淆矩阵。这里 Y = 1 是正预测,Y=0 是负预测。当我们计算其他公平性指标时,我们还将参考这个矩阵。

图 4:困惑矩阵(来源:作者)

查看图 5 ,您可以看到我们如何使用混淆矩阵来计算精确度。也就是说,准确度是在所有的观察中,真阴性和真阳性的数量。换句话说,准确性是正确预测的百分比。

图 5:精确度计算(来源:作者)

表 4 通过受保护的特征给出了我们模型的精确度。比率列给出了非特权(0)到特权(1)的精确度。对于这两个受保护的功能,您可以看到,非特权组的准确性实际上更高。这些结果可能会误导你,让你认为这种模式是在惠及无特权群体。

表 4:受保护特征的模型精度(来源:作者)

问题是准确性可能会掩盖模型的结果。例如,不正确的肯定预测(FP)会降低准确性。然而,这个人仍然会从这个预测中受益。例如,即使预测是错误的,他们仍然会得到贷款或工作机会。

机会均等(真实阳性率)

为了更好地捕捉模型的好处,我们可以使用真实正利率(TPR)。你可以在图 6 中看到我们是如何计算 TPR 的。分母是实际阳性数。分子是正确预测阳性的数量。换句话说,TPR 是被正确预测为阳性的实际阳性的百分比。

图 6: TPR 计算(来源:作者)

记住,我们假设积极的预测会带来一些好处。这意味着分母可以看作是应该从模型中受益的人数。分子是应该并且已经受益的人数。所以 TPR 可以解释为从该模型中正当获益的人的百分比。

例如,以一个贷款模型为例,其中 Y=1 表示客户没有违约。分母是没有违约的人数。分子是没有违约的数字,我们预测他们不会违约。这意味着 TPR 是我们贷款给的好客户的百分比。对于招聘模型,它将被解释为收到工作邀请的优秀候选人的百分比。

表 5 给出了我们模型的 TPR。同样,该比率给出了非特权(0)对特权(1)的 TPR。与准确性相比,您可以看到非特权群体的 TPR 较低。这表明,较少比例的弱势群体从这一模式中受益。也就是说,高收入人群中被正确预测为高收入的比例较小。

表 5:受保护特征的 TPR(来源:作者)

与流行性一样,我们可以通过在受保护特征的交叉点找到 TPR 来更进一步。您可以在表 6 中看到这些值。请注意,当这个人同时属于两个非特权群体时,TPR 甚至更低。事实上,白人男性的总生育率比非白人女性高 50%以上。

表 6:受保护特征交集的 TPR(来源:作者)

使用 TPRs 让我们在等式 1 中首次定义公平。在机会均等下,如果特权群体和非特权群体的 TPR 相等,我们认为模型是公平的。在实践中,我们会给统计不确定性留有余地。我们可以要求差值小于某个截止值(等式 2 )。在我们的分析中,我们采用了比率。在这种情况下,我们要求比率大于某个截止值(等式 3 )。这确保了非特权群体的 TPR 不会明显小于特权群体。

图 7:平等机会的定义(来源:作者)

问题是我们应该使用什么截止值?这个问题实际上没有好的答案。这取决于你的行业和应用。如果你的模型有重大影响,比如抵押贷款申请,你将需要一个更严格的截止日期。截止时间甚至可以由法律来定义。无论哪种方式,在衡量公平性之前,定义临界值是很重要的。

假阴性率

在某些情况下,您可能想要捕捉模型的负面结果。您可以使用图 8 中的 FNR 来完成此操作。同样,分母给出了实际阳性的数量。除了现在我们用错误预测的负数作为分子。换句话说,FNR 是被错误预测为阴性的实际阳性的百分比。

图 8: FNR 定义(来源:作者)

FNR 可以解释为错误地没有从模型中获益的人的百分比。例如,应该有没有获得贷款的客户的百分比。对于我们的模型,它是被预测为低收入的高收入者的百分比。

您可以在表 7 中看到我们模型的 FNR。现在,非特权群体的净生育率更高。换句话说,更高比例的特权群体错误地没有受益。在这个意义上,我们有一个类似的结论,当使用 TPRs 与均衡的赔率。这种模式似乎对弱势群体不公平。

表 7:受保护特征的 FNR(来源:作者)

事实上,要求 fnr 平等会给我们带来与机会平等相同的定义。这是因为在等式 1 中可以看到线性关系。换句话说,平等的 TPR 意味着我们也有平等的 fnr。你应该记住,我们现在要求这个比率小于某个临界值(等式 2 )。

图 9:用 FNR 评估公平性(来源:作者)

使用 fnr 来定义平等机会似乎没有必要。然而,在某些情况下,使用负面结果来定义框架可以更好地说明你的观点。例如,假设我们建立一个模型来预测皮肤癌。FNR 将给出患有癌症但未被诊断为癌症的人的百分比。这些错误可能是致命的。最终,以这种方式构建公平可以更好地强调不公平模型的后果。

均等的赔率

另一种获取模型益处的方法是通过观察假阳性率(FPR)。如图 10 所示,分母为实际底片的数量。这意味着 TPR 是被错误预测为阳性的实际阴性的百分比。这可以解释为从模型中不正当获益的人的百分比。例如,它将是收到工作邀请的不合格人员的百分比。

图 10: FPR 计算(来源:作者)

在我们的模型中,FPR 会给出被预测为高收入的低收入者的数量。你可以在表 8 中看到这些数值。同样,特权群体的税率更高。这告诉我们,更高比例的特权群体错误地从该模式中获益

表 8:受保护特征的 FPR(来源:作者)

这就引出了公平的第二个定义,均等几率。与平等机会一样,这一定义要求贸易保护率是平等的。现在我们还要求 FPR 相等。这意味着均等的赔率可以被认为是公平的更严格定义。同样有道理的是,一个模型要公平,整体利益应该是平等的。这是一个相似比例的群体应该合法和非法受益。

图 11:均等赔率定义(来源:作者)

均等赔率的一个优点是,我们如何定义目标变量并不重要。假设 Y = 0 会带来收益。在这种情况下,TPR 和 FPR 互换的解释。TPR 现在获得了非法利益,而 FPR 现在获得了合法利益。均等赔率已经使用了这两个比率,因此解释保持不变。相比之下,对平等机会的解释发生了变化,因为它只考虑 TPR。

完全不同的影响

我们对公平的最后一个定义是不同的影响 (DI)。我们首先计算图 12 中的购买力平价率。这是被正确(TP)或错误(FP)预测为阳性的人的百分比。我们可以将此解释为将从模型中受益的人的百分比。

图 12:购买力平价计算(来源:作者)

对于我们的模型,它是我们预测的高收入人群的百分比。您可以在表 9 中看到这些值。这些数字再次表明,这种模式对弱势群体不公平。也就是说,他们中受益于这种模式的比例较小。虽然,当解释这些值时,我们应该考虑这个定义的一个缺点。我们将在本节的最后讨论这一点。

表 9:不同的影响(来源:作者)

根据 DI ,如果我们有相等的购买力平价率,我们认为模型是公平的(等式 1 )。同样,在实践中,我们使用一个截止值来给出一些余地。这个定义应该代表不同影响的法律概念。在美国,有一个将临界值设定为 0.8(T21)的法律先例。即非特权群体的购买力平价不得低于特权群体的购买力平价的 80%。

图 13:不同的影响定义(来源:作者)

DI 的问题在于它没有考虑地面的真实情况。回想一下探索性分析中的流行值。我们看到这些是倾斜的。特权群体的价值更高。对于一个完全精确的模型,我们不会有假阳性。这意味着流行率将与不同的影响率相同。换句话说,即使对于一个完全精确的模型,我们可能仍然有一个低的不同影响比。

在某些情况下,期待相同的患病率或 DI 可能是有意义的。例如,我们会期望一个模型预测同等比例的男性和女性是某个工作的高质量候选人。在其他情况下,没有意义。例如,肤色较浅的人更容易患皮肤癌。我们预计肤色较浅的人患皮肤癌的几率较高。在这种情况下,低 DI 比率并不表示模型不公平。

公平性定义代码

我们使用函数【公平性 _ 度量】来得到上面所有的结果。这需要一个带有实际值(y)和预测目标值(y_pred)的数据帧。它使用这些来创建一个混淆矩阵(第 5 行)。这与我们在图 4 中看到的 4 个值相同。我们得到这 4 个值(第 6 行),并使用它们来计算公平性度量(第 8–13 行)。然后,我们将这些指标作为数组返回(第 15 行)。

您可以在下面看到我们如何使用这个函数来实现竞争保护功能。我们首先将人口的子群传递给公平性度量函数具体来说,我们得到特权(第 2 行)和非特权(第 3 行)组的指标。然后,我们可以获得非特权与特权指标的比率(第 6 行)。

为什么我们的模型有偏差?

基于对公平的不同定义,我们发现我们的模型对弱势群体是不公平的。然而,这些定义并没有告诉我们为什么我们的模型是不公平的。为此,我们需要做进一步的分析。一个好的起点是回顾我们最初的探索性分析。

例如,利用相互信息,我们发现婚姻状况是性别的潜在代理变量。通过查看表 10 中的细分,我们可以开始理解这是为什么。记住,婚姻状况= 1 表示这个人已经结婚了。我们可以看到,62%的男性已婚。而人口中只有 15%的女性已婚。

表 10:按性别和婚姻状况分列的人口(资料来源:作者)

表 11 中,我们可以看到已婚人群的患病率比高 6 倍。模型在进行预测时将使用这种关系。也就是说,它更有可能预测那些收入超过 5 万美元的已婚人士。问题是,正如我们在上面看到的,大多数已婚人士将是男性。换句话说,女性不太可能结婚,因此模型不太可能预测她们的收入超过 5 万美元。

表 11:按婚姻状况分列的流行率(资料来源:作者)

最后,还有更多的工作要做,以充分解释为什么模型是不公平的。这样做的时候,我们需要考虑所有不公平的潜在原因。我们在这篇文章中已经提到了一些。你也可以在下面的第一篇文章中深入了解它们。下一步是纠正不公平。在下面的第二篇文章中,我们将探讨定量和非定量方法。

****

我希望这篇文章对你有帮助!如果你想看更多,你可以成为我的 推荐会员 来支持我。你可以访问 medium 上的所有文章,我可以得到你的部分费用。

https://conorosullyds.medium.com/membership

图像来源

所有图片都是我自己的或从www.flaticon.com获得的。在后者的情况下,我拥有他们的保费计划中定义的“完全许可”。

数据集

Kohavi 和 b . Barry Becker,(1996 年),成人数据集, 加州欧文:加州大学信息与计算机科学学院(许可证:CC0:公共领域)https://archive.ics.uci.edu/ml/datasets/adult

参考

Pessach,d .和 Shmueli,e .(2020),算法公平性。https://arxiv.org/abs/2001.09784

Mehrabi,n .、Morstatter,f .、Saxena,n .、Lerman,k .和 Galstyan,A .,(2021),关于机器学习中的偏见和公平的调查。https://arxiv.org/abs/1908.09635T2

Hardt,e . Price 和 and Srebro,2016 年。监督学习中的机会均等。https://proceedings . neur IPS . cc/paper/2016/file/9d 2682367 c 3935 defcb 1 f 9 e 247 a 97 c 0d-paper . pdf

Besse,p .,del Barrio,e .,Gordaliza,p .,Loubes,J.M .和 Risser,l .,2021 年。通过统计奇偶性的棱镜调查机器学习中的偏差。https://arxiv.org/abs/2003.14263****

用 Python 分析 Strava 数据

原文:https://towardsdatascience.com/analysing-strava-data-with-python-b8a5badb019f

使用 Python 分析 Strava 数据的分步指南

几年前,我通过分析我的 Strava 数据自学了如何使用 R。这是一种有趣的学习方式,结合了我的两大爱好——数据和跑步(一般来说是运动)。

现在,我在用 Python 做同样的事情。在这篇文章中,我将分享我在对我的 Strava 数据进行一些数据争论和探索性数据分析(EDA)时采取的步骤,同时希望从中得出一些有趣的见解,并为其他 Python 初学者分享有用的技巧。我相对来说是 Python 的新手,但是我发现记录过程有助于我学习,也可能帮助你!

下载你的 Strava 数据

首先,我们需要获取数据集。我从 Strava 网站下载了我的 CSV 格式的数据,但是你也可以直接连接到 Strava API

要从 Strava 网站获取数据,请将鼠标悬停在页面右上角的图标上,导航至“设置”。导航到“我的帐户”,点击页面底部的“开始”。

作者提供的图片—除非另有说明,所有图片均由作者创作

在下一页,您将看到三个选项。在选项 2“下载请求”下,点击“请求存档”。不久之后(通常不到一个小时,取决于存档的大小),与您的 Strava 帐户相关的电子邮件将收到您的 zip 文件格式的存档。您想要的文件将被称为 activities.csv,我倾向于忽略那里的其余文件。

导航到您的 Jupyter 笔记本,点击“查看”下方的“上传文件”按钮上传 CSV。

现在,您应该可以在 Jupyter 笔记本左侧的文件浏览器中看到您的 CSV 文件。

导入库

现在我们需要下载将用于分析的库。

import pandas as pd 
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from datetime import datetime as dt

接下来,我们需要使用 Pandas 库将上传到 Jupyter 中的 CSV 转换为表格格式,Pandas 库是一个非常强大和流行的数据分析和操作框架。

df = pd.read_csv('strava_oct_22.csv') #read in csv
df.columns=df.columns.str.lower() #change columns to lower case

数据角力

根据过去的经验,我知道从 Strava 下载的活动包含了一大堆无意义和不相关的领域。因此,我想在开始之前稍微清理一下数据集。

以下是几个帮助您了解数据集的函数:

df.shape(818, 84)

shape 函数告诉您数据集中的行数和列数。我可以看到我有 818 行数据(每行对于一个单独的活动是唯一的)和 84 列(也称为变量或特性)。我知道这些列中的大部分对我来说都没用,因为它们要么是 NaN 值(不是数字),要么就是没用,但是为了确认我们可以使用。head()函数,它返回数据集中每一列的前 5 个值。

我们也可以使用。info()返回数据集中变量的所有列标题。

在上面的输出中,您还可以看到每个变量下面的“非空计数”,这可以让您了解该变量填充了多少行。让我们通过只选择我们认为相关的列来消除不必要的干扰。

 #Create new dataframe with only columns I care about
cols = ['activity id', 'activity date', 'activity type', 'elapsed time', 'moving time', 'distance',   
         'max heart rate', 'elevation gain', 'max speed', 'calories'
       ]
df = df[cols] 
df

这看起来更好,但数据集缺少一些关键变量,我想在我的分析中包括这些变量,默认情况下它们不是提取的一部分,例如平均速度和每小时公里数。因此,我需要自己计算它们。在此之前,我需要仔细检查新数据框中的数据类型,以确保每个字段都是我需要的格式。我们可以利用。为此,请键入。

df.dtypes

我可以看到数据集中的大多数字段都是数值(int64 是一个数值,float64 也是,但有小数)。然而,我可以看到活动日期是一个对象,当我开始查看时间序列分析时,我需要将它转换为日期数据类型。类似地,距离是一个对象,我需要将它转换成一个数值。

我可以使用 datetime 库将我的活动日期从 object 转换为 date 数据类型。我们还可以从活动日期创建一些额外的变量,即提取月、年和时间,我们将在后面的分析中使用这些变量。

#Break date into start time and date
df['activity_date'] = pd.to_datetime(df['activity date'])
df['start_time'] = df['activity_date'].dt.time
df['start_date_local'] = df['activity_date'].dt.date
df['month'] = df['activity_date'].dt.month_name()
df['year'] = df['activity_date'].dt.year
df['year'] = (df['year']).astype(np.object) #change year from numeric to object
df['dayofyear'] = df['activity_date'].dt.dayofyear
df['dayofyear'] = pd.to_numeric(df['dayofyear'])
df.head(3)

接下来,我们需要使用 Pandas 库中的 to_numeric 函数将距离转换为数值。这将允许我们有效地创建分析所需的新变量。

#convert distance from object to numeric
df['distance'] = pd.to_numeric(df['distance'], errors = 'coerce')

现在距离是一个数值,我可以创建一些附加变量来包含在我的数据框中。

#Create extra columns to create metrics which aren't in the dataset already
df['elapsed minutes'] = df['elapsed time'] /60 
df['km per hour'] = df['distance'] / (df['elapsed minutes'] / 60)
df['avg pace'] = df['elapsed minutes'] / df['distance']

因为我已经添加和修改了一些变量,所以让我们使用。再次键入数据以检查数据帧的格式是否正确。

那看起来好多了。最后,在我们进入 EDA 之前,我希望我的数据集只包含运行,因为我知道我的大多数活动都是这种类型的。为了确认这一点,我可以使用。value_counts()。

df['activity type'].value_counts()

如你所见,我的大部分活动都是跑步,所以我将创建一个名为“跑步”的新数据框架,并专注于此。我还知道数据中有一些错误的数据条目,可能是我忘记停表了,或者是 Strava 崩溃了,所以我也要过滤掉一些极端的结果。

runs = df[df['activity type'] == 'Run']
runs = runs[runs['distance'] <= 500]
runs = runs[runs['elevation gain'] <= 750]
runs = runs[runs['elapsed minutes'] <= 300]
runs = runs[runs['year'] >= 2018]

探索性数据分析

我们现在有了一个干净的数据集,我们可以开始可视化并从中提取有趣的见解。在 EDA 中,一个好的第一步是创建一个 pairs 图,它可以让你快速地看到单个变量的分布和两个变量之间的关系。这是确定后续分析趋势的一个很好的方法。如果你的数据集中有很多变量,这可能会变得混乱,所以我将选择几个作为重点。

pp_df = runs[['distance', 'elevation gain', 'km per hour', 'max heart rate', 'calories']]
sns.pairplot(pp_df);

这一行代码非常强大,我们已经可以从中得出一些有用的见解。对角线上的直方图允许我们看到单个变量的分布,而上下三角形上的散点图显示了两个变量之间的关系(或缺乏关系)。我能看出距离和卡路里之间是正相关的。我可以在距离直方图上看到有一个左偏斜,这意味着我跑的距离更短。有趣的是,卡路里与每小时公里数和最大心率成反比——让自己更努力并不一定意味着失去更多卡路里。

pairs 图是一种很好的可视化数据的方式,但是您可能希望查看数据集的一些汇总统计数据,以了解平均值、中值、标准偏差等。为此,请使用。描述功能。

runs.describe().round(0)

我真的很喜欢描述功能,这是一种快速获取数据摘要的方法。当我看到上面的输出时,我的第一反应是惊讶于距离,我跑步的中位数(或第 50 百分位)只有 6 公里!但话说回来,我想我只是在去年左右才加大了距离。EDA 的美妙之处在于,当您对数据进行初步调查时,您能够发现模式、发现异常、测试假设和检查假设,因此,让我们更深入地了解距离变量。

我将使用一个箱线图来可视化一年中距离的分布,看看我关于去年我增加了距离的假设是否正确。箱线图是显示数据的可视化摘要的一种很好的方式,使我们能够识别平均值、数据集的分散性和偏斜的迹象。

fig, ax = plt.subplots()
sns.set(style="whitegrid", font_scale=1)
sns.boxplot(x="year", y="distance", hue="year", data=runs)
ax.legend_.remove()
plt.gcf().set_size_inches(9, 6)

正如我所想,在过去的几年里,我的跑步距离确实增加了,从 2018 年到 2022 年,中值距离每年都略有增加,但增加幅度更大。

让我们按月划分年份,看看一年中所走的距离是如何变化的。我们可以通过使用 seaborn 库中的条形图来实现这一点。

sns.set_style('white')
sns.barplot(x='month', y='distance', data=runs, hue='year', ci=None, estimator=np.sum, palette = 'hot',
           order =["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"])
plt.gcf().set_size_inches(17, 6)
plt.legend(loc='upper center')
#plt.legend(bbox_to_anchor=(1.05, 1), loc='upper right', borderaxespad=0)
;

从上面的柱状图可以清楚地看出,我的距离是在夏天的几个月里下降的。以 2022 年为例,在 2022 年 1 月、2 月和 3 月,我跑的距离超过 150 公里,明显超过我在 4 月至 8 月跑的距离。这种趋势在其他年份也很明显。

让我们创建一个名为 season 的新变量,方法是使用。isin()。

runs['season'] = 'unknown'
runs.loc[(runs["month"].isin(["March", "April", "May"])), 'season'] = 'Spring'
runs.loc[(runs["month"].isin(["June", "July", "August"])), 'season'] = 'Summer'
runs.loc[(runs["month"].isin(["September", "October", "November"])), 'season'] = 'Autumn'
runs.loc[(runs["month"].isin(["December", "January", "February"])), 'season'] = 'Winter'

我们现在可以创建另一个箱线图来显示季节距离。

ax = sns.boxplot(x="season", y="distance", palette="Set2",
                    data=runs,
                    order =["Spring", 'Summer', 'Autumn', 'Winter'])
plt.gcf().set_size_inches(9, 7)

对于那些不熟悉箱线图的人来说,粗线代表中值,方框代表四分位数范围(数据的中间 50%),下面和上面的线代表最小值和最大值,黑点代表异常值。我发现观察我的行为如何随季节变化真的很有趣,看起来我真的不喜欢长时间的夏季跑步,我 75%的跑步都在 7k 以下。

结束注释

在 Python 的介绍中,我们已经介绍了很多,包括如何开始将文件读入 pandas,清理数据集以优化性能,以及创建额外的变量以包含在我们的分析中。为了更好地理解我们的数据,我们生成了一些汇总统计数据,并开始使用 matplotlib 和 seaborn 可视化我们的数据集。

现在数据集已经清理干净,我们有了更好的理解。我期待着对数据进行进一步的分析,并引入一些数据科学技术。例如,我计划建立一个线性回归模型来预测我的下一个 5 公里时间,这取决于一些变量,如今天是什么日子/季节/时间,或者路线的海拔是多少,如果我包括一些天气数据,甚至是温度和风速。

感谢阅读,直到下一篇!

posted @ 2024-10-18 09:27  绝不原创的飞龙  阅读(355)  评论(0)    收藏  举报