使用分布式计算进行神经成像
是的,终于到了使用 PySpark 进行医学成像的时候了
学分:Pexels
如今,由于分辨率、磁场强度、联合体和基础设施的提高,神经影像数据也成为大数据。事实上,每天用几种医学成像模式采集大量的 3D 和 4D 图像。几个数据集与数千个文件一起公开可用,仅举几个例子:
- 人类连接体项目(HCP)
- 1000 功能连接体项目 (1000FCP)
- 老年痴呆症神经影像倡议
这些数据集包括功能、结构、扩散、人口统计和其他信息,这些信息每天都在增加,这就产生了用分布式和并行系统加速处理的需求。在影像模态中,功能磁共振成像(fMRI)是一种通过检测血流的相关变化来测量大脑活动的技术。在被称为血液动力学反应的过程中,活跃的神经元比不活跃的神经元接收更多的氧气,这可以通过核磁共振成像仪检测到。从数据的角度来看,这被转换成 4D 体积(对于脑扫描是 3D 的,然后在时间上跟随血液演变)。因此,如果我们分析几个时间点,每个文件的大小以陡峭的斜率线性增长。此外,信号极易受噪声影响,因此必须应用多种噪声消除算法。类似的情况也发生在扩散数据上,这里我们用不同的梯度来代替时间点。因此,文件也将是 4D 的,但不是时间点,每个体积将代表不同的梯度角度。
fMRI 文件的简单表示(鸣谢 Sarraf & Ostadhashem
存储、预处理和分析这种类型的数据在计算上是昂贵且耗时的,因此应该利用具有既定方法的并行计算的优势。功能性和扩散 MRI 的解决方案之一是**使用 Spark / PySpark。**这些工具允许读取和加载成像数据,将它们转换为可并行操作的弹性分布式数据集,并将它们转换回成像格式,如 NIFTI 。据报道,与传统方法相比,使用(Py)Spark 的计算时间可以减少到四分之一(参见例如 Makkie 等人 2019 )。在本文中,我总结了一些越来越复杂的实验/测试。从简单的独立成分分析开始,到功能连接,再到训练大脑时间序列的深度卷积网络。
Spark 大数据技术和工具,如 Hadoo p,是面向可靠、可扩展、分布式计算的开源软件编程平台和项目。特别是 Spark 正变得广泛使用,这是由于使用数千个节点的计算时间的改进,以及 Spark (MLlib)和 GraphX(GraphX)的机器学习库的扩展。特别是对于神经成像,典型的管道将使用 Nibable 包(http://nipy.org/nibabel)来访问数据(DICOM 或 Nifti),然后将数据转换为**弹性分布式数据集(RDD)格式,这是 Spark 架构的基本结构。**实际上,rdd 是分区的记录集合。奇特之处在于它们是容错的,可以并行操作。Spark 独立调度程序由三个主要进程组成:主进程、工作进程和驱动程序。应用程序被提交给驱动程序,然后主程序协调进程,将它们分发到集群上的工作节点。典型的工作流程(未显示细分主/工作)如下所示:
fMRI 火花管道示例(鸣谢 Sarraf & Ostadhashem
Saman Sarraf 和 Mehdi Ostadhashem 向展示了一项任务,包括加载、通过 MELODIC 工具进行独立组件检测,比较了使用和不使用 PySpark 在 Python 中执行的相同过程,显示了以下计算成本:
同一任务的处理时间(学分萨拉夫&奥斯塔哈舍姆
尽管很成功,但这只是在本地机器上的一个简单实验。Boubela 等人在 2015 年描述了一个更现实的场景,他们甚至在亚马逊云上使用了下面的架构。在这种情况下,任务是通过使用时间序列的皮尔逊相关性来构建功能性连接体。
架构可以总结如下
使用建议分析方法的数据流。2015 年布贝拉等人的功劳
特别是,在前面提到的研究中,作者在具有 192 GB RAM、两个英特尔至强 X5690 处理器和四个 Nvidia Tesla GPUs 的服务器 Ubuntu Linux(可选 GPU 计算)上从 fMRI 数据计算功能连接性;Spark 集群由 10 台 48 GB 内存的 Linux 机器和 2 个英特尔至强 X5550 处理器组成。
为了在云上实现这一点,亚马逊提供了一个名为 EMR 的解决方案,已经由 Yarn、Hadoop 和 Spark 进行了设置。常规设置是一个带有 **S3 数据桶的集群。**下面的视频展示了向这样的基础设施发送数据是多么容易。
关于实现,作者设计了Scala(Spark 的本地语言)中的专用文件阅读器,在多个节点上并行读取 4D NIfTI 文件,并将结果收集到 Spark 环境中的 RDD 中。读者已经友好的通过 github 分发。
voxelwise 时间序列数据存储在一个 RowMatrix 对象的列中,该对象是用于与 Apache Spark 机器学习库 MLlib 接口以处理大数据的最常见对象。运行独立成分分析或主成分分析的代码将非常类似于众所周知的 Python 脚本,如 Sci-kit :
**from** **pyspark.mllib.linalg** **import** Vectors
**from** **pyspark.mllib.linalg.distributed** **import** RowMatrix# Toy exmple data
rows = sc.parallelize([
Vectors.sparse(5, {1: 1.0, 3: 7.0}),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)
])
mat = RowMatrix(rows)
*# Compute the top 4 principal components.* pc = mat.computePrincipalComponents(4)
*# Project the rows to the linear space spanned by the top 4 principal components.* projected = mat.multiply(pc)
此外,连接组学研究特别适合 Spark,因为 GraphX 库定义了两个 rdd,一个包含顶点,另一个包含边,以便在图上进行分布式计算。
其他值得一提的研究是显微镜 Spark 框架 Thunder ,以及 Milad Makkie 关于用于 fMRI 大数据分析的快速和可扩展的分布式深度卷积自动编码器的工作。
演职员表 Makkie 等人 2019
特别是 Makkie 和他的同事表明,即使在 Spark 上运行来自 TensorFlow 的 autoencoder,也有可能在 Spark 上实现 fMRI 分析框架(如下图所示)。需要重点承担的是,这个版本的 TensorFlow 不是传统的,需要专门下载。
# for tensorflow>=2.0.0
pip install tensorflowonspark# for tensorflow<2.0.0
pip install tensorflowonspark==1.4.4
总之,基于 Spark 的分布式计算已经被广泛应用于多个任务。如今,需要分析来自 fMRI、扩散或显微镜的大数据集,利用分布式结构作为 Spark 联合到 GPU 上的并行计算可以是一种解决方案。我在这里只提到了几个例子,显示了计算时间和其他性能的提高。
如果科学家无法访问 Spark 集群(例如从大学),一个可能的解决方案是在亚马逊云上运行 EMR 结构,使用 S3 存储桶来存储数据(如上面的视频所示)。
在神经成像的特殊情况下,加强再现性也很重要,并且还应该记住另外两件事(尽管它们不是特定于分布式系统的,也应该用于独立的应用中):
fMRIprep 工作流程(图片来自 J.Gorgolewski )
如果您对 neuroimage 的分布式和并行解决方案有进一步的建议,欢迎在此发表评论。
参考文献
随意连接:
将 Docker 用于深度学习项目
作为一名机器学习工程师,我每天都使用 docker 容器,这帮助我节省了大量时间,并保持有序。
保罗·泰森在 Unsplash 上的照片
在这篇文章中,我将解释如何在我的日常项目中使用 Docker。关于这个主题有很多很好的文档和视频,但我想分享一种我在从事的几个行业项目中一直使用的方法。
Docker 是什么?
Docker 使用操作系统级虚拟化来交付称为容器的软件包中的软件。每个停靠容器都是从停靠图像中创建的。一个映像拥有构建环境的所有信息(库、文件夹、文件、操作系统等)。容器是相互隔离的。
Docker 流-按作者分类的图像
官方 Docker 文档可以在此链接找到。
要下载 Docker,您可以访问这个链接,在本文的其余部分,我将假设 Docker 已经正确安装在机器上。
深度学习项目为什么要用 Docker?
当你在机器上运行深度学习培训时(在云上或本地),你需要能够轻松地运行培训,而不必每次都为设置环境而挣扎。此外,如果出于某种原因,您想在另一台机器上运行此培训,您不希望再次经历所有设置。
理想情况下,我们希望有一个命令可以放心地跨机器运行。这就是为什么我几乎一直使用 Docker 容器进行培训。
以下是 Docker 对我的主要优势:
- 所有必需的包都已经安装了训练的正确版本:例如,如果我想使用 Tensorflow2 进行一次训练,我为此配置了一个 docker 映像,另一个用于 Tensorflow1 ,另一个用于 Pytorch 。
- 如果在单个 GPU 上运行,我可以对所需的 GPU 进行分区。
- 由于所有容器都是独立的,因此如果执行崩溃,其他进程不会受到影响——因此其他 GPU 不会受到影响,例如当我选择特定的 GPU 用于训练时。
- 如果使用 git 存储库作为源代码,我通常会将它添加到主 docker 映像中,然后在每次创建 docker 容器时进行 git pull,并切换到我想要使用的分支/提交。
- 本地文件夹/文件和 NAS 可以在启动容器时轻松挂载——因此不需要复制,节省了时间,尤其是在我调试某些东西的时候。
Docker 简介
以下是您需要了解的一些基本命令:
- 显示当前运行的容器:
docker ps
- 显示所有容器(甚至那些不再运行的容器):
docker ps -a
- 显示本地保存的图像:
docker images
- 移除 docker 容器:
docker stop container_name # if container is running
docker rm container_name
- 移除所有 docker 容器(不再运行):
docker container prune
- 移除图像:
docker rmi image_name
- 删除所有 docker 图像(非常小心这一个!):
docker image prune -a
在 Docker 容器中运行 Tensorflow2 培训
Docker 图像
首先,我们提取包含 Tensorflow 版本 2.1.04 和 Python3 的 NVIDIA 映像(这将需要一些时间):
docker pull nvcr.io/nvidia/tensorflow:21.04-tf2-py3
Docker pull done 按作者分类的图像
完成后,我们使用命令docker images
检查图像是否在本地图像列表中:
列出 Docker 本地图像—按作者分类的图像
我们确实看到了带有标签21.04-tf2-py3
的图像nvcr.io/nvidia/tensorflow
。
用这个图像启动一个容器,并浏览一些标志
我们可以使用此图像创建一个容器:
docker run -it --rm --name tensorflow2-container --network=host nvcr.io/nvidia/tensorflow:21.04-tf2-py3 bash
Tensorflow2 图像中的 Docker 容器已打开-作者提供的图像
我们在这个命令中使用了一些特定的标志:
-it
用于打开一个交互终端--rm
是用来当我们退出集装箱时,它会把它移走--name
用于用自定义名称给容器命名--network=host
用于访问容器中的互联网(与主机相同的网络)- 然后,我们有了要使用的图像的名称,后跟
bash
以在容器中创建一个交互式 shell
如果我们在另一个终端中运行docker ps
,我们将看到我们的新容器:
docker ps 命令来查看新启动的容器——按作者排序的图像
我们确实看到了我们的集装箱tensorflow2-container
✅
现在,如果我们想将我们的本地工作区用于训练脚本,我们可以通过使用-v /Users/margauxmforstyhe/workspace/:/workspace
将我的工作区文件夹挂载到容器中。此参数将我们计算机上的工作区文件夹挂载到容器中的基本工作区文件夹。
在我们当前的容器中,如果我们运行ls
,我们会看到:
Docker 容器中的默认工作空间文件夹-按作者排序的图像
让我们退出当前使用命令exit
打开的 docker 容器,并使用工作区文件夹创建一个新的容器:
docker run -it --rm --name tensorflow2-container --network=host -v /Users/margauxmforstyhe/workspace/:/workspace nvcr.io/nvidia/tensorflow:21.04-tf2-py3 bash
并运行ls
:
Docker 容器中的本地工作区文件夹-按作者排序的图像
➡️本地工作区安装在 Docker 容器中,我们现在可以使用它进行培训/测试。
当我做一些训练测试时,我使用一台带有 GPU 的机器,并为 GPU0 选择一个带有--gpu=device=0
的 GPU。然后,当我完成测试时,我通常运行如下命令来开始训练:
docker run -i -d --rm --gpus=device=0 --name tensorflow2-container --network=host -v /Users/margauxmforstyhe/workspace/:/workspace nvcr.io/nvidia/tensorflow:21.04-tf2-py3 bash -c "export PYTHONPATH=/workspace && python3 /workspace/training_repo/train.py .... {parameters for the training}"
这里我们有一个处于分离模式的 docker 容器 ( -d
意味着我们看不到终端中代码的执行)在 0 号 GPU 上运行本地训练脚本。
**注意:**这和推理脚本完全一样,你只需要改变被调用的 python 脚本。
另一种方法是拥有一个包含所有训练脚本的 git 存储库,并将其作为映像的一部分添加进来。让我们用一个 Dockerfile 来做这件事吧!
使用 Docker 文件构建 Docker 映像,并使用 git 存储库作为训练存储库
Dockerfile 文件用于创建图像。例如,我们想要在我们之前使用的映像nvcr.io/nvidia/tensorflow:21.04-tf2-py3
之上创建一个映像,然后我们想要从 Github 克隆training _ repo并安装运行培训的所有需求(例如install rasterio
,或者安装一个特定包的版本),这为我们提供了这个 docker 文件:
FROM nvcr.io/nvidia/tensorflow:21.04-tf2-py3RUN apt-get update
RUN git clone [https://github.com/MargauxMasson/training_repo.git](https://github.com/MargauxMasson/training_repo.git)
RUN pip install -r /workspace/training_repo/requirements.txt
RUN lsWORKDIR /workspace/CMD "ls"
为了构建这个图像——我们将把它命名为tensorflow-21.04-tf2-py3-with-requirements-and-git-repo
——我们使用命令docker build
(需要在 Dockerfile 所在的文件夹中运行):
docker build . --network=host -t tensorflow-21.04-tf2-py3-with-requirements-and-git-repo
使用 Dockefile 构建图像—按作者分类的图像
我们看到图像的构建工作正常,当我们用docker images
检查时,我们确实看到了新的图像tensorflow-21.04-tf2-py3-with-requirements-and-git-repo
。
**注意:**docker 构建命令中的“.”
表示名为Dockerfile
的 docker 文件位于我们运行命令的文件夹中。
现在,当我们使用这个映像启动容器时,我们不需要挂载本地工作区,因为 git repo 已经在映像中了:
docker run -it --rm --name tensorflow2-container --network=host tensorflow-21.04-tf2-py3-with-requirements-and-git-repo bash
实际上, training_repo 就在容器的工作区中。
即使 git 存储库中的代码发生了变化,也可以不加修改地使用这个映像。当启动容器时,我们可以git pull
或git checkout
到任何想要的分支/提交:
docker run -i -d --rm --gpus=device=0 --name tensorflow2-container --network=host tensorflow-21.04-tf2-py3-with-requirements-and-git-repo bash -c "cd /workspace/training_repo && git pull && git checkout my_training_dev_branch && export PYTHONPATH=/workspace && python3 /workspace/training_repo/train.py .... {parameters for the training}"
同样,正如启动容器时所建议的,我们可以添加这些标志:--shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864
所以:
docker run -i -d --rm --gpus=device=0 --name tensorflow2-container --network=host --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 tensorflow-21.04-tf2-py3-with-requirements-and-git-repo bash -c "cd /workspace/training_repo && git pull && git checkout my_training_dev_branch && export PYTHONPATH=/workspace && python3 /workspace/training_repo/train.py .... {parameters for the training}"
有很多方法可以使用 Docker,但这是我喜欢使用它进行训练和推理的方式,它帮助我保持有组织性,因为我有一些特定的图像(或至少 Docker 文件),我可以自信地使用它们,并知道我的训练代码将毫无困难地运行。
GIF 来自https://media.giphy.com/media/g0gtihsbzj5pSWgcml/giphy.gif
使用 EDA 生成业务理解
数据科学
对客户细分和外部因素的洞察
在几个以神经网络为特色的背靠背项目(你可以在这里探索这里和这里)并决定从头开始构建一个神经网络之后,我想开始一个以 EDA 为重点的项目,以保持我的熊猫技能敏锐并回到数据科学的根源。从数据中获得深刻的、可操作的理解和建议。
数据
首都自行车共享数据集分布很广。可以在 UCI 或数据世界上找到。它包含 2011 年和 2012 年的约 17,000 条意见。虽然数据相当干净,但有点棘手,因为数据的粒度,或者一次观察代表的是一天中的一个小时。这些功能包括天气数据 —温度、风阻和天气类型,时间数据 —日期、星期几、小时和假日,以及乘客数量 —分为临时乘客和注册乘客。
如果我们绘制两年内每小时的总用户数,我们可以快速了解全年的乘客量变化。
如果适用的话,我喜欢通过查看我的主要特性和目标特性的分布来开始我的 EDA 项目。在这一点上,我没有建模任何东西,但是查看这个分布可以快速了解高级行为。
一段时间内的用户总数。图片作者。
客户细分…
…是一个强大的工具。它可以阐明客户群之间的主要差异,以及这些不同的偏好如何影响业务决策并推动客户体验的改善。
潜伏在这个数据集中的是一个客户细分力量的完美例子,以及当你没有意识到它的发生或者你愿意忽略它时会发生什么!
按一周中的每一天来看每小时的总用户数,这几天之间似乎没有显著差异!
一周中每一天每小时的用户总数。图片作者。
所以我们可能会得出结论,一周中的某一天并不会真正影响乘客数量,对吗?好吧,让我们试着把总用户分解成临时用户和注册用户。
一周中每一天每小时的登记乘客和临时乘客。图片作者。
这里我们看到一个非常不同的故事!这两个群体有相反的行为。注册用户通常是在工作日骑行的商务通勤者,而临时用户是为了休闲或锻炼,因此在周末骑行更多。凑巧的是,将它们聚合在一起几乎完美地隐藏了这种区别。
这就是 EDA 可以为企业提供有价值的细节的地方。现在,我们可以开始区别对待这些细分市场,以满足它们不同的定位和需求。
也许我们想为通勤者提供折扣,或者为临时用户启动一个“打卡”计划,让他们每乘坐 X 次车就能获得一次免费租赁。或者尝试用打折的升级包把临时用户转化为注册用户。或者走另一条路!只在周末给注册用户一个免费的“伙伴通行证”,这样他们可以带一个朋友,增加临时乘车人数(并可能获得一个新客户)。
我们可以通过查看每小时登记的乘客数量来确认登记的乘客主要是通勤者。上午 8 点和下午 5 点至 6 点的高峰时段有明显的高峰。
一天中每个小时登记的乘客人数。图片作者。
我们看到休闲骑手的模式非常不同(记住,他们在周末骑得更多)。每小时的客流量集中在下午,毕竟那是最舒适的乘车时间!请注意,y 轴的比例相对于注册乘客量要小得多。
一天中不同时段的临时乘客。注意与上面相比的 y 轴刻度。图片作者。
为了巩固我们的理解,我们可以看看这两组人在假期是如何骑车的。如果我们知道注册用户大多是商务通勤者,我们可能会希望他们在假期少骑点车,因为他们不用上班。另一方面,也许临时用户利用这一天去公园骑自行车!
假日的登记乘客和临时乘客。图片作者。
这似乎有一些证据,尽管数据中似乎也有相当多的噪音。如果是假日,注册用户确实骑得少,临时用户骑得差不多——大多数假日是在工作周,所以因为临时用户在工作周骑得不多,我们真的不希望看到他们的骑行量有很大差异。
天气
现在让我们看看一些影响乘客的外部因素,主要是环境因素,如天气和温度。气温对整体乘客量有很大影响。甚至全年客流量的差异也常常归因于温度。冬天的客流量并不是因为冬天而减少,而是因为外面是 0 度!
数据集有 4 类天气:
- 晴朗,少云,部分多云
- 薄雾和多云,薄雾和破碎的云,薄雾
- 小雪、小雨和雷雨、小雨和零散的云
- 大雨、冰粒、雷暴和雾、雪和雾
这是一个相当武断的评级系统,但它会完成工作。
按天气类型划分的注册乘客和临时乘客。图片作者。
在这两个客户群中,天气恶劣程度和乘客量之间存在负相关关系。随着天气恶劣程度的增加,乘客数量会减少。(或者你可以说这是一种积极的关系,随着天气的改善,乘客数量会增加)
但是在注册乘客中有一个有趣的现象。暴风雨条件(最坏的天气)的 95%置信区间包括其他天气类型!这里发生了什么事?
嗯,没有进一步的调查,很难说出细节,但我们可以假设。例如,我假设对于许多骑车人来说,自行车租赁是他们唯一的通勤选择,因此无论天气如何都必须骑车上下班。他们的行为非常缺乏弹性,或者很少有其他选择(也许他们没有汽车,火车/地铁也不在他们的社区服务)。
另一个假设是,这些通勤者在早上条件正常时骑车去上班,然后一场突如其来的暴风雨(或错过的天气预报)让他们骑车去上班。他们别无选择,只能骑自行车下班回家,否则他们可能会努力避免这种情况。
结论
探索性数据分析是数据科学过程中的一个重要步骤。你需要成为数据专家,才能理解你正在看的东西!即使是看起来简单明了的数据,比如我们的原始数据,乘客数量看起来稳定,也可能隐藏着细微的差别,这将改变你对你的商业案例的看法。
深入了解您的客户群将使您能够针对特定人群制定战略,并避免“一刀切”的客户获取和保留心态。毕竟,我们已经看到注册用户和临时用户有非常不同的需求,但在对公司的影响方面却是互补的。你不想把重点放在注册用户上,以至于疏远临时用户。
最后,一些因素——比如众所周知的天气——将永远在你的控制之外。再多的营销或复杂的建模都无法阻止一月对大多数乘客来说太冷,也无法阻止他们乘坐预报中有雷雨的火车。但了解这些外在因素如何影响你的客户并将其纳入你自己的业务预测中仍然很重要。
你可能只需要一些创造性的思维——也许是一个动态定价模型,当天气/温度变得不舒服时降低价格,以鼓励继续骑行。无论你的商业案例是什么,都要带着不断学习的心态去对待它,记住要像使用左脑一样多的使用右脑。
连接
我一直在寻找连接和探索其他项目!这个项目的回购,所有的数字和代码都可以在我的 GitHub 上找到。
使用实体嵌入提高机器学习模型的性能
在 ML 模型中实现由神经网络学习的嵌入的教程
米卡·鲍梅斯特在 Unsplash 上的照片
本文的目的是提供关于如何在 ML 模型中实现由神经网络学习的嵌入的信息。因此,我们不会详细讨论嵌入理论。
注意:假设你了解深度学习和机器学习的基础知识
什么是实体嵌入,为什么使用实体嵌入?
不严格地说,实体嵌入是分类变量以连续方式的向量表示。在神经网络的上下文中,嵌入将每个实例的特征从其原始空间转换到低维向量空间表示,同时保留来自其特征的信息,并且还在嵌入空间中有意义地表示每个类别。因此,使用嵌入可以让模型了解每个变量之间的相互关系,从而提高性能。
实现
使用由神经网络学习的嵌入来训练 ML 模型的一般步骤是:
- 用嵌入层训练神经网络。
- 从训练好的神经网络中提取嵌入。
- 用来自训练好的神经网络的分类变量的嵌入替换分类变量。
- 使用嵌入来训练你的 ML 模型。
在本教程中,我们将使用 sklearn、fastai、PyTorch 和著名的泰坦尼克号数据集进行演示。您可以使用自己选择的框架来复制这一点。在本教程之前,已经完成了数据清理和特征工程。
#required libraries
from sklearn.metrics import classification_report
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
from fastai.tabular.all import *
在使用 FastAI 将数据输入神经网络之前,对数据进行预处理
dls = TabularPandas(df_train, y_names="Survived", y_block=CategoryBlock,
cat_names = ['Cabin', 'Title', 'Sex'],
cont_names = ['Age', 'Pclass', 'Fam_size'],
procs = [Categorify, FillMissing, Normalize], splits = RandomSplitter(valid_pct=0.2)(range_of(df_train)))
to_nn = dls.dataloaders()
创建一个 TabularLearner,用 lr_find()找到一个合适的学习速率。
learn = tabular_learner(to_nn, metrics=accuracy)learn.lr_find()
训练神经网络。
learn.fit_one_cycle(8, 2e-2)
从训练的神经网络中提取嵌入,并用来自训练的神经网络的分类变量的嵌入来替换分类变量。
#function to embed features ,obtained from fastai forums
def embed_features(learner, xs):
xs = xs.copy()
for i, feature in enumerate(learner.dls.cat_names):
emb = learner.model.embeds[i]
new_feat = pd.DataFrame(emb(tensor(xs[feature], dtype=torch.int64)), index=xs.index, columns=[f'{feature}_{j}' for j in range(emb.embedding_dim)])
xs.drop(columns=feature, inplace=True)
xs = xs.join(new_feat)
return xsemb_xs = embed_features(learn, to_nn.train.xs)
emb_valid_xs = embed_features(learn, to_nn.valid.xs)
使用嵌入来训练你的 ML 模型。
rf = RandomForestClassifier(n_estimators=400, min_samples_leaf=10,
max_features=1/2, max_samples = 50)
rf = rf.fit(emb_xs,to_nn.train.y)
valid_preds = rf.predict(emb_valid_xs)
print(classification_report( to_nn.valid.y,valid_preds))
图片来自作者。
与在原始数据集上训练的随机森林相比,在嵌入数据上训练的随机森林上,我们获得了 3%的准确性提高,这是一个相当大的提高!
结论
使用实体嵌入不仅可以帮助我们更好地理解和可视化我们的数据,而且还可以提高 ML 模型的性能。嵌入是处理分类变量的有用工具,也是对传统编码方法(如一键编码)的升级。
为数据项目使用环境:初学者指南
照片由克里斯里德在 Unsplash 上拍摄
通过一些简单的步骤改善您和您的团队的编码体验
介绍
当我开始用 Python 编程时,对我来说比较困惑的问题之一是如何恰当地管理我安装在计算机上的包。我通常的工作流程是,当我需要一个新的包时,我将它安装在默认的 Python 系统中,而不知道我在做什么。因此,由于包之间的不兼容问题,我的 Python 开始出现错误。此外,我的代码在我同事的机器上无法运行,因为他们没有我使用的相同版本的软件包。几个月后,我发现了虚拟环境这个神秘的概念。当我发现这个工具以及如何使用它们时,它完全改善了我的编码体验。
虚拟环境基本上是一个隔离的设置,您可以在其中指定与依赖项及其版本相关的所有功能,以开发特定的项目。简而言之,就是在你的电脑上安装一个新版本的软件(比如 Python,Rstats,Javascript ),它不会与默认版本或其他环境共享任何东西。在这种情况下,虚拟环境允许您:
- 根据您正在进行的每个项目的需求,拥有几个版本的 Python(或 R)。例如,由于 Python 开发人员在发布新版本时会增加和减少特性,这将有助于避免版本错误和不兼容性。
- 您可以精确地指定每个项目需要哪些包以及哪些版本。当您定义需求时,您的合作者也可以复制您的环境,避免由于不同机器上的不同规范而导致的不兼容性。
为 Python 创建虚拟环境有两种定义明确且有据可查的方法:[virtualenv](https://docs.python.org/3/library/venv.html)
和。一方面,我们有[virtualenv](https://docs.python.org/3/library/venv.html)
,一个允许我们创建和控制我们的环境的环境管理器。安装软件包最简单的方法是通过。对于 Stata 用户来说,这相当于 scc。另一方面,我们有conda
,他既是环境经理也是包经理。
在这篇文章中,我将教你如何用这两种工具创建环境,并利用这一神奇的工具!
目录
- 使用
venv.
创建和管理环境 - 使用
conda
创建环境。 - 我的个人工作流程。
- 结束语
使用venv
创建和管理环境。
# 1\. Update pip package manager:
# Mac/Linux:
$ (base) python -m pip install --user --upgrade pip
# Windows:
$ (base) python -m pip install --upgrade pip # 2\. Install virtualenv.
# Mac/Linux:
$ (base) python -m pip install --user virtualenv
# Windows:
$ (base) python -m pip install --user virtualenv# 3\. Using your terminal, go to the folder of the project where you are working:
$ (base) cd path/to/your/cool/project# 4\. Now, you can create a virtual environment using $ (base) python -m venv your-env-name # 5\. Activate the environment:
# Mac/Linux:
$ (base) source your-env-name/bin/activate
# Windows:
$ (base) your-env-name\Scripts\activate.ps1
恭喜你。!你只是创造了一个环境。如果您使用的是 Anaconda,您的终端可能看起来像这样:
$ (base)(your-env-name)
现在,我们可以开始在虚拟环境中安装软件包了。为了便于说明,我们将使用一个最关键的包来执行科学计算:[NumPy](https://numpy.org)
。
# Check the installed packages
$ (base)(your-env-name) pip freeze# Install the latest version of numpy
$ (base)(your-env-name) pip install numpy# Install a specific version:
$ (base)(your-env-name) pip install numpy==1.17.2 # Install a version greater than or equal to a specific one:
$ (base)(your-env-name) pip install numpy>=1.17.2 # Upgrade a package to a newer version:
$ (base)(your-env-name) pip install --upgrade numpy
使用pip
安装包有很多变化和命令。以下是文档的链接。
为一个项目安装多个包是很常见的。除了numpy
,让我们想象你需要处理数据帧(pandas
)和图形(NetworkX
)。您可以指定一个requirements.txt
文件来管理您需要的所有包。
# Location of this file: path/to/your/cool/project/requirements.txt networkx>=2.4
pandas>=1.1.0
numpy>=1.17.2
使用以下命令安装requirements.txt
中的所有软件包:
$ (base)(your-env-name) pip install -r requirements.txt
最后,要停用或删除环境:
# Deactivate the environment
$ (base)(your-env-name) deactivate# Delete the environment
# Mac/Linux:
$ (base) rm -rf your-env-name
# Windows:
$ (base) Remove-Item -LiteralPath "your-env-name" -Force -Recurse
使用conda
创建环境。
我们已经知道如何使用venv
和pip
来管理环境和包。另一种广泛使用的方式是使用conda
。正如我们前面所讨论的,conda
既是一个环境又是一个包管理系统,所以您可以使用conda
创建环境和安装包。根据你的操作系统,点击这里安装conda
。
# 1\. Check if conda was installed correctly
# This command will show all the installed packages in base ...
$ (base) conda list # ... and this will show all the environments
$ (base) conda env list # 2\. Create a new environment with an specific python version
$ (base) conda create -n your-env-name python=3.8 # You can create the environment with some packages
$ (base) conda create -n your-env-name python=3.8 networkx pandas>=1.1.0 # Activate (deactivate) the environment
$ (base) conda activate your-env-name
$ (my-env) conda deactivate
默认情况下,您的所有环境都位于您的conda
目录中。例如,在我的机器中,环境保存在/Applications/anaconda3/envs/your-env-name
中。这种方法与venv
后面的方法不同,因为后者在项目的同一个文件夹中创建环境。
# Create and env in an specific directory
$ (base) cd path/to/your/project
$ (base) conda create -p ./your-env-name # or alternatively
$ (base) conda create -p path/to/your/project/your-env-name # To activate this environment
$ (base) conda activate -p path/to/your/project/your-env-name
作为一个包管理器,conda
默认安装了来自 Anaconda 仓库的包。你也可以从第三方软件仓库安装软件包( Conda-Forge 是最流行的一个),也可以从pip
安装。这就是它的工作原理!
# IMPORTANT! # Remember to activate the environment before installing packages $ (base) conda activate -n your-env-name # -f path/to/your/project/your-env-name # Install a package from the Anaconda repo
$ (your-env-name) conda install numpy # Install a package from conda forge
$ (your-env-name) conda install -c conda-forge numpy # ... and add the channel to the configuration
$ (your-env-name) conda config --append channels conda-forge
# you can also define which channel to prioritize
$ (your-env-name) conda config --set channel_priority strict # Try to avoid pip if you are using conda!
$ (your-env-name) pip install numpy # Install a requirements.txt file
$ (your-env-name) conda install --file requirements.txt
我发现用conda
管理环境的一个惊人之处是,您可以在单个.yml
文件中指定配置的每个方面。例如,让我们假设您有一个environment.yml
配置:
name: your-env-name
channels:
- conda-forge
- defaults
dependencies:
- python=3.7
- pytest
- ipykernel
- ipython
- pip:
- autopep8==1.5.4
- dropbox==10.4.1
- paramiko==2.7.2
- redis==3.5.3
- twilio==6.41.0
通过该文件,您可以使用以下方式创建环境:
# To create the env
$ (base) conda env create -n your-env-name -f /path/to/environment.yml # To update the env
$ (base) conda env update -n conda-env -f /path/to/environment.yml
同样,如果你想在.yml
中保存一个环境的规格,你可以这样做!
$ (base) conda activate your-env-name
$ (your-env-name) conda env export > your-env-name.yml
更多详细信息,请阅读文档!
我的个人工作流程。
来源:照片由 Kelly Sikkema 在 Unsplash 上拍摄
在我使用的任何环境中,有几个包是我一直想要的。这里是规范:)!
name: null
channels:
- conda-forge
- defaults
dependencies:
- python>=3.7 # Specify your desire version
- ipython # To develop and run my code
- pytest # To perform testing to the code
- autopep8 # Code formatter for the PEP8 guidelines
- mypy # Static typing
- flake8 # For linting (spell and grammar checker of python code) prefix: path/to/env/conda-env
使用venv
,该工作流程如下:
$ (base) cd path/to/your/project
$ (base) python -m venv venv
$ (base) source venv/bin/activate
$ (venv) pip install ipython pytest autopep8 mypy flake8 --upgrade pip
$ (venv) pip install -r requirements.txt # This is where I specify all the packages I'm gonna use!
结束语
使用虚拟环境将有助于您避免许多因不兼容问题导致的未知错误给自己和同事带来的麻烦。如果你喜欢这个帖子或者你有一些意见,请在 Twitter 上联系我!开始为您的项目使用环境吧!
参考资料和进一步阅读
最初发布于https://Ignacio riveros1 . github . io。
使用 fastai 回调进行高效的模型训练
图像快门库
利用提前停止和模型保存回调的力量
当你训练一个深度学习模型时,你想从你用来训练模型的资源中获得最大的收益。如果你使用像 Paperspace Gradient 这样的按小时付费的环境,时间就是金钱。如果你能在更短的时间内训练出你的模型,你就能省钱。即使你正在使用 Colab,并且计价器没有运行,你自己的时间仍然是宝贵的,所以知道如何最大限度地利用你可用的时间和能力来训练你的深度学习模型是值得的。在本文中,我将描述两个回调,你可以在 fastai 中使用它们来确保你的模型训练尽可能高效。我在本文中描述的例子在我的 Packt bookDeep Learning with fastai Cookbook中有更详细的解释。
训练深度学习模型的两个问题
fastai 与 Keras 有一个共同的特点,Keras 是另一个常用的深度学习高级框架。在这两个框架中,模型训练过程不是开箱即用的。默认情况下,模型训练过程存在以下问题:
- 训练过程将按照您在 fit 语句中指定的次数继续进行,即使您想要优化的指标不再改进。
- 您在训练过程结束时获得的模型具有来自最后一个时期的权重,即使存在其权重会导致模型具有更好性能的更早时期。
幸运的是,fastai 和 Keras 都以回调的形式包含了这两个问题的解决方案。在本文的剩余部分,我将解释 fastai 中的这些回调。关于如何使用回调来控制 Keras 中的模型训练过程的类似描述,请参见我的曼宁著作的第 6 章“结构化数据深度学习”。
基线:在没有回调的情况下训练模型
为了查看 fastai 回调的影响,我们将从训练一个没有回调的模型开始。该模型在 fastai 策划的数据集 ADULT_SAMPLE 上进行训练,该数据集包含个人的详细信息,如受教育年限、婚姻状况和职业类别。
成人 _ 样本数据集
在成人样本数据集上训练的模型的目标是预测给定个人的工资是高于还是低于 50 k。
我们首先训练没有回调的模型:
请注意对 set_seed()的调用。我们这样做是为了在不同的训练运行之间的每个时期获得一致的结果。这使我们能够在训练运行之间进行比较,并强调回调的影响。如果我们不调用 set_seed(),我们将在运行之间得到不一致的结果。例如,历元 2 上的精度对于每次训练运行将是不同的。
以下是应用于不带回调的学习者对象的 fit 语句的输出:
没有回调的基线的 fit 语句的输出
精确度增加到时段 2,然后在时段 3 下降,并在剩余时段振荡直到时段 9。当我们对学习者对象运行 validate()时:
learn.validate()
输出显示,在训练运行结束时,训练模型的精度是历元 9:
不带回调的模型的 validate()输出
由于没有回调,我们遇到了本文开头提到的两个问题:在模型停止改进之后,训练运行继续进行,并且从训练运行中出来的经过训练的模型的性能比在训练运行期间看到的最佳性能差。
添加提前停止回调,以便在模型性能停止提高时停止训练运行
现在我们已经建立了一个没有回调的基线,让我们添加一个早期停止回调,以便在模型性能停止提高时停止训练运行。
我们将使用用于训练基线模型的相同的 dataloaders 对象,但是这一次我们将在 fit 语句中指定一个早期停止回调:
fit 语句的输出现在显示,尽管指定了 10 个时期,但是训练运行只进行到时期 5。耐心参数被设置为 3,因此在每个高精度标记之后,训练过程得到 3 个时期的改进。如果在高水位标记后的 3 个时期内没有改善,训练过程将自动停止。
带有提前停止回调的 fit 语句的输出
在时段 2 中精度的高水位标记之后,精度没有变得更好,因此训练运行在 3 个时段之后,在时段 5 之后停止。
当我们在学习者对象上运行 validate()时,输出显示训练过程产生的模型的精度再次是训练运行的最终时期的精度,即使更早的时期具有更高的精度:
通过提前停止回调训练的模型的 validate()输出
保存训练过程中的最佳砝码组
添加早期停止回调是对基线的改进,因为在准确性不再提高后,我们没有运行那么多无效的时期。然而,在最终的训练模型中,我们仍然没有达到最佳精度,因此仍然有改进的空间。为了获得最佳的准确性,我们将添加一个模型保存回调,以确保经过训练的模型在训练过程中具有最佳的准确性。
我们将再次使用用于训练基线模型的相同的 dataloaders 对象,但是这次我们将在 fit 语句中指定两个回调。我们还会将学习者对象的路径设置为可写目录,以便在培训过程中保存模型:
就像上一节中的模型一样,fit 语句的输出显示,即使指定了 10 个时期,训练运行也只进行到时期 5。
带有提前停止回调和模型保存回调的 fit 语句的输出
再次,由于提前停止回调,在精度在时段 2 中的高水位标记上停止提高之后,训练过程自动停止 3 个时段。
训练结束时,训练好的模型的精度如何?对该模型的学习者对象运行 validate()表明,精度不是来自训练运行的最终时期的精度。这一次,精确度与我们在纪元 2 的高水位标记中看到的一致。
通过提前停止回调和模型保存回调训练的模型的 validate()输出
通过回调、提前停止和模型保存,我们解决了本文开头提到的两个问题:
- 多亏了早期停止回调,我们避免了做一堆无效的时期,因为精度不再提高。
- 由于模型保存回调,从训练过程中得到的训练模型具有我们在训练过程中看到的最佳准确性。
结论
通过使用 fastai 中的提前停止和模型保存回调,您可以从培训周期中获得最大收益。您将避免在模型没有改进的时期耗尽容量,并且您可以确保在训练周期结束时,您拥有具有最佳性能的模型。
以下是与本文相关的一些资源:
- 本文检查的代码:https://github . com/packt publishing/Deep-Learning-with-fastai-Cookbook/blob/main/ch8/training _ with _ tabular _ datasets _ callbacks . ipynb
- 预定地点:https://www . packtpub . com/product/deep-learning-with-fastai-cookbook/9781800208100
- 关于这个主题的视频:【https://youtu.be/qkRok0e3yvs
使用 FastAPI 重新创建烧瓶教程
实践教程
不仅仅是另一篇“Flask 与 FastAPI”文章——学习 Python web 框架的有用练习
Flask 和 FastAPI 是两种流行的 Python web 框架。很容易在网上找到这两个框架的大量比较——如果你正在阅读这篇文章,你可能已经读过你的“Flask vs. FastAPI”文章。
这里有一个非常的快速总结。Flask 出现的时间更长,并且面向小型网络应用。作为较老的框架,它往往有更大的用户群和更多的教程和回答的问题。FastAPI 较新,适合创建 REST APIs。它越来越受欢迎,尤其是对于机器学习用例。但是一般来说,这两个框架非常相似——您可以用 Flask 做的任何事情都可以用 FastAPI 来完成,反之亦然。
Flask 拥有的一个东西是一个伟大的初学者教程,用于构建一个简单的应用程序,用户可以在其中注册、登录和创建帖子。FastAPI 有很好的构建 API 的文档,但是缺少像 Flask 示例这样的基础应用的简单教程。我正在学习这两个框架,所以我决定使用 FastAPI 重新创建 Flask tutorial 应用程序是一个很好的练习。我想其他初学者可能会从我学到的东西中受益。
如何充分利用这篇文章
- 查看 GitHub 上的 **源代码。**我不是在这里写一个完整的教程。我将指出 Flask 和 FastAPI 应用程序之间的一些关键差异,但您必须去 GitHub 查看完整的源代码以及所有内容是如何组合在一起的。
- 阅读 烧瓶教程 。它解释了我们正在构建的应用程序,此外,教程中还有许多与框架无关的有用信息。如果你已经熟悉 web 框架,你可以浏览一下教程,但是如果你是新手,完成它可能是有益的(最多只需要几个小时)。
- **自己动手!**从头开始编写应用程序,从 Flask 示例开始并将其转换为 FastAPI,或者克隆我的 GitHub 库对其进行修补。最好的学习方法是实践!
教程应用程序
在 Flask 教程中,我们构建了一个名为 Flaskr 的应用程序。我使用 FastAPI 构建了一个等效的应用程序,并将其命名为 Fastr。这两款应用都允许用户使用用户名和密码注册,登录,并以博客格式发表文章。这些应用程序运行在存储用户和帖子数据的 SQLite 数据库之上。
使用 Flask(左)和 FastAPI(右)创建的同一个 web 应用程序。
Flask 和 FastAPI 应用程序之间的区别
除了字体和颜色的表面变化,这些应用程序在幕后的实现方式也有一些关键的不同。我将在这里重点介绍其中一些,大致按照您在 Flask 教程中遇到它们的顺序。在这篇文章中我不会讲太多技术,所以请务必参考 GitHub 上的源代码以了解更多细节。
这不是一个完整的列表,但这里有一些 Flask tutorial 应用程序和我在 FastAPI 中的实现之间的关键差异:
- **无应用工厂。**Flask 教程推荐在
__init__.py
文件中实现一个应用工厂。我通常不喜欢在__init__.py
文件中放太多代码,所以我在main.py
文件中创建了 FastAPI app 对象,这是 FastAPI 文档中的典型模式。 - **用紫玉米跑步。**在 Flask 中,我们设置一些环境变量,然后调用
flask run
。FastAPI 推荐使用uvicon,所以我们用uvicorn fastr.main:app
运行我们的应用。 - 同一事物的不同名称。代码的某些部分几乎完全相同,只是命名规则略有不同。例如,Flask 中的 Blueprint 相当于 FastAPI 中的 APIRouter,用法几乎相同。
- 填写缺失的部分。 Flask 内置了许多功能,其中一些功能是一个基本的 FastAPI 应用程序所没有的。我们需要手动挂载静态文件并指定我们的模板引擎(这两个都是在 Flask 中自动完成的)。我们也没有像在 Flask 中那样内置“会话”对象——我们需要使用 SessionMiddleware 对象来实现该功能。一旦我们添加了这些部分,它们在框架之间的行为几乎是相同的。
- Pydantic 模型和类型检查。 FastAPI 与 Python 类型提示和 Pydantic 数据模型紧密集成,允许大量自动数据验证。例如,在我们的应用程序中,我们不必像在 Flask 中那样显式验证“用户名”字段是否已填充。
- **更强大的数据库实施。**这并不是严格意义上的必要更改,但是我使用 SQLAlchemy 实现了后端数据库,而不是直接使用 sqlite3。这是在 FastAPI 文档中推荐的模式,它有一些好处,比如允许我们以后容易地改变到不同的数据库后端(例如 PostgreSQL)。这种变化需要一些代码重组 Fastr 应用程序在
db/
目录下有所有与数据库相关的代码。 - 重写测试。这是比较棘手的部分之一,因为 Flaskr 单元测试的设置是非常特定于 Flask 框架的。首先,我们需要在
conftest.py
文件中更改设置测试数据库和应用程序的夹具。之后,我们对 Flask 和 FastAPI 应用程序进行了大量相同的测试,但是在一些测试中,语法和预期的响应是不同的。许多变化都是在 FastAPI 应用程序中使用 SQLAlchemy 的结果。如果您运行 Fastr 应用程序的单元测试,您会发现它们通过了全面覆盖。
结论
如果你比较一下 Flask 教程应用和 my FastAPI 实现的源代码,你会发现它们的相似之处多于不同之处。FastAPI 应用程序中有更多的样板代码来复制 Flask 内置的一些功能。但是你也获得了 FastAPI 的一些好处,比如来自类型提示的数据验证和自动生成的交互式文档。
对于这类 app,我会推荐 Flask 或者 FastAPI 吗?我不知道。对于如此简单的事情,这并不重要。一旦我们开始添加更多的功能,我们可能会从一个框架中受益更多。例如,如果我们想增加对异步请求的支持,FastAPI 本身就包含了这种能力。或者,如果我们有一个具体而复杂的特性想要快速实现,我们可能会更幸运地在大量的 Flask 社区示例中找到一个起点。最终,任何一个人都可以完成工作,并且很容易将你的知识从一个人转移到另一个人。
成为媒体会员访问成千上万作家的故事!
在 Python 中使用 For 循环:计算概率
为什么循环对于构建完整的统计模型是不可或缺的
循环是学习如何用 Python 编码的一个非常重要的部分,尤其是在实现大量数字的计算时。
对于统计学家和数据科学家来说,最常见的诱惑是跳过编码中更平凡的方面——我们假设软件工程师可以简单地以适当的方式重新格式化代码。
然而,在许多情况下,编写代码的人需要理解模型背后的统计数据以及如何通过循环迭代模型输出——这两个过程根本不能独立开发。
这里有一个例子,说明在 Python 中对循环使用可以极大地增强统计分析。
累积二项式概率的背景
在进行概率分析时,考虑事件发生几率的两个变量是 N (观察次数)和λ(λ——我们的命中率/在单个间隔内发生的几率)。当我们谈论累积二项式概率分布时,我们的意思是说试验次数越多,事件发生的总体概率越高。
probability = 1 — ((1 — λ)^N)
例如,在公平骰子上掷出数字 6 的几率是 1/6。然而,假设同一骰子滚动 10 次:
1 — ((1–0.1667)^10) = 0.8385
我们看到掷出数字 6 的概率现在增加到 83.85%。
基于大数定律,试验次数越大;事件发生的概率越大,即使单次试验中的概率非常低。因此,让我们生成一个累积二项式概率,来演示在试验次数增加的情况下概率是如何增加的。
无循环模型
这是一个不使用循环计算累积二项式概率的脚本。
import numpy as np
import pandas as pdl = 0.02
m = 0.04
n = 0.06p=np.arange(0, 100, 1)h = 1 - l
j = 1 - m
k = 1 - nq = 1-(h**p)
r = 1-(j**p)
s = 1-(k**p)
- l 、 m 、 n 代表三个个体概率。
- p 代表试验次数(最多 100 次)
- q 、 r 和 s 代表累积二项式概率,即试验次数每增加一个单位,概率增加
以下是生成的输出示例:
>>> qarray([0., 0.02, 0.0396, 0.058808, 0.07763184, 0.0960792, 0.11415762, 0.13187447, 0.14923698, 0.16625224, ..., 0.8532841, 0.85621842, 0.85909405, 0.86191217, 0.86467392])>>> rarray([0., 0.04, 0.0784, 0.115264, 0.15065344, 0.1846273, 0.21724221, 0.24855252, 0.27861042, 0.307466, ..., 0.97930968, 0.9801373, 0.9809318, 0.98169453, 0.98242675])>>> sarray([0., 0.06, 0.1164, 0.169416, 0.21925104, 0.26609598, 0.31013022, 0.35152241, 0.39043106, 0.4270052, 0.46138489, 0.49370179, 0.52407969, 0.5526349, 0.57947681, ..., 0.99720008, 0.99736807, 0.99752599, 0.99767443, 0.99781396])
我们看到,对于概率 q 、 r 和 s ,对于给定数量的试验,累积概率以不同的速率增加。
也就是说,在不使用循环的情况下开发这个模型有一个关键的缺点,即个体概率只能采用最终用户指定的值。如果我们希望从 0.01 连续迭代到 0.99 呢?
循环模型:列表理解和 2D 数组
这一次,将通过使用一个单独的概率变量来构建模型,该变量迭代值 0.01 到 0.99 ,并且将使用 100 次试验来计算累积二项式概率。
import numpy as np
import pandas as pd # List comprehension
probability=[x*0.01 for x in range(1,100)]
probability=np.array(probability)
probabilityh = 1 - probability
h# Construct 2D array
result = 1-h[:, np.newaxis] ** np.arange(1,100)
result
生成的概率变量的输出如下:
>>> probabilityarray([0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, ... 0.96, 0.97, 0.98, 0.99])
注意,对于概率变量,有必要使用**列表理解。**这是因为 Python 的 range() 函数只能处理整数,不能处理浮点值。更多信息见下面的堆栈溢出指南。
当查看代码的最后两行时,您会注意到构建了一个 2D 数组来计算累积二项式概率。当最初尝试计算这些值而不是使用 2D 数组时,数组已被计算出来,但值并没有按预期的顺序排列。
>>> for i in range(1,100,1):
>>> print(1-(h**i))[0.01 0.02]
[0.0199 0.0396]
...
[0.62653572 0.86191217]
[0.63027036 0.86467392]
相反,我们希望数组的顺序为 [0.01,0.0199,…,0.62653572,0.63027036] 和 [0.02,0.0396,…,0.86191217,0.86467392] 。
正如下面的 Reddit 线程中所解释的,转置上面的不会有任何用处,因为 h 是一维数组。
另一种方法是计算 2D 数组,然后直接打印出来:
>>> result = 1-h[:, np.newaxis] ** np.arange(1,100)
>>> resultarray([[0.01, 0.0199, 0.029701, ..., 0.62653572, 0.63027036], [0.02, 0.0396, 0.058808, ..., 0.86191217, 0.86467392], [0.03, 0.0591, 0.087327, ..., 0.94946061, 0.9509768], ..., [0.97, 0.9991, 0.999973, ..., 1., 1., 1.], [0.98, 0.9996, 0.999992, ..., 1., 1., 1.], [0.99, 0.9999, 0.999999, ..., 1., 1.,1.]])
从上面可以看出,计算了从 0.01 到 0.99 的累积二项式概率。
以这种方式对循环使用允许我们自动从 0.01 迭代到 0.99——试图手动这样做太麻烦且容易出错。
结论
在本例中,您看到了如何:
- 用 Python 计算累积二项式概率
- 使用 for 循环遍历大范围的值
- 使用列表理解处理一系列浮点值
- 当无法转置 1D 数组中包含的值时,设计 2D 数组
非常感谢您的宝贵时间,非常感谢您的任何问题或反馈。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。
在 Python 中使用函数装饰器
了解如何通过使用函数装饰器来扩展函数的功能
在这篇文章中,我将谈谈 Python 中的 函数装饰器 ,一个不容易把握的话题,却是 Python 编程中极其有用的设计模式。
在 Python 中,函数装饰器实际上是函数包装器。
函数装饰器通过包装函数来扩展函数的功能,而不修改其最初的预期行为。
像往常一样,让我们从基础开始,慢慢地理解什么是函数装饰器。
在函数中定义函数
在 Python 中,函数体可以包含一个或多个函数:
def do_something():
def internal_function():
return "In internal_function() function"
return internal_function
在上面的代码片段中,在do_something()
中我有另一个名为internal_function
的函数。当do_something()
被调用时,它会将internal_function
返回给调用者。让我们验证一下:
f = do_something()
type(f) # *function*
在上面的语句中,当internal_function
返回给调用者时,我将它赋给了一个名为f
的变量。因为可以将函数赋给变量,所以现在可以使用变量调用函数,如下所示:
f() # 'In internal_function() function'
将函数传递给函数(也称为包装函数)
让我们修改do_something()
,使它现在有一个名为f
的参数。该参数现在将接受一个可以在internal_function()
中调用的函数参数:
def do_something(**f**):
def internal_function(): **return f()** return internal_function
假设现在我有了另一个名为function1
的函数:
def function1():
return 'function1'
我现在可以将这个函数传递给do_something()
:
f = do_something(**function1**)
f
现在将包含对internal_function()
的引用,该引用返回执行function1
的结果(传递到do_something()
)。作为函数调用f
和直接调用function1()
是一样的:
f() # 'function1'
# same as:
function1() # 'function1'
本质上,我们在这里做的是用另一个函数包装
*function1*
,在本例中是*do_something*
。
函数装饰器
在 Python 中,do_something()
被称为函数装饰器。而不是将function1
传入do_something()
(就像我们在上一节所做的那样):
f = do_something(**function1**) # think of this as wrapping function1
# with do_something
Python 允许您给函数装饰器加上前缀“ @”符号,并将其放在您想要换行的函数之前,如下所示:
**@do_something**
def function1():
return 'function1'
他被称为函数装饰者。
您现在可以正常调用function1()
:
function1() # 'function1'
带参数的函数修饰符
在前面的例子中,您的函数装饰器(do_something()
)包装了一个不接受任何参数的函数(function1()
不接受任何参数)。如果您现在想将它包装在一个接受参数的函数中,比如下面的函数,该怎么办呢?
def add(n,m):
return n + m
在这种情况下,需要给internal_function()
添加参数:
def do_something(f):
def internal_function(**n,m**):
return f(**n,m**) return internal_function
您现在可以像这样使用函数装饰器:
@do_something
def add(n,m):
return n+madd(**4,5**) # 9
如果你不使用@符号,你的代码将如下所示:
f = do_something(add)
f(4,5) # 9
这个函数装饰器现在可以应用于任何接受两个参数的函数。但是,如果您想将它应用于接受不同数量参数的函数,该怎么办呢?在这种情况下,您可以使用 *args 和 **kwargs 类型定义一个通用函数装饰器。
如果你不熟悉它们的工作方式,请参考我的文章“理解 Python 中的args 和 * kwargs”(https://towards data science . com/Understanding-args-and-kwargs-in-Python-321937 f 49 C5 b)。
我们修改后的do_something()
函数装饰器现在看起来像这样:
def do_something(f):
def internal_function(***args, **kwargs**):
return f(***args, **kwargs**) return internal_function
您现在可以将do_something()
函数装饰器应用于具有不同参数的函数:
@do_something
def add(n,m):
return n+m**@do_something
def square(n):
return n**2**print(add(4,5)) # 9
**print(square(2)) # 4**
函数装饰符的使用
我知道你脑子里的下一个问题是:那么函数装饰器的用例是什么?上面的函数 decorators 似乎没有做任何有用的事情。
我给你举个例子。假设您想创建一个日志文件,在每次调用函数时记录一个条目,详细信息如下:
- 被调用函数的名称
- 函数接收的参数
- 调用了时间函数
如果没有函数装饰器,您需要将代码插入到您希望记录的所有函数中。但是这样做,你实际上是在修改函数的功能。您希望在不修改函数的情况下做到这一点。
你可以用一个函数装饰器轻松解决这个问题。
要创建一个函数装饰器,首先要创建一个函数,比如说,function_logger()
,其中有一个内部函数叫做wrapper()
(你可以使用任何你想要的名字):
from datetime import datetimedef function_logger(f):
def wrapper(*args, **kwargs):
# store the current date and time, and function details
# into a log file:
date_time = datetime.now().strftime("%y-%m-%d %H:%M:%S")
with open("log.txt", "a") as logfile:
logfile.write(f'{f.__name__}: {args}, {date_time}\n')
return f(*args, **kwargs) return wrapper
现在,您可以对任何函数应用函数装饰器:
**@function_logger**
def add(n,m):
return n+m**@function_logger**
def square(n):
return n**2print(add(4,5))
print(square(2))
调用这些函数将导致在名为 log.txt 的日志文件中添加一个条目:
add: (4, 5), 21-05-29 14:16:56
square: (2,), 21-05-29 14:16:56
如果您不再需要记录函数,只需删除函数装饰器!
带参数的函数修饰符
上一节中的例子显示了function_logger
函数装饰器在每次调用被记录的函数时为日志文件创建一个条目。如果您希望根据函数的类型将条目记录到不同的文件中,该怎么办?嗯,很简单——你只需用另一个函数包装function_logger()
,把文件名作为参数传入,然后返回function_logger
函数,就像这样:
from datetime import datetime**def logger(filename):**
def function_logger(f):
def wrapper(*args, **kwargs):
date_time = datetime.now().strftime("%y-%m-%d %H:%M:%S")
with open(**filename**, "a") as logfile:
logfile.write(
f'{f.__name__}: {args}, {date_time}\n')
return f(*args, **kwargs)
return wrapper
**return function_logger**
现在,您可以指定要使用的日志文件:
**@logger('log1.txt')**
def add(n,m):
return n+m**@logger('log2.txt')**
def square(n):
return n**2print(add(4,5))
print(square(2))
如果不使用@符号,您的代码将如下所示:
f = logger(‘log1.txt’)(add)
f(4,5)
简单?我希望这篇文章能揭开 Python 中函数装饰器背后的奥秘。下次您遇到函数装饰器时,您不必再想了——只要把它们想象成函数包装器就行了。
基于模糊逻辑的道路交通拥挤指数估计
思想和理论
如何利用大量 GPS 数据为基于模糊的道路交通拥堵量化系统提供信息
在本文中,将对“基于速度概率分布的拥挤指数估计的模糊推理系统”一文进行概述。目标是以一种更“可读”的方式呈现论文的发现,并鼓励读者学习新的东西或在他们的研究中使用这些知识。
在讨论大城市面临的挑战时,拥堵是主要话题。因此,本文的主要目标是提出另一种道路交通拥堵量化方法,但这一次使用了一种称为速度转换矩阵(STM) [ 2 ]的新型道路交通建模技术。拥塞量化方法基于四个步骤:
- STM 计算。
- 质心(CoM)坐标的计算代表了从 STM 提取的交通状态。
- 使用 CoM 坐标作为模糊推理系统的输入导致范围[0,1]内的拥塞指数。
- 通过从《公路通行能力手册》(HCM) [ 3 ]中提取领域知识,使用遗传算法优化 FIS。
让我们从一些使用方法的背景开始。
速度转换矩阵
STMs 的概念最近才出现[ 2 ],它基于马尔可夫链理论,表示被观察系统的两个或多个状态之间的转移概率。STM 表示矩阵,该矩阵示出了在某个时间间隔内在两个连续路段之间过渡时改变车辆速度的概率。关于 STMs 的详细信息可以在以下文章中找到:
质心
使用由 STM 表示的交通模式的 CoM 坐标来估计拥塞指数。使用这种方法是因为物体在 STM 中的位置提供了道路交通分析环境中的基本信息。它揭示了受监控的十字路口或路段的交通状态。下图显示了使用 STMs 时 CoM 计算的重要性。如果将 CoM 放在左上角,我们可以观察到源链路和目的链路的速度都很低,表现为拥塞。如果将 CoM 放在矩阵的中间,可以观察到一些不稳定的操作。而如果将 CoM 放在右下角,我们可以观察到由于起点和目的地链路的速度非常大而导致的自由流动情况。使用这些陈述,我们可以同意当使用 STM 作为交通数据模型时,CoM 是交通状态估计的关键参数。
三个 STM 例子的质心:拥塞(左),不稳定流(中),自由流(右)(图片作者:作者)
模糊推理系统
基于知识的决策有时很难用离散值来完成,而一些非离散值可以更好地代表问题。例如,如果你需要根据屋外的温度来决定如何着装,你可能会使用一些非离散值,如“热”、“相对热”、“不太冷”或“冷”这些变量被称为语言变量,因为它们不代表离散值,而这些值是用单词量化的。
关于用 Python 实现模糊推理系统的有趣教程可以在[ 4 中找到。
在这里,模糊推理系统(FIS)派上了用场!FIS 可以用语言术语表示变量,用隶属函数表示具有特定术语的隶属度。让我们使用下面的图像来显示上述温度示例:
用语言值表示的简单模糊变量(图片来源:作者)
请注意本例中的第 1 点和第 2 点。在第一点,很明显温度是“热”的最强成员,而在第二点,它介于“不太冷”和“冷”之间。为了创建决策系统,我们需要基于一些先验知识定义规则来帮助我们,在这种情况下,决定穿什么。在基于 FIS 的系统中,我们使用简单的“如果-那么”规则,如下图所示。
基于模糊的决策规则集(图片来源:作者)
此示例显示了最简单的 FIS,只有一个输入变量(温度)和一个输出变量(衣服类型)。
对于拥塞量化 FIS,我们使用了两个输入变量(CoM 的 x 坐标和 CoM 的 y 坐标)和一个输出变量(拥塞指数),如下图所示。
基于经验的初始 FIS 变量(图片来源:作者)
输入和输出变量用语言表达式“小”、“中”和“大”来描述输入变量被限制使用值[0,20],因为 STM 维度是 20x20。输出变量表示拥挤指数,由于结果的解释更容易,拥挤指数被限制在区间[0,1]内。
该 FIS 的 If-then 规则按以下方式设置:
- x 和 y 坐标的“小”值代表“大”拥堵指数。
- x 和 y 坐标的“中等”值表示“中等”拥堵指数。
- x 和 y 坐标的“大”值代表“小”拥堵指数。
总共有九条规则涵盖了上述基本价值观以及它们的其他组合。详细内容可以在论文[ 1 ]中找到。
在这里,最初的 FIS 是代表。它是仅利用先前研究者的经验构建的。为了验证初始 FIS,必须使用一些领域知识进行调优。
用遗传算法优化 FIS
在这种情况下,优化涉及调整初始 FIS 隶属函数、规则或两者,以便更好地表示领域知识。
如果你使用的是 Matlab,关于 FIS 优化的细节可以在这里找到[ 5 ]。
专家系统的关键部分之一是领域知识表示。本文参考了 HCM 的领域知识表示方法。数据基于 HCM 服务水平值进行标注,该值使用相对于自由流速度(FFS)的速度值进行定义。FFS 可以定义为单车在空路段的速度,也可以定义为观察路段的速度限制。HCM 定义了从 A 到 F 的六个服务水平,其中 A 代表 FFS 的交通流量,车辆之间几乎没有相互作用,F 代表严重拥堵情况。创建 LoS 矩阵是为了直观地表示转换矩阵上的 LoS 值,如下图所示。
从 HCM 中提取的用于标注测试数据集的矩阵(图片来源:作者)
它用颜色表示 LoS,两个 LoS 值之间的过渡用相应的颜色混合表示。为了量化拥堵级别,以如下方式将 LoS 值合并为三类:(I)由标记为 A 和 B 的 LoS 表示的自由流动交通状况被标记为“低”,(ii)由标记为 C 和 D 的 LOS 表示的交通状况被标记为“中”,以及(iii)由 LOS 值 E 和 F 表示的拥堵交通状况被标记为“高”。然后,通过用提到的类别标签标记数据来创建两个数据集。
第一个是用于基于遗传算法的 FIS 优化的训练数据集,第二个是用于结果验证的测试数据集。适应度函数和关于优化过程的细节可以在原始论文[ 1 ]中找到。
优化结果可以在下图中观察到。优化后,它显示了初始 FIS 变量(纯黑色)和 FIS 变量(红色)。可以观察到,除了输出变量之外,初始 FIS 非常适合优化的那个。我们可以看到“大”这个词偏离了最初的 FIS,它应该改为一个阶跃函数。
基于领域知识的优化 FIS 变量(图片来源:作者)
结果
优化 FIS 的结果如下图所示。它代表输入变量和输出变量之间的关系。可以看出,当 CoM 的 x 和 y 坐标较小时,拥塞指数较高。在这种情况下,车辆在输入和输出观察到的道路交通路段上具有非常低的速度。当 CoM 的 x 和 y 坐标很大时,可以观察到相反的情况。在这里,我们有一个小的拥堵指数和接近自由流速度的速度。
优化 FIS 的结果;CoM 坐标与拥堵指数关系图(图片来源:作者)
问号代表 STM 的区域,可以用从非常低到非常高的速度和相反的速度的偏离跃迁来描述。这些区域可以代表异常区域,并且可能是瓶颈检测的方法。这些主题将在我们未来的研究中涉及。
结论
本文提出了一种利用大量 GPS 数据和模糊推理系统进行拥堵量化的方法。这里,速度转换矩阵的新概念被用作交通数据建模方法。
目的是以一种更具“可读性”的方式展示论文“基于速度概率分布的拥堵指数估算模糊推理系统”[1”的研究结果,并鼓励读者学习新知识或在自己的研究中使用这些知识。
如果你对类似的、与交通相关的话题感兴趣,请关注我的媒体简介,或者在研究门户:)查看发表的研究
如有任何问题或建议,欢迎评论或联系我!https://www.linkedin.com/in/leo-tisljaric-28a56b123/
参考
[1]l . tiljari,E. Ivanjko,Z. Kavran,T. Cari,基于速度概率分布的拥堵指数估计模糊推理系统,
交通研究程序,55,2021:1389–1397
[2]l . tiljari,T. Cari,B. Abramovi 和 T. Fratrovi,使用速度转换矩阵对全市范围内的交通状态进行评估和分类(2020) ,可持续性,12,18:7278–7294
[3] HCM 2016:公路通行能力手册。华盛顿特区:交通研究委员会,2016 年。
[4]模糊推理系统在 Python 中的实现,https://towards data science . com/Fuzzy-Inference-System-implementation-in-Python-8 af 88d 1 f 0 a 6 e
[5]调整模糊推理系统,https://WW2 . mathworks . cn/help/Fuzzy/tune-Fuzzy-Inference-Systems . html
使用 fuzzyjoin 帮助您分析讨厌的在线调查数据
… 名字不符的时候特别有用!
几天前,我被要求帮助某人整理一项在线调查的结果。问题是,在线调查分两部分进行,每一部分都有自己的谷歌表格。请求是执行一个简单的线性回归模型,其中一个 Google 表单记录了响应变量,另一个 Google 表单记录了解释变量。然而,每个调查参与者都没有唯一的标识符,他们被告知输入的唯一信息是他们的名字。这会很有趣的!下面是我如何在 r 中使用fuzzyjoin
来做这件事(及其背后的思考过程)的记录。
数据
为了让你更好地了解这些数据有多糟糕,这里有一个在两种表格中记录姓名的快照(姓名已被更改,因为这是个人数据,但这实际上是看起来的样子)。第一组数据记录了他们的总体幸福感…
幸福数据
第二组数据记录了他们在工作和与孩子相处时面临的困难。第二组数据是在谷歌表单上完成的,但名字是用下拉列表。这就迫使这些名字按照他们注册的名字有固定的格式。
难度数据
所有 4 个名字都出现在两组数据中,但是正如你所看到的,在名字的输入上有相当多的差异。例如,在新加坡,许多老一辈的人只登记了中文名字(例如“郭丁丽”),却采用了英文名字(例如“Mary Kwek”),而没有在政府登记。事实证明这的确是个问题。
输入模糊连接
和我一起工作的人熟悉 R 语言,因此要求我用 R 语言做这个项目。看着各种可用的库,我最终决定用fuzzyjoin
作为最好的前进方式。
导入文件后,我使用在fuzzyjoin
库中找到的stringdist_join
函数来执行两个文件的匹配和组合。在继续解释每一步的用途之前,我将把我使用的整个代码块放进去。
前两个参数,by
和mode
类似于通常的dplyr
连接,所以我不会真的触及它们。
第一个重要论点是method
。在这种情况下,method 允许您选择想要使用的度量,以便量化单词字符串之间的距离。点击,您可以找到完整的指标列表。我并不真正熟悉所有的指标,但我知道有几个(例如 Hamming )肯定不合适,因为它们要求文本字符串具有相同的字符数。相反,为了找出哪种方法给了我最好的结果,我创建了数据集的一个子集,并尝试了几种方法,直到我找到一种我认为(通过目测结果)看起来最好的方法。如你所见,我最终选定了 Jaro 距离。经过反复试验,它给了我最好的结果。
下一个参数是ignore_case
,我显然将它设置为“TRUE ”,因为有些名字是大写的,而有些不是。
max_dist
然后设置最大允许距离。在这种情况下,因为我使用“JW”返回 0 和 1 之间的距离,所以我选择了 0.5 之间的某个值。同样,这将涉及一点点的尝试和错误,以找到一个最佳的距离。max_dist
越大,返回的匹配数越多,返回的数据帧也越大。在我的子集中只有 25 个名字,将其设置为 1.0 会返回 1078 行,而设置为 0.5 会得到 564 行。
distance_col
允许你创建一个存储匹配之间距离的列——这是一个非常有用的列,可以在以后过滤匹配。
有了 564 行之后,我按照 Name.x(这是第一个数据集中的姓名列,带有“官方姓名”)将它们分组,并使用slice_min
来只保留每个姓名之间距离最小的那一行。
然后我把所有的名字按降序排列,看距离最高的名字。我被告知,并不是每个调查参与者都完成了两个调查,因此一些名字将不得不被删除,因为事实上根本没有匹配。快速浏览这些名字,我看到第一次“真正的比赛”是在距离降到 0.27 以下后才开始的,因此增加了一个filter
。
然后……完成了!我从两个完全不同的数据集中提取了两组相当混乱的名字,并用fuzzyjoin.
将它们连接在一起。感谢阅读,我希望当你需要分析任何名字混乱、不匹配的调查数据时,这篇技巧对你有所帮助!
最初发表于 http://zachlim98.github.io/me
使用高斯混合模型来转换用户项目嵌入并生成更好的用户聚类
通过使用高斯混合模型生成新的和更紧密的用户特征来改进用户项目嵌入的聚类
马库斯·斯皮斯克在 Unsplash 上的照片
一.导言
一家销售音乐唱片的大公司聘请你帮助他们更好地了解客户和他们的偏好,以便他们可以个性化他们的服务。该公司主要感兴趣的是了解听众中存在的群体类型、他们的聆听模式和偏好。为了这个任务,他们给了你他们最珍贵的工件——他们完整的用户-艺术家交互数据集。
用户-项目交互数据是表示用户和项目之间不同参与形式的常见方式,如用户观看的电影、用户点击的广告或用户购买的购物项目。它被认为是一种有点原始但有用的输入形式,用于根据用户参与的项目找到他们之间的相似之处,或者预测他们将来会参与的项目。
由 9 个用户和 4 个项目组成的原始交互数据。表示相同数据的另一种常见方式是使用用户-项目对列表。
因为原始交互数据通常非常稀疏和嘈杂,所以通常的做法是首先将其嵌入到较低维度的空间中,以便仅保留关于每个项目或用户的重要或相关信息。然后,数据的低维表示可以用于各种下游任务,例如根据用户选择的项目之间的相似性对用户进行分类或聚类。
在低维空间中嵌入用户-项目交互数据的方式不止一种。最简单的方法是通过一种降维算法(如 PCA)来运行它,同时保持尽可能多的方差。有人可能会说,输出并不是真正的嵌入,尽管它可以类似地用于对数据进行聚类,并找到用户之间的相似之处。
另一个非常常见(并且通常更加准确)的策略是使用某种形式的矩阵分解,以便将用户项目矩阵(N_users * N_items)分解为两个低维矩阵——N _ users * N _ latent _ factors 和 N _ items * N _ latent _ factors——其乘积就是原始的用户项目矩阵。得到的潜在维度将捕获关于每个项目的最相关的信息或特征,并因此提高下游聚类任务的性能。
3 个用户和 3 个潜在因素的低维嵌入数据
3 个项目和 3 个潜在因素的低维嵌入数据
假设您确实能够创建一个较低维度的嵌入层,它仅使用 50 个维度来准确表示您所获得的数据集中存在的大约 4,000 位艺术家(或项目)。然而,您尝试的大多数聚类算法仍然无法在数据中找到任何有意义的聚类,这些数据似乎太密集,无法以有意义的方式进行聚类。
这是一个很常见的场景。随着使用的频繁和项目数量的增加,嵌入层的维度和复杂性通常也会增加。当您有几千个经常使用的项目时,很可能您的嵌入层将包含至少几十个压缩维度,这并不总是很好地聚集(尽管它们可能对其他类型的任务非常有用)。显然,您可以将数据嵌入到任意数量的维度中,但是在某个点上,如果您的维度太低,您可能会开始丢失关于用户之间区别的重要信息。
在这篇文章中,我想建议一种方法来进一步细化难以聚类的用户嵌入,以提高其聚类的性能和结果。简而言之,它基于从项目嵌入数据中学习高斯混合模型,然后基于每个用户成为每个项目聚类分布的一部分的概率,使用它来生成新的用户向量。除了简单且在足够多的实验中被证明是有用的这一事实之外,所提出的方法没有什么特别巧妙的地方,使得共享它成为一个好主意。虽然它是针对聚类用户-项目嵌入的用例,因为它依赖于共享相同向量空间的项目嵌入和用户嵌入的可用性,它当然可以扩展到其他上下文。
这篇文章分为四个主要部分。第二部分展示了一种从用户-项目交互数据生成嵌入的方法(尽管还有更多)。第三部分尝试对数据进行聚类,并展示了聚类中的主要问题。第四节介绍并实现了我们提出的方法。第五节得出结论。
二。嵌入交互数据
这里我要用的数据集是 LastFM 360K 。正如您在下面看到的,它可以很容易地格式化为普通的用户-项目交互数据。
不幸的是,我不能使用任何形式的原始数据集或子集来测试这里提出的方法,但 LFM 的数据是公开可用的,似乎是一个很好的替代方案。然而,值得注意的是,LFM 数据集在几个方面有所不同,这实际上使得结果不如我们进行的原始实验令人印象深刻。仅举一个明显的例子——LFM 的数据集实际上登记了用户、项目和使用次数。这意味着每个“行”或用户-条目对都应该根据所报告的交互数量进行加权,以获得准确的结果。为了简单起见,我选择忽略这些复杂性,但这显然应该在实际场景中加以考虑。
所以第一步是生成嵌入。我实际使用的嵌入模型是 Word2Vec。虽然 W2V 是一个最初设计用于根据单词在句子中的出现来创建单词嵌入的模型,但它也可以很好地创建多种序列的嵌入,包括根据用户“购物篮”中的出现来嵌入项目。关于如何生成嵌入,我不会涉及太多的细节,因为这里描述的方法可以应用于任何嵌入,而不管它是如何生成的,而且,我相信附件笔记本中的相关代码是非常简单明了和可重复的。
不过简而言之,我使用了(Gensim 的)W2V,以便根据艺术家在用户历史中的出现来学习嵌入他们。换句话说,我们将向模型提供用户列表和每个用户听过的艺术家列表,模型将为每个艺术家生成代表其最重要特征的潜在因素向量。
例如,由于列表中的艺术家之一是“罗伯特·舒曼”,我们训练的模型将生成相应的嵌入记录,该记录用由我们数据集中所有艺术家组成的空间中的向量来表示“罗伯特·舒曼”,如果它是准确的,则将其“定位”为更接近于类似于的艺术家的向量,即通常与舒曼一起出现在具有类似历史的用户中的艺术家。
舒曼的嵌入向量
虽然有更准确的方法来验证嵌入的准确性,但使用一些可以直观测试的示例来验证它也是一个好主意。如下所示,我们确实期望从音乐艺术家和收听用户的数据集学习的嵌入来检测相同流派的艺术家之间的相似性。
因此,现在我们有了准确的嵌入矩阵,我们可以继续对我们的用户进行聚类,并更好地了解数据中存在的组的类型和结构。然而,使用 W2V 嵌入交互数据的一个缺点是,当我们对聚集用户感兴趣时,它只留给我们嵌入项目或艺术家。处理这种情况的一种方法是获取每个用户参与的项目或艺术家的所有向量,并获取它们的平均值或中间值。换句话说,如果我听了舒曼和米勒·戴维斯的音乐,那么我的用户将由一个向量表示,该向量等于:
(model.wv['robert schumann'] + model.wv['miles davis']) / 2
详细介绍这种方法的优点和缺点或者回顾其他方法需要很长时间,但这是一种在相关潜在空间中捕捉用户“位置”的相当有效的方法。上面得到的向量将在舒曼和迈尔斯之间,以某种方式描述我的偏好和像我一样的用户的偏好。例如,在下面的代码中,我为一个听了两位古典艺术家(舒曼和马勒)和两位爵士艺术家(米勒·戴维斯和桑尼·罗林斯)的用户生成了一个均值向量。您可以看到,与该用户均值最相似的 10 位艺术家确实是爵士乐和古典音乐艺术家的混合体。
因此,我们将通过取每个用户听过的所有艺术家的平均值来创建用户矩阵。
三。聚类用户
所以现在我们开始尝试将我们的用户聚集起来。我们将使用 HDBSCAN,这是一种高效的基于密度的聚类算法,因为我们希望该算法根据数据的结构来确定数据中的聚类数,而不是对数据的形状及其分布进行假设,还因为它是一种相对快速高效的算法。不幸的是,我们不太成功。
不出所料,HDBSCAN 已经确定,除了少数几个小集群,我们的大部分数据都是噪声,上面的 2D 投影(用户嵌入)在一定程度上解释了这一点。我们的数据非常密集,这意味着用户不容易区分零件。这种结果很常见,通常需要进行更多的数据转换和超参数调整实验。
在这一点上,有些人会争辩说,试图“撕毁”聚类有些人为或徒劳,因为数据以这种方式聚集在一起的原因是…因为这就是数据点之间的关系和距离。然而,记住手头的问题是至关重要的。我们的任务是对拥有相似偏好和行为模式的用户进行细分。换句话说,该任务的目的是将数据分割成簇。然而,因为我们的数据非常密集,所以我们需要找到一种方法,让相似的用户比他们真正的更近,让不同的用户彼此更远。
如上所述,这篇文章的目的是为你处理这种情况的方法库增加一个工具。但是,在此之前,我只想简要展示两个其他方法或转换,作为基线。
首先,尽管有些争议,但提炼“大块”数据的一种非常有用的方法是使用流形学习算法,如 TSNE 或 UMAP,以便更好地表示局部结构,并通过使相似的样本更接近来进一步提炼聚类。我选择使用 UMAP 是因为它在大 Ns 上的良好性能以及它在局部和全局结构之间的平衡。
正如你在下面所看到的,我们的数据现在有了一些改进,因为 UMAP 确实进一步细化了聚类,将它们分成一部分,使 HDBSCAN 能够找到更密集的结构。但是,我们的大部分数据仍然是集中在一起的。
另一种常见的(也是更简单的)分解集群的方法是简单地获取日志并规范化数据。我在这里忽略了很多理论,但主要部分是,使用下面的代码应用这种转换将为我们留下以下结构。
使用对数变换和归一化来分离聚类
这种方法似乎也是一种改进,尽管它在 2D 空间中比在多维空间中分离得更好。换句话说,如果要在 2D 执行聚类,这种转换会很有帮助,但这意味着将丢失大量数据,这意味着结果的值将取决于手头的问题和数据。
三。从集群到分布
让我简要解释一下提议的转换的逻辑,然后深入研究实现。
我们有一个向量空间,包含了项向量和用户向量(其中每个用户向量等于用户参与的项向量的平均值)。我们想把相似的用户拉近到一起。因为使用户相似的是他们听相同的项目或艺术家的事实,那么,换句话说,我们希望用户更接近他们参与的项目,而远离他们不参与的项目。这让我们尝试下面这个简单的方法。
- 通过学习一个混合模型来聚类项目向量,该混合模型基于每个项目都是从高斯分布中采样的假设。当这个阶段完成时,我们有 K 个项目集群,而每个集群代表一个项目分布。一个好的结果将产生相似项目的聚类,例如摇滚乐队、流行艺术家等的聚类。但是,即使这种聚类不准确,它也仅用于转换数据(或从数据中生成新的要素)。让我们称这个阶段的结果为项目集群。
- 基于每个用户成为每个项目聚类的一部分(或其分布的一部分)的概率,为每个用户创建一个向量。例如,假设在步骤 1 中,我们找到了两个项目分类—一个是古典音乐艺术家,另一个是爵士乐艺术家。在步骤 2 中,我们迭代我们的用户向量,并估计每个用户向量成为每个项目聚类的一部分的概率。换句话说,对于每个用户,我们估计其向量从古典艺术家项目聚类中采样的概率,以及其向量从爵士艺术家项目聚类中采样的概率。简单来说:
Assuming we have 2 clusters (or distributions): Z1 and Z2, and user vector X1The new vector of user 1 will be [P(Z1|X1), P(Z2|X1)]
为什么这很有帮助?我还不能提供一个数学上严格的证明,部分原因是它并不总是正确的,但是下面的例子可能是解释直觉的一个起点。
考虑下图中的向量 A、B 和 C(每个向量由 2D 空间中的数据点表示),并假设 B 和 C 之间以及 A 和 B 之间的距离相等(或几乎相等)。很明显,根据它们在空间中的位置,很难确定它们相对于周围其他向量的相似性(或不相似性)。换句话说,问题“B 更像 C 还是更像 A?”很难回答,因为它们的距离几乎相等。这就是为什么基于密度的聚类算法的性能有些差。但是,如果我们将数据视为矢量分布的混合,并且如果我们发现有两种分布——蓝色和红色,那么通过比较矢量 A、B 和 C 由蓝色分布生成的概率,它们与周围其他矢量相比的相对相似性(或不相似性)会变得更加明显。之所以如此,是因为相对清楚的是,样本 B 和 C 最有可能是从与 a 不同的分布中“取样”的。
幸运的是,使用 sklearn 的高斯混合模型(GMM),这种方法也很容易实现。当 Sklearn 的 GMM 类适合数据集时,它假设数据点是从 K 个高斯分布的混合中生成的,并从数据中学习高斯混合模型,从而提供为每个样本或向量分配其最可能属于的高斯的能力。
因此,对于步骤 1,我们使用 GMM 来聚类我们的项目并生成项目聚类,而每个聚类对应于项目向量的高斯分布。
我使用这个简单的 util 函数来对项目进行聚类,并返回训练好的模型,我需要这个模型来估计以后的概率。
可以使用多种方法来选择 K 分量的数量。对于我的任务,我使用了最小化 davies_bouldin 分数的 K。
对于阶段 2,这是重要的一点,对于每个用户向量(代表用户已经听过的项目向量的平均值),我们使用训练模型的 predict_prob 方法来获得它成为每个项目聚类的一部分的概率。
因此,我们最终得到一个用户数据集,而每个用户向量现在由它成为每个项目聚类的一部分的概率向量来表示。让我们再次尝试使用 HDBSCAN 对其进行集群。
好多了。正如预期的那样,我们的新数据或转换后的数据本质上更紧密地聚集了用户,这些用户的向量最有可能是由某个项目集群或分布生成的。如上所述,这种转换使得用户之间的相似性或差异变得更加“激烈”,因为现在每个向量代表一组概率,而不是多维空间中的位置。
这些结果可以而且应该被进一步检验,看看这些分类是否有意义。这个数据集和我的实现中的一个问题是,我没有去掉最受欢迎的艺术家,这在某种程度上扭曲了结果,以及为了简单起见我跳过的其他步骤,这也使结果不那么令人印象深刻。
然而,我们仍然可以注意到某些用户群最常听的艺术家在主题上的明显相似性。例如,我们注意到一群用户对电子音乐(下图中的群 2)或重金属摇滚(下图中的群 1)有着强烈的偏好。
喜欢电子音乐的用户群
喜欢重金属摇滚的用户群
四。结论
当用户和项目之间的交互更加强烈并且用户偏好变化很大时,用户项目嵌入通常很难聚类。这种数据集最常见的方法通常包括应用某种形式的特征变换,以便使相似样本之间的相似性更明显,差异更清晰。
在这篇文章中,我分享了另一种可能有助于解决这种情况的方法。虽然它以类似的方式操作,但它可以被视为一种特征生成方法,而不是一种变换。该方法首先基于物品数据学习高斯混合模型,该模型本质上假设数据是从混合高斯分布生成的。然后,它为每个用户向量或记录生成一个新的特征集,该特征集表示从每个分布中被采样的概率。在这样做的时候,它本质上是用一个不同的度量来代替距离相似性度量,这个度量就是成为任一分布的一部分的概率。
这种方法已经在足够多的实验中产生了非常好的结果,值得分享,但是它还远远没有经过认真的思考或测试。话虽如此,但它通常做得很好,而且肯定会有益于出现类似困难的其他领域。
希望会有帮助
笔记本可以在这里找到
使用高斯过程回归作为生成模型,使用 Python
由 Unsplash 上的 Edge2Edge 媒体拍摄
以下是如何使用高斯过程生成新数据
如今,我们可以有把握地说**生成模型是人工智能的热点。**处理数据的人可能知道技术细节,而对于非技术人员来说,能够从现有数据集生成新东西的想法听起来基本上像科幻小说。出于这个原因,当非技术人群遇到像 这样的东西时,深度假冒 它会爆炸得相当快。
实际上有很多生成模型,而且大多数都使用深度学习方法(你听说过 GANs 吗?)。
你必须忍受的主要的、永恒的、巨大的权衡是,同时拥有可解释性和深度学习几乎是不可能的。
我的意思是,当你使用深度学习时,你有如此多的参数、层和操作,以至于你真的无法理解你的模型在做什么:你只知道(或希望)它工作得非常好。
在我们的研究中,我们需要创建一个具有特定属性的生成模型:它需要是可解释的。我们需要非常确定它实际上在创造什么,为什么,以及如何创造。所以我们使用高斯过程。
在这篇文章中,我想告诉你如何使用一个非常简单的算法,在给定一个参数作为输入的情况下,从现有的点中创建一组新的点。我们开始吧!
1.先决条件
让我们把事情简单化:我们正在谈论高斯过程回归。这意味着,首先,它是一个回归模型:
假设在某个值 X_1 =10 处有一个点 Y_1 = 5,在另一个值 X_2 = 14 处有另一个点 Y_2 = 20。回归模型帮助您找出 X_1 = 10 和 X_2 = 14 之间的 Y 值。
当然,你可以尝试用直线,多项式,正弦和余弦来做。高斯回归当然使用高斯分布。你基本上是在假设你可以用一个平均值和一个标准差来模拟你的点数。这意味着您的过程的不同表示将根据平均值和标准偏差进行分布。
如果你想更深入一点,这里是它的工作原理。
我使用 LaTeX 生成的图像
这意味着给定点 X1 的子集和值 y1 的子集,我们可以预测在点的 X2 子集中会发生什么。
那些已经知道所有这些东西的人可能已经注意到,在一天结束时,一切都依赖于一个被称为内核k .的函数
现在,我可以花几个小时写关于内核的东西,我甚至不是这方面的专家,所以我甚至不会去尝试(查看这里以了解更多)。我们需要知道的是内核根据其他点与固定点 x 的距离来确定我们给予其他点的权重。
例如,有理由假设,如果你想了解湖人队是否会在 2021 年赢得戒指,你会看他们 2019 赛季的赛程,也许还有 2018 赛季的赛程,但你不会看 1969/1970 赛季的赛程,因为这可能会误导人。
通常,设置权重的方式是使用 RBF 核 。你也可以使用 白化内核 添加一些噪点修正。
所以说重点吧。假设你的鼻窦受到某种噪音的干扰。
请不要运行这个!你会得到一个错误,因为我没有定义很多东西。
如果你使用我们所说的,我们可以得到这样的东西。
这就是你通常使用高斯过程回归的方式…
但是我们如何从我们的数据集中生成一组新的点呢?
换句话说:
如何才能最终使用高斯生成回归作为生成模型?
基本上我们要这样做:给定某个输入值(我们就称之为 T)我们要生成一个全新的点列表(称为时间序列)。
让乐趣开始吧。😃
2.这个想法
现在我们知道了这一切是如何工作的,我们需要理解如何真正地产生新的东西。
假设你有一个正弦函数,这个正弦的振幅取决于外界温度。大概是这样的:
我用乳胶制作的图像
其中噪声具有零均值和固定方差。
想象你有 200 个温度,每个温度 100 个点。这意味着,例如 T = [1,2,…,200]和 x = [1,2,…,100]。您将拥有一个 200 行 100 列的矩阵(表格)。你可以为每张桌子复制我们之前讨论过的想法。
另一方面,你可以反过来想!你可能会想到 100 个点,说每个点有 200 个温度值。如果现在你考虑你能得到的这种“颠倒”的情况,给定温度 t1 下的某一点 x_0 ,和在同一点 x_0 下的,对于另一个温度 T2 = t1+1,在两者之间会发生什么,例如在 T3 =(T2+t1)/2。
如果对数据集的所有点都这样做,你将得到 T3 =(T2+t1)/2 的整个点集。
伟大的事情:极其简单。坏消息:数据集的每个点都有一个模型,所以你不想有太多的点。
3.代码
我说了很多。让我们用一些代码深入研究一下。😄
3.1 图书馆
这是你需要的:
其实没什么特别的,只是相当主流的模块,比如 sklearn,matplotlib,numpy,pandas,seaborn…
3.2 实验
在我的实验中,我保持事情简单。当然,你可以想象有一个不同参数的时间序列列表,并应用你将在这几行中找到的相同推理。
我坚持分析这个信号的想法:
我用乳胶制作的图像
但是让我们说得更清楚些:
训练集特性:
- t 是一个“温度”,我们假设从 100 到 200 有 100 个等间距的温度
- x 是一个固定向量**,由 0 和 10 *π**之间的 50 个点组成
- 噪声方差= 5,均值= 0
测试设置特性:
- x 是同上
- T 是通过采样由中间温度组成的等间距数组的 20 个值 T29 得到的:T_test = (100.5,101.5,102.5,…)
下面是如何生成这个数据集。
训练集代码:
测试设置代码:x
实际上,您可以在这里用这段代码来拟合您的模型:
拟合你的模型:
这个模型会给你一堆拟合的模型(我们说过,每个点一个),如果你要的话,训练集上的结果。
然后,您可以使用以下代码生成新点:
运行训练好的模型并生成新的点。
整个过程就是由这个简单的函数完成的:
但是事情变得有趣多了。😃
还记得我们之前讲过的内核吗?嗯,可以调整内核以获得尽可能好结果。
实际上,来自 sklearn 的伟大的家伙们会优化这个过程,以便你可以尽可能地获得最佳值,但是尝试给算法一个提示作为起点是明智的。然后他们会开始围绕它进行优化。
当然,我们不知道该给出什么样的暗示。为此,我们将进行网格搜索,并从中获得超参数的最佳组合。
在优化我们的模型之前,我们当然需要定义我们在寻找什么。一般来说,我们可能需要两样东西:
- 良好边界(几乎所有数据都在 1.96 方差内)
- 好的意思(很合适……相当合理)。
换句话说,我们想要评估边界和 MSE 中包含的点数。您可以使用以下代码来实现:
评估自己的表现
这就是你如何网格搜索你的最优值。注意,我们玩的是一个双参数游戏: l 和 n 。有关它们的更多信息,请访问https://scikit-learn.org/stable/modules/generated/sklearn.gaussian_process.kernels.WhiteKernel.html和https://scikit-learn.org/stable/modules/generated/sklearn.gaussian_process.kernels.RBF.html。
网格搜索功能(一):
网格搜索功能(二)
4.结果呢
网格搜索功能执行以下步骤:
- 生成您的培训数据
- 生成您的测试数据
- 网格搜索你输入的所有模型
- 给你表演
让我们试试这个:
现在让我们看看表演吧!
有些模型的均方误差很差,但是 l=0.5 的模型看起来不错。
边界包含的点数是多少?
好的,如果我们设置 l=0,我们得到基本上完美的边界(记住 x 只有 50 个点),但是均方误差很可怕。所以就 MSE 和 in 点数而言,最好的是 n=0.01 和 l = 0.5。
让我们修复它们:
并使用它们:
这是你的最佳预测,从一个输入(T=101.5)生成一个全新的时间序列:
放大:
给定一个温度输出,我们生成了整个点序列:
我们使用高斯回归作为生成模型!
性能怎么样?它们符合我们的预期:
5.结论
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于现有最新技术的文章。
再见:)
利用图论有效解决数据科学问题
即使是最基本的也会让你开始!
为什么我应该现在学这个,而不是以后?
在数据科学领域有很多东西要学。通常,作为一名数据科学家,您倾向于边走边学。如果你当前的任务需要新的方法,你做一些研究,直到你能解决手头的问题。然而,如果我们没有学习这门学科的基础知识,图论的应用通常隐藏在我们意想不到的地方。在这篇文章中,我将向你展示在某些应用中,图论的基础知识是如何发挥作用的
- 大大加快你的代码
- 减少大量循环、索引等功能。来甜的俏皮话
- 使以前几乎无法解决的任务成为可能
所有学到的概念都会非常基础,并配有图片或代码。另外,我不会介绍超过 5 个新名词。
基本概念和术语
一个图由一组 节点 {a1,a2,…,和}以及一组 边 ,组成,这些边将部分或全部节点相互连接起来(图 1 )。例如,假设有一组机场{ap1,ap2,ap3}。如果所有机场之间都有航班,但 ap2 只提供到 ap3 的航班,则边是{(ap1,ap3),(ap3,ap1),(ap2,ap3),(ap3,ap2)}。典型地,术语“图”是指所有边也以它们的反转形式(ap1,ap3)——>(ap3,ap1)存在的那些图。否则,它被称为,这不是本指南的一部分。**
图 1:有三个节点和两条(唯一的)边的图的例子。除非另有说明,所有图片均由作者提供。
每一个图都可以用它的 邻接矩阵 用数字形式表示。在这种情况下,它是:
如您所见,这个对称矩阵在存在连接两个节点的边的地方保存 1,在不存在边的地方保存 0。更技术性的描述是所有节点对之间长度为 1 的路径的数量。
这就是你开始行动所需要的全部理论。让我们解决一些现实世界的问题。你可以在 github repo 中找到所有的代码。
数据准备
资料组
作为一个现实世界的应用程序,我决定使用一个飞行数据集。公开发布,该数据集包括在 2500 个参与机场起降的所有航班。下载指纹为“MD5:0 df 632 f 65 E1 d 7 b 7 DD 6 f 89294 e 81861 e 0”的数据集来复制我的分析。
清洁
对于这个分析,我只保留了开始(“起点”)和到达(“目的地”)的位置以及时间戳(“日期”)。此外,出于演示的目的,我随机抽取了 1000 个航班作为样本。关于精确的复制,请参见链接库。
分析
为了举例说明图论方法的效用,我们将向数据科学家提出一些关于飞行数据集的基本问题。然后,我们将比较典型的 pandas 方法和图表方法的简单性和效率。
预分析:创建邻接矩阵
图形方法基于操纵邻接矩阵。因此,我们必须首先创建它。将计算邻接矩阵所花费的时间视为一项投资。这是值得的。
首先,我们需要获得数据集中所有唯一机场的列表。使用 pandas 和 numpy 很容易做到这一点(不要忘记导入它们)。
**airports = np.unique(np.append(df[“origin”], df[“destination”]))**
由于 python 列表是有序的,每个机场现在都与一个固定的数值相关联。这个值将代表它在邻接矩阵中的位置。此外,我们可以使用这些来将数据集中的每个字符串映射到一个数值,这允许有效地计算邻接矩阵。
**mapping_dict = {k:i for i,k in enumerate(airports)}
df_mapped = df.applymap(lambda x: mapping_dict[x])**
现在,邻接矩阵只有一步之遥。我们将创建一个 nxn 零矩阵,n 是唯一机场的数量。然后,我们遍历每个数据帧行一次,看看是否需要将 0 值更改为 1。
**# Create null-matrix
A = np.zeros((len(airports), len(airports)), dtype = int)# Enter 1 into the null-matrix where there is an edge
for date, flight in df_mapped.iterrows():
i, j = flight["origin"], flight["destination"]
if A[i,j] == 0:
A[i,j], A[j,i] = 1,1**
好吧,我们做到了!这是我们的邻接矩阵:
问题 1:你能直接从 A 飞到 B 吗?
这个问题看起来极其琐碎。确实是!然而,即使对于这种问题,图表方法也优于标准方法。解决这个问题的常见方法是遍历每一行,如果有这样的直接路径,则返回 True。这种类型的函数可能如下所示:
**def exists_direct_path(df, node1, node2):
# Loop through each row
for i, row in df.iterrows():
# Check whether (node1, node2) or (node2, node1) is in the dataset
if {row["origin"], row["destination"]} == {node1,node2}:
return True
return False**
使用邻接矩阵,简单地索引它就可以了:
**A[node1, node2] == 1**
但是等等?这不是伪比较吗?“exists _ direct _ path”函数不做我们在预分析中做的事情吗?事实上,图表方法在这里只有优势,因为我们已经预先计算了普通方法现在做的事情。然而,这才是重点!我们已经遍历了每一行一次,现在可以简单地通过索引来检查每一条路径。使用“exists_direct_path”,函数的每个调用都将执行相同的循环。这显然是低效的。
事实上,做 200 次这样的比较花了我的笔记本电脑 18 秒。图表法耗时 0.0006 秒。我们已经可以看到:
有些问题使用标准的 python 和 pandas 方法完全可以解决,但是与基于图形的方法相比效率非常低。
问题 2:A 直接链接到多少个机场?
让我们关注一个稍微复杂一点,但仍然简单的问题。想象一下,你的老板想在另一个城市开设一家子公司,但这个城市与世界其他地方的最佳连接是至关重要的。
用传统方法解决这个问题意味着遍历所有行,并跟踪在类似列表的东西中找到的所有可能的目的地。它可能是这样的:
**def degree(df, node): # degree is the technical term
# Setup empty list
flights = []
# Loop through every row
for i, row in df.iterrows():
# If the node is either an origin or a destination, there must be a direct path
if row["origin"] == node:
flights.append(row["destination"])
elif row["destination"] == node:
flights.append(row["origin"])
# Remove duplicates
flights = list(set(flights))
return len(flights)**
使用邻接矩阵,这同样简单得多:
**A[node].sum()**
就像以前一样,运行时效率是惊人的。获得 200 个节点的度数花了我的笔记本电脑 19.5 秒,而基于图的方法花了 0.02 秒。
问题 3:从 A 地到 B 地旅行时,我需要换几次车?
让我们来看最后一个相当复杂的问题。假设一位顾客正在计划他的假期。他想在当地机场起飞,心中有五个可能的目的地。然而,他只想在旅途中换乘两次或更少。在这种情况下,我将从基于图的方法开始。你很快就会明白为什么。
我们可以利用邻接矩阵的另一个便利特性。请记住,前面我将邻接矩阵定义为表示每对节点之间长度为 1 的路径的数量。结果是,如果你把邻接矩阵提升到 2 的幂,你会得到长度为 2 的路径数,以此类推。换句话说:如果机场 ap1 和 ap2 之间的航班需要至少三次转机,你会发现:(A3)[ap1,ap2] > 0。因此,要找到两个机场之间的最短路径,我们需要将 A 乘以自身,直到我们找到一个值> 0。**
这个技巧允许我们通过提高 A 的幂来有效地找到最短路径,直到找到一条路径。一旦找到它,最后一个 A 数就是最小路径长度。我们还需要告诉计算机什么时候停止寻找,否则它会一直寻找,直到找到一条路径。在这种情况下,我将这个最大长度设置为 10。如果没有找到匹配的⁰,程序将返回 10。
**def shortest_path(A, x, y, iterations = 10, mapping_dict = mapping_dict):
# Create copy of Matrix to work on
M = A.copy()
# Get numerical representations of airports
i, j = mapping_dict[x], mapping_dict[y]
# Define the maximum power to which A is to be raised
iterations = 10 # A = A*A until a path is found or the max iterations are reached
for k in range(iterations):
if M[i,j] == 0:
M = np.matmul(M,A)
else:
return (x,y,k)**
回到手头的问题,您的客户想要在“LFPO”起飞,并记住了目的地“YWHA”、“LTAC”、“LIRF”、“埃夫拉”和“KRFI”。
**home_airport = "LFPO"
vacation_destinations = ["YWHA", "LTAC", "LIRF", "EVRA", "KRFI"]
max_changes = 2**
使用“shortest_path”功能,我们可以很容易地告诉他最多 2 次换乘就可以到达哪些目的地。
**n_changes = [shortest_path(A, home_airport, destination) for destination in vacation_destinations]acceptable = [destination for location, destination, changes in n_changes if changes <= 2]acceptable**
输出:
**['LIRF', 'EVRA']**
现在,让我们用一种不基于图论的不同方法来实现它。弄清楚如何做到这一点并不容易。事实上,我认为解决这个问题的唯一方法是尝试将多个航班相互链接的每种组合。一旦找到所有组合,最短路径就是所有组合中具有正确起点和终点的最短长度的组合。
如果你曾经使用过具有指数复杂度的算法,你可能已经知道这种方法有多糟糕。总共有 5 或 10 个机场时,它可能工作得很好。但是以 1000 个机场为例,在你的分析完成之前,你的孙辈们就已经退休了。与这种指数复杂度相比,基于图的方法具有 O(n ) 的复杂度,因为它所做的只是乘以矩阵。
可能是一些非常聪明的人想出了我不知道的算法,这些算法比这种暴力方法更有效。然而,很明显的是:
对于关于多个对象之间存在某种关系的某些数据科学问题,了解基本的图论将允许您编写高效的代码,并解决用其他方法实际上无法解决的“基本”问题。
结论
我们已经探索了使用图论方法来回答某些(看似)简单的数据科学问题。很明显,许多问题我们可以用循环来解决。使用基于图形的方法更容易解决。一旦邻接矩阵被创建,关于感兴趣的对象之间的关系的存在的大多数问题可以使用像索引或求和这样的单一表达式来回答。如果您的代码要应用于大量数据,或者如果同样的问题可能会再次发生,那么计算一次邻接矩阵是非常值得的。
此外,使用普通方法实际上无法解决一些问题,因为算法的复杂性甚至与中等规模的数据集不兼容。如果出现这种情况,您可能想后退一步,看看基于图的方法是否有一个简单的解决方案,就像本指南中所做的那样。
为了进一步阅读,我将把你链接到 Vaidehi Joshi 的这篇伟大的文章。它涵盖了更详细的理论基础,提供了更多的图论应用实例,并链接到一些更深入的文献。
非常感谢您的阅读!
用 Python 中的 Matplotlib 创建测井数据直方图
用直方图可视化数据分布
在 Unsplash 上 Marcin Jozwiak 拍摄的照片
介绍
直方图是探索性数据分析和数据科学中常用的工具。它们是优秀的数据可视化工具,看起来类似于条形图。然而,直方图使我们能够深入了解一组数据中的值的分布,并使我们能够在一个简洁的图中显示大量数据。在岩石物理学和地球科学领域,我们可以使用直方图来识别异常值,也可以挑选关键的解释参数。例如,来自伽马射线的粘土体积或页岩体积端点。
要创建直方图:
- 我们首先取一条测井曲线,并确定其中包含的数值范围。例如,me 可能有一个伽马射线测井,来自我们的一口井,范围从 5 到 145 API。
- 然后,我们将整个范围划分为不同的仓或区间。以伽马射线测井为例,我们可以得到从 0 到 10、11 到 20、21 到 30 一直到 141 到 150 的区间。
- 创建容器后,我们获取数据并将每个值分配到适当的容器中。
- 我们最终得到的是一个区间与频率的关系图,如下图所示
在这个简短的教程中,我们将看到如何使用 matplotlib 在 python 中快速创建直方图。我们还将看到如何定制绘图以包含额外的信息,如百分位值和平均值。
相关的 Python 笔记本可以在这里找到。
本教程的相关视频可以在我的新 YouTube 频道上找到:
导入库和加载 LAS 数据
任何 python 项目或笔记本的第一步通常是导入所需的库。在这种情况下,我们将使用lasio
来加载我们的 las 文件,pandas
来存储我们的测井记录数据,而matplotlib
用于可视化我们的数据。
import pandas as pd
import matplotlib.pyplot as plt
import lasio
我们在这个简短教程中使用的数据来自公开发布的 Equinor Volve 数据集。详情请见这里。该数据集由 Equinor(前挪威国家石油公司)发布,旨在促进研究、开发和学习。在本练习中,我们将使用该数据集中的一口井。
为了读取数据,我们将使用 lasio 库,这是我们在之前的笔记本和视频中探索过的。
las = lasio.read("Data/15-9-19_SR_COMP.LAS")
然后,我们将 las 文件转换为 pandas dataframe 对象。
df = las.df()
使用.describe()
方法,我们可以探索数据的汇总统计。
df.describe()
我们可以看到,这个文件中有七条测井曲线。
- AC 代表声波压缩慢度
- 井径仪校准
- 容积密度的 DEN
- 伽马射线的 GR
- 中子孔隙度的 NEU
- RDEP 深电阻
- 中等电阻率 RMED
使用熊猫创建直方图
我们可以使用 pandas 创建一个快速直方图,而不依赖于导入其他库。
df['GR'].plot(kind='hist')
plt.show()
使用 df[‘GR’]创建的 Volve 数据集的伽马射线测井的简单直方图。plot(kind='hist ')
使用 matplotlib 创建直方图
我们也可以像这样使用 matplotlib 创建相同的直方图。
plt.hist(df['GR'])
plt.show()
使用 plt.hist(df[‘GR’])创建的 Volve 数据集的伽马射线测井的简单直方图。图片由作者提供。
这产生了一个非常小的情节。我们可以看到值的范围从 0 到 150,API 为 250 时有很小一部分数据。每个 bin 大约有 25 个 API 宽,这是一个相当大的范围。
我们可以通过为 bins 参数指定一个 set number 来控制它,在本例中,我们将它设置为 30。
plt.hist(df['GR'], bins=30)
plt.show()
条柱数量增加的直方图。图片由作者提供。
让我们通过给箱子添加边缘颜色来稍微整理一下情节。
plt.hist(df['GR'], bins=30, edgecolor='black')
plt.show()
当我们这样做时,我们可以看到 API 低于 100 的仓位实际上是两个独立的仓位。
为了进一步整理绘图,我们可以指定 x 和 y 标签,并设置 x 轴限制。
plt.hist(df['GR'], bins=30, color='red', alpha=0.5, edgecolor='black')
plt.xlabel('Gamma Ray - API', fontsize=14)
plt.ylabel('Frequency', fontsize=14)
plt.xlim(0,200)
plt.show()
除了条形,我们还可以添加一个核密度估计,它为我们提供了一条显示数据分布的线。
df['GR'].plot(kind='hist', bins=30, color='red', alpha=0.5, density=True, edgecolor='black')
df['GR'].plot(kind='kde', color='black')
plt.xlabel('Gamma Ray - API', fontsize=14)
plt.ylabel('Density', fontsize=14)
plt.xlim(0,200)plt.show()
matplotlib 直方图,顶部绘制了核密度估计。图片由作者提供。
向图中添加额外信息
当计算粘土和页岩体积作为岩石物理工作流程的一部分时,我们通常使用百分位数作为我们的解释参数。这减少了异常值或少量数据点的影响,例如,这些数据点可能代表薄的热页岩。
我们可以使用内置的熊猫函数来计算关键的统计数据:mean()
和quantile()
。
mean = df['GR'].mean()
p5 = df['GR'].quantile(0.05)
p95 = df['GR'].quantile(0.95)
print(f'Mean: \t {mean}')
print(f'P05: \t {p5}')
print(f'P95: \t {p95}')
这将返回以下输出。
Mean: 71.98679770957146
P05: 12.74656
P95: 128.33267999999995
为了更好地了解这些点相对于我们的数据的位置,我们可以使用 axvline 将它们添加到绘图上,并传入计算的变量、颜色和标签。
df['GR'].plot(kind='hist', bins=30, color='red', alpha=0.5, edgecolor='black')
plt.xlabel('Gamma Ray', fontsize=14)
plt.ylabel('Frequency', fontsize=14)
plt.xlim(0,200)
plt.axvline(mean, color='blue', label='mean')
plt.axvline(p5, color='green', label='5th Percentile')
plt.axvline(p95, color='purple', label='95th Percentile')
plt.legend()
plt.show()
绘制了关键统计线的 matplotlib 直方图。图片由作者提供。
摘要
在这个简短的教程中,我们介绍了如何将测井曲线显示为直方图,并定制一个适合包含在报告和出版物中的图。
感谢阅读!
如果你觉得这篇文章有用,请随时查看我的其他文章,这些文章从不同的角度研究了 Python 和测井数据。你也可以在 GitHub 找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
在非英语文本中使用拥抱脸模型
作者图片
如何在非英语文本上使用来自拥抱脸的预先训练的英语语言模型
一个预训练模型是一个保存的机器学习模型,它以前在一个大数据集(例如维基百科中的所有文章)上训练过,以后可以用作一个执行特定任务的“程序”(例如找到文本的情感)。
拥抱脸对于预先训练好的语言处理模型来说是一个很好的资源。也就是说,大多数可用的模型都是针对流行语言(英语、西班牙语、法语等)训练的。).幸运的是,许多较小的语言都有预先训练好的模型可用于翻译任务。在这里,我将通过以下方式演示如何使用可用的模型:
- 将输入文本翻译成英语,
- 使用预先训练的英语模型执行特定任务,
- 将结果翻译回原始语言。
我使用爱沙尼亚语(约 110 万人的母语)作为输入语言,并评估该工作流程在以下任务中的实用性:
- 情感分析。
- 提取问题回答。
- 文本生成。
- 命名实体识别。
- 总结。
翻译
翻译是将文本从一种语言翻译成另一种语言的任务。这将是我们每个示例中的第一个也是最后一个任务。访问拥抱脸部模型的方法之一是通过他们的推理 API ,它能够运行推理(从机器学习模型中询问一些东西),而无需本地安装或下载任何模型。首先,你需要在拥抱脸注册,并在你的个人资料中获得你的 API 令牌。
使用 API 包括:
- 从模型中枢中选择模型并定义端点
ENDPOINT = https://api-inference.huggingface.co/models/<MODEL_ID>.
- 用您的个人 API 令牌定义头部。
- 定义查询的输入(强制)和参数(可选)。
- 运行 API 请求。
下面的端到端代码示例说明了这一点。通过从模型库中复制模型名称来定义 API 端点;2.设置一个 API 令牌,在拥抱脸注册账号后可以从你的用户设置中找到;3.定义向 API 发出 POST 请求的函数;4.定义我想翻译的输入文本,并使用这个输入和 5 运行 API 查询。提取结果。 NB:对于下面的翻译,我们只需要重复步骤 4 和 5(定义新的输入并提取结果)。
情感分析
情感分析是将输入文本分类为正面或负面的任务。可能的类别数量取决于特定的预训练模型。例如,一些模型仅使用两个类别(阳性、阴性),而其他模型使用三个(阳性、中性、阴性)或更多。
对于情感分析,我使用变形金刚库,这是继推理 API 之后的另一个简单选项,用于访问预训练的模型。通过笔记本中的!pip install transformers
或终端中的pip install transformers
安装库。
在给定任务中使用预训练模型的最简单方法是使用pipeline('name-of-the-task')
功能。该库为特定任务下载预先训练好的模型,推理在您的本地机器上运行(回想一下,如果您不想下载模型,可以使用推理 API)。让我们看看它是如何进行情感分析的:
- 首先,我们用管道函数和任务名称创建一个分类器。您可以从文档中找到可用的任务。
- 接下来,我们将输入文本从原始语言翻译成英语。
- 最后,我们用一行代码运行情感分析并提取结果。下面的例子很成功——翻译得不错“我们想要最好的,但结果总是这样。”我们正确地将句子的情绪归类为负面。
这是另一个例子,但是这次任务由于错误的翻译而失败了。翻译回复说“支持民主的最佳论据是与普通选民进行五分钟的谈话”,但正确的翻译应该是“反对民主的最佳论据是 T2”。在翻译和使用预先训练的英语模型时,要记住错误翻译的可能性。
抽取式问题回答
抽取式问题回答是基于输入文本回答问题的任务。在下面的例子中,我将给出一个关于我正在工作的单位的一般描述,并尝试回答“这个单位在做什么?”。让我们看一下下面的代码:
- 创建问答管道(第 2 行)。
- 提供原文的上下文,并将上下文翻译成英语。
- 用原文提供问题,并将其翻译成英语。
- 通过管道运行问题和上下文,并提取答案。
- 将答案翻译回原文。
上面的输出显示,翻译并不完美,但它会做的工作。“电子实验室在做什么?”这个问题的答案也有点尖锐但核心意思是正确的。
文本生成
文本生成(也称为因果语言建模)是在给定句子开头的情况下预测下一个单词的任务。换句话说,这就是机器学习模型试图成为作家的任务😄!
在下面的例子中,我们:
- 生成文本生成管道。
- 用爱沙尼亚语定义两个句子开头,然后翻译成英语。
- 使用管道根据开头生成文本,并将生成的单词数限制在 50 个以内。
- 将生成的文本翻译回爱沙尼亚语。
正如你从上面的结果文本中看到的,我们的模型产生了相当多的废话,比如“爱沙尼亚用燃烧天然气的水力发电厂发电”😄。也就是说,Est-to-Eng-to-Est 的工作流程似乎工作得很好,并且这个例子中的翻译质量非常好。
命名实体识别
命名实体识别(NER)的任务是试图从文本中找到人名、地名、组织名。在下面的例子中,我将给出两个爱沙尼亚语句子作为输入,并尝试从中检测所有命名实体。让我们看一下下面的代码:
- 初始化命名实体识别管道,并为我们的模型输出的的类定义合理的名称。在这里,我创建了一个字典,使用类代码作为键,爱沙尼亚语的含义作为值。这些以后会用到。
- 定义输入文本。
- 将输入内容翻译成英语。
- 为 NER 定义函数:我们的函数 a .)用输入运行 ner pipeline,b .)用爱沙尼亚语(在 for 循环中)的合理名称替换所有模糊的类名,c .)将属于同一实体的字符串/标记组合在一起(您可以在不分组的情况下尝试该函数以查看原始输出)。
- 打印所有实体及其所属的类。
上述结果表明,该模型能够正确检测所有命名实体,并将它们划分到相应的类别(组织或个人)。
摘要
摘要是将一篇长的文本或文档概括成一篇较短的文本的任务。在下面的例子中,我使用了 Google 的 T5 模型,该模型是根据混合输入数据(包括来自 CNN 和 Daily Mail 的文档)进行训练的。同样,我们可以遵循已经熟悉的工作流程:
- 用“摘要”任务初始化 Transformers 管道。
- 提供一个输入句子或文档。
- 将输入内容翻译成英语。
- 用预先训练好的英语模式总结课文。您可以提供摘要的最大和最小长度作为参数—这里,我将摘要的长度限制为 30 个令牌。
- 翻译回输入语言。
上面的总结者能够很好地捕捉输入的核心思想,但是输出有一些语法问题。因此,我们可以预期,该模型应该用于有意识的用户快速捕捉长文档的核心含义。
摘要
本文通过将翻译作为工作流的一部分,展示了在非英语语言上使用预先训练的英语语言模型的想法。这个概念在五个不同的任务上进行了测试:
- 在情感分析中,输出的质量很大程度上取决于翻译的质量。在这里,我们试图检测两个爱沙尼亚句子的情感——其中一个例子是成功的,另一个由于翻译错误改变了句子的意思而失败。
- 在回答问题时,我们看到我们的例子在翻译中几乎没有丢失任何东西。
- 文本生成也可以工作,但是很难看到这个任务的任何“真实”用例,因为输出纯粹是虚构的。
- 在命名实体识别中,我们在翻译中没有损失太多,我们的方法非常适用。
- 在文本摘要中,模型应该用于捕获较长文本的核心思想,而不是生成语法正确的文本。
简而言之,translate 的概念->使用预先训练的英语模型-> translate back 是一种有用的方法,可以在较小的非流行语言上完成各种自然语言处理任务。
使用倒排索引进行高效的文档相似度计算
实践教程
一种在某些情况下更有效地处理成对相似性计算的简单方法,使用 python 示例。
图片作者:https://www.pexels.com/photo/clear-light-bulb-355948/
搜索查询背后的魔力
我目前的谷歌搜索显示 Quora 上有超过 300 万个问题,但即使你正在键入一个新问题,它们也能立即向你显示类似的问题。我只需在谷歌的搜索栏中输入这些信息,算法就会根据我的搜索查询立即向我显示最相关的内容。
尽管有许多先进的概念来实现这个*魔术,*我将向您展示一个古老而简单的方法,用于在某些场景中更有效地处理文档的成对相似性计算。我将把它与暴力方法进行比较,并给出一些 Python 代码示例。
完整代码可以在这里找到: GitHub 链接。
相似性比较的重要性
如果我们可以用向量解释任何信息,那么通过一些相似性比较,我们就可以找到这个域中隐藏的联系。
相似性比较方法被大量用于聚类向量。它用于主题检测、文档聚类、语言翻译或检查抄袭。它还用于在社交媒体网站上推荐 youtube 视频或朋友建议。
还有更多的应用领域。简而言之,如果我们可以用向量解释任何信息,我们就可以利用它进行信息检索。
了解倒排索引
假设您有大量包含许多单词的文档。在这种结构中,根据单词所属的文档来收集单词。换句话说,文档是父,文字是子。倒排索引方法颠倒了这种关系,将单词作为父级,将文档作为子级。我将用一个例子来说明这一点。
假设您有这样的文档:
doc1: "Today is a beautiful, and a sunny day to start my workout."
doc2: "I will not be able to come today to meet with him."
doc3: "Our class meeting starts soon!"
doc4: "My class starts at 6."
在标记化、删除停用词和一些清理之后,文档看起来像这样:
doc1: ["today", "beautiful", "sunny" "day", "start", "workout"]
doc2: ["come", "today", "meet"]
doc3: ["class", "meet", "start", "soon"]
doc4: ["class", "start"]
现在我们可以颠倒文件和文字。
today : [doc1, doc2]
beautiful : [doc1]
sunny : [doc1]
day : [doc1]
start : [doc1, doc3]
workout : [doc1]
come : [doc2]
meet : [doc2, doc3]
class : [doc3, doc4]
soon : [doc3]
现在我们可以看到哪些单词出现在哪些文档中。或者,我们甚至可以演示单词在文档中的位置,但是为了简单起见,我们将使用这个演示。
我们如何从倒排索引结构中获益?
假设您正试图通过这些文档的特征嵌入(可以用 word2vec、doc2vec、BERT 或 TF-IDF 等创建的文档的数字表示)对它们进行聚类。),你会通过这些向量的余弦相似度来做到这一点。在强力方法中,由于有四个文档,您必须进行 6 次不同的相似性检查。
倒排索引结构有助于我们发现具有共同单词的文档。只有当一个文档与其他文档至少有一个共同的单词时,我们才会将它们进行比较。
为了提高效率,我们将按照数字顺序遍历文档。对于文档 1,它出现在单词“今天”和“开始于另一个文档”的集合中。这些集合的组合给出了[文档 1,文档 2,文档 3]。因此,我们将计算(文档 1,文档 2)和(文档 1,文档 3)之间的相似性得分。我们可以接着看第二份文件。
由于我们已经讨论了第一个文档,我们将不再考虑它。因为文档 2 与文档 4 一起出现在“meet”集合中,所以我们将只找到相似性(文档 2,文档 4)。
对于文档 3,由于它存在于文档 4 的“类”集合中,所以我们将只找到相似性(文档 3,文档 4)。
因为我们只剩下一个文档,所以我们不需要做任何计算。在前面的步骤中,我们已经涵盖了所有可能的配对。
因此,我们计算了。
(doc1, doc2) (doc1, doc3) (doc2, doc4) (doc3, doc4)
比较的次数比蛮力方法少两个!这似乎对性能有一点影响。因此,让我们用一个真实的数据集来尝试这种方法。
Python 的真实例子
我们将从这里加载一个文本数据集,它包含不同主题的土耳其文本数据。
数据是这样的:
category text
0 siyaset 3 milyon ile ön seçim vaadi mhp nin 10 olağan...
1 siyaset mesut_yılmaz yüce_divan da ceza alabilirdi pr...
2 siyaset disko lar kaldırılıyor başbakan_yardımcısı ar...
3 siyaset sarıgül anayasa_mahkemesi ne gidiyor mustafa_...
4 siyaset erdoğan idamın bir haklılık sebebi var demek ...
我们会在使用之前清除数据。
现在是时候生成一些特征向量了。目前,我所知道的最好的生成特征向量的方法是使用句子-Bert。你可以点击这里了解更多。但是,这超出了这篇博文的范围。因此,我们将使用简单的 TF-IDF 进行演示。
现在让我们试试暴力和倒排索引方法。
蛮力方法:
倒排指数法:
结果如下:
强力方法:
倒排指数法:
这两种方法之间没有明显的区别,不像承诺的那样。现在是时候讨论倒排索引方法的瓶颈了。
只有当文档足够清晰并且每个文档中的字数很少时,倒排索引方法才是有用的。否则,它可能比强力方法慢,因为生成倒排索引结构需要额外的计算。
这个问题最常见的解决方案是减少每个文档中的数据。有许多不同的特性选择方法,这些方法超出了本文的范围。为了解决这个问题,我们将从每个描述文档的文档中提取 50 个关键字,并用它们来表示文档。
提取关键词
结果如下:
其中“B.F .”代表“蛮力”,“I.I .”代表“倒排索引”。原始数据显示关键字提取前的结果,简化数据显示关键字提取后的结果。
提取关键字后,强力方法的比较次数没有变化,运行时间略有不同。不过倒排索引性能提升明显!这几乎比使用 1000 行数据的暴力方法快 50 秒。如果您正在处理数千行数据,那么运行时将会有显著的不同。
你可以在这里找到完整的代码: GitHub 链接。
最终意见
这篇博客文章展示了一种加速两两相似性比较操作的普通而简单的方法,并用 python 例子展示了它。结果表明,倒排索引方法仅在文档很短或非常清晰的情况下有用。尽管如此,对长数据使用数据简化技术还是有很大的不同。
今天,根据数据和挑战,可能有更好、更合适的方法。然而,倒排索引是一种易于实现和理解的方法。
就是这样。我希望你喜欢它,非常感谢你阅读我的博文。
如果您愿意,可以通过以下链接联系我:
推特:【https://twitter.com/etolga_ayan
领英:https://www.linkedin.com/in/emre-tolga-ayan-7b9a0a149/
GitHub:https://github.com/tolgayan/
干杯,
埃姆雷·托尔加·阿扬
参考
[1] S .耶尔德勒姆,土耳其语文本分类的基准数据,https://www.kaggle.com/savasy/ttc4900
[2] K. Ganesan,教程:用 TF-IDF 和 Python 的 Scikit-Learn 提取关键词,https://ka vita-Ganesan . com/Extracting-Keywords-from-text-tfi df/#。YDNHzegzZnJ
[3]利用倒排索引、草绘和采样的高效文档相似度,https://cosine similarity . org/content/inverted-index-sketching-sampling . html