部署到 IBM Cloud Kubernetes 服务的机器学习应用程序
Python、Docker、Kubernetes
Annamária Borsos 摄影
我们将看到,与之前的文章相比,使用 IBM Cloud 创建 Kubernetes 集群非常容易。Kubernetes 丰富的资源可能会让你很难找到基本的东西。简化 Kubernetes 开发并使其易于部署的一个简单方法是使用 IBM Cloud Kubernetes Services 之类的解决方案。为了创建部署到 IBM Cloud Kubernetes 服务的机器学习应用程序,我们需要一个 IBM Cloud 帐户 ( 注册一个免费帐户 ) ,,) IBM Cloud CLI,Docker CLI,Kubernetes CLI。
在 IBM Cloud 上创建 Kubernetes 服务
IBM Cloud 上的 Kubernetes 服务提供了两种集群类型:
-一个空闲集群(一个工作池,带有一个虚拟共享工作节点,具有两个内核、4GB RAM 和 100GB SAN)
-完全可定制的标准集群(虚拟共享、虚拟专用或裸机),适用于繁重的工作。
如果我们只是想探索,自由集群是很棒的。
在 IBM Cloud 中,只需几次点击,我们就可以自动创建一个 Kubernetes 服务。首先,我们需要连接到我们的 IBM 云仪表板。
我们转到 IBM Kubernetes Service,单击 create clusters,为我们的集群键入一个名称,根据我们的帐户(付费或免费),我们可以选择适当的集群类型(在我们的示例中,我们将只创建一个具有 2 个 vCPUs 和 4 个 RAM 的工作节点),几分钟后,集群就创建好了。
来源:作者
一旦集群准备就绪,我们可以单击我们的集群的名称,我们将被重定向到一个新的页面,其中包含有关我们的集群和工作节点的信息。
来源:作者
要连接到我们的集群,我们可以单击 worker node 选项卡来获取集群的公共 IP。
来源:作者
搞定了。我们可以使用 IBM 云外壳进行快速访问。
如果我们想使用自己的终端,我们需要一些先决条件,如果还没有安装的话。我们需要安装所需的 CLI 工具:IBM Cloud CLI、Kubernetes 服务插件(ibmcloud ks)、Kubernetes CLI (kubectl)。
要安装 IBM Cloud CLI,我们将在终端中键入以下内容来安装独立的 IBM Cloud CLI (ibmcloud):
curl -fsSL [https://clis.cloud.ibm.com/install/linux](https://clis.cloud.ibm.com/install/linux) | sh
这是针对 Linux 的。您可以找到自己的发行版所需的所有命令。
我们通过在出现提示时输入我们的 IBM Cloud 凭据来登录 IBM Cloud CLI:
ibmcloud login
如果我们有一个联邦 ID,我们可以使用 IBM Cloud log in–SSO 来登录 IBM Cloud CLI。
否则,我们也可以使用 IBM Cloud API 键进行连接,如下所示:
ibmcloud login — apikey < IBM CLOUD API KEY >
如果还没有完成,我们可以创建一个 IBM Cloud API 密匙。为此,我们需要转到 IBM 云控制台,转到管理>访问(IAM)并选择 API 密钥:
来源:作者
我们可以单击 create an IBM Cloud API key,添加名称和描述,并将 API key 复制或下载到一个安全的位置。由于上面的命令,我们可以登录。
我们可以为 IBM Cloud Kubernetes 服务安装 IBM Cloud 插件(ibmcloud ks):
ibmcloud plugin install container-service
为 IBM Cloud Container Registry 安装 IBM Cloud 插件(ibmcloud cr):
ibmcloud plugin install container-registry
我们还可以安装 IBM Cloud Kubernetes 服务可观察性插件(ibmcloud ob)
ibmcloud plugin install observe-service
我们的环境中已经安装了 Kubernetes CLI。如果还没有安装,只需按照几个步骤这里。
如果我们想列出客户中的所有集群:
ibmcloud ks cluster ls
来源:作者
我们可以通过运行以下命令来检查我们的群集是否处于健康状态:
ibmcloud ks cluster get -c IBM_Cloud_node
这里, IBM_Cloud_node 是我们的集群名;您还可以使用集群的 ID。
来源:作者
机器学习应用程序的容器化
这个简单的例子将展示如何使用 Python API 和 Flask 创建一个 Docker 容器,通过一个训练有素的机器学习模型来执行在线推理。为此,我们将使用 scikit-learn 和 Iris 数据集训练一个简单的 C-支持向量分类模型,我们将把它分成训练数据和测试数据。
首先,让我们考虑以下文件:
-
Dockerfile
-
train.py
-
api.py
-
requirements.txt
你可以在 GitHub 上找到所有的文件。
train.py 是一个 python 脚本,用于加载和训练我们的模型。 Dockerfile 将用于构建我们的 Docker 映像,requirements.txt (flask,flask-restful,joblib)用于 Python 依赖关系,而 api.py 是将被调用来使用 api 执行在线推理的脚本。
train.py 文件如下:
我们还需要构建一个 API 来接收数据(X_test)并输出我们想要的东西。在我们的例子中,我们将只请求模型的分类分数:
我们现在已经准备好容器化你的烧瓶应用程序。在我们的项目目录中,我们用 jupyter/scipy-notebook 映像创建了我们的 Dockerfile ,设置了我们的环境变量并安装了 joblib 和 flask ,我们将 train.py 和 api.py 文件复制到映像中。
我们想公开 Flask 应用程序运行的端口(5000 ),所以我们使用 expose。
为了检查我们的应用程序是否运行正常,让我们在本地构建并运行我们的映像:
docker build -t my-kube-api -f Dockerfile .
来源:作者
docker run -it -p 5000:5000 my-kube-api python3 api.py
来源:作者
我们现在可以使用 curl 测试应用程序:
curl [http://172.17.0.2:5000/](http://172.17.0.2:5000/line/232)score
来源:作者
一切正常。
将映像推送到 IBM 云注册中心
有用!既然我们的应用程序工作正常,我们可以进入下一步,在 Kubernetes 集群中部署它。在此之前,我们需要将图像推送到存储库。这里,我们将在 IBM Cloud Registry(一个私有存储库)上推送映像。从我们的帐户仪表板中,我们可以选择集装箱注册:
来源:作者
我们需要使用以下命令在本地安装容器注册插件:
ibmcloud plugin install container-registry -r “IBM Cloud”
来源:作者
然后,我们登录我们的帐户:
ibmcloud login
然后,我们命名并创建我们的名称空间:
ibmcloud cr namespace-add xaviervasques
来源:作者
我们使用以下命令将本地 Docker 守护进程登录到 IBM Cloud Container 注册表中:
docker login -u iamapikey -p <YOUR API KEY> de.icr.io
我们选择一个存储库和标签,通过它我们可以识别图像:
docker tag my-kube-api de.icr.io/xaviervasques/my-kube-api:latest
而我们推送图像(docker 推送 <region_url>/ / <image_name>: ):</image_name></region_url>
docker push de.icr.io/xaviervasques/my-kube-api:latest
来源:作者
我们可以通过检查图像是否在我们的私有注册表上来验证图像的状态:
ibmcloud cr image-list
将应用程序部署到 Kubernetes
一旦图像上传到私有注册中心,我们就可以将应用程序部署到 Kubernetes。我们可以使用用户界面或 CLI。对于本章,我们将使用 CLI。我们使用上面的步骤创建我们的 Kubernetes 集群(我们也可以使用命令行创建一个:IBM cloud ks cluster create classic–name my-cluster)。要查看状态,我们键入以下命令:
ibmcloud ks clusters
来源:作者
我们的 my-k8s Kubernetes 集群已经启动并运行。我们可以将 kubectl 连接到集群:
ibmcloud ks cluster config — cluster my_k8s
我们可以检查我们是否连接到集群:
kubectl get nodes
来源:作者
我们将在主节点中创建一个名为“base”的文件夹,并在其中创建以下 YAML 文件:
o namespace.yaml
o 部署. yaml
o service.yaml
o service_port.yaml
o kustomization.yaml
namespace.yaml 文件提供了 Kubernetes 资源的范围:
这个部署. yaml 将让我们管理一组相同的吊舱。如果我们不使用部署,我们将需要手动创建、更新和删除一组 pod。这也是一种轻松自动缩放应用程序的方式。在我们的示例中,我们决定创建两个 pod(副本),加载我们之前推送的 Docker 映像,并运行我们的 api.py 脚本。
service.yaml 文件将我们在一组 Pods 上运行的应用程序作为网络服务公开。
我们还需要创建 service_port.yaml 文件:
我们创建 service_port.yaml 文件的原因是,通过使用 Kubernetes 集群中任何工作节点的公共 IP 地址并公开一个节点端口(node port ),可以通过互联网访问我们的容器化应用程序。我们可以使用这个选项来测试 IBM Cloud Kubernetes 服务和短期的公共访问。
最后,我们创建了 kustomization.yaml 文件:
我们可以配置自己的镜像 pull secret,在 Kubernetes 名称空间而不是默认名称空间中部署容器。通过这种方法,我们可以使用存储在其他 IBM Cloud 帐户中的图像,或者存储在外部私有注册表中的 us 图像。此外,我们可以创建自己的映像 pull secret 来执行 IAM 访问规则,这些规则限制对特定注册表映像名称空间或操作(如 push 或 pull)的权限。我们有几个选项可以做到这一点,其中之一是将图像获取秘密从 Kubernetes 的默认名称空间复制到我们的集群中的其他名称空间。
让我们首先列出集群中的名称空间:
kubectl get namespaces
来源:作者
然后,让我们列出 IBM Cloud Container 注册表的 Kubernetes 默认名称空间中的图像提取秘密:
kubectl get secrets -n default | grep icr-io
来源:作者
为了部署我们的应用程序,我们在主节点中使用这个命令:
kubectl apply — kustomize=${PWD}/base/ — record=true
我们将 all-icr-io 图像提取秘密从默认名称空间复制到我们选择的名称空间。新的图像获取秘密被命名为<名称空间 _ 名称> -icr- <区域> -io:
kubectl get secret all-icr-io -n default -o yaml | sed ‘s/default/mlapi/g’ | kubectl create -n mlapi -f -
我们检查秘密的创建是否成功:
kubectl get secrets -n mlapi | grep icr-io
要查看部署到此名称空间中的所有组件:
kubectl get ns
我们应该获得以下输出:
来源:作者
要查看部署的状态,我们可以使用以下命令:
kubectl get deployment -n mlapi
来源:作者
要查看服务的状态,我们使用以下命令:
kubectl get service -n mlapi
来源:作者
我们可以获得集群中一个工作节点的公共 IP 地址。如果您想要访问专用网络上的工作节点,或者如果您有一个 VPC 集群,请获取专用 IP 地址。
ibmcloud ks worker ls — cluster my_k8s
我们现在可以通过 curl 或您的 web 浏览器来使用我们的部署模型了:
curl [http://172.21.193.80:31261/score](http://172.21.193.80:31261/score)
来源:作者
我们还浏览我们的 Kubernetes 仪表盘并查看我们的服务和许多功能:
来源:作者
后续步骤
当我们致力于将机器学习/深度学习模型投入生产时,在某个时间点会出现一个问题。我在哪里部署我的代码用于训练,在哪里部署我的代码用于批量或在线推理。我们经常需要在多架构环境和混合/多云环境中部署我们的机器学习流程。我们已经了解了如何在 IBM Cloud 上部署应用程序,以及如何部署本地/虚拟机。Kubernetes 可以在各种平台上运行:从简单的集群到复杂的集群,从我们的笔记本电脑到多架构、混合/多云 Kubernetes 集群。问题是什么是最适合我们需求的解决方案。
来源
https://developer . IBM . com/technologies/containers/tutorials/scalable-python-app-with-kubernetes/
https://cloud . Google . com/community/tutorials/kubernetes-ml-ops
https://github.com/IBM/deploy-ibm-cloud-private
https://kubernetes . io/fr/docs/setup/pick-right-solution/# solutions-clés-en-main
https://www . IBM . com/cloud/architecture/tutorials/micro services-app-on-kubernetes?任务=1
https://cloud.ibm.com/docs/containers?topic = containers-registry #其他
https://cloud.ibm.com/docs/containers?topic=containers-nodeport
https://cloud.ibm.com/docs/containers
使用 U-net 进行多类图像分割的迁移学习的机器学习工程师教程
图像分割模型调试指南
从二元到多类分割任务的 U-net 模型(图片由作者提供)
图像语义分割是计算机视觉领域中最重要的研究和工程领域之一。从自动驾驶的行人和汽车分割[1]到医学图像中病理的分割和定位[2],有几个图像分割的用例。随着深度学习模型广泛用于机器学习(ML)模型的端到端交付,U-net 模型已经成为跨自动驾驶和医疗成像用例的可扩展解决方案【3–4】。然而,大多数现有的论文和方法执行二元分类任务来检测背景上的感兴趣的对象/区域[4]。在本实践教程中,我们将回顾如何从二进制语义分割任务开始,并转移学习以适应多类图像分割任务。
对于一个 ML 工程师来说,在他们的日常工作流程中最大的挫折之一就是花数小时训练一个 ML 模型,结果却毫无意义,比如输出的不是数字 (NaN)或者全是 0 值的图像。在本教程中,我们将通过一个二进制 U-net 模型扩展到多类语义分割的示例,学习从现有作品(论文/代码库)开始 ML 模型构建过程的逐步过程,并对其进行重大修改,以适应您自己的数据需求。
如图 1 所示,U-net 模型表示编码层和解码层以及编码层和各个解码层之间的跳跃连接。跳过连接的主要优点是,它将每个深度层的编码和解码结果相结合,以实现前景(需要输出为白色的像素)与背景(需要输出为黑色的像素)之间的一致分离。
图 1:深度=4 的 U 网模型的例子。左边的层是导致提取密集特征的编码器层,后面是右边的解码器层。灰色箭头表示跳过连接。来源:https://www . dlology . com/blog/automatic-defect-inspection-with-end-to-end-deep-learning/
最终目标是为新的代码库优化调整模型超参数,我们从[5]中现有的 U-net 代码库开始,该代码库使用视网膜图像数据集[6]上的二进制语义分割,以使用 DIARETDB1 数据集[7–8]修改用于视网膜图像病理学多类分类的代码库。病理学分类为亮病变(BL),由“硬渗出物”和“软渗出物”表示,红色病变由“出血”和“红色小点”表示[8]。
数据模型转换的三个主要步骤如下:1)数据准备 2)数据模型和过程 3)结果和度量。
第一步:数据准备:从二进制到多类
构建端到端 ML 模型的第一步是基准测试,这包括尽可能复制现有的代码库/文件。如果这需要修改当前数据集,使其与现有工作的数据相似,那么就应该这样做。
从[5]中的 U-net 代码库开始,对于我们手头的多类病理学分类任务,我们首先复制 U-net 代码库[5]用于单一病理学,出血检测任务。使用下面的代码将带注释的图像遮罩( Y ,表示标签)二进制化。
产生的出血标记显示在下面的图 2 中。
图 2(左):显示灰度出血遮罩(Y)。2(右)显示了通过阈值处理(Y>0.1)获得的二值化出血掩模(图片由作者提供)
医学图像数据集通常会带来“小数据挑战”的问题,即训练样本有限。为了解决这个问题,经常使用使用 Keras 的图像数据增强。这种方法的目标是在运行时生成单个图像及其遮罩的多个放大/缩小、旋转、平移/缩小等价物。下面显示的数据生成器有助于实现这一点。
当在具有很少样本的数据集上训练时,一个重要的考虑是,被训练的模型有被样本像素中的类别不平衡严重偏向的趋势。例如,[8]中的数据集包含 89 幅图像,但是只有大约 30 幅图像包含对应于病理学的大的感兴趣区域。剩下的标记图像大多是 *Y=0。*因此,如果用所有图像训练一个模型,将倾向于预测大多数图像为 Y=0 ,这意味着病理将大部分被遗漏。为了避免这个问题,并将图像数据分为训练集和测试集,我们使用图像 ID 1–27 进行训练,而图像 ID 28–89 仅用于测试。
当从二进制分段迁移到多类分段时,第一个要求是适当地格式化数据。给定原始图像 X 和标签 Y ,则 X 的维数为【m×m×r】,Y 为【m×m×d】,其中输入 U-net 模型的图像维数为 m=256,r=3 (RGB 图像),d 表示多类分类的类数(此处 d=4)。U-net 模型的要求是输入和输出必须属于相同的域/维度,在本例中为[m x m]。唯一的区别是输入 X 可以是彩色或灰度图像,而输出 Y 表示对应于每个病理掩模的二进制图像平面。因此,每个输出图像平面代表像素级的一对一分类。使用下面的代码生成一个图像遮罩示例,用于红色平面中的红色损伤(组合了“出血”和“红色小点”),绿色平面和背景蓝色平面中的明亮损伤(组合了“硬渗出物”和“软渗出物”)。
结果如下图 3 所示。
图 3:为视网膜病理学数据集生成的多类基础事实标签。红色平面代表“出血”和“红点”,绿色平面代表“硬渗出物”和“软渗出物”。(图片由作者提供)
第二步:数据模型和流程
训练最佳 ML 模型的关键任务涉及超参数优化,这涉及选择最佳参数集,以确保深度学习 U-net 模型的权重和偏差非常合适。我们为以下项目执行超参数选择:
- 编译器(Adam)
- 学习率(0.0001)
- 精度指标:要最大化的骰子系数[9]
- 损失度量:负骰子系数(要最小化)
准确性度量和损失度量的其他选项是“准确性”和“分类 _ 交叉熵”,以及“样本 _ 权重=时间”[10]以迎合数据不平衡。
另一个需要调整的模型参数是模型复杂度(即由 U-net 的大小强加的训练复杂度)。因此,需要在深 U 网(深度=4)和浅 U 网(深度=3)上进行训练,图表如下所示。参数较少的模型通常最适合“小数据挑战”。深度为 3 和 4 的模型的变化可以在[11]中看到。
图 4(左)深度=4 的 U 形网。从 Tensorboard 获得的深度=3 的 4(右)U 形网。(图片由作者提供)
在模型训练结束时,损失曲线证明了训练过程的有效性。我们实施的损耗曲线如下图 5 所示。
图 5:二元分割(橙色曲线)与多类分割(蓝色曲线)的张量板损失和准确性度量。多类计算复杂度明显高于二进制分割。(图片由作者提供)
第三步:成果和指标
一旦模型被训练,最后的任务是评估测试数据。定性地,结果如图 6 所示。
图 6:用 40 次运行后训练的模型演示二元和多类语义分割。(图片由作者提供)
对于定量评估,我们考虑以下指标:精确度、召回率、准确度、交集/并集(IoU)和 F1 分数[9] [12],如下所示在二元分割之后。
输出可以在 Github [11]中找到。多类分割中的一个关键部分是使用下面给出的多维 Dice 系数损失。
由于输出 Y 有“d”平面,第一个任务是展平平面,如图[13]和图 7 所示。,随后计算组合骰子系数。因此,多类分割的 Dice 系数在所有输出平面上是相加的,因此它可以超过值 1。
图 7:来自[13]的图像展平的例子。对于 4 维输出 Y,输出被平坦化,随后对平坦化的输出进行 Dice 系数评估。因此,Dice 系数在这种情况下是相加的。(图片由作者提供)
最后,多类分割的定量评估涉及所报告的宏观和微观级别的度量。虽然宏观水平的精度、召回率、准确度、IOU 和 F1 分数对所有类别的权重相等,但微观水平的指标在类别不平衡的情况下更可取,以提供加权结果,如[14]所示。
结论
将学习从现有的 ML 模型转移到新的数据集和用例需要一个战略性的工作流来确保最佳的数据建模。关键在于将工作流程构建为:数据、流程和结果。使用本教程中介绍的分步指南,将有可能将 U-net 模型不仅扩展到其他二元分类任务,而且扩展到多类分类任务。其他处理多类分割的代码库在[15]中。
为了进一步提高本教程中显示的性能,可以针对所有红色和明亮的病变组合更大的图像尺寸和一对一对所有分割方法。此外,为了将教程扩展到更多的类,增加的结果“Y”可以保存为。npy 文件而不是图像,并且可以使用 load 命令来加载增强数据而不是 load_img(仅输入 3D 图像数据)。)使用所提出的方法,读者现在应该具备从现有代码库学习并根据自己的需要修改它们的手段和方法,同时扩展模型和方法的范围。
参考文献
[1] Piccoli,Francesco,等人,“FuSSI-Net:意图预测网络的时空骨架融合”arXiv 预印本 arXiv:2005.07796 (2020)。
[2]罗伊乔杜里,索希尼。“减少医学图像中观察者间可变性的少数镜头学习框架.” arXiv 预印本 arXiv:2008.02952 (2020)。
[3]张、、刘庆杰、王.“用深度残差 u 网提取道路.” IEEE 地球科学与遥感快报15.5(2018):749–753。
4 拉金先生。“查看我们毕业生的最终项目”。[Online]https://blog . fourth brain . ai/check-out-our-graduates-final-projects?UTM _ campaign = Project % 20 presentation % 20 day&UTM _ source = TDS-blog&UTM _ medium = blog&UTM _ term = ml 调试指南&UTM _ content = ml-调试指南
[5]罗伊乔杜里。“使用 TF 2.x 进行医学图像分割的 Unet”【在线】https://github.com/sohiniroych/Unet-using-TF2
[6]胡佛。“凝视计划”。https://cecas.clemson.edu/~ahoover/stare/probing/index.html
[7] Roychowdhury、Sohini、Dara D. Koozekanani 和 Keshab K. Parhi。“梦:使用机器学习的糖尿病视网膜病变分析.” IEEE 生物医学和健康信息学杂志18.5(2013):1717–1728。
[8]Singh,Ramandeep 等,“糖尿病性视网膜病变:最新进展”印度眼科学杂志 56.3 (2008): 179。
[9]E Tiu。“评估您的语义细分模型的指标”。【在线】https://towards data science . com/metrics-to-evaluate-your-semantic-segmentation-model。
[10]栈溢出[在线]U-net:如何提高多类分割的准确性?
[11]罗伊乔杜里。“用于多类语义分割的 U-net”。【在线】https://github . com/sohiniroych/U-net-for-Multi-class-semantic-segmentation
12h .库马尔。对象检测和分割的评估度量:mAP[Online]https://kharshit . github . io/blog/2019/09/20/evaluation-metrics-for-object-detection-and-segmentation
[13]https://stack overflow . com/questions/43237124/what-of-the-role-of-flatten-in-keras
[14]数据科学堆栈交换。多类分类设置中的微观平均与宏观平均性能[在线]:https://data science . stack exchange . com/questions/15989/微观平均与宏观平均性能多类分类设置
[15]托尼。Unet:使用 Keras 进行多重分类【在线】https://github . com/hzc Tony/U-net-with multi-class ification
机器学习模型监控清单:要跟踪的 7 件事
如何监控您的模型以及使用哪些开源工具
图片作者。
**建立一个机器学习模型并不容易。**在生产中部署服务更加困难。但是,即使你设法把所有的管道粘在一起,事情也不会就此停止。
一旦模型投入使用,我们必须立即考虑如何平稳地操作它。毕竟,它现在正在提供商业价值!模型性能的任何中断都会直接转化为实际的业务损失。
**我们需要确保模型交付。**不只是作为一个返回 API 响应的软件,而是作为一个我们可以信任来做出决策的机器学习系统。
这意味着我们需要监控我们的模型。而且还有更多的东西要找!
如果生产中的 ML 让你措手不及,这里有一个需要注意的清单。
1.服务健康
机器学习服务还是服务。您的公司可能已经建立了一些您可以重用的软件监控流程。如果模型实时运行,它需要适当的警报和负责任的人员随叫随到。
即使您只处理批量模型,也不要例外!我们仍然需要跟踪标准的健康指标,比如内存利用率、CPU 负载等等。
我们的目标是确保服务正常运行,并遵守必要的约束条件,例如响应速度。
一个开源工具来检查: Grafana 。
2。数据质量&完整性
图片作者。
机器学习模型有问题吗?在绝大多数情况下,数据是罪魁祸首。
**上游管线和模型断裂。**用户突然改变了模式。数据可能在源头消失,物理传感器失效。这样的例子不胜枚举。
因此,验证输入数据是否符合我们的期望是至关重要的。检查可能包括范围符合性、数据分布、要素统计、相关性或我们认为对数据集“正常”的任何行为。
我们的目标是确认我们正在输入模型可以处理的数据。在它返回不可靠的响应之前。
一个开源工具查: 远大前程 。
3。数据&目标漂移
事情变了。即使我们处理非常稳定的过程。几乎每个机器学习模型都有这个不方便的特质:会随着时间退化。
当模型接收到训练中未见过的数据时,我们可能会经历 数据漂移 **。**假设用户来自不同的年龄组、营销渠道或地理区域。
数据漂移的一个例子。图片作者。
如果现实世界的模式发生变化,漂移的概念就会出现。想象一下像全球疫情这样影响所有顾客行为的偶然事件。或者市场上提供大量免费层的新竞争产品。它改变了用户对你营销活动的反应。
概念漂移的一个例子。图片作者。
这两种漂移的最终衡量标准是模型质量的下降。但是有时候,实际值还不知道,我们无法直接计算出来。在这种情况下,有先行指标可以跟踪。我们可以监视输入数据或目标函数的属性是否发生了变化。
**例如,您可以跟踪关键模型特征和模型预测的分布。**然后,如果它们明显不同于过去的时间范围,则触发警报。
截图来自显然是的报道。
我们的目标是获得世界或数据发生变化的早期信号,是时候更新我们的模型了。
一个开源工具检查: 显然是 。
4.模型性能
要知道你的模型是否运行良好,最直接的方法就是将你的预测与实际值进行对比。您可以使用模型训练阶段的相同指标,无论是分类的精度/召回率,还是回归的 RMSE,等等。如果数据质量或现实世界的模式发生了问题,我们将会看到指标下降。
截图来自显然是的报道。
这里有一些警告。
首先,事实真相或实际标签通常会有延迟。例如,如果你做一个长期的预测,或者数据传递有滞后。有时你需要额外的努力来标记新的数据,以检查你的预测是否正确。在这种情况下,首先跟踪数据和目标漂移作为早期警告是有意义的。
第二,人们不仅需要跟踪模型质量,还需要跟踪相关的业务 KPI。ROC AUC 的下降并不能直接说明它对营销转化有多大影响。将模型质量与业务度量联系起来或者找到一些可解释的代理是至关重要的。
**第三,你的质量度量应该适合用例。**例如,如果您有不平衡的类,准确性度量就很不理想。对于回归问题,您可能会关心错误符号。因此,您不仅应该跟踪绝对值,还应该跟踪误差分布。区分偶然的异常值和真正的衰退也很关键。
所以明智地选择你的衡量标准吧!
我们的目标是跟踪模型服务于其目的的程度,以及当出现问题时如何调试它。
一个开源工具来查: 显然是 。
5。按部门划分的业绩
对于许多型号,上述监控设置就足够了。但是如果您处理更关键的用例,就有更多的项目需要检查。
比如模型哪里出错多,哪里做的最好?
**你可能已经知道一些要跟踪的特定细分市场:**比如你的高端客户相对于总体客户的模型准确性。它需要一个自定义的质量指标,只为您定义的段内的对象进行计算。
**在其他情况下,主动搜索低性能的部分是有意义的。**假设您的房地产定价模型在某个特定的地理区域一贯显示高于实际的报价。这是你想要注意的事情!
根据用例,我们可以通过在模型输出之上添加后处理或业务逻辑来解决这个问题。或者通过重建模型来说明表现不佳的部分。
我们的目标是超越总体性能,了解特定数据切片的模型质量。
6.偏见/公平
当涉及到金融、医疗保健、教育和其他模型决策可能有严重影响的领域时,我们需要更加仔细地检查我们的模型。
例如,基于不同人口统计组在训练数据中的表示,不同人口统计组的模型性能可能不同。模型创建者需要意识到这种影响,并拥有与监管者和利益相关者一起减轻不公平的工具。
为此,我们需要跟踪合适的指标,比如准确率的奇偶校验。它适用于模型验证和持续生产监控。因此,仪表板上还有一些指标!
我们的目标是确保公平对待所有子群体,并跟踪合规情况。
一个开源工具来检查: Fairlearn 。
7.极端值
我们知道模型会出错。在一些用例中,比如广告定位,我们可能并不关心单个输入是奇怪的还是正常的。只要它们不构成模型失败的有意义的部分!
在其他应用程序中,我们可能希望了解每个这样的情况。为了最小化错误,我们可以设计一套规则来处理异常值。例如,让他们进行人工审核,而不是自动做出决定。在这种情况下,我们需要一种方法来检测并相应地标记它们。
我们的目标是标记模型预测不可信的异常数据输入。
一个开源工具检查: 谢顿不在场证明-检测
监控听起来可能很无聊。但是,让机器学习在现实世界中发挥作用是至关重要的。不要等到模型失败才创建你的第一个仪表板!
最初发表于https://www.kdnuggets.com并与Emeli Dral合著。
更多关于生产机器学习的内容,请查看 我们的博客 和关于 ML 监控和验证的动手 教程 。
一个主要的 Seaborn 策划技巧,我希望我能早点学会
实践教程
了解 Seaborn 中的轴级、图形级和对象级绘图
来自 Unsplash 上的詹姆斯·哈里森(@jstrippa)
Seaborn 是一个非常棒的绘图库,我希望我能在我的 Python 载体中早点开始使用它。我一直是 Matplotlib 的用户,我会花几个小时在一些项目上微调我的绘图美学,以便它们在演示过程中真正吸引同事的注意。我的第一个私家侦探总是说,如果你不出席一个有阴谋的会议,你就没有准备好,所以我总是做好准备。我一点也不知道 Seaborn 在 Python 中绘图是多么简单,尽管它比 Matplotlib 产生了更具视觉吸引力的固定绘图。
数据
我们将使用一个关于企鹅的基本的 Seaborn 数据集,其中有关于几个物种种群的各种记录信息。我们可以查看下面的数据框架:
pen = sns.load_dataset('penguins')pen.head()
企鹅数据集
图形级和轴级功能
Seaborn 的代码基础有一个平面名称空间。Seaborn 中的所有功能都是从顶层访问的,但是代码仍然有层次结构。有几个模块的功能可能通过不同的方式实现相同的目标。
当我第一次开始使用 Seaborn 时,我很困惑为什么有时包装 Matplotlib 样式会改变我的 Seaborn 图,但其他时候 Matplotlib 样式不会像预期的那样工作。这是因为一些函数将数据绘制到一个matplotlib.pyplot.Axes
对象上,而其他函数通过一个 Seaborn 对象与 Matplotlib 接口。这些分别被称为轴级和图形级功能。
在层次结构中,轴级功能位于图形级功能之下。例如,sns.displot()
是一个图形级功能,包含了histplot
、kdeplot
、ecdfplot
和rugplot
轴级功能。Seaborn 中的每个模块都有一个图形级函数,可以创建底层轴级函数的任何类型的绘图。然而,正因为displot()
可以创建所有四种类型的图,使用图形级功能或更具体的轴级功能有其优点和缺点。
看看如何创建一个简单的 KDE 图,显示按物种划分的喙深度的核心密度:
图级
fig_lvl = sns.displot(data=pen, x='bill_depth_mm', hue='species', kind='kde', fill=True)
数字级 KDE 图
数字级别类型
轴水平
fig_ax = sns.kdeplot(data=pen, x='bill_depth_mm', hue='species', shade=True)
轴级 KDE 图
坐标轴图形类型
这两幅图看起来非常相似。我们可以看到图级函数以 plot 的kind
作为自变量。他们也生产两种不同的物品。
另外,请注意为了给它们加阴影,我使用了不同的关键字参数。有了sns.displot()
,我需要fill=True
,而sns.kdeplot()
需要shade=True
;使用sns.kdeplot()
时,关键字fill
用纯色填充曲线,而不是更不透明的阴影曲线。寻找sns.displot()
的关键字参数可能很困难,因为它适应多种类型的图*。*
考虑
造型
当我们使用轴级绘图时,我们实际上是在使用一个看起来很漂亮的 Matplotlib 对象。这给我们留下了大量的 Matplotlib 功能的进一步定制工作,许多人已经习惯于使用这些功能。
另一方面,Seaborn 样式可能更复杂,因为每种体形类型下都存在多种类型的绘图。由于这种内部层次结构,像sns.displot()
这样的函数的文档字符串不会包含所有可用的参数,因为有些参数只适用于某些类型的绘图。然而,一旦你习惯了为 Seaborn figures 寻找关键词的困难,它们就能很好地工作,并提供一些非常有用的工具。
兼容性
当我们创建一个 Seaborn 图形级别的绘图时,它是在自己的轴上绘制的,该轴是在创建时启动的。这意味着我们不能在现有的轴上使用 Seaborn 图形级别的绘图。然而,Seaborn 有一些锦囊妙计来轻松创建网格图,我们将在下面讨论。
轴级绘图是 Matplotlib 对象,可以像任何其他 Matplotlib 图形一样对待。它们可以被捆绑成一个用 Matplotlib 制作的支线剧情网格,当你制作大量不同的剧情时,这真的很有用。除了更容易遵循文档,Matplotlib 兼容性是轴级绘图的最大优势之一。
让我们看看在使用 Matplotlib 子图时,如何使用 Seaborn 轴级函数:
fig, ax = plt.subplots(1,2,figsize=(8,4), gridspec_kw=dict(width_ratios=[4,3]))sns.scatterplot(data=pen, x='bill_depth_mm',y='body_mass_g', hue='species', ax=ax[0])sns.kdeplot(data=pen, x='bill_depth_mm', hue='species', shade=True, ax=ax[1])fig.tight_layout()
Matplotlib 子图中的 Seaborn 轴级图形
更改图形大小和标记轴
我经常对一个新的地块做的头两件事是使它和我的其他地块大小一致,并标记轴。使用 Matplotlib,调整图的大小和标记轴是许多人熟悉的任务,虽然 Seaborn 不同,但它仍然非常简单。
轴级
轴级绘图使用标准的 Matplotlib 图形实例化来确定大小,并使用 Matplotlib 命令来设置每个轴标签。
plt.figure(**figsize**=(12,6))fig_ax = sns.histplot(data=pen, x='bill_depth_mm', hue='species', multiple='stack')plt.**xlabel**('Bill Depth (mm)')
用轴设置标签和大小-标签图
图级
当创建图形和 Seaborn 自己的轴标签函数时,图形级绘图使用height
和aspect
参数。
fig_lvl = sns.displot(data=pen, x='bill_depth_mm', hue='species', multiple='stack', kind='hist', **height**=5, **aspect**=2)fig_lvl.**set_axis_labels**("Bill Depth (mm)")
使用图形级别的绘图设置标签和大小
带有 Seaborn 数字级情节的支线情节
我们可以使用 Seaborn 创建一些非常有用的绘图网格,通过分类变量自动分隔日期。用 Matplolib 做这件事可能要做更多的工作,包括设置数据子集和为每个轴创建单独的图。
fig_lvl = sns.relplot(data=pen, x='bill_depth_mm', y='body_mass_g', **col**='species', kind='scatter')fig_lvl.set_axis_labels("Bill Depth (mm)", "Body Mass (g)")
图级散点图很容易被分类变量划分
将 Matplotlib 与子情节一起使用
这种绘图也可以用 Matplotlib 完成,但是需要更多的思考。我是这样做的:
species = pen['species'].unique()
fig, ax = plt.subplots(1,3,figsize=(16,6))for a, spec_name in zip(ax,species):
df = pen[pen['species'].str.match(spec_name)]
a.scatter(x=df['bill_depth_mm'], y=df['body_mass_g'],s=15)
a.set_xlim([10,23])
a.set_ylim([2000,6500])
a.set_title(spec_name)
带有 Matplotlib 的支线剧情
我选择使用一个 for 循环来遍历每个轴和每个物种名称。通过这种方式,您可以设置 x 和 y 边界,而不必为每个轴重复相同的代码。这比 Seaborn 需要更多的工作和考虑。我必须手动按物种划分子集,制作正确数量的支线剧情,并对所有数据设置合理的 x/y 限制;Seaborn 自动完成所有这些工作!相当不错。
对象级绘图
当您使用图形级函数sns.displot()
来创建直方图时,对象类型是seaborn.axisgrid.FacetGrid
,这意味着它是一个与要显示的 Matplotlib API 接口的 Seaborn FacetGrid
对象。我们还可以直接使用对象来创建一个图,这将打开进一步的定制。
fig_obj = sns.FacetGrid(pen, col='species', margin_titles=True, height=6)fig_obj.map(plt.scatter, "bill_depth_mm", "body_mass_g", s=30)fig_obj.set(xlim=(10,23),ylim=(2000,6500))fig_obj.set_axis_labels("Bill Depth (mm)", "Body Mass (g)", fontsize=14)
对象级绘图
这个散点图看起来和我们的图形级sns.relplot()
图一样,但是通过 Seaborn FacetGrid
对象利用了plt.scatter
。这是使用 Matplotlib 实现这种绘图的更简单的方法。要使用 Matplotlib 语法进一步调整您的绘图,您可以向map()
调用添加更多的关键字参数,或者使用fig_obj.set()
函数。您也可以使用类似fig.et_axis_labels()
的 Seaborn 图形级绘图函数。
对象提供了灵活性
对于 Seaborn 对象,我们可以做更多的事情。它们更难使用;通常,您可能需要打开两页(或更多页)的文档才能获得您正在寻找的所有参数。但是这些额外的工作得到了一些很好的回报,比如通过数据变量实现简单的分面。
fig = sns.FacetGrid(pen, col="island", hue="species", aspect=.75, height=3.5)fig.map_dataframe(sns.scatterplot, x="bill_depth_mm", y="body_mass_g")fig.set_axis_labels("Bill Depths (mm)", "Body Mass (g)")fig.add_legend()
使用 sns.scatterplot 的 Seaborn FacetGrid 图
包扎
这将节省您理解 Seaborn 中存在的功能层次的时间。大多数情况下,轴级函数是最直接的选择。文档更容易查找和遵循,如果您决定将它们包含在其他 Matplotlib 图中,它们也是兼容的。
正如我们通过使用relplot()
按照上面的分类变量分割一个图所看到的,Seaborn figures 使得一些可视化的创建比 Matplotlib 简单得多。因此,虽然大多数基本绘图可以创建为 Matplolib axes 对象,但也有使用 Seaborn Figure 对象的良好用例。Seaborn 可能旨在为用户提供简单性,但它具有高级功能,有助于完善您在 Python 中的绘图目标。
证明文件
- 海博恩
- Matplotlib
声明:本文所有情节均由我为此而作。
数学爱好者的隐马尔可夫模型指南
它们是如何工作的,以及它们为什么被“隐藏”起来。
隐马尔可夫模型可以用于研究这样的现象,其中只有一部分现象可以被直接观察到,而其余部分不能被直接观察到,尽管它的影响可以在所观察到的事物上感觉到。未观察到的部分的影响只能估计。
我们使用两个随机过程的混合来表示这种现象。
这两个过程之一是一个“可见过程”。它用于表示现象的可观察部分。使用合适的回归模型,如 ARIMA 、整数泊松模型或曾经流行的线性模型,对可见过程进行建模。无法观察到的部分由一个“隐藏过程表示,其中使用马尔可夫过程模型建模。
如果你是马尔可夫过程的新手,请阅读下面的文章,然后回到这里继续阅读:
一个隐马尔可夫模型的真实例子
让我们来说明如何使用隐马尔可夫模型来表示真实世界的数据集。
下图显示了美国的月失业率:
美国月度失业率(数据来源:美国弗雷德在公共领域许可下)(图片由作者提供)
上图显示了大范围的正增长和负增长区域。我们假设有一些观察者不知道的隐藏过程在起作用,在两种“制度”之间摇摆不定,当前的制度实际上正在影响观察到的通货膨胀率趋势。
在对上述数据集建模时,我们将考虑一个回归模型,它是以下两个随机变量的混合**😗*
- 可观察的随机变量 y_t, 用于表示失业率的可观察模式。在每个时间步 t , y_t 就是在 t 的失业率的观察值。
- 一个隐藏的随机变量s _ t它被假定改变它的状态或制度,而每次制度改变,它都会影响观察到的就业模式。换句话说,值 s_t 的变化会影响 y_t 的均值和方差。这是隐马尔可夫模型背后的主要思想。
我们来看看如何精确表达 y_t 和 s_t 之间的这种关系。现在,让我们假设 s_t 在两种状态 1 和 2 之间切换。
重要的问题是:为什么我们称 s_t 为‘隐藏’随机变量?
我们称之为隐藏的,因为我们不知道它何时改变其状态。如果我们知道在每一个时间点上哪个机制是有效的,我们将简单地使 s_t 成为一个回归变量,我们将在 s_t 上回归 y_t !
隐藏随机变量 s_t 的公式
对于失业数据集,我们将假设 s_t 服从具有以下状态转移图的两状态马尔可夫过程:
一个 2 态马尔可夫过程(图片由作者提供)
上面所示的马尔可夫链有两种状态,或者说是编号为 1 和 2 的状态。在这两种状态之间有四种可能的状态转换:
- 状态 1 到状态 1: 这个转换以概率*p11 发生。
从而 p_11= P(s_t=1|s_(t-1)=1)。*这被解读为系统在时间 t 处于状态 1 的概率,假定其在前一时间步 (t-1)处于状态 1 。 - 状态 1 到状态 2 带转移概率:
P _ 12 = P(s _ t = 2 | s _(t-1)= 1)。 - 状态 2 到状态 1 带转移概率:
P _ 21 = P(s _ t = 1 | s _(t-1)= 2)。 - 状态 2 到状态 2 带转移概率:
P _ 22 = P(s _ t = 2 | s _(t-1)= 2)。
由于马尔可夫过程需要在每个时间步都处于某种状态,因此:
p11 + p12 = 1,和,
p21 + p22 = 1
状态转移矩阵 P 让我们以如下紧凑矩阵形式表达所有转移概率:
2 态马尔可夫过程的状态转移矩阵(图片由作者提供)
P 包含根据当前状态转移到下一个状态的概率。
状态概率向量 π_ t 包含在时间 t 处于某一状态的无条件概率。对于我们的 2 步马尔可夫随机变量 s_t, 状态概率分布 π _t ,(又称为δ*_ t****)***由以下二元向量给出:
马尔可夫变量的状态概率分布 s_t(图片由作者提供)
可以看出,如果我们以 s_t 的某个先验(初始)概率分布为0开始,那么*【π_*t可以通过简单地将 P 与自身 t 的次数相乘 的矩阵来计算
给定 t=0 时的概率分布和转移矩阵 P (图片由作者提供),马尔可夫过程在 t 时的状态概率分布公式
**这样我们就完成了马尔可夫分布随机变量 s_t 的公式化。记得我们假设 s_t 是隐藏变量。
让我们暂停一下,提醒自己两件我们不知道的重要事情:
- 我们不知道从一种状态转换到另一种状态的确切时间步骤。
- 我们也不知道跃迁概率 P 或状态概率分布*π_*t。
因此,我们到目前为止所做的是假设存在一个以随机变量 s_t 为特征的两态马尔可夫过程,并且 s_t 正在影响以随机变量 y_t 为特征的观察到的失业率。**
可观测时间序列变量 y_t 的公式
让我们假设没有影响失业率的隐马尔可夫过程。基于这一假设,让我们为失业率构建以下回归模型:
y_t 表示为平均值和误差项之和(图片由作者提供)
我们说的是在任意时刻 t ,观察到的失业率 y_t 是建模均值μ_ cap _ t 和残差** ε_t 之和。建模均值 μ_cap_t 是回归模型对 t 时刻失业率的预测。残差 ε_t 就是从观测速率中减去预测速率。我们将互换使用术语建模含义和预测含义。**
我们将进一步假设我们已经使用了一个非常好的回归模型来计算模型化的均值 μ_cap_t. ,因此,残差 ε_t 可以被假设为,即其方差不随均值变化,此外, ε_t 正态分布在一个零均值和一些方差σ周围。在符号形式上, ε_t 是一个 N(0,σ ) 分布随机变量。****
**现在让我们回到 μ_cap_t 。由于 μ_cap_t 是回归模型的预测值,所以 μ_cap_t 实际上是某个回归函数 η(。)这样说:
μ_cap_t = η(。)
**η的不同选择(。)会产生不同族的回归模型。
例如,如果 η(。)= 0 ,我们得到 白噪声模型 : y_t = ε_t.
如果 η(。)是所有观测值的常数均值y _ barI .e .:
y _ bar =(y _ 1+y2+…+y _ n)/n, 我们得到一个 均值模型:y _ t = y _ bar。
一个更有趣的模型可能依赖于一组“ m+1 ”数量的回归系数β_ cap*=【β_ cap _ 0,β_cap_1,β_cap_2,…,β_ cap _ m】,这些回归系数将因变量y链接到回归变量的矩阵*******
回归变量矩阵 X 和系数向量 β (图片由作者提供)
在上图中, X 中第一列 1 作为回归 β_cap_0 拟合截距的占位符。“cap”符号表示它是训练模型后系数的拟合值。而X***_ t是在时间 t 的一排 X 。***
如果链接函数η()x***_ t,【β_ cap】)是线性的,就得到线性模型。如果链接函数是指数函数,则得到 泊松 , NLS 等。回归模型等等。***
让我们仔细看看线性模型,其特征在于以下一组等式:
由建模平均值组成的线性模型,表示为回归变量的线性组合(图片由作者提供)
冒着使残差 ε_ t 相关的风险,也允许在x***_ t、中引入 y_t 的滞后值,如下所示:***
模型平均值表示为回归变量的线性组合,包括因变量 y_t 的滞后副本。(图片由作者提供)
如果你想知道,不,上述模型是不是一个自回归模型在 ARMA 的意义上。稍后,我们将看看“真实的”AR(1)模型是什么样子的。**
我们的模型说明是不完整的,除非我们在上述模型中还指定了 y_t. 的概率(密度)函数,我们将假设 y_t 是具有平均值 μ_cap_t 的正态分布,并且恒定方差σ:
y 的概率密度函数(图片作者提供)
上述等式应理解为:在回归变量向量_ t和拟合系数向量 β_cap 的条件下,失业率在时间 t 为 y_t 的概率(密度)正态分布,具有恒定方差σ和条件均值μ_ cap _ t******
预测平均值表示为回归向量 x _t 和拟合系数向量 β_cap 的点积(图片由作者提供)
这就完成了 y_t 的可视化流程的制定。
现在让我们将隐马尔可夫过程和可见过程“混合”成一个隐马尔可夫模型。
混合隐马尔可夫变量 ST 和可见随机变量 y t
理解隐马尔可夫模型的关键在于理解可见过程的建模均值和方差是如何受隐马尔可夫过程影响的。
下面我们将介绍马尔可夫变量 s_t 影响 μ_cap_t 和σ的两种方式。
马尔可夫转换动态回归(MSDR)模型
假设我们将回归模型定义如下:
均值模型,其中均值根据基础马尔可夫过程的状态在不同值之间切换(图片由作者提供)
在上面的等式中,我们说模型的预测均值根据潜在马尔可夫过程变量 s_t 在时间 t 处的状态而变化。
和前面一样,预测均值 μ_cap_t_s_t 可以表示为某个链接函数 η(.),即μ_cap_t_s_t = η(。).
**对于这个模型,我们定义 η(。)如下:
预测平均值表示为大小为[n x 1]的 1 向量和大小为[1 x 1]的系数向量的点积(图片由作者提供)
上面的等式是点积x _ tβ_ cap的一个特殊的简单情况,这里不涉及回归变量。因此X*_ t是一个大小为【1 X 1】的矩阵,包含数字 1 ,正如我们前面看到的,它是回归的截距 β_0 的占位符。β_ cap也是一个【1×1】矩阵,只包含回归的截距 β_0_s_t. 两者的点积就是标量值 β_0_s_t,它是截距在时间 t 处于马尔可夫状态 s_t 下的值*****
如果我们假设马尔可夫过程在一组 k 状态【1,2,3,…j,…,k】上运行,则更容易将上述等式表达如下:
可视过程的预测均值根据隐马尔可夫过程所处的状态在不同的值之间切换(图片由作者提供)
回归模型的方程式如下:
均值模型,其中均值根据隐马尔可夫过程所处的状态在不同值之间切换(图片由作者提供)
上式中, y_t 为观测值, μ_cap_t_j 为马尔可夫过程处于状态 j 时的预测均值, ε_t 为回归的残差。
在我们的失业率数据集中,我们假设 s_t 在两种状态 1 和 2 之间切换,这为 μ_cap_t_j 提供了以下规格:
可视过程的预测均值在两个值之间切换,这取决于隐藏的 2 状态马尔可夫过程处于哪个状态(图片由作者提供)
这又产生了用于 y_t 的混合过程,该混合过程在两种方式 μ_s_1 和 μ_s_2 之间切换,如下所示:****
受两状态隐马尔可夫过程影响的均值模型(图片由作者提供)
y_t 对应的两个条件概率密度如下:
y_t 的两个概率密度函数对应于隐马尔可夫过程的两个 2 态(图片由作者提供)
但是每个观察到的 y_t 应该只有一个概率密度与之相关联。
我们将使用 全概率 定律来计算这个单一密度,该定律规定,如果事件 A 可以与事件 A1、事件 A2 或事件 A3 等成对地联合发生,那么 A 的无条件概率可以表示如下:
全概率定律(图片由作者提供)
这是一种图形化的看待方式。有“n”种不同的方式到达节点 A:
事件 A 发生的 n 种不同方式(图片由作者提供)
使用该定律,我们得到在时间 t 观察到特定失业率 y_t 的无条件概率密度如下:
一个 2 态马尔可夫模型影响下的 y 的无条件概率密度(图片作者提供)
敏锐的读者可能已经注意到,在上面的等式中,我们混合了概率和概率密度,但是在这里也可以这样做。以求和形式书写的上述等式如下:
一个 2 态马尔可夫模型影响下的 y 的无条件概率密度(图片由作者提供)
上式中,概率 P(s_t=1) 和 P(s_t=2) 是两态马尔可夫过程的状态概率π****_ t1和π***_ T2*😗*****
2 状态马尔可夫过程的状态概率向量(图片由作者提供)
我们已经知道,为了计算状态概率,我们需要假设一些初始条件,然后使用下面的等式:
给定 t=0 时的概率分布和转移矩阵 P (图片由作者提供),马尔可夫过程在 t 时的状态概率分布公式
其中π*_ 0为初始值 t=0 , P 为状态转移矩阵:*****
2 态马尔可夫过程的状态转移矩阵(图片由作者提供)
上面的模型是所谓的马尔可夫转换动态回归(MSDR) 模型家族的一个简单例子。****
评估和培训
训练该模型包括估计以下变量的最佳值:
- 状态转移矩阵 P ,即本质上的转移概率p11和p22、****
- 我们样本数据集中与两个预测失业率水平、和相对应的状态特定回归系数 β_cap_0_1 和β_cap_0_2,
- 恒定方差σ。
可以使用最大似然来进行估计,其找到、β_cap_0_1* 、 β_cap_0_2 (使用 β_cap _s 矩阵和σ的值,这些值将最大化观察整个训练数据集 的联合概率密度换句话说,我们希望最大化以下产品:*******
观察数据集的可能性(图片由作者提供)
在上面的乘积中,概率密度f(y= y _ t)由我们前面看到的等式(1)给出。
最大化该乘积的自然对数通常更容易,这有利于将乘积转换成总和。因此,我们最大化以下对数-可能性:**
观察数据集的对数似然性(图片由作者提供)
对数似然性的最大化是通过对对数似然性 w.r.t .的每个参数 p_11 、 p_22 、 β_cap_0_1 、 β_cap_0_2 和σ取偏导数来完成的,将每个偏导数设置为零,并使用诸如 Newton-Raphson 、 Nelder-Mead 、等一些优化算法来求解五个方程的结果系统
马尔可夫转换动态回归模型(一般情况)
MSDR 的一般方程可以表述如下:
用预测均值 μ_cap_tj 和残差 ε_t 之和表示的观测值 y_t(图片由作者提供)
其中, μ_cap_t_j 是回归变量矩阵x*_ t和特定体制拟合系数向量β_ cap**_ j的函数。即、*****
μ_ cap _ t _ j =η(x_ t,β_ cap_ j)
但是这一次,注意回归系数向量被称为β_ cap*_ j对应于第 j 个马尔可夫状态。*****
如果马尔可夫模型对’ k’ 状态【1,2,…,j,…,k】, β_cap_s 进行运算,则大小为 [(m+1) X k] 的矩阵如下:**
大小为[(m+1) x k]的系数矩阵(图片由作者提供)
中心思想是,根据[1,2,…,k 中的哪个马尔可夫状态或“状态” j *当前有效,回归模型系数将从 β_cap_s 切换到适当的状态特定的向量β_ cap***_ j因此得名‘马尔可夫切换动态回归模型’。********
k-状态马尔可夫过程本身由以下状态转移矩阵 P 控制:
马尔可夫过程的状态转移矩阵 P (图片作者提供)
并且在时间步长 t 具有以下状态概率分布 π_ t :
k 状态马尔可夫过程的状态概率分布向量(图片由作者提供)
到目前为止,我们假设 y_t 的线性指定条件均值函数如下:
正态分布 y_t 的条件均值,表示为回归变量 x t、y_t 的滞后版本和马尔可夫状态相关拟合系数 ***β_cap j 的线性组合(图片由作者提供)***
上式中, μ_cap_t_j 是马尔可夫状态 j 下 t 时刻的预测均值。x_ t是回归变量【x _ 1t,x_2t,…,x _ mt】在时间 t 的向量,β_ cap_ j是状态特定系数的向量【β_ cap _ 0j,β_cap_1j,β_cap_2j,…,β_cap_mj****
正如我们在 2 态马尔可夫情况下所看到的(参考等式)。1),这为 y_t 产生正态分布的概率密度如下:
以 k 态马尔可夫分布随机变量 s_t 影响下的 y_t 的 β_cap_s 和 P 为条件的概率密度函数(图片由作者提供)
等式(2)只是等式(1)在 k 马尔可夫状态上的扩展。
y_t 不用正态分布。实际上,假设 y_t 代表一个整数随机过程即 y_t 取值 0,1,2,…等。这种过程的例子是纽约市每天的机动车事故数量,或者网站每小时的点击数。这种过程可以使用 泊松 过程模型来建模。在这种情况下,泊松分布 y_t 的概率质量函数采用以下形式:
k 状态马尔可夫过程模型影响下的泊松分布 y_t(图片由作者提供)
其中,特定于制度的均值函数表示如下:
上式中,x_ t和β_ cap**_ j与线性均值函数含义相同。**
训练和评估
马尔可夫转换动态回归模型的训练包括下列变量的最优值的估计:
模型的系数:
大小为[(m+1) x k]的系数矩阵(图片由作者提供)
状态转移概率:
马尔可夫过程的状态转移矩阵 P (图片由作者提供)
和方差σ。
如前所述,估计过程可以是最大似然估计* ( MLE ),其中我们求解一个 (k +(m+1)k +1) 方程系统(实际上,比那个数少得多),对应于 k 马尔可夫转移概率、 (m+1)k 系数 β_cap_s 和方差σ。*
*In an upcoming article, we’ll look at how to build and train both **Linear and Poisson MSDR models** using **Python** and the **statsmodels** library.*
现在让我们看看另一种类型的隐马尔可夫模型,称为马尔可夫切换自回归(MSAR)* 模型。***
马尔可夫转换自回归(MSAR)模型
考虑以下月失业率的模型方程:
在隐马尔可夫过程影响下运行的 AR(1)模型(图片由作者提供)
这里,我们说的是在时间 t 的失业率围绕特定制度的平均值 μ_t_s_t 波动。波动是由两个分量的总和引起的:
- 第一个分量代表在前一时间步的观测值与在前一时间步的拟合状态特定平均值的偏差分数,**
- 第二个分量来自残差 ε_t 。
与 MSDR 模型一样,隐马尔可夫过程的状态会影响模型的拟合均值。
注意,该模型不仅依赖于时间 t 的状态值,还依赖于前一时间步 (t-1) 的有效状态。
上述规范可以很容易地扩展到包括过去的 p 时间步长,从而得到遵循 AR§ 设计模式的 MSAR 模型。
模型说明和评估
MSAR 模型的模型规范的一般框架,包括 y_t 的概率密度函数的规范和估计程序(MLE 或期望最大化)与 MSDR 模型保持一致。
不幸的是,在前面的步骤中,模型对马尔可夫状态的依赖使规范过程和估计变得相当复杂。
我们不会在这里深入讨论这些细节,但是与 MSDR 模型一样,我们将在下一篇文章中研究如何用 Python 和 statsmodels 构建和训练 MSAR 模型。
参考文献和版权
数据集
美国劳工统计局,失业率[UNRATE],从圣路易斯美联储银行检索;https://fred.stlouisfed.org/series/UNRATE, 2021 年 10 月 29 日。在公共许可证下可用。
书
Cameron A. Colin,Trivedi Pravin K ., 对计数数据的回归分析 ,计量经济学学会专论№30,剑桥大学出版社,1998 年。国际标准书号:0521635675
詹姆斯·d·汉密尔顿, 时间序列分析 ,普林斯顿大学出版社,2020 年。ISBN: 0691218633
形象
所有图片的版权均为 Sachin Date 下的 CC-BY-NC-SA 所有,除非图片下方提到了不同的来源和版权。
相关阅读
***
感谢阅读!如果你喜欢这篇文章,请 关注我 获取关于回归和时间序列分析的技巧、操作和编程建议。***
支持向量机的数学解释
加深对最流行的机器学习模型之一的理解
图片由来自 Pixabay 的 Gerd Altmann 提供
支持向量机(SVM)是数据科学领域最流行的机器学习模型之一。直觉上,这是一个相当简单的概念。然而,从数学上来说,支持向量机看起来就像一个黑盒。
在这篇文章中,我有两个目标:
- 我想揭开支持向量机背后的机制,让你更好地理解它的整体逻辑。
- 我想教你如何用 Python 实现一个简单的 SVM,并使用https://www.gradio.app/部署它。最后,您将能够构建这样的东西:
作者创建的 GIF
说到这里,让我们开始吧!
复习:什么是支持向量机?
作者创建的图像
支持向量机(SVM)是一种监督分类技术。支持向量机的本质只是找到一个区分不同类别的界限。
- 在二维空间中,边界称为直线。
- 在三维空间中,边界称为平面。
- 在任何大于 3 的维度中,该边界称为超平面。
让我们假设有两类数据。支持向量机会找到一个边界,使两个类之间的间隔最大化(见上图)。有许多平面可以分隔这两个类别,但只有一个平面可以最大化类别之间的边距或距离。
注释
n =数据点数
m =属性数
x_ij =第 j 个数据点的第 I 个属性
如果数据点是蓝色,y _ j = 1;如果数据点是红色,y _ j = 1
数学
读完这篇文章后,你会明白上面的等式想要达到什么目的。如果看起来很混乱也不用担心!我会尽我所能一步一步地分解它。
请记住,这涵盖了基本支持向量机的数学,不考虑内核或非线性边界之类的东西。
分解一下,我们可以把它分成两个独立的部分:
作者创建的图像
红色部分:红色部分专注于最小化错误,即 SVM 产生的错误分类点的数量。
蓝色部分:蓝色部分着重于最大化利润,这在本文前面已经讨论过了。
先说蓝色部分,或者说方程的第二项。
第二项:利润最大化
正如我前面所说的,您可以在两类数据点之间放置许多边界,但是只有一个边界可以最大化两类之间的边距(如上面的虚线所示)。
我们希望决策边界尽可能远离支持向量,以便当我们有新数据时,它会以更大的确定性落在这个或那个类别中。
让我们假设上面的两个等式分别代表边距的两边。不要太担心 m/-m,只要注意它们如何表示一条线的方程。
两条虚线之间的距离由下面的公式得出,你可以在这里阅读更多关于的内容:
不用那么担心这个方程是怎么推导出来的。相反,请注意,随着 1 和 m (a1,a2,… am)之间的所有 a 变小,分母变小,距离或边距变大!
现在你明白了第二个术语(蓝色部分)的意思,让我们来谈谈第一个术语(红色部分)。
第一项:最小化误差
实际上,你不可能找到一个超平面,完美地将不同的类彼此分开。即使它存在,也不总是你想要使用那个超平面。
考虑下图:
从技术上讲,我们可以设置边界,使红色和蓝色类位于边界的右侧。然而,假设左边的蓝色方块是一个异常值,那么拥有一个具有较大裕度的不完美超平面可能更理想,这被称为软裕度**😗*
既然我们已经介绍了“误差”的概念,你应该明白支持向量机的完整等式是试图最小化误差,同时最大化余量**。**
既然我们已经理解了第一个术语背后的目标,让我们重新审视这个等式:
用英语来说,这个等式说的是“取各点误差之和”。
那么^这部分方程是如何表示每个点的误差的呢?让我们深入探讨一下:
我们把 m 和-m 分别设为 1 和-1。实际上,它们可以是任何数字,因为 m 是裕度的比例因子。
因为如果数据点是蓝色的,y_j = 1,如果数据点是红色的,y _ j = 1,所以我们可以组合上边界的方程和下边界的方程来表示所有点:
这可以重写为以下内容:
这个等式假设所有的点都被分类在等式的右边。对于边界错误一侧的任何一点,它都不满足方程。
对你们中的一些人来说,我敢打赌,你脑袋里的灯泡已经亮了。如果没有,不用担心!我们快结束了。
记得我说过这个方程“取每个点的误差之和。”具体来说,就是取零的最大值,第二部分。规则如下:
让这个代表 Z
让上面的等式代表 z。
- 如果给定点在线的右侧,那么 Z 将大于 1。这意味着第一项的第二部分将是一个负数,所以给定点将返回 0(没有错误)
- 如果给定点在线的错误一侧,那么 Z 将小于 1。这意味着第一项的第二部分将是一个正数,因此给定点将返回一个大于 0 的值(错误)。
结论
作者创建的图像
就是这样!总之,支持向量机的目标是通过最小化 a_i 来最小化总误差和最大化裕度。
下一步是什么?建造 SVM。
既然您已经理解了支持向量机背后的数学,下一步就是用 Python 实际构建一个支持向量机模型并部署它!
我将使用经典的 iris 数据集来展示如何用 Python 构建支持向量机(完整代码见 此处 )。
设置
开始之前,您需要安装以下库:
- Numpy
- 熊猫
- 海生的
- Sklearn
- 格拉迪欧
**# Importing libraries**
import numpy as np
import pandas as pd
import seaborn as sns**# Importing data**
iris=sns.load_dataset("iris")
数据准备
from sklearn.model_selection import train_test_split**# Splitting features and target variables**
X=iris.drop("species",axis=1)
y=iris["species"]**# Splitting data into train and test sets**
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
数据建模
from sklearn.svm import SVC**# Creating model**
model = SVC(probability=True)
model.fit(X_train,y_train)
使用 Gradio 创建 Web 应用程序
**# Writing a prediction function**
def predict_flower(sepal_length, sepal_width, petal_length, petal_width):
df = pd.DataFrame.from_dict({'Sepal Length':[sepal_length],
'Sepal Width': [sepal_width],
'Petal Length': [petal_length],
'Petal Width': [petal_width]
})predict = model.predict_proba(df)[0]
return {model.classes_[i]: predict[i] for i in range(3)}**# Importing gradio** import gradio as gr**# Creating Web App** sepal_length = gr.inputs.Slider(minimum=0, maximum=10, default=5, label="sepal_length")sepal_width = gr.inputs.Slider(minimum=0, maximum=10, default=5, label="sepal_width")petal_length = gr.inputs.Slider(minimum=0, maximum=10, default=5, label="petal_length")petal_width = gr.inputs.Slider(minimum=0, maximum=10, default=5, label="petal_width")gr.Interface(predict_flower, [sepal_length, sepal_width, petal_length, petal_width], "label", live=True).launch(debug=True)
现在你知道了!你应该有一个全功能的 web 应用程序,在那里你可以摆弄模型的输入,并立即看到输出概率。
作者创建的 GIF
感谢阅读!
如果你坚持到了最后,恭喜你!现在,您应该对基本支持向量机的工作原理有了很好的理解,而且您现在知道如何构建自己的全功能 SVM web 应用程序。
如果你喜欢这个,请给这个一些掌声,并在媒体上跟随我!
我一如既往地祝你学习顺利。😄
不确定接下来要读什么?我为你挑选了另一篇文章:
**
又一个!
</4-machine-learning-concepts-i-wish-i-knew-when-i-built-my-first-model-3b8ca9506451>
特伦斯·申
麦当劳与塔可钟的模拟:寻找更健康的选择
在 Unsplash 上由 Shaafi Ali 拍摄的照片
想象一下。你结束了一整天的工作,决定在回家的路上买些杂货。办完一些事情后,你看看手表:晚上 9 点。为了让自己休息一下,你决定去快餐店吃晚餐。听起来熟悉吗?我敢打赌。
我们都过着极其忙碌的生活。虽然我们都知道快餐不一定是最健康的选择,但许多人仍然利用这些设施,因为它们非常方便。我们倾向于把所有这些连锁餐馆捆绑在一起,认为它们和下一家一样不健康。然而,研究具体的营养事实是区分它们并做出更健康饮食的正确选择的关键。在这个案例研究中,我关注了两家非常受欢迎的快餐店,麦当劳和塔可钟,并回答了这个古老的问题:
从长远来看,在哪个餐馆吃饭会是更健康的选择?
模拟系统
我首先从两家餐馆拿了菜单,给每一项标上主菜、饮料、配菜、甜点、酱汁或调料。对于麦当劳的信息,我从 Kaggle 上的数据集中提取,对于塔可钟,我基于我找到的营养事实网站自己创建了数据集。我保留了每家餐厅特有的饮料,并在分析中丢弃了其余的。也就是说,可乐、雪碧等饮料。从两个数据集中删除,因为我只考虑餐厅的商标食品。该分析的范围仅包括午餐和晚餐菜单项目。
然后,我创建了四个不同的模拟场景,在这些场景中,顾客将从每家餐厅购买餐食,关键的区别在于单词“餐食”的定义。
模拟 1: 顾客单独购买一道主菜
模拟 2: 顾客购买一道主菜和一杯饮料
模拟 3: 顾客购买一份主菜、一份饮料和一份配菜
模拟 4: 顾客购买一道主菜、一杯饮料、一道配菜和一道甜点
为了使分析更加现实,我在两家餐馆的某些菜单项中创建了规定。如果随机选择沙拉作为配菜,脚本会随机选择一种沙拉酱来搭配正餐。如果鸡块或鸡条被选作主菜,那么脚本也会随机选择一种酱与之搭配。对于塔可钟主菜,我确保每个模拟随机选择两种酱来搭配每个主菜。
**对于每个模拟,这些组合的膳食被取样 10,000 次。**由此可以清楚地区分不同的工厂。让我们首先考虑一个最简单的情况,顾客单独点一道主菜。根据模拟 1,我创建了以下图:
模拟 1 结果表明单独主菜的营养分布相似。图片作者。
请注意,两个机构的平均卡路里、脂肪卡路里、胆固醇和钠含量非常相似。我很好奇这种趋势是否适用于所有模拟,这让我比较了所有模拟运行的这些特征的平均值。简而言之,答案是否定的。我创造了这个向我们展示结果的图。
所有模拟的平均值(n=10,000)。图片作者。
我们看到,在不同的试验中,营养特性会有明显的差异。模拟 1 实际上是唯一一个对两家餐馆产生看似相似结果的试验。这意味着餐馆里的饮料、配菜和甜点在营养质量上表现出更大的差异。前面提到的特点,麦当劳比塔可钟高很多。为了评估这些差异在数学上是否显著,我对所有模拟进行了非参数测试(Mann-Whitney U 测试)。可以预见的是,我在所有试验中获得了非常低的 p 值,表明了上述两家餐厅的营养特征之间的显著差异。然而,我并不认为模拟 1 会有显著的不同。
让我们更仔细地看一下模拟 4,它包括一顿全餐。在我看来,这应该是我们最重视的审判。如果我们对前 500 个样品的营养特性进行快照比较,我们会看到以下结果:
总的来说,麦当劳比塔可钟拥有更多的热量和脂肪。图片作者。
正如你所看到的,来自脂肪、胆固醇、反式脂肪和饱和脂肪的热量通常比麦当劳高。为了获得一些关于这些特征中的一些对于麦当劳有多高的定量测量,让我们在模拟 4 中观察 ECDF 对于每一餐的脂肪和胆固醇的卡路里的不同。
更大比例的麦当劳食品含有来自脂肪和胆固醇的更高热量。
正如我们所看到的,大约 37%的麦当劳食物含有超过 600 卡路里的脂肪,这是塔可钟一餐的最大值。此外,大约 60%的塔可钟餐从脂肪中摄取 220 到 410 卡路里的热量,而 60%的麦当劳餐从脂肪中摄取 420 到 650 卡路里的热量。此外,大约 77%的塔可钟餐含有低于 400 卡路里的脂肪,而只有大约 18%的麦当劳餐具有相同的质量。
从右边的图中,我们可以看到所有的塔可钟餐都没有达到大约 90 毫克的胆固醇。另一方面,只有 18%的麦当劳食品低于同样的胆固醇水平。事实上,30%的麦当劳餐含有超过 180 毫克的胆固醇——是塔可钟餐最大胆固醇值的两倍。
那么这一切意味着什么呢?
在我们做出一个轻快的结论之前,让我们考虑一下在这个分析中探索的特征的意义和获得的总体结果。每一个有 10,000 个样本的模拟都产生了两家餐馆之间实质性的和显著的营养差异。具体来说,我们观察到麦当劳在所有进行的模拟中所有特性的值都明显较高。这一定是坏事吗?
根据哈佛大学 T . H Chan 公共卫生学院的研究,由于反式脂肪对我们的血管和心脏有负面影响,所以它是最不适合食用的脂肪。这种脂肪的大量摄入会导致胰岛素抵抗、糖尿病和许多其他并发症。根据 MayoClinic 的一篇文章,从饱和脂肪中获得的最大建议热量是你每日总热量的 10%。一般来说,一个人每天要摄入大约 2000 卡路里的热量,这意味着要保持*【健康】*,脂肪中的热量上限为 200 卡路里。假设你在快餐店吃饭,你很可能会超过这个门槛。然而,在麦当劳吃一顿全餐,你会把它变成原来的三倍或四倍的可能性远远大于在塔可钟。
根据美国美国食品药品监督管理局的一篇文章,高钠饮食会导致心脏病和增加中风的风险。他们指出,推荐的每日钠摄入量少于 2300 毫克。根据模拟 4,麦当劳一餐超过这个数值,而塔可钟平均保持在 1500 毫克左右。最后,较高水平的胆固醇会因脂肪沉积而导致血管堵塞。从模拟快照和 ECDF 图中,我们看到麦当劳的平均胆固醇含量明显高于塔可钟的。
当然,在这项研究中,除了有条件地添加调料/酱料之外,我赋予单词*、【餐】*的定义将在我的模拟中引起算法偏差。在不同的条件下运行更多的模拟来增加分析的真实性是一个好主意。尽管如此,我相信我目前的结果是很有见地的!
总而言之,我的分析结果指向一个清晰的结论:从长远来看,塔可钟是更健康的选择。
如果你喜欢这篇文章,并且对更多类似的内容感兴趣,你可以关注我的 Medium。这个项目的代码可以在我的 Github 这里找到。感谢阅读!
TensorFlow 2.0 中深度 Q 学习的最小工作示例
一个多臂土匪的例子来训练一个 Q 网络。使用 TensorFlow,更新过程只需要几行代码
深度,就像深度 Q-learning 一样。克里斯·米凯尔·克里斯特在 Unsplash 上的照片
深度 Q 学习是任何强化学习(RL)实践者的武器库中的一个主要部分。它巧妙地规避了传统 Q-learning 的一些缺点,并利用神经网络的能力进行复杂值函数的逼近。
本文展示了如何在 TensorFlow 2.0 中实现和训练深度 Q 网络,并以多臂土匪问题(一个终止的一次性游戏)为例进行了说明。还提供了对时间差异学习的一些扩展。不过,我认为极小工作示例中的*【极小】*相当字面上的意思,所以重点是有史以来第一次实现深度 Q 学习。
一些背景
在深入学习之前,我假设你已经熟悉了普通 Q 学习和人工神经网络。没有这些基础知识,尝试深度 Q 学习可能会是一次令人沮丧的经历。以下更新机制对您来说应该没有秘密:
Q-learning 的更新函数[1]
传统的 Q-learning 在一个查找表中为每个状态-动作对显式存储一个 Q 值——本质上是对累积折扣奖励的估计。当在特定状态下采取行动时,观察到的奖励会提高价值评估。查找表的大小为 |S|×|A| ,其中 S 为状态空间, A 为动作空间。Q-learning 对于玩具大小的问题往往很有效,但是对于更大的问题就不行了。通常,不可能在任何地方观察到所有的状态-行为对。
在 16 格网格上移动的 Q 学习表示例。在这种情况下,有 16*4=64 个状态-动作对,应该学习它们的值 Q(s,a)。[图片由作者提供]
与普通 Q-学习相比,深度Q-学习将状态作为输入,通过多个神经网络层,并输出每个动作的 Q 值。深度 q 网络可以被视为一个函数 f:s→[Q(s,a)]_∀ a ∈ A 。通过对所有状态采用单一表示,深度 Q 学习能够处理大的状态空间。不过,它预先假定了合理数量的动作,因为每个动作都由输出层中的一个节点表示(大小 |A| )。
深度 Q 网络的例子。在本例中,输入是网格(16 个图块)的一键编码,而输出表示四个动作中每个动作的 Q 值。[图片由作者提供]
在通过网络并获得所有动作的 Q 值之后,我们照常继续。为了平衡勘探和开发,我们利用了一个基本的ϵ-贪婪政策。通过概率 1-ϵ 我们选择最佳动作(输出层上的argmax
操作),通过概率 ϵ 我们对随机动作进行采样。
TensorFlow 2.0 实施
在 TensorFlow 中定义 Q 网络并不难。输入维度等于向量状态的长度,输出维度等于动作的数量(如果可行动作的集合是状态相关的,则可以应用掩码)。Q 网络是一种相当简单的神经网络:
在 TensorFlow 2.0 中创建 3 层 Q 网络的示例代码。输入是状态向量,输出是每个动作的 Q 值。
权重更新在很大程度上也是为您处理的,但是您必须向优化器提供一个损失值。损失代表观察值和期望值之间的误差;需要一个可微分的损失函数来正确地执行更新。对于深度 Q 学习,损失函数通常是简单的均方误差。这实际上是 TensorFlow 中的内置损失函数(loss=‘mse’
),但我们将在这里使用GradientTape
功能,跟踪您的所有操作来计算和应用梯度[2]。它提供了更多的灵活性,并且贴近底层数学,这在转向更复杂的 RL 应用时通常是有益的。
均方损失函数(观察与前面提到的更新机制的相似性)表示如下:
深度 Q 学习的均方误差损失函数
深度 Q 学习方法的一般 TensorFlow 实现如下(GradientTape 在水下施展魔法):
深度 Q 学习训练程序概述。
多臂土匪
多臂土匪问题是 RL[3]中的经典。它定义了若干吃角子老虎机:每台机器 i 都有一个平均收益 μ_i 和一个标准差 σ_i. 每个决策时刻,你玩一台机器,观察由此产生的奖励。当玩得足够频繁时,你可以估计每台机器的平均回报。不言而喻,最佳策略是玩平均收益最高的老虎机。
让我们把 Q-learning 网络的例子付诸行动(完整的 Github 代码这里)。我们定义了一个具有三个完全连接的 10 节点隐藏层的简单神经网络。作为输入,我们使用值为 1(表示固定状态)的张量作为输入,四个节点(表示每个机器的 Q 值)作为输出。网络权重被初始化,使得所有 Q 值最初都是 0。对于权重更新,我们使用学习率为 0.001 的 Adam 优化器。
下图显示了一些说明性的结果(10,000 次迭代后)。勘探和开发之间的权衡是显而易见的,尤其是在根本不勘探的时候。注意,结果并不过分准确;普通 Q-learning 实际上对这类问题表现得更好。
多臂土匪问题的 q 值和真值。ϵ=0.0(左上)、ϵ=0.01(右上)、ϵ=0.1(左下)和ϵ=1.0(右下)经过 10,000 次迭代后的结果。探索越少,感知的最佳行为越接近。[图片由作者提供]
时间差异学习
多臂强盗绝对是一个最小的工作例子,但是只处理了我们没有看到直接回报以外的最终情况。让我们看看如何处理非终结的情况。在这种情况下,我们部署时间差异学习—我们使用 Q(s ‘,a’) 来更新 Q(s,a) 。
获得对应于下一个状态*s’的 Q 值本身并不难。你只需将s’*插入到 Q 网络中,并输出一组 Q 值。总是选择最大值,因为这是 Q 学习而不是 SARSA,并使用它来计算损失函数:
next_q_values = tf.stop_gradient(q_network(next_state))
next_q_value = np.max(next_q_values[0])
注意,Q-网络是在一个**stop_gradient**
运算符内调用的【4】。提醒一下,GradientTape
跟踪所有的操作,并且同样会使用next_state
输入执行(无意义的)更新。使用stop_gradient
操作符,我们可以安全地利用对应于下一个状态*s’*的 Q 值,而不用担心错误的更新!
一些实施说明
虽然上面概述的方法原则上可以直接应用于任何 RL 问题,但您会经常发现性能相当令人失望。即使对于基本问题,如果你的普通 Q 学习实现胜过你的花哨的深度 Q 网络,也不要感到惊讶。一般来说,神经网络需要许多观察来学习一些东西,并且通过为所有可能遇到的状态训练单个网络来固有地丢失一些细节水平。
除了良好的神经网络实践(例如,标准化、一键编码、正确的权重初始化),以下调整可能会大大提高算法的质量[5]:
- 小批量:不是在每次观察后更新网络,而是使用批量观察来更新 Q 网络。稳定性通常通过多次观察的训练来提高。每次观察的损失被简单地平均。
tf.one_hot
掩码可用于多个动作的更新。 - 经验重放:建立一个先前观察值的缓冲区(存储为 *s,a,r,s’*元组),从缓冲区中抽取一个(或多个,当使用迷你批处理时),并插入 Q 网络。这种方法的主要好处是消除了数据中的相关性。
- 目标网络:创建一个只定期更新(比如每 100 次更新)的神经网络副本。目标网络用于计算Q(s’,a’),而原始网络用于确定 Q(s,a) 。此过程通常会产生更稳定的更新。
外卖
- 一个深度 Q 网络是一个简单的神经网络,以状态向量作为输入,输出对应于每个动作的 Q 值。通过对所有状态使用单一表示,它可以处理比普通 Q-learning(使用查找表)大得多的状态空间。
- TensorFlow 的
GradientTape
可以用来更新 Q 网络。对应的损失函数是接近原始 Q 学习更新机制的均方误差。 - 在时间差学习中,基于*Q(s’,a’)更新 Q(s,a) 的估计值。
stop_gradient
算子确保对应于Q(s’,a’)*的梯度被忽略。 - 深度 Q 学习伴随着一些实现挑战。如果香草 Q-learning 实际上表现更好,不要惊慌,特别是对于玩具大小的问题。
使用多臂盗匪的最小工作示例的 GitHub 代码可以在 这里 找到。
想要稳定你的深度 Q 学习算法?下面这篇文章可能会让你感兴趣:
转而寻求实施政策梯度方法?请检查我的文章与连续和离散情况下的最小工作示例:
参考
[1]萨顿,理查德和安德鲁巴尔托。强化学习:简介。麻省理工学院出版社,2018。
[2] Rosebrock,A. (2020)使用 TensorFlow 和 GradientTape 来训练 Keras 模型。【https://www.tensorflow.org/api_docs/python/tf/GradientTape
[3] Ryzhov,I. O .,Frazier,P. I .和 Powell,W. B. (2010 年)。多臂土匪问题中一期前瞻策略的鲁棒性。Procedia 计算机科学,1(1):1635{1644。
[4]TensorFlow (2021)。于 2021 年 7 月 26 日从https://www.tensorflow.org/api_docs/python/tf/stop_gradient获得
[5]维基百科贡献者(2021)深度 Q-learning。于 2021 年 7 月 26 日从https://en.wikipedia.org/wiki/Q-learning#Deep_Q-learning获得
自行车共享系统再平衡的混合整数优化方法
实践教程
旅行商问题在蒙特利尔碧溪的应用
JsTremblay 在维基共享资源上的照片
时间是 2022 年。加拿大的人口已经完全接种了疫苗,在一个炎热的七月下午,蒙特利尔的公园充满了生机,比西骑手挤满了街道,在老港口的咖啡店之间穿梭,沿着拉钦运河转圈。生活又变得美好了。
BIXI 来源于单词“bicycle”和“taXI”,是一个非营利的蒙特利尔公共自行车共享系统,拥有 8000 多辆自行车,分布在岛上的 600 多个车站。BIXI 从每年的 4 月到 11 月开放,它填补了蒙特利尔公共交通系统的一个重要空白,既连接了公共汽车和地铁系统之间的用户,又为短途旅行提供了一种健康、有趣、环保的替代交通工具。客户只需从一个站点取走一辆自行车,以一定的时间和费用租用一段时间,然后返回网络中的另一个站点。然而,由于 BIXI 的成本方案是为短期出行设计的,系统的动态性质可能会造成自行车短缺发生在需求高的位置,而自行车过剩积累在需求低的位置。
战略性地重新分配自行车的能力是 BIXI 客户服务的一个关键决定因素。为了减少自行车共享系统内的潜在失衡,BIXI 的运营商(被称为“司机”)被要求使用卡车将自行车从一个站点重新分配到另一个站点,以便在必要时重新平衡系统。因此,研究 BIXI 如何通过重新平衡自行车来缓解短缺和过剩,从而满足客户不断波动的需求,具有重要的价值。这样做可以确保 BIXI 能够继续吸引更多的骑手,同时最大限度地减少由于系统失衡导致的低效率。
设计自行车再平衡策略
在这个项目中,我们旨在使用混合整数优化来生成自行车再平衡策略,以满足 BIXI 客户不断变化的需求,同时最大限度地降低运营分销系统所产生的估计成本。
简而言之,优化问题包括从一系列可用选项中选择最优选项,称为决策变量,其中最佳选项是最大化或最小化目标函数,同时满足一系列标准,称为约束。优化是运营建模的一个基本分支,具有广泛的实际应用,从安排医院人员到帮助亚马逊设计包裹递送的最佳路线。为了创建最佳的 BIXI 再平衡策略,我们的决策是找到最佳的时间表和自行车数量来进行再平衡,这将最小化目标函数,即 BIXI 的运营成本。为此,我们还需要满足系统*、施加的各种约束*,比如一辆卡车一次可以运载的自行车数量。
为了建立模型,我们使用了 Gurobi 优化求解器,这是一个用于求解线性、二次和混合整数优化问题的编程工具。Gurobi 支持多种编程语言,我们的模型是用 Python 编写的。请随意查看 Github 的代码。
数据和范围
BIXI 为 2014 年至 2019 年拍摄的所有车站和游乐设施提供公开可用的记录。这包括每个站点的地理坐标位置,以及用户所有行程的记录,例如行程日期、持续时间以及自行车在哪个站点上车和下车。我们发现,由于自成立以来碧溪的业务大幅扩张,每年的客流量和车站配置都有很大差异。因此,我们采用了 2019 年的最新可用数据作为我们模型的基础。
带有坐标位置的碧溪站名称示例-每个站都有一个唯一的代码。
2019 年 5 月碧溪乘客历史样本
BIXI 系统包括大蒙特利尔地区的 611 个站点;然而,这些车站中的大多数都位于市中心及其周围。为了节省计算时间以及实现现实可行的路线,选择了凡尔登区内覆盖 13.65 km 的 18 个站点作为感兴趣区域。每个车站都有不同数量的自行车停靠站。由于我们无法获得此信息,我们随机生成了每个码头 10-25 辆自行车的估计范围内的码头容量。为了完成凡尔登区的再平衡活动,我们假设一辆载重量为 40 辆自行车的卡车将被分配到有剩余自行车的站点,并将它们运送到有短缺的站点。
蒙特利尔凡尔登区的 18 个碧溪站用作感兴趣区域。
模型公式:
基本优化模型是通过量化再平衡系统的运营成本并从乘客的估计收入中减去这些成本来构建的,以最小化组织成本,服从路线和再平衡约束。我们基于经典的旅行推销员问题(TSP) 背后的直觉建立了我们的模型,这是一个优化问题,其中“推销员”必须恰好访问一次指定位置的列表,同时选择最短的可能路线并返回到起始位置。
参数
我们首先定义了以下参数以纳入我们的模型:
决策变量
接下来,我们定义了我们的决策变量。这些变量定义了卡车司机必须做出的选择,特别是司机是否会访问一个车站,以及他们将取放多少辆自行车。
目标函数
我们的首要目标是最小化总运营成本,包括每辆自行车的预计自行车消毒成本( h )、每公里的汽油成本( g )以及卡车司机的固定小时工资( w )。这里, B 代表被重新定位的自行车数量,而 D 代表卡车行驶的总距离。
约束条件
最后,我们构建了两组约束——一组关于所选路线,另一组关于自行车重新平衡——来复制必须满足的各种现实需求。从路线的角度来看,我们调整了旅行推销员问题的约束,以确保在不需要重新平衡时,卡车不需要访问每个车站,因为对于 BIXI,卡车只需要在自行车数量低于或高于我们定义的预定阈值时访问车站。
对于重新平衡的限制,我们需要确保卡车将每个站点重新平衡到其理想水平,并且它不会承载超过其容量的自行车。
要深入了解我们如何解决这个问题,请访问我们的 Github。
估计再平衡的成本
为了确保 BIXI 能够保持足够的资金来执行这一重新平衡过程,我们还估算了其运营产生的成本和收入。我们估计重新平衡的成本如下:
- 卡车司机工资:20.52 美元/小时
- 新冠肺炎自行车消毒费用:0.875 美元/辆
- 燃料成本:1.256 美元/升
- 预计燃油效率:4.25 公里/升
- 旅行价格(燃料成本*效率):0.294 美元/公里
估计乘客的收入
通过将每次出行的成本乘以会员与非会员出行的次数,可以估算出感兴趣区域每天和每周的乘客总收入。虽然 BIXI 提供额外的折扣和套餐,但数据并未区分这些旅行,因此所有旅行最终都归入以下费用方案:
会员与非会员旅行的费用方案。请注意,在一定时间后,每增加 15 分钟,将收取 3 美元的费用。
把所有的东西放在一起!
模型公式是使用 Gurobi 9.1 和 Python 3.7 完成的。总之,我们定义了四个主要的决策变量,涉及(1)卡车是否会在站点之间行驶,(2)在到达站点之前它载有多少辆自行车,(3)它卸下的自行车数量,以及(4)它装载的自行车数量。该模型随后选择最佳决策,使得与再平衡相关联的成本最小化,同时确保路线选择和再平衡约束保持满足。
结果
一旦模型建立起来,我们就可以通过将乘客数据(每个站点的自行车接送)输入到模型中来模拟和评估它在任何给定时间的性能。该模型以每小时为基础进行重复评估,由此在每小时开始时更新自行车流,并随后为该小时生成重新平衡路线和决策。
2019 年 7 月 8 日上午 8:00-9:00 凡尔登碧溪站模拟自行车再平衡操作结果。
为了让我们一窥每小时的再平衡系统会是什么样子,我们创建了一个地图,展示了 7 月 8 日 8:00-9:00 卡车的移动。我们看到,在这一小时内,总共有 5 个站点需要重新平衡。当卡车到达车站(1)时,我们假设它装载了 20 辆自行车(总容量的一半)。然后,在站点 2,卡车装载了两辆自行车,这意味着在此点之前站点一定已经超过了理想容量上限。到达站点 3 时,卡车现在装载了 22 辆自行车,随后卸下了一辆自行车,这意味着站点 3 低于其理想容量下限。在第 4 站,卡车现在载有 21 辆自行车,并卸下两辆,在第 5 站,它载有 19 辆自行车,并卸下一辆。然后,卡车返回 1 号站,该小时的再平衡任务完成。从下面的图表中我们可以看到,在这一小时内,总共行驶了 5.56 公里, 6 辆自行车重新平衡,费用为27.40 美元。
2019 年 7 月 8 日卡车再平衡计划的逐小时结果
为了跟踪再平衡进度,我们计算了从 8:00 到 20:00 每小时开始时的净差异,因为这些时间被视为 BIXI 接送最活跃的时间。如果我们全天运行该模型,我们会发现成本最高的时间是从 16:00 到 17:00,在这一时间需要重新分配 10 辆自行车。中午 12:00-13:00 是第二昂贵的时间;然而,这似乎与行驶的距离更大有关,而不是重新分配的自行车数量。从逻辑上讲,这些繁忙时间对于所选的时间段是有意义的,因为从 16:00 到 17:00,客户将使用该服务下班回家,而从 12:00 到 13:00,客户可能在午餐休息时间使用该服务。
新冠肺炎扩展:优先考虑重要员工的自行车出行
随着最近的新冠肺炎疫情,企业不得不彻底发展其业务模式,以适应不断变化的条件和新出现的客户需求。自行车共享系统也不例外。在疫情开始时,BIXI 经历了基本卫生保健工作者的乘车人数的增加,为了满足这些需求,BIXI 开始向基本工作者提供 30 天的免费会员资格。
基于这一信息,我们通过优先考虑医院附近的自行车可用性来探索原始模型的扩展,以确保医护人员能够获得自行车。为了做到这一点,我们修改了原始的优化问题,在系统的一个新的子区域内为医院增加了一个必需的子行程。基本上,卡车将被要求依次访问医院附近的所有站点,以确保首先满足医院工作人员的需求,然后再前往该地区的其他站点。对于扩建,我们选择了蒙特利尔市中心的一个子区域,该区域包括 57 个车站和 6.07 km 的面积,包括西端的蒙特利尔神经医院(Neuro)和东端的蒙特利尔大学医院中心(CHUM)。CHUM 周围的三个站和 Neuro 周围的七个站被选择作为预定义的子旅程,并且起始站也被设置为 CHUM 医院附近的优先站之一。
虽然最近的一项研究在更复杂的环境中提出了类似的想法,但我们通过添加以下两个约束条件实现了具有优先级的路线选择解决方案,其中,ℙ表示一组优先站点,ℝ表示一组常规站点。
约束(3.1)确保卡车在离开起始优先站之后首先访问优先站,约束(3.2)保证从优先站到常规站的访问不超过一次。只有当所有优先站都已被服务并且随后有后续常规站要被服务时,才发生该动作。注意,当需要重新平衡的所有站都是优先站时,约束(3.2)是非绑定的。
以下两张地图显示了添加优先级约束后路线的变化情况:
不添加(左)和添加(右)医院附近车站优先访问限制的路线。
通过添加这组约束,我们观察到卡车仍然能够满足每小时的重新平衡需求,但是正如预期的那样,访问的顺序发生了变化,因此首先访问每个选定的医院站。例如,通过在 2019 年 7 月 8 日 12:00- 13:00 运行一次再平衡迭代,观察到距离从 6.97 km 增加到 7.62 km ,表示相关成本从33.94 美元增加到40.14 美元。有趣的是,计算复杂度也显著增加;基本模型的求解时间不到 1 秒,而具有优先级约束的 1 小时相等时间段的求解时间为 36.84 秒。考虑到蒙特利尔有超过 10 个主要的健康网络,并且许多网络有几个分支,当考虑是否进行这样的操作时,每小时再平衡成本和计算强度的这种潜在增加不是微不足道的。
最后的想法
这个项目的目标是利用 BIXI 的开放访问历史乘客数据集来模拟自行车的持续再平衡对于保持系统平稳运行和满足自行车需求是多么必要,同时满足 BIXI 的底线。
实际上,BIXI 的自行车再平衡系统可能要复杂得多,例如,自行车再平衡要求的阈值可能是通过考虑天气、人口密度、一天中的时间和交通等因素的算法生成的。我们为再平衡选择固定阈值的假设显然没有捕捉到可能影响再平衡决策的全部潜在变量。此外,虽然我们创建了固定的边界区域,卡车只能在这些区域内运行,但 BIXI 通常会部署多辆卡车,在整个岛屿上灵活行驶,从而更容易处理波动的需求。我们还大大简化了成本和费用的估算,因为 BIXI 还会产生营销、管理和数据收集费用。因此,我们的模型不能从企业整体的成功来解释,而只能从模拟的再平衡过程的有效性来解释。
总的来说,我们的分析旨在通过使用混合整数规划技术来优化自行车的再平衡,以满足预期需求,缓解系统中的过剩和短缺,并保持成本效益,从而深入了解蒙特利尔的 BIXI 自行车共享运营。虽然我们只关注了几个选定的地区和时间段,但所创建的模型是可扩展的,可以扩展到其他行政区或时间段。通过提供对维持这样一个动态系统的要求的关键见解,我们最终试图证明蒙特利尔岛上存在进一步提高友好和高效的交通共享运输系统的价值的机会。
如果您想分享任何想法,请随时在 Linkedin 上联系我们。
其他资源:
关于我们的更多信息:
https://www.linkedin.com/in/duncan-w/ https://www.linkedin.com/in/hannaswail/ https://www.linkedin.com/in/beiqizhou/ https://www.linkedin.com/in/tzhang96/
一种更容易获得和复制的方法,用于加利福尼亚人工收割作物的卫星制图
变更数据
在整体粮食安全、可负担得起的农产品的可用性和美国农业劳动力短缺之间的密切联系的推动下,洛克菲勒基金会的统计和机器学习团队看到了一个为手收作物开发一种可访问和可复制的早期绘图工具的机会。仍在开发中的手工收割作物制图项目旨在创造一种工具,仅使用卫星图像来了解手工收割作物的产量变化。最佳替代方案是美国农业部农田数据层(CDL ),它使用包括私人来源在内的许多数据层输入,主要关注主要作物,直到第二年早春才发布。迄今为止,我们的模型相对于 CDL 有所改进:它完全基于公开可用的数据,它适用于一次一种作物和一个地区,并且它从同年 8 月开始在加利福尼亚州运行,同时提供与 CDL 相比略有下降的准确性。本博客重点关注动机、方法和迄今为止的发现,但深度学习模型和该模型的扩展方面的工作仍在继续。
我们的方法:预见生产挑战
绘制手工收割作物地图的最初动机来自疫情之初,当时有关边境关闭、国内旅行限制、学校关闭等消息表明农业工人短缺进一步加剧。在疫情开始之前,劳动力已经存在基线缺口——2019 年,加州农场局联合会和加州大学戴维斯分校的一项研究发现,在过去的五年里,加州 56%的受访农民无法获得所需的全部劳动力。当新冠肺炎封锁在美国开始时,各地的劳动者都面临着一个艰难的决定,即是否要冒着暴露于病毒的风险去上班和工作,通常是与他人密切接触。根据 2018 年全国农业工人调查,在出生于墨西哥的美国农场工人中,69%的人季节性迁移,这些人受到封闭的陆地边界和关闭的美国领事馆的进一步挑战。对于生活在美国、有孩子但没有强大支持网络的员工来说,要么呆在家里,要么努力为失学的孩子找到一个不太可能负担得起的托儿服务,这两者之间的选择增加了额外的复杂性。这些和其他因素都导致不工作的决定,加剧了现有的劳动力短缺,给依赖工人收获作物的生产者带来了巨大的不确定性。
受这种短缺影响的手工收获的作物是洛克菲勒基金会食品倡议组织称之为“保护性食品”的更广泛食品的一部分——这些食品有助于预防和保护与饮食有关的疾病,根据健康指标和评估研究所的全球疾病负担研究,这是美国最大的死亡原因。虽然许多食物可以起到保护作用,但即使对于最脆弱的家庭来说,水果和蔬菜也是最容易获得和负担得起的。在 2018 年美国最常购买的二十种水果和二十种最常购买的蔬菜中, 14 和 10 分别是手工收获的,包括苹果、核果、柑橘、浆果、西红柿和蔬菜等农产品。手工收割作物产量减少意味着价格上涨和供应减少,限制了最弱势群体获得这些食物。因此,理解手工收获的食品生产系统——并跟踪生产的变化——感觉与我们在洛克菲勒基金会推广保护性饮食的工作密切相关。
蒂姆·莫斯霍尔德在 Unsplash 上拍摄的加州草莓采摘者的照片
作为食品和遥感领域的首席数据科学家,我假设加剧的劳动力短缺以及其他压力因素(不可预测的市场、重大野火等)。)可能会导致生产者在 2020 年或 2021 年不冒险种植。这些大规模决策可能会导致产量大幅下降,从而影响美国最弱势群体获得高营养食品的能力。基于遥感的制图非常适合这种假设检验,因为数据存在于大规模数据中,可追溯到多年前,分辨率允许在整个区域范围内以 10 米乘 10 米的比例进行分类。我把重点放在加州中央谷,因为根据加州农业部的数据,这里生产蔬菜的⅓,水果和坚果的⅔,以及美国 90%以上的葡萄、李子、橄榄、杏仁、开心果和其他作物。该山谷以西部的海岸山脉和东部的内华达山脉为界,总长度为 450 英里(720 公里),宽 50 英里(80 公里),相当于约 18,000 平方英里(47,000 平方公里)和约 4.7 亿个要分类的“地块”或像素。前面的文档描述了我迄今为止的方法和结果。
加州县与中央山谷重叠,在谷歌地球引擎中可视化。
方法:准备随机森林分类器
为了完成这项任务,我首先选择了一种作物分类方法。随机森林长期以来一直是遥感工作的主要内容,因为它相对简单,并且具有高数据维度和多重共线性——光学遥感数据的两个基本特征。虽然深度学习模型因其突破性见解和提高准确性的潜力而受到极大关注,但随机森林始终是测试的基线,因此是一个合理的起点。其次,我决定首先专注于创建一个二元随机森林分类器,原因很简单,只要分类器能够识别感兴趣的作物,其余的土地覆盖是什么就无关紧要了。从整体系统的角度来看,周围区域的土地覆盖总是很重要的,但是由于多类分类器在任何一种作物的确定性较低时通常倾向于像主粮这样的高覆盖作物,因此本项目中感兴趣的目标作物可能会被低估-因此,二元分类是一个合理的起点。
鉴于 Rockefeller Foundation 没有手收作物位置的现有样本集,并且由于疫情期间不可能进行野外工作,我选择从美国农业部的历史农田数据图层分类中进行采样作为替代。如前所述,CDL 每年都会为整个美国创建一张最有可能种植作物的地图,并报告分类以及每个像素的分类概率。虽然这种分类对主要作物表现更好,但它是更可靠数据或定制数据收集的良好替代品。对于每个训练集,我首先从聚焦区域内的 CDL 中采样 10,000 个像素,并过滤掉 80%或更高置信度的像素。我将这些样本分类重新映射为目标作物或非目标作物的二元分类,然后使用这些像素位置对卫星图像进行采样。
对于不熟悉卫星图像的读者来说,术语“卫星图像”可能有点误导——特定时间和位置的每个“图像”都是一组光栅“波段”,用于捕捉特定波长的反射阳光强度和反射率。因此,使用卫星影像需要转换和操作多个栅格堆栈,这将在本文稍后简要介绍。下面是卫星图像波段堆栈和单个像素样本的几何图形的可视化:
仙黛尔·诺顿编写
对于这项工作,我选择 Sentinel-2 作为光学数据,Sentinel-1 作为雷达数据,因为它们重复次数多,分辨率高,有历史数据,可以自由访问。我有意只选择两个来源来创建最精简、最可复制的模型;作为参考,CDL 农田数据层使用来自戴莫斯-1 和英国-DMC-2(两者都是欧洲星座中的有限接入卫星)、哨兵-2、陆地资源卫星 8 和资源卫星 2 的产品,以及美国地质调查局土地覆盖层和美国农业部 CDL 层基于2019 年文档。
许多土地分类可以简单地用来自精选卫星源的单一精选图像来完成——例如,对城市公园进行分类,并挑选植物生长的图像。然而,由于许多植物在一年中的某些时间在光谱带上具有非常相似的值,物种和作物分类问题通常需要与衰老曲线相关的时间序列信息,衰老曲线涉及植物的逐渐变绿,然后随着叶绿素的产生而变褐,然后死亡。在不同的光谱带,尤其是近红外光谱,这条曲线通常会显示为读数变亮,然后变暗,因为叶绿素在高强度下反射某些波长。因为不是所有的植物都在完全相同的时间长出叶子或产生相同面积的叶子和相同体积的叶绿素,衰老曲线的特征可以是非常有用的,甚至是唯一的标识符。对整个季节的值取平均值是一个强大的转换,可以大大减少数据量,同时捕捉曲线的本质。应用这个来准备 Sentinel-2 的图像,我用 QA60 cloudmask 波段遮蔽了云层,然后对 3 月初至 7 月底的所有图像进行平均。这一时间框架抓住了中央山谷的夏季生长季节,许多作物在 3 月种植或转移到外面,7 月收获。在每个像素处取平均值显著地将数据集的大小从大约十个 Sentinel-2 图像(每两周捕获一次)减少到仅仅一个,减少了噪声并有助于捕获独特的衰老曲线特征。尽管 Sentinel-1 图像数据作为合成孔径雷达(SAR)卫星产品有着根本的不同,但出于类似的原因,它也被平均化。下面是一个例子,说明在加利福尼亚的整个生长季节,单个像素上的单个虚构光学波段如何变化,以及在遥感工作中,作物的生物物理变化如何转化为衰老平均值。
仙黛尔·诺顿编写
在上面想象的场景中,这些田地自上一季以来就没有被开垦过,可能有少量的杂草生长;李树还没有叶子。在室外种植甜椒、甜瓜和卷心菜之前,将对土地进行翻耕——这将吸收光线的潮湿土壤带到表面,因此降低了卫星观测到的反射率。在主要作物入土并且树木作物开始开花并长出叶子后,植物将显著增加它们的叶面积和叶绿素含量,增加反射率,直到接近收获时达到峰值。之后,植物会枯死,松散的叶子和卷心菜、甜瓜和甜椒地会被翻耕,反射率会下降。在这个模拟示例中,您可以看到,虽然大多数作物在六月底左右有一个相似的高峰期,但它们各自的曲线是独特的。当我们平均整个季节的反射率值时,每种作物都有一个独特的值,这是曲线本身的一个相当独特的 ID-李子 6.1 最高,卷心菜 2.8 最低。这项工作中使用的所有波段的唯一 id 组合产生了每种作物的唯一 id 向量。
接下来,我从采样波段中准备了一些指数,用于对 Sentinel-1 和 Sentinel-2 准备的影像进行实验,并使用随机森林模型来确定重要变量,这极大地有助于模型开发过程,尤其是减少准备的数据集的大小。为了了解处理的数据量,在中央山谷所有像素使用的所有波段和所有日期的图像派生指数之间,大约有 3000 亿个数据点。最终使用的波段和指数为蓝色、Sentinel-2 观测的角度、作物残留覆盖指数(CRC)、归一化燃烧指数(NBR)、使用红色代替近红外的绿色叶绿素植被指数(GCVI)以及使用 Sentinel-1 的精细 Lee 滤波器的 VH 多极化波长斑点测量。样本按县、流域(代表覆盖流域流域的 3-4 个县)或 2019 年整个中央流域准备。这些样本被送入随机森林,该森林使用了 160 棵树,并将 2018 年图像的内核减少到 5 个像素正方形,即 50 米乘 50 米的瓦片。Kern、Glenn、Butte 和 Tehama 县被排除在外,因为它们在 EE 中返回了错误,而 Shasta 被排除在外,因为它在中央山谷中包含如此少量的农业像素。随机森林的可视化如下所示,其中样本向量输入向量包括单波长观测值和如上所述的整个生长季节的平均指数:
仙黛尔·诺顿编写
然后使用 2019 年相应测试年的 CDL 评估按县分类的结果影像,仅包括置信度为 80%或更高的像素。下图显示的是加利福尼亚州同一区域的田地,仅包含卫星影像、分类器输出和用于参考的杏树 CDL 分类(其中杏树在分类器影像中为浅灰色,在 CDL 影像中为白色)-请注意,分类很接近,但并不完全相同。精度和召回被用来评估分类器的性能对 CDL,它只报告精度和召回在国家一级。并非所有作物都有足够的数据用于分类器——许多不太常见的手工收割的作物,或者在特定区域不太常见的作物,不能用于建模。例如,圣华金县的樱桃代表 1,063,300 个像素中的 62,617 个像素,约占高可信度农业像素景观的 5.9%,但返回的结果较差,这与杏仁的 186,400 个像素相比相形见绌,杏仁约占圣华金高可信度农业景观的 17.5%,建模效果非常好。
由 Madeline Lisaius 使用谷歌地球引擎编写
结果、观察和结论
从上表中可以看出,数据丰富的坚果作物,尤其是杏仁,在所观察的县和日期表现非常好。相比之下,数据越少的作物——葡萄、西红柿和樱桃——表现越差,部分原因是它们用于训练的数据越少,部分原因是用于分类的像素数量(与覆盖率更高的杏树相比)非常少,有时超过 1000 万个像素中只有 10,000 个像素。这些结果是初步的,但是它们给出了与 CDL 相比该模型的功效的感觉。
总的来说,我们所描述的模型在许多情况下在 CDL 的范围内执行,其优点是仅使用公共数据,能够在同年 8 月开始运行,并且公开可用。然而,有许多可能的扩展和改进途径,可以使这种模式高效地扩展到更多的作物。最有效但劳动密集型的方法之一是通过亲自实地考察或从种植者处获得参考坐标来收集感兴趣作物的样本。这将通过在 CDL 采样框架之外增加高置信度数据来增加可以成功建模的作物数量。虽然自举数据是一种选择,但在特定生长条件下的物种生理——转化为跨波长的独特遥感光谱特征集——不容易模拟,因此应谨慎使用这种方法。改进模型的下一步将是增加一层再处理,以去除分类图像中的斑点像素,这将提高召回率和精确度。
该模型本身还没有实现更高层次的目标,即能够以必要的精度在田间水平上精确地测量手工收获作物的种植和生长面积的变化。尽管如此,我们对这一模型的潜力感到非常兴奋,与 CDL 相比,这是一种更轻便、更容易理解作物,尤其是非主粮作物的方法。随着更多的工作,该模型可以更精确地用于目标地区,在这些地区,比例过高或过低的农民正在改变行为以应对冲击,作为一种工具来确定投资目标和确定学习机会。该模型还可以用来预测大规模和特定的粮食生产的变化。由于气候变化和长期劳动力趋势,加州和其他生产手工收割作物的地区面临越来越多的困难,了解景观水平的变化对于了解美国营养食品的供应比以往任何时候都更加重要。该工具的未来版本和其他版本可能有助于提供见解,这些见解可以补充农场工人、农民和其他利益相关者的生活经验,从而就如何保护性地养活美国提供整体和微妙的观点。
具有 Streamlit 和 Plotly 的多页交互式仪表板
数据可视化
Streamlit 使漂亮的交互式多页仪表板变得简单
Streamlit 应用非常简洁——图片由作者提供
Streamlit 的伟大之处在于,它不仅漂亮,而且简单。
几个月前,我写了一篇关于如何使用 Plotly 和 Flask 编写仪表板的文章——这是使用 Dash 的替代方法,我对此非常满意。这个应用程序由两个文件组成,一个 20 多行的 Python/Flask 应用程序和一个近 30 行的 HTML/Javascript 文件。你可以在这里看到这篇文章:
[## 使用 Plotly 和 Flask 的交互式 Web 仪表板
towardsdatascience.com](/an-interactive-web-dashboard-with-plotly-and-flask-c365cdec5e3f)
然后我决定尝试使用 Streamlit 实现同样的东西:结果是一个大约十几行的 Python 文件。四分之一大小!不仅写起来简单快捷,而且看起来也更好看。
因此,我想分享我的经验,首先,创建一个简单的交互式 Streamlit 应用程序和 Plotly 图表,然后将它开发成一个更复杂(但仍然简单)的多页应用程序。在这个过程中,我研究了如何绘制 Plotly 图表、在 Streamlit 中控制布局以及使用下拉菜单导航。
第一个应用程序将是我的 Flask 应用程序的克隆,看起来像这样:
作者简单的 Streamlit 应用程序图片
用户从左边的下拉菜单中选择一个国家,并在右边绘制相应的图表。
Streamlit 应用程序只是一个 Python 程序,它使用 Streamlit 库,并在终端窗口中使用 Streamlit 命令运行。例如
streamlit run myapp.py
然后它会启动你的浏览器并运行应用程序。
不过,首先要做的是。要使用 Streamlit,您显然必须安装它:
pip3 install streamlit
或者其他任何你用来安装 Python 包的神秘方法。
简单的 Streamlit 应用程序
你用类似这样的东西开始你的 Python 程序:
import streamlit as st
import pandas as pd
import plotly.express as px
我将使用 Pandas 来存储和操作数据,并绘制图表。
出于演示的目的,我使用 Plotly 包中包含的 Gapminder 数据。所以,下一段代码是:
df = pd.DataFrame(px.data.gapminder())
这将创建一个数据帧,其中包含一个关于世界上所有国家的信息表,如下所示:
国家数据-按作者分类的图像
表中列出了每个国家,以及几十年来的预期寿命、人口和人均 GDP 数据。也给出了每个国家的大陆(也有其他数据,但这里不感兴趣)。
我们最初要做的是创建一个选定国家的人口和人均 GDP 的图表。因此,我们需要一个国家名称列表,用户可以从中进行选择。这可以通过从数据框中选择 country 列并创建一个唯一名称列表来轻松实现。
clist = df['country'].unique()
运行之后,我们在变量clist
中有一个唯一名称的列表。
国家列表-作者图片
下拉菜单是在clist
的侧边栏中创建的,如下所示:
country = st.sidebar.selectbox("Select a country:",clist)
所有的 Streamlit 函数都以st
开始。st.sidebar
在屏幕左侧创建一个侧边栏,.selectbox
使用第一个参数中提供的提示从列表中创建一个下拉列表。
接下来我们画图表。
fig = px.line(df[df['country'] == country],
x = "year", y = "gdpPercap", title = country)
在这里,您可以看到我们在变量fig
中创建了一个折线图,只选择数据框中与所选国家对应的行。
整个程序如下所示,正如我前面提到的,它只有十几行代码。
import streamlit as st
import pandas as pd
import plotly.express as pxdf = pd.DataFrame(px.data.gapminder())
clist = df['country'].unique()country = st.sidebar.selectbox("Select a country:",clist)st.header("GDP per Capita over time")fig = px.line(df[df['country'] == country],
x = "year", y = "gdpPercap", title = country)
st.plotly_chart(fig)
有几行我还没讲过。st.header("GDP per Capita over time")
创建标题,st.plotly_chart(fig)
绘制图表。
当我们运行这个程序时,我们得到了这个:
作者简单的 Streamlit 应用程序图片
我认为,对于这样一个小程序来说,这已经很不错了。
但事情会变得更好。
多页应用程序
我努力说服自己,我可以像使用 Dash 一样轻松地使用 Flask 和 HTML 制作应用程序,作为努力的一部分,我创建了一个简单的多页面应用程序,每个页面使用相同的 HTML 模板,但从 Flask 加载不同的数据。你可以在这里看到这篇文章:
[## 用 Plotly 和 Flask 实现 Web 可视化。
towardsdatascience.com](/web-visualization-with-plotly-and-flask-3660abf9c946)
Streamlit 的情况就不同了。没有对多页面特定支持,我猜这不是一个遗漏。Streamlit 并不意味着创建完整的网站。
但有时一点分页是有用的,所以我写了一个新的应用程序,它在第一个基础上扩展,提供更多信息,并在两个页面上运行。看起来也更好看!
我们的目标是拥有两个页面,一个针对国家,另一个针对大洲。在每一页上,你可以选择一个国家或洲,你会得到两个图表,一个是 GDP,另一个是人口。你可以在下面看到大陆页面的样子。
作者提供的多页 Streamlit 应用程序图片
实际上,也许你看不太清楚,所以这是页面左侧的放大图。
来自多页 Streamlit 应用程序的详细信息-作者图片
左侧是带有下拉菜单的侧栏,您可以在其中选择国家或洲。页面的主要部分包含图表,并有自己的下拉菜单来选择哪个洲(欧洲、亚洲等)。)或者哪个国家。
国家页面看起来像这样。
作者提供的多页 Streamlit 应用程序图片
这个方法很简单。只需在if… else…
语句中使用侧边栏下拉菜单返回的值,并在if
块或else
块中显示适当的数据。代码如下所示:
page = st.sidebar.selectbox('Select page',
['Country data','Continent data'])if page == 'Country data':
# Display the country content here
else:
# Display the continent content here
我使用分栏来并排显示图表,我将布局设置为 wide 以给自己多一点空间。
除此之外,替换这些注释的代码与我们在第一个例子中看到的非常相似。
以下是完整列表:
import streamlit as st
import pandas as pd
import plotly.express as pxst.set_page_config(layout = "wide")df = pd.DataFrame(px.data.gapminder())st.header("National Statistics")page = st.sidebar.selectbox('Select page',
['Country data','Continent data'])if page == 'Country data':
## Countries
clist = df['country'].unique() country = st.selectbox("Select a country:",clist) col1, col2 = st.columns(2) fig = px.line(df[df['country'] == country],
x = "year", y = "gdpPercap",title = "GDP per Capita")
col1.plotly_chart(fig,use_container_width = True) fig = px.line(df[df['country'] == country],
x = "year", y = "pop",title = "Population Growth")
col2.plotly_chart(fig,use_container_width = True)else:
## Continents
contlist = df['continent'].unique()
continent = st.selectbox("Select a continent:",contlist) col1,col2 = st.columns(2) fig = px.line(df[df['continent'] == continent],
x = "year", y = "gdpPercap",
title = "GDP per Capita",color = 'country')
col1.plotly_chart(fig) fig = px.line(df[df['continent'] == continent],
x = "year", y = "pop",
title = "Population",color = 'country')
col2.plotly_chart(fig, use_container_width = True)
现在,如果我聪明的话,我会意识到我在这个应用程序中有大量非常相似的代码,所以会写一些函数来减少这种情况。但是现在我会让它保持原样。
利弊
Streamlit 易于使用并产生非常好的结果,但它只为相当简单的应用程序设计。布局复杂的多页面应用程序并不是它真正想要的,这很公平。
Streamlit 确实有一些很好的布局组件(改天会有更多),多页面应用可以通过 Python 3.10 模式匹配更好地实现(见这里和这里)——改天也会有更多。
一如既往地感谢阅读。你可以在我的 Github 页面下载代码,并在这里看到最终应用的演示,在我的网页上有一大堆关于 Streamlit、数据可视化和 Python 的文章。
更新:我最近设计了一个更复杂的方法,在 Streamlit 的一个网站上创建多个应用程序。在这里阅读: 如何将 Streamlit 应用程序库构建为一个单独的 Web 应用程序
如果你不是一个媒体订阅者,那就注册吧,这样你就可以每月花 5 美元阅读尽可能多的文章。在这里注册我会赚一小笔佣金。
通过社交媒体分析长期直播事件的多视角方法
思想和理论,纽约,巴黎,米兰,伦敦
四大时装周的案例
Armen Aydinyan 在 Unsplash 上的照片
社交媒体正在主宰人们的日常生活。在我们的生活中,它既是信息的来源,也是分享个人经历和观点的地方。
特别是,它在大型活动(如阿拉伯之春、美国总统选举和许多其他场景)中展示了巨大的沟通、互动和社区建设潜力。
我们提出了一种实用的方法来研究所谓的长期现场活动(LRLEs) 的影响和作用,即活动持续时间长,由某个主办实体组织,在某些地点实际举行,并在很长一段时间内周期性重复。
我们旨在通过探索和分析与 LRLEs 相关的社交媒体平台上产生的用户生成内容(UGC)来研究此类事件。事实上,UGC 主要反映了参与者在实际活动中的亲身体验,因此是关于事件发生的信息。
这吸引了许多社区。对于公共部门来说,这是一扇新的大门,可以探索一些无法解释的主题,如社会的行为模式和改善沟通。对于品牌和企业来说,这是一种了解客户和投放广告的有利可图的方式,因为与其他形式的广告相比,它们可以以更低的成本吸引更多的公众注意力。
我们设计了一种实用的数据科学方法,其目标如下:
- 理解事件期间用户行为的时间动态。
- 了解用户的地理分布及其发布活动。
- 研究活动期间的品牌知名度。
- 检测事件位置和以品牌为目标的参与者的发布动态之间的相关性。
- 确定影响用户生成内容的受欢迎程度的主要因素,并设计模型来预测此类内容的受欢迎程度。
以下是一个概念模型,代表了在此环境中发挥作用的主要实体:
长期运行的实时事件的概念模型(图片由作者提供)
应用多模态方法需要考虑帖子相关的信息、帖子的内容和用户特征。特别是,我们需要提取和分析:
- 与用户相关的特征,也称为社交环境属性,收集发帖用户的相关信息,如其关注者和关注人数、个人资料类型、等。
- 与内容相关的信息,取决于短信的类型(例如,YouTube 上的视频和 Twitter 上的文本),可能包括视觉特征。
- 帖子相关的特征,即通过 API 收集的帖子的元数据,如时间和地理属性、标签、标记、评论数、等。
- 事件相关特征,包括目标事件的统计信息,如平均点赞数,或事件本身的具体信息。
为了展示我们的工作方法,我们选择了四大时装周,分别在纽约、巴黎、米兰和伦敦举办。我们在 Instagram 上收集关于这些事件的 UGC 内容,通过 Instagram API,通过基于标签的数据收集方法来最大化覆盖范围。产生的数据集由 905,726 条 Instagram 帖子、171,078 条与之相关的用户资料和 723,831 张图片组成。
帖子中城市间相关性的重叠。[图片由作者提供]
该研究从理解帖子与事件的相关性开始。结果显示,大多数帖子(92.866%)只与四个城市之一相关。尽管如此,还是存在一些重叠,如此处所示。动机是一些用户发布内容将他们与许多时装周联系起来,只是为了增加他们的知名度。这显然与事件的研究无关,因此可能是至关重要的。
采用的分析维度包括以下报告的维度:
- 标签分析
- 时间分析
- 地理影响
- 网络分析(关注者)
- 流行预测
- 品牌分析
(左)用户的追随者数量( y 轴)与对数形式的追随者数量( x 轴)和(右)。[图片由作者提供]
仔细观察左图,我们可以注意到三个更密集的区域,分别位于位置 0、999 和 7500。第一个峰值是 840 个用户的结果,正好有 0 个追随者,但不同的追随者计数。他们可能是决定不关注任何人或未使用或伪造账户的名人。还有一种可能是 Instagram 禁止了一些用户关注其他人。另一方面,第三个高峰,收集了非常正确部分的所有点,可能是机器人,他们关注许多帐户,以吸引追随者或增加他们的帖子或帐户的可见性。他们的追随者数量平均少于他们的追随者数量。这个峰值对应于 234 个用户,他们准确地关注 7500 个帐户,但是具有不同的关注者计数。这个具体数字可能是 Instagram 的一项政策造成的,该政策禁止用户关注超过 7500 个个人资料。
对于地理分析,我们可以利用地理标记。在收集的 905,726 个帖子中,42.59%带有地理标签。这是每个事件最相关区域的热图。
每个事件的帖子热图。[图片由作者提供]
活动-品牌关系也是一个相关的维度,尤其是对于理解不同品牌的作用和影响。因此,它们中的每一个都被映射到该位置。例如,您可以在这里看到 Spearman 相关性分析的热图矩阵,显示了从四大城市中每个城市对两个品牌(迪奥和香奈儿)的回复中获得的值之间的相关系数。
迪奥和香奈儿影响着不同的时装周。[图片由作者提供]
使用 SVR、XGboost 和 RNN 方法,还开发了一个事件相关帖子受欢迎程度的预测器。
其他分析结果的快速总结显示在本文结尾的图片中,而该方法的完整描述则发表在科学论文(开放获取)中:
Javadian Sabet A .、Brambilla M .、Hosseini M. **一种用于分析社交媒体上长期直播事件的多视角方法。“四大”国际时装周案例研究。在线社交网络与媒体,第 24 卷,2021,100140,刊号 2468–6964,【https://doi.org/10.1016/j.osnem.2021.100140】。
(https://www . science direct . com/science/article/pii/s 2468696421000239)
数据集也可以在网上获得,在这篇数据集文章中有所描述:
Brambilla M .、Javadian Sabet A .、Hosseini M. 社交媒体在长期直播事件中的作用:四大时装周数据集的案例。 数据简介,35 条【2021】,106840 条10.1016/j . DIB . 2021.106840
多重分析视角。[图片由作者提供]
自然语言处理初级读本
使用 Python 的常见 NLP 任务概述
照片由 Skylar Kang 从 Pexels 拍摄
自然语言处理 NLP 用于分析文本数据。这可能是来自网站、扫描文档、书籍、期刊、推特、YouTube 评论等来源的数据。
这本初级读本介绍了一些可以使用 Python 执行的常见 NLP 任务。示例大多使用自然语言工具包(NLTK)和 scikit 学习包。假设您具备 Python 和数据科学原理的基本工作知识。
自然语言指的是像英语、法语、阿拉伯语和汉语这样的语言,而不是像 Python、R 和 C++这样的计算机语言。NLP 使文本数据的部分分析自动化,这在以前只有定性方法才有可能。这些定性方法,如框架/主题分析,不能扩展到大量的文本数据。这就是 NLP 的用武之地。它还被用于创建聊天机器人、数字助理(如 Siri 和 Alexa)等其他用途。
本笔记本中使用的数据来自https://www.english-corpora.org/corona/【1】的数据子样本。这些数据是关于冠状病毒疫情的,代表了来自各种媒体来源(例如报纸、网站)的子集,并且是 2020 年 1 月至 5 月期间的数据。该数据包含大约 320 万个英语单词。
使用 Python 加载文本数据
让我们从查看文本数据开始,看看它是什么样子的。我们可以使用操作系统( os )库来列出我们的文本文件夹中的所有文件。在这种情况下,文件位于名为 text 的文件夹中,该文件夹位于名为 NLP 的文件夹中,该文件夹与 Python 源代码文件相关(例如 Jupyter notebook 或。py 文件)并用“.”表示。/".
import os
os.listdir("./NLP/text")
这将生成一个包含 5 个文本文件的列表:
>>> ['20–01.txt', '20–02.txt', '20–03.txt', '20–04.txt', '20–05.txt']
如您所见,有 5 个文本文件(*。txt)。我们可以使用标准的 Python 文件处理函数来打开文件。在这种情况下,我们将把它限制在前 10 行,以了解文件的结构和它包含的信息。在下面的输出中,为了简洁起见,这被缩短了。 next 函数用于遍历文件中的行。
with open("./NLP/text/20–01.txt") as txt_file:
head = [next(txt_file) for i in range(10)]
print(head)>>> ['\n', '@@31553641 <p> The government last July called the energy sector debt situation a " state of emergency . " <p> This was during the mid-year budget review during which the Finance Minister Ken Ofori-Atta castigated the previous NDC government for entering into " obnoxious take-or-pay contracts signed by the NDC , which obligate us to pay for capacity we do not need . " <p> The government pays over GH ? 2.5 billion annually for some 2,300MW in installed capacity which the country does not consume . <p> He sounded alarmed that " from 2020 if nothing is done , we will be facing annual excess gas capacity charges of between $550 and $850 million every year . <p> JoyNews \' Business editor George Wiafe said the latest IMF Staff report expressing fears over a possible classification is " more of a warning " to government . <p> He said the latest assessment raises concerns about the purpose of government borrowings , whether it goes into consumption or into projects capable of generating revenue to pay back the loan . <p> The move could increase the country \'s risk profile and ability to borrow on the international market . <p> @ @ @ @ @ @ @ @ @ @ issue another Eurobond in 2020 . <p> The Finance Minister Ken Ofori-Atta wants to return to the Eurobond market to raise $3bn to pay for expenditure items the country can not fund from domestic sources . <p> The government wants to spend GH ? 86m in 2020 but is projecting to raise only GH ? 67bn . It leaves a deficit of GH ? 19bn , monies that the Eurobond could make available . <p> The planned return to the Eurobond market is the seventh time in the past eight years . <p> Ghana is already among 10 low-income countries ( LICs ) in Africa that were at high risk of debt distress . <p> The country in April 2019 , successfully completion an Extended Credit Facility ( ECF ) programme , or bailout , of the International Monetary fund ( IMF ) . \n']
文本表示
为了以数字方式存储和使用字符,通常用编码系统来表示它们。有 ASCII (美国信息交换标准码)等不同的字符编码标准。例如,字母“a”由 ASCII 码 097 表示,在二进制中也是 01100001。还有其他编码集,如 UTF-8 (Unicode(或通用编码字符集)转换格式,8 位)支持可变宽度的字符。这个系统中的字母“a”是 U+0061。在 Python 中,我们可以像这样直接使用 UTF-8:
u"\u0061">>> 'a'
您可能需要转换导入的文本数据的字符编码,以执行进一步的处理并更好地表示某些符号(例如表情符号🌝).您可以使用 sys 模块中的 getdefaultencoding 函数检查您正在使用的编码类型。要更改编码,您可以使用 setdefaultencoding 函数,例如sys . setdefaultencoding(" utf-8 ")。
import sys
sys.getdefaultencoding()>>> 'utf-8'
数据预处理
应用 NLP 时,数据处理有几个阶段。这些根据确切的上下文而有所不同,但通常遵循类似于下图所示的路径。这通常包括访问文本数据,无论是网页、推文、评论、PDF 文档还是原始文本格式。然后将其分解为算法可以轻松处理的表示形式(例如,表示单个单词或字母的标记),移除常用单词(停用词)(例如,“and”、“or”、“the”)。进一步的标准化之后是诸如特征提取和/或去除噪声的任务。最后,各种模型和方法(例如,主题建模、情感分析、神经网络等。)都适用。
文本数据的通用预处理方法(图片由作者提供)
因为我们已经有了一些要处理的文本,所以我们可以看看预处理数据的后续步骤。我们可以从标记化开始。很多 Python 库都可以完成标记化,包括机器学习库 scikit learn。自然语言处理任务的一个流行库是自然语言工具包(NLTK):
import nltk
**注意:**Python 中另一个强大的 NLP 替代库是 spaCy 。
符号化
即使在库中,也经常有不同的标记器可供选择。例如,NLTK 也有一个 RegexpTokenizer(使用正则表达式)。这里我们将使用 TreebankWordTokenizer 来过滤掉一些标点符号和空格。
from nltk.tokenize import TreebankWordTokenizer
我们将从文本数据中提取一小段文本来说明这是如何工作的。在这里,我们将它作为一个字符串(Python 中用来存储文本数据的一种数据类型)存储在一个名为 txt 的变量中。
txt = "The government last July called the energy sector debt situation a state of emergency. <p> This was during the mid-year budget review during which the Finance Minister Ken Ofori-Atta castigated the previous NDC government for entering into obnoxious take-or-pay contracts signed by the NDC , which obligate us to pay for capacity we do not need . <p> The government pays over GH ? 2.5 billion annually for some 2,300MW in installed capacity which the country does not consume ."
简单的第一步可能是将所有文本转换成小写字母。我们可以通过下功能来实现。
txt = txt.lower()
txt>>> 'the government last july called the energy sector debt situation a state of emergency. <p> this was during the mid-year budget review during which the finance minister ken ofori-atta castigated the previous ndc government for entering into obnoxious take-or-pay contracts signed by the ndc , which obligate us to pay for capacity we do not need . <p> the government pays over gh ? 2.5 billion annually for some 2,300mw in installed capacity which the country does not consume .'
接下来我们可以创建一个treebankwodtokenizer类的实例,并使用 tokenize 函数,传入我们的 txt 变量。输出如下所示(显示前 20 个)。
tk = TreebankWordTokenizer()
tk_words = tk.tokenize(txt)
tk_words[:20]>>> ['the',
'government',
'last',
'july',
'called',
'the',
'energy',
'sector',
'debt',
'situation',
'a',
'state',
'of',
'emergency.',
'<',
'p',
'>',
'this',
'was',
'during']
这个 casual_tokenize 对于社交媒体符号化很有用,因为它可以很好地处理用户名和表情符号之类的事情。 TweetTokenizer 也为 Twitter 分析保持了完整的散列标签。
处理停用词和标点符号
下一件常见的事情是删除停用词。这些是在句子结构中使用的常见高频词,但对分析来说没有太多意义。这些词包括“the”、“and”、“to”、“a”等。我们可以从 NLTK 库中下载这些单词的列表,并将它们存储在一个变量( sw )中,如下所示:
nltk.download("stopwords", quiet=True)
sw = nltk.corpus.stopwords.words("english")
我们可以看看这些单词的前 20 个。
sw[:20]>>> ['i',
'me',
'my',
'myself',
'we',
'our',
'ours',
'ourselves',
'you',
"you're",
"you've",
"you'll",
"you'd",
'your',
'yours',
'yourself',
'yourselves',
'he',
'him',
'his']
在写这篇文章的时候,列表中有超过 100 个停用词。如果你感兴趣,可以使用 len 函数查看列表中有多少单词(例如 len(sw) )。
现在让我们把这些停用词从标记词中去掉。我们可以使用 Python list comprehension 来过滤 tk_words 列表中不在停用字词( sw )列表中的字词。
tk_words_filtered_sw = [word for word in tk_words if word not in sw]
如果您不熟悉列表理解,它们本质上是一种创建列表和遍历列表数据结构的简洁方法。这通常避免了需要多几行单独的“for 循环”代码。假设我想求 0 到 5 的平方。我们可以像这样使用 for 循环:
squared_nums = []
for n in range(5):
squared_nums.append(n**2)
虽然这有预期的效果,但我们可以创建列表,并使用列表理解来遍历它,而不是将它组合成一行代码:
squared_nums = [n**2 for n in range(5)]
如果我们输出变量 tk_words_filtered_sw 的内容,我们可以看到很多停用词现在都被删除了(为简洁起见,缩写为):
tk_words_filtered_sw>>> ['government',
'last',
'july',
'called',
'energy',
'sector',
'debt',
'situation',
'state',
'emergency.',
'<',
'p',
'>',
'mid-year',
'budget',
'review',
'finance',
'minister',
'ken',
'ofori-atta',
'castigated',
'previous',
'ndc',
'government',
'entering',
'obnoxious',
'take-or-pay',
'contracts',
'signed',
'ndc',
','
...
可以看到文本中仍然有句号(句号)、问号、逗号之类的标点符号。同样,我们通常会从列表中删除这些内容。这可以通过多种不同的方式来实现。这里,我们使用字符串库来删除存储结果的标点符号,这个结果存储在一个名为 no_punc 的变量中。
import string
no_punc = ["".join( j for j in i if j not in string.punctuation) for i in tk_words_filtered_sw]
no_punc>>> ['government',
'last',
'july',
'called',
'energy',
'sector',
'debt',
'situation',
'state',
'emergency',
'',
'p',
'',
'midyear',
'budget'
...
然后我们可以从列表中过滤掉这些空字符串(“”)。我们可以将结果存储在一个名为 filtered_punc 的变量中。
filtered_punc = list(filter(None, no_punc))
filtered_punc>>> ['government',
'last',
'july',
'called',
'energy',
'sector',
'debt',
'situation',
'state',
'emergency',
'p',
'midyear',
'budget'
...
最后,我们可能还想从列表中删除数字。为此,我们可以使用 isdigit 函数检查字符串是否包含任何数字。
str_list = [i for i in filtered_punc if not any(j.isdigit() for j in i)]
如您所见,文本数据可能非常混乱,需要在开始运行各种分析之前进行大量清理。另一种去除不必要单词的常用方法是使用词干法或词干法。
词干化和词汇化
词干化指的是将单词缩减到它们的词根(词干)形式。例如,单词“waited”、“waities”、“waiting”可以简化为“wait”。我们可以看一个叫做波特词干分析器的普通词干分析器的例子。首先,我们可以创建一个简短的单词列表来演示这是如何工作的。这一阶段通常跟随单词的标记化。
word_list = ["flying", "flies", "waiting", "waits", "waited", "ball", "balls", "flyer"]
接下来,我们将从 NLTK 导入 Porter 词干分析器。
from nltk.stem.porter import PorterStemmer
制作一个名为 ps 的 PorterStemmer 类的实例。
ps = PorterStemmer()
最后用一个列表理解列表中的每个单词。其结果可以在下面看到。
stem_words = [ps.stem(word) for word in word_list]
stem_words>>> ['fli', 'fli', 'wait', 'wait', 'wait', 'ball', 'ball', 'flyer']
在本应具有不同词干的单词被词干化为同一个词根的情况下,可能会发生词干过度。也有可能得到下面的词干。这在本质上是相反的(应该词根相同的单词却不是)。有各种不同的词干分析器可以使用,如波特,英语斯特梅尔,派斯和洛文斯仅举几例。波特梗是最广泛使用的一种。
有些词干分析器比其他词干分析器更“苛刻”或更“温和”,因此您可能需要尝试不同的词干分析器来获得想要的结果。您也可以根据输出决定在停用词移除之前或之后应用词干。
另一方面,词条释义是通过识别一个单词相对于它出现的句子的意义来工作的。本质上,上下文对于引理满足很重要。这种情况下的词根叫做引理。例如,单词“better”可以用单词“good”来表示,因为这个单词就是它的来源。这个过程在计算上比词干提取更昂贵。
我们需要首先从 NLTK(一个包含英语名词、形容词、副词和动词的大型单词数据库)下载 wordnet 资源。
nltk.download('wordnet', quiet=True)
我们将导入并创建一个 WordNetLemmatizer 的实例,并将其应用于我们之前使用的单词列表。
word_list>>> ['flying', 'flies', 'waiting', 'waits', 'waited', 'ball', 'balls', 'flyer']from nltk.stem import WordNetLemmatizer
lm = WordNetLemmatizer()lem_words = [lm.lemmatize(word) for word in word_list]
lem_words>>> ['flying', 'fly', 'waiting', 'wait', 'waited', 'ball', 'ball', 'flyer']
如果我们将 lem_words 与 stem_words (如下)进行比较,您可以看到,尽管在某些情况下相似/相同,但对于一些单词,如“flying”和“flies ”,使用引理化保留了其含义,否则词干化将会丢失该含义。
stem_words>>> ['fli', 'fli', 'wait', 'wait', 'wait', 'ball', 'ball', 'flyer']
标准化/缩放
移除特征(单词/术语)的另一种方法是使用代表术语频率的 tf-idf 重新调整数据,逆文档频率。
tf-idf 方程(图片由作者提供)
这用于查看一个术语(单词)对文档集合(语料库)中的单个文档有多重要。我们本质上是用这个作为权重。术语频率是该单词/术语在文档中出现的频率。例如,如果我们有两个文件𝑑1 和𝑑2 看起来像这样。
𝑑1 =“那个小男孩在房子里。”𝑑2 =“那个小男孩不在房子里。”
如果在文档 1 ( 𝑑1)中感兴趣的术语是“男孩”,这在七个单词中出现一次 1/7=0.1428。我们可以对每个文档中的每个术语做同样的事情。一旦我们计算出术语频率,我们就可以用语料库中文档总数的对数乘以包含该术语的文档数。这告诉我们一个特定的术语与一个文档是更相关、更不相关还是同等相关。在上面的示例中,文档二中的单词“not”对于区分这两个文档非常重要。高权重是由于文档中的高术语频率和语料库(文档集合)中的低术语频率。TF-IDF 也可以用于文本摘要任务。
为了在实践中使用 TF-IDF,我们将需要使用我们前面看到的文本文件。为了做得更好,我保存了一个没有停用词和 HTML 段落标签的版本。这也被转换成小写。本质上,这是使用一个循环一个接一个地打开文件,一行一行地读取它们,分割成单词。如果这个词不是停用词,它将被写入一个新文件。最后, BeautifulSoup 库用于从文本数据中去除所有的 HTML 标签。
**from** bs4 **import** BeautifulSoup
file_path **=** "./NLP/text/"
file_list **=** ['20-01', '20-02', '20-03', '20-04']
**for** file **in** file_list:
current_file **=** open(file_path **+** file **+** ".txt")
line **=** current_file.read().lower()
soup **=** BeautifulSoup(line)
words **=** soup.get_text().split()
**for** word **in** words:
**if** word **not** **in** sw:
formated_list **=** open((file_path **+** file **+** '-f.txt'),'a')
formated_list.write(" "**+**word)
formated_list.close()
current_file.close()
我们现在可以加载这些文件并将它们的内容存储在变量中。
path = "./NLP/text/processed/"txt_file_1 = open(path + "20–01-f.txt")
file_1 = txt_file_1.read()txt_file_2 = open(path + "20–02-f.txt")
file_2 = txt_file_2.read()txt_file_3 = open(path + "20–03-f.txt")
file_3 = txt_file_3.read()txt_file_4 = open(path + "20–04-f.txt")
file_4 = txt_file_4.read()
我们可以将从文件中提取的文本数据放在一个列表中,以便于使用。
data_files = [file_1, file_2, file_3, file_4]
接下来,我们将导入 pandas 库,它在数据科学中经常使用,并提供以表格结构(数据框)表示数据的功能。接下来,我们将从 sklearn 机器学习库中导入tfidf 矢量器,它将标记文档并应用 IDF 权重。
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
最后,我们可以创建该类的一个实例,并使其适合数据。我们可以在数据框中显示结果,其中每个要素和相关的 TF-IDF 权重按降序排列。最后,为了简洁起见,我们将只输出前 30 个术语。
tv = TfidfVectorizer(use_idf=True)
tfIdf = tv.fit_transform(data_files)
df = pd.DataFrame(tfIdf[0].T.todense(), index=tv.get_feature_names(), columns=["TF-IDF"])
df = df.sort_values('TF-IDF', ascending=False)
df.head(30)
熊猫数据框的输出-为简洁起见而缩短(图片由作者提供)
词频
我们可以对单词数据做的最简单的事情之一是查看一个独特的单词在文档中出现的频率(和/或它在语料库中的累积频率)。我们可以使用 FreqDist 函数来计算一个字典,它的键/值对包含每个术语(单词),后跟它在文本中出现的次数。例如,单词“government”在我们之前使用的原始简短示例文本中出现了 3 次。
dist = nltk.FreqDist(str_list)
dist>>> FreqDist({'government': 3, 'p': 2, 'ndc': 2, 'capacity': 2, 'last': 1, 'july': 1, 'called': 1, 'energy': 1, 'sector': 1, 'debt': 1, ...})
为了使它更容易可视化,我们可以将它输出为一个图。正如您所看到的,单词 government 出现了 3 次,字母 p(来自 HTML 段落标记)出现了两次,其他单词只出现了一次。
dist.plot();
单词的情节和出现频率(图片由作者提供)
另一种显示单词频率的视觉方式是使用单词云,这里单词越大,出现的次数就越多。为此,我们可以使用 WordCloud 库。
from wordcloud import WordCloud
我们还需要来自用于各种可视化的 matplotlib 库中的绘图( plt )。 %matplotlib inline 设置绘图命令,以确保当与 Jupyter 笔记本等字体端一起使用时,绘图出现在代码单元格下方并存储在笔记本中。
import matplotlib.pyplot as plt
%matplotlib inline
接下来,我们需要将数据转换成 word cloud 函数的正确格式。它接受一个字符串,因此我们将把标记化的字符串列表折叠成一个单独的字符串,单词之间留有空格,使用内置的 join 函数进行字符串连接(将字符串连接在一起)。
flattend_text = " ".join(str_list)
接下来,我们可以使用传入文本字符串的 generate 函数创建单词云。
wc = WordCloud().generate(flattend_text)
最后,我们将使用双线性插值选项输出关闭轴文本的单词云,以平滑图像的外观。
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.show()
词云输出(图片由作者提供)
您还可以使用其他可选参数。一些流行的方法包括设置最大字体大小(max_font_size)和包含的字数(如果你有很多字的话会很有帮助)以及改变背景颜色。例如,我们可以将显示的最大字数设置为 10,并将背景设置为白色。
wc_2 = WordCloud(max_words = 10, background_color = "white").generate(flattend_text)
plt.imshow(wc_2, interpolation='bilinear')
plt.axis("off")
plt.show()
词云输出(图片由作者提供)
n 元语法分析
当我们把单词符号化,并把它们表示成一个单词包时,我们就失去了一些上下文和意义。单个单词本身并不能说明太多,但是它们与其他单词一起出现的频率可能会说明很多。例如,“信息”和“治理”这两个词可能经常一起出现,并具有特定的含义。我们可以用 n 元语法来解释这一点。这是指一起出现的多个令牌。这些标记可以是单词或字母。这里我们将使用单词。两个词(𝑛=2)被称为双字,三个词被称为三字,等等。使用 n 元语法有助于我们保留文本中的一些含义/上下文。
我们将使用前面相同的短文本:
txt>>> 'the government last july called the energy sector debt situation a state of emergency. <p> this was during the mid-year budget review during which the finance minister ken ofori-atta castigated the previous ndc government for entering into obnoxious take-or-pay contracts signed by the ndc , which obligate us to pay for capacity we do not need . <p> the government pays over gh ? 2.5 billion annually for some 2,300mw in installed capacity which the country does not consume .'
接下来,我们可以从 NLTK 实用程序包中导入 ngrams 函数。
from nltk.util import ngrams
我们将使用与之前相同的标记器再次标记文本。
tk = TreebankWordTokenizer()
tk_words = tk.tokenize(txt)
最后,我们将这个标记化的列表传递到 ngrams 函数中,并指定𝑛(在本例中为二进制数的 2)(为简洁起见而缩写)。
bigrams = list(ngrams(tk_words, 2))
bigrams>>> [('the', 'government'),
('government', 'last'),
('last', 'july'),
('july', 'called'),
('called', 'the'),
('the', 'energy'),
('energy', 'sector'),
('sector', 'debt'),
('debt', 'situation'),
('situation', 'a'),
('a', 'state'),
('state', 'of'),
('of', 'emergency.'),
('emergency.', '<'),
('<', 'p'),
('p', '>'),
('>', 'this'),
('this', 'was'),
('was', 'during'),
('during', 'the'),
('the', 'mid-year'),
('mid-year', 'budget')
...
在这里,我们可以看到像“合同”和“签署”或“政府”和“支付”这样的术语,它们提供了比单个单词更多的上下文。作为预处理的一部分,您也可以从文本数据中过滤出 n 元语法。
我们还可以使用 BigramCollocationFinder 类来确定二元模型出现的次数。这里我们按降序对列表进行排序。
from nltk.collocations import BigramCollocationFinder
finder = BigramCollocationFinder.from_words(tk_words, window_size=2)
ngram = list(finder.ngram_fd.items())
ngram.sort(key=lambda item: item[-1], reverse=True)
ngram>>> [(('the', 'government'), 2),
(('<', 'p'), 2),
(('p', '>'), 2),
(('which', 'the'), 2),
(('government', 'last'), 1),
(('last', 'july'), 1),
(('july', 'called'), 1),
(('called', 'the'), 1),
(('the', 'energy'), 1),
(('energy', 'sector'), 1),
(('sector', 'debt'), 1),
(('debt', 'situation'), 1),
(('situation', 'a'), 1),
(('a', 'state'), 1),
(('state', 'of'), 1),
(('of', 'emergency.'), 1),
(('emergency.', '<'), 1),
(('>', 'this'), 1),
(('this', 'was'), 1),
(('was', 'during'), 1),
(('during', 'the'), 1),
(('the', 'mid-year'), 1),
(('mid-year', 'budget'), 1)
...
情感分析
这包括分析文本以确定文本的“正面”或“负面”程度。这可以给我们关于人们的观点/情绪的信息。这可以应用到一些事情上,比如浏览产品评论,以获得产品是否被看好的总体感觉。我们还可以将它用于研究目的——例如,人们在他们的推文中对戴口罩持肯定还是否定态度。为此,我们可以训练一个模型或使用现有的词典。VADER 是用 Python 实现的。这会产生一个介于-1 和+1 之间的正面、负面和中性情绪得分。它还产生一个复合分数,即阳性+中性标准化 (-1 比 1)。首先,我们将下载词典。词典包含与单个单词或文本串相关的信息(例如,语义或语法)。
nltk.download("vader_lexicon", quiet=True)
我们导入了情感分析器类SentimentIntensityAnalyzer,并创建了一个名为 snt 的实例(用于情感)。
from nltk.sentiment.vader import SentimentIntensityAnalyzer
snt = SentimentIntensityAnalyzer()
然后,我们可以向函数 polarity_scores 传递一些文本数据(例如,我们的第二个文件)。您可以在下面的字典数据结构中看到作为键/值对返回的分数。
snt.polarity_scores(data_files[1])>>> {'neg': 0.101, 'neu': 0.782, 'pos': 0.117, 'compound': 1.0}
在这个例子中,查看第二个文本文件,我们可以看到一个占主导地位的中性情绪得分(0.782),后面是一个正得分(0.117)和一个负得分(0.101)。你可以在不同的时间点比较情绪,看看它是如何变化的,或者在不同的小组之间。有时使用的另一个指标是净情绪得分(NSS),它是通过从积极得分中减去消极得分来计算的。首先,我们需要将分数存储在一个变量中来访问它们。
sent_scores = snt.polarity_scores(data_files[1])
然后我们可以从正数中减去负数。
nss = sent_scores['pos'] — sent_scores['neg']
print("NSS =", nss)>>> NSS = 0.016
主题建模
主题建模通常用于文本挖掘,它允许我们创建一个统计模型来发现文本数据中的主题。有各种不同的方法/算法可以做到这一点。我们将在这里看一对夫妇。这是一种无人监管的方法。我们首先来看看潜在语义分析(LSA),它与主成分分析(PCA)的工作原理相同。LSA 是一个线性模型,并假设文档中的术语呈正态分布。它还使用 SVD(奇异值分解),这在计算上是昂贵的。这种方法减少了数据中的噪声。
注意: SVD 的工作原理是将一个文档术语矩阵分成 3 个连续的方阵(其中一个是对角矩阵),然后将它们转置并再次相乘。这个方法可以用来求矩阵的逆矩阵。
第一阶段涉及创建文档术语矩阵。这在行中表示文档,在列中表示术语,在单元格中表示 TF-IDF 分数。然后,我们将 SVD 应用于这个矩阵,以获得最终的主题列表。
文档-术语矩阵,行中有文档,列中有术语。单元格包含 TF-IDF 分数(图片由作者提供)
我们可以使用之前使用的相同的tfidf 矢量器来计算 TF-IDF 分数。我们将限制术语(特征)的数量,以将所需的计算资源减少到 800。
from sklearn.feature_extraction.text import TfidfVectorizer
v = TfidfVectorizer(stop_words='english', max_features=800, max_df=0.5)
X = v.fit_transform(data_files)
如果我们查看文档-术语矩阵的维度,我们可以看到行中有 4 个文档,列中有 800 个术语。
X.shape>>> (4, 800)
我们现在需要实现 SVD,我们可以使用截断的 SVD 类来实现它。这将为我们做繁重的工作。
from sklearn.decomposition import TruncatedSVD
我们可以用参数 n_components 指定主题的数量。在这种情况下,我们将它设置为 4,假设每个文档有一个不同的主主题(**注意:**我们还可以使用像主题一致性这样的方法来确定主题的最佳数量 k )。
svd = TruncatedSVD(n_components=4)
接下来,我们拟合模型并获得特征名称:
svd.fit(X)doc_terms = v.get_feature_names()
现在我们可以输出与每个主题相关的术语。在这种情况下,4 个主题中的每一个都有前 12 个。
for i, component in enumerate(svd.components_):
terms_comp = zip(doc_terms, component)
sorted_terms = sorted(terms_comp, key=lambda x:x[1], reverse=True)[:12]
print("")
print("Topic "+str(i+1)+": ", end="")
for term in sorted_terms:
print(term[0], " ", end="")>>> Topic 1: rsquo href ldquo rdquo ventilators easter keytruda quebec unincorporated ford books inmates
Topic 2: davos wef sibley denly stamler comox nortje caf pd rsquo href ldquo
Topic 3: hopland geely easyjet davos vanderbilt wef asbestos macy jamaat sibley denly stamler
Topic 4: rsquo href ldquo rdquo quebec div eacute src noopener rel mdash rsv
然后,由您根据这些主题所包含的单词来确定它们可能代表什么(主题标签)。
主题建模的另一个流行选项是潜在狄利克雷分配(LDA ),不要与其他 LDA(线性判别分析)混淆。LDA 假设单词的狄利克雷分布,并创建语义向量空间模型。其实现的具体细节超出了本入门书的范围,但本质上,它将文档术语矩阵转换为两个矩阵。一个表示文档和主题,第二个表示主题和术语。然后,该算法尝试根据主题生成所讨论的单词的概率计算来调整每个文档中每个单词的主题。
我们使用与前面 LSA 相同的概念。首先,我们用计数矢量器将数据标记化。然后,我们再次创建 LDA 类的实例,将主题数量设置为 4,并输出前 12 个。
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
fitted = cv.fit_transform(data_files)
from sklearn.decomposition import LatentDirichletAllocation
lda = LatentDirichletAllocation(n_components=4, random_state=42)
lda.fit(fitted)
然后,我们可以像以前一样输出结果:
doc_terms = cv.get_feature_names()for i, component in enumerate(lda.components_):
terms_comp = zip(doc_terms, component)
sorted_terms = sorted(terms_comp, key=lambda x:x[1], reverse=True)[:12]
print("")
print("Topic "+str(i+1)+": ", end="")
for term in sorted_terms:
print(term[0], " ", end="")>>> Topic 1: said 19 covid people coronavirus new health also would one pandemic time
Topic 2: 021 040 25000 421 4q 712 85th 885 accrues accuser acuity afterthought
Topic 3: said coronavirus people health 19 new covid also cases virus one would
Topic 4: 021 040 25000 421 4q 712 85th 885 accrues accuser acuity afterthought
如果我们认为这些数字不相关,我们可能还想回去过滤掉它们。您可以看到,这与我们之前看到的 LSA 产生了非常不同的结果。首先,LSA 是主题建模的良好开端。如果需要,LDA 提供不同的选项。
摘要
这本初级读本概述了现代 NLP 任务中使用的一些常用方法,以及如何用 Python 实现这些方法。每种方法都有细微差别,需要根据手头的任务来考虑。还有像词性标注这样的方法,也称为语法标注,可以为算法提供额外的信息。例如,你可以在某些单词上标注该单词是名词、动词、形容词还是副词等信息。这通常被表示为具有单词、标签的元组列表,例如[(‘build ‘,’ v ‘),(’ walk ‘,’ v ‘),(’ mountain ‘,’ n’)]。还有许多方法可以表示单词和术语,用于后续处理,例如 word2vec 和单词包等。您选择的格式将再次取决于任务和算法要求。与所有机器学习和数据科学一样,数据预处理所花费的时间通常最长,并且对生成的输出有很大影响。读完这本初级读本后,你应该有希望开始从文本数据中获得一些有趣的见解,并对你可能用来分析这类数据的一些方法有所了解。
参考
[1]戴维斯,马克。(2019-)冠状病毒语料库。可在 https://www.english-corpora.org/corona/的在线购买。