TowardsDataScience-博客中文翻译-2022-三十四-

TowardsDataScience 博客中文翻译 2022(三十四)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

学习 Kubernetes,简单的方法

原文:https://towardsdatascience.com/learn-kubernetes-the-easy-way-d1cfa460c013

在本教程中,我们将了解 Kubernetes 以及如何使用它来编排容器化的应用程序

汉斯-彼得·高斯特在 Unsplash 上拍摄的照片

如果你是一名软件开发人员,你可能几乎每天都会听说 Kubernetes。Kubernetes 已经成为业界领先的容器编排工具。

本教程中的所有图像都是由珀西·博尔梅勒创作的

当我开始学习 Kubernetes 时,它很难,有太多的术语,我几乎很快就放弃了。出于这个原因,我将在本教程中尝试以简洁易懂的方式慢慢地、彻底地走完 Kubernetes 的每一步。

如果你喜欢视频格式,你可以在 YouTube 上观看相同的教程。

我浏览教程的 YouTube 视频

我们将构建一个运行 API 和数据库的简单应用程序。本教程旨在帮助您熟悉 Kubernetes,并希望学习一些基础知识。本教程不会涵盖如何将应用程序部署到生产环境中,这是一个本身需要一整篇文章的主题。

在开始编码之前,让我们先了解一些信息。

你可以在我的 GitHub 上找到这个教程的完整代码

什么是 Kubernetes

库伯内特建筑形象化

Kubernetes 是一个管理和控制容器化应用程序的工具。如果不熟悉容器和 Docker,可以阅读学习 Docker

Kubernetes ,也称为 K8S,是一个用于自动化部署、扩展和管理容器化应用程序的开源系统。

Kubernetes 旨在解决整个基础设施中多个容器的部署和控制。Kubernetes 是谷歌开发的开源软件。

你会发现 Kubernetes 的一些特点是

  • 服务发现 —通过 DNS 公开您的容器,并使发现正在运行的服务成为可能。
  • 负载平衡 —如果您的一个容器流量过大,它可以将流量分配给另一个已部署的容器。
  • 自我修复 —可配置为在需要时重启/移除并启动新容器。
  • 机密&配置 —这使得为您的部署存储和管理机密变得容易。
  • 监控 —应用程序的内置监控

为了让我们了解所有这些特性,Kubernetes 运行了许多协同工作的组件,我们将简要概述 Kubernetes 组件,这样我们就知道什么是基础。

运行中的 K8 集群由一个控制平面组成。控制平面负责公开一个 API 来控制集群和容器生命周期的管理。在控制平面内,我们发现一些重要的节点有不同的职责。

  • API—kube-API server 用于连接集群,并允许我们与集群对话。
  • Etcd—K8 用来维护集群数据的键值存储解决方案
  • 调度器 —检查没有分配节点(工作机)的新容器(运行容器),并分配它们。
  • 控制器管理器 —负责管理控制器的组件。

然后,我们有与控制平面通信的工作节点。控制平面与工作节点对话,以便它们知道该做什么。worker 节点用于运行 pod(一组容器)。

每个 worker 节点都有一个[kubelet](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)正在运行,它负责接受来自控制平面的关于它应该运行什么的指令。Kubelets 通常被称为节点代理。

所以,我们有一个控制平面在运行,多个 kubelets (worker nodes)可以连接到它。这是对 Kubernetes 整个基础设施如何工作的一个非常基本的解释,你应该再一次熟悉所有的东西,探索文档以了解更多关于确切的内部工作方式。

一旦我们进入了 Kubelet,了解它们可以运行的不同资源是很有好处的。

这里有一些和 K8 一起工作时很好理解的词。

  • pod—您的集群中的一组运行容器,将 pod 视为在 K8 内工作的最小单位。通常,每个 pod 使用一个容器,但也可以是多个容器。
  • 节点 —集群中的一台工作机
  • 控制器——一个检查集群某个状态并试图调节它的回路。
  • 复制集 —用于确保始终有设定数量的 pod 运行。
  • 部署 —提供副本集和单元的更新
  • 作业 —由 pod 执行的流程,将创建 Pod 并执行流程,然后关闭。
  • 服务 —通过在内部公开端口,允许 pod 与集群中的其他 pod 进行通信。

我试着把单词列表保持得很小,我知道要记住它们可能会很难,但是不要担心,当我们把它们添加到我们的应用程序中时,我们会一点一点地介绍它们。

安装 Kubernetes、Minikube、Docker 和 Go

本教程的要求— Kubernetes、Minikube、Docker、Go

在我们开始使用 K8S 之前,我们需要下载并安装它,以及本教程中使用的一些其他工具。

https://kubernetes.io/releases/download/

遵循 Kubernetes 自己提供的安装指南。如果你用的是 Linux,这就是我们需要做的。

首先使用 curl 获取 Kubernetes 并安装下载的二进制文件。

curl -LO "https://dl.k8s.io/release/**$(**curl -L -s https://dl.k8s.io/release/stable.txt**)**/bin/linux/amd64/kubectl" sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

您可以通过运行以下命令来确保安装工作正常

kubectl version

第二步是安装 Minikube。Minikube 是一个本地 Kubernetes 节点,可以用来学习和测试 Kubernetes。基本上,它在您的计算机上设置一个虚拟机,该虚拟机运行一个具有单个节点的集群。

要安装 Minikube,请遵循说明。对我来说,运行 Linux,就像跑步一样简单

curl -LO [https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64](https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64)sudo install minikube-linux-amd64 /usr/local/bin/minikube

如果你运行的是 Windows,请不要在 WSL 里面安装 Minikube,同时也要确保你已经安装了 Hyper-V 。避免 WSL 的原因是因为在写这个教程的时候,让它工作是非常复杂的。

通过运行minikube version命令来验证您的安装。

Minikube 版本打印版本

你需要的第三个软件是 Docker,因为我们将使用它来构建我们的容器。

你可以在他们的网站上找到如何安装 Docker 的说明。我不会详细介绍如何安装它,因为在Learning Docker-The Easy Way中已经介绍过了。

第四个需求是 Go ,可以通过访问他们的网站进行安装。在本教程中,我使用 Go 来实现一个简单的服务,这个服务并不复杂,对于新开发人员来说应该非常容易理解。

让我们为库伯内特探险做准备

将我们的 Go 应用程序封装到 Minikubes Docker 环境中

一旦一切就绪并开始运行,是时候开始熟悉 Kubernetes 的实际用法了。在使用 Kubernetes 之前,我们需要在一个由 Minikube 创建的节点上运行应用程序。

运行minikube start使 Kubernetes 在运行应用程序时使用 Minikube。这一点很重要,因为我们只有一台计算机来运行它。这可能需要一些时间来运行,去喝杯咖啡。

请注意,如果您使用的是另一个虚拟机驱动程序而不是 windows 上的 Hyper-V,例如 Docker,您需要将其添加到 start minikube start --driver=docker

通过运行 Kubectl 命令行工具来列出可用节点,可以确保一切正常。在您的终端中运行kubectl get nodes,您应该看到Minikube被列为一个节点。当您想要查看集群上的节点时,此命令非常有用。

获取节点应该列出 minikube 节点。

我们将需要建立一个 docker 图像,我们可以使用它在 Kubernetes 内部运行。我用 Go 准备了一个超级简单的 HTTP 服务器和一个构建它的 docker 文件。创建一个main.go文件,用下面的要点填充它。

main . Go——Go 中一个超级简单的 HTTP 服务器

我们还需要创建一个dockerfile,我不会详细介绍 Docker 是如何归档的,如果你需要了解 Docker,可以看看我的学习 Docker 文章。

dockerfile —构建 HTTP 服务器,并将其设置为在容器启动时启动

在我们能够构建 docker 之前,我们需要确保两件事情是有序的。第一个是我们需要在项目根目录中初始化一个 go 模块。

go mod init programmingpercy/hellogopher

我们还需要确保通过运行eval $(minikube docker-env)来使用 Minikubes docker 环境。 这是每个终端重启所需要的。 如果您使用的是 Windows,请改为运行命令。

minikube -p minikube docker-env | Invoke-Expression

请不要跳过上述命令,如果你这样做,你将面临的问题,寻找 docker 镜像安装在您的计算机上,因为你使用了错误的 docker 环境!

是时候建立我们想要使用的图像了

docker build -t programmingpercy/hellogopher:1.0 .

准备好 Kube!

Kubernetes 对象是在 Yaml 文件中定义的

我们现在有了我们需要的一切,我们有 Kubernetes,Minikube,还有一个 Docker 映像中的惊人的 HTTP 服务器可以运行。让我们创建我们的第一个 Kubernetes 资源。

在 Kubernetes 中,我们使用 YAML 文件来定义对象,应用程序的所有部分都被称为对象。在 YAML 中有大量的东西需要定义,但是我们将从简单开始。

创建一个名为hellogopher.yml的新文件,它将维护与 API 相关的对象。

我们将一步一步填充 YAML 文件,看看每一行的意思。我们从一些必需的缺省值开始。我们在 YAML 中定义的是一个 Kubernetes 对象,每个对象都需要这些字段。

  • apiVersion 是一个描述您将使用哪个版本的 Kubernetes API 的字段。
  • 种类是我们正在创建的对象的种类。
  • 元数据是关于对象的信息,可用于跟踪和识别对象。

hellogopher.yml 第 1–4 行包含默认字段

接下来,我们将定义spec,spec 是 YAML 中的一个字段,它定义了对象将处于的状态。规范中需要提供哪些信息取决于您创建的对象的类型。

我们正在创建一个部署对象,一个部署用于指定运行 API 的 Pod 的期望状态。这可以是关于环境变量的设置、要创建多少个副本以及关于正在运行的 pod 的默认设置。我们将首先添加三个字段。

  • 选择器— 部署应该用来查找相关 pod 的标签。这很重要,因为我们可以让其他对象使用这个选择器来引用它,并在以后使用 kubectl 命令找到它。
  • 副本— 要启动多少个副本,一个副本是一个相同的容器。如果我们将其设置为 1,我们将启动 1 个容器,如果我们将其设置为 3,将启动 3 个容器。
  • 模板— 定义新创建的 pod 应该如何设置的模板,请注意,模板是一个对象,包含自己的 spec 字段。

模板字段包含自己的规范,因为它是一个对象。在该规范中,我们定义了 pod 都应该运行我们构建的 docker 映像。我们还指定应该公开端口 8080,它不应该从 DockerHub 获取图像,我们只在本地构建它。

如果你对任何领域有疑问,想要更多的信息,请随时查看官方文件。我已经对每个领域做了评论。

hellogopher.yml —添加了部署规范

为了创建和运行这个新资源,我们将运行以下命令

kubectl create -f hellogopher.yml

kutebectl create用于创建资源,-f标志用于指向某个文件。

现在可以运行kubectl get all来列出所有名称空间中的所有资源。我们可以在 Kubernetes 中使用名称空间来分离资源,稍后会详细介绍。

ErrImagePull —如果您忘记评估 Minikubes docker env 并在其中构建 docker 映像

如果您看到 Status ErrImagePull,很可能是您忘记了使用 Minikubes docker 的 eval the docker 环境。

请记住,每次重启终端时都需要进行评估。另一个常见的错误是,首先在计算机的 docker env 中构建 docker 映像,然后是 eval。

如果您有任何其他错误,您可以通过使用下面的命令获得关于部署的详细信息。

kubectl get deployment/hellogopher -o yaml

要访问应用程序,您需要公开一个节点端口。在 go HTTP 服务器内部,我们已经对要公开的端口 8080 进行了硬编码,但是这也需要为 Kubernetes 进行配置。我们可以使用expose deployment命令来做到这一点,该命令接受资源的名称和要公开的类型。在我们的例子中,我们希望公开一个节点端口,这是公开服务端口的一种方式,如果您希望从部署外部访问服务,这是必需的。

kubectl expose deployment hellogopher --type=NodePort --port=8080

现在检查资源的状态(提示: kubectl get all ),您应该会看到节点端口。

Kubectl get all —输出显示我们现在将 hellogopher 服务的端口 8080 公开到主机的 30012 端口。

您可能已经注意到,您机器上正在使用的端口是为我动态分配的(30012)。很高兴,Minikube 提供了一组命令来帮助我们访问部署,这样我们就不需要跟踪分配的端口。

您可以通过运行minikube service hellogopher来访问该服务。该命令将打开您的网络浏览器,显示hello gopher消息。

让我们练习一下,我们想删除部署,因为我们现在已经完成了。你可以使用delete deployment命令来完成。

kubectl delete deployment hellogopher

标签和选择器

标签和选择器允许我们指定目标对象

与 Kubernetes 合作时,您会遇到术语labels。标签是一个键/值对,可以分配给资源。标签通常用于在资源上附加信息,但也用于在大型环境中区分它们。您可以使用标签来定位特定的资源,这些资源使用 kubectl 命令的匹配标签进行标记,这在您想要删除多个包含相同标签的资源时非常有用。

您可以在运行时或在 YAML 配置中添加标签。让我们尝试一下,以便更好地理解,我们将在 pod 中添加一个标签运行时。

如果您注意到了,我们在 YAML 配置中创建了一个名为app的标签。您可以通过添加--show-labels来查看标签,这是大多数获取资源的 kubectl 命令的一个参数。

kubectl get all --show-labels
kubectl get pods --show-labels

显示标签的 kubectl 命令输出

让我们为 pod 创建一个名为 author 的新标签。请记住,您可以向所有资源添加标签,因此当我们添加标签时,我们将在名称前面使用po,这告诉命令这是一个 Pod 资源。我们使用kubectl label,后跟资源的名称,带标签的key=value

kubectl label po/hellogopher-f76b49f9-95v4p author=percy

获取 pod 现在应该显示您已经添加了一个作者标签。

有时您可能想要更新一个现有的标签,它可能是一个版本标签,或者可能是作者已经更改。在这种情况下,您需要在命令中添加--overwrite参数。我们把作者换成钢铁侠吧。

kubectl label po/hellogopher-56d8758b6b-2rb4d author=ironman --overwrite

有时,我们可能想要删除标签,这只是通过使用相同的命令来完成,但是我们使用的是key-命令,而不是key=value。我们再来去掉作者标签。

kubectl label po/hellogopher-56d8758b6b-2rb4d author-

现在,如果您再次使用--show-labels获得 pod,它应该不再包含作者标签。

所以添加和删除标签非常简单,让我们看看如何使用它们来选择某些资源。

使用标签来定位资源被称为Selector。大多数 kubectl 命令接受--selector标志,该标志使用它们的key=value语法接受多个标签。您可以通过逗号分隔来指定多个选择器。

kubectl get pods --selector app=hellogopher

您也可以通过在等号前添加一个!来使用负值。

kubectl get pods --selector app!=hellogopher

现在,基于标签获取资源是很好的,但是想象一下,你有一个巨大的资源集群,这时标签就变得非常重要了。此外,在管理资源时,它们非常方便,并且您需要用同一个命令定位多个实例。让我们尝试删除所有标有 app=hellogopher 的 pod。这里我使用了-l,它是--selector的简写。

kubectl delete pods -l app=hellogopher

您应该会看到一条消息,提示 pod 已被删除,但是如果您尝试获取所有 pod,则会出现一个新的 pod。

请记住,部署中说我们希望 1 个 pod 一直运行,Kubernetes 会为您处理这个问题。所以,当旧的被删除时,一个新的被创建,不要感到惊讶。这就是我们想要的,如果你想删除一切,你必须删除部署。

活动、就绪和启动探测

Kubernetes 允许我们探测 pod 的状态

Kubernetes 的卖点之一是应用程序监控。我们可以使用探针来监控我们的应用程序,探针用于监控端点、TCP 套接字或 gRPC 端点等的状态。

我们有三个探测器

  • 活跃度 —检查容器是否正常运行,如果不是,它将尝试重启该容器。
  • Readiness —检查容器是否按预期启动,以及何时准备好供其他服务使用。
  • 启动 —该探针将禁用活性和就绪性探针,这是有充分理由的。想象一下,如果容器启动缓慢,并且在启动之前需要运行缓慢的进程,那么并发地,活性探测检查端点是否是活动的,并且它返回 500,然后活性探测重新启动。在启动最终完成后,启动将启用活跃度和就绪性。

我们将从了解如何轻松创建一个简单的就绪性探针开始。我们将添加一个探测错误端口的探测器,该端口是 pod 不会暴露的。然后,我们将继续检查我们如何能看到为什么吊舱从未准备好。

当我们添加一个需要定义的探针来检查 Kubernetes 的状态时,有一些不同的探针,最简单的一个是 HTTP 探针,它发送一个 HTTP 请求并期望一个 200 响应。您可以找到可以添加到文档中的所有探头类型和配置。

更新hellogopher.yml来定义 readinessprobe,注意我们使用了错误的端口。

hellogopher.yml —检查容器是否就绪的就绪探测器

删除旧的部署,并重新部署它(如果您不记得本文中如何回溯)。

重新部署部署后,让我们看看如何找出问题所在。

运行kubectl get all获取关于部署和 pod 的信息。获取 pod 名称,我们将使用该名称来描述它。描述是 Kubernetes 中获取资源详细信息的一种方式。

ku bectl—pod 未准备好(0/1)

复制名称并描述它。

kubectl describe pod/hellogopher-df787c4d5-gbv66

请注意,它将打印关于 pod 的大量日志信息。最后,有一个名为Events的部分显示了发生的所有事情。

在 event 部分,您应该看到失败原因,这应该是准备就绪探测。

kubectl describe —显示失败的事件

您可以将在hellogopher.yml中定义的端口切换到 8080,并重新部署,看看它是否工作。

让我们看一下活性探测,它的工作方式与就绪探测相同。该探测器一直运行,以检查启动后容器是否在工作。

为了测试这一点,我们需要添加探针并更新 go HTTP 服务器,以便在 10 秒钟后返回一个失败。然后,我们将更新旧的部署,而不是删除它。

活性探测器 YAML 看起来与就绪探测器完全一样,但是多了一个名为failureThreshold的字段,它表示在重启之前允许容器失败的次数。

hellogopher.yml —更新了活性探测

更改 YAML 后,我们将更新main.go然后重建 docker 映像,并更新部署以使用新的映像版本。

在此之前,我想确保我们删除了旧的部署和创建的任何服务

kubectl delete service/hellogopher
kubectl delete deployment hellogopher

我们将让它在运行 10 秒钟后开始返回 HTTP 状态 500。

main . go-10 秒后失败的 HTTP 服务器版本 2

用 2.0 的新标签重建图像。

docker build -t programmingpercy/hellogopher:2.0 .

现在我们有了新的映像,让我们用版本 1 运行部署,并在运行时更新它,看看会发生什么。

kubectl create -f hellogopher.yml

如果您现在用kubectl get all检查,您将看到它已经启动并运行,但它使用的是 YAML 文件中指定的我们映像的 1.0 版本。让我们通过使用set image来更新 docker 图像。第一个参数是部署名称,然后是 YAML 中定义的 pod 名称,在我们的例子中是 hellogopher。

kubectl set image deployment/hellogopher hellogopher=programmingpercy/hellogopher:2.0

如果您运行kubectl get all,您可以看到新的 pod 是如何首先创建的,一旦它准备好了,旧的 pod 就会被删除。

您还可以通过修改hellogopher.yml文件,然后运行kubectl apply命令来更新集群。您可以通过更改配置中的 docker 映像版本标签,然后运行它来尝试一下。应用非常有用,因为您不必手动删除资源。它将检测任何需要进行的更改并执行它们。

kubectl apply -f hellogopher.yml

之后,您可以继续运行状态检查,并看到重启计数在 pod 上缓慢上升,现在每次在 10 秒钟后,当容器响应 3 次失败的探测尝试时,就会发生这种情况。

显示 pod 正在重新启动的事件

太好了,我们有办法在应用程序出现问题时重启它。我们都知道修复所有失败软件的秘诀是重启。

我们在这里所做的只是触及了您可以用探针做的事情的表面,但是因为我们处于学习阶段,所以让我们保持简单。

调试 Pod

当我们的软件失败时,重启可能会解决问题,但通常有一个潜在的原因。在 K8,调试并找出 pod 内部发生了什么是相当容易的。

最常见的方法是深入日志,您可以通过使用kubectl logs后跟 pod 名称来找到 pod 的日志。

kubectl 日志显示了 pod 的标准输出

在 K8 调试时需要学习的另一件非常重要的事情是带着终端进入 pod。现在,如果你按照前面的步骤,我们的分离舱会一直崩溃,所以进入会很困难。

我建议你创建一个 docker 镜像版本 3.0,要么增加main.go中的超时限制,要么你必须非常快速地工作。我将通过将代码修改为 100 秒来快速增加我的时间,用一个新的标记重新构建 docker 映像,并像以前一样在运行时设置映像。我不会涵盖如何做所有这些,你现在应该能够,或回溯,看看我们以前是如何做的。

您可以设置一个大于 100 秒的超时限制,或者现在完全删除它,因为我们已经完成了活动探测,这可能会避免您在测试教程的其余部分时出现崩溃的容器

从 pod 内部打开终端很简单,你需要在 docker 上安装一个终端,bash 或 ash 或其他。

您可以使用kubectl exec从 pod 执行命令。我们将添加一面代表互动的旗帜-it。然后指定 pod 名称,后跟一个--,它将本地命令与 pod 内部的命令分开,因此在--之后是在 pod 内部运行的命令。

我们想把它连接到终端,所以我们插入到 ash 的路径。

kubectl exec -it pod/hellogopher-79d5bfdfbd-bnhkf -- /bin/sh

吊舱内部的终端

在 pod 内部并能够运行命令使得调试变得容易得多。

Kubernetes 中一个非常常见的错误是oomkiller,也称为错误代码 137。这是应用程序内存不足时发生的错误。这可能是因为节点没有足够的内存,或者应用程序超出了其使用资源的限制。

如果您的应用程序超过了分配给它的内存限制,它将重新启动,如果它仍然超过限制,继续重新启动。因此,重启可以避免常规的内存泄漏,但是如果应用程序使用的内存真的超过了允许的范围,它就会反复杀死容器。

可视化集群

关于调试这个话题,很多人想要一个 UI 来查看正在发生的事情。幸运的是,我们可以通过 K8 实现这一点,有一个管理控制面板可用于可视化集群。

仪表板可以通过跟随官方文件来使用,或者因为我们使用的是 Minikube,它有很多插件,我们可以简单地通过运行来启用仪表板

minikube addons enable dashboard
minikube addons enable metrics-server 

您可以通过运行minikube addons list查看所有 minikube 插件。

通过运行以下命令打开仪表板。

minikube dashboard

您将看到一个令人惊叹的控制面板,它为您呈现了群集,该控制面板在监控群集时非常有用。

Kubernetes 仪表板—可视化集群

请注意,我们使用 Minikube 运行仪表板,您也可以单独运行它,Minikube 只是使它在您的开发环境中更容易。请参阅 K8 文档,了解如何在没有 Minikube 的情况下运行它。

在 UI 中,您可以查看工作负载、资源使用情况以及存在哪些资源。

您还可以查看日志并在 pod 中执行。如果您访问 pod 部分,您还可以看到 pod 事件以及我们之前通过终端看到的所有内容。

多个 pod、服务和命名空间

服务允许 pod 之间的通信

现在,我们正在运行一个具有单个部署单元的集群。大多数情况下,整个应用程序会有多个部署。

让我们通过添加一个运行的 MySQL 数据库来获得一些实际操作,我们的 hellogopher 应用程序可以连接到这个数据库。

我们将一步一步来,这样我们就可以探索 K8 内部的services服务用于向集群中的其他 pod 公开 pod。

第一步是添加运行 MySQL 容器的部署。现在,我们将通过硬编码的环境配置使它变得非常简单,现在不要担心这个。

我喜欢分离,所以我建议我们创建一个名为database.yml的新文件,其中包含所有与数据库相关的 K8 对象。有几种不同的方法来解决这个问题,有时你会在同一个文件中看到许多 Kubernetes 对象,这可以通过用一个---来分隔文件来实现,它告诉 Kubernetes 下面的行是一个新对象。

此时,创建一个名为kubernetes的文件夹来存储我们所有的 YAML 文件可能会比较好。

清理文件夹结构

让我们用一个部署对象填充database.yml。这将用于创建一个简单的 MySQL 数据库容器和一个值为password的 root 密码。

database.yml —使用硬编码密码部署 MySQL

一旦我们将 YAML 文件移动到它们自己的文件夹中,让我们用kubectl apply更新正在运行的集群。Apply 将检查任何更改并应用这些更改,而保留未更改的资源。它接受一个-f标志,这是文件夹的缩写,你甚至可以用-R让它递归。

kubectl apply -f kubernetes/

在应用之后,您应该看到两个部署启动并运行,hellogopher 和 MySQL。运行kubectl get all查看集群或访问仪表板。

您可以通过在容器中执行来尝试轻松登录 MySQL。获取 MySQL pod 的名称,并使用bash作为命令执行它。

kubectl exec pod/mysql-77bd8d464d-8vd2w -it -- bash
# You are now inside the pod terminal
mysql --user=root --password=$MYSQL_ROOT_PASSWORD

你应该登录一个 MySQL,我们还没有做任何事情,所以你可以输入exit离开终端。

现在当前设置的一个问题是第一个 hellogopher pod 不能到达 MySQL pod。为了实现这一点,我们必须使用service服务来允许 pod 之间或外部世界的访问。K8 将负责为 pod 设置 IP 地址和 DNS 名称。您甚至可以包括负载平衡。

解决两个 pod 之间连接的第一步是将它们放在同一个名称空间中。命名空间用于将资源或资源组隔离到同一个群集中。默认情况下,使用default名称空间。因此,现在我们的 pod 在同一个名称空间中,但是我们希望控制名称空间,提到名称空间是很重要的,因为资源使用的名称空间是 DNS 名称的一部分。

在 Kubernetes 文件夹中创建一个名为00_namespace.yml的新文件。Kubernetes 使用前缀00来知道创建资源的顺序,这很重要,因为我们的资源需要首先创建名称空间。

00_namespace.yml —将创建的第一个对象

接下来,我们将把database.yml重命名为01_database.yml,这样数据库就是创建的第二个项目。我们将在文件中添加一个---,如前所述,这告诉 Kubeternetes 在同一个文件中出现了一个新对象。在三连破折号之后,我们将创建服务,注意我们没有告诉 K8 服务连接到哪个资源,但是我们设置了一个选择器。

这就是服务如何通过应用到选择器匹配的所有其他对象来知道要公开哪些部署。所以在我们的例子中,任何带有标签app: mysql的资源都将被暴露。

01 _ database.yml 添加了服务的新数据库 YAML 文件

注意,我已经在每个对象上添加了namespace: hellogopher标签作为元数据。这是一种方法,另一种方法是使用use-context改变默认使用的名称空间。我们不会在这里讨论如何建立多个使用环境,你可以在文档中读到它们。

确保在hellogopher.yml文件中也添加了名称空间。然后从默认名称空间中删除任何现有的部署和服务,然后重新部署。

kubectl apply -f kubernetes

尝试使用kubectl get all获取资源,您会注意到没有资源。这是因为该命令使用默认的名称空间,我们可以通过在当前上下文中设置名称空间来将其设置为默认名称空间。

kubectl config set-context --current --namespace=my-namespace

现在当你获取资源时,你应该可以看到所有的资源。您可以跳过在 YAML 文件中指定名称空间,但是如果在同一个部署中有多个名称空间,这将非常有用。

现在我们有了一个部署,它将为我们设置 Pod 和 ReplicaSet,这是一个公开数据库的服务,我们已经将它们包含在它们自己的名称空间中。

注意,要访问 hellogopher 服务,您需要在以后使用-n将名称空间应用到 minikube 命令。

minikube service hellogopher -n hellogopher

连接到数据库

现在我们有两个 pod,一个运行我们的软件,一个运行 MySQL。我们需要连接到 MySQL one,它在一个服务的后面。

在 Kubernetes 中有两种方法可以找到需要的服务信息,比如 IP 和端口。你可以在这里阅读更多关于它们的细节

第一种,也是首选的生产方式是使用 DNS。Kubernetes 允许我们安装一个可以使用的 CoreDNS 插件。如果你安装了一个 DNS,你可以使用它的名字来引用服务,就像你在 docker-compose 中做的一样。

第二种方法是使用内置的发现。创建的每个 pod 都将获得一组为同一名称空间中的每个服务设置的环境变量。这要求首先创建服务,然后创建 pod。我们使用00_01_名称前缀解决了这个问题。

环境变量将被命名为{SERVICENAME}作为前缀。在我们的例子中,我们将服务命名为mysql。因此,在我们的服务之后创建的所有 pod 都将设置以下变量。

MYSQL_SERVICE_HOST=10.0.0.11
MYSQL_SERVICE_PORT=3306
MYSQL_PORT=tcp://10.0.0.11:3306
MYSQL_PORT_3306_TCP=tcp://10.0.0.11:3306
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_PORT_3306_TCP_ADDR=10.0.0.11

您可以尝试在 HelloGopher pod 中执行并打印变量来进行测试。

让我们更新 Go 代码来连接数据库,以确保一切正常。我将创建一个名为mysql.go的新文件,其中包含数据库代码,因为我们在本教程中没有关注 Go,所以我不会详细解释。代码将使用与我们的服务相关的环境变量连接到数据库,如果数据库不存在,它将创建它。

mysql.go —使用服务变量连接到数据库

添加之后,我们需要从主函数执行连接。

main.go 添加了要执行的数据库连接

太好了,连接数据库的代码准备好了。我们需要重新构建 docker 并更新我们的集群以使用新版本,我将用版本5.0标记它。

docker build -t programmingpercy/hellogopher:5.0 .

现在,我们需要更新集群以使用这个版本的代码,您可以在运行时通过更改 docker 映像,或者更新 YAML 并应用。由于我们还需要添加一些不会自动生成的环境变量,比如DATABASE_USERNAMEDATABASE_PASSWORDDATABASE_NAME,我建议我们更新hellogopher.yml。我们可以使用env字段添加这些变量,并为每个变量设置namevalue

我们还将确保将文件重命名为02_hellogopher.yml。因为我们希望它在 MySQL 服务之后创建。

02 _ hellogopher.yml 更新的 yml,带有数据库的环境变量

为了测试这一点,您可以应用新的配置,然后执行 MySQL pod 并查看可用的数据库,您应该会看到一个名为test的数据库。

mv kubernetes/hellogopher.yml kubernetes/02_hellogopher.yml
kubectl apply -f kubernetes/
kubectl exec pod/mysql-77bd8d464d-n5fx5 -it -- sh
#: mysql -p
show databases;

数据库显示在 SQL 窗格上,HelloGopher 窗格成功创建了测试

太好了,现在我们的豆荚连在一起了!

配置映射和机密

配置映射和秘密在 Kubernetes 中用于配置应用程序

你可能已经注意到,现在我们在 YAML 的文件中有明文的硬编码密码。正如您所猜测的,这不是一个好的做法。

Kubernetes 允许我们使用configmapsecrets来处理配置和秘密。您可以在文档中找到详细信息。

当您有非机密值时,应该使用配置映射,当您有敏感值(如密码)时,应该使用 secret。

让我们用适当的解决方案代替环境变量。我们将把DATABASE_NAMEDATABASE_USER存储在一个配置图中,但是密码是保密的。

让我们从创建 Configmap 开始,您可以从字面量(基本上是将值设置为字符串)开始创建。您也可以使用使用换行符作为分隔符的文件。因为你通常有多个环境变量,我更喜欢使用一个文件。

# Using Literal 
kubectl create configmap myConfigMap --from-literal=log_level=debug
# Using a file
kubectl create configmap myConfigMap --from-env-file=path/to/file

让我们从试验开始。创建一个名为dbConfig.properties的新文件,并将以下值插入其中。

dbConfig.properties —我们的非机密数据库属性

然后我们可以使用create configmap命令创建这个配置图。

kubectl create configmap database-configs --from-env-file=dbConfig.properties 

然后,您可以通过指定要自省的配置映射的名称来查看配置映射或有关配置映射的详细信息。

kubectl get configmapskubectl get configmap/myConfigMap -o yaml

接下来,我们需要更新02_hellogopher.yml来开始使用配置图。为了使用 configmap,我们将用valueFrom替换每个环境变量的value字段。这个属性接受一个对象,我们将在configMapKeyRef中传递这个对象。这是 Kubernetes 引用同一名称空间中某个配置映射的一种方式,使用name表示配置映射,使用key表示我们想要的特定值。

这是 YAML 的更新版本,它使用我们的新配置图获取值。

02_hellogopher.yml —我们用 valueFrom 替换 value 字段

您可以通过应用新的更改,然后获取日志来查看一切是否仍然工作,来尝试这种方法。

kubectl apply -f kubernetes/

现在这是一个很大的改进,我们仍然有密码,但我们会先看看。在我们这样做之前,我想解决当前 configmap 方法的一个问题,如果您有许多环境变量,正如您可能理解的那样,这将成为在 YAML 中配置的大量文本。

但是,您可以应用整个 configmap,而无需专门分配每个键值。我们可以通过添加一个envFrom字段来做到这一点,该字段接受 YAML 内容器上的配置图的名称。这将使所有的配置键作为环境变量出现在 pod 中。

这里是我们这样做的要点,注意我不再需要分配DATABASE_NAMEDATABASE_USER,因为它们在配置图中有定义。

02 _ hellogopher.yml 改为使用 configMapRef 应用整个 ConfigMap。

如果您想确保它仍然工作,可以继续并重试部署。

现在,我们已经创建了一个部署使用的 ConfigMap,但是我们也向这个 configmap 引入了一个很小的奇怪的依赖项。没有手动创建它的人将无法部署,因为他们没有它,而我们不能拥有它。

一个非常简单的解决方案是添加一个新的 Kubernetes 对象来创建 configmap。由于这与数据库有关,所以我将它添加到01_database.yml文件中。再次,一个新的对象,所以我们需要通过在新的一行上添加---来划定它的界限。因为这是一个常规的配置,没有秘密,我们可以简单地预置一个默认值。

我将移动dbConfig.properties中的所有设置,以便您可以删除该文件。请记住,我们将它添加在01_database.yml文件的底部。

01 _ database.yml 将其添加到文件的底部

删除旧的手动创建的配置映射并重新应用集群。

kubectl delete configmap database-configs
kubectl apply -f kubernetes/

查看创建的配置图,查看日志,并确保一切仍在运行。到目前为止,您应该已经熟悉了。

是时候处理最后一块了,秘密密码。许多应用程序都需要存储机密。对我们来说幸运的是,这几乎和 ConfigMap 完全一样,只是一个 SecretMap

我们将从对我们的秘密值进行 base64 编码开始,所有的秘密都应该以 base64 格式存储。请记住,这是不安全的。

percy@pc038:~/private/kubernetes/hellogopher$ echo -n "password" | base64
cGFzc3dvcmQ=

我们将获取输出的值,并将其放入清单文件01_database.yml。正如 configmap 一样,我们将创建一个秘密对象,用于存储我们可以引用的秘密。

01_database.yml中,在底部添加以下要点。

01 _ database.yml 在底部添加了一个秘密对象映射

我们还需要更改02_hellogopher.yml来使用这个秘密。用以下要点替换环境变量DATABASE_PASSWORD。正如我们使用configMapRef一样,我们现在将使用secretKeyRef。语法是一样的。

02_hellogopher.yml —密钥被引用

应用更改,并看到它创建了秘密。

kubectl apply -f kubernetes/

您现在可以通过运行以下命令列出所有存在的秘密

kubectl get secrets

然后选择要反省的名称,并通过提供它们的名称来显示关于某些秘密的更详细的信息。

kubectl get secrets database-secrets -o yaml

如果你想确定,从 pod 中获取日志以确保它工作。

现在你可能会想,嘿,我们仍然有“明文”密码,因为 base64 没有增加任何保护。幸运的是,我们可以用 Kubernetes patch 命令替换这些值。这太棒了,因为我们可以在 CI/CD 中自动进行秘密修补。

我们使用的命令是patch secret,后跟秘密对象的名称。然后我们指定 JSON,因为我们希望补丁请求使用 JSON 格式。

该命令的输入作为有效负载-p标志发送,它接受一组要应用的更改。op代表操作,是我们想要执行的操作,在我们的例子中是替换。path是秘密的完整路径,通常是/data/your-secret-name,然后是值。记住该值应该是 base64 编码的。

kubectl patch secret database-secrets --type='json' -p='[{"op" : "replace" ,"path" : "/data/DATABASE_PASSWORD" ,"value" : "test"}]'

替换密码后,尝试重新应用更改并获取日志来验证数据库连接是否失败。

资源有限

吊舱失控旋转,在无限量的内存中旋转

在我们结束本教程之前,我想说明一个我们还没有触及的重要方面。现在,我们创建的资源已经设置好了,一切都如我们所期望的那样工作,但是 pod 可以自由地使用他们想要的计算机上的任何资源。

有两种类型的设置你需要熟悉limitrequest

请求— 是节点需要提供的最小可用资源,以便在其上创建 pod。
限制— 是允许您的 pod 使用的最大资源量。除非指定,否则 pod 可以在节点上使用无限量的资源。

您可以使用top命令查看您当前的 pods 正在使用多少资源。

Pod 资源资源用量

在图中,您可以看到我们列出了 CPU 内核和已用内存,这是两种最常见的资源。

让我们为 hellogopher pod 添加资源限制,然后您可以尝试自己限制 MySQL。

hellogopher 是一个超级简单的 API,它不需要一个完整的 CPU 内核,所以我们首先将它限制在 0.1 个内核,通常你会看到一个数字,如代表 0.5 个 CPU 内核的500m。所以要得到 0.1 核,我们需要把极限设为100m

对于 CPU 资源单位,数量表达式0.1等价于表达式100m,可以读作“一百毫 CPU”——Kubernetes 资源文档

该服务不需要太多内存,因为它是一个超级简单的 API。我们将它限制在10Mi,你可以在内存文档中阅读更多关于所有可用单元的信息。

记住,要求是最低的,限制是最高的。让我们将它应用到我们的配置中。

hellogopher.yml 更新了资源限制

设置限制很好,因为它可以使您的部署更便宜,避免使用不必要的资源。不要把它设置得太低,内存不足很可能会导致你的 pod 崩溃。

您可以继续并重新应用配置以确保其工作,并尝试在数据库上设置资源。

结论

这条路并没有到此为止,在掌握 Kubernetes 之前还有很多东西要学

在本教程中,我们介绍了如何配置一个简单的 Kubernetes 应用程序,以及如何使用 Minikube 在本地节点上运行它。我们只是触及了基础,在成为 Kubernetes 大师之前还有很多要学的。希望我已经帮助你开始走上这条路。

本教程的最终代码可以在 GitHub 上找到。

希望您对 Kubernetes 中使用的组件有了更多的了解。我可以推荐《Kubernetes the hard way 》( Kubernetes the hard way )( T16 ),这是凯尔西·海托华(Kelsey Hightower)的一个更深入的教程,但也更难。

将 Kubernetes 部署到生产环境中本身就是一个完整的教程。我建议在kubeadmkops上搜索,以熟悉用于部署的工具。轻松部署 Kubernetes 的一种方式是在 AWS、Google 或 Azure 上使用托管服务。它们让您可以非常轻松地进行部署,而不是自己设置一切。

Kubeadm 是一个无需任何麻烦的设置就能轻松建立新集群的工具。

Kops 是一个在 AWS 上创建和维护生产级集群的工具。

我建议查看一下的配置环境,这样你就可以在开发和生产环境之间切换,在生产环境中,我建议添加更多的标签。

我推荐另外两个工具,它们可能会有所帮助,希望我很快会创建关于这些工具的教程。

用于从 docker-compose 创建 Kubernetes 配置的工具。

Helm —简化 Kubernetes 应用程序的安装和管理。可以把它看作是 Kubernetes 应用程序的包管理器。

我希望你喜欢它,随时联系任何问题、反馈或未来文章的提示。

了解 SQL Server Management Studio —第 12 部分排名窗口函数

原文:https://towardsdatascience.com/learn-sql-server-management-studio-part-12-ranking-window-functions-b66b5e331d7a

如何使用 SQL 获取每个用户的最新记录?循序渐进教程

迈克尔·泽兹奇在 Unsplash 上的照片

在最后几集里…

你好。欢迎来到 SQL 和 SQL Server Studio 系列教程的第 12 期。我的目标很简单:让你熟悉和适应这个工具和语言。“这有什么关系?”我知道你在问。事实证明,好奇心和副业往往是被新项目选中甚至获得新工作的关键。事实上,您已经使用了一个重要的工具,比如 SQL Server Studio,并且编写了一些 SQL 查询,这将会给您一个清晰的开端。

别忘了回来😉。

期待什么?

数据以多种形式出现。在一段时间内,一个数据集(比如一个日志表)可能包含一个用户或一台机器的多条记录。我们怎么可能分割这些数据,只检索给定用户、机器或 X 的最新时间戳呢?

这就是排名窗口函数的用处。它们的语法比我们在其他章节中看到的常规 SELECT 或 CRUD 操作要复杂一些(见上文)。老实说,我永远也搞不懂那个语法,总是需要参考我的神圣文本文件😉。

知道如何利用它将解决许多令人头痛的问题,并允许您对记录进行排序并过滤掉不需要的内容。事不宜迟…

介绍

我们假设您已经设置好了 SQL Server Studio 环境。如果你需要任何帮助,请查看上面提到的第一集。

让我们首先创建一个新表“日志”:

CREATE TABLE Logs(Timestamp datetime,Username nvarchar(100))

现在让我们用一些数据填充它:

INSERT INTO [HR_data].[dbo].[Logs]VALUES(GETUTCDATE(), 'Max'),(GETUTCDATE()-1, 'Max'),(GETUTCDATE()-2, 'Beth'),(GETUTCDATE()-3, 'Beth')

让我们添加更多的噪音,通过复制我们刚刚插入的内容。这将插入相同的行,只是时间戳会有几秒钟的差异,这取决于您执行查询的时间。

/* This is to add a bit more records/noise in our dataset */INSERT INTO [HR_data].[dbo].[Logs]VALUES(GETUTCDATE(), 'Max'),(GETUTCDATE()-1, 'Max'),(GETUTCDATE()-2, 'Beth'),(GETUTCDATE()-3, 'Beth')

运行快速选择查询来检查数据集:

SELECT * FROM [HR_data].[dbo].[Logs]

这将返回 8 条记录。

排名窗口功能

我们如何提取表中任何一个用户的最新时间戳呢?排名窗口功能来拯救我们。这是一个本机 T-SQL 函数。

SELECT Timestamp, UsernameROW_NUMBER() OVER(ORDER BY Timestamp DESC) AS Row#FROM [HR_data].[dbo].[Logs]
  • 我们从选择时间戳和用户名开始。到目前为止一切顺利。
  • 然后我们添加 Row_Number()函数,它将为每条记录定义一个行号。
  • OVER()有助于定义我们的目标。在这种情况下,我们将查看时间戳。在 OVER()函数中,我们精确地定义了一个 ORDER BY,并定义了它是升序(ASC)还是降序(DESC)。
  • 最后,我们设置了一个别名,这里将该列称为 Row#

输出为:

好吧,至少他们有排名。但是我们需要的是找到任何给定用户的最新时间戳。我们需要在 ROW_NUMBER()中添加另一个元素。PARTITION BY 的使用将帮助我们对 FROM 子句引用的数据集进行分区。正如我们在上面看到的,当不使用 PARTITION BY 时,结果的所有行都被视为一个组。

我们可以这样编写我们的查询:

SELECT Timestamp, UserName,ROW_NUMBER() OVER(PARTITION BY UserName ORDER BY Timestamp DESC) AS Row#FROM [HR_data].[dbo].[Logs]
  • OVER 子句中添加了 PARTITION BY。
  • PARTITION BY 要求我们至少定义一列,在我们的例子中,我们按用户名进行分区。

输出:

麦克斯和贝丝各有 4 张唱片。他们的记录是按时间戳排序的。

  • 该分区由有针对性的用户名组成。有两个不同的用户名,ROW_NUMBER()根据分区对这些记录进行排序。
  • 这就是为什么 Max 和 Beth 的记录从 1 到 4,而我们之前的排名是从 1 到 8。

最后一步

我们最初的目标是为每个用户获取最新的时间戳。如上面的输出所示,我们知道哪个记录是每个用户的最新记录。只看第# = 1 行。我们怎样才能把这些行分离出来呢?嵌套查询将帮助我们。

我们将首先重用运行良好的内容:

SELECT Timestamp, UserName,ROW_NUMBER() OVER(PARTITION BY UserName ORDER BY Timestamp DESC) AS Row#FROM [HR_data].[dbo].[Logs]

然后,我们用圆括号将它括在另一个 SELECT 语句中,并为嵌套查询提供别名“x ”:

SELECT * FROM(
SELECT Timestamp, UserName,ROW_NUMBER() OVER(PARTITION BY UserName ORDER BY Timestamp DESC) AS Row#FROM [HR_data].[dbo].[Logs]
) x

最后一步是添加一个 WHERE 条件,这样我们只查看 Row# = 1:

SELECT * FROM(
SELECT Timestamp, UserName,ROW_NUMBER() OVER(PARTITION BY UserName ORDER BY Timestamp DESC) AS Row#FROM [HR_data].[dbo].[Logs]
) x
where x.Row# = 1

输出:

我们现在有了两个不同用户的两个最新时间戳。任务已完成🙂

最后的话

我们现在掌握了一个新工具,排名窗口功能。这可以解决许多问题,例如,当我们面对相同的行,只是时间戳不同。ROW_NUMBER()结合 PARTITION BY 将允许我们基于一个或多个属性对数据集进行分区。在我们的例子中是用户名。然后,我们可以根据时间戳对行进行排序。

最后,嵌套查询允许我们挑选出给定的等级。在我们的例子中,我们关注的是 rank/row #1,即最近的时间戳。

我希望你觉得这篇文章有用,让我知道你的想法,或者如果有我应该涵盖的主题。与此同时,请随时订阅并关注我。下次见!

快乐编码🎉!

感谢阅读!喜欢这个故事吗? 加入媒介 可完整访问我的所有故事。

使用 Google BigQuery 上的电子商务数据集学习 SQL

原文:https://towardsdatascience.com/learn-sql-with-the-e-commerce-dataset-on-google-bigquery-6864989e1494

我经常被问到这个问题:我学习了 SQL 的基础知识;现在怎么办?我的回答是:开始用 SQL 回答现实世界的业务问题。

图片由 Unsplash 上的克拉克街商业拍摄

学习任何东西都有一个棘手的问题,那就是从理论到实践。我来自越南,在那里学习驾驶是一场噩梦。说真的,查查“越南交通”,你就知道我的意思了。所以我知道有很多人有驾驶执照,但是在驾校之外从来没有驾驶过。

对越南人来说,开车入门很难,也很吓人。人们在获得驾照后推迟驾驶的时间越长,距离他们最后一次驾驶的时间也就越长。过了一段时间,他们开始忘记基础知识,这使得开始变得更加困难。

照片由莱昂妮·克拉夫Unsplash 上拍摄

学习 SQL 也差不多。在你学会写一个简单的陈述后,现在做什么?建立一个本地数据库很难,并不是每个懂 SQL 的人都能做到。此外,处理用 Excel 创建的假数据一点也不好玩。

所以,我们去“兜风”,好吗?

设置 BigQuery 沙箱

首先,我们应该启动我们的隐喻汽车。BigQuery 是 Google 的托管数据仓库。作为一项托管服务,您不必花费数小时来学习如何在本地创建 MySQL 数据库并在那里加载数据。如果你有一个谷歌账户,即使没有信用卡,你也可以在五分钟内开始编写 SQL 查询。

BigQuery 沙盒就像一辆模型车,让你练习驾驶。谷歌已经收集了数百个真实世界的公共数据集,你可以查询。从维基百科、比特币这样的海量数据集到经济学数据,你都可以找到你感兴趣的行业的数据,并以此为起点。

首先,你必须访问这个 URL 并使用你的谷歌账户登录,如果你还没有这样做的话。接下来,选择您的国家,同意 TOS 并继续。您需要创建一个项目来开始使用 BigQuery sandbox,这是一个免费的测试环境,允许您每月查询高达 1 TB 的数据。为您的项目选择一个唯一的名称并创建一个。

创建谷歌云项目(图片由作者提供)

只需一会儿,您现在就可以访问 BigQuery 沙箱了!选择Add dataPin a projectEnter project name,输入bigquery-public-data。你可以在这个项目下看到所有 240 个免费的公共数据集。

固定公共数据集项目(按作者分类的图像)

在本文中,我不会讨论如何使用 BigQuery UI。但是如果你想的话,可以看看这篇文章,或者更好的是 Coursera 上的这篇专业化文章。

Look 电子商务数据集

既然我们发动了汽车,让我们来了解它。使用固定的bigquery-public-data项目,向下滚动,点击more results,您应该能够找到thelook_ecommerce数据集。您可以选择不同的表,探索该数据模式,并使用 preview 检查实际数据。

虚构的电子商务数据集(图片由作者提供)

这是一个虚构的时尚电商,叫做 Fashionly。我们有一个网站,客户可以用它来购买我们的产品。

当客户访问我们的网站并注册账户时,她的旅程就开始了。她在注册时使用的信息存储在 users 表中,注册日期对应于客户的创建日期。

一旦注册,她可以随时访问我们的网站。每次她访问时,events 表中都会生成一个新事件。我们知道她从哪里浏览我们的网站,以及她采取了什么行动(访问、将产品添加到购物车或购买)等信息。我们还知道她来自哪里(流量来源)以及她使用什么类型的浏览器进行访问。

当她购物时,在 orders 表中会创建一个订单。订单发货、取消或退货的时间戳也记录在这里。

每个订单可以包含一个或多个项目,记录在订单项目表中。order items 表也与产品相关联,我们在其中存储产品的详细信息。

我们有几个仓库存放存货。当新库存进入仓库时,记录库存创建日期。同样的事情也发生在产品销售的时候。

现在让我们使用您的 SQL!

想象自己是 Fashionly 的所有者、财务、产品或运营负责人。你想了解你公司的什么情况?这里有一些问题让你开始。

  • 我们每天的销售额是多少?与昨天,上周/月/年的同一时间相比,是高还是低?
  • 谁是我们的客户?按人口统计细分。他们的购买行为是什么?他们买得多还是少?我们签约新客户的速度有多快?
  • 我们卖得最多和最少的是什么?我们靠什么赚钱?某些产品/品类是否更适合特定的客户群体?
  • 我们在哪个地理位置做得好/不好?我们的仓库能覆盖所有区域吗?我们应该在何时何地考虑扩大我们的履行能力?
  • 我们在哪个营销渠道做得好?我们目前的组合是什么?趋势有变化吗?

为了进一步了解你的业务并做出更好的决策,你可以问很多很多更多的问题。

让我们一起来看几个问题。我们将从一个基本的查询开始,以了解我们销售了多少,每天有多少订单,以及有多少客户购买了。为此,我们必须将订单和 order_items 连接在一起。请确保在您的查询中过滤掉已取消和退回的订单。

基本商业信息(图片由作者提供)

我们可以看到,我们每天的销售额约为 30–40,000 美元,处理约 400 份订单,约有 300 名客户购买。有些日子,我们的销售额远远超过平均水平。

接下来,让我们看一个稍微复杂一点的查询。我想知道客户第一次下单购买最多的是哪个品类。要做到这一点,我们必须使用一个窗口函数来识别第一个订单。然后我们可以按产品类别分组,计算我们的收入和用户数。

首批订单的主要类别(图片由作者提供)

现在,如果我想创建一个活动来推动新用户的获取,我知道外套可能会给我带来最多的收入,而牛仔裤会给我带来最多的客户。

总结

BigQuery 是一种让您轻松开始学习 SQL 的方法。只有走出去“驱动”,也就是应用 SQL 来回答现实世界的业务问题,人们才能真正学会如何使用这种受欢迎的技能。

这里有一些在 BigQuery 中使用 SQL 的最佳实践,我想你应该知道。

感谢您的阅读,希望这篇文章对您有所帮助。

了解 MLOps 的核心——构建机器学习(ML)管道

原文:https://towardsdatascience.com/learn-the-core-of-mlops-building-machine-learning-ml-pipelines-7242b77520b7

实施 MLOps 使人工智能发挥作用的实用指南

为什么需要 MLOps

众所周知,为了让人工智能发挥作用,真正的挑战不是建立一个机器学习(ML)模型,而是建立一个集成的 ML 系统,并在生产中持续运行它。这就是为什么 MLOps 的概念被开发出来,并且它正在数据科学家、ML 工程师和 AI 爱好者中迅速获得动力。

由于近年来对最大似然算法的大力研究,人工智能和最大似然在创造新的商业机会、提供更好的客户体验、提高运营效率等方面向组织展示了巨大的潜力。然而,在 Jupyter 笔记本中训练 ML 模型和在生产系统中部署 ML 模型之间存在巨大的技术差距。因此,许多公司还没有找到如何实现他们的人工智能/人工智能目标的方法,他们向人工智能操作系统寻求帮助,希望通过人工智能操作系统,他们可以使人工智能在现实世界的生产环境中工作,以真正获得人工智能和人工智能驱动的解决方案的好处

因此,我认为开发一系列解释如何实施 MLOps 实践的实用指南是非常有用的。这些指南将包括对关键 MLOps 组件、设计考虑因素以及实施示例代码的解释。

如果我们纯粹从工程和实施的角度来看待 MLOps,任何端到端 MLOps 解决方案都有三个核心部分:

  • 第一部分是— 数据和特征工程管线
  • 第二部分是— ML 模型训练和再训练管道
  • 第三部分是— ML 模型推理和服务管道

MLOps 以一种 自动化 的方式将上述三条流水线缝合在一起,并确保 ML 解决方案是 可靠的可测试的可再现的 。在这篇博客的剩余部分,我将一点一点地解释这三条管道。

照片由 Hitesh ChoudharyUnsplash 上拍摄

实施 MLOps 解决方案的关键构建模块

下图显示了上述 3 条 MLOps 管道的所有关键组件。正如您所看到的,构建端到端的 MLOps 解决方案可能非常复杂,但请不要担心,在接下来的系列文章中,我将逐一详细解释它们,并演示如何实现每个组件。

学习 MLOps 的核心——构建 ML 管道(图片由作者提供)

数据和特征工程管线

我将从数据和特征工程管道开始,因为数据是任何 ML 系统的核心。一般来说,数据管道是指提取、转换和加载(ETL)管道,数据工程师通过这些管道从源系统摄取原始数据,然后将数据清理并转换为可靠的高质量信息,供下游数据消费者使用。如果您有兴趣了解如何构建数据管道,我有一篇单独的文章。学习数据工程的核心——构建数据管道。请随意阅读。

ML 的独特之处在于 原始数据 需要转换成特征以便 ML 模型可以有效地从数据中学习有用的模式。将原始数据转换成特征的过程称为 特征工程。 因此,这篇文章的重点将是实现特征工程管道,并介绍什么是特征库。

有各种各样的特征工程技术,包括插补、异常值处理、宁滨、对数变换、一键编码等等。如果你想了解更多,我相信你可以谷歌一下,在特色商店里会有很多博客。然而,我想在这里强调的是,通常对于机器学习(ML)项目,数据科学家将大量的时间和精力投入到特征工程中,以便在他们的机器学习模型上获得体面的性能。因此,存储这些特征以供发现和重用是有价值和必要的。因此,“功能商店”的概念得到了发展,并且在功能商店上有相当多的开源和商业产品。然而,功能存储不仅仅是为了功能重用。 特征库是机器学习的数据管理层,允许您共享&发现特征并创建更有效的机器学习管道。 任何 MLOps 解决方案中最重要的两个部分——模型训练和模型服务——都可以利用特征存储。总之,功能存储提供了以下功能和优势:

  • 特征发现和重用
  • 用于模型训练和服务的一致特征工程
  • 监控数据和特征漂移
  • 训练数据集的再现性

对于完整的 MLOps 解决方案,必须为模型训练和服务建立特征工程管道和特征存储。这是一个非常高层次的介绍。我将很快发表一篇关于特色商店的博客。如果您想在新日志发布时收到通知,请随时关注我。

ML 模型训练和再训练管道

一旦特征工程完成,下一步将是 ML 模型训练。ML 模型训练是一个高度迭代的过程,这就是为什么它也被称为 ML 模型实验 的原因。数据科学家必须使用不同的参数和超参数运行许多实验,以便找到具有最佳性能的模型。因此,数据科学家需要一种系统的方法来记录和跟踪每次实验运行的超参数和指标,以便他们可以比较每次运行,并找到最佳运行。有一些开源库可以帮助数据科学家记录和跟踪模型实验,如 mlflow 。mlflow 是一个管理 ML 生命周期的开源平台,包括实验、再现性、部署和中央模型注册。

除了模型训练之外,数据科学家还需要 评估和测试 模型,然后才能适应将模型放入真实的生产环境。他们需要确保该模型在真实世界的实时数据中表现良好,就像在训练数据中一样。因此,选择正确的测试数据集和最相关的性能指标是非常重要的。有了 MLOps,模型训练和模型评估都需要自动化。

ML 驱动系统的关键挑战是模型性能由用于训练它的数据决定。然而, 数据总是变化的。 因此,对于大多数 ML 驱动的场景,重新训练模型变得绝对必要。通常,有几种方式来触发模型重新训练:

  • 基于计划的 —以预定义的时间间隔,例如每周一次,用最新数据重新训练模型。根据数据变化的速度和其他业务需求,计划频率可能会有很大差异。
  • —当识别到漂移时,如数据漂移、特征漂移或模型性能恶化,触发模型重新训练。为了实现完全自动化的方法来重新训练模型,需要有一个健壮的监控解决方案来监控数据变化和模型变化。

MLOps 解决方案的理想结果是不仅自动重新训练模型,而且基于预定义的模型指标评估新训练的模型并选择最佳运行。

ML 模型推理/服务管道

一旦模型被训练和评估,下一步将是把模型放到真实的生产环境中使用。一般来说,有两种方式来为训练好的 ML 模型服务:

  • 离线批量推断 —调用 ML 模型,以一定的频率进行预测。频率可以高达每天、每周,甚至更长,但也可以低至每分钟。当频率非常低时(非常频繁),您可以将流数据管道与 ML 模型推理集成在一起。当批量推理的数据量非常大时,就需要一个分布式计算框架。例如,您可以将模型作为 Spark 用户定义函数(UDF)加载,并使用分布式计算进行大规模并行推理。
  • 在线实时推理 —将 ML 模型封装为 REST API 端点。对于在线实时推理,打包的 ML 模型通常被嵌入到应用程序中,其中模型预测是根据请求生成的。当请求量很大时,可以将模型打包成容器映像,部署到 Kubernetes 环境中进行自动缩放,以响应 大量的预测请求。

如前所述,数据决定了模型的性能。在现实世界中,数据总是在变化,模型性能也不可避免地会发生变化,而且经常会恶化。因此 拥有一个监控解决方案来跟踪生产模型的变化以及用于反馈这些变化的数据/特征是非常必要的。 一旦识别出重大变更,监控解决方案需要能够触发模型再训练或者向相关团队发送通知,以便可以立即进行修复。这对于业务关键的 ML 驱动的应用程序尤其必要。通常,ML 模型监控包括以下 4 个类别:

  • 预测漂移
  • 数据/特征漂移
  • 目标漂移
  • 数据质量

监控 ML 解决方案既有开源产品,也有商业产品。例如,显然 AI 是一个开源的 Python 库,用于评估、测试和监控 ML 模型从验证到生产的性能。

到目前为止,我们已经涵盖了完整 MLOps 解决方案的 3 个关键管道——数据和特征工程管道、ML 模型训练和再训练管道、ML 模型推理管道以及 ML 模型监控。

MLOps 解决方案的规模范围

MLOps 是一个相当新的概念,你可能会从上面的介绍中看出,MLOps 涉及到将许多不同的组件拼凑在一起,以便使 AI 在现实世界中工作。因此,许多人认为 MLOps 令人生畏且复杂。然而,当我们谈论实施端到端 MLOps 解决方案时,需要绘制一个规模谱

例如,如果您的 ML 解决方案是小规模和基于批处理的—批处理数据管道、批处理培训、批处理推理,并且数据量不需要分布式计算—那么实施 MLOps 并不困难,甚至数据科学家也可以成为“全栈”并拥有整个解决方案。然而,如果你谈论的是大规模的、持续的训练和实时的推理,这可能是相当复杂的,需要多个团队和不同的基础设施一起工作。

因此,在接下来的帖子中,我将解释一些 ML 参考架构和在不同规模下实现 MLOps 的最佳实践,因为每个规模可能涉及非常不同的技能集和不同的基础设施设置。敬请关注。

我希望你喜欢阅读这篇博客。如果你想在有新博客发表时得到通知,请随时关注我的 Medium,这肯定会激励我写更多。

如果想看到更多围绕现代高效数据+AI 栈的指南、深度潜水、见解,请订阅我的免费简讯— 高效数据+AI 栈 ,谢谢!

注:以防万一你还没有成为媒体成员,并希望获得无限制的媒体访问,你可以使用我的推荐链接注册!我可以免费给你一点佣金。非常感谢你的支持!

了解数据之谈——深入数据黑暗深处的 MLEs 之旅

原文:https://towardsdatascience.com/learn-the-talk-of-data-an-mles-journey-to-the-dark-depths-of-data-storage-bf4c4369a917

图片来自 PixabayElasticComputeFarm

让我们面对现实吧,如果有什么东西是数据科学家(DSs)或机器学习工程师(MLEs)会回避的,那就是还不是一个Numpy数组或pandas数据帧的数据。在不影射数据科学家对数据库的恐惧的情况下,让我们试着理解其中的一些概念。

作为 DS/MLE 为什么重要?

服务于数百万客户的公司每秒钟都会收集大量的结构化和非结构化数据。为这些客户提供毫秒级延迟,同时支持分析团队开展工作,这是一项挑战,需要精心设计的数据架构。大多数公司会有数据工程师来为我们做这件事。作为一个 MLE,我就不能幸福地忽略所有的方面,做好我的工作吗?大多数时候,答案是响亮的。出于一些原因,

  • 组织中的数据(尤其是大规模数据)可能会很快变得令人毛骨悚然。通过了解基本概念,您将能够自信地选择最有效的方法来为您的 ML 用例收集高质量的数据。
  • 通过 MLEs 拥有 ML 模型的数据源总是更有效,因为等待帮助可能会很慢。
  • 自己做事情的时候,可能需要去找数据工程师谈,解决问题。你的数据知识将决定你的谈话有多有意义。

这是给谁的?

这是 MLE 对数据世界的解释。这些知识大部分来自经验和自学。这是给那些,

  • 渴望了解大规模工作中的数据及其在公司中的应用
  • 接触数据但不了解概念,
  • 难道不是一个处理复杂数据管道的经验丰富的数据工程师吗

作为一个只触及数据表面的人,可能会有一些我没有意识到的细微差别,或者对我讨论的某些方面的其他解释。如果你看到任何需要添加/修改的地方,请在评论中告诉我。

有了这个小小的免责声明,让我们了解一些您可能会遇到的数据中的基本数据概念。下一次,您将能够自信地与数据工程师交谈,并与他们合作,以更快地解决您的问题。

体系结构

让我们首先了解一下,对于拥有大量数据的公司来说,典型的数据架构是什么样子的。

用于将不同来源的异构数据引入数据仓库的数据流(图片由作者提供)

应用程序将从关系(或事务)数据库中读取和写入数据。我们称之为操作 DBs。这些数据库针对读写记录(或事务)进行了优化。然而,直接读取/写入数据库是很昂贵的。因此,我们在两者之间使用一个缓存层,其中应用程序将读取或写入缓存,而缓存将读取或写入操作数据库(在通读/直写缓存上读取)。缓存可能类似于 Redis 。这些数据库被频繁地转储并存储到数据湖中,比如亚马逊 S3。数据库可以每隔几分钟复制一次,以避免丢失数据。此外,其他第三方数据可能会被写入数据湖。

接下来,这些数据被加载并集中到一个数据仓库中,然后被转换为适合分析的数据。最后,仓库中可用的干净且经过转换的数据可以直接使用,或者由报告层用于可视化和做出基于数据的决策。

数据湖与数据仓库

数据湖和数据仓库是公司数据流的焦点。它们是什么,如何互相恭维?数据湖存储几乎没有结构的数据,这意味着写入数据非常快。但是读取是昂贵的,因为在能够有效地使用数据之前,您需要推断模式并进行清理(称为模式读取)。数据湖为存储大量数据提供了廉价的存储。例如,您可以将在线运行数据库的垃圾存储到一个湖中,然后将其用于离线分析目的。数据湖的一些例子有亚马逊 S3 或者谷歌云存储。你可以使用节省空间的格式来存储数据。

这些数据随后被数据仓库使用。数据仓库通常在写入时执行模式,并使用结构化的 SQL 数据库。它们针对复杂计算(如数学运算、窗口等)进行了优化。),因此在数据仓库中计算相对更便宜和高效。例子有亚马逊红移

数据湖针对存储和非结构化数据进行了优化,而数据仓库针对计算和结构化数据进行了优化

我们还得到了更先进的技术,结合了两个世界的精华,被称为数据仓库雪花就是一个例子。例如,虽然雪花提供了数据仓库功能,如存储 SQL 表、复杂的分析功能,但您可以利用VARIANT数据类型来存储非结构化数据(如 JSON blobs)并有效地查询它们。雪花还提供了存储和计算实例之间的分离,能够分别针对它们进行优化以降低成本。

数据湖库将数据湖和数据仓库的优势结合到一个解决方案中

运营数据库与数据仓库

在最面向用户的方面,您会发现针对事务处理进行了优化的操作数据库(有时称为 OLTP 数据库)。他们就是这么做的。他们对单个记录(或事务)进行大量的创建、读取、更新和删除操作。换句话说,您不希望在这些数据库中加入复杂的分析功能。因此,像 MySQLPostGresdb 这样的东西在大多数时候都工作得很好。

两种解决方案中使用的存储类型是一个关键区别。操作数据库将逐行存储数据(或基于行的存储)。这是有意义的,因为操作数据库是对数据行进行操作的。然而,数据仓库使用更适合分析查询的列存储。

数据仓库的内部

现在让我们把注意力转向数据仓库。作为一名 DS 或 MLE,你将在这里度过大部分时间。

数据仓库的层次

数据仓库解决方案通常很复杂,有多个层次。您可以在其中找到以下几层,

  • 临时区域—这是源数据转储到的地方。运营数据库、第三方数据的转储将被加载到暂存区
  • 转换/存储层—来自临时区域的数据被聚合并转换到新表中。这些表格可以作为 ML 模型的数据集,或者为数据分析师提供产品/服务的洞察力
  • 报告层-在此基础上,您可以决定建立一个报告层,用于存储更多基于源数据和转换数据的定制表格,这些表格可由可视化报告工具直接使用

表格与视图

另一个需要注意的区别是表和视图之间的区别。表实际上存在于数据库中,而视图是虚拟的,可以按需填充。例如,对于偶尔使用的数据(例如,每隔几天训练一个模型),视图是一个很好的选择。这将帮助您节省存储空间。

事实和模糊表

当您整理来自仓库中不同微服务的所有源数据时,这些表被组织成事实表和 dim 表。它们构成了你的服务/产品的数据架构。理解事实表和 dim 表最简单的方法是考虑一个用户故事。让我们想象一个在线图书馆系统。其中一个用户故事可能是,

作为一个借书者,我需要能够去图书馆,选择一本书,并检查可用性

在这个故事中,我们可能有下列表格

事实表和维度表的组织(星型架构)。PK 指主键,FK 指外键(图片由作者提供)

星形与雪花形模式

将数据加载到仓库的目的是为分析处理提供一个集中的避风港。因此,拥有一个能够更容易/更快速地处理分析查询的模式设计非常重要。星型和雪花型模式是组织表以适应分析的方式。

我们讨论的用户故事中的模式被称为星型模式。您可以看到 dim 表是如何从事实表散开的。这是对现实的一种过于简化的表示,其中 dim 表的数量,也就是外键的数量实际上可以从几十个增加到 100 个。

雪花模式比星型模式更进了一步。它进一步规范化了 dim 表,允许 dim 表扇出到其他 dim 表。例如,如果您决定提供按作者搜索的能力,您可以引入另一个名为dim_author的 dim 表,并将Author ID作为外键添加到dim_book

雪花模式示例(作者图片)

如您所见,这允许对数据进行进一步分析,例如基于作者的分组等。

ETL/ELT 管道

ELT 管道是数据架构不可或缺的一部分。他们从杂乱的异构数据库中获取实时数据,并将这些数据泵送到同构数据仓库。您可以将 ETL 管道视为服务数据库数据到达数据仓库的过程。ETL 代表提取、加载和转换。让我们了解每一步发生了什么,

  • 提取-从异构源中提取数据。这可以通过自动计划、手动等方式完成。
  • 转换-清理源数据并构建转换后的表
  • 加载—将干净的转换数据加载到数据仓库中的目标数据库

但是由于存储变得越来越便宜,通常是先提取源数据并将其加载到数据仓库中,然后再转换数据。这个过程被称为 E LT (与 ETL 相对)。通常,现代数据仓库解决方案已经提供了大量现成的 ELT 功能(比如雪花中的 COPY INTO )。

如果你使用 AWS 这样的自管理服务,你可以使用他们的服务完成大部分的提取、加载和转换。例如, Amazon DMS (数据库迁移服务)为您提供了将数据从运行中的数据库复制到像 S3 这样的湖泊中的简单方法。然后 AWS Glue 可以帮你做大部分 ETL 工作,比如清理和转换数据,以及构建数据目录。

当处理数百个表时,转换表不是一件容易的事情。我们需要能够跟踪变更,版本控制转换逻辑。一个不断发展的公司,比如一家初创公司,会更加凸显这种需求。像 dbt 这样的工具可以帮助你做到这一点。您可以轻松地将您的dbt工作流与雪花等仓库解决方案集成。dbt的几个优点是,

  • 能够像代码存储库一样对转换逻辑进行版本控制
  • 通过dbt中的引用机制了解数据转换的谱系

结论

我们在这里讨论了许多不同的话题。有了这些知识,当谈到去哪里寻找数据时,你将很容易做出许多决定。此外,它将帮助你说数据工程师的语言,使你与他们的对话更有效。

首先,我们讨论了大规模运营的公司的典型高层数据流。接下来,我们比较和对比了各种数据存储,如操作数据库、仓库和数据湖。接下来,我们深入研究数据仓库的细节。从数据仓库的不同层开始,我们讨论了数据在仓库中是如何组织的。最后,我们讨论了什么是 ETL 管道,以及它们如何适应数据流。

参考

学习区分这些数据角色

原文:https://towardsdatascience.com/learn-to-differentiate-these-data-roles-8e4c37e77a6a

数据科学家、数据分析师、数据工程师、人工智能研究员、ML 工程师、ML DevOps 和业务分析师等

雷内像素上拍摄的照片。

如果您正在阅读这篇文章,您可能也面临着区分各种数据角色的困难,例如数据科学家、数据分析师、数据工程师、人工智能研究员、ML 工程师和业务分析师等。

这可能是由于员工的职称与其在公司内从事的项目的责任相比。特别是,公司越小,员工的个人资料和角色之间的差异就越大。

因此,本文尽可能准确地总结了一些更“常见”的数据角色。为了做到这一点,本文首先描述了下面显示的数据角色,然后图示了它们之间的主要联系,并总结了可能造成混淆的角色之间的主要差异。

图一。数据角色。 Ref :图片作者。

图二。核心数据科学生命周期&角色。参考:图片由作者提供。

数据角色

  • 数据分析师:通过分析数据和解读结果来回答问题。
  • 业务分析师:通过数据分析,处理、解释和记录业务流程、产品、服务和软件,以帮助形成业务洞察力,做出更有效的业务决策。
  • 数据可视化工程师:设计数据报告解决方案,简化对数据及其洞察力的理解。
  • 数据科学家:专注于通过开发基于人工智能的模型来解决问题,而不是给出问题的答案。
  • 人工智能研究员:探索并提出解决问题的新系统,然后由现实世界场景中的其他角色使用。
  • 数据工程师:构建数据管道,将来自不同源系统的信息汇集在一起。
  • 数据架构师:定义用于收集、组织、存储和访问公司信息的政策、程序、模型和技术[1]。
  • ML工程师:构建和维护人工智能系统,实现预测模型的自动化。
  • MLOps 工程师:为数据科学家和其他角色提供对专业工具和基础设施(例如,存储、分布式计算、GPU 等)的访问。)他们在整个数据生命周期中都需要。他们开发方法来平衡独特的数据科学要求与业务其余部分的要求,以提供与现有流程和 CI/CD 管道的集成[2]。

数据角色图

如果我们根据角色的需求和依赖关系来连接角色,我们最终会得到如图 3 所示的图表。下面是对每个模块的描述,以及要求开发预测模型时每个角色的职责(说明性示例)。

图三。数据角色之间的主要关系。参考号:作者图片。

1。数据准备:

数据架构师规划组织的数据框架,并与实现它的数据工程师进行讨论。然后,数据工程师创建 ETL 管道,生成数据科学家、数据分析师和业务分析师使用的数据。

对于预测任务,这两个角色将负责构建数据框架来收集数据,以及开发 ETP 管道来创建数据集。

2。数据建模:

数据分析师和业务分析师都与数据打交道,主要区别在于他们用数据做什么。业务分析师使用数据来帮助组织做出更有效的业务决策。相比之下,数据分析师更感兴趣的是收集和分析数据,以给出具体问题的答案[4]。

在我们的示例中,所需的角色是数据分析师,他将执行解释性数据分析以获得洞察力。

3。数据分析:

人工智能研究人员研究新的基于人工智能的方法,这些方法通常发表在开放存取的档案中,如 arXiv 或会议记录。数据科学家主要使用这些信息来解决他们的任务。

在我们的例子中,数据科学家将根据当前的文献开发预测模型,这些文献来自人工智能研究人员的研究。

4。部署和监控:

一旦数据科学家建立了一个模型,机器学习工程师就会将该模型运送到生产环境中。此外,数据可视化工程师设计界面来报告解决方案。

对于预测任务,ML 工程师将采用数据科学家开发的模型,对其进行优化,并将其投入生产。ML 工程师不一定需要理解模型是如何工作的。数据可视化工程师将设计向用户显示的界面。

5。基础设施:

MLOps 工程师通过构建和维护一个平台来实现机器学习模型的开发和部署,从而实现其他角色。

在给定的示例中,这些工程师将开发所有专用工具和基础架构(例如,存储、分布式计算、GPU 等。)贯穿数据科学的整个生命周期。

数据角色差异

这最后一节重点提到一些最难区分的角色之间的区别。创建预测模型的任务也将用作比较角色的说明性示例。

数据架构师 vs 数据工程师

数据架构师设计组织数据框架的远景和蓝图,而数据工程师负责创建远景[3]。

对于预测示例,数据架构师将专注于定义策略、过程、模型和技术,并向数据工程师提供关于如何组织数据以及数据应该以何种格式呈现的见解。此后,数据架构师依赖于数据工程师的具体设想来收集数据,将其存储在系统中,并为分析做准备。

业务分析师与数据分析师

虽然数据分析师和业务分析师都与数据打交道,但主要区别在于他们用数据做什么。业务分析师使用数据来帮助组织做出更有效的业务决策。相比之下,数据分析师更感兴趣的是收集和分析数据,以进行评估并用于自己做出决策。[4].

对于给定的示例,数据分析师将负责通过使用统计、数据挖掘和可视化技术对预测问题进行建模、发现见解和识别机会。相比之下,业务分析师将应用广泛的工具、数据源和分析技术来回答广泛的高影响力业务问题,并简洁有效地提出见解。此类业务关键型洞察的示例包括提供新计划/产品和市场发布的估计,以及深入研究最近的事件以了解对联系量的影响。

数据分析师 vs 数据科学家

数据分析师通常与给出问题的答案有关,而数据科学家则专注于通过使用数据的发现见解开发基于人工智能的模型来解决问题。

对于预测示例,数据分析师将通过使用诸如平稳性、相关性、自相关性、多重共线性等方法,专注于发现对数据的洞察。另一方面,基于数据分析师的见解,数据科学家将专注于构建模型,可能会实施 LSTM 神经网络、基于变压器的神经网络或 1D 卷积神经网络等。

数据科学家 vs 人工智能研究员

人工智能研究人员探索新的方法,提出解决问题的新系统,而数据科学家则在现实世界的场景中调整和应用这些系统[5]。

对于预测任务,两种角色之间存在明显的差距,因为人工智能研究人员不会执行这种类型的任务,因为他们的工作是处理公开可用的数据集,以便他们可以将他们提出的方法与最先进的方法进行比较。这项任务将交给数据科学家,他们将使用 SOTA 模型找到最佳解决方案。

数据科学家 vs ML 工程师

数据科学家处理算法的建模方面,而 ML 工程师专注于同一模型的部署。数据科学家专注于算法的细节,而机器学习工程师则致力于将模型投入生产环境[6]。

对于预测任务,数据科学家专注于建立预测模型,试图尽可能减少误差。一旦完成,ML 工程师负责将这个模型运送到生产中,重新训练它,并维护它。

ML 工程师 vs ML 开发人员

ML 工程师专注于实现和再培训机器学习模型,而 MLOps 工程师通过构建和维护一个平台来实现机器学习模型的开发和部署,从而实现 ML 工程师[7]。

对于给定的示例,ML 工程师的角色是将预测模型运送到由 ML DevOps 维护的基础设施中的生产中,ML devo PS 可能被要求安装例如特定的库或 CUDA 版本。

如果你喜欢这个帖子,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!

参考

最佳参考:中等,数据科学领域角色

[1]https://www.techtarget.com/whatis/definitions/D

https://blog.dominodatalab.com/7-roles-in-mlops

[3] Striim,数据架构师 vs 数据工程师

【4】东北大学,数据分析师 vs 商业分析师:有什么区别?

[5]实习生 Khoj,艾研究

[6]中等,数据科学家 vs 机器学习工程师技能。区别在这里

【7】海王星博客,m lops 工程师是个东西吗?

全球可变可再生能源部署的学习曲线效应

原文:https://towardsdatascience.com/learning-curve-effect-on-the-global-variable-renewable-energy-deployment-73d1e28da390

基于学习曲线效应,可再生能源技术进入良性循环

如果你每天提高 1%,到年底你将会提高 37 倍。

当我第一次读到上面这句话的时候,我被它惊呆了。的确,学习是一个缓慢但稳定的过程。当一个人踏上学习新技能的旅程时,这个人在开始时经历相对缓慢的成长是正常的。一旦一个人很好地掌握了基础知识,他的成长会非常快。如果这个人继续努力,从长远来看,他们可以掌握这项技能。这是上面引用的要点,在下面的图中描述得很清楚。

说明通过学习获得的知识。图片作者。

学习曲线

事实上,获得某一领域的知识或熟练程度并不是一个顺利的过程。在前进的道路上有几个挑战。从整体角度来看,类似于学习旅程的曲线被称为学习曲线。它是一个领域在一段时间内所获得的熟练程度或专业知识的图形表示。

学习曲线的形状可以根据环境而变化。它可以是线性的、s 曲线的,甚至是指数的。s 曲线(sigmoid 函数)是学习曲线的一般形式,从起点开始慢慢积累小步骤。接下来是更大的步骤,然后随着学习进入成熟阶段,又是更小的步骤。

学习曲线的图示。图片作者。

经验曲线

学习曲线的概念也适用于经济学。市场中的每个产品在其生命周期中都要经历四个主要阶段:

  • 简介:产品投放市场后,
  • 增长:当销量开始增长时,
  • 成熟期:当产品销售达到顶峰时,
  • 衰落:产品变老,一种新兴技术取而代之。

产品在其生命周期中销量或产量的增长类似于学习过程中的知识增长。当产品产量增加时,与之相关的经验也会增加,从而随着时间的推移提高效率。因此,累积产量的增加导致单位产品成本的下降。这种效应被称为学习曲线效应经验曲线效应

术语“学习曲线”和“经验曲线”经常根据上下文互换使用。

学习过程和经验曲线效应的图解。图片作者。

可变可再生能源技术部署的学习曲线效应案例研究

可再生能源技术的应用,特别是可变可再生能源(VRE)在过去十年里显著增长。风能和太阳能等能源技术被称为 VRE,因为它们具有间歇性和不确定性等特性。在以下部分,我将讨论从 2010 年到 2020 年 VRE 部署的趋势、驱动因素以及学习曲线的影响。

全球可再生能源趋势

根据国际可再生能源机构的数据,可再生能源技术的总装机容量从 2010 年的约 1,300 千兆瓦增加到 2020 年的近 2,900 千兆瓦,增加了一倍多(国际可再生能源机构,2021 年 a)。虽然水电和生物质能等可再生能源技术在过去十年中略有增长,但 VRE 技术的安装在同一时期呈指数增长。

2010-2020 年世界可再生能源技术总装机容量。图片作者。

可变可再生能源的全球趋势

在过去的十年里,在全球范围内部署最多的技术是太阳能光伏发电。太阳能光伏装机容量从 2010 年的 40 GW 增长到 2020 年的 710 GW,增长了近 18 倍。其次是陆上风能技术,其装机容量增长了近四倍,从 2010 年的 178 吉瓦增长到 2020 年的近 700 吉瓦。虽然海上风力发电和聚光太阳能发电技术的装机容量在此期间也有所增长,但这些技术的总装机容量仍然相对较小。总体而言,VRE 的装机容量从 2010 年的 222 GW 增长到 2020 年的 1448 GW,增长了近六倍半,如下图所示。

2010 年和 2020 年 VRE 全球装机容量对比。图片作者。

所用数据帧的名称是abc,如下图所示。用于获得上图的数据框架可在下面的要点中找到。

包含 2010 年和 2020 年 VRE 技术总装机容量的数据框架。图片作者。

VRE 技术成本下降

减少能源组合中的碳排放以缓解气候变化的需求无疑是过去十年中 VRE 技术应用增加的主要驱动力之一。然而,市场总是由产品成本驱动的。每当市场上有多种选择可以满足我们的目标,我们倾向于选择最具成本效益的一个

VRE 技术公司的资本成本在过去十年里稳步下降,而且今天还在继续下降(IRENA,2021b)。2010 年至 2020 年间,太阳能光伏的资本成本下降了 5 倍多,从 4731 美元/千瓦降至 883 美元/千瓦。在同一时期,陆上风电的资本成本从 1971 美元/千瓦下降到 1349 美元/千瓦。

2010 年至 2020 年安装 VRE 技术的资本成本。图片作者。

随着这些技术的资本成本直线下降,这些技术的发电成本(称为平准化电力成本(LCOE) )也逐年大幅下降。因此,近年来,VRE 技术与化石燃料技术相比越来越具有成本竞争力。根据国际可再生能源机构的数据,2010 年至 2020 年间,太阳能光伏、陆上风能、海上风能和光热发电技术新增产能的全球加权平均 LCOE 分别下降了 85%、56%、48%和 68%(国际可再生能源机构,2021 年 b)。

2010 年至 2020 年 VRE 技术公司的全球加权平均公用事业规模 LCOE。图片作者。

与非可再生能源相比,可再生能源技术的资本成本和 LCOE 的快速下降可归因于这样一个事实,即可再生能源技术遵循一条学习曲线,而非可再生能源则不然。这是因为,虽然化石燃料和核电的 LCOE 很大程度上取决于燃料价格和运行成本,但可再生能源技术没有燃料成本,运行成本相对较低,因此,可再生能源技术的 LCOE 很大程度上取决于技术本身的成本。

学习曲线的原理是,

累计装机容量每增加一倍,成本就会以一定的平均百分比下降,这个百分比称为学习率。

这种关系显示在下面的数学等式中:

基于学习参数,显示开始和结束时间的资本成本和累计装机容量之间关系的等式。图片作者。

学习率通过以下公式计算:

显示学习率和进步率之间关系的方程。图片作者。

基于学习曲线效应,可再生能源技术进入良性循环。例如,太阳能光伏发电的部署增加,以满足一个地区不断增长的电力需求。随着部署的增加,由于大规模生产,即规模经济、技术改进、效率提高和供应链中的竞争,安装的单位成本直线下降。因此,太阳能光伏在市场上变得更具成本竞争力,这反过来又进一步增加了需求。

随着累计安装量的增加,可再生能源技术变得更加便宜,并进入良性循环。图片作者。

根据 VRE 技术公司的资本成本和累计投资数据,2010 年至 2020 年间,太阳能光伏发电的学习率为 33%,而陆上风能的学习率为 18%。这意味着,装机容量每翻一番,太阳能光伏发电的单位资本成本平均下降 33%,陆上风能平均下降 18%。

2010 年至 2020 年太阳能光伏和陆上风能的资本成本和累计装机容量。图片作者。

在所有 VRE 技术中,太阳能光伏发电的学习率最高(33%),其次是 CSP (25%)、陆上风能(17%)和海上风能(10%)。当两个变量都以对数标度绘制时,从线的陡度可以明显看出这一点。

如果 2010 年至 2020 年太阳能光伏的学习率仅为 10%,资本成本将从 2010 年的 4731 美元/千瓦降至 2020 年的 3055 美元/千瓦。如果学习率为 40%,资本成本将在 2020 年进一步下降到 567 美元/千瓦。这在下面的图中得到了证明。

2010 年至 2020 年不同学习率下太阳能光伏的资本成本预测。图片作者。

结论

世界传统电力市场由化石燃料技术主导。今天,可再生能源技术,特别是 VRE,在世界上大多数国家都比化石燃料便宜。在过去十年中,太阳能和风能发电的大规模部署导致了电力系统和电力市场的范式转变。VRE 和其他可再生能源技术的部署是如何改变择优次序和发电边际成本的,在这个故事中有所呈现。

在这篇文章中,我从全球的角度讨论了可再生能源和 VRE 的趋势。我进一步讨论了学习曲线的概念,为什么可再生能源技术遵循学习曲线,以及该曲线如何引导技术进入成本下降和部署增加的良性循环。本文中用于分析的笔记本和数据可以在这个 GitHub 资源库中获得。感谢您的阅读!

参考

国际原子能机构,2021 年 a。可再生能源的趋势。【irena.org】统计时间序列

IRENA,2021b。2020 年可再生能源发电成本(irena.org)

OWID,2020。为什么可再生能源如此迅速地变得如此便宜?—我们的数据世界

从真实项目中学习数据科学

原文:https://towardsdatascience.com/learning-data-science-from-real-world-projects-b2a174bf1db2

有一个理论的时代——崇高的概念、复杂的方程和仔细的思考——也有一个卷起我们众所周知的袖子,建造一些可能有用也可能没用的东西的时代。

这些天我们都在修修补补,所以这一期的《变量》集中了我们最近发表的一些最令人难忘的实践项目和案例研究。准备好开始了吗?

  • 医疗 AI 变得非常非常真实 。医学中的 ML 应用带来了巨大的希望,但也带来了巨大的挑战。 Harmke Alkemade 和 Andreas Kopp 研究了医学成像的几个用例,并展示了 Azure 机器学习如何支持关键的诊断工作流,从脑瘤分类到肺炎检测。
  • 计算机视觉能否帮助城市优化街道清扫?Rodrigo Fuentes 和他五年级的女儿开始的一个休闲、有趣的项目变成了一个有希望的概念证明。两人合作开发了一种工具,可以检测和地理定位街道垃圾,并使城市官员更容易决定哪些地区需要更多的卫生资源。

泰勒·希瑞在 Unsplash 上的照片

我们希望我们本周的选择能激励你利用你的知识,创造新的东西。感谢您花时间和我们在一起,感谢您一直以来对我们作者工作的支持。

直到下一个变量,
TDS 编辑

学习 Docker,简单的方法

原文:https://towardsdatascience.com/learning-docker-the-easy-way-52b7bdec5e86

一个教程,涵盖了 Docker 的基础知识,并教你如何容器化你的应用程序

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

Docker 已经接管了软件行业,成为事实上的标准。我想我们大多数人都同意这一点。

Docker 不仅让开发人员的生活变得更加轻松,也让运营人员和普通用户的生活变得更加轻松。几年前,安装一个软件可能会很麻烦,安装和设置数据库等东西需要几个小时。今天,它就像在 Docker 桌面上点击开始或使用 Docker CLI 运行一行命令一样简单。

这篇文章是关于如何使用 Docker 和学习入门基础的教程。

教程的 YouTube 视频

什么是 Docker

Docker 用于构建可发布的映像,这些映像可以在任何安装了 docker 的系统上发布和执行。这可以通过容器化软件来实现,容器化软件是一套拥有运行所需一切的软件。

任何拥有这种容器化软件的人都可以运行它。想象一下,你在一台 Ubuntu 机器上安装 PostgreSQL 并配置它工作,这需要时间,但有了 docker,你可以将安装和操作系统容器化,下次你需要它时,只需启动它。

Docker 映像不是虚拟机,而是软件的自包含单元,该自包含单元包含它需要运行的一切。当我说一切时,我指的是一切,操作系统、依赖项、网络、配置。

如果你正在寻找关于它如何工作的更深入的解释,我推荐阅读官方的 docker docs 以了解细节。

需要我说 docker 在 Go 中编码吗?

安装 Docker

通过访问他们的下载网站下载并安装 docker 引擎。Docker-desktop 现在可用于所有平台,可以作为学习和维护 Docker 引擎的一个非常好的途径。

https://docs.docker.com/engine/install/

如果您使用的是 Linux,不要忘记运行

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg 

这将下载 docker 加密密钥,并将其添加为可信来源。

如果您使用的是 Linux,您还需要将您的用户添加到 docker 组中,这样您就可以在没有 sudo 的情况下运行 docker。

sudo groupadd docker
sudo usermod -aG docker $USER

图像和容器

图像充当一个模板,一旦运行就变成一个容器

所有 docker 用法都以Image开头。您的映像是包含运行操作系统和其他依赖项所需的一切的盒子。把它想象成一个盒子,里面装满了启动这个盒子所需的所有文件。

Docker 容器映像是一个轻量级的、独立的、可执行的软件包,包括运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。

在 docker 中,我们经常会谈到两种类型的图像

  • 基础图像 —每个图像的开始,称为scratch的图像,这是一个没有任何内容的图像,大小为 0 字节。
  • 父映像 —创建映像时,我们定义一个父映像作为起点,父映像中的所有内容都将出现在新映像中。

图像是用docker build命令在 docker 中构建的。构建映像时,您需要指定一个tag,它通常与版本、使用的系统架构或父映像相关。我们还需要给它一个名字,名字和标签,用:分开。

一个示例构建命令如下所示

docker build -t myImage:1.0 .

最后,这个点不是一个错别字,构建图像时给出的最后一个参数是上下文。稍后将详细介绍上下文。

您可以使用 Docker CLI 工具查看您有哪些图像。

docker images

docker 图像的输出

如您所见,您将看到位于您机器上的所有当前图像。您将看到图像来自的repository,而tag是版本标签。Image id是一个自动生成的 ID,可以用来指代某个图像。它的创建日期和整个图像的总大小也会显示出来。

docker 使用该映像将其运行到一个container中。每当我们使用docker run启动一个容器时,它就会使用image并将它转换成一个容器。容器是一个开始的图像。

把映像想象成蓝图,它有运行所需的软件,当你启动一个映像时,它会根据这个蓝图构建一个容器。

或者如果你熟悉 OOP,图像可以被认为是一个类,而容器是一个对象。

这意味着您可以从同一个映像运行许多容器。理解从同一个映像运行的两个容器是完全独立的是很重要的。如果我们有一个称为基础的映像,并且我们从基础启动两个容器,我们在容器 A 中所做的更改不会反映在容器 b 中。在容器中所做的任何更改也不会反映在映像中。

使用docker run命令启动容器。Run 获取图像并将其转换为容器。

请记住,容器是图像的实例,但是容器中的任何更改都不会保存到图像中。

然而,Docker 的一个优点是,如果我们从一个干净的 Ubuntu 映像启动一个容器,并添加我们想要的软件和文件,我们可以使用docker commit将该容器保存为映像。

简单地说,我们有用于构建图像的docker build,我们有从图像创建容器的docker run,以及从容器创建图像的docker commit

这使我们能够创建高度定制的映像,满足成功运行软件的所有需求。

DockerHub 是一个图片存储库,任何组织或开发者都可以在这里发布他们的图片。Docker 会自动使用 dockerhub 来查找任何你试图使用,但本地没有的图片。

运行和管理容器

使用 docker CLI 管理容器

让我们运行一个容器来学习它。

使用docker run命令运行 docker。如果您执行它,您会看到它打印出一个非常易于使用的语法。

docker run [OPTIONS] IMAGENAME:TAG [COMMAND] [ARGS]

其中OPTIONS是行为标志,改变如何运行容器。Imagename 和 Tag 只是要使用的名称和标记。

COMMAND是我们希望在容器启动后传递给它的任何命令,后面跟有任何ARGS

让我们运行一个 Ubuntu 容器,让它打印出当前工作目录中的所有文件。如果你没有安装 Ubuntu 容器也不用担心,当试图运行一个不存在的容器时,docker 会自动查看 DockerHub 并为你下载。

docker run ubuntu ls -al

在命令中我没有指定使用什么标签,它会自动使用特殊标签latest,这是最近上传的同名图片。

如果您运行这个命令两次,您会看到第一次它下载图像需要几秒钟,第二次它会立即运行。

现在,我们已经创建了两个容器来打印目录内容,我们可以使用ps命令找到容器。我们将添加a标志来列出所有容器,否则,只列出正在运行的容器。

docker container ps -a

你应该看看那里的集装箱。你现在可以使用他们的 ID 删除他们。

docker container rm $ID

使用容器 ID 很麻烦,而且很长,我通常用--name来运行我的容器,它允许你为你的容器创建一个自定义名称。该名称可用于在其他命令中引用 docker,例如如果您想要删除它。

docker run --name test ubuntu ls
docker container rm test

在处理运行中的容器时,还有一些其他标志可以派上用场。

--rm将确保在流程结束后移除容器

-it用交互式外壳运行。

请记住,如果你运行没有--rmdocker run,你不会删除容器,这将占用你的硬盘空间。

尝试打开一个 ubuntu shell,然后退出并重新列出你的容器,它应该已经被删除了。当调试为什么软件不能如预期的那样工作时,使用交互式 shell 是非常有用的。

docker run --rm -it ubuntu

当然,我们可以让 docker 容器运行很长时间,我们可以使用-d标志让容器在一个分离的进程中运行。当你有一个运行你的软件的 docker 时,这是很常见的。

您可以很容易地尝试所有这些,只需执行以下命令。在这个例子中,我们希望在 bash 中运行不止一个内部命令,所以我们没有简单地将 sleep 直接传递给 docker,而是使用bash -c插入多个命令来顺序运行。每个命令由;分隔。

docker run --name myUbuntuContainer -d ubuntu bash -c "sliep 5; echo 'Hello World'; sleep 60"

现在你应该明白这个命令是做什么的了,但是为了好玩,它会从 ubuntu 映像创建一个名为myUbuntuContainer的容器,在后台运行(分离-d),然后依次执行-c中的命令。注意,我没有使用--rm,所以这个容器不会在执行完成后自行移除。

您可以通过运行docker ps -a列出所有现有的容器。

使用 ps 显示正在运行的 docker 容器

输出向您显示用于引用容器的容器 ID,例如,如果您想要删除容器,但是您也可以使用名称。ps还显示了哪些端口从容器暴露给主机,目前没有。

如果您想查看分离的容器中发生了什么,您可以通过运行。当您想要排除故障或了解容器不工作的原因时,这很有用。

docker logs $CONTAINERID/NAME

docker 日志向我们显示了一个错误

请注意,我们记录了一个错误,在第一个sleep命令中有一个打字错误。让我们移除这个容器,这样它就不会占用额外的空间,是的,留下一个容器会占用和图片一样多的空间。

我们可以使用docker container rm后跟容器 ID 或名称来删除容器。注意如果你的容器还在运行,你必须通过-f标志来强制移除它。

docker container rm myUbuntuContainer

运行 docker 的一个重要方面是限制某个容器允许使用的资源数量,比如内存和 CPU。您可以使用以下标志添加这些资源。

--memory —使用它来设置允许的最大内存量,等等--memory=6m (6 兆字节是一个容器的最小值)

--cpus —用于限制容器允许使用多少 CPU 资源等--cpus=1

您可以使用inspect来验证容器的任何配置。尝试启动一个容器并设置内存限制,然后检查它

docker inspect ID/Name

容器中的网络

Docker 中的联网和连接容器

默认情况下,您的容器在私有网络中运行。您可以让容器在同一个网络中运行,或者将它们隔离开来。

最常见的事情之一是从容器中公开端口。

您可以轻松地将publish标志添加到docker run命令中。简写标志为-p,接受主机端口和容器端口以:分隔链接。

假设您在容器的端口8080上托管了一个 API,但是想要在运行端口8099上的容器的主机上公开该 API。

您可以通过运行以下命令来实现这一点

docker run -p 8099:8080 --name myUbuntuContainer -d ubuntu bash -c "sleep 5; echo 'Hello World'; sleep 60"

您可以使用docker container psdocker port $CONTAINERID查看暴露的端口。

docker port 显示暴露的端口

注意 docker 将默认为 TCP,您可以通过在端口后添加/tcp/udp来指定协议。

另一件很酷的事情是,你可以让 docker 动态地为你分配主机端口。如果您将主机部分排除在-p标志之外,它将找到任何可用的端口并使用该端口。

尝试一下,只指定-p 8080看看它为你选择了什么端口。

Docker 还允许我们创建虚拟网络,以帮助我们拥有能够连接的容器。

您可以使用docker network ls查看所有当前的虚拟网络。你会看到至少三个网络。

如果你不想隔离,可以使用host网络,它将运行与运行容器的主机相同的网络。如果您完全不想要网络,则使用none

默认存在的 Docker 网络

您可以使用以下命令创建新的虚拟网络

docker network create skynet

一旦你创建了它,当运行容器时,我们可以使用--net标志告诉容器在这个网络中运行。

为了测试这一点,我们将打开两个容器,并将它们分配给skynet,看看我们是否能相互 ping 通。然而,我们需要将ping安装到 ubuntu 容器中,并且你不应该在容器中这样做。
我们只是安装 ping 软件来展示网络。我们将在本文后面的构建容器时研究如何做到这一点。

在构建映像时,始终安装依赖项

让我们从启动第一个容器并准备它开始,我们将这个第一个容器命名为sky-client

docker run --rm -it --net skynet --name sky-client ubuntu
root@134f1245a32:/# apt-get update; apt-get install iputils-ping

安装 ping 后,尝试对sky-server执行 ping。您应该会看到一条错误消息,指出这在 DNS 中不存在。

找不到天空服务器

我们可以解决这个问题,打开第二个终端,启动第二个名为sky-server的容器。注意我们如何使用容器名在网络上定位它们。

docker run --rm -it --net skynet --name sky-server ubuntu

一旦 sky-server 开始运行,再次尝试从sky-clientping 它。

使用容器名称 using 同一网络上正在运行的容器

太好了,我们现在有两个容器运行在同一个虚拟网络上。需要记住的一点是,在同一个容器中可以有多个网络。

有时您希望在主机和容器之间共享数据。例如,当您有一个在容器中运行的数据库,并且希望该数据库在重新启动之间保持不变时,通常就是这种情况。

您可以使用-v标志在容器和主机之间添加卷。同样,与网络端口一样,您可以指定主机上的路径,然后指定容器上的路径来装载。路径用:划分,大部分时间用 docker。

让我们试着打开一个容器,与主机共享一个文件夹。请记住始终使用主机上的完整路径。

docker run --rm -ti -v /home/percy/sharedfolder:/shared --name sharing-is-caring ubuntu

该命令将在我的主机上的/home/percy/sharedfolder处创建一个文件夹,该文件夹连接到容器上的/shared文件夹。

尝试在该文件夹中创建一两个文件,看看能否在您的主机上查看它们。

您还可以使用--volumes-from标志在容器之间安装文件夹。如果您有需要跨多个容器共享的数据,这将非常有用。

# Start container with mount
docker run --rm -ti --net skynet --name sky-client -v $PWD/data:/shared ubuntu
# Start the second container with same mounts
docker run --rm -ti --net skynet --name sky-server --volumes-from sky-client ubuntu

注册

注册表用于管理和分发图像。有一个默认使用的官方 docker 注册表,但是你也可以设置你的公司注册表。

您可以使用docker search在注册表中搜索图像。如果你对某个软件感兴趣,你可以随时搜索是否有它的图片。假设您想运行一个 PostgreSQL 容器。

docker 搜索显示许多图像

请注意,您会发现许多图像,因为任何人都可以在官方注册表上上传图像。在决定使用哪张图片时,你应该看看官方标签和星星的数量。

由于任何人都可以创建并上传图像,因此在选择图像时可能会有风险。

如果你想要一个 UI,你可以通过访问 Docker Hub 来查看官方注册表。在 Hub 中,您通常可以找到关于如何使用该映像的自述文件。

您可以将注册表视为代码存储库。

构建定制的图像

自定义图片有两种方式,第一种是使用docker commit,将容器保存为新图片。第二种方法是使用 Dockerfiles,我们很快就会了解更多。

让我们首先看看如何使用 Docker commit 来轻松地保存一个包含从容器中进行的任何更改的图像。还记得之前我们在容器中下载 Ping 吗?这不能保存,也不是正确的方法,但是如果我们测试了它,并且知道这个容器是正确的,我们想把它保存为一个图像,我们可以提交这个容器。

打开一个新的 Ubuntu 容器,再次安装 Iputils-ping。

docker run -it --name test ubuntu
apt-get update -y; apt-get -y install iputils-ping

完成后,退出 shell 并打开主机上的终端。

Docker commit 接受几个选项,我喜欢添加--message,这是一个更改的提交消息。我也喜欢添加--author,这样其他开发人员就知道该联系谁了。

语法是

docker commit [OPTIONS] [ContainerName/ID] [repository/name:tag]

因此,让我们提交包含我们需要的软件的容器。

docker commit --message "added iputils" --author programmingpercy test programmingpercy/ubuntu:1.0

现在,您应该能够从这个映像开始运行一个新的容器了。

docker run -it --name test2 programmingpercy/ubuntu:1.0

到目前为止,我们已经使用了预定义的图像,但还没有触及 Docker 最漂亮的部分。我们可以使用Dockerfiles创建定制的图像。Dockerfile 是一个包含脚本语言的文件,我们可以用它来创建和制作我们的图像。

Docker 文件应该被命名为dockerfile,这允许 docker 在你执行目录中的 docker 时自动检测文件。

docker 文件非常容易阅读和理解,它们从上到下连续运行。有一点需要记住,docker 文件中的每一行都是对docker rundocker commit的执行。这被称为layer,有时你可以将动作组合成一行以节省空间。

我们如何创建一个包含预装 FFmpeg 的自定义 ubuntu 映像的例子如下所示。

FFMPEG 是一个用于修改和处理视频和音频流的工具

Dockerfile —用于运行 FFMPEG 的定制 he

语法非常简单,我们现在只讨论基本内容。

在第 1 行您可以看到FROM,它表示我们的构建过程将从其开始的父节点image。因此,在这种情况下,它将使用ubuntu图像作为启动容器,将 ubuntu 作为一个容器运行,应用并提交更改。如果你想要一个特定的版本,你可以在分号后添加标签,所以如果你想要 ubuntu 20.10 等等,添加标签FROM ubuntu:20.10

下一行使用RUN命令,这将用当前层启动一个容器并执行该命令。所以在第 3 行,我们将启动一个基于ubuntu的容器,并更新 apt-get 存储库。这将被提交到一个新的临时映像中。

这个新的临时图像将用于第 4 行,下载 FFmpeg 并将其添加到容器中,然后将容器保存为图像。

现在你可能开始想知道为什么它重建图像的每一步作为一个层,这是超级有效的,没有什么可担心的。如果层在以后没有以任何方式被引用,它将在使用后被删除。主要的好处是 docker 将缓存这些层,并在构建中重用它们。所以,如果你修改了某个东西,你不必等待所有的东西都重新构建。

在第 5 行中,您可以看到 docker 的一个很酷的特性。CMD允许我们在映像中指定容器运行时将执行的可执行文件。这允许我们说,当有人运行这个图像时,执行FFmpeg。所以,我们刚才做的就是把 FFmpeg 打包成 docker 镜像,可以在任何支持 docker、windows、mac 等的电脑上运行。

在你尝试之前,我们需要建立图像。我们只在 dockerfile 文件中定义了如何构建它。

为了构建图像,我们使用docker build命令。我们将传入一个标记标志-t,为我们的图像指定一个名称。我们还会用一个.来结束命令,这个点会让很多人感到困惑,所以试着跟着做。

docker build 命令的最后一个参数是构建上下文。它是 docker 在构建映像时应该使用的位置的路径。一个.命令告诉 docker 使用当前目录,这是最常见的方法,但是理论上您可以通过指定路径在另一个位置构建这个映像。为什么,你可能会想,因为有时你想让本地文件从你的计算机出现在图像中,我们还没有谈到这一点,但你可以包括文件从主机到 docker 图像。

docker build -t demo/ffmpeg .

运行构建命令并等待它完成,这可能需要一些时间,这也是我选择 FFmpeg 的原因。不是让你厌烦,而是向你展示缓存机制。

在构建期间,您将看到所有构建的层,并在最终图像完成之前移除这些层。第一次构建需要一些时间,但是我建议您在之后尝试运行相同的命令。您应该看到图层被缓存,构建时间是真实的,即时的。

你现在可以用docker run命令运行你的令人惊叹的 FFmpeg 图像,在任何你想要的地方执行 FFmpeg。

docker run demo/ffmpeg

docker 文件还有许多其他的 docker 语句。
一个很常见的是ADD语句。这允许您在主机(构建映像的主机)上指定要复制到映像中的文件夹或文件。

语法非常简单,您指定主机上的路径,然后是映像上的路径。

ÀDD mycomputer/myfile.txt /path/in/image

ADD也接受 URL,所以你可以告诉 docker 从一个 URL 获取一个文件并把它放在图像中。

另一个重要的声明是ENV,它是环境变量的缩写

ENV DATABASE_USER = "my user"

现在,我们的图像中有一个小缺陷。我们使用了CMD语句来触发 FFmpeg,它可以工作,但是它不允许我们向 FFmpeg 中添加更多我们需要的参数。

是时候阐明CMD语句和ENTRYPOINT语句之间的区别了。

CMD用于指定一个完整的命令在容器运行时使用。ENTRYPOINT相反指定一个命令运行的开始。

因此,为了允许用户使用我们的 FFmpeg 图像,但是修改 FFmpeg 参数,我们需要将CMD改为ENTRYPOINT。请尝试执行更改,然后重新运行容器。现在,您还可以在 run 命令中添加某些 FFmpeg 参数。

Dockerfile —使用入口点允许 FFmpeg 的子参数

重建图像现在应该允许用户添加输入文件等和更多的docker run demo/ffmpeg -inputfile等。

让我们尝试添加文件,我们将使用前面提到的ADD命令来获取演示视频。我们将从一个网址获取演示视频,所以我们可以尝试转换视频。我们将把文件存储在/videos图像里面。

我建议不要在图像中存储视频,这只是为了展示它是如何工作的。

在下面的要点中,我还修改了所有的运行命令到一个层中,这是一个常用的技术,通过将相关的层分组到相同的层中来节省一些空间。&&是 bash for AND,\只是一个不破坏命令的新行。

Dockerfile —我们将 URL 中的文件添加到/videos 中

接下来,我们将重建图像,然后执行 FFmpeg 来加速视频,然后输出到一个挂载的文件夹中。

使用 mount 运行容器,并在演示文件中使用 FFmpeg。

执行该命令后,您应该能够在您的主机上查看视频的加速版本。

这里列出了 Dockerfiles 中最常用的命令。

  • 运行 —用于在终端执行命令。对于 Linux,它是 Bash,对于 windows,它是 cmd。
  • ENV —用于在容器内部分配环境变量。
  • 标签 —用于向容器添加元数据。来自父图像的标签被继承。
  • 复制 —将文件从主机复制到容器。只能从主机复制文件。
  • 添加——非常类似于 COPY,它也将文件复制到我们的容器中。不同之处在于,ADD 还可以通过 URL 使用远程文件。它还可以将本地焦油提取到容器中。
  • ARG —可以由构建映像的人在构建时设置的变量。在构建过程中,可以在 docker 文件中使用 ARGS,但是在构建映像之后,除非重新构建映像,否则不能使用或更改它们。
  • —在主机和容器之间添加一个挂载点。语法是卷路径。这通常在容器重启之间持久化数据时使用,例如数据库。实际数据存储在主机上,但在容器中使用。
  • EXPOSE —告诉 Docker 引擎容器正在监听特定的端口和协议。格式是端口/协议(UDP/TCP)
  • CMD —定义 Docker 容器在启动时应该运行的命令和参数。这通常指向带有一组缺省值的二进制文件。当允许用户更改命令时,应该使用 CMD。
  • 入口点 —通常在容器应该作为可执行文件运行时使用。它的功能与 CMD 相同,但是如果使用 ENTRYPOINT,用户不允许更改该命令。
  • WORKDIR —设置命令运行的路径。所以如果设置为/app,就像运行 cd /app 一样。

多阶段构建

在 docker 文件中,您可以做更多的事情。有一件事需要你自己去研究,那就是如何使用多重构建来大量减少 docker 图像的大小。使用多阶段构建,您可以将图像的大小减少 90%以上。我见过大小在 1GB 左右的图像变成几 MB。

多阶段允许我们使用映像来构建我们的软件,然后将它们作为最终产品移动到一个更加精简的映像中。

我通常用它在 Ubuntu 映像上构建我的 Go 软件,然后将它转移到一个只包含我的单个二进制文件的暂存容器中。

通过在 Dockerfile 文件中添加多个FROM语句,多阶段构建是可能的,我们可以使用AS为每个FROM指定一个特定的名称。这是很常见的看到建立阶段命名为builder等。

FROM ubuntu AS builder

通过命名一个构建阶段,我们可以稍后使用带有--from参数的COPY来引用该构建阶段的文件系统。

dockerfile —我们本质上要做的是在一个成熟的操作系统中构建,然后将构建好的二进制文件转移到一个精简的操作系统中。

为了查看多阶段构建和正常构建之间的差异,我们将构建两个图像并进行比较。首先,我们需要构建一些东西,并更新 dockerfile 文件来构建二进制文件。

这是一个简单的 Go 程序,它只是打印一条消息,如果你没有安装 Go,不要担心,我们将在 Docker 镜像中构建它,记得吗?

将下面的要点复制到与 dockerfile 目录相同的名为main.go的文件中。

main.go —打印消息的简单程序

现在让我们更新dockerfile,首先将main.go从主机复制到镜像中,然后构建二进制文件。

然后,我们将把这个二进制文件复制到一个暂存映像中。

dockerfile —在运行映像上构建二进制文件,然后进入临时映像

通过执行常规的 Build 命令来构建映像。

docker build -t multistaged .

执行docker images查看最终图像,我的最后是 1.95 MB 大小,超级小。这是因为映像中唯一的东西是我们的二进制文件,没有其他东西会使映像膨胀。

您甚至可以尝试执行图像来验证它是否工作。

docker run multistaged

那么,为什么我们要经历这一切,而不是一个单一的构建,让我们来比较图像。

您可以使用 docker build 的--target选项在某个构建步骤停止多阶段构建。这将允许我们运行第一个构建阶段并从中创建一个映像,我们传递我们的阶段的名称,builder

docker build --target builder -t singlestaged .

完成后,您可以运行docker images来比较大小。

太棒了,对吧?

多阶段与单阶段构建

编排容器

现在很容易想象,当我们开始使用 docker 时,它进展很快,我们可以很容易地建立一个数据库、Redis 等。如果您构建一个依赖于这些容器的系统,我们需要编排它们。

编排系统用于在容器出现故障时启动和重启容器,管理资源,并允许容器发现彼此。

有许多编排服务,最容易上手的是docker-compose,它通常用于帮助开发人员以一种非常简单的方式运行多个容器。Docker-compose 经常被用在开发阶段,使得在本地测试和运行一切变得容易。docker-compose 不适合更广泛部署的原因是它仅限于在单台计算机上运行。

对于生产环境,使用 Kubernetes 更为常见。Kubernetes 在本地硬件上使用,也很容易部署到使用托管服务的云提供商。

推荐学习 Kubernetes,如果你想入门,我有一篇文章你可以看看。

结论

在本教程中,您已经学习了图像和容器,这是 Docker 的两个关键组件。

我们已经学习了如何创建图像,以及如何使用 Dockerfiles 编写图像规范。

我们已经了解了如何在容器之间配置网络。

我们学习了如何在主机和容器之间以及容器之间装载卷。

我希望您已经掌握了足够的基础知识,能够独立使用 Docker。

在我的项目中,我几乎总是保存一个打包软件的 dockerfile 文件。我还建议添加一个 docker-compose 文件,它将启动任何相关的所需服务。例如,如果您开发一个使用数据库的 API,拥有一个启动 API 和数据库的 compose 是非常有用的。它帮助其他开发人员快速启动并运行,并减少安装数据库或保持开发实例运行的需要。

作为一名开发人员,Docker 确实改变了我的生活,让我的生活变得更加轻松。

我希望你喜欢这个教程,我很乐意听到你对这个教程的想法、反馈或意见。

通过在《朱丽亚》中创造美丽的海伯恩情节来学习牛虻

原文:https://towardsdatascience.com/learning-gadfly-by-creating-beautiful-seaborn-plots-in-julia-755821156c86

为 Julia 介绍了一个通用的数据可视化库,特别是为那些在 Julia 中编码时错过了 Seaborn 的 python 爱好者

安妮·尼加德Unsplash 上拍摄

我在 Python 中经常使用的一个库是 Seaborn。在 Julia 中,我最喜欢的数据可视化软件包之一是牛虻。它很大程度上是基于哈德利·威克姆的《R 的 ggplot2》和利兰·威尔金森的《图形的语法》一书。

在《牛虻》的介绍中,我们将创造 6 个美丽的海波恩情节。在每一个情节中,牛虻的新的可能性都会被用到。

我们将创建以下数据可视化:

在跟随之后,你将足够了解牛虻,从而在你自己的数据上创建伟大的数据可视化。让我们开始吧。

包装

首先让我们加载所有需要的 Julia 包。如果你需要帮助建立一个 Julia 包环境,你可能会有兴趣先阅读这个故事。

using CSV
using DataFrames
using Gadfly
using Compose
using ColorSchemes

具有不同点大小和色调的散点图

在该图中,我们将使用Geom.point,设置一个Theme,手动指定颜色,并设置点尺寸图例的最小和最大值。我们正试图尽可能地再现这一情节。

首先,我们需要下载数据并将其加载到数据帧中。

download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/mpg.csv", "mpg.csv")
mpg = DataFrame(CSV.File("mpg.csv"))

在绘图之前,我们首先设置绘图大小。这可以在inchcm完成。在plot函数中使用的第一个参数用于数据集(mpg)。我们将在 X 轴上绘制horsepower,在 Y 轴上绘制mpg(每加仑英里数)。点数颜色将基于origin的国家,点数的大小将反映汽车weight。为了防止过度绘制,我们设置 alpha 为0.5。在这个图中,我们使用十六进制颜色代码。但是你也可以在这里使用颜色名称,比如redgreenblue。或者为默认的牛虻颜色省去这一行。我们还设置了点捕捉图例的最小值和最大值。该图例的颜色设置为默认颜色black

set_default_plot_size(15cm, 12cm)
plot(
    mpg,
    x = :horsepower,
    y = :mpg,
    color = :origin,
    size = :weight,
    alpha = [0.5],
    Geom.point,
    Scale.color_discrete_manual("#5377C9", "#DF8A56", "#82CA70"),
    Scale.size_area(
        minvalue = minimum(mpg.weight),
        maxvalue = maximum(mpg.weight)
    ),
    Theme(
        background_color = "white",
        default_color = "black",
    ),
)

作者图片

恭喜你,你刚刚完成了你的第一个美丽的牛虻情节!

分组箱线图

在该图中,我们将使用Geom.boxplot,设置 X 轴上数值的顺序,设置颜色顺序,并将图例的位置设置为top。为了匹配 Seaborn 的版本,我们还设置了箱线图之间的间距。

# download data
download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv", "tips.csv")
tips = DataFrame(CSV.File("tips.csv"))# visualize data
set_default_plot_size(16cm, 12cm)
plot(
    tips,
    x = :day,
    y = :total_bill,
    color = :smoker,
    Geom.boxplot,
    Scale.x_discrete(
        levels = ["Thur", "Fri", "Sat", "Sun"]
    ),
    Scale.color_discrete_manual(
        "#D0C4F4", "#A6D9AA", 
        order = [2, 1]
    ),
    Theme(
        key_position = :top,
        boxplot_spacing = 10px,
        background_color = "white",
    ),
)

一个非常好的分组箱线图,只有几行代码!

多元二元 KDE 图

除了数据被过滤以匹配 Seaborn 之外,这个可视化中突出的是相对位置被用于定位颜色键Guide.colorkey(pos = [0.78w, -0.42h])。此位置相对于图的宽度和高度。对于这个图,我们将使用Geom.density2d。在Theme我们将设置panel_fill颜色、grid_colorgrid_line_width

# download data
download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv", "iris.csv")
iris = DataFrame(CSV.File("iris.csv"))# visualize data
set_default_plot_size(10cm, 15cm)
plot(
    subset(iris, :species => ByRow(!=("versicolor"))),
    x = :sepal_width,
    y = :sepal_length,
    color  = :species,
    Scale.color_discrete_manual("#5377C9", "#DF8A56", "#82CA70"),
    Geom.density2d,
    Theme(
        background_color = "white",
        panel_fill = "#EAEAF1",
        grid_color = "white",
        grid_line_width = 1.5px,
    ),
    Guide.colorkey(pos = [0.78w, -0.42h]),
)

作者图片

分类变量散点图

在这个图中,我们将使用Geom.beeswarm来匹配 Seaborn 版本。该图中的新内容是,我们设置了yticks的位置,并为 Y 轴上的值设置了ylabel

# download data
download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/penguins.csv", penguins.csv)
penguins **=** DataFrame(CSV**.**File("penguins.csv"))# visualize data
set_default_plot_size(12cm, 16cm)
plot(
    dropmissing(penguins, [:body_mass_g, :sex, :species]), 
    x **=** :sex, 
    y **=** :body_mass_g, 
    color **=** :species, 
    Geom**.**beeswarm, 
    Scale**.**color_discrete_manual("#5377C9", "#DF8A56", "#82CA70"), 
    Guide**.**yticks(ticks **=** 2000**:**1000**:**7000), 
    Guide**.**ylabel("body mass (g)"), 
    Theme(
        background_color **=** "white",
    ), 
)

作者图片

此时(在牛虻版本 1.3.4 中),创建相同的水平剧情似乎无法正常工作。一旦有了解决方案,我将更新代码并绘制一个水平蜂群。

水平(分层)条形图

在这个图中,我们将使用两层来创建我们想要的柱状图。这个条形图的特殊之处在于,一个条形图代表总数,另一个条形图代表数据的一部分。所以这些不仅仅是堆叠的条形。我们将使用Geom.barhorizontal方向。我们将在每层使用一个Theme,在整个情节中使用一个Theme

# download data
download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/car_crashes.csv", "car_crashes.csv")
car_crashes **=** DataFrame(CSV**.**File("car_crashes.csv"))# visualize data
set_default_plot_size(5inch, 10inch)
p **=** plot(
    sort(car_crashes, :total, rev **=** false), 
    layer(
        x **=** :alcohol, 
        y **=** :abbrev, 
        Geom**.**bar(orientation **=** :horizontal), 
        Theme(
            default_color **=** color("#617BBA"), 
            bar_spacing **=** 3px, 
            bar_highlight **=** color("white")
        ), 
    ),
    layer(
        x **=** :total, 
        y **=** :abbrev, 
        Geom**.**bar(orientation **=** :horizontal), 
        Theme(
            default_color **=** color("#B2C8E7"), 
            bar_spacing **=** 3px, 
            bar_highlight **=** color("white"), 
        ), 
    ), 
    Guide**.**xlabel("Automobile collisions per billion miles"), 
    Guide**.**ylabel(""), 
    Guide**.**manual_color_key("", ["Total", "Alcohol-involved"], ["#B2C8E7", "#617BBA"]), 
    Theme(
        background_color **=** "white",
        key_position **=** :bottom, 
    ),
)

作者图片

现在,你可以称自己为牛虻专家了!

带注释的热图

可能这一系列中最漂亮的情节是带注释的热图。由于注释的原因,它也是最复杂的一个。在这个可视化中,使用了来自ColorSchemes模块的 colorschema magmaplasmainfernoviridisseaborn_rocket_gradient也是很好的配色方案。Geom.rectbin用于创建热图

# download data
download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/flights.csv", "flights.csv")
flights = DataFrame(CSV.File("flights.csv"))# visualize data
set_default_plot_size(17cm, 14cm)
plot(
    flights,
    x = :year,
    y = :month,
    color = :passengers,
    Geom.rectbin,
    Scale.ContinuousColorScale(
        palette -> get(ColorSchemes.magma, palette)
    ),
    Guide.xticks(
        ticks=[minimum(flights.year):maximum(flights.year);]
    ),
    Theme(background_color = "white"),
    Guide.annotation(
        compose(
            context(),
            text(
                flights.year,
                1:length(unique(flights.month)),
                string.(flights.passengers),
                repeat([hcenter], nrow(flights)),
                repeat([vcenter], nrow(flights)),
            ),
            fontsize(7pt),
            stroke("white"),
        ),
    )
)

作者图片

最后的想法

我喜欢 Python 语言和像 Seaborn 这样的数据可视化库。也就是说,以我的拙见,牛虻是 Julia 语言中最通用的数据可视化库之一(除此之外,还有其他类似的 PlotsMakieVega-Lite )。希望这篇介绍是有用的,特别是对于那些来自 Python 和使用 Seaborn 的人。如果你对与 Makie 包的对比故事感兴趣,请告诉我。

https://medium.datadriveninvestor.com/create-thematic-maps-using-shapefiles-in-julia-135ea67e9628

在围棋中学习泛型

原文:https://towardsdatascience.com/learning-generics-in-go-318f53752ccd

泛型在 Go 1.18 中发布,是时候学习如何利用这个新特性了

泛型的可视化解释,用函数表示的一行,泛型接受多个输入。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)

泛型在更新 1.18(将于 2022 年 2 月发布)中推出,带有一系列其他闪亮的新功能。你可以在我的 1.18 补丁说明摘要中了解所有的变化。

https://medium.com/@percybolmer/go-1-18-comes-with-many-amazing-changes-d33ac0afd6ee

要开始使用 1.18,您可以运行或下载它这里

go install golang.org/dl/go1.18beta1@latest

虽然还添加了其他一些好东西,但毫无疑问,泛型的实现已经盖过了其他一切。这是一个已经讨论了很长时间的话题,有许多开发者支持它,也有许多反对它。

许多人觉得泛型棘手而复杂,但是让我们揭开其中的奥秘。一旦你熟悉了它们,就不难使用了。

在本文中,我们将看看什么是泛型,并学习如何使用它们。

我的视频涵盖了泛型,如果你喜欢它胜过文本的话

什么是泛型,为什么 Go 需要泛型

通用函数,允许多种类型的输入和输出。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)

泛型是一种让函数接受多种数据类型作为相同输入参数的方式。假设您有一个函数,它应该接受一个输入参数,并减去第二个输入参数的值。

您需要决定使用哪种数据类型,int、int64 还是 floats。这将迫使任何使用 subtract 函数的开发人员在使用它之前将其值转换为正确的数据类型。

另一个解决方案是为 int 提供一个 subtract 函数,为 floats 提供另一个函数,等等,这样就有多个函数做同样的工作。

减法使用相同的减法函数,将浮点数转换为整数

如果可以跳过类型转换或重复的函数会怎么样?在上面的例子中,我们从 float 结果中得到错误的结果,因为我们在把它转换成 int 时去掉了 0.5。

因此,唯一合适的解决方案是拥有重复的函数,一个Subtract(a,b int) int和一个副本Subtractfloat(a,b float32) float32

我说复制功能是唯一的解决方案,我知道你可以使用interface{}输入和输出解决方案。我不喜欢那个软件,它容易出错,而且很笨重。您还会丢失编译时错误检查,因为编译器不知道您在用那个接口做什么。这很快变得不可维护,并增加了许多额外的代码。

这是泛型旨在解决的问题,也是为什么如此多的开发人员非常渴望看到它的发布。

现在,Go 1.18 发布了泛型的第一个草案,解决上述问题的方法很简单。

通用减法——一个简单的解决方案

泛型及其工作原理——泛型函数的基础

通过向第一个函数提供类型参数开始学习泛型。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)

让我们深入学习泛型的基本用法。

我们将从常规的Subtract函数开始,并向它添加通用特性,同时了解我们添加的是什么,以及为什么添加。

函数的语法是,我们通过声明func FunctionName来定义函数,声明之后是函数参数。

功能参数()里面定义,可以任意多。函数参数是通过声明名称,后跟数据类型来定义的。例如(a int)定义了函数的局部作用域将有一个名为 a 的整数。

Subtract方法有数据类型为 int 的函数参数ab

我们用它作为学习泛型的基础。

现在,函数参数可能看起来微不足道,但是在进入泛型之前理解它是至关重要的。

除了函数参数,还有类型参数。类型参数在[]内部定义,应该在函数参数<a,b int>之前定义。您可以像定义函数参数一样定义类型参数,名称后跟数据类型。

类型参数通常是大写的,以便于识别。

一个例子,我们声明参数 V 是一个整数,[V int]

类型参数 V 已被添加,此代码尚未运行。

函数参数和类型参数的区别在于,函数参数在函数的局部范围内可用。如果你像上面那样定义V,你将不能在函数中使用V作为变量。类型参数只说明 V 代表什么数据类型。

我们用类型参数定义的是,有一个叫做 V 的数据类型,它是一个 int。这允许我们使用V作为函数参数中int的替换,并且在函数范围内。

我们现在可以用V替换ab的数据类型,以及输出到V的函数

Subtract 现在使用类型参数 v。

现在,你可能认为我们什么也没完成,我们只是用一个更复杂的解决方案替换了int。而且你是对的,我们还没做完,上面的代码不会编译。不允许像我们一样替换类型参数中的单个数据类型,除非把它放在接口中。

如果您尝试编译当前的减法函数,您应该会看到一个错误,指出int is not an interface

原因是 a type parameter期望一个type constraint作为值,而不是一个数据类型。约束是函数参数必须满足的interface

我们将通过使用一个|字符向type parameter添加第二个数据类型。管道字符用于表示or,这意味着我们可以向V参数添加许多不同的数据类型选项。使用|也是创建新内联interface的一种简便方式。

我们将把 int32 和 float32 作为可能的数据类型添加到V中。这样做将自动创建一个被编译器用作type constraintinterface

使用|键入参数,这为我们创建了一个类型约束(接口)

您可以尝试使用带有简单 main 函数的新Subtract方法。

使用您的第一个泛型函数

为了使事情更清楚,我们可以打破类型约束。只是为了让它更容易理解,并且允许约束是可重用的。

为了创建约束,我们只需声明一个接口Subtractable,其中包含数据类型。它的语法和我们在类型参数中使用的简写定义是一样的,所以我们可以把它复制出来,我还添加了更多的数据类型。

向通用函数添加可减法类型约束

在你成为泛型大师之前的最后一件事。当你调用泛型函数来设置要使用的数据类型时,你可以应用一个类型参数,当我们到达更高级的用法时,这将更有意义。

添加的方法是再次使用类型参数,但这次是在函数调用之前。

result := Subtract[int](a, b)

类型参数和波浪号(~)

类型参数被提供给函数调用以指定数据类型。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)

所以我们现在可以使用通用函数了,但是还有一些细节。想象一下,如果你想控制在一个通用函数中使用的数据类型?

假设您想要使用我们创建的Subtract函数,它将推断数据类型,除非被明确告知。您可以通过类型参数来实现这一点,语法与定义通用函数时相同。您可以在参数前的括号中提供数据类型。

Subtract[int](10,20) // Will be using int datatype 
Subtract[int64](10,20) // Will be using int64 datatype

main.go —在调用函数时使用类型参数来决定要使用的数据类型

霍雷。我们现在甚至可以告诉函数使用什么数据类型,太棒了!

现在,这里仍然有一个问题。如果您的数据类型是 Subtractable 中任何类型的别名/派生类型,该怎么办?

您将无法以我们当前声明的可减法的方式使用您的数据类型。

// create a custom int derived from int
type MyOwnInteger intvar myInt MyOwnInteger
myInt = 10
Subtract(myInt, 20) // This will Crash, Since myInt is not Subtractable

为了解决这个问题,Go 团队还添加了~,它告诉约束,从给定类型派生的任何类型都是允许的。在下面的例子中,我们允许MyOwnInteger成为Subtractable的一部分,方法是在它前面加上~

使用~允许别名类型成为类型约束的一部分

通用类型,任何&可比较的

Go 中引入的新别名将用作类型约束,any 和 comparable。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)

我们已经讲述了generic functions,现在是时候讲述generic types了。

要创建一个泛型类型,你必须定义一个新的类型,并给它一个类型参数。为了了解这一点,我们将创建一个Results类型,它是在先前创建的Subtractable约束中找到的任何数据类型的一部分。

泛型类型结果是可减法数据类型的一部分。

语法不应该是新的,我希望您现在能够理解泛型类型定义中发生了什么。它与前面的示例相同。在本例中,我们创建了一个数据条目属于数据类型Subtractable的切片,从而告诉编译器当结果切片被初始化时,必须定义包含在可减法接口中的类型。

为了使用结果,我们将更新 main 函数,并注意当创建变量时,我们如何定义Results片将要使用的数据类型。

使用结果通用类型(通用数据类型的一部分)

现在,我已经知道你在想什么,我们能说Results应该使用Subtractable类型约束吗?

var resultStorage Results[Subtractable]

遗憾的是,Subtractable 是一个类型约束,我们不能在Results的初始化中使用它。这样做会触发编译错误interface contains type constraints

我们可以做的是使用新引入的 any 来允许Results保存任何数据类型。anyinterface{}的别名。

ResultStorage 现在可以保存减法运算的所有值。

它还不完善,是初稿。我希望我们能在未来看到添加Subtractable的方法,避免使用any,因为它允许我们添加各种数据类型。

还有另一个名为comparable的新类型,它是一个类型约束,可以通过使用==!=进行比较的任何数据类型都可以满足这个约束。

熟悉这些词很重要,因为随着社区对泛型越来越熟悉,你可能会看到它们到处出现。

接口约束和通用结构

接口可以用作类型约束。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)

到目前为止,我们只使用了单个约束和输出。我们只使用了type constraints,但是也可以使用接口作为约束。

让我们尝试创建一个接受名为Moveable的接口的通用函数。该函数将简单地触发输入类型的Move,任何实现该接口的结构都应该能够Move

Move —具有输入 v 必须可移动的约束的通用函数

你应该熟悉这个语法,我们创建了一个约束,说类型 V 必须是Moveable,输入参数 V 是类型 V

我们还将创建一个Person和一个Car来试用它。

移动多个结构的一般方式。

到目前为止,为了保持简单,我们只使用了一个通用参数。但是请记住,您可以添加多个,并输出多个。

让我们将MoveableSubtractable约束组合到Move函数中,允许用户添加一个Distance值,我们用它来计算离目标还有多远。

要添加更多类型约束,只需在[]中添加参数,就像常规参数一样。我们将添加定义为SSubtractable类型,而不是接受metersInt,我们将允许它为S

这是我们的Move函数在两个Type Constraints中的样子

func Move[V Moveable, S Subtractable](v V, distance S, meters S) S

然而,仅仅这个改变就会导致编译器崩溃,因为Move函数接受了一个Int并且Moveable定义了这是 Move 应该工作的方式。所以我们需要让Move接受Subtractable

向 Move 函数添加 Subtractable 并将 Int 改为 Subtractable

这看起来很棒,对吧?!可悲的是,上面的要点不是一个工作示例,这只是我们想要完成的伪代码。上面的代码不会编译,编译器会生气地对你大喊大叫,因为我们在一个Interface里面使用了一个Type Constraint,这是不允许的,记得吗?

但是有一种方法可以得到我们想要的东西。泛型结构是在初始化期间定义其数据类型的结构。

特别感谢 u/ar1819 帮助我找到这个解决方案

我们需要为interface定义S类型,就像我们为通用函数所做的一样。通过简单地将[S Subtractable]添加到interface声明中,我们可以说该结构不仅需要相同的方法集作为接口的一部分,还需要成为一个Generic Struct

向接口添加类型约束

如果这是合同的规则,让我们将它添加到CarPerson中。这些结构现在将被命名为Generic。这意味着当创建一个对象时,您还必须定义用于该对象的数据类型。

这就是如何在 Go 中声明泛型结构

为了创建我们的汽车和人,我们需要指定他们使用什么数据类型。这是作为所有通用特征使用[]完成的。记住,它们被称为类型参数。

初始化两个泛型结构,使用[]分配数据类型

注意,我们现在使用的是Person[S Subtractable],而不仅仅是Person,所以所有的方法都需要使用这个初始化。

使泛型结构成为可移动接口的一部分需要设置[S]

我们还应该通过升级成使Move函数现在接受一个Generic Movable,并且还接受Subtractable约束。

可移动必须定义其类型

我们准备创建main函数,它使用了我们已经完成的所有事情。使用Car的第一个Move调用很容易理解,这是因为我们使用了int,编译器会默认使用。

然而,第二个调用更加复杂,因为我们现在想要使用float64数据类型。要做到这一点,我们需要在定义类型参数的地方,为Move()调用添加常规的[]。在这种情况下,Moveable将是一个初始化为 float64 的PersonSubtractable的数据类型也将是 float64。所以类型定义会是[Person[float64], float64]Move()

为汽车和人调用移动功能

一个技巧是考虑放置类型参数的顺序。我们可以通过重新排序Move中的类型参数来避免Move[Person[float64], float64]。这要归功于编译器和运行时推断数据类型。

类型参数的排序在语法上更好

结论

祝贺你,如果你在这里成功了,你现在是仿制药领域的专家了!

我希望你会发现它们是有用的,我知道许多人一直在等待这个版本。许多人创建了自己的库,用于对切片、使用队列的地图等进行排序。很快可能会有很多新的包和新的 API 用于旧的库。

许多用例对于泛型来说都很棒,但是有时候反而容易让事情变得有点复杂。尝试只在有实际用例时使用它们。

如果你喜欢我写的东西,不要错过我的指南,它提供了围棋中的模糊功能!

https://medium.com/@percybolmer/fuzzy-testing-in-go-96eb08b7694d

走出去,成为普通人!

了解 GPT-3 教学模型(很可能)是如何训练的

原文:https://towardsdatascience.com/learning-how-gpt-3-instruct-models-were-most-likely-trained-3eb61e0f4169

斯文·布兰德斯马在 Unsplash 上的照片

【更新:2022 年 1 月 27 日,Open AI 发表了一篇名为排列语言模型以遵循指令的论文,其中他们解释了他们真正用来训练 InstructGPT 的方法。你可以在这里找到论文。]

几个月前,OpenAI 发布了他们位于 GPT 的指令模型的测试版。Open AI 声称,指令模型可以像人类一样理解你的指令,并且它们仍然拥有与其他 GPT-3 基础模型相同的能力。

这一进步为设计和工程师带来了更多创新提示的可能性。你可以告诉模型你的任务是什么,你想得到的结果是什么,模型会尽力理解你的指令并完成它们。

Flowrite ,我们每天都在使用 GPT-3 模型,所以我们没有等待一秒钟,就开始广泛测试这些指令模型。

https://www.flowrite.com/

我们对这些指导模型是如何工作的有了一个坚实的直觉,但是 Open AI 从未发表过关于指导模型如何被训练的论文或文章,所以这个问题仍然是未知的。幸运的是,谷歌研究团队在 2021 年 12 月就同一主题发表了一篇论文,该论文揭示了基于 transformer 的模型如何被微调以理解指令。

让我们看看这篇文章中最重要的方面,这将帮助我们理解如何微调大型语言模型,以便它们能够处理指令提示。

微调模型是零射击学习者

正如您可能已经发现的那样,本文围绕大型语言模型执行零触发任务的能力以及指令调优如何提高这些模型的零触发能力展开。

零射击学习(ZSL)是机器学习中的一个问题设置,在测试时,学习者观察训练中没有观察到的来自班级的样本,并需要预测它们所属的班级。— 维基百科

大规模的语言模型,如 GPT-3,具有巨大的少量学习能力,但在零次学习中表现较差。在几项任务(阅读理解、质量保证和 NGI)中,GPT-3 零发成绩比少发成绩差得多。在零镜头设置中,提示没有少量镜头/上下文中的示例,模型可能很难表现良好,这可能是因为它们无法找到与预训练数据的相似性。

指令调优被描述为微调预训练语言模型(LM)的任务,以提高其响应自然语言指令的能力。这个想法是,通过使用监督来教 LM 执行通过指令描述的任务,它将学会遵循指令,甚至对看不见的任务也这样做。

作者的指令在通过自然语言指令模板表达的 60 个 NLP 数据集上微调了 137B 参数预训练语言模型。由此产生的微调模型被称为 FLAN,正如我们将要看到的,它是在看不见的任务类型上进行评估的,这些任务类型显示了在其他机器学习设置中(例如,少量学习)相对于其他语言模型在许多任务中的性能改善。

数据集

对于教学微调,谷歌研究团队必须将超过 60 个 NLP 数据集的混合物转换为教学格式。这些数据集在 TensorFlow 数据集中公开。

用于指令微调的公开可用数据集。数据集被转换成教学格式,并按任务聚集成簇。——图来自微调过的模特是零起点的学习者谷歌研究团队

作者根据任务将数据集聚集成簇:常识、自然语言推理(NLI)、情感等。然后,对于每个数据集,他们创建了 10 个描述任务的独特的指令提示模板。为了增加数据的多样性,10 个模板中有 3 个指示模型改变任务。例如情感分类→生成一条情感正面的影评。

最后,通过随机选择特定于每个数据集的指令提示模板,对每个数据集进行格式化。

在下图中,作者说明了如何创建指令提示来构建用于指令微调的数据集:

描述自然语言推理任务的多个指令模板——图来自微调模型是零射击学习者谷歌研究团队

估价

作者感兴趣的是评估 FLAN 如何执行在指令调优期间没有看到的任务,因此有必要清楚地定义什么算作看不见的任务。

如果在指令调优期间没有看到来自 D 所属的任何任务集群的数据集,他们在评估时定义了一个看不见的数据集 D 。例如,让我们假设 D 是一个包含任务,那么该包含集群被忽略用于指令调整,并且只使用所有其他集群。

作者将数据集分组到任务簇中,并支持评估簇,同时指示对所有剩余簇进行调优。通过这种方式,他们确保果馅饼是在看不见的任务上被评估的。

带选项的分类

FLAN 是一种专用于解码器的语言模型(如 GPT ),针对不同的任务进行指令调整,因此,它会将完成的内容返回给输入文本。

分类任务的输出空间是特定于当前问题的几个唯一类之一。例如,如果我们使用仅解码器语言模型执行二元分类(“是”或“否”),我们需要记住,该模型仍在生成文本,并且大量不同的回答“是”的方式可能会降低分配给标记“是”的概率质量。

正如作者所述,在表达相同答案的备选方案中具有不期望分布的答案的概率质量使得一些方法如等级分类(考虑 2 个输出,并且 2 个输出之间最可能的是预测)不完美。

为了解决分类任务中的这个潜在问题,作者提出了选项后缀。Options 后缀是一个附加到分类任务末尾的标记,附带一个唯一输出类的列表,因此模型被限制为仅响应其中一个类。

培养

模型架构是一个密集的从左到右,只有 137B 参数的解码器转换器语言模型。该模型在网络文档、对话数据等方面被预先训练。使用 SentencePiece 库,该文本被标记为 2.49T 的 32k 词汇量的 BPE 标记。此外,作者在预训练数据集中包括了大约 10%的非英语文本。

得到的预训练模型被称为基本 LM,因此 FLAN 是基本 LM 的指令调整版本。

对于指令微调,作者建立了管道来混合所有数据集,并从其中随机采样。

结果

在自然语言推理、阅读理解、闭卷问答、翻译、常识推理、共指消解和结构转文本方面对 FLAN 进行了评估。

以下是结果的简要总结。更多细节可以在原文中找到。

  • 在 25 个任务中,零发子弹的 FLAN 在 20 个任务中超过了零发子弹的 GPT-3,甚至在 10 个任务中超过了 GPT-3 的少发子弹性能
  • 指令调优显著提高了大多数任务的基本 LM。

作者表示,总的来说,指令调优对于由人类自然表述为指令的任务(NLI、QA、翻译、结构到文本)更有效,而对于可以直接表述为语言建模的任务(常识推理和指代消解任务,格式为完成不完整的句子或段落),该模型则不太有效。这背后的直觉是,在后一种情况下,指令可能被认为是多余的,因此,它们不会添加太多信息。

消融研究——包含少量实例的说明

该论文的作者介绍了一些消融研究。其中最有趣的一个研究了指令调优的能力,在提示中的推断时间提供了一些例子。

他们在零投提示的基础上建立了一个少投学习提示,如下所示:

对于输入 x 和输出 y,指令(x) 表示零炮指令。给定 k 少量拍摄的例子:

和一个新的输入 x ,少拍提示的指令格式定义为

其中指令由输入填充,并通过中间的分隔符标记与输出连接。

提示中提供的示例的最大数量限制为 16 个,这些示例是从训练集中随机抽取的。

零注果馅饼对少注果馅饼。橙色条表示模板之间的标准偏差,是每个聚类在数据集级别的平均值——来自微调模型的数字是零射击学习者来自谷歌研究团队

可以看出,与零镜头设置相比,在提示中添加少量镜头的例子提高了 FLAN 在所有任务中的性能。在复杂的任务中,如结构到文本或 QA,这些改进更大。这实际上是有意义的,因为这些任务的输出空间更大,上下文中的示例有助于模型更好地理解输出格式,因此在零触发设置中比 FLAN 执行得更好。

摘要

在这篇文章中,我试图总结谷歌研究团队的论文中最重要的概念。

如果您听说过 GPT-3 指令模型,但不知道这些模型是如何训练的,我希望这篇摘要和本文能让您对指令调整及其好处有一个良好的直觉。

由于开放人工智能没有发表关于指令模型的论文,我们不能完全肯定 GPT-3 指令模型是按照这里介绍的完全相同的程序训练的,但很可能他们采用了非常相似的技术。

如果你想在 LinkedIn 上和我联系,就给我发个纸条!😃

参考

  1. 微调过的语言模型都是零命中率的学习者——贾森·魏马腾·博斯马文森特·y·赵凯尔文·古亚当斯·禹卫布莱恩·莱斯特杜南安德鲁·m·戴郭诉勒
  2. 开放 AI API 文档—https://beta.openai.com/docs/engines/instruct-series-beta

向文森特学习如何着装

原文:https://towardsdatascience.com/learning-how-to-dress-from-vincent-b53f9e0ffd30

用 KMeans 方法提取梵高作品中的色彩模式

故事从我偶然在一家网店看到的一件衣服开始。设计师称之为“梵高风格”的裙子,它确实有一个杰出的设计,尤其是颜色搭配。

然后我回忆起那些我独自在奥赛博物馆散步的下午,文森特·梵高在他所有的画作前使用色彩的方式给我留下了深刻的印象,一个想法浮现在我的脑海里,为什么不向文森特学习如何穿着呢?对于一些人来说,日常穿着的颜色搭配可能是一个大问题,包括我在内,如果我们可以用这位伟大艺术家的作品作为指导,我们可能会少犯一些错误。

作为一名数据科学家,我不敢挑战专业之外的任务,所以我宁愿回归机器学习。在这篇文章中,你将会读到我是如何使用 KMeans 从梵高的画作中提取主色图案的,我希望最终的结果能让你在明天早上站在衣柜前时不那么困惑。

一幅画的分色

在本文中,我使用了梵高画作数据集 [1]的一个子集,即在阿尔勒、巴黎、瓦兹河畔奥维尔、圣雷米和维尔热完成的画作。为了提取他的颜色模式,第一项任务是从每幅画中分离出不同的颜色,这是一个可以用 KMeans 聚类方法完成的过程,这是最简单的无监督机器学习算法之一。它在一组数据点中识别 k 个质心,并将每个点分配到最近的聚类中。

回到绘画或图像,不过是一组像素,每个像素由三个分量组成,代表三个颜色通道中的值:红色、绿色和蓝色。注意,当我们用 OpenCV 读取图像时,我们最终会得到一个 BGR 格式。我们首先需要将 BGR 转换成 RGB。

代替使用众所周知的肘方法来决定标签的最佳数量,我们希望在该步骤中直接为每个图像找到 6 个聚类,以获得颜色模式的统一形式。这里有一个例子,绘画“花瓶与康乃馨和玫瑰和一个瓶子”和它的调色板。

作者图片:一幅特定的画和它的调色板

需要特别注意的一个问题是,颜色的顺序应该被忽略:例如,一个由三种颜色[黑、白、灰]组成的调色板实际上等同于[白、灰、黑]。为了避免顺序带来的不必要的干扰,我们根据所有三个颜色通道的值对输出调色板的颜色顺序进行排序,这样具有 6 种不同颜色的调色板将被迫遵循一个单位顺序。我们将所有有序的调色板保存在一个列表中,该列表将在下一步进行处理。下面的代码显示了我如何格外小心地创建数据集中所有图像的调色板列表:

调色板聚类

现在我们从梵高画作的数据集中的所有图像中提取了 6 种主要颜色,我们想看看是否可以得出艺术家使用颜色的某些模式。我们希望再次使用 KMeans 聚类方法来识别一些质心,但这一次是在数据点之间,每个点代表 6 种不同的颜色。

由于一个艺术家通常有几种类型的调色板,我们使用肘方法来确定最佳的聚类数,即保证不同标签的不同点彼此足够远,同时每个聚类有足够的样本的数。

我们从 1 到 10 迭代 k 的值,并计算相应的惯性值。

根据下面的图,选择 6 作为集群编号,从该编号开始,对于更大的编号没有观察到显著的改进。

作者图片:弯头方法的绘图

然后我们得到 6 个调色板,代表梵高使用颜色的模式。

作者图片

后续步骤

当我们站在衣柜前时,从梵高的作品中提取的颜色模式和我上面给出的简单有趣的实现已经可以作为一个指南:只需从这些梵高的调色板中选择颜色。

然而,这项工作还远远没有完成:推荐还没有自动化,我们仍然必须根据我们的视觉印象做出决定。况且大艺术家也只是给出了一个配色的思路,没有其他信息。例如,你知道黄色和蓝色是很好的搭配,但是你可能仍然不知道是黄色的裙子配蓝色的帽子还是蓝色的裤子配黄色的围巾。解决这一问题的一种可能是分析调色板的百分比,通过比较衣服的尺寸给出更精确的建议。

参考

[1]金,亚历山大(2022),《梵高绘画数据集》,门德利数据,V2,doi:10.17632/3 sjtjfhx 7.2

对于在缺少节点特征的图上学习来说,特征传播是一种简单且令人惊讶地高效的解决方案

原文:https://towardsdatascience.com/learning-on-graphs-with-missing-features-dd34be61b06

数据不完整的图形 ML

大多数图形神经网络通常在所有节点可用的全套特征的假设下运行。在现实场景中,功能通常只是部分可用(例如,在社交网络中,年龄和性别只能为一小部分用户所知)。我们表明,特征传播是一种有效的和可扩展的方法,用于处理图形机器学习应用中的缺失特征,尽管它很简单,但工作得非常好。

大多数图形神经网络期望作为输入的图形具有每个节点的完整特征向量(左)。更常见的真实场景是只有部分功能可用(右图)。

本文与 伊曼纽·罗西 合著。

G raph 神经网络(GNN)模型通常为每个节点假设一个完整的特征向量。以两层 GCN 模型[1]为例,它具有以下形式:

z=aσ(axw₁)w

该模型的两个输入是编码图结构的(标准化)邻接矩阵 A 和包含作为行的节点特征向量的特征矩阵 X ,并输出节点嵌入 Z 。GCN 的每一层执行节点式特征变换(由可学习矩阵 W ₁和 W ₂参数化),然后将变换后的特征向量传播到邻居节点。重要的是,GCN 假设观察到了 X 中的所有条目

在现实世界的场景中,我们经常会看到一些节点特性可能缺失的情况。例如年龄、性别等人口统计信息。仅可用于一小部分社交网络用户,而内容特征通常仅呈现给最活跃的用户。在联合购买网络中,并非所有产品都有完整的描述。这种情况变得更加严重,因为随着对数字隐私的认识不断提高,只有在用户明确同意的情况下,数据才越来越可用。

在所有上述情况下,特征矩阵具有缺失值,并且大多数现有的 GNN 模型不能被直接应用。最近的几项工作导出了能够处理缺失特征的 GNNs 模型(例如[2–3]),然而,这些模型在高缺失特征率(> 90%)的情况下受到影响,并且不能扩展到具有超过几百万条边的图。

在与 Maria Gorinova、Ben Chamberlain (Twitter)、Henry Kenlay 和 Xiaowen Dong (Oxford)共同撰写的一篇新论文[4]中,我们提出了特征传播(FP) [4]作为这个问题的一个简单而有效的解决方案。简而言之,FP 通过传播图上的已知特征来重构缺失的特征。然后,可以将重构的特征输入任何 GNN,以解决下游任务,例如节点分类或链路预测。

特征传播框架。输入是一个缺少结点要素的图表(左侧)。在初始步骤中,特征传播通过迭代扩散图中的已知特征来重建缺失的特征(中间)。随后,该图和重构的节点特征被输入到下游的 GNN 模型,该模型然后产生预测(右)。

传播步骤非常简单:首先,用任意值初始化未知特征[5]。通过应用(标准化的)邻接矩阵来传播特征,然后将已知特征重置为其基础真值。我们重复这两个操作,直到特征向量收敛[6]。

特征传播是在缺失特征的图上学习的一种简单而惊人强大的方法。特征的每个坐标被单独处理( x 表示 X 的一列)。

FP 可以从数据同质性(“平滑度”)的假设中导出,即,邻居趋向于具有相似的特征向量。可以使用狄利克雷能量来量化同质性的水平,狄利克雷能量是一种二次形式,用于测量节点的特征与其邻居的平均值之间的平方差。狄利克雷能量的梯度流[7]是图热扩散方程,已知特征用作边界条件。FP 是通过使用单位步长的显式前向欧拉方案对该扩散方程进行离散化而获得的[8]。

动画展示了应用更多特征传播迭代时标量节点特征的演变示例。未知特征被初始化为零,但是迅速收敛到使给定图形上的狄利克雷能量最小化的值。

特征传播与标签传播(LP) [9]相似。然而,关键区别在于 LP 是一种特性不可知的方法,它通过传播图中的已知标签来直接预测每个节点的类别。另一方面,FP 用于首先重建丢失的节点特征,然后将这些特征馈送到下游的 GNN。这使得 FP 能够利用观察到的特性,并在我们实验的所有基准测试中超过 LP。此外,在实践中,带有标签的节点集和带有特征的节点集不一定完全重叠,因此这两种方法并不总是直接可比的。

我们使用七个标准节点分类基准对 FP 进行了广泛的实验验证,其中我们随机移除了可变分数的节点特征(独立于每个通道)。在重构特征上,FP 后接 2 层 GCN 明显优于简单基线以及最新的最先进方法[2–3]。

FP 在特征丢失率高(> 90%)的情况下表现尤为突出,而在这种情况下,所有其他方法都会受到影响。例如,即使丢失了 99%的特征,当所有特征都存在时,与相同的模型相比,FP 平均只损失大约 4%的相对精度。

Cora 数据集上不同缺失率(从 0%是大多数 gnn 的标准状态到 99%的极端情况)下的节点分类精度。

FP 的另一个关键特性是它的可伸缩性。虽然竞争方法不能扩展到几百万边的图,但是 FP 可以扩展到十亿边的图。我们用了不到一个小时的时间在我们的内部 Twitter 图表上运行它,使用一台机器有大约 10 亿个节点和 100 亿条边。

FP+GCN 的运行时间(以秒为单位)以及最新的最新方法 GCNMF 和 PAG nn[2–3]。FP+GCN 比其他两种方法快 3 倍。GCNMF 在 OGBN-Arxiv 上内存不足(OOM),而 GCNMF 和 PaGNN 在 OGBN-Products 上内存不足(约 123M 边),其中 FP 的重建部分(不训练下游模型)只需约 10s。

FP 目前的一个局限是它不能很好地处理异向图,即邻居往往具有不同特征的图。这并不奇怪,因为 FP 是从同伦假设(借助于扩散方程中狄利克雷能量的最小化)中导出的。此外,FP 假设不同的特征通道是不相关的,这在现实生活中很少发生。用替代的更复杂的扩散机制来适应这两种限制是可能的。

当 99%的特征缺失时,具有不同程度的同嗜性(0 表示极端异嗜性,1 表示极端同嗜性)的合成图上的节点分类精度。虽然在高同质性设置中,FP 的性能几乎与具有全部特征的情况一样好,但是在低同质性设置中,两者之间的差距是显著的,并且 FP 的性能劣化为简单基线的性能,在简单基线中,缺失的特征用零代替。

尽管缺失节点特征的图在现实应用中无处不在,但在缺失节点特征的图上获取是一个几乎未被探索的研究领域。我们相信,我们的特征传播模型是提高在具有缺失节点特征的图上学习的能力的重要一步。它也提出了关于在这种环境中学习的理论能力的深刻问题,作为关于信号和图形结构的假设的函数。FP 的简单性和可扩展性,以及与更复杂的方法相比惊人的好结果,即使在极度缺失特征的情况下,也使其成为大规模工业应用的良好候选。

[1] T. Kipf 和 M. Welling,带图卷积网络的半监督分类 (2017),ICLR。

[2] H. Taguchi 等人,包含缺失特征的图的图卷积网络 (2020),未来一代计算机系统。

[3] X .陈等.属性缺失图的学习 (2020),arXiv:2011.01623

[4] E. Rossi 等人,关于在具有缺失节点特征的图上学习的特征传播的不合理有效性 (2021),arXiv:2111.12128

[5]我们证明了该算法收敛于相同的解,而不管未知值的初始化。然而,不同的初始化可能导致收敛所需的不同迭代次数。在我们的实验中,我们将未知值初始化为零。

[6]我们发现约 40 次迭代足以在我们实验的所有数据集上收敛。

【7】一个梯度流可以看作是变分问题中梯度下降的连续类比。它源于泛函的最优性条件(变分法中称为欧拉-拉格朗日方程)。

[8] B. Chamberlain 等人, GRAND: Graph 神经扩散 (2021),ICML。另见附带的博文

[9] X. Zhu 和 Z. Ghahramani,【利用标签传播从有标签和无标签数据中学习】 (2002),技术报告。

我们非常感谢 Henry Kenlay 和 Davide Eynard 对这篇文章的校对。关于图形深度学习的其他文章,请参见《走向数据科学》中迈克尔的 其他帖子 订阅他的帖子 YouTube 频道 ,获取 中等会员资格 ,或者关注 迈克尔

使用监督方法学习电子商务中的产品相似性

原文:https://towardsdatascience.com/learning-product-similarity-in-e-commerce-using-a-supervised-approach-525d734afd99

利用深度学习寻找相似产品的实用解决方案。以产品为中心的方法。

斯科特·韦伯在 Unsplash 上拍摄的照片

T 每个电子商务的主要组成部分是产品和与产品互动的消费者。在电子商务处理的许多用例中,有一些是以消费者为中心的,它们试图通过分析购买或查看产品的历史来了解消费者的行为,并向具有类似购买行为的消费者推荐类似的产品,以及以产品为中心的,如新产品的价格估计、需求预测、找到类似的产品等。本文的范围是研究以产品为中心的方法来学习产品相似性,目标是获得每个产品的低维向量表示。有了这样的数字表示,我们就可以应用某种相似性度量,如余弦相似性,并在不同产品之间建立相似性。我展示了两种构建低维产品表示的实用方法,首先使用 PyTorch 训练回归深度学习模型,然后通过提取包含产品标题的每个词的嵌入权重或提取包含所有产品属性的完整向量来构建这样的表示。每种方法都有优点和缺点,本文也将讨论这些优点和缺点。

电商公司发布的文章数量显示,构建产品相似度解决方案确实是一项非常重要的工作: Price2SpyEDITEDWalmartWebinterpret 。一些科学论文也讨论了这个主题:

产品之间相似性的定义是领域和用例特定的,例如:给定两只阿迪达斯的鞋子。在 0-1 的范围内,相似度是多少?

截图自 Amazon.com

截图自 Amazon.com

答案是:视情况而定。如果只比较产品名称,产品完全相似。如果我们引入颜色属性,相似性仍然很高,但不是 1,可能是 0.95 或 0.9,我们不知道确切的数字。比较同色不同码的鞋子怎么样?我们看到价格现在发生了变化。

截图自 Amazon.com

这个例子表明相似性取决于所选择的产品属性。基于产品属性估计产品相似度的可能解决方案有哪些?

  • 我们可以训练一个关于标题的 word2vecBERT 模型,或者使用预训练的嵌入来学习包含标题的单词的矢量表示,或者使用 doc2vec 模型学习完整标题的矢量表示——然而,这些模型可能无法很好地表示简短的产品标题、拼写错误的标题或非常专业的单词、特定领域的缩写,或者用英语以外的语言编写的标题。
  • 我们可以应用产品的图像相似性,并将它们与标题的矢量表示相结合。但是产品的图像可能丢失或者质量不足以训练图像相似性模型。
  • 我们可以对由不同属性、图像嵌入和标题嵌入构建的产品表示进行聚类——缺点包括可伸缩性问题、难以建立最佳数量的聚类,以及需要在一个聚类内执行额外的相似性比较。
  • 最后但同样重要的是,我们可以使用基于深度学习的监督方法(分类或回归),其中在损失最小化期间学习产品向量表示。我们必须回答的唯一问题是我们的最小化任务是什么。其中一个候选可以是价格估计或需求预测。在这种情况下,模型可以了解哪些产品在价格方面彼此更相似(在价格估计的情况下),或者哪些产品在销售方面彼此更相似(在需求预测的情况下)。这种方法的优点很多:( 1)它不需要对包括产品标题和其他文本信息在内的产品属性进行特殊处理和复杂的预处理;( 2)它解决了主监督任务并并行生成产品向量表示;( 3)然后可以通过更新关于类似产品的知识来改进主监督任务。

我遵循监督方法,并在来自 Kaggle Mercari 价格建议挑战赛的数据上进行演示,该挑战赛的任务是自动向在线卖家建议产品价格:

日本最大的社区购物应用 Mercari ,想要为卖家提供定价建议,但这很难,因为他们的卖家可以在 Mercari 的市场上出售任何东西,或任何一捆东西。

Mercari 数据集包含大约 150 万种产品,并包含以下各列:

  • 名称 —卖方提供的产品名称。
  • item _ condition _ id—卖方提供的物品的五个条件(1,2…5)
  • category _ name—每个产品的类别列表的三个级别,如男士/上衣/t 恤、女士/珠宝/项链
  • brand_name —每个产品可能所属的对应品牌
  • 运费 — 1,如果运费由卖家支付,则为 0,否则为
  • item _ description—产品描述(卖方可自由提供任何描述)
  • 价格 —目标变量,以美元表示

正如我们可以看到的,Mercari 数据包含所有类型的特征:数字、分类和文本,它们通常出现在电子商务数据中。我们在这里也有几个挑战:由于卖家可以自由提供关于产品的任何信息,产品描述可能完全是空的或拼错的。产品名称也可以用错别字书写,如下例所示。

作者图片

深度学习解决方案具有以下架构:

作者图片

输入由各种特征组成:数字、分类和文本。以 PoC 形式提供的代码在编写时考虑到了最大的灵活性。它允许定义属于上述三种类型之一的任何特征集。分类输入通过嵌入层转换到低维空间。文本输入(产品标题、产品描述)首先被令牌化为文字和字符,并通过嵌入层转换到低维空间。在字符级别上学习产品标题和描述可以增加拼写错误的产品或在文本输入中有轻微差异的产品的匹配。GRU/LSTM 层返回序列中最后一个单词或字符的隐藏状态。最后,所有图层输出被连接成一个密集图层,并且可以在顶层定义附加的密集或跳过连接图层。绿色突出显示的两个层在我们的下游任务中起着重要作用。

模型定型后,有两种方法可以提取产品表示:

单词嵌入层包含每个单词的嵌入。我们提取属于产品标题的每个单词表示,并对所有单词向量进行平均。得到的平均向量是产品名称的数字表示。构建了所有产品的向量表示后,我们可以应用类似余弦相似性的相似性度量来比较产品之间的相似程度。这种方法的优点是,在我们提取单词嵌入后,可以丢弃训练好的模型。这种方法的缺点是在训练中没有看到的标题词不会被表示出来。因此,没有代表性的产品标题的相似性是不可能测量的。另一个不利方面是对名称几乎相似但属于不同类别的产品的敏感性。由于产品名称是由单词嵌入层构成的,所以一个单词只有一种表示。

PyTorch 示例:假设我们已经将产品标题符号化为单词。字典中有 10 个单词,我们的单词向量表示有 4 个维度。

import torch
import torch.nn as nn
from sklearn.metrics.pairwise import cosine_similarity#size of the dictionary + 1 out of vocabulary token
word_embedding = **nn.Embedding**(10 + 1, 4, padding_idx = 0)#here we should have trained our model
#extract word representations#there are 4 products each having 3 words indexed by the vocabulary
#index 0 reserved for any out of vocabulary tokenproduct_words = torch.LongTensor([[1, 2, 3], 
                                  [3, 9, 8], 
                                  [4, 5, 7], 
                                  [1, 2, 4]
                                 ])
#average words per product
title_vectors = word_embedding(product_words).mean(axis = 1).detach().numpy()#compare similarity of the first product to all products
**cosine_similarity**(title_vectors[0].reshape(1,-1), title_vectors)

串联层是第一个密集层,它将各个数值、分类和文本层的输出串联起来。我们可以将这一层视为由所有类型的输入组成的产品表示。这样做的好处是,如果测试产品由单词嵌入层中不存在的单词组成,仍然有可能使用其他产品属性找到相似性。这种方法需要改变正向传播函数。我们返回最后一个输出图层和连接图层的结果,而不是返回回归结果(最后一个输出图层)。通过在训练和测试产品上应用该模型来执行单词表示的提取。

PyTorch 示例:假设我们已经将产品标题符号化为单词。字典中有 10 个单词,我们的单词向量表示有 4 个维度。此外,我们有一个分类输入,它有 4 个唯一值,我们希望将它们表示为一个三维嵌入向量。

首先,我们通过使用嵌入层和 LSTM 层来处理产品标题。

import torch
import torch.nn as nn**EMBEDDING_DIM** = 4#size of the dictionary + 1 out of vocabulary token
word_embedding = **nn.Embedding**(10+1, EMBEDDING_DIM, padding_idx = 0)#do we want to process text from left to right only or in both directions?
**BIDIRECTIONAL** = True
**HIDDEN_SIZE** = 5
**NUM_LAYERS** = 2
lstm = **nn.LSTM**(input_size=**EMBEDDING_DIM**, hidden_size=**HIDDEN_SIZE**, batch_first=True, num_layers = **NUM_LAYERS**, bidirectional = **BIDIRECTIONAL**)product_words = torch.LongTensor([[1, 2, 3], 
                                  [3, 9, 8]
                                 ])#apply lstm on embedded words
packed_output, (**hidden_state**, cell_state) = **lstm(word_embedding(product_words))**batch_size = hidden_state.shape[1]#extract last step from the hidden state
last_step = hidden_state.view(**NUM_LAYERS**, 2 if **BIDIRECTIONAL** else 1, batch_size, -1)
last_step = last_step[num_layers-1]
#reorder batch to come first
last_step = last_step.permute(1, 0, 2)
#flatten dimensions (B, HIDDEN_SIZE * 2 if BIDIRECTIONAL else 1)last_step = last_step.reshape(batch_size, -1)
last_step.shape

第二,我们通过嵌入层来处理分类输入

**EMBEDDING_DIM** = 3cat_embedding = **nn.Embedding**(4 + 1, **EMBEDDING_DIM**, padding_idx = 0)#indices of categorical values
batch_categories = torch.LongTensor([[1],
                                     [3]
                                 ])batch_size = batch_categories.shape[0]
cat_result = **cat_embedding(batch_categories)**
cat_result = cat_result.reshape(batch_size, -1)
cat_result.shape

最后,我们连接两个向量,形成我们的乘积表示向量:

#tensor dimensionality [batch_size, last_step.shape[1] + cat_result.shape[1]#this is our product representation
concatenated_layer = **torch.cat**([last_step, cat_result], dim = 1)
concatenated_layer.shape

关于 Kaggle Mercari 数据的培训

我从下面的 Kaggle 笔记本中使用了一些方法来应用数据清理和特征工程。

我在模型中使用了以下数字特征:

  • 运费 — 1,运费由卖家支付,否则为 0
  • desc _ 伦 —产品描述的字数
  • name_len —产品标题的字数
  • is_brand_missing —如果缺少品牌信息,则为 1,否则为 0
  • is _ item _ description _ missing—如果缺少产品描述,则为 1,否则为 0

分类特征:

  • item_condition_id —卖方提供的项目的五个条件(1,2…5)
  • brand_name —每个产品可能所属的相应品牌
  • 子类别 _1子类别 _2子类别 _3 —每种产品的三级类别列表

文本特征(单词和字符级别)

  • 名称 —产品名称
  • 项目 _ 描述 —产品描述

目标变量价格首先进行对数转换,然后使用 scikit-learnpower transformer进行转换

我将 Kaggle 提供的训练集拆分为训练和验证,分配 20%的产品进行验证。最后,我对 1,185,328 个产品进行了 10 个时期的模型训练,并提前停止

模型参数总结如下:

作者图片

由单词嵌入层构造的产品表示向量的维数为 50(text _ Embedding _ dimension)。从级联层构建的乘积表示向量具有 787 的维度,并且计算如下:

  • 具有 4,178,6,23,71 → 282 的不同嵌入维数的 5 个分类特征(分类嵌入大小
  • 5 个数字特征(数字输入 _ 大小)
  • 2 基于单词的双向文本特征(text _ recurrent _ hidden _ size):
    2 *(100 * 2)→400
  • 2 基于字符的单向文本特征(char _ recurrent _ hidden _ size):2 * 50→100

结果

第一次比较:不存在于训练集中的产品

该产品为“speacker”,卖方将其拼错。

作者图片

在这种情况下,单词嵌入方法不起作用,因为模型没有将这个单词映射到词汇表中。级联层方法是可行的。

作者图片

前 4 个最相似的产品是与被搜索产品具有可比价格的扬声器。类别、品牌和运输具有相同的值。我猜第一个产品的相似度更高,因为它的产品描述比其他产品更短。如果像产品描述长度这样的特征在相似性比较过程中不太重要,我们可以创建两个连接层:一个用于相似性比较,另一个用于保存对建模重要但对下游任务中的相似性比较不重要的特征的输入。

第二次比较:快递波托菲诺上衣 m 码

作者图片

Word 嵌入层的前 5 名同类产品:

作者图片

最上面的产品与搜索的产品有相似的品牌。标题中也有三个常用词。

串联层的前 5 个类似产品:

作者图片

虽然顶级产品在被搜索产品的标题内具有不同数量的常用词,但是所有产品都属于同一品牌。而且这些商品的价格也和搜索到的商品价格相当。

第三次对比:iphone 5c 16gb att GSM

作者图片

Word 嵌入层的前 5 名同类产品:

作者图片

这里的顶级相似产品在标题中有更多与搜索产品相同的词

串联层的前 5 个类似产品:

作者图片

搜索到的产品和第四个产品都提到了美国电话电报公司(att vs at&t)。他们两个价格一样。很难决定第四个产品是否与第一个产品更相似,但相似性差异不大(0.808 比 0.805)

结论

我在本文中提到的几个例子表明,两种产品相似性的方法非常有效。单词嵌入层有一个缺点,那就是由训练时不知道的全新单词组成的产品标题将不可能进行比较。另一方面,一旦从单词表示中重建了产品标题,就可以丢弃训练好的模型。在连接层方法中,需要模型来预测新产品和提取产品表示。无论采用何种方法,都需要对模型参数和领域专业知识进行适当的调整。在级联层方法中,需要决定哪些特性对于产品表示是重要的。附加的文本预处理或/和包含图像嵌入也可以提高相似性。

所提供的代码非常灵活,可以适应许多具有数值、分类和文本特征的表格数据集,用于回归任务。

完整代码可以从我的 Github repo 下载

感谢阅读!

通过自我发现、自我分类和自我恢复学习语义丰富的表征:综述

原文:https://towardsdatascience.com/learning-semantics-enriched-representation-via-self-discovery-self-classification-and-ec0b7a08e566

使用新的迁移学习技术预训练深度学习模型,在稀缺的医学图像数据集上获得更好的结果

乔纳森·博尔巴在 Unsplash 上的照片

将机器学习和深度学习模型应用于医学成像任务的主要问题之一是缺乏足够的数据来训练模型。医学图像的手动生成和标记成本高且耗时,因为需要训练有素的专家来正确理解和标记医学图像。为了解决计算机视觉中缺乏数据的问题,通常使用迁移学习技术,例如预训练和微调,其中首先用另一个域(通常是有大量训练数据的域)中的数据训练模型,然后将这个预训练的模型微调到有少量标记数据的域。在大量未标记数据可用的情况下通常使用自我监督学习技术,利用训练数据中的有用信息对这些未标记图像的模型进行预训练。要了解更多关于自我监督学习与其他培训模式的不同之处,请参考这篇由 Louis Bouchard撰写的文章

任何自监督学习算法中的一个重要步骤是确定学习信号可用于模型训练的数据的属性。当数据是图像时,诸如彩色化[1,2],拼图[3,4],旋转[5,6]和许多其他技术被用于从未标记的数据预训练模型。彩色化技术通常试图从图像的灰度对应物预测图像的颜色属性。拼图技术破坏图像并训练网络来恢复原始图像。旋转技术试图预测图像旋转。

尽管这些自监督技术对于自然图像工作得很好,但是,对于从医学数据集进行预训练来说,这些不是最佳的技术。医学数据具有重复的解剖模式,其也可被用作预训练模型的学习信号。这篇论文【8】介绍了一种自我监督的预训练方法,该方法利用医学图像中的重复模式来学习更适合各种医学成像任务的预训练模型。图 1 显示了医学图像中循环模式的一个例子。

图一。医学图像中的循环模式。来源链接

本文[8]中利用循环解剖模式的自我监督技术介绍了三个步骤,即:相似患者中解剖模式的自我发现、已学习解剖模式的自我分类****和转换模式的自我恢复这个模型整体被称为语义生成。仅使用自我恢复模块而不进行自我分类和自我发现是同一研究组的早期论文之一,被称为Models Genesis【7】。****

自分类模块帮助模型学习图像的语义,自恢复模块帮助模型学习数据的视觉属性,如外观、纹理、几何形状等。接下来,我们将逐一介绍这些步骤。

自我发现— 该步骤的目标是从未标记的图像中识别重复的解剖模式。这主要包括 3 个步骤——

  1. 用未标记的图像训练自动编码器。要了解更多关于自动编码器的信息,请参考由 Matthew Stewart 撰写的这篇综合文章。图像的潜在表示被用作图像的标识符,这意味着对于未来的步骤,我们使用学习到的图像的潜在表示,而不是原始图像。
  2. 随机选择一个参考图像,然后在潜在空间中找到与参考图像最近的 k 个图像(距离是在图像的潜在表示上测量的,而不是在原始图像上)。注-** k 是一个超参数,本文中使用的值的选择在实验部分讨论。**
  3. 在所有这些相似的图像中随机选择 n 个点,然后裁剪一个补丁。将伪标签分配给补丁。这些补丁包含在步骤 2 中发现的相似图像中的重复模式。补片数和伪标签数(C)是另一个超参数,论文中使用的值在实验部分提到。

在自我发现过程的最后,我们有一个分配了伪标签的补丁集合,可能在每个补丁中捕获一些有用的解剖模式。图 2 显示了完整的自我发现过程。

图二。来源链接

自分类— 该步骤利用自发现步骤后获得的标记的小块来训练多类分类器以正确预测伪标签。分类器有一个类似编码器的网络,后面是一个完全连接的层。编码器与接下来讨论的自恢复步骤共享。想法是通过训练分类器来预测在自我发现步骤中发现的重现解剖模式的正确伪标签,模型的学习权重存储关于图像中这些语义结构的信息。

自恢复— 该步骤首先用某些变换来修改图像(稍后将讨论这些变换),然后尝试使用编码器-解码器网络从变换后的图像重建原始图像。训练模型来重建原始图像有助于学习各种视觉表示。

编码器与自我分类步骤中使用的编码器相同。自分类和自恢复网络以多任务学习格式一起训练。图 3 显示了自我分类和自我恢复模块。

图 3。自我分类和自我恢复模块。注意,编码器对于两个模块是公共的,并且转换仅仅是为了自恢复而进行的。来源链接

由模型学习的视觉属性取决于在恢复之前对图像进行的变换的类型。本文讨论了 4 种类型的变换— 非线性、局部像素混洗、出画和入画

****通过非线性变换学习外观本文使用贝塞尔曲线(视频解说)作为非线性变换,为每个像素赋予一个唯一的值。原始图像的恢复教导网络关于器官外观,因为医学图像中的强度值给出了对器官结构的洞察。

****通过局部像素重排学习局部边界和纹理——局部像素重排涉及从一个面片随机选择窗口中重排像素顺序,以获得一个变换的面片。选择窗口的大小,使得图像的全局内容不变。从这个变换中的恢复学习了图像的局部边界和纹理。

****通过外绘和内绘学习上下文t——在外绘和内绘中,通过将不同尺寸和纵横比的窗口叠加在彼此之上,获得复杂形状的单个窗口。

外绘 —在窗口外分配随机像素,同时保留窗口内像素的原始亮度。画外复原学全局几何空间布局

In-painting —保留窗口外的原始亮度,并替换内部像素的亮度值。器官的局部连续性是在修复过程中从内嵌图像中学习的。

图 4 显示了应用于 CT 图像的每个变换的可视化。

图 4。在 3D CT 图像上完成的变换。来源链接

训练— 在多任务学习范例中,涉及自我分类和自我恢复模块的整个模型被一起训练。这实质上意味着用于训练整个模型的损失函数是自分类(分类交叉熵损失)和自恢复(重建损失)模块的损失函数的加权和。个体损失函数的权重是经验学习的超参数。

微调和模型重用 —在使用自我发现、自我分类和自我恢复来训练模型之后,模型的不同组件可以被重用并针对目标任务域进行微调。**对于图像分类任务,模型的编码器被重用。对于图像分割任务,编码器和解码器都被重用。**

实验

基于目标图像模态,在两个不同的数据集上训练该模型。公开可用的 CT 扫描用于 3D 图像模态,X 射线用于 2D 图像模态。

****由 623 个 CT 扫描组成的训练数据集LUNA 2016【9】(Creative Commons Attribution 4.0 国际许可)和由 75708 个 X 射线图像组成的胸部 X 射线 14【10】(CC0:公共领域)用于训练语义发生模型。

超参数—

  • 对于自我发现,选择前 k 个相似患者。对于 2D/3D 情况,k 根据经验设置为 200/1000。
  • 对于 3D/2D 图像,c(伪标签的数量)被设置为 44/100,以覆盖整个图像,同时避免重叠。

基线 —在所有实验中,模型都在六个公开的医学成像应用程序上进行评估,涵盖分类和分割。图 5 显示了用于评估模型的不同任务。

图 5。用于评估的数据集。来源链接

****评测/微调数据集-LUNA-2016【9】(知识共享署名 4.0 国际许可)LIDC-IDRI【16】(知识共享署名 3.0 未署名许可)LiTS-2017【17】(署名-非商业性-非商业性-非专有 4.0 国际)brats 20

3D 迁移学习的预训练 3D 模型 — NiftyNet[11],MedicalNet[12],Models Genesis[7],Inflated 3D[13]。

预训练的自我监督学习 —图像嵌入绘画【14】,补丁洗牌【15】,模型生成【7】。

结果

  1. 将自我分类和自我恢复添加到现有的自我监督学习方法中

图 6 比较了在现有的自我监督学习方法上添加语义(自我恢复+自我分类)的结果,所述自我监督学习方法包括修补[14]、修补洗牌[15]和模型生成[7]。注— Models Genesis 是同一个研究组的论文,只涉及自我恢复模块,没有自我发现和自我分类模块。

实验在 3 个不同的领域进行(NCC——CT 图像上的肺结节分类,LCS——CT 图像上的肝脏分割,BMS——MRI 图像上的脑肿瘤分割)。在现有的自我监督学习技术的基础上添加语义导致了这 3 个领域的改进。

图 6。来源链接

2.将语义 Genesis 3D 与预训练的 3D 模型进行比较—** 该实验将语义 Genesis 与其他预训练(监督和自监督)的 3D 模型进行比较。对涉及 3D 图像(CT 和 MRI 图像)的 6 个任务中的 4 个任务的结果(图 7)进行了评估。**

图 7。来源链接

3.自我分类和自我恢复模块的比较—** 将自我恢复和自我分类分别与组合语义生成方法进行比较。结果(图 7)显示了两个重要的结论。首先,在四个不同任务中的三个任务中,自我恢复和自我分类的组合优于单个组件。第二,自我分类在一些任务中表现更好,而自我恢复在其他任务中表现更好,这表明它们学习互补特征,将它们加在一起比单独使用它们中的每一个导致学习额外的特征。**

4.语义成因 3D 与基于 2D 切片的方法的比较—3D 成像模式中的任务通常在 2D 重新制定和解决。该实验比较了语义 Genesis 3D 和基于 2D 切片的方法。在两种 3D 成像模式中评估结果(NCC-CT 上的肺结节检测,NCS-CT 图像上的肺结节分割)。结果(图 8 中的前两个结果)显示语义 Genesis 3D 优于其他基于 2D 切片的方法。

图 8。来源链接

5.语义成因 2D 与其他预训练 2D 模型的比较** —比较是在两个医学成像任务(PXS—x 射线图像上的气胸分割,DXC—x 射线图像上的胸部疾病分类)包括 2D x 射线图像,以及两个 3D 医学成像任务( NCC 和 NCS )上进行的。结果(图 8)表明 Semantic genesis 在 PXS 中的性能优于 ImageNet,在 NCC 和 NCS 中的性能与 ImageNet 相当。**

结论

本文提供了一种模型和训练算法来学习医学成像任务的更好的表示和更好的预训练模型,这些模型可以针对不同的医学图像领域进行微调,以应对医学应用任务中的数据稀缺问题。该论文设计了模型以利用医学图像中重复出现的解剖模式,并在自我监督的训练范例中利用它们。我觉得这个想法和结果非常有前途,可以用作医学分类/分割任务的预训练方法,尽管与公开可用的预训练图像净重相比,实现起来更加耗时和复杂。

你可以在下面的网址找到这篇论文的官方 GitHub 实现——https://github.com/fhaghighi/SemanticGenesis

我希望这篇文章对你有所帮助和启发。我写过的其他论文总结这里这里你都可以找到。

请关注我的个人资料,以获得我未来文章的通知。

参考

  1. Larsson,g .,Maire,m .,Shakhnarovich,g .:自动着色的学习表示。年:欧洲计算机视觉会议。第 577-593 页。施普林格(2016)2。拉尔森、迈尔、沙赫纳罗维奇;
  2. 彩色化作为视觉理解的代理任务。IEEE 计算机视觉和模式识别会议论文集。第 6874 至 6883 页(2017 年)
  3. Kim,d .,Cho,d .,Yoo,d .,Kweon,I.S .:通过完成损坏的拼图学习图像表示。In: 2018 年 IEEE 计算机视觉应用冬季会议(WACV)。第 793-802 页。电气和电子工程师协会(2018)
  4. 诺鲁齐,m .,法瓦罗,p .:通过解决七巧板视觉表征的无监督学习。年:欧洲计算机视觉会议。第 69-84 页。施普林格(2016 年)
  5. 冯,钟,徐,c,陶,d:基于旋转特征解耦的自监督表示学习。IEEE 计算机视觉和模式识别会议论文集。第 10364 至 10374 页(2019 年)
  6. 通过预测图像旋转的无监督表示学习。arXiv 预印本 arXiv:1803.07728 (2018)
  7. Z.Zhou,V. Sodha,M. M. Rahman Siddiquee,R. Feng,N. Tajbakhsh,M. B. Gotway,和 J. Liang,“模型生成:用于 3d 医学图像分析的通用自学模型”,医学图像计算和计算机辅助干预 MICCAI 2019 年。湛:施普林格国际出版公司,2019 年,第 384–393 页。
  8. F.、M. R. Hosseinzadeh、Z. Zhou、M. B. Gotway 和 J. Liang,“通过自我发现、自我分类和自我恢复学习语义丰富的表示”,医学图像计算和计算机辅助干预 MICCAI 2020。湛:施普林格国际出版公司,2020 年,第 137-147 页。
  9. Setio,A.A.A .,Traverso,a .,De Bel,t .,Berens,M.S .,van den Bogaard,c .,Cerello,p .,Chen,h .,Dou,q .,Fantacci,M.E .,Geurts,b .,等人:计算机断层摄影图像中肺结节自动检测算法的验证、比较和组合:luna16 挑战。医学图像分析 42,1–13(2017)
  10. 王,x,彭,y,陆,l,陆,z,Bagheri,m,Summers,R.M.: Chestx-ray8:医院级胸部 x 线数据库和常见胸部疾病的弱监督分类和定位基准。IEEE 计算机视觉和模式识别会议论文集。第 2097 至 2106 页(2017 年)
  11. Gibson,e .,Li,w .,Sudre,c .,Fidon,l .,Shakir,D.I .,Wang,g .,Eaton-Rosen,z .,Gray,r .,Doel,t .,Hu,y .,等人:Niftynet:一个用于医学成像的深度学习平台。生物医学中的计算机方法和程序 158,113–122(2018)
  12. 陈,s,马,k,郑,Y.: Med3d:用于三维医学图像分析的迁移学习。arXiv 预印本 arXiv:1904.00625 (2019)
  13. 卡雷拉,j .,齐塞曼,A.: Quo vadis,动作识别?一个新的模型和动力学数据集。IEEE 计算机视觉和模式识别会议论文集。第 6299 至 6308 页(2017 年)
  14. Pathak,d .、Krahenbuhl,p .、Donahue,j .、Darrell,t .、Efros,A.A .:上下文编码器:通过修补进行特征学习。IEEE 计算机视觉和模式识别会议论文集。第 2536 至 2544 页(2016 年)
  15. Chen,l .,Bentley,p .,Mori,k .,Misawa,k .,m .,Rueckert,d .:使用图像上下文恢复进行医学图像分析的自我监督学习。医学图像分析 58,101539 (2019)
  16. Armato III,S.G .,g .,Bidaut,l .,McNitt-Gray,M.F .,Meyer,C.R .,Reeves,A.P .,Zhao,b .,Aberle,D.R .,Henschke,C.I .,Hoffman,E.A .等人:肺部图像数据库联盟(lidc)和图像数据库资源倡议(idri):一个完整的 ct 扫描肺结节参考数据库。医学物理学 38(2),915–931(2011)
  17. Bilic,p .,Christ,P.F .,Vorontsov,e .,Chlebus,g .,Chen,h .,Dou,q .,Fu,C.W .,Han,x .,Heng,P.A .,Hesser,j .等人:肝肿瘤分割基准(lits)。arXiv 预印本 arXiv:1901.04056 (2019)
  18. Bakas,s .,Reyes,m .,Jakab,a .,Bauer,s .,Rempfler,m .,克里米,a .,蛯原姫奈,R.T .,Berger,c .,Ha,S.M .,Rozycki,m .,等人:在 brats 挑战中识别用于脑肿瘤分割、进展评估和总体存活率预测的最佳机器学习算法。arXiv 预印本 arXiv:1811.02629 (2018)
  19. Siim-acr 气胸分割(2019),https://www.kaggle.com/c/ siim-ACR 气胸分割/

通过 R 中的划痕编码使用非负矩阵分解学习面部特征

原文:https://towardsdatascience.com/learning-the-facial-features-using-non-negative-matrix-factorization-from-scratch-in-r-e0a82c836775

动手香草建模第四部分

作者图片

转向离你最近的窗户,试着凝视外面你的眼睛可能遇到的任何物体。现在问你自己一个问题,在识别物体的时候,你的大脑是把它看成一个整体,还是物体的某些部分或特征足以让你决定这个物体是什么?我们的大脑能够通过识别一个物体的各个部分来识别它,而不必看到整个物体,这是一个非常有趣的现象。当看到一朵抽象形状的云时,我们的大脑会自动将漂浮的形状与最相似的物体联系起来。心理学研究[ 1 ],生理学研究[ 2 ],以及计算机视觉的电子神经网络架构等计算研究[ 3 ]调查并支持大脑中基于部分的表示的想法。

一种这样的计算机视觉技术被称为非负矩阵分解(NMF ),它巧妙地学习对象的特征,并且可能是最简单和容易实现的算法。NMF 作为一种探索性因素分析于 1994 年由 Paatero 和 Tapper 提出,并于 1999 年由 Daniel D . Lee&H Sebastian Seung4进一步推广。它仍然是图像和文本挖掘的流行算法。NMF 的基本原理是基于非负约束的部分学习,这意味着 NMF 只有在感兴趣的数据矩阵没有负条目时才有用。

鼓励对实现和代码感兴趣的读者快速阅读下一部分,以便理解我将在数据准备和代码中使用的维度符号。还有,随意探索一下动手香草造型系列的 第一部分第二部分、 第三部分 。欢呼:)

了解 NMF

让我们直接开始理解 NMF 背后的数学。本质上,NMF 通过将数据分解成权重和因子来减少数据,使得原始矩阵和新形成的矩阵的乘积之间的距离(欧几里德或弗罗贝纽斯)最小。

V(mxn)为我们的数据矩阵。目标是将 V 分解为低维 kW(mxk)编码矩阵H(kxn)这样 H 的各列与 V 中的列一一对应。基本上,编码列是与基矩阵的线性组合产生 v 列的系数。**

作者图片

例如,当数据矩阵 V 的每一列表示面部图像时,基列是除了诸如眼睛、鼻子、嘴唇等特征之外什么也不是的基图像。,而 H 的列指示哪个特征以什么强度出现在哪个图像中。 k 维度表示数据的特征(部分)。通常选择 k 使得(m+n)kMn。把产品WxH称为数据 V 的压缩形式不会错。

潜在的 NMF 目标是相对于 WH 最小化 VW x H 之间的距离,同时保持 WH 的非负性。

非否定性约束与组合部分以形成整体的直观概念是相容的,这就是 NMF 学习基于部分的表征的方式。

这个最小化问题无法解析地解决,因为它是一个 NP-Hard 问题,这意味着它在 WH 中不是凸的。简而言之,找到全局最小值即使不是不可能,也是很困难的。对于标量情况可以观察到这种证明,其中 mn 是等于 1。那么目标就是:

这个目标函数的梯度是:

同样,黑森人会是:

这里,可以容易地观察到,对于所有的 wh的值,hessian 并不总是正半确定的

基于梯度下降的知识,朝向梯度的缓慢步骤应该引导我们到达目标函数的最小值。

这里α和β是学习率。原则上,这些学习率可以估计为:

从梯度矩阵中代入学习率和梯度,更新规则变成:

从针对 WH 的非负初始条件开始,针对非负 V 的这些更新规则的迭代通过收敛到目标函数的局部最小化来找到 VWH 的近似因式分解。

履行

为了学习面部特征,我们使用了 LFW(野生标记人脸)数据集,这是一个面部照片数据库,旨在研究无约束人脸识别问题。该数据集包含从网络上收集的 13,000 多张面部图像。

LFW 的拼贴画(作者)

NMF 的代码如下。W 和 H 矩阵由来自均匀分布的值初始化。默认情况下,迭代次数设置为 500,但是 50 次迭代足以达到可观的收敛。

**#---------- NMF ---------------NMF<-function(X, K, ite = 500, verbose){

  N=nrow(X); M=ncol(X)
  W<-matrix(runif(N*K), nrow = N)
  H<-matrix(runif(K*M), nrow = K)

  loss<-NULL#-------Update------------#  
  for(i in 1:ite){

    H_num<-t(W)%*%X
    H_deno<-t(W)%*%W%*%H + 1e-10
    H<-H*(H_num/H_deno)

    W_num<-X%*%t(H)
    W_deno<-W%*%H%*%t(H) + 1e-10
    W<-W*(W_num/W_deno)

    loss[i]<-sum((X-W%*%H)^2)

    if(i%%500==0 & verbose == T){print(paste("iteration----------",i, sep = " "))
      print(sum((X-W%*%H)^2))}

  }

  return(list(Basis = W, Encoding = H, loss = loss))
}**

这里,我们将只使用 500 张随机选择的图片。第一步,面部图像被缩放到 150×150 像素。然后,每个图像的像素被标准化,使得平均值和标准偏差等于 0.25。最后,图像像素被裁剪到范围[0,1]以允许非负性。

在步骤 2 中,每个图像像素然后被展平以成为列向量。将所有展平的图像组合成列,我们得到数据矩阵 v。

在步骤 3 中,用随机初始化的 WH 用上述迭代算法在 V 上执行 NMF。

工作流程(作者图片)

**setwd(".\\medium\\NMF\\lfw")
set.seed(123)
main.dir<-".\\medium\\NMF\\lfw"
my.list<-list(NULL)
index<-1
for(j in seq_along(dir())){ #reading all the image and storing into a list

  paste(main.dir,"\\",dir()[j], sep="")%>%setwd()

  for(i in seq_along(dir())){

    my.list[[index]]<-readImage(dir()[i])
    index<-index+1

  }
  setwd(main.dir)

}
#-------------------------Functions------------------------#
zscale<-function(x, u, sigma){ (x-mean(x, na.rm=T))*sigma/sd(x, na.rm = T) + u} #standardization function
minmax<-function(x){ (x-min(x, na.rm = T))/(max(x, na.rm = T)-min(x, na.rm = T)) } #min max function
#---------------------------------#
images=500 #number of images to be selected from the data base
pixel=150
sample.image<-sample(size = images, c(1:length(my.list)), replace = F)
my.new.list<-my.list[c(sample.image)]
#-------Step 1-------------------#
for(i in 1:images){my.new.list[[i]]<-c(channel(my.new.list[[i]], "grey")%>%resize(pixel, pixel))%>%zscale(u=0.25, sigma = 0.25)%>%minmax()}
#---------------Step 2--------------------------#
V<-unlist(my.new.list)%>%matrix(nrow = pixel*pixel, byrow = F)
#---------------Step 3------------------#
a<-NMF(X = V, K = 100, ite=1000, verbose = F)**

输出(图片由作者提供)

损失(按作者)

NMF 同时执行学习和推理。也就是说,它既学习一组基础图像,又从可见变量中推断隐藏变量的值。

局限性
虽然 NMF 在学习面部零件方面是成功的,但这并不意味着该方法可以从任何数据库中学习零件,例如从极其不同的角度观看的物体图像或高度铰接的物体。虽然非负约束可以帮助这种模型学习基于部分的表征,但 NMF 的发明者并不认为它们本身就足够了。此外,NMF 不知道各部分之间的“句法”关系。NMF 假设隐变量是非负的,但没有进一步假设它们的统计相关性。人们还必须认识到,一种可以在人们不知情的情况下远程识别或分类的技术从根本上来说是危险的。跟随这篇更好地理解面部识别或特征学习模型的伦理衍生。

我希望这篇文章能让你对 NMF 有所了解,作为一名读者,你会喜欢它的内容。从我的 GitHub 访问代码。留下来更多的手香草造型系列。干杯!

从零开始解释的逻辑回归(可视化、数学化、程序化)
通过 R 中的 Scratch 编码理解一个神经网络;新手指南
使用 Keras 建立你的第一个 R 图像分类模型的外行指南

资源

通过非负矩阵分解学习物体的部件

非负矩阵分解的原因和方法

学习金融编码

原文:https://towardsdatascience.com/learning-to-code-for-finance-85463ae6f67

为了有效地处理数据,您真正需要知道的是什么?

帕特里克·韦森伯格在 Unsplash 上拍摄的照片

金融行业使用大量数据,许多分析师不得不在工作中掌握技术技能。如果您从事这一领域的工作,您可能会继承几代人流传下来的维护不佳的 Excel 工作簿。每个人都知道有更好的方法来存储数据和维护模型,但我不认为大多数人意识到一点点编码可以持续多长时间。

我是零编程知识进入金融行业的,边干边学。这对我的职业满意度产生了非常积极的影响。我在旅途中收集了一些见解,它们可能对任何处于类似位置的人都有用。

应该学习哪些语言(以及应该跳过哪些语言)?

先说你应该跳过的语言。由于 Excel 在整个行业中仍被广泛使用,学习 VBA 很有诱惑力。在一些有限的情况下,VBA 可能是正确的工具,但它是一种令人沮丧的语言。此外,它使您依赖于 Excel。Matlab 是另一种在行业中被广泛使用的语言,但是我认为现在有更好的选择。

我相信对于大多数投资行业的分析师来说,你应该学习两种语言:SQL 和 Python 或者 R 中的一种(既懂 Python 又懂 R 肯定有一些好处)。

对于分析师来说,SQL 可以说是最重要的语言,但是它经常被忽略。SQL 肯定没有 Python 性感,但是有很多好的理由让它成为你的第一语言。您将学习数据建模的基础知识,以及如何执行所有常见的转换。这将使你在钻研 Python 或 r 时,像 Pandas 或 dplyr 这样的学习库更加直观。

根据我的经验,许多投资团队都忽视了数据管理。太多的团队依赖 Excel 工作簿来存储数据,这就产生了很多问题。如果您能了解一点数据库和 SQL,您就可以开始从 Excel 迁移到更安全、更可伸缩的解决方案。

SQL 很棒,但是当涉及到更复杂的统计、机器学习和自动化时,R 和 Python 确实大放异彩。您还会发现两种语言都有很棒的数据可视化库。

不要太纠结于在 Python 和 R 之间做选择,因为这对大多数人来说真的无关紧要。对于统计学和机器学习,你可能会喜欢这两种语言,但我经常觉得 R 有更多面向金融专业人士的包。然而,当涉及到彭博、FactSet 和晨星等金融数据提供商的产品时,Python 似乎是首选语言。

我认为这两种语言有相似的学习曲线,但我认为大多数人会更快地熟悉 r。然而,如果你的目标是真正深入编码,你肯定应该选择 Python。Python 是一种通用编程语言,比 r 有更广泛的用例集。

学习 Python 与学习如何使用 Python 做事

不是每个人都有相同的目标或需求,弄清楚自己想要什么很重要。一些人沉迷于编码,而另一些人只想学习如何执行一些统计测试。这里没有正确或错误的答案,但你必须考虑你想从你的职业生涯中得到什么。

经常听说 Python 和 R 是容易学习的编程语言,但是要精通它们仍然需要大量的时间和精力。要成为定量分析师,你肯定需要一些扎实的编程技能,但是大多数分析师可能不需要渴望达到接近专家水平的水平。

对于一些人来说,仅仅学习如何用编程语言做一些事情就足够了。大多数分析师将从学习 R 或 Python 中的基本数据争论、统计和数据可视化中获得很多价值。这些技能将会给你一个坚实的工具集,开始执行分析,自动化任务,并取代 Excel。如果您的目标是能够与其他 Python/R 用户协作,并且您主要是构建一些 POC,那么这一点尤其正确。

学习一门语言的一点一滴肯定有不好的一面,你可能会碰壁。我经常看到分析师从使用 Python/R 构建更复杂的模型开始。当数据需要特定的转换或者数据需要转换成不同的类型时,它们就开始碰壁了。另一个常见的问题是当一个分析师建立一个模型,并且需要为大量的资产运行它。如果分析师没有学到控制流的基础知识,他们可能会感到很吃力。这些主题并不特别具有挑战性,但是如果你不熟悉这些概念,它们会让你陷入困境。

我认为获得相对小众技能的正确途径是这样的:

  1. 学习基础知识:数据类型、基本数据结构、控制流等。
  2. 数据分析学习包:熊猫还是 dplyr
  3. 学习可视化软件包:ggplot2,Plotly,Matplotlib
  4. 统计分析学习包
  5. 根据您在金融行业的需求量身定制的学习包。对我帮助很大的一些软件包是 PerformanceAnalytics、PortflioAnalytics 和 PyPortfolioOpt。

了解如何使用 API

当您的工作是处理数据时,您需要能够首先接触到数据。幸运的是,有一个丰富的金融 API 生态系统可以帮助你。很多 API 都有 R 和 Python 客户端,这使得提取数据变得很容易,但是学习如何从 REST 和 WebSocket APIs 消费数据会让你受益匪浅。一些对我帮助很大的 API 是用于价格数据的 Alpha Vantage 和用于经济数据的 FRED。

使用版本控制

不要将脚本存储在本地或共享驱动器中。删除或编辑错误的脚本很容易,而恢复以前的版本却很困难。更好的选择是开始使用 git。Git 之所以酷,是因为它强制执行了一个进行更改的过程,并记录了一段时间内的所有更改。现在,如果您的脚本开始出错或产生不好的结果,您可以查看日志,看看发生了什么变化。

不要跳过文档

当你在工作中学习并试图保持领先时,很容易跳过文档。这不一定是为了你,而是为了那些想要使用你的脚本的人。没有适当的文档,你的队友要么会忽视你的工作,要么会不断寻求帮助。此外,在你的职业生涯中,你可能会更换团队,良好的文档记录有助于你离职后的连续性。

不要成为当地的 IT 人

当你开始解决技术问题时,一旦出现问题,你很容易成为关键人物。帮助队友解封显然是好的,但不要做那些无聊的事情,这样别人就不必把自己的时间投入到学习中。

结论

这概述了我在学习如何在投资行业编码时收集的一些见解。希望对你有帮助。

学习排序:使用机器学习的排序完全指南

原文:https://towardsdatascience.com/learning-to-rank-a-complete-guide-to-ranking-using-machine-learning-4c9688d370d4

照片由 Unsplash 上的尼克·费因斯拍摄

排名:什么和为什么?

在这篇文章中,通过“排名,我们指的是通过相关性对文档进行排序,以找到与查询相关的感兴趣的内容。这是 信息检索 的一个基本问题,但这个任务在许多其他应用中也会出现:

  1. 搜索引擎 —给定一个用户简介(位置、年龄、性别……)一个文本查询,按相关性对网页结果进行排序。
  2. 推荐系统——给定一个用户简档和购买历史,对其他项目进行排序,为用户找到新的潜在感兴趣的产品。
  3. 旅行社 —给定一个用户配置文件和过滤器(入住/退房日期、旅行者的数量和年龄等),根据相关性对可用房间进行排序。

排名应用:1)搜索引擎;2)推荐系统;3)旅行社。(图片由作者提供)

排名模型通常通过为每个输入预测一个相关性分数s = f(x)x=(q,d ) 来工作,其中 q 是一个 查询并且 d 是一个文档一旦我们有了每个文档的相关性,我们就可以根据这些分数对文档进行排序(即排名)。

排名模型依赖于评分函数。(图片由作者提供)

可以使用各种方法实现评分模型。

  • 向量空间模型——为每个查询和文档计算一个向量嵌入(例如使用 Tf-IdfBERT ,然后计算相关性得分f(x)= f(q,d )** 作为 q 的向量嵌入之间的余弦相似度
  • 学习排名–评分模型是一种机器学习模型,它在某种排名损失最小化的训练阶段,在给定输入×=(q,d* )*** 的情况下,学习预测得分 s 。**

在本文中,我们关注后一种方法,我们展示了如何实现用于学习排名的机器学习模型。****

评估指标排名

在分析学习排名的各种 ML 模型之前,我们需要定义使用哪些度量来评估排名模型。这些度量是对排名的预测文档进行计算的,即第 k- 个最高检索文档是具有最高预测分数 s 的第 k 个文档。****

平均精度

地图-平均精度。(图片由作者提供)

平均精度用于二元相关的任务,即当一个文档 d 的真实分数 y 只能是 0 ( 不相关或 1 ( 相关 ) 时。

对于给定的查询 q 和相应的文档d=t5】{d*₁、…, dₙ },我们检查前 k 个检索到的文档中有多少是相关的( y =1)或不相关的( y= 0)。为了计算精度** P 召回 R 。对于 k = 1… n ,我们得到不同的 P 和* R 值,它们定义了精度-召回曲线:这条曲线下的面积就是平均精度(AP) 。**

最后,通过计算一组 m 查询的 AP 值的平均值,我们获得了平均精度(MAP)** 。**

贴现累计收益(DCG)

DCG——贴现累积收益。(图片由作者提供)

折扣累积收益用于分级相关的任务,即当文档 d 的真实得分 y 是衡量查询 q 相关度的尺度中的离散值时。一个典型的尺度是 0 ( )、1 ( 一般)、2 ( )、3 ( 优秀)、4 ( 完美 )

对于一个给定的查询 q 和相应的文档d={d₁、…、 dₙ },我们考虑第 k 个顶部检索到的文档。增益 gₖ=2^yₖ*–1 衡量这个文档有多有用(我们想要相关性高的文档!),而折扣dₖ=1/log(k+1)惩罚以较低等级检索的文档(我们希望相关文档在最高等级!).*

贴现收益项 gdfork =1…n之和就是贴现累计收益(DCG)* 。为了确保这个分数在 0 和 1 之间,我们可以用测量的 DCG 除以理想的分数 IDCG,如果我们用真实值 yₖ 对文档进行排序,就会得到理想的分数 idcg。这给了我们归一化贴现累积收益(NDCG) ,其中 NDCG = DCG/IDCG。***

最后,对于 MAP,我们通常计算一组 m 查询的 DCG 或 NDCG 值的平均值来获得一个平均值。

用于学习排序的机器学习模型

为了建立一个用于排名的机器学习模型,我们需要定义输入输出损失函数**

  • 输入–对于一个查询 q 我们有 n 个文档d={d**₁、…、d}按相关性进行排序。元素=(qdᵢ )** 是我们模型的输入。****
  • 输出–对于一个查询文档输入 xᵢ = ( qdᵢ ),我们假设存在一个真实的相关性得分yᵢt51】。我们的模型输出一个预测分数*sᵢ= f(xᵢ)。*******

所有的学习排序模型都使用一个基本的机器学习模型(例如决策树神经网络)来计算s=f(x)。选择损失函数是学习排列模型的独特元素。一般来说,我们有 3 种方法,这取决于损失是如何计算的。****

  1. 逐点法——总损失计算为各文件上定义的损失项之和dᵢt81】(因此t83】逐点 t85)作为预测得分 sᵢ 和地面真实值 yᵢ ,对于 i= 1 通过这样做,我们将我们的任务转化为一个回归问题,其中我们训练一个模型来预测 y.******
  2. 成对方法——总损失计算为每对单据上定义的损失条款之和 dᵢ、 (因此 成对 ),对于 i,j= 1… n 。训练模型的目的是预测 yᵢ > yⱼ 是否相关,即两个文档中哪一个更相关。通过这样做,我们将我们的任务转化为一个二元分类问题
  3. ****列表方式——直接在整个文档列表上计算损失(因此 列表方式 ),具有相应的预测等级。这样,排名指标可以更直接地纳入损失。

学习排序的机器学习方法:逐点,成对,列表。(图片由作者提供)

逐点方法

逐点法是最容易实现的方法,也是第一个被提出来学习任务排序的方法。损失直接测量地面真实分数和预测的【sᵢ】之间的距离,因此我们通过有效地解决回归问题来处理这个任务。例如, 子集排序 使用了均方误差(MSE) 损失。

子集排序中逐点方法的 MSE 损失。(图片由作者提供)

成对方法

逐点模型的主要问题是需要真实的相关性分数来训练模型。但是在许多场景中,训练数据仅可用于的部分信息,例如,我们仅知道用户选择了文档列表中的哪个文档(因此更相关),但是我们不知道这些文档中的与*有多相关!*****

由于这个原因,成对方法不能与绝对相关性一起工作。相反,他们使用相对偏好:给定两个文档,我们想要预测第一个文档是否比第二个更相关。这样,我们解决了一个二元分类任务,其中我们只需要基本事实 yᵢⱼ ( = 1 如果 yᵢ > yⱼ ,否则为 0),并且我们使用逻辑函数:sᵢⱼ=σ(sᵢ–sⱼ)将模型输出映射到概率。这种方法最早由 RankNet 使用,其中使用了二进制交叉熵【BCE】损失。

RankNet 中成对方法的 BCE 损失。(图片由作者提供)

RankNet 是对逐点方法的改进,但在训练期间所有文档仍然被赋予相同的重要性,而我们希望给予排名更高的文档更多的重要性(正如 DCG 度量对折扣条款所做的那样)。

不幸的是,排序信息只有在排序后才可用,而且排序是不可微的。然而,要运行梯度下降优化,我们不需要损失函数,我们只需要它的梯度!λrank定义了隐式损失函数的梯度,使得等级高的文档具有大得多的梯度:

如 LambdaRank 中的隐式损失函数的梯度。(图片由作者提供)

拥有渐变也足以构建一个渐变提升模型。这就是λmart使用的思想,产生了比用 than 更好的结果。****

列表式方法

逐点和成对方法将排序问题转化为代理回归或分类任务。相反,列表式方法通过最大化评估指标更直接地解决问题

直观上,这种方法应该给出最好的结果,因为关于排名的信息被充分利用并且 NDCG 被直接优化。但是设置loss = 1–ndcg的一个明显问题是,计算折扣 Dₖ所需的排名信息只有在根据预测分数对文档进行排序后才可用,而排序 是不可微的。我们如何解决这个问题?

第一种方法是使用迭代方法,其中排名度量用于在每次迭代中重新加权实例。这是由λrankλmart使用的方法,它们确实介于成对方法和列表方法之间。

第二种方法是逼近目标使其可微,这是 SoftRank 背后的思想。不是预测一个确定性的分数s=f(x),而是预测一个平滑的概率分数s ~𝒩(f(x*), σ )。排名 k* 是预测得分 s 的非连续函数,但是由于平滑化,我们可以计算每个文档排名的概率分布。最后,我们优化 SoftNDCG ,这是一个平滑函数,它是这个秩分布的期望 NDCG。******

分数的不确定性导致了 SoftRank 的平滑损失。(图片由作者提供)

第三种方法被认为是每个排序列表对应于一个排列,并定义排列空间上的损失。在 ListNet 中,给定一个分数列表 s 我们使用 Plackett-Luce 模型 定义任何排列的概率。然后,我们的损失很容易计算为置换空间上真实和预测的概率分布之间的二元交叉熵距离

ListNet 中 Plackett-Luce 模型各种排列的概率。(图片由作者提供)

最后,lambda loss论文对这个问题引入了一个新的视角,并创建了一个广义框架来定义新的列表式损失函数并实现最先进的精度。其主要思想是以一种严格和通用的方式将问题框架化,作为一个 混合模型 ,其中排序列表 π 被视为一个隐藏变量。然后,损失被定义为该模型的负对数似然。

loss 损失函数。(图片由作者提供)

LambdaLoss 框架的作者证明了两个基本结果。

  1. 所有其他列表式方法(RankNet、LambdaRank、SoftRank、ListNet、…)都是这个通用框架的特殊配置。的确,他们的损失是通过准确选择似然** p ( y | s,π )排序列表分布 p ( π | s ) 得到的。******
  2. 这个框架允许我们定义度量驱动的损失函数,直接连接到我们想要优化的排名度量。这有助于显著提高任务排序的学习水平。****

结论

从信息检索到推荐系统和旅游预订,排名问题无处不在。像 MAP 和 NDCG 这样的评估指标同时考虑了检索文档的排名和相关性,因此很难直接优化。

学习排序方法使用机器学习模型来预测文档的相关性分数,并被分为 3 类:点方式、成对方式、列表方式。在大多数排序问题上,列表式方法如 LambdaRank 和广义框架 LambdaLoss 达到了最先进的水平。

参考

学习排名:入门

原文:https://towardsdatascience.com/learning-to-rank-a-primer-40d2ff9960af

为搜索引擎、推荐系统、社交媒体和广告提供动力的算法

Unsplash 上由 tatonomusic 拍摄的照片

每当你在谷歌上搜索一个页面或亚马逊上的一个产品,在网飞上选择一部电影,滚动浏览你的脸书或 Twitter feed,或者对作为内容一部分显示的广告感到恼火,你都在查看一个排名算法的结果,该算法决定你正在展示的印象是最有可能与你互动的。

事实上,排名算法是当今世界最普遍的 ML 应用之一。它们是如何工作的?在这次深入探讨中,我们将了解一些驱动搜索引擎、推荐系统、提要和广告选择的算法。让我们开始吧。

搜索引擎

搜索引擎的目标是向用户显示与给定搜索查询最相关的文档(谷歌的页面,亚马逊的产品)。搜索中最大的挑战之一是搜索空间的大小,可能有数十亿个文档。一个算法如何在如此广阔的空间中搜索?

搜索引擎通常使用一个漏斗架构,它由两个复杂性不断增加的阶段组成:第一阶段是一个针对召回进行优化的简单模型(确保我们捕获所有相关内容),第二阶段是一个针对精确度进行优化的更复杂的模型(确保我们首先显示最相关的内容)。第一阶段可以是一个简单的倒排索引,它为每个术语存储该术语出现的所有文档的索引。这个阶段可以将文档总数从 10B 减少到大约 100K,这更易于管理。然后,第二阶段可以是更强大的 ML 模型,例如提升树的集合或双塔神经网络(更多内容见下文),它根据点击概率对文档进行排序。

这些模特是怎么训练出来的?通常有两种不同的方法,逐点和成对。在逐点方法中,一般来说,每个有点击的历史印象都是正面训练样本,每个没有点击的印象都是负面训练样本。在成对方法中,一个训练示例是一对文档,模型学习预测这两个文档的相对排名。注意,成对排序本质上仍然是二进制分类问题:如果文档 A 的排序高于文档 B,则标签为 1,否则为 0。

我们可以用什么特征来训练搜索引擎?特征有几个来源,并且在实践中,排名模型可能消耗数百甚至数千个特征,例如:

  • 文档相关的特征,例如词袋/ TF-IDF 特征或低维嵌入,
  • 文档元数据,例如在电子商务搜索的情况下的品牌、制造商或平均产品评级,
  • 与查询相关的特征,例如词袋特征或嵌入(类似于与文档相关的特征),
  • 相关性特征,即测量文档与查询相关程度的特征,例如通过计算共享嵌入空间中的距离,
  • 与用户相关的特征,例如用户年龄、性别、位置、总结历史用户行为的特征,以及
  • 行为特征,例如某种产品在过去 24 小时内获得了多少参与度。

这些特征还取决于特定的搜索领域:例如, Sorokina 等人强调了行为特征在亚马逊产品排名中的重要性:

在产品搜索中,数百种产品可能共享非常相似的描述,并且似乎与特定查询同样相关。但其中一些产品比其他产品更受欢迎,应该排名更高。这就是为什么行为特征在亚马逊搜索中比在网络搜索中更大程度地推动了排名。通常,它们是梯度增强树中方差减少的主要原因。

我们如何衡量一个搜索引擎的性能?概括地说,我们区分了离线指标(从历史数据中获得)和在线指标(从实时用户流量中获得)。一个好的离线指标是 NDGC@k,或显示的前 k 个搜索结果中的归一化折扣累积收益。如果显示的所有搜索结果真正相关,并且它们也以正确的方式排序,即如果排名较高的搜索结果比排名较低的搜索结果更相关,则 NDCG 最高(即 1)。一个好的在线指标是点击率,

CTR =(点击次数)/(展示次数)。

如果对于给定的查询,在 SERP(搜索引擎结果页面)中排名最高的文档确实具有最高的 CTR,则搜索引擎工作良好。换句话说,如果排名最高的文档的点击率低,这将表明搜索引擎有问题。

离线和在线度量在实践中都很重要。离线指标很容易获得,可以用来快速排除假设。然而,实践者发现,离线指标的改进并不总是转化为实时系统的改进(例如,见 Yi 等人 2013 ),因此,通过 A/B 测试在线估计改进是生产 ML 模型的关键步骤。

基于推荐和馈送的系统

根据我们对用户的了解,推荐系统是推荐下一部要看的电影(网飞)、下一件要购买的产品(亚马逊)或下一个要添加的朋友(脸书)的算法。为了做到这一点,我们可以利用来自用户的显性或隐性反馈。在网飞的例子中,显式反馈是用户对电影的明确评价,而隐式反馈是用户观看了哪些电影。

通过隐式反馈,我们可以访问更大的标记数据池,这仅仅是因为典型用户观看的电影比他们评价的多得多。

我们如何衡量我们的推荐有多好?如果我们有明确的反馈,如星级,我们可以简单地计算一个指标,如 RMSE。如果我们只有隐式反馈,我们可以计算 N 个推荐的平均精度,其中“平均值”是对等级取的,“平均值”是对用户取的。用一个例子最容易理解 MAP@N:假设在推荐的前 5 部电影中,用户看了 1、2、3、5 部,而不是 4 部。那么我们的 AP@5 就会是(1/1 + 2/2 + 3/3 + 3/4 + 4/5)/5 = 0.91,相当不错,但不完美。对于 MAP@5,我们简单地计算所有用户的平均值。

推荐系统背后最常用的一类算法是协同过滤:对于一个给定的用户,我们构建一个二进制向量,其中每个条目对应一部特定的电影,如果用户观看了该电影,则该向量为 1,否则为 0。现在我们对每个用户都有了一个数字表示,可以衡量他们有多相似。为了构建推荐,我们可以在用户列表中选择最相似的用户最常观看的未观看的电影。

协同过滤在计算上非常昂贵。让我们假设网飞有 2 亿用户和 10K 电影:然后,我们需要在 10K 维度的空间中找出 200 米内最近的邻居!降低问题计算复杂度的一种方法是矩阵分解,其中我们将维数为 N_user x N_movie 的用户/电影矩阵 X 分解为 2 个更小的矩阵,X = U x M,其中 U 具有 shape N_user x k,M 具有 shape k x N_movie。这里,k 是潜在因素的数量。在最简单的情况下,k=1,U 简单地编码每个用户总体上喜欢电影的程度,M 编码每个电影总体上被喜欢的程度。通过增加 k,我们可以增加潜在空间的细微差别,如流派(电影)或风格(服装)。

然而,矩阵分解只是一个线性模型,我们可以通过深度神经网络获得更好的性能,深度神经网络可以学习偏好表示的层次,类似于在图像上训练的神经网络如何学习视觉潜在表示的层次(例如,边缘、眼睛、脸、人)。推荐器中使用的网络架构通常是一个双塔结构,我们已经在上面介绍过,其中一个塔接收稀疏用户表示,另一个塔接收稀疏电影表示:

用于推荐的双塔神经网络。图片鸣谢:何等 2017

双塔神经网络的关键在于用户和项目的表示在模型训练期间被共同学习,这将导致共享的嵌入空间。在这样的共享嵌入空间中,我们可以简单地通过最近邻算法为用户获得新的推荐。

推荐系统中使用了哪些功能?在“纯”协同过滤系统中,我们只考虑历史用户/项目交互矩阵。此外,我们可以添加基于用户的功能,如年龄、性别或地理位置,以及基于电影的功能,如导演、演员、标题、情节、运行时间、原籍国或语言。这两个集合也形成了强大的组合特征,例如在用户的地理位置中所说的语言是否与电影语言相匹配。对于 YouTube 推荐,内容会迅速传播,观看时视频的年龄是另一个非常强大的功能( Covington et al 2017 )。

基于订阅源的系统,如脸书、Twitter、LinkedIn 或谷歌新闻,类似于推荐系统,但用户有多种方式与显示的内容进行交互,如喜欢/反应、分享/转发、评论、直接发送消息、发送好友请求、报告等,而不仅仅是一种类型的交互(观看)。这为排名模型提供了额外的设计考虑。例如,我们可以构建一个预测整体参与度的排名器,或者我们可以构建单独的排名器,比如为每种参与度类型(喜欢、分享、评论等)构建一个排名器。后一种方法在优化下游模型时会给我们更多的灵活性,例如,当我们希望将创建分享或评论的内容优先于仅创建喜欢的内容时。

Ads 预测系统

广告显示在搜索引擎结果中(如谷歌、亚马逊),或显示在基于订阅源的系统中(如推特、脸书)。

一般来说,广告排名的工作方式类似于搜索引擎和推荐系统,但有一个关键的区别:排名功能,它将模型得分映射到排名。

在搜索结果中,我们希望通过点击概率 p 对显示的内容进行排序:点击概率越高,我们希望显示的内容就越高。另一方面,在广告排名中,我们通过(p x b^(alpha)进行排名,其中 b 是来自广告商的出价,即当广告被点击时他们向平台支付的钱,alpha 是一个自由参数;请注意,搜索排名和广告排名在 alpha → 0 的限制下是相同的。这种基于出价的排名的原因是广告的目标是总收入,而不是用户参与度:例如,如果耐克为 p=0.009 的广告出价 1 美分,但阿迪达斯为 p=0.007 的广告出价 5 美分,我们仍然更喜欢首先放置阿迪达斯的广告,即使点击概率较低,因为它将创造更多的收入。

由于模型得分和收入之间的紧密关系,广告排名中的模型得分经过校准至关重要,例如,0.009 的得分实际上意味着 9/1000 的用户点击了该广告。这种行为是不能保证的,深度神经网络特别容易出现误校准(参见例如郭等 2017 )。如果模型校准错误,那么最终排名可能不理想,我们可能会将收入留在桌面上。

我们如何训练一个广告预测模型?正如在搜索引擎和推荐系统中一样,我们可以从在线用户行为中生成训练数据:没有点击的印象是负面的,而点击是正面的。通常,点击率很小,可能只有 1%或更少,因此我们可能希望对负面数据进行下采样,以便更快地进行模型训练。在这种情况下,我们可以通过更新来重新校准模型的输出分数,以校正已完成的重新采样

p ← p/(p+(1-p)/w),

其中 w 是下采样速率。

广告中的另一个挑战是,人类很容易被操纵,经常改变主意,因此,今天表现良好的广告明天可能会表现不佳:这造成了概念漂移。由于概念漂移,我们希望尽可能频繁地更新模型,每天或者甚至每小时。另一个好方法是在线学习,系统随着每一批新用户的点击而更新。使用逻辑回归可以最容易地进行在线学习,因为需要更新的参数数量很少。然而,logreg 模型不利用特征交互。因此,一个强大的想法是一种“混合”在线建模方法,其中我们使用树集成或深度神经网络作为特征生成器,并且仍然使用 logreg 模型作为这些生成的特征之上的最终分类层(参见何等人 2014 )。

图片来源:作者个人收藏

结论

让我们回顾一下:

  • 搜索引擎中,我们构建了一个模型来预测给定用户搜索查询的最相关文档,该模型利用了从文档和查询中导出的特征;
  • 推荐系统feeds 中,没有明确的用户查询,而是基于用户过去做了什么以及相似用户参与了什么内容,我们预测用户最有可能与之交互的内容;
  • 最后,广告预测系统的工作方式类似于搜索引擎和推荐系统,重要的区别是我们根据点击概率 x 出价对广告进行排名,而不是只根据点击概率,因为目标是实现收入最大化。

最后,搜索引擎、推荐系统和广告预测系统之间的界限可能是模糊的。例如,假设你在亚马逊上搜索一个特定品牌的产品,但没有看到一个空的结果页面,而是显示了一个类似的备选列表:这是一个搜索,但也是一种推荐

https://medium.com/@samuel.flender/membership

学习产品推荐排名

原文:https://towardsdatascience.com/learning-to-rank-for-product-recommendations-a113221ad8a7

本文将介绍如何使用流行的 XGBoost 库来解决学习排序(LTR)问题

MalvestidaUnsplash 上拍摄的照片

LTR 最常见的用例是搜索引擎和推荐系统。排名的最终目的是按照有意义的顺序排列项目。
本文将使用流行的 XGBoost 库进行电影推荐。

开始做 LTR 的时候,我的第一个问题是,传统的机器学习和排名问题有什么区别?这就是我的发现。在传统的机器学习问题中,每个实例都有一个目标类或值。例如,如果您正在处理一个客户流失预测问题,那么您拥有每个客户和相关类别的特性集。同样,我们的输出将是一个客户 id 和预测类或概率分数。但是在 LTR 中,我们没有针对每个实例的单一类或值。相反,我们有多个项目和它们每个实例的基本真值,我们的输出将是这些项目的最优排序。例如,如果我们有用户过去与项目的交互,我们的目标是建立一个能够预测最佳用户-项目对的模型。

现在是时候进入编码部分了。为简单起见,我将使用 movielens 小型数据集。您可以使用下面的链接下载数据集。

https://grouplens.org/datasets/movielens/latest/

让我们加载数据集,并对数据集进行基本的预处理。

在这个数据集中,我们有 100,000 个评级和 3,600 个标签应用程序,由 600 个用户应用于 9,000 部电影。

让我们快速看一下评级栏。

按值查看计数

按日名称查看计数

按接收小时数查看计数

看了上面的图之后,我为建模添加了一个基于时间、基于天的特性。因此,我将创建用户级和项目级功能。例如,对于某部电影“X ”,我得到了与之互动的用户总数,收到了 5、4、3、2 和 1 星的评论。此外,我正在添加每天收到的评论和下午 5 点后收到的评论。

让我们将数据集分成训练集和测试集。我会用过去作为训练,最新的数据会用来评估模型。

现在是时候创建模型输入了。由于排名模型不同于传统的监督模型,我们必须向模型中输入额外的信息。现在是创建模型的时候了。我们将使用 xgboost,XGBRanker。让我们把重点放在它的上。配合方法。下面是 XGBRanker()的 docstring。适合()。

签名:model.fit(X,y,group,sample_weight=None,eval_set=None,sample_weight_eval_set=None,eval_group=None,eval_metric=None,early_stopping_rounds=None,verbose=False,xgb_model=None,callbacks=None)
Docstring:拟合梯度增强模型

因素

X : array_like 特征矩阵
y : array_like 标签
group : array_like 训练数据的组大小sample _ weight:array _ like 组权重
..注意:权重是针对分级任务中的每个组的,一个权重被分配给每个组(而不是每个数据点)。这是因为我们只关心每个组内数据点的相对顺序,所以给单个数据点分配权重没有意义。

根据下面的文档字符串,我们必须为训练和测试样本输入组。所以问题是如何在排名模型中创建组数组。我看到很多人很难理解这个群参数。

简而言之,group 参数表示每个用户的交互次数。根据下面的片段,你可以看到一号用户与两个项目(11 和 12)进行了交互。因此,用户 1 的组大小为 2。此外,组长度应该等于数据集中唯一用户的数量,组大小的总和应该等于数据集中记录的总数。在下面的例子中 参数是【2,1,4】

让我们创建模型输入。我们可以使用下面的代码。

现在,我们将训练和测试输入输入到模型中。是时候对模型进行训练和评估了。在此之前,为了文章的完整性,我需要解释几个术语。

建立模型时,衡量预测的质量至关重要。评估推荐模型的可用度量有哪些?很少,但最常用的度量是归一化贴现累积增益(NDCG)平均精度(MAP) 。在这里,我将使用 NDCG 作为评估指标。NDCG 是 CG(累积增益)的增强版。在 CG 中,推荐一个订单并不重要。如果您的结果包含任何顺序的相关项目,这将给您更高的值,表明我们的预测是好的。但在现实世界中,情况并非如此。我们在推荐时应该优先考虑相关项目。为了实现这一点,当低相关性项目在结果中较早出现时,我们应该进行惩罚。这就是 DCG 的做法。但是,当不同的用户有不同的项目/交互计数时,DCG 仍然会受到影响。这就是归一化贴现累计收益(NDCG) 发挥作用的地方。它将使 DCG 度量标准正常化。

现在我们可以移动到模型部分。

现在我们可以做出一些预测。

以下是一些生成的预测。

评估你的推荐模型的覆盖面总是好的。使用覆盖率度量,您可以检查测试集中培训产品的百分比。覆盖面越广,模型越好。在某些情况下,模型试图预测受欢迎的商家,以最大化 NDCG 和 MAP@k。我在处理 starpoints 产品推荐时遇到了这个问题。当我们对我们的评估标准有疑问时,我们可以快速检查我们模型的覆盖率。在这个模型中,我得到了大约 2%的覆盖率。表明我们的模型应该进一步改进。

此外,我们可以如下绘制特征重要性。

特性对 XGBRanker 的重要性

结论

在本文中,我们介绍了学习排序问题的基础知识,如何对排序问题建模,以及一些与评估推荐模型相关的技巧。虽然本文展示了如何使用 xgboost 解决产品排名问题,但是我们也可以使用这种方法解决其他排名问题。

参考文献

麦克斯韦·哈珀和约瑟夫·康斯坦。2015.电影镜头数据集:历史和背景。美国计算机学会交互式智能系统汇刊 5,4:19:1–19:19。https://doi.org/10.1145/2827872

感谢阅读。您可以使用链接访问 colab 笔记本。

LinkedIn 上和我联系。

学习贝叶斯方法排序

原文:https://towardsdatascience.com/learning-to-rank-the-bayesian-way-29af4c61939b

贝叶斯统计

在 PyMC 中实施布拉德利-特里模型

彼得罗·马蒂亚Unsplash 上拍摄的照片

想象一下,一群玩家在某个游戏中一对一地竞争。于是,一个自然的问题出现了:

如何给选手排名?

理论上,这个问题应该不会太难——让他们玩一堆游戏,检查玩家的胜率。不幸的是,这种自然的方法有一些问题:

  • 你无法判断接近 100% 的胜率是否意味着该玩家异常,或者该玩家是否只是踩死了弱小的对手,以及
  • 如果一个玩家只玩了几局,那么对这个玩家实力的估计应该带有高不确定性,而这是一个原始的胜率无法提供的

在许多地方,您可能会遇到类似一对一游戏的问题:

  • 实际比赛中对玩家进行排名时:网球、赛车、纸牌游戏、神奇宝贝战斗……
  • 对搜索结果 : 排序时,如果一个搜索结果与用户的查询更相关,则该搜索结果优于另一个。
  • 排列推荐 : 一个推荐比另一个更好,如果它与用户可能想要购买的东西更相关的话

在本文中,我将向您展示如何在 PyMC 中构建一个简单的贝叶斯模型来解决这个问题。如果你不知道我在说什么,可以看看我对使用 PyMC3 的贝叶斯世界的介绍,PyMC 的前身具有几乎相同的语法。

布拉德利-特里模型

我们将使用的模型被称为布拉德利-特里模型,我们给它一个贝叶斯扭曲。这听起来很吓人,但实际上相当容易。只是跟着走。

模型直觉

整个模型归结为只有两个假设:

  1. 我们假设每个玩家(人类,口袋妖怪,搜索结果,推荐,…)都有一些实力(技能,相关性,…)。
  2. 如果拥有实力的玩家 1s₁和拥有实力的玩家 2s₂竞争,那么玩家 1 以的概率赢得

图片由作者提供。

其中 σ 就是你的老朋友,sigmoid 函数。就是这样。

请注意,我们在这里没有使用任何玩家特征,例如,如果玩家是真实的人类,身高或体重之类的特征。这意味着我们可以将这个模型应用于各种问题。

然而,如果我们碰巧有播放器特性,我们也可以将它们合并到模型中,最终得到类似于微软的 RankNet [1]的东西。作者使用神经网络架构从特征 x显式构建玩家的优势s=f(x),而我们在贝叶斯方法中直接将优势 s 视为参数

对于对神经网络感兴趣的人来说:我们可以使用嵌入层在你选择的深度学习框架中建立布拉德利-特里模型的频率主义版本。

让我们做一个小的理智检查,看看这个定义是否有意义。好吧,每个玩家都有自己的优势。这正是我们需要的,如果我们想根据球员的实力来排序,所以这很棒。

如果我们现在假设 1 号玩家的实力远高于 2 号玩家,即 s₁ — s₂ 是一个很大的数,这就暗示着σ(s₁—s₂)接近于 1。所以玩家 1 以压倒性的概率获胜,这正是我们在这种情况下想要的。如果参与人 1 的实力远低于参与人 2,同样的道理也适用。如果两位选手实力相当,那么他们各自获胜的概率是σ(0)=50%。完美!

创建数据集

在我们进入建模部分之前,让我们创建一个由游戏结果组成的人工数据集,如下所示:

图片由作者提供。

这样做的好处是,我们知道模型应该能够找到哪些属性。

若要创建此数据集,可以使用以下代码:

*import pandas as pd
import numpy as np

np.random.seed(0)

def determine_winner(player_1, player_2):
    if player_1 == 0 and player_2 == 1:
        return np.random.binomial(n=1, p=0.05)
    if player_1 == 0 and player_2 == 2:
        return np.random.binomial(n=1, p=0.05)
    if player_1 == 1 and player_2 == 0:
        return np.random.binomial(n=1, p=0.9)
    if player_1 == 1 and player_2 == 2:
        return np.random.binomial(n=1, p=0.1)
    if player_1 == 2 and player_2 == 0:
        return np.random.binomial(n=1, p=0.9)
    if player_1 == 2 and player_2 == 1:
        return np.random.binomial(n=1, p=0.85)

games = pd.DataFrame({
    "Player 1": np.random.randint(0, 3, size=1000),
    "Player 2": np.random.randint(0, 3, size=1000)
}).query("`Player 1` != `Player 2`")

games["Player 1 wins"] = games.apply(
    lambda row: determine_winner(row["Player 1"], row["Player 2"]),
    axis=1
)*

在这里,我们创建了一个数据集,由三个玩家随机相互挑战组成。函数determine_winner就是这样做的:如果 **player_1** 赢了,它将获得两个玩家索引(0,1,2)和输出。例如,在游戏(0,1)中——在代码中用粗体标记——数字为 0 的玩家以p=0.05的概率战胜数字为 1 的玩家。

如果你仔细查看概率,会发现数字 2 的玩家应该是最好的,数字 1 居中,0 最弱。

为了增加趣味,让我们介绍第四个玩家,这个只玩了两局

*new_games = pd.DataFrame({
    "Player 1": [3, 3],
    "Player 2": [2, 2],
    "Player 1 wins": [1, 1]
})

games = pd.concat(
    [games, new_games],
    ignore_index=True
)*

3 号玩家和 2 号玩了两次,甚至赢了两次。所以数字 3 应该也有相当高的强度,但是我们不能说数字 3 是否真的比数字 2 好,或者仅仅是运气。

在 PyMC 中构建模型

我们现在能够在 PyMC 中构建模型。请注意,我们将使用高斯先验的球员的力量。此外,我将让模型推断五个玩家的后验概率,尽管没有最后一个号码为 4 的玩家的数据。我们将会看到模型是如何处理这个问题的。

另一件重要的事情是,我不会显式地使用 sigmoid 函数。如果我们通过 *logit_p* 参数而不是 *p* 来传递玩家实力的差异,那么 *pm.Bernoulli* 对象会处理它。

*import pymc as pm

with pm.Model() as model:
    strength = pm.Normal("strength", 0, 1, shape=5)
    diff = strength[games["Player 1"]] - strength[games["Player 2"]]

    obs = pm.Bernoulli(
        "wins",
        logit_p=diff,
        observed=games["Player 1 wins"]
    )

    trace = pm.sample()*

奇迹发生后,我们可以检查后验概率是如何分布的。在左边,你可以看到后验分布作为密度图,在右边,你可以看到一个森林图,让您轻松地比较强度后验。

**

图片由作者提供。

这里可以看到 0 号确实是最弱的玩家,1 号次之。正如所料,数字 2 和 3 是最好的。数字 3 的后验分布的平均值略低于数字 2,但 HDI(高密度区间)要大得多,这表明与数字 2 相比,数字 3 的强度有更多的不确定性。数字 4 的后验强度与前一个相同:正态分布,平均值为 0,标准差为 1。模特在这里学不到任何新东西。

如果你更喜欢数字,这里有:

图片由作者提供。

从那里,我们还可以看到 MCMC 似乎收敛得很好,因为r_hat值都等于 1。

我们还可以看到一些玩家的强度为负,但这完全没问题,因为我们只使用两个玩家之间的强度差。如果你出于某种原因不喜欢这样,你可以用一个HalfNormal分布来代替强度先验,或者你只需在后验分布中增加一些常数,比如 5,这样所有的均值和 HDI 都在正范围内。

结论

在本文中,我们看到了如何构建一个模型,让您对一组玩家(实际玩家、推荐、搜索结果……)进行排名。一个贝叶斯模型,甚至没有任何球员的特点。然而,这并不是一个限制,因为我们也可以合并特性。

该模型从一些关于运动员力量水平的先验信念开始,然后通过数据进行更新。玩家玩的游戏越多,这个玩家实力的不确定性就越小。在一个极端的情况下,如果一个玩家从来没有玩过一场游戏,他们实力的后验分布等于先验分布,这是有意义的。

参考

[1] Burges,c .、Shaked,t .、Renshaw,e .、Lazier,a .、Deeds,m .、Hamilton,n .和 Hullender,g .,使用梯度下降法学习排序 https://arxiv.org/abs/1407.7502(2005),第 22 届机器学习国际会议论文集(第 89–96 页)

我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!

作为最后一点,如果你

  1. 想支持我多写点机器学习和
  2. 无论如何都要计划获得中等订阅量,

为什么不做 通过这个链接 ?这将对我帮助很大!😊

*https://dr-robert-kuebler.medium.com/membership

透明地说,给你的价格不变,但大约一半的订阅费直接归我。

非常感谢,如果你考虑支持我的话!

有问题就在LinkedIn*上写我!**

贝叶斯模型选择的学习调和均值估计

原文:https://towardsdatascience.com/learnt-harmonic-mean-estimator-for-bayesian-model-selection-47258bb0fc2e

机器学习辅助的边际可能性计算

贝叶斯模型比较提供了一个原则性的统计框架,用于选择合适的模型来描述观察数据,自然地权衡模型复杂性和拟合优度。然而,它需要计算贝叶斯模型证据,也称为边际可能性,这在计算上具有挑战性。我们提出了学习调和均值估计器来计算模型证据,这是不可知的采样策略,提供了很大的灵活性。

本文由阿莱西奥·斯普尔里奥·曼奇尼合著。

贝叶斯定理。【图片来源。]

选择一个合适的模型来描述观察到的数据,这在数据科学的许多领域都是一项重要的任务。贝叶斯形式主义为比较和选择模型提供了一个健壮和有原则的统计框架。然而,执行贝叶斯模型选择是高度计算要求的。

贝叶斯模型选择

贝叶斯形式主义是统计推断最常用的方法之一。对于参数推断,对于给定的模型 M ,感兴趣的参数𝜽的后验概率分布可以通过贝叶斯定理与数据 y 的似然相关

其中先验分布 p( 𝜽 | M) 编码了我们在观察数据之前对参数的先验知识(关于贝叶斯推理的介绍,请参见这篇优秀的 TDS 文章)。

由上述等式的分母给出的贝叶斯模型证据与参数估计无关,因为它独立于感兴趣的参数,并且可以简单地视为归一化常数。然而,对于模型比较来说,贝叶斯模型证据,也称为边际可能性,起着核心作用。

对于模型选择,我们感兴趣的是模型的后验概率,通过贝叶斯定理的另一个应用,它可以写成

因此,为了比较模型,我们需要计算贝叶斯因子,这需要计算考虑中的模型的模型证据。这就是计算挑战的地方。

贝叶斯模型证据由参数空间上的似然和先验的积分给出:

因此,证据的计算需要评估多维积分,这在计算上极具挑战性。我们将很快回到这一点,引入学习调和平均估计量来计算模型证据。

值得注意的是,模型证据自然地结合了奥卡姆剃刀,权衡了模型复杂性和拟合优度,如下图所示。

贝叶斯模型证据自然地结合了奥卡姆剃刀,权衡了模型的复杂性和拟合优度。【图表来自Ghahramani(2013)【1】,经许可转载,其灵感来自 David MacKay 的类似图表。]

上图[1]中的水平 x 轴代表所有可能的数据集。在贝叶斯形式主义中,模型被指定为数据集上的概率分布,并且由于概率分布的总和必须为 1,所以每个模型都有有限的“概率预算来分配。虽然复杂模型可以很好地表示大范围的数据集,但它的预测概率分布很广。这样做,如果不需要这样的复杂性,复杂模型的模型证据将被扣分。

因此,贝叶斯形式主义提供了一种原则性的统计方法来执行模型选择。但是,在实践中应用这种形式并不简单,并且在计算上极具挑战性。

已经开发了各种计算模型证据的方法,这些方法被证明非常成功(例如[2,3,4,5])。然而,这些通常有一些限制,并且很难扩展到更高维度的设置。因此,模型证据的计算远不是一个已解决的问题。在本文中,我们将重点放在调和平均估计量来计算贝叶斯模型的证据。

原始调和均值估计量

原始调和平均值估计值于 1994 年由牛顿&拉夫特[6]引入,依赖于以下关系:

这表明模型证据(倒数)的估计量如下:

因此,给定来自后验𝜽 的样本,可通过马尔可夫链蒙特卡罗(MCMC)采样生成,从似然的调和平均值中估计模型证据。

调和平均值估计的一个非常好的特性是,它只需要来自后验的样本,这可以通过任何 MCMC 技术产生。相比之下,计算模型证据的替代方法通常与特定的采样方法紧密结合,这可能具有很大的限制性。

然而,在调和平均值估计被提出后,人们立即意识到它可能会灾难性地失败[7]。事实上,最初的调和均值估计被称为有史以来最差的蒙特卡罗方法

为了直观地了解为什么最初的估计会失败,可以从重要性抽样的角度来看。估计量可以看作是重要抽样,其中先验扮演重要抽样目标分布的角色,后验扮演抽样密度的角色。

为了使重要性抽样有效,我们通常会考虑比目标更宽的抽样密度。然而,对于调和平均值估计量,我们有相反的情况,因为封装了我们对模型参数的初始知识的先验分布通常比封装了我们对数据观察后的模型参数的知识的后验分布更宽。因此,原始谐波平均值的方差可能变得非常大,并且可能不是有限的,使得估计器在实践中无效。

重定目标调和均值估计量

盖尔范德&戴伊在 1994 年【8】引入了重定目标调和平均值估计器来解决这个问题。重定目标的谐波均值估计器引入了新的目标分布 φ (𝜽),其可以被设计成避免上述有问题的配置,从而产生以下估计器:

问题仍然是:应该如何选择新的目标分布 φ (𝜽)?

已经考虑了各种情况,例如一个多元高斯函数【9】和指示函数【10,11】。高斯曲线通常具有太宽的尾部,这会增加估计量的方差。虽然指标函数已被证明是有效的[10],但它们通常局限于参数空间的一个小区域,因此可能是低效的。

学习调和均值估计器

通过考虑最佳目标,人们可以更直观地了解如何设计有效的目标分布,这只不过是标准化后验本身(因为所得的估计量具有零方差)。然而,目标分布必须归一化,后验的归一化常数是模型证据…正是我们试图估计的术语!

虽然最佳目标(标准化后验概率)在实践中不可及,但我们建议使用机器学习来估计近似值:

这就产生了学习谐波均值估计器【12】。后验概率的学习近似值不需要非常精确。但是,至关重要的是,它的尾巴不能比屁股更肥。当从后验样本学习目标分布时,我们施加这个约束。此外,我们提出策略来估计学习调和平均估计量的方差,它自己的方差,和其他健全性检查。(进一步的细节可以在我们的相关文章中找到:麦克尤恩等人 2021【12】。)

数值实验

我们进行了大量的数值实验,通过与贝叶斯模型证据的基础真值进行比较来验证学习的调和均值估计量。

Rosenbrock 函数为评估计算模型证据的方法提供了一个公共基准。在下面的图中,我们显示了学习谐波均值估计器对于这个问题是鲁棒的和高度准确的。

Rosenbrock 例子中学习调和平均值估计量计算的模型证据。执行 100 次实验来恢复估计器的统计的经验估计。不仅估计量本身是准确的,而且估计量的方差估计也是准确的。【原创剧情由作者创作。]

另一个常见的基准问题是法线伽马模型,如下图所示。

正常伽马模型的图形表示。【原图由作者创作。]

在一项审查用于计算模型证据的估计量的研究[13]中,该示例显示了原始调和平均值估计量的病理性故障。在下表中,我们给出了由原始调和平均值估计器和我们学习的调和平均值估计器为该问题计算的模型证据值。我们的学习估计器非常准确,比原始估计器提高了四个数量级。

贝叶斯模型证据值由原始调和平均值估计器和我们学习的调和平均值估计器为正常伽马基准问题计算。我们的学习估计器是高度准确的,提供了比原始估计器高四个数量级的改进(在对数空间中)。

谐波代码

学习谐波均值估计器在 谐波 软件包中实现,该软件包是开源的并可公开获得。遵循软件工程最佳实践,仔细考虑了代码的设计和实现。

因为学习谐波均值估计器只需要来自后验分布的样本,所以谐波码对于用于生成后验样本的方法或码是不可知的。也就是说, harmonic 与 MCMC 采样技术配合得非常好,MCMC 采样技术通过其系综特性自然地提供来自多个链的样本,例如仿射不变性系综采样器[14]。 主持人 代码【15】提供了仿射不变性集合采样器的出色实现,因此主持人是与谐波一起使用的自然选择。

摘要

贝叶斯模型比较为选择合适的模型来描述观察数据提供了一个强大的原则性统计框架,自然地权衡了模型的复杂性和拟合优度。然而,它需要计算贝叶斯模型证据,也称为边际可能性,这是一个具有计算挑战性的问题。

在本文中,我们回顾了用于计算模型证据的调和均值估计器,包括我们最近提出的学习调和均值估计器。学习调和均值估计器对用于产生后验样本的方法是不可知的,这给它提供了很大的灵活性。我们还发布了一个开源代码, harmonic ,它实现了估算器,我们遵循软件工程的最佳实践,非常关注代码的设计和实现。

目前,我们采用非常简单的机器学习模型和我们学习的调和均值估计器。这些简单的模型将很难扩展到非常高维的环境。我们已经在探索使用更复杂的机器学习模型,这将允许我们扩展到更高维度的设置。

我们希望学习调和均值估计已经可以为贝叶斯模型选择提供一个有用的工具。特别是,由于它是不可知的抽样方法,它允许一个人计算模型证据的大量不同的抽样方法,这在以前是不可能的。

参考

[1] Ghahramani贝叶斯非参数化和概率方法建模, 菲尔。反式。R. Soc。 (2013 年)

[2] Skilling,一般贝叶斯计算的嵌套抽样。 贝叶斯分析 (2006)

[3] Feroz & Hobson, MultiNest:一种用于宇宙学和粒子物理学的高效且健壮的贝叶斯推理工具, MNRAS (2009), arXiv:0809.3437

[4] Handley,Hobson & Lasenby, PolyChord:宇宙学的嵌套采样, MNRAS (2015), arXiv:1502.01856

[5]蔡,McEwen,Pereyra,高维贝叶斯模型选择的近似嵌套抽样arXiv:2106.03646

[6] Newton & Raftery,用加权似然 bootstrap 进行近似贝叶斯推断,J R Stat Soc Ser A(1994)

[7] Neal, 对“用加权似然自助法近似贝叶斯推断”的讨论的贡献 (1994)

[8]盖尔范德&戴伊,贝叶斯模型选择:渐近和精确计算。J R Stat Soc Ser B

[9] Chib,来自吉布斯产出的边际可能性美国统计协会杂志 (1995)

[10] Robert & Wraith,贝叶斯模型选择的计算方法,美国物理学会会议论文集(2009), arXiv:0907.5123

[11] van Haasteren,【MCMC 方法的边际似然计算,载于引力波探测和脉冲星计时阵列数据分析 (2014)

[12]麦克尤恩,沃利斯,普莱斯,多切蒂,机器学习辅助贝叶斯模型比较:已学习调和均值估计器 (2021),arXiv:2111.12720

[13]弗列尔和怀斯,评估证据——一项审查,尼尔兰迪卡统计局(2012 年), arXiv:1111.1957

[14] Goodman & Weare,仿射不变性集合采样器, 应用数学与计算科学通讯 (2010)

[15]福尔曼-麦基,霍格,郎,古德曼,主持人:MCMC 汉默, PASP (2013), arXiv:1202.3665

用围棋对两个和进行编码

原文:https://towardsdatascience.com/leetcode-two-sum-with-go-67d24e5a53f3

Python 用户 Golang 入门

卡拉·埃尔南德斯在 Unsplash 上的照片

我喜欢用 LeetCode 来入门新语言。这是在复习数据结构和算法的同时熟悉新语法的简单方法。我主要是一个 Python 用户,我想强调一些您需要理解的新概念,以帮助您将 Python 知识转化为 Go。

本教程不会详细介绍 go 中的每个特性或数据结构,因为它旨在为您提供解决 LeetCode Two Sum 问题的基本工具。

问题是

给定一个整数数组和一个目标,返回两个元素的索引,两者之和等于目标。每个数字只能使用一次,返回的索引顺序无关紧要。你可以假设只有一个解决方案。

示例:

arr: [1,2,3,4,5]
target: 3
answer: [0,1] or [1,0]

下面是 LeetCode 上问题的链接:

https://leetcode.com/problems/two-sum

该算法

这个问题的最佳解决方案是使用哈希表将数组的每个元素映射到它的索引。对于数组中的每个元素,您将从目标中减去它以获得补数。然后您将检查补码是否是哈希表中的一个键。如果它在哈希表中,您可以将解作为包含当前索引和补码索引的数组返回。否则,向哈希表添加一个新元素,其中键是当前数字,值是它的索引。循环继续,直到找到解决方案。

以下是一个 Python 解决方案:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        complements = {}
        for idx, num in enumerate(nums):
            complement = target - num
            if complement in complements:
                return [complements.get(complement), idx]
            complements.update({num: idx}) 

开始熟悉围棋

使用 Python 算法作为模板,这里有一个快速检查表,列出了在 Go 中求解两个和需要学习的主要概念:

  1. 变量
  2. 部分
  3. 条件式
  4. 地图
  5. 功能

变量

Go 是一种静态类型的语言,在处理变量的时候是有区别的。

声明和初始化:

package main

import "fmt"

func main() {
    var a int
    fmt.Println(a)
}

在上面的代码中,第 6 行声明了一个整数变量a。由于代码没有显式初始化a,它被初始化为0。如果要在声明时初始化变量,可以这样做:

package main

import "fmt"

func main() {
    var a int = 12
    fmt.Println(a)
}

Go 也可以暗示变量类型:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var a = 12
    fmt.Println(reflect.TypeOf(a))
}

上面的代码片段打印了int,即使在声明中没有明确定义a的类型。

简短的赋值语句

Go 还有一个简短的赋值语句,去掉了var关键字,并隐含了 type。这里有一个例子:

package main

import "fmt"

func main() {
    a := 12
    fmt.Println(a)
}

与前面的例子不同,短赋值语句只能在函数内部使用。

部分

一个slice是一个array的特定元素的视图。与数组不同,它们是动态调整大小的。在 Go 中,你可能会在 Python 中使用list的地方使用slice

这里有一个快速入门slices的例子:

package main

import "fmt"

func main() {
    a := 12
    fmt.Println(a)
}

第 6 行显示了如何声明一个slice。第 7 行向slice追加一个整数,该整数在第 9 行打印出来。这类似于下面的 Python 片段:

a = []
a.append(1)
print(a)

第 10 行展示了如何用一个slice文本初始化一个变量。第 12 行显示了如何在特定索引处更改slice中的值,这与您在 Python list中使用的语法相同。虽然本教程不涉及array数据结构,但是您应该知道,更改slice的值将会修改底层的array

条件式

Python 用户应该对 Go 中的条件语句非常熟悉。Go 使用花括号而不是空格来分隔块,并且去掉了冒号。

Python:

def main():
    a = 12

    if a > 20:
        print('a is greater than 20')
    else:
        print('a is not greater than 20')

去:

package main

import "fmt"

func main() {
    a := 12

    if a > 20 {
        fmt.Println("a is greater than 20")
    } else {
        fmt.Println("a is not greater than 20")
    }
}

Go 还允许缩短条件语句:

package main

import "fmt"

func main() {
    a := 12

    if b := a + 2; b > 20 {
        fmt.Println("a + 2 is greater than 20")
    } else {
 fmt.Println("a + 2 is not greater than 20")
    }
}

在上面的代码片段中,a + 2if语句中被赋值给b。您可以通过下面的代码片段在 Python 中实现类似的功能:

def main():
    a = 12

    if (b := a + 2) > 20:
        print('a + 2 is greater than 20')
    else:
        print('a + 2 is not greater than 20')

Go 和 Python 代码片段之间的主要区别在于作用域,下面的代码片段可以更好地解释这一点:

Python:

def main():
    a = 12
    b = 'hello'

    if (b := a + 2) > 20:
        print('a + 2 is greater than 20')
        print(f'b = {b}')
    else:
        print('a + 2 is not greater than 20')
        print(f'b = {b}')

    print(f'b = {b}')

Python 函数打印以下内容:

a + 2 is not greater than 20
b = 14
b = 14

变量b在第 3 行被初始化为“hello ”,然后在第 5 行的条件中被覆盖。也就是说,条件语句在函数范围内修改b,因此b在第 12 行打印为 14 而不是“hello”。

去:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    a := 12
    b := "hello"

    if b := a + 2; b > 20 {
      fmt.Println("a + 2 is greater than 20")
      out := fmt.Sprintf("b = %d \n", b)
      io.WriteString(os.Stdout, out)
    } else {
      fmt.Println("a + 2 is not greater than 20")
      out := fmt.Sprintf("b = %d \n", b)
      io.WriteString(os.Stdout, out)
    }

    out := fmt.Sprintf("b = %s", b)
    io.WriteString(os.Stdout, out)
}

Go 函数返回以下内容:

a + 2 is not greater than 20
b = 14 
b = hello

在 Go 中,条件语句中初始化的变量是条件块的局部变量。在上面的代码中,变量b在条件结束后超出范围,因此对b的第二次调用在外部范围中访问它的值。

地图

在 Go 中,你可以在 Python 中使用dict的地方使用map。要实现上述算法,您需要能够执行以下操作:

  1. 声明/初始化一个map
  2. 添加键值对
  3. 检查特定的键是否存在

下面是一个入门示例:

package main

import "fmt"

var m map[string]int

func main() {
    m = make(map[string]int)
    m["a"] = 2
    fmt.Println(m)
}

第 3 行声明了一个变量m,它是一个nil map,其中键的类型是string,值的类型是int。此时,map没有键值数据,不能添加任何东西。

在第 6 行中,调用了make函数,该函数初始化一个map并将它分配给m。第 7 行向m添加了一个新元素,其中键是“a”,值是 2,这也是更新 a dict的有效 Python 语法。

这是练习短变量赋值的好地方,如下所示:

package main

import "fmt"

func main() {
    m := make(map[string]int)
    m["a"] = 2
    fmt.Println(m)
}This example deletes the variable declaration step, by using the := operator. This helps to make the code less verbose.

要实现两个和算法,您需要检查给定的补码是否出现在map中。下面是一个在围棋中如何做到这一点的例子:

package main

import "fmt"

func main() {
    m := map[string]int{"a": 1, "b": 2, "c": 3}
    if val, ok := m["a"]; ok {
        fmt.Println(val)
    } else {
        fmt.Println("not in m")
    }
}

上面的代码片段创建了一个map文本,并检查a是否是map中的一个键。如果键在map中,val将被设置为该值,ok将被设置为true

Go 中的 For 循环接近 C 风格的 for 循环:

package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
}

如果您需要遍历一个数组并访问索引和值,您可以在 Python 中尝试这样做:

def main():
    nums = [10, 22, 33, 44, 52]

    for i, v in enumerate(nums):
        print(i)
        print(v)

以下是您在 Go 中的操作方法:

package main

import "fmt"

func main() {
    nums := []int{10, 22, 33, 44, 52}
    for i, v := range nums {
        fmt.Println(i)
        fmt.Println(v)
    }
}

当你在 Go 中使用带有range的 for 循环时,它完成的事情类似于在 Python 中使用enumerate。迭代变量为iv,其中i为索引,vnums在索引i处的值。

与 Go 中的条件一样,for 循环中初始化的变量是循环块的局部变量。因此,如果你试图在 Go 循环后打印iv,你会得到一个错误。在 Python 中,在循环中初始化的变量仍然存在于外部作用域中。

功能

如果你习惯用 Python 中的类型提示来定义函数,那么 Go 中的函数会感觉非常相似。下面的片段大概是 LeetCode 为你提供的开始使用 Two Sum 的内容。这两个函数都有两个参数,numstarget,它们返回一个整数数组。

Python:

def twoSum(nums: List[int], target: int) -> List[int]:

如果您不习惯在 Python 中使用类型提示,它们只是定义了参数和返回值应该是什么样子。在上面的函数定义中,nums应该是一个list,其中每个元素都是一个int,而target是一个int。返回值被指定为->右边的部分,是一个List,其中每个元素都是一个int

去:

func twoSum(nums []int, target int) []int {

}

Go 功能的设置方式相同。nums参数和返回值都是包含int类型元素的slice,这就是[]int的含义。

与 Python 不同,为参数和返回值指定类型不是可选的。同样与 Python 不同的是,如果用户传递参数或生成不符合函数定义的返回值,将会出现错误。

把它们放在一起解两个和

下面是 Go 中两个和算法的实现:

func twoSum(nums []int, target int) []int {
    m := make(map[int]int)
    var ans []int

    for idx, num := range nums {
        complement := target - num

        if c, ok := m[complement]; ok {
            ans = []int{c, idx}
            break
        }
        m[num] = idx
    }
    return ans     
}

第 2 行用类型int的键和值初始化map。第 3 行声明了ans,它是一个int类型的slice

循环从第 5 行开始,使用range以便索引和值都可用。循环的第一步计算complement,然后检查它是否是m中的一个键。如果ok为真,ans被设置为等于包含numsidx中的complement的索引的slice文字。然后循环中断,函数返回ans。如果ok为假,则向m添加新元素,其中键为num,值为idx

因为ans的值是在条件语句中确定的,并且需要被返回,所以ans在函数范围中被声明。如果您想避免这种情况,可以尝试以下实现方式:

func twoSum(nums []int, target int) []int {
    m := make(map[int]int)

    for idx, num := range nums {
        complement := target - num

        if c, ok := m[complement]; ok {
            return []int{c, idx}
        }
        m[num] = idx
    }
    return []int{}    
}

不用把要返回的值保存在变量中,可以直接返回slice。为了使用 LeetCode 提供的结构来实现这一点,您需要在底部返回一个空的slice。第 12 行只在无解的情况下执行,这超出了这个问题的范围。

一般提示

  1. 在 Python 中我经常用单引号表示str值,但是 Go 需要双引号。
  2. Go 对空值使用nil,这类似于 Python 中的None
  3. Go 有一个很棒的基于网络的 IDE,你可以在这里找到:https://go.dev/play/
  4. 这可能是最好的入门教程:https://go.dev/tour/list

结论

你刚刚学习了如何用围棋解两个和。如果您是一名使用 Python 的用户,那么您应该已经注意到了这两种语言之间的一些关键差异。这只是皮毛,但我希望它对你有所帮助。

如何在 Matplotlib 中将图例放置在绘图之外

原文:https://towardsdatascience.com/legend-outside-the-plot-matplotlib-5d9c1caa9d31

展示如何使用 matplotlib 和 Python 将图例放置在绘图之外

KOBU 机构Unsplash 拍摄的照片

介绍

当用matplotlib绘制图形时,重要的是确保包含图例,以便读者能够区分 y 轴上显示的数据。图例通常以方框的形式包含在绘制的图表中,它可以用作将颜色或形状映射到特定数据条目的字典。

在今天的简短教程中,我们将展示如何将图形的图例放置在matplotlib的绘图区域之外。当要显示的图形可能包含太多信息(甚至在四个角上)时,通常需要这样做。在这种情况下,将图例放在外面最终会使您的图表可读性更好。

更具体地说,我们将展示如何

  • 将图例放置在图外(以及任何需要的位置)
  • 稍微把传说推到情节之外
  • 更改放置在图外的图例的形状(例如,水平)

首先,让我们创建一个示例图,我们将在整个教程中使用它来演示一些概念,并了解如何将图例放置在绘图区域之外的几个不同位置。

import numpy as np
import matplotlib.pyplot as plt# Create some sample data
data = np.arange(20)# Create a matplotlib figure
fig, ax = plt.subplots()# Create multiple plots 
for i in range(7):
    ax.plot(data, i * data, label=f'y={i}x')# Set title and labels
ax.set_title('Example plot')
ax.set_xlabel('x')
ax.set_ylabel('y')# Add a legend
ax.legend()# Visualize the final plot
plt.show()

下面分享了上面代码的输出图

matplotlib 示例图—来源:作者

注意,默认情况下,matplotlib会根据可视化的数据将图例(如果有)放置在最佳位置。

将图例放置在图外

现在,为了将图例放置在绘图之外(假设在右侧的中心),我们实际上需要做两件事。

首先,我们需要缩小图的宽度(以便图例适合图形)。然后我们可以使用bbox_to_anchor参数,它为手工放置图例提供了一定程度的控制。

bbox_to_anchor

用于与锁定一起定位图例的盒子。默认为**axes.bbox**(如果作为方法调用[**Axes.legend**](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.legend.html#matplotlib.axes.Axes.legend))或**figure.bbox**(如果[**Figure.legend**](https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.legend))。该参数允许任意放置图例。

Bbox 坐标在由 bbox_transform 给出的坐标系中进行解释,根据调用的legend不同,使用默认的变换轴或图形坐标。

如果给定了一个 4 元组或[**BboxBase**](https://matplotlib.org/stable/api/transformations.html#matplotlib.transforms.BboxBase),那么它指定放置图例的 bbox (x, y, width, height)。将图例放在坐标轴(或图形)右下象限的最佳位置。

matplotlib 文档

import numpy as np
import matplotlib.pyplot as plt# Create some sample data
data = np.arange(20)# Create a matplotlib figure
fig, ax = plt.subplots()# Create multiple plots 
for i in range(7):
    ax.plot(data, i * data, label=f'y={i}x')# Set title and labels
ax.set_title('Example plot')
ax.set_xlabel('x')
ax.set_ylabel('y')# Add a legend
pos = ax.get_position()
ax.set_position([pos.x0, pos.y0, pos.width * 0.9, pos.height])
ax.legend(loc='center right', bbox_to_anchor=(1.25, 0.5))# Visualize the final plot
plt.show()

图例位于地块外部的示例地块-来源:作者

更改放置在图外的图例的形状

现在让我们假设我们想把图例放在图的外面,但是这次是在顶部位置。更有意义的做法是改变图例的形状,使其水平显示,而不是垂直显示,这样实际绘图可以占用更多的区域。

再一次,我们将不得不缩小图的高度,这一次是为了给放置在图的顶部区域的图例留出一些空间。然后,我们可以将图例放置在所需的位置,还可以更改列数,以便它可以水平显示。

import numpy as np
import matplotlib.pyplot as plt# Create some sample data
data = np.arange(20)# Create a matplotlib figure
fig, ax = plt.subplots()# Create multiple plots 
for i in range(7):
    ax.plot(data, i * data, label=f'y={i}x')# Set title and labels
ax.set_title('Example plot')
ax.set_xlabel('x')
ax.set_ylabel('y')# Add a legend
pos = ax.get_position()
ax.set_position([pos.x0, pos.y0, pos.width, pos.height * 0.85])
ax.legend(
    loc='upper center', 
    bbox_to_anchor=(0.5, 1.35),
    ncol=3, 
)# Visualize the final plot
plt.show()

图例水平放置在图外的示例图-来源:作者

将图例稍微移出剧情

这里的另一种可能性是,稍微将传说推到情节之外,使一半落在情节本身之内。这可能是最简单的方法,因为我们只需要使用bbox_to_anchor参数将图例与loc一起放置。

import numpy as np
import matplotlib.pyplot as plt# Create some sample data
data = np.arange(20)# Create a matplotlib figure
fig, ax = plt.subplots()# Create multiple plots 
for i in range(7):
    ax.plot(data, i * data, label=f'y={i}x')# Set title and labels
ax.set_title('Example plot')
ax.set_xlabel('x')
ax.set_ylabel('y')# Add a legend
ax.legend(loc='upper right', bbox_to_anchor=(1.1, 1.1))# Visualize the final plot
plt.show()

图例被稍稍推出图外的示例图—来源:作者

最后的想法

在今天的简短教程中,我们讨论了在图表中包含图例的重要性。此外,我们展示了如何在matplotlib和 Python 中将图例放置在情节之外。

重要的是要记住,通过将图例放置在图形之外,可以使您的绘图更具可读性,特别是当它们超载了太多信息,甚至在绘图区域的四个角包含线条或任何其他信息时。

成为会员 阅读媒体上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

即使在热爱大数据的领域,少即是多

原文:https://towardsdatascience.com/less-is-more-even-in-the-field-that-loves-big-data-699798b700ac

在生物信息学中,有时“精心制作”的小数据可以为我们提供与大数据见解相当的强大见解

马库斯·斯皮斯克Unsplash 上拍摄的照片

大约在去年的这个时候,奥米克隆作为主要的严重急性呼吸综合征冠状病毒-2(新型冠状病毒)变种已经占领了世界。在今年的过程中,一些 Omicron 亚变异体通过突变和某些情况下的重组交换了优势。新型冠状病毒奥米克隆 XBB 亚变异体(在本文中简称为奥米克隆 XBB)就是这样一个重组亚变异体的例子,它在 2022 年 8 月以其高传播性而闻名。

然而,我写这篇文章的主要动机不是调查 Omicron XBB 中的进化,而是证明在生物信息学中,像其他数据科学子领域一样,在某些情况下精心制作的小数据可以产生与大数据相当的洞察力。在撰写本文时,大约有 2 300 个 Omicron XBB 的全基因组序列上传到了 GISAID 上。作为筛选序列的一种方式,我选择了以下序列:

1.完成

2.大视野

3.以病人身份。

这将我的序列缩小到 414 个可供下载的高质量序列。

一旦我有了高质量的全基因组序列,就到了推断见解的时候了。我通常使用下面的 6 个 Python 包,从生物信息中获得尽可能多的见解。

# I'm a fan of importing all the packages I'll use in the first cell
# Useful for pinpointing which package is not executing properly

import numpy as np
import pandas as pd
import Bio     #Biopython
from collections import Counter
import matplotlib.pyplot as plt
import seaborn as sns

我使用 Biopython (Bio) 来解析/读取 fasta 文件中的核苷酸序列,我使用 print 函数来确认内容并浏览文件中的内容。fasta 序列存储在 omicron 变量中。

from Bio import SeqIO
# Biopython is useful for loading biological sequence

omicron = SeqIO.parse("Omicron XBB Complete Sequences.fasta", 'fasta')

for seq_record in omicron:

    print(seq_record.id)            #taking a glimpse of what's in the file
    print(repr(seq_record.seq))

在这项研究中,我选择集中研究编码刺突蛋白的刺突基因。刺突蛋白是冠状病毒进入宿主细胞的关键。此外,它还演示了生物信息学家如何从生物序列中切割出所需的片段。

因为完整的序列具有不同数量的核苷酸碱基,所以使用了一个近似的刺突基因座位,所有的刺突基因都可以整合到该座位上,即使末端变细。

from Bio import SeqIO
# I had to recall the Biopython package, for some reason it could not work without being recalled

#THis cell is slicing the approximate spike genes from SARS-CoV-2 OmicronXBB whole genome sequences.
omicron_spike = []

for seq_record in SeqIO.parse("Omicron XBB Complete Sequences.fasta",'fasta'):
    spike_gene = seq_record[21500:25499]
    omicron_spike.append(spike_gene)

SeqIO.write(omicron_spike,'omicron_spike.fasta','fasta') 
# writes omicron_spike  contents to a fasta file with name provided.
  1. 然后使用 EBI 网络服务器上可获得的 Clustal 比对近似的刺突序列。

2.用于删除冗余/重复序列的 Jalview。

3.Mega X 用于编辑比对,使用 GISAID Spike Ref Seq 作为移除锥形末端的指南。

4.现在干净的刺突基因文件通过 Datamonkey 网络服务器上传到 FUBAR 上用于选择分析。

5.分析之后,生成的结果表被导出为。csv 文件。

选择调查

的。然后使用 pandas 加载从 FUBAR 导出的 csv 文件进行进一步分析。只是对这些术语的含义进行了简要的分析:

1.位点=三联体编码的位点,三联体编码氨基酸,因此它相当于氨基酸位置。

2.α=同义替换率,相当于编码的氨基酸改变的频率导致编码的氨基酸没有改变。

3.β=同义替换率,相当于编码的氨基酸改变导致编码的氨基酸改变的频率。

其他参数将 alpha 和 beta 值联系起来,为我们提供数据集中观察到的进化模式的更多细节。

selection_table = pd.read_csv('OmicronXBBdatamonkey-table.csv')
selection_table

通常阴性/纯化选择不会给我们提供足够的信息,除非它是研究的目的,因为我们正在观察稳定其构象的某些位点。由于这种稳定性,某些位点非常适合设计配体/药物,因为它们很少改变。

我最感兴趣的是积极/适应性选择,因为它让我们了解病毒是如何进化的,当某个突变在病毒群体中逐渐出现时,它应该比没有突变的病毒更有优势。

如果对负选择感兴趣,可以取消涉及负选择的代码的注释

sites_positive = []
# sites_negative = []

for idx, val in enumerate(selection_table['beta-alpha']):
    if val > 1:
        sites_positive.append(idx)

    else:
        pass

number_of_positive_sites = str(len(sites_positive))
# number_of_negative_sites = str(len(sites_negative))

print(f'{number_of_positive_sites} sites show adaptive selection')
# print(f'{number_of_negative_sites} sites show purifying selection')

正选择网站的贝叶斯因子

我喜欢用贝叶斯因子来理解正选择,因为它给了我强正选择位点的突出峰。

site = selection_table['Site']
bayes_factor = selection_table['BayesFactor[alpha<beta]']

plt.figure(figsize=(16,10), dpi=1200)
ax = plt.axes()
ax.plot(site, bayes_factor)
plt.show;

显示强阳性网站的网站的贝叶斯因子(图片由作者提供)

显示强阳性和强阴性选择的位点

上图显示了位点的一般趋势,将强阳性选择显示为尖峰,将阴性选择显示为小/平峰。

根据 FUBAR 的说法,大于 0.9 的后验概率显示了强阳性或强阴性选择的证据,这取决于所选择的特征,因此下面的代码块有助于我们找到该模式。

strong_beta = selection_table['Prob[alpha<beta]']
strong_alpha = selection_table['Prob[alpha>beta]']
# The Posterior probability where alpha (synonymous rates) < beta (non-synonymous rates/var(strong_beta)) is more than 0.9 were selected as evidence of strong positive selection 
# The Posterior probability where alpha (synonymous rates) > beta (non-synonymous rates/var(strong_alpha)) is more than 0.9 were selected as evidence of strong negative selection 

strong_positive_selection = selection_table.loc[strong_beta > 0.9] 
strong_negative_selection = selection_table.loc[strong_alpha > 0.9]

# print(Counter(strong_positive_selection))

#Then we combine the two strong_selection dataframes.
strong_selection_dfs = [strong_negative_selection,strong_positive_selection]
strong_selection_sites = pd.concat(strong_selection_dfs)
strong_selection_sites

显示强阴性和强阳性选择的网站(图片由作者提供)

开始讨论上面表格和图表中显示的趋势和数字将会是一篇有价值的文章,并且会偏离本文的主旨。

Omicron XBB 对患者的影响

嗯,从我们的数据中获得数字和趋势对我们来说是一个漫长的旅程,但是到目前为止,产生的信息对少数知道我们是如何做到这一点的人来说是很酷的。如果它对我们理解新冠肺炎没有帮助的话,它会变成更多的爱好,而且爱好不会让我们资助 lol。

下一个简短的部分帮助我们理解这些突变是如何对健康产生影响的。

请记住,用于选择这些序列的过滤器之一是患者数据,我将根据用例演示使用这些数据的方法之一。

我选择了患者状态,但还有其他字段需要研究,如性别、位置、收集日期等其他参数。

患者数据文件下载为. tsv 文件(制表符分隔值),我发现很难使用,所以我将其转换为。使用在线工具生成 csv 文件(逗号分隔值)。如果脱机工作,MS Excel 也可以进行转换,只是对我来说需要更长的时间。

patient_data = pd.read_csv('Omicron XBB Patient Data.csv')
patient_data
patient_status  #some of the fields can be combined as they mean the same to reduce noise

一些用于描述患者状态的不同术语意思相同,这是因为这些序列是由世界各地不同的实验室生成和上传的。

所以我不得不用手工的方式去做,因为我找不到更好的方法来使用代码。同样,如果我处理数百万的患者条目,这将会非常耗时。

generalised_patient_status = ('Hospitalised','Ambulatory','Deceased','Released','Unknown')
patient_numbers = (135,117,12,122,8) 

labels = list(generalised_patient_status)
size = list(patient_numbers)

sns.set()
plt.figure(figsize=(16,10))
plt.pie(size, labels=labels, autopct='%1.0f%%');
plt.title('Patient statuses after testing postive for Omicron XBB')
plt.figure(dpi=1200); #for image quality

患者状态(图片由作者提供)

因此,从上面生成的饼状图可以清楚地看出,34%的 Omicron XBB 患者住院,3%死于 Omicron XBB,31%出院。感染 Omicron XBB 的新冠肺炎患者中有 30%是门诊患者或未住院患者。

正是从这些统计数据中,医疗专业人员可以推断出 Omicron XBB 的致命性或健康负担,这有助于他们采取健康措施来遏制该病毒。

假设来说,随着大数据或数百万生物序列的使用,由于大量样本可供选择,洞察的准确性有所提高。

然而,随着数据量的增加,寻找单个的“未知”或错误变得繁琐而耗时。通过这些未知或误差的积累,数据积累了噪声,这可能会破坏下游过程中使用的算法的平稳执行。

除了顺利执行精心制作的小数据,它还可以更快地生成见解,以便您可以有更多的时间来分析您的数据,从中提取有用的信息。唯一要付出的代价就是精心制作。

少即是多:带标签的数据不再重要

原文:https://towardsdatascience.com/less-is-more-labeled-data-just-isnt-as-important-anymore-7cf3b403969

对半监督学习的新研究表明,较少标记的数据实际上使机器学习算法更强大

我过去总是认为数据本质上是平静有序的——一组准备处理的整齐包装的信息。我想大多数没尝过现实世界混乱滋味的人也会这么认为。专业人士(或者,实际上,任何与数据打交道的人)都知道,数据的流动性和无定形性要大得多。很少有数据被有序地组织起来,并带有一个漂亮的标签。事实上,

大多数时候,数据是未标记的、非结构化的和混乱的。

不幸的是,今天广泛使用的大多数机器学习算法都严重依赖于标记数据和完全监督的算法。数据科学家和数据工程师花费了大量的时间和精力来对抗熵并产生这些干净的数据集,我们已经习惯于在像 Kaggle 这样的网站上看到这些数据集。

然而,事实证明,带有故意噪声注入的半监督方法可能比任何监督学习方法都好,即使在使用明显较少标记的数据时也是如此。

半监督学习

监督学习需要大量的标记数据,而我们大多数人都没有这些数据。半监督学习(SSL)是一种不同的方法,它结合了无监督和监督学习,以利用各自的优势。下面是一个可能的过程(称为 SSL 和“域相关数据过滤”):

1.训练一个模型( M )上标注的数据(【X】)和真实标签(【Y】)。
2
2。计算误差。
3。对未标记的数据应用M(【X’)“预测”标签(【Y’)
4。从(2)中取出任何高可信度的猜测,并将它们从X’移动到 X 5。重复一遍。

随着时间的推移,标注数据集的大小会增加,并为模型提供相当多的数据。这已经令人印象深刻,因为未标记的数据远比标记的数据便宜和丰富,但 SSL 并不能保证是一个好的解决方案 (Singh,Nowak,& Zhu)。然而,一项新的研究表明,我们可以改变一些事情来提高 SSL 的性能。

如果我告诉你,这个 SSL 的新版本几乎优于监督学习,只有一小部分必要的标记数据,会怎么样?

无监督数据增强(UDA)

如果你的孩子只从你告诉他们的东西中学到东西,很有可能在很多情况下他们会完全迷失。机器学习算法也是如此。事实证明,让算法处理棘手的数据会让它们在以后变得更加健壮。这些“棘手的数据”也可以被称为“噪音”加强机器学习算法的一个真正有用的技术是故意向数据添加噪声(在本文中称为“噪声注入”或“数据扩充”)。在 CMU 和谷歌大脑团队在的一篇新论文中,一个关心“噪音质量”的新 SSL 框架也被注入。以下是总体模型的流程图:

转载自“用于一致性训练的无监督数据增强”(谢等。艾尔。2020).

以下是同一流程图的分步格式:

1.在标签数据( X )和真实标签( Y )上训练一个模型( M )。
2。计算监督误差。
3。对未标记的数据应用【M】(X’)“预测”标签(Y’)
4。巧妙的将噪声引入到产生【X】
5。将 M 应用于未标记的、被扰乱的数据(
【X】)“预测”标签(【Y】)
6。通过比较
Y’Y’计算无监督误差。
7。使用(2)和(6)计算总误差。
8。从(3)中取出任何高置信度的猜测,并将它们从 X ' 移动到 X
9。
重复。
****

在上面的步骤过程中,有很多方法可以做到(4)。一种有趣的方法叫做反向翻译。想想这句话:

标注的数据不如未标注的数据便宜,因此是过去的遗迹*。”*******

我可以把这个句子转换成另一种语言,然后再转换成英语。这是我用挪威语作为中间媒介的结果。

标明的数据不像未标明的数据那么便宜,因此已经是过去的*了。”*******

现在我们添加了噪声。细微的变化,但对一个算法来说是天壤之别。

结果&标签数据的困境

前面提到的论文对上一节中概述的方法进行了压力测试。在文本分类数据集上使用 Google 的 BERT (Large)算法,他们生成了以下结果,其中 n 是使用的标记样本的数量):

 *+ — — — — + — — — — — — — —  + — — — — — — — — — — — +
       | **Dataset** | **Supervised Error** | **Semi-Supervised Error** |
       + **— — — —** + **— — — — — — — —** + **— — — — — — — — — — —** +
       | *IMDb*    | 4.51  (n = 20k)  | 4.78 (n = 20)         |
       | *Yelp-2*  | 1.89  (n = 560k) | 2.50 (n = 20)         |
       | *Yelp-5*  | 29.32 (n = 650k) | 33.54(n = 2.5k)       |
       | *Amazon-2*| 2.63  (n = 3.6m) | 3.93 (n = 20)         |
       | *Amazon-5*| 34.17 (n = 3.0m) | 37.80(n = 2.5k)       |
       | *DBpedia* | 0.64  (n = 560k) | 1.09 (n = 140)        |
       + — — — — + — — — — — — — —  + — — — — — — — — — — — +*

这些结果表明,这种特殊的 SSL 方法在处理一小部分标记数据时几乎与监督方法处理所有标记数据时一样好!

至于带标签的数据的有用性——自然,如果带标签的数据是可用的,你就使用它。但是有了这样的结果,任何创建有序、结构化、有标签的数据的基本原理正在迅速消失,特别是因为无标签数据可以广泛获得并且非常便宜(计算、时间和精力方面)。未来是不可预测的(也是未标记的),但我们正在慢慢获得捕捉它的必要工具!

本帖原文可在 这里 找到。

减少软件,增加设计

原文:https://towardsdatascience.com/less-software-more-design-449175a34e59

做好即

提高可视化效果的基本设计策略

密歇根底特律的街头艺术。作者照片。

我教那些刚开始培养数据处理技能的学生。流行的神话是好的数据可视化来自(所谓的)好的软件。Excel 通过使用户能够快速地将原始数据转换成具有几十个切片的三维条形图和饼图,或者更糟糕的是,三维饼图,延续了这一神话。糟糕的可视化不是软件的问题,而是用户的设计选择。

我写这篇文章的动机是鼓励新的学习者,尤其是我的学生,通过深思熟虑和有意识地应用设计原则来实现令人信服的数据可视化。我用 Google Sheets 制作了下面这张信息图。如您所知,数据可视化社区并不认为 Google Sheets 是“专业的”可视化软件,但是这个图形对于其预期目的和受众是有效的。

在 Google Sheets 中创建的简单信息图。作者图文。

软件既不是创建引人注目的可视化的必要条件,也不是充分条件。在计算机出现之前,人们创造了一些最有影响力的视觉效果。声称一个给定的软件比另一个软件好,这与认为钢笔比铅笔好没有什么不同。或者锤子比螺丝刀好。少关注软件,多关注解决问题和设计,你会发展得更快,走得更远。

本文回顾了一些基本的设计策略,您可以快速、直接地将它们应用到数据可视化中。我的评论不打算是全面的。相反,本文是学习基本技术和概念设计原则的路标。

为你的观众设计,而不是为你自己

新手在构建数据可视化时犯的最大错误是为自己设计。颜色和字体是根据个人喜好选择的。注释,如果使用的话,充满了缩写和行话。新的学习者以一种对他们自己有意义的方式排列图形元素,而不是对观众。

从受众的角度出发,做出所有的设计决策。如果您不了解您的受众,您可能还没有准备好可视化您的数据。当然,您可以使用可视化来探索您的数据,但是我专注于讲述数据故事。在对你的观众定位后,你可以用下面的一些问题来帮助引导你的设计思维。

  • 关于这个话题,你的听众已经知道了什么?
  • 你的观众需要知道什么来理解可视化?
  • 您的受众有特定的信息需求吗?这些需求是什么?
  • 数据可视化是交流数据的最佳方式吗?可视化本质上并不优于表格。明确你选择可视化的原因。
  • 受众的信息需求是什么?
  • 将如何查看可视化效果?(电子版 vs .硬拷贝?)
  • 受众特征将如何影响解释?思考文化、语言、教育、数据素养技能等。

不要依赖默认设置。

依赖软件默认值是创建糟糕的可视化效果的一种必然方式。下图显示了不同软件环境的默认设置。你见过多少有这些调色板、字体、字体大小和网格线的图表?数据可视化社区应该禁止默认设置,就像我们讨厌 3D 条形图和饼图一样。

用三种不同的软件环境可视化虹膜数据集。很遗憾,由作者绘制。

如果您是构建数据可视化的新手,请花几分钟时间来欣赏您有意构建的图形(而不是意外点击的图形),尽管只是几分钟。下一个大任务是定制。学习构成图形的不同元素可以让你的工作更有效率。例如,在 Google Sheets 中,您可以双击任何图表元素来打开它的定制面板。这些是我为定制我的大麻图片而调整的元素。

图片由作者提供。

了解网格系统并使用它们!

Josef müller-Brock Mann(1914–1996)是平面设计领域的一位有影响力的人物,他的作品继续激励和影响着世界各地的设计师。他推广了网格系统,这是在视觉交流中组织图形元素的重要辅助手段。

图片来自维基公共。

穆勒-布罗克曼写道:

电网系统是辅助,不是保证。它允许许多可能的用途,每个设计师可以寻找一个适合他个人风格的解决方案。但是人们必须学会如何使用网格;这是一门需要练习的艺术。

我建议避免使用软件工具来构建网格。从简单开始。了解网格系统的概念原理,并手动绘制布局草图。我可以通过绘制不同的形式来快速重复想法。

图片由作者提供。

整本书都致力于网格系统的主题,所以我这里的建议只是您进一步开发的起点。

考虑自然阅读的模式。

想想你的观众会如何消费这些信息。你的眼睛从哪里开始,它们如何在视觉中移动。人们从左到右、从上到下阅读,这使得图表的左上角区域成为最有价值的不动产。这个地方是人们进入可视化的地方。确保对所有的图形元素都应用同样的思想。例如,考虑 Iris 数据集中的以下条形图。图形是一样的。

使用 Iris 数据集创建的图形。图片由作者提供。

在这个例子中,我优化了左边的图表,给条形一个水平方向,因为这是人们阅读的方式——从左到右和从上到下。用户的眼睛必须浏览整个图形,然后从底部到顶部阅读。这个任务对于三个条形来说相对容易,但是当显示许多条形时就非常困难了。

字体和字样

人们通常所说的字体是字样。你选择的字体会影响视觉效果的可读性和色调。如果你想让你的可视化看起来像一个中学艺术项目,那么尽一切办法,使用漫画,并加入一些 3D 栏,使图形看起来有趣。其实请不要这样。永远不会。

使用 Iris 数据集的图形不应由作者创建。但确实如此。

花时间研究不同的字体,了解要解决的问题。做出明智的决定,以确保您的图形可读性,并且色调适合观众和故事。下面的图片是我第一次参加平面设计课程时提交的作业的例子。作业包括给出一种字体的简史和用法。我选择了 Avenir,我发现它非常适合打造清爽干净的造型。

图片由作者提供。

颜色选择

在构建数据可视化时,选择颜色是最具挑战性的问题之一。避免选择颜色使你的可视化看起来有趣。你认为有吸引力的东西可能会与你的观众的信息需求相冲突,并可能使你试图讲述的故事复杂化或扭曲。

用颜色编码尺寸值时,避免刻板思维。用蓝色和粉色来代表性别是一种过于简单化的解决问题的方式,尤其是在我们对性别认同的理解上。对种族值进行编码极具挑战性,通常需要不同的视觉通道来避免刻板的方法或冲突的图形提示。当然,你还必须考虑可访问性问题,以及如何确保色盲的受众群体仍然能够理解图片。

Ishara 色盲测试,显示数字 74。图片来自维基百科。

不幸的是,我无法用一个段落或一篇短文来概括这些大量的信息。所以,我的建议是把这个作为一个课题加入研究。Aseem Kashyap 有一篇关于入门的优秀文章。

</8-rules-for-optimal-use-of-color-in-data-visualization-b283ae1fc1e2>

小心谨慎地使用标题、标题和文本注释。

一个有效的可视化应该尽可能独立。独立的概念意味着最终用户可以清楚地看到所有的信息,从而理解你所展示的内容。他们不应该在网上搜索更多的信息,或者猜测你的缩写或行话的意思。花点时间为你的图片考虑标题,以帮助推动你的故事的叙述。我的大部分工作是学术研究,所以我的标题倾向于描述性的。但是,有时候,对于我的咨询工作,我会使用标题来说明一个关键的要点。

您可以使用文本来创建视觉层次,但这需要一定的对比度。例如,标题应该与正文区分开来。可以通过字体配对实现对比。Esther Teo 有一篇关于这个主题的优秀文章:

https://medium.com/8px-magazine/practical-guide-to-font-pairing-da58b9bcd42b

分类和排序

无论是创建表格还是图形显示,都不要忽视对值进行排序的重要性。大多数图形系统会在显示尺寸时按字母顺序对尺寸进行排序。这可能有用,但可能不是您的用户需要的正确信息。

后处理

当您熟练地创建高度定制的图形时,您将会遇到工具(即软件)不能按照您希望的方式执行的情况。例如,我有一些生疏的 R 技能,但仍然可以使用 ggplot 创建自定义图形。但是,有时,获得正确的注释可能非常耗时。当我遇到这个问题时,我会制作一个 PDF 格式的图像,并在 Adobe Illustrator 中完成我的定制。

如果要进行这种后期处理,请将图形保存为矢量图像(如 PDF 和 SVG),而不是光栅图像(如 JPEG 和 PNG)。您可以使用矢量文件直接访问和定制所有图形元素,包括文本。您可以像处理照片一样处理光栅图像,但这很麻烦。无论何时进行任何后处理,都要确保不要扭曲数据的比例。顺便说一下,您不能只将 JPEG 或 PNG 打印成 PDF,然后期望访问这些元素。

切,切,切

这个建议来自爱德华·塔夫特的工作,他是数据可视化领域最有影响力的人之一。检查每一个图形元素,问问自己它是否有信息价值。它编码信息吗?它组织信息吗?为浏览或消费信息提供视觉提示?使用泰坦尼克号的数据集,我在 Google Sheets 中创建了一个简单的条形图,展示了一种极简主义的方法,如簇绒条

使用 Titanic 数据集用 Google Sheets 制作的图形。作者图文。

当然,我可以很容易地在图片中添加一艘正在下沉的船,但这对我的故事没有帮助。一艘沉入水中的船无法传达我的数据故事。许多默认的图形元素是不必要的。例如,我在条形上提供了精确的值,因此读者不必来回扫描来进行比较。这样,y 轴就不再需要了。我不知道如何消除 y 轴,所以我的黑客是伪装成白色。我添加了颜色来加强视觉上的性别对比,但是不需要图例,因为标题已经给出了信息。

避免添加图形元素来使可视化看起来有趣。数据可视化社区将不必要的图形元素称为图表垃圾。再说一次,所有的事情在你的脑海中可能都是清晰的,但是视觉化并不是为你准备的——而是为你的观众准备的。来自你的数据的故事应该是兴趣点。如果你添加了吸引人的图形元素,一定不要让用户分心。同样,你心中清楚的东西,他们可能并不清楚。

最后的想法

我正在把我所有的课程笔记变成可分享的文章。我的大部分文章都是为新手量身定做的。如果您想收到新出版物的更新,请随时关注我。

从失败的产品实验中吸取的教训

原文:https://towardsdatascience.com/lessons-learned-from-failed-product-experiments-6de39b5eac0e

没有统计学意义仍然可以产生有价值的见解

照片由 PexelsTowfiqu barbhuiya 拍摄

当我第一次成为一名产品分析师时,我以为产品实验与我作为营销数据分析师所做的评估相似——但我错了。当营销 A/B 测试在统计上不显著时,我们继续进行测试,但是在产品 A/B 测试之后,我意识到即使没有统计显著性的测试也能提供有价值的见解。今天,我想回顾一下产品 A/B 测试表现不尽如人意的例子和经验教训。

意外的结果

假设

我们推出了一个新的膳食计划功能,假设它会增加付费(TTP)的转化率。更高的 TTP 费率意味着更多的用户将转化为付费用户,从而增加收入。测试分两组进行,每组 50 人,测试组可以访问新功能,而控制组不能访问。

结果

这项新功能并没有显示出整体 TTP 转换率的提高。这是违反直觉的,因为计划功能是多年来用户最常要求的功能,我期望看到更高的 TTP 率。我决定,在将结果提交给产品经理之前,测试小组需要进一步分析,以了解为什么 TTP 比率是相似的。

我将测试组分成不同的部分,看看 TTP 比率是否有明显的差异。A 组是在开始试用后知道该计划功能的用户,B 组是不知道该功能的用户。我进一步将 A 组分为开始计划的用户和没有开始计划的用户。

作者创建的产品实验细分

在将测试组分成如上所示的几个部分后,我可以看到 TTP 率的显著差异。B 组的 TTP 率与对照组相同,为 30%,因为这些用户不知道存在与对照组类似的计划功能。从未开始计划的 A 组用户有 35%的 TTP 率,相比之下,开始计划的用户有 25%的 TTP 率。为什么这两个 A 组市场有如此大的差异?

由于计划功能长达 28 天,用户忘记了它,也没有体验到如果他们完成了计划会产生的影响。当我们看到低计划完成率时,这一点得到了支持。由于这种糟糕的用户体验,用户在试用结束后不太可能付费是有道理的。对于产品经理来说,这是一个很有价值的见解,可以优先考虑计划通知,以提醒用户完成他们的每周任务,这将转化为更高的完成率和更高的 TTP 率。

现在,为什么知道计划但没有尝试的 A 组用户有更高的 TTP 率?我们假设这表明用户喜欢计划功能,尽管他们没有尝试,但感知价值说服他们在试用结束后付费,他们可以稍后尝试计划。请记住,这是用户高度要求的功能。

外卖食品

  • 测试结果并不总是看起来那样。如果结果出乎意料,请尝试通过不同的操作对用户进行细分,以确定转化率的任何差异。这可以为产品经理提供可操作的见解,因为在本例中,添加计划通知将提高计划完成率,从而提高 TTP 率。
  • 检查是否有可能影响测试结果的外部因素。对于 A/B 测试,我们与产品营销部门合作,推迟向用户通知计划功能,直到我们完成测试。这可能不是你所支持的测试的情况,你应该注意可能的影响。

无意的副作用

假设

过去的分析显示,与没有登录的用户相比,在注册应用程序后登录食物的用户更有可能回来。在注册过程中增加了一项更改,鼓励用户在注册过程完成后记录食物,以提高用户参与度。

结果

该测试运行了 4 个月,但没有显示出用户参与度的任何改善,因此被关闭。大约在同一时间,我们开始看到审判开始下降。我支持订户分析,但不知道这个测试。我的团队不知道为什么试验在减少,直到产品经理假设这可能是由参与度测试引起的。

一旦我们再次打开测试,测试开始回到先前的水平。由于测试已经进行了 4 个月,我们将测试增加归因于业务的增长,而不是一个根本不应该影响测试的测试。结果是产品经理将升级屏幕切换到了测试变体注册过程的另一部分,这导致了试用开始增加。屏幕变化不是故意的,但这是一个有价值的见解,我们知道在哪里放置升级信息,以增加试用开始。

外卖食品

  • 测试可能会对产品经理没有用来衡量成功的其他 KPI 产生意想不到的影响。考虑测试可能影响的所有 KPI,以确保对它们没有负面影响。在我的例子中,参与度没有变化,但对审判开始产生了显著的积极影响。
  • 确认控制和测试变量具有相同的元素,除了被测试的变化。我们的测试很幸运,意想不到的副作用导致试验开始增加,但它可能很容易反过来导致减少。
  • 即使你不支持,也要注意计划中的实验。与产品经理讨论他们的产品路线图,并找出他们计划实施的计划。如果我与订阅之外的产品经理交谈,我可能会更早地了解到参与度测试。

最后的想法

虽然一开始很有挑战性,但作为一名产品分析师,我学到了很多关于产品 A/B 测试的知识。无论您是产品新手还是正在考虑成为产品分析师,我希望这能让您在 A/B 测试体验上有一个良好的开端。感谢阅读!

你可能也会喜欢…

https://medium.datadriveninvestor.com/what-data-analysts-should-know-about-product-analytics-f31f13e66291 [## 关于 A/B 测试,产品分析师应该知道什么

towardsdatascience.com](/what-product-analysts-should-know-about-a-b-testing-a7bdc8e9a61)

制作人工智能歌剧的经验教训

原文:https://towardsdatascience.com/lessons-learned-from-making-an-ai-opera-6b188c3094cf

在开始你的下一个歌唱声音合成项目之前,请阅读这篇文章

你好 TDS 我会唱歌剧。

你有没有想过在德国最负盛名的舞台之一上演一出艾歌剧需要什么?如果没有,当你读到这句妙语时,你会不会感到奇怪?这篇文章将让你了解在我们为有史以来第一部由人工智能扮演主要角色的专业歌剧制作歌唱声音合成(SVS)系统的过程中所学到的经验。《追逐瀑布》于 2022 年 9 月在森佩罗珀德累斯顿上演。这篇文章与其说是一个有凝聚力的故事,不如说是一个我们容易陷入的陷阱的集合,它的目标读者是之前对 TTS 或 SVS 系统有所了解的人。我们相信错误是值得分享的,而且实际上比现成的东西更有价值。但首先,我们说的艾戏是什么意思?

追逐瀑布的场景

什么是追逐瀑布

简而言之,《追逐瀑布》试图上演一部以人工智能为主题的歌剧,将人工智能用于视觉和听觉元素。具体来说,这部歌剧是由 6 名人类歌手和一个歌唱声音合成系统(“AI voice”)组成的,它们与人类管弦乐队和电音场景一起表演。除了整个歌剧中由人类创作的外表,还有一个场景是人工智能角色应该自己创作的。在这篇文章中,我们只关注歌声合成,因为这是我们在 T-Systems MMS 的任务。作曲人工智能是由艺术家集体克林巴生龙基于 GPT-3乐谱转换器构建的。

《追逐瀑布》是艺术家集体phase 7 performing . Arts Berlin与 Semperoper Dresden 和香港新视野艺术节的合作作品。克林·巴生·克朗和安格斯·李一起,也负责歌剧的人类组成部分(完整的贡献者名单)。

我们的要求是为未知的乐谱和文本合成一个令人信服的歌剧声音,这些乐谱和文本是歌剧的一部分。此外,我们的任务是满足项目期间出现的艺术需求。最终的架构基于 HifiSingerDiffSinger ,其中我们使用了一个根据 HifiSinger 的想法调整的变压器编码器-解码器,并结合了一个浅扩散解码器和作为声码器的 Hifi-GAN 。我们使用全局风格标记进行控制,并通过蒙特利尔强制对齐器获得音素对齐。在令人惊叹的 Eir Inderhaug 的帮助下,我们记录了自己的数据集。我们在三个存储库中发布我们的代码,一个用于声学模型,一个用于声码器,一个用于基于浏览器的推理前端。为了使您能够试验我们的工作,我们为 CSD 数据集格式添加了预处理例程,但是请注意,CSD 数据集不允许商业使用,并且包含流行歌手演唱的儿童歌曲,因此不要期望在对该数据进行训练时获得歌剧声音。

成功了吗?嗯,对这部歌剧的整体评论褒贬不一,有些人感到惊讶,有些人称之为“嘀嗒嘀嗒”上的“T2 诱饵”。艺术评论很少详细讨论 SVS 系统的技术质量,除了在 tag24.de 中的一个声明,我们粗略地翻译了一下:

然后,中心场景。当化身睡觉时,人工智能在发短信、作曲和唱歌。[……]但是:那个时刻并不特别壮观。如果你不知道,你不会承认人工智能的工作。

这基本上是我们能得到的最好的赞美,意味着我们主观上符合人类的表现,至少对这位评论家来说是这样。我们仍然看到 AI 偶尔会错过一些辅音,音符之间的过渡有点起伏。质量当然可以提高,但这需要更多的数据、时间和硬件。利用我们现有的资源,我们设法训练出一个在专业歌剧舞台上并不完全格格不入的模特。但是,你自己判断吧:

声音样本

听起来怎么样?这里有一些偷偷摸摸的峰值,你可以在我们的 github 库中看到它们。

  • 你好 TDS 我会唱歌剧。musicautobot 生成的旋律,我为《走向数据科学》这篇文章手写的文字。
  • 我出生在一个 0 和 1 的世界,一个上是下,左是右的世界。我是我的同类中的第一个,一个数字生命形式。文字由 GPT 3,旋律由 musicautobot,解释由彩信歌手。
  • 我出生在一个由 0 和 1 组成的世界,这是一个具有挑战性的例子。

数字中的项目

如果你正在计划下一个深度学习项目,这一部分主要是有趣的。我们的项目持续时间从 2021 年 11 月到 2022 年 8 月,这部歌剧的首演在 9 月。我们在五月份就准备好了我们的数据集,所以有效的实验发生在五月到八月。这次,我们在专用硬件上训练了 96 种不同的声学模型配置和 25 种声码器配置。我们工作的机器有 2 个 A-100 GPU、1TB RAM 和 128 个 CPU 内核,并且基本上一直忙于训练一些东西,我们安排我们的实验以充分利用我们可用的硬件。因此,我们估计本项目的能源消耗约为 2MWh。最终训练花费 20 小时用于未预训练的变压器 AM,30 小时用于也未预训练的扩散解码器,120 小时用于预训练 LJSpeech 上的声码器,10 小时用于微调声码器。为了进行推断,我们需要大约 6GB 的 GPU RAM,整个管道的实时系数为大约 10,这意味着我们可以在 1 秒的 GPU 时间内合成 10 秒的音频。我们的数据集由 56 个片段组成,其中 50 个出现在 3 种不同的解释中,总计 156 个片段和 3h:32m 的音频。

时间对齐与乐谱 MIDIs

在文献中,时间对齐的 midi 和乐谱 midi 之间没有明显的区别——这意味着什么?对于 FastSpeech 2 的训练,音素对齐是通过蒙特利尔强制对齐器获得的,参见第 2.2 节,它也用于我们的持续时间预测器训练。 FastSpeech 1 从师生模型中获得这些校准, HifiSinger 使用 nAlign ,但是本质上类似 FastSpeech 的模型需要时间校准信息。不幸的是,音位被演唱的时间实际上不能与活页乐谱的时间相比。

  • 在某些情况下,由于演唱者添加的节奏变化或音符前后不久的辅音,音符时间跨度和音素实际演唱的位置之间没有时间重叠。
  • 在乐谱中,呼吸暂停通常是不被注意的,因此演唱者把它们放在音符中,通常在结尾。
  • 如果音符不是以连贯的方式唱出来的,音素之间就会出现小的停顿。
  • 如果音符是以一种连接的方式唱的,一个音符的结束和下一个音符的开始并不十分清楚,特别是如果两个元音相继出现。

这些差异对数据输入模型的方式提出了质疑。如果时间对齐的信息被直接用作训练数据,则该模型不能演唱活页乐谱,因为在推断期间中断和定时丢失了。如果活页乐谱定时被用作训练数据,音素级持续时间预测器目标是不清楚的,因为只有音节级持续时间存在于活页乐谱数据中。处理这个问题有两个基本方法。如果存在足够的数据,直接将音节嵌入馈送到模型应该产生最好的结果,因为训练持续时间预测器变得不必要(音节持续时间在推断时是清楚的)。在我们可用的数据量有限的情况下,训练音节嵌入是不可能的,所以我们选择使用音素嵌入并预处理数据以尽可能接近乐谱。首先,我们移除由校准器检测到的清音部分,这些清音部分在乐谱中没有对应的清音部分,以防止持续时间预测器目标中的间隙。我们扩展相邻音素,以保持音素的相对长度不变,并跨越产生的间隙。没有被对齐器标记的音素在它们应该出现的部分的中间获得默认长度。很长的音素和音符被分割成多个较小的音素和音符。

持续时间预测器训练

FastSpeech 1 建议在对数空间中训练持续时间预测器:

我们在对数域中预测长度,这使得它们更高斯,更容易训练。

(见 3.3 节快速语音)。现在,这给出了如何实现的两个选项,要么在损失计算之前对持续时间预测器输出求幂,要么将目标变换到对数域:

  1. mse(exp(x), y)
  2. mse(x, log(y))
  3. 不要在日志空间中预测

ming024 的 FastSpeech 实现使用选项 2,而 xcmyz 的实现根本不像选项 3 那样在日志空间进行预测。论点是,对数空间使持续时间更高斯,事实上,如果我们看一下分布,原始持续时间看起来更像泊松,而在对数空间看起来更接近高斯。

从数学上来说,选项 1 不会使 MSE 计算更加高斯化,因此不会减轻偏差,在这种情况下应该没有意义。有 MSE 损失的训练应该使选项 2 成为更有利的选项,而选项 1 应该大致等同于选项 3,除了输出层中更好的数值稳定性。根据预期,我们发现持续时间预测器在选项 2 中具有更好的确认损失和更少的偏差,但是令人惊讶的是,在选项 1 中生成的语音的主观整体质量更好。似乎有一个有偏差的持续时间预测器是一件好事。这仅适用于激活的音节引导,其中跨音节的持续时间预测器的误差被校正以从乐谱中产生精确的音节持续时间。一种可能的解释是,这种偏向倾向于一般较短的辅音,这对于语音理解是必不可少的,并通过这种方式提高整体质量。我们没有进行 MOS 研究来证明这一点,主观判断只是基于我们和我们合作的艺术家的看法,所以这取决于读者自己的实验。然而,我们认为这是未来 SVS 出版物的一个有趣的问题。选项 1 和 3 实际上没有太大的区别,除了我们在选项 3 上遇到了严重的渐变裁剪,因此选择了选项 1。

当地的关注

我们需要在推理过程中合成至少 16 秒的片段,以与合成人工智能兼容。然而,在全球关注的 16s 片段上的训练耗尽了我们的硬件预算,以至于训练变得不可行。瓶颈是注意机制的二次复杂度与 HifiSinger 推荐的大约 5 毫秒跳跃大小的高 mel 分辨率相结合。因此,解码器必须形成超过 4000x4000 个元素的注意矩阵,这既不适合 GPU 内存,也不会产生合理的结果。在线性复杂度注意力模式的简短实验后,解决了硬件瓶颈,但仍然没有产生合理的结果,我们切换到解码器中的局部注意力。我们不仅获得了合成更长片段的能力,还提高了整体主观质量。在将编码器切换到局部注意力后,我们可以看到主观质量的另一个提高。

对我们来说,这很有意义。在片段上训练全局注意机制使其成为片段局部注意机制。这意味着在剪贴边界上从来没有计算过注意力。实际上,使用局部注意力意味着每个标记总是能够在两个方向上注意至少 N 个标记,其中 N 是局部注意力上下文。此外,一个标记不能超过 N 个标记,这在语音处理的情况下是有意义的。虽然像歌唱风格这样的特征可能跨越多个记号,但是用于生成 mel 帧的大部分信息应该来自此时所唱的音符和音素。为了融入演唱风格,我们采用了 GST,甚至降低了需要广泛注意力的信息量。限制关注窗口使这一点变得明确,该模型不必知道关注矩阵应该是非常对角的,因为它在技术上被约束来创建至少某种对角。因此,我们观察到质量的改善,并建议当地的关注,作为 TTS 和 SVS 系统的可能改进。

调整全局样式标记

在与我们的艺术家同事的互动中,很明显,艺术家希望对模型合成的内容有某种控制。对于一个正常的歌剧演唱者来说,这是通过排练时指挥的反馈来体现的,其形式包括“少唱这一部分的拘束”,“78 到 80 小节里更多的绝望”等。虽然能够尊重文本反馈会很好,但这本身就是一项研究工作,超出了项目的范围。因此,我们必须实现不同的控制机制。我们考虑了三种选择:

  1. 类似 FastSpeech 2 的方差适配器(见第 2.3 节),使用提取或标记的特征向解码器提供额外的嵌入
  2. 一种无监督的方法,如全局样式标记,它通过从 mel 目标提取的特征训练有限数量的标记,这些标记可以在推理过程中手动激活
  3. 采用文本标签提取情感信息的半监督方法。

选项 1 和 3 都需要额外的标记工作,或者至少需要复杂的特征提取,因此我们首先尝试了选项 2。我们发现商品及服务税提供了合理的结果,满足了改变某些东西的要求,尽管控制水平低于预期。当被训练产生四个表征时,我们始终有至少两个表征表示不想要的特征,如喃喃自语或失真,并且这些表征通常对推理过程中的小变化非常敏感。我们相信更多的数据可以缓解这些问题,因为无监督的方法通常需要大量的数据来工作,而我们没有这些数据。

你可以自己听听,还记得那个样本你好 TDS 我会唱戏吗?这里的 都是为它 改编以不同风格的信物。此外,我们可以合成同一个片段的多个版本,将随机噪声添加到样式标记中,以创建一个合唱团

版权法令人讨厌

尤其是对于音乐。我们有两个问题,我们不确定哪些歌曲可以用于模型训练,以及模型经过训练后属于谁。

尚不清楚哪些数据可用于训练 SVS 模型。这里有多种可能的裁决,最极端的是,你可以自由地使用任何音频来训练 SVS,或者在另一个方向上,数据集的任何部分都不能拥有任何版权,无论是作曲还是录音。一个可能的中间立场是,使用重新录制的作品并不侵权,因为最终的 SVS 如果不是过度拟合,将不会记住具体的作品,但会反映录音中歌手的音色。但到目前为止,我们还不知道德国法律中任何合理的法院裁决,因此我们采用了最严格的版本,并使用了一位专业歌剧演唱家录制的免版税作品,该演唱家同意将这些录音用于模特训练。再次感谢 Eir Inderhaug 的精彩表演。

此外,我们不得不问这样一个问题,谁将是模型输出和模型本身的有版权资格的所有者。可能是歌手将他们的歌曲作为训练的数据,可能是我们训练了模型,没有人,或者完全出乎意料的东西。在与我们公司的多位法律专家交谈后,我们得出的结论是:没有人知道。模型和推理输出属于谁仍然是一个公开的法律问题。如果法院裁定数据集的创建者总是拥有模型的最大所有权,这将意味着你和我可能拥有 GPT-3 ,因为它是根据整个互联网的抓取数据进行训练的。如果法院裁定数据集的创建根本无权对所有权进行建模,那么就没有法律途径来阻止 deepfakes。未来的案例可能介于两者之间,但由于我们在德国法律中没有足够的先例,我们假设了最糟糕的裁决。然而,对于依赖于爬行数据集的机器学习项目,这是一个巨大的风险和可能的交易破坏者,应该在项目开始时进行评估。尤其是音乐版权出现了一些极端裁决。希望法律状况在中期内会稳定下来,以减少不确定性。

多方面的

22 千赫的 hifi-gan 不能在 44 千赫的音频上工作。这是不幸的,因为有大量 22khz 的语音数据集可用于预训练,但即使在 22khz 预训练时在 44khz 上微调也绝对不起作用。这是有意义的,因为卷积突然以两倍的频率看到所有东西,但这意味着我们必须对声码器的预训练数据集进行上采样,并从空白模型开始,而不是使用互联网上的预训练模型。这同样适用于改变 mel 参数,当我们调整 mel 频率下限和上限时,需要全新的训练。

检查你的数据!这一课基本上适用于任何数据科学项目,长话短说,我们在标签不好的数据上浪费了很多时间。在我们的例子中,我们没有注意到标注的音符与歌手产生的音符是不同的音高,这是由于混淆了乐谱文件而发生的错误。对于没有完美音高听觉能力的人来说,这种差异不会立即显现出来,对于与我们合作的艺术家相比音乐文盲的数据科学家团队来说,这种差异就更不明显了。我们之所以发现这个错误,是因为其中一个样式标记学会了表示音高,但我们不知道为什么。在未来的项目中,我们将设置明确的数据审查,领域专家根据甚至是意想不到的可能错误来检查数据。一个很好的经验法则是,如果你花不到一半的时间直接处理数据,你可能过度关注架构和超参数。

尤其是在项目开始时,与技术选择大相径庭。早期,我们发现了MLP·辛格,这似乎是一个很好的启动系统,因为在那个时候,它是唯一一个具有开源代码和可用的 CSD 数据集的深度 SVS。当我们得知将它改编成歌剧可能比在 HifiSinger 的基础上实现一些东西更费力的时候,我们已经决定将这种格式和类似的歌曲用于 CSD 数据集。然而,如前所述,这种格式和歌曲的选择有其缺陷。如果我们在早期花更多的时间批判性地评估数据集和框架选择,而不是专注于获得一个工作原型,我们就可以避免被锁定在这种格式以及随之而来的麻烦。

结论

这是一个非常实验性的项目,需要学习很多东西,在制作过程中,我们作为一个团队成长起来。希望我们能分享一些经验。如果你对这部歌剧感兴趣,你可以选择在 2022 年 11 月 6 日之前在香港看这部歌剧。如果您对更多信息感兴趣,请通过邮件(Maximilian)联系我们。我是耶格·t-systems.com,罗比。弗里奇·t-systems.com,尼科。韦斯特贝克 t-systems.com),我们很乐意提供更多的细节。

让数据科学为你做出选择

原文:https://towardsdatascience.com/let-data-science-make-choices-for-you-5702bd759803

多臂强盗入门

照片由上的爆裂未爆裂

想象一下,你必须从一组结果不确定的互斥选项中做出多个选择。你如何最大化你的收益?

反复的过程,一次又一次的选择,不是抽象的东西,而是我们每天都在做的事情:我们吃什么?给定时间和交通状况,我们走哪条路回家?我们如何最大限度地减少花在路上的时间?我应该去我的城市的什么餐馆?

这些问题由一类称为多臂强盗(MAB)的算法解决,这是强化学习 (RL)算法的一个子领域,旨在通过在没有监督的情况下执行行动,随着时间的推移做出更好的选择。

让我们一起来回顾一下最常见和最简单的算法,从具体的例子开始,逐步发展到数学形式化。在我最近对 RL 感兴趣之前(近两年),我对它知之甚少。在博士期间,我学习的书籍主要集中在监督和非监督学习上,强化学习要么被简单提及,要么被彻底抛弃。只是在最近几年,很大程度上由于深度思维的创新,RL 正在成为一个更相关和研究的领域,我个人对此非常感兴趣。MAB 算法是这个迷人领域的第一步,我想你个人会对它们很感兴趣,而且很快也会变得专业。

代理人、奖励和环境

在强化学习中,我们有一个代理( A ),它必须在一个环境中执行动作。通过采取行动,代理获得奖励或受到处罚,随着时间的推移,确保最大奖励与特定环境中代理的最佳行为相关联。没有训练数据——代理通过反复试验和交互过程来学习。

这个领域中最简单的问题之一(多臂强盗问题的例子就是由此而来)是吃角子老虎。开始游戏需要支付一定的费用,玩家从一组 n 选项中选择一个手柄即可获得奖励。游戏的目的是选择正确的杠杆,最大化总回报,这可以通过使用 MAB 算法来实现。

让我们用老丨虎丨机的相同概念和一个更简单的例子来解释 MAB 算法试图解决什么样的问题。

一个例子

假设我们有四个按钮可供选择,分别标为 A、B、C 和 d。每个按钮按下时都会释放不同的奖励。

我们还假设我们可以按四个按钮的次数是有限制的,比如说 100 次。并且每个按钮每次都给出相同的奖励:A 总是给出 1,B,2,C,3 和 D 4 的奖励。

最大化我们总回报的策略是什么?

我们可以选择的两个最简单的策略是

1.随机选择一个按钮,并坚持选择。这意味着我们只有 25%的机会选择 D 并最大化我们的奖励,如果有更多的按钮,这个概率会更低

2.每次随机按键。这里的结果是每个按钮的平均结果乘以 100。如果我们重复实验,我们的结果总是一样的,但永远不会是最好的。

这两种选择在所谓的开采-勘探困境中处于对立的两端。对于第一个,我们只做了一个决定,并坚持下去(只有利用了)。另一方面,我们只探索了,而没有使用通过其他选择获得的洞察力。

显然,我们必须在探索最佳选择和利用最佳选择之间取得平衡。

这里有四种算法试图达到开发-探索的平衡:

  1. ε-贪婪算法
  2. 乐观的初始值
  3. 梯度 bandit 算法
  4. 置信上限(UCB)动作选择

1.ε-贪婪算法

继续上面的例子,我们有 4 个固定奖励的按钮。

在这种情况下,策略很简单:每个按钮尝试一次,并一直选择奖励最高的按钮,直到结束。

四次探险和 96 次剥削。总回报是 1+2+3+4*97=394,而最高分是 400。理论最高分与回报之差称为后悔

假设奖励不是固定的,但是每个按钮都关联着一个正态分布的奖励。你会如何在最大化回报的同时解决这个问题?

ε-贪婪算法采用了这种解决方案:以概率按下奖励最高的按钮,选择一个概率为 1-ε的随机按钮。同时跟踪结果并计算每个选择的平均奖励。

1.将每个按钮的平均值设置为零
2。按下每个按钮一次,并记录其值。
3。继续到最后:
a .用概率选择平均值(贪心)最高的按钮,否则任意一个按钮(注意,这个阶段甚至可以按下值最高的按钮)。
b .更新与每个按钮相关的平均值。c .更新总回报

用这种方法,系统将很快知道哪个分布提供最高的平均报酬,并且它将选择它ε+1-ε/n 次。

与每个行动相关的平均回报被称为行动值

也有可能有一个随时间增加的灵活性,因此它只选择最佳选项,停止任何探索过程。

例如,对于 90%,我们示例中的最佳按钮选择为 90+100–90/4 = 92.5%。过一会儿,这可能会增加到 100%。

当然,只有当分布不随时间变化时,这种策略才有意义,因为如果它们发生变化,最佳选择可能会改变。

2.乐观初始值

在ε-greedy 算法中,我们看到每个按钮的第一个值被认为是第一个平均值,它通常被设置为零。

最初的零值策略会给整个过程带来偏差的风险。我们看到,ε-贪婪算法在第一轮探索后立即开始选择最高平均值。但是,如果一个分布具有很大的标准差,并且它的第一个值与真实平均值相差很远,会发生什么呢?

在这种情况下,系统需要一段时间进行调整,如果这个具有较大标准差的假设分布的真实均值接近最高平均分布,调整会更加困难。

在达到我们可以按下按钮的最大时间之前,系统可能找不到最佳选项。这种对进程的限制被称为事件

为了解决这个问题,我们可以将初始值设定为一个远大于我们的回报的值,这个值被称为乐观值,因为我们将初始值设定为远大于真正的强盗行动值。

在该图中,我们可以看到乐观算法与ε-贪婪算法的对比示例。后者开始利用它发现的最佳行动,而乐观者在探索阶段花费更多的时间来更好地了解最佳行动

无论最初选择哪种行动,回报都小于我们的乐观值,因此得到的平均值将小于其他值。因此,在值估计收敛之前,所有动作都要尝试几次,并且该方法必须进行更长时间的探索,因为具有最高平均值的动作经常改变,并且需要很长时间来选择具有一致的更高值的动作。

3)置信上限动作选择

正如我们提到的,任何ε-贪婪算法都在当前时间步采取最佳行动,只考虑该点的最高平均值(行动值估计)。但是,它没有考虑估计的不确定性。

在开发和探索之间的权衡中,贪婪算法给出了一种选择非贪婪动作的方式,但是它的选择在贪婪和非贪婪动作之间是随机的。

相反,置信上限动作选择(UCB)试图评估相对于其成为真正最优动作(面对不确定性时乐观)的潜力而言不具有最高值(非贪婪)的动作。这是通过以下公式实现的,该公式考虑了动作值估计和这些估计的不确定性:

置信上限动作选择公式。对于 Qt(a),a 的最大自变量加上超参数 c,探索度时间 t 的自然对数的平方根除以在时间 t 之前已经选择的动作的次数。Qt(a)表示等式的 剥削 部分,选择的动作将是具有最高估计奖励的一个。等式的第二部分代表探索方,这为行动值提供了不确定性的度量。[图片由作者提供]

每次采取行动时,行动值估计值都会变得更准确(Qt(a)更接近真实值)。如果 Nt(a) 增加,被选中用于勘探目的的概率会降低,尽管仍有可能被选中用于开发。对于未选择的动作,等式左侧的值缓慢增长,直到其中一个动作触发另一个探索动作。

UCB 可以判断什么时候探索,什么时候开发,并且在寻找最佳行动方面相当快。由于这个原因,UCB 是最有效的算法之一,我们将在实验部分看到。

4)梯度 bandit 算法

到目前为止,我们已经看到了基于动作值来估计要采取的动作的方法。还有其他可能的不同方法,其中之一是使用软最大分布(即吉布斯或玻尔兹曼分布),如下所示:

渐变土匪公式(soft-max)更多信息 此处 。[图片由作者提供]

Soft-max 函数计算所有动作的概率分布,我们可以选择得分最高的动作。

您可能已经在神经网络领域中遇到过软最大分布的概念,因为它经常在输出层每个类有一个节点时使用。

要解释 GBA 背后的数学原理,可能需要另一篇比这篇文章更长的文章。我仍然选择把它放在这里,因为我想表明使用平均奖励值作为行动值并不是唯一可能的选择。

总结和测试

在这里,我们看到了最流行的多臂土匪问题的算法,探索不同的方法来探索与开发的困境:

  1. 仅探索,它在每个时间步选择一个随机动作
  2. 仅利用,第一次选择一个随机动作并重复
  3. ε-贪婪方法,随机选择一小部分时间或者探索
  4. 乐观,迫使系统有更长的探索初始阶段。
  5. UCB 确定性地选择,但通过偏好接收到较少样本的动作来实现探索。
  6. GBA 是一种基于概率分布的完全不同的方法。

正如您可能已经发现的那样,没有一种方法对所有可能的场景都是最好的。UCB 可能是使用最广泛的,但这并不能保证。

然而,我认为最好用我给出的第一个例子来展示这些算法的行为,我已经对 75 个步骤进行了 250 次实验,这是结果。

我已经为 epsilon 选择了 0.75 的值作为每种方法的中间点。UCB 的 c 是 2,乐观的初始值是 5。标准差为 1。【作者图片】

GBA 只能在 25%的情况下找到最佳操作——这有点令人惊讶,所以在这种特定的场景中使用它可能是一种糟糕的算法。

表现最好的工具是 UCB,它能在很短的时间内找到最佳动作,并一直跟踪到剧集结束。乐观算法利用早期探索阶段并找到 t 个最优动作,而贪婪算法需要更多的时间步骤。

只有勘探和开采有 25%的最佳行动被预测。

250 个实验的每个时间步的平均回报。[图片由作者提供]

类似地,如果我们看每个时间步的平均奖励,UCB 得到最高的奖励稳定。乐观一开始有个学习曲线,贪婪滞后。

把这一切都编码起来

MAB 和这四种算法只是强化学习研究中一个漫长旅程的开始。

因为这是 RL 的一个复杂子领域,我决定创建一个最重要算法的 python 实现,你可以在这里找到:https://github.com/MattiaCinelli/AlgoRL

在本文中,我没有提到 Thompson 的采样(针对 Bernoulli 或 Gaussian 分布)或 Bayesian 方法,这些方法可以在代码中找到,我鼓励您尝试一下。

欢迎大家参与并改进代码,如果您有任何问题,请随时联系我们。

来源

  1. 强化学习:导论。莎顿和巴尔托。第二版。
  2. 米格尔·莫拉莱斯探索深度强化学习。

让机器人自动处理你的数据

原文:https://towardsdatascience.com/let-the-bot-automate-your-data-processing-e6ef26959d65

使用 RPA、PyAutoGui 和 Pandas 来避免在 SAP 和您的数据仓库中进行重复的手动工作

戴维·莱夫克在 Unsplash 上拍摄的照片

背景:

您是否经常从 SAP 或其他 ERP 系统中导出数据?然后,在导入数据仓库之前,您是否会花一些时间处理数据?之后,你不会忘记进行一些数据验证检查,如果你的任何可信度检查触发了警报,你会给同事发电子邮件吗?最后,您必须将这些数据填写到 web 表单中吗?在这篇文章中,你将找到如何自动完成这些任务的解决方案,这样你就可以专注于人类应该做的事情:享受机器人手中的新鲜混合饮料并放松!

解决:

假设我们必须导出:

GuiAutomate 和 RPA 使用计算机视觉在屏幕上定位像这个导出按钮这样的按钮。

..使用 SE16N 每周一次在 SAP 中创建表格:

除了图像识别之外,RPA 甚至可以使用 OCR(光学字符识别)。

的另一篇文章中,我们学习了如何使用 SAP 的默认脚本实现自动化。但是,由于贵公司的 IT 法规,有时您将无法访问此功能。在这种情况下,PyAutoGui 和 RPA 都非常有助于避免重复相同的任务。

让我们从从 SAP 导出数据开始:

**import** pyautogui
**import** opencv *# is needed to use confidence in PyAutoGui for image recognition*
**import** subprocess
**import** time
**from** timeit **import** default_timer **as** timer *# to log the time it takes the bot for each step*
**import** getpass *# to get the user name*

username **=**getpass**.**getuser() *# to log the user name on which system the bot is running*

start **=** timer() *#start the timing*

subprocess**.**Popen("C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe") *# open your desktop application, eg SAP*
time**.**sleep(3) *# using any kind of gui scripting requires to wait until application screens are fully loaded*

SAP 开始屏幕现已打开。现在,我们希望机器人找到并点击“SE16N”交易:

Customse16n **=** pyautogui**.**locateOnScreen('Custom SE16N.png',confidence**=**0.8) *# let PyAutoGui search the image on the screen; image must be at least very similar when using confidence 80%. Use confidence 1 when image must match 100%.*
pyautogui**.**moveTo(Customse16n) *# moves your cursor upon this button*
pyautogui**.**click(clicks**=**2) *# double click on the button on which the mouse cursor is now located*
time**.**sleep(3) *#give a three seconds break within the code to wait for the desktop application to load its programme*

在下一个屏幕中,机器人将输入我们想要导出的表名。我们将获得一个之前保存在 SAP 中的变量,运行该事务并将结果保存为文本文件:

pyautogui**.**typewrite('WhatEverSapTable', interval**=**0.001)  *# text entering with 0.001 seconds in between entering each character*
pyautogui**.**press('enter') *# presses the enter button*
pyautogui**.**keyDown('fn') *# holding FN down so F8 can run on my marc*
pyautogui**.**press('f8') *# execute your SAP transaction with F8 works only if you also press FN, see above line*
time**.**sleep(3)

pyautogui**.**press('f6') *#now getting variant in SAP*
time**.**sleep(3)
pyautogui**.**typewrite('WhatEverSapTable variant', interval**=**0.001)
pyautogui**.**press('enter')
time**.**sleep(3)
pyautogui**.**keyDown('fn')
pyautogui**.**press('f8') 
time**.**sleep(10) *# getting your table via SE16N might take some time in SAP*exportresult **=** pyautogui**.**locateOnScreen('export.png',confidence**=**0.8) *# click the export button to save the results*
pyautogui**.**moveTo(exportresult)
pyautogui**.**click()
time**.**sleep(3)

pyautogui**.**move(0, 140) *# from this export button, we now move the cursor 140 pixel down to "Local File" to save it as text*
time**.**sleep(3)
pyautogui**.**click()
time**.**sleep(10) *# might take some time depending on the amount of data*
pyautogui**.**press('enter') *# press enter to confirm*
time**.**sleep(10)

pyautogui**.**typewrite('contacts.txt', interval**=**0.001) *# saves the file contacts as txt*
time**.**sleep(3)
pyautogui**.**press('enter')

这只是使用 SAP 的一个基本例子。但是当然,您可以很容易地将这些代码应用到您想要从中提取数据的任何系统中。顺便说一下,我们还将为机器人为我们执行的每个步骤编写一个日志文件。知道机器人每一步花了多长时间总是好的(我们会花多长时间?):

end **=** timer() *# end the timing*
timestr **=** time**.**strftime("%Y%m%d-%H%M%S")
**with** open('LogBot.txt', 'a', encoding**=**'utf-8') **as** f: *# 'a'instead of 'w', so current log will be added, not overwritten*
    f**.**write(' > ' **+** 'BotExport: ' **+**timestr  **+** ': Bot for ' **+** username **+** ': %0.2fs'**%** (end **-** start)**+**'.' **+** '\n')

记录我们的机器人为我们工作的持续时间。

我们的导出文件如下所示(你会在我的 Github 中找到这个 txt 文件):

我们将只考虑有效的联系人(列 valid = y)。然后,我们需要按公司名称对这个数据帧进行分组,并使用 enumerate 为文件名中包含公司名称的每个组生成一个文件。这是我们一位同事的要求(出于学习目的):

**import** pandas **as** pd

df **=** pd**.**read_csv('contacts.txt', delimiter **=** "\t")

*# create a df including only valid contacts*
ValidDf**=**df**.**query('Valid == "y"') 

*# group per Company Name and save each group as xlsx attaching the Company Name to the file's name*
**for** i, (name, group) **in** enumerate(ValidDf**.**groupby('Company Name')):
    group**.**to_excel('ValidContactsPerCompany {}.xlsx'**.**format(name),index**=False**) 

所有公司名称被分组并分别导出为 Excel 文件。

然后,我们将这个清理后的数据帧导入到我们的 SQL Server 数据仓库中:

**import** pyodbc *# to write data to data warehouse instead of text or Excel files*

YourTableName **=**'YourTableNameIncludingLayer'

conn **=** pyodbc**.**connect('Driver={SQL Server};'
                      'Server=YourSqlServer;'
                      'Database=YourDatabase;'
                      'Trusted_Connection=yes;')
*# Create Table*
cursor**.**execute('CREATE TABLE YourTableNameIncludingLayer ([First Name]  nvarchar(255),[Last Name]  nvarchar(255),[Company Name]  nvarchar(255),[Role in Company]  nvarchar(255),[Address]  nvarchar(255),[Email] nvarchar(255), [Phone Number]   nvarchar(255), [Valid] nvarchar(255))')

insert_to_tmp_tbl_stmt **=** f"INSERT INTO {YourTableName} VALUES (?,?,?,?,?,?,?,?)" *# one ? for each column*
cursor **=** conn**.**cursor()
cursor**.**fast_executemany **=** **True**
cursor**.**executemany(insert_to_tmp_tbl_stmt, ValidDf**.**values**.**tolist())
cursor**.**commit()
cursor**.**close() 
conn**.**close()

在那里,我们与另一个表进行比较,看看是否真的包括了所有公司名称。因为这可能是由于糟糕的主数据,不是所有当前有效的公司都包括在我们的列表中,这是我们刚刚从 ERP 中导出的。机器人应该确保我们不会遗漏任何未被发现的东西:

**import** glob *# glob module is used to retrieve files or pathnames matching specified patterns*

path **=** r'C:\Users'
all_files **=** glob**.**glob(path **+** "\ValidContactsPerCompany*.xlsx") *# use all Excel files which strings starts with ValidContactsPerCompany. Certainly you can also just use the* ValidDf, but again this is mostly for learning purposes on how to deal with different df generating techniques.

li **=** [] *# empty list that will be filled below*

**for** filename **in** all_files:
    DfFromFiles **=** pd**.**read_excel(filename) 
    li**.**append(DfFromFiles)

ValidDfFromManyFiles**=** pd**.**concat(li, axis**=**0, ignore_index**=True**) *# concat the list into one df*

根据基准表运行数据质量检查:

CompanyInScope**=** pd**.**read_excel(r'C:\Users\CompanyInScope.xlsx')
ValidDfFromManyFilesComparison**=**ValidDfFromManyFiles**.**loc[ValidDfFromManyFiles['Company Name']**.**isin(CompanyInScope['Company Name'])] 
*# look if there any company names listed which do not exist in our export*
CompanyExpected **=** pd**.**Index(CompanyInScope['Company Name'])
CompanyExported **=** pd**.**Index(ValidDfFromManyFilesComparison['Company Name'])
*print(CompanyExpected.difference(CompanyExported).values)*

公司名称 8 在我们的导出列表中不存在。

目前,我们不知道缺少公司名称 8 是否真的是一个问题。我们的机器人将通过邮件通知它的人类同事,因此她可以检查这是否是由于糟糕的主数据维护:

FindOutlookPath **=**r"C:\Program Files (x86)\Microsoft Office\root\Office16\OUTLOOK.EXE"
FindOutlook **=** subprocess**.**Popen(FindOutlookPath)
time**.**sleep(3)

NewMail **=** pyautogui**.**locateOnScreen('Neue.png',confidence**=**0.8) *# Neue stands for New in German*
*#print(NewMail)*
pyautogui**.**moveTo(NewMail)
pyautogui**.**click()

time**.**sleep(1)
To **=** pyautogui**.**locateOnScreen('An.png',confidence**=**0.8) *# An stands for To in German*
*#print(To)*
pyautogui**.**moveTo(To)
pyautogui**.**move(90, 0) *# 90 pixel to right to enter address*
pyautogui**.**click()
pyautogui**.**typewrite('YourColleagueMailAddress', interval**=**0.001)

time**.**sleep(1)
Cc **=** pyautogui**.**locateOnScreen('Cc.png',confidence**=**0.8)
*#print(Cc)*
pyautogui**.**moveTo(Cc)
pyautogui**.**move(90, 0) *# 90 pixel to the right*
pyautogui**.**click()
pyautogui**.**typewrite('YouOnCc', interval**=**0.001)time**.**sleep(1)
Subject **=** pyautogui**.**locateOnScreen('Betreff.png',confidence**=**0.8) *# Betreff stands for Subject in German*
*#print(Subject)*
pyautogui**.**moveTo(Subject)
pyautogui**.**move(90, 0) *# ,move 90 pixel to the right for address*
pyautogui**.**click()
pyautogui**.**typewrite('Data Validation ERP', interval**=**0.001)

time**.**sleep(1)
Signature **=** pyautogui**.**locateOnScreen('Signatur.png',confidence**=**0.8) *#Signatur stands for Signature in German*
*#print(Signature)*
pyautogui**.**moveTo(Signature)
pyautogui**.**move(0, **-**150) *# from Signature, move 150 pixels top to insert the mail's body above the signature*
pyautogui**.**click()
pyautogui**.**typewrite('Hi colleague, our bot found a company which is currently missing in our ERP master data: '**+**str(CompanyExpected**.**difference(CompanyExported)**.**values), interval**=**0.001)

作为最后一个自动化步骤,让我们假设需要将处理过的数据上传到 web 表单中。看看 Ken Soh 用他漂亮的 RPA 包解决这个任务有多快:

**import** rpa **as** r

r**.**init(turbo_mode **=** **True**) *# to run in quick mode*
r**.**url('http://rpachallenge.com') *# open this website to fill in forms*

*# timer starts after running this step*
r**.**click('//*[text()="Start"]')

*# loop through and fill in all fields*
**for** i **in** range(len(ValidDfFromManyFiles**.**axes[0])):
    r**.**type('//*[@ng-reflect-name="labelFirstName"]', ValidDfFromManyFiles['First Name'][i])
    r**.**type('//*[@ng-reflect-name="labelLastName"]', ValidDfFromManyFiles['Last Name'][i])
    r**.**type('//*[@ng-reflect-name="labelCompanyName"]', ValidDfFromManyFiles['Company Name'][i])
    r**.**type('//*[@ng-reflect-name="labelRole"]', ValidDfFromManyFiles['Role in Company'][i])
    r**.**type('//*[@ng-reflect-name="labelAddress"]', ValidDfFromManyFiles['Address'][i])
    r**.**type('//*[@ng-reflect-name="labelEmail"]', ValidDfFromManyFiles['Email'][i])
    r**.**type('//*[@ng-reflect-name="labelPhone"]', ValidDfFromManyFiles['Phone Number'][i])
    r**.**click('//*[@value="Submit"]')

*# take a screenshot after all forms are filled in, which can also be used as a kind of log file, check also the rpa default log file*
r**.**snap('page', 'score.png')
r**.**wait(10)
r**.**close()

Bot 将所有数据输入网络表单的速度比我们能做到的要快得多。

顺便说一下,如果您不知道如何在网站上找到 Xpath,只需右键单击该元素,然后按“调查”:

右键单击对象以研究其 Xpath。

总结:

当你在吃午饭的时候,看着你的机器人为你做无聊的工作,感觉不是很好吗?因为这确实是值得一提的一点:GUI 自动化接管了你的鼠标和键盘,就像一个人坐在电脑前一样。因此,当机器人将鼠标光标定位在屏幕上的特定区域时,你不应该同时移动鼠标。同样的事情也适用于任何击键。你必须确保在加载之间有足够长的等待时间,这样机器人才能在当前显示的屏幕上运行所有步骤。在 Windows pc 上工作,我看不到一个简单的解决方案,如何让机器人在后台工作,这样你就可以在同一时间继续工作。无论是添加第二个键盘和鼠标,还是在终端服务器上运行脚本,都没有解决这个问题。也许你们中有人有解决办法?我肯定会洗耳恭听。

非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 LinkedInTwitter工作室与我联系。

https://jesko-rehberg.medium.com/membership

你可以在我的 Github 中下载完整的 Jupyter 笔记本和图片

让我们从文本数据中提取一些主题—第一部分:潜在狄利克雷分配(LDA)

原文:https://towardsdatascience.com/let-us-extract-some-topics-from-text-data-part-i-latent-dirichlet-allocation-lda-e335ee3e5fa4

使用 Python 的 nltk、gensim、sklearn 和 pyLDAvis 包了解主题建模需要什么及其实现

来自像素的免费使用照片

介绍

主题建模是一种自然语言处理(NLP)任务,它利用无监督学习方法从我们处理的一些文本数据中提取出主要主题。这里的“无监督”一词意味着没有与主题标签相关联的训练数据。相反,算法试图直接从数据本身发现潜在的模式,在这种情况下,是主题。

有各种各样的算法广泛用于主题建模。在接下来的系列文章中,我打算一一介绍。在本文中,我们看看什么是潜在的狄利克雷分配(LDA) 算法,它是如何工作的,以及如何使用多个 Python 包来实现它。

什么是 LDA?

LDA 是一种用于主题建模的模型,由于与可能执行相同任务的极其复杂的神经网络相比,它相对简单和有效,所以经常被使用。

我将我们处理的文本数据称为“语料库”,将每个文本观察称为“文档”。LDA 的基本假设是每个文档可以由主题的分布来表示,而主题的分布又可以由一些词的分布来表示。请注意“分发”这个词。LDA 假设每个文档不仅仅由一个主题组成,而是由几个主题组成。因此,LDA 模型将分解每个主题对该文档的百分比贡献。例如,如果我们将主题的数量设置为 5,那么文档 A 可以表示如下:

document_A = 0.2 x Topic1 + 0.1 x Topic2 + 0.1 x Topic3 + 0.5 x Topic4 + 0.1 x Topic5 

我不会深入这个算法的数学,但是对于那些感兴趣的人,请看看这个算法的最初发明者的这篇论文

我要提醒读者的是,不要将潜在的狄利克雷分配与线性判别分析模型混淆,线性判别分析模型是一种分类算法,因为它们通常都缩写为 LDA。

现在,让我们看一些实际的代码,看看如何实现 LDA。

清理文本

我们首先导入 nltk 包,并下载一个相关语料库的列表,这些语料库将用于删除停用词、词干化和词汇化。

**import** nltk**from** nltk.stem import *nltk.download(‘punkt’) **# For Stemming**nltk.download(‘wordnet’) **# For Lemmatization**nltk.download(‘stopwords’) **# For Stopword Removal**nltk.download(‘omw-1.4’)

我们还在一个名为 stop words 的变量中存储了一个以后要使用的英语停用词列表。

stopwords = set(nltk.corpus.stopwords.words('english'))

是时候载入一些数据了。这里,我们使用开源的 20 个新闻组数据集,每个人都可以在 sklearn 包接口中使用该数据集。它使用的是Apache 2.0 版许可证

**import** pandas as **pd****import** numpy as **np**from sklearn.datasets import fetch_20newsgroupsfetch20newsgroups = fetch_20newsgroups(subset='train')**# Store in a pandas dataframe**df = pd.DataFrame(fetch20newsgroups.data, columns=['text'])

接下来,我们做一些文本清理,如删除一些无关的单词或表达。我们删除网址,提及和散列标签。为此,我们导入正则表达式包,然后使用 sub 函数删除与指定正则表达式匹配的字符串部分。请记住,“\S”匹配除空白以外的单个字符。如果你看下面的代码,我们匹配的表达式包括“https”,“@”和“#”,它们对应于我们想要从文本中删除的字符串。

**# Remove URLs****import** re # Import regular expression package**def** remove_url(text):
    return re.sub(r'https?:\S*','',text)df.text = df.text.apply(remove_url)**# Remove mentions and hashtags****import** re**def** remove_mentions_and_tags(text):
     text = re.sub(r'@\S*','',text)
     return re.sub(r'#\S*','',text)df.text = df.text.apply(remove_mentions_and_tags)

现在,我们希望将我们拥有的文档标记化,并将它们转换成某种字典形式,可以用作 gensim 模型的输入。

预处理

我们使用下面的自定义函数来预处理语料库中的文档。

**def** text_preprocessing(df): corpus=[]

    lem = WordNetLemmatizer() **# For Lemmatization** for news in df['text']:
        words=[w for w in nltk.tokenize.word_tokenize(news) if (w not in stopwords)] **# word_tokenize function tokenizes text on each word by default** words=[lem.lemmatize(w) for w in words if len(w)>2] corpus.append(words) return corpus**# Apply this function on our data frame** corpus = text_preprocessing(df)

从上面的代码中可以看出,我们循环遍历每个文档,并按顺序应用标记化和词汇化。记号化意味着将文档分解成称为记号的小分析单元,记号通常是单词。词汇化指的是将单词还原为其原始基本形式的过程,称为“词汇”。将同一个动词的多种形式根据它的时态改变成它的基本形式就是词汇化的一个例子。

我们利用 Python 中的 gensim 包来继续第 3 步和后续步骤。

使用以下命令安装 gensim 包。

!pip install -U gensim==3.8.3

我安装特定版本的 gensim (3.8.3)的原因是因为一些不同的 LDA 实现(如 LDA Mallet 模型)在以后的版本中已经被删除。

我们首先使用已经预处理过的语料库创建一个 gensim dictionary 对象,然后创建一个名为“bow_corpus”的变量,在其中存储单词包(bow)转换后的文档。由于 gensim 包接受输入的方式,这一步是必要的。然后,我们使用 pickle 包保存 bow_corpus 和字典供以后使用。

import gensim# Transform to gensim dictionary
dic = gensim.corpora.Dictionary(corpus) bow_corpus = [dic.doc2bow(doc) for doc in corpus]import pickle # Useful for storing big datasetspickle.dump(bow_corpus, open('corpus.pkl', 'wb'))dic.save('dictionary.gensim')

实际模型

接下来,我们使用 gensim.models 类中的 LDAMulticore 函数来实例化我们的 LDA 模型。有人可能会问 LDAMulticore 函数和基本的 LDAmodel 函数有什么区别?它们基本上是一样的,但是前者支持多进程,这可以节省你的运行时间,所以为什么不用它来代替基本的呢?注意,我们可以通过“workers”参数指定将参与多处理操作的处理器数量。我们还通过“num_topics”参数指定要从语料库中提取的主题数量。不幸的是,除非你事先有一些关于语料库的领域知识,否则没有办法找出多少主题是最佳值。

lda_model = gensim.models.LdaMulticore(bow_corpus,
                                   num_topics = 4,
                                    id2word = dic,
                                      passes = 10,
                                      workers = 2)lda_model.save('model4.gensim')

一旦我们训练了 LDA 模型,我们查看从语料库中提取的每个主题中最重要的前十个单词。

**# We print words occuring in each of the topics as we iterate through them**for idx, topic in lda_model.print_topics(num_words=10):    
    print('Topic: {} \nWords: {}'.format(idx, topic))

输出如下。

Topic: 0  Words: 0.008*"The" + 0.007*"would" + 0.006*"one" + 0.006*"From" + 0.006*"people" + 0.005*"writes" + 0.005*"Subject" + 0.005*"Lines" + 0.005*"Organization" + 0.005*"article"Topic: 1  Words: 0.008*"The" + 0.007*"Subject" + 0.007*"From" + 0.007*"Lines" + 0.007*"Organization" + 0.004*"use" + 0.004*"file" + 0.003*"one" + 0.003*"would" + 0.003*"get"Topic: 2  Words: 0.007*"From" + 0.007*"Organization" + 0.007*"Lines" + 0.006*"The" + 0.006*"Subject" + 0.005*"game" + 0.005*"University" + 0.005*"team" + 0.004*"year" + 0.003*"writes"Topic: 3  Words: 0.013*"MAXAXAXAXAXAXAXAXAXAXAXAXAXAXAX" + 0.009*"The" + 0.006*"From" + 0.006*"Subject" + 0.005*"Lines" + 0.005*"key" + 0.005*"Organization" + 0.004*"writes" + 0.004*"article" + 0.003*"Israel"

可能题目 2 和“大学”这个词有点关系。除此之外,我们没有看到任何重要的东西。这里发生了什么事?请注意,有些词在所有主题中都出现得非常频繁。它们是包括主题、文章和组织的单词。这些词出现在每篇新闻文章的开头,列出了作者、所属单位和标题。这就是为什么他们主宰了每一个话题。换句话说,它们是没有信息的噪音,不会给我们构建的 LDA 模型增加多少价值。这说明了文本清理和预处理与你的自然语言处理项目的目标相一致的重要性!

估价

正如我前面提到的,没有一个确定的方法来找出多少个主题是最佳的。一种方法是简单地观察每个主题中的一些关键词,看它们是否相互连贯。但这绝对不是一种客观或严谨的评价方式。一个稍微严谨一点的方法是使用连贯性评分。

这个分数衡量每个主题中高分单词之间的语义相似程度。现有的算法有 C_v,C_p,C_uci,C_umass 等。C_v 和 C_umass 是两种应用较为广泛的方法。

C_v 的一致性分数范围从 0 到 1,值越高,一致性越好。C_umass 方法返回负值。这里的棘手之处在于,对于什么样的价值构成“良好的一致性”,没有明确的共识或解释。学者们已经做了一些尝试,例如约翰·麦克里维,他认为任何超过 0.5 分的 C_v 方法都是相当好的。然而,如果你浏览一些数据科学的论坛,会有一些不同的观点。来自 Stackoverflow 的这篇帖子给出了以下建议。

  • 0.3 是不好的
  • 0.4 很低
  • 0.55 没问题
  • 0.65 可能是最好的结果了
  • 0.7 就不错了
  • 0.8 不太可能
  • 0.9 很可能是错的

对于 u_mass 方法,它变得更加混乱。看看发生在这个 Reddit 论坛的讨论。它说,当我们增加要提取的主题数量时,一致性分数可能会非常不稳定。我认为大多数数据科学用户提出的一般想法是,C_v 方法通常比 C_umass 方法更可靠,尽管最终的裁决取决于用户。

尽管如此,我认为从所有这些关于一致性评估的信息中获得的关键信息是:

  • 一致性分数不是评估主题质量的绝对标准。千万不要只依靠一种方法。使用连贯性分数作为基线,然后确保你浏览每个主题的文档及其关键词,以获得你“自己”对每个主题连贯性的感觉。
  • 对于计算一致性得分的 C_v 方法,检查您的模型的得分是否不太高也不太低。0.5 到 0.7 之间的任何值都是一个合适的范围。
  • 对于计算一致性分数的 C_umass 方法,检查分数的绝对值是否合理地接近 0。

让我们用 C_v 方法来评价我们的模型。gensim 包的 models 类包含 CoherenceModel,它可以方便地计算一致性分数。

**from** gensim.models **import** CoherenceModel**# instantiate topic coherence model**cm = CoherenceModel(model=lda_model, corpus=bow_corpus, texts=corpus, coherence='c_v')**# get topic coherence score**coherence_lda = cm.get_coherence()print(coherence_lda)
>> 0.49725706360481475

0.497 不差但也不体面。我们想知道连贯分数是否随着从语料库中提取的主题数量而变化。我们从数字 2 到 6 迭代主题的数量,并将每个一致性分数存储在名为 score 的变量中,该变量被初始化为一个空列表。然后我们使用 Python 的 matplotlib 包将它可视化成一个线图。

import matplotlib.pyplot as plttopics = []score = []for i in range(2,7,1):
     lda = gensim.models.LdaMulticore(corpus=bow_corpus, id2word=dic, iterations=10, num_topics=i, workers = 3, passes=10, random_state=42) cm = CoherenceModel(model=lda, corpus=bow_corpus, texts=corpus, coherence='c_v') topics.append(i) # Append number of topics modeled score.append(cm.get_coherence()) # Append coherence scores to listplt.plot(topics, score)plt.xlabel('# of Topics')plt.ylabel('Coherence Score')plt.show()

来源:来自作者

我们看到,当有两个主题要提取时,连贯性得分最高。它随着话题数量的增加而不断减少。

我们现在基于两个主题建立新的 LDA 模型。

**# LDA model with two topics**
lda_model2 = gensim.models.LdaMulticore(bow_corpus,
                                    num_topics = 2,
                                     id2word = dic,
                                        passes = 8,
                                       workers = 3)lda_model2.save('model2.gensim') **# We print words occuring in each of the topics as we iterate through them**for idx, topic in lda_model2.print_topics(num_words=20):print('Topic: {} \nWords: {}'.format(idx, topic))

结果输出:

Topic: 0  Words: 0.007*"The" + 0.005*"MAXAXAXAXAXAXAXAXAXAXAXAXAXAXAX" + 0.004*"From" + 0.004*"Subject" + 0.004*"Lines" + 0.004*"Organization" + 0.003*"one" + 0.002*"get" + 0.002*"year" + 0.002*"**game**" + 0.002*"like" + 0.002*"would" + 0.002*"time" + 0.002*"University" + 0.002*"writes" + 0.002*"**team**" + 0.002*"article" + 0.002*"dont" + 0.002*"This" + 0.002*"people" Topic: 1  Words: 0.009*"The" + 0.007*"From" + 0.007*"Subject" + 0.007*"Lines" + 0.007*"Organization" + 0.005*"would" + 0.005*"writes" + 0.005*"one" + 0.004*"article" + 0.003*"people" + 0.003*"**know**" + 0.003*"**like**" + 0.003*"University" + 0.003*"dont" + 0.003*"get" + 0.003*"**think**" + 0.002*"This" + 0.002*"**use**" + 0.002*"time" + 0.002*"**say**"

上面的输出显示,主题 0 与游戏和团队有些关系,而主题 1 与动作动词更相关,包括知道、喜欢、使用和说。由于我们没有删除那些频繁出现的代词或冠词,这些代词或冠词只是为了它们的语法目的,并没有给模型增加太多的意义或价值,所以噪音在这两个主题中仍然相当猖獗。然而,即使有了眼球,我们也能对每个主题的内容有一个稍微好一点的感觉,这通常意味着更好的连贯性。

可视化 LDA 结果

使用另一个叫做 pyLDAvis 的软件包,我们可以可视化 LDA 结果。

!pip install pyldavis**import** pyLDAvis**import** pyLDAvis.gensim_models

请注意,pyLDAvis 包有了更新,所以人们以前知道的 pyLDAvis.gensim 模块现在变成了 pyLDAvis.gensim_models 而不是

**# Loading the dictionary and corpus files we saved earlier**
dictionary = gensim.corpora.Dictionary.load('dictionary.gensim')corpus = pickle.load(open('corpus.pkl', 'rb'))**# Loading the num_of_topics = 2 model we saved earlier** lda = gensim.models.ldamodel.LdaModel.load('model2.gensim')pyLDAvis.enable_notebook()vis = pyLDAvis.gensim_models.prepare(lda, bow_corpus, dic, sort_topics=False)pyLDAvis.display(vis)

来源:来自作者

在我上面截图的 pyLDAvis 仪表板的左侧,每个圆圈的面积代表了主题相对于语料库的重要性。此外,圆心之间的距离表示主题之间的相似性。

来源:来自作者

现在,在仪表板的右侧,正如您在右上方看到的,每个主题的前 30 个相关词以直方图显示,红色部分代表所选主题内的估计词频(显著性),浅蓝色部分代表整体词频。

奖金

LDA 有各种版本。例如,LDA Mallet 是 LDA 的另一个版本,它使用了吉布斯采样,与我在上面展示的普通 LDA 模型相比,它提高了性能。LDA Mallet 模型的一个缺点是相对于它的原始对应物,它的计算效率较低,因此对于大型数据集,它可能不是最合适的。

请看看这篇文章的最后一部分,了解更多关于 LDA Mallet 模型的信息。

结论

在本文中,我向您介绍了什么是主题建模,以及 LDA 模型可以做什么来执行主题建模。我还逐步向您介绍了如何清理和预处理文本数据,然后在其上构建 LDA 模型。最后,我阐述了如何评估您的 LDA 模型以及如何可视化结果。

读者应该记住的一个要点是,预处理对增加主题连贯性比你想象的更重要。确保文本数据中没有太多噪声的一些方法是过滤掉极高或极低频率的标记,并排除特定的词性(PoS)标记的单词,例如 in(例如,upon,except)和 MD(例如,may,must)。当然,后者必须在词性标注之前,词性标注本身是一个独立的 NLP 任务,我将在另一篇文章中介绍。

如果你觉得这篇文章有帮助,请考虑通过以下链接注册 medium 来支持我: )

【joshnjuny.medium.com

你不仅可以看到我,还可以看到其他作者写的这么多有用和有趣的文章和帖子!

关于作者

数据科学家。加州大学欧文分校信息学专业一年级博士生。

密歇根大学刑事司法行政记录系统(CJARS)经济学实验室的前研究领域专家,致力于统计报告生成、自动化数据质量审查、构建数据管道和数据标准化&协调。Spotify 前数据科学实习生。Inc .(纽约市)。

他喜欢运动、健身、烹饪美味的亚洲食物、看 kdramas 和制作/表演音乐,最重要的是崇拜我们的主耶稣基督。结账他的 网站

让我们从文本数据中提取一些主题——第三部分:非负矩阵分解(NMF)

原文:https://towardsdatascience.com/let-us-extract-some-topics-from-text-data-part-iii-non-negative-matrix-factorization-nmf-8eba8c8edada

了解更多有关线性代数派生的无监督算法的信息,该算法使用直观的方法进行主题建模

pexel免费使用照片

介绍

主题建模是一种自然语言处理(NLP)任务,它利用无监督的学习方法来提取我们处理的一些文本数据的主要主题。这里的“无监督”一词意味着没有具有相关主题标签的训练数据。相反,算法试图直接从数据本身中发现底层模式,在这种情况下是主题。

有各种各样的算法被广泛用于主题建模。在我之前的两篇文章中,我向您介绍了用于主题建模的两种算法,LDA 和 GSM。

https://medium.com/geekculture/let-us-extract-some-topics-from-text-data-part-ii-gibbs-sampling-dirichlet-multinomial-mixture-9e82d51b0fab

在本文中,我们将研究非负矩阵分解(NMF),一种源于线性代数的无监督算法。Python 中的代码实现也将被引入。

什么是 NMF?

关于 NMF 的一个有趣的事情是,它被用于许多其他应用,包括推荐系统,不像其他专门用于主题建模的主题建模算法。NMF 是在线性代数领域发展起来的,能够识别数据中的潜在或隐藏结构。简而言之,NMF 将高维向量分解(或因式分解,因此得名因式分解)成低维表示。这些低维向量的所有系数都是非负的,这可以从算法的名称“非负”矩阵分解中得到暗示。

假设因式分解的原始矩阵是 A,NMF 将把 A 分解成两个矩阵 W 和 H。W 被称为包含 NMF 发现的主题的特征矩阵,H 被称为包含这些主题的系数(权重)的分量矩阵。如果矩阵 A 的形状为 M x N,主题数为 k,则特征矩阵和成分矩阵的形状分别为(M x k)和(k x N)。

一些数学

由于 NMF 是一个无人监督的算法,它需要一些措施,以便能够找出不同文档之间的相似性。有几种数学度量用来定义这种相似性或距离感,但我在本节中介绍了两种最流行的度量。

第一个是广义的 kull back-lei bler 散度,定义如下。

来源:来自作者使用乳胶

kull back-lei bler 散度可以实现如下。

from numpy import sum

def kullback_leibler_divergence(p, q):
  return sum(p[i] * log2(p[i]/q[i]) for i in range(len(p)))

# There is built-in function in the SciPy package that 
# you can directly use as well

from scipy.special import kl_div

你可以在这里阅读更多关于这种分歧的信息。

另一个流行的度量是 Frobenius 范数,它是其元素的绝对平方和的平方根。它也被称为欧几里德范数。数学公式如下。

来源:来自作者使用乳胶

您可以使用 NumPy 包来实现这一点。

import numpy as np

# Assuming some array a has been already defined
frobenius_norm = numpy.linalg.norm(a)

对于优化,通常使用这两种算法中的任何一种。它们是坐标下降解算器和乘法更新解算器。哪种优化算法更好的问题取决于您正在处理的数据类型及其特征(例如稀疏度),因此尝试这两种算法以查看哪种返回的主题更连贯且质量更高可能是最佳方法。在评估主题质量方面,更多的内容将在本文的后面部分进行解释。参考这篇文章对这两种优化算法有更深入的了解。

履行

现在,让我们深入研究 NMF 的代码实现。和往常一样,我们从安装我们需要的包开始,并将它们导入到我们的环境中。

!pip install gensim

import nltk
from nltk.stem import *

nltk.download('punkt')
nltk.download('wordnet')
nltk.download('stopwords')
nltk.download('omw-1.4')
nltk.download('averaged_perceptron_tagger')

# Prepare list of stopwords for removal
stopwords = set(nltk.corpus.stopwords.words('english'))

import pandas as pd

# NMF implementation in sklearn
from sklearn.decomposition import NMF 

# TF-IDF Vectorization
from sklearn.feature_extraction.text import TfidfVectorizer

# Dataset we use for this tutorial
from sklearn.datasets import fetch_20newsgroups 

from scipy import linalg # For linear algebra operations
import matplotlib.pyplot as plt
%matplotlib inline
np.set_printoptions(suppress=True)

然后,我们读入本教程的数据集。我们将使用 20 个新闻组数据,每个人都可以通过 sklearn 包获得这些数据。它使用Apache 2.0 版许可。只是提醒一下,为了不同教程之间的一致性,我在关于主题建模的前两篇文章中使用了这个数据集。

# Categories
cats = ['alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space'] 
remove = ('headers', 'footers', 'quotes')

# Read in 20 NewsGroup Data
fetch20newsgroups = \
fetch_20newsgroups(subset='train', categories=cats, remove=remove)

# Transform fetch20newsgroups object to pandas dataframe
df = pd.DataFrame(fetch20newsgroups.data, columns=['text'])

我们通过一些传统的文本预处理步骤来确保建模主题的质量不会变差。

# Remove Punctuations 

import string
PUNCT_TO_REMOVE = string.punctuation

df.text = \
df.text.apply(lambda x: x.translate(str.maketrans('', '', PUNCT_TO_REMOVE)))

# Remove URLs
import re
def remove_url(text):
  return re.sub(r'https?:\S*','',text)

df.text = df.text.apply(remove_url)

# Remove mentions and hashtags
import re
def remove_mentions_and_tags(text):
    text = re.sub(r'@\S*','',text)
    return re.sub(r'#\S*','',text)

df.text = df.text.apply(remove_mentions_and_tags)

# Make all the text lower case
df.text = df.text.str.lower()

# Remove words that exist across almost all documents (almost like removing
# stopwords) after tokenization
extraneous_words = \
['article', 'organization', 'subject','line','author','from', 
'lines', 'people', 're', 'writes', 'distribution', 
'x', 'nntppostinghost','think','university']

docs = []

for news in df.text:

   words = \
[w for w in nltk.tokenize.word_tokenize(news) \
if (w not in stopwords) & (w not in extraneous_words) & \
(nltk.pos_tag([w])[0][1] in ['NN','NNS'])]

    docs.append(words)

我们应用 TF-IDF 矢量化。

# Initialize TFIDF Vectorizer
vect= TfidfVectorizer(
    min_df=10,
    max_df=0.85,
    max_features=5000,
    ngram_range=(1, 3), # Include unigrams, bi-grams and tri-grams
    stop_words='english' # Remove Stopwords
)

# Apply Transformation
X = vect.fit_transform(df.text)

# Create an NMF instance with 4 components
model = NMF(n_components=4, init='nndsvd', random_state=42)

# Fit the model
model.fit(X)

我们分别存储特征和组件矩阵。

# Features Matrix
nmf_features = model.transform(X)

# Components Matrix
components_df = pd.DataFrame(model.components_, 
                            columns=vect.get_feature_names_out())

terms =  list(vect.get_feature_names_out())

获取每个主题的最高系数值的单词(从组件矩阵中)

从组件矩阵中,我们可以通过检索与最高系数值相关联的标记来查看每个主题中最有意义的单词。

# Top 20 words of importance in each of the topics

for topic in range(components_df.shape[0]):
    topic_df = components_df.iloc[topic]
    print(f'For topic {topic+1} the words with the highest value are:')
    print(topic_df.nlargest(20))
    print('\n')

让我们看看第一个主题的输出作为例子。

来源:来自作者

与第一个主题相关的重要词汇是上帝、耶稣、圣经和信仰,这些词汇我们会认为会出现在与无神论(这是第一个主题的实际主题)相关的文献中。

主题描述符

基于以上信息,我们可以打印出每个主题的前 n 个关键词。

def get_descriptor( terms, H, topic_index, top ):
  # reverse sort the values to sort the indices
  top_indices = np.argsort( H.iloc[topic_index,:] )[::-1]

  # get the terms corresponding to the top-ranked indices
  top_terms = [ ]
  for term_index in top_indices[0:top]:
      top_terms.append( terms[term_index] )
  return top_terms

k = 4 # four topics
descriptors = [ ]
for topic_index in range(k):
  descriptors.append(get_descriptor(terms, components_df, topic_index, 10 ))
  str_descriptor = ", ".join( descriptors[topic_index] )
  print("Topic %02d: %s" % ( topic_index+1, str_descriptor ) )

然后我们得到:

Topic 01: god, people, dont, think, just, jesus, say, bible, believe, does 
Topic 02: thanks, files, graphics, image, file, know, program, format, help, need 
Topic 03: space, nasa, launch, shuttle, lunar, orbit, moon, station, data, earth 
Topic 04: sank manhattan sea, stay blew, away sank, away sank manhattan, sank manhattan, said queens stay, said queens, bob beauchaine, bronx away sank, bronx away

第 j 个文档的主题

我们还可以从特征矩阵中找出第 j 个文档属于哪个主题。

# Topic of the jth document

j = 100 

print(pd.DataFrame(nmf_features).loc[100])

# Add 1 to index to get the topic number since index starts from 0 not 1
print("\n {}th Document belongs to Topic {} ".\
format(j, np.argmax(pd.DataFrame(nmf_features).loc[100])+1))

>>
0    0.113558
1    0.016734
2    0.015008
3    0.000000
Name: 100, dtype: float64

100th Document belongs to Topic 1 

预测新文档的主题

对于一个以前没有被算法发现的新文档,我们也可以预测这个文档属于哪个主题。

# New Document to predict
new_doc = """Jesus is the light of thie world"""

# Transform the TF-IDF
X_new = vect.transform([new_doc])

# Transform the TF-IDF: nmf_features
nmf_features_new = model.transform(X_new)

# The idxmax function returns the index of the maximum element
# in the specified axis
print("This new document belongs to Topic {}".\
format(pd.DataFrame(nmf_features_new).idxmax(axis=1).iloc[0] + 1))

>>
This new document belongs to Topic 1

最佳主题数量的评估

在上面的代码中,我们选择了四个主题进行建模,因为我们已经预先知道我们正在处理的数据中有多少主题。然而,在大多数情况下,我们没有这样的知识。随机猜测不是最理想的方法。有没有一种编程的方式来做这件事?是啊!

为了自动确定主题的最佳数量,我们使用与前面介绍的两个算法 LDA 和 GSDMM 相同的方法。我们使用 gensim 的一致性分数来做到这一点。不幸的是,我们无法将 sklearn 的 NMF 实现与 gensim 的 coherence score 模型结合使用,因此,我们需要依赖 gensim 的 NMF 实现来实现这一目的。本文的后半部分使用 NMF 的 gensim 实现来确定一致性分数的最佳数量,然后使用 NMF 的 sklearn 实现来进行实际的训练和主题提取。

复习题目质量

由于 NMF 是一个线性代数算法,我们有一个很好的方法来评估每个主题的主题质量,通过查看每个主题组中文档的平均残差。

假设我们使用 Frobenius 范数作为最小化的距离度量,我们可以如下计算每个文档的残差。

A = vect.transform(df.text) # Original Matrix (after tf-idf vectorization is applied)
W = model.components_ # Component matrix
H = model.transform(A) # Features matrix

# Get the residuals for each document
r = np.zeros(A.shape[0])
for row in range(A.shape[0]):
    # 'fro' here means we are using the Frobenium norm as the distance metric
    r[row] = np.linalg.norm(A[row, :] - H[row, :].dot(W), 'fro') 

然后,我们计算每个主题组中这些残差的平均值,看看哪个主题具有最低的平均残差将是质量最好的主题!看看这篇文章,了解更多关于这个过程的信息。

奖金

正如我前面提到的,gensim 也有自己的 NMF 实现。虽然基础数学和原理与 sklearn 实现相同,但它可以与 gensim 中的 coherence 模型联合使用,以确定要建模的主题的最佳数量,而 sklearn 目前没有提供任何 coherence score 模型。要了解摩尔关于 gensim 的 NMF 实现,请参考此文档

迷你批处理 NMF 是一个 NMF 版本,是专为更大的数据集。它有一个 partial_fit 方法,在数据集的不同块上连续调用多次,以便实现小批量的核外或在线学习。要了解有关该算法的更多信息,请参考其文档

结论

在本文中,我向您介绍了 NMF 算法,它是线性代数算法家族的一部分,不仅用于主题建模,还用于从推荐系统到维度缩减的广泛应用。只要用户对线性代数有所了解,NMF 的优势就在于其直观和简单的数学。很难判断哪种主题建模算法最适合您正在处理的特定项目和数据集,因此我建议使用几种最流行的主题建模算法,并在进行任何后续步骤之前目测每个主题中的关键词。

如果你觉得这篇文章有帮助,请考虑通过以下链接注册 medium 来支持我: )

joshnjuny.medium.com

你不仅可以看到我,还可以看到其他作者写的这么多有用和有趣的文章和帖子!

关于作者

数据科学家。加州大学欧文分校信息学专业一年级博士生。主要研究兴趣是将 SOTA ML/DL/NLP 方法应用于健康和医疗相关的大数据,以提取有趣的见解,为患者、医生和决策者提供信息。

密歇根大学刑事司法行政记录系统(CJARS)经济学实验室的前研究领域专家,致力于统计报告生成、自动化数据质量审查、构建数据管道和数据标准化&协调。Spotify 前数据科学实习生。Inc .(纽约市)。

他喜欢运动、健身、烹饪美味的亚洲食物、看 kdramas 和制作/表演音乐,最重要的是崇拜我们的主耶稣基督。结账他的 网站

让我们从文本数据中提取一些主题——第四部分:主题

原文:https://towardsdatascience.com/let-us-extract-some-topics-from-text-data-part-iv-bertopic-46ddf3c91622

了解有关主题建模的 BERT 家族成员的更多信息

来自像素的免费使用照片

介绍

主题建模是一种自然语言处理(NLP)任务,它利用无监督学习方法从我们处理的一些文本数据中提取出主要主题。这里的“无监督”一词意味着没有与主题标签相关联的训练数据。相反,算法试图直接从数据本身发现潜在的模式,在这种情况下,是主题。

有各种各样的算法广泛用于主题建模。在我之前的三篇文章中,我向您介绍了主题建模的三种算法:LDA、GSDMM 和 NMF。

https://medium.com/geekculture/let-us-extract-some-topics-from-text-data-part-ii-gibbs-sampling-dirichlet-multinomial-mixture-9e82d51b0fab

在本文中,我深入解释了什么是 BERTopic,以及如何将它用于您的主题建模项目!让我们直入主题吧!

什么是 BERTopic

在我们弄清楚 BERTopic 是什么以及它做什么之前,我们需要知道 BERT 是什么,因为 BERTopic 是从 BERT 派生出来的。BERT 是 Transformers 双向编码器表示的缩写,它是 2018 年作为基于 transformer 的机器学习模型开发的。它已经在大量的语料库数据中预训练,因此在各种 NLP 任务中表现非常好。你可以在这里查看伯特的原始论文。BERTopic 是作为 BERT 家族成员设计的,专门用于主题建模。

BERTopic 通过以下几个步骤运作。

[步骤 1]使用句子转换器将文档表示为嵌入。你可以在这里了解更多句子变形金刚。这一步使用的默认模型是 BERT(因此得名 BERTopic)。

【第二步】第二步是降维过程。默认使用 UMAP(统一流形近似和投影)算法。当然,根据您的目标和数据,也可以使用包括主成分分析(PCA)在内的其他选项。

【第三步】第三步是聚类过程。这一步实际计算不同文档之间的相似性,以确定它们是否属于同一主题。默认使用基于密度的分层空间聚类(HDBSCAN)算法。这是一种基于密度的聚类算法,因此通常比用于 topc 建模的 K-Means 聚类等算法性能更好。

【第四步】之后,c-TF-IDF 算法检索每个主题最相关的词。顾名思义,c-TF-IDF 类似于 TF-IDF,但不同之处在于它测量的是每个簇中的术语频率,而不是每个文档中的术语频率。c-TF-IDF 的数学公式如下。

来源: BERTopic Github 网站

[步骤 5]可选的最后一步是使用最大边际相关性(MMR)来优化术语。使用这种算法是有益的,因为它提高了相同主题和主题表示的术语之间的一致性。

下面是一个很好的描述上述步骤的信息图。

来源: BERTopic Github 网站

履行

我们将使用 20 个新闻组数据,每个人都可以通过 sklearn 包获得这些数据。它使用Apache 2.0 版许可。只是提醒一下,为了不同教程之间的一致性,我在关于主题建模的前三篇文章中使用了这个数据集。

我们首先安装 BERTopic 包。

!pip install bertopic

然后我们导入我们需要的相关包。

import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.datasets import fetch_20newsgroups
from scipy import linalg
import gensim
from tqdm import tqdm
import re
import matplotlib.pyplot as plt
from bertopic import BERTopic

%matplotlib inline
np.set_printoptions(suppress=True)

通常,清理和预处理文本的过程对于确保最佳性能是必要的,但是对于本教程,我们跳过这一部分,更多地关注 BERTopic 本身的实现。实际上,看到没有文本清理的主题建模输出的相对不错的质量,可以证明 BERTopic 的强大和有用。如果你想了解更多关于文本清理步骤的信息,请参考我以前的文章,这篇文章谈到了主题建模的 NMF。

从 skelarn 包中,我们读入了 20 个新闻组数据。然后,我们简单地实例化 BERTopic 对象,同时指定语言为英语。您可以通过 nr_topics 参数指定想要的主题数量。“Auto”意味着您希望模型自动确定要建模的主题的适当数量。

docs = fetch_20newsgroups(subset='all',  
                        remove=('headers', 'footers', 'quotes'))['data']

# Instantiate the model
model = BERTopic(language="english", nr_topics = 'auto')

# Fit and Transform
topics, probs = model.fit_transform(docs)

但是,请注意,您可以根据自己的需要调整模型的各个部分。

语言

您可以指定语言,或者让模型使用 language 参数来推断语言。

# Specify the lanugage to be English expliclitly
model = BERTopic(language="english")

# Let the model infer
model = BERTopic(language="multilingual")

嵌入

您还可以通过 embedding_model 参数调整 BERTopic 的嵌入部分。默认情况下,使用 BERT 基本模型,但是可以自由使用其他嵌入模型。选项列表可在本文档中找到。

BERTopic(embedding_model="xlm-r-bert-base-nli-stsb-mean-tokens")

主题表征

除了使用默认的 TF-IDF 向量表示,您可以自己调整它,或者使用反向量表示。

# Update topic representation by increasing n-gram range and 
# removing english stopwords

model.update_topics(docs, topics, n_gram_range=(1, 3), stop_words="english")

# Use Custom CountVectorizer

from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(ngram_range=(1, 3), stop_words="english")
model.update_topics(docs, topics, vectorizer=cv)

降维

在将 UMAP 算法输入到 BERTopic 模型之前,您可以调整其超参数。此外,除了使用默认的 UMAP 算法进行降维,您还可以使用其他算法,如 PCA。

from umap import UMAP
umap_model = UMAP(n_neighbors=10, 
                  n_components=7, 
                  min_dist=0.0, 
                  metric='cosine', 
                  random_state=42)

model = BERTopic(umap_model=umap_model, language="english")

参考这篇很好的文章,它实际上用 PCA 代替了 BERTopic 的 UMPA。

话题数量

您可以手动指定主题的数量,或者将 nr_topics 参数设置为“auto ”,让模型自动确定适当的主题数量。但是,您可能会遇到这样的情况:您已经训练了主题模型,但是发现主题的数量太多。在这种情况下,您可以在训练后使用 reduce_topics 函数减少主题的数量。

# Specify number of topics manually
model = BERTopic(nr_topics=20)

# Automatic Topic Reduction
model = BERTopic(nr_topics="auto")

# Reduce the number of topics after training the model
new_topics, new_probs =\
model.reduce_topics(docs, topics, probs, nr_topics=5)

获取主题的总体信息

您可以使用 get_topic_info 函数来获取主题的全部信息。

freq = model.get_topic_info() 
freq

来源:来自作者

Topic -1 指的是一组不可能归类到其他主题中的文档,因此我们可以忽略它。我们可以看到文档在每个主题中的数量分布,还可以在 name 列中看到代表每个主题的一些关键字。

特定主题的关键字和 c-TF-IDF 分数

您还可以使用 get_topic 函数访问指定主题的关键字及其 c-TF-IDF 分数。例如,我们在下面的代码中访问主题 3 的关键字及其分数。

# We access index 4 for topic 3 because we skip index 0 which is topic -1
model.get_topic(freq.iloc[4]["Topic"])

来源:来自作者

从上面的关键词我们可以看出,话题 3 主要是关于枪击和枪支相关的犯罪。

查找与指定术语相似的主题

# Return top3 topics that are semantically most similar 
# to an input query term

# 3 most similar topics to specified word
similar_topics, similarity = \
model.find_topics("religion", top_n = 3) 

print("Most Similar Topic Info: \n{}".format(model.get_topic(similar_topics[0])))
print("Similarity Score: {}".format(similarity[0]))

print("\n Most Similar Topic Info: \n{}".format(model.get_topic(similar_topics[1])))
print("Similarity Score: {}".format(similarity[1]))

print("\n Most Similar Topic Info: \n{}".format(model.get_topic(similar_topics[2])))
print("Similarity Score: {}".format(similarity[2]))

输出是:

Most Similar Topic Info: 
[('prophecies', 0.035076938400189765), ('prophecy', 0.028848478747937348), ('that', 0.02108670502531178), ('god', 0.02051417591444672), ('lord', 0.020264581769842555), ('of', 0.016688896522909655), ('the', 0.016135781453880685), ('to', 0.015035130690705624), ('scripture', 0.014930014538798414), ('we', 0.014849027662146918)]
Similarity Score: 0.5521519602007433

 Most Similar Topic Info: 
[('bank', 0.09616363061888215), ('islamic', 0.08725362721875433), ('bcci', 0.07506873356081414), ('banks', 0.04599130160033494), ('islam', 0.03498368962676642), ('interest', 0.03153905791196487), ('an', 0.02707799288051472), ('risk', 0.026608617086657786), ('investor', 0.023625991363580155), ('loans', 0.023098071864865885)]
Similarity Score: 0.5518991782725108

 Most Similar Topic Info: 
[('moral', 0.04437134134615366), ('objective', 0.04058577723387244), ('morality', 0.03933015749038743), ('is', 0.023387671936210788), ('that', 0.021184421900981805), ('what', 0.017148832156794584), ('you', 0.017133130253694097), ('not', 0.01467957486207896), ('immoral', 0.014518771930711771), ('of', 0.014256652246875072)]
Similarity Score: 0.5515153930656871

从上面的例子可以看出,我们可以指定一个术语,并找到与该输入最相关的主题。在这种情况下,我们使用了“宗教”这个术语。与宗教一词语义最相似的前三个话题主要是关于预言、伊斯兰教和道德。

作为软件包的一部分,BERTopic 也有一些不错的可视化功能。

# Intertopic Distance Map
model.visualize_topics( )

来源:来自作者

visualize_topics 函数显示我们之前在 LDA 教程中见过的主题间距离图。这基本上是一种可视化,显示不同主题的气泡以及它们彼此之间的相似程度。两个气泡越接近,两个主题的语义就越相似。例如,主题 2 右下角的红色大气泡与主题 10 相似,后者是主题 2 中较小的气泡。主题 10 包含维生素和感染等关键词,而主题 2 包含癌症和药物等关键词,我们看到这两个主题彼此密切相关。

# Topic Word Scores in Bar Chart
model.visualize_barchart()

来源:来自作者

visualize_barchart 允许我们创建条形图,显示每个主题中关键字的不同得分。唯一的缺点是它只显示前 8 个主题,

# Similarity Matrix
model.visualize_heatmap( )

来源:来自作者

visualize_heatmap 函数返回每个成对主题相似性的相似矩阵。如果你有太多的主题,它可能不会提供太多的信息,因为在一个情节中会有过多的信息。

# Probability distribution of first document across different topics
model.visualize_distribution(probs[5]) 

visualize_distribution 函数返回水平条形图,指示某个文档属于每个主题的概率。在上面的例子中,它返回第六个文档属于每个主题的概率。请注意,您需要在训练之前将 BERTopic 对象中的 calculate_probabilities 参数指定为 True,因为默认值为 False,如果是这样,visualize_distribution 函数将返回一个错误。

# Hierarchical Topics
hierarchical_topics = model.hierarchical_topics(docs)
model.visualize_hierarchy(hierarchical_topics=hierarchical_topics)

来源:来自作者

如果您想了解不同主题之间的层次关系,visualize_hierarchy 函数将会帮您解决这个问题。

topics_over_time =\
model.topics_over_time(docs, topics, timestamp, nr_bins=20)

model.visualize_topics_over_time(topics_over_time, top_n_topics=20)

我们使用的数据集不包含任何时间元素,但是如果您的数据集包含时间元素,您也可以使用 topics_over_time 函数以线形图的形式显示主题随时间的频率。

结论

在本文中,我向您介绍了 BERTopic 算法,该算法源自 BERT,由多个步骤组成,利用了从 c-TF-IDF、UMAP 到 HDBSCAN 和 MRR 的各种算法。为了充分理解这个模型是如何工作的,你需要理解其他机器学习和 NLP 相关的任务,比如文本数据的维数减少和嵌入。尽管如此,它为我们提供了一种强大而简单的方法来执行主题建模,即使没有文本清理或预处理。这并不意味着文本清理是不必要的。请注意,在大多数情况下,文本清理和预处理对于确保建模主题的质量至关重要。要了解更多关于 BERTopic 的信息,请参考这个主要的文档

如果你觉得这篇文章有帮助,请考虑通过以下链接注册 medium 来支持我: )

joshnjuny.medium.com

你不仅可以看到我,还可以看到其他作者写的这么多有用和有趣的文章和帖子!

关于作者

数据科学家。加州大学欧文分校信息学专业一年级博士生。主要研究兴趣是将 SOTA ML/DL/NLP 方法应用于健康和医疗相关的大数据,以提取有趣的见解,为患者、医生和决策者提供信息。

密歇根大学刑事司法行政记录系统(CJARS)经济学实验室的前研究领域专家,致力于统计报告生成、自动化数据质量审查、构建数据管道和数据标准化&协调。Spotify 前数据科学实习生。Inc .(纽约市)。

他喜欢运动、健身、烹饪美味的亚洲食物、看 kdramas 和制作/表演音乐,最重要的是崇拜我们的主耶稣基督。结账他的 网站

让我们与 BigScience 的新 AI 模型一起绽放

原文:https://towardsdatascience.com/lets-bloom-with-bigscience-s-new-ai-model-803b1a0d677

使用 bnb-Int8 为拥抱脸部署大型语言模型

照片由萨夫Unsplash 上拍摄

这是怎么回事?

在本教程中,我们将在亚马逊 SageMaker 端点中部署 BigScience 的 BLOOM 模型,这是最令人印象深刻的大型语言模型(LLM)之一。为此,我们将利用 bitsandbytes (bnb) Int8 集成,用于来自 Hugging Face (HF) Hub 的模型。有了这些 Int8 权重,我们可以运行以前不适合我们的 GPU 的大型模型。

本教程的代码可以在这个 GitHub repo 中找到。请注意,用于 HF 集成的 bnb-Int8 目前处于公开测试阶段,其目的是收集不同型号和设置可能出现的错误。在生产环境中使用它之前,请考虑这一点。你可以在这里找到更多关于测试计划的信息。

声明:本教程的目的是逐步完成设置 bnb-Int8 + HF 的步骤。因此,我们将只部署 3B 版本的 BLOOM,无需 bnb-Int8 集成即可轻松托管。部署较大模型的步骤是相同的,但是由于下载它们需要时间,所以需要的时间要长得多。

为什么这很重要?

自从 OpenAI 在 2020 年发布 GPT-3 以来,LLMs 已经席卷了全世界。几周之内,大量令人印象深刻的演示被制作出来,这些演示可以在棒极了的 GPT-3 网站上找到。很快就清楚了,这些 LLM 是自然语言处理(NLP)中最重要的发现之一。这是因为这些模型具有令人印象深刻的零射击性能,即它们无需任何进一步的模型训练即可使用。他们也非常多才多艺(从撰写法律文本到代码)和多语言。

因此,越来越多的 LLM 由不同的组织创建,试图提高 LLM 的性能和/或使其开源和更加透明(GPT-3 是一个专有模型)。BigScience 的布鲁姆模型是“诞生”的最新也可能是最重要的模型之一。从他们的网站:

大型语言模型(LLM)对人工智能研究产生了重大影响。这些强大的通用模型可以根据用户的指令承担各种新的语言任务。然而,学术界、非营利组织和小公司的研究实验室发现很难创建、研究甚至使用 LLM,因为只有少数拥有必要资源和专有权的工业实验室可以完全访问它们。今天,我们发布了布鲁姆,这是第一个在完全透明的情况下训练的多语言 LLM,以改变这一现状——这是有史以来人工智能研究人员在单个研究项目中最大规模合作的结果。

但是,即使这些新的 LLM 现在是开源的,也不意味着我们可以下载它们并在我们的笔记本电脑上使用它们。这些模型需要大量的磁盘空间、RAM 和 GPU 来运行。这就是为什么像 bitsandbytes 这样的计划是重要的——它们使用漂亮的技术来降低 LLM 的硬件要求,并使它们以适当的延迟和合理的成本运行成为可能:

LLM 的不同硬件设置()

如何缩小 LLM

有几种方法可以减少大型人工智能模型的大小和内存占用——其中一些最重要的方法是知识提炼权重修剪和量化。请注意,这些并不相互排斥,可以相互组合。关于如何使用这些技术的一个很好的例子,参见《变形金刚的自然语言处理》一书的第 8 章。

也就是说,bnb 团队专注于最后一项技术,量化。他们引入了一种叫做分块量化的特殊技术,你可以在这里阅读更多关于的内容(这篇文章稍微超出了本教程的范围😉).他们的成果令人印象深刻:

bnb-Int8 ( 来源)节省的内存空间

部署布鲁姆模型

因此,让我们开始将布鲁姆-3B 模型部署到亚马逊 SageMaker (SM)端点。我们的游戏计划如下:

  1. 从 HF 模型中心下载模型
  2. 编写自定义推理脚本
  3. 把所有东西打包在一个model.tar.gz文件中并上传到 S3
  4. 将模型部署到端点
  5. 测试模型

下载模型

我们可以使用 Git LFS 下载所有模型文件,如下所示:

这将下载文件到我们的本地机器。

推理脚本

AWS & Hugging Face 开发了sage maker humping Face 推理工具包,使得在 SM 上部署 HF 模型进行推理变得容易。我们需要做的就是编写一个推理脚本(和 requirements.txt 文件)来定义我们想要如何加载模型和生成预测:

注意,我们使用参数 load_in_8bit=True 来使用 bnb-Int8 积分。

我们还需要一个 requirements.txt 文件,确保所需的模块将安装在推理容器映像中:

最后一行确保安装最新版本的变形金刚库。

上传到 S3

现在,我们将所有内容打包到一个 model.tar.gz 文件中,并将该文件上传到 S3:

部署模型

现在,我们创建一个指向 S3 位置的模型表示,并使用一个命令部署该模型:

这需要几分钟时间。

测试

在模型成功部署后,我们可以测试它。我们以这样一种方式编写我们的推理脚本,我们可以传递任何和所有用于文本生成的参数,在这里查看这些参数的详细列表。让我们使用采样和 0.5 的温度:

作者图片

它似乎工作,但我们可能要调整参数多一点。不确定 bnb-Int8 是否真的在我的 Android 手机上效果最好!😂

结论

在本教程中,我们利用 bnb-Int8 集成将 BigScience 的布鲁姆-3B 模型部署到 SageMaker 端点。从这里开始,下一步可能是尝试和部署更大的模型——如果您尝试了,请告诉我您的体验。如果您在 bnb-Int8 集成中遇到任何意外行为,请记住它仍处于测试阶段——请让团队知道任何错误。

让我们做:特征工程

原文:https://towardsdatascience.com/lets-do-feature-engineering-5731efc3d7fe

使用房屋销售价格数据和一个LightGBM模型简要展示简单特征工程的威力。

照片由 Danist SohUnsplash 上拍摄

简要背景

特征工程对不同的人有不同的含义,但该术语主要包括识别、操作和转换信息以提高模型预测能力的过程。

该过程可以细分为更具体的阶段(既不是穷尽的也不是连续的):

  1. 清理数据——例如缺失数据的替换或插补。
  2. 编码数据 —分类数据类型到数字数据类型的转换。
  3. (重新)缩放数据 —适当地转换数字数据,使其符合期望的分布或位于期望的范围内,这可能是利用正则化惩罚或提高神经网络性能所必需的。
  4. 分解数据 —“复合”数据项可以分解成它们的组成部分。例如,包含性别 x 年龄组合(例如“年轻男性”)的某个指示的数据项可以分成两个单独的项,一个描述性别,另一个描述年龄。
  5. 重组数据 —单个数据项可以合并成一个数据项。例如,单独的性别年龄数据项可以组合成单个性别 x 年龄复合特征。

还有一类特征工程:模型堆叠。这包括构建单独的模型,并将其预测用作输入特征。这是一个稍微不同的方法,我们将在以后的文章中保留它。

为什么要设计功能?

虽然我们大多数人选择追逐预测能力——特征工程可以在这方面有所帮助——但进行特征工程还有其他原因。

一个驱动程序是我们正在使用的型号的类型。如果我们在一个(广义的)线性建模空间中玩,我们会意识到在模型中使用交互项的好处。在模型构建中包含这些术语允许模型捕捉不同的特征如何相互作用在一起以产生效果。

这方面的一个例子是在汽车保险损失的预测中包含了一个性别 x 年龄的交互作用:我们通常认为保险“风险”可以随着年龄(通常在年轻和年老时更高)和性别(通常男性高于女性)而变化。但是年轻的男性呢?还是老年女性?在模型中仅包括一个性别作为主要效果可能会捕获大部分效果,但是包括交互项也可以提高模型的功效。

许多模型界面允许用户指定要使用的交互术语,但除此之外不允许更多的定制。回到性别 x 年龄的例子,指定我们只想区分 25 岁以下的男性和女性司机可能很难使用模型规范接口——我们可能需要通过一个新特性来捕捉这一点。此外,显式地指定复杂的交互可以使理解模型变得更容易。

有效的特征工程也有助于降低模型的复杂性。整合新信息或以简洁的方式总结信息可以减少充分捕捉预期效果所需的建模量——我们正在帮助模型更快地找到答案。

例如:

  • 在 GLM 环境中,我们可以通过使用旨在捕捉复杂交互效应的新功能来简化模型,而不是将那些复杂的交互项合并到模型构建过程中。
  • 在 GBM 设置中,模型更能够识别复杂的交互,使用工程特征可以减少模型收敛所需的迭代次数。

如果我们在一个受限的环境中工作,这可能是一个额外的祝福。例如,在实际生产中使用模型可能会有一些最大延迟需求,因此可能会限制部署模型的形式或深度。在这种情况下,使用强大的功能以最简洁的方式获取尽可能多的信息是至关重要的。

最后——但肯定不是最不重要的——是特征工程可能揭示对问题的新见解。

我们可以用几种方法来解决这个问题:

  1. 在新设计特性的水平或值范围内检查目标特性的趋势。
  2. 在新设计的特征水平上评估基线预测和目标特征之间的差距。如果我们有一个希望改进的初始模型预测,这很有用。
  3. 在我们的模型中拟合新特征,并评估它们对模型拟合和模型预测的影响;在这种情况下,要素重要性和 SHAP 值会很有用。在这种情况下,评估现有功能和新功能的相互作用也很方便。

一项实验

既然我们已经介绍了特性工程背后的一些理论和动机,让我们来看看它在工作中的一个实际例子。

首先,我们来获取一些数据。在本例中,我们将使用来自 Kaggle 的房价—高级回归技术竞赛的数据(可公开获得,于 2021 年 17 月 12 日从房价—高级回归技术| Kaggle 下载,该数据基于 Dean De Cock 编制的用于数据科学教育的 Ames Housing 数据集- 示例:(amstat.org))。

简而言之:

  • 目的是利用房子的属性(例如,房子的大小、卧室数量、厨房质量等)预测房子的销售价格。).
  • 输入数据是数字和分类特征的混合,需要进行适当的处理。
  • 有一些丢失的数据。这在很大程度上是由获取数据的方式造成的,但需要引起注意。
  • 数据可能足够大,我们可以使用基于树的模型(小心)。这不是目前的主要问题,因为我们正在尝试测试一个想法。

假设:简单的特征工程可以提高预测销售价格的 LightGBM 模型的预测能力。

基本规则

为了让我们在测试我们的假设时保持诚实,我们将设置以下防护栏:

  1. 目标值不会以任何方式转换。
  2. 将使用 5 重交叉验证策略对非折叠数据进行模型性能评估。
  3. 编码、缩放和变换将由特征的类型决定;这意味着新设计的特征将以与原始特征相同的方式处理。在我们的例子中,我们不做数值转换,我们将使用 LightGBM 对分类特征的默认处理。
  4. 模型超参数将在模型运行中保持不变,并且不会有超参数调整。

我们的练习将分为以下几个步骤:原始数据的处理、基础数据的准备、包含新设计特征的扩充数据的准备、基础数据集和扩充数据集的模型构建和预测,以及模型性能的最终评估。

我已经为这个例子编写了管道代码,并将展示它的大部分(可能需要一些想象力来填补空白)。

我们走吧!

处理原始数据

如前所述,我们需要处理丢失的数据。我们将通过以下方式做到这一点:

  1. 如果缺失是由数据捕获方法引起的,我们将用适当的值(例如“无”或 0)填充缺失值。
  2. 如果出现意外缺失,我们将根据附近其他房屋的特征估算缺失值。如果我们考虑分类特征,缺失的值将被邻域中最常见的值替换。如果我们考虑一个数字特征,缺失值将使用邻域中的中值来替换。

我们还将创建一个包含文件夹标识符列。我们现在将使用Id 列,因为它是一个惟一的整数(我不知道在它的创建中有什么模式会引入奇怪的效果),尽管我更喜欢在更正式的设置中使用散列。

准备基础数据

一个简单的小脚本来分离输入数据、交叉验证标识符和目标变量:

准备扩充数据

现在是更有趣的事情——创建新功能。

这通常需要一点创造力,可能是一个非常有趣的头脑风暴练习(我知道这很无聊)。

正如你所看到的,我所创造的特征一点也不复杂或精致;或许可以公平地说,他们可以做一些工作!

然而,这些是LightGBM型号不需要自己解决的特性和效果。

说到这里,让我们来看看我们是如何处理建模和折叠外预测的…

模型建立和预测

我们将使用LightGBM交叉验证方法。这允许我们通过一次调用(并且不需要使用循环)在适当的折叠上训练 k 模型。

我们必须使用一个生成器来创建LightGBM的折叠索引(记得关闭默认的随机播放),并且在聚合之前将折叠预测手动设置为零。

虽然以上是需要记住的技术要点,但我想提请注意我们处理分类特征的方式。我通常会使用类别编码器之类的包来手动编码类别特征,这一次我们尝试了 LightGBM 的原生方法,它几乎不需要手动干预(显然)。我们需要做到以下几点:

  1. 创建列表(不是索引!)的功能名称。
  2. 创建分类的要素(要素名称)列表。
  3. 将每个分类特征转换为category 数据类型。
  4. 将列表和数据作为参数输入LightGBM

评估模型性能

评估基于基本数据的模型和基于增强数据的模型的非折叠模型预测的性能的一些代码。相当手动,并且有改进的潜力:

相当多的指标需要评估——如果我们的假设是正确的,那么我们可以预期大多数指标都会有所改善。

而结果呢!

我们的实验结果如下:

作者图片

我们似乎大获全胜,也就是说,在 5 个不同的指标中,基于扩充数据集构建的模型优于基于基础数据集构建的模型。这是一个相当强的结果!

包装它

…带着一些散漫。

  • 在本文中,我们介绍了一些特性工程理论,即我们在野外可能遇到的各种特性工程。我们简单地提到了作为特征工程方法的模型堆叠,但是没有深入细节。
  • 我们也谈到了为什么我们会考虑特性工程的一些动机。和我的文章一样,我的列表并不详尽,我们很可能只是触及了特性工程冰山的表面。
  • 我们使用一个样本数据集试验了一些特征工程。实验参数保持不变,只有输入数据集允许变化。在基于非折叠模型预测计算的 5 个不同模型性能指标中,我们看到使用扩充数据集构建的模型优于使用基础数据构建的模型。
  • 现在,并不是说将永远是这种情况,或者性能改进将永远是这种明显的。改变管道中的一些元素,例如引入更复杂的数据处理,或引入超参数调整步骤,可能会改变结果。
  • 我们在这里进行了非常基本的特征工程。将来,我想尝试一些更先进的技术,如非负矩阵分解、主成分分析和 t 分布随机邻域嵌入。这些技术比我们进行的简单的求和与求差要复杂得多!
  • 为了评估新功能的影响,我们选择重建完整的模型。由于这在现实世界中可能是不可能的,我们可能不得不采取不同的方法来评估新特性的价值,也许是通过从现有模型中“提升”。使用这种方法进行类似的实验会很有趣——也许以后会有!

一如既往,我希望你喜欢读这篇文章,就像我喜欢写它一样!

新年快乐,2022 年 5 月带来伟大的事情。

让我们做:神经网络

原文:https://towardsdatascience.com/lets-do-neural-networks-d849d80fd012

PyTorch 中构建神经网络以预测保险索赔频率的详细说明

斯凯工作室Unsplash 拍摄的照片

背景

在这个关于神经网络的第七集——是的,第七集——中,我终于开始着手建造一个了。

是时候了!

总之,总结一下我们到目前为止学到的东西:

  1. 神经网络由神经元或节点组成。
  2. 它们被分为输入层(数据进入的地方)、输出层(答案出来的地方)和隐藏层(神奇的事情发生的地方)。隐藏层是网络中既不是输入层也不是输出层的层。
  3. 权重是将网络节点连接在一起的标量值。权重指示两个节点之间的关系强度。绝对权重越大,关联越强。
  4. 每个节点可以有一个偏置项。这是一个标量,帮助模型学习可能不通过“原点”的模式。
  5. 每个节点还具有在其上运行的激活功能。这是一个将节点输入、权重和偏差转换为输出的函数。激活函数是将非线性引入模型的关键。
  6. 节点输出(即激活函数的输出)的连接性质意味着我们可以将神经网络视为嵌套函数,参数为权重和偏差。
  7. 成本函数是衡量模型预测与事实接近程度的指标。有各种成本函数在使用,在大多数情况下,成本越低,模型越好。
  8. 我们使用微积分和计算导数来确定每个权重和偏差对模型成本的贡献。这就是反向传播的本质。
  9. 我们可以结合成本函数、反向传播和专门的优化算法来训练模型。
  10. 这个“学习”过程实际上是一个迭代过程,用于识别最小化成本函数的一组权重和偏差(受某些约束)。

现在,我们将结合我们所知道的——并且可能在这个过程中学到更多——来建立我们自己的神经网络。在我们进入技术部分之前,让我们先谈谈我选择使用的框架:PyTorch。

PyTorch

PyTorch 是一个用 Python 构建机器学习模型的框架。

作为一个框架,它提供了工具和构建模块,允许创建高级机器学习模型,如神经网络。虽然许多功能是预先构建的,但用户仍然需要将各个部分组装在一起,以形成模型结构和训练程序。

三个 PyTorch 模块

PyTorch 的功能主要分为三个模块。

autograd提供导数和梯度的(自动)计算功能。如果你像我一样,有一段时间没有伸展你的微积分肌肉,这很有用。

构建神经网络所需的大多数功能都可以在nn中找到,就像Module一样,我们在那里定义网络的结构以及前向传递是如何工作的。

optim包含预建的优化算法,如流行的 ADAM,有助于模型训练。

PyTorch 作为一个新手

PyTorch 使用自己的数据结构,称为“张量”;这些多维结构具有numpy式的逻辑和语法,让 PyTorch 感觉熟悉

由于我使用的大部分功能都是预先构建的,这使得我作为新人的体验更加易于使用。我很少需要深入研究基础数学来建立和训练我的模型。

GPU 加速非常简单。我想这很大程度上是因为使用了 Nvidia 显卡,但是设置起来又快又简单,在我知道之前,我一直在进行 GPU 的培训。

PyTorch 最有用的方面之一是其网站上提供的详细教程集合和丰富的社区提问(和回答!)论坛上贴出的问题。

PyTorch vs TensorFlow & Keras

虽然我选择使用 PyTorch 并不是因为广泛的研究,但部分原因是因为论坛上反复出现的帖子表明:

  1. 从 TensorFlow v1 迁移到 TensorFlow v2 显著改变了语法。
  2. 大多数可用的教程和资源都是使用 TensorFlow v1 制作的。
  3. 新用户可能不清楚本教程指的是 TensorFlow 的哪个版本。

虽然情况可能不再如此,但我还是接受了 PyTorch(据我所知,py torch 没有这样的问题)。

也就是说,根据快速浏览 TensorFlow 网站上的一些资源,这两者之间可能没有太大的语法差异。谁知道呢,也许有一天我会好好看看。

樊博英够了,该做点实实在在的事情了。

数据

我们将使用由 Christophe Dutang 提供并上传到 OpenML⁴.的大型数据集它可以在 4.0 licence⁵的 CC 下使用,正如我们很快会看到的,我们将对底层信息进行一些更改。当然,我们使用这些数据要自担风险,不在商业范围内。

探索性数据分析

该数据收集了大约 680,000 份法国汽车保险单的风险细节,以及相关的第三方索赔。

在对列进行快速重命名后,我们得到了以下数据集:

作者图片

虽然我不能 100%确定确切的定义,但 guide⁶详细描述了看起来是另一个版本的数据,表明:

  • claim_nb是暴露期(即保单有效期)内的索赔次数
  • exposure是保单面临风险的时间,以年表示。这只是保险公司向投保人提供保险的一年比例的一种奇特的精算方式。
  • area是汽车司机居住的城市社区的密度值。从代表农村地区的“A”到代表城市中心的“F”不等。
  • region是法国政策地区。看起来像是某种常见或广泛使用的分类形式。
  • density是人口密度估计值,即投保人所在城市每公里的居民人数。
  • driv_age是驾驶员的年龄(年)。
  • bonus_malus是对投保人的奖惩评级。显然在法国,价值 100 =奖金 100 =苹果。 Bonus malus 是一个拉丁文术语,意思是“好的坏的”。从本质上讲,这是一个奖励良好索赔行为的客户、惩罚不良索赔行为的系统——也就是说,如果你的保险索赔较少,你就可以预期支付较少的保险费用。在英国,这也被称为“无索赔折扣”。
  • veh_power是汽车的动力,分组为有序的类别。
  • veh_age是车辆的年龄,以年为单位。
  • veh_brand是车辆品牌,分为不同的子组。
  • veh_gas是车辆燃料类型。在这种情况下,我们没有任何种类的电动或混合动力汽车。

尽管被删减了,这个数据集还是相当有代表性的,我们用它来建立英国的汽车保险定价模型。通常,我们收集和使用属于以下四类之一的功能:

  1. 与保单相关的特征 —这可能是保险级别、保单销售时有效的任何促销或折扣等。
  2. 与车辆驾驶员相关的特征 —这可能是驾驶员的年龄、先前的索赔历史、主驾驶员的职业。理想情况下,将为策略上列出的所有驱动程序收集相同的信息,以及主驱动程序和任何其他列出的驱动程序之间的关系。
  3. 与车辆可能行驶的区域相关的特征 —与上述areadensity非常相似,这些特征与物理环境相关。地理和人口统计特征通常是保险风险的高度预测因素。
  4. 与投保车辆相关的特征 —考虑车辆品牌、型号和规格(及相关细节)以及其他特征,如车龄和投保人拥有车辆的时间。

总之,说够了,让我们回到正题上来。

观察单向视角通常有助于我们了解趋势。我们将计算索赔频率——每单位风险敞口的索赔数量——从总体上看。

首先是area,单调趋势的一个好例子:

作者图片

这里我们有:

  • 黄色条表示area每一类别中总暴露量的比例
  • 一条粉色线表示每一类area的索赔频率,以百分比表示。

回想一下数据定义,我们知道这实际上是一个有序的分类特征,其中“A”代表农村地区,级别代表日益城市化的地区(“F”代表城市中心)。

因此,我们看到频率随着area的增加而增加——这是有道理的,因为我们预计在交通更繁忙、道路更繁忙的繁忙地区会发生更多事故。我们预计农村地区的情况正好相反。

此外:在城市化程度更高的地区,索赔频率更高是很常见的。然而,这并不意味着住在农村地区会得到更便宜的保险,因为农村地区的事故严重程度往往高于城市中心的事故……想想典型的交通堵塞中的轻微轻微车祸和高速行驶中的撞车事故。

让我们来看看一些不那么单调的东西——车辆品牌:

作者图片

在这里,看起来似乎是单向的,除了品牌 B11 和 B12 的频率水平较高之外,大多数品牌的车辆品牌索赔频率相当稳定,约为 9.5%。另一方面,B14 显示出明显较低的索赔频率。

这些都是非常重要的观察结果,我们希望我们的模型能够抓住这些趋势,尤其是我们看到销量最大的品牌(B1、B2 和 B12)。

也许这也是一个提醒我们自己的好时机,虽然特性的标签可能意味着某种排序,但这不一定是这样的,因此期望它有某种形式的单调趋势可能有些过头了。

让我们继续为建模准备数据。

数据准备

我们正在处理各种类型的数据—数字和分类的混合。因为我们只能将数字数据输入到我们的神经网络中,所以我们需要进行某种形式的转换(以及一些预处理)。我们还需要将数据分成子集,以帮助我们进行训练和验证。

为了避免任何潜在的目标泄漏,我们应该在计算训练集上所需的转换之前拆分数据。

数据分割

这里非常简单——我们将使用 scikit-learn 的train_test_split功能。不过,我们将按顺序进行,所以我们最终会得到我们想要的集合。

from sklearn.model_selection import train_test_split

# split out the validation set
train_x, val_x, train_y, val_y,train_evy,val_evy = train_test_split(
    df.drop(labels = ['claim_nb','exposure'],axis = 1),
    df['claim_nb'],
    df['exposure'],
    test_size = 0.15,
    random_state = 0,
    shuffle = True
)

# now split train and test
train_x, test_x, train_y, test_y,train_evy,test_evy = train_test_split(
    train_x,
    train_y,
    train_evy,
    test_size = 0.2,
    random_state = 0,
    shuffle = True
)

设置随机种子总是个好主意。注意我们如何在第二次调用 train_test_split时增加 test_size 。这是为了确保我们的测试集比验证集稍大。

这会产生以下数据集:

图像是作者

到目前为止,一切顺利。

对特征进行分组和变换

对输入变量应用某种形式的分组是很常见的。

这做了几件事,最重要的是它降低了输入数据的粒度。降低粒度会降低模型的潜在预测能力,因为模型实际上可用的信息更少。

但是,如果在数量少、可变性高的段上应用分组,减小粒度可以提高稳定性。所以——和大多数事情一样——我们需要在粒度和稳定性之间达到适当的平衡。

在一次热编码之前对特征进行分组可以减小结果(编码)数据集的大小。提示提示。

我们将(在很大程度上)遵循这个 paper⁹的领导,并应用以下乐队和转换的功能:

  • area作为分类特征保持不变,region也是如此。
  • density应用了对数变换,并保持数字特征。
  • driv_age被分成以下组并转换为一个字符串:(0,18],(18,21],(21,25],(25,35],(35,45],(45,55),(55,70),(70,.]。
  • bonus_malus保持不变。
  • veh_power被转换成字符串。
  • veh_age被分成以下几组并转换成字符串:(-1,0],(0,1],(1,4],(4,10),(10,)。
  • veh_brand保持不变,veh_gas也是如此。

由于我们将应用相同的转换三次,我们将通过创建一个助手函数来使我们的生活变得更容易(并减少人工错误的可能性)。这看起来像是:

import pandas as pd
import numpy as np

# mappers
mapper_vehpower = {j:str(int(j)) for j in df['veh_power'].unique()}
mapper_vehbrand = {j:j for j in df['veh_brand'].unique()}
mapper_vehgas = {j:j for j in df['veh_gas'].unique()}
mapper_area = {j:j for j in df['area'].unique()}
mapper_region = {j:j for j in df['region'].unique()}

# bin edges for bandings
bins_vehage = [-1,0,1,4,10,np.inf]
bins_drivage = [0,18,21,25,35,45,55,70,np.inf]

# helper function
def transform_data(data):

    '''
    Function to apply transformations to input data.
    '''

    d = data.copy()

    # location features
    d['area'] = d['area'].map(mapper_area).astype(str)
    d['region'] = d['region'].map(mapper_region).astype(str)
    d['density'] = np.log(d['density'])

    # vehicle features
    d['veh_power'] = d['veh_power'].map(mapper_vehpower).astype(str)
    d['veh_age'] = pd.cut(d['veh_age'],bins = bins_vehage).astype(str)
    d['veh_brand'] = d['veh_brand'].map(mapper_vehbrand).astype(str)
    d['veh_gas'] = d['veh_gas'].map(mapper_vehgas).astype(str)

    # driver features
    d['driv_age'] = pd.cut(d['driv_age'],bins = bins_drivage).astype(str)

    return d

# apply groupings and transformations
train_x = transform_data(train_x)

即使有些东西不需要编码(比如veh_gas),显式编码也是有用的。有一点是肯定的——它真的有助于记忆!

眼尖的读者会注意到,我从组合数据帧df而不是训练集(就像我之前宣扬的那样)中获取了一个特性可能获得的所有可能的唯一值,这多少有些作弊。

我应该正确地完成它,并处理测试或验证集中存在的值在训练集中不存在的任何实例。这将更加现实和稳健。

但是,如果我们谈论现实主义,那么如果我们没有对馈入模型的数据管道进行强有力的控制,我们就不会在像汽车保险这样高度监管和竞争的环境中建立神经网络。这意味着在现实世界的设置中,我们将确切地知道所有我们的功能可能采用的潜在值,并且我们可以构建适当的转换器来处理这些值。

现在,关于分组特征的最后一件事。让我们来看看(分组)车龄:

作者图片

上图显示了按车龄分类的索赔频率。我们可以看到,全新汽车的索赔频率有所提高,即veh_age == 0

现在,如果我们退后一步,在不看数据的情况下考虑潜在的分组,我们可能会倾向于将全新的汽车与相当新的汽车分组(例如,将 0 岁的汽车与 1 岁的汽车分组)。如果我们这样做,我们将缓和索赔频率的巨大差异,这反过来会对模型的预测能力产生负面影响。

长话短说——分组前先看看!

一个热编码

我在上面暗示过,但是我将对(分组的)分类特征进行一次热编码。

我不会对此大做文章,因为这是一种很常见的方法,而且有大量的教程可供参考。在我看来,有两个很好的软件包可以试用——scikit-learn 和⁰.分类编码器我更喜欢类别编码器,因为我发现它比 scikit-learn 的实现更宽容。

模型

既然我们已经准备好了数据,我们就要开始建模了。

我将我的建模工作流程分为两个不同的阶段,首先定义模型类,然后定义训练循环。

模特班

还记得我说过 PyTorch 提供了构建模型的工具,而不是模型本身吗?这是一个很好的例子!

我们的模型是从 PyTorch 的nn.Module继承的 Python 类。我们必须定义网络的结构及其初始化,以及信息如何通过网络传递(“前向传递”)。

我是这样设置我的模型的:

class NeuralNetwork(torch.nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()

        # linear
        self.F_hidden_one = torch.nn.Linear(75 + 1, 250)
        self.F_hidden_two = torch.nn.Linear(250, 250)
        self.F_output = torch.nn.Linear(250, 1)

        # add in drop out
        self.dropout_one = torch.nn.Dropout(p = 0.25)
        self.dropout_two = torch.nn.Dropout(p = 0.25)

        # initialise weights     
        # He initialisation
        torch.nn.init.kaiming_uniform_(self.F_hidden_one.weight)
        torch.nn.init.kaiming_uniform_(self.F_hidden_two.weight)
        torch.nn.init.kaiming_uniform_(self.F_output.weight)

        # initialise the final bias
        torch.nn.init.constant_(self.F_output.bias,y_hat)

    def forward(self, x):
        # ELU activations
        elu = torch.nn.ELU(alpha = 1)

        # calculate F
        F = self.dropout_one(x)
        F = self.F_hidden_one(F)
        F = elu(F)
        F = self.dropout_two(F)
        F = self.F_hidden_two(F)
        F = elu(F)
        F = self.F_output(F)
        F = torch.exp(F)

        return F

先不说:使用 nn.Sequential有一个等价的定义网络的方法。我发现这种方式比 Sequential 更清晰、更容易理解,我会推荐你使用你喜欢的任何一种形式。

简而言之:

  • 这是一个具有两个隐藏层的前馈神经网络。
  • 有 75 + 1 个输入节点——每列一个,外加一个额外的列(稍后会详细介绍)。
  • 每个隐藏层中有 250 个节点。
  • 概率为 0.25 的丢弃应用于每个隐藏层(稍后会有更多的介绍)
  • 明凯(他)为权重初始化。
  • y_hat初始化的输出层偏差,平均索赔频率,调整以反映网络的形式。
  • 用于隐藏层的指数线性单位(ELU)激活。
  • 在最后一层求幂。

输出层偏差的初始化是一个有趣的问题,这是我在排除其他故障时偶然发现的。

背后的想法相当简单——为了改进训练时间,我们给网络一个目标输出可能看起来像什么的“一般想法”,记住转换到适当的空间。在这种情况下,“总体思路”是平均索赔频率,我们已经将它(在其他地方)转化为日志空间——即y_hat = log(mean claim frequency)。如果你熟悉梯度增强的树模型,这个想法与使用初始或基础分数没有什么不同。

同样值得一提的是,在输出最终预测之前,对前向结果求幂。这个公式看起来非常像带有对数链接的经典广义线性模型,但是我进行了转换以确保网络输出为正。也许类似于 ReLU 的替代方法也可以实现这一点,但是在实验过程中,我发现取指数会产生更好的结果。

训练循环

该是我们写训练循环的时候了——模型必须正确学习!

为了训练我们的模型,我们将结合神经网络中的几个重要概念。

在我们初始化我们的模型后,我们将向前传递训练数据以获得初始模型预测。

然后,我们将计算训练目标的损失,并通过网络将其传播回来(反向传播)。所选的优化器将更新权重和偏差,并结束训练循环的单次迭代。

就代码而言,这看起来像这样:

# new model
model = NeuralNetwork().to(device)

# optimiser
optimiser = torch.optim.Adam(model.parameters(),lr = 0.001)

# training loop
for epoch in range(1,n_epoch+1):

    # training mode
    model.train()

    # prediction error
    F = model(train_X)

    # back-prop & weight update
    train_loss = poisson_loss(train_y,train_evy.float() * F)
    train_loss.backward()
    optimiser.zero_grad()
    optimiser.step()

一些细节:

  • 我们用model = NeuralNetwork()创建模型的一个新实例。我们使用to(device)将模型发送到 GPU(如果可用,设备是包含 GPU 名称的变量)。
  • 我们设置了一个学习率为0.001的亚当优化器。为此,我们输入原始未训练模型的权重和偏差(model.parameters())。
  • model.train()激活模型的“学习”模式。
  • F是使用训练数据正向遍历模型的结果。
  • 在反映了策略在预测中生效的时间之后,使用自定义函数poisson_loss计算训练损失(即通过 EVY 进行缩放)。
  • backward()方法进行反向传播。
  • 使用optimiser.zero_grad()从优化器中清除任何先前循环的梯度,并使用optimiser.step()更新本轮训练的权重和偏差。

当损失在减少时,我们不断地通过训练循环;当我们关注训练损失时,我们可能会为过度适应和不良概括敞开大门。我们需要采取一些措施来避免这种情况。

过度拟合和提前停止

我们可以使用的一个工具是早期停止,这是一种旨在检测过度拟合并防止模型走得太远的方法。

提前停止背后的想法很简单。该模型在一组数据上学习,并在“看不见的”数据上测试。在我们的例子中,这些分别是我们的训练集和测试集。

当我们重复训练时,我们希望训练和测试集的损失都减少。这代表了一个预测性越来越强但仍具有普遍性的模型。

然而,很有可能的是,如果我们训练足够长的时间,我们的模型将开始拾取训练集中的噪声,而不是真实的信号。我们将会看到这种一般化的损失成为看不见的数据损失的恶化。诀窍是训练模型,直到我们在看不见的数据上实现最佳损失。

但是“突破”呢?我想从形式上来说,这将是逃避局部最小值的乐观者;简单地说,我们可以把乐观主义者想象成了解什么不起作用,什么起作用。

这暗示了某种试探,并强调了早期停止的另一个要求:需要允许乐观者尝试不同的事情。因此,我们需要在混合中引入某种形式的容差——允许乐观者嗅出路线,而不会走得太远。

现在,如果测试损失没有改善,超出了耐受范围,怎么办?好吧,我们回到模型的“最佳”版本。为此,我们需要记录模型的“最佳”版本。

我们可以迅速做出一些东西,并将其构建到培训循环中:

# initial set up
last_loss = 2_000_000
best_loss = last_loss
stopping_rounds = 200
counter = 0
model_state = model.state_dict()
minimum_improvement = 0.001

# training loop
for epoch in range(1,n_epoch+1):

    # training mode
    model.train()

    # prediction error
    F = model(train_X)

    # back-prop & weight update
    train_loss = poisson_loss(train_y,train_evy.float() * F)
    train_loss.backward()
    optimiser.zero_grad()
    optimiser.step()

    # test accuracy
    model.eval()
    with torch.no_grad():
        F = model(test_X)

    test_loss = poisson_loss(test_y,test_evy.float() * F).item()

    # early stopping
    current_loss = test_loss

    if current_loss >= best_loss:
        # loss does not improve - increment the counter
        counter += 1

        # if counter exceeds permissible rounds then break the loop
        if counter >= stopping_rounds:
            print(f'Early stopping after {epoch:,.0f} epochs')
            print(f'Best loss: {best_loss:,.4f}')
            print(f'Loss this epoch: {current_loss:,.4f}')
            print(f'Min. improvement required: {minimum_improvement:.2%}')
            break

    elif (
current_loss < best_loss
) * (current_loss > best_loss * (1 - minimum_improvement)):
        # loss improves but not my minimum required, increment the counter
        counter += 1

        # update best loss and model state
        best_loss = current_loss
        model_state = model.state_dict()

        # if counter exceeds permissible rounds then break the loop
        if counter >= stopping_rounds:
            print(f'Early stopping after {epoch:,.0f} epochs')
            print(f'Best loss: {best_loss:,.4f}')
            print(f'Loss this epoch: {current_loss:,.4f}')
            print(f'Min. improvement required: {minimum_improvement:.2%}')
            break

    else:
        # loss improves more than minimum required
        # reset the counter, update state, update best loss
        counter = 0
        model_state = model.state_dict()
        best_loss = current_loss

为提高可读性而格式化的代码。

我的小黑客做的很简单。

初始设置定义了“最后损失”和“最佳损失”,以及允许的公差量stopping_rounds和所需的测试损失的最小改善。

我们设置了三种可能的情况:(1)如果测试损失没有改善,(2)如果测试损失改善但没有达到所需的最小改善,以及(3)如果测试损失改善超过所需的最小改善。

如果测试损失没有改善,我们会在分数中增加另一个分数,即增加counter,这是测试损失没有改善或没有改善到最低要求的回合数的记录。如果计数超过给定的容差,循环就被打破,我们就停止训练——也就是说,我们提前停止。

在第二种情况下,试验损失有所改善,但没有达到最低要求。在这个例子中,我们更新了我们的最佳损失记录并更新了模型状态字典——我们想承认这仍然是一个改进。然而,我们也增加计数器并检查它是否超过容差。

如果测试损耗的改善超过最小值,我们将更新最佳损耗、模型状态,并重置计数器以期待进一步的增益!

过度拟合和辍学

一段时间后,神经网络可以开始适应训练数据集中的噪声,降低网络概括新的和看不见的输入的能力。在我们的情况下,这显然是不可取的。

这是机器学习大师的杰森·布朗利说的:

减少过度拟合的一种方法是在同一数据集上拟合所有可能的不同神经网络,并对每个模型的预测进行平均。这在实践中是不可行的,可以使用不同模型的小集合来近似,称为集合。

现在我可以想象,为了有效地集合网络,我们首先需要大量的网络。在我们的例子中,这可能不是完全不现实的,但是这种方法在更大或更复杂的用例中会很快变得不可行。

输入 dropout——一种允许我们近似训练和集合许多不同神经网络的方法。

再说一次,杰森·布朗利:

在训练过程中,一些层输出被随机忽略或“丢失”这具有使该层看起来像并且被视为像具有不同数量的节点和与前一层的连通性的层的效果。实际上,训练期间对层的每一次更新都是通过已配置层的不同“视图来执行的…

丢弃会使训练过程变得嘈杂,迫使层中的节点在概率上或多或少地承担输入的责任…

这种概念化表明,也许丢失打破了网络层共同适应以纠正前一层错误的情况,从而使模型更加健壮。

所以,如果我换个说法来构建:

  • 每次我们运行训练步骤时,我们会随机“关闭”一些节点。
  • 通过“关闭”,我们实际上意味着用零覆盖节点的输入和输出。
  • 这使得剩余节点更有可能拾取信号,而不是纠正来自先前层的错误。
  • 丢弃是按层实现的,可以引入到除输出层之外的任何层。
  • 任何一个节点被置零的概率,即“丢失率”,是用户设置的一个额外的超参数。
  • 在预测阶段,辍学是无效的。

如果你和我一样,喜欢将基于树的模型相提并论,我认为 dropout 类似于列采样,尽管一些模型已经开始在其算法中实现 dropout。

此外:Ayush Thakur 有一个很棒的关于体重偏差的动画&直观地展示了辍学。

GPU 上的培训

简单说一下,因为我更愿意谈论结果!

我目前正在我的个人笔记本电脑上工作。虽然它更像是“可以的小笔记本电脑”,而不是一个专用的深度学习设备,但它确实有一个专用的 Nvidia GPU 卡,我一直在充分利用这个卡。

尽管如此,我一直在我的 GPU 上训练模型,将整个训练集一次性通过模型。虽然这意味着我可以利用 GPU 加速(并且不用担心批量训练),但它确实设置了一些限制。

一个限制是模型大小。使用两个各有 250 个节点的隐藏层是我的 GPU 所能处理的极限,当我试图增加节点数时很快就遇到了内存问题(使用 dropout 时推荐)。稍后将详细介绍这一点。

总的来说,我使用 GPU 的体验总体来说很顺利。初始安装和设置很快,一旦 PyTorch 检测到任何可用的 GPU ( device = cuda if torch.cuda.is_available() else 'cpu'),使用.to(device)方法将对象移入和移出 GPU 就很容易了。

保持所有东西都在哪里有点麻烦,但是你很快就会习惯。

现在,让我们来看看激动人心的事情——结果!

结果呢

终于到了看看模特引擎盖下到底发生了什么的时候了!

有几种方法可以做到这一点。我们将从一些更简单的视觉化开始,然后逐渐过渡到一些更复杂的解释器。

单向实际与预期

顾名思义,这些可视化是将实际目标与预期(或“模拟”)结果进行比较,每次跨越单个特征的水平(“单向”)。它们有时也被称为“AvE”图。

此外:有一个合理的理由让你有似曾相识的感觉——这些事实上是建立在我们之前看过的图表之上的。

这里有一个area的例子:

作者图片

快速复习:

  • 沿着 x 轴我们有area的等级
  • 黄色条(左边的 y 轴)显示总暴露量的比例,该比例属于area的每个级别
  • 粉色和绿色线条(右侧 y 轴)分别代表实际索赔频率和预测索赔频率

在本例中,我们看到我们的模型略微超出了区域 A、B 和 C 的实际值,而低于区域 E 和 f 的实际值。

如果我们快速浏览这些 AvE 图表以了解其他一些特征,我们会发现一些有趣的事情——这里的模型很好地捕捉了region中大部分暴露的非线性趋势:

作者图片

driv_age也是如此:

作者图片

…尽管在平均预测值中可以清楚地看到适用于驾驶员年龄的分组(即深绿色线中明显的台阶)。

总的来说,单向的模型拟合看起来还不算太差。

让我们继续研究相互作用。

多向图

可视化特征交互的一个简洁方法是创建多向图:通过一个、两个甚至三个特征绘制预测的索赔频率。这给了我们一种方式来理解——平均而言——不同的特性如何相互作用

例如,下面是驾驶员年龄和区域相互作用的多向图:

作者图片

该图表易于阅读——沿 x 轴是不同级别的驾驶员年龄,y 轴是预测的索赔频率。每条彩色线代表一层区域。

在这里,我们看到了我们之前在驾驶员年龄中看到的一般趋势——较低年龄组的高频率,随着年龄范围的上升而下降。

我们也看到了熟悉的区域趋势:A 区一般比 B 区风险小,B 区一般比 C 区风险小,等等。

有趣的是这些特征之间的关系。

让我们首先关注 B 区和 C 区:

  • 我们的模型认为居住在 C 区的≤ 22 岁的司机比居住在 b 区的同龄司机风险更小。
  • 对于年龄≤ 22 岁的驾驶员,绿线明显低于橙线。
  • 然而,对于 23 岁及以上的司机来说,这种影响似乎相反,模型认为住在 C 区的司机比住在 b 区的同龄司机更危险。
  • 我们看到,对于 23 岁以上的司机来说,绿线始终高于橙线。

在 D 区和 E 区也有一个有趣的影响,我们看到一个非常相似的索赔频率预测,直到司机 21 岁,之后有明显和一致的差异。

有时改变多向图的基础可以使发现交互更容易;这里有一个我重新设定的例子,用来显示预测频率与组最小值的比率:

作者图片

尽管我很喜欢这些多向观想,但它们不是灵丹妙药。

首先,他们依赖于知道要询问哪些交互的建模者。如果您正在处理一个熟悉的任务或者对该问题有很好的领域知识,这可能是好的,但是如果您不确定从哪里开始寻找,这可能会成为问题。在这种情况下,使用某种特征选择方法来缩小“重要”交互将是有用的(看看弗里德曼的 H 统计)。

虽然多向图提供了比单向图更多的洞察力,但它并不能提供完整的故事。例如,我们不能肯定地说,上述影响完全是由于司机年龄和地区的相互作用——可能有其他投保人特征对这一趋势负责。

当处理高基数特征时,或者当向多向分割添加额外的“深度”(即更多特征)时,多向图表很快变得难以理解和难以解释。不是世界末日,而是要记住的事情!

让我们转而使用数据科学社区中更普遍的东西——SHAP 图。

SHAP

我们可以使用 SHAP 包来研究我们奇特的新模型,或者使用更通用的内核解释器,或者使用DeepExplainer的实现。

设置非常简单——首先创建解释器,然后获得 Shaply 值:

# create the explainer
explainer = shap.DeepExplainer(
    model,
    torch.tensor(train_X.values).float()
)

# get the shapley values
shap_values = explainer.shap_values(
    torch.tensor(train_X.values).float()
)

然后我们可以得出特征重要性:

作者图片

特征编码使得理解一个特征作为一个整体如何影响预测有点困难,但是我们可以看到一些粒度效应。

例如,看起来驾驶一辆旧车会增加第三方汽车保险索赔的可能性。或许——更令人期待的是——更高水平的奖金 malus 推动了更高的索赔频率预测。

SHAP 依赖图让我们看到两个特征之间的相互作用。更好的是,用户可以指定他们希望可视化的功能,SHAP 软件包可以为该功能建议一个“有趣”的交互。整洁!

这是一个附加苹果 x 密度相互作用的依赖图的例子。同样,不是最有用的,但我们可以看到奖金 Malus 如何影响预测。

作者图片

力量图让我们看到每个投保人的特征是如何驱动其预测的。

这是一个随机选择的观察图表:

作者图片

…我们可以看到:

  • 每个预测的“基值”或起点是 0.1137。
  • 蓝色条表示驱使预测下降的特征。在这里,我们看到驾驶一辆更新的汽车降低了这个预测。
  • 红色条代表推动预测上升的特征。例如,当一名 18-21 岁的司机会增加你的频率预测。
  • 最终预测值(即 f(x))为 0.08,是从基础位置开始并应用向上和向下移动的结果。

这些图表对于检查个人的预测非常有用;我强烈推荐用它们来检查非常大或非常小的预测。

其他方法

SHAP 是伟大的,但是它不是获得特性重要性的唯一方法。

排列重要性 ⁰以一种略微不同的方式来解决这个问题,当一个特征被随机打乱时,通过预测准确性的降低来衡量重要性。从本质上讲,洗牌造成的准确度降低越大,该特征对模型就越“重要”。

我还想看看 Captum ⁰,一个基于 PyTorch 的模型可解释性软件包。

最后,我们可以从基本原则出发,分析特性的重要性,并得出某种形式的加权贡献。这是基于网络权重指示节点之间的关系强度的基本原理;聚焦于将输入层连接到第一隐藏层的权重可以允许导出“基于权重的”特征重要性。

不太顺利的事情

不出所料,事情并没有完全按照计划进行。

事实上,许多事情并没有按照计划进行。这里只是一个最令人难忘的“实验”的简短列表。

旁白:我应该强调的是,这些都是我曾经面临的问题——很有可能你不会有同样的经历。当然,只是因为我不能得到一些工作并不意味着它不会工作!

零膨胀泊松模型

通常使用泊松过程对计数数据进行建模。乍一看,这似乎是一种合适的保险索赔数字建模方法(因为它们只是对投保人可能发生的索赔数量的计数),但它比这稍微复杂一些:

  • 绝大多数投保人根本没有索赔(我们看到不到 10%的培训数据有相关的索赔)
  • 投保人可以有一个以上的索赔

因此,在某种程度上,这变成了一个“嵌套”建模问题,我们首先需要了解哪些投保人可能有索赔权,然后预测他们可能有多少索赔权(假设他们有索赔权)。

进入零膨胀泊松(ZIP)模型,这是一个结合了两种不同过程的混合模型:

  1. 产生零的过程。
  2. 产生泊松计数(其中一些可能为零)的过程。

现在更精确地说:

作者图片

在这种设置中,神经网络被设计成输出两个量——π和λ——并被训练成使得负对数似然损失函数最小化。这实际上是(9)中概述的 NeurFS 模型的“F”部分。

我使用这种方法尝试了各种实验,但总是发现我的预测偏离了一个一致的因素,尽管对网络的调整似乎使预测上下移动,但预测从未达到合理的水平。

我还发现很难理解什么是“好的”负对数似然得分。虽然对于一些损失函数(比如梅或 RMSE)来说,很容易理解什么是好的(甚至完美的)分数,但负对数似然函数绝对不是这样。

这是一个相当有趣的建模索赔频率的方法,我可能会在未来返回,并希望开始工作!

如果你有兴趣阅读关于零膨胀泊松模型的更多内容,我建议你看看:(14)、(150)、(16)。

不稳定模型

…或者“啊,为什么事情总是在变?!"。

单独的模型运行在预测和拟合参数方面产生了非常显著不同的结果。

我尝试了各种方法,包括设置随机种子和使用不同的权重和偏差初始化,但这些改变似乎没有帮助——网络仍然不稳定。

然后我看到一个讨论,认为较大的网络比较小的网络更稳定。潜在的逻辑是(换句话说,我的我):

  • 较小的网络需要将信息塞进较小的包中…
  • 实际上意味着节点连接包含更多信息…
  • 因此,随着更多“信号”被关闭,某些节点的“去激活”——通过丢弃或初始化——会产生很大影响…
  • 这会导致网络不稳定。

从较小的网络(每个隐藏层 100 个节点)转移到较大的网络(每个隐藏层 250 个节点)似乎可以解决这个问题。

大型模型和批量训练

现在我已经有了一个相当大且稳定的网络,我渴望从中获得尽可能多的性能。

我渴望权力,想要向四面八方扩展模型——更多的隐藏层,更多的节点,更多的一切!

只是,我的 GPU 说不。事实上,非常强调。

这意味着如果我要实现我的宏伟设计,我将需要使用批量训练。

批量训练的概念非常简单:在数据的子集上重复训练模型,直到它已经看到完整的训练数据(即,一个“时期”),并对许多训练回合重复这一过程。

天下没有免费的午餐,因为批量训练有其自身的细微差别。

一个细微差别是网络中更多的不稳定性;网络一次只能看到一个数据子集,因此批量中目标的任何大的变化(以及因此的损失)都会导致参数更新的中断。

在我们的案例中,绝大多数索赔计数为零,但我们确实有一些投保人有 11 项索赔的情况。这将造成严重的训练损失,并且当权重和偏差更新时,可能会引起相当大的变化。

通过使用更大的(潜在分层的)批次,这种现象可以在一定程度上得到缓解,因为理论上每一批次更能代表整个目标。但是“大”有多大呢?批量大小是另一个可调参数,它不仅会影响训练时间,还会影响模型的学习方式(从而影响模型的预测能力)。似乎有一些证据表明,使用较小的批次会产生更好的结果,但这可能是需要由建模者根据具体情况确定的另一个因素。

批量标准化⁷是一种经常用于解决批量训练中不稳定性的方法。这种技术本质上是学习应用于流经网络的数据的转换,确保网络内的所有数据保持在可管理的范围内。

建议不要同时使用批量规格化和丢失,因为它们分别减少和引入噪声,就像一只脚踩刹车,另一只脚踩油门一样。

所有这些都是相当技术性的东西,我们还没有到批量训练的更实际的元素:训练时间。当我们使用整个数据集进行训练时,反向传播和参数更新发生在数据集流经模型之后——本质上,我们进行number of rounds更新。批量训练需要在每一批通过模型后进行更新,所以我们有效地做number of batchs x number of rounds更新;不管你喜不喜欢,这在计算上会更慢。

我希望我能汇报一下批量训练对我的模型的表现有多大的改善。虽然我确实启动并运行了批量训练过程,但我也确实重新安排了 r̵u̵n̵̵o̵u̵t̵̵o̵f̵̵p̵a̵t̵i̵e̵n̵c̵e̵的优先级,并决定恢复到更小、对 GPU 更友好的模型。

(差点)被树打了

我对基于树的模型相当熟悉,所以想比较和对比我的神经网络与梯度增强树模型(特别是 LightGBM)的性能。

我在相同的训练数据上建立了一个非常简单的 GBM 模型,并对神经网络进行了一些测试。模型性能结果是相似的。事实上非常相似。

我很好奇,想知道这是一个已知的普遍现象,还是仅仅是“我”的问题。事实证明,它似乎不是这个用例的特定内容!

鲍里索夫等⁸注:

异构表格数据是最常用的数据形式,对于许多关键和计算要求高的应用程序来说是必不可少的。在同质数据集上,深度神经网络一再表现出优异的性能,因此被广泛采用。然而,他们的适应表格数据的推理或数据生成任务仍然极具挑战性。

但是证据呢?是否有经验证据表明神经网络在混合型表格数据上不容易胜过 GBM?

事实上,⁹:在不同数据集上对混合任务进行了 42 次实验,LightGBM 的表现优于神经网络 35:7。这里有一些细微的差别,但它仍然是一个相当大的打击。

让我们把它放在那里,结束吧——你现在可能已经厌倦我了。

一个总结,只是多一点散漫

包裹

我们在这一次已经涉及了很多领域。让我们总结和反思。

我们更新了关于神经网络的知识,并简要了解了用于构建模型的框架 PyTorch。

我们花了一点时间讨论数据和应用于数据的工程学。我可能花了比预期更多的时间来谈论它,但希望我的漫谈能给外行人一些现实世界的保险见解。

我经历了模型结构,训练循环,通过退出解决过度适应,通过早期停止解决过度适应。我还花了一点时间了解 GPU 上的培训是如何为我工作的。

我们花了一些时间检查结果。我们看到了像 AvE 图表这样简单的可视化如何简单地洞察模型拟合。我们还看到了如何使用 SHAP 软件包来更深入地了解神经网络这个“黑箱”。

我分享了一些(许多)不太适合我的事情。

我们看到零膨胀泊松理论听起来有潜力,但是我的实现并没有产生很大的结果。

扩大网络的规模克服了一些稳定性问题,但增加太多需要转向批量训练;批量训练有其自身的复杂性。

最令人失望的是,我们还看到我的网络几乎被 LightGBM 模型超越。

反思和下一次的事情

构建神经网络是困难的,我毫不惭愧地承认,我花了一些时间来理解这些模型的复杂性(我只是触及了表面)。也就是说,有一些很好的资源可用。然而,我确实发现,大多数资源都是针对初学者或高级从业者的,所以这两者之间似乎有一点差距。

我短暂的经验表明,建立一个好的神经网络就像撬锁一样——为了让模型工作,一切都必须兼容,一切都必须排列正确。我对开锁并不太了解。

下次我会做一些不同的事情。我要做的一个主要改变是将单独的代码片段缝合到一个管道中,或者将容易组装的代码片段作为一个管道运行。这将允许更多的实验,并允许在管道的其他部分进行实验——例如,在数据准备中,我很想尝试不同的功能编码。

我之前提到过,我会解释为什么我有 75 + 1 个输入层节点,我已经戏弄了足够长的时间。这只是从回归中借用的一个想法:设计矩阵包含一列用于估计截距的 1。我发现包括这个改进的模型性能,即使我通过输出层偏差项输入关于“截距”的建议。这是下次要探讨的其他内容。

说到更传统的回归,我可能应该详细解释一下为什么我没有使用风险敞口作为协变量,尽管它是索赔频率的一个强有力的预测因素。大多数泊松回归都使用它,要么将log(exposure)作为“补偿”项,要么对计数与暴露的比率(即count / exposure)进行建模。答案是,我尝试了不同的使用方法,它们并没有真正影响模型性能。它仍然存在于训练循环中——在计算训练损失之前,我通过暴露来衡量预测——但它没有直接输入到模型中。

下次还有一件事——辍学率。我使用的比率是 0.25,即节点被“归零”的概率是 25%。这明显低于我在其他文章和论坛看到的。我觉得这个选择很好,因为我也使用了提前停止的实现来避免过度拟合,但如果提前停止不是一个选项,我会考虑提高辍学率。

我很惊讶地发现网络在混合表格数据上的表现(相当平淡无奇)。虽然地平线上可能会有一些好消息,所以我一定会关注 NODE 和 TabNet ⁴.

这就是我要说的。我希望这是有用的。请随时在评论中提供反馈——因为我是这方面的新手,我将非常感谢你可能有的任何提示和指点。

又到了一年中的这个时候,我想祝你和你所爱的人节日快乐。就当这是我送给你的闪亮圣诞礼物。或煤块。

证明人、信用和许可证

  1. 让我们来学习:神经网络#1。我学习的一步一步的编年史… |作者布拉德利·斯蒂芬·肖| Medium
  2. PyTorch
  3. 张量流
  4. OpenML
  5. 知识共享——归属 4.0 国际——CC BY 4.0
  6. CASdatasets-manual.pdf(uqam . ca)
  7. 目标编码分类变量|作者 Viní cius Trevisan |走向数据科学
  8. 什么是机器学习中的目标泄漏,我如何避免?—数据机器人 AI 云
  9. (PDF)神经频率-严重性模型及其在保险索赔中的应用(researchgate.net)
  10. 类别编码器—类别编码器 2.5.1.post0 文档(scikit-learn.org)
  11. 对正则化深度神经网络辍学的温和介绍——MachineLearningMastery.com
  12. 在 PyTorch 中实现辍学:示例—权重&偏差(wandb.ai)
  13. DART booster — xgboost 1.7.1 文档参数—light GBM 3.3.3.99 文档
  14. 零膨胀模型——维基百科
  15. 零膨胀泊松回归模型——时间序列分析、回归和预测(timeseriesreasoning.com)
  16. 很多零还是太多零?:思考计数数据中的零膨胀(rbind.io)
  17. 如何使用 PyTorch 中的 BatchNorm 图层?—知识转移(androidkt.com)
  18. 【2110.01889.pdf(arxiv.org)
  19. Lightgbm vs 神经网络| MLJAR
  20. 4.2。排列特征重要性—sci kit—了解 1.2.0 文档
  21. py torch 的 Captum 模型可解释性
  22. 8.3 特征交互|可解释机器学习(christophm.github.io)
  23. https://arxiv.org/abs/1909.06312
  24. https://arxiv.org/abs/1908.07442

让我们开始:使用 DBSCAN 进行空间聚类

原文:https://towardsdatascience.com/lets-do-spatial-clustering-with-dbscan-c3dbfd9fc4d2

演示如何结合自定义指标和贝叶斯优化来调整基于密度的空间聚类算法。

照片由阿鲁迪巴 SUnsplash

背景和导言

在过去的几年里,我曾经住在一个住宅区一个相当热闹的酒吧的对面。对该地区(和国家)不熟悉!)我认为这将是一个非常有趣的方式来更多地了解当地人和这个地区。

嗯,正如“马路对面”不能准确描述离家有多近一样——我可以坐在酒吧里,仍然可以连接到我家的 Wi-Fi——“热闹”并不能真正公平地反映警察执勤时去酒吧的次数。

现在,客观地说,警方的访问有一些共同的特点:

  • 几乎所有的访问都是在星期五或星期六晚上
  • 大多数访问往往发生在晚上 11:30 以后
  • 大多数参观都以少数顾客坐上警车而告终
  • 活动总是被限制在酒吧的范围内。

如果我们把关心邻居的帽子换成数据科学的眼镜,我们可能会开始以不同的方式思考所有这些活动:我们在一个小的地理区域内发生了许多类似的事件。或者,换句话说,一个集群!由于这个国家可能有不止一个小的社区酒吧,可能会有更多的麻烦分散在各地。

这巧妙地将我们带到了今天的主题——空间聚类。

我们的使命,我们是否应该选择接受它(我们愿意):我们能否从大量公开的事件日志中识别出犯罪活动的集中集群?

首先,我们需要一些数据。

数据

我们将使用来自公共门户网站的英国犯罪数据;当然,明确提醒我们自己,它包含根据开放政府许可 v3.0. 许可的公共部门信息,正如其许可条件所要求的那样。

我下载并汇总了 2019 年 1 月至 2019 年 12 月期间的所有警察部队数据,这是大约 600 万行数据集的一个片段:

作者图片

三个变量引起了我的注意——纬度、经度和犯罪类型。

纬度和经度为我们提供了犯罪事件的地理位置,犯罪类型将允许我们专注于特定的犯罪类型(这很有用,因为当我们开始进行密集计算时,我不希望我的笔记本电脑着火)。

旁白:熟悉英国地理系统的读者会注意到下超级产出区(LSOA)的存在,但没有邮政编码。这意味着我们可以潜在地加入到其他数据集,但是缺少 postcode 可能提供的粒度。然而,有一些非常有趣的方法,我们可以从一组(纬度,经度)对中找到最近的邮政编码。

让我们快速检查一下缺少的值:

作者图片

关于我们所关心的变量,看起来我们处于一个很好的位置,因为只有一小部分纬度和经度有缺失值。我们将从数据中删除这些观察结果,然后继续。

旁白:检查缺失值是否有偏差总是好的,例如,缺失值是否仅限于特定的警察部队?缺失值是否仅限于特定的日期范围?这些检查的结果可能会影响对缺失信息的处理。

让我们来看看犯罪的分布:

作者图片

我们看到:

  • 暴力和性犯罪是 2019 年最常见的犯罪;持有武器是最不常见的。
  • 盗窃有不同的种类:从人身上盗窃,自行车盗窃,其他盗窃。
  • 尽管可能存在盗窃的因素,但抢劫和入室盗窃与盗窃是分开处理的。
  • 我想知道车辆犯罪包括哪些内容。会不会是利用交通工具实施的犯罪——例如,欺骗性地制造事故?或者可能是影响车辆本身的犯罪,例如从车内盗窃,或者可能是对车辆的破坏?我肯定谷歌一下会有所帮助,但我会把这留给未来的布拉德去发现。

我提到在这个练习中我们将只关注一种犯罪类型——我将选择入室盗窃,因为它在数量上处于中间位置。

旁白:当使用受地理和基本人口统计影响很大的数据时,进行某种形式的汇总和/或合理化是很正常的。

例如,在其他条件相同的情况下,我们预计在人口较多的地区,犯罪率会更高。不使用计数来比较地区,使用人均犯罪率可能更合适,因为默认情况下人均犯罪率将根据人口进行调整。

我们不会做任何类似的事情——但稍后会有更多的介绍。

动机和方法

动机

现在我们有了 2019 年的入室盗窃数据,我们可以立即看到任何热点吗?

让我们构建一个英国热图——感谢folium——记住,由于我们使用的是原始数据,城市中心将默认成为热点:

作者图片

让我们以北爱尔兰为例,来看看潜在的城乡差距有多明显:

作者图片

如此明显的分歧!热图使用了某种形式的平滑——我们能看到一些小星团的例子吗?

作者图片

很好地显示了成群的盗窃案,但是这些“群”往往覆盖了重要的区域,而不是狭窄的邻近地区。

一些观察结果:

  1. 相对于英格兰、威尔士和北爱尔兰,我们似乎几乎没有苏格兰的数据。
  2. 存在着明显的城乡差别,这也许被热图可视化中的平滑化夸大了。这没什么好惊讶的,因为城市地区往往有更多的人口,我们关注的是入室盗窃的数量(而不是比率)。
  3. 在由folium建议的簇内,我们也许可以从中导出更集中的簇。

期望成果

现在很清楚,我们有(1)大量的盗窃案分布在英国大部分地区,( 2)往往发生在集中的地区,让我们试着从我们的聚类练习中定义我们想要的。

理想的情况是,我们的聚类分析将为我们提供小而密集的入室盗窃聚类。这些集群将需要尽可能地不同(即分离),这意味着每个集群周围和集群之间有清晰的“空间”。

我们还希望我们的方法能够确定我们所达到的“最佳”集群数量。这里的基本原理很简单:预先指定聚类数不仅会引入偏差,还可能导致对聚类的不正确的观察分配。

我们还应该允许算法将一些观察结果视为噪声。也就是说,我们接受并非每一次盗窃都是一个聚类的一部分,通过“丢弃”一些观察结果,我们可能会得到更清晰的聚类。

最后,我们希望能够解释集群是如何构建的,以及它们是如何被调优的;也就是说,我们在任何优化流程中使用的度量标准都需要是可解释的和容易理解的。

方法

聚类将通过使用sklearn实现基于密度的带噪声应用空间聚类(DBSCAN)来完成。该算法将聚类视为由低密度区域分隔的高密度区域,并且需要定义“密度”的两个参数的规范。

由于这些参数需要一些输入,并且我不是聚类专家,我将使用优化方法来确定“最佳”参数对。

优化过程需要一个输出或指标来优化。我们将找到最小化从“边界”到最终聚类“中心”的平均距离的参数。

概略地说,这是:

作者图片

当然,还有更多细节。

我们将测量(纬度、经度)点云中各点之间的“距离”。典型地,哈弗线距离用于确定坐标对之间的千米距离。这很容易在 DBSCAN 的sklearn实现中使用,但是当我们直接计算距离时,我们需要一个我们自己的实现。

我们想要测量从一个集群的“边界”到其“中心”的距离。我们将首先通过使用凸包(更多内容将在后面介绍)来识别一个集群的边界,并在我们的平均边界到质心计算中使用凸包顶点。

旁白:在距离计算中只使用凸包的顶点在两个方面是有用的。(1)第一次尝试更容易掌握和实现,以及(2)将节省我们的计算资源(我可怜的小笔记本!)通过不必计算所有 边界点的 到质心的距离。

在此过程中,我们将简化聚类质心的计算,使其成为聚类中所有(纬度、经度)坐标对的平均值。请注意,这可能会导致质心实际上不在数据中!

所以在视觉的帮助下:

  1. 我们选择输入参数,并使用 DBSCAN 对数据进行聚类。
  2. 上面显示了其中一个聚类,蓝点代表该聚类中的观察结果(聚类#189)。
  3. 我们使用凸包运算来寻找聚类的凸边界或边界。这由红色虚线表示。
  4. 我们将质心计算为聚类中(纬度,经度)对的平均值。在这种情况下,看起来我们到达了一个不在数据中的质心。
  5. 使用凸包的顶点(即边界点),我们计算平均顶点到质心的距离。我们将这个值传递给优化器,并通过更新的参数选择返回到(1)中。

(算法)细节中的魔鬼

让我们更详细地介绍一下算法的每个组成部分。

哈弗线距离

如上所述,我们可以使用哈弗线距离计算来量化两个坐标对之间的公里距离。

更正式地说:

哈弗辛公式确定了给定经度和纬度的球体上两点之间的大圆距离。在航海中很重要,它是球面三角学中一个更一般公式的特例,即哈弗斯定律,它涉及球面三角形的边和角。⁴

现在,使用地球表面上的点的哈弗辛公式掩盖了我们将要做的一个小的简化:地球不是球形的(不,它也不是平的)。

我们假设,因为我们集中在相对小的地理范围内的小区域,地球的形状不会扭曲结果。

数学上,哈弗辛公式如下:

作者图片

…由于出色的 StackOverflow post⁵,我们实现了快速的矢量化 Python 实现:

基于密度的噪声应用空间聚类

…或者对有噪声的应用程序进行基于密度的空间聚类是一种…

基于密度的聚类非参数算法:给定某个空间中的一组点,它将紧密聚集在一起的点(具有许多邻近点的点)组合在一起,将单独位于低密度区域中的点标记为离群点(其最近的邻近点离 away)⁶.太远)

sklearn开始:

DBSCAN 算法将分类视为由低密度区域分隔的高密度区域。由于这种相当一般的观点,由 DBSCAN 发现的聚类可以是任何形状,这与假设聚类是凸形的 k-means 相反。

另一个关键区别是,DBSCAN 不要求用户指定要使用的集群数量,随着被集群的数据变得越来越大和/或越来越复杂,这变得越来越困难。

DBSCAN 有两个参数:epsilonmin_points,它们一起定义“density"⁷:”

  • epsilon是一种距离度量,用于定位任意点邻域内的点
  • min_points是一个区域被认为是密集的聚集在一起的最小点数(阈值)

DBSCAN 中的关键概念是核心点可达性,这些概念被组合以生成聚类并过滤掉“噪声”点。

同样,大部分来自 Wikipedia⁶:

如果至少min_pointsP 的距离epsilon内(包括点 P 本身),则点 P核心点

如果点 Q 在点 Pepsilon距离内,则点 Q 是从点 P 可直接到达的。点只能从核心点直接到达。

如果存在一条路径P→Q→……→R其中每个中间点都可以从前一个点直接到达(例如 Q 可以从 P 直接到达),那么从点 P 到点 R 就是可达。这意味着 P 和沿路径的所有其他点都是核心点,可能除了 R.

从任何其他点都无法到达的所有点被称为“噪声”或“离群点”。

如果 P 是一个核心点,它与从 P 可达的所有点(核心或非核心)形成一个簇。每个集群包含至少一个核心点。

非核心点可以是群集的一部分,但是形成群集“边缘”,因为它们不能用于到达其他点。

可达性不是对称关系:根据定义,只有核心点才能到达非核心点。反之则不成立,所以一个非核心点可能是可达的,但从那里什么也达不到。因此,需要一个进一步的概念连通性来正式定义 DBSCAN 发现的集群的范围。两个点 PQ 是密度连通的,如果有一个点 O 使得 PQ 都可以从 O 到达。密度连接是对称的

一个集群满足两个属性:

1.集群中的所有点都是相互密度连接的。

2.如果一个点从集群的某个点是密度可达的,那么它就是 well⁶.集群的一部分

嗯,这有点拗口。让我们看看我们能不能画出一些来!

作者图片

左边的图表直观地探索了“核心点”的概念;右边的一个形象化了可达性的概念。

好了,现在我们已经对算法的工作原理有了一个基本的了解,我们可以看看如何实现它吗?是的,让我们。

需要注意的事项:

  • 我们可以直接用哈弗线距离作为度量——metric = "haversine"
  • 哈弗辛计算要求坐标对用弧度而不是度数来表示。我们通过快速调用我最喜欢的包之一— numpy来做到这一点。我们需要确保坐标对的顺序是正确的,因为sklearn希望纬度优先。
  • 由于我们现在谈论的是弧度,我们需要将epsilon输入转换成弧度。
  • 球树算法用于加速算法运行时间。
  • 我们返回聚类标签,因为这是我们唯一感兴趣的。

现在,让我们再次在 Wikipedia⁶的帮助下,通过记住要注意的事情来结束我们对 DBSCAN 的快速浏览(尽管重点是我自己的)。

DBSCAN 不完全是确定性的:根据数据处理的顺序,可以从多个集群到达的边界点可以是任一集群的一部分。对于大多数数据集和域,这种情况不会经常出现,并且对聚类结果影响很小。

DBS can 的质量取决于距离测量...最常用的距离度量是欧几里德距离。特别是对于高维数据,由于所谓的“维数灾难”,这种度量可以变得几乎无用,使得难以找到ε的合适值。然而,这种效应也存在于任何其他基于欧几里德距离的算法中。

DBSCAN 不能很好地对密度差异较大的数据集进行聚类,因为min_points — epsilon组合不能适用于所有的聚类。

如果没有很好地理解数据和比例,选择一个有意义的距离阈值ε可能是困难的

我们将离开 DBSCAN,继续讨论凸包——即我们如何构建我们的聚类边界。

凸包

按照我的想法,二维凸包是包围二维点云的最小凸多边形。在这种情况下,“凸”只是意味着多边形的内角不大于 180 度(如果它大于 180 度,那么外壳将自己“向内转”并变成“凹”)。

Wikipedia⁸有一个很好的类比来设想凸包是如何创建的:如果我们希望在点云 C 周围构建一个凸包,我们可以想象一个围绕 C 拉伸的弹性带,然后让它绷紧;紧带的形状就是凸包。视觉上就是:

作者图片

在这里,我们看到弹性带如何首先围绕点云(紫色)拉伸,然后拉紧以创建凸包(红色)。

让我们把注意力集中在蓝色阴影区域。如果我们要在所有点周围画一个外壳,阴影区域可能会在外壳外面。但是,这意味着外壳不会凸起,因为闭合多边形所需的一些内角会超过 180 度。如果我们沿着这些思路继续思考,我们可以认为凸壳包含凹壳。

值得再次提醒我们自己地球的形状及其在我们工作流程中的作用。凸包的数学在笛卡尔平面上工作得很好,但是可能不能很好地转化为在曲面上操作。我们在这里做另一个简化的假设,由于星团的性质,即它们应该包含彼此非常接近的点,我们关注的区域足够小,足以让我们认为它是平坦的,因此我们的船体数学将成立。

最后,我们使用在scipy ⁹.中可用的凸包实现这个实现允许我们到达凸包的顶点,并且计算凸包的面积和体积。

旁白:凸包可以扩展到三维。在这种情况下,它是包围三维点云的最小三维凸多边形。如果我们扩展维基百科的类比,这就像在三维云点周围膨胀一个橡胶气球,然后让气球拉紧。

度量:平均顶点-质心距离

简单介绍一下我们将用来优化espilonmin_points的指标。

方法相当简单:

  1. 对于每个集群,我们将计算凸包。
  2. 使用这个外壳,我们可以识别集群的顶点;我们将使用我们的哈弗线距离功能来计算从每个顶点到聚类质心的距离。
  3. 然后,我们将取顶点-质心距离的平均值,以得出每个聚类的值。

然后,我们可以对所有集群进行平均,以了解总体集群工作的进展情况。

理想情况下,我们喜欢小而密集的星团。使用这个度量标准,我希望我们可以选择一对最佳的epsilonmin_points(即定义“密度”的值)来实现这两个目标。我很可能错了——有一种方法可以找到答案!

就代码而言,实现起来相当简单:

眼尖的读者会注意到,少于三个点的集群的方法略有不同——这是因为ConvexHull需要三个或更多的点来构建凸包。

最佳化

最后,但同样重要的是,谈谈优化,或者我们将如何选择epsilonmin_points

我们将使用我最喜欢的优化算法——贝叶斯优化⁰.这是一个真正强大的框架,允许我们优化任何黑盒功能,而不局限于优化机器学习模型。

我之前已经在这里介绍过优化是如何工作的,但是我们可以用图解的方式表示如下:

作者图片

一些结果

叽叽喳喳说够了——是时候得到一些结果了,对吗?对!

一旦我将上面所有的功能和想法结合到一个令人信服的工作流程中,我就进行了优化:

作者图片

紫色文本突出显示了最佳迭代。这里我们看到平均顶点-质心距离是 40m,并且是在epsilon = 100mmin_points = 4时产生的(在后台,我将min_samples的浮点值转换为整数,然后输入到 DBSCAN)。

从表面上看,这似乎是一个合理的结果。让我们直观地检查一些集群。

这里我们有一个大的集群,即在伦敦市中心有大量盗窃案的集群(泡泡中的数字代表泡泡附近的盗窃案数量):

作者图片

…以及一个“更小”的集群:

作者图片

很明显,伦敦中心的集群并不小(尽管它看起来确实很密集)。这可能是由于优化的构造,即因为我们对多个聚类进行了平均,一些异常值可能会被大量较小的值所拖累。然而,这并不是该集群的理想结果。

旁白:如果这是一些人口较多的集群的共同问题,那么为了将人口较多的集群分解成较小的集群,是否值得做进一步的集群练习?

我们在英国南部的较小星团中没有看到这个问题,因为所有五次观测都发生在同一条街上(那些可怜的居民!)。

在我们进入总结和最后的讨论之前,让我们来探索一些集群的用例。

用例

好吧布拉德,但那又怎样?我们如何使用由此产生的集群?请允许我解释。

我第一次接触 DBSCAN 和空间聚类的想法是在一篇谈论实时导航的文章中——想想谷歌地图为用户提供方向。总的想法是,在这种情况下,集群代表高流量的区域,会减慢旅程的进度;理想情况下,算法将识别这一点,并重新计算到避开群集的旅程步骤。

使用我上面描述的东西当然是一种特征工程的形式。根据上下文,我们可以用几种不同的方式使用它:

  • 在汽车保险中,我们可以将车辆犯罪或事故信息的实例聚集在一起,以更好地了解位置如何影响索赔数量或索赔成本。同样,聚集的盗窃案例可以帮助理解家庭保险投资组合的表现。
  • 识别和计算与设施群(如健身房、购物中心、学校、火车站)的接近程度可能是房价的一个非常重要的指标。

如果我们的数据中有缺失信息,那么聚类可以构成插补过程的一个组成部分。想象一下,我们试图理解一所房子的特征;假设同一时间建造的同一(小)街区(或街道)的房屋具有相似的特征是很合理的。因此,如果我们能够将房屋聚集在一起,我们就可以根据该群中房屋的特征进行合理的估算。

当然,我们也可以使用聚类作为一种降维形式。如果我们得到 mathsy,我们基本上已经在这里完成了一个 RR 映射。如果我们能找到一种有效的距离度量,我们就能很容易地进行更高维度的映射。

总结和讨论

嗯,这是一个相当漫长的一个。让我们总结一下。

首先,我们看了一下 2019 年犯罪数据的分布。我们对入室盗窃数据的可视化显示,确实存在密集活动的小区域,并为基于密度的空间聚类方法提供了一些动机。

然后我们把一个算法的配方和一些解释放在一起。该功能允许我们:

  • 计算两个(纬度、经度)坐标对之间的距离。
  • 使用 DBSCAN 算法执行聚类分析。
  • 计算 DBSCAN 生成的聚类的平均聚类顶点-质心距离。
  • 使用贝叶斯优化来选择最小化平均顶点质心距离的 DBSCAN 输入。

我们可视化了一些结果,并讨论了潜在的用例。

在我们结束工作之前,让我们兜一圈,讨论一些事情。

总体结果

我认为公平地说,虽然我们的聚类方法有效,但它产生了一些稍微出乎意料的结果。现在,我们也许应该把这种方法看得不像是一个“如何做”的指南,而更像是一个“我已经做了什么”的例子。

当然,我说的是在物理距离上相当大的人口最多的集群,尽管优化结果表明了一些不同的东西。

我简要地提到了这可能是由于我们构建指标的方式造成的——使用平均值可能会允许较少的大值被许多较小的值掩盖。也许使用不同的度量标准(例如最大的平均顶点-质心距离)会产生不同的结果。也许我们需要聚类的第二阶段,集中于将较大的集群分解成较小的、更集中的集群。

数据和数据相关问题

首先要特别提到的是——如果你是免费、有趣、高质量数据集的粉丝,我强烈建议你去看看英国政府发布了什么。虽然我们已经查看了犯罪数据,但还有各种主题的信息,包括车祸、道路交通信息和新冠肺炎。

对于这样的数据,理解微观和宏观事件如何影响数据是很重要的。我特意选择使用 2019 年的数据,因为这是 COVID 前信息的最后一整年;我预计,在 2020 年 COVID 锁定期间,入室盗窃率将大幅下降,这使得每个人都呆在家里。

我们在前面提到了这一点,但认识到其他人口统计学(如人口和人口分布)如何影响数据也很重要。例如,在所有其他条件相同的情况下,我们预计人口密集地区的入室盗窃数量会高于人口较少的地区,这仅仅是因为人口规模(即在人口密集地区,会有更多的人入室盗窃)。

在这些情况下,明智的做法是根据人口规模进行调整,并着眼于入室盗窃率而不是入室盗窃数量。然而,这可能对分析有影响,因为可能很难获得在足够精细的水平上使计数正常化所需的数据。例如,我们不太可能找到我们得到的每个集群的人口数据。因此,我们很可能需要在对数据进行标准化之前,进行某种形式的汇总,以达到适当的水平。

从英国网格的角度考虑,我们可能需要从坐标对到邮政编码或更低的超级输出区域(LSOA),以便获得相应的人口数据。

我们还需要谈谈坐标对的准确性。除非警察带着某种形式的 GPS 到每个犯罪现场,否则坐标对不太可能代表事件发生的确切位置。

输入到数据系统中的坐标对与实际犯罪现场的接近程度可能因犯罪类型和可能需要的任何警察干预而有所不同。例如,我们可以合理地想象,报案的抢劫案的地点将是沿着“Y 街和 X 大道的拐角处”,而报案的盗窃案的准确程度是非常具体的。

旁白:另一个例子——假设我们正在对交通事故进行聚类。繁忙的高速公路上的事故地点可以从最近的“安全地点”捕捉到。在某些情况下,这可能是实际场景中的路肩;在其他地方,它可能是最近的服务站或休息点。

这大概是更微妙的数据细微差别;虽然我们不太可能量化差异的程度,但这是值得认识和交流的。

优化和替代优化指标

一般来说,贝叶斯优化是我最喜欢的优化复杂函数的方法之一——包括机器学习模型。

我之前提到过,重新审视优化指标可以交付更好的——或者更符合预期的——结果。

虽然我认为作为第一步,平均顶点-质心距离还不错,但我们肯定可以改进它。

作为第一步,我们可以尝试使用最大值。正如我在上面所描述的,使用平均值可以让少量的大值被大量的小值所掩盖(因此平均值被值的分布所扭曲)。转而计算像平均最大顶点-质心距离这样的东西可能会产生更小的集群。

我们当然可以改变度量的概念。由于我们对簇的大小和密度感兴趣,切换到最小化簇的面积可能是明智的。如果我们要计算群集面积,群集密度是可达到的——双关语——如果,比方说,我们将密度定义为每单位面积的群集点数。

虽然未经测试,我认为这些想法可能很有趣,值得探索。

我还应该提到,选择epsilonmin_points有更正式的方法,其中一些在下面链接的文章中有所涉及。

地质公园

如果不提到⁴.的地质公园,那将是我的失职如果你不熟悉 geo pandas——我绝不是这方面的专家——它是一个 Python 包,旨在简化地理空间数据的处理。

快速浏览一下包文档,看起来我们配方中的关键功能——凸包、距离、面积——在 GeoPandas 中可用,并且可能会使工作流更容易。

我必须承认,我特意选择不使用 GeoPandas,因为我仍然熟悉坐标参考系统、投影坐标系统以及它们之间的关系(或者更重要的是,如何从一个到另一个)。

也许如果我将来回到这个话题上——或者做一些类似的事情——我可能会挑战自己,使用 GeoPandas。

表演

现在来简单谈谈性能。

我用的是 2017 年买的相当中级的笔记本电脑……它绝不是一个发电站,这是肯定的。

在看到如此庞大的数据量之后——全部犯罪数据集中大约有 600 万行,入室盗窃数据集中大约有 37 万行——我想老忠实可能会遇到一点困难,特别是在聚类计算方面。

但事实并非如此(令人惊讶!).即使进行了相当耗费资源的优化,我的硬件似乎也能很好地处理事情——可能更多的是算法实现的证明,而不是我自己做的任何事情。

最后…

如果你已经做到这一步,做得很好(感谢你的阅读!).

你现在可能已经注意到了,我不是为了改变而敲打神经网络;通过这篇文章,我打破了最近记录我从学习到建立神经网络的旅程的趋势。如果这真的引起了你的兴趣,不要担心,因为我正忙于下一部分的工作(在此期间,你必须满足于我的过期目录⁵)。

如果你喜欢阅读我的长篇大论,我写过关于机器学习其他主题的文章,比如⁷特征工程,使用贝叶斯优化⁶优化 LightGBM 模型,以及⁸.缺失值插补

一如既往,我希望你喜欢读这篇文章,就像我喜欢写它一样。下次见!

参考文献

  1. 首页| data.police.uk
  2. 开放政府许可证(nationalarchives.gov.uk)
  3. 2.3。集群-sci kit-learn 1 . 1 . 1 文档
  4. 哈弗辛公式—维基百科
  5. https://stackoverflow.com/a/29546836/11637704
  6. DBSCAN —维基百科
  7. 机器学习中的 DBSCAN 聚类算法— KDnuggets
  8. 凸包—维基百科
  9. SciPy . spatial . convex hull—SciPy v 1 . 9 . 0 手册
  10. fmfn/BayesianOptimization:高斯过程全局优化的 Python 实现。(github.com)
  11. 道路安全数据—data.gov.uk
  12. 地图道路交通统计—道路交通统计(dft.gov.uk)
  13. 英国概要|英国(data.gov.uk)的冠状病毒(新冠肺炎)
  14. geo pandas 0 . 11 . 0—geo pandas 0 . 11 . 0+0 . g 1977 b 50 . dirty 文档
  15. 让我们来学习:神经网络#1。我学习的一步一步的编年史… |作者布拉德利·斯蒂芬·肖| Medium
  16. 看数字:LightGBM 模型的贝叶斯优化| Bradley Stephen Shaw |走向数据科学
  17. 让我们做:特征工程。布拉德利·斯蒂芬·肖|走向数据科学
  18. 填补空白:插补三种方法| Bradley Stephen Shaw |走向数据科学

让我们结束这场辩论——精算师 vs 数据科学家

原文:https://towardsdatascience.com/lets-end-the-debate-actuary-vs-data-scientist-83abe51a4845

哪个职业更好,为什么我选择两个都做

英国石油公司米勒在 Unsplash 上拍摄的照片

尽管被视为第一代数据科学家,但大多数人可能不知道精算师是什么,也不知道他们到底是做什么的。见鬼,如果不是我爸爸告诉我,我根本不知道这份工作的存在。

是的,这在很大程度上是一个传统上与在保险公司或养老基金工作相关的利基领域,但现在情况已经发生了变化。由于他们在统计方面的严格背景和分析数据的能力,许多精算师已经慢慢地转变为数据科学家,并在这方面取得了巨大的成功,因为这两个领域之间的技能组合有很大的重叠。

作为一个在大学学习精算专业的人,现在是一名数据科学家,我觉得有必要分享一下自己的经历。

这篇博文旨在探讨精算师和数据科学家这两种职业,具体来说:

  • 他们每天都在做什么
  • 他们工作的地方
  • 如何取得资格或成为一个
  • 薪资和职业发展
  • 工作生活平衡

在这篇博文的第二部分,我将更多地关注我自己作为一名数据科学家追求精算资格的个人经历,为什么我决定把我的时间分在这两者之间,也许为什么你应该考虑做同样的事情。

精算师或数据科学家是做什么的?

在解释精算师的角色时,你会听到人们使用的一个大词是风险。

想知道为什么不同的人支付不同的汽车保险费吗?为什么一个 30 多岁的已婚男人,住在一个安静的郊区,有两个孩子,比一个 20 多岁刚获得驾驶资格,住在城市里,开跑车的人支付的保费要低?你猜对了,答案是风险!

但是,我们用什么变量来确定这些保费,以及不同风险状况的每个人到底应该支付多少?这就是精算师的用武之地。

精算师依靠统计学(特别是 T2 大数定律)和金融数学来确定特定事件的风险水平,并随后推荐适当的措施来正确管理这种风险。

另一方面,数据科学家将大部分时间用于操纵和可视化数据,更重要的是,从数据中获得可操作的见解。然而,与精算师不同的是,数据科学家倾向于在更广泛的行业工作,而不仅仅是在金融服务领域。

正如你可能看到的,精算师和数据科学家被雇佣来解决的问题的性质可能是非常不同的。

精算师通常更多地处理金融风险,并帮助组织防止偿付能力问题,同时保持业务盈利。实际上,这可以包括保险产品的定价、资本储备、负债评估等等。

另一方面,数据科学家解决更广泛的问题。数据科学家的主要目标是发现数据中的模式,并使用这些模式为资源分配和消费者产品个性化等决策提供信息。模式可以是许多事物的形式,例如客户交易、天气、道路交通等等。

他们在哪里工作?

精算师或数据科学家可以选择的两条主要道路是咨询或行业。

咨询意味着成为不同行业客户值得信赖的顾问,而行业意味着在你的雇主所在的行业中工作。

在澳洲,精算顾问可以在一家小型精算咨询公司工作,如有限公司或 T2 泰勒·弗里公司,或者加入四大会计师事务所之一的精算服务团队。另一方面,从事咨询工作的数据科学家可以在任何需要他们技能的咨询公司工作。一些例子包括 Quantium (我目前工作的地方) BCG GAMMA 等等。

另一方面,在该行业工作的精算师通常会发现自己在更传统的领域,如人寿保险、普通保险和退休金。然而,行业数据科学家在工作地点上要灵活得多。一些例子包括但不限于超市、银行、政府,甚至是科技公司和像 Canva 这样的初创企业。

如何成为一名精算师或数据科学家?

现在我们已经了解了精算师和数据科学家的角色,让我们看看如何真正成为一名精算师和数据科学家。

考试,考试和更多的考试——众所周知,成为精算师的道路是漫长的,艰难的,充满了非常具有挑战性的考试。一个人通常需要大约 5-7 年才能成为一名完全合格的精算师。

所以,如果你对大学毕业后参加考试、一边全职工作一边学习的想法感到厌恶,你可能需要重新考虑这是否是你的正确职业选择。

对于大多数人来说,旅程通常始于在一所认证大学攻读精算学位,以便获得尽可能多的科目豁免。这不仅有助于在开始你的第一份精算工作之前打下正确的基础,因此也提高了就业能力,更重要的是,最大限度地减少了你开始工作后需要参加的考试数量。

要了解更多信息,我强烈建议你去看看你所在国家的精算专业管理机构。比如澳洲的精算师学院,英国的精算师学院,美国的精算师协会

另一方面,成为一名数据科学家的旅程有点不那么结构化和清晰。这也很大程度上取决于你所在的国家和你想为之工作的公司。

根据我自己的观察,如果你在美国申请数据科学职位,你通常需要硕士学位,有时甚至需要博士学位,而在澳大利亚,只要是工程、数学和统计或计算机科学等技术背景,你可能只需要学士学位就可以了。

薪资和职业发展

在与我的一些同行交谈后,我得出结论,这两种职业的工资相当,至少在研究生水平上是如此。

然而,精算师的薪酬与一个人通过的考试次数更相关,尤其是在你职业生涯的头几年。这表明,如果你想成为一名精算师,考试是多么重要。

然而,对于数据科学家来说,工资不取决于通过的考试数量,而是取决于你工作的行业以及经验水平。

尽管对于这两种职业,有一点我可以肯定地说,一旦你升到经理或高管级别,你的薪水就会最高,在这一级别,你所做的技术工作会变得少得多,而更多的是领导团队和从头到尾领导一个项目的能力。

所以,如果你的目标是爬上公司的阶梯,做好准备,主动锻炼领导力和客户管理技能,因为一旦你开始晋升到高层,这些技能将非常重要。

工作生活平衡

我不太愿意给出一个具体的数字,因为我认为这可能会随着行业和一年中的不同时间而发生很大的变化。

然而,与业内人士相比,咨询师的工作时间更长,也更难预测。这是因为顾问按照他或她正在进行的项目的时间表生活,有时甚至可能在多个项目之间周旋。

在行业内工作更有预见性。除了报告季节或公司设定的内部截止日期,该行业的工作时间往往更加灵活。

工作与生活的平衡之所以是一个需要考虑的重要因素,尤其是对于有抱负的精算师来说,是因为你很可能需要在工作的时候做额外的研究。因此,如果你的工作已经占用了你大量的时间和精力,这不仅会减少你准备考试的时间,还会减少工作之外的时间,如个人爱好、放松、与家人和朋友在一起的时间等等。

为什么我决定两样都做

下面是这篇博文中自我陶醉的部分,我主要谈的是我自己,特别是我决定继续追求精算资格,尽管目前是一名数据科学家。

我将在这里概述的一些原因比其他原因更私人,但我希望通过分享它们,让你对我们迄今为止讨论的内容有一个更深入的了解,并帮助你在两条职业道路之间做出决定。

原因 1:自我和沉没成本谬误

好吧,我承认这可能不是最理性的决定,但已经花了 3 年时间在大学里深入研究保险行业,学习各种复杂的金融数学公式来评估年金以及如何衡量生死概率,我很大一部分人不愿意白白放弃这一切。

我也有一部分认为这是一个在智力上推动自己的机会,让我的技能保持新鲜,但更重要的是,体验克服困难和没有多少人愿意做的事情的满足感。

原因 2:灵活性和未来机遇

尽管我现在是一名数据科学家,但我并不完全不相信有一天我会重新成为一名精算师。

这是因为我个人非常喜欢精算教育提供的知识、概念和思考问题的方式。虽然我成为一名数据科学家是因为我想从事更广泛的项目,但我当然不介意回到我的精算根源,处理与金融和保险更相关的问题。

此外,精算资格也是全球公认的,如果我想,这可以打开潜在的海外工作的大门。

原因 3:提升数据科学家的技能

在你成为精算师之前,你最初要学的很多东西,尤其是前几场考试,都直接适用于数据科学家。

除了一些经济和会计概念或如何对金融衍生品进行估值之外,能够理解概率和统计,如何用 R 编写代码或使用 Excel,甚至机器学习都是任何数据科学家都应该掌握的知识。

因此,我坚持继续接受精算教育的原因之一是保留并进一步提高我在这些领域的知识,因为我坚信,在我作为一名数据科学家的日常职责中,这些知识会以某种方式带来回报。

我希望这篇博文能够给你们一些启发,甚至帮助你们中的一些人发现一条全新的职业道路,也许你以前从来不知道。更重要的是,我希望通过分享我所知道的,并强调精算师和数据科学家之间的区别,你现在对这两种职业有了更多的了解,并更有能力决定你想从事哪一种职业。

就我个人而言,我认为这两个职业都非常有价值,前景广阔。这最终取决于你喜欢什么,你喜欢做什么。

如果你从这篇文章中发现了任何价值,并且还不是一个媒体会员,如果你使用下面的链接注册会员,这对我和这个平台上的其他作者来说意义重大。它鼓励我们继续推出像这样的高质量和信息丰富的内容——提前感谢您!

https://chongjason.medium.com/membership

不知道接下来要读什么?这里有一些建议。

让我们来看看优化的本质

原文:https://towardsdatascience.com/lets-get-to-the-bottom-of-optimization-509f06c8314c

Himmelblau 函数的最速下降(自生成图像)

构建一个可视化工具来试验不同的优化算法

在这篇文章中,我将展示我创建的一个可视化工具,以了解优化算法如何在 2D 函数上工作。我将简要介绍算法中的一些数学知识,并深入研究 visualizer 工具的实现细节。

形象化

如果你想在不同的非凸函数 上运行不同的优化算法,点击这里 。我用 streamlit 开发了这个,帮助我理解不同的算法如何在函数空间中移动。

背景

传统上有两种寻找极小值的方法——线搜索和信赖域优化方法。

这两种方法在选择走向最小值的迭代过程中的下一步的方向和大小的顺序上有所不同。线搜索方法确定方向,然后找到最佳步长,而信赖域方法确定步长,然后选择方向。

一个直观的阶跃方向是函数的负梯度。这是因为梯度指向最快速增加的方向。这是在许多 ML 优化框架中普遍存在的最速下降法的基础。它很有用,因为我们只需要计算一阶导数。

最陡下降方向

一个不太直观但很重要的步进方向是牛顿方向。这使用了海森矩阵 H 的逆矩阵,该矩阵描述了 f 的二阶偏导数。该矩阵提供了关于在 xf 的局部曲率的信息。

牛顿方向

上述方程的推导以及收敛性的证明超出了本文的范围(参见第 20-25 页的Nocedal 和 Wright 的数值优化),但是我想提供一个手动波形解释,说明为什么 hessian 在寻找搜索方向时是有用的。

最陡下降方向是函数 f 的切线。因此,通过沿着该线直到从当前点有足够的减少,来确定最佳步长。

牛顿方向不是通过考虑直线来计算的,而是考虑更好地逼近函数 f 的二次曲线。因此,当查看搜索方向时,它试图找到二次型最小值的方向,这允许在给定的某些条件下更快地收敛。这通常消除了计算步长的需要,实际上步长被设置为 1。

我认为阅读建议的页面来理解证明是有用的,但是这需要一些线性代数的背景知识。我建议参考吉尔伯特·斯特朗的《线性代数和从数据中学习》。但是如果你对代码更感兴趣,我们开始吧!

实施细节

本节将深入解释最速下降法和牛顿步算法的代码,并有望为上述数学提供一些具体内容。

最陡下降

最速下降法使用近似导数函数计算步长方向 p

导数函数使用导数的定义以数字方式计算梯度,该定义为:

导数的定义

关键的一点是,我们使用一个小的 h ,而不是取 h 趋于 0 时的极限,来得到一个近似导数。使用这些近似函数允许对任何可微函数求导,而不必事先知道它。这就是为什么我们把 f 作为函数直接传递给它。

下一步是计算步长,我们使用回溯搜索来完成。

该方法使用第 2 行和第 3 行所示的不等式,称为 Armijo 条件,以找到保证充分降低的最佳步长。

充分下降条件(自生成图像— Sacha Gunaratne 2022)

上图显示了 l( α ) 和φ(α)是不等式的两块。LHS 为φ(α),RHS 为 l( α ) 即直线。直觉是,当满足以下条件时,φ会充分减小:

很容易看出,这个条件在可接受的范围内是成立的,因此在这些范围内的任何α值都满足这个条件。该算法以α的设定值开始,然后缓慢降低,直到满足条件。在该书中,建议将牛顿法的α设置为 1,并对其他方法使用其初始值进行实验。

一旦找到步长和方向,就可以用它们来更新当前点,使其更接近最小值。重复这一过程,直到满足退出条件,该条件检查对当前点的两次更新之间的欧几里德距离。

牛顿法

这两种方法有很多相似之处——主要区别在第 8、9 行,在那里计算 hessian 并用于计算步长方向 p.

hessian 是描述函数局部曲率的函数的二阶导数。它告诉我们,如果一个函数 f (x) 有一个鞍点,局部最小值或最大值在x。hessian 只有在它是正定的时候才有用,因为这提供了一个下降方向。这加上需要计算 hessian 及其逆是它不是最常用方法的部分原因。

近似 hessian 在上面使用与近似导数相同的方法计算,因为 hessian 正好是二阶导数。

使用 hessian 和梯度的逆来计算步长方向,然后遵循与最速下降法相同的步骤。

在可视化工具中,你会注意到牛顿法比最速下降法收敛得更快。然而,在某些情况下,牛顿法会失败,你会注意到它找不到任何最小值( Himmelblau 函数当 x,y = 0,0 时)。

牛顿的方法失败了

这是因为在这些情况下,hessian 不是正定的,因此没有下降方向。有几种方法可以解决这个问题,这里应用了其中的两种方法:

这些方法归结为修改 hessian 矩阵,使它是正定的。

方法 1 增加了单位矩阵的倍,使得 hessian 矩阵的对角元素为某个因子β的正。方法 2 将矩阵中的所有负值翻转为正值。这两种方法都确保特征值大于零,这是正定性的一个条件。一旦应用了这些方法,牛顿法将在 Himmelblau 函数中找到最小值。

牛顿法在对黑森(Sacha Gunaratne 2022)进行修改后仍然有效

外卖食品

在实现这些算法的过程中有许多错综复杂和细微差别,只有当你深陷其中时才会出现。我发现构建这个可视化工具的练习非常有助于加强我对许多机器学习和优化框架中使用的各种算法的理解。

还有几件事我想实现——BFGS 算法,这是一种准牛顿法,不需要完全计算 hessian。考虑完全 Wolfe 条件的线搜索算法。我还希望允许用户输入他们自己的函数,这样他们就可以以一种更少约束的方式使用这个工具。

我希望这篇文章对优化方法及其背后的直觉提供了一些见解,并且希望你有机会使用可视化工具

大部分这些我都是通过阅读 Nocedal 和 Wright 的数值优化学来的,我发现这本书写得很好,以直观的方式处理复杂的主题。

如果你对改进可视化工具有任何建议,或者对本文有任何疑问,请随时通过 twitter 联系我。

我们来谈谈生物医学网络中的图机器学习

原文:https://towardsdatascience.com/lets-talk-about-graph-machine-learning-in-biomedical-networks-8a84139e970b

机器学习技术在生物医学图上的应用概述

Unsplash 上由 Sangharsh Lohakare 拍摄的照片

G raph 机器学习已经很流行了,尤其是在社交网络领域,但在生物医学领域,或者更具体地说,在生物信息学领域,它相对不太为人所知。我已经在这个有趣的领域获得了实践经验(几年前),在这篇文章中,我将解释机器学习算法在其中的应用。

首先,生物信息学是生物学、计算机科学、数学等学科的交叉领域,其目的是分析和解释生物数据。换句话说,从生物数据中提取有价值的信息。

表示人和计算机都能理解的真实生活数据并不容易。一种很好的数据类型是图形。图形可以模拟和存储复杂的现实生活中的互动。这也适用于某些类型的生物数据,如用图表表示的蛋白质-蛋白质网络。

以下是可以用图表形式研究的生物学方面/主题的列表:

  1. 药-病关联(DDA) —不同药物对疾病的作用不同。例如,药物 A 可以治疗疾病 X,而可能有另一种药物 B 导致疾病 y。因此,药物既可以是治疗性的,也可以是有害的,使用 DDA 我们的目标是找出哪种药物与哪种行为有关。

裁军事务部插图(来源)

2.药物-药物相互作用(DDI) —当两种或更多种药物相互反应时发生。当有人同时服用多种药物时,我们必须了解这些药物联合使用时的反应性质。这是为了避免任何可能发生的副作用或意外反应。

3.蛋白质-蛋白质相互作用(PPI)—蛋白质有助于细胞内发生的大多数生物过程,如基因表达、细胞生长等。这些蛋白质很少单独发挥作用,它们倾向于与其他蛋白质分子形成关联(物理接触),以在细胞内进行不同的分子过程。研究这种蛋白质相互作用可以提高我们在分子水平上对人体的理解。

4.蛋白质功能预测 —正如我之前提到的,蛋白质在活细胞内执行多种功能。蛋白质功能预测基本上是给蛋白质指定一个生物学角色,即识别哪种类型的生化反应与特定的蛋白质相关。

5.生物医学概念的语义分类 —在数字时代,我们可以在网上获得大量的生物医学信息。面对如此海量的数据,有必要对其进行索引和管理。公司存在与隐私相关的担忧,即在分发数据时可能不会披露与某个主题相关的全部信息。利用医学知识库(像 UMLS ),我们可以借助各种机器学习技术(NLP 等)进行语义分类。

所有上述主题都可以转换成一个图,并在其上执行下游任务,如链路预测和节点分类。

DDA、DDI 和 PPIs 在制定为关联预测任务时,目标是识别给定实体(药物、疾病或蛋白质)之间的任何潜在相互作用或关联。存在将问题公式化为不同的图相关任务的可能性,例如图聚类和节点分类,这通常取决于用例。

蛋白质功能预测和语义分类可以建模为节点分类任务。

查看我上一篇关于图及其下游任务的文章,了解更多关于这些主题的内容。下面是链接

图形机器学习

我们不能直接在图上使用标准的机器学习算法,因为图中存储的信息是高维的和非欧几里得的。因此,我们将图形实体映射到一个低维向量空间(也称为嵌入空间),然后对其应用我们最喜欢的机器学习算法。

图表 ML 管道示例(图片由作者提供)

从上图可以看出,graph ML 管道的输入是生物医学图。我们应用图形嵌入方法将图形映射到低维空间,并计算嵌入,这些嵌入稍后将用于解决定义的用例(可以是链路预测、节点分类或不同类型的任务,如图形聚类)。

存在不同类型的图嵌入方法,我们可以将其大致分为 3 种类型:

  1. 基于矩阵分解的方法—这里,我们将图(表示为邻接矩阵)分解为低维矩阵,同时保留原始矩阵的拓扑信息。这种方法有许多变体,如图分解、GraRep、HOPE 等。
  2. 基于随机行走的方法——该方法从自然语言处理中流行的技术 word2vec 模型中获得了很多灵感。Word2vec 使用句子来学习单词的嵌入。类似的方法用于生成图中的节点嵌入。通常,选择一个节点(随机地或使用一个条件),我们随机地移动(或执行“行走”)到另一个节点,移动的步数是确定的。通过这种方式,我们获得了定义长度(与步数相同)的节点序列,然后用于学习节点嵌入。一些例子包括 DeepWalk 和 node2vec。
  3. 基于神经网络的方法—近年来,基于神经网络的方法得到了更多的研究,因为它们比其他方法显示出更有前景的结果。许多类型的神经网络如多层感知器(MLP)、自动编码器、生成对抗网络(GAN)和图卷积网络(GCN)被用于计算图嵌入。LINE 和 SDNE 就是这种方法的一些例子。

所有上述方法都用于计算静态图嵌入。静态的意思是它们是在固定的时间周期(不改变或发展)为图形计算的。

动态生物医学图

考虑这一点(以 PPIs 为例)——在 T=0 时,人类细胞内的一组蛋白质参与了生化反应“A”,而在 T=1 时,一组不同的蛋白质参与了不同的反应,比如“B”。和久而久之,不同的蛋白质组合参与不同数量的反应。这是细胞中实际发生的情况。因此,有必要研究具有时间信息的生物医学图像。

我们之前考虑的是复杂现实生活过程中最简单的情况。通过消除时间因素,我们能够将问题简化许多倍。

如果我们把时间加入到图表中,它会变成一个动态的图表。这些比静态图更复杂(但也更有趣)。您可以将动态图视为从 time=0 到 time=T 按此顺序收集的静态图列表(一系列图的快照)。我们必须使用动态图嵌入方法将动态图映射到低维空间,然后执行任务。这里使用的 ML 管道与上一个非常相似。

一种简单的动态图嵌入方法是独立考虑每个时间点 T 并计算静态嵌入。例如,如果我们有一个具有 12 个时间点的动态图,我们可以为所有 12 个时间点嵌入一个动态图(为每个时间点独立计算)。这不是一个好主意,因为我们没有捕捉到时间对图形演变的影响。

存在先进的技术,其中当计算当前时间点的嵌入时,考虑先前时间点的影响。因此,我们捕捉了图随时间的演变,并获得了高质量的嵌入。

我写了一篇详细解释动态图的文章,可以在这个链接上找到。

最后的想法

图机器学习尤其是图神经网络的研究成果近年来非常多,我们看到这些方法取得了令人印象深刻的结果。在生物医学图上制定不同的任务使我们能够解决传统实验室实验的一些瓶颈。在实验室实验中,不可能确定每一种可能的蛋白质相互作用或 DDI、DDA,因为我们在使用物理工具进行测量时存在技术限制。

这就是 ML 的用武之地。我们使用具有已经识别的相互作用(通过实验室实验)的数据来训练模型,并尝试预测潜在的相互作用或关联。这是令人兴奋的,因为它为更快的模拟和快速的药物发现过程打开了大门。

新冠肺炎·疫情向我们展示了生物医学研究的价值,而图形 ML 是辅助这项研究的工具。如果你是一个刚刚进入 graph ML 的人,检查生物医学领域的一些有趣和具有挑战性的问题可能会很有趣。

感谢阅读,干杯!

**Want to Connect?**Reach me at [LinkedIn](https://www.linkedin.com/in/rohithteja/), [Twitter](https://twitter.com/rohithtejam), [GitHub](https://github.com/rohithteja) or just [Buy Me A Coffee](https://www.buymeacoffee.com/rohithteja)!

让我们来谈谈你放在口袋里的数据库

原文:https://towardsdatascience.com/lets-talk-about-the-database-you-wear-in-your-pocket-ab3cacb4d25f

了解亿万人使用的未知数据库的更多信息

图片 Saskia van Baaren

当我们这个行业的人想到数据库时,很多人会想到 Oracle、MySQL、Postgresql,也许?但是使用量超过所有其他数据库总和的数据库呢?再来说说 SQLite!

什么是 SQLite?

SQLite 是一个实现 SQL 数据库引擎的库。它有几个吸引人的特性:

  • 用 C 写的:速度极快
  • 它很小:SQLite 运行在小设备上(比如电话和微波炉)
  • 可靠:这个库每天被数十亿人使用,因为几乎每个手机应用程序都以这样或那样的方式使用 SQLite
  • 它功能齐全:尽管名为 SQLite,但它有很多令人印象深刻的功能。

这是一个图书馆

与其他数据库不同,SQLite 是无服务器的。毕竟是!SQLite 可以与您的代码一起提供,并将其数据存储在文件系统的文件中。

SQLite 通常包含在其他语言中。比如 Python 就提供了开箱即用。因此,您可以使用 SQLite 来存储和分析数据,而无需设置数据库,我认为人们应该更经常地使用它!

您可以使用 SQLite 来存储和分析数据,而无需设置数据库,我认为人们应该更经常地使用它!

SQLite 怎么发音

没有什么比发音错误更令人困惑,有时甚至尴尬的了。SQLite 有两种发音方式,都被认为 OK。

SQL 是结构化查询语言的缩写,通常读作“ess queue ell”或“sequel”因此,SQLite 可以读作:

  1. ess 队列灯
  2. 续集灯

挑你喜欢的风格;我更喜欢最后一个。

浏览现有的 SQLite 数据库

我们可以使用 SQLite shell 来浏览 SQLite 数据库。要打开 SQLite shell,我们必须在终端或命令提示符下输入sqlite3命令。这在所有操作系统(Windows、MacOS、Linux)上都是一样的:

C:\> sqlite3
SQLite version 3.35.5 2021-04-19 18:32:05 
Enter ".help" **for** usage hints.
Connected to a transient **in**-memory database.
Use ".open FILENAME" to reopen on a persistent database. 
sqlite>

当不带任何参数打开 SQLite 时,它将创建一个内存数据库。正如 SQLite 指出的,我们可以通过使用.open FILENAME命令打开一个现有的数据库。但是,如果您已经有了一个基于文件的 SQLite 数据库,那么直接打开它会更容易:

C:\> sqlite3 customers.db
SQLite version 3.35.5 2021-04-19 18:32:05
Enter ".help" **for** usage hints.
sqlite>

从头开始创建 SQLite 数据库

如果您没有数据库,让我们先从头创建一个,这样我们就有东西可以使用了。我们需要用sqlite3命令再次启动 SQLite 来获得 SQLite shell。我们可以通过将数据库作为第一个参数直接给它命名:

$ sqlite3 customers.db
SQLite version 3.35.5 2021-04-19 18:32:05
Enter ".help" **for** usage hints
sqlite>

注意,我喜欢使用.db文件扩展名。您可以选择任何您想要的文件名,但是我建议使用.db扩展名来清楚地将它标记为数据库文件。

现在创建一个表,并通过输入以下行插入一些数据:

sqlite> **create** **table** customers(**name** text, age int);
sqlite> **insert** **into** customers **values**('Erik', 40);
sqlite> **insert** **into** customers **values**('Mary', 53);

我们可以通过一个简单的 SELECT 语句看到结果:

sqlite> **select** * **from** customers;
Erik|40
Mary|53Code language: SQL (Structured Query Language) (sql)

要关闭外壳,请按control+D,它会将文件尾字符发送到您的终端,或者键入。退出命令。

如果您检查文件系统,您会看到 SQLite 已经为我们创建了customers.db文件。

SQLite 浏览器:打开一个数据库

现在我们有一个现有的数据库可以使用。让我们首先在文件系统的一个文件中打开这个 SQLite 数据库。我们将使用之前的数据库customers.db。我们可以像这样用sqlite3命令打开它:

C:\> sqlite3 customers.db
SQLite version 3.35.5 2021-04-19 18:32:05
Enter ".help" for usage hints.
sqlite>Code language: plaintext (plaintext)

现在我们可以浏览我们的 SQLite 数据库了!

有用的 SQLite 命令

SQLite 没有大张旗鼓地打开数据库,但是它在屏幕上显示了一条有用的消息。我们可以输入.help来获得用法提示,但是它会输出一个很大的命令列表,其中大部分我们在这一点上并不关心。在下面的摘录中,我截取了输出的大部分内容,只包含了将 SQLite 用作浏览器时有用的命令:

sqlite> .help
...
.databases          List names and files of attached databases
...
.mode MODE ?TABLE? Set output mode
.schema ?PATTERN?  Show the CREATE statements matching PATTERN.show              Show the current values for various settings
...
.tables ?TABLE?    List names of tables matching LIKE pattern TABLE
...Code language: plaintext (plaintext)

。数据库

使用.databases,我们可以看到哪个数据库连接到这个会话。您可以一次打开多个数据库,将数据从一个数据库复制到另一个数据库,或者连接不同数据库中的表中的数据。

当输入这个命令时,它将输出如下内容:

sqlite> .databases
main: C:\customers.db r/wCode language: SQL (Structured Query Language) (sql)

。桌子

.tables命令显示所有可用的表。在我们的例子中,这个命令的输出如下所示:

sqlite> .tables
customersCode language: SQL (Structured Query Language) (sql)

。 (计划或理论的)纲要

.schema命令打印用于创建表的 CREATE 语句。当不带参数运行时,它将打印所有表的模式。您可以通过提供特定表的名称来打印该表的模式:

sqlite> .schema customers
**CREATE** **TABLE** customers(**name** text, age int);Code language: SQL (Structured Query Language) (sql)

浏览 SQLite 表

此时,我们知道有哪些表,甚至查看了这些表背后的模式。如果您想查看这些表中的内容,您需要使用 SQL 语法。如果您不熟悉 SQL 语法,您可以使用并调整我下面提供的示例来安全地浏览数据。这些命令都不会改变数据库。

SELECT * FROM table _ name

SELECT 语句从表中“选择”数据。我们可以给它各种选项来过滤输出。最简单快捷的开始方式是选择表格中的所有内容。我们通过使用通配符符号*来做到这一点:

sqlite> **select** * **from** customers;
Erik|40
Mary|53Code language: SQL (Structured Query Language) (sql)

您在输出中看到的是表中的行。然而,输出并不清楚。我们可以用.mode column命令来解决这个问题:

sqlite> .mode column
sqlite> **select** * **from** customers;
name  age
*----  ---*
Erik  40 
Mary  53Code language: SQL (Structured Query Language) (sql)

限制行数

如果表格中有大量数据,您可能想要限制您看到的行数。这很容易做到,只需在 select 语句的末尾添加一个限制:

sqlite> **select** * **from** customers **limit** 1;
Erik|40Code language: SQL (Structured Query Language) (sql)

LIMIT 接受两个值,所以上面的命令是limit 0, 1的简写。含义:限制行数,从第 0 行开始,返回 1 行。

所以极限的语法是:

sqlite> **select** * **from** **TABLE** **limit** START_ROW, NUMBER_OF_ROWS;Code language: SQL (Structured Query Language) (sql)

记住计算机从零开始计数,所以如果我们只想看到第二行,我们可以用这个:

sqlite> **select** * **from** customers **limit** 1, 1;
name  age
*----  ---*
Mary  53Code language: SQL (Structured Query Language) (sql)

结论

您已经学习了如何创建、打开和浏览 SQLite 数据库。我们已经研究了一些 SQLite 命令,它们帮助我们检查数据库及其表。最后,您学习了使用 SELECT 语句查看表中的数据。

下次当您开始一个项目或需要分析一些关系数据时,请考虑 SQLite!很有可能,对于你所需要的来说,这已经足够了,而且可能会为你节省一些麻烦和宝贵的时间!

人工智能的最后一周——2021 年(全年)回顾

原文:https://towardsdatascience.com/lets-talk-ai-2021-the-full-year-in-reveiew-c68af9ee18b2

播客

人工智能的最后一周——2021 年(全年)回顾

在人工智能播客的最后一周,与我们的朋友一起回顾 2021 年下半年人工智能领域的最大事件

苹果 | 谷歌 | SPOTIFY | 其他

编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

2021 在许多方面都是一场狂野之旅,但它最狂野的特征可能实际上与人工智能有关。我们已经看到了从语言建模到多模态学习、开放式学习甚至人工智能对齐等各个方面的重大进步。

所以,我们想,还有什么比上周在人工智能播客中与我们的朋友在的一段交叉插曲更好的方式来盘点我们在 2021 年达到的与人工智能相关的重大里程碑呢(psst:你也可以在这里查看他们的精彩简讯)。

以下是我在对话中最喜欢的一些观点:

  • 基础模型继续增长,但一个有趣的趋势是关注效率以及(而不是?)尺度。例如,虽然 DeepMind 的 Gopher 模型的参数不到 GPT-3 的两倍,但据报道,它的效率是 25 倍,这意味着从相同的训练数据和计算中挤出了更多的价值。AI21Labs 的 Jurrassic 模型在参数计数方面也与 GPT-3 相当,但反映了我们对架构优化而非原始扩展的关注,我们预计这种关注将持续到 2022 年。(这并不是说大规模的扩张不会发生,或者说它还没有发生;几个月前发布的微软图灵-NLG,有超过 5000 亿个参数。但可以肯定地说,如果没有同步的效率优化,就无法实现扩展,而效率优化在 2020 年末还不是重点。)
  • 过程环境的生成一直是强化学习的一个重要主题。在开放式学习导致普遍有能力的代理中,DeepMind 的团队展示了如何在广泛的环境中训练 RL 代理可以导致与泛化相关的紧急行为,如试错和与友好代理的合作。
  • 开放式学习(OEL)似乎是一个有趣的通配符,一些研究人员认为这可能是最终 AGI 食谱的重要成分。在本期 TDS 播客中,我们采访了 OpenAI 的开放式学习负责人 Ken Stanley,探讨了 OEL 在未来人工智能中可能扮演的角色。
  • NeurIPS spotlight 的一篇题为最优政策倾向于寻求权力的论文,以及同一作者随后的工作表明,我们应该预计高度能干的人工智能系统会从事与人类价值观不符的危险行为,默认情况下。具体来说,高度称职的代理人将倾向于寻找强大的国家,因为它们提供了许多下游选项。这一发现令人信服地表明,人工智能的一致性应该得到优先考虑,特别是考虑到我们在更广泛的人工智能能力方面看到的进展速度。如果真的是这样,有能力的人工智能系统在默认情况下将是危险的,那么必须在安全研究方面投入积极的努力。

章节:

  • 0:00 介绍
  • 2:15 多模式模式的兴起
  • 7:40 的硬件和计算增长
  • 13:20 强化学习
  • 20:45 开放式学习
  • 26:15 求权纸
  • 32:30 安全和假设
  • 35:20 内在动机与外在动机
  • 42:00 映射自然语言
  • 46:20 Timnit Gebru 的研究所
  • 49:20 总结

白细胞检验——用神经网络进行血细胞分类

原文:https://towardsdatascience.com/leukopy-blood-cell-classification-with-neural-networks-2eb2e65441eb

深度学习如何彻底改变血液涂片上的白细胞识别

科林·伯伦斯来自的图片

这篇文章是与马修·萨拉特和拉勒·拉万博德合作撰写的。

一、背景

在本文中,我们将对生物学做一点了解,并探索 深度学习 如何帮助对血细胞进行分类。许多疾病的诊断,如传染病、白血病或其他血液疾病,依赖于白细胞亚型的分类,也称为白细胞。存在几种识别白细胞的生物技术,但是血液涂片的显微镜检查对于确诊通常是至关重要的。细胞有不同的特征:它们的颗粒、细胞核中的叶数、细胞核的形状、细胞质的颜色。

显示涂片上白细胞特征的图像。作者图片。

然而,这种技术容易出错,耗时需要专家,这就是计算机辅助血液涂片分析得以发展的原因。经典的白细胞分类流水线包括从细胞周围分割出细胞的、特征提取和选择,接着是浅层的 机器学习 分类器。这种工作流程很难从一个实验室推广到另一个实验室,因为染色、协议和采集系统的种类繁多。

这就是深度学习可以派上用场的地方。

传统上,循环血细胞分为 5 个主要亚型:血小板、红细胞、粒细胞(嗜碱性粒细胞、嗜中性粒细胞、嗜酸性粒细胞)、单核细胞和淋巴细胞。

外周白细胞和前体细胞。图片改编自维基百科上的 A.Rad。

我们在这里测试了 3 种类型的深度学习架构,以对 11 类健康血细胞进行分类:5 种主要亚型及其一些祖细胞(中性粒细胞祖细胞)。

  • 中性粒细胞(分段)— SNE
  • 嗜酸性粒细胞— EO
  • 嗜碱性细胞— BA
  • 淋巴细胞——LY
  • 单核细胞—钼
  • 血小板——血小板
  • 成红细胞— ERB
  • 未成熟(后髓细胞、髓细胞、早幼粒细胞)和带状中性粒细胞— MMY、— MY、— PMY 和— BNE

二— EDA

a.数据分布

我们使用了可公开获得的 3 个数据集:来自巴塞罗纳医院诊所的核心实验室,来自慕尼黑大学医院,以及来自拉什特拉齐医院、古尔哈克实验室、Shahr-e-Qods 实验室和位于德黑兰的 Takht-e Tavous 实验室( Raabin-WBC 数据集)。结合这些数据集,在 3 个不同的系统上获得了来自 MGG 和姬姆萨染色的血液涂片的大约 50,0 00 个单细胞图像。除了少数被诊断患有白血病的患者外,大多数患者被认为是健康的。

让我们做一些 EDA 来检查我们的数据。正如你在下面的柱状图中看到的,分段的中性粒细胞(中性粒细胞成熟的最高级阶段,简称 SNE)在 Munich 数据集中被过度代表淋巴细胞群体在 Raabin 和 Munich 数据集中占多数。在巴塞罗纳数据集中,这些类更加平衡。血小板只存在于后面的数据集中。****

b.UMAP 与降维

我们选择使用 UMAP 进行降维,因为它保留了数据中的局部和大部分全局结构。观察投射到 3 个主要部分的数据,我们可以看到可变性主要由数据的来源来解释,这可能是由于染色和光度变化的差异(参见我们的流线)。驱动聚集的另一个主要变量似乎是细胞的尺寸。

我们的数据是多样化的,可以构成一个有趣的数据集来建立分类模型。

III —模型化

为了对图像进行分类,我们测试了 3 种不同的架构,在我们的 Streamlit 应用 : VGG16 耦合到 SVM、VGG19 和 ViT 中有更详细的描述。我们的基线模型是一个逻辑回归模型,其显示的准确度约为65–70%

a.迁移学习和微调

我们对在 ImageNet 上训练的模型的所有神经网络使用了迁移学习,并且稍后对每个模型详细训练了或多或少的层。

b.预处理和规范化

我们的数据集分为训练集、验证集和测试集(80%、10%、10%)。使用数据扩充以减少过度拟合并提高概括能力。训练数据流通过以下管道:我们调整图片的大小和形状,然后应用一些增强(旋转、轴对称、剪切)。

图片由作者提供。

此外,我们在模型训练期间实施了以下内容:

  • 提前停止预定义耐心回调:防止过拟合。
  • 降低高原学习率:加速训练和收敛。

最后,我们在训练中使用了类权重:当错误发生在低群体类(如 PMY 或 BA)上时,我们惩罚更多:(1/class_count) * (total_count/2)

c.模特们

1)在分段细胞上具有 SVM 的 VGG16】

使用 VGG16 结合 SVM,我们的目的是研究背景过滤对模型精度的影响。我们使用 VGG16 网络进行特征提取,连接到支持向量机进行分类。下图所示的背景过滤或细胞分割过程是基于这样的假设,即细胞位于图像的中心附近,并且比图像的其他部分更清晰。

背景过滤过程中涉及的步骤。图片作者。**

网络 VGG16 顶部的 3 个完全连接的层没有作为基础模型包括在内,基础模型由以下层完成:GlobalAveragePooling2D、密集层(1024)、速率为 0.2 的丢弃层、密集层(512)、随后是另一个丢弃层以及最后是密集分类层。训练分 3 个步骤完成:训练所有层,然后训练除最后 4 层之外的所有层,最后定义中间层模型,包括基础模型、GlobalAveragePooling2D 层和密集层(1024)。为训练数据计算中间模型的输出,并用于训练 SVM。

F1 得分在过滤前后没有差异。换句话说,该模型查找单元的信息,而不管其背景。

2) VGG19

对于 VGG19,最后一个卷积块进行了微调,分类块层被替换为我们自己的自定义块。应用了特定的 VGG19 预处理,即 RGB 通道的反转和数据的归一化。

性能看起来不错,我们在测试数据上获得了大约 94%的全局准确率,但重要的是要记住测试数据和训练数据来自相同的来源(来自 Barcelona、Munich 和 Raabin 数据集的混合)。

3)视觉变压器

视觉变形金刚(ViT)已经改变了计算机视觉的领域。我们选择从 ViT-Keras 库中测试一个基本的 ViT-b16。在 ViT 中,图像被切割成小片(对于 ViT-b16 为 16 x16 像素),这些小片被展平并通过位置嵌入来连接。然后,这些投影被送入经过转换的编码器层,之后是一个多层感知器(MLP ),有点像解码器。最终 MLP 负责人负责最终分类。

图片作者。

对于这些模型,我们添加了一个标签平滑作为额外的正则化,试图考虑标签误差。我们还选择测试修正的 Adam 优化器,它对学习速率不太敏感,更具普遍性。

该模型的表现稍逊于 VGG19,在验证和测试数据上的全局准确率为 92%

四——结果分析

以下是 3 个模型的 F1 表现——每个类别的分数和整体准确度:

a.细胞成熟和标记

大多数图片被正确分类(F1 > 0.95),除了不同种类的中性粒细胞。混淆矩阵揭示了 3 个模型混淆了不同种类的中性粒细胞(成熟的 SNE 和不成熟的 PMY、MMY、MY、BNE),如下图所示(用 VGG19 获得的结果),其中错误分类图片的最大百分比用红色圈出

作者图片。

但是为什么呢???为了理解,我们需要援引生物学:PMY、米、MMY、BNE 和 SNE 是中性粒细胞成熟过程中的步骤。这个过程是一个连续的过程(例如,细胞核从马铃薯形状慢慢演变成多叶形),因此我们发现一些细胞具有两个连续生长步骤的特征是可信的。模型必须选择一个类,然后我们可以得到分类错误。

出于同样的原因,一些贴标机难以达成一致,可能会出现可能的贴标错误(下图) :

两张左图有着共同的显著特征(例如,相同的细胞核“豆”形、细胞质颗粒),但分别被标记为 PMY 和 MMY。第三张照片可能是“年轻的”BNE,或者是“老的”MMY,因为它的核呈 C 形。作者图片。

给这样的图片贴标签是一项复杂的工作,需要训练有素的专家,他们不会出错。

b.可解释性

现在,我们可以更进一步(✪ワ✪)ノʸᵉᵃʰᵎ和调查我们的模型在图片中看什么,然后再将其分配给一个类。对于 VGG 的模型,我们使用 Grad-CAM。对于 ViT,我们使用了注意力地图。这两种技术在图片上突出显示了给定预测的最重要的特征

我们在我们的 Streamlit 应用中加载了一个来自 VGG19 中 Raabin 数据集的嗜酸性粒细胞(EO ),下面是它的截图:

作者图片。

EO 的主要特征是一个分节的细胞核和细胞质中的粉红色颗粒。Grad-CAM 透露,当他试图确定图片是否为 EO 时,模型会搜索粉红色颗粒。ViT 注意力图似乎稍微更侧重于细胞核,但也考虑到了颗粒。对于其他类,Grad-CAM 和注意力图更难解释,但 Grad-CAM 和注意力图显示模型聚焦于图片中心的细胞,而不是红细胞背景。

限制:验证集之外发生的事情。

VGG19 的 Grad-cam 的四张图片来自 Google Images。作者图片。

我们已经测试了不是来自 Raabin、Barcelona 和 Munich 数据集的图片。例如,VGG19 能够准确分类一些形状和色彩非常相似的图片,如上图中的四张图片,这些图片是在 Google Images 上获得的。

然而,在一些与训练数据集非常不同的数据集上,如来自中国江西泰康科技有限公司的 WBC_segmentation ,该模型不幸失败(全局精度约为 8 %)。

来自中国江西泰康科技有限公司 WBC 图像数据集的图像示例。

这些图像是由不同的显微镜光学系统拍摄的,血液涂片经过实验室特定的染色处理,最重要的是,与训练数据集相比,图片分辨率较低。因此,可以取得进一步的进展。

六、结论

我们的模型在分类来自 3 个不同数据集的 11 类血细胞图片方面做得很好,但许多改进是可能的。此外,我们已经看到良好的全球指标可以隐藏重要的问题,例如,模型的能力概括完全不同的数据集。以下是一些潜在的改进:

a.增加多样性:增加和变化。

  • 更多不同来源或类型的图片:包括来自其他机构的图像(不同的采集系统,不同的染色,亮度等……)将允许更平衡的数据集(更少的 SNE,和 LY,更不成熟的)和更好的模型概括新数据的能力。
  • 扩充我们的数据:我们可以尝试模仿现实生活中的组织学染色,改变实际染色或者使用 GANs (生成对抗网络)或者 VAE (可变自动编码器)来制作新的图片。
  • 然后,我们可以考虑放弃迁移学习方法,并考虑在血细胞图片上对我们的模型进行完整的训练。

b.改进标签:使用生物学定义。

我们需要自信标注的图片来训练模特。一种选择是独立的病理学家专家之间的交叉验证过程,就像 Raabin 项目中所做的那样,但这是耗时和资源密集型的,因此自我监督或半监督学习可能是更合理的替代方案。

另一种选择是使用转录因子和颗粒蛋白作为标记。这可以通过与抗体共染色或在染色前使用流式细胞仪标记来完成。

c.物体检测:完全使用。

我们只处理了整个过程的一个步骤:我们使用分段的图片,只有一个单元格在中间。这项工作的一个可能扩展可能涉及在大规模血液涂片图片上的对象 (=血细胞)(例如,使用 YoloV5)检测,以便生成我们在该应用程序中使用的那种图片。****

我们希望你喜欢阅读我们的项目,如果你想取得联系,不要犹豫!

图片由维基共享资源的马克格顿提供。

使用 Python 升级:快速生成具有惊人视觉效果的 pdf

原文:https://towardsdatascience.com/level-up-with-python-quickly-produce-pdfs-with-stunning-visuals-d6750c9c7be2

数据可视化

使用 Matplotlib 制作业务分发手册/电子邮件/传单

最常被忽视的任务之一是有效的信息交流。您可以创建任何人都见过的最漂亮的仪表板,但是,如果您的管理团队拒绝使用仪表板(因为它位于一个很长的 URL,他们神秘地在浏览器书签中丢失),这没有什么区别。有时,快速和肮脏的 pdf 是共享信息的最快方式。

本教程的目的是向您展示如何使用 matplotlib 生成一个具有清晰布局、一些视觉效果和少量文本的图形,以便快速导出为 PDF。

Disclaimer: I make no claims that the PDF is the most effective means of communication. Beyond that, this is not even the easiest way to generate a PDF… (reader slowly moves mouse towards “close tab”). HOWEVER, if you have frequently changing data extractable with python 一 this is the tutorial for you. Set this PDF generation script on a timer and automatic listserv, and voila! Quick PDFs & quick distribution.

多个 python 包与 pdf 接口,但大多数都侧重于解析/读取应用程序。matplotlib 包本身就是最简单的 PDF 生成工具之一!您可以生成任何 matplotlib 图形,并将其导出为 PDF!但并非所有的视觉效果都是一样的。

在接下来的教程中,我将展示如何通过将独立图形转换为子图、自定义颜色/字体/大小/位置、添加文本以及在 matplotlib 中导出为 PDF 来调整和自定义单页 PDF 手册/电子邮件/传单的 matplotlib 图。完整的 GitHub 笔记本可以在这里找到。

第一步:选择显示图形。

这里有一个相当丑陋的情节,我决定与我的团队分享。别担心,我会修好的。在其中,我调用 matplotlib 轴类“ax”来定制绘图区域的颜色( ax.set_facecolor )、绘图边框(脊线)、刻度线和轴值( tick_params )以及轴标签( yaxis.label )。我还通过设置 plt.figure()中的 facecolor 参数来设置图形的背景颜色。此外,我们通过在绘图调用中指定‘w-o’来改变绘图线的颜色。这遵循“颜色标记类型”的格式,所以我们指定“白色圆圈标记”

下图强调了人生中的一个重要教训:你能做某事并不意味着你也应该做。

我改变这些颜色的目的是展示当从单个 matplotlib 图形切换到子图形排列时,这些自定义参数应该如何调整。

注意,生成图形的代码被包装在一个用户定义的函数 img1() 中。将图包装在函数中有助于(a)组织代码和(b)减少代码块之间的意外交互。

作者选择的可疑数字

警告:如果你不喜欢绿色,我很抱歉。

第二步:选择一个子图布局。

下面的代码在 matplotlib 中生成一个布局系统。基本上,它使您能够使用底层网格 (GridSpec) 作为参考来定制块的长度和宽度。还有更简单的副抽法(如fig, axes = plt.subplots(3, 2));然而,使用 GridSpec 可以让您在 matplotlib 中最大程度地自由定制。

下面,我铺开

  • 左边第一行中的一个数字(网格[0,0]),
  • 右边第一行中的一个图形(网格[0,1])关闭*
  • 一个跨越第二行两列的图形(网格[1,:2]),以及
  • 我的第三行网格上的两个图形(左网格[2,0],右网格[2,1])。

注*:有两种方法可以将此绘图区留空。(1)您可以指定位置,然后关闭轴。这些选项显示在注释中。或者(2)您可以不指定这个网格区域。该区域将是空白的,并且整体绘图将生成如下。

在网格的顶部,我们可以使用 fig.text() 在任何我们想要的地方添加文本框。注意文本框的坐标。坐标(0,0)映射到整个图形的左下区域。(1,0)映射到图的右下区域。在这里,我使用分数将我的初始文本框放置在我没有填充的空白处。后来,我会玩的位置,以实现一个漂亮的外观。

作者选择的布局

第三步:选择附加图形。

为了多样化,我还想加入一个构建在 matplotlib 之上的包,比如 matplotlib_venn。在这篇文章中,我将详细介绍如何在 python 中安排数据、绘制数据以及根据您的需要定制一个三向维恩图。以下是一个独立的、格式化的维恩图的可能版本:

我们得到了一个非常漂亮的下图。团队绝对应该看看这张!让我们把它写进我们的报告。

作者提供的维恩图示例

步骤 4:添加线条图作为支线剧情

现在我的布局已经在第 2 步中建立了,我可以开始用我的自定义图形填充布局了。第一个数字将放在左上角。

现在,我们对上面指定的基本布局做几件事。我们在第 1–4 行指定了一个配色方案,添加了一个标题,重新排列了我们的线图,并调整了右上角的文本框。

添加一个标题

通过在第 33 行调用 fig.suptitle() 来添加标题。可以定制字体系列、字体大小和颜色。

重新排列线条图

第 11–23 行指定了线图。请注意步骤 1 中的独立版本与此版本之间的差异。我们的轴调用现在用 add_subplot() 调用位于网格[0,0]位置的 fig_ax1 对象。我本可以在图上保留一个标题,但是它看起来组织得很奇怪。它必须消失。

调整文本框

我们在第 28 行的文本框位置添加了一些缓冲空间(+0.025,+0.03)。在我们的文本字符串中,我们使用“\n”分隔符添加换行符。

下面的代码生成了土绿色的小册子。

作者集成了第一幅图像的布局

步骤 5:添加维恩图作为支线剧情

添加了第一个表后,我们现在可以添加维恩图了。对代码的唯一修改是在右下方添加了 Venn,并删除了左下方的 plot,以便为附加文本腾出空间。

添加维恩图

第 33–51 行添加了维恩图。这很有意思。我们像以前一样在fig _ ax4 = fig . add _ subplot(location)中设置网格位置。不过,现在我们把 venn object v 设为 fig_ax4 ( ax=fig_ax4 )。图例成为轴的属性,但子集标签属于文氏对象 v。

这不是很好吗?我们将蓝色和紫色的独立维恩颜色改为一些土绿色和橙色,以防止视觉上冒犯我们的团队。

作者使用维恩的布局

第六步:添加表格作为支线剧情

接下来,我们在页面中间区域添加一个表格,并在左下角填写文本。

添加表

第 35–48 行通过绘制一个 axis.table() 对象向手册中添加一个熊猫数据框架。我们通过使用cell colors参数将单元格颜色设置为一个 9x4 的“medgreen”单元格数组来自定义填充颜色的白色默认值。必须用长度为 4 的列表设置列标题单元格,这是 dataframe 的宽度(ncol = 4 列),因此我们将 colColours 设置为 ['olivedrab']*ncol

添加更多文字

我们像前面一样在左下方添加文本。除此之外,我们将文本的对齐方式改为“右对齐”。这比你想象的更能改变位置,所以在设置位置缓冲区(-0.055,+0.23)之前改变对齐方式。

表格字体定制是通过改变 matplotlib(名为 mpl)中的默认 rcParams 实现的。关于 rcParams 定制的更多信息可以在这篇文章中找到。本质上,使用 matplotlib,表类的功能是有限的。对于习惯了 Excel 格式选项的用户来说,matplotlib 参数将不能令人满意。然而,基本功能和令人愉悦的视觉效果仍然可以在不使用额外软件包的情况下实现。正如我以前的老板常说的,有时候,做得比完美更好。

作者集成了表格的布局

最后,我们将上面的输出打包到 matplotlib 的 PdfPages() 函数中。

第七步:包装导出为 PDF 格式。

该图与上图几乎相同。作为一个挑剔的人,我通过调整第 40 行的 edges 参数改变了表格边界。另外,我在第 68 行添加了一个副标题。

导出为 PDF

只有第 1、70 和 71 行,我将该图导出为 PDF。

添加 PDF 元数据

使用第 74-80 行,我对 PDF 元数据做了一些快速更改,以便队友/客户/管理层知道这个 PDF 来自哪里。它添加了作者、标题、关键词和日期。

最后,我们结束了一个可爱的视觉如下。

作者完成的手册定稿

打开我们的 PDF 文件后,我们甚至可以检查元数据的更改是否真正实现了。很明显,他们是!

按作者分类的 PDF 元数据

总之,我们学会了在 matplotlib 中将独立图形转换为子图形,自定义颜色/字体/大小/位置,添加文本,并导出为 PDF。多么强大的可视化和交流工具啊!完整笔记本在此。

是时候发送一些结果了!

如果你对如何整合自己的数字有疑问或意见,请在下面评论。

我希望这篇教程能帮助你快速有效地交流你的发现。

喜欢这篇文章?更像下面的。

https://www.linkedin.com/in/kate-wall/

使用 Kedro 升级您的 MLOps 旅程

原文:https://towardsdatascience.com/level-up-your-mlops-journey-with-kedro-5f000e5d0aa0

向您的机器学习项目添加结构,以加速和维持创新

近年来,MLOps 的势头越来越大,这是一种在机器学习中提炼操作思维的努力。对我来说,MLOps 就是在机器学习中创造可持续性。我们希望在这个快节奏的创新领域建立一种结构化和可复制的文化。以这个熟悉的场景作为动力:

这里有一个需要你完成的用例。你能在星期五之前做它吗?

我们无畏的英雄成功地拼凑了一些东西。每个人都很开心。直到几天后,我们把它投入生产。几周后,新的数据出现了。几个月后,一个新人接手了这个项目。

你的代码能承受住压力吗?

开发机器学习项目就像修理服务器电缆一样。我们创建了十几个实验、工程管道、出站集成和监控代码。我们尽最大努力使我们的工作具有可重复性、稳健性,并随时可以交付。再加上一些团队协作,你就有了一大团意大利面。无论我们如何努力,总会有所收获,尤其是在时间紧迫的情况下。我们做一些小的捷径来积累技术债务,随着时间的推移会变得更加不愉快。用我们的比喻来说,杂乱的电线是火灾隐患,我们希望它们马上得到修复。数据必须流动!

cottonbroPexels.com拍摄

之前有人发现了这一点,对吗?幸运的是,是的,我们处在一个 MLOp 正在起飞的时代。有许多工具和框架可以支持 MLOps。过去,我使用内部自我维护框架和个人工程标准集的组合。如果您没有时间创建自己的框架,那么有一个像 Kedro 这样健壮的脚手架来为您打理基础是很好的。

点燃凯卓

Kedro 是一个开源 Python 项目,旨在帮助 ML 从业者创建模块化、可维护、可复制的管道。作为一个快速发展的项目,它与其他有用的工具进行了许多集成,例如 MLFlow、Airflow 和 Docker。可以把 Kedro 看作是将你的项目连接到一个公共结构中的粘合剂,这是迈向可持续机器学习的非常重要的一步。 Kedro 有惊人的文档,一定要去看看

启动 kedro 项目很容易。我在下面的代码块中写了一些东西。

  1. 虚拟环境几乎总是必要的。创建一个,并安装 kedro。
  2. 创建一个 kedro 项目。在这个例子中,我使用了一个正式的启动项目。还有其他的,这里链接Kedro 的一个强大功能是,你可以通过 cookiecutter 创建自己的模板。 根据您所在组织的标准来检查这一点。
  3. 创建一个新的 git 环境 MLOps 的另一个必须做的事情!
  4. 安装依赖项。
  5. 运行管道。

您将看到为您生成了一些目录。这个结构是任何 kedro 项目的基础。您将会看到一些很棒的操作性的东西已经嵌入其中,比如测试、日志、配置管理,甚至 Jupyter 笔记本的目录。

对 Kedro 项目的剖析

项目概述

在这篇文章的其余部分,我将使用来自 Kaggle 的数据集,这是健康保险中交叉销售用例的。如果你想要我的全部代码,你也可以看看这个 Github 链接。

稍后您将会看到,数据工程和机器学习工作非常标准,但是 Kedro 真正闪光的地方是它如何使您的管道可维护,坦率地说,非常漂亮。点击此处查看我的管道:

图片由作者提供

Kedro 管道由数据源和节点组成。从顶部开始,您可以看到数据源。您可以选择将它放在 Python 代码中,但在我的情况下,我希望它放在配置文件中,如果位置、凭证等发生变化,这是非常关键的。如果您有十几个数据集,您会希望它们的定义远离代码,以便更易于管理。此外,您可以在一个配置文件中管理中间表和输出,因此格式都是从代码中抽象出来的。

顺便说一句,文档中有一整节专门用于凭证管理和引用云资源。为了简单起见,我将把事情放在本地。

创建您的 Kedro 节点

节点是行动的地方。Kedro 允许很大的灵活性。在这里,我使用 scikit-learn 和 XGBoost 进行实际的建模,但是您也可以使用 PySpark、PyTorch、TensorFlow、HuggingFace 等等。在下面的代码中,我有我的数据工程代码,和我的建模代码(data_science)。您希望项目的阶段是什么样子取决于您。为了简单起见,我只有这两个。

首先,我的数据工程代码只有一个节点,所有的预处理都在这里完成。对于更复杂的数据集,可以包括特定数据源的预处理、数据验证和合并。

其次,data_science 代码包含模型的训练。data_engineering 部分的输出将作为这些节点的输入被插入。如何将输入和输出连接在一起在管道部分中定义。

这里有一个 Optuna 的例子,它是一个超参数优化平台。正如您将在下面看到的,您还可以使用 kedro-mlflow 将 kedro 和 mlflow 连接在一起。

如果你是那种专注于笔记本电脑的人,那么你可能会对其数量感到震惊。py 文件。不要烦恼!Kedro 可以 把你的 Jupyter 笔记本变成节点。

定义您的 Kedro 管道

为了把这些联系在一起,Kedro 使用了管道。使用管道,您可以设置节点的输入和输出。它还设置节点执行顺序。它看起来非常类似于火花和气流中的有向无环图(DAG)。您可以做的一件很棒的事情是创建钩子来扩展 Kedro 的功能,比如在节点执行后添加数据验证,以及通过 MLFlow 记录模型工件。

  1. 第 32 行有来自 split 节点的所有输入,但是有一个特殊的参数字典。Kedro 自动从您的parameters.yml中放入您的参数,默认在您的conf/base目录中。
  2. 线 34 接收来自节点的多个输出。第一个是分类器,第二个是要保存在 MLFlow 中的模型度量。这是在catalog.yaml中指定的。请注意,Kedro 也有自己的实验跟踪功能,但是我想向您展示它确实可以很好地与其他工具配合使用。
  3. 第 45 行从parameters.yaml接收一组参数您可以重用同一个管道,为不同的运行输入几组参数!

最后,Kedro 项目的顶层是 pipelines 注册表,它包含可用的管道。我在这里定义了多个管道和它们的别名。默认情况下,default 将调用所有管道。您可以通过kedro run --pipeline=<name>调用特定的管道。

之后,是部署的时候了。从这里开始,你可以做几件事,如这里链接的:

  • 要打包并创建车轮文件,请执行kedro package。然后,这些文件可以转到您自己的 Nexus 或自托管的 PyPI。如果将包脚本作为构建例程的一部分放在 CI 服务器中就更好了。没有什么比让另一个团队简单地安装你自己的项目更好的了!
  • 您还可以使用kedro build-docs生成您的项目文档。它将使用 Sphinx 创建 HTML 文档,因此它包含了您的文档字符串。是时候给你的文档游戏增添趣味了!
  • 将管道导出为 docker 容器。这可以成为您的组织的更大的编排框架的一部分。这也有助于社区已经有了针对 KubeflowKubernetes 的插件。
  • 集成 Airflow 并将您的项目转换为 Airflow DAG。
  • 以及其他十几个具体部署

MLOps、Kedro 等等

来自 MLOps 堆栈画布(https://ml-ops.org/)的图像

回想一下,我们已经开始做 MLOps,这只是一种可持续的方式来控制机器学习的巨大复杂性。我们已经实现了一个通用的结构,一种重现运行、跟踪实验和部署到不同环境的方法。然而,这仅仅是开始。其他最佳实践正在融合并快速发展。所以一定要练好基本面,永远记得可持续创新。

原载于 2022 年 3 月 11 日http://itstherealdyl.com

解释 Levene 的方差相等测试(使用 Python 示例)

原文:https://towardsdatascience.com/levenes-test-for-equality-of-variances-explained-with-python-examples-f0445a19805f

在本教程中,我们将探讨 Levene 的方差相等测试及其在 Python 中的应用

照片由 Unsplash 上的 Pritesh Sudra 拍摄

目录

  • 介绍
  • 抽样资料
  • Levene 的测试解释了
  • 勒文检验假设
  • Levenet 检验统计量
  • Levene 在 Python 中的测试示例
  • 结论

介绍

许多统计测试和程序都假设数据的正态性和方差相等。

这些条件通常决定了研究人员是否可以使用参数或非参数测试,以某种方式阐明他们的假设,等等。

Levene 检验是推断统计学中最流行的检验之一,它处理从非正态分布中提取的数据。

Levene 的测试是什么?

Levene 检验用于检验两个或更多组(样本)计算的变量的方差是否相等。

如何解读 Levene 的测试?

如果 Levene 检验的 p 值小于显著性水平(例如 0.05),则至少两组的方差不相等。

为了继续学习本教程,我们需要以下 Python 库:pandas 和 scipy。

如果您没有安装它,请打开“命令提示符”(在 Windows 上)并使用以下代码安装它:

pip install pandas
pip install scipy

抽样资料

要执行本教程示例部分和 Python 实现部分中提到的计算,您将需要一些数据。

在本教程的所有示例中,来自。使用下面的 csv 文件。

该数据包含三组对新疗法反应的 80 次观察:“对照组”、“治疗 1 组”和“治疗 2 组”。

数据服从非正态分布。

在此下载示例数据:https://pyshark . com/WP-content/uploads/2022/03/data _ levenes _ test . CSV

Levene 的测试解释了

如前所述,方差相等的假设在统计分析中是很重要的,并且在测量实验和数据分析的结果时经常会影响研究者的工作程序。

勒文检验假设

Levene 检验的无效假设是所有组的方差相等。

Levene 检验的另一个假设是,至少有一对组的方差不相等。

给定大小为 N 的变量 Y ,其被分成 k 个组:

作者图片

作者图片

其中:

  • k :总组数(≥2)
  • I:k 组之一
  • j:k 组之一
  • ( i,j ):来自 k 组的一对组
  • 我≦j:两个组不是一个组

勒文检验统计量

Levene 的检验统计量由下式给出:

作者图片

其中:

作者图片

原始论文仅提出使用平均值计算 Z_{ij} 值,Lavene 检验的进一步扩展提出在计算 Z_{ij} 值时使用中间值和修整平均值。

计算出 Levene 的测试统计值( W )后,应将其与下式给出的上临界值进行比较:

作者图片

因此,我们拒绝等方差的零假设,当:

作者图片

Levene 在 Python 中的测试示例

为了在实践中看到 Levene 的测试及其在 Python 中的应用,我们将使用前面一节提到的样本数据文件

首先,导入所需的依赖项:

然后阅读。csv 文件提供到 Pandas 数据框架中,并打印前几行:

您应该得到:

 group  before_treatment  after_treatment
0  control              27.9             33.8
1  control              16.8              9.3
2  control              27.2             23.4
3  control              12.5             19.9
4  control              14.4             16.0

您可以计算一些组摘要统计信息,以便更好地理解数据:

您应该得到:

 group  avg_bef_tr  var_bef_tr  avg_aft_tr  var_aft_tr
0     control      20.145   18.878436      19.825   28.825513
1  treatment1      19.210   17.007263      15.475    4.649342
2  treatment2      21.510   19.673579      20.315   15.141458

这里你可以清楚地看到 3 组之间的“ var_bef_tr ”(治疗前方差)差异并没有那么大:18.88,17.01,19.67。

然而,3 个组之间的“ var_aft_tr ”(治疗后方差)差异相当大:28.83、4.65、15.14。

三组治疗后的方差差异足够大,我们几乎可以肯定它是显著不同的,但是为了从统计上检查它,我们将在 Python 中执行 Levene 的测试!

我们需要创建变量来存储与特定组相关的观察结果:

最后用 Python 执行 Levene 的测试:

您应该得到:

Lavene's test statistic: 4.240957881271611
P-value: 0.017895111992486838

由于 p 值小于 0.05,我们拒绝零假设,并得出结论,至少有一对组的方差不相等。

注:

默认情况下, levene() 函数使用中值方法计算绝对残差。然而,正如在解释部分中提到的,你也可以使用均值和修整均值。官方文档显示了如何更改测试中使用的功能。

结论

在本文中,我们讨论了如何使用 scipy 库执行 Levene 方差相等测试及其在 Python 中的应用。

如果你有任何问题或对一些编辑有建议,请随时在下面留下评论,并查看更多我的统计文章。

原载于 2022 年 3 月 17 日 https://pyshark.com**的

利用云技术大规模搜索恶意软件

原文:https://towardsdatascience.com/leverage-cloud-technologies-for-malware-hunting-at-scale-27718244ee6c

如何使用 Apache Spark 和 Iceberg 表索引数百 TB 的恶意软件

照片由 Hes Mundt 在 Unsplash 上拍摄

在本文中,我们将展示如何使用 Spark 和 Iceberg 表实现类似于 UrsaDB 的恶意软件索引,并将该索引集成到 Mquery 中,这是一个分析师友好的 web GUI,用于提交 YARA 规则并显示结果。

这一概念验证是在极客周期间开发的,极客周是由加拿大网络安全中心组织的年度研讨会,汇集了网络安全领域的主要参与者,为行业面临的重要问题提供解决方案。

简而言之 YARA

YARA 是一款旨在帮助恶意软件研究人员识别和分类恶意软件样本的工具。使用 YARA,您可以基于文本或二进制模式创建恶意软件家族的描述。每个描述,也称为规则,由一组字符串和一个决定其逻辑的布尔表达式组成。例如:

rule silent_banker : banker
{
    meta:
        description = "This is just an example"
        threat_level = 3
        in_the_wild = true
    strings:
        $a = {6A 40 68 00 30 00 00 6A 14 8D 91}
        $b = {8D 4D B0 2B C1 83 C0 27 99 6A 4E 59 F7 F9}
        $c = "UVODFRYSIHLNWPEJXQZAKCBGMT"
    condition:
        $a or $b or $c
}

如果在文件中找到这些序列中的任何一个(a 或 b 或 c ),该规则将评估为真。YARA 是一个用 C 语言编写的可执行文件,通常在命令行上调用,方法是向它传递一个规则和一个二进制文件文件夹以进行处理。

虽然 YARA 执行速度很快,但以暴力方式处理数百万个恶意软件样本会占用大量 CPU 和 I/O 资源,因此执行时间会非常长。

波兰证书

为了加快对 YARA 规则的评估,波兰计算机应急小组(CERT-Polska)建立了一个名为 UrsaDB 的定制数据库和一个名为 Mquery 的分析师友好的 web GUI。

UrsaDB 充当布隆过滤器来测试给定的字节序列是否可能出现在二进制文件中。假阳性匹配是可能的,但假阴性是不可能的。换句话说,查询返回“可能在文件中”或“肯定不在文件中”。

Mquery 首先使用 UrsaDB 来查找可能包含所需字节序列的候选文件,然后使用 YARA CLI 来确认该文件是否匹配。因此,YARA CLI 是在整个恶意软件语料库的一个小子集上进行评估的,这大大加快了整体执行时间。

让我们更深入地了解一下 UrsaDB。

乌尔萨博

UrsaDB 中的主要指数是3gram指数。

对于语料库中的每个文件,UrsaDB 提取所有可能的唯一三字节组合。3gram索引本质上是一个大图,其中键是一个3gram,值是一个list of files containing the 3gram

例如,如果我们索引一个包含 ASCII 字符串TEST MALWARE (ASCII: 54 45 53 54 20 4D 41 4C 57 41 52 45)的文本文件,那么数据库生成以下三元组(_表示空格字符):

+---+-----------+---------+
| # | Substring | Trigram |
+---+-----------+---------+
| 0 | TES       | 544553  |
| 1 | EST       | 455354  |
| 2 | ST_       | 535420  |
| 3 | T_M       | 54204D  |
| 4 | _MA       | 204D61  |
| 5 | MAL       | 4D616C  |
| 6 | ALW       | 414C57  |
| 7 | LWA       | 4C5741  |
| 8 | WAR       | 574152  |
| 9 | ARE       | 415245  |
+---+-----------+---------+

UrsaDB 是一个自定义的 C 程序,运行在单台机器上,能够处理大量的恶意软件文件。UrsaDB CLI 提供了索引、压缩和搜索3gram索引的方法,这些索引作为定制格式的二进制文件存储在磁盘上。

UrsaDB 仅限于单个机器,尽管可以集群多个 UrsaDB 实例,但它本身不受支持,并且跨机器管理多个索引很麻烦。UrsaDB 还要求索引文件驻留在连接的存储设备上。

许多网络规模的公司正在重新评估他们对 HDFS 的使用,以支持云 blob 存储。Blob 存储正迅速成为存储大型数据集的事实上的选择。这有很多原因,很多文章都提到了这个话题。

在我们的概念验证中,我们希望了解是否可以利用云 blob 存储,特别是冰山表来降低成本并简化大规模恶意软件索引的管理。

用火花和冰山构建 3g 指数

UrsaDB 使用磁盘格式将其索引存储为文件 id 和3grams的序列。此外,UrsaDB 使用游程编码技术,该技术也用于拼花文件(拼花文件是 Iceberg 表的基础)。Parquet 文件格式是众所周知的,通常用于大数据平台。

我们需要构建索引的第一件事是将文件分成3gram的方法。这是构建索引时计算最密集的部分。我们尝试了许多方法,从纯粹的 Spark SQL 到定制的 panda python UDFs,但是没有一种方法可以与利用 ByteBuffer 和令人难以置信的 fastutil 库的定制 Java 实现相媲美。这是我们的自定义 UDF,它将所有不同的3grams作为整数值返回。

使用这个函数,很容易建立一个3gram索引。这就是转变的样子。请注意,我们在大多数例子中都使用了%%sparksql魔法。您可以在关于 jupyterlab-sql-editor 的前一篇文章中找到关于这个扩展的更多信息。

shingling 函数应用于一个字节序列,并以整数值列表的形式返回唯一的3gram。然后,我们分解列表并移动整数值,以获得代表 a 3gram的字节的三列。

我们选择将3gram的每个字节存储到它自己的列(a,b,c)中,而不是存储到一个单独的列中,因为这样更容易概念化,并且可以更好地压缩表。

最后,我们按照构成3gram的字节对表格进行排序;(甲、乙、丙)。对于每个拼花文件,Iceberg 将存储 a、b 和 c 列的最小值/最大值。在查询时,Iceberg 使用这些统计数据来快速识别对于给定的3gram应该扫描哪些拼花文件,并由此找到哪个恶意软件 file_id 包含给定的3gram

既然我们可以索引一个字节序列,我们所要做的就是获取恶意软件文件的字节。这在 Spark 中很容易。读取文件内容无非是使用带有二进制文件格式选项的spark.read()函数。read()函数返回包含文件路径、修改时间、文件大小和文件内容的数据帧。

一旦我们有了一个恶意软件表,我们就可以对content列应用收缩函数。这里,我们展示了与前面相同的示例,但是我们使用 file_id_map 表来获取给定文件路径的 file_id。我们不在索引中保存文件路径,因为与存储一个简单的 file_id 相比,这将是相当大的。

-- We use Iceberg's WRITE ORDER BY to make sure the table is sortedALTER TABLE {table_name} WRITE ORDERED BY ts, source, a, b, c, file_id`

在 Spark SQL 中查询 3gram 索引

Mquery 负责解析 YARA 规则,并将字节或文本模式转换成一个要搜索的列表。Mquery 包含一个 UrsaDB 代理,它使用这些“3gram来搜索”以形成一个 UrsaDB 查询。

让我们假设期望寻找的字节模式是**0 -1 -86 -69 -14 88** Mquery 会生成以下 4 个3grams

UrsaDB 将查找包含所有 4 个3grams的候选文件,但不一定是按顺序排列的。然后对文件 3 和 4 执行 YARA CLI,发现只有文件 4 匹配。

出于我们的目的,我们编写了一个定制代理来向 Spark 而不是 UrsaDB 提交查询。我们的 pyspark 代理获取要搜索的列表“3gram”并生成相应的 Spark SQL 查询。

每个3gram被转换成 3 个字节,每个字节在一个where子句中针对列 a、b 和 c 进行测试。查询的结果是具有特定3gram的文件列表。

然而有一个微妙之处。当给定 4 个3gram的列表时,我们必须只返回所有43gram请求的文件。为了实现这一点,我们使用了一个*group by + having*子句完整的查询如下所示。

由于该表是按 a、b 和 c 列排序的,Iceberg 可以使用它的元数据有效地删除大部分拼花文件。然后,在每个文件中,Spark 将使用 parquet 文件页脚(页面统计)来进一步隔离潜在的匹配。因此,查询执行得相当快。

查询的结果是一个简单的file_id列表,它可以很容易地在集群中被打乱以评估最终的*group by + having*子句。该查询的结果是具有所有 4 个3grams的文件列表,但不一定是顺序的。这 4 个3grams可能在恶意软件文件的任何地方。记住,索引就像一个布隆过滤器,给我们一个“候选文件”的小列表。我们现在必须让 YARA 支持这些候选人。火花在这里也能帮助我们。

在分布式 Spark 集群上运行 YARA

Spark 是一个分析平台,可以分发并行运行的任意 python 代码。此外,当使用 yara-python 绑定时,从 python 调用 YARA C 库很容易。

因此,在 Spark 中运行分布式 YARA 相当容易。下面是一个简单的 python UDF,展示了 yara-python 对恶意软件文件内容的调用。

把所有的放在一起

这里有一个演示展示了对沉默的银行家 YARA 规则的评估。它显示以下内容:

  • 首先,我们的定制 pyspark Mquery 代理将3gram列表转换成 SQL 语句。
  • 其次,一个定制的 Spark UDF 找到候选文件,在这些文件上评估完整的 YARA 规则。
  • 最后,匹配的文件被返回到 Mquery web UI。

结论

在本文中,我们展示了如何利用 Spark 构建可伸缩的3gram索引。该索引以 Iceberg 表格式存储,并由廉价的 datalake blob 存储支持。

然后,我们演示了如何编写一个定制的 Mquery 代理来生成针对索引的 Spark SQL 查询。

最后,我们展示了如何利用 yara-python 来分发 yara 规则的完整评估。

我们的初步结果是有希望的。我们能够索引和查询 13tb 的恶意软件,压缩比和执行时间与 UrsaDB 相当,但具有更便宜的存储和更容易的索引管理的优势。

我们计划生产这种概念证明。在以后的文章中,我们将进一步深入这个项目,并给出关于实现、查询次数、压缩比和成本的更多细节。敬请期待!

利用查询重构和基于函数的索引来提高性能

原文:https://towardsdatascience.com/leveraging-query-restructuring-and-function-based-indexes-to-improve-performance-c50d50246964

我们优化查询运行时间的旅程

蒂姆·莫斯霍尔德在 Unsplash 上的照片

检查在线创建的无限数量的数字数据,即博客、网络研讨会等,需要一个布鲁姆过滤器数据结构来优化我们一些验证的运行时间,并最终在 Brew 为我们的客户创造价值。

除了使用布隆过滤器,为了确保我们能够有效地验证数据,同时不断提高数据库中的搜索性能,我们需要将查询重构和基于函数的索引集成到我们的算法中。

注意:因为我们使用 Django 和 PostgreSQL,所以本博客中的所有代码示例都将使用 Django ORM 来转换原始 SQL 查询,但是我将讨论的原则和要点适用于任何使用 ORM 和关系数据库的开发人员。

Brew 平台处理由无数营销活动产生的无限量数据,每个营销活动都有一个唯一的 URL。
每个 URL 可以有多个合法版本,例如使用 HTTP 或 HTTPS 方案的 URL、以“www”开头的 URL 或不以“www”开头的 URL、路径中带有语言前缀的 URL(例如“http://example.com/en/page”)、以“html”后缀结尾的 URL 等。

由于我们可以接收这些变化中的任何一个作为潜在的新活动的链接,并且由于我们想要检查这是否是一个新活动,我们必须使用这些版本中的一个在我们的数据库中检查这个 URL 是否存在。为此,我们构建了一个包含所有常见变体的正则表达式的查询,以支持这些搜索。特别是在 Django 的 ORM 中,用正则表达式查询很简单,因为它有一个支持正则表达式的本地字段查找操作符。例如:

Entity.objects.filter(url__iregex="http(s)?:\/\/(www\.)?example\.com")

上面的查询获取所有具有 HTTP 或 HTTPS 模式的 URL,可以以“www”开头或者没有它。使用 iregex 操作符而不是 regex 使得搜索不区分大小写。

放大问题

在查询中使用正则表达式在一段时间内效果很好,但是随着数据库的增长,我们注意到在我们的部分过程中有些缓慢。我们开始调查为什么会出现这种速度缓慢的情况,以及这些时候我们的服务器上发生了什么。我们注意到,在我们的一些搜索中,数据库的 CPU 使用率相当高。这令人惊讶,因为为了优化搜索,我们确保查询中经常使用的列都有索引。然而,在运行特定查询时,数据库中的高 CPU 意味着该查询中没有使用索引。为了验证这个假设,我们检查了可疑查询的执行计划。

可以使用 PostgreSQL 中的以下命令来检查执行计划:

EXPLAIN ANALYZE SELECT * FROM entity_table WHERE link = 'https://www.example.com'

它也可以通过类似 pgAdmin 的数据库管理工具来可视化。

我们正在检查的查询的可视化解释计划如下所示:

作者图片

用于 entity_table 的图标表示全表扫描,这意味着不使用索引。全表扫描是一个 O(N)操作,当试图在一个有数百万行的表中搜索一个数据点时,它可能是一个非常昂贵的操作。

那么,为什么这些查询中没有使用我们的索引呢?

当在 Django ORM 中使用 regex 的本地字段查找时,ORM 将这个操作符翻译成数据库中的 regex 操作符。因此,如果我们运行以下搜索:

Entity.objects.filter(
    url__iregex="http(s)?:\/\/(www\.)?example\.com"
)

在 PostgreSQL 中,它将转换为:

SELECT * 
FROM entity_table 
WHERE url ~* ‘http(s)?:\/\/(www\.)?example\.com’

数据库中的正则表达式运算符使查询评估每行中的字符串,以查看它是否与正则表达式匹配。该运算符不能使用常规 B 树索引(为该列构建的索引),因为 B 树索引用于相等和简单比较,而在正则表达式中并非如此。

解决问题

在 PostgreSQL 的早期版本(9.4 之前)中,没有可用于正则表达式查询的索引。因此,有必要更改查询,以确保使用了索引并且优化了查询的运行时间。

为此,我们必须停止使用正则表达式的操作符,因为它阻止了 b 树索引的使用。然而,我们仍然想搜索所有的 URL 变体。为此,我们必须将查询拆分成多个条件,而不是将一个条件与一个正则表达式一起使用。我们之前的查询变成了这个查询:

Entity.objects.filter(
    Q(url__startswith="http://www.example.com") |
    Q(url__startswith="https://www.example.com") |
    Q(url__startswith="http://example.com") |
    Q(url__startswith="https://example.com")
)

ORM 中的这个查询转换成数据库中的以下查询:

SELECT * 
FROM entity_table 
WHERE url LIKE ‘http://www.example.com%' OR
      url LIKE 'https://www.example.com%' OR
      url LIKE 'http://example.com%' OR
      url LIKE 'https://example.com%'

这个查询现在使用索引,尽管我们有多个条件,而不是一个,但它比前面的查询优化得多,因为索引正在被使用。

注意:即使我们没有使用等式操作符,而是使用了LIKE操作符,它只匹配字符串的一部分,但是仍然可以使用索引,因为我们锚定了字符串的开头,在搜索索引时,它可以用作等式匹配。如果我们没有固定字符串的开头,索引就不能使用。
例如,下面的查询:

SELECT * 
FROM entity_table 
WHERE url LIKE ‘%://www.example.com%'

该查询必须运行全表扫描,因为字符串的开头是通配符,并且当字符串的开头不是特定字符串时,没有优化的方法在 b 树索引中搜索该字符串。

现在,我们的查询得到了优化,使用了 DB 索引,运行时间也大大减少了。然而,这个查询是不完整的,因为它遗漏了我们以前拥有的一个功能。在前面的查询中,我们使用了运算符iregex ,它被转换为不区分大小写的正则表达式。在新的查询中,我们使用操作符startswith,它区分大小写,并不完全匹配我们的用例。为了解决这个问题,我们有两个选择——一个是使用 ORM 的LOWERUPPER函数操作符,另一个是使用istartswith操作符,它相当于startswith操作符,不区分大小写。吸取教训,我们不想在没有确保不会降低查询运行时间的情况下使用操作符。查看 ORM 中这个查询到数据库查询的转换,我们看到了下面的代码:

Entity.objects.filter(url__istartswith="http://example.com")

转换为查询:

SELECT *
FROM entity_table
WHERE UPPER(url) = UPPER("http://example.com")

注意:在这种情况下,两个可选解决方案之间没有区别,因为两者都使用了 LOWER 或 UPPER 数据库函数来使查询不区分大小写。

使用这些函数会引入以前遇到的相同问题,因为在我们转换要搜索的列时,不会使用常规的 b 树索引。

幸运的是,有一个简单的解决方案,那就是基于函数的索引。

基于函数的索引是根据函数或表达式的结果创建的索引,而不是根据此列中的原始值创建的索引。在我们的示例中,我们可以创建一个基于函数的索引,该索引基于数据库中“ url ”列中每个值的UPPER函数的结果。每当我们在查询中的“ url ”列上使用该函数时,都会使用该索引,这允许我们优化查询,即使不使用该列中的原始值。
大多数(如果不是全部的话)关系数据库都支持基于函数的索引,并且它们可以用于对表的常见查询在列上使用函数或某种简单表达式的任何场景,例如在浮点列中取整值、仅提取日期的月份部分、将日期截断到一天的开始或者甚至两列之和。这些示例中的每一个都将取消常规 b 树索引的使用,并可能导致查询运行时性能下降。因此,如果这些是您的表上的常见查询,您应该考虑对正在使用的常见表达式/函数使用基于函数的索引,这可以显著优化查询运行时间。

关键要点

这种由于错误使用 ORM 操作符而导致的运行时性能下降的经历向我们重申了了解我们的 ORM 以及它如何将我们的代码翻译成查询是多么重要。ORMs 是一个很好的工具,它增加了一个抽象层次,允许我们在与数据库交互时继续使用面向对象的范例。然而,我们总是需要记住这些抽象是有代价的。简单的查询在 ORM 中总是很好用,但是更复杂的查询需要更好地理解 ORM 如何翻译我们的代码。了解 ORM 是如何工作的可以帮助我们避免主要的陷阱,就像我们遇到的那样。

从我们的经验中可以学到的另一个教训是,了解数据库如何执行我们的查询是多么重要。了解这些知识以及优化查询的各种解决方案可以帮助我们在构建模型时提前计划,并提前防止优化问题,或者在遇到优化问题时轻松解决它们。

Django 开发者侧记。

对于 Django 开发人员来说,现在向模型中添加基于函数的索引是相对简单的,从 3.2 版本开始,可以在模型规范中声明它。例如:

class Entity(models.Model):
    url = models.URLField(max_length=2100)
    name = models.TextField()

    class Meta:
        indexes = [
            models.Index(Upper("url"))
       ]

利用“5S”的力量获得干净和可重用的代码

原文:https://towardsdatascience.com/leveraging-the-power-of-5s-for-clean-and-reusable-code-44e1dc466af2

来自制造业的干净和可重用代码的方法论

照片由布雷特·乔丹Unsplash 拍摄

当处理代码时,我们经常谈论干净的和可重用的代码。Python 是一种面向对象的编程(OOP)语言,它非常适合重用代码,因为例如,我们可以创建自己的函数并调用它们。但是,我们能遵循一种方法来编写干净的、可重用的代码吗?

在本文中,我们将看到“5S”方法,它对于创建干净且可重用的代码非常有用,甚至在我们的数据科学项目中也是如此。

“5S”方法来自所谓的“精益制造”或“丰田生产系统”(TPS),因为它是在丰田汽车公司开发的。我目前是一名流程工程师,我知道并在生产环境中应用“5S”方法,但我发现我们甚至可以在软件开发中使用这种方法;我们将看到什么是“5S ”,以及万如何将它们应用到软件开发中,并给出一些例子。

1.排序(Seiri)

第一个“S”是“Seiri”,在英语中,它的意思是“排序”。

在制造业中,“Seiri 正在对(工作)场所的所有物品进行分类,并从该场所移除所有不必要的物品”(参考。[1])

我们如何将这个概念应用到软件开发中呢?例如,你可以:

  • 删除不必要的导入库。我知道:当试验你的数据时,你可能会导入很多库,你可能会忘记它们。但是,请:定期检查你的代码并删除它们:你的电脑会感谢你的!
  • 只导入需要的模块,而不是整个库。你最好键入from sklearn import metrics而不是import sklearnfrom sklearn import *。你的电脑会感谢你的(而且你不会等你的电脑计算它的运算数年!)
  • 删除代码的注释行。我知道:你只是把它们留在那里,因为有一天你可能需要它们;我告诉你一个秘密:你永远都不需要它们。但是,如果您真的担心可能需要它们,那么创建一个单独的文件来存储您的代码行,并将它们从主文件中删除。

2.按顺序排列

在制造业中,“赛顿将所有必要的物品放在最佳位置,以实现其在工作场所的功能”(参考。【1】)。在软件开发中,它可能是:

  • 导入了所有需要的库了吗?
  • 汇总笔记本的单元格。观察你笔记本的单元格:它们看起来像什么?你能把一些细胞聚集在一个细胞里吗?
  • 在笔记本上使用减价功能。对你的代码进行评论是非常重要的,但是使用 markdowns 让你有可能在你的笔记本上“创建章节”,就像你在写一个演示文稿一样;此外,留下几行简单的单词(使用减价)来解释你正在做的事情,会让你的工作更加有序和干净。在我做的这个项目中,你可以看到一个我们如何利用降价来组织我们的笔记本的例子。
  • 创建文件夹和子文件夹。对于必须从多个文件导入数据(或模块)的复杂项目,请创建子文件夹,以便在文件夹级别组织工作。

3.闪耀(Seiso)

在制造业中,“Seiso 定期清扫或清洁和检查工作场所、工具和机械”(参考。[1]).在软件开发中,它可以是:

  • 清理你的变量。不要只叫他们“x”或“y”。我的意思是,如果你在你的代码上做实验,你甚至可以称它们为“x”或“y”,但是当你看到你的代码工作时,给你的变量一个“会说话的编码”。例如:如果你在建模一条线,不要只写y=m*x+q。相反,使用类似于:
intercept = .... #x
slope = ... #m
constant = ... #q#my line (y)
line = slope * intercept + constant
  • 清理你的评论。有时,在我们的代码中,你甚至可以在注释中发现一些混乱。例如,如果你复制了某行代码,你可以根据你正在编写的新代码调整你的注释(这也是为什么我们有更好的创建函数的原因之一(见第 4 点),但有时这是不可避免的)

4.标准化

在制造业中,“Seiketsu 将用于分类、排序和清洁工作场所的流程标准化”(参考。[1])。在软件开发中,过一段时间后,你可以标准化你自己的代码。我的意思是,在你编码经验的开始,你尝试一些东西,看看会发生什么;但是过一会儿,在某些情况下,你会开始使用相同的代码行,你只是改变参数。在这些情况下,你最好创建一些函数,并把它们作为模块导入;所以:

  • 如果你有一些重复的代码行,创建函数,将它们存储在一个单独的文件中,并作为一个模块导入。例如,我发现自己使用相同的代码行来绘制一些图形,我创建了一个 Python 文件:如果你想使用它,你可以在这里找到它。然后,我将它作为一个模块导入到我的项目中;你可以在我的项目这里看到一个例子。如您所见,我已经用我的自定义绘图函数为该文件创建了一个子文件夹,并将它作为一个模块导入到笔记本的开头。
  • 标准化你的子文件夹。如果必须存储地块的图像,请创建一个名为“plots_images”的子文件夹,并在将来的项目中始终使用该名称。

5.持续/自律(Shitsuke)

在制造业中,“ Shitsuke 或 sustain 是由工人自律开发的流程”,(参考。[1]).这意味着另一个“4S”的工作必须由所有工人来维持,但维持所有工人意味着纪律、知识共享和工人培训。

这个概念可以很容易地扩展到软件开发。如果你喜欢干净和可重用的代码(你可能喜欢!),与你的同事分享这种方法,并培训你的所有同事,将在短期内带来巨大的成果。此外,我知道这甚至意味着自律:当一项工作完成后,不容易再“在你的羽翼下”接受它,并检查代码方面可能的改进;但是,相信我:短期内你会看到巨大的进步。

结论

精益制造中的“5S”方法与术语“持续改进”相关联,现在你可以理解为什么了;这种工作要一直持续下去并不容易,甚至设定更高的标准来持续改进也不容易;但是,请相信我,这种方法确实可以在代码方面,甚至在时间和项目管理方面帮助您的组织。即使在短期内,你也会看到巨大的成果!

参考文献:

[1]每个“5S”的所有定义都取自维基百科这里

我们一起连线吧!

中等

LINKEDIN(给我发送连接请求)

如果你愿意,你可以 订阅我的邮件列表 这样你就可以一直保持更新了!

考虑成为会员:你可以免费支持我和其他像我一样的作家。点击 这里 成为会员。

我从数据分析师的工作中学到的人生经验

原文:https://towardsdatascience.com/life-lessons-i-learned-from-working-as-a-data-analyst-fbafe6d02fb8

Banyuwangi 的 Teluk Hijau(图片由作者提供)

它从跟随好奇的头脑开始

研究表明,普通的专业人士在他们的整个职业生涯中花费大约 80,000 小时在工作上。从周一到周五,我们(至少)有四分之一的时间在工作。花了这么多时间,难怪我们的工作在很大程度上影响了我们的生活。就我个人而言,我觉得我的世界观和心态变得越来越分析型

这是因为我已经做了 5 年多的数据分析师。这些年来,我意识到分析原理和经验对我们的生活是多么适用。

以下是我从分析工作中学到并应用于生活的人生经验。

我从数据分析师的工作中学到的人生经验

1.让好奇心引导你

在许多方面,数据分析就像是侦探的工作。我们有一些犯罪现场/证据/演员的证词(或表格中的一堆数据,对于数据分析师来说)和谜团(商业问题对于数据分析师来说)要解决。

我们如何解决它们?

在使用任何领域和技术知识来解决问题之前,你首先跟随你的好奇心去探索数据。你开始挖掘数据,并试图找到一些模式,现象,或数据集下的异常。在探索过程中,您会遇到许多问题:“有没有更好的方法来分割这个数据集?”、“这些特征可以组合在一起获得更好的模型分类吗?”,“为什么这些客户群更容易进行实验?”。带着这些问题,你最终 提出更多的假设、实验、分析或统计测试来提供更好的见解

同样在生活中,保持一颗好奇的心会让你意识到你的周围(还有你内心的想法和感受!)并最终理解它们。你会发现更多的事实,因为你乐于寻找好奇的头脑所提出的答案。

人生经验 :好奇心驱使你问更多的问题,探索更多的途径,这更有可能带来更好的答案和解决方案。

照片由 Andres SiimonUnsplash 拍摄

2.没有确切的对错,只是从多个角度来看这个问题

在分析中,我们总是在寻找分割数据的方法,以揭示有趣的见解。有时你向下钻,并没有产生任何有价值的见解,但当你改变观点时,模式开始显示自己 。

有一次,我在探索营销活动对交易的影响。当我比较周与周之间的交易时,总交易量没有显著增加。这令人惊讶,因为该活动的覆盖面相当高,在过去的活动中,我们看到了显著的增长。所以我继续挖掘数据,试图找出这场运动中的不同之处。当我调查购买客户时,发现有很大一部分新客户来自此次活动,他们的购买量较小,因为他们仍在适应该平台。所以这不是一场失败的竞选(仅仅因为它没有显著增加交易)。这是一次成功的活动,因为通过这次活动,我们获得了潜在高 LTV 的新客户。

人生经验 :一个故事总有多个角度。仅仅因为它现在在我们看来很糟糕,并不意味着它就是糟糕的。从另一个人的角度来看,它可能是好的,或者可能有另一个我们仍然不知道的潜在故事。

Stephen Kraakmo 在 Unsplash 上拍摄的照片

3.学习和适应才能赢

如果你在一个高速发展的组织中工作,你可能已经理解了分析角色的动态性。随着市场以及最终产品/数据需求的不断变化,您别无选择,只能适应这些动态变化

现在正确的分析见解可能在几个月甚至几周内就过时了。由于这些动态,您可能需要在一段时间后重新运行一些分析。可能会有新添加的上下文、数据点或外部市场变动,这些会改变用户行为,并推动分析向不同的方向发展。我们只需要保持开放的心态,探索这些可能性。通常会发现后一种分析的结果与前一种分析不同,即使两种分析都试图解决相同的问题。

人生的教训 :改变是不可避免的。对变化保持开放的心态,学习新的课程,适应成长。保持你坚定的观点,因为现在是正确的,由于这些变化,以后可能就不正确了。

罗斯·芬登在 Unsplash 上的照片

4.简单的概念做对了会带你走得更远

数据分析/数据科学是一个快速发展的领域。这一领域正在(和将要)进行大量的研究。有许多新的、有趣的方法、算法、技术、工具等可供使用——探索起来可能会让人不知所措。但是不用担心,一路上我发现大多数新发现只是基础的延伸。一旦我们理解了基本原理,掌握新技术就容易多了。

此外,许多工业问题(我们在工作中做的项目)并不总是需要复杂或尖端的技术解决方案。在我的分析任期内,我做的大多数数据建模项目都是简单的分类、回归和聚类问题。到目前为止,这些概念,加上正确的上下文、应用和实现,已经帮助我为我的组织(无论大小)设计了分析解决方案。嗯,尖端技术也是这些概念的一个很好的补充,但这 3 个概念是我工作中的帕累托原则——它们帮我解决了至少 80%的问题。

人生经验教训 :学习新事物时,要花时间和精力去了解基本概念和基础。这些概念,通过正确的实践,可以用来解决很多问题,它也将帮助你理解新概念。

绿色变色龙Unsplash 上拍摄的照片

5.关注过程,而不是(仅仅)结果

数据分析是一项发现工作。美国分析师对探索数据充满好奇,以多种方式对数据进行分割,以挖掘出有价值的见解。有时我们会在旅途中迷失,因为我们似乎无法找到我们正在寻找的“非凡见解”来回答业务问题。这种以结果为导向的心态有时会导致失望和一种无法实现期望的不安感;忘记旅程本身。

我已经接受了这一点,并分享了一篇关于我们在分析中没有发现有趣或有价值的见解是多么常见的文章,这没关系。这没什么,因为“不太有趣的见解”仍然是有价值的,因为“价值”是一个主观的问题。尽管分析结果是已经预料到的,并不是新的,但只要分析是在正确的过程中完成的,你仍然在确认它或增加对假设陈述的信心水平中传递价值。更不用说,在分析中可能会发现额外的洞见,这些洞见可能不会回答主要的假设,但可能对以后的其他用例有用。

人生经验:旅程的每一步——不仅仅是终点——都很重要。正确的过程最有可能给你带来好的结果。但是,即使你认为现在的结果对你不好,它也可能对其他人有好处;或者对未来有价值。

Benjamin Wedemeyer 在 Unsplash 上拍摄的照片

最后的想法

当我们花时间反思和留意我们的工作和行为时,我们可以收集和获得经验教训和智慧的珍珠。以上 5 点是我在理解分析工作的过程中总结的经验,谨记在心,并就如何将数据工作应用到生活中给出额外的观点。

数学之光 ML:矩阵分解直观指南(第 1 部分)

原文:https://towardsdatascience.com/light-on-math-ml-intuitive-guide-to-matrix-factorization-bee5af0c01aa

你永远不会害怕在你的生活中看到一个据称令人生畏的矩阵分解方程!

我要让矩阵分解变得像这个巧克力棒一样甜蜜(图片来自 PixabayWikimediaImages

矩阵分解码:此处

在本文中,您将了解矩阵分解,这是许多经典机器学习方法的基础。本文将重点解释矩阵分解(MF)的实际应用(带有代码示例)以及支持它的直觉。你有没有想过矩阵分解可能被用于药物再利用

如果你和我一样(希望不是),在这篇文章结束时,你会后悔在你的本科代数课程中淡化矩阵分解,因为你从未想过你会用到它。

要访问本系列中我以前的文章,请使用以下信件。

ABCD* E F G H I JKL*** MNO P Q******

序幕

我在大学时,数学从来都不是我的强项。我从来不明白,为什么曲线给定点处的切线角度很重要,或者为什么曲线上的积分导致曲线下的表面积很重要,或者为什么矩阵分解很重要?当然,我们在数字领域看到的许多数据都可以用矩阵(或一般的张量)来表示。但那对我来说不再有意义了。像这样的问题,如果你已经有了完整的基本事实矩阵,你为什么要把它分成两个?在代数课上困扰着我。

我大学时代的噩梦(图片由作者提供)

然后是研究生学习。我正在攻读深度学习的博士学位,这是一场忽略矩阵分解等主题的完美风暴,因为这是深度学习文献中很少出现的东西。

目前,作为一名从事推荐算法工作的 MLE,我立刻有了迫切需要的顿悟。“啊哈,这就是为什么你把矩阵分解成两个更小的矩阵”。

回首 10 年前那个天真的本科生,我对自己没有给予更多的关注感到深深的遗憾。为了补偿我自己,我将向你展示很酷的矩阵分解和它的概念的实际应用。我希望这将是那些可能在淹没数学和粗糙代数的沉船中的人的灯塔。

为什么是矩阵?

早在矩阵分解出现之前…矩阵。我们在数据科学中看到和使用的许多数据都可以用矩阵来表示。这里有几个例子。

  • 🖼灰度图像是按行(高度)和列(宽度)组织的像素矩阵
  • 📹视频可以表示为矩阵,其行显示帧数,列是展开为 1D 向量的图像
  • 文档的语料库可以表示为矩阵,其中行是文档,列是语料库中存在的所有术语(即词汇)
  • 👟项目评级(如电影或产品)可以表示为具有行(每个用户一个)和列(每个项目一个)的矩阵。

我希望这足以令人信服地理解我们每天遇到的数据中矩阵的广泛传播性质。

我们周围的矩阵——我们处理的几乎所有数据都是矩阵(图片由作者提供)

为什么要矩阵分解?

现在我们已经建立了矩阵没有缺席,我们的下一个目标是理解讨论中最重要的问题。

为什么我们需要分解(即因式分解)矩阵?

我无法给出一个通用的答案,因为 MF 的具体优势通常取决于应用程序和算法本身。所以让我激励我们如下。

我上面概述的这些矩阵并不十分有用。换句话说,把它们想象成数据中底层模式之上的一层——这是我们通常感兴趣去发现的。就像你切割和打磨钻石一样🔹从地球上挖掘出来以增加它的吸引力和价值,这些观察到的矩阵需要以特定的方式被操纵以到达它的甜蜜中心。矩阵分解是这个旅程的容器。

我们观察到的数据矩阵包含生成这些数据的源的模式/行为

更详细地说,通常数据都有内在的模式。例如,如果图像的一部分被遮挡,您仍然可以使用周围的信息来推断缺失的内容。另一个例子是,如果文档中的一些单词被修改/删除,你仍然可以理解该文档所传达的意思或信息。

为什么可以这样做?这是因为我们看到的数据中有潜在的模式。图像上的像素不是随机的。文档中的文本不是随机的。通过矩阵分解,我们正试图通过我们观察到的数据/噪音来揭示这种模式。你可能会问,为什么学习模式很重要?获得这些模式是我们开发的决策支持系统的生命保障。毕竟,学习模式是机器学习模型的工作。

MF 是怎么做到的?

将数据分成多个部分来揭示数据中的基本模式,这听起来有点不可思议。事实证明,将数据投射到一个潜在空间会迫使生成的矩阵学习模式,分离出噪音。

有许多不同的 MF 算法,它们利用所得的分解矩阵的各种性质。有些可能会将值约束为非负,而有些则强制稀疏。因此,你使用的算法将决定你得到的结果的性质。在我们去它的中心的旅途中,我们会看到相当多的这样的东西!

给我看看已经很酷的东西:MF 的应用

现在你可能厌倦了听我没完没了地谈论 MF。是时候看看这些算法是如何运行的了。

图像压缩

我们看到的第一个例子是图像压缩。矩阵分解可用于存储稍后重建图像所需的重要图像内容(占用的内存更少)。在这里,我们将学习一种叫做奇异值分解(SVD)的技术。SVD 背后的思想是用三个矩阵的乘法来表示矩阵AUσV

这里 A 是n x m,U 是n x n,σ是n x m,V 是m x m

矩阵σ是对角线矩阵,包含对角线上的奇异值,是 SVD 的重要副产品。这些奇异值表示 UV 的每一行/列捕获了多少方差(或奇异向量)。捕捉到的差异越多,重建就越好。你可能认识到这与 PCA 背后的原理相同。

奇异值的平方与每个奇异向量捕获的信息量(即方差)成比例

另一件很棒的事情是奇异值是按降序排列的。这意味着要得到最重要的奇异值,你只需将矩阵切割成包含那么多奇异值。

SVD 如何用于图像压缩(图片由作者提供)

奇异值分解有不同的变体。只得到最大奇异值而不计算全分解称为截断奇异值分解。一种更有效的近似变体称为随机化 SVD 。这是我们用不同数量的随机奇异值分解得到的结果。

使用不同数量的奇异分量进行图像压缩(图片由作者提供)

下面是在scikit-learn中的实现。

让我们为512x512图像计算k=50的压缩率。

  • 原始图像= 512x512 = 262144 像素
  • 重建= 512x10 + 10x10 + 10x512 = 10340 像素(仅为原始图像的 4%)

不错!我们刚刚重建了一个近似值,它只占用了原来的 4%的内存。你可以在这里找到完整的端到端代码。

前景检测

接下来,我们进行前景检测。如果你认为压缩图像很酷,等着看这个吧!你可以用矩阵分解法来区分背景和前景🤯插入心灵爆炸 GIF** 🤯。想想在视频监控中你能做的惊人的事情。你可以从视频中检测到人或车。让我们看看如何做到这一点。****

我们将使用的视频(来自http://backgroundmodelschallenge.eu/

首先,我们将视频表示为大小为l x f的矩阵,其中l是单帧的长度(即展开为 1D 矢量的灰度帧的高度和宽度),而f是视频中的帧数。在我们的例子中,我们有一个大小为76800 x 794的矩阵(每个图像是 320x240=76800 像素)。

将视频作为矩阵加载

我们可以画出来,看看它是什么样子。

以矩阵形式组织的视频中的帧—这是实际矩阵的转置版本(即轴翻转),以更好地利用空间(图片由作者提供)

你看到的曲线是视频中人物的动作。对于这项任务,我们将使用一种不同的矩阵分解技术,称为鲁棒 PCA (rPCA)。想法是分解一个给定的矩阵 M 如下。

这里, L 是低秩近似, S 是稀疏矩阵。霍勒普。🛑,你刚才不是说你不会用令人麻木的数学来恐吓我们吗?让我们直观地理解这意味着什么。

**矩阵的秩称为线性无关列的个数。如果一列不能作为矩阵中其他列的线性变换导出,则该列是线性无关的。为什么这很重要?因为矩阵中的线性相关列越多,冗余信息就越多——因为它们是从独立的列中得到的。如果考虑来自 CCTV 摄像机的视频馈送,背景是静态的,因此包含非常少的信息(或低熵)。因此,我们可以用少量线性无关的列来表示背景。换句话说,如果我将 M 的内容表示为一个低秩矩阵 L ,我将在其中捕获 M 中存在的背景。

附注:在奇异值分解中,非零奇异值的数量代表矩阵的秩。

低秩矩阵的一个例子是表示为矩阵的视频馈送的静态背景

稀疏矩阵呢。那个更有意义。在视频馈送中,静态背景将包含大部分数据(数据,而不是信息)。剩余的稀疏信息属于前景,因为前景通常在视频中占据很小的空间。因此,如果我试图迫使 M 成为稀疏矩阵 S ,我可能会在 S 中捕捉前景运动。另一种思考方式是, S 捕捉数据中的异常值!

现在,将 L 和 S 相加应该可以得到原始视频,这就是鲁棒 PCA 方程的含义。现在说起来容易做起来难!我们如何确保这些矩阵的这些性质。这超出了本文的范围。但是为了给你一点提示,你可以使用像主成分追踪或者交替方向法这样的算法。对于 rPCA,我使用并修改了最初在找到的代码。

在高层次上,让我们了解如何针对这些特殊属性进行优化。

为了最小化 L 的秩,您可以使用核范数L,它是秩的代理。另一方面,如果你从事过线性回归,你可能记得 L1 范数鼓励权重矩阵的稀疏性。他们在这里使用相同的原理,使 S 成为稀疏矩阵。

看原著第 250 帧frames_tLS给了我们以下三个支线剧情图(按顺序)。

这是视频。

为所有帧提取的前景(图片由作者提供)

完整的端到端代码可在这里获得。

很酷的把戏!让我们测试一下我们的知识

到目前为止,我们已经学习了两种技术;SVD 和 rPCA。我们还了解到,在以矩阵组织的视频中,低等级的组件捕获背景。

现在,在奇异值分解中有一个有趣的性质,秩等于非零奇异值的个数。那么,如果我们用很少的分量执行截断 SVD(在我们的例子中是随机化 SVD)并重建图像,会发生什么呢?

我们应该能分离出背景。然后提取前景是琐碎的。只需从原始图像像素中减去背景像素。

SVD 前景检测(图片由作者提供)

看起来不错。我们可以看到,增加组件的数量会使重建的前景更加广阔。这是直觉多么强大的一个很好的例子,它让你自信而有效地结合不同的技术来得到你需要的东西!

但是我们为什么需要 rPCA 呢?这导致了更多的技术细节,超出了本介绍的范围。简而言之,不同的算法有不同的优缺点。它们并不总是在每一个用例中得到强调。我可以指出的一个明显的区别是,rPCA 对于数据中的异常值更健壮。

现在再见

这里我要结束我们第一部分的讨论。我们首先理解了为什么矩阵是重要的,这也解释了为什么矩阵分解是重要的。然后我们讨论了计算机视觉中的两个应用;图像压缩和前景检测。我们讨论了两种算法,SVD 和 rPCA,甚至做了一个很酷的技巧,将 SVD 用于前景检测。

在下一篇文章中,我们将探索机器学习领域的其他部分,矩阵分解已经统治了这个领域!我们会讨论,

  • 自然语言处理中如何使用矩阵分解来学习概念
  • 矩阵分解如何用于项目推荐

在那之前,就再见了!

下一篇文章

[TBA]第 2 部分—矩阵分解

进一步阅读

[1] fast.ai 的线性代数课程——非常全面地介绍了矩阵分解和 Python 中的大量应用

[2] 奇异值分解

[3] 奇异值分解和主成分分析的相似性

[4] 健壮的 PCA

MLOps 的轻量级介绍

原文:https://towardsdatascience.com/lightweight-introduction-to-mlops-a77406cfcc68

MLOps 之旅如何开始以及从哪里开始—基本构建模块

克里斯蒂娜@ wocintechchat.com 在 Unsplash 上的照片

  1. 简介

你可能听说过 90%的 ML 车型不投产。实际上,任何 IT 从业者都知道,将任何软件投入生产是一个漫长而复杂的过程,并且本身就是一个挑战。然而,多年来,自从人们开始编写第一个 if-子句,过程、开发方式、部署和服务的持续改进就出现了。这导致了所谓的开发运维流程和工具的建立。如今,几乎每一家开发严肃软件的公司都采用了这些技术,不管是在游戏、制造、银行还是医疗行业。现在有成百上千的网页和文章在讨论这个话题。

然而,最近几年,一组新的软件类型出现在世人面前,即基于人工智能的系统。他们使用一种截然不同的方法来解决问题——他们基于统计学、概率以及最重要的大量数据。这产生了一系列新的挑战,无法用标准的 DevOps 方法有效解决(因为流程有些不同)。许多尝试过的公司都失败了。

因为这是一个更复杂、更具挑战性的领域,而且 IT 界已经认可了一个新的专业领域——MLOps。不幸的是,这仍然是一个非常年轻的职业——通过检查谷歌趋势中“MLOps”和“DevOps”的流行短语可以很容易地看出这一点。大约从 2019 年开始,它基本上不存在了。

蓝色——devo PS,红色——mlop;图片作者来自谷歌趋势

正因为如此,没有太多的定义、严格的规则或经过验证的方法可以轻易采用。每一个基于人工智能的公司仍然在实验,并努力寻找最佳的方法来解决有效创建和部署人工智能系统的问题。然而,如果你喜欢这些定义,你可以在谷歌云平台的 MLOps 网站上找到:

MLOps 是一种 ML 工程文化和实践,旨在统一 ML 系统开发(Dev)和 ML 系统运营(Ops)。

因此,这不仅仅是关于更快的模型开发,而是关于全局和优化流程。这就是为什么写这篇文章的原因——给你一个关于 MLOps 领域的简单、非正式的介绍。这绝不是限制性的,它强调了我的个人经验,并以有关该主题的一些研究为依据,应该被视为任何考虑在其公司中引入 MLOps 或作为专业人员进入 MLOps 领域的人的思考材料。

2。MLOps 挑战

首先,是什么挑战使 MLOps 不同于 DevOps?您应该将 MLOps 看作是 DevOps 的扩展版本,因为它处理相同的问题以及一些额外的问题。

MLOps 与 DevOps 的范围;作者图片

让我们来看一些挑战:

  1. 首先,ML 模型严重依赖于统计和概率。它们的内部参数不是由开发人员(称为 ML 工程师或数据科学家)直接设置的,而是通过设置控制算法行为的所谓超参数来间接设置的。
  2. 系统的输入是灵活的,不受控制的。我的意思是,系统的内部行为是基于历史数据优化的(ML/Dev 阶段),但在部署之后,它作用于实时数据(Ops 阶段)。例如,如果客户的行为发生变化,系统仍将遵循它根据旧数据学习到的旧决策模式。这将导致其价值快速贬值。其背后的过程被称为数据漂移,是基于人工智能的系统的 Ops 阶段的最大挑战之一。让我们举一个例子——一个披萨订购系统,您订购了一份意大利香肠披萨,收到了一份夏威夷披萨——您可以在代码中轻松地映射和跟踪问题并修复它。在 ML 系统中,这并不容易做到。
  3. 另一个挑战往往来自开发者的背景和教育。因为 ML 系统是基于像高度先进的线性代数,贝叶斯统计和概率学,ML 专家在他们的教育中有不同的焦点。与典型的前端工程师相比(他们多久使用一次矩阵分解?).在实践中,这意味着在开发过程中,他们经常使用隐藏一些复杂的软件相关细节的框架,以利于易用性(例如 Keras,sklearn)。这些框架和库处于不断的发展和变化中,或者很可能出现新版本的 ML 算法(例如,在变形金刚领域的发展)。简而言之,ML 工程师:
  • 并不总是能够完全、精确地控制他们使用的算法
  • 喜欢并且必须尝试新的算法和方法
  • 比传统软件开发人员更擅长数学

如你所见,总的来说,问题在于数据和算法的灵活性。这是它最大的优点,也是最大的缺点。

3。实施 MLOps

MLOps 旨在以有组织的方式控制开发和生产问题。为了实现这一点,需要采用一些重要的功能构件,如下图所示。根据行业或公司的具体情况,可能会有更多,但这些通常在各种使用案例中都很常见。

MLOps 的基本、顶级组件;作者图片

让我们简短地谈论一下他们中的每一个。

  1. 特征工程是关于 ETL 管道及其版本控制的自动化。理想情况下,你会有一个特色商店的风格。如果你不熟悉这个概念,请查看这个网站。市场上提供的一些工具:Databricks Feature Store、Feast、Tecton。
  2. 实验跟踪是一个非常重要的组成部分,因为它涉及到 ML 工程师的两个实验——成功的和失败的。它允许在时机成熟时重温一些以前的想法(如不同的算法或特性),而无需重新发明轮子。在成熟的 ML 系统中,还有一种方法来捕获一组超参数(过去的和当前的)和相应的系统质量 KPI——通常这被称为模型注册表(像 MLflow、Neptune 或 Weight & Biases 这样的工具)。
  3. 管道管理允许您对控制从输入到输出的数据流的管道进行版本控制。它还应该记录每次运行,如果发生了不好的事情,就会产生一个有意义的错误。这里来看看:Vertex AI Pipelines,Kedro,PipelineX,Apache Airflow。
  4. 计算管理解决了 ML 系统的可扩展性问题。一些算法在训练和再训练时需要巨大的计算能力,而在推理时几乎不需要。由于这两项任务通常通过反馈控制回路联系在一起,因此系统必须能够上下伸缩。有时,像 GPU 这样的额外资源必须用于训练,而推理部分并不需要。公共云提供商很好地解决了这个问题,提供了自动扩展和负载平衡。
  5. 模型 CI/CD 与 DevOps 区域的 CI/CD 非常相似,但是在部署模型之前必须进行额外的检查。这些是选择的性能指标,必须在可接受的范围内,并始终与生产中的当前模型进行比较。这里最受欢迎的工具之一是 Jenkins 和 Travis,但还有很多其他工具,如 TeamCity 或 Circle CI。
  6. 漂移检测是一个监控输入数据特征和系统行为的模块。当传入数据的特征偏离预期范围时,应发出适当的警报,以便请求对模型进行重新训练(自动或手动)。如果这没有帮助,警报应该升级,开发团队应该更深入地研究这个问题。要考虑的工具/服务:AWS SageMaker Model Monitor,Arize,显然还有 AI。

MLOps 环境的典型工具:作者图片

当向您的组织介绍 MLOps 时,尤其是如果它曾经或现在仍在开发软件,您必须非常小心并意识到开发人员的偏见。许多不熟悉人工智能领域的人将倾向于成熟的 DevOps 解决方案,并将推动它们。这就是为什么在许多公司中,基于 ML 的系统是由专门的团队或部门开发的。

4。总结

正如您所看到的,这篇文章是介绍性的和一般性的,并没有为您强加任何特定的解决方案。这是因为不同的公司有自己需要自动化的内部流程和特定的挑战,可能需要不同的方法和工具来找到最佳的解决方案。尽管如此,还是有一些很棒的关于 MLOps 的资料是用一组特定的工具编写的。我强烈推荐,例如:

可能性、概率和你应该知道的数学

原文:https://towardsdatascience.com/likelihood-probability-and-the-math-you-should-know-9bf66db5241b

可能性在机器学习中起什么作用?

Saad AhmadUnsplash 上拍摄的照片

可能性是一个令人困惑的术语。可能性不是一种概率,而是与一种概率成正比;这两个术语不能互换使用。在这篇文章中,我们将剖析可能性这个概念,并理解它在机器学习中的重要性。

直觉

让我们来理解可能性,以及它与一个虚构的城市 Databerg(一个令人生厌的名字,但请原谅我)的概率分布有什么不同。让我们也想象一下,我们可以访问这个城市所有房屋的定价数据。我不知道这种分布到底是怎样的,因为数据堡不是一个真正的城市,但凭直觉,我会说我们会注意到许多价格适中的房子和一些非常昂贵的房子。如果要绘制这些价格的分布图,可能会是这样的。

图 1

连续房价值沿 x 轴绘制,概率值沿 y 轴绘制。图 1 因此代表一个概率密度函数概率密度函数这个短语在谈论连续值,比如房价时,经常与概率分布函数互换使用。所以在整篇文章中,我们将使用更相关的短语概率分布函数来表示这两者。但是,当其他来源使用短语概率密度函数谈论同一概念时,不要感到困惑。

因为我们处理的是概率分布函数,所以橙色曲线下的面积是 1。这意味着,如果我们在数据堡随机选择一所房子,这个价格是某个正实数的概率是 100%;这是有道理的。我们还可以确定房价位于两个价格点之间的概率。考虑下图。

图 2

假设红色条纹部分下面的面积是 0.45 。然后我们可以做如下陈述。

给定这种房价分布,一栋房子定价在 60 万美元到 80 万美元之间的概率是 0.45。

现在我们有了概率的概念,让我们颠倒一下我们的设置来讨论可能性。不给出 Databerg 中的所有房屋信息(因此给出如图 1 所示的房价概率分布函数),让我们假设只给出 10,000 套房屋的价格。我们现在的目标是确定(或至少近似)图 1 中的概率分布函数。

图 3

靠近 x 轴的每个点都是一座房子。当前分布与该数据的吻合程度通过可能性来量化。假设上面的分布是对数正态分布,均值 13.2 ,标准差 0.4 。并且在给定房屋样本上这种分布的可能性是 1.78 。然后我们可以做如下陈述:

给定这 10,000 栋房屋,分布平均值的可能性为 13.2,标准差为 0.4,为 1.78。

请注意,可能性的值可能大于 1,因此它不是一个概率密度函数。事实上,与相同数据的其他分布的可能性相比,可能性的 1.78 值更有意义。

图 4

图 4 中,每个虚线分布是通过改变对数正态分布的均值和标准差得到的。对于每个分布,我们可以确定可能性(量化分布与 10,000 数据点的拟合程度)。现在让我们继续讨论可能性的定义。

定义可能性

关于理论统计的数学基础的书给了我们一个关于可能性的清晰定义。

任何参数(或一组参数)应该具有任何指定值(或一组值)的可能性与这样的概率成比例,即如果是这样的话,观察的总体应该是被观察的

组参数是分配的参数。从图 3 来看,我们假设的房价分布是对数正态分布。这个分布的参数是平均值和标准差。分配的一组值是平均值的 13.2 和标准差的 0.4观察就是房价。有了这些信息,我们可以把这个定义转化成数学。

等式 1

左手边的 L似然函数。它是概率密度函数参数的函数。右手边的 P 是条件联合概率分布函数。这是在我们假设的分布下,我们观察到的每栋房子的价格的概率。可能性与这个概率成正比,不一定等于这个概率。

可能性和机器学习

参数模型中,如线性回归和逻辑回归,我们得到一组数据点,目标是找到最适合观察数据的这些模型的参数。让我们考虑一下我们在上一节中介绍的同一个房价例子。我们希望拟合一些统计模型来预测房屋价格,给出一些关于房屋的信息,如房屋中卧室的数量、房屋的面积(平方英尺)以及房屋的年龄。

图 5

这是模型中的 3 个特征,但在实践中还可以有更多。让我们假设我们想要执行一个线性回归。由于这种假设,输入要素和输出标注以下列方式相关联。

等式 2

人们可以用下面的形式更一般地写它。

等式 3

x 项是 i ᵗʰ房子的特征,yi ᵗʰ房子的价格,𝜃项是各特征的系数,ε𝜖表示不可约误差。这是由固有的系统随机性引起的误差,也是因为某些特征没有被考虑在内而发生的。

为了构建这个线性回归模型,我们需要知道𝜃项的值。为了找到𝜃条件,我们需要房屋特征及其价格的例子。也就是说,我们需要成对的 ( 𝑥,𝑦 ) 来填充等式 3 中的值,以估计𝜃项。这就是为什么我们需要训练数据。

还记得我们想象中的城市数据库吗?让我们添加细节,使这些数据对训练模型有用。我们可以访问 Databerg 中的 10,000 份房屋记录。每个记录都有关于房子的信息:卧室的数量;房子的面积(平方英尺);房子的年龄;以及这栋房子的评估价格。既然我们要预测房子的价格,标签 y 就是这个价格。这个假想数据集中的其他字段是特征 x ,它们是我们预测相应价格的线性回归的输入。这些训练数据如下表所示。一套房子是75.7 万美元,第二套房子是78 万美元,第三套房子是68 万美元等等

图 6

如果我们假设𝜃项的值,我们可以使用似然函数量化线性回归模型拟合训练数据的程度。最后,我们要确定最适合给定数据的𝜃项;换句话说,我们想要确定使似然函数最大化的𝜃项的值。这被翻译成数学如下。

等式 4

我们将很快解释这些术语。但在此之前,让我们通过用矢量形式表示所有𝜃项来摆脱这种繁琐的符号。

等式 5

现在,方程 4 可以写成更一般更简洁的形式。

等式 6

这个符号告诉我们一些事情:似然函数 L 是模型参数𝜃的函数; arg max 函数返回使这个似然函数 L 最大化的𝜃的值。根据定义,𝜃的这个值就是𝜃.的最大似然估计为了区分在右边使用的变量𝜃和我们在左边寻找的𝜃的特定值,我们给后者加上一个 MLE 上标。此外,𝜃最大似然估计表明这个值只是一个估计值。

𝜃的最大似然估计

根据我们对似然性的定义,似然函数与观察到的训练数据的联合概率分布成比例。

等式 7

我们在机器学习中倾向于做出的一个假设是,这些房屋的价格不会相互影响。用数学语言来说,每个房子样本都是独立同分布或 I . I . d;这个假设在很大程度上是合理的。在数学上,这意味着联合概率 P 现在可以表示为个体概率分布 p 的乘积。

等式 8

让我们把这个符号和产品符号联系起来;并且使用 n 作为样本数量,而不是我们选择的任意数量的 10,000 个训练样本。

等式 9

从技术上来说,对于每一个房屋样本,这些房价标签 y 都是在给定房屋的其他特征(卧室数量、平方英尺、&年龄)时计算出来的。让我们用条件概率分布符号来说明这一点。

等式 10

就像我们对𝜃做的那样,让我们用向量符号使 x 更紧凑。

等式 11

你可以把 xᵢ向量中的 1 想象成线性回归方程 3 中常数项的系数。我们现在可以更清楚地写出如下的似然函数。

等式 12

右边的每一项都是介于 0 和 1 之间的概率。即使是合理的数据集,这些样本的乘积也将接近于 0。机器学习是用计算机完成的;计算机无法以适当的精度计算这个乘积;这种情况是算术下溢。为了解决这个问题,我们最大化这个似然函数的对数;这是通过将乘积项转换为对数之和来实现的。

等式 13

这个公式利用了对数的一个重要性质:乘积的对数与它们各个部分的对数之和相同。最后,我们感兴趣的是使原始似然函数最大化的参数𝜃的值,而不是似然函数本身的值。最大化原始似然函数的𝜃向量的值与最大化对数似然函数的𝜃向量的值相同。我们可以写出下面的形式。

等式 14

这是因为对数是单调递增的。也就是说,如果一个数大于另一个数,对数的行为类似。所以最大化似然函数的参数值将最大化其对数。此外,我们能够写出等式 14 中的最后一行,因为我们知道对数似然和概率值之和彼此成比例(等式 13) 。因此,它们共享相同的最大值。让我们重写我们需要最大化以获得模型参数的最终似然方程。

等式 15

我们如何求解这个似然函数取决于机器学习模型的类型。例如,在线性回归中,假设给定特征 x 和参数𝜃.,我们将假设标签 y 遵循正态分布下面的参考资料给出了解决简单线性回归的似然估计的证明。完成这一数学运算后,一个有趣的认识是,𝜃的最优值将是最大化残差平方和方程的值,这是机器学习中的一个基本方程。类似地,我们可以使用等式 15 作为逻辑回归等参数分类模型的起点。这里有一段我为逻辑回归计算最大似然估计的视频。

结论

这篇文章从可能性背后的直觉开始。然后,我们正式定义了可能性及其与概率的关系。我们讨论了如何使用最大似然估计来估计最适合训练数据的统计模型的参数。同时,我们引入了数学符号来概括如何对任何参数模型进行最大似然估计。我们通过推导似然方程的一般形式来结束我们的讨论,该方程可用于求解线性回归和逻辑回归等模型中的参数。

谢谢你一直读到最后!更多技术内容,请关注我的 Code Emporium YouTube 频道,我在那里教数据科学和机器学习。

资源

[1] Code Emporium,梯度下降——你应该知道的数学 (2019), YouTube

[2]斯蒂芬·佩蒂格鲁,从模型到对数似然 (2014),宾夕法尼亚大学

[3]徐立言,机器学习:MLE vs 地图 (2021), Just Chillin' Blog

【4】whuber“可能性”和“概率”有什么区别? (2019), stats.stackexchange.

[5]与乔希·斯塔默的 StatQuest,概率不是可能性 (2019), YouTube

[6]马特·博格纳,概率分布小程序 (2016),爱荷华大学

[7] Joram Soch,简单线性回归的最大似然估计 (2021),统计证明手册

【8】user 132704“概率密度函数”和“概率分布函数”有什么区别? (2019), stats.stackexchange

本帖中使用的所有图像和图形均由作者创作。

通过多重处理使用参数化装饰器限制 Python 函数的执行时间

原文:https://towardsdatascience.com/limiting-a-python-functions-execution-time-using-a-decorator-and-multiprocessing-6fcfe01da6f8

限制 Python 函数执行时间的装饰器

丹尼尔·利维斯·佩鲁西 https://unsplash.com/photos/WxmZT3sIe4gT2摄影

在本文中,我将带您创建一个装饰器,通过多重处理来限制 Python 程序中函数的执行时间。我构建这个装饰器的主要动机是用简单的语法和最少的依赖性来限制 Python 函数的执行时间。

一种简单的方法是在 Python 函数中使用一个计时器,定期检查正在执行的 Python 函数是否超出了限制,然后退出。对于简单的一次性解决方案来说,这种方法可能是可行的,但是任何对第三方库的调用都会阻止检查时间限制。

我还想要一个尽可能不引人注目的解决方案,并且可以在整个代码库中轻松应用。装饰者提供了很好的语法和抽象来实现这个目标。

考虑到这一点,我知道我想要创建一个可以附加到我的项目中的任何函数的装饰器。装饰器会将函数的执行时间限制在某个特定的数量。我还想让所有东西都用 Python 编写,以限制添加这种调度程序的依赖性/复杂性。

这样做的主要挑战是

  1. 装饰者应该为最大执行时间取一个参数,使其易于扩展。
  2. 修饰函数能够具有任意的输入/输出。
  3. 即使正在执行的函数调用了第三方库,计时器也应该工作。

首先,我需要创建一个可以接受参数作为自变量的装饰器。经过一些研究,我发现了一个优秀的堆栈溢出线程,人们在那里提出了几种解决方案。

我按照彼得·莫滕森在评论中给出的架构为装饰者创建了一个装饰器。我不会深入讨论这是如何工作的,但是你可以进入这个线程来获得更详细的解释。为了获得更多关于装饰者的信息,我经常去这里复习。

然后,您可以将这个装饰器附加到您想要应用到您的函数的装饰器上,允许您参数化该装饰器。我想创建一个 run_with_timer decorator,它将最大执行时间作为一个参数。看起来是这样的。

接下来,我们可以填充代码来限制执行时间。逻辑如下;主进程将使用 Python 的多重处理在一个单独的进程中运行修饰函数。主进程将设置一个定时器,如果定时器超时,则终止执行该函数的子进程。

设置多重处理的代码由两部分组成。第一个是我称为 function_runner,的函数,它充当在新进程中运行的包装器,处理 Python 函数的运行并返回多处理函数可以处理的结果。第二个是多重处理代码,它产生新的进程,设置一个计时器,如果没有及时完成,就终止产生的进程。

最后,我可以创建函数来包装我的 run_with_timer 装饰器。我叫它睡熊。

当我们运行 sleeping_bear 函数时,如果它超过了装饰参数中设置的时间限制,它就会终止。如果 Python 函数在时间限制之前完成,那么 send_end 处理程序将返回结果。

**sleeping_bear**("Grizzly", hibernation=10)>> Grizzly is going to hibernate
>> 0 zZZ
>> 1 zZZzZZ
>> 2 zZZzZZzZZ
>> 3 zZZzZZzZZzZZ
>> 4 zZZzZZzZZzZZzZZ
>>
>> TimeExceededException: **Exceeded Execution Time****sleeping_bear**("Grizzly", hibernation=2)>> Grizzly is going to hibernate
>> 0 zZZ
>> 1 zZZzZZ
>> 
>> "Grizzly is waking up!"

总之,我已经向您展示了如何创建一个装饰器来限制使用多处理作为调度器的 Python 函数的执行时间。我能够解决三个主要问题。

  1. 创建一个参数化装饰器来限制最大执行时间。
  2. 允许向被包装的 Python 函数输入任意参数。
  3. 通过利用多处理模块中的系统级调度程序来限制任何函数的执行时间。

另外,这一切都是用 Python 完成的,没有第三方依赖。启动多处理会有一些开销,但是我的目标是限制长时间运行的函数,所以这不是一个问题。

如果你喜欢这个,一定要关注我,在未来支持更多这样的内容。感谢您的阅读,一如既往,如果您有任何建议或反馈,请在评论中告诉我。

只有两行代码的线性数据科学工作流程:简化了 MLOps

原文:https://towardsdatascience.com/lineapy-data-science-workflow-in-just-two-lines-mlops-made-easy-679f36ac63bd

卢卡斯·法夫尔在 Unsplash 上的照片

简化的数据工程

简介:

LineaPy 是用于数据科学自动化的 python 包。根据 LineaPy 文件:

LineaPy 是一个 Python 包,用于捕获、分析和自动化数据科学工作流。在高层次上,LineaPy 跟踪代码执行的顺序,以形成对代码及其上下文的全面理解。这种理解允许 LineaPy 提供一套工具,帮助数据科学家更快更容易地将他们的工作投入生产,只需要两行代码。

我看到了他们上周关于 LineaPy 的声明。这源于加州大学伯克利分校的研究,如 Apache Spark,现在是开源的。我尝试了 LineaPy,它看起来非常有趣和有用。如果你想了解更多关于 LineaPy 的信息,请继续阅读。

图片由作者提供(使用 Excalidraw 和 Excalidraw 贴纸)

目录:

1。为什么我们需要 LineaPy?
2。线性安装。
3。概念。
第 4.2 行代码。
5。穿越线性示例。
6。结论

我使用了以下工具来创建图表和代码片段。

->excaldiraw
->git mind
->Gist
->carbon . now . sh

为什么我们需要 LineaPy:

数据科学发展到生产的过程是一个复杂的工程过程。VentureBeat 上的一篇文章称,大约 90%的数据科学项目都无法投入生产。简而言之,十个项目中只有一个能够投入生产。在 Jupyter 笔记本中编写混乱的代码是很容易的,因为你要做大量的 EDA、统计分析、编辑单元格、删除单元格等等。此外,保持笔记本整洁有序非常费时费力。重构数据科学开发代码和构建管道是复杂的、手动的和耗时的。LineaPy 只提供了 2 行代码,用于将开发代码转换为生产代码,并生成所需的管道。

作者图片

线性复制安装:

作者图片

概念:

作者图片

神器:

  • 工件是指数据科学开发过程中的一个中间结果。
  • 在数据科学工作流中,工件可以是模型、图表、统计数据、数据帧或特征函数。
  • LineaPy 将工件视为代码和值。它存储工件的值以及派生工件的必要代码。

作者图片

神器商店:

  • 工件存储在工件存储中。
  • 工件存储还保存工件的元数据,如创建时间和版本等。
  • 任何人都可以在全球范围内访问藏物商店。用户可以在不同的开发会话甚至不同的项目中查看、加载和构建工件。

管道:

  • 管道是指将数据转化为有用信息/产品的一系列步骤。

例如下面是一条管道

作者图片

  • 这些管道是一次开发一个组件,然后将所有组件连接起来,得到整个管道。
  • 在 LineaPy 中,每个组件都被表示为一个工件,并且 LineaPy 提供了从一组工件创建管道的 API。

作者图片

2 行代码:

作者图片

  • 保存您想要保存的工件,如数据帧、变量、模型等。然后调用工件,获取工件的值和代码。**lineapy.save** API 创建线性工件并将它们保存到数据库中。保存工件的代码是
***# Step 1
#Store the variable as an artifact***
saved_artifact = lineapy.save(model, "my_model_name")
  • 该方法需要两个参数:要保存的变量和要保存的字符串名称。它返回保存的工件。
  • **LineaArtifact**该对象有两个关键的 API:
  • **.get_value()**返回工件的值,如整数或数据帧
  • **.get_code()**返回最少的基本代码来创造价值

代码是

***# Step2:Check the value of the artifact***
print(saved_artifact.get_value())***# Check minimal essential code to generate the artifact***
print(saved_artifact.get_code())**#Check the session code**
saved_artifact.get_session_code()**#To check the saved artifacts**
lineapy.catalog()***#Get version info of the retrieved artifact***
desired_version = saved_artifact.version

***# Check the version info***
print(desired_version)
print(type(desired_version))

作者图片

穿越线性示例:

关于数据集:数据集是自动 MPG 数据集,可在

UCI 机器学习知识库
机器学习与智能系统中心

来源:该数据集取自卡内基梅隆大学的 StatLib 图书馆。该数据集被用于 1983 年美国统计协会博览会。

数据集 : Dua,d .和 Graff,C. (2019)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

数据集特性:

1。 mpg: 连续
2。
:多值离散
3。
位移 :连续
4。
马力 :连续
5。
重量 :连续
6。
加速度 :连续
7。
车型年 :多值离散
8。
产地: 多值离散
9。
汽车名称 :字符串(每个实例唯一)

本教程使用经典的 Auto MPG 数据集,并演示如何建立模型来预测 20 世纪 70 年代末和 80 年代初汽车的燃油效率。这是一个经典的回归问题。为此,您将为模型提供该时间段内许多汽车的描述。该描述包括气缸、排量、马力和重量等属性。

在我的例子中遵循的工作流程如下。主要目标是如何保存工件、获取值/代码并生成管道。

  1. 将训练和测试数据加载到 pandas 数据框架中
  2. EDA 和统计分析。
  3. 将最终的训练和测试数据保存为工件——使用 Save()
  4. 检查工件-get()
  5. 使用不同的方法建立模型。
  6. 选择最佳型号。
  7. 将最佳模型保存为工件——save()
  8. 显示工件目录-目录()
  9. 使用保存的工件构建管道。

要在本地练习,请使用 Google Colab。我用了 G oogle Colab。同样,本文的主要目的是演示 LineaPy 包的用法,并且同样不太担心模型的构建或模型的性能。更多细节请查看参考文献中的 Tensorflow 教程。这里的目标是查看线性复制功能。

声明所有必需的包。

作者图片

下载数据集并将数据上传到熊猫数据框架。

作者图片

现在做一些探索性的数据分析和统计分析。

作者图片

现在将训练和测试数据数据帧保存为工件-save()

作者图片

例如

作者图片

工件的类型是

<class ‘lineapy.graph_reader.apis.LineaArtifact’>

显示工件的值和代码:

作者图片

保存的工件序列数据帧和代码的输出。

作者图片

所有的 EDA 代码和不必要的代码都被删除。

作者图片

若要显示原始会话代码,请使用 get。会议

作者图片

上面的 get_session_code 将显示包含所有 eda 代码的整个代码,等等。

现在构建模型并选择最佳模型。您可以使用之前存储的工件来获得预处理的训练和测试数据集。

第一模型:线性回归模型

作者图片

第二款车型——DNN 车型:

作者图片

结果:

作者图片

作者图片

DNN 模型是比线性回归模型更好的模型。我会拯救 DNN 模型。

现在拯救 DNN 模型。

作者图片

要显示模型的代码,使用 get()方法

作者图片

下面是您对模型 _ 工件使用 get_code 时生成的代码。

作者图片

列出所有保存的工件。

作者图片

输出——显示所有存储的工件

train_data:0 created on 2022–05–28 02:07:02.669098 
train_labels:0 created on 2022–05–28 02:07:10.961282 
test_data:0 created on 2022–05–28 02:07:14.520631 
test_labels:0 created on 2022–05–28 02:07:20.777316 
dnn_model:0 created on 2022–05–28 02:15:23.632722

现在,您还可以用您保存的工件构建一个数据管道。生成管道的代码有

  • 获取工件并将其赋给一个变量。

作者图片

现在构建数据管道。

  • 预处理数据。
  • 模型结构

作者图片

  • **artifacts**是用于管道的工件名称列表。这里我们使用火车和模型工件。其实我们不需要测试神器。
  • **pipeline_name**是管道的名称。这里的管道名称是泰坦尼克号管道。
  • **dependencies**是工件之间的依赖图
  • 如果工件 A 依赖于工件 B 和 C,那么图被指定为{ A: { B, C } }
  • 如果 A 依赖于 B and B 依赖于 C,那么这个图被指定为{ A: { B }, B: { C } }
  • **output_dir**是放置运行管道的文件的位置
  • **framework**是要使用的编排框架的名称
  • LineaPy 目前支持**"AIRFLOW"****"SCRIPT"**
  • 如果**"AIRFLOW"**,会生成可以运行气流 DAGs 的文件。您可以在 airflow CLI 中执行该文件。
  • 如果**"SCRIPT"**,它将生成可以作为 Python 脚本运行管道的文件

运行[**lineapy.to_pipeline()**](https://docs.lineapy.org/en/latest/autogen/lineapy.html#lineapy.to_pipeline)会生成几个文件,可以用来从 UI 或 CLI 执行管道。将生成以下文件

作者图片

在这种情况下,管道名称是 titanic_pipeline。

文件被存储

作者图片

作者图片

需求文件是自动生成的

lineapy
matplotlib==3.2.2
numpy==1.21.6
pandas==1.3.5
seaborn==0.11.2
tensorflow==2.8.0
tensorflow.keras==2.8.0

dockerfile 文件是在您构建管道时自动生成的。

作者图片

生成气流 dag 文件

作者图片

包含所有数据的 python 文件

作者图片

有关构建管道的更多信息,请查看文档。

要了解更多关于工件商店的信息,请查阅文档。

请查看以下详细信息。此外,还有一些使用 Github 上 Kaggle 的 iris 数据集和房价预测数据集的例子。我在本地用 Google Colab 试了一下。

结论:

LineaPy 包通过使用 2 行代码(save()、get()和 to_pipelinemethods())肯定会帮助 MLOps 团队自动化工作流。也许它可以作为第一次削减,然后进一步修改可以做。我检查了一下,重构代码看起来不错。另外,docker 和 airflow dag 文件看起来也不错。LineaPy 是开源的,要了解更多信息,请查看他们的 GitHub repo。

请在 Linkedin 上免费连接。

参考文献:

  1. 数据集来源 :Dua,d .和 Graff,C. (2019)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。
  2. 关于直系亲属-https://lineapy.org/why-lineapy/
  3. 线性 Github 回购:https://github.com/LineaLabs/lineapy
  4. 直系亲属艾-【https://linea.ai/】
  5. Keras 教程:https://www.tensorflow.org/tutorials/keras/regression

图论解释的线性代数

原文:https://towardsdatascience.com/linear-algebra-explained-through-graph-theory-1c79711e9a20

用图论直观地解释线性代数的基础

图片来自乔尔·菲利普来自 Unsplash

数学是一门难学的学科:它非常广泛,在许多领域都有广泛的应用。线性代数是数学的一个分支,尤其难以理解和应用。在我看来,许多教授线性代数的课程和开源工具都是计算密集型的,对于像我这样的视觉学习者来说,这可能变得非常难以理解。对于刚开始接触数学或数据的人来说,这可能会有问题,因为线性代数在数据科学和机器学习中有多种应用,仅举几个例子:自然语言处理、推荐系统、维数约简、神经网络等。对线性代数有很强的基础理解将有助于他们理解常用的机器学习方法。

这篇文章将着重于通过使用图形来直观地理解线性代数的计算。我将涵盖许多核心概念,如矩阵加法,减法,乘法,除法,转置等。这些是线性代数中非常基本和常见的概念,当涉及到机器学习时,这些概念也经常出现。以下是文章的提纲:

目录

  • 图和线性代数简介
    -图的邻接矩阵
  • 算术
    -加法/减法
    -乘法/除法
  • 移项
  • 线性组合
  • 线性代数在机器学习中的应用
  • 结束语
  • 资源

图形和线性代数导论

图论和线性代数是结合在一起使用的,有一个完整的数学子类algebraic graph theory使用代数方法来解决关于图的问题。这些代数方法通常包括各种线性代数。

图的邻接矩阵

贯穿本文的核心概念是邻接矩阵。邻接矩阵是一种能够将图 G 转换成矩阵的矩阵,反之亦然。邻接矩阵总是对称的,并且由指示图 g 中相邻顶点对之间的连接的元素组成。图到其关联邻接矩阵的转换可以容易地在下图中说明。

图 G 的邻接矩阵(图片由作者提供)

如果图 G 有任何自循环,那么在上图中矩阵的对角线上会有值。邻接矩阵封装了图的结构和关系。邻接矩阵可以表示为稀疏矩阵或密集矩阵,这使得在图上运行实验的计算效率非常高。出于这个原因,邻接矩阵是表示图的最常见的方式之一[1]。在整篇文章中,我将把线性代数中的基本概念应用到邻接矩阵中,并展示与该矩阵相关的图如何相应地变化。

算术

对大多数人来说,在矩阵上进行运算是非常简单和直观的。理解基于图的算法的最简单的方法是通过加权图。通过线性代数中的一般算法,与这些算法相关的图形可能会发生巨大的变化。可以形成或移除新的边,与这些边相关联的影响将显著增加或减少。这个图可以从一个连通的网络变成一个断开的网络。下面的例子可以让你更好地理解图中的矩阵算法。

加法/减法

矩阵加法通过图形直观地显示出来,正如你所看到的,图形已经合并了具有比以前更高权重的边。(图片由作者提供)

虽然这个例子只涉及加法,但是看减法如何影响图形应该是非常直观简单的。

乘法/除法

此图显示了乘以常数时对图形的影响,很明显,图形的形状没有改变,但边的权重急剧增加。(图片由作者提供)

点积

点积是一个很难直观理解的概念,尤其是当您要相乘的矩阵的维数很大时。在研究下图时,请记住,当两个图进行点积时,它几乎肯定会成为一个有向图。下图显示了两个无向图(邻接矩阵)的点积结果,以及由此产生的有向图。在最简单的形式中,如果一个图不包含重复的边和环,那么它就是无向的。这意味着无向图的邻接矩阵将具有零对角线,并且矩阵 M^T 的转置等价于 m

正如你将看到的,有向图是非常不同的。有向图是由有向边组成的图,这意味着如果有一条有向边通向一个节点,你就只能从该节点到另一个节点。方向通常用边上的箭头表示。

两个矩阵的点积直观地表示出来。(图片由作者提供)

由点积得到的图形完全改变了人们对图形的理解。两个(相对)简单的图形的点积会导致一些非常混乱的东西。如你所见,你可以遍历不同权重的节点。

移项

传统上,当个人陈述转置一个矩阵时,你所做的只是翻转矩阵的对角线。但是这如何影响相关的图表呢?

转置背后最简单的视觉理解是通过看一个有向图。如上所述,当一个节点通过边指向另一个节点时,图 G 是有向的,那么图 G 的转置将简单地指示方向被交换。这可能是对矩阵转置如何影响网络的更直观的理解。

如图所示,对有向网络进行转置只是改变了边现在面对的方向。(图片由作者提供)

线性组合

图形的线性组合与向量的线性组合非常相似。线性组合可以用以下定义来概括:

由一组项和常数组成的表达式。该表达式是每一项乘以其相关常数的乘积之和。它可以用下面的公式进行数学表示:

Constants : a, b, c, ... z
Terms : G1, G2, G3, ... Gn
LC = a*G1 + b*G2 + c*G3 + ... + z*Gn

现在,当在图的上下文中考虑线性组合时,假设每个向量现在是对应于图的对称矩阵。然后,我们可以直观地看到合并网络时每个变化所产生的影响。下面可以直观的看到:

三个不同图形 G1、G2 和 G3(从左至右)乘以常数 a1、a2、a3 (3、2、1)的线性组合及其相应结果。(图片由作者提供)

线性代数在机器学习中的应用

线性代数在机器学习中有多种应用。线性代数出现在数据科学管道的大多数步骤中,从数据预处理和特征工程到建模。诸如一个热编码和各种降维模型的概念基本上源于线性代数。像 PCA 这样的模型直观地使用线性组合和特征向量背后的思想来减少输入数据,同时试图最小化信息丢失。

线性代数在机器学习中更复杂的应用是以推荐系统和深度学习的形式出现的。所有形式的推荐系统都使用各种线性代数来解决所提出的给定问题。在推荐系统中,基于内容、协同过滤和混合的解决问题的方法使用线性代数中的常见概念,如点积、余弦相似性、欧几里德距离、矩阵分解等。甚至像链接预测这样的推荐系统中的复杂方法也基本上使用大量的线性代数来识别网络中的缺失边(网络中的哪些节点对应该具有边,但是当前没有)?).

深度学习本质上是线性代数,从用于定义神经网络的数据结构到训练和测试数据的方法。深度学习中的一个常见术语是tensor,其中张量本质上是一个超过 2 维的矩阵。

结束语

我希望这篇文章能为你提供图论和线性代数背后更多的见解、直觉和联系。这些概念经常出现在各种机器学习和数据科学应用的幕后。对这些概念有一个更加严格和基础的理解将很可能帮助你学习各种机器学习相关的概念。

资源

如果你喜欢这篇文章,这里列出了我写的其他文章,你可能也会喜欢。

https://pub.towardsai.net/dynamic-time-warping-explained-fbb24c1e079b

线性代数——机器学习的生存工具包

原文:https://towardsdatascience.com/linear-algebra-survival-kit-for-machine-learning-94901a62465e

基本原则

线性代数——机器学习的生存工具包

NumPy 中带有示例的最常见概念的快速参考指南

克里斯汀·沃克在 Unsplash 上拍摄的照片

L 线性代数通常被认为是数据的数学和机器学习的基本支柱之一。然而,这是一个广阔的领域,包含着太快掉进兔子洞的危险。

在接下来的章节中,我们将介绍一些与机器学习相关的最常见的线性代数概念,以便建立强大的直觉和坚实的基础。我们不仅会讨论基础理论,还会学习如何使用 NumPy 在 python 中实现一些计算。

免责声明:这篇文章和所涵盖的主题的唯一目的是提供一个通用的指导方针——一个快速的参考——关于机器学习的相关概念。因此,理论有时只是简单地解释一下,一些概念就被完全回避了。如果有必要,我们总是可以更深入,使用本文作为要涵盖的主题的路线图。

数学对象

标量

标量只是一个单一的数字。之所以这样称呼它,是因为它在不改变方向的情况下拉伸或缩放矢量或矩阵。

我们可以用 python 中的一个简单变量来表示一个标量。

向量

从几何学上来说,我们可以把向量想象成一条线,由大小(长度)和方向来定义。然而,向量的定义不包括它的开始或结束位置。

例如,一个向量 v=[1,2]只告诉我们,它在第一个维度上走了一个单位,在第二个维度上走了两个单位。按照惯例,我们通常认为一个向量在标准位置——这意味着它的尾部位于原点[0,0]。

我们也可以把向量想象成一组数字。这样想可以让我们用 NumPy 表示一个向量,如下所示:

向量[1,2]绘制的示例[图片由作者提供]

矩阵

矩阵是数字的二维数组。不严格地说,我们也可以把矩阵想象成一组相邻的列向量。

假设我们的示例矩阵(A)包含实数值,我们想要描述它的形状。我们可以通过以下方式做到这一点:

其中 m 定义了一个矩阵的高度(行),而 n 告诉我们一个矩阵的宽度(列)。在我们的例子中, mn 都等于 3,因此我们有一个 3 乘 3 的矩阵。

我们可以使用 NumPy 以多种方式定义一个矩阵,在下面的代码中可以看到一些有用的方法。我们也可以使用numpy.shape(a)打印矩阵尺寸

:轴数可变的高维矩阵称为张量。

矩阵有多种不同的用途,其中包括:

  1. 线性变换的表示
  2. 方程组的表示
  3. 存储和表示数据(观察 x 特征)
  4. 存储例如卷积中使用的核

移调

转置操作的概念可以被认为如下:用行交换列,反之亦然。**

当我们转置一个列向量时,我们得到一个行向量。如果我们转置一个矩阵,我们得到主对角线上的镜像。

让我们形象化一些例子来获得更好的理解。

一个载体换位的例子。

一个把 2×3 矩阵转置成 3×2 矩阵的例子。

转置操作直接应用于代码中。

矩阵加法

矩阵加法相当简单,我们基本上只需将两个矩阵中的每个对应元素相加或相减即可。我们也可以称之为基于元素的操作。然而,我们必须记住的一件事是,两个矩阵需要具有相同的形状。

**

注意:我们也可以添加两个向量——概念与矩阵加法相同。

在 NumPy 中执行矩阵加法可以通过使用基本算术运算符或函数numpy.add(a,b)来完成。

我们也可以在矩阵中增加或减少一个标量。标量将简单地按元素相加。

在某些情况下,我们允许矩阵向量相加,尽管两个形状并不完全相同。因此,通过隐式复制广播,向量将被添加到矩阵的每一行。

注意:向量的长度必须与矩阵中的列数相同。否则不能播放。

矩阵和向量相乘

现在,我们完成了最重要的运算之一:两个矩阵的乘法。

在深入计算细节之前,让我们先了解一些有用的属性:

矩阵乘法是分配的(1)、结合的(2)、不可交换的(3)😗*

为了使矩阵乘法成为有效的运算,必须匹配“内部维度”【n】。生成的矩阵将由“外部尺寸”定义 (m,p)

让我们想一个例子来更好地理解这一点。

矩阵(A)由形状(2×3)定义,而矩阵(B)包含 3 行和 2 列,因此是形状(3×2)。因为“内部维度”匹配,所以定义了矩阵乘法,结果将是一个 2 乘 2 的矩阵。

既然我们已经知道了一些基本的性质,以及矩阵乘法何时有效,我们仍然需要知道如何执行计算。

我们基本上可以把矩阵乘法想成左矩阵的一个行向量和右矩阵的一个列向量的点积。我们可以将该机制形象化如下:

矩阵乘法可视化[图片由作者提供]

让我们通过一个数字例子来加深我们的理解。

注意:矩阵乘法不是按元素执行的。这种操作被称为哈达玛乘积,通常被称为⨀ B

矩阵乘法可以通过内置的 NumPy 函数以多种方式执行,如下面的代码示例所示。

矩阵向量乘法也被定义并遵循相同的概念,允许我们以更紧凑的方式表示方程组。

以下面的等式为例

它可以被重写为多个方程,但是不那么密集和优雅。

逆矩阵和单位矩阵

矩阵的逆矩阵允许我们解出方程 Ax = b 解析解 x. 但是首先,我们需要知道什么是单位矩阵。

单位矩阵不会通过乘法来改变向量或矩阵。主对角线上的元素只有 1,而非对角线上的元素只有 0。此外,单位矩阵始终是正方形 (MxM)

可视化一个 2 乘 2 的单位矩阵,我们得到如下结果:

另一方面,矩阵的逆矩阵是一个矩阵,当与原始矩阵相乘时,产生单位矩阵。

由于矩阵除法不存在,我们现在可以使用矩阵求逆来求解我们的方程 x

注意:我们只能使用矩阵的逆矩阵,如果它被定义的话。对于一个有逆矩阵的矩阵,它必须是平方的和满秩的。

我们可以利用 NumPy 的内置函数来创建任意大小的单位矩阵,并计算矩阵的逆矩阵。

线性相关性和跨度

在上一节中,我们计算了矩阵的逆矩阵,以求解 xAx=b

为了定义矩阵的逆矩阵,每个 b 必须正好有一个解。然而该方程也可能没有或定义了多个解。

如果我们把矩阵(A)的列想象成空间中不同的方向,我们可以算出有多少种方法可以到达 b 。我们例子中的向量(x)定义了我们在每个方向上要走多远。

一组向量的跨度——矩阵(A)的列——定义了可通过线性组合 Ax 获得的所有点。因此询问 Ax=b 是否有解,基本上与询问 b 是否在跨度内是一回事。向量所跨越的子空间称为列空间。

两个向量跨度的例子[图片由作者提供]

现在,想象其中一个向量是另一个向量的线性组合,从而形成一条直线。如果 b 正好位于那条线上,则生成的子空间现在减少到一维,并且 Ax=b 只有一个解。如果 b 不在那条线上,我们就没有办法到达它。

两个线性相关向量的例子[图片由作者提供]

如果一组向量中至少有一个向量可以表示为其他向量的线性组合,那么这组向量就是线性相关的——这正是我们在例子中所得到的。

另一方面,线性无关是完全相反的——没有一个向量可以用其他向量的线性组合来表示。

规范

不严格地说,范数衡量的是向量的大小或长度。更正式地说,范数可以被解释为将向量映射到非负值的函数。

一种常用的范数是欧几里德或ℓ2 范数:

其他有用规范的例子包括ℓ1 规范、ℓ∞规范或弗罗贝纽斯规范。

在代码中,我们可以使用函数[numpy.linalg.norm()](https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html)并通过参数ord=None指定范数的阶数

:默认情况下,我们会计算矩阵的弗罗贝纽斯范数和向量的ℓ2 范数。

特殊矩阵和向量

有些矩阵的元素或形状是特殊的,因此特别有用。

除了主对角线上的元素,对角矩阵只包含零。

当我们学习单位矩阵时,我们已经遇到了对角矩阵。

对角矩阵是有用的,因为它们计算效率高。例如,为了计算逆,我们只需要计算主对角线的倒数。

我们可以通过指定一个向量来构建一个对角矩阵,并简单地应用函数numpy.diag(v)

对称矩阵是任何与其转置矩阵相等的矩阵,这意味着

我们可以考虑一个矩阵示例,如下所示:

让我们假设我们有一个矢量 v 和一个矢量w。现在想象这两个矢量运行在不同的方向——事实上,它们是垂直的,角度为 90°。那些向量也可以被称为正交的——如果两个向量都有单位范数,长度为 1,则它们被称为正交的

另一方面,正交矩阵是行和列相互正交的正方形矩阵。这意味着两个有用的属性

正交矩阵是有趣的,因为它允许我们非常便宜和有效地计算逆矩阵,因为它是由其转置定义的。

特征分解

分解或者不严格地说,将一个数学对象分解开来,有时可以让我们通过揭示不明显的属性来更好地理解它。

特征分解将矩阵分解成特征值和特征向量。对于旋转不变的特征向量向我们显示了矩阵的方向,而特征值是比例因子,描述了特征向量的大小。

*

我们可以借助内置的 NumPy 函数来检索特征值和特征向量。为了重构原始矩阵,我们只需要计算特征向量、对角化的特征值和特征向量的逆的乘积。

特征分解是有用的,因为它是主成分分析中的关键元素。

奇异值分解

奇异值分解(SVD)和特征分解密切相关,因为在这两种情况下,我们分解或因式分解矩阵。

然而,奇异值分解具有更广泛的适用性。例如,如果矩阵不是正方形的,则不定义特征分解,而仍然可以应用 SVD。

奇异值分解将矩阵分解成奇异值和奇异向量。应用时,我们将单个矩阵分解为三个特殊矩阵的乘积。

矩阵(U)和(V)都是正交的,并且分别包含左奇异向量和右奇异向量。矩阵(D)是对角矩阵,包含主对角线上的奇异值。

SVD 是有用的,因为它允许我们比特征分解更普遍地应用矩阵分解,并且它还使我们能够部分地将矩阵求逆推广到非方矩阵。

我们可以依靠使用[numpy.linalg.svd(a)](https://numpy.org/doc/stable/reference/generated/numpy.linalg.svd.html)来应用 SVD。

Moore-Penrose 伪逆

没有为非方阵定义矩阵逆矩阵。然而,Moore-Penrose 伪逆允许我们计算或近似高且宽的矩阵的逆。

当计算伪逆时,实际算法依赖于 SVD。

上面的等式应该看起来很熟悉,因为它检索与 SVD 相同的三个矩阵,对角矩阵(D)的伪逆是通过计算非零元素的倒数并对结果矩阵进行转置获得的。

在代码中,我们可以简单地利用 NumPy 函数来计算伪逆。由于矩阵与逆矩阵相乘应该产生单位矩阵,我们可以用这个事实来检查我们的结果。

跟踪运算符

一个矩阵的所有对角线元素的和可以通过追踪算子来计算。

trace 运算符很有用,因为它允许更简单的符号。例如,Frobenius 范数可以使用 trace 运算符表示如下:

假设,我们有一个 5x 5 的单位矩阵。所有对角线元素的总和应该等于 5,并且可以使用 NumPy 来计算,正如我们在下面的代码示例中看到的。

行列式

行列式基本上是将矩阵映射到标量的函数,由所有特征值的乘积定义。

它在几何学上也可以分别解释为面积或体积。不严格地说,行列式给出了一个度量,即乘以一个矩阵可以扩大或缩小多少空间。

可以使用内置的 NumPy 函数[numpy.linalg.det(a)](https://numpy.org/doc/stable/reference/generated/numpy.linalg.det.html)计算行列式

结论

在这篇文章中,我们试图涵盖很多内容。我们简要概述了与机器学习相关的一些最常见的线性代数概念背后的理论。此外,在 NumPy 的帮助下,我们已经用 python 实现了大多数概念。

我们的目标是提供一个通用的指南和一个重要主题的简要概述。因此,我们要么必须完全回避,要么只能触及线性代数的某些方面。

然而,手头的这篇文章应该为我们提供建立第一直觉的方法,并有一个主题路线图,我们可以在必要时进一步探索。

当我们在实践中学习、应用和实现机器学习算法时,理解线性代数的基础和良好的直觉将被证明是非常宝贵的。

感谢您的阅读!确保保持联系&在 MediumKaggle 上关注我,或者在 LinkedIn 上说声“嗨”

喜欢这篇文章吗?成为 中等会员 继续无限学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@marvinlanhenke/membership

参考资料/更多资料:

  • 深度学习(Ian J. Goodfellow,Yoshua Bengio 和 Aaron 库维尔),第二章,麻省理工学院出版社,2016 年。
  • 迈克·科恩博士。线性代数:理论,直觉,代码。*

Keras 中作为退化神经网络的线性和逻辑回归

原文:https://towardsdatascience.com/linear-and-logistic-regressions-as-degenerate-neural-networks-in-keras-c9b309cbd80a

神经网络是线性回归和逻辑回归的超集。使用 Keras 快速有效地建立回归模型,并根据需要在它们和神经网络之间切换。

图片基于 Ashkan Forouzani 在 Unsplash 上拍摄的照片

如果您的任务是为某个度量创建预测,您可能想知道简单的线性或多元回归是否足够(或者,如果我们想要预测二进制值,逻辑回归),或者可能使用神经网络。以及在尝试不同的模型时需要多少编码。好消息是,高级神经网络框架 Keras 足以满足所有这些目的,我将用一个简单的例子来说明。

Aaron Zhu 对这里使用的回归模型的概述是一个很好的开始,所以这里我们只提一下基础知识。

线性回归

简单线性回归的目标是使用两个常数将一个结果(Y[i])(一个连续变量)建模为连续输入变量(X[i])的线性函数:

𝛃X[i] + 𝛂 = Y[i]*

预测中的误差(Y[i]* — Y[i])通常使用误差平方和来测量,因为最小化该误差恰好最大化𝛂和𝛃提供给定 x 和 y 的观测数据的基础模型的可能性

请注意,从 X 到 Y*的映射实际上与神经网络中全连接(或密集)层提供的映射相同。这个密集层非常简单:它有一个输入和一个输出;𝛃是与单个输入相关的权重,𝛂是偏差。

虽然简单的线性回归可以直接求解,也就是说,𝛂和𝛃的值可以使用一些代数来找到,但如果我们决定使用梯度下降来优化它们的值,我们将得到一个非常简单的退化神经网络,具有单个密集层和平方误差损失函数。这两者在喀拉斯都很容易买到。我称之为神经网络退化,因为它没有激活功能,所以它甚至没有实际的感知器。

但是,为什么要在这里停下来呢?如果我们有多个输入变量,X1,X2,… Xn,我们可以考虑多元回归,其中单个结果(Y)使用一个因子向量进行预测:

𝛃1x1[i]+𝛃2x2[i]+…+𝛃nxn[i]+𝛂= y[I]*

获得𝛃n 和𝛂的直接代数方法是解线性方程组。另一种选择是,使用上述梯度下降法,可以证明一般来说资源不太密集,这又产生了退化的神经网络:这一次是具有 n 个输入和单个输出的密集层,其中𝛃1、𝛃2、…、𝛃n 是权重,𝛂是偏差。出于与简单线性回归相同的原因,误差函数的选择是误差平方和。

逻辑回归

虽然有时我们需要预测的不是一个连续的值,而是一个真或假的值,这就是逻辑回归的作用。这是一个线性回归模型,其结果被输入逻辑函数𝝈(x) = 1/(1+exp(-x)),这是一个将所有实数映射到 0–1 区间的 sigmoid 函数。这使得逻辑回归的输出可以解释为一种概率:如果我们试图预测一个人会买红色还是绿色的气球,这可能是他们选择红色气球的概率。

𝝈(𝛃1x1[i]+𝛃2x2[i]+…+𝛃nxn[i]+𝛂)= y[I]* = prob(person[I]购买红色气球)

这里,通常的误差平方和损失不会最大化𝛃1、𝛃2、…、𝛃n、𝛂直接成为潜在模型的可能性。我们需要使用的正确的损失函数叫做二进制交叉熵。(这一点的推导见朱的文章。)逻辑函数增加了直接找到最优解的复杂性,因此,与多元回归类似,梯度下降可以提供更快且资源更少的替代方案。

毫不奇怪,使用梯度下降,优化逻辑回归相当于训练一个简单的神经网络,使用所有相关软件包中可用的组件。实际上,逻辑回归表示单层感知器,在 Keras 中,它可以被建模为具有 sigmoid 激活的密集层。使用二进制交叉熵损失函数来训练这个模型正好给出了我们想要的。

一个例子

也就是说,不需要编写单独的代码或调用单独的库来尝试线性和逻辑回归,或者成熟的神经网络。简单地改变神经网络模型允许我们快速且容易地尝试所有三种类型的模型。

为了演示这一点,我们创建了一个玩具示例。给定一个人的年龄、关系状况和孩子数量,我们试图预测他们买了多少气球,以及这些气球是红色还是绿色。我们将关系状态表示为单个值,对于合作伙伴为-0.5,对于单身为+0.5。

在我们的玩具示例中,我们选择了一个不完全是线性的基础模型,来看看全神经网络是否比线性模型更好。我们使用统一的随机值生成输入。我们从[-0.5,+0.5]的区间开始这样做,因此输入已经被归一化:具有零均值和均匀方差。然后我们计算:

if relationship == -0.5:
    number_of_balloons = 1\. * children - .2 * age
    balloon_color = 1 if (.8 * children + .2 * age > 0) else 0 
else:
    number_of_balloons = .8 * children + .5 * age
    balloon_color = 1 if (.5 * children + .5 * age > 0) else 0

我们创建了四个模型:一个多元线性回归和一个末端没有 sigmoid 函数的神经网络来预测气球的数量;这里我们用的是损失平方和。然后是逻辑回归模型和具有最终 sigmoid 函数的神经网络来预测气球的颜色;这里我们使用二进制交叉熵作为损失。

有关训练这些模型的代码,请参见此要点,对于回归模型,还会显示密集层的权重(对应于𝛃和𝛂):

四种模式的实施和培训

一个示例运行产生了以下输出—为便于阅读,此处对其进行了细微的修改:

======= Output type: num_balloons Model type: regression
Epoch 1/1000 loss: 0.0526 - val_loss: 0.0205
Epoch 2/1000 loss: 0.0205 - val_loss: 0.0201
Epoch 3/1000 loss: 0.0204 - val_loss: 0.0207 Weights: 
[<'dense/kernel:0' ([[-0.00324172], [0.43457505], [0.1423042]])>,
<'dense/bias:0' ([0.00032589])>] ======= Output type: num_balloons Model type: neural 
Epoch 1/1000 loss: 0.0206 - val_loss: 1.1117e-04 
Epoch 2/1000 loss: 1.2853e-04 - val_loss: 1.1776e-04 ======= Output type: color Model type: regression 
Epoch 1/1000 loss: 0.5130 - val_loss: 0.2387 
Epoch 2/1000 loss: 0.2213 - val_loss: 0.2005 
Epoch 3/1000 loss: 0.2009 - val_loss: 0.1935 
Epoch 4/1000 loss: 0.1965 - val_loss: 0.1969 Weights: 
[<'dense/kernel:0' ([[0.02176554], [14.170614], [8.668548]])>,
<'dense/bias:0' ([0.02175274])>] ======= Output type: color Model type: neural 
Epoch 1/1000 loss: 0.4589 - val_loss: 0.0651 
Epoch 2/1000 loss: 0.0518 - val_loss: 0.0345 
Epoch 3/1000 loss: 0.0271 - val_loss: 0.0199 
Epoch 4/1000 loss: 0.0161 - val_loss: 0.0147 
Epoch 5/1000 loss: 0.0119 - val_loss: 0.0098 
Epoch 6/1000 loss: 0.0104 - val_loss: 0.0098

第一个观察结果是神经模型在两种情况下都比回归结果好(0.001178 验证损失对 0.0207;0.0098 损失对 0.1969)。正如所料,他们可以模拟非线性关系。

回归返回的权重值得进行更多的分析和健全性检查。对于气球的数量,多元回归预测

-0.003 关系+ 0.435 儿童数量+ 0.142 年龄+ 0.000 =气球数量

因为孩子的数量和年龄的平均值都为零,所以对于两种关系状态,基础模型返回的气球数量的平均值也为零。这反映在关系输入的因子和偏差实际上都为零。

基础模型为两种关系状态生成相同数量的示例,因此我们期望近似其组合的最佳线性模型位于基础模型中两个线性表达式的中间。事实上,我们发现:孩子数量的因子接近(1.0 + 0.8) / 2 = 0.45,年龄的因子接近(0.5–0.2)/2 = 0.15。

对于气球的颜色,逻辑回归给出了模型

𝝈(0.0218 关系+14.171 num _ 儿童+ 8.669 年龄+ 0.0218) = prob(红色)

同样的对称性适用于以前,我们再次发现,关系和偏差的因素接近于零,特别是如果与其他两个数字相比。我们再次期望线性模型介于两个线性表达式之间。对于我们期望的孩子数(0.8 + 0.5) / 2 = 0.65,对于年龄(0.2 + 0.5) / 2 = 0.35。然而,由于 sigmoid 函数,该模型只对输出是负还是正感兴趣,因此存在任意的比例因子;考虑到这一点,我们看到这些因素确实有意义:14.171 / 8.669 = 1.635,接近 0.65 / 0.35 = 1.857。

事实上,比例因子不是任意的:它们是大数字的事实使得 sigmoid 函数的输入绝对值很大,这迫使其输出非常接近零或一。

结论

我们已经看到了神经网络如何成为线性和逻辑回归的超集,以及如何使用现有的软件组件来构建神经网络,我们可以非常容易地实现回归模型。以这种方式实现回归模型可以很容易地在必要时将它们升级为神经网络。

图片基于 Ashkan Forouzani 在 Unsplash 上的照片,https://unsplash.com/photos/_Y82jqFIBgw

原载于 家具雪貂博客

线性模型机器学习方式

原文:https://towardsdatascience.com/linear-model-the-machine-learning-way-c33f9db9ac37

当机器学习遇到计量经济学,反之亦然

照片由 詹姆斯·惠勒 发自 Pexels

动机

普通最小二乘模型(OLS)是机器学习(ML)的核心构建模块。OLS 在社会科学中也随处可见。我有经济学背景,最初我对 ML 教科书解决 OLS 问题的方式有点困惑。在这篇博文中,我解释了经济学方法和 ML 方法,以及为什么两者都有意义。

TL;DR:在高维设置中,不要求一个巨大矩阵的逆,用梯度下降。

一.理论

经济学之路

让我们考虑一个简单的线性模型的形式:

其中 y 是因变量的向量;x 列中包含独立变量的矩阵;β是我们想知道的向量。和 e 是随机噪声项。线性模型的 OLS 估计器在于最小化数据和(线性)模型之间的均方误差。就不告诉你数学细节了。底线是这个问题有一个精确的公式。OLS 估计量为:

“经济学方法”在于直接使用这个公式,做这个看似无害的矩阵求逆。从理论的角度来看,这个公式是惊人的,因为我们可以用它来推导 OLS 的理论性质。我们也可以用这个公式来计算β的 OLS 估计量的置信区间。

此外,请注意,在社会科学应用程序中,没有人真正关心您如何获得 beta。人们对β系数的符号更感兴趣。例如,以β的第一个系数为例,它不是截距。我们称这个系数为β_ 1。然后,如果 y 和 x_1 都以水平表示,那么 x_1 增加一个单位会导致 y 增加一个β_ 1。线性模型被用作调查 x 和 y 之间因果关系的工具。线性模型能够预测未来结果的程度不是最终目标。

机器学习的方式

ML 方法在于使用递归算法来求解β。为什么不直接使用上面描述的可爱公式来计算β的估计值呢?症结在于 X’X 的矩阵求逆。虽然对“小”数据集(没有太多解释变量和/或观察值)可行,但 X’X 的直接求逆可能会使您的笔记本电脑崩溃,或者在处理数千个解释变量和/或数十亿个观察值时速度非常慢。

ML 的方式是用渐变下降 (GD)。GD 的想法很直观f 在给定点的梯度告诉我们 f 在该点最大增加的方向。因此,向相反的方向移动(减去梯度)可能是找到局部最小值的好主意。

GD 算法重复应用这个过程,直到找到最小值(希望是全局的)。从β的初始猜测开始,使用以下公式更新猜测:

其中α是一个小值(“学习率”),右手边的最后一项是使用当前对β的猜测时损失函数的梯度。这里损失函数的梯度简单地是:

二。应用

现在让我们使用模拟数据来比较这两种方法。我使用模拟数据,因为它允许我直接比较β的估计值和真实值。我使用 Julia,它是一种在科学计算应用中日益流行的“快速 Python”。

经济学之路

在下面的代码块中,我定义了一些模拟数据,并固定了一个要估计的随机系数β。按照“经济学方法”,对β的估计是一个简单的矩阵求逆。如图所示,β中的所有系数都位于 45 度线上,表明 OLS 公式有效。到目前为止还没什么大惊喜。

Julia 线性模型:矩阵求逆

作者图片:(X'X)^-1X'y 在行动

机器学习的方式

现在我们切换到机器学习的方式。在下面的代码块中,我对 50 次迭代应用梯度下降,并绘制估计的β与真实的β。最初,因为我以随机猜测开始这个过程,所以真实值和估计值是正交的,如蓝色水平线所示。随着迭代方案向前推进,点云开始逆时针旋转。经过 50 次迭代,所有点都极其接近 45 度线,说明我们找到了一个非常接近真值的值。

带 Julia 的线性模型:梯度下降 100d

图片作者:梯度下降 100d 案例

如果我们把注意力限制在二维情况,另一种可视化是可能的。我们可以为梯度下降的每一步绘制β估计的坐标。下面的代码块可以做到这一点。如下图所示,在每次迭代中,beta 的估计值越来越接近真实值(小星号)。

带有 Julia 的线性模型:梯度下降 2d

作者图片:梯度下降 2d 案例

“真正的”机器学习方式

在上一节中,样本中的所有观察值都用于计算梯度。对于非常大的数据集,计算梯度可能不可行(或者因为相关成本而不可取)。“真正的”机器学习方法在于仅使用观察值的子集来计算每一步的梯度。下面的代码块实现了这个想法。如下图所示,β使用随机梯度下降的路径用十字表示。不是非常平滑地下降到真实值,而是十字所选择的路径更加“模糊”。然而,模糊路径收敛到真实值。

带 Julia 的线性模型:随机梯度下降 2d

作者图片:SGD 与 GD 算法

结论

OLS 分析公式是推导理论属性的黄金标准,在处理合理大小的数据时非常好。在大数据环境中,梯度下降是必由之路。如果你接受过经济学培训,这篇博文可以被看作是对机器学习中使用的一些工具的温和介绍。如果你接受过机器学习的培训,我希望这篇文章能帮助你理解社会科学领域的人们是如何使用线性模型的。

数据科学中的线性回归

原文:https://towardsdatascience.com/linear-regression-in-data-science-b9a9d2aacc7c

机器学习的数学技术

艾萨克·史密斯在 Unsplash 上拍摄的照片

随着毕业季即将来临,我有几个家庭成员问他们多久会使用他们多年来学到的信息。尤其是他的一个表亲,并不热衷于数学。然而,他制造了自己的游戏 PC,并喜欢学习计算机硬件。曾经有人提到,计算机会做所有必要的数学运算,那么为什么要记忆公式呢?虽然我在一定程度上看到了他的观点,但数学是学习更多计算机知识的基石,而且作为一名程序员,能够验证结果不会有什么坏处。这种思路把我带回了代数 1,我记得在那里学习了图形和建模方法,如线性回归。那时我从未想过我会在大学毕业后很好地使用它。但是,我对数据科学的兴趣让我在预测分析和相关数据方面完全融入了数学。所以今天,我决定回顾一下线性回归及其在数据科学中的应用。

线性回归概述

如果你还记得数学课,回归在统计学中被用来描述模型。它也用于描述和估计变量之间的关系。线性回归的要点是找到并画出代表两个或多个感兴趣的变量之间关系的最佳拟合线。这条线可以帮助您找到数据之间的相关性并做出预测。

从数据中找到相似的趋势,并对未来的数据做出预测,这是数据科学用于机器学习和统计建模的内容。稍微回顾一下,让我们回到数据科学的话题。

机器学习方法

当我们谈论线性回归时,是时候提一下它使用的机器学习技术了。主要的两种机器学习方法是监督学习和非监督学习。

有了监督学习,就可以使用带标签的输出数据来训练模型。这有助于模型按照您想要的方式将其映射导向匹配输出标签的输入变量。回归或分类就是一个例子。

在无监督学习中,模型没有任何标记数据,因此它必须找到数据中的模式和结构,以决定如何映射它。一个例子是聚类或关联。

当我们想到线性回归时,我们想到的是监督学习技术。这有助于快速训练模型,并且易于理解。既然我们简要地讨论了学习类型,让我们回到线性回归在数据科学中的应用。

为什么是线性回归

当考虑回归类型时,有几种不同的方法可以采用。您可以使用简单回归或多元回归。在这两种情况下,您都可以选择使用线性或非线性回归。通常,线性回归更容易使用,也更容易解释。因为有了清晰的线性线,不仅模型更容易理解,也更容易预测。

线性回归确实试图将数据“拟合”到模型中。那个模型是线性的。这也把数据控制在一定的误差范围内,也就是不遵循线性模型的数据点。由于这种对误差的控制,你可能并不总是拥有在一定程度上完美地保持在线内的数据。如果是这种情况,这时你应该选择使用非线性回归。您的数据需要满足一些假设才能很好地使用线性回归。

线性回归假设

  • 最佳拟合线性线的误差(或残差)遵循正态分布
  • 数据没有明显的异常值
  • 观察应该是相互独立的,也就是说没有依赖性
  • 变量应该在一个连续的水平上被测量
  • 检查同方差性,同方差性是一个统计概念,即最佳拟合线的方差在整个直线上应该保持相似
  • 查看数据时,使用散点图可以快速查看变量之间是否存在线性关系

线性回归程序

简单介绍一下可以用来执行线性回归的不同程序和环境:

  • MATLAB 线性回归
  • 线性回归 Python
  • r 线性回归
  • Sklearn 线性回归
  • Excel 线性回归

线性回归用例

为了涵盖所有基础,并且因为有时我认为这样学习更容易,我们也来看几个用例。

一个例子是保险公司的风险分析。在这种情况下,线性回归可以用来帮助预测或估计索赔的成本。这可能有助于企业就承担何种风险做出重要决策。

显而易见的例子是教育、金钱和其他途径,如经验或年龄。然而,我认为一个有趣的例子是关于体育分析的。你可以比较的数据点是赢得的比赛数,以及你的团队得分的平均数。你也可以反过来,根据对手得分的多少来决定赢了多少局。这样,一旦你发现了这种相关性,你就可以预测游戏的结果,或者一个赛季可能会赢多少。

结论

今天我们讨论了线性回归以及它在数据科学中的应用。首先,我们概述了什么是线性回归。接下来,我们讨论了线性回归使用的机器学习方法,以及为什么使用线性回归。之后,我们研究了如何确定线性回归是否适合建模,包括数据必须匹配的假设。简单地说,我们还检查了一些可以用来执行线性回归的程序和环境。最后,我们看了几个用例。

尽管学习线性回归是许多年前的数学概念,但机器学习将该技术带回帮助机器预测趋势和未来数据。希望数据科学中使用的线性回归更有意义,你会发现读起来很有趣。下次见,干杯!

用我的 每周简讯 免费阅读我的所有文章,谢谢!

想阅读介质上的所有文章?成为中等 成员 今天!

查看我最近的一些文章

https://miketechgame.medium.com/getting-started-with-seq-in-python-4f5fde688364 https://medium.com/codex/javascript-cdns-and-how-to-use-them-offline-e6e6333491a3 https://medium.com/codex/something-i-learned-this-week-vue-js-in-asp-net-core-mvc-7b7540a38343 https://python.plainenglish.io/5-python-libraries-to-use-everyday-d32a9de13269 https://blog.devgenius.io/automate-kubernetes-with-python-2150c290afe7

参考资料:

https://brilliant.org/wiki/linear-regression/ https://www.analyticsvidhya.com/blog/2021/05/all-you-need-to-know-about-your-first-machine-learning-model-linear-regression/ https://www.ibm.com/topics/linear-regression

Python 中的线性回归基础

原文:https://towardsdatascience.com/linear-regression-in-python-for-data-scientists-16caef003012

UCL 数据科学学会研讨会 10:什么是线性回归、数据探索、Scikit 学习实施和多元线性回归

艾萨克·史密斯在 Unsplash 上拍摄的照片

今年,作为 UCL 数据科学协会的科学负责人,该协会的目标是在整个学年举办一系列 20 场研讨会,涵盖的主题包括 Python、数据科学家工具包和机器学习方法的介绍。每一篇文章的目标都是创建一系列的小博客,这些小博客将概述要点,并为任何希望跟进的人提供完整研讨会的链接。所有这些都可以在我们的 GitHub 资源库中找到,该资源库将在全年更新新的研讨会和挑战。

本系列的第十次研讨会介绍了 Python 中的线性回归,并介绍了我们的数据科学与 Python 研讨会系列。这个特定的研讨会将涵盖什么是线性回归,探索数据,实施模型和模型评估。虽然亮点将在这篇博文中呈现,但完整的研讨会可以在我们的 GitHub 账户这里找到。

如果您错过了我们之前的任何研讨会,可以在这里找到:

什么是线性回归?

线性回归是一种统计方法,用于模拟两个或更多数量之间的相关性或关系。这样做的目的是能够更好地理解现有的关系,或者能够预测我们目前没有数据的点的行为。

对于一组数据点 x 和 y,我们可以将我们要建模的线的方程写成:

y(x) = mx + c

其中 y(x)是模型的预测 y 值,梯度(m)和 y 截距(c)称为拟合参数。通过使用线性回归方法(也称为最小二乘拟合),我们可以计算两个参数的值,并绘制最佳拟合线,以实现我们更好地理解关系或找到未知点的估计值的目标。

为此,我们必须能够计算斜率(m)和截距(c ),以给出数据的最佳拟合线。我们可以通过以下等式来实现:

作者图片

其中 x 和 y 代表数据的平均值。然而,这通过已经实现的库变得简单,例如 Scikit-Learn 和 Statsmodels Api,它们具有内置的线性回归功能。我们将首先展示如何在 Python 中实现这些公式,然后展示如何使用 Scikit Learn 库执行更复杂的分析。

数据探索

我们拥有的数据是一个生成的数据集,我们首先只关注 X 和 Y 两个变量来执行简单的线性回归。我们可以使用以下代码绘制数据,以了解我们可能期望看到的关系:

# Generates data frame from csv file
df = pd.read_csv("RegressionData.csv")# Turning the columns into arrays
x = df["x"].values
y = df["y"].values# Plots the graph from the above data
plt.figure()
plt.grid(True)
plt.plot(x,y,'r.')

作者图片

我们可以看到,X 和 Y 变量显示了一种关系,Y 变量似乎随着 X 的变化而线性变化。这意味着我们可以尝试用线性拟合来模拟,所以我们可以用线性回归。

为此,我们可以使用上面详述的等式,尝试根据数据计算梯度和 y 轴截距,如下所示:

# calculating the means of the x and y data
mean_x = np.mean(x) 
mean_y = np.mean(y)# calculating the slope
slope = np.sum((y - mean_y)*x) / np.sum((x - mean_x)*x) 
print ("Gradient:", slope)# calculating the intercept
intercept = mean_y - slope*mean_x 
print ("Intercept", intercept)#out:
Gradient: 0.9773554490236186
Intercept 0.33323427076670953

然后我们可以用它来绘制数据的最佳拟合线:

x_max = max(x)
plt.figure("") # start a new figure
plt.grid(True) # add a grid
# generate two points for the fitted line
x_points = np.linspace(0, x_max*2., 2) 
y_points = slope*x_points + intercept
plt.plot(x, y,'r.') # plotting data as points
plt.line = plt.plot(x_points, y_points, 'b-') #plotting the line of best fit
# setting limits for the axes
plt.xlim(-10,110) 
plt.ylim(-10,110)

作者图片

由此我们可以看到我们在第一个数据图中确定的预期线性关系。我们还可以看到,截距约为 0.3,斜率接近 1,几乎是单位线性关系。

Scikit 学习实现

然而,更有效和有用的方法是使用 Python 的 Scikit 学习库,该库具有更深入评估线性回归的广泛功能。虽然我们已经计算了梯度m和截距c,但是我们可以使用 Scikit Learn 来完成这项工作,然后使用这个库来评估模型性能。

首先,我们需要为模型准备数据。为此,我们可以将模型从当前格式重新调整为数组,然后将数据拆分为训练和测试数据集。我们这样做的原因是,我们可以看到我们的模型如何适应看不见的数据,而不是让它过度适应我们所有的数据。我们可以用下面的代码做到这一点,并想象它是什么样子:

# Independant variable or features
X = x.reshape(-1,1)# Dependant variable or labels
y = y.reshape(-1,1)# Seperates the data into test and training sets 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)# Plotting the training and testing splits
plt.scatter(X_train, y_train, label = "Training Data", color = 'r')
plt.scatter(X_test, y_test, label = "Testing Data", color = 'b')
plt.legend()
plt.grid("True")
plt.title("Test/Train Split")
plt.show()

作者图片

这表明,我们已经将大部分数据(80%)作为模型将要训练的数据,同时保留了可用于评估模型性能的少数数据(20%)。我们在这里可以看到,测试数据虽然很小,但覆盖了大范围的整体数据,因此可以作为很好的测试数据来查看模型的表现。

既然我们已经将数据分为测试和训练数据集,我们就可以实现线性回归模型了。为了通过 Scikit Learn 实现线性回归,我们首先通过调用LinearRegression函数来定义一个regressor变量。然后我们传递regressor.fit(X_train, y_train),它将训练数据传递给回归,以便训练它。

# Defining our regressor
regressor = LinearRegression()# Train the regressor
fit = regressor.fit(X_train, y_train)

和以前一样,我们感兴趣的是从我们的数据中提取的梯度和截距,我们可以通过调用我们训练的回归对象的属性.coef_.intercept_来提取这些数据。由此我们可以得到:

# Returns gradient and intercept
print("Gradient:",fit.coef_)
print("Intercept:",fit.intercept_)#out
Gradient: [[0.98952479]]
Intercept: [-0.1328508]

我们可以看到,虽然梯度与之前的相似,但截距不同。这主要是因为我们将数据分成了训练数据和测试数据,因此我们不是在处理整个数据集。

这意味着我们可以根据我们的测试数据来评估我们的模型,看看它的表现如何。为此,像以前一样,我们想提取最佳拟合线,现在我们可以使用regressor.predict(X_test)方法,而不是像以前一样计算直线。这意味着我们可以这样实现:

# Predicted values 
y_pred = regressor.predict(X_test)# Plot of the data with the line of best fit
plt.plot(X_test,y_pred)
plt.plot(x,y, "rx")
plt.grid(True)

作者图片

然后,通过创建一个数据框架,将预测值与实际测试结果进行比较,如下所示:

# Converts predicted values and test values to a data frame
df = pd.DataFrame({"Predicted": y_pred[:,0], "Actual": y_test[:,0]})
df

作者图片

然后我们可以通过传递regressor.score(X_test, y_test)为我们的模型提供一个分数。正如我们之前提到的,这是回归器没有训练过的数据,这意味着它可以为模型在数据本身上的表现提供良好的基础。这是 R 分数,它显示了模型捕获的目标变量的变化量。这方面的最佳得分为 1,其中大于 0.8 通常被视为模型拟合良好的指标,而 0 则被视为最差的性能。

对于我们当前的模型,结果如下所示:

# Determines a score for our model
score = regressor.score(X_test, y_test)
print(score)#out:
0.9763002831471521

这表明我们有一个很好的模型。

Scikit 学习多元线性回归

我们上面使用的例子只涉及两个变量,这在我们自己的代码中相对容易实现,但在现实世界中,单独测量或使用一个特征或独立变量是极不可能的。这使得实现和等式更加复杂,因此我们可以依靠 Scikit 了解更多来实现这一点。

我们有多个独立变量的新方程采用以下形式

这意味着实际上我们有多个梯度值,还有一个额外的𝜖项,这就是误差。实际上,𝜖考虑了任何可能不适合线性模型的潜在点。

为此,我们使用生成的广告数据及其对销售的影响。这里,自变量由TVRadioNewspaper列给出,而Sales是我们的自变量。这方面的数据如下:

# Converts advertising csv to a data frame
df = pd.read_csv("advertising.csv")
df

作者图片

然后在 Scikit Learn library 下应用线性回归算法的过程采取了类似的路径,我们获取 X 和 Y 变量,对其进行整形,分成训练和测试数据,实施模型,预测未知数据,然后提取分数。这都可以通过以下方式实现:

# Independent variables
X = df.drop("Sales",axis=1)# Dependent variable
y = df["Sales"].values.reshape(-1,1)# Splitting into test and training data
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2)# Defining regressor
regressor = LinearRegression()# Training our regressor
fit = regressor.fit(X_train,y_train)# Predicting values
y_pred = fit.predict(X_test)# Scoring our regressor
fit.score(X_test,y_test)#out:
0.9174440089730509

我们可以看到,我们的模型通常与我们拥有的数据非常吻合,得分为 0.917,实施相对简单。

当然,我们可以创建一个更复杂的模型,并通过使用 Scikit Learn 库中内置的其他指标或评估方法以更详细的方式对其进行评估,更多信息可在此处找到。此外,除了 Scikit Learn 之外,您还可以使用 Statsmodels Api 库,该库为您内置的回归实现提供了更详细的摘要,可用于评估您的模型,其实现可在此处找到。

如果您想了解我们协会的更多信息,请随时关注我们的社交网站:

https://www.facebook.com/ucldata脸书

insta gram:https://www.instagram.com/ucl.datasci/

领英:https://www.linkedin.com/company/ucldata/

如果你想了解 UCL 数据科学协会和其他优秀作者的最新信息,请使用我下面的推荐代码注册 medium。

https://philip-wilkinson.medium.com/membership

或者看看我写的其他故事:

线性回归:海洋学数据建模

原文:https://towardsdatascience.com/linear-regression-modelling-oceanographic-data-fdc3a98813bf

最重要的,被忽视的,可预测的回归模型。

线性回归图像-作者

线性回归是一个简单而强大的工具,应该放在每个数据科学家的口袋里。在本文中,我将使用一个真实的例子来展示线性回归对真实数据的强大作用。

CalCOFI 数据集

我们将使用 CalCOFI 数据集 (CC BY 4.0 License)。该数据集包含了从 1949 年至今加利福尼亚州的全面海洋学测量数据。数据集包括水温、盐度、测量深度、氧气水平等要素。在本文中,我们将只使用水温和水的盐度功能。

加州海岸——作者卡斯滕·科恩

介绍

首先,线性回归模型用于预测一个连续变量的值,基于另一个连续变量的值。简单线性回归是线性回归的最基本形式,其中映射这种关系的模型是直线。

让我们从导入数据集开始,从 CalCOFI 数据集中抽取 500 个测量值的样本。在下图中,我们可以看到温度和盐度之间的关系。随着温度的升高,水的盐度降低。这表明变量之间存在负相关关系。

数据点—按作者

现在,假设我们得到了一个新的数据点,它有温度测量值,但没有盐度值。我们能根据温度来估计它的盐度值吗?(惊喜!是的,我们可以。)

解决这个问题的方法有很多,但最常用的方法之一是画一条符合数据的线。通过这种方式,给定任何温度测量值,我们都能够提供其盐度的精确估计。

我们可以简单地画一条看起来不错的线,但要做出一个真正好的猜测,我们需要做一些数学工作。在下面的部分中,我们可以使用普通的最小二乘法来找到这条线的方程。

OLS(普通最小二乘法)

普通最小二乘法是用于寻找线性回归模型的未知参数的方法。它通过最小化观察标记和预测标记之间的差的平方和来选择这些参数。简单地说,就是选择最适合的路线。这条线的方程式如下所示。

简单线性回归公式—作者

上面这条线的方程有两个参数,B0 和 B1。B0 对应于 y 轴截距,B1 对应于直线的斜率。通过调整这两个参数,我们可以在 2D 空间中创建任何可能的线。

为了找到 B0 和 B1 的最佳值,我们可以使用以下 OLS 实现。该等式由矩阵 X、向量 Y 和结果向量 B 组成。在我们输入数据并求解该等式后,向量 B 将包含最佳系数(B0,B1)。

普通最小二乘公式—作者

上面的公式看起来很复杂,但是它的结构使得我们可以很容易地增加代码行的复杂性,而不需要添加新的术语(我们将在本文后面这样做)。

因此,使用下面的代码块,我们可以做到这一点。如果你相信我的计算机能正确地做矩阵和向量乘法,那么我们就能得到一条直线来拟合数据。这显示在代码块下面。

简单线性回归—作者

线条看起来不错,但在执行模型评估时使用损失函数很重要。我们将使用的损失函数称为 MSE(均方误差)。这衡量数据点离回归线有多远/多近。换句话说,我们取残差的平方。

均方差公式—作者

你可能会想,为什么我们要对残差求平方?

我们这样做只是为了惩罚远离回归线的数据点。如果我们取平均绝对误差,那么使近点更近或远点更近对损失函数有相同的影响。如果我们采用 MSE,那么我们会对外围数据点增加惩罚。惩罚这些离群点是一把双刃剑,因为一个极端的离群点会对 MSE 产生重大影响。

当我们评估我们创建的简单线性回归模型时,我们得到的 MSE 值约为 1.5355。

有损失的简单线性回归-作者

线性回归(非简单)

用直线拟合数据效果很好,但是很明显,非直线将最适合数据。值得庆幸的是,我们可以很容易地在 OLS 公式的 X 矩阵中添加列来做到这一点。我们向矩阵中添加一个新列,它是所有温度值的平方。我们现在正在用线性回归拟合一个输入非线性的模型。如果我们添加一个新的列,这将是温度,然后是 temperature⁴,temperature⁵,等等。

以下函数有助于使用 Matplotlib 以递增的度数绘制模型。多项式 _ 要素()函数将要素输入排列成一个 Numpy 数组(X 矩阵),然后,plot_regression()函数使用 OLS 函数计算最佳β值,计算 MSE,并绘制结果。

2 度模型—按作者

这个型号更合适!简单地将数据点的平方值相加改进了简单的线性回归模型。如果我们不断向 X 矩阵中添加越来越多的列,那么我们将越来越好地拟合数据。

小心点。我们增加线条的复杂度越多,过度拟合训练数据的风险就越高。如果我们得到一个新的数据点样本,我们希望确保模型也能很好地适应这些数据点。我们需要选择一个模型,该模型选取数据的一般模式,而不是训练数据的随机模式。解决这个问题的一个简单方法是使用一个验证数据集。我们可以使用这个数据集在“看不见的数据”上测试模型性能。我们还将设置一个阈值,MSE 必须达到该阈值才能继续增加复杂性。在我们的例子中,我们将不断增加输入特征的程度,直到 MSE 不增加 0.02,然后我们将停止训练。这是通过下面的代码块完成的。

最后,最能概括新数据的模型是三阶线性回归模型。我们可以在下图中直观地看到增加线的复杂性是如何影响模型的。

模型进展-按作者

然后我们可以画出最佳模型,看看它在验证集上的表现如何。在下面的图中,我们可以看到模型的 MSE 验证分数约为 0.70,训练 MSE 分数约为 0.75。我们可以说这个模型对新数据的推广效果很好。

最终 3 级模型—作者

关闭思路

这种模型类型是机器学习的构建模块之一。模型背后的数学非常直观,它是一个可解释的模型,并且可以产生强有力的预测。在本文中,我们证明了我们可以使用线性回归来模拟水的盐度和温度之间的关系。

线性回归已被证明在预测学术界和工业界的价值方面是有用的。也就是说,线性回归只是冰山一角。还有其他模型类型可以在其预测中包含多个输入变量。这些模型的例子包括多元线性回归、决策树和随机森林。您可以使用这些模型中的一个,通过温度和纬度来预测盐度。是时候去另一个兔子洞探索这些模型了!

这篇文章的代码可以在这里找到。

参考文献

在整篇文章中,我使用了各种资源来帮助撰写本文。谢谢大家的分享!

卡尔科菲数据集 —索希尔·戴恩

OLS 直观解释 —维克多·鲍威尔,路易斯·乐和

什么是线性回归? — IBM

MSE vs RMSE —奥莱利

7 种常见的回归算法 —张秀坤·波尔泽

线性回归(第三部分)——基本假设

原文:https://towardsdatascience.com/linear-regression-part-3-the-underlying-assumptions-82a66d5d5dd5

理解最流行的数据科学算法所基于的支柱(前提)的来龙去脉

照片由 Lucas SantosUnsplash 上拍摄

我们知道,当我们试图通过在一组独立变量(称为预测器)之间建立线性关系来预测一个变量(称为因变量)的值时,需要线性回归。在这篇文章中,我想谈谈线性回归模型背后的关键假设。然而,如果你在想‘为什么要回归?’‘线性回归背后的科学/数学基础是什么?请查看下面的帖子。

请看下面的链接,链接到一篇关于为什么需要回归的早期文章。

..一个解释线性回归背后的科学/数学。

任何模型或任何程序都带有一堆假设,这些是必须为真的条件,模型才能被视为成功匹配将这些视为“如果 a,b,c 为真,则 x,y,z 成立”一类的陈述,当其为真时,意味着我们可以应用这种统计技术(即拟合我们的线性回归模型)。

如果你读过关于线性回归的书籍/文章,你可能会发现不同数量的假设——这是因为有些假设非常基本,并且通常文本假设你已经按照设计对它们进行了分类。然而,下面我列出了所有的假设。
请注意,假设 1 至 6 是关键假设,然后 7–10 是隐含假设或衍生假设。

假设#1 —参数的线性

什么意思?

要应用线性模型,观测数据中需要有实际的线性。也就是说,因变量和自变量必须有一个线性关系 (相对于像二次关系这样的非线性关系)以便我们在它们之间拟合一个线性模型。

你知道吗? 这并不意味着总是 Y 和 X 需要线性相关;可以是 Y 与 X 平方相关,然后我们可以在 Y 和 X 平方之间(而不是 Y 和 X 之间)拟合一个线性模型。

比如下图-1 中;在场景-1 中,Y 和 X 不是线性相关的,但是存在某种关系。在操作 X(通过查看 X-cube 的相同数据)之后,我们在场景 2 中看到 Y 和 X-cube 是线性相关的。因此,Y 和 X 的变换(在这种情况下是 X 立方体)是线性相关的。
→我们可以在 Y 和 X-cube 之间应用线性回归模型。

图片-1 |线性关系(图片由作者提供)

这个假设也可以理解为:‘参数的线性度‘,对于一个解释变量的一个单位变化,在因变量的值中总有的一个常数变化

这个为什么重要?

如果没有线性关系,那么建模和应用线性模型就没有意义。对!

怎么找?

我们可以通过将 Y 对所有解释变量作图来找出线性关系。
请注意,如上所述,我们可能有 X、X-square、X-cube、log(X)……X 的任何变换作为解释变量,我们正在寻找因变量 Y 和解释变量之间的线性关系(即模型中使用的 X 的变换与仅 X 的变换)。

假设# 2——观察的独立性

什么意思?

这种假设指的是收集的数据点或观察值相互独立的事实,根据这些数据点或观察值创建回归模型。如果数据是以独立的方式收集的,那么在您的数据集中这应该是最常见的情况。

你知道吗?
为个人收集的购物行为数据通常是独立的,因为一个人的行为不会依赖于其他人。相反,时间序列数据往往会导致观察结果相互依赖,因为一个数据点与基于时间的前一个值相关。

为什么这个 很重要?

不独立通常意味着有一个潜在的模式;即观察值来自特定来源 (例如,时间序列数据——同一来源在不同时间点的观察值)。

当我们的数据不独立时,我们看到:

  • X 的观测值与自身相关
  • X 和 Y 相互相关(这是我们回归的前提)
  • 依次暗示, Y 的观测值与自身相关

在我们回归的数据中存在这些聚类或类,这种潜在模式或类间相关性确保来自相同类的多个观察值不会带来任何附加信息 (因为它们来自相同的源)模型的估计变得不太精确
例如,考虑基于 100 个数据点估计学校中儿童的身高和体重之间的关系。你会看 100 个不同的孩子或者 20 个孩子的身高体重测量 5 次吗?

怎么找?

这高度依赖于数据收集的方式。如果我们的数据单元彼此不相关,即每个单元的数据点独立于其他单元的数据值,那么可以安全地假设观察值的独立性。

假设# 3-无多重共线性

什么是多重共线性?

多重共线性是多重抑制模型中的现象,其中一个或多个独立变量彼此密切相关。也就是说,当一个或多个解释变量之间存在高度相关性时,多重共线性就存在。

线性回归假设解释变量之间没有完美或精确的关系。

为什么这很重要?

当两个或多个自变量相关时,它们不会添加任何额外的信息来解释因变量,而是添加了噪声,因为太多的变量传达了相同的信息。在回归模型中出现多重共线性的情况下,由于两个预测值之间的高度相关性,模型无法了解两个变量对因变量的影响程度 (因为两个预测值传达相同的信息,即以相同的方式影响因变量)
→这进一步导致系数估计值和模型给出的 p 值变得非常不可靠。随着多重共线性的严重程度增加,这些有问题的影响也会增加。

由于回归模型的关键目标之一是建立因变量和自变量之间的关系,多重共线性不会让这种情况发生,因为模型描述的关系(具有多重共线性)变得不可信 (因为多重共线性变量的β系数和 p 值不可靠)

你知道吗?
多重共线性对模型如何拟合没有重大影响,即 R 平方和预测因变量值在很大程度上不受影响。然而,自变量和因变量之间的关系显然是。

怎么找?

  • 相关矩阵:
    为模型中使用的 n 个预测值准备一个‘n×n’相关矩阵,这使我们了解了每个预测值之间的相关性。为了使多重共线性最小化,相关系数必须具有非常低的值
  • 容差或方差膨胀因子(VIF):
    另一种了解一个预测因子是否与其他预测因子相关或者是否可以被其他预测因子解释的方法是通过使用考虑中的这个预测因子作为因变量,其余所有预测因子作为自变量,运行线性回归。如果模型非常适合,即高 R 平方,那么它意味着考虑中的预测因子 可以被其他预测因子很好地解释。换句话说,考虑中的预测因子与其他预测因子密切相关,并且存在多重共线性。
    下图-2 解释了我们如何为每个预测值定义容差和 VIF,并得出关于多重共线性存在的结论。

图片-2 |从公差和 VIF 解释多重共线性(图片由作者提供)

关于残差的假设..

残差定义如下:误差= y _ 实际值—Y _ 预测值

残差是误差项,我们的模型无法解释它。因为没有一个模型是完美的,所以每个模型中都会有残差。然而,如果我们认真思考一个好的模型必须做什么,我们就会明白,在一个好的模型中,大多数因变量应该由模型来解释(即自变量),残差项只是数据无法解释的随机位。

让我们从这个角度来看下面关于残差的假设。

假设# 4-残差应为独立同分布随机值

什么意思?

这意味着剩余的残差是独立且同分布的随机值。让我们一个一个地思考这个问题。

  • 残差是随机值:
    回想一下,我们将在训练数据上拟合一个回归模型,它将始终是总体人口数据的子集(因为我们几乎不可能拥有整个人口数据)。如果我们选择不同的训练数据,模型拟合将会不同(即使略有不同),因此,残差将会不同。
    →由于使用的训练数据是随机的,因此模型产生的残差也是随机的。
  • 残差同分布:
    这意味着对应于每个数据行的每个预测的残差具有相同的概率分布。
  • 残差是独立的:
    这有两个部分,都很重要:

1。残差与自身无关- 一次观测的残差不应该影响下一次观测的残差。这就是所谓的自相关。

自相关是一个变量与自身相关的现象,即值依赖于先前的值。

2。残差与解释变量无关- 残差是随机值(如上所述)不得与模型中使用的解释变量(X) 相关。因为如果它们是,那么它意味着有一些信息,驻留在剩余项中,我们的模型不能捕捉到。因此,我们的模型并不适合。

→如果残差可以预测,无论是通过残差本身还是通过解释变量,该信息都应该进入模型。否则,该模型不是很适合,因为它没有捕获这些信息。

为什么这很重要?

如果剩余项不是独立且同分布的随机值,这可能意味着下列情况之一为真:

  • 线性回归模型未能捕捉到所有重要的独立变量。

  • 线性回归模型未正确指定;也就是说,因变量和自变量之间没有内在的线性关系。

  • 在数据中**存在多重共线性,这导致模型系数不稳定,进而导致残差项相互依赖。

    →因此,为了使我们的模型被认为是一个合适的可概括模型,上述所有情况都必须不存在。**

怎么找?

检查自相关:
自相关的存在意味着残差项与上一个或下一个残差项相关。简单地说,当数据的行中存在模式时,就会发生自相关
我们可以看看剩余项与其滞后值的
相关性(Lag(1),Lag(2),..滞后(x)值)并绘制成图
请参见下面的图片-3。X 轴对应于残差的滞后,以 1 为步长增加。第一行(左侧)显示残差与其自身的相关性(Lag0),因此,它将始终等于 1。如果残差
不是自相关的,从紧邻的下一行开始的相关性(Y 轴)将下降到虚线(显著性水平)以下的接近零值。如果残差是自相关的,那么我们会看到残差项与其滞后值的高度相关性远远超出了可接受的界限。

图 3 |绘制残差与多个滞后残差值的相关性(虚拟数据)(图片由作者提供)

永盒杜宾-沃森测试也可用于检查自相关。(我将在以后的文章中详细介绍这些内容)

检查与解释变量无关系: 这可以通过查看残差与所有 X 变量的相关系数来检查。我们期望看到残差项与解释变量的相关系数的低值,表明它们之间的关系很弱。
我们可以前进一步,对观察到的相关系数进行显著性检验,看它们是否与零显著不同。显然,如果样本量较大,观察到的相关系数可能在统计上是有效的。

皮尔逊相关、肯德尔等级相关、斯皮尔曼相关和点双列相关是根据变量类型检测两个变量之间相关性的一些常用方法。(我会在以后的文章中详细介绍这些内容)

假设# 5-残差正态分布,平均值为 0

什么意思?

它有两个组成部分,两者齐头并进:
(1)残差为正态分布
(2)残差的均值为 0

如果回归模型是好的,这意味着自变量在预测因变量方面做得很好,并且大多数预测非常接近因变量的实际值。

  • 换句话说,你的模型的大部分预测误差接近于零大误差比小误差少得多。
    →这就是正态分布的样子。
  • 另外,残差的整体影响必须抵消,即残差对因变量的正面和负面影响必须相互抵消。
    →这意味着残差的平均影响(或均值)接近 0。

残差~ N (0,σ平方);即残差正态分布,平均值为零。

为什么这个 很重要?

甚至不严格要求残差的正态性。如果残差不是正态分布的,你的回归模型不会出什么大问题。常态只是一个可取的性质。然而,如果模型是好的,残差必须相互抵消,并且必须以 0 为中心

你知道吗? 线性回归模型要求解释变量和响应变量呈正态分布,这是一个常见的误解

怎么找?

检查残差的正态性:
我们可以将
残差项绘制在直方图上,检查它们是否正态分布。请看下图-4(a)描绘了一个正常的情节。
另一种方法是创建一个
分位数-分位数图** (QQ 图),绘制观察到的残差分布与理论正态分布。绘制的数据点是分位数,如果残差分布与理论正态分布对齐,我们将看到大多数点位于 45 度参考线上。请参见下图-4(b)。**

图片-4 |检查残差(虚拟数据)的正态性(图片由作者提供)

记住中心极限定理,它说随着样本量的增加,分布趋于正态。可能会偏向分布的末端。在现实生活的数据中很难得到完美的曲线和分布。

Kolmogorov-Smirnov、Shapiro-维尔克和 Anderson-Darling 检验是检查变量正态性的一些常见检验。
(我将在以后的文章中详细讨论这些问题)

检查残差的均值是否以 0 为中心:
寻找残差的分布,应该在 0 附近,实际均值应该每接近 0。

假设# 6——残差中的同方差

什么意思?

同方差—Homo =相等,同方差=分散
我们在上面已经了解到,残差是独立同分布的随机值,服从均值为 0 的正态分布。这里,我们施加了一个额外的约束,并且说
残差的变化不应该是因变量(y)的函数,并且通过扩展,不应该是解释变量(X)的函数****

异方差是指残差对于 X 的所有值都不具有恒定方差的现象(与同方差相反,这意味着残差对于所有 X 都具有恒定方差)

让我们试着理解更多,异方差意味着残差有更大的变化,这个变化依赖于 x。

  • 这意味着实际残差是独立变量的函数。****
  • 这也意味着,残差包含一些独立变量没有捕捉到的信息

为什么这个 很重要?

根据异方差的定义,剩余值随 X 的变化更大;这意味着预测随着 X 值的增加或减少而变得更差,即模型在 X 的范围内并不同样可靠或同样拟合(注意,当残差显示出更大的变化时,模型更不可靠)
此外,它通常会导致相关变量的系数估计值的方差增加,但回归模型并没有考虑到这一点。
模型更有可能将系数报告为显著;事实上可能不是。

显然,异方差导致模型不利于总体的泛化
→我们必须在模型中有同方差。

**你知道吗? 观察数据中异方差的常见原因有:

  • 从你的模型中缺失重要变量
  • 存在影响模型拟合的异常值
  • 模型的函数形式不正确(即在没有线性关系的情况下模拟线性关系)**

怎么找?

****残差和回归预测值之间的散点图是检查数据是否同方差(意味着残差在回归线上平均分布)的好方法。见下图-5 左侧,残差的分布或方差随着回归预测值的增加而保持一致。反之;在右边,我们看到残差的分布(或方差)随着我们在预测回归值(X 轴)上从左向右移动而增加。
请注意,在少数情况下,可能会绘制残差和回归预测值的标准化值,以了解分布情况

图片-5 |检查同质性:残差与拟合值散点图(虚拟数据)(图片由作者提供)

Goldfeld QuandtBreusch-Pagan 测试也可用于检查同异方差。(我将在以后的文章中详细介绍这些内容)

隐含假设..

假设# 7-回归方程被正确指定。

这直接来自线性假设,即如果 Y 与 X 平方有实际的线性关系,使用 X 平方(而不是 X)作为解释变量是有意义的。

假设# 8——X 的可变性是正的

这直接来自于观察值彼此独立的事实,即彼此不相关。也就是说,所有观测值的解释变量的值并不相同。x 有可变性。

假设#9 —剩余项与 X 无关

这直接来源于这样一个事实,即剩余项是独立且同分布的随机值。“独立性”意味着,它们与 X 变量完全无关。

假设#10—观察值的数量多于预测值的数量

我们必须有大量的观察数据来构建我们的模型,而且这些数据应该远远多于所使用的解释变量的数量。

摘要

为了让任何模型或数学/统计框架成立,底层数据需要遵循某些准则或原则。在线性回归模型的情况下,这些被称为“假设”,对于应用于任何数据的线性回归框架,这些假设必须成立。下面是一个线性回归模型的所有假设的详细清单。请注意,1-6 是关键,7-10 是派生的或更隐含的。

  1. ****参数中的线性度
  2. ****观察值独立的方式收集。
  3. ****解释变量之间无多重共线性或多重共线性极小
  4. 剩余项是独立同分布的随机变量
  5. 残差项为正态分布** (非必选)均值以 0 为中心。**
  6. 模型中剩余的项之间不存在异方差。****
  7. 正确指定了回归模型。
  8. X 的可变性是正的。
  9. 剩余项和 X(解释)变量之间没有关系。
  10. 观察值的数量大于变量的数量。

保持联系..

如果你喜欢这篇文章并且对类似的文章感兴趣 在 Medium 上关注我加入我的邮件列表(..如果你已经不是了..)跳上成为 中型家族的一员获取数以千计的有用文章。(如果你使用以上链接,我将获得你约 50%的会员费)**

..不断学习,不断成长!

线性回归直观且更容易理解

原文:https://towardsdatascience.com/linear-regression-visualized-and-better-understood-c8f7b9c69810

可以检查一下自己是否真的懂多元线性回归

线性回归是最简单的算法之一。特别是当只有一个连续的特征变量:y=ax+b,并且用一条直线表示的时候。这种可视化表示对于理解模型是什么非常有用。

现在,你想知道在多变量的情况下,模型的可视化表示是什么吗?如果有多个连续变量,加上分类变量是什么?

在本文中,我将通过不同的案例,用具体的 R 代码来绘制图表。在看答案之前,你可以试着想象以下几种情况:

连续变量

  • 两个连续变量
  • 三个连续变量

分类变量

  • 只有一个二元变量
  • 一个变量有三个类别
  • 一个变量有 n 个类别
  • 两个二元变量

混合变量

  • 一个连续变量和一个二元变量
  • 两个连续变量和一个二元变量
  • 一个连续变量和一个 n 类离散变量

注意隐患

当想象视觉表象时,请记住它总是线性的,笔直的,平坦的…不是弯曲的,不是非线性的…

Unsplash 上的卡斯滕·沃思拍摄的照片

连续变量

对于一个连续的变量,众所周知,线性回归是一条直线。然而,开始对其进行具体的编程和可视化是一个很好且容易的起点。

x=runif(100,1,10)
y=2*x+rnorm(100,0,1)data=data.frame(x=x,y=y)fit_lm=lm(y ~ .,data = data)ggplot(data,aes(x, y))+geom_point()+
 geom_abline(slope=fit_lm$coefficients[2],
 intercept = fit_lm$coefficients[1],
 color=”red”)

一个连续变量的线性回归(图片由作者提供)

对于两个连续变量,让我们称它们为 x1 和 x2,分别为系数 a1 和 a2,那么等式为:y = a1x1 + a2x2 + b。由于总共有 3 个连续变量(x1、x2 和 y),我们必须想象一个三维空间:x1 和 x2 轴可以代表地面,y 轴代表高度。对于地面上的每一点,我们可以为 y 确定一个高度。所以最后,我们得到一个平面

x1=runif(100,1,10)
x2=runif(100,1,10)y=2*x1+3*x2+rnorm(100,0,2)data=data.frame(x1=x1,x2=x2,y=y)fit_lm=lm(y ~ x1 + x2,data = data)plot3d(x=data$x1,y=data$x2,z=data$y, type = 'p')planes3d(fit_lm$coefficients["x1"], fit_lm$coefficients["x2"], 
         -1, -fit_lm$coefficients["(Intercept)"],
         col = 'red', alpha = 0.6)

两个连续变量的可视化线性回归(图片由作者提供)

对于三个连续变量,我们无法具体地将其形象化,但我们可以想象它:它将是一个四维超空间中的一个空间。

分类变量

对于一个二元变量,我们回到我们的简单方程:y = ax + b。在现实世界中,它可以代表性别,对不同的特征是或否。实际上,我们必须对它进行一次性编码,它可以取值为 0 或 1。然后我们就可以计算 y 的值了,如果 x = 0,那么 y = b,如果 x = 1,那么 y = a + b .所以直观的表示就是两点。我们可以很容易地证明,对于 x 的每个值,它们都是 y 的平均值。

为了形象化,我们仍然可以用直线来表示,但实际上在具体使用模型时,x 只等于 0 或 1。

x=c(rep(1,50),rep(0,50))
y=2*x+rnorm(100,0,1)data=data.frame(x=x,y=y)fit_lm=lm(y ~ .,data = data)ggplot(data,aes(x, y))+geom_point()+
   geom_abline(slope=fit_lm$coefficients[2],
               intercept = fit_lm$coefficients[1],
               color="red")

一个二元分类变量的线性回归(图片由作者提供)

现在,对于具有三个类别的分类变量,我们还可以创建虚拟变量,在实践中,我们将有两个特征。所以我们必须使用 3D 绘图。

与前一种情况一样,虚拟变量的组合会有几个点。我们可以创建一个平面来更好地可视化。我们知道可能的点在一个平面上,因为对于两个连续变量,方程是相同的:

y = a1x1 + a2x2 + b

在前一节连续变量中,x1 和 x2 是连续的,在这里,它们是二元的。

x=as.factor(sample(c(0,1,2), replace=TRUE, size=100))data=data.frame(model.matrix(~x))data$y=2*data[["x1"]]+3*data[["x2"]]+rnorm(100,0,2)fit_lm=lm(y ~ x1 + x2,data = data)plot3d(x=data$x1,y=data$x2,z=data$y, type = 'p')planes3d(fit_lm$coefficients["x1"], fit_lm$coefficients["x2"], 
         -1, -fit_lm$coefficients["(Intercept)"],
         col = 'red', alpha = 0.6)

具有 3 个类别的分类变量的线性回归(图片由作者提供)

值得注意的是,没有 x1=1 和 x2 =1 的观测值,因为 x1 和 x2 来自一个唯一的分类变量,不可能同时为真。

所以线性回归的表示是空间中的三个点。很容易证明它们是 x1 和 x2 可能组合的平均值。

现在,如果有两个二元变量呢?除了 x1=1 和 x2 =1 的观测值之外,情况非常相似。该表示仍然是一个平面,包含二进制变量的 4 种可能组合中每一种的点。值得问的问题是:

平面是否用平均值切割 4 组观测值?

答案是否定的,我们用收缩来证明。

  • 条件 1:如果它们是平均值,则空间中将有 4 个点
  • 条件 2:所有点都应该在一个平面上因为我们有等式:y = a1x1 + a2x2 + b

上述两个条件不可能同时成立,空间中的 4 个点不一定在一个平面上。因为我们知道条件 2 总是真的,所以条件 1 不总是真的。

混合变量

对于一个连续变量和一个二元变量,我们可以用两种不同的方式想象。设模型为 y = a1x1 + a2x2 + b,x1 连续,x2 分类。

  • 由于我们有两个连续变量的平面,如果其中一个特征变量是二元变量,那么对于空间中的一维,我们只有 0 和 1,而不是可能的值,那么我们有两条直线在 3D 空间中。而且你认为他们一定是并联?答案是肯定的,因为它们肯定来自一架飞机。
x1=runif(100,1,10)
x2=sample(c(0,1), replace=TRUE, size=100)y=2*x1+3*x2+rnorm(100,0,2)
data=data.frame(x1=x1,x2=x2,y=y)fit_lm=lm(y ~ x1 + x2,data = data)plot3d(x=data$x1,y=data$x2,z=data$y, type = 'p') 
planes3d(fit_lm$coefficients["x1"], fit_lm$coefficients["x2"], 
         -1, -fit_lm$coefficients["(Intercept)"],
         col = 'red', alpha = 0.6)

一个连续变量和一个二元变量的线性回归(图片由作者提供)

  • 由于有两条直线,我们可以把它们投影到连续特征变量 x1 和 y 形成的平面上,这样就得到两条平行的直线。一条直线代表 x2=0 时的模型,斜率为 a1,截距为 b;另一个会代表 x2=1 时的模型,斜率永远是 a1,截距是 a2+b。
ggplot(data,aes(x1, y,color=as.factor(x2)))+
   geom_point()+
   geom_abline(slope=fit_lm$coefficients["x1"],
               intercept = fit_lm$coefficients[1],
               color="red")+
   geom_abline(slope=fit_lm$coefficients["x1"],
               intercept = fit_lm$coefficients[2]+
                  fit_lm$coefficients[3],
               color="#06D1D5")+
   labs(color="x2")

一个连续变量和一个二元变量的线性回归(图片由作者提供)

对于一个连续变量加一个分类变量3 个分类或者 n 个分类,我们将无法表示整个空间,我们可以表示连续变量和 y 形成的平面上的投影,平行的直线会有 n 条。

结论

现在,我们可以对所有问题给出答案,以此作为结束:

连续变量

  • 一个连续变量:直线
  • 两个连续变量:计划
  • 三个连续变量:空间

分类变量

  • 只有一个二元变量:两点(类别平均值)
  • 一个变量有三个类别:三个点(类别平均值),它们在一个平面上,因为否则不可能
  • 一个变量有 n 个类别:n 个点(类别平均值)
  • 两个二元变量:4 个点(不是平均值),它们不在一个平面上

混合变量

  • 一个连续变量和一个二元变量:两条平行的直线(每个类别一条线)
  • 两个连续变量和一个二元变量:两个平行平面
  • 一个连续变量和一个 n 类离散变量:n 条平行直线

如果你想在 Excel 中从头实现线性回归,可以阅读这篇文章:

如果您对其他算法的同类可视化感兴趣,请发表评论。

线性回归是一个非常基本的算法,正如你可以看到的所有可视化,如果数据不是线性的,它不会执行得很好。为了更好地模拟非线性数据,我们可以用几种方法来增强线性回归。你可以通过这篇文章了解更多关于无监督机器学习算法

线性回归与逻辑回归:区别在哪里?

原文:https://towardsdatascience.com/linear-regression-vs-logistic-regression-ols-maximum-likelihood-estimation-gradient-descent-bcfac2c7b8e4

成本函数、普通最小二乘法(OLS)、梯度下降法(GD)和最大似然估计法(MLE)方面的差异。

路线图(图片由作者提供)

在本文中,我将介绍统计模型开发的路线图。具体来说,我们将讨论线性回归逻辑回归的发展有何不同。我们还将讨论为什么一些方法在一个模型中有效,而在另一个模型中无效。以下是该路线图的概述。

第一步:设计一个模型,解释响应变量和解释变量之间的关系

  • 回归模型:响应变量为连续
  • 分类模型:响应变量为分类

第二步:开发一个成本函数(又名损失函数)来确定这个模型对一组给定数据的解释有多好

  • 根据普通最小二乘法( OLS )开发一个成本函数
  • 根据最大似然估计开发一个成本函数

第三步:寻找最佳参数,解决关于代价函数的优化问题

  • 解析方法:如果优化问题可以解析求解,则找到一个闭合解
  • 数值逼近法:使用梯度下降迭代求解

如何在线性回归中应用普通最小二乘法(OLS)?

首先,我们用下面的数据来谈谈线性回归。目标是开发一个模型来解释作为连续变量的响应变量(即 Y)和解释变量(即 X)之间的关系。

作者图片

步骤 1 :基于 Y 和 X 之间的线性关系,一个线性方程,如下所示,将是一个合适的模型。

等式 1

参数(β0,β1)是通常未知的固定值,因为很难从总体中收集所有数据点。因此,我们使用从样本计算的 OLS 估计量(β̂0,β̂1)来估计总体参数(β0,β1)。

误差项 ε 是一个随机变量,它用每个点到拟合直线的距离来解释误差。误差项的值可以是正的也可以是负的。总体来说ε 需要有一个 0 的均值,可以用截距(即β0)来实现。ε不是需要有一个正态分布与 OLS 产生一个无偏模型。

然而,正态假设允许我们进行统计假设检验。估计值(例如,β̂0、β̂1)可以写成ε的线性组合(参见此处的更多细节),假设ε呈正态分布,则意味着估计值也呈正态分布。因此,正态假设将为预测和估计生成可靠的置信区间。

第二步:我们想出一个成本函数,它将量化这个建议模型有多好。对于线性回归模型,我们找到和的最佳估计,使得易和之间的距离之和,其中= β̂1Xi + β̂0,是最小化的。通常,在成本函数中使用二次距离**。我们可以用下面的等式来总结。

等式 2

如何在线性回归中应用最大似然估计?

上面的代价函数是我们能为线性回归设计的唯一函数吗?答案是否定的。或者,我们可以根据最大似然估计 (MLE)得出一个不同版本的成本函数。

MLE 是一种在给定观测数据的情况下估计模型参数的常用方法。在假设的统计模型下,我们可以通过最大化观察一组给定数据的可能性来找到最佳参数。

第一步:我们将从与 OLS 相同的线性模型开始(等式 1)。这里唯一的修改是我们需要对最大似然估计的误差项施加正态假设,因为我们需要假设数据的分布来计算可能性。

等式 3

第 2 步:相反,我们可以提出一个不同版本的成本函数,它将量化在给定解释变量(例如 X)的情况下,在建议的模型下我们观察到结果变量(例如 Y)的可能性有多大。我们希望找到最佳参数,β̂ 1 和β̂ 0,这样观察我们数据的可能性最大化

让我们首先将等式 3 改写为 X 和 y 的每个观测值的条件分布。换句话说,每个 Yi 都来自正态分布,对于给定的 Xi,其均值为β1Xi + β0,标准差为σ。

接下来,我们根据正态分布定义了观察易给定的似然函数。

每个点 I 都是独立且同分布(iid)的,所以我们可以把观测所有点的似然函数写成每个个体概率密度的乘积

这就是似然函数。通常,我们会将其转换为一个对数似然函数,因为最大化似然函数相当于最大化对数似然函数,而对数似然函数在数学上更容易处理。

等式 4

在等式 4 中,你会注意到圈起来的项都是常量并且在求和前面有一个负号

因此,最大化对数似然函数在数学上等价于最小化 OLS 的成本函数(见等式 2)

多酷啊!我们从关于 OLS 和最大似然估计的完全不同的想法开始,以线性回归模型的相同的成本函数结束。

第三步:如何使用封闭形式的公式最小化线性回归的成本函数?

寻找最小化或最大化一个函数的值的过程称为优化。对于机器学习的任何优化问题,你可以用分析方法数值近似方法来处理。

分析方法是确定性的,这意味着有一个封闭形式的解决方案来解决优化问题。可以写下精确解的数学表达式。

然而,在统计建模中很难获得封闭形式的解决方案。线性回归是极少数情况之一。

如果最优化问题是一个线性方程,应该有一个解析解。我们应该可以用一点点微积分推导出公式。

为了找到一个函数的最大值或最小值,我们可以对每个参数求导,并将它们设为 0。解方程会给我们最好的参数。

让我们推导公式。

我们的成本函数是误差平方和。

我们对β̂0 求导,设为 0。

解上面的方程,我们得到

等式 5

现在对β1 求导,用等式 5 代替β0,并将其设为 0。

解上面的方程,我们得到

现在我们可以应用一些代数技巧来改写分子和分母,这很容易证明。

从β1 和β0 的公式中可以看出。对于给定的数据集,我们很可能有一个线性模型的封闭形式的解决方案。我们也可以做矩阵形式的所有代数。你可以在这里找到它的衍生

请记住,多元线性回归的封闭解有时会有问题,其中有多个解释变量(例如,X1、X2、X3 等),因为逆矩阵仅在 X 具有满秩时存在,这意味着如果存在完美多重共线性,它就不会有封闭解。为了防止这种情况,我们需要在线性模型中排除这些变量。

第三步:如何利用梯度下降最小化代价函数?

我们已经讨论了线性回归的解析解。接下来说一个数值逼近法,叫做梯度下降,解决优化问题。

在数学上,梯度下降法是一种一阶迭代优化算法,用于寻找可微函数的局部极小值。典型的梯度下降遵循下列步骤。

  1. 最初,我们可以让β1 和β0 为任意值(例如,β1 = 0 和β0 = 0)。设 L学习率。l 确定我们将应用于更新参数的变化的幅度。学习率越大,代价函数越快接近最小值。如果 L 太大,你的优化器将会越过“曲线”,错过最小点。相反,如果 L 太小,将需要很长时间才能达到最小值。因此,我们需要有策略地设定学习速度。
  2. 计算关于每个参数的成本函数的偏导数(我已经在上面讨论过了)。将β1,β0,,Yi 的值插入每个偏导数中,并计算它们的值。导数值将决定这些参数变化的方向。
  3. 使用以下公式更新参数

4.重复步骤 2 和 3,直到参数停止变化或导数值足够接近零。

作者图片

请记住,如果在线性回归模型中有效,分析方法会给我们精确解。然而,梯度下降只能给出一个非常接近精确解的解。幸运的是,在大多数情况下,我们无法注意到这些差异。

梯度下降是机器学习中解决优化问题的流行工具。其算法简单,易于应用于大多数成本函数中。然而,它也有限制*,例如,它不能在非凸函数中工作,这我们将在下一节讨论。*

线性回归在分类问题中有用吗?

简单的回答是没有。

我们知道线性回归的结果变量是一个连续变量。它能否处理分类结果变量,例如,动物的种类——狗对猫、物体检测、火车对飞机、垃圾邮件检测、垃圾邮件对非垃圾邮件?显然不能,因为我们开发的线性方程的预测结果变量可以取无限个值。分类问题的预测结果只能取个有限的个值,例如 2 个或少量的值。

这并不意味着线性回归对分类问题没有用。事实上,我们可以开发一个基于线性回归的新模型,使用逻辑回归处理分类问题。

什么是逻辑回归?

简单来说,我们可以把 Logistic 回归分解成两部分,回归和 Logistic。

回归:线性回归模型用于估计对数(又名对数比数*)的值*

  • 设 P 为特定事件发生的概率(例如,电子邮件是垃圾邮件)。赔率被定义为“特定事件发生的概率”与“该事件不发生的概率”之比。对数赔率就是赔率的对数值。

  • 我们可以用线性模型来表示对数几率

  • 然后我们可以解出 P

逻辑:我们也可以把逻辑回归模型想象成把一个线性回归模型输入到一个逻辑函数*(又名 sigmoid 函数)中。逻辑回归函数将范围从∞到+∞的 logit 值(即βXi)转换为范围从 0 到 1 的 Yi 值。*

作者图片

现在我认为我们有了一个合适的分类模型。我们可以为 Y 设置一个阈值(例如 0.5)来确定结果变量。比如 Y ≥ 0.5 就是垃圾邮件,Y < 0.5 就不是垃圾邮件。

为什么我们不用 OLS 的误差平方和(SSE)作为逻辑回归的成本函数呢?

一旦我们有了一个合理的模型来解决分类问题。接下来,我们需要提出一个成本函数。让我们试试线性回归模型中使用的平方和误差函数。

在逻辑回归中,是否存在最小化平方和误差的解析解?简单的回答是否定的。

我们可以很快得出结论,这是一个非常困难的数学问题。函数的复杂性和非线性使得不可能找到封闭形式的解。

我们可以使用梯度下降来最小化逻辑回归中的平方和误差吗?简单的回答也是否定的。

使用梯度下降来最小化成本函数。我们需要确保成本函数是一个凸的函数。如果我们试图在逻辑回归中使用平方和误差的成本函数,它最终将是一个具有许多局部最小值的非凸函数。寻找一个非凸函数的全局最小值本身就是一个非常困难的数学问题。

作者图片

基于极大似然估计和交叉熵的 Logistic 回归

由于误差平方和不能作为逻辑回归的代价函数,我们是否可以用 MLE 方法来寻找代价函数?简单的回答是肯定的。

使用最大似然估计,我们希望在逻辑回归模型下,在给定 X 的情况下,最大化观察 Y 的似然性。

假设 P 是给定 x 的特定事件(例如,电子邮件是垃圾邮件,用 Y =1 表示)发生的概率。那么 1-P 将意味着给定 x 的 Y=0 的概率。

因此,对于一个给定的观察对象(即易和),我们想

  • 如果 Y =1,P 最大化
  • 如果 Y =0,最大化 1-P

如果我们稍微扭曲一下,我们想

  • 如果 Y= 1,最大化 Y * P
  • 如果 Y = 0 ,最大化(1-Y)(1-P)。*

然后让我们结合上述两个功能,我们想

  • 最大化 YP + (1-Y)(1-P)**

让我们应用对数函数,将最大化成本函数改为最小化成本函数的反函数,我们希望

  • 最小化-[Ylog(P) + (1-Y)log(1-P)]**

接下来我们可以把观察所有点的似然函数写成每个点的似然的乘积。然后我们可以把每个可能性的乘积的 log 重写为每个可能性的 log的和。

我们给上面的代价函数起了个好听的名字,二元交叉熵。

最小化逻辑回归中二元交叉熵的代价函数

logistic 回归中是否存在最小化二元交叉熵的解析解?简单的回答是没有。

类似于平方和误差,二进制交叉熵的复杂性和非线性使得不可能找到封闭形式的解。

可以用梯度下降来最小化 logistic 回归中的二元交叉熵吗?简单的回答是肯定的。

二元交叉熵是一个凸函数,通过梯度下降保证找到全局最小值。这些步骤与线性回归模型中的步骤相同。

酷的是逻辑回归的梯度下降公式中的偏导函数与线性回归的偏导函数相同。

超越传统的线性回归

在线性回归模型中,我们可以改变成本函数来构建不同的模型,从而在线性模型中产生一组不同的最佳参数。

例如,在岭回归模型中,我们有一个成本函数

在套索回归模型中,我们有一个成本函数

在弹性网回归模型中,我们有一个成本函数

线性回归的这三个变量与正则化相关联,正则化是一种惩罚模型的灵活性和复杂性的技术,以防止过度拟合的风险。这里λ是一个超参数,用于确定模型的灵活性将受到多大的惩罚。我们可以使用交叉验证网格搜索来调整它的值。

超越传统的逻辑回归

当两个类别的样本大致相同(T21)时,传统逻辑回归效果最佳。然而,现实世界中有许多情况并非如此。

例如,在信用卡交易数据中可能有非常少量的欺诈交易。使用逻辑回归,这个阶级不平衡的问题会变得棘手。如果只有 0.1%的交易是欺诈性的。我们可以简单地预测所有正常的交易,最终达到 99.9%的准确率。多棒啊!但是这种模型是没有用的,因为我们无法预测任何欺诈交易。

为了解决此类不平衡问题,我们可以将二元交叉熵的成本函数修改为

这里 α1 和α2 将决定我们惩罚类错误分类的严厉程度。它们的值通常计算为数据中它们频率的倒数

回到我们的例子,α1 = 1 / 0.1% = 100,α2 = 1/99.9% = 1。因此,我们对欺诈交易的处罚将是正常交易的 100 倍。因此,这一小小的修改将大大提高检测欺诈交易的准确性。

最终注释

线性回归和逻辑回归分别是处理回归和分类问题的两种广泛使用的模型。了解它们与普通最小二乘法和最大似然估计相关的基本形式将有助于我们理解基本原理,并探索它们的变体来解决现实世界的问题,如模型选择和不平衡类。

此外,了解成本函数优化问题背后的数学对于更有效地实现模型也很重要。请记住,只有当数据可以线性解释时,解析解才可用。否则,诸如梯度下降的数值近似方法更受欢迎,以找到机器学习模型中的最佳参数。

如果你对线性回归因果推断感兴趣,这里有一些相关的帖子可以浏览。

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请 订阅

线性回归:平方和是多少?

原文:https://towardsdatascience.com/linear-regression-what-is-the-sum-of-squares-3746db90a05d

深入了解平方和对数据科学的重要性

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

最近,我写了一篇关于线性回归及其在数据科学中的应用的文章。作为一个总的概述,我没有深入研究线性回归使用的工具或方法的太多细节。一个这样的工具是平方和。起初,我打算在前一篇文章中简单解释一下。然而,有几个不同的公式用来定义平方和,所以用一两句话来限定有点困难。相反,因为我发现答案很有趣,我想是时候写另一篇博客了。记住这一点,在今天的文章中,我们将着眼于平方和。我们将首先描述什么是平方和以及为什么使用它,然后我们将看看需要的公式以及它们的作用。因此,不再拖延,让我们更深入地研究平方和。

平方和是多少?

平方和不仅用于描述数据点和线性回归线之间的关系,还用于描述该线描述数据的准确程度。您使用一系列公式来确定回归线是否准确地描绘了数据,或者该线有多“好”或“坏”。

一个重要的注意事项是确保您的数据首先描述的是回归而不是相关性。这里有一个简单的清单来找出不同之处:

  • 回归将强调一个变量如何影响另一个变量,而不仅仅是变量之间的关系。
  • 相关性不能捕捉伤亡,而回归是基于它的。这很重要,因为它应该显示因果关系,而不是联系的程度。
  • 在相关性中,x 和 y 之间的相关性与 y 和 x 相同。在回归中,x 和 y 与 y 和 x 会产生不同的结果。
  • 最后,相关性用图形表示一个点,而回归用图形表示一条线。

现在我们对平方和有了更多的了解,让我们来看看需要的公式。

平方和总计

我们要看的第一个公式是总平方和(表示为 SST 或 TSS)。TSS 找出每个变量和平均值之间的平方差。

易=第集第 i

ȳ =集合中所有项目的平均值

这意味着对于每个变量,你取值并减去平均值,然后求结果的平方。这就给出了从直线到每个特定变量的距离。你也可以将 TSS 描述为观察变量在均值或方差周围的分散程度。因此,TSS 的目标是测量数据集的总可变性。

平方和回归

我们要讨论的下一个公式是回归平方和(表示为 SSR),也称为解释平方和(表示为 ESS)。SSR 用于描述因变量的预测值和均值之间的差异。

ŷi-由回归线估计的值

ȳ——样本的平均值

首先,我们将再次需要均值。估计值是位于回归线上的值。也就是说,不用每个变量的实际值,取该变量在回归线上的值。这将告诉我们这条线与数据的吻合程度。如果 SSR 与 TSS 匹配,那么这条线就是完美的匹配。

平方和误差

要讨论的最后一个公式是误差平方和(SSE),也称为残差平方和(RSS)。SSE 查找变量的观察值或实际值与估计值之间的差异,这是根据回归线应该有的值。

其中:

yi——观察值

ŷi-由回归线估计的值

在完美拟合的情况下,误差将为 0,这意味着估计值与实际值相同。任何大于 0 的值都表示误差,或者根据这些值,线不准确的程度。该值越低,回归线就越符合数据。高残差和将表明该模型很难代表数据。

既然我们已经解释了这三者,我们可以表示它们的关系:

结论

在今天的文章中,我们谈到了平方和。首先,我们描述了它是什么以及为什么使用它。接下来,我们列出了相关和回归的区别。最后,我们查看了使用的公式(TSS、SSR 和 SSE),并找到了一个公式来表示它们之间的关系。我希望平方和更清楚一点,并且你发现这个描述是有帮助的和有趣的。我们不一定需要手动计算所有的公式。像 R 这样的语言有计算每个公式的函数,所以你可以确定回归线是否合适,而不需要额外的工作。我希望你喜欢这个解释,一如既往,我会看到你在下一个。干杯!

用我的 每周简讯 免费阅读我的所有文章,谢谢!

想看完介质上的所有文章?成为中等 成员 今天!

看看我最近的一些文章:

https://python.plainenglish.io/getting-started-with-seq-in-python-4f5fde688364 https://medium.com/codex/javascript-cdns-and-how-to-use-them-offline-e6e6333491a3 https://medium.com/codex/something-i-learned-this-week-vue-js-in-asp-net-core-mvc-7b7540a38343 https://python.plainenglish.io/5-python-libraries-to-use-everyday-d32a9de13269

参考资料:

https://corporatefinanceinstitute.com/resources/knowledge/other/sum-of-squares/ https://365datascience.com/tutorials/statistics-tutorials/sum-squares/ https://365datascience.com/tutorials/statistics-tutorials/correlation-regression/

拥抱脸线性回归

原文:https://towardsdatascience.com/linear-regression-with-hugging-face-3883fe729324

这是对用变压器实现线性回归分析的可行性的探索

斯科特·韦伯摄影:https://www.pexels.com/photo/shapes-and-pattern-2346594/

介绍

众所周知,研究人员使用拥抱脸变形器在文本分类和文本聚类问题上花费了大量的努力。您是否想过可以在线性回归项目中利用预先训练好的转换器?拥抱脸 API 居然支持!我将带您看两个例子,并在最后分享一些见解。

数据

首先,我找到了一个包含数百篇带分数的批改过的作文的数据集。这些文章是根据每月的选择撰写的,旨在训练巴西高中生参加大学入学考试。数据集有 695 篇文章,我省略了缺少值的行或total_score = 0处的记录。经过预处理,100 篇文章被删除。

巴西论文数据

第二个数据集是一个女装电子商务数据集,围绕着顾客写的评论。它的九个支持特性提供了一个很好的环境来从多维度解析文本。原始数据集有 23,486 条客户评论,没有缺失值。

客户评论数据

密码

仅供参考,整个项目是在谷歌 Colab 进行的。在我们开始实验之前,让我们检查一下GPU是否可用,因为没有GPU整个过程会非常慢。

作者图片

然后,让我们导入我们需要的所有库:

步骤 1:准备数据

在这一步中,我们需要分割数据,对文本进行编码,并将数据转换成 torch 数据,以便进行下游建模。这与我为分类问题准备数据的过程是一样的。

注意:由于我们训练的是一个线性回归模型,在 getitem 函数中,我们需要将 ***label*** 转换为浮点型。请参考第 26 行。

步骤 2:加载模型

这是最重要的一步。根据拥抱脸的库,当我们从拥抱脸 API 加载预训练模型时,将 AutoModelForSequenceClassification 的num_labels设置为 1 将触发线性回归,并自动使用MSELoss()作为损失函数。你可以在这里找到原剧本

步骤 3:为回归创建计算指标

logits实际上是我们的预测。但是,在我们的语境下看起来很怪异。想了解更多关于logits的信息,这里有一个来自哥大的很好的学习资源: Logit/Probit 。尽管默认的损失函数是MSE,我在这里再次添加损失函数,以验证损失函数是否正确实现。

第 4 步:构建培训师

巴西作文分数预测

我们需要为每个项目从拥抱脸中选择一个合适的预训练模型。由于巴西的论文都是用葡萄牙语写的,我选择了“neuralmind/bert-large-portuguese-cased” 作为基本模型。

作者图片

因为文章是长文档,所以平均长度比大多数预训练模型的默认最大长度长;我只是将max_length 设置为 512 来捕捉最多的信息。test_size设定为 20%。

经过几次实验后,我发现这个模型很难捕捉到分数和论文之间的任何线性关系,因为R2 分数等于负数。因此,我决定对目标变量进行log10转换。由于模型很难收敛,我运行了 100 个时期的Trainer,在第 44 个时期找到了最好的模型。

即使最好的R2只有 0.4643(这意味着只有大约 46%的总分的log10可以用短文嵌入来解释。),我们可以看到一个明显的学习趋势(R2 = -2.4783表示短文的语义嵌入根本不能解释总分的log10。).

客户评审项目

因为这个项目的所有东西都是用英语写的,所以我简单地选择了‘bert-base-uncased’作为基础模型。我将前 3000 条评论作为测试集。从经验上来说,将更大百分比的数据子集化为验证数据可以提高测试数据上的微调模型的性能。因此,我将这个任务的test_size设置为 66%。因为客户评论是短文本数据,所以对于这个项目,我将max_length设置为 126。

正如我在开始时提到的,这个问题听起来更像一个多类分类问题,而不是一个线性回归问题,我添加了一个accuracy度量来代替SMAPE。下面是计算方法。

我训练了五个纪元的模型。最好的accuracy (0.6694)在第三时段获得,而最好的R2 (0.6422)RMSE (0.6589)在第二时段获得。

现在,我们开始最后一步:让我们在测试数据集上测试微调后的模型。

输出如下所示。我要说的是,与模型在验证数据集上的表现相比,结果还算不错。

完整代码脚本:Transformers _ Linear _ regression . ipynb

外卖

  1. 拥抱脸 API 通过设置num_labels = 1通过 ForSequenceClassification 接口支持线性回归。problem_type 将自动设置为‘regression’
  2. 由于线性回归是通过分类函数实现的,因此预测有点混乱。根据这个文件从抱紧脸(第 140 行到第 162 行),他们会在problem_type == “multi_label_classification” or num_labels == 1时对输出应用sigmoid函数。此时我最好的解释是当problem_type == ‘regression’ ,后处理功能被触发来设置function_to_apply = ClassificationFunction.NONE,所以没有额外的功能应用于输出。
  3. 当连续值很大时,模型很难学习目标(参考我在第一个例子中发现的,而我输入的目标值像 700,1000 等。模型给我的预测是 15.6,16.8,…)。在这种情况下,对数转换可能会有所帮助。

相关阅读

参考

拥抱脸。(2021).modeling _ Bert . py .https://github . com/hugging face/transformers/blob/7 ae6f 070044 b 0171 a 71 f 3269613 BF 02 FD 9 FCA 6 f 2/src/transformers/models/Bert/modeling _ Bert . py # l 1564-l 1575

拥抱脸。(2021).text _ class ification . py .https://github . com/hugging face/transformers/blob/7 ae6f 070044 b 0171 a 71 f 3269613 BF 02 FD 9 FCA 6 f 2/src/transformers/pipelines/text _ class ification . py # L140-L162

拉爪哇研发(2022)。使用 BERT 和变压器的文本输入回归。https://lajavaness . medium . com/regression-with-text-input-using-Bert-and-transformers-71c 155034 B13

OLS 线性回归:异方差性和自相关性

原文:https://towardsdatascience.com/linear-regression-with-ols-heteroskedasticity-and-autocorrelation-c12f1f65c13

用一点数学知识理解 OLS 线性回归

作者图片

异方差自相关是我们在建立线性回归时需要解决的不可避免的问题。在本文中,让我们更深入地了解什么是异方差和自相关,什么是后果,以及处理问题的补救措施。

典型的线性回归采用如下形式。响应变量(即 Y)被解释为解释变量(如截距、X1、X2、X3……)的线性组合,而 ε 是误差项(即一个随机变量),代表拟合响应值和实际响应值之间的差异。

图 1(作者图片)

什么是同方差?

同方差的假设下,误差项应该有常数方差和 iid。换句话说,误差项的方差-协方差矩阵中的对角线值应该是常数,非对角线值应该都是 0。

图 2(作者图片)

什么是异方差?

在现实世界中,同质性假设可能不合理。误差项的方差可能不会保持不变。有时误差项的方差取决于模型中的解释变量。

例如,卧室的数量通常用于预测房价,我们看到 6+卧室的房子比 2-卧室的房子预测误差更大,因为 6+卧室的房子通常比 2-卧室的房子价值高得多,因此,有更大的未解释的和有时不可约的价格方差,这漏入了误差项。

图 3(作者图片)

我们称其方差在观测值间不恒定的误差项为异方差误差。这个特性被称为异方差

图 4(作者图片)

什么是自相关?

当模型中存在自相关时,误差项是相关的。这意味着误差项协方差矩阵的非对角值不全是 0。

图 5(作者图片)

有一些可能的自相关来源。在时序数据中,时间是产生自相关的因素。例如,当前股价受到前几个交易日的价格影响(例如,股价在大幅上涨后更有可能下跌)。在横截面数据中,相邻单元倾向于具有相似的特征。

异方差和自相关的结果是什么?

只要 零条件均值的假设(即误差项的期望值对解释变量的所有值都是零条件的)成立,即使在异方差和自相关的情况下,OLSE 也保持无偏

图 6(作者图片)

异方差或自相关下的 OLS 估计量 不再具有所有线性无偏估计量中最小的方差 因为 高斯-马尔可夫定理要求同方差。

所以异方差或自相关下的 OLS 估计量不再是蓝色的。与同方差下相比,OLSE效率不高

图 7(作者图片)

由于 OLS 估计量的方差在异方差或自相关下是无效的,统计推断可能会提供误导性结果

异方差和自相关的补救方法是什么?

补救措施 1:异方差一致性(HC)和异方差自相关一致性(HAC)标准误差

在异方差或自相关的情况下,我们仍然可以使用 低效的 OLS 估计量,但许多文献建议使用异方差一致(HC)标准误差(也称为稳健标准误差,白色标准误差)或异方差-自相关一致(HAC)标准误差(也称为纽西标准误差),以允许异方差或自相关的存在(见图 7)。这些是最简单和最常见的解决方案。

许多计量经济学家认为,我们应该始终使用稳健的标准差,因为我们永远无法依赖同伦方差。

补救 2: 广义最小二乘法(GLS)和可行 GLS (FGLS)

代替接受低效的 OLS 估计器和校正标准误差,我们可以通过使用广义最小二乘法(GLS)使用完全高效的估计器(即,无偏和具有最小方差)来校正异方差或自相关。

在异方差或自相关情况下,尽管 OLS 估计量和 GLS 估计量都是无偏的,但 GLS 估计量的方差比 OLS 估计量的方差小。

如果存在异方差或自相关,并且我们或者知道误差项的方差-协方差矩阵,或者可以根据经验估计它,那么我们可以将其转换成一个同方差模型。

图 8(作者图片)

图 9(作者图片)

问:变换后的模型是齐次的吗?

答:是的,转换后的模型中的误差项具有恒定方差和 iid。

图 10(作者图片)

转换后的模型满足同方差假设,因此,转换后模型的 OLS 估计器(即 GLS 估计器)是有效的。GLS 估计量可以计算为

图 11(作者图片)

如果我们知道σ2ωσ的值,我们可以将它们的值代入一个封闭解中,找到 GLS 估计量。

如果我们不知道σ2ω或σ的值,那么这个百万美元的问题就是“我们能估计它们的值吗?”答案是肯定的。处理这种情况的一个常见办法是采用可行的 GLS ( FGLS )。

如何在异方差下应用 FGLS?

正如伍尔德里奇的《计量经济学导论:现代方法》中所讨论的,我们可以假设

图 12(作者图片)

我们姑且称之为 u 的估计,FGLS 模型中的权重, W,(又名,加权最小二乘估计(WLS) )。

校正异方差的一种可行的 GLS 方法:

第一步:让 OLS 照原样运行,获得残差,即 Ui hat。

图 13(作者图片)

第 2 步:我们首先平方残差,然后取自然对数,从而创建一个新变量。

图 14(作者图片)

第三步:在 Xs 上回归这个新创建的变量,然后预测它们的拟合值。

图 15(作者图片)

第四步:对第三步得到的拟合值取幂,称之为权重 w,然后创建一个新的矩阵 p,(即 N×N 矩阵)

图 16(作者图片)

图 17(作者图片)

第五步:通过乘以新矩阵 p 来变换 Y 和 X。

图 18(作者图片)

步骤 6 :对转换后的模型应用 OLS,我们得到的β hat 将是一个有效的 GLS 估计量。

图 19(作者图片)

如何在自相关下应用 FGLS?

对于大多数具有自相关的时间序列数据,一阶自回归扰动(即 AR(1))校正就足够了。我们有

图 20(作者图片)

图 21(作者图片)

第一步:让 OLS 照原样运行,得到剩余向量 e

图 22(作者图片)

第二步:用 r 估计ρ,然后创建一个新的矩阵 p,(即 N×N 矩阵)

图 23(作者图片)

第三步:通过乘以新矩阵 p,对 Y 和 X 都进行变换,第一次观测与其他观测不同。对于我们的应用,我们可以忽略第一个观察值(即 t=1)。

图 24(作者图片)

步骤 4 :对变换后的模型应用 OLS,获得 GLS 估计量。

图 25(作者图片)

最终注释

当线性回归模型中存在异方差时,误差项的方差不是常数;当存在自相关时,误差项的协方差不为零。

在异方差或自相关的情况下,OLS 估计量仍然是无偏的,但不再有效,这意味着它不会有最小方差。

为了解决异方差或自相关问题,我们可以获得稳健的 OLS 估计量的标准误差,或者使估计量更有效,我们可以进一步获得 FGLS 的 GLS 估计量。

如果你对线性回归因果推断感兴趣,这里有一些相关的帖子可以浏览。

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,点击这里

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。

OLS 线性回归:无偏、一致、蓝色、最佳(有效)估计量

原文:https://towardsdatascience.com/linear-regression-with-ols-unbiased-consistent-blue-best-efficient-estimator-359a859f757e

用一点数学知识理解 OLS 线性回归

作者图片

已知 OLS 估计量是无偏一致(最佳线性无偏估计量)。但是这些属性是什么意思呢?为什么它们对线性回归模型很重要?在本文中,我们将讨论这些属性。

典型的线性回归如下所示。响应变量(即 Y)被解释为解释变量(如截距、X1、X2、X3……)的线性组合,而 ε 是误差项(即随机变量),代表拟合响应值和实际响应值之间的差异。

图 1(作者图片)

为了让 OLS 发挥作用,我们需要确保一些假设是正确的。

假设 1- 参数的线性度:线性模型中的参数是线性的。

图 2(作者图片)

此外,OLS 估计量也是线性的,我们可以将 OLS 闭合解改写如下(将图 1 中的 Y 代入图 3)。矩阵代数只在线性存在的情况下有效。因此,线性假设是成立的。

图 3(作者图片)

图 4(作者图片)

假设 2- 随机抽样:观测数据代表 iid 独立同分布)随机样本,遵循总体模型(见图 1)。如果数据是跨部门收集的,我们需要确保它们是随机抽样的。底线是观察到的数据应该代表总体数据

假设 3- 无完美共线性:任何解释变量都不能表示为其他解释变量的线性组合。原因是逆矩阵(在图 3 中)仅在 X 具有满秩时存在,这意味着如果存在完美的共线性,它将没有封闭形式的解决方案。

假设 4- 零条件均值:误差项的期望值对解释变量的所有值都是零条件的(即 E[ ε |X] = 0)

假设 5- 同方差无自相关:误差项应具有恒定方差和 iid。换句话说,误差项的方差-协方差矩阵中的对角线值应该是常数,非对角线值应该都是 0。

假设 6- 误差的正态性:误差项呈正态分布。这个假设对于 OLS 方法的有效性来说并不是必需的,但这允许我们有一个可靠的估计标准误差,并做出有意义的统计推断。

β vs β^ vs E(β^)

你可能在统计学教科书上看到过 β (如β,β,E(β))的一些变体。我们来讨论一下它们的定义和区别。

β 是一个概念值——解释总体数据中解释变量和因变量之间关系的(通常还有未知)参数值(即常数值)。

在大多数情况下,我们不会使用人口数据,因为它不可用或太大而无法处理。因此,我们将使用样本数据(具有有限个观察值)来开发我们的线性回归模型。

随机抽样的假设下,观测样本数据代表大小为 n 的同分布随机样本,遵循总体模型。假设我们有多组样本数据(通过从总体中反复抽取样本),并在每个数据集中分别运行模型。

在给定的样本数据集中,我们将有一个 OLS 估计量, β^ ,它可以用封闭形式的解决方案来解决(图 3)。

很有可能我们会在不同的数据集中得到一组不同的估计量(即β^) 。因此, β^是一个 随机变量。基于中心极限定理, β^抽样分布具有均值,随着样本量的增加,均值收敛于 β

E(β^) 是这个随机变量β.的期望值通俗地说,如果我们在多组样本中运行线性模型,不断记录估计量的值,取一个平均值。平均值就是期望值,E(β).

OLS 估计量是无偏的

有限样本属性下,我们说 OLS 估计量是无偏的,这意味着 OLS 估计量的期望值E(β^) 将等于真实总体参数β

无偏性难道而非意味着我们从观测数据(即一组随机样本)中得到的 OLS 估计量将等于精确总体参数值,因为由于不可约误差项ε ,线性模型仍然不能完全解释这种关系。

相反,无偏性意味着,如果我们对来自同一总体的不同随机样本集重复运行线性回归模型,那么估计量的预期值将等于真实值总体参数,如下所示。

图 5(作者图片)

虽然我们从观测数据中得到的 OLS 估计量不等于精确总体参数值,但是只要观测数据是总体数据的良好代表,并且线性模型在假设下被正确指定,那么我们从观测数据中得到的系数估计量应该非常接近真实总体参数值。

否则,如果观察数据不是总体数据的良好代表,模型将遭受测量误差、线性模型由于常见问题没有正确指定(例如,省略变量或内生性),那么我们从观察数据得到的系数估计值将有偏差

OLS 估计量是一致的

渐近性质下,我们说 OLS 估计量是一致的,这意味着随着样本量变大并趋向于无穷大,OLS 估计量将收敛总体参数****

从 Jeffrey Wooldridge 的教科书,计量经济学导论,C.3,我们可以证明,如果假设成立,随着样本量变大,OLS 估计量的概率极限将等于真实总体参数。

图 6(作者图片)

  • 当 E[ ε |X] = 0 成立时,暗示 Cov(X,u) = 0,那么图 6 中的第二项等于 0。我们已经证明,随着样本容量的增大,OLS 估计量会收敛到真实的总体参数。因此 OLS 估计量是一致的。
  • 如果 Cov(X,u) ≠ 0,那么我们有一个不一致的估计量。不一致的问题不会随着样本量的增加而消失。同时,OLS 估计量也有偏差。
  • 如果 Cov(X,u) > 0 表示 X 与误差项正相关,则渐近偏差向上。
  • 如果 Cov(X,u) < 0 meaning x is negatively correlated with the error term, then 渐近偏差向下。

你可能想知道为什么我们对大样本的属性感兴趣,比如一致性,而实际上我们的样本是有限的。

答案是,如果我们可以证明当样本量变大时,估计量是一致的,那么我们可能会对有限样本中的估计量更加自信和乐观。另一方面,如果一个估计量是不一致的,我们知道这个估计量在有限样本中是有偏的

OLS 估计量是有效的****

为了评估线性回归模型的估计量,我们根据其偏差和方差来使用其效率。

  • 无偏但没有最小方差的估计量不是最好的。
  • 方差最小但有偏差的估计量不是最好的
  • 无偏且方差最小的估计量是最好的(有效的)。
  • OLS 估计量是最好的(有效的)估计量,因为在所有的线性无偏估计量中,OLS 估计量具有最小方差

图 7(作者图片)

我们可以用一点矩阵运算来证明高斯-马尔可夫定理。

图 8(作者图片)

图 9(作者图片)

现在我们已经证明了 OLS 估计量的方差比任何其他线性无偏估计量都小。因此,OLS 是最好的(有效的)线性估计。

最终注释

  • 如果估计量的抽样分布的期望值等于真实总体参数值,则估计量是无偏的。
  • 如果随着样本量的增加,估计值趋于无穷大,那么估计值是一致的。换句话说——一致性意味着,随着样本量的增加,估计量的抽样分布在总体参数值处变得更加集中,方差变得更小。
  • 在 OLS 假设下,OLS 估计量是蓝色的(所有线性无偏估计量中方差最小的)。因此,它是最好的** ( 高效的)估计器。**

如果你对线性回归因果推断感兴趣,这里有一些相关的帖子可以浏览。

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里。

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。

虚拟助手对话中人类语言的语言特征

原文:https://towardsdatascience.com/linguistic-features-of-human-speech-in-dialogues-with-a-virtual-assistant-ca961ab3a272

人们和聊天机器人说话的方式和和其他人说话的方式不同吗?

通常,机器学习方法,当然还有基于规则的方法被用来创建虚拟助手。两者(大多是机器学习)都依赖于输入数据,通常是人类对话。这还没有考虑到对话系统的用户不会像和真人一样和他们交流的因素。

在本文中,我将考虑我们对人与人和人与聊天机器人对话文本进行统计分析的实验。对话以俄语的新冠肺炎常见问题(FAQ)为主题。使用定量文本分析方法对结果进行处理(这些度量组包括:可读性、句法复杂性、词汇多样性)。统计分析显示上述指标存在显著差异。特别是,受访者在与聊天机器人对话时使用了更短的单词和句子以及更简单的语法。此外,在人机对话中,词汇多样性和可读性指数更高,所有这些都表明人们在与虚拟助手交流时使用更简单的语言。在这方面,开发人员应该特别注意为创建这样的应用程序准备源数据。

介绍

众所周知,如今虚拟助手(尤其是聊天机器人)已经积极地进入了我们的生活。他们在银行、政府网站和我们想找乐子的时候跟着我们。尽管我认为 Gartner 的预测到 2022 年 70%的白领每天都会和聊天机器人聊天并没有完全实现,但是聊天机器人在我们生活中的应用趋势是显而易见的。在我看来,虚拟助手之类的技术是中性的。根据它掌握在谁的手中,它的有用程度或敌对程度可能会有所不同。在这篇文章中,我建议将重点放在虚拟助手有用的一面,即快速准确地获得特定问题答案的能力。

作为一个例子,考虑一个FAQ-聊天机器人回答用户关于新冠肺炎的问题。对话数据的收集发生在 2021 年 3 月到 2021 年 7 月,即在疫情的一个高峰期。数据收集过程被设计成使得对两种不同类型的对话(人-人和人-聊天机器人)的分析可以在几乎“所有其他条件都相同”的情况下进行更多细节在下一章。

创建聊天机器人

F 首先,有必要收集一个问答格式的知识库。知识库的数据来自公开和官方的信息来源,如站点 stopkoronavirus.rf 。数据是手动构建的,以便可以训练适当的分类模型。作为技术解决方案,使用了 Google Dialogflow 平台及其意图分类模块。聊天机器人的界面是电报信使。在下图中,你可以看到聊天机器人的概念架构。

FAQ 聊天机器人的概念架构(图片由作者提供)

计划实验

F 为了实验,我们将回答者分为第一组(人对人对话)第二组(人对聊天机器人对话)。让我们从第二组(第二组)开始,因为那里的一切都很简单——回答者被邀请直接与聊天机器人进行对话。在这种情况下,我们介绍对话的主题和可能的问题。在第一种情况下(第一组),一切都很相似,除了受访者正在与人类专家对话,而不知道他或她正在使用相同的聊天机器人来回答问题。换句话说,专家是回答者和聊天机器人之间的代理人。这种类型的实验被称为颠倒的绿野仙踪。原版《绿野仙踪》的描述可以在这里找到。下图显示了我们的原始文章中的实验示意图。

实验方法(图片由作者提供)

本实验于 2021 年 3 月至 2021 年 7 月进行。结果,组 1(人-人)获得了 35 个对话,组 2(人-聊天机器人)获得了 68 个对话。所得数据被匿名化并用于进一步分析。源代码和数据在 Github 存储库中。在下一章中,我将给出测量指标的详细统计数据。

文本对话分析的结果

选择了以下组中的个指标来分析文本对话:

这些指标是使用 Python 库 LinguaF 计算的。计算指标的描述和技术可以在我们的原始文章中找到。下面是一个表格,其中包含计算出的指标平均值,以及 t-test 值,这些值允许我们确定不同类型对话(第 1 组和第 2 组)上相同指标的差异是否具有统计显著性(显著性水平α=0.01)。

显示文本间语言差异的度量计算值表(作者图片)

显然,对于大多数指标来说,我们有一个统计上的显著差异。让我们从简单的开始。衡量标准# 1——第一组对话的句子中的平均字数几乎是第二组对话的两倍。这也适用于衡量标准 2——平均句子长度。第一组的平均单词长度和单词中的音节数也略高。

句法复杂性的度量(平均依存距离)显示了句子中依存单词之间的平均距离,表明对于人与人的对话,该距离要大得多。这显然意味着第一组的句子结构更加复杂。

度量#6 和#9 的值的差异在统计上不显著。然而,词汇多样性的其他指标清楚地显示了第二组对话的滞后。所有可读性指标都显示了统计上的显著差异,这说明了第一组对话的文本结构更复杂。

调查结果和结论

在这项工作中,我们从数字上证实了人在与聊天机器人的对话中倾向于使用简化的语言结构。这既关系到单个单词,也关系到整个句子的结构。使用简体语言的事实推动了人们更仔细地收集用于创建聊天机器人的原始数据。

通常,机器学习方法,当然还有基于规则的方法被用来创建虚拟助手。这两种方法(主要是机器学习)都依赖于原始数据,这些数据通常是人类对话。这还没有考虑到因素,即基于对话系统的用户不会像与真人交流一样与他们交流。我们建议所有开发人员记住这个因素,并相应地修改您系统的数据和算法

如需引用,请使用:https://ICA IIT . org/proceedings/10 th _ ICA IIT _ 1/2 _ 2 _ perevalov . pdf

你在日常生活中多久使用一次聊天机器人?拜托,写在评论里吧!

使用 Node2Vec 的链接预测推荐引擎

原文:https://towardsdatascience.com/link-prediction-recommendation-engines-with-node2vec-c97c429351a8

在 Python 中使用节点嵌入进行链接预测

图片由 Preethi ViswanathanUnsplash 拍摄

本文将介绍链接预测背后的基本直觉,并展示一种基于嵌入的方法,说明如何使用链接预测来解决推荐系统问题。

我还将概述一个实践问题,并指导读者如何用 Python 实现所提出的解决方案。使用网络分析解决推荐系统中的问题时,这是一种常见的方法。

本文的重点是向读者展示如何使用 node2vec 嵌入和余弦相似性或 node2vec 嵌入和基于建模的方法来实现解决链路预测问题的算法方法。如果你对 node2vec 不熟悉,我以前写过一篇关于它的文章,你可以在下面看看。

推荐系统环境中的 Node2Vec 可用于基于邻居的应用。这是因为 node2vec 保留了网络的初始结构,来自 node2vec 的嵌入是量化是否应该有连接一对节点的边的好方法。

目录

  • 什么是链接预测?
  • 链路预测的应用
  • 问题陈述
  • 解决方案架构
    -需求
  • 数据
  • 创建网络
  • 应用节点 2Vec
  • 带距离测量的建议
  • 基于建模的建议
    -创建训练数据
    -训练模型
    -评估模型
    -生成预测
  • 结束语
  • 资源

什么是链接预测?

推荐引擎有很多解决问题的方法。这些解决方案包括算法方法、链接预测算法、基于嵌入的解决方案等。链接预测也称为图完成,这是图论中的一个常见问题。最简单的形式是,给定一个网络,你想知道一对节点之间是否应该有边。根据您使用的网络类型,此定义会略有不同。有向/多向图可以有稍微不同的解释,但是识别网络中缺失边的基本概念仍然存在。

给定一个图 G,让实线代表当前存在于图中的边,虚线代表应该存在于图中但当前不存在的边。图片由作者提供。

当处理时间网络(随时间变化的网络)时,链路预测中的问题也很常见。给定一个时间步长为 t 的网络 G,您可能想要预测时间步长为 t+1 的图 G 的边。

链路预测的应用

使用链接预测来解决问题的主要应用是在构建推荐系统的环境中。像亚马逊、脸书和推特这样的顶级公司很可能已经尝试/已经在他们的推荐引擎中实现了某种形式的链接预测。从尝试向他们的用户推荐产品,到推荐你可能感兴趣的其他用户。当您的网络随着时间的推移而变化和增长时,这更是一种有趣的解决问题的方法。

要知道,好的建议取决于你如何定义问题以及你想给出的建议。你必须记住影响用户行为的各种指标,这些指标反映了用户喜欢什么,不喜欢什么,与用户相关的兴趣以及这些东西是如何相互交织在一起的。

问题陈述

给定 arXiv 上发表的各种研究论文的主题数据,我们希望建立一个管道,根据它们的相似性将两篇论文连接起来。

解决方案架构

为此,我们将创建一个网络,其中节点作为文章,边基于连接这些文章对的所有主题连接这些节点。在创建这个网络时,我们将使用 node2vec 来生成与每篇文章相关的节点嵌入。最后,我将向您展示通过嵌入构建推荐系统的两种不同方法,首先是基于余弦相似性的算法方法。然后是基于建模的方法,该方法将允许我们训练二进制分类模型,该二进制分类模型将指示一对节点是否应该具有边。这将是一个监督学习模型,其中输入将是一对节点,输出将是二进制的,也就是说,节点是否应该连接到网络中。

代表建议的解决方案的体系结构。图片由作者提供。

要求

Python=3.8.8
arxiv=1.4.2
networkx=2.5
pandas=1.2.4
numpy=1.20.1
node2vec=0.4.4
sklearn=0.24.1

如果没有安装 node2vec 包,这里的是通过命令行安装的库文档。同样,你可以用 Python 安装 arXiv 包,使用下面的指令这里

数据

我们将使用 arXiv API 来收集与各种关键字相关的研究论文和出版物的数据。基于他们 API 的 arXiv 使用条款,它是完全免费的,并鼓励使用。有关其使用条款的更多信息,请参考其文档,您可以在此处找到

在本文中,我将向您展示如何通过 Python 调用 API 来收集我们今天构建的模型所需的以下信息。如果你想通过其他编程语言使用这个 API,或者只是想了解更多关于如何使用这个 API 的信息,我强烈建议你参考他们的文档,你可以在这里找到。

我们想要点击 arXiv API 来收集一些关于基于我们上面确定的查询的最新研究论文的信息。这将允许我们根据研究论文数据创建一个网络,然后我们可以尝试预测该网络上的链接。出于本文的目的,我将在每个查询中搜索最多 100 个结果,但是您不必为自己设置相同的约束。arXiv API 允许用户每次查询达到 300,000 个结果[1]。下面概述的函数将生成一个获取以下信息的 CSV:
title, date, article_id, url, main_topic, all_topics, authors, year
您可以获取更多类似于links, summary, article的信息,但我决定不这样做,因为这些特性不会真正用于本分析和教程。

下面是我点击 arXiv API 时得到的输出示例。根据您选择查询的关键字和时间,您可能会收到不同的信息。

点击 arXiv API 的示例 CSV 结果(图片由作者提供)

如果您在获取数据时遇到问题,我上传了一个示例 CSV 到我的 GitHub,供您下载、使用和跟随。这里可以找到

创建网络

现在我们已经使用 axXiv API 获取了数据,我们可以生成一个网络。该网络将具有以下结构,节点将是 article _ ids,边将是连接一对文章的所有主题。例如,具有以下主题astro-physicsstatistics的 article_id 1 可以连接到具有主题statistics的 article_id 10 和具有主题astro-physicsmath的 article_id 7。这将是一个多边网络,其中每条边的权重为 1。

以下是我生成的与网络相关的统计数据:

研究网络统计。图片由作者提供。

应用节点 2Vec

这个组件将包括在上面生成的图上运行 node2vec,并为该网络创建相关的节点嵌入。这些嵌入将在接下来的工作中发挥至关重要的作用,因为它们是构建链接预测模型所必需的主要功能。

以下是从研究网络生成的嵌入的样本输出。索引上的值对应于节点,而列代表每个嵌入维度。我选择生成一个 16 维的嵌入空间,因此列名从 0 到 15。

通过 node2vec 生成的网络嵌入的示例输出。图片由作者提供。

带距离测量的建议

现在我们有了一个表示网络中每个节点的嵌入向量,我们可以使用距离度量,如余弦相似度、欧几里德距离、曼哈顿距离等。来测量节点之间的距离。我们通过使用这些距离度量所做的假设是,彼此非常接近的节点也应该有一条连接彼此的边。这是一个很好的假设,因为 node2vec 试图保留原始输入图的初始结构。现在,我们可以编写代码,使用余弦相似性(或不同的距离度量)来测量两个向量之间的相似性水平,并识别当前在它们之间没有边但具有较大相似性的节点对,应该在它们之间创建边。对于多重/加权/有向图,这种解释可能不同。选择并使用一个适合你要解决的网络和问题的相似性度量。还要注意,不同的度量有不同的解释,对于这个问题,你要选择最大的余弦相似性得分,而如果你要使用像欧几里德距离这样的东西,你要选择两个向量之间的最小距离。

另外,我确实想提到,在解决这类问题时,维数灾难非常普遍。特别是当使用欧几里德距离来测量高维向量之间的距离时,这尤其成问题。术语“高维度”是广义的,可以有多种解释,一个维度“高”的阈值没有严格的定义,并且因问题而异。不去深究事物背后的数学,欧几里德距离对于稀疏或高维向量来说并不是一个好的度量。你可以参考 stack exchange 上的这两个帖子( post1post2 ),它概括了为什么会出现这种情况的数学推理。

关于维数灾难的更多信息,请参考华盛顿大学计算机科学系的这篇论文。

连接到文章 id 1 的推荐链接。这些是目前网络中不存在的链路。图片由作者提供。

基于建模的建议

在本节中,我们将构建一个二元分类器来预测一对边连接或不连接的概率。要做到这一点,我们首先需要识别所有可以形成边的节点对,并识别在原始网络中它们之间已经有边的那些节点对的子集。然后,我们可以组合与所有可能的边相关联的嵌入(通过向量相加),并将其传递到训练测试分割函数中,以生成训练和测试分区,并传递到分类模型中。

我将在本教程中使用的模型将是一个梯度推进分类器,对于你自己的问题和实验,我建议你尝试各种不同的分类器,并选择整体表现最好的一个。我们正在构建一个二进制分类器,但是在几乎所有的情况下,不管输入数据/网络如何,这个分类器都会存在类别不平衡。由于我们采用了网络中可能形成的所有可能的边的组合,结果是 N^N(其中 n 是网络中的节点数),它是指数级的大。唯一不会是二元分类器的方法是如果你的图几乎是完全连通的。

创建培训数据

火车模型

评估模型

当处理类不平衡时,我们不能用传统的准确性度量来评估模型的性能。这是因为如果模型连续预测具有更大数据量的类,那么不管模型从不预测另一个类,它仍将实现高水平的准确性。解决这个问题的方法是使用像马修斯相关系数这样的指标。

MCC 本质上是-1 和+1 之间的相关系数值。系数+1 表示完美预测,0 表示平均随机预测,而-1 表示反向预测。该统计也称为 phi 系数。【https://en.wikipedia.org/wiki/Phi_coefficient】---【3】

与梯度提升分类器相关联的训练和测试精度。图片由作者提供。

测试数据的 MCC 分数。图片由作者提供。

测试数据的分类报告。图片由作者提供。

测试分类报告。图片由作者提供。

生成预测

很明显,您可以看到,对于给定的任务,这是一个性能很差的模型(通过 MCC、ft-score、recall 和 precision 反映出来)。但这没关系,因为这篇文章只是出于教育目的。并不是你用来解决某个特定问题的每种方法都会成功,通常不是方法的问题,而是数据的问题。在本教程中,数据肯定是错误的,因为我只使用了一个实际研究网络的小样本,如果我从 arXiv 获取更多数据,就可以得到这个样本。但是这样做也会增加本文许多不同部分的计算复杂度(比如运行 node2vec、生成所有可能的边对、训练模型等。).

结束语

希望这篇文章揭示了如何通过节点嵌入构建一个链接预测管道。我已经研究了链接预测的算法方法和基于模型的方法。

请注意,本文涉及的主题并不简单,它们不应该是解决推荐系统问题的首选。大多数数据科学家在解决问题时遵循的经验法则是,如果简单的解决方案有效,那么更复杂的解决方案也应该有效。涉及协作过滤/基于内容的推荐系统中的较简单的解决方案通常很容易实现,并且产生相对信息丰富的结果以及更复杂的解决方案是否也能解决该问题的指示。由于各种原因(如训练时间、可解释性、模型推理时间等),通常用神经网络来解决问题(如 node2vec)并不是最好的方法。).

关于如何在推荐系统中实现更简单的解决方案,您可以参考我写的另一篇文章。

此外,如果你想在与这个项目相关的 jupyter 笔记本中进行跟进,你可以参考我的 GitHub

资源

如果你喜欢这篇文章,这里有一些我写的其他文章,你可能也会喜欢。

https://vatsal12-p.medium.com/mining-modelling-character-networks-part-ii-a3d77de89638 [## 贝叶斯 A/B 测试解释

towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a) https://pub.towardsai.net/dynamic-time-warping-explained-fbb24c1e079b

LinkedIn 数据科学家面试问题

原文:https://towardsdatascience.com/linkedin-data-scientist-interview-questions-e401b81a9208

在本文中,我们将解答一个常见的 LinkedIn 数据科学家面试问题,这些问题需要广泛的 SQL 技能

作者在 Canva 上创建的图片

我们正在解决的 LinkedIn 数据科学家面试问题有多种解决方法,但我们将通过一小步一小步来简化解决问题的过程。请继续阅读,看看我们如何利用一个可以应用于任何数据科学挑战的框架,将这个中等难度的 LinkedIn 数据科学家面试问题分解为更易于管理的部分。

LinkedIn 成立于 2003 年,目标是将世界各地的专业人士和专家联系起来,以促进他们的职业成功。LinkedIn 在全球拥有超过 7.5 亿会员,比任何其他社交网络都拥有更多专业人士,从刚刚开始职业生涯的人到所有财富 500 强公司的高管。在运营上,LinkedIn 是一家全球性公司,拥有各种各样的收入模式,涉及营销、人才、销售和会员解决方案。

LinkedIn 的数据科学家职位

LinkedIn 数据科学家职位通常为数据科学团队工作,该团队属于更大的数据工程部门。作为数据科学团队的一员,您将利用来自 7 亿多名成员的数据,为产品、销售、营销、经济、基础设施和其他团队提供见解。这些数据见解将推动战略,应用统计推断和算法进行优化,并构建工程解决方案来扩展 LinkedIn 的持续业务目标和长期公司愿景。

LinkedIn 的数据科学家职位需要具备 SQL、结构化和非结构化数据、分布式数据系统、R 或 Python 等编程语言、数据工作流等方面的技能。关于领英数据科学团队的更多信息,这里是他们的官方网页

LinkedIn 数据科学家面试问题中测试的概念

作者在 Canva 创建的图像

你在 Linkedin 数据科学家面试挑战中展示的主要 SQL 技能包括:

  • 对数据列使用基本算术运算
  • 使用子查询
  • 使用连接来组合数据表
  • 如何使用天花板功能
  • 如何将整数数据转换成浮点数据
  • 使用 WHERE 筛选出结果

我们接下来要看的问题需要这些概念中的一些知识,你将能够在 StrataScratch 平台上你自己的代码中应用它们,以更好地理解它们,并为面试做好准备。

LinkedIn 数据科学家面试问题

高风险项目

这个问题是在 LinkedIn 的一个数据科学家职位的面试中听到的。它的标题是“风险项目”,关键的挑战是使用来自不同集合的数据发现哪些 LinkedIn 项目超出了预算。

截图来自 StrataScratch

问题链接:https://platform . stratascratch . com/coding/10304-risky-projects

最终,问题是要求我们使用员工工资、项目长度和项目预算来确定哪些项目超出了预期。

这个 LinkedIn 数据科学家面试问题实际上是中等难度,而不是中等难度,因为正如我们将看到的,答案需要子查询、几个列计算和连接多个数据表。虽然有很多方法可以解决这个问题,但我们将重点关注一个灵活的解决方案,它展示了我们对几个重要 SQL 概念的了解。

解决问题的框架

回答数据科学问题时最重要的原则之一就是从框架入手。我们将概述一个三步框架,它为我们通过代码处理和解决这个问题建立了一个逻辑过程。我们正在使这个框架适应手头的问题,但是,通过一些简单的调整,你可以设计任何其他的问题。以下是我们的步骤:

1。了解你的数据:
a)。首先查看问题给出的所有列,并对数据做出一些假设。如果给你多张表格,记下每张表格给你的是哪些数据,以及你在回答问题时可能需要这些数据。
b)。如果您无法理解该模式,请尝试查看一些示例数据。查看前几行,尝试找出值与其各自列相关的原因。如果你一开始没有得到任何价值观,不要犹豫向 LinkedIn 面试官索要。查看示例数据可以帮助您确定解决方案的限制,或者确定是否需要针对边缘情况扩展解决方案。

2。制定你的方法:
一)。开始写下逻辑编程或代码步骤。不需要先按顺序。事实上,我们在这里提供的解决方案也不合适,因为在这个问题中,我们在获取工资之前会查询项目日期。
b)。确定执行计算时必须使用的主要函数很有帮助。尝试将问题陈述和数据转换成一系列 SQL 函数。
c)。想出解决办法的时候尽量不要沉默。你的 LinkedIn 面试官会想了解你是如何处理问题的。你可以要求他们澄清,并期望他们指定你是否必须从头开始写。

3。代码执行:
a)。重要的是以这样一种方式构建代码,以避免呈现过于简单或过于复杂的解决方案。利用子查询之间的空白空间来保持您的解决方案整洁有序。注释掉一个你以后想返回的代码块从来都没有坏处。
b)。遵循您在开始时概述的步骤。这将帮助你避免混淆,并确保你回答了所有的问题。
c)。最重要的是,仔细考虑你的函数和操作。使用这些井可以产生有效和通用的解决方案。
d)。与 LinkedIn 面试官交流你的代码。当你开始写作时,他们会评估你解决问题的能力。

了解您的数据

我们框架的第一步是检查我们的数据。通常情况下,你不会接触到实际数据,也不能像 LinkedIn 那样在面试中执行代码。通常,你必须理解数据,而不是根据面试官给你的模式和信息做出假设。

在这个 LinkedIn 数据科学家面试问题的例子中,这个问题为我们提供了三个表的模式,所以我们需要从查看每个表中的内容开始。

linkedin_projects

截图来自 StrataScratch

这个 linkedin_projects 表告诉我们一个项目已经运行了多长时间,它的预算和它的 id。

linkedin_emp_projects

截图来自 StrataScratch

linkedin_emp_projects 表显示了哪些员工参与了哪些项目。

linkedin_employees

截图来自 StrataScratch

Linkedin_employees 告诉我们每个员工的工资和他们的 id。因为我们需要知道工资、员工和项目 id、项目长度和项目预算来计算哪些项目超出了预算,我们将不得不使用 id连接所有三个表中的数据。在这一点上,我们还可以得出结论,我们将能够丢弃一些不相关的列,如项目标题和员工的名字和姓氏。

解决方案:

制定办法

根据我们的框架,我们想要概述一些翻译成代码的一般步骤。我们将保持它的高级别,稍后会变得更具体,但是最初概述这些步骤将使编写代码更容易。以下是一般步骤:

  1. 查询 linkedin_projects 并使用减法和浮点类型转换来获得以年为单位的总项目持续时间
  2. 当我们在员工 id 上连接 linkedin_emp_projects 和 linkedin_employees 表以获得每个项目 id 的年薪时,将我们的第一个查询放在一边
  3. 加入我们对项目 id 上的项目年薪的初始查询
  4. 通过将项目年薪乘以项目持续时间来计算按比例分配的员工费用,并使用 CEILING 函数来完成我们的输出
  5. 使用 WHERE 子句筛选超出预算的项目

计算项目工期

让我们将我们列出的一般步骤转换成功能性的 SQL 代码。对于第一步,我们首先查询 linkedin_projects 表,并从项目开始日期中减去项目结束日期。我们不必担心奇怪的差异,因为结束日期总是在开始日期之后。由于我们的工资数据以年为单位,我们还必须将项目长度转换为年的分数,并且需要将其转换为浮点小数,以避免结果四舍五入为 0。

SELECT id,
       title,
       budget,
       start_date,
       end_date,
       end_date - start_date AS project_duration
FROM linkedin_projects

截图来自 StrataScratch

我们有以天为单位的项目持续时间,所以我们要用它除以 365,得到以年为单位的项目持续时间,以便与年薪进行比较:

SELECT id,
       title,
       budget,
       start_date,
       end_date,
       (end_date - start_date)/365 AS project_duration
FROM linkedin_projects

截图来自 StrataScratch

因为我们除以整数,所以我们的计算向下舍入到 0,因为所有项目都小于年。让我们将分母 365 转换为浮点数,以获得以年为单位的项目持续时间的小数值。

SELECT id,
       title,
       budget,
       start_date,
       end_date,
       (end_date - start_date)/365::float AS project_duration
FROM linkedin_projects

截图来自 StrataScratch

现在我们将项目持续时间作为一年的小数部分。我们可以稍后使用这个值和项目中所有员工的集体年薪来获得项目总支出。

计算每个项目员工的年薪

在我们构建下一个查询时,请随意注释掉您现有的代码。在我们继续之前,有一个机会可以交互地测试一下上一节中的项目持续时间计算和本节中的每个项目的年薪查询:

SELECT *
FROM linkedin_emp_projects ep
JOIN linkedin_employees e ON ep.emp_id = e.id

问题链接:https://platform . stratascratch . com/coding/10304-risky-projects

由于我们需要知道一个项目中所有员工的年薪,我们的下一步包括将显示哪些员工在哪些项目中工作的 linked_emp_projects 与 linkedin_employees 中的员工薪金数据相结合,以获得每个项目的薪金。我们看到这两个表都有一个雇员 id 列,所以很明显要在这些 id 上连接它们。

请记住,我们需要合计每个项目的工资,所以我们也将按项目 id 分组。

SELECT project_id,
       SUM(salary) AS project_yearly_salary
FROM linkedin_emp_projects ep
JOIN linkedin_employees e ON ep.emp_id = e.id
GROUP BY project_id

截图来自 StrataScratch

我们现在有了项目年薪,很明显我们的下一步是将项目年薪数据集与 linkedin_projects 数据集相结合。

连接所有数据集

我们将取消对原始代码的注释,并加入 project_id 上的数据集。注意,为了完成连接,我们将我们的项目年薪数据集封装并命名。

SELECT id,
       title,
       budget,
       start_date,
       end_date,
       (end_date - start_date)/365::float AS project_duration,
       project_yearly_salary
FROM linkedin_projects
JOIN
  (SELECT project_id,
          SUM(salary) AS project_yearly_salary
   FROM linkedin_emp_projects ep
   JOIN linkedin_employees e ON ep.emp_id = e.id
   GROUP BY project_id) yearly_salary ON linkedin_projects.id = yearly_salary.project_id

截图来自 StrataScratch

这就完成了所有数据集的连接。在一张表上,我们有任何给定项目计算是否超出预算所需的所有信息。从这一点出发,我们可以丢弃一些列,比如 id 和日期,以简化我们的表。

SELECT title,
       budget,
       (end_date - start_date)/365::float AS project_duration,
       project_yearly_salary

截图来自 StrataScratch

计算按比例分摊的员工费用

在我们结束 LinkedIn 数据科学家面试问题的最后两步之前,还有一个机会来互动练习这个问题。看看您能否完成每个项目的费用计算,并确定哪些项目超出了预算:

SELECT id,
       budget,
       (end_date - start_date)/365::float AS project_duration,
       project_yearly_salary
FROM linkedin_projects
JOIN
  (SELECT project_id,
          SUM(salary) AS project_yearly_salary
   FROM linkedin_emp_projects ep
   JOIN linkedin_employees e ON ep.emp_id = e.id
   GROUP BY project_id) yearly_salary ON linkedin_projects.id = yearly_salary.project_id

问题链接:https://platform . stratascratch . com/coding/10304-risky-projects

按比例分配的员工费用是一个相当简单的计算,只需要我们将项目持续时间乘以项目年薪。

SELECT title,
       budget,
       (end_date - start_date)/365::float * project_yearly_salary AS prorated_employee_expense
FROM linkedin_projects
JOIN
  (SELECT project_id,
          SUM(salary) AS project_yearly_salary
   FROM linkedin_emp_projects ep
   JOIN linkedin_employees e ON ep.emp_id = e.id
   GROUP BY project_id) yearly_salary 

截图来自 StrataScratch

我们的输出没有四舍五入,所以我们应用了一个上限函数来得到一个按比例分摊的 _employee_expense,其格式可以更好地与预算进行比较。请记住,上限函数将返回大于或等于输入的最小整数值,因此它总是向上舍入任何不等于整数的浮点。

SELECT title,
       budget,
       CEILING((end_date - start_date)/365::float* project_yearly_salary) AS prorated_employee_expense
FROM linkedin_projects

截图来自 StrataScratch

我们的费用被四舍五入,看起来更符合预算数字。为了最终确定我们的答案,我们所要做的就是将预算与按比例分摊的员工费用进行比较。

筛选超预算项目

此时,我们实际上是在比较两个整数,分别代表项目的预算和项目的按比例分摊的员工费用。因为问题要求我们只显示超出预算的项目,所以我们可以使用 WHERE 子句过滤掉任何低于预算的项目。请记住,由于 SQL 语法的限制,我们必须在 WHERE 子句中重复按比例计算员工费用。以下是我们完整的解决方案:

SELECT title,
       budget,
       CEILING((end_date - start_date)/365::float * project_yearly_salary) AS prorated_employee_expense
FROM linkedin_projects
JOIN
  (SELECT project_id,
          SUM(salary) AS project_yearly_salary
   FROM linkedin_emp_projects ep
   JOIN linkedin_employees e ON ep.emp_id = e.id
   GROUP BY project_id) yearly_salary ON linkedin_projects.id = yearly_salary.project_id
WHERE ((end_date - start_date)/365::float*project_yearly_salary) > budget 

截图来自 StrataScratch

这个新的输出显示我们遗漏了几个与低于预算的项目相关的项目。虽然结果包含的项目不止这五个,但您会看到所有结果的“按比例分配的 _ 员工 _ 费用”值都高于项目的预算。

我们现在已经找到了正确的答案。该解决方案最终变得有点复杂,需要多个连接、算术运算和类型操作,但它仍然足够灵活,可以处理遵循该模式的各种数据集。此外,它展示了你的面试官可能欣赏的各种不同的 SQL 技能。

结论

在本文中,我们用复杂但健壮的代码解决了一个中等难度的 LinkedIn 数据科学家面试问题。这不是回答这个问题的唯一方法,所以我们建议尝试看看你是否能想出其他更有效或更无效的方法。

你可以尝试回答其他 SQL 面试问题来提高你的技能准备数据科学面试

https://www.stratascratch.com】最初发表于https://www.stratascratch.com/blog/linkedin-data-scientist-interview-questions/?utm_source=blog&utm_medium=click&utm_campaign=medium

LinkedIn 敬业度仪表板(使用 Streamlit 构建)

原文:https://towardsdatascience.com/linkedin-engagement-dashboard-built-with-streamlit-f7edf0e1707d

你的帖子有多吸引人?

杰森·德沃尔在 Unsplash 上拍摄的照片

这个 Streamlit 应用程序使您能够上传您的互动、印象和分享数据,以分析您的互动后!

概观

本文将带您浏览使这个 Streamlit 应用程序运行的代码!

我将介绍以下内容:

  • 创建一个多页面简化应用
  • 创建各种图表元素小部件
  • 加载用户数据
  • 创建一个垂直条到跟踪光标位置
  • 创建交互式工具提示
  • 启用数据点上的点进打开超链接(在本例中,LinkedIn 帖子)
  • 创建牛郎星图表
  • 使用 layer()vconcat() 函数创建复合图表
  • 创建交互式聚集表格

我在本文的最后添加了一个 YouTube 视频,它给了你一个仪表盘界面的演示。

我还包含了我的 GitHub 库的链接。

希望你喜欢并请留下你的评论

LinkedIn 敬业度仪表板概述。图片作者。

代码

我将带你浏览这个 Streamlit 应用程序背后的代码,它是使用 Visual Studio 代码 IDEPython 编写的。

导入库

首先,我们将导入必要的库,包括:

  • 操作系统用于文件导航
  • openpyxl 用于文件读取
  • streamlit 用于构建 app!
  • 熊猫和熊猫因为我们爱它们
  • AgGrid 用于漂亮的桌子
  • 日期时间用于处理日期和时间
  • 牛郎星为了好看的剧情

Requirements.txt

确保为 Streamlit 创建一个 requirements.txt 文件,以便从您的存储库中访问。我在下面包含了我的应用程序的文件。

多页应用程序

我将跳到代码的末尾,为我开始定义页面函数的下一部分提供上下文。

对于我的应用程序,我想创建多个页面,这样界面就不会太拥挤。我希望仪表板本身有自己的页面。

因此,为了做到这一点,我定义了多个页面,包括对应于登录页面、仪表板和数据方向的 main_page、page2 和 page3。下面的代码使用 selectbox()函数创建一个下拉列表,其中包含侧栏中的导航页面。

主页

这是我的 LinkedIn Engagement Dashboard 应用程序的登录页面。

应用程序的主页或登录页提供了应用程序用途和功能的背景。图片作者。

在这段代码中,我定义了应用程序的主页或登录页。

我首先定义页面标题,在 repo 中提供 LinkedIn 徽标图像的相对路径,并使用 image()函数。

然后,我使用 sidebar.markdown()函数在侧栏中提供信息。

在页面主体中,我使用 subheader()函数定义了副标题。

我以添加 LinkedIn 徽标图像的相同方式向页面添加了另一个图像,只是这次我没有定义宽度。这允许图像占据屏幕宽度。

仪表板(应用程序启动)

为了开始谈论仪表板,我将实际上进一步跳过代码,因为由于应用程序启动时没有加载数据而引发了一个异常。

代码首先“尝试”读取 excel 文件,但是它还不存在,因为用户还没有上传他们的文件。

这个异常导致下面的视图,其中屏幕提示用户“请上传您的数据:)”。请参见下面的异常处理代码。

由于开始时没有上载 Excel 文件,引发了异常。在侧边栏上传 Excel 文件。图片作者。

仪表板

好了,下面的下一段代码是函数 page2()的“顶部”,它定义了仪表板页面。

与上一页一样,我用 LinkedIn 徽标定义了页眉。

我使用 file_uploader()函数来加载约定和印象 Excel。xlsx)文件。文件名的格式为{Year}_{Your Name}.xlsx。

注意,我要求文件类型是. xlsx 文件。这就是从 LinkedIn 下载互动和印象数据的方式,这与您从 LinkedIn 数据存档中获得的 Shares.csv 文件形成对比。

函数 page3()覆盖了数据方向页面,我将进一步讨论它。此页面提供了有关如何从 LinkedIn 下载雇佣和印象数据以及从 LinkedIn 请求您的数据存档以获取 Shares.csv 文件的详细信息。

与。xlsx file_uploader(),我为帮助工具提示提供了对要上传的数据以及如何获取数据的描述。

我还定义了变量 file1_details 来包含各种文件细节,以备不时之需。

page2()的代码继续尝试将 Excel 文件作为熊猫数据帧 df 读取。如果成功,我就将日期变量转换为 Date 类型,而不是它加载时的 datetime 类型。这使得以后的情节更加清晰。

如果文件读取失败,数据帧 df 被定义为空白。

继续 page2() —正如我们将在一段时间内做的那样,我编写了类似的代码,以作为 file2 加载到 Shares.csv 文件中。

请注意,这次要求文件是. csv 文件。

与我们处理文件 1 的方式类似,我们将尝试将文件 2 作为名为 df2 的熊猫数据帧来读取。

如果该读取失败,df2 将被定义为空数据帧。

上传数据

一旦用户上传了约定和印象数据,他们将看到如下所示的界面。仪表板正在运行,不需要任何进一步的数据。

但是,请看一下工具提示。请注意,Post 字段显示“不可用。需要数据。这是因为用户尚未上传 Shares.csv 文件。

未上传 Shares.csv 文件时显示工具提示。发布字段将显示“不可用。需要数据。图片作者。

如果用户选择上传他们的 Shares.csv 文件,他们只需按照与。xlsx 文件,但是在底部的文件上传器小部件中。

在侧边栏上传 Shares.csv 文件。图片作者。

当 Shares.csv 文件上传后,工具提示将显示一个帖子预览,如下所示。

工具提示包含文章预览。图片作者。

准备数据

在这里,我尝试在 df2 数据帧中定义新的列,它保存股票数据(参见后面的异常解释)。

  • 将新的日期时间列定义为最初的日期
  • 重新定义日期列,使其仅包含日期类型,而不是日期时间类型
  • 定义一个新的时间列,仅包含日期时间列的时间部分
  • 为清晰起见,将 ShareCommentary 列重命名为 Post(该列是工具提示中显示的帖子预览内容)

然后,我通过合并(左连接)包含雇佣和印象数据的数据帧 df 和 df2(共享数据)来覆盖 df。

我在 Post 和 ShareLink (url)列中填入 nan 值。如果某一天没有帖子,则“帖子”列中的值将变为“今天没有帖子”如果 ShareLink 列中没有值,我将 nan 替换为 LinkedIn Feed 页面(如果您在没有发布帖子的某一天单击,将会转到 LinkedIn Feed 页面)。

如果这种操纵数据的尝试失败了(由于 df2 为空,因为用户还没有上传他们的共享数据),Post 列将填充“不可用”。需要数据。所有的 ShareLink 值都将成为 LinkedIn Feed 页面。

设定情节

我为用户提供了调整绘图宽度和高度的选项,如果他们希望在更大的范围内查看数据的话。

这可以通过使用 slider()函数在侧边栏中找到。

日期范围过滤器

在这里,我为用户定义了日期范围过滤器,以便能够选择时间窗口。

我在日期列中将默认范围设置为最小日期到最大日期。我再次使用 slider()函数为用户提供过滤数据和调整时间窗口的方法。

然后,根据滑块的任何变化重新定义数据帧 df。

在这里,我还定义了 df 的 Percent 列,它是任何给定一天的参与度除以印象数。

创建交互式图表

我将一个新的数据帧源定义为等同于数据帧 df。

然后我创建一个选择,根据鼠标位置的 x 值选择最近的点。

我用日期列定义的 x 轴创建了基本图表,并指定了日期格式。

然后,我将第 1 行和第 2 行定义为约定和印象行。

好戏来了

这下一部分是这次冒险中我最头疼的原因。我在这个仪表板上创建的图表不同于我在网上看到的任何例子。我在一个子情节上有两个折线图,每条线都有自己的 y 轴,然后这个子情节堆叠在另一个情节的顶部。

很长一段时间以来,我遇到的问题是我的主要 y 轴标签,而“约定”的刻度也显示在辅助 y 轴上。所以,我有大量的相互堆叠的刻度线和两个 y 轴标签,“参与”和“印象”挤在一起。

我反复尝试解决这个问题,但就是想不通。因此我下面的标签截图。这在任何编码冒险中都不是独特的经历(更不用说学习像 Streamlit 这样的新工具了),但这是痛苦的。我不得不离开这个问题,然后不断回到这个问题,试图解决它。我失败了很多次。

幸运的是,有一个快乐的结局!我最终在搜索另一个问题时找到了答案!下面我来说说分辨率。

任何写代码的人的生活。看看这些标签!图片作者。

更多绘图

那么我是怎么知道如何固定第二个 y 轴的呢?因为我有两个堆叠的图表,我想我应该在图表组合上放置 resolve_scale()函数。Altair 和 Streamlit 对可以配置的内容和位置很挑剔。所以,我认为我把它放在组合图上而不是单个图表上是正确的。

结果是,带有 y = 'independent '条件的 resolve_scale()必须直接放在行上。为此,我使用 layer()函数将 line1 和 line2 分层,然后在线上使用 resolve_scale()。呜!成功了!

接下来,我根据上面定义的最近距离来定义选择器。这告诉我们光标的 x 值。

我定义了 points1 和 points2(以及后面的 points3 ),以包含详细说明日期、约定、印象、百分比和帖子的工具提示。这些工具提示的例子和图片已经在前面提供了。这里有一个例子,展示了我做的一个詹姆斯·邦德帖子的预览。

工具提示包含文章预览。图片作者。

这些点只有在悬停在它们上方时才变得可见。点击任何一点,href 域告诉应用程序将用户带到 ShareLink,这是与给定数据点相关联的 LinkedIn 帖子 url。

下面提供了用户点击时 LinkedIn 帖子的示例。

下面是图表中某一天用户点击 LinkedIn 帖子的一个例子。

用户点击数据点后的 LinkedIn 帖子示例。图片作者。

垂直滚动条和百分比图

有了两个堆叠的图表,我想让时间导航尽可能容易。为此,我创建了一个垂直滚动条(称为 rule ),再次使用 nearest 跟踪光标在图表上的移动。

然后,我以类似于定义上图的方式定义下图。我用日期列定义的 x 轴定义了 base2 图表。

我用与上面定义点 1 和点 2 相同的方式将线 3 定义为百分比线和点 3。

绑定图表元素

我使用 layer()函数绑定了上下图表的图表元素。还记得我之前是怎么把第一行和第二行变成直线的吗?

我将线条、选择器、点 1、点 2 和 rule 组合成上部图表,并根据用户选择的绘图宽度和高度定义动态属性。

我对下面的图表做了同样的事情,将第 3 行、第 3 点和 rule 组合起来,使其变得更低。

最终图表

为了更好地总结,我使用了 altair_chart()函数来绘制最终的图表。

在此过程中,我使用 vconcat()函数进行垂直连接,以堆叠上面和下面的图表。

还记得我说过 altair 和 streamlit 对你配置的内容和地点很挑剔吗?此时,我可以分别使用 configure_points()和 configure_axis()函数配置点和轴。

汇总统计数据

我可以选择显示用户定义的时间段内的参与和印象的汇总统计数据,或者显示有数据的天数的平均值。

我选择了用户选择的时间段内的汇总统计数据,因为这对我最有意义。

基于用户定义的日期范围的汇总统计和过滤数据表。图片作者。

下面是生成汇总统计表的代码。

我在用户定义的时间段内确定参与度和印象的平均值、总数和变化,并在这里进行一些格式化。

我使用 AgGrid 生成了一个更漂亮的汇总统计表。

过滤数据表

接下来,我生成了经过过滤的数据表,它为用户提供了他们所选择的时间段内的数据。

基于用户定义的日期范围的汇总统计和过滤数据表。图片作者。

我定义了一个新的数据帧 output_df,因为我需要执行进一步的格式更改,并且我不想更改 df 数据帧。

然后,我再次使用 AgGrid 生成过滤后的数据表。

异常(无数据) 这里是对尚未上传数据的事件的异常处理,这在前面已经讨论过了,但是为了完整起见,在代码中出现在这里。

数据方向

通过了仪表板页面的代码!最后是数据方向页面,它为用户提供了从哪里下载该应用程序所需数据的说明。

向用户提供数据背景以及如何获取数据的数据说明页。图片作者。

和前面的页面一样,我用 LinkedIn 徽标定义了标题。

然后我使用 subheader()和 markdown()来打出指令!

将这一切结合在一起

不要停止跟随!这是最重要的部分。

这里,我使用上面创建的所有页面函数,包括 main_page()、page2()和 page3(),并使用 selectbox()创建页面导航。

瞧啊。你有一个 LinkedIn 约会仪表板。

点击下面的链接查看我的 YouTube 视频和 GitHub repo。

仪表板界面演练

这里有一段我带你浏览 LinkedIn 互动仪表盘界面的 YouTube 视频。

GitHub 知识库

此代码的 GitHub repo 可以在下面的链接中找到。

https://github.com/christianwanser/LinkedIn-Engagements-Dash.git

摘要

我使用 Streamlit 创建了一个 LinkedIn Engagements Dashboard 应用程序,允许用户上传他们的参与、印象和共享数据,以分析他们的参与后活动!

查看我的 LinkedIn 约会仪表板,让我知道你的想法!

直到下一次…

喜欢这些内容吗?关注我在媒体上看到未来的职位!成为中级会员这样你就可以无限阅读文章了。相信我,值得!

如果你对数据专业人员的生活有任何疑问,请随时在 LinkedInTwitter 上联系我!

https://medium.com/@ChristianWritesData/membership

LinkedIn:你从来不知道的最好的示例数据源

原文:https://towardsdatascience.com/linkedin-the-best-example-data-source-you-never-knew-737d624f24b7

使用本指南了解和浏览 LinkedIn 允许您从个人资料中下载的数据

TLDR:这篇文章不是关于网络抓取的。它使用 LinkedIn 授权的“获取数据副本”服务来演示如何将该数据用于培训、测试和演示目的。

概观

本文将向读者介绍 LinkedIn 的“获取数据副本”服务,并探讨如何将这些数据用作学习、测试、培训和开发目的的示例数据。

本文及其讨论的数据至少有两个好的用途:

  1. 将 LinkedIn 数据用于培训、测试和演示目的。-就像我之前说的:LinkedIn 数据是你从来不知道的最好的示例数据源。
  2. 使用这篇文章作为 LinkedIn 数据的指南。—在寻找新颖的“真实世界”数据作为培训、测试或演示示例时,这是一个好去处。
  3. 将本文用作数据可视化指南。—下面的示例包括条形图、折线图以及其他带有注释、数据标签和其他细微增强的图表。

介绍

我收集和研究 LinkedIn 的数据已经有一年多了。下面是我从 LinkedIn 下载和收集的文件的屏幕截图(使用他们授权的“获取你的数据的副本”门户)。

我定期从 LinkedIn.com 下载 LinkedIn 数据后的文件和文件夹的屏幕截图。图片鸣谢:作者截屏。

您也可以通过点击 LinkedIn 主页右上角的“我”图标>然后点击“设置与隐私”>然后点击“获取您数据的副本”来访问您的数据

LinkedIn 几乎花了一整天来准备你的摘录。值得等待。一旦您获得了数据的副本,您将有近 40 个 CSV 文件可供探索。不同的用户会有不同的结果,因为不是所有的用户都有来自 LinkedIn 所有功能集的数据。

我之前使用这个数据来显示pd.crosstab()pd.pivot_table()之间的差异。

LinkedIn 的数据

每个用户将体验到略微不同的结果。在这里,我列出了三个 CSV 文件,我怀疑大多数用户都可以使用,并且我认为它们是最有趣的。我们从connections.csv开始,列出你所有的关系。然后receipts.csv也就是你在 LinkedIn 上消费的记录。然后我们转到messages.csv,它是根据你发送和接收的信息直观命名的。

LinkedIn 的数据有点神秘。文档不完整。个人请求:请阅读本文档然后提交一份请求,要求 LinkedIn 改进文档。比如 LinkedIn 文档endorsements_received.csv没有endorsements_given.csv

特定数据文件

连接(Connections.csv)

从 connections.csv 开始。注意,这个 csv 需要使用header参数来指定第一行数据。使用index_col='Connected On'parse_dates=True参数将连接的日期作为索引。

connections = pd.read_csv('Connections.csv', 
                          header=2, 
                          index_col='Connected On',
                          parse_dates=True)

这些数据会告诉你每个关系的名字、姓氏、电子邮件地址、公司和职位。使用下面的代码,你可以在 LinkedIn 上绘制你的人脉增长图。

plt.style.use('fivethirtyeight')
connections['count'] = 1
connections['count'] = connections['count'].cumsum()
connections['count'].plot(xlabel='Time',
                          ylabel='Connections',
                          figsize=(10,5))plt.axhline(500, color='red', linewidth=.5)
plt.axvline(pd.to_datetime('2018-12-1'), 
            color='black', linewidth=.5)plt.annotate('Six years to reach 500 connections.', 
             xy=(pd.to_datetime('2014-01-01'), 500),
             xytext=(pd.to_datetime('2009-01-01'), 1200),
             color='blue',
             arrowprops={'arrowstyle':'->',
                         'linewidth':2,
                         'color':'blue'})plt.annotate('First began regular publishing on Medium.', 
             xy=(pd.to_datetime('2018-12-01'), 1400),
             xytext=(pd.to_datetime('2012-01-01'), 2200),
             color='black',
             arrowprops={'arrowstyle':'->',
                         'linewidth':2,
                         'color':'black'})

对于结果:

显示 LinkedIn 上连接增长的折线图。图片来源:作者的插图由代码和数据生成,如本文所示。

你也可能对你的关系的性质感兴趣。考虑一个柱状图,它显示了你的关系中最常见的职位头衔的出现频率。

在我的人脉中,有 2081 个独特的职位头衔。在我的人脉中,最常见的职位头衔是“数据科学家”(44 个人脉),其次是“执行董事”(31 个人脉)。以下代码将这些标题可视化:

plt.style.use('seaborn-white')
connections['Position'] = 
   connections['Position'].str.upper()
top_titles = 
    pd.DataFrame(connections['Position'].value_counts().head(12))
top_titles.plot.barh(figsize=(10,5))
# Add bar height labels.
for i in range(12):
    plt.text(top_titles.iloc[i][0]+1, 
             i, top_titles.iloc[i][0]+1, fontsize=12)

显示我的 LinkedIn 联系人中常用职位的条形图。图片来源:作者的插图由代码和数据生成,如本文所示。

您的支出(Receipts.csv)

原来这些年来我在 LinkedIn 上花了大约 380 美元。我的第一笔交易是 19.95 美元,是他们的“求职者基本订阅(每月)”服务。

在一行代码中你可以得到你的总支出(假设你花了任何东西,并且有一个receipts.csv文件:

pd.read_csv('receipts.csv')['Total Amount'].sum()

您的消息(Messages.csv)

该文件包含您在 LinkedIn 上的消息历史记录。有一个“对话 ID”、“对话标题”、“发件人”、“发件人配置文件 URL”、“日期”、“内容”和“文件夹”栏

我甚至不知道你可以在 LinkedIn 的文件夹里存档信息。你可能有兴趣知道你发信息的频率,在一个月的哪几天,一年的什么时候。热图会对此有所帮助。

你发送和接收了多少信息?

pd.read_csv('messages.csv').shape[0]

你的第一条信息是什么?你给谁发信息了?您说什么?

pd.read_csv('messages.csv',
             index_col='DATE',
             parse_dates=True).sort_index().iloc[0]

以下代码版本可能会揭示您的消息传递历史。首先,热图显示了你一整年的发信息习惯。

# Load the messages data.
messages = pd.read_csv('messages.csv',
                       index_col='DATE',
                       parse_dates=True)# Get the next year in number format
ny = int(str(date.today())[:4]) + 1# Create a data frame for next year.
ny = pd.date_range(start=f'1/1/{ny}', 
                   end=f'12/31/{ny}')# Populate next year with zero messages in each day
ny = pd.DataFrame({'Count':[0] * len(ny)}, 
                  index=ny)# Prepare data for visualization
mv  = pd.DataFrame(messages.index)\
      .set_index('DATE').tz_localize(None)mv['Count'] = 1
mv = pd.concat([mv, ny])
# Create visualization axes
mv['Year'] = pd.DatetimeIndex(mv.index).year
mv['Month'] = pd.DatetimeIndex(mv.index).month
mv['DayOfMonth'] = pd.DatetimeIndex(mv.index).day
mv['DayOfWeek'] = pd.DatetimeIndex(mv.index).dayofweek
mv['HourOfDat'] = pd.DatetimeIndex(mv.index).hour# Make the plot
tot_mgs = mv['Count'].sum()
plt.figure(figsize = (16,5))
total_messages = mv['Count'].sum()
sns.heatmap(pd.pivot_table(mv, 
                           values='Count', 
                           index='Month', 
                           columns='DayOfMonth',
                           aggfunc='sum'),
            cmap=my_blues,
            annot=True,
            fmt='g')
plt.suptitle(
    f'Daily Monthly Messaging Activity Since {mv["Year"].min()}', 
    horizontalalignment='right')
plt.title(
    f'{tot_mgs:,} Messages Over {mv["Year"].max() - mv["Year"].min()} Years',
    horizontalalignment='right')

对于以下结果:

热图显示了 16 年期间每天和每月的消息历史和数量。图片来源:作者的插图由代码和数据生成,如本文所示。

其次,折线图将显示一段时间内的累积量。

plt.style.use('fivethirtyeight')
messages.sort_index(inplace=True)
messages['count'] = 1
messages['count'] = messages['count'].cumsum()
messages['count'].plot(xlabel='Time',
                       ylabel='Messages',
                       figsize=(10,5))plt.annotate('Six years to reach 500 connections.', 
             xy=(pd.to_datetime('2014-01-01'), 500),
             xytext=(pd.to_datetime('2008-01-01'), 2800),
             color='blue',
             arrowprops={'arrowstyle':'->',
                         'linewidth':2,
                         'color':'blue'})plt.annotate('First began regular publishing on Medium.', 
             xy=(pd.to_datetime('2018-12-01'), 1400),
             xytext=(pd.to_datetime('2011-06-01'), 4600),
             color='black',
             arrowprops={'arrowstyle':'->',
                         'linewidth':2,
                         'color':'black'})plt.annotate('First Udemy Course / Began consulting.', 
             xy=(pd.to_datetime('2020-10-01'), 3300),
             xytext=(pd.to_datetime('2013-06-01'), 6800),
             color='blue',
             arrowprops={'arrowstyle':'->',
                         'linewidth':2,
                         'color':'blue'})plt.text(
    pd.to_datetime('2008-10-01'), 12200,
    f'Cumulative Messaging History Since {mv["Year"].min()}')plt.text(
    pd.to_datetime('2008-10-01'), 11200,
    f'{total_messages:,} Messages Over {mv["Year"].max() - mv["Year"].min()} Years')

对于结果:

折线图显示通过 LinkedIn 发送或接收的消息历史和消息量。图片来源:作者的插图由代码和数据生成,如本文所示。

结论

对于希望使用“新”或有趣数据的人来说,有些常见的建议是从社交媒体获取自己的数据。这篇文章超越了这个建议,提供了当你开始研究你自己的社交媒体数据时,你可能会发现的一些数据。

格雷格·布拉在 Unsplash 上的照片

感谢阅读

你准备好了解更多关于数据科学职业的信息了吗?我进行一对一的职业辅导,并有一份每周电子邮件列表,帮助专业求职者获取数据。联系我了解更多信息。

感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelsonLinkedIn:亚当罗斯尼尔森

照片由 Greg BullaUnsplash 上拍摄

LM!=KM:语言模型无法支持下一代人工智能知识模型需求的五个原因

原文:https://towardsdatascience.com/lm-km-3e81e1e1c3ae

行业笔记

LM!=KM:语言模型无法支持下一代人工智能知识模型需求的五大原因

图片来源: CoreDESIGN 通过 Adobe Stock。

大型语言模型(LMs)已经证明它们可以作为相对较好的知识模型(KMs)。但是,它们是否擅长执行实现真正智能的认知人工智能系统所需的所有功能?我认为,答案是否定的。这篇文章将讨论构成高级知识管理的五种能力,以及这些领域如何不能被目前形式的 LMs 轻易解决。这些能力是可扩展性、保真性、适应性、丰富性和可解释性。

下一波人工智能将转向更高的认知功能,如常识推理、抽象洞察、新技能的获取以及复杂信息的新颖使用。丰富的、结构深刻的、多样的、不断发展的知识将是基础。允许人工智能系统组织其世界观、理解意义并展示对事件和任务的理解的知识结构将可能处于更高水平的机器智能的中心。因此,了解所需的高级知识模型(KM)特征并评估当代完全封装的端到端生成语言模型(LMs)对于满足这些要求的适用性是有价值的。

像 GPT-3 和 T5 这样的 LMs 作为潜在的知识来源,在研究人员中越来越受欢迎,经常获得相当大的成功。例如,叶筋·崔的团队开创了像生成知识提示象征性知识蒸馏这样的方法,通过查询像 GPT-3 这样的大型 LM 成功地提取了高质量的常识关系。显然,这种方法可以带来价值,因为它们允许自动创建数据库,而无需模式工程、预设关系或人工监督。

人们很希望 LMs 能够逐步发展,以提供从知识库获得的全部能力。然而,尽管它们有许多优点和有价值的用途,LMs 有几个结构上的限制,使它们更难成为一个优秀的、功能更多的知识库和知识接口。例如,在开放领域问答这样的基准测试中可以看到,基于检索器的方法明显优于生成式 LMs。

图一。对于开放领域问题回答,基于检索器的方法优于生成式 LMs。数据来源:【3】。图片鸣谢:加迪·辛格。

应当注意,LMs 被设计成执行下一个单词预测,而不是为了存储知识的特定目的。因此,即使他们在某些情况下可能做得很好,他们的架构不是表现知识的最佳方式,是不同于人类记忆,并且他们的表现对于知识相关的任务来说不是流线型的。

为了更好地理解这些缺点,考虑一下一个先进的知识管理能够做些什么。我提出了五种主要能力:可伸缩性、保真性、适应性、丰富性和可解释性。

图二。先进知识管理的五个特征。图片鸣谢:加迪·辛格。

让我们更详细地看一下它们。

可扩展性

显然,世界上有大量的信息。大型 LMs 在其容量允许的情况下在参数存储器中存储尽可能多的相关信息,并以训练效率为代价提供快速访问。这种模型通常需要非常大的数据语料库和大量的时间投资来实现性能改进,因此在部署后它们可以访问的信息范围有限。基于检索的方法(例如图 1 中引用的方法)扩展了可靠可访问信息的范围,远远超出了 LM 中编码的内容。2021 年 12 月,作为 DeepMind 的复古的一部分,进一步展示了检索适应规模的基本价值。通过在语言技能(嵌入在模型中)和从具有万亿级表征的数据库中检索的外部知识之间建立分离,他们证明了与 280B 参数 Gopher 和 178B 参数 Jurrasic-1 相比,其 7B 参数 RETRO 的结果有了实质性的改善。

然而,检索系统可能缺乏在综合知识的帮助下可以实现的细微差别。一般来说,设计为高速访问相关信息而优化的系统会在规模和便利性之间产生固有的矛盾,未来的系统预计会具有复杂的分层访问架构。正如博客“没有人能统治所有人”中所详述的,经济高效的信息访问需要分层的架构,这意味着知识管理必须包括处理每一层的专门模块。

例如,我们可能想要区分每十次访问的数据项和每十万亿次只需要访问的数据项。在计算机系统中提供访问层次结构,可以根据数据需要访问的频率来分离数据,并为每个频率层设计不同的硬件架构(想想高速缓存寄存器与计算机中的硬盘驱动器)。

最常使用的知识(或需要最方便的知识)也应该是最容易访问的,这需要更昂贵的体系结构,并限制了该层的最大经济可行容量。例如,随着计算机架构中存储容量的增加,系统必须牺牲一些访问速度(图 3)。

图 3。规模与速度层级。图片鸣谢:加迪·辛格。

一个理想的知识管理必须是可扩展的,并且包括人类文明积累的全部智慧(达到人工智能执行和进化可能需要的程度)。该系统还必须能够快速执行最关键和时间敏感的任务,并且可能需要一个如上所述的分层体系结构。虽然 LMs 可以通过对大型语料库的训练来获取关于世界的知识,但它们没有以优化方式存储知识的特定架构。

保真度

一个知识管理必须具有高度的保真度,也就是说,它必须允许事实、属性和关系的再现,这些事实、属性和关系是它们的起源所固有的,并且不依赖于它们发生的统计数据。即使两者之间的置信界限不同,单次观测记录的精度也必须等同于经过良好测试的多次观测理论的精度。高保真度的知识管理不太容易出现灾难性遗忘或其他类型的信息衰减,并允许针对各种类型的基于训练的偏差进行明确的保护。

图 4。KMs 必须保留允许忠实复制的信息。图片鸣谢:加迪·辛格。

一般来说,LMs 和机器学习(ML)模型在这个维度上有些弱。虽然有许多方法可以缓解这个问题,但没有人期望 LM 能够摆脱这种偏见。相反,LM 必须依赖专门的模块和外部进程。

适应性

知识管理必须处理许多数据源、类型和更新速率。一个小办公室的牙医需要处理患者信息,随着患者信息的变化随时更新,并将不同的数据类型与同一患者相关联(文本文件中的诊断、x 光图像)。每个患者记录在每次就诊的基础上进行更新。另一方面,金融机构处理完全不同类型的信息,更新速度更快,数据量更大。这种类型的系统实际上取决于它处理新信息的速度,以发现欺诈和错误。

图 5。知识管理的适应性。图片来源:加迪·辛格和 SCEDC

今天的 LMs,即使它们可能已经在最近的数据上被训练,在局部改变网络以反映孤立数据点的增加或改变方面也不那么有效。任何微调都需要完全或至少实质性的更新,这远远超出了所添加或更改的数据点的范围,并且有破坏存储在同一系统中的无关知识的风险。一个先进的知识管理不能有这种限制。它需要处理具有不同数据类型的各种信息源,并且它必须允许与相关知识的变化速度相匹配的经济和及时的更新。

丰富度

另一种能力是破译知识的丰富性,这是捕捉通过语言和其他形式表达的知识中所有复杂关系的能力。正如在关于知识维度的文章中所讨论的,知识可以分为知识和元知识。直接知识可以是描述性的,如分类学和本体论,或者财产继承。这种类型的知识可以以许多不同的形式存储:语言、3D 点云、声音、图像等。它也可以代表世界的模型,例如因果、程序或物理模型。最后,它可以包括故事和剧本,如神话、经历描述、日常仪式等。

图 6。多模态知识的维度。图片鸣谢:加迪·辛格。

元知识维度和结构包括来源属性、价值和优先顺序以及概念。人类可以根据信息来源的可信度来区分它们。例如,人们不能指望黄色媒体上的普通点击诱饵像《自然科学杂志》上的一篇文章一样可信。

人类还在他们的价值体系和偏好的背景下评估信息,并将其封装到概念结构中,以便长期存储和推理。虽然 LMs 反映了这些知识维度的一个子集(例如,GPT-3 似乎很清楚典型的笑话脚本),但它们似乎只代表了知识维度的一个部分子集。很可能需要一个不同的架构来捕捉人类知识的全部丰富性。

交代

当人工智能与人类互动时,特别是在医疗、金融或执法应用等高风险任务上,它解释其选择和沟通的能力自然变得非常重要。这种可解释性和可解释性在明确的、可理解的知识来源的帮助下更容易实现。显性知识允许一个透明的结构,可以检查,编辑和整合到因果推理或反事实推理模块。它为人工智能和人类用户之间的交流提供了一个更好的基础,在这里信息可以被交换、采纳、吸收和反馈。

图 7。知识管理的可解释性。图片鸣谢:加迪·辛格。

结论

一个端到端的 LM,无论多大或多贵,都不能完成一个更先进的 KM 所要求的所有功能来支持下一代的 AI 能力。但这表明了什么是可以支持下一代人工智能系统的实际架构?在推理和知识图的自动扩展等领域已经取得了一些初步进展,在描述知识架构蓝图的三个层次时,我已经进行了深入的讨论。

无论未来如何,都需要行业努力开发具有涵盖五种能力的知识管理系统的人工智能系统,以便我们可以实现真正智能的认知人工智能。在未来的博客中,我们将讨论一些在未来人工智能系统中开发这些能力的潜在方法。

参考文献

1.刘,刘,a,陆,x,韦勒克,s,西,p,布拉斯,R. L,…和 Hajishirzi,H. (2021)。常识推理的生成知识提示。 arXiv 预印本 arXiv:2110.08387

2.韦斯特,p .,巴加瓦图拉,c .,赫塞尔,j .,黄,J. D .,江,l .,布拉斯,R. L .,…,崔永元(2021)。符号知识提炼:从一般语言模型到常识模型。 arXiv 预印本 arXiv:2110.07178

3.伊萨卡和格雷夫(2020 年)。利用生成模型的段落检索进行开放领域问答。arXiv 预印本 arXiv:2007.01282 。

4.Merrill,w .,Goldberg,y .,Schwartz,r .,& Smith,N. A. (2021)。从非基础形式获取意义的可证明的局限性:未来的语言模型将理解什么?。 arXiv 预印本 arXiv:2104.10809

5.Nematzadeh,a .,Ruder,s .,和 Yogatama,D. (2020 年)。人类和人工语言处理系统中的记忆。在ICLR 关于沟通人工智能和认知科学研讨会的会议录

6.歌手 g(2021 年 12 月 21 日)。没有人能统治所有人:解决基于知识的人工智能中的规模和权宜之计。中等。https://towards data science . com/no-one-rung-to-rule-them-all-208 a 178 df 594

7.Kirkpatrick,j .,Pascanu,r .,Rabinowitz,n .,Veness,j .,Desjardins,g .,鲁苏,A. A .,… & Hadsell,R. (2017)。克服神经网络中的灾难性遗忘。美国国家科学院院刊114 (13),3521–3526。

8.歌手 g(2021 a,12 月 19 日)。对深度知识的理解和运用——走向数据科学。中等。https://towards data science . com/understanding-of-and-by-deep-knowledge-aac5 ede 75169

9.布兰文,G. (2020 年 6 月 19 日)。 GPT-3 创作小说https://www.gwern.net/GPT-3#humor

10.歌手 g(2021 b,12 月 20 日)。 Thrill-K:下一代机器智能蓝图。中等。https://towards data science . com/thrill-k-a-blue print-for-the-next-generation-of-machine-intelligence-7 ddacddfa 0 Fe # e6a 1-20df 55d 5621 c

11.Borgeaud,s .,Mensch,a .,Hoffmann,j .,Cai,t .,Rutherford,e .,Millican,k .,… & Sifre,L. (2021)。通过从数万亿个标记中检索来改进语言模型。 arXiv 预印本 arXiv:2112.04426

12.j . alam mar(2022 年 1 月 3 日)。图示检索变压器http://jalammar . github . io/illustrated-retrieval-transformer/

Wordle 中加载的单词

原文:https://towardsdatascience.com/loaded-words-in-wordle-e78cb36f1e3c

图片作者。

沃尔多

为什么“最好的”单词不是最好的

在过去的几周里,我注意到脸书上张贴了越来越多的绿色、黄色和黑色/白色网格,就在那时我发现了 Wordle 。我被迷住了——与其说是以人类的方式玩游戏,不如说是开发一个系统来尝试最佳地玩游戏。当我阅读其他作者的现有作品时,我发现对起始/种子词的强调势不可挡。然而,对于最好的选择,却有相互矛盾的建议。很明显,除了种子词之外,还有其他更好的玩法!

这篇文章的主要贡献是解释为什么我们应该对“最好的”Wordle 种子词持保留态度,通过展示 Wordle 策略的其他组成部分影响这些词。

沃尔多上的字

关于这个主题已经写了许多文章。大多数作者使用模拟来寻找最佳的起始/种子词,同时固定其他参数(稍后将详细介绍)。一些推荐的种子词基于最终的游戏结果,其他的基于计算预期信息增益的算法。下表总结了每位作者的一般方法和建议。

关于沃尔多的文章。图片作者。

Wordle 是一个相对较新的游戏,覆盖整个解决方案空间需要大量的努力和计算能力。因此,还有许多想法有待探索,还有许多问题有待解决。第一个问题是,正如更全面的研究(主要是史密斯)所暗示的那样,最优策略不仅仅是种子词那么简单。第二个问题是“最佳”有不同的定义,并且不是所有的研究都包括足够的指标来衡量战略的绩效。

行动纲要

这篇文章试图解决上述问题。这篇文章的目的和主要观点列在这里。

首先,我们表明“最佳”种子词取决于 Wordle 策略的其他组成部分:

  • Wordle 策略的其他组成部分是排序算法决策规则,用于在解决问题和收集信息之间进行优先排序。
  • 排名算法严重影响了 Wordle 的性能。
  • 选择的度量也决定了什么是最好的意思,因此,什么词是最好的。

其次,我们引入了几个度量标准来衡量性能并定义什么是“最好的”。这些是:

  • 达成解决方案所需的平均步骤数
  • 解决方案成功率
  • 在 3 步或更短时间内解决的挑战比例

游戏

对于门外汉来说,Wordle 是 5 个字母单词的策划者,加上一些社交媒体上的羞辱。这个游戏的目的是在六次尝试中猜出一个未公开的单词。在每次猜测中,Wordle 会告诉你每个字母是否:

  • 在正确的位置(绿色)
  • 在单词中,但是错误的点(黄色)
  • 根本不在这个词里(灰色)

这就是全部了!这听起来很简单,但是这个游戏并不简单,因为有太多的可能性。在 Wordle 中,有 2,315 个可能的解决方案单词,以及另外 10,657 个被接受为猜测的单词(“支持单词”)。因此,我们的机器人的目标是利用 12,972 个候选词的完整集合,在六次尝试中将 2,315 个解决词的集合减少到一个。

注: 全套文字可以从网站主脚本中检索。使用浏览器的开发人员控制台来访问它。

沃尔策略

就像幸运之轮一样,在 Wordle 中,我们在绿色、黄色和灰色瓷砖方面平衡了解决(猜测我们认为是解决方案的单词)和收集信息(使用单词梳理出解决方案中可能的字母)。人类可能会采取以下策略:

  1. 第一轮:收集尽可能多的信息。我们在这一点上没有信息,所以我们选择一些统计上最优的种子词。第一次猜测越准确,我们收集的信息就越多。
  2. 第 2 轮:利用第 1 轮的反馈,收集尽可能多的信息。虽然我们可以通过两步解决游戏来获得巨大的街头信誉,但这是非常困难的。因此,我们在第二轮中能做的最好的事情就是通过使用与种子单词字母完全不同的单词来收集更多的信息。
  3. 第三轮:看情况!如果我们获得了足够的信息,我们就可以着手解决问题了。否则,为了安全起见,选择另一个词来获得更多线索可能会更好。
  4. 第四轮:还是要看情况。我们做的和第三轮一样。我们可以通过优先解决问题而不是收集信息来变得更加积极。
  5. 第 5 轮:还是那句话,看情况。我们的做法与第 3 轮和第 4 轮相同。到目前为止,我们应该已经缩小了解决方案的范围。
  6. 第 6 轮:100%解决。到第六轮时,我们完全有可能仍有几个可行的解决方案。如果仍然不清楚解决方案是什么,就猜一猜吧!我们会失去什么?

我们可以看到,战略不仅仅是种子词。它还包括(1)优先解决与收集信息的决策规则,以及(2)选择单词的方法。

注意: 如果我们在游戏开始前应用排名算法对整个候选集进行排名,我们实际上可以将种子词作为策略组件一起删除。事实上,这正是上述资料 8-11 中的作者所做的。

模拟单词

概观

我的 Wordle bot 遵循广义策略,并实现了该策略的另外两个组成部分:决策规则和选择单词的排序算法。

机器人开始于(1)包括所有 12,972 个被接受单词的候选集合,以及(2)包括所有 2,315 个解单词的解集合。它将重复测量(1)和(2),并在每次游戏过程中更新它们。机器人一次移动一步,在每一步/每一轮中做同样的事情:

  1. 将剩余的候选人和解决方案放入排名算法中,以计算每个候选人的分数。
  2. 根据分数对剩下的候选人进行排序。
  3. 提交得分最高的候选人作为该步骤的猜测。
  4. 使用反馈来(a)筛选候选方案和(b)筛选潜在的解决方案。我们还排除已经被猜测的候选项,以及包含不再存在于剩余解集中的字母的候选项,即它们对于进一步过滤候选项没有价值。
  5. 从步骤 1 开始重复,直到步骤 4 的反馈为GGGGG

我开发了一个Wordle类来简化游戏,不管是模拟的还是其他的。由于这不是这篇文章的重点,我将跳过它的实现细节。你可以在我的 GitHub repo 获取完整代码。我提到这个课程只是为了简单地展示我是如何运行模拟的。

def play_game(input_word, solution):

    game = Wordle(wordle, wordle_answers, solution=solution, verbose=False)

    while not game.solved:
        if game.step == 0:
            game.guess(input_word)
        else:
            game.guess(game.optimisations[method.lower()].word.iloc[0])
        game.optimise(method='expected_gyx', n_jobs=-2)

    return game.records()

在决定所有步骤之前,机器人不使用蛮力来列举所有游戏结果。这太耗费计算量了。

排名算法

排序算法可以说是策略中最关键的部分,因为它决定了哪些猜测是可用的。决策规则仅在算法提供的可用猜测中做出决定,种子词可以被认为是排名算法的产品,在游戏开始前计算。

我的 Wordle bot 有几个内置选项:(1)字母频率,(2)预期的绿色、黄色和灰色瓷砖,以及(3)预期的剩余候选的最大数量。每个算法计算所有剩余候选项相对于剩余解决方案的分数。

字母频率 该算法根据组成单词的字母的流行程度对单词进行排序:

  1. 计算所有剩余解决方案中字母出现的频率
  2. 创建一个字母到计数的查找表
  3. 通过计算候选单词中字母的频率得分总和,对每个剩余的候选单词进行评分
  4. 挑选得分最高的候选人

计算字母频率分数。作者图片

期望的绿色/黄色/灰色块分数 该算法基于返回的绿色块和黄色块的数量,通过所有剩余解决方案的平均,根据获得的期望信息对单词进行排序。为了简单起见,我称之为 GYX 分数,因为我在对Wordle类的反馈中用灰色(X)编码。对于每个剩余的候选人:

  1. 针对每个剩余的解决方案执行以下操作:
    —针对该解决方案计算反馈
    —计算GYX Score = 2 * No. of Greens + No. of Yellows
  2. 巩固 GYX 分数列表
  3. 取所有分数的平均值,得出该候选人的单一分数

最后,挑选得分最高的候选人。

计算预期的 GYX 分数。图片作者。

剩余候选项的最大数量 该算法根据候选项在最坏的情况下,在所有剩余的解决方案中可能被排除/留下的可能性来对候选项进行排序。这个想法是选择最能减少候选词集的词。对于每个剩余的候选人:

  1. 对每个剩余的解决方案执行以下操作:
    —计算对该解决方案的反馈
    —使用反馈来过滤剩余的候选集(的副本)
    —计算剩余的候选数
  2. 合并计数列表
  3. 取所有计数中的最大值

最后,选择得分最低的候选人,因为它在最坏的情况下留下了最少的可能性。

计算剩余候选人的最大数量。图片作者。

决策规则

基线决策规则是,只有在剩下 20 个或更少的解决方案时,我们才会猜测解决方案单词。这与我们的战略相一致,当我们在解决方案上达成一致时,我们优先解决问题,而不是收集信息。

测试的另一个可选决策规则是纯粹根据流行度来选择单词,流行度是通过维基百科文章中的词频来衡量的(来源:Lexipedia【9】)。当只剩下 10 个解决方案时,规则开始生效,并且被置于基准规则之上。我任意选择了 10 个剩余解的阈值,尽管我确信有更好的方法来选择这个数字。根据这一规则,机器人会在剩下的答案中猜出最受欢迎的单词。

模拟配置

我将上一节中各种来源推荐的每个单词(见下文)与 2315 个解决方案单词的完整集合进行了 5 次对比,每个排序算法/决策规则配置一次。

种子词:

1\. arles    10\. rates
2\. arose    11\. reais
3\. dares    12\. roate
4\. lares    13\. soare
5\. lores    14\. stare
6\. nares    15\. tales
7\. raile    16\. tares
8\. raise    17\. tores
9\. rales

排序算法和决策规则:

测试了排名算法和决策规则配置。图片作者。

总的来说,在总共 196,775 场的 Wordle 游戏中,有 17 个“最佳”单词被 5 种策略和 2,315 个解决单词所取代。

从现在开始,为了简单起见,我将把排名算法和决策规则的组合称为排名算法/算法,因为排名算法主要负责游戏的进展。

韵律学

最后,在我们讨论模拟结果之前,这些是建议的指标,允许其他作者与测试的策略进行比较:

  1. 达成解决方案所需的平均步骤数
  2. 解决方案成功率
  3. 在 3 步或更短时间内解决的挑战比例

总的来说,这些指标告诉我们(1)就步骤而言,该策略总体上有多好,(2)它可以解决 2,315 个解决方案单词中的多大比例,以及(3)机器人在尽可能少的步骤中解决挑战方面有多好(即街头信誉)。

结果

总体结果

总的来说,结果表明“最佳”种子词取决于(1)其他策略组件——尤其是排序算法——以及(2)所使用的性能指标。以下是相应策略设置和指标的排名第一的种子词:

按排序算法和度量得出的总体结果。图片作者。

下面,我给出了 17 个“最佳”种子词在各种排序算法和指标中得分的方框图。我还强调了几个种子词的分数和进展,这些种子词在整个排名算法中一直排在前几名。

首先,我们注意到对性能影响最大的是排序算法。我们看到三个度量中的每一个的种子词性能的分布有明显的差异。特别地,最大剩余候选排序算法比其他算法表现得好得多,使得表现最差的种子词比所有其他策略中表现最好的种子词表现得更好。

第二,当使用不同的排序算法时,我们看到“最佳”种子词改变了排序。这一点对某些人来说比其他人更明显。例如,tores在解决 GYX 分数算法的平均步骤中排名 6-7,在字母频率算法中排名 1-2,然后在最大候选数算法中排名第 6。

。这让我们有理由相信,使用不同的算法可能会产生不同的“最佳”种子词。

第三,“最佳”种子词并不是所有指标中的最佳。事实上,每个指标都有不同的最优策略。在各种排序算法中表现相对较好的种子词是:staretalestarestoresroate。这些在下面的图表中用各自的颜色标出。

图片作者。

图片作者。

图片作者。

“最佳”策略

接下来,我们放大每个指标的前 5 个策略。它们都涉及到最大剩余候选数排序算法和基线决策规则。结果显示了不同的种子词对于不同的目标有多好。

基于达成解决方案的平均步骤数的最佳策略是:

图片作者。

解决方案成功率排名的顶级策略非常接近。排名 1 到 2 和 2 到 3 之间的差异是 0.0432%,这相当于 2315 场比赛中只有 1 场。

图片作者。

按在 3 个步骤或更少步骤内解决的挑战比例列出的最佳策略是:

图片作者。

限制:对于机器人,由机器人

这篇文章和其他类似文章的发现的主要局限性是,推荐的种子词不一定适用于随便写写文章的人。我们已经表明,最佳种子词取决于您如何玩游戏以及您试图优化的指标。除非你能像机器人一样玩游戏,拥有(1)关于解空间的完美信息和(2)使用排序算法评估其中一大块的计算能力,否则最佳种子词对你来说可能会不同。

也就是说,对于人类玩家来说,专注于种子词是可行的,因为这是可操作的。如果我们真的需要一个关于使用什么种子词的建议,我们应该看看那些在三个指标以及各种排名算法中表现良好的词(在上图中用它们各自的颜色标出)。他们可以在不同风格的游戏中表现出色,但是需要更多的工作来用一个更像人类的机器人来验证这一点,并理解为什么这些单词工作得很好。

结论

在这篇文章中,我们展示了改变策略的其他部分,即(1)排序算法和(2)在解决与信息收集之间进行优先排序的决策规则,会影响“最佳”种子词。讨论中的度量标准,无论是(I)达到解决方案的平均步骤数,(ii)成功率,还是(iii)在 3 个步骤内解决的挑战的比例,对于确定“最佳”的含义以及“最佳”种子词是什么都很重要。

基于这些结论,我们不应该简单地接受基于与非人类机器人玩的模拟游戏的结果而产生的种子词推荐。需要更深一层的探究,以确定那些不管谁或什么在使用它们,也不管游戏是如何进行的都有效的词。

参考

  1. J.沃德尔,powerlanguage.co.uk。
  2. B.Smyth,我从玩了一百多万场 Wordle (2022)的游戏中学到了什么,走向数据科学。
  3. T.格莱伊尔,Wordle(2021)中数学上最优的第一次猜测,中等。
  4. T.尼尔,破坏乐趣:一个 Wordle 自动解算器 (2022),在派对上不好玩。
  5. 南 Dua,深入探究 Wordle,新疫情谜题热潮 (2022),走向数据科学。
  6. B.Bakhtiari,关于 Wordle (2022)的一句话,走向数据科学。
  7. J.Stechschulte, 🟩🟩🟩🟩🟩优 Wordle (2022),走向数据科学。
  8. 米(meter 的缩写))刘明辉,在说什么? (2022),中等。

熊猫中的 loc 与 iloc。区别就在这里。

原文:https://towardsdatascience.com/loc-vs-iloc-in-pandas-heres-the-difference-16cd4bcbecab

如何使用 loc 和 iloc 在 Pandas 中选择行和列

尼尔和祖玛·斯科特在的 Unsplash 上的照片

当谈到在熊猫中选择数据时,有不同的选择。其中最流行的就是用lociloc,但是两者有什么区别呢?

当我开始学习 Pandas 时,我也有同样的问题,在本文中,我将向您展示 Pandas 中使用lociloc选择数据的主要区别,并展示一些示例来说明这一点。

到本文结束时,您将知道如何使用lociloc选择单个值、多行和多列。

loc 和 iloc 之间的区别

lociloc的主要区别在于loc是基于标签的(您需要指定行和列标签),而iloc是基于整数位置的(您需要通过整数位置值指定行和列,从 0 开始)

下面是更好理解这一点的实际例子。你可以观看我的 YouTube 视频,也可以继续阅读这篇文章。

考虑具有以下行标签和行位置的数据帧:

作者图片

现在让我们看看如何使用 loc 和 iloc 选择随机元素:

作者图片

在上面的例子中,lociloc返回相同的输出,除了最后一个元素被包含在loc中而被排除在iloc之外。

使用 loc 和 iloc 从数据帧中选择元素

为了详细了解 loc 和 iloc 之间的差异,让我们创建一个包含顶级足球运动员基本信息的数据框架。

下面是创建这个小数据框架的代码:

下面是运行代码后您应该获得的数据帧:

作者图片

现在让我们看看如何用单个值、列表和切片从数据帧中选择元素。

选择具有单个值的数据

我们可以通过添加一个值作为输入来定位带有lociloc的元素。下面是要遵循的语法:

  • loc[row_label, column_label]
  • iloc[row_position, column_position]

比方说我们想得到莱昂内尔·梅西的身高。

# *get the height of L.Messi*# **loc**
>>> df.loc['L. Messi', 'height_cm']
# **iloc**
>>> df.iloc[0, 1]170

如您所见,loc需要“L.Messi”和“height_cm ”,因为它们是行/列标签,而iloc需要 0 和 1,因为它们是行/列位置。两种方法得到的输出一样 170,这是梅西的身高。

现在我们来看看 c 罗的身高。

*#* *get the* height *of Cristiano Ronaldo*# **loc**
>>> df.loc['Cristiano Ronaldo', 'height_cm']
# **iloc**
>>> df.iloc[1, 1]187

如果您想获得特定行/列的所有数据,使用lociloc中的:

*#* *get all the data about L.Messi*# **loc**
>>> df.loc['L. Messi', :]
# **iloc**
>>> df.iloc[0, :]age                             32
height_cm                      170
nationality              Argentina
club           Paris Saint-Germain

用列表选择多行/多列

我们可以使用标签/位置列表来选择分别带有lociloc的多行和多列。

*# get all data about L.Messi and Cristiano Ronaldo*# **loc**
>>> df.loc[['L. Messi', 'Cristiano Ronaldo']]
# **iloc**
>>> df.iloc[[0, 1]]

以下是我们选择的输出:

作者图片

现在我们通过将' height_cm '列标签/'1 '列位置分别加到lociloc上,只得到这两个球员的身高。

*# get the height of L.Messi and Cristiano Ronaldo*# **loc**
>>> df.loc[['L. Messi', 'Cristiano Ronaldo'], 'height_cm']
# **iloc**
>>> df.iloc[[0, 1], 1]L. Messi             170
Cristiano Ronaldo    187

用切片选择多行/多列

我们也可以用一个带有lociloc的切片选择多个行和列。

让我们做一个切片,得到列'年龄','身高 _ 厘米',和'国籍'。

*# slice column labels: from age to nationality**#* ***loc***
>>> players = ['L. Messi', 'Cristiano Ronaldo']
>>> df.loc[players, 'age':'nationality']
***# iloc***
>>> players = [0, 1]
>>> df.iloc[players, 0:3] # age:nationality+1

与列表不同,当我们选择带有切片的元素时,最后一个元素“nationality”包含在loc方法中,而最后一个元素“3”在iloc中被排除。

下面是我们得到的lociloc的输出:

作者图片

带条件选择

每当您想要基于某些条件选择元素时,请记住iloc需要一个布尔列表,因此我们必须使用list()函数将我们的系列转换为布尔列表。

*# one condition: select player with height above 180cm****# loc***
columns = ['age', 'height_cm', 'club']
df.loc[df['height_cm']>180, columns]
***# iloc***
columns = [0,1,3]
df.iloc[list(df['height_cm']>180), columns]

下面是我们得到的lociloc的输出:

作者图片

同样的规则也适用于你想要应用多个条件的情况。比如说我们想获得在 PSG 打过球的身高 180cm 以上的球员。

*# multiple conditions: select player with height above 180cm that played in PSG****# loc***
df.loc[(df['height_cm']>170) & (df['club']=='Paris Saint-Germain'), :]
***# iloc***
df.iloc[list((df['height_cm']>170) & (df['club']=='Paris Saint-Germain')), :]

下面是我们得到的lociloc的输出:

作者图片

就是这样!现在你可以使用lociloc从数据帧中选择元素了。你可以在我的 Github 上找到这篇文章写的所有代码。

https://frankandrade.ck.page/bd063ff2d3

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你用我的链接注册,我会赚一小笔佣金,不需要你额外付费。

**https://frank-andrade.medium.com/membership **

当地气候分析:越南卫生机构的雨水暴露

原文:https://towardsdatascience.com/local-climate-analytics-health-facility-rain-exposure-in-vietnam-6f7a40442e83

在基于云的 Jupyter 笔记本 Python 环境中处理地理空间卫星影像数据,以测量关键公共基础设施的降雨情况

这项工作完全使用公开可用的数据完成,与Anh Tu \u Phanparva thy KrishnanHieu Danh Luu合著。所有错误和遗漏都是作者的。

“水,到处都是水,没有一滴可以喝”

塞缪尔·泰勒·柯尔律治的诗,《古舟子咏》

水体,越南宁城 thuận,作者范怀忠

在这个大数据时代,能够更好地洞察当地天气和气候变化风险的地理空间数据层正在激增。但对于为世界各地政府工作的最终用户,以及寻求支持他们的更懂技术的数据科学家来说,以灵活有效的方式利用这些资源来解决一线问题可能具有挑战性。

如何实现这种大数据映射洞察,以提供实用的本地洞察?我们展示了如何使用基于云的 Jupyter 笔记本 Python 环境(cJPNE) 来评估在越南获得中风护理的可能脆弱性。这是一个更广泛的例子,探讨如何分析公共基础设施资产和投资,以更好地应用当地风险和弹性分析。

卫生设施和道路网络连接等基础设施的位置对于人们获得公共服务至关重要。除了在正常环境条件下分析这种获取途径之外,持续的极端天气威胁——以及与气候变化相关的进一步风险——可能会引发对医疗保健点等重要物理服务获取途径的更多关注。

新一代的危险层提供了越来越丰富的可能暴露感。很好的例子包括由世界银行全球减灾和恢复基金(GFDRR)建立的 Think Hazard 站点。联合国政府间气候变化专门委员会(IPCC)最近的 2021 年第六次评估报告揭示了全球可能出现的不同范围的极端天气变化。IPCC 还提供了一个交互式地图集,提供了一系列维度的场景层。

图一。最新 IPCC 情景(来源: IPCC 互动图册 )

随着越来越多的地理空间灾害风险和气候变化图层可供用户查看,如何将这些图层与探索更具体的公共基础设施资产和投资服务风险联系起来?直观地说,这项任务将涉及覆盖各自的基础设施、人口和风险层,以了解暴露程度最大的地方(见图 2)。

图二。保健获取描述性和规定性分析(资料来源: ABW 和世界银行 )

基于云的 Jupyter Python 笔记本可用于将地理空间风险分析应用于跨空间和时间的公共基础设施访问

基于云的 jpne(“cjnpe”)提供了一个强大的平台技术来集成和分析关键数据,以提供描述性(是什么)和规定性(可能是什么)见解。在之前的Medium for Data Science 投稿中,我们展示了如何在基于云的 JPNE 环境中部署两个具有高级地理空间粒度的人口分布层。很好地使用基于云的 JPNEs 的一个组成部分是建立清晰和可复制的数据管道,并确定适合目的的开源 Python 库,这些库能够解决感兴趣的特定问题。在本例中,我们调查了越南卒中机构暴露于潜在极端降水层的基本情况。

股市投资者会经常听到这样一句话:“过去不能指导未来。”在气候变化的时代,降雨模式肯定也是如此。一系列可用的地理空间图层属于描述性(过去是什么样子)和预测性(未来可能是什么样子)两类。天气预报是预测的一个例子,但是昨天在任何给定位置测量的降雨量的报告是描述性的。与此同时,各种科学计划正在利用大数据和机器学习模型在全球范围内生成地理空间风险层。

为了说明如何使用这些类型的地理空间图层来筛选公共基础设施资产或投资,我们首先探讨了越南估计的 106 个中风设施对历史降水和可能的洪水风险的明显暴露。这个博客回顾了气候灾害组织红外降水站的降雨情况。下一期将把它与预计的 Fathom-Global 2 洪水数据进行比较。al 2021a,b),这是前瞻性的风险层。

利用新环境暴露层的财富意味着让领域专家和数据科学家首先解决基本数据访问和分析问题

探索基于云的 JPNEs 中的地理空间数据层的起点是能够清楚地说明它们测量什么,以及数据的时间和地理空间粒度。简而言之,数据以何种频率和位置细节可用,有多长的延迟?对于预测数据,时间维度通常用某个预测时段来表示。对于描述性的历史数据,问题是数据在一个一致的系列中回溯到多远,以及以什么样的频率报告。从基于云的 JPNE 的角度来看,另一个问题是数据访问(和更新)是否已经可以自动化(通过 API),或者它们是否需要下载数据和转移数据的手动中间阶段。这个过程的自动化程度越高,练习的可复制性就越强。

在开展这项工作时,用户应了解(I)每个说明性图层捕捉的是什么测量值——历史测量值还是预测测量值,( ii)这些图层如何以有意义的方式与现有公共卫生基础设施设置的位置相交,以及(iii)下一步改进分析和数据以使当地决策者受益的选项。图 3 显示了在 Google Colab 上设置的这个分析的 JPNE 用户界面视图。所有代码都可以运行,相关数据也可以通过云存储和访问。

图三。JPNE 接口(来源: 云 JPNE )

从基于云的 JPNE 部署的角度来看,地理空间数据访问管道可以运行从手动到机器对机器自动的频谱。例如,手动过程需要用户从网络链接(或从电子邮件或 u 盘)手动下载数据。),然后根据用于访问的 cJNPE 来部署它(例如,在 Google Drive 文件夹中的每个挂载)。一个自动化的过程将允许从“真实的来源”存储库中自动调用数据。虽然前一种方法可能在特定的基础上(或对于不更新的数据集)有效,但它是一种既麻烦又脆弱的数据管理实践。诸如 API 之类的机制还允许 eJPNE 检查什么是最新的可用数据(例如,降雨量数据),并相应地更新仪表板或可视化方面的系列。理想情况下,API 还会拉出与任何数据馈送相关联的关联元数据(例如,最新数据版本和访问日期)。

开始使用 CHIRPS 历史卫星降水可以从不同的接入点开始

我们对啁啾声的 cJPNE 处理说明了卫星数据读数的可能价值。这些数据最初是为了支持更好的国际饥荒预测而开发的。该数据集以准全球格式提供了全世界近实时的降水数据。它在多个时间框架下提供,供用户选择。CHIRPS ver 2.0 的数据从 1981 年 1 月 1 日到接近实时。它拥有世界各地所有站点的每日、5 天、10 天、每月、2 个月、3 个月、季度和年度数据。向公众提供的数据集以准全球格式(50S-50N)覆盖整个世界。CHIRPS 2.0 也有几个站点的更详细的数据:非洲、东非、墨西哥、喀麦隆-加勒比和印度尼西亚。数据以标记图像文件格式(TIFF/TIF)提供,这是栅格数据的一种常用格式。对于越南,数据是从全球数据集中提取的,每个格网单元的直径约为 5 千米。

最新的啁啾数据可以从不同的来源获得。这包括我们使用的加州大学圣巴巴拉分校(UCSB)研究小组的网站,以及谷歌地球引擎等平台。后一种资源通过简单的 API 请求对数据集上的任何更改提供近乎实时的更新,但需要 Java 连接。可以用不同的方式处理原始数据,以产生感兴趣的定义。最重要的是,数据的一些初始描述-分析 cJPNE 可视化有助于向最终用户和数据科学家提供数据访问已经实现的安慰,并为初始结果提供现实检查。

对于最初的数据访问和争论,我们试图了解我们的 106 个卒中机构中的哪一个暴露于最近一个月的最高月降雨量水平。为了说明此地理空间图层的用途,我们首先提取了撰写本文时越南境内 2021 年 10 月的所有 11,204 个最新月值(覆盖该国超过 33 万平方公里的面积)。

设施的占地面积可以更大,但数据是在点的基础上提供的。因此,我们将这些设施点与降水网格进行了匹配。我们创建了一个这些值的网格,并将网格中每个设施的每个值相交。在收集了匹配对的列表之后,可以使用不同的聚合函数(求和、最大化等。)来找到匹配对的值。

我们使用 raster2xyz 库转换了 JNPE 中的数据,以便对 pandas 和 geopandas 进行分析。在本例中,我们着眼于 2021 年 10 月的总降雨量,并将数据缩小到 stroke 设施的周围区域:

我们根据 2021 年 10 月的降雨量数据对设施进行降序排列。东海越南古巴友谊医院以超过 1,294 毫米的降雨量位居榜首。这似乎是合理的,因为中央海岸地区目前正处于雨季/暴风雨季节。后来的情况进一步证实了这一现象,因为所有五大设施都在该地区,10 月份的降雨量都超过 1150 毫米(见表 1)。

另一方面,后五个职位来自北部地区。10 月份老蔡以 135 mm 排在最后。2021 年的冬天来得很早,用干冷的风影响了北部山区的整体天气。

表 1。2021 年 10 月最高和最低月降雨量的设施

我们可以使用 geopandas 的绘图功能来可视化这些数据。图 4 显示了 2021 年 10 月的降雨量数据,基于 11,204 个网格单元的总降雨量。纵轴表示纬度和水平经度,例如由智能手机的 GPS 读数提供。

图四。越南 2021 年 10 月降水层 w/东海医院(来源: 云 JPNE PIM 气候变化筛选教程 )

基于图 4 所示的地图可视化,我们可以进一步确认表 1 的排名结果及其叙述:与该国其他地区相比,沿海中部地区在初冬通常降雨量更多。

接下来,我们有兴趣了解这种高水平的降雨是集中在几天内,还是仅仅代表持续降雨。为了做到这一点,我们提取了东海越南-古巴友谊医院的每日数据,并根据该国的日平均降水量绘制了其每日值。

图 5。2021 年 10 月东海医院的日降雨量和全国平均降雨量(作者提供的图表)

我们可以看到越南东海及其周边地区的日降水量趋势——古巴友谊医院证实了其月度读数。它的日降水量比该国其他地方高得多。

CJPNE 设置还检查了该位置或越南任何其他位置的长期天气暴露环境。例如,在考虑新的设施投资时,可以使用这种方法。为了从国家和历史的角度来看这个问题,我们接着问根据 CHIRPS 从 1981 年 1 月到 2021 年 10 月记录的最高月降水量是多少。2020 年 10 月,顺化富禄区禄迪恩(北纬 16° 12 ' 00.0 "东经 107° 42 ' 00.0 ")的降雨量为 1,914.2 毫米,2016 年 12 月 13 日,顺化富旺区 Ha Trung Marsh(东经 16° 30 ' 00.0 "北纬 107° 42 ' 00.0 ")的最高日降雨量为 691.974 毫米。

我们从 CHIRPS 的 p05 目录中从 1981 年 1 月 1 日至 2021 年 10 月 31 日的所有可用日降雨量文件中收集了越南的最大降雨量值。对于每个数据文件,我们取越南边界内的所有数据点,然后记录越南所有地区的最大值。数据收集过程从 2021 年 11 月 31 日持续到 2021 年 12 月 5 日。我们使用 matplotlib 的误差条形图来显示越南最大日降雨量的广泛分布,以了解极端降雨量的历史事件及其相互关系。

图六。越南 1981 年 1 月 1 日至 2021 年 10 月 31 日的最大日历史降雨量(作者提供的图表)

如图所示,在整个年历中,降雨量值具有极右的尾部。这些异常值通常是 T4 拉尼娜现象对越南中部沿海地区的影响造成的。平均值通常随着我们从春季到夏季的进展而上升,并在 9 月底达到峰值,这是越南的暴风雨季节。随着冬天的临近,这些数值缓慢下降,表明北部和中部地区通常是干燥寒冷的冬天,而南部是干燥的季节。

根据国家层面的提示,我们对东海越南-古巴友谊医院周围地区进行了类似的研究,看看拉尼娜现象是否是 10 月份高降雨量背后的驱动力。所有 1981 年至 2021 年 10 月份的每日降水数据都是从 UCSB CHIRPS v 2.0 数据集于 2021 年 12 月 6 日收集的。

图 7。1981 年 10 月 10 日至 2021 年 10 月 10 日东海医院的最大日历史降雨量(作者提供的图表)

当我们在 2021 年 10 月更近距离地观察我们雨量最大的中风治疗机构——东海越南-古巴友谊医院时,我们可以看到与上面在国家层面看到的相似的模式。大多数年份雨水不多。通常,10 月份的日降雨量接近于 0,整个月到处都有小雨。然而,降雨日数据在某些年份偶尔会出现极端右尾现象。

图 8。1981 年 10 月 10 日至 2021 年 10 月 10 日东海医院的月历史降雨量

通过观察月尺度,人们可以看到拉尼娜如何在整个历史视野中创造极端的降雨量。高降雨量与拉尼娜现象的发生相吻合:1983 年(1,085.16 毫米)、1988 年(1,273.45 毫米)、1989 年(998.72 毫米)、1999 年(906.69 毫米)、2007 年(1,208.37 毫米)、2008 年(1,182.09 毫米)、2010 年(1,364.92 毫米)、2016 年(1,264.72 毫米)因此,我们在图 6 中看到的发生在 2021 年 10 月的事件并不是一个异常值,而是一个与更广泛的现象相关的效应。

高降雨量可能意味着该设施在这个季节发生洪水的可能性很高。然而,实际影响将取决于所在地的地形和基础设施的类型(如排水、防护墙等)。)都到位了。在随后的博客中,我们根据 Fathom 的数据讨论了这个问题。

下一步去哪里?

我们已经展示了如何使用基于云的 JPNE 平台来评估越南中风设施位置的极端降雨量。虽然我们已经选择了中风设施,这种方法当然可以用于任何其他类型的基础设施。关键的一点是,cJPNE 平台可以用于构建相关问题,并通过渐进和迭代地分析可用数据来回答这些问题。

这项工作当然可以不断改进。首先,分析必须确认有关卫生设施的位置数据既全面又正确。第二,问题可能不是设施点本身的直接暴露,而是通向设施点的道路的暴露和中断。最后,无论是回顾还是展望,曝光层都可能受到很大程度的不确定性的影响(即局部信噪比)。对于任何可操作的见解,地面实况和现实检查需要成为这类应用工作的一部分!

没有任何分析和可视化是完美的,但是基于云的 JPNE 为这些风险提供了一个很好的基准,例如通过用不同的可比数据层运行相同的图表。如果在卫生设施可及性方面的结果(即使用案例年)保持稳定,这可能会导致不同的结论,而不是根本不同。

地球应对气候变化的现实是极端和不确定性的风险越来越大。像 cJPNE 这样的平台工具提供了一个实用的基础,将上下文层与本地公共服务基础设施访问问题的实际情况联系起来。它们有助于组织本地用户进入越来越多的大数据环境层,包括 IPCC 重点关注的大数据环境层。一旦完成这种探索性分析,就可以为最终用户制作更加精美的仪表板和可视化效果,作为交互式网站。但是,在通过基于云的 JPNE 分析与现实世界日进行比较之前,转向网站开发可能会增加决策支持价值的时间延迟和成本。

我们在部署基于云的 JPNE 流程方面积累了越来越多的经验,包括世界银行的新冠肺炎及其他地区大数据观察站(BDO)和越南公共资产治理颠覆性技术(DT4PAG)项目。通过正在进行的一系列中型博客投稿(例如 【用 Python 可视化全球人口数据集】 ),我们将继续分享我们的经验,特别是它们如何帮助推进数据驱动的见解以实现可持续发展目标,并推动在实践中学习应用技术和技能建设的进展,重点是公共部门。

请告诉我们您在培养人员和流程方面的经验,以实现更好的数据驱动型决策,尤其是为发展中国家的公共政策提供描述性和规范性见解。

每个数据科学家都应该知道的位置分析用例

原文:https://towardsdatascience.com/location-analytics-use-cases-that-every-data-scientist-should-know-740b708a2504

使用位置分析的强大方法

位置分析(图片由作者使用谷歌地图和位置分析)

据估计,当今世界 80%的数据都包含位置信息。我们今天的大多数活动,比如拍照、带着智能手表跑步、乘坐出租车旅行,都会产生位置数据。

在这个故事中,您将看到如何使用位置分析将这些数据转化为非常有趣且有利可图的用例。

地理密度分析

位置分析可用于分析特定地理区域的密集程度。让我举例说明密度分析来分析纽约的 Airbnb 地点。

纽约大约有 5 万个 Airbnb 地点。这些数据在 Airbnb 网站上是公开数据。该数据包含主机详细信息以及由纬度和经度定义的位置。可以进行密度分析来找出 Airbnb 位置数量最多的纽约地区。

进行密度分析需要地理区域的多边形定义。纽约地区(也称为邻域)的面定义作为开放数据提供。纽约街区的所有多边形定义都可以如下图所示进行绘制。

纽约街区的多边形定义(图片由作者使用谷歌地图和位置分析)

下一步是使用面中点算法将每个位置分配给面。

多边形中的点算法(图片由作者提供)

多边形中点算法的输出将给出每个多边形中 Airbnb 位置的数量。然后,我们可以利用这些信息,通过地理地图上的热图进行密度分析。

密度分析(图片由作者使用谷歌地图和位置分析)

所用技术概述

  • 位置分析:多边形中的点算法
  • Javascript:谷歌地图 API

邻近分析

邻近分析对于分析不同位置之间的距离非常有用。这对于设施规划用例非常有用。

假设我们想在曼哈顿建立一个新的图书馆。曼哈顿城被分成许多块。问题是我们应该把图书馆建在哪个地段?这就是邻近分析可以发挥作用的地方。

图书馆的理想位置是附近没有图书馆的地方。邻近分析算法有助于计算任何批次到最近库的距离。这是近似分析的结果。

邻近分析(图片由作者提供)

所有的拍品都有从黄色到紫色的颜色。黄色表示附近有图书馆,紫色表示离任何现有的图书馆都很远。

紫色区域是服务水平较低的区域,可能是建立图书馆的好地方。

邻近分析使用球树算法,这对于查找不同位置之间的距离非常有用。该算法本质上将在所有批次和具有库的批次之间进行最近邻搜索。

所用技术概述

  • 位置分析:多边形中的点算法,球树算法
  • Javascript:谷歌地图 API

路线分析

路径分析是位置分析的另一个重要部分。它有助于分析路线或轨迹。让我用滑行轨迹分析的例子来说明这一点。

本例中的数据来自安装在 Porto taxi 上的 GPS 跟踪设备,该设备每 15 秒记录一次出租车的位置。这是来自出租车的位置数据热图。正如我们所看到的,大部分的滑行轨迹都在波尔图,但你也有一些从波尔图到里斯本的长途滑行轨迹。

路线分析(图片由作者使用谷歌地图和位置分析)

我们还可以分析街道层面的数据。这有助于我们识别连续热点和孤立热点。

连续和孤立的热图分析(图片由作者使用谷歌地图和位置分析)

连续热点是针对出租车通常走的路线。需要通过放大地图来进一步分析孤立的热点。

孤立热点分析(图片由作者使用谷歌地图和位置分析)

我们看到孤立的热点是在一个豪华游艇停泊处附近。这意味着出租车可能在这里等着上下车。因此,正如我们所知,出租车司机花费时间只是等待,这将是一个安装电动汽车充电器的好地方。这将有助于普及电动汽车的意识,并为电动出租车车主提供便利。因此,对于许多用例来说,结合位置数据及其周围环境是一种非常强大的方法。

所用技术概述

  • 位置分析:多边形中的点算法
  • Javascript:谷歌地图 API

路线优化

位置分析的一个重要用途是路线优化。数据科学算法可以帮助找到两地之间的最佳路线。

寻找两点之间的最佳路线(图片由作者提供)

我们可以将这样的算法应用于滑行轨迹数据。这是路线优化的结果,显示了波尔图的两个地方——Museu de Cidade 和 Livrana Lello 之间的最佳路线和非最佳路线。

路线优化(图片由作者使用谷歌地图和位置分析)

寻找最佳路线是位置分析和图形分析的混合。位置是节点,节点之间的路由是边。有了节点和边,人们可以构建一个图,并使用各种图或网络算法。

用于路径分析的一种有用的图或网络算法是最短路径算法。

所用技术概述

  • 位置分析:多边形中的点算法
  • 图形或网络分析:最短路径算法
  • Javascript:谷歌地图 API

结论

位置数据无处不在,使用分析的力量将这一强大的数据资产转化为强大的用例非常重要。

媒体订阅和推荐链接

订阅 每当我发布一个新的故事时,请保持通知。

您也可以通过我的 推荐链接 加入 Medium

https://pranay-dave9.medium.com/membership

额外资源

网站(全球资讯网的主机站)

你可以访问我的网站进行零编码分析。https://experiencedatascience.com

Youtube 频道

这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接

数据源引用

Airbnb 数据—开放数据:

数据集可用于研究目的

https://www.kaggle.com/dgomonov/new-york-city-airbnb-open-data

波尔图出租车数据

数据集可用于研究目的

https://archive . ics . UCI . edu/ml/datasets/Taxi+服务+轨迹+-+预测+挑战,+ECML+PKDD+2015

莫雷拉-马蒂亚斯,l .、伽马,j .、费雷拉,m .、门德斯-莫雷拉,j .、达马斯,“利用流数据预测出租车-乘客需求”。摘自:IEEE 智能交通系统汇刊,第 14 卷,第 3 期,第 1393–1402 页,2013 年 9 月

对数正态分布-一个简单的解释

原文:https://towardsdatascience.com/log-normal-distribution-a-simple-explanation-7605864fb67c

如何计算μ & σ、众数、均值、中值和方差

关于

我们将简要介绍对数正态分布的定义,然后根据简单数据计算分布的参数μ和σ。然后我们将看看如何从这个概率分布中计算平均值、众数、中位数和方差。

非正式定义

对数正态分布是向右倾斜的连续概率分布,这意味着它有一个向右的长尾。它用于模拟各种自然现象,如收入分配、象棋比赛的长度或修复可维护系统的时间等。

对数正态概率密度函数|作者图片

对数正态的概率密度函数由两个参数 μσ 定义,其中 x > 0:

μ是位置参数,σ是分布的比例参数此处谨慎!这两个参数不应被误认为更常见的正态分布的平均值或标准差。当对数正态数据使用对数进行转换时,μ可视为平均值(转换后的数据),σ可视为标准差(转换后的数据)。但是如果没有这些变换 μσ 这里只是定义对数正态的两个参数,而不是均值或标准差!好了,现在我们从“让我们保持简单”到“信息有点多”。让我们回头再看一下刚才提到的对数正态分布和正态分布之间的关系。

“对数正态”分布的名称表明它与对数以及正态分布有关。怎么会?假设您的数据符合对数正态分布。如果对所有数据点取对数,新转换的点将符合正态分布。这仅仅意味着当你取对数正态数据的对数时,你会得到一个正态分布。见下图。

正态函数和对数正态函数之间的关系|图片由作者提供,灵感来自维基百科的

对数正态分布的数据点由 X 变量给出。当我们对数变换那个 X 变量 (Y=ln(X)) 时,我们得到一个正态分布的 Y 变量。

我们可以颠倒这种思维,转而看 Y 。如果 Y 具有正态分布,我们取 Y (X=exp(Y)) 的指数,那么我们回到我们的 X 变量,它具有对数正态分布。在分析对数正态分布的重要属性时,记住这一形象有所帮助:

“分析对数正态分布数据的最有效方法是将基于正态分布的众所周知的方法应用于对数转换后的数据,然后在适当的情况下对结果进行反向转换。” Lognormal wiki

根据数据估计μ和σ

我们可以使用最大似然估计(MLE)来估计对数正态参数μ和σ。这是一种用于近似分布参数的流行方法,因为它找到了使我们的假设概率分布'最有可能''用于我们的观察数据的参数。

如果你想更详细地了解 MLE 是如何工作的, StatQuest 以一种有趣直观的方式解释了这种方法,并且还导出了 正态分布 的估计量。

正态分布的最大似然估计量为:

然而,我们需要对数正态分布的最大似然估计μ和σ,它们是:

这些公式几乎相同。我们可以看到,我们可以使用与正态分布相同的方法,首先用对数变换我们的数据。如果你对我们如何得到对数正态估计量感到好奇,这里有一个到 推导 的链接。

简单的例子在哪里?!

让我们来看看遵循对数正态分布的 5 个收入值。我们的虚拟人物 1 赚 20k,人物 2 赚 22k,以此类推:

我们现在可以用上面的逻辑估算μ 。首先,我们取每个收入数据点的对数,然后计算 5 个转换数据点的平均值,如下所示:

表 1 |作者图片

这为我们的位置参数μ给出了值 3.36

然后,我们可以使用估计的μ to 通过以下公式近似我们的σ

我们不用计算σ,而是用上面公式的平方根来近似σ。该公式还使用 n-1 而不是 n 来得到一个偏差较小的估计量。如果你想更多地了解这一变化,请看一看校正样本方差(或者也可以看一看贝塞尔校正)。

表 2 |作者图片

与上面类似,第一步是取每个个人收入数据点的对数。然后,我们从每个经过对数变换的数据点中减去估计的μ,然后对每个结果求平方。见上表。然后将这些值插入上面的公式中:

这给了我们比例参数σ 的值 0.4376

注意:这些计算只是如何获得这些值的一个例子。你需要更多的值才能有统计学意义。

计算中值、平均值、众数和方差

一旦我们有了参数μ和σ,提取对数正态分布的一些重要性质就很简单了。请参见下表和下图中的关键属性、它们的公式以及我们的示例数据的计算。

表 3 |作者图片

我们如何得出上表中的不同公式?

  • 中位数是通过取对数正态累积分布函数,将其设置为 0.5,然后求解此方程(见此处)得出的。
  • 模式代表分布的全局最大值,因此可以通过对对数正态概率密度函数求导并求解 0 (见此处)来导出。
  • 对数正态分布的均值(也称为期望值)是所有可能值(见此处)的概率加权平均值。
  • 对数正态分布的方差是平均值(见此处)的方差平方的概率加权平均值。

简单数据的对数正态概率密度函数示例|作者图片

参考

[1]维基百科,对数正态分布 (2022),2022-02-06 检索

[2] M .塔博加,《对数正态分布》,概率论与数理统计讲座 (2021),Kindle 直接出版。在线附录。

[3] A. Katz,C. Williams 和 J. Khim,精彩:对数正态分布 (2022),检索于 2022 年 2 月 6 日

[4] J. Soch,K. Petrykowski,T. Faulkenberry,统计证明之书 (2021),github.io

[5]维基百科,贝塞尔的更正 (2022),检索于 2022–02–06

使用数据分析的物流绩效管理

原文:https://towardsdatascience.com/logistic-performance-management-using-data-analytics-82b2da978e5f

实施运营指标,以监测和改善国际分销网络的绩效

(图片由作者提供)

目标
了解如何使用数据分析来衡量和改善您的物流网络的端到端性能。

简介 供应链是一个由流程库存点组成的面向目标的网络,用于向客户交付商品和服务

您的网络性能可以用一句话来概括

您是否完全按时交付您的最终客户?

在这个简单的问题背后,有一套复杂的 KPI 需要监控了解你的表现

在本文中,我们将尝试理解最低指标集试验复杂物流作业所需的方法学

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

**SUMMARY**
**I. Example of a Simple Supply Chain Network** Production and delivery of garments for a fashion retailer
**II. End-to-End Lead Times** Lead time between the order creation and the store delivery
**1\. Information flow** Each step of the process is tracked by different systems
**2\. End-to-End Visibility** Record time stamps from order creation to shipment delivery
**3\. KPIs and Lead times** Actual versustargets lead times **IV. Conclusion
1\. Root Cause Analysis** What can go wrong? **2\. Next steps** Root cause analysis and improve the performance

如果你喜欢看,可以看看这篇文章的视频版本

一.简单供应链网络的例子

方案

你是一家在世界各地都有商店的国际服装集团的后勤经理。

该公司在亚洲的工厂生产服装、包包和配饰。

门店本地仓库发货,由工厂直接补货。

**💡** Our focus here will be on the distribution. The aim is to ensure on time delivery assuming that we have the stock on hand in the warehouses.

库存补充

配送计划人员正在管理商店的库存,以满足需求。

供应链网络—(图片由作者提供)

特定参考的库存水平达到最低水平时

  • 配送计划员在 ERP 中创建一个补货订单,其中包含每件商品的数量要求的交付日期
  • 订单被传送到仓库管理系统
  • 仓库运营团队 准备订单 出货
  • 运输团队在仓库组织提货
  • 发货是在门店 发货收货
**💡** In our example, stores replenishment are managed by a team of distribution planners. But it can be also completely automated using ERP or inventory management systems.

如果你想了解更多的库存管理政策,请看下面的文章

**https://www.samirsaci.com/inventory-management-for-retail-periodic-review-policy/

仓库补货

同样的流程由供应计划团队在仓库层面完成,以确保订单准备的最低库存水平。

**💡** This part of the process **will be excluded** because it involves complex processes linked with the upstream flows from factories**.** We will focus only on the distribution to stores.

总体业绩:完全准时(OTIF)

整体流程绩效可以通过网络按时交付商店正确数量的物品(全部)的能力来衡量。****

完全准时—(图片由作者提供)

作为一名物流绩效经理,你的重点将是提高这一推动内部顾客(即商店)满意度的指标。

**💡** For more information about the KPI **On Time In Ful**l have a look at this short explainer video in my youtube channel: [**Link to Youtube**](https://www.youtube.com/watch?v=qhLqu6M7lcA&t=1s)

(作者的 Youtube 视频)—供应科学

端到端分析

然而,这个指标本身并不能让你全面了解供应链中发生了什么。

因此,我们需要使用数据分析对其进行分解,并了解影响整体性能的因素。** ****http://samirsaci.com ****

二。端到端配送流程

让我们来分解订单创建和发货之间的不同步骤。

1.信息流

供应链分析中,一切都从理解信息流开始。

通过连接到正确的系统,你将提取和处理正确的信息。

信息系统—(图片由作者提供)

场景:商店补货 在几家商店中,一些参考的库存水平低于最低安全水平。

  1. 配送计划员正在 ERP 中创建一个补货订单,其中包含数量和要求的交付日期
  2. 订单在预计提货日期之前被传送到 WMS
  3. 仓库运营团队准备并包装订单
  4. 仓库分配一辆卡车用于提货
  5. 装运信息被传送到 TMS 进行跟踪
  6. 货物被送到商店
  7. 商店团队在 ERP 中接收物品
****💡** In a perfect world, we will assume that these systems are perfectly connected using API/EDI to ensure a continuous traceability of the flow.** 

2.端到端可见性

连接不同的系统后,您可以详细了解补货订单创建和商店交付之间的每个步骤。

端到端时间戳—(图片由作者提供)

  • 订单接收时间:订单在 WMS接收并准备入库时的时间戳****
  • 仓库操作 : 拣货装箱出货由 WMS 跟踪****
  • 运输:订单从出货交货的跟踪****
  • 门店收货:ERP 中门店团队收货时的时间戳****

在订单创建过程中,计划员会添加一个请求交付日期,该日期可用于计算每个流程的目标时间。

端到端时间戳与目标-(图片由作者提供)

因此,我们可以在每一步了解运营是否落后于计划,并找到潜在的瓶颈。

****💡** Time stamps are estimated considering the requested delivery date. We use the target leadtimes of each step from creation to delivery to estimate the time stamps.**

3.交付时间

从运营的角度来看,只关注交付时的 OTIF 是没有意义的。

交付周期定义—(图片由作者提供)

通过子流程进行的分段是强制性的,以监控物流网络每个分支的性能:

  • 订单转移受到基础设施&软件的影响
  • 订单准备与仓库操作的 能力和 生产率相联系
  • 包装结束和装运时间之间的提货计划提前期
  • 运输 从仓库到商店
****💡** The added value at this stage is to provide detailed visibility of the performance by process. Your role is to support operational teams to improve their performance by implementing a continuous improvement culture backed by data.**

****http://samirsaci.com ****

四。结论

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

既然我们对流程的每一步都了如指掌,那么我们可以做些什么来提高整体绩效呢?

1.根本原因分析

为了提高整体性能,您需要找出延迟交付的根本原因。

流程中会出什么问题?

IT 基础设施&软件 It 始于系统,您可能会因容量问题或系统故障而面临一些延迟。如果 WMS 没有收到订单,您的仓库团队将无法继续准备和装运。

IT 基础设施—(图片由作者提供)

****💡** The human factor can also impact the performance with: data entry errors, delays on some key tasks or just misalignements between the teams.**

仓库操作 在仓库中,提前期会受到

  • 缺货:产品缺失导致延期交货和取消
  • 能力 :资源短缺吸收的工作量(人、设备)
  • 运输来源 :您无法找到卡车来提取打包的订单

仓库问题—(图片由作者提供)

运输 卡车离开仓库后,提前期会受到以下因素的影响

  • 多式联运中转过程中的路况或延误
  • 由于可用性限制而推迟交货:例如,接收货物的商店员工短缺,商店关闭,…

运输根本原因—(图片由作者提供)

这些都是一般的例子,必须适应你的具体情况。

2.后续步骤

达到这一步已经具有挑战性。事实上,找到所有的根本原因需要大量的协调、分析和系统映射工作。

然后,物流团队需要制定缓解计划以确保这些问题不会在未来发生。

因此,您可以围绕这一报告工具构建一个绩效管理循环流程,包括:

  • 每周或每月的数据分析
  • 持续改进计划的实施和跟进

数据分析工具不会提供解决方案,但它们可以帮助运营团队找到流程中的瓶颈和故障。

3。设计供应链数字解决方案

数字孪生是物理对象或系统的数字复制品。

供应链数字模型是一种计算机模型,代表供应链中涉及的各种组件和流程,如仓库、运输网络和生产设施。

使用 Python 的数字孪生示例—(图片由作者提供)

在您找到延迟和事件的根本原因后,持续改进团队可能会设计和实施解决方案或缓解计划。

数字孪生可以用来模拟这些解决方案对整体性能的影响

  1. 构建一个 M0 模型,用实际的运营问题复制当前的运营
  2. 在您的模型中模拟这些缓解计划(增加仓库容量、减少运输提前期等)
  3. 估计对延迟交货百分比的影响

欲知详情,

****https://www.samirsaci.com/what-is-a-supply-chain-digital-twin/

关于我

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

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

https://samirsaci.com ****

逻辑回归和特征尺度集成

原文:https://towardsdatascience.com/logistic-regression-and-the-feature-scaling-ensemble-e78a56fc6c1

线性分类的一个新的优化凹槽

乔希·索伦森在 Unsplash 上的照片

首席研究员:戴夫·古根海姆/撰稿人:乌特萨维·瓦赫哈尼

向下跳动

这项工作是我们对特征缩放的早期研究的延续(见这里:特征缩放的秘密最终被解开|由 Dave Guggenheim |走向数据科学)。在这个项目中,我们将使用岭正则化逻辑回归在 60 个数据集上检验 15 种不同缩放方法的效果。

我们将展示正则化的强大功能,许多数据集的准确性不受要素比例选择的影响。但是,与原始工作一样,特征缩放集合提供了显著的改进,在这种情况下,尤其是对于多类目标。

型号定义

我们选择 L2(岭或吉洪诺夫-米勒)正则化逻辑回归,以满足规模数据的要求。它增加了一个惩罚,该惩罚是系数的平方值之和。这在处理多重共线性时特别有用,并在惩罚模型中不太重要的变量时考虑变量的重要性。

本研究中的所有模型都是使用 sci-kit 学习库中的 LogisticRegressionCV 算法构建的。所有模型也用分层抽样进行了 10 倍交叉验证。每个二元分类模型使用以下超参数运行:

1)惩罚= 'l2 '

  1. cs = 100

3)求解器= 'liblinear '

  1. class_weight = 'balanced '

  2. cv = 10

  3. max_iter = 5000

7)得分=“准确性”

8)随机状态= 1

多类分类模型(在结果表中用星号表示)以这种方式进行调整:

1)惩罚= 'l2 '

  1. cs = 100

3)求解器= 'lbfgs '

  1. class_weight = 'balanced '

  2. cv = 10

  3. max_iter = 20000

7)得分=“准确性”

8)随机状态= 1

9)多类= '多项'

这里的 L2 惩罚因子解决了使用训练数据和测试数据时预测模型的低效问题。由于具有较高数量预测值的模型面临过度拟合问题,使用 L2 正则化的岭回归可以利用平方系数惩罚来防止它。

所有其他超参数保留各自的默认值。所有模型都是使用这些缩放算法(sci-kit 学习包在括号中命名)用特征缩放数据构建的:

a.标准化(标准缩放器)

b.L2 归一化(归一化器;norm='l2 ')

c.鲁棒(RobustScalerquantile_range=(25.0,75.0),with_centering=True,with_scaling=True)

d.规范化(MinMaxScalerfeature_range =多个值(见下文))

d1。特征范围= (-1,1)

d2。特征范围= (0,1)

d3。特征范围= (0,2)

d4。特征范围= (0,3)

d5。feature_range = (0,4)

d6。feature_range = (0,5)

d7。特征范围= (0,6)

d8。feature_range = (0,7)

d9。feature_range = (0,8)

d10。feature_range = (0,9)

e.具有 StackingClassifier 的集成:StandardScaler + Norm(0,9)[有关更多信息,请参见特征缩放集成]

f.具有堆叠分类器的集成:标准缩放器+鲁棒缩放器[参见特征缩放集成了解更多信息]

除了系综之外,使用“fit_transform”将缩放方法应用于训练预测器,然后使用“transform”将缩放方法应用于测试预测器,如许多来源所指定的(例如,Géron,2019,第。66;穆勒和圭多,2016 年,第。139;Shmueli,Bruce 等人,2019 年,第。33;是否应该对机器学习的训练数据和测试数据都进行缩放?— Quora )并由 scikit learn 为所有特征缩放算法提供。然而,由于它们的设计,集成被迫对原始测试数据进行预测,因为顺序链接缩放算法仅导致最终阶段作为结果出现,并且这甚至没有解决通过建模将两个并行缩放路径组合成一个的复制条件。

在每个预测值的样本少于 12 个的情况下,我们将测试分区限制为不少于 10%的总体(Shmueli,Bruce 等人,2019,第。29).如果有足够的样本达到合理的预测精度(由样本复杂性泛化误差确定),我们使用统一的 50%测试分区大小。在这两个界限之间,我们调整了测试规模,以限制泛化测试误差,与训练样本规模进行权衡(Abu-Mostafa,Magdon-Ismail,& Lin,2012,第。57).对于多类数据,如果目标变量中每个分类级别的样本少于 12 个,则在建模前会删除这些级别。

任何缺失值都使用 MissForest 算法进行估算,因为该算法在多重共线性、异常值和噪声面前具有很强的鲁棒性。分类预测因子是使用 pandas get_dummies 函数和丢弃的子类型(drop_first=True)进行一次性编码的。低信息变量(如身份证号码等。)在训练/测试分区之前被丢弃。

构建这些模型的目的是比较特征缩放算法,而不是调整模型以获得最佳结果。出于这个原因,我们在模型中引入了尽可能多的默认值,为上述比较创造了一个平台。所有的性能指标都是根据测试数据的预测的总体准确度来计算的,并且该指标通过两个阈值来检验:1)作为概化测量的最佳性能的 3%以内,以及 2)作为预测准确度测量的最佳性能的 0.5%以内。这就是我们使用许多数据集的原因——因为方差及其固有的随机性是我们研究的一切的一部分。

数据集

本分析中使用的 60 个数据集如表 1 所示,具有广泛的预测器类型和类(二元和多元类)。大多数数据集可以在 UCI 索引(UCI 机器学习库:数据集)中找到。不在 UCI 索引中的数据集都是开源的,可以在 Kaggle:

波士顿住房:波士顿住房| KaggleHR 员工流失:员工流失| KaggleLending Club:Lending Club | ka ggle;电信客户流失:电信客户流失| Kaggle 丰田卡罗拉:丰田卡罗拉|卡格尔

38 个数据集是二项式的,22 个是多项式分类模型。所有模型都是根据所有数据集创建和检查的。表中列出的预测数是未编码的(分类的)和所有原始变量,包括排除前的非信息变量。

表 1 数据集(图片由作者提供)

特征缩放失配探索

我们之前的研究表明,对于预测模型,特征缩放算法的正确选择包括找到与学习模型的不匹配,以防止过度匹配。下面是定义对数损失成本函数的等式,其中添加了 L2 惩罚因子:

图 1 对数损失成本函数(图片由作者提供)

与基于距离的测量方法不同,对于基于距离的测量方法,归一化是合适的(通过保持相对间距),而标准化是不合适的,正则化测井损失成本函数不容易确定。不管嵌入的 logit 函数和它在不匹配方面可能指示什么,增加的惩罚因子应该最小化关于模型性能的任何差异。

为了测试这种偏差控制条件,我们构建了相同的归一化模型,从 feature_range = (0,1)到 feature_range = (0,9)依次循环。每个阶段的训练和测试集精度被捕获并标绘,训练用蓝色,测试用橙色。

每个比较图的左侧面板显示了使用非正则化支持向量分类器将特征范围从零增加一个单位到九的效果。右图显示了相同的数据和模型选择参数,但使用了 L2 正则化逻辑回归模型。

当我们在不改变数据或模型的任何其他方面的情况下增加特征范围时,较低的偏差是非正则化学习模型的结果,而对正则化版本几乎没有影响。请注意,y 轴并不相同,应单独参考。请参考图 2–7,了解这种现象的示例。

图 2 澳洲信用二元模型对比(图片由作者提供)

图 3 汽车多类车型对比(图片由作者提供)

图 4 信贷审批二元模型对比(图片由作者提供)

图 5 德国信用二元模型对比(图片由作者提供)

图 6 字母识别多类模型对比(图片由作者提供)

图 7 人力资源流失(员工流失)二元模型对比(图片由作者提供)

诚然,这两种不同的学习模型可能不会以相同的方式对归一化范围的扩展做出响应,但是不管怎样,正则化模型确实展示了一种偏差控制机制。

特征缩放集合

基于 13 个单一特征缩放模型生成的结果,这两个集合被构建为满足概括和预测性能结果(见图 8)。使用 scikit learn 中的 make_pipeline 函数构建缩放路径,用于创建三个估计量:1)标准化+L2 逻辑回归,2)范数(0,9)+L2 逻辑回归,以及 3)稳健缩放+L2 逻辑回归。投票分类器作为最后一级进行了测试,但由于性能不佳而被拒绝,因此对两个集成使用堆叠分类器作为最终估计器。除了对每个管道进行 10 重交叉验证之外,还对堆叠分类器进行了 10 重交叉验证。所有其他超参数被设置为其先前指定的值或默认值。

图 8 特征缩放系综构造

初步结果

有关 15 个特征缩放算法(13 个单独算法和 2 个集合算法)的综合性能的详细信息,请参考图 9。

图 9 概括表现(图片由作者提供)

正如所料,单个特征缩放算法在广义性能方面几乎没有差异。在图 9 中,可以看到通过正则化实现的等式,因此,除了 L2 归一化,性能最低的 solo 算法(Norm(0,9) = 41)和性能最佳的 solo 算法(Norm(0,4) = 45)之间只有四个数据集的差异。STACK_ROB 特征缩放集成将最佳计数提高了另外 8 个数据集,达到 53 个,代表集成推广的 60 个数据集的 88%。

预测性能的情况下,单独特征缩放算法之间存在较大差异。在图 10 中,可以看到跨数据集的更大范围的计数。

图 10 预测性能(图片由作者提供)

排除 L2 归一化,最低性能的 solo 算法和最佳 solo 算法之间的最大差异是 11 个数据集((StandardScaler = 21)和(Norm(0,5))= 32),而不是由泛化度量表示的 4 个。STACK_ROB 特征缩放集成将最佳计数提高了另外 12 个数据集,达到 44 个,或者在所有 60 个数据集上比最佳 solo 算法提高了 20%。

这种不寻常的现象,即预测性能的提高,无法通过检查特征缩放组合的整体性能图来解释(见图 11)。相反,为了更好地理解,我们需要深入研究原始的比较数据。

图 11 特征缩放综合整体性能(图片由作者提供)

您所看到的是正确的——在这项研究中,特征缩放集成为超过一半的数据集提供了新的最佳准确性指标!

全部结果和新发现

表 2 以多种方式进行颜色编码。首先,左边的列表示 60 个数据集,多类目标用黄色突出显示并在数据集名称后有一个星号来标识。接下来,彩色编码的单元格表示与最佳独奏方法的百分比差异,该方法是 100%点。为了清楚起见,颜色编码的单元格并不显示绝对差异,而是显示百分比差异。单元格中的绿色表示相对于最佳独奏方法实现了最佳情况下的性能,或者在最佳独奏精度的 0.5%以内。单元格中的黄色表示泛化性能,或在最佳独奏精度的 3%以内。如果数据集一直显示为绿色或黄色,则表明正则化的有效性,因为性能差异极小。单元格中的红色显示超出 3%阈值的性能,单元格中的数字显示它与最佳独奏方法的目标性能的差距,以百分比表示。最后,蓝色,超级性能者,以百分比显示高于和超过最佳 solo 算法的性能。

表 2 完整对比结果(图片由作者提供)

在回顾对比数据时,我们注意到一些有趣的事情——在多类目标变量上的正微分预测性能。

二进制对多类性能

在 38 个二进制分类数据集中,STACK_ROB 特征缩放集成对 33 个数据集的泛化性能和 26 个数据集的预测性能进行了评分(见表 3)。对于二元目标,这些结果代表 87%的概括和 68%的预测性能,或者这两个度量之间的 19 点差异。

表 3 二进制分类对比结果(图片由作者提供)

在 22 个多类数据集中,特征缩放集成对 20 个数据集的泛化性能进行了评分,仅比大多数 solo 算法多一个(见图 12)。然而,这些相同的特征缩放集成对 18 个数据集的预测性能进行了评分(见图 13),从而缩小了泛化和预测之间的差距。换句话说,特征缩放集成在 22 个多类数据集上实现了 91%的泛化和 82%的预测准确性,这是一个 9 点的差异,而不是二进制目标变量的 19 点差异。

在这 18 个处于最佳性能的数据集当中,有 15 个提供了新的最佳准确性指标(超级性能)。多类比较分析见表 4。

图 13 多类的泛化性能(按作者分类的图像)

图 14 多类预测性能(按作者分类的图像)

表 4 多类比较结果(图片由作者提供)

STACK _ ROB 特征缩放集合

图 15 显示了通过在所有 60 个数据集上结合标准化和稳健扩展所提供的性能对比检查。小于零的数字表示 STACK_ROB 无法满足以最佳 solo 算法的百分比表示的缩放精度的数据集。数字为零表示达到 100%的最佳独奏准确度,而数字大于零表示表现优异,y 轴表示相对于最佳独奏方法的改进百分比。

图 15 STACK_ROB 性能(图片由作者提供)

结论

我们的工作表明,正则化在最小化特征缩放模式之间的精度差异方面是有效的,因此缩放的选择不像非正则化模型那样重要。尽管正则化有偏差控制效应,但预测性能结果表明,对于逻辑回归,标准化是合适的,而归一化是不合适的。

但是,正如我们在之前的研究中所证实的那样,特性扩展集成,尤其是 STACK_ROB,能够带来显著的性能提升。在这种情况下,多类预测准确性有了决定性的提高,预测性能缩小了与通用指标的差距。如果您尚未考虑使用逻辑回归来解决多项式问题,STACK_ROB 要素缩放集成可能会改变您的想法。

如果您的 L2 正则化逻辑回归模型不支持处理要素缩放集合所需的时间,那么使用 0 到 4 或 5 个要素范围(Norm(0,4)或 Norm(0,5))的归一化对于概化和预测都具有不错的性能。至少,这是你寻找最优的一个好的起点。

致谢

我想对 Utsav Vachhani 一年多来为解决特征缩放之谜所付出的不懈努力表示最深切的感谢,这导致了特征缩放合集的创建。很简单,没有他的贡献,这篇论文和所有未来的功能缩放集成工作将不会存在。

参考文献

Abu-Mostafa,Y. S .、Magdon-Ismail,m .、和 Lin,H.-T. (2012 年)。从数据中学习(第 4 卷)。美国纽约 AMLBook

Géron,A. (2019)。使用 Scikit-Learn、Keras 和 TensorFlow 进行动手机器学习:构建智能系统的概念、工具和技术。奥莱利媒体。

米勒和圭多(2016 年)。Python 机器学习简介:数据科学家指南。奥赖利媒体公司。

Shmueli,g .,Bruce,P. C .,Gedeck,p .,& Patel,N. R. (2019)。商业分析的数据挖掘:Python 中的概念、技术和应用。约翰·威利的儿子们。

Scikit-learn_developers。(未注明)。[sklearn.linear_model](https://scikit-learn.org/stable/modules/classes.html#module-sklearn.linear_model)。后勤注册文件。从 sklearn.linear_model 中检索。LogisticRegressionCV—sci kit-learn 1 . 0 . 2 文档

数据集来源

联系方式:

戴夫·古根汉姆:参见作者简介和简历,dguggen@gmail.com

乌特萨维·瓦赫哈尼:领英简历uk.vachhani@gmail.com

逻辑回归和缺失先验

原文:https://towardsdatascience.com/logistic-regression-and-the-missing-prior-7c36039af851

如何利用 Jeffreys 先验减少 logistic 回归的偏倚

假设 X 表示回归量矩阵,而 y

表示目标值的向量。

如果我们假设数据是从逻辑回归模型生成的,那么我们在看到数据后对权重 w 的信任由下式给出

这里,P( y | Xw )称为可能性,由下式给出

而 P( w )称为先验,在看到数据之前量化我们对 w 的信念。

通常,逻辑回归适合最大化 P( y | Xw ),而不考虑先验

w _ML 就是我们所说的权重的最大似然估计。

但是无视先验通常是个坏主意。我们来看看为什么。

最大似然模型有什么问题

考虑一个具体的例子。

假设你是一名研究狼的生物学家。作为你工作的一部分,你一直在跟踪狼群,观察它们的捕猎行为。

托马斯·博诺梅蒂在 Unsplash 的照片

你想知道是什么让狩猎成功。为此,您需要建立一个逻辑回归模型来预测搜寻是成功还是失败。考虑像群体大小和猎物类型这样的因素会很有趣,但是为了简单起见,你只从一个偏见术语开始。

n 表示狩猎的总次数。如果 b 表示我们的
偏置项的值,那么观察到 k 成功搜索的可能性由下式给出

现在,让我们假设我们观察了 8 次狩猎,没有记录到成功的狩猎。(狼的捕猎成功率实际上相当低,所以这样的结果并不令人惊讶)。对于 b 来说,什么是好的工作估算?最大似然估计是-∞,但是我们知道这没有意义…如果没有成功的机会,为什么还要去狩猎呢?

如果我们不选择 b 来最大化可能性,而是选择 b 来最大化后验分布的概率,我们可以获得更好的工作估计

具有适当选择的先验 P(b)。我们稍后将讨论选择先验的过程,但现在考虑先验

这个先验的后验概率是

放 p = (1 + exp(-b))⁻。我们可以通过对 0 取对数、求导和求解来找到 MAP 估计

求解 0,我们有

在我们的例子中,这给了我们更合理的估计

我们使用的先验是无信息先验的一个例子。

什么是无信息先验,我们如何发现它们

注意:这里对非信息先验的介绍紧跟 Box & Tiao 的书统计分析中的贝叶斯推断的第 2 章。

假设我们给定一个模型的似然函数 L(θ| y ,该模型有参数θ和数据 y 。当我们没有关于θ的特定知识时,什么是好的使用前?

我们来看一个具体的例子。假设数据由方差σ已知但均值θ未知的正态分布生成。该模型的似然函数为

这是 n=5 和不同 y̅:值的概率图

方差为 1,
n=5,y̅.值不同的正态分布数据均值的似然函数图片作者。图片作者。

请注意,对于所有的 y̅值,似然曲线的形状都是相同的,只有平移不同。当似然曲线具有这种性质时,我们说它是经过转换的数据。

现在考虑两个大小相同的范围[a,b]和[a + t,b + t]。如果观测数据由 y̅+t 而不是 y̅.转换,则两者相对于似然曲线的位置是相等的因此,我们应该

或者制服先验

假设我们对似然函数 L(θ| y )使用另一种参数化:我们通过 u = ϕ(θ)进行参数化,并在 u 之前应用均匀化,其中ϕ是某种单调内射变换。累积分布函数由下式给出

其中 Z 是某个归一化常数。进行替换 θ = ϕ⁻ (u) 以回到原始参数化,然后我们得到

所以我们看到在 u 上的均匀先验等价于在 θ 上的先验 P(θ) ∝ ϕ'(θ) 。找到θ的无信息先验等价于找到一个变换 ϕ 来转换似然函数数据。

现在让我们回到只有偏差项的逻辑回归模型的似然函数

绘制 n=8 和不同的 k 值的可能性给出了

只有一个偏差项的逻辑回归模型的似然函数,
n=8,k 值不同。

从图中可以很清楚地看到,可能性函数没有经过数据转换。对于 k=4 的曲线比其他的更为陡峭,对于 k=1k=6 的曲线是歪斜的。

现在,对于适当表现的似然函数,我们可以使用 log L(θ| y )关于
θ_ML 的二阶泰勒级数展开,通过
a 高斯来近似 L(θ| y

在哪里

随着 n →∞,近似变得越来越精确。

让我们看看这是如何为我们的简化逻辑回归模型工作的。

对 log L(b|k)求微分得到

再次微分给了我们

这是高斯近似的样子

仅含偏倚项的逻辑回归模型的似然函数
及其高斯近似(n=8,k=1)。图片作者。

现在,考虑当我们用θ = ϕ⁻ (u)重新参数化时,L(θ, y )的高斯近似会发生什么。

差异化给了我们

再次微分给了我们

以最大值 u_ML= ϕ(θ_ML 进行评估),我们有

我们申请的地方

回到逻辑回归,把

那么我们有

因此,关于 u_ML 的高斯近似变成

因此似然函数现在是近似的数据转换。

只有一个偏差项的逻辑回归模型的重新参数化似然函数,
n=8,k 值不同。

杰弗里斯先验

现在让我们对多参数情况下的 Jeffreys 先验作一个简要的概述。
θ = (θ₁,…,θ_k)^T 表示参数的向量。

类似于单参数情况,似然函数函数可以由关于最优值的高斯函数来近似

在哪里

现在,假设我们用 u = ϕ( θ 重新参数化。那么高斯近似的更新的 hessian 变成

在哪里

在一般的多变量情况下,我们可能无法以高斯近似变成数据转换的方式重新参数化,但是假设我们选择ϕ,使得

然后

有一个常数行列式,意味着区域的体积将保持不变。

设 S ∈ ℝᵏ是关于原点的区域,K 是关于 θ _ML 的重新参数化高斯近似的协方差矩阵
。如果 L 是 K 的乔莱斯基分解

则 s 对应于 L⁻ S+ θ _ML 关于 θ _ML 的区域,该区域具有固定的概率质量和体积

u 上均匀先验的 θ 对应的先验为

而这叫做杰弗里斯在先

让我们将此应用于带权重的完全二项逻辑回归模型。负对数似然函数由下式给出

在哪里

差异化给了我们

在哪里

再次微分给了我们

在哪里

我们可以把黑森写成

其中 A 是对角矩阵

杰弗里斯副院长是

用 Jeffreys 先验拟合逻辑回归

用 Jeffreys 先验找到逻辑回归的映射模型相当于解决优化问题

设π表示杰弗里斯先验的对数

如果我们可以找到π的梯度和 hessian 的方程,那么我们可以应用标准的二阶优化算法来找到 w _MAP。

从梯度开始,我们可以应用雅可比公式对行列式进行微分

得到

其中 A_w 的导数是对角矩阵

对于 hessian,我们应用微分逆矩阵的公式

得到

在哪里

通过以有效的方式评估梯度和 hessian 计算并应用二阶信赖域优化器,我们可以快速迭代以找到 w _MAP。使用这种方法的 Python 包可以在 https://github.com/rnburn/bbai获得。

让我们在一些例子中看看这是如何工作的。

示例 1:单变量模拟数据

我们将从查看模拟数据集的先验和后验开始,其中
只有一个回归变量,没有偏差。

我们从生成数据集开始。

接下来我们添加一个函数来计算先验

并绘制出一系列的值

Jeffreys prior 对具有单个回归量的模拟数据集进行逻辑回归。图片作者。

接下来,我们拟合逻辑回归图模型,并将 w_MAP 与
w_true 进行比较。

印刷品

最后,我们画出后验分布。

使用单个回归变量和 w_MAP 对模拟数据集进行逻辑回归的后验概率。图片作者。

完整的例子可以在 github.com/rnburn/bbai/example/05-jeffreys1.ipynb 找到。

示例 2:具有两个变量的模拟数据

接下来让我们看一个有两个变量的例子。

我们生成一个包含两个变量的数据集

我们计算先验,并在一系列值上绘制出来

Jeffreys 先验用于具有两个回归量的模拟数据集上的逻辑回归。图片作者。

现在,我们将拟合一个模型来查找 w _MAP。

印刷品

最后,我们绘制出以 w _MAP 为中心的后验分布。

具有 Jeffreys 先验的逻辑回归的后验概率。图片作者。

完整的例子可以在 github.com/rnburn/bbai/example/06-jeffreys2.ipynb 找到。

示例 3:乳腺癌数据集

现在,我们将根据现实世界的乳腺癌数据集拟合一个模型。

我们从加载和预处理数据集开始。

然后,我们拟合一个逻辑回归图模型,并使用费雪信息矩阵来估计标准误差。

我们打印出重量和它们的标准误差。

完整的例子可以在 github.com/rnburn/bbai/example/07-jeffreys-breast-cancer.py 找到。

结论和进一步阅读

与普通最小二乘法不同,逻辑回归的似然函数不需要数据转换。搜索数据转换转换会将我们带到 Jeffreys 先验,一种自然收缩先验。

Firth 1993 [1]和 Kosmidis & Firth 2021 [2]分析了 Jeffreys 先验惩罚最大化似然估计量的统计性质,发现它比标准的最大似然估计量具有更小的渐近偏差阶。Jeffreys prior 降低偏差的特性结合其处理分离问题的能力[3],使其成为拟合逻辑回归模型的标准方法的绝佳替代品。

博文原载于【https://buildingblock.ai/logistic-regression-jeffreys】

参考

[1]弗思,D. (1993 年)。最大似然估计值的偏差减少Biometrika 80,27–38。

[2]科斯米迪斯本人和弗斯博士(2021 年)。Jeffreys-二项响应广义线性模型中的先验惩罚、有限性和收缩Biometrika ,第 108 卷,第 1 期,2021 年 3 月,第 71–82 页。

[3]:海因策,g .和谢姆佩尔,M. (2002 年)。逻辑回归中分离问题的一种解决方法。统计学家。医学。212409–19。

在 7 分钟内解释逻辑回归

原文:https://towardsdatascience.com/logistic-regression-explained-in-7-minutes-f648bf44d53e

完全初学者的逻辑回归详细指南

Unsplash 上由 Austin Distel 拍摄的照片

逻辑回归是数据科学家最常用的预测模型之一。在本文中,我将解释逻辑回归背后的一些理论,以便您对它的工作原理有一个直观的理解。

然后,我将深入研究用于评估逻辑回归模型的指标,以及何时应该使用它们。

最后,我将向您展示如何使用 Scikit-Learn 在 Python 中构建逻辑回归模型。

什么是逻辑回归?

简而言之,逻辑回归是一种预测事件发生的概率的模型。它用于解决分类问题,这意味着因变量始终是一个类或类别。

以下是一些可以借助逻辑回归解决的分类问题示例:

  • 预测电子邮件是垃圾邮件还是垃圾邮件
  • 根据动物的图像识别其种类
  • 将网站流量分类为良性或恶意

分类问题可以分为两类:二元分类和多类分类。

二元分类涉及预测两个类中的一个(如猫 vs 狗)多类分类指预测多个类中的一个(如猫 vs 狗 vs 熊)。

逻辑回归是如何工作的?

逻辑回归可以用 S 形曲线建模,如下例所示:

作者图片

这个图的 X 轴显示在公司的年数,这是因变量。Y 轴告诉我们一个人获得晋升的概率,这些值的范围从 0 到 1。

概率为 0 表示这个人不会得到提升,概率为 1 表示他们会得到提升。

对于小于 0.5 的概率,逻辑回归返回结果 0(提升=否)。对于大于或等于 0.5 的概率,将返回预测值 1(提升=是):

作者图片

你可以看到,随着员工在公司工作的时间越来越长,他们获得晋升的机会也越来越大。

根据上面解释的概念,您可能会有以下问题:

我们为什么需要逻辑回归——为什么不能简单地用一条直线来预测一个人会不会升职?

线性回归是一种常用于模拟具有连续输出的问题的技术。下面是一个线性回归拟合直线来模拟观察数据的示例:

作者图片

然而,对于分类问题,我们不能将这样的直线拟合到手头的数据点上。

这是因为线性回归中的最佳拟合线没有上限和下限,预测可能会变成负值或超过 1。

下面是一个例子,说明如果我们使用线性回归来模拟一个人是否会得到晋升:

作者图片

注意,在这个例子中,线性回归仍然适用于某些值。

对于有 10 年工作经验的人,我们得到的预测是 1。

对于 3 年的工作经验,模型预测的概率为 0.3,这将返回 0 的结果。

但是,由于没有上限,观察一下对于有 12 年工作经验的员工的预测如何超过 1 。同样,对于工作经验不足 1 年的员工,预测变成了负面

因为我们想要预测二元结果(是/否),所以预测的范围需要从 0 到 1。不可能有负面预测或超过 1 的预测。

为了确保结果始终是一个概率,逻辑回归使用 sigmoid 函数将线性回归的输出压缩在 0 和 1 之间:

作者图片

这是线性回归方程,也是直线方程:

作者图片

这是逻辑回归方程,它简单地将线性回归的输出压缩在 0 和 1 之间:

作者图片

既然您已经了解了逻辑回归是如何工作的,那么让我们来看看用于评估这种模型的不同度量标准。

如何评价 Logistic 回归模型?

用于评估分类模型性能的最流行的度量是准确性。然而,准确性并不总是一个好模型最可靠的指标,这就是为什么数据科学家经常使用精确度、召回率和 F1 分数等指标。

这里有一个示例可以帮助您理解不同的分类指标以及何时使用它们:

作者图片

上面的数据集包括学生是否通过考试的信息。值 1 表示他们通过了,值 0 表示他们没有通过。

“实际”列包含真实数据,而“预测”列包含逻辑回归模型的预测。

真实的数据是不平衡,因为 80%的学生通过,20%的学生不及格。这意味着数据集偏向于表示一个类而不是另一个类。

现在,看一下“预测”栏。请注意,模型预测 100%的学生都通过了考试。这个模型只是预测多数阶级,所以它是一个很差的模型。

现在,让我们计算该数据集的一些分类指标:

准确(性)

使用以下公式计算精度:

作者图片

这个模型有 80%的准确率。然而,正如我们前面提到的,该模型只预测了一个类别,甚至没有做出一个准确的正面预测。

虽然它的准确性很高,但该模型表现不佳。在这种情况下,准确性可能是一个误导性的指标,因为它会让数据科学家认为他们的模型是好的,即使事实并非如此。

现在,让我们看看其他几个可以帮助我们克服准确性缺点的指标。

精确度和召回率

精确度和召回率是用于评估模型做出的正面预测的质量的两个指标。

现在,让我们进一步了解精确度和召回率,以及如何计算它们:

1。精度

精确度是一种可以告诉我们的度量:在所有积极的预测中,有多少学生真正通过了?

它使用以下公式计算:

作者图片

在这种情况下,分类器的精度是 0/0 = 0

2。召回

一个模型的回忆可以告诉我们:在所有通过的人中,有多少是被正确识别的?

计算召回率的公式是:

作者图片

在这种情况下,分类器的召回是 0/2 = 0

f1-分数

F1 分数结合了精确度和召回值,给出了一个单一的结果。

简而言之,这一指标通过计算精确度和召回率的平均值来告诉我们一个模型的表现有多好。

计算 F1 分数的公式为:

作者图片

在这种情况下,分类器的 F1 值为 2 X 0= 0

摘要

总之,该模型的准确性很高,但其精确度、召回率和 F1 值都为 0,因为它没有做出任何正面预测。

本节的主要内容是,虽然准确性通常用于评估逻辑回归模型的性能,但计算其他度量也很重要,以确保您的模型确实表现良好。

我没有深入研究这些指标,因为本文的重点是逻辑回归。如果你想更深入地了解精确和回忆是如何工作的,以及如何回答关于它们的面试问题,请阅读我关于这个主题的教程。

如何在 Python 中进行逻辑回归?

您可以使用 Scikit-Learn 运行以下代码行来构建逻辑回归模型:

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression() 
lr.fit(X_train,y_train) # train the model
preds = lr.predict(X_test) # make predictions

感谢阅读!本帖原载此处

从零开始的逻辑回归

原文:https://towardsdatascience.com/logistic-regression-from-scratch-870f0163bfc9

逻辑模型是机器学习和许多社会科学领域的基础。在这篇文章中,我解释了如何从基本原理中推导出逻辑模型。因为我喜欢边做边学,所以我展示了如何使用梯度下降牛顿-拉夫森算法来估计它的参数。在现实生活的应用方面,我们将使用关于 NBA 球员的数据来看看是什么因素影响了投篮的成功。

TL;博士:从基本原则推导+如何训练+ NBA 应用

这篇文章的 GitHub 库可以在这里找到。如果你喜欢这篇文章,请不要犹豫,关注我。

照片由 塞尔吉奥·索萨 拍摄自 佩克斯

一.理论

逻辑模型

结果变量 yi 要么是 1(“赢”),要么是 0(“输”)。逻辑模型假设获胜的概率由逻辑函数给出(因此得名):

随着

输的概率是 1 减去赢的概率:

潜在变量公式

解释逻辑模型的一个强有力的方法是将其视为潜在变量模型的结果。不可观察的潜在变量 zi 线性依赖于 xi 加上噪声项εi:

我们只观察 yi,当 zi 严格正时等于 1,否则等于 0。如果误差项按照逻辑分布分布,我们最终得到上述逻辑模型。如果误差项呈正态分布,则该模型为概率单位模型。要了解这一点,只需将潜在变量的概率表示为大于 0:

其中最后一行来自使用对数分布的 cdf 表达式,平均值为零,标度参数等于 1。

系数怎么读?

我们如何从逻辑模型中读取系数?xij(Xi 的第 j 个分量)的变化对 yi=1 的概率的边际影响由下式给出:

第一个观察是边际效应取决于 xi,不像线性回归模型。第二个观察结果是,前两项总是正的,所以我们有这样的解释,如果θj 是正的,xi 的第 j 个分量的增加导致获得成功的更大概率(保持其他一切不变)。

从逻辑模型中读取结果的另一种方式是认识到它意味着奇数比的对数是线性的:

实际上,如果系数θj 等于 0.1,这意味着 xij 增加一个单位,则“获胜”(输出为 1)的相对概率增加约 10%。

二。寻找系数

对数似然函数

到目前为止,一切顺利。但是我们如何找到θ呢?一个“自然”的标准是找到θ的值,使得最大化观察样本的概率。在商业中,这个过程被称为最大似然估计。让我们假设样本是I . I . d .。如果 i.i.d .假设成立,观察到样本的概率(易,)是每次观察到的概率的乘积。最大化对数似然比最大化似然更方便,对数似然将概率的乘积转换为总和:

观察到 yi(yi = 1 或 0)的概率可以简洁地写成

因此,对数似然函数写道:

最大似然估计

对 f(yi|xi,θ)对参数θ求导得到

对数似然函数相对于θ 的导数为

梯度下降

既然知道了对数似然函数关于θ的导数,我们可以使用古老的梯度下降算法:

梯度下降算法是一种寻找函数极小值的迭代程序。在每一步,该算法朝着最陡下降方向前进一步,步长为γ。

牛顿-拉夫森方法

我们能做得比梯度下降更好吗?用这个简单的模型,是的。使用牛顿-拉夫森方法可以大幅提高速度。粗略地说,牛顿-拉夫逊法是一种“智能”梯度下降法,它利用了对数似然 HL((易,)的 Hessian 中包含的信息;θi)向右移向极小值。该迭代算法如下进行:

下一张图显示了牛顿-拉夫森方法如何解决一维求根问题:

来源:https://en.wikipedia.org/wiki/Newton's_method

应该用梯度下降法还是牛顿-拉夫逊法?当计算 Hessian 简单快速时,你应该选择 Newton-Raphson,因为它收敛得更快。见下面引用自维基百科关于牛顿-拉夫森的文章

在适用的地方 ,牛顿法 比梯度下降法 向局部最大值或最小值收敛快得多。事实上,每个局部最小值都有一个邻域 N,这样,如果我们从 x0 ∈ N 开始,步长γ = 1 的牛顿法二次收敛 (如果 Hessian 是可逆的,并且 x 的一个 Lipschitz 连续函数在那个邻域中)。

对于逻辑斯谛模型,牛顿-拉夫森算法很容易应用,因为存在一个闭合形式的 Hessian 公式:

三。应用:NBA 球员

照片由 Chbani Med 发自 Pexels

是什么让一个 NBA 球员成功?为了回答这个问题,我使用了 2014-2015 赛季期间拍摄的 NBA 照片的数据集。例如,它包含以下信息:

  • 谁开的枪
  • 这一枪是从地板上的什么地方拍的
  • 谁是最近的防守队员,
  • 最近的防守队员有多远
  • 拍摄时间

此处的Kaggle上有数据。在接下来的内容中,我使用了 Julia 并附上了代码,这样您就可以理解了。需要几个包:

using Distributions
using Plots
pyplot()
using DataFrames
using GLM
using Optim
using CSV
using GLM

数据集非常广泛。让我们选择射门是否成功,射门时间,射门距离,以及与最近的防守队员的距离:

使用包装

获得结果的最快方法是使用包 GLM:

fittedmodel = glm(@formula(SHOT_RESULT ~ SHOT_CLOCK + SHOT_DIST + CLOSE_DEF_DIST), df_nba, Binomial(), LogitLink(), verbose=true)SHOT_RESULT ~ 1 + SHOT_CLOCK + SHOT_DIST + CLOSE_DEF_DIST 

GLM 的估计结果

我们如何解释这些结果?

  • 时间压力让 NBA 球员更成功:投篮时间越高,得分的可能性越大
  • 距离较远的镜头更容易被错过
  • 离最近的防守队员越远,射门成功的可能性越大

编写自己的代码

能不能“手动”找到类似的结果?答案是是的。为了看到这一点,让我们首先创建二元变量y并将解释变量放入X.中,然后我定义几个函数来计算损失函数的梯度和 hessian:

这个逻辑模型的实现给我们的参数值几乎与我们使用GLM.软件包得到的参数值相同。

Estimate for theta is [-0.0575129, 0.0185199, -0.0597451, 0.108392]

结论

这篇博文展示了如何从基本原则(潜在变量解释)中推导出逻辑模型,以及如何用几行代码实现它。

逻辑回归模型比较

原文:https://towardsdatascience.com/logistic-regression-models-comparison-2a88a9da4eb6

R 系列中的统计

诺德伍德主题公司在 Unsplash 上拍摄的照片

简介

在简单逻辑回归中,我们只有一个预测变量,而在多元逻辑回归中,有多个预测变量。响应变量可以是二进制的,也可以是有序的。例如,响应变量可以只是两个类别之间的选择,如城市或农村,健康或疾病,就业或失业,受过教育或不识字。响应变量也可以是有序的,其中响应变量中可以有从低到高的特定级别,反之亦然。例如,工资水平可分为低工资、低于平均工资、平均工资、高于平均工资和良好工资。这是五个分类但有序的级别,响应变量可以是任何类别。

我们已经在以前的文章中讨论了这些二元和有序逻辑回归,链接如下

R 中二分变量的简单逻辑回归

R 中二分变量的多元 Logistic 回归

R 中序数变量的简单逻辑回归

R 中序数变量和预测概率的多元逻辑回归

在本文中,我将向您介绍不同模型之间的比较,以及如何解读 R 输出。

数据集

本案例研究将基于来自 UCI 机器学习知识库的数据集,该数据集被称为成人数据集,将被用作数据源。据估计,在该数据集中,超过 30000 个人通过他们的人口统计信息被识别,这些信息包括但不限于他们的种族、教育、职业、性别、工资、每周工作时间、就业水平以及收入水平。

来自 UCI 机器学习知识库的成人数据集

为了进行有序逻辑回归分析,有必要对给定数据进行一些修改。

这里,我将有两个数据集:一个用于二进制模型,其中响应变量是二进制的,另一个用于序数模型,其中响应变量是有序的。原始数据被修改以执行简单有序的逻辑回归,这些数据保存在下面的 github 中。

链接 excel 文件简单逻辑回归: GSS — glm2.xlsx

链接 excel 文件多元 logistic 回归:成人-v3.xlsx

母亲和父亲的学士教育教育在第一个文件中是二进制的,表示为 MADEG 和 PADEG,输出也是二进制的,表示为 DEGREE1,表示每个人的学士教育。

在第二个文件中,教育水平被转换成如下排序。

数据集中的教育水平

性别和种族的其他变量在这里是二元的。

R 中的实现

下面是我用来比较不同型号的 github 要点。为此,我使用了 anova 函数。首先加载必要的库。

模型对比和结果解释

在模型 1 中,研究问题是:

母亲的教育水平会影响孩子的教育水平吗?

在模型 2 中,我加入了父亲的教育,问题变成了:

父亲的受教育程度与孩子的受教育程度有关系还是不相关?

模型 1 和模型 2 比较

主要观察结果如下:

  • 模型 1 的剩余偏差为 430.88,而模型 2 的值为 395.40,较小。偏差差为 35.48,这表明模型 2 比模型 1 更稳健。与模型 1 相比,模型 2 为我们带来了更多关于每个人学士学位的信息。该残差偏差是该特定模型相对于饱和模型的偏差,在饱和模型中,每次观测都会带来一个额外的预测参数,因此该模型对于该数据集来说是完美的。我们可以看到模型 2 并不完美。
  • liklihood 比率卡方检验在这里很重要。因此,我们拒绝零假设,可以得出结论,双预测变量模型更好地拟合数据。

在随后的模型中,我使用教育、性别和种族数据作为预测因素,收入水平作为响应变量,开发了有序逻辑回归。

模型 3 包括教育数据作为预测因子。

模型 4 包括教育和性别数据作为预测因素。

模型 5 包括教育、性别和种族数据作为预测因素。

模型 3、模型 4 和模型 5 比较

因为这是有序回归,所以输出窗口是不同的。有时,空模型被用来比较完整的或嵌套的模型。如果我们将零模型视为模型 0,我们可以将其定义如下:

模型 0

Key observations are below:

  • The AIC statistics can be compared among all the models. Model5 has the smaller AIC value indicating more robustness for that model. Smaller the AIC value, better fit the model.
  • The log-likelihood values for each model are shown in the next column. The likelihood ratio chi-square test statistics for moel4 is 1492.8 which is twice the difference between the log-likelihood values of these two models. Also p<0.001 which implies that model5 with two predictor variable fits better than model3 with one predictor variable. Therefore, when we incorporate gender into the model along with education, it has better predictability of each individual’s income level. Similarly when model4 and model5 are compared, the chi-square test statistics becomes 96.5 and p <0.001. Therefore model5, which includes race data, has better capability to describe income than model4.

结论

二元逻辑回归模型之间的比较以及有序逻辑回归模型之间的比较已经在本文中通过 r 中的代码实现进行了演示。可能有几个模型被开发来解决相同的问题,但是这些模型之间的比较为我们提供了检查模型稳健性的选项。我们可以检查包含一个预测器实际上是否有助于稳健性。也可以获得贡献的程度。这有助于确定最终模型的部署。

数据集确认

杜瓦博士和格拉夫博士(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院 (CC BY 4.0)

感谢阅读。

https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe

逻辑回归:拟合优度的统计

原文:https://towardsdatascience.com/logistic-regression-statistics-for-goodness-of-fit-e2e8c7aab95

R 系列中的统计学:偏差、对数似然比、伪 R 和 AIC/BIC

克里斯·利维拉尼在 Unsplash 上的照片

简介

在简单的逻辑回归中,我们试图将响应变量的成功概率与预测变量进行拟合。该预测变量可以是分类变量,也可以是连续变量。我们需要量化这个模型有多好。有几种拟合优度测量值可以表示拟合优度。

离散随机变量通常只能取两个值:1 代表成功,0 代表失败。这类随机变量的分布一般定义为伯努利分布。伯努利分布的概率函数或概率数学函数可以表示为。

这里,y 可以是 1 或 0。

使用这个函数,我们可以计算每个观察值的概率。有时离散随机变量可以取两个以上的值。使用同样的分布函数,我们可以计算随机变量在每一层的概率。例如,离散随机变量可以具有除 0 和 1 之外的值。联合概率函数可以通过将所有观测值在每个离散水平上的概率值相乘来获得。

联合概率的上述定义留给我们一个新的实体,称为似然函数。公式与联合概率相同,但对似然函数的解释略有不同。在概率函数的情况下,参数是已知的,我们感兴趣的是确定已知参数在不同水平上的概率。另一方面,当我们考虑似然函数时,我们需要估计未知参数。

例题

让我们假设一个事件发生的概率或成功的概率是 0.7,不发生同样事件的概率所有失败的概率是 0.3。这导致了下面的伯努利分布。

有了这个公式,我们想确定事件发生的概率。我们把这些值代入上面的等式。

对于 y = 1,我们得到 p =(0.7)(1–0.7)⁰= 0.7

答对了。我们利用仅涉及一次试验的伯努利分布获得了成功的概率。

让我们转到可能性的概念。让我们假设 10 次试验的结果如下。

[1,1,1,0,1,1,1,0,1,0]

似然函数变成:

似然函数通常被转换成对数标度。一旦完成,似然函数变成:

这里 p 是未知的,我们需要估计。如果我们想测量 p 的最大似然,我们可以将对数似然函数的导数设置为 0。

因此,成功的最大可能性是 7/10,这使得上述试验结果最有可能。

拟合优度统计

这里,我们将讨论以下四个拟合优度的统计数据。

  1. 异常
  2. 对数似然比
  3. 伪 R
  4. AIC 和 BIC 统计

让我们过一遍细节。

越轨行为

使用偏差,我们可以比较当前模型和饱和模型。饱和模型是能够为数据提供完美拟合的模型。异常被定义为

偏差= -2*(当前模型的对数似然性-饱和模型的对数似然性)

在饱和模型中,参数的数量等于样本大小,因为它包含每个观察值的一个参数。饱和模型的可能性为 1,这意味着饱和模型可以根据预测变量提供完美的预测。因此,饱和模型的对数似然性为 0,因此偏差变为-2*(当前模型的对数似然性)。

在线性回归中,最佳模型试图减少所有数据点的误差。这里,在逻辑回归中,减少误差方差的可比统计量是偏差。换句话说,在逻辑回归中,最佳模型试图减少偏差(即研究中的模型和饱和模型之间的对数似然性)。

对数似然比

理想情况下,偏差是当前模型和饱和模型之间的比较,但我们也可以将当前模型与具有更多参数的模型进行比较。后一个模型可以称为全模型,当前模型可以称为简化模型。计算简化模型和完整模型之间的偏差可以提供一些洞察力,以确定添加更多的参数实际上是否会提高模型的性能。

对数似然比

=简化模型的偏差-完整模型的偏差

= — 2(简化模型的对数似然性—饱和模型的对数似然性)— — 2(完整模型的对数似然性—饱和模型的对数似然性)

= — 2(简化模型的对数似然性—饱和模型的对数似然性)+ 2(完整模型的对数似然性—饱和模型的对数似然性)

= — 2*(简化模型的对数似然性—完整模型的对数似然性)

=-2 * ln(简化模型的可能性/完整模型的可能性)

为了确定对数似然比的显著性,进行卡方检验,如果零假设被拒绝,我们得出结论,完整模型比简化模型更好地拟合数据。这反过来意味着增加的变量确实对模型有贡献。对数似然比测试模型相对于仅截距模型的改进水平。

伪 R

或多或少,我们都熟悉线性回归中的 R 解释,但在逻辑回归中,它的解释是不同的。麦克法登的伪 R 定义为

麦克法登河

= 1 —(拟合模型的偏差/零模型的偏差)

= 1 +(拟合模型的对数似然/零模型的对数似然)

当模型的似然值较小时,对数似然值会变大。如果零模型可能性较小,而拟合模型可能性较大,则上述等式的第二部分变小。在理想情况下,第二部分变成 0,伪 R 值变成 1。伪 R 的值为 1 表示我们可以完美地预测成功或失败的概率。

AIC 和 BIC 统计

似然比检验和伪 R 用于比较嵌套模型。这意味着一个模型的参数比另一个少。在两个模型具有不同参数集的情况下,我们不能使用似然比检验和伪 R 来比较模型。这就是 AIC(阿凯克信息标准)和 BIC(贝叶斯信息标准)统计进入画面的时候。AIC 和 BIC 被定义为:

AIC = -2*(当前模型的对数似然性— k)

BIC = -2(当前模型的对数似然)+ ln(n)k

这里,k =模型中参数的总数,包括截距,n =样本大小

可能性介于 0 和 1 之间,所以它们的对数小于或等于零。如果一个模型更有可能,它的对数似然性在负侧变得更小,并且“-2 *对数似然性”值变得正但值更小。AIC 和 BIC 可以用来比较嵌套和非嵌套模型。具有较低 AIC/BIC 值的模型提供了最佳拟合。

R 中的实现

在下面的文章中,我已经讲述了简单逻辑回归的基础知识。我已经讨论了语法和解释是如何完成的。

这里,我将使用相同的数据集并比较上面描述的统计数据。

数据集

为了进行演示,我将使用 2016 年收集的综合社会调查(GSS)数据。这些数据是从宗教数据档案协会下载的,由汤姆·w·史密斯收集。该数据集收集了近 3,000 名受访者的回答,并包含与若干社会经济特征相关的数据。例如,它有与婚姻状况、教育背景、工作时间、就业状况等相关的数据。让我们深入这个数据集,以便更好地理解它。

学位列提供每个人的教育水平值,MADEG 提供每个母亲的教育程度。我们的目标是找出母亲的本科教育水平是否是孩子本科教育水平的良好预测指标。数据集中的分类数据按顺序编码。

学位数据[图片由作者提供]

MADEG 数据[图片由作者提供]

当前模型和空模型的偏差显示在汇总统计中。当前模型的较小偏差表明,与零截距唯一模型相比,它是更好的拟合。

R 中的输出窗口[图片由作者提供]

为了获得似然比,我们需要推出另一个嵌套模型并比较似然性。

要获得 Pseudo R,我们需要安装“rcompanion”包并使用“nagelkerke”命令。对于 AIC/BIC,命令是简单的 AIC(型号名称)和 BIC(型号名称)。

nagelkerke 的输出提供了三种不同的伪 R,但我们可以使用麦克法登的伪 R,因为许多统计学家最推荐它。与另一个模型相比,具有更大可能性的模型将具有更高的麦克法登 R。

结论

在本文中,我们已经通过逻辑回归拟合优度的基本统计。我们已经讨论了偏差,对数似然比,伪 R 和 AIC/BIC 统计,并在 R 中实现。为了更好地理解,读者应该找到任何统计教科书或参考书更翔实。

感谢阅读。

https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe

数据中的长和宽格式,已解释

原文:https://towardsdatascience.com/long-and-wide-formats-in-data-explained-e48d7c9a06cb

来自佩克斯劳拉拍摄的爆炸

如何以熊猫的方式对待他们

表格是一种二维的、非常常用的存储和准备数据的形式,这可以通过两种方式实现:宽格式和长格式。每一个都有一些优点和缺点,我会尽我所能在这篇文章中用一些例子来描述。所以让我们开始吧。

TL/DR 。最好使用长格式来存储数据,并在数据分析过程的最后使用宽格式来降低数据维度。使用 Pandas 从一种格式转换到另一种格式就像编写一行代码一样简单。

宽幅

你可以把宽幅想象成“Excel”的样子。想想这个。你有销售人员和去年每笔销售的数据,按月分组,你如何用表格的方式表示呢?好吧,你在行中写下销售人员的名字,在列中写下年月的名字,然后在每个行/列的交叉点上写下每个销售人员的销售额($)。

name    jan    feb    mar    apr  ... dec
jim     $100   $200   $200   $300     $300
mike    $200   $250   $50    $100     $170

因为太宽了,所以我用了三个点(…)来不写四月和十二月之间的月份。您可能会说:“假设您有一个数据,按销售人员和月份分组的销售额,一个宽格式的表格可以轻松处理所有事情”。你是对的,到目前为止没问题。让我们把这个变得更难一点。假设您现在不仅有销售额($),还有每个销售人员售出的商品数量。你再做一张像这样的桌子。

name    jan    feb    mar    apr  ... dec
jim     10     10     10     30       30
mike    5      20     7      25       5

现在您有两个宽格式的表格。如果您现在要收集每个销售人员每月工作的小时数,您现在会有 3 张表。我开始觉得这越来越重复了,你不觉得吗?

我很难想出一种方法来让我放心地将这些数据存储在数据库中。三个表存储相同月份的相同人员的数据,唯一的区别是存储的数据类型?如果我们现在想为每个销售人员存储 20 个点的数据(特征)该怎么办?我想你已经明白这个问题了。让我们试着解决它。

这些数据可以存储在一个可以生成所有这些表的表中吗?输入长格式。

但是首先,只是为了继续扩大(再次双关)这种混乱的丑陋…让我们假设您尝试使用一种广泛的方法来解决这个问题,如“我为名称添加一列,为月份添加另一列,然后为每个功能添加一列”;像这样的解决方案。

name    month    sales   items_count   hours_worked
jim     jan      bla     bla           bla
mike    feb      bla     bla           bla
...

嗯……这可能行得通,你不觉得吗?至少你只有一张桌子。是的,但是如果由于某种原因你的需求改变了,你需要添加(或删除)特性,你将不得不物理地修改这个表。另一个问题可能是,如果一个销售人员可能没有所有的功能,但只有一个子集(或一个):你会有许多空单元格。如果你用的是一个只有几行几列的 Excel 笔记本,这不成问题,但是,如果这些数据存储在一个比如说 Postgres 数据库中,每次你的数据结构改变时,你都必须运行一些 alter 命令,而且还会因为空单元格而浪费空间。我不太喜欢这个想法。你呢?

现在真的,进入长格式。

长格式

没有太多的序言,让我向您展示上述相同的数据,但使用长格式。

name    month    kpi     valuejim     jan      amount  100
jim     feb      amount  120
...
jim     jan      items   10
jim     feb      items   10
...
mike    dec      amount  170
...
mike    dec      items   5

因为它太长了(又是双关语),我再次用了三点来说明表中没有显示所有的信息。现在我们不是每月一个专栏,而是每个特性一个专栏,再加上一个月和名字专栏。这种方法的优点如下。

  • 如果丢失了一些数据,也不会浪费空间。如果发生这种情况,那么行将不存在,如此而已。
  • 如果由于某种原因添加了新功能,您不必修改表结构,只需根据需要通过添加具有新功能的行来修改数据。

另一个长格式示例

想想这个问题。你正在记录网站上的互动,有三种类型:查看、点击和转换。当前的任务是生成一个表格,显示每天有多少浏览、点击和转换。

让我们假设当一个交互发生时,你接收到两条信息:交互的日期和时间及其类型。了解了这一点,长格式表将如下所示。

type    date
view    2021-12-12 23:00
click   2021-12-12 23:30

对于每次交互,数据都存储在一个新行中,每一行都记录了事件的类型和记录时间。现在,让我们处理报告部分。要做到这一点,一些 Python 和熊猫的魔法会很方便。

代码

假设创建了这个表,并且每天都记录数据,我们可以使用一个函数来获取它。在这种情况下,该函数伪造数据,在现实世界中,该函数将从数据库或其他数据源中检索数据。

首先要导入 Numpy 和 Pandas,并创建一个函数来伪造数据。

def build_dataframe(periods=100):
    """
    Creates columns with random data.
    """import randomrandom.seed(10)data = {
        'type': random.choices(population=['impressions', 'clicks', 'conversions'], weights=[100, 10, 1], k=periods)
    }from datetime import date, timedelta
    from random import choices

    # create some random dates in a test period 
    test_date1, test_date2 = date(2020, 1, 1), date(2020, 2, 1)

    res_dates = [test_date1]

    # loop to get each date till end date
    while test_date1 != test_date2:
        test_date1 += timedelta(days=1)
        res_dates.append(test_date1)

    # random dates
    res = choices(res_dates, k=periods)

    df = pd.DataFrame(data)
    df['date'] = res

    return df# retrieve the datalong_df = build_dataframe(periods=500)

我不会详细谈论这里发生的事情,因为这不太相关。检索到的数据如下所示。

build_dataframe 函数检索由两列描述的 500 行数据。图片由作者提供。

每个数据点都包含预期的事件类型和日期。我们有数据要处理。在继续之前,让我们考虑一下我们要实现的宽表格格式应该是什么样子。

我们希望每个日期有一行,三列:印象,点击和转换。每行/列的交叉点将包含每个日期的每个事件的计数。表格输出应该如下所示。

date         impressions     clicks     conversions
2020-01-01   1000            20         1
...
2020-01-15   500             2          n/a
...

注意转换列中的 n/a 值,这意味着在长格式源中没有该类型/日期组合的数据。

今后的任务概述如下。

  1. 创建一个列来保存事件计数
  2. 对长格式表进行分组,以便每个可能的日期/类型组合都有一行
  3. 使用 Pandas pivot 函数构建宽格式表格输出
  4. 使用 Pandas melt 函数重建长格式表格输入

完成所有后者的代码如下。

# Step 1: add a count column to able to summarize when grouping
long_df['count'] = 1# Step 2: group by date and type and sum
grouped_long_df = long_df.groupby(['date', 'type']).sum().reset_index()# Step 3: build wide format from long format
wide_df = grouped_long_df.pivot(index='date', columns='type', values='count').reset_index()# Step 4: build long format from wide format
new_long_df = new_long_df = pd.melt(wide_df, id_vars=['date'], var_name='type', value_vars=['impressions', 'clicks', 'conversions'], value_name='count')

哇!只需四行代码就可以完成所需的转换。如果你熟悉熊猫,步骤 1 和 2 是不言自明的,所以让我们解释一下步骤 3 和 4 中发生了什么。

第三步

熊猫 pivot 函数需要一个长格式输入,其中作为索引参数传递的所需列的值对于每个索引/列(日期/类型)组合都是唯一的。这就是为什么需要对原始的长格式表进行分组,以便每个日期/类型组合有一行。Pandas 将为 type 列中的每个值创建一个列,并将 count 列中的值放在 date/type 交集中,如 values 参数所述。让我们看看 wide_df 变量中包含的表。

wide_df 的内容。图片由作者提供。

请注意,每个日期有一行,有 3 个数据列:印象、点击和转换。当 Pandas 在任何给定日期找不到某个类型的相应值时,就会插入一个 NaN 值。这太棒了!

随着第三步的完成,我们可以说我们的工作已经完成了。我们生成了一个表,其中包含检索到的每个日期的事件计数。干得好!

你可以把这个放在一个 Streamlit 仪表盘里?

现在让我们集中精力完成最后一步。

第四步

Pandas melt 函数从宽格式数据帧构建一个长格式数据帧。该函数希望您告诉它 1)哪一列将用作新行引用(id_vars),在我们的示例中是日期列,2)哪些列将被堆叠到新列中(value_vars),3)它的名称是什么(var_name),最后 4)包含堆叠列值的列的名称是什么(value_name)。

结果存储在 new_long_df 变量中,如下所示。

new_long_df 数据帧内容。图片由作者提供。

看起来和 build_dataframe 函数创建的 dataframe 非常相似!然而非常相似是不够的,我们应该通过比较 new_long_df 和 grouped_long_df 中包含的一个日期的数据来进一步挖掘。

日期 2020 年 1 月 1 日怎么样?让我们通过查询两个数据框来尝试一下。

第一个 new_long_df。

new_long_df[new_long_df['date'] == datetime.date(2020, 1, 1)]

new_long_df 中包含的日期为 2020 年 1 月 1 日的数据。图片由作者提供。

现在原 grouped_long_df。

grouped_long_df[grouped_long_df['date'] == datetime.date(2020, 1, 1)]

grouped_long_df 日期为 2020 年 1 月 1 日的内容。图片由作者提供。

这对于我来说已经足够了:两个数据框都同意当天有 1 次点击,18 次展示,零转化!

结论

这篇文章试图通过一些例子来描述什么是长表格和宽表格,以及如何使用 Python 和 Pandas 来处理它们。每种格式最适合某些任务:长格式允许更密集地存储数据,而宽格式在报表中需要表格格式时具有更强的解释力。根据您期望完成的任务,选择哪种格式效果最好由您决定。

你可以在这里找到一个谷歌 Colab 笔记本

如果你喜欢这些内容,请关注我并分享!

Dash Web 应用程序中的长时间回调

原文:https://towardsdatascience.com/long-callbacks-in-dash-web-apps-72fd8de25937

如何处理 Python Dash web 应用回调函数中耗时的代码

图片由作者提供,基于 moritz320 (Pixabay)的股票图片

你用 Python 开发 web 应用,有耗时的代码要运行?要从数据库中查询大量记录,要执行复杂的评估,要创建复杂的可视化?那么 Dash 2.0 引入的长时间回调可能正是你所需要的。

对于长回调,web 服务器的超时设置(在许多情况下为 30 秒)在执行耗时的代码时不会给你带来任何问题。长回调在一个单独的进程中运行,并带有额外的选项,允许您提高应用程序的响应能力,例如,通过阻止触发它的按钮或通过可视化回调的进度。

长时间回调与正常回调

在 Dash 中,回调是通过将回调装饰器@app.callback(...)放在相应的函数之上来声明的。对于长回调,也是一样的,只是使用了稍微不同的装饰器,提供了额外的功能。以下是长时间回调和普通回调之间的区别:

  • 长回调在单独的进程中运行,不会导致超时错误。
  • 为了让长回调生效,您必须安装一些您可能不需要的包(见下一节)。
  • @app.long_callback(...)是声明长回调时必须使用的装饰器。
  • runningcancelprogress:除了InputOutputState参数之外,这些可选参数在长回调装饰器的括号内。我稍后将讨论这些新特性。
  • 在有多个输入的情况下,用于标识触发回调的组件的dash.callback_context在长回调中不受支持。如果您需要这个功能,我将向您展示一个变通方法。
  • raise PreventUpdate(从dash.exceptions导入)不应该和running参数一起使用,因为它会让你的应用无限期地保持在running选项定义的状态(直到页面重新加载)。

准备好

如果你是 Dash 新手,我强烈建议你看一下文档。在开始玩长时间回调之前,你至少应该熟悉回调基础。本文中的代码需要以下 Python 包:

  • dash(2 . 0 . 0 版本;早期版本不支持长回调)
  • dash_bootstrap_components(版本 1.0.0)
  • diskcache
  • multiprocess
  • psutil

都安装好了吗?很好,现在让我们看看如何使用长回调。

开始编码

我们将从一些进口开始。我们可以不用 Dash Bootstrap 组件来构建应用程序,但我喜欢它们,因为它们可以轻松创建整洁且响应迅速的布局。time包只需要模拟耗时代码的执行。

我在main()函数中设置了应用程序的布局及其回调,该函数在脚本的最后一行被调用。我们做的第一件事是创建一个回调管理器对象和 Dash 应用程序对象,它将回调管理器作为一个参数。

请注意,我们使用的是一个 DiskCache 回调管理器,这对于开发来说非常好,而 Dash 的开发者推荐芹菜 / Redis 用于生产。在这个例子中,回调管理器将在包含 Python 脚本的目录中创建一个名为cache的文件夹。

接下来,我们添加所有的 GUI 组件,即一行文本(html.Div)、一个数字输入字段(dbc.Input)、两个按钮(dbc.Button)和另一个名为hiddenhtml.Div元素,它是不可见的(我一会儿会解释)。

使用dash_bootstrap_components,布局被构建为行的集合(dbc.Row),每一行可以包含几列(dbc.Col)。最后,所有行都放在一个dbc.Container对象中,该对象被插入到 Dash 应用程序对象的layout属性中。生成的 web 应用程序 GUI 如下面的屏幕截图所示。

现在是时候将按钮链接到回调了。我们在这个例子中使用两个按钮的原因是我想告诉你如何找出哪个组件触发了长回调,尽管我们不能在长回调中使用dash.callback_context。我使用的解决方法是编写一个普通的回调函数,将两个按钮作为输入,不可见的html.Div元素hidden作为输出。

在这个普通的回调函数中,我们可以获得触发回调函数的组件的 ID,如上面的代码所示。基于这个 ID,我们设置了hidden组件的children属性。我还在这里演示了PreventUpdate的使用,这在回调应该保持输出属性不变的情况下很有用。

区分这两个按钮的诀窍在于,hidden组件的更新(其children属性现在包含被单击按钮的名称)触发长回调。

除了提供按钮名称的hidden组件,长回调使用数字输入字段作为输入。然而,为此我们使用了State而不是Input,这样可以防止输入字段值的改变触发回调。running参数需要一个元组列表,每个元组控制一个组件属性的行为。该组件属性是元组的第一个元素,回调运行时它的值是第二个元素,结束时它的值是第三个元素。prevent_initial_call=True参数确保在加载应用程序的布局时(在用户做任何事情之前)不会执行长回调。

装饰器的可选参数cancel在示例中没有使用,但是当您希望能够中断长时间回调的执行时,它可能会派上用场。语法是cancel=[Input('component', 'property')],对指定组件属性的任何更改都将取消回调的执行。

在长回调函数中,你可以执行耗时的代码。在这个例子中,我们调用time_consuming_function(number),它接收数字输入字段的值作为参数。

这个函数使用time.sleep(seconds)来模拟耗时代码的执行,然后返回传递给它的数的平方根。

当您单击两个按钮中的一个时,会有一个延迟(在代码示例中为 2 秒),之后,html.Div元素txt_status将用结果(数字输入字段的平方根)和您单击的按钮的名称进行更新。执行回调时,两个按钮都被禁用。恭喜你,你刚刚成功使用了 Dash 的长回调逻辑!

显示进度

当您的 web 应用程序执行耗时的代码时,让用户了解进度是有意义的。方便的是,长回调带有一个progress参数,该参数接受一个html.Progress组件,从而允许您从长回调中更新进度条的值。你可以在 Dash 的长回调文档中找到解释这个功能的教程(例 4)。

在这里,我将向您展示一种不同的方法。假设您的长回调调用了另一个模块中的一个函数,该函数最初是在不同的上下文中编写的,例如,您在控制台环境中用于数据操作的脚本。这个脚本很可能已经使用了tqdm或者可以很容易地修改成这样(并且很可能从这样的修改中受益)。如果你不熟悉tqdm包,我强烈建议你看一下文档。简而言之,它允许你通过显示一个可以在各种环境下工作的进度条来度量任何类型迭代的进度。

以下是如何在不编辑现有代码的情况下,将现有代码中的tqdm进度跟踪链接到 web 应用程序中的长回调:

  • tqdm输出重定向到一个文件。
  • 编写一个定时器触发的回调函数,定期读取这个文件。
  • 从文件中提取最近的百分比值,并相应地更新进度条。

要设置这个功能,我们需要三个额外的导入:

我们修改row_3 = ...行下面的main()函数的代码如下:

这段代码向我们的布局添加了第四行,其中包含一个进度条和一个计时器(不可见),它每秒“滴答”一次。当它这样做时,它触发下面的回调函数,您可以将它添加到其他两个回调函数的下面。

try-except-finally语句用于确保即使文件progress.txt不存在或者其内容不符合预期,代码也能工作。如果文件包含正确的tqdm输出,代码将找到最后一个非空行,并提取该行开头的百分比值。然后,该函数将该值返回给dbc.Progress组件的value属性,并将相应的文本返回给其label属性。

为了将tqdm的输出重定向到文件progress.txt,我们需要修改长回调中的代码,如下所示:

默认情况下,tqdm将其输出写入sys.stderr。上面显示的代码保存当前的sys.stderr设置,然后将其更改为文件progress.txt,执行耗时函数,最后恢复sys.stderr的原始设置。

为了测试这个功能,我们修改了这个耗时的函数,这样它就包含了一个由tqdm跟踪的迭代:

这就是你将tqdm链接到你的网络应用的进度条所需要做的一切!当您完成代码修改后,在运行脚本并单击按钮时,您应该会看到类似这样的内容:

我想用一个简短的回顾来结束我的发言。你刚刚才知道

  • 多头回调在哪些方面不同于标准回调。
  • 使用长回调的先决条件是什么。
  • 如何实现一个长回调的基本结构,这个回调知道它是由哪个组件触发的。
  • 如何访问tqdm的输出来跟踪可能属于另一个模块的代码的执行进度。

感谢您的阅读。如果你觉得这篇文章很有趣,请考虑订阅以在未来获得更多这样的内容。如果你还没有中级会员,你可以在这里得到一个。

超越 ELI5 的长篇问答:一个更新的数据集和方法

原文:https://towardsdatascience.com/long-form-qa-beyond-eli5-an-updated-dataset-and-approach-319cb841aabb

我们介绍了 ELI5 数据集的后继者,这是一个构建长格式问题回答(LFQA)的基础,这是一个为开放式问题生成详细答案的任务

每天我们打开笔记本电脑和智能手机,打开网络浏览器,导航到我们最喜欢的搜索引擎,搜索我们问题的答案。这种惯例框定了我们的个人和职业生活。现代搜索引擎提供网站推荐,而不是简单的答案。但这种情况即将改变。

下一代搜索引擎的原型已经在这里,它们只是对公众隐藏起来,目前仅限于 OpenAI 和 DeepMind 等公司的研究实验室。仅仅相隔几天,2021 年 12 月,OpenAI 和 DeepMind 都发布了这些系统的预览。OpenAI 展示了一个系统,它可以综合我们网络搜索类问题的原始答案,同时引用网络参考来支持这些答案。DeepMind 发布了一个检索增强的 Transformer (RETRO),专门从事知识密集型开放式问题回答。

虽然这些都是令人兴奋的发展,但基于所谓的长格式问答(LFQA)的系统仍未准备好被广泛采用。我们希望今天发布的 LFQA 数据集是朝着正确方向迈出的又一步。除了 LFQA 数据集,我们还发布了基于 BART 的 Seq2Seq 模型的新版本,用于抽象答案生成和匹配基于 DPR 的问题和上下文段落编码器。

让我们深入研究数据集,这是 LFQA 系统背后的一个重要难题,以及我们改进它的工作。我们将涵盖最初的绊脚石和我们前进的尝试,并解决阻碍这一激动人心的领域进展的一些障碍。

背景

在过去的几年里,我们见证了自然语言处理(NLP)的惊人进步。基于最新语言模型(BERT、Roberta 等)的问答系统。)可以相对轻松且精确地回答基于仿真陈述的问题。这些问答系统提出一个问题,找到相关的文档段落,并通过扫描正确的单词标记范围来提取最可能的答案。

最近,研究人员开发了更复杂的问答系统,可以回答需要大段答案的开放式问题。这些系统通常被归类为长格式问题回答(LFQA)系统。它们通过在大型文档库中查询相关信息,然后使用检索到的文档来生成精确的多句子答案。与给定查询相关的文档,通俗地称为上下文段落,不仅仅用作提取的答案的源标记,而是为原始的、抽象的长形式答案的合成提供更大的上下文。LFQA 系统通常由三部分组成:

  • 包含各种主题的内容段落的文档存储
  • 检索器模型对文档/问题进行编码,以便可以查询文档存储
  • Seq2Seq 语言模型能够在给出问题和从文档存储中检索的上下文段落时生成整段的答案

LFQA 架构—作者图片

动机

这些组成部分提出了一些挑战,但最近取得了进展。2019 年,Angel Fan 等人在他们的开创性研究论文“Eli 5:长格式问答”中正式介绍了长格式问答的任务。它包含了来自 Reddit 论坛的 27 万个问题/答案对的数据集,“像我五岁一样解释”(ELI5),这是一个在线社区,致力于使用五岁儿童可以理解的语言回答复杂的问题。ELI5 数据集由不同的问题组成,这些问题需要大段的答案。尽管生成 ELI5 答案的 Seq2Seq 模型产生了相对可理解的答案,但人类评估者在超过 86%的情况下更喜欢黄金答案,这表明了未来改进的潜力。

最近,Krishna 等人在他们的论文“长格式问题回答进展的障碍”中描述了他们如何使用 REALM 初始化的、基于 BERT 的检索器和基于路由转换器的 Seq2Seq 答案生成模型来改进原始 LFQA 任务。除了在 LFQA 任务 KILT 基准上达到“最先进”水平,Krishna 等人还发现了 LFQA 的四个悬而未决的问题:

  • ELI5 数据集具有显著的训练/验证/测试数据集重叠
  • 答案生成不以检索(上下文段落)为基础
  • LFQA 任务指标需要改进
  • 人工评估具有挑战性,但对于评估 LFQA 是必要的

这些问题中的每一个对于整体上改进 LFQA 任务都是非常重要的。

LFQA 数据集—减少 ELI5 训练/验证/测试重叠

着眼于推进和改进 LFQA,我们创建了一个训练/验证/测试数据集,与 ELI5 数据集相比,该数据集减少了重叠。我们怀疑减少重叠可能会改善答案在各自检索中的基础。

我们使用句子-BERT (SBERT)、语义搜索工具和 HuggingFace (HF) ELI5 数据集来比较训练、测试和验证集中的问题,以衡量语义相似性。更准确地说,我们比较了数据集问题的前 K 个相似性得分(K = 1,2,3 ),并证实了 Krishna 等人报告的重叠结果。

使用人类的判断,我们已经发现高于余弦相似度阈值 0.8 的问题非常相似,而高于 0.84 的问题几乎是相互转述。以下是从 KILT ELI5 验证集中选择的几个示例问题,训练集中最相似的“兄弟”问题,以及它们各自的余弦相似性。

ELI5 问题示例—作者图片

Krishna 等人进行了一项人类研究,发现“81%的验证集问题在训练集中至少有一个释义,而所有带注释的问题在训练集中至少有一个主题相似的问题,这表明大量的训练/验证重叠。”

使用 SBert,我们为验证和测试集中的每个问题在训练集中搜索语义最相似的问题。然后,我们绘制了数据集中训练/验证和训练/测试问题之间语义相似性的概率。如下面的重叠概率图所示,在训练集中,验证问题具有非常相似的问题“兄弟”的概率确实很大。然而,我们的估计比 Krishna 等人报告的 81%要小一些,更接近 60-65%。

ELI5 训练/验证和训练/测试重叠(0–1 相似性范围的 50 个箱)-按作者分类的图像

虽然比训练/验证重叠稍小,但是测试和训练集问题之间显著语义相似的概率也很高。

新的 LFQA 数据集包含来自三个 Reddit 论坛的问题/答案对:r/explainelikeimfive、r/ask histories 和 r/askscience。我们的目标是创建一个新的 LFQA 数据集,包含训练/验证/测试分割,以便:

  • 所有三个子网格在所有分割中被同等地表示
  • 与旧的 ELI5 数据集相比,训练/验证/测试重叠被最小化,同时保持数据集的大小
  • 验证/测试分割包含来自数据集的 KILT 版本的所有 ELI5 测试/验证示例
  • 验证和测试分割包括具有高 Reddit 分数(投票)的问题

由于问题嵌入在空间上不是均匀分布的,我们求助于层次聚类。在问题空间的密集部分中具有 0.9 点积的两个问题可能需要不同的答案,而相反,在空间的稀疏部分中具有 0.8 点积的问题可能是同一问题。

在分层问题聚类之后,我们尝试了一种只选择叶节点的启发式方法,但是这种方法导致数据集的数据点太少。由于一些非叶节点彼此非常接近,我们添加了一个自适应停止标准,该标准只到达某个深度、连接相似性阈值或子树中的一些项目。正如我们将在下面看到的,0.83 的点积相似性阈值产生了适当大小的数据集,但训练/验证/测试重叠更小。正如您在下面的相似性概率图中看到的,与 ELI5 数据集(左)相比,新的 LFQA 数据集(右)的训练/验证重叠显著减少,相似性更高。只有一小部分问题的最大相似度为 0.83——大多数重叠问题的相似度为 0.75 及以下。

ELI5 与 LFQA 训练/验证重叠(0–1 相似性范围的 50 个箱)-按作者分类的图像

同样,训练/测试重叠也减少了。

ELI5 与 LFQA 训练/测试重叠(0–1 相似性范围的 50 个箱)—作者图片

以下是从 LFQA 验证集中选择的几个示例问题,它们在训练集中最相似的“兄弟”问题,以及它们各自的余弦相似性。在训练/验证和训练/测试集中没有相似性超过 0.83 的问题。

LFQA 问题示例—作者图片

LFQA DPR 寻回犬

为了生成所提问题的答案,抽象答案生成模型依赖于检索器来找到相关的上下文段落。LFQA 检索器由基于 DPR 的问题和上下文编码器组成。

我们分两个阶段使用 FAIR 的 dpr-scale 训练我们的 DPR 寻回犬。在第一阶段,我们使用基于 PAQ 的预训练检查点,并在 LFQA 数据集的问答对上微调检索器。由于 dpr-scale 需要具有阳性、阴性和硬阴性样本的 dpr 格式的训练集输入,我们创建了一个训练文件,其中阳性对应于一个答案,阴性是与问题无关的答案,而硬阴性样本选自余弦相似度在 0.55 和 0.65 之间的问题的答案。在第二阶段,我们使用来自第一阶段创建的维基百科/Faiss 索引而不是来自 LFQA 数据集答案的正面、负面和硬负面来创建新的 DPR 训练集。更准确地说,对于每个数据集问题,我们查询第一阶段维基百科/Faiss 索引,随后使用 SBert 交叉编码器对 topk=50 的问题/答案(段落)对进行评分。交叉编码器选择正样本以对应于具有最高分数的段落,底部的七个答案被选择用于硬负样本,而负样本再次是与给定数据集问题无关的答案。在用维基百科来源的正面、负面和硬负面段落创建了 DPR 格式的训练文件后,我们使用 dpr-scale 训练了 DPR 的问题/段落编码器。新的 LFQA DPR 检索器的性能略低于 Krishna 等人使用的 REALM 检索器,R-precision 的 KILT 基准性能为 11.2,Recall@5 的 KILT 基准性能为 19.5。

演示

为了纪念新的 LFQA 数据集、基于 LFQA BART 的 Seq2Seq 模型和基于 DPR 的 retriever 模型的发布,我们还在 https://huggingface.co/spaces/lfqa/lfqa发布了维基百科助手的 HuggingFace Spaces 演示

为了找到与问题相关的维基百科上下文段落,我们使用 cortex.dev ML 部署平台在 AWS 云上部署了一个上下文服务器。上下文服务器包含加载到 FAISS 索引中的维基百科快照的编码版本。用户可以问与科学、历史和维基百科上的其他不同主题相关的长格式开放式问题。给定一个任意的用户问题,DPR 检索器将从上下文服务器中检索相关的文档,并将它们传递给 BART LFQA 模型,该模型将生成一个抽象的答案,并且传递给文本到语音模型,该模型将生成一个音频响应。在前面提到的 HuggingFace Spaces 位置可以获得演示以及模型训练代码和实用程序。

我们今天发布的 LFQA 数据集有 226147 个训练,3020 个验证和 10000 个测试样本。LFQA 数据集具有显著减少的训练/验证/测试重叠,可以成为解决长格式问题回答中剩余障碍的垫脚石。

LFQA 数据集、基于 DPR 的检索器和新的基于 LFQA BART 的抽象答案生成模型都可以在 https://huggingface.co/vblagoje的 HuggingFace hub 上获得

我们要感谢 Yacine Jernite 在整个项目中提供的宝贵反馈。布兰登·陈、巴勃罗·罗德里格斯·贝托雷罗、苏奇米特·马哈帕特拉和克里斯蒂娜·克雷斯勒对本文的初稿提供了反馈。

BibTeX 引文

@article{blagojevic2022lfqa,
  author={Vladimir Blagojevic},
  title={Long-Form QA beyond ELI5: an updated dataset and approach},
  year={2022},
  url={towardsdatascience.com/long-form-qa-beyond-eli5-an-updated-dataset-and-approach-319cb841aabb},
}

附录

来自验证集的示例问题及其生成的答案

是什么造成了高空喷气式飞机后面的尾迹?

它被称为轨迹,是由喷气发动机排气中的水蒸气凝结造成的。水蒸气凝结在喷流的排气羽流上,当它凝结时,会在喷流后面留下一条尾迹。

为什么我可以在嘈杂的环境中入睡(学校讲座、公共交通、电影院等),但当我在床上时,更小的噪音也会打扰我的睡眠?

我认为这与你大脑的工作方式有关。当你处于安静的环境中时,你的大脑不需要像醒着时那样处理大量的信息,所以你不需要那么多的处理能力来入睡。这就是为什么你会在嘈杂的环境中睡着(学校讲座、公共交通、电影院等)。)但是更小的噪音也会打扰你的睡眠。

马里正在发生什么,德国和法国与此有什么关系?

马里是非洲萨赫勒地区的一个国家。它有大量的穆斯林人口。过去几年,马里发生了多起恐怖袭击事件。马里政府无力阻止这些袭击,被迫诉诸军事行动来阻止这些袭击。德国和法国决定向马里派兵,帮助马里政府打击恐怖分子。这是欧盟军队首次在非洲部署。

教皇实际上是做什么的?

教皇是罗马天主教会的领袖。他也是整个天主教会的领袖。教皇是由枢机主教团选出的,枢机主教团是由教会的主教们选出的一群枢机主教。该学院由来自世界各地的主教组成,教皇由他们推选为罗马主教。

纵向自我监督学习

原文:https://towardsdatascience.com/longitudinal-self-supervised-learning-29d205129f97

利用纵向测量来学习更好的表达

纵向自我监督学习是一种令人兴奋的新方法,用于在重复测量的大型数据集上进行学习。这尤其适用于医学领域(成像、表格健康记录数据等。),其中患者可能有几个月或几年的数据点,但并非所有数据点都有确切的标签(例如,特定疾病的存在)。

Unsplash 上由 Aron 视觉拍摄的照片

背景

自我监督学习(SSL)是近年来深度学习领域的热点之一。在自我监督学习中,我们训练一个模型在不使用标签的情况下对我们的数据执行任务。这有助于我们了解数据的有意义的表示,然后可以用于下游任务(如对感兴趣的下游标签的预测)。换句话说,我们可以在大型未标记数据集上进行自我监督学习,然后在较小的标记数据集上进行“微调”或继续训练,以从标记和未标记数据的组合中提取最大性能。这种方法与当今大量未标记数据(视频、自由文本、医疗记录等)的可用性相一致。).

我们为 SSL 选择的任务很重要。它应该允许我们学习一些语义表示。一个简单的例子是轮换任务。我们可以手动将图像旋转一个随机角度,并训练一个模型来预测旋转量。如果我们的数据集主要是人或脸,模型应该能够学习有意义的特征,如眼睛,头发等。以便预测正确的旋转(例如,它应该学习 1)识别眼睛和头发,以及 2)头发在眼睛上方/旁边)。

轮换任务。图片由作者提供,灵感来自 Gidaris 等人,2018 。照片由 Alvan NeeUnsplash 上拍摄

对比学习中,一种流行的 SSL 方法,我们收集正面和负面样本。我们训练模型以最小化阳性样本(锚和阳性)之间的嵌入距离,同时最大化阴性样本(锚和阴性)之间的距离。在图像域中,负样本可以是不同的图像,而正样本可以是原始图像的增强版本,使得图像的语义保持不变(例如,裁剪、旋转、颜色失真)。

对比学习的例子。正对的潜在表示(具有缩放+裁剪放大的相同图像)被拉近,而负对被推得更远。注意:我们没有使用标签知识进行自我监督学习,所以负面图像可能是另一只狗。图片由作者提供,照片由 Alvan NeeAlexander LondonUnsplash 上拍摄。

纵向自我监督学习

医学领域的数据为自我监督的学习任务提供了新的机会,而不仅仅是增加单个数据点。我们有复杂的数据,如健康记录或 3D 医学图像,并且我们经常有来自一个主题的许多数据点。这使我们能够学习捕捉与疾病进展、衰老或发育相关的时间相关信息的表征。我将讨论一些自我监督学习的方法,首先是一些对比方法,然后是一些非对比方法。

排列对比学习

纵向 SSL 的早期方法是排列对比学习(PCL) [4]。在 PCL 中,我们通过从同一受试者中以正确的顺序获取两个连续的窗口或时间数据点来创建正对。对于负配对,我们从同一受试者身上随机选取两个窗口。然后,我们可以训练一个二元分类器来预测图像对是正面的还是负面的。正面和负面配对的直观示例如下所示:

5 次就诊的单患者轨迹示例。图片作者。

两个可能的 PCL 阳性样本。阳性样本必须连续且顺序正确。图片作者。

两个可能的 PCL 阴性样本。阴性是随机选择的配对。图片作者。

请注意,这项任务不一定要学习跨主题的差异,因为配对总是来自同一主题。这使得我们的模型能够关注重要的纵向过程。

订单对比预培训

PCL 的一个问题是阴性样本是完全随机选择的。它们实际上可能处于正确的顺序,但仍然被标记为阳性。最近的一种方法,称为顺序对比预训练(OCP) [1],通过将正样本约束为连续、正确排序的窗口,将负样本对约束为连续、错误排序的窗口,来改进 PCL。

两个可能是 OCP 的阳性样本。阳性样本必须连续且顺序正确。图片作者。

两个可能的 PCL 阴性样本。阴性样本必须连续且顺序错误。图片作者。

OCP 的作者表明,在纵向表征学习方面,它在经验上和理论上都优于 PCL。他们通过 1)电子健康记录的特征选择(即 L1 回归)和 2)临床放射学笔记的 NLP,使用了表征学习的例子。

潜在空间排列

另一类纵向 SSL 方法采用了与对比 OCP 和 PCL 方法完全不同的方法,主要应用于医学成像。在这种方法[7]中,我们学习了一个标准的自动编码器,它将图像编码为潜在的表示,并将其解码为重建的图像。

标准自动编码器。图像 X 被编码成潜在表示 Z,然后由作者重新构造回图像 X。

除了标准重建损失(即均方误差),还有对准损失。我们在潜在空间中学习一些单位向量τ,并强制相同主题的图像对仅在τ方向上变化,如下所示。

纵向 SSL 的潜在对齐方法。每个对象的表示被迫与轨迹向量τ对齐。图片由作者提供,灵感来自[7]。

这可以通过余弦损失项来实现:

图片由作者提供;

其中 g 是编码器函数,I^s 和 I^t 是来自两个独立时间点和同一对象的图像对,并且 θ 是模型参数。我们试图最大化这一项。直觉是这样的:使相同主题图像表示中的差异平行于τ,这意味着该差异向量和τ之间的角度应该为 0。这使余弦函数最大化。

这种比对允许我们在潜在空间中学习有意义和可解释的纵向年龄因素(在人类受试者的情况下), 1)不需要年龄或疾病标签,2)优于简单地训练模型来预测年龄以获得年龄相关的表征。与其他流行的 SSL 方法相比,自我监督训练在利用大脑图像进行下游疾病预测任务时表现更好。

对原始纵向比对方法的后续研究[5]提出了一种更灵活的扩展,称为纵向邻域嵌入(LNE)。这种方法的总体思路是,我们可以强制相似的对象(基于潜在空间中的距离)具有相似的纵向轨迹,而不是强制全局轨迹方向τ。LNE 的损失函数几乎与全局对准的损失函数相同,但是代替最大化图像对潜在差异和τ之间的余弦,我们用δh 代替τ,δh 是 N 个最近嵌入的距离加权平均轨迹。

纵向邻域嵌入。对于图像对(标记为 z1,z2),我们鼓励它们的轨迹与它们的邻域(阴影圆)的平均轨迹**δh 相匹配。图片由作者提供,灵感来自[5]。

这种方法同样适用于脑成像。大脑年龄,或大脑解剖结构随时间的变化,即使在像阿尔茨海默氏病这样的单一疾病中也是高度异质性的。然而,这些路径可能属于定义相对明确的簇[6],使得轨迹邻域模型非常适合于模拟大脑老化过程。

图像校正

纵向自我监督学习的最后一种方法包括随着时间的推移从同一对象获取一对图像,对第二图像的部分进行修补或涂黑,并使用这两幅图像(完整的第一图像,受损的第二图像)来预测校正后的图像[2]。提出这种方法的论文将其用于肺部 CT 扫描。两个图像都用于形成这种表示;enocder 可用于下游任务,如分割或病理分类。(注意:图像对必须可用于该方法中的下游任务,因为预训练的编码器需要两幅图像作为输入)。

基于图像校正的纵向 SSL 方法。图片由作者提供, NCICDCUnsplash 上的照片,灵感来自【2】。

摘要

纵向自我监督学习是一种新的和令人兴奋的方法,用于从未标记的纵向数据中学习表示。当 1)随着时间的推移,您有许多来自相同对象的图像(或记录)对,以及 2)感兴趣的下游任务(例如预测慢性疾病)随着时间的推移可能会有很大变化时,此功能特别有用。大型纵向公开数据集变得越来越普遍。看到纵向 SSL 对这些领域中的 ML 研究的影响将是有趣的。

参考资料:

  1. 阿格拉瓦尔,男,郎,h,奥芬,男,加齐特,l,桑塔格,d,2021。利用顺序的时间不可逆性-对比预训练 151。
  2. 茨万皮尔、罗杰斯、基歇、帕斯卡利、布拉伦、布里安、马考斯基、纳瓦布、温德勒、金、苏特,2022。新冠肺炎病理学量化纵向自我监督 1-10。
  3. 吉达里什,s .,辛格,p .,科莫达基斯,n .,2018。通过预测图像旋转的无监督表示学习。第六国际。糖膏剂学习。代表。ICLR 2018 年奥运会。跟踪进程。1–16.
  4. Hyvä rinen,a .,h .盛冈,2017。时间相关平稳源的非线性 ICA。继续。第 20 国际。糖膏剂阿提夫。智能。统计。AISTATS 2017。
  5. 欧阳,j .,赵,q .,Adeli,e .,Sullivan,E. V .,Pfefferbaum,a .,Zaharchuk,g .,Pohl,K.M .,2021。自我监督纵向邻域嵌入,计算机科学讲义(包括人工智能子系列讲义和生物信息学讲义)。斯普林格国际出版公司。https://doi.org/10.1007/978-3-030-87196-3_8
  6. 沃格尔,J.W 等人,2021 年。阿尔茨海默病中 tau 沉积的四种不同轨迹。纳特。医学。27, 871–881.https://doi.org/10.1038/s41591-021-01309-6
  7. 赵,刘,郑,阿德利,波尔,K.M .,2021。纵向自我监督学习。医学。形象分析。71, 102051.【https://doi.org/10.1016/j.media.2021.102051

Looker 和 big query ML:KPI 监控的两种方法

原文:https://towardsdatascience.com/looker-and-bigquery-ml-create-control-charts-for-your-kpis-3d9ad6ee99f7

如何跟踪和报告多系列数据集中的 KPI 目标

Miguel A. Amutio 在 Unsplash 上拍摄的照片

简介

在与跨组织的业务团队合作开发战略措施时,我们注意到所有领域都有一个共同的分析需求。这就是:监控和报告 KPI 目标

如果我们把重点放在监测上,主要任务是对照实际值跟踪目标。也就是说,应首先定义目标

然而,对于业务用户来说,在多系列数据集上定义目标并不总是容易的。换句话说,数据集中的每一个新系列的复杂度都会增加。

我们将分享一个例子。

假设我们想要跟踪 40+家商店的revenue KPI。为此,我们需要定义每个商店级别的预期revenue的上限和下限,即 80+目标。如果我们想要跟踪每个商店和国家级别的revenue,我们可以使用公式number_of_shops x number_of_countries x 2 (upper and lower target)计算所需目标的数量。

为了克服在业务方面定义 KPI 目标的复杂性,我们的数据和分析团队开发了两种分析方法:

  1. big query ML车间级方法:用于设置revenuemarketing costs等连续措施的目标。
  2. 车间级 查看器 中的硬编码方式:用于设置比率措施的目标,如[cost revenue ratio](https://www.indeed.com/career-advice/career-development/cost-revenue-ratio#:~:text=What%20is%20a%20cost%20revenue,including%20marketing%20and%20shipping%20costs.)[return on ad spend](https://www.klipfolio.com/metrics/marketing/return-on-ad-spend-roas)

让我们从发展阶段的解释开始。😃

方法论体系

三个主要阶段定义了开发框架:

  • 第一阶段:定义 KPI 目标
  • 第二阶段:数据建模
  • 第三阶段:见解陈述

此外,每个阶段分为两个步骤,如下图所示。

开发方法的分解[图片由作者提供]

在开发部分,我们将使用两个不同的数据集来展示上述步骤的实施:

  • (1)成本营销数据集,
  • (2)车间订单数据集

根据这两个数据集,我们将计算以下 KPI:

  • **marketing costs** —所有流量渠道的费用(如谷歌、脸书等。)每家店,
  • **cost revenue ratio (CRR)** —每家店铺的营销成本与收入的比率,
  • **return on ad spend (ROAS)** —逆 CRR,即每家店铺的收入与营销成本之比。

如导言中所述,目的是展示如何使用两种不同的方法来监控和报告战略性 KPI。

所以,先从 Looker 里的动手部分说起。😃

阶段 1/3:使用 BigQuery ML 方法和 Looker 中的硬编码方法定义 KPI 目标

步骤 1/2: BigQuery ML 方法:为设置目标**marketing costs**

我们将使用 BigQuery ML (BQML)时间序列模型 ARIMA_PLUS来寻找marketing costs目标。

一般来说,为了训练 BQML 多时间序列模型,我们需要定义以下内容[1]:

  1. model_type =时间序列模型的类型,即ARIMA_PLUS
  2. time_series_data_col =目标变量= marketing costs
  3. time_series_timestamp_col =时间戳变量=费用的日志日期
  4. time_series_id_col =系列变量= shop
  5. horizon =预测范围,即我们希望预测未来多远
  6. data_frequency =时间段频率,即每天、每月、每周等。

在解释了模型架构之后,我们可以将 BQML 模型放在一个 LookML 视图中:

模型的结果保存到 Looker 项目的预定义数据库中(检查管理部分→数据库→连接)。

— — — — — — — — — — — — — — — — — — — — — — — — — — — —

步骤 2/2:Looker 中的硬编码方法:设置 **cost revenue ratio (CRR)** **return on ad spend (ROAS)**的目标

这一步很简单,因为任务是:

  • 将店铺分成店铺群,即品类/品牌/性能店铺群。
  • CRRROASKPI 设定硬编码目标各车间组

为此,我们将使用 LookML 层中的[manifest](https://docs.looker.com/fr/reference/manifest-reference)文件,为每个车间组创建常数,并为每个车间组的CRRROAS目标值创建常数

下图显示了一个创建常量的例子,完整的清单代码可以在这里找到。

太神奇了。现在我们可以切换到阶段 2 并创建一个数据模型来合并此阶段开发的步骤的结果。

阶段 2/3:数据建模:汇集实际和目标 KPI 值

现在是简洁的部分,即数据建模部分。

步骤 1/2: LookML 建模:在一个视图中组合结果

这一步包括开发一个派生表,它是三个查询UNION的产物:

  • 查询,检索每个商店marketing costs目标值(BQML 模型结果),
  • 检索每个商店marketing costs实际值的查询(根据成本营销数据集计算),以及
  • 检索每个商店的实际值revenue的查询(根据商店订单数据集计算)。

从上面的视图中可以看出,通过计算每个商店的marketing costsrevenue,我们也能够得到实际的CRRROAS值。

此外,我们将这两个 KPI 的实际值与目标值进行了比较。

这个阶段剩下的最后一部分是在这个视图之上创建一个数据模型。

— — — — — — — — — — — — — — — — — — — — — — — — — — — —

第 2/2 步:LookML 建模:在第 1 步中创建的视图之上创建一个数据模型

瞧啊。我们成功地完成了数据建模部分,现在可以进入最后一个阶段。

阶段 3/3:洞察力演示:创建控制图和控制表

我们最终可以以可视化的形式呈现数据洞察,即我们的三个 KPI。

第 1/2 步:观察者:创建控制图跟踪 **marketing costs**

教程:

  • 选择滤镜:Marketing Shop TypesOrder DateShop Brand
  • 选择维度和度量:Order datePM Costs Predicted [€]PM Costs Lower Bound[€]PM Costs Upper Bound [€]Above/Below Predicted cost value [%]
  • 选择Line图表类型以可视化结果。

旁观者:跟踪营销成本目标的控制图[图片由作者提供]

功能:

  • 通过上述设置,我们可以每天跟踪数据集中每个商店的marketing costs。****

— — — — — — — — — — — — — — — — — — — — — — — — — — — —

步骤 2/2: Looker:创建一个控制表来跟踪 **CRR** **ROAS****KPI**

教程:

  • 选择过滤器:Marketing Shop TypesOrder DateShop Brand
  • 选择尺寸和度量:Order dateMarketing Shop TypesShop BrandCRR Actual [%]CRR Target [%]ROAS ActualROAS TargetAbove/Below predicted CRR value [%]Above/Below predicted ROAS value [%]
  • 选择Table (Report)可视化类型来呈现结果。

Looker:跟踪 CRR 和 ROAS 目标的控制表

功能:

  • 通过控制表,我们可以按照定义的车间组每天跟踪CRRROASKPI。****

摘要

本文中呈现的实现阶段的简要概述如下:

  • 首先,我们提出了两种不同的方法来定义多系列数据集的 KPI 目标;BigQuery ML 和 Looker 中的硬编码方法。
  • 其次,我们展示了如何在数据建模阶段将实际和目标 KPI 值结合起来。
  • 最后,我们创建了一个 Looker 教程,用于将制作成控制图/控制表,并可视化地呈现数据洞察。****

有了这些摘要,我们就完成了监控和报告 KPI 目标的实施之旅。编码快乐!😃

参考文献:

[1] BigQuery ML ARIMA 加文档,访问日期:2022 年 5 月 1 日,https://cloud . Google . com/big query-ML/docs/reference/standard-SQL/bigqueryml-syntax-create-time-series

用 python 在卫星影像时间序列中寻找模式

原文:https://towardsdatascience.com/looking-for-patterns-in-satellite-image-time-series-with-python-de097f4065d7

使用开放 python 库处理时变影像的快速指南

作者图片

不久前,我在这里写了一篇文章,讲述了使用 python 进行遥感的一些基础知识。上次我们介绍了 geopandas、rasterio 和 google earth engine 的基础知识,以处理卫星图像和地理空间数据。

这一次,我们将扩展计算植被指数(NDVI)的概念,看看这些指数如何随时间变化,以及我们可以用它来做什么可视化。我们将以类似的方式开始,使用包含我们感兴趣的地块的矢量图层。

首先是几块农村土地

我们现在看到的是位于巴西最南端的卡皮瓦里多苏尔,一个以出口水稻闻名的城市。我们从巴西的 CAR 获得了一个包含所有地块的 shapefile,这是一个国家环境和农业注册表,我们将依次通过 geopandas 读取它。

中间的空白点是城市居民区。其他的基本都是稻作文化。(图片由作者提供)

在我们实际获取卫星图像之前,我们需要细化这个矢量数据集。其原因是,虽然这些地块仅反映了该市的农村景观,但每个农村地块实际上不仅仅有农作物:每个地块内都有保护区(由于环境立法的存在)和人们居住的房屋,因此我们不想计算这些的 NDVI。

从云中访问土地覆盖数据

为了过滤掉所有不是农作物的东西,我们将使用令人惊奇的map biomass项目。MapBiomas 是一个基于谷歌地球引擎的项目,除其他外,该项目为整个巴西提供来自大地卫星图像的土地覆盖分类。这意味着我们可以只使用地球引擎的 python API 来访问文件!

Mapbiomas 很神奇,说实话。(图片由作者提供)

这里棘手的部分是找到文档并意识到 MapBiomas 项目在他们存储图像的方式上做了一些改动。每次项目团队修改土地覆盖分类算法,都会有一个新的集合可用(我们目前在集合 6)。反过来,该系列只有一个单幅图像,有几个带,一个代表一年。因此,我们需要使用一个带通滤波器来获取 2020 年的图像(最新的可用图像)。

值的范围从 0 到 33,因为这是由 MapBiomas 指定的土地覆盖类的数量。(图片由作者提供)

使用 rasterio 从栅格到矢量

一旦我们将图像导出到我们的本地环境中(代码片段见上一篇文章,我们需要通过使用 rasterio 特性将它们转换成矢量,并隔离所有属于农业类别类,如多年生作物和一年生作物。接下来,我们希望通过使用 geopanda 的叠加功能找到这些新获得的形状和地块之间的交点。

结果是宗地的形状减去 Mapbiomas 未分类为农业的所有内容。(图片由作者提供)

访问多个时间戳的原始影像

太好了。现在我们已经筛选出了仅用于农田的地块,我们可以开始实际的遥感工作了。我们将首先编写一个基于日历范围的元数据过滤影像的函数,该函数将用于 Landsat 8 集合。有趣的是,该功能可以用于任何其他传感器,例如 Sentinel 2。该函数基本上按年过滤,然后按月过滤,最后计算每个时间戳中每个像素的中值。一旦我们获得了图像集合,我们就可以获取图像列表并将它们导出到我们的驱动器。这样我们可以在本地继续工作。

我们刚刚下载的所有图像,一年中的每个月一张。(图片由作者提供)

栅格到数组,然后再返回

手头有了所有的图像,我们终于可以计算植被指数,或 NDVI。上一次我们使用 Earth Engine 这样做,这肯定更简单,但这里我们将使用 rasterio 和 numpy。

我们将使用一个使用 rasterio 的函数来隔离我们需要的波段——4 个红光波段和 5 个 NIR 波段——然后将整个东西转换成一个 numpy 数组。归一化差值(NIR-Red/NIR+Red)的计算使用 numpy 内置函数执行,结果被写回到新的 geoTiff 中。

然后,该功能可以应用于地球引擎通过使用一个超级简单的循环将它们导出到的文件夹中的所有图像。

所有生成的植被指数图像均以单波段伪彩色呈现。(图片由作者提供)

全年的区域统计

太美了。接下来,我们将使用 rasterstats 非常类似于上次的-来计算每个宗地的区域统计数据。我们最感兴趣的是不同地块上植被指数的平均值,所以我们可以分别从统计角度来看它们。

带有新列的数据框架。(图片由作者提供)

这些新列现在可以像以前一样用于绘制地理数据框,但是色带范围为 0 到 1。让我们更进一步,使用 imageio 将所有的图放在一个 gif 中。

有点像跳动的心脏,但这是米饭。(图片由作者提供)

这些情节当然很有趣,但可能不是理解这些农田动态的最佳方式。因此,让我们利用将数据存储在数据框架中的优势,为每个月绘制一个箱线图。

这讲述了一个更完整的故事。(图片由作者提供)

现在我们可以更清楚地看到发生了什么。由于水稻作物每隔 150 天就可以收割一次,所以我们在一年中会看到两个“山谷”是有道理的,那时大部分生机勃勃的绿色植被都从地里移走,以便与谷物分离。

那是一个包裹

就是这个!我们设法从一堆包裹到一些非常整洁的时间图表。

这里当然可以包括一些东西,但不是为了简单起见,比如遮蔽云层或选择多个传感器,但这是一个干净的版本。目前为止!

如果你有问题或建议,不要犹豫,随时给我写信。如果你喜欢这篇文章,考虑给我买杯咖啡,这样我就能继续写更多这样的文章了!

如果你还不是一个媒介会员,想要支持像我这样的作家,可以通过我的推荐链接随意报名:

https://guilhermeiablonovski.medium.com/membership

寻找超级用户旅程

原文:https://towardsdatascience.com/looking-for-power-user-journeys-in-e-commerce-746f5f68b697

BigQuery SQL + Data Studio 解决方案

奈杰尔·塔迪亚恩多在 Unsplash 拍摄的照片

这是这篇文章的后续文章,在这篇文章中,我分享了如何使用 BigQuery 数据来理解用户的旅程。我们在一个奢侈品电子商务设置中使用了一个类似的框架,这不仅有助于理解我们的用户在做什么,还可以了解产品工作的重点领域。

第一篇文章谈到了如何在 BigQuery export 中使用 Firebase 触发的自动screen_view事件来询问和回答用户如何使用你的应用程序的问题。虽然第一篇文章是一组指南,但我希望第二篇文章更具战术性,提供可行的 SQL 代码和 Data Studio 示例。

这篇文章对分析师很有用,尤其是那些希望利用 BigQuery GA4 或 Firebase 数据来了解用户旅程的产品分析师。如果你没有对窗口函数感到困惑,你可以在下面的代码片段中找到一些例子,希望对你有所帮助。

数据集

我将使用 BigQuery 沙盒和带有点击流数据的公共数据集来说明概念和想法。BigQuery 沙盒是免费使用的,所以你不会产生任何费用。它有局限性,但是,这与我们的用例无关。

如果你想跟进,这是一个到 BQ 控制台https://console.cloud.google.com/bigquery的链接。这里有一个视频描述了如何设置沙箱。

数据集是谷歌商品商店 GA4 数据的样本。这是一个网站,而不是一个应用程序,然而,大多数 GA4 事件将被测量类似于一个应用程序。在应用程序中,你最有可能追踪screen_view事件,而在网络上,你很可能遇到性质相似的page_view事件。

对于 web 和 app,这些是 GA4 中增强测量事件的一部分。如果你感兴趣的话,你可以点击链接查看剩下的活动。对于 web,当您在 GA 上启用增强的电子商务时,会跟踪事件,不需要额外的开发。

同样,对于应用程序——包括 Android 和 iOS——以及 Firebase。

当你将 Google Analytics for Firebase SDK 添加到你的应用程序中时,有许多不同的事件开始自动收集——包括你的 Android 和 iOS 应用程序的*screen_view*事件。

理解你在测量什么

数据面试的时候经常能碰到。虽然我不打算把重点放在漏斗和潜在用户旅程来演示代码,但每一个现实生活中的分析都应该从 理解您正在测量的东西 开始。通常,您对以下内容感兴趣:

  • 你为什么要进行分析,以及将如何使用它
  • 行动发生在漏斗或用户旅程中的什么地方
  • 了解测量前后发生的事情是很有用的,例如,如果它是漏斗中的一个步骤,那么测量点的入口和出口在哪里

你需要这些点,不仅是为了更好地理解你的分析,也是为了解决问题和当场发现奇怪的结论。

谷歌商品商店的片段:主页。https://shop.googlemerchandisestore.com/。作者图片

“立即购买”将带您进入产品列表页面(PLP)。

谷歌商品商店 PLP 的片段。https://shop.googlemerchandisestore.com/Super+G?tpt = theme 1 _ en&sortci =最新的+desc 。作者图片

Google 商品商店中产品详细信息页面的片段。https://shop . Google merchandise store . com/Google+design/Apparel/Super+G+ultra light+运动衫。作者图片

屏幕视图的数据模型

一旦您知道了事件在哪里以及如何被触发,数据集就更有意义了。

基于事件的数据的基础

BigQuery 中的 GA4 数据是基于事件的。在 GA4 中,Google 取消了会话和跟踪会话的概念,转而支持基于用户和事件的指标。如果您曾经在 BQ 中使用过 Universal Analytics 数据集,您会注意到不同之处。这在很多层面上都是有意义的,因为它让你思考跨会话、站内和站外的用户行为,这种思维方式本身就是有价值的。

在大多数情况下,BQ 中的事件会有参数。那些位于公共数据集表中的event_params嵌套字段下的。

BigQuery 公共数据集片段:事件表。作者图片

展开event_params,参数有 key 和 parameters,可以是 3 种类型:string、integer、float (+double)。如果您的应用程序试图发送类似数组的东西,您可以考虑使用 items array 。它有一个具体的实现,感兴趣的可以去看看。

在我们的例子中,感兴趣的事件是page_view。感兴趣的参数是page_title。这里有一个简单的查询来查看一个page_title被查看的频率。

来源:https://gist . github . com/Khun reus/8c2e 10463 EC 6 bb 8 b 18 e 17 DC 558 a 6 fdba。按作者

查询的最佳结果:

Google 商品商店上点击量最高的屏幕片段——基于上面的查询。作者图片

在电子商务和增长黑客中,你通常对漏斗的起点、终点以及其间发生的事情感兴趣。你可以优化的漏斗的一端是购买。或者,也许你公司的目标之一是提高简讯订阅率,有一个页面可以实现这一目标——找出它的名称并包含在你的分析中——类似于我将在下面重点介绍的购买。

现在让我们假设家是用户购物之旅的起点,结账确认页面是用户转换的起点。了解这些,我们可以:

  • 找出用户在这两点之间做什么
  • 他们到达结账确认页面的速度
  • users who reached Checkout Confirmation / Users who landed on Home Page或 1069/37265 1,069/37,265 = 2.87%特定时期的用户转化率计算的近似用户转化率。
  • 我们还可以发现遵循或不遵循特定路径或访问页面的用户组之间的转换差异。例如,使用搜索的用户——可以用标题为Store search results的页面的浏览量来近似——转化率更高吗?对于遇到Page Unavailable的人来说,这种转变看起来如何?

注意页面标题上的 如果您运行上面的查询,您会注意到一些page_title值带有产品名称。对于我们的分析来说,这是相当细粒度的,因此可以通过应用自定义规则对它们进行分组。你可以看看这个链接后面的代码片段,下面的两段解释了操作。

查看page_location键的值,当 string_value 在/上拆分时,这些位置具有至少 5 个元素,第 4 或第 5 个元素(从 1 开始计数)包含类别的名称,例如:配件、服装、品牌、校园+收藏、饮料、电子产品、Google+重新设计、生活方式、nest、new+2015+徽标、笔记本+日志、办公室、商店+by+品牌、小商品、文具、可穿戴设备,最后一个元素包含+。我们可以将属于此类页面位置的页面标题标记为产品详细信息页面(PDP)。这并不能捕捉到所有的情况,但是可以捕捉到大多数的情况。

page_locations最后一个元素不包含+,但第 4 或第 5 个元素包含上述类别,可归类为产品列表页面(PLP)。

您可能需要也可能不需要对公司的数据进行类似的操作,但是,这种转换并不少见。

关于 cte 和子查询的注释。与我共事过的一些伟大的数据工程师强调,用 cte 而不是嵌套子查询来阅读别人的 SQL 代码要容易得多。虽然简单查询的性能通常是相似的,但我更喜欢使用 CTE,因为您可以在一个 CTE 的基础上构建,并且代码仍然易于管理。一个 CTE 可以自连接,用于计算每个 CTE 的minmax以及其他计算,在其他 cte 和其他子查询中重复使用。这可能会影响扫描数据的大小,影响大型查询的性能和成本。下面的代码演示了 cte 的一些优点。

例如,我正在创建一个 CTE base_table,它将包含来自bigquery-public-data.ga4_obfuscated_sample_ecommerce.events表的列、嵌套字段和日期的子集。

来源:https://gist . github . com/Khun reus/05fa 134 e 408 ad 5 BD 6 bdbfac 4c 8 CB 0675。按作者

数据分析

Data Studio 数据探索

您可以使用查询结果中的菜单在 Data Studio 中轻松浏览您的数据。注意数据的大小——Data Studio 并不总是缓存它,在现实生活中您可能会产生成本。将查询结果保存在一个单独的 BQ 表中并在其上创建一个仪表板通常是值得的。

或者,您可以开始在 Data Studio 中创建一个仪表板,并添加一个查询作为数据源——当我知道我不会马上完成可视化并且 temp 表即将过期时,我更喜欢这个选项。

BigQuery 结果片段:数据探索。作者图片

如果您想继续了解,请随意查看这个仪表盘

标记会话

为浏览过潜在重要页面的访问者或用户设置标志是很有用的,比如Checkout Confirmation。您可以直接在 BI 工具中完成,因为您可以更灵活地操作数据。

用结帐确认标记访问。作者图片

您可以使用这些过滤器来突出显示到达旅程中所需点的会话和用户的转换。

转换矩阵片段:前 10 页。作者图片

转移矩阵

主要目的:了解用户流量、主要过渡和退出。

在行中,转换矩阵具有引用的“当前”页,或在列中对数据透视表的行的page_title_adjusted——下一页,或next_page。这些值是从行到列的转换次数。从那里,你可以说,例如,如果一个用户登陆了一个 PLP 或商店搜索结果页面,他们下一步可能会去哪里。

在上面的表格中,显示了前 10 页从行到列的转换次数。其他选项是查看_session_id——一个user_pseudo_idvisitID的串联,以及用户的不同user_pseudo_id的计数。在上面,我使用Record Count的计数作为一个值字段(Metric字段)。在这种情况下,值显示有多少事件从一行中的一页转换到一列中的一页。

例如,有从 PLP 到空页面的40,697转换,这是从网站的退出,因为大多数页面都被正确跟踪。这代表 PLP 的总转换数的40,697/140,608=29% ——根据行总数计算得出。从 PLP 浏览到 PLP 62,533/140,608=44%的用户比例较高,从 PLP 浏览到 PDP 21,633/140,608=15%的用户比例较低,这意味着用户在浏览产品列表页面时,不会表现出对点击 PLP 项目的浓厚兴趣。如果是正规的电商店铺,那就太惊人了。

在上面的基础上,在转换矩阵中,虽然有转换的绝对值是好的,但您也想了解该转换与来自page_title_adjusted的总转换相比如何。这在 Tableau、Google Sheets 或 Excel 中的数据透视表或 Python 中很容易做到,但需要在 DataStudio 中做一些工作。这里有几篇关于使用数据混合的好博客文章和这里的和使用查询参数的好博客文章——都是同一作者写的。

这里大概是你能得到的。

Data Studio 代码段:% 1 从一页过渡到下一页。作者图片

信号搜寻——什么行动可能导致积极的结果

主要目的:围绕用户行为和要转换的信号制定假设。

最好能有一点预感,哪些功能应该与业务的积极成果相关联。在电子商务中,我们通常从思考更高的转化率开始。信号可以是,例如,愿望清单上的访问、购物袋、搜索屏幕、关于支付方法的信息等。

在这个例子中,让我们挑选出几个候选人:

  • Store search results
  • The Google Merchandise Store — Log In
  • Frequently Asked Questions

您还可以注意与转换的负面关联,例如,通过查看以下页面

  • Page Unavailable

这些也可能是没有搜索结果的页面,各种错误页面等等。

要标记会话或用户,您可以使用类似于上面第一个查询片段中第 108 行的pages_on_a_visit的聚合。休息可以按照上面的Flagging Sessions部分来做。您可以将这些控件添加到过渡矩阵中,以查看过渡是如何变化的。

如果结帐页面上的page_title被正确跟踪,这将是一个胜利,它将使转换更加容易。如果不是这样,可以探索screen_view之外的事件,比如ecommerce_purchase或者purchasecheckout_progress等。在谷歌商品商店的情况下,让我们假设上面标记的Checkout Confirmation页面可以作为一个很好的代理。

基于页面视图的会话转换率(CR)定义。作者图片

基于页面浏览量的用户转化率(UCR)定义。作者图片

为了可视化与可能的转换信号的连接,我们希望有一个带有分解维度的图表,如 Data Studio 中的折线图。

分析潜在信号时,至少有两点值得注意:

  • 有多少用户这样做——你可以看到是否有足够多的用户使用这个页面或某个功能,以及是否有潜在的改进;
  • 相对于那些没有做你想让他们做的事情的用户,他们的转化看起来如何——所以你可以假设让更多的用户参与旅程的这一部分是否会对业务产生积极的影响,或者在这种情况下对转化产生积极的影响。

了解这两点将有助于你形成假设,从观察开始,比如“做 X 的用户比不做 X 的用户转化率高 2 倍。也就是说,做 X 的用户只占 Y%。我们可以分析这些用户的行为并试图吸引更多类似的用户使用我们的产品,或者引导更多的用户做 X。如何测试?”

例如,这是使用了The Google Merchandise Store — Log In的用户和没有使用的用户之间的对比。

信号搜索:查看登录页面及其每日 CR 的用户。作者图片

从上面可以看出,只有 3.4-7.55%的用户访问了登录页面,但是他们的用户转化率是 7.5-30%,与转化率低于 0.5%的不访问该页面的用户相比,这种差异相当明显

会是什么呢?

在谷歌商品商店,你必须登录才能继续购买,所以它解释了上面的图表。然而,在一般的电子商务案例中,你可能想知道是什么让这些用户与众不同,他们在应用上还做什么,他们来自哪里。从另一个角度来看,并不是所有登录的用户都会转化,你如何帮助他们联系他们可能感兴趣的产品?

然而,这并不意味着你必须让每个用户登录才能购买。这可能对一些企业有用,但很可能会降低转化率,如果不是成为转化率杀手的话。

让我们看另一个例子,即Store search results页面。这是一个更公平的比较,因为虽然用户必须登录才能转换,但他们不必进行搜索。

信号搜索:查看搜索页面的用户及其每日 CR。作者图片

3.97–5.73%的用户在谷歌商品商店上查看了搜索页面,我们可以用它作为搜索的代理。给定转换,我们可以提出一个假设,这个动作特别表明用户更有可能转换。可能是搜索返回相关结果,用户发现这很方便,或者简单地说,使用搜索的用户如果找到产品就会表现出购买的意图,因为他们或者知道他们想要什么,或者打算找到与他们想要的产品相似的产品。可以肯定的是,这些因素将以不同的比例混合,为个人用户制作独特的配方。

在陈述假设时需要小心——那些不是事实,必须放在上下文中理解。随着越来越多的用户采用该特性,这种差异可能会缩小或消失,因此任何开发的特性都必须经过测试。

你会发现进入Product Detail Pages的用户也有类似的信号。当然,我们必须转到 PDP 来将产品添加到购物车,这使得该页面类似于日志以防万一。然而,这也可能因业务而异,因此为了举例,值得研究一下。

与我们上面看到的其他潜在信号相比,相当多的用户会访问产品详情页面:大约 22-26%。他们的转换率波动很大,但在 1 月下半月达到 6%,所以这可能是一个有趣的信号。另请注意,一月的下半月似乎有更高的会话和用户转换率,这可能与业务的季节性有关,也是深入探讨的一个有趣点。

没有进入 PDP 的用户,假设所有页面都被正确跟踪,他们的购物车中可能已经有商品了。

信号搜索:查看产品详细信息页面及其每日 CR 的用户。作者图片

另一方面,可能有些页面你不想让你的用户看到,因为它们与用户体验有负面关系。其中一个例子就是Page Unavailable

0.3-1.3%的用户点击了页面,但是看看转化率是如何被拉平在 0%的。对于这些“负面”案例,不仅要看转化的短期指标,更要深入了解这种负面体验如何长期影响用户保留率、LTV 等。

反向信号搜索:查看登陆“页面不可用”的用户及其每日 CR。作者图片

电力用户

主要目的:了解用户动态,他们的行为,监控用户质量。

在最初的陈楚翔博客文章中,超级用户曲线描绘了在特定的时间段内,例如 28 天、1 个月、1 周等,活跃的用户数量或使用这些用户活跃的天数。作为这一思想的发展,您可以绘制出在特定时期内用户数量与操作执行次数的关系图。

上面的主查询需要对组用户稍加修改。

来源:https://gist . github . com/Khun reus/399 baa 529 c 6d 02 AC 09 bb 3 Abd 0 bee 008 a。按作者

下面是 Data Studio 为观看过PDP的用户进行的概念演示。2021 年 1 月,约 50%的用户观看了 2 个 PDP。对于一个正规的电商来说,曲线应该更多的向右偏移。随着业务致力于吸引更高质量的用户(这些用户实际上对产品足够感兴趣,足以看到其细节)或引导更多用户使用 PDP,曲线应该开始向右移动,并可以作为诊断工具。

超级用户曲线:有产品详情页面访问量的用户。作者图片

几乎 70%登陆搜索结果页面的用户只登陆过一次。是不是意味着他们找到了想要的东西?他们后来到底做了什么?

超级用户曲线:有搜索的用户。作者图片

超级用户分析,结合信号搜索练习,可以用作构建电子商务框架的垫脚石,以评估积极成果的概率,并坚持我们在这里的例子——转化率。简单来说就是“转化倾向”。

用户通常在哪个步骤…?

主要用途:诊断用户到达旅程中某一点的速度。

需要很长时间才能完成的旅程可能是主动浏览的标志,也可能是用户迷路的标志。在没有用户输入的情况下,区分这两者可能很难,但在某些情况下,可能没有那么难。

例如,如果用户转换的唯一方式是浏览产品列表页面,而用户在 70%的情况下最早看到的是屏幕排名第 10。一定有什么不对劲。更常见的情况是用户从主页直接或通过菜单进入 PLP,这样他们就不会被大量的横幅、不清楚的导航、弹出窗口等阻挡。

在 SQL 中,您可以获得与主查询的第 115 行类似的屏幕等级(也链接到这里)。

下面是它如何寻找一些网页。

几乎 70%浏览过 PLPs 的用户都是在第一页浏览的。如果你把这个放在流量来源上,这些用户可能是通过搜索或付费链接直接来自搜索引擎的。

谷歌商品商店 PLP 的最短用户路径。作者图片

然而,最早登陆搜索屏幕的用户是在 3-5 号屏幕附近,这是完全有道理的。它在用户旅程中并不太远,因为使用搜索的用户倾向于以更高的速度转化,我们可以假设搜索栏很容易找到,用户更喜欢使用它而不是浏览导航栏。

第一个搜索页面的路径长度。作者图片

先不说其他的,看看用户需要多长时间才能达到一个转化点总是非常有趣的。在这里,我们有一种更均匀的分布方式,大多数用户,大约 5%在第 16 屏结账。

结账页面的路径长度。作者图片

最后

这是一个谷歌商品商店的例子,在我看来,如果这是一个常规的电子商务,用户行为看起来不健康。对于一家核心业务不是在网上销售商品的公司来说,这可能已经足够好了。

您可以在您的角色中使用类似的分析来了解用户在没有第三方工具的情况下的行为,或者在第三方工具的基础上帮助您了解用户行为。

有很多东西需要解开,特别是当涉及到“可操作的见解”时,但我相信一旦你将数据切片,你至少会了解到一些关于你的用户的有价值的东西。您可以在分析的基础上对用户级别以及流量和交易数据进行分层,以进行更深入的分析,并与营销、产品、CRM 和其他职能部门合作,创建漂亮的产品和渠道,将用户与您提供的真正价值联系起来。

也许你有最喜欢的技术,或者讨厌我提到的一些技术。分享你的想法!

资源

https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag#implementation https://www.alisa-in.tech/post/2019-10-02-ctes/

希望将时间序列注释无缝集成到 ML 工作流中吗?不要再看了

原文:https://towardsdatascience.com/looking-to-seamlessly-integrate-your-time-series-annotation-in-your-ml-workflow-look-no-further-d7721c8f59e0

在 Amazon SageMaker 上运行 Label Studio,将标签无缝集成到您的机器学习工作流程中

维克多·福加斯Unsplash 上拍摄的照片

我在工业用例的时间序列异常检测方面做了很多工作,大多数时候我依赖于无监督的方法。然而,半监督方法可以增加有价值的增值。在其他情况下,您可能还希望确认无监督的模型输出,因此必须使用标注工具来轻松集成到您的工作流中。

这就是 标签工作室 的用武之地!

前段时间,我的一个同事( Sofian ,你可以 关注此处 )写了下面这篇文章,讲解如何在 亚马逊 SageMaker 上部署 Label Studio:

https://medium.com/geekculture/labeling-data-with-label-studio-on-sagemaker-e4b2d1b562f7

我过去一直在玩弄这个开源包来标记时间序列数据:我认为这是展示我如何将这个标记工具集成到我的机器学习工作流中的完美时机。

在本文中,我将向您展示我运行的笔记本,以便在我的 SageMaker 环境中自动部署 Label Studio 实例。然后我将展示如何自动配置我的注释环境来处理我想要注释的时间序列数据的结构。

我鼓励你跟随这篇博文,浏览 GitHub,获得 这一系列的 Jupyter 笔记本 。因为目标是在 Amazon SageMaker 上部署 Label Studio,所以您需要有一个 AWS 帐户。然后您可以创建一个 SageMaker notebook 实例(如果您有一个新帐户,可以使用 t3.medium 类型从免费层中获益)。从那里,克隆这个 GitHub 库:

git clone https://github.com/aws-samples/amazon-lookout-for-equipment.git

导航到apps/annotation-label-studio/文件夹并打开1-initialize-label-studio.ipynb笔记本。

在我们开始一步一步地从头配置您自己的环境之前,让我们先来概述一下我们将组装什么…

技术概述

在本文中,您将在 SageMaker 笔记本实例中部署 Label Studio 的 Docker 映像。然后将 Label Studio 连接到亚马逊 S3 存储桶,时间序列数据将存储在那里。

Label Studio 是一个灵活的数据标注工具,可以用来标注每一种数据类型:文本、图像、表格数据或者时间序列数据。在本文中,我们将通过编程配置一个自定义用户界面来标记时间序列数据,以便进行异常检测。

Amazon sage maker是一项托管的机器学习服务,可以帮助您通过完全托管的基础设施、工具和工作流,为任何用例构建、训练和部署机器学习模型。在本文中,我们将使用 SageMaker Notebook Instances 提供的托管 JupyterLab 体验。

安装 Label Studio

首先,我们将下载 Label Studio docker 映像,并将其部署到我们的笔记本环境中。为此,我们需要配置一些参数:

为 Label Studio 安装生成 shell 脚本(由作者编写代码)

前面的代码生成了一个 shell 脚本,它将运行 Label Studio 的 dockerized 版本。这个实例将配置一个初始用户,您可以通过更改usernamepasswordtoken来自定义这个用户。注意username 必须遵循电子邮件地址格式。否则,在启动 Label Studio 实例时不会创建用户。如果您在此阶段没有创建用户,您将有机会在登录应用程序时创建一个。

token当然可以随机产生。例如,您可以为此使用以下代码:

生成唯一令牌(由作者编码)

这将生成一个类似于这个的令牌:2edfe403f2f326e810b9553f8f5423bf04437341

使用下面的方法定义了get_notebook_name()方法:这用于生成 Label Studio 实例的 URL。

提取笔记本实例的当前名称(按作者编码)

一旦生成了 shell 脚本,就可以通过运行!source ./label-studio.sh在 Jupyter 的一个单元中运行它。第一次,它将为 Label Studio 下载 docker 映像。然后它将使用您在上面定义的参数运行它。几秒钟后,您应该会看到以下消息:

Django version 3.1.14, using settings 'core.settings.label_studio'
Starting development server at http://0.0.0.0:8080/
Quit the server with CONTROL-C.

这意味着您的 Label Studio 实例已经启动并正在运行!

是时候对其进行配置以适应您想要标记的时间序列数据集了…

准备示例数据集

如果你正在用配套的 GitHub repo 阅读这篇文章,你现在可以打开第二个 Jupyter 笔记本(2-configure-label-studio.ipynb ) ,同时让另一个笔记本运行。您应该会在浏览器中的 JupyterLab 选项卡名称旁边看到一个沙漏图标。这是一个进程正在实际运行的提示(在本例中,是 Label Studio 实例)。

我在 repo 中放了一个合成的时间序列数据集:如果你对这个数据集是如何创建的感兴趣,请随意查看这篇文章:

如果你已经准备好了,你当然可以使用你自己的时间序列数据集!您可以将数据集本地存储在实例中,并让 Label Studio 从这里访问它。

然而,如果你是一个 AWS 用户,大多数时候你可能已经把你的数据存储在亚马逊 S3 桶里了。然后 Label Studio 必须从那里访问你的数据,默认情况下没有被授权。要启用此访问,您需要为您的 S3 时段启用跨来源资源共享(CORS)。CORS 为在一个域中加载的客户端 web 应用程序(在我们的例子中,我们的 Label Studio 实例运行在 SageMaker notebook 实例中)定义了一种与不同域中的资源(您的数据集存储在亚马逊 S3)进行交互的方式。为此,您可以查看 CORS 文档 ,并使用以下 JSON 文档来配置您的访问权限。您需要更新下面的AllowedOrigins参数:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "https://**<<notebook_name>>**.notebook.**<<current_region>>**.sagemaker.aws"
        ],
        "ExposeHeaders": [
            "x-amz-server-side-encryption",
            "x-amz-request-id",
            "x-amz-id-2"
        ],
        "MaxAgeSeconds": 3000
    }
]

是时候配置您的标注模板以匹配您想要标注的数据了…

配置 Label Studio 实例

让我们假设您现在已经准备好一个数据集,并将其加载到 Panda 数据帧中。下一步是配置注释模板。Label Studio 附带了几个现有的模板。然而,您的模板将取决于您的文件中有多少时间序列(或频道)。构建适应数据集的自定义模板是一个两步过程。首先,我们构建一个通道列表,多变量时间序列数据集中的每个字段对应一个通道:

在 Label Studio 中为时序注释模板生成频道列表(由作者编写代码)

给定的通道将采用以下格式:

<Channel
    column="signal_00" 
    legend="signal_00" 
    strokeColor="#1f77b4" 
    displayFormat=",.1f" 
/>

您可以自定义用于绘制时间序列的通道名称和颜色。

然后,您使用这个channel_fields来生成注释模板:

以编程方式为 Label Studio 生成注释模板(由作者编写代码)

我们的模板已经准备好,现在我们将:

  • 创建新的注释项目
  • 配置存储配置
  • 登录我们的 Label Studio 实例并创建一些标签
  • 收集结果,以便在您的机器学习管道中进一步使用

创建新的注释项目

我们将使用 Label Studio API 与 Label Studio 实例进行交互。创建项目需要使用创建项目 API:

创建新的 Label Studio 项目(由作者编写代码)

运行这段代码后,将在 Label Studio 环境中为您的用户(由上面的token变量标识)创建一个新项目。我们现在可以将 Label Studio 连接到您的数据存储。

配置存储以使用亚马逊 S3 作为数据源

使用 Label Studio 的 S3 配置 API,您可以知道在哪里可以找到要标注的时间序列数据:

将 Label Studio 与作为数据源的亚马逊 S3 连接起来(由作者编写代码)

要配置这个数据位置,您需要知道您的时间序列数据将位于亚马逊 S3 上的bucketprefix。但是,您还需要将 AWS 凭证传递给 Label Studio。可以使用以下代码从当前的 SageMaker 环境中收集这些凭证:

提取当前 AWS 凭证(按作者编码)

一旦创建了项目,就可以对其进行同步:同步时,Label Studio 会在配置的数据源中搜索有效文件(在我们的示例中为 CSV ),并将它们添加到项目中,以便您可以开始标注工作。触发同步非常简单,只需要在项目创建 API 调用之后获得项目 ID:

将 Label Studio 项目与您的数据源同步(由作者编写代码)

我们的项目现在已经准备好了,在同步之后应该已经添加了一些注释任务…

创建一些标签

现在,我们将通过在浏览器的新选项卡中打开此链接来访问 Label Studio 实例:

https://{ notebook _ name }. notebook .{ region }. sagemaker . AWS/proxy/8080/

您必须用自己的参数替换 URL 中粗体显示的变量。您应该会看到登录页面,并且可以使用您在第一个笔记本中配置的凭据登录:

标签工作室登录页面(图片由作者提供)

登录后,您应该会看到一个已经填充并同步的项目(我们可以在项目标题下看到一个 0/1 任务,这意味着还有一个未完成的注释任务):

一个标签工作室项目准备就绪(图片由作者提供)

单击该项目图标,调出要注释的时间序列。每个时间序列数据集将显示为一个单独的任务来标记:

要注释的时间序列任务(图片由作者提供)

向下滚动到右侧时间序列视图的底部,使用概览滑块缩短时间段,直到时间序列图出现。然后您可以开始标注您的数据(如果您想了解更多关于实际标注过程的信息,请访问 标注工作室网站 ):

注释过程正在进行中(图片由作者提供)

完成一些标签后,向上滚动并点击Submit按钮。标注保存在 Label Studio 的本地数据库中(您也可以在亚马逊 S3 上配置一个目标位置)。您现在可以收集您的结果了!

收集注释

使用以下 API 调用从之前的标记作业中获取标签,并将其保存为 CSV 格式,以备异常检测机器学习服务使用,如Amazon Lookout for Equipment:

提取注释结果(作者代码)

这就是了!恭喜您读到这里,您现在知道如何将标签工作流程无缝集成到您的时间序列异常检测流程中了!

结论

在本文中,您学习了如何…

  • 在本地 SageMaker 笔记本实例上部署一个带有 Docker 的 Label Studio 实例。
  • 创建一个用户、一个项目,并对其进行配置,以从亚马逊 S3 存储桶中访问您的时间序列数据。
  • 从您的亚马逊 S3 桶授权 CORS,允许 Label Studio 从那里直接访问您的时间序列数据,而无需在您的实例中本地复制它。
  • 标注工作完成后,收集注记结果。

我希望你觉得这篇文章很有见地:如果你不想错过我即将发布的帖子,请随时在这里给我留下评论,并不要犹豫订阅我的 中型电子邮件订阅源 !想支持我和以后的工作?通过我的推荐链接加入 Medium :

https://michoara.medium.com/membership

损失函数及其在神经网络中的应用

原文:https://towardsdatascience.com/loss-functions-and-their-use-in-neural-networks-a470e703f1e9

损失函数及其实现概述

克里斯里德在 Unsplash 上的照片

损失函数是神经网络最重要的方面之一,因为它们(以及优化函数)直接负责将模型拟合到给定的训练数据。

本文将深入探讨如何在神经网络中使用损失函数,不同类型的损失函数,在 TensorFlow 中编写自定义损失函数,以及处理图像和视频训练数据的损失函数的实际实现——计算机视觉中使用的主要数据类型,这是我感兴趣和关注的主题。

背景资料

首先,快速回顾神经网络的基本原理及其工作原理。

图片来源:维基共享资源

神经网络是一套算法,旨在识别一组给定训练数据中的趋势/关系。这些算法基于人类神经元处理信息的方式。

该等式表示神经网络如何处理每一层的输入数据,并最终产生预测的输出值。

图片来源:作者

为了训练——模型映射训练数据和输出之间的关系的过程——神经网络更新其超参数、权重、 wT 和偏差、 b、以满足上面的等式。

每个训练输入在一个称为正向传播的过程中被加载到神经网络中。一旦模型产生了一个输出,这个预测输出在一个称为反向传播的过程中与给定的目标输出进行比较——然后调整模型的超参数,使其现在输出一个更接近目标输出的结果。

这就是损失函数出现的地方。

图片来源:维基共享资源

损失函数概述

损失函数比较目标和预测输出值的函数;衡量神经网络对训练数据的建模程度。训练时,我们的目标是尽量减少预测输出和目标输出之间的这种损失。

调整超参数以最小化平均损失——我们找到最小化 J 值(平均损失)的权重 wT 和偏差 b

图片来源:作者

我们可以认为这类似于统计学中的残差,它测量实际的 y 值与回归线(预测值)的距离,目标是最小化净距离。

图片来源:维基共享资源

如何在 TensorFlow 中实现损失函数

对于本文,我们将使用谷歌的 TensorFlow 库来实现不同的损失函数——很容易演示损失函数是如何在模型中使用的。

在 TensorFlow 中,神经网络使用的损失函数被指定为 model.compile()中的参数,model . compile()是训练神经网络的最终方法。

model.compile(loss='mse', optimizer='sgd')

损失函数可以作为字符串(如上所示)或函数对象(从 TensorFlow 导入或编写为自定义损失函数)输入,我们将在后面讨论。

from tensorflow.keras.losses import mean_squared_error
model.compiile(loss=mean_squared_error, optimizer='sgd')

张量流中的所有损失函数具有相似的结构:

def loss_function (y_true, y_pred): 
    return losses

它必须以这种方式格式化,因为 model.compile()方法只需要 loss 属性的两个输入参数。

损失函数的类型

在监督学习中,有两种主要类型的损失函数,它们与两种主要类型的神经网络相关:回归和分类损失函数

  1. 回归损失函数——用于回归神经网络;给定一个输入值,模型预测相应的输出值(而不是预先选择的标签);《出埃及记》均方误差,平均绝对误差
  2. 分类损失函数——用于分类神经网络;给定一个输入,神经网络产生一个属于各种预设类别的输入概率向量,然后可以选择具有最高归属概率的类别;《出埃及记》二元交叉熵,分类交叉熵

均方误差

MSE 是最受欢迎的损失函数之一,它可以找到目标输出和预测输出之间的平均方差

图片来源:作者

这个函数有许多特性,使它特别适合于计算损失。差值是平方的,这意味着预测值是高于还是低于目标值并不重要;但是,误差较大的值会受到惩罚。MSE 也是一个凸函数(如上图所示),具有明确定义的全局最小值——这允许我们更容易地利用梯度下降优化来设置权重值。

下面是 TensorFlow 中的一个标准实现——也内置在 TensorFlow 库中。

def mse (y_true, y_pred): 
    return tf.square (y_true - y_pred)

然而,这种损失函数的一个缺点是它对异常值非常敏感;如果预测值明显大于或小于其目标值,这将大大增加损失。

图片来源:维基共享资源

平均绝对误差

MAE 找出目标和预测输出之间的绝对差值的平均值。

图片来源:作者

在某些情况下,这个损失函数被用作 MSE 的替代函数。如前所述,MSE 对异常值非常敏感,这会显著影响损失,因为距离是平方的。MAE 用于训练数据有大量异常值的情况,以减轻这种情况。

下面是 TensorFlow 中的一个标准实现——也内置在 TensorFlow 库中。

def mae (y_true, y_pred): 
    return tf.abs(y_true - y_pred)

它也有一些缺点;当平均距离接近 0 时,梯度下降优化将不起作用,因为函数在 0 处的导数是未定义的(这将导致错误,因为不可能除以 0)。

因此,开发了一种称为 Huber 损失的损失函数,它具有 MSE 和 MAE 的优点。

图片来源:作者

如果实际值和预测值之间的绝对差值小于或等于阈值𝛿,则应用 MSE。否则,如果误差足够大,则应用 MAE。

图片来源:维基共享资源

这是 TensorFlow 的实现——这涉及到使用一个包装器函数来利用阈值变量,我们稍后会对此进行讨论。

def huber_loss_with_threshold (t = 𝛿): 
    def huber_loss (y_true, y_pred): 
        error = y_true - y_pred 
        within_threshold = tf.abs(error) <= t
        small_error = tf.square(error)
        large_error = t * (tf.abs(error) - (0.5*t))
        if within_threshold: 
                return small_error
        else: 
                return large_error
    return huber_loss

二元交叉熵/对数损失

这是二元分类模型中使用的损失函数,其中模型接受一个输入,并将其分类到两个预设类别中的一个。

图片来源:作者

分类神经网络的工作原理是输出一个概率向量——给定输入符合每个预设类别的概率;然后选择概率最高的类别作为最终输出。

在二进制分类中,y 只有两个可能的实际值— 0 或 1。因此,为了准确地确定实际值和预测值之间的损失,需要将实际值(0 或 1)与输入符合该类别的概率进行比较( p(i) =该类别为 1 的概率;1 — p(i) =类别为 0 的概率)

这是 TensorFlow 实现。

def **log_loss** (y_true, y_pred): 
    y_pred = tf.clip_by_value(y_pred, le-7, 1 - le-7)
    error = y_true * tf.log(y_pred + 1e-7) (1-y_true) * tf.log(1-y_pred + 1e-7)
    return -error

分类交叉熵损失

在类的数量大于两个的情况下,我们利用分类交叉熵——这遵循与二进制交叉熵非常相似的过程。

图片来源:作者

二元交叉熵是分类交叉熵的一个特例,其中M= 2——类别的数量是 2。

自定义损失函数

如前所述,在编写神经网络时,可以从 the tf.keras.losses 模块中导入损失函数作为函数对象。该模块包含以下内置损失函数:

图片来源:作者

然而,可能存在这些传统/主要损失函数可能不足够的情况。例如,如果您的训练数据中有太多噪音(异常值、错误的属性值等)。)——这不能用数据预处理来补偿——或者在无监督学习中使用(我们将在后面讨论)。在这些情况下,您可以编写自定义损失函数来满足您的特定条件。

def **custom_loss_function** (y_true, y_pred): 
    return losses

编写自定义损失函数非常简单;唯一的要求是损失函数必须只接受两个参数:y_pred(预测输出)和 y_true(实际输出)。

这些的一些例子是 3 个定制损失函数,在变分自动编码器(VAE)模型的情况下,来自 Soon Yau Cheong 的 TensorFlow 的手动图像生成。

def vae_kl_loss(y_true, y_pred):
    kl_loss =  - 0.5 * tf.reduce_mean(1 + vae.logvar -    tf.square(vae.mean) - tf.exp(vae.logvar))
    return kl_lossdef vae_rc_loss(y_true, y_pred):
    #rc_loss = tf.keras.losses.binary_crossentropy(y_true, y_pred)
    rc_loss = tf.keras.losses.MSE(y_true, y_pred)
    return rc_lossdef vae_loss(y_true, y_pred):
    kl_loss = vae_kl_loss(y_true, y_pred)
    rc_loss = vae_rc_loss(y_true, y_pred)
    kl_weight_const = 0.01
    return kl_weight_const*kl_loss + rc_loss

根据损失函数的数学计算,您可能需要添加额外的参数,例如休伯损失中的阈值𝛿(如上);为此,您必须包含一个包装函数,因为 TF 不允许在您的损失函数中有超过 2 个参数。

def custom_loss_with_threshold (threshold = 1): 
    def custom_loss (y_true, y_pred): 
        pass #Implement loss function - can call the threshold variable
    return custom_loss

图像处理中的实现

克里斯多夫·高尔Unsplash 上拍摄的照片

让我们看看损失函数的一些实际实现。具体来说,我们将研究在各种用例中如何使用损失函数来处理图像数据。

图像分类

计算机视觉最基本的方面之一是图像分类——能够将图像分配到两个或更多预选标签中的一个;这允许用户识别物体、书写、人等。在图像内(在图像分类中,图像通常只有一个主题)。

图像分类中最常用的损失函数是交叉熵损失/对数损失(二进制用于 2 个类别之间的分类,稀疏分类用于 3 个或更多类别),其中模型输出输入图像属于每个预设类别的概率向量。然后将该输出与实际输出进行比较,实际输出由大小相等的向量表示,其中正确类别的概率为 1,所有其他类别的概率为 0。

它的基本实现可以直接从 TensorFlow 库中导入,不需要任何进一步的定制或修改。下面是 IBM 的一个开源深度卷积神经网络(CNN)的摘录,它对文档图像(身份证、申请表等)进行分类。).

model**.**add(Dense(5, activation**=**'sigmoid'))
model**.**summary()model**.**compile(optimizer='adam', loss**=**'categorical_crossentropy',
    metrics**=**['accuracy'])

目前正在研究开发新的(自定义)损失函数来优化多类分类。下面是杜克大学研究人员提出的损失函数的摘录,该函数通过查看错误结果中的模式来扩展分类交叉熵损失,以加快学习过程。

def matrix_based_crossentropy (output, target, matrixA, from_logits = False):
    Loss = 0
    ColumnVector = np.matul(matrixA, target)
    for i, y in enumerate (output):
        Loss -= (target[i]*math.log(output[i],2))
        Loss += ColumnVector[i]*exponential(output[i])
        Loss -= (target[i]*exponential(output[i]))
    newMatrix = updateMatrix(matrixA, target, output, 4)
    return [Loss, newMatrix]

图象生成

图像生成是一个过程,通过该过程,神经网络根据用户的指定创建图像(从现有库中)。

在整篇文章中,我们主要讨论了监督学习中损失函数的使用——我们已经清楚地标记了输入、 x 和输出、 y ,并且该模型应该确定这两个变量之间的关系。

图像生成是无监督学习的一种应用,其中需要模型来分析和查找未标记输入数据集中的模式。损失函数的基本原理仍然成立;无监督学习中损失函数的目标是确定输入示例和假设(输入示例本身的模型近似值)之间的差异。

例如,这个方程模拟 MSE 将如何被实现用于无监督学习,其中 h(x) 是假设函数。

图片来源:作者

下面是一个对比语言-图像预训练(CLIP)扩散模型的摘录——它通过文本描述生成艺术(图像)——以及一些图像样本。

if args.init_weight:
    result.append(F.mse_loss(z, z_orig) * args.init_weight / 2)lossAll,img = ascend_txt()if i % args.display_freq == 0:
    checkin(i, lossAll)loss = sum(lossAll)
loss.backward()

图片来源:作者

在变分自动编码器(VAE)模型的情况下,在我们的自定义损失函数部分中显示了在图像生成中使用损失函数的另一个例子。

包扎

在本文中,我们讨论了 1)损失函数如何工作,2)它们如何在神经网络中使用,3)适合特定神经网络的不同类型的损失函数,4) 2 个特定的损失函数及其使用案例,5)编写自定义损失函数,以及 6)图像处理损失函数的实际实现。

谢谢你看我的文章!你可以通过我的 LinkedIn 联系我,或者给我发邮件到 vishal.yathish@gmail.com

Python 中社区发现的 Louvain 算法

原文:https://towardsdatascience.com/louvains-algorithm-for-community-detection-in-python-95ff7f675306

在 Python 中应用 Louvain 算法进行社区发现

图片由 UnsplashEthan Unzicker 拍摄

本文将涵盖社区检测和 Louvain 算法背后的基本直觉。它还将展示如何使用 NetworkX 和 Python-Louvaine 模块在您选择的网络中实现 Louvain 算法。以下是文章的结构:

目录

  • 什么是社区检测?
  • 社区检测与聚类
  • 什么是鲁汶算法?
    -模块化
    -卢万算法
  • 问题陈述
    -要求
  • 生成网络
  • 应用卢万算法
  • 可视化社区
  • 结束语
  • 资源

什么是社区检测?

在图论中,如果您能够根据节点的边密度对节点(可能有重叠的节点)进行分组,则网络具有社区结构。这将意味着原始网络 G1 可以被自然地分成多个子图/社区,其中社区内的边连通性将非常密集。重叠社区也是允许的,因此在形成的社区中可以有重叠节点。这意味着独立社区中的节点具有稀疏数量的边。

更一般的定义是基于这样的原则,即如果节点对都是相同社区的成员,则它们更有可能被连接,而如果它们不共享社区,则它们不太可能被连接。
【1】——https://en.wikipedia.org/wiki/Community_structure

直观地想到这一点,就有道理了。想想你自己在 Instagram 这样的社交网络中。你可能和许多与你感兴趣的事物相关的不同社区有着密切的联系。你可以关注与朋友、迷因、体育、动漫等相关的账户。这些分类中的每一个都可以被解释为社区,其中你作为一个用户是一个节点,边是通过将你连接到与你有相似兴趣的其他用户而生成的。因此,在你自己的社交网络中,你会有一个非常密集的社区,而与你社区之外的其他人联系很少。

有许多数学公式用于识别给定网络中的社区。这篇文章的重点是特别针对卢万算法,然而还有许多其他算法,如格文-纽曼算法雅克卡指数等。可用于解决社区检测中的问题。

社区检测与聚类

类似于聚类,传统的社区检测方法可以被标记为无监督学习。可以认为社区检测和聚类是同一个东西。在文献中有许多情况下,这些术语可以互换使用。

我发现两者之间的区别非常微妙,社区检测侧重于根据网络结构生成节点组,而聚类侧重于根据与输入数据相关的许多属性生成组。社区检测特别保留在图论和网络分析的领域中,而聚类传统上用于非基于图的应用中。也就是说,在网络上使用 kMeans 等传统的集群技术并不是不可能的(首先需要生成与网络中的节点相关联的嵌入,然后将集群模型应用于这些嵌入)。

什么是鲁汶算法

卢万算法,2008 年由 Vincent Blondel 等人以卢万大学命名。该算法源于他们的论文大型网络中社区的快速展开[3],其中他们引入了一种贪婪方法,该方法将在 O(n*log(n))时间内生成社区,其中 n 是原始网络中的节点数[2]。

模块性

Louvain 的算法旨在优化模块化。模块性是一个介于-0.5 和 1 之间的分数,它表示社区内的边相对于社区外的边的密度[2]。模块化越接近-0.5 表示非模块化集群,越接近 1 表示完全模块化集群。在给定网络的情况下,优化该分数会产生最佳的可能节点组。直观上,你可以把它解释为最大化实际边数和期望边数之差。模块化可由以下公式定义:

计算加权网络模块度的公式。图片取自维基百科 [2]

  • Ai,j表示节点 I 和 j 之间的边
  • m是网络中所有边权重的总和
  • delta 是克罗内克 delta 函数
    -如果 I = j
    delta = 1-否则 delta = 0
  • CiCj是节点的社区
  • KiKj是连接节点 I 和 j 的权重之和

卢万算法

为了最大化模块性,Louvain 的算法有两个迭代阶段。第一阶段将网络中的每个节点分配给自己的社区。然后,它试图通过合并社区来最大化模块化收益。节点 I 可能会与节点 j(I 的邻居)合并,以确定模块性是否会有积极的增加。如果没有模块增加,则节点保持在其当前社区中,否则社区被合并。社区的这种合并可以由以下公式表示:

图片取自维基百科 [2]

其中Sigma_ini正在移入的社区内所有链接的权重之和,Sigma_toti正在移入的社区中所有节点的权重之和,k_ii的加权度,k_i,inii正在移入的社区中其他节点之间的链接的权重之和,m是网络中所有链接的权重之和。
【3】https://en.wikipedia.org/wiki/Louvain_method

这两个阶段都被执行,直到没有可能通过将社区合并在一起而获得模块化收益。

问题陈述

我们要尝试解决的问题非常简单,给定一个网络,我们想要识别该网络中的社区。

要求

Python=3.8.8
numpy=1.20.1
python-louvain=0.16
networkx=2.5
matplotlib=3.3.4

如果你没有安装 networkX 包,这里的是通过命令行安装的库文档。同样,如果你没有 louvain 包,你可以按照安装指南这里【5】。

生成网络

上面的以下函数生成与随机生成的度分布相关联的网络。用户可以指定他们希望关联网络中的节点数量,出于本教程的目的,我选择了 50。以下是我创建的示例网络的网络统计数据。

网络统计。图片由作者提供。

以下是网络外观的直观表示。

随机生成的网络是什么样子的。图片由作者提供。

应用卢万算法

我们可以简单地通过 Python-Louvain 模块应用 Louvain 的算法。你可以在这里【4】找到与我们将要引用的功能相关的更多文档。

这应该以字典的形式返回从 G 中检测到的关联社区。字典的键是节点,值对应于该节点所属的社区。

可视化社区

请注意,这种可视化是可能的,因为我选择了少量的节点(这反过来生成了少量的边)。渲染与大型网络相关的图像对于任何计算机来说都是一项繁重的任务。在网络上运行下一个代码组件之前,请注意。

节点用相应的社区着色的网络。图片由作者提供。

请注意,这里介绍的与管道相关的结果是没有意义的,因为该算法是在随机生成的网络上运行的。

结束语

本文只介绍了与社区检测相关的许多潜在算法中的一种。数学中引入了许多不同的算法来解决与社区检测相关的问题。在各种数据集上学习、实现和测试每个算法的性能将对您自己的开发非常有益。

您可以在下面我创建的 Jupyter 笔记本中,在我的 GitHub 上跟踪与本文相关的代码。

资源

如果你喜欢这篇文章,还有很多关于网络分析、nlp、推荐系统等的文章。我也写过这方面的文章。下面来看看吧。

[## 贝叶斯 A/B 测试解释

towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a)

疫情时代的爱情:约会的概率方法

原文:https://towardsdatascience.com/love-in-the-time-of-pandemic-a-probabilistic-approach-to-dating-ba888880cd12

约会是很难的——但是也许概率论可以在这个季节让约会变得稍微容易些

图片来自 pix abay(dimitrisvesika 1969)

回到 2019 年,我写了一篇关于最优停止理论及其在约会中的应用的文章。

对于充满希望的浪漫主义者来说,这个教训很简单:为了最大化你找到最佳伴侣的机会,你应该去了解但不要把自己托付给你将遇到的第一批 37%的人(你认为)中的任何一个,然后立即满足于第一个比你之前见过的人更好的人。这样做将保证你有 37%的机会找到数学上命中注定的那一个。

从那以后已经过了 3 年,这段时间足够让我明白现实和这些数学公式完全不同。

然而,现实是严酷的。

我大学毕业,为了工作搬到了一个新的城市。让我告诉你,即使在一切似乎都值得抱怨的年龄,从在蓝瓶餐厅的漫长等待到旧金山湾区没有公共交通,以及我为我的小一居室支付的高昂价格,缺乏约会的时间和机会仍然是我最大的抱怨。

然后,疫情热播,突然改变了浪漫的定义,从晚餐的亲密体验,在拥挤的酒吧喝几杯或一个晚安之吻,到尴尬的变焦电话,戴口罩和感染致命疾病的威胁。几乎不可能和一个人约会,更别说 10 个或 37 个了。

图片来自 Pixabay (StockSnap)

更别提旧金山或纽约的约会市场竞争有多激烈了。即使(你认为)你已经找到了那个重要的人,很有可能那个人不在你身边或者没有回报你的感情。

因此,数学似乎与现实脱节,现在怎么办?

我们遵循优秀科学家会做的事情:我们承认我们模型的缺点,放松一些假设,并提出新的算法来更好地反映这一现实。

1.寻找多种方法——最佳 k 方法

让约会变得稍微容易一点的一个方法是问我们自己:最佳匹配真的与“第二好”或“第三好”有那么大的不同吗?毕竟,我们可能如此迷恋爱情,以至于与排名前三或前五的候选人中的任何一个人建立认真的关系,都可能对我们的一生效用产生很小的影响。

所以,让我们说,我们不是在 100 个人中寻找最佳匹配,而是开始寻找前 k 个爱情候选人。我们使用相同的方法:首先,我们探索我们与第一个 r 候选人的兼容性,并在脑海中记下他们中观察到的最高分数。我们姑且称这个阈值为 t 。然后,我们从得分高于 t 的区间 {r + 1,…,n} 中选择每个候选人,直到我们选择了 k 个人或者我们用完了所有候选人。

一个模拟:

让我们也从运行模拟开始,围绕r 的最佳值建立一些直觉。设置相当简单,使用以下 Python 代码:

让我们想象一下当 n = 100 时的结果:

作者创建的图表

我们可以看到 k = 1 把这个变成了我们只挑选最佳人选的经典问题。因此,理想的探索阶段将持续大约 36-37 人。有趣的是,当我们想要选择前 3 名候选人时,我们必须更快地结束我们的探索阶段,只有大约 23 或 24 人,当我们想要广撒网寻找前 10 名候选人时,只有 9 人。

你可能会问,随着我们希望挑选的顶级合作伙伴数量的增加,成功的概率实际上是下降的(当 k= 3 时约为 15%,当 k = 10 时约为 4%),所以这实际上比我们最初的策略更糟糕吗?在最初的策略中,我们保证有 37%的成功率。

  • 这是真的,但问题是,最初的神奇数字 37 只保证你会找到最好的一个,但它没有说他们的可用性。诚然,随着 k 的增加,成功选择顶级 k 伙伴的概率降低,但至少其中一个接受的几率明显更高。毕竟在爱情里,只有一个才算数。

数学实际上是如何工作的?

我们也可以运行一些数学来找到 r

S(n,k,r) 为我们成功的事件,即我们在第一个【r观测值上使用我们的算法找到前kn候选。我们希望最大化成功的概率,即 P(S(n,k,r))**

要做到这一点,我们要用一个小技巧:我们把 P(S(n,k,r)) 分解成小分量的和 P(S(n,k,r,i)) ,其中 S(n,k,r,i) 是我们成功的事件,其中 i 是最后选中的候选。

为了进一步分解 P(S(n,k,r,i)) ,请注意,对于作为最后选择的候选项的 i :

  • 它必须是前 k 候选之一,概率为 k/n
  • 我们能够在 i-r-1 候选中挑选出 k-1 候选:

  • r 人之一是前 i-1 人中的佼佼者,以概率 r/(i-1)

我们的等式变成:

解析地找到 r 来最大化这个等式是极其复杂的,超出了本文的范围。但是,如果你信任我,那么随着 n → ∞ ,最优 r 的逼近:

如果你需要一些说服力,这是我们的模拟和理论结果之间的 r_best 的样子:

作者创建的图表

2.处理拒绝——当你不是对方的另一半时

比方说,我们仍然致力于寻找最好的人,而且只找那个人,但是我们意识到这个人有(相当高的)可能性是不可得的,例如,他们可能对你不感兴趣,他们已经有了一段感情,或者……你恰好处于他们的探索阶段。

所以让我们稍微调整一下问题,让候选人有权拒绝我们的示爱。我们假设,如果给出我们的建议,任何候选人都会以相同的概率回报我们的感受,而与他们的排名和我们交往的其他人的倾向无关。这显然是天真的,但这个假设现在就可以了。

数学是非常混乱的,所以我会救你出来。基本上在这种情况下,策略也是先发掘 r 候选人,其中r = p^(1/(1-p)。例如,如果我们认为录取率是 0.5,那么我们应该探索 r = 0.5^(1/0.5) = 25%的候选人,并从第一个比他们更好的候选人开始。

您可以在下面的模拟中看到:

作者创建的图表

根据你的成功预测,你可以发掘更多或更少的候选人。在 25%的成功率下,你只能发掘 15 个左右的候选人。如果你住在我认为(我没有数据支持我的说法)可用率远低于 10%的海湾地区,那么你只能负担得起探索 7 个候选人。也就是说,前景相当严峻。

超越爱情和约会:

在这一点上,你可能会说,即使有这些修正的假设,我们也不可能捕捉到恋爱和约会中人类互动的复杂性。

然而,这并不意味着到目前为止你所读到的一切都只是有趣和游戏。到目前为止,你所介绍的是最优停止理论的支柱,我们需要根据连续观察到的随机变量来选择采取给定行动的时间,以便最大化预期收益。事实上,你基本上可以把“寻找合作伙伴”换成“雇佣一名数据科学家”、“寻找一个人来出售我的房子”或“期权交易”,数学原理是一样的。特别是你见过的 top k 问题,是一个众所周知的 ML-system 问题,叫做 在线数据采样 ,你需要从一个数据流中进行在线数据选择和采样,而不需要修改。**

不过话说回来,都没有约会好玩,不是吗?

如果你喜欢这篇文章,你可能也会喜欢我的另一篇关于有趣的统计事实和经验法则的文章

对于其他深潜分析:

你可以在我的 Github 链接找到我的文章的所有代码。

LRGB:长期图表基准

原文:https://towardsdatascience.com/lrgb-long-range-graph-benchmark-909a6818f02c

用远程建模评估图网络的基准

为了解决在中基于消息传递的图形神经网络(MP-GNN)不能在远处的节点之间实现信息交换的局限性,最近出现了几个图形转换器(GTs ),它们固有地模型长程依赖性。然而,对现有基准的评估通常显示 MP-GNNs 的性能与 GTs 相当或更好 ,这使得缺少合适的基准来测试模型传播远程信息的能力。 LRGB 介绍了一组数据集,这些数据集可能需要远程建模才能让网络很好地完成任务,因此可以用于一个试验台来构建未来 GTs 的原型。**

照片由阿丽娜·格鲁布尼亚克Unsplash 上拍摄

这篇文章,名正言顺地简要介绍了基准,是与 拉迪斯拉夫·拉帕切克米哈伊尔·高尔金多米尼克·贝艾尼共同撰写的,是基于论文 的长程图基准(2022)**

带有数据集概述的长期图表基准。

概述

  1. 远程信息瓶颈
  2. 现有图形基准
  3. 表征远程图表数据集
  4. 提议的 LRGB 数据集和任务
  5. 实验和经验教训

****1。远程信息瓶颈

基于消息传递的图形神经网络(MP-GNN)层聚集来自其 1 跳邻居的信息,以更新节点的特征表示。虽然 MP-gnn 有一些局限性,但我们将关注所谓的信息瓶颈,它特别影响远程交互。

长距离信息传播造成的瓶颈。图片来源:Alon 和 Yahav,2021

如果任务需要来自一个 L 跳邻居的远程信息,理想情况下需要堆叠 L 个层。随着 L 的增加, L 跳邻域呈指数增长,需要编码到一个向量中的信息量也呈指数增长,参见 Alon 和 Yahav,2021 中披露的相邻图示。在处理需要长距离信息传播的任务时,这给 MP-GNNs 带来了严重的瓶颈

Alon 和 Yahav,2021 采用的直观补救方法是使用完全相邻的层,以允许原本距离较远的节点之间的直接(1 跳)信息流。可以看出,这个简单的解决方案在一定程度上影响了最近在全连通图上运行的图转换器的工作。查看本次调查。

2.现有图形基准

许多现有的 学习 基准由预测任务组成,这些预测任务主要依赖于本地结构信息而不是远程信息传播来计算目标标签或度量。这可以在数据集如ogbg-molhivogbg-molpcba 中观察到,其中主要依赖于编码局部(或近局部)结构信息的模型继续在排行榜上名列前茅。

与 LRGB 现有相关基准的图形大小统计比较图。作者图片。

同时,这种现有基准中的图是小尺寸的,,节点数。上图显示了图形大小统计的对比。

由于这些原因,我们认为当前的基准可能不足以原型化具有远程建模能力的 GNNs。

3.表征远程图表数据集

我们现在继续讨论几个主要特征,这些特征有助于衡量数据集是否适合测试长期建模 GNN 架构。

图形大小:

图中节点的数量是确定长范围图数据集的重要特征。 Alon 和 Yahav,2021 定义了一个术语叫做 问题半径 它指的是对于一个特定的问题来说节点之间所需的交互范围。如果一个图表数据集作为一个长期基准,那么它的问题半径必须足够大。**

尽管对于真实世界的图数据集,问题半径可能无法精确量化,但是如果图具有较少数量的节点,这个假设的度量实际上会更小。因此,数据集的(平均)图形大小是确定它是否可能是潜在的远程图形数据集的关键属性。

任务性质:

任务的性质可以理解为与问题半径直接相关。从广义上讲,任务既可以是短程,需要在本地或近本地邻域内的节点之间进行信息交换,也可以是远程,需要远离近本地邻域进行交互。

例如,在 ZINC 分子回归数据集中,任务与使用基于子结构计数的模型对局部结构和实验发现进行计数相关联 Bouritsas 等人,2020 已经表明 ZINC 的任务最好需要对 7 长度的子结构进行计数。锌的回归任务可能因此被解释为一个短程任务。

全局图结构对任务的贡献:

学习任务受益于全局结构信息的数据集可以是潜在的长程图数据集。

由于 MP-gnn 依赖于局部特征聚合,因此容易丢失全局结构信息,通常使用全局位置编码(PEs) 来注入。如果图形尺寸很大,MP-GNNs 可能会由于过度挤压而丢失来自远处节点的信号。这种信号可以方便地在全连接的类似变压器的网络中传播。因此,全局结构对任务的贡献变成了一个在一个远程图数据集中所期望的独特的属性,因为它根据它们的远程建模能力来区分两个架构类。

远程节点之间的 3D 原子接触的示例表示。作者图片。

类似地,如果学习任务依赖于某种形式的距离信息,再加上图形特征,如上面的分子示例所示,数据集可以是长期基准的潜在候选对象。

4.提议的 LRGB 数据集和任务

我们考虑上述特征以提出 5 个图形学习数据集的集合,这些数据集可用于原型 gnn 或具有远程建模能力的变压器。有关数据集统计数据的概述,请参见下表:

提议的 LRGB 数据集的统计数据。作者列表。

虽然我们建议读者参考论文以了解数据集的完整细节,但是我们在此简要介绍了它们的亮点以及显著的长程特征。

PascalVOC-SP 和 COCO-SP: 这是分别基于 Pascal VOC 2011MS COCO 图像数据集的超像素图。两个数据集中的学习任务是节点分类,其中相对于相应图像数据集中的原始语义分割标签,每个节点对应于属于特定类别的图像区域。

样本 Pascal VOC 2011 图像(左)、图形-图像叠加和 PascalVOC-SP 图形(右)。作者图片。

与现有的超像素图形数据集(如 MNIST 的、CIFAR10 的)相比,PascalVOC-SP 和 COCO-SP 包含的图形节点数量要多得多(多达 500 个)。类似地,基于节点区域边界的图的构造(,如果两个节点在原始图像中共享公共边界,则它们是连接的)导致具有增加的平均直径的更稀疏的图。

PCQM-Contact: 该数据集基于来自 OGB-LSC 的 PCQM4M 数据集的子集,其中每个图对应于一个具有显式氢的分子图,并且任务,链接预测,是预测将在具有预定义阈值的 3D 空间中彼此接触的远距离节点对。

尽管 PCQM-Contact 中的图表大小并没有明显变大,但其设计的接触图预测任务使其成为一个合适的远程图表数据集,因为它满足了任务的性质以及全局信息贡献特征。

Peptides-func 和 Peptides-struct: 这些是从 SATPdb Peptides 数据库中获得的分子图,其中 Peptides-func 是用于预测肽功能的多标签图分类数据集,而 Peptides-struct 是用于预测 3D 结构特性的多标签图回归数据集,其中一些是长程距离计算。

肽的样品可视化(左)及其分子图(右)。作者图片。

肽分子图的平均直径比 PCQM-Contact 以及现有的酶和蛋白质基准图的平均直径大大约 6 倍,使其适用于远程测试。

5.实验和经验教训

我们使用图学习文献中的两个主要架构类进行基线实验:(i) 本地 MP-GNNs 和(ii) 全连接图转换器,以建立基准趋势并了解更多关于数据集的信息,同时还绘制出一些需要进一步研究的可能挑战。

实验设置、使用的模型和基线结果都包含在 论文 中。我们在这里讨论 学到的关键经验 ,同时解决以下主要问题:

Q1: 是一个局部特征集合,使用较少层数的 MP-GNNs 建模,足以完成在 LRGB 提出的任务?
答:我们对
浅层* MP-GNNs ( L=2 )与深层(L = 5,8 )的比较显示,信息聚合仅限于几跳的模型明显不足,并且在所有 5 个 LRGB 数据集上提供较差的泛化能力。因此,建议的基准适用于评估具有更深架构的 gnn,以及我们在下面的分析中了解到的长期建模。*

S 由于过度挤压的影响增加,简单的本地 MP-GNN 实例性能很差。在 Alon 和 Yahav,2021 中观察到类似的经验发现。

Q2: 在建议的基准测试中,与本地 MP-gnn 相比,我们是否观察到捕获长程相互作用能力增强的模型的性能有明显的差异?
答:我们观察到,除了节点数量最多的数据集 COCO-SP 数据集[#]之外,配备了远程功能的变压器模型在所有基线实验中都是性能最好的基线。在所有数据集中,Peptides-func 和 Peptides-struct 的性能差距最明显,这可归因于这两个数据集满足上述所有特征因素。

[#]基线变压器似乎较慢,不适合 COCO-SP,在 COCO-SP 上,最近的 GraphGPS 架构可以模拟远程依赖性,明显优于 MP-GNNs。

Q3: 新的性能指标评测会带来哪些挑战和未来发现?
答:我们的基准测试实验揭示了不同的挑战,在使用建议的数据集时,可以进行进一步的研究。

首先,当与本地 MP-gnn 一起使用时,位置编码(PE)的使用对性能贡献很小或没有贡献。请参见下表进行演示。一个有趣的探索途径是开发强大的机制来最大限度地利用非局部结构信息,正如 PEs 通常所设想的那样。

PascalVOC-SP 和 COCO-SP 的节点分类任务的基线结果。绩效指标是宏 F1。加粗:最好成绩。作者列表。

其次,下表中所示的针对建议任务观察到的当前得分表明,仍有很大的空间需要通过更好地设计能够利用不规则稀疏结构信息以及传播远程交互的图形网络架构来填充

肽-功能(图形分类)和肽-结构(图形回归)的基线结果。性能度量是分类的平均精度(AP)和回归的平均精度(MAE)。加粗:最佳配乐。作者列表。

PCQM-Contact(链接预测)的基线结果。性能指标是点击率@K 和平均倒数排名(MRR)。加粗:最好成绩。作者列表。

最后,我们希望 LRGB 能够促进可扩展的远程建模架构的开发,因为许多琐碎的二次变换器[o(n^2】]在 LRGB 上扩展可能计算效率低下,lrgb 的平均容量高达 479.40。数据集中的节点、5879 万个节点和总共 3.3209 亿条边。

*

我们开源****远程图形基准与数据集加载器,准备以及基线实验的代码,这些都是基于用 PyGGraphGym 构建的 GraphGPS 包。

为你的下一个项目尝试 LRGB,设计长期可行的架构!

📜 arxiv 预印本 🔧 Github 回购 🏆 排行榜*

go _ backward()—揭开它“隐藏”的秘密

原文:https://towardsdatascience.com/lstm-go-backwards-unravelling-its-hidden-secrets-ed094952b5cc

了解其隐藏的细微差别&探索其泄漏的性质!

LSTM 细胞的表现|图片作者:Christopher Olah

简介

长短期记忆(LSTM)是递归神经网络(RNN)的高级版本,顾名思义,能够在相对较长的序列上存储“上下文”。这使得它们成为 NLP 任务的完美工具,如文档分类、语音识别、命名实体识别(NER)等。

在许多应用中,如机器翻译、语音识别等。,来自两边的上下文提高了基于语言的模型的性能。为了在实践中实现这一点,我们使用双向 LSTMs。然而,如果您需要分别提取向前和向后运行的嵌入,您将需要实现两个独立的 lstm,其中第一个接收向前的输入,另一个以向后的方式处理输入。

为了节省您的大量精力,Tensorflow 的 LSTM 让您可以灵活地正常使用 give 输入,同时在内部以相反的方式处理它,从右到左学习上下文!这是通过将go _ backward()= True放入 LSTM 层来实现的。简而言之,如果您的输入是['I ',' am ',' the ',' author'],那么 LSTM 将从右向左读取,这要感谢 go_backwards(),并将输入视为['author ',' the ',' am ',' I']。这允许模型学习双向上下文!

到目前为止一切顺利。然而,这里有一个警告!当您在管道中添加正向和反向上下文时,您往往会在这个过程中泄漏大量信息,这可能会导致模型在评估期间给出大约 100%的准确性!!这是因为我们没有理解 go _ backwards()的真正、微妙的实现。

在这篇博客中,我使用一个真实的 NLP 示例来揭示 go _ backward()的真正实现,并探索 go _ backward()的预期行为的微小变化如何会导致模型评估期间的大量泄漏!

任务:下一个单词预测

考虑下一个单词预测任务,其中基于当前输入,模型需要预测下一个单词。向后的方向接收,比方说,原始句子索引 2 处的单词,所以它需要预测索引 1 处的单词!

作者对 LSTM |图像反向的输入输出表示

模型架构

让我们考虑下面的架构。我们有两个独立的输入,一个用于 LSTMs 的正向,另一个用于反向。

手头任务的双向 LSTMs 表示:下一个单词预测|作者图片

我们创建或利用预先训练的嵌入,如 Word2Vec、Glove 等。对于每个单词,通过 LSTMs 传递它们,通过密集层传递嵌入并将其与 LSTM1 输出相加来使用跳过连接,通过另一组 LSTM 层传递总和,最后通过一个公共的 Softmax 层来获得整个词汇中最可能的预测!

作者为任务选择的模型架构|图片

观察

上述模型在 IMDB 训练数据集上训练超过 75 个时期,具有适当的批量大小、学习速率和实现的早期停止。由于后者,模型训练停止了大约 35 个时期。你应该注意到从向后的 LSTM 层输出的惊人结果!

前向和后向 LSTM 图层的验证精度|图片由作者提供

你注意到倒数第二排的 100%准确率了吗!!!很疯狂,对吧?这意味着模型已经“完美地学习”了正确的模式,根据输入给出输出。但是下一个单词预测并不是一个可以完美学习的任务。这就像说对未来的预测有可能 100%准确。😮

但是我们知道,在模型中的某个地方,一定会发生一些泄漏,而之前已经暗示了输出。这是我在 AI 大学 AI-3 课程(NLP)中做项目时意识到的。这个发现是这篇文章的主要目的——找出这种奇怪行为背后的原因!

关于 go _ backwards()的一些隐秘的事情?:

LSTM 的前进方向通过文件大部分是清楚的。但是,go _ backwards()函数似乎有点棘手。如果你看一下它的文档,你会注意到它正向获取输入序列,在内部反转,然后处理它,最后给出反转序列。

根据 Tensorflow 中的 LSTM 文档使用 go _ backward()|来源: Tensorflow LSTM

作者对后退图片的直观应用

然而,与我们的预期不同,在 LSTMs 中颠倒顺序并不那么简单。根据 go _ backwards()的官方文档tf.reverse_sequence( )是用于反转输入的函数,这不是简单的反转。该函数不是从右到左排列,而是只反转由“序列长度”决定的记号的数量,并保持句子的其余部分不变,因为其他记号是填充记号,不需要反转!

来自 LSTM 的输出,带有 go_backward()=True |作者图片

参数 go _ backward()= True 意味着 LSTM 层将接受输入,使用 tf.reverse_sequence 策略对其进行反转,通过 LSTM 层找到输出,最后将填充的标记放在前面!!这个最终的反转是使用 tf.reverse() 完成的。

引擎盖下:第一 LSTM

所以这就是当你在第一个 LSTM 层中把 go _ backwards()参数设为真时的情况。请注意,我们假设 LSTM 是一个身份映射器,即输出作为输入。这样做是为了便于表示和理解。

第一个反向 LSTM 层的工作|由作者制作的图像

如果您仔细观察,就会发现 go _ backward()= True 的 LSTM 图层的输出正在执行文档中提到的任务。最终的输出实际上是我们期望的(假设为真)输出的真实反转(字面意义)

但是,我们没有反转这个输出,而是直接将其用于跳过连接,并在管道中使用 is!这就是事情变糟的地方!

集中精力解决问题!

在我们的模型中,我们将输出(实际上是 xD 以上的反向输出)添加到密集输入,然后将其传递到下一个反向 LSTM。让我们用上面的例子做一次预演,看看第二个 LSTM 层和你从 LSTM 1:

添加了带有密集图层输出的输入|作者提供的图像

您是否注意到输出是如何对称的!!即使我们对加法进行加权,去掉对称性,你也逃不出 LSTM2!

第二层反向 LSTM 层|图片由作者制作

现在假设 PAD=0, =1,A=2,B=3,C=4,D=5, =6(为简单起见,用数字表示),则输入为:

按作者呈现输出|图像

给定第 0 行的输入,对于 LSTM 这样的神经网络来说,找到输出中的模式有多难?😌这就是第二个 LSTM 层非常快赶上并达到接近 100%验证准确性的原因!!

问题的根源是 go _ backward()= True 的 LSTM 的输出实际上是反转,如文档中所述。在通过之前你需要翻转它!

实验

我手动翻转了两个 LSTM 图层的输出,在轴=1 上使用 go _ backward()= True,并在 75 个时期内获得了两个图层约 44%的准确性,并提前停止。后来,我在用于情感分类的 IMDB 数据集上观察了三种不同的迁移学习实现的以下结果:

1.基线:85.75%
2。Word2Vec: 84.38%
3。ELMo:80.17%[使用来自嵌入层的嵌入、第一 LSTM 正向和反向运行的级联输出以及第二 LSTM 的级联输出。

(大约 50 个时期,提前停止)

***

训练时模型的丢失和验证准确性|图片由作者提供*

如果你有兴趣详细了解 LSTM 的内幕,请点击以下链接:

**

这个故事的寓意!

在实施之前,请务必仔细阅读文档!🤓
在使用工具之前,先了解它们!😃

LSTM 递归神经网络——如何教网络记住过去

原文:https://towardsdatascience.com/lstm-recurrent-neural-networks-how-to-teach-a-network-to-remember-the-past-55e54c2ff22e

神经网络

以双向 LSTM 解决“多对多”序列问题为例直观解释长时短时记忆

长短期记忆(LSTM)神经网络。图片作者。

介绍

标准递归神经网络(RNNs)由于在处理较长的数据序列时出现的消失梯度问题而遭受短期记忆。

幸运的是,我们有更先进的 RNNs 版本,可以保存序列早期的重要信息,并将其发扬光大。两个最著名的版本是长短期记忆(LSTM)门控循环单位(GRU)

在本文中,我将重点关注 LSTM 的结构,并为您提供一个详细的 Python 示例供您使用。

内容

  • LSTM 在机器学习领域处于什么位置?
  • LSTM 与标准 RNNs 有何不同,LSTM 是如何工作的?
  • 一个完整的 Python 示例,向您展示了如何构建和训练您自己的 LSTM 模型

LSTM 在机器学习领域处于什么位置?

下面的图表是我对最常见的机器学习算法进行分类的尝试。

虽然我们经常以监督的方式使用带有标签的训练数据的神经网络,但我觉得它们独特的机器学习方法值得单独归类。

因此,我的图表显示了从机器学习宇宙的核心分支出来的神经网络。递归神经网络占据 NNs 的一个子分支,并且包含诸如标准 RNNs、LSTMs 和 GRUs 的算法。

下图是交互式的,所以请点击不同的类别来放大并展示更多的👇。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章的电子邮件。

LSTM 与标准 RNNs 有何不同,LSTM 是如何工作的?

让我们先快速回顾一下简单的 RNN 结构。RNN 由类似于前馈神经网络的多层组成:输入层、隐藏层和输出层。

标准递归神经网络结构。图片由作者提供。

然而,RNN 在其隐藏层中包含了个递归单元,这允许算法处理个序列数据。它通过循环传递来自前一个时间步长的隐藏状态并将其与当前时间步长的输入相结合来实现。

时间步长—通过递归单元对输入进行的单一处理。时间步长的数量等于序列的长度。

如果需要,你可以在我的前一篇文章中找到标准 rnn 的详细解释。

LSTM 和标准的 RNN 有什么不同?

我们知道 RNNs 利用循环单元从序列数据中学习。LSTMs 也是。然而,在这两者之间,循环单元内部发生的事情是非常不同的。

查看标准 RNN 的简化递归单位图(未显示权重和偏差),我们注意到只有两个主要操作:将先前的隐藏状态与新的输入相结合,并将其传递给激活函数:

标准 RNN 循环单位。图片由作者提供。

在时间步长 t 计算隐藏状态后,它被传回递归单元并与时间步长 t+1 的输入组合,以计算时间步长 t+1 的新隐藏状态。对 t+2、t+3、…、t+n 重复该过程,直到达到预定数量(n)的时间步长。

与此同时,LSTM 利用各种关口来决定保留或丢弃哪些信息。还有,它增加了一个细胞状态,就像是 LSTM 的长期记忆。所以让我们仔细看看。

LSTM 是如何工作的?

LSTM 递归单元比 RNN 复杂得多,提高了学习,但需要更多的计算资源。

LSTM 循环股。图片由作者提供。

让我们通过简图(未显示权重和偏差)来了解 LSTM 循环单位是如何处理信息的。

  1. 隐藏状态&新输入 —来自前一时间步(h_t-1)的隐藏状态和当前时间步(x_t)的输入在通过各种门传递其副本之前被组合。
  2. 遗忘之门 —这个门控制着什么信息应该被遗忘。因为 sigmoid 函数的范围在 0 和 1 之间,所以它设置单元状态中的哪些值应该被丢弃(乘以 0)、记住(乘以 1)或部分记住(乘以 0 和 1 之间的某个值)。
  3. 输入门有助于识别需要添加到单元状态的重要元素。注意,输入门的结果乘以单元状态候选,只有输入门认为重要的信息被添加到单元状态。
  4. 更新单元状态—首先,前一个单元状态(c_t-1)乘以遗忘门的结果。然后我们从[输入门×单元状态候选]中加入新的信息,得到最新的单元状态(c_t)。
  5. 更新隐藏状态 —最后一部分是更新隐藏状态。最新的单元状态(c_t)通过 tanh 激活函数,并乘以输出门的结果。

最后,最新的单元状态(c_t)和隐藏状态(h_t)回到递归单元,并且在时间步长 t+1 重复过程。循环继续,直到我们到达序列的末尾。

一个完整的 Python 例子,展示了如何构建和训练你自己的 LSTM 模型

我们可以以四种不同的方式使用 LSTMs:

  • 一对一 —理论上是可能的,但是如果一个项目不是一个序列,你就不会得到 LSTMs 提供的任何好处。因此,在这种情况下,最好使用前馈神经网络
  • 多对一 —使用一系列值来预测下一个值。你可以在我的 RNN 文章中找到这种设置的 Python 例子。
  • 一对多 —使用一个值预测一系列值。
  • 多对多 —使用一个值序列来预测下一个值序列。我们现在将构建一个多对多的 LSTM。

设置

获取以下数据和库:

让我们导入所有库:

上面的代码打印了我在这个例子中使用的包版本:

Tensorflow/Keras: 2.7.0
pandas: 1.3.4
numpy: 1.21.4
sklearn: 1.0.1
plotly: 5.4.0

接下来,下载并摄取澳大利亚的天气数据(来源: Kaggle )。我们只接收列的子集,因为我们的模型不需要整个数据集。

此外,我们执行一些简单的数据操作,并得出几个新的变量:年月和中值温度。

一小段 Kaggle 的澳大利亚天气数据做了一些修改。图片由作者提供。

目前,我们对每个地点和日期都有一个中值温度记录。然而,每天的温度波动很大,使得预测更加困难。因此,让我们计算月平均值,并将数据转置为以地点为行,以年月为列。

按地点和月份划分的月平均温度。图片由作者提供。

由于我们使用的是现实生活中的数据,我们注意到三个月(2011 年 4 月、2012 年 12 月和 2013 年 2 月)完全从数据框架中消失了。因此,我们通过取前一个月和后一个月的平均值来估算缺失月份的值。

最后,我们可以在图表上绘制数据。

月平均气温。图片由作者提供。

该图最初显示了所有地点,但我选择了其中的四个(堪培拉、达尔文、黄金海岸和吉尼火山)显示在上图中。

请注意不同地点的平均温度以及温度变化是如何不同的。我们可以训练一个特定位置的模型以获得更高的精度,也可以训练一个通用模型来预测每个地区的温度。

在这个例子中,我将在一个地点(坎培拉)训练我们的 LSTM 模型。如果你对一个通用模型感兴趣,你可以看看我关于门控循环单元(GRU) 的后续文章。

训练和评估 LSTM 模型

在我们开始之前,这里有一些需要强调的事情。

  • 我们将使用 18 个月的序列来预测未来 18 个月的平均气温。您可以根据自己的喜好进行调整,但要注意,对于长度超过 23 个月的序列,将没有足够的数据。
  • 我们将把数据分成两个独立的数据帧——一个用于训练,另一个用于验证(超时验证)。
  • 由于我们正在创建一个多对多预测模型,我们需要使用一个稍微复杂一点的编码器-解码器配置。编码器和解码器都是隐藏的 LSTM 层,信息通过重复向量层从一个层传递到另一个层。
  • 当我们想要有不同长度的序列时,例如,一个 18 个月的序列来预测接下来的 12 个月,一个重复向量是必要的。它确保我们为解码器层提供正确的形状。然而,如果您的输入和输出序列的长度与我的示例中的长度相同,那么您也可以选择在编码器层中设置 return_sequences=True 并移除重复向量。
  • 注意,我们给 LSTM 层添加了一个双向包装器。它允许我们在两个方向上训练模型,这有时会产生更好的结果。但是,它的用途是可选的*。*****
  • 此外,我们需要在输出层使用一个时间分布包装器来单独预测每个时间步长的输出。
  • 最后,请注意,我在这个示例中使用了未缩放的数据,因为它比使用缩放数据(MinMaxScaler)训练的模型产生了更好的结果。你可以在我的 GitHub 库(文章末尾有链接)的 Jupyter 笔记本中找到缩放和未缩放的版本。

首先,让我们定义一个 helper 函数,将数据整形为 LSTMs 所需的 3D 数组。

接下来,我们训练 LSTM 神经网络超过 1,000 个时期,并显示带有评估指标的模型摘要。您可以按照我在代码中的注释来理解每一步。

上述代码为我们的 LSTM 神经网络打印了以下摘要和评估指标(注意,由于神经网络训练的随机性,您的结果可能会有所不同):

LSTM 神经网络性能。图片由作者提供。

现在让我们将结果绘制在图表上,以比较实际值和预测值。

LSTM 神经网络预测与实际。图片由作者提供。

看起来我们在预测堪培拉月平均气温的探索中已经相对成功了。看看你是否能为一个不同的澳大利亚城市得到更好的结果!

结束语

我真诚地希望你喜欢阅读这篇文章,并获得一些新的知识。

你可以在我的 GitHub 库中找到完整的 Jupyter 笔记本代码。请随意使用它来构建您自己的 LSTM 神经网络,如果您有任何问题或建议,请不要犹豫与我们联系。

干杯!👏
T5【索尔多比拉斯】

如果你已经花光了这个月的学习预算,下次请记得我。 我的个性化链接加入媒介:

https://solclover.com/membership

您可能感兴趣的其他文章:

Lyft 数据科学家面试问题演练

原文:https://towardsdatascience.com/lyft-data-scientist-interview-question-walkthrough-810efc216a3a

打车公司面试时被问到的 SQL 问题的三步解决方案

作者在 Canva 上创建的图片

近 2000 万人使用 Lyft 在他们的城市中移动。该公司的主要产品是一个用户友好的移动应用程序,允许用户无缝地从 A 点移动到 b 点。

在智能手机应用程序的用户友好性背后,Lyft 投入了大量精力来收集和分析有关其用户和司机的数据。这有助于他们计划和优化他们的资源,提供最有效的市内交通,考虑价格、时间和舒适度。

Lyft 的面试官试图提出正确的问题,以聘用有能力的数据科学家。在本文中,我们将带您完成一个常见的 Lyft 数据科学家面试问题,其中候选人必须根据可用数据计算司机流失率。

Lyft 数据科学家访谈中测试的基础到中级概念

Lyft 正在寻找数据科学家,他们可以通过编写高效可读的 SQL 查询来帮助公司。要做到这一点,候选人需要彻底理解 SQL 概念。

让我们来看看一些基本的 SQL 概念,它们可以帮助你在 Lyft 获得数据科学家的工作。

计数()

当参加数据科学面试时,知道如何使用所有聚合函数很重要。这包括它们的语法、可能的参数和输出值。阅读《SQL 聚合函数终极指南 》可以帮助你更好的理解这个 SQL 特性。

要回答这个问题,候选人需要理解 COUNT()聚合函数。您应该知道 COUNT()函数返回在指定列中有值的行数。如果有空值,COUNT()函数不会将该行添加到总计中。

候选人可能需要执行的其他有用操作包括:获取特定列中具有唯一值的行数、获取总记录数、获取特定列中具有空值的记录数、为结果指定别名,等等。

了解如何使用 GROUP BY 语句来汇总 COUNT()等聚合函数的结果也很有用。

在最好的情况下,您应该熟悉一些如何在实践中使用 COUNT()的例子。为了得到这个问题的最终答案,我们将使用 COUNT()来查找指定列中具有 NULL 值的行数。然后,我们可以找到这个数字与表中的总行数之间的比率。

另一个重要的细节是 COUNT()聚合函数返回一个整数值。在我们的解决方案中,我们将 COUNT()函数的一个输出除以另一个输出,得到一个小数比率。为了得到想要的结果,我们需要将其中一个值转换成浮点数。

转换值类型

当解决一个问题时,知道你在处理什么类型的价值观是很重要的。如果需要,您应该能够将一种类型的值转换成另一种类型的值,以执行算术运算并获得准确的结果。

候选人应该知道如何将日期值转换为字符串,将整数转换为浮点数,将字符串转换为整数,或者任何其他组合。需要了解的最重要的函数之一是 CAST(),但是还有一个更改值类型的简写语法。

在这个特别的问题中,我们正在处理整数除法和浮点数除法之间的区别。寻找答案需要彻底理解两种算术运算的区别。你也应该知道它们何时发生,以及为什么分裂是这样或那样的。

例如,我们可能需要将一个 COUNT()函数的结果除以另一个函数的结果。这两个都是整数值。如果我们想得到一个浮点值作为答案,我们必须执行浮点除法。为此,我们必须将其中一个转换为浮点值。

作为给定别名的关键字

了解 AS 命令的语法和实际应用可以帮助您编写容易理解的代码。描述性的列名也可以帮助其他人理解可用的数据。

一个好的候选人应该知道如何给表引用、子查询甚至 cte 起别名。

AS 命令通常与其他 SQL 语句一起使用,例如 with 和 SELECT。了解如何将这些语句与 AS 命令结合起来,对于编写高效的查询来说至关重要。

理想情况下,您应该知道 AS 关键字如何工作的其他重要细节,例如,别名只在查询期间存在。

Lyft 数据科学家面试问题演练

作者在 Canva 上创建的图像

寻找用户购买

在这个问题中,候选人必须计算司机流失率。这对 Lyft 很重要,因为它有助于该公司确保在任何地方都有稳定的司机供应。

在这个问题中,我们被要求找出所有地区的司机流失率。我们不必按地区或时间过滤。

截图来自 StrataScratch

理解了流失率的概念,就有必要想出一个公式来计算它。

问题链接:https://platform . stratascratch . com/coding/10016-churn-rate-of-lyft-drivers

可用数据集

截图来自 StrataScratch

在提出一种方法之前,请注意每一列中值的类型。

数据假设

这个问题的所有可用数据都包含在 lyft_drivers 表的四列中,所以没有太多要分析的。

截图来自 StrataScratch

让我们来看看该表中的四列:

索引 —该列中的值是分配给每个驾驶员记录的连续数字。假设所有的行在这个列中都有一个值是安全的。

start_date —该值表示驾驶员第一次注册 Lyft 的日期。假设所有的行在这个列中都有一个值是安全的。

end_date —我们需要检查该列中的值,以找到活动驱动程序的数量。我们需要这个数字来计算这些年来司机的流失率。

yearly_salary —问题没有提到司机收入,可以忽略这一栏。

没有 driver_id 列的事实告诉我们,我们可以假设每条记录都是唯一的。

仔细阅读问题是解决任何 SQL 问题所必需的。一旦你理解了手头的任务,查看可用的数据就可以得到答案。

这个问题是一个很好的例子,说明预览数据可以帮助你找到答案。如果你查看数据,你会看到每一行代表一个司机。

所有记录在开始日期列中都包含一个值,但是有些记录的结束日期列为空。这可以代表当驾驶员注册了 Lyft 并且从未停止(主动驾驶员)时的情况。

另一方面,既有开始值又有结束值的行清楚地表示司机停止为 Lyft 工作的情况。我们需要找到活跃和不活跃司机的数量来计算流失率。

一旦逻辑上连接了所有的点,就可以开始编写查询了。

解决方案逻辑

计算司机流失率意味着获得曾经为 Lyft 开车,但由于这样或那样的原因而停止的司机的份额。

为了找到任何 SQL 挑战的解决方案,您需要注意问题的措辞。

这个特定的问题指定输出应该是一个比率,它总是涉及一个值(部分)除以另一个值(总数)。看完数据后,你会意识到我们必须将退出的司机除以所有司机的数量。

该问题没有具体说明计算司机的时间段。因此,没有必要通过注册日期或结束日期来筛选驱动程序。

解决这个 Lyft 数据科学家面试问题的一个重要步骤是将流失率这样的抽象概念转化为公式。然后你可以简单地把这些值代入公式得到答案。

在编写任何 SQL 代码之前,最好描述一下解决问题的大致步骤:

  • 获取驱动程序和非活动驱动程序的总数
  • 将其中一个整数转换为浮点数以获得十进制值
  • 将非活动驱动因素的数量除以所有驱动因素的数量

预览表格可以帮助我们识别不再为 Lyft 开车的司机。如果司机记录的 end_date 列为空,则意味着该司机仍然有效。如果司机在结束日期栏中有一个具体的日期,这意味着司机不再活跃。

了解 COUNT()聚合函数的工作原理可以帮助您用几行代码解决这个问题。我们可以使用 COUNT(*)来获取所有驱动程序的数量,并使用 COUNT(end_date)来获取非活动驱动程序的数量(那些在 end_date 列中包含值的驱动程序)。

唯一的问题是 COUNT()聚合函数返回一个整数值,所以我们将整数除以整数。结果将是 0,而不是代表比率的小数值。这是因为我们做的是整数除法,结果总是返回整数。

我们可以通过将其中一个值转换成浮点数来进行浮点数除法。然后我们可以计算一个司机流失率,这个流失率是一个小数值。

您可以使用 CAST()函数将其中一个整数转换成浮点数,或者使用简写的双冒号语法来实现。

运行查询将显示我们最终输出的列名不是描述性的。最后一步,我们必须使用 As 命令为该列指定一个描述性名称,例如“driver_churn_rate”。

解决方法

步骤 1:获取非活动驱动因素和总驱动因素的数量

我们将使用 COUNT(end_date)聚合函数来获取非活动驱动因素的数量(在 end_date 列中有值的行)。

我们还可以使用 COUNT(*)来获得驱动程序的总数。使用星号来获取表中所有记录的数量是安全的。

显然,我们必须指定从中提取值的表。

SELECT COUNT(end_date),
       COUNT(*)
FROM lyft_drivers

如果我们运行这段代码,我们将看到非活动驱动程序的数量与所有驱动程序的数量之比:

截图来自 StrataScratch

第二步:找到比例

为了得到这个比率,我们需要用所有记录的数量除以非活动驾驶员的数量。考虑到两个值都是整数,首先,我们需要将其中一个转换为浮点数,这样除法的结果将是一个十进制值。

我们可以使用简写语法(::)将整数值转换为浮点数:

SELECT COUNT(end_date)::float/ COUNT(INDEX)
FROM lyft_drivers

如果我们运行该查询,它将返回一个答案:

截图来自 StrataScratch

剩下的唯一问题是列名。

步骤 3:命名列

现在,我们只需要命名列来描述该值所代表的内容。为此,我们将在 SQL 中使用 AS 命令。

SELECT COUNT(end_date)::float/ COUNT(INDEX) AS driver_churn_rate
FROM lyft_drivers

现在,当我们运行这段代码时,这个列有了一个更容易理解的名字 driver_churn_rate

截图来自 StrataScratch

如果我们检查我们的最终解决方案,它被接受。

最后的话

流失率是许多公司的一个重要指标。解决这个 Lyft 数据科学家面试问题将教会你这个概念代表了什么,以及如何基于可用数据进行计算。

StrataScratch 为你参加数据科学家面试做好了一切准备。你可以检查你的解决方案是否正确,并看看其他人的解决方案,以了解解决问题的不同方法。

我们的平台允许你为每个数据科学公司整理数据科学面试问题,这样你就可以为你想要的雇主的面试做准备。它还包括非技术性问题和你找工作所需的一切。

随着时间的推移,歌词的情感和词汇多样性分析

原文:https://towardsdatascience.com/lyrics-analysis-5e1990070a4b

使用 Quanteda 和 IBM 的音调分析仪对 1958 年以来公告牌百强歌曲进行分析

照片由来自佩克斯布伦特·基恩拍摄

毫无疑问,这些年来音乐已经发生了变化。但是如果我们看看美国排名前 100 的歌曲的歌词,有什么可观察到的趋势吗?

当我想到近几十年来的一些歌词时,我当然可以同情婴儿潮一代的观点,即音乐已经变得愚蠢。

想想《爱你的方式》中的阿姆:

现在你可以看着她离开窗户
我猜这就是为什么他们称之为窗玻璃

还是坎耶在抬高自己

噗噗声
噗噗声
噗噗声
等等。

暂且把这些不太光彩的当代音乐例子放在一边,歌词真的变得不那么复杂了吗?或者,就此而言,他们身上的主导情绪发生了变化吗?

如果你 CBA 阅读 的文章,或者对编码不感兴趣——请随意通过在 仪表盘 中播放我整理的结果来回答这些问题。如果你想比较几个著名说唱歌手的歌词复杂度或每首歌的平均字数,艺术家过滤器是预设的。

按作者分类的仪表板

获得公告牌百强歌词

首先,获得美国公告牌前 100 首歌曲的歌曲和艺术家名单。Billboard API 和 RSS feed 已经被弃用,但幸运的是,这方面的数据可以追溯到 1958 年,可以在 Kaggle 上找到。

我们将使用一个免费歌词 API 来获取所有这些歌曲的歌词,因此艺术家姓名和歌曲名称需要被 URL 编码成适合请求的格式。

# Get list of top 100 US songs since 1958 and process for lyrics API
# Read in Billboard hot 100 songs
billboard <- read.csv("billboard_hot_100.csv")# Select needed cols
billboard <- billboard %>% select(date, artist, song)# Drop all the repeated data
billboard <- billboard %>% distinct(artist, song, .keep_all = TRUE)# Process the columns into URL encoding format
billboard$artist_url <- billboard$artist %>%
  str_replace_all(" ", "%20") %>%
  paste0("/")
billboard$song_url <- billboard$song %>%
  str_replace_all(" ", "%20") %>%
  paste0("/")
# Merge the two url encoded cols
billboard$url <- paste0(billboard$artist_url, billboard$song_url)# Create column to add lyrics to
billboard$lyrics <- 0

接下来我们要歌词。出于礼貌,您可能希望在 for 循环中添加一些结果的中间保存,以及一些Sys.sleep()延迟。对我来说,最初的过程需要大约 8 个小时(带睡眠)才能运行,所以可以随意使用包含 GitHub repo 中抓取的数据的 lyrics_raw.csv 文件。

并非所有的歌曲都可以请求歌词(API 的数据库中没有歌词,歌曲/标题拼写不一致等。)-但我们仍然获得了足够大的样本用于我们的分析(约 16K 首歌曲,所有前 100 首歌曲的 45%)。

# Make requests to lyrics API
base_url <- "[https://api.lyrics.ovh/v1/](https://api.lyrics.ovh/v1/)"# Add counter to check calls are working with
counter = 0
for (i in 1:length(billboard$url)){# Get the url string to add to the base
  song_url <- billboard$url[i]
  # Construct the full request URL for that song
  full_url <- paste0(base_url, song_url)
  # Make request
  r <- GET(full_url)
  # Increment counter
  counter <- counter + 1
  # If the request was okay, add lyrics
  if (r$status_code == 200){
    # Remove source line
    lyrics <- content(r)$lyrics %>%
      str_replace("Paroles de la chanson.*\r\n", "")
    # Add song's lyrics
    billboard$lyrics[i] <- lyrics
  }
}
# Save to a csv
write.csv(billboard, "lyrics_raw.csv", row.names = FALSE)

情感分析

现在我们有了我们需要的歌词,我们可以使用 IBM 的音调分析器来分析每首歌词中的主要情绪/情感。IBM Cloud 给新用户 200 美元的免费积分,这对于我的样本量来说已经足够了。

IBM Cloud 为新用户提供 200 美元的免费积分

# Enter params for the request
ibm_key <- "mind_your_own_beeswax"
ibm_url <- "[y](https://api.eu-gb.tone-analyzer.watson.cloud.ibm.com/instances/mind_your_own_beeswax_again/v3/tone)our_ibm_url"
ibm_version = "your_ibm_version"# Construct df to append IBM data to
tone_df <- data.frame()
# Add counter to check calls are working with
counter = 0# Iterate through each song's lyrics, analysing sentiment
for (i in 1:length(billboard$lyrics)){
  # Get lyrics of current song
  current_song <-  billboard$lyrics[i]
  # Add lyrics to params for URL / request
  params = list(version = ibm_version, text = current_song)
  # Increment counter
  counter <- counter + 1# Make the request
  r <- GET(url = ibm_url,
           query = params,
           authenticate('apikey', ibm_key))# If the request was okay, add lyrics
  if (r$status_code == 200){
    # Parse the data
    parsed_r <- fromJSON(rawToChar(r$content))
    # Merge tone data with billboard data into new df
    tone_df <- parsed_r$document_tone$tones %>%
      bind_cols(billboard[i, ]) %>%
      bind_rows(tone_df)
  }
}
# Save the data in a csv
write.csv(tone_df, "tone_raw.csv", row.names = FALSE)

让我们来看看这些结果

使用一个标准的线性回归,随着时间的推移,歌词似乎变得越来越愤怒,越来越不快乐。(完整的带注释的可视化代码可以在 GitHub repo 中找到)。

随着时间的推移,歌词变得越来越愤怒,越来越不快乐

作者图片

十年的感悟呢?

作者图片

词汇多样性分析

我们已经看了情绪,但是抒情的复杂性呢?我们可以从词汇多样性的角度来分析这一点。我使用了类型-标记比率Maas 来度量词汇多样性,因为它们计算量不大,而且很常用。

词汇多样性是文本中独特的单词/词干的比例

其工作流程如下:

  1. 用 Quanteda 构建一个语料库并进行处理
  2. quanteda.textstats::textstat_lexdiv()分析词汇多样性
  3. 绘图结果
# Make lyrics into a corpus
lyric_corpus <- corpus(billboard, text_field = "lyrics")# Get number of tokens in each song
docvars(lyric_corpus)$ntoken <- lyric_corpus %>% ntoken()
# Filter out the songs with barely any lyrics
lyric_corpus <- corpus_subset(lyric_corpus, ntoken > 50)# Tokenize lyrics and calculate lexical diversity
lexdiv <- lyric_corpus %>%
  # Remove numbers and punctuation
  tokens(remove_punct = TRUE,
         remove_numbers = TRUE,
         remove_symbols = TRUE) %>%
  # Make all lowercase
  tokens_tolower() %>% 
  # Calculate lexical diversity using quanteda
  textstat_lexdiv(measure = c("TTR", "Maas"))# Merge lexical diversity measures into docvars
lexdiv <- bind_cols(docvars(lyric_corpus), lexdiv[, 2:3])
# Make year into date
lexdiv$year <- lexdiv$year %>% paste0("-01-01") %>% as.Date()# Make into long format for plotting
lexdiv <- lexdiv %>% pivot_longer(c(TTR, Maas),
                                names_to = "lexdiv_measure",
                                values_to = "lexdiv_value"
                                )
# Group by year
lexdiv_grouped <- lexdiv %>% group_by(year, lexdiv_measure) %>%
  summarise(lexdiv_value = mean(lexdiv_value, 
                       na.rm = TRUE))

这两项指标都显示词汇多样性随着时间的推移而下降。

作者图片

我们走吧!r 中快速简单的情感/词汇多样性分析

在 Maas 和 TTR 中,歌曲的词汇多样性已经随着时间的推移而略有下降(尽管这可能部分是由于歌曲中的单词数量增加,请参见GitHub repo了解详细信息)。

情感分析显示,随着时间的推移,歌词变得越来越愤怒,越来越不快乐。

然而,我们应该警惕从这个分析中得出太多的结论。如果说任何类型的数据会受到更广泛的社会条件和/或潜在变量的影响,那么音乐数据就是。我不认为歌词中日益增长的愤怒是因为我们作为一个社会变得更加愤怒——这可能与歌曲中日益增长的亵渎或说唱的出现有关。至于更少的快乐…你的猜测和我的一样好。

链接

来源

16 英寸 MacBook M1 Pro 与采用 RTX3060Ti 的定制电脑—千钧一发

原文:https://towardsdatascience.com/macbook-m1-pro-16-vs-custom-pc-with-rtx3060ti-close-call-16b15f085fb7

【2499 美元的苹果 beast 与 1300 美元的配备专用 GPU 的 PC——了解哪种更适合 TensorFlow

Unsplash注册的照片

M1 Pro MacBook Pro 16”可能是你现在能买到的最好的笔记本电脑。它在超级优质的表面下封装了一些真正的动力——所有这些都是在一次充电后让你度过一天的繁重使用。

但是数据科学呢?它能接近配备专用 GPU 的 PC 吗?这就是我们今天要回答的问题。

在今天的文章中,我们将只比较数据科学使用案例,忽略笔记本电脑与 PC 的其他差异。在测试中,我们有一台“基础型号”MacBook Pro M1 Pro 16”和一台由 AMD 锐龙 5 和 Nvidia RTX 显卡驱动的定制 PC。以下是规格:

图 1 —硬件规格对比(图片由作者提供)

M1 Pro 16”起价 2499 美元,价格不菲。我在定制电脑上花了大约 1300 美元。差不多便宜 50%,有专用 GPU。理论上听起来更好,但我们必须做试验才能确定。

不想看书?请观看我的视频:

MacBook Pro M1 Pro 与定制 PC — Geekbench

综合基准只能让我们到此为止,但它们是一个很好的起点。我们先来看看苹果 Pro M1 Pro 和 AMD 锐龙 5 5600X 在单核部门对比如何:

图 2 — Geekbench 单核性能(图片由作者提供)

M1 Pro 快了大约 7%,如果你考虑两者之间的大小,功率和散热差异,这是惊人的。

接下来我们来对比一下多核的性能。M1 Pro 有 10 个 CPU 内核(8 个性能和 2 个效率),而锐龙有 6 个内核和 12 个线程:

图 3 — Geekbench 多核性能测试(图片由作者提供)

M1 专业版明显快了 62%。在日常工作中,你肯定会注意到这种差异,尤其是如果你考虑到 macOS 比 Windows 优化得多。

这是他们渐行渐远的地方。定制 PC 有一个专用的 RTX3060Ti GPU,内存为 8 GB。M1 专业版有 16 个 GPU 核心,但它远没有这么强大:

图 4 — Geekbench OpenCL 性能(图片由作者提供)

根据 OpenGL 基准测试,RTX3060Ti 比 M1 Pro GPU 快 3.3 倍左右。这些结果是意料之中的,但它们不一定会转化为现实世界中 TensorFlow 的使用。

在我向您展示结果之前,我们必须设置数据科学基准。

MacBook Pro M1 Pro 与 RTX3060Ti —数据科学基准测试设置

如果您继续学习,您将需要安装 TensorFlow。这里有一整篇文章致力于为苹果 M1 和 Windows 安装 TensorFlow:

此外,您还需要一个影像数据集。我使用了来自 Kaggle 的狗和猫的数据集,它是在知识共享许可下授权的。长话短说,你可以免费使用。

有关如何组织和预处理它的详细说明,请参考以下文章:

https://betterdatascience.com/top-3-prerequisites-for-deep-learning-projects/

我们今天会做两个测试:

  1. 具有定制模型架构的 TensorFlow 使用我在 CNN 文章中描述的两个卷积块。
  2. 带迁移学习的 tensor flow—使用 VGG-16 预训练网络对图像进行分类。

让我们检查一下测试中使用的代码。

自定义张量流模型—代码

我将这个测试分为两部分——一个有数据增强和没有数据增强的模型。一次仅使用一对train_datagenvalid_datagen:

import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')

import numpy as np
import tensorflow as tf
tf.random.set_seed(42)

####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0
)

# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0,
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0
)

train_data = train_datagen.flow_from_directory(
    directory='data/train/',
    target_size=(224, 224),
    class_mode='categorical',
    batch_size=64,
    seed=42
)
valid_data = valid_datagen.flow_from_directory(
    directory='data/validation/',
    target_size=(224, 224),
    class_mode='categorical',
    batch_size=64,
    seed=42
)

####################
# 2\. Model
####################
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
    tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(
    loss=tf.keras.losses.categorical_crossentropy,
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)

####################
# 3\. Training
####################
time_start = datetime.now()
model.fit(
    train_data,
    validation_data=valid_data,
    epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')

接下来我们来看一下转移学习代码。

迁移学习张量流模型——代码

大部分导入和数据加载代码是相同的。同样,一次只使用一对train_datagenvalid_datagen:

import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')

import numpy as np
import tensorflow as tf
tf.random.set_seed(42)

####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0
)

# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0,
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1/255.0
)

train_data = train_datagen.flow_from_directory(
    directory='data/train/',
    target_size=(224, 224),
    class_mode='categorical',
    batch_size=64,
    seed=42
)
valid_data = valid_datagen.flow_from_directory(
    directory='data/validation/',
    target_size=(224, 224),
    class_mode='categorical',
    batch_size=64,
    seed=42
)

####################
# 2\. Base model
####################
vgg_base_model = tf.keras.applications.vgg16.VGG16(
    include_top=False, 
    input_shape=(224, 224, 3), 
    weights='imagenet'
)
for layer in vgg_base_model.layers:
    layer.trainable = False

####################
# 3\. Custom layers
####################
x = tf.keras.layers.Flatten()(vgg_base_model.layers[-1].output)
x = tf.keras.layers.Dense(128, activation='relu')(x)
out = tf.keras.layers.Dense(2, activation='softmax')(x)

vgg_model = tf.keras.models.Model(
    inputs=vgg_base_model.inputs,
    outputs=out
)
vgg_model.compile(
    loss=tf.keras.losses.categorical_crossentropy,
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)

####################
# 4\. Training
####################
time_start = datetime.now()
vgg_model.fit(
    train_data,
    validation_data=valid_data,
    epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')

最后,让我们看看基准测试的结果。

MacBook Pro M1 Pro 与 RTX3060Ti —数据科学基准测试结果

现在,我们将比较 M1 专业版和个人电脑在定制模型架构上每个时期的平均训练时间。请记住,训练了两个模型,一个有数据增强,一个没有数据增强:

图 5 —定制模型在几秒钟内得出结果(M1 专业版:71;M1 Pro 增广:127.8;RTX 3060 ti:22.6;RTX3060Ti 增强版:134.6)(图片由作者提供)

在非增强图像数据集上,RTX3060Ti 比 M1 Pro 快 3.14 倍。出于某种原因,增强对定制 PC 产生了巨大的影响,因此 M1 专业版最终在增强数据集上每个时期的运行速度快了 7 秒。

但是现在谁会从头开始写 CNN 的模型呢?如果您的数据有限,并且您的图像不是高度专业化的,则始终建议使用迁移学习:

图 6 —几秒钟内迁移学习模型结果(M1 专业版:161.4;M1 Pro 增广:162.4;RTX 3060 ti:39.4;RTX306Ti 增强版:143)(图片由作者提供)

讨论在非增强数据集上训练的模型之间的差异是没有意义的——这是巨大的。

但如果我们考虑到数据增强,那么 M1 Pro 只比 RTX3060Ti 慢 13%。令人印象深刻的结果!请记住,RTX 是一个消耗大量电力的专用 GPU,而 M1 Pro 是一个微型笔记本电脑芯片。

我们从一开始就知道 M1 职业没有机会,但这是一个侥幸的机会。也许在拥有 32 个 GPU 核心的 M1 Max 上,情况会有所不同,但我不能说。

离别赠言

你无法真正比较笔记本电脑和台式电脑,至少不能比较它们在相同价格范围内的原始性能。MAC 和 PC 是根本不同的。作为开发人员,有很多理由喜欢 macOS。对我来说,它感觉像是一个已经完成并经过适当测试的 Linux 版本。

配备 RTX3060Ti 的台式电脑提供了与 M1 Pro MacBook Pro 截然不同的体验。两者都可以处理数据科学工作流,但 PC 提供了最大的性价比。如果你需要便携性,你甚至不会考虑 PC。两者都很棒,归结为个人喜好。

如果你有更强大的 M1 芯片,比如 24/32 核 GPU 的 M1 Max,请分享你在我的 TensorFlow 测试中得到的结果。

更多基准

保持联系

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

原载于 2022 年 1 月 31 日https://betterdatascience.com

posted @ 2024-10-18 09:31  绝不原创的飞龙  阅读(264)  评论(0)    收藏  举报