TowardsDataScience-博客中文翻译-2021-九-

龙哥盟 / 2024-10-21 / 原文

TowardsDataScience 博客中文翻译 2021(九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

数据科学家对微服务的介绍

原文:https://towardsdatascience.com/a-data-scientists-introduction-to-microservices-7772d356fe4d?source=collection_archive---------31-----------------------

作者图片

相信我,数据科学家们,你们需要它!

如果你像我一样是一名数据科学家,需要开发以最终用户为中心的解决方案,你的产品/项目经理可能会要求你演示你的解决方案,因为它可以在生产环境中工作。您需要设计一个 POC(概念验证),从用户的角度演示工作流程。

为此,您需要对微服务有所了解。为了让自己有一个基本的了解,我参加了 Udacity 的免费课程。我总结了我从课程中学到的东西,以供数据科学家同仁参考。

微服务架构:意味着什么?

微服务架构一词指的是任何软件设计模式,即模块化易于部署独立扩展

一个应用程序是由一组开发人员创建的。开发人员引入的错误可能会破坏整个代码,导致软件发布延迟。为了防止这种情况,必须有一个适当的系统,让每个开发人员独立地将他们的代码投入生产。这确保了代码的轻松部署。

这使得代码模块化,因为由一个开发者创建的应用程序部分独立于由另一个开发者创建的另一部分。

但是,假设你的 app 有多个这样的可移动部分(姑且称每个部分为服务)。为了让应用程序按预期工作,每个部分都需要与其他部分进行通信。所以你看,部署的复杂性随着移动部件或服务数量的增加而增加。为此,我们需要一些工具来协调服务。进入 Kubernetes 等平台!

组件的马赛克(图片由作者提供)

微服务架构解决了哪些问题?

微服务架构用于应用程序/产品整体维护比较麻烦的地方。微服务是单独开发和部署的模块化组件。

然而,这些体系结构都有性能成本。与对调用的响应非常快速的整体架构(与微服务截然相反,其中代码的所有部分都紧密耦合)不同,基于微服务的架构需要从一个部分向另一个部分发送一组调用来获得所需的响应,这增加了响应时间的开销。

Docker 和 Kubernetes:

Docker 和 Kubernetes 已经成为 DevOps(开发运营)领域的流行语,特别是随着持续开发和持续集成(CD 和 CI)的需求越来越强。

CD 和 CI 对软件生命周期的共同意义是,无论对代码做什么更改,都应该集成到生产环境中,而不要关闭环境。

docker 从您构建的应用程序中创建一个容器。它没有受到周围发生的任何变化的影响。因此,如果您有多个应用程序相互通信,那么如果它们被转换成单独的容器,每个应用程序都可以独立维护。

Kubernetes 是把一切粘在一起的胶水。它协调容器之间的“对话”。

结论

对于那些没有开发团队来实现他们的解决方案的数据科学家来说,用 Linux 的基础知识武装自己(Docker 与 Linux 命令一起工作),Docker 和 Kubernetes 将带你走上一段漫长的旅程。

在 ggplot2 中使用一些高级可视化的数据讲述项目

原文:https://towardsdatascience.com/a-data-storytelling-project-with-some-advanced-visualization-in-ggplot2-fb60dfc56dde?source=collection_archive---------16-----------------------

由 Ethan Hu 在 Unsplash 上拍摄的照片

ggplot2 中一些实用的数据准备和有用的绘图技巧

我认为,数据可视化是与人交流数据的最有效方式。因此,对于每个数据科学家或分析师来说,学习可视化非常重要。本文将分享一些 R 中的可视化。我将使用 R 中的 ggplot2 库,这是一个丰富的库。ggplot2 库中包含了许多可视化技术。我自己也在尽可能多的学习。

几周前,我不得不使用这个公共数据集,并使用一些可视化技术进行总结。但是为了准备用于可视化的数据,也使用了一些数据分析技术。

这些是这个项目所必需的库:

library(ggplot2)
library(psych)
library(dplyr)
library(tidyr)
library(ggpubr)
library(usmap)

在这里,我首先导入数据集。该数据集显示了美国各州的农产品出口数据。

us_ag = read.csv("[https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv](https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv)")

以下是该数据集的列:

names(us_ag)

输出:

[1] "code"   "state"    "category"  "total.exports" "beef"         
 [6] "pork"  "poultry"  "dairy"  "fruits.fresh"  "fruits.proc"  
[11] "total.fruits" "veggies.fresh" "veggies.proc" "total.veggies" "corn"         
[16] "wheat" "cotton"

让我们开始真正的乐趣吧!

我认为看看农产品出口之间是否有关联会很有趣。我将制作一个仅包含农产品的较小数据集,并制作一个相关图。

corr_data = us_ag[, c("pork", "beef", "poultry", "dairy", "total.fruits", 
"total.veggies", "corn", "wheat", "cotton")]

使用此 corr_data 数据集绘制相关图:

corPlot(corr_data, cex = 0.8, main="Corrlation Between Variables",
        las = 2)

作者图片

这里显示了一些有趣的相关性。出口大量玉米的州也出口大量猪肉。相关系数为 0.77。家禽和棉花也显示出 0.61 的显著相关性。

下一个图将是一个发散条形图,显示该州的总出口是低于平均水平还是高于平均水平。

在此之前,需要做一些数据准备。首先,我需要标准化总出口列。

us_ag$exports_z = round((us_ag$total.exports - mean(us_ag$total.exports))/sd(us_ag$total.exports), 2)

现在,我将这些数据标记为“低于平均水平”和“高于平均水平”。如果 exports_z 值小于零,它将被视为低于平均值,否则将被视为高于平均值。然后,我们将根据 exports_z 值对数据集进行排序。

us_ag$export_level = ifelse(us_ag$exports_z < 0, "below", "above")
us_ag_sorted = us_ag[order(us_ag$exports_z),]us_ag_sorted$state = factor(us_ag_sorted$state, levels=us_ag_sorted$state)

为发散图准备数据。下面是制作发散图的代码块:

{r fig.height=11, fig.width=6}
ggplot(us_ag_sorted, aes(x = state, y = exports_z, label=exports_z)) + 
  geom_point(stat = 'identity', aes(col=export_level), size=7) + 
  scale_color_manual(name="Exports",
                     labels = c("Above Average", "Below Average"),
                     values = c("above"="#00ba38", "below"="#f8766d"))+
  geom_text(color="white", size = 2) +
  labs(title="Diverging Bar Plot Showing Exports Level",
       subtitle = "Normalized total export",
       x = "Normalized Total Exports",
       y = "State") +
  ylim(-2, 5)+
  coord_flip()

剧情是这样的:

作者图片

如需详细信息,我还想看看每个州的每个产品的出口。产品这么多。如果我想把它们都放在同一个情节中,这不是很有用。所以,我把它们分成了几个情节。最终,我会把它们结合起来。

第一个地块将只包括牛肉、猪肉和家禽。代码如下:

{r fig.height=4, fig.width=12}
us_ag %>% select(state, beef, pork, poultry) %>%
  pivot_longer(., cols = c(beef, pork, poultry),
               values_to="Val") %>%
  ggplot(aes(x = state, y = Val, color=name, alpha = 0.7))+
  geom_point(size = 4) + 
  scale_color_manual(values = c("beef" = "black", "pork" = "red", "poultry" = "green")) +
  geom_line(aes(group=name)) + 
  theme(axis.text.x = element_text(angle = 90)) + 
  guides(alpha=FALSE)+labs(x = "State",
                           y = "Exports",
                           title = "Beef, Pork, and Poultry Exports of States"
                           )

输出图:

作者图片

如你所见,爱荷华州是最大的猪肉出口地,德克萨斯州是最大的牛肉出口地。

乳制品、全部水果和全部蔬菜包含在下一个地块中:

{r fig.height= 4, fig.width=14}
us_ag %>% select(state, total.fruits, total.veggies, dairy) %>%
  pivot_longer(., cols = c(total.fruits, total.veggies, dairy),
               values_to="Val") %>%
  ggplot(aes(state, Val, fill = name)) + geom_col(width = 0.9)+
  theme(axis.text.x = element_text(angle = 90))

剧情是这样的:

作者图片

加州出口的水果、蔬菜和奶制品数量最多,远远超过其他任何一个州。

最后,玉米、棉花和小麦:

{r fig.height= 4, fig.width=14}
us_ag %>% select(state, corn, cotton, wheat) %>%
  pivot_longer(., cols = c(corn, cotton, wheat),
               values_to="Val") %>%
  ggplot(aes(state, Val, fill = name)) + geom_col(width = 0.9)+
  theme(axis.text.x = element_text(angle = 90))+
  labs(x = "State",
       y = "Exports",
       title = "Corn, Cotton, and Wheat Exports of States")

输出图在这里:

作者图片

得克萨斯是最大的棉花出口国。当我住在德克萨斯州的时候,我参观了很多棉花田,爱荷华州是最大的玉米出口地。其他几个州也出口大量玉米。如图所示。

如果我把这三个图放在一起,在演示中会看起来很好,并且会覆盖一页。下面是将它们结合在一起的代码:

{r fig.height=10, fig.width=14}
p1 = us_ag %>% select(state, beef, pork, poultry) %>%
  pivot_longer(., cols = c(beef, pork, poultry),
               values_to="Val") %>%
  ggplot(aes(x = state, y = Val, color=name, alpha = 0.7))+
  geom_point(size = 4) + 
  scale_color_manual(values = c("beef" = "black", "pork" = "red", "poultry" = "green")) +
  geom_line(aes(group=name)) + 
  theme(axis.text.x = element_text(angle = 90)) + 
  guides(alpha="none")+
  labs(x = "State",
       y = "Exports",
       title = "Beef, Pork, and Poultry Exports of States")p2 = us_ag %>% select(state, total.fruits, total.veggies, dairy) %>%
  pivot_longer(., cols = c(total.fruits, total.veggies, dairy),
               values_to="Val") %>%
  ggplot(aes(state, Val, fill = name)) + geom_col(width = 0.9)+
  theme(axis.text.x = element_text(angle = 90))+
  labs(x = "State",
       y = "Exports",
       title = "Dairy, Fruits, and Vegetables Exports of States")p3 = us_ag %>% select(state, corn, cotton, wheat) %>%
  pivot_longer(., cols = c(corn, cotton, wheat),
               values_to="Val") %>%
  ggplot(aes(state, Val, fill = name)) + geom_col(width = 0.9)+
  theme(axis.text.x = element_text(angle = 90))+
  labs(x = "State",
       y = "Exports",
       title = "Corn, Cotton, and Wheat Exports of States")ggarrange(p1, p2, p3, nrow=3)

下面是输出图:

作者图片

对于下一个图,我决定在美国地图上绘制最好的出口产品。为此,我需要经纬度数据。该数据集本身不包括任何后期数据。所以,我简单地从 Kaggle 下载了一些经纬度数据。你可以在这里找到最近的数据。请放心下载使用。

首先,我将把这个纬度较长的数据与主数据集合并:

latlong = read.csv("statelatlong.csv")
latlong = rename(latlong, c("code" = "State"))
latlong = subset(latlong, select=-c(City))
latlong1 = latlong[, c(3, 2, 1)]
us_ag_ll = merge(latlong, us_ag, by = "code")

这个 us_ag_ll 数据集包括所有的列和经度数据。

我们需要找出哪个产品是每个州出口最多的产品。为此,我将创建一个仅包含所有农产品的数据集:

df = us_ag[, c("beef", "pork", "poultry", "dairy", "total.fruits", "total.veggies", "corn", "wheat", "cotton")]

下面的代码块找出最大值和具有最大导出值的产品的名称:

df$max_val = apply(X=df, MARGIN = 1, FUN = max)
df$maxExportItem= colnames(df)[apply(df,1,which.max)]

这个 maxExportItem 列需要包含在主数据集 us_ag_ll 中。

us_ag_ll$max_export_item = df$maxExportItem
us_ag_ll$max_val = df$max_val

我们需要转换纬度和经度数据,以便能够在地图中使用它们。

transformed = usmap_transform(us_ag_ll[, c("Longitude", "Latitude")])

如果需要,请检查转换后的数据集。

“经度”和“纬度”列已被转换,在转换后的数据集中创建了两个名为“经度. 1”和“纬度. 1”的列。

我们将使用“经度. 1”和“纬度. 1”来绘图。

我一直认为在地图上放一些值是个好主意。我会把每个州的最大出口项目和出口金额。所以,我们应该准备好数据。

us_ag_ll$Max_Export_Val = paste(us_ag_ll$max_export_item, us_ag_ll$max_val, sep="-")

所有的数据准备都做好了!现在让我们开始策划吧!颜色梯度将代表总出口。地图中的值将显示每个州的最大出口项目和最大出口项目的出口值。

{r fig.height=11}
plot_usmap(data = us_ag_ll, values = "total.exports", color = "blue")+
  geom_text(data=us_ag_ll, aes(x = transformed$Longitude.1,
                                y = transformed$Latitude.1, 
                                label = Max_Export_Val), size=3.5, color = "black")+
  scale_fill_continuous(low = "white", high = "blue", name = "Total Exports", label = scales::comma) + 
  labs(title = "Total Exports in Color in Each State", subtitle = "Top Exports Items and Export Values of Each State")+
  theme(legend.position = "bottom", plot.title = element_text(size = 20, face = "bold"),
        plot.subtitle = element_text(size = 18))

输出:

作者图片

地图中值的位置可以通过一些更好的经纬度数据来改进。我用我在卡格尔找到的东西。

这就是我这篇文章想分享的全部内容!

结论:

有许多不同的方法可以处理数据集并将其可视化。如果你也像我一样是一个初学者,并试图提升你的技能,请尝试你自己的想法,如果你能以自己的方式改善这些情节,请随时通过评论部分的 GitHub 链接分享你的代码。

欢迎在推特上关注我,喜欢我的 T2 脸书页面。

一部有两个时间维度的三幕达克斯戏剧

原文:https://towardsdatascience.com/a-dax-drama-in-3-acts-with-two-time-dimensions-aa65abbf5f41?source=collection_archive---------12-----------------------

我的一个客户提出了一个看起来相对容易的请求,但我花了三天时间和很多精力去解决。我重新学习了一些关于达克斯的课程,我邀请你跟随我的三幕剧来解决问题。

engin akyurt 在 Unsplash 上拍摄的照片

搭建舞台

我的客户每天将销售文档的所有元数据加载到他的数据仓库数据库的快照表中。从每月的第一天到最后一天,每天都会覆盖快照数据。随着新月份的开始,新的月度快照开始,上个月不再更改。每个快照都有过去十年中创建的文档的元数据。这个表非常大,有超过 2 . 2 亿行,需要将近 11 GB 的 RAM,并且还在增长。

因为他们使用 SQL Server Analysis Services (SSAS)将数据存储在表格模型中,所以我可以在 Vertipaq Analyzer 中向您展示该表的大小:

有些数据是引用的。这些报价是企业要求的报告中有趣的部分。

他们需要查看一段时间内的报价计数和报价销售额,并且希望将这两个数字与 12 个月前的结果进行比较。

最大的问题是,需要从 12 个月前的快照中检索 12 个月前创建的报价数据。

用户应该能够用 Power BI 中的限幅器定义任意时间窗口。该报告必须使用相应的快照,自动调整 12 个月前相同时间窗口的检索。

很简单,不是吗?

还有一点复杂的是,他们想要区分公开报价、成功报价和失败报价。他们使用“提供状态”维度来区分每种状态。

现在,让我们拉开帷幕。

照片由格温·金在 Unsplash 上拍摄

第一个动作—检索当前期间

您可能已经注意到,我需要处理两个日期维度:

  • 在 SSAS 模型中,快照日期被命名为“时间”
  • 文档创建日期在 SSAS 模型中命名为“创建时间”
    我在这个表的日期列上使用了一个切片器,允许用户过滤报告中报价的创建日期

我现在不想透露多个日期维度的利弊。这是我需要面对的事实,我无法改变。

第一步是找到实际时间窗口的正确快照:

Last Snapshot by Creation Date = VAR LastCreationDate =
        MAX(‘Time Creation’[Date]) VAR GetSnapshot =
        CALCULATE(
            LASTNONBLANK(
                ‘Document’[Snapshot Date]
                ,SUM ( Document[SalesAmountNet] ) )
            ,’Document’[Snapshot Date] <= LastCreationDate
        )RETURN
    GetSnapshot

我将这个公式存储在 Power BI 文件的一个中间度量中,因为我在其他度量中需要这个值。

哦,我忘了说,所有需要的度量都是独一无二的,所以我直接在 PowerBI 文件中创建它们,它使用一个到 SSAS 模型的活动连接。

获取韩元报价的方法如下:

Quotes won =CALCULATE (
    SUM ( Document[SalesAmountNet] )
    ,FILTER ( ‘Offer Status’, ‘Offer Status’[Offer Status Number] IN {“2”, “11”, “13” } )
    ,FILTER(
        VALUES(‘Document’[Snapshot Date]),
        ‘Document’[Snapshot Date] = [Last Snapshot by Creation Date]
    )
)

我需要两个进一步的措施来获得开放和丢失的报价。这三种衡量标准的区别仅在于用于过滤数据的报价状态编号列表不同。我还需要另外三个度量来计算引用的数量。所以,我总共需要六个基本度量。

在这个测量中有一个小故障,这是我在验证结果时发现的。稍后我将向您展示这一点,在看到我如何解决其他问题后,这将更有意义。

第二步——找到 12 个月前的快照

计算所选创建日期的前一期是非常简单的。您需要使用 PARALLELPERIOD() 函数来回溯 12 个月:

VAR LastCreationDatePY =
    PARALLELPERIOD(‘Time Creation’[Date], -12, MONTH)

我可以使用这种机制来获取匹配的快照。

现在戏剧开始了。

我的第一次尝试是这样的:

Last Snapshot by Creation Date (PY) = VAR MaxLastCreationDatePY = EOMONTH(
                                LASTDATE(‘Time Creation’[Date]), -6) VAR GetSnapshot =
        CALCULATE(
            MAX(
                ‘Document’[Snapshot Date])
                ,’Document’[Snapshot Date] <= MaxLastCreationDatePY
                ,ALL(‘Document’[Snapshot Date])
            )RETURN
    GetSnapshot

我的理由是:我喜欢过滤文档表上的快照日期。因此,我从这个列中移除过滤器并更换过滤器。

令人惊讶的是我总是得到一个空的结果。为什么?

我花了一些时间,在一位同事的帮助下,才意识到在切片器的“时间创建”表上有一个过滤器。我需要用 ALL(时间创建)从那里移除过滤器来操作过滤器。

工作 DAX 公式如下:

Last Snapshot by Creation Date (PY) = VAR MaxLastCreationDatePY = EOMONTH(
                               LASTDATE(‘Time Creation’[Date]), -12) VAR GetSnapshot =
        CALCULATE(
            MAX(
               ‘Document’[Snapshot Date])
            ,’Document’[Snapshot Date] <= MaxLastCreationDatePY
            ,ALL(‘Time’)
            ,ALL(‘Time Creation’)
        )RETURN
    GetSnapshot

我需要全部(“时间”),因为报告中的“时间”表也有一些外部影响。

**第一课:**不要忘记查看整张图片,并在正确的表格和列上操作过滤器。

第三步—使用正确的时间窗口计算正确快照上的数量

现在,我需要将正确的快照与文档创建日期的前一时期的时间窗口结合起来。

我一步一步地创建度量,直到得到有意义的结果。

这是我的第一个工作措施:

Quotes won (PY) = VAR LastCreationDatesPY =
        PARALLELPERIOD(‘Time Creation’[Date], -12, MONTH) VAR Result =
        CALCULATE (
            SUM ( Document[SalesAmountNet] )
            ,FILTER(‘Offer Status’,
            ‘Offer Status’[Offer Status Number] IN {“2”, “11”, “13” }
            )
            ,ALL ( ‘Time Creation’)
            ,LastCreationDatesPY
        )RETURN
    CALCULATE(
        Result
        ,FILTER(
            VALUES(‘Document’[Snapshot Date])
            ,’Document’[Snapshot Date] = [Last Snapshot by Creation Date (PY)]
        )
    )

我从这个措施中得到了一些有意义的结果,我认为我满足了要求。

但是,当我开始用来自源数据库的数据验证结果时,我注意到了显著的差异。有些事情还不太对劲。

我回到了绘图板。

经过几个小时的尝试和失败,我停下来休息了一会儿。

**第二课:**如果你陷入困境,走开,做些不同的事情

休息过后,我重新开始,尝试了一种完全不同的方法。

我使用 DAX Studio 编写了一个 DAX 查询并获得中间结果,我可以用源数据库验证这些结果。

**第三课:**走一步算一步。不要试图做一个巨大的飞跃,并期望一切都会顺利。

由在 Unsplash 上高清拍摄的历史

然后我意识到这个查询产生了正确的结果,我可以在 DAX 度量中使用这个查询。

这种方法是正确的选择,最终措施如下:

Quotes won (PY) = VAR LastCreationDatesPY =
        PARALLELPERIOD(‘Time Creation’[Date], -12, MONTH)RETURN
    SUMMARIZE(
        CALCULATETABLE(
            Document
            ,ALL(‘Time’)
            ,FILTER(
                VALUES(‘Time’[Date])
            ,’Time’[Date] = [Last Snapshot by Creation Date (PY)]
        )
        ,FILTER(‘Offer Status’,
            ‘Offer Status’[Offer Status Number] IN {“2”, “11”, “13” }
            )
        ,ALL(‘Time Creation’)
        ,LastCreationDatesPY
        )
    ,”Sum sales”, SUM ( Document[SalesAmountNet] )
    )

这项措施中有一些有趣的部分:

  • 按“时间”[日期]过滤:我需要过滤时间维度表上的数据,以获得正确的快照。如前所述,第一幕的小节出现了小故障。这里的诀窍是过滤“时间”[日期],而不是“文件”[快照日期]。
    过滤维度表总是比事实表更好的方法。它给了你更多的使用度量的灵活性,在我的例子中,它产生了正确的结果。
  • 对报价状态和时间创建的过滤也是如此。我只过滤维度表。
  • 不使用变量。
    这种方法是最大的游戏规则改变者。
    使用变量时应用过滤器是有区别的。简单地说,但不完全准确,过滤器不是“向后”应用于变量。当您需要在 DAX 度量中应用多个筛选器时,必须仔细测试筛选器是否按预期应用。你可以在这里阅读更多关于这个问题的内容:DAX(SQLBI)中的变量

第四课:记住基础知识。它们仍然适用,不管你认为你知道多少。

照片由JESHOOTS.COM在 Unsplash 上拍摄

顺便说一句。第一幕的正确步骤如下:

Quotes won = CALCULATE (
        [Sales (Document)]
        ,FILTER ( ‘Offer Status’, ‘Offer Status’[Offer Status Number] IN {“2”, “11”, “13” } )
        ,FILTER(
            VALUES(‘Time’[Date]),
            ‘Time’[Date] = [Last Snapshot by Creation Date]
            )
        )

收场白

除了第一课,我不再重复上面提到的四课。

如果您尽可能过滤维度表,那将是最好的。如果你不知道下面的维度建模是如何工作的,你可以找到一些有用的链接。

通常,我更喜欢只有一个日期表;正如我的文章中所讨论的,用扩展的日期表改进你的报告的 3 种方法。

我怀疑这个特定的案例可能更难用一个中央日期表来解决。我没有用另一个型号试过。所以,我可能错了。

但是只要想想一个度量中需要的user relationship()调用以及我在变量方面遇到的问题——如果没有必要,我不想尝试一下。

最后但同样重要的是:
**第五课:**对照源数据测试和验证你的结果,直到你知道一切都是正确的。

了解维度建模的链接:

数据建模最佳实践—第 1 部分 Power BI 和分析服务(YouTube)

维度数据建模逐步指南

尺寸建模示例(PDF)

维度建模技术(金博尔集团)

区块链人生活中的一天

原文:https://towardsdatascience.com/a-day-in-the-life-of-a-blockchain-eb352980ee16?source=collection_archive---------40-----------------------

…从数据科学家的角度来看

由 Zineb Fafa 在 Unsplash 拍摄的照片

正如我在之前的一篇文章中所讨论的那样,区块链产生了大量高质量的数据,数据科学家可以用这些数据来回答一系列具有学术和实践重要性的问题。特别令人感兴趣的是支持高吞吐量智能契约的区块链,它支持可扩展的分散式应用 (dApps)的开发和部署。

这样的区块链平台之一是 TRON 。它在游戏和金融相关应用的开发者中特别受欢迎。在这里,我将使用从 TRON 收集的事务数据来说明它们可以揭示的一些模式。本文是面向希望了解更多有关区块链数据及其分析方法的数据科学家的技术教程。所有的例子都是用 R 实现的。我们假设读者对区块链技术有很高的理解,并且熟悉 R 及其[tidyverse](https://www.tidyverse.org/)工具。这篇文章的代码可以在 Github 上找到。为了节省空间,我省略了用于生成大多数图表的代码。

免责声明 :本文任何部分均不构成理财建议。使用或依赖此处提供的材料完全由您自行承担风险和判断。我既不附属于也不支持此处提到的任何公司。提及这些公司及其产品仅用于说明目的。

本研究中使用的数据

与其他现象一样,人们可以在不同的时间尺度上研究区块链交易。涵盖更长时期的粒度数据往往描绘出一幅更加微妙的画面。然而,收集大量链上数据会产生巨大的技术开销(查询时间、存储和计算资源等)。).为简单起见,我们将使用覆盖一天数据的数据集,即 2021 年 7 月 2 日。没有理由选择这个特定的日期,这只是一个例子。

数据已通过函数[get_block_info()](https://levelup.gitconnected.com/how-to-query-blocks-on-the-tron-blockchain-2ee3c46156ec)从 R 包[tronr](https://levelup.gitconnected.com/introducing-tronr-an-r-package-to-explore-the-tron-blockchain-f0413f38b753)中收集。该函数获取感兴趣的块的编号,并以嵌套 tible 的形式返回数据。2021 年 7 月 2 日在 TRON 区块链上产生了 28,730 个区块,其数量从 31,570,498 到 31,599,227 不等。这些区块共包含 7,819,547 项交易。交易 ID ( tx_id)、执行给定交易的系统智能合约调用的类型(contract_type)以及该交易的发起者(from_address)和接收者(to_address)的账户地址存储在由get_block_info()返回的 tibble 的列表列tx中。该数据集中第一个数据块的数据如下所示:

require(dplyr)
require(tidyr)
require(tronr)
#> R toolbox to explore the TRON blockchain
#> Developed by Next Game Solutions (http://nextgamesolutions.com)block_data <- get_block_info(
    latest = FALSE, block_number = "31570498"
)glimpse(block_data)
#> Rows: 1
#> Columns: 11
#> $ request_time    <dttm> 2021-07-26 20:25:24
#> $ block_number    <chr> "31570498"
#> $ timestamp       <dttm> 2021-07-02
#> $ hash            <chr> "0000000001e1ba4267746fce622a90ba6a51f~
#> $ parent_hash     <chr> "0000000001e1ba4135f66566c15b648563d4b6~
#> $ tx_trie_root    <chr> "254pKSrGpmLT3y7xdFAaZfXR93ZrsKgRJoxZH~
#> $ confirmed       <lgl> TRUE
#> $ size            <int> 61119
#> $ witness_address <chr> "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH"
#> $ tx_count        <int> 200
#> $ tx              <list> [<tbl_df[200 x 4]>]block_data %>% select(tx) %>% unnest(cols = tx)
#> # A tibble: 200 x 4
#>    tx_id           contract_type   from_address    to_address    
#>    <chr>           <chr>           <chr>           <chr>         
#>  1 59ebe232ea5032~ VoteWitnessCon~ TXi4QTAWYGhF4Z~ TTcYhypP8m4ph~
#>  2 aff3e7fb7f277b~ TransferContra~ TRYavwpJnwhr9T~ TQMVcC2adh61t~
#>  3 8661b623b377a2~ TriggerSmartCo~ TRpzBAQHCVKHBW~ TBRs8xwajQVbD~
#>  4 ed437306b65880~ TransferAssetC~ TXAVuHVM1pBnZA~ TYBtHbJiQ2bDa~
#>  5 5a94ce1c7b0390~ TransferAssetC~ TAdqErGeD2CgTp~ TSnjNCyK3r58w~
#>  6 360999180f84eb~ TransferAssetC~ TAFpxz9pKyGDwr~ TPRiphADhwe1g~
#>  7 42a97206891117~ TransferAssetC~ TJABpJLWNXJ2xj~ TX8BumaUQ1bpR~
#>  8 baa45f5a4a18aa~ AccountCreateC~ TEoDHGSTu2Kh97~ TFLnMpKhGBUUo~
#>  9 614032934ce86c~ TransferContra~ TSWHGYtBwzpFXL~ TBy4dMk6nWhWV~
#> 10 01de787975ff49~ TransferContra~ TVU2khdDkUu67F~ TSEnu5xcNMyNi~
#> # ... with 190 more rows

整个数据集可以下载到 R 中,如下所示(这个 RDS 文件的大小超过 600 Mb,所以请耐心等待——这可能需要一些时间,取决于您的互联网连接的带宽):

dat <- readRDS(url("https://chilp.it/eee138c"))dat %>% dim()
#> [1] 28730    11names(dat)
#>  [1] "request_time"    "block_number"    "timestamp"      
#>  [4] "hash"            "parent_hash"     "tx_trie_root"   
#>  [7] "confirmed"       "size"            "witness_address"
#> [10] "tx_count"        "tx"

每 3 秒钟就有一个新的区块

块类似于分类账中的页面,这些逻辑单元用于分组和记录区块链在一定时期内发生的交易。多亏了授权利害关系证明共识机制,创区块链上的区块生成速度非常快——每 3 秒钟一次。然而,每 6 小时有 4 个维护周期,每个周期耗时 6 秒。因此,在我们的示例中,块之间的平均时间刚刚超过 3 秒:

dat$timestamp %>% diff() %>% mean()
#> Time difference of 3.00731 secs

在 TRON 中,有 27 个“见证人”,也称为“超级代表”(SR),即可以创建和确认新块的网络节点:

dat$witness_address %>% unique() %>% length()
#> [1] 27dat$witness_address %>% unique() %>% head()
#> [1] "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH"
#> [2] "TJBtdYunmQkeK5KninwgcjuK1RPDhyUWBZ"
#> [3] "TTjacDH5PL8hpWirqU7HQQNZDyF723PuCg"
#> [4] "TWkpg1ZQ4fTv7sj41zBUTMo1kuJEUWTere"
#> [5] "TCEo1hMAdaJrQmvnGTCcGT2LqrGU4N7Jqf"
#> [6] "TAAdjpNYfeJ2edcETNpad1QpQWJfyBdB9V"

在大多数情况下,可以在 TRONScan 网站上找到证人身份或其假名。例如,上面列出的六个地址属于以下实体:

  • TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH:币安跑马圈地
  • TJBtdYunmQkeK5KninwgcjuK1RPDhyUWBZ:京东投资
  • TTjacDH5PL8hpWirqU7HQQNZDyF723PuCg:新赌注
  • TWkpg1ZQ4fTv7sj41zBUTMo1kuJEUWTere : TRONLink
  • TCEo1hMAdaJrQmvnGTCcGT2LqrGU4N7Jqf:特龙斯坎
  • TAAdjpNYfeJ2edcETNpad1QpQWJfyBdB9V:蚂蚁投资集团

证人每天生成的区块总数变化不大,从 1050 到 1067,平均为 1064:

dat$witness_address %>% table() %>% range()
#> [1] 1050 1067dat$witness_address %>% table() %>% mean()
#> [1] 1064.074

每个区块平均包含 274 个交易

每个块的事务数量有一个稍微不对称的分布,有一个长的右尾巴(图 1)。平均而言,每个块包含 274 个事务。然而,一些区块有多达 1126 笔交易,而另一些则根本没有记录:

dat$tx_count %>% summary()
#> Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>  0.0   229.0   274.0   272.2   316.0  1126.0

图一。2021 年 7 月 2 日 TRON 区块链上每个区块的交易数分布。

虽然很少,“空”块对于创区块链来说是完全正常的。当事务太少而无法打包到一个块中时,会生成这些事件。

资产转移和智能合同触发是最普遍的交易类型

在创区块链上可以进行 30 多种交易。这些类型通过各自的“系统智能合同实现。在我们的数据集中,ca。所有交易的 65%与各种代币("TransferAssetContract")和 ca 的转移相关。13% —用特龙(TransferContract)的本币特龙尼斯 (TRX)转账。超过 19%的交易涉及触发其他智能合同(TriggerSmartContract),例如那些在分散应用中实现逻辑的合同。其他类型的交易(例如,创建新账户、代币等。)的频率要低得多:

dat %>%
  select(tx) %>%
  unnest(cols = c(tx)) %>%
  group_by(contract_type) %>%
  summarise(n = n()) %>%
  mutate(percent = round(n / sum(n) * 100, 3)) %>%
  arrange(-n)#> # A tibble: 13 x 3
#>    contract_type                         n percent
#>    <chr>                             <int>   <dbl>
#>  1 TransferAssetContract           5071888  64.9  
#>  2 TriggerSmartContract            1508562  19.3  
#>  3 TransferContract                1041006  13.3  
#>  4 AccountCreateContract            166234   2.13 
#>  5 FreezeBalanceContract             10321   0.132
#>  6 VoteWitnessContract                8643   0.111
#>  7 WithdrawBalanceContract            6857   0.088
#>  8 UnfreezeBalanceContract            5119   0.065
#>  9 CreateSmartContract                 829   0.011
#> 10 AccountPermissionUpdateContract      77   0.001
#> 11 AccountUpdateContract                 8   0    
#> 12 AssetIssueContract                    2   0    
#> 13 ParticipateAssetIssueContract         1   0

每个块的事务越多,块就越大

假设数据块用于“捆绑”一组连续的事务,那么自然会期望数据块大小(以 Mb 为单位)与此类事务的数量正相关。图 2 证实了这一点。

图二。每个块的事务数量和块大小之间的关系。

然而,这种关系的强度并不一致。当数据低于每块 90 笔交易时,关系是线性的,非常紧密。之后,相关性仍然是强的和正的,但是块大小方差增加了。此外,在 ca 上方的图上出现了两组独立的点。每个块 450 个事务。总体而言,图 2 显示,2021 年 7 月 2 日,区块链在几个不同的制度下运作。

区块链上的活动全天变化很大

每个区块的交易数量表现出适度的每日季节性,即从午夜到大约。09:00 ( UTC 时区,这里和其他地方),然后再次类似地缓慢下降。然而,全天有许多局部波峰和波谷(图 3)。

图 3。每个块中事务数量的时间变化。在本文的这里和其他地方,红线是一个基于 GAM 的平滑器,用来突出趋势。

一些峰值持续了连续几个街区,反映了区块链活动中的短期政权转移。让我们更仔细地看看这种转变中最显著的,发生在公元。03:00 和 03:06。在此期间,每个块的事务数量在平均水平和方差方面都有所增加(图 4)。

图 4。区块链上的活动在 03:00 到 03:06 之间发生了变化。为了比较,显示了轮班前后的五分钟间隔。

如下图所示,这种明显的政权转移是由一个特定的地址(THtbMw6byXuiFhsRv1o1BQRtzvube9X1jx,一个 TRON 账户,其身份不明)开始向另一个地址(TSQyuZowokRp3TMRCcrbsDnhnnDSg7gtMT,一个智能合同与一个大型赌博网站相关联)发起大量交易引起的。为了简化符号,我将第一个实体称为“地址 A ”,第二个实体称为“地址 B ”。

# Addresses that interacted between 03:00 and 03:06,
# ordered by the number of transactions:dat %>%
  filter(
    timestamp >= as.POSIXct("2021-07-02 03:00:00", tz = "UTC"),
    timestamp < as.POSIXct("2021-07-02 03:06:00", tz = "UTC")
  ) %>%
  select(tx) %>%
  unnest(cols = tx) %>%
  group_by(from_address, to_address) %>%
  count() %>% arrange(-n) %>% head()#> # A tibble: 6 x 3
#> # Groups:   from_address, to_address [6]
#>   from_address                 to_address                      n
#>   <chr>                        <chr>                       <int>
#> 1 THtbMw6byXuiFhsRv1o1BQRtzvu~ TSQyuZowokRp3TMRCcrbsDnhnn~ 26897
#> 2 TCd4rituYSmbeEDxXpDVF7ordH3~ TFJDc5RmS5HuLRujYSFwHadHQb~  1198
#> 3 TCd4rituYSmbeEDxXpDVF7ordH3~ TSvin3om2vWuw5stbxTeDQrpqH~   647
#> 4 TCd4rituYSmbeEDxXpDVF7ordH3~ TTagN2hiUpt7HBbLTgLWi8qQoS~   374
#> 5 TAUN6FwrnwwmaEqYcckffC7wYmb~ TR7NHqjeKQxGTCi8q8ZY4pL8ot~   184
#> 6 TH4RjfiSXxz71fNP3U3p6XwKYZ8~ TVj7RNVHy6thbM7BWdSe9G6gXw~   149

在 03:06 之后不久,每个块的事务数量再次变得不那么不稳定,尽管其平均水平仍然有所提高(图 4)。相互作用的地址对 AB 仍然是最活跃的一个,尽管这些地址之间的事务数量大幅下降:

# Addresses that interacted between 03:06 and 03:11,
# ordered by the number of transactions:dat %>%
  filter(
    timestamp >= as.POSIXct("2021-07-02 03:06:00", tz = "UTC"),
    timestamp <= as.POSIXct("2021-07-02 03:11:00", tz = "UTC")
  ) %>%
  select(tx) %>%
  unnest(cols = tx) %>%
  group_by(from_address, to_address) %>%
  count() %>% arrange(-n) %>% head()#> # A tibble: 6 x 3
#> # Groups:   from_address, to_address [6]
#>   from_address                 to_address                      n
#>   <chr>                        <chr>                       <int>
#> 1 THtbMw6byXuiFhsRv1o1BQRtzvu~ TSQyuZowokRp3TMRCcrbsDnhnn~  6713
#> 2 TCd4rituYSmbeEDxXpDVF7ordH3~ TFJDc5RmS5HuLRujYSFwHadHQb~  2223
#> 3 TCd4rituYSmbeEDxXpDVF7ordH3~ TSvin3om2vWuw5stbxTeDQrpqH~   396
#> 4 TCd4rituYSmbeEDxXpDVF7ordH3~ TTagN2hiUpt7HBbLTgLWi8qQoS~   367
#> 5 TH4RjfiSXxz71fNP3U3p6XwKYZ8~ TVj7RNVHy6thbM7BWdSe9G6gXw~   199
#> 6 TAUN6FwrnwwmaEqYcckffC7wYmb~ TR7NHqjeKQxGTCi8q8ZY4pL8ot~   171

作为比较,在政权转移前几分钟,前 6 个相互作用的地址对列表如下:

# Addresses that interacted between 02:55 and 03:00,
# ordered by the number of transactions:dat %>%
  filter(
    timestamp >= as.POSIXct("2021-07-02 02:55:00", tz = "UTC"),
    timestamp < as.POSIXct("2021-07-02 03:00:00", tz = "UTC")
  ) %>%
  select(tx) %>%
  unnest(cols = tx) %>%
  group_by(from_address, to_address) %>%
  count() %>% arrange(-n) %>% head()#> # A tibble: 6 x 3
#> # Groups:   from_address, to_address [6]
#>   from_address                 to_address                      n
#>   <chr>                        <chr>                       <int>
#> 1 TCd4rituYSmbeEDxXpDVF7ordH3~ TSvin3om2vWuw5stbxTeDQrpqH~   517
#> 2 TCd4rituYSmbeEDxXpDVF7ordH3~ TTagN2hiUpt7HBbLTgLWi8qQoS~   253
#> 3 TH4RjfiSXxz71fNP3U3p6XwKYZ8~ TVj7RNVHy6thbM7BWdSe9G6gXw~   200
#> 4 TAUN6FwrnwwmaEqYcckffC7wYmb~ TR7NHqjeKQxGTCi8q8ZY4pL8ot~   176
#> 5 TNMQ6BJyycCSMuVmVWH2Re4FySm~ TAQuJmiy83mcnyAtB6wMST6bSY~   136
#> 6 TNaRAoLUyYEV2uF7GUrzSjRQTU8~ TR7NHqjeKQxGTCi8q8ZY4pL8ot~   114

政权更迭期间,地址 A 向地址 B 发送了哪些交易?为了回答这个问题,我们使用tronr包中的[get_tx_info_by_id()](https://levelup.gitconnected.com/how-to-query-transactions-on-the-tron-blockchain-7e646ee546fd)函数收集了一个更详细的数据集(详见 Github )。该数据集可以如下加载到 R 中:

shift_tx <- readRDS(url("https://chilp.it/969333f"))shift_tx %>% dim()
#> [1] 26897    19names(shift_tx)
#>  [1] "request_time"             "tx_id"                   
#>  [3] "block_number"             "timestamp"               
#>  [5] "contract_result"          "confirmed"               
#>  [7] "confirmations_count"      "sr_confirm_list"         
#>  [9] "contract_type"            "from_address"            
#> [11] "to_address"               "is_contract_from_address"
#> [13] "is_contract_to_address"   "costs"                   
#> [15] "trx_transfer"             "trc10_transfer"          
#> [17] "trc20_transfer"           "internal_tx"             
#> [19] "info"

由于地址 B 属于智能合约,因此毫不奇怪,在政权转移期间,它从地址 A 接收的所有 26,897 笔交易都属于类型"TriggerSmartContract":

shift_tx$contract_type %>% unique()
#> [1] "TriggerSmartContract"

这些交易都没有直接涉及任何资产的转让,无论是 TRX、 TRC-10 还是 TRC-20 代币:

shift_tx$trx_transfer %>% sum()
[1] 0

shift_tx$trc10_transfer %>% sum(na.rm = TRUE)
[1] 0shift_tx$trc20_transfer %>% sum(na.rm = TRUE)
[1] 0

然而,TRON 区块链上的智能合约调用通常会触发所谓的“内部事务”,这些事务在“幕后”实施各种操作,包括资产转移。关于这些交易的信息可以在shift_tx表格的列表栏internal_tx中找到。例如,以下是与 03:03 发生的“正常”交易之一相关的内部交易数据:

shift_tx$internal_tx[[1]] %>% glimpse()#> Rows: 9
#> Columns: 12
#> $ internal_tx_id           <chr> "3c4c8d286b643d1a7e0b23e2dd~
#> $ from_address             <chr> "TBPrJYARpfAe9kmnHvMAWcqimn~
#> $ to_address               <chr> "TCcrsGF9PdLxJF869dQsK4V5QE~
#> $ is_contract_from_address <lgl> TRUE, TRUE, TRUE, TRUE, TRU~
#> $ is_contract_to_address   <lgl> TRUE, TRUE, TRUE, TRUE, TRU~
#> $ confirmed                <lgl> TRUE, TRUE, TRUE, TRUE, TRU~
#> $ rejected                 <lgl> FALSE, FALSE, FALSE, FALSE,~
#> $ token_id                 <chr> "TRX", "TRX", "TRX", "TRX",~
#> $ token_name               <chr> "Tronix", "Tronix", "Tronix~
#> $ token_abbr               <chr> "TRX", "TRX", "TRX", "TRX",~
#> $ vip                      <lgl> FALSE, FALSE, FALSE, FALSE,~
#> $ amount                   <dbl> 0.0, 0.0, 0.0, 0.0, 0.0, 0.~

汇总所有内部事务,我们可以看到,实际上总共有 185,113 个 TRX(相当于大约由于在政权转移期间从 AB 的智能合约调用,当日 12,032 美元)易手;

shift_tx %>% 
  select(internal_tx) %>%
  unnest(internal_tx) %>% 
  filter(amount > 0) %>% 
  group_by(token_id) %>% 
  summarise(total_amount = sum(amount))#> # A tibble: 1 x 2
#>   token_id total_amount
#>   <chr>           <dbl>
#> 1 TRX           185113.

这种内部交易分析的下一个合乎逻辑的步骤将涉及更好地理解不同账户之间的 TRX 流动,确定这些账户所属的实体等。我将把这作为一个练习留给读者。

相互作用的区块链地址形成一个网络

通常将区块链交易数据表示为网络(图),其中地址作为节点,交易作为连接节点的边(参见示例这里和这里)。

例如,让我们看一下在研究的当天产生的第 10 个块的交易:

dat$tx[[10]]
#> # A tibble: 278 x 4
#>    tx_id           contract_type   from_address    to_address   
#>    <chr>           <chr>           <chr>           <chr>        
#>  1 85e2dc35e095d5~ TransferContra~ THXPAPiRd62EWk~ TV8oyJ4VEQgE~
#>  2 0733b9aea36aa5~ TransferAssetC~ TCjeJWKkiEgih2~ TA9uJrWuTueK~
#>  3 0c841ce6d571da~ TransferAssetC~ TP8BWU6dYD1fHc~ TSpnfuhALfLJ~
#>  4 51802e2f59cb7b~ TransferAssetC~ TAdTQUqsumn1NG~ TX6HKSS5VnUr~
#>  5 9e3b34b7f99a8f~ TransferContra~ TWq5u64ic279j8~ TM9H9cS6wZox~
#>  6 0820513585ac7b~ TriggerSmartCo~ TJZziktTWeCXgR~ TBRs8xwajQVb~
#>  7 5cc4dd58b3c25c~ TransferAssetC~ TLmqHdqkD5shgj~ TUrCM9mV41FN~
#>  8 a526124805cb6e~ TransferAssetC~ TEpyCakhKtwvtY~ TGmgoGskK3pX~
#>  9 4449d115da5c13~ TransferAssetC~ TWoJEZhqpJxzu7~ TNLAoWMw6WTk~
#> 10 03df1b68f2d7fa~ TransferAssetC~ TJFhAVZjBaCMvt~ TQ8M5KzRJRZi~
#> # ... with 268 more rows

首先,我们将通过(I)找到所有唯一的发送(from_address)和接收(to_address)地址对,以及(ii)计算每对的交易数量(边权重)来总结上述数据:

nb <- dat$tx[[10]] %>%
  group_by(from_address, to_address) %>%
  summarise(weight = n()) %>% 
  ungroup() %>%
  arrange(-weight)nb
#> # A tibble: 238 x 3
#>    from_address                to_address                 weight
#>    <chr>                       <chr>                       <int>
#>  1 TCd4rituYSmbeEDxXpDVF7ordH~ TSvin3om2vWuw5stbxTeDQrpq~     33
#>  2 TEoDHGSTu2Kh97HjCbbcEUaCTm~ TCcjjJ4v7zu6W4F1wwBhuc7EB~      2
#>  3 TJP3A9agR5CP31UE29mhFXK1wk~ TCXeYmM1g44qtRVEHbf9ehwgB~      2
#>  4 TNXscxfqxNtpaeUZKoEUkvEY5e~ TGsyWcVkFBxTknYzXGWvrZPaX~      2
#>  5 TP7NeoT2sM1hkpTuqNrvHorrRz~ TNmiCAm7Q38jKvWsrSRReREuy~      2
#>  6 TPaGwxVw7q26DCxRSGG9b2BNRB~ TTmdBHmoPqXE7EC22QtzPi2Ag~      2
#>  7 TTAct3jgaabAhxSvoZW9SAGVu4~ TWMiYQvrXuyQw5pk6Yt2L6oQF~      2
#>  8 TTfGseRet2nHfvi5KhPx9vCf6Q~ TA7oywPknyAewrq9bss7AzGGs~      2
#>  9 TTfGseRet2nHfvi5KhPx9vCf6Q~ TYYoV3GVuwjHkCYKfYRYwLjBb~      2
#> 10 TA1EHWb1PymZ1qpBNfNj9uTaxd~ TDyoqH6N91PAYrihFzE1VER7H~      1
#> # ... with 228 more rows

产生的 tibble 现在可以被转换成类"igraph"的对象,正如在流行的包igraph中实现的:

require(igraph) # popular R package for network analysisnb_graph <- nb %>%
  graph_from_data_frame(., directed = TRUE)summary(nb_graph)
#> IGRAPH c55f992 DNW- 350 238 -- 
#> + attr: name (v/c), weight (e/n)

得到的对象是一个有 350 个节点和 238 条边的图。为了可视化这个图,我们可以使用[ggnetwork](https://cran.r-project.org/web/packages/ggnetwork/vignettes/ggnetwork.html),一个 R 包,它为ggplot2实现了几个方便的网络几何图形(另一个很棒的网络可视化 R 包是[ggraph](https://ggraph.data-imaginist.com/))。结果如图 5 所示:

require(ggplot2)
require(ggnetwork)# Figure 5:
ggplot(fortify(nb_graph, arrow.gap = 0.007),
       aes(x = x, y = y, xend = xend, yend = yend)) +
  geom_edges(
    color = "#57cbcc",
    arrow = arrow(length = unit(3, "pt"), type = "closed")
  ) +
  geom_nodes(color = "#343a40") +
  theme_blank()

图 5。31570507 块中记录的 TRON 地址(黑点)和交易(蓝绿色箭头)的网络。一些地址在一天内向同一个接收地址发出了几笔交易。然而,为了清楚起见,在该图中忽略了边缘权重。

图 5 揭示了区块链交易网络的典型模式:

  • 网络主要由小的断开元件组成,每个元件有 2-4 个相互作用的地址;
  • 有几个星形组件,要么一个地址接收来自许多其他地址的事务,要么一个地址向许多其他地址发出事务。

交易网络的结构属性可以使用各种度量标准进行定量表征,例如:

  • 直径:从一个节点到达另一个节点必须经过的最大节点数。
  • 入度出度:分别是与一个节点相关的传入或传出事务的数量。表征整个有向网络时,感兴趣的通常是最小和最大的入度和出度。
  • 平均度:边数除以节点数。
  • 边缘密度:边缘数量与最大可能边缘数量之比。
  • 相似性:一种优先依附的度量,即节点倾向于依附在其他在某些方面相似的节点上的现象。分类度通常被计算为成对链接节点之间的皮尔逊相关度系数。

使用igraph中的各个函数可以很容易地计算出所有这些指标。对于图 5 所示的网络,我们得到:

# Diameter:
diameter(nb_graph, directed = TRUE)
#> [1] 33# Max in-degree:
degree(nb_graph, mode = "in", loops = FALSE) %>% max()
#> [1] 32# Max out-degree:
degree(nb_graph, mode = "out", loops = FALSE) %>% max()
#> [1] 9# Average degree:
gsize(nb_graph) / gorder(nb_graph)
#> [1] 0.68# Edge density:
edge_density(nb_graph, loops = FALSE)
#> [1] 0.001948424# Assortativity:
assortativity_degree(nb_graph)
#> [1] -0.228756

总的来说,这些结果与我们之前通过观察图 5 发现的模式一致。

交易网络的结构全天变化很大

人们很自然地会认为,特定区块交易网络的结构是动态的,与区块链正在发生的事情保持一致。让我们为每个块计算前面提到的结构度量,看看它们是如何随时间变化的。

首先,我们将每个块的事务列表转换成类igraph的图形对象,并将结果存储在嵌套的 tibble 中:

require(purrr)nw_stats <- dat %>%
  select(timestamp, tx) %>%
  group_by(timestamp) %>%
  unnest(tx) %>%
  select(-c(tx_id, contract_type)) %>%
  mutate(to_address = if_else(
    is.na(to_address), from_address, to_address)
  ) %>%
  group_by(timestamp, from_address, to_address) %>%
  summarise(weight = n()) %>%
  group_by(timestamp) %>%
  nest(nw = c(from_address, to_address, weight)) %>%
  mutate(nw = map(nw, graph_from_data_frame))# The resultant tibble contains a list-column `nw`, where
# each element is an `igraph` object:
nw_stats
#> # A tibble: 28,719 x 2
#> # Groups:   timestamp [28,708]
#>    timestamp           nw      
#>    <dttm>              <list>  
#>  1 2021-07-02 00:00:00 <igraph>
#>  2 2021-07-02 00:00:09 <igraph>
#>  3 2021-07-02 00:00:12 <igraph>
#>  4 2021-07-02 00:00:15 <igraph>
#>  5 2021-07-02 00:00:18 <igraph>
#>  6 2021-07-02 00:00:21 <igraph>
#>  7 2021-07-02 00:00:24 <igraph>
#>  8 2021-07-02 00:00:27 <igraph>
#>  9 2021-07-02 00:00:30 <igraph>
#> 10 2021-07-02 00:00:33 <igraph>
#> # ... with 28,698 more rowssummary(nw_stats$nw[[1]])
#> IGRAPH 7f07025 DNW- 270 189 -- 
#> + attr: name (v/c), weight (e/n)

现在,让我们编写一个计算网络度量的实用函数,然后使用来自purrrmap()函数将它应用于存储在nw_stats中的每个igraph对象:

# Function to calculate network metrics:
get_nw_stats <- function(g) {
  tibble(
    n_nodes = gorder(g),
    n_edges = gsize(g),
    diameter = diameter(g, directed = TRUE),
    max_in_degree = degree(g, mode = "in",
                           loops = FALSE) %>% max(),
    max_out_degree = degree(g, mode = "out",
                            loops = FALSE) %>% max(),
    avg_degree = n_edges / n_nodes,
    edge_density = edge_density(g, loops = FALSE),
    assortativity = assortativity_degree(g)
  )
}# Calculate network metrics for each block:
nw_stats <- nw_stats %>%
  mutate(stats = map(nw, get_nw_stats))glimpse(nw_stats$stats[[1]])
#> Rows: 1
#> Columns: 8
#> $ n_nodes        <int> 270
#> $ n_edges        <dbl> 189
#> $ diameter       <dbl> 3
#> $ max_in_degree  <dbl> 20
#> $ max_out_degree <dbl> 10
#> $ avg_degree     <dbl> 0.7
#> $ edge_density   <dbl> 0.00260223
#> $ assortativity  <dbl> -0.1411257# Unnest `nw_stats` for easier plotting:
nw_stats_flat <- nw_stats %>%
  ungroup() %>%
  select(timestamp, stats) %>%
  unnest(cols = stats)glimpse(nw_stats_flat)
#> Rows: 28,719
#> Columns: 9
#> $ timestamp      <dttm> 2021-07-02 00:00:00, 2021-07-02 0~
#> $ n_nodes        <int> 270, 409, 525, 555, 28, 680, 658, ~
#> $ n_edges        <dbl> 189, 293, 398, 436, 27, 535, 485, ~
#> $ diameter       <dbl> 3, 4, 8, 6, 1, 8, 3, 52, 46, 33, 3~
#> $ max_in_degree  <dbl> 20, 30, 78, 98, 27, 135, 63, 64, 2~
#> $ max_out_degree <dbl> 10, 10, 8, 9, 1, 12, 8, 10, 5, 9, ~
#> $ avg_degree     <dbl> 0.7000000, 0.7163814, 0.7580952, 0~
#> $ edge_density   <dbl> 0.002602230, 0.001755837, 0.001446~
#> $ assortativity  <dbl> -0.1411257, -0.2274452, -0.3283519~

图 6 显示了每个块的节点数(左图)和边数(右图)随时间的变化。不出所料,这两个指标的动态与每个块的事务数量相似(见图 3)。此外,考虑到特定区块交易网络的高度不连接性,节点数量与边的数量密切相关(图 6,底部面板)。

图 6。每块事务网络中节点数量(左上图)和边数量(右上图)的时间变化。底部面板说明了这两个指标之间的相关性。

最大出度显示了与节点和边数相似的日内变化,而最大出度尽管有多个局部波峰和波谷,但平均而言几乎没有趋势(图 7)。这一发现表明,交易网络的结构主要是由同时与给定接收地址交互的发送者数量驱动的。

图 7。每块交易网络的最大入度(左图)和最大出度(右图)的时间变化。注意 Y 轴的对数刻度。

边密度有一个温和的 U 型趋势,因此与节点和边的数量负相关。尽管程度要小得多,但它也与最大入度负相关(图 8)。

图 8。上图:每块交易网络的边密度的时间变化。下图:边密度分别与节点数、边数和最大入度之间的关系。

全天平均网络度相对稳定,在 0.7 左右震荡(图 9)。

图 9。每块交易网络平均度的时间变化。

每块交易网络的直径在一天中的大部分时间里显示出一致的高变化(从 1 到大约。80;图 8)。然而,这一指标的方差急剧下降,其平均水平同时在 ca。03:00 和 05:00,即在上文详细描述的区块链活动中的明显政权转移之后不久。

图 10。特定区块交易网络直径的时间变化。注意 Y 轴的对数刻度。

在大多数情况下,分类系数为中等负值(图 11),表明低度节点倾向于附着于高度节点。这一发现与在其他非常不连通的交易网络中存在的一些大型星形组件非常一致,这些组件通常可以在 TRON 区块链上观察到(例如,参见图 5)。

图 11。按块交易网络的分类性。

然而,在一天中有多个峰值。这种峰值的频率在一天结束时会增加。图 12 比较了两个交易网络——一个最低(-0.53),一个最高(0.79)。

图 12。2021 年 7 月 2 日在 TRON 区块链上记录的具有最低和最高分类系数的每块交易网络。

结论

本文展示了一些模式,这些模式可以从支持高吞吐量智能契约的区块链上生成的数据中提取出来。然而,它仅仅触及了表面。例如,其他值得探讨的有趣方面包括区块链实体的去匿名化、各类账户之间的资产转移、连锁活动与区块链托管的代币价格之间的联系等。我将在以后的文章中讨论这些和其他研究问题。

您可能还喜欢:

https://levelup.gitconnected.com/introducing-tronr-an-r-package-to-explore-the-tron-blockchain-f0413f38b753 https://medium.com/geekculture/market-data-for-8000-cryptocurrencies-at-your-fingertips-c76d7e8f43ca

在你走之前

我提供数据科学咨询服务。取得联系!

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

数据工程师的一天

原文:https://towardsdatascience.com/a-day-in-the-life-of-a-data-engineer-d65293272121?source=collection_archive---------14-----------------------

分解数据工程师在 2021 年的主要活动

编码【数码影像】https://unsplash.com/@jefflssantos|海绵宝宝清洁【数码影像】https://imgflip.com/meme/81959717/Spongebob-Cleaning

数据工程在 2021 年的角色已经超越了更好或更坏的范围。因此,角色的多种定义不断涌现。数据工程师做更多的分析(也称为新角色定义,分析工程师),数据管道,处理更多的基础设施(DevOps),还是机器学习工程?基本上,一个普通的数据工程师会花多少时间变得有点模糊。然而,这些类别属于技术活动,我们经常忘记它只是花费时间的一部分。在这篇文章中,我们将分解成不同的活动一个数据工程师典型的一天是怎样的。

编码—30%到 40%

让我们来定义编码的真正含义:

  • 开发数据管道/API/微服务。
  • 设置/维护基础设施
  • 修复错误,改进代码库,文档

根据项目阶段的不同,您将在不同的编码方面工作:新特性、调试、维护和稳定性。

同样值得记住的是,编码不仅是“更多”(增加代码行),也是“更少”——删除代码。一个很好的例子是在这里查看 Apache Spark 的顶级提交者。我们可以看到,它们中的大部分实际上是负比例的;他们删除的行比添加的多!

所以不,编码不是主要活动!多项研究表明,一名软件工程师每天会花 30%到 40%的时间编写代码。那个数字和我的经历完全相关。

项目和时间管理—20%到 30%

这是一个具有挑战性的部分,因为用这些很容易变得没有效率。衡量项目/时间管理的效率是困难的,而且你通常不是等式中唯一的变量。

这些活动主要分为两种类型:

  • 写作:门票整理,路线图等。
  • 会议:站立,冲刺规划等。

写作是(差不多?)这是每次会议的先决条件。一份适当的预读材料或议程会加速讨论,让每个人都达成共识。

数据传播——10%到 15 %

数据工程师大部分时间都坐在锤子(数据消费者又名数据分析师/数据科学家/业务/微服务)和铁砧(数据生产者)之间。如果数据消费者出了问题,首当其冲的将是数据工程师。

愤怒的母猫[数码图片]https://imgflip.com/meme/195076787/Angry-lady-cat

在那种情况下,你是坏警察。您需要通过设置规则和传播数据文化来发挥您的作用。你有时候不得不说不,你可能要把人拉回现实。能够礼貌温和地交流现实中的里程碑是一种无价的技能。

编写最佳实践,与利益相关者和数据制作者交流,并向他们展示这些指南是为了帮助每个人和提高生产力,而不是阻止他们。

审查—10%到 20 %

复习是一个重要的类别,因为它基本上是你学习最多的时候。当你独自学习新事物时,很难知道你是否在正确的轨道上。与他人(同事、利益相关者)建立紧密的反馈回路至关重要。你学会了你做得好的地方和你需要适应的地方。

审查可分为 3 个不同的类别:

  • 代码审查
  • 项目审查
  • 绩效评审(团队或同行评审)

有时候,我会花更多的时间审查代码,而不是编码。这不是一件坏事。可能是我需要熟悉一个新的代码库,或者有一些大的特性我想仔细检查。

项目评审可以是事后分析,也可以是对涉众的演示。它基本上是与特定项目相关的一切,了解什么是/曾经是错的,什么是/曾经是好的。这也是一个分享最佳实践和建立约定的机会:编码风格、文档等。

技术观察—5%到 15%

即使不是每天,对今天的数据工程师来说,进行技术观察也是必不可少的,因为新的工具和框架涌现得如此之快,如果不想过时,您需要跟随趋势。

当人们想到 technology watch 时,他们有时会想,“这就是那个只会炒作新玩具的老姐。”但实际上,做技术观察并不一定要关注重大突破性的新技术,还包括:

  • 阅读文章,书籍。
  • 使用新的库/框架或设计模式改进您当前的设置。
  • 跟进可以简化您的设置或降低成本的新云服务或功能。

如果你想在这里获得更多关于趋势的见解,你可以阅读我的博客文章和我们的数据技术技能雷达。

无效—1%到 10 %

“这需要很大的努力才能变得如此不思进取”

实话实说吧。我们总有这样的日子,我们觉得我们所做的一切都不属于上面的任何一类。

浏览你的 LinkedIn feed,谈论你在喝咖啡休息时玩的最后一个游戏,没有成效的会议,这些都是会让你在某天情绪低落的各种活动。

如果你认为这是你时间的一部分,拥有它并承认它没有错,只要它不会占用你太多的时间。

结论

我们可以猜测,编码只是的冰山一角。是的,沟通是几乎所有部门的关键。我总是试图每周记住这些比率,以确保我相应地花费我的时间。这些比率当然会根据你公司的文化和规模而变化。

你错过了什么吗?请随意分享您的比率或/和我可能忘记的部分!

迈赫迪·瓦扎又名迈赫迪欧·🧢

感谢阅读!🤗 🙌如果你喜欢这个,请在跟我来🎥 Youtube ,✍️ ,或者🔗 LinkedIn 了解更多数据/代码内容!

支持我写作 ✍️通过加入媒介通过这个链接

兼职作家(和全职数据科学家)的一天

原文:https://towardsdatascience.com/a-day-in-the-life-of-a-part-time-writer-and-a-full-time-data-scientist-4697086c622b?source=collection_archive---------11-----------------------

不,你不必辞掉工作去做你喜欢的事情。

当我说我写的时候,我拿起笔,纸,写。我喜欢它!(照片由来自 Pexels 的 Judit Peter 拍摄)

我是一个注重隐私的人。我是个作家。像我这样的人确实存在。

尽管我是一名作家,但我一直把个人生活放在一边。我写的是数据科学、工作生活、副业以及我的个人发展历程。基本上,我个人经历的东西和我相信会给你,读者,增加价值的东西的交集。

我被问得最多的一个问题是:“你是如何在全职工作的情况下做到这些的?”

我想得越多,就越意识到这没有秘密可言。只是我绝对热爱我的工作。随着时间的推移,我已经学会了一些对我有用的东西,并一直坚持下来。在体验生活的同时享受全职工作的稳定是一件非常美妙的事情。

这是我现在生活中典型的一天。

上午 7:30:创作者的制胜套路

我如何开始我的一天。(照片由乔丹·麦昆在 Unsplash 上拍摄)

我的一天从在附近散步开始,听播客或书籍摘要。在我不想走路的日子里,我会骑着我在禁闭时买的 Orbitrack 健身器。

晨间运动不仅帮助我过上更健康的生活方式,还增强了我的创造力。我不知道如何解释这一点,但我最棒的想法是在早上散步或骑自行车时想到的。

早晨出汗需要淋浴。快速洗完澡后,我抓起早餐,坐在办公桌前。我的秘密来了:吃早餐时,我拿出一张纸,写下 10 个文章创意。

你可以随意偷这个,因为我是从詹姆斯·阿尔图切和阿约德吉·阿沃西卡那里偷的。我尝试了这个技巧,因为这两位博客专家都将它融入了他们的日常生活,并取得了成功。

最初,我想不出 10 个主意。即使我做了,也是垃圾。如果我没有读到那些博客传奇的好处,我早就辞职了。但幸运的是,我继续。

在听了几个星期的播客、书籍摘要和写下想法后,我确实发现了一些可靠的想法。现在我的想法似乎比我以前有时间写的还要多。

最后,我在开始工作前编辑我的文章。这些是我前一天晚上写的草稿。我试着在早上想出第一份草稿,但它似乎对我不起作用(稍后会有更多的内容。)

我给你的最大收获是:

作为一个创作者,你需要磨练的最重要的技能就是你的创造力。尽一切可能在早晨增强你的创造力。

从别人的内容中获取灵感。它可能来自你最喜欢的播客、书籍、文章、视频、设计、照片等等。

想出点子是一种被证明可以磨练你创造力的方法。如果你是 YouTuber 用户,你可以想出视频创意。对于播客创作者,列出你想采访的人以及你想问他们的问题。总会有出路的。

上午 9 点:启动数据科学家的工作日

在一次深度工作会议中。(照片由来自 Pexels 的 Karolina Grabowska 拍摄)

我不想让你嫉妒,但是我热爱我的工作。我想从事数据科学和人工智能,尽管我知道这是一个竞争激烈和不断发展的行业。

作为一名数据科学家,最好的一点是每个工作日都是独一无二的。并非所有的日子都令人兴奋,但仍然充满挑战。当你对工作感到兴奋时,你经常会过度工作,几乎整天都在工作。

工作再刺激,生活中还有更重要的事情。过去,我花了太多时间在工作上,才意识到家庭、朋友、爱人和个人生活中的抱负对每个人的生活都很重要。

所以,作为一个经验法则,我尽量把我的工作限制在典型的晚上 9-5 点。要做到这一点,这里有一些对我很有效的方法。

争取我深入工作的时间

深度工作是我所有支柱的基础。在他的书《深度工作》(Deep Work)中,我接触到了卡尔·纽波特的技巧,他谈到了在一个分心的世界中专注。

深度工作是专注于一项认知要求高的任务而不分心的能力。这是一种能让你快速掌握复杂信息并在更短时间内产生更好结果的技能。—加州新港

我每天从早上 9 点到中午 12 点挡 3 个小时去深度工作。这让我可以勾掉一天中最重要和最紧急的任务。我的同事意识到了这一点,很少在这个时间段内安排会议。

这个想法是在一个预定的时间内积极地专注于一件事,同时在这个特定的时间段内排除所有干扰。长时间反复练习这一点会成为一种常态。这个策略最大的好处是你可以在更短的时间内完成更多的工作。

整合我的个人任务管理系统

我的个人任务管理系统是一本 120 页的书,我在里面记下所有的事情。

我根据的重要性和紧迫性写下一周的任务,并在一周内一一列出。开会时我会记笔记。在我深入工作的时候,我写下我的想法。

大卫·艾伦说得很好:

“你的头脑是用来有想法的,而不是持有想法的。”

选择我的关机程序并每次都执行它

即使一切按计划进行,有时工作似乎不会结束。那你会怎么做?

你在工作和其他事情之间设定了界限。

我进行了一次深入的工作会议。我可能已经划掉了当天最紧急和最重要的任务。其余的可以等到明天。我执行我的关机程序。

我的关机程序很简单:在本子上记下明天要做的任何事情,钉上我需要的标签,退出微软团队,关闭我的工作笔记本,最后走向冰箱。

啜饮我最喜欢的果汁是对我充满挑战的一天工作的奖励。

不,你不必辞职(或欺骗)

我们听说人们在放弃朝九晚五的工作去进行他们自己的创造性冒险后享受自由。对他们来说是好事,但我们看不到全貌。由于幸存者偏见的本质,我们从来没有听到那些失败者的消息。

然后我们听到一些人鼓励在他们的全职工作上作弊,以支持他们创造性的副业。如果我的工作能从雇主那里得到稳定的报酬,我就应该为此而工作。

我应该想办法增加更多的价值,并要求更大的份额。如果工作场所是有毒的,我应该考虑换一个更好的工作场所。作弊到底是怎么回事?

我努力提高工作效率,所以我甚至不需要作弊。我有足够的时间来处理我创造性的一面——忙碌(一会儿再谈)。

你有没有注意到,最终,人们关心的是你产生的结果,而不是你为获得它们付出的时间?

你是个创造者。可以有一份稳定的全职工作。不要让任何人告诉你不是这样。

下午 6 点:给最重要的人

给家人、朋友和爱人。(图片来自埃琳娜童话来自派克斯

当我完成工作时,我试着花时间和我爱的人在一起,因为每个人都完成了他们的工作、学习和其他承诺。

这些时间没有固定的计划。我们会像一家人一样一起吃饭。有时我们会玩 Ludo,carrom 或者只是在家聊天。

有时我会打电话给我在另一个国家的一个大学朋友。有时候我会和以前的同事聊聊。

你明白了——如果我把其他事情都做对了,却没有花足够的时间和我爱的人在一起,这真的值得吗?

晚上 9 点:写,读,重复

很抱歉我花了这么长时间才告诉你我每天创作的部分。但我是在晚上做的——我想我应该遵循白天的自然流程?

不管发生什么事,当钟敲 9 点的时候,你都会看到我在书桌前写作。正如威廉·福克纳的名言:

“我只在灵感迸发时写作。幸运的是,它每天早上九点钟敲响。”

我试过在早上写作;好像对我不起作用。所以我选择了晚上。你什么时候写并不重要。重要的是,你已经拨出了每天的时间表,坐下来写下与你的动机水平无关的东西。

我每天都有灵感来拜访我

当你没有写作的灵感时,你很难写作。我已经尝试过多次,但都失败了。以下是我如何让自己受到鼓舞的:从各种平台上获取鼓舞人心的内容。

  • 我接着去了 TED 演讲,看了一个相关的演讲。我通常保持一到两次。大多数 TED 演讲都是鼓舞人心的;专家用亲身经历来传递它们。首先,观看亚当·格兰特谈论原创思想家的习惯。
  • 我在 Medium 上看过相关文章。我阅读我最喜欢的作家、朋友和在我自己领域引起我注意的故事。这有助于我了解什么可行,什么不可行的趋势。
  • 我用 Blinkist 来听别人推荐给我的书籍的音频摘要。我的“我想读的书”清单太大了,我怀疑我永远也读不完。

更多的时候,当我有意识地消费内容时,我进入了心流。

在我写下一个字之前,我会这样做

我看过 Zulie 在 Twitch 上直播,然后就这样开始写作。我已经接受了每个人都有自己的写作风格,对于我来说——我需要在我写之前组织好我将要写的。我是这样做的:

  • 我从我的想法中挑选了一个标题。还记得造物主的制胜套路吗?是的。我已经做了工作。所以现在,我要选择一个,然后继续做下去。
  • 我创建了这篇文章的思维导图。我写下我想在文章中传达的要点,然后在每个要点下写下要点。我希望我的介绍和结论是什么样的?基本上,它是文章的骨架。
  • 我为文章调研。除非你是埃隆·马斯克或吴恩达,否则你需要事实和研究来支持你的观点。这包括在多个选项卡上阅读更多内容,收集统计数据,并找到文章的相关引用。

然后,我不得不写

老实说,写作是无可替代的。到目前为止,我已经挑选了一个想法,标题,构思了整篇文章,研究了支持的事实和引用。我之前所做的一切只是为了让这部分变得更简单:写初稿。一旦时间到了,我就写。

我不会说谎;有些日子我似乎还是写不出任何东西。我没有解决这个问题的办法。我试着接受今天不是我的日子,第二天再做我的事情。

我不同时写作和编辑。这是一个错误。写作和剪辑需要不同的思维模式,两者都做将影响最终作品的质量。

一个伟大的作家也读很多书

是的,我确实听读书摘要,但是没有什么可以代替实际的阅读。我对借用他人大脑的想法很感兴趣,而书籍恰恰可以帮助你做到这一点。多酷啊。

"如果你想成为作家,你必须首先做两件事:大量阅读和大量写作。"—斯蒂芬·金

通常我会拿起我的实体书,读大约半个小时,然后才会觉得困。我阅读速度很慢;平均一个月只看一两本书。我正在考虑买一个 Kindle,所以如果你用过,也请告诉我你的经历。

12 午夜:一夜好眠

是时候了。晚安,我的朋友。(图片由来自 Pexels 的 Andrea Piacquadio 拍摄)

我姐姐痴迷于睡个好觉,这也影响了我。我做过各种各样的睡眠黑客和睡眠实验。这听起来很科学,但确实有效。

我尽量坚持在午夜 12 点左右睡觉,在早上 7 点左右醒来。这是对我有效的方法,我仍然没有发现早上 5 点醒来的需要。

如果我可以立即入睡,醒来时没有任何闹钟,感觉新鲜和精力充沛,我还想要什么?

最后,个人想法

这不是我的典型文章之一。通常,我会写一些对我的读者有价值的东西,提交给一家出版社,然后继续前进。我不惜一切代价避免在个人层面上写我。

今天不一样。现在是午夜 12 点 23 分,我还在考虑是否应该发表这篇文章。我是不是太诚实了?为什么这感觉太暴露了?人们会因为我的套路来评判我吗?如果我的雇主看到这个会怎么想?

随着自我怀疑的蔓延,我和一个朋友分享了草稿,并寻求反馈。他说他不能代表我的读者,但是他发现我典型的一天是有价值的,并且会试着把一些融入他的一天。

如果你正在读这篇文章,不知何故,我决定出版。

这引出了我作为一个创造者的最后一点;自我怀疑时不时会出现。不要犹豫伸出手来——我们都知道作为一个创造者保持一致有多难。与普遍的看法相反,许多人愿意帮助我们。

成为一名作家给我的日常生活增添了许多色彩——我希望你们每个人也是如此。

要获得更多关于进入数据科学、真实体验和学习的有用见解,请考虑 加入我的电子邮件好友私人列表

Spark 3.0 中数据框架的入门指南

原文:https://towardsdatascience.com/a-decent-guide-to-dataframes-in-spark-3-0-for-beginners-dcc2903345a5?source=collection_archive---------11-----------------------

从概念上理解转换

Apache Spark 是一个分布式引擎,它为最终用户提供了两个 API 来构建数据处理管道。Apache Spark 3.0 中最常用的 API 是 DataFrame API,它非常流行,特别是因为它用户友好、易于使用、非常有表现力(类似于 SQL),并且在 3.0 中非常丰富和成熟。该 API 特别适合处理具有某种结构的数据,只需使用几行代码就可以编写一个执行一些重要转换的查询。

在 Spark 中,一件事情可以用不同的方式实现是非常典型的,这种灵活性带来了力量,但有时也带来了困惑。如果有更多的方式,它们之间的区别是什么?哪种方式更好或效率更高?正如我们将看到的,数据帧上的列转换也是这种情况,我们将试图澄清这一点以避免一些误解。

在本文中,我们将解释什么是数据帧,如何创建它,以及如何使用它来执行各种数据转换。我们将在最新的稳定版本中使用 PySpark API,该版本是在撰写本文时(2021 年 1 月)的 3.0 版本。

什么是数据帧?

Spark 中的 DataFrame 是一种抽象,它允许我们以一种很好的方式处理分布式数据。它表示具有表格结构的数据,数据集中的每条记录就像包含一些字段的行,每个字段都有名称和数据类型,因此每个字段就像表中的一列。它确实非常类似于数据库中的表,我们只需要记住,DataFrame 表示在处理期间将分布在集群上的数据。此外,如果您的数据没有真正的表格结构,例如,每条记录只是一个带有一些文本消息的字符串,您仍然可以用只有一列的 DataFrame 来表示它。

数据帧创建

创建数据帧有六种基本方法:

  1. 最基本的方法是转换另一个数据帧。例如:
# transformation of one DataFrame creates another DataFrame
df2 = df1.orderBy('age')

2.您也可以从 RDD 创建数据框。RDD 是 Spark 中的一种低级数据结构,它也代表分布式数据,主要在 Spark 2.x 之前使用。它慢慢变得更像 Spark 中的内部 API,但如果您愿意,仍然可以使用它,特别是它允许您创建如下数据帧:

df = spark.createDataFrame(rdd, schema)

3.下一个更有用的方法(尤其是对于原型)是从本地集合创建数据帧,例如,从列表创建数据帧:

l = [(1, 'Alice'), (2, 'Bob')] # each tuple will become a rowdf = spark.createDataFrame(l, schema=['id', 'name'])

如果您想要测试您的转换,但是您还不想在真实数据上运行它们,这是非常有用的。如果您知道模式,您可以创建一个像这样的小数据帧。

4.对于原型制作,快速创建一个数据帧也很有用,该数据帧将具有特定数量的行,并且只有一个列 id ,使用序列:

df = spark.range(10) # creates a DataFrame with one column id

5.下一个选项是使用 SQL。我们将有效的 SQL 语句作为字符串参数传递给 sql() 函数:

df = spark.sql("show tables") # this creates a DataFrame

6.最后,创建数据帧的最重要选项是从源中读取数据:

这里的选项被指定为键-值对,根据数据的格式有不同的键可用。要检查所有可能的选项,请参见 PySpark 文档。这段代码还有各种替代方法和快捷方式,例如:

df = spark.read.parquet(path, **options)df = spark.read.format('json').schema(schema).load(path, **options)

如果我们的数据源是使用 Hive metastore 存储的,我们可以使用它的表名来访问它,如下所示:

df = spark.read.table('table_name')# or just:
df = spark.table('table_name')

这实际上更加优雅,因为您不必处理模式、文件格式或数据路径,您需要知道的只是表的名称,Spark 从 metastore 中挑选所有需要的信息。请注意,除了 Hive 表格格式,还有其他表格格式,如 Delta 或 Iceberg,我们可以在一些单独的文章中介绍它们。

因此,我们展示了如何创建数据帧的六种可能方式,正如你所见,所有这些方式(除了第一种)都是从 spark 对象开始的,它是 SparkSession 的一个实例。Spark session是在 Spark 应用程序开始时创建的,它是你进入数据帧世界的通行证,没有它你无法创建数据帧。因此,通常在 Spark 应用程序开始时,创建对象如下:

from pyspark.sql import SparkSession**spark** = (
    SparkSession
    .builder
    .appName('name_of_your_application')
    .enableHiveSupport() # if you want to use Hive metastore
    .getOrCreate()
)

数据帧操作

您可以在数据帧上调用两种类型的操作,即转换和操作。转换是懒惰的,这意味着当您调用它们时,它们不会触发计算,相反,它们只是在幕后构建一个查询计划。所以当你这样打电话的时候:

result = df.dropDuplicates(['user_id']).orderBy('age')

调用了两个转换,但是没有进行任何计算,数据本身仍然在存储中,等待您处理它。相反,您只是创建了一个 DataFrame result ,它具有一个查询计划,该计划表示数据将按照 user_id 列进行重复数据删除,并按照 age 列进行排序。但是所有这些都将在您具体化查询之后发生,这是您调用一些操作的时候。因此,在调用一个动作(通常是一个请求输出的函数)后,Spark 将触发计算,计算本身由多个步骤组成:Spark 优化器优化查询计划,生成一个物理计划,然后将计划编译成 RDD DAG(有向无环图),该图接下来被划分为在集群上执行的阶段和任务。

如果您想查看查询计划,只需调用:

df.explain()

要阅读更多关于查询计划的信息,请随时查看我的另一篇文章或观看我在 2019 年欧洲星火峰会上的演讲。

数据帧转换

转换本身可以分为两组,数据帧转换和列转换。例如,第一组转换整个数据帧

df.select(col1, col2, col3)
df.filter(col('user_id') == 123)
df.orderBy('age')
...

最常用的数据帧转换可能如下(但这当然取决于用例):

  1. select()withColumn() —用于投影列
  2. filter() —用于过滤
  3. orderBy()sort()sort within partitions()—用于排序
  4. distinct()dropDuplicates() —用于重复数据删除
  5. join () —对于加入(参见我的另一篇文章关于 Spark 3.0 中的加入)
  6. groupBy () —用于聚合

有关 DataFrame 转换的完整列表,请参见 PySpark 文档中的 DataFrame 类。

列转换

另一方面,列转换用于转换单个列,它们用在 *withColumn()、select()、*或 selectExpr() 转换中,允许您向 DataFrame 添加新列:

from pyspark.sql.functions import initcap# capitalize the first letter of the user name and save it to a new # column name_capdf.withColumn('name_cap', initcap('user_name'))

这里,列转换是通过函数 initcap() 实现的,该函数转换来自 user_name 列的字符串。另一方面, withColumn 转换是一个数据帧转换,因为它转换整个数据帧—它添加了一个新列。因此,典型的用例是您想要添加一个新列,该列是使用一些转换从其他列派生而来的。

对于列转换,通常可以使用多种不同的方式来表示,在上面的示例中,同样可以使用 select (或者等效地使用 exprselectExpr )来实现:

df.select('*', initcap('user_name').alias('name_cap'))df.select('*', expr('initcap(user_name) as name_cap'))df.selectExpr('*', 'initcap(user_name) as name_cap')

请注意 selectwithColumn 之间的区别,后者投影数据帧中的所有列,并添加一个具有给定名称的新列,而 select 仅投影那些作为参数传递的列,因此如果您希望所有列都在其中,您要么需要显式列出它们,要么使用上面的带星号的符号。另外,在 select 中转换的结果列将有一个默认名称,所以如果您想使用一个自定义名称,您必须使用 alias() 来重命名它。

另外,请注意函数 expr() 允许您将 SQL 表达式作为引号中的字符串传递,类似地, selectExpr() 只是使用select()+*expr()*的快捷方式。在表达式中,您可以使用 SQL 文档中的任何 SQL 函数。

现在你可能会对 expr 函数的用例感到困惑,我们很快会解释这一点。在此之前,让我们看看如何表达列转换的另一种方式,它使用了来自列类的方法。考虑这样一个例子,我们想从单词的开头提取一个长度为 3 的子串。使用来自列类的方法 substr() 我们可以这样做:

df.withColumn('new_col', col('user_name').substr(1, 3))

正如您所看到的,column 类中的这些方法是在 Column 对象上调用的,您可以使用 col 函数或使用点或括号符号来构造该对象,如下所示:

# all of these create a column object:col('user_name')
df.user_name
df['user_name']

也可以使用 pyspark.sql.functions 包中的 substring() 函数来编写上面的示例,如下所示:

df.withColumn('new_col', substring('user_name', 1, 3))

这里的 user_name 可以是一个字符串——列的名称,也可以是一个列对象,这个函数更加灵活,可以处理这两种情况,这对于这个包中的函数来说是典型的,但是,这种灵活性有其局限性,您马上就会看到。

我们来澄清一下

您可能想知道使用带有 expr 的 SQL 函数和直接使用 pyspark.sql.functions 包中的函数(我们在这里称之为 DSL 函数)有什么区别。嗯,如果您查看这两个文档( SQL 、 DSL ),您将会看到这些函数中的许多确实是相同的(在某种意义上,它们具有相同的名称并为您提供相同的功能),但是在某些情况下,SQL 函数更加灵活,允许您实现 DSL 函数不容易实现的功能。

让我们再次考虑带有子串的例子,DSL 子串函数的后两个参数、位置和长度必须是整数——它们不能是列对象。但是有时有一个用例,您需要从另一列获取参数,例如,您希望子字符串的长度是动态的,并且您希望在每一行上使用不同的值。DSL substring 函数不允许您这样做——位置和长度需要保持不变,每行都相同。

这是我们上面提到的限制,可以使用 expr() 中的 SQL 函数绕过它。考虑这个例子,在这个例子中,我们希望根据一列中的信息生成另一列的子串。假设我们想要获取第一个 n 字符,其中 n 存储在另一个名为 len: 的列中

l = [('abcd', 2), ('abcd', 1)]df = spark.createDataFrame(l, ['str', 'len'])df.show()+----+----------+
| str|    len   |
+----+----------+
|abcd|         2|
|abcd|         1|
+----+----------+

现在让我们看看我们有哪些选项来实现 len 字符的子串:

# (1) raises error:
df.withColumn('new_col', substring('str', 1, 'len')) # (2) raises error:
df.withColumn('new_col', substring(col('str'), 1, col('len')))# (3) raises error:
df.withColumn('new_col', substring(col('str'), lit(1), col('len')))# (4) raises error:
df.withColumn('new_col', col('str').substr(1, 'len'))# (5) raises error:
df.withColumn('new_col', col('str').substr(1, col('len')))**# (6) this works:**
df.withColumn('new_col', col('str').substr(lit(1), col('len')))**# (7) this works:**
df.withColumn('new_col', expr('substring(str, 1, len)')).show()+----+---+-------+
| str|len|new_col|
+----+---+-------+
|abcd|  2|     ab|
|abcd|  1|      a|
+----+---+-------+

如您所见,我们无法使用 pyspark.sql.package 中的子串来完成,每次尝试(1–3)都会失败。但是如果我们使用 SQL API 中的子串函数 expr 它可以工作(第 7 次尝试),SQL 函数会更加灵活,并且能够意识到 len 实际上是 DataFrame 中一列的名称,它从那里获取长度。我们可以看到使用 substr 方法的尝试 6 也是可行的,但是我们需要确保两个参数都是列,这就是为什么我们必须对第一个参数使用lit(1)(lit函数将从我们传入的常量创建一个列对象)。然而,与 SQL 函数相比,class 列提供的方法并不丰富,所以在其他一些情况下,我们在这里找不到相应的函数。一个这样的例子在这个 StackOverflow 问题中讨论过,另一个与 array_sort 有关,我在我的另一篇文章这里中描述过。

列转换-摘要

总而言之,如果您想要创建一个列转换,那么在文档中有三个地方可以找到合适的函数:

  • pyspark.sql.functions (这里我们称之为 DSL 函数)
  • 列类(列类的方法——它们在列对象上被调用)
  • SQL 函数(来自于 expr 或者 *selectExpr,*里面要用到的 SQL API,这里我们称之为 SQL 函数)

就性能而言,它们都是等效的,因为它们很懒,只是简单地更新了查询计划。与 DSL 函数相比, expr() (或 selectExpr() )使用的 SQL 函数有时更灵活,允许您将所有参数作为列名传递。通常你不需要这种额外的灵活性,你决定使用哪种方法通常是风格的问题,也可能是基于团队成员之间关于他们更喜欢在代码库中使用什么的一些共识。

高级转换

还有三组更高级的列转换,它们是用户定义函数、高阶函数和窗口函数,我们可以在一些单独的文章中讨论它们,因为它们值得更详细的探讨。简单来说,用户定义的函数允许我们通过一个非常简单的接口来扩展 DataFrame API,因此使用原生 Python (Scala/Java)函数,我们可以实现 API 中尚未提供的任何自定义转换,然后我们可以将它用作列转换。从 Spark 2.4 开始,高阶函数得到了很好的支持,它们用于转换和操作复杂的数据类型,比如数组或映射。窗口函数允许我们计算由窗口和框架定义的多组行的各种聚合或排名。

行动

如上所述,动作是我们要求一些输出的函数。这些函数触发计算并在 Spark 集群上运行一个作业。通常的情况是该动作运行一个作业,然而,在某些情况下,它可以运行更多的作业(例如,参见我的另一篇文章,其中我解释了为什么 show() 函数可以运行更多的作业)。一些典型的操作包括:

  • count() —计算由 DataFrame 表示的数据集中的行数。
  • show() —在屏幕上打印数据帧所代表的数据集中的 20 条记录。
  • collect() —打印屏幕上的所有记录。这应该小心使用,因为它实际上从所有执行器收集所有数据并将其带给驱动程序,这是潜在的危险,因为如果数据很大,它会使驱动程序崩溃,因为它的内存是有限的。如果数据已经被过滤或聚集得足够多,那么它的大小对驱动程序来说不是问题,那么这个函数就很有用。
  • toPandas() —这类似于 collect() ,但是结果不是一个记录列表,而是一个熊猫数据帧。
  • take(n) —也类似于 collect() ,但是它只收集 n 条记录。如果您想检查是否有一些数据,或者 DataFrame 是否为空,这可能很有用( df.take(1) )。
  • 写入 —创建一个数据帧写入器,允许将数据保存到外部存储器。

让我们来看看描述 Spark 中操作的最后一张图(有一些例子):

结论

在这篇笔记中,我们特别介绍了 Spark SQL 和 DataFrames。我们首先解释了什么是数据帧,我们已经看到了创建数据帧的六种方法,并且我们花了一些时间解释了可以用它做什么样的操作。我们还指出了 SQL 函数的灵活性,当您需要从数据帧中以列的形式提供多个参数时,它允许您绕过相应 DSL 函数的限制。我们解释了列转换的各种方法之间的区别,这是一个经常令人困惑的主题,尤其是对于 Spark 初学者来说。

对 k-means 的深入探究

原文:https://towardsdatascience.com/a-deep-dive-into-k-means-f9a1ef2490f8?source=collection_archive---------24-----------------------

关于最流行的聚类算法,您只需要知道

Evie S. 在 Unsplash 上拍摄的照片

介绍

聚类问题在数据科学中非常常见。潜在的问题总是在你的数据中找到相似的观察组。根据您的领域,这可能是具有相似偏好的客户、在您的生物检测中表现相似的股票或细胞。最大的吸引力在于它是一种无人监管的方法。这意味着你不需要任何标签,而是算法为你找到标签。

最简单的算法是 k-means,我相信您已经听说过它,并且可能使用过它。但我总是发现从头开始编写这些算法很有帮助,即使是简单的算法。今天的帖子结构如下,只需跳转到你最感兴趣的部分,或者阅读所有内容以获得完整图片:

  • k-均值理论简介
  • k 均值实现
  • 如何求 k 的理想数
  • k-means 的优缺点
  • 与其他聚类算法的关系,如 k-mediods 或模糊 c-means
  • 摘要

说到这里,让我们开始吧。

k 均值理论

无监督学习方法试图在你的数据中找到结构,而不需要你方太多的初始输入。这使得它们对任何类型的应用都非常有吸引力。与任何其他聚类算法一样,k-means 希望将相似的观察值分组在一起,同时分离不相似的点。K-means 只需要 1 个超参数,即 k ,期望聚类数。这使得它很容易运行,但也有一些缺点,稍后会讨论。在数学上,k-means 专注于最小化类内平方和(WCSS ),也称为类内方差、类内距离或惯性:

组内平方和的定义。k 表示集群。

其中 k 是集群,||。||是欧几里得范数,在这种情况下是两点之间的欧几里得距离。

由于我们最小化所有的集群 C ,我们可以将优化函数写成如下:

k 均值的优化函数

其中|Cᵢ|是群集的基数,即其中的观察值的数量。

人们也可以将其解释为最大化聚类之间的总方差,也称为聚类间距离,如总方差定律所述:

总方差=解释方差(WCSS) +未解释方差(聚类间距离)

k 均值实现

我们将逐一介绍 k-means 的所有步骤。对于这个实现,我试图非常明确,使代码尽可能容易理解。当然,这不是最快的实现,但这里的目标是首先理解。你也可以在我的 GitHub 上以 RMarkdown 文档的形式关注这个例子。

因为我们将在这个例子中使用 iris 数据集,所以让我们假设我们是园丁。我们辛辛苦苦培育出三种不同的美丽花朵,可惜忘了给幼苗加上标签。它们还没有开花,但我们真的想确保只一起种同样的花。我们现在所能做的就是测量外面的叶子,也就是所谓的萼片。让我们先来看看数据:

head(iris)

如你所见,数据集包含了关于萼片、花瓣的信息,以及这种植物来自三个物种中的哪一个,这就是我们想要预测的。因此,让我们首先只选择我们示例中的数据:

df <- iris[, c(“Sepal.Length”, “Sepal.Width”)] 
plot(df)

目测只显示两组,尽管我们预期有 3 种不同的物种。那么让我们看看 k-means 是否也能找到第三组。

距离度量

首先,我们需要定义距离度量,我们将使用欧几里德距离来测量每个点到中心的距离:

euclidean_distance <- function(p1, p2) {
  dist <- sqrt( (p1[1] - p2[1])^2 + (p1[2] - p2[2])^2 )
  as.numeric(dist)
}

这个距离函数当然只对二维情况有效,但是可以很容易地扩展到任何你想要的维度。如果输入是一个 data.frame 或一个命名的 vector,我们将输出封装在对as.numeric()的调用中以删除任何名称。

初始化

K-means 需要对第一个中心进行初始猜测。这也意味着,我们需要将期望中心的数量输入到算法中,这是 k-means 最突出的缺点之一。我们将在它自己的章节中讨论这一点,现在让我们假设我们知道我们有三个不同的品种。

为了初始化算法,我们简单地从数据集中随机选择三个点。为了强调我们需要在没有替换的情况下这样做,我也写出了这个可选参数,即使默认值已经设置为 FALSE。

k <- 3 
centers <- df[sample(nrow(df), k, replace = FALSE), ]

循环

接下来,我们需要计算每个点到这些中心的距离。

distances <- matrix(Inf, nrow = nrow(df), ncol = k)
    for (i in seq_len(nrow(df))) {
      for (j in seq_len(k)) {
        distances[i, j] <- euclidean_distance(df[i, ], centers[j, ])
      }
    }

现在我们可以将每个点分配到它最近的中心。为此,我们逐行检查结果,并选择距离最小的条目。

cluster_id <- apply(distances, 1, which.min)

最后,我们需要计算每个集群的新中心。为了保持高度显式的编码风格,我们也在 for 循环中这样做。

for (i in seq_len(k)) {
      this_cluster <- df[cluster_id == i,]
      centers[k, ] <- apply(this_cluster, 2, mean)
    }

瞧,这就是一轮 k 均值的全部内容。因为我们在这一步中有效地移动了中心的坐标,所以 k-means 属于坐标下降算法组。

但是当然,一次迭代是不够的。相反,我们需要在数据集上迭代多次,并总是调整我们的中心。在我们的实现中,我们选择在终止之前运行 k-means 定义的迭代次数。要做到这一点,我们可以将所有内容包装在 while 循环中,并放入一个函数中。可选地,如果算法已经收敛(即,中心在两次迭代之间不改变),或者总 WCSS 的变化低于定义的阈值,则可以包括提前终止的检查。

分析我们的测试数据

让我们进行分析,并与真实物种进行比较

set.seed(42)
cluster_id <- my_kmeans(data = df, k = 3, n_iterations = 10)

正如你所看到的,K-means 确实能够将植物分成三组,这与潜在的地面真相相似。

如何求 k 的理想数

正如我们所见,k-means 要求我们输入预期的聚类数。这是一个严重的局限性,因为我们可能不一定事先知道这一点。此外,k-means 将总是输出具有指定数量的 k 个聚类的聚类,不管这是否有意义。那么我们该如何应对呢?有几种可能的方法。

肘法

一种非常常见的解决方案是所谓的肘法。我们用几个候选值 k 执行 k-means,然后选择最好的一个。为了进行这种选择,我们使用总 WCSS,即所有集群的 WCSS 的总和。

为了更直观地解释这一点,首先想象一个 k = 1 的例子。所有事件将属于同一个聚类,这意味着总 WCSS 将等于数据集中的总平方和。如果我们将k增加到 2,至少一些点将属于第二簇。这将减少第一簇内的 SS,并且总 WCSS 也将更小,因为总 WCSS 具有 k = 1 。所以 k变得越高,WCSS 就越小。在 k =观测数量的极端情况下,WCSS 将为 0。

为了选择最佳的 k ,我们需要在 WCSS 的减少和我们的模型对数据的过度拟合之间找到最佳的平衡。肘方法通过选择 WCSS 行为中“弯曲”最强的点来实现。为了说明这一点,让我们在数据中绘制从 1 到 10 的 k 值的行为。从现在开始,我们将使用已经实现的 k-means 方法,因为它更快,并且已经实现了 WCSS,这是结果列表的 tot.withinss 元素。

within_cluster_ss <- c()
for (k in 1:10) {
  within_cluster_ss[k] <- kmeans(df, k)$tot.withinss
}

我们可以推测弯曲最强的点在 k = 3 处,但不妨在 k = 4 处。

此外,如果能自动获得这个选项,而不需要我们手动选择,那就太好了。我们可以用一个小的助手函数来自动化这个过程。首先,我们在最大值点和最小值点之间拟合一条线。然后我们计算每个点到这条线的距离。距离最高的点是肘点。或者,可以用最大绝对二阶导数来计算点。

事实上,它返回 k = 3 ,但这只是一个试探。例如,重新运行相同的示例,但是这次在 1 和 20 之间改变 k。您将看到,在这种情况下,它将返回 k = 4 作为最佳聚类数。

剪影分数

另一种评估哪个 k 最符合数据的方法是所谓的轮廓分数。它测量一个聚类中的点的相似性,并将其与其他聚类中的点的相似性进行比较。其范围在-1 和 1 之间,其中-1 表示与其指定的分类非常不相似的点,而 1 表示与其分类高度相似的点。值为 0 表示该点位于两个相邻簇之间的边缘。让我们一步一步来看:

在运行 k-means 之后,对于每个聚类,我们计算每个点到同一聚类中所有其他点的平均距离。也可以将其解释为每个点与其被分配到的聚类有多相似,低值表示高相似性。设 i 是聚类 Cᵢ 中的数据点,并且 d 是距离度量(在我们的例子中是欧几里德距离)。那么这个点 I 到它的集群的距离被定义为:

其中 |Cᵢ| 再次是集群的基数,即其中元素的数量。较小的值表示一个与其聚类非常相似的点。

或者,我们可以排除一个点到自身的距离,得到:

在第二步中,我们计算该点与其他聚类的相似性。因此,对于每个其他聚类,我们计算点到属于聚类 k 的所有点 j 的平均距离。然后,相异度被定义为到最近聚类的平均距离,最近聚类也被称为点 i 的“相邻聚类”。

最后,我们需要相互权衡,得出轮廓分数:

让我们假设我们有一个点在它所分配的簇的正中心。在这种情况下,到其分配的聚类的距离为 0,这产生 1:

相反,两个聚类之间的边界处的点到其所分配的聚类的中心和最近的相邻聚类的中心的距离相等。接下来是:

理论上,剪影分数也可以变为负值(直到-1),这表示一个点应该被指定给不同的聚类。对于 k-意味着不应该发生这种情况,因为我们根据距离来分配点。

对于一个集群只包含一个成员的情况,我们需要定义一个固定的分数。我们选择 0,因为它是介于-1 和 1 之间的中性选择。所以在 s(i) = 0 的情况下,如果|Cᵢ| = 1

对于聚类是否良好的程序性评估,通常计算每个 k 在所有点上的 s 的平均值。然后,我们选择具有最高平均值的 k

silhouette_scores <- c()for (k in 2:10) {
  cluster_id <- kmeans(df, k)$cluster
  silhouettes <- silhouette(df, cluster_id)
  silhouette_scores <- cbind(silhouette_scores, silhouettes)
}colnames(silhouette_scores) <- 2:10
silhouette_scores <- apply(silhouette_scores, 2, mean)
best_k <- names(which.max(silhouette_scores))

或者,可以绘制给定 k 的所有 s(i) 值,并直观地评估聚类:

k <- 3
kmeans_res <- kmeans(df, k)
cluster_id <- kmeans_res$cluster
silhouettes <- silhouette(df, cluster_id)
result_df <- data.frame(cluster_id = cluster_id, silhouette = silhouettes)
result_df <- result_df[order(result_df$cluster_id, result_df$silhouette), ]

聚类结果评估

集群基数

我们已经知道集群基数是集群中事件的数量。根据您对数据的了解,您可能期望大小相等的簇,或者大小不同的簇。

我看到一些人建议集群应该总是大小相等,但是我不认为这是真的。如果数据中真的有更多罕见的群体正在形成他们自己的群体呢?对于具有罕见特征的患者、社会网络分析、具有独特基因表达水平的细胞或只是你正在研究的商品制造过程中的差异,这可能是真的。因此,如果您的集群显示出不同的基数,不要太担心,但还是要研究离群值。

集群基数与集群数量

聚类大小描述了一个聚类中所有点到其中心的总距离。幸运的是,R 中的 k-means 实现已经为您计算了这些值:

这个值本身并不能提供太多的洞察力,类似于单独的集群基数。但是把两者结合起来会在结果中显示出更好的趋势。直观上,人们会期望具有很少成员(即低基数)的聚类也具有这些点到聚类中心的低总差异。所以这两个值应该是正相关的。如果不是这种情况,这将意味着分散的集群,这可能意味着这些实际上是噪声点。

正如我们所看到的,这个图对于 k的低值没有太大帮助。为了更好地说明这一点,让我们画出 k = 10 的结果。我们可以观察到一个明显的异常值:

集群特征

最后,最后但可能是最重要的一步是描述您的集群。根据您所在的领域,这可能是维度的简单汇总统计(在我们的示例中,花瓣有多长/多宽),或者其他描述,如图像强度、基因表达水平等。毕竟,当运行聚类分析时,您感兴趣的是出现了哪些组,所以在找到它们之后详细描述它们!

k 均值的优势

在深入研究了实现之后,让我们关注 k-means 的一些更高级的特征。这是一种流行的算法,有几个优点:

概念上很容易理解

正如我们所看到的,这个理论很简单,人们可以很容易地想象和理解 k-means 背后的概念。

它基本上是用所有编程语言实现的

几乎每种常见的编程语言都有 k-means 的实现,这使得在数据集上运行它变得很容易。

它只有一个超参数

k ,集群的数量

它与观察次数成正比。

其复杂度为 O(n k d i),其中

  • n= d 维向量的数量
  • k =集群的数量
  • i =收敛前的迭代次数

k-means 的缺点

另一方面,k-means 的简单性也带来了一些主要的缺点:

它需要 k 的先验知识

这是一个主要缺点,因为人们通常不知道数据中存在的聚类数。此外,它将总是准确地返回 k 个集群,不管数据中实际上有多少个是有意义的。我们可以通过尝试不同的 k 并选择最好的一个来减轻这一点,但是正如我们所看到的,这也并不总是奏效。

这是一种启发式算法

由于实际问题是 NP 难解决的,通常使用启发式算法来寻找解决方案,主要是 Loyd 算法。这意味着返回的结果也可能只是局部最优。

它不是决定性的

由于初始化是随机的,我们使用启发式算法,多次运行 k-means 可以返回非常不同的结果。

它受到离群值的严重影响

由于欧几里德距离是两点之间的差的平方,异常值会严重影响结果。

随着维度的增加,它的伸缩性很差

随着维数的增加,它们之间的欧几里德距离变得非常相似。这就是众所周知的维数灾难。但是如果距离不再有意义,我们的算法也会崩溃。

k-means 做出的假设

与任何算法一样,有一些明确或隐含的假设

聚类是球形的/到中心点的方差是均值

由于距离度量是对称的,它将总是找到具有球形边界的聚类。请注意,这并不意味着它适用于所有圆形的点云,例如考虑环形集群。k-means 将无法从外环中分离内部点云。

集群大小相等

有几篇关于堆栈溢出的帖子和帖子在讨论 k-means 是否假设集群应该大小相等。基本度量标准没有明确地做出这种假设,但是从算法的工作方式可以看出这是有意义的。假设我们有两个大小非常不同的集群。因为我们随机选择初始中心,所以我们有很高的概率在大的集群中初始化这两个中心。小集群将无法“吸引”其中一个中心,从而导致错误的结果。为了了解实际情况,让我们构建两个大小分别为 50 和 10000 的集群:

尺寸同样重要

这与星团大致呈球形的假设方向一致。如果一个维度的规模比其他维度大得多,它将主导聚类。因此,在聚类之前对数据进行规范化非常重要。

与其他聚类方法的关系

K-means 是许多不同聚类变体发展的起点:

k-表示++的意思

k-means++使用了改进的初始化。它不是随机选择所有初始中心,而是随机选择第一个中心。然后,选择所有后续中心,使它们在新中心和所有现有中心之间具有最大距离。

k-中间值

k-medians 选择中位数而不是平均值来计算聚类中心。通常,它使用曼哈顿距离而不是欧几里德距离作为其度量,但也可以使用更复杂的度量,如 Kullback-Leibler 散度。

k-mediods/PAM/k-center

k-mediods 选择聚类中最有代表性的点作为中心,因此中心始终是数据集中的一个实际数据点。这不要与 k-中值混淆,k-中值的中心不必是实际的数据点。这是可以看到的,因为二维的中线可能不是来自同一点!

它还使用了一个略有不同的迭代步骤:在初始化之后,对于每个聚类,它尝试将其包含的每个点作为潜在的新的聚类中心,并挑选一个最小化所选择的相异度度量的点。

最后,它在想要使用的不同度量方面也非常灵活。

模糊 c 均值

它不是硬聚类,而是分配每个点属于给定聚类的概率。这意味着一个点可以是多个簇的成员,这在某些情况下可能是有意义的(例如基因属于多个途径)。

高斯混合模型

也可以将 k-means 解释为不考虑协方差的高斯混合模型的硬聚类变体。期望步骤是将每个观察值分配到其关闭中心的步骤。最大化步骤是重新计算中心的步骤。

摘要

总之,我们已经看到 k-means 是一种简单而直观的算法,可以在基本上任何编程语言中快速应用于数据集。它的主要缺点是需要输入预期的聚类数。尽管如此,它的丰富性和简单性使它成为一个有用的工具,可以快速地在您的数据上运行它以使您熟悉它。

如何利用移动数据了解公民的习惯?

原文:https://towardsdatascience.com/a-deep-dive-into-mobility-data-6c76926712be?source=collection_archive---------35-----------------------

使用瑞士一天的手机数据的每日趋势和城市分类。

作者图片

从 1 天的移动数据中可以了解到什么?这是我将在这篇博文中回答的问题。我将首先关注数据获取和处理,然后看一看可以从这些匿名数据中获得的洞察力。

我使用的数据来自于 Swisscom Mobility Insight 平台免费试用。瑞士电信是瑞士最大的电信公司。每天大约有 200 亿次手机和网络之间的交互被记录和汇总。这些信息有助于更深入地了解人们如何移动、旅行和生活。

提供了整个瑞士的多个指标,并聚合到 100m x 100m 的切片。可以获得诸如男女性别比例、年龄分布和人口密度等信息。对数据进行 k-匿名化,以避免泄露可能允许识别个人的信息。

所有这些信息都可以从 Mobility Insight 平台热图 API 中每小时或每天获得。由于免费试用只提供 2020 年 1 月 27 日的数据,这将是我关注的一天。

以下代码查询 Swisscom API 并获取瑞士任何给定城镇的数据。由于查询 API 是一个非常耗时的过程,所以决定将重点放在瑞士的部分城市上。用于查询 API 的 python 代码可以在 Github 上找到。

在清理了数据并在 Pandas DataFrames 中做好准备后,这里是我研究的一些见解。

男性/女性比例

Alex Iby 在 Unsplash 上拍摄的照片

y 轴(计数)表示与 x 轴匹配的图块数量。

直方图中男性的比例(%)——按作者分列的图像

用箱线图表示上述数据可以更容易地比较观察到的数值范围。

从上图中我们可以看到,在猫的领地中,每块地上雄性的比例(%)。—作者图片

不出所料,我们可以观察到正态分布。检查你认为正确的事情总是好的,你永远不知道什么时候会出现一些奇怪的事情,迫使你更深入地挖掘数据,以获得一些有趣的见解。

一段时间内的年龄分布

杆长在 Unsplash 上拍照

可用的数据包含城市中年龄分布的每小时人口统计数据。数据分为 4 类:

作者图片

洛桑

作者图片

苏黎世

作者图片

在这两个大城市可以看到的趋势是,在工作时间,城市里的年轻人越来越多。

人口密度

图片由 Ishan @seefromthesky 在 Unsplash 上拍摄

随着时间的推移,人口密度的演变似乎是一个潜在的有趣想法。我不知道该从这些数据中期待什么。y 轴表示密度,x 轴表示一天中的时间

人口密度得分在[0,1]之间标准化,以显示城镇中密度的演变趋势,而不是每个城镇之间的密度差异。—作者图片

上面的图表有点拥挤,但它确实显示了至少有两种截然不同的行为。一些城市白天的人口密度似乎有所增加,一些城市似乎有所减少。为了更清楚地了解上面的数据,我们提取了一些城市的非标准化密度图表:

滑雪胜地——作者图片

作者笔下的郊区

大型城镇—作者图片

第一批图表和其他图表有很大不同。Laax 和 Saas-Fee 的惊人图表似乎可能的假设是,我们正在观察滑雪日开始和结束时涌入的滑雪者。似乎这些度假胜地的大多数滑雪者在别处过夜,并往返于度假胜地去滑雪。距离 Saas-Fee 17 公里的 Zermatt 镇的天气向我们展示了一个晴朗的日子,有几朵分散的云和最高零下 6 摄氏度。这是跑步和享受滑下斜坡的美好一天的完美天气。

被分析的其余位置是较大的城镇或郊区。对所观察到的行为的一个可能的解释是,市民在晚上离开之前在白天通勤到这些地方。

使用 DBSCAN 进行聚类

使用给定城市的可用数据作为特征向量,并在运行 DBSCAN 之前对其进行归一化,我们可以看到出现了多个集群。所使用的特征向量是使用按年龄类别划分的男性比例的日平均值以及我们之前刚刚看到的小时密度构建的。DBSCAN 产生了以下聚类:

使用 PCA(主成分分析)进行可视化,以提供有意义的可视化。集群 0,1,3 都是滑雪场。第二类是工业很少的城镇,但有许多人居住和通勤上班。集群 4 是较大的城镇。—作者图片

查看集群 2 的人口密度

作者图片

从上面的探索中,我们已经表明,通过简单地使用往返于这些位置的市民的移动性信息,可以将城市分为大的类别,如郊区、旅游滑雪场或大城市。

结论

移动数据是一个很好的信息来源,可以用来了解我们城市的功能。

从我们的分析中,我们观察到,查看移动数据可以让我们识别有滑雪的城镇。人口统计还显示,年轻人在上学期间前往城市中心。

在 SARS-COV2 疫情期间,这一数据变得更有价值,因为它提供了一种数字方法来衡量瑞士公民习惯的变化。

用于产生这个数据故事的所有代码和笔记本都可以在 Github 上的这里获得。

深入探讨 Neo4j 链接预测管道和 FastRP 嵌入算法

原文:https://towardsdatascience.com/a-deep-dive-into-neo4j-link-prediction-pipeline-and-fastrp-embedding-algorithm-bf244aeed50d?source=collection_archive---------7-----------------------

了解如何在 Neo4j 图形数据科学库中训练和优化链接预测模型,以获得最佳结果

在我的上一篇博文中,我介绍了 Neo4j 图形数据科学库中最新可用的链接预测管道。自从这篇文章发表后,我花了更多的时间来深入挖掘和了解管道的内部工作原理。我一路走来学到了一些东西,想和大家分享一下。首先,我打算展示链接预测管道如何组合节点属性来生成链接预测模型的输入特征。然而,当我开发内容时,我注意到了一些关于使用 FastRP 嵌入算法的见解。因此,在这篇博文结束时,您将有望了解更多关于 FastRP 嵌入模型的知识,以及如何将多个节点特征组合起来作为链接预测模型的输入。

这篇文章中使用的代码可以在 GitHub 上获得。

图形导入

我必须找到一个小的网络,这样我就可以很容易地想象我们进行的结果。我决定使用《权力的游戏》第一季电视节目中的互动网络,该网络由安德鲁·贝弗里奇提供。

图形模型。图片由作者提供。

图形模型由角色及其交互组成。我们将交互关系视为无向的,其中人物 A 与人物 B 交互,这直接暗示人物 B 也与人物 A 交互。我们也知道两个人物交互了多少次,并且我们将该信息存储为关系属性。

如果你想跟随这篇文章中的例子,我推荐在 Neo4j 沙箱中使用一个空白项目。它是 Neo4j 数据库的免费云实例,预装了 APOC 和图形数据科学插件。

GitHub 上提供了数据集,因此我们可以使用以下 Cypher 查询轻松地将其导入 Neo4j:

LOAD CSV WITH HEADERS FROM "https://raw.githubusercontent.com/mathbeveridge/gameofthrones/master/data/got-s1-edges.csv" as row
MERGE (s:Character{name:row.Source})
MERGE (t:Character{name:row.Target})
MERGE (s)-[i:INTERACTS]-(t)
SET i.weight = toInteger(row.Weight)

链路预测流水线

在引擎盖下,Neo4j 中的链接预测模型使用逻辑回归分类器。我们正在处理一个二元分类问题,我们想要预测一对节点之间是否存在链接。在高层次上,链路预测流水线遵循以下步骤:

图片由作者提供。

在这篇文章中,我们将关注前两个步骤,所以让我们仔细看看那里发生了什么。

链接预测流水线的特征工程。图片由作者提供。

作为第一步,您必须定义节点特性。例如,您可以使用自定义节点属性,如年龄或性别。还可以使用 PageRank 或介数中心性等图算法作为初始节点特征。在这篇博客文章中,我们将从使用 FastRP 节点嵌入来定义初始节点特性开始。FastRP 嵌入算法的优点在于,它可以捕获网络信息,并保持图中相邻节点之间嵌入空间的相似性。目前,您不能使用成对信息作为输入要素,如共同邻居的数量或一对结点之间的最短路径的长度。

在第二步中,链接要素组合器根据一对结点属性创建单个要素。目前,有三种技术可用于将一对节点属性合并到单个链接特征向量中:

  • 余弦距离
  • L2 或欧几里德距离
  • 哈达玛乘积

在上面的例子中,我使用了 Hadamard 乘积将一对节点属性组合成一个链接特征向量。所有可用的链接特征组合器技术都是顺序不变的,因为链接预测流水线目前仅支持预测无向关系。您可以在单个管道中使用多个链路特征组合器来定义几个特征向量,然后将这些向量连接起来作为链路预测模型的输入。稍后我会带你看一个例子。

定义结点要素和链接要素组合器后,您可以训练模型来预测新的连接。

节点特征工程

您可以在定义链接预测管线之前预处理节点特征。如果仅使用图算法(如节点嵌入或中心性测量)作为节点特征,也可以将它们直接包括在管道定义中。在第一个例子中,我们将使用 FastRP 嵌入作为我们的链路预测模型节点特征。因此,我们有可能将它们包含在管道定义中。然而,我们将首先对节点嵌入结果做一个简短的分析,因此在深入管道定义之前,我们需要将节点嵌入存储到图中。我们从投影一个无向图开始。请看一下文档以获得关于图形数据科学库内部工作的更多信息。

CALL gds.graph.create('gots1', 'Character', 
  {INTERACTS:{orientation:'UNDIRECTED', properties:'weight'}})

我们将使用 Louvain,一种社区检测算法,来帮助我们更好地理解 FastRP 嵌入算法的结果。您可以使用下面的 Cypher 查询将社区结构信息存储回数据库。

CALL gds.louvain.write('gots1', 
  {writeProperty:'louvain', relationshipWeightProperty:'weight'})

在这篇博文中,我将使用 Neo4j Bloom 来可视化算法和链接预测的结果。如果你想学习如何用 Bloom 可视化网络,请看一下本指南。

网络的社区结构。节点根据它们所属的社区进行着色。图片由作者提供。

现在我们可以继续执行 FastRP 嵌入算法了。该算法将为图中的每个节点产生嵌入或固定大小的向量。我的朋友 CJ Sullivan 给写了一篇很好的文章解释了 FastRP 算法的内部工作原理。

CALL gds.fastRP.write('gots1', 
  {writeProperty:'embedding', embeddingDimension:56, relationshipWeightProperty:'weight'})

首先,我们将使用 t-SNE 散点图可视化来评估 FastRP 嵌入。存储的节点嵌入是长度为 56 的向量,由嵌入尺寸参数定义。t-SNE 算法是一种降维算法,我们可以用它将嵌入维数降低到两维。长度为 2 的向量允许我们用散点图来形象化它们。我用于降维和散点图可视化的 Python 代码是:

该代码产生以下可视化效果:

FastRP 嵌入的 t-SNE 散点图可视化。使用 Louvain 算法,基于节点所属的社区对节点进行着色。图片由作者提供。

FastRP 嵌入和 Louvain 算法是独立执行的,但是,我们可以观察到 FastRP 嵌入在嵌入空间中聚集了相同社区中的节点。这并不奇怪,因为 FastRP 是一种基于社区的节点嵌入算法,这意味着在图中接近的节点在嵌入空间中也是接近的。接下来,我们将评估图中节点之间的余弦相似性。

MATCH (c:Character)
WITH {item:id(c), weights: c.embedding} AS userData
WITH collect(userData) AS data
CALL gds.alpha.similarity.cosine.stats({
  data: data,
  topK: 1000,
  similarityCutoff: 0.1
})
YIELD nodes, similarityPairs, min, max, mean, p25, p50, p75, p90, p95, p99
RETURN nodes, similarityPairs, min, max, mean, p25, p50, p75, p90, p95, p99

结果

图中所有节点之间的平均余弦相似系数约为 0.5。大约 25%的节点对具有大于 0.81 的余弦相似性。嵌入空间中的节点非常相似,因为我们有一个只有 126 个节点的小图。接下来,我们将评估由关系连接的节点对之间的余弦和欧几里德距离。

结果

用关系连接的大多数节点对具有高余弦相似性。同样,这是意料之中的,因为 FastRP 被设计成将网络拓扑结构转换成嵌入空间。因此,我们期望图中的相邻节点在嵌入空间中非常相似。我们将检查余弦相似度小于 0.5 的网络中连接的节点对,因为这有点出乎意料。首先,我们必须用 Cypher 标记它们:

MATCH p=(c1:Character)-[i:INTERACTS]->(c2:Character)
WHERE gds.alpha.similarity.cosine(c1.embedding, c2.embedding) < 0.5
SET i.show = True

现在,我们可以用 Neo4j Bloom 来想象它们。

余弦相似度小于 0.5 的节点对之间的关系。图片由作者提供。

看起来具有较低余弦相似性的连接节点对主要出现在我们在网络中的各种集群或社区之间具有连接时。如果你记得 t-SNE 可视化,相同社区中的节点在嵌入空间中被很好地分组。然而,我们在来自不同社区的节点之间有一些关系。当我们有来自不同社区的节点之间的连接时,它们的相似性会降低。这些节点似乎有更高的等级,这意味着它们在自己的社区内有许多链接,然后有几个链接指向其他社区。因此,它们更类似于其社区内的邻居,而不太类似于来自其他集群的邻居。

链接特征组合器

我们已经准备好嵌入,并且我们知道成对的连接节点很可能在嵌入空间中具有高余弦相似性。现在,我们将评估不同的链路特征组合器如何影响链路预测模型的输出。

余弦组合器

有趣的是,我们要看的第一个组合器是余弦相似性组合器。

余弦链路特征组合器。图片由作者提供。

链接要素组合器获取成对的结点要素,并将它们组合成一个链接要素,然后将其用作预测新链接的逻辑回归模型的训练数据。我们已经完成了余弦相似性分析,因此我们知道具有高余弦相似性的节点对很可能是相连的。因此,您可能会想象新的预测链接将位于具有高余弦相似性的尚未连接的节点对之间,因为这正是我们的训练数据看起来的样子。

我们将用于生成链接预测的 Python 脚本是:

这是一个有点长的脚本,因为我们需要为每个链接特征组合器选项定义整个链接预测管道。最后,预测的链接被存储到 Neo4j,这样我们就可以用 Bloom 来可视化它们。在我的前一篇博文中,我对管道做了一步一步的解释。如前所述,我准备了一个 Jupyter 笔记本,里面有所有的代码,所以你不用直接从博文里复制。

让我们使用余弦链接特征组合器来检查预测的链接。

使用余弦链接特征组合器预测的前 20 个链接。图片由作者提供。

具有预测链接的结点对的平均欧几里德相似度和余弦相似度为:

正如我们可以想象的,预测链接的节点之间的余弦相似性平均为 0.999。因此,结果与我们的训练数据一致。此外,我们可以从结果中了解一些关于 FastRP 嵌入的信息。低度的外围节点在嵌入空间中非常相似,即使它们不是直接相连的。例如,可视化图像左下角的蓝色节点都非常相似。所有四个节点只有一个关系,它们共享唯一的邻居节点。

L2 链接特征组合器

接下来,我们将看看 L2 链接功能组合器。L2 链接要素合并器计算两个结点要素之间的欧几里德距离。

使用 L2 链接特征组合器预测的前 20 个链接。图片由作者提供。

结果几乎与余弦链接特征组合器相同。看起来 FastRP 嵌入算法优化了网络中相邻节点之间的余弦和欧几里德距离相似性。

哈达玛链接特征组合器

哈达玛链接要素组合器使用哈达玛乘积来生成链接要素。哈达玛乘积就是简单的熵乘。

哈达玛链路特征组合器。图片由作者提供。

让我们使用 Hadamard 链接要素组合器来检查预测的链接。

使用 Hadamard 链接特征组合器预测的前 20 个链接。图片由作者提供。

具有预测链接的结点对的平均欧几里德相似度和余弦相似度为:

一些预测的链接类似于余弦和欧几里德链接特征组合器结果。例如,红色社区中的预测链接几乎完全相同。另一方面,该模型预测在蓝色集群的中心而不是外围有更多的链接。

使用多链路特征组合器

在前面的示例中,我们仅使用了一个链路特征组合器。在余弦或 L2 链接特征组合器的情况下,我们有效地仅将单个输入特征用于逻辑回归模型。实际上,拥有多个最能描述您的领域的输入要素是有意义的。作为演示,我们将添加优先附件输入作为第二个链接功能。看 Neo4j 中的文档,优先附着定义为一对节点之间乘以节点度。在实践中,优先连接模型假设具有较高节点度的节点更有可能形成新的连接。遗憾的是,我们还不能自动添加优先附件链接功能,但我们可以手动添加。为了添加优先附件输入特征,我们将首先计算所有节点的节点度值。有时您希望使用逻辑回归模型对输入要素进行归一化,因此我将向您展示如何在链接预测管道中直接缩放要素。然后,我们只需添加 Hadamard 链接特征组合器,它会将输入矩阵相乘,在本例中是节点度数。因此,如果我对数学的理解正确的话,得到的链接特征应该代表优先连接,因为我们有效地增加了节点对之间的节点度数。

在链路预测流水线中使用多个链路特征组合器。图片由作者提供。

在链路预测管道中,您可以拥有任意数量的链路特征组合器。然后,所有链路特征组合器的结果被连接成单个向量,该向量被用作链路预测逻辑回归模型的输入。我们可以将度数计算和缩放直接添加到管道中,而不必预先准备度数特性。

我们需要将以下三个步骤添加到我们的管道中。第一个查询将度值变异为命名图。第二步将使用 MinMax scaler 缩放度数值,并将其转换为 scaledDegree 属性。最后,我们使用 Hadamard 链接特征组合器来组合节点度数。最后,我们将快速评估添加二级链接要素对链接预测结果的影响。

我们使用具有 FastRP 嵌入的余弦链接特征组合器和具有缩放节点度的 Hadamard 组合器得到以下结果。

图片由作者提供。

添加二级链路功能会将预测链路从网络外围转移到中心。在某种程度上,这是有意义的,因为优先连接更喜欢度数较高的节点之间的链接,而这些节点通常位于网络的中心。

使用具有 FastRP 嵌入的欧几里德链接特征组合器和具有缩放节点度的哈达玛组合器,我们得到以下结果。

次要链接特性并没有真正影响结果。我还尝试不缩放节点度数值,然后预测的链接会强烈地向中心移动。

使用 Hadamard 链接特征组合器而不缩放节点度数。图片由作者提供。

如果我们在使用 Hadamard 合并器之前不缩放节点度数,优先连接特征将成为主导。因此,预测的链路是在具有高度数的节点之间,而不是在外围的低度数节点之间。

结论

我真的很喜欢写这篇博文,并且在这个过程中学到了很多关于 FastRP 嵌入算法和链接预测管道的知识。简要总结如下:

  • FastRP 更有可能在相邻节点之间分配相似度较低的高相似度
  • 另一方面,不同社区的连接节点之间的余弦相似度可以低于 0.5
  • 使用多个链接要素组合器可以帮助您更好地描述您的领域
  • 缩放节点特征影响链接预测逻辑回归模型的结果

我鼓励你开始一个免费的 Neo4j 沙盒项目,并开始在你的图表中预测新的连接。让我知道进展如何!

和往常一样,代码可以在 GitHub 上获得。

深入内乌顿

原文:https://towardsdatascience.com/a-deep-dive-into-neuton-dab72db4b2d0?source=collection_archive---------25-----------------------

机器学习

平台概述、其功能和使用示例。

作者图片

几天前,我发现了一个非常有趣的基于云的平台,名为内乌顿,它允许建立机器学习模型,无需任何编码或机器学习知识。在实践中,您可以通过三个简单的步骤开发一个高性能模型:

  • 上传数据集
  • 训练模型
  • 做预测

在这篇文章中,我给出了一个内乌顿的概述,它是如何工作的,以及一个它的用法的例子。内乌顿的免费版本在这里有,你可以试试:)

1 内乌顿概述

内乌顿是一个基于云的平台,它允许在没有任何机器学习知识的情况下开发高性能的机器学习模型。与其他现有解决方案相比,内乌顿不需要安装任何软件或库。唯一的要求就是在平台中创建一个账号并开始使用。

根据作者的说法,内乌顿基于一种神经网络 (NN) 独特的专利算法,该算法在不断交叉验证的训练过程中,从单个神经元自动增长神经网络,并在模型过度拟合之前停止。所以没有预定义的 NN 架构!很刺激,对吧?

此外,部署在内乌顿的型号可以很容易地嵌入到微型设备中,因为它们的尺寸很小

内乌顿的有趣之处在于其自我组织的能力,无需数据科学家的外部干预,即可进行数据预处理、参数调整等。内乌顿甚至警告说,如果一个模型已经过时,需要用一组新的数据进行训练。

2 与内乌顿合作

当你登录内乌顿时,你会看到一个非常友好的界面。您可以在旅程开始时添加新的解决方案(即项目)。一旦你创建了一个新的解决方案,你只需要执行三个简单的步骤:数据集,训练,预测。

作者图片

2.1 数据集

第一步涉及数据集上传。您的数据集必须满足数据集要求,包括(但不限于)以下方面:

  1. 数据集必须是 CSV 文件,其中第一行必须指示列名。
  2. 文件格式必须是 utf-8。

数据集是否已经用数据预处理或特征工程技术进行了预处理并不重要。上传一个合理组织的数据集就足够了,数据集也可能包含缺失值、重复值等。无论如何,内乌顿也提供了一个高级模式,在这里你可以执行基本的操作,启用/禁用数据预处理和特征工程,训练时间限制和时间序列分析设置。

下一步包括在数据集的可用列中选择目标变量。然后,您可以选择任务类型(回归、二元分类或多类分类),以及要考虑的度量标准。您可以从许多指标中进行选择,如下图所示:

作者图片

2.2 培训

在内乌顿训练是最简单的任务。你所需要做的就是按下按钮开始训练。

作者图片

云中会创建一个临时虚拟机来执行您的培训任务。培训可能需要一些时间。但是,有一个进度条显示完成的百分比。也可以在训练操作结束时通过电话接收消息

作者图片

2.3 预测

一旦模型被训练,相应的虚拟机就被关闭,并且该模型可以被用于执行预测或者被下载以被部署用于生产。

必须通过界面中的特定按钮启用预测:

作者图片

预测可以在三个级别上执行:

  • 网络预测
  • REST API 访问
  • 可下载的解决方案

预测结果可以导出为 CSV 文件,也可以在内乌顿进行分析。

2.4 模型质量评估

内乌顿提供了一个可解释办公室,即一套允许解释和评估模型的工具。

解释根据模型解释器和特性重要性矩阵,提供对模型逻辑的理解。

模型解释器解释其预测背后的模型原理。此外,该工具还回答了“如果...会怎么样”的问题:“如果我更改了任何要素的任何值会怎么样-预测会如何变化……”这是理解数据的一个很好的机制,一定要尝试一下。

顾名思义,重要性矩阵显示了模型中每个特性的重要性。重要性矩阵以有序条形图的形式提供,其中显示了前 10 项或后 10 项功能,如下图所示:

作者图片

模型评估在流程的每一步执行,从数据集上传、探索性数据分析到最终应用,在最终应用中,您可以根据新数据验证模型。下图显示了如何在流程的每个阶段执行模型评估:

作者图片

所有提供的图表都经过精心策划,易于阅读。下图显示了由内乌顿 EDA 提供的关联热图示例:

作者图片

3 用法示例

作为一个数据集示例,我将测试 Kaggle 上的钻石数据集,这是我在 Bex T. 发表的一篇题为厌倦了陈词滥调的数据集?这里有来自所有领域的 18 个令人敬畏的选择。

Diamond 数据集拥有 50,000 多条记录,可用于回归和多类分类任务。在我的例子中,我关注回归。

作者图片

数据集包含分类字段和数值字段。作为目标列,我考虑价格。

一旦数据集上传到内乌顿,预处理就会以惊人的速度自动执行。下图显示了预处理后数据集的一些列:

作者图片

有趣的是,切割特征,最初是一个分类特征,现在已经被分割成许多数字特征。这个过程称为一次热编码。此外,所有的数字特征已经自动地标准化和标准化。

内乌顿的惊人之处在于,只需点击几下鼠标,预处理任务就能完成,不需要人工分析数据集!

为了训练模型,我选择优化 RMSE 度量。训练过程可能需要一个多小时,训练结束后,会显示以下雷达图:

作者图片

整体模型质量指数为 88%,MAE 为 320.32。与相应的最高票数的 Kaggle 模型相比,其中 MAE 为 731.44,内乌顿表现更好。

摘要

在这篇文章中,我描述了内乌顿,一个非常强大的机器学习平台,它不需要任何机器学习或类似的知识。得益于专有的神经网络算法,内乌顿实现了非常高的性能,这引发了以下问题:

是否仍然有必要使用传统的机器学习方法来开发预测模型,或者像内乌顿这样的平台可能是未来的发展方向?

你可以在评论里回答这个问题。

如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的信息。

你愿意支持我的研究吗?

你可以每月订阅几美元,解锁无限的文章——点击这里。

额外奖金

由达里奥·拉德契奇撰写的一篇非常有趣的文章,题为 NeutonAI:介绍一个非常有效的无代码 AutoML 平台解释了如何一步一步地使用内乌顿网络界面。

相关文章

深入探究具有多模态神经元的 OpenAI 剪辑

原文:https://towardsdatascience.com/a-deep-dive-into-openai-clip-with-multimodal-neurons-a7dbbd963ef6?source=collection_archive---------25-----------------------

模型可解释性

探索剪辑的可解释性和可视化它的激活揭示了许多令人惊讶的事实!

照片由莫里茨·金德勒在 Unsplash 上拍摄

几个月前,OpenAI 发布了 CLIP ,这是一个基于转换的神经网络,使用对比语言-图像预训练来对图像进行分类。CLIP 的表现令人印象深刻,因为它使用了一种不寻常的方法,将文本和图像结合起来作为图像分类的输入。通常,只有一份报纸吹嘘新的最先进的性能,存档一些非常高的分数,就是这样。CLIP 最棒的是,前几天又出了一篇小论文,探讨 CLIP 的可解释性。

我们的论文基于近十年来对解释卷积网络的研究,从观察到许多经典技术可直接应用于 CLIP 开始。我们使用两个工具来理解模型的激活: 特征可视化 ,它通过对输入进行基于梯度的优化来最大化神经元的放电,以及 数据集示例 ,它查看来自数据集的神经元的最大激活图像的分布。

来源: OpenAI

这篇论文非常有趣,因为它揭示了许多有用的信息,有助于解释为什么 CLIP 表现如此出色。就我个人而言,我对神经网络可解释性的话题非常感兴趣,只是因为我最近了解到,将一个实际的人工智能模型投入生产是非常重要的。

使用特征可视化(我稍后会谈到),CLIP 的作者开始在两个层次上检查模型。第一个是神经元水平,所以他们会在网络中传递一些相似的图像,并检查相同的神经元是否被激活了相似的数量。这非常有趣,因为举例来说,如果你有一个对动物进行分类的网络,想象一下你有一个“狗神经元”,或者一个“猫神经元”。此外,如果在你完成分析后,你的网络未能对某种动物进行分类,你会知道去哪里找!

这里有很多要谈的,我想这篇文章会谈到很多领域。最有趣的事情之一实际上是看看激活图,由于许可问题,我不能在这里包括它们,但我强烈建议你去那里看看。另一个有趣的领域(也是我将在这里讨论的领域)是基于图像的神经网络的可解释性,以及它们从分析中实际获得了什么好处。

特征可视化

图片由作者提供。在左边,你可以看到一张组织病理学幻灯片。右边可以看到 DeepDream 激活图,白色像素显示高激活像素,黑色像素显示低激活。

因此,我通常会关注事情的“新闻”方面,我的文章最重要的结果是总结最近发布的模型。然而,在谈论 CLIP 之前,我想解释一下这里最常见的模型可解释性技术是如何工作的。我认为这将是非常有益的读者,因为它不会是一个剪辑具体的解释(因此可以适用于其他模型)。如果您不感兴趣,可以跳过“特性可视化”这一节

解释该系统的第一步是实现文献中的以下三种方法。虽然神经网络解释领域是一个相对较新的发展,但这些方法已经在一系列问题上取得了成功。

  1. 类显著图

这种模型解释方法包括根据给定图像的像素对该图像所获得的类别分数的影响来对该图像的像素进行排序。我们首先考虑一个使用线性模型的简化示例。对于矢量化的图像 I 和类别 c,通过求导我们可以看到,图像 I 的像素的重要性由权重向量 wc 的分量给出。这里我们看到了这个方法的关键思想。权重值的大小显示了图像中的哪些像素最需要改变以增加类别分数。

然而,我们实际上并没有使用线性系统来进行预测。事实上,CNN 应用的函数是高度非线性的,因此我们必须使用一阶泰勒展开来近似图像邻域中的得分函数

给定具有 m 行 n 列的图像 I,我们首先将图像转换成灰度,使得每个像素只有一个值。权重矩阵 w(通过执行单次反向传播得到)也具有 M 乘 n 的维度。对于图像 I 中的每个像素(I,j ),类别显著性图 M ∈ Rm×n 被定义为:

Mi,j = |wi,j|

这种方法比下面概述的两种方法快得多,因为只需要一次反向传播。

2。特定类别图像生成

该方法旨在根据模型生成的分数生成“代表该类”的图像。生成的图像是图像 I ,其最大化给定类别 c 的得分 Sc(I)。该图像也根据 L2 范数被正则化。

使用反向传播以类似于训练模型时的方式找到该图像 I。关键区别在于,现在我们不是保持相同的输入并优化权重,而是保持权重不变并优化输入(即图像中像素的值)。对于这种方法,图像被初始化为每个像素具有随机的 RGB 值。

3。深层梦境图像生成

深度梦图像生成和特定类图像生成的关键区别在于,起始图像不再是随机的。在深度梦的情况下,来自数据集的实际图像被用作初始图像。该方法的其余部分与特定于类的图像生成相同;我们简单地执行反向传播并找到相对于输入图像的梯度来更新图像。

上面概述的方法已经在论文中实现,主要是在来自 ImageNet 数据集的图像上实现。这些图像是日常物体,通常由清晰的前景和背景组成。

来源: Github

可解释性分析的结果

好了,现在让我们回到剪辑。因此,作者探索了模型的可解释性,并将神经元分为多个组:区域、人和情感神经元。请注意,他们的大部分分析是在剪辑的视觉方面,而不是文本方面。由此产生的分析显示了图像和它们的意义之间不寻常的联系。

比如对于情绪神经元来说,无聊会等于“放松+暴躁+日落”。此外,该模型在许多情况下显示出明显的偏差(因为数据集)。例如,“非法移民”似乎与“拉丁美洲”高度相关。我的观点不是重复你在他们的文章中读到的例子,而是可解释性分析的好处。因为作为一个机器学习开发者,这是相当有用的信息。

非常搞笑的失败案例

我相信你们很多人可能在媒体上看到过这个:

来源:https://openai.com/blog/multimodal-neurons/

这基本上表明,尽管 CLIP 对苹果进行了正确的分类,但简单的“纸笔激活攻击”就可以欺骗网络,使其认为这张图片是 iPod。这让我大开眼界,因为它表明尽管 CLIP 是一个强大的模型,但它很容易被愚弄。我认为这对当前的许多人工智能系统来说都是正确的(我想知道埃隆·马斯克如何看待人工智能将接管世界)。我并不想打击人,我认为我们意识到这样的问题是一件好事,这样我们就可以解决它们,这也是本文的主要目的之一,促进更多的可解释性研究。此外,我认为分析可以使神经网络失败的攻击是这种研究的一个重要方面。

显微镜

最后,他们发布的最有趣的东西之一是 OpenAI 显微镜。这是一个工具,您可以使用它来亲自查看这些激活!

最终想法

我希望你喜欢这篇文章,我尽量不让它太长。我认为我希望你能从中获得的最重要的成果之一是在你的机器学习项目中有一个小的可解释性部分。这将有助于你深入理解深度学习,并将提供你可以展示的极其有价值的图像(除了你的分数和指标之外)。

深入探究水母周围的分区

原文:https://towardsdatascience.com/a-deep-dive-into-partitioning-around-medoids-a77d9b888881?source=collection_archive---------13-----------------------

实践教程

k-means 的最终进化以及为什么你之前可能学错了

阿丽娜·格鲁布尼亚在 Unsplash 上的照片

在我作为一名数据科学家的工作中,我经常遇到简单算法不够用的问题,因为它们陷入了局部最优。这通常会导致开发过程中的许多挫折,因为您首先认为您的方法是有效的,但随后发现它并不一致,或者不是对您的所有数据集都有效。在我的 k-means 及其变体系列的最后一篇文章中,我将讨论 k-medoids 算法,通常也称为围绕 medoids 划分(PAM)。它具有基本确定性的优点,能够可靠地找到非常好的解决方案。这确实以更高的计算成本为代价,但是如果您的数据集不是非常大,如果您需要可靠的结果,它仍然是一个很好的候选。像往常一样,你也可以在我的 GitHub 上找到这篇文章的所有代码。

k-medoids 的直觉

与 k-中位数一样,k-medoids 也通常使用曼哈顿度量,但中心现在总是数据集中的实际点。我们现在计算中点,而不是质心,因此也就是中面。这增加了该方法的可解释性,因为数据中的代表性点总是可以被检索到。这经常与 k-medians 混淆(你可以在下面的文章中查看),在 k-medians 中,中心点不需要是一个实际的对象。

考虑以下示例集群,由二维的 5 个点组成:

作者图片

因为中位数是在 k-中位数中为每个维度单独计算的,所以中位数将是 x = 3,y = 3。但是在数据集中不存在点(3,3)。

针对 k-medoids 实现了多种算法,最常见的算法还是 Lloyd 风格的算法(也称为 Voronoi 迭代)和围绕 medoids 的真实划分(PAM)。不幸的是,Lloyd 风格的算法通常也被称为 PAM,但这是不正确的,因为 PAM 的构建阶段(我们将在后面看到)与 Lloyd 非常不同。真正 PAM 的构建阶段是该算法成功的关键步骤,这也是为什么 Llody 风格的 k-medoids 通常比 PAM 得到更差的结果。

k-medoids 劳埃德风格

为了简单起见,让我们首先以 Lloyd 风格实现 k-medoids,然后在此基础上构建真正的 PAM。像往常一样,我们首先随机初始化中心,
,但是现在中心的更新是完全不同的。

更新步骤现在称为交换阶段。顾名思义,我们考虑将当前的 medoid 与其簇中的所有其他点进行交换。对于每个候选交换,我们计算总成本,即该簇中所有点到新 medoid 的距离之和。我们会记住所有成本较低且表现最佳的互换,即成本最低的互换。

如果成本不能再降低,则算法终止。请注意,这并不意味着我们达到了一个全球最小值。因为我们只执行降低成本的步骤,所以如果算法没有在全局最小值“谷”内初始化,它就没有办法走出局部最小值并进入全局最小值。

围绕水母的分割(PAM)

最后是 PAM 算法。正如我之前已经暗示的,它有一个独特的构建阶段,可以确保非常好的初始化。下面的交换阶段与我们之前在 Lloyd 风格的 k-medoids 中实现的相同。

在构建阶段,选择第一个 medoid 作为具有最小成本的 med oid,成本是到所有其他点的所有距离的总和。因此,第一个点是数据集的最中心点。

然后迭代地选择所有进一步的点。对于所有非中面体,我们计算选择该点作为下一个中面体的成本(也是从候选中面体到所有其他非中面体的距离之和),然后选择具有最小成本的一个作为下一个中面体。

为了澄清这确实是真正 PAM 算法,您可以在这里查阅最初发明它的作者的
论文或书籍。

正如人们立即看到的,这是计算昂贵的执行。在我们的实现中,我们将在每次迭代中计算所有的距离,一个不太昂贵的解决方案是只计算一次距离矩阵(并且只计算一个三角形,因为它是对称的),然后只根据成本计算的需要对其进行索引。

这种算法的优点是详尽的构建阶段通常已经达到了非常好的聚类。在收敛之前,下面的交换阶段通常只执行几次。作者甚至指出,有时甚至可以忽略它,但仍然可以获得良好的分区。

在 Lloyd style k-medoids 和 true PAM 之间的交换阶段也有一些差异:Lloyd 只考虑同一个集群内的交换,而 PAM 考虑所有当前的非 medoids 进行潜在的交换,而不管它们当前是否在同一个集群内。这增加了 PAM 的搜索空间,并有可能使它找到更好的解决方案。

PAM 的另一个特性是它接近于确定性的,
因为它在初始化时不使用随机元素,并且总是将所有点视为可能的下一个 medoids。由于所考虑的两个 medoids 的成本之间可能存在联系,因此根据这些联系的解决方式,该算法并不是 100%确定的(即,可以随机解决联系,或者根据点出现的顺序来解决)。)

算法之间的比较

在整个系列中,我们已经实现了许多不同的算法,让我们就运行时间和结果对它们进行一些比较。因为我们在 base R 中实现了所有东西,而没有利用矢量化,所以运行时间将比使用 C 或 FORTRAN 中构建的优化算法长得多。

聚类结果

让我们从可视化结果开始。当然,“相同”聚类的颜色在不同的算法之间可以不同,因为它们不知道哪个聚类属于哪个物种。

iris 数据集上所有 k 均值变量的比较。所有算法都能找到大致正确的分割。请注意,颜色不需要匹配,因为它们彼此独立运行。图片作者。

除了 k-中值之外,大多数算法都能找到或多或少正确的聚类。我们还看到 PAM 算法实际上根本不执行任何交换,这突出了它在构建阶段的优势!
还要记住,如果比较 PAM 和 Lloyd k-medoids 之间的迭代次数,PAM 每次迭代只执行一次交换,而 Lloyd k-medoids 对每个当前 medoids 执行一次交换,因此总交换次数为 k *次迭代。

如果您想更多地了解可以用哪些客观指标来判断您的聚类结果,请查看我的文章中关于 k-means:

[## 对 k-means 的深入探究

towardsdatascience.com](/a-deep-dive-into-k-means-f9a1ef2490f8)

运行时间

最后,让我们比较不同算法的运行时间,让我们也
检查一下从 R 到 FORTRAN 的实现速度有多快:

我们实现的不同算法的运行时间比较。PAM 的运行时间最高,其次是 Lloyd 风格的 k-medoids。图片作者。

正如所料,PAM 是最慢的算法,其次是 Lloyd 风格的 k-medoids。因为其他线在标尺上非常接近,所以让我们来看看比率:

对数级运行时间的比较。与我在 R. Image by author 中的实现相比,我们可以观察到在 FORTRAN 中实现的基本 k-means 有很大的不同。

我们的普通 k 均值算法比基本 k 均值算法慢 4000 倍!这个
演示了如果用 C++这样的低级语言更有效地实现一个算法,你可以获得巨大的性能增益。但是我们的目标不是效率,而是理解。

摘要

恭喜你能走到这一步。有了 PAM,您现在知道了一种非常复杂的聚类方法,它可以稳健地应用于许多数据集。由于其高计算成本,它可能不完全适合非常大的数据集。如果你是这种情况,那就去看看专门为此设计的算法,比如 CLARA 或 CLARANS。这篇文章也总结了我关于 k-means 和相关聚类算法的迷你系列。请继续关注接下来的节目!

对足球语言的深入探究

原文:https://towardsdatascience.com/a-deep-dive-into-the-language-of-football-2a2984b6bd21?source=collection_archive---------14-----------------------

模型可解释性

深入到 Player2Vec,解释足球动作,球员表现,语义。

Wix.com 大学的数据科学家 Ofir Magdaci

作者图片。

在之前的帖子中,我们了解了一个单词——嵌入 表示足球的全球语言 。在本文中,我们将在这些嵌入的基础上开发解释器,理解模型能够捕捉哪些方面,并阐述其结果。

本作使用的所有代码 都可以在 Github 上的 Football2Vec 库中获得(主要是讲解器和讲解器模块)。

先验知识

这项工作在很大程度上基于我的第一篇文章——使用 NLP 嵌入足球语言。建议先阅读它,因为它提供了上下文。

数据集

本工作中使用的数据基于 Statsbomb 开放数据集。数据集中的每场比赛都由球队元数据、比赛元数据(例如,舞台、体育场等)组成。),最重要的是,手动收集和标记事件数据。文档可以在数据集的 GitHub 存储库上找到。

激励和总结

在上一篇文章中,我展示了 Action2Vec,一个 Word2Vec 模型,它允许我们将足球语言的语义嵌入到一个 32 维空间中。此外,我介绍了 PlayerMatch2Vec,这是一个 Doc2Vec 模型,它产生 32 个大小的向量,代表特定比赛中的一名球员。最后,我通过对 PlayerMatch2Vec 表示进行简单的平均来展示 Player2Vec 模型。下面是它的样子:

图一。玩家 2 的 UMAP 互动情节。也可以在这里访问。图片作者。

在本文中,我们将深入探讨这些模型。我们将探索各种技术来理解它们,分析它们并解释它们的输出。我们的努力将集中在解释特定的结果和整个模型上。但在我们这样做之前,让我们首先掌握可解释性的含义及其对机器学习模型的重要性。维基百科在人工智能的背景下解决了这个问题:

可解释性:“可解释的 AI 是求解的结果能够被人类理解的人工智能(AI)。这与机器学习中的‘黑匣子’概念形成了对比,在黑匣子中,即使是它的设计者也无法解释为什么人工智能会做出特定的决定。”

解释者可以在决策、调试或检测偏差中起到至关重要的作用,特别是对于无人监督的问题,比如我们的问题(因为我们没有下游任务)。这是检查模型学到了什么,以及这些方面是否与领域相关的好方法。但也许最强的动机在于,大多数人不会相信他们无法解释或理解的模型。

像 Doc2Vec 这样的复杂模型可以捕捉简单模型(如逻辑回归(LR ))所不能捕捉的模式。然而,LR 是一个更容易解释的模型,允许直接访问特征的系数并理解它们在任何预测中的重要性。这篇文章的目的就是要缩小这个可解释的差距。

通常将解释者分为两种主要类型:

  1. 局部解释者,旨在解释个别预测或输出**。**
  2. 全局解释器描述模型的一个完整行为。它揭示了全局,从整体上验证了模型。

我将把重点放在四种可解释性方法上,包括局部的和全局的,我发现它们在实践中是最丰富和可靠的:基于表征的解释者、类比、相似性、和创造玩家的变化。

除了解释预测,使用这些方法,我们将能够为转会市场上的目标球员建立复杂的档案。例如,我们可以寻找像安托万·格里兹曼这样的球员,但要完成更多的运球,或者像菲利佩·科蒂尼奥那样用他较弱的脚踢得更好。

理解表征的维度语义

首先,我们将检查玩家的向量并探索可能的语义模式。由于每个维度都与玩家的属性有些关联,所以可以通过比较 32 个维度中每个维度的高值和低值来推断他们的潜在意义。

表 Player2Vec 表示中所选条目具有高/低值的球员示例(总共 32 个指数)。

有些图案很明显。例如,维度#21 似乎与游戏的一些攻击方面相关联。然而,最好的做法是让一个对足球领域有更深更广知识的领域专家参与到这个过程中来。

这个方法充当了模型如何在语义空间中放置玩家的全局解释器。这个解释器的输出是一组可解释的全局特征,我们可以用它来推断玩家的游戏风格或理解玩家之间的相似性。

行动的解释者——行动类比

词语类比:用行动解释行动

类比*是一种描述语言中单词之间语义关系的操作。这种关系代表了语言中包含的语义属性。所以,在某种意义上,类比可以作为工具来考察模型语义**。***

一个比喻的基本结构由**给出:**词 A1 →词 A2 ~词 B1 →词 B2。也就是说,单词 A1 到 A2 的关系与 B1 到 B2 的关系相同。所有符合这种关系的单词在语义上都是相似的。

英语语言模型中一个著名的类比是“国王对王后~男性对女性”。这个类比展示了英语中的性别关系以及皇室的地位。这种类比在忽略性别的语言中是无法模仿的,例如,我们的足球语言。从数学上来说,这个类比暗示着国王王后之间的距离雄性雌性之间的距离大致相同。

这个模型能够捕捉到足球的哪些方面?让我们用类比来找出答案!我将解释第一个例子的基本原理,并对所有其他例子使用相同的符号。澄清一下,所有的类比都是经过挑选的而不是

全类比示例:传球方向学习

如上所述, *A to A' ~ B to B ',形式的每个类比定义了一个关系,其中“to”代表距离,通过减去矢量计算。在该示例中,关系A-A’B-B’*表示通过角度的变换。使用这个类比,我们本质上是在问,如果我们对另一个给定的动作 B 应用与 *A 到 A’*相同的变换,结果会是什么(根据模型)。因此,我们可以这样描述这个类比: A - A' ~ B -?

为了便于分析,我们将反其道而行之:给定关系A-A’,以及一个动作B’,我们希望使用相同的关系找到最初的动作 B。经过一些基本的代数,寻找与短语最相似的动作时就遇到了类比: A - A' + B'

在通路方向类比的情况下,我们将从我们的词汇中选择一个随机通路 A ,并提取相同但方向相反的通路——A’。此单变化体现了 类比关系 。我们将选择与 A '方向相同的第二个随机路径,即 B '。

  • A = '(3/5,3/5):()| ground-long | left _ foot '—从球场中间向右的一次地面传球。
  • A' = '(3/5,3/5):()| ground-long | left _ foot '—同样的传球,但是向左。
  • B' = '( 2 /5,3/5) <传球>:()|低长|左脚'—从球场中间向右的一个地面传球。

给定这些输入,与类比最相似的三个动作,即符合 B 的最佳动作是:

  • ( 2 /5,3/5) <传球> 😦 )| -med | left _ foot’,余弦相似度= 0.761——向右一个高传球,从与*B’*相同的位置。
  • ( 2 /5,3/5) <传球> 😦 )| -long|left_foot ',余弦= 0.760——向右一个高长传球,从与 B' 相同的位置。
  • ( 2 /5,3/5) <传球> 😦 )| 落地 -long|left_foot ',余弦= 0.748——a 落地长传向右,从与 B' 相同的位置。

图 2:学习传球方向的说明性类比图。B1/2/3 是符合类比等式的最佳动作:A - A' + B' =?。实线代表 A 或 B,虚线代表 A '或 B '。绿色代表 A,A ',红色代表 B,B '。通过距离(短/中/长)由箭头长度表示。在这里,A '是与 A 相同的传球,但方向相反(左)。从后面一个位置看,b 和 A 是一样的。B1/2 是到 B 的镜像通道,具有不同的高度和长度。B3 正是镜像通行证。图片作者。

那么我们从中学到了什么?首先,似乎最佳配合,即具有最高相似性的动作,是具有更大距离和更向前方向的相同传球。第二个最佳选择是左脚向前短传。第三个最相似的字,其实是一个方向相反腿相反的镜像传球。确实有趣!

但是等等,预计最直观符合类比的镜像动作只排在第三,这是个问题吗?嗯,在我看来,一点也不:

  • 许多动作在数据中出现的次数很少,这使得很难将它们恰当地放置在语义空间中。老实说,如果它没有至少一次提到“更多的数据将产生更好的结果”,那么它就不是一篇真正的数据科学文章。
  • 所有三个候选人可能很适合这个类比,因为他们达到了非常高的余弦相似值,超过了数以千计的其他传球动作。
  • 通过简单的试探法确定通过方向,并指向方向类别的中间。不同的方法可能导致不同的结果。

总的来说,我们可以得出结论,模型很好地捕捉到了传递方向语义。既然我们已经了解了类比是如何建立的,让我们来回顾更多。

向前移动球学习

这个类比旨在捕捉将球向前推,更接近目标的基本概念。类比关系:传球→从更靠前的位置(更接近目标)同样传球。

图 3:向前移动球的说明性类比图。这里,A '是和 A 一样的传球,但是是从后面一个位置。b’是来自对称 y 区域的相同通路。所有候选人都符合这一类比,很好地反映了向前推进的概念。图片作者。

我发现看到动作位置和类型总是被恰当地捕捉是非常有趣的,而附加属性在不同的候选者之间略有不同。

脚比喻

类比关系:右脚传球→左脚同样传球。

*图 4:学习脚语义的说明性类比图。*这里,A’是和 A 一样的传球,但是用的是左脚。b '是随机的左脚传球。所有顶级候选人都是相似的右脚传球,表明符合类比。图片作者。

为了简单起见,我去掉了传球的技术属性。当找到相似的记号时,模型总是尊重它们,所以只要假设这些类比适用于任何技术和类型。

传球高度模拟

类比关系:高度较低的传球→高度较高的相同传球。

*图 5:学习身高语义的说明性类比图。*这里,A’是和 A 一样的传球,但是高度更大。b '是随机高通。所有的顶级候选人都是相似的,这表明这种类比是非常合适的。图片作者。

语境理解:理解目标

类比关系:被扑出的一脚射门→与进球结局相同的一脚射门。

*图 6:学习目标情境的说明性类比图。*此处,A '与 A 是同一个射门,其中 A '射门得分,而 A 射门被扑出。“b”是从相似的位置随机射门,导致进球。最佳候选镜头完全符合类比,因为这些镜头被错过或保存。此外,B1/3 代表了类比的另一种可能的解释:守门员救球创造的进球可以导致反弹进球,因此,也可以被视为助攻。图片作者。

这个真的让我大吃一惊。我认为 A 到*A’*反映了将糟糕的击球变得更好。这是描述它的一种方式。在禁区内的投篮通常会被扑出并反弹得分。所以,在某种程度上,这也是一种很好的辅助方式(许多 FIFA 游戏玩家会觉得很熟悉😉).因此,潜在的助攻可以符合这个类比,以及错过的高球投篮。

到目前为止还不错,但我们才刚刚开始。

玩家解说# 1——用玩家类比解说玩家

正如我们对动作所做的那样,我们可以产生玩家对玩家的类比。由于文档代表随机变量,其中每个都是特定比赛中的玩家,类比本身是随机变量。听起来令人困惑,因此,我对类比中每个玩家的比赛进行了抽样,并重复了十次类比。结果高度一致。

后卫对前锋的类比

我们可以通过计算这些球员之间的距离来研究一个简单的防守者和一个进攻者的关系。我们希望不同对的中后卫和前锋之间的距离是相似的。

皮克(中后卫)-苏亚雷斯(斯蒂克)+本泽马(前锋)~?

三名至少有 5 场比赛的顶级匹配球员:马克斯韦尔·卡贝利诺(左后卫)、约尔迪·阿尔巴(左后卫)和哈维尔·马斯切拉诺(中后卫)。

中心到侧面类比

我们可以对比相似位置的球员,一个在中路,一个在边路。例如,从中后卫到侧翼:

阿尔巴(左后卫)-皮克(中后卫)+拉莫斯(中后卫)~?

最佳匹配:丹尼尔·阿尔维斯(右后卫),卢卡斯·迪涅(左后卫)。

风格和技能类比

为此,我们将比较位置相似但打法或技巧不同的球员。

例如,我选择了西甲前锋安托万·格里兹曼,并测量了他与奥斯曼·登贝勒的距离——后者也是一名攻击手,但更倾向于边锋。他们之间的差异可以通过他们在球场上的平均位置,每场比赛的运球次数等数据很容易地指出来。然后,我选择了小内马尔·达席尔瓦·桑多斯,我(和模特)觉得他更像登贝勒,而不是格里兹曼。由此产生的类比是:格里兹曼-登贝勒+内马尔~?。

最佳匹配:佩德罗·罗德里格斯(Pedro rodríguez)——前锋(在巴塞罗那期间)。我印象深刻,但我也有偏见。那么——你觉得怎么样?让我知道!

图 7 :说明性玩家类比剧情。每个图中, B 值是拟合类比方程的前几名: A - A' + B' =?*。实线代表 A 和 B,虚线代表 A’和 B’。绿色代表 A,A ',红色代表 B,B '。*图片作者。

每个类比都有自己的深度,但是我们有真正令人兴奋的事情要做…就在现在…

玩家 2 号解释器——将玩家和动作结合成玩家变体

所以玩家的类比很好,但是让我们更进一步。我们有一个巨大的玩家代表集合和一个非常丰富的语言来描述它——让我们把它们混合起来。

**将玩家和动作结合起来,可以让我们为玩家产生无限的局部变化,**跨越一个或多个技能。例如,我们可以创造更多射门或传中的进攻变化,或者通过用成功的铲球代替糟糕的铲球来提高防守技能。这些变异可以作为解释者,类似于石灰。

让我们以托尼·克罗斯为例。根据我们的模型,与塞斯克·法布雷加斯相比,他更不像安德烈斯·伊涅斯塔。为什么?通过模拟足球空间中不同版本的克罗斯,每个版本都有不同的修改,我们可以看到他与伊涅斯塔之间的两个独特因素是**运球和得分。**听起来很有希望,但首先,三个评论:

  1. 结果一致性——在训练期间由 Doc2Vec 对不可见文档的推断表示是随机的。因此,我重复这个过程几次,取平均结果。
  2. 过滤低相似度的结果似乎是合理的(我过滤余弦相似度的结果< 0.7) and players with lower similarity scores than the inspected player himself.
  3. 文档设计:player 2 vec 模型是由一个球员和一场比赛定义的,跨场比赛取平均值来代表一个球员。在训练期间,模型嵌入了真实足球财产的本地文档。为了避免推理中的垃圾输入和垃圾输出,我们还应该提供有意义的、设计合理的推理内容。

我们可以在玩家文档上应用什么操作来产生有意义的变化?怎么才能做到呢?

简单方法——矢量求和

由于 Word2Vec 和 Doc2Vec 模型本质上是线性的,因此我们可以通过将玩家表示向量和特定动作集的特别文档相加来产生玩家变化。例如,我们可以对运球动作进行采样,并按照一种单词袋的方法将它们全部组装到一个文档中。

在第一篇中,我演示了这个方法的用法。这样我就能断定安德烈斯·伊涅斯塔+发件箱得分——运球~托尼·克罗斯。

尽管结果很有希望,但设计这些文档是一个微妙的过程。因此,我将介绍两种我感觉更舒服的方法:修改玩家动作和丰富玩家动作。

修改玩家动作

这里,我们对玩家的文档进行迭代循环,并执行一组给定的 **干预每个都持有一个 模式 以跟随,**和一个 修改函数来转换观察到的动作。当条件允许时,我们以概率 p 执行这个修改。我们可以遵循这种方法,在保留原生文档设计的同时,为玩家创造与游戏许多方面相关的无尽变化。

注意:由于这种方法依赖于改变现有的动作,信息变化要求球员在比赛中足够频繁地执行这个动作。

例如,我们可以创建 Frenkie de Jong 的变体,其中他的传球被转换为高海拔(图 8)。对于 p=0.1 ,大部分相似球员是蒂亚戈、法布雷加斯、阿瑟、拉基蒂奇等。余弦相似度值为> 0.95。当我们增加 p 时,他们的相似度降低,而其他球员的相似度增加,比如罗伯特·皮雷。当 p > 0.8 (球员动作剧烈变化率)最相似的球员其实都是门将,因为他们倾向于频繁使用高球。

这种修改在我们的二维简化图(图 8)中也非常明显。随着 p 的增加,我们可以观察到非标准的行为,因为相应的变化不同于所有的本地玩家。然而,这是一个提醒的好时机——它仅仅是数据的一个非常压缩的投影,仅此而已。代表的全部知识嵌入在所有 32 个维度中。

图 8 包括奥斯曼·登贝勒的另一个变体,减少了右腿的使用。干预 模式是右脚动作,修改器功能只是将动作的腿切换到左腿(图 8)。对于 p=0.1 ,最相似的三名球员分别是佩德罗·罗德里格斯、艾萨克·昆卡和克里斯蒂安·泰洛。然而对于 p=0.8 ,三个最相似的球员是艾萨克·昆卡、博扬·克尔奇和杰拉德·德洛费。克里斯蒂安·泰洛也使用双腿,现在已经退出了前三名。

这是所有这些变化在 2d 中的样子,使用我们心爱的 UMAP 投影:

图 8:数据集中所有玩家的 Plotly interactive player 2 vecUMAP投影,以及选定玩家的变化。球员按位置上色,而一些精选的知名球员按名字上色,可以直接筛选。缩放和全屏视图可用于阅读重叠文本。每个变异名称遵循相同的命名模式:<玩家名> _ <技能名> _ <使用概率>。图片作者。

顺便说一句,我还试图通过将玩家文档中观察到的每一个失败的动作以概率 p 转化为成功来提高玩家的技能水平。然而,这样做并没有产生任何有趣的发现。正如我几次提到的,我们的表征处理的是玩家做了什么,而不是他做得有多好。这很好地证实了这一说法。为了解决技能水平问题,该模型必须进行调整和扩展。

丰富玩家动作

对于我们想要也改变动作流行度的情况,我们可以从相关的动作族中添加单词,或者删除带有机会 p 的匹配动作。

像以前一样,我们迭代地遍历玩家的文档并执行一组给定的干预,每个干预都有一个要遵循的模式和一个丰富器函数来构建要遵循的期望动作。当条件允许时,我们以概率 p 执行这种强化,导致技能相关动作的更多出现。

做富民应该关心哪些事情?例如,如果我们想给一个球员添加更多的运球,我们应该在匹配的位置和适当的场合添加它们——比如在接到球之后。我们也希望避免不切实际的情况,如在自己的 5 米栏内运球。

为此,我使用 1-gram 模型确定了适当的情况,这基本上意味着我模拟了给定最后一个动作发生的概率,如在数据中观察到的。概率,p _ 运球,是每次动作后出现运球的概率:25%的运球发生在传球后,23%的运球发生在接球后,20%的运球发生在持球后,13%的运球发生在压球后,4%的运球发生在接球后。为了简单起见,我决定忽略所有其他动作类型的 15%的长尾,并跳过在团队自己的半场执行的动作。

那么,如果我们减少一个球员运球的次数,会发生什么呢?图 8 也展示了天才运球运动员安德烈斯·伊涅斯塔的这些变化。对于 p=0 :最相似的是蒂亚戈、亚瑟、法布雷加斯(余弦相似度> 0.9)。对于 0.02 < p < 0.2 ,余弦值仍然是> 0.9 但是顺序变了——理查德·普伊格和库蒂尼奥最相似。最后,当 0.2 < p < 0.5: 亚瑟达到了 0.97 的余弦相似度,而所有其他人都没有通过 0.7 的最小阈值。

使用玩家变化解释嵌入维度

我们也可以利用变异来了解表征维度。具体来说,我们可以计算所有变量类型的每个向量维数的方差。具有高变化值的尺寸实际上是在 p 的不同值中变化最大的尺寸,因此,与应用的修改关联最大。

图 9:不同转化技能的标准差柱状图。y 轴是标准偏差值,x 轴是制图表达的矢量维度。上面的图描述了所有的技能修改,而下面的图描述了所有的技能强化。由于规模上的显著差异,这两种方法被分开显示。总的来说,技能丰富触发了玩家向量表示的更高机会。“投篮”和“运球”是指通过提高动作的结果来提高技能质量。图片由作者提供。

我们可以从图 9 中了解到,不同的技能影响不同的维度。这也适用于技能相同,但干预类型不同的情况——修改还是丰富。总的来说,富集导致更高的方差,即,对表示的更显著的影响。

此外,我们看到一些技能比其他技能对向量维度有更高的绝对影响。例如,与修改传球高度相比,改变球员脚侧的数量级要低得多。

正如我之前提到的,仅仅改变动作的结果(见图 9 中的“射门”和“运球”技巧)会产生非常小的影响,几乎无法与纯粹的噪音区分开来。

因此,我们了解到一些元素在表示中比其他元素更占主导地位。对模型或预处理的扩展可以减轻这种情况。例如,为了赋予因改变拍摄结果而产生的差异以力量,我们可以为目标添加一个单独的单词,并将它们放在适当的位置。这将创建模型可以捕获的依赖关系。

还有一点:理解表示差异

就在我们到达这篇文章的第 90 分钟之前——你还记得我在第一篇文章中提到的,一个球员在空间中的扩散也可能意味着什么吗?所以,我决定去看看,这是它的样子:

图 10:Plotly interactiveUMAPplayer 2 vec 表征方差的投影。球员是按位置着色的。图片作者。

我们应该记住这仅仅是一个 32 维图像的 2D 投影(使用我们亲爱的 UMAP )。但是,我们可以看到一个明显的线性趋势。这种趋势有什么意义吗?事实证明,有可能。

我在各个位置上突出了一些顶级球员的名字。顺势而为,遇到这样的选手比逆势而为的可能性要大得多。这些球员在不同的比赛中会改变他们的行为吗?他们真的是更好的球员吗?当然,存在选择偏差,数据是不平衡的(因此方差受到影响),所以在得出结论之前有更多的东西要分析。同时,你可以交互式地探索它。

摘要

在这篇文章中,我们深入了解了足球语言和球员 2Vec 模型,我们了解了不同类型的解释者,包括全球和本地的,我们使用类比作为语义测量工具,包括动作和球员。

然后,我们转向了更先进的解释球员的方法。具体来说,我将修改和丰富** 玩家的概念引入到玩家的变体中,允许我们解释玩家的表现,以及实体之间的相似性和距离。**

使用哪个解释器以及出于什么目的由您决定,这取决于业务需求。正如我之前提到的,我们可以用例子、想法和不同的方法来填满一本书——还有许多其他的方法,其中一些可能会为一些用例提供更好的服务。

下一步是什么?

所以,我不得不就此打住。下一个帖子将主要是关于玩家技能评估,包括我承诺的花哨的 Streamlit 仪表盘。

如有任何意见、建议或要求,请随时联系我。

终场哨声响起。

对 ARIMA 模型的深入研究

原文:https://towardsdatascience.com/a-deep-dive-on-arima-models-8900c199ccf?source=collection_archive---------3-----------------------

实践教程

从白噪音到 SARIMAX 及其他

达斯坦·卡迪尔在 Unsplash 上的照片

预测未来永远是一个普遍的挑战,从决定是现在还是下周种植作物,结婚还是保持单身,出售股票还是持有,或者上大学还是全职玩音乐。我们永远不可能完美地预测未来[1],但是我们可以使用统计领域的工具预测来更好地理解未来。

预测涉及时间序列数据,或随时间的重复测量。在每小时的温度、每天的电力消耗或每年的全球人口估计等数据中,我们可以寻找将数百或数千个数字归结为几个定义性特征的模式。我们可以使用时间序列分析来量化数值上升或下降的速率,测量一个数值与前几个相关的程度,将我们的数据分解为其潜在的重复周期等等。

过去五年的 P 500 每日价格,一个被高度研究的时间序列的例子。截图来自谷歌财经。

为了总结时间序列并预测其未来,我们需要对时间序列中的值之间的关系进行建模。今天与昨天、一周前或去年有相似之处吗?时间序列之外的因素,比如噪音或者其他时间序列,起到了多大的作用?

为了回答这些问题,我们将从一个基本的预测模型开始,并迭代到一个完整的自回归移动平均(ARMA)模型。然后,我们将进一步包括 综合季节性外源性 组件,扩展成一个 SARIMAX 模型。

换句话说,我们将在此基础上构建:

对此:

这看起来很复杂,但每一部分——自回归的**(蓝色)、移动平均线(紫色)、外生的(绿色)和季节性的(黄色)——只是加在一起。我们可以通过添加或删除项,在原始数据和差分(红色)数据之间切换,轻松调整或降低模型的复杂性,以创建 ARMA、SARIMA、ARX 等。模特。**

一旦我们建立了一个模型,我们将能够预测未来的时间序列如下。但也许更重要的是,我们还将理解产生我们的时间序列的潜在模式。

作者图片

(强制说明:本帖不构成投资建议;所有的例子只是为了说明的目的。)

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

目录

  • 入门指南
  • AR:自回归
  • 马:移动平均线
  • 把它放在一起
  • 附加组件
  • Python 中的时间序列

入门指南

自相关

在我们开始构建任何模型之前,我们需要涵盖一个对于描述时间序列至关重要的主题: 自相关 。自相关意味着“自相关”:它是一个时间序列的值与早期值的相似性,或者说滞后于**。例如,如果我们的时间序列是值[5, 10, 15],我们的滞后-1 自相关将是[10, 15][5, 10]的相关性。**

我们可以用自相关图将当前值之前的值的相关性可视化。这些图是通过计算每个值(y_t)与前一时间步( y )、两步前(y)、三步前(y_)的值的相关性而构建的。y 轴显示滞后 n 处的相关性强度,我们认为阴影误差区间之外的任何值都是显著相关性。

滞后零点的相关性总是 1: y_t 最好和 y_t 完全相关,否则就有问题。对于剩余的滞后,有三种典型的模式:1)缺乏自相关,2)逐渐衰减,3)急剧下降。(尽管在现实世界的数据中,你可能会得到#2 和#3 的混合。)

作者图片

下面我们来看看芝加哥植物园每日标准普尔 500 收盘价(左)和每日最高气温(右)的自相关。标准普尔 500 指数的价格相关性如此之强,以至于你必须查看过去三个月以上的数据才能找到不相关的价值。芝加哥的气温变得不相关得更快,大约在两个月前,但随后从另一边射出,变成与 4-7 个月前的气温负相关的**

作者图片

偏自相关

自相关图是有用的,但滞后之间可能存在实质性的相关性“溢出”。例如,在标准普尔 500 价格中,滞后-1 相关性达到惊人的 0.994——很难获得后续滞后的良好读数,因为第一个滞后间接影响所有下游相关性。

这就是 偏相关 可以成为一个有用的度量的地方。部分自相关是 y_t 和 y_的相关性,控制早期滞后的自相关。

假设我们想要测量滞后-2 自相关,而不测量滞后-1 溢出。我们不是直接测量 y_t 和 y_的相关性,而是拟合 y _ t ~β₀+βy _ 的线性回归,y _ ~β₀+βy _ 的回归,然后找出残差之间的相关性

残差量化了 y_t 和 y_中无法用 y_解释的变化量,使我们能够公正地看待 y_t 和 y_之间的关系。然后我们可以重复这个过程,测量直到 n 的滞后的回归残差之间的相关性,找到滞后 n 的偏相关。例如,我们可以通过将 y _ t ~β₀+βy _ +βy _ 的残差与 y _ ~β₀+βy _ +β的残差进行回归来测量滞后 3 时的偏相关

这是标准普尔 500 价格和芝加哥温度的偏相关图。注意滞后-1 自相关如何保持高度显著,但随后的滞后从悬崖上跳水。

作者图片

自相关和偏自相关图可用于确定简单的 AR 或 MA 模型(相对于完整的 ARIMA 模型)是否足以描述您的数据[2],但您可能不会以这种方式使用它们。自自相关图首次推出以来的五十年里,你的笔记本电脑可能已经强大到足以执行强力扫描,以找到最能描述你的数据的 ARIMA(甚至 SARIMAX)模型的参数,即使有成千上万的观察值。因此,这些图作为可视化数据的时间相关性的补充方式可能更有用。

平稳性

与任何统计模型一样,预测时间序列数据时必须满足一些假设。最大的假设是时间序列是平稳的。换句话说,我们假设描述时间序列的参数不随时间变化。无论在时间序列的哪个位置,您都会看到相同的均值、方差和自相关性。[3]

作者图片

这并不意味着我们只能预测看起来像上面绿色乱糟糟的时间序列。虽然大多数真实世界的时间序列不是平稳的,但我们可以时间序列转换成平稳的,根据平稳数据生成预测,然后对预测进行解转换以获得真实世界的值。一些常见的转换包括求差(然后可能再次求差),取数据的对数或平方根,或者取百分比变化。

转换是必要的,因为线性模型要求它们建模的数据是独立的并且同样可能从母体中提取。时间序列数据则不是这样——任何自相关都会立即违反独立性假设。但是独立随机变量的许多便利之处——例如大数定律和中心极限定理—也适用于平稳时间序列。因此,使时间序列平稳是能够对我们的数据建模的关键一步。

照片由 Ahmad Odeh 在 Unsplash 上拍摄

AR:自回归

有了一些基础知识之后,让我们开始构建我们的 ARIMA 模型。我们将从 AR自回归成分开始,然后再加入移动平均线和积分。

AR(0):白噪声

我们能建立的最简单的模型是不带任何项的模型。嗯,差不多了。只有一个常数和一个误差项。

这种时间序列被称为白噪声。** ϵ_t 是从均值为 0、方差为 σ 的正态分布中抽取的随机值。[4]每个值都是独立绘制的,意味着 ϵ_tϵ_ϵ _{t+1}、或任何其他 ϵ _都没有关联。用数学方法写,我们会说:**

因为所有的 ϵ_t 值都是独立的,所以模型 y_t = ϵ_t 所描述的时间序列只是一个无法预测的随机数序列**。你对下一个值的最佳猜测是 c ,因为 ϵ_t 的期望值是零。**

下面是从标准差递增的正态分布中提取的三个白噪声时间序列。 c 为零,因此从等式中省略。

作者图片

我们无法预测的随机值的时间序列实际上是一个有用的工具。对于我们的分析来说,这是一个重要的零假设——数据中是否存在一种模式,足以将序列与白噪声区分开来?我们的眼睛喜欢寻找模式——即使实际上并不存在——因此白噪声比较可以防止假阳性。

白噪声也有助于确定我们的模型是否捕获了它可以从我们的时间序列中获得的所有 信号 。如果我们预测的残差不是白噪声,我们的模型忽略了一个模式,它可以用来生成更准确的预测。

AR(1):随机游动和振动

让我们开始在模型中加入自回归项。这些项将是我们的时间序列的滞后值,乘以将那些先前值最好地转化为我们的当前值的系数。

在 AR(1)模型中,我们通过取我们的常数 c ,加上由乘数 α ₁调整的先前时间步长 y_,然后加上白噪声 ϵ_t ,来预测当前时间步长 y_t。

₁的值在我们的时间序列中扮演着决定性的角色。如果 α ₁=1,我们得到一个t23】随机行走 。与白噪声不同,我们的时间序列可以自由地偏离它的原点。在许多应用中,随机漫步是一个非常有用的随机过程模型,例如建模粒子在流体中的运动,觅食动物的搜索路径,或者股票价格的变化。

作者图片

所以当 α ₁ = 0 时,我们得到白噪声,当 α ₁ = 1 时,我们得到随机游走。当 0 < α ₁ < 1 时,我们的时间序列展现出 均值回复 。这很微妙,但你会注意到这些值是相互关联的它们倾向于在零附近徘徊,就像不那么混乱的白噪声。这一过程的一个真实例子是股票价格的巨大变化:突然变化之后往往是均值回归。

作者图片

在拟合 AR 模型时,统计软件包通常在执行最大似然估计时将 α 参数空间约束为 1 ≤ α ≤1。除非你在模拟指数增长或剧烈振荡,否则这些模型描述的时间序列可能不是你想要的。

作者图片

最后,我们对 c 的解释随着 AR(1)模型的变化而变化。在 AR(0)模型中, c 对应于我们的时间序列的中心位置。但是由于 AR(1)模型考虑到了 y_, c 现在表示我们的时间序列趋势向上(如果c0)或向下(如果c0)的速率。

作者图片

AR(p):高阶项

给我们的模型增加更多的滞后只是增加αn * y 项的问题。这是 AR(2)模型的样子,附加项用蓝色突出显示。

这表示当前时间步长 y_t 的值由我们的常数 c 确定,加上前一时间步长 y_的值乘以某个数 α ₁,加上两个时间步长前 y_的值乘以另一个数 α ₂,再加上白噪声值 ϵ_t 。为了适应这种类型的模型,我们现在估计₁和₂.的值

随着我们在模型中加入更多的滞后,或者我们开始包括移动平均、外生或季节项,将自回归项重写为总和将变得有用。(统计学家也使用后移符号,但是我们将坚持使用总和法来避免学习曲线。)这里是 AR(p)模型的一般形式,其中 p 是滞后的数量。

上面的等式简单地说“我们的当前值 y_t 等于我们的常数 c ,加上每个滞后 y_乘以它的系数 α_n ,加上 ϵ_t 。”不管 p 是 1 还是 100,我们都可以使用相同的等式……尽管如果你的模型有 100 个滞后,你可能需要考虑加入我们将要描述的下一个术语:移动平均线。

照片由 HiveBoxx 在 Unsplash 上拍摄

马:移动平均线

ARIMA 模型的第二个主要部分是移动平均线部分。这个分量不是滚动平均值,而是白噪声中的滞后。

ϵ_t 术语,以前是我们添加到预测中的一些容易被遗忘的噪音,现在占据了中心舞台。在 MA(1)模型中,我们对 y_t 的预测是我们的常数加上带有乘数 θ ₁的先前白噪声项ϵ_{t−1},加上当前白噪声项 ϵ_t

如前所述,我们可以用下面的总结来简明地描述 MA(q)模型:

这里有三个 MA(1)时间序列,它们的值随着ϵ_{t−1}.的乘数 θ ₁的变化而变化如果你在看这些的时候没有经历一个“啊哈”的时刻,不要难过;它们应该看起来相当类似于白噪声。

作者图片

移动平均过程远没有自回归过程直观——什么时间序列不记得它过去的行为,但记得它以前的随机噪声?然而,数量惊人的现实世界时间序列移动平均过程,从商店商品和销售的错位,应对自然灾害的电池购买,以及低通滤波器,如汽车音响上的高音旋钮。

这里有一个愚蠢但有用的错位例子来自 YouTuber ritvikmath 。想象一下,在一个重复的聚会上,你被分配给每位客人一个纸杯蛋糕。y_t 是应该带多少个纸杯蛋糕。你预计大约有 10 个人,所以你从带 10 个纸杯蛋糕开始。当你到达时,你会注意到你供应过多或过少的纸杯蛋糕( ϵ_t)

在接下来的会议中,你带来了 10 个纸杯蛋糕,这 10 个蛋糕是根据与上一次会议(现在的ϵ_{t−1})的差异乘以某个系数( θ ₁).)而调整的你想把你上次掉的蛋糕数量变成负数,所以你设置了 θ ₁=−1.例如,如果你上次少了两个(ϵ_ = 2),你应该多带两个。**

出席的客人数量基本上是随机的,但是客人会记得上次会议的蛋糕数量**——如果蛋糕太多,就会有更多的客人出席,如果蛋糕太少,就会有更少的客人出席。**

因此,我们可以在每次会议中模拟正确数量的纸杯蛋糕,如下所示。蓝色部分是你带去参加会议的蛋糕数量,橙色部分是我们带去的蛋糕数量和出席人数之间的差额。

这里要注意的是这个时间序列并不关心自己的历史**(纸杯蛋糕的正确数量);它只受到短暂记忆的外部随机噪声的影响(纸杯蛋糕的数量和参加聚会的人数之差)。因此,这是一个移动平均过程。**

布鲁克·拉克在 Unsplash 上拍摄的照片

把它放在一起

在讨论了 AR 和 MA 过程之后,我们有了构建 ARMA 和 ARIMA 模型所需要的一切。正如您将看到的,这些更复杂的模型只是由 AR 和 MA 组件相加而成。

ARMA:自回归移动平均

虽然许多时间序列可以归结为纯自回归或纯移动平均过程,但您通常需要结合 AR 和 MA 分量来成功描述您的数据。这些 ARMA 过程可以用以下等式建模:

ARMA 方程简单地说明了当前时间步长的值是一个常数加上自回归滞后及其乘数之和,加上移动平均滞后及其乘数之和,再加上一些白噪声。这个方程是广泛应用的基础,从建模风速,预测财务回报,甚至过滤图像。

下面是四个 ARMA(1,1)时间序列。与上面的 MA(1)图一样,很难查看下面的任何时间序列并直观地了解参数值,甚至很难知道它们是 ARMA 过程而不是 AR 或 MA。我们现在所处的位置是,我们的时间序列已经变得太复杂,无法通过肉眼观察原始数据来直觉判断模型的类型或参数。

作者图片

但是没关系。接下来,我们将开始使用 AIC 来确定哪个模型最能描述我们的数据,是 AR、MA 还是 ARMA 模型,以及每个组件的最佳滞后数。我们将在这篇文章的最后讨论这个过程,但同时让我们讨论 SARIMAX 模型的剩余部分。

ARIMA:自回归综合移动平均线

我们来到了这篇博文的同名主题:ARIMA 模式。尽管如此,我们实际上会看到 ARIMA 模型只是一个 ARMA 模型,由模型而不是用户来处理预处理步骤。

让我们从 ARIMA(1,1,0)模型的等式开始。(1,1,0)意味着我们有一个自回归滞后,我们对数据进行一次差分,我们没有移动平均项。

注意我们是如何从对 y_t 建模到对 y_t 和 y_之间的变化建模的。这样,我们的自回归项,以前是 α(y_) ,现在是α₁(y _ -y _ )。

**换句话说,**ARIMA 模型就是差分时间序列的 ARMA 模型。就是这样!如果我们用 d_t 代替 y_t,代表我们的差分数据,那么我们就又有了 ARMA 方程。

我们可以自己进行差分,但是如果 d 大于 1,就会变得很麻烦,大多数统计软件包会为我们处理差分。例如,这里有一个 Python 演示,展示了对原始数据的 ARIMA(1,1,1)模型和对差分数据的 ARMA(1,1)模型的模型系数是如何相等的。

上面,我们首先使用arma_generate_sample函数来模拟具有指定 αθ 参数的 ARMA 过程的数据。然后,我们使用ARIMA函数来拟合原始数据的 ARIMA 模型和差分数据的 ARMA 数据。最后,我们比较了两个模型的估计参数,表明它们是相等的。

这是对原始数据的 ARIMA(1,2,1)模型与对经过两次差分的数据的 ARMA(1,1)模型的相同概念证明。

克里斯·劳顿在 Unsplash 上拍摄的照片

附加组件

有了 AR、MA 和 I 组件,我们就可以分析和预测大范围的时间序列。但是有两个额外的因素将真正把我们的预测提升到下一个水平:季节性外部变量。在阅读一些代码并结束这篇文章之前,让我们简单地介绍一下。****

学生:季节性

顾名思义,季节性是指在数据中以固定频率重复的模式:每天、每两周、每四个月等重复的模式。例如,电影院的门票销售往往与上周的销售相关,而房屋销售和气温往往与前一年的数值相关。

季节性违背了 ARIMA 模型的平稳性假设,所以我们需要对其进行控制。我们可以通过使用季节性 ARIMA 模型来实现。这些模型有着广泛的应用,从预测巴西的登革热病例到估计台湾的机械工业产出或希腊的发电量。

以下是 SARMA 模型的通用等式,季节性因素以橙色突出显示。(对于 SARIMA,用差异数据 d_t 替换 y_t。)

请注意,季节性和非季节性成分看起来非常相似。这是因为季节性模型在滞后偏移一定数量的滞后 s ,即我们季节性的频率上,符合一组额外的自回归和移动平均成分**。**

例如,对于具有强周季节性的每日电子商务利润模型,我们设置 s = 7。这意味着我们将使用一周前的值来帮助预测明天会发生什么。这个过程可以用如下的 SARMA(0,0)(1,0)₇模型来建模。

但对于现实世界的时间序列,即使是高度季节性的数据也可能更好地用一两个非季节性成分建模:季节性成分可能会捕捉到长期模式,而非季节性成分会调整我们对短期变化的预测。我们可以修改我们的模型,加入一个非季节性的自回归项,例如,把它变成一个 SARMA(1,0)(1,0)₇模型。

SARIMA 模型还允许我们根据季节频率来区分我们的数据,以及任何非季节差异。一个常见的季节性 ARIMA 模型是 SARIMA(0,1,1)(0,1,1),下面我们用 s = 7 来表示。滞后在等式的右侧,以便于阅读。

x:外来变量

**到目前为止,我们描述的所有组件都是时间序列本身的特性。我们的最后一个组成部分,**外生变量,通过考虑外部数据对我们时间序列的影响来抵消这一趋势。

这听起来不应该太吓人——外生变量只是任何非时间序列模型中的特征。例如,在一个预测学生考试成绩的模型中,一个标准的线性回归会使用像学习小时数睡眠小时数这样的特征。与此同时,ARIMAX 模型还将包括*内生特征,如学生以前的 n 考试成绩。*

ARIMAX 模型中其他一些外部变量的例子包括油价对美国汇率的影响、室外温度对电力需求的影响,以及关于伤残保险索赔的经济指标。

这是我们完整的 SARIMAX 方程的样子。外来术语用绿色突出显示。

请注意,外部因素的影响已经间接地包含在我们的时间序列的历史中。例如,即使我们在美国汇率模型中不包括石油价格项,石油的影响也会反映在汇率的自回归或移动平均成分中。任何真实世界的时间序列都是几十个或几百个外来影响的结果,所以为什么要用外来术语呢?

虽然在我们的模型中,外部效应是在内生项中间接表示的,但直接测量这些影响仍然更有效。例如,我们的预测将对外部因素的变化做出更快的反应,而不是需要等待它在滞后中反映出来。

Jonathan Chng 在 Unsplash 上拍摄的照片

Python 中的时间序列

到目前为止,我们已经介绍了时间序列模型的自回归、移动平均、综合、季节性和外生成分背后的理论和数学。只需一点点努力,我们可能就能为 SARIMAX 模型填写方程,并手工生成预测。但是我们如何找到模型的参数值呢?

为此,我们将使用 Python 的statsmodels库。我们将首先拟合一个模型,其中我们提前知道我们的模型阶应该是什么,例如一个自回归滞后和两个移动平均滞后。在下一节中,我们将展示如何使用pmdarima库来扫描潜在的模型订单,并为您的数据找到最佳匹配。

拟合模型

假设我们是某电子商务公司的数据科学家,我们想预测接下来几周的销售额。我们有去年每日销售额的 CSV,以及每天的广告支出。我们知道,我们希望我们的模型有一个非季节性的 ARIMA(1,1,1)成分,一个为期 7 天的季节性 AR(1)成分,以及一个广告支出的外部变量。

换句话说,我们知道我们想要一个 SARIMAX(1,1,1)(1,0,0)₇模式。下面是我们如何在 Python 中实现这样一个模型。

就是这样!我们用SARIMAX指定我们的模型,然后用.fit方法拟合它(必须保存到一个单独的变量中)。然后我们可以用summary方法得到一个 scikit-learn 风格的模型概要。predict方法让我们将模型样本内预测与实际值进行比较,而forecast方法为未来n步骤生成预测。

比较模型拟合

这些都很好,但是如果我们事先不知道我们的模型订单应该是什么呢?对于简单的 AR 或 MA 模型,我们可以分别查看部分自相关或自相关图。但是对于更复杂的模型,我们需要执行模型比较。****

我们通常与 AIC 进行模型比较,以衡量我们的模型与数据的吻合程度。AIC 通过惩罚更复杂的模型来防止过度拟合——是的,随着我们添加更多的项,模型的准确性不可避免地会提高,但这种提高足以证明添加另一项是合理的吗?

为了确定最佳模型,我们对各种模型订单执行参数扫描,然后选择具有最低 AIC 的模型。(如果我们特别担心过度拟合,我们可以使用更严格的 BIC 来代替。)

我们可以依靠 pmdarima 库中的auto_arima函数来完成繁重的工作,而不需要自己编写一堆for循环。我们只需输入 AR、I 和 MA 元件的初始阶数估计值,以及我们会考虑的每个元件的最高阶数。对于季节性模型,我们还需要传递季节性的频率,以及季节性 AR、I 和 MA 分量的估计值。

从本文开始,我们将在标准普尔 500 每日收盘价中找出最优的 SARIMA 模型。

上面的代码说,SARIMA(2,1,0)(1,0,0)₇模型最适合标准普尔 500 指数过去五年的数据…有一些重要的警告!在你把你一生的积蓄赌在这个模型的预测上之前,我们应该记住,尽管有一个方便的参数扫描,我们仍然可以从建模这个数据中挤出很多准确性。

首先,我们没有以任何方式对数据进行预处理,例如扫描异常值或插入任何间隙。类似地,我们没有检查任何转换是否会使数据更容易预测,例如采用百分比回报或平方根。

最后,我们的季节性频率 7 有点无中生有——可能有一个季节性,如每月、每季度或每年,这大大提高了我们的模型性能。诸如此类的考虑对于发布不会让你尴尬的预测是必不可少的!

(还有一个提醒,这个帖子不是投资建议!;-))

为什么不是深度学习?

还有一个我们还没有涉及的概念,它为这篇文章提供了一个重要的视角。经典统计学是伟大的,但在机器学习的时代,ARIMA 是过去的遗物吗?当像脸书的 Prophet 和 LinkedIn 的 Greykite 这样的开源库生成的预测比精心打磨的 SARIMAX 模型更准确时,为什么还要费心去理解早先那个移动平均纸杯蛋糕的例子呢?

作者图片

这个问题触及了机器学习和统计学之间的一个重要区别,最终是准确性和可解释性之间的权衡。要选择使用哪种工具,你必须明白你的目标是生成最准确的预测*,还是理解数据中潜在的生成过程。***

当你想最大限度地提高准确性,并愿意牺牲一些可解释性时,机器学习——尤其是深度学习——是首选工具。例如,递归神经网络是一种强大的预测工具,但解释网络如何生成预测需要搜索向前和向后反馈的隐藏层的混乱——这是一项艰巨的任务。如果你公司的高层领导正在根据你的预测权衡一个重大的商业决策,他们不太可能接受一个甚至你都不太明白它是如何运作的模型。**

因此,ARIMA 模型可能是一个有吸引力的替代方案,即使其预测能力较低。通过具体了解时间序列中的值如何相互关联,就更容易对时间序列本身建立直觉,例如今天的值对明天的影响有多大、季节性的循环等等。

正确的工具——机器学习或经典统计学——最终取决于您分析的更广泛的业务背景。(通过使用曲线拟合模型的线性组合,Prophet 实际上位于这两个极端之间的。)

菲德尔·费尔南多在 Unsplash 上的照片

结论

这篇文章深入探讨了 ARIMA 时间序列预测模型家族。我们从预测模型的一些基础理论开始,包括自相关和平稳性。然后,我们建立了简单的自回归模型,其中时间序列的现值是一些先前值的加权和。然后,我们研究了移动平均过程,这是一种不直观但常见的现象,时间序列会记住历史外部噪声。

我们将这些成分放在一起形成 ARMA 模型,然后展示了 ARIMA 模型如何简单地成为我们差分数据的 ARMA 模型。然后,我们展示了如何通过添加一组季节性自回归和移动平均成分来解释季节性,以及直接测量外部因素对我们数据的影响。

最后,我们用 Python 训练了一个 SARIMAX 模型,并进行了参数扫描,为我们的标准普尔 500 收盘价数据集确定了一个潜在的模型订单。然后我们讨论了为什么 ARIMA 仍然是一个有用的选择,即使有更精确的方法存在。

时间序列预测是一个巨大的领域,我们可以用模型选择和公式的细微差别来填充更多的博客帖子。但是不管你的时间序列知识如何,我希望它继续上升。

最好,
马特

脚注

1.介绍

“我们永远无法完美预测未来”听起来显而易见,但其背后的推理实际上很吸引人。在 智人 一书中,尤瓦尔·诺亚·哈拉里谈到了两种类型的混沌系统。第一种是混沌,它对预测没有反应,比如天气。天气是通过无数空气分子的非线性相互作用出现的——这非常难以预测,但我们可以使用越来越强的计算机模拟来逐步提高我们的准确性。

与此同时,第二类混沌系统对关于它的预测做出反应。历史、政治和市场都是这种系统的例子。例如,如果我们完美地预测明天的石油价格会比今天高,那么购买石油的热潮将会改变今天和明天的石油价格,导致预测变得不准确**

作为数据科学家,我们习惯于认为我们的分析与我们试图理解的过程是分开的。但是在我们的预测实际上影响结果的情况下,我们几乎没有希望完美地预测未来…除非我们让我们的猜测保持安静。

2.偏自相关

自相关图中滞后 n 处的急剧下降表示 MA(n) 过程,而偏相关图中的明显下降表示 AR(n) 过程。但是,除非您的数据集确实非常庞大,否则您很可能最终会进行参数扫描,以确定最能描述您的数据的模型的参数。如果两个自相关图都消失了,你看到的是 ARMA 或 ARIMA 过程,无论如何都需要进行参数扫描。

3.平稳性

我陷入了一个长长的兔子洞,试图理解平稳性假设的真正含义。在平稳过程与非平稳过程的对比图中,我使用一个有噪声的正弦波作为平稳过程的例子。这个数据集确实通过了增强的 Dicky-Fuller 测试,但是如果你将数据扩展到 1000 个样本,ADF 测试不再说时间序列是平稳的。**

这是因为 ADF 本质上衡量的是向均值的回归——非平稳过程没有漂移的问题,之前的滞后不提供相关信息。同时,平稳过程的滞后值在预测下一个值时提供相关信息。**

4.AR(0):白噪声

在我们的示例中, ϵ_t 值是从正态分布中采样的,因此这是高斯白噪声。 我们可以很容易地使用另一种分布来生成我们的值,比如均匀分布、二进制分布或正弦分布。

预测下一个单词的深度学习方法

原文:https://towardsdatascience.com/a-deep-learning-approach-in-predicting-the-next-word-s-7b0ee9341bfe?source=collection_archive---------8-----------------------

教计算机预测句子中的下一组单词

Szabo Viktor 在 Unsplash 上拍摄的照片

在本教程中,我们将逐步完成构建深度学习模型的过程,该模型用于预测种子短语后面的下一个单词。例如,在我们键入“候选人是”之后,我们将要求计算机预测接下来的 10 个单词。

虽然智能手机中用于帮助发送短信的尖端模型要复杂得多,但本文应该会让您对这一预测(分类)任务中涉及的方法有一个大致的了解。

本文将回顾:

  1. 文本处理:标记化、n_gram 排序、工程特征和标签以及单词嵌入
  2. 建立双向 LSTM 模型
  3. 使用我们的模型基于种子短语预测单词

我们来编码吧!

本教程的所有开发都是使用 Google Colab 的数据处理功能完成的。你当然可以在本地机器上运行这段代码,但是除非你有一个专用的 GPU,否则训练时间可能会相当长。

在本教程中,我们将使用用户对纽约时报文章链接的评论片段。由于这只是一个教程,自然语言数据在计算上非常昂贵,这个数据集的有限范围非常适合我们的需要。

让我们加载所需的库,因为训练数据驻留在 Google Drive 上,所以我们将 gdrive 挂载到 Colab。最后,我们将把目录更改为存放训练数据的位置。请注意,将您的 Google Drive 与 Colab 链接会自动在您的 Google Drive 中创建“Colab 笔记本”目录。由于“Colab”和“Notebooks”之间的空格,您的路径目录可能略有不同。例如,注意用于指定空间“/content/drive/my drive/Colab \ Notebooks”的反斜杠

import csv
import nltk
import string
import pandas as pd
import numpy as np
from google.colab import drive
from keras import backend as K
import tensorflow as tf
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.layers import Embedding, LSTM, Dense, Bidirectional
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adamdrive.mount('/content/drive/')%cd '/content/drive/MyDrive/Colab Notebooks'

确定哪些数据将用于训练我们的深度学习模型非常重要。在尝试构建现代文本预测模型时,您显然不希望使用莎士比亚的作品来训练您的模型。你的模型将会学习莎士比亚所写的那些不能有效地翻译成今天的现代英语的单词的上下文细微差别。使用最有可能反映你希望预测的句子类型的数据集将产生最佳结果。也就是说,利用非常大的广义文本数据集,想想维基百科,也可以产生准确的结果。

我们将使用 Pandas 读入 CSV 文件并打印出前 5 行。我们将使用突出显示的“snippet”列,其中包含用户评论的摘录。我们的数据有很强的政治和全球新闻内涵,但对于本教程来说,这就足够了。

path = '/content/drive/MyDrive/Colab Notebooks/ArticlesMarch2018.csv'df = pd.read_csv(path)df.head()

文本处理

在构建模型之前,我们需要对数据应用一些处理步骤。首先,让我们将所有片段组合成一系列字符串。

snippet = '\n'.join(df['snippet'])print(type(snippet))print(snippet)

一个由外部片段组成的长串联字符串

接下来,我们将把任何大写的单词转换成小写,以减少我们的词汇量。否则,我们会有重复的单词,唯一的区别是大写字母(即。美洲/美国)。注意 lower()函数将我们的字符串转换成了一个字符串列表。

我们的语料库包含 1385 个独特的片段。

corpus = snippet.lower().split('\n')print(len(corpus))print(type(corpus))print(corpus[:2])

包含我们的片段字符串的列表

标记器

由于计算机不能处理原始文本数据,我们需要对我们的语料库进行标记,以将文本转换为数值。Keras 的 Tokenizer 类根据词频转换文本,其中最常见的词的标记值为 1,其次是最常见的词的标记值为 2,依此类推。结果是一个字典,其中包含唯一单词的键/值对及其基于词频确定的分配标记。

我们的词汇表中有 6862 个独特的单词,外加(1)个词汇表之外的单词。注意,Keras 的 Tokenizer 类自动从我们的语料库中删除所有标点符号。通常,在处理 NLP 预测任务时,我们会删除所谓的“停用词”或对句子没有太大意义的术语(即。那个、他、在、为等。).然而,由于我们试图预测类似人类语言的句子,我们将保留停用词。

tokenizer = Tokenizer()
tokenizer.fit_on_texts(corpus)
word_index = tokenizer.word_index
total_unique_words = len(tokenizer.word_index) + 1 print(total_unique_words)print(word_index)

独一无二的单词及其标记化版本的单词索引词典

虽然下面的代码对于我们的分析来说不是必需的,但是为了准确起见,比较实际的文本和它的标记化版本是一个很好的实践。

通过应用下面的代码,我们可以看到我们整个标记化的语料库。例如,第一个片段以*“美国有一个生产力问题”*开始,这个问题已经被标记为【193,14,2,2796,699】。检查上面的 word_index 字典,我们看到字母“a”已经用数字 2 进行了标记,这与标记的第一个代码片段相对应。

for line in corpus:
   seqs = tokenizer.texts_to_sequences([line])[0]print(seqs)

标记化代码片段示例

n 克

这是本教程的一个棘手的部分。在典型的监督回归或分类问题中,我们的数据集将包含 x 值(即输入要素)和 y_values(即。标签),这允许模型学习我们的特征中与我们的标签相关的独特模式。我们的数据集似乎缺少 y_values(即标签)。不要担心,n_grams 帮助我们分割数据,以获得我们的标签。n_gram 是长度为 n 的单词序列。例如,“芝加哥小熊队”是长度为 3 的 n_gram(即。3 _ 克)。

多对一 N 元序列

我们将把这个概念向前推进一步,迭代每个标记化的片段,以创建 n_gram+1 大小的 n_gram。有时这被称为“多对一”序列图。例如,我们在第一个代码片段中的前五个标记化单词(即。*美国有生产力问题)有【193,14,2,2796,699】。*通过创建 n_gram+1 个 n_gram 序列,我们得到下面的列表。

请记住,这种多对一的方法适用于每个片段,而不是整个语料库。因此,排序后的 n_grams 从每个新片段开始。

假代币例句

我们对所有的片段使用这种方法,最终产生了将近 27,000 个 n _ grams。让我们更详细地检查一下代码。首先,我们创建一个空列表来保存上面描述的 n_gram 序列。接下来,我们遍历未分词片段的语料库,并对每个未分词片段应用“texts_to_sequences”方法。正如我们在上面看到的,“texts_to_sequences”方法只是将每个片段转换成它的标记化版本。接下来,我们迭代每个标记,从第二个标记(index=1)开始,直到所有剩余的标记。在每次迭代中,我们将 n_gram 的序列附加到 input_sequences,并扩展 n_gram+1。

input_sequences = []for line in corpus:
  token_list = tokenizer.texts_to_sequences([line])[0]
    for i in range(1, len(token_list)): 
      n_gram_seqs = token_list[:i+1]
      input_sequences.append(n_gram_seqs)print(len(input_sequences))
print(input_sequences)

N_Grams 的填充

请注意,n_grams 长度不同,我们的模型要求 n_grams 的每个输入序列长度相同。因此,我们需要将每个 n_gram 序列用零“填充”到最长 n_gram 的长度。Max_seq_length 将标识最长 n_gram 序列的长度(即 41).那么“pad_sequences”方法将在我们的令牌之前添加零(即 padding='pre)到 max_seq_lenth(即。41).

max_seq_length = max([len(x) for x in input_sequences])
input_seqs = np.array(pad_sequences(input_sequences, maxlen=max_seq_length, padding='pre'))print(max_seq_length)
print(input_seqs[:5])

长度为 41 的填充 n_gram 序列

创建要素和标签

现在我们已经填充创建和填充了 n_gram 序列,我们可以提取我们的特征和标签。回想一下,每个 n_gram 序列的长度是 41 个值。前 40 个值将是我们的特性,第 41 个值将是我们的标签。理论上,这是一个多类分类问题,因为模型将学习我们单词之间的关系,然后提供关于哪个单词应该是我们序列中的下一个单词的概率。该模型只能提供它见过的单词的概率(即唯一单词总数:6863)。由于这是一个多分类问题,我们也将对我们的标签应用一次性编码。下面我们打印了前三个特征和标签(没有一次性编码)来展示我们的方法。

x_values, labels = input_seqs[:, :-1], input_seqs[:, -1]y_values = tf.keras.utils.to_categorical(labels, num_classes=total_unique_words)\print(x_values[:3])
print(labels[:3])

我们的一次性编码标签(即 y_values)的形状为 26,937(即测序的 n _ grams 的数量)乘以 6,863(即语料库中唯一单词的数量)。除了第一个单词,我们语料库中的每个单词都是一个特征和标签,因为模型将学习哪些单词更有可能和更不可能跟随其他单词序列。

单词嵌入

在这些类型的 NLP 问题中,游戏的名称是“上下文”。我们如何优化我们的战略(即。数据处理和模型复杂性)以使我们的模型能够更好地学习单词之间的关系。以目前的形式,我们的数据为我们的模型学习提供了前进的背景。换句话说,由于我们的 n_gram 序列,我们知道哪些单词更有可能或更不可能跟在其他单词后面。然而,用于表示每个单词的标记只不过是频率计数,其提供了关于单词之间的上下文信息的模型有限信息。例如,单词“good”和“great”在意思上非常相似,但它们的标记值却是(299,673)。

print(tokenizer.word_index['good'])print(tokenizer.word_index['great'])

单词嵌入使我们能够在 n 维空间中表示单词,其中诸如“good”和“great”之类的单词在这个 n 维空间中具有计算机能够理解的相似表示。在下图中,我们可以看到单词的嵌入(7 维),如狗、小狗、猫、房子、男人、女人、国王和王后。维度对我们来说是未知的(不可解释的),因为模型在迭代数据时会学习这些维度。也就是说,为了帮助读者理解单词嵌入模型,让我们假设维度是“生物、猫、人、性别、皇室、动词、复数”。我们可以看到“房子”有一个-0.8 嵌入在 D1(即。而所有其他单词在“生物”维度上得分相对较高。通过学习这些嵌入,计算机能够更好地理解单词之间的上下文关系,因此应该能够更好地预测种子短语之后的下一个单词。

Keras 有一个“嵌入”层,可以基于我们的语料库构建自己的单词嵌入,但我们将利用斯坦福大学名为“GloVe”(单词表示的全局向量)的预训练单词嵌入模型。手套嵌入有几种风格,但我们将使用 100 维版本。您需要从这个链接下载预先训练好的模型。这个模型被训练了十亿个单词。独特的话)40 万字。

首先,让我们处理嵌入文本文件并生成一个字典,其中包含作为键的单词/字符和作为值的 100 维数组。

path = '/content/drive/MyDrive/Colab Notebooks/glove.6B.100d.txt'embeddings_index = {}with open(path) as f:
  for line in f:
    values = line.split()
    word = values[0]
    coeffs = np.array(values[1:], dtype='float32')
    embeddings_index[word] = coeffsdict(list(embeddings_index.items())[0:2])

现在让我们创建一个矩阵,其中包含手套词嵌入(即。100 维数组)仅用于我们词汇表中的单词。首先,我们创建一个形状为 6863 的零矩阵(即我们的语料库中唯一单词的总数)乘以 100(即手套包含 100 个尺寸)。然后,我们将遍历“word_index”字典,该字典包含您的语料库中的唯一单词作为键,以及它们相应的标记作为值。在 word_index 字典的每次迭代中,我们将获取相应的手套嵌入(即 100 维数组)存储在“嵌入 _ 索引”中,并更新嵌入 _ 矩阵(即用 100 维数组替换零)。

embeddings_matrix = np.zeros((total_unique_words, 100))for word, i in word_index.items():
   embedding_vector = embeddings_index.get(word)
   if embedding_vector is not None:
     embeddings_matrix[i] = embedding_vector;

构建我们的模型

我们将构建一个相对较浅的模型,由一个嵌入层、三个 LSTM 层、三个分离层和一个完全连接的密集层组成。作为嵌入层的第一层将使我们能够利用预训练的手套单词嵌入系数/权重。嵌入层需要唯一字总数的 input_dim (即词汇量的大小)和一个 output_dim ,它指定了我们想要的单词嵌入维数。因为我们使用手套 100 维,我们的 out_dim 参数将是 100。我们将把 embeddings_matrix 传递到权重参数中,以便使用手套权重,并将可训练参数设置为“False”,否则,我们将重新训练手套权重。最后,我们将把输入长度参数设置为“最大序列长度-1”。

递归神经网络

回想一下,帮助您的模型理解上下文或单词之间的关系最终会产生一个性能更好的模型。我们使用 n_gram 序列和单词嵌入来帮助我们的模型学习这些关系。我们现在可以利用一个特别适合于检查单词序列之间关系的特定模型结构。在传统的神经网络中,输入和预测/输出彼此完全独立。另一方面,我们试图预测句子中的下一个单词,这意味着网络需要“记住”来自以前单词的上下文信息。

一个递归神经网络(RNN) 模型架构结合了以前的数据来做出未来的预测。换句话说,RNNs 有一个内置的记忆功能,它存储来自前一个单词的信息,并在预测下一个单词时使用该信息。也就是说,简单的 rnn 很难“记住”序列中较早学习到的信息。换句话说,给定一个单词的句子,RNN 可以合并几个相邻单词之间的信息,但是随着 RNN 继续迭代句子,句子中第一个和后面的单词之间的关系开始失去它们的意义。当您有一个包含几十或几百个隐藏层非常深的模型时,这尤其重要。

一种长短期记忆(LSTM) 模型通过学习单词之间的关系并允许重要的关系通过网络传播来解决这个问题。这样,在一个序列的前几个单词中学习到的信息可以影响序列/句子、段落、章节等中的进一步预测。此外,我们将利用双向 LSTM,它将学习从左到右和从右到左阅读句子的单词之间的关系。

我们的 LSTM 层的输入总是三维的(即批量大小,最大序列长度,特征数量)。 batch_size 将被设置为 None ,这简单地指定该层将接受任意大小的批处理。 Max_seq_length 是 40 ,因为所有的代码片段都已被标记化并填充为 41 减 1 作为我们的标签。最后,我们有 100 个特征,因为手套有 100 个尺寸。

我们将添加三个 删除层 (删除 30%)来帮助模型过度拟合训练数据。

深度学习是一种徒劳的练习,因为你可以在剩余的时间里调整参数。选择正确的优化器只是我们可以调整的另一个参数。我们最终选择了 Adam,因为它在直接模型准确性和效率方面都优于 SGD 和 RMSProp,因为损失减少的速度几乎是 SGD 和 RMSProp 的两倍。

由于该模型的输出可以是 6863 中的 1(即 total_unique_words)单词,这是一个多类分类问题,这就是为什么我们使用分类交叉熵作为我们的损失函数。

K.clear_session()model = tf.keras.Sequential([tf.keras.layers.Embedding(input_dim = total_unique_words, output_dim=100, weights=[embeddings_matrix], input_length=max_seq_length-1, trainable=False),tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(256, return_sequences=True)),tf.keras.layers.Dropout(0.2), tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(256)),tf.keras.layers.Dropout(0.2),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dense(total_unique_words , activation='softmax')])model.compile(optimizer=Adam(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])tf.keras.utils.plot_model(model, show_shapes=True)

最后,我们将使用 x 值和 y 值来拟合/训练模型。选择 batch_size 是我们可以调整的另一个参数。因为我们有 26,937 个 n _ gram 来训练我们的模型,所以一次加载所有 26,937 个 n _ gram 是非常低效的。让我们针对 120 个时期训练我们的模型,其中 1 个时期需要所有 26,937 个 n _ grams 运行模型一次。因此,26937(即。27,000)除以 120 意味着每批大小应为 225 n 克。然而,为了充分利用我们的内存,我们将使用 batch _ size 256。我们还将对训练和验证数据应用 80/20 分割。

history = model.fit(x_values, y_values, epochs=120, validation_split=0.2, verbose=1, batch_size=256)

模型评估

我们的模型肯定是过度拟合了我们的训练数据,因为训练精度继续提高,而验证精度却停滞不前。性能良好的 NLP 模型非常需要数据。拥有数千万单词的数据集并不罕见,而我们的语料库只有 6863 个单词。我们可以添加额外的 LSTM 层,更多的正则化技术以及更多的数据来改进模型,但本教程旨在为读者提供如何构建和建立单词预测模型的一般概念。也就是说,让我们看看我们的模型预测实际的新片段有多好。

种子文本预测

让我们来看看我们的模型能在多大程度上推广到以前从未见过的新闻片段。我们将用上个月每个实际新闻片段的前几个词作为模型的种子,并比较模型的预测。

def prediction(*seed_text*, *next_words*): 
  for _ in range(next_words):
  token_list = tokenizer.texts_to_sequences([seed_text])[0]
  token_list = pad_sequences([token_list], maxlen=max_seq_length-1, padding='pre') predicted = np.argmax(model.predict(token_list, verbose=1), axis=-1) ouput_word = "" for word, index in tokenizer.word_index.items():
    if index == predicted:
      output_word = word
      break
  seed_text += ' '+output_word
  print(seed_text)seed_phrase = "I understand that they could meet"
next_words = len("with us, patronize us and do nothing in the end".split())prediction(seed_text, next_words)

模型生成的文本当然是围绕全球新闻和政治的。我们看到诸如“一代”、“政府”、“借贷”、“监管”、“政策”等词,这些词符合模型学习的数据类型。不幸的是,从人类片段中识别模型生成的文本并不困难。此外,当我们使用一个上下文非常不同的种子短语“我真的很喜欢骑我的摩托车”时,模型很难对一个类似于准确英语句子的句子进行分类。

实际新闻片段 1

“我知道他们可能会见我们,资助我们,但最终什么也不做”

种子短语 1

“我知道他们可以见面”

模型生成 1

“我知道他们可以应对你们这一代人的挑战,也知道你们为什么要借钱”

实际新闻片段 2

“该机构计划在周二公布一项新规定,该规定将限制该机构在制定政策时可以使用的科学研究种类。”

种子短语 2

"该机构计划在周二发布一项新的规定,限制."

模型生成 2

“该机构计划在周二公布一项新的规定,将限制政府在 9 月份发生的事情,他们必须是第三个”

实际新闻片段 3

“支持增加限制的枪支拥有者可能是一个被忽视的群体。一些人变得更加直言不讳,游行并证明支持限制。

种子短语 3

“支持增加限制的枪支拥有者”

模型生成 3

支持增加对专家限制的枪支所有者最近计划对钢铁和铝关税征收全面关税,同时

自生成种子短语

“我真的很喜欢骑摩托车”

模型生成短语

“我真的很喜欢骑摩托车,但每个人都问我讨厌这个问题”

结论

在自然语言处理领域,这无疑是一个激动人心的时刻。像 OpenAI 的“GPT-3”这样的模型正在继续推动我们的自然语言处理能力的进步。这篇教程是对文本分类的简单介绍,我希望它能激发你对这个令人兴奋的领域的兴趣。

对 OpenAI 的 GPT 3 能力的一些见解链接。

一种基于深度学习的数据合成方法及其在金融交易中的应用

原文:https://towardsdatascience.com/a-deep-learning-based-method-for-synthesizing-data-with-an-application-in-financial-transactions-9c3936423d9a?source=collection_archive---------20-----------------------

理解大数据

如何使用变分自动编码器和一般线性回归预测表格交易数据

由粘土银行在 Unsplash 上拍摄

背景

销售预测在商业计划中起着极其重要的作用。这是一个有用的工具,它从过去和当前的销售数据中提取信息,智能地预测未来的业绩。有了准确的销售预测,决策者可以更有信心地优化营销策略和业务扩展计划。例如,如果你的销售预测说,你的年销售额的 50%将发生在圣诞节期间,你将需要在秋季增加库存和产量,为销售高峰做准备。感恩节后,你可能还想雇佣更多的销售人员。如果你对竞争对手有准确的销售预测,从而更好地了解他们的优先事项和目标,你就可以在计划促销活动时领先一步。一份好的销售预测可以告知你业务的许多其他方面。

对于这个项目,我实现了一个框架,使用支付应用程序的历史交易记录来预测销售额。该框架应用宏观经济指数来约束未来的总交易数量,并应用表格变量自动编码器(TVAE)来模拟单个交易记录。本文提供了一些示例代码,有关代码的详细信息,请查看我的 Github 。

数据

本项目使用的数据集来自某支付 app,是一个表格数据,从 2018 年 9 月到 2020 年 1 月,交易记录超过 300 万条。每个记录包含客户(年龄、性别、地址等。)、账户(账户类型、银行等。)、交易(交易类型、金额、日期等。)和零售商信息(零售商名称、部门等)。).首先,我选择了如下变量:“账户类型”、“消费者性别”、“消费者年龄”、“交易日期”、“标准化零售商”、“基本描述(部门)”和“购买金额”。

表 1。表格数据中所选变量的样本

方法

合成数据生成器获取输入表格数据,从中学习模型,并输出与输入数据具有相似统计属性的新合成数据。当法规或其他隐私问题限制对原始数据的访问,或者原始数据的收集成本很高,或者根本无法获得时,通常会使用这种方法。根据最近麻省理工学院关于合成数据生成技术的基准测试研究,表格变分自动编码器(TVAE)合成器的性能优于其他合成器,值得推荐。

自动编码器的基本思想是将编码器和解码器构造为神经网络,并使用迭代优化过程来学习最佳编码-解码方案。编码器类似于降维技术,如主成分分析(PCA ),它将输入数据转换为潜在空间中的表示,解码器从潜在空间中随机采样,并将其转换为表格数据。训练是有规律的,以避免过度拟合,并确保潜在空间具有良好的属性,使生成过程。

这里我使用 TVAE 合成器加载历史交易数据作为输入,训练模型,输出模拟的交易数据。输出数据从历史时间段反映了客户的消费习惯,但缺乏交易总数的约束。因此,我建立了历史交易次数与宏观经济指标(GDP、CPI、失业率等)之间的广义线性回归模型。)并将其用于预测未来的交易计数。此外,我还需要根据历史指数预测未来的经济指数。提议的工作流程如下:

  • 给定未来某年某月,例如 2021 年 2 月,根据历史指数预测宏观经济指数。
  • 使用回归模型预测 2021 年 2 月的交易计数 N。
  • 使用经过训练的 TVAE 合成器生成 N 条交易记录。

特征工程和零售商嵌入

我从数据清理和特征工程开始。表格数据维护得很好,缺失值或无效值(即负年龄)不到 2%,所以对于缺失值或无效值,我简单地删除了它们。

数值小于 10 的分类变量用一个热编码处理,包括“账户类型”(借方或贷方)、“消费者性别”(男性或女性)和“原文描述”。请注意,“SIC Description”仅保留具有最大计数的前 9 个值,并将其余记录归入“other”中。

起初,我将“年龄”视为一个连续的数字变量,但综合的年龄分布看起来并不是最佳的。30 到 50 岁之间的一大块光谱丢失了。我通过将年龄转换成“年龄范围”来确定它(即 20-25,25-30,30-35,等等。)作为模型输入,然后通过从每个范围中随机选择一个数字,将模拟范围转换回“年龄”。这是一种将连续数值转换为离散的固定宽度箱,并将这些离散值视为类别的经典方法。

我将“交易日期”分解为两个分类值:“一个月中的某个时间段”和“一周中的某一天”。“一个月的周期”包含变量“开始”(一个月的 1-10 天)、“中间”(一个月的 11-20 天)和“结束”(一个月的 21 天到月末),这是指任何给定月份的不同周期。“星期几”是指一周中的某一天(星期一、星期二等)。).这种方法忽略了年度和月度变化,而是关注周变化和日变化。原因是,当使用宏观经济指数(通常每月发布)预测交易数量时,我们会纳入年度和月度变化,因此合成器不一定需要包括历史记录中的信息。

“标准化零售商”是最难处理的变量。它包含 2000 多家零售商,因此需要嵌入来将维度减少到可管理的水平。在这里,我应用“item2vec”将“标准化零售商”转换为 10 维嵌入。为了向读者提供一些上下文,在自然语言处理(NLP)领域中,“word2vec”通过将每个句子视为共享相似上下文的“单词集”来学习保持单词的语义特征和单词之间的关系的单词嵌入。“item2vec”是其在非 NLP 领域的变体之一,其中它将“项目集”作为与句子(“单词集”)类似的上下文,并找出项目之间的关系。使用嵌入代替一键编码有两个主要优点。首先,减少的维度可以用有限的内存来管理。第二,它更好地代表了变量的距离或相似度。

“来自网络搜索、电子商务和市场领域的研究人员已经意识到,就像人们可以通过将句子中的单词序列视为上下文来训练单词嵌入一样,同样可以通过将用户动作序列视为上下文来训练用户动作嵌入。”

— Mihajlo Grbovic,Airbnb

训练项目嵌入的最大挑战是如何定义“项目集”,或者如何找出与这些项目最相关的上下文。对于这个项目,我对零售商测试了几种不同的分组方法:a)对在同一天与同一个客户有交易的零售商进行分组。b)属于同一部门并在同一天进行交易的集团零售商。c)属于同一部门并与同一客户有交易的集团零售商。

事实证明,选项 C 表现最好,在将 10 维减少到 2 维后,我绘制了零售商向量的散点图。彩色地图指示不同的扇区。这表明嵌入可以很好地对零售商进行分组。

图一。按行业分类的零售商嵌入

我使用了来自 gensim 的 word2vec 模块来训练 retailer2vec 模型。word2vec 模块将每个句子视为“集合”,并有一个参数窗口大小来确定我们搜索每个单词的“上下文”的范围。相比之下,在我们的 retailer2vec 实现中,零售商的“含义”应该被同一集合中的所有邻居捕获。换句话说,我们应该考虑属于同一部门并与同一客户有交易的所有零售商,以确定每个零售商的“意义”。然后需要根据每个零售商集合的大小来改变窗口大小。

为了在不修改 gensim 库的底层代码的情况下解决这个问题,一种方法是定义超大窗口大小(例如,999999),其远大于训练数据中设置的任何零售商的长度。尽管这种解决方法并不理想,但它确实取得了可以接受的结果。最好的选择可能是修改 gensim 中的底层代码。下面是使用 word2vec 训练嵌入的示例代码。

from gensim.models import Word2Vecmodel = Word2Vec(sentences = training_data,
                 iter = 10,
                 min_count = 5,
                 size = 10, 
                 workers = 4, 
                 sg = 1, 
                 hs = 0, 
                 negative = 10, 
                 window = 999999)

当将综合零售商嵌入反转回零售商时,该模型将只检查综合部门中零售商之间的距离(“SIC 描述”),并挑选最近的一个。

列车 TVAE 合成器

经过特征工程,我得到了一个 46 列、超过 300 万条记录的输入矩阵。我使用了来自 SDgym 的 TVAE 合成器,其中训练和生成样本都很好地封装在模块中。请注意,该函数要求指定分类列和顺序列。然而,当我为一次性编码变量显式指定分类列时,合成数据很难产生合理的结果。另一种方法是假设所有的单热编码变量都是连续的数值,并在所有列中选择合成结果的绝对最大值,这样就可以了。更多验证可以在第 4 节中找到。下面是训练 TVAE 合成器和检查输出样本的示例代码。

from sdgym.synthesizers import TVAESynthesizersynthesizer = TVAESynthesizer()
synthesizer.fit(data)
sample = synthesizer.sample(1)

考虑到输入矩阵的规模较大,建议用 GPU 训练合成器。为任何新用户提供免费 GPU 访问的谷歌 Colab 或 300 美元信用的谷歌云平台将是一个良好的开端。

收集和预测经济指标

我从 5 个常见的宏观经济指数开始,它们是:国内生产总值(GDP),消费者价格指数(CPI),多伦多证券交易所(TSX),失业率和汇率(加元对美元)。为了在 eco 指数和事务计数之间建立一个回归模型,我需要首先收集这些数据。加拿大统计局提供 API 服务,提供对每个工作日发布的数据和元数据的访问。API 命令由 Python 库 stats_can 进一步包装,可以在任何脚本中轻松调用该库来检索最新的 eco 指数数据。用户所需要做的就是为所需的索引指定一个“矢量”作为输入。值得注意的是,默认情况下,“矢量”不显示在加拿大统计局网站的索引表上,用户需要在表格设置中打开“显示矢量标识符和坐标”,使其可见。我将在另一篇文章中介绍如何使用 stats_can 从加拿大统计局自动检索数据。下面是使用 stats_can 从加拿大统计局检索 eco 指数的示例代码。

import stats_caneco_vec_map = {'CPI':'v41690973',
               'Exchange_Rate_USD':'v111666275',
               'GDP':'v65201210',
               'Unemployment_Rate':'v91506256',
               'TSX':'v122620'}
vectors = list(eco_vec_map.values())
N = 36
df = stats_can.sc.vectors_to_df(vectors, periods = N)

我使用脸书开发的开源库 prophet T1 来预测经济指数。该库用于预测基于加法模型的时间序列数据,其中非线性趋势符合每年、每周和每天的季节性,加上假日影响。例如,给定过去三年的 GDP 记录(总共 36 个月的记录),我们可以预测下个月的 GDP。下面是使用 prophet 预测生态指数的示例代码。

from fbprophet import Prophetm = Prophet()
m.fit(df_input)
future = m.make_future_dataframe(periods = 1, freq = "MS")    
forecast = m.predict(future)

广义线性回归模型

我以多元线性回归作为基线,但是发现预测的事务数有时可能是负数。一个自然的替代方法是使用广义线性模型(GLM)。在这里,我使用了一个带有泊松分布和对数链接函数的 GLM,将历史月度交易计数与月度经济指数相关联。下面是使用 scikit-learn 训练 GLM 的示例代码。

from sklearn.linear_model import TweedieRegressor
from sklearn import preprocessingglm = TweedieRegressor(power = 1, alpha = 0.5, link='log')
scaler = preprocessing.StandardScaler().fit(X_train)
X_train_scaled = scaler.transform(X_train)
glm.fit(X_train_scaled, y_train)

理想情况下,预测的事务数量可以随着每月更多数据的进入而得到验证。

结果

以验证合成数据是否表现出与输入数据相似的统计特性。我研究了年龄和购买量分布,顶级行业和顶级零售商的购买量柱状图(表 2)。

虽然综合采购金额可以很好地反映总分销,但它很难解决每个零售商的销售,为了解决这一问题,我对每个零售商的“交易金额”进行了后校正,方法是根据输入数据缩放每个零售商的平均交易金额。通过后校正,每个零售商对合成数据的购买量与输入数据拟合得更好,尽管遗漏了一些异常值。

表二。输入数据和合成数据之间的比较

讨论

该项目提供了一个框架,使用支付应用程序的历史交易记录来预测销售。基本思想是使用宏观经济指数预测总销售量,然后生成综合交易记录来填充该量。该框架利用项目嵌入、表格变分自动编码器(TVAE)、时间序列预测和广义线性模型等方法。它产生相当好的预测,可以用来告知零售商业务的许多方面。

然而,仍有改进的余地。零售商嵌入可以通过定义更多关联相关零售商的集合来改进。例如,对于最大的群体“餐馆”,如果我们可以从 Yelp 等应用程序中收集标签或消费水平($$$),并基于这些信息定义用于训练的集合,零售商嵌入可以更好地表示所有餐馆之间的关系,这最终可以提高餐馆零售商的销售额。目前,我们仍然需要对销售进行后校正,以获得每个零售商更好的销售分布,预计改进的零售商嵌入可以帮助消除后校正。

对于未来的工作,我计划在谷歌云平台上部署该模型。每月月初,该模型将自动收集上月的经济指数,预测当月的指数和总交易量,然后详细生成当月的交易量。当新的交易记录到来并预测未来几个月时,它还将允许合成器的重新训练。

参考

[1] L .徐,m .斯科拉利杜,a .-因方特,k .韦拉马钱恩,用条件甘对表格数据建模。(2019),https://arxiv.org/abs/1907.00503

[2] O. Barkan,N. Koenigstein,Item2Vec:用于协同过滤的神经项目嵌入。(2017),【https://arxiv.org/abs/1603.04259

对装饰的深入探究

原文:https://towardsdatascience.com/a-deeper-dive-into-decoration-ca4334ca5cf8?source=collection_archive---------45-----------------------

如何用 Python 编写灵活的、可重用的装饰器

作者:爱德华·克鲁格和道格拉斯·富兰克林。

照片由澳门图片社在 Unsplash 上拍摄

介绍

我们首先介绍一些函数式编程概念的背景知识,并讨论计时和跟踪。

接下来,我们用两个例子来说明装饰模式及其语法,tracefunctimefunc。为此,我们使用 Python 库functoolstime

然后我们转到对functools.wraps的更深入的讨论,以及它如何保存被修饰函数的元数据。最后,我们用一些例子来说明这种保护。

为什么要跟踪和计时一个函数

跟踪是在程序运行时记录函数的输入和输出。有经验的程序员使用跟踪来排除程序故障,通常作为调试器的替代品。这些程序员使用跟踪来识别生产中的问题,并使用跟踪来复制错误。对于正在学习语言的初学者来说,描摹也是极好的。

计时更直接。更快的功能是更好的功能。

时间就是金钱,朋友。—加斯洛维

为了用 Python 编写装饰函数,我们依赖于functools 和范围意识。我们来回顾一下范围和装饰。

装饰、关闭和范围

修饰是 Python 中的一种设计模式,允许您修改函数的行为。装饰器是一个函数,它接受一个函数并返回一个函数的修改副本。

当编写闭包和装饰器时,必须记住每个函数的作用域。函数在 Python 中定义范围。闭包可以访问返回它们的函数的范围;装饰者的范围。

有关这些概念的更多背景知识,请查看这篇 3 分钟的文章。

跟踪装饰者:@tracefunc

让我们用跟踪装饰器来说明functools.wraps的重要性。

tracer.py

Functools 在第 11 行变得很重要,在这里我们访问func.__name__来打印一个跟踪。如果我们没有在第 7 行传递funcfunctools.wraps,当我们使用多个装饰器时,这个 print 语句可能会返回错误的func.__name__。返回错误的元数据会导致跟踪器无效。

functools.wraps 对装饰者的重要性

随着我们的代码变得越来越复杂,functools.wraps对于保持我们的装饰器按预期工作变得越来越重要。让我们更深入地了解一下这个工具。

装饰器@functools.wraps(见上面第 7 行)是一个方便的函数,用于在定义包装函数时调用update_wrapper作为函数装饰器。update_wrapper的目的是更新包装器功能,使其看起来像包装的功能。这就是我们如何通过装饰管道传递函数的元数据。

文档说得很好:

这个函数的主要用途是在装饰器函数中,它包装被装饰的函数并返回包装器。如果包装函数没有更新,返回函数的元数据将反映包装定义,而不是原始函数定义,这通常没有什么帮助。
— Python 文档

换句话说,如果不使用这个装饰器工厂,原始装饰函数的元数据将会丢失。这将使我们的tracefunc返回包装器的踪迹,而不是被修饰的函数。

彼得·海因西乌斯在 Unsplash 上的照片

这个装饰器的可重用性

注意func被作为第 4 行的参数。然后在第 8 行,我们通过*args, **kwargs,进入我们的闭包。这些*args, **kwargs用于计算第 10 行func(*args, **kwargs)result

再次使用 tracer.py 来保存滚动

*args**kwargs的灵活性使得tracefunc几乎可以处理任何功能。我们闭包的 print 语句是为了访问函数的__name__argskwargsresult来为func创建一个跟踪

在编写自己的装饰器时,请记住这种灵活性。

应用@tracefunc

这是我们的追踪器在一个函数上的应用。我们在我们希望修饰的函数定义上方应用带有@符号的装饰器。

这个例子很简单,但重点是tracefunc对任何函数都有效。

traced_func.py

我们看到argskwargs 在打印语句中各自的位置。函数的返回值跟随粗箭头;在这三种情况下,Python 的默认结果是“无”

Output of func.__name__ ,*args, **kwargs => result

该跟踪向我们显示了传递给函数的正确的函数名、args 和/或 kwargs,以及该函数的结果。将这个装饰器应用到一个函数中,可以让用户更清楚地看到函数执行时发生了什么。

现在让我们讨论一下我们的时序装饰器。

计时装饰:@timefunc

这个装饰器看起来和tracefunc非常相似,拥有相同的可重用/灵活的结构。注意functool.wraps的位置。同样,它也是我们闭包的装饰者。回想一下,这是保存我们的func的元数据,以便在我们的 print 语句中返回正确的名称。

timer.py

这个装饰器返回传递给timefunc的关于func 的信息。在第 13 行,start 启动计时。然后,第 14 行的result 存储func(*args, **kwargs).的值,在此之后,在 print 语句报告函数执行所花费的时间之前time_elapsed被计算。请注意,函数的结果被返回,但用户看不到。

应用@timefunc

我们再一次用@符号来应用这个装饰器。

用@timefunc 装饰函数

当在第 13 行调用 single_thread 时,我们看到了func.__name__ 和它执行所用的时间。

timefunc 修饰的 single_thread 的输出

同时计时和追踪

假设您想要查看一个函数的轨迹和执行时间。现在我们有了两个写得很好的装修工,就好办了!

我们所要做的就是将两个装饰器都应用到正在讨论的函数中。

装饰 _ 管道. py

在计时器之前应用跟踪器可以确保我们不会将跟踪器的打印语句执行添加到我们正在记录的运行时中。

现在我们看到了这个函数的执行时间和轨迹。注意,在这种情况下,我们返回的是一个列表列表。这样的信息对于调试脚本是非常宝贵的!

deco_pipe.py 输出

我们还看到了运行时,它对于优化函数和选择最佳代码非常有用。

结论

我们已经深入了解了 decorators 以及如何用 Python 对它们进行编码。装饰者返回一个闭包。闭包是装饰器返回的东西。

我们使用functools.wraps通过装饰器的闭包来保存被装饰的func的元数据。从而允许我们在 decorators 的 print 语句中返回 func 的名称。这变得越来越重要,我们通过多个装饰器传递函数。

Timefunc 和 Tracefunc 有相似的代码模式,但用例不同。我们可以使用这些装饰器来分别或协同计时和跟踪函数。这些装饰器为我们提供了一个优雅的、可重用的模式来跟踪函数运行时和行为。

PyTorch 神经网络详细指南。变压器()模块。

原文:https://towardsdatascience.com/a-detailed-guide-to-pytorchs-nn-transformer-module-c80afbc9ffb1?source=collection_archive---------0-----------------------

一步一步的指导,以充分了解如何实施,培训,并推断创新的变压器模型。

我最近越来越多地涉足机器学习领域。当我在理解复杂问题或编写神经网络时遇到问题时,互联网似乎有所有的答案:从简单的线性回归到复杂的卷积网络。至少我是这么认为的…

一旦我开始在这种深度学习的事情上变得更好,我偶然发现了无与伦比的变形金刚。原论文:“注意力是你所需要的全部”,提出了一种构建神经网络的创新方法。不要再绕圈子了!本文提出了一种由重复的编码器和解码器块组成的编码器-解码器神经网络。其结构如下:

变压器结构:【https://arxiv.org/abs/1706.03762

左边的模块是编码器,右边的模块是解码器。如果你还不理解这个模型的部分,我强烈推荐阅读哈佛的“带注释的变形金刚”指南,在那里他们从头开始用 PyTorch **编写变形金刚模型。在本教程中,我不会涉及像“多头注意力”或“前馈层”这样的重要概念,所以你应该在继续阅读之前了解它们。如果您已经从头开始看了一遍代码,您可能会想,您是否需要为您所做的每个项目到处复制粘贴这些代码。谢天谢地,没有。像 PyTorch 和 Tensorflow 这样的现代 python 库已经通过导入包含了易于访问的 transformer 模型。然而,这不仅仅是导入模型并插入其中。今天我将解释如何使用和调整 PyTorch nn。变压器()模块。我个人努力寻找关于如何实施、培训和从中推断的信息,所以我决定为你们所有人创建我自己的指南。

第一步

进口

首先,我们需要导入 PyTorch 和我们将要使用的其他一些库:

基本变压器结构

现在,让我们仔细看看变压器模块。我建议从阅读 PyTorch 的文档开始。正如他们解释的那样,没有强制参数。该模块带有“你所需要的只是注意力”模型超参数。为了使用它,让我们从创建一个简单的 PyTorch 模型开始。我将只更改一些默认参数,这样我们的模型就不会花费不必要的时间来训练。我将这些参数作为我们课程的一部分:

位置编码

变压器模块不关心输入序列的顺序。这当然是个问题。说“我吃了一个有菠萝的披萨”和说“一个菠萝吃了我的披萨”是不一样的。谢天谢地,我们有一个解决方案:位置编码。这是一种根据位置“赋予”元素重要性的方式。关于它如何工作的详细解释可以在这里找到,但是一个简单的解释是我们为每个元素创建一个向量,代表它相对于序列中每个其他元素的位置。位置编码遵循这个看起来非常复杂的公式,实际上,我们不需要理解它:

位置编码公式:作者图片

出于组织和可重用性的考虑,让我们为位置编码层创建一个单独的类(它看起来很难,但实际上只是公式、漏接和一个剩余连接):

完成我们的模型

现在我们有了 PyTorch 中唯一没有包含的层,我们准备完成我们的模型。在添加位置编码之前,我们需要一个嵌入层,以便我们序列中的每个元素都被转换成我们可以操作的向量(而不是固定的整数)。我们还需要一个最终的线性层,这样我们就可以将模型的输出转换成我们想要的输出尺寸。最终的模型应该是这样的:

我知道…这看起来很吓人,但是如果你理解每个部分的作用,这实际上是一个非常简单的实现模型。

刷新一些重要信息:目标掩蔽

您可能还记得模型结构中有一个特殊的模块,叫做“屏蔽的多头注意力”:

蒙面多头注意:https://arxiv.org/abs/1706.03762

那么…什么是掩蔽?在我向你解释之前,让我们快速概括一下,当我们把张量输入到模型中时,它们是怎么回事。首先,我们嵌入并编码(位置编码)我们的张量。然后,我们的源张量被编码成一个难以理解的编码张量,我们将它与嵌入和编码的(位置上的)目标向量一起输入到我们的解码器。我们的模型要学习,不能只给它看整个目标张量!这会直接给他答案。

这个问题的解决方案是一个掩蔽张量。这个张量由大小(序列长度 x 序列长度)组成,因为对于序列中的每个元素,我们向模型多显示了一个元素。这个矩阵将被添加到我们的目标向量中,因此矩阵将在转换器可以访问元素的位置由零组成,在转换器不能访问元素的位置由负无穷大组成。有插图的解释可能对你更有帮助:

目标矢量蒙版:作者图片

刷新一些重要信息:填充遮罩

如果你不知道,张量是可以存储在 GPU 中的矩阵,因为它们是矩阵,所以所有维度都必须有相同大小的元素。当然,在处理 NLP 或不同大小的图像等任务时,这种情况不会发生。因此,我们使用所谓的特殊令牌。这些标记允许我们的模型知道句子的开始在哪里(< SOS >),句子的结束在哪里(< EOS >),以及哪些元素正好填充剩余的空间,以便我们的矩阵具有 sam 序列大小(< PAD >)。这些标记还必须转换成它们相应的整数 id(在我们的例子中,它们分别是 2、3 和 4)。填充序列看起来像这样:

填充一批序列:作者图像

为了告诉我们的模型这些标记应该是不相关的,我们使用一个二进制矩阵,其中填充标记所在的位置上有一个值,填充标记不在的位置上有:

填充遮罩:作者的图像

创建屏蔽方法

为了创建我们讨论过的两个屏蔽矩阵,我们需要扩展我们的 transformer 模型。如果你懂一点 NumPy,理解这些方法做什么是没有问题的。如果你看不懂,我推荐你打开一个 Jupyter 笔记本,一步一步去了解他们是做什么的。

完整的扩展模型如下所示(请注意 forward 方法的变化):

获取我们的数据

为了这个项目,我将创建一组假数据,我们可以用它来训练我们的模型。该数据将由如下序列组成:

  • 1, 1, 1, 1, 1, 1, 1, 1 → 1, 1, 1, 1, 1, 1, 1, 1
  • 0, 0, 0, 0, 0, 0, 0, 0 → 0, 0, 0, 0, 0, 0, 0, 0
  • 1, 0, 1, 0, 1, 0, 1, 0 → 1, 0, 1, 0, 1, 0, 1, 0
  • 0, 1, 0, 1, 0, 1, 0, 1 → 0, 1, 0, 1, 0, 1, 0, 1

如果您对数据创建部分不感兴趣,请随意跳到下一节。

我不想解释这些函数是做什么的,因为用基本的数字知识很容易理解它们。我将创建所有大小为 8 的句子,这样我就不需要填充,我将把它们随机组织成大小为 16 的批次:

培训和验证

培养

现在我们有了数据,可以开始训练我们的模型了。让我们从创建我们的模型、损失函数和优化器的实例开始。我们将使用随机梯度下降优化器、交叉熵损失函数,以及 0.01 的学习率。我也将使用我的图形卡进行此培训,因为它需要的时间较少,但这不是必需的。

在继续之前,我们需要理解的一个重要概念是,我们作为输入给转换器的目标张量必须向右移动一(与目标输出张量相比)。换句话说,我们想要给模型用于训练的张量必须在开始时有一个额外的元素,在结束时少一个元素,并且我们用来计算损失函数的张量必须向另一个方向移动。这是因为如果我们在推理过程中给模型一个元素,它就会给我们下一个元素。

目标向量转换:作者图片

既然我们已经掌握了这个概念,让我们开始编码吧!训练循环是标准训练循环,除了:

  • 目标张量在预测期间被传递给模型
  • 生成目标掩码来隐藏接下来的单词
  • 可能还会生成填充遮罩并将其传递给模型

确认

验证循环与我们的训练循环完全相同,只是我们不读取或更新梯度:

执行培训和验证

在本例中,我为模型训练了 10 个时期。为了简化训练,我创建了一个 fit 函数,它在每个时期调用训练和验证循环,并打印损失:

这会产生以下结果

Training and validating model
------------------------- Epoch 1 -------------------------
Training loss: 0.5878
Validation loss: 0.4172------------------------- Epoch 2 -------------------------
Training loss: 0.4384
Validation loss: 0.3981------------------------- Epoch 3 -------------------------
Training loss: 0.4155
Validation loss: 0.3852------------------------- Epoch 4 -------------------------
Training loss: 0.4003
Validation loss: 0.3700------------------------- Epoch 5 -------------------------
Training loss: 0.3842
Validation loss: 0.3443------------------------- Epoch 6 -------------------------
Training loss: 0.3592
Validation loss: 0.3069------------------------- Epoch 7 -------------------------
Training loss: 0.3291
Validation loss: 0.2652------------------------- Epoch 8 -------------------------
Training loss: 0.2956
Validation loss: 0.2195------------------------- Epoch 9 -------------------------
Training loss: 0.2684
Validation loss: 0.1947------------------------- Epoch 10 ------------------------- Training loss: 0.2501
Validation loss: 0.1930

结果

训练后,我们获得每个时期的以下损失:

训练结果:作者图片

推理

我们可以看到,我们的模型似乎学到了一些东西。是时候检查它是否存在了,但是…我们如何检查它呢?对于我们从未见过的数据,我们没有目标张量。这就是移动我们的输入目标和输出目标张量产生影响的地方。正如我们之前看到的,当给定一个元素时,我们的模型学会了预测下一个令牌。因此,我们应该能够给我们的模型输入张量和开始令牌,它应该给我们返回下一个元素。如果当模型预测一个令牌时,我们将它与我们以前的输入连接起来,我们应该能够慢慢地将单词添加到我们的输出中,直到我们的模型预测到令牌。

以下是该过程的代码:

运行这段代码的输出是:

Example 0
Input: [0, 0, 0, 0, 0, 0, 0, 0]
Continuation: [0, 0, 0, 0, 0, 0, 0, 0, 0]Example 1
Input: [1, 1, 1, 1, 1, 1, 1, 1]
Continuation: [1, 1, 1, 1, 1, 1, 1, 1, 1]Example 2
Input: [1, 0, 1, 0, 1, 0, 1, 0]
Continuation: [1, 0, 1, 0, 1, 0, 1, 0]Example 3
Input: [0, 1, 0, 1, 0, 1, 0, 1]
Continuation: [1, 0, 1, 0, 1, 0, 1, 0]Example 4
Input: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
Continuation: [0, 1, 0, 1, 0, 1, 0, 1, 0]Example 5
Input: [0, 1]
Continuation: [0, 1, 0, 1, 0, 1, 0]

所以这个模型确实得到了我们序列的要点,但是当它试图预测延续时仍然会犯一些错误。例如,在“示例 4”中,模型应该预测 1 作为第一个令牌,因为输入的结尾是 0。我们还可以看到,在推理过程中,我们的句子不需要有相同的长度,输出也不会有相同的长度(见“例 5”)。

结论

我相信这篇文章可以帮助许多初级/中级机器学习开发人员学习如何在 PyTorch 中使用 transformer 模型,并且,由于其他语言中的结构是相同的,所以这篇教程可能也对其他框架(如 Tensorflow)有用(希望如此)。

如果您有任何建议或发现任何错误,请随时留下评论,我会尽快修复它。

全 Colab 笔记本:https://drive . Google . com/file/d/15 gy trsd-ou 6 yzvyjwis 48 ysrxfpecv 9 r/view?usp =共享

【https://danielmelchor.com】网站:

联系人:dmh672@gmail.com

自然语言处理(NLP)的详细入门

原文:https://towardsdatascience.com/a-detailed-novice-introduction-to-natural-language-processing-nlp-90b7be1b7e54?source=collection_archive---------20-----------------------

入门

Python 中 NLP 入门的终极代码指南

由 Joshua Hoehne 在 Unsplash 上拍摄的照片

语言是我们使用的最重要的交流媒介。它包括地理和知识的界限。研究表明,处理自然口语或书面语在各种业务中是有用的,从搜索自动完成/自动纠正、翻译、社交媒体监控、定向广告到分析调查,以及总体上增加对外语的整体理解。在下面的故事中,我们将使用 NLP 实现两个不同的算法。在第一个算法中,我们将定义一组语法规则,并根据规则解析一个句子,在第二个算法中,我们将搜索一个网站,并找到它的内容。

自然语言处理(NLP)是人工智能(AI)中的一种方法,用于分析和构建能够以人类所说的语言(例如英语)运行的智能系统。当系统希望基于用户以文本或语音形式的输入工作,并且用户正在添加常规使用英语的输入时,需要对语言进行处理。

  • 自然语言理解(NLU) :处理过程中的理解阶段负责将自然语言中给出的输入映射为有益的表示。它还分析了程序输入语言的不同方面。
  • 自然语言生成(NLG) :处理的生成阶段用于从第一阶段创建自然语言。生成从文本规划开始,文本规划是从知识库中提取相关内容。接下来是句子规划阶段,在这个阶段,将组成句子的必要单词被选择出来。以文本实现结尾是句子结构的最终创造。

自然语言处理的挑战

  1. **词汇歧义:**这是一般只在单词中出现的第一级歧义。例如,当一个代码被赋予一个像“board”这样的词时,它不知道是把它当作名词还是动词。这导致这段代码的处理不明确。
  2. 语法级歧义:这是另一种类型的歧义,与机器如何感知它相比,它与短语的发音方式更有关系。例如,像这样的句子,“他用一顶蓝色的帽子升起了天窗”。这可能意味着两件事之一。要么他在蓝色帽子的帮助下升起一个天窗,要么他升起一个有红色帽子的天窗。
  3. **指称歧义:**使用代词的指称构成指称歧义。例如,两个女孩正在跑道上跑步。突然,她说,“我累坏了”。这个程序不可能解释两个女孩中谁累了。

自然语言处理|作者图片

构建自然语言处理器

在构建自然语言处理器时,总共有 5 个执行步骤:

  1. **词法分析:**NLP 算法对自然语言的处理从识别和分析输入单词的结构开始。这部分被称为词汇分析,词典代表一种语言中使用的各种单词和短语的选集。它是把一大块单词分成结构段落和句子。
  2. 句法分析:一旦句子的结构形成,句法分析就检查形成的句子和短语的语法。它还形成单词之间的关系,并消除逻辑上不正确的句子。例如,英语语言分析器拒绝句子,“一把伞打开一个人”。
  3. **语义分析:**在语义分析过程中,现在检查输入文本的意义,即,它提取句子中存在的所有单词的精确字典,并随后检查每个单词和短语的意义。这是通过理解手头的任务并将其与语义分析器相关联来完成的。例如,像‘热冰’这样的短语被拒绝。
  4. **语篇整合:**语篇整合步骤形成句子的故事。每一个句子都应该与它的前一个和后一个句子有关系。这些关系受到语篇整合的制约。
  5. **语用分析:**一旦所有的语法和句法检查完成,现在检查句子在现实世界中的相关性。在语用分析过程中,每个句子都被再次回顾和评估,这一次是用一般知识检查它们在现实世界中的适用性。

标记化、词干化和词条化

标记化

为了阅读和理解句子中的单词序列,标记化是将序列分成称为标记的更小单元的过程。这些标记可以是单词、数字,有时也可以是标点符号。标记化也称为分词。这是标记化工作原理的一个示例:

板球、棒球和曲棍球是主要以手为基础的运动。
标记化输出:"板球"、"棒球"、"和"、"曲棍球"、"正"、"主要"、"手"、"基础"、"运动"

句子的结尾和开头被称为单词边界,这个过程是为了理解给定句子的单词边界。

  • **Sent_tokenize 包:**这是句子标记化,将输入转换成句子。这个包可以在 Jupyter 笔记本from nltk.tokenize import sent_tokenize中使用这个命令安装
  • **Word_tokenize 包:**类似于句子分词,这个包把输入的文本分成单词。这个软件包可以使用命令from nltk.tokenize import word_tokenize安装在 Jupyter 笔记本上
  • **wordpunctokenizer 包:**除了单词标记化之外,这个包还将标点符号作为标记。可以使用from nltk.tokenize import WordPuncttokenizer进行安装

词干

当研究人类在对话中使用的自然语言时,由于语法原因会出现一些变化。例如,像 virtual、virtual 和 virtualization 这样的词在本质上意思相同,但在不同的句子中意思不同。为了让 NLTK 算法正确工作,它们必须理解这些变化。词干提取是一个启发性的过程,它理解单词的词根形式并帮助分析其含义。

  • **PorterStemmer 包:**这个包内置在 Python 中,使用 Porter 的算法计算词干。功能是这样的,一个输入单词“running”产生一个词干单词“run”。可以用这个命令from nltk.stem.porter import PorterStemmer将其安装到工作环境中
  • **LancasterStemmer 包:**Lancaster stemmer 的功能类似于波特的算法,但严格程度较低。它只是将单词的动词部分从其来源中删除。例如,单词“writing”在运行 Lancaster 算法后返回“writ”。可以用这个命令from nltk.stem.lancaster import LancasterStemmer将它导入到环境中
  • **SnowballStemmer 包:**这也与其他两个算法的工作方式相同,可以使用命令from nltk.stem.snowball import SnowballStemmer导入。这些算法具有可互换的用例,尽管它们在严格程度上有所不同。

词汇化

向单词添加形态学细节有助于提取它们各自的基本形式。这个过程是使用词汇化来执行的。词汇和词法分析都会导致词汇化。这一程序旨在消除屈折词尾。得到的基本形式称为引理。

  • **WordNetLemmatizer 包:**wordnet 函数根据所呈现的单词是用作名词还是代词来提取单词的基本形式。可以用下面的语句from nltk.stem import WordNetLemmatizer导入这个包

数据分块的概念

顾名思义,分块就是将数据分成块的过程。它在自然语言处理领域非常重要。组块的主要功能是像名词短语一样对不同的词类和短词短语进行分类。一旦标记化完成并且输入被分成标记,分块为算法标记它们以便更好地理解它们。分块使用了两种方法,我们将在下面阅读:

  • **向上分块:**向上或向上分块就是缩小问题。在组块的过程中,句子变得抽象,输入的单个单词和短语被概括。例如,像这样的问题,“公共汽车的目的是什么?”分块后,将回答“运输”
  • **向下分块:**与向上分块相反,在向下分块的过程中,我们深入语言,物体变得更加具体。例如,一个像“什么是汽车?”将产生具体的细节,如颜色,形状,品牌,大小等。汽车柱子被砸倒的照片。

定义语法规则和实现组块:在接下来的部分中,我们将在一个单词组块上实现英语语法规则。该过程将要求输出以弹出窗口的形式显示在屏幕上。 如果您在 Jupyter Notebook 中本地运行代码,则不需要额外的步骤。 * 但是为了在 Colab 中运行代码,我们需要在 Colab 本地环境中安装一个虚拟显示器。完成此操作的步骤如下所示。

* * *在 Google Colab 上构建虚拟显示引擎

为了将树显示为输出,我们的代码需要打开一个新的显示窗口。Tk,或tkinter通常会为你的界面创建 GUI(像一个新窗口)。但是 Colab 运行在云中的 web 服务器上。因此,它无法在运行它的本地计算机上打开新窗口。Colab 提供的唯一交互是通过网络笔记本界面。要显示我们需要显示分块代码的 NLTK 树,请在您的 Colab 环境中运行下面的代码。

### CREATE VIRTUAL DISPLAY #### Install X Virtual Frame Buffer
!apt-get install -y xvfb 
import os# create virtual display with size 1600x1200 and 16 bit color. Color can be changed to 24 or 8
os.system('Xvfb :1 -screen 0 1600x1200x16  &') # tell X clients to use our virtual DISPLAY :1.0.
os.environ['DISPLAY']=':1.0' ### INSTALL GHOSTSCRIPT (Required to display NLTK trees) ###
!apt install ghostscript python3-tk

在这个例子中,我们将执行名词短语组块,这是组块的一个类别。在这里,我们预定义了程序将用来执行组块的语法概念。

NP 组块被定义为不包含其他 NP 组块,并且依赖于词性标注的大部分信息。

**名词短语分块:**在下面的代码中,我们将执行名词短语(NP)分块,其中我们搜索对应于单个名词短语的组块。为了创建一个 NP-chunker,我们将定义一个组块语法规则(如下面的代码所示)。该算法的流程如下:

a.这条规则说,只要组块识别者找到一个可选的限定词(DT),后跟任意数量的形容词(JJ),然后是名词(NN),就应该形成一个 NP 组块。我们在一个示例句子上使用这个语法来构建组块解析器,并以图形方式将输出显示为一棵树。

实现分块算法的代码|作者

NLTK 树|上面代码的输出|作者图片

主题建模和识别数据中的模式

文档和讨论通常总是围绕主题展开。每一次谈话的基础都是一个话题,讨论围绕着这个话题展开。为了让 NLP 理解和处理人类对话,它需要在给定的输入中导出讨论的主题。为了进行同样的计算,算法对输入运行模式匹配理论来确定主题。这个过程叫做主题建模。它用于发现需要处理的文档的隐藏主题/核心。主题建模用于以下场景:

  • **文本分类:**它可以改进文本数据的分类,因为建模将相似的单词、名词和动作组合在一起,而不是使用单个单词作为单一特征。
  • **推荐系统:**基于推荐的系统依赖于建立相似内容的基础。因此,主题建模算法可以通过从给定数据计算相似性矩阵来最好地利用推荐系统。

上述分类代码的输出。我们观察到,NLP 算法正确地将特斯拉确定为网络链接中谈论最多的话题。|作者图片

完整的代码库

对于上面执行的两个代码笔记本,包含所有输入和输出的完整代码都在下面链接的存储库中。我强烈建议任何阅读本文的人一起浏览代码,以便更好地理解上面讨论的概念。

https://github.com/rjrahul24/ai-with-python-series/tree/main/08.%20Natural%20Language%20Processing

结论

戴维·巴洛在 Unsplash 上拍摄的照片

对语言及其与人类认知的联系的研究令人着迷。在本文和上面的代码中,我们执行了两个基本的语言处理任务——名词短语分块和文本分类。这些是最常用的 NLP 技术之一的基本实现。机器天生被设计成与数字打交道,而语言则完全相反,处理单词、短语、语法和许多无法直接教给机器的复杂结构。因此,NLP 是一个极其广阔的研究领域。本教程的目的是为进入这个多元宇宙提供一个起点。我建议您通过下面参考资料部分提到的链接来更好地掌握自然语言处理。

有趣的机器学习阅读

关于我

我是一名数据工程师和人工智能研究员,目前在微软 Xbox game analytics 工作,我在日常工作中实现了类似的管道,以分析游戏收购、使用和健康情况。除了我的专业工作,我还在研究如何实施人工智能来平衡世界各地受多年来逐渐气候变化影响的地区的经济。请随时在 Twitter 或 LinkedIn 上与我联系,讨论、提问或合作项目。

参考

  1. https://search enterprise ai . tech target . com/definition/natural-language-processing-NLP
  2. https://www . analyticsvidhya . com/blog/2020/07/top-10-自然语言处理应用-nlp/
  3. https://www . wonder flow . ai/blog/natural-language-processing-examples
  4. https://www.quora.com/What-is-pragmatic-analysis-in-NLP
  5. https://aclanthology.org/W17-5405.pdf
  6. https://www.oak-tree.tech/blog/data-science-nlp
  7. https://towards data science . com/natural-language-processing-NLP-for-machine-learning-d 44498845 d5b
  8. https://www . geeks forgeeks . org/NLP-chunking-and-chinking-with-regex/
  9. https://medium . com/grey atom/learning-pos-tagging-chunking-in-NLP-85 F7 f 811 A8 CB
  10. https://www . analyticssteps . com/blogs/what-are-recommendation-systems-machine-learning

一头扎进 Dash

原文:https://towardsdatascience.com/a-dive-into-dash-ed5561f11021?source=collection_archive---------38-----------------------

用于创建仪表板的 Python Web 框架

斯蒂芬·道森在 Unsplash 上拍摄的照片

在我的办公室工作中,有一些关于仪表板的讨论。通常,我们使用它们向业务用户表示数据,但是在新系统中,它被推到了次要位置。脑子里想着仪表盘,我想知道 Python 有什么,不是为了工作,而是为了个人使用。在谷歌上快速搜索后,我找到了一个名为“Dash”的 web 框架。因此,让我们来看看 Dash 是什么,安装它,然后运行一个简短的教程,或试运行,如何使用它。

什么是破折号?

Dash 是一个 Python 开源 web 框架,用于构建数据的图形化表示。这些数据显示在浏览器中,然后可以通过 URL 共享。2017 年首次发布。Dash 是基于 Flask 的 web 服务器功能,但也可以用 React.js 创建一个更具交互性的网页,用 plotly.js 使用更多的图表选项。

Dash 用于数据科学,更具体地说,用于研究数据。虽然您可以使用 PowerPoint 或 Libre Office Impress 等工具,但 Dash 允许您通过纯 Python 创建基于数据的图表。Dash 也不需要广泛的 web 开发知识,因为它的 Flask 实现是初学者友好的,没有背景经验也可以学习。

创建图表时,Dash 有多种选项,例如简单的条形图或饼图,但也可能包括其他功能。其中一个功能是使用下拉菜单或范围来提供更多的自定义数据。在分析您的图表时,您可以在屏幕上显示多个,这将允许您以不同的格式查看数据。

对于 Dash 的介绍,我们不会对选项太感兴趣。我们将简单地创建两个基本图形,并且只使用破折号。这意味着我们还不需要 Plotly 来满足我们的需求。这也意味着除了基本的 Dash,我们不需要安装任何东西。现在我们已经对 Dash 有了更多的了解,让我们直接开始安装吧。

安装仪表板

安装 Dash 相当简单。我们只需要一个命令就可以让它运行。如果您最近没有更新您的 pip 版本,请务必更新。

sudo pip3 install dash

对我来说,这就是我需要安装的全部。在一个教程中,我发现您可能还需要安装以下内容:

sudo pip3 install dash-html-components
sudo pip3 install dash-core-components

在您第一次安装 Dash 时,应该已经安装了额外的包。当我运行额外的安装时,我收到了尝试返回的“满足要求”消息。不过,包含这些内容总比抱歉好,以防某些东西没有正确安装或者任何版本过期。

使用破折号创建简单图形

现在我们已经准备好开始编码了,我首先创建了一个名为 graph_app.py 的文件。对于 HTML 组件和核心组件,使用别名是为了以后更容易引用。

import dash
import dash_core_components as dcc
import dash_html_components as html

接下来,我们将使用 Dash 创建应用程序。这可以通过使用以下代码行来完成:

app = dash.Dash()

我们的下一个代码将关注于创建实际的图形。对于数据,我现在输入数据,而不是从文件中读取。这主要是为了学习。为了创建一个仪表板,我们使用一个包含图表的 Div 与 web 页面进行交互。

首先,我们创建一个 H1 标题,在页面上创建一个基本名称。您可以使用任何您喜欢的描述符。

接下来,我们将创建第一个图形,这将是一个条形图。在这个图中,我们将首先声明 ID。接下来,我们将声明图形部分。这部分将包含我们的数据。在数据中,我们还将声明我们的图形类型,即“条形图”。为了声明我们的图表,我们使用 dash 核心组件。

之后,我们将创建我们的第二个图形,这将是一个线图。对于这个图,我们需要将数据包含在一个字典中。这些数据本身将会出现在我们价值观的另一个字典中。然后,我们还将声明图形的大小。最后,我们将声明图的 id。完成后,我们可以关闭 Div。

最后,我们将在代码的 Flask 部分启动我们的应用程序。当我们将应用程序设置为运行服务器时,我还声明了主机位置,因为由于在服务器上运行代码,默认位置在我的机器上是不可访问的。如果在本地运行,可能不需要声明主机。

if __name__ == '__main__':
     app.run_server(debug=True, host="your_host_address")

现在,我们要做的就是运行代码。

本地运行的应用程序。

最后,我们只需导航到网页。

结论

Dash 是一个 Python Web 框架,用于可视化网页上的数据。它利用 Flask 来创建一个 web 服务器,使得只需很少的前端经验就可以轻松创建页面。可以使用这个库创建图表来研究和分析数据。它类似于来自 PowerPoint 或 Libre Office Impress 的图表,但是是用纯 Python 编写的。

Dash 从一开始就非常简单,理解相关的组件,比如在哪里输入值,或者在哪里设置图形类型,都是相当简单易懂的。也许下次您需要创建图表时,您可以考虑使用 Python 来可视化您的数据。我认为 Dash 可能是一个非常有用的工具,学习起来很有趣。虽然我们只坚持基本的,但 Dash 提供了各种其他选项。该项目可以从我们离开的地方扩展,所以我认为 Dash 可能是我将来会再次使用的工具。我现在唯一后悔的是还在大学时代就学会了一点短跑。利用 Python 比用 Excel 完成作业要有趣得多。希望你也发现 Dash 同样有趣。让我知道你的想法,并随意评论你用 Dash 构建的图表。下次见,干杯!

用我的 每周简讯 免费阅读我的所有文章,谢谢!

想阅读介质上的所有文章?成为中等 成员 今天!

查看我最近的文章:

https://python.plainenglish.io/getting-the-weather-with-python-dce8f20331fd https://python.plainenglish.io/build-your-own-plex-client-using-python-11cf2e566262

参考资料:

https://dash.plotly.com/introduction https://pypi.org/project/dash/ https://realpython.com/python-dash/

制表文本数据的 DL 解决方案

原文:https://towardsdatascience.com/a-dl-solution-for-tab-text-data-f92e2b68eb16?source=collection_archive---------27-----------------------

它能打败 XGBoost 吗?

图像去飞溅

动机

DL 模型在视觉和自然语言处理等领域取得了巨大的成就。然而,当涉及到表格数据时,它似乎显示出较低的成功率,并且在大多数应用程序中,它的性能比 XGBoost 差。

有很多原因可以解释这种现象,例如,对于表格数据没有明显的可排序度量(当我们处理布尔变量或类别时,我们可以优先假定的唯一度量是离散的)。但是,对组合数据的处理很少:既包含表格列又包含文本的数据。在这篇文章中,我将介绍这种数据库的 DL 解决方案。我们的目标变量将是一个多分类变量。

为了展示引擎,我将使用 scikit-learn 的数据集服务创建一个模拟数据库。

**def** get_mock_db(my_pickle_name):
     covert=fetch_covtype(data_home=**None**, download_if_missing=**True**,
                            random_state=**None**, shuffle=**False**)
     categories=[**'alt.atheism'**, **'soc.religion.christian'**,            
                       **'comp.graphics'**, **'sci.med'**]
     twenty_train = fetch_20newsgroups(subset=**'train'**,remove= 
           (**'headers'**, **'footers'**, **'quotes'**), categories=categories,          
                                shuffle=**True**, random_state=42)clean_text=[(i.replace(**'\n'**,**''**).replace(**'\t'**,**''**).replace('\\',
     ''),j) for i,j in **z**ip(twenty_train.data,twenty_train.target)]    
     clean_text =[  i **for** i **in** clean_text **if** len(i[0])>20]
     len_0 =len([i **for** i **in** clean_text **if** i[1]==0])
     len_1 =len([i **for** i **in** clean_text **if** i[1]==1])
     len_2= len([i **for** i **in** clean_text **if** i[1]==2])
     len_3 =len([i **for** i **in** clean_text **if** i[1]==3])
 a, b =covert.data.shape
 cov_0 = [covert.data[j] **for** j **in** range(a) **if** covert[**'target'**]
     [j] ==4 ][:len_0]cntr=0
**for** j **in** range(len(clean_text)):
    **if** clean_text[j][1]==0:
            a1,b1 =clean_text[j]
            clean_text[j]= (a1, [cntr],b1)
        cntr+=1
    **if** cntr ==len_0:
            **break** cov_1 = [covert.data[j] **for** j **in** range(a) **if** covert[**'target'**]
     [j]==1 ][:len_1]
cntr=0
**for** j **in** range(len(clean_text)):
    **if** (len(clean_text[j])==2) **and** clean_text[j][1]==1:
        a1,b1 =clean_text[j]
        clean_text[j]= (a1, cov_1[cntr],b1)
        cntr+=1
    **if** cntr ==len_0:
        **break**cov_2=[covert.data[j] **for** j **in** range(a) **if** covert[**'target'**][j]==3 ][:len_2]
cntr=0
**for** j **in** range(len(clean_text)):
    **if** (len(clean_text[j])==2) **and** clean_text[j][1]==2:
        a1,b1 =clean_text[j]
        clean_text[j]= (a1, cov_2[cntr],b1)
        cntr+=1
    **if** cntr ==len_0:
        **break**cov_3=[covert.data[j] **for** j **in** range(a) **if** covert[**'target'**][j]==3 ][:len_3]
cntr=0
**for** j **in** range(len(clean_text)):
    **if** (len(clean_text[j])==2) **and** clean_text[j][1]==3:
        a1,b1 =clean_text[j]
        clean_text[j]= (a1, cov_3[cntr],b1)
        cntr+=1
    **if** cntr ==len_0:
        **break
with** open(my_pickle_name, **'wb'**) **as** f:
    pickle.dump(clean_text, f, pickle.HIGHEST_PROTOCOL)
print (**"files_genrated"**)
**return**

我们可以用多种方式转换表格数据,比如按原样接受整数列,执行确定性聚合,或者将布尔变量转换为一个统一的编码表示。然而,在这篇文章中,我将尝试一种不同的方法:我将为。表格数据。为此,我们将使用 Tabnet 。这个包提供了几种嵌入方法。

下面的代码执行这样的嵌入

**from** pytorch_tabnet.pretraining **import** TabNetPretrainer
clf = TabNetPretrainer()   clf.fit(X_train[:n_steps])#Here we embed the data 
embed_tabular = clf.predict(X_test)[1]raw_data =[(text[j], embed_tabular[j], label[j]i[0],j,i[2]) **for** i,j **in** zip(data, embed_tabular]

现在,数据已经准备好并保存到 pickle 文件中。我们可以使用 Huggingsface 的分词器对文本进行预处理

标记化

在这一部分中,我们将文本转换成一种允许 Huggingsface 引擎执行嵌入的格式。我习惯了标记化器的类型:

伯特和FnetT5**。**前者因变压器的出现而广为人知,后者是一种全新的方法,建议用 FFT 代替多头层。人们可以在这里了解它。

**def** get_tokenizer(data_yaml, bert_tokeinizer_name):
    **if** data_yaml == enum_obj.huggins_embedding.fnet.value:
        **return** FNetTokenizer.from_pretrained(**'google/fnet-base'**)
    tokenizer = AutoTokenizer.from_pretrained(bert_tokeinizer_name,   
             use_fast=**True**)
    **return** tokenizerbatch_encoding = tokenizer.batch_encode_plus([text  **for text ** **in** data], **tokenizer_params, return_tensors=**'pt'**)

根据外部 Yaml 标志,我们选择我们的记号赋予器。最后一行是用伪代码写的,表示令牌化步骤本身。

创建张量文件夹

为了训练和评估,我们需要将数据(表格和文本的组合)放入张量文件的文件夹中,这些文件将使用 Pytorch 的数据加载器上传到神经网络。我们给出了生成这个文件夹的几段代码。我们从这两个函数开始:

 **def** bert_gen_tensor(input_t, tab_tensor, lab_all_Place,     
       file_name, batch_enc, index_0):
    input_m = torch.squeeze(torch.index_select(
         batch_enc[**"attention_mask"**], dim=0,                   
                 index=torch.tensor(index_0))) 
    torch.save([input_t, input_m, tab_tensor, lab_all_Place],     
            file_name)
    **return

def** fnet_gen_tensor(input_t, tab_tensor, lab_all_Place,          
           file_name, batch_enc=**None**, index_0=-1):  
     torch.save([input_t, tab_tensor, lab_all_Place], file_name)
     **return**

人们可以看出它们几乎是相似的。它们反映了 BertFnet 所需要的张量之间的差异, Bert 需要一个键: attention_mask" ,而 Fnet 不存在这个键。因此,一方面,我们需要为每种嵌入方法保存不同的张量集,而另一方面,我们希望有一个唯一的代码。我们如下解决这个问题:

**def** generate_data_folder_w_tensor(data_lab_and_docs, data_yaml):
 .
 . **if** data_yaml[**'embed_val'**] ==               
                       enum_obj.huggins_embedding.fnet.value:
        proc_func = fnet_gen_tensor
    **else**:
        proc_func = bert_gen_tensor

除了关键问题之外,该文件还包含输入数据、表格数据和标签。我们对这两种方法的需求处理如下:

我们现在准备迭代数据项并创建 tensors 文件夹

**for** i, data **in** enumerate(data_lab_and_docs):
        file_name = pref_val + **"_"** + str(i) + **"_"** + suff_val
        tab_tensor = torch.tensor(data[1], dtype=torch.float32)
        input_t =  
      torch.squeeze(torch.index_select(batch_encoding[**"input_ids"**],dim=0,    index=torch.tensor(i)))

        proc_func(input_t, tab_tensor,  data[2],file_name, 
                batch_enc= batch_encoding, 0index_0=i)
        dic_for_pic[file_name]= data[2]

        **with** open(data_yaml[**'labales_file'**], **'wb'**) **as** f:
           pickle.dump(dic_for_pic, f, pickle.HIGHEST_PROTOCOL)

这个循环非常简单;我们设置文件名并使用函数来保存信息。我们创建一个字典,将文件名映射到它们的标签值,以备将来需要。

当过程结束时,我们有一个 tensors 文件夹用于训练和评估步骤。我们差不多可以开始训练了。差不多?是啊!因为我们正在使用 Pytorch,所以我们需要创建我们的数据加载器。

**import** torch
**import** enum_collection **as** enum_obj
**import** random

**class** dataset_tensor :
    **def** __init__ (self, list_of_files, embed_val ):

        self.list_of_files =list_of_files
        random.shuffle(list_of_files)
        **if** embed_val==enum_obj.huggins_embedding.fnet.value:
            self.ref =[0,1,2]
        **else**:
            self.ref=[1,2,3]

    **def** __getitem__(self, idx):
       aa =torch.load(self.list_of_files[idx])
       **return** aa[0], aa[self.ref[0]], a[self.ref[1]],aa[self.ref[2]]

    **def** __len__(self) :
        **return** len(self.list_of_files)

您可能会注意到,这是 Pytorch 数据加载器的标准结构。有一件事需要澄清: self.ref 数组。由于 FnetBert 在单个文件中使用不同的张量集合,并且我们希望使用相同的训练程序,我们为 Fnet 输出一个 void 变量(输入项两次)。 self.ref 决定我们输出的文件中张量的索引。

型号

这可能是代码中最有趣的部分。该模型执行两个步骤:

  • 嵌入按照给定的配方( FnetBert )嵌入标记化的数据
  • 处理嵌入张量,并将其映射为类别数量大小的张量

第一步取决于我们选择的嵌入类型。因此,我们要求他们每个人都有一个特殊的“前锋

**class** my_fnet(FNetForSequenceClassification):

    **def** __init__(self, config, dim=768):
        super(my_fnet, self).__init__(config )
        self.dim= dim
        self.num_labels = 4

        self.distilbert = FNetModel(config)
        self.init_weights()

        self.pre_classifier = nn.Linear(self.dim, self.dim)
    **def** forward(self,  input_ids=**None**):
            outputs = self.distilbert( input_ids=input_ids)
            pooled_output = outputs[1]
            pooled_output = self.dropout(pooled_output)
            **return** pooled_output**class** my_bert(BertForSequenceClassification):
 **def** __init__(self, config, dim=768):
        super(my_bert, self).__init__(config )
        self.dim= dim
        self.num_labels = 4
   self.distilbert = BertModel(config)
        self.init_weights()

        self.pre_classifier = nn.Linear(self.dim, self.dim)
    **def** forward(self,
                    input_ids=**None**,
                    attention_mask=**None**,
                    head_mask=**None**,
                    inputs_embeds=**None**,
                    output_attentions=**None**,
                    output_hidden_states=**None**,
                    return_dict=**None**,

                    ):
            return_dict = return_dict 
          **if** return_dict **is not None else** self.config.use_return_dict
          distilbert_output = self.distilbert(
                input_ids=input_ids,
                attention_mask=attention_mask,
                head_mask=head_mask,
                inputs_embeds=inputs_embeds,
                output_attentions=output_attentions,
                output_hidden_states=output_hidden_states,
                return_dict=return_dict,
            )
            hidden_state = distilbert_output[0]  pooled_output = hidden_state[:, 0]  *# pooled_output = self.pre_classifier(pooled_output)  
            # hidden_state = distilbert_output[0]* **return** pooled_output

模型本身如下所示:

**class** my_model(nn.Module):
   **def** __init__(self, data_yaml, my_tab_form=1, dim=768):
        super(my_model, self).__init__( )
        self.forward =self.bert_forward
        **if** data_yaml[**'embed_val'**] ==    
               eum_obj.huggins_embedding.distil_bert.value:
            self.dist_model  = my_dist.from_pretrained(**'distilbert-           
                         base-multilingual-cased'**,num_labels=4 )
        **elif** data_yaml[**'embed_val'**] ==   
          enum_obj.huggins_embedding.base_bert.value:
           self.dist_model =   
           my_bert.from_pretrained(**'bert-base-multilingual-cased'**,  
                             num_labels=4)
        **else**:
            self.dist_model = my_fnet.from_pretrained(**'google/fnet-     
                       base'**, num_labels=4)
            self.forward = self.fnet_forward
        **if** my_tab_form>0 :
           localtab= data_yaml[**'tab_format'**]
        **else** :
            localtab =my_tab_form
        **if** localtab ==  enum_obj.tab_label.no_tab.value:
            print (**"no_tab"**)
            self.embed_to_fc = self.cat_no_tab
            self.tab_dim = 0
        **else** :
            self.embed_to_fc = self.cat_w_tab
            self.tab_dim =data_yaml[**'tab_dim'**]

        self.dim=dim
        self.num_labels =4

        self.pre_classifier = nn.Linear( self.dim, self.dim)
        self.inter_m0= nn.Linear(self.dim +self.tab_dim,216)

        self.inter_m1 = nn.Linear(216,64)
        self.inter_m1a = nn.Linear(64, 32)

        self.inter_m3 = nn.Linear(32, self.num_labels)
        self.classifier = nn.Linear(self.dim, self.num_labels)
        self.dropout = nn.Dropout(0.2)

   **def** cat_no_tab (self, hidden, x):
        **return** hidden
    **def** cat_w_tab (self, hidden, x):
        **return** torch.cat((hidden, x),dim=1)

    **def** fnet_forward(self, x,
                 input_ids=**None**, attention_mask=**None**):

        hidden_state = self.dist_model(input_ids)

        pooled_output = torch.cat((hidden_state, x), dim=1)
        pooled_output = self.inter_m0(pooled_output)  *# (bs, dim)* pooled_output = nn.ReLU()(pooled_output)  *# (bs, dim)* pooled_output = self.dropout(pooled_output)  *# (bs, dim)* pooled_output = self.inter_m1(pooled_output)  *# (bs, dim)* pooled_output = nn.ReLU()(pooled_output)  *# (bs, dim)* pooled_output = self.dropout(pooled_output)  *# (bs, dim)* pooled_output = self.inter_m1a(pooled_output)   pooled_output = nn.ReLU()(pooled_output)  pooled_output = self.dropout(pooled_output)    logits = self.inter_m3(pooled_output)  **return** logits

   **def** bert_forward(self, x, input_ids=**None**,                
        attention_mask=**None**) :
        hidden_state =self.dist_model(input_ids, attention_mask)
        pooled_output = self.embed_to_fc(hidden_state, x)

        pooled_output = self.inter_m0(pooled_output)  pooled_output = nn.ReLU()(pooled_output)  *# (bs, dim)* pooled_output = self.dropout(pooled_output)  *# (bs, dim)* pooled_output = self.inter_m1(pooled_output)  *# (bs, dim)* pooled_output = nn.ReLU()(pooled_output)  *# (bs, dim)* pooled_output = self.dropout(pooled_output)  *# (bs, dim)* pooled_output = self.inter_m1a (pooled_output)  *# (bs, dim)* pooled_output = nn.ReLU()(pooled_output)  *# (bs, dim)* pooled_output = self.dropout(pooled_output)  *# (bs, dim)* logits = self.inter_m3(pooled_output)  *# (bs, num_labels)* **return** logits

除了我们已经讨论过的不同转发功能之外。我们有 my_tab_form 标志。这个标志表示我们是单独使用文本还是组合数据(我们通常使用后者)。在代码方面,我们用一个 void 函数代替 torch.cat (它输出输入张量)

**if** my_tab_form>0 :
           localtab= data_yaml[**'tab_format'**]
**else** :
            localtab =my_tab_form

损失函数

我们的损失往往是一个标准的交叉熵。尽管如此,因为对于某些应用程序,我们需要减少与其中一个类别相关联的特定故障,所以我添加了几个特定的函数来“唯一地惩罚”这些错误。人们可以在这里阅读更多

主循环

最后,我们可以使用来自外部的整个块来执行训练步骤。我们从带来数据开始:

**with** open(**"project_yaml.yaml"**, **'r'**) **as** stream:
    data_yaml = yaml.safe_load(stream)
list_of_files =os.listdir(data_yaml[**'tensors_folder'**])
list_of_files =[data_yaml[**'tensors_folder'**]+i **for** i **in** list_of_files]X_train,X_test =train_test_split(list_of_files, test_size=0.2)# Creating dataloader!!
train_t =dataset_tensor(X_train, data_yaml[**'embed_val'**])
train_loader = DataLoader(train_t, batch_size=data_yaml[**'batch_size'**], shuffle=**True**)

test_t = dataset_tensor(X_test, data_yaml[**'embed_val'**])
test_loader = DataLoader(test_t, batch_size=data_yaml[**'batch_size'**], shuffle=**True**)

我们上传 Yaml 文件并将数据分割以进行训练和测试(例如,0.2 的测试大小不需要深入的理论支持)。对于不太熟悉 torch 的读者来说,最后几行为训练和测试文件创建了数据加载器。

device = **""
if** torch.cuda.is_available():
    device = torch.device(**"cuda:0"**)

model = my_model(data_yaml) **#pre -training usage
if** data_yaml[**'improv_model'**]:
    print(**"Loading mode"**)
    model_place = data_yaml[**'pre_trained_folder'**]
    print (model_place)
    model.load_state_dict(torch.load(model_place, map_location=**'cpu'**))

**if** device:
    model =model.to(device)

我们为拥有 GPU 的幸运读者设置了设备。之后,我们定义模型结构并上传其权重,以防我们希望使用预训练的模型。

optimizer = torch.optim.AdamW(model.parameters(),
                              lr=1e-5, eps=1e-8)
loss_man = loss_manager(data_yaml[**'batch_size'**],   
        data_yaml[**'target_val'**],data_yaml[**'reg_term'**])
model.train()

我们定义了优化器(它应该降低大多数变压器任务的学习率)和损失函数。现在我们可以执行训练迭代

**for** i_epoc **in** range(data_yaml[**'n_epochs'**]):
    running_loss = 0.0
    counter_a=0
    **for** batch_idx, data **in** enumerate(train_loader):

        a, b, d, c=  data
        **if** device :
            a=a.to(device)
            b=b.to(device)
            c=c.to(device)
            d=d.to(device)

        ss=model(x=d, input_ids=a, attention_mask=b)

        loss =loss_man.crit(ss, c)
        running_loss += loss.item()

        loss.backward()
        print(loss, batch_idx)
        optimizer.step()
        optimizer.zero_grad()

    print (**"Epoch loss= "**,running_loss/(counter_a+0.))
    print (**"End of epoc"**)
    torch.save(model.state_dict(),  data_yaml[**'models_folder'**] + **"model_epoch_"** + str(i_epoc) + **".bin"**)

我们可以在培训结束时使用 tqdm 添加一个评估循环:

**from** tqdm **import** tqdm
.
.
. **with** torch.no_grad():
    **with** tqdm(total=len(test_loader), ncols=70) **as** pbar:
        labels = []
        predic_y = []
        **for** batch_idx, data **in** enumerate(test_loader):

            a, b, d, c = data
            **if** device:
                a = a.to(device)
                b = b.to(device)
                c = c.to(device)
                d = d.to(device)
            labels.append(c)
            outp = model(x=d, input_ids=a, attention_mask=b)
            probs = nn.functional.softmax(outp, dim=1)
            predic_y.append(probs)
            pbar.update(1)

        y_true, y_pred = convert_eval_score_and_label_to_np(labels, predic_y)

结果和他们的分析留给他们自己感兴趣的读者。

摘要

我们提出了一个组合数据模型和一个训练它的 DL 引擎。从架构上讲,我们提出了一个简单的神经网络。但是,对于一些经过训练和测试的任务,它显示出比类似的 XGBoost 模型更好的结果。这给了 DL 引擎能够很好地处理表格数据的希望。除了单纯的 DL 方面之外,我相信组合数据任务被赋予了很好的数学谜题,因为它不仅研究不同的数据类型,还研究它们所导致的度量和拓扑。可排序和不可排序变量以及完全和不完全拓扑之间的接口可以提供广泛的研究领域。在阅读这篇文章时,人们可能会问的另一个问题是,我们是否可以改进用于 Fnet 的 FFT(例如,使用小波)。

承认

我希望感谢 Uri Itai 在撰写本文期间提供了富有成效的想法。

代码位于这里。

ML 建模中的一个公平的世界,男人和女人应该得到同样的对待

原文:https://towardsdatascience.com/a-fair-world-in-ml-modeling-where-men-and-women-should-be-treated-the-same-way-f7d3e78e83b1?source=collection_archive---------16-----------------------

公平和偏见

基于 UCL 成人数据的 ML 模型性别公平性研究

由 Unsplash 上的地图框拍摄

背景

不幸的是,我们生活的这个世界曾经被各种刻板印象和歧视所主宰——种族、性别、取向等等。只是在最近几十年,随着社会运动、教育和技术的进步,人们开始接受多样性并促进平等。然而,在很长一段时间里,这些无知的偏见严重冲击着人们生活的方方面面。

数据是真实世界的反映。我们从过去收集的信息不可避免地带有偏见或不公平。我们是数据科学家,我们从历史数据中训练机器学习/AI 模型。我们一直在努力工作,以确保我们的模型是“准确的”。但是隐含在数据中的偏见通常被 ML 模型所忽略和继承,这可能会造成负面的社会影响。

在本文中,我将使用 20 世纪 90 年代收集的 UCL 成人数据集来说明:

  • 如何将公平定义为一种衡量标准
  • ML 模型是如何不公平的
  • 如何通过简单的特征选择来提高模型的公平性

数据集

UCL 成人数据集是很多初学者熟悉二分类算法的经典数据集。该数据集是从 1994 年美国人口普查数据库中提取的。每行代表一个人。属性包括基本的人口统计变量,如年龄、性别、种族、职业、教育等。预测任务是确定一个人的年收入是否超过 5 万。数据集经过预处理,因此大部分都是干净的,只有少数值缺失。

在最近的许多研究中,本文用于 ML 公平性研究(例如 this )有两个充分的理由:

  1. 它是在一个性别和种族平等没有得到适当解决的时代收集的
  2. 它包含敏感变量,即反映数据中社会偏见的种族和性别

在这项研究中,我们将使用这个数据集来训练 ML 模型,并检查它们的公平性和准确性。

公平定义

那么,我们如何将公平性定义为一个数据科学指标呢?我们可以利用在我们如何分析 COMPAS 累犯算法中使用的定义,该论文由杰夫·拉森、苏亚·马特、劳伦·基什内尔、朱莉娅·安格温在 2016 年撰写,研究 COMPAS 累犯算法中的种族偏见。在文章中,作者强调指出:

黑人被告经常被预测比他们实际上有更高的再犯风险。我们的分析发现,与白人被告相比,两年内没有再犯的黑人被告被错误归类为高风险的可能性几乎是白人被告的两倍(45%比 23%)。

白人被告通常被认为比他们更不危险。我们的分析发现,在接下来的两年内再次犯罪的白人被告被错误地标记为低风险的几率几乎是黑人再次犯罪的两倍(48%对 28%)。

具体来说,比较黑人和白人被告的假阳性(FP)和假阴性(FN)率,以测试算法是否对任何种族有偏见。

在我们的上下文中,我们将比较男性和女性之间的 FP & FN 比率。这是一种直观的衡量方法,可以看出模型是否倾向于预测某一性别的高收入。

数据中的偏差

像任何数据科学项目一样,第一步总是执行探索性数据分析(EDA)来理解数据。

训练数据中有 32561 条记录,其中男性 21790 条,女性 10771 条。男性年收入超过 5 万英镑的可能性是女性的 3 倍(30.6%比 10.9%)。

作者图片

进一步按职业和性别细分数据,很明显,在同一职业中,男性比女性挣得多的可能性要大得多。

按作者分类的图片——按职业和性别分类的高收入百分比

同样学历的男性和女性之间也存在类似的收入差距。一个拥有学士学位的女性和一个拥有高等学位的男性一样有可能挣到 5 万多英镑。

按作者分类的图片——按教育和性别分类的高收入百分比

从上面的分布来看,数据集呈现出对女性的强烈负面偏见——无论职业或教育水平如何,她们往往挣得更少。如果没有任何干预,我们从数据集训练的模型也可能有很大的偏差。垃圾进,垃圾出。

ML 实验和见解

实验的目标是:

  • 测试模型公平性(男性和女性的 FP 和 FN 比率)
  • 测试简单方法(如特征选择)对模型公平性的影响

预处理

数据集已经非常干净,因此除了删除缺少值的记录之外,不需要太多的数据清理。就特征而言,教育、原籍国、种族被删除。 Education 有一个数字双射变量 education-num。母国种族也是敏感变量。在这项研究中,他们被移除以关注性别偏见。

分类变量,即工作类别婚姻状况、职业关系是一次性编码的。由于数据集中男性样本较多,因此执行随机向下采样,以确保两种性别的记录数量相同。此外,创建单独的标准化数据集(标准标准化)来训练逻辑回归。

在上述预处理步骤之后,训练数据集包括 19,546 条记录。留下 20% (3,910)的记录作为评估数据集来确定模型阈值。一个独立的测试数据集包含 15,055 条记录。基于测试数据集上的模型性能报告最终结果。

基线模型

训练了三个 ML 模型——逻辑回归、随机森林分类器和 LightGBM 分类器。模型超参数保持为缺省值。这些训练好的模型在评估数据集上评分。达到最高 F1 分数的阈值将被选择并应用于测试结果。

调查的结果

不出所料,所有这些模型在测试数据集上的预测中都表现出对女性的强烈偏好。

  • 女性经常被错误地预测为比她们实际上更穷。与男性相比,富裕女性被错误归类为低收入的可能性大约为 30%-40%*(男性对女性,LR: 26%对 38%,RF: 25%对 34%,LGB: 22%对 31%)
  • 男性经常被错误地预测为比他们实际上更富有。贫穷男性比女性更有可能被错误归类为高收入人群(男性比女性,LR:18%比 4%,RF: 18%比 5%,LGB: 16%比 4%)

*计算方式([高%]—[低%])/[低%]。

按作者分类的图像—所有基线模型的 FP(差误分类)和 FN(富误分类)率

偏差也反映在模型得分分布中。男性得分曲线明显更偏向右侧,得分中位数高得多。还值得一提的是,该模型对某些男性特别有信心,并给该群体打高分(右端的尖峰)。没有模特信任的这种女性群体。

按作者分类的图片— LGB 模型得分分布,男性 1 分

假设银行使用这个模型来评估贷款申请人的财务状况。你是一位申请贷款的女士,和另一位有相似教育背景和职业的男士一起。你的申请被拒绝了,但是这个人被接受了,因为模型给出了不同的预测。你会有什么感觉?数据科学家可能会认为该模型只是在学习历史模式。但是权力越大,责任越大。人工智能应用在世界各地产生了巨大的影响。当然,我们希望 ML 模特界有一个公平的世界,男人和女人应该得到同样的对待。这不会很难。

通过特征选择减轻不公平

这可能是最直观的解决方案——我们能移除那些导致模型偏差的特征吗?

在上面的案例研究中,我们不仅需要移除性别变量,还需要移除与性别密切相关的变量。快速检查的方法是通过皮尔逊相关。如下图所示。性别与婚姻状态和*关系下的一些一次性编码类别密切相关。*例如:

  • 关系 _ 丈夫只适用于男性
  • 关系 _ 妻子只适用于女性
  • 婚姻状况-已婚-公民-配偶是指与平民配偶结婚。他们大多数是男性
  • 婚姻状况 _ 已婚-未婚大多是女性

因此,我们可以简单地从数据集中删除三个变量,即性别、婚姻状况、关系,以阻止性别相关信息流向 ML 模型。

按作者分类的图片—皮尔逊与性别相关(蓝色与男性相关)

在特征选择之后,让我们检查重新训练的模型的公平性。性别偏见明显减少!富误分类率差从约 30%-40%减少到约 10%-30%,贫分类率差从约 300%减少到约 30%-40%。

尽管有了巨大的进步,我们不得不承认这不是完美的解决方案,因为这个模型仍然偏向于男性富有。这可能是因为其他变量中嵌入了性别信息,例如职业。从上面的图表可以看出,某些职业,如技工或行政文员,也与性别相关。但是,在这种情况下,我们不能删除职业变量,因为它非常重要。

按作者分类的图片—所有重新训练模型的 FP(差误分类)和 FN(富误分类)比率

公平的模型是好模型吗?

您可能已经注意到了基线和重新训练模型之间的差异。男性和女性的新生力量比率相对一致。但是再培训模型的 FP 率要差得多,尤其是对低收入女性而言。经过再训练的模型更公平,只是因为它们对所有低收入人群做出了同样糟糕的预测。

在许多情况下,当变量被移除时,模型性能会恶化。以 LGB 模型为例,基线模型更准确,AUC 和 F1 更高。

按作者分类的图片— LGB 模型比较—基线和重新训练

严酷的事实是,额外的公平不是免费的。通常需要用较低的预测精度来补偿。我们不是生活在社会平等的最好时代,我们的过去甚至更糟。由于时代的特点,从“青铜时代”收集的数据很可能是有偏差的。一个完全精确的模型会记住过去的所有模式,包括那些歧视。任何干预都会迫使模型偏离最准确的配置。

那么…公平的模型是好模型吗?是的,它总是一个好模型。世界上有许多好东西,它们都是有价格的。我们追求环保,但我们不会关闭所有的工厂。我们希望多元化,但我们不允许任何人未经适当许可就进入这个国家。我们需要一个公平的模型,但我们不能完全放弃准确性。需要探索社会公正和模型可用性之间的中间点,以确保数据科学家在“不作恶”的同时“创造价值”。

结尾注释

文章中介绍的技术非常简单。过去几年出现了许多复杂而有效的算法——变量正则化、建模后调整、超参数优化……特征选择可能是所有技术中最琐碎的技术。

我写这篇关于特性选择的文章有两个原因:

  1. 我刚刚开始这方面的研究,现在我还没有 100%的信心能很好地解释这些复杂的算法。
  2. 更重要的是,我迫不及待地想向你们展示,通过使用这样一个简单的技术,我们已经可以在如此大的程度上促进公平。

刻板印象来源于无知。数据科学改变了世界。当我们继续这样做的时候,只需要一小步就能产生巨大的影响。为什么不这样做呢?

跨多个可扩展数据中心的容错 Kafka 复制工具

原文:https://towardsdatascience.com/a-fault-tolerant-kafka-replication-tool-across-multiple-datacenters-that-scales-1a36a96764b1?source=collection_archive---------23-----------------------

为 Apache Kafka 引入新的跨数据中心复制工具

图片由Pixabay.com上的苏曼利提供

介绍

Apache Kafka 是事实上的数据流平台,用于高性能数据管道、流分析和关键任务应用。对于企业来说,随着业务的不断增长,许多场景将需要从一个 Kafka 实例发展到多个实例。例如,关键服务可以迁移并在专用实例上运行,以实现更好的性能和隔离,从而满足 服务级别协议 或目标

另一个例子是灾难恢复(DR)——主数据中心中的实例被持续镜像到备份数据中心。当灾难发生在主实例中时,应用程序(或者称为“服务”)将快速故障切换到备份数据中心,并在最短的停机时间内继续运行。

最后但同样重要的是,当企业在多数据中心模式下运营时,数据首先被路由到地理位置位置附近的数据中心,然后被传输到远程数据中心的中央集群,称为“聚合集群,以获得数据的整体和完整视图。

上述任何一种场景都需要一种复制实时数据的工具,它有 5 个要求:(1)容错和水平扩展,(2)低延迟和高性能,(3)跨数据中心,(4)非常强的消息传递保证,(5)简单透明的故障转移和回切到应用程序。

产品调查

名为 MirrorMaker 的传统开源工具可以将数据从一个 Kafka 实例复制到另一个 Kafka 实例。但是,它有几个缺点,使得维护低延迟多数据中心部署和构建透明的故障转移和故障恢复计划非常困难,主要原因如下:

  • 没有干净的机制在镜像的 Kafka 实例之间迁移生产者或消费者。来自两个实例的消费者补偿对彼此没有意义。
  • 重新平衡会导致延迟峰值,这可能会触发进一步的重新平衡,因为它使用高级消费者 API。

来自优步的 uReplicator 解决了一些镜子制造商的问题。但是它使用 Apache Helix,这需要额外的领域知识和维护。

合流复制器应该是一个更好的解决方案,但它是一个专有的企业软件。

我们想推广 MirrorMaker 2 (或者叫 MM2 ),一个新的 Kafka 组件来替代遗留的 MirrorMaker。它满足了在 Kafka 实例之间跨数据中心复制数据的所有上述五个要求。

下面,我们将讨论 MM2 的三个主要实际使用案例:

迁移到新的 Kafka 实例

随着工作量的增加,以下风险最终会暴露在一个 Kafka 实例上:

  • Kafka 实例的任何动荡都会影响所有服务或应用程序
  • 资源竞争:服务与共享资源竞争
  • 不可预测的 SLO:一个服务可能会占用无限量的资源,导致其他服务无法满足 SLO 的需求
  • 恢复和维护速度较慢:当数据量和工作负载较大时,Kafka 实例中重新平衡数据分区的速度会变慢。
  • 没有“一刀切”:一套配置不能满足不同服务的“冲突”期望(稳定性高于性能,性能高于一致性)
  • Kafka 实例的任何维护(例如升级、节点交换)都需要与所有工程团队沟通

为了减轻上述风险:可以考虑在专用 Kafka 实例上运行关键服务。为了从一个 Kafka 实例迁移到另一个实例,AWS 为他们的托管 Kafka 提供了一个教程,可以推广到开源的 Apache Kafka。

灾难恢复

尽管 Kafka 实例中的数据在所有代理中都有 3 个副本,但当所有代理都位于同一区域时,整个实例仍然可能不可用,这可能会突然脱机,或者大多数代理由于一些机架停机而脱机。为了实现更高的可用性,设置一个备份 Kafka 实例并不断地从主实例复制到备份实例变得非常重要。当主服务器不可用时,所有服务都被路由到备份服务器。

最简单的方法是,当主服务器宕机时,不要让生产者向备份服务器发送新数据。更实际的情况是,生产者被重定向,并继续向备份生产新数据。当主实例从灾难中恢复且数据完好无损时,只有灾难期间生成的新数据需要通过 MM2 从备份镜像回主实例。

跨多个数据中心的主动-主动复制

在下面所示的主动-主动设计中,一个 MM2 实例将数据从起点 DC-1 拷贝到目标 DC-2,另一个 MM2 实例将数据从起点 DC-2 拷贝到目标 DC-1。

MirrorMaker 2 实现跨数据中心的主动-主动复制。作者图片

“生产者 1”在其本地 DC-1 中写入“主题 1”,而“生产者 2”在其本地 DC-2 中写入“主题 2”。

“消费者 1”可以从 DC-1 中的“生产者 1”产生的“主题 1”中读取数据,也可以从最初由 DC-2 中的“生产者 2”产生然后复制到 DC-1 的“主题 2 镜像”中读取数据。反之亦然。

在灾难导致 DC-1 失败的情况下,DC-2 中的“生产者 2”和“消费者 2”可以继续运行。如果 DC-1 的停机时间很短,并且“主题 1”生成的数据不太重要,则不一定需要主动将“生产者 1”故障转移到 DC-2,因为 DC-2 仍在运行。当 DC-1 恢复时,MM2 的两个实例将赶上并继续跨数据中心复制数据。

从应用的角度来看,主动-主动部署提高了可用性性能,因为“消费者 1”和“消费者 2”几乎实时接收相同的数据(可能顺序不同)。一个数据中心完全故障不会影响另一个数据中心的数据消耗。

摘要

在接下来的几篇博客中,我们将介绍几个后续话题,包括:

  • 如何在 5 分钟内为概念验证设置 MM2
  • 跨数据中心的一次性消息传递保证
  • 从现有镜像解决方案迁移到 MM2 的工具
  • 更多…

更多文章敬请关注!

关于编码面试问题——数组的几点注记

原文:https://towardsdatascience.com/a-few-notes-on-coding-interview-problems-array-63bb370c0241?source=collection_archive---------11-----------------------

我从每天的阅读中逐渐了解到。

照片由 Matteo Di Iorio 在 Unsplash 上拍摄

遵守他们的规则。

编码挑战是软件工程、数据工程甚至数据科学中技术面试的基础。我在市场上见过或经历过的几乎所有工作面试都要求你通过几项编码挑战,以证明你有足够的解决问题的技能。

当然,对于编码挑战是否是评估你专业技能的合适工具有很多争论。然而,这仍然是一个你需要熟悉和掌握的标准,以便在技术领域找到你梦想的工作。

我在编码面试准备过程中遇到的一个问题是,题目很大。你有机会在编码面试中面对任何情况,就看你的运气了。例如,主题可以是遍历复杂数据结构或动态编程的基本数组。

我发现很难掌握和理解这个问题,因为对于一些特定的问题,它需要你有计算机科学的先验知识,这样你才能以它应该的方式解决问题。

比如,你可能会想出一个幼稚的常识性解决问题。您彻底地编写并仔细地测试了代码,并通过了所有的测试用例。你终于觉得,至少我在没有太多提示帮助的情况下解决了问题。

但是在提交解决方案后,他们会向您抛出一个时间限制超过阈值的错误。之后一切都落地了,你需要开始思考更好的解决方案。

熟能生巧。

萨姆·穆卡达姆在 Unsplash 上拍摄的照片

尽管我上面提到了一个悲惨的例子,幸运的是,许多平台可以帮助你完成这个旅程。有几种类型的平台与此相关。

首先,用于公司招聘流程的平台。它们提供了一个工具来监控和评估基于特定编码问题的候选人。公司可以对候选人进行比较和排名,从成千上万的申请中选择几个有价值的候选人进行现场面试。因此,这是一个你需要提高流程的地方,这样你才有机会进入招聘流程的下一步。

其次,该平台为应聘者提供了一个练习编码面试技巧的社区或场所——如 Leetcode 、 HackerRank 等网站。所以你可以开始评估你的技能,从最简单的问题到我认为我无法在面试时限内解决的最难的问题。尽管如此,在实际面试之前知道并熟悉这个问题还是有好处的。

我已经做了什么。

照片由奥拉夫·阿伦斯·罗特内在 Unsplash 拍摄

最近一直在做一个日常 Leetcode。如果可能的话,我试着一天完成五道题(简单水平)。我的策略是从易到难解决问题,并根据接受率对问题序列进行排序。录取率越高,应该越容易。

我的 Leetcode 热图—从 6 月开始作者截图

我开始在数组问题上做 Leetcode,因为它在类别部分有最多的问题。此外,在我看来,与其他主题相比,数组问题不需要太多的先验知识。当你学习任何一门编程语言时,你总会把数组或列表数据结构作为一种原始数据结构来面对。

基于作者问题类别截图的问题数量

关于上图中的所有数组问题,在主类别中仍然有各种子类别。因此,要全面了解数组问题并不容易。你通过的问题越多,你获得的经验就越多。这很简单。

我们到了。

Todd Quackenbush 在 Unsplash 上拍摄的照片

听到看到很多评论说解决编码面试只靠你的常识。我解决不了问题怎么办?是不是说明我常识不够?

简单的回答是然而,我相信我们可以练习和发展我们的常识。

当你解决问题或学习别人的解决方法时。你增强了你的常识。

这就像你在工具箱里放了更多合适的工具,当你面对一个新问题时,你可以挑选或组合一个合适的工具(概念、技术、思想、方法)来解决当前的问题。

以我的拙见,我认为如果你以前从未有过这方面的经验,第一次想出一个创新的解决方案并不容易。不过,等你的工具箱里有了足够的工具之后,事情就好办了。

这就是为什么我写这篇文章来帮助你们节省时间来复习 Leetcode 中的问题和优雅的解决方案。**我试着收集我认为有助于解决数组问题的东西。**另外,我可以用这篇文章作为参考,在将来回顾这些技术。

**免责声明:**以下提示基于我的看法。所有代码片段都来自 Leetcode 问题的讨论环节。所以,未必是解决问题的最好办法。

遍历各列

有了数组操作的基本知识,我们就可以沿着输入数组一直走下去。然后,无论维度有多大,我们都可以基于索引访问值。

我的开始方法是通过固定列号遍历列,并遍历每一行以获得列中的目标元素。但是当我看到这个片段的时候,我被其中的zip(*a)部分惊呆了。

这是 python 中遍历列的一种简单而有效的方法。为了给你更多的解释,*a是将每一行提取到三个列表中。之后,将三个列表传递给zip函数,将每个索引中的元素映射在一起。

比如用*a,会得到三个单独的列表[1,2,3] [4,5,6] [7,8,9]。然后传递给zip 函数,它将相同索引中的元素映射在一起。这里从zip得到的第一个元素(0th index)将是(0th index)索引(1,4,7)中的所有元素。

想申请吗?— 矩阵中的幸运数字

旋转阵列

使用上面类似的概念,现在您可以用一行代码将数组旋转 90 度。让我在下面的代码片段中向您解释一下这是怎么回事。

面对这个问题时,我遍历了数组的边界,并根据数组的长度移动了元素。它在工作,但它不是那么容易被人读懂的代码。此外,与下面的代码片段相比,它包含许多行。

你可以看到这里的主要概念是zip(*a[::-1])组件。除了这里有一个[::-1]部分之外,它与前面的代码几乎相同。[::-1]部分通常表示 python 中的反向数组操作。

给定a = [1,2,3],可以用a[::-1]反推,结果[3,2,1]

合并两个操作

  1. 从下到上反转 2D 阵列。
  2. 交换 2D 数组的行和列。

仅此而已。假设没那么直观。让我一步一步来阐述。

  1. 首先,你反转 2D 阵列。所以,你得到了[[7,8,9], [4,5,6], [1,2,3]]
  2. 现在,你可以看到第一列的顺序是我们将矩阵旋转 90 度后所期望的。那么如何得到那个元素列表而不是当前的呢?—遍历列。
  3. 使用zip(*a)将数组分成[7,8,9] [4,5,6] [1,2,3],然后将每个索引映射在一起。所以你得到(7,4,1) (8,5,2) (9,6,3)
  4. 维奥拉,你已经得到你想要的结果了。

这里有一个例子,说明你是如何结合之前学过的知识来解决更复杂的运算的。

想申请吗?— 确定是否可以通过旋转得到矩阵

利用位运算

对于这个问题,还是挺直白的。你可以把这个问题分成几个步骤。首先,你可以找到每个单词中每个字符的出现频率。然后,你只需要找到共同性格的交集。所以一步一步解决也没那么难。

然而,当我找到 Lee215 建议的解决方案时。这个解决方案如此优雅,以至于我自己都想不出来。他使用了&=位运算来更新常用字符的交集。好像他很了解Counter对象的深度细节。此外,这个方法对默认的dict对象不起作用。

此外,collections 库是 python 附带的标准库。所以我认为学习如何使用它是很好的,这样可以缩短你在编码面试环节的时间。

重要的是,你要知道Counter对象是怎么做出来的,以防面试官对规则非常严格,要求你解释幕后的步骤。

这是一个很棒的解决方案,扩展了我的知识边界。这是解决问题的一个新鲜和创新的方法。我很高兴能学到一些新东西。还有,如果我以前没见过,我也不会用这种方法解决问题。

现在,我有了另一种更新Counter交集的方法。此外,如果使用按位 OR 操作符,它可以是具有相同概念的Counter操作的并集。有机会将该技术应用于其他情况。

想申请吗?— 找共性。

按多个关键字排序

排序是一个基本的操作,你可以在 python 中用sortedlist.sort()来完成。区别在于一个是就地操作,而另一个不是。

然而,我对它还不够了解,有时我需要艰难地进行分类。例如,我可以循环并比较每个元素是否满足我的条件,并将正确的元素放在正确的位置。

在这里,我学到了一些新的东西,我可以用多个键对值进行排序。该键可以是值本身,也可以是基于其他对象的值。

在下面的代码中,我先用数字的频率(freq[x])对数组进行排序,然后如果有相等的一对数字。我用一个-x语句再次按降序排序。

它很简短,当你回来回顾代码时,它非常直观。

想申请吗?— 相对排序数组,按递增频率排序数组

最后的想法

本杰明·戴维斯在 Unsplash 上拍摄的照片

解决一个问题有很多方法。这取决于你对你正在处理的工具或问题了解多少。

为了开发一个更好更快的解决方案,你必须掌握你所使用的编程语言以及数据结构和算法概念。如果你只知道想法,但不能将其转录成代码,那么它在编码面试中是没有用的。

此外,假设你既有如何解决问题的想法,又有将其转化为代码的能力,但如果你在时间限制和压力下做不到,这也是毫无意义的。

要通过所有这些障碍,你需要有足够的经验来同时意识到和回忆解决问题的正确方法。在这里除了一遍又一遍的练习,什么也帮不了你。

你可能会在编码面试的第 10 次或第 100 次被拒绝。请不要难过,早点放弃。坚持下去,总有一天你会得到你梦想的。

帕泰鲁什·西达

如果你喜欢这篇文章,并希望看到更多这样的东西。

  • 跟着我上媒体
  • 其他渠道? LinkedIn , Twitter ,以及脸书

从大三学生那里获得数据科学工作的一些技巧

原文:https://towardsdatascience.com/a-few-tips-to-get-a-job-in-data-science-from-a-junior-bb3009335f47?source=collection_archive---------17-----------------------

一位前求职者给现在求职者的建议

找一份数据科学方面的工作有时会是一个真正的挑战。由于最近的危机,各地的工作机会都减少了。与此同时,数据科学领域正在吸引越来越多的人,如大学毕业生和正在转行的人。

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

竞争可能真的很难,我在过去的一年里经历了这一点。在网上和课堂上做了几个兼职课程(SQL,数据科学家的云计算……),完成了几个项目之后,就开始找工作了。在我找工作期间,我结束了对我的工作申请的跟踪,我意识到,从 2020 年 10 月到 2020 年 12 月,我总共申请了 142 份工作。通过这些,我收到了八个招聘人员的回电,还收到了两份工作邀请。请注意,我只是在申请加拿大的工作,但主要是在大多伦多地区。

从我的个人经历中,我清楚地发现,最大的一步是从招聘人员那里得到回电话是你的简历,这就是你的简历必须完美的原因。

由于我现在在一个技术岗位上工作,我有机会看到招聘过程的另一面,我可以分享一些技巧,让你的简历对招聘人员和招聘经理来说非常棒。我从我参加的聚会、与我交谈过的人以及我自己作为候选人的经历中收集了这些建议。请注意,这是我个人的观点,有些读者可能不同意我的观点。这完全没关系,我同意你的不同意见。开始吧!

1-链接,链接,链接!

如果你申请数据科学方面的工作,比如数据分析师、数据科学家、数据工程师,招聘人员可能会喜欢看到你做过的项目,尤其是当你没有该领域的专业经验时。因此,至少添加一个到你的 GitHub、你的媒体文章和/或你的个人网站的链接是很重要的。就我而言,我在 GitHub 上创建了一个简单的网站,免费收集我的所有信息,我甚至添加了一个链接,指向我的兼职课程证书(托管在 Google Drive 上)和一个链接,指向我工作过的公司的网站。这样做的目的是让招聘人员或招聘经理的日子好过一些。在一些公司,如果你的简历上没有 GitHub 链接,你就不会被考虑。

塔曼娜·茹米在 Unsplash 拍摄的照片

如果你已经发表了科学论文,我也鼓励你把这些链接。

如果你是一名自由职业者,例如,加入你的 Upwork 个人资料的链接,那么招聘人员可以查看你的工作和客户的反馈。

2-避开学校的项目数据集,做一些性感的事情

这是一个我见过几次的提示,使用泰坦尼克号数据集和猴子花数据做项目是没问题的,但你可能想展示更多,因为这些数据集已经被反复研究过了。如果你避免使用 Kaggle 上流行的数据集作为你的主要项目,那是最好的。

我认为你可以使用的最好的数据集是你自己的数据集,比如你的 WhatsApp 数据用于 NLP 项目。这是我在 WhatsApp 上做的项目:

或者,你可以在你在 Spotify 上听的音乐 Strava 上分析你的体育活动。你也可以从你感兴趣的网站上删除一些数据。我们在任何地方都创建了如此多的数据!

而且,使用别人的脚本来准备或清理你的数据也是完全可以的。你不需要创造所有的东西,但是,在这种情况下,请注明你获得作品的作者。

在我的兼职课程中,讲师为我们的项目使用了一个 API,并试图找到一个以前从未做过的项目,以表明我们能够做得比平均水平更多。我最终用 Twitter API 做了一个分析法国政治运动的项目。见下面我写的文章。

随着云计算的兴起,我鼓励你在云上做一个项目(GCP、Azure 或 AWS ),因为许多公司都在转向云计算,而且由于真正缺乏了解云计算的人,这值得一试,对吗?你可以选择 GCP、AWS 或 Azure。

2 —只有一页?

在我找工作的过程中,我有机会和许多来自加拿大和其他地方的科技招聘人员交谈,他们中的大多数人告诉我,有一份两页的简历是可以的,但超过两页就不好了,可能会成为他们马上解雇你的理由。因此,你不需要把所有的东西都挤在一页纸里,我建议你做一份两页纸的简历。

3-将你的技能与你的项目或工作经验相匹配

很有可能在你的简历中,有一部分是你正在使用和知道的所有技术技能,比如 Python、SQL、Tableau、Spark。拥有这些技能当然很好,但是,招聘人员会查看你在项目或以前的工作中应用这些技能的地方,我的建议是确保所有技能都出现在项目或工作描述中,避免列出所有流行的技术来欺骗申请人跟踪系统。

尽量不要列出超过七八项技能,以免让读者不知所措,也不要列出与工作无关的技能。我建议只列出你真正擅长的技能。我读到一个建议,如果你在过去 5 年中没有使用过某项技术,就没有必要把它写进你的简历。

4-使用模板或创建一个模板。

如果你用的是微软的 Word,你可能会接触到简历模板,你也可以在网上找到资源。有些真的很棒,而且几乎一直在用,这让你的简历更有美感,对招聘人员来说更易读。您也可以选择一个模板,然后进行个性化设置。

5 —避免软技能,但要证明它们!

我们都有软技能,它们对工作非常重要。这里列出了最基本的技能:沟通、团队合作、解决问题、时间管理、批判性思维和决策。

我建议不要把软技能写进简历,而是要证明它们。例如,如果你擅长交流,你可以把你写的一篇文章或任何其他证明的链接放到网上。你也可以在“工作经历”部分提供相关证据。

6-你不需要 100%符合职位描述

当我第一次开始找工作时,我意识到我没有很好地匹配许多职位描述;我害怕申请,我审查自己。在与几位业内人士交谈后,我意识到这些描述可能真的令人不知所措。描述有时可以由不在该领域的人来写。我最终读到的职位描述更像是公司的愿望清单,如果我符合大多数主要技能,那么我会继续前进并申请。

万一你最终被招聘人员选中,而你不知道公司正在寻找的一项主要技能,我建议你在面试前参加一个速成班。例如,我被一家大公司选为数据工程师,招聘人员告诉我要对 Apache Airflow 做好充分准备。在与经理面谈的前几天,我花了 15 美元在 Udemy 上上了一堂课,学习阿帕奇气流,我在几个小时内就学会了它的基础知识。然而,经理从来没有问我任何关于气流的问题,我的申请被拒绝了,因为我太年轻了,不适合这个职位。我的精力没有浪费,因为我现在对这项技术有了一些了解。

AHMED HINDAWI 在 Unsplash 上拍摄的照片

另一个例子,如果你知道你申请的公司正在使用 GCP,但你知道 AWS,我会建议你做和我一样的步骤。参加一个快速入门课程,了解一些关于 GCP 的基本知识。然后你甚至可以对招聘经理说,你不太了解 GCP,但你几天前刚上了一门课。这将向招聘经理表明你是一个积极主动的候选人,并且你愿意付出额外的努力。

有了这几条建议,你也许能在其他候选人中找到一个更好的位置,但是招聘过程并不总是一个理性的过程,这也需要一点运气。

一旦你被招聘人员选中,我还有一些关于如何处理招聘过程的建议,我会在以后的文章中分享。

关于表征学习的几点看法

原文:https://towardsdatascience.com/a-few-words-on-representation-learning-151d37cf292c?source=collection_archive---------25-----------------------

我们如何在不依赖人类注释数据的情况下学习好的表示?

图片由皮克斯拜的艾哈迈德·加德拍摄

在过去的二十年里,人工智能(AI)领域在研究和工业应用方面取得了长足的发展。逻辑回归和朴素贝叶斯等机器学习算法可以从一组特征中识别模式,并解决那些通过将知识硬编码到专家系统中似乎不可能解决的问题。这些浅层学习算法可以执行相对复杂的任务,如产品推荐或学习区分垃圾邮件和非垃圾邮件。更有趣的是,

这些浅层机器学习算法的性能很大程度上取决于它们作为输入接收的表示。

例如,如果我们决定使用朴素贝叶斯构建一个垃圾邮件检测器,将大量原始的非结构化电子邮件数据传递给分类器将不会有所帮助。

相反,在将文本输入分类器之前,我们需要找到一种不同的方法来表示文本。值得注意的是,这种应用程序的一种常用文本表示是单词袋模型。这个想法是为了表现文本,以便每个单词的重要性很容易被抓住。也就是说,每个单词的词频(图 1)代表一个单词在文本中出现的次数,是一种流行的文本表示,可用于训练垃圾邮件过滤模型。

图 1:从一组文档中,构建一个包含唯一单词集的字典,然后将每个文档表示为一个包含该文档中每个单词的计数(次数)的特征向量——作者图片

通过查看图 2,我们可以看到良好表示对于训练机器学习模型的力量。在本例中,我们可能希望使用逻辑回归等机器学习模型来寻找蓝色和绿色圆圈之间的线性分离,即 2D 线。然而,显而易见的是,学习线性边界的模型在这样的示例中不会成功,因为在数据的当前状态下,无法使用线来区分这两个类。幸运的是,如果我们改变输入表示,而不是传递原始数据,我们传递值的平方,我们将看到数据情况将完全改变,两组之间的线性分离在特征空间中变得明显。的确,陈述很重要。

图 2:表示对于逻辑回归等浅层机器学习模型很重要。一个简单的变换,例如对原始特征的值求平方,可能就足以解决这个问题——图片作者。

然而,预先知道如何改变数据表示以使特征空间中的线性分离变得明显并不简单。不同的功能通常具有不同的属性,这些属性可能适合也可能不适合解决给定的任务。以词袋术语频率特征为例。这些特征集中于一个单词在文本中出现的次数,但是丢弃了诸如语法和词序之类的信息。对于其他自然语言问题,单词之间的语义关系是必要的,语法和单词在文本中出现的顺序对于解决特定任务可能是至关重要的。

这就是为什么手工设计特征的过程被认为是如此具有挑战性的问题。例如,想象我们想要建造一个汽车探测器。我们知道汽车有一些主要特征,使它们不同于其他物体。有人可能会说,这些组成部分就是轮子的存在。在这种情况下,要构建汽车分类器,我们需要有一种方法来表示各种类型汽车的车轮。这就是问题变得复杂的地方。

毕竟,我们如何才能创造出一种车轮检测器,它能通用于所有类型的现有车轮,并对光线、形状和尺寸扭曲的多种组合具有鲁棒性?

这就是深度神经网络发挥作用的地方。通过深度学习,我们不需要关心如何手动指定车轮检测器,以便它可以对所有类型的现有车轮都是鲁棒的。相反,通过以分层模式组成一系列线性和非线性变换,深度神经网络具有通过组合简单概念来学习合适表示的能力,从而导出复杂结构。简而言之,经典的监督计算机视觉分类器将原始数据作为输入,每一层都迭代地从前面的层中提炼特征。因此,第一层的神经元可以学习能够从高频输入中检测边缘和轮廓的程序,并且随着特征穿过层的层级,后续的神经元结合预先存在的知识来学习能够检测对象部分的更复杂的程序。最后,我们可以使用这些细化的表示,并学习一个线性分类器来将输入映射到一组有限的类。

*图 3:深度神经网络通过组合简单的概念来学习表示,从而在分层管道中导出复杂的结构。每一层迭代地提炼来自前几层的信息。最后,分类器采用转换后的表示,并在类别之间绘制线性边界。*图片来源:深度学习

从这个角度来看,深度神经网络是表征学习模型。

在高层次上,典型的监督神经网络具有两个组件,(1)编码器和(2)线性分类器。编码器转换输入数据并将其投影到不同的子空间。然后,低级表示(来自编码器)被传递到线性分类器,该线性分类器绘制分隔类的线性边界。

然而,表征学习的目的是将表征映射到其他表征,而不是通过将表征映射到目标来解决任务(经典分类器会这样做)。这些学习到的表示通常是密集、紧凑的,并且可以推广到类似的数据形态。换句话说,这些表示可以很好地移植到其他任务中,并且已经成为解决那些很难甚至不可能获得数据注释的问题的主要方法。

故事到此为止

近年来,监督学习已经成为训练深度神经网络的领先和成功的方法。大型(最重要的是,带注释的数据集,如 ImageNet)的可用性允许研究人员训练深度神经网络模型,并学习概念层次方面的表示。这些表示不仅解决了手头的任务,在这种情况下的图像分类,而且由于它们的一般化属性,它们还可以用作学习不同的下游任务的良好起点,例如对象检测、分割、姿态估计等等。

诀窍很简单,采用大型带注释的视觉语料库,训练卷积神经网络,然后转移其知识来解决第二个任务,该任务通常没有足够的标签来从头训练深度网络。这个被称为迁移学习的概念已经被成功地用于许多不同的领域来解决众多的任务,并且在商业应用中得到了广泛的应用。

图 4:到目前为止,这个故事涉及到注释数据、计算能力和非常深度的神经网络。有了这种组合,几乎任何模式匹配问题都可以解决。换句话说,我们不受算法的限制。我们受限于我们能收集多少精选数据——作者图片

扩展这个配方的主要限制因素在于获取带注释的数据。作为一个概念,ImageNet 数据集是计算机视觉研究人员的标准数据集,包含 1400 万张图像和大约 2200 万个概念。然而,如果我们将 ImageNet 与所有可用的互联网图像集进行比较,ImageNet 下降了大约五个数量级。

图 5:经典视力问题。从左上到右下,随着预测级别从分类到实例分割(按作者分类的图像)的增加,注释级别变得更精细。

此外,如果我们看看计算机视觉数据集的前景,可用的带注释样本的数量会根据手头的任务急剧减少。假设我们只考虑最流行的计算机视觉任务,如对象分类、检测和分割(图 5)。在这种情况下,随着预测级别变得更加复杂(从整个图像标签到像素级注释),带标签的示例数量会大幅减少(图 6)。这就是为什么大多数对象检测和分割解决方案总是将 ImageNet 预训练网络作为优化的起点。

*图 6:收集和注释数据的代价反映在可用数据集的大小上。随着注释级别的增加,从图像级别的标签到像素级别的注释,可用的精选数据集的大小急剧减小。*图片来源:弱监督下的视觉学习。

**然而,对于领域发生重大变化的问题,ImageNet 预训练模型可能不是理想的解决方案。**这可能是医疗保健中许多应用的情况,例如全切片图像中乳腺癌转移的分类和检测。在这样的应用中,如果与原始的未注释数据组相比,精选的注释样本组是最小的。注释此类记录的过程通常需要专业的病理学家花费数小时,他们需要数年的培训才能完成此类工作——在这种情况下,注释大型数据集变得极其昂贵和耗时。

图 7:对于许多医疗保健应用来说,使用基于 ImageNet 的预训练模型可能不是答案——作者图片

此外,人类注释数据的速度无法与我们生成的数据量成比例。换句话说,如果我们事先不知道要优化的任务,那么准备深度学习训练所需的大规模监督数据集可能会非常复杂和昂贵。

所有这些例子都强调了从无注释数据中学习概括表示的重要性。许多研究领域,包括半监督弱监督以及最近的自监督学习,试图学习可以转移到新任务的表示,只使用少量或根本不使用带注释的例子。

半监督学习的目的是将一些带注释的观察结果与一个更大的无注释样本数据集结合起来。另一方面,弱监督学习探索了大量有噪声的,并且大多数时候是不精确的标签作为监督信号。最后,自我监督学习就是在没有明确监督信号的情况下学习世界的良好模型通过优化这些借口任务,网络学习有用的表示,可以容易地转移到以数据有效的方式学习下游任务。

深度无监督表示学习

到目前为止,我们一直在讨论深度神经网络如何通过解决监督任务来学习可概括的表示。一般来说,我们可以利用神经网络从任务 A(用大量带注释的数据训练)获得的知识,并应用该知识来学习单独的任务 B(通常缺少注释) 这里的问题是,我们仍然依赖于拥有大量带注释的数据来首先学习表示法。此外,根据任务的性质,注释数据可能会非常困难,甚至是不可能的。

但是,如果我们可以通过无监督(无标签)的方式学习任务来学习同样好(甚至更好)的表征,会怎么样呢?

深度无监督表示学习寻求从无标签数据中学习一组丰富的有用特征。我们的希望是,这些表示将提高许多下游任务的性能,并减少每次我们试图学习新任务时人工注释的必要性。具体而言,在计算机视觉(CV)中,无监督表示学习是最重要的、长期存在的一类问题之一,对世界各地的许多研究人员构成了重大挑战。随着计算能力越来越强,数据继续通过多个传感器无处不在地被捕获,创建能够以无标签方式从原始数据中提取更丰富模式的算法的需求从未如此迫切。

近年来,由于自然语言处理领域的最新突破,深度无监督表示学习受到了广泛关注。伯特和 GPT 都是基于深度学习的模型,在训练管道中不需要手动注释,他们的成功激发了新一代算法在计算机视觉应用中的发展。更具体地说,最近开发的自我监督学习领域,利用了数据中编码的监督,是 CV 和 NLP 应用的无监督表示学习最新成功背后的主要驱动力。

让我们以变压器或 BERT 的双向编码器表示为例。 **BERT 是一种深度无监督语言表示模型,它从非结构化文本中学习上下文表示。**上下文无关模型,如 word2vec 和 GloVe 学习单词嵌入,而不考虑单词出现的上下文。这是一个限制,因为许多单词根据它们使用的上下文表达不同的意思。例如,像“银行”这样的词可能出现在与金融相关的上下文中,如“银行帐户”,或者它可能用于描述河流的边缘。不同的是,伯特基于单词出现的上下文来学习表征。因此,BERT 能够学习更丰富的语义表示,根据上下文捕捉单词的不同含义。

此外,BERT 还通过解决一种特殊的自我监督任务进行了优化,这种任务不需要人工注释数据。也就是说,在训练期间,在通过变换器编码器之前,从输入句子中屏蔽掉一定百分比的随机选择的标记。编码器将输入句子映射到一系列嵌入向量(句子中的每个单词一个向量)。这些向量随后通过一个 softmax 层,该层计算整个词汇的概率,以便最有可能的单词有更高的机会被选中。换句话说,这是一个填补空白的任务,其中 BERT 旨在从部分可用的上下文中重建被破坏的输入信号。

图 8:对于语言模型,解决方案空间是离散的,屏蔽输入并从上下文中重建输入的任务是 BERT 成功的关键原因之一。图片由作者受 基于能源的模型讲座启发。

这个特殊的任务被称为屏蔽自动编码器, ,它已经被证明在我们有离散概率空间的情况下工作良好。

然而,诸如 BERT 之类的系统的最重要的特征是,在训练之后,我们可以在没有太多可用训练数据的不同下游任务上微调 BERT 参数,并且与从头开始训练系统相比,我们将实现显著的精度改进。这尤其重要,因为数据注释是深度神经网络训练中的一个重要瓶颈。

聚类和自我监督学习

多年来,聚类技术一直是从无注释数据中学习结构的主要方法。经典方法,例如 K-Means ,优化距离度量作为代理,以围绕公共质心对类似概念(例如图像)进行分组。最近的方法,例如由 Caron 等人提出的用于视觉特征的无监督学习的深度聚类(Deep Clustering)和由 Li 等人提出的无监督表示的原型对比学习(Prototypical Contrastive Learning)已经尝试将聚类与深度神经网络相结合,作为以无监督方式从非结构化数据中学习良好表示的方式。这些结果表明,结合聚类方法与自我监督的借口任务是一个突出的技术。

另一方面,自监督学习是一种无监督学习的方法,它涉及从未标记数据中学习语义上有意义的特征。自我监督学习的第一种方法是设计一个预测任务,只需探索数据中存在的特征就可以解决这个问题。这个子任务或借口任务充当代理,使网络学习有用的表示,这些表示可用于简化学习不同下游任务的过程。

虽然经典的无监督方法在训练期间不使用任何标签,但大多数提出的借口任务使用与经典的有监督学习算法相同的框架和损失函数。因此,自我监督的借口任务也需要标签进行优化。然而,用于优化这些托词任务的标签(或伪标签)仅来自数据或其属性。

*图 9:旋转预测是预测任务的一个例子,它探索数据的特征以设计监控信号。*作者图片,灵感来自通过预测图像旋转的无监督表示学习。

一般来说,自我监督的借口任务包括取出数据的一些部分,并挑战网络来预测丢失的部分。它可以根据前面的上下文预测句子中的下一个单词,或者根据前面的上下文预测视频的下一帧。事实上,这种托词任务在 NLP 应用程序中取得了巨大的成功,主要是因为文本数据是离散的。因此,天真地将这些相同的概念应用于 CV 应用是困难的,因为图像是连续的,使得可能的解决方案的空间更加复杂。

图 10:虽然图像修复方法产生了相当一致的结果,但这些模型学习的表示仍然不足以与以监督方式学习的表示(作者的图像)相媲美。

此外,创建自我监督的借口任务可能类似于为分类器手工设计特征的过程。不清楚哪个借口任务有效,为什么有效。然而,最近的自我监督学习方法偏离了一种通用方法,该方法涉及一种与基于对比的损失函数相结合的实例辨别借口任务。

总的想法是通过逼近相似的概念同时推开不同的概念来学习表征。在撰写本文时,对比学习方法在无监督表示学习中保持着最先进的性能。事实上,监督和非监督预训练之间的差距从来没有那么小,对于一些下游任务,使用自我监督技术预训练编码器已经比监督训练好。

在下一篇文章中,我们将探索自我监督学习方法的前景。我们将深入探讨一些最成功的实现,并讨论该领域的现状。

感谢阅读!

[1] Devlin,Jacob 等,“Bert:用于语言理解的深度双向转换器的预训练” arXiv 预印本 arXiv:1810.04805 (2018)。

[2]潘宁顿、杰弗里、理查德·索赫尔和克里斯托弗·曼宁。"手套:单词表示的全局向量."2014 年自然语言处理经验方法会议论文集。2014.

[3] Mikolov,Tomas 等,〈单词和短语的分布式表示及其组合性〉。 arXiv 预印本 arXiv:1310.4546 (2013)。

[4]卡隆,玛蒂尔德等,“用于视觉特征的无监督学习的深度聚类。”欧洲计算机视觉会议(ECCV)会议录。2018.

[5]李,,等.“无监督表征的原型对比学习”arXiv 预印本:2005.04966 (2020)。

原载于 2021 年 4 月 15 日https://sthalles . github . io

最流行参数的现场指南

原文:https://towardsdatascience.com/a-field-guide-to-the-most-popular-parameters-d734596c3f26?source=collection_archive---------4-----------------------

花点时间探索一些基本原理

本文将带您浏览一下统计数据中最受欢迎的参数!如果你不确定什么是统计的参数或者你对概率分布的工作原理不清楚,我建议在继续之前,先浏览一下我在第一部分的初学者友好介绍。

如果您是这个领域的新手,请在第 1 部分中了解您的分销基础知识。图片由作者提供。

注: 如果一个概念对你来说是新的,就按照我的解释链接。如果早期的东西感觉太技术化,请随意跳到更低级的可爱的小动物迷因。

准备好最喜欢的列表了吗?让我们开始吧!

平均

这个词读作平均。

预期值

一个期望值,记为 E(X)E(X=x) ,是随机变量 X 的理论概率加权均值*。***

通过加权(乘以)每个潜在值 x 可以发现 X 可以以其相应的概率P(X = X)然后将它们组合起来(对于像身高这样的连续变量使用积分,或者对于像身高四舍五入这样的离散变量使用求和): E(X

图片:来源。

如果我们正在处理一个公平的六面骰子, X 可以以 1/6 的相等概率取{1,2,3,4,5,6}中的每个值,所以:

e(X)=(1)(1/6)+(2)(1/6)+(3)(1/6)+(4)(1/6)+(5)(1/6)+(6)(1/6)= 3.5

换句话说,3.5 是 X 的概率加权平均值,没人关心 3.5 甚至不是掷骰子的允许结果。

差异

出于我将在时刻解释的原因,在上面的 E( X )公式中,用(X-E(X))替换 X 会给你一些非常有用的东西。但首先,让我授权你在冲动袭来时计算它:

v(X)= E[(X—E(X))=∑【*X—*E(X)】P(X = X)= E[(X)]—【E(X)】

最后一点是一个方便的重新表述,我将把它的证明留给感兴趣的读者作为家庭作业。(如果看起来遗漏了一些步骤,不要感到惊讶。他们是。)嗯,不喜欢那个链接里的证明?试试这个。

*让我们用一个旋转的公式来得到一个公平骰子的方差:V(X)=∑[X-E(X)]P(X = X)=*∑(X-3.5)P(X = X)=(1–3.5)(1/6)+(2–3.5)(1/6)+

瞬间

哈,“在一个时刻*”——这是上一节中的一个小小的双关语(几乎肯定除了我自己没人觉得好笑)。咳咳。*

矩是一种特殊的期望值。他们有一个模式:

第一原始力矩:E[(X)] ……第一中心力矩:E[(X — 𝜇)]
第二原始力矩:E[(X)] …。第二中心矩:E[(X — 𝜇) ]
第三原始矩:E[(X) ] …..……第三中心矩:E[(X — 𝜇) ]
………………..诸如此类……

矩是值得知道的,因为它们告诉你分布的形状。在某种程度上。缩放第三中心矩给你分布的偏斜度*,而缩放第四中心矩给你分布的峰度(“尾部”)。*

高阶矩

至于更高的时刻…嗯,我提出第五时刻的唯一原因是它是我的朋友们在研究生院组建的统计翻唱乐队的名字。(是的,我们是书呆子。我知道。)

第五时刻表演一首关于线性回归的歌。

尾部

等等,“尾随”?!是的,这是一个在统计中频繁出现的词。我记得一个朋友在打开他的第一本统计学教科书后给我发了一封电子邮件:“反面?WTF,这些是数字还是这些是狐猴?”

图片:来源。

我代表统计部门,深表歉意。给它们命名为“尾巴”的人可能当时正在吸食某种,呃,药用的东西,并且认为这种分布形状像一个有尾巴的动物。或者两条尾巴。不,这里没有任何动物可疑之处…

峭度

峰度是描述尾部丰满度的一种方式。你可能不会经常提到峰度,所以要克制住记忆任何相关信息的冲动。相反,当你需要的时候就去查(就像我刚刚做的那样)。

在这里,我给你做了一个峰度(尾数)指南。好吧,这个可能是个更好的向导。

歪斜

当谈到左偏、右偏、正偏、负偏的分布时,很难记住哪个是哪个…直到你意识到答案在尾巴指向的方向。所以,让我们用下面的恐龙来试试:

无论“尾巴”指向哪里,那就是你的答案。图片:来源。

这将是一个左偏(或负偏)分布,因为这是尾巴指向的地方。如果你足够努力地斜视你的大脑,每当你看分布的时候,你也会开始看到尾部。

这到底是什么?!一个高斯混合?当很难决定尾巴指向哪一边时,就没有太多偏斜。图片:来源。

矩母函数

如果你没有足够的时间呢?那么力矩生成函数( MGFs )就是为你准备的。mgf 很酷的一点是它们唯一地决定了分布(所以你可以用它们来代替CDF 和 pdf)它们给你一个快速的方法来计算所有你想要的时刻。

或者你可能不需要它们,因为你主要是在处理最受欢迎的时刻:

第一原始矩= 𝜇 = E[(X)]
第二中心矩= 𝜎 = E[(X — 𝜇) ]

眼熟吗?没错。又见面了,均值和方差!

方差,再次

方差告诉你一个随机变量与其平均值相差多少。有多“全面”呢?

想想一个平均数本身能告诉你多少。例如,如果你看了我的平均睡眠时间,你会猜我是一个睡眠冠军…直到你看到差异(哦,天哪)。一个晚上的 4 小时和另一个晚上的 12 小时并不等同于 8 和 8,尽管它们的意思是一样的。

方差越低,越容易做出预测。

谈到概率,方差是一个非常重要的概念,因为方差越低,就越容易做出预测。当没有方差时,你就有了确定的答案。

唉,方差不是传达这种信息的最合适的方式。这就是为什么在礼貌的公司里传播你的结果之前先求平方根是礼貌的。那个平方根有一个特别的名字…

标准偏差

标准差是方差的平方根:它是𝜎而不是𝜎,所以它测量的是同样的东西。用标准差而不是方差来报告结果更友好,因为我们的伙伴的标准差是一个有意义的距离度量尺度。

把那些自称为“偏差”的东西想象成测量就餐者和长长的霍格沃茨风格宴会桌中心线之间的距离。图片:来源。

总的来说,你可以把那些自称为“偏差”的东西,看作是对食客和长长的霍格沃茨风格宴会桌中心线之间距离的概括。

在学校里,你会学到标准差告诉你平均值的平均分布……某种程度上。

实际上,最真实的衡量标准是所谓的平均绝对偏差。

疯的

平均绝对偏差是当我说“你的值围绕平均值的平均分布”时,你直觉上想象的偏差**

不幸的是,它的公式可能是一个绝对的害虫——绝对值函数(消除负号的函数)有一个尖角,使得一些优化技术变得疯狂,所以我们通常更喜欢用标准差来代替。够近了。

这一节有展开绰绰有余,所以让自己居中*。*

中位数

Median 读作“中间的东西。”中值通常是你在说“平均收入”时想要考虑的数量如果一群人有以下以千美元计的工资:{1,1,1,1,1,1,1,1,1,1,1,1,10000000000 },那么平均工资(1,000 美元)本身比平均工资(7,692,307,693,000 美元)更能概括这个群体的情况(一半在 1,000 美元以上,一半在 1,000 美元以下)。对于那些过分关注的人,你会注意到这种形状的分布被描述为单尾和右偏。

这是给你们三个分享我对电影的品味的人。

方式

模式读作“最常见的值。”对应的模式是分布/直方图出现峰值的地方。当你听到一个分布是 多峰 时,这意味着有不止一个峰值。当一个分布是 对称单峰 时,就像漂亮的小钟形曲线一样,众数也恰好是均值。如果你想在技术上正确,你就不要再说“普通人”了,实际上你指的是“模态人”**

为什么是均值和方差?

如果人们更直观地思考中位数和 mad,为什么学生要学习均值和方差呢?简而言之,均值和方差函数更便于执行各种数学技巧。

中心极限定理(CLT)说,如果你正在处理大量数据,你可以安全地假设样本平均值的分布是正态的(钟形),就像这个 plushy。在以后的文章中,我会告诉你所有关于 CLT 和便捷计算的可怕代价,但我现在就停在这里,让你消化你如此耐心吸收的大量信息。图片:来源。

你会惊奇地发现,“标准”的做事方式往往是回答简单的问题,而不是正确的 T21 问题。不要认为你在课堂上学到的技术是你应该用于重要工作的。你能培养的最好的统计技能是独立思考的能力。

你会惊奇地发现,做事情的“标准”方式常常是回答“简单”的问题,而不是“正确”的问题。

感谢阅读!人工智能课程怎么样?

如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:

在这里欣赏整个课程播放列表:bit.ly/machinefriend

与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格联系。

第一次品尝法典

原文:https://towardsdatascience.com/a-first-taste-of-codex-71385ee8aaa4?source=collection_archive---------22-----------------------

作者图片

试用 OpenAI 的自动代码生成系统

2020 年,我技术生涯中的一个亮点是进入 GPT 3 号。多亏了这个视频中提供的建议,我得以访问 GPT 3 号,并发布了一系列视频,描述了我用 GPT 3 号进行的实验,以生成来自英国的 h 的 git 命令,创建一个电影琐事聊天机器人,并导航伦敦地铁和纽约地铁。

我对 GPT-3 可以解决的各种问题印象深刻,所以当我听说 Codex 时,我急于尝试一下。Codex 专注于 GPT-3 的功能之一,从英语语言描述中生成代码。Codex 生成各种语言的代码,包括 Python 和 JavaScript。我很少使用 JavaScript,所以一旦我接触到 Codex,我认为用一些简单的 JavaScript 用例来练习它会很好。我对 JavaScript 非常熟悉,可以用 Codex 生成的代码解决简单的问题,但同时我也对 JavaScript 非常熟悉,如果 Codex 能为我生成一些代码,我会从中受益。

在这篇文章中,我将描述我从最初的 Codex 实验中学到的东西。

目标

对于这个最初的实验,我希望 Codex 生成一个简单的网页,允许我选择美国的州,当一个州被选中时,显示该州的地图。

我应该注意到,我最初试图为加拿大各省而不是美国各州做这个实验。我遇到了一些问题,我怀疑是因为加拿大各省的培训材料没有美国各州的丰富。一旦我将主题领域从加拿大各省转移到美国各州,我就能够为我的用例获得不错的结果。

实验

开始使用 Codex 真的很容易。我从 OpenAI 得到的访问邮件把我带到了测试环境,并把我指向了我想要的东西,即 JavaScript 沙箱。在沙盒中,我输入以下提示来生成代码:

  • 美国的一项控制规定:
create a selection list of the states of the united states
  • 该提示生成了以下 JavaScript 代码:

  • 选择列表中第一个州的初始地图图像。我了解到,我需要用一个图像的 URL 作为 Codex 的种子,该图像具有规则的结构,当初始的州名被其他州名替换时,该结构将产生有效的图像 URL。我还了解到,通过显式命名控件(例如“map_image”),我可以在以后的步骤中具体引用它:

display an image of a map of Alabama sized 40% from https://www.nationsonline.org/maps/USA/Alabama_map.jpg. call the image "map_image"
  • 该提示生成了以下 JavaScript:

  • 用当前所选州的地图图像替换初始地图图像。我了解到,为了为具有多单词名称的州(例如新泽西州、南达科他州)的图像生成有效的 URL,我需要明确要求 Codex 用下划线替换州名中的空格:

when a state is selected from the selection list, replace map_image with the image of the map of that state. Replace spaces with underscores in the state names
  • 该提示生成了以下 JavaScript:

这些是我的简单应用程序和它们生成的代码所需的提示。

实验结果

经过一段时间的反复试验,上面的提示生成了代码,这些代码生成了我在 Codex JavaScript 沙箱中想要的内容:

在 Codex JavaScript 沙箱中运行的应用程序

一旦我在 JavaScript 沙箱中测试了应用程序,我就点击 Export to JSFiddle 按钮进入 Fiddle :

Codex 在 Fiddle 中生成的代码

在 Fiddle 中,Codex 在 JavaScript Playground 中生成的 JavaScript 被包装在 HTML 中。我可以保存这个 HTML 来获得应用程序的自包含版本。

结论

通过三个提示和一点点尝试和错误,我能够从 Codex 获得一个简单的、自包含的应用程序。作为一个偶尔使用 JavaScript 的用户,我非常欣赏 Codex 对语法细节的关注。当我得到意想不到的结果时(例如,显示了多个地图,而不仅仅是当前所选状态的地图),很容易调试问题,更新生成的代码,并修复问题。

  • 你可以在这里看到实验结果。
  • 你可以在这里看到一个相关的法典实验的视频:https://youtu.be/d9sbsSA8xS4

处理工作场所持续学习的四步流程

原文:https://towardsdatascience.com/a-four-step-process-to-handle-continual-learning-in-the-workplace-3ae3a539bd43?source=collection_archive---------30-----------------------

加入一个关于处理压力的 Twitter 空间的 3 个关键经验

照片来自 Unsplash 上的汉斯-彼得·高斯特

有些日子太安静了,对自己没好处,所以我反而一边工作一边去找点东西听。在推特上,一个空间正在举行“处理社交媒体上的压力”,主要发言人是圣地亚哥、丹尼·汤普森、萨曼莎·波兹曼和弗朗切斯科·丘拉。不过,这个演讲并不仅仅关注社交媒体,有时还会进入一些令人兴奋的 STEM 讨论。我在工作的时候把它放在后台,偶尔记下一些我觉得有见地的东西。因此,我想和你们分享我从这次演讲中获得的一些见解。

持续学习

在数据科学或任何 STEM 领域,你会不断地学习。无论你是第一次学习一种新的算法,学习一种新的编程语言,还是致力于软技能,总有一些需要改进的地方。我经常发现自己羡慕那些职业生涯较晚的人,想知道他们是如何做到这一点的。

我从这次演讲中得到的第一个评论是:

不要总是试图向专家学习。相反,向比你水平高一点的人学习。理解他们为了达到这个目标做了什么。

这个建议对于 STEM 领域的人来说是极好的。是的,我们总是可以从专家那里学到一些东西,但是从每个人身上也有一些东西可以学到。走向数据科学,网上还有各种背景的人提供的大量其他资源。这些人可以教给我们一些他们学到的新东西,并且发现这些东西很有帮助。

在最近换了工作之后,我发现自己比以往更多地查找关于 SQL 的文档和教程。我已经深入研究了 100 多行复杂的 SQL 语句,但这并不容易。在这段时间里,我一直在寻找比我更了解 SQL 的人,他们可以教我我需要知道的东西,并帮助填补空白。我不需要专家;我需要前面那个最近学过的人。我指望这个人解释他们的成功和失败,并告诉我什么对他们有效。

冒名顶替综合症

我从这次演讲中记下的另一个笔记是关于冒名顶替综合症的。我专门从这个话题写下了一些笔记,因为这击中了要害,我觉得我们许多人都在对自己的能力有同样的不安全感。最近,我写了一篇文章,讨论我在换工作后与骗子综合症的斗争。

根据阿林·库契奇的说法,“冒名顶替综合征指的是一种内心体验,即认为自己不如别人认为的那么有能力。”这和我换工作后的感觉一样,因为我担心自己做顾问是个错误。当我听到这个 Twitter 空间时,这个话题被提起,我记下了以下内容:

不要把注意力放在消极的方面,用你的怀疑来推动你前进。提高的最快方法之一是给自己挑战去学习一些东西或者变得更好。用冒名顶替综合症来学习,支点,成长。

我喜欢你转移注意力的想法。当一个消极的想法突然出现在你的脑海中,用一些积极的东西来回击。你能从那个想法中得到什么让它变得有意义?当我刚开始工作时,我脑海中闪现的一个想法是,“这里的 SQL 比我预期的多得多;我不擅长 SQL。”我没有把注意力放在负面评论上,而是换了个角度。

  • 我确实了解 SQL 的基础知识,可以填补空白。
  • 对于我来说是新的 SQL 函数,我可以利用这段时间来学习它们。
  • 我可以利用这段时间更好地连接表,并根据我的分析过滤数据。

焦虑和冒名顶替综合症可能具有挑战性,但让我们将这些想法转向积极的方面,以帮助我们学习和成长。

想法和研究

我从这个演讲中记下的最后一个笔记是关于想法和研究的。今天,我们周围有如此多的媒体:社交媒体、文章、书籍、播客、视频等等!如此多的信息,你每天都可以在指尖消费。

我经常发现自己在工作中使用的媒体分为两类:(1)领导力/职业发展和(2)技能发展。让我们把重点放在技能发展上,特别是 SQL,因为我们之前已经讨论过了。由于我一直在提高我的 SQL 技能,我偶然发现了 StackOverflow 上的许多帖子。这些对于回答一些问题很有帮助,并且通常对我正在做的事情提供了一个简要的解释。不幸的是,它们并不总是足够的。

当你阅读和倾听不同的人时,允许他们激发一个想法。不要把它当成 100%的事实。相反,做你的研究并向他们学习。

当我阅读这些帖子时,我让它们给我带来了灵感。我现在有了新的起点。使用这个,我查找 SQL 文档中讨论的命令,并尝试分解纸上发生的事情。这就是我如何学会并开始理解它们背后的力量。

  1. 把消极的想法变成积极的。这里的 SQL 比我预想的多了很多,而我并不擅长 SQL有很多 SQL 函数对我来说是新的,我可以利用这段时间学习那些
  2. 学习— 找一个与你正在努力解决的小问题相似的例子。我用 StackOverflow 找到了一个使用WINDOW functions的样本。
  3. Pivot — 拿这个例子,多研究,多借鉴。我采用了用于WINDOW function的 SQL 文档,并在我的例子中对其进行了分解,在纸上完成了整个过程以理解方法。
  4. Grow — 从这个基本例子和我的研究中,我学会了如何使用WINDOW functions并将它们应用于多个查询,以生成我需要的分析。

对我来说,这是一个很好的过程,可以用来在工作中不断学习,同时也可以与可能潜入我日常生活的消极思想和焦虑做斗争。

最后的想法

总而言之,我很高兴在过去的一周里,我参加了关于“在社交媒体上处理压力”的 Twitter 空间。发言者从他们自己的经历中得到了一些很好的教训。我明白这些教训如何能转化到我的工作中,我希望它们也能教会你一些东西。以下是在工作场所处理持续学习的四个步骤:

  1. 用你的疑惑推动你前进。将你的消极想法转换成积极的想法。
  2. 向比你高一级的人学习。
  3. 当你阅读时,不要把事情当成 100%的事实。利用这些教导来进行更多的研究。
  4. 在发现下一个挑战或疑问之前,通过应用这些新发现的知识来成长。

那么,在过去的一周里,你学到了什么?他们如何改变了你看待自己工作的方式?

如果你想阅读更多,看看我下面的其他文章吧!

库存优化

原文:https://towardsdatascience.com/a-framework-for-inventory-optimization-71d4496aec75?source=collection_archive---------8-----------------------

你需要多少库存?一切都与供给、成本和需求有关。

正如我的书库存优化:模型&模拟中所解释的那样,不管是好是坏,库存在供应链中无处不在。关于需要多少库存以及哪里需要库存的核心问题经常是同事之间无休止的争论,尤其是当政治游戏(而不是定量/定性分析)驱动决策的时候。

盘点做对了

储备产品有助于全球各地的公司按时向客户供货,并为任何不可预见的事件提供缓冲。此外,由于持有库存使生产过程与销售过程分离,它允许计划者生产更长的生产批次,降低生产成本。

换句话说,正确的库存优化可以保护您免受供需变化的影响,降低总体成本,并优化服务水平。

库存做错了

然而,持有存货有两个主要缺点。第一个当然是它的成本:库存只不过是随着时间推移而贬值的沉睡的现金。储存产品是要花钱的。此外,库存过多会带来风险。你真的能卖出所有这些产品吗?他们不会让久而久之过时吗?

简而言之,你持有的越多,成本就越高,风险就越大。

保持更少的库存可能会部分防止呆滞/过时库存的风险。尽管如此,它也无助于为您的客户提供足够的服务水平。实际上,在某些情况下,库存管理是如此的有缺陷,以至于供应链面临着低服务水平和呆滞/过时的库存。

如图“库存权衡”所示,我们看到保持适当数量的库存就是在各种风险和成本之间进行适当的权衡。

库存权衡

库存优化框架

库存优化是优化库存策略以实现目标的科学。

如下图库存优化框架所示,库存策略只是实现特定目标的工具。我们还需要考虑供应链环境(供应、成本和需求),同时受到物流限制(仓库大小、卡车数量等)的约束。

设定正确的目标将允许我们在过多和过少的库存之间做出正确的权衡。

库存优化框架

让我们逐一讨论这些要素(目标、库存政策、供应链环境)。

️🎯目标

在进入库存策略之前,我们应该首先定义我们追求的业务目标。换句话说,首先,我们需要看到我们希望供应链实现的目标。然后**、**进入库存优化的细节。

对服务水平的(无用)追求

大多数从业者都在寻找特定的、任意的服务水平目标。

“我们需要达到 95%的服务水平”

他们希望有足够的库存来实现特定的服务级别目标。我想到了两个问题:

  • ❔ **你怎么知道 95%是不是最佳值?**为什么不是 98%或者 92%?的每一件产品都应该达到 95%的服务水平吗?还是只是一个平均值?
  • ❔ **你如何定义服务水平?**量化服务水平的方法有很多。在实践中,一些服务级别定义会导致高分(填充率)或低分(周期服务级别)。[1]

更好——更清晰!—库存政策的目标应该是最小化成本或最大化利润。

服务水平应该只是库存优化的副产品,而不是其主要目标。

成本最小=利润最大化

大多数模型——像著名的 EOQ 库存模型——讨论成本最小化** ,一些讨论利润最大化*。

两者本质上是一样的,条件是所有相关成本都包含在模型中。您需要在成本模型中包含由延期交货/销售损失引起的机会成本;因此,最小化成本将导致与利润最大化练习相同的优化。

只有当所有相关成本(包括机会成本)都包含在我们的模型中时,为成本最小化(而不是为目标服务水平)优化库存策略才会起作用。

*注意,大多数库存模型都假设销售永远不会损失,但总是延期交货。在实践中,这意味着如果你有一些超额需求(需求>供应),你将面临一些延期交货成本,而不是未实现利润的机会成本(可能远大于延期交货成本)。

💰利润、损失和投资回报率

实际上,一个企业应该追求投资回报(ROI)最大化:你想最大化你的利润,同时减少你的投资。

换句话说,企业希望提高资本效率。

然而,优化单一产品的投资回报率不会在公司层面产生最佳的投资回报率。这是因为投资回报应该在全球范围内进行衡量和优化(由于所有的全球资本投资),而不能按产品进行局部优化。

总之,每个企业都希望优化其投资回报率。然而,创建一个包括所有资本投资的全球模型是试图煮沸海洋。相反,我们希望优化每个产品的利润,这与最小化每个产品的成本是一样的——条件是我们包括所有的机会成本。

库存优化的目标:利润最大化!

⚙️️库存政策

库存政策将通过回答三个简单明了的问题来控制您的库存水平:

  • 📦订购多少
  • 📆什么时候来点菜
  • 🏭在哪里点菜

第三个问题(在哪里订购)通常是隐含的——但不是无关紧要的!如果每个仓库/梯队都知道何时订购以及订购多少,库存本地化问题就隐式解决了。多级供应链中的库存定位问题称为多级库存优化* (MEIO)。如果你对 MEIO 感兴趣,我在我的书中描述了一个框架,可以轻松地解决分销供应链的 MEIO。[1]*

库存策略:多少何时何处订购

这里有一些库存政策的例子(你应该很容易确定订购多少何时订购):

  • 我们每周五晚上向供应商下订单,这样他们可以在周一早上准备好订单,并在周二发货。我们总是订购足够的数量,有 3 周的库存。
  • 我们每个月的第三个工作日向中国的供应商下订单。我们补充库存,有 1000 件存货。
  • 我每周六早上去超市。我买了足够多的牛奶,总共有 6 升。
  • 当库存水平达到 3 件时,我订购 10 件。
  • 一旦我的冰箱里剩下两瓶牛奶,我会去超市买六瓶。
  • 当我的打印机显示我只剩下 10%的墨水时,我会订购一套新的墨盒。

供应链环境

我们现在有了目标(利润最大化)和实现目标的工具(库存策略)。我们需要讨论供应链环境*,这将决定什么是最佳库存政策。三个要素主要决定了这种环境:*

  • 🏭供应
  • 🛍️需求
  • 💰费用

供求

对于每种产品,我们应该分析:

  • *🏭**供给。*预期供应提前期及其可变性(你的供应商/生产可靠吗?).
  • *🛍️ **需求。*我们需要了解两个方面:
    📊期望的需求分布:你期望有多少需求(平均而言),以及有多少可变性。
    📅你的客户愿意等你的产品多久。这是库存政策的一个驱动力:要求现成产品的客户将不会得到与接受等待数周的客户同样的服务。

供应链环境:供给和需求

如果你只是想达到特定的服务水平目标,评估需求和供应的可变性就足够了。但是我们追求利润最大化。这将要求我们审视供应链产生的成本。

费用

如图“库存成本”所示,我们看到 5 种主要的成本类型:💳采购,🚚交易,📦持有,🗑️到期,和💶短缺成本。

库存成本

让我们逐一分析这些成本。

1.采购成本💳

这基本上是销售的 (COGS)商品的成本:采购价格或生产成本。**

一些因素会影响采购成本:

  • 如果产品是从供应商那里购买的,购买成本有时会因为批量折扣而降低。
  • 一些产品的采购成本会随着时间的推移而降低。高科技元件往往就是这样。

2.交易成本🚚

交易成本是向供应商(内部或外部)下订单(或交易)引发的所有成本。

让我们将这些成本分为供应商和客户两方面。

  • 供应商:运输、包装、提货、转换时间……
  • 客户:订单检验&接待,仓库催货&员工,下单所需时间(库存分析、购买流程、谈判……),…

提示:仓库里的大部分员工工作都是因为交易(成本)。

3.持有成本📦

与储存(或拥有)产品相关的成本。

可变部分(与产品更相关)

  • 产品资本成本
  • 过时(想想高科技产品)
  • 产品保险
  • 损坏、丢失、盗窃
  • 库存控制(检验)

固定部分(更多与仓库相关)

  • 仓库的资本成本
  • 仓库保险
  • 存储设备/基础设施
  • 软件/信息技术(WMS)
  • 安全性
  • 照明、供暖

提示:你可以很容易地把(大部分)持有成本想象成晚上仓库睡觉时发生的所有成本。所有这些产品放在仓库里一晚上要多少钱?(请记住,仓库中的大多数员工工作是因为交易(流入/流出),而不是因为库存。)

4.到期成本🗑️

由于保质期/失效日期而丢弃产品的所有相关成本。

***到期还是持有成本?*过期成本在某种程度上类似于持有成本,因为它们随着现有库存量的增加而增加。尽管如此,由于他们的行为是不同的——持有成本比到期成本更具线性——我喜欢将这些成本分开。

5.短缺成本💶

**短缺成本是指需求超过供给(出现库存短缺)时发生的所有成本。不知何故,短缺成本是收入损失和客户惩罚的混合。

了解你的客户

在查看短缺成本估算之前,我们必须分析在短缺情况下客户的典型行为。

他们会…(从最好到最差)

  • …等着补货?
  • …转而购买另一个类似的产品?
  • …放弃购买?
  • …去比赛?

理解他们的行为将有助于我们估计错过销售的(机会)成本。

当你缺货时(版权所有:尼古拉斯·万德普特)

**参见我的上一篇文章,“捕捉供应链中不受约束的需求”,了解更多关于捕捉实际需求而不仅仅是销售的信息。

*

根据客户的行为,缺货可能会导致延期交货或销售损失。但情况可能更糟:品牌声誉受损、更大订单的损失(如果订单需要某个特定的零件),信息丢失,甚至停产。让我们逐一回顾所有这些短缺效应。

  • **利润率/利润。**如果你失去了一笔销售,你就错过了实现一些利润的机会。产品利润应该是任何短缺成本估算的基线吗?如前所述,这完全取决于你的客户的购买习惯。当面临短缺时,如果他们喜欢购买你的另一种产品,你仍然能够实现利润。所以你没有损失任何实际的钱(但仍然让你的客户感到沮丧)。
  • 商誉/声誉。遭遇库存短缺将导致销售损失或延期交货。两者都会让你的客户失望。这表现为声誉(或商誉)的损失——即使你的客户决定购买你的其他产品。
    **我们如何估算这种名誉损失的价值?**但这是你战略的核心。如果你的供应链战略是为你的客户提供高质量的服务,你可能想要给这种声誉损失一个慷慨的价值(换句话说,你重视你的客户的时间和偏好)。另一方面,如果你遵循低成本战略,你可能希望绕过品牌声誉的损失(换句话说,你将优化供应链的成本,并假设你的客户会喜欢低价、精益的供应链并有耐心)。
  • 比赛。如果你正在为一种特定的产品进行直接竞争,你可能想要增加你的短缺成本,以反映这样一个事实,即每当你的一个客户面临你的产品短缺时,你的竞争对手就会赚钱。
  • **合同罚款。**一些 B2B 合同包括服务水平协议(SLA)。在这种情况下,任何糟糕的服务都会导致供应商受到处罚。这些应包括在短缺成本中。
  • 信息/数据丢失。大多数公司没有捕捉到真正的需求,而只是跟踪实现的销售(记住,我们预测的是需求,而不是销售)。每当你面临库存短缺时,你的销售额就会持平于 0。如果你用实际销售额作为需求的代表,那么你的需求估计也是如此。这种糟糕的需求估计反过来会影响你的下一次预测,导致更大的预测误差,导致更糟糕的库存分配,导致更多的短缺…如果你正在努力正确捕捉需求,你可以在短缺成本中包括“数据惩罚”。
  • 生产停止。在制造环境中,缺少一个子组件将会中断整个生产过程。因此,丢失一个组件的成本与不运行整个生产流程的机会成本成正比。
  • 紧急/探险。面临延期交货可能会(从合同上)迫使你加快向客户发货。进行这种紧急考察的成本(使用更快的运输路线,特殊的特别过程)应该加入到短缺成本中。

结论

让我们回顾一下我们的库存优化框架。我们有,

  • 1 个目标:📈收益
  • 2 个问题:📦订购多少钱📆何时订购
  • 3 个库存驱动因素:🏭供应,💰成本和🛍️需求
  • 5 成本:💳采购,🚚交易,📦持有,🗑️到期,和💶不足

库存优化框架*

👉我们在 LinkedIn 上连线吧!

感谢

我要感谢 Stefan de Kok 的评论、见解和支持。

来源

[1]范德普特,尼古拉(2020)。库存优化:模型和模拟,柏林,波士顿:德格鲁埃特出版社,2020 年。https://doi.org/10.1515/9783110673944

[2]范德普特,尼古拉(2021)。四维预测框架。走向数据科学。https://towards data science . com/the-4-dimensions-forecasting-framework-f 7884 EC 1472

*

如何引用这篇文章vande put,Nicolas (2021)。库存优化框架。走向数据科学。

关于作者

icolas Vandeput 是一名供应链数据科学家,擅长需求预测和库存优化。他在 2016 年创立了自己的咨询公司 SupChains ,并在 2018 年共同创立了 SKU 科学——一个快速、简单、实惠的需求预测平台。尼古拉斯对教育充满热情,他既是一个狂热的学习者,也喜欢在大学教学:自 2014 年以来,他一直在比利时布鲁塞尔为硕士学生教授预测和库存优化。自 2020 年以来,他还在法国巴黎的 CentraleSupelec 教授这两个科目。他于 2018 年出版了 供应链预测的数据科学(2021 年第 2 版)和 2020 年出版了 库存优化:模型与模拟

*

每个数据科学家都应该知道的免费且强大的标签工具

原文:https://towardsdatascience.com/a-free-and-powerful-labelling-tool-every-data-scientist-should-know-ce66473c7557?source=collection_archive---------7-----------------------

我用过的最好的标签工具之一

照片由来自 Pexels 的 Markus Spiske 拍摄

通知:您有新的工作请求!

作为一名数据科学家,您肯定需要训练模型来满足您组织的需求。大多数情况下,您需要来自公司内部的标记数据,以便构建定制的解决方案。

一天,一位产品经理找到您,希望您构建一个命名实体识别模型,以提高下游数据科学产品的质量。由于这个产品需要推出的时间很短,他雇佣了一群人类贴标机来协助你。你下一步应该做什么?

给我们工具,我们将完成这项工作

作者温斯顿·丘吉尔

你需要一个标签工具

任务目标明确,人力充足,有期限。下一步是什么?一个有用的标签工具。请注意强调“有用”。但是我说的“有用”是什么意思呢?该工具至少应该让您能够:

  1. 随时在内部或云中跟踪标签质量。在整个贴标项目完成之前,如果注释人员没有按照说明进行操作,可以通知他们。
  2. 跟踪每个注释者的进度。截止日期已经给了你,所以必须给每个注释者一个完成标注的具体截止日期。作为项目负责人,你应该能够跟踪进度,以确保每个人都在正轨上。
  3. 允许多个注释一起工作。偏见是机器学习中最有问题的话题之一。作为一名数据科学家,您不希望在花了几天或几周时间训练您的模型后,却发现它有偏差,注释偏差会影响您的模型。作为一种可能的解决方案,多个作者可以在同一任务/文章上合作,并且只批准被一致接受的标签。
  4. 用最少的努力后处理您的标签结果。你将会欣赏的一个特别的方面是你能容易地处理被标记的数据。根据我的经验,当我为客户标记项目时,JSON 格式是最容易使用的。如果该工具可以支持多种类型的格式来导入和导出数据,那就太好了。
  5. 为人工注释者提供一个用户友好的界面。为我开发一个好的数据标注工具主要是基于这个因素。我不仅负责准备客户文档,还必须训练人工注释如何使用标签工具,尽可能减少人为错误。因此,如果你使用易于使用的标签工具,你可以省去很多麻烦。
  6. 编码你的标签界面来满足你的需求。我确信,作为一名程序员,你希望你的工具由代码控制。通过编码来定制界面可以将时间用于其他重要的任务。

这些是我希望我的标签工具至少具备的特性。像这样的工具可能会花费你数千美元和无数小时的劳动来开发。免费找到这种现成工具的最好方法是什么?

标签工作室

在我开始之前,让我声明如下:我不为 Label Studio 工作,也不隶属于 Label Studio。这只是我为客户项目工作的个人经历。

https://github.com/heartexlabs/label-studio

当我在开发一个代表客户标记数据的系统时,我遇到了这个工具。鉴于它是一个开源和免费的数据标记工具,我对它的灵活性和功能性感到非常惊讶。通过几个简单的步骤,我将向你展示如何构建一个简单的 NER 标签接口。

案例研究:NER 标签制度

文章摘自https://www . Reuters . com/life style/science/sko rea-prepares-launch-first-国产-space-rocket-2021-10-20/ 。图片作者。

您可以按照 Github 资源库中的说明轻松设置 Label Studio。它支持本地机器上的安装和云中的部署。作为我为客户工作的一部分,我使用 Docker 构建并部署了这个工具。它允许他在本地存储数据,因为凭据是他的业务的主要要求。为了演示这个工具,我将使用 Heroku 按钮。您可以按照您认为适合您业务的任何方式部署它。

设置您的第一个项目

建立新项目。图片作者。

登录后,您将能够看到项目页面。创建项目的过程如上面的 gif 图所示。对于这个项目,我称之为 NER。

我们的下一步是上传我们自己的数据,为贴标签做准备。为了方便起见,我创建了一个脚本来帮助您开始。作为起点,我引用了 Reuters.com 的两篇文章。在 Colab 中运行上面的脚本,您应该能够查看 JSON 格式的任务列表,如下所示:

[
 {
    “data”: { 
        “text”: “TOKYO, Oct 20 (Reuters) - A volcano ...”
    }
 },
 {
    “data”: { 
        “text”: “HONG KONG, Oct 20 (Reuters) - Bitcoin ...”
    }
 }
]

可以通过上传 label_studio_input.json 文件导入数据。之后,您可以在最后一个选项卡中从大量模板中进行选择,以定制您的标签界面。默认的 NER 模板可以用来启动我们的第一个 NER 项目。以下是向您展示第一个项目页面的步骤。

上传数据并使用默认的 NER 模板创建您的第一个项目。图片作者。

开始贴标签

标签数据。作者视频。

Label Studio 是迄今为止我用过的最简单的工具。这个工具的用户界面让我很容易使用。正如你在上面的视频中看到的,你只需选择标签按钮,并突出显示与之相关的单词。如果你想做得更快,可以用热键激活标签按钮。通过按“3”键,可以激活“LOC”标签的标签按钮。完成后,您可以点击右侧面板上的“提交”来确认您的标签。只需编辑文本,然后根据需要进行更新。

你可以看到整个贴标过程是多么直观。如何将您的结果导出以供将来处理?

导出您的结果

导出数据超级简单。图片作者。

完成标注后,将其导出为不同的格式,以便对结果进行后期处理。在这个演示中,我将把它导出为 JSON 格式,并进一步把结果整理成 Panda 数据帧。

以下脚本向您展示了清理 label studio 结果的一个非常简单的步骤:

它只提供这些吗?

肯定不是!在本文的前面,我们讨论了好的数据标签工具的定义特征。让我们来看一下您可以为不同的业务场景定制的一些很酷的功能。

标签的附加任务和每件商品的更多标签

为您自己的数据集自定义标签配置。图片作者。

# Code for the default NER template<View>
    <Labels name=”label” toName=”text”>
        <Label value=”PER” background=”red”/>
        <Label value=”ORG” background=”darkorange”/>
        <Label value=”LOC” background=”orange”/>
        <Label value=”MISC” background=”green”/>
    </Labels>
    <Text name=”text” value=”$text”/>
</View>

在上面的案例研究中,我只向您展示了如何使用免费提供的模板启动 NER 项目。此外,Label Studio 为您提供了一个代码编辑器,您可以使用它来定制您的标签模板。代码采用 XML 格式,因此您可以使用标记向数据集添加自定义标签配置。以下是两个允许您进一步自定义数据集的示例。

#1 修改标签颜色并添加更多标签

标签的原始颜色。图片作者。

NER 的默认模板为 ORG 和 LOC 标签分配了非常相似的颜色。在标记后检查注释时,可能会造成混乱。通过将 LOC 标签的颜色改为蓝色,我们可以避免这种人为错误。为了使任务更完整,我们还将添加更多的标签。

# Modify the LOC background color to blue<View>
    <Labels name=”label” toName=”text”>
        <Label value=”PER” background=”red”/>
        <Label value=”ORG” background=”darkorange”/>
        <Label value=”LOC” background=”blue”/>
        <Label value=”MISC” background=”green”/>
        <Label value=”BRAND” background=”yellow”/>
        <Label value=”TIME” background=”purple”/>
    </Labels>
    <Text name=”text” value=”$text”/>
</View>

修改过的标签。图片作者。

#2 新标签任务

在建立项目时,我们只有选择一个模板的选项。即使在单个项目的情况下,也可能需要进行多任务推理。在我客户的一个项目中,我被要求设计一个多标签分类模型,该模型也执行 NER。向我们现有的任务中添加一个新的标签工作只是简单地在脚本中添加几行。

<View>
    <Labels name=”label” toName=”text”>
        <Label value=”PER” background=”red”/>
        <Label value=”ORG” background=”darkorange”/>
        <Label value=”LOC” background=”blue”/>
        <Label value=”MISC” background=”green”/>
        <Label value=”BRAND” background=”yellow”/>
        <Label value=”TIME” background=”purple”/>
    </Labels>
    <Text name=”text” value=”$text”/>
    <Taxonomy name=”article_class” toName=”text”>
        <Choice value=”world”>
            <Choice value=”africa”/>
            <Choice value=”america”/>
        </Choice>
        <Choice value=”business”>
            <Choice value=”environment”/>
            <Choice value=”finance”/>
        </Choice>
    </Taxonomy>
</View>

新增多标签分类任务。图片作者。

我们可以看到,添加一个名为 Taxonomy 的新标签会立即为现有任务生成一个多标签分类作业。

现在你知道 Label Studio 是多么容易使用,你现在可以在你的下一个标签项目中使用它。最后,我想指出一个更重要的特点。

协作是不可或缺的

毫无疑问,标记工作是劳动密集型的,这意味着我们需要将任务分散到多个标注者中,以便尽快完成。为了减少人为偏见,我们应该为每个标记任务分配多个人。好消息是 Label Studio 允许注释者注册,如果需要,他们可以处理相同的任务。

要像现有的注释者一样开始注释相同的文章,他们需要做的就是在他们的帐户下创建一个新的选项卡:

注释者可以处理相同的任务。图片作者。

外卖食品

本文的目标是向您展示如何构建一个标签工具来支持您的下一个标签项目。除了它的一系列免费特性,Label Studio 还允许你用几行代码定制你的标签界面。该平台不仅支持自然语言处理任务,还支持计算机视觉和语音识别任务。如果你打算长时间使用这个工具,那么团队版将是理想的选择。

Label Studio 确实做得很好,让这个标签工具尽可能地易于使用。无论你是一家初创公司还是一家小企业,当你开发一个新的机器学习项目时,标签有时会成为一个问题。设计新的贴标工具时,成本和时间会增加。在这方面,Label Studio 是一个可行的选择。

参考

  1. https://labelstud.io/

关于作者

Woen Yon 是新加坡的一名数据科学家。他的经验包括为几家跨国企业开发先进的人工智能产品。

Woen Yon 与少数聪明人合作,为当地和国际初创企业主提供网络解决方案,包括网络爬行服务和网站开发。他们非常清楚构建高质量软件的挑战。如果你需要帮助,请不要犹豫,给他发一封电子邮件到 wushulai@live.com。

他喜欢交朋友!在 LinkedIn 和 Medium 上随时与他联系

https://medium.com/@laiwoenyon

展示您的项目的免费数据科学组合模板:使用它来创建您自己的项目

原文:https://towardsdatascience.com/a-free-data-science-portfolio-template-to-showcase-your-projects-create-your-own-in-under-two-ce8560099400?source=collection_archive---------16-----------------------

本文将使用 Angular、D3.js 和 Google Firebase,带您一步步地创建自己的数据科学产品组合,并将其部署到互联网上

模板部分|作者图片

建立一个作品集来展示你的数据科学工作似乎是一项艰巨的任务。而且,在花了无数个小时微调你的模型之后,你如何最好地与世界分享你的结果呢?那么,在你跑去找网站建设者之前,为什么不试着自己建一个呢!它实际上不会花费你太多时间,这样做的一些主要优点是:

  • 在向世界展示你的项目时,你会有更多的自由。网站构建器很方便,但通常你会受到提供给你的布局类型的限制,你可能不得不重新构建你的代码来让事情正常运行。
  • 你会学到一项新技能!数据科学家需要很好地理解 web 框架是如何工作的。为什么不花几分钟浏览一些 HTML、CSS 和 JavaScript 代码呢——这将大有帮助!
  • 建立一个定制网站可以向未来的雇主展示你是这份工作的全面候选人。不可避免地,他们会对你花时间和精力部署你的项目和内容印象深刻。
  • 如果你的项目托管在互联网上,将会有更多的人可以访问。
  • 很好玩!建立和展示你的技能才是最重要的。

如果我已经说服了你,那么请继续阅读这个分步指南(和视频),从从 GitHub 下载初始模板一直到将其部署到互联网。

本演练附有一个全面的 YouTube 视频,你可以在这里找到:

从 GitHub 下载模板到使用 Google Firebase 将其部署到互联网的分步演练|作者视频

第一步:创建 GitHub 账户并安装 Git

为了下载资源库,您首先需要一个 GitHub 帐户和 Git。如果您已经具备了这两个条件,请随意跳到第 2 步。

  • 如果你没有 GitHub 账户,你可以在这里免费创建一个:https://github.com/
  • 接下来,你需要在你的机器上安装 Git:https://git-scm.com/downloads

步骤 2:克隆 GitHub 存储库并将文件下载到您的本地机器上

成功安装 Git 后,您现在可以访问包含 Angular 模板的存储库:https://github . com/zachalexander/data-science-portfolio-template

  • 要将它下载到您的本地机器上,您需要打开一个终端窗口(强烈推荐 VSCode )并运行:
git clone [https://github.com/zachalexander/data-science-portfolio-template.git](https://github.com/zachalexander/data-science-portfolio-template.git%60)

第三步:下载 npm(软件包管理器)

Angular 应用程序使用某些模块和包,与其他 web 框架类似,组织和安装这些包的最佳方式之一是通过名为 npm 的包管理器。如果您已经安装了 npm,请随意跳到步骤 4。

  • 如果你还没有安装 npm,你需要在这里安装:https://www.npmjs.com/get-npm

步骤 4:安装 Angular 命令行界面(CLI)

为了在本地运行我们的 Angular 应用程序,并最终编译我们的部署文件,我们需要安装 Angular CLI。您可以通过在终端中运行以下命令,使用 npm 下载该文件:

npm i @angular/cli

步骤 5:在本地机器上运行数据科学组合

现在到了激动人心的部分,让我们来看看模板!为此,您需要使用命令行 cd 命令导航到我们项目目录的根文件夹:

cd data-science-portfolio-template

进入模板目录的根文件夹后,您将需要运行以下命令:

npm install

这将需要几分钟的时间,但会安装我们的应用程序所需的所有软件包。完成后,您可以运行以下命令:

npm start

如果一切顺利,您应该看到您的终端输出如下所示:

Angular 代码的成功编译,可在本地查看,网址为http://localhost:4200|图片由作者提供

然后你可以打开一个网络浏览器(最好是谷歌 Chrome 或 Safari),导航到 http://localhost:4200 。在那里,您应该能够看到数据科学产品组合模板!这是主页的顶部:

模板主页顶部|作者图片

第六步:编辑模板以展示你的技能

在浏览器中打开模板后,您会注意到有很多地方需要您进行编辑。例如,您想要添加自己的头像(目前是库存照片),用自己的内容更新项目页面,并链接到您想要展示的任何特定内容。

在视频演练中,我将更详细地介绍一些您可以真正将它变成自己的地方!但是,这里有一个需要立即编辑的文件的快速列表:

首页组件 html

-data-science-portfolio-template
--src
---app
----components
-----home
**------home.component.html**

这将是您编辑大量主页 html 的地方。有些代码注释看起来像这样:

<!-- EDIT CONTENT BELOW -->

这些表示需要您进行编辑以使其更加个性化的地方。

家居组件风格

-data-science-portfolio-template
--src
---app
----components
-----home
**------home.component.scss**

这将包含对您想要添加/更改的任何照片的调整。有些代码注释看起来像这样:

// project photo 1 -- feel free to change

这将突出显示您可以更改照片映射的位置。作为参考,我用于造型的所有照片都可以在这里的资产文件夹中找到:

-data-science-portfolio-template
--src
**---assets**

所以,你可以把你想用的任何照片添加到资产文件夹中,然后把文件名改成你的照片,而不是 scss 文件中的sample.jpg

导航条组件 html

-data-science-portfolio-template
--src
---app
----components
-----navbar
**------navbar.component.html**

您需要在此处更改“您的姓名”文本以进行个性化设置,并更新按钮以链接到您的社交媒体页面。

页脚组件 html

-data-science-portfolio-template
--src
---app
----components
-----footer
**------footer.component.html**

您可能想要更改页脚中的任何内容,包括指向您的社交媒体页面的链接,以便为您自己的网站定制这些内容。

更改配色方案或字体

-data-science-portfolio-template
--src
**---styles.scss**

要更改字体,您可以使用 Google Fonts 并导入您的首选项,类似于此文件中的当前设置:

当前谷歌字体加载到模板|作者图片

您还必须浏览 home.component.scss 文件,以更新转换到特定元素上的任何字体样式。

通过用您喜欢使用的十六进制值调整代码,可以很容易地更改配色方案:

应用程序的当前调色板|作者图片

更新或删除简历页面

你可能想或不想利用简历页。要将其从应用程序中移除,您可以执行以下操作:

  1. 导航至 app-routing.module.ts:
-data-science-portfolio-template
--src
---app
**----app-routing.module.ts**

2.注释或删除简历页面的路径(以下注释为例):

按作者注释简历路径|图片

3.要么把这个按钮从 home.component.html 页面上去掉,要么把它链接到你简历的其他地方。

要更新简历页面,您需要导航至该页面:

-data-science-portfolio-template
--src
---app
----components
-----resume
**------resume.component.html**

并根据你的工作经验、教育程度、兴趣等编辑其中的任何部分。

更新 D3.js 图形上的注释

例如,初始模板有一个第 4 节注释,可能需要更改。为此,您需要导航至:

-data-science-portfolio-template
--src
---app
----components
-----simplelinechart
**------simplelinechart.component.ts**

并编辑第 106、123、139 和 155 行,其中包含以下代码:

note: {title: 'Section Four' <- change this to your preferred title}

向您的文件夹添加额外的页面

我不会在这里赘述太多细节,但我会参考这篇优秀的中型文章,它将带您了解如何在 Angular 中向您的应用程序添加额外的页面:https://zerosandones . medium . com/how-to-create-navigation-in-Angular-9-application-using-Angular-router-6ee 3 ff 182 EB 6

当您编辑时,请确保保存您的每个文件,以便您可以在本地开发时看到它们!当您准备好将其部署到互联网时,请继续下一步。

第 7 步:为生产构建项目

当您对所有本地编辑和更改感到满意时,您将需要打开一个单独的终端窗口(或者在当前终端窗口上关闭开发服务器)并运行:

ng build

这个命令将编译您的代码,压缩它,并使它准备好部署!如果你遇到困难,可以观看视频寻求帮助。

第八步:让我们在网上举办吧!

哇,恭喜你走到了最后一步!现在我们需要将它部署到互联网上,这样每个人都可以见证你的伟大。

此时,我强烈建议跟随视频(从 18:45 开始)。

为此,我们将使用 Google Firebase 。这是免费托管,所以你不必担心积累任何费用。为了开始,你需要再次使用 npm 来下载 firebase 工具。

在终端中,运行:

npm install -g firebase-tools

然后,你需要导航到:https://firebase.google.com/,并登录你的谷歌账户。如果您还没有创建,您必须创建一个。然后,您可以单击“转到控制台”开始。

然后,您将需要创建一个新项目,我将在视频中详细介绍这个项目。完成此操作后,您可以导航回您的终端窗口,确保您位于项目文件夹的根目录下(即 data-science-portfolio-template ),然后运行:

firebase login

您应该能够使用 firebase 帐户进行身份验证。之后,您可以将新项目链接到 firebase-tools CLI,并完成视频 **(从 20:20 开始)**中概述的步骤。

一旦您正确地链接了文件并为部署做好了一切准备,您只需运行:

firebase deploy

当这个命令完成时,您应该在输出中看到一个托管 URL,它包含到您完全部署的投资组合的链接!

正如视频中提到的,这个网址可以分享给朋友,未来的雇主等。你也可以通过几个简单的步骤通过 Firebase 为你的网站建立一个自定义域名,但是,你必须购买域名(这部分不是免费的!)。

如果你已经完成了这些步骤,现在你已经在互联网上拥有了自己的作品集,那么恭喜你!我希望这个模板能很好地为你服务,并能帮助展示你做的很酷的工作。

正如在视频演练的最后提到的,我不是 web 开发人员,并且已经通过自学了解了大部分内容。因此,对于那些更精通网络开发的人来说,我很乐意与他们合作,让这个项目变得更容易访问,更高效!

如果你喜欢这个内容,或者想看看我做的其他工作,请随时到我的网站 zach-alexander.comhttps://www.zach-alexander.com(你会注意到模板看起来非常类似于我自己的网站:),我还有几个页面作为例子。

如何使用此 web 应用程序创建 GitHub 个人资料自述文件

原文:https://towardsdatascience.com/a-free-tool-to-take-your-github-profile-to-the-next-level-dd877a304d74?source=collection_archive---------14-----------------------

一个简单的 web 应用程序和大约 15 分钟的时间可以让你的 GitHub 个人资料脱颖而出。本文还包含一系列资源和教程,可以让你的 GitHub About Me/Read Me 看起来更漂亮😍

克拉克·蒂布斯在 Unsplash 上拍摄的照片

你可能已经在 GitHub 上看到了用一个很酷的 ReadMe 特性解释“秘密”回购的文章,但是推迟了实际创建它。我制作了一个应用程序来帮你完成大部分工作😃

目录

  1. 创建关于我的“秘密”回购
  2. GitHub 关于我/个人资料自述文件生成器
  3. 表情符号和 GIFS
  4. 访客徽章
  5. Github 统计
  6. 最新的博客帖子
  7. 瓦卡提时间统计
  8. 社交图标
  9. 自述文件的灵感来源
  10. 资源
  11. 结论

创建“秘密”回购🔓

  • 转到https://github.com/new创建一个新的 GitHub 库
  • 将您的 GitHub 用户名设置为存储库名称。我的 GitHub 用户名是 rahulbanerjee26,因此我的 repo 名也应该是 rahulbanerjee26

新回购截图

我想这已经不是秘密了

  • 选中该框以添加自述文件
  • 在你做改变的时候,暂时保密。一旦完成,我们将把它公开

初始自述文件的屏幕截图

Github 关于我/个人资料自述文件生成器

回购:https://github.com/rahulbanerjee26/githubAboutMeGenerator

作者制作的工具截屏

我使用 streamlit 创建了一个简单的 web 应用程序,它可以接受输入并为您生成一个非常酷的自述文件。

https://share . streamlit . io/rahulbanerjee 26/githubaboutmegenerator/main/_ _ init _ _。py

你所需要做的就是输入你的名字,社交账号,技能,并回答关于你正在学习和工作的问题,它会为你生成自述文件。

特征

  • gif
  • 社交图标
  • 技能图标
  • 访客徽章
  • Github 统计
  • 瓦卡提时间统计
  • 。yml 文件来显示订阅源中的博客文章
  • 能够在自述文件的开头添加图像横幅
  • 用实时更新编辑降价的编辑器
  • 随机笑话卡

除了创建秘密回购和设置工作流之外,该工具基本上完成了本文中提到的所有工作。在不久的将来,我还计划在 READMEs 中加入更多的设计。

如果您喜欢该工具,请考虑“主演”回购:)

表情符号和 gif💯

下面是 Markdown 中表情符号的一般语法

:emojiName:

这类似于你在 Medium 的文本编辑器中使用表情符号。你可以在这里找到大量的表情符号。若要使用表情符号,请点按表情符号并使用它的一个短代码。

要使用 gif,请执行以下操作

  • 下载 gif
  • 上传到你的秘密回购
  • 点击上传的文件,并将 URL 复制到其中
  • 使用图像标签
<img src="pathTo.gif" width="30px">

你可以在这里找到一些很酷的 gifs】

# Hello World <img src = "[https://github.com/rahulbanerjee26/rahulbanerjee26/blob/main/hFZ.gif](https://github.com/rahulbanerjee26/rahulbanerjee26/blob/main/hFZ.gif)" width = 50px>
Hi! My name is Rahul. Thank You for taking the time to view my GitHub Profile :smile:

访客徽章📛

网址:【https://visitor-badge.glitch.me/

回购:https://github.com/jwenjian/visitor-badge

只需复制并粘贴以下命令

![visitors](https://visitor-badge.glitch.me/badge?page_id=page.id)

将末尾的 page.id 替换为唯一的名称,如 your_user_id.your_repo_name。在我的例子中,它看起来像这样

![visitors]([https://visitor-badge.glitch.me/badge?page_id=rahulbanerjee26.rahulbanerjee26](https://visitor-badge.glitch.me/badge?page_id=rahulbanerjee26.rahulbanerjee26))

每当您查看/编辑您的自述文件时,徽章计数都会增加查看次数,因此我建议您在公开您的报告之前添加一个数字或一些独特的东西,以将查看次数初始化为 0

GitHub 统计📊

回购:https://github.com/anuraghazra/github-readme-stats

作者截图

## My GitHub Stats 📊<a href="[https://github.com/anuraghazra/github-readme-stats](https://github.com/anuraghazra/github-readme-stats)">
  <img align="left" src="[https://github-readme-stats.vercel.app/api?username=**rahulbanerjee26**&count_private=true&show_icons=true&theme=radical](https://github-readme-stats.vercel.app/api?username=rahulbanerjee26&count_private=true&show_icons=true&theme=radical)" /></a>
<a href="[https://github.com/anuraghazra/convoychat](https://github.com/anuraghazra/convoychat)">
  <img align="center" src="[https://github-readme-stats.vercel.app/api/top-langs/?username=**rahulbanerjee26**](https://github-readme-stats.vercel.app/api/top-langs/?username=rahulbanerjee26)" />
</a>

替换' api 后的文本?usermame ' 与您的 GitHub 用户名。

最新的博客帖子📮

回购:https://github.com/gautamkrishnar/blog-post-workflow

  • 将以下内容添加到您的自述文件中
**# Blog posts**
<!-- BLOG-POST-LIST:START -->
<!-- BLOG-POST-LIST:END -->
  • 创建一个文件夹。 github ,在里面创建另一个名为 workflows 的文件夹,在里面创建一个名为**blog-post-workflow . yml .实质上创建。github/workflows/blog-post-workflow . yml .**如果您在 Github 中创建新文件时将其粘贴为文件,它会为您创建文件夹。
  • 在里面。yml 文件,粘贴以下内容
name: Latest blog post workflow
on:
  schedule: # Run workflow automatically
    - cron: '0 * * * *' # Runs every hour, on the hour
  workflow_dispatch: # Run workflow manually (without waiting for the cron to be called), through the Github Actions Workflow page directly
jobs:
  update-readme-with-blog:
    name: Update this repo's README with latest blog posts
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: gautamkrishnar/blog-post-workflow@master
        with:
          feed_list: "[https://www.realpythonproject.com/feed/](https://www.realpythonproject.com)"
  • 将**‘feed _ list’**的值替换为博客 feed 的 URL。您可以输入多个 URL,用逗号分隔。你的媒体提要是https://medium.com/feed/@用户名
  • 保存文件并单击操作

作者截图

  • 您应该会在左侧栏中看到您的新工作流。点击它,然后点击运行工作流。给它几秒钟
  • 一旦您看到工作流已成功执行的消息,请转到您的自述文件,您应该会看到您的博客帖子。如果你看不到它,就等一分钟。

瓦卡时间统计💻

回购:【https://github.com/anuraghazra/github-readme-stats】T2

作者截图

首先,你需要确保你的 Wakatime 数据是公开的

  • 在右上角点击您的个人资料图标,然后点击设置
  • 确保用户名旁边的文本框中有一个值
  • 选中以下复选框:'公开显示照片','公开显示代码时间','公开显示语言,编辑器,操作系统,类别'
  • 在**‘公开显示代码时间’旁边的下拉列表中,选择‘过去 7 天’**如果您有免费版本,下拉列表中的其他选项将不起作用
  • 点击保存

键入以下代码

## My Wakatime Stats ⌚
<a href="[https://github.com/anuraghazra/github-readme-stats](https://github.com/anuraghazra/github-readme-stats)">
  <img align="center" src="[**@rahulbanerjee26**](https://github-readme-stats.vercel.app/api/wakatime?username=<a href=)&compact=True">https://github-readme-stats.vercel.app/api/wakatime?username=[**@rahulbanerjee26**](http://twitter.com/rahulbanerjee26)&compact=True"/>
</a>

用您的 Wakatime 用户名替换 src 后的粗体文本。您可能需要在您的用户名前加一个“@”来使卡片正确显示。

社交图标📲

包含社交图标类似于处理图像或 gif。您可以下载图标并将其保存在 repo 中,或者提供图标的外部 URL。

<img align="left" width="22px" src="https://raw.githubusercontent.com/iconic/open-iconic/master/svg/globe.svg" / href= "https://github.com/"><img align="left" width="22px" src="https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/youtube.svg" / href="[https://youtube.com/](https://twitter.com/)"><img align="left" src="https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/twitter.svg" href="[https://twitter.com/](https://twitter.com/)"/>[<img align="left" src="https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/linkedin.svg" href= "[https://www.linkedin.com/in/](https://www.linkedin.com/in/)"/>

您可以访问免版税图标,无需在此注明

自述文件的灵感来源👏

查看我的自述

https://github.com/rahulbanerjee26

一些很酷的读物的合集

https://github.com/abhisheknaiidu/awesome-github-profile-readme

资源

  • 不需要任何属性的免版税图标
  • 免费图标,必填属性
  • GitHub 回购图标
  • Gifs
  • 表情符号

结论

我希望这篇文章对你有所帮助。一个好的关于我的页面绝对可以让你脱颖而出。在评论中分享您的个人资料:)

我最近用 WordPress 创建了一个博客,如果你能看看的话,我会很高兴的😃

在 LinkedIn 上与我联系

https://www.linkedin.com/in/rahulbanerjee2699/

原载于 2021 年 2 月 27 日【http://www.realpythonproject.com】

用 Python 编写递归函数的友好指南

原文:https://towardsdatascience.com/a-friendly-guide-for-writing-recursive-functions-with-python-52441bd7ff5f?source=collection_archive---------14-----------------------

了解如何使用递归来优雅地编写代码

凯文·杨在 Unsplash 上的照片

起初,如果解释得很糟糕,递归似乎是不可能学会的。我仍然记得第一次遇到它时,我觉得它很有挑战性。为了深入理解它,我需要做许多练习。

这很难,因为我的大脑本能地使用迭代来解决问题,比如 for 循环和 while。但是经过一些努力,我在做很多练习的同时,能够理解递归函数的基础。

在这篇文章中,我想向你介绍什么是递归,并用插图展示递归函数的例子,这肯定有助于以更有效的方式理解这个主题。

递归函数

从单词“recursive”中你可以直观地看出,当函数调用自身时,它就是递归的。因此,同一个函数被调用一次或多次。在编写任何递归函数之前,您需要考虑两种情况:

  1. 基础案例是解决问题时需要考虑的最简单的案例。这也导致了递归的结束。
  2. 递归案例包括解决问题的一般解法,使用递归函数。

例如,让我们尝试计算一个正数的阶乘:

在这种情况下,我们可以确定两种不同的情况:

基本情况:如果数字等于 0 或 1,函数返回 1

递归情况:我们考虑剩余的情况,其中数字大于 1。

比如我们算 3 的时候!,有一些步骤可以解决这个简单的问题:

  1. 呼叫fact(3)
  2. fact(3)返回3*fact(2),这里要调用的新函数是fact(2)
  3. fact(2)返回2*fact(1),这里要调用的新函数是fact(1)
  4. fact(1)返回1
  5. 从第 4 步回到第 1 步解决问题

作者插图。递归函数计算 3!= 6

其他示例

让我们试试其他三个例子,看看你是否真正理解了递归函数的工作原理。

  1. 写一个递归函数,当且仅当数字是偶数时返回 True

我们需要再次确定这两种情况:

基本情况:如果数字等于 0,则数字为偶数

递归情况:我们考虑所有情况,n=0 除外。例如,当 n=4 时。

作者插图。当 n=4 时。

2.写一个递归函数,对一个列表的元素求和,这个列表至少需要有一个元素

这两种不同的情况是:

基本情况:如果列表中只有一个元素,函数返回列表的第一个元素

递归情况:在剩下的情况下,我们使用递归函数。每次都删除列表中的第一个。

递归调用可以通过显示以下步骤来解释:

sum_list([1,2,3,4])        #1st call with [1,2,3,4] 
1 + sum_list([2,3,4])      #2nd call with [2,3,4]
1 + 2 + sum_list([3,4])    #3rd call with [3,4]
1 + 2 + 3 + sum_list([4])  #4th call with [4]1 + 2 + 3 + 4              #return from 4th call with 
                           sum_list([4])=4
1 + 2 + 7                  #return from 3rd call 
1 + 9                      #return from 2nd call 
10                         #return from 1st call

3.给定一个列表(至少有一个元素),写一个递归函数,只返回偶数元素

这一次,我们可以发现:

基础案例:如果只有一个元素,如果元素是偶数我们返回列表,否则返回[]。

递归情况:在剩下的情况下,应用递归功能。和以前一样,我们每次在递归函数中从列表中删除一个元素。

同样,我们可以看到递归调用被分成不同的步骤:

even_elements([0,1,2,3])       #1st call with [0,1,2,3]
[0]+even_elements([1,2,3])     #2nd call with [1,2,3] 
[0]+[]+even_elements([2,3])    #3rd call with [2,3]
[0]+[]+[2]+even_elements([3])  #4th call with [3][0]+[]+[2]+[]                  #return from 4th call with 
                               even_elements([3])=[]
[0]+[]+[2]                     #return from 3rd call
[0]+[2]                        #return from 2nd call
[0,2]                          #return from 1st call 

最终想法:

递归函数的概述终于结束了。现在,您应该对什么是递归以及如何编写递归函数有了更好的了解。它可以提供一些优势。首先,递归将一个复杂的任务分解成简单的子任务。此外,它适合处理 Python 数据结构,如堆栈、队列和链表。但是你也应该知道,这种类型的解决方案并不适用于所有问题。每个递归调用都等待下一个递归调用,然后,有时迭代会花费更多的时间。你应该比较迭代的时间和递归函数的时间来确定。无论如何,递归函数在数据科学领域有众所周知的应用,例如递归神经网络、决策树、随机树和隔离树,其中递归是使它们工作的必要条件。我希望这篇教程对你有用。感谢阅读。祝您愉快!

你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都可以收到电子邮件!

用人工神经网络友好地介绍计算机视觉

原文:https://towardsdatascience.com/a-friendly-introduction-to-computer-vision-with-artificial-neural-networks-d2a38acc047c?source=collection_archive---------37-----------------------

Keras 让计算机视觉变得简单

基于 Keras 和人工神经网络的手写数字识别

梁杰森在 Unsplash 上的照片

介绍

如果你正在寻求在高层次上理解人工神经网络,并看到它们在计算机视觉中的实现,这篇文章就是为你准备的。

计算机视觉:首先,我认为通过解释一下什么是计算机视觉来介绍这篇文章是合适的。

计算机视觉可以被广泛认为是计算机解释数字图像或视频的能力。

你可能已经在社交媒体上看到了人脸识别软件和自动照片标签的应用。它通常与其他机器学习任务结合使用。例如,它与文本到语音机器学习一起使用,为盲人提供音频描述,帮助他们理解视频内容。

挑战

我希望带你通过一个入门级的计算机视觉挑战。使用神经网络,我们将建立一个程序来解释手写数字。

这项挑战是一项公开的卡格尔竞赛。因此,如果你是计算机视觉的新手,我鼓励你参加比赛,建立你自己的网络并提交你的分数。对于任何类型的机器学习,阅读加上实践从长远来看都会导致精通。

先决条件

我假设读者已经对机器学习这个概念有所了解。你应该了解机器学习过程是如何工作的,训练和测试一个模型意味着什么,以及它们通常是如何评估的。如果你对机器学习一点都不熟悉,请阅读这篇文章作为开始。

数据

我们收到了两个来自 MNIST(现代国家标准与技术研究所)的 CSV 文件,其中包含数字格式的手写数字。数字(从 0 到 9)由大小为 784 的一维数组表示。这实际上是一个 28 乘 28 像素的展平图像。

阵列中的每个位置代表一个像素,为该像素分配 0 到 255 范围内的整数来表示该像素的亮度。

正如 Kaggle 机器学习任务的常见情况一样,其中一个文件是我们的训练数据,另一个是我们的测试数据。训练数据被标记,而测试数据没有被标记。按照机器学习的最佳实践,我进一步将训练数据分成训练和验证集。

更多数据请点击这里。

作者图片:Kaggle 提供的 MNIST 训练数据中的前 10 个条目

工具

我们将使用带有 Tensorflow 的 Keras API 作为后端。有很多计算机视觉的框架,但是我发现 Keras 对初学者非常友好,代码也很容易解释和实现。

什么是神经网络?

我不会在这里详细讨论神经网络,但是我会提供一个简单的直觉。

许多书籍使用人类脑细胞的类比作为描述和介绍神经网络的方式。在此之后,他们经常用大量的数学证明来淹没读者。在早期,我发现这种方法令人困惑,因为我无法将数学与类比相协调。我将采取一种稍微不同的方法,试图通过省略类比来揭开人工神经网络的神秘面纱。

我喜欢把神经网络想象成具有难以置信的灵活性的数学函数。正因为如此,它们可以识别数据中的模式,而更基本的功能却不能。

:数学函数就是接受输入,执行运算,然后返回输出的东西。

人工神经网络的灵活性可以通过查看以下属性来理解:

:神经网络的基本结构是三层:输入层、隐层和输出层。深度神经网络有两个以上的隐藏层。增加更多的层可以在一定程度上增加网络的灵活性。

架构:人工神经网络的架构是指输入层、隐含层和输出层相互连接的方式。有许多可用的神经网络体系结构,每一种都适用于特定的任务。例如,递归神经网络适用于预测文本任务。

更多关于神经网络架构的信息请点击。

学习:人工神经网络通过反向传播的过程进行学习。这是网络中的权重在几次迭代(时期)中被调整,以最小化网络预测值和真实标签值之间的误差。一般来说,权重越大,预测复杂模式的灵活性越大。

用于调整权重和最小化误差的主要学习算法是随机梯度下降。

激活函数:这些是非线性数学函数,使神经网络能够检测模式。它们被设置在隐藏层和输出层;它们的类型取决于预测任务。例如,二进制标签分类要求激活函数是 sigmoid 函数(类似于逻辑回归)。

架构&参数

对于我们的数字识别任务,我构建了两个简单的人工神经网络架构。它们具有以下架构和参数:

Layers :一个输入层,定义为大小为 784 的一维数组。两个隐藏层,第一层有 300 个神经元,第二层有 100 个神经元。十个神经元的输出层,用于预测十个手写数字中的任何一个。

激活函数:隐含层神经元的激活函数为 ReLU,一般容易训练。输出层激活函数是 Softmax。这样做的理由是,我们有一个多类分类问题。

损失函数:这是我们训练网络时需要最小化的目标函数。对于我们的任务,我们将使用“稀疏分类交叉熵”。这是最合适的,因为目标变量(标签)是 0 到 9,每个标签都是唯一的。随机梯度下降是用于最小化该损失函数的算法。

时期:这是指我们将在网络上执行的反向传播的迭代次数。对于每个体系结构,我将任意将其设置为 30。

网络 1:序列神经网络

这是 Keras 中可以构建的最简单的人工神经网络。该结构由从输入到输出依次相互连接的层组成。

作者图片:序列神经网络模式

构建网络: Keras 为顺序神经网络提供了一个模板。从那里简单地添加你想要的层是非常直观的。summary()函数为您提供网络架构的详细信息,包括每层中的权重数。

总结提供了有关网络架构的信息。我们的网络中有 266,610 个参数(权重)。

编译:只用几行代码就实现了。

试衣:试衣也…

结果:在第 30 个历元,序列神经网络对训练数据的准确率为 99.82%,对验证数据的准确率为 97.64%。考虑到最小的超参数调整,这是一个不错的性能。

注意— 预计我们的网络在验证数据上的表现稍差,但当它表现明显更差时,我们应该担心过拟合。

按作者分类的图像:x 轴上的纪元,y 轴上的准确度分数

网络 2:深度和广度神经网络

这种网络的体系结构使其能够通过将输入层传递到整个网络并直接传递到输出层来学习复杂和简单的模式。

图片作者:深度和广度神经网络

构建:Keras 没有为深度和广度网络提供模板,所以我们必须手动构建。

注意——编译和拟合我们的深度和广度网络的代码与我们的顺序网络完全相同

与我们的序列版本相比,我们的深度和广度神经网络中有额外的 8,850 个参数。

**结果:**在第 30 代,我们的深度和广度网络在训练集上的准确率为 99.69%,在验证集上的准确率为 97.62%。这与顺序网络的性能相当。你认为这可能是为什么?

测试提交

序列神经网络在测试数据上取得了 97.21%的准确率。这使得该模型在 Kaggle 排行榜上名列前 70%。

在这一点上,有许多事情你可以尝试改进。例如,卷积神经网络架构可能会导致更高的精度。

试着在每一层的神经元数量和时期数量上做些尝试,看看是否会得到更好的分数。和往常一样,您可以通过我的 GitHub 页面使用这些代码。

谢谢大家!

https://github.com/john-adeojo/MNIST-Digit-Classification https://www.linkedin.com/in/john-adeojo/

将机器学习算法完全端到端部署到实际生产环境中

原文:https://towardsdatascience.com/a-full-end-to-end-deployment-of-a-machine-learning-algorithm-into-a-live-production-environment-3d9971ade188?source=collection_archive---------3-----------------------

如何使用 scikit-learn、pickle、Flask、Microsoft Azure 和 ipywidgets 将 Python 机器学习算法完全部署到真实的生产环境中

照片由 Fotis Fotopoulos 在 Unsplash 上拍摄

介绍

2021 年 10 月,我撰写了一篇关于“将机器学习和数据科学项目部署为公共 Web 应用程序”的文章(参见https://towards Data Science . com/Deploying-Machine-Learning-and-Data-Science-Projects-as-Public-we B- Applications-3abc 91088 c11)。

在本文中,我探讨了如何使用 Voila、GitHub 和 mybinder 将 Jupyter 笔记本部署为公共可用的 web 应用程序。

文章发表后,我收到了读者的反馈,他们对如何进一步推动生产部署感兴趣,以探索如何将机器学习算法完全部署到真实的生产环境中,以便以平台无关的方式“消费”它,这导致了本文的想法…

第一步:开发机器学习算法

第一步是开发我们想要部署的机器学习算法。在现实世界中,这可能需要几周或几个月的开发时间,以及跨越数据科学管道步骤的大量迭代,但对于这个示例,我将开发一个基本的 ML 算法,因为本文的主要目的是找到一种方法来部署供“消费者”使用的算法。

我从 ka ggle(https://www.kaggle.com/prathamtripathi/drug-classification)中选择了一个数据集,它是由作者以“CC0:公共领域”许可证创建的,这意味着它没有版权,可以不受限制地用于其他作品中(详见https://creativecommons.org/publicdomain/zero/1.0/)。

开发预测性机器学习算法以在给定一系列患者标准的情况下对药物处方进行分类的 Python 代码如下

0.99 0.012247448713915901

在这一点上,我们可以看到,我们有一个经过训练的机器学习算法来预测药物处方,并且交叉验证(即折叠数据)已用于评估 99%的模型准确性。

到目前为止,一切顺利...

我们将把这个模型部署到生产环境中,虽然这是一个简单的例子,但我们不希望每次用户想要预测药物处方时都必须在真实环境中重新训练我们的模型,因此我们的下一步是使用pickle来保存我们训练好的模型的状态...

现在,每当我们想要使用训练好的模型时,我们只需要从model.pkl文件中重新加载它的状态,而不是重新执行训练步骤。

第二步:根据训练好的模型进行个人预测

我将在第二步做几个假设-

  1. 机器学习算法的消费者要求对单个患者而不是一批患者进行预测。
  2. 这些消费者希望使用类似文本的参数值(例如血压=“正常”或“高”)与算法进行交流,而不是像 0 和 1 这样的标签编码等价物。

因此,我们将从查看用作算法输入的所有标签编码分类特征的值开始…

Sex ['F', 'M'] [0, 1] 

BP ['HIGH', 'LOW', 'NORMAL'] [0, 1, 2] 

Cholesterol ['HIGH', 'NORMAL'] [0, 1] 

Drug ['DrugY', 'drugC', 'drugX', 'drugA', 'drugB'] [0, 3, 4, 1, 2]

现在我们有了一个列表,其中列出了数据中出现的每个分类特征的唯一值,以及由LabelEncoder()转换的相应数值。

有了这些知识,我们可以提供一组映射文本类值(例如“高”、“低”等)的字典。)转换成它们的编码等价物,然后开发一个简单的函数来进行如下的单独预测…

然后,可以通过调用该函数根据原始数据的值进行一些预测来验证该实现,这样我们就知道输出应该是什么…

'drugC'
'DrugY'

请注意,我们的predict_drug函数不需要训练模型,而是将之前由pickle保存其状态的模型“再水合”到model.pkl文件中,我们可以从输出中看到药物推荐的预测是正确的。

步骤 3:开发 Web 服务包装器

到目前为止,一切看起来都很好,但主要问题是:我们的机器学习算法的客户或消费者必须用 Python 编程语言编写,不仅如此,我们必须有能力改变和修改应用程序。

如果第三方应用程序想要使用和消费我们的算法,如果这个第三方应用程序不是用 Python 编写的,该怎么办?也许它是用 Java、C#、JavaScript 或其他非 Python 语言编写的。

这就是 web 服务的用武之地。web 服务是一个“包装器”,它使用 http GET 和 http PUT 命令接收来自客户端和消费者的请求,调用 Python 代码并将结果作为 HTML 响应返回。

这意味着客户端和调用者只需要能够制定 HTTP 请求,几乎所有的编程语言和环境都有办法做到这一点。

在 Python 世界中,有几种不同的方法可用,但我选择的是使用flask来构建我们的 web 服务包装器。

代码并不复杂,但要配置 VS 代码以使开发人员能够调试 flask 应用程序是一个挑战。如果您需要这一步的教程,请查看我的文章“如何在 VS 代码中调试 Flask 应用程序”,可以在这里找到—https://towardsdatascience . com/How-to-Debug-Flask-Applications-in-VS-Code-c 65 c 9 BD bef 21。

以下是 web 服务的包装代码…

从 Anaconda Navigator 页面启动 VS 代码 IDE(或者通过启动 Anaconda 命令提示符并键入code)。这将使用 conda 基本环境启动 VS 代码,这是运行和调试 flask 应用程序所必需的。

通过点击“运行和调试”,然后选择“Flask 启动并调试 Flask web 应用程序”,可以从 VS 代码内部启动 web 服务

作者图片

如果一切都按计划进行,终端窗口中的最后一条消息应该是Running on http://127.0.0.1:5000/ (Press CTRL+C to quit),这表明您的 flask web 应用程序已经启动并正在运行。

您现在应该使用这些方法之一来测试您的 web 服务

  1. 打开网页浏览器,输入: http://127.0.0.1:5000/drug?年龄=60 &性别=F &血压=低&胆固醇=高& Na_to_K=20
  2. 打开 anaconda 命令提示符并输入:curl -X GET "http://127.0.0.1:5000/drug?Age=60&Sex=F&BP=LOW&Cholesterol=HIGH&Na_to_K=20"

作者图片

如果您想了解更多关于开发flask应用程序和 web 服务的信息,这些文章是一个很好的起点

  • https://programminghistorian . org/en/lessons/creating-API-with-python-and-flask
  • https://code.visualstudio.com/docs/python/tutorial-flask

步骤 4:将 Web 服务部署到 Microsoft Azure

我们现在有一个预测机器学习算法,可以以 99%的准确率预测药物处方,我们有一个助手功能,可以进行个体预测,我们有一个 web 服务包装器,可以从浏览器或命令行调用这些组件。

然而,所有这些仍然只能从开发环境中调用。下一步是将一切都部署到云中,这样客户就可以通过公共互联网“消费”web 服务。

有许多不同的公共服务可用于 web 应用程序部署,包括-

  • Google—https://cloud . Google . com/app engine/docs/standard/python 3/building-app/writing-web-service
  • 亚马逊网络服务—https://medium . com/@ rodkey/deploying-a-flask-application-on-AWS-a 72 Daba 6 bb 80
  • 微软 Azure—https://medium . com/@ nikovrdoljak/deploy-your-flask-app-on-Azure-in-3-easy-steps-B2 Fe 388 a 589 e

我选择 Azure 是因为它是免费的(对于一个入门级的帐户),易于使用,快速并且完全与 VS 代码集成,这是我最喜欢的开发环境。

步骤 4.1:将 Azure App 服务扩展添加到 VS 代码中

将任务切换成 VS 代码,进入“扩展”(Ctrl+Shft_X),加入“Azure App Service”扩展。添加扩展后,你会在活动栏中看到一个新的 Azure 图标-

作者图片

步骤 4.1:创建 Azure 帐户

您必须有一个帐户才能开始部署到 Azure cloud,并且您必须在注册过程中提供信用卡详细信息。但是,除非您明确选择退出免费许可,否则不会向您收费。

你可以按照该页面上的说明进行操作—https://azure.microsoft.com/en-gb/free/通过浏览器创建你的免费 Azure 帐户,但最简单的方法是点击活动栏中的新 Azure 图标,然后选择“创建免费 Azure 帐户”(或者如果你已经有一个,则选择“登录 Azure”)——

作者图片

步骤 4.3:创建 Azure Web 应用程序

下一步是通过点击“应用服务”窗口中的“+”号创建一个 Azure web 应用来托管您的应用。系统将提示您输入应用程序的名称。该名称将用于最终的网址,它必须是唯一的,但除此之外,名称并不特别重要。

当提示输入许可证类型时,选择“免费试用”-您的 web 应用程序现在将创建完毕,您可以开始部署了。

作者图片

步骤 4.4 创建一个“requirements.txt”部署文件

在将应用部署到 Azure 之前,您必须在与 Flask web 应用相同的文件夹中创建一个“requirements.txt”文件,该文件包含 Azure 为了运行您的应用而必须安装的所有依赖项和库的列表。如果库不在部署的环境中,应用程序将崩溃,这一步是至关重要的。

我们 app 的requirements.txt内容如下-

作者图片

需要注意的几点是-

  1. 如果使用 pip 安装,库名必须与您键入的完全匹配,例如pip install Flask
  2. 注意Flask有一个大写字母“F”。这是因为 Flask 的大写方式不同寻常,通常库都是小写的。
  3. 需要sklearn来执行再水合的model.pkl。虽然代码中没有明确引用 sklearn 和DecisionTreeClassifier,但是它们是model.fit所需要的,所以如果省略了sklearn,应用程序将会崩溃。
  4. 不需要对pickle的引用,因为这个库是核心 Python 安装的一部分。如果您包含了pickle,部署将会崩溃,因为您不能执行pip install pickle

如果您坚持这些规则,您的部署将会工作,并且任何错误消息通常都足够提供信息,使得通过一点互联网研究就可以解决问题。

步骤 4.5 将你的应用部署到 Azure

如果到目前为止你已经遵循了这些步骤,那么现在你在 VS 代码中已经有了一个 Flask 应用程序。您的应用程序代码文件将被称为app.py,应用程序名称为app。Flask 应用已经在本地开发 web 服务器上进行了测试。

您已经安装了 VS Code Azure 应用程序扩展,并使用它创建了一个 Microsoft Azure 免费帐户和一个 Azure web 应用程序。

您应该在 VS 代码中打开 Flask 应用程序,并且您已经准备好将应用程序部署到云中。

只需点击蓝色圆圈图标旁边的 web 应用程序名称,然后点击“+”号旁边的云图标即可。

出现提示时,选择以下选项-

  • 选择要部署的默认文件夹
  • 选择“免费试用”订阅
  • 选择您创建的 web 应用程序名称
  • 如果提示覆盖,请选择“部署”
  • 当要求“总是部署…”时,选择“现在跳过”
  • 当部署开始时,单击“输出窗口”

现在坐下来煮一杯咖啡,同时应用程序展开-

作者图片

部署完成后,点击“浏览网站”,您将被带到运行app.route("/")功能的正确 URL。

只需添加我们用来测试本地部署的相同 URL 参数,您将看到完全部署的 web 应用程序的输出!-

【https://graham-harrison68-web03.azurewebsites.net/drug? 年龄=60 &性别=F &血压=低&胆固醇=高& Na_to_K=20

作者图片

需要注意的一点是:过了一会儿,azure 应用程序进入睡眠状态,之后的第一次调用需要很长时间。

如果你选择升级到付费的 Azure 订阅,有一个选项可以让应用程序保持刷新和“唤醒”,但在免费订阅中,睡眠相关的延迟是不可避免的,因为该订阅旨在进行测试,因此有一些限制。

步骤 5:构建客户端应用程序来使用 Azure 部署的 Web 服务

此时,任何可以调用 web 请求的编程语言或环境都只需几行代码就可以调用部署的 web 服务。

我们确实从 C#、JavaScript 等非 Python 环境开始。都可以使用,但我将通过编写一些代码从 Python 客户端使用ipywidgets调用部署的应用程序来结束这个示例

作者图片

如果您使用默认值单击“处方”,建议应该是“药物”。

将年龄改为 60 岁,将 Na 改为 K 改为 20 岁,并应规定“吸毒”。把年龄放回 47 岁,把钠对钾放回 14 岁,把血压改为“高”,应该开 drugA。

这些简单的测试证明,使用基于决策树的预测机器学习算法的 Azure 托管的 web 服务完全部署到公共云,可以被任何能够执行http GET命令的开发环境调用,并且完全端到端工作。

结论

这涉及到相当多的步骤,但使用现成的库和免费工具,包括 scikit-learn、pickle、flask、Microsoft Azure 和 ipywidgets,我们已经构建了一个完全工作的、公开可用的机器学习算法的云部署和一个完全功能的客户端,以调用和使用 web 服务并显示结果。

进一步阅读

如果你觉得这个教程很有趣,我会推荐这篇文章,它深入讨论了模型部署策略的细节

https://neptune.ai/blog/model-deployment-strategies

感谢您的阅读!

如果你喜欢读这篇文章,为什么不看看我在 https://grahamharrison-86487.medium.com/[的其他文章呢?](https://grahamharrison-86487.medium.com/)

此外,我很乐意听到您对这篇文章、我的任何其他文章或任何与数据科学和数据分析相关的内容的看法。

如果你想联系我讨论这些话题,请在 LinkedIn 上找我—https://www.linkedin.com/in/grahamharrison1或者发电子邮件到ghar rison @ Lincoln college . AC . uk。

如果你想通过订阅来支持作者和全世界成千上万为文章写作做出贡献的人,请使用这个链接——【https://grahamharrison-86487.medium.com/membership。注意:如果你使用这个链接注册,作者将收到一定比例的费用。

使您的初始 EDA 变得轻而易举的功能

原文:https://towardsdatascience.com/a-function-that-makes-your-initial-eda-a-breeze-8e9549d69fb3?source=collection_archive---------26-----------------------

约书亚·阿拉贡在 Unsplash 上拍摄的照片

EDA(探索性数据分析)是数据科学旅程中至关重要的一部分,它让我们能够理解给定的数据,并指导我们做出建模决策。在做出任何假设之前,先“感受”一下数据,这对于形成有价值的见解至关重要。坏消息呢?这通常很耗时。

我的上一个项目使用了奥斯汀动物收容所提供的数据,这些数据来自奥斯汀开放数据门户。基于动物的摄入信息(动物类型、年龄、品种、颜色、摄入月份等),使用预测模型来确定是否要收养动物。).收容所里有超过 150,000 只动物,为了了解奥斯汀的摄入量,探索这些不同的分布是很重要的。

当我开始探索和创建条形图时,我发现自己一遍又一遍地编写相同的代码。通常当这种情况发生时,我会问自己,“我应该创建一个函数吗?“最终,创建这个功能节省了我很多时间。更聪明地工作,而不是更努力。这是我创建的函数:

def initial_eda(df):
    # List of categorical columns
    cat_cols = df.select_dtypes('object').columns

    for col in cat_cols:
        # Formatting
        column_name = col.title().replace('_', ' ')
        title= 'Distribution of ' + column_name

        # Unique values <= 12 to avoid overcrowding
        if len(df[col].value_counts())<=12: 
            plt.figure(figsize = (8, 6))        
            sns.countplot(x=df[col], 
                          data=df, 
                          palette="Paired",
                          order = df[col].value_counts().index)
            plt.title(title, fontsize = 18, pad = 12)
            plt.xlabel(column_name, fontsize = 15)
            plt.xticks(rotation=20)
            plt.ylabel("Frequency",fontsize = 15)
            plt.show();
        else:
            print(f'{column_name} has {len(df[col].value_counts())} unique values. Alternative EDA should be considered.')
    return

对我的数据运行该函数时,输出如下:

请注意,没有创建颜色和品种列中的地块。如果我的函数中没有第 11 行,这两个视觉效果就会被创建出来:

呀!不是信息性的,显然不是我想在任何演示中包含的视觉效果。这解释了为什么我将函数限制为从少于 13 个唯一值的列创建图。然而,这个最大值可以调整。这些列中唯一值的数量被打印出来,以指导对这些列的进一步探索,而不是这些“不那么漂亮”的图。

对于创建的大量视觉效果,几乎不需要编码。在完善这些图形的过程中,可以进一步探索该功能,但对于初始 EDA,该功能可以在进入建模阶段之前节省大量时间。我计划在未来的项目中使用这个功能,我希望其他人也能发现这很有用!

要了解更多关于这个项目的信息,你可以在我的个人 GitHub 这里查看。

一个模糊字符串匹配的故事

原文:https://towardsdatascience.com/a-fuzzy-string-matching-story-314bbecaa098?source=collection_archive---------11-----------------------

了解如何构建模糊字符串匹配基线模型

布鲁诺·马丁斯在 Unsplash 上的照片

在构建基于机器学习应用的新产品时,我们需要从不同的来源提取、收集和存储数据。无论采用何种提取方法(API、网络抓取或其他),收集的数据仍然需要经过可能是最重要、最耗时的任务,即把来自这些来源的数据整合、匹配和规范化到数据库中。

在这篇文章中,我们将讨论关于模糊字符串匹配。这个问题包括尝试匹配相似但不同的字符串(可能是由于拼写差异或错误),例如“Lionel Messi”和“L. Messi”或“Argentina”和“Argentina”。

但是这有什么关系呢?我给你讲个故事吧(免责声明:本文所有人物和事件均属虚构,与现实有任何雷同,纯属巧合)。

开始

John 拥有一家大型连锁售货亭,他希望使用竞争对手(其他售货亭、附近的商店和超市)的价格建立更精确的定价模型。不久前,他曾经付钱给神秘顾客,让他们每隔一个月访问并收集一些竞争对手的价格数据,直到他意识到这根本不划算,因为价格似乎每个月都要调整多次。

意识到这一点后,他迅速采取行动,决定建立一个网络抓取服务,以更高的频率提取竞争对手的产品价格。实施该解决方案后,一切似乎都很顺利,成功地收集了数据,John 觉得他已经击败了系统,并认为他的数据团队将能够直接构建新的定价模型。然而,有一个他没有考虑到的大问题:废弃产品的拼写不一定与他的售货亭目录匹配,这意味着他无法直接将他的内部数据与外部来源进行关联和比较。例如:

【健怡可乐 1.5L】≠“可乐 dieet 1500ml”

那么,他在意识到不能使用数据后做了什么呢?首先,在一次突然的暴怒中,他弄坏了他的键盘。后来,冷静下来后,他继续询问他的数据团队是否知道如何解决这个问题(使用他的另一个键盘)。“独角兽”员工卡尔看到了赢得约翰好感的机会,他告诉约翰,他以前处理过类似的问题,可以在一周内获得 MVP(但说实话,他不知道自己在做什么)。约翰愉快地同意了,但是他没有给卡尔一周时间去做,而是给他两周时间。

寻找解决方案

利用过去被证明有效的方法,卡尔用他最可靠的工具“谷歌”搜索“python 中的字符串匹配”。他很快就看到了几篇关于正则表达式匹配字符串的帖子,一种他从未听说过的新距离,称为“Levenshtein 距离”,以及模糊字符串匹配的概念。但是没有一个人给他提供实用的解决方案,直到他偶然发现了一篇名为“模糊字符串匹配的故事”的文章,作者是一个叫 Ezequiel Ortiz Recalde 的人。这篇文章为他提供了一些有用的建议,告诉他如何通过一个实际的例子来完成匹配过程和构建基线模型。我们现在来看看卡尔看到了什么。

1。Imports 首先,我们安装并导入主库,用于匹配售货亭或超市中的饮料字符串(数据是手动生成的)。

#!pip install fuzzywuzzy
#!pip install python-Levenshtein
#!pip install polyfuzz
#!pip install nltkimport pandas as pd
import numpy as np
import string
import refrom nltk.metrics.distance import edit_distance
from fuzzywuzzy import fuzz
from polyfuzz import PolyFuzz
from polyfuzz.models import TFIDF

2。数据

我们已经生成了两个饮料数据集(外部数据和内部数据)。第一个是指可能通过抓取网站获得的外部数据,而第二个是复制一个售货亭的虚拟官方饮料目录的一部分。在这里,我们可以看到两者的示例:

外部数据

内部数据

3。字符串预处理

在尝试匹配不同来源的字符串之前,有一些有用的转换可以简化这个问题:

  • 将字符串转换为小写;
  • 小心地去掉符号和标点符号;
  • 删除连续的空格(或全部,取决于问题);
  • 小心地删除停用词(注意删除关键词,因为它们在将来可能会有用;避免删除太多特定的单词,因为你仍然希望你的模型能够概括);
  • 标准化不同表达式的拼写(例如,在 grams 的情况下,我们可以找到 g,gr,gs,g,gr,gs。等);

根据这些建议,我们使用下面的函数来清理两个数据帧的列:

在应用该函数之前,我们创建要修改的列的副本,以便保留原始版本:

external_data["product_cleaned_from"]=\
external_data["product"].copy()internal_data["product_cleaned_to"]=\
internal_data["product_name"].copy()

将函数应用于两个数据帧:

string_processing(df=external_data,columns_to_clean=["product"])string_processing(df=internal_data,columns_to_clean=["product_name"])

这样做之后,我们终于有了干净的数据进行匹配。接下来,我们将讨论要使用的主要方法。

4。使用 Tf-Idf 矢量化进行字符串匹配

词频逆文档词频矢量化是许多 NLP 应用中使用的一种技术,其目的是考虑单词在文档中以及在所有语料库中出现的次数,来近似估计该单词在文档中的相关性。通过这种方法,我们获得了每个文档中每个单词的“权重”向量,我们可以将它用作分类模型的特征。

但是,这与字符串匹配有什么关系呢?我们可以将令牌划分成 n 元文法,然后获得它们的 TF-IDF 向量化。一旦我们有了 n-gram 的权重向量,我们就可以计算它们之间的相似性。注意,相对于使用基于 Levenshtein 距离的比率来检查在所有可能的短语对的每一个之间应该进行多少编辑的缓慢过程,这允许我们利用矩阵运算的快速计算。

匹配过程如下进行:

a) 定义两个字符串列表,一个列表“From”(指我们想要匹配的内容)和一个列表“to”(在本例中为目录)。

b) 定义 n 元文法的数量,其中的字符串将被分割。

c) 用两个列表拟合 TF-IDF 矢量器,以便得到所有可能的唯一 n 元文法的表示。

d) 使用 TF-IDF 表示,将每个列表转换为相同的表示(如果一个字符串不包含矢量器的唯一 n 元语法分区之一,则该分区的值将为 0)。在这一步中,我们将获得两个矩阵,其列数与唯一 n-grams 划分的列数一样多(两个矩阵的列数相同,因为矢量器适合使用两个列表),行数指的是每个源列表中的元素数。

幸运的是,整个过程已经集成到了 PolyFuzz 库中,但是我们仍然想知道我们是否理解了眼见为实的工作原理:

首先,我们将导入一些解释所需的函数:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

接下来,我们生成要匹配的字符串列表:

from_list = ["coke 1.5L","sprite zero 1.5L"]
to_list = ["diet coke 1.5L"]

我们稍微修改了这个函数,为在 PolyFuzz 库中使用的标记创建 n 元语法分区,使其直接使用 3 元语法。

def _create_ngrams(string: str): result = []
   for n in range(3, 4):
      ngrams = zip(*[string[i:] for i in range(n)])
      ngrams = [''.join(ngram) for ngram in ngrams if ' ' not in ngram]

   result.extend(ngrams)
   return(result)

这个函数接收一个字符串,并为每个由空格分隔的标记创建一个 n 大小的分区。例如,“健怡可乐”会被分成“die,iet,cok,oke”。接下来,我们能够使用 _create_ngrams 函数来拆分标记并创建列表元素的矢量化表示,从而适应 scikit learn 的 TfidfVectorizer :

# Define the vectorizer
vectorizer = TfidfVectorizer(min_df=1, analyzer=_create_ngrams).fit(to_list + from_list)

注意这里有两个显式参数 min_dfanalyzer 。第一个是指一个划分必须在语料库中出现以被包括在词汇表中的最小次数,而第二个是用于指定如何创建矢量器的词汇表,即从单词或 n 元语法中创建。

给定输入列表,我们可以检查结果词汇。

vectorizer.vocabulary_

这些数字表示矩阵的列索引(“. 5L”将在列 1 中)。

创建矩阵表示后,我们继续转换每个列表以匹配其结构,接收稀疏矩阵:

tf_idf_to = vectorizer.transform(to_list)
tf_idf_from = vectorizer.transform(from_list)

为了检查每行中这三个字母的权重,我们将 tf_idf_to 和 tf_idf_from 转换为密集格式,然后转换为 dataframe,使用词汇索引来设置列名:

matrix_from=pd.DataFrame(tf_idf_from.todense(),\
columns=sorted(vectorizer.vocabulary_))matrix_to=pd.DataFrame(tf_idf_to.todense(),\
columns=sorted(vectorizer.vocabulary_))

两个矩阵都有 n 个元素 x n 个词汇,如下所示:

矩阵 _ 从

matrix_to

既然我们有了矩阵,我们就能够计算 n-grams 划分的向量之间的相似性。在这种情况下,我们使用余弦相似度:

cosine_similarity(matrix_from, matrix_to)

带输出:

这给出了记号向量对之间的相似性得分。在这种情况下,我们可以看到“可乐 1.5L”比“雪碧零度 1.5L”更类似于“健怡可乐 1.5”。我们刚刚做的是矩阵之间的点积:

np.dot(np.array(matrix_from.iloc[0]),np.array(matrix_to.iloc[0]))

返回 0.6936279421797706,而

np.dot(np.array(matrix_from.iloc[1]),np.array(matrix_to.iloc[0]))

返回 0.1373086119752811。

5。其他有用的度量:令牌集比率和 Levenshtein 距离

我们只检查了字符串之间相似性的一个度量,但是根据您正在比较的字符串类型,还有其他可能有帮助的度量。对于这个特殊的例子,我喜欢在分析中包括记号集比率和 levenshtein 距离。

标记集比率是字符串的相似性度量,它考虑了值的重复和单词顺序的差异(例如,“diet coke”与“coke diet”相同,对于这些类型的情况,我们会对捕捉这样的直接答案感兴趣)。我们用一个简单的例子“健怡可乐 1.5ml”和“健怡库克减肥药 1.5ml”来看看如何计算:

# We create the 2 strings
s1="diet coke 600ml"
s2="diet cooke diet 600ml"

I)我们从预处理开始,将字符串分离成标记:

tokens1 = set(fuzz.utils.full_process(s1).split())
tokens2 = set(fuzz.utils.full_process(s2).split())

这里 tokens1 = { ' 600ml ','可乐','减肥' }

ii)然后,我们取记号集,并获得它们的交集和差异:

# Intersection
tokens1.intersection(tokens2)# Difference (which tokens are we missing so that both sets are equal)
diff1to2 = tokens1.difference(tokens2)
diff2to1 = tokens2.difference(tokens1)

iii)我们连接了有序交集和差集,解决了字符串的顺序问题:

#We concatenate the ordered intersections and differencessorted_sect = " ".join(sorted(intersection))
sorted_1to2 = " ".join(sorted(diff1to2))
sorted_2to1 = " ".join(sorted(diff2to1))

iv)我们组合每个选项中的标记来重构整个字符串:

combined_1to2 = sorted_sect + " "+ sorted_1to2
combined_2to1 = sorted_sect + " "+ sorted_2to1

v)我们消除了重复的空白:

sorted_sect = sorted_sect.strip()
combined_1to2 = combined_1to2.strip()
combined_2to1 = combined_2to1.strip()

vi)我们计算不同组合对的模糊率(即归一化的 Levenshtein 距离):

模糊率

pairwise = [fuzz.ratio(sorted_sect, combined_1to2)/100,
 fuzz.ratio(sorted_sect, combined_2to1)/100,
 fuzz.ratio(combined_1to2, combined_2to1)/100] 

带输出[0.8,0.77,0.97]。

vii)我们保留具有最大模糊率的配对及其相似性。

关于 Levenshtein 距离,我们简单地使用 nltk 的 edit_distance 函数。这告诉我们将一个字符串转换成另一个字符串所需的编辑次数。

6。构建基线模型

现在我们已经对这个过程有了更好的理解,剩下的就是构建一个管道,它接受两个列表,用它们内容的分区来拟合一个矢量器,计算矢量之间的点积,并匹配具有最高相似性得分的对。这就是 Polyfuzz 库发挥作用的地方,它为我们提供了一个完全遵循我们刚才解释的模型。此外,我们可以向最终结果添加一些额外的信息(标记集比率和 Levenshtein 距离),以避免在后验结果过滤中只有一种方法来检查匹配质量。

以下代码块详细描述了整个过程:

7 .。结果

这里有一个获得结果的例子,你可以通过检查笔记本来探索其余的。

8。结束语

在这篇简短的文章中,我们展示了一个简单的框架,它可能能够为任何模糊字符串匹配问题产生不错的结果(至少对我来说是这样)。不管 kiosk 产品的例子是什么,只要您需要将非规范化或自由文本与某种已经过企业理想验证的目录或参考进行匹配,这应该会对您有所帮助。

结束了

读完之后,在着手解决他的匹配问题之前,Carl 做了每个人都会做的事情,即他喜欢并分享了这篇文章😉。

参考

https://en.wikipedia.org/wiki/Approximate_string_matching https://openclassrooms.com/en/courses/6532301-introduction-to-natural-language-processing/7067116-apply-the-tf-idf-vectorization-approach https://github.com/MaartenGr/PolyFuzz

异方差的高斯过程模型

原文:https://towardsdatascience.com/a-gaussian-process-model-for-heteroscedasticity-aa0db109473e?source=collection_archive---------45-----------------------

思想和理论

如果你的回归方差是非平稳的,试试这个

介绍

在处理连续回归问题时,一个常见的现象是非常数残差方差,也称为异方差。虽然异方差经常出现在统计学和计量经济学中,但它在主流机器学习和数据科学文献中似乎没有受到太多的关注。尽管通过最小化 MSE 来预测平均值通常就足够了,而且更加实用,但对方差的适当处理有时也是有帮助的。请看我的过去的博文来了解更多关于这个话题的想法。

在这篇文章中,我想展示一个如何使用高斯过程来模拟异方差数据的例子。因为解释每一个理论方面都远远超出了这篇文章的范围,所以如果你对这样的模型感兴趣,我推荐阅读参考资料。首先,让我们从一个简单的问题定义开始。

问题定义

非恒定方差模型的核心是假设输入数据和目标变量的方差之间存在某种函数关系。假设也是高斯目标变量,我们可以构建以下概率设置:

简单地说,给定一些输入数据,相应的目标应该是高斯型的,均值和方差是我们输入的任意函数。因为我们今天的重点是方差,所以让我们用一个零均值函数来简化一下,即

我们现在的任务是为 sigma 平方找到一个合适的函数。

如果我们事先不太了解我们的目标函数,那么无论我们提出什么模型,都应该考虑到我们对于 sigma 平方的实际函数形式的不确定性。这也被称为认知不确定性,也是贝叶斯机器学习的主要考虑因素之一。简单地说,我们现在不再期望单一的模型能够最好地描述我们的目标函数。取而代之的是,考虑一组可能无限大的模型,我们的目标是在这组模型上放置一个概率分布(也称为后验分布),以便在给定假设(也称为先验分布)的情况下,最能描述数据(也称为可能性)的模型是最有可能的模型。

这通常是在权重空间中通过描述模型行为的参数集以隐含的方式定义我们的模型集来完成的——可能机器学习中最著名的例子是贝叶斯神经网络。另一种更抽象的方法是直接在函数空间中工作,也就是说,我们现在明确地寻找最可能的函数,而不需要参数来首先描述它们。因为我们在贝叶斯领域工作,这也意味着先验和后验分布不再放在参数上,而是直接放在函数上。这种建模最具代表性的框架之一是高斯过程(GP)回归。

如果这对你来说是一个新概念,听起来很混乱,我建议现在不要担心潜在的假设,只看公式。根据引用数量,最受欢迎的关于高斯过程模型的书籍之一,机器学习的高斯过程 (GPML)非常清晰地介绍了理论设置,并且是完全开源的。为了防止这篇文章过于浮泛,我不会过多地讨论细节,而是建议你研究你自己不了解的主题。

模型

我们的目标是通过 GP 对目标变量的变化方差进行建模,如下所示:

这意味着我们的方差函数的对数是一个 GP,我们需要通过指数压缩原始 GP,以确保方差总是大于零。将实数直线映射到正实数的任何其他函数在这里都可以,但指数函数无疑是最受欢迎的函数。以上也暗示了 GP 实际上是我们模型的一个潜在组成部分,我们只是从收集的数据中间接观察到的。最后,我们通过 delta 被加数假设 GP 核上的附加噪声,这使得模型在实践中更加稳定。

然后,我们可以通过贝叶斯定理导出后验分布,如下所示:

虽然对于一些基本 GP 模型,可以推导出封闭形式的左侧,但在我们的情况下,我们不能这样做。相反,我们将应用拉普拉斯近似法并通过一个合成的多元正态来近似它

对于二元分类模型,拉普拉斯近似的确切步骤在 GPML 著作第 3 章中有解释,我们只需要调整我们模型的方法。

总之,我们近似的平均参数应该匹配后验的模式,而它的协方差矩阵是我们的数据对数似然函数的 Hessian 矩阵的负逆。我们有:

第一个方程是从后验公式的分母不依赖于我们的目标这一事实和对数函数的单调性推导出来的。后一个方程是从后验函数最大值附近的二阶泰勒展开式导出的。

为了找到稍后一些示例数据的近似平均值和最佳核超参数,我们将把整个损失插入到自动微分包中,并让计算机完成剩下的工作。对于我们近似的协方差矩阵,我们需要实际计算海森矩阵。GP 模型的一个常见简化是假设实现了潜在 GP,目标变量的独立观测值:

这允许我们将 Hessian 矩阵简化为处处为零,除了它的对角线是对数似然函数相对于 GP 的二阶导数:

右手边可以通过将标准高斯对数似然相对于方差微分两次来导出,同时考虑我们的指数变换:

这产生了

最后,我们需要推导出所谓的后验预测分布,即我们对新的、未观察到的输入的预测:

对于我们的设置,我将只陈述来自 GPML 第 3 章的结果,没有前面的推导。首先,我们需要计算潜在 GP 的后验预测分布,使用我们上面的近似,它是另一个 GP:

其中 K 变量表示训练和评估数据集的核协方差矩阵,以及训练和评估数据集之间的核互协方差矩阵。如果您熟悉 GP-回归,您可以看到后验均值和协方差项与标准情况下几乎相同,只是我们考虑了拉普拉斯近似的均值和协方差。

最后,我们可以通过忽略 GP 后验预测函数来导出新数据的后验预测分布:

这个积分也是难以处理的——幸运的是,由于我们只想评估后验预测分布,我们可以通过蒙特卡罗抽样从目标分布中进行抽样。

为了在实践中演示这种方法,我在 Julia 中实现了一个简单的例子。完整的代码可以在 Github 上的这个 Jupyter 笔记本中找到。

一个使用 Julia 的快速示例

数据是一个简单的 1D 玩具示例,有 200 个观察值和生成分布

即,输入变量在-3 和 3 之间被均匀采样,并且目标是从具有周期性方差的零均值高斯中提取的:

200 个训练观测值(红点)采样自零均值(蓝线)高斯分布,方差遵循 X 的正弦函数;蓝色区域表示平均值的两个标准偏差。

为了完全定义 GP,我们还需要指定核函数——这里我选择了一个标准平方指数(se)核加上已经提到的加性噪声项,即

其中所有三个超参数都需要为正。我们现在有了定义必要的函数和结构所需的所有公式(Julia 的对应物是面向对象语言中的类)

在优化上述核超参数和拉普拉斯近似之后,得到的函数后验预测分布如下:

数据生成过程(蓝色)与后验预测分布(红色)。已经为更大范围的输入域绘制了后验预测分布,以证明观察数据之外的不确定性的增加。

为了查看位于我们的训练数据范围之外的数据会发生什么情况,对该间隔执行了评估。正如你所看到的,随着我们对未知的研究越深入,后验预测方差会急剧增加。

这正是在认知不确定性的影响下应该发生的事情。在某种程度上,模型可以学习哪些函数描述了与训练数据接近的目标函数。另一方面,可能同样很好地描述我们观察之外的数据的候选函数集随着我们远离训练数据而变大。

简而言之,我们的测试数据与训练数据越不相似,我们的预测就越不确定。这种不确定性由后验预测分布的方差表示,方差越大,不确定性越大。

通过比较位于观测数据中心的 X 的后验预测密度与位于该范围之外的 X 的后验预测密度,我们也可以很好地看出这一点:

X=0 时的后验预测密度(左)与 X=-3.5 时的后验预测密度(右)。由于 X=-3.5 位于我们的训练数据的范围之外,后验预测不确定性更高,这导致了重尾分布。

很明显,在-3.5 处的后验预测密度意味着 y 的潜在值的范围比零处的后验预测密度宽得多。能够量化这种不确定性是贝叶斯机器学习最有趣的特性之一,我强烈建议深入研究这个庞大的主题。

更进一步

很明显,我们使用的示例只是一个玩具数据集,还不能证明所提出的模型在现实世界中的能力。如果您感兴趣,请随意使用和修改代码,并在更真实的东西上尝试该模型。

这种模型的一个潜在应用是金融时间序列数据,众所周知,这些数据在危机时期表现出高度可变的变化。虽然 GARCH 模型在这里通常被认为是最先进,但 GP 模型可能是一个有趣的替代模型。对一般连续回归问题的另一个可能的改进是也将数据均值建模为 GP。

关于可伸缩性的最后一句话:像我们在这里讨论的普通 GP 模型因对大型数据集不可行而臭名昭著。幸运的是,许多聪明人已经找到了解决这些问题的方法,至少在某种程度上。如果您对此类方法感兴趣,您可以在 2019 年高斯过程暑期学校的这些幻灯片中找到概述。

今天就到这里。感谢你读到这里,如果你有任何问题或者在这篇文章中发现任何错误,请在评论中告诉我。

原载于 2021 年 6 月 28 日【https://sarem-seitz.com】

共轭先验的温和介绍

原文:https://towardsdatascience.com/a-gentle-intro-to-conjugate-priors-8be6ac0d31f6?source=collection_archive---------21-----------------------

为了更快更简单的贝叶斯推理

共轭先验可以使贝叶斯推理更快更直接。让我们看一看贝叶斯法则,来理解我们的意思。

贝叶斯法则:后验概率与似然概率和先验概率成正比。图片作者。

当我们进行贝叶斯推断时,我们通常试图识别参数的后验(我们在看到数据后对真实模型参数的信念)。就像任何类型的建模一样(回归、深度学习等。)我们要学习参数。这里我们将它们量化为分布而不是点估计。

P( θ )是我们对参数的先验信念,是我们在推断之前必须赋予的东西。P(Y| θ 是给定参数下我们数据的似然。出于这些目的,我们可以忽略 P(Y ),因为它对θ的所有值都是一样的。

注:要了解更多关于贝叶斯推理的直觉背景,请随意参考我以前的 文章

可能性

可能性是我们关注的第一个术语。它的形式取决于我们选择使用什么样的模型。例如,在线性回归模型中,我们假设数据围绕回归线呈正态分布。因此,每个数据点的可能性对应于具有平均 X^T B(即回归线上的点)和方差σ(误差方差)的正态 PDF(概率密度函数)。

不过,线性回归不是本文的重点。我们将使用一个更简单的(单参数)模型,二项式似然模型。

二项式分布模拟了一系列的伯努利试验。伯努利试验是一种单次随机二元试验(如抛硬币),结果 1 的概率为 θ ,结果 0 的概率为(1- θ )。二项式分布对所有 n 次试验的结果总和(即抛硬币的头数)进行建模。

伯努利试验的可能性对于值 1 是简单的 θ ,对于值 0 是(1- θ )。对于 0.7 的 θ 值,伯努利试验可能性如下所示:

作者图片

或者作为一个等式:

作者图片

对于 Y=0 和 Y=1 的值。

用概率 θn 次伯努利试验的总和进行建模的二项式似然性具有类似的似然函数。

作者图片

其中这个时间 y 是“成功”的总次数,或者结果= 1 的次数,其中的可能值在 0 和 n 之间。

让我们用一个例子来更具体地说明这一点。

我们有一种降压药,它有偏头痛的副作用,我们想弄清楚当你服用这种药时,头痛的概率是多少。假设我们正在研究 30 名患者。“数据”或患头痛的患者总数的可能性是:

作者图片

对于 θ = 0.1(或副作用概率为 10%),可能性看起来像这样:

作者图片

一系列 n=30 次伯努利试验的二项式可能性(y 成功的可能性),其中 θ = 0.1。(图片由作者提供)

正如所料,在给定 θ = 0.1 的情况下,最有可能的情况是,我们的患者群体中有 10% ( y =3)出现了副作用。

在先的;在前的

现在回到我们问题的贝叶斯部分。请记住,我们试图使用一些数据*来推断副作用发生率 θ 。*我们实际上从类似的、已经测试过的药物中获得了一些先验信息,即副作用发生率往往在 15%左右。由于我们使用的数据有限(我们只有 30 个受试者要测试),我们希望以某种方式将我们的先验知识融入到模型中。

我们用前面的项 P( θ )来做这件事。P( θ )代表我们对副作用发生率 θ 真实值的信念,所以应该是某个概率分布,可能值在 0 到 1 之间。从技术上讲,P( θ )可以是任何一种分布。但是回头看看贝叶斯法则:

作者图片

请记住,对于 θ 的所有可能值,我们将可能性 p(y| θ ) 和先验 P( θ )相乘,这样我们就可以得到 θ | y. 的后验置信分布

一个有点幼稚的方法是,为 P( θ )选择一些分布,从这个分布中取一堆样本,将 P( θ )的 PDF 乘以每个 θ 样本的 P(y| θ )的 PDF,形成我们的后验概率。但是我们可以用更聪明的方法来做,根本不需要任何采样。

如果似然 P(y| θ 和先验 P( θ )可以来自同一个“家族”,或者相对于 θ 有相同的项,那么我们就可以很容易地将两个概率分布相乘。结果是一个与先验类型相同的后验分布。除了更快的推断之外,每当我们获得更多的数据时,我们可以使用这个后验作为先验。

让我们再次看看药物副作用例子的可能性分布。二项式可能性是

作者图片

在我们的研究中,我们有 n =30 名患者,所以我们的可能性等式看起来像这样。

作者图片

现在,我们希望找到一个具有相同项(相对于 θ )的先验分布,以便我们可以执行一个乘法步骤,而不是任何积分或采样。我们实际上并不关心 30-choose-y 项,因为和 P( y )一样,它对所有的 θ 值都是一样的。

因此,我们检查了一些可能的分布…并发现 2 参数贝塔分布看起来很像二项式似然!

作者图片

*Beta PDF 的第一部分中的伽马函数并不是非常重要;同样,我们不必关心它们的推论,因为它们不是 *θ的函数。其他项( θ 的某次方幂和(1- θ )的某次方幂,与我们的可能性相匹配。

现在,我们知道应该使用带有参数 ab 的 Beta 先验分布进行快速推断。换句话说,我们将需要选择参数 ab ,使贝塔先验分布看起来像我们关于 θ 的先验信念。

在后面的

接下来,当我们将似然性 P(y| θ 和先验 P( θ )相乘时,我们得到一个后验 P( θ | y ),它也具有贝塔分布。

二项似然模型后验分布的推导。图片作者。

就是这样!对于任何先前的 P( θ ) = Beta( ab ),我们有关于 θ 的后验置信,以及 n 试验的任何数据,并且 y 总“成功”(结果等于 1,原谅称副作用成功的误称)。

现在回到我们的例子,我们之前认为副作用发生率可能在 15%左右,我们可以使用β(a = 3,b=17),看起来像这样:

**θ的先验分布。图片作者。

用 E[P( θ )]或均值 0.15。注意:如果我们不太有信心,我们可以选择一个更宽的先验,其均值仍然为 0.15。值得试验一下先验参数,以找到一个准确描述先验置信度的参数。

如果我们给 30 个病人服用这种药物,发现有副作用,我们的后验概率就变成了

二项式后验概率的例子。作者图片

**θ的后验分布。图片作者

我们从之前的信念(在看到数据之前)到之后的信念(在看到数据之后)的变化如下:

**θ的先验和后验分布。图片作者

我们现在对我们关于副作用率的信念更有信心了一点(如更窄的后验曲线所示),期望值或均值从 0.15 变为 0.12,这可以从已知的 Beta 分布的均值 a/(a+b)计算出来。

总之,使用共轭先验进行贝叶斯推断的步骤如下:

  1. 定义您正在使用的模型,以及该模型的可能性
  2. 找到与可能性具有相同形式(相对于 θ )的先验分布类型
  3. 通过将似然性和先验相乘找到后验分布。

使用共轭先验的一些好处是速度(不需要 MCMC 采样),以及由此产生的明确定义的后验,从中可以很容易地获得汇总统计或用作将来接收更多数据时的先验(“贝叶斯更新”)。

一些示例共轭先验是二项式似然的β先验、泊松似然的γ先验以及正态似然的平均参数的正态先验(具有已知方差)。

代码生成评估的简明介绍

原文:https://towardsdatascience.com/a-gentle-introduction-to-code-generation-evaluation-c8dff8c3d19a?source=collection_archive---------33-----------------------

可用代码生成评估指标的概述和比较

由威廉·沃比在 Unsplash 上拍摄的照片

随着 OpenAI Codex 模型的发布,主要由于一些模型的令人印象深刻的结果,使用深度学习的代码生成正在成为一个热门话题,我认为这将使我们重新评估和重新想象我们未来如何创建软件。

与任何深度学习模型一样,训练过程需要一个指标来评估性能。目前,有很多方法可以评估这类模型。然而,还不清楚(我认为)哪一个是最好的,这促使我写这篇文章。

这篇文章概述了用于代码生成模型的度量标准,并使用一个真实的例子来比较每一个度量标准,以突出它们的优点和缺点。

运行示例

为了帮助评估每个指标,我们将使用下面描述的示例,其中我们有三个模型负责预测所提供提示的代码片段,我们希望使用分数来选择最佳模型。在现实世界中,人们会比较整个测试或验证集的结果,但是为了这篇文章,我们将只使用一个样本。

图片由作者提供。

您可能已经注意到,模型 3 的预测是错误的(a-b而不是a+b)。我添加了这个预测来查看哪些指标只标记了逻辑和语法错误。

精确匹配

精确匹配(EM)或有时被称为完美准确度或精确度,是检查候选人是否与参考精确匹配的度量。在我们的示例中,所有候选人的 EM 分数都是 0。

在分类任务中,准确性可能会有所帮助。然而,这一指标并不能帮助量化代码生成的性能。众所周知,一个特定的功能可以用不同的方式实现(例如递归对迭代,函数对命令,等等。),并且这个度量不能考虑这方面,使得它对于代码生成任务没有用处(我认为)。

尽管如此,一些代码生成基准仍然使用它,但这可能是因为遗留的原因。例如,CodeXGLUE benchmark [1]确实在代码生成排行榜中报告了 EM 分数,但他们仍然依赖 CodeBLEU 进行整体评估。

蓝色

BLEU**【2】是最初引入来衡量机器翻译性能的指标,其中精确匹配未能考虑一个句子有多个有效翻译以及人工评估代价昂贵的事实。**

BLEU 度量的基础是基于精度测量的改进的 n 元语法精度(pn) ,直觉地认为如果参考标记在候选翻译中匹配,则认为该参考标记用尽。该指标的计算方法如下:

图片由作者提供。灵感来源于[2]。

BP 因子使长候选项(即ct【18】t【6】r)的分数保持不变,并惩罚短候选项(即 cr )并且直觉地认为较短的预测具有包含来自参考的所有单词的更大概率。

以下是我们示例的 BLEU 分数结果,其中 N=4, wn =1/N。

图片由作者提供。

如果我们依靠 BLEU 分数来选择最佳候选,我们将选择第三个,它是与参考共享最多标记的一个。但是逻辑上是错误的(a-b而不是a+b)。还有,你可以看到第一个候选人比第二个得分低,即使是变量名不同的同一个函数。

一个单词不会(可能)改变整个句子的翻译意义,因此蓝色是这项任务的一个很好的度量。然而,对于代码生成来说,未能捕捉到这样的问题使得 BLUE 对于文本到代码的生成来说是次优的。

CodeBLEU

CodeBLEU 通过包含编程语言的语法和语义特征来扩展 BLUE for code。该指标的计算方法如下:

图片由作者提供。灵感来源于[3]。

应用于每个术语的权重是总和为 1 的超参数。

****加权 N-Gram 匹配是 BLEU 分数的加权版本,其中关键字(即 for、int、public 等。)与其他单词相比具有更高的权重。此指标的计算类似于 BLEU,只是对修改后的 n-gram 精度稍作调整,如下所示,其中带红色下划线的术语表示不同标记的权重。在 CodeBLUE 论文[3]中,作者选择关键词权重是其他标记权重的五倍。

图片由作者提供。灵感来源于[3]。

****语法 AST 匹配是通过比较参考和候选语法子树来计算的准确性度量,同时忽略叶子,因为它们代表变量或函数名。子树是使用树-sitter 工具从抽象语法树(AST)中提取的[4]。下图比较了我们示例中的一个子树。

图片由作者提供。

语法 AST 匹配计算如下:

图片由作者提供。灵感来源于[3]。

该度量的主要目标是评估候选语法,因为子树的不匹配意味着存在语法错误(即,缺少标记、数据类型错误、错误的运算符等)。).

****语义数据流匹配是一种使用数据流来评估推荐人和候选人之间的语义差异的指标[5]。

数据流是一个图表,其中变量代表节点,它们的源代表边。下图是参考文献中第一个参数和我们示例中第二个候选参数的数据流图。

图片由作者提供。

如果我们将代码视为单词列表,则数据流中使用的索引(即a, 3)就是该特定变量的索引。例如,def的索引为 0,参考文献中return的索引为 10。

语义数据流匹配度量计算如下:

图片由作者提供。灵感来源于[3]。

现在我们对 CodeBLEU 指标有了一个高层次的理解,让我们看看它在我们的例子中是如何表现的。下表显示了每个候选人的 CodeBLEU 分数。

图片由作者提供。

从上面的结果可以看出,第三个候选人仍然是 CodeBLEU 的最佳候选人,分数甚至高于 BLUE。但是,第一个候选人的分数比第二个候选人的分数好,与蓝色分数相比有所提高。

尽管 CodeBLEU 通过增加语法和数据流信息,在正确的方向上衡量代码生成性能,但我们仍有改进的空间。首先,在这个例子中,我们仍然会选择错误的候选人。第二,CodeBLEU 仍然倾向于“格式化”与参考相同的预测,使得前两个候选之间的得分差异显著(89.30 对 60.51)。

功能正确性

即使从我们的简单示例中,也很容易看出基于匹配的度量,无论是精确的(准确性)还是模糊的(BLEU 或 CodeBLEU),对于代码相关的任务来说都是次优的(要进行更深入的研究,可以查看 Ren 等人的工作(2020) [3])。因此,最近的工作[6,7,4]转向功能正确性。

功能正确性背后的想法是,如果代码片段通过了一组精心策划的单元测试,就像开发人员验证他们的代码一样,那么它就是有效的。它通常与 pass@k metric [7]结合使用。模型的任务是为特定问题生成 k 个预测(或者在代码生成的情况下给出提示)。如果只有一个解决方案通过了单元测试,那么问题就被认为是解决了。

在我们的例子中使用单元测试,我们可以很容易地发现前两个候选项是有效的,而最后一个是无效的。然而,通过各自的单元测试收集足够大的一组问题,并确保这组问题代表整体的模型性能,这并不是一项简单的任务。希望随着该领域研究的进展,我们会有更多像 OpenAi 的 human-eval [9]这样的精选数据集。

结论

这篇文章概述并比较了用于评估代码生成模型的指标。我们已经看到,基于匹配的度量是次优的,要么过于严格(即准确性),要么不能完全捕捉代码的语法和语义属性(即 BLEU 和 CodeBLEU)。另一方面,功能正确性可以正确地测试代码,代价是为每个问题制定一组单元测试。

在你走之前

在 Twitter 上关注我,我经常在那里发关于软件开发和机器学习的微博。

参考

  • [1] CodeXGLUE 基准测试
  • [2] 帕皮尼,k;鲁科斯,s。t .沃德;朱,王伟杰,2002。BLEU:一种自动评估机器翻译的方法。《计算语言学协会第 40 届年会论文集》,311–318。
  • [3] 任,s,郭,d,陆,s,周,l,刘,s,唐,d,孙,n,周,m,Blanco,a,马,s .arXiv 预印本 arXiv:2009.10297,2020。
  • [4] 树保姆
  • [5] 郭,婷;任;鲁;冯;唐博士;刘;周;段;尹;姜博士;等人 2020。GraphCode-BERT:用数据流预先训练代码表示。arXiv 预印本 arXiv:2009.08366 。
  • [6] Lachaux,M.-A .,Rozi`ere,b .,Chanussot,l .,和 Lample,g .编程语言的无监督翻译。ArXiv,abs/2006.03511,2020
  • [7] Kulal,s .,Pasupat,p .,Chandra,k .,Lee,m .,Padon,o .,Aiken,a .,和 Liang,P. S .,Spoc:基于搜索的伪代码代码。h .沃勒赫、h .拉罗歇尔、a .贝格尔齐默、f .阿尔奇 e-Buc、f .福克斯和 r .加内特(编辑。),神经信息处理系统进展,第 32 卷。柯伦联合公司,2019。
  • [8] 陈唐山、杰里·特沃克、希佑俊、元、恩里克·庞德、贾里德·卡普兰、哈里·爱德华兹、尤拉·布尔达、尼古拉斯·约瑟夫、格雷格·布罗克曼等。评估基于代码训练的大型语言模型。arXiv 预印本 arXiv:2107.03374。
  • [9] OpenAI 人类评估。

微分编程的简明介绍

原文:https://towardsdatascience.com/a-gentle-introduction-to-differentiable-programming-a5f7f5781741?source=collection_archive---------15-----------------------

了解人工智能工具如何解决非人工智能问题

罗马法师在 Unsplash 上拍摄的照片

在一个高层次上,差异化编程是编写可以自动差异化的软件的范例。用简单的英语,我们可以快速检查程序输入的变化如何影响程序输出。例如,假设我们编写了一个自动微分汽车模拟器。使用这个系统,我们可以模拟一辆汽车,并研究如何改变它的一些变量(例如,轮胎半径,悬架等。)与其输出(例如,燃料消耗、噪音等)的变化有关。).

这种模拟器的美妙之处在于,它使我们能够不断改进我们的模拟汽车。例如,如果我们的目标是减少燃料消耗,我们可以迭代运行我们的模拟,寻找对其消耗有负面影响的变量,并尝试新的设置。同样,一个可区分的工厂模拟器可以调整其操作,以减少浪费或增加产量。

差异化不是一个新颖的概念。可区分编程的真正创新是自动完成,没有定制的例程或解算器。这个微小的变化包含了相当多的含义,因为通过差异化进行优化是一个强大但极其昂贵的框架。

迄今为止,利用导数解决问题一直是一个小众话题,只留给更倾向于数学的领域,如人工智能和运筹学。然而,有了自动微分,启动和运行这些系统所需的许多数学基础工作都被取消了,从而将这些方法展示给了更广泛的受众。

如果你仍然不确定这个概念是否与你相关,请考虑深度学习的进展量。有了自动微分,研究人员开始询问这些系统如何改进它们的网络,以在特定数据集上表现更好,这一过程被称为反向传播。通过这样做,该领域开始更容易地探索不同的网络和方法,最终取得了我们今天看到的巨大成功,如 GPT-3 和斯泰勒根模型。

在整篇文章中,我将详细阐述什么是微分以及它能实现什么,然后概述如何使用 TensorFlow 进行通用微分编程。最后,我们概述了它的一些局限性以及在哪些情况下不能使用它。

如果这个话题引起了你的兴趣,请继续阅读:)

可微分规划

让我们从什么是差异化编程的一些定义开始,从最字面的定义开始,到更有用的定义:

  • 一种可以自动区分程序的编码方式
  • 使用梯度下降解决问题的通用范例
  • 将深度学习推广到更广泛的一类问题

解开这些定义,三个概念脱颖而出:微分、梯度下降、深度学习。为了理解接下来的部分,我们需要更好地理解这些术语。

区别

想象你是一家成长中企业的老板。你有一个单一的工厂,你对它的生产力相当不满意。为了改善这种状况,你雇佣了一位顶尖的数学家,他仔细研究了工厂流水线,并得出结论:其生产率遵循以下公式:

F(x)= 此处添加疯狂数学

x 工厂中工作的团队数量和 F(x) 工厂产量,以生产的单位数千计。其余的呢?其余的你不知道。所以我们能做的就是尝试数值和询问数学家。

现在,有四个团队,所以,根据公式,你的工厂产量是 F(4) = 64 千单位。作为所有者并寻求提高工厂产量,你问数学家:我应该增加还是减少团队数量?

暂停一下这个例子,你刚才问了数学上人们称之为的函数导数:改变一个输入如何影响输出。在我们的案例中,改变团队数量如何影响工厂的整体生产。

如果导数为正,意味着增加团队数量会提高生产率。同样,负导数指出更少的团队会更有生产力。但是,请记住导数只告诉你正确的方向;它没有告诉你应该改变多少。

梯度下降优化的第一步。作者图片

从视觉上看,我们知道有四个团队,工厂生产 64,000 个单位,增加组的数量(向右移动),我们期望增加我们的产量。

回到例子,你不需要知道如何计算导数,因为我们可以自动计算。我说的自动,是指值得信赖的数学家,他会告诉你导数是正的。

梯度下降

因为我们知道我们需要往哪个方向发展(雇佣更多的团队),但是我们不知道我们需要增加多少个团队,我们做一个猜测:我们再雇佣三个团队。结果工厂用七个团队生产 F(7)= 9.1 万台,提高了 2.7 万台!

第二步。从 4 个团队到 7 个团队,我们的产量从 64,000 台增加到 91,000 台。作者图片

看到结果,你决定再次向数学家寻求更好的方向—— 答案是雇佣更多的团队。

有点贪心,你决定比上次更进一步,所以你雇佣了五个新团队,总共 12 个。回到公式,F(12) = 96。这次没有这样的改善,尤其是考虑到你现在有更多的薪水要付。

第三步。从 7 个团队到 12 个团队的巨大飞跃只是略微增加了产量:从 9.1 万台增加到 9.6 万台。这一次,导数换到了另一边(蓝色箭头)。指出我们现在需要减少(而不是增加)团队的数量。作者图片

你困惑地问数学家,你是否应该雇用更多的人。数学家这次摇摇头,告诉你裁员会提高生产率(开玩笑,她说的原话是“导数是负的”)。

伤心但贪婪的你决定放走两队,一共保留十队。使用公式,F(10) = 100。事实上,正如这位数学家所说,让一些人离开会让工厂不那么拥挤,从而提高生产率。

第四步也是最后一步优化。通过取消两个团队,你达到了最佳表现。你知道它是最优的,因为导数是 0。因此,你找到了最佳点。作者图片

此时,你不确定是否应该解散更多或更少的队伍,所以你再次询问数学家。令你惊讶的是,她说工厂现在很完美。导数为零。向任何方向发展都会损害生产力。

本节描述的过程是梯度下降(或上升),梯度只是导数的一个别称。在非正式的术语中,我们通过沿着梯度冲浪来优化一个函数。如果它说增加,你就增加。如果它让你减少,你就减少。

这个过程完美吗?不,不是的。从某种意义上说,这是一种数学直觉。它告诉你方向,但不是你要走多远。此外,你要从某个地方开始,根据你从哪里开始,你将不得不走或多或少,直到你找到那个甜蜜点。

深度学习

到目前为止,你可能会问,每个团队的成本是多少?我们优化的只是生产率,而不是利润。要做到这一点,我们需要在公式中加入其他相关元素,如工人的工资、单位成本、销售价格、产品需求等。

其中一些因素是季节性的,如工资和需求,每月略有不同。其他的随着生产力而变化。例如,你做的越多,通过大量购买材料你就能节省越多。相反,当你向市场投放更多的产品时,你的产品需求会向相反的方向下降。此外,获得的利润也要缴税,具体视总收入而定。

考虑到 t 为当前月份, u 为生产的单位数量, p 为您打算出售每个单位的价格, x 为团队数量,数学家得出您工厂的利润是所有这些项的组合,如公式所示:

利润(t,u,p,x) =税收(需求(t,p) ⋅ u⋅ (p 成本(u))—工资(x)

最后,为了得到一整年的感觉,数学家建议对所有十二个月的这个公式的值求和。

你的第一印象是你究竟会用这样一个方程做什么。但是,你很快回忆起你不必知道很多;只要你有一个梯度可以遵循,你就可以慢慢的但是稳步的优化这个公式,沿着梯度方向走。你唯一真正的任务是定义利润函数;系统会完成剩下的工作。

在人工智能中,神经网络只不过是简单函数的组合,这些简单函数产生一个复杂的整体。通过反复向它提供输入并查询方向来调整它的参数,我们将这个数字怪物从输出胡言乱语驯服为输出值。神经网络是复杂性理论的完美例子,随机森林也是如此。他们展示了复杂的行为是如何从简单、愚蠢的片段中产生的。

神经网络只不过是一堆神经元阵列。使用 NNSVG 创建

深度学习中的“深度”是这些系统基于冗长的功能组合,从它们的普通片段中产生复杂的“深度”行为。因此,这些系统对于简单的问题来说过于强大,但对于复杂的问题来说却足够强大。相反,肤浅的模型擅长更直接的任务;然而,它们的简单性是有限制的。

几十年来,深度模型从未流行过:(1)它们太重了,无法使用,以及(2)它们很难训练。虽然摩尔定律有望解决前者,但难以捉摸的后者仍然难以捉摸。

什么变了?基于梯度的技术。我们不使用直接且可解释的训练算法,而是迭代优化模型,以在关键指标上获得更高的分数。更好的是,如果我们设计算法来为我们计算梯度,我们可以疯狂地研究模型,而不太关心整体分析表达式的存在(或长度)。

在我们的例子中,一个真正的工厂模拟器将是一个复杂的元素组合,每个元素都有自己的一组变量来模拟真实事物的基本工作方式。虽然这种系统几乎不可能进行分析推理,但自动微分引擎给了我们难得的机会来理解这些复杂的野兽,并根据需要驾驭它们。

用一种更哲学的语气来说,虽然我们无法计算生活的梯度,但我们总是努力遵循我们认为会改善生活的方向。无论是攻读学位、转换领域,还是仅仅为了升职,我们都在一次又一次地猜测,希望能过上更幸福、更充实的生活。如果我们可以缩小视野,展望未来,我们会的,但是既然我们不能,我们最好的选择就是跟随我们所知道的本地最佳方向。某种意义上,对数学来说,梯度下降只是应用生活。

编码示例

在上面的例子中, F(x) 实际上是 *-x + 20x。*当时,我们进行了人工梯度下降。在这一节中,我们将讨论如何通过软件实现这一点,以及我们可以如何巧妙应对。代码如下:

优化函数的梯度上升的简单张量流实现

上面的 TensorFlow 代码定义了一个函数 x 和两个变量: x0 ,初始化为 TensorFlow 变量,以及步骤,这是我们将执行的优化遍数。第 8–14 行定义了实际的优化循环。对于每一步,我们使用一个 GradientTape 对象来记录 y = function(x0) 的梯度。换句话说,我们正在对当前 x0F(x) 的值进行采样,并将其保存到一个名为 y 的变量中。

真正的魔力在第 12–14 行中定义。首先,我们让磁带计算 x0 相对于 y 的梯度。换句话说,我们在问 x0 如何影响y——输入如何影响输出。然后,在第 14 行,我们沿着渐变的方向移动当前的猜测( x0 )。这一次,我们使用渐变值作为我们应该移动多少的预感。具体来说,我们移动四分之一的梯度。原因很简单:步长越大,超过优化的几率就越高。小步走总是比大步走更安全。最后,第 13 行打印了我们的进度。

运行该代码片段,我们得到:

步长[1/10]: F(7.00) = 64.00,梯度:12.00
步长[2/10]: F(8.50) = 91.00,梯度:6.00
步长[3/10]: F(9.25) = 97.75,梯度:3.00
步长[4/10]: F(9.62) = 99.44,梯度:1.50
步长[5/10

到了第八步,我们已经非常接近 10,可以将结果四舍五入到 100。用数学术语来说,我们在八步之内收敛。这种方法的最终目标是趋同——达到一个除此之外任何改进都微不足道的程度。

出于教育目的,我强烈建议您自己运行上面的代码片段,并使用 0.25 系数。我们称之为“学习率”或“步长”如果你把它设置得太大,比如 2,优化就会失败。从 4 会到 28,-44,172,-476,1468……如果你设置的太小,0.01,那就要走 100 多步才能到达 *x = 10。*在实践中使用这些技术时,调整学习率对于提高结果至关重要。

上面的例子是一个坡度上升的场景。我们希望最大化一个函数。更流行的算法是梯度下降,它寻求最小化一个函数。后者更受欢迎,因为它允许我们设定目标并不断向推进,以最小化到目标的距离。例如,当在机器学习中工作时,我们经常希望我们的模型的误差达到 0。因此,这是一个最小化公式。

下面的代码片段试图优化我们的函数,通过最小化 y 和 48 之间的距离来逼近任意值 48。

实现梯度下降以找到接近目标的函数值。

我们添加了函数接近目标,该函数计算 y 和我们的目标 t 之间的平方距离。在我们的优化过程中,我们现在计算 y,,然后我们计算 t 。此外, x0 的梯度现在是相对于 t 而言的。最后我们把学习率调整为 -0.001 。负号表示坡度下降中的下降。通过反复试验找到了 0.001 的值。大于该值的值不会在十步内收敛。

到目前为止,上面的代码片段足以解决许多需要您为函数找到一个好的输入值的任务。神经网络的训练仅仅是寻找最佳参数来匹配输入和输出。例如,考虑关于定制训练循环的官方指南。该函数成为神经网络, approach_target 被损失函数代替,并且 x0 更新变成(Adam)优化器。然后,整个过程被布置成在几个时期内对几个批次进行工作。

限制

作为实现梯度下降的可微分编程框架,它的用例与限制本质上是相同的。梯度下降的一般用例如下:

  1. 问题是数值的(即,它可以被定义为数学表达式)
  2. 它足够光滑(也就是说,你可以计算它的导数)
  3. 它太复杂了,你不能用更简单的方法解决它

这导致了可微编程的主要限制:它不能解决不能作为纯数值问题的问题。例如,如果你的问题充满了 if 语句,它就不再足够平滑,并且很可能无法通过梯度下降进行优化。另一方面,如果你的问题本质上是随机的,这些技术仍然可以使用,但是也要考虑梯度的随机性。

相反,如果你的问题完全是数值化的,但过于简单,就应该使用其他技术。在上面的例子中, F(x) 是 *-x + 20x。*我们使用梯度下降四次将其优化为 F(10) = 100 。但是,如果我们早一点知道解析表达式,我们会计算导数的解析形式(F’(x)=-2x+20),求它的根(x = 10),得到我们的答案。此外,这些方法不适合整数,因为它们不平滑。为此,您需要使用一些额外的技巧来生成整数解。

到目前为止,这些都是你根本不应该使用梯度下降的情况。然而,即使在你可以使用它的情况下,有一件事你也不应该忘记:你总是需要一个优化的起点。

如果从其他位置开始尝试几次梯度下降优化,您可能会收敛到不同的值。对于多变量的多维问题更是如此。不能保证你会达到最好的结果。唯一的保证是,你会在一个比你开始时更好的地方结束,或者,在最坏的情况下,留在你现在的地方。

你可以把它想象成在喜马拉雅山的一个随机的地方,能够看到你面前只有十英尺。这足以爬到你所在的山顶,但你永远不会知道你是在最高峰还是仅仅在局部最大值。正如下图所示。

本杰明·沃罗斯在 Unsplash 上拍摄的照片。作者编辑

他的一切都是为了现在。如果你对这篇文章有任何问题,请随时评论或与我联系。

如果你是中新,我强烈推荐订阅。对于数据和 IT 专业人员来说,中型文章是 StackOverflow 的完美组合,对于新手来说更是如此。注册时请考虑使用我的会员链接。

感谢阅读:)

维度缩减的简明介绍

原文:https://towardsdatascience.com/a-gentle-introduction-to-dimensionality-reduction-21b3fa63f1ca?source=collection_archive---------30-----------------------

采用探索性因子分析(EFA)和主成分分析(PCA)

作者拍摄的

正如探索性一词所暗示,探索性因素分析(EFA)是寻求理解变量之间关系的初步检验。当第一次接触数据时,研究人员可能对输入变量及其关系没有任何假设。研究者的出发点是了解模式和影响这种模式的因素,以确定变量之间的关系。本文的主题 EFA 是一种统计技术,旨在确定影响这些变量的潜在因素。

为了在这个主题上有所发展,我将下一步进入模型构建领域(我最喜欢的学科)来解释全民教育的基本原理。在建立模型时,收集给定数量的输入变量(自变量),这些变量被识别为与解释输出变量(也称为因变量)的变化相关。假设输入和输出变量之间存在某种关系,人们试图测量输出变量的变化量,这种变化量可以用一个保持所有其他常数的输入变量来解释,“其他条件不变”。由于“噪声”,如人们无法捕捉的未观察到的人类行为,缺乏时间和资源来收集影响输出变量的全部输入,因此引入了一个误差项,该误差项表示模型中未包括的任何其他内容。从数学上讲,这可以用以下公式表示:

在运行上述线性回归模型时,要检查的一件事是输入变量之间的关联程度(如果它们解释了因变量的变化,那么它们之间一定存在某种关系)。我们称之为“相关性分析”,旨在测量两个或多个输入变量之间的关联程度。因为太多的输入变量会导致模型过于完美,所以需要确保模型中只包含相关变量,即解释因变量大部分变化的变量。如果过度拟合,模型会变得过于特定于它所训练的数据,因为它会记住正在建模的关系。不够一般化,当模型在看不见的数据上运行时,它就不能理解现在存在于数据中的“更新”关系,从而导致不可靠的预测。因此,太多的变量是不好的,但人们希望确保相关的变量被包括在内。这就是全民教育发挥作用的地方。在机器学习术语中,诸如 EFA、主成分分析(PCA)等技术属于无监督学习方法的范畴,后者是一种仅分析输入变量以从数据中学习并理解输入之间的模式以及识别具有重大影响的因素的技术。

现在,让我们退一步,问一问:我们能不能确定一个给定数量的对多个输入变量有着共同重要影响的“因素”?相关输入之间的一些变化可以用“共同因素”来解释吗?如果是这种情况,是否有可能将“独特因素”变异类型确定为影响单个输入变量?在这种相互关联的关系中,是否也存在一种无法测量的遗传误差比例?在 EFA 中,输入变量是以下各项的函数:公共因子、独特因子和误差项。从技术上讲,这是这样写的:

在这里,共同的变化是我们感兴趣的!EFA 关注影响多个变量的共同因素,因此可以使用这些因素来减少进入模型的变量数量,从而降低其复杂性,而不损害其预测能力。

探索性因素分析中的一个关键概念是关于潜在变量的概念。从变量的形式来看,这些是未被观察到的但有影响的因素,它们解释了输入变量中常见的很大一部分变化。在本文中,我将交替使用“潜在变量”和“因素”的定义。在最顶层,潜在变量是从原始输入变量的组合中产生的。因此,生成的因子的数量等于原始变量的数量。根据每个原始投入所占的方差比例,产生一个加权值,称为因素负荷。这些很重要!因素负荷是范围从-1 到 1 的系数,测量给定因素对给定输入的相关性和影响大小。主要目的是确定潜在变量的数量,其中包含在原始输入变量中的最大量的信息被保留,如通过因子加载所测量的。

好的,那么如何计算因子载荷呢?好了,是时候再退一步,深入了解更多细节了。在 EFA 中,生成的潜在变量(新变量)的数量等于原始输入变量的数量。在此基础上,一个众所周知的变量之间的相关矩阵和相关矩阵的特征向量和特征值被导出。成对地,对于每个潜在变量,生成特征向量和特征值。前者,每个特征值的权重向量指示一个方向,而后者测量由该方向上的给定潜在变量解释的方差的量。取特征值的平方根(潜在变量的标准偏差)是奇异值分解,当乘以特征向量时产生因子载荷。因此,上面的转换为跨因子的每个原始输入变量生成一个因子加载。这很重要!因子载荷反映了每个原始输入变量与潜在变量的相关程度。具有最高特征值的特征向量是第一个潜在变量。因此,按照降序,第一个潜在变量是最重要的潜在变量,占数据中大部分(普通)方差,其次是第二个,依此类推。

随着额外的潜在变量的增加,数据中较少的差异得到解释。传统上,加载值大于 0.3 的因子表示相关变量。如同在相关性中一样,“高”相关值在定义时是一个主观点。现在,负载系数大于 1 的情况时有发生。这些被视为虚假结果的指示,由所谓的不当解决方案引起,是输出有问题的迹象。

接下来,我将通过实现 EFA 和主成分分析(PCA)来说明以上内容。这样做,我的目的是强调这些统计技术在揭示和描述数据集的底层结构方面的力量。在比较这两者时,本文只讨论关键的相同点和不同点,主要是为了根据手头的分析来强调这两者的用途。

本文使用了来自一家私人公司的 98,586 个观察值和总共 22 个输入变量的数据集。由于保密原因,数据来源不能透露。然而,使用类似的变量类型可以很容易地再现该分析。通过对上述数据集进行 EFA 和 PCA,我的目标是在实施降维技术时建立一种合理的方法,而不是关注输出本身。

分析由三个阶段组成:第一阶段:根据人口数据的 14 个变量分析第一次产出。在第一组输入变量中,发现了相对较少数量的缺失值,因此提供了相对较大的数据集作为分析的基础。第二阶段:在初步结果的基础上,对输入处理提出了一些调整。与第一阶段相反,输入变量现在被标准化为标度变量,因此它们的范围都在(0–1)之间。第三阶段:在市场营销、消费者行为数据(即消费、沟通渠道)的基础上纳入额外变量,共 22 个变量进行分析。添加这些是为了说明模型设定错误,因为后者已被确定为可能导致不当解决方案的相关元素。但是,添加的第二组输入变量中存在大量缺失值,导致运行分析的数据集较小。

第一阶段:初步结果

可变治疗:

某些输入变量缺少值的条目被删除,因为这些条目仅占总数据点的 0.03%,从而将观察值的数量从 98,594 减少到 98,586。

由于异常值的存在,使用四分位间距方法,Q3+(1.5IQR)以上的数据点被封顶,因此移除了位于盒图上超出上须的值。*

主要结果:

海伍德案件侦破。海伍德案例是方差被限制为负值或接近零值的因子载荷的结果。正如我们在上面看到的,因素分析是基于被分析变量的相关矩阵。在海伍德的情况下,一组变量的任何线性组合都会产生一个不正定的协方差矩阵。从技术上讲,由于方差是标准偏差的平方根输出,因此它不能为负,因为这表示有偏差/不可靠的因子加载估计。

这就是所谓的不适当的解决方案,其中估计值(因子负载)被限制在一个定义的范围内,使得潜在的问题(负方差)未被修正,从而允许无效的估计值。在这里,由于潜在变量的解释明显不符合分析的目的,因此因素负荷是有偏差的,因此需要纠正。在这种情况下,软件产生以下信息(或类似信息):“潜在变量的协方差矩阵不是正定的”。在这里,输入变量领域的研究人员可以选择使用协方差表会话运行模型摘要,以检查高度相关的变量,并相应地整理/移除它们。

纠正海伍德案例需要考虑的因素主要有:1)变量中的高值,2)模型设定错误,3)样本大小。由于我使用的是相对较大的数据集,并且假设变量没有定义域,我将首先尝试不同的输入模型来说明模型的错误设定,即添加第一个和(可能)删除后面的输入变量,因为我的兴趣是识别潜在变量,突出所有可用变量中最重要的输入变量。在此之前,我将首先归一化输入变量,以确保它们都在相同的值范围内,从而消除较高值导致有偏差结果的可能性。

基于以上所述,进行第一次整改。

第二阶段:变量规范化。

可变治疗:

在这个阶段,变量被规范化,保持其他一切不变。使用最小-最大缩放变量进行缩放,数据点固定在 0-1 范围内。上述目的是通过防止偏向具有较大值的特征从而促进最小梯度下降和收敛来提高算法的性能。

主要结果:

海伍德案件仍然存在。根据型号规格进行进一步调整。

第三阶段:正常化模型规格。

可变治疗:

在第三个也是最后一个阶段,增加了更多的变量。这组输入变量被发现有更大比例的缺失值。在这个阶段,我将删除最近添加的一组变量中缺少值的条目,使观察总数减少到 65,605。具有缺失值的变量的中值填充是常见的做法,以便保留原始的观察值。在本文中,考虑到在此之后留下的相对较大的数据集,我将删除丢失的值,因此我确保分析符合其目的,并且没有其他偏见元素被带入分析。

因为机器学习技术被证明为具有较高值域的特征分配较高的权重,所以变量保持归一化,如在先前阶段执行的那样。

主要结果:

没有找到不合适的解决方案。

最终输出是什么样的?这是如何解读的?根据所使用的包,EFA 输出中会显示不同的信息和指标。然而,从最终产出的分析来看,就相关因素而言,仍应得出相同的结论。在不同的软件包中,我使用来自 R 的 factanal 函数,它是基于最大似然估计(MLE)的。尽管最大似然估计可能更为人所知,但其他软件包有一个参数,可以用来建立一个给定的估计量。读者应该熟悉其他软件包执行的不同评估方法,这超出了本文的范围。

下面一行代码显示了 factanal 函数的主要参数:

根据 factanal 函数,定义了以下参数:数据框架、因子数量(怀疑相关)和因子轮换方法。简而言之,对于给定数量的因子,对于所有涉及的变量,不存在唯一的因子加载矩阵解,而是存在无限数量的(因子加载)解。在二维空间中,出于可视化和解释的目的,两个因子可以被认为是两个轴*,其中因子负载作为代表来自每个原始输入变量的方差贡献的值。这些轴的旋转方法(基于方差方向的最佳拟合)被称为正交因子、垂直因子(轴)的“varimax ”,即假设因子之间没有相关性。从希腊单词“直立角”的意思来看,这仅仅意味着坐标空间中的轴是垂直的,因此没有关联。我再次建议读者熟悉不同的旋转方法。*

运行 factanal 函数的输出如下所示。让我们提醒自己,一个是选择潜在变量的数量,这些潜在变量在原始输入变量中捕捉到最大数量的(公共)方差。因此,EFA 旨在确定一个少于原始变量总数的最佳潜在变量数。以下输出基于 3 因素解决方案。可以看出,唯一性度量是首先提供的。范围从 0 到 1。基于每个原始变量的独特方差类型贡献,解释该度量的一种方式是获得所谓的公度性度量。公度是一组原始输入变量共有的方差的比例,由变量 ith 贡献,并由所选因子的数量(在本例中为 3)说明。这通过从唯一性值中减去 1 来计算。

是的,唯一性值越低,给定输入变量的方差贡献越大。例如,从下面的输出来看,教育对原始变量中常见的数据总方差有 92%的显著贡献,即(1–0.079 = 0.921)。事实上,该变量的因子负荷接近 1,即 0.949,因此与因子 1 高度相关,因子 1 之后是管理角色变量。让我们再一次提醒自己,因子载荷代表了一个给定变量相对于一个给定因子的相关性。它的范围从-1 到 1。正如我们在整个分析过程中所讨论的,以及我们将在接下来演示的,因子加载用于理解一个因子中具有最高公共方差贡献的变量。重要的是,这有助于描述和标记潜在/未观察到的变量/因素。

那么,“高”方差是如何定义的?这是全民教育中隐含的主观性的一部分。因此,这应该主要由研究者的解释和它如何适合手头的分析来确定。好的,给定这个,如何解释载荷,从而描述和定义它们?

**

从输出中,突出显示了因子负载大于 0.3 的变量。该值越高,给定输入变量相对于因子的相关性越高。

可以看出,因素 1 将教育水平、社会地位、房价、收入、需求、管理角色职位等变量确定为相关变量,它们都与个人的社会经济水平的潜在变量相关联。

因素 2 确定了与社会保障转移、社会福利、津贴指标的潜在变量相关的变量,如租房、从未工作或失业状态、面积密度、房子里的人数、福利状态。

最后,因素 3 确定了一些变量,如一个家庭的人数、房子大小、来自其他资产的收入,作为衡量生活水平、购买力的潜在变量*。***

输出的最后部分显示 SS 加载、比例方差、累积方差和假设检验。SS 载荷是载荷的平方和,如果该值大于 1,则表明该因子具有显著的解释力。如黄色单元格所示,这些值的减少是后续因素,解释了数据中较少的变化。值高于此阈值的因子将被保留。累积方差告诉我们,当考虑到前三个因素时,数据中 46%的常见方差得到了解释。这可能看起来很低,表明需要包括更多因素,在这种情况下超过 3 个。同样,这是基于手头的分析。可以运行具有不同数量的因素以及不同数量的原始输入变量的测试,以验证最后一部分如何基于后续调整而变化。

可以绘制特征值图来形象化上述内容。在 R 中,Dino 的“paran”包可用于运行平行分析 Scree 图。如图所示,特征值标绘在 y 轴上(由因子解释的总公共方差),因子数标绘在 x 轴上。正如我们所了解的,特征值代表由给定因子解释的所有原始变量的总公度方差。因此,我们希望选择值大于 1 的因子数。这被用作因子选择的标准,其中 1 个因子预期比 1 个原始变量解释更多的方差。正如预期的那样,可以看到因子 1 占最大方差,如蓝色曲线所示,从第一个因子到最后一个因子,方差逐渐减小。事实上,三因素解决方案显示并验证了足够数量的因素可用于此分析。

两者都是用于降维的统计技术,为了便于比较,本文额外提到了主成分分析(PCA ),从而突出了与 EFA 相比的关键差异。

主成分相当于一个潜在变量,是观察到的原始变量的线性加权组合。考虑坐标空间中的轴,PCA 最小化数据相对于主分量(轴)的平方距离的和。提取的成分数量旨在保留观察到的由原始变量引起的大部分方差。在这里,与 EFA 类似,高度相关的变量(因此对数据中的大部分方差有贡献)在第一个分量中表示,这是解释数据中的大部分方差的分量,随后是第二个分量,随后随着分量的增加,解释的方差越来越小。对于 PCA,原始变量的总数被变换到具有相等数量的新变量(主分量)的新坐标系中。根据线性变换,生成特征值和特征向量。代表由每个变量贡献的方差量的特征值,以及作为代表最适合数据的方差方向的权重的特征向量,迭代地导出本质上正交的分量。主成分和 EFA 共享相似的假设,当如上所述处理原始输入变量时,在本分析中已经考虑了这些假设。对这些的详细描述超出了本文的范围,但这是研究人员需要进一步研究的主题。

在 R 的不同包中,princomp()函数用于执行 PCA,输出如下所示。

其中参数“cor”在函数中定义了使用相关矩阵来导出主分量。

可以看到,已经创建了 22 个组件,这等于原始变量的数量。在这里,我们将“方差比例”理解为由给定成分解释的最大方差。“累积比例”将提供一个累计的最大变化量,该变化量可解释给定数量的成分。对于比较例 3,这是 51%。事实上,通过提取所有 22 个成分,我们将考虑数据中的所有变化,这就是“累积比例”等于 1 的地方。然而,我们已经知道 PCA 的目标是一个少于原始变量数的最优分量数。现在,这很重要:在这里,最大值这个词在理解 PCA 和 EFA 之间的关键区别时变得很重要。简单地说,主成分分析报告了由成分解释的观察到的原始输入变量的最大变化量。相比之下,全民教育报告的是由于无法测量和/或观察的因素而在给定数量的原始输入变量中常见的变化总量。因此,PCA 坚持共同方差等于总方差的强假设。这是,

因此,使用哪一个主要基于手头的分析。

下表显示了五氯苯甲醚在碎石图上的特征值。y 轴标绘方差,x 轴标绘分量数,这清楚地显示了随着更多主分量的加入,方差被解释为下降的速率。

结论

本文使用了一个大小为 98,594 x 22 的数据集。它执行 EFA 和 PCA 作为统计技术来降低数据集中的维数。

在 EFA 下,潜在变量的数量代表由于未观察到的因素而出现在数据中的共同方差。

在 PCA 下,提取的成分的数量解释了来自观察到的原始变量的数据中存在的最大方差。

这两种技术都旨在获得一个最优解,其中组件的数量或因子的数量少于原始输入变量的数量。

出于演示目的,本文使用相同的数据集来运行这两种技术。然而,基于手头的分析,研究人员应该致力于确定最合适的技术来使用。

对 MLOps 的简单介绍

原文:https://towardsdatascience.com/a-gentle-introduction-to-mlops-7d64a3e890ff?source=collection_archive---------1-----------------------

作者图片

行业笔记

机器学习操作世界指南。

你好。

你想了解 MLOps,你来对地方了。

在本文中,我们将了解什么是 MLOps 或机器学习操作。我将试图简化 ML 操作及其相关基础设施的庞大而有趣的世界。这篇文章是写给那些想了解 ML 模型如何被部署到产品中,阶段,过程,以及它所涉及的问题的人的。😅

让我们开始吧!

**来源:**G【iphy.com】T4

什么是 MLOps?

机器学习操作涉及一组过程,或者更确切地说,涉及为将 ML 模型部署到生产环境而实施的一系列步骤。在 ML 模型生产就绪之前,需要采取几个步骤。这些流程确保您的模型可以针对大量用户进行扩展,并准确执行。💯

我们为什么需要 MLOps?

创建一个 ML 模型来预测你想让它从你输入的数据中预测什么是很容易的。然而,创建一个可靠、快速、准确并能被大量用户使用的 ML 模型是困难的。

MLOps 的必要性可总结如下:

  • ML 模型 依赖于海量数据 ,单人难以掌握。
  • 难以掌握的参数 我们在 ML 模型中进行了微调。微小的变化会导致结果的巨大差异。
  • 我们必须跟踪模型使用的特征,特征工程是一项独立的任务,对模型的准确性有很大的贡献。
  • 监控一个 ML 模型不同于监控一个已部署的软件或 web 应用程序。
  • 调试一个 ML 模型是一门极其复杂的艺术
  • 模型依赖真实数据进行预测,随着真实数据的变化,模型也要随之变化。这意味着我们必须跟踪新的数据变化,并确保模型进行相应的学习。

还记得软件工程师使用的老借口吗😅我们想避免它。

软件工程师 vs ML 工程师(图片由作者提供)

DevOps 与 MLOps

你一定听说过老好人德沃普斯。构建和部署软件应用程序的过程。您可能想知道 MLOps 有何不同。

DevOps 循环(图片由作者提供)

DevOps 阶段的目标是开发软件应用程序。您计划要发布的应用程序的功能、编写代码、构建代码、测试代码、创建发布计划并部署它。您监控部署应用程序的基础设施。如此循环,直到应用程序完全构建完成。

在 MLOps 中,事情是不同的。我们实施以下阶段

ML 项目生命周期(图片由作者提供)

范围界定— 我们定义项目, 检查问题是否需要机器学习来解决 。执行需求工程,检查相关数据是否可用。验证数据是否无偏见,是否反映了真实世界的用例。

数据工程— 该阶段包括收集数据、建立基线、清理数据、格式化数据、标记和组织数据。

建模— 现在我们来到编码部分,在这里我们创建 ML 模型。我们用处理过的数据训练模型。执行误差分析,定义误差测量,并跟踪模型性能。

部署— 在这里,我们打包模型,根据需要将其部署在云中或边缘设备上。打包可以是——用暴露 REST 或 gRPC 端点的 API 服务器、部署在云基础设施上的 docker 容器、部署在无服务器云平台上的模型包装,或者基于边缘的模型的移动应用。

监控— 部署完成后,我们依赖监控基础设施来帮助我们维护和更新模型。这一阶段有以下组成部分:

  1. 监控我们部署的基础架构的负载、使用、存储和运行状况。这告诉我们 ML 模型部署的环境。
  2. 监控模型的性能、准确性、损耗、偏差和数据漂移。这告诉我们模型是否如预期的那样执行,对于真实世界的场景是否有效。

有时会有一个反馈循环,因为一些模型可能需要从用户输入和预测中学习。这个生命周期对于大多数 ML 用例都是有效的。

了解了 ML 项目的基本生命周期之后,让我们看看 ML 方面的基础设施场景是怎样的。

ML 生产基础设施

ML 基础设施(图片由作者提供)

现在,我们了解了将模型部署到生产环境中所需的基础设施设置。上图可以看到,ML 代码只是其中的一小部分。让我们一个一个的去了解组件。

数据收集— 该步骤包括从各种来源收集数据。ML 模型需要大量的数据来学习。数据收集包括整合与问题相关的各种原始数据。也就是说,图像分类可能需要你收集所有可用的图像或者在网上搜寻图像。语音识别可能需要您收集大量的音频样本。

数据验证— 在这一步中,我们检查数据的有效性,收集的数据是否是最新的、可靠的、反映真实世界的,是否采用适当的可消费格式,数据的结构是否正确。

特征提取— 这里,我们为模型选择最佳特征进行预测。换句话说,您的模型可能不需要完整的所有数据来发现模式,一些列或部分数据可能根本不会被使用。当删除几列时,一些模型表现良好。我们通常根据重要性对特征进行排序,高重要性的特征被包括在内,低重要性或接近零重要性的特征被丢弃。

配置— 这一步包括建立通信协议、系统集成,以及管道中的各种组件如何相互通信。您希望您的数据管道连接到数据库,您希望您的 ML 模型以适当的访问权限连接到数据库,您的模型以某种方式公开预测端点,您的模型输入以某种方式格式化。系统所需的所有必要配置都需要正确完成并记录在案。

ML 代码— 现在我们来看实际的编码部分。在这个阶段,我们开发一个基础模型,它可以从数据中学习并进行预测。有大量的 ML 库支持多种语言。例如:tensorflow,pytorch,scikit-learn,keras,fast-ai 等等。一旦我们有了一个模型,我们就开始通过调整超参数来改善它的性能,测试不同的学习方法,直到我们满意地认为这个模型比它的前一个版本表现得相对更好。

机器资源管理— 这一步包括 ML 模型的资源规划。通常,ML 模型在 CPU、内存和存储方面需要大量资源。深度学习模型依赖 GPU 和 TPU 进行计算。训练 ML 模型涉及时间和金钱方面的成本。较慢的 CPU 需要更多的时间,功能强大的 CPU 价格更高。模型越大,您需要投资的存储空间就越大。

分析工具— 一旦您的模型准备就绪,您如何知道该模型的性能是否符合标准。我们在这个阶段决定模型分析。我们如何计算损失,我们应该使用什么样的误差度量,我们如何检查模型是否漂移,预测结果是否正确,模型是否过拟合或欠拟合?通常,我们用来实现模型的库附带了分析工具包和误差度量。

项目管理工具— 跟踪一个 ML 项目非常重要。在处理庞大的数据、特性、ML 代码、资源管理时,很容易迷失和混乱。幸运的是,互联网上有很多项目管理工具可以帮助我们。

服务基础设施— 一旦模型被开发、测试并准备就绪,我们需要将它部署到用户可以访问的地方。大多数模型部署在云上。像 AWS、GCP 和 Azure 这样的公共云提供商甚至有特定的 ML 相关特性来简化模型的部署。根据预算,您可以选择适合您需求的提供商。

如果我们正在处理基于边缘的模型,我们需要决定如何使用 ML 模型,它可以是用于图像识别、语音识别等用例的移动应用程序。我们还可以为某些用例定制芯片和处理器,比如特斯拉的自动驾驶。在这里,我们必须考虑有多少计算能力可用,以及我们的模型有多大。

监控— 我们需要实现一个监控系统来观察我们部署的模型和运行它的系统。收集模型日志、用户访问日志和预测日志将有助于维护模型。有几种监控解决方案,如 greylog、elasticstack 和 fluentd。云提供商通常会提供他们自己的监控系统。

走一遍

现在我们已经了解了 ML 项目生命周期是如何工作的,ML 产品中的基础设施场景是怎样的。我们将学习如何在生产中部署 ML 模型。

为了理解这一点,我们来看看 Jen 和她对 ML 引擎的追求。

珍有一大片南瓜地,每年她都把南瓜卖给市民和当地的南瓜香料拿铁工厂。由于她每年都有巨大的需求,她看着每个南瓜并检查它是好是坏变得很乏味。

所以她找你来帮助她开发一个 ML 引擎,帮助她预测一个给定的南瓜是好是坏。

珍需要有人帮她分类南瓜。(图片由作者提供)

这是一个简单的分类问题。

让我们讨论一下解决这个问题的方法。

  1. 首先,我们收集所有关于 Jen 的南瓜的信息。所有好南瓜,好南瓜,坏南瓜的照片。然后我们请好心的市民寄来他们从珍那里买的南瓜的照片。(有发西瓜图的!😑他们真可耻😒)这一步是 EDA +编译数据集。
  2. 既然我们已经收集了很多图片。是时候给这些照片贴标签了,在 Jen 的帮助下,我们给几百张照片贴了标签。我们检查图片的分辨率,设置标准分辨率,丢弃低质量的图像,格式化图像对比度和亮度以获得更好的可读性。这一步是数据准备
  3. 现在我们训练一个张量流模型来对图像进行分类。假设我们使用带有 ReLU 激活的顺序神经网络。我们定义 1 个输入层,2 个隐藏层,1 个输出层,只是一个基本的卷积神经网络。我们将图像数据集分成训练集和测试集,将训练数据作为输入提供给卷积网。模型被训练。这一步是模特训练。
  4. 一旦模型被训练,我们通过使用测试数据集来评估模型。基于预测,我们比较结果并检查预测的准确性。这一步是模型评估。
  5. 我们调整模型的超参数,以增加准确性,重新训练模型,并再次评估它。这个迭代一直进行到我们对 Jen 的模型满意为止。这一步是模型分析。
  6. 现在我们有了工作模型,我们部署它,以便 Jen 可以在日常工作中使用这个 ML 引擎。我们在云中创建一个带有预测 API 的服务器,创建一个应用程序或网站,她可以在那里上传图像并实时获得结果。这是模型部署。

从数据准备到部署,所有工作我们都是手动完成的。**恭喜你!这个 MLOps 的过程叫做 Level-0。**我们已经实现了我们的部署,但是所有的事情都是手动完成的。可以参考下图。

MLOps 级(图片由作者提供)

珍很开心。😄

分类(按作者分类的图片)

现在需求已经开始上升。您不能每天手动训练模型。因此,您创建了一个自动化管道来验证数据、准备数据并训练模型。您还试图通过比较多个误差度量来获取最佳可用模型。管道会处理好一切。**该流程是 MLOps 的一级。**这里模型的训练和分析是自动进行的。您只需检查是否有适当的数据可用,并确保没有倾斜的数据集,以便模型得到适当的训练。

MLOps Level-1(图片由作者提供)

大多数公司都达到了这一水平。这也可以由单个数据科学家或 ML 工程师实现。当您在开发环境中测试模型时,这已经足够好了。

现在让我们问自己几个问题。

  • 你的模型能够用不同种类的南瓜复制这个结果吗?
  • 当新数据添加到数据集中时,您的模型能够重新训练吗?
  • 你的模型能同时被几十万人使用吗?缩放良好?
  • 当您在一个大区域甚至全球范围内部署模型时,您如何跟踪它们呢?

这将我们引向级别 2。

是时候看大图了……

MLOps Level-2(图片由作者提供)

我们来分解一下流程。

  • 我们所做的一切 红线上方的 ,在流程图中——就是Level——1
  • 这整个 精心安排的实验 现在是 自动化 ML 流水线的一部分。
  • 我们引入了一个 特征库 ,它从各种来源提取数据,并将数据转换成模型所需的特征。ML 管道批量使用来自存储的数据。
  • ML 管道连接到一个 元数据存储器 。可以把它想象成簿记,因为您不需要手动训练模型——这个存储有管道中每个阶段的记录。一个阶段完成后,下一个阶段查找记录列表,找到前一个阶段的记录,从那里开始。
  • 然后将模型存储在 模型注册表中。 我们这里存储了一堆各种精度的模型。基于需求,适当的模型然后被发送到 CI/CD 管道 ,该管道将其部署为 预测服务。 授权用户能够在需要时访问预测服务。
  • 对该系统的性能进行监控。假设你有一批新的转基因南瓜。你的模特没有意识到这一点。这是一个新的数据集,被错误分类的概率很高。 这种性能下降会触发一个触发器,导致模型根据新数据进行重新训练。
  • 这种循环继续下去。

当性能下降或有新数据可用时,我们会重新训练模型。保持模型是最新的,以便真实世界的变化反映在模型中。 例句:你的模型不应该推荐盒式磁带,当世界已经转向数字流媒体的时候。

如果你不知道卡带是什么,就去谷歌一下😆

从这里去哪里?

唷!那真是一次冒险。拍拍自己的背😄

我们讨论了什么是 MLOps?你为什么要用它?生产基础架构设置会是什么样的?而且,一旦您有了基础结构,您将如何实施它—过程。

您可以从创建简单的模型和自动化步骤开始。记住,这是一个反复的过程,需要时间来做好。

一定要检查一些 MLOps 工具,如 MLFlow 、谢顿核心、 Metaflow、和 Kubeflow 管道。

感谢您的阅读!注意安全!再见!👋 😄

莫斐斯是对的!(来源:Giphy.com)

学分:

从 DeepLearning.ai 的 MLOps Specialization 获得的参考和概念很优秀的课程,可以去看看。

本文中使用的所有图片,除了 gif,都是我创作的。我用 Krita 画画。

自然语言处理中的迁移学习

原文:https://towardsdatascience.com/a-gentle-introduction-to-transfer-learning-in-nlp-b71e87241d66?source=collection_archive---------12-----------------------

迁移学习是 NLP 中最热门的话题之一——了解它是什么,以及如何将它应用到您自己的项目中

照片由 Pexels 的 Lisa Fotios 拍摄

本文简要概述了什么是自然语言处理(NLP)中的迁移学习,以及为什么它是自切片面包以来最伟大的事情。如果你想体验一下玩这些令人敬畏的预训练模型是什么感觉,请在这里和文章末尾查看代码的链接;几乎没有编程经验的人也可以在 NLP 中做一些真正高级的事情。

作为本文的简短入门,自然语言处理(NLP)指的是使用机器学习来处理自然文本,其中“自然”指的是我们在书籍和报纸中找到的那种文本,而不是例如计算机编程代码(好的,一些模型是学习编码,但我们将在这里更一般地谈论“自然”语言)。这项技术正在推动令人惊叹的事情,从自动文章摘要,到反应灵敏的聊天机器人,甚至创造性的写作生成。

训练计算机视觉或自然语言模型可能会很昂贵。它需要大量的数据,在计算机视觉的情况下,这些数据需要由人类来标记,并且需要花费大量时间在昂贵的硬件上进行训练。开放人工智能(Open AI)于 2019 年发布的基准设定语言模型 GPT-2,估计已花费约 160 万美元训练。有了这样的成本,任何人将如何与开放人工智能或任何其他在 NLP 领域进行研究的组织竞争?

好消息是,你不需要和他们竞争。你可以在网上免费下载这些模型,在巨大的数据集上进行了预先训练,随时可以使用。更好的是,您可以非常快速地微调这些模型,以处理特定数据集的细微差别。为了让你对微调和训练模型之间的区别有所了解,微调就像把你的车拿到机械师那里换一个新的火花塞,而训练/预训练就像换一个全新的引擎。举一个具体的例子,假设你经营一家大型食品配送公司,你想了解人们对你公司的推文的情绪(积极或消极的意图),这样你就可以迅速处理消极的情绪。考虑到你有足够多的推文输入到模型中,你可以在很短的时间内对其进行微调,以预测推文是正面还是负面的。例如,我最近为我的硕士研究微调了一个关于食品评论的分类模型,只用了大约 40 分钟的训练就达到了 98%的分类准确率(预测评论是正面还是负面)。如果您要从头开始训练一个模型,可能需要几个小时或更长时间,需要您自己收集和预处理大量数据,并且不如微调后的模型准确。多么大的胜利!

这一过程是所谓的迁移学习的本质,即采用一个被训练来完成一项任务的模型(正如这些预训练模型被训练来做的那样),然后对其进行微调,以完成一项相关但不同的任务。它在计算机视觉中的应用刚刚超过五年,并且已经在 NLP 中掀起波澜大约三年了。为了更好地理解它是如何工作的,我们需要短暂地转向对语言模型如何工作的高级理解。

作者图片

在将单词转换成机器学习模型可以理解的数字形式后,这些被输入到模型的主要部分,该部分(最常见的)是一个深度的多层神经网络。目前最流行的语言模型 Transformer 有一个结构,在这个结构中,它们在句子中的每个单词之间建立了一套非常深刻的关系,创建了一个句子的非常丰富的(数字)表示。这种丰富的表示然后被输入到模型的最后一层,给定句子的一部分,该层被训练来预测下一个单词。它通过给出预测下一个单词是什么的置信度来做到这一点。在上图的例子中,模型非常确定下一个单词是“oceans”。

NLP 中迁移学习的一个迷人之处是,研究人员发现,当你训练一个模型来预测下一个单词时,你可以拿起训练好的模型,砍掉预测下一个单词的层,放上一个新层,只训练最后一层——非常快——来预测句子的情绪。现在,记住这是而不是模型被训练去做的事情;它被训练来预测句子中的下一个单词;然而,当它处理并转换成丰富的表示时,它似乎正在捕捉句子中的大量相关信息,这些信息被输入到最后一层以预测下一个单词。但你可以做的不仅仅是微调模型来预测句子的情绪。你可以将模型微调为一个对话式人工智能,非常快(这些家伙在两个小时内完成了),或者你可以将模型微调为接受问题并相对快速地从一段文本中提取答案。

所以你为什么要在乎呢?以前,语言模型通常是为非常具体的任务而训练的,并且经常是从零开始训练,相信我,从零开始训练语言模型可能是一件痛苦的事情!作为一名研究人员,这意味着你可以通过下载这些预训练的模型并根据你的用例调整它们来快速测试新的想法。例如,我正在为我的硕士论文做一些无监督风格转移的工作,能够使用预先训练的模型完成这些任务意味着我可以非常快地重复想法。作为一名从业者,这意味着你可以相对容易地开始将 NLP 整合到你的产品或服务中;你需要更少的数据来微调模型,你可以开始利用尖端技术原型化新的想法,你可以开始真正地发挥创造力。不过有一点需要注意:让一个 250MB 到 1.5GB 的模型足够快地运行以满足客户的需求无疑是云工程的一项重大成就。

HuggingFace 的团队做了令人难以置信的工作,使得这些模型非常容易使用。你基本上只需要几行代码就可以加载这些预先训练好的模型,并开始尝试各种可能性。如果你想检查一下,我已经把 这个在线笔记本 放在一起,你可以玩玩。你几乎不需要任何编程经验——只需阅读说明——并开始想象如何将这一惊人的技术融入到你的产品或服务中。作为参考,如果你不熟悉 python,笔记本基本上是运行 python 脚本的一种简单方式。

对于一些轻松的事情,到这里来玩一下本文中提到的 GPT-2 模型。清除文本,键入一两句你自己的话,按 tab 键,看看人工智能会给出什么。摆弄这个让我思考人类与 AI 艺术交互的未来会是什么样子。

如果你想更深入地了解迁移学习的技术概况,请查阅这篇令人难以置信的文章,如果你想知道迁移学习如何应用于计算机视觉等其他领域,请查阅我之前写的这篇文章。

用于激光雷达点云特征提取的 GIS 管道

原文:https://towardsdatascience.com/a-gis-pipeline-for-lidar-point-cloud-feature-extraction-8cd1c686468a?source=collection_archive---------5-----------------------

如何从激光雷达点云中自动提取特征

使用 DBSCAN 无监督的基于密度的机器学习模型“Image by Author”对点云数据点进行分类

在本文中,我将带您以自动化的方式从激光雷达点云中提取要素。

激光雷达点云数据是描述表面或对象的点的集合。每个点都用 x、y、z 坐标描述,在某些情况下还与附加属性相关联,例如点的分类。

激光雷达数据用于精确提取地理参考矢量要素,如建筑物、道路、树木。认识到如[1]中有两个主要步骤:

  1. 分类:给点云数据集中的每个点分配一个类。如果有足够的训练数据,一种方法是使用 PointCNN 神经网络。输出是每个数据点的一个类。
  2. GIS 管道:从分类后的点云数据中提取有用格式的相关特征。除了提取之外,生成所提取特征的属性信息,例如物体高度、物体表面积、质心纬度/经度、与其他特征的接近度(例如..g 离最近的路有多远),等等。

本文将扩展第(2)点,重点关注自动 GIS 特征提取,假设数据已经分类,并且每个数据点都被分配了一个类别。

数据和预处理

本节将深入研究数据采集、预处理和目视检查。在开始科学或开发工作之前,这通常是重要的一步。

1。数据下载和转换

有许多服务提供激光雷达数据下载。为了配合本教程进行样本数据采集,我们将从[3]下载一个激光雷达数据集。使用地理空间数据时,了解数据集的空间参考系统始终非常重要。此切片的空间参考系统是 EPSG:7415 [4]。根据维基百科,空间参考系统的定义是

空间参考系统或坐标参考系统是用于定位地理实体的基于坐标的本地、区域或全球系统

下载数据的服务器不是很快,所以下载可能需要一些时间。

我花了几个小时下载数据集,所以在这一节的最后,我将分享一个从我的 google drive 下载预处理版本的链接。

下载的文件是 Laz 格式

LAZ 格式是激光雷达数据的无损压缩。

它可以将原始 Las 文件压缩到其大小的 7%到 25%。速度是每秒一百万到三百万点[5]。

下载的文件大小为 2.2 GB。解压缩 LAZ 文件到原来的 Las 的一个选择是使用 pylas。提取的 Las 文件为 13 GB。令人印象深刻的压缩比!下面是如何使用 pylas python 包在 python 中将 LAZ 文件解压缩为 LAS 文件。

#!pip install pylas[lazrs,laszip]
input_laz_path = ‘data/C_37EZ2.laz’
output_las_path = ‘data/C_37EZ2.las’
laz = pylas.read(input_laz_path)
las = pylas.convert(laz)
las.write(output_las_path)

2.点云目视检查

从 QGIS 3.18 开始,QGIS 中提供了直接加载和可视化 las 点云数据的功能。它非常简单,允许数据可视化,叠加卫星图像,并呈现高质量的地图。

我们必须手动定义点云文件的 CRS(坐标参考系统),以显示在 QGIS 的查看器窗口中。对于卫星图像叠加,有许多 QGIS 插件,其中最好的一个是由 NEXTGIS 开发的快速地图服务,它拥有最著名的底图 XYZ URL[6]。我们正在使用的数据集已经对大多数点进行了分类,并且可以使用如下图例进行映射:

在 QGIS“作者提供的图像”中目视检查下载的点云文件

仔细观察叠加了卫星图像的分类点云,我们可以看到,并非所有要素都与背景卫星图像中的完全相同。仅捕获地面、建筑物、水对象,没有关于例如树木的道路表面的附加信息。

一个原因可能是点云数据获取的日期(2014 年)与叠加背景图的日期(谷歌的 2021 年)不同。绿色区域也被错误地归类为地面,也许这是一个多雪的冬季?

对于 POC(概念验证),我们应该将数据大小减少到最小 AOI(感兴趣区域)。但是,我们还应该考虑到,当缩放到更大的数据集时,基于最小 AOI 的输出算法将保证相同的结果。

为此,让我们手动选择一个包含大多数类的 AOI,并在屏幕上绘制一个具有相同 CRS 的多边形,如下所示:

用户定义的感兴趣区域,用于在开发过程中减少数据量“按作者排序的图像”

要将激光雷达点云数据裁剪为特定的 AOI,有许多工具和库,如 LAStools [7] clip 或其他 python 选项,如 PDAL。为什么我们需要将激光雷达数据集裁剪成较小的感兴趣区域:

  1. 在开发阶段,我们需要尝试许多解决方案,每次在大数据集上运行脚本并等待解决方案验证是一场噩梦
  2. 在大多数情况下,开发发生在本地机器上,那里的资源是有限的。那么同样的代码可以在云或者更大的服务器上使用
  3. 如果数据很小,可以放入内存中,那么直观地检查算法的质量就容易得多。

输出较小的感兴趣区域如下所示:

从大型激光雷达数据文件“作者提供的图像”中截取感兴趣区域

快速直观调查显示,数据包含三类类别:

  1. 建筑物
  2. 地面
  3. 不保密的

建筑物的特征似乎表现得很好,并且很容易识别几何形状。水特征看起来不完整,如果我们从这个样本中自动捕获它,可能会导致连线的多边形片段。

感兴趣区域中分类激光雷达数据点中的每个类可视化“按作者分类的影像”

为了限制此分析的范围,此工作将只关注从分类的激光雷达数据中提取建筑物。

特征抽出

根据[1],从激光雷达数据中提取物体有两种技术可以使用,这两种技术中的每一种都可以适用于特定的用例,并且取决于业务需求和验收标准:

  1. 光栅分析通过光栅化点云图层,然后将光栅应用于多边形
  2. 使用无监督机器学习算法 DBSCAN 将每个对象分离为一个簇,然后应用边界多边形操作或其他操作来逼近对象的边界。

在本报告中,我们将解释并应用这两种技术。

1.光栅分析

要从激光雷达数据点捕捉要素,第一步是将 3d 矢量点(X,Y,Z)转换为基于格网的栅格表示。此过程称为栅格化,输出栅格将是数字表面模型(DSM)。并且还将包含每个像素中相应的平均点高度。

一个可以做到这一点的图书馆是 PDAL[7]。为了向前推进,我们需要指定结果 DSM 的分辨率。在这种情况下,让我们使用 4 倍的平均点间距,这是最佳做法,但可能会因使用情形而异。点与点之间的平均间距大约为 30 厘米(手动在屏幕上测量几次),然后我们选择像素大小为 1.2 米。

要在 mac 上安装 PDAL,请使用以下命令:

brew install pdal
Pip install pdal

PDAL 有一个 python API,可用于脚本编写和从 python 创建数据管道,API 实现如下:

pdal_json = {
 “pipeline”: [
 INPUT_LAS_PATH,
 {
 “type”: “filters.range”,
 “limits”: “Classification[6:6]”
 },
 {
 “filename”: OUTPUT_TIFF_DEM,
 “gdaldriver”: “GTiff”,
 “output_type”: “all”,
 “resolution”: “1.2”,
 “type”: “writers.gdal”
 }
 ]
 }
 pdal_json_str = json.dumps(pdal_json)
 pipeline = pdal.Pipeline(pdal_json_str)
 count = pipeline.execute()

在上面的代码块中,JSON 对象 pdal_json 存储了所需的管道定义。首先,我们输入 las 文件路径作为 INPUT_LAS_PATH,然后我们指定我们要提取的限制类是 6 号类,它是 building。之后,我们将输出文件名指定为 OUTPUT_TIFF_DEM,并指定输出 TIFF 图像的分辨率。

一旦我们将该定义文档传递给 PDAL 管道 API,它将根据输入开始执行,并从输入列表中输出 TIFF 文件。非常简单方便。

这将产生如下的 GeoTIFF 文件,它是一个我们指定的像素大小为 1.2 米的光栅文件。此外,每个像素包含点云数据中像素的高度。下面的可视化可以使用 QGIS 和 quickmapservices 插件为背景卫星图像生成。

使用 PDAL“作者提供的图像”从点云输出栅格

下一步是从生成的 DEM 中提取建筑物覆盖区。有不止一种工具可以做到这一点。例如:

  1. 使用 GDAL 多边形化实现
  2. 使用栅格形状

这两种工具都为栅格中共享一个公共像素值的所有像素连接区域创建矢量面。每个多边形都创建有一个指示该多边形像素值的属性。由于 DEM 包含作为像素值的像素高度,因此从 DEM 文件中提取多边形会产生多个具有相同像素值的小多边形。这不是我们要找的。

要提取建筑物覆盖区,我们应该将 DEM 视为二进制 tiff 文件,其中建筑物像素可以设置为 1,并且没有建筑物数据设置为 np.nan 值。这个预处理步骤然后可以进入另一个 rasterio 形状提取或 GDAL 多边形化的过程,以提取与一个多边形具有相同值的像素。

对激光雷达数据点应用栅格化和 GDAL 多边形化

最后,有了每个多边形的多边形化坐标,我们就可以使用 shapely 和 geopandas 创建一个地理数据框架,将每个建筑物作为一个要素。

Shapely 和 geopandas 是 python 中最著名的地理空间数据处理库。

请注意,在此步骤中,在保存输出矢量文件之前,特征的 CRS 应设置为 7415。

总共提取的特征是感兴趣区域中的 155 个建筑物

import rasterio
from rasterio.features import shapes
import numpy as np
from shapely.geometry import Polygonmask = None
with rasterio.Env():
 with rasterio.open(‘SlopeNew.tif’) as src:
 image = src.read(1) # first band
 results = (
 {‘properties’: {‘raster_val’: v}, ‘geometry’: s}
 for i, (s, v)
 in enumerate(
 shapes(image.astype(np.float32), mask=mask, transform=src.transform)))

 geoms = list(results)

通过这样做,我们遍历了每个提取的多边形,并将它们全部保存到一个几何列表中。这些几何图形可以在 geopandas 中读取,并将显示为如下所示的地理数据框架,这样,90%的困难工作就完成了。

生成的矢量化建筑物形状如下所示,在本文的后面,我们将应用后处理步骤从这些建筑物中提取有意义的特征,请继续阅读。

来自激光雷达的矢量化建筑物“作者提供的图像”

2.无监督机器学习 DBSCAN 聚类

在上一节中,我们使用栅格分析和多边形化提取了建筑物覆盖区。虽然这种方法工作正常,但它需要将数据转换为完全不同的类型(栅格),这会丢失一些与原始数据点相关的重要值。

在下面的方法中,我们将使用无监督的机器学习聚类方法来将彼此靠近的所有点聚类为一个聚类,然后从这些点提取建筑物边界。

激光雷达数据存储在 las 文件(或压缩的 laz 文件)中。要应用聚类,让我们首先提取每个点 X、Y、Z 的坐标以及每个点的相关类,如下所示:

def return_xyzc(point):
 x = point[0]
 y = point[1]
 z = point[2]
 c = point[5]
 return [x, y, z , c]pcloud = pylas.read(input_las_path)
points = [return_xyzc(i) for i in pcloud]
building_pts = [point for point in points if point[3] == 6]

产生的数据格式将是点 X,Y,Z,C 的数组,其中 X 是经度,Y 是纬度,Z 是高程,C 是点的类别。感兴趣区域的总数据点为 12,333,019。从建筑数据开始,我们只过滤建筑类别,即类别 6。这导致总共 1,992,715 个建筑点

[[87972210, 439205417, 699, 6],
 [87972259, 439205351, 57, 6],
 [87972267, 439205339, 1716, 6],
 [87972419, 439205135, -331, 6],
 [87972319, 439205284, 1030, 6]]

然后可以在背景地图上显示上述点,以查看建筑物类在 2d 中的外观,在这种情况下,每个数据点都存储为存储在 shapefile 中的 2d GIS 点,高程是一个属性。这不同于以前的 las 文件数据存储。

提取一类(建筑类)“作者图像”的矢量数据点

之后的下一步是应用 DBSCAN 集群。

DB scan 是对有噪声的应用程序进行基于密度的空间聚类。寻找高密度的核心样本并从中扩展聚类。适用于包含相似密度聚类的数据。

在 DB 扫描中,有两个核心参数,第一个是ε,它是两个样本之间的最大距离,其中一个样本被认为在另一个样本的邻域中。第二个参数是 min_samples,它是被视为核心点的点的邻域中的样本数(或总权重)。这包括点本身。

使用 DBSCAN 算法,输出如下,其中每个点都与将该聚类与其他数据点分开的聚类标签相关联,对于是噪声且不靠近ε参数内的其他点的点,该算法会将其值设置为-1,因为它不与任何聚类数据点相关联。看看下面的输出有多清晰

使用 DBSCAN 对激光雷达数据点进行聚类,并在背景卫星图像上进行可视化“作者提供的图像”

下一步是分别使用来自每个聚类的数据点来提取建筑物覆盖区和该对象的相关属性。为了单独处理每个群,问题陈述如下:

在背景卫星图像地图“作者提供的图像”上分离并可视化的一组点

以上建筑总积分= 35580 分,只针对一个单体建筑。

给定一个物体的三维坐标,下一步的目标是:

  1. 提取该物体的平均高度
  2. 计算对象的二维边界
  3. 计算相关的几何细节,如面积和质心点。

为了从对象的相关矢量数据点提取对象的 2d 边界,有许多技术,例如凸包和阿尔法形状。为此,我们将使用阿尔法形状

阿尔法形状算法在数据点周围构建最佳拟合边界

python 的 API 用法很简单,唯一需要的参数是 alpha。在我们的例子中,我们将它设置为 1,因为数据点彼此靠近,我们对精确的边界感兴趣。

 building = building_pts_gdf[building_pts_gdf.cluster==76]
shape = alphashape(building.geometry, alpha=1)

Alpha Shape 算法的输出是一个易于识别的多边形,可以存储为任何几何格式,如 WKT

对 DBSCAN 分类点云“按作者分类的图像”的一个聚类运行 Alpha Shape 算法的结果

让我们花些时间来看看基于输入点的边界有多清晰。如果使用凸包,输出看起来会像一个圆

我们现在要做的就是遍历所有集群,分别计算每个集群的建筑边界。

接下来,对于每一个建筑形状,我们都可以计算出以下属性,这对于使用输出数据文件非常有用:

  1. 边界几何形状
  2. 以点的中位高度计算的建筑物标高
  3. 总面积
  4. 周长
  5. 质心 x 坐标
  6. 质心 y 坐标

这可以使用简单的 python 和匀称的函数来完成

building = building_pts_gdf[building_pts_gdf.cluster==76]
shape = alphashape(building.geometry, alpha=1)
height = building.z.median()
area = shape.area
perimeter = shape.length
centroid_x = shape.centroid.x
centroid_y = shape.centroid.y 

生成的提取建筑形状如下,令人敬畏且干净,就像是由 AutoCAD 专业人员手动绘制的一样。

包含建筑物高度的 LIDAR 点云矢量特征提取结果“作者图像”

艰苦的工作已经完成,现在我们有了一个数据管道,可以接受点云输入和输出自动提取的特征和相关的几何属性。接下来是后处理

后处理

通常在主处理管道完成后进行后处理。后处理工作的目的因项目和数据类型而异,广义而言,这些是一些后处理目标:

  1. 质量检查和异常值去除:例如,去除具有不现实区域或小区域的特征,检测重叠特征并将其合并,等等
  2. 几何计算:从物体几何中计算有意义的信息,如面积或周长。例如,计算要素质心、计算要素
  3. 丰富性:从外部数据集中向要素添加更多属性信息,例如通过参考公交车站的外部数据源,为所有房屋与最近公交车站的距离添加附加信息。

1.质量检查和异常值去除

自动化质量检查和异常值消除需要对提取特征的允许应用有相当好的理解,并与最终用户或产品经理(最终用户的声音)合作,以制定最终的产品质量验收标准。

从上一步“作者图像”中创建的 GIS 数据管道中提取的建筑物中的离群建筑物

对于我们的用例,我们将把质量检查和异常值去除的范围限制在提取的建筑区域,在那里它应该在合理的范围内。为此,我们假设最小的建筑包括一个 3*3 平方米(9 平方米)的房间。该后预处理步骤将消除 40 个多边形,使感兴趣区域中提取的建筑物总数达到 115 个。

离群点检测和移除“作者图像”后最终提取的建筑层

2.几何计算

为了在实际业务中使用,并创建一个易于查询的数据集,可以在后处理步骤中为每个要素提取有意义的计算属性。这些特征的一些例子是面积、质心 x、质心 y 等等。该步骤将在以后帮助非空间数据用户查询各种业务用例和利用率的数据。

geopandas 中的几何计算非常简单,如下所示

building_gdf[‘area_m2’] = building_gdf.area
building_gdf[‘centroid_x’] = building_gdf.geometry.centroid.x
building_gdf[‘centroid_y’] = building_gdf.geometry.centroid.y

3.数据丰富

丰富通常是指用从其他来源获得的相关上下文来附加或增强收集的数据。地理空间数据丰富意味着通过引用外部数据源向数据集添加更多属性。几种常见的数据源是:

  1. 道路基础设施网络
  2. 兴趣点数据库
  3. 人口分布
  4. 行政边界

我们将在本报告中跳过应用此步骤,因为它可以无限扩展。

工程

在笔记本上写对于原型制作很有效,但是为了创建一个数据管道并与其他团队成员协作,需要对最终逻辑正确的脚本应用很少的步骤。下面是产品化的三件速成之事。

  1. 代码格式化

当有人在做原型的时候,通常代码的样式不是很好,缩进和空格在这个阶段不是优先考虑的。完成逻辑后,你可以使用一个自动化的造型工具,如谷歌的 YAPF[8]。一旦安装了这个库,从命令行运行它,让 google 设计你的代码。

yapf building_extractor.py > building_extractor_styled.py

2。对接

对 python 应用程序进行 dockerize 是一个很好的做法,可以避免依赖性冲突,特别是在这种情况下,我们使用了如此多的库,其中一些依赖于单独安装的二进制数据,如 PDAL。在没有 docker 的情况下复制相同的环境将是一个挑战。

3。单元测试

在运行输出之前,应该使用单元测试来测试逻辑函数,如 alpha shape 算法、几何计算。

形象化

除了静态地图之外,拥有一个交互式地图并能够可视化输入和输出总是很棒的。为此,我使用了 deck.gl 和一些额外的 HTML / JS 代码。

1.输入点可以是交互式的

为了可视化输入点云,我从 DBSCAN 分类点云数据的输出中提取了一些建筑物,交互式地图如下:

https://xmap.ai/maps/pcloud/dbscan_pcloud_3d.html

HTML 三维可视化,用于查看 DBSCAN“作者图像”中的点云数据

2.输出具有丰富属性的提取面

这是地理信息系统数据管道的最终产品。输出数据保存为 geojson 对象,使用 deck.gl 进行可视化,并按高度着色。

https://xmap.ai/maps/pcloud/buildings_vector.html

GIS 管道最终输出产品的 3D 可视化,构建-构建带有“按作者分类的图像”属性的矢量

摘要

激光雷达数据处理对于测绘和自动驾驶等许多应用来说是必不可少的。对于大多数应用来说,自动目标检测和提取是确保激光雷达产品取得商业成功所必需的。虽然现在许多工具都提供现成的分析工具,如 Arcgis,但编程自己的工具可以更加灵活地添加或调整以适应特定的用例。在本文中,我们探讨了如何使用两种不同的技术从激光雷达密集点云中提取建筑物覆盖区:

  1. 光栅化和多边形化
  2. 无监督机器学习基于密度的聚类和阿尔法形状边界检测

我们还探索了标准的后处理技术,例如

  1. 提取每个几何图形的基本属性(如面积、周长和质心)的几何计算
  2. 如有需要,可从外部数据集(如 POIs 数据库的道路网络)中添加更多实体属性

然后,我们研究了一些确保代码可重用性的工程步骤,例如:

  1. 使用自动格式化工具进行代码格式化
  2. 码头化
  3. 单元测试

最后,我们使用交互式地理空间可视化来查看分类的点云和最终输出的建筑数据,如以下两个链接所示:

该报告中的整个分析可以端到端自动进行,如果需要,可以安排为批处理作业。

问题或询问

我在这里https://mohamedbatran.com,如有任何问题或希望我们合作,请联系我们!

https://www.linkedin.com/in/mbatran/领英

参考

[1]https://medium . com/geoai/object-extraction-from-mobile-lidar-point-clouds-with-machine-learning-CB 15 fcbb 5597

[2]https://github.com/beedotkiran/Lidar_For_AD_references

https://download.pdok.nl/rws/ahn3/v1_0/laz/C_37EZ1.LAZ

https://epsg.io/7415

[5]https://www . cs . unc . edu/~ isenburg/lastools/download/laszip . pdf

https://plugins.qgis.org/plugins/quick_map_services/

[7]https://github.com/LAStools/LAStools

[8]https://pdal.io/workshop/exercises/analysis/dtm/dtm.html

https://github.com/google/yapf

所有编码人员都需要的 Git cheatsheet

原文:https://towardsdatascience.com/a-git-cheatsheet-that-all-coders-need-bf8ad4d91576?source=collection_archive---------0-----------------------

曾经意外删除文件或必要的代码?或者您希望回顾一下您的代码的旧版本?

克里斯蒂娜·朗普夫在 Unsplash 上拍摄的照片

我对 Git 有一种正常的爱恨交加的关系,这意味着当只有我一个人在项目中工作时,我喜欢 Git,而当我不得不将自己的代码与其他人的代码合并时,我讨厌它。但是所有的婚姻都有起有落,对吧。事实是,作为一名程序员,在某些时候,你会需要 Git,一旦你开始使用它,你就不会停止,即使它有时会令人沮丧。所以这里有一个备忘单,当你很久以后重新访问 Git,或者想学习 Git 的更多应用,或者如果你需要时间旅行并取回旧代码时,你可以使用它。

警告:Git 无法显示您的未来代码;你必须做这件事😜。

在我们开始之前,让我解释一下 Git 的结构,这样您可以更好地理解这些命令。

  • 存储库(简称 repos):它们是一个项目的不同版本的文件的集合。
  • 远程存储库:远程/在线存储的当前存储库。因此,我们在 Github 或 Gitlab 网站上看到的回购是这些项目的远程回购。它们包含了每个人做出和推动的改变。
  • 本地存储库:存储在本地设备上的当前存储库。它包含您所做的更改,也可以包含远程 repo 上的更改。
  • 提交:它们本质上代表了代码库的版本。每次提交都包含有关回购最后状态的更改。
  • 分支:一个分支代表一个独立的开发路线。当我们创建一个分支时,我们可以说是创建了一个全新的工作目录、临时区域和项目历史。新的提交记录在当前分支的历史中。

我们将在前面了解更多,所以让我们开始吧。

由吉菲

1) git 配置

让我们从为 Git 设置环境开始。在您的终端上运行这些命令来设置 Git 的全局配置(对于所有未来的存储库)。

$ git config --global user.name "John Doe"
$ git config --global user.email "johndoe@email.com"

该命令的另一个有趣用途是设置别名。我们在下面的命令中为命令gitcommit 设置了一个别名,所以现在 git co 将实际运行 git commit 。这对于有很多标志的较长命令很有帮助。

$ git config --**global** **alias**.co commit

2) git 初始化

接下来,我们需要将一个文件夹初始化为 git 存储库。这个命令实际上在你的文件夹中创建了一个. git 隐藏文件夹。这个文件夹表示它是一个 git repo,存储 git 所需的元数据。

//Run the following command inside the folder
$ git init//Run the following command to create a new directory that is a git repo
$ git init DIRECTORY_PATH/DIRECTORY_NAME

3) git 克隆

如果您想要使用一个已经存在的 git repo(远程 repo),您需要首先在您的本地设备上创建它的一个副本(本地 repo)。为此,我们使用克隆命令。首先,复制该存储库的克隆链接(这通常出现在存储远程存储库的位置)。

这个链接看起来会像这样:https://github.com/harsh-99/SCL.git。有了它之后,在您希望下载该存储库的文件夹中运行下面的命令。

$ git clone LINK
$ git clone [https://github.com/anveenaik99/onboardScripts.git](https://github.com/anveenaik99/onboardScripts.git)

4)获取 git

假设多人正在编辑您项目的远程存储库,这意味着你们都在协作编码,所以他们的更改对您来说是必不可少的。为了在需要时下载他们的更改,您需要您的本地 repo 了解这些更改,也就是说,您需要获取这些更改。

因此命令 git fetch 下载远程存储库的详细信息和设备上的更改。按如下方式使用它:

//download the state for all the branches
$ git fetch //fetch for just one branch
$ git fetch <remote> <local> 
//<remote> is the name of the remote branch
//<local> is the name of the local branch//an example of it is 
$ git fetch origin master

5) git pull

git pull 是两个命令的混合 git fetch + git merge 。当我们之前使用 Git fetch 时,它首先将远程存储库的当前状态下载到我们的本地设备上。但是我们的文件还没有改变。为了对我们的文件进行修改,我们需要 git merge,它基于远程版本更新我们的本地文件。

假设我有一个名为 test.py 的文件,在我的本地存储库中,它看起来像这样。

而文件的远程版本如下

我们希望带来这些变化,并把它们与我们的变化融合在一起。当更改彼此不冲突时,合并会顺利进行。但是通常情况下,我们会对别人也修改过的同一段代码进行修改。这就是 Git 抛出合并冲突的时候。至少可以说,我被这个词吓坏了,但我意识到这是有史以来最简单的事情来解决。让我们看看在前面的例子中是如何做到的。

有两种可能的方法来合并同一文件的上述版本。这些可能性如下:

选项 1

选项 2

选择哪个版本完全取决于您,取决于您想要实现的目标,但是让我向您展示如何锁定您的选择。当引发合并冲突时,它看起来像这样:

第 1、5 和 8 行表示您的文件有合并冲突,要消除冲突,您需要做的是选择第一个版本(第 2–4 行)、第二个版本(第 6–7 行)或两者的组合。然后删除第 1、5 和 8 行,继续您正在做的事情。

6) git 状态

现在运行这么多命令,我们显然想验证事情是否按计划进行。好的一面是 Git 允许我们随时检查状态。

$ git status //a sample output of this command is as follows
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your **local** commits)

Untracked files:
  (use "git add <file>..." to include **in** what will be committed)

	README.txt
	lab1

以上建议的命令都包含在这个博客中,所以为了更好的理解,可以跳到其中任何一个。

7) git 添加

我们的下一步是告诉 Git 我们希望它记住的变化。因此,对于每次提交,我们使用命令 git add 将更改添加到我们希望在提交中反映的文件夹中。

假设我在名为 GitTutorial 的 git repo 中的 test.py 文件中声明了一个新变量。我想通知 Git 这个更改,并让它保存这个更改。然后我将使用如下的 Git add 命令:

$ git add /path-to-test-py/test.py

现在,当您运行 git status 命令时,您会看到这个文件名用绿色书写,因为 git 对这个文件进行了更新。

如果你需要删除一个文件或文件夹,你可以使用 git rm 命令。

8) git 提交

现在我们已经添加或删除了需要通知 Git 的更改,我们提交这些更改。这在某种程度上最终确定了我们代码库的下一个版本。我们可以回到所有过去的提交来查看版本历史。该命令的工作方式如下。

$ git commit -m "The message you want to write to describe this commit"

-m 标志有助于编写描述提交的消息。

9) git 推送

到目前为止,我们所做的一切都发生在我们的本地存储库,但在某些时候,我们需要把它推到远程存储库,以便其他人可以看到和使用我们的代码。 git push 命令可以做到这一点。叫它如下:

$ git push <remote> <local> 
//<remote> is the name of the remote branch
//<local> is the name of the local branch//an example of it is
$ git push origin master

上述命令将我们的本地提交推送到主分支。

10) git 日志

当然,在执行多次提交之后,我们实际上想要看看代码是如何发展的。正如我们将在前面了解到的,也有可能许多人提交到他们的分支,并且在某些时候可能想要将他们的分支与不同的分支合并。我们的 repo 中已经完成的所有此类操作都可以使用 git log 命令来访问,如下所示:

$ git log --graph --oneline --decorate
//a sample output
*   0e25143 (HEAD, main) Merge branch 'feature'
|\  
| * 16b36c6 Fix a bug in the new feature
| * 23ad9ad Start a new feature
* | ad8621a Fix a critical security issue
|/  
* 400e4b7 Fix typos in the documentation
* 160e224 Add the initial code base

这里,我们在每行开头看到的字母数字代码代表每次提交,如果我们想要恢复或执行其他功能,将会使用这些代码。

另请参见 git shortlog。

11) git 还原

我们来到 Git 的一部分,当我们出错并想回到以前的代码时,我们将需要它。 git revert 可以被描述为撤销按钮,但是很聪明。它不只是及时返回,而是将过去的更改带入下一次提交,这样不需要的更改仍然是版本历史的一部分。

对于 *git revert,*我们需要之前在日志中看到的提交代码。

$ git log --oneline
86bb32e prepend content to demo file
3602d88 add new content to demo file
299b15f initial commit$ git reset --hard c14809fa
//this command will not changes files that you have not git added 

还有许多其他的方法可以恢复,所以,如果你需要的话,一定要检查一下。

12) git 分行

该命令允许我们创建、列出、重命名和删除分支。我们来看几个例子。

//this lists the name of the branches present
$ git branch 
main 
another_branch 
feature_inprogress_branch//delete a branch safely
$ git branch -d <branch>
$ git branch -d another_branch

13) git 结帐

git checkout 命令允许您在由 git 分支创建的分支之间导航。

//switch to a different branch
$ git checkout <branch_name>
$ git checkout another_branch//create a new branch
$ git checkout -b <new_branch_name>
$ git checkout -b new_feature_branch

14) git 差异

有时我们需要比较不同版本或者不同分支之间的代码;这就是我们使用 git diff 的时候。

//print any uncommitted changes since the last commit.
$ git diff//compare code between two branches
$ git diff branch1 branch2//print the uncommitted changes made in one file
$ git diff /filepath/filename

15) git rebase

到了我们的最后一个命令,也是我最害怕的一个命令:p。有时我们需要合并代码,或者将代码从主服务器拉到我们的分支服务器,或者许多其他情况。

告诉你一个要点:

Rebase 是专门用于将一个分支的变更集成到另一个分支的两个 Git 工具之一。另一个变更集成实用程序是git merge。合并总是一个向前移动的变更记录。或者,rebase 具有强大的历史重写功能。

让我们看看 git rebase 做了什么。

 B -- C (another_branch)
   /      
  A--------D (master)Rebasing another_branch onto master

             B -- C (another_branch)
            /      
  A--------D (master)

相应的代码

$ git pull origin master
$ git checkout another_branch
$ git rebase master

重新定基和合并可能是最复杂的命令,因为您必须解决大量的合并冲突才能恢复代码的顺序。但是它们对于你坚持基于版本控制的环境是至关重要的。我恳求你坚持完成这些,即使它们变得混乱,这样你的代码的结构完整性仍然是正确的。像平常一样解决合并冲突,把所有对你来说重要的代码都取出来。

另一个与 rebase 类似的功能是 git merge(正如我们上面看到的)。Git merge 主要用于合并两个分支,其工作方式类似于 rebase。

结论

我希望这篇博客对你有所帮助;请在评论中告诉我你的想法。尽管我取笑了 Git,但它绝对是任何开发人员最重要的工具之一,最好马上掌握它!所以要不断探索和尝试。

在媒体上关注我们,获取更多此类内容。

成为 介质会员 解锁并阅读介质上的许多其他故事。

文本相似性一瞥

原文:https://towardsdatascience.com/a-glance-at-text-similarity-52e607bfe920?source=collection_archive---------26-----------------------

自然语言处理笔记

如何计算文档之间的相似度

蒂姆·J在 Unsplash 上的照片

什么是文本相似度?

考虑以下两句话:

  • 我的房子是空的
  • 我的矿上没有人

尽管这两个句子是以两种完全不同的格式写成的,但是人类可以很容易地确定这两个句子表达了非常相似的意思;这两个句子的交集只有一个共同的词“是”,它不能提供任何关于这两个句子有多相似的见解。尽管如此,我们仍然期望相似性算法返回一个分数,通知我们句子非常相似。

这种现象描述了我们所说的语义文本相似性,我们的目的是根据每个文档的上下文来识别相似的文档。由于自然语言的复杂性,这是一个相当困难的问题。

另一方面,我们还有另一个现象叫做词汇文本相似性。词汇文本相似性旨在识别文档在单词级别上的相似程度。许多传统技术倾向于关注词汇文本相似性,并且它们通常比慢慢崛起的新深度学习技术实现起来快得多。

本质上,我们可以将文本相似性定义为试图确定两个文档在词汇相似性和语义相似性方面有多“接近”。

这是自然语言处理(NLP)领域中的一个常见但棘手的问题。文本相似性的一些示例用例包括对文档与搜索引擎中的查询的相关性进行建模,以及理解各种人工智能系统中的相似查询,以便向用户提供统一的响应。

文本相似性的流行评价指标

每当我们执行某种自然语言处理任务时,我们需要一种方法来解释我们正在做的工作的质量。“文档非常相似”是主题,与具有 90%准确性分数的模型相比,信息不多。度量为我们提供了客观的、信息丰富的反馈来评估任务。

流行的指标包括:

  • 欧几里得距离
  • 余弦相似性
  • 雅克卡相似性

我在向量空间模型中介绍了欧几里德距离和余弦相似性,而桑克特古普塔关于文本相似性度量概述的文章详细介绍了 Jaccard 相似性度量。

为了更好地理解我们评估文本相似性的两种方法,让我们用 python 编写上面的例子。

Python 中的词汇文本相似性示例

# importing libraries
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import TfidfVectorizer# utility function to evaluate jaccard similarity
def jaccard_similarity(doc_1, doc_2):
    a = set(doc_1.split())
    b = set(doc_2.split())
    c = a.intersection(b)
    return float(len(c)) / (len(a) + len(b) - len(c))# defining the corpus
corpus = ["my house is empty", "there is no one at mine"]# to evaluate cosine similarities we need vector representations
tfidf = TfidfVectorizer()
X = tfidf.fit_transform(corpus)# printing results
print(f"Cosine Similarity: {cosine_similarity(df, df)[0][1]}\nJaccard Simiarity: {jaccard_similarity(corpus[0], corpus[1])}")**>>>> Cosine Similarity: 0.11521554337793122
     Jaccard Simiarity: 0.1111111111111111**

与我们认为这些文档相似的想法相反,两个评估指标都表明我们的句子一点也不相似。这是意料之中的,因为正如我们之前所说的,文档不包含相似的单词,因此它们不被认为是相似的。

Python 中的语义文本相似性示例

from gensim import corpora
import gensim.downloader as api
from gensim.utils import simple_preprocess
from gensim.matutils import softcossimcorpus = ["my house is empty", "there is no one at mine"]dictionary = corpora.Dictionary([simple_preprocess(doc) for doc in corpus])glove = api.load("glove-wiki-gigaword-50")
sim_matrix = glove.similarity_matrix(dictionary=dictionary)sent_1 = dictionary.doc2bow(simple_preprocess(corpus[0]))
sent_2 = dictionary.doc2bow(simple_preprocess(corpus[1]))print(f"Soft Cosine Similarity: {softcossim(sent_1, sent_2, sim_matrix)}")**>>>> Soft Cosine Similarity: 0.7836213218781843**

正如所料,当我们考虑所用句子的上下文时,我们能够识别出我们的文本非常相似,尽管没有很多共同的单词。

包裹

读完这篇文章后,你现在知道了什么是文本相似性,以及测量文本相似性的不同方法(即词汇文本相似性和语义文本相似性)。这里有一个给有抱负的数据科学家的想法:如果你是一名求职者,正在寻找进入 NLP 的突破口,一个想法可能是创建一个简历解析器,告诉你你的简历与工作描述有多相似。

感谢阅读!在 LinkedIn 和 Twitter 上与我联系,了解我关于人工智能、数据科学和自由职业的最新帖子。

相关文章

</5-ideas-for-your-next-nlp-project-c6bf5b86935c>

机器学习词汇表

原文:https://towardsdatascience.com/a-glossary-for-machine-learning-52ecf147ce38?source=collection_archive---------28-----------------------

简要解释基本术语和概念

萨曼莎·加德斯在 Unsplash 上的照片

如果你正在阅读这篇文章,你正在或计划在数据科学领域工作。因此,我认为你很清楚这个领域的潜力和重要性。

数据科学是一个如此广阔的领域,可能存在于我们可以收集数据的任何过程中。工具和技术因任务而异,但最终目标是相同的:使用数据创造价值。

机器学习是数据科学的一个主要子领域。它涉及在没有明确指示的情况下探索数据中的规则、模式或关系的算法。我们将数据提供给机器学习算法,并期望它从中提取有价值的见解。

在这篇文章中,我将简要解释你在机器学习中可能遇到的关键术语和概念。在某种意义上,它可以被认为是机器学习的词汇表。

注:如果你想阅读这篇西班牙语文章,它发表在 Planeta 聊天机器人上。

监督学习

监督学习涉及标记的观察或数据点。给定一组观察值,监督学习算法对自变量(即特征)和因变量(即目标或标签)之间的关系进行建模。

考虑一个模型,根据房子的年龄、位置和大小来预测房子的价格。在这种情况下,年龄、位置和大小是特征,价格是目标。

无监督学习

无监督学习不涉及观察的标签。无监督学习算法在一组观察值中寻找潜在的结构或模式。

一个例子是零售企业根据顾客的购物行为将他们分成不同的组。没有与客户相关联的标签。相反,无监督学习算法有望找到这样的标签。

强化学习

强化学习基于行动奖励原则。代理人通过反复计算其行为的回报来学习达到目标。

这可以被视为从互动中学习。从这个意义上说,强化学习类似于我们从错误中学习的方式。主体与环境相互作用以达到其目标,并评估其行动的结果。强化学习的常见应用是计算机游戏和机器人。

分类

分类是一种处理离散(或分类)目标变量的监督学习技术。例如,检测电子邮件是否是垃圾邮件是一项分类任务。它也被称为二进制分类,因为目标变量只有两个可能的值,垃圾邮件或非垃圾邮件。如果目标变量包含两个以上的值(即类),则称为多类分类。

分类算法有几种评估标准。一些常用的是分类准确度、对数损失、精确度、召回率和 AUC。

回归

回归是一种监督学习技术,其中目标变量是连续的。典型的例子可以是房价预测。

在回归任务中,基于预测值与实际值的接近程度来评估机器学习算法。一些常用的回归评估指标是平均绝对误差(MAE)、平均绝对百分比误差(MAPE)和均方误差(MSE)。

使聚集

聚类是一种无监督的学习技术。它以这样一种方式对观察值进行分组,即同一组中的观察值彼此之间比其他组中的观察值更相似。这些组被称为集群。

与分类不同,聚类中的观察值(即数据点)没有标签。根据顾客的购物行为将他们分组是聚类的一个例子。

过度拟合

机器学习算法通过用数据训练来学习。这些算法非常强大,可以学习训练数据的每一个细节。然而,我们不希望这样。

训练模型的主要目的是能够将它们用于新的、以前未见过的数据(例如测试数据)。训练数据和测试数据之间总会有微小的差异。因此,模型应该很好地概括,而不是记忆训练数据的细节。

当模型试图很好地拟合训练数据,以至于不能推广到新的观察结果时,就会出现过度拟合。广义模型在新的观察上表现得更好。如果一个模型比需要的更复杂,我们很可能会过度拟合。

过度拟合(图片由作者提供)

欠拟合

从某种意义上说,适应不足是适应过度的反义词。欠拟合模型未能捕捉训练数据的足够细节。因此,它在训练或测试数据上的表现都不好。

欠拟合(图片由作者提供)

偏见

偏差是平均预测值与实际值相差多远的度量。如果我们试图用一个非常简单的模型来模拟一个复杂的关系,就会产生偏差。

高偏差模型的预测非常相似。由于它对数据内的变化不敏感,因此该模型在训练数据和测试数据上的准确性都非常低。具有高偏差的模型可能导致拟合不足。

差异

方差是偏差的反义词,对数据内的变化敏感。具有高方差的模型对训练数据中非常小的变化或噪声很敏感。异常值也可能被模型捕获。

具有高方差的模型在训练数据上表现良好,但在测试数据上失败。因此,它们会导致过度拟合。

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

L1 正则化

模型过于复杂会导致过度拟合。因此,控制复杂性是过度拟合的解决方案。

正则化通过惩罚模型中较高的项来控制复杂性。如果增加一个正则项,该模型试图最小化损失和模型的复杂性。

L1 正则化通过在每次迭代中从权重中减去一个小的量来强制无信息特征的权重为零,从而最终使权重为零。Lasso 回归使用 L1 正则化。

L2 调控

这是控制模型复杂性的另一种技术。岭回归使用 L2 正则化。

L2 正则化迫使权重趋向于零,但并不使它们完全为零。L2 正则化就像在每次迭代中去除一小部分权重的力。因此,权重永远不会等于零。

原木损失

日志损失是分类模型常用的评估指标。分类算法通常输出属于特定类别的观察值的概率值。类别标签被分配为具有最高概率的标签。

Log loss 基于概率值而不是分配的标签来评估模型。因此,它提供了一个更体面和彻底的评价。

例如,-log(0.9)等于 0.10536,而-log(0.8)等于 0.22314。因此,90%的把握比 80%的把握会导致更低的测井损失。然而,在分类准确性方面,90%或 80%的把握不会产生任何影响。

结论

我们已经介绍了机器学习中的一些关键术语和概念。要学的东西还很多,但基础永远是重要的。他们为接下来的事情打下基础。

对基础有一个全面的了解,会对你的机器学习之旅有帮助。

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

为 Atari 游戏实现 PPO 的图形指南

原文:https://towardsdatascience.com/a-graphic-guide-to-implementing-ppo-for-atari-games-5740ccbe3fbc?source=collection_archive---------2-----------------------

代码近似策略优化之旅的收获

达里尔 丹尼尔

图片由 sergeitokmakov 在 Pixabay 上拍摄

了解最近策略优化(PPO)的工作原理并编写一个有效的版本是很困难的。这可能会在很多地方出错——从误解数学和张量不匹配到实现中的逻辑错误。我和一个朋友花了大约一个月的时间才完全运行 PPO,在那里我们成功地实现了该算法,并确信我们了解它是如何运行的。本指南总结了我们努力构建最终算法和直觉的所有领域。这个博客旨在涵盖 PPO 的基本理论,我们如何决定实现我们的版本,使用张量形状,测试,并使用记录的度量来调试我们所建立的。我希望你喜欢它!

因为这篇博文打算覆盖 PPO,所以它将假设一些基础强化学习(RL)的先验知识。特别是 RL 问题的基本设置,关于行动者-批评家模型如何工作的一些概念,贝尔曼方程背后的概念,以及对什么是政策梯度方法的模糊理解。

Github Repo — [ 链接 ]

🏔高层理论

概括一下——PPO 属于一类叫做策略梯度(PG)的算法。基本思想是直接更新政策,使采取行动提供更大未来回报的可能性更大。这是通过在一个环境中运行算法,并基于代理的动作收集状态变化来完成的——这些交互的集合称为轨迹。一旦收集了一个或多个轨迹,该算法将查看每一步,并检查所选择的行动是否导致积极或消极的奖励,并相应地更新政策。

图 1.1:训练策略梯度算法的高层图。(图片由作者提供)

对动作进行采样并收集轨迹

通过逐步与环境交互来采样轨迹。为了采取单个步骤,代理选择一个动作并将其传递到环境中。

PG 算法通过接受输入(环境的状态或观察)来决定采取哪种行动,然后,对于离散的行动空间,输出每个可能行动的概率。神经网络通常会计算这个概率。神经网络的输出由其权重决定,通常记为π_θ,其中π表示策略,θ表示网络的权重(这对下一部分很重要)。

为了选择一个动作,代理从神经网络(称为 logits)给出的概率分布中进行采样——然后这个动作被用来为环境提供它的下一个输入。比如图 1.2 中,动作右有 50%的几率被选中(左 12.5%,上 25%,下 12.5%)。作为从概率中选择动作的结果,探索在 PG 算法中固有地存在,因为动作中的随机性来自选择概率。随着代理人越来越确定什么是正确的行动,探索将随着不正确行动的概率降低而减少。一旦采取行动,所有指标都会被存储起来,供以后在培训中使用(观察、行动、记录行动概率、价值、奖励、完成)。

图 1.2:图示了在策略梯度强化学习算法中选择动作的方法。(图片由作者提供)

更新代理

在深入研究 PPO 之前,先从较高的层面上了解一下 PG 算法的数学知识,以便对该算法将要做的事情有一个完整的了解。然而,由于这篇博文是关于 PPO 的实现,我们将不得不跳过计算损失和更新代理背后的推导过程的细节(另外,如果你对 PG 背后的数学不感兴趣,那么可以随意跳过接下来的 3 个标题)。

**更新策略:**任何 PG 算法的构造块都是关于如何更新网络的公式。图 1.3 中的更新公式表明,给定一个特定的状态-动作对[4]乘以学习速率[3],网络的新权重[1]被更新为当前权重[2] +当前网络的梯度。这是如何更新网络权重的基础公式。直观上,梯度是权重的方向(+/-),在该方向上,策略的改变将使在给定状态下采取的行动更有可能(反转它们以使它们不太可能)。

图 1.3:政策梯度权重更新方程。(图片由作者提供)

从数学角度对政策梯度更新如何工作的完整解释,没有比大卫·西尔弗的讲座更好的地方了。

那么我们如何计算这个梯度呢?令人欣慰的是,所有最常见的深度学习框架都会保留一个通过神经网络的信息的计算图,并保留网络梯度部分的记录。

**调整权重方向和数量:**通过更新方程,我们知道特定状态-动作对的“梯度”是什么方向——也就是说,通过改变梯度方向上的神经网络权重,使该动作(在特定状态下)更有可能。当然,我们不希望让每一个行动都更有可能,如果得到了不好的结果,我们希望降低采取行动的概率。这就是优势函数[1]所体现的——一个行动比我们预期的好或坏多少。如果一个行为导致我们比以前过得更好(积极的优势),权重在梯度方向上改变,有效地使该行为更有可能被再次选择。如果情况更糟,我们在渐变方向上减少权重,使它变得不那么轻

另一个考虑是权重的更新量,如果仅仅是梯度的优势倍被更新,将会有非常积极的更新(为什么在这里有很好的解释)。因此,为了抵消这一点,我们将更新除以策略的输出[2],这称为重要性采样(对特定更新赋予多少权重)。

图 1.4:使用附加变量更新规则,以告知梯度需要更新的方向和量。(图片由作者提供)

例如,如果动作输出为[2,3,5,1]并且选择了值为 5 的动作(动作 3),渐变将除以 5 以减少渐变的更新量。这是为了阻止行动 3 的价值快速增加,因此没有其他价值获得被选择的机会(因为这是在概率基础上完成的,如果我们有[2,3,100,1],其他行动不太可能被选择),即使它们可能在长期产生更高的回报。

**链接起来:**这就把最后一步留给了更新规则,它来自于链规则的魔力。利用这一点,我们可以用所采取行动的梯度记录代替偏导数分数。这一部分很重要,因为它是 PG 和 PPO 算法的基础。

图 1.5:最终更新规则。(图片由作者提供)

同样,这并不是对策略梯度算法背后的数学理论的全面解释,而是对日志值来源的快速回顾/概括。

**可视化更新:**当理论被应用于实践时,它看起来会像图 1.6 描述的那样。首先,运行轨迹[步骤 1],并保存所有相关值(记录概率、价值和奖励等)。一旦完成了轨迹,就可以计算贴现回报和优势[步骤 2](伟大的文章在这里解释什么是优势,但作为一个提醒,在advantages = discounted_return - expected_return预期回报被评论家计算为价值)。[步骤 3]接下来,通过查看对数概率并乘以优势来计算每一步的损失,如图 1.5 所示。[步骤 4]最后,我们取所有这些损失的平均值,用梯度下降法做一次反向传递。

图 1.6:显示通过 4 步流程更新政策梯度权重的流程图。收集轨迹,计算优势,计算损失,最后更新网络权重。请注意,学习率是在初始化时在优化器中设置的。(图片由作者提供)

PPO——主要理念

原 PPO 纸— 链接

使用策略梯度方法,一旦收集了一些轨迹,将使用相同的轨迹多次更新模型,以便从收集的新数据中学习。请记住,此更新的大小由优势的大小和重要性样本(表示为行动的对数概率)共同决定。

PPO 增加了一个额外的因素,即概率比,以防止大量更新的发生。这被定义为原始动作日志概率和新模型的动作日志概率之间的差异。

图 1.7:损失函数使用新旧政策行动日志概率的比率,称为保守政策迭代(CPI)。PPO 论文第 3 (6)节中的公式。(图片由作者提供)

**问题:**PG 算法的问题在于,当执行更新时,策略变化可能发生的幅度(即权重变化)会将策略推入一个区域,当执行另一个梯度下降时,所有方向基本相同,并且错过了奖励的峰值。图 1.8 展示了这一点——单次更新错过了回报的峰值,现在当在新的轨迹上执行梯度下降时,很难说“左”或“右”移动是否会导致更好的政策损失。这会导致更新将策略破坏到不可恢复的程度。

图 1.8:图表显示了一个政策更新是如何超越的,以及大量更新错过的高回报是如何获得的。(图片由作者提供)

**解决方案:**PPO 论文的解决方案是在原始动作日志概率和新模型的动作日志概率之间创建一个比值。如果该比率变得过大或过小,它将被限制在+/-一个设定值(通常为 0.2)。这将防止策略占用太多的更新步骤;这可以在图 1.9 中看到。

图 1.9 显示了 PPO 如何裁剪策略以强制较小的策略更新。(图片由作者提供)

在数学上,这是用一个削波函数表示的,在 PPO 论文中也称为替代函数:

图 1.10:PPO 论文提出的截断替代(损失)函数,选择截断和未截断概率比的最小值。PPO 论文第 3 (6)节中的公式。(图片由作者提供)

如果你想更详细地了解这一点, Arxiv Insights 在 YouTube 上有一个非常好的解释,从理论角度解释了 PPO 是如何工作的。

**流程的可视化实现:**从实际的角度来看,会发生以下情况:

  • 首先,我们使用初始模型(model_1)收集一些轨迹,确保保存所采取行动的行动日志概率。使用我们的轨迹,初始模型被更新为 model_2,并且现在不同于原始模型。
  • 然后,使用我们从轨迹[3]中收集的相同状态,计算新的对数概率2。
  • 然后,使用新模型和旧模型的动作日志概率之间的比率来计算新策略应该更新的标量。比率是按步骤计算的。如果比例过大或过小,将根据代理函数进行裁剪。

图 1.11—PPO 的更新流程。(图片由作者提供)

💻实施准则

希望理论部分足以给出一些关于代理如何采取行动的直觉,模型被更新,为什么 PPO 工作以及为什么需要裁剪和概率比。接下来的部分解释了我们是如何为自己实现 PPO 的——有些可能是显而易见的,有些则不是。然而,如果你想完全理解我们的实现,浏览所有的东西可能会有帮助,因为会有差异。在本节中,将会引用我们的代码,因此将您的屏幕分成两半可能会有所帮助,一部分打开博客文章,另一部分打开代码,当然,已经说过,通读这些内容对您自己最有用!

我通常如何分析旁边有博客文章的代码😋(图片由作者提供)

概观

总的来说,有三个主要的类和一个运行整个系统的文件:

  • Model.py [ 类 ] -这包含了代理的逻辑,既能对给定的输入起作用,又能从轨迹收集的数据中学习。
  • Memory.py [ 类——包含轨迹的所有数据,并计算优势。它还允许代理对轨迹的随机部分进行采样,以批量更新代理。
  • Config.py [ 类 ] -这包含 RL & PPO 中所需的所有设置,例如学习率、要走的步数和超参数,如用于裁剪大小的 epsilon。
  • Utils.py [ 文件 ] -这是运行采样和训练循环的主文件。

从图 2.1 中的流程图可以看出,培训代理的方式本质上是图 1.1 中概述的扩展。首先,程序初始化我们所有的变量,然后算法分两个不同的阶段运行,收集轨迹并根据收集的数据更新代理。

**收集轨迹:**通过在健身房环境中玩耍来收集轨迹;代理根据其观察在模拟中采取行动。当该循环激活时,单个步骤保存在代理的存储器中。

**更新代理:**一旦采取了足够多的步骤,就会计算折扣收益和优势——代理然后从“内存”中采样所有步骤,以计算损失并更新代理(所有捕获的数据都经过训练)。一旦执行了所有更新,循环又从头开始。

图 2.1 —流程图显示了我们的 PPO 代理如何培训以及这与我们的课程有何关系。(图片由作者提供)

设置

首先,实验中使用的所有组件都需要初始化。其中的主要部分是:

  • 健身房环境
  • 代理,包括添加模型和内存
  • 记录我们的实验(我们使用权重和偏差)

创建雅达利环境

代理训练的环境通常是由 OpenAI gym 创建的,这是一个为物理任务和 Atari 游戏创建模拟环境的框架。输入环境对其作出反应的动作,然后将这种变化作为一组观察结果返回给代理。随之而来的还有奖励,以及游戏是否已经让代理知道他们的行为是“好”还是“坏”。

当人类玩家看 Atari 游戏时,我们会看到构成图像的 RGB 颜色,以及我们玩游戏时画面的稳定变化。我们可以把每一帧都直接传送给我们的代理。这将被描绘成一个张量160,192,3,代表游戏的尺寸和 RGB 像素。然而,在典型的 Atari 游戏实现中(以及那些在原始论文中完成的),一些技巧被用来更有效地训练,我们已经将它们包含在了env.py文件中(这受到了很大的影响,并且部分复制自 Costa 的 CleanRL 实现)。

改变游戏风格:

前几个技巧是关于改变游戏的基本运作方式,以帮助代理人前进一点点。

  • episodic Life Env(envs . py:61)—这确保了一集只有在所有生命都失去时才结束,而不是一个生命都失去时。因此,折扣奖励将贯穿整个游戏而不是个人生活——这很重要,因为它将允许代理人进一步进入游戏。
  • nooplesetenv(envs . py:12)——作为雅达利引擎中的一个技术细节,如果你从头开始玩,游戏将永远是一样的。为了获得游戏引擎中的随机行为(而不是每次都是相同的版本),采取了一组随机的动作来将游戏设置为伪随机状态[ 论文 ]。这对于帮助代理概括其知识至关重要。
  • FireResetEnv(envs . py:41)—如果游戏只在按下开火命令时开始,它会在初始化时自动开火,这样代理就不必学习如何开始游戏。
  • Max 和 Skip ( envs.py:97 ) —代理采取的任何动作都将应用于游戏中的四个时间步骤。将返回所有生成帧的每个像素的最大值,类似于最大池。这些帧的奖励也将被累加。长的短的是,这都是为了避免玩游戏时经历的闪烁[ 更好的解释 ]。
  • clip rewards(envs . py:125)—通常来说,在 RL 中,如果奖励保持在“合理的范围”(大约+/- 1)内,代理训练得最好,这是为了避免在计算折扣回报时更新太多。剪辑奖励因为这个原因把奖励削减到+/- 1。

增加雅达利观察输出:

  • 帧堆栈( envs.py:187 )-代理被传递一个帧堆栈,代表实际游戏中的四个时间步骤。这是为了让代理可以有运动的感觉。例如,在突破中,球可能处于 4 个不同的位置,表示在特定方向上的移动。
  • wrap frame(envs . py:134)—这是一个自定义方法,用于重新构造 Atari gym 环境的输出。在所有的增强都发生之后,产生了一个张量(4, 84, 84)——使用 WrapFrame,它随后被转换为每一帧的(84, 84, 4)。这是为了与我们代理的 CNN 头兼容。

就单一环境的张量大小而言,这最终看起来是这样的:

图 2.2:行动的单一环境取样,作用于环境并产生相关的输出。(图片由作者提供)

所有这些增加输出和改变游戏玩法的技术都是为代理人从 Atari 游戏环境中更有效地学习而设计的。它可以被认为是机器视觉任务中的数据增强。

实现:增强在env.py中实现,它们在一个名为make_env(env_id, seed)的 thunk 函数中分组。这个函数可以在任意点调用,指定种子和游戏类型返回一个环境;这种实现方式有助于创建多个环境。

注意:人们可能已经注意到在图 2.2 中有一个增强输入块,它给代理的输入增加了一个维度,这是因为 PyTorch 2D CNN 块期望输入一个批处理。在我们的例子中,这是一个一批。

多重环境

我们选择实现多个环境,以利用我们家用 DL 机器的额外 CPU 和 GPU。这加快了训练的速度,也是一次关于多种环境如何工作的很好的学习练习。

当添加多个环境时,健身房环境的所有输出都获得一个额外的维度 n ,其中 n 是环境的数量。然而,我们代理人的输入没有任何不同。这是因为我们已经在单一环境中增加了 4 个维度的输入(如图 2.2 所示。).

在我们的Config.py中实现了多个环境,这里通过包装开放基线的T4 中的make_env函数来初始化环境,如下所示。

envs = VecPyTorch(
	DummyVecEnv(
		[make_env(env_id, self.seed+i, i) for i in range(self.num_env)]
	), 
self.device)

这个函数期望“一个创建环境的函数列表(每个调用返回一个体育馆。Env 实例,这就是为什么make_env返回一个创建 gym-env 而不是 gym-env 本身的 thunk 函数。VecPyTorch本质上是基于 VecEnv ,它“抽象了异步矢量化环境”。这是与矢量化环境交互的一种更简单的方式。

多环境采用我们对单个环境所做的,并以一种易于处理的方式对它们进行矢量化。所有其他游戏的增强和环境的重塑都和在单一环境中一样。

图 2.3:显示多个环境的数据流的图表,从动作的采样开始,到放置在环境中以及结果输出。(图片由作者提供)

创建代理

Atari PPO 代理创建自一个基类,该基类(图 2.4)通过超参数初始化两个关键部分:

  • 模型 -这是 CNN 的模型,我们已经配置好了,它被分成“头”“演员”和“评论家”。头部对传入的数据进行预处理,然后演员和评论家使用这些数据来计算他们各自的值。
  • 内存——稍后会深入探讨这个话题,但基本上,内存存储的是在表演阶段收集的状态转换。初始化决定了存储器的形状和大小,从而决定了展开时间的长短。

通过这两个类实例,代理能够执行 3 个基本功能:

  • Act :接收(多个环境的)观察结果,并提供相应的行动和这些行动的日志概率。
  • AddToMemory :一旦采取行动,该步骤的所有数据必须存储起来以备后用。这就是 AddToMemory 函数的作用;它将信息存储在 PyTorch 张量中。
  • Learn :这将在后面的章节中进一步探讨——但本质上,这将从内存中获取处理过的数据,并相应地更新模型。

图 2.4:显示代理结构的图表。(图片由作者提供)

型号

这个模型是一个普通的演员-评论家网络。一个 CNN 头接收游戏的帧作为输入,加上所有像素被缩小 255,这是为了确保输入范围在 0 和 1 之间。CNN 的输出然后被传递到两个完全连接的层。第一层输出作为评论家的单个值,然后第二个网络输出作为演员的游戏环境中可用的动作的数量。

图 2.5-演员-评论家网络。(图片由作者提供)

设置该类是为了调用一个act方法(models.py:76)来返回演员制作的actionlog probabilitiesentropy,以及评论家预测的value。为了获得动作,来自参与者的逻辑立即被采样(models.py:79)。

初始化网络权重

最后一个重要因素是确保每一层都已经初始化。一个名为layer_init的通用函数包围着网络中的每一层,这是为了避免消失或爆炸梯度(解释这是如何工作的这里是一篇好文章)。

记忆

创建一个内存类(而不仅仅是将单个变量存储为 PyTorch 数组)的决定在一定程度上阻止了强化学习讨论的不和谐(向在促进讨论方面表现出色的社区大声疾呼),在那里我们正在讨论核心功能的测试。众所周知,RL 算法会无声地失败,这意味着它们要么不会抛出错误,但永远不会学习,或者更糟的是,学习了,但没有发挥出最大潜力。这是一个巨大的问题,因为它使调试变得难以置信的困难,并且需要很多很多的实验;而在使用 CartPole 这样的简单环境时,v1 可能永远不会浮出水面。这就是为什么我们决定创建一个类来存储保存的轨迹,但它的责任也是计算优势,贴现回报和从内存中采样正确的数据。这样,我们可以为每个单独的函数编写测试(测试可以在/tests/test_memory.py中找到)以确保输出完全符合预期。这也有助于验证我们对优势和贴现回报的理解。

内存本身(如图 2.6 所示)有三个关键方面:

  • 保存的变量-轨迹被保存到内存中。数据存储在预定义的 PyTorch 张量中,因为这样可以避免将数组转换为张量等。
  • 计算优势 —如何计算将在下一节解释;重要的部分是,它根据采样的轨迹计算优势和折现回报。
  • 从内存中采样 —因为我们所有的数据都是有序的,并且仍然是我们的n数量环境的格式,所以该函数本质上是将我们所有的轨迹压缩到一个长张量中,然后对随机部分进行采样以进行训练(在更新模型部分中对实现进行了更详细的解释)。

图 2.6 —显示内存类结构的图表。(图片由作者提供)

内存的初始化决定了它的形状和大小,需要各种参数。这包括环境的数量和环境本身获得的观察和行动空间。为了确定采取多少“步骤”,还需要存储器的大小。最后,还需要伽玛等超参数。

权重和偏差( W & B

最后,我们初始化 W & B——这是我们了解代理表现的重要工具。它允许丹尼尔和我在我们自己的机器上分析数据和运行实验,并让我们两个都可以获得结果,同时远程工作。

拥有一些测井工具对于理解哪里出错以及不同超参数对模型的影响至关重要。在后面的部分中,我们将深入研究各种度量如何帮助我们调试工作。

收集轨迹

训练 PPO 智能体的第一步是收集轨迹——智能体和环境初始化后,如图 2.7 所示。代理根据对环境的观察采取行动,然后采取下一步行动。然后,状态和动作的序列最终被保存到存储器中,用于训练阶段。

图 2.7-如何收集轨迹的流程图概述。(图片由作者提供)

计算代理的动作

该操作由代理[Models.py:109]上的 with act 函数执行。

class ActorCritic(nn.Module):
	# ... def act(self, x, action=None):
		values = self.critic(self.forward(x))
		logits = self.actor(self.forward(x))
		probabilities = Categorical(logits=logits)
		if action is None:
		    action = probabilities.sample()
		return action, probabilities.log_prob(action), values, probabilities.entropy()

为了行动,模型接收来自健身房环境的观察(x)。然后,观察结果通过返回值(该状态的值是什么)的 critic 网络,然后通过返回 logits(神经网络的输出)的 actor-network。

该值按原样返回。然而,我们想把我们的逻辑转换成一个概率分布,这样我们就可以对一个动作进行采样。这是通过 PyTorch 的分类函数完成的——这个函数将我们的逻辑值变成与逻辑值成比例的分布,总和为 1。分类类的好处是,该类还可以从分布中抽取动作样本,计算动作的对数概率和熵(熵将在后面的部分中进一步解释)。

保存轨迹

保存轨迹是相当直观的,但对奖励、观察和完成如何从健身房环境中出来有一个可视化的表示是有用的。对于环境中的每一步,返回 3 个不同维度的变量(观察、奖励和完成——如图 2.8 所示)。

图 2.8 —从单个环境返回的数据的可视化表示。这张图显示了不同步骤的各种场景,一些得到了奖励,一些结束了这一集。图例中的数字代表返回的张量的维数。(图片由作者提供)

代理与环境的每次交互(作用于多个环境)都会返回一个观察、奖励和完成。在图 2.8 的例子中,第一步没有奖励,情节还没有结束,除非另有说明,否则以后的每一步都是如此。这说明收集的数据作为跨多个环境的完整步骤被附加到内存中,由蓝框表示。这意味着保存到存储器中的是:

  • 观察结果(n, 80, 80, 4)
  • 奖励(n, 1)
  • 多内斯(n, 1)

然后以维度张量的形式保存在存储器中:

  • 观察结果(x, n, 80, 80, 4)
  • 奖励(x, n, 1)
  • 完成(x, n, 1)

其中 x 是步数,这意味着代理训练的“全局”步数实际上将是 x 乘以 n,因为我们有 n 个环境的 x 个步数。

更新政策

PPO 的第二阶段是从我们收集的轨迹中学习——这由Models.py 中的 learn 函数启动,并由util.py:L113中的训练循环调用。当这开始时,首先计算优势,这一点很重要,因为所有轨迹都需要保持顺序,以计算正确的贴现回报。一旦完成,我们可以从内存中随机取样,计算我们的损失并更新模型。轨迹上的更新发生设定的次数,由超参数决定。一旦完成,旧的模型将被更新的模型所取代,这个过程将重新开始,但这次(希望)会有更好的性能。这个过程可以在图 2.9 中看到。

图 2.9-显示 PPO 学习阶段的流程图,该图将代理和内存类上调用的函数以及动作流混合在一起。(图片由作者提供)

计算优势

优势被定义为预期回报减去实际回报,这表明模型的表现比预期的好或坏。计算这些优势分两步——首先,需要计算贴现回报。然后从贴现回报中减去期望值(由评论家计算)以获得优势。

如果你不熟悉贴现回报,这是一个很棒的视频,但本质上它采用当前回报+下一个状态的值乘以一个贴现因子(γ),如图 2.10 所示(G 是贴现回报)。

图 2.10:贴现回报公式。(图片由作者提供)

为了计算贴现收益,我们不能从轨迹的起点开始,而必须从终点开始。这是因为我们需要迭代计算下一个状态的值是多少。这在最后一个状态出现了问题,因为没有下一个值,这就是为什么该函数还采用了last_valuelast_done,它们在贴现回报的第一次计算中使用(在图 2.11 的情况下,它是-0.5 作为最后一个值,这一集没有完成)。在许多书中,您可能会将此视为 n 步引导。我们不(必须)等待整个情节结束,而是在 n 步之后估计如果情节结束时该状态的值。

作为可能发生的情况的一个例子,在图 2.11 中,假设轨迹已经被收集(步骤 1),然后迭代地向后计算贴现回报。

图 2.11:贴现回报的计算示例。(图片由作者提供)

这个逻辑在我们的[Memory.py](https://github.com/DarylRodrigo/rl_lib/blob/master/PPO/Memory.py)类中实现:

def calculate_discounted_returns(self, last_value, next_done):
    with torch.no_grad():
      # Create empty discounted returns array
      self.discounted_returns = torch.zeros((self.size, self.num_envs)).to(self.device)
      for t in reversed(range(self.size)):
        # If first loop
        if t == self.size - 1:
          next_non_terminal = 1.0 - torch.FloatTensor(next_done).reshape(-1).to(self.device)
          next_return = last_value.reshape(-1).to(self.device)
        else:
          next_non_terminal = 1.0 - self.dones[t+1]
          next_return = self.discounted_returns[t+1]
        self.discounted_returns[t] = self.rewards[t] + self.gamma * next_non_terminal * next_return

reverse for 循环以相反的顺序在收集的轨迹上迭代,以计算贴现收益。如图 2.11 所示,第一个计算使用评论家输出的最后一个预测值,所有其他情况都将使用以前的贴现回报。新的折现回报是奖励+ gamma *之前的折现回报。next_non_terminal用于检查折扣是否应设置为 0,因为剧集已结束。最后,为了获得优势,内存只需在相应的时间步长上获取 discounted_returns 并减去收集的值。

计算贴现回报可能是一件令人头痛的事情,而确保在许多不同的情况下正确计算贴现回报更是难上加难。很容易犯错误,在某些情况下,这意味着算法仍然可以训练,但性能不如预期。为了真正检查所有的边缘情况,建议编写测试用例并根据它们运行计算。对于我们的实现,这可以在/tests/test_memory.py中找到。

更新模型

获取数据

为了更新模型,首先对内存进行采样,这是通过从内存中保存的轨迹中提取小批量(随机分段)来完成的。这是通过get_mini_batch_idxs功能完成的。该函数为内存中的每个条目创建一个索引,并将它们随机分配到小批量的数组中;例如,这可能会返回

[ 
	[1013,456, ... 147,328], 
	..., 
	[295, 1937, ..., 49,846]
]

然后将小批量数组([1013,456, ... 147,328])中的第一个元素传递给sample函数,该函数期望从内存中取出一个索引数组。对于该数组中的每个索引,将返回以下值:

  • prev_states
  • prev_actions
  • 上一个日志问题
  • 折扣 _ 退货
  • 优势
  • 上一个值

政策性亏损

损失函数由保单损失和价值损失组成。作为一个提醒,可能值得查看图 1.11 来记住更新 PPO 模型的方法。

首先,需要计算两个替代函数(比率的限幅)。代理函数需要概率比——这使用更新模型的对数概率和当前模型的对数概率(用于收集轨迹)。这是保存操作和观察值的主要原因-现在可以将观察值传递到更新的模型中,并计算在轨迹期间采取的操作输出了哪些新的对数概率。

一旦计算出比率,代理函数就相对简单了,我们只需乘以优势(在前一节中计算)并使用箝位函数将其限制为 1 + ϵ和 1- ϵ.那么损失是两者中的最小值。

# Grab sample from memory
prev_states, prev_actions, prev_log_probs, discounted_returns, advantage, prev_values = self.mem.sample(mini_batch_idx)# find ratios
actions, log_probs, _, entropy = self.model.act(prev_states, prev_actions)
ratio = torch.exp(log_probs - prev_log_probs.detach())# calculate surrogates
surrogate_1 = advantages * ratio
surrogate_2 = advantages * torch.clamp(ratio, 1-self.epsilon, 1+self.epsilon)# Policy Gradient Loss
pg_loss = -torch.min(surrogate_1, surrogate_2).mean()

价值损失

为了计算价值损失,我们采用我们认为的观察值的均方误差和实际的折现回报。

# Grab sample from memory
prev_states, prev_actions, prev_log_probs, discounted_returns, advantage, prev_values = self.mem.sample(mini_batch_idx)# Calculate values from new model
new_values = self.model.get_values(prev_states).view(-1)# Generate loss
value_loss = F.MSE(new_values, discounted_returns)

更新

更新也是非常标准的,我们简单地反向传播所有的损失加在一起,并包括熵。

# Loss
loss = pg_loss + value_loss - self.entropy_beta*entropy_loss# calculate gradient
self.optimiser.zero_grad()
loss.backward()
# Clip grad to avoid massive updates
nn.utils.clip_grad_norm_(self.model.parameters(), 0.5)
self.optimiser.step()

如果整个内存集的模型更新了 n 次,这个过程就会重复。然后我们再次重复整个过程,希望模型收敛!

最后

要运行我们的环境,请导航至 PPO 文件夹并运行:

python main_atari.py

要设置权重和偏差,您需要进行自己的配置并注册项目,如果没有,tensorboard 仍会记录所有的实验数据。

🕵️‍♂️分析我们的结果

运行实验时,测量的关键值是:

剧集奖励

这是要记录的最明显的指标,它告诉我们代理在给定步骤的表现如何。

学习率

记录学习率,以防其在实验的整个生命周期中发生变化。对于这个实验,它将保持不变,但是,需要退火来获得最新的结果。另一篇博客文章将会介绍这是为什么以及如何工作的。

保单损失

人们普遍认为,政策损失是一个难以得出结论的指标,这取决于优势是否正常化,或者如果使用学习率退火,这看起来会有所不同。

价值损失

这被定义为贴现回报和批评家所想之间的均方误差,它显示了批评家对一个国家价值的预测有多好。通常这个值很低,然而,当一个代理的表现比预期的好或者差很多时,价值损失就会飙升,这表明批评家正在更新它对这个状态的看法。

RL 中的熵是一个动作是否被采取的可预测性(所以我们有多确定在一个给定的状态下我们可能向左);因为动作是从分布中取样的,如果其中一个输出非常高,这意味着将采取什么动作是相当确定的。相反,如果所有的可能性都处于同一水平,那么就很难预测下一步会采取什么行动(这篇伟大的文章对进行了更深入的探讨)。

从洞察力的角度来看,这意味着如果熵非常高,代理人并不真正知道什么是正确的行动,当熵较低时,代理人开始更加确定自己的行动。这就是为什么,总的来说,熵会下降。

KL 发散

当使用政策梯度方法计算采取何种行动时,这些行动将被表示为一组概率——这些概率的对数随后被用于计算我们模型的损失。

当您在“学习”阶段使用对数概率更新模型时,通常会克隆您的模型并更新新模型。一旦你的模型经历了所有的更新,它将会与原始模型略有不同。

KL 散度是对原始模型和新模型的对数概率差异的度量。KL 背离越高,模型变化越大。根据经验,KL 偏离值在 0+/-0.03 左右意味着模型更新良好,而高 KL 偏离值可能表明模型更新步长过大,这可能导致模型不收敛。这就是 KL 散度成为如此有用的观察指标的原因。

使用指标

在查看指标时,很难确定实验中的确切问题,尤其是因为很难确定某些东西是否没有收敛,或者算法是否有问题。有一件事极大地帮助了我们,那就是根据不同的指标来寻找“平均”性能。例如,从图 3.1 中可以看出,代理人似乎在学习,但却无法收敛——但当同时查看价值损失(即贴现回报和评论家想法之间的 MSE)时,很明显,评论家的行为非常可疑,价值损失无处不在,而且经常过高。经过仔细检查,我们发现价值损失的计算中有一个错误的变量名。

图 3.1—PPO 代理的奖励(左)和价值损失(右),价值损失计算不正确。(图片由作者提供)

当我们构建我们的代理时,Costa 的 CleanRL 指标给了我们很大的帮助。

我们的指标

经过大量工作,我们设法得到了 50 英镑左右的报酬。显然不理想,还有很多工作要做,但这需要给算法增加一些额外的技巧,也很有可能在更多的训练后它会达到~400。

图 3.3:PPO 代理的所有记录指标。(图片由作者提供)

创建您自己的 PPO 时,参考这些指标可能会有所帮助。

查看代理

代理在不同培训阶段的表现的可视化显示。

0、10m、20m 步后代理。(图片作者)

🙏结束语和未来工作

从头开始实施 PPO 是一个有趣的挑战,这无疑将我对 RL 和患者的理解延伸到了极限。真正有帮助的是有人一起工作,这既是一种动力,也有助于在我们陷入困境时提出想法。

从我们的实验来看,很明显,仅仅运行一个普通的 PPO 算法不会达到顶级的性能。因此,我们也根据基线规范实施了 PPO,达到了 400 的奖励。我的目标是发布一篇博文,介绍为尽快获得结果所需的修改!

图 3.5—PPO 突破代理的培训,所有基线特征均已实施。(图片由作者提供)

PPO 突破代理正在实施所有基线功能。(图片由作者提供)

🔥承认

丹尼尔和我要感谢 Costa Huang 在解释他的 PPO 实施如何运作时所给予的帮助。没有他的帮助,这条路肯定会长得多。

RoC 和 AUC 的图解说明

原文:https://towardsdatascience.com/a-graphical-explanation-of-roc-and-auc-183705caeb27?source=collection_archive---------23-----------------------

理解阈值不变性并警惕一个常见错误

这篇文章将帮助你理解 AUC 相对于其他指标的优势,它是如何计算的(使用 RoC ),以及为什么有必要这样计算。

大自然拥有巨鸟(图片来自ykaaivu来自 Pixabay

简介

如果您已经构建了一个分类器,那么您肯定已经使用准确度、精确度、召回率或 F 值等指标来衡量模型的性能。但是这些指标中的每一个都是在定义了衡量指标的截止概率(比如 0.5)之后计算的。

AUC 相对于其他指标的优势

当你有两个竞争模型,并想比较他们的表现呢?如果模型 1 在截止值为 0.5 时表现最佳,而模型 2 在截止值为 0.4 时表现最佳,会怎样?那么只检查上面提到的指标将会给出一个不完整的视图。因此,在比较两个模型时,建议检查受试者工作特征(RoC)曲线下的面积。

这是因为 AUC 是阈值不变指标。我们的意思是,AUC 不依赖于用于计算分类性能的概率阈值。相反,我们迭代 0 到 1 之间的所有截止概率,并计算每个截止点的 TPR 和 FPR。

然后画一条曲线——FPR 对 TPR 的 RoC 曲线——并计算曲线下的面积。这是 AUC。

计算 AUC——正确的方法

我有一个分类器,可以预测下个月顾客再次购买的可能性[ 零售交易案例研究 ]。

分类器的概率输出[图片由作者提供]

我们使用sk learn . metrics中提供的 roc_curve 函数来计算 TPR 和 FPR。

Roc 在每个阈值返回 TPR 和 TPR 图片由作者提供]

不用担心 1 排 1.99 的阈值。根据 sklearn 的官方文档, thresholds[0]表示没有实例被预测,并且被任意设置为 max(y_score)+1。

计算 AUC 如下:

并将 RoC 曲线绘制为:

RoC 的正确表示[图片由作者提供]

计算 AUC 时的常见错误

很多时候,当我们太专注于编写计算 f1 的代码(或者准确度、精确回忆等)时。)喜欢:

f1 = f1_score(y_true,y_pred)

…我们最终复制了那一行,并对其进行编辑,以计算 AUC,如下所示:

AUC = roc_auc_score(y_true,y_pred)

人们忘记了 f1 使用二值化输出,而 AUC 需要模型的概率输出。因此,正确的代码应该是:

AUC = roc_auc_score(y_true,y_pred_prob)

为什么是错的?

搞乱 AUC 的阈值不变性质会怎么样?对分类器的二值化结果计算 AUC 会发生什么?

让我们用同样的例子来理解。

通过最大化 F1,我们找到了零售交易案例研究的最佳概率截止值。然后,我们将概率输出转换为二进制形式。

分类器的概率输出(L)和二值化输出(R)[作者图片]

现在计算所有可能的概率阈值下的 TPR 和 FPR..

等待..我们不是刚把输出二进制化了吗..?

这样我们只有两个概率阈值——0 和 1

所以 TRP 和 FPR 看起来像是——实际上只有两个条目——一个代表 0,一个代表 1。

二值化输出的 RoC 图片由作者提供]

RoC 图看起来像这样:

具有二值化输出的 RoC 图片由作者提供]

注意 AUC 从 74%下降到 68%。

当我们叠加两条 RoC 曲线时,差异变得明显。

由于二值化输出而丢失的区域[图片由作者提供]

蓝色和橙色线之间的区域是通过计算二进制化输出的 AUC 而损失的区域。

概括多个截止阈值

通过在多个阈值上进行二进制化,绘制二进制化输出的 RoC。然后画一条连接所有曲线拐点的曲线,我们最终得到正确的 RoC 曲线。

加入拐点得到 RoC[图片作者]

紫色线实际上是绘制在概率输出上的 RoC 曲线。

结论

我希望这些图表能阐明为什么 AUC 是根据分类器的概率输出计算的,以及我们所说的 AUC 是阈值不变的意思。

感谢阅读。想了解更多这样的数据科学知识,请阅读我的其他帖子,并在 Linkedin 上与我联系。

伟大的 Python 库:伟大的期望

原文:https://towardsdatascience.com/a-great-python-library-great-expectations-6ac6d6fe822e?source=collection_archive---------2-----------------------

总是知道从你的数据中可以期待什么

照片由托拉尔·穆罕默德在 Unsplash 拍摄

我们生活在大数据时代。万亿字节的数据不断在我们周围流动。在这个巨大的数据流中,不可避免地会出现一些问题。

与数据相关的问题是我们需要应对的现实。为了获得可靠和准确的产品,持续监控数据质量至关重要。

Great Expectations 是一个 Python 库,它帮助我们验证、记录和描述我们的数据,以便我们始终确保它是好的,就像我们期望的那样。

Great Expectations 提供了几个函数来从许多不同的角度评估数据。下面是一个快速检查列中所有值是否唯一的示例:

expect_column_values_to_be_unique函数不仅返回简单的真或假答案,还提供其他有用的信息,如所有值的数量、意外值的数量等等。

在本文中,我们将通过在一个样本数据集上实现一些很棒的函数来发现 Great Expectations 库。

我们可以轻松地用 pip 安装它。

pip install great_expectations# in a jupyter notebook
!pip install great_expectations

我们现在可以导入它了。

import great_expectations as ge

让我们从创建用于示例的数据框开始。我之前已经创建了一个填充了模拟数据的销售数据集。

需要注意的是,我们需要一个兼容的数据框架,以便能够应用远大前程库中的功能。

一种选择是使用from_pandas函数转换熊猫数据帧。

import pandas as pdsales = pd.read_csv("Sales.csv")df = ge.from_pandas(sales)

另一种选择是直接使用《远大前程》的read_csv功能。

df = ge.read_csv("Sales.csv")df.head()

df(作者图片)

id 列应该总是唯一的,重复的 id 值可能会产生严重的后果。我们可以很容易地检查该列中值的唯一性。

df.expect_column_values_to_be_unique(column="id")# output
{
  "meta": {},
  "result": {
    "element_count": 1000,
    "missing_count": 0,
    "missing_percent": 0.0,
    "unexpected_count": 0,
    "unexpected_percent": 0.0,
    "unexpected_percent_total": 0.0,
    "unexpected_percent_nonmissing": 0.0,
    "partial_unexpected_list": []
  },
  "success": true,
  "exception_info": {
    "raised_exception": false,
    "exception_traceback": null,
    "exception_message": null
  }
}

Great Expectations 库的函数返回一个包含多条信息的 json 文件。我们可以把它赋给一个变量,从中提取特定的信息。

在上面的例子中,我们实际上感兴趣的是成功值是否为真。

uniqueness = df.expect_column_values_to_be_unique(column="id")uniqueness["success"]
True

如果它是假的,那么我们应该寻找进一步的细节。

假设我们预期产品的价格在 1 到 10000 之间。让我们检查一下价格栏是否符合我们的预期。

sales_ge.expect_column_values_to_be_between(
    column="price", min_value=1, max_value=10000
)# output
{
  "meta": {},
  "result": {
    "element_count": 1000,
    "missing_count": 0,
    "missing_percent": 0.0,
    "unexpected_count": 2,
    "unexpected_percent": 0.2,
    "unexpected_percent_total": 0.2,
    "unexpected_percent_nonmissing": 0.2,
    "partial_unexpected_list": [
      0.76,
      0.66
    ]
  },
  "success": false,
  "exception_info": {
    "raised_exception": false,
    "exception_traceback": null,
    "exception_message": null
  }
}

成功是假的,有两个意外值。

我们还可以检查分类列中的值是否在给定的集合中。

df.expect_column_values_to_be_in_set(
    column = "product_group", 
    value_set = ["PG1","PG2","PG3","PG4", "PG5"]
)

我没有粘贴其余示例的输出,因为它有点冗长。不过你可以自己练习。

远大前程库还有其他几个有用的功能。这将需要写几页来做一个例子。请随意查看期望词汇表,查看所有可用的功能。

另一个我觉得很有用的功能是expect_column_values_to_be_increasing

考虑以下数据帧:

我的 _df

如果我们希望 quantity 列中的值总是增加,我们可以使用我刚才提到的函数。

my_df.expect_column_values_to_be_increasing(column="Quantity")

我们还可以基于多个列检查唯一性。例如,我们可能希望每个产品都有唯一的产品组和 id 组合。

下面是我们如何使用 Great Expectations 库实现这个控件:

df.expect_compound_columns_to_be_unique(
    column_list=["product_group","id"]
)

Great Expectations library 是用于数据质量检查的标准且易于维护的解决方案。我们只介绍了它的一小部分功能。我强烈建议查看期望词汇表。

你可以成为一名媒介会员来解锁我的作品,以及媒介的其他部分。如果您使用以下链接,我将收取您的一部分会员费,无需您支付额外费用。

https://sonery.medium.com/membership

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

如何用 FuzzyWuzzy 和 HMNI 构建模糊搜索算法的指南

原文:https://towardsdatascience.com/a-guide-on-how-to-build-a-fuzzy-search-algorithm-with-fuzzywuzzy-and-hmni-26855ce1818b?source=collection_archive---------14-----------------------

马库斯·斯皮斯克在 Unsplash 上的照片

在这篇文章中,我将指导你如何构建一个模糊搜索算法。这种算法的一个非常实际的用例是,我们可以用它来查找一个品牌的替代名称“亚马逊”,我们希望它返回字符串,如“AMZ”,“AMZN”或“AMZN MKTP”。

这篇文章的提纲如下:

  • 用 FuzzyWuzzy 进行模糊搜索
  • 用 HMNI 进行模糊搜索
  • 集成算法的模糊搜索
  • 返回一个替换名称表

用模糊不清的东西搜索

https://github.com/seatgeek/fuzzywuzzy

FuzzyWuzzy 是一个很棒的 python 库,可以用来完成模糊搜索工作。本质上,它使用 Levenshtein 距离来计算序列之间的差异/距离。

根据维基百科,Levenshtein 距离是评估将一个单词变成另一个单词所需的最小数量的单字符编辑(插入、删除或替换)的度量。这意味着 FuzzyWuzzy 中的评估指标可以很好地对拼写错误的单词执行模糊搜索,并捕获输入之间的最长公共子序列**。**

但在某些情况下,例如,品牌名称的缩写,仅仅知道字符水平的差异可能是不够的。在返回最相似的名字匹配之前,知道语音和语义的不同也是有意义的。

因此,我想介绍另一个名为 HMNI 的库,它可以帮助我们检查输入之间的语音相似性,但首先让我建立一个样本数据集,以便进行更合适的测试。

从 FuzzyWuzzy 库开始,要安装它,我们可以运行以下命令:

# Using PIP via PyPI
pip install fuzzywuzzy# Or the following to install python-Levenshtein too
pip install fuzzywuzzy[speedup]

然后,我们将继续为我们的测试创建一个样本数据集。

# Sample Dataset
df = pd.DataFrame(index =['AMAZON', 'Netflix', 'PayPal', 'Apple', 'Spotify', 'Apple', 'Facebook', 'Google', 'Starbucks'],
                  columns = ['AMZ', 'PP', 'FACEBK', 'SPTF*', 'APPL', 'STARBK', 'GG', 'Starbucks TORONTO', 'NFLIX'])# Print 
df

样本数据集

我们将行索引设置为完整的品牌名称,将列名设置为这些品牌的可能缩写。

现在,我们可以定义一个函数,将两个字符串作为输入,返回一个相似性得分作为输出。在 FuzzyWuzzy 中,我们可以使用函数 fuzz.ratio() 来计算两个输入之间的相似性得分。

# Customized similarity function with FuzzyWuzzy
def similarity_fuzzy(word1, word2):

    score = fuzz.ratio(word1, word2)

    d = score/100

    return d

现在,我们只需要在每对行索引和列名上实现 similarity_fuzzy 函数,用相似性得分替换这些 NaN 值。

from tqdm import tqdmfor i in tqdm(range(8)): # range in number of rows 

    for j in range(9): # range in number of columns 

        df.loc[df.index[i], df.columns[j]] = similarity_fuzzy(str(df.index[i]), str(df.columns[j]))

df

FuzzyWuzzy 的输出

正如我们所看到的,FuzzyWuzzy 不能很好地为输入的品牌名称找到正确的缩写。我认为主要原因是因为缩写丢失了很多字符,所以使用 Levenshtein 距离的度量不是这种情况下的最佳解决方案。

这就是我认为计算语音相似度的新视角会有很大帮助的地方!

用 HMNI 搜索

https://github.com/Christopher-Thornton/hmni

一般来说,HMNI 是一个遵循应用软逻辑来近似拼写和语音(声音)特征的认知过程的库。

一篇值得探索的好文章:

为了测试 HMNI 的模糊名称匹配性能,我们可以遵循与 FuzzyWuzzy 相同的步骤。

要安装 HMNI:

# Using PIP via PyPI
pip install hmni

要初始化匹配器对象:

import hmni
matcher = hmni.Matcher(model='latin')

要自定义我们的相似性函数:

def similarity_hmni(word1, word2):

    d = matcher.similarity(word1, word2)

    return d

然后,我们可以在同一个样本数据集上测试 similarity_hmni 函数来比较性能。

HMNI 的产出

FuzzyWuzzy 和 HMNI 之间的区别非常明显。HMNI 似乎更善于根据潜在的语音特征找到品牌输入的缩写。

但这并不意味着使用 HMNI 没有坏处。例如,看看“贝宝”和“苹果”,我们发现 HMNI 往往不善于区分这两个品牌,因为相似性得分分别为 0.71 和 0.41 以及 0.66 和 0.94。如果我们向数据集中添加更多的输入,这可能会造成一些混乱。此外,对于“Starbucks”和“Starbucks Toronto”之间的精确匹配,HMNI 应该对其预测更有信心,但现在它只返回值 0.5。

使用集成算法进行搜索

这可能意味着我们应该考虑整合两个维度,语音相似性和 Levenshtein 距离,以实现最佳平衡。

我对此的解决方案很简单。只需将两个函数添加到一个新函数中,我们将调整权重以确定最终的输出分数。

def similarity_calculator(word1, word2):

    score_1 = fuzz.ratio(word1, word2) # score from fuzzywuzzy

    score_2 = matcher.similarity(word1, word2) # score from hmni

    score_1 = score_1/100

    score = 0.2*score_1 + 0.8*score_2 # customize your own weights 

    return score

集成算法的输出

通过将这两个函数整合在一起,我们似乎达到了一个平衡,即我们可以将 60%设置为一个阈值来区分匹配和不匹配。除了星巴克,我们可能应该使用直接搜索这些大品牌。python 中的 find() 函数。

最后一步

现在,剩下的工作是为品牌输入创建一个顶级备选名称匹配表。对于样本数据集,我选择只返回具有最高相似性得分的最佳匹配。代码如下所示:

# Return a new column 'Max' that contains the alternative names for each brand input
df['Max'] = df.astype(float).idxmax(axis=1)# Create a new dataframe 'result' to display only the input & output columns
result = pd.DataFrame(list(df.index), columns=['Input'])
result['Alternative Names'] = list(df.Max)
result

替代品牌表

您的最终输出将类似于上面的数据集。

感谢您的阅读。我希望这篇文章能对那些正在寻找如何用机器学习构建模糊搜索算法的人有所帮助。

我认为这篇文章可能是你开发自己的模糊搜索算法的一个很好的开始:)

再次感谢!

下篇见~

人工智能敏捷数据控制指南

原文:https://towardsdatascience.com/a-guide-to-agile-data-mastering-with-ai-3bf38f103709?source=collection_archive---------24-----------------------

主数据为事务和操作提供了通用词汇。

什么是主数据?

组织需要处理各种数据,包括事务性数据、非结构化数据、分层数据、元数据和实体数据。例如,对于客户,组织会生成交易、客户账户信息(包括人口统计数据)、营销接触点和互动等数据。所有这些数据都是作为分散在不同数据应用程序中的零星数据点收集的。

常见的业务实体——人、事物、地点和概念是主数据。主数据定义了顶级业务对象。根据 Gartner 的调查:

主数据是一组一致且统一的标识符和扩展属性,用于描述企业的核心实体,包括客户、潜在客户、公民、供应商、站点、层次结构和会计科目表

主数据支持企业间的信息共享。它为事务和操作提供了通用的词汇表。它是构建所有业务应用程序的基础。

数据孤岛

让我们看一下组织中存储和管理数据的通常方式。

随着公司的发展,其数据系统也在发展。为了照顾一个业务线,或者管理一个产品,或者处理一个新的领域,一个团队的人一起工作。现在,这个团队需要特定的工具来保存和操作他们的数据。比起公司销售人员,他们可能更喜欢自己开发的 CRM 或电子表格。对于他们特定的产品线,他们可能会构建一些定制的应用程序。他们的供应链系统可能与总部的不同。在少数情况下,他们也可以通过公司的工具和系统。所以是混搭。

这一切都很好,他们需要完成工作。他们需要遵守当地法规。他们必须迎合他们的观众,并交付成果。将数据置于自己的控制之下,使他们能够快速行动,快速响应不断增长的业务需求。他们可以用对他们最有意义的方式来表示实体。内部 IT 或合同工程师,很可能是两者的结合,帮助他们运行和管理这些系统。

大公司也和其他大公司合作。他们收购或合并,为他们的数据系统鸡尾酒添加更多的组合!

概括起来

  • 业务单位和部门构建并使用专门的应用程序
  • 数据保存在这些独立的应用程序中
  • 这些孤岛对于运营效率至关重要

数据孤岛是孤独的战士。

马特·斯卡兰迪斯在 Unsplash 上拍摄的照片

什么导致了数据孤岛?

数据孤岛可能是多种因素造成的。

结构的

部门角色和职责以及公司的结构方式通常会导致数据孤岛。

地理学的

除此之外,地理边界和法规(例如数据位置)也是一个因素。不同的国家有不同的做生意的方式,像在印度,手机信息是非常普遍的,所以客户记录可能会有。但是在美国,人们很注重隐私,不喜欢轻易分享他们的手机信息。此外,每个国家都有法规要求,因此业务和数据都围绕这些要求构建。数据也可以用地区语言显示。

技术的

大多数应用程序,包括 ERP 和 CRM,都不能很好地进行集成,所以这些专用系统也导致了数据孤岛的技术原因。您可能喜欢对数据进行分区——比如将数据分割成一个区域,以便更好地管理它。因此,这也可能是数据仓库的一个技术原因

数据仓库的示例

客户数据孤岛

这是一个客户数据如何被孤立的例子。以下是一些包含客户相关信息的企业应用程序

  • Web 数据存储
  • 店内数据
  • 产品级数据存储
  • 电子邮件营销数据
  • 遗留系统
  • 通过 M&A 的客户数据

在上面的客户数据仓库的例子中,多个系统保存着客户旅程和与企业交互的片断。这通常不仅仅是这些系统。回到 2017 年,普通营销部门使用 16 种不同的技术平台。

采购数据仓库

典型的采购流程涉及多个利益相关方和系统。供应商在 ERP 中注册,然后在另一个系统中提交并批准规格。对供应商场所和合规性的检查由另一个团队使用他们的流程和应用程序数据库来完成。签约和法律部门参与其中,他们带来了额外的数据和见解。批准供应商供应品的交付团队是他们系统的另一个参与者。

  • 供应商注册
  • 需求规格
  • 检查
  • 合同
  • 交付

数据控制

人们意识到,当他们组合这些单元数据系统或数据孤岛时,他们可以从他们的数据中获得更大的价值。以客户数据为例。我们可以跨多个产品线连接和合并客户数据,并了解买家倾向。我们可以发现交叉销售和追加销售的机会。我们可以建立个性化的沟通和有针对性的渠道。公司可以更快、更准确地搭载客户。我们可以提高销售效率,通过他们喜欢的渠道提供他们想要的产品和服务,让客户高兴,只需对他们有更多的了解。

我们还可以管理风险和法规遵从性—了解他们与客户或家庭(比如保险)相关的风险有多大。或者说哪些系统存储了 GDPR 主题访问请求(SAR)的客户数据。

跨数据仓库的供应商数据整合产生了关于跨业务单位、区域和地理位置的供应商支出的极好的洞察力。公司可以了解跨部门的价格一致性,更快地接纳新的供应商,并管理与供应商位置和劳动实践相关的风险。

类似地,集成其他数据资产(如供应品)有助于跨部门共享它们,或找出给定供应品的最佳供应商,或推动业务部门之间的定价一致性。整合数据仓库以了解客户、产品、供应商、合作伙伴、供应品、员工和其他业务实体会产生巨大的价值。

数据掌握本质上是相信

整体大于部分之和

粘土银行在 Unsplash 拍摄的照片

**数据控制也称为主数据管理或 MDM。

数据控制的好处

整合不同行业的数据仓库具有巨大的价值。重要的是要明白,我们并没有打破这些数据孤岛。数据孤岛按原样存在。我们希望他们留下来——保持他们的功能,保持他们的敏捷。让各自的团队来管理和拥有它们,并在保持绩效的同时继续推动业务发展。通过数据控制和运营数据,我们还试图看到整体情况,并用整体的组织视图来增强我们的焦点视图。通过这种方式,我们可以利用从统一和集成的数据中收集的见解来增强我们已经很聪明的团队。职能团队可以使用来自 MDM 的信息,更有效地运营、节省成本、降低风险并保持合规性。

实际上,我们正在利用每个孤岛中的集体智慧,对可信数据做出明智的选择。

数据控制系统的关键部件

为了构建实体的统一视图,MDM 系统集成了数据仓库,并将数据集中在一个地方进行处理。然后统一它。数据主控制系统帮助企业拼凑一个实体,比如说一个客户,将它定义为名字、姓氏、电子邮件、电话号码、组织、地址、头衔等属性的组合。

用于数据控制的模式映射

一旦实体被定义为属性的集合,来自不同筒仓的属性被映射到该实体。有些映射很简单,你只需告诉系统 silo1 中的 last_name 可以映射到 lastName,或者 silo2 中的 lName 可以映射到 lastName。在某些情况下,我们需要对数据进行一些准备或转换,比如将数据 silo 3 中的 first_name 和 last_name 连接起来,并将其映射到统一客户数据的 customer_name 属性。我们可能还需要清除一些包含错误值的记录,如 NA 或空白,或出生年份的 1900,因为我们不希望我们的输出被破坏或出错。

数据匹配

在模式映射之后,数据主控系统将来自不同筒仓的实体的数据分组在一起。人们以多种方式将数据输入他们的系统,许多数据记录可能来自合作伙伴或客户提供的值。因此,在拼写错误、称呼、缩写、语言等方面有很多变化。因此,系统允许您配置对哪些记录进行分组或匹配,以代表一个客户或产品或供应商。

在一个基于规则的系统中,这些规则是由 IT 和业务协作手工制作的,并定期调整以避免命中和失误,并收敛到期望的结果。

数据母带——黄金唱片

一旦一个实体的数据被分组,我们就想要构建一个真正的价值——用数据管理的术语来说就是一个黄金拷贝。我们定义如何在对组有贡献的源系统中挑选一组有代表性的属性。对于电子邮件数据,我们可以更信任 CRM 这样的源系统,只有当应用程序数据库中的地址在过去 3 个月内进行了更新时,我们才更有信心。如果源系统在与组匹配的记录中有许多空白值,我们可能希望忽略其中的任何值。黄金记录可以暴露给数据仓库,他们可以使用它来丰富他们的源系统,并收集更多关于他们的数据的信息。

等级制度

理解关系、一个实体如何与其自身以及其他实体相关联是至关重要的。为记录分配层次结构或类别有助于我们做到这一点。一些分层数据可能是特定于组织的,例如谁向谁报告。但是在许多情况下,特别是对于零件、服务和供应品,有像 UNSPSC 或 eClass 这样的分类法来定义它们所属的类别。大多数大型组织也有完整的内部分类方案,很多时候不止一个。

数据控制系统允许我们定义自定义层次结构或选择标准层次结构并为其分配记录。这样,我们可以了解关系,并将属于同一组的不同记录组合在一起。例如,通过层级管理,我们可以说我们在电气设备或文具项下采购了多少物资。

数据控制流程

让我们试着理解一个典型的主数据管理系统是如何工作的。

作者图片

获取数据

主数据管理系统连接到数据仓库,并将数据放到一个中心位置进行进一步处理。

按摩

这一步包括模式映射、数据标准化和规范化。为了将记录集成在一起,我们需要输入记录具有相同的字段集。我们还需要字段以同样的方式表示在其中捕获的信息。例如,在所有记录中,像性别这样的类别应该是男性/女性或 1/2。

相称的

协调多条记录以表明它们中的哪些属于同一个实体称为匹配。让我们看看需要匹配的来自 3 个不同源系统的客户记录。

作者图片

在数据匹配期间,这 3 个在所有属性上都有变化并且没有公共标识符的记录将被分配一个匹配标识符或聚类标识符,以指示它们相互链接。

合并

一旦记录被聚集,使得属于一个实体的记录具有唯一的标识符,我们就可以将它们组合起来,以构建一个干净、统一和可信的视图。这也被称为金唱片。

出版

分析、报告、合规、风险和运营等下游应用程序需要针对各自的用例访问主实体。记录的这种发布使筒仓引用相同的数据属性并改进它们的功能。

数据控制的挑战

总的来说,当我们谈论集成时,有很多事情需要考虑端到端数据控制系统

赞助

赞助挑战来自于谁赞助数据控制项目。应用程序共享数据并反过来获得丰富的数据,这是业务优势还是纯技术优势?典型的主控优势分散在各个业务部门,因此预算成为一个大问题。

所有权

哪个部门负责构建 it 和管理主控解决方案—是为整个企业提供服务的 it 部门吗?将使用数据的是业务部门吗?由于主数据涉及销售、营销、运营、支持、采购、合规和风险,因此建立一个能够平衡每个团队的个人需求的明确所有者非常重要。通常,所有者是组织中的高层人员,有权并有义务在整个公司范围内推动主数据管理计划。

协调

所有利益相关者和系统需要相互协调,以确保数据管理无缝进行。

管理

许多数据是敏感的,或者不需要暴露给每个个人或团队。因此,当我们统一和掌握数据时,正确的访问控制至关重要。此外,确定每个数据集的所有者也很重要。

过程

跨地域的部门间协调需要什么样的流程?主数据管理系统如何影响企业当前的工作流程?

因此,它是所有权、协调、治理、过程的组合,这些必须在数据控制中结合在一起。

技术的

数据格式

要掌握的应用程序以不同的格式和数据存储来保存实体信息。主数据系统可能需要使用关系数据库、NOSQL 数据存储、云存储和本地文件系统的混合数据。格式可能从专有到 JSON、XML、CSV、Parquet、Avro 等等。

数据源中的模式变化

实体表示通常因系统而异。一个源系统中的 name 属性在另一个源系统中可能被分为名字、中间名和姓氏。即使属性相同,列命名也可能不同,例如,firstName、fName、first_name 都可以表示名字。同样,每一个类别也可能不一样。男性和女性的记录可以在一个系统中标记为 M 和 F,在另一个系统中标记为 1 和 2。

数据匹配

计算机理解相等或比较,但不理解模糊匹配。匹配具有属性差异且没有单一公共标识符的记录是非常具有挑战性的,因为没有现有的操作员可以做到这一点。因此,在数据匹配系统中有 3 个主要挑战

匹配定义

我们需要非常小心匹配过程,以及如何定义哪些属性和记录确实可以说是相同的。错别字、前缀、后缀、空白值、额外字符和缩写是需要考虑的一些常见变化。

音阶

一旦建立了相似性的概念,我们需要将每条记录与其他记录进行比较,计算的次数会随着记录数量的增加而成平方增加。下表列出了单个属性记录所需的比较。

作者图片

随着数据量增加 10 倍,比较的次数增加 100 倍,使得数据匹配成为一个具有计算挑战性的问题。

品种

企业需要处理不同种类的数据,所有企业都需要掌握这些数据。客户、供应商、产品、员工和供应品都有不同的属性,因此我们需要相应地关注每个领域。

语言

大型跨国公司以地区和本地语言保存数据。由于每种语言都有其复杂性,基于声音或字符串相似性来匹配记录在这里变得具有挑战性。例如,中国人的名字是短字符串,即使在完全不同的名字中也有许多相同的字符。

层级管理

这个世界是建立在关系之上的。客户有家庭、雇员、雇主和其他关系。他们处理特定的产品和服务。供应商有本地、地区和全球办事处。组织有多个地址—法律地址、帐单地址、送货地址对他们来说都可能不同。所有主数据都需要在 MDM 系统中有效地建模。

当前主数据管理工具的评论

多个行业都广泛需要主数据管理。每个与客户或供应商打交道的大型或小型组织都有一些不同的运营系统,如 ERP、CRM、PoS 和应用程序数据库,最终都必须有一个主数据管理系统来建立单一的真实来源。因此,大多数现有的主数据管理软件都提供了通用的工具和库,以满足它们所服务的不同行业的需求。这允许跨不同的域部署主数据管理工具。然而,有许多条件逻辑需要针对每个领域和行业进行调整。

企业软件传统上是围绕关系数据库构建的。在三层体系结构中,有一个基于 web 的或胖客户机,一个中间层有应用程序逻辑和关系数据库中的数据持久性,大多数主数据管理系统都是基于规则的。基于规则的主数据管理面临着巨大的规模挑战,既包括数据源和属性的数量,也包括数据匹配所需的大量比较。想象一下,试图掌握 100 个有 10 个属性的源系统。仅通过手动操作进行模式映射就需要花费几个月的时间。

类似地,想象建立实体匹配的规则。照顾不同属性的变化是一项痛苦而艰难的工作。此外,由于这是一项计算量很大的工作,我们希望避免将所有内容进行比较,因此传统的基于规则的主数据管理系统遵循一个繁琐的过程,即数据分析、标准化、规范化、模糊键定义、相似性规则定义和数据库调整。

为了确保我们可以通过传统的匹配来匹配记录,我们尝试将它们都放在同一个数据库中。进行数据分析是为了了解属性特性和缺失值。属性是标准化的,因此它们以相同的方式表示信息,例如,如果 1 在一个数据源中表示女性,而在另一个数据源中表示 F,则它们都将更改为 1 或 F 或女性。发布此消息,通过将字段值更改为相同的大小写来规范化值,删除特殊字符,将 st .更改为 Street,反之亦然。再次分析数据为我们提供了模糊匹配键,这实质上是一种将记录分组到存储桶中的方法,以便只比较存储桶中的记录。相似性规则是在更清晰的数据上定义的。由于所涉及的计算,即使在定义了模糊匹配键之后,也需要进行大量的数据库调优。

由于配置和实施中的所有这些工作流,主数据管理部署的典型成本为数百万,实施成本是基本许可证成本的 4 倍。这将导致长达数年的漫长部署周期。很多主数据项目拍预算,无法掌握每一个系统。添加新的数据控制系统既费钱又费时,需要几个月的努力。除此之外,基于规则的主数据管理系统还存在性能和可伸缩性问题,数据库调优周期长,并且无法处理数百万条记录。

MDM 中的 AI

什么是人工智能

人工智能一词被广泛用于各种各样的应用。有关于人类智能、感知和判断被机器模仿的讨论。通过人工智能技术在音频、视觉和文本分析方面的许多令人兴奋的突破为我们打开了无限的可能性。机器学习是人工智能中的一套工具和技术,是一个充满希望的领域,其中一套算法从数据中导出模式并进行推理。机器学习可以在我们已经有很多我们知道的数据点的情况下被监督,或者在我们没有结果,只有输入数据点的情况下被监督。

广义地说,在监督机器学习过程中,我们从我们已经知道结果的数据中学习。哪些记录属于同一个供应商?哪一项记录是文具消费?这个输入数据集称为训练数据集。接下来,我们从这些数据中构建表示。这被称为特征工程。假设我们有一对要匹配的记录。一个特征可以是公共字符的数量。或者对于花费分类,它可以是供应品的话。

优化模型/算法以尽可能接近训练数据集的结果。最终,当我们对模型的性能感到满意时,当我们知道预测是准确的并且错误率较低时,我们可以将它转移到生产中。我们可以构建一个数据管道,从原始数据中获取特征,应用模型并获得预测。现在好的方面是,如果我们在选择人工智能模型和构建我们的特征方面做得很好,我们就不需要数据中每个变化的代表性样本。这就是规则失效、人工智能大放异彩的地方。它可以概括。有时它过于一般化,但是有一些技术来平衡它。

MDM 中的 AI

由于所涉及的复杂性和规模,在 MDM 中使用 AI 使我们能够以更低的成本更快地获得结果。数据控制是企业数据堆栈的重要组成部分,通过利用人工智能,我们可以消除旧 MDM 技术的许多令人痛苦的方面,并用最先进的学习系统取而代之,这些学习系统部署速度快,规模大。

利用人工智能实现大规模敏捷数据控制

当我们审视数据主管理系统的核心时,我们意识到,自传统主数据管理系统建立以来,技术领域已经发生了巨大的变化。ETL 是一个非常成熟的领域,开源技术如 Kafka、Change Data Capture、Apache Nifi 等大量涌现,它们可以帮助我们将数据导入系统或从系统中导出数据。每个云提供商还提供其数据摄取和提取工具套件。因此,我们只需要关注核心数据按摩、匹配和治理。

敏捷数据控制专注于解决核心主数据功能,让我们使用我们已经熟悉的最先进的技术来提取和加载数据。通过使用人工智能,敏捷数据控制使我们能够快速构建 MDM 系统,推荐操作并增强我们的主数据工作流。AI 还允许此类工具通过自动分析和学习数据试探法进行扩展,消除脏数据的噪声,以便可以学习模式映射或模糊匹配键,并且只需要最少的数据按摩和零标准化。

作者图片

实验数据分析指南

原文:https://towardsdatascience.com/a-guide-to-analyzing-experimental-data-7b05eea7e70c?source=collection_archive---------17-----------------------

动手教程,思想和理论

如何克服创业恐慌,并从你的实验中获得有见地的结果

由卢卡斯·布拉塞克在 Unsplash 上拍摄

你曾经运行过实验研究,或者执行过一些 A/B 测试吗?如果有,你应该熟悉分析前的恐慌:如何让数据揭示你的实验是否起作用?在经济学、公共政策、市场营销和商业分析领域,我们每天都面临着来自进行实验分析实验结果的挑战。

作为研究人员——我们自己也在为干净高效的实验工作流程而奋斗——我们决定与您分享一份实用指南,其中包含您想要分析实验数据时需要遵循的所有步骤。我们不能保证旅程会很短,但我们保证会很有趣!

像往常一样,你可以在其 Github 库中找到本指南中使用的所有代码。

谁能从本指南中受益?

本指南专门开发了一个协议用于实验数据的分析,如果你经常发现自己在笔记本电脑前一片空白,它会特别有帮助。

我们将简要描述什么是实验,以及为什么——如果设计得好——它能克服观察性研究的常见问题。之后,我们将使用 Qualtrics 上的简单实验流程生成模拟数据(使用哪种软件生成数据并不重要)。

最后,我们将编写一个即用且相当灵活的 R 代码来应用于您的实验数据。特别是,我们将强调您心目中的标准实验分析与为实现您最想要的输出而编写的代码之间的映射。

理想情况下,任何进行或计划进行实验的人都可以看看我们的指南!

实验流程

与观察数据相比,使用实验的主要优势在于,精心设计的实验可以让你测量 因果关系

确保因果效应估计的实验的主要特征是人们被随机分配到一个实验条件下。这种特征称为“随机化,它防止具有某些特征的人自行选择进入治疗组和对照组。

治疗组和对照组遵循相同的实验流程,但与对照组相比,治疗组的参与者面临不同的步骤:对自变量操纵

通过比较治疗组和对照组的结果变量如何变化,我们将评估自变量操作是否会对结果变量产生因果关系

现在,让我们编码吧!

对于这个分析,我们将使用几个 R 包来导入、整形、可视化和分析我们的(模拟)实验数据。记住:如果你还没有安装软件包,使用代码“install.packages()”来安装,如下面注释行所示。

# install.packages('packagename')library(tidyverse);library(data.table) # data wrangling and reshaping
library(ggplot2) # plotting
library(QuantPsyc) # To generate results
library(grf)
library(viridis) # For esthetics
library(stargazer) # optional, for reporting

第 0 步:导入并检查数据

我们将在本教程中使用的数据是由 Qualtrics 生成的,这是一个用于设计问卷和实验调查的流行网站。

我们基于前面描述的流程开发了一个实验调查。然后,为了我们的分析目的,我们生成了 500 个自动(“测试”)响应

我们下载了 CSV 格式的测试响应,现在将它们导入到 r 中。我们还重命名最重要的变量,并将数字变量转换成——嗯,数字量:

dt <- fread('ExperimentalAnalysis_TestData.csv', stringsAsFactors = F, header = T) %>% as_tibble()dt <- dt %>% 
  rename(Treatment = FL_8_DO, 
         Outcome_T = Q3_1, 
         Outcome_C = Q7_1,
         Initial_Beliefs = Q15_1,
         AttnCheck = Q16,
         Age = Q8, 
         Nationality = Q9, 
         Internet_use = Q10,
         Last_election = Q11, 
         Which_politician = Q12)dt <- dt %>% 
  mutate_at(.vars = vars(Outcome_T,Outcome_C,Initial_Beliefs), .funs = as.numeric)

我们将使用**"tible"数据结构**,因为我们的数据集相当小,tible 允许我们直接在控制台中很好地预览数据。对于较大的数据集(即超过几百万行的数据集),您可能希望切换到“ data.table ”结构。

实际上,你可能不仅要处理不同的数据大小,还要处理不同的软件,或者成百上千的响应——这没多大关系,只要你能导入 R 中的数据矩阵,并从数据中恢复治疗控制结果变量。

初始检查

在第一次检查时,我们需要删除对我们的分析没有用的前两行(slice 函数)。但是,我们将保存第一行(slice(1)),因为它包含了变量的描述。

dt
colnames(dt)dt_question_labels <- dt %>% slice(1)dt_question_labels <- dt_question_labels %>% 
  t() %>%
   as.data.frame() %>% 
   mutate(Label = rownames(.))(dt <- dt %>% 
  slice(-c(1,2)))

未完成病例

我们需要确保我们只分析来自完成调查的回答者的数据。如果您注意到样本中的许多受访者没有完成调查(通常,约 5%的遗漏情况是可以接受的),这可能是您的调查、您的导入程序或两者都有问题的迹象。

检查分配到治疗组或对照组的参与者是否有相同的完成率是一个好的做法。如果不是这样,你的实验的内在有效性可能会有危险。

dt %>% 
  group_by(Finished) %>%
  summarise(n = n())# If you have 'False' in the result, kick out those peopledt <- dt %>% 
  filter(Finished == 'True')

测试响应

我们还需要删除在任何“预览模式或“测试”模式中给出的答案,因为这些答案不是从您的实验样本中生成的。(注意:在我们的例子中,所有答案都是在“测试”模式下生成的)。

dt <- dt %>% 
  # filter(!(Status %in% c('Survey Preview', 'Survey Test')))
  filter(!(Status %in% c('Survey Preview')))

遗漏答案

我们需要检查治疗或结果变量中是否有缺失数据。在这里,您可以找到治疗或结果中有缺失数据的应答,并识别应答者 ID。由于没有治疗方法,这些反应是无效的。

如果发现数据缺失,需要尽快了解数据缺失的原因。这可能是一个技术错误,或者是您的调查存在系统问题。

(test_NA <- dt %>% 
   # select respondent ID's, outcome variables, and treatment indicator
  select(ResponseId, Treatment, Outcome_C, Outcome_T) %>% 
   # mutate everything into numeric
   mutate_at(., vars(Outcome_C, Outcome_T), as.numeric) %>% 
   mutate(Treatment_numeric = as.numeric(as.factor(Treatment))) %>% 
   # if a cell is empty/missing, flag with NA
   mutate_all(., function(x) ifelse(x == '', NA, x)) %>% 
  bind_rows(tibble(Outcome_C = NA, Outcome_T = NA, Treatment_numeric = NA)) %>% 
  rowwise() %>% 
  mutate(sum = sum(Outcome_C, Outcome_T, Treatment_numeric, na.rm = T)) %>%
  filter(sum == 0))# If ResponseID is equal to NA, then you're fine and you can skip this part.# if you have missing data, you can collect the ID's here:if (sum(test_NA$sum != 0)) {

  message("Your data has missing values! Find the cause ASAP.")

  Missing_ids <- dt %>% 
  select(ResponseId, Treatment1, Treatment2, ControlGroup) %>% 
  bind_rows(tibble(Treatment1 = NA, Treatment2 = NA, ControlGroup = NA)) %>% 
  rowwise() %>% 
  mutate(sum = sum(Treatment1,Treatment2,ControlGroup, na.rm = T)) %>%
  filter(sum == 0)

  Missing_ids <- Missing_ids$ResponseId

  # and remove them from the sample

  dt <- dt %>% 
    filter(!(ResponseId %in% Missing_ids))

}

注意力检查失败

我们还需要排除未通过注意力检查的回答者(在我们的数据集中标记为 AttnCheck)。在我们的模拟样本中,339 个机器人没有通过注意力检查。我们剩下 161 个可用的响应。

同样,为了保持研究的内部有效性,检查特定类别的人是否没有通过注意力检查是一种好的做法。

# How many people failed the attention check?
dt %>% 
  filter(AttnCheck != 'A') %>% 
  tally()dt <- dt %>% 
  filter(AttnCheck == 'A')

离群值

作为对我们数据的最后一次健康检查,我们将检查一些回答者是否花费了异常长的时间来完成我们的调查。我们采用一个任意但常见的阈值:我们将标记并排除完成时间大于或小于平均值的 3 标准差的回答者。

在我们的测试数据中,6 个机器人的完成时间大于或小于平均值的 3 个标准差。

# How many outliers are there?
dt %>% 
  filter(`Duration (in seconds)` < sd(`Duration (in seconds)`)*3 & `Duration (in seconds)` > -(sd(`Duration (in seconds)`)*3)) %>% 
  tally()dt <- dt %>% 
  filter(`Duration (in seconds)` < sd(`Duration (in seconds)`)*3 & `Duration (in seconds)` > -(sd(`Duration (in seconds)`)*3))

结果变量

如果在对 Qualtrics 调查进行编码时,您分别测量了治疗组和对照组受试者的结果变量,那么我们还需要添加一个单独的列,其中包含两个结果的值。

dt <- dt %>% 
  rowwise() %>% 
  mutate(Outcome = sum(Outcome_T, Outcome_C, na.rm = T))

控制变量

在我们最后的数据准备步骤中,我们将分类变量转换成因子。这一步并不是绝对必要的,但它将有助于数据的可视化和分析。

dt <- dt %>% 
  mutate_at(c('Age', 'Nationality', 'Internet_use', 'Last_election'), as.factor)

随机化的完整性

我们需要确保两组(治疗组和对照组)的参与者平均拥有相似的特征——换句话说,参与者被随机分配到实验条件。

我们可以运行双样本独立 t 检验来评估二元变量和连续变量 在不同条件下是否不同。

当变量为分类时,我们将使用卡方检验来比较这些变量在不同组之间的差异。例如:

chisq.test(dt$Treatment, dt$Age, correct=FALSE)

如果这些检验的 p 值高于 0.05,那么我们可以假设随机化起作用了。

步骤 1:数据可视化

在正式分析实验数据之前,重要的是我们将它可视化。视觉化是一个强大的工具,可以发现任何不可信的情况——比如失败的随机化,失败的操纵,或者天花板和地板效应——并且对效应的方向有一个初步的感觉。

首先,我们将可视化结果变量在整个实验条件下的分布。我们对分布的一些特定时刻特别感兴趣,比如平均值和中位数。

dt %>% 
  group_by(Treatment) %>% 
  summarise(mean_T = mean(Outcome),
         median_T = median(Outcome))dt %>% 
  group_by(Treatment) %>% 
  mutate(mean_T = mean(Outcome),
         median_T = median(Outcome)) %>% 
  ggplot(aes(x = Outcome)) +
  geom_histogram() +
  geom_vline(aes(xintercept = mean_T), color = 'red', linetype= 2) +
  geom_vline(aes(xintercept = median_T), color = 'blue', linetype= 2) +
  facet_wrap(.~Treatment) +
  theme_bw() +
  labs(caption = 'Red line: mean; Blue line: median')

从分布中我们可以看到,在这个模拟数据集中,结果变量平均值在治疗组中(51.53)略高于对照组(47.95)。

或者,我们可以按组直接绘制平均值,以及平均值的自举标准误差**:**

dt %>% 
  group_by(Treatment) %>% 
  mutate(mean_T = mean(Outcome),
         median_T = median(Outcome)) %>%
  ungroup %>% 
  ggplot(aes(x = Treatment, y = Outcome)) +
  stat_summary(fun.data = mean_cl_boot) +
  theme_bw()

我们还可以检查具有不同背景特征的人之间的结果如何变化。例如,让我们看看年龄:

dt <- dt %>%
  mutate(Age = factor(Age, levels = c('18 or younger', '19-24', '25-35',
                              '36-45', '46-55', '56 or older')))
dt %>%
  ggplot(aes(x = Age, y = Outcome, color= Treatment)) +
  stat_summary(fun.data = mean_cl_boot, position = position_dodge(width = .5)) +
  theme_bw() +
  scale_color_viridis(discrete = T) +
  coord_flip()

第二步:分析

如果到目前为止所有的检查都给了我们关于随机化的信心,那么比较治疗和对照之间结果变量的变化将会给我们一个因果效应。****

非参数分析

如果我们不能安全地假设我们正在分析的数据遵循正态分布,我们需要使用非参数检验来研究治疗组和对照组之间的平均结果变量如何变化。服务于此目的的测试是威尔克森测试

参量分析

如果数据具有正态结构(参见 Jarque-Bera 检验),我们可以使用独立的双样本 t 检验来比较治疗组和对照组的结果变量。

t.test(dt$Outcome[dt$Treatment == 'Control'],dt$Outcome[dt$Treatment == 'Treatment1'] , alternative = "two.sided", paired = F)

除了 t 检验之外,我们还将对一个常数自变量(治疗)——我们将把它转换为一个二元变量,如果参与者属于治疗组,则值为 1——以及所有其他控制变量——执行一个线性回归结果变量。

推荐使用回归分析有两个原因:

  1. 评估治疗是否对结果变量有任何影响,保持其他一切不变
  2. 如果随机化没有正常进行,评估治疗结果变量的影响。
fit <- lm(Outcome ~ 1 + Treatment + Age + Internet_use, data = dt)
summary(fit)

步骤 3:替代(非参数)分析

我们还建议您尝试使用广义随机森林来估计异质性治疗效果。当你的数据的不同阶层之间的不同时,治疗效果是不同的——换句话说,当的治疗效果在不同的回答者子群之间有很大差异时。****

为了使用 GRF 估计异质性治疗效果,我们需要将治疗(W)、结果(Y)和异质性变量(X)分别编码为三个独立的矩阵。

W <- dt[, 'Treatment'] %>%
  mutate(Treatment = ifelse(Treatment == 'Treatment1', 1, 0)) %>% 
  as.matrix()X <- model.matrix(~ 0 + ., dt[c('Age','Nationality', 'Internet_use')])Y <- dt %>%
  select(Outcome) %>%
  as.matrix()cf <- causal_forest(X, Y, W)

最后,我们可以估计全样本的条件平均处理效果(CATE),以及处理样本的条件平均处理效果(CATT)。

average_treatment_effect(cf, target.sample = “all”)
average_treatment_effect(cf, target.sample = “treated”

结论

如果你做到了这一步:祝贺你完成了对实验数据的分析!

你会有什么不同的做法?你会在工作流程中增加哪些重要的步骤?请在评论中告诉我们。

关于作者

弗朗切斯科·卡波扎是伊拉斯谟经济学院的博士生。他利用在线和实地实验来理解人类对政治和劳动的选择。

Martina Pocchiari 是伊拉斯谟大学鹿特丹管理学院的博士生。Martina 进行实验室和实地实验,研究人们在网络社区中的行为。

Python 中的 Args、Kwargs、打包和解包指南

原文:https://towardsdatascience.com/a-guide-to-args-kwargs-packing-and-unpacking-in-python-393095dda89b?source=collection_archive---------5-----------------------

这些强大的编码工具如何实现功能灵活性

作者:爱德华·克鲁格和道格拉斯·富兰克林。

照片由 Michael Dziedzic 在 Unsplash 上拍摄

介绍

在讨论 Python 函数时,我们都听说过参数关键字参数(args 和 kwargs) 。参数通常由数值组成,而关键字参数,顾名思义,是语义性的。在编写函数时,*args**kwargs经常被直接传入一个函数定义。

由于函数定义中使用了星号,因此该函数可以处理任意数量的 args 和 kwargs。这些星号是打包和解包操作符。

运行该文件时,会生成以下输出。

注意,第 5 行的参数,两个 args 和一个 kwarg,根据它们的类型被正确地放入 print 语句中。同样值得注意的是输出中的圆括号和花括号。Args 被打包成一个元组,kwargs 被打包成一个字典。

在我们更深入地研究 Python 中的打包和解包值之前,让我们再多谈谈函数参数。

Python 中有两种参数,位置参数关键字参数,前者是根据位置指定的,后者是键值对的参数。

调用函数时不能省略没有默认值的参数。它们必须以正确的顺序和位置传递。

先简单说一下打包和拆包。

包装和拆包

星号是解包操作符,用于解包 Python 中 iterable 对象的值。通常与参数相关联的单星号运算符(*)可用于任何 iterable。与 kwargs 关联的双星号(**)只能用于词典。

这些星号是打包和解包的操作符。然而,你可以不用它们来打包和解包。查看下面的代码。

在这个要点中,我们将元组t分解成三个变量;abc。当我们打印这些变量时,我们将看到元组的单个元素。

1
2
3

让我们在字典上使用同样的解包模式。

当 k 和 v 被打印出来时,我们看到:

Hello
15

这个例子稍微复杂一点,你可以在第 3 行看到。我们使用.items()迭代一个字典,使用tuple()将字典转换成一个元组,这样我们就可以使用[0]提取元组的第一个元素。所有这些将键值对打包成kv

包装和拆包操作员

现在您已经对 Python 中的解包值有了一些了解,让我们进入操作符***。看看这个带有单个星号的打包和解包的例子。

我们使用相同的解包模式,将三个变量设置为一个列表。然而,这里我们给变量b添加了打包操作符。你能猜到打印出来的报表会是什么样子吗?

1
[2,3,4,5]
6

我们可以看到,a被设置为等于列表中的第一个元素,c被设置为最后一个元素,中间的所有元素都被打包到了b中。当您知道要定义多少个变量,但不确定要在每个变量中放入多少个元素时,这可能很有用。

双星号允许我们使一些复杂的字典过程非常优雅地发生。例如,我们可以使用双星号来合并两个唯一键的字典。

字典 _ 合并. py

当我们运行这个文件时,我们会看到下面的输出。

合并词典

请注意,merge_two_dictionaries中的 return 语句被括在花括号中。这确保了我们的函数返回一个字典。

现在我们来讨论一下*args**kwargs

*参数

注意**args** 只是一个名字**。你不需要使用名称args。这里重要的是使用拆包操作符 ( *)。然而,使用args是规范的。记住,使用解包操作符*得到的可迭代对象不是list而是tuple。**

当您使用*操作符解包一个列表并将参数传递给一个函数时,就好像您在单独传递每一个参数。下面用一个常见的编码面试问题来展示一下*args的威力。

你的任务是编写一个将整数相加的 Python 函数。

听起来很简单,对吧?

我的初始代码可能看起来像这样。

完全符合“整数和”的标准。假设我们对照这些测试来运行它。你能看出什么会出错吗?

我们的前两个测试没有问题,但是第三个测试,对三个整数求和,产生了一个错误。看起来我们的 sum 函数只能接受两个位置参数。

2 != 3

让我们看看能否使用*args编写一个更好的加法函数。

现在我们有了一个可以接受任意数量参数的函数。通过循环这些参数并将它们的值加到total来计算总和。

现在让我们通过一些测试。

这个新函数通过了所有的测试!当我们想吸纳许多论点,但我们不确定有多少时,*args是一个很好的选择。

* *克瓦查

同样,您不需要使用名称kwargs。这里重要的是使用拆包操作符 ( **)。然而,使用名称kwargs是规范的。

在函数定义中**kwargs*args一样工作,但是它不接受位置参数,而是接受任意多个关键字参数。

**kwargs通常用于保存在对象之间传递的消息。我们可以看到这是下面的装饰代码。如果您想了解更多关于 decorators 的知识,请查看这段代码所在的文章。

tracefunc 装饰器

在第 8 行,我们看到tracefunc_closure**kwargs作为参数。这允许我们传递给 tracefunc 的任何东西在对象之间传递时都被保留。让我们用这个装饰器来追踪一个函数,看看第一手资料。

下面是打印语句输出。

注意,tracefunc 将数据帧识别为 args,将pd.merge的关键字参数识别为 kwargs。此外,我们不必对 tracefunc 做任何事情来使它与pd.merge兼容。Tracefunc 能够接收这些 kwargs 并将它们传递给pd.merge。这表示接口保留。

我们可以在 pandas 文档中看到关键字参数和**kwargs的例子。这里我们看到 dataframe.info 可以接受 6 个不同的关键字参数。在这种情况下,我们必须使用正确的关键字或者按照位置顺序放置值。然而,后一种情况不必要地牺牲了可读性。

dataframe.assign 将**kwargs作为其唯一的参数。

这听起来很简单,然而**kwargs非常灵活。当将键值对作为**kwargs传递给df.assign时,我们的值可以是一个简单的列表,也可以是一个更复杂的 lambda 函数。列表和 lambda 函数处理在.assign的源代码中实现。**kwargs的意义在于这个函数可以拿任何一个键并使用它。我们把这个键命名为state 还是temp_f并不重要。

结论

无论是编写函数来接受任意数量的参数和关键字参数,还是打包和解包列表和字典中的值*args and **kwargs都允许一些非常灵活、可读和有用的代码。此外,*args and **kwargs是理解 Python 中更广泛的编程和函数编写概念的基础。

构建非线性最小二乘(NLS)回归模型指南

原文:https://towardsdatascience.com/a-guide-to-building-nonlinear-least-squares-nls-regression-models-310b97a7baeb?source=collection_archive---------13-----------------------

图片由 Alexandra_Koch 来自 Pixabay ( Pixabay 许可)

以及用 Python 和 SciPy 编写的 NLS 回归教程

非线性最小二乘法(NLS) 是一种优化技术,可用于为包含非线性特征的数据集构建回归模型。这种数据集的模型在系数上是非线性的。

本文的结构:

第 1 部分:NLS 回归模型的概念和理论。这部分有一些数学知识。如果你喜欢数学和/或对非线性最小二乘回归如何工作感到好奇,你会喜欢它。

第 2 部分:关于如何使用 Python 和 SciPy 构建和训练 NLS 回归模型的教程。你不需要阅读第一部分来理解第二部分。

第一部分:NLS 回归背后的理论

我们将遵循这些代表性惯例:

“帽子”符号(^)将用于在数据上拟合回归模型的过程中生成的值。例如, β_(hat)拟合的系数的向量。

y _ OBS是因变量 y 的观测值的向量。

普通样式的变量是定标器,粗体样式的变量代表它们的向量或矩阵等价物。例如,y_obs_i 是包含大小为(m×1)的 y_obs 向量的第 I 个观察值的定标器。

我们将假设回归矩阵 X 的大小为(m x n ),即它有 m 个数据行,每行包含 n 个回归变量。y 矩阵的大小为(m×1),系数矩阵的大小为(m×1)(或者转置形式为 1×m)

现在让我们来看三个可以用 NLS 训练的非线性模型的例子。

在下面的模型中,回归系数 β_1β_2 是 2 和 3 的幂,因此不是线性的。

系数非线性的模型(图片由作者提供)

e 为模型的残差,即观测值 y 与预测值(即 R.H.S. 上的 β_0,β_1β_2x的组合)

以下模型是一个自回归时间序列模型,包含系数 s β_1β_2 的乘法关系,因此本质上是非线性的。

系数非线性的自回归时间序列模型(图片由作者提供)

在下面的模型中,预测值是回归变量 X 的线性组合的指数函数。

指数均值模型(图片由作者提供)

最后一个公式通常用于 泊松回归模型 或其衍生模型,如广义泊松模型或负二项式回归模型。具体而言,拟合均值 _cap 被表示为泊松概率分布的条件均值,如下所示:

给定单位时间内看到 y 个事件的泊松概率单位时间内 _(cap)个事件的平均预测率,其中 _(cap)是回归参数的函数为了简洁起见,我们去掉了 _i 下标(图片由作者提供)

这种泊松回归模型用于拟合基于计数的数据集,例如每天在自行车共享计划中租赁其中一辆自行车的人数。

NLS 优化是如何工作的?

在 NLS,我们的目标是寻找模型参数向量,它将最小化残差的平方和。换句话说,我们必须尽量减少以下情况:**

拟合回归模型的残差平方和(图片由作者提供)

_cap_i (模型对数据集中第行的预测)是模型参数向量 β_cap 和回归变量 x_i 的函数,即:

由模型预测的给定x _ I 的条件均值是拟合的 β和 x_i 的函数(图片由作者**

_i 替换为f(β_ capx _ I)在前面的 RSS 等式中,我们有:

拟合回归模型的残差平方和(图片由作者提供)

再次, β_cap 是拟合 系数的 **向量,而 x_i 是回归变量矩阵 X 的第行。****

最小化 RSS 的一种方法是相对于 β_cap 对 RSS 进行微分,然后将微分设置为零并求解 β_cap、 ,即:

RSS w . r . t .β_ cap的偏导数,并设置为零(图片由作者)

由于 β_cap 是对应于 n 回归变量 x1,x2,…xn 的长度为 n 的向量,RSS 需要对 w.r.t .进行部分微分,这些 β_cap_j 系数的每一个和每一个方程都设置为零。例如,RSS w.r.t. β_cap_1 的偏导数如下:

残差平方和 w.r.t .系数的偏导数 β_1 (图片由作者提供)

由于有 n 个系数 β_cap_1 到β_cap_n ,我们得到上面在 n 个变量中所示的 n 个方程。然而,相对于【OLS】估计,这个 n 方程组没有封闭形式的解。因此,我们必须使用迭代优化技术,在每次迭代 k 时,我们对如下所示的 β_cap_1 至β_cap_n 的值进行小的调整,并重新评估 RSS:

在第k 次迭代时,将 β_j 增加 δ β_j

已经设计了几种算法来有效地更新 β_cap 向量,直到达到将最小化 RSS 的一组最佳值。其中最主要的是基于信任区域的方法,例如信任区域反射算法、leven Berg-Marquardt 算法和被想象命名为 Dogbox 算法。SciPy 支持所有三种算法。

让我们回到前面介绍的指数均值模型。在这个模型中,我们有:

指数均值模型(图片由作者提供)

在 RSS 中替换:

拟合泊松模型的剩余平方和(图片由作者提供)

注意,指数中的 x_iβ_cap 是两个维度为【1 x n】【n x 1】的矩阵的矩阵乘法,因此结果是一个【1x1】矩阵,即有效的缩放器。*

对上面的等式 w.r.t. β_cap 进行微分,并将微分设置为零,我们得到下面的一组等式(以向量格式表示),其需要使用上面提到的迭代优化算法之一来求解:

该方程组的解产生泊松回归模型的拟合估计量 β_cap (图片由作者提供)

第 2 部分:使用 Python 和 SciPy 的 NLS 回归教程

让我们使用非线性最小二乘技术将泊松回归模型拟合到一个为期两年的租赁自行车日常使用数据集。

数据集的前 10 行如下:

租赁自行车使用计数(来源: UCI 机器学习库)(图片由作者)

你可以从这里下载数据集。

回归模型

我们将建立一个回归模型,其中因变量* ( y )为:***

total_user_count :自行车租赁者总数

回归变量矩阵 X 将包含以下解释变量:

季节:当前天气季节
年份:当前年份:0=2011 年,1=2012 年
:当前月份:1 至 12 日
假日:测量是否在假日进行(是=1,否=0)
工作日:星期几(0 至 6)
工作日
2 =薄雾+多云,薄雾+碎云,薄雾+少云,薄雾。
3 =小雪、小雨+雷雨+散云、小雨+散云。
4 =暴雨+冰粒+雷雨+薄雾,雪+雾。
temp :温度,归一化到 39C
atemp :真实感受,归一化到 50C
嗡嗡声:湿度,归一化到 100
风速:风速,归一化到 67

让我们导入所有需要的包

*****from** scipy.optimize **import** least_squares
**import** pandas **as** pd
**from** patsy **import** dmatrices
**import** numpy **as** np
**import** statsmodels.api **as** sm
**import** statsmodels.formula.api **as** smf
**import** statsmodels.stats.stattools **as** st
**import** matplotlib.pyplot **as** plt***

将数据集读入熊猫数据帧:

***df = pd.**read_csv**('bike_sharing_dataset_daywise.csv', **header**=0, **parse_dates**=[**'**dteday**'**], **infer_datetime_format**=True)***

创建训练和测试数据集。由于我们将该数据视为横截面数据,我们将随机选择 90%的数据行作为我们的训练数据,剩余的 10%作为我们的测试数据:

***mask = np.**random**.**rand**(**len**(df)) < 0.9
df_train = df[mask]
df_test = df[~mask]
**print**('Training data set length=**'**+**str**(**len**(df_train)))
**print**(**'**Testing data set length=**'**+**str**(**len**(df_test)))***

用 Patsy 语法创建回归表达式。我们说 total_user_count 是因变量,它取决于波浪号(~)右侧提到的所有变量:

使用 Patsy 雕刻出 yX 矩阵:

***y_train, X_train = **dmatrices**(expr, df_train, **return_type**=**'**dataframe**'**)
y_test, X_test = **dmatrices**(expr, df_test, **return_type**=**'**dataframe**'**)***

*让我们定义几个函数。我们将定义的第一个函数是计算指数平均值的函数:mu _ cap= exp(X β_ cap):

*****def** calc_exponentiated_mean(beta, x):
    lin_combi = np.**matmul**(np.**array**(x), np.**array**(beta))
    mean = np.**exp**(lin_combi)
    **return** mean***

我们将定义的第二个函数是在给定输入矩阵 βXy_obs 的情况下计算简单残差r _ I =(y _ predicted—y _ OBS):

*****def** calc_residual(beta, x, y_obs):
    y_pred = **calc_exponentiated_mean**(beta, x)
    r = np.**subtract**(y_pred, np.**array**(y_obs).**flatten**())
    **return** r***

**将系数 β 向量初始化为所有 1.0 值。X 中有回归变量(数一数验证!)和回归截距,即总共两个变量。所以 β 大小为( 1 x 12)。我们将构建的 numpy 向量将具有转置形状(12,),这适合我们,因为我们必须将 X_train 与该向量相乘,并且 X_train 的形状为(661,12):

***num_params = **len**(X_train.**columns**)
beta_initial = np.**ones**(num_params)***

最后,是时候使用 SciPy 中的最小平方()方法在(y_train,X_train)上训练 NLS 回归模型,如下所示:

***result_nls_lm = **least_squares**(**fun**=calc_residual, **x0**=beta_initial, **args**=(X_train, y_train), **method**='lm', verbose=1)***

注意,我们正在使用leven Berg–Marquardt 算法 ( 方法=‘lm’)来执行向量的迭代优化。**

result_nls_lm.**x**变量包含拟合的 β 向量,换句话说, β_cap

让我们将 β_cap 组织成一个熊猫数据帧并打印出这些值:

****df_beta_cap = pd.**DataFrame**(**data**=res_lsq_lm.**x**.**reshape**(1,num_params), **columns**=X_train.**columns**)**print**(df_beta_cap)****

我们看到以下输出显示了每个回归变量的拟合系数值和拟合回归截距:

每个回归变量的拟合系数值和拟合回归截距

预言;预测;预告

****让我们看看我们的模型在我们之前雕刻出来的测试数据集 X_test 上表现如何。我们将使用之前定义的 calc_exponentiated_mean()函数,但这一次,我们将传入拟合的 β_cap 向量和 X_test 😗***

****predicted_counts=**calc_exponentiated_mean**(**beta**=result_nls_lm.x, **x**=X_test)****

让我们绘制预测计数与实际计数的对比图:

****actual_counts = y_test[**'registered_user_count'**]fig = plt.**figure**()
fig.**suptitle**(**'Predicted versus actual user counts'**)predicted, = plt.**plot**(X_test.**index**, predicted_counts, 'go-', **label**='Predicted counts')actual, = plt.**plot**(X_test.**index**, actual_counts, **'ro-',** **label**='Actual counts')plt.**legend**(**handles**=[predicted, actual])
plt.**show**()****

测试数据集上的预测自行车用户数与实际自行车用户数的对比图(图片由作者提供)

纸张、书籍和数据链接

Cameron A. Colin,Trivedi Pravin K ., 计数数据的回归分析 ,计量经济学学会专论№30,剑桥大学出版社,1998 年。国际标准书号:0521635675

数据集

Fanaee-T,Hadi,和 Gama,Joao,“结合集合检测器和背景知识的事件标记”,《人工智能进展》(2013):第 1–15 页,Springer Berlin Heidelberg,doi:10.1007/s 13748–013–0040–3。

形象

本文中的所有图片的版权归 CC-BY-NC-SA 所有,除非图片下方提到了不同的来源和版权。

相关文章

**** ****

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

用 Python 编写的加拿大足球指南

原文:https://towardsdatascience.com/a-guide-to-canadian-football-with-python-43fdbd8cb7a6?source=collection_archive---------65-----------------------

关于 Python 如何帮助我们提取加拿大足球联赛的详细数据的教程

约翰·托尔卡西奥在 Unsplash 上拍摄的照片

语境

如今,NFL 通过一个 API,几乎实时地发布所有比赛的信息。这使得分析师、统计学家、编码员或者仅仅是精通技术的足球迷能够分析、剖析、操纵每个动作的大量变量:倒地和距离、剩余时间、动作类型、传球、冲刺、空中、触球后获得的码数等。

这些结果的可用性使人们能够重新处理这些数据,创建预测模型并开发新的指标,如增加的获胜概率或增加的预期点数。这些新的指标给了游戏一个全新的视角,更加客观,基于以前对很多很多游戏的观察。

如果说那些数据在 NFL 越来越频繁的使用,在加拿大足坛依然不是这样。我不能假装能够解释为什么,但我认为原因之一可能是缺乏对详细比赛结果的宣传。因为是的,每部戏的细节都有。也许它们不像 NFL 的数据那样详细或干净,但通过其 API 深入了解加拿大足球联盟的详细数据是可能的。我在下文中提出了一个关于如何使用 Python 和 Jupyter 笔记本来提取 2019 赛季所有这些数据的指南。

对 API 的访问

首先,要访问 API,需要向 CFL 请求一个密钥。就其本身而言,文档提供了你可以获取的每种类型数据的宝贵信息。在这里,我将重点介绍“一步一步”选项。

逐步指南

库导入

首先,让我们下载我们需要的所有库:

  • 请求:对于到 API 的连接
  • pandas:用于处理数据和转换成易于分析的数据帧
  • json & json_normalize:用于结果数据类型的转换

连接到 API 和季节统计

该 API 的一个重要特性是,基于 game_id,一次只能下载一场比赛的详细报道。因此,第一步将是下载本赛季所有比赛的列表,以获得完整的 game _ ids 列表。

我们的请求将遵循文档中指示的结构,并且我们将在“响应”变量中传递结果。当然,在这里你必须使用你自己的密钥,由 CFL 提供:

这里我们需要一点数据转换,必须转换 JSON 对象中的响应,然后将它赋给一个新的变量“output”:

这个变量是一个字典,意味着调用 output.keys()方法将返回“数据”、“错误”和“元”。由于所有结果都在“数据”列中,我们将调用它,然后将其转换为表格,最后转换为 DataFrame:

让我们花点时间来确保 df.head()的一切看起来都很好:

作者图片

到目前为止,一切顺利!

导出播放数据

请注意,第一列是 game_id 列,即我们必须用于分析的列。我们的要求是 2019 赛季每一场个人比赛的总结,我们知道每行只有一场比赛。这意味着它们不是重复的,因此我们可以简单地将该列转换为一个列表,其中包含:

我们知道本赛季有 95 场比赛(包括季后赛),但我们被限制为每分钟 30 次调用 API。为了绕过这一点并且不使 API 过载,我们将列表分成几个部分:

现在,我们创建一个循环,它将遍历第一个列表,获取每个 game_id,请求这个特定游戏的详细数据,并在 DataFrame 中转换数据。我还添加了一个带有 game_id 的列。就我个人而言,我喜欢跟踪我提取的数据,所以我将每个数据帧保存在一个单独的 Excel 文件中。这不是强制性的,但我发现如果我事后必须做一些检查,会更容易。

一旦完成,你只需要在触发第二个循环之前等待一分钟:

您可以对循环 3 和循环 4 重复相同的操作。注意,每个循环的最后一步是将所有文件名添加到一个列表中。

主数据框架的创建

因此,我们的最后一步是将所有列表合并成一个全局列表,并创建一个循环,遍历每个文件,打开它,将数据加载到一个数据帧中,然后关闭文件。对于我的档案,我再次将我的主数据框架保存为 Excel 文件。

最后,在我们所有的操作中,pandas 向我们的 Dataframe 添加了一个列“Unnamed: 0 ”,所以我们只需删除它,并将我们的 final_data 命名为:

这为我们提供了以下信息:

作者图片

我们做到了!2019 赛季所有比赛的所有信息的最终数据框架。

结论

正如我们所看到的,通过几个简单的步骤,我们可以轻松地获得整个赛季的所有比赛数据,包括季后赛。事实上,如果我们删除 Excel 步骤,代码可能会简单一点。我喜欢跟踪我提取的数据,但是如果您愿意,您可以很容易地修改这段代码并删除这些步骤。

我喜欢把这看作一个开始,而不是一个结论:我希望这可以帮助加拿大足球获得更多分析师和统计学家的关注,并且在不久的将来,我们将拥有与 NFL 相同的 CFL 指标。

我这个教程的代码可以在 这里 找到。

Python 文本清理指南

原文:https://towardsdatascience.com/a-guide-to-cleaning-text-in-python-943356ac86ca?source=collection_archive---------11-----------------------

自然语言处理笔记

为机器阅读准备自然语言

摄 Unsplash 上的创意交流

文本是非结构化数据的一种形式。根据维基百科,非结构化数据被描述为“没有预定义的数据模型或者没有以预定义的方式组织的信息。”【来源 : 维基百科。

不幸的是,计算机不像人类;机器不能像我们人类那样阅读原始文本。当我们处理文本数据时,我们不能从原始文本直接进入我们的机器学习模型。相反,我们必须遵循这样一个过程:首先清理文本,然后将其编码成机器可读的格式。

让我们来介绍一些清理文本的方法——在另一篇文章中,我将介绍一些对文本进行编码的方法。

案例规范化

当我们写作时,出于不同的原因,我们在句子/段落中使用不同的大写单词。例如,我们用大写字母开始一个新句子,或者如果某物是一个名词,我们将大写第一个字母来表示我们正在谈论一个地方/人,等等。

对于人类来说,我们可以阅读文本,并直观地判断出句子开头使用的“the”与后面句子中间出现的“the”是同一个单词,但是,计算机不能——机器会将“the”和“The”视为两个不同的单词。

因此,规范单词的大小写很重要,这样每个单词都是相同的大小写,计算机就不会把同一个单词当作两个不同的符号来处理。

*# Python Example*
text = "The UK lockdown restrictions will be dropped in the summer so we can go partying again!" *# lowercasing the text*
text = text.lower()
**print**(text)**>>>> the uk lockdown restrictions will be dropped in the summer so we can go partying again!** 

删除停用词

在大多数自然语言任务中,我们希望我们的机器学习模型能够识别文档中为文档提供价值的单词。例如,在情感分析任务中,我们想要找到使文本情感向一个方向或另一个方向倾斜的单词。

在英语中(我相信大多数语言也是如此,但不要引用我的话),有些词比其他词使用得更频繁,但它们不一定会给句子增加更多价值,因此可以肯定地说,我们可以通过从文本中删除来忽略它们。

注意:删除停用词并不总是最好的主意!

*# Importing the libraries* 
import nltk
from nltk.corpus import stopwords
nltk.download("stopwords")stop_words = set(stopwords.words("english"))
**print**(stop_words)**>>>> {'over', 'is', 'than', 'can', 'these', "isn't", 'so', 'my', 'each', 'an', 'between', 'through', 'up', 'where', 'hadn', 'very', "you'll", 'while', "weren't", 'too', 'doesn', 'only', 'needn', 'has', 'just', 'd', 'some', 'into', 've', 'didn', 'further', 'why', 'mightn', 'and', 'haven', 'own', "mightn't", 'during', 'both', 'me', 'shan', "doesn't", 'theirs', 'herself', 'the', 'few', 'our', 'its', 'yourself', 'under', 'at', "you've", 're', 'themselves', 'y', 'ma', 'because', 'him', 'above', 'such', 'we', "wouldn't", 'of', 'from', 'hers', 'nor', "shouldn't", 'a', 'hasn', 'them', 'myself', 'this', 'being', 'your', 'those', 'i', 'if', 'couldn', 'not', 'will', 'it', 'm', 'to', 'isn', 'aren', 'when', 'o', 'about', 'their', 'more', 'been', "needn't", 'had', 'll', 'most', 'against', 'once', 'how', "didn't", "shan't", 'there', 'all', "should've", 'he', "don't", 'she', 'which', 'below', 'on', 'no', 'yourselves', "wasn't", 'shouldn', 'by', 'be', 'have', 'does', "aren't", 'itself', 'same', 'should', 'in', 'before', 'am', "won't", 'having', "you'd", 'mustn', 'for', "that'll", 'that', "couldn't", 'wasn', 'won', "hasn't", 'as', 'until', 'wouldn', "mustn't", 'his', 'ain', "you're", 'out', "she's", 'other', 'are', 't', 'you', 'off', 'yours', 'ourselves', 'himself', 'down', "haven't", 'ours', 'now', "hadn't", 'do', 's', 'her', 'with', "it's", 'then', 'weren', 'any', 'after', 'whom', 'what', 'who', 'but', 'again', 'here', 'did', 'doing', 'were', 'they', 'was', 'or', 'don'}***# example text*
text = "The UK lockdown restrictions will be dropped in the summer so we can go partying again!"*# removing stopwords*
text = " ".join([word **for** word **in** text.split() **if** word **not in** stop_words])**print**(text)**>>>> uk lockdown restrictions dropped summer go partying again!**

删除 Unicode

ASCII 将表情符号和其他非 ASCII 字符格式化为 Unicode。从本质上讲,Unicode 是一种通用的字符编码标准,所有语言中的每个字符和符号都被分配了一个代码。Unicode 是必需的,因为它是唯一允许我们使用各种不同语言检索或连接数据的编码标准,但问题是…它在 ASCII 格式中不可读。

:来自 Python 指南的示例代码

*# creating a unicode string*
text_unicode = "Python is easy \u200c to learn" *# encoding the text to ASCII format*
text_encode = text_unicode.encode(**encoding**="ascii", **errors**="ignore")# decoding the text
text_decode = text_encode.decode()# cleaning the text to remove extra whitespace 
clean_text = " ".join([word **for** word **in** text_decode.split()])
**print**(clean_text)**>>>> Python is easy to learn.**

删除网址、标签、标点、提及等。

根据我们正在处理的数据类型,我们可能会面临各种增加噪声的挑战。例如,如果我们正在处理来自 Twitter 的数据,找到各种标签和提及并不罕见——这是指包含 Twitter 行话中另一个用户用户名的推文。

如果这些特征对我们试图解决的问题没有价值,那么我们最好将它们从数据中删除。然而,由于在许多情况下我们不能依赖于一个定义的字符,我们可以利用名为 Regex 的模式匹配工具来帮助我们。

import re *# removing mentions* 
text = "You should get @BlockFiZac from @BlockFi to talk about bitcoin lending, stablecoins, institution adoption, and the future of crypto"text = re.sub("@\S+", "", text)
**print**(text)**>>>> You should get  from  to talk about bitcoin lending, stablecoins, institution adoption, and the future of crypto
------------------------------------------------------------------**
*# remove market tickers*
text = """#BITCOIN LOVES MARCH 13th A year ago the price of Bitcoin collapsed to $3,800 one of the lowest levels in the last 4 years. Today, exactly one year later it reaches the new all-time high of $60,000 Thank you Bitcoin for always making my birthday exciting"""text = re.sub("\$", "", text)
**print**(text)**>>>> #BITCOIN LOVES MARCH 13th A year ago the price of Bitcoin collapsed to  3,800 one of the lowest levels in the last 4 years. Today, exactly one year  later it reaches the new all-time high of 60,000 Thank you Bitcoin for  always making my birthday exciting**
**------------------------------------------------------------------**
*# remove urls*
text = "Did someone just say “Feature Engineering”? https://buff.ly/3rRzL0s"text = re.sub("https?:\/\/.*[\r\n]*", "", text)
**print**(text)**>>>> Did someone just say “Feature Engineering”?
------------------------------------------------------------------**
*# removing hashtags* 
text = """.#FreedomofExpression which includes #FreedomToProtest should be the cornerstone of any democracy. I’m looking forward to speaking in the 2 day debate on the #PoliceCrackdownBill & explaining why I will be voting against it."""text = re.sub("#", "", text)
**print**(text)**>>>> .FreedomofExpression which includes FreedomToProtest should be the  cornerstone of any democracy. I’m looking forward to speaking in the 2 day  debate on the PoliceCrackdownBill & explaining why I will be voting against it.
------------------------------------------------------------------**
*# remove punctuation* import stringtext = "Thank you! Not making sense. Just adding, lots of random punctuation."punct = set(string.punctuation) 
text = "".join([ch for ch in tweet if ch not in punct])**print**(text)**>>>> Thank you Not making sense Just adding lots of random punctuation**

词干化和词汇化

当我们在做 NLP 任务时,我们可能希望计算机能够理解“walked”、“walk”和“walking”只是同一个单词的不同时态,否则,它们会被不同地对待。词干化和词汇化都是用于在 NLP 中规范化文本的技术——为了进一步简化这个定义,我们简单地将一个单词简化为它的核心词根。

根据维基百科的定义:

  • 词干化( 链接 ) — 在语言形态学和信息检索中,词干化是将屈折词还原为其词干、词基或词根形式——一般为书面词形的过程。
  • 词条释义化( 链接**)——**语言学中的词条释义化是将一个词的屈折形式组合在一起的过程,这样它们就可以作为一个单项来分析,通过该词的词条或词典形式来识别。

尽管定义非常相似,但是每种技术缩减单词的方式却非常不同(我可能会在另一篇文章中探讨),这意味着这两种技术的结果并不一致。

import nltk
from nltk.stem.porter import PorterStemmer
from nltk.stem import WordNetLemmatizerwords = ["walk", "walking", "walked", "walks", "ran", "run", "running", "runs"]
**-----------------------------------------------------------------**
*# example of stemming*
stemmer = PorterStemmer()for word in words: 
    **print**(word + " ---> " + stemmer.stem(word))**>>>> walk ---> walk
     walking ---> walk
     walked ---> walk
     walks ---> walk
     ran ---> ran
     run ---> run
     running ---> run
     runs ---> run
------------------------------------------------------------------** *# example of lemmatization* lemmatizer = WordNetLemmatizer()for word in words:
    **print**(word + " ---> " + lemmatizer.lemmatize(word))**>>>> walk ---> walk 
     walking ---> walking 
     walked ---> walked 
     walks ---> walk 
     ran ---> ran 
     run ---> run 
     running ---> running 
     runs ---> run**

包裹

在本教程中,我们介绍了如何在 Python 中清理文本。

具体来说,我们涵盖了:

  • 我们为什么清理文本
  • 清理文本的不同方法

感谢您的阅读!在 LinkedIn 和 T2 Twitter 上与我保持联系,了解我关于数据科学、人工智能和自由职业的最新消息。

数据科学研究项目指南

原文:https://towardsdatascience.com/a-guide-to-data-science-research-projects-a5e25f9884e8?source=collection_archive---------29-----------------------

无论你是新手还是经验丰富的工程师,开始一个数据科学研究项目都可能具有挑战性——你希望你的项目对数据科学社区和你的投资组合来说是有意义的可访问的有价值的。在这篇文章中,我将介绍两个框架,你可以用它们来指导你的数据科学研究项目。请注意,本指南并非详尽无遗,而是基于我在数据科学和机器学习方面的经验,我认为这对初学数据的科学家可能有所帮助。

研究过程——从构思到实施

  1. 确定你想要探索或解决的研究问题。
  2. 问自己以下问题:
  • 我是想专注于工程工作还是纯研究?

例如,如果您正在构建一个新的机器学习应用程序,工程可能涉及为算法构建一个框架和用户界面,并使用部署基础架构来加快数据访问。

纯研究可能涉及尝试以创新的方式操纵模型的属性(例如,创建一个新的损失函数)或创建一个全新的模型,如您在 NeurIPS 和类似会议的论文中看到的研究类型。

也有可能将工程和纯研究结合起来——你需要考虑最适合研究目标的方法。

  • 我的研究目标是超越现有基线(例如,在 ImageNet 数据集上获得最高分),还是使用创新方法探索尚未建立坚实基线的研究领域?这两种类型的研究问题对数据科学都很有价值。

3.**进行文献综述。**查看该领域的现有工作,分析相关方法的优势和劣势。找出你感兴趣的文献中的空白。

4.设计您提议的解决方案并实施基线/原型(参见下面的开发流程)。

5.根据您在步骤 2 中的回答,迭代开发原型。

开发流程—先决条件

以下步骤的目标是指导您为您的用例选择最佳的数据存储选项、机器学习模型以及开发和部署流程。在概述了这些方面之后,我建议可以开始开发过程了。

  1. 数据是什么类型,如何存储?

示例:

  • 如果您试图预测的变量是数值型的,您可以将您的问题陈述定义为一个正常的回归应用程序。用于处理这一问题的流行 ML 模型很少是线性回归、决策树、支持向量机和神经网络或深度学习。
  • 如果数据是按时间排序的,你可以考虑时间序列方法——除了深度学习方法,还有传统的算法。
  • 如果您正在处理大量数据,根据数据格式,您可以考虑探索大数据存储和处理解决方案,如 Apache Spark、Cassandra、Redis 等。一些大数据存储解决方案可以轻松地支持机器学习工具/算法。

2。模型将在哪里托管,如何部署?可交付物可以被结构化为管道或系统吗?如果可以,系统的输入和输出是什么样的?

作为这一步的一部分,你需要问这样一个问题:“我可以使用的模型类型有什么限制吗?”例如,模型是用于流数据应用程序(在特定时间段(如 1 小时)内持续向模型提供数据)还是用于大型数据集的典型预测?您的用户将通过命令行或网页访问模型吗?

你可以考虑使用像 Luigi 和 Apache AirFlow 这样的工具来帮助你的项目进行流水线和工作流管理。

3。我正在寻找的模型有什么关键的和值得拥有的特征,什么样的 ML 算法符合这种描述?

要回答这个问题,你需要回到你项目的目标。以下是一些你可能会问的问题:

  • 你的目标是基于数据预测和改善某个因素或事件吗?
  • 您认为理解您的算法用来进行预测的特征是否重要(例如,理解如何改进某个因素/事件)?

这可能会决定你的型号选择。例如,使用决策树,您可以确定算法认为哪些功能对预测和决策路径最重要,但是这些模型可能无法为您的应用程序提供很高的准确性。然而,神经网络可以提供很高的准确性,但解释起来更具挑战性。

有一些解决方案可以让你同时获得可解释性和良好的准确性。例如,神经网络和决策树可以通过神经支持的决策树结合,尽管这些方法可能有一定的权衡(例如,需要预先训练的权重)。你需要调查你正在考虑的方法的利弊,以评估它们如何适合你的问题陈述。

4。你将如何调试和评估你的模型?你会考虑什么样的衡量标准和可视化?

示例:

  • 您选择的指标可能取决于文献中的现有方法,这些方法可能是您在研究过程中发现的。例如,在处理不平衡数据时,除了原始精度度量外,您还可以选择包括马修斯相关系数或平衡精度。
  • 当您考虑您将为您的管道实施什么工具和流程时,请记住记录您可能对您的模型进行的超参数的任何重要更改,或者使用诸如Weights and bias之类的工具进行调试。
  • 可视化是开发过程的重要组成部分,可用于调试模型(例如,学习曲线)、可解释性(例如,显著性图),以及数据分析和最终报告(例如,单词云)。在开始开发之前,试着考虑可以增加项目实现、质量和价值的可视化类型。

这两个框架中的每一步都需要一些思考和反复试验。我会在以后的博客文章中介绍一些有用的技巧,所以如果你觉得这篇文章有帮助,请继续关注!

原载于 2021 年 4 月 5 日【http://demystifymachinelearning.wordpress.com】

Python 中的降维指南

原文:https://towardsdatascience.com/a-guide-to-dimensionality-reduction-in-python-ce0c6ab91986?source=collection_archive---------19-----------------------

使用随机森林和 PCA 进行降维

照片由斯蒂夫·约翰森在像素上拍摄

降维是将高维数据转换为低维格式,同时保留最重要的属性的过程。这项技术在许多行业都有应用,包括定量金融、医疗保健和药物研发。降维的应用很多,所以每个数据科学家都应该知道一些降维的最新方法。

一种易于理解的降维方法是随机森林特征重要性。随机森林是一种机器学习算法,它使用许多决策树来完成分类和回归任务。每个决策树都会对数据中的值提出“是”或“否”的问题,然后根据答案将它们分成相似的组。大多数随机森林包允许您提取重要的要素,这些要素是最有效地分隔数据不同部分的字段。

例如,为了预测借款人是否会拖欠贷款,信用评分将是一个重要的特征,它可以最有效地将数据分为拖欠和非拖欠借款人。虽然这种方法很有用,但它仅限于监督学习过程,在这种过程中,我们知道我们试图预测的输出。在这个例子中,输出是借款人是否会违约。

在某些用例中,数据科学家有数据,但没有框架监督学习问题。监督学习需要带标签的数据,这意味着对于每个输入,我们都有一个代表我们试图预测的结果的标签。类似地,也存在数据科学家访问高维未标记数据的用例。这里,未标记的数据只是缺少指定预测模型输出的字段。例如,有权访问贷款借款人数据,但没有任何贷款状态信息(违约/无违约)。

当面临高维、无标签数据(例如,数百到数千列)的问题时,您可以使用无监督的降维技术。最常见的无监督学习降维方法之一是主成分分析(PCA) 。PCA 表示具有大量列的数据集,具有较少列的较小数据集,称为主成分,然后可以用于分析趋势、聚类和异常值,甚至可以帮助构建监督学习问题。

Scikit-learn 是一个 Python 机器学习库,它有许多易于使用的模块来进行降维。Scikit-learn 中的 ensemble 模块具有用于分类和回归任务的随机森林算法。在每个监督学习用例中,随机森林可用于减少数据的维数。对于无监督的降维任务,分解模块有 PCA 包。

在这里,我们将了解如何对 Lending Club 数据 se t 应用随机森林特征选择和 PCA。Lending Club 是一家向公众发布数据的点对点贷款公司。数据集包含借款人信用历史和贷款状态。

Lending Club 的数据集可以在 Kaggle 上公开获得,并受知识共享许可协议的保护。

读入并准备数据

让我们从导入 Pandas 库开始:

进口熊猫作为 pd

然后,我们可以放宽行和列的显示限制:

pd.set_option('display.max_columns', None)pd.set_option('display.max_rows', None)

接下来,让我们将数据读入熊猫数据框:

df = pd.read_csv()

让我们看看输出数据中有多少列和多少行:

print("Number of Columns: ", len(list(df.columns)))print("Number of rows: ", len(df))

作者图片

我们看到有 142 列和 290 万行。对于大多数机器来说,这是一个相当大的数据集,因此它是降维的一个很好的候选对象。

现在,让我们显示前五行数据。由于这个数据集相对较大,下面的截图中的列被截断了。当您在本地计算机上运行此代码时,您将能够看到所有的列:

print(df.head())

作者图片

在本地机器上处理这种规模的数据集可能会很麻烦。正如您在运行这段代码时会注意到的那样,读入数据和显示等简单任务需要相当长的时间。因此,我们将只处理这里的数据子集。在我们的分析中,我们只考虑信用卡还款贷款。这对应于值为 credit_card 的目的列。让我们过滤我们的数据,仅包含信用卡还款贷款:

df = df[df['purpose'] == 'credit_card']

让我们也取列的一个小子集。我们将考虑数据中的以下字段:

columns = ['loan_amnt', 'loan_status', 'funded_amnt', 'funded_amnt_inv', 'term', 'int_rate','mths_since_recent_revol_delinq','home_ownership', 'verification_status',
 'num_accts_ever_120_pd', 'num_actv_bc_tl', 'num_actv_rev_tl', 'avg_cur_bal', 'bc_open_to_buy', 'bc_util', 'chargeoff_within_12_mths', 'delinq_amnt', 'last_fico_range_low', 'last_fico_range_high']df = df[columns]

有关这些字段含义的详细信息,请参见数据字典。一些重要的领域是贷款金额、利率、房屋所有权状态、FICO 分数和活跃账户数。

现在,让我们将过滤后的数据框写入一个新的 csv 文件,我们将其命名为 credit_card_loans.csv:

df.to_csv("credit_card_loan.csv", index=False)

现在,让我们将新的 csv 文件读入一个单独的数据框。我们将把这个新的数据帧称为 df_credit:

df_credit = pd.read_csv(“credit_card_loan.csv”)

现在,让我们打印新的行数和列数:

print("Number of Columns: ", len(list(df_credit.columns)))
print("Number of rows: ", len(df_credit))

作者图片

我们看到我们的数据现在有 18 列和 695,665 行。这种尺寸设置更容易使用。

接下来,让我们打印前五行数据:

print(df_credit.head())

作者图片

在我们讨论任何具体的方法之前,请注意我们已经能够从列和行的角度显著降低数据的维度。但是我们仍然需要做更多的数据准备。请注意,有些列缺少值— NaN 表示不是数字。让我们估算每一列的平均值:

def fill_na(numerical_column):
   df_credit[numerical_column].fillna(df_credit[numerical_column].mean(), inplace=True)

fill_na(‘mths_since_recent_revol_delinq’)
fill_na(‘num_accts_ever_120_pd’)
fill_na(‘num_actv_bc_tl’)
fill_na(‘num_actv_rev_tl’)
fill_na(‘avg_cur_bal’)
fill_na(‘bc_open_to_buy’)
fill_na(‘bc_util’)

作者图片

我们看到缺失值已被估算。接下来,让我们将分类列转换成机器可读的代码。当使用 Python 中可用的大多数机器学习包时,这种转换是必要的:\

def convert_categories(categorical_columnn):
 df_credit[categorical_columnn] = df_credit[categorical_columnn].astype(‘category’)
 df_credit[f’{categorical_columnn}_cat’] = df_credit[categorical_columnn].cat.codes

convert_categories(‘home_ownership’) 
convert_categories(‘verification_status’) 
convert_categories(‘term’)

作者图片

现在,房屋所有权、期限和验证状态列有了相应的分类列。我们要看的第一种方法是使用随机森林特征重要性来降低维数。这是一种受监督的机器学习方法,因为随机森林需要标记的数据。

因此,我们需要做的下一件事是从 loan_status 列生成标签。首先,让我们打印贷款状态的唯一值集:

print(set(df_credit["loan_status"]))

作者图片

为简单起见,让我们只考虑贷款状态结果完全支付和违约/冲销。我们也将结合这些。

df_credit = df_credit[df_credit[‘loan_status’].isin([‘Fully Paid’, ‘Default’, ‘Charged Off’])]

让我们也为这些贷款状态结果创建二元标签。值为 1 将对应于违约/注销,意味着贷款未还清且已被收回,而值为 0 意味着贷款已全部还清:

df_credit[‘loan_status_label’] = np.where(df_credit[‘loan_status’] == ‘Fully Paid’, 0, 1)
print(df_credit.head())

作者图片

最后,让我们过滤数据框中的列,以便我们只使用具有机器可读值的列:

columns2 = [‘loan_amnt’, ‘loan_status_label’, ‘funded_amnt’, ‘funded_amnt_inv’, ‘term_cat’, ‘int_rate’,’mths_since_recent_revol_delinq’,’home_ownership_cat’, ‘verification_status_cat’,
 ‘num_accts_ever_120_pd’, ‘num_actv_bc_tl’, ‘num_actv_rev_tl’, ‘avg_cur_bal’, ‘bc_open_to_buy’, ‘bc_util’, ‘chargeoff_within_12_mths’, ‘delinq_amnt’, ‘last_fico_range_low’, ‘last_fico_range_high’]
df_credit = df_credit[columns2]
print(df_credit.head())

作者图片

最后,让我们将利率列转换为数字列:

df_credit[‘int_rate’] = df_credit[‘int_rate’].str.rstrip(‘%’)
df_credit[‘int_rate’] = df_credit[‘int_rate’].astype(float)
df_credit.fillna(0, inplace=True)

随机森林

我们现在可以应用随机森林了,这是一种基于树的集成算法,它构建了一系列树数据结构,并对数据中的统计数据提出是或否的问题。这些树中的每一个都基于答案进行预测,并且这些树被组合以进行单个预测。让我们从 Scikit-learn 导入随机森林分类器和训练/测试分割方法

from sklearn.ensemble import RandomForestClassifierfrom sklearn.model_selection import train_test_split

让我们定义我们的输入和输出,并分割我们的数据用于训练和测试。这一步是必要的,这样我们就不会过度适应训练数据中的噪声,并确保我们的模型在对未来数据进行预测时能够很好地概括。

X = df_credit[[‘loan_amnt’, ‘funded_amnt’, ‘funded_amnt_inv’, ‘term_cat’, ‘int_rate’,’mths_since_recent_revol_delinq’,’home_ownership_cat’, ‘verification_status_cat’,
 ‘num_accts_ever_120_pd’, ‘num_actv_bc_tl’, ‘num_actv_rev_tl’, ‘avg_cur_bal’, ‘bc_open_to_buy’, ‘bc_util’, ‘chargeoff_within_12_mths’, ‘delinq_amnt’, ‘last_fico_range_low’, ‘last_fico_range_high’]]
y = df_credit[‘loan_status_label’]X_train, X_test, y_train, y_test = train_test_split(X, y , random_state=42, test_size = 0.33)

接下来,让我们将随机森林模型与训练数据相匹配,并生成要素重要性图:

import seaborn as snsimport matplotlib.pyplot as pltmodel = RandomForestClassifier()model.fit(X_train, y_train)features = [‘loan_amnt’, ‘funded_amnt’, ‘funded_amnt_inv’, ‘term_cat’, ‘int_rate’,’mths_since_recent_revol_delinq’,’home_ownership_cat’, ‘verification_status_cat’,
 ‘num_accts_ever_120_pd’, ‘num_actv_bc_tl’, ‘num_actv_rev_tl’, ‘avg_cur_bal’, ‘bc_open_to_buy’, ‘bc_util’, ‘chargeoff_within_12_mths’, ‘delinq_amnt’, ‘last_fico_range_low’, ‘last_fico_range_high’]

feature_df = pd.DataFrame({“Importance”:model.feature_importances_, “Features”: features })
sns.set()
plt.bar(feature_df[“Features”], feature_df[“Importance”])
plt.xticks(rotation=90)
plt.title(“Random Forest Model Feature Importance”)
plt.show()

作者图片

我们看到 FICO 分数、利率和当前余额是三个最重要的特征。我们可以清楚地使用随机森林特征重要性来缩小预测信用风险时要考虑的因素。

这种方法的缺点是它假设我们有标签。在这里,它是贷款状态。但是,我们经常可以找到这样的用例,我们希望缩小数据中的一个大的列列表,而不需要任何标签。这种方法最常用的技术是主成分分析。

主成分分析

主成分分析的工作原理是从不相关的较大集合中找出较小的列值集合。这种方法的工作原理是将独立的、不相关的特征表示为原始特征的总和。

我们先从 Sklearn 导入 PCA 包开始。我们还需要 Sklearn 中预处理模块的 StandardScaler 方法。

from sklearn.decomposition import PCAfrom sklearn.preprocessing import StandardScalerNow, let’s define the input for our PCA algorithm:X = df_credit[features2]

接下来,让我们使用 standardScaler 方法缩放数据。当算法计算组件时,此步骤有助于数值稳定性:

scaler = StandardScaler()X_scaled = scaler.fit(X)

接下来,让我们用四个组件定义一个 PCA 对象,适合 X_scaled,然后生成我们的组件:

pca=PCA(n_components=4)pca.fit(X_scaled)X_components=pca.transform(X_scaled)

然后,我们可以将该组件存储在 Pandas 数据框中:

components_df = pd.DataFrame({‘component_one’: list(X_components[:,0]), ‘component_two’: list(X_components[:,1]),
 ‘component_three’: list(X_components[:,2]), ‘component_four’: list(X_components[:,3])})print(components_df.head())

作者图片

现在,我们将该类存储在一个名为 labels 的变量中,并定义一些用于格式化我们的绘图的变量:

labels=X.loan_status_label
color_dict={0:’Red’,1:’Blue’}fig,ax=plt.subplots(figsize=(7,5))sns.set()
for i in np.unique(labels): 
 index=np.where(labels==i)
 ax.scatter(components_df[‘component_one’].loc[index],components_df[‘component_two’].loc[index],c=color_dict[i],s=10,
 label=i)

plt.xlabel(“1st Component”,fontsize=14)
plt.ylabel(“2nd Component”,fontsize=14)
plt.title(‘Scatter Plot of Principal Components’)
plt.legend()
plt.show()

作者图片

我们可以在散点图中看到不同类别之间的明显区别。虽然我们有标签,我们可以使用我们的 PCA 算法的健全性检查,在实践中,PCA 是以一种无人监督的方式使用。这意味着我们可以使用这些方法来探索我们感兴趣的任何列的不同集群。

例如,我们可以探索信用分数甚至收入的聚类。对这些值的聚类分析可能对贷款人有用,因为它允许他们考虑信誉良好的借款人,否则他们会被拒绝贷款。一个例子是一个信用分数很低的研究生,他最近在科技行业找到了一份六位数薪水的工作。当借款人在研究生院时,他们可能在偿还贷款方面有困难,但他们现在开始新的工作时可能是有信用的。

这种类型的聚类分析可以帮助发现这些借款人,否则他们将被拒绝贷款,这可以转化为贷方的更高收入。从这个意义上说,PCA 非常强大,因为它允许您分析数据中不同的组,而不需要任何预定义的标签。

这篇文章中的代码可以在 GitHub 上找到。

结论

大多数跨行业的数据科学团队都面临着降低数据维度的任务。这可能是为了进行简单的分析,构建可解释的模型,甚至是对大型数据集执行聚类分析。当你有一个明确定义的监督学习问题时,随机森林对于维数减少是有用的。在我们的案例中,我们的标签是信用违约和完全还清贷款。随机森林也很吸引人,因为你可以直接解释你的模型中使用的特征对于决定一个结果有多重要。当数据中没有标签时,PCA 是一个强大的工具。公司可以使用主成分分析来探索数据中的不同组,然后用于决策。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/dimensionality-reduction-python

DVC 和达格舒布机器学习实验指南

原文:https://towardsdatascience.com/a-guide-to-dvc-and-dagshub-for-machine-learning-experiments-fc4d157f07e5?source=collection_archive---------32-----------------------

MLOps

了解使用 DAGsHub 平台轻松管理数据、机器学习模型和监控模型性能的方法

图片作者|元素作者(freepik.com)

DVC 和达格·哈马舍尔德简介

在本指南中,我们将了解 DVC 以及 DAGsHub 如何让机器学习工程师轻松跟踪各种实验。我们将在合成的 Titanic 数据集上训练我们的模型,并基于分类模型运行各种实验。最后,我们将使用 DAGsHub 交互式仪表板进行可视化和比较。在我们深入编码之前,我想给你们简单介绍一下 DVC、FastDS 和 DAGsHub。

DVC

我们目前有两种选择来使用大文件版本控制。第一个是 git-lfs ,这是一个基于 git 的存储库的插件。git-lfs 非常慢,很难处理,所以我们有一个替代方案,称为 DVC(数据版本控制),它处理大型文件,如数据集和机器学习模型。DVC 旨在帮助 mlops 处理大型文件、数据集、机器学习模型和指标。它通常用于使机器学习实验具有可重复性。数据版本控制 DVC

DVC 具有监控数据变化的特殊能力,并且能够更快地将数据推送到远程服务器。它与基于 git 的系统共享功能,但它是独立的版本控制,专门处理大数据。

FastDS

图片来自 FastDS 经许可使用

FastDS 帮助数据科学家和机器学习工程师使用 Git 和 DVC 来协助他们,以便他们可以立即对代码和数据进行版本控制。FastDS 是由 DAGsHub 开发的,旨在最大限度地减少人为错误并自动化重复任务。 fastds PyPI

这个工具会让你在处理 Git 和 DVC 的时候变得简单

达格舒卜

DAGsHub 类似于 GitHub,它帮助数据科学家和机器学习工程师共享数据、模型、实验和代码。它允许您和您的团队轻松共享、审阅和重用您的工作,为机器学习提供 GitHub 体验。 DAGsHub 文档

DAGsHub 不仅如此,它还提供了实验、mlflow 集成、机器学习管道可视化、性能指标比较和可视化。使用 DAGsHub 的最大好处是它可以轻松地使用不同的功能,以及整个平台如何专注于帮助数据科学家和机器学习工程师。

使用 DAGsHub 构建机器学习模型

在这个机器学习项目中,我们将学习如何使用 DAGsHub 库来跟踪超参数和性能指标。我们将在简单的泰坦尼克号数据集上工作,通过在 3 个不同的机器学习模型上进行实验来预测乘客的存活率,并最终在 Kaggle 表格竞赛上检查我们的模型性能。该项目涉及多种工具,我们将学习如何使用它们来简化跟踪实验。

泰坦尼克号数据集

本次比赛使用的数据集是合成的,但基于真实的数据集(在这种情况下,实际的 OpenML Titanic !)是使用 CTGAN 生成的。该数据集也可在表格游乐场系列—2021 年 4 月| Kaggle 获得。

原始数据集受Creative Commons—Public Domain Mark 1.0许可,因此可以不受限制地公开使用。

我们在数据文件夹中有三个文件可用, *test.csv、train.csv 和 submission . CSV。*我们将把训练数据集分割成训练和测试数据帧,用于训练和验证。将通过在测试数据集上预测存活率来进行最终提交。

关于变量的信息清楚地解释了每一列包含的内容,我们将把这些信息用于特性工程。

数据字典

变量定义键生存 Survival0 =否,1 =是舱位等级 1 =第一,2 =第二,3 =第三性别年龄年龄 sibsp #泰坦尼克号上的兄弟姐妹/配偶号#泰坦尼克号上的父母/子女号船票号码票价乘客舱位号码登船港口 C =瑟堡,Q =皇后镇,S =南安普顿

包含乘客社会经济地位的等级:上层、中层和下层。年龄小于 1 是合理的,例如 xx . 5。sibsp以兄弟姐妹和配偶的形式定义家庭关系。最后, parch 以父子形式定义家庭关系。

这个数据集是使用 CTGAN 合成的,以避免作弊,并在 Kaggle 比赛中获得满分。如果你的模型在这里表现良好,显然你会有一个更好的算法。

导入

  1. dagshub 用于跟踪性能指标和超参数的库。
  2. pandas 用于数据帧, NumPy 用于数据扩充。
  3. sklearn 用于特征工程和机器学习模型
  4. joblib 用于保存我们的模型
import dagshub
import pandas as pd
from sklearn import preprocessing
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (
    roc_auc_score,
    average_precision_score,
    accuracy_score,
    precision_score,
    recall_score,
    f1_score,
)
from sklearn.model_selection import train_test_split
import joblib
import numpy as np

常数变量

我们将删除 Name、SibSp、Parch 和 Ticket,以简化我们的数据集。我们的主要重点是学习 DVC 和达格舒布实验。我们将保持我们的数据集和模型架构师简单。

  • 将目标列设置为存活。
  • 添加培训、测试和提交文件路径。
drop_cols = ["Name", "SibSp", "Parch", "Ticket"]
obj_col = "Survived"
train_df_path = "Data/train.csv"
test_df_path = "Data/test.csv"
sub_df_path = "Data/sample_submission.csv"

特征工程

在这些功能中,我们:

  1. 清洁驾驶室立柱
  2. 通过结合 SibSp 和 Parch 创建新的色谱柱。
def feature_engineering(raw_df):
    df = raw_df.copy()
    df["Cabin"] = df["Cabin"].apply(lambda x: x[:1] if x is not np.nan else np.nan)   
    df["Family"] = df["SibSp"] + df["Parch"]
    return df

模型结构

所有三个实验都与这个函数相关,因为我们要将 SGDClassifier 更改为另一个 sklearn 分类器。我们的主要关注点将是监控我们在测试和培训绩效指标上的准确性得分。对于基线,我们使用带有 loss="modified_huber "的简单 SGDClassifier。

def fit_model(train_X, train_y, random_state=42):
    clf = SGDClassifier(loss="modified_huber", random_state=random_state)
    clf.fit(train_X, train_y)
    return clf

分类特征

在训练我们的模型之前,我们需要使用标签编码器将分类数据转换为数字数据。在我们的例子中,我们将性别船舱上船作为分类列。

def to_category(train_df, test_df):
    cat = ["Sex", "Cabin", "Embarked"]
    for col in cat:
        le = preprocessing.LabelEncoder()
        train_df[col] = le.fit_transform(train_df[col])
        test_df[col] = le.transform(test_df[col])
    return train_df, test_df

估价

这个函数使用一个模型、X 和 y 来返回一个字典,其中包含二进制分类的所有可能的度量。对于 AUC平均精度分数,我们将使用预测概率,但对于其余指标,我们将使用简单预测,该预测将返回 10

def eval_model(clf, X, y):
    y_proba = clf.predict_proba(X)[:, 1]
    y_pred = clf.predict(X)
    return {
        "roc_auc": roc_auc_score(y, y_proba),
        "average_precision": average_precision_score(y, y_proba),
        "accuracy": accuracy_score(y, y_pred),
        "precision": precision_score(y, y_pred),
        "recall": recall_score(y, y_pred),
        "f1": f1_score(y, y_pred),
    }

提交

提交函数将预测测试数据集上的幸存者,并将数据帧保存在 CSV 中,以便我们检查公共和私有数据集上的模型性能。

def submission(clf, X):
    sub = pd.read_csv(sub_df_path)
    sub[obj_col] = clf.predict(X)
    sub.to_csv("Submission/submission.csv", index=False)

训练功能

这个函数是主函数,它将依次运行上述所有函数。

  1. 加载训练和测试数据集。
  2. 将特征工程功能应用于训练和测试 _df
  3. 删除不需要的列
  4. 使用标签编码器将分类列转换为数字列
  5. 用零填充缺失值
  6. 运行 dagshub_logger 以创建 metric.csv 和 params.yml
  7. 将训练数据集分为训练和测试
  8. 训练我们的分类模型
  9. 将模型另存为。joblib
  10. 将数据存储在 params.yml 中的日志记录超参数
  11. 对培训和测试进行评估
  12. 记录保存在 metric.csv 中的评估指标
  13. 正在创建提交文件。

主要的收获是 dagshub 库如何让我们轻松地跟踪性能和参数。DAGsHub 平台查看 metric.csv 和 params.yml,以在实验选项卡中创建交互式可视化。

你也可以通过创建一个 python 对象来做实验记录。

如果你有兴趣尝试各种其他方法来记录参数和指标,我建议你查看文档 DAGsHub 文档 教程

def train():
    print("Loading data...")
    df_train = pd.read_csv(train_df_path, index_col="PassengerId")
    df_test = pd.read_csv(test_df_path, index_col="PassengerId")
    print("Engineering features...")
    y = df_train[obj_col]
    X = feature_engineering(df_train).drop(drop_cols + [obj_col], axis=1)
    test_df = feature_engineering(df_test).drop(drop_cols, axis=1)
    X, test_df = to_category(X, test_df)
    X.fillna(0, inplace=True)
    test_df.fillna(0, inplace=True)
    with dagshub.dagshub_logger() as logger:
        print("Training model...")
        X_train, X_test, y_train, y_test = train_test_split(
            X, y, test_size=0.33, random_state=42, stratify=y
        )
        model = fit_model(X_train, y_train)
        print("Saving trained model...")
        joblib.dump(model, "Model/model.joblib")
        logger.log_hyperparams(model_class=type(model).__name__)
        logger.log_hyperparams({"model": model.get_params()})
        print("Evaluating model...")
        train_metrics = eval_model(model, X_train, y_train)
        print("Train metrics:")
        print(train_metrics)
        logger.log_metrics({f"train__{k}": v for k, v in train_metrics.items()})
        test_metrics = eval_model(model, X_test, y_test)
        print("Test metrics:")
        print(test_metrics)
        logger.log_metrics({f"test__{k}": v for k, v in test_metrics.items()})
        print("Creating Submission File...")
        submission(model, test_df)if __name__ == "__main__":
    train()

我们需要试验 Jupyter 笔记本中的所有功能,当我们认为代码中存在错误时,我们会创建一个包含所有代码的 main.py 文件。要运行这个文件,我们只需在终端中键入“python main.py”。

DAGsHub 回购

首先,您需要在 DAGsHub 上创建一个帐户,如果您已经有了一个帐户,您只需点击创建按钮,然后点击新建存储库,如下所示。

作者图片

添加您的存储库名称和描述。如果您想添加许可证或自述文件,您可以在该选项卡上完成所有操作,最后单击“创建存储库”。

作者图片

法斯特、吉特和 DVC

在我们初始化之前,我们需要创建一个项目文件夹并安装 fastds / dvc 。之后,创建文件夹数据、模型和提交。最后,用一个命令“fds init”初始化 Git 和 DVC

pip install fastds
pip install dvc
mkdir -p Data Model Submission
fds init

作者图片

我们可以使用 fds 将所有三个文件夹添加到 dvc 中,但是我们的两个文件夹是空的,因为我们还没有运行主 python 文件。fds CLI 将询问您有关数据文件夹的信息,并将其添加到 dvc,但文件夹的其余部分将被忽略,因此我们需要使用“dvc add”手动添加它们。将文件夹添加到 dvc 后,我们需要将这些文件夹添加到 gitignore 中,这样它们就不会被 git 版本控制跟踪。DVC 类似于 Git,但是它在跟踪更大的文件时非常灵活,比如大于 100 MB 的模型和数据集。

fds add Model Data Submission
dvc add Model
git add Model.dvc . gitignore

作者图片

我们将对 Submission 文件夹执行类似的操作,因为我们也在跟踪 submission.csv

dvc add Submission
git add Submission.dvc . gitignore

下图包含设置 git 和 dvc 远程服务器的帮助命令。

作者图片

首先,我们将通过添加我们的存储库 URL 来添加 git 远程服务,您可以从 DagsHub 存储库中的远程按钮复制该 URL。然后,我们将通过添加远程服务器链接、用户名和密码来添加 dvc remote。之后你就万事俱备了。好运

git remote add origin [https://dagshub.com//.git](https://dagshub.com//.git)
dvc remote add origin [https://dagshub.com/kingabzpro/DVC-ML-Experiments.dvc](https://dagshub.com/kingabzpro/DVC-ML-Experiments.dvc)
dvc remote modify origin --local auth basic
dvc remote modify origin --local user kingabzpro
dvc remote modify origin --local password your_token

现在,您需要通过使用 git add、git commit 和 git push 来提交和推送您的代码以远程访问服务器。

git add .
git commit -m "Initialized project"
git push -u origin master

如果您是第一次使用它,将会弹出安全窗口。您只需要添加您的用户名和私有令牌就可以开始了。

作者图片

DAGsHub 上的模型和实验

在这一部分,我们将使用三种不同的机器学习模型进行三个实验,我们将学习如何提交和推送 Git 和 DVC 到 DAGsHub。最后,我们将比较结果,探索惊人的度量可视化。

实验#1

用基线代码和一个简单的 **SGD 分类器运行我们的第一个实验。**运行 python 文件确实打印了一些训练和测试指标,但我已将其删除,以便进行简单的最终比较。

**python main.py**
Loading data...
Engineering features...
Training model...
Saving trained model...
Evaluating model...
Creating Submission File...

我们将在初始运行后提交对 dvc 和 git 的更改,以设置基线。下面的代码展示了提交更改,然后将这些更改推送到远程服务器是多么简单

dvc commit -f Model.dvc Submission.dvc
git add Model.dvc Submission.dvc main.py metrics.csv params.yml
git commit -m "SGDClassifier"

使用 git/dvc push,我们可以将数据、模型和代码推送到远程服务器。

git push --all
dvc push -r origin

下图显示了您的存储库在第一次运行后的样子。

作者图片

我们将单击“实验”选项卡,探索我们的结果。我已经删除了一些额外的列,接下来我将重命名实验名称。您还可以尝试其他选项,让您的结果看起来更容易理解。

我们的测试准确率是 60 %,这是非常糟糕的,f1 的分数是 0.17,非常糟糕。我们需要选择另一种模式或尝试不同的技术来获得更好的结果。

作者图片

实验#2

在第二个实验中,我们将把模型改为决策树分类器,然后再次运行整个过程。

  1. 运行 main.py
  2. 提交 git 和 dvc
  3. 推送 git 和 dvc
python main.pydvc commit -f Model.dvc Submission.dvcgit add Model.dvc Submission.dvc main.py metrics.csv params.ymlgit commit -m “DecisionTreeClassifier”git push –alldvc push -r origin

作者图片

正如我们在上面的图片中看到的,我们有另一个结果更好的实验。与 SGD 分类器相比,决策树表现得相当好。我们还可以通过单击下面所示的特定实验来详细研究所有指标和参数。

作者图片

我们有相当平衡的结果,我们将通过使用集合模型来改进它们。

实验#3

在第三个实验中,我们将把模型改为随机森林分类器,然后再次运行整个过程。现在我们是专家,正在进行实验,并把它推向 DAGsHub。

python main.py
dvc commit -f Model.dvc Submission.dvc
git add Model.dvc Submission.dvc main.py metrics.csv params.yml
git commit -m "RandomForestClassifier"
git push --all
dvc push -r origin

在完成第三个实验后,我们将通过选择所有三个提交并单击 compare 按钮来比较所有三个结果,如下所示。

作者图片

正如你在下面看到的,我们很容易比较不同的结果。根据您的指标,您将收到一个折线图或条形图。很明显,兰登森林在准确度f1 得分上表现相当不错。

作者图片

您可以向下滚动,甚至检查所有三个实验训练指标和详细比较。

私有数据集分数

让我们以性能最好的模型“submission.csv”为例,在私有隐藏数据集上检查我们的结果。如你所见,我们的模型表现很好,精度为 0.7364。您还可以通过将模型提交给 Kaggle completion 或使用 Kaggle CLI 来检查模型性能。

kaggle competitions submit -c tabular-playground-series-apr-2021 -f submission.csv -m "Message"

作者图片

结论

在本指南中,我们学习了如何使用各种工具来开始机器学习实验,以及 DAGsHub 平台有多强大。通常我们对大文件使用 git-lfs,但是它非常慢,你可能得不到一个更好的版本系统。这就是为什么 DVC 是数据科学版本系统的更好选择,因为我们必须处理相当大量的文件和模型。我们还学习了如何使用 fastds 以及如何使用 dagshub python 库创建实验指标。 MLflow 是 DAGsHub 平台支持的另一种方法,用于跟踪指标和模型性能。该平台还提供了可视化数据或机器学习管道的能力,如果您正在处理复杂的基础设施,这将非常有帮助。总的来说,我们已经学会了如何使用简单的机器学习模型来预测泰坦尼克号的幸存者,并使用工具来跟踪参数和性能指标。

DAGsHub Experiments 提供了灵活的选项来添加标签、重命名实验和简化列。它还提供了每个实验的详细摘要,并允许我们通过使用可视化来比较各种实验。

我很喜欢摆弄这些实验,并将它们推送到 DAGsHub 服务器上。为了将来的参考,我们可以使用 TensorFlow 训练一个深度学习模型,并使用 MLflow 来跟踪模型性能。我们还可以创建一个机器学习管道,使其按照特定的顺序运行,并在 DAGshub 的帮助下帮助我们可视化整个机器学习架构。

源代码

你可以在 DAGsHubkingabzpro/DVC-ML-Experiments上找到包含代码和实验的资源库。我会建议你叉我的仓库,自己实验。DAGsHub 的互动性相当强,你会立刻爱上它。

https://dagshub.com/kingabzpro/DVC-ML-Experiments

激动人心的 DAGsHub 实验项目

  • 自我监督何时能改善少投学习?——再现性报告:阿琼 2000 阿肖克/FSL-SSL
  • 这个库是 Python 3.8 和 TensorFlow 2.4 中深度解释惩罚的重新实现:shaileshsridhar 2403/Re-CDEP
  • 该项目使用 DAGsHub 中所有可用的功能来预测 Kaggle Titanic 数据集上的存活率: jnirschl/titanic_dvc

学习资源

  • 要获取 DAGsHub 实验的完整教程:概述— DAGsHub 文档
  • 观看关于 DVC 和数据版本控制的视频教程:用 DVC 进行数据版本控制(动手教程!)— YouTube
  • 了解如何处理泰坦尼克号数据集并产生最佳结果: Kaggle Titanic:机器学习模型(前 7%) |作者 Sanjay。M |走向数据科学
  • 了解 ml flow:ml flow 简介——机器学习生命周期的开源平台——YouTube
  • 了解禁食:禁食(dagshub.com)
  • Kaggle 竞赛最佳计分器解决方案: DAE +堆叠 CV 分类器| Kaggle

关于作者

阿比德·阿里·阿万 ( @1abidaliawan )是一名认证数据科学家专业人士,热爱构建机器学习模型和研究最新的人工智能技术。目前在 PEC-PITC 测试人工智能产品,他们的工作后来获得批准用于人体试验,如乳腺癌分类器。

你可以在我每周发布文章的 LinkedIn 上关注我。

原载于 2021 年 10 月 5 日【https://www.analyticsvidhya.com】

Python 文本编码指南

原文:https://towardsdatascience.com/a-guide-to-encoding-text-in-python-ef783e50f09e?source=collection_archive---------11-----------------------

自然语言处理笔记

教计算机理解人类语言

由约书亚·阿拉贡在 Unsplash 上拍摄的照片

我们对文本进行编码的原因是计算机不理解字符、单词或句子;计算机只能处理数字,因此,如果我们希望教会计算机理解自然语言,那么我们应该在将文本数据输入任何机器之前将其编码成数字格式。

因此,文本编码可以定义为将文本转换成有意义的数字/矢量表示的过程。在这个过程中,我们希望保留单词和句子之间的上下文和依赖关系,以便机器能够检测与文本相关的模式以及理解上下文。

对文本进行编码有多种方式:

  • 一键编码
  • 基于索引的编码
  • 单词袋(蝴蝶结)
  • 术语频率—反向文档频率(TF-IDF)
  • 单词嵌入

这不是全部的清单,但足够开始了。每种编码都有自己的优缺点,每种编码都可能更适合一项任务。BOW 等最基本的编码不保留单词的顺序,而其他编码则利用 Word2Vec 等神经网络。

让我们用 Python 实现这些。

一键编码

一键编码将分类变量表示为二进制向量。要实现这一点,首先应该将分类值映射到一个整数,然后将每个整数表示为一个二进制向量——这意味着该向量除了整数的索引之外都是 0。

# define the documents
corpus = ["i cant wait to get out of lockdown", "the uk is soon going to be free soon", "linkedin is social media"]# converting text to intergers
token_docs = [doc.split() for doc in corpus]
all_tokens = set([word for sentence in token_docs for word in sentence])
word_to_idx = {token:idx+1 for idx, token in enumerate(all_tokens)}# converting the docs to their token ids
token_ids = np.array([[word_to_idx[token] for token in token_doc] for token_doc in token_docs], dtype=object)
token_ids_padded = pad_sequences(token_ids, padding="post")
token_ids = token_ids.reshape(-1, 1)# convert the token ids to one hot representation
one_hot = OneHotEncoder()
X = one_hot.fit_transform(token_ids_padded)# converting to dataframe
X_df = pd.DataFrame(X.toarray())
X_df

一键编码实施—按作者分类的图像

每个文档由一个张量表示,每个张量由一系列 0 和 1 组成。尽管一键编码保留了单词顺序,但随着文档变长,我们最终会得到非常大的稀疏向量。

基于索引的编码

基于索引的编码也保留了单词顺序。要实现基于索引的编码,只需用一个索引映射每个单词。我们还需要给每个句子添加填充,这样它们的长度就都相等了。基于字典中的映射,每个文档通过一系列索引来表示,其中每个数字是一个单词的编码。

import numpy as np
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from tensorflow.keras.preprocessing.sequence import pad_sequences# define the documents
corpus = ["i cant wait to get out of lockdown", "the uk is soon going to be free soon", "linkedin is social media"]# converting text to intergers
token_docs = [doc.split() for doc in corpus]
all_tokens = set([word for sentence in token_docs for word in sentence])
word_to_idx = {token:idx+1 for idx, token in enumerate(all_tokens)}# converting the docs to their token ids
X = np.array([[word_to_idx[token] for token in token_doc] for token_doc in token_docs], dtype=object)# padding the sequences
X_padded = pad_sequences(X, padding="post")# converting to pandas df
X_df = pd.DataFrame(X_padded) 

索引编码数据帧—按作者分类的图像

注意:列名代表单词在文本中出现的位置

基于索引的编码之所以失败,是因为它在文本之间引入了不真实的数字距离,例如,为什么单词“cat”的索引是数字 1,而“dog”是数字 324?

单词袋(蝴蝶结)

单词包编码技术的名称来源于这样一个事实,即文档中单词的任何信息或结构都将被丢弃,因此,这就像您将一组单词放入一个包中并摇晃它,它只关心一个单词是否出现在文档中以及它出现的次数。它不在乎它出现在哪里。

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer# define the documents
corpus = ["i cant wait to get out of lockdown", "the uk is soon going to be free soon", "linkedin is social media"]# implementing BoW
bow = CountVectorizer()
bow.fit(corpus)
X = bow.transform(corpus)# converting to dataframe
X_df = pd.DataFrame(X.toarray(), columns=sorted(bow.vocabulary_))
X_df

BoW 数据框—作者图片

虽然 BoW 模型是一个非常容易掌握和实现的模型,但它在许多方面都存在不足,最明显的是失去了意义,因为我们放弃了词序,我们失去了使用该词的上下文。

术语频率—反向文档频率(TF-IDF)

对词的频率进行评分的问题是,尽管没有给文档增加多少价值,但高频词开始在文档中占据主导地位。例如“和”、“该”、“一个”等词语。在英语中很常见,所以这些单词在文档中出现得更频繁是很常见的。TF-IDF 认识到了这个问题,并对所有文档中最突出的单词进行了惩罚。

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer# define the documents
corpus = ["i cant wait to get out of lockdown", "the uk is soon going to be free soon", "linkedin is social media"]# implement tfidf
tfidf = TfidfVectorizer()
tfidf.fit(corpus)
X = tfidf.transform(corpus)# convert to dataframe
X_df = pd.DataFrame(X.toarray(), columns=sorted(tfidf.vocabulary_))
X_df.T

TF-IDF 数据帧的转置—作者提供的图像

然而,由于 TF-IDF 编码是基于 BOW 编码的,它也不能捕获文本中的单词位置、语义以及在不同文档中的共现,但是它也很容易计算。

单词嵌入

单词嵌入是迁移学习的一个很好的例子。迁移学习是机器学习中的一个研究问题,它专注于存储在解决一个问题时获得的知识,并将其应用于不同但相关的问题。【来源 : 维基百科。

深入各种单词嵌入或迁移学习如何工作的全部细节超出了本文的范围,但是我们仍然会看到如何实现 Word2Vec 编码。

from gensim.models import Word2Vec# define the documents
corpus = ["i cant wait to get out of lockdown", "the uk is soon going to be free soon", "linkedin is social media"]# Word2Vec model
w2v = Word2Vec(min_count=1,
               size=20,
               window=2,
               alpha=0.03)# building the vocab
w2v.build_vocab(sentences=corpus)# training the model
w2v.train(sentences=corpus, total_examples=w2v.corpus_count, epochs=10)

:NLP 会专门为迁移学习做一篇文章。

包裹

对文本数据[和所有其他类型的数据]进行编码的目的是确保数据可以被机器正确地使用。在本文中,我介绍了 5 种不同的文本数据编码方法。当上下文真的很重要时,我喜欢使用分布式表示,比如单词嵌入,但是有很多例子表明像单词袋这样简单的技术是有效的,比如分类任务。

感谢阅读!在 LinkedIn 和 Twitter 上与我联系,了解我关于人工智能、数据科学和自由职业的最新帖子。

相关文章

</a-guide-to-cleaning-text-in-python-943356ac86ca?source=collection_tagged---------0----------------------------> </5-ideas-for-your-next-nlp-project-c6bf5b86935c>

估计效率指南

原文:https://towardsdatascience.com/a-guide-to-estimator-efficiency-bae31a06e570?source=collection_archive---------15-----------------------

实践教程

使用真实世界的数据集解释效率的概念及其用法

在本文中,我们将讨论以下主题:

  • 什么是效率?
  • 什么是统计估计量,如何定义它的效率?
  • 如何计算一个估计量的效率?
  • 如何利用效率建立更好的回归模型?

让我们开始吧!

什么是效率?

用外行人的话说:

效率是衡量你投入的每单位时间、精力或金钱能从一件事情中得到多少使用的标准。

几乎任何事情的效率都可以用有用产出与总投入的来表示:

效率的一般定义(图片由作者提供)

下面是效率的两个例子:

  • **电动机的效率:**直流电动机的效率是在其轴上测得的功率输出与泵入其中的总 DC 电功率之比。
  • **生产效率:一个国家乃至整个地球的生产效率,往往以其 GDP 与人口之比来衡量,又称人均 GDP。**2020 年地球的人均 GDP 为10925 美元。

**效率是一个无量纲的量。**但也有例外。在某些领域,尤其是在经济学领域,效率有一个维度,通常是货币维度,如人均 GDP。

当表示为无量纲量时,效率是一个在 0.0 到 1.0 之间变化的实数,表示任何设备的有用输出至多与泵入设备的总输入一样高。

有了这个背景,让我们把注意力转向统计科学中定义的效率。我们将首先介绍统计科学中的一个基本工具,即统计估计量。

什么是统计估计量?

让我们陈述一下评估者的非正式定义:

估计量是一种统计方法,用于估计总体中某些参数(如平均值或中值)的真实但未知的值。这是通过使用构成值样本的数据点中包含的信息来实现的。

估计量的例子

给定一个 n 值*【y _ 1,y_2,…,y _ n】*的样本,这里有一些总体均值 μ 估计量的例子(有好有坏):

随机选择的均值(不好!):

该估计器通过指定从样本中随机选择的值为 μ(作者图片)来估计总体平均值 μ

n 个值的平均值(好):

该估计器通过取 n 个样本值*(图片由作者提供)*的平均值来估计总体 μ 的平均值

可以证明n 值平均估计量比随机选择估计量具有更好的性质。具体来说,n 值平均值估计量比随机选择估计量具有更低的方差,并且它是总体均值 μ 的一致估计量。

线性模型的估计器

让我们来看看一个常用回归模型中使用的估计量。以下估计器估计条件均值 μ ,即以回归变量向量 X 为条件的均值,采用一组特定的观察值*【X _ 1,x_2,…x_m】。μ_cap* 是使用 θ_cap 计算的估计条件均值,θ_cap 是拟合模型系数的向量。

线性模型的估计量(图片由作者提供)

现在让我们把注意力回到手头的话题:效率。

和其他事情一样,计算统计估计量的效率是可能的。

估计量的效率

考虑一个估计器 T ,它被设计用来估计(预测)一些总体参数 *θ。*我们刚刚复习了几个 T 和 *θ的例子。*例如,T =总体均值的 n 值平均值估计量 μ *即θ=μ。*这种估计器的效率 T 表示为两个方差的比值,如下所示:

总体参数θ估计量 T 的效率(图片由作者提供)

为什么 Tθ 的估计会有方差?这是因为,在现实世界中, T 无法访问整个群体的值。事实上,如果是这样的话, T 将不再需要“估计”任何东西。相反,在现实中, T 总是呈现一个值的样本。并且每次测试对该样本进行处理时,都有可能产生对总体参数 θ 的不同估计。例如,如果您向 n 值平均值估计器 T 提供 100 个数据样本,每个样本的大小为 n ,那么估计器将产生总体平均值 μ 的 100 个估计值。这 100 个对 μ 的估计会显示出真实总体均值 μ 附近的一些差异。这个方差叫做 T 的方差。

有了上面的想法,很容易看出,如果你要为同一个总体参数 θ 设计两种不同类型的估计量 T1T2 ,那么它们有可能(实际上很有可能)会各自表现出不同的特征方差。假设 Var(T1) > Var(T2) 。现在,假设有人对 θ 提出了第三种估计量 T3 ,使得 Var(T3) 甚至低于 T2 ,以此类推。人们可能有理由怀疑,如果给定一个总体参数 be,那么 θ的估计量所显示的方差是否有一个下界。原来在一定条件下,恰好存在这样一个下界,它被称为 克莱姆-拉奥界

将我们的注意力转回效率方程,我们看到上式中的分子是 克拉姆-拉奥界限

假设你设计了一个估计量 T ,它的实际方差等于克拉美–拉奥界,那么这意味着你的估计量的效率是完美的 1.0。在所有其他情况下,估计器的效率范围为*【0 到 1.0】*。

无偏估计量的效率

如果估计量为 无偏 ,则克莱姆-拉奥界是估计量的 费希尔信息 I(T(θ)) 的倒数。

因此,对于某总体参数 θ 的一个无偏估计量 T ,则 T(θ) 的效率表示为:

总体参数θ的一个无偏估计量 T 的效率(图片由作者提供)

费希尔信息可能是一个需要理解的复杂概念。我们将按以下方式描述它:

费希尔信息

假设你正在处理一个随机变量 T ,假设它遵循某种概率分布 f(。),如正态或泊松分布。假设函数 *f(。)*接受某个参数 θ。θ的例子有正态分布的平均值 μ ,或者泊松分布的平均事件率 λ 。那么 T 的 Fisher 信息提供了一种方法来衡量 T 包含的关于θ 的真实总体值的信息量(比如总体的真实均值)。

理解估计量效率概念的三种不同方式

估计量的效率是对其特征不止一个方面的度量。以下是考察评估者效率的三种相关方法:

估计器效率作为其精度的度量

与完全有效的估计量相比,*估计量的效率是对其估计的参数的真实总体值的“紧密”程度的衡量。*完全有效的估计量是其方差等于该类估计量的克拉美-罗界的估计量。因此,效率的概念直接基于估计者预测的变化程度。估计者预测的方差概念非常重要,我们将很快说明如何使用真实世界的数据集来计算它。

作为所需最小样本量决定因素的估计效率

估计器的效率也是对你需要多少(或多少,取决于你的观点)数据点来达到期望的估计质量水平的一种度量。评估的质量可以用多种方法来衡量。一种流行的度量是损失函数,例如均方误差(MSE) 。这里的想法是,一个高效的估计器将需要比它的低效率兄弟更小的样本,以生成等于或低于 MSE 的期望阈值的预测。

由于获取数据总是一件昂贵的事情,所有其他事情大致相同,它可以帮助您获得一个高效的估计器,而不是追逐问题的最大数据集。

估计量效率作为比较两个估计量的一种方式

efficiency 属性还为您提供了一种比较样本问题和相同样本数据集的两个竞争估计量的估计精度(和准确度)的方法。或者,估计器的效率为建模者提供了一种方法,如果他们选择的估计器需要匹配精度(或准确度——记住它们是而不是同一个东西,就可以确定样本大小需要多大(或多小)!)的竞争估计量。

一个重要的特例

两个估算器的效率可以通过简单地比较各自估算器预测的方差来进行比较,即方差较低的一个被认为更有效,假设满足以下条件:

  1. 这两个估计量用于预测总体的同一个参数。例如,两者都是总体均值的估计量。
  2. 这两个估计器属于同一类,即由这两个估计器产生的预测遵循相同的概率分布。例如,由两个估计器产生的估计是泊松分布的。在这种情况下,两个估计量对于它们正在估计的总体参数具有相同的 Fisher 信息。
  3. 这两种估计量都是它们所估计的总体参数的无偏估计量。在这种情况下,它们的 Fisher 信息的倒数就是方差的 Cramer-Rao 界,这又使得两个估计量的方差的 Cramer-Rao 界相同。

当上述三个条件成立时,效率方程的分子,即方差的下限,对于两个估计量是相同的。因此,可以通过简单地比较它们各自预测的方差来比较这些估计量的效率。

因此,我们得到了以下重要结果:

在一组无偏估计量中,如果某个总体参数 θ 的预测服从相同的概率分布,那么预测方差最小的估计量就是最有效的估计量。

我们现在来看看如何计算 n 值平均值估计量的效率方程的分子和分母。

让我们从分母开始:估计者预测的方差

估计量的方差是什么意思,如何计算?

使用以下思维实验可以最好地理解评估者预测的方差概念:

假设您希望在夏季的几个月中估计某个公共海滩上每立方毫升海水中的细菌平均数量。为此,你在一天的不同时间在海滩收集 100 个水样,并测量每个样本中的细菌数量。这是你的细菌计数样本数据集:【y _ 1,y_2,…,y _ 100】。接下来,您决定使用 n 值平均值估计器来估计平均细菌数,并使用这个观察到的样本平均值 y_bar 作为您对总体平均值 μ 的估计。总之,您使用 n 值样本平均值 y_bar 作为总体平均值 μ 的估计值。

但是现在假设,你的一个朋友在海滩上随机选择的 100 个地方收集了另一组水样。他们将获得第二组 100 个细菌计数: *[y_1,y_2,…,y_100]而另一个样本意味着 y_bar_2。*假设 200 人重复这个过程,他们会在他们自己当中,以 200 的样本结束 y_bar_1,y_bar_2,…,y_bar_200 。这些 200 样本均值本身将围绕真实总体均值 μ分布(近似正态分布)。因此可以计算这些样本均值的方差。

因此,总体均值μ的 n 值均值估计值本身是一个随机变量,它遵循一个既有均值又有方差的概率分布。

可以看出n 值平均值估计量是总体均值的无偏估计量,即它的期望值实际上是总体均值 μ 。此外,该估计量是一致的,这意味着它的预测将收敛于总体均值 μ 作为 n → N ,即整个总体的大小。在海滩示例的情况下, N 可以被安全地认为是无穷大。

还可以看出n 个值的平均值估计量的预测方差是 σ /n ,其中 σ 是我们为了构建大小为 n 的样本而使用的基础总体值的方差。

事实上,估计量方差的概念对于效率的计算非常重要,我们将用一个真实的例子来说明它。

估计量方差的真实例子

我们将使用从 Zillow Research 根据其免费使用条款下载的以下 30K+数据点数据集:

房价同比百分比变化预测(来源: Zillow Research

数据集中的每一行都包含对美国特定地理位置的房价年同比百分比变化的预测。该值在列 ForecastYoYPctChange 中。

我们的目标是估计全美房价同比百分比变化的平均预测值,即人口平均值 μ

假设我们不能一次性访问这个 30K 行的完整数据集。相反,我们碰巧只能进入 100 个随机选择的地点。我们需要使用 100 点样本来估计全美房价年同比百分比变化的平均预测。

以下 Python 代码阐释了这项任务。

  • 我们将把数据集加载到内存中
  • 接下来,我们将随机选择 100 个数据点进行替换。“with replacement”技术确保每个数据点独立于任何其他点进行选择。这种技术会在我们的样本中导致重复,但是当值的总体很大时,重复的机会是最小的。从好的方面来看,需要使用替换技术进行采样,以使统计数学能够很好地工作。
  • 最后,我们将使用 n 值平均值估计器来估计总体平均值 μ
**import** math **import** pandas **as** pd
**import** numpy **as** np
**from** matplotlib **import** pyplot **as** plt
**from** scipy.stats **import** norm**#Load the data file**
df = pd.**read_csv**(**'zhvf_uc_sfrcondo_tier_0.33_0.67_month.csv'**, **header**=0, **infer_datetime_format**=True, **parse_dates**=[**'**ForecastedDate**'**])**#Randomly select 100 data points with replacement** df_sample = df.**sample**(**n**=100, **replace**=True)**#Print the mean of the sample. This is our estimate of the population mean mu
print**('Estimate of population mean mu='+**str**(df_sample['ForecastYoYPctChange'].**mean**()))

现在,让我们重复上述过程 100 次,以产生总体均值 *μ的 100 个独立估计。*我们将绘制这 100 个平均值,看看预测值的分布情况:

means = []
for i in **range**(100):
    df_sample = df.**sample**(**n**=100, **replace**=True)
    means.**append**(df_sample['ForecastYoYPctChange'].**mean**())**#Plot the distribution** plt.**hist**(means, **bins**=25)
plt.**show**()

我们看到下面的情节:

样本均值的频率分布(图片由作者提供)

如果我们继续这种抽取大小为 n (=100) 的样本的做法,我们会发现频率分布将在真实总体均值 μ处开始达到峰值。

这是 10,000 个样本平均值的频率分布:

10000 个样本均值的频率分布(图片由作者提供)

我们得出了以下重要的观察结果:

n 值平均值估计器的预测本身是一个随机变量,它遵循一个具有平均值和方差的概率分布。

如果估计量是无偏的,其预测的均值将与真实总体均值 μ 相同,因为预测的数量趋于无穷大:

如果估计量是无偏的,其预测的期望值 μ_cap 等于总体均值μ(图片由作者提供)

我们感兴趣的是估计者预测的方差。

以下是均值估计器 10,000 次预测的方差:

print('Variance of the estimator='+str(np.**var**(means)))

它打印出以下内容:

Variance of the estimator=**0.020872772633239996**

上述对方差的深入研究也给我们带来了意想不到的红利。它使我们能够估计房价变化预测总体的方差 σ 。回想一下,n 值均值估计量的方差是 σ /n ,其中 σ 是基础总体的方差,n =样本量=100 。所以我们可以估计总体的方差为 2.08728。

绕回来…

让我们回到估计器 T 的效率等式,它产生某个总体参数 θ 的无偏估计:

总体参数θ的无偏估计量 T 的效率(图片由作者提供)

到目前为止,我们已经对估计者预测中的方差概念有了一些了解,也就是上式中的分母。

为了计算分子,我们需要知道有关估计量的费雪信息。为了知道费雪信息,我们需要知道估计量的预测的概率分布。

对于我们用来估计房价同比变化的 n 值平均值估计值,我们知道以下信息:

  1. n 值均值估计器的预测在估计数趋于无穷大的渐近情况下近似正态分布(这一点可以证明)。
  2. n 值平均值估计器生成总体平均值 μ 的无偏估计。因此,n 值平均值估计器预测的均值就是总体均值μ
  3. 估计者预测的方差是σ/n其中 σ 是价格变化的潜在总体的方差,而 n 是取平均值的样本大小。在我们的例子中, n=100

因此,我们可以对 μ 的 n 值平均值估计量的概率分布陈述如下:

n 值均值估计器的预测值呈正态分布,均值等于总体均值μ,方差等于总体方差 σ,按样本大小 n 缩放(图片由作者提供)

可以证明,正态分布且已知方差 σ 的未知总体均值 μ 的一个估计量的 Fisher 信息,就是 1/ σ。

因此,总体均值 μ 的 n 值均值估计量的 Fisher 信息为 n/σ。

再次回忆一下,这个估计器预测的方差是 σ /n

因此,总体参数 μ 的 n 值均值估计的效率为:

总体平均值的 n 值平均值估计量的效率 μ是完美的 1.0(图片由作者提供)

这是一个重要的结果。它表明 n 值平均值估计量尽管简单,却是一个有效的估计量。

总体均值 μ 的 n 值均值估计量是一种有效的估计量。

相关阅读

引用和版权

Fisher R. A. ,(1922)论理论统计的数学基础,*伦敦皇家学会哲学汇刊。A 辑,包含数学或物理性质的论文。*222309–368。【http://doi.org/10.1098/rsta.1922.0009

形象

本文中的所有图片的版权归 CC-BY-NC-SA 所有,除非图片下方提到了不同的来源和版权。

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

对预期目标的探索

原文:https://towardsdatascience.com/a-guide-to-expected-goals-63925ee71064?source=collection_archive---------12-----------------------

使用事件数据可视化与镜头相关的趋势和概率

作者图片

在这里,我将介绍预期目标(xG)的概念,并对事件数据进行探索。这将是关于预期目标的三部分系列的第一部分。第二部分将围绕从该事件数据构建机器学习模型,而第三部分将探索该模型的应用、优势和不足。

xG 是什么?

足球比赛的结果比其他任何运动都更容易受到随机时刻和“运气”的影响。仅仅是失误、射门偏出、守门员失误和有争议的裁判判罚就能决定最终结果。足球是一项英寸的运动。

这些影响由于进球是罕见的事件而被放大;一场比赛平均产生 2.5 个进球。此外,大多数比赛以平局告终,或者仅仅由几个进球决定,这意味着一个进球对比赛结果有很大的影响。

作者图片

因此,当如此多的比赛由细微的差距定义时,运气和随机性会产生显著的影响。这也使得绩效难以评估;顽强的 1-0 胜利是值得的表现还是一系列幸运事件的产物?有时候,这是很难用肉眼评价的。我们希望通过在检查比赛时尽可能消除随机性来量化和限定表现。

为了进球,你必须首先尝试射门。十年前,评估一场比赛的表现只需要看一下总投篮次数和目标投篮次数。虽然这些是评估机会创造的有用工具,但它们并不能说明全部情况,因为并非所有的机会都是平等的。有许多因素影响射门得分的可能性。

这就是 xG 发挥作用的地方。xG 根据许多因素来衡量射门得分的概率。这些因素包括距离射门位置的距离、相对于球门线的角度、比赛状态(比分是多少)、是否是头球、射门是否来自反击以及其他因素。为了简单起见,我们的探索将只关注其中的三个因素。我们可以使用这一指标对一场比赛中的所有机会进行求和,以根据我们在模型中汇总的因素来确定一支球队应该进多少球。我们甚至可以进一步将这一点应用到一系列比赛、一个赛季甚至一个经理的任期中。

因此,xG 可以作为一支球队进攻实力和后防实力的衡量标准。它还可以用来分析球员在危险区域创造射门机会的能力以及他把握机会的能力。总之,当我们试图量化一支球队的进球能力时,xG 模型帮助我们消除了一部分与得分机会相关的随机因素,这最终是足球的最终目标。

我们将在后面看到,我们可以使用 xG 来预测未来的结果,指导球员招募决策和评估教练指导,但首先让我们尝试探索一些数据。

数据探索

在我们开始构建 xG 模型之前,我们需要考虑我们对什么样的数据感兴趣。显然,我们需要大量的射门数据,但更重要的是,我们需要这些数据来描述导致进球的射门类型。我们可以推断出,我们需要的最重要的因素是拍摄时离球门的距离、相对于球门的角度以及拍摄时身体的哪一部分。

图 1:请注意,角度是指射门位置与球门线所成的角度。注意它是如何根据位置变化的。图片作者。

足球数据通常分为两种形式:事件数据和跟踪数据。事件数据记录所有有球事件以及它们发生在球场上的位置(如射门、传球、铲球、运球),而跟踪数据记录球员的位置和球在整个比赛中每隔一段时间的位置。

我今天将使用的事件数据来自 Wyscout。它涵盖了 2017/2018 赛季欧洲 5 大国内联赛(英超、法甲、德甲、西甲、意甲)所有比赛的所有赛事。

虽然这一部分的一些发现对那些对足球有广泛了解的人来说似乎是初级的,但我始终认为检验我们的假设是重要的,因为它们有时会误导人。我认为最好的开始是问,大多数投篮发生在球场的什么地方?

作者图片

马上,我们可以得出一些结论。该分布表明:

1\. A majority of shots happen between 10 and 20 meters. 2\. Shots taken within about 6 meters are quite rare in comparison to shots taken outside 10 meters3\. Oddly enough, there is a trough in the local region taken between 18m to about 25m4\. As expected, players rarely take shots from angles less than 5 degrees, corresponding to central shots close to goal.

角度分布与距离分布一致,因为从更近(更大的角度)拍摄的镜头更难产生。

仅仅通过一个简单的分布图,我们就可以得出结论,要打出接近球门中心的射门是相当困难的。虽然我们现在知道射门是如何根据距离和角度分布的,但我们还没有解决导致进球的射门与没有进球的射门有何不同。

作者图片

上面的 violin 图与盒须图的作用相似,但也提供了数据的核分布估计(本质上是分布的平滑)。在根据射门结果划分数据时,我们可以看到,平均而言,导致进球的射门比没有进球的射门更接近球门。射门得分的平均距离约为 12 米,相比之下,那些没有进球的平均距离约为 18 米。类似地,球门通常从 20 度到大约 50 度的角度得分。

因此,虽然很难在球门附近创造射门机会,但小提琴的情节表明,那些靠近中心的射门机会往往会导致进球。

让我们看看标题是如何影响均值和分布的。

作者图片

正如我们所料,头球通常是在 18 码(16.5 米)内完成的。有趣的是,结果的平均值和分布相差不大,所以这是我们应该考虑的事情。

我们通过一些基本的分布图获得了一些惊人的洞察力,但我们可以更进一步。通过绘制球场上的投篮密度,我们可以更好地想象这些变量如何影响结果。也就是说,我们希望将间距分成多个面元,计算每个面元内拍摄的镜头数量,然后使用颜色渐变来显示面元之间的密度差异。

作者图片

这些密度图的功能与上面的小提琴图类似,但让我们更直观地了解球场的哪些区域通常会射门和进球。这是因为我们可以看到距离和角度如何影响同一地块上的镜头分布。正如我们从小提琴的情节中了解到的:

1\. Shots are seldom taken from either side the box due to the poor angle2\. A majority of the shots are taken around the penalty spot (11m)3\. Goals are normally scored within 11 meters and within a very narrow passage

在我们深入研究之前,我们需要解决为什么盒子边缘的镜头数量急剧减少。这可能有多种原因。

1.We have generalize that all football pitches are 105m x 68m when in reality every pitch has its own unique dimensions. The trough may be a product of our generalization.2.Another factor may be due to how Wyscout records their data. There may be an inclination to record a shot near the penalty box line as either inside the box or outside. Therefore it is possible that shots that happen on the line are being mischaracterized. 3.Lastly, there may be a psychological effect happening to the players. Players may resist the temptation to shoot when just outside the box in order to dribble into the box in the hope of winning a penalty. Defenders tend to defend much more tentatively when the opposition are in the box due to the possibility of a costly foul and it is possible that attackers want to take advantage of this.

还有其他的可能性,很难对低谷做出任何结论,但是现在我们必须接受它。

现在回到更多的数据可视化。正如你可能猜到的,我们可以绘制一个概率密度来评估球场的哪些区域有很高的射门概率,哪些区域没有。

作者图片

正如所料,你离球门越近,射门就越有可能进球。请注意,存在某些异常值,这些异常值中的概率密度非常高。这是因为从那些区域拍摄的一些镜头,它们导致了进球。如果我们有 10 个季度的数据,我们会看到一个更加同质的概率密度。

对于那些从未使用过 xG 的人来说,最令人惊讶的事情之一是,在 11 米以外进球的概率实际上比我们在现场观看比赛时所欣赏的要低。正是由于这个原因,这种分析是重要的。我们倾向于高估机会的质量,例如禁区外的射门,而事实上射门是非常困难和低效的。所以,下次你最喜欢的球员在 11 米开外投篮不中时,记住他只有十分之三的机会得分。

那么头球呢?

作者图片

虽然头球表现出与常规投篮相似的趋势,但它们的总体概率值较低。这似乎表明,虽然平均来说头球离球门更近,但它们也代表了一个更难把握的机会。正如我们将看到的,这是一个重要的发现,影响了我们对机会评估的解释。

在我们进入一些机器学习之前,我想结束我们对这些趋势的一些见解。我们假设球门线的距离和角度都会影响射门得分的概率,我们已经从上面的图表中看到了这一点。但是这种关系的本质是什么呢?随着我们远离目标,得分的概率如何变化?我们可以像以前一样,用一些精心设计图表来解决这些问题。

作者图片

第一件引人注目的事情是,当我们离目标越远,得分的可能性就越大。这意义深远,因为它极大地降低了远距离拍摄的价值。为什么会这样呢?到目前为止,我们忽略了这样一个事实,即随着我们远离目标,与目标的角度会减小。所以我们有这种距离的“加倍因子”。我们可以假设,这是因为当我们增加射击距离时,不仅距离更远,而且目标也变得更小。

太棒了。现在,我们已经看到了数据可视化的力量,以及即使是最简单的图表如何帮助我们辨别大型数据集背后的信息。正是由于这个原因,在数据探索上花费时间和精力是如此重要。这将为我们在下一部分讨论机器学习打下良好的基础。回头见!

这里所有与分析和可视化相关的代码,请访问我的 github 。

PySpark 中随机森林分类器开发指南

原文:https://towardsdatascience.com/a-guide-to-exploit-random-forest-classifier-in-pyspark-46d6999cb5db?source=collection_archive---------2-----------------------

鸢尾花分类实用说明指南

照片由阿德埃尔·grőber在 Unsplash 上拍摄

在本文中,我将一步一步地指导你如何使用 PySpark 通过随机森林分类器对鸢尾花进行分类。

我使用了流行的 Iris 数据集,并在文章末尾提供了数据集的链接。我使用 Google Colab 进行编码,我还在参考资料中提供了 Colab notebook。

Pyspark 是 Apache Spark 的 Python API,pip 是 Python 包的包管理器。

!pip install pyspark

使用上面的命令,可以使用 pip 安装 pyspark。

from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('ml-iris').getOrCreate()
df = spark.read.csv('IRIS.csv', header = True, inferSchema = True)
df.printSchema()

首先,我需要为 Spark 中的所有功能创建一个入口点。SparkSession 类用于此目的。SparkSession.builder()创建一个基本 SparkSession。使用appName(name)为应用程序设置名称。在这里,我将“ml-iris”设置为应用程序名称。没有必要设置名称,如果没有设置,将为应用程序生成一个随机名称。getOrCreate()如果没有现有会话,则创建新的 SparkSession。否则,它将获取现有会话。

spark.read.csv(“path”)用于将 CSV 文件读入 Spark 数据帧。数据框是一种 2D 数据结构,它以表格格式设置数据。Iris dataset 有一个头,所以我设置了header = True,否则 API 会把头当作一个数据记录。inferSchema 属性与列类型相关。默认情况下,inferSchema 为 false。它将以字符串的形式给出所有的列。这里我设置了inferSchema = True,所以 Spark 遍历文件,推断每一列的模式。printSchema()将以树形格式打印模式。

输出:

import pandas as pd
pd.DataFrame(df.take(5), columns=df.columns).transpose()

pandas 是一个用于数据分析的工具包。这里df.take(5)返回前 5 行,df.columns返回所有列的名称。DataFrame.transpose()转置数据帧的索引和列。它将列写成行,将行写成列。

输出:

numeric_features = [t[0] for t in df.dtypes if t[1] == 'double']
df.select(numeric_features).describe().toPandas().transpose()

df.dtypes返回所有列的名称和类型。这里,我们将 Double 类型的列分配给 numeric_features。select(numeric_features)返回新的数据帧。describe()计算统计数据,如列的计数、最小值、最大值、平均值,toPandas()返回当前数据帧作为熊猫数据帧。

输出:

由于我们对正在处理的数据集有了很好的了解,我们可以开始进行要素转换。特征转换意味着缩放、转换和修改特征,以便它们可以用于训练机器学习模型,从而做出更准确的预测。为此,我使用了字符串索引器和向量汇编程序。

首先,我使用 Vector Assembler 将萼片长度、萼片宽度、花瓣长度和花瓣宽度组合成一个向量列。在这里,新的单一向量列称为 features。

输出:

然后,我使用字符串索引器将物种的字符串列编码为标签索引的列。默认情况下,标签根据频率进行分配。因此,最常见的物种的指数为 0。

输出:

如您所见,我们现在有了名为 labelIndex 和 features 的新专栏。

pd.DataFrame(df.take(110), columns=df.columns).transpose()

最下面一行是 labelIndex。我们可以看到 Iris-setosa 的标签索引为 0,Iris-versicolor 的标签索引为 1。Iris-virginica 的 labelIndex 为 2。

现在,我们已经转换了我们的功能,然后我们需要将数据集分成训练和测试数据。

randomSplit()将数据帧随机拆分成训练集和测试集。在这里,我为可复制性设置了种子。0.7 和 0.3 是分割作为列表给出的数据集的权重,它们的总和应为 1.0。

输出:

现在我们可以导入和应用随机森林分类器。随机森林是一种通过在训练阶段构建多个决策树来运行的方法。随机森林选择大多数树的决策作为最终决策。

随机森林分类器的表示(图片由作者提供)

它属于监督学习,主要用于分类,但也可以用于回归。随机森林分类器是有用的,

  1. 不要过度拟合
  2. 高准确度
  3. 估计缺失数据

这里的featuresCol是数据框的要素列表,在我们的例子中是要素列。labelCol是目标特征,即 labelIndex。rf.fit(train)将随机森林模型拟合到我们名为 train 的输入数据集。rfModel.transform(test)转换测试数据集。这将向数据框中添加新列,例如预测、原始预测和概率。

输出:

通过下面的输出,我们可以清楚地比较实际值和预测值。

predictions.select("labelIndex", "prediction").show(10)

输出:

现在,我们已经将分类器应用于我们的测试数据,并且得到了预测。然后我们需要评估我们的模型。

multi classclassificationevaluator 是多类分类的赋值器。因为我们有 3 个类(鸢尾-Setosa,鸢尾-Versicolor,鸢尾-弗吉尼亚),我们需要多类分类评估器。方法evaluate()用于评估分类器的性能。

输出:

现在可以看到我们的模型精度很高,测试误差很低。这意味着我们的分类器模型表现良好。

我们可以使用混淆矩阵来比较预测的鸢尾物种和实际的鸢尾物种。

MulticlassMetrics 是 pyspark mllib 库中多类分类的评估器。

输出:

根据混淆矩阵,在 47 个测试数据中有 44 个(12+16+16)物种被正确分类。3 种被错误分类。

我希望这篇文章能帮助您学习如何使用 PySpark 并使用随机森林分类器完成分类任务。我在下面提供了数据集和笔记本链接。

编码快乐!😀

资源

资料组

https://www.kaggle.com/arshid/iris-flower-dataset

Colab 笔记本

https://colab.research.google.com/drive/1en1X946wqbv4SC_cCLmspJa7iGL8DvYo?usp=sharing

最优化遗传“学习”算法指南

原文:https://towardsdatascience.com/a-guide-to-genetic-learning-algorithms-for-optimization-e1067cdc77e7?source=collection_archive---------21-----------------------

利用强化学习和遗传算法重建图像

从更广泛的数学或计算角度来看,优化问题被定义为从所有可行解中寻找最佳解的问题。在机器学习和人工智能方面,执行这些任务的两个重要算法是强化学习和遗传算法。它们的目的是为给定的问题陈述从一系列可能的解决方案中找到“最佳”的解决方案。在下面的文章中,我们将密切关注这些算法,并将看到它们在图像处理问题上的实际应用。

重复的基因突变和递归形成了遗传算法的基础|图片由数码艺术家在 Pixabay 上制作

什么是遗传算法?

遗传算法是随机的、自适应的启发式搜索算法,作用于可行解的群体。他们需要宽松支持的群体生物学和选择机制。

遗传算法是基于自然选择和遗传学的思想。新的解决方案通常是通过“变异”这个群体的成员,并通过“匹配”2 个解决方案来创建替代解决方案。

上层的解决方案被选择来繁殖和改变,因此更严重的被丢弃。它们是概率搜索方法;这意味着他们探索的状态并不完全由问题的性质决定。随机方法有助于引导搜索。遗传算法被用在人工智能中,就像不同的搜索算法被用在人工智能中一样——寻找潜在的解决方案来找出解决问题的方案。

什么是强化学习?

强化学习算法的运行机制|作者图片

强化学习通常被认为是一种目标驱动和高度自适应的机器学习技术。它由两个基本要素组成:状态和行动。

算法的策略是在某个状态下执行一个动作。学习者必须不断探索以产生最佳策略。

强化学习在算法实现上不同于监督学习和非监督学习,因为它将学习视为主体与其环境之间通过探索和评估进行交互的过程。上图简单展示了操作是如何执行的。代理首先感知当前的环境状态,并基于此选择要应用的动作。一旦环境接受了这个更新的动作,状态就会改变,代理就会得到奖励。根据环境的新的和发展的状态,代理继续选择更新的动作,并且这被递归地重复,直到它达到终止状态。

强化学习与遗传算法的融合

遗传算法是一种通用的启发式搜索方法,旨在找到问题的最优解。为了在遗传算法中收敛和使用强化逻辑,在遗传算法的适应度函数中增加了一个控制结构,它可以动态地调整种群的多样性。强化学习的目标是通过调整策略来最大化累积的回报,而遗传算法的目标是执行变异,直到期望的函数适应度最大化。

将强化学习机制引入遗传算法的交叉和变异操作,以确定将导致最佳可能优化的交叉片段和变异点。

作者将强化学习(RL)层添加到遗传算法(GA)图像中

遗传算法是如何工作的?

GA(s)通过对给定问题的可能解决方案进行汇集来发挥作用。所有的可能性然后经历一系列的重组和突变(类似于我们在自然遗传学中看到的)。然后,它们生育后代,这个过程会重复好几代。一代人中的每个孩子都被赋予一个适应值。该适应值基于 GA 试图解决的目标,并且产生更高适应值的代被给予机会来交配并产生甚至更适合的个体。这一直持续到达到停止标准(退出功能)。GAs 是达尔文适者生存理论的一个伟大实现。

遗传算法结构被极大地随机化,并且这种实现的性质为优化图像处理算法提供了极大的激励。与所有其他搜索算法相比,GAs 在局部搜索中的表现要好得多(考虑到它们处理历史数据的属性),并且由于图像是复杂的基于矢量的环境的一部分,这种优化的搜索方式允许更好的处理,从而更快地执行计算机视觉任务。以下是遗传算法工作的步骤和阶段。这些步骤通常是连续的,根据算法的准确性,有些步骤可能是重复的。

实现遗传算法来重建图像

  • **第一步:**读取输入,第一步是随机生成一个可能的解,不考虑其准确性。
  • 步骤 2: 初始解被赋予一个适应值。该适合度值保持为所有下一代解决方案的可比值。
  • **第三步:**对选择的解进行变异和交叉操作,然后进行重组。从这个解的组合中,提取最佳适应值。
  • 步骤 4: 这个新产生的最佳适应度解的后代被插入到最初给我们第一个可能解的群体中。这两种解决方案然后被交叉和变异。它们的变异输出被检查其适合度值。
  • 步骤 5: 如果这个新的解满足停止条件,则停止变异,并将其选为主解,否则步骤 4 继续进行更多的变异和交叉,直到满足基于停止条件的最佳拟合解。

常规图像处理工作流程|作者提供的图像

  1. 将图像转换成一维矢量

大多数图像处理算法的第一步是将图像文件格式转换为数字数组,因为机器对数字执行操作。我们对输入图像做同样的处理,并将其转换为矢量。

def image_to_vector(img_array):
     return numpy.reshape(a=img_array, newshape =              (functools.reduce (operator.mul, img_array.shape)))

2。将一维向量转换为数组

在转换的矢量上,我们标准化矢量的形状以创建定义的图像大小。

def vector_to_array(input_vector, shape):
   *#Checking whether reshaping is possible* if len(input_vector) != functools.reduce(operator.mul, shape):
      raise ValueError("Reshaping failed")
   return numpy.reshape(a=input_vector, newshape=shape)

3。定义适应度函数

简单定义的适应度函数是一个递归函数,它将群体的候选解作为输入,并产生一个输出,说明该解相对于所考虑的群体是如何“适合”的,或者换句话说,是如何“有效优化”的。

理想的适应度函数应该具有以下特征:

`计算起来应该相当快,因为在得到一个解之前,函数要进行多次(数千次)迭代。

它必须定量地测量解决方案的适合度或确定如何从给定的解决方案中产生适合的个体。

image_encode = image_to_vector(input_image)def fitness_func(int_one, int_two):
     fitness_value = numpy.sum(numpy.abs(image_encode-int_one))
     fitness_value = numpy.sum(image_encode) - fitness_value
     return fitness_value

4。定义回调函数

对群体的每一代及其突变执行回调函数。这个函数的参数是 GA 算法本身、到目前为止已处理的评估数以及当前的群体大小。

def callback_func(genetic_var):
    if genetic_var.generations_completed % 500 == 0:
    matplotlib.pyplot.imsave( 'Final_GA_Image_ ' + str(   genetic_var.generations_completed )+'.png', vector_to_array(genetic_var.best_solution()[0], input_image.shape))

5。用所有参数初始化遗传算法类

正如在 PyGAD 文档中所解释的,遗传算法初始化会接受多个用于其构建的参数。使用上面定义的适应性和回调函数。指定了需要迭代的群体数量(这些数量可以根据输入的复杂性而增加)。重要的是,还定义了世代数和父母交配数,这有助于算法构建后续的子群体。最后,从上面提到的文档中,有许多突变类型可供选择(在这个例子中,我们选择了“随机”)

# Initiate the Genetic Algorithm class with the given parameters
# Number of Parent Solutions to consider
genetic_var = pygad.GA(num_generations=40999, num_parents_mating=12,# Choosing which fitness function to use
fitness_func=fitness_func,# Lower scale entry point (Should be integer between 0-1)
init_range_low=0,# Higher scale exit point (Should be integer between 0-1)
init_range_high=1, # Number of populations to work through
sol_per_pop=44,# Number of genes to use
num_genes=input_image.size,        
mutation_by_replacement=True,# % of genes from the population that need to be mutated
mutation_percent_genes=0.02, # Type of mutation
mutation_type="random", callback_generation=callback_func,
random_mutation_min_val=0,# Mutation minimum and maximum values (range 0-1)
random_mutation_max_val=1)

6。运行遗传算法并找到最佳解决方案

run()函数启动遗传算法,最终 best_solution()属性为我们提供了重建图像的最佳输出。

*# Run the GA instance* genetic_var.run()*# Metrics of the best solution* int_one, result_fit, int_two = genetic_var.best_solution()

输出

这些是输入图像和重建图像(并排)。该输出是在初始群体的 40998 代之后产生的。所有生成和结果输出都在下面链接的存储库中。我们看到,经过多代处理后,图像逐渐接近原始输入图像。

有关使用 Python 实现遗传算法代码的详细信息,请参考下面提到的知识库。往复运动图像或其他对象所需的代数高度依赖于输入的性质及其复杂性。因此,创建一个能够非常快速地评估群体的适应度函数非常重要。

https://github.com/rjrahul24/ga-reinforcement-learning-image-restoration

我们今天看到的大多数机器人应用程序都是使用 GAs 构建的|照片由 Sebastian Kurpiel 在 Unsplash 上发布

为什么要使用遗传算法?

  • 气体在它们应用中是固有稳定的。
  • 这些算法能够在非常大的问题空间上提供优化。
  • 与传统的人工智能解决方案不同,GAs 不会因为给定输入的轻微变化或数据中存在噪声而中断。

结论

遗传算法通常用于生成优化和随机搜索问题的高质量解决方案。它们的应用依赖于生物启发算子,如突变、交叉和选择。今天,气体被应用于多种多样的行业,从制造创造性的机器人到卫星通讯调度,T2(由美国国家航空航天局实施),T3。这篇文章旨在提供一个增强的教程,说明如何使用气体来重建图像。这是机器人科学的主要应用之一,其中人工机器可以学习人类的视觉。如参考文献[7]中所述,可以遵循其他几个教程来学习 GAs 的深入应用。

关于我

我是纽约哥伦比亚大学的一名软件工程师兼数据科学研究员,目前正在研究如何减少气候变化对世界贫困人口的影响。这些人受我们砍伐树木和在地球表面推混凝土的行为影响最大。如果你的研究或工作抱负也与我一致,请务必通过 Twitter 或 LinkedIn 与我联系,我们可以一起努力建设 负责任的人工智能。

[参考文献]

  1. https://arxiv.org/pdf/1905.04100.pdf
  2. https://www . science direct . com/science/article/ABS/pii/s 0957417408006064
  3. https://www.hindawi.com/journals/mpe/2020/1698323/
  4. https://pygad.readthedocs.io/en/latest/
  5. https://www.staracle.com/general/evolutionaryAlgorithms.php
  6. https://en . Wikipedia . org/wiki/List _ of _ genetic _ algorithm _ applications
  7. https://en.wikipedia.org/w/index.php?title =遗传 _ 算法&action =编辑& section=32

(高度)分布式 DNN 训练指南

原文:https://towardsdatascience.com/a-guide-to-highly-distributed-dnn-training-9e4814fb8bd3?source=collection_archive---------16-----------------------

将培训扩展到多名员工时需要注意什么

劳拉·奥克尔在 Unsplash 上的照片

如今,数据分布式培训风靡一时。在数据分布式培训中,学习是在多名员工身上并行进行的。多个工人可以驻留在一个或多个训练机器上。每个工人从其自己的完全模型的相同副本开始,并在训练数据的不同子集(本地批次)上执行每个训练步骤。在每个训练步骤之后,它发布其结果梯度,并考虑所有工人所学习的组合知识来更新其自己的模型。用 k 表示工人数量,用 b 表示局部批量,对 k 工人进行分布式训练的结果是,在每一个训练步骤中,模型在 kb* 样本的全局批量上进行训练。很容易看出数据分布式培训的吸引力。每个训练步骤更多的样本意味着更快的训练,更快的训练意味着更快的收敛,更快的收敛意味着更快的部署。如果我们能在一个中训练 ImageNet,为什么要训练 29 个小时?如果我们能在 76 分钟内训练出伯特,为什么还要训练 3 天呢?对于特别大或复杂的网络,分布式训练对于模型在足够低的时间周期内进行训练以使模型可用来说几乎是必不可少的。

问题是,许多关于该主题的教程可能会给你这样的印象,即转换训练脚本以执行数据分布式训练几乎就像轻按开关一样容易,并且训练收敛的线性缩放几乎是有保证的。然而,对于许多模型,当然是复杂的模型,这与事实相去甚远。要在分布式培训中取得成功,需要解决许多细节问题,包括如何调整优化器设置,如何最小化共享梯度的开销,以及如何处理培训数据。这篇博文的目的是让您了解数据分布式训练的“真实情况”。我们将讨论一些实现细节和挑战,并强调设定明确的性能目标和使用适当的工具来衡量这些目标的重要性。

虽然我的重点是 TensorFlow 2 Keras 模型(特别是 TensorFlow 2.3),但我们将涵盖的要点也与其他培训框架相关。

亚马逊网络服务(AWS)、谷歌云平台(GCP)或微软 Azure 等云服务环境是多员工培训的自然环境。最明显的原因是提供了几乎无限的规模。在云中,我可以根据我的开发进度承诺和成本限制,扩展到任意数量的员工。在云中培训的另一个好处是,它为您节省了为多员工培训设置和维护您自己的机器的时间和麻烦。此外,云环境通常提供特定的服务来促进多员工培训。我将引用一些 AWS 提供的服务,但是同样,一般的评论也适用于其他环境,并且在其他云环境中也可能存在等效或类似的支持。

重要声明:我已经在这篇文章的草稿上坐了很久了。我的感觉是,这个帖子从来都不完整。总是有更多的框架、其他数据梯度共享方法和其他优化器需要考虑。过了一会儿,我意识到,随着所有新论文、算法和技术的不断引入,这篇文章永远不会完整。所以在这里,为了它的价值…我的不完整的博客帖子。如果你认为还有什么需要补充的,请不要犹豫,联系我。

注意:在本文的上下文中,工作者指的是一个单独的处理核心(例如 GPU)。在其他上下文中,术语 worker 可能指可能包含多个内核的训练实例。例如,在 TensorFlow 文档中,工作者指的是由一个或多个计算设备组成的训练实例。

我要感谢我尊敬的同事 Dennis Kreinovich 和 Amit Weizner ,他们对分布式训练算法解决方案的研究为这篇文章做出了贡献。

序幕

运行数据分布式培训时有两个主要挑战:

模型收敛: 模型收敛是指模型在预定义测试集上的评估达到期望结果时的训练点。假设当在单个工人上运行时,我们的模型在对 n 个样本进行训练之后,或者在训练数据的 N 次遍历之后收敛。当扩展到相同类型的多个工作者时,我们的目标是在训练数据的相同样本/遍历总数之后收敛。例如,如果我们扩展到 k 个工人,我们的目标是在每个工人看到 n/k 个样本或遍历 N/k 次训练数据时收敛。正如我们将在下面看到的,达到这个目标通常需要调整训练算法的一些元素。

训练时间表现 : 训练时间表现是指我们能够训练的速度。我们将通过模型每秒训练的样本数来衡量这一点。如果我们能够在单个工人上以每秒 R 个样本的速度进行训练,我们的目标将是在相同类型的 k 个工人上以每秒 Rk* 个样本的速度进行训练。先验,考虑到数据分布培训包括对单个工人进行培训时不存在的额外步骤的开销,即梯度共享,如何实现这一目标并不明显。我们将在下面讨论最小化这种开销的不同技术,以及其他可能影响训练速度的实现细节

如果我们能够实现这两个目标,在相同的样本总数和 1/ k 的时间内收敛,那么我们已经成功地将我们的培训作为工人数量的函数进行线性扩展。线性比例意味着如果对单个工人的培训需要 T 时间,那么对 k 工人的培训将需要 T/k 。在许多情况下,您可能会发现,您能够成功地培训多名员工,但无法实现线性扩展。这可能已经足够好了。如果你能在明显少于 T 的时间内训练,那么运行 k 工人的成本可能值得加速开发。在这种情况下,您需要明确决定什么是可接受的权衡。很明显,如果对 k 工人的培训花费 T 时间,也就是说,只要一个工人,那么你最好坚持一个工人。但是如果你能够用 4 个工人将开发时间减少一半呢?在某些情况下,这可能是一个可以接受的,尽管不太理想的权衡。

注意,虽然测量不同实现细节对训练速率(每秒样本数)的影响只需要几次训练迭代,但是测量对收敛时间的影响可能需要更多的时间。

在整个讨论过程中,我们将牢记这两个基本挑战,并研究它们如何受到不同实现选择的影响。

第 1 章—性能分析

绩效分析是指对培训运行速度(每秒样本数)和培训资源利用方式的测量。

在以前的帖子中(例如这里和这里),我提倡使用强大的工具和方法来分析性能,并将性能分析集成到您的 DNN 开发过程中。当扩展到多个工作人员时,性能分析的重要性甚至更大。事实上,在我看来,这是一个先决条件。如上所述,分布式培训的主要挑战之一是保持可接受的培训时间性能。显然,为了做到这一点,我们需要适当的工具,以便全面了解:我们单个员工的绩效,当我们扩展到多个员工时,绩效如何变化,以及这如何受到不同实施决策的影响。

在考虑扩展到多个员工之前,您应该首先确保您已经用尽了所有潜力来从单个员工那里获得最大的绩效。这不仅是一种提高培训速度的更廉价的方式,而且当你最终扩展到多个员工时,它还会放大潜在的绩效收益。例如,如果您的单个工作人员利用率为 25%,那么在考虑数据分布之前,您最好先确定并解决性能瓶颈。您可以将性能提升四倍,而无需额外的工人成本和梯度共享的额外开销。

衡量绩效有许多不同的工具和技术。 TensorFlow Profiler 提供了许多性能统计数据,用于评估 TensorFlow 中的模型训练,包括步进时间、轨迹查看、内存利用率、设备到设备的数据传输(梯度共享)等。使用 Amazon SageMaker 服务时,您可以利用 Amazon SageMaker 调试器的分析功能来测量系统资源利用率,并找到资源利用率模式和训练阶段之间的相关性。

请务必查看我以前发表的一些关于性能分析的文章,了解这个重要主题的更多细节。

https://aws.amazon.com/blogs/machine-learning/identifying-training-bottlenecks-and-system-resource-under-utilization-with-amazon-sagemaker-debugger/

第 2 章—分布式培训框架

当扩展到多员工培训时,你需要做的第一个决定就是使用什么样的框架。在本帖中,我们将重点关注两个可用选项:

  1. Horovod —一个流行的库,支持 TensorFlow、Keras、PyTorch 和 Apache MXNet,以及
  2. TensorFlow 内置的分布式培训支持。

这两个选项的共同点是,它们都使您能够通过几行代码将您的培训脚本转换为在多个工作人员上运行。两者的文档都很全面且易于使用,并且有大量的教程。梯度共享算法也非常相似(下面将详细介绍)。TensorFlow 分布式培训选项集成到 TensorFlow 中,因此对其内部工作方式有更深入的了解。在每个实例上调用训练脚本的单个实例,TensorFlow 处理多个工人上的模型复制、工人之间的输入分布以及梯度共享。另一方面,Horovod 更像是一个黑盒解决方案,因为它只有几个与训练周期交互的点,主要是梯度累积。在 Horovod 选项中,为每个工人运行培训脚本。脚本的每次调用都会在其对应的 worker 上创建一个模型副本,并独立管理输入数据流。

很难指出两种选择中的任何一种比另一种更好。在许多情况下,框架的性能依赖于模型架构、模型超参数和分布式训练实现的其他细节。我强烈建议你熟悉这两者。首先,有选择总是好的。更重要的是,您可能会发现您的一些模型在一个框架中比在另一个框架中伸缩得更好。此外,您可能会遇到其中一个选项不可用的情况。例如,直到 TensorFlow 版本,内置的分布式训练代码中存在一个错误,该错误禁止用户在 renorm 标志设置为 False 的情况下使用批处理规范化层。另一方面,如果您选择在 TPU 核心上运行您的培训,您别无选择,只能使用内置的 TensorFlow TPU 分发策略。最后,您可能会发现一些理想的特性只在其中一个框架上实现。例如,在最近的一篇帖子中,我对 Elastic Horovod 进行了扩展,这是 Horovod 的一个引人注目的功能,即使在一些工人被打断的情况下,也能实现连续培训。

另外一个值得一提的框架是亚马逊 SageMaker 的分布式数据并行库。正如我们上面提到的,并将在下一节详细讨论的,分布式训练的主要挑战之一是如何减少梯度共享的开销。SageMaker 库引入了一种新的算法,该算法利用特定的亚马逊云基础设施内部来优化梯度数据通信。更多细节请看最近发布的这个功能的。

2022 年 8 月 30 日更新——查看这篇博客文章,了解关于额外分布式培训框架的精彩调查。

https://neptune.ai/blog/distributed-training-frameworks-and-tools

第 3 章—梯度共享策略

成功的数据分布式训练运行的核心要素是梯度共享策略。一个强有力的战略需要。确保所有工人的培训同步进行。以最小化开销的方式进行。如果你的梯度共享算法不能成功地保持工人模型之间的同步,那么你还不如对每个工人运行许多独立的(浪费的)训练会话。如果你的算法成功地保持了同步,但是以一种三倍于步骤时间的方式,那么使用多个工人的价值将会很低。

我们将把讨论分成两部分。首先,我们将讨论梯度共享算法的一些主要不同之处。然后,假设有一个固定的梯度共享算法,我们将介绍一些减少数据流量开销的技术。

梯度共享算法

梯度共享算法可以根据以下两个参数大致分类:

参数服务器与对等通信:在基于参数服务器的算法中,一个或多个专用参数服务器从所有工人收集梯度更新,然后将结果广播回工人。在使用多参数服务器的解决方案中,变量分布在服务器之间。根据实现的不同,参数服务器可能位于 CPU 上,也可能位于 GPU 上。它们可以驻留在专用服务器或现有的培训资源上。例如,参见 TensorFlow 基于参数服务器的梯度共享解决方案。

在基于对等的解决方案中,每个工作者交流其结果梯度,并从其他工作者收集梯度更新。有多种 AllReduce 算法用于收集和累积梯度。一个简单的 AllReduce 算法的例子是,每个工人把它的梯度发送给其他工人。这导致总数据有效载荷为 k(k-1)G ,其中 k 是工人的数量,而 G 是所有梯度的大小。然而,还有许多更好的算法。最常见的是环-所有减少,其中多个消息在单向环中的工人之间传递。使用这种技术,总的数据有效载荷可以减少到分布在 2(k-1)* 通信跳*上的 2(k-1)G 。*看看这个帖子,详细描述了 Ring-AllReduce 的工作原理:

通常, AllReduce 算法将被设计为最大化底层硬件的利用率。例如,参见 TensorFlow 的HierarchicalCopyAllReduce。

“参数服务器”或“对等通信”这两种选项的具体实现决定了数据流的分布图,即通信通道和每个通道上传输的数据量。

同步与异步算法:在同步算法中,所有工人的培训步骤同步进行。在每一步之后,每个工作者等待所有其他工作者共享他们的梯度,并在将结果参数更新应用到其自己的模型副本之后继续下一步。同步算法确保模型副本在训练的所有阶段都是对齐的。在异步算法中(通常与基于参数服务器的策略相关联),工作人员不会等待。他们收集任何可用的更新,立即应用它们,并继续下一步。因此,每个复制品上的模型状态在每一步都可能不同。自然,异步方法将具有更低的延迟。另一方面,由模型复制品中的差异导致的附加噪声可能对收敛速度有负面影响。

显然,算法的最佳选择在很大程度上取决于具体的模型和训练环境。今天提供的最常见的算法,也是一个很好的起点,是同步环-所有减少算法或它的一些变体。这是 Horovod 使用的底层算法,也是 TensorFlow 的镜像策略使用的默认算法。查看 TensorFlow 的分布式培训指南,了解支持的其他分布式策略的概述。

优化数据传输

显然,梯度共享算法的选择将影响数据通信量,从而影响训练时间的总开销。在本节中,我们假设一个固定的算法,并讨论减少数据流开销的不同技术。

  1. 利用专用硬件加快数据传输速率:例如,如果您的员工通过高速互连进行连接,他们应该是首选的通信渠道。在大多数情况下,您应该更喜欢将您的培训分布在具有 8 个互连工作人员的单个实例上,而不是通过网络进行通信的 8 个单个工作人员实例上。另一个例子是上面提到的亚马逊 SageMaker 的分布式数据并行库,它实现了一个基于参数服务器的解决方案,能够通过利用底层 AWS 云基础设施的特定组件来提供有竞争力的结果。详见白皮书。
    最后一个例子,像谷歌 TPU 和 Habana Gaudi 这样的专用培训加速器之所以能够展示出令人印象深刻的规模速率,其中一个主要原因是它们经过了专门设计,旨在最大化单个员工之间的数据流速率。
  2. 调整梯度共享控制:一些梯度共享算法可能包括微调其性能的控制。例如,某些算法的性能可能会受到分配给它们的系统资源量的影响。
  3. 降低渐变精度:你可以通过降低渐变精度(比如用 float16 代替 float32)或者使用其他一些压缩方法来降低渐变共享的有效负载。确保你的训练不会受到这种操纵。
  4. 与反向传播重叠梯度共享:一个普通算法将等到所有梯度计算完毕后再传播结果。然而,一旦计算出各个梯度,人们就可以传达它们。通过将梯度更新消息分成包含梯度子集的多个分组,这些子集根据它们被计算的时间来排序,我们可以通过将其与反向传播重叠来减少梯度共享的开销。

要了解如何在 TensorFlow 中实现这些技术,我推荐以下来自去年 TFDevSummit 的视频。

注意:我们在这一部分的重点是引入训练步骤时间的潜在开销。但是,您应该注意渐变共享可能带来的其他副作用。例如,训练实例的网络 IO 带宽有上限。然而,在具有单个实例的训练环境中,该带宽几乎专门用于数据输入,而在通过网络执行梯度共享的多实例环境中,该网络带宽现在在数据输入和梯度消息之间共享。如果您的单个实例存在网络 IO 瓶颈,这将对您的线性扩展能力产生负面影响。同样,如果您遇到了 CPU 瓶颈,并且选择了需要 CPU 周期的梯度共享算法,那么您也将面临越来越多的线性扩展挑战。

第 4 章—培训数据输入

在数据分布式培训场景中,管理输入数据有两种主要策略。首先,我们通过分割(拆分)输入来确保每个工作者处理数据的不相交子集。在第二种情况下,我们简单地将完整数据集的随机混洗副本提供给所有工人。虽然这听起来很简单,但有许多实施细节会对你成功训练的能力产生有意义的影响。

数据集分片

有多种方法可以对数据进行分片。您可以在数据集创建过程中对数据进行分片。如果数据被分组到文件中,则可以在创建 Dataset 对象的过程中共享文件列表。最糟糕的做法是处理所有的输入数据,并在输入时将其分发给工作人员,因为这样会带来浪费的开销,可能会在 CPU 和/或网络输入上带来性能瓶颈,并可能会大大降低您的培训速度。不幸的是,这有时是 TensorFlow 的数据集分布的默认行为。正如这里的所记录的,TesnorFlow 将尝试按文件分片,但如果不成功,它将在数据集记录级别分片。注意,在 TensorFlow 文档的上下文中,一个 worker 指的是一个训练实例。

**示例—tensor flow with Pipe Mode dataset:正如我在过去所详述的,在亚马逊的 SageMaker 环境中对大型数据集进行训练的一种令人信服的方式是使用亚马逊 SageMaker 管道模式。不幸的是,TensorFlow 在 PipeModeDataset 上的默认行为将是记录级别的分片。回想一下,在每个培训实例中,Tensorflow 将管理所有工人的数据输入。假设我们运行 4 个实例,每个实例有 8 个 GPU 工作线程,总共有 32 个工作线程。每个训练实例最终将提取和处理 4 倍于其实际需要的数据量。考虑到网络输入带宽和 CPU 周期的限制,这可能会引入瓶颈并降低速度。一种解决方案是配置管道输入创建,将分发设置设置为‘shardedbys 3 key’并使用distribute _ datasets _ from _ function函数定制数据集分片。这样,我们可以确保每个实例只处理将要使用的记录。

整个数据集的随机洗牌

分片的一种替代方法是简单地向每个工人输入完整数据集的随机洗牌。请注意,产生的数据遍历可能与分片的情况不同。特别地,随机混洗可能导致某些记录在训练开始时被处理多次,而在接近训练结束时根本不被处理,而不是在训练过程中被均匀地分散。在大多数情况下,这种选择不会影响收敛速度,但可能会有对此敏感的模型。

**示例—Horovod with pipe mode dataset:与 TensorFlow 相反,在 horo VOD 中,每个工人的输入都是独立处理的。特别是,在 k 工人的情况下,分片选项将需要定义 k 唯一数据集。然而,如果你使用亚马逊 SageMaker 管道模式,你会发现你被限制在每个培训工作 20 个管道。即使你只需要每个工人一根管子,你也只能有 20 个工人。如果你需要每个工人更多的管道,你将有一个较低的限制。一种可能的解决方案是将分片和洗牌结合起来。假设每个工作线程需要一个管道,您有 4 个实例,每个实例有 8 个 GPU 工作线程。创建 8 个管道,索引从 1 到 8,全部指向同一个数据源,每个管道的分布设置都设置为‘shardedbys 3 key’,并且每个管道都有一个唯一的 ShuffleConfig 实例,即唯一的 shuffle 种子。在每个实例中,将具有本地 id (local_rank) i 的 worker 连接到具有相同 id 的管道。结果是 8 个管道中的每一个将包含唯一的混洗,并且对于每个时期,本地 id 为 i 的所有工作者将遍历与管道 i 相关联的完整数据集。使用这种解决方案,您可以扩展到无限数量的训练实例,而不需要额外的管道。您仍将被限制为每个工人最多 2 根管道(例如训练测试),因为 3 将需要 3*8=24 根总管道,这大于 20 根管道的限制,但这一限制更易于管理。

第 5 章—训练算法更新

正如我们在第一节中所述,成功的分布式训练的两个主要挑战之一是最小化达到模型收敛所需的总样本数(或训练数据集的总遍历数)的增加。当使用 k 名员工在分布式环境下进行培训时,全球批量规模会增加 k 倍。在许多情况下,您会发现用于学习原始批量的模型超参数不再适用于大批量。如果批量大得多,即我们有大量工人的情况下,尤其如此。许多教程可能会让您相信,所需要的只是对所选模型优化器的学习速率进行(线性)校正。可悲的是,对于非平凡的模型,情况往往并非如此。您可能会发现,在 k 工人的情况下,找到合适的收敛超参数可能会很困难。你甚至会得出这样的结论:这是不可能的。更糟糕的是,你可能成功地找到了适用于 k 名员工的方法,却发现同样的方法不适用于不同数量的员工。更糟糕的是,如果你不能在一个特定的全球批量上达成一致,可能不清楚这是因为你还没有找到正确的配方,还是因为你已经达到了批量增加的极限。参见此处了解为什么会有这样的限制(感谢 Denis Akhiyarov 作为参考)。

您可能会遇到的另一个现象是执行大批量训练时的 泛化差距 问题。随着全局批量的增加,您可能会发现在训练集和验证集上测量的指标度量之间的差距越来越大。正如这篇论文中所描述的,这是因为大批量训练比小批量训练更倾向于收敛于不同的最小值。感谢 Denis Akhiyarov 让我注意到这一现象。

解决大批量情况下的模型收敛问题是一个活跃的研究领域,为此任务提出了专门的训练策略,如兰姆、拉尔斯、阿达姆等等。请记住,更改优化器或其他某个超参数可能会增加训练步骤的时间,使达到线性伸缩的目标变得更加困难。例如, TensorFlow 插件库包含了一个 LAMB 优化器的实现。然而,您可能会发现,与您的默认优化器相比,它增加了步骤时间,在这种情况下,您可能想要考虑创建一个定制的内核来确保它的最优性。

底线是,虽然实现(高度)数据并行化训练的机制可能很容易,但确保模型收敛以及时的方式有时可能相当困难。

第 6 章—提高成本效率的技巧

培训资源非常昂贵,而且成本通常与员工数量成线性比例关系。虽然高成本是分布式培训的一个不容置疑的症状,但我们应该始终意识到这一点,并寻找机会调整我们的开发习惯以降低成本。以下是一些建议:

使用低成本“现货”实例:许多云服务提供商为多余的计算实例提供大幅折扣。在 AWS 中这些被称为 亚马逊 EC2 Spot 实例 ,在 Google Cloud 中它们被称为 可抢占 VM 实例 ,在微软 Azure 中它们被称为 低优先级 VM。权衡的结果是,如果对它们的需求增加,这样的实例可能会在使用过程中被终止,并且如果其中一个工作人员变得不响应,大多数现代框架将无法通过分布式培训。但是,假设您的培训会话不在关键路径中,并且您定期捕获检查点,在发生中断时可以从这些检查点恢复,那么使用 spot 实例可能对您来说是一个有吸引力的选择。您还可以考虑使用容错解决方案,如 Elastic Horovod ,这是 Horovod 的一项引人注目的功能,即使在现场终止的情况下也可以不间断地进行培训,同时允许根据不断变化的工人数量动态更新培训超参数。看看我最近关于这个话题的文章。

培训课程的自动化监控和管理:您必须引入自动化方法来监控培训课程,识别失败的运行,并终止它们。如果你只依靠手动发现,你可能会发现自己为无意义的训练周期付出了过多的代价。

使用高级超参数调整方法:高级(通常基于贝叶斯)超参数调整相对于简单方法(如手动搜索或网格搜索)有众所周知的优势。如上所述,考虑到高成本和增加识别适当超参数的复杂性的可能性,这种技术在分布式设置中更加重要。

将评估卸载到单个工作实例:使用多个实例将需要对您的培训流程进行一些修改。检查点管理应该只由其中一个工人(主要工人)来处理。如果您正在使用 Horovod,并且您的培训会话需要安装依赖包,那么您需要确保每个实例仅由一个工人执行安装(当其他工人睡觉时),以避免竞争情况。一个经常被忽视的因素是评估。训练流程通常会穿插定期评估,以便监控评估指标如何随时间变化。虽然您当然可以对多个工作人员执行分布式评估(有一些简单的方法可以在工作人员之间累积指标,并且可以调整批处理大小以最大化资源利用率),但是让我大胆地猜测,加速评估并不是您使用多个实例的原因。您可以考虑的一件事是将评估生成到其他单个工作实例上,而不是在多实例环境中运行它们。这不仅会节省你的 d 分布式评估的开销,而且你还会享受到不间断训练带来的额外训练速度提升。当然,如果您需要快速评估和/或您的培训流程取决于评估的结果,这可能不是一个好的选择。

明智地开展分布式培训:明智地选择何时开展培训可以节省大量成本。这可以通过例子得到最好的证明:假设你已经成功地扩展到一个 k 工人设置,这样你的培训时间已经减少了一个 2/k 的因子(与期望的因子 1/k 相反),并且你已经决定,在一般情况下,额外的成本值得加速开发时间。现在假设你在一个特定的环境中开始一个新的培训课程,在这个环境中,你不关心培训是及时完成 T 还是及时完成 2T/k ,例如,就在回家过周末之前。在这种情况下,您可能需要重新考虑运行分布式培训,以便将培训成本减半。

在最近的一篇文章中,我谈到了一些额外的提高培训效率的发展习惯。这样的习惯在分布式训练环境中变得更加重要。

</6-development-habits-for-increasing-your-cloud-ml-productivity-becdc41eb289>

摘要

运行数据分布式培训是加速您的培训、减少您的开发周期时间和增加您的市场竞争力的一种令人信服的方式。然而,成功的数据分布训练可能也需要大量的开发工作。在开始这次冒险之前,确保你有合适的工具来评估你的进展。为可接受的比例因子设定清晰的目标、清晰的成功标准和清晰的界限。尝试尽早了解调整模型的复杂性,以确保收敛于较高的全局批量。通过仅在回报合理的时间和地点实施数据分布式培训,明智地计算您的决策。

面板数据回归指南:理论与 Python 实现。

原文:https://towardsdatascience.com/a-guide-to-panel-data-regression-theoretics-and-implementation-with-python-4c84c5055cf8?source=collection_archive---------0-----------------------

作者图片

入门

面板数据回归是控制未观察到的独立变量对因变量的依赖性的一种强有力的方法,它会导致传统线性回归模型中的有偏估计量。在本文中,我想分享这个主题背后最重要的理论,以及如何一步一步地用 Python 构建面板数据回归模型。

我写这篇文章的意图是双重的:首先,在我看来,很难找到一个简单易懂的综合面板数据回归模型的解释。其次,在 Python 中执行面板数据回归不像在 R 中那样简单,这并不意味着它的效率更低。因此,我决定分享我在最近的一个项目中获得的知识,以便使未来的面板数据分析可能更容易一些;-)

说够了!让我们深入主题,描述什么是面板数据,为什么它如此强大!

什么是面板数据?

“面板数据是一个二维的概念,其中相同的个体在不同的时间段被重复观察。”

一般来说,面板数据可以被视为横截面和时间序列数据的组合。横截面数据被描述为在特定时间点对多个对象和相应变量的一次观察(即一次观察)。时间序列数据只在一段时间内重复观察一个对象。面板数据通过随时间从多个相同的对象收集数据,将两者的特征包含到一个模型中。

简而言之,我们可以把它想象成一条时间线,在这条时间线上,我们周期性地观察同一个个体****。

面板数据设计图解

让我们进一步分解上面的定义,并在一个样本面板数据集上逐步解释它:

样本面板数据集

*****“面板数据是一个二维概念[…]”***:面板数据一般以行列二维方式存储(我们有一个 9 行 4 列的数据集)。值得注意的是,我们总是需要一栏来标识被观察的个人(栏 person )和一栏来记录数据收集的时间点(栏 year )。这两列应该被视为多索引。

*****“[……]其中相同的个体[…]”*:我们有个体人 A、人 B 和人 C,,我们从这些个体中收集变量 xy 。个体和观察到的变量将永远保持不变。

注意:这种预先性也是与另一个经常混淆的数据概念的主要区别,即汇集横截面**。虽然两者都可以被看作是一段时间内汇总的横截面数据,但主要的区别是面板数据总是观察相同的个体,而这在混合横截面中无法得到证明。**

汇集横截面示例:

汇总的横截面数据

”[……]在不同的时间段反复观察。:我们从 2018、2019、2020 收集数据。

到目前为止一切顺利…现在我们明白什么是面板数据了。但是这个数据概念背后的含义是什么,我们为什么要使用它???

***答案是…*异质性和由此产生的内生性!也许你已经听说过传统线性回归模型中的这个问题,在这种模型中,异质性经常导致有偏差的结果。面板数据能够解决这个问题。

由于异质性和内生性对于理解我们为什么使用面板数据模型至关重要,我将在下一节中尝试简单地解释这个问题。

未观察到的异质性导致的内生性问题

“其他自变量的未观察到的相关性称为未观察到的异质性*,自变量与误差项(即未观察到的自变量)之间的相关性称为内生性。”***

比方说,我们想分析饮用咖啡如何影响注意力水平的关系。简单的线性回归模型如下所示:

简单线性回归

其中:

  • Concentration_Level 是因变量(DV)
  • β0 是截距
  • β1 为回归系数
  • 咖啡 _ 消费是自变量(四)
  • ɛ是错误的术语

然而,该模型的目标是探索咖啡 _ 消耗量 (IV)与浓度 _ 水平* (DV)之间的关系。假设 IV 和 DV 正相关,这意味着如果 IV 增加,DV 也会增加。让我们把这个事实加到我们的公式中:***

静脉注射与家庭暴力的关系

但是,如果有另一个变量会影响现有的 IV,并且没有包含在模型中,该怎么办呢?比如疲倦有很大几率影响咖啡 _ 消费(如果你很累,你明显会喝咖啡;-) ).如果你还记得本文的第一句话,这种变量被称为不可观测的独立变量。它们“隐藏”在误差项之后,例如,如果 Coffee_Consumption 与这样的变量正相关,误差项将随着 Coffee_Consumption 的增加而增加:**

IV 与误差项的相关性

这又会导致 DV 浓度水平的估计值过度增加。因此,估计的 DV 是有偏差的,并且将导致不准确的推断。在我们的例子中,偏差将是浓度水平的红色过度增加。**

异质性导致的有偏估计量

幸运的是,有一种方法可以处理这个问题……也许你已经猜到了,面板数据回归!面板数据的优势在于,我们可以通过将异质性确认为固定随机来控制回归模型中的异质性*。但是在下一节中会有更多的介绍!***

面板数据回归的类型

以下解释基于这种符号:

注释

其中:

  • y = DV
  • X = IV(s)
  • β =系数
  • α =个体效应
  • μ =特殊误差

基本上,面板数据有三种回归类型:

1) 联营: 联营可以描述为对面板数据执行的简单 OLS(普通最小二乘)模型。它忽略了时间和个体特征,只关注个体之间的依赖。然而,简单 OLS 要求未观察到的独立变量和 IVs 之间没有相关性(即外生性)。让我们把它写下来:****

外来性假设

关于pool dols的问题是,即使上述假设成立, alpha 也可能随着时间的推移而具有序列相关性。因此,pool dols大多不适合面板数据。

α之间的序列相关性

注意:为了解决这个问题,有另一个回归模型叫做 FGLS (可行广义最小二乘法),它也用于下面描述的随机效应模型。

******2) 固定效应(FE)模型:FE 模型将未观察到的独立变量的个别效应确定为一段时间内的常数(“固定”)。在有限元模型中,未观察到的独立变量和 IVs(即内生性)之间可能存在关系:

允许内生性

有限元模型的诀窍在于,如果我们假设α为常数,并从每个方程项中减去平均值,α(即未观察到的异质性)将为零,因此可以忽略不计:

消除有限元模型中的个体效应

单独来说,特质误差(由 my = 未观察到的随时间和单位变化的因素表示)仍然存在,并且必须是外来的和非共线的。

然而,因为异质性是可以控制的,这个模型允许异质性存在于模型中。不幸的是,由于个体效应是固定的,依赖关系只能在个体内部观察到。

**注:FE 模型的另一种选择是 LSDV 模型(最小二乘虚拟变量),其中(固定)个体效应由虚拟变量表示。该模型将导致完全相同的结果,但有一个主要缺点,因为如果回归模型很大,它将需要更多的计算能力。

3) 随机效应(RE)模型: RE 模型将未观察到的独立变量的个体效应确定为一段时间内的随机变量。他们能够在 OLS 和 FE 之间“切换”,因此可以关注两者,依赖于和之间的个体内的*。重新建模背后的想法如下:*******

比方说,我们有与上面相同的符号:

注释

为了包括估计量之间以及估计量之内,我们首先需要定义何时使用哪种估计量。通常,如果 alphaIV(s) 之间的协方差为零(或非常小),则在它们之间没有相关性,并且 OLS 模型是优选的。如果协方差不为零,则应使用有限元模型消除以下关系:

何时使用哪种型号?

如上所述,使用 OLS 的问题在于α随时间的序列相关性。因此,重新模型根据误差项的序列相关性来确定采用哪个模型。为此,该模型使用了术语λ。简而言之, lambda 计算 alpha 的方差有多大。如果它是零,那么就不会有 alpha 的方差,这反过来意味着 PooledOLS 是首选。另一方面,如果α的方差趋向于变得非常大,那么λ趋向于变为 1,因此消除α并采用 FE 模型可能是有意义的。

决策过程

现在我们知道了常见的模型,我们如何决定采用哪种模型呢?让我们看看那个…

如何决定哪个型号合适?

**在 PooledOLS 和 FE/RE 之间选择:基本上,简单线性回归模型有五个必须满足的假设。其中两个可以帮助我们在 PooledOLS 和 FE/RE 之间进行选择。

这些假设是(1)线性,(2)外生性,(3a)同伦方差和(3b)非自相关,(4)独立变量不是随机的和(5)没有多重共线性。

如果假设 (2)(3) (或两者)被违反,那么 FE 或 re 可能更合适。

**在 FE 和 RE 之间选择:回答这个问题取决于你的假设,如果个体的、未被观察到的异质性是一个常数或随机效应。但是这个问题也可以通过豪斯曼测试来回答。

****豪斯曼检验:简单来说,豪斯曼检验就是内生性的检验。通过运行 Hausman 测试,零假设是 IV(s)和 alpha 之间的协方差为零。如果是这种情况,那么 RE 优于 FE。如果零假设不成立,我们必须使用有限元模型。

所以,我们现在理解了面板数据回归背后的理论。让我们进入有趣的部分,一步一步地用 Python 构建模型:

用 Python 实现面板数据模型

第一步: 导入数据集并转换成正确的格式。

我将使用“Guns.csv”数据集,它通常在 r 中提供。如该数据集的描述中所述:“Guns 是 1977 年至 1999 年美国 50 个州以及哥伦比亚特区(总共 51 个州)的平衡数据面板。”(注意:如果数据集中没有缺失值,面板数据集称为“平衡的”,否则称为“不平衡的”)。

为了简单起见,我将只使用数据集提供的以下列:

  • **状态:这一栏代表我们被观察的个体。
  • **年:年一栏记录了我们定期收集的数据(1977-1999 年之间)。
  • **收入:收入是我们的 IV,表示为人均个人收入。
  • **暴力:暴力是我们的家庭暴力,包括暴力犯罪率(事件/ 100,000 居民)。

我们的“研究”问题是: 收入如何影响犯罪率?

***# Import and preprocess data
import pandas as pddataset = pd.read_csv(‘Guns.csv’, usecols = [‘state’, ‘year’, ‘income’, ‘violent’],\
 index_col = [‘state’, ‘year’])years = dataset.index.get_level_values(‘year’).to_list()
dataset[‘year’] = pd.Categorical(years)***

第二步: 从池开始,检查所需的假设

我建议开始表演台球。因为它可以被看作是一个简单的 OLS 模型,它必须满足某些假设(那些在“如何决定哪个模型是合适的?”).如上所述,如果违反了条件 2 或 3(或两者都违反),那么有限元/重新建模可能更合适。由于条件 2 只能用豪斯曼测试进一步测试,我们现在将坚持证明条件 3。

执行池化:

***# Perform PooledOLS
from linearmodels import PooledOLS
import statsmodels.api as smexog = sm.tools.tools.add_constant(dataset['income'])
endog = dataset['violent']
mod = PooledOLS(endog, exog)
pooledOLS_res = mod.fit(cov_type='clustered', cluster_entity=True)# Store values for checking homoskedasticity graphically
fittedvals_pooled_OLS = pooledOLS_res.predict().fitted_values
residuals_pooled_OLS = pooledOLS_res.resids***

检查条件 3:

条件 3 分为 3a(同质性)和 3b(非自相关性)。这些假设可以通过许多不同的测试来检验。对于条件 3a,我将向您展示如何用图形识别异方差,以及如何执行怀特测试布鲁希-帕甘测试(两者相似)。对于条件 3b,我将向你们展示德宾-沃森测试*。***

***# 3A. Homoskedasticity
import matplotlib.pyplot as plt
 # 3A.1 Residuals-Plot for growing Variance Detection
fig, ax = plt.subplots()
ax.scatter(fittedvals_pooled_OLS, residuals_pooled_OLS, color = ‘blue’)
ax.axhline(0, color = 'r', ls = '--')
ax.set_xlabel(‘Predicted Values’, fontsize = 15)
ax.set_ylabel(‘Residuals’, fontsize = 15)
ax.set_title(‘Homoskedasticity Test’, fontsize = 30)
plt.show()***

异方差的残差图

基本上,残差图代表预测值(x 轴)对残差(y 轴)。如果绘制的数据点展开,这是方差增长的指标,因此也是异方差的指标。因为这似乎是我们例子中的情况,我们可能有第一个违反。但是让我们用白人-和布鲁许-异教徒-测试来检验一下:

***# 3A.2 White-Test
from statsmodels.stats.diagnostic import het_white, het_breuschpaganpooled_OLS_dataset = pd.concat([dataset, residuals_pooled_OLS], axis=1)
pooled_OLS_dataset = pooled_OLS_dataset.drop([‘year’], axis = 1).fillna(0)
exog = sm.tools.tools.add_constant(dataset['income']).fillna(0)white_test_results = het_white(pooled_OLS_dataset[‘residual’], exog)labels = [‘LM-Stat’, ‘LM p-val’, ‘F-Stat’, ‘F p-val’] 
print(dict(zip(labels, white_test_results)))# 3A.3 Breusch-Pagan-Test
breusch_pagan_test_results = het_breuschpagan(pooled_OLS_dataset[‘residual’], exog)
labels = [‘LM-Stat’, ‘LM p-val’, ‘F-Stat’, ‘F p-val’] 
print(dict(zip(labels, breusch_pagan_test_results)))***

简单来说,如果 p < 0.05, then heteroskedasticity is indicated. Both tests give very small p-values (White-test: 3.442621728589391e-44 ,breus ch-Pagan-test:6.03266972194746 e-26

****因此,我们已经证明了我们的第一次违规!让我们执行假设 3b:

***# 3.B Non-Autocorrelation
# Durbin-Watson-Test
from statsmodels.stats.stattools import durbin_watson

durbin_watson_test_results = durbin_watson(pooled_OLS_dataset[‘residual’]) 
print(durbin_watson_test_results)***

德宾-沃森测试将有一个介于 0-4 之间的输出。平均值(= 2)表示没有发现自相关,0–2 表示正自相关(越接近零,相关性越高),2–4 表示负自相关(越接近 4,相关性越高)。在我们的例子中,结果是 0.08937264851640213 ,这清楚地表明强正自相关。

因此,假设 3b 也被违反,因此,FE/RE 模型似乎更合适。

因此,让我们建立模型!

第三步: 执行 FE- and RE-model

***# FE und RE model
from linearmodels import PanelOLS
from linearmodels import RandomEffectsexog = sm.tools.tools.add_constant(dataset['income'])
endog = dataset[‘violent’]
# random effects model
model_re = RandomEffects(endog, exog) 
re_res = model_re.fit() 
# fixed effects model
model_fe = PanelOLS(endog, exog, entity_effects = True) 
fe_res = model_fe.fit() 
#print results
print(re_res)
print(fe_res)***

结果有限元模型:

有限元模型结果

结果重新建模:

重新建模结果

在本例中,两者的表现相似(尽管 FE 的表现似乎略好)。因此,为了测试哪个模型应该是首选的,我们将最终执行豪斯曼测试。

第四步: 执行豪斯曼测试

注意:因为我对 econtools 包中提供的 hausman 函数有问题(协方差不起作用),所以我稍微修改了一下函数。所以,如果你遵循这个原则,欢迎你使用这个函数。

***import numpy.linalg as la
from scipy import stats
import numpy as npdef hausman(fe, re):
 b = fe.params
 B = re.params
 v_b = fe.cov
 v_B = re.covdf = b[np.abs(b) < 1e8].sizechi2 = np.dot((b — B).T, la.inv(v_b — v_B).dot(b — B)) 

 pval = stats.chi2.sf(chi2, df)return chi2, df, pvalhausman_results = hausman(fe_res, re_res) 
print(‘chi-Squared: ‘ + str(hausman_results[0]))
print(‘degrees of freedom: ‘ + str(hausman_results[1]))
print(‘p-Value: ‘ + str(hausman_results[2]))***

由于 p 值很小( 0.008976136961544689 ),可以拒绝零假设。因此,有限元模型似乎是最合适的,因为我们的模型显然具有内生性。

为了对内生性建模,我们现在可以执行像 2SLS (2 阶段最小二乘法)这样的回归模型,其中工具变量有助于处理内生性,但这是另一篇文章的内容;-)

我真的希望你喜欢这篇文章,它可以帮助你克服面板数据回归的常见问题。当然,请不要太挑剔,因为这是我在这个平台上的第一篇帖子:-)

数据行业问题解决指南

原文:https://towardsdatascience.com/a-guide-to-problem-solving-in-data-industry-4f57a61b3091?source=collection_archive---------8-----------------------

行业笔记

…或者,由一名新入职的数据分析师实习生进行观察

约翰·施诺布里希在 Unsplash 上的照片

随着行业的不断发展,人们很容易被最新的数据科学工具分散注意力,忘记真正重要的东西——应用和实施。毕业后,我开始了数据分析师的实习,并很早就了解到在课堂上处理数据和在工作场所处理数据是有区别的。解决问题——可以说是我一天中大部分时间都在做的事情——并不是一边用手指敲打键盘一边向数据之神祈祷(即使是半满意)。这不仅仅是简历上的一个时髦词;此外,到目前为止,我在实习中还学到了一些有效解决问题的方法。所以,事不宜迟,这里有一个由数据分析师实习生提供的数据行业解决问题的简单指南。

1.奥卡姆剃刀——不要让事情过于复杂。

当数据科学 twitter 上到处都是宣扬使用 xyz 库的神奇之处或者 abc 模型的好处的人时,一个天真的学生(也就是我)很容易被信息的海洋淹没,认为一个涉及数据的问题需要大量复杂的代码。在我实习的前几周,我几乎是本能地通过导入机器学习库来完成我的第一个任务,甚至没有完全考虑这个任务。最终,我意识到解决问题并不是把一个 ML 模型扔给我得到的任何数据集,并希望它有效。

无论您的任务是什么,并不总是需要使用数据科学领域的最新进展,事实上,过于复杂的事情可能会花费您更多不必要的时间。有时,只需一个 Excel vlookup 命令就能最好地完成任务;其他时候,只有可视化是必要的。如果我到目前为止学到了什么,那就是简单是最好的(或者至少是最好的开始)。做任何事情都有时间和地点,在使用更花哨的工具而不是更基本的工具之前,考虑一下你的任务在哪里。

2.同事的专业化将拯救你

在我们小小的实习生团队中,有些人更倾向于 Excel,有些人更倾向于 Python,还有一些人在行业中有着宝贵的经验。大学时的小组工作总感觉像拔牙,但在现实世界中,无论你的工作多么独立,在一个数据团队上,小组工作是不可避免的。让一个人负责你不太了解的部分,在他们完成后接手,会节省你很多时间和精力。

照片由马塞尔·埃伯勒在 Unsplash 上拍摄

这个平台上的很多人都写 T 型员工,你在一个领域很精通,但在其他领域不太精通。这并不是说你应该对其他不是你强项的方面一无所知,而是说你应该专注于你的专长,并经常温习其他方面。只要意识到你的 T 型身材的局限性,了解你的队友的优势,你的解决方案就能得到极大的改善(在时间和质量上)。

相信我,在时间紧迫的情况下,当你完全陷入 Excel 灾难时,谷歌“如何:索引匹配函数”真的不是完成任务的方法。解决问题有时需要在别处寻找最有效的路线,知道去哪里(以及如何)寻找也很重要。

3.有效沟通(我的意思不是通过漂亮的数据可视化)

好的,数据可视化在 powerpoint 幻灯片上看起来很棒,可能正是利益相关者对你的要求;然而,我所指的实际上是有效地与你的同事交谈。即使在我每天盯着屏幕 8 小时的技术领域,磨练软技能以尽可能高效地表达你的观点也很重要。

你可以创建一个问题的解决方案,但它可能不是最好的,甚至不是正确问题的解决方案。与你的同事或利益相关者进行有效的沟通可以提高你产生公司/客户真正需要的结果的能力(而不是你认为需要的结果)。想想《超级马里奥兄弟》中的马里奥,他吃了红白蘑菇(不是毒品的委婉说法)长高了——交流可以成为改善你工作的蘑菇。沟通技巧可以通过实施基本的商业实践来提高,比如实际抄送给正确的人,而不是点击“全部回复”来回复邮件,或者如果你不是一个好的演讲者,考虑在表达你的想法之前在脑海中写下一份清单。

4.熟悉和“我不知道”的诅咒

作者,2021

你知道,当你认为你已经掌握了西班牙语的所有水平,实际上说话像个蹒跚学步的孩子,但当有人在现实生活中试图对你说火速西班牙语时,你拒绝说"我不知道"因为那真的很尴尬?是啊。

我们把“我不知道”框成了一件让人不以为然的事情,当这是一个证明自己的绝佳机会的时候。在谷歌时代(大喊 StackExchange!),你需要学习回答问题的一切都触手可及。我的“我不知道”可能是“我现在不太确定,我愿意学”。此外,这不仅是一个展示学习意愿的机会,也是一个扩展技能的机会。有些问题只是需要不同的东西,并且不顾一切地使用同样的老方法,这并不适合这个问题,只是因为它很熟悉,可能会比执行一些快速的谷歌搜索更延迟您的结果。

(没错,我就是那个不得不谷歌“如何制作数据透视表”的傻瓜,没错,从那以后我学会了很多 Excel。是的,为了提高效率,我开始更多地使用 Excel。)

在第二点,利用你的队友的专长,和第四点,成为一个“终身学习者”之间需要建立一个平衡。如果一个人能够在时间敏感的任务之间管理这种跷跷板,他们也许会成为更大的资产。

除了解决问题,我可能只是一个实习生,但是在你的职业生涯中保持静止不动,从长远来看,可能不仅对你有好处…

结论:四点可能不足以包含解决问题的所有内容

不要拿着燃烧的干草叉来找我,我知道我提到的一切甚至没有触及问题解决的表面。老实说,学生和真正的工作成年人之间的转换包含更多或更少的自由。我可能不会被指导我的项目的大纲所束缚,但现在我要对利益相关者、经理和同事负责。没有人会规定如何解决问题,但我很确定,在这四点上努力可以提高我自己的解决问题的能力,即使不能提高你的能力。

Python 环境、依赖和包管理指南:Conda +诗歌

原文:https://towardsdatascience.com/a-guide-to-python-environment-dependency-and-package-management-conda-poetry-f5a6c48d795?source=collection_archive---------23-----------------------

大蟒

如何将包自动添加到您的环境文件中,而不必担心依赖关系

*除非另有说明,所有使用的图片均为作者所有。

如果您在不同的开发阶段处理多个 Python 项目,那么您的系统上可能会有不同的环境。有各种工具可以创建一个隔离的环境,并安装您的项目所需的库。这篇文章讨论了 Python 打包、环境和依赖管理系统的不同可用技术。然后,我们将检查一个理想的设置(当然,在我看来🙂)适合大多数使用 conda 和poem的 Python 项目。

在本文中,库和包这两个词可以互换使用,它们都指 Python 包。

让我们首先列出不同的技术组,并强调几个工具

  1. 一个环境管理系统:Virtualenv、康达环境、 Pipenv
  2. 包依赖解析器: conda,Pipenv,poem
  3. 包库: PyPI,Anaconda 等。

关于包存储库的快速说明

最流行的 Python 包存储库是 Python 包索引(PyPI),这是许多 Python 库的公共存储库。您可以通过运行pip install package_name从 PyPI 安装软件包。Python 库也可以使用 conda 打包,conda 包的一个流行宿主是 Anaconda。您可以在 conda 环境中运行conda install package_name来安装 conda 包。

康达:百事通?

Pipenv 是为了解决 virtualenv 的许多缺点而创建的。但是,我不会将 virtualenv 和 Pipenv 视为环境管理者的主要原因是:

  • 我想有安装康达软件包的灵活性。
  • 与 conda 不同,virtualenv 和 Pipenv 都只是 Python 环境。

正如您在简介中可能注意到的,conda 管理环境、包和依赖项。不仅如此,它还是语言不可知的。此外,conda 还可以在活动的 conda 环境中使用 pip 来安装 PyPI 包。您可以通过运行以下命令来安装一个全新的 conda 环境

conda create -n env_name python=3.7

总是建议拥有一个包含您的库及其特定版本的环境文件。由于可移植性、可维护性和可再现性,这一点很重要。您可以使用以下命令从文件(例如,下面的 environment.yaml 文件)创建 conda 环境

conda env create -f environment.yaml

现在,你可能会说,太好了,conda 什么都做,所以,让我们在 conda 环境中使用 conda 包,让 conda 解决任何依赖问题。

conda 的问题

我认为康达做得太多了。在使用 conda 几年后,这里是我对 conda 作为一个包和依赖管理的一些观察:

性能问题

我对 conda 的主要问题是它的性能问题。创建一个新的环境,甚至更新一个旧的环境,有时可能需要很长时间,尤其是当您有很多包的时候。这可能是因为 conda 试图解决依赖性。很少有超过 30 分钟的时候(是的,30 分钟,不是 30 秒!)营造环境。我最初认为有一个连接问题或连接到包存储库的问题。

依赖关系解析程序问题

Conda 甚至可能无法解决依赖性问题。由于我们看不到特定 conda 包的依赖性(不像 poem),解决这些问题可能不容易。

Python 打包

conda 的另一个问题是当你想为你的库构建一个 conda 包并发布它的时候。这不是小事(至少对我来说),因为你需要几个配置文件(像 meta.yml,setup.py,)。).你可能也有依赖问题。你可以在这里找到更多关于如何构建 conda 包的信息。

诗意

诗词是 2018 年初步发布的 python 打包和依赖管理系统。它平滑地处理了依赖性,特别是如果您在一个新的环境中使用诗歌,然后添加您的 Python 包。它还可以以确定的方式处理项目的其他工具和配置,因为它使用 TOML 格式作为 Python 配置文件。简而言之,TOML 旨在使用易于阅读的最小配置文件。poem 使用pyproject.toml配置文件来安装 python 包并设置配置。

pyproject.toml: Python 配置文件

pyproject.toml file 是在 PEP518 中定义的一个新的 Python 配置文件,用于存储构建系统需求、依赖关系以及许多其他配置。在大多数情况下,您甚至可以替换setup.cfgsetup.py文件。您可以将大多数与特定 python 包相关的配置保存在一个pyproject.toml文件中,比如 pytest、coverage、bumpversion、Black code styling 等等。以前,你要么在单独的文件中编写这些配置,要么在其他配置文件中编写,比如setup.cfg。然而,pyproject.toml可以包含所有的需求,也可以包含所有的项目包需求。

提议的设置

我推荐使用 conda 作为环境管理器,pip 作为包安装器,poem 作为依赖管理器。在这种情况下,您可以获得 conda 环境中的所有 PyPI 包,在极少数情况下,您可以安装 conda 包。以下是使用诗歌和建议设置的一些好处:

  • 更好的依赖性管理(通常比 conda 依赖性解析器更快)
  • 具有大多数包配置(例如,pytest、coverage、bump2version、等)。)排成一列纵队。
  • 必要时可以安装 conda 软件包(这应该是你最后的选择!)
  • 诗可以自动添加新的包到pyproject.toml文件中。
  • 诗歌可以显示单个包的库依赖列表。
  • 构建 Python 包并发布到 PyPI 就像运行两个命令一样简单!
  • 生产和开发环境不需要单独的环境文件。

步骤 1:创建一个最小的 conda 环境

您可以通过运行conda env create -f environment.yaml从下面的 YAML 文件中创建一个 conda 环境。这将创建一个拥有 Python 3.8 的全新 conda 环境。在 conda 环境中,您可以传递一个通道列表(顺序很重要),您可以从这些通道中安装您的软件包。除了 Anaconda Cloud 上的默认通道(由 Anaconda Inc .管理)之外,还有其他可以安装包的通道。一个受欢迎的渠道是 conda-forge ,其中包括一个由社区主导的包集合。如果你有一个私人的康达频道,你可以写在频道部分。

第二步:安装诗歌工具

你可以按照他们的说明在这里安装诗歌。对于 OSx、Linux 或 WSL (Windows 子系统 Linux),推荐的方法是使用下面的命令安装 poem。

curl -sSL [https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py](https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py) | python -

注意:使用自定义安装程序的首选方法(下载 get-poem . py 脚本的第一种方法)安装诗歌将会安装与系统其余部分隔离的诗歌。

⚠️虽然不推荐,也有一个 pip 版本的诗歌,你可以安装(pip install poetry)。开发人员警告不要在文档中使用 pip 版本,因为它可能会导致与环境中的其他包发生冲突。但是,如果我们的环境基本上是空的(虽然在创建 conda 环境时有些基础包是像 pip 一样安装的),那么通过pip安装大概就可以了!

第三步:配置你的诗歌

要为一个新项目配置诗歌,诗歌使得创建一个包含所有您想要的设置的配置文件变得非常容易。您可以通过简单地运行poetry init来交互式地创建一个pyproject.toml文件。这将提示一些关于您想要安装的 Python 包的问题。您可以按 Enter 键使用默认选项进行处理。

通过运行诗歌初始化进行交互式配置

正如你在上面的截图中看到的,你可以只为开发依赖添加一些包。为你的项目初始化诗歌将会创建一个pyproject.toml文件,它包含了我们在设置过程中定义的所有配置。我们有一个针对所有依赖项的主要部分(在生产和开发环境中都使用),但我们也有一个部分包含主要用于开发目的的包,如 pytest、sphinx、。这是相对于其他依赖管理工具的另一个优势。您的生产和开发环境只需要一个配置文件。

步骤 4:安装依赖项

一旦在一个pyproject.toml文件中有了您的依赖项和其他配置,您就可以通过简单地运行

poetry install

这将创建一个poetry.lock文件。这个文件基本上包含了所有包的精确版本,这些包用那些特定的版本锁定了项目。您需要提交pyproject.toml文件和poetry.lock文件。我强烈建议您不要手动更新 poetry.lock 文件。让诗歌发挥它的魔力吧!!

诗歌技巧

添加新包

如果您想在您的环境中添加(或删除)一个包,我强烈建议您使用下面的命令

poetry add package_name

这将自动将包名和版本添加到您的pyproject.toml文件中,并相应地更新poetry.lockpoetry add负责所有的依赖项,并将包添加到[tool.poetry.dependencies]部分。

如果您想将一个包添加到您的开发环境中,您可以简单地传递一个如下的--dev选项

poetry add package_name --dev

你可以指定一个包的特定版本,甚至可以通过 git+https 或者 git+ssh 来添加一个包(更多细节见此处)。

移除包

您可以按如下方式删除软件包

poetry remove package_to_remove

显示包依赖关系

如果您想查看环境中所有已安装软件包的列表,可以运行以下命令

poetry show

请注意,这也将显示包的依赖关系。有时查看 Python 包的依赖关系会很有帮助。幸运的是,您可以使用poetry show来做到这一点。例如,我们可以使用下面的命令来查看我们环境中的requests包的依赖列表

poetry show requests

项目中请求包的所有依赖项

更好的是,您只需运行

poetry show --tree

所有项目依赖关系的树

从上图中,您可以看到蓝色字体的包名(requests 和 pytest)被显式地添加到了pyproject.toml文件中。黄色的其他库是它们的依赖项,不需要在 toml 文件中。

注意:您可以使用pip freeze ( pip freeze > requirements.txt如果您想将结果输出到一个文件中)来输出您的环境中所有已安装的包,但是那将会非常混乱。

结论

在这篇文章中,我们讨论了不同的 Python 环境、包管理和依赖解析工具。然后,我们回顾了如何使用 conda 作为环境管理器和 poem 作为包管理器和依赖解析器的设置,以及在 Python 项目中使用这种组合的好处。

希望这篇文章对你有用。

感谢阅读!

订阅我的邮件列表如果你喜欢这个帖子。我喜欢写关于数据科学、机器学习、统计学以及有趣的 Python 库和技巧的文章。也可以关注我上 领英 ,或者 推特

有用的链接

** *https://github.com/carlosperate/awesome-pyproject https://ahmed-nafies.medium.com/pip-pipenv-poetry-or-conda-7d2398adbac9

原载于https://www.ealizadeh.com*。**

Python 良好实践指南

原文:https://towardsdatascience.com/a-guide-to-python-good-practices-90598529da35?source=collection_archive---------3-----------------------

回顾 Python 中的一些最佳实践,从项目结构到代码格式化,再到一些优秀的旧代码管理技巧

安德烈·卡斯塔哈在 Unsplash 上拍摄的照片

这篇文章是从我正在写的关于数据科学的书“数据科学懒惰指南”中的章节中摘录的。阅读类似文章,参考我的博客或我的媒体页面。别忘了在 LinkedIn 和/或 Twitter 上问好。 🖖

介绍

编写现在有效的代码很容易。编写明天能工作的代码是困难的。编写明天就能工作的代码,并且足够直观,任何人都可以理解和遵循——现在我们已经碰到了超级困难的事情😀。通过观察和我一起工作的几位 ML 工程师和数据科学家,我注意到他们几乎都有自己独特的编码风格。嗯,不要误解我,主观上是一件好事,我认为这是导致创新的原因。也就是说,在团队中工作,甚至是在开源协作中,同意某些规则是有帮助的。这就是这篇文章背后的想法,为 python 实践者提供一套精选的指南,他们可以从中挑选。至此,让我们来看看一些好的实践,它们不仅能帮助我们创建一个工作的,而且是一段漂亮的代码😄。为了涵盖这个主题,我们将讨论三个部分,

  1. **项目结构化:**关于如何组织代码的想法
  2. 代码格式:关于如何让你的代码易于理解的想法
  3. 额外提示:一些对你有长远帮助的事情

项目结构

在这一部分中,我们将主要讨论如何构建完整的 python 项目的一些良好实践。为此,我们将着眼于两种不同的可能性,任何人都可以根据他们的项目的简单或复杂程度来选择。

类型 1:经典

  • 这是最基本的格式,但也暗示了有组织的结构。当我们的项目仅由几个模块/脚本组成时,可以遵循这一点。示例项目的目录可能如下所示:
my_project             # Root directory of the project
├── code               # Source codes
├── input              # Input files
├── output             # Output files
├── config             # Configuration files
├── notebooks          # Project related Jupyter notebooks (for experimental code)
├── requirements.txt   # List of external package which are project dependency
└── README.md          # Project README
  • 从名字可以明显看出,code文件夹包含各个模块(.py文件),inputoutput分别包含输入和输出文件,notebook包含我们用于实验的.ipynb笔记本文件。最后,config文件夹可以包含yamljsonini文件中的参数,并且可以由代码模块文件使用【config parser】(config parser—配置文件解析器— Python 3.7.11 文档)来访问。
  • requirements.txt包含项目需要的所有外部 python 包的列表。维护该文件的一个优点是所有这些包都可以使用pip install -r requirements.txt命令轻松安装。(无需手动安装每个外部软件包!)。一个例子requirements.txt文件如下图所示*(同* *package_name==package_version* 格式)
BeautifulSoup==3.2.0
Django==1.3
Fabric==1.2.0
Jinja2==2.5.5
PyYAML==3.09
Pygments==1.4
  • 最后,README.MD包含了项目的内容、原因和方式,以及一些关于如何运行项目的伪代码和示例用例。

类型 2:凯德罗

  • Kedro 不是一个项目结构化策略,它是一个由 QuantumBlack Labs 发布的 python 工具,为你做项目结构化😎。最重要的是,它们提供了大量的功能,使我们的项目组织甚至代码执行过程变得超级简单,这样我们就可以真正专注于最重要的事情——实验和实现!
  • 他们的项目结构如下所示。还有 btw,我们可以通过运行kedro new命令创建一个空白项目(不要忘了先通过 *pip install kedro* ) 安装 kedro)
get-started         # Parent directory of the template
├── conf            # Project configuration files
├── data            # Local project data (not committed to version control)
├── docs            # Project documentation
├── logs            # Project output logs (not committed to version control)
├── notebooks       # Project related Jupyter notebooks (can be used for experimental code before moving the code to src)
├── README.md       # Project README
├── setup.cfg       # Configuration options for `pytest` when doing `kedro test` and for the `isort` utility when doing `kedro lint`
└── src             # Project source code
  • 虽然大多数目录与其他类型相似,但有几点需要注意。Kedro 对不同模块进行分组的方式是创建不同的“管道”。这些管道存在于 *src* 文件夹中,该文件夹又包含模块文件。此外,它们对执行的单个功能进行了明确的分离——这些功能存储在 *nodes.py* 文件中,这些功能随后与 *pipeline.py* 文件(全部在单个管道文件夹中)*中的输入和输出相连接。Kedro 还通过将参数存储在conf文件夹中来分离代码和参数。
  • 除了帮助组织项目之外,它们还提供了顺序或并行执行的选项。我们可以执行单独的功能*(在* *nodes.py* ) ,或者单独的管道*(是功能的组合)*,或者一次完成整个项目。我们还可以创建完整项目的 doc,或者将项目编译并打包为 python .whl文件,只需运行一个命令。要了解更多细节,请相信我,我们只是触及了表面,请参考他们的官方文档。

代码格式

  • 采用自顶向下的方法,让我们先来看看一段整洁的代码。我们将在后面更详细地讨论代码的各个方面。现在,假设有人让你写一些脚本,那么一段理想的代码文件应该是什么样子。
  • 以下代码摘自csv_column_operations.py模块文件。它是为提示生成的:“编写一个以 CSV 文件为输入并返回一列之和的函数”

Some might argue why do such an overkill for a simple piece of code. Note, it's a dummy example. In real life, you will develop more complex pieces of codes and hence it become quite important that we understand the gist.
  • 现在让我们更深入地研究上面代码的各个方面。

模块结构

  • 模块是扩展名为.py的 python 文件,包含可执行代码或函数或类等。
  • 通常,我们从模块定义开始模块,这是我们提供模块的一些基本细节的区域。我们可以使用下面的模板*(很容易与上面显示的真实代码进行比较)*
"""<Short description><Long description>Author: <Name> <email>Created: <date>
"""
  • 接下来,我们应该使用注释行清楚地隔离模块的各个部分,如导入、代码区等。
  • 最后,在底部,我们可以包括一些关于如何运行代码的例子。将这些脚本包含在if __name__ == '__main__':中确保它们只在文件被直接执行时运行*(像* *python csv_column_operations.py* ) 。因此,当您说在另一个脚本中导入模块时,这些代码段不会运行。

功能结构

  • 函数是执行特定任务的基本代码块。一个模块由几个函数组成。为了告诉用户一个特定的代码块做什么,我们用一个函数定义来开始这个函数。下面提供了一个样本模板,
"""DescriptionParamters
---------
<parameter_1>: <data_type>
    <parameter_1_description>Returns
---------
<output_1>: <data_type>
    <output_1_description>
"""
  • 在此之后,我们可以开始添加相关的代码行。确保使用注释来分隔函数中不同的逻辑代码块。
  • 在编码部分的开始要处理的一件重要事情是检查参数和输入数据,以获得一些数据类型或数据内容相关的基本问题。大多数代码中断都是由于愚蠢的错误造成的,比如有人提供了错误的输入,在这种情况下,我们应该打印或记录警告消息,然后优雅地退出。上面相同的代码在步骤 1 部分包含两个这样的初步但重要的检查。

命名约定

我们可以遵循几个格式约定,比如骆驼案、蛇案等。挺主观的,看开发商。下面是一些命名 python 代码不同实体的例子*(取自 PIP8 惯例——有一些修改)*😇,

  • **模块名:**模块应该有简短的、全小写的名字(例如:csv_column_operations.py)
  • **函数或方法名:**函数名应该是小写的,单词之间用下划线分隔,以提高可读性。另外,不要忘记添加动词!(例:perform_column_sum())
  • **变量名:**类似于函数名,但没有动词!(例如:list_of_news)
  • **类名:**类名通常应该使用 CapWords 约定。(例:FindMax)
  • **常量名称:**常量通常在模块级定义,全部用大写字母书写,用下划线分隔单词。(例:MAX_OVERFLOWTOTAL)。

添加评论

PEP-8 定义了三种类型的注释,

  • **块注释:**这是为单个或一组代码行编写的。当你想解释一组代码行或者只是想分离代码时,这是可以做到的。在上面的例子中,你可以看到# Step {1, 2, 3}被用作隔离注释,而# run when file is directly executed被用于解释一组代码行。
  • **行内注释:**与代码添加在同一行。例如,看看# to handle csv files是如何被用来证明熊猫包装进口的。PEP-8 建议谨慎使用行内注释。
  • **文档字符串:*用于模块、函数或类的文档。PEP-257 建议对 docstring 使用多行注释(使用" ")。上面的示例代码中提供了一个模块和函数文档字符串(文档字符串的缩写)*的示例。

我们应该在评论中尽可能地描述。尝试将代码的功能部分分开,为复杂的代码行提供解释,提供关于函数输入/输出的细节,等等。你怎么知道你有足够多的评论?—如果你觉得有你一半专业知识的人不用半夜给你打电话就能理解代码!😤

缩进—制表符与空格

  • 坦白地说,我只打算用一根长棍子去碰这个话题,🧹.已经有几篇文章、 reddit 线程甚至电视剧(硅谷📺)这个话题在哪里讨论了很多!
  • 想要我的 2 美分吗?选择任何现代 IDE (比如 VSCode,Sublime 等),设置缩进为制表符,设置 1 个制表符= 4 个空格。完成的😏

其他提示

到目前为止,我们已经讨论了如何构建项目或者格式化代码。接下来,我们将介绍一组通用的良好实践,它们将为您省去一些痛苦😬

记录

  • 而不是在控制台中打印临时的语句(做一个cls然后噗的一声就没了💨),更好的办法是将这些语句保存在一个单独的文件中,您可以随时回去参考。这是伐木📜
  • Python 提供了一个用于日志记录的内置函数。通过参考官方的如何,记录到一个文件超级简单,
 # import
import logging
# config the logging behavior
logging.basicConfig(filename='example.log',level=logging.DEBUG)
# some sample log messages
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
  • 请注意,日志有一个层次级别,以隔离不同严重性的日志。在上面的例子中,level参数表示被跟踪并保存到文件中的最低级别。根据官方的 how to ,这些是不同的日志记录级别,以及关于何时使用哪个*(按严重性递增顺序)*的一些细节,

Python 中不同级别的日志记录。来源:链接

证明文件

  • 如果您计划在可预见的将来维护代码或者将代码移交给其他人,那么代码的文档化是绝对必要的。只要问问任何一个开发人员,当他们为他们计划使用的包找到现成的、精心组织的文档时,他们会有多兴奋!另一方面,自己创建一个看起来相当困难,不是吗?我的意思是,看看 sklearn 或者熊猫的美女博士。😮
  • 很抱歉吓到你了,但实际上这很简单😉。还记得我们之前遵循的所有函数和模块 docstring 以及格式吗?事实证明,我们可以利用许多开源工具,如 pydoc 和 sphinx 来创建成熟的 HTML 文档!讨论实际细节超出了本文的范围,但是按照这两个包的“如何”步骤并准备好文档是相当容易的。
  • 最后一点,如果你用的是 Kedro,这个过程就更简单了。您所要做的就是运行一个命令— kedro build-docs --open来创建文档并自动在您的默认浏览器中打开它!

虚拟环境

虚拟环境(VE)可以被认为是 python 环境的本地副本,是专门为一个项目创建的。这个本地副本就像一张白纸,因为任何需要的包都可以单独安装在这里。为任何新项目创建一个新的虚拟环境是极其重要的,因为,

  • 每个项目都有自己的依赖树*(一个特定版本的包需要另一个特定版本的包才能运行)*
  • 在开发一个项目时,我们可能需要降级或升级一个包,如果在基本 python 环境中进行,将会影响您的系统!
  • 因此,一个单独的 python (VE)副本,在那里你可以安装任何你想要的东西,似乎是最合理的解决方案。

使用 VE 基本上需要两步,

  • 创建一个 VE: 这可以通过在项目根目录下运行命令python3 -m venv tutorial-env来完成。(注意, *tutorial-env* 是 VE 的名字,你可以用重命名为任何东西)
  • 激活 VE: 这可以通过在 Windows 上运行命令tutorial-env\Scripts\activate.bat和在 Unix 或 MacOS 上运行命令source tutorial-env/bin/activate来完成。

就是这样!安装,卸载,升级或降级任何你想要的!

Remember to switch to another VE when you start working on another project or/and to deactivate the VE when you want to move to base VE.

参考

  • Python 代码 PEP8 风格指南
  • 凯卓
  • Python 3 文档

干杯。

Python 正则化指南

原文:https://towardsdatascience.com/a-guide-to-regularization-in-python-8abf91ebca9a?source=collection_archive---------14-----------------------

使用正则化来防止深度学习模型中的过拟合

安娜·涅克拉舍维奇在的图片

过度拟合是数据科学家在构建高度复杂的模型时面临的一个常见问题。当模型非常适合训练数据,但随后在新数据上进行测试时表现不佳时,就会出现这种情况。

这个问题在构建深度神经网络模型时最常出现,深度神经网络模型是一种统计模型,松散地表示大脑中的连通性。这些模型往往很复杂,因为它们可能包含数百到数千个参数。由于高度的复杂性,这些模型可以拾取随机噪声作为真正的趋势,这导致在对新数据进行推断时性能不佳。

对于任何使用深度学习模型进行预测的企业来说,过度拟合都是一个很大的问题。例如,如果一家公司想要预测客户保持率,过度拟合模型可能会将数据中的随机噪声和异常值表示为重要的统计趋势。因此,当用于预测客户未来是否会重复购买时,该模型的表现将会很差,从而导致公司的收入损失巨大。

几种方法通常用于防止深度学习模型中的过度拟合。套索回归,也称为 L1 正则化,是一种在复杂模型(如神经网络)中防止过度拟合的流行方法。L1 正则化通过向模型添加惩罚项来工作。这种损失会导致模型中的一些系数变为零,您可以将其解释为丢弃分配了随机噪声、异常值或在数据中发现的任何其他统计上无关紧要的关系的模型权重。

通常,L1 正则化对于模型构建过程的特征选择步骤是有用的。具体来说,您可以使用它来移除预测能力不强的要素。例如,在预测客户保持率时,我们可能会访问一些对做出准确预测不太有用的功能,如客户的姓名和电子邮件。

另一种正则化方法是岭回归,也称为 L2 正则化。岭回归的工作原理是均匀收缩分配给模型中要素的权重。当模型中的要素高度相关时,此方法非常有用。在客户保持示例中,高度相关的特征可能是上次购买花费的美元或购买的商品数量。这两个特征高度相关,因为顾客购买的商品越多,他们花的钱就越多。共线要素的存在也会对模型性能产生负面影响。

Python 库 Keras 使得构建深度学习模型变得容易。深度学习库可用于建立分类、回归和无监督聚类任务的模型。此外,Keras 还使得将 L1 和 L2 正则化方法应用于这些统计模型变得很容易。通过在单行代码中指定参数值,L1 和 L2 正则化都可以应用于深度学习模型。

在这里,我们将使用电信公司流失数据来建立一个预测客户保留率的深度神经网络模型。该数据包含一个虚构的电信公司的信息。

数据准备

首先,让我们导入 Pandas 库并将电信客户流失数据读入 Pandas 数据框:

进口熊猫作为 pd

df = pd.read_csv('telco_churn.csv')

接下来,让我们显示前五行数据:

print(df.head())

作者图片

为了构建客户流失模型,我们需要将数据中的客户流失列转换为机器可读的值。在 churn 的值为“否”的情况下,我们将分配标签“零”,在 churn 的值为“是”的情况下,我们将分配标签“一”

让我们导入 Numpy 包并使用 where()方法来标记我们的数据:

import numpy as npdf['Churn'] = np.where(df['Churn'] == 'Yes', 1, 0)

数据中的许多字段都是分类的。我们需要将这些字段转换成机器可读的分类代码,以便训练我们的模型。让我们编写一个函数,它接受分类列名的列表,并修改我们的数据框以包含每列的分类代码:

def convert_categories(cat_list):
    for col in cat_list:
        df[col] = df[col].astype('category')
        df[f'{col}_cat'] = df[col].cat.codes
        df[f'{col}_cat'] = df[f'{col}_cat'].astype(float)

让我们定义分类列的列表:

category_list = ['gender', 'Partner', 'Dependents', 'PhoneService', 'MultipleLines', 'InternetService',
                  'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV',
                  'StreamingMovies', 'Contract', 'PaperlessBilling', 'PaymentMethod']
convert_categories(category_list)

作者图片

我们可以看到,我们的数据框现在包含每个分类列的分类代码。

接下来,让我们定义我们的输入和输出:

df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
df['TotalCharges'].fillna(0, inplace=True)
cols = ['gender_cat', 'Partner_cat', 'Dependents_cat', 'PhoneService_cat', 'MultipleLines_cat', 'InternetService_cat',
                  'OnlineSecurity_cat', 'OnlineBackup_cat', 'DeviceProtection_cat', 'TechSupport_cat', 'StreamingTV_cat',
                  'StreamingMovies_cat', 'Contract_cat', 'PaperlessBilling_cat', 'PaymentMethod_cat','MonthlyCharges',
                  'TotalCharges', 'SeniorCitizen', 'tenure']X = df[cols]
print(X.head())
df['Churn'] = df['Churn'].astype(int)
y = df['Churn']

接下来,让我们为 Scikit-learn 中的模型选择模块导入训练/测试分割方法。让我们为训练和测试拆分我们的数据

from sklearn.model_selection import train_test_splitX_train, X_test_hold_out, y_train, y_test_hold_out = train_test_split(X, y, test_size=0.33)

用于分类的神经网络

为了开始构建我们的分类神经网络模型,让我们从 Keras 中的 layers 模块导入 dense layer 类。让我们从 Keras 的 models 模块中导入 sequential 类,从 Scikit-learn 的 metric 模块中导入 accuracy 方法:

from tensorflow.keras.layers import Densefrom tensorflow.keras.models import Sequentialfrom sklearn.metrics import accuracy_score

现在,让我们定义并拟合我们的模型,并使模型符合我们的训练数据。我们将建立一个具有两个隐藏层和 32 个神经元的神经网络。我们还将使用 20 个时期,这对应于通过训练数据的次数。

让我们定义我们的模型对象。

model = Sequential()

接下来,让我们使用 add 方法添加一个密集层。我们需要传入特性的数量,即列列表的长度,以及输入,即包含列列表长度的元组。我们还将根据正态分布并使用校正线性单位(ReLu)激活函数来初始化权重值。激活函数是模拟神经元放电的函数:

model.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu'))

接下来,我们将使用 add 方法添加两个隐藏层。这些层将具有 32 个神经元,并且还使用 ReLu 激活功能:

model.add(Dense(32, activation='relu'))model.add(Dense(32, activation='relu'))

然后我们需要添加输出层,它将有一个神经元和一个 softmax 激活函数。这将允许我们的模型输出类别概率,以预测客户是否会流失:

model.add(Dense(1, activation='softmax'))model.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy'])model.fit(X_train, y_train,epochs =20)

作者图片

我们可以看到,随着每个历元的增加,损耗一般会减少,而精度会增加。

现在让我们进行预测。预测输出是对应于测试数据中每个输入的流失概率的列表。我们可以将预测转换为二进制分数,其中大于 50 %( 0.5)的概率值将被分类为流失,标签为 1。否则,它将被归类为标签为 0 的客户,对应于留在公司的客户:

y_pred = model.predict(X_test)y_pred = np.where(y_pred > 0.5, 1, 0)Let’s also calculate the accuracy of our model:print(“Accuracy: “, accuracy_score(y_pred, y_test))

作者图片

我们看到我们的模型准确率为 77.9%,这是相当不错的。让我们看看是否可以通过 lasso 回归来提高性能。

拉索回归(L1 正则化)

Keras 使得用神经网络模型实现 lasso 回归变得简单明了。Keras 中的正则化器包有一个我们可以调用的方法,在我们的神经网络层中命名为 l1。这将对层中的权重应用惩罚项,这将有助于防止过度拟合。

通常,lasso 回归将无关紧要的要素权重设置为零,从而允许模型包含最重要的要素以进行准确预测。让我们从 Keras 导入正则化包:

from tensorflow.keras import regularizers

接下来,让我们定义一个新的模型对象,我们称之为 model_lasso。

model_lasso = Sequential()

在输入层中,我们将使用正则化程序包中的 l1 方法为 kernel _ regularizer 传入一个值:

model_lasso.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu', kernel_regularizer = regularizers.l1(1e-6)))

接下来的几行代码与我们最初的神经网络模型相同。唯一的区别是我们正在使用模型对象 model_lasso,而不是模型:

model_lasso.add(Dense(32, activation='relu'))
model_lasso.add(Dense(32, activation='relu'))
model_lasso.add(Dense(1, activation='sigmoid'))
model_lasso.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy'])
model_lasso.fit(X_train, y_train,epochs =20)y_pred = model_lasso.predict(X_test)
y_pred = np.where(y_pred > 0.5, 1, 0)
print("Accuracy With Lasso: ", accuracy_score(y_pred, y_test))

作者图片

如果模型中有许多对性能没有积极贡献的要素,套索回归是一个很好的选择。因此,它作为一个功能选择工具非常有用。

岭回归(L2)

在 Keras 中,将岭回归应用于神经网络模型也很容易。类似于套索方法,我们只需要在神经网络的层中调用一个方法名 l2。lasso 和 ridge 之间的区别在于,前者倾向于完全丢弃无关紧要的值,而 ridge 只是减少我们的神经网络中所有特征的权重大小。让我们定义一个名为 model_ridge 的新模型对象:

model_ridge = Sequential()

在输入层,我们将使用 l2 方法:

model_ridge.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu', kernel_regularizer = regularizers.l2(1e-6)))

剩下的和我们上面做的类似:

model_ridge.add(Dense(32, activation='relu'))
model_ridge.add(Dense(32, activation='relu'))
model_ridge.add(Dense(1, activation='sigmoid'))
model_ridge.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy'])
model_ridge.fit(X_train, y_train,epochs =20)y_pred = model_ridge.predict(X_test)
y_pred = np.where(y_pred > 0.5, 1, 0)
print("Accuracy With Ridge: ", accuracy_score(y_pred, y_test))

作者图片

使用 ridge,精度比我们构建的第一个神经网络以及使用 lasso 的神经网络稍好。选择最佳的正则化方法取决于用例。如果使用模型中的所有输入要素很重要,那么岭回归可能是正则化的更好选择。这可能是为了训练我们的模型而需要保留某些特征的情况。

例如,一个薄弱的特征可能仍然是一家公司的杠杆。他们可能希望了解模型预测如何随着弱特征的值的变化而变化,即使它对性能的贡献不是很大。

这篇文章的代码可以在 GitHub 上找到。

结论

对于使用神经网络等复杂模型的数据科学团队来说,防止模型过度拟合非常重要。如果不加以考虑,过度拟合会对公司的收入产生重大影响。具有许多参数的模型,如神经网络,特别容易过度拟合,并可能给研究人员一种模型性能良好的错觉。

通常,过拟合模型在对当前数据进行测试时表现出很强的性能,而一旦向模型提供新数据,其性能就会非常差。例如,在客户流失的情况下,如果客户不会重复购买,overfit 模型可能能够以很高的准确度进行预测。然而,当出现新的客户数据时,overfit 模型将表现不佳,并且不再能够预测客户流失。这种不准确会导致公司浪费大量的金钱和资源,通过广告和促销瞄准错误的客户,而忽略了实际上可能流失的客户。出于这个原因,对于每个数据科学家来说,很好地理解如何使用 lasso 和 ridge 回归来防止复杂模型的过度拟合是一项重要的技能。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作和 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/overfitting-regularization-python

简化 it 指南—数据科学前端变得更简单

原文:https://towardsdatascience.com/a-guide-to-streamlit-frontend-for-data-science-made-simpler-c6dda54e3183?source=collection_archive---------23-----------------------

让您的数据科学项目变得漂亮并可共享——所有重要概念的演练

Javier Allegue Barros 在 Unsplash 上拍摄的照片

在我们的数据科学工作中,甚至在我们投入时间和精力的一个很酷的副业项目中,我们多次面临如何正确展示我们工作的困境。我们应该自己建立一个新的网页,确保我们的模型作为一个 API 工作,最后,把它作为一个包放在一起,并期望其他人像我们一样运行和交互它吗?

做前端工作很难

因此,对于我们这些从不费心费力学习 HTML、CSS 和 Javascript 的细微差别的后端人员来说,Streamlit 是来拯救我们的。

什么是 Streamlit?

简而言之,这是一个允许我们通过用 Python 编写所有代码来为我们的机器学习和数据科学应用构建前端的库。漂亮的用户界面可以通过库中的众多组件轻松设计出来。这是他们网站上的完整文档。

这意味着您可以轻松地在 python 项目中拥有按钮、漂亮的文本显示、可滚动框、下拉列表、文件上传功能。

该项目

让我们从本文中的石头剪刀布项目开始:

如果你还没有读过那篇文章,这就是我们在那里做的工作——石头剪刀布数据集是来自三类图像的图像集合,你猜对了——石头、纸和剪刀手势。我们在Fastai深度学习库的帮助下,使用一些快速迁移学习对数据集进行图像分类。

在本文中,我们导入了数据集,训练了模型并保存了它。

现在是我们围绕它设计一个交互界面的时候了。

以防你对整个代码库感到疑惑:它都在这里。阅读本教程时,请随意跟随。:)

https://github.com/yashprakash13/RockPaperScissorsFastAI

该应用程序

我们将在我们的应用程序中添加以下两种预测图像的方法。我们可以:

  • 从测试图像列表中选择要预测的图像,或者
  • 上传新的测试图像进行预测

这听起来很简单,对吗?是的,就是它!但是有了这个,你也将获得在你进一步的项目中使用许多不同的 streamlit 组件的知识。

我们现在应该继续并确保我们安装了库。

在您的虚拟环境中时,从终端执行此操作:

pipenv shell #activate your virtual env
pipenv install streamlit #install streamlit

现在,在你最喜欢的代码编辑器中打开你的项目目录。

对于此应用程序,我们将在未来构建一个完整的容器化(dockerized)可共享应用程序,因此请确保首先构建此项目结构:

项目结构

整个应用程序将存在于应用程序目录中。

我们的项目需要放在模块目录中。

我们训练过的模型(酸洗过的)将存放在模型目录中。

最后,我们将拥有我们的 start.py ,它是在模块之外但在应用程序目录之内运行应用程序的主要文件。

另外两个文件只是使用虚拟环境时生成的 pipenv 文件。

现在我们已经解决了这个问题,让我们写一些代码吧!

一些重要的功能

整个应用程序只包含一些辅助函数和一个运行它们的主函数。

让我们看看 load_model.py 里面有什么

定义训练模型路径并导入 fastai:

from fastai.vision.all import *SAVED_MODEL_PATH = './models/trained_model.pkl'

最后,定义分类器函数:

def _get_model():
    model = load_learner(SAVED_MODEL_PATH)
    return modeldef perform_prediction(image_path):
    model = _get_model()
    pred = model.predict(PILImage.create(image_path))
    return pred[0]

我们还定义了另一个函数,通过 PIL 图书馆打开并显示图像:

def get_opened_image(image):
    return Image.open(image)

现在,我们可以转到 start.py 了。

Streamlit 组件

让我们将 load_model.py 模块中的助手函数与该文件中的函数连接起来:

**def** classify(image_file):
    pred = perform_prediction(image_file)
    **return** pred

现在让我们继续使用 streamlit 应用程序。

导入库:

import streamlit as st

首先,我们这样定义应用程序的标题:

st.sidebar.title("RPS Image Classifier")

接下来,我们确定我们有一个主函数。让我们创建一个 侧边栏——来容纳应用程序中的所有交互组件。

现在,我们定义上传图像的功能。

image_file = st.sidebar.file_uploader('Upload an image', type = 'png')

最后,我们制作了一个按钮,帮助我们将图像加载到应用程序中并显示出来:

if image_file and st.sidebar.button('Load'):
        image = get_opened_image(image_file)
        with st.beta_expander('Selected Image', expanded = True):
            st.image(image, use_column_width = True)

现在,我们还包括选择现有图像文件的选项:

image_file_chosen = st.sidebar.selectbox('Select an existing image:', get_list_of_images())

这里, get_list_of_images 是我们在 load_model.py 中定义的一个新函数:

def get_list_of_images():file_list = os.listdir(PATH_TO_TEST_IMAGES)return [str(filename) for filename in file_list if str(filename).endswith('.png')]

唷!这对于我们的 UI 来说已经足够了。

你的应用现在应该是这样的:

应用程序当前状态

这些是我们构建的组件:

  • st.title —写入标题的组件。各种用例的其他文本组件是: st.textst.writest.markdown 。我们在这里也会用到这些。
  • st .侧栏-构建上图中的侧栏。
  • st .侧栏. file _ uploader-使文件上传器的小部件在侧栏中可用。你也可以在没有边栏的情况下使用它,比如:st.file_uploader。然后,它将出现在上面图像中看起来空白的主要区域。
  • st.image —简单显示加载到应用程序中的图像的小部件。
  • st.selectbox —制作一个下拉列表,列出要选择的项目(这里是图片),而不是自己上传文件。

定义预测功能

这是我们一直在等待的部分,不是吗?多亏了 fastai,这个步骤也很简单。

def perform_prediction(image_path): model = _get_model() pred = model.predict(PILImage.create(image_path)) return pred[0]def _get_model(): model = load_learner(SAVED_MODEL_PATH) return model

这两个函数帮助我们在任何给定的图像上进行预测。我们从 fastai 加载学习模块,然后返回结果。在这种情况下,它是 pred 变量中的第一项。因此, pred[0]

我们现在唯一需要做的就是将这个功能链接到我们的 start.py 中。

prediction = classify(os.path.join(PATH_TO_TEST_IMAGES, image_file))

就这样!

让我们也显示结果吧!

st.subheader('Prediction')st.markdown(f'The predicted label is: **{prediction}**')

这个应用程序现在看起来是这样的:

事后的想法

如果你到目前为止一直跟随着我,花点时间祝贺你自己学习了一个酷的、有用的库的基础知识!为数据科学项目构建前端的难易程度从未如此之高,您肯定应该尝试在您未来的项目中应用更多这方面的内容。我一定会的!

**在下一篇文章中,**我将在 Docker 的帮助下包含这个应用程序。所以请继续关注!

谢谢你的阅读!

您可能想了解一些资源:

以下是我所有数据科学文章的主代码库:

https://github.com/yashprakash13/data-another-day#ml-mini-projects-because-crash-courses-are-the-best

我也在推特上写数据科学和软件工程。

深度学习领域指南

原文:https://towardsdatascience.com/a-guide-to-the-field-of-deep-learning-9bb9b21dae2?source=collection_archive---------14-----------------------

从入门级到专家级

机器学习的领域是巨大的。你很容易被外面的大量信息淹没。为了不迷路,下面的列表可以帮助你估计你在哪里。它提供了广阔的深度学习空间的轮廓,并且不强调某些资源。在适当的地方,我已经包括了帮助你定位的线索。

作者摘录的清单。这份名单可以在 GitHub 这里和 on idea这里获得。

由于列表变得相当长,我已经包括了上面的摘录;完整的列表在这篇文章的底部。

入学水平

入门级分为 5 类:

  • 数据处理向您介绍小型数据集
  • 经典机器学习涵盖了经典机器学习技术的关键概念
  • 网络涵盖了传统的 dnn、CNN 和 rnn
  • 理论列出了上述范畴背后的概念
  • 概述列出了你在这个阶段遇到的主要事情

数据处理

在入门级产品中,使用的数据集很小。通常,它们很容易放入主存储器。如果它们没有经过预处理,那么只需要几行代码就可以应用这些操作。主要针对主要领域音频图像时间序列文本进行操作。

经典机器学习

在进入深度学习的大领域之前,学习基本技术是一个不错的选择。这些包括回归、聚类和支持向量机。在列出的算法中,只有 SVM 算法可能稍微复杂一点。不要让你自己被这个压倒:试一试,然后继续前进。

网络

没有最重要的成分就没有深度学习:各种变体的神经网络;gan、AEs、VAEs、变压器、dnn、CNN、rnn 等等。但是还没有必要涵盖所有内容。在这个阶段,看一下最后三个关键词就足够了。

理论

没有神经网络就没有深度学习,没有(数学)理论就没有神经网络。你可以从了解数学符号开始学习。起初有点可怕,但你很快就会接受这种简洁的风格。一旦你掌握了它,看看矩阵运算,神经网络背后的一个核心概念。

有了这些知识,你就可以进行卷积运算,这是另一个核心概念。

简而言之,您将一个矩阵移动到另一个矩阵上,并计算重叠区域之间的内积。有许多变体——继续学习,你也会自然地使用它们!

一般

您可以放心,您可以在入门级的家用机器上使用所有类型的网络。典型的参数数量大约为一百万,对于您的任务来说已经足够了。主要是,你将不会专注于让一个网络运行,但也学习他们周围的东西。这包括学习您的工具、查阅文档、分析数据等等。

中间能级

中级和初级之间没有真正的分界线。你会注意到这一点,主要是通过处理更大的数据集,使用先进的网络进行定制项目,以及想出更好的方法来启动培训。你在这个阶段遇到的一切都是建立在你之前的工作之上的:

  • 数据处理关注更大的数据集
  • 定制项目是你将要做的事情
  • 网络变得更加先进
  • 训练让你更深入地训练一个网络
  • 理论侧重于拓展你的背景知识
  • General 列出了您在该级别使用的几个项目

数据处理

一旦你处理了几个千兆字节的数据集,你就会解决新的问题。足够快地从驱动器中获取它们是至关重要的,这可能涉及规范化技术和自定义管道。由于数据集往往会变得更加复杂,您可能需要修改扩充、自定义生成器或收集额外的数据。

自定义项目

正如在我以前的文章中,定制项目是中级的核心。自定义意味着工作在你自己的任务上,不再跟随关于 MNIST 的教程。这涉及所有主要领域,并为即将到来的类别创造了巨大的协同效应。

网络

在定制项目中工作,你自然会接触到更大的网络。这些网络通常具有高级层来接管繁琐的任务。数据正常化?为此只需使用 BatchNormalization 层。但是如果你的任务还没有图层,你会怎么做呢?答案很简单:你写一个自定义层。

除了高级层,高级架构也是现阶段的一个东西。普通的密集网络慢慢被复杂的语言模型取代,CNN 用于在生成网络中生成图像,而暹罗网络对于不平衡的数据集来说很方便。

培养

与各种各样的网络打交道可以让你更快地接触到新技术。其中之一是迁移学习,你可以使用一个针对你的问题进行不同任务训练的网络。微调的方向类似,意味着冻结大部分权重。仅更新剩余的“非冻结”重量

前两种技术也用于嵌入的概念中。嵌入是表示额外知识的一种真正聪明的方式。想象一棵树。一棵树不仅仅是树叶和木头。一棵树净化空气,提供阴凉,并为各种动物提供一个家。换句话说,“树”不仅仅是一个词,而是一个概念,涉及各种附加信息。您可以通过使用嵌入来合并这些信息。

从这个类别中可以学到更多,嵌入只是一个开始。如果你有一个复杂的数据集,你可以写你自己的回调来控制训练进度。或者您切换到多 GPU 培训,在不同的设备上复制模型,并同步更新。

更进一步,您可以将这与定制的训练循环结合起来,大概是在云中(因为只有少数人手头有一个以上的 GPU)。如果你需要强大的动力,那就使用 TPU。

理论

到目前为止,你看到的所有概念还包括一个潜在的理论。学习理论方面一定不要涉及复杂的数学,而是要理解事物是如何工作的。

在神经网络的情况下,这个驱动因素是反向传播算法。解析地确定更新通常在计算上是不可行的。诀窍是通过无数次应用衍生链规则来传播更新。你甚至可以用手做到这一点!

导数受激活函数的影响,最终在优化器的帮助下应用。给定一些损失函数,这些优化器确保你每一步都做得更好。观察这种进展涉及一些目标函数来评估当前权重的良好性。这就是指标发挥作用的地方,因为您已经处于中级水平,所以您将会遇到一些高级指标。

到目前为止,您所学的所有内容也向您介绍了神经网络优化中的常见问题。有一些技术可以减轻它们,看看高级(标准化)层背后的理论。在这个意义上,嵌入不是层,但仍然是自然语言处理的一个重要部分。对于生成网络,对应的是概率计算。

一般

从轻量级到高级网络伴随着更多的参数。您现在将主要处理大约 1 亿、2 亿个参数的模型。训练这样的网络是可行的,是的,但是可能需要你切换到更快或更多的硬件。

在很大程度上决定训练时间的一个驱动因素是您使用的数据集。较小的数据集处理速度更快,较大的数据集需要更长时间。您现在可能还需要处理无监督的数据,或者必须创建自定义数据集分割。

一旦您为前面的任务编写了定制代码,使用代码版本控制系统就变得非常有用。您最不希望遇到的情况是,一个工作代码因为一些更改而中断,并且无法回滚。当你进行实验跟踪时,可以避免类似的问题。一旦你达到了好的指标,你就想知道为什么,像权重和偏差这样的工具可以帮助你。

现在你研究了各种各样的主题,都有自己的挑战,你磨练了思考 ML 问题的能力。这是一个有用的副产品:你把你以前在不同的环境中学到的东西用于一个新的问题。一旦你解决了这个问题,为什么不为 GitHub 上的一些项目做点贡献呢?

优等

与之前的中级水平相比,您现在转移到更大的数据集,在组合中添加生成网络,并检查各种技术以加快您的训练。高级级别分为五个类别:

  • 数据处理要求您处理大型复杂的数据集
  • 定制项目现在包括生成网络
  • 培训探索缩短培训时间
  • 理论包括高级优化器和强化学习
  • 将军收集了你将会学到的广泛的东西

数据处理

从中级到高级包括处理更大的数据集。以前的大小大约是几十千兆字节,现在是几百千兆字节,大了 10 倍。请放心,您的处理管道仍将工作——但是您可能希望分布它们,以加快速度。

这个阶段的另一个主题是多模态数据集。这意味着数据集包含来自多个领域的样本:例如,结合了文本描述的图像。

自定义项目

除了你以前的项目,你现在探索了生殖网络的迷人世界。为此,您可以从自动编码器开始,然后过渡到 GANs 及其衍生产品。

培养

培训类别现在包括先进的技术,以提高效果和最大限度地减少培训时间。为了验证你的训练是否成功,你需要依靠精确的跟踪方法。对于简单的指标,进度条可能就足够了。但是随着更多要跟踪的内容和高级度量标准的出现,理解起来很容易变得错综复杂。解决方案是编写一个定制的跟踪例程。不过你不必从头开始,TensorBoard 和 Weights&bias 这样的工具会让记录变得简单。

一旦你达到一个平台期(简单地说,分数不再增加),你可以尝试安排学习速度。学习速度不是固定的,而是在训练过程中进行调整。这可能包括将学习速率除以一个常数因子,或者在两个边界之间循环。尝试一下,看看什么适合你的情况。

当一台机器不能胜任时,您可以分配工作负载。将其与混合精度训练相结合,以获得更快的速度。

如果您的模型或其激活对于单个设备来说太大,您可以求助于模型并行训练。在数据并行训练中,您的模型被复制,数据被分割成副本,权重更新被同步。在模型并行训练中,您拆分模型并将其层放置在不同的设备上。

这可以与多工作人员设置相结合,其中不同的设备不仅仅是不同的本地 GPU,而是来自不同的工作人员。下一个合乎逻辑的步骤将是利用多个 TPU 工人。这带来了严重的速度问题——但是要确保你的代码是高效的。

理论

这个阶段的理论从高级优化器开始。每个人都知道并使用默认的,但除此之外还有许多贡献。只要看看来自 TensorFlow 插件的可用优化器就能获得灵感。您可以将优化器与权重衰减相结合,平均更新,或者只更新一些变量。

还有强化学习领域在等着你。虽然这不是一个很难的话题,但在早期要学的东西太多了。但是一旦你具备了数学知识(初级)和中级知识,你就可以试一试了。

一般

就参数数量而言,您现在要处理多达十亿个参数。不用说,这已经不是一个人的项目了。建立如此大规模的网络需要团队合作和高效的代码。一旦您成功地训练了那些十亿参数模型(或者任何模型),您就可以部署它了。

但是还有更多。一些谣言表明每天有一百篇 ML 论文发表。保持最新是不可能的。但是你至少可以通过阅读报纸和订阅精选的时事通讯来使这变得更容易。大型(科技)公司的博客也是一个很好的信息来源。就我的资源而言,我喜欢吴恩达的The Batchnews, TensorFlow 的博客,Sebastian Ruder 的 news on NLP ,偶尔还会访问 DeepMind 的研究页面。

专家级

明确地说,你的旅程越远,界限就越模糊。以下所有有趣的事情都是建立在你之前所学的基础上的。没有什么是全新的——除了(部分)离开 CS 领域或者访问未知领域。

专家级包含三大类别:

  • 数据处理增加了 on-GPU 流水线
  • 理论引入量子 DL 并超越 CS
  • 通用扩展到万亿参数型号

数据处理

这是一个简短的类别,它以 GPU 上的管道为唯一项目。大多数管道可以很容易地设置为在 CPU 上运行。让他们在 GPU 上工作是另一回事。我记得看过一篇关于 GPU 编程的论文,我猜是 2010 年左右的,作者描述了他们如何使用 Nvidia 的 CUDA 来编码。自那以后已经取得了很大进展,随着 JAX 的进步,另一个机会出现了。

你需要一个 GPU 上的管道吗?如果数据预处理成为瓶颈,那么是的。

理论

在这个层次上,你肯定知道你的兴趣是什么。为了给你一些想法,你可以看看量子物理,图形神经网络和进化算法。现在已经超越了计算机科学。

如果你仍然好奇,请阅读开放式。

一般

Hornik 的通用逼近定理指出,一个函数可以用一个单层网络来逼近,与所需参考函数的偏差相当小。挑战在于找到这个近似值。实际上,更深层次的网络往往更有效。所以,你可以试试那些万亿参数模型。

训练这样的大型架构也是你研究的一部分。研究不局限于“专家”级别,更重要的是基础知识。理解论文(嗯,大部分),实施论文,教学也是如此。

各级最大的挑战是为整个社会做贡献。不仅仅是将卷积从 2 秒加速到 1 秒,而是更进一步 10 步,解决世界上紧迫的问题。这并不局限于计算机科学家,而是需要许多(科学)领域的共同努力。这是团队合作。我列出的线索通常是不详尽的,它们被写成路标。

接下来去哪里?

这是深度学习领域的一个详细但不详细的概述。详细,因为我试图创建几个阶段及其典型项目的概述。不详细,因为总是有进一步的东西要包括在内。

这样说来,有几个资源:

  • 入门级的可以去查一下 TensorFlow 开发者职业证书 (之后试试 TF 考试,看看你已经学会了什么!)
  • 伯克利的全栈深度学习课程
  • DeepMind 的高级深度学习&强化学习讲座
  • 张量流:数据和部署专业化和张量流:高级技术专业化

如果你有合理的进一步的东西,请让我知道。

进一步说明

  • 我肯定忘记了一些重要的事情
  • 根据我自己的经验,这是严重偏颇的
  • 这项工作使用了我跟踪 ML 进展的帖子作为基础;我扩展了它,并添加了更多的注释
  • 这是完整的列表:

作者的完整列表。在 GitHub 这里有,在 idea这里也有。

知识图表指南

原文:https://towardsdatascience.com/a-guide-to-the-knowledge-graphs-bfb5c40272f1?source=collection_archive---------2-----------------------

入门

笔记的合并,简要但温和地介绍了知识图表,并在几个实际的方面发光。

照片由克林特·王茂林在 Unsplash 上拍摄

目录

  • 简介
    -什么是知识图(KG)?
    -为什么是 KG?
    -KG 怎么用?
  • KG in practice -开源 KG
    -创建定制 KG
    • KG 本体
      -托管 KG(数据库)
      -从 KG 中查询事实

介绍

在本节中,我们将通过询问一些关于 KG 的简单但直观的问题来介绍 KG。事实上,我们将涵盖知识图的内容、原因和方式。我们还将通过一些现实世界的例子。

什么是知识图?

  • 为了更好地理解知识图表,让我们从理解它的基本单元即“事实”开始。事实是一公斤可以存储的最基本的信息。事实可以用三个一组的方式来表示,
    • HRT : <头,关系,尾>-
      -SPO:<主语,谓语,宾语>
  • 记住,上面的描述只是为了命名的缘故,因此你可能会遇到人们以任何一种方式提到这个事实。让我们遵循本文的 HRT 表示。因此,无论哪种方式,事实包含 3 个元素(因此,事实也称为三元组),可以帮助将 KG 直观地表示为图形,
    — **头或尾:**这些是实体,它们是真实世界的对象或抽象概念,表示为节点
    — **关系:**这些是表示为边的实体之间的连接
  • 下面显示了一个简单的 KG 示例。事实的一个例子是<BoB, is_interested_in, The_Mona_Lisa>。你可以看到 KG 只不过是多个这样的事实的集合。

知识图表示例。来源: [http://www.w3.org/TR/rdf11-primer 4]

  • 注意,以 KG 为单位存储的事实的数据类型没有限制。如上面的例子所示,我们有一些人(鲍勃,爱丽丝,..)、绘画(蒙娜丽莎)、日期等,在 KG 中表示为节点。

为什么是知识图?

这是任何人被介绍给 KG 时都会问的第一个问题,也是一个有效的问题。我们将尝试讨论一些点,在这些点上,我们将 KG 与普通图,甚至其他存储信息的方式进行比较。目的是强调使用 KG 的主要优势。

与正常图表相比

  • 异构数据:支持不同类型的实体(人、日期、画等)和关系(喜欢、出生于等)。
  • 模拟真实世界信息:更接近我们大脑对世界的心理模型(像正常人一样表现信息)
  • 执行逻辑推理:遍历一条路径中的图来建立逻辑连接(A 的父亲是 B and B 的父亲是 C,因此 C 是 A 的祖父)

与其他存储类型相比

  • 结构化表示:与文本数据等非结构化表示相去甚远
  • 消除冗余:与表格数据相比,无需添加大部分为空的列或行来添加新数据(一些事实)
  • 查询复杂信息:对于关系比单个数据点更重要的数据,比 SQL 更好(例如,如果您必须在 SQL 查询中执行大量JOIN语句,这本来就很慢)

KG 怎么用?

知识图表可以用于大量的任务——无论是逻辑推理、可解释的建议、复杂的分析还是更好的存储信息的方式。有两个非常有趣的例子,我们将简要讨论。

谷歌知识面板

  • 在谷歌上查询一个名人、地点或概念,它会在右边返回一个知识面板。该面板包含各种各样的信息(描述、教育、出生、死亡、引用等),有趣的是采用不同的格式(文本、图像、日期、数字等)。
  • 所有这些信息都可以存储在 KG 中,下面显示了一个这样的例子。这展示了存储信息是多么容易,也说明了阅读和理解 KG 的事实是多么直观。事实上,谷歌使用 KG 作为存储此类信息的基础。

Google 使用的基于知识图的知识面板示例。[右]谷歌搜索爱因斯坦时显示的实际面板。[左]再现了我们如何在 KG 中存储类似的信息。来源:作者+谷歌。

推荐系统

  • 经典算法考虑用户-项目交互来生成推荐。随着时间的推移,新创建的算法开始考虑关于用户和商品的额外信息,以改善推荐。
  • 下面,我们可以看到一个 KG(电影 KG),它不仅包含用户-物品连接(这里是人-电影),还包含用户-用户交互和物品属性。这个想法是,提供所有这些额外的信息,我们可以提出更准确、更明智的建议。在不进入精确算法的情况下,让我们合理化一下可以产生什么样的推荐。
  • 《阿凡达》可以推荐给,
    —鲍勃:因为它和《星际穿越》和《盗梦空间》一样属于科幻类型(鲍勃已经看过了)
    —爱丽丝:因为它是由詹姆斯·卡梅隆执导的(《泰坦尼克号》)
  • 《血钻》可以推荐给,
    ——鲍勃:迪卡普里奥在《盗梦空间》中也演过
  • 这个简单的思考练习应该展示如何使用 KG 很容易地以事实的形式表示大量真实世界的交互。然后,我们可以将基于 KG 的算法用于下游用例,如生成推荐。

电影推荐任务的知识图示例。来源:[1]

实践中的知识图

在这一节中,我们将从一个从业者的角度来看 KG。我们会通过一些开源的,随手可得的 KG。在某些情况下,我们甚至想要创建自己的 KG,所以我们也将讨论一些关于它的指针。然后通过讨论 KG 本体,我们会很快理解一个 KG 可以构造的一些规则和方式。最后,我们将讨论 KG 托管数据库,并学习如何查询 KG(从中获取事实)。

开源知识图表

另一方面,虽然有一些小型的和特定领域的 KG,但是我们也有许多大型的和领域不可知的 KG,其中包含所有类型和形式的事实。一些著名的开源知识图是,

  • DBpedia 是一个基于社区的众包项目,旨在从各种维基媒体项目中提取结构化内容。
  • Freebase:一个大规模的、协作编辑的交叉链接数据数据库。被吹捧为“一个公开共享的世界知识数据库”。它被谷歌收购,用来驱动自己的公斤。2015 年,终于停产了。
  • OpenCyc :是 Cyc 全部功能的入口,Cyc 是世界上最完整的通用知识库和常识推理引擎之一。
  • Wikidata :是一个免费、协作、多语言的数据库,收集结构化数据为 Wikimedia 项目提供支持。
  • YAGO :巨大的语义知识库,来源于维基百科、WordNet 和 GeoNames。

开源知识图的高级统计。来源:[2]

创建自定义知识图

尽管有几个开源的 KG,我们可能需要为我们的用例创建特定领域的 KG。在那里,我们的基础数据(我们希望从中创建 KG)可以有多种类型——表格、图形或文本 blob。我们将介绍如何从文本等非结构化数据中创建 KG 的一些步骤,因为使用最少的领域知识和脚本将结构化数据转换成 KG 相对更容易。完整的过程可以分为两步,

  • **事实创建:**这是第一步,我们解析文本(逐句)并提取类似<H, R, T>的三元组格式的事实。当我们处理文本时,我们可以利用预处理步骤,如标记化、词干化或词汇化等来清理文本。接下来,我们要从文本中提取实体和关系(事实)。对于实体,我们可以使用命名实体识别(NER)算法。对于关系,我们可以使用句子依存分析技术来寻找任何一对实体之间的关系。代码为的示例文章。
  • **事实选择:**一旦我们提取了几个事实,接下来明显的步骤就是删除重复的并识别可以添加到 KG 中的相关事实。为了识别重复,我们可以使用实体和关系消歧技术。这个想法是为了在重复的情况下巩固相同的事实或事实的元素。比如“阿尔伯特·爱因斯坦”在文中也可以写成“阿尔伯特·e .”或“a·爱因斯坦”,但实际上,它们都是指同一个实体。最后,我们可以有一个全面的基于规则的系统,该系统基于像冗余信息(A → sibling of → B存在,因此B → sibling of → A 是冗余的)或不相关信息这样的因素来决定哪个三元组应该被添加到 KG 或者哪个可以被跳过。

创建自定义知识图的步骤。来源:作者+ [3]

知识图本体

  • 本体是世界的模型(实际上只是一个子集),列出了实体的类型、连接它们的关系以及对实体和关系组合方式的约束。在某种程度上,本体定义了实体在世界中如何连接的规则。
  • 资源描述框架(RDF)和网络本体语言(OWL)是一些用于本体建模的词汇框架。它们为表达这些信息提供了一个通用的框架,因此可以在应用程序之间交换这些信息而不会失去意义。

RDF 模式三元组(非正式)。来源:作者+ [4]

  • RDF 提供了创建本体的语言,我们将用它来创建一个样本 KG。下面你可以看到 KG 用乌龟语言为 KG 创建的脚本。注意,在脚本的顶部,我们正在创建对许多预定义的本体的引用,因为没有必要重新发明轮子。接下来,为了创建 KG 的事实(或三元组),我们可以遵循PREFIX命令下面的行。
  • 注意,每个实体和关系都有一个唯一的标识符(它们的唯一键或 UID)。在整个代码中,相同的实体或关系应该由相同的 UID 引用。接下来,使用预定义的模式,我们可以为一个实体添加事实(用图形术语来说,将一个连接的边和尾节点添加到头节点)。这些事实可能包括另一个实体(通过它们的 UID 引用)、一些文本、日期(日期时间格式)、链接等。

一个用 Turtle 语言编写的脚本,用来创建样本知识图。来源:[4]

  • 最后,一旦我们准备好了脚本(带有ttl扩展——用于 Turtle 语言的脚本),这个脚本就包含了我们 KG 的完整模式和定义。就其本身而言,这可能并不有趣,因此该文件可以导入到任何 KG 数据库中,以实现美观的可视化和高效的查询。

主机知识图表

有两种类型的数据库可用于存储图形信息。第一种是“属性图”,如 Neo4j 和 OrientDB,它们不支持 RDF 文件(开箱即用),有自己的定制查询语言。另一方面,我们有“RDF 三元组存储”,它支持 RDF 文件和查询语言,如 SPARQL,它被普遍用于查询 KG。其中最著名的有(带开源版本),

  • GraphDB:onto text 的解决方案,提供前端(可视化)和后端(服务器)服务来查看和查询托管的知识图。
  • Virtuoso :由 OpenLinkSoftware 开发的解决方案,提供查询托管 KG 的后端服务。它还支持结合使用 SQL 和 SPARQL 来查询 KG。在它上面,很多像 DBpedia 这样的开源 KG 都托管在 Virtuoso 上。

查询知识图

  • 一旦事实被创建为 RDF 并托管在像 Virtuoso 这样的 RDF 三元组存储上,我们就可以查询它们以提取相关信息。SPARQL 是一种 RDF 查询语言,能够检索和操作以 RDF 格式存储的数据。一个有趣的读物是演练 DBpedia 和 Triplestore 。
  • 大多数 RDF 三元组存储都提供了可视化的 SPARQL 查询页面来获取相关信息。对于我们的例子,让我们使用一个这样的由 Wikidata 公开的可视化查询助手(如下所示)。显示了一个示例查询,其中我们想要提取作为家猫实例的所有实体(我们只想要一些猫🐱).如前所述,每个实体都有一个 UID,因此关系<instance of>被表示为P31,实体<house cat>被表示为Q146。该查询很容易理解,从第 2 行到第 5 行,我们只是想表达我们想要的是家猫实例的任何实体。因为 Wikidata 包含多种语言的数据,所以需要第 6 行来过滤特定于英语的结果。结果(实体及其 UID 和一些基本细节)显示在查询下方。

用 SPARQL 语言查询知识图。来源:作者+ [5]

  • 开源的 KG 也为经常使用的查询公开了几个现成的 API。下面显示了一个这样的 API(针对 Wikidata),它返回给定实体的相关信息。下面我们可以看到查询实体Q9141wbgetentities API 的结果,该实体是泰姬陵的 UID。

使用可用的 API 查询知识图。来源:作者+ [6]

结论

让我们就此打住。到目前为止,我们已经讨论了知识图的基础,并处理了使用 KG 的一些实际问题。本文是我在 UoT 做的客座演讲的第一部分的弱转录本。第一堂课(因此也是这篇文章)的目的是向初学者介绍 KG,并向中级读者介绍一些最重要和最有用的资源和工具 w.r.t. KG。至于更高级的听众,想讨论 KG 嵌入技术或者 KG 研究与应用赛道,可以参考第二讲。或者联系本文的第二部分🤗

**作者注:**这篇文章也作为我的数据科学(WIP)——数据科学懒惰指南 的章节出现。这是我在塔尔图大学为计算社会科学小组做的客座演讲的第一部分的弱转录本。

参考

  1. 基于知识图的推荐系统综述,郭庆宇等,2020
  2. 哪个知识图最适合我?迈克尔·费尔伯等人。艾尔。2018
  3. 知识图的研究综述:表示、获取和应用,季少雄等,2021
  4. RDF 引物
  5. Wikidata SPARQL 查询助手
  6. 维基数据 API 服务

干杯。

比率和比例回归指南

原文:https://towardsdatascience.com/a-guide-to-the-regression-of-rates-and-proportions-bcfe1c35344f?source=collection_archive---------4-----------------------

统计数字

在 Python 中使用 beta 回归建模概率

托马斯·凯利在 Unsplash 上的照片

通常在现实世界的回归问题中,目标变量可能是一个概率或一个比例。你可以考虑对你的文章的阅读率、你最喜欢的足球队的胜率或者你的企业的客户流失率进行建模。所有这些问题的共同点是目标变量被限制在 0 和 1 之间。

在这篇文章中,我们将探讨在这种情况下会出现什么样的困难,为什么标准的线性回归可能不适用,以及如何解决这个问题。

问题是

让我们考虑一下,你想根据几个变量,比如大小和位置,对你网站上的广告点击率进行建模。这里,我们生成一些模拟数据:

在产生回归问题之后,目标变量 y 被 expit 函数转换,使得它位于 0 和 1 之间的区间中(这将在下面解释)。请注意,我们生成的回归数据不在有界区间内,而是基于实数,为了方便起见,存在高斯误差。这不是最佳的,只是为了说明的目的。

这是绘制一个可观测量对 y. 的数据

由作者策划。

我们可以看到,所有的y-值都在区间[0,1]内,并且与可观测值的一些相关性是可见的。

太好了,让我们使用普通最小二乘法建立一个线性回归模型来模拟数据!

一切看起来都很好。所以我们来看看预测。这里我们绘制了 X4 可观察值与预测值 y 的对比图:

线性回归模型的预测。数据位于[0,1]区间之外。由作者策划。

如您所见,该模型预测负值以及大于 1 的值。因为我们想把我们的目标变量解释为点击率,这是没有意义的。

此外,代表概率或比率的随机变量通常是贝塔分布的。然而,普通的最小二乘回归假设数据遵循高斯分布。

有没有一种方法可以建立一个回归模型,既能保证预测值位于区间[0,1]内,同时又能说明 beta 分布的数据?确实有。在下文中,我们将研究由**法拉利和克里巴里-内托【1】**描述的贝塔回归模型。

注意,我们将只讨论区间[0,1]上的数据,因为这是最常见的。然而,该模型也适用于任意区间[a,b]上的有界数据。为此,只需转换目标变量 y → (y-a)/(a-b)。

先决条件

在我们开始解决问题之前,我们将讨论几个我们需要用到的函数。

在有界数据上执行回归的一个关键角色是所谓的链接函数 g ,它从有界区间[0,1]映射到实数:
g: [0,1] → R

完成这项工作的一个突出功能是 logit 功能。我们可以使用 logit 的逆函数来做相反的事情,称为 expit 或 sigmoid 函数:

这是两个函数的样子:

Logit 和 Expit 函数。红线表示一些示例点。作者的情节。

logit 函数在[0,1]区间的边缘是陡峭的,因此将值从接近 1 映射到大实数(例如,logit(0.99) = 4.60)。然而,对于非常高或非常低的值,expit 函数是平坦的,缓慢地接近 0 和 1(例如,expit(5) = 0.993)。

这两个函数是我们的工具箱,用于从[0,1]区间到实数的映射,以及从实数到区间的映射。

除了 logit 和 expit 函数,我们还将使用 beta 发行版。贝塔分布在概率建模中特别有用,因为它在区间[0,1]上工作。贝塔分布采用两个形状参数 α 和 *β。*不同的参数看起来会有很大的不同:

由 Pabloparsil —自己的作品,CC BY-SA 4.0,https://commons.wikimedia.org/w/index.php?curid=89335966

这是贝塔分布的定义:

但是,我们将使用稍有不同的参数化,替换为 =α/(α+β) 和 *ϕ=α+β.*在这种情况下可以解释为分布的平均值,而 ϕ 可以解释为它的精度。通过这种参数化,贝塔分布看起来如下:

贝塔回归

现在我们有了解决问题的必要工具。

贝塔回归的一般思想是,我们使用一个链接函数 g (例如,logit)来从我们的有界空间[0,1]映射到实数。在那里,我们将通过最大化相应的可能性来执行回归,假设我们的数据是贝塔分布的。为了解释有界空间[0,1]中的系数,我们需要使用 expit 函数将数据映射回来。因为我们在实数空间中执行回归,并且稍后才映射回我们的有界空间,所以我们可以保证来自我们的模型的任何预测也有界到区间[0,1]。

下面我们将介绍不同的步骤,包括相应的 python 实现。

首先我们建立回归模型。为此,我们假设对于每个数据点,我们的目标变量 y (例如,我们广告的点击率)由贝塔分布T3 的平均值表示。进一步我们假设我们可以将建模为:

其中 β ᵢ是回归参数,指数 t 表示我们的数据点。g 是我们的链接函数,如上所述。这意味着我们通过 logit 函数将(描述 y 的贝塔分布的均值)从有界区间转换为实数。在这里,我们通过一个标准的线性回归模型对 g( ) 建模。

但是我们如何获得参数 β ᵢ呢?我们在这个模型下最大化我们数据的可能性。可能性定义为:

这意味着对于每个数据点,我们都要乘以给定模型的数据的概率。在这种情况下, f 是上面定义的贝塔分布。我们想要找到参数β使得可能性最大。通常,在实践中更容易最小化似然性对数的负值(对数似然性)。但这只是技术细节。您可以在[1]中查找对数似然的分析形式。

我们可以认为我们的分析包含两个不同的世界。我们有一个概率界于区间[0,1]和实数界的世界。logit 和 expit 函数是这两个世界之间的联系纽带。这里是一个概览,其中世界的不同参数生活中。

贝塔回归模型概述。作者图。

我们的问题定义在[0,1]之间的有界区间上。这就是目标变量 y 的来源。我们假设 y 可以用均值*和精度 ϕ 的贝塔分布来建模。此外,我们在实数空间中定义我们的回归模型,因为我们假设 g( ) 可以由 ᵢ *建模。*然后,我们用平均值和 *ϕ、*找到最佳参数 β ᵢ *来最大化数据 y 相对于贝塔分布的可能性。记住取决于 β ᵢ经由
= g(Xβ) 。为了解释我们的模型做出的预测,我们需要用 g(Xβ)将它们映射回有界区间[0,1]。

下面是实现对数似然的 python 代码:

现在,我们可以运行一个优化器来最小化对数可能性。我们希望找到最佳参数 = g 、⁻ (Xβ) 和*、ϕ* ,使得数据 y 相对于我们的模型的可能性最大。请注意, ϕ 也是一个回归参数,包含在优化中。这样做是为了找到贝塔分布的最佳拟合形状。

为了约束 ϕ > 0 ,最小化器的边界是必要的,这很重要,因为没有为 ϕ < 0 定义贝塔函数。最小化器的输出表明收敛成功,并返回回归参数βt17】以及 ϕ:

我们可以检查回归是否可以恢复生成数据时使用的回归参数。为此,我们需要将它们乘以 50,因为我们使用该因子将目标变量映射到[0,1]区间(参见第一段代码)。如果我们这样做,我们得到(括号中的真值):

X0 = 10.8 (10.7),
X1 = 0.1 (0.0),
【X2 =-0.1(0.0),
X3 = 66.3 (66.5),
X4 = 41.5 (41.2)。

如您所见,我们能够以高精度重现回归参数。

最后,让我们看看我们的模型所做的预测。记住,我们需要使用 expit 函数将数据从实数空间映射回我们的有界区间:

预测数据介于 0 和 1 之间。由作者策划。

我们可以看到,所有的值都在区间[0,1]内,正如它们应该的那样。

结论

我们看到,对比率、概率或比例等有界数据进行回归有几个陷阱。首先,回归模型可能会预测不在区间内的值,从而做出不切实际的预测。第二,像比率或概率这样的数据通常不是高斯分布的,这使得普通的最小二乘法不适合。贝塔回归兼顾了这两点。在变换的空间上执行回归模型,然后将结果变换回有界区间。此外,该模型假设数据是贝塔分布的。这篇文章包括在 python 中执行 beta 回归所必需的代码。

如果你想深入概率和贝叶斯分析的世界,看看这篇文章:

资源

[1]法拉利公司和克里巴里-内托公司(2004 年)。模拟比率和比例的贝塔回归。应用统计学杂志31 (7),799–815。

XGBoost 超参数指南

原文:https://towardsdatascience.com/a-guide-to-xgboost-hyperparameters-87980c7f44a9?source=collection_archive---------6-----------------------

Jeremy Allouche 在 Unsplash 上拍摄的照片

常用超参数清单

如果你问,有哪一种机器学习算法能够在回归和分类中持续提供卓越的性能?

XGBoost 就是了。它可以说是最强大的算法,越来越多地用于所有行业和所有问题领域,从客户分析和销售预测到欺诈检测和信用审批等等。

也是很多机器学习比赛中的获奖算法。事实上,XGBoost 在 Kaggle 平台上的 29 场数据科学竞赛中有 17 场使用了。

不仅仅是在商业和比赛中,XGBoost 已经被用于大型强子对撞机(希格斯玻色子机器学习挑战)等科学实验中。

其性能的关键是其超参数。虽然 XGBoost 非常容易实现,但是困难的部分是调整超参数。在本文中,我将讨论一些关键的超参数,它们的作用以及如何选择它们的值。

但是在我去之前,我们先来谈谈 XGBoost 是如何工作的。

XGBoost 一览

XGBoost(或 eXtremeGradientBoost)不是传统意义上的独立算法。它更像是一个“提升”其他算法性能的开源库。它优化了算法的性能,主要是在梯度推进框架中的决策树,同时通过正则化最小化过拟合/偏差。

XGBoost 的主要优势是:

**灵活性:**可以执行回归、分类、排名等用户自定义目标的机器学习任务。

可移植性:它可以在 Windows、Linux 和 OS X 以及云平台上运行。

**语言支持:**支持多种语言包括 C++、Python、R、Java、Scala、Julia。

云系统上的分布式培训: XGBoost s 支持多台机器上的分布式培训,包括 AWS、GCE、Azure 和 Yarn clusters。

XGBoost 的其他重要特性包括:

  • 大型数据集的并行处理能力
  • 可以处理缺失值
  • 允许调整以防止过度拟合
  • 具有内置的交叉验证

下面我将首先介绍 XGBoost 的一个简单的 5 步实现,然后我们可以讨论超参数以及如何使用它们来优化性能。

履行

1)导入库

对于这个演示,我们不需要太多。从sklearn库中,我们可以导入用于分割训练和测试数据以及准确性度量的模块。注意,首先您需要安装(pip install)这个XGBoost 库,然后才能导入它。

# loading data
from sklearn.datasets import load_iris# to split data into training and testing set
from sklearn.model_selection import train_test_split# XGBoost library
import xgboost as xgb# evaluation metrics
from sklearn.metrics import accuracy_score

2。导入数据

我们使用的是 Scikit Learn library 内置的iris 数据集,所以不需要做数据预处理。加载数据后,我们只需分别存储输入要素和目标变量。

df = load_iris()
X = df.data
y = df.target

数据集有 4 个特征(预测值):

df.feature_names>> 
['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']

目标有 3 个类。

df.target_names>> array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

3。准备输入数据

首先,我们将数据分为训练集(70%)和测试集(30%)。作为一个额外的步骤,我们需要将数据存储到一个兼容的 DMatrix 对象中,以实现 XGBoost 兼容性。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state = 1)train = xgb.DMatrix(X_train, label = y_train)
test = xgb.DMatrix(X_test, label = y_test)

4。超参数

由于这是一个简单的演示,让我们只选择 3 个超参数。

# specify hyperparameters
params = {
    'max_depth': 4,
    'eta': 0.3,
    'num_class': 3
}epochs = 10

5。建模

现在您已经指定了超参数,绘制模型并进行预测只需要几行代码。

# train model
model = xgb.train(params, train, epochs)# prediction
y_pred = model.predict(test)

因此,即使使用这个简单的实现,该模型也能够获得 98%的准确性。

超参数

现在让我们进入这篇文章的核心——超参数。

XGBoost 中有许多不同的参数,它们大致分为 3 种类型:

  • 一般参数
  • 助推器参数
  • 任务参数

XGBoost 文档中列出了大约 35 个不同的超参数。然而,并不是所有的都同样重要,有些比其他的更有影响力。

XGBoost 超参数的部分列表(合成者:作者)

下面是在网格搜索中经常调整的一些参数,以找到最佳平衡。

频繁调整的超参数

  • ***n_estimators*** : 指定要提升的决策树数量。如果 n_estimator = 1,这意味着只生成一棵树,因此没有提升在起作用。默认值是 100,但是您可以使用这个数字来获得最佳性能。
  • ***subsample*** : 表示训练样本的子样本比率。子样本= 0.5 意味着在生长树之前使用了 50%的训练数据。该值可以是任何分数,但默认值是 1。
  • ***max_depth***:限制每棵树能长多深。默认值为 6,但是如果模型中存在过度拟合问题,您可以尝试其他值。
  • ***learning_rate*** ( 别名***:***eta***)***:是一个正则化参数,在每个 boosting 步骤中收缩特征权重。默认值为 0.3,但人们通常使用 0.01、0.1、0.2 等值进行调整。
  • ***gamma*** ( 别名***:***min_split_loss***)***:是树修剪的另一个正则化参数。它规定了种植一棵树所需的最小损失减少量。默认值设置为 0。
  • ***reg_alpha*** ( 别名***:***):它是 L1 正则化参数,增加它的值使模型更加保守。默认值为 0。
  • r***eg_lambda*** ( 别名***:***lambda***)***:L2 正则化参数,增加其值也会使模型保守。默认值为 1。

特殊用途超参数

上述参数集是“通用”参数,您可以随时调整这些参数以优化模型性能。有一些特殊用途的超参数在特定情况下使用:

***scale_pos_weight*** : 该参数在数据集不平衡的情况下非常有用,尤其是在分类问题中,一个类别的比例只占总观察值的一小部分(例如信用卡欺诈)。默认值为 1,但可以使用以下比率:总负面实例(如无欺诈)/总正面实例(如欺诈)

***monotone_constraints***:如果您想要增加对预测因子的约束,例如,一个非线性的、增加的、具有较高信用评分的信用贷款批准的可能性,您可以激活该参数。

***booster***:你可以选择使用哪种升压方式。您有三个选项:“dart”、“gbtree”(基于树)和“gblinear”(岭回归)。

***missing***:不完全是缺失值处理,而是用来指定在什么情况下算法应该将一个值视为缺失值(例如,客户年龄的负值肯定是不可能的,因此算法将其视为缺失值)。

***eval_metric***:指定使用什么损失函数,如回归使用 MAE、MSE、RMSE,分类使用对数损失。

摘要

就在 5 年前,还没有多少人知道 XGBoost(它最初于 2014 年发布,现在它正在彻底改变数据科学和机器学习领域,并应用于各种行业。训练 XGBoost 非常简单,但最难的部分是参数调整。在本文中,我展示了一个简单的演示实现,并讨论了最常用的超参数。

感谢阅读。请随意订阅以获得我的媒体文章通知,或者通过 Twitter、或 LinkedIn 与我联系。

逻辑回归手册

原文:https://towardsdatascience.com/a-handbook-for-logistic-regression-bb2d0dc6d8a8?source=collection_archive---------17-----------------------

关于逻辑回归的所有细节的备忘单

由故事在 Freepik 上的图片

逻辑回归是一种线性分类算法。分类是一个问题,其中的任务是将一个类别/类分配给一个新的实例,从现有的标记数据(称为训练集)中学习每个类的属性。分类问题的例子可以是将电子邮件分类为垃圾邮件和非垃圾邮件,查看身高、体重和其他属性来将一个人分类为健康或不健康,等等。

使用邮件主题和内容将电子邮件分类为垃圾邮件/非垃圾邮件。图片来源:自创

线性模型只能捕捉形式为 Y = mx + b 的直线关系,其中 m 是斜率,b 是截距。

逻辑回归方程看起来像…

逻辑回归方程

…其中β是需要学习的系数,x 是特征/解释变量。y 是响应变量,我们将更详细地研究它。

你也可以在 DataTrek 频道浏览这个话题的视频内容。DataTrek Youtube 频道上的逻辑回归系列的完整播放列表可在此处获得。

DataTrek :逻辑回归简介

为什么要学习逻辑回归?

我想到的一个问题是,当强大的非线性算法如支持向量机、深度学习、基于树的等可用时,为什么还要学习逻辑回归?这样做的理由如下。

  1. 这是数据科学从业者想到的第一个监督学习算法,用于创建一个强大的基线模型来检查提升。
  2. 这是一个基本的,强大的,易于实现的算法。这也是一个非常直观和可解释的模型,因为最终输出是描述响应变量和特征之间关系的系数。
  3. 更重要的是,它引入了理论概念,如最大似然估计、交叉熵、使用 sigmoid 和 softMax 函数,这些都是理解深度学习或其他复杂算法所不可或缺的。

逻辑回归的正确响应变量“Y”应该是什么?

线性模型的一个需求就是要有‘Y’,要连续无界的变量。因为,我们正在解决一个分类问题,我们希望为一个实例分配类,这个实例是一个离散值,既不是连续的,也不是无界的,这是一个问题。

备选方案可以是改为预测属于正类的实例的概率‘p’。实例属于负类的概率将自动变为(1-p)。概率是一个连续值,但仍在[0,1]之间有界,因此对无界变量的需求仍未得到满足。

营救几率日志

概率被定义为事件发生的概率与事件不发生的概率之比。赔率的对数取赔率的对数,从而得到在-无穷大到+无穷大范围内的连续且无界的值。

比值对数公式

找回概率

在分类任务中,最终目标是将类标签分配给实例。我们需要一种方法来从响应变量中获得概率,在逻辑回归的情况下,响应变量是比值对数。我们通过一个 sigmoid /logistic 函数来传递概率对数,从而得到概率。逻辑回归因使用了逻辑/Sigmoid 函数而得名逻辑。

Sigmoid 函数来恢复概率。图像来源:自创

Sigmoid 是非线性函数,为什么 Logistic 回归还是线性模型?

是的,sigmoid 是一个非线性函数,但逻辑回归仍然是一个线性模型,因为估计概率响应的 logit(比值比对数)是预测值的线性函数。Sigmoid 仅用于取回概率,但实际建模作为线性模型发生,以估计响应变量(概率对数)和特征之间的关系。

逻辑回归的损失函数

为了学习逻辑回归模型的最佳参数/系数/β集,我们需要一个可以最小化的损失函数。

可以用均方差吗?

损失函数的一个候选可以是预测概率(来自 sigmoid 函数)和实际类别标签(已知为训练集)之间的均方误差(MSE ),但结果表明,逻辑回归的 MSE 是非凸的,因为预测概率来自应用于 logit 的非线性,损失函数结果是非凸的。因为 MSE 是非凸的,并且全局最小值不存在,我们忽略它。

对数损失或交叉熵损失进行救援

我们发现,逻辑回归的对数损失或交叉熵损失是一个凸函数,可以用来寻找最佳的参数/系数/β集。

设ŷ为预测概率,y 为真实标签。给定 n 个数据点,对数损失/交叉熵如下所示。

逻辑回归的对数损失或交叉熵损失

最小化逻辑回归的交叉熵损失类似于最大化对数似然。

最小化损失函数,学习贝塔系数

与线性回归不同,逻辑回归不存在封闭形式/解析解。但是,由于损失函数(对数损失/交叉熵损失)是可微分的,因此可以利用像梯度下降这样的数值解来最小化损失并学习正确的参数。

我们所学内容的总结。图片来源:自创

逻辑回归在多类分类中的扩展

在多类分类任务中,存在多个类/类别。例如,通过观察萼片长度、萼片宽度、花瓣长度和花瓣宽度来预测花的不同品种。给定一个实例,我们需要将它分配到一个类/类别中。

图片来源:维基百科

处理多类分类的逻辑回归调整

  • softMax 函数用于从响应变量 y 中获取概率。给定 k 类,类 i 的概率如下所示。这就是为什么多类分类的逻辑回归也称为 Softmax 回归,或者也称为多项式逻辑回归。

图片来源:维基百科

  • 在多类分类的逻辑回归中,每个类都有自己的超平面、自己的一组参数/系数/β,softmax 充当归一化层。因此,在二元分类中,每个特征学习一个β,而对于多类分类,每个类的每个特征学习一个β。

解决多类分类任务的另一种方法

解决多类分类任务的另一种方法是将问题转化为二元分类任务。这是通过两种方式实现的。

  1. 一对一分类
  2. 一对其余分类。

你可以在这里阅读更多相关信息。

作为最后一步,为了测试我们对逻辑回归的学习,我们可以从执行探索性数据分析、拟合模型和分析学习到的系数开始,解决一个多类分类问题。在这本笔记本中,我总结了鸢尾花数据集上逻辑回归的所有重要方面,以及额外的 EDA 和可解释的 AI 部分。请随意叉笔记本,延伸分析&学习。

https://www.kaggle.com/abhishekmungoli/logistic-regression-iris-dataset/notebook

结论

通过这篇博文,我们观察了逻辑回归的不同方面,如响应变量、恢复概率、损失函数以及求解。希望你喜欢它,如果你有进一步的疑问,请随时联系我们。

如果你有任何疑问,请联系我。我将有兴趣知道你是否有一些有趣的问题要解决,需要一些指导。

我的 Youtube 频道获取更多内容:

DataTrek Youtube 频道

关于作者-:

Abhishek Mungoli 是一位经验丰富的数据科学家,拥有 ML 领域的经验和计算机科学背景,跨越多个领域并具有解决问题的思维方式。擅长各种机器学习和零售业特有的优化问题。热衷于大规模实现机器学习模型,并通过博客、讲座、聚会和论文等方式分享知识。

我的动机总是把最困难的事情简化成最简单的版本。我喜欢解决问题、数据科学、产品开发和扩展解决方案。我喜欢在闲暇时间探索新的地方和健身。关注我的 Linkedininsta gram并查看我的往期帖子。我欢迎反馈和建设性的批评。我的一些博客-****

另外,查看我关于线性回归的博文或视频版本这里。

******https://medium.com/geekculture/a-complete-guide-to-linear-regression-cfa984055671

DataTrek :线性回归系列

  • 确定您的数据分布
  • 降维:PCA 与自动编码器
  • 体验遗传算法的威力
  • 每个数据科学家都应该避免的 5 个错误
  • 以简单&直观的方式分解时间序列
  • GPU 计算如何在工作中拯救了我?
  • 信息论& KL 分歧第一部分和第二部分
  • 使用 Apache Spark 处理维基百科,创建热点数据集
  • 一种基于半监督嵌入的模糊聚类
  • 比较哪个机器学习模型表现更好
  • 分析 Fitbit 数据,揭开疫情封锁期间身体模式变化的神秘面纱
  • 神话与现实围绕关联
  • 成为面向业务的数据科学家指南******

使用 Spark 分析大数据的实践演示

原文:https://towardsdatascience.com/a-hands-on-demo-of-analyzing-big-data-with-spark-68cb6600a295?source=collection_archive---------6-----------------------

理解大数据

扫描一部小说,计算圆周率,对五千万行进行回归

由伯特·布瑞尔在 Unsplash 拍摄的照片

云服务公司 Domo 估计,2020 年,WhatsApp 用户每分钟发送 4170 万条信息,网飞播放 40.4 万小时的视频,24 万美元在 Venmo 上易手,6.9 万人在 LinkedIn 上申请工作。在这些数据中,是那些公司用来理解现在、预测未来,并最终在高度竞争的市场中生存下来的模式。

但是,如何从如此庞大的数据集中提取洞察力,以至于当你试图将它们加载到pandas时,你的笔记本电脑会死机?当一个数据集的行数超过美国家庭在 50 年内的收入中值[1]美元时,我们可以前往 BestBuy.com,将他们的电脑按“最贵”排序,并支付一些现金购买一台昂贵的机器。

或者我们可以试试 阿帕奇 Spark

在这篇文章结束时,你会明白为什么你不需要昂贵的硬件来分析大规模数据集——因为你已经做到了。在统计一部小说中每个字母的出现频率、手工计算 π 以及处理一个有 5000 万行的数据帧之前,我们会先讲一下什么是 Spark。

目录

  1. 大数据的分析框架
  2. 统计小说中的字母频率
  3. 计算 π
  4. 火花数据帧和机器学习

照片由法比奥在 Unsplash 上拍摄

大数据的分析框架

**Spark 是一个处理海量数据的框架。**它的工作方式是 将你的数据 划分成子集, 将子集 分发到工作节点(无论它们是笔记本电脑上的逻辑 CPU 核心还是集群中的整个机器),然后 协调 工作节点来分析数据。本质上,Spark 是一种“分而治之”的策略。

一个简单的类比有助于形象化这种方法的价值。假设我们想统计图书馆的藏书数量。“昂贵的计算机”方法将是教一些人尽可能快地数书,训练他们多年来在冲刺时准确计数。虽然看起来很有趣,但这种方法并没有多大用处——即使是奥林匹克短跑运动员也只能跑这么快,如果你的点书员受伤或决定转行,你就没那么幸运了!

与此同时,Spark 方法是随机找 100 个人,给每个人分配图书馆的一个区域,让他们清点所在区域的书籍,然后把他们的答案加在一起。这种方法更具可扩展性,容错性更强,成本更低……而且看起来可能还很有趣。

Spark 的主要数据类型是 弹性分布式数据集(RDD) 。RDD 是分布在许多地方的数据的抽象,就像实体“沃尔玛”是世界上数百万人的抽象一样。使用 rdd 感觉就像在内存中操作一个简单的数组,即使底层数据可能分布在多台机器上。

入门指南

Spark 主要是用 Scala 编写的,但是也可以在 Java、Python、R 和 SQL 中使用。我们将使用 PySpark ,Spark 的 Python 接口。要安装 PySpark,请在终端中键入pip install pyspark。你可能还需要安装或更新 Java。当您可以在终端中键入pyspark并看到类似这样的内容时,您就知道一切都已经设置好了。

本文中的其余代码可以在 Spark 终端中运行,或者在单独的 Python 或 Jupyter 实例中运行。我将在 Jupyter 中运行剩余的代码,这样我们就可以使用非常方便的%%timeit IPython 神奇命令来测量代码块的速度。

下面是一个小小的 PySpark 演示。我们首先手动定义SparkSession来启动到 Spark 的连接。(如果您在 PySpark 终端中,这已经为您完成了。)然后我们创建一个数组的 RDD,可视化前两个数字,并打印出最大值。通过.getNumPartitions,我们看到 Spark 将我们的数组分配给了我机器上的八个逻辑核心。

有了这些基础知识,我们就可以开始利用 Spark 处理大型数据集了。由于您可能没有任何 TB 或 Pb 大小的数据集可供分析,因此我们需要有点创造性。先说一本小说。

Arif Riyanto 在 Unsplash 上拍摄的照片

统计小说中的字母频率

古腾堡计划是一个公共领域书籍的在线知识库,所以我们可以从那里提取一本书进行分析。让我们读一读列夫·托尔斯泰的《战争与和平》——我一直想读一读,或者至少知道字母表中每个字母出现的频率![3]

下面我们用美汤 Python 库从小说的网页中获取 HTML,整理段落,然后追加到一个列表中。然后我们删除第一个 383 段落,它们只是目录!我们剩下 11,186 个段落,从 4 个字符到 4381 个字符不等。(那串一类的人物,不过用战争与和平,也许小说人物也是如此。)

尽管战争与和平是一部大型小说,但我们看到pandas仍然可以毫无问题地处理高级指标——line 38 在我的笔记本电脑上几乎可以立即运行。

但是当我们开始问更难的问题时,比如整本书中每个字母的出现频率,我们会开始注意到 Spark 的性能有了实质性的提高。这是因为这些段落可以彼此独立地处理;Spark 会一次处理几个段落,而 Python 和pandas会逐个处理。

像以前一样,我们开始我们的 Spark 会话,并为我们的段落创建一个 RDD。我们还加载了Counter,一个为计数而优化的内置 Python 类,以及reduce,稍后我们将使用它来演示基本的 Python 方法。

然后用rdd.map(Counter)Counter命令映射到每一段。请注意,除非我们添加.take(2)来输出前两个结果,否则什么都不会发生——Spark 执行惰性评估,这是一种优化查询链的方法,直到实际需要返回一个结果。

rdd.map(Counter)为我们提供了一个新的 RDD,其中包含每一段的字母频率,但我们实际上想要整本书的字母频率。幸运的是,我们可以通过简单地将Counter对象加在一起来做到这一点。

我们使用.reduce方法执行从多元素 RDD 到单个输出的缩减,传递一个匿名加法函数来指定如何折叠 RDD。[4]结果是一个Counter对象。然后,我们通过使用它的.most_common方法打印出十个最常见的字符来完成我们的分析。

获胜者是…太空!这是相同的频率,但在一个很好的柱状图中。

作者图片

使用 Spark 值得吗?让我们通过计时我们的任务在 Spark 和基本 Python 中运行的时间来结束这一部分。我们可以使用%%timeit IPython magic 命令在分离 Jupyter 笔记本单元格,看看我们的方法如何比较。

在 Spark 中:

在基本 Python 中:

使用 Spark,我们比使用基本 Python 快 67.7%。太棒了。现在我需要决定如何利用这多出来的半秒钟空闲时间。

照片由普里西拉·杜·普里兹在 Unsplash 拍摄

计算 pi

关于如何使用随机数计算圆周率,有很多很棒的教程。简单的总结就是我们在(0,0)和(1,1)之间生成随机的 x-y 坐标,然后计算那些落在半径为 1 的圆内的点的比例。然后,我们可以通过将这个比例乘以 4 来求解 π 。在下面 10,000 个点的可视化中,我们将蓝色点的数量除以总点数,得到 π /4。

作者图片

我们生成的点越多,我们对 π 的估计就越准确。这是 Spark 的理想用例,因为生成的点都是独立的。与其分析预先存在的数据,不如使用我们的 worker 节点分别生成数千个点和*来计算落在圆内的那些点的比例。*然后我们可以取我们的比例的平均值,作为我们对 π 的最终估计。

这是我们让每个工人运行的函数。我试图在 1)每次生成一个点(内存低,但速度慢)和 2)一次生成所有样本,然后计算比例(有效,但可能达到内存限制)之间取得平衡。我发现的一个体面的解决方案是将n_points分成几个,计算每个块在圆内的点数比例,然后在最后得到比例的平均值。

现在让我们创建一个 RDD 并将calculate_pi映射到每个元素,然后取每个元素的估计值 π 的平均值。

我们对圆周率的估计还不算太差!再次使用%%timeit,我们看到在我的机器上,基本 Python 需要大约 3.67 秒,而 Spark 需要 0.95 秒,提高了 74%。不错!

维克多·加西亚在 Unsplash 上拍摄的照片

火花数据帧和机器学习

让我们再做一个例子,这次使用 Spark 在 rdd 之上提供的一个很好的抽象。在类似于pandas的语法中,我们可以使用 Spark 数据帧对太大而不适合pandas df 的数据执行操作。然后我们可以用我们的数据框架训练机器学习模型。

火花数据帧

您可能没有一个有几百万行的数据帧,所以我们需要生成一个。因为根据定义,我们试图创建一个太大而不适合pandas的数据集,所以我们需要分段生成数据帧,反复保存 CSV 以便稍后用 PySpark 接收。

我们做 5000 万行吧,只是为了好玩。我们将生成 50 个 CSV,每个 CSV 有 1,000,000 行。我们的数据将包括四名学生的考试成绩,以及他们前一天花在学习和跳舞上的时间。这些是致力于大数据的学生,他们每人将参加大约 1250 万次考试!

我们现在将从 CSV 创建一个 Spark 数据帧,可视化模式,并打印出行数。但是在定义 Spark 会话时,我们首先需要添加一个配置选项——为了运行下面的代码块,我需要将驱动程序内存加倍,以避免 Spark 崩溃!我们将使用.config('spark.driver.memory', '2g')线路来实现这一点。

现在让我们分析我们的数据。我们将从计算每个人的行数开始。

各 1250 万次考试……不可思议。现在让我们找出每个人学习时间、跳舞时间和考试成绩的平均值。

不出所料,人与人之间没有差异,因为我们在生成数据时没有指定任何差异。平均值也正好在数据指定范围的中间:0-8 代表小时,0-100 代表分数。

为了使这些值更好更全面,我们实际上需要创建一个用户定义函数(UDF) 。对于我们可以应用于 groupby 操作的函数,我们将加载pandas_udfPandasUDFType。我们还导入了DoubleType,它引用了函数返回值的数据类型。在定义了我们的函数之后,我们使用spark.udf.register使它对我们的 Spark 环境可用。

现在我们可以将它应用到我们的数据框架中。注意,由于我们使用了.agg,我们需要传入一个字典,其中包含我们想要应用 UDF 的列。我没有打出{'study': 'round_mean', 'dance': 'round_mean', 'score': 'round_mean'},而是用了字典理解来装腔作势。虽然这可能是相同的击键次数…

干净多了!UDF 是一个强大的工具,可以对 PySpark 中没有的数据进行计算。

机器学习

最后,让我们对数据进行线性回归。在训练模型时,您可能习惯于将特征向量分布在数据帧的各个列中,每个特征一个。例如,在sklearn,我们可以直接在score列和['study', 'dance']列上安装模型。

**然而,Spark 期望一行的整个特征向量驻留在一列中。**因此,我们将使用VectorAssembler将我们的studydance值转换成一个新的 2 元素向量的features列。

现在我们实际上符合我们的模型。我们将数据分成训练集和测试集,然后使用LinearRegression类找到featuresscore之间的关系。最后,在计算测试集上的 RMSE 之前,我们可视化我们的系数及其 p 值。

在我们完全伪造的数据中,看起来学习对考试成绩有相当大的影响,而舞蹈……没有那么大。有了这么多数据,我们的 p 值都是“零”也就不足为奇了——有了 5000 万行,所有这一切表明系数并不完全为零。看看 RMSE:我们模型的预测平均比实际分数低 6.12 分!自动生成的数据总是那么干净漂亮。

照片由 Ameen Fahmy 在 Unsplash 上拍摄

结论

这篇文章深入探讨了如何使用 Spark 处理大数据。我们从分布式计算的概述开始,然后计算托尔斯泰的战争与和平中每个字母的频率,用随机生成的数字估计 π ,最后分析一个 5000 万行的 Spark 数据帧。这篇文章有望为你成为谷歌下一个炙手可热的数据工程师打下基础。

如果你有兴趣了解更多,兔子洞会更深!在分析方面,有针对网络应用的图形处理和针对连续数据流的结构化流。在工程方面,通过更好地配置驱动程序和工人的数量和大小,有大量的优化可以挤出来——我们只是在一台机器上使用默认数量的工人节点,但是当在多台机器上运行 Spark时,情况会变得复杂得多。类似地,分配合适的内存量对于避免 Spark 崩溃或让其他应用程序挨饿至关重要。

但是,一旦你掌握了 Spark 的细微差别,建立模型来发现 Venmo 上的欺诈行为,或者为网飞用户确定完美的下一场演出,或者帮助 LinkedIn 上的人找到下一份工作,都将是一小步。当你有工具去理解永无止境的数据时,这个世界就是你的了。

最好,
马特

脚注

1.介绍

根据Census.gov的数据,2019 年的家庭收入中值为 68703 美元。将这个乘以 50 得到 343 万,比我们在这篇文章中分析的一些数据要小。

2.大数据的分析框架

我不得不深入兔子洞去了解 Spark 具体将任务分配给什么硬件。您的计算机有几个具有独立处理能力的物理 CPU 内核。举例来说,这就是你如何在 YouTube 播放时输入 Word doc 并翻阅照片。

我们可以更进一步,尽管一个物理 CPU 内核可以通过两个或更多个“逻辑”内核之间的超线程同时处理多个任务。逻辑内核充当独立的内核,每个内核处理自己的任务,但它们实际上只是同一个物理内核。诀窍在于,物理内核可以以令人难以置信的速度在逻辑内核之间切换,利用任务停机时间(例如,在你输入搜索词后等待 YouTube 发回数据)来挤进更多计算。

当在本地机器上运行时,Spark 将任务分配给计算机上的所有逻辑核心,除非您另外指定。默认情况下,Spark 为每个内核留出 512 MB,并将数据平均分配给每个内核。可以用sc.defaultParallelism查看逻辑核的数量。

3.统计小说中的字母频率

在这篇文章的早期草稿中,我自己动手为一部“小说”生成了文本。我看了一些lorem ipsumPython 包,但是有点不一致;我发现了非常有趣的 Bacon Ipsum API 但是不想用几千段的请求淹没它。下面的代码使用随机字符串生成一部 10 万段的“小说”,或者说 8.9 倍的《战争与和平》只有区区 11186 段。原来写小说比我想象的要容易得多!

4.统计小说中的字母频率

您可以很容易地用更复杂的函数或者您已经提前定义的函数来减少 rdd。但是对于很多大数据处理来说,操作通常非常简单——将元素加在一起,通过某个阈值进行过滤——所以在使用这些函数的一两次之外显式定义这些函数有点大材小用。

Python 中 SQL 与 NoSQL 数据库的实践演示

原文:https://towardsdatascience.com/a-hands-on-demo-of-sql-vs-nosql-databases-in-python-eeb955bba4aa?source=collection_archive---------6-----------------------

实践教程

用 SQLAlchemy 和 PyMongo 打动你的朋友

艺术家对 MongoDB 数据库的解读。由乔尔·菲利普在 Unsplash 上拍摄的照片

从古代的政府、图书馆和医疗记录到当今的视频和物联网流,我们一直需要高效存储和检索数据的方法。昨天的文件柜已经变成了今天的计算机 数据库 ,对于如何最好地组织数据有两种主要的范例:关系型*(SQL)与非关系型*(NoSQL)方法。**

数据库对任何组织来说都是必不可少的,所以了解每种类型在哪里有用是很有用的。我们将从简单介绍 SQL 和 NoSQL 背后的历史和理论开始。但是记住抽象的事实只能让你到此为止——然后我们将实际上用 Python 创建每种类型的数据库,以建立它们如何工作的直觉。我们开始吧!

数据库的时间序列

SQL 数据库

在 20 世纪 50 年代企业开始采用计算机后不久,数据库就出现了,但是直到 1970 年关系数据库才出现。关系数据库的主要思想是通过只存储一次数据来避免重复数据,数据的不同方面存储在具有正式关系的表中。然后可以从不同的表中提取相关的数据,过滤,并用 SQL 或结构化查询语言中的查询重新排列。****

假设我们是一所学校,正在组织学生、成绩和教室的数据。我们可以有一张像这样的大桌子:

作者截图

**然而,这是一种非常低效的数据存储方式。因为学生有多个考试分数,**将所有数据存储在一个表中需要复制信息,我们只需要列出一次,如 Jerry 的爱好、教室 ID 和老师。如果你只有少数几个学生,这没什么大不了的。但是随着数据量的增长,所有这些重复的值最终会消耗存储空间,并使从表中提取实际需要的数据变得更加困难。

相反,将这些信息分解到单独的表格中,然后将表格中的信息相互关联会更有效率。这就是我们的桌子的样子:

作者截图

与主表中的 42 个单元相比,只有 30 个单元-提高了 28.5%!对于这组特定的字段和表格,当我们增加学生数量时,比如增加到 100、1000 或 1000000,改进实际上稳定在 38%。仅仅通过重新排列数据,就节省了三分之一以上的存储空间!

但是仅仅将数据存储在单独的表中是不够的;我们仍然需要模拟他们的关系。我们可以用如下所示的实体关系图来可视化我们的数据库模式。

作者截图

此图显示一个教室由多个学生组成,每个学生有多个年级。我们可以使用这个模式在一个关系数据库管理系统(RDBMS) 中创建一个关系数据库,比如 MySQL 或 PostgreSQL ,然后开始我们愉快的、高效的存储方式。

NoSQL 数据库

所以我们已经找到了解决所有数据存储问题的方法,对吗?不完全是。关系数据库非常适合于容易存储在表中的数据,比如字符串、数字、布尔值和日期。例如,在我们上面的数据库中,每个学生的爱好可以很容易地作为一个字符串存储在一个单元格中。

但是如果一个学生有不止一个爱好呢?或者,如果我们想跟踪业余爱好的子类别,比如锻炼、艺术或游戏,该怎么办?换句话说,如果我们想存储这样的值会怎么样:

**hobby_list = ['gardening', 'reading']
hobby_dict = {'exercise': ['swimming', 'running'],
              'games': ['chess']}**

为了允许兴趣爱好的列表,我们可以创建一个中介多对多表来允许学生拥有多个兴趣爱好(并且允许多个学生拥有相同的兴趣爱好)。但是对于爱好词典来说,它开始变得复杂了……我们可能需要将我们的词典存储为一个 JSON 字符串,这实际上并不是为关系数据库而构建的。[1]

随着互联网的普及,关系数据库的另一个问题出现在 90 年代:如何只用一台机器处理万亿字节和千兆字节的数据(通常是 JSON) 关系数据库被设计成垂直扩展:当需要时,为托管数据库的服务器配置更多的 RAM 或 CPU。****

但是有一点,即使最昂贵的机器也不能处理数据库。更好的方法可能是横向扩展:从增加更多的机器,而不是试图让你的一台机器更强大。这不仅更便宜——万一你的一台机器出了故障,它还能增强恢复能力。(事实上,使用廉价硬件的分布式计算是谷歌从一开始就使用的策略。)****

谷歌幻灯片作者的杰作

NoSQL 数据库通过允许嵌套或可变类型数据并运行在分布式机器集群上来满足这些需求。 [2]这种设计使他们能够轻松存储和查询大量非结构化数据(即非表格数据),即使记录的形状完全不同。穆罕默德有一份 50 页的整洁有序的爱好和子爱好的 JSON,而杰里只喜欢园艺?没问题。

但是,当然,这种设计也有它的缺点,否则我们就都换了。缺少数据库模式意味着没有什么可以阻止用户写入垃圾(例如,意外地将“name”字符串写入“grade”字段),并且很难在数据库的不同集合之间连接数据。使用分布式服务器还意味着查询可能会在更新同步之前返回过时的数据。[3]那么,在 SQL 和 NoSQL 数据库之间的正确选择取决于你愿意处理哪一个缺点。

游戏时间

够理论;让我们用 Python 实际创建每种类型的数据库。我们将使用sqlalchemy库创建一个简单的 SQLite 数据库,并且我们将使用pymongo创建一个 MongoDB NoSQL 数据库。确保安装sqlalchemypymongo来运行下面的代码,以及启动一个 MongoDB 服务器。

结构化查询语言

在专业环境中,我们可能希望通过专用的 RDBMS 用实际的 SQL 代码创建一个数据库。但是对于我们这里简单的概念证明,我们将使用 SQLAlchemy 。

SQLAlchemy 允许您通过一个 对象关系映射器 来创建、修改 Python 中的关系数据库并与之交互。概括起来,主要思想是 **SQLAlchemy 使用 Python 类来表示数据库表Python 类的实例可以被认为是表的*行。*

我们首先加载必要的sqlalchemy类。第 1 行中的导入用于建立到数据库的连接(create_engine)并定义我们的表的模式(ColumnStringIntegerForeignKey)。下一个导入,Session,让我们读写数据库。最后,我们使用declarative_base创建一个模板 Python 类,它的子类将被映射到 SQLAlchemy 表。

现在,我们将教室、学生和年级表创建为 Python 类ClassroomStudentGrade。注意,它们都继承自Base SQLALchemy 类。我们的类很简单:它们只定义相应的表名及其列。

现在我们创建数据库和表。create_engine启动一个 SQLite 数据库,[4]然后我们打开它。第 4 行开始我们的会话,第 7 行从我们的 Python 类创建数据库表。

我们现在生成我们的数据。ClassroomStudentGrade的实例作为每个表中的行。

现在,我们终于将数据写入数据库。与 Git 类似,我们使用session.add将每一行添加到暂存区,使用session.commit实际写入数据。

干得好!让我们通过重新创建本文中的第一个表来结束这一部分。

NoSQL

现在我们切换到 MongoDB。确保安装 MongoDB 并且启动一个本地服务器。下面,我们导入pymongo,连接到我们的本地服务器,然后创建一个名为classDB的数据库。

在关系数据库中,我们用 记录 创建 。在 NoSQL 数据库中,我们用 文档 创建 集合 。下面,我们创建一个名为classroom的集合,并插入包含每个学生信息的字典。

注意hobbies中的数据类型是如何不同的:对 Jerry 来说是一个字符串,对 Muhammed 来说是一个字典。(即使在hobbies字典中,值也是列表和字符串。)MongoDB 不在乎——没有模式来强制数据的数据类型或结构。

我们可以通过迭代从db.classroom.find返回的对象来查看我们的数据。我们使用pprint使读取输出更容易。注意 MongoDB 是如何给每个文档添加一个惟一的对象 ID 的。

我们可以很容易地添加带有新字段的文档。

最后,让我们执行两个查询来突出我们的 MongoDB 数据库与 SQLite 的不同之处。第一个查询通过查找包含一个birthday字段的所有文档来弥补数据库模式的不足。第二个查询搜索在hobbies的嵌套字段exercise中包含running的文档。

结论

这篇文章讲述了 SQL 数据库和 T2 NoSQL 数据库的历史以及它们的优缺点。然后,我们通过用 Python 和 SQLAlchemy 和 MongoDB 创建每种类型的数据库,使事情变得更加具体。我强烈推荐您接触一些相当理论化的概念,如 SQL 与 NoSQL 之战——我从撰写本文的代码中学到了很多!请关注未来的一篇文章,其中有一些编码错误,我将探讨 SQLite 和 MongoDB 的一些限制。

最好,
马特

脚注

1.NoSQL 数据库

这个关于栈溢出的精彩回答解释了关系数据库与 JSON 的斗争有两个主要原因:

  1. ****关系数据库假设其中的数据是规范化的。与 JSON 键相比,查询规划器在查看列时得到了更好的优化。
  2. ****不能在 JSON 键上创建表之间的外键。要关联两个表,必须使用列中的值,这里是行中的整个 JSON,而不是 JSON 中的键。

2.NoSQL 数据库

我发现很难对 NoSQL 数据库如何存储数据有一个简短的描述,因为有多种 NoSQL 数据库类型。键值数据库对其数据几乎没有任何限制,文档数据库对其内容的格式有基本的假设(例如 XML 与 JSON),而图形数据库对节点和边的存储方式可能有严格的要求。同时,面向列的数据库实际上由表组成,但是数据是按列而不是按行组织的。

3.NoSQL 数据库

因为 NoSQL 数据库分布在多台服务器上,所以在一台服务器上的数据库更改反映到所有其他服务器上之前,会有一点延迟。这通常不是问题,但是您可以想象一个场景,在所有服务器都知道第一次取款之前,有人能够从一个银行帐户中取款两次。

4.结构化查询语言

SQLAlchemy 的一个非常好的特性是它如何为您处理 SQL 语法的差异。在这篇文章中,我们使用了 SQLite 数据库,但是我们可以切换到 MySQL 或 Postgres,只需改变我们传递给create_engine的字符串。所有其他代码保持不变。编写使用 SQLAlchemy 的 Python 应用程序的一个很好的策略是在编写和调试时从 SQLite 开始,然后在部署时切换到生产就绪的东西,如 Postgres。

熊猫数据帧“分类”实践指南

原文:https://towardsdatascience.com/a-hands-on-guide-to-sorting-dataframes-in-pandas-384996ca6bb8?source=collection_archive---------18-----------------------

熊猫中数据排序的十种方法

freepik 创作的健康照片

我与熊猫的图书馆的幽会还在继续。最近,我一直试图更深入地研究这个库,并在字节大小的文章中巩固一些熊猫的特征。我写过关于在使用 pandas 时减少内存使用的文章,将 XML 文件转换成 pandas dataframe 的文章,以及开始使用 pandas 中的时间序列的文章等等。在本文中,我将触及数据分析的一个非常有用的方面,那就是排序。我们将从一个简短的介绍开始,然后快速跳转到一些在熊猫中有效执行排序的方法。

整理

如果您是 excel 用户,当您打开电子表格时,首先要做的事情之一就是根据一列(或多列)对它们进行升序或降序排序。从技术上讲,排序是一种根据元素的等级对元素进行排序的方式。有完整的文档专门用于编程领域的各种排序算法。下面是一些常用的排序算法。

不同的排序算法|按作者排序的图片

不同的排序算法具有不同的排序数据的基本原理。例如,在冒泡排序中,算法将一个元素与其相邻元素进行比较,并以期望的顺序交换它们。另一方面,合并排序基于分治技术。在选择排序中,无序数组首先被分成相等的两半,然后以排序的方式组合。解释所有这些算法的复杂性超出了本文的范围,但是如果它引起了你的兴趣,这里有一篇很棒的文章用很好的可视化解释了这个概念。

熊猫的分类

现在让我们看看如何在 pandas 中执行排序。为此,我将使用一个非常有趣的数据集,其中包含一个排名前一百的 Github 库**。**公开在 Kaggle 上有售。数据集每天更新,由其他属性组成,如几个分支、项目描述、语言,甚至项目描述。让我们加载数据集,看看它的结构。

注:您可以使用下面的笔记本跟随:

https://www.kaggle.com/parulpandey/a-quick-guide-to-sorting-in-pandas

df = pd.read_csv('Most starred Github Repositories.csv')
df.head()

作者图片

让我们快速浏览一下数据集的各个列:

  • 项目名称:Github 中仓库的名称
  • Stars: 对知识库的一个书签或赞赏的展示。
  • **Forks:**fork 是您管理的存储库的副本。
  • **语言:**项目使用的主要编程语言
  • **未解决的问题:**问题是建议的改进、任务或者与存储库相关的问题。尚未解决的问题被标记为未决问题。
  • 描述:详述项目目的的一段文字。
  • **最后一次提交:**提交,或“修订”,是对一个文件(或一组文件)的单独更改。此字段存储上次提交的日期和时间。

注:以上所有定义均取自 Github 词汇表。

当前数据集按照恒星 ⭐️的数量排序,即恒星数量最多的项目排在最前面,依此类推。Pandas 支持三种排序:按索引标签排序、按列值排序以及两者的组合排序。现在,让我们通过一些示例来看看对此数据集进行排序的不同方式:

1.按单列排序

熊猫里面用来排序的函数叫做[**DataFrame.sort_values(**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sort_index.html#pandas.DataFrame.sort_index)**)**。它用于根据列或行值对DataFrame进行排序。让我们按照分叉列对数据集进行排序。

 forks = df.sort_values(by='Forks',ascending=False)
forks.head(10)

按单列排序|按作者排序图像

函数dataframe.sort_values带有很多参数。在我们阅读这篇文章的过程中,我们将会谈到几个重要的问题。在上面的例子中,我们遇到了其中的两个:

  • by :可选的by参数用于指定用于确定排序顺序的列。
  • 升序:指定数据帧是按升序还是降序排序。默认值是升序。要按降序排序,我们需要指定ascending=False

2.按多列排序

Pandas 还可以在多个列上对数据集进行排序。简单地说,在sort_values函数中传递所需列名的列表,如下所示:

df.sort_values(by=['Open Issues','Stars']).head(10)

按多列排序|按作者排序图像

在上面的例子中,我们已经根据项目的open issues号和stars号对数据帧进行了排序。请注意,默认情况下,排序是按升序进行的。

3.按具有不同排序顺序的多列排序

当按多列排序时,也可以为不同的列传递不同的排序顺序。

df.sort_values(by=['Open Issues', 'Stars'],
        ascending=[False, True]).head(10)

按不同排序顺序的多列排序|按作者排序的图片

在上面的例子中,dataframe 将首先在Open Issues列按升序排序,然后在Stars列按降序排序。

4.按索引排序

对数据帧进行排序的另一种方式是通过索引。在第 1 节中,我们创建了一个名为 forks 的数据帧。这只是原始数据帧的另一个版本,已经在Forks列上进行了排序。数据帧如下所示:

按作者在“分叉”列|图像上排序的数据集

显而易见,索引是无序的。我们可以使用dataframe.sort_index()函数对其进行排序。

forks.sort_index()

按索引排序|按作者排序

或者,您可以通过将上面函数中的参数传入ascending=False来对索引进行降序排序。

5。排序时忽略索引

在对数据帧进行排序时,也可以完全忽略索引列。这导致从 0 到 n-1 标记的索引,其中 n 指的是观察的数量。

df.sort_values(by='Forks',ascending=False, ignore_index=True).head()

按作者排序的索引|图像

相反,如果ignore_index未设置为真(或默认),则得到的排序数据帧应该是:

未排序索引|按作者排序的图片

6.选择排序算法

我们在开始时触及了不同排序算法的主题。默认情况下,sort_values使用快速排序算法。但是,我们可以使用 kind 参数在 **' 快速排序、【mergesort】和【heap sort】**算法之间进行选择。请记住,该选项仅在对单个列或标签进行排序时适用。

df.sort_values(by='Forks', kind='mergesort')

7.按列名排序

此外,我们还可以使用 sort_index()函数使用列名而不是行对数据帧进行排序。为此,我们需要将轴参数设置为 1。

df.sort_index(axis=1).head(5)

按列名排序|按作者排序图像

以上各栏已按字母升序排列。通过设置ascending=False,也可以降序排序。

8.就地执行操作

通过将inplace参数设置为True,所有的分拣操作都在原位完成。这意味着现有的数据帧被修改。当**inplace** = False操作发生在数据帧的一个副本上时,该副本被返回。原始数据帧保持不变。

df.sort_values(by='Forks', inplace=True)

9.处理缺失值

数据通常包含空值。使用na_position作为开头或结尾,在sort_values()函数中,我们可以选择将 NaNs 放在开头或结尾。

df.sort_values(by='Forks', na_position='first') #NaN placed first
df.sort_values(by='Forks', na_position='last') #NaN placed in the end

10.排序前对值应用 key 函数

我们还可以在排序前对值应用一个键函数。该函数需要一个Series并返回一个与输入形状相同的序列。它将独立应用于每一列。在下面的例子中,我们首先将列Project Name转换成小写,然后对该列上的数据帧进行排序

df.sort_values(by='Project Name',key=lambda col: col.str.lower())[:5]

在按作者排序|图像之前,对值应用 key 函数

结论和额外资源

在本文中,我们研究了使用 pandas 库对数据帧进行排序的不同方法。我们研究了 sort_values()和 sort_index()函数及其参数的用法。如果你想更深入地了解细节,官方文档是一个很好的资源。

新手使用倾向评分的实践介绍

原文:https://towardsdatascience.com/a-hands-on-introduction-to-propensity-score-use-for-beginners-856302b632ac?source=collection_archive---------2-----------------------

实践教程

做中学

照片由纳丁·马里奥在 Unsplash 上拍摄

这是与阿莱克斯·鲁伊斯、赫苏斯·塞奎德斯、霍安·卡普德维拉和博尔哈·维拉斯科在因果 ALGO Bcn 内的联合作品。你可以在这里找到一篇解释相同主题的理论文章。

任何领域都需要解决一个常见的数据问题,这涉及到弄清楚治疗是否对某个结果有任何影响。乍一看,这似乎没有那么难。但是对于任何掉进研究因果关系的兔子洞的人来说,很明显事实并非如此。从随机对照试验(RCT)数据库计算这些效应与从观察数据集计算是不同的。因为低维或高维协变量研究的问题也不一样。

任何对因果推理有所了解的人都可能会对这个条目感兴趣,并希望通过一个代码示例来了解它的工作情况,在这个示例中,我们将回顾一些相对简单的方法来计算来自观察数据集的平均治疗效果(ATE)。

现在,让我们通过一个实际操作的 Python 代码示例来开始这段旅程。你可以在 Github 中找到 Jesús Cerquides 的原始笔记本和数据集这里。

我们从一家医院的历史记录中得到这个数据库:

有人问我们:

如果我们决定从现在开始治疗每一个人,与不治疗任何人相比,死亡人数会有多少变化?

我们的第一直觉是,利用给定的数据,简单地计算出接受治疗的人群相对于未接受治疗的人群的死亡百分比,然后简单地将两者相减。

这个计算让我们得出以下答案:如果我们决定治疗所有人,死亡率将增加 8.5%(将从 23.56%跃升至 32.08%)。

这个结果会告诉我们,我们不应该对待我们的人口,但是我们的常识可能会“刺痛”并使我们怀疑是否真的是这样。这是理所当然的,因为我们忽略了一个微小但非常重要的细节,那就是治疗在吸烟人群和非吸烟人群中的分布并不均匀。换句话说,治疗不是随机的,因此,这种计算 ATE 的方法是完全错误的。

定义这种现象的另一种方式是用“混杂”这个词。基本上,这个词的意思是有一个变量或一组变量(混杂因素)以某种方式影响接受治疗的概率,同时也影响结果。

理想情况下,如果我们想评估某种治疗对我们认为的结果的影响,我们应该避免任何类型的混淆效应。我们可以通过设计一个随机分配治疗的 RCT 来做到这一点,因此混杂因素对它的分布没有影响。但这里的问题是,这样做并不总是道德的。例如,如果我们想评估吸烟(“治疗”)对哮喘青少年肺部健康(结果)的影响。为了做一项 RCT 研究,我们应该找一些青少年,随机分配一半人强制吸烟。显然,这是不道德的,也是绝对不应该做的。然而,我们可以检索患有哮喘的青少年的数据,并记录他们是否吸烟,他们的肺部健康等级是什么,特别是任何可能影响任何青少年吸烟机会的变量。在这个例子中,一个明显的可能混杂因素是社会经济地位,因为低地位会增加不稳定的熟悉环境和更多暴露于不良习惯的机会(影响“治疗”),并且它肯定会通过减少获得良好健康计划的机会来影响青少年的肺部健康(结果)。这就是我们所说的观察数据集,我们知道有一组混杂因素在起作用。在这个问题上的专家知识是尝试包括所有可能的混杂因素的关键,这样我们可以使用因果推断技术更好地估计治疗对结果的影响。

如开头所述,在本条目中,我们将首先使用 Pearl 的调整公式[1]介绍一些简单的方法;然后是倾向得分,应用 Rosenbaum 和 Rubin 在他们 1983 年的著名论文[2]中介绍的两种方法。

调整公式

如果你读过或细读了 Judea Pearl 的书《统计学中的因果推断:初级读本》,你肯定知道并且喜欢这个调整公式(如果你没有读过并且对这个领域感兴趣,现在就去读吧!).也许你是通过另一种方式知道的。以防你还不了解它,让我把它呈现给你:

其中 Y 是结果,X 是治疗,Z 是协变量或混杂变量。现在,我不打算进入这个公式是如何形成的或它的更正式的细节,因为这不是本文的目的。只要知道表达式 P(Y|do(X))指的是 Y(结果)的概率,如果我们对所有人群进行 X(治疗)就足够了。根据这个定义,我们可以用下面的方法计算 ate:

因此,澄清了这一点,让我们回到我们最初的例子。我们展示的这个数据集实际上是伪造的。使用贝叶斯网络,我们指定了我们的 3 个变量(吸烟者,治疗,死亡)之间的依赖关系,并生成了一个数据集。

由于我们确切地知道每个变量的所有真实概率及其所有可能的组合,我们可以应用该公式来计算如果我们治疗每个人死亡的确切概率,以及如果我们不治疗任何人死亡的概率。

现在,有了这两个结果,我们可以计算平均治疗效果(ate)。

这种治疗的真正效果是降低了 8.3%的死亡率,与我们第一次计算的结果正好相反。所以,真正的答案是,我们绝对要对待所有的患者。现在,有这样精确的百分比是非常罕见的。我们当然可以用一个好的观测数据集,应用相同的系统来近似 ate,但是当数据集有一个高维的协变量集时,这个方法的问题就出现了。想象一组数百个协变量。为所有协变量计算这个公式太麻烦了。这不是一个非常简单的例子,但在现实世界中,这是一个非常常见的情况。在这种情况下,我们应该怎么做?

作为平衡得分的治疗倾向

这就是 Rosenbaum 和 Rubin 在 1983 年试图解决的问题,他们建议我们使用倾向得分(或在给定一组协变量的情况下获得治疗的概率)作为平衡得分。他们的推理如下。平衡分数是一组协变量的任何函数,它捕获了依赖于治疗的一组协变量的所有信息。这样的平衡分数将允许我们以相对简单的方式模拟混杂因素和治疗之间的关系。平衡分数的最小表达式是倾向分数。

计算倾向得分相对简单,即使在高维的协变量集合中也是如此。在这种情况下,我们能做的是使用逻辑回归建模,将治疗作为目标变量。但是为了能够在我们接下来要讨论的方法中使用这个倾向分数,有一些限制。

在因果推理中,我们会发现一个共同的主题,那就是非发现条件。这意味着,从根本上说,为了能够使用观察数据库对 ATE 值进行合理的近似,我们必须考虑所有可能作为混杂因素的变量。

为了确保这一点,形式上需要满足 2 个假设:

稳定单位治疗值假设(SUTVA):样本中任何单位的任何结果都独立于其他单位的治疗分配。

在给定一组协变量的情况下,治疗分配应该是非常可忽略的:如果样本的每个单位都有机会(即使很小)接受每种治疗,并且如果治疗分配和结果在给定协变量的情况下是条件独立的。

如果满足这两个假设,我们就可以使用我们现在要复习的方法了。

事不宜迟,我们来看看它们。

倾向得分配对匹配

和以前一样,我们将回顾把它们应用到我们的具体例子中的方法。如前所述,我们能够计算准确的 ATE,因为我们知道每个变量组合的准确概率。这些方法假设我们不知道它们,因为对于高维协变量集,这几乎是不可能的。因此,我们将比较他们对 ATE 值的估计和已知的真实结果。

让我们从计算我们的倾向分值开始。其正式定义如下:

其中 x 是协变量集合的特定组合, z = 1 等同于接受治疗。

在我们的具体案例中,它被翻译为:

现在我们计算每个病人的倾向:

在这种情况下,只有 2 个可能的值,因为我们的混杂因素是二元的。一旦计算并添加到我们的数据帧中,我们可以用两种不同的方式配对。

配对版本 1

在这个版本的配对中,我们将每个接受治疗的患者与具有相同倾向评分的对照患者配对。在这个例子中,出于效率原因,我们减少了接受治疗的患者数量。

现在,每个接受治疗的患者都有一个随机抽样的未治疗患者,具有相同的倾向评分:

有了匹配对,我们可以连接两个数据框,并简单地计算治疗和未治疗结果之间的平均差异:

我们得到的结果是,死亡百分比被有效地估计为下降,这是好的,因为它与之前计算的实际影响一致。但是,我们知道真实的 ATE 是 0.083。这个结果明显有失偏颇。

让我们来看看配对的第二个版本。

配对版本 2

我们先来看看我们的倾向得分的分布:

很明显,我们有大多数倾向得分低的患者。

我们可以将患者分为两组,高倾向(> 0.5)和低倾向(< =0.5):

现在,我们建立了一个配对样本,但与之前不同的是,治疗和未治疗的人群都是从高或低倾向得分人群中随机抽样的。

在为这个新的配对数据集计算 ATE 的值之后,我们可以看到该值比第一个版本的配对匹配的偏差小得多。

我们能做得更好吗?

次级分类

这里,该方法与配对匹配的第二版本相关,因为它依赖于倾向得分的分布。它的历史是,我们可以细分我们的人口类别的范围分解倾向得分。将我们的总体分类到每个类别中(在本例中只有两个,因为协变量是二元的),我们只需要计算每个子类的 ate,然后应用下一个公式:

这样做后,我们的结果非常非常接近 ATE 的已知真实值。

结论

在本文中,我们回顾了几种相对简单的计算平均治疗效果的方法。首先是经典的调整公式,非常适用于协变量为低维的设置,如我们的示例。然后使用最适合高维协变量集的倾向得分。

在本例中,校正公式为我们提供了 ATE 的精确结果,因为我们已经构建了数据集并知道所需的特定概率。然后,我们能够与倾向得分方法进行比较,即使在这个例子中,最好的方法是子类化,但这并不意味着在任何其他类型的问题中也是如此。我们建议在开始使用一种方法之前,先测试不同的方法,包括这里介绍的方法和其他没有在这里讨论的方法。

参考资料:

[1]j .珀尔、m .格里穆尔和 n .朱厄尔,《统计学中的因果推断:入门》。(2016).威利。

[2]罗森鲍姆和鲁宾博士(1983 年)。倾向评分在因果效应观察研究中的核心作用。 Biometrika, 70 (1),41–55。doi:10.2307/2335942

dbt 实践教程

原文:https://towardsdatascience.com/a-hands-on-tutorial-for-dbt-f749f7749c8d?source=collection_archive---------21-----------------------

入门指南

通过只编写 SQL SELECT 语句,将 dbt 用于复杂数据转换。运行测试,生成文档,使用宏,以及更多的功能。

本帖中使用的所有图片/视频均由作者创作,除非另有说明。(图标来自https://flaticon.com)

dbt(数据构建工具)是一个使用 select SQL 语句的数据转换工具。它允许您创建复杂的模型,使用变量和宏(也称为函数),运行测试,生成文档,以及更多的特性。

dbt 不提取或加载数据,但是它在转换数据库中已经可用的数据方面很强大——dbt 在 ELT(提取、加载、转换)过程中执行 T。

在这篇文章中,你将学会如何…

  • 配置 dbt 项目
  • 创建 dbt 模型(SELECT 语句)
  • 使用全局变量和宏构建复杂的 dbt 模型
  • 参考其他 dbt 模型构建复杂模型
  • 运行测试
  • 生成文档

先决条件

注册

你可以在 getdbt.com 报名。免费计划对于小型项目和测试来说是一个很好的计划。

具有填充数据的数据库

你可以查看我关于如何在 Heroku 上部署一个免费* PostgreSQL 数据库的帖子。这篇文章提供了如何做的一步一步的指导。*

您还可以查看本文附带的 GitHub repo 中的数据摄取脚本。

*https://github.com/e-alizadeh/sample_dbt_project

按照上面所述,我们已经在 PostgreSQL 数据库中生成了两个表,我们将在本文中使用它们。数据库中有两个表,分别是covid_latestpopulation_prosperity。您可以在 GitHub repo 上找到这篇文章的摄取脚本。

dbt CLI 安装

您可以按照下面的 dbt 文档页面上的说明来安装 dbt 命令行界面(CLI)。

https://docs.getdbt.com/dbt-cli/installation

dbt 项目的基础

为了使用 dbt 工具,有三件主要的事情需要了解。

  • dbt 项目
  • 数据库连接
  • dbt 命令

dbt 怎么用?

dbt 项目是包含.sql.yml文件的目录。最少需要的文件是:

  • 名为dbt_project.yml的项目文件:该文件包含 dbt 项目的配置。
  • 模型(.sql文件):dbt 中的模型仅仅是一个.sql文件,包含一个单个 **select** 语句

每个 dbt 项目都需要一个 dbt_project.yml 文件——这是 dbt 知道一个目录是 dbt 项目的方式。它还包含告诉 dbt 如何操作您的项目的重要信息。

你可以在这里找到更多关于 dbt 项目的信息。

💡dbt 模型基本上是一个带有 SELECT 语句的.sql文件。

dbt 命令

dbt 命令以dbt开始,可通过以下方式之一执行:

  • dbt Cloud(dbt Cloud 仪表板底部的命令部分),
  • dbt CLI

有些命令只能在 dbt CLI 中使用,如dbt init。我们将在本文中使用的一些 dbt 命令是

  • dbt init(仅在 dbt CLI 中)
  • dbt run
  • dbt test
  • dbt docs generate

dbt 项目设置

步骤 1:使用 dbt CLI 初始化 dbt 项目(示例文件)

您可以使用[dbt init](https://docs.getdbt.com/reference/commands/init)生成样本文件/文件夹。特别是,dbt init project_name将创建以下内容:

  • 一个~/.dbt/profiles.yml文件(如果不存在)
  • 名为[project_name]的新文件夹
  • dbt 入门所需的目录和示例文件

注意:由于dbt init会生成一个名为project_name的目录,为了避免任何冲突,您应该没有任何同名的文件夹。

dbt 初始化<project_name></project_name>

结果是一个包含以下示例文件的目录。

sample_dbt_project
├── README.md
├── analysis
├── data
├── dbt_project.yml
├── macros
├── models
│   └── example
│       ├── my_first_dbt_model.sql
│       ├── my_second_dbt_model.sql
│       └── schema.yml
├── snapshots
└── tests

对于这篇文章,我们将只考虑最少的文件,并删除多余的东西。

sample_dbt_project
├── README.md
├── dbt_project.yml
└── models
    ├── my_first_dbt_model.sql
    ├── my_second_dbt_model.sql
    └── schema.yml

步骤 2:建立一个 Git 存储库

您可以使用设置期间指定的现有回购协议。您可以按照这里的 dbt 文档来配置存储库。

或者,如果你想创建一个新的回购…

您可以从创建的目录中创建新的存储库。你可以像下面这样做

git init
git add .
git commit -m "first commit"
git remote add origing <repo_url>
git push -u origin master

步骤 3:在 dbt Cloud Dashboard 上建立一个新项目

在上一步中,我们创建了一个包含示例模型和配置的示例 dbt 项目。现在,我们想要创建一个新项目,并在 dbt Cloud 仪表板上连接我们的数据库和存储库。

在我们继续之前,您应该已经

  • 一些数据已经存在于数据库中,
  • 包含上一步生成的文件的存储库

您可以按照下面的步骤在 dbt Cloud 中设置一个新项目(请记住,这一步与上一步不同,因为我们只生成了一些示例文件)。

在 dbt Cloud 上建立一个新的 dbt 项目

我们项目的dbt_project.yml文件如下所示(你可以在这篇文章附带的 GitHub repo 中找到完整版本)。

dbt_project.yml

dbt 型号和功能

dbt 模型

让我们创建简单的 dbt 模型来检索表中的几列。

covid 19 _ latest _ statsdbt 模型(models/covid 19 _ latest _ stats . SQL)

人口 dbt 模型(models/population.sql)

⚠️ 注意:dbt 模型名是models目录中 sql 文件的文件名。模型名称可能不同于数据库中的表名称。例如,在上面的例子中,dbt 模型population是数据库中population_prosperity表上的SELECT语句的结果。

运行模型

您可以通过执行dbt run来运行您的 dbt 项目中的所有模型。下面显示了一个示例 dbt 运行输出。您可以看到运行所有 dbt 模型的摘要或详细日志。这对于调试您在查询中可能遇到的任何问题非常有帮助。例如,您可以看到一个失败的模型抛出了一个 Postgres 错误。

失败的详细日志 jinja _ and _ variable _ usagedbt 模型

金佳和宏

dbt 使用 Jinja 模板语言,使得 dbt 项目成为 SQL 的理想编程环境。使用 Jinja,您可以进行 SQL 中通常不可能的转换,比如使用环境变量或宏——SQL 的抽象片段,类似于大多数编程语言中的函数。每当你看到一个{{ ... }},你已经在用 Jinja 了。关于 Jinja 和定义的附加 Jinja 风格函数的更多信息,可以查看 dbt 文档。

在这篇文章的后面,我们将讨论由 dbt 定义的自定义宏。

使用变量

定义一个变量

您可以在您的dbt_project.yml中的vars部分下定义您的变量。例如,让我们定义一个名为selected_country的变量,其默认值为USA,另一个名为selected_year的变量,其默认值为2019

带有已定义变量的 dbt_project.yml 文件

使用变量

您可以通过[var()](https://docs.getdbt.com/reference/dbt-jinja-functions/var) Jinja 函数({{ var("var_key_name") }})在您的 dbt 模型中使用变量。

宏指令

dbt_utils中有许多有用的转换和宏可以用在你的项目中。关于所有可用宏的列表,你可以查看他们的 GitHub repo 。

现在,让我们将 dbt_utils 添加到我们的项目中,并按照以下步骤进行安装:

  1. 将 dbt_utils 宏添加到您的packages.yml文件,如下所示:

dbt_utils 包添加到 packages.yml

2.运行dbt deps安装软件包。

使用 dbt deps 安装软件包

复杂 dbt 模型

模型(选择)通常堆叠在另一个之上。为了构建更复杂的模型,你必须使用[ref()](https://docs.getdbt.com/reference/dbt-jinja-functions/ref)宏。ref()是 dbt 中最重要的功能,因为它允许您引用其他模型。例如,您可能有一个做多种事情的模型(又名 SELECT query ),而您不想在其他模型中使用它。如果不使用前面介绍的宏,将很难构建一个复杂的模型。

使用ref()和全局变量的 dbt 模型

我们可以使用本文前面定义的两个 dbt 模型构建更复杂的模型。例如,让我们创建一个新的 dbt 模型,它根据国家代码连接上述两个表,然后根据选定的国家和年份进行过滤。

jinja _ and _ variable _ usagedbt 模型(models/jinja _ and _ variable _ usage . SQL)

关于上述查询的几点:

  • {{ref('dbt_model_name')}}用于指项目中可用的 dbt 型号。
  • 你可以从像{{ref('dbt_model_name')}}.column_name这样的模型中得到一个专栏。
  • 您可以通过{{var("variable_name)}}使用dbt_project.yml文件中定义的变量。

上面的代码片段将来自populationcovid19_latest_stats型号的数据连接到国家代码上,并根据 selected_country=USA 和 selected_year=2019 进行筛选。模型的输出如下所示。

jinja _ and _ variable _ usagedbt 模型的输出

您也可以点击编译 sql 按钮查看编译后的 SQL 代码片段。这非常有用,特别是如果您想在 dbt 工具之外运行查询的话。

jinja _ and _ variable _ usagedbt 模型编译的 SQL 代码

使用 dbt_utils 包和宏的 dbt 模型

dbt_utils包中包含可以在 dbt 项目中使用的宏(又名函数)。所有宏的列表可以在 dbt_utils 的 GitHub 页面上找到。

让我们在 dbt 模型中使用 dbt_utils [pivot()](https://github.com/dbt-labs/dbt-utils/#pivot-source)[get_column_values()](https://github.com/dbt-labs/dbt-utils/#get_column_values-source)宏,如下所示:

使用 _dbt_utils_macros dbt 模型(models/using _ dbt _ utils _ macros . SQL)

上面的 dbt 模型将在 dbt 中编译成下面的 SQL 查询。

使用 _dbt_utils_macros 从编译 SQL 查询 dbt 模型

在 dbt 中运行测试

使用 dbt 的另一个好处是能够测试数据。开箱即用,dbt 有以下通用测试:uniquenot_nullaccepted_valuesrelationships。对模型进行这些测试的示例如下所示:

schema.yml (dbt 测试)

你可以通过dbt test运行测试。您可以在下面看到输出

在 dbt 云仪表板上运行 dbt 测试的结果

关于 dbt 测试的更多信息,您可以访问 dbt 文档。

在 dbt 中生成文档

您可以通过简单地在命令部分运行dbt docs generate来为您的 dbt 项目生成文档,如下所示:

为 dbt 项目生成文档

您可以通过单击查看文档来浏览生成的文档。您可以在这里看到生成的文档的概述。

命令生成的文档 dbt docs 生成

除了dbt docs generate之外,dbt docs 还可以为 web 服务器提供生成的文档。为此,您只需运行dbt docs serve。更多关于为你的 dbt 项目生成文档的信息可以在这里找到。

其他功能

使用钩子和操作的数据库管理

有些数据库管理任务需要运行额外的 SQL 查询,例如:

  • 创建用户定义的函数
  • 授予对表的特权
  • 还有更多

dbt 有两个接口(钩子和操作)来执行这些任务,重要的是对它们进行版本控制。这里简单介绍一下钩子和操作。有关更多信息,您可以查看 dbt 文档。

钩住

钩子只是在不同时间执行的 SQL 片段。钩子在dbt_project.yml文件中定义。不同的挂钩有:

  • pre-hook:模型建立前执行
  • post-hook:模型建立后执行
  • on-run-start:在dbt run开始时执行
  • on-run-end:在dbt run结束时执行

操作

操作是在不运行模型的情况下调用宏的一种便捷方式。使用[dbt run-operation](https://docs.getdbt.com/reference/commands/run-operation)命令触发操作。

注意,与钩子不同,您需要在一个 dbt 操作中显式执行 SQL。

结论

dbt 是一个很好的工具,绝对值得一试,因为它可以简化您的数据 ELT(或 ETL)管道。在这篇文章中,我们学习了如何设置和使用 dbt 进行数据转换。我向您介绍了该工具的不同特性。特别是,我提供了一个分步指南

  • 配置 dbt 项目
  • 创建 dbt 模型(SELECT 语句)
  • 使用全局变量和宏构建复杂的 dbt 模型
  • 参考其他 dbt 模型构建复杂模型
  • 运行测试
  • 生成文档

您可以在下面找到包含所有脚本(包括数据摄取脚本)的 GitHub repo。

https://github.com/e-alizadeh/sample_dbt_project

感谢阅读🙏

加入我的邮件列表接收类似的帖子 。也可以关注我上 LinkedInTwitter。**

有用的链接

*

参考

https://docs.getdbt.com/docs/introduction

原载于https://ealizadeh.com**

Google Cloud 上基于 ML 的 Web 应用程序持续部署管道的实践教程

原文:https://towardsdatascience.com/a-hands-on-tutorial-to-continuous-deployment-pipelines-for-ml-based-web-apps-on-google-cloud-ba3957c87bc1?source=collection_archive---------17-----------------------

实践教程

使用 Flask 部署 ML 应用程序的综合指南

机器学习(ML)模型通常利用从以前看到的(训练)数据中学习模式的能力,并应用它们来预测新(测试)数据的结果。通过允许访问多个用户和测试环境,将 ML 模型部署为 web 应用程序可以帮助测试经过训练的 ML 模型的功效,从而收集测试性能指标。然而,ML web 应用程序在生产中的部署可能是一个非常复杂的过程,可能需要确保在应用程序更新过程中用户的停机时间最短。Google Cloud Platform (GCP)等基于云的部署解决方案通过管道和触发器高度简化了持续集成和持续部署(CI/CD)的流程,这些管道和触发器可以确保完整性检查以及更新应用程序运行的集成代码库的完整性。

在本教程中,我们将回顾一个详细的示例,其中我们通过 CD 管道在 GCP 上部署了一个 ML 模型 web 应用程序。要遵循从数据分析到最终应用部署的步骤,请在[1]处分叉 github 存储库,并遵循以下步骤。

第一步:数据和 ML 模型描述

第一步是理解数据,然后是 ML 模型的创建和后续部署。对于这一步,我们参考文件夹 app_files 下的文件 model.py

对于本教程,我们将使用图 1 所示的[2]中的 50_Startup.csv 数据,其目标是使用三个特征(x₁,x₂,x₃)来训练回归模型(f(X))作为( R & D 支出、管理成本、营销成本),并预测公司的'利润 ' (Y)。

图 1:用于回归数据建模的训练数据集的快照。

我们的目标是估计利润作为其特征的函数为 f(x)=g(x₁,x₂,x₃),其中 g(.)分别代表特征 x₁、x₂、x₃的线性或非线性组合,它们可以从训练数据中估计。

虽然线性回归是上述数据集最简单的解决方案,但对于大型数据集而言,最具可扩展性的数据模型之一仍然是基于决策树的集成模型,因为它们支持数据聚合和可扩展性。在这项工作中,我们实现了一个基于非线性决策树的回归模型,如图 2 所示。为了训练和可视化回归模型,运行文件 model.py ,代码片段如下所示。

决策树回归器通过在特征子空间中划分子区域或块来进行训练,使得相似的样本聚集在一起,以最小化估计的残差平方和(RSS)[3]。在测试时,每个样本被投影到分割的特征子空间上,然后从相应的子区域读出平均样本值作为估计结果[3]。图 2 示出了最大深度为 5 的已训练决策树回归模型的非线性特性。下面显示了跨每个特征可视化训练模型的代码。

图 2:决策树回归模型示例,适用于 R&D 成本单一特征的可视化数据集。测试数据集为[16000,135000,450000]。

终端第一步命令:python model.py

第二步:使用 Flask 的 ML Web 应用

构建了 ML 模型后,下一步是将其打包并作为 Web 应用程序。这里我们指的是 app_files 中包含的 templates 文件夹下的文件 app.py 和 html 文件。

我们利用来自[4]的基于 Flask 的 RESTful api。表述性状态转移(REST)是 web API 的标准架构设计,自 2010 年问世以来,Flask 一直是 python web API 服务的选择。设计 Flask-API 的两个主要组件是[5]:

  1. 烧瓶库和 POST HTTP 方法来服务于预测。
  2. 对用户与 ML 模型交互有用的前端 HTML 文件。

与[4–5]中一般基于 Flask 的 python 部署相比,app.py 文件中的一个重要变化是,正在运行的 app.py 启动了一个在 http://0.0.0:8000 上运行的 API,而不是它的默认设置。

这一改变是为了启用一个 Google Cloud Run (GCR ),它监听 http://0.0.0,端口 8080。有关调试 GCR 应用程序的方法的详细信息,请参见[6]。

终端第二步命令 : python app.py

第三步:为谷歌云创建一个 Docker 容器

现在我们有了一个功能性的 web 应用程序,我们需要为 Google Cloud 运行打包它。为此,我们将创建一个 Docker 容器,如[7]所示,它是一个独立的软件,可以安装在任何物理或虚拟机上运行容器化的应用程序。本教程的 Docker 文件,如下所示,必须包括 4 个关键组件。

  1. 应用程序所需的操作系统;
  2. requirements.txt 文件中的库和包等依赖项列表;
  3. 应用程序文件;
  4. 启动应用程序的命令。

步骤 4:为云运行创建一个 YAML 文件

运行 CD 管道所需的最后一个组件是一个基于[7]中的应用程序的 YAML Ain 标记语言文件。YAML 文件遵循数据序列化标准来构建 CD 管道的配置设置。关于为你的应用构建 YAML 文件的细节可以在[8]找到。下面显示的这个教程的 YAML 文件包含 4 个步骤。

  1. 如果 docker pull 请求失败,那么将执行 exit 0,即不会显示任何错误消息,构建过程将继续最后一次构建。
  2. 如果目前没有构建,则创建一个构建,否则使用缓存构建。
  3. 将 docker 图像推送到 GCR
  4. 将应用程序部署到 GCR。

现在我们已经有了运行和部署 ML web 应用程序的所有组件,让我们创建一个触发器来在 Google Cloud 上部署应用程序。

第五步:创建 CD 管道并运行

现在我们有了一个本地运行的应用程序,下一个任务是在 GCP 建立一个项目,该项目将生成触发器和云运行。遵循以下步骤,确保您有一个可行的 Google cloud 项目来开始 CD 流程。

  1. 在谷歌控制台注册:https://console.cloud.google.com/home/
  2. 创建一个新项目,如下所示:

在 GCP 创建项目

3.确保为项目激活“计费”。

4.从 Github 代码开始创建一个 CD 管道。为此,请使用您的 Github 帐户中的分叉 Github 存储库。

5.进入页面:https://console.cloud.google.com/cloud-build/triggers。确保您的项目在顶部面板上被选中。接下来点击底部的“创建触发器”按钮,如下所示。

在 GCP 制造触发器

6.这将把你带到下一页,在这里你需要为你的分叉 Github 库创建一个‘Push to a branch’事件。要部署应用程序,请选择如下所示的“云构建”配置文件选项,并点击底部的“创建”。

使用 Github 代码创建推送触发器的步骤

7.现在创建了一个触发器。点击触发器旁边的运行选项,查看生成的日志。这将需要几分钟的时间来运行和部署。最后,您应该会看到如下所示的内容。

推动触发器运行的结果

8.现在转到位于https://console.cloud.google.com/run的 GCR 控制台,注意你的应用程序(flaskappml)如下所示运行:

在 GCR 部署的应用程序

点击“flaskappml”将带您进入 GCR 页面,网址显示在右上角。这是您部署的 webapp 的 URL,您现在可以在任何设备上测试它。

离开前别忘了清理扳机和 GCR。这对于确保适当的资源使用至关重要。

结论

构建 ML 模型并将它们部署在云平台上不仅增强了可用性,而且还有助于收集对模型限制条件的见解。为了确保部署的 web 应用程序健壮,CI 管道支持以系统的方式将代码提交到存储库中,而 CD 管道支持代码健全性触发和检查,以确保成功的应用程序部署。将本教程扩展到其他 web 应用程序是一个相对简单的过程。这将需要以下 4 个主要变化:

  1. 更新 app_files 以反映您感兴趣的应用。
  2. 更新 Dockerfile 和 requirements.txt 文件来表示应用程序所需的库。
  3. 更新 YAML 文件以包含更多 DevOps 测试案例。
  4. 将所有更新的代码作为持续集成管道提交给 Github 或 Git repo。

现在,您已经拥有了为自己的基于 ML 的用例构建 CI/CD 管道所需的资源。

参考资料:

[1] S .罗伊乔杜里。Flask ML 模型 CD 管道教程【在线】:https://github . com/sohiniroych/Flask-ML-Pipeline _ GCP-Tutorial/

[2] K .维拉库马。启动—多元线性回归。[在线]:https://www . ka ggle . com/karthickveerakumar/startup-logistic-regression

[3]李。决策树分类回归分析[在线]: 决策树分类回归分析| Lorraine Li |走向数据科学

[4] A .贾雷尔。startup:REST API with Flask[Online]:https://www . ka ggle . com/ahammedjaleel/startup-REST-API-with-Flask

5 格林伯格先生。使用 Python 和 Flask 设计 RESTful API 在线]:https://blog . miguelgrinberg . com/post/designing-a-RESTful-API-with-Python-and-Flask

[6]谷歌云。故障排除。https://cloud.google.com/run/docs/troubleshooting

7j .阿劳霍。谷歌云上的机器学习部署管道运行[在线]:https://github.com/jgvaraujo/ml-deployment-on-gcloud

[8]谷歌云。app.yaml 参考[在线]:https://cloud . Google . com/app engine/docs/standard/python/config/appref

关于使用 Power BI 和 DAX 过滤上下文的一个很难的教训

原文:https://towardsdatascience.com/a-hard-lesson-on-filter-context-with-power-bi-and-dax-c0ce5f657af4?source=collection_archive---------17-----------------------

一个客户对他的报告有意见。我被要求找到问题并解决它。这是关于它的故事

上周,一位客户要求我分析 Power BI 报告中显示的总数与导出到 Excel 后的数值总和之间的差异。这项任务被证明是非常具有挑战性的,最终,差异是由于 Power BI 和 DAX 如何工作以及它如何计算数字。简而言之:过滤器上下文是如何工作的。

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

场景

客户是一家销售建筑材料的公司。它有一个订单积压。每天都会计算订单积压中的销售额,并将其加载到数据仓库数据库中。

这个量是所谓的半累加性度量。半累加性意味着不可能累计一段时间的总量,如仓库中的库存或银行账户的状况。

用户选择一个月,报表显示订单编号列表和每个订单对应的销售额。

总计显示所选月份所有订单的销售额。

由于销售订单积压的快照每天都被加载到数据仓库数据库中,因此对于销售额,只需考虑所选月份的最后一天。

问题

用户得到一个订单编号列表,其中包含相应的成本中心和销售额。

其中一个用户希望对数据进行更深入的分析,将 Power BI 报告中的数据导出到 Excel。

当他将所有订单的总和与 Power BI 报告上显示的总和进行比较时,他注意到了一个巨大的差异。

Power BI 与 Excel 的比较

这个结果是我开始调查的出发点。

DAX 衡量标准

客户使用了以下 DAX 衡量标准:

Last Sales Amount = 
 CALCULATE (
 SUM ( ‘SalesOrderBacklog’[SalesAmount] ),
 FILTER (
 ‘SalesOrderBacklog’,
 ‘SalesOrderBacklog’[Snapshot_Date]
 = MAX ( ‘SalesOrderBacklog’[Snapshot_Date] )
 )
 )

乍一看,这个措施看起来还可以。

它只考虑表中最高的日期,计算 SalesAmount 列的总和。

但是当你更深入地研究这个公式时,你开始注意到这个机制与你预期的略有不同:

  1. 您需要一次查看报告中的每一行
  2. 然后,您将看到该度量对订单号和成本中心的每个组合执行一次迭代,并对每个组合执行计算
  3. 您需要将选定的月份作为过滤器

当您结合这些步骤时,您会开始理解该度量正在执行以下操作:

  1. 它得到第一行。假设成本中心 2130 和订单号 781731701 的组合
  2. 该度量将获取该组合在所选月份
    中的最新日期的行。这些行不必是所选月份中的最后一个日期
  3. 选择这些行后,它将计算 SalesAmoun 列的总和,并返回值。在这种情况下,20'078.38

现在,我们需要看看这个度量是如何计算总数的。

该度量不知道在表的“总计”行中考虑了哪个订单号或成本中心。

这意味着该度量将获取所选月份中日期最晚的行。选择之后,它将计算所有这些行的 SalesAmount 列的总和,并返回值,在本例中为:
9'036'744.8601,表示所选月份的最后一天!

计算结果基于与上述行中考虑的行不同的完整行选择。

这个过程就是所有行的总和导致总数为 10'183'398.1177 的原因。

不同的措施

在 DAX,我可以用另一种方法来计算股票价值。我可以使用 LASTNONBLANK()或 LASTNONBLANKVALUE()函数来代替自定义公式。

当使用这些函数之一时,测量看起来完全不同:

Last Sales Amount = 
 VAR LastDateWithData = LASTNONBLANK(‘Date ‘[Date]
 ,SUM ( ‘SalesOrderBacklog’[SalesAmount] )
 )
 RETURN
 CALCULATE(
 SUM ( ‘SalesOrderBacklog’[SalesAmount] )
 ,LastDateWithData
 )

在这里,我采用了两步走的方法:

  1. 我使用 LASTNONBLANK()函数获取最新日期,该日期在 SalesAmount 列中有值。
    这一步考虑所选的月份
  2. 然后,对具有此日期的所有行计算 SalesAmount 列的总和

现在,我使用一个添加到模型中的日期表。但是使用' SalesOrderBacklog '[Snapshot _ Date]不会有任何不同。

使用这种方法,DAX 只计算这些行的结果,其中包含所选月份最后一天的数据。

当我现在导出数据时,当我导出数据并计算所有行的总和时,我得到了正确的结果。

不同的要求

现在,我们需要了解用户的确切需求。

我们可以提出以下两个问题:

  1. 您是否需要获取所选月份
    最后一个月的订单列表,或者
  2. 您是否需要在所选月份打开的所有订单的列表,而不管这些订单在月末是否仍在积压列表中?

如果用户对第一个问题回答是,那么使用 LASTNONBLANK()函数的度量就是正确的。

如果用户对第二个问题回答“是”,那么我们需要更改度量来计算正确的总和,并增加一部分来满足这一要求。

一种方法是预先计算变量中的所有行,并检查当前过滤器上下文是否是总计行:

Sales (Order Backlog) Extd = 
 VAR SelectedMonth = SELECTEDVALUE(‘Date’[YearMonthShort])

 VAR ListOfValues =
 CALCULATETABLE(
 SUMMARIZE(
 ‘SalesOrderBacklog’
 ,’SalesOrderBacklog’[Order No]
 ,’SalesOrderBacklog’[Costcenter]
 ,”SalesOrderBacklog”, [Last Sales Amount Custom]
 )
 ,FILTER(
 ‘Date’
 ,’Date’[YearMonthShort] = [Selected Month]
 )
 )

 RETURN
 IF(OR(HASONEFILTER(‘SalesOrderBacklog’[Order No])
 ,HASONEFILTER(‘SalesOrderBacklog’[CostCenter])
 )
 ,[Last Sales Amount]
 ,SUMX(
 ListOfValues
 ,SUM ( [SalesOrderBacklog] )
 )

 )

这种方法的工作方式如下:

  1. 使用 SELECTEDVALUE()函数检索选定的月份,并将其存储在 selected Month 变量中
  2. 变量 ListOfValues 得到一个包含订单编号和成本中心所有组合的表,以及每个组合的销售额总和
    a。该表受所选月份的限制(FILTER()函数)
  3. IF 函数检查当前过滤器上下文是否包含订单编号或成本中心的值。
    a .如果是,则调用基本度量返回实际行
    的销售额 b .如果否,则计算表变量 ListOfValues 中所有行的总和。

Power BI 计算出正确的逐行结果和正确的总计行。但是它有两个主要缺点:

  1. 性能:非常慢。我等了几分钟,等待超过 200 万行的结果
  2. 不可伸缩:您只能对这个特定的表使用这个度量,因为它只要求 order-number 和 cost-centre 两列。

我的客户决定使用通用的测量方法,而不是上面描述的最后一种方法。

因为他的决定,我没有花任何时间试图优化这个变种。

要吸取的教训

我必须吸取的最大教训是,在对 DAX 测量结果进行故障诊断时,不要忽略关键细节。

照片由 jaikishan patel 在 Unsplash 上拍摄

使用 DAX 时,过滤器上下文是最重要的因素。

用户的问题误导了我,我花了很多时间回到基础去理解是怎么回事。

我希望你觉得这篇文章有趣并且有价值。如果你对这个话题有任何疑问,请发表评论。

一颗隐藏的宝石:df.select_dtypes()

原文:https://towardsdatascience.com/a-hidden-gem-df-select-dtypes-a660db0e8609?source=collection_archive---------21-----------------------

一个关于 2014 年熊猫功能的微帖子一直在我的雷达下飞行。

有多少次你把一个熊猫数据帧输入到另一个库的一个实用函数中,却因为有对象列而失败。也许这是来自 seaborn 的图表?(我们在看你 sns.clustermaps() )。

>>> sns.clustermap(
        df, 
        method='ward', 
        cmap="YlGnBu", 
        standard_scale=1
    )TypeError: unsupported operand type(s) for -: 'str' and 'str'

因此,我构建了一个列的所有数据类型的列表,过滤掉那些非数字列,并将结果数据帧传递给只需要数字列的函数。

>>> numeric_cols = [
        col 
        for col, dtype 
        in df.dtypes.items() 
        if dtype=='float64'
    ]>>> print(numeric_cols)
['col1', 'col2']>>> sns.clustermap(
        df[numeric_cols], 
        method='ward', 
        cmap="YlGnBu", 
        standard_scale=1
    )

来自 E-GEOD-20108 的数据—单倍体和二倍体菌株对乙醇的反应|图片由作者提供

当我昨天在研究一些与酵母中基因表达相关的数据时,我开始思考。一定有更好的方法。按数据类型过滤数据帧的列的概念应该很常见,足以成为一行程序!事实证明确实如此,尽管我认为我已经爬了足够多的次数去查阅文档,以至于可以跨越熊猫 API,但事实证明总是有新的隐藏的宝石!

Pandas DataFrames 有一个名为select_dtypes()的内置方法,这正是我想要的。并且重写上面的代码来使用这个新的(对我来说)函数看起来像下面这样(注意 select_dtypes()取一个列表,可以过滤多种数据类型!):

>>> sns.clustermap(
        df.select_dtypes(['number']), 
        method='ward', 
        cmap="YlGnBu", 
        standard_scale=1
    )

我相信你们中的大多数人都已经经历过这个了,因为(我在熊猫 git repo 上做了一个 git 责备,看看它是什么时候添加的:2014 年🤦)。无论如何,我希望也许有人会像我一样发现这很有帮助。编码快乐!

<https://seaborn.pydata.org/generated/seaborn.clustermap.html> 

进化计算:一颗隐藏的竞争情报宝石

原文:https://towardsdatascience.com/a-hidden-gem-of-ci-evolutionary-computation-ef8d5e815344?source=collection_archive---------33-----------------------

三种复杂的,受生物启发的算法

给 AI 下定义会导致很多争论,所以让我们来谈谈一些具体的东西:计算智能。计算智能由三个分支组成——神经网络、进化计算和模糊系统。在这篇文章中,我将深入进化计算的三个领域——遗传算法、粒子群优化和蚁群优化。

Jr Korpa 在 Unsplash 上的照片

动机

进化计算(EC)是受生物系统启发的竞争智能的一个分支。 在我的上一篇文章中,我强调了 CI 的各个分支,但是在这里,我们只关注 EC。在 IEEE CIS 中,EC 社区有很强的影响力。他们的期刊影响因子 11.554,还不算寒酸。虽然这组算法共享灵感,但在 EC 中有不同的类别。我对做出一个绝对的陈述感到紧张(因为我确定会有反证),但这些算法通常旨在优化一个函数——为给定的情况找到最佳的参数集。不太复杂的算法可以尝试每个参数组合(强力搜索),但这在计算上是不可能的。然而,EC 算法抓住了机会,更雄辩地提供了一个解决方案。在 EC 分支中有多个类别,但在这篇文章的剩余部分,我将深入到我最喜欢的几个类别— 遗传算法、粒子群优化和蚁群优化

遗传算法

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

遗传算法可以说是进化计算中最流行的算法类型。气体在模拟自然选择过程中找到了灵感。我不太记得高中生物,但我记得自然选择的三个关键要素是突变、交叉和选择。经过许多代,最适合的生物生存下来,它们的遗传物质繁殖到下一代。但是,这个概念如何在计算智能中发挥作用呢?

理论

记住,GAs 寻求为给定的问题找到最佳的参数集。为了做到这一点,GAs 采取了分代方法。从第一代开始,GAs 评估每个“染色体”的适合度,在“群体”中找到最适合的。基于“选择”方法,选择“亲代”染色体。他们的遗传物质传递给下一组染色体或下一代。当我们达到收敛(我们的解决方案足够好)时,该过程结束。要查看完整的示例,请参见这篇文章。

应用

由于适应度函数的灵活性,遗传算法可以求解多种类型的优化函数。一个例子可能是组合问题。你玩过“荒岛”这个游戏吗?你会带哪五样东西去荒岛?现在,这个问题对人类来说很容易解决,但对算法来说很难。但是,遗传算法有效地解决了这个问题。为了构造一个适应度函数,我们向 GA 提供了一个可能的项目列表以及它们的价值。遗传算法搜索不同的组合,直到它最大化的价值的五个元素,你可以采取。

去荒岛应该带哪些物品?还是让阿嘎自己想办法吧!背景照片由阿比扬·阿西夫拍摄

你可能会问,为什么不用蛮力呢?嗯,如果我们有一千个项目,就有超过一万亿种组合。意思是,这需要一段时间的蛮力,我们明天就要去岛上了;).当然,GA 必须进行调整,但是它可以搜索列表中的元素并返回最合适的集合。

粒子群优化

照片由里斯·肯特什在 Unsplash 上拍摄

动物群体的行为影响粒子群优化算法。想想鸟群的行为;它们会栖息在同一个地方。在飞行过程中,它们会以不可思议的方式移动(我在谷歌上搜索过这个,它被称为 murmuration)。他们是如何如此完美地一起行动的?粒子群算法试图用数学方法来表示这种现象。

理论

每个粒子(或者我们例子中的鸟)代表被优化的参数。像气体一样,初始化发生,优化函数评估每个粒子。PSO 的独特性来自于它对每个粒子的表示。它的位置和速度代表每个粒子。每次迭代后,每个粒子都根据其位置(最佳解)和速度进行更新。通常,每个粒子由它的邻域中的最佳解(接近它的粒子)和最佳全局解(迭代中的粒子)更新。例如实现,查看这个 pos t。

应用

事实证明,提出一个真实世界的解决方案对 PSO 来说是一个挑战——如果你有的话,我很乐意听一些例子。所以,我要回到我上一篇文章的例子——寻找最好的葡萄酒。在这个例子中,每个粒子都有一个酸度、甜度和酒精含量的值。目标是找到这三个特征的完美结合,找到最好的葡萄酒。假设我们有一个覆盖大部分 3D 空间的数据集(酸度对甜度对酒精含量)。

在这个例子中,每个酒瓶都是一个“粒子”,粒子群优化算法将找到酸度、甜度和酒精含量的完美组合。背景图片由泰瑞·维里斯迪斯拍摄

我们的算法生成许多粒子,优化函数根据我们对葡萄酒的配置的喜欢程度来评估每个粒子。每一次迭代都会产生稍微好一点的葡萄酒,因为粒子会发现我们喜欢葡萄酒的哪些品质。

蚁群优化

斯坦尼斯拉夫·罗日科夫在 Unsplash 上的照片

你看过蚂蚁吗?你注意到它们是如何形成线条的吗?这不是一个随机的过程,而是它们的生物学驱动的。当蚂蚁在探索时,它们会在找到必要的资源时释放信息素。信息素吸引其他蚂蚁沿着同一方向前进——留下更多的信息素。这一过程通过提供到达资源的最短路径来提高群体的效率。你能猜出这有助于解决什么类型的问题吗?我最喜欢的是优化图中的路径(寻找最佳路径)。

理论

存在多种 ACO 策略,但是我将强调 ACO 使用的主要步骤。记住,边和节点组成了图形。简而言之,边是节点之间的连接。ACO 处理加权图,这意味着每条边都有相关的成本。为了启动 ACO 算法,生成一组初始的“蚂蚁”。每只蚂蚁沿着不同的路径在图中行进——寻找最短的路径。蚂蚁遍历之后,每条边的信息素都会进行数学更新,突出显示哪些方式是好的,哪些是不好的。经过多次迭代后,我们希望找到图的最短路径。

应用

ACO 的一个明显的应用是寻找旅行的最佳路径。如果我想环游世界,我可以模拟我想去的城市之间的距离。ACO 可以找到最好的旅行计划,这样我就不会在飞机上浪费太多时间。

ACO 优化我们的路线,以确保我们采取最佳路径。背景照片由托马斯·金托拍摄

使用蚁群算法,一群蚂蚁可以穿越许多不同的旅行路线。最终,他们会找到最大化我的偏好的路径。也许我们想远离高速公路?也许我想要最快的方法。然而,我们决定优化行程;蚂蚁将通过多次迭代找到最佳路径。

结论

使用生物系统作为动机证明创造了复杂问题的雄辩的解决方案。CI 的许多贡献来自进化计算的分支。他们找到最佳参数集的能力继续给人留下深刻印象。在这篇文章中,我从较高的层面谈到了这些算法能做什么,但请注意,它们可能会变得非常复杂。甚至这篇论文的标题(与我以前的导师合著)学习模糊测度的约束保持遗传算法及其在本体匹配中的应用【1】也强调了这些算法可以变得多么复杂。在 EC 中存在更多的算法,我希望有一天能写下它们。我希望你以这些为灵感来解决你的问题,甚至基于一个完全不同的生物系统创建一个新的算法。

参考

[1]http://derekt Anderson . com/pdf/Constraints % 20 保留% 20Genetic %算法% 20 for % 20 learning % 20 fuzzy % 20 measures % 20 with % 20 an % 20 应用程序% 20to %本体%20Match.pdf

流行神经网络体系结构的高级解释

原文:https://towardsdatascience.com/a-high-level-explanation-of-popular-neural-network-architectures-49dce63f02e8?source=collection_archive---------34-----------------------

从人工神经网络到变形金刚,包括 NLP 的例子

图片来自 unsplash 的 Solen Feyissa(来源)

神经网络无处不在,因为它们能够很好地捕捉数据中的非线性关系。本文旨在简明扼要地解释流行的神经网络结构。目的是为它们如何工作提供一种直觉,但更重要的是,为什么它们的结构可能对不同的问题有用。每个部分都有到资源的链接,用于深入解释这些主题。

内容

  1. 人工神经网络
  2. 卷积神经网络
  3. 递归神经网络(RNN)
  4. 变压器网络和注意机制
  5. 结束语

人工神经网络

神经网络是一个函数,它获取输入张量 X (具有 i 行和 j 列)并将其映射到输出 y_hat 以尝试估计真实值 y 。该函数具有影响映射的未知参数 θ 。最佳 θ 是使 yy_hat 之间的误差最小的那些。

网络本身由输入层、隐藏层和输出层组成;下面的图 1 显示了一个带有一个隐藏层的简单网络。

图 1:具有单一隐藏层和单一向量输入的神经网络

每一层的输出作为下一层的输入。层 (L) 具有 i 个节点,其值由向量 a_i^(L) 表示。这是由非线性激活函数 ϕ^(L) 变换的前一层 a_j^(L-1) 的输出节点的函数,如下式所定义:

W_ij 是一个乘法因子(称为权重矩阵),它线性组合前一层的输出节点。

在图 1 所示的简单网络中,预测输出由下式给出:

其中:

在这种情况下,需要估计的模型的未知参数都是权重矩阵 W_ij^(1)W_1j^(2) 。这些通过最小化输出和真实值之间的误差来确定,J =误差(y_hat,y) 。其梯度 J_i = Nabla(J) 相对于每个分量 θ 被用于将的值微移至更接近最优值。然后使用 θ 的新值,即 θ = θ-ηJ_i* (其中 η 是步长)来计算新的 y_hat ,并且重复该过程多次迭代。这个过程被称为反向传播,也就是通常所说的学习

伊恩·古德菲勒的书是深入研究神经网络的数学方面的一个很好的资源。另一方面,3Blue1Brown 的视频为这些网络如何工作提供了一个很好的视觉效果。

卷积神经网络(CNN)

虽然前馈神经网络非常有用,但它们很难从高维数据中提取空间特征。卷积神经网络(CNN)正是为了做到这一点而设计的。对于一个直观的例子,考虑手写字母的图像分类。每个字母都是由像素表示的图片,如下图 2 所示。然而,由于这些字母是手写的,所以同样的字母会被扭曲、移位、缩小或旋转。

图 2:两个手写 X 像素

作为人类,我们可以识别每幅图像中的空间特征,并认识到它们都代表字母 x。然而,正常的前馈网络会将每个像素视为独立的数据点,因此会将图 2 中的两个字母视为完全不同。CNN 通过提取空间特征纠正了这一点。图 3 显示了图 2 中代表字母 x 的两幅图像的特征。

图 3:定义字母 X 的空间特征

这些特征是通过滑动窗口提取的,称为内核,用随机权重初始化。在图像的每个窗口,像素值与核的权重进行卷积(即,例如,取点积)。然后应用称为的过程,其中在每个窗口中,取最大值(有效地提取最重要的特征)。在卷积层之后,添加标准前馈层以实现分类。整个过程的图像如下图 4 所示。

图 4:显示应用于图像的 CNN 分类器的工作原理的图表

这个图像示例也可以扩展到其他领域,例如在自然语言处理(NLP)中,您可以用单词及其嵌入维度代替像素,如图 5 所示。

图 5:应用于文本的 CNN 分类器

在这种情况下,内核是单向的,它从每个单词的上下文窗口中提取特征。这种思想可以扩展到更大的文本片段,例如,表示变成三维的句子。处理文本数据时的一个关键区别是句子的长度可能不同,因此通常会指定最大句子长度。

亚历山大·阿米尼的视频在高层次上很好地解释了 CNN。对于那些对数学方面感兴趣的人,再次推荐 Ian Goodfellow 的书。

递归神经网络(RNN)

与图像或其他数据不同,文本包含空间和序列数据。因此,前馈神经网络和 CNN 都没有提供一种自然的方式来读取文本数据。递归神经网络(RNNs)是一种具有反馈回路的结构,能够顺序读取数据。

图 6:具有反馈回路的 RNN 结构(左)和展开的 RNN(右)

因此,每个时间步的输出状态h _ t作为该时间步的输入x _ t和前一个单元的输出状态的函数给出。因此:

此外,文本数据具有需要考虑的长期依赖性。例如,考虑预测下列句子中的下一个单词的任务:

我在法国长大,但我现在住在波士顿。我说得很流利…

在这种情况下,RNN 将不得不使用触发单词 France 来预测下一个单词 French。然而,由于单词彼此远离,在训练期间,法国的影响消失了。因此,需要某些储存记忆的激活功能,称为门控细胞。一种常见的门控细胞被称为长短期记忆(LSTM)。示意图如图 7 所示。

图 7:显示门控细胞的 LSTM 网络示意图

需要注意的重要一点是,LSTM 单元有四个作用:忘记先前状态的不相关信息,存储新信息,更新单元值(单独),控制输出信息。这调节了流经细胞的信息,从而更好地捕捉长期依赖性。

文本处理中的另一个考虑是信息是双向流动的。例如,现在考虑我们例句的未来上下文:

我在法国长大,但我现在住在波士顿。我说得很流利…因为我在法兰克福住了 5 年。

单向 RNN 将仅捕获过去的信息,因此可能预测法语。然而,当考虑到未来的背景时,德语会更合适。因此,还开发了双向 RNN 模型,例如 bi-lstm,并且显示出比单向模型执行得更好。

值得一提的是,尽管 rnn 提供了更自然的文本阅读,但它们通常真的很难训练,因此仅依赖于空间特征的 CNN 通常在许多任务上表现更好。

艾娃·索莱马尼用直观的方式解释了 RNNs,特别是 LSTMs。关于 LSTMs 的详细指南,读者可以参考 Olah 的博客。

变压器网络和注意机制

RNN 模型的一个关键限制是信息会在很长的序列中丢失。虽然 LSTM 能够捕获一些内存,但是结构本身是顺序的,因此输出不是每个序列元素的直接函数。注意机制通过确保序列中的每个项目直接影响输出来解决这个问题。下面的图 8 给出了这方面的高级示意图。

图 8:使用 RNN(左)和基于注意力的网络(右)的机器翻译示例

这具有模仿人类注意力的经验效果,其中机器关注序列中信息最丰富的部分,如图 9 所示。

图 9:网络关注每个单词(红色)的信息的可视化

变形金刚网络是依赖注意机制的模型,没有递归。它们的结构支持独立计算,使 GPU 并行化变得容易。变压器是自然语言处理任务的 SOTA 模型。感兴趣的读者可以参考 Peter Bloem 的博客和 Jay Alammar 的博客。

结束语

随着 GPU 的改进,神经网络结构在数据科学中将变得越来越重要。跟上关于它们的文献几乎是不可能的,但在进一步研究之前,作为起点,了解它们在高水平上是如何工作的总是有用的。

我将更新这篇文章,提供更多关于其他结构的信息(如图形神经网络、层次神经网络等…),敬请关注!

所有图片均由作者提供,除非另有说明。

一位招聘经理为了一个初级数据科学家的职位,浏览了 120 份简历

原文:https://towardsdatascience.com/a-hiring-manager-who-went-through-120-resumes-for-a-junior-data-scientist-role-21216d0507d4?source=collection_archive---------0-----------------------

由电视台的记者拍摄的照片

办公时间

努力写好你的简历和求职信

你想要一份工作,你向你所在地区的所有公司发送简历。但是如果没人给你回电话呢?准备好接受招聘经理的一些建议吧,他为一个初级数据科学家的职位浏览了 120 份简历。这是一个令人大开眼界的故事,讲述了糟糕的简历和求职信是如何被雇主忽视的,所以一定要让你的简历脱颖而出!

背景:我想招聘一名初级数据科学家加入我的数据科学团队。有人告诉我,现在非常缺乏技能,尤其是在 IT 和数据领域。也许是因为 Covid 边境关闭(我在澳大利亚,那里我们仍然对大多数非澳大利亚人关闭边境)或者到处都在发生的“大辞职”,我不确定我能得到的人才水平。因为不确定,我把工作描述留得很笼统。我在寻找一些具有 python 编码经验的 SQL 知识。

1.招聘经理会花大约 6 秒钟看一份简历,然后决定是否继续阅读

尽管我很想告诉你,我彻底检查了每一份简历,但我就是做不到。在决定是否值得花时间阅读之前,我只有大约六秒钟的时间浏览每份简历。如果我不这么认为,我就直接去下一个。信不信由你,绝大多数简历看起来就像是微软 office 的模板,几乎没有任何定制。

即使我对易于使用的简历模板没有任何问题,我仍然希望看到以某种方式定制的东西。不要误会我的意思,我并不期望他们在简历模板中加入独特的元素。但是至少让它看起来像你用模板做了什么!对于一些人(比如我自己)来说,在评估你付出的努力之前花不到 6 秒钟看每份简历是很简单的。

把你的简历当作你的营销小册子。你在推销自己,而你的简历就是你获得面试的门票。你想让你的简历在某些方面脱颖而出,而不是和其他人混在一起!

2.有求职信的人比没有求职信的人获得面试的机会更大

有目的地,在招聘广告中,我没有要求职信。我想看看申请人是否愿意写一封求职信,告诉我为什么他们认为自己非常适合。我还想潜在地排除那些我也听说过的简历提交服务。所以很容易排除那些没有求职信的申请者。我还是看了一部分没有附信的简历,但不是全部。

我注意到一些事情:

  • 记得和招聘经理联系。尽量避免“招聘委员会”或“相关人员”
  • 第一段应该说明你为什么申请这个职位。推销你的产品。
  • 以前的项目的例子应该写得非常简洁。击中要点。确保你说你做了什么,而不仅仅是这个项目是关于什么的。

你也应该更好地使用与你的技能、行业或你之前工作经历相关的关键词。使用这些关键术语将有助于 HR 更快地为您找到合适的机会。

3.在申请之前,研究一下公司,确保你有资格申请这个职位

当你开始寻找一个新的角色时,你往往会尽可能多地申请。相信我,几年前我也做过同样的事。然而,如果你不熟悉这家公司,不知道他们是做什么的,也不知道你为什么想在那里工作,你的简历不太可能会被看第二眼。记住,在没有对公司做任何调查的情况下,很容易发现准备好的求职信和简历。

在申请任何职位之前,确保你已经对公司和职位做了充分的准备。它不需要很长。简单地浏览他们的网站和新闻故事就足够了。还有一个建议:安排一次与招聘经理的快速通话,问几个问题。这是脱颖而出的一种方式。

4.大多数人用同样的词来描述自己,比如“专家”、“勤奋者”和“创造性的思考者”

这是一个初级的角色,但我看到许多简历在所有技术领域都有“专家”级别的技能,例如 Python、SQL、机器学习、敏捷方法、NLP 等。真的吗?这有点扯淡,或者说他们的目标很低。我认为是前者。我只想看到他们有一些经验。

当使用像“我是一个努力工作的人”或“一个有创造力的思考者”这样的普通术语时,你必须展示你是一个努力工作的人或有创造力的思考者。如果不是,我会假设相反的情况;这让很多人翻白眼。

你可能会想,“但是说这些话有什么错呢?”。问题是当你在简历和求职信中反复使用同一种语言时。

我建议如下:

  • 如果你能充分解释你所说的勤奋工作者是什么意思,只在求职信中使用一两个像“勤奋工作者”这样的词,例如,每周工作 xx 小时,但仍然有剩余的精力,或者因为你对该领域的热情而想在周末/晚上工作。
  • 如果你说“我是一个努力工作的人”或“一个有创造力的思考者”,请给出这样的例子。不要只是描述它,并假设我知道那是什么意思。

不要泛泛而谈;详细说说!它向我展示了你的经验,也让我的工作更容易看到你是否适合我们公司的文化。

5.请记住,你的学习意愿比你的经验更重要,尤其是对于初级角色。

可以合理地假设,初级数据科学家不会被期望准确地知道如何为具有挑战性的数据项目构建解决方案。招聘经理寻找的是经过适当培训和指导后能够胜任这份工作的人。不仅如此,最有可能的是,你的职业生涯才刚刚开始。这意味着我们对你学习新事物的意愿以及你工作的动力感兴趣。如果你说你知道如何做每件事,但你不想学习任何新的东西,你不适合几乎任何角色。

经理们正在考虑与你、现有团队合作以及管理你有多容易。关键因素是一个人愿意倾听、学习、适应和保持谦逊。因此,在你的求职信中说你不知道如何做某事,但是你和其他人一起工作并学习了解决问题的新技能。对大多数经理来说,那将是美妙的音乐。

感谢您花时间阅读我的文章。我希望它能给你一些有用的见解,让你的简历在众多应聘者中脱颖而出,并引起招聘经理的注意。如果你喜欢你所读的,请在下面留下你的任何问题或想法!祝您愉快:)

使用朴素贝叶斯分类器的情感分析指南

原文:https://towardsdatascience.com/a-hitchhikers-guide-to-sentiment-analysis-using-naive-bayes-classifier-b921c0fb694?source=collection_archive---------19-----------------------

自然语言处理中最优美的定理之一的教程。

图片来自 Unsplash

分类是机器学习和人类智能的核心。识别声音、面孔或者我们每天看到的图像都属于分类的范畴。现在来看朴素贝叶斯,对于任何开始从事自然语言处理领域的人来说,这是朝着这个目标迈出的第一步;这就是为什么我们了解如何实现这一点以及发动机罩下发生了什么是至关重要的。在这篇文章中,我们将学习如何从头开始实现朴素贝叶斯,并将其用于情感分析。

指数:

  1. 情感分析
  2. 术语
  3. 朴素贝叶斯定理
  4. 推导
  5. 训练模型
  6. 已解决的示例
  7. 结论

情绪分析

现在,情感分析,顾名思义,基本上是我们对陈述的情感进行分类的任务,或者更简单地说,是一个特定陈述试图传达的情感;无论它是正面的还是负面的,悲伤的还是讽刺的;侮辱性的或者有益健康的或者善良的。让我们再举几个例子来详细说明这一点;

假设镇上有一家新餐馆,你和你的朋友决定在那里吃晚餐,并体验这个地方。你真的玩得很开心,当你的朋友问你的意见时,你会说一些类似的话:

图片来自 Unsplash

1。“这个地方太棒了。食物很美味,气氛也很愉快

现在你的朋友有相反的信仰。这个新地方不适合他。所以当你反过来询问他的意见时,他会说:

2.这个地方真可怜。食物很难吃,环境让我无法忍受,让我很不舒服

我想我们都同意,陈述 1 表达了一种积极的情绪,而陈述 2 代表了一种消极的情绪或情感。同样重要的是,我们要注意的是,到底是什么给了我们线索,让我们能够将句子分为肯定和否定。

答案?线索…

如果你仔细观察…在句子中有一些特定的词支配着情感。正面词汇如 棒极了,好吃,过瘾 等。使句子积极,而像 可怜的、可怕的、不舒服的 等词语。把这个句子变成否定句。事实上,如果我们在一个句子中替换这些特殊的提示,它会完全改变它的意思。让我展示给你看:

作者图片

在每一个例子中,如果你注意到,通过改变一个关键的单词改变了整个句子的味道,而句子的其余部分保持不变。这就是情感分析的本质。此外,我们将把我们的讨论限制在二元分类上,将句子分类为肯定或否定。

术语

在深入研究数学之前,我们先来谈谈术语。我们在这里要做的属于监督机器学习;这意味着我们将被提供训练输入,并且这些输入中的每一个都将与其正确的输出相关联。现在你的模型的工作是理解这些数据,观察和分析给定的输入和输出之间的关系,并最终以合理的准确度预测输出;给定新的输入。

走得更远;我们通常用 x 表示输入,用 y 表示输出,其中 y ∈[y,yb,yc,…..yn]类,你的模型的任务是预测一个特定的输入 x 属于哪个输出类。现在,在这种情况下,我们将处理单词和句子,所以在术语上有一点变化。对于由多个特征组成的文档(基本上是句子),我们的输入将是' d ',对于类,我们的输出将是' c '在这里,类将代表积极(积极情绪)或消极(消极情绪)。

所以最后我们会得到一个输入 d,我们的模型必须学会预测它属于哪个类,c。

既然我们已经完成了本质和细节,让我们从定理开始。

朴素贝叶斯定理

我们先来看看贝叶斯定理:

P(A | B) = P(B | A) * P(A) / P(B) →(1)

让我们来看看这些术语:

  • P(A | B) =假设事件 B 发生,事件 A 发生的概率
  • P(B | A) =假设事件 A 发生,事件 B 发生的概率
  • P(A) =事件 A 发生的概率
  • P(B) =事件 B 发生的概率

因此,贝叶斯定理给了我们一种寻找条件概率的方法。贝叶斯定理是朴素贝叶斯定理的核心。

现在我们能够描述多项式朴素贝叶斯定理了。顾名思义,这个定理使用了一个贝叶斯分类器,并对特征之间的相互作用进行了简化。

我们在朴素贝叶斯中考虑的最重要的假设之一叫做**词袋。**这意味着算法真正关心的是单词及其频率,即该单词在我们的数据中出现了多少次。这个词在我们的句子(文档)中的位置一点也不重要。我们只需要记录特定单词在文档中出现的次数。就是这样。

我举个例子解释一下:

茶让我快乐。红茶、绿茶、奶茶,不管是哪一种;只要是茶我就满足了。我是喝茶长大的,每喝一口都会让我想起过去的美好时光”

我们来进一步分析一下:

作者图片

这基本上就是单词袋的概念。哪里有茶、我、快乐、满足等字眼无关紧要。被用在句子中,重要的是它的频率。

图片来自 Unsplash

推导

现在让我们试着用数学公式来表达。

如果您还记得,我们的主要目标是找到给定特定句子(文档)的类别(积极或消极情绪)。所以我们可以这样解决这个问题:

1.假设我们有一组可能的类 c。

2.我们发现一个文档在一个特定类中的概率。

本质上是给定一个文档的一个类的条件概率。

3.我们迭代所有的类,找到哪个类的条件概率最大;给我们答案。

将所有步骤结合在一起,我们得到:

作者图片

这里,术语ĉ表示具有最大概率的类。

我们已经了解了贝叶斯定理。所以现在我们可以把条件概率的公式代入等式(2)

作者图片

我们可以进一步简化这个等式。在对类进行迭代时,我们的文档当然不会改变,只有类会改变;所以我们可以安全地从分母中去掉 P(d ),而不会引起任何大问题。因此,我们修改后的方程变成:

作者图片

术语 P(d|c)称为似然概率

第二项 P(c)称为先验概率

我们可以通过将每个文档分成一组特性来进一步简化它 f1,f2,f3,…..fn。

作者图片

在我们推导的这一点上,我们将做一个非常重要的假设。我们将假设给定的每个特征 f 的概率是相互独立的。这是非常关键的一步,它大大降低了我们问题的时间复杂度。让我们多理解一点。

如果两个事件 X 和 Y 彼此独立,则事件一起发生的概率(P(X 和 Y))变为:

P( X ∩ Y) = P(X) * P(Y)

这意味着:

P( f 1 | c ∩ f 2 | c) = P(f 1 | c) * P(f 2 | c)

我们可以进一步简化方程(5 )!此外,假设事件彼此独立,我们不必考虑每个特征如何与另一个特征相关,或者一个特征在给定另一个特征的情况下发生的概率。这为我们节省了大量的计算能力。

因此,我们的最终等式变成:

P( f 1,f 2,…,f n | c)= P(f 1 | c)P(f 2 | c)…P(f n | c)->(6)

或者

作者图片

当然,一个句子中的特征是它的单词……因此,如果我们将等式中的特征替换为第 I 个位置的单词的 wi ,我们可以如下重新构建等式:

作者图片

唷!!!!我们终于完成了推导。现在,让我们转到如何在实际问题中应用这个概念。

训练模特

1.计算先验概率。我们将首先找到属于每个类的文档的数量。找到每一类中文档的百分比将会给我们所需的先验概率。

让我们假设类 c 中的文档数量是 Nc。

假设文档总数为n。

所以,P(c)=Nc/n total->(9)

2.计算 似然概率。这是有点棘手的地方。我们的主要目标是找到单词 wi 在所有 c 类文档的所有单词中出现的次数。我们首先将所有 c 类文档连接成一个大的“c 类”文本。然后我们使用 wi 在这个连接文档中的频率来给出似然概率。

作者图片

这里 V 代表词汇表,它是所有文档中所有单词的集合,不考虑类别

然而,此时我们将面临一个非常独特的问题。假设我们输入的文档是,

我喜欢那部电影

单词“loved”只出现在积极的类别中,而没有“loved”的例子出现在消极的类别输入中。现在,根据等式(8),我们必须通过乘以每个类别的似然概率来找到概率。如果我们计算出单词“loved”对于类别“negative”的可能性概率,我们得到:

p("爱过" | "负")= 0

现在,如果我们把这个值代入方程(8),我们的类“负”的整个概率变为零;不管其他值是什么?

为了解决这个问题,我们将在分子和分母中引入一个附加项,拉普拉斯平滑系数。我们的等式将被修改如下:

作者图片

这里 a 是拉普拉斯平滑系数。我们通常认为它的值是 1。

3.在 eqt(8)中插入先验和似然概率。

既然我们已经计算了先验概率和似然概率,我们就可以简单地继续下去,把它代入。

不过,我们有几种方法可以优化这一过程:

a.使用对数:如果我们在方程(8)的两边都应用对数,我们可以将方程转换成特征的线性函数,这将大大提高效率。

这是我们的原始方程:

作者图片

现在,如果我们对两边都应用对数,我们会得到一个线性函数:

作者图片

b.停用词:像这个、那个、安、曾、当等词。做

通常不会有助于陈述的情绪。我们可以完全去掉它们来简化我们的模型训练。

c.**未知单词:**每当你面对一个出现在测试数据集中,但在从训练数据创建的词汇中不存在的单词时,建议完全丢弃这些单词,并且在概率计算中不考虑它们。

d.二进制多项式朴素贝叶斯:这是多项式朴素贝叶斯的一个略微修改的版本。在这里,我们将更重视一个词是否存在,而不是它的出现频率。正如我们已经看到的,单个单词可以引起句子情感的巨大变化,因此,忽略该特定单词在句子中出现的次数,而专注于该特定单词在文档中是否存在,这将是一种合乎逻辑的方法。

已解决的示例

最后,现在我们已经熟悉了朴素贝叶斯分类器,我们可以在一个例子中实现这些知识。

训练数据集:

autor 提供的图像

测试数据集:

作者图片

这是一个虚构的电影评论数据集。电影评论被分别分为正面和负面两类。

让我们解决问题:(我们将考虑平滑系数, *a,*为 1)

对于第一个测试用例:

先验概率:

P(c = '正')= 3/6 = 1/2,P(c = '负')= 3/6 = 1/2

似然概率:

P('Great' | c = '正数')= 1 + 1 / (9 + 19) = 0.0714

p('电影' | c = '正')= 2 + 1 /(9 + 19) = 0.1071

P('Great' | c = '负数')= 0 + 1/(10 + 19) = 0.0344

P('movie' | c = '负数')= 0 + 1/(10 + 19) = 0.0344

最后,我们应用等式(8)中的发现,返回最大概率:

P(c = '正')* P('伟大' | c = '正')* P('电影' | c = '正')= 0.5 * 0.0714 * 0.1071 = 0.00382

P(c = '负')* P('伟大' | c = '负')* P('电影' | c = '负')= 0.5 * 0.0344 * 0.0344 = 0.000591

由此我们可以说这个测试用例属于 正向 类。

对于第二个测试用例:

先验概率:

P(c = '正')= 3/6 = 1/2,P(c = '负')= 3/6 = 1/2

在这个例子中我们遇到了未知单词: this,is。我们在计算中不会考虑它们。

似然概率:

P('boring' | c = '正数')= 0 + 1 / (9 + 19) = 0.03571

p('可悲' | c = '正')= 0 + 1 /(9 + 19) = 0.03571

P('and' | c = '正数')= 0 + 1 /(9 + 19) = 0.03571

P('boring' | c = '负数')= 1 + 1 / (10+ 19) = 0.0689

p('可悲' | c = '负')= 1 + 1 /(10 + 19) = 0.0689

P('and' | c = '负数')= 1 + 1 /(10 + 19) = 0.0689

最后,我们应用等式(8)中的发现,返回最大概率:

P(c = '正')* P('无聊' | c = '正')* P('可悲' | c = '正')* P('和' | c = '正')= 0.5 * 0.03571 * 0.03571 * 0.03571 = 0.00002277

P(c = '负')* P('伟大' | c = '负')* P('电影' | c = '负')= 0.5 * 0.0689 * 0.0689 * 0.0689 = 0.00016354

由此我们可以说这个测试用例属于 类。

结论

情感分析广泛应用于社交媒体监控、市场研究等领域。它是自然语言处理最重要的方面之一。朴素贝叶斯分类器是自然语言处理的第一步。希望你喜欢阅读这篇文章。

实现二进制多项式朴素贝叶斯分类器的 python 代码可以在我的 github repo 中找到。数据集也包含在回购中。

建模基础的直观解释(希望如此)

原文:https://towardsdatascience.com/a-hopefully-intuitive-explanation-of-modeling-basics-450e06bb6338?source=collection_archive---------36-----------------------

仅使用基础代数和自己的直觉理解线性回归。

数据科学,像许多其他技术领域一样,起初看起来似乎令人生畏,仅仅是因为它使用的术语。像回归、均方误差、偏差和方差这样的术语听起来复杂而不直观,但花哨的词语有时就是这样;潜在的概念可能比修饰过的名字所暗示的要简单得多!事实上,通过一点解析和一些具体的例子,我希望您会发现这些概念非常直观。

如果你对数据科学一无所知,你可能还是听说过预测建模,至少是顺便听说过。简而言之,预测建模是使用现有信息来确定假设或未来事件的结果的过程。更简单地说,如果你和我在玩石头剪子布,如果你注意到我在一次欠考虑的策略尝试中,在连续十轮中选择了纸,那么如果你在第十一轮中玩剪刀,你将会赢。通过使用前十轮作为数据,你能够以很高的概率或可能性正确预测下一轮的结果。

自然,预测模型的应用涉及的数字精度比这稍高,但坚实的概念理解使数字更容易解释。在上面的例子中,有三种截然不同的结果可供我选择:石头、布或剪刀,很明显我已经建立了一种模式,也就是说,我每一轮都不是随机的。但是,我们试图确定一个更连续的变量的值,比如房子的价格,而不是不同类别的结果。比方说,除了有人随意决定价格,就像我在玩纸时决定的那样,还有某些其他变量,比如大小、位置、条件等等。这对决定房子的销售价格产生了影响。听起来…实际上很准确,不是吗?直觉上,这些因素应该会对房价产生影响,我们可以使用一种叫做线性回归的工具来精确地确定这些因素对房价的影响程度。

为了演示,我将使用国王郡的房屋销售价格数据集,这是由 Kaggle 提供的。该数据集提供了 2014 年至 2015 年间出售的房屋的几个特征,包括平方英尺、状况、邮政编码、卧室数量等。我们将从查看一个家庭的平方英尺开始。

作者图片,在 Python 上创建

在上图中,每个点代表一所售出的房屋,x 轴代表其面积,y 轴代表价格(以百万计)。我们可以看到这里有一种模式。这些点呈扇形散开,似乎向右上方“流动”。接下来,我们想问:这个数字模式符合我们直觉告诉我们的模式吗?我说是的;随着房子面积的增加,价格也应该上涨。

但是,如果我们的最终目的是预测,那么仅仅根据这个图表,我们能多好地预测某个平方英尺的房子的价格呢?不太好,因为我们只有一个模糊的形状。由于图中的数据点是单独的测量值,所以当我们绘制它们时,自然会出现“混乱”。您可能还记得代数,如果我们想要在给定某个 x 值(平方英尺)的情况下确定 y 值(在本例中是价格),通常需要一个公式。最简单的方法是近似一条穿过这些数据点的线,取沿途点的平均值。我们称之为“最佳拟合线”,因为它确实是这样的;最符合给定数据的直线。

作者图片,在 Python 上创建

还可以从代数中回忆一下,一条直线的方程是 y = mx+b,其中(x,y)是直线上的一点,m 是它的斜率,b 是它的 y 截距。让我们想一想在这个上下文中斜率是什么意思。红线的斜率结果是 280 左右。因为斜率是上升的,所以 280 的斜率意味着每增加一平方英尺,价格就上涨 280。这就是我们如何用数字描述该数据集的平均面积和价格之间的关系!

但是,你可能会看着那张图表并想,当然,也许红线是平均预测值,但是有一些点明显偏离它,特别是在图表的顶部附近。事实证明,我们有一种方法来测量我们的线离数据集上的每个点有多远。我们通过测量每个数据点的 y 值与回归线上相应的 y 值之间的距离,将这些距离平方,将它们相加,然后取平均值。这被称为均方误差,因为它给出了我们预测失误的平均平方距离。

我们的均方误差可能很高的一个可能原因是,我们只看一个特征,平方英尺,来确定房屋的价格。如果一栋房子很大,但有严重的结构缺陷,该怎么办?或者,如果一个家很小,但它是那些很酷的小房子中的一个,而这些天人们出于某种原因真的很喜欢它呢?我们知道,除了大小之外,还有许多其他因素会影响房屋的成本,我们的红线预测没有将这些因素考虑在内。出于这个原因,我们会说,这个模型有很大的偏见,因为它偏向于平方英尺作为唯一的价格预测。

另一方面,如果我们要对一所房子的每一个可以想象的特征进行建模,我们可能会面临相反的问题,即基于并不存在的关系进行预测。例如,假设我们发现家中有地毯的房间数量除以房屋建造年份,再乘以吊扇数量,与房屋价格相关。然而,如果我们试图建立一个模型来预测我们还没有看过的其他房子的价格,这种关系很有可能不会持续下去。出于这个原因,我们会说这个模型有很高的方差。

上面的两个例子旨在说明“偏差-方差权衡”,这是一种说模型越复杂,偏差越低,但偏差越低,方差越高的方式。偏差和方差的概念不像其他概念那样直观,但希望模型中太少的特征会导致高偏差,太多的复杂特征会导致高方差的概念似乎是合理的。

如果你觉得这很有趣,并想了解更多,在线数据科学资源并不缺乏!我推荐看一些 StatQuest 视频开始。

带有 Vaex 版本 4.0 的混合 Apache Arrow/Numpy 数据框架

原文:https://towardsdatascience.com/a-hybrid-apache-arrow-numpy-dataframe-with-vaex-version-4-0-5c0d56524ced?source=collection_archive---------24-----------------------

阿帕奇之箭和 NumPy 之间的桥梁(由https://twitter.com/modestasu拍摄)

介绍

Vaex 数据帧一直非常快。它完全是在核心之外构建的(您的磁盘大小是极限),突破了单台机器在大数据分析环境中所能做到的极限。

从版本 2 开始,我们增加了对字符串数据的更好支持,与当时的 Pandas 相比,速度提高了近 1000 倍。为了支持这种看似微不足道的数据类型,我们不得不选择一种磁盘和内存格式,并且不想重新发明轮子。阿帕奇箭是一个显而易见的选择,但不符合当时的要求。然而,我们仍然在 Vaex 中添加了字符串支持,但是以一种未来兼容的方式,以便当时间到来时(现在!),我们可以采用 Apache Arrow,而不呈现过去过时的数据,也不需要数据转换。为了与 Apache Arrow 兼容,我们开发了vaex-arrow包,它使得与 Vaex 的互操作性变得平滑,代价是可能到处都有内存副本。

阿帕奇箭头

在 Apache Arrow 版本中,是时候将 Arrow 支持集成到 Vaex 的核心中了(Python 包“vaex-core ”),取代了“vaex-arrow”包。虽然所有版本的 Vaex 都支持磁盘上相同的字符串数据(HDF5 或 Apache Arrow 格式),但 Vaex 4.0 版的不同之处在于,我们现在将这些数据作为箭头数组传递。

您可以混合使用 Arrow 和 NumPy

虽然在默认情况下,Arrow 在与 NumPy 混合时采用了类似熊猫的方法将缺失值转换为NaN,但在 Vaex 中,我们确保您的缺失值保持缺失值,并且您的数组不会因此被转换为浮点型。

我们不仅在 Vaex 的核心库中采用了 Apache Arrow,还将快速字符串算法从 Vaex 转移到了arrow.compute。因此,我们不仅有数据的互操作性,而且对字符串数据的操作也有相同的语义。这意味着 Vaex、Pandas、Dask DataFrame 都可以以相同的方式对相同的数据进行操作。这项工作是由陈·扎克伯格发起的,我们感谢汤姆·奥格斯伯格的组织。

列表支持

虽然对数字和字符串数据的支持完成了大部分工作,但是 Apache Arrow 支持更有趣的数据结构。例如,分割一个字符串会产生一个字符串列表。这种数据结构在高性能的内存布局中是理想的(连续布局与许多小缓冲区相比),并且仍然允许我们像常规字符串数组一样对其进行操作。这意味着我们可以split/tokenize字符串,并对其应用类似strip的操作,而不必每次都经历连接和分割:

字符串拆分甚至可以多次完成,从而创建一个嵌套列表,而不会有任何性能损失。

为什么是混合动力?

Apache Arrow 将给整个数据分析世界带来很多好处,而不仅仅是 Python。它发展很快,移动很快,我们相信这将是 Python 中分析的未来,在许多领域补充或替代 NumPy。然而,采用需要时间,大多数人可能更愿意看到 NumPy 数组。因此,Vaex 版本 4 a 数据帧可以同时容纳 NumPy 数组和 Apache Arrow 数组,以使过渡期更容易。如果数据来自 HDF5 文件或外部 NumPy 数组,我们将其作为 NumPy 数组保存,但字符串除外。如果数据来自一个箭头文件或外部箭头数组,我们将它们保存为箭头数组。

如果您碰巧有一个 Arrow 文件,但是更喜欢使用 NumPy 数组(因为计算语义的原因),那么您可以延迟转换除字符串数据之外的所有数据。

拼花地板支架

Apache 的 Arrow 来自核心的 Parquet 支持,这是一个非常受欢迎的特性。尽管以前版本的 vaex-arrow 提供了一些拼花支持,但我们现在支持延迟加载,允许您扫描大于内存的拼花数据集。虽然 Parquet 会以一些 CPU 为代价,但它的尺寸减小使它成为云存储的一种有吸引力的格式,在云存储中,网络带宽通常是一个限制因素。

使用 Apache Arrow 的另一个好处是支持读取分区的 parquet 文件。这非常方便,因为从 Spark/Hadoop 世界生成的文件通常是这种格式的,这使得从这些平台获取数据更加容易。

写入分区文件

我们还支持创建分区文件,这可能不是 vaex 中性能最好的部分,但我们用它来测试,并认为它可以很好地共享。

读取分区文件

如果分区是 Hive 格式的,就可以开始了,只需打开目录:

对于所谓的“目录”格式,您必须手动给分区命名:

泡菜支架

在 4.0 版本中,Vaex 支持数据帧的酸洗,这使得与 Dask、Ray 或 Python 的多处理库的集成变得更加容易。

虽然您可能习惯于以低效的方式保存数据,但在许多情况下,Vaex 本身并不保存数据。当我们这样做时:

你可能会惊讶,这只有 907 字节!。如果您检查文件,您会注意到我们提取了路径和内部状态(比如虚拟列df.tip_amount / df.total_amount)。

当您想将整个数据帧存储到磁盘或通过线路发送时,这非常有用。我们不是复制 1tb 的数据,而是简单地提取原始文件位置和您对它所做的所有操作(转换和过滤)。请注意,这对于云存储来说尤其适用,因为所有计算机的文件位置都是相同的。

尽管使用 Vaex 时,我们主要关注的是从单台强大的机器中获得最大的性能,但这为优化分布式计算铺平了道路,适用于那些真正需要集群的情况。

多处理并行应用

认识到df.apply是完成工作所必需的逃生出口,我们还必须认识到,与 Vaex 的其他部分相比,这非常慢,CPU 使用率降到了 100%。这里的瓶颈是由全局解释器锁(GIL)引起的,而不是直接由 Vaex 引起的。尽管如此,我们觉得应该尝试找到一种方法来提高 apply 方法的性能。

现在,默认情况下,apply使用多处理模块,让你的apply飞起来!

云存储

虽然 S3 对 HDF5 的支持出现在早期的 Vaex 版本中,但在 4.0 版中,我们通过 Apache Arrow 和 FSSpec 项目对此进行了进一步改进。Vaex 可以向许多云存储系统读写大多数文件格式,其中性能最好的是 S3。对于 S3 和谷歌云存储,我们原生支持它们的文件路径,例如"s3://vaex/testing/xys.hdf5""gc://vaex/testing/xys.hdf5".在所有其他情况下,可以通过 Apache Arrow 或 FSSpec 指定文件系统对象。

结论

我们希望您和我们一样对 Vaex 的 4.0 版本感到兴奋。将 Apache Arrow 和 NumPy 统一在一个 DataFrame 库中给我们带来了丰富的数据结构(例如字符串列表)、与 NumPy 的兼容性、互操作性(零拷贝数据共享)以及总体光明的未来。核外 Parquet 支持和改进的云存储支持使得在云上工作更加透明。当您需要求助于纯 Python 代码时,改进的 pickle 支持和默认并行应用允许更好的性能。

装置

$ pip install "vaex==4.*"
# Just want to core functionality?
$ pip install "vaex-core==4.*"
# Using conda or mamba?
$ conda install -c conda-forge vaex=4

投稿?

如果您想投稿或寻找问题,请加入我们在 https://github.com/vaexio/vaex[的 GitHub,或者](https://github.com/vaexio/vaex)https://github.com/vaexio/vaex/discussions的更多一般性问题。

自然语言处理的混合方法

原文:https://towardsdatascience.com/a-hybrid-approach-to-natural-language-processing-6435104d7711?source=collection_archive---------30-----------------------

将 NLP 集成到传统机器学习工作流的指南

阿玛多·洛雷罗在 Unsplash 上的照片

自然语言处理(NLP)是一个理论上复杂的概念。我已经尝试了几种实现这些算法的方法,几乎所有的方法都很复杂。最近,我尝试了这种新方法,从概念上讲,我们经历了一个简单的机器学习工作流程,从数据采集开始,到做出预测结束。在这个工作流程中,我添加了一个额外的步骤来处理语言,这使得它更容易理解和实现。

请注意:这个实现并不是一个能解决所有 NLP 问题的标准化方法。它旨在为从事预测分析工作和不熟悉处理语言的人提供一个入门工具包。

重温经典的机器学习步骤

  1. **数据采集:**所有的分析都需要数据。数据获取或收集是将数据提供给模型的过程。数据可以来自不同的来源,如网络抓取、数据仓库、音频和视频等实时数据流。然后,获取的数据可以以数据帧的形式存储在工作空间中,与 Python 的分析结构无缝协作。
  2. **清理和预处理:**在大多数情况下,原始数据包含的信息可能对分析没有帮助,反而不利于预测。因此,在数据采集完成后,我们执行一些清理和预处理,包括移除异常值、区分特征和标签、标准化特征、删除无助于分析的列、输入分类变量等步骤。
  3. **探索性数据分析:**EDA 步骤在分析的开始阶段非常重要,因为它揭示了我们将要处理的数据的相关信息。EDA 有助于理解数据,发现模式、类别和一些统计推断,如均值、中值、标准差和方差。
  4. **建立/训练 ML 模型:**一旦所有的数据都准备好进行分析并标准化,我们就开始建立机器学习模型。构建模型背后的想法是为它提供大量的数据来学习。该算法使用这些数据来学习数据中的模式,并训练自己处理同类的未知数据。正如我们在第一个案例研究中看到的,基于问题和期望的解决方案,有大量的 ML 算法可供选择。
  5. **做预测:**最后输出的是 ML 模型将对未知数据做的预测。一旦用数据训练了算法,它就理解了上下文中的各种信息点是如何相互关联的。在预测阶段,算法被输入一个输入变量及其相关值。该模型接收该输入并预测期望的输出值。

将 NLP 层集成到 ML 工作流中

在机器学习管道中,为了包括自然语言处理(NLP ),我们在数据处理和清理阶段添加了一些步骤。训练和测试算法仍然以相同的方式运行。工作流程中唯一合乎逻辑的变化是将文本转换为机器学习算法可以处理的数字数据。在这一转换过程中,有许多因素需要考虑。例如,一个单词出现的次数有助于确定谈论的话题。

NLP 和 ML 的混合方法

自然语言一般都很乱。音调、解释和含义因人而异,很难与准确理解这些概念的模型一起工作。因此,机器学习本身不能作为 NLP 解决方案。ML 模型对于识别文档的整体情感或理解文本中存在的实体是有用的。但他们在提取主题或将情感与个体实体或主题相匹配时会发生冲突。因此,在混合 NLP 方法中,我们向 ML 模型解释规则。这些规则是语言中遵循的惯例。这些规则和模式有助于算法将分类与人类直觉更紧密地联系起来。

在机器学习系统中添加 NLP 步骤|作者图片

使用这种方法进行情感分析

**问题陈述:**输入数据集包含用户发出的关于美国境内六家航空公司的推文。目的是将这些推文和信息在情绪上分为积极、中立和消极。这将使用传统的监督学习机制来完成,其中输入训练数据集将从推文中确定。通过训练集,我们将构建一个创建类别的模型。

**解决方案:**这个问题的方法将涉及机器学习管道,遵循上面提到的步骤。初始化将从导入该分析所需的必要库开始。探索性数据分析是下一步,我们将探索数据中的各个部分是如何相互关联的,并理解输入。接下来,我们将在预处理过程中添加一个新步骤,执行文本处理。由于机器学习算法对数字数据有效,我们将把文本输入处理成数字输入。最终,一旦预处理完成,我们将继续使用 ML 模型,并对整个数据集运行它,了解它的准确性。

*# Data Source:* [*https://raw.githubusercontent.com/kolaveridi/kaggle-Twitter-US-Airline-Sentiment-/master/Tweets.csv*](https://raw.githubusercontent.com/kolaveridi/kaggle-Twitter-US-Airline-Sentiment-/master/Tweets.csv)import_data_url = "https://raw.githubusercontent.com/kolaveridi/kaggle-Twitter-US-Airline-Sentiment-/master/Tweets.csv"

执行初始步骤:数据集的数据导入和探索性数据分析(EDA)。在这个故事中,我们将不讨论导入数据和执行初始清理和 EDA 的代码,因为这里的重点是在这个经典工作流之间集成 NLP。如果你想浏览整个源代码,我已经在这个故事的底部链接了完整的项目。

一旦我们完成了初始预处理,现在是时候清理特定于自然语言的数据了。这是我们将添加只适用于语言的代码的部分。

ML 循环中的 NLP 层

在接下来的两节中,我们将开始处理自然语言。这个流程很简单:我们首先将语言(文本)转换成向量,然后执行一些清理,以使模型能够更好地预测。

*# Cleaning of data: Since these tweets might contain punctuation marks and other non-relevant characters, we will process those and remove them from the model**# Let us also divide the feature and label sets for this data* feature_set = sentiment_tweets.iloc[:, 10].values
label_set = sentiment_tweets.iloc[:, 1].values
cleaned_feature_set = list()for input_phrase in range(0, len(feature_set)):
*# 1.Removing all the special characters (*,etc.) and single characters (a,an,etc.)* clean_feature = re.sub(r'\W', ' ', str(feature_set[input_phrase]))
clean_feature= re.sub(r'\s+[a-zA-Z]\s+', ' ', clean_feature)
clean_feature = re.sub(r'\^[a-zA-Z]\s+', ' ', clean_feature)*# 2.Convert the entire phrase to lower cases* clean_feature = clean_feature.lower()
cleaned_feature_set.append(clean_feature)

用于处理文本的附加库

我们将在下一节中使用两个额外的函数来增强我们对文本的处理。

NLTK“停用词”: 停用词是大多数搜索引擎被编程忽略的常用词(如“the”、“A”、“an”、“in”)。它们不会在索引条目以进行搜索的过程中出现,也不会在作为搜索查询的结果检索它们时出现。因此,这些字在处理期间是不需要的,并且会浪费处理时间和资源。NLTK(自然语言工具包)是由 16 种不同语言组合而成的单词列表,是 Python 的一部分。下面是 NLTK 中预定义的英语停用词列表。可以通过在非索引字本地目录的 english.txt 文件中添加/删除单词来修改该列表。

{‘ourselves’, ‘hers’, ‘between’, ‘yourself’, ‘but’, ‘again’, ‘there’, ‘about’, ‘once’, ‘during’, ‘out’, ‘very’, ‘having’, ‘with’, ‘they’, ‘own’, ‘an’, ‘be’, ‘some’, ‘for’, ‘do’, ‘its’, ‘yours’, ‘such’, ‘into’, ‘of’, ‘most’, ‘itself’, ‘other’, ‘off’, ‘is’, ‘s’, ‘am’, ‘or’, ‘who’, ‘as’, ‘from’, ‘him’, ‘each’, ‘the’, ‘themselves’, ‘until’, ‘below’, ‘are’, ‘we’, ‘these’, ‘your’, ‘his’, ‘through’, ‘don’, ‘nor’, ‘me’, ‘were’, ‘her’, ‘more’, ‘himself’, ‘this’, ‘down’, ‘should’, ‘our’, ‘their’, ‘while’, ‘above’, ‘both’, ‘up’, ‘to’, ‘ours’, ‘had’, ‘she’, ‘all’, ‘no’, ‘when’, ‘at’, ‘any’, ‘before’, ‘them’, ‘same’, ‘and’, ‘been’, ‘have’, ‘in’, ‘will’, ‘on’, ‘does’, ‘yourselves’, ‘then’, ‘that’, ‘because’, ‘what’, ‘over’, ‘why’, ‘so’, ‘can’, ‘did’, ‘not’, ‘now’, ‘under’, ‘he’, ‘you’, ‘herself’, ‘has’, ‘just’, ‘where’, ‘too’, ‘only’, ‘myself’, ‘which’, ‘those’, ‘i’, ‘after’, ‘few’, ‘whom’, ‘t’, ‘being’, ‘if’, ‘theirs’, ‘my’, ‘against’, ‘a’, ‘by’, ‘doing’, ‘it’, ‘how’, ‘further’, ‘was’, ‘here’, ‘than’} 

**TF-IDF(词频—逆文档频率)tfidf 矢量器 😗* 每个词在词袋模型中都具有相同的权重和重要性。但是,在现实世界的场景中,讨论的主题可以通过理解上下文中重复出现的单词来推导。TF-IDF 遵循的逻辑是,在组合的所有文档中出现较少而在单个文档中出现多次的那些单词对预测的分类具有较高的贡献。顾名思义,TF-IDF 是两种解释(术语频率和文档频率)的组合。简而言之,如果一个词的词频(在文档中出现的次数)较高,并且文档频率的倒数(该词出现的文档数)较高,则该词的分类值增加。TF-IDF 的典型用途是搜索引擎优化算法。

  • TF =(一个单词在一个文档中的出现次数)/(文档的总字数)
  • IDF = Log((文档总数)/(包含该单词的文档数))
*# Changing the text to a numerical form: All machine learning and statistical models use mathematics and numbers to compute data. Since the input here is textual, we will use the TF-IDF scheme to process words.
# Import the necessary packages* from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizerinput_vector = TfidfVectorizer (max_features=3000, min_df=6, max_df=0.8, stop_words=stopwords.words('english'))cleaned_feature_set = input_vector.fit_transform(cleaned_feature_set).toarray()

训练、测试、分割

我们工作流程的语言处理部分现在已经完成。这是向经典机器学习管道的过渡。使用训练、测试、分割方法来验证模型的性能。训练部分用于拟合模型。测试用于评估训练部分的适合度。该函数将数据随机分为训练集和测试集。

*# Let us now use the Train, Test, Split function to divide this data into training and testing sets. We will use the training set to train the model and find the best suitable model for this prediction and then run that model on the test data to finalize the prediction score*from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(cleaned_feature_set, label_set, test_size=0.33, random_state=42)

建模和培训

根据经验,我一直认为用多种算法测试模型的性能是个好主意。算法的选择可以根据输入的类型来区分,但是应该总是在一个以上的算法上进行测试。下面,我将用四个不同的模型测试数据。

*# Random Forest Classification* rf_classifier = RandomForestClassifier(n_estimators=200, random_state=42)
rf_classifier.fit(X_train, y_train)
rf_classifier_score = rf_classifier.score(X_train, y_train)*# Support Vector Machine Linear Classification* svc_classifier = SVC(kernel='linear')
svc_classifier.fit(X_train, y_train)
svc_classifier_score = svc_classifier.score(X_train, y_train)*# Logistic Regression* lr_classifier = LogisticRegression(random_state=0, solver='lbfgs', multi_class='ovr').fit(X_train, y_train)
lr_classifier_score = lr_classifier.score(X_train, y_train)*# K-Nearest Neighbors Classification* knn_classifier = KNeighborsClassifier(n_neighbors=5)
knn_classifier.fit(X_train, y_train)
knn_classifier_score = knn_classifier.score(X_train, y_train)

训练数据的结果

基于训练,我们看到随机森林分类器给我们最好的结果。虽然 99.2%的准确率可能预示着过度拟合,但我们将看到它在测试数据集上的表现。

每个分类器上的结果|按作者分类的图像

测试数据的结果

对测试数据运行相同的随机森林分类器。

*# Final prediction using the best-case algorithm from the above table* final_pred = rf_classifier.predict(X_test)*# Accuracy score of the final prediction* print(accuracy_score(y_test, final_pred))
>>> 0.7667632450331126

我们观察到,测试数据的预测分数不如训练数据的预测分数好。但是,由于随机森林是我们最好的结果算法,为了提高这个分数,我们可以用更好的规则建立模型。添加算法要遵循的附加规则的预处理阶段为分析增加了许多意义。建立更好的规则总是可以提高准确性,因为我们在日常语言中使用的语法、词性和其他词源的概念对于 NLP 模型来说是未知的。我们在模型创建的处理阶段给模型这些规则。

有关该项目的详细实现,您可以访问以下链接的存储库:

https://github.com/rjrahul24/ai-with-python-series/blob/main/09.%20NLP%2C%20Bag%20of%20Words%20and%20Sentiment%20Analysis/Hybrid_Approach_to_NLP.ipynb

就这样结束了!!|照片由斯潘塞·伯根在 Unsplash 上拍摄

结论

本指南旨在让精通机器学习算法并希望开始自然语言之旅的工程师或分析师熟悉自然语言处理(NLP)。我们实现了一个情感分析模型,该模型将 Twitter 推文作为数据输入,并预测推文的情感作为输出。我希望本教程能够让初学者开始运行具有基本功能的 NLP 代码。有关 ML 中 NLP 代码的更高级实现,请参考参考资料部分的链接[5]和[6]。

参考

  1. https://www . geeks forgeeks . org/removing-stop-words-nltk-python/
  2. https://sci kit-learn . org/stable/modules/generated/sk learn . feature _ extraction . text . tfidfvectorizer . html
  3. https://www . lex alytics . com/lex ablog/machine-learning-natural-language-processing
  4. https://monkeylearn.com/blog/nlp-ai/
  5. https://medium . com/coders-camp/20-machine-learning-projects-on-NLP-582 effe 73 b 9 c
  6. https://analyticsindiamag . com/8-different-NLP-scenarios-one-can-take-up-for-a-project/

人工智能法

原文:https://towardsdatascience.com/a-i-law-7303da740023?source=collection_archive---------21-----------------------

快速浏览律师事务所机器人的一面

廷杰伤害律师事务所在 Unsplash 上的照片

你有没有意识到,你的下一个律师可能是一个人工智能(AI)系统,以光速与人工智能法官辩论?嗯,我们还没到那一步,所以你不会很快从你的机器人吸尘器那里得到法律咨询,但事情进展得相对较快。

几年前,一位名为 LawGeex 的人工智能律师向 20 位拥有数十年经验的美国顶级公司律师发起了挑战。这项任务相对简单。他们每人都得到了五份保密协议(大多数商业交易中用来建立保密关系的标准合同)。每个律师都必须审查它们并找出任何问题。

美国顶级公司律师平均花一个半小时来审查它们,它们的平均准确率达到了 85%。而 LawGeex 则在 26 秒内完成了任务,准确率高达 94%。当然,这并不标志着法律职业的终结,但它确实提出了几个重要的问题。

想一想律师花在审阅文件上的时间吧,记住,当他们疲劳时,出错率会增加。如果我们用人工智能系统来代替呢?始终如一,永不厌倦。然后,律师的工作将转变为审查员,利用他的直觉,抽查文件,并验证人工智能的工作。这种工作流程降低了律师事务所及其客户的成本,同时也解放了有技能的律师,让他们可以将时间花在更高层次的任务上。

照片由Adem may在 Unsplash 拍摄

但是人工智能系统能做的远不止这些。律师机器人全天候支持律师事务所及其客户。以下是几个例子:

  • IBM Watson 聊天机器人 ROSS 帮助律师进行研究。用户用日常用语查询罗斯;它识别相关案例并返回所需信息。此外,它会在夜深人静时持续监控法院判决,并在发现有趣的事情时发出警报。
  • BillyBot 是一个初级店员机器人,为在线用户提供必要的信息。然后,它会向他们推荐免费的在线法律资源,帮助他们决定前进的方向。
  • Automio 是一个面试机器人,帮助律师从他们的客户那里获得初始信息,并为他们提供最常见的请求。它减少了律师花在重复性和低价值工作上的时间。总的来说,客户更加满意,因为他们可以在任何需要的时候获得他们的法律文件。

其中一些人工智能系统甚至更进一步,在简单的案例中代表他们的客户。DoNotPay 机器人是一个聊天机器人,旨在帮助人们获得罚款。尽管大多数政府都提供了几种质疑这些罚款的方法,但实际上,很少有人知道如何去做。这个机器人要求用户给他所需要的信息;它会自动生成一封抗辩信并代表他们发送。在运营的第一年,DoNotPay 机器人帮助了伦敦和纽约的大约 25 万人。它赢得了超过三分之二的案件,其客户节省了超过 300 万欧元的罚款。

迈克尔·泽兹奇在 Unsplash 上的照片

最后,人工智能还可以帮助律师事务所窥视未来法院案件的水晶球。通过分析过去的诉讼,系统可以对数据进行预测分析,并提取有意义的见解。它可以突出特定情况下最关键的因素。它还可能确定影响结果的特定陷阱。此外,它甚至可以在案件开始之前预测案件的成功与否。使用人工智能的律师事务所可以获得的情报数量是无穷的,可以帮助律师在接手法庭案件之前更好地装备自己。

从这几个例子可以看出,人工智能律师将成为工具,并为今天的律师事务所提供竞争优势。它们将对通过人工智能增强(人工智能支持律师的工作)使平凡的工作变得更有效率至关重要。在某些情况下,人工智能律师甚至会接手案件,处理简单的任务。有一点很清楚,单独工作不再是一种选择,因为结果比让律师和人工智能系统合作差得多。更重要的是,客户现在可以以更低的价格获得更好的服务。当然,这种改变不会一蹴而就,但要获得竞争优势,现在是邀请 AI 法律合伙人加入律所的合适时机。

如果你喜欢这篇文章并想联系我,请联系我🐦 碎碎念 ,🔗LinkedIn,📷insta gram或者😊https://www.facebook.com/alexieidingli/

*

Alexei DingliProf 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他出版了几本同行评审的出版物,并成为马耳他的一员。由马耳他政府成立的人工智能特别工作组,旨在使马耳他成为世界上顶尖的人工智能国家之一。*

构建高级对象检测管道之旅——将 YoloV5 的性能提升一倍

原文:https://towardsdatascience.com/a-journey-of-building-an-advanced-object-detection-pipeline-doubling-yolov5s-performance-b3f1559463bf?source=collection_archive---------12-----------------------

我在一次 Kaggle 物体探测比赛中使用的几个技巧在比赛的大部分时间里将我的分数提高到大约前 10%

迭戈·PH 在 Unsplash 上的照片

我花了三个月深入研究物体探测。我已经尝试了大量的东西,从实现 YoloV5,VFNets,DETR 等最先进的模型到融合对象检测模型和图像分类模型以提高性能。在比赛的早期阶段,我努力提高基线模型的分数,但我找不到有用的在线资源,这就是我写这篇文章的原因。我想带你踏上一段旅程,从起点到终点,简要地向你展示我几乎将分数翻倍的每一步。

官方的竞争指标是(平均)平均精度,这是最常用的对象检测指标之一。为了向您展示每一步相对于其他步骤的改进,我将在旁边添加它的分数。

  1. 第一步是建立一个简单的基线——0.126 地图

我很确定这是一个陷阱,很多数据科学家早就掉进去了。我们总是兴奋地用我们能想到的每一种技术从最复杂的模型开始。这是一个巨大的错误,你最终会变得沮丧并离开你的 ML 项目,即使你不这样做,你也很可能会超负荷!

我得到了惨痛的教训,但最终我用这些规范构建了一个初始模型:

  • YoloV5-XL
  • 图像从 3K 分辨率调整到 512

我知道这听起来很简单,最初,我也是这么想的。但是,实际上,建立基线可能是最烦人的步骤之一。因为有多个步骤,比如将输出后处理成竞争对手的格式等等(我不想深究)。

此外,我的实际初始 YoloV5-XL 模型只得到 0.064(上面的一半),我花了 2 周时间调试它,结果发现我没有正确地规范化输入数据!

2。删除其中一个输入类!— 0.143 地图(+13%)

这一招当时对我来说没多大意义。有 14 个输入类、13 种不同的疾病和 1 个“无发现”类。大约 70%的数据集属于“无发现”类,只有 30%属于其他类。一个竞争对手发现你可以丢弃这个类,并使用“2 类过滤器”技巧来预测它(见下文)。这使得数据集不那么倾斜。此外,它允许训练明显更快(因为你将在更少的图像上训练)。

3。增加训练和推理图像分辨率— 0.169 mAP (+18%)

第二步是将图像分辨率从 512 提高到 1024。这是一个微不足道的进步,但我想在这里表达的观点是,如果我从这个决心开始,我可能不会进一步提高我的分数。仅仅是因为在这个更高分辨率上的训练导致批量大小从 16 减少到 4(以不耗尽 GPU 内存),这大大减慢了训练过程。这意味着更慢的实验,你不想用慢实验开始一场竞赛…

4。定影效率 Net 和 yolov 5–0.196 mAP(+16%)

这不是我的主意,我从一个公共内核得到的。但是,这是我在 Kaggle 比赛中遇到的最好的想法之一。我想强调的是,在 Kaggle 上进行比赛的主要好处之一是你可以从社区中学到东西。

这里的主要想法是在这里训练一个图像分类模型(EfficientNet ),它可以实现非常高的 AUC(大约 0.99)并找出一种将其与对象检测模型融合的方法。这被称为“2 级过滤器”,实际上比赛中的每个人都采用了这个想法,因为它大大提高了分数。

如果您感兴趣,您可以在此找到更多信息:

5。加权框融合(WBF)后处理— 0.226 mAP (+15%)

这对我来说也是一个很新的想法,不是你能在网上轻易找到的。加权盒子融合是一种过滤掉物体检测模型产生的盒子数量的技术,使得结果更准确和更正确。它的性能超过了现有的类似方法,如非最大抑制(NMS)和软 NMS。

应用 WBF 的结果如下所示:

图片由作者转载。

如果你有兴趣了解更多,你可以在这里查看我的文章:

  • 与 WBF 融合的 5 重交叉验证— 0.256 mAP (+13%)

我犯的一个最大的错误是我忘记了做交叉验证,这就是为什么这是一个要点而不是一个编号点。这也是我想写这篇文章的主要原因之一,强调 ML 基础的重要性。我太专注于应用新技术和提高性能,以至于忘记了应用这个基本的 ML 技术。

如果你想知道我是如何将 0.256 附加到它上面的,那是因为我阅读了一些在比赛结束后发布的解决方案,这是他们大多数人在与我相似的模型上交叉验证后得到的结果。

最终的管道可以在这里看到:

作者图片

  • 我尝试过,但无法解决的事情
  1. 训练 DETR。DETR 是一个神奇的物体检测转换器,我在之前写了一篇的文章。我认为这是将我所写的一切付诸实践并真诚测试它的时刻。然而,我没有发现他们随代码提供的文档有什么帮助,我也找不到很多有用的资源。此外,我花了大约 3 个星期(大约是比赛时间的四分之一)试图让它工作。我这样说的原因是,虽然离开你一直在努力的解决方案可能很难,但在实验性的 ML 世界中,有时必须这样做,老实说,我希望我早点离开它。但是,从好的方面来看,我发现另一个名为 MMDetection 的库提供了 DETR,并且使用起来更加容易。
  2. WBF 预处理。虽然很多竞争者说这提高了他们的分数,但并没有提高我的分数。这就是关于 ML 的事情,不是所有的技术都必然以同样的方式使不同的模型受益。

最后,如果您感兴趣,可以在这里找到我的代码:

https://github . com/mostafaibrahim 17/VinBigData-Chest-x ray-Object-detection-

最终想法

我希望你喜欢这篇文章,并从我的错误中吸取教训。我希望看到更多的人分享他们的比赛经验,因为我发现这很有帮助。网上有很多关于构建基线的教程,但是关于下一步该做什么的很少。

清理动物收容所数据之旅

原文:https://towardsdatascience.com/a-journey-through-cleaning-animal-shelter-data-86c310fd0e18?source=collection_archive---------34-----------------------

变更数据

用数据拯救动物的生命

照片由多萝西娅·奥德尼在 Unsplash 拍摄

在我的上一篇文章中,有一个关于动物收容所分析的理论驱动的讨论。这篇文章重点介绍了庇护所对分析的需求、面临的挑战以及应对这些挑战的可能解决方案。在庇护所分析数据的主要挑战之一是收集的数据质量下降。在这篇文章中,我打算阐明收容所的数据质量问题以及我是如何处理这些问题的。

数据显示“嗨!很高兴认识你!”

收容所收集的数据大约有 9-13 列,我无法准确定位,因为随着收容所开始增加更重要的列,列数每年都在变化。我收到的数据是关于收容所动物的数据,收集了 5 年的时间,大约有 15000 条记录。数据记录了救援的几个细节,其中一些是

  • 被救动物的俗名是什么?
  • 获救的动物是什么物种?
  • 救援是什么时候进行的?
  • 这只动物是从哪里被救出来的?
  • 谁是线人?

现在,这是一个金矿。我有 15000 行关于动物救助的数据。这是我第一次在现实世界挑战中工作,我对这些数据的工作可以真正产生影响

好吧,你知道这是迟早的事。所以,让我继续吧——这些数据很快就变成了邪恶的东西,并决心吸干我最后一点耐心。很快,我就被一些非常现实的问题弄糊涂了!

为数据牵线搭桥

首先,数据是内部碎片。每年有 5 个不同的 excel 文件,每个文件每个月有 12 个单独的工作表。我从来没有处理过任何东西,除了一个美丽的组合。csv 文件作为我本科课程的一部分。

幸运的是,我的在线学习教会了我熊猫,这似乎是一个不错的选择。但是,如何在 excel 文件中合并工作表呢?我不得不把 60 张数据结合在一起。我只好为数据扮演红娘。

幸运的是,谷歌快速搜索帮助我找到了我的指南。问题解决了!这很简单。我跑去参加聚会。

接受数据并不完美

当我第二天回去工作时,我有一个简单的议程——对我的数据进行一些汇总统计,并让我的协调员介绍我的一些发现,这(很可能)会给团队留下深刻印象。我首先统计了获救最多的物种。这是收容所过去 5 年中获救最多的 5 个物种。

来源

我问我妈家里有没有绳子。她没有。我在这里,写这篇文章。

哭了几天后,我回去工作了。我打开 excel 文件(数据的原始来源)看了一下数据。这种人工检查可以帮助理解数据中一些最紧迫的问题,这是非常神奇的。通过检查,我发现数据中存在以下问题

  • 缺少值
  • 拼写错误
  • 案例错误
  • 学名混淆
  • 不规则的日期格式

现在,这些之所以是“问题”而不仅仅是被忽略的现象,是因为对有问题的数据进行的 分析比根本没有进行的分析 更糟糕。在动物收容所的背景下,如果我向团队提供从不一致的数据中获得的数字,从战略和决策的角度来看可能会有一些非常严重的问题。

我现在有了清理数据的任务。快速迂回,我认识到几乎每篇“数据科学初学者”文章都谈到数据清理仅限于人们喜欢的流程图绘制的第一步。我认为不止如此。数据清理可能会融入整个过程。数据清理不必局限于处理缺失值或数据问题。它更多的是将我们拥有的数据表示成更适合手头任务的形式。

回来,我是如何清理数据的呢?

让数据“更好”

没有通用的方法来清理数据。但是,不同的数据清理过程有一些共同的步骤。这些步骤可以很容易地在互联网上找到。在我掌握的数据中,属于这一类别的清洁作业包括

  1. 处理缺失值— 在几种可用的不同策略中,省略通常被认为是一种糟糕的策略,因为它会导致数据丢失。在我的情况下,省略一些记录是必要的,因为其中一些缺乏重要的细节,如救援日期。但是,这个比例很小。
  2. **统一的日期格式——**救援日期在一些表格中被编码为string,而在另一些表格中被编码为date。熊猫中的日期处理的快速梳理有助于纠正这些问题。

处理名称中的不一致

但是,这些只是冰山一角。真正的问题是命名和拼写错误。当面临这样的挑战时,我认为我们倾向于遵循两种主要方式:

  • 方法 1 或手动方式——想法是手动纠正问题
  • 方法 2 或自动化方式——编写一些代码来诊断数据问题并清理它

在庇护所数据的上下文中,使用手册意味着找到并替换所有拼写错误的名字。很有趣,不是吗?自动化方法要求我编写一个超级智能程序来完成所有这些工作。好吧,我显然是力不从心了:)

手动方式将确保更好地控制清洁,而自动方式将要求我以控制换取速度。

那么,我到底做了什么?好吧,我选择了大多数人在两个极端选项之间困惑时选择的道路——我创造了一个两全其美的第三个选项!我称之为混合变种,决定写一个脚本,在我的干预下,以半自动的方式清理数据。

首先,这些名字是通过一个初步的清理管道传送进来的。图 1 对此进行了描述。

图一。初步命名清洗管道(鸣谢:作者)

混合方法的想法很简单→ 使用 相似性分数 对密切相关的动物/物种名称进行聚类。将群集显示为输出,检查它并删除不属于该群集的条目。重复这个过程,直到达到某个停止标准。

这种混合方法如图 2 所示。

图二。清理不一致的名称(鸣谢:作者)

为了找出一种动物的正确拼写,我在谷歌上搜索了拼写。事后看来,我本可以将这部分自动化,并进一步减少手动操作。

在得到过去 5 年的数据之前,我首先得到了收容所去年运营的数据。我使用手动方法清理了这些初始数据。我花了大约 10 个小时(中间有休息时间,这样我才不会发疯)来做这个清洁工作。构建这个有用的脚本帮助我清除了命名中的不一致,减少了很多麻烦,整个过程只花了大约 2 个小时!

呜哇!我终于“净化”了我的数据。但是,责任仅限于清理数据吗?

为未来做准备

“清理一次数据,你是个好人。确保它永远不会变得混乱,你是一个英雄。”

庇护所显然受益于我在数据清理操作后提供给他们的分析。然而,重要的是向庇护所传达其现有数据清理做法存在的问题。这样做将有助于利益相关者展望未来,并为在数据驱动的决策浪潮中冲浪做好准备!

当我决定与团队交流这些数据问题时,我从与父亲的一些富有洞察力的谈话中得到了 3 条建议

  • 说实话
  • 不要表现得“无所不知”,因为你不知道数据收集有多困难
  • 成为解决方案的一部分,不要只是描述问题然后离开

最后,在庇护所协调员的帮助下,我提交了一份单独的报告,内容是庇护所如何更有效地利用其数据做出决策,并详细介绍了如何解决他们的一些数据收集和质量挑战。

平心而论,如果不是庇护所的团队非常乐于助人且以结果为导向,这一切都不可能实现。

如果你对“动物收容所分析”感兴趣,我相信你会喜欢我之前关于这个想法的文章。

用数据拯救动物生命。《动物收容所简介》… |作者:Ramshankar Yadhunath | 2021 年 3 月|走向数据科学

这篇文章最初发表在我的个人博客上。

如果你对这篇文章的内容有任何疑问,请随时联系我。你可以在 Linkedin 和 Twitter 上找到我。

神经网络之旅(第 0 部分)——线性代数快速入门

原文:https://towardsdatascience.com/a-journey-through-neural-networks-part-0-fast-introduction-to-linear-algebra-e9f9ffa46513?source=collection_archive---------25-----------------------

深度学习中使用的核心线性代数概念的更新

照片由来自 Pexels 的 Lum3n 拍摄

"数学的研究就像尼罗河,始于细微之处,终于壮丽."—查尔斯·凯勒·科尔顿(1820 年)

本文是系列文章的一部分,旨在解释主要的神经网络模型是如何工作的:

  1. 单层感知器
  2. 前馈神经网络
  3. 卷积神经网络
  4. 递归神经网络

因为这篇论文不是对任何一个神经网络的解释,而是对那些模型使用的核心线性代数概念的快速 介绍,所以它被标为“第 0 部分”。

在整个系列中,我尽了最大努力保持数学**“互补”但“非必要”。你应该能够理解事物如何运作**(或多或少)而不需要掌握底层的数学。然而,如果你需要快速更新一些概念,比如向量、矩阵、导数,或者如果你希望更深入地理解神经网络,我强烈推荐你阅读这一部分。

要求:我希望你有基本的数学知识(方程,不等式,笛卡尔坐标..)和知道什么是变量,函数,或者标量,至少在本质上。

目录

  1. 矢量
  2. 矩阵
  3. 哈达玛产品
  4. 线性变换
  5. 导数和偏导数

— 5.1 推导的直觉

— 5.2 推导规则

— 5.3 偏导数

6.渐变

矢量

在数学中,更具体地说是线性代数中的**,向量是元素**的有限个的集合。例如, x = [2,3,5]是一个三元素向量, x =[ x₁,…,xₙ ]是一个 n- 元素向量。通常用字母上方的箭头表示(图 4 )。向量可以是列向量,也可以是行向量。列向量的转置(T)是行向量。相反,行向量的转置是列向量。

图 4。列向量、行向量以及列向量到行向量的转置。每个向量的大小为 n × 1

****向量可以被标量乘、加、减或除。如您所料,这会产生一个相同大小的向量,每个元素都乘以/除以/加上/减去那个标量(图 5 )。

图 5。向量与标量的乘法和加法

两个向量的加法/减法非常简单(图 6 )。然而,这两个向量需要有相同的大小 x=【1,4,5,11】和y=【3,4,12,22】为 2 个大小为 4 的向量。

图 6。2 个向量的加法/减法

点积(~矢量乘法 ) 有点棘手。当你做两个向量的点积时,你得到的不是一个新的向量,而是一个标量 ( 图 7 )。怎么会?设x=【1,4,5,11】和y=【3,4,12,22】又是我们 2 个大小为 4 的向量。

图 7。2 个向量的点积

这里,我们将每个向量的第一个元素相乘,每个向量的第二个元素相乘…每个向量的最后一个元素加在一起,然后我们把每个结果加起来。如果您希望将两个向量相乘并最终得到一个相同大小的向量,您需要使用哈达玛乘积(参见哈达玛乘积部分),也称为元素级乘法。

关于矢量的更多。

[数]矩阵

矩阵是一组变量(数字、表达式、函数……),排列在 n 行和 m中,用于定义加法或乘法等运算。如果有帮助,您可以将矩阵显示为堆叠的列/行向量**。关于向量,矩阵可以转置(图 8 )、相加(图 9 )、相减或相乘(图 10 )。**

矩阵的转置是一种将矩阵翻转到其对角线**上的操作。设 A 是一个大小为(3,2)的矩阵。它的转置是大小为(2,3)的矩阵。**这个运算在线性代数中真的很有用,尤其是矩阵相乘的时候!

图 8。来自 www.mathsisfun.com的 gif

与向量一样,用标量对矩阵进行加/减/乘/除操作将创建一个相同大小的新矩阵,矩阵的每个元素都被标量加/减/乘/除。

要相加(或相减)两个矩阵,它们必须具有相同的形状**。设 A 和 B 是大小为( n × n )的方阵。可以添加它们,因为它们具有相同的行数和列数。**

****

图 9。两个大小为(3,3)的矩阵相加

要做 2 个矩阵的点积(矩阵乘法),第一个矩阵的列数必须与第二个矩阵的行数匹配。这意味着在非方阵**(又名。大小为 2×2,3×3,…, n × n 的矩阵,其中一个必须转置。记住矩阵乘法是不可交换的 (AB ≠ BA)。**

图 10。大小为(3,3)的两个矩阵相乘

例如,让我们看看如何得到 56 和 72。对于 56,因为它是乘法矩阵的第一个元素,我们取 A 的第一行和 B 的第一列,我们做:

对于 72,因为它是我们相乘的矩阵的最后一行和第二列,我们取 A 的最后一行和 B 的第二列,我们这样做:

乘法矩阵的中间元素在第二行第二列。因此,我们取 a 的第二行( r₂ )和 b 的第二列( c₂ )做与I-元素类似的操作:

更多关于矩阵乘法。

哈达玛乘积

Hadamard 乘积,或元素-wize 乘法,是一种乘法,其中矩阵 a 的每个 xᵢⱼ元素乘以矩阵 b 的元素 yᵢⱼ**(图 11 )。考虑用于点积的相同矩阵 A 和 B(图 10 )。**

图 11。大小为(3,3)的两个矩阵的 Hadamard 乘积

如你所见,Hadamard 乘积(元素-wize 乘法)并没有给出与点积相同的矩阵。做矩阵乘法的时候要时刻小心!!

更多关于 Hadamard 产品的

线性转换

让我们称之为线性变换。首先,要确定我们的线性变换 T 真的是线性变换,它必须遵循一些规则 ( 图 12 )。设 2 个向量 abℝⁿ,x∈ ℝ. T 是线性变换,如果

图 12。线性变换的规则。x 是标量,a 是矢量

这意味着,如果我将线性变换应用于 a + b如果我将它应用于 a 和 b 并将结果相加,我应该得到相同的结果。同样,如果我将线性变换应用于( x.a) ,如果我将它应用于 a 素数,然后将结果乘以标量 x ,我会得到相同的结果。

如果你不熟悉术语 a,b∈ ℝⁿ,它基本上意味着我们的向量 a 和 b 都是“大小 n”的,并且包含ℝ空间中的元素(即-1.2,3,56.00897,-787,2/3…).

线性变换的主要例子由矩阵乘法给出。让我们举一些例子:

  1. 设 A 是一个大小为(3,2)的矩阵。t 是来自 ℝ →ℝ 的线性变换,定义如下:

图 13。矩阵(3,2)线性变换 T

2.由 T(w,x,y,z) = (x-y+z,w-z+y)定义的来自ℝ⁴→t32】ℝ的线性变换 t 由矩阵给出:

图 14。由矩阵(2,4)给出线性变换 T

更多和更多 ( 推荐)关于线性变换

导数和偏导数

实数函数的导数测量函数在某一点相对于其参数变化的敏感度。我们来举例说明一下。

直觉的推导:

你正从柏林旅行到东京(相当长的一段旅程)。现在是晚上 8:50 分,你在马路中间。你的车很旧了,车速表也坏了。抛开危险的情况不谈,你怎么知道你的车的速度?

让我们拿 100 米外的 2 个路标,测一下你从第一个路标到第二个路标所用的时间**(图 15 ),再用除以这个时间(因为速度=距离/时间)。**

图 15。我们得到了 100 米的距离和 2 秒的时间。这意味着我们的汽车以 50 米/秒或 180 公里/小时(112 英里/小时)的速度行驶

太好了。你现在知道你的车的速度是 180 公里/小时(112 英里/小时)。但是等等……开车时,你减速以避开一个鲁莽的司机,然后以同样较慢的速度移动到下一个路标。你在这 100 米中没有保持恒定的速度!为了避免计算错误,你决定测量两棵树相距 50 米的时间 ( 图 16 ),认为你应该在这个较短的时间内获得一个恒定的速度。

图 16。我们得到两棵树之间的距离是 50 米,时间是 1.16 秒。这意味着我们的车以 43 米/秒或 154 公里/小时(98 英里/小时)的速度行驶

太好了。我们的测量更精确一点,但是你运气不好**……你打了个喷嚏,还踢了油门踏板最后 5 米。在这 50 米中,速度不是恒定的。为了避免实验过程中的任何变化,你将距离减少到 1 米,然后 50 厘米,5 厘米,1 毫米…你继续,继续,继续,将距离减少到几乎为 0 的点。你刚才在这里做的是直观展示导数的定义 ( 图 17 ):**

图 17。你越减少两个地标之间的距离来测量速度,你就越精确。你选择的两个地标之间的极限距离越接近 0 (limite: h →0),你就越准确!

现在,你在做高等微积分的时候会很少用到这个符号。事实上,你将会看到类似于图 18 中的符号。

图 18。两种类型的导数符号:f'(x)和δy/δx,其中 y = f(x)

在这个例子中,你试图找到 f(x) = (3- x )相对于 x 的导数。求导数的过程叫做微分。它可以写成 f'(x)或 dy/dx,其中 y = f(x)

推导规则:

**推导规则很多。比如一个常数的导数永远是 0,c 的导数 xⁿ 是 c n.xⁿ⁻(例:f'(2x )= 2×3x),导数 a 复合函数 f(g(x))是 f'(g(x))。g'(x)… 了解它们并理解它们从何而来是个好主意。它们都遵循导数的定义。比如这里的 就是复合函数求导的证明。

更多关于推导规则

偏导数:

当你试图对一个多变量函数求导时会发生什么?还记得我总是提到“关于我感兴趣的变量”的导数吗?就像“速度”是距离“相对于时间”的导数?或者说 f'(x)f(x) 相对于 x 的导数?嗯,当你计算一个多变量函数的偏导数时,你所做的就是计算那个函数对一个变量的导数,忽略另一个(又名。将它们定义为“常数”)。你用变量来区分

符号:**代替用于导数的 dy/dx,我们使用符号∂(参见图 19图 20 中的示例)。

设 f(x,y) = 3x + 4y。计算 f(x,y) 相对于 x (图 19)的偏导数相当于说:“让我们计算 f(x,y)的导数,如果 y 是常数而不是变量”。

记住:

  1. 常数的导数总是等于 0。
  2. 和的导数等于它的导数之和[f (x)+ g(x)]' = f'(x) + g'(x)
  3. 的衍生物 cxc*n .xⁿ⁻,*t55】c一个常数

图 19。f(x,y) = 3x + 4y 关于 x 的导数(≈ y 设为常数)。

尝试自己做这个函数关于 y 的偏导数**。你需要记住的一个数学规则是 x ⁰ = 1 。有了这个和上面解释的所有东西,你应该能够对函数 f(x,y)相对于 y 求导。**

剧透:f/y = 4

梯度

梯度的定义

梯度在数学、物理、生物中无处不在……在多变量计算的情况下它是一个强大的工具。例如,它在优化理论中起着重要作用,在这里它被用来最大化/最小化一个函数。处理神经网络训练的时候也是天天用。****

对于多变量的标量值函数,梯度定义为向量场∇f** ,其在点 p 的值是其分量为 fp 的偏导数∂的向量(图 20 )。**

图 20。n 维空间中函数 f 在 p 点的梯度向量

简单来说,梯度向量可以被认为是某事物增长最快的速度和方向。假设你在一个房间里。一个散热器在房间的一端散热,另一端一个空调冷却空气**(图 21 )。它们以恒定的速度加热/冷却空气。温度稳定不变(温度与时间无关)。温度梯度表示温度上升最快的方向。这个梯度的大小(它的幅度)显示了这个效应的强度。**

图 21。温度的梯度矢量表示。

你可以看到,我离空调越远,梯度就变得越大。但是在某一点上,梯度停止增加,然后又开始下降。这是因为房间中间的两个点之间的温差比靠近加热/冷却源的两个点之间的温差更大。

梯度在机器学习中真的很有用。它可以在线性回归中用于避免使用矩阵求逆**(当有大量特征时计算量很大)、在岭回归、在对数回归、在神经网络 …**

坡度上多了。

结论

很容易理解神经网络的概念。使用诸如 Tensorflow 或 Pytorch 之类的 API 创建一个更容易。媒体或其他网站上有很多教程。但是完全理解它们是如何工作的是一项长期的工作,尤其是如果你像我一样,来自不同于数学或计算机科学的背景。****

恢复精神是件好事。知道基本面更好。能够轻松应用它们是最好的。坚持学习,一步一个脚印,你会好起来的。

我希望在我的下一篇文章“神经网络之旅(第一部分)——人工神经网络和感知机”中,或者在我将来写的任何其他论文中,能再次见到你。感谢您阅读快乐学习!****

神经网络系列之旅

** **

神经网络之旅(一)——人工神经网络和感知器

原文:https://towardsdatascience.com/a-journey-through-neural-networks-part-1-artificial-neural-network-and-perceptron-e970614b9cc7?source=collection_archive---------26-----------------------

从生物学到 Python 代码

纳斯蒂亚·杜尔希尔在 Unsplash 上拍摄的照片(已修改)

D eep-Learning,很大一部分人工智能,越来越受欢迎。随着计算机变得更加强大,数据的可用性增加,模型变得更加强大,使得深度学习蓬勃发展。深度学习参与了新冠肺炎疫苗和药物研究(纽约时报,2020 年)、蛋白质折叠与 AlphaFold (DeepMind,2020 年)、在监视 (Carnegie,2017 年)、自动驾驶汽车 (Intellias,2020 年)和其他领域,被许多人认为是下一个罗塞塔石碑。

本文提出了人工神经网络的概念以及一个著名的模型:单层感知器**。**

为避免信息过载,部分概念将不作完整描述,其他神经网络模型将在未来的一篇文章中解释(敬请关注!).尽管如此,每个主要概念都会指向一个 url 来更深入地讨论这个主题。

目录

  1. 什么是深度学习

— 1.1 大脑与深度学习—生物激励技术

— 1.2 大脑和深度学习—差异

— 1.3 大脑和深度学习——技术的极限

2.机器学习术语定义

— 2.1 监督、半监督和无监督学习

— 2.2 自变量和因变量

— 2.3 训练、验证和测试装置

3.感知器,前馈神经网络的祖先

— 3.1 简介

—3.2 理论步骤

—3.3 应用示例

— 3.4 极限值

4.Python 中感知器类的例子

5.结论

什么是深度学习?

你可能在两位数据科学家之间的模糊讨论中听说过深度学习,或者在网上一篇有点模糊的文章中听说过人工智能机器人将统治世界。好消息是,还没有……还没有。

深度学习是人工智能领域的一部分。牛津语言将其定义为一种“类型的机器学习,基于人工神经网络[……]用于从数据中提取逐步更高层次的特征。别担心,这个定义在本文的这一端会更清晰。

大脑和深度学习——生物启发技术

先说生物神经元。神经元是存在于大脑中的微小细胞,它们使用少量的电流和化学物质来相互交流。当你写、想、抓、喊、打或读著名的“你好! 克诺比将军”,大脑特定部位一定数量的神经元点亮,产生这种行为(图 1 )。

图一。当阅读“你好!克诺比将军……”(图片来自 Pixabay )

F.Y.I:照片中,“亮起来”的区域是韦尼克区和布罗卡区(左中—语言区)、视觉皮层(右下—视觉过程)和运动皮层的某些部分(左上—运动过程),这些区域在语音阅读时使用。

你可以想象,大脑中有相当数量的神经元。我们谈论的是数十亿。但是这些神经元并不是随机堆积在大脑中,希望一切顺利。事实上,你可以发现被称为神经元的众多结构,它们组织良好,具有特定的神经密度、长度以及与其他的连接。你也可以把大脑分成执行特定任务的全局区域。例如,顶叶皮层用于空间任务,而视觉皮层主要由 7 层结构组成(图 2 ),专门处理视觉过程。

图二。视觉皮层中不同层次的神经元。(背景图片来自维基百科)

深度学习领域复制了这种构造神经元的方式,将其简化,诞生了人工神经网络。这个想法是一样的,用 人工神经元,每层更深一层,处理信息最终输出一些东西(图 3 )。神经网络的这些节点层和每个网络专门从事一个独特的(或多或少复杂)过程像人脸检测、语法理解、基于文本的情感分类…

不管专业化程度如何,任何深度学习算法的目标都是减少预测输出和期望输出之间的误差。他们通过更新他们模型的一些参数来做到这一点,直到误差不再减少。

图 3。取一个句子,转换成数字,通过神经网络,输出一个新的句子!

大脑和深度学习——差异

其中生物神经元用于输出行为反应,例如举手人工神经网络用于以一种或另一种形式输出数据。可以用来将一张图片归类为“风景或“城市”,预测何时买卖股票,驾驶自动驾驶汽车等。

其中生物神经元从经验和重复中学习人工神经网络从数据中学习

最后,如果生物神经元没有被充分使用,它们就会死亡,如果人工神经元被认为在网络中不够有用,它们就会停止处理数据

大脑和深度学习——技术的极限

大脑是无限复杂的,我们还不能用人工神经网络来复制它。此外,生物神经元利用过程 s 等一个离子通道来诱发反应。我们不能用人工神经网络做同样的事情。为了模拟这些电和化学反应,人工神经网络(ann)纯粹且完全基于数学函数

如果您需要数学概念的更新,请随时查看文章“神经网络之旅(第 0 部分)——线性代数快速介绍”!在下一节中,将解释一些机器学习定义。

机器学习术语定义

深度学习是机器学习 ( 图 4 )的一个子领域。接下来的定义适用于机器学习领域,因此也适用于深度学习领域。

图 4。机器学习算法的类型

有监督的、无监督的和半监督的

当你使用机器学习算法时,你需要数据来训练你的模型。该数据可以有三种类型:标记、半标记和未标记 ( 图 5 )。

图 5。有标签和无标签数据的例子

正如你所看到的,在标记的数据中,我们知道一个 35 岁,拥有硕士学位和 8 年工作经验的人的工资是 55K 美元。**在未标注的数据中,**我们仍然知道年龄、经验和教育程度,但不知道工资是多少。如你所料,在半标签数据中,只有某些工资是已知的。

当你用标记的数据训练一个模型时(像图 6 中的回归,决策树,SVM…),你是在执行监督学习。这是最常见和最准确的学习类型。你的模型试图通过比较其预测输出和标签数据来减少其误差。该模型试图拟合数据。

图 6。回归作为监督学习算法。这条线尽可能地符合数据。

当你用未标记数据训练一个模型的时候,你就是在执行无监督学习。该模型检查您的数据点,并尝试猜测每个数据点是否属于特定类别(又名。集群如图图 7你不确定假设是否正确,因为你没有任何标签可以和这个假设做比较。这就是为什么无监督学习通常不太准确,人们倾向于避免它。这样做的好处是,你不需要有标签数据,这通常是更昂贵的获取。事实上,你需要有人来标记数据,检查任何错误…

例如,谷歌创造了一个游戏,要求玩家说出一幅图像是什么(一辆汽车?一个人?…).然后,它记录了答案,并设法创建了一个带有标签的数据集,其中包含基于这些数据的数百万条观察结果。但是花了很多年,花了很多钱。

图 7。基于 K-均值无监督算法的数据聚类

在半监督学习中,模型使用半标记数据。

更多关于半监督、无监督和监督学习。

自变量和因变量

在监督学习中,因变量是我们试图预测的数据。这是我们正在观察的事物,我们可以看到的行为。在我们之前的例子中,这是工资。但是它可能是一所房子的价格,取决于它的大小和房间数量。也可能是基于以前的成绩学习时间的的考试结果。在机器学习领域,我们称之为 y 向量

如果 y 指的是我们数据集中的标签数据,那么我们通常称之为 yₜᵣᵤₑ.

如果 y 指的是我们的模型预测的值,我们通常称之为 yₚᵣₑ (pre for prediction)

任何机器学习的目标都是缩小 yₜᵣᵤₑ和 yₚᵣₑ之间的差异!

在监督、非监督和半监督学习中,独立变量是我们的模型用来预测我们的向量y的值。这是可能对观察值产生 影响。年龄、工作经验和文凭可能会影响薪水。房间的数量和房子的表面积会对它的价格产生影响。在机器学习领域,我们称这个独立变量矩阵为特征** ,我们通常称之为x .**

注——在数学中,大写字母通常指矩阵。小写字母通常指向量。因此 y 是向量, x 是特征向量, X 是特征矩阵!

机器学习中更多的依赖于因变量和自变量。

训练、验证和测试装置

构建机器学习模型是一个迭代过程。你通常在你的模型中设置初始参数,并改变这些参数直到你找到最佳组合(又名。你的预测和你想要的 y 值之间的误差最小。

在这个迭代过程中,我们将数据分成 3 类 ( 图 8 ) :

  1. 一个 车组 ,反复使用调整参数
  2. 一个 验证集 ,允许您检查在训练阶段所做的调整是否正确
  3. 一个 测试集 ,这是验证我们模型的最后一步。一旦我们确定我们的模型运行良好,我们就把它应用到最后一组,这将给我们最终模型的准确性。

图 8。训练-验证-测试集中的数据拆分

为什么在构建模型时要将数据集分成 3 份?

以足球为例。**你执教一支名为学员的足球队。你的目标是赢得每一场比赛。你开始训练“学员”对抗另一个叫做“学员”的团队。

每场比赛后,你完善你的团队战略,你的位置和你的球员。现在到了不能再做调整的时候你赢了每一场对“火车人”的比赛。你可能会想:“哇,我的团队是最棒的,他们远远超过了其他团队,一定非常棒”。

但是在你和新球队的第一场比赛中,你输了。很难。你的团队只是被训练和打磨过,以便在与对手的对抗中表现出色。它特别学会了与这支球队对抗,而不是推广到其他球队。

用机器学习的术语来说,我们说模型过度拟合了数据。

你已经从错误中吸取了教训。你带着你的团队,对着“车队进行训练,但这次,在每次调整后,你都要对着另一个叫做 Bulk 的车队验证你的策略。此外,你决定找一个第三个对手,它将在所有训练-调整-验证阶段结束时用于进行实际测试。祝贺你,你已经创建了一个训练集(The Trainies),一个验证集(The Bulk)和一个测试集(The 无名的第三个对手)。这可以防止过度拟合和改变特定数据集的参数,而 允许将模型推广到其他数据。

将数据划分到训练-验证-测试集中总是一个好主意。比例由你决定,但通常是 70%/20%/10%。

关于数据集分割的更多信息。

感知器,所有前馈神经网络的祖先

"神经网络就像回归或 SVM 模型一样,是一种数学函数."安德烈·布尔科夫

介绍

正如你之前看到的,人工神经网络可以粗略地比作大脑。最初由神经生理学家和数学家在 1943 年(麦卡洛克和皮茨,1943 )作为命题逻辑单元(和/或)提出,人工神经网络的第一个实用想法后来由认知心理学家(罗森布拉特,1958 )提出。

*罗森布拉特基于大脑的生物结构创造了一个电子设备,它可以通过反复试验来学习输入。他诞生了感知器。这个网络由一个输入层、一个隐藏层和一个输出层 ( 图 1 )组成,隐藏层由**一个或多个称为 TLU 的节点(*阈值逻辑单元)组成。 TLU 由加权和函数和阶跃函数组成。

图一。在隐藏层有一个 TLU 节点的单层感知器

理论步骤

在感知器中,我们将输入 xᵢ乘以权重向量 wᵢ 。然后我们计算加权和 wₛᵤₘ=σxᵢwᵢ,这给我们一个单一的数字,并且通过一个阶跃函数 ( 图 2 )。该函数简单地将 Wₛᵤₘ与阈值θ进行比较。如果 Wₛᵤₘ ≥ θ,则阶跃函数输出 1。否则输出 0

图二。θ = 0.25 的阶跃函数 s(x)示例。如果 xᵢ ≥ θ,则 s(xᵢ) = 1.0。否则,s(xᵢ) = 0。

如果模型预测值 yₚᵣₑ 等于期望值 yₜᵣᵤₑ ,则模型成功。否则,使用两个公式更新权重和阶跃函数阈值:=wˡ⁻+学习率×(yₜᵣᵤₑ—yₚᵣₑ)×x***,θˡ =θˡ⁻ — 学习率× (Yₜᵣᵤₑ — yₚᵣₑ)*

应用示例

图 3。感知器网络步骤。在将输出与真实值 Y 进行比较之后,完成权重的更新

单层感知器(图 3 )取一个输入向量 x = [8,2.2,-0.1,3.0]。权重随机设置为w=【1.5,0.2,6,-0.40】。初始阶跃函数设置为θ = 0。我们希望我们的模型,基于 x 值,输出 yₜᵣᵤₑ = 0。让我们看看是不是这样:

  1. *计算加权和σxᵢwᵢ=(8 * 1.5)+(2.2 * 0.2)+(-0.1 * 6)+(3 -0.40)=10.64
  2. 将阶跃函数应用于 10.64 ,这给了我一个预测 yₚᵣₑ = 1
  3. yₜᵣᵤₑ= 0*****yₚᵣₑ= 1,因此 yₜᵣᵤₑ ≠yₚᵣₑ 。模型没有给我一个好的预测。我需要更新权重!*
  4. 用θ = 0,学习率 = 0.1, yₜᵣᵤₑ = 0, yₚᵣₑ = 1, w = [1.5,0.2,6,-0.40], x = [8,2.2,-0.1,3.0]:

5.计算新的加权和σxᵢδwᵢ= 2.85

6.将阶跃函数(参数θ = -0.1)应用于这个新结果,得到 yₚᵣₑ!= yₜᵣᵤₑ.我们的模型仍然是错误的…让我们再来一次:

7.用θ = -0.1,学习率 = 0.1, yₜᵣᵤₑ = 0, yₚᵣₑ = 1, w = [0.7,-0.02,6.01,-0.7]和 x = [8,2.2,-0.1,3.0]:

8.计算新的加权和σxᵢδwᵢ=-4.93

9.将阶跃函数(参数θ = -0.2)应用于这个新结果,得到 yₚᵣₑ = yₜᵣᵤₑ!

****模型现在成功预测了产量,给出了 x 和新的权重!如果模型不能给我一个正确的预测,我重复第 7 步到第 9 步,直到它给我一个正确的预测!

图片由at ree23从 Pixabay 获得

限制

问题是,单层感知器太简单了。比如不能解决异或(XOR)分类问题。此外,在感知机被开发出来的时候,计算能力有些有限,这导致了在随后的几十年里对神经网络的兴趣下降。后来,一个多层感知器模型解决了最初单层感知器的一些限制,它成为了第一个前馈神经网络**(这个网络将在**“神经网络之旅”** ) 的下一篇文章中介绍)。**

Python 中感知器类的简单例子

对于那些想看 python 中感知器的简单代码实现的人,请跟着做吧!

我们需要导入 **numpy** 这是一个著名的用于数据科学的库。它让我们做一些矩阵/向量/数学运算,如矩阵求逆、等。,是一个真正节约资源的包。我们将使用 numpy 计算我们的矢量化矩阵运算。查看** 此处 的 numpy 文档。**

好了,现在我们需要创建我们的感知器类**,它将包含拟合和预测方法。正如你所看到的,感知器类只有 4 个参数 : learning_rate, ,它定义了我们的感知器学习的速率n_iter, ,它定义了历元的数量(用于训练我们的模型的~循环), bias,它们让你选择是否要在你的模型θ中添加一个 bias 参数**

我们的权重被设置为 None** ,当我们调用 perceptron.fit()方法时**将被初始化。 init_weight 属性用于避免多个权重的初始化(它帮助我们的模型知道权重是否已经初始化)。****

注意——建议您创建一个 docstring 来解释参数和属性、类返回的内容以及可能出现的错误。方法也应该被适当地描述。

现在,让我们创建我们的拟合函数。这种方法用于将我们的模型与数据(又名。更新权重以适合数据)。正如我们对应用示例所做的那样,如果模型预测不正确,我们需要计算*=wˡ⁻+学习率× (Yₜᵣᵤₑ — yₚᵣₑ)× x 和ˡ =θˡ⁻ — 学习率× (Yₜᵣᵤₑ — yₚᵣₑ)*** 。在代码的第一部分,权重被初始化(有或没有偏差,取决于偏差参数值)。代码的第二部分循环通过由 n_iter 设置的迭代次数,并且在每个时期更新权重和θ(阶跃函数阈值)。通常,如果有偏差或没有偏差,代码略有不同,因此出现了 **if bias**条件。**

最后,预测方法,它使用 numpy 的惊人的**np.where()**方法实现了一个阶跃函数。它所做的只是将每个net_input(x)值与θ进行比较,如果net_input(x) ≥ θ,则将值设置为 1。否则,它会将net_input(x)设置为 0。****

这就对了。你得到了一个基础感知类**。你可以添加一个.score()方法,将其修改为 multioutput,将阶跃函数改为单位阶跃或成对阶跃函数……我会让你好好享受它的。**

结论

祝贺阅读本文。完整地、小块地、只有几个段落或只是欣赏图片,你现在对数据科学和深度学习的世界有了更多的了解。

深度学习的主题很复杂,你永远不要害怕花时间去理解这个主题。继续看文章,教程,应该就没事了!为了帮助你记住本文的主要部分,这里有一个总结:

记住神经网络是数学函数**,即需要数据来学习。你的目标是获取大量(带标签的)数据,**通过输入→隐藏→输出层。网络会学习(又名。每次给他相同的数据时,更新权重和偏置)多一点。这种重复传递相同数据的过程称为历元。最后,您通过传递您的测试数据集来验证您的模型,这将为您提供模型的最终准确性。

非常感谢您阅读这篇文章。祝您愉快!S tay 收听了“神经网络之旅”的下一篇文章,它将讨论前馈神经网络**!这些是更复杂的神经网络。**

神经网络系列之旅

**https://pierre-louis-weiss.medium.com/a-journey-through-neural-networks-part-0-fast-introduction-to-linear-algebra-e9f9ffa46513

参考

  1. m . AlQuraishi(2019 年)。CASP13 的 AlphaFold,生物信息学,35 (22),第 4862–4865 页。https://doi.org/10.1093/bioinformatics/btz422
  2. 布尔科夫,A. (2019)。一百页的机器学习书
  3. 费尔德斯坦,S. (2019)。AI 监控的全球扩张——卡内基国际和平基金会
  4. Géron,A. (2019)。使用 Scikit-Learn、Keras & Tensorflow 进行机器学习。第二版。奥赖利。
  5. Hastie,Tibshirani,r .,Friedman,J. (2017 年)。统计学习的要素。数据挖掘、推理和预测。第二版。施普林格。
  6. 海丁 V. (2020)。联网汽车平台的大数据:引擎盖下是什么?。互联汽车平台:用大数据驱动未来——Intellias。
  7. 麦卡洛克,W.S .,皮茨,w.《神经活动中固有观念的逻辑演算》。数学生物物理学通报 **5,**115–133(1943)。https://doi.org/10.1007/BF02478259
  8. 罗森布拉特 f(1958)。感知器:大脑中信息存储和组织的概率模型。心理复习65 (6),386–408。https://doi.org/10.1037/h0042519
  9. Sah P. (1996 年)。神经元中 ca(2+)-激活的 K+电流:类型、生理作用和调节。神经科学趋势19 (4),150–154。https://doi . org/10.1016/s 0166-2236(96)80026-9**

XGBoost 之旅:里程碑 1

原文:https://towardsdatascience.com/a-journey-through-xgboost-milestone-1-ff1be2970d39?source=collection_archive---------12-----------------------

设置背景

马丁·亚当斯在 Unsplash 上的照片

欢迎来到另一个文章系列!这一次,我们讨论的是 XGBoost (极限梯度提升)——21 世纪数据科学家中领先且最受青睐的机器学习算法。大多数人说 XGBoost 是一种赚钱的算法,因为它很容易超越任何其他算法,给出尽可能好的分数,并帮助其用户从数据科学竞赛中获得豪华现金奖励。

我们正在讨论的主题既广泛又重要,因此我们将通过一系列文章来讨论它。这就像一次旅行,对新人来说可能是一次漫长的旅行。我们一步一步地讨论整个话题。在这里,每个子主题被称为一个里程碑。当您完成所有里程碑的旅程时,您将在以下方面拥有丰富的知识和实践经验(使用 R 和 Python 有效地实现算法)。实现算法时,默认的编程语言是 Python。重要的地方我会偶尔用 R。

  • 里程碑 1: 设置背景
  • 里程碑 2: 使用 XGBoost 分类
  • 里程碑 3: 使用 XGBoost 进行回归
  • 里程碑 4: 通过交叉验证评估您的 XGBoost 模型
  • 里程碑 5: XGBoost 的超参数调优
  • 里程碑 6: 为 XGBoost 准备好数据
  • 里程碑 7: 用 XGBoost 构建管道

先决条件

我假设你已经熟悉了流行的 Python 库如 numpypandasscikit-learn 等并使用过 Jupyter NotebookRStudio 。建议对交叉验证、机器学习管道等机器学习技术以及决策树、随机森林等算法有很好的了解和理解。你也可以通过阅读以下我之前写的内容来刷新你的记忆。

  • 使用决策树训练回归模型
  • 随机森林——决策树的集合
  • 用简单的英语解释 k 重交叉验证
  • 具有机器学习流水线的多项式回归

设置编码环境

在进行任何机器学习任务之前,有必要设置您的本地机器。对于 Python 和 R 用户来说,获取 Python、R 和其他数据科学库最简单的方法就是通过 Anaconda 安装。对于数据科学来说,它是 Python 和 R 的最受欢迎的发行版。它包括所有的东西:数百个包,ide,包管理器,导航器等等。它还提供了安装新库的工具。您需要做的就是通过 Anaconda 终端运行相关的命令。要开始使用 Anaconda:

  • 转到https://www.anaconda.com/products/individual
  • 点击相关的下载选项
  • 下载安装文件后,双击它并按照屏幕上的说明在本地机器上安装 Anaconda

Anaconda 安装程序选项(图片来自 Anaconda 官网)

安装后,您可以在桌面上找到图标。双击它启动 Anaconda Navigator 。大多数常用的软件包,如 numpy、pandas、scikit-learn,都已经附带了 Anaconda。您不需要单独安装它们。但是,XGBoost 包不是一个内置的包。我们需要手动安装它。如果您使用的是 Windows 操作系统,

  • 就去https://anaconda.org/anaconda/py-xgboost
  • 只需复制conda install-c anaconda py-xgboost
  • 打开 Anaconda 导航器
  • 点击环境选项卡,然后点击向右箭头基本(根)
  • 从下拉菜单中选择打开端子
  • 现在应该会出现一个新窗口。粘贴conda install-c anaconda py-xgboost并点击回车。
  • 按照说明完成安装

通过 Anaconda 终端安装 XGBoost(图片由作者提供)

现在通过 Anaconda Navigator 启动 Jupyter 笔记本。形成 Jupyter 主页,打开 Python 3 笔记本并运行

将 xgboost 导入为 xgb

如果没有错误,您已经成功安装了 Python 的 XGBoost 包。现在您已经准备好使用 Python 中的 XGBoost 包了

注意:如果您使用的是 MacOS 或 Linux,

  • 前往https://anaconda.org/conda-forge/xgboost
  • 复制康达安装-康达锻造 xgboost
  • 重复以上相同的步骤

也可以在 Jupyter Notebook 中使用 XGBoost 配合 R 编程语言。要在 Jupyter 笔记本中安装并运行 R:

  • 启动 Anaconda 导航器
  • 要安装 R 语言和 r-essentials 包,选择环境创建一个新环境。点击创建
  • 将环境命名为“ R ”。在旁边,选择 Python 版本(此时为 Python 3.8)和 r,从下拉菜单中选择 r 。点击创建
  • 等到安装完成。点击箭头右边的 R
  • 从下拉菜单中选择打开端子
  • 现在应该会出现一个新窗口。运行康达安装-c r r-xgboost
  • 按照说明完成安装
  • 安装后,再次点击箭头右侧的 R选择用 Jupyter 打开笔记本选项。
  • 要为 R 语言创建新笔记本,在 Jupyter 笔记本菜单中,选择新建,然后选择 R
  • 在新笔记本中运行库(" xgboost")
  • 如果没有错误,您已经成功地为 R 安装了 XGBoost 包。现在,您可以在 Jupyter Notebook 中使用 XGBoost 包了。

要在 RStudio 中安装 XGBoost:

  • 启动 RStudio。
  • 转到工具标签,然后安装包
  • 在新窗口中,在字段中输入 xgboost
  • 点击安装

作者图片

**注意:或者,您可以在 R 终端中运行install . packages(" xgboost ")**在 RStudio 中安装 XGBoost。

XGBoost 所需的背景知识

决策树

决策树是一种非参数监督学习方法,能够发现数据中复杂的非线性关系。他们可以执行分类和回归任务。决策树是 XGBoost 模型的基础。

XGBoost 是一种由不同的机器学习模型组成的集成(组)方法。XGBoost 中组成系综(组)的各个模型被称为基础学习者。最常用的 XGBoost 基础学习器是决策树。这就是为什么决策树中的知识在学习 XGBoost 时如此重要。

阅读以下资源,了解更多关于决策树的信息。

  • 使用决策树训练回归模型
  • sci kit-learn 中的决策树分类器
  • sci kit-learn 中的决策树回归器

随机森林

随机森林 是当今最强大的机器学习算法之一。它是一种监督的机器学习算法,可用于分类(预测离散值输出,即一个类)和回归(预测连续值输出)任务。

像 XGBoost 一样,随机森林是决策树的集合(组)。在随机森林中,每棵决策树通过打包(引导聚集)进行组合。你可以通过阅读下面这篇我写的文章来了解更多关于装袋和随机森林的知识。

  • 随机森林——决策树的集合

还建议看一看 Scikit-learn 关于随机森林算法的官方文档。

  • sci kit-learn 中的随机森林分类器
  • sci kit-learn 中的随机森林回归器

弱学习者对强学习者

一个弱学习器是一个机器学习算法,比概率稍微大一点。例如,预测略低于 50%的决策树可以被认为是弱学习者。

一个强学习者,相比之下,是一个机器学习算法,它从数据中学习了很多,表现相当好。

装袋与增压

如果你读过我以前的文章,随机森林——决策树的集合,你现在对装袋已经很熟悉了。在随机森林中,每棵树都通过装袋组合在一起。

XGBoost 通过 boosting 合并树,这是一种从以前的树所犯的错误中学习的方法。Boosting 通过数百次迭代将弱学习者转化为强学习者。

摘要

在本文中,我们刚刚开始 XGBoost 之旅。有许多里程碑需要完成。我们还为整个旅程制定了一个大纲。

随着我们的进展,您将了解 XGBoost 如何在幕后工作。在接下来的里程碑中,您还将获得实现 XGBoost 算法的实践经验。所以,阅读接下来的文章很重要。我会尽快出版它们。敬请关注最新消息!

感谢阅读!

本教程由 鲁克山·普拉莫迪塔数据科学 365 博客作者设计创作。

阅读我在 https://rukshanpramoditha.medium.com的其他文章

2021–03–02

XGBoost 之旅:里程碑 2

原文:https://towardsdatascience.com/a-journey-through-xgboost-milestone-2-f3410109be5a?source=collection_archive---------14-----------------------

使用 XGBoost 分类

马丁·亚当斯在 Unsplash 上的照片

欢迎来到**【XGBoost 之旅】**系列第二篇。今天,我们将在“心脏病”数据集上构建我们的第一个 XGBoost 模型,并制作一个小的(但有用的)web 应用程序来与最终用户交流我们的结果。以下是我们今天讨论的话题。

我们讨论的话题

  • 形成一个分类问题
  • 识别特征矩阵和目标向量
  • 构建 XGBoost 模型(Scikit-learn 兼容的 API)
  • 描述**【准确性】【ROC 曲线下面积】**指标
  • 解释 XGBoost 分类器超参数
  • 构建 XGBoost 模型(与 Scikit-learn 不兼容的 API)
  • XGBoost 的数据矩阵
  • Shapash Python 库为我们的 XGBoost 模型创建一个小的 web 应用程序
  • 做一些奇特的想象

先决条件

在继续之前,请确保您已经阅读了 XGBoost 系列的第一篇文章(XGBoost 之旅:里程碑 1 —设置背景 )。这将帮助您设置自己的计算机来运行和试验这里讨论的代码。除此之外,我还假设您对 Python Scikit-learn ML 库有基本的了解。

我们开始吧!

心脏病数据集

今天,我们在“心脏病”数据集上构建了我们的第一个 XGBost 模型(此处下载)。下图显示了数据集的前 5 行。

“心脏病”数据集的前 5 行(图片由作者提供)

下图包含由 Pandas info() 方法返回的数据集的信息。

“心脏病”数据集的有用信息(图片由作者提供)

数据集没有缺失值。所有的值都是数字。因此,不需要预处理步骤,数据集就可以使用了。该数据集有 303 个观测值和 14 个特征(包括“目标”列)。

现在,我们定义我们的问题陈述。

问题陈述

基于年龄、性别、cp、…、thal,我们想要预测一个给定的人(一个新的实例)是否患有心脏病(1 级)。这是一个分类问题,因为它的结果是一个离散值(一个已知的类)。我们用来解决这个分类问题的算法是 XGBoost (XGBClassifier)。因此,我们将为这个分类问题构建一个 XGBoost 模型,并使用模型的评估指标(如准确性和 ROC 曲线下的面积)来评估它在测试数据(看不见的数据/新实例)上的性能。

我们提供特性矩阵和目标列作为 XGBoost 模型的输入数据。

  • **特征矩阵:**称为 X 。包括除“目标列”之外的所有列。该矩阵可以是熊猫数据帧或二维 numpy 阵列的形式。
  • **目标矢量:**称为 y 。包括数据集的“目标列”。这个向量可以是熊猫序列或一维 numpy 数组的形式。

之后,XGBoost 模型(带有用户自定义参数)会根据 Xy 学习规则。基于这些规则,我们对新的或未知的数据进行预测。

让我们通过编写 Python 代码构建我们的第一个 XGboost 模型来获得实践经验。

构建 XGboost 模型

这里,我们讨论两种情况。

  • 使用 Scikit-learn 兼容 API 构建模型
  • 使用 XGBoost 自己的非 Scikit-learn 兼容 API 构建模型

随着我们的进展,您将会看到这两个 API 之间的区别。

使用 scikit-learn 兼容 API 构建模型

构建 XGBoost 模型最简单的方法是使用它的 Scikit-learn 兼容 API。**“sci kit-learn 兼容”**表示您可以使用 Scikit-learn 。fit() /。用 XGBoost 预测() 范式。如果你以前用过 Scikit-learn,这里就没有什么新东西了。让我们编写完整的 Python 代码来构建 XGBoost 模型。

等到加载 Python 代码!(代码片段-1)

上述代码段的输出是:

作者图片

准确率:85.2% —这是测试集上所有正确预测的总和除以测试集中的观察总数。我们的模型正确地预测了 100 次观察中的 85 次。这个准确度分数对于初始模型来说并不差,因为我们还没有用最优超参数组合来调优模型(模型调优部分将在 XGBoost 系列的第 5 篇文章中讨论)。

**注:**如果您在 train_test_split() 函数中为 random_state 参数设置不同的整数值,您会得到略有不同的精度分数。我们将在 XGBoost 系列的第 4 篇文章中解决这个问题。

ROC 曲线下面积:91% — ROC 是概率曲线,曲线下面积(AUC)是类别可分性的度量。AUC 表明模型能够区分多少类别。AUC 越高,模型预测 0 为 0,1 为 1 越好。非常差的模型的 AUC 接近 0。如果一个模型的 AUC 为 0.5,则该模型根本没有类别分离。91%的 AUC 对于我们的模型来说是一个非常好的值。它有很强的区分这两个阶级的能力。

让我们一行一行地解释上面的 Python 代码。

首先,我们导入所有必要的带有社区标准约定的库( xgboost →xgbnumpy →np 等)。然后我们用 Pandas read_csv() 函数加载数据集。数据集在同一个工作目录中。然后我们创建 Xy 。通过混洗数据集,我们为 Xy 创建了训练和测试集。这是因为我们需要用训练集训练我们的模型,用测试集评估我们的模型——新的或看不见的数据。我们从不使用训练阶段使用的相同数据来测试我们的模型。如果您这样做,您将获得更好的准确性分数,但是模型将无法对新的或看不见的数据进行归纳,并且对新的或看不见的数据的预测将不会准确。

然后我们从 XGBClassifier() 类创建一个 XGBoost 对象(称为 xgb_clf )。用于分类的 XGBoost 模型被称为 XGBClassifier 。我们在 XGBClassifier() 类中指定了 6 个超参数。

  • 这里,XGBoost 使用决策树作为基础学习器。通过设置 max_depth=3 ,每棵树将进行 3 次分割并在那里停止。
  • n_estimators=100: 集合中有 100 棵决策树。
  • **objective = ' binary:logistic ':**我们的模型中使用的损失函数的名称。二进制:逻辑是 XGBoost 中二进制分类的标准选项。
  • booster='gbtree': 这是 ML 模型在每一轮 boosting 中使用的基础学习者类型。**‘GB tree’**是 XGBoost 默认基础学习者。使用 booster='gbtree ',XGBoost 模型使用决策树,这是非线性数据的最佳选择。
  • n_jobs=2: 使用处理器的两个内核进行并行计算来运行 XGBoost。
  • random_state=1: 控制创建树的随机性。您可以使用任何整数。通过为 random_state 指定一个值,您将在代码的不同执行中获得相同的结果。

使用上述超参数创建模型后,我们使用训练集对其进行训练。然后,我们在测试集上进行预测。最后,我们使用两个评估指标评估该模型——准确性和 ROC 曲线下面积。

接下来,我们通过运行 XGBoost 自己的非 Scikit-learn 兼容 API 获得了相同的结果。

使用 XGBoost 自己的非 Scikit-learn 兼容 API 构建模型

创建 XGBoost 模型的另一种方法是使用 XGBoost 自己的非 Scikit-learn 兼容 API。**“不兼容 Scikit-learn”**表示您不能使用 Scikit-learn 。fit() /。predict()范型和其他一些 Scikit-learn 类。让我们编写完整的 Python 代码来构建 XGBoost 模型。

等到加载 Python 代码!(代码片段-2)

上述代码段的输出是:

准确率评分和上一个一模一样!这个 API 的主要区别在于,我们显式地创建了一个名为 DMatrix 的特殊数据结构,这是一个由 XGBoost 使用的内部数据结构。函数的作用是:将类似数组的对象转换成矩阵。在 scikit-learn 兼容 API for XGBoost 中,这种转换发生在后台,我们不需要显式创建 DMatrices。使用 DMatrices 时,该算法针对内存效率和训练速度进行了优化。

该 API 的其他不同之处包括:

  • 为了训练模型,我们使用 XGBoost 自带的 train() 函数。之前,我们使用 Scikit-learn fit() 方法来训练模型。
  • 预测以概率的形式返回。我们需要将它们转换成类(整数:0 和 1)
  • 对于这个 XGBoost 模型,我们不能使用一些 Scikit-learn 函数。例如,我们不能将 plot_roc_curve() 函数与使用此 API 创建的 XGBoost 模型一起使用。

注意:只要有可能,我推荐你使用 XGBoost scikit-learn 兼容的 API。这是因为它的语法非常一致且易于使用。除此之外,我们还可以充分利用所有的 Scikit-learn 库函数。

创建一个小型 web 应用程序,与最终用户讨论 XGBoost 模型

在这里,我们利用了 Shapash Python 库的优势,该库旨在使机器学习模型能够被没有太多技术知识但有兴趣看到可视化结果的最终用户解释和理解。只需几行代码(也许 5 或 6 行),我们就可以毫不费力地用 Shapash 做出一些奇特的可视化效果。让我们开始吧。

**注:**要了解更多关于 Shapash Python 库的信息,请阅读其官方文档。

装置

只需在 Anaconda 命令提示符下运行以下命令来安装 Shapash 。安装后,你可以用 Python 在你的 Jupyter 笔记本中使用 Shapash

pip install shapash --user

制作网络应用

使用 XGBoost scikit-learn 兼容的 API 创建 XGBoost 分类模型后(运行上面的代码片段-1 ,执行以下代码创建 web 应用程序。 xpl 对象的 compile() 方法将 X ( X_test )、XGboost 模型( xgb_clf )的测试数据和预测作为与 X_test 具有相同索引的熊猫序列。预测的数据类型(y_pred_as_series)应该是 integer 或者 float(我们需要明确定义为 dtype=np.int 或者 dtype=np.float)。否则,您将得到一个错误。

等到加载 Python 代码!(代码片段-3)

运行后,web 应用程序链接现在应该出现在 Jupyter 输出中(作为下图中的第二个链接)。点击它启动网络应用程序。

作者图片

你可以看到一些奇特的可视化效果(特性重要性图,特性贡献图,局部解释图)。最棒的是你可以和这些情节互动。观看下面 50 秒的视频,了解我如何在这个 web 应用程序中与情节进行交互。

XGBoost Web 应用程序演示(视频由作者提供)

在这个 web 应用程序中,您甚至可以下载单个地块并将其保存在本地计算机上。你可以根据你试图解决的问题来解释它们。例如,您可以使用特征贡献图来回答诸如“我的模型中的特征如何影响预测?”。

以下是从 Shapash 库中创建的一些图。

特征重要性图

作者图片

特征贡献图(分类变量的小提琴图)

作者图片

特征贡献图(连续变量的散点图)

作者图片

为了完成这篇文章,我将可视化一个单独的决策树 XGBoost 模型中的基础学习者。下面的代码显示并保存了模型中的第一个决策树(索引为 0)。

等到加载 Python 代码!(代码片段-4)

输出是:

可视化 XGBoost 基础学习者(图片由作者提供)

摘要

在 XGBoost 文章系列的第一篇文章中,我们刚刚开始了 XGBoost 之旅。在那里,我们设置了在我们自己的计算机上运行 XGBoost 的环境。更进一步,在本文中,我们用 XGBoost 完成了一个分类任务。我们已经讨论了支持 XGBoost 的两个 API 之间的区别。不仅如此,我们还制作了一个小的(但是有用的)web 应用程序来与最终用户交流我们的模型。最棒的是,我们只编写了 5 行额外的代码来创建 web 应用程序。web 应用程序非常具有交互性。添加文章结尾,我添加了一些花哨的可视化。

下一步是什么?我们还没有讨论 XGBoost 的数学背景。在下一篇文章中,我将使用 XGBoost 构建一个回归模型。在那里,我还会讨论“制定 XGBoost 的学习目标”等数学背景。在本文中,更多的重点放在了技术和编码部分,这对于在真实世界数据集上实现该算法非常有用。现在,您将拥有实现算法和为最终用户可视化结果的实践经验。

请继续关注 XGBoost 系列的下一篇文章的更新!

感谢阅读!

本教程由 鲁克山·普拉莫迪塔数据科学 365 博客作者设计创作。

在 https://rukshanpramoditha.medium.com阅读我的其他文章

2021–03–07

XGBoost 之旅:里程碑 3

原文:https://towardsdatascience.com/a-journey-through-xgboost-milestone-3-a5569c72d72b?source=collection_archive---------4-----------------------

使用 XGBoost 进行回归

马丁·亚当斯在 Unsplash 上的照片

欢迎来到**【XGBoost 之旅】系列第三篇。到目前为止,我们已经完成了 2 个里程碑。到目前为止,我们所做的是,基本上,我们讨论了如何设置系统以在我们自己的计算机上运行 XGBoost,我们还使用 XGBoost 完成了一项分类任务,并创建了一个小型(但有用)的 web 应用程序,以便与没有太多技术知识的最终用户交流我们的结果。如果你是第一次来这里,我推荐你阅读“XGBoost 之旅”**系列的前两篇文章。以下是链接。

  • 【XGBoost 之旅:里程碑 1 —设置背景
  • XGBoost 之旅:里程碑 2 —使用 XGBoost 分类

更具体地说,今天,我们将使用 XGBoost 对“波士顿房价”数据集执行回归任务。以下是我们今天讨论的话题。

我们讨论的话题

  • 形成回归问题
  • 构建 XGBoost 回归模型(Scikit-learn 兼容的 API)
  • 描述**‘RMSE’‘R 平方’**指标
  • 创建预测误差和残差图
  • 解释 XGBoost 回归变量超参数
  • XGBoost 的目标函数
  • 将 L2 正则化应用于我们的 XGBoost 模型

波士顿房价数据集

“波士顿房价”数据集是 Scikit-learn 中的内置数据集。要访问数据,你需要做的就是调用 load_boston() 函数,并将它赋给一个名为 data 的变量,这是一个 Python 对象。然后我们调用那个对象的各种属性得到 X (特征矩阵) y (目标向量)和列名。当我们编写代码时,您将看到如何做到这一点。现在,只需看看“波士顿房价”数据集的结构和可变信息。

“波士顿房价”数据集的前 5 行(图片由作者提供)

下图包含由 Pandas DataFrame info() 方法返回的数据集的变量信息。

“波士顿房价”数据集的可变信息(图片由作者提供)

数据集没有缺失值。所有的值都是数字。因此,不需要预处理步骤,数据集就可以使用了。该数据集有 506 个观测值和 13 个特征(不包括“目标”列)。目标列(可以使用 数据目标属性访问)包含中值房价。要找到关于这个数据集的更多信息,请阅读它的文档。

定义问题

基于 CRIM,ZN,…,LSTAT,我们希望预测给定房屋(新实例)的房价中值。这是一项回归任务,因为其模型预测的是连续值输出(房价是连续值)。

我们用来解决这个回归问题的算法是 XGBoost。用于回归的 XGBoost 模型称为 XGBRegressor 。因此,我们将为这个回归问题构建一个 XGBoost 模型,并使用均方根误差(RMSE) 和 **R 平方(R-决定系数)**来评估它在测试数据(看不见的数据/新实例)上的性能。我们还将使用各种图形技术,如预测误差图、残差图和残差分布,来评估回归模型并验证其假设。

让我们通过编写 Python 代码在“Boston house-prices”数据集上构建 XGboost 回归模型来获得实践经验。

构建 XGboost 回归模型

这里,我们使用 XGBoost Scikit-learn 兼容 API。**“sci kit-learn 兼容”**表示您可以使用 Scikit-learn 。fit() /。predict()****paradigm和其他几乎所有的 Scikit-learn 类。这是代码。

等到加载 Python 代码!(代码片段-1)

上述代码段的输出是:

代码片段-1 的输出

请注意我是如何使用几个 print() 函数在同一个输出中打印多个值和图形的。为了创建预测误差和残差图,我只使用了两行代码。在这里,我利用了 Yellowbrick Python 库。Yellowbrick 是一个优秀的机器学习可视化库。我推荐您使用它,因为您只需一两行代码就可以制作精美的可视化效果,而且它与 Scikit-learn 完全兼容。如果你感兴趣的话,读一下它的文档。

我们的模型返回测试数据的 RMSE 值为 2.86(y 单位)。这个值吗?为了找到答案,我们来看看目标列(房价)的一些统计方法。

目标列的统计度量

标准偏差为 9.197,平均值为 22.53,我们得到的 RMSE 值(2.86)非常好。RMSE 值越小,模型越好。平均而言,我们模型的价格预测值与实际值相差 2.86 个单位

我们模型的 r 值是 0.89。这意味着我们的模型捕捉到了房价中观察到的 89%的可变性,另外 11%是由于其他一些因素,当然还有随机性!

作为图解法,我们可以使用预测误差图、残差图和残差分布来评估我们的回归模型并验证其假设。

  • **预测误差图:**我们可以看到大部分的点都在一条直线上。我们可以将该图与 45 度线进行比较,在 45 度线上,预测与模型完全匹配。总的来说,预测是跟随着实际房价的。
  • **残差图:**我们看不到预测和残差之间的任何模式。因此,我们可以验证残差是不相关或独立的。这对我们的模式来说是个好兆头。
  • **残差分布图:**通过查看这个图,我们可以验证残差(实际值-预测值)近似正态分布。

我们创建的模型遵循标准回归假设。

现在,是时候描述一下 XGBRegressor() 超参数了。我们指定了 7 个超参数。

  • max_depth=3: 这里,XGBoost 使用决策树作为基础学习器。通过设置 max_depth=3 ,每棵树将进行 3 次分割并停止在那里。
  • n_estimators=100: 集合中有 100 棵树(个体模型)。
  • **objective = ' reg:squarederror ':**我们的模型中使用的损失函数的名称。reg:squarederror 是 XGBoost 中回归的标准选项。你也可以使用 reg:linear 得到相同的结果。但是 reg:linear 现在已经被弃用,在未来的版本中会被删除。
  • booster='gbtree': 这是 ML 模型在每一轮 boosting 中使用的基础学习者类型。**‘GB tree’**是 XGBoost 默认基础学习者。使用 booster='gbtree ',XGBoost 模型使用决策树,这是非线性数据的最佳选择。
  • n_jobs=2: 使用处理器的两个内核进行并行计算,以运行 XGBoost。
  • random_state=1: 控制创建树的随机性。您可以使用任何整数。通过为 random_state 指定一个值,您将在代码的不同执行中获得相同的结果。
  • learning_rate=0.05: 每轮提升缩小树的权重。降低 learning_rate 可防止过度拟合。

XGBoost 的目标函数

XGBoost 的目标函数决定了预测值与实际值的差距。我们的目标是找到一个为目标函数提供最小值的模型。XGBoost 的目标函数由两部分组成:

  • 损失函数
  • 正则化项

数学上,这可以表示为:

XGBoost 的目标函数(图片由作者提供)

损失函数

对于回归问题,损失函数是均方误差(MSE) 。对于分类问题,损失函数是对数损失。XGBoost 中常见损失函数的超参数值为:

  • reg:squarederrorreg:linear —用于回归
  • 二元:逻辑 —用于二元分类
  • multi:softprob —用于多类分类

正则项

正则化可以定义为**【对模型复杂度的控制】**。我们需要创建一个既准确又简单的模型。正则项是一个惩罚项,用于防止模型过度拟合。XGBoost 与其他基于树的模型的主要区别在于,XGBoost 的目标函数包含一个正则项。XGBoost 中的正则化参数是:

  • **伽玛:**默认为 0。小于 10 的值是标准值。增加该值可以防止过度拟合。
  • reg_alpha: 叶子权值上的 L1 正则化。较大的值意味着更多的正则化,并防止过度拟合。默认值为 0。
  • reg_lambda: 对叶权重的 L2 正则化。增加该值可以防止过度拟合。默认值为 1。

将 L2 正则化应用于我们的 XGBoost 模型

现在,我们将 L2 正则化应用于上面创建的 XGBoost 模型。我们将为 reg_lambda 超参数尝试 4 个不同的值。我们可以用一个简单的来完成循环。

等到加载 Python 代码!(代码片段-2)

输出是:

L2 正则化对 XGBoost 模型的影响

这里,我们可以注意到,随着“λ”值的增加,RMSE 增加,R 平方值减少。

摘要

到目前为止,我们已经完成了 XGBoost 系列的 3 个里程碑。今天,我们用 XGBoost 的 Scikit-learn 兼容 API 执行了一个回归任务。正如我们在分类问题中所做的那样,我们也可以使用 XGBoost 的非 Scikit-learn 兼容 API 来执行回归。

在下一篇文章中,我将讨论如何使用 XGBoost 执行交叉验证。敬请关注最新消息!

感谢阅读!

本教程由 鲁克山·普拉莫迪塔数据科学 365 博客作者设计创作。

在 https://rukshanpramoditha.medium.com阅读我的其他文章

2021–03–11

XGBoost 之旅:里程碑 4

原文:https://towardsdatascience.com/a-journey-through-xgboost-milestone-4-303545a40fb0?source=collection_archive---------20-----------------------

通过交叉验证评估 XGBoost 模型

马丁·亚当斯在 Unsplash 上的照片

你好。这是我们 XGBoost 旅程的第四个里程碑。到目前为止,我们基本上讨论了用 XGBoost 执行回归和分类任务。如今,通过交叉验证的 XGBoost 模型评估得到了更多的重视。还记得在“用 XGBoost 分类”(里程碑 2)的文章中,如果为 train_test_split() 函数中的 random_state 参数设置不同的整数值,会得到略有不同的准确率分数吗?今天,我们将通过交叉验证来解决这个问题。

基本上,我们将使用兼容 Scikit-learn 和不兼容 Scikit-learn 的 API 执行交叉验证。但是,最后一件事!在继续之前,请务必阅读以下文章,它们是今天内容的先决条件。那些文章中讨论的概念非常重要,我强烈推荐你阅读它们。

先决条件

  • 【XGBoost 之旅:里程碑 1(设置背景)
  • XGBoost 之旅:里程碑 2(用 XGBoost 分类)
  • 用简单的英语解释 k 倍交叉验证

我们开始吧!

使用 Scikit-learn 兼容 API 评估 XGBoost 模型

这里,我们使用相同的“心脏病”数据集(在此下载)和在“XGBoost 之旅:里程碑 2(用 XGBoost 分类)”文章中构建的分类模型。

**“sci kit-learn compatible”表示这里我们可以使用 sci kit-learncross _ val _ score()**函数与 XGBoost 进行交叉验证。

让我们来写 Python 代码。

等到加载 Python 代码!(代码片段-1)

输出是:

代码片段-1 的输出(图片由作者提供)

0.818 的平均精度更稳健,是我们模型的良好性能估计。请注意,这里我们执行了 10 重交叉验证,并为我们的模型获得了 10 个不同的精度值。

这里我们只输入 Xy 。我们不需要把数据集拆分为 X_trainy_trainX_testy_test 。在交叉验证中,分割是根据我们在 cv 中指定的折叠数在内部完成的(这里是 10)。使用交叉验证可以保证我们的 XGBoost 模型的准确性分数不会受到随机数据分割过程的太大影响。如果我们只使用 train_test_split() 函数而不进行 t 交叉验证,那么根据我们在 train_test_split() 函数中提供的 random_state ,准确率得分会有很大的差异。在交叉验证中,使用 10 次(cv=10)这样迭代的平均值来计算精确度!

在 k-fold 交叉验证中,我们假设数据集中的所有观察值都以数据没有偏差的方式很好地分布。这就是为什么我们首先使用混洗函数混洗数据集。

接下来,我们使用 XGBoost 非 Scikit-learn 兼容的 API 执行相同的交叉验证过程。

使用 XGBoost 非 Scikit-learn 兼容 API 评估 XGBoost 模型

用 XGBoost 执行交叉验证的另一种方法是使用 XGBoost 自己的非 Scikit-learn 兼容 API。“与 Scikit-learn 不兼容”意味着这里我们不使用 Scikit-learncross _ val _ score()函数,而是使用 XGBoost 的 cv() 函数和显式创建的d matrix。A DMatrix 是由 XGBoost 使用的内部数据结构。

让我们来写 Python 代码。

等到加载 Python 代码!(代码片段-2)

输出是:

交叉验证结果(作者图片)

在这里,您将得到一个格式良好的输出,其中包含许多细节。XGBoost 的 cv() 函数返回每个折叠的训练和测试错误。你可以用 1 个误差得到精度。这里的平均准确度分数与之前的分数非常接近。有一些小的不同,因为在混排数据时涉及到随机性。

摘要

今天,我们讨论了交叉验证过程,它可以用来以一种更好的方式评估我们的 XGBoost。我们已经用 XGBoost 的两个不同的 API 进行了交叉验证。我们在两个 API 中获得了相同的准确度分数,但是 XGBoost 自己的非 Scikit-learn 兼容 API 获得了更多的细节。

在下一篇文章中,我们将讨论如何调优 XGBoost 的超参数。交叉验证也包括在内。因此,如果你仍然不太熟悉交叉验证,请阅读我的“用简单英语解释的 k 重交叉验证”文章。下次见!

感谢阅读!

本教程由 鲁克山·普拉莫迪塔数据科学 365 博客作者设计创作。

阅读我在 https://rukshanpramoditha.medium.com的其他文章

2021–03–19

快速强化学习之旅

原文:https://towardsdatascience.com/a-journey-towards-faster-reinforcement-learning-1c97b2cc32e1?source=collection_archive---------11-----------------------

提示和技巧

由于并行化和 Numba,本文可以在几分钟内为您节省数小时

让我们为我们的飞机研发项目增加动力(来源: Pixabay

从伊卡洛斯燃烧自己的翅膀到莱特兄弟翱翔天际,人类花了几千年才学会飞行,但人工智能要多久才能做到?

在这篇文章中,我们将回顾强化学习的一个实践方面:如何让它更快!我进入强化学习的旅程是一次奇妙的经历,从理论知识到应用实验。然而,有一件事真的让我很苦恼,那就是在尝试另一个想法来改进我的项目之前,我不得不等待代理人完成培训。所以,有一天我决定想办法让整个过程更快。这篇文章的目标是作为加速你的 RL 项目的不同方法的指南。要做到这一点,我们将看到我如何加速我的飞机项目,以超过 2 倍的原始速度!

这篇文章是一系列文章的一部分,我在这些文章中写了我使用人工智能让飞机飞起来和投入强化学习的旅程(第 1 部分是这里的和这里的和第 2 部分是和)。尽管我们将使用前几篇文章中创建的一些材料,这篇文章可以独立阅读。

介绍

强化学习的效率

RL 是机器学习的一个强大而通用的分支。它允许模型学习,不是通过例子甚至数据,而是通过与环境的交互来优化决策。这个过程的结果称为策略。

机器学习技术的主要类别。图片由作者提供,图标来自平面图标

这种特殊性允许强化学习用于其他机器学习(ML)技术失败或甚至不适用的地方。然而,当涉及到 RL 和 ML 都可以应用的领域时,后者往往更有效。即使在其他应用中,RL 仍然可能相对较慢。为了解决这个问题,我们需要提高 RL 的效率。

在这种情况下,效率是什么?

效率可以通过两个不同的方面来定义:

  • 数据效率:在我们的目标达到之前,我应该有多少数据点(对于 ML)或与环境的交互(对于 RL)?
  • 时间效率:给定固定的硬件和适量的数据,需要多长时间才能达到我们的目标?这包括收集数据和用它来训练模型(对于 ML)或代理(对于 RL)。

从现在开始,我们将只讨论 RL 的效率,并尝试看看如何提高它。

RL 中更大的效率成本往往来自于“数据”。数据收集(在在线 RL 中与环境交互,或在离线 RL 中收集数据用于后续计算)的难度/成本可能会因设置不同而有很大差异。当获得像 Atari 2600 games 这样的模拟时,收集数据是相当简单的,但当涉及到控制 Space X 火箭时,成本会迅速上升(如果你打算让它坠毁几次,成本会更高)。由于现实世界的强化学习伴随着它的问题,今天我们将从讨论基于模拟的强化学习开始。

运行一个 Atari 2600 游戏听起来比破坏几个这样的游戏便宜,然而,他们确实使用 RL 着陆!来源: Giphy

即使在模拟中,一些比另一些更有效。你的基本 2D 健身房环境,如 CartPole,在每秒发作次数方面将比 Atari 2600 游戏更有效。然而,模拟确实有一个主要的优势,那就是它实际上和驱动机器的电力一样便宜。因此,当使用模拟进行在线 RL(代理与模拟实时交互)时,数据效率和时间效率变得错综复杂,并融合到一个共同的目标中:尽可能快地与环境交互。

这个目标可以转化为环境代码的优化。为了进一步讨论这个主题,我们将使用我在《RL:我的飞机模拟》中开始旅程以来一直工作的环境。

培训环境

我们将在 100 集里评估代理商的时间效率。在选定的设置中,称为水平飞行,代理人学习尽可能直线驾驶飞机,尽可能接近给定的目标高度。

机长正试图将飞机保持在水平线代表的高度!作者图片

代理的行动不能缩短或延长发作的持续时间。即使我们不关心本文中的代理性能,代理的实际训练时间仍然必须包括在内,以便对训练时间和加速可能性有一个正确的了解。我们将使用的代理是近端策略优化(简称 PPO)。

摘要

在本文中,我们将讨论两种提高 RL 项目速度的方法:

  • 环境,使用代码加速
  • 代理,使用并行化和超参数

在本文中,我们将不讨论所选代理在该过程中的影响。在描述不同药剂的文献中已经很好地测量和讨论了效率。此外,请注意,因为我们只对本文中的执行速度感兴趣,所以我们只培训代理,而不评估其在奖励或目标实现方面的表现。

1.执行时间测量

如何精确测量执行时间?

现在,为了优化我们的代码,让我们看看它的每个部分运行需要多长时间(也称为分析)。然而,测量执行时间并不像听起来那么简单。我们可以测量代码开始和执行结束之间的时间,然后称之为一天…但是这只有在没有其他进程运行的情况下才有可能,因为任何其他进程都可能影响执行性能。因此,如果我们想要精确地测量执行时间,并且能够比较它们来量化改进,我们首先需要隔离执行。在个人电脑上做这些太难了,因此我们将在没有其他进程运行的专用 AWS EC2 实例(基于云的专用计算机)上运行这些测试,这将保证我们在 iso 可用资源上运行代码。对于第一组实验,我们将使用一个 t2.micro ,它是 AWS 自由层的一部分。

项目结构

这个项目由两个主要部分组成,即环境、代理

标准 RL 框架。作者图片

代理相对简单,因为它主要由库管理:stable-baselines3。

环境是一个定制的飞机环境,在本文中开发,遵循 OpenAI gym 模板。它被细分为多个子功能,我们将在深入分析时发现这些子功能。

这两部分通过以下方式进行通信:

在一个事件中,代理人和环境之间相互作用的简化视图。作者图片

代理观察环境的当前状态,并根据该观察及其当前策略选择动作。该动作随后被环境接收,环境据此计算下一个状态和奖励。根据这些信息,代理可以更新其参数以提高未来的性能。

执行时间分析

让我们从测量开始:我们收集 100 集以上的执行时间,并对不同部分之间平均花费多少时间的结果进行平均:动作选择(基于当前状态和当前策略选择动作),展开(收集数据或与环境交互),以及实际学习(根据新的体验更新模型权重)。“其他”类别只是将所有其他操作重新分组,这些操作对于理解培训没有用处,并且是过程的一小部分(调用回调、更新信息缓冲区和检查动作格式)。

图 1:RL 过程中时间消耗的重新分配。作者图片

环境(橙色)大约占计算成本的三分之一,而代理成本(绿色的动作选择和蓝色的参数更新)几乎占了其余的三分之一。让我们从与环境的互动开始我们的优化之旅。我们不会在这里讨论实现的细节,您只需要知道在健身房环境中有 3 个主要部分:

  • 步骤:计算行动对环境的影响
  • 奖励:它计算奖励
  • 终端:评估代理是否已经到达终端状态

现在让我们测量这些不同部分的执行时间,并对它们进行分析。

图 2:时间消耗在“环境步骤”中的重新分配(上图(1)中的最高消耗)。作者图片

计算成本的主要来源是与环境的推理(蓝色的“环境步时间”)。让我们仔细看看这一部分,它计算行动对环境的影响。再说一次,如果你不想的话,没有必要去了解函数做什么的细节。对不同功能的描述可以在文章的最后找到,或者在前面的文章中找到。就像之前一样,让我们看看计算时间是如何在这些不同的任务中分配的:

图 3:时间消耗在“包络步骤时间”中的重新分配(上图(2)中的最高消耗)。作者图片

我们可以看到空气动力学计算(compute_dyna)的明显优势,这是命令对飞机状态(位置、速度、加速度、俯仰角等)影响的计算。然而,其余的操作仍然占所花费时间的 50%,前 3 位的成本是推力和角增量以及马赫数计算。这种成本主要来自于“复杂”函数的使用,如 NumPy 的 clip 和 norm 函数。

让我们深入最后一层,看看 compute_dyna 的计算成本是如何分布的(更多细节请参考第一篇或本文结尾):

图 4:时间消耗在“计算动态”中的重新分配(上图(3)中的最高消耗)。作者图片

在这里,牛顿第二定律是最昂贵的函数。这并不奇怪,因为它有最多的运算,而不仅仅是琐碎的运算,比如三角学和处理数组。

然而,再一次,马赫数的计算伴随着巨大的代价,因为范数计算确实是昂贵的。

Cx(以及 Cz、Sx 和 Sz)也很重要,因为它有很多运算,比如三角学或平方根。最后,新速度和位置的计算是最后一个重要的成本,只留下代码中无关紧要的部分(重量、襟翼或坠毁的计算)。

看一看我们在这里编译的所有元素,我们看到除了基本操作符(+、-、/和*)之外,涉及数学函数的计算往往很昂贵,即使使用 NumPy 的优化版本也是如此。我们代码中最差的函数是 numpy.linalg.norm 函数。

既然我们已经确定了计算成本的领域,让我们来看看降低它们的解决方案。

2.使用 Numba 的代码加速

来源:跨编程语言的能效。结果以与 c 语言相比的效率比率表示。例如,Python 消耗的能量是 c 语言的 75.88 倍。

从上表中可以看出,就执行时间而言,Python 并不是一种高效的语言。这并不奇怪,因为 Python 最出名的是它的简单性和开发速度,而不是它的性能。当使用用更有效的语言设计的库时(例如用 C++和 CUDA 编码的 Tensorflow ),这个缺陷可以得到部分解决。对于代码的其余部分,我们需要另一个解决方案。

我们可以首先尝试修改代码来优化它,避免昂贵的功能,重复,使用较低的精度…我们将假设这项工作已经完成。我们还可以尝试用更高效的语言(如 C++)重新编写,这是我尝试过的解决方案,但不如我们将要讨论的解决方案高效:

来源: Numba

Numba 是一个基本上在执行时将部分 Python 代码转换成 C 语言的库。它允许将 Python 的简单性与 C 语言的高效性结合起来。它唯一的缺点是仅限于纯 Python 和 NumPy 函数,并且在代码的第一次迭代中有少量的前期编译成本(但在后面的迭代中没有)。如果你想了解更多细节,我建议你看下面的视频。

这里有一个很棒的视频展示了 Numba 的利与弊。

使用 Numba 非常简单,您只需在函数中添加一个装饰器,并确保它只包含 Numba 兼容的元素(只有 Python 或 NumPy,没有列表,只有 NumPy 数组)。

用 Numba 转换的一个函数的例子

@njit decorator 代表NopythonJustINTime。没有 Python,这意味着如果使用了任何 Numba 不能处理的代码,Numba 将会引发错误,这会降低速度。JustInTime 意味着它将在需要之前将代码编译成 C。 nogil (无全局解释器锁)允许多重处理(这在以后会很有用) fastmath 顾名思义,允许以牺牲一些精度为代价的更快的数学运算。

我们将把这个装饰器应用于空气动力学计算中的所有函数,并观察结果:

Numba 加速后(左)和加速前(右)空气动力学计算时间分配的比较。作者图片

结果很好看!对于空气动力学计算,我们在 Numba 上比 Python 有了 363%的改进。现在让我们向上移动一个阶段,到“下一个功能的动作”,它调用空气动力学计算作为其计算时间的主要来源。

提醒:空气动力学计算(右侧)是“下一个功能的动作”(左侧)的一部分,它计算动作对下一个状态的影响。作者图片

Numba 加速后(左)和加速前(右)动作到下一状态计算的时间分配比较。作者图片

这同样适用于整个“到下一个状态函数的动作”,我们在 Numba 上比 Python 提高了 326 % !没有任何辛苦!

我们已经释放了能量!(来源:吉菲)

我们现在已经加速了我们环境的主要部分。让我们来看看我们对全局训练时间的影响,不限于来自环境的下一步的计算,还包括动作选择和代理的实际更新(图中的训练时间) :

不同配置的总训练时间的组成。作者图片

我们 326%的提速变成了 12%的提速!令人失望?是的,可以预见?也是的。这种差异是由于这样一个事实,即整个过程的加速不仅仅是基于与我们到目前为止已经优化的环境的相互作用。在这种特殊情况下,环境交互仅占计算成本的一小部分,因此,即使这部分的重要改进对总训练时间的影响也很小。然而,我们会就此止步吗?绝对不是,现在让我们来看看如何加速这个过程的其他部分

3.RL 加速:并行化和超参数

加快强化学习训练的第二个显而易见的方法是拥有一个更快的代理。假设你已经选择了一个高效的代理(在我们的例子中是 PPO),你还能做什么?好了,并行化来了!

并行化的力量(来源: Giphy

强化学习不允许在剧集内部并行化太多。但是,可以同时运行多个环境,并让代理同时学习所有环境!为了做到这一点,我们将使用稳定基线 3 来实现我们的代理(这是一个非常好的、有良好文档记录的 RL 库,为了简单起见,我强烈推荐它)。稳定基线允许定义矢量化环境(基本上是堆叠环境)及其与代理的连接,代理将能够同时从所有堆叠环境中学习:

矢量化环境的创建。更多信息请参考 SB3 的文档。

太好了,我们可以并行化环境!现在让我们来看看并行环境如何加快我们的学习过程:

4 个环境的单一环境与并行环境。作者图片

如此图所示,在并行化环境时,我们可以用我们可以同时运行的并行环境的数量来划分操作选择和环境交互的时间。这是因为我们可以同时运行几个环境,而只需要运行一个环境。此外,动作选择还可以同时在几个环境中并行化,这也降低了成本。

现在让我们看看这如何转化为我们的实际执行时间。为此,我们将根据环境的数量对执行速度进行基准测试。为此,我们将使用一个带有 32 个 vCPU 的 c5a.8xlarge AWS 实例,并为每种环境运行 1000 集。

总执行时间与并行环境数量。作者图片

我们可以看到时间收敛到 200 秒左右。这是因为在某些时候,与其他成本相比,环境时间将变得可以忽略不计,我们将在下一张图中看到这一点。

现在,让我们来看看与前面的方法相比,这种加速是如何实现的。使用 32 个 vCPU 实例只是为了进行基准测试,为了限制成本,我将回到使用 4 个 vCPU 的机器上:

不同配置的总训练时间的组成。作者图片

我们可以看到,动作选择已经大大减少,同时也稍微减少了环境交互时间。这使我们的速度比原始设置提高了 67%。现在,时间成本的主要来源是培训时间,让我们也尽量减少培训时间。

正如在经典的机器学习中一样,减少训练时间可以通过几种方式来完成(时期数、学习速率、批量大小、GPU/图形处理单元上的计算)。然而,它们中的大多数也会影响模型的性能。最常见和最安全的方法是批量和使用 GPU。

批量

批量大小表示要同时处理的用于梯度计算的数据点的数量,如在标准机器/深度学习中。这不要与同时使用的环境数量相混淆,这里我们说的是代理更新步骤,参考上面的单环境与并行环境图)。

我们想要什么,小批量还是大批量?

  • 批量越大,梯度计算越精确,但花费的时间越长。同时,较大的批处理大小允许在这些计算中进行并行化,从而节省时间。
  • 批量越小,梯度计算越不精确,但速度越快。关于准确性,有时,一些“不准确的”小批量更新比计算较少的较大批量更新更快地收敛到真正的最优值。

批量大小的权衡不是一件容易的事,我强烈建议你阅读更多关于这个主题的文章。在强化学习中,最佳批量通常在 64 到 2048 个样本之间变化,但像任何超参数一样,它通常需要根据当前问题进行调整,以获得最佳性能。让我们看看,如果我们将批量从默认的 64 个样本增加到 1024 个样本,我们的培训会进行得如何:

不同配置的总训练时间的组成。作者图片

正如所料,我们观察到列车运行时间大幅缩短。请注意,这种减少有时可能是以牺牲性能为代价的,但是,在本文中,我们只对减少整体训练时间的方法感兴趣。当调整到最佳性能时,读者当然应该管理训练时间和性能之间的平衡。我们只是在审查减少总体流程时间的方法,但它们确实需要一些判断力。随着这个重要的减少,动作选择和环境交互现在是主要的执行时间原因。在具有复杂环境的应用程序中,通常会出现这种情况。现在,我们通过 Numba 实现的环境执行成本的降低真的很棒!

GPU 加速

当您的网络中有重要维度的输入时,GPU 加速通常是一件好事,最好的例子是计算机视觉和 CNN。然而,在 RL 中,除非你考虑的状态空间是一个图像或者是一个非常高的维度,否则使用 GPU 来加速梯度计算和更新很可能是有害的。你从 GPU 获得的计算梯度的速度将不如将数据从 CPU(计算环境)移动到 GPU 所增加的时间。如上所述,在大多数复杂环境中,环境和行动选择成本通常是主要问题。在这种设置下,CPU 运行环境的速度通常比 GPU 快得多,因此促使选择 CPU 来运行环境和其余代码(与在 GPU 上运行环境和代理以消除在 CPU 和 GPU 之间移动数据的成本相反)。

GPU 配置(1 个 GPU 个 vCPU,AWS g4dn.xlarge 实例)与 vcpu 对时间曲线的对比。我们可以看到,它大大减缓了这一进程。只尝试了一种 GPU 配置,因为成本太高,结果很简单。作者图片

4.云计算

就像我们的飞机一样,让我们向云移动吧!来源:吉菲

目前 236%的提速,令人满意有余!然而,让我们更进一步,看看使用更强大的机器会在多大程度上提高我们的性能。为此,我将再次使用一个 c5a.8xlarge AWS 实例,它具有比我的计算机更快的 CPU(2.0 GHz 对 3.0Ghz)和更多的 vCPU 对 32)。让我们来看看结果:

不同配置的总训练时间的组成。作者图片

我们可以看到,使用更快的 CPU 时,性能提升了 236%,达到 278%;使用更多 vCPUs 并行环境时,性能提升了 349%。然而,这也带来了运行 AWS 实例的成本。因此,我们很可能会坚持 236%的改进来试验未来的应用程序,而只将 349%的改进用于大规模训练。

任务完成了。

结论和下一步措施

在本文中,我们看到了加速 RL 项目的不同简单方法。这些加速的方法允许更快的训练,因此更快的反复试验来创建适合我们需要的工作 RL 代理。这也有助于降低计算成本,可能是财务成本(电力、计算机或实例的成本)或环境成本(过程中消耗的能源)。提出的方法快速且易于实现,因此可以应用于几乎任何 RL 项目。

从我们的原型到我们的快速环境,我们已经走过了漫长的道路!照片由菲利普·布朗在 Unsplash 上拍摄

我希望这篇文章可以被其他人作为一个资源,当他们想让他们的 RL 项目更快的时候可以去看看!我打算写的下一篇文章将谈论奖励工程:如何定义一个有效的强化学习奖励,以在你的自定义设置中达到你的目标。

感谢阅读,我真的很喜欢分享我的强化学习之旅,我将非常感谢你的回报。如果您有任何问题,请随时联系我,并关注下一部分!

扬恩·贝特洛

领英

Github

密码

环境代码(分支:与本文相关的代码的 testing_automation)。请随意测试,它包括易于安装的诗歌文件。

https://github.com/YannBerthelot/CaptAIn_v2/tree/testing_automation

编辑 SB3 版本,打印代码不同部分的时间:

https://github.com/YannBerthelot/stable-baselines3

资源

  • Numba 文档
  • 稳定基线 3 文档

功能描述

env_step

  • compute_dyna:根据当前状态和施加到飞机上的力,计算下一个时间步的飞机的加速度、速度和位置。
  • compute_mach:根据当前速度计算马赫数。
  • 计算海拔系数:根据当前海拔计算反应堆能量的减少。
  • new_theta:根据飞行员的输入计算新的 theta 角度(基本上是将 a -1 对 1 的输入映射到一个实际角度)。
  • new_thrust:根据飞行员的输入计算新的推力功率((基本上将 0 到 1 的输入映射到实际值))
  • clip_thrust:将飞行员选择的推力值限制在限制值之间,以防止时间帧之间的θ出现重大差异
  • clip_theta:将飞行员选择的俯仰角(theta)值限制在限制值之间,以防止推力在时间范围内出现重大差异
  • compute_fuel:根据当前状态计算油耗和剩余油量。
  • get_obs:根据上述所有内容更新当前观察结果,并将其提供给代理。

compute_dyna

  • 牛顿:使用牛顿第二定律计算飞机新的加速度矢量
  • 马赫:计算马赫数(这个是计算速度对不同因素如 Cx 和 Cz 的影响所需要的)
  • gamma:计算 gamma(地面和速度向量之间的角度)
  • cx,cz:计算飞机沿 x 和 z 轴的阻力系数
  • sx,sz:沿着 x 和 z 轴计算平面的表面
  • 阻力:计算阻力和升力的相互作用(它们使用相同的公式)
  • new_pos_V:使用新计算的加速度计算飞机的(近似)下一个速度和位置。
  • 坠毁:检查飞机是否已经坠毁(希望没有)。
  • 襟翼:计算襟翼对表面的影响(Sx 和 Sz)和阻力系数(Cx 和 Cz)
  • p:根据飞机的质量计算出它现在的重量。

用于机器学习网络应用部署的 Kubernetes 架构

原文:https://towardsdatascience.com/a-kubernetes-architecture-for-machine-learning-web-application-deployments-632f7765ef29?source=collection_archive---------23-----------------------

理解大数据

使用 Kubernetes 降低机器学习基础设施成本,轻松扩展资源。

照片来自维基百科

Kubernetes 成为容器编排的参考。容器编排意味着启动容器、关闭容器、纵向扩展容器(归因于内存和 CPU 的数量)和横向扩展容器(并行运行的容器数量)。

Kubernetes 是否为机器学习增加了价值?机器学习需要大量的资源来训练一个模型,但需要少量的资源来服务预测。Kubernetes 会自动调整资源!机器学习通常意味着长时间的训练,可以通过将任务分配给几台计算机来缩短训练时间。Kubernetes 在调整必要的计算机数量方面大放异彩。总的来说,Kubernetes 通过随着时间的推移只需要必要的资源来降低基础设施成本。

本文描述了一个用于机器学习 web 应用程序的 Kubernetes 架构。术语“Kubernetes 架构”回答了“应用程序如何部署到 Kubernetes 中?”。本文分四个部分回答了这个问题:

  1. 定义一个机器学习网络应用。
  2. 分析将机器学习 web 应用程序部署到生产环境的要求。
  3. 建议使用 Kubernetes 架构来部署机器学习网络应用。
  4. 为分布式培训调整架构。

如果您对本文感兴趣,您可能还会对以下内容感兴趣:

机器学习网络应用的定义

在高层次上,机器学习应用负责两件事:

  1. 训练模型:基于训练数据集,它训练估计器
  2. 服务预测:基于经过训练的估计器和新数据,它预测新的值

作者图片

一个例子:需求预测者

假设有一个应用程序可以预测城市某个区域的踏板车需求。它的输入和输出是红色的。在下一步中,每个输入和输出都将被映射到它的 Kubernetes 组件。

作者图片

  • 输入:它从数据库中读取历史需求。
  • 输出:它用一个 REST API 服务于预测。Uvicorn 是网络服务器。
  • 序列化:它在硬盘上读写经过训练的评估器。这是训练阶段的输出,也是预测阶段的输入。

部署机器学习网络应用的要求

部署机器学习网络应用的要求:

作者图片

  1. 确保代码和估算器之间的一致性。更改估计器库(例如 sklearn、xgboost、tensorflow 的版本)可能需要重新训练模型。添加特征、更新特征或删除特征也需要重新训练模型。因为 web 应用程序不断地为预测服务,所以必须避免部署的代码和估计器之间的差异。
  2. 避免模型漂移。昨天训练好的模型,明天就不那么准确了。这就是所谓的模型漂移。它在某些领域比在其他领域更重要。在预测中,最新的观察很重要,所以模型漂移得很快。相反,在计算机视觉中,如果任务是对不同类型的汽车进行分类,当新型号的汽车出现时,模型就会漂移。为了更加精确,必须避免模型漂移。
  3. 垂直缩放。训练一个模型需要大量的记忆。为了避免内存不足的错误,必须提供足够的内存量。
  4. 水平缩放。训练机器学习模型可能会很长。将培训分布在多台计算机上会有所帮助。必须提供足够数量的计算机。
  5. **共享受过训练的估计器。**模型的估计器在一台计算机上训练,预测在另一台计算机上提供。这些预测也可以由几台计算机来完成。串行估计器必须在所有计算机之间共享。

用于机器学习网络应用的 Kubernetes 架构

在生产中部署机器学习网络应用增加了新的需求。本节介绍如何使用 Kubernetes 解决这些问题。

Kubernetes 术语

之前,这篇文章谈到了“计算机”,在 Kubernetes 中它被翻译为“豆荚”。

吊舱是 Kubernetes 中最小、最基本的可部署物体。Pod 代表集群中正在运行的进程的单个实例。箱包含一个或多个容器,例如码头集装箱。

Kubernetes 发动机文件

但是为了使事情更简单:在本文中,每个 pod 总是有一个容器。这篇文章还谈到了 Kubernetes 的服务:

Kubernetes 服务是集群中已部署的一组 pod 的逻辑抽象(它们都执行相同的功能)。因为 pod 是短暂的,所以一个服务启用一组 pod,它们提供特定的功能(web 服务、图像处理等)。)被分配一个名称和唯一的 IP 地址(clusterIP)。

在这个例子中,Kubernetes 将有一个服务。这项服务负责预测需求。它将做两件事:训练机器学习模型和服务预测。

用于机器学习网络应用的 Kubernetes 架构

作者图片

Kubernetes 作业(又名 pod-init)

Kubernetes 作业是在实际部署之前执行的 pod,在 web 服务器的 pod 启动之前执行。Kubernetes 的作业可以用来训练模型。因此,可以确保代码和估计器之间的一致性。

Kubernetes cronJob

Kubernetes cronJob 是计划定期执行的 pod。它可用于使用最新数据定期(例如每天)重新训练模型。因此,可以避免模型的漂移。

Kubernetes 共享卷

Kubernetes 共享卷在 pod 之间共享文件。因此,经过训练的估计器可以在吊舱之间共享。

在上面的模式中,序列化模型实际上保存在 AWS S3 上,但是纯 Kubernetes 方法是使用共享卷。

Kubernetes 垂直 pod 自动缩放器

Kubernetes 垂直 pod 自动缩放器会随着时间的推移向 pod 动态分配所需的内存和 CPU。因此,可以避免内存不足错误,并确保垂直缩放。

水平缩放

这一要求由两个组件满足。部署吊舱的数量由 **Kubernetes 水平吊舱自动缩放器控制。**它启动和停止 pod,以满足并发 HTTP 请求的数量。此外,Kubernetes 作业有一个参数“ parallelism ”来控制有多少个 pod 并行运行。作业用于训练模型,在 Kubernetes 之外,以分布式方式训练机器学习模型需要一些适应。这是下一节的重点。

分布式训练的适应性

在部署过程中训练模型会使部署更容易。贡献者不需要考虑:这个新特性将如何影响遗留评估者?新的数据标准化将如何影响传统的评估者?库的更新将如何影响遗留评估者?

尽管如此,它有一个缺点:在部署期间(在 pod 初始化步骤中)训练模型可能会持续很长时间,并使部署工作流(CI/CD)效率低下。缓慢的部署流程破坏了 CI/CD 最佳实践。

在这种情况下,将培训分散到几个单元会有所帮助。一些库被设计成分发一个模型的训练:例如 MLlib 或 Tensorflow。这不是本文的重点。

在其他情况下,培训是适合几个模型,而不是一个。在这种情况下,很容易用主/工模式来分配培训。假设入口点是 bin/master.py 和 bin/worker.sh,这是 Kubernetes 编排的 pod 的表示:

作者图片

浅绿色的框显示了被操作的 Kubernetes 对象的种类:它们按照类型对 pod 定义进行分组。中间的绿色方框是 pod 的定义,即 YAML,如下图所示。深绿色的方框显示了该定义的实际实例。

apiVersion: batch/v1beta1
kind: CronJob
spec:
  schedule: "40 2 * * *"

在一个分布式世界中,Kubernetes 协调多个 pod 实例。

对于 jobs 和 cronJobs,首先执行名为“master”的 pod:它在数据库(redis,Kubernetes 之外)中创建任务。然后,工人们开始了:他们执行任务。需要终止工作进程,以便 Kubernetes 停止 pod,然后启动部署 pod。对于芹菜,下面的伪代码很有帮助:

结论

部署机器学习 web 服务增加了新的需求,如确保代码和估计器之间的一致性、可伸缩性、在所有计算机之间共享模型以及避免模型漂移。本文展示了用 Kubernetes 解决这些需求的一种方法。此外,它还为降低基础设施成本增加了巨大价值。

一个容易提取和标记维基百科数据的标签工具

原文:https://towardsdatascience.com/a-labelling-tool-to-easily-extract-and-label-wikipedia-data-63f58e2e76ae?source=collection_archive---------20-----------------------

使用 DataQA 从维基百科建立一个训练数据集

照片由 Unsplash 上的哈迪贾·赛义迪拍摄

免责声明:我是 DataQA 的开发者,data QA 是一个免费的开源文本探索和标记工具。

任何数据科学家的一个非常典型的任务是必须通过找到与其相关的附加属性来“丰富”一些种子数据集。诸如此类的问题经常出现:

  • 这份 2000 家公司名单的总部在哪里?
  • 这 500 种药物的主要副作用是什么?
  • 这 400 家公司推出的主要产品有哪些?
  • 这份 1000 名人榜的主要朋友有哪些?

好吧…也许不是最后一个。

维基百科拥有近 650 万篇文章(还在增加),是世界上最大的人类知识库之一,因此也是我们大多数人寻找答案的第一个地方。好消息是它是在线的,而且(大部分)可以免费使用,坏消息是它主要是由非结构化文档组成的,手动提取这些信息会花费不合理的时间。开源工具 DataQA 最近刚刚推出了一项新功能,可以让你快速有效地从维基百科中提取信息。在本教程中,我们将带您完成基本步骤。

下载你的数据

本教程的先决条件是拥有感兴趣的文章的维基百科 URL(或子路径)列表。在本教程中,我们有一个几乎 400 家公司的列表,其中一些是私有的“独角兽”,另一些是公开上市的,我们希望提取这些公司推出的产品的所有名称。输入文件必须是 csv 文件。

示例输入 csv 文件。

步骤 1:创建一个新项目,从维基百科中提取实体

启动 DataQA 并创建一个新项目。关于如何安装和启动 DataQA 的说明可以在官方文档网站上找到。安装只是一个简单的 pip 安装命令。

创建一个项目来从 Wiki 页面提取实体。

步骤 2:加载包含页面 URL 的 csv 文件。

下一步是加载文件。DataQA 只在启动时连接互联网,一切都在本地运行,你的数据不会离开你的电脑。

上传输入文件。

上传可能需要几分钟,具体取决于有多少行。一个包含 400 个网址的列表大约需要 10 分钟。DataQA 从维基百科中提取段落和表格,并对所有内容运行 NLP 管道。DataQA 中的每个文档都只是原始文章的一部分,大约是一个段落。标注较小的文本块要容易得多。

步骤 3:加载带有实体名称的 csv 文件。

下一步是上传一个带有我们想要标记的实体名称的文件。在这个例子中,只有一个名为“products”的类型,所以我们的 csv 除了列名之外只有一行。

我们的实体名称。这个文件可以根据需要包含任意多的实体,但是对于这个项目,我们只有“产品”。

上传带有实体名称的文件。

完成这一步后,您将进入项目的汇总表,其中包含关于已处理文档总数的信息。

在我们的例子中,尽管原始文件只有 400 个 wikipedia urls,但最终上传的文本“块”总数略超过 12,000。

项目概述:从维基百科中提取了 12,172 个不同的章节/段落。

开始标记你的文件

一旦数据被上传,你可以点击左边的“搜索”开始探索从维基百科中提取的数据。

查询“产品”的搜索结果示例

手动标记 12,000 份文档实际上并不是一项可行的任务。幸运的是,DataQA 提供了一些方法来缩小最有可能包含所需信息的文档的范围。每个段落都以页面部分的名称开始,我们注意到在这些维基百科页面中发现一个名为“产品”的部分是非常常见的。您可以输入正则表达式来匹配特定的文档或实体,并自动提取实体。

  • 我们可以创建一个规则,提取正则表达式 部分产品之后的实体。?开发* ,
  • 并且还 款产品。?发布* 。

创建一个规则,自动标记正则表达式后面的任何实体。

我们还可以使用其他规则来缩小要标记的文档的范围:

  • 用名为“产品”的列标记任何表。
  • 获取任何包含单词“develop”和包含大写字母的名词短语的句子的段落。
  • 获取包含单词“release”和包含大写字母的名词短语的句子的任何段落。

添加这些简单的规则后,我们的汇总表如下所示:

这些规则从超过 12,000 个候选段落中确定了 1176 个可能包含产品名称的段落。

现在我们准备给这些文档贴标签。单击与规则位于同一行的手形图标,将仅显示该规则选择的文档。

标记包含带有名为“产品”的列的表格的文档。

点击上面的手形图标将带您进入包含 Wiki 表格的文档,这些表格有一个名为“产品”的列。使用 DataQA,可以从表格和段落中提取实体。在许多情况下,这些表将包含特定的产品名称,这就是我们希望从这个项目中提取的内容。

从 Wiki 表中提取实体的示例。

只有某些跨度是有效的实体。例如,不可能将单词的一部分标记为实体。第一次单击检查图标时,将验证跨度的有效性。如果高兴,第二次点击时,这些标签被确认并保存,显示下一个文档。

将结果导出为 csv 格式

一旦我们对收集的标签数量感到满意,我们就可以将标签导出为 csv 文件。

输出 csv 文件将包含以下字段:

  • row_id :段落的索引,
  • 网址:原维基百科网址
  • 正文:文档、
  • is_table :如果文档是 Wiki 表,则为 True,
  • 手动 _ 标注:手动标注跨度,
  • merged_label :所有规则跨度,
  • 【规则名称】:单个规则跨度。

读取带有标签的文件。

输出 csv 文件。

manual_label 列包含所有已被手动标记的量程。

我们现在有了一个公司名称与该公司推出的所有产品的映射。

当段落是表格时,文本是该表格的 csv 格式版本,可以加载到 pandas 数据框架中:

将维基表读入熊猫数据帧。

暂时就这样吧!

在以后的博客文章中,我们将看到这些标签如何被用来训练 NER 模型。

参考

  • Github 库:https://github.com/dataqa/dataqa
  • 官方文档页面:https://dataqa.ai/docs/latest