如何构建快速迭代的主动学习数据管道
在计算机视觉的深度学习领域,有效利用主动学习可以显著提高构建 ML 模型的速度。还可以降低建立 ML 模型的成本。
主动学习数据管道
主动学习数据管道有四个主要部分:
- 在第一部分中,使用神经网络(NN)版本 n 对新的训练数据进行推理
- 主题专家评估在步骤 1 中执行的推理的结果。基于这个评估,由主题专家团队发现边缘案例(就像放射学 AI 中的放射科医生)。
- 人们使用标注工具标注边缘案例。
- 包括在步骤 2 中发现的边缘情况的新训练数据用于训练 NN 版本 N+1。
主动学习数据管道( 来源:人工智能最新技术 )
标记工具在边缘案例发现中的作用
创建地面实况(标注数据集)的过程包括标注专家使用标注工具创建精确的标注。除了提供标注功能,标注工具还可以通过提供其他功能在构建主动学习数据管道中发挥非常重要的作用,例如:
- 允许主题专家查看新训练数据的推断结果。
- 允许主题专家识别和标记当前神经网络版本(版本 N)失败的边缘案例。
标注工具可以通过两种不同的方式提供此功能:
选项 1:上传由神经网络版本 N 生成的预注释(PNG 掩码)
数据科学家可以上传显示神经网络版本 N 对新数据推断结果的 PNG 掩码。
上传 PNG 蒙版
选项 2:导入神经网络版本 N
导入神经网络版本 N,并允许使用神经网络版本 N 对新的定型数据运行推理。
标记工具允许边缘案例发现
利用 TrainingData.io 构建您的主动学习数据管道
TrainingData.io 有一个 SaaS 解决方案,允许主题专家以两种方式查看神经网络的结果:
- SaaS 解决方案能够导入您的神经网络,并使用导入的神经网络对新的训练数据进行推理。一旦推理的结果可用,主题专家可以评估结果。
- SaaS 解决方案可以导入由神经网络推理生成的 PNG 蒙版。这允许主题专家评估神经网络的结果。
一个好的注释平台不仅能满足注释者的需求,还能帮助人工智能团队与组织中的以下角色协作:
- 数据科学家
- 数据经理
- 注释者(内部和外部)
- 主题专家(审核者)
TrainingData.io 使用 NVIDIA Clara 为放射学(DICOM / NIFTI)提供主动学习数据管道。
如何在 SpaCy 中建立快速的“最相似词”方法
NLP-in-Production
以及对新发布的名为 Owl 的单词相似性 API 的介绍
自然语言处理(NLP)的最新发展引入了在生产中必须谨慎使用的语言模型。例如 spaCy 大型英文模型,en-core-we b-LG包含 60 多万个 300-d 向量。或者说,预先训练好的word 2 vec-Google-news-300模型包含 300 万个 300-d 的单词和短语向量。当您想要计算这些高维向量的度量时,解决方案可能很容易受到计算能力的影响。
在这篇文章中,我想分享我是如何加速 spaCy 库以用于单词相似性 API 的。与 Gensim 相反,spaCy 不支持高效的最相似方法。我最近发布了一个单词相似度 API,名为 Owl。这个 API 允许您使用包括 spaCy 在内的各种 word2vec 模型提取与目标单词最相似的单词。给定一个单词,这个 API 返回一个单词组列表,这些单词组与预定义上下文中的原始单词相似,比如 News 或 General。一般语境使用 spaCy 大英语模式。
如果您不熟悉 Word2Vec 模型,我推荐您先阅读下面的文章。你会发现为什么 Word2Vec 模型在机器学习中既简单又具有革命性。
Gensim 还是 spaCy?不了解 Word2Vec 机型的基础知识也没关系。
towardsdatascience.com](/word2vec-models-are-simple-yet-revolutionary-de1fef544b87)
—如何使用 spaCy 提取最相似的单词?
没有捷径可走。我必须计算目标单词的向量与 word2vec 模型中存储的大量向量之间的距离。然而,我可以用过滤器细化搜索空间,用优化的计算库加速计算。
我不需要恢复所有向量。我可以使用 spaCy 提供的.prob
属性来修剪向量(即细化搜索空间)。该属性有助于选择英语中最常用的单词w.prob >= -15
。在这里,您可以使用任何适合您的问题的过滤器。例如,您可能想要使用代表一个单词的阳性或阴性的.sentiment
属性来过滤向量池。下面的代码展示了如何为 spaCy 构建一个最相似的方法。不过,这段代码并没有进行快速运行的优化。
—如何使用 spaCy 加速最相似的单词?
延迟是 API 服务中的一个重要因素。我负担不起哪怕 1 秒钟的额外计算时间。因此,我优化了上面解释的最相似的方法,以满足我希望我的 API 满足的需求。
我进行了大量实验,以确定实现中哪些部分最耗时。首先,我怀疑sort
方法必须修改。对代码进行分析后,我发现.similarity
方法是延迟的主要来源,必须进行优化。
SpaCy 使用后端的余弦相似度来计算.similarity
。因此,我决定在上面的most_similar
方法中,用它的优化的对应物替换word.similarity(w)
。优化的方法cosine_similarity_numba(w.vector, word.vector)
使用 Numba 库来加速计算。结果得到了显著改善,即延迟从约 5 秒(API 不可接受)降至约 1.5 秒(API 可接受)。您应该用下面的行替换most_similar
方法中的第 12 行。
by_similarity = *sorted*(queries, key=*lambda* w: **cosine_similarity_numba(w.vector, word.vector)**, reverse=*True*)
下面,您可以使用 Numba 库找到余弦相似性的优化实现。根据其文档介绍, Numba 是一个开源的 JIT (Just In Time)编译器,可以将 Python 和 NumPy 代码翻译成快速机器码。这个功能对于以最小的改动加速一个人的代码是至关重要的。
—最好的最相似单词服务是什么?
必须根据结果的上下文和粒度来选择最相似的单词服务。上下文由 word2vec 模型表示**,而粒度由聚类质量**决定。
spaCy 大型英语模型包含在斯坦福大学手套项目培训的手套普通爬行 word2vec 模型。人们可能希望使用在特定数据语料库上训练的上下文化 word2vec 模型。所以,根据项目需求,必须选择 word2vec 模型。Owl API 让您有机会使用各种 word2vec 模型。
结果的粒度是最相似服务的一个重要特征。在下面,我想比较我在上面介绍的最相似方法和 Owl API 的最终结果。您将看到后一种方法的结果更加细化,因此更加有用。
"Owl (en-core-web-lg)": **{**
0: **[**"seattle", "portland", "washington"**]**,
1: **[**"denver", "chicago", "nashville"**]**,
2: **[**"toronto", "montreal", "ontario", "sydney"**]**
**}**"spaCy (en-core-web-lg)": **[**
"toronto","seattle","montreal","portland","ontario","denver",
"washington","chicago","sydney","nashville"
**]**
Owl API 建立在一系列 word2vec 模型之上,包括 spaCy en-core-web-lg 和高级聚类技术,以创建革命性的单词相似性服务。这个 API 可以用于各种项目,包括语言理解或推荐系统。你可以在这里找到更多关于 Owl API 的信息。
[## 人工智能:非正统的教训:如何获得洞察力和建立创新的解决方案
亚马逊网站:人工智能:非正统课程:如何获得洞察力和建立创新的解决方案电子书…
www.amazon.com](https://www.amazon.com/gp/product/B08D2M2KV1/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0)
临终遗言
当您想要在生产中使用 NLP 模型时,您必须使用子例程的优化实现来加速计算。例如,在自然语言处理中,余弦相似度经常为各种任务计算。因此,您必须确保它被有效地实现和编译,例如使用 Numba 库。另外,我强烈推荐使用 Owl API。根据需要,您可以从上下文化和粒度化的结果中受益。
感谢阅读!
如果你喜欢这个帖子,想支持我…
[## 通过我的推荐链接加入 Medium—Pedram Ataee 博士
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
pedram-ataee.medium.com](https://pedram-ataee.medium.com/membership)**
如何在 10 分钟内构建一个令人惊叹的交互式仪表盘
我给有抱负的数据科学家和数据分析师的建议
谷歌数据工作室免费提供快速和惊人的可视化!
使用 Google Data Studio 构建您的交互式仪表盘
获得交互式仪表盘的会员资格非常昂贵
我在与同事合作我的本地变更时遇到困难
仪表盘版本太多,不知道分享哪个
对于数据科学家和分析师来说,将我们的数据和结果传达给非技术用户非常重要。我们需要制作和分享令人惊叹的仪表板,使其易于交互。
虽然 Tableau 和 Qlikview 等 BI 产品有一个简单的界面来产生这种可视化效果,但技术许可费用很高。他们要求会员在你的同事之间私下分享。对于 Tableau,每月花费 50 美元。
现在,改变游戏规则的是
如果我告诉你,你可以在你的 Google Drive 中使用一个免费的、直观的、可共享的交互式仪表盘工具。
欢迎来到谷歌数据工作室
谷歌数据工作室是什么?
Google Data Studio 是一个数据可视化工具,用于生成交互式仪表板。借助 Data Studio,您可以:
- 导入—可靠地提供实时数据 : JSON/CSV、BigQuery、Sheets 等。
- 互动——产生洞察力:使用拖放方法开发交互式仪表盘。
- 分享——提升您的技能:通过 Google Drive 私下存储、协作和推广您的仪表盘。
全部免费。
本教程将帮助您准备使用实时数据开发仪表板。请随意跟随本教程或直接访问仪表板,在这里您可以复制并亲自尝试…
**附言:**如果您想要一种简单的方式来开发和部署您的仪表板即服务。我也为 Dash & Plotly 提供一个教程。它们是构建在 Matplotlib 和 Flask 之上的 python 库,是构建 web 的有用框架。
就这样,让我们开始吧。
导入:使用 BigQuery 可靠地提供实时数据
使用大查询导入实时 JHU 数据
Google BigQuery 提供了一个无服务器的、免费的、高度可扩展的数据仓库系统,可以在几秒钟内分析万亿字节的数据。它易于使用,可扩展,并准备好扫描数据。Google Data Studio 将您的数据连接到许多不同的来源。
在本教程中,我们将导入 JHU(约翰·霍普斯金大学)的 Covid 数据表到我们的仪表板。方便的是,谷歌已经为我们准备好了 JHU 实时数据连接。
在私有设置中,您希望为 BigQuery 设置有限的访问权限,以防止泄漏给您的用户。但是,一旦发布了仪表板,您的用户就可以访问您的报告和见解中的聚合数据。
互动:快速产生见解
构建 Data Studio 仪表板
在 Google Data Studio 中,您可以插入图表并用相关数据(日期/地区)自动填充图表。很多时候,除了滤镜和风格,你不会需要接触规格。
交互分配是自动的。默认情况下,每个日期/组件选择器已经将交互分配给其他可视化。这允许您在一个仪表板中进行切片和切块。
您还可以通过将组件分组来配置交互。
分享:跟踪和推广您的惊人仪表板!
共享定期电子邮件报告
共享 Google Data Studio 仪表板与共享 Google Drive 链接是一样的。您不再需要为您的同事和经理创建本地工作版本。
有了 Google Drive Storage,你将拥有一个私人的真相来源。您可以轻松链接到板载合作者或审阅者。这是通过指定收件人访问的访问链接来实现的,就像使用 Google Docs 一样。
此外,Google Data Studio 使得向您的利益相关者发送重复的电子邮件报告变得更加容易。您可以自由设置定期报告调度和发送您的生活数据给他们。
跟踪所有更改的版本历史
云中另一个有用的功能是版本历史。在协作中,您可能会忘记我们随着时间的推移所做的更改。版本历史通过为您提供一个统一的版本历史功能来跟踪所有更改,从而防止了这种情况。
有了这个,您可以轻松地审计和恢复到一个稳定的版本,并像往常一样协作。
最后的想法
在本教程中,我们了解了 Google Data Studio 的三大优势:
- 导入:使用 BigQuery 提供可靠的实时数据
- 互动:快速产生见解
- 分享:跟踪和推广您令人惊艳的仪表盘!
有了它,您可以将实时数据导入 dashboard,拖放交互式可视化和令人惊叹的样式,并与您的同事和经理轻松分享。
一如既往,如有任何问题,请通过 Linkedin联系我。如果时间允许,我很乐意回答你的问题。
索利·德奥·格洛丽亚
关于作者
文森特用 ML @ Google 对抗网络滥用。文森特使用高级数据分析、机器学习和软件工程来保护 Chrome 和 Gmail 用户。
除了在谷歌的工作,Vincent 还是《走向数据科学媒体》的特约撰稿人,为全球 50 万以上的观众提供有抱负的 ML 和数据从业者的指导。
在空闲时间,文森特在佐治亚理工学院攻读硕士学位,并为铁人三项/自行车旅行进行训练。
最后,请通过 LinkedIn , Medium 或 Youtube 频道 联系文森特
与 GatsbyJS 和 Netlify 一起构建一个超棒、超快的数据科学组合
我如何将我的旧博客转移到一个新的现代网络堆栈
今年我想完成的第一件事就是完全重新设计我的博客。这一个是使用基于 Python 的静态站点生成器结合 Flex 主题构建的。
总而言之,它包含了有趣的功能——比如移动响应和 Disqus、AddThis 和 Google Analytics 的插件。而且,最重要的是,它很容易与 Markdown 文件集成,这对于文章写作来说很方便。
我以前的博客
一切正常,直到我注意到一些问题:
慢速加载页面
随着我的帖子越来越长,加载时间也在不断增加。虽然这在桌面上通常不是问题,但移动设备却深受其害。这影响了我的大部分观众——他们主要使用移动设备。
有限的主题模板
我使用的模板不允许我展示个人项目和我正在开发的开源工作。添加新页面也不容易。事实上,我不得不深入主题的 HTML 代码,并对其进行调整以满足我的需求。我发现将主题的逻辑从博客的引擎中分离出来是很乏味的。
一些代码突出显示问题
嵌入代码片段是我的帖子的重要部分。不幸的是,我注意到一个令人沮丧的呈现问题出现在移动设备上,比如长行代码的换行。说真的,谁愿意读那种代码?
一组有限的插件
Pelican 有各种各样的插件,但是其中一些已经过时或者只适用于 Pelican 的特定版本——还有一些功能缺失了。
寻找替代方案
所以我开始寻找鹈鹕的替代品。我想的是一个框架,它允许我构建快速加载和响应的页面,同时保持高度的定制化。
那时我听说了盖茨比。
盖茨比
“Gatsby 是一个基于 React 的免费开源框架,可以帮助开发人员构建速度极快的网站和应用。”—gatsbyjs.org
我觉得官方的描述总结的很好。除此之外,让我转而看盖茨比的原因是:
- 它建立在最新的 web 技术之上,比如 React 和 webpack:因此,建立一个新的组合是学习新的前端框架的一个机会
- Gatsby 可以连接到各种数据源,包括 Markdown 文件、API、数据库等。
- Gatsby 通过在
public
文件夹中以静态文件的形式构建您的站点,使站点部署变得非常容易,这些文件随后可以被推送到 GitHub、Netlify 或 Amazon Amplify - 它只加载渲染网站内容所需的关键 HTML、CSS 和 JS,所以点击速度非常快
我们可以继续下去,但说服你自己的唯一方法是尝试,对吗?
盖茨比入门
我的目标不是给你一个如何使用 Gatsby 的完整教程:事实上,互联网上有大量的资源。你可以查一下的官方文件,比如。
但是这里有一些快速开始的步骤:
准备您的环境
- 安装节点和 npm。例如,如果你使用的是 Mac,假设你有自制软件,你可以启动这个命令。
brew install node
如果你使用的是 Windows 或 Linux 发行版,你可以查看文档。这很简单。
- 安装 Git
- 通过启动以下命令安装 Gatbsy CLI
npm install -g gatsby-cli
以下部分借用自文档:
现在您已经准备好使用 Gatsby CLI 工具来创建您的第一个 Gatsby 站点。使用该工具,您可以下载“启动者”(带有一些默认配置的部分构建的站点),以帮助您更快地创建某种类型的站点。您将在这里使用的“Hello World”启动程序是一个启动程序,具有 Gatsby 站点所需的基本要素。
打开你的终端。
经营盖茨比新 hello-worldhttps://github.com/gatsbyjs/gatsby-starter-hello-world。(注意:根据您的下载速度,所需的时间会有所不同。为了简洁起见,下面的 gif 在安装过程中暂停了)。
运行
cd hello-world
。运行
gatsby develop
。
差不多就是这样。现在,您有了一个在本地运行的静态网站。尝试修改它的内容,你会看到(保存后)你的修改被实时更新。
使用预先存在的模板
如果你不是我这样的前端忍者,可以在官网的 Showcase 标签中查找自己喜欢的模板。
有大量的开源网站和作品集都是使用 Gatsby 构建的。
使用 Gatsby 建立的网站和作品集示例
盖茨比插件
现在盖茨比框架中最有趣的部分:盖茨比插件库。
如果你想扩展网站的功能,比如添加图像响应、搜索引擎优化、谷歌分析、离线支持、网站地图、robot.txt 等等,你可以在这里找到你需要的一切。
社区充满活力,插件得到了很好的支持。您可以在搜索栏中搜索它们。
盖茨比插件
如果你想让你的博客或作品集有很好的 SEO,我发现了这个 repo 列举了一系列有用的 Gatsby 插件。
部署
一旦您对您的网站满意了,您就可以通过运行项目根目录下的 build 命令来生成准备部署的静态文件。
gatsby build
这个命令创建一个静态版本的网站,它可以在互联网上运行,并驻留在public
文件夹中。你既可以把它复制到你的服务器,推送到 GitHub 页面,也可以使用部署服务(比如 Netlify )。我个人选择 Netlify 是因为我发现它很容易使我的工作流程自动化。
网络生活主页
文档很棒,您可以找到许多博客文章,带您完成在 Netlify 上部署 Gatsby 站点的过程。这里有一个来自 gatsbyjs.org 的很棒的和一个来自 netlify.com 的和一个。
您可能会有的几个问题
那么你以前的帖子会怎么样?
我的博客从 2016 年就开始直播了,所以我肯定不会把以前的帖子扔掉。幸运的是,在《盖茨比》中把它们改写成减价文件很容易,所以我这样做了。这甚至是一个更好地重组文件夹结构的机会。
新博客的 URL 结构不同。如果用户点击一个旧的链接会发生什么?
我使用gatsby-plugin-netlify
通过指定新旧 URL 之间的映射来创建永久的 URL 重定向(使用 301 HTTP 代码)。当执行构建命令时,这个插件在public
文件夹的根目录下生成一个_redirects
文本文件,该文件写下这个映射并告诉 Netlify 正确执行这些重定向。我发现这个解决方案非常方便。
下面是一个_redirects
文件的例子:
新旧 URL 之间的映射
你以前的 Disqus 评论怎么了?他们会迷路吗?
希望不会。Disqus 提供名为 URL Mapper 的迁移服务,需要手动输入新旧 URL 之间的 CSV 映射。
这里有一个教程带你完成这个过程。
谷歌分析怎么样?你是创建一个新账户还是保留旧账户?
我搜索了一下这个问题的答案,发现一个堆栈溢出线程在谈论这个问题。
推荐的解决方案是…什么都不做。你只需要在你的新网站上重复使用你的旧账户。
你的 AddThis 账户怎么样?你会失去之前在社交分享上的统计数据吗?
很遗憾,是的。AddThis 以不同的方式处理每个 URL,它无法判断新的 URL 应该被视为旧的 URL。
那么新网站看起来怎么样呢?
可以在 GIF 中查看。
如果你想建立自己的网站,你知道你现在要做什么。
感谢阅读!
📝将这个故事保存在期刊中。
👩💻每周日早上醒来,你的收件箱里会有本周最值得关注的科技新闻。阅读科技简讯中值得注意的内容。
如何从头开始构建 KNN 分类模型,并使用 Streamlit 对其进行可视化
虽然像 sklearn 这样的图书馆让我们的生活变得更加容易,但是从头开始制作一个模型总是一个好的实践。在本教程中,我们将从头构建一个 KNN 分类模型,并使用 Streamlit 构建一个 web 应用程序对其进行可视化。下面是最终应用程序的演示。
最终 Streamlit 应用的截屏
KNN 概述
KNN 或 K 近邻用于分类和回归。在本教程中,我们将使用它进行分类。由于目标标签是已知的,所以这是一种有监督的算法。它本质上接受一个输入,并找到 K 个最接近它的点。然后检查最近点的标注,并将输入分类为出现次数最多的标注。假设我们想要建立一个模型,根据输入的体重、身高将动物分类为狗或猫。如果 K = 3,我们找到离输入最近的 3 个点并检查它们的标签。如果 3 个最近点中的 2 个有标签“狗”,我们的模型将输入分类为“狗”。如果 3 个最近的点中的 2 个有标签“猫”,我们的模型将输入分类为“猫”
步伐
- 标准化数据集并存储它,即确保所有值都在 0 和 1 之间。
- 取一个输入数据点,并从数据集中的所有记录中找出距离。将距离存储在列表中。
- 对包含距离的列表进行排序,并检查排序列表中前 K 条记录的标签
- 将输入分类为在前 K 条记录中出现次数最多的标签
首先,我们将创建所有我们需要的助手函数。然后我们再把它们结合起来,加入一些 streamlit 函数,构建一个 web app。
为了便于理解和可视化,我们将使用具有 2 个要素和二进制标签(即“0”和“1”)的数据集。
助手功能
标准化数据的函数
为了规范化一个值列表,我们迭代每个值,并找到列表中的值和最小值之间的差。然后我们将它除以列表中最大值和最小值的差。
标准化数据的方程式
*def* **min_max_normalize(*lst*)**:
minimum = min(lst)
maximum = max(lst)
normalized = [(val - minimum)/(maximum - minimum) for val in
lst]
return normalized
该函数接受一个值列表,并返回规范化的值
计算欧几里德距离的函数
该函数将用于计算两个给定点之间的距离。我们将使用欧几里德公式来计算距离。
计算欧几里德距离的公式
*def* **distance(*element1* , *element2*):**
x_distance = (element1[0] - element2[0])**2
y_distance = (element1[1] - element2[1])**2
return (x_distance + y_distance)**0.5
该函数接受两个 2D 点,并返回它们之间的欧几里得距离。由于我们考虑的数据集只有两个要素,因此我们只考虑 x 和 y 坐标。随着特征数量的增加,该函数将需要改变以找到所有指数之间的平方差。
查找输入点和数据集中所有点之间距离的函数
我们迭代数据集中的每个值,并使用上面的 距离 函数来计算两点之间的距离。然后,我们存储距离并进行排序。
*def* **find_nearest(*x* , *y* , *input* , *k*):** distances = []
for id,element in enumerate(x):
distances.append([distance(input , element),id])
distances = sorted(distances)
predicted_label = get_label(distances[0:k] , y)
return predicted_label, distances[0:k] , distances[k:]
该函数将以下参数作为输入:
- x: 这是包含这两个特征的数据集
- y: 这包含了 x 中每一行的标签。它们是分别映射的,即 x[i] 的标签是 y[i]
- **输入:**这是一个 2D 数组,包含我们想要分类的点的特征
- k: 我们希望模型考虑的最近邻的数量
首先,我们创建一个空数组来存储距离。我们需要在数据集中存储记录的距离和索引。该索引可以在 y 数组中使用,以找到该记录的标签。
然后我们对距离进行排序。接下来,我们使用 get_label 函数(将在下面讨论)来获取最常出现的标签。
由于距离数组被排序,前 k 个元素,即**距离【0:k】**是我们输入的 k 个最近邻。我们返回输入的预测标签、k 个最近的邻居和其余的邻居。
查找最常出现的标签的功能
我们基本上得到 k 个最近的邻居,检查每个邻居的标签。在我们的例子中,我们只有两个标签“0”和“1”。如果邻居的标签是“0 ”,我们对“0”的出现增加计数,并对“1”进行同样的操作。我们比较两个标签出现的次数,并返回计数较高的标签。
*def* **get_label(*neighbours*, *y*):** zero_count , one_count = 0,0
for element in neighbours:
if y[element[1]] == 0:
zero_count +=1
elif y[element[1]] == 1:
one_count +=1
if zero_count == one_count:
return y[neighbours[0][1]]
return 1 if one_count > zero_count else 0
该函数将 k 个最近邻作为输入。邻居中的每条记录都包含距输入点的距离及其原始 id。我们使用 id 和 y 数组来获取记录的标签。然后我们检查标签并返回预测的标签。
我们已经创建了所需的助手函数。现在我们将它们与一些 streamlit 函数结合起来。令人兴奋的东西!😎
必需的库
我们将使用 Plotly 来绘制我们的图形,因为 Plotly 绘制交互式图形。对于我们的数据集,我们将从 sklearn.datasets 导入一个数据集。
import streamlit as st
import pandas as pd
from sklearn.datasets import make_blobs
import plotly.express as px
import plotly.graph_objects as go
我将把教程的剩余部分分成三个部分
- 用户输入
- 导入数据集并将其可视化
- 可视化预测
用户输入
App 截图
我们将使用 streamlit 的 title 方法显示一个标题,使用 slider 方法创建一个数字滑块来获取用户的输入。由于我们的数据是标准化的,我们希望输入也是标准化的。因此,我们限制用户输入 0 到 1 之间的值。我们还可以从用户那里获取输入,并使用数据集中的最小和最大值来规范化输入。
st.title("KNN Visualize")x_input = st.slider("Choose X input", *min_value*=0.0, *max_value*=1.0,*key*='x')y_input = st.slider("Choose Y input", *min_value*=0.0, *max_value*=1.0,*key*='y')k = st.slider("Choose value of K", *min_value*=1, *max_value*=10,*key*='k')input = (x_input,y_input)
每次滑块值更改时,整个 python 脚本都会重新运行,变量将根据滑块包含新值。
导入数据集并将其可视化
App 截图
x , y = make_blobs(*n_samples* = 100 , *n_features* = 2 , *centers* = 2, *random_state*= 2)
make_blobs 函数为我们创建了一个数据集,它看起来类似于上图中的分布。在现实世界中,数据集不会如此合作,但这个数据集现在已经足够了。我建议你使用 matplotlib 绘制一个散点图,看看数据的分布情况。
x 包含特性, y 包含各自的标签
# Normalizing Data
x[:,0] = min_max_normalize(x[:,0])
x[:,1] = min_max_normalize(x[:,1])# Dataframe
df = pd.DataFrame(x , *columns* = ['Feature1' , 'Feature2'] )
df['Label'] = yst.dataframe(df)
首先,我们使用之前创建的规范化辅助函数来规范化我们的数据。然后我们结合 x 和 y 数组来创建一个数据帧。我们使用 streamlit 的数据帧方法来查看数据帧。
# Initial Data Plotfig = px.scatter(df, *x* = 'Feature1' , *y*='Feature2', *symbol*='Label',*symbol_map*={'0':'square-dot' , '1':'circle'})fig.add_trace(
go.Scatter(*x*= [input[0]], *y*=[input[1]], *name* = "Point to Classify", )
)st.plotly_chart(fig)
你可以阅读 Plotly 的文档来更好的理解上面的代码。我们用刚刚创建的数据帧的散点图创建一个图形。我们还添加了输入点,以便更好地理解它相对于数据集中其他点的位置。Streamlit 的 plotly_chart 方法以 plotly 图形为参数,在我们的 app 上绘制交互图形。
预测并可视化它
App 截图
#Finding Nearest Neighbourspredicted_label , nearest_neighbours, far_neighbours = find_nearest(x ,y , input ,k)st.title('Prediction')st.subheader('Predicted Label : {}'.format(predicted_label))
我们使用我们之前创建的 find_nearest 函数来获得预测的标签和 id、k 个最近邻居和远邻居的距离。
我们显示预测的标签 us streamlit 的子标题方法
nearest_neighbours = [[neighbour[1],x[neighbour[1],0],x[neighbour[1],1],neighbour[0],y[neighbour[1]]] for neighbour in nearest_neighbours]nearest_neighbours = pd.DataFrame(nearest_neighbours , *columns* = ['id','Feature1','Feature2','Distance','Label'])st.dataframe(nearest_neighbours)
上述代码基本上使用最近邻的 id,并将 id、距离与记录及其标注的 feature1、feature2 值相结合。我们使用组合列表来创建包含最近邻居信息的数据帧。然后我们使用 streamlit 的 dataframe 方法来显示它。这个数据框架将帮助我们理解下面的图表。
far_neighbours = [[neighbour[1],x[neighbour[1],0],x[neighbour[1],1],neighbour[0],y[neighbour[1]]] for neighbour in far_neighbours]far_neighbours = pd.DataFrame(far_neighbours , *columns* = ['id','Feature1','Feature2','Distance','Label'])fig2 = px.scatter(far_neighbours,*x*='Feature1',*y*='Feature2',*symbol*='Label',*symbol_map*={'0':'square-dot' , '1':'circle'})
我们为远邻居创建一个类似的数据帧。我们使用 Plotly 来绘制散点图。我们现在将添加输入和将输入连接到其 k 个最近邻居的线。
for index,neighbour in nearest_neighbours.iterrows():
fig2.add_trace(
go.Scatter( *x*=[input[0], neighbour['Feature1']], *y*=[input[1],
neighbour['Feature2']],*mode*='lines+markers' , *name* = 'id
{}'.format(*int*(neighbour['id'])) )
)st.plotly_chart(fig2)
我们迭代每个邻居,并在邻居和我们创建的图形的输入点之间添加一条线。最后,我们使用 plotly_chart 方法绘制图形。
就这样👏我们从头开始创建了一个 KNN 分类器,并创建了一个 Streamlit 应用程序来可视化它
如果您有兴趣部署您的 streamlit 应用程序,请查看我的教程。
你可以在这里找到 GitHub 回购。
我对机器学习的世界还是相当陌生的,如果你发现任何错误或任何可以优化的代码,请让我知道!我总是乐于接受反馈😃
我最近用 WordPress 创建了一个博客,如果你能看看的话,我会很高兴的😃
[## Python 项目教程-使用这些 Python 项目教程改进您的简历/作品集。
使用 Streamlit 共享部署您的机器学习 Web 应用程序在我以前的文章中,我谈到过构建一个…
realpythonproject.com](https://realpythonproject.com/)
在 LinkedIn 上与我联系
[## Rahul baner JEE——产品工程实习生——EY | LinkedIn
查看 Rahul Banerjee 在世界上最大的职业社区 LinkedIn 上的个人资料。拉胡尔有 4 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/rahulbanerjee2699/)
在 Twitter 上与我联系
如何建立机器学习模型
关于建立机器学习模型的卡通信息图。(由 Chanin Nantasenamat 绘制)
数据科学 | 机器学习
学习数据科学的可视化指南
学习数据科学可能看起来令人生畏,但事实并非如此。让我们让学习数据科学变得有趣而简单。因此,挑战在于我们如何让学习数据科学变得既有趣又简单?
漫画很有趣,既然“一张图胜过千言万语”,那么为什么不制作一部关于数据科学的漫画呢?带着这个目标,我开始在我的 iPad 上涂鸦构建机器学习模型所需的元素。过了几天,上面显示的信息图是我想出来的,也发表在了 LinkedIn 和数据教授 GitHub 上。
资料组
数据集是您构建机器学习模型之旅的起点。简单地说,数据集本质上是一个 M × N 矩阵,其中 M 表示列(特征),而 N 表示行(样本)。
列可以分解为 X 和 Y 。首先, X 是特征、自变量、输入变量等几个相似术语的同义词。其次, Y 也是类标、因变量、输出变量几个术语的同义词。
一个数据集的卡通插图。(由 Chanin Nantasenamat 绘制)
应当注意,可用于*(可执行回归或分类)的数据集将包含 X 和 Y ,而可用于 无监督学习 的数据集将只有 X 。*
此外,如果 Y 包含定量值,则数据集(由 X 和 Y 组成)可用于任务,而如果 Y 包含定性值,则数据集(由 X 和 Y 组成)可用于 分类任务**
探索性数据分析
进行探索性数据分析(EDA)是为了获得初步的理解,并让我们熟悉数据集。在一个典型的数据科学项目中,我首先要做的事情之一就是执行 EDA 来“目测数据”,以便更好地理解数据。**
我通常使用的三种主要 EDA 方法包括:
- 描述性统计 —均值、中位数、众数、标准差
- 数据可视化 —热图(识别特征内部相关性)、箱线图(可视化组差异)、散点图(可视化特征之间的相关性)、主成分分析(可视化数据集中呈现的聚类分布)等。
- 数据整形 —透视数据、分组数据、过滤数据等。
****NBA 球员统计数据的示例方框图。从 Jupyter 笔记本上获得的关于 GitHub 教授的数据。
****NBA 球员统计数据的示例关联热图。从 Jupyter 笔记本上获得的剧情数据 GitHub 教授。
****NBA 球员统计数据的柱状图示例。从 Jupyter 笔记本上获得的剧情资料 GitHub 教授。
****NBA 球员统计数据散点图示例。从 Jupyter 笔记本上获得的剧情资料 GitHub 教授。
要获得更多关于用 Python 执行这些探索性数据分析的分步教程,请查看我在 Data Professor YouTube 频道上制作的视频。
数据预处理
数据预处理(也称为数据清理、数据争论或数据篡改)是对数据进行各种检查和审查的过程,以便纠正缺失值、拼写错误、标准化/标准化值以使其具有可比性、转换数据(如对数转换)等问题。
“垃圾进,垃圾出。”
—乔治·富希塞尔
正如上面的引用所表明的,数据的质量将对生成的模型的质量产生很大的影响。因此,为了实现最高的模型质量,应在数据预处理阶段投入大量精力。据说,数据预处理很容易占据数据科学项目所用时间的 80%,而实际的模型构建阶段和随后的模型后分析占据了剩余的 20%。
数据分割
列车测试分离
在机器学习模型的开发中,希望经过训练的模型在新的、看不见的数据上表现良好。为了模拟新的、看不见的数据,可用数据经受数据分割,由此它被分割成 2 部分(有时称为 训练测试分割 )。特别地,第一部分是较大的数据子集,用作(例如占原始数据的 80%),第二部分通常是较小的子集,用作(剩余的 20%数据)。应当注意,这种数据分割被执行一次。****
接下来,训练集用于建立预测模型,然后将这样的训练模型应用于测试集(即用作新的、未见过的数据)以进行预测。最佳模型的选择是基于模型在测试集上的性能做出的,并且在努力获得最佳可能模型的过程中,也可以执行超参数优化。**
训练-验证-测试分割
数据拆分 的另一种常用方法是将数据拆分成 3 部分:(1)训练集,(2)验证集,(3)测试集。与上文所述类似,训练集用于构建预测模型,也在验证集上进行评估,从而进行预测、模型调整(例如超参数优化)并根据验证集的结果选择最佳性能模型。正如我们所看到的,类似于上面对测试集执行的操作,这里我们对验证集执行相同的过程。注意 测试集 不涉及任何模型的建立和准备。因此,测试集可以真正充当新的、看不见的数据。谷歌的机器学习速成班对这个话题进行了更深入的探讨。
交叉验证
为了最经济地使用可用数据,通常使用N 重交叉验证(CV),从而将数据集划分为 N 重(即通常使用 5 重或 10 重 CV)。在这样的 N 褶皱 CV 中,其中一个褶皱被遗漏作为测试数据,而剩余的褶皱被用作建模的训练数据。
例如,在一个 5 折 CV 中,1 折被遗漏并用作测试数据,而剩余的 4 折被汇集在一起并用作建模的训练数据。然后将训练的模型应用于前述的遗漏折叠(即测试数据)。这个过程反复进行,直到所有的折叠都有机会作为测试数据被忽略。因此,我们将构建 5 个模型(即,5 个折叠中的每一个都作为测试集被排除),其中 5 个模型中的每一个都包含相关的性能指标(我们将在接下来的部分中讨论)。最后,指标值基于从 5 个模型中计算出的平均性能。
在 N 等于数据样本数的情况下,我们称之为 留一交叉验证 。在这种类型的 CV 中,每个数据样本代表一个折叠。例如,如果 N 等于 30,则有 30 个折叠(每个折叠 1 个样本)。与任何其他 N 折叠 CV 一样,1 个折叠被排除作为测试集,而剩余的 29 个折叠用于构建模型。接下来,应用建立的模型对遗漏褶皱进行预测。如前所述,该过程迭代执行总共 30 次;并且计算 30 个模型的平均性能并用作 CV 性能度量。
模型结构
现在,有趣的部分来了,我们终于可以使用精心准备的数据来建立模型了。根据目标变量(通常称为 Y 变量)的数据类型(定性或定量),我们将建立一个分类(如果 Y 是定性的)或回归(如果 Y 是定量的)模型。
学习算法
机器学习算法可以大致分为三种类型:
- 监督学习 —是一个机器学习任务,它在输入 X 和输出 Y 变量之间建立数学关系。这样的 X , Y 对构成了用于建模的标记数据,以学习如何从输入预测输出。
- 无监督学习 —是一项机器学习任务,仅利用输入的 X 变量。这种 X 变量是学习算法在建模数据的固有结构时使用的未标记数据。
- 强化学习 —是一种机器学习任务,它决定下一步的行动,并通过试错学习来实现这一点,以努力实现回报的最大化。
超参数优化
超参数本质上是直接影响学习过程和预测性能的机器学习算法的参数。由于不存在适用于所有数据集的“一刀切”的超参数设置,因此需要执行 超参数优化 (也称为超参数调整或模型调整)。
我们以随机森林为例。在使用 randomForest R 包时,通常需要进行优化的两个常见超参数包括mtry
和ntree
参数(这对应于RandomForestClassifier()
中的n_estimators
和max_features
以及 scikit-learn Python 库中的RandomForestRegressor()
函数)。mtry
( max_features
)表示在每次分割时随机抽样作为候选的变量的数量,而ntree
( n_estimators
)表示要生长的树的数量。
另一种流行的机器学习算法是支持向量机。要优化的超参数是径向基函数(RBF)核的C
和gamma
参数(即,线性核只有C
参数;多项式内核的C
和exponential number
)。C
参数是限制过拟合的惩罚项,而gamma
参数控制 RBF 核的宽度。如上所述,通常进行调整以达到用于超参数的最佳值集,尽管如此,仍有研究致力于寻找C
和gamma
参数的良好初始值( Alvarsson 等人,2014 年)。
特征选择
顾名思义,特征选择就是从最初的大量特征中选择特征子集的过程。除了实现高度准确的模型之外,机器学习模型构建的一个最重要的方面是获得可操作的见解,为了实现这一点,能够从大量特征中选择重要特征的子集是很重要的。
特征选择的任务本身可以构成一个全新的研究领域,在这个领域中,人们正在努力设计新的算法和方法。在众多可用的特征选择算法中,一些经典方法基于模拟退火和遗传算法。除此之外,还有大量基于进化算法的方法(如粒子群优化、蚁群优化等)。)和随机方法(如蒙特卡洛)。
我们自己的研究小组也探索了在醛糖还原酶抑制剂的定量结构活性关系建模研究中使用蒙特卡罗模拟进行特征选择( Nantasenamat 等人,2014 )。在我们的课题 遗传算法搜索空间拼接粒子群算法作为通用优化器 ( 李等 2013 )中,我们还设计了一种基于结合两种流行的进化算法即遗传算法和粒子群算法的新的特征选择方法。
****遗传算法搜索空间拼接粒子群优化(GA-SSS-PSO)方法的原理示意图,如在 2 维中使用 Schwefel 函数所示。“原搜索空间(a)x∈[–500,0]在每个维度上以 2 的固定间隔拼接成子空间(图中一个维度等于一个横轴)。这产生了四个子空间(b–e ),其中每个维度上的 x 的范围是原来的一半。GA 的每个字符串编码一个子空间的索引。然后,GA 启发式地选择一个子空间(e ), PSO 在那里启动(粒子显示为红点)。PSO 搜索子空间的全局最小值,并且最佳粒子适应度被用作编码该子空间的索引的 GA 串的适应度。最后,遗传算法进行进化,选择一个新的子空间进行探索。重复整个过程,直到达到令人满意的误差水平。”(转载自化学计量学与智能实验室系统,第 128 卷,遗传算法搜索空间拼接粒子群优化作为通用优化器,第 153–159 页,版权(2013),经爱思唯尔许可)
机器学习任务
监督学习中两个常见的机器学习任务包括分类和回归。
分类
经过训练的分类模型将一组变量(定量或定性的)作为输入**,并预测输出类别标签(定性的)。下图显示了由不同颜色和标签表示的三个类别。每个彩色小球代表一个数据样本,每个样本**
****多类分类问题示意图。三类数据样本以二维方式显示。此图显示了数据样本的假设分布。这种可视化图可以通过执行 PCA 分析和显示前两个主成分(PCs)来创建;或者,也可以选择并可视化两个变量的简单散点图。(由 Chanin Nantasenamat 绘制)
示例数据集
以企鹅** 数据集(最近被提议作为大量使用的虹膜数据集的替代数据集)为例,其中我们将 定量 (喙长、喙深、鳍长以及体重)和 定性 (性别和岛屿)特征作为输入,这些特征唯一地描述了企鹅的特征,并将其分类为三个 物种 中的一个数据集由 344 行和 8 列组成。先前的分析显示,数据集包含 333 个完整病例,其中 19 个缺失值出现在 11 个不完整病例中。**
性能指标
我们如何知道我们的模型何时表现好或坏?答案是使用性能指标,评估分类性能的一些常用指标包括准确性(Ac)、敏感性(Sn)、特异性(Sp)和马修相关系数(MCC)。
计算精度的公式。
计算灵敏度的公式。
计算特异性的方程式。
计算马修斯相关系数的方程式。
其中 TP、TN、FP 和 FN 分别表示真阳性、真阴性、假阳性和假阴性的情况。应当注意,MCC 的范围为 1 至 1,MCC 为 1 表示最差预测,值为 1 表示最佳预测。此外,0 的 MCC 表示随机预测。
回归
简而言之,一个训练好的回归模型可以用下面的简单等式来最好地概括:
Y=f(X)
其中 Y 对应于定量的输出变量, X 指的是输入变量, f 指的是映射函数(从训练模型中获得),用于计算作为输入特征函数的输出值。回归示例的上述等式的本质是,如果已知 X ,则可以推导出 Y 。一旦计算出 Y (我们也可以称之为“预测”),可视化结果的一个流行方法是绘制一个实际值与预测值的简单散点图,如下所示。
****实际值与预测值的简单散点图。(由 Chanin Nantasenamat 绘制)
示例数据集
波士顿住房数据集是数据科学教程中常用的示例数据集。数据集由 506 行和 14 列组成。为简明起见,下面显示的是标题(显示变量的名称)加上数据集的前 4 行。
在 14 列中,前 13 个变量被用作输入变量,而中值房价(medv
)被用作输出变量。可以看出,所有 14 个变量都包含定量值,因此适合于回归分析。我还做了一个一步一步的 YouTube 视频,展示了如何用 Python 构建一个线性回归模型。
在视频中,我首先向您展示了如何读取波士顿住房数据集,将数据分离为 X 和 Y 矩阵,执行 80/20 数据分割,使用 80%子集构建线性回归模型,并应用训练好的模型对 20%子集进行预测。最后,显示了实际值与预测值medv
的性能指标和散点图。
****测试集(20%子集)的实际与预测 medv 值的散点图。剧情摘自 Jupyter 笔记本上的数据 GitHub 教授。
性能指标
执行回归模型的性能评估是为了评估拟合模型能够准确预测输入数据值的程度。
评估回归模型性能的常用度量是 决定系数 ®。
从等式中可以看出,R 基本上等于 1 减去残差平方和与总平方和之比。简而言之,它可以说是解释方差的相对度量。例如,如果 R = 0.6,则意味着该模型可以解释 60%的方差(即,即 60%的数据符合回归模型),而未解释的方差占剩余的 40%。
**另外,*以及 ***均方根误差【RMSE】也是预测残差或误差的常用度量。
从上式可以看出,MSE 顾名思义,通过取误差平方的平均值就可以很容易地计算出来。此外,MSE 的简单平方根产生 RMSE。
分类过程的直观解释
现在让我们再来看看分类模型的整个过程。以企鹅数据集为例,我们可以看到,企鹅可以由 4 个定量特征和 2 个定性特征来表征,这些特征然后被用作训练分类模型的输入。在训练模型时,需要考虑的一些问题包括:
- 用什么机器学习算法?
- 超参数优化应该探索什么样的搜索空间?
- 使用哪种数据拆分方案?80/20 分成还是 60/20/20 分成?还是 10 倍 CV?
一旦模型被训练,产生的模型可以用于对类别标签进行预测(即在我们的例子中是企鹅种类),其可以是三种企鹅种类中的一种:阿德利、下巴带或巴布亚企鹅。
除了仅执行分类建模,还可以执行主成分分析(PCA),这将仅利用 X(独立)变量来辨别数据的潜在结构,这样做将允许固有数据聚类的可视化(如下所示为假设图,其中聚类根据 3 个企鹅物种进行颜色编码)。
****建立分类模型的流程示意图。(由 Chanin Nantasenamat 绘制)
订阅我的邮件列表,获取我在数据科学方面的最佳更新(偶尔还有免费内容)!
关于我
我是泰国一所研究型大学的生物信息学副教授和数据挖掘和生物医学信息学负责人。在我下班后的时间里,我是一名 YouTuber(又名数据教授)制作关于数据科学的在线视频。在我制作的所有教程视频中,我也在 GitHub 上分享 Jupyter 笔记本(数据教授 GitHub 页面)。
数据科学、机器学习、生物信息学、研究和教学是我的激情所在。数据教授 YouTube…
www.youtube.com](https://www.youtube.com/dataprofessor?sub_confirmation=1)
在社交网络上与我联系
✅YouTube:http://youtube.com/dataprofessor/
♇网站:http://dataprofessor.org/(在建)
♇LinkedIn:https://www.linkedin.com/company/dataprofessor/
♇Twitter:https://twitter.com/thedataprof
♇Facebook:http://facebook.com/dataprofessor/
♇github:https://github.com/dataprofessor/
♇insta gram:**
如何通过 5 个步骤建立一个识别信用卡欺诈的机器学习模型
使用 Kaggle 数据集的实际建模指南
随着电子商务和数字交易的激增,身份欺诈也已上升到每年影响数百万人。2019 年,仅美国的欺诈损失估计约为 169 亿美元,其中很大一部分包括信用卡欺诈损失。
除了加强网络安全措施,金融机构越来越多地转向机器学习,以在欺诈交易发生时识别和拒绝欺诈交易,从而限制损失。
我在 Kaggle 上偶然发现了一个信用卡欺诈数据集,并建立了一个分类模型来预测欺诈交易。在本文中,我将通过 5 个步骤来构建一个有监督的机器学习模型。下面是五个步骤的概要:
- 探索性数据分析
- 列车测试分离
- 建模
- 超参数调谐
- 评估最终模型性能
一.探索性数据分析
当开始一个新的建模项目时,为了理解数据集,从 EDA 开始很重要。在这种情况下,来自 Kaggle 的信用卡欺诈数据集包含 284,807 行 31 列。这个特定的数据集不包含空值,但是请注意,在处理现实中的数据集时,情况可能并非如此。
我们的目标变量被命名为class
,它是 0 和 1 的二进制输出,1 代表欺诈交易,0 代表非欺诈交易。剩余的 30 列是我们将用来训练我们的模型的特征,其中绝大多数已经使用 PCA 进行了转换,因此是匿名的,而只有两列(time
和amount
)被标记。
目标变量
我们的数据集非常不平衡,因为数据集中的大多数行(99.8%)都是非欺诈性交易,并且有一个class = 0
。欺诈交易仅占数据集的约 0.2%。
这种类别不平衡问题在欺诈检测中很常见,因为欺诈(希望如此)很少发生。由于这种类别不平衡的问题,我们的模型可能没有足够的欺诈案例可供学习,我们将通过在建模阶段试验抽样方法来缓解这一问题。
国际银行特征
为了初步了解我们的功能,我发现 seaborn 的 pairplot 函数非常有用,特别是因为如果我们引入hue='class'
参数,我们可以通过目标变量绘制出分布。下图按标签显示了数据集中的前 10 个要素,橙色代表 0 或非欺诈性交易,蓝色代表 1 或欺诈性交易。
按目标变量值划分的数据集中前十个要素的成对图。
正如您在 pairplot 中看到的,一些特征的分布因标签而异,这表明这些特征可能对模型有用。
二。列车测试分离
由于数据集已经被清理,我们可以继续将数据集分成训练集和测试集。这是重要的一步,因为您无法根据模型训练的数据有效地评估模型的性能!
我使用 scikit-learn 的train_test_split
函数将我们数据集的 75%分割为训练集,剩下的 25%作为测试集。值得注意的是,我将stratify
参数设置为等于标签或train_test_split
函数中的y
,以确保在训练集和测试集中有我们标签的比例示例。否则,如果在我们的训练集中没有标签为 1 的例子,模型将不会学习欺诈交易是什么样的。同样,如果在我们的测试集中没有标签为 1 的例子,我们就不知道模型在遇到欺诈时会有多好的表现。
X_train, X_test, y_train, y_test = train_test_split(X, y, shuffle=True, stratify=y)
三。建模
由于我们的数据集是匿名的,所以没有特征工程要做,所以下一步是建模。
三. a .选择 ML 模型
有不同的分类模型可供选择,我尝试构建简单的模型来选择最佳的模型,稍后我们将调整超参数来进行优化。在这种情况下,我训练了一个逻辑回归模型、随机森林模型和 XGBoost 模型来比较它们的性能。
由于类别不平衡,在这种情况下,准确性不是一个有意义的度量。相反,我使用 AUC 作为评估指标,取 0 到 1 之间的值。AUC 测量模型将随机正例(class = 1
)排在随机负例之上的概率。
为了评估模型性能,我使用分层 K-Fold 交叉验证按类别标签分层采样,因为我们的数据集是高度不平衡的。使用模型 AUC 分数,我做了一个箱线图来比较模型的 AUC 分数范围。
不同分类模型的 AUC 分数箱线图
毫不奇怪,XGBoost 似乎是我们三个选择中的最佳模型。XGBoost 模型的 AUC 中值为 0.970,相比之下,随机森林模型为 0.944,逻辑回归模型为 0.911。因此,我选择 XGboost 作为我未来的模型。
比较取样方法
如前所述,我还试验了不同的采样技术来处理类不平衡问题。我试用了imblearn
的随机过采样、随机欠采样和 SMOTE 功能:
- 随机过采样使用替换对少数类进行采样,直到达到定义的阈值,我将其保留为默认值 0.5,因此我们的新数据集在 0 和 1 的标签之间有 50/50 的分割。
- 随机欠采样对多数类进行采样,默认情况下没有替换,但是您可以将其设置为使用替换进行采样,直到我们的数据集在 0 和 1 的标签之间有 50/50 的分割。
- *SMOTE(合成少数过采样技术)*是一种数据扩充方法,它从少数类中随机选择一个示例,找到其最近邻居的 k (通常为 k =5),选择一个随机邻居,并在该随机邻居和原始示例之间的特征空间中创建一个合成新示例。
我使用imblearn.pipeline
中的管道函数来避免泄漏,然后使用分层 K-Fold 交叉验证来比较 XGBoost 模型与上面列出的三种不同采样技术的性能。
使用不同抽样方法比较 XGBoost 模型的 AUC 分数
三种取样方法的中值 AUC 分数非常接近,在 0.974 到 0.976 之间。最终,我选择了 SMOTE,因为它在 AUC 分数上的范围更小。
四。超参数调谐
我选择用一个叫做hyperopt
的包来使用贝叶斯超参数调优,因为它比网格搜索或随机搜索等其他方法更快、更有见识。我想为我的 XGBoost 模型调优的超参数是:
max_depth
:一棵树的最大深度;介于 4 到 10 之间的值。min_child_weight
:构成叶节点或分支末端的样本的最小权重之和;1 到 20 之间的值。subsample
:每棵树随机抽样观察;0.5 到 0.9 之间的值。colsample_bytree
:每棵树的柱或特征的随机样本;0.5 到 0.9 之间的值。gamma
:分割一个节点所需的最小损耗减少,用于防止过拟合;介于 0 和 5 之间的值。eta
:learning _ rate;介于 0.01 和 0.3 之间的值。
为了使用 hyperopt,我首先用超参数和它们各自的界限来设置我的搜索空间,以进行搜索:
space = {
'max_depth': hp.quniform('max_depth', 4, 10, 2),
'min_child_weight': hp.quniform('min_child_weight', 5, 30, 2),
'gamma': hp.quniform('gamma', 0, 10, 2),
'subsample': hp.uniform('subsample', 0.5, 0.9),
'colsample_bytree': hp.uniform('colsample_bytree', 0.5, 0.9),
'eta': hp.uniform('eta', 0.01, 0.3),
'objective': 'binary:logistic',
'eval_metric': 'auc'
}
接下来,我定义了一个目标函数来最小化将从先前定义的搜索空间接收的值:
def objective(params):
params = {'max_depth': int(params['max_depth']),
'min_child_weight': int(params['min_child_weight']),
'gamma': params['gamma'],
'subsample': params['subsample'],
'colsample_bytree': params['colsample_bytree'],
'eta': params['eta'],
'objective': params['objective'],
'eval_metric': params['eval_metric']}
xgb_clf = XGBClassifier(num_boost_rounds=num_boost_rounds, early_stopping_rounds=early_stopping_rounds, **params)
best_score = cross_val_score(xgb_clf, X_train, y_train, scoring='roc_auc', cv=5, n_jobs=3).mean()
loss = 1 - best_score
return loss
下面列出了返回的最佳超参数,我们将用它来训练我们的最终模型!
best_params = {'colsample_bytree': 0.7,
'eta': 0.2,
'gamma': 1.5,
'max_depth': 10,
'min_child_weight': 6,
'subsample': 0.9}
动词 (verb 的缩写)最终模型性能评估
为了训练最终的模型,我使用了imblearn
的管道来避免泄漏。在管道中,我首先使用SMOTE
来扩充数据集,并包含更多的正面类供模型学习,然后用第四步中找到的最佳超参数训练一个 XGBoost 模型。
final_model = imblearn.pipeline.Pipeline([
('smote',SMOTE(random_state=1)),
('xgb', XGBClassifier(num_boost_rounds=1000,
early_stopping_rounds=10,
**best_params))])
退伍军人指标
以下是评估最终模型性能的一些指标:
AUC
最终模型的 AUC 得分为 0.991!这表明我们的最终模型能够很好地对订单欺诈风险进行排序。
分类报告
测试集分类报告
精度
True Positives/(True Positives + False Positives)
类别 0 的精度为 1,表示所有标记为属于类别 0 的项目确实是非欺诈性交易。类别 1 的精度是 0.86,这意味着 86%被标记为类别 1 的项目确实是欺诈交易。换句话说,最终的模型正确地预测了 100%的非欺诈交易和 86%的欺诈交易。
召回
True Positives/(True Positives + False Negatives)
类别 0 的召回为 1,这意味着所有非欺诈性交易都被标记为 1,即属于类别 0。第 1 类的召回率为 0.9,因此我们的最终模型将 90%的欺诈交易标记为属于第 1 类。这意味着最终的模型能够捕捉到 90%的欺诈交易。
F1 得分
2 * (Recall * Precision)/(Recall + Precision)
F1 分数是精确度和召回率的加权调和平均值。类别 0 的测试集上的最终模型预测的 F1 分数为 1,而类别 1 的最终模型预测的 F1 分数为 0.88。
V.b .功能重要性
为了理解该模型,查看 Shap 摘要和特征重要性图是有用的。不幸的是,该数据集中的大多数特征都被匿名化了,但是图中显示 v14、v4 和 v12 是最终模型中最重要的前 3 个特征。
最终 XGBoost 模型的特性重要度图
最终 XGBoost 模型测试集的 Shap 摘要图
最后的想法
在仅仅五个步骤中,我们构建了一个 XGBoost 模型,该模型能够基于该数据集中提供的 30 个特征来预测交易是否是欺诈性的。
我们的最终模型的 AUC 值为 0.991,高得令人难以置信!但是,值得注意的是,这是使用预先清理(和处理)的数据集完成的。事实上,特征工程是建模中至关重要的一步,但由于使用匿名数据集的限制,我们在这里没有机会这样做。
我希望这个使用真实数据集的动手建模练习能够帮助您更好地理解创建机器学习模型来预测欺诈背后的机制。我很想知道匿名化的特征是什么,尤其是最具预测性的特征。如果你对它们有什么想法,请在下面评论!
要查看代码,请查看我在 Github 上的 jupyter 笔记本文件。谢谢大家!
参考
脚注
[1]:标枪策略。2020 年身份欺诈研究:身份欺诈危机的起源。https://www . javelin strategy . com/coverage-area/2020-身份-欺诈-研究-起源-身份-欺诈-危机
如何尽快用 Python 构建地图仪表盘
使用几行 python 代码创建可共享地图仪表盘的一种非常简单、直接、容易且快速的方法。
机场仪表板
我们已经看到了使用 python 和 geoviews 库在地图上绘制一些数据是多么容易。然而,说实话,每次当你想改变图中显示内容的特定方面时,编辑代码行会非常令人沮丧。因此,构建一个地图仪表板通常更有用、更方便,即使是最没有经验的用户也可以通过点击几下鼠标来更改地图。幸运的是,有一种简单的方法可以实现这一点,使用 Panel 和 Param。
Panel 是一个开源的 Python 库,它允许您通过将用户定义的小部件连接到绘图、图像、表格或文本来创建自定义的交互式 web 应用程序和仪表盘。Panel 也可以与单独的Param项目一起使用,以完全声明的方式创建带有或不带有关联可视化的交互式可配置对象。
出于本教程的目的,我们将构建一个地图仪表板来可视化我国希腊和邻国土耳其最繁忙机场的客流量,以便进行比较,就像我们在 geoviews 的演示中所做的那样。积分模块。这一次,数据集更新了 2019 年的客运量,现在包含 2018 年和 2019 年的数据,以及伊斯坦布尔的新机场。
数据集可以在这里找到。
首先,我们需要导入我们将要使用的必要的库和模块。
import pandas as pd
import numpy as np
import geoviews as gv
from geoviews import opts, dim
import param, panel as pn
gv.extension('bokeh')
我们将前面提到的数据集读作airports_df
,看起来像这样。
机场 _df
现在让我们做一些准备工作。
我们创建 HoverTool 来指定当我们将鼠标悬停在这些点上时显示的信息。
from bokeh.models import HoverTool
tooltips = [('IATA', '[@IATA](http://twitter.com/IATA)'),
('Passengers', '[@passengers](http://twitter.com/passengers){0.000 a}m'),
('City', '[@city](http://twitter.com/city)'),
('Country', '[@country](http://twitter.com/country)'),
('Longitude', '$x'),
('Latitude', '$y'),
('Year', '[@year](http://twitter.com/year)')
]
hover = HoverTool(tooltips=tooltips)
我们创建了一个列表,其中包含所有的国家和值" All" 。所以在这种情况下,它包含值[‘GR ‘,’ TR ‘,’ All’]。我们将在仪表板参数“国家”中使用该列表。
country_list = airports_df['country'].unique().tolist()
country_list.append('All')
country_list
我们还创建了一个字典,其中包含我们使用的 tilemap 的主要选项,因此也包含整个地块的主要选项。
topts = dict(width=1100, height=680, xaxis=None, yaxis=None, \ show_grid=False)
现在让我们使用参数和面板构建仪表板。结构如下。首先,我们使用 Param 定义主对象,在这里我们设置仪表板选项的参数。在 Param 对象内部,我们还定义了绘图,在这里我们传递用户设置的参数。
现在,我们所要做的就是将上述对象传递给dashboard
,并使用 Panel 连续显示dashboard.param
和dashboard.plot
,即左边是参数,右边是绘图。
dashboard = Airports(name="Airports Dashboard")
DashboardPanel = pn.Row(dashboard.param, dashboard.plot)
最后,有两种方式来运行仪表板。或者在同一个 jupyter 笔记本中,或者在单独的标签中。
对于第一个选项,只需键入
DashboardPanel.servable()
而对于第二个选项,只需键入
DashboardPanel.show()
仪表板将如下所示。
当然,用户可以在仪表板上添加更多的参数和图表。这个只是为了演示的目的。
今天到此为止。希望你觉得有用。下次见!
点击这里,你可以随时在 LinkedIn 上找到我。
如何从头开始构建矩阵模块
如果您一直在为矩阵运算导入 Numpy,但不知道该模块是如何构建的,本文将向您展示如何构建您自己的矩阵模块
动机
Numpy 是一个有用的库,它使您能够轻松地创建矩阵和执行矩阵操作。如果你想知道用 Numpy 创建矩阵的技巧,请查看我的博客这里。但是如果您想创建一个具有 Numpy 库中没有的特性的 matrix 类呢?为了能够做到这一点,我们首先应该了解如何构建一个矩阵类,使我们能够创建一个矩阵,该矩阵具有矩阵的基本功能,如打印、矩阵加法、标量、元素或矩阵乘法,具有访问和设置条目。
本教程结束时,您应该已经具备创建自己的矩阵模块的基础。
Joshua Sortino 在 Unsplash 上拍摄的照片
为什么上课?
创建一个类允许创建一类对象的新实例。每个类实例可以有不同的属性和方法。因此,使用一个类将使我们能够创建一个具有矩阵属性和多种功能的实例。比如 A = [[2,1],[2,3]],B = [[0,1],[2,1]],A + B 应该给我们一个矩阵[[2,3],[4,4]]。
__method__
是私有方法。即使您不能直接调用私有方法,Python 中的类中的这些内置方法将让编译器知道当您执行特定的函数或操作时要访问哪个方法。你只需要为你的目标使用正确的方法。
构建一个矩阵类
我将从我们想要创建的开始,然后根据我们的目标找到创建类的方法。我建议您在添加更多方法时测试您的类,看看该类的行为是否如您所愿。
创建并打印矩阵对象
我们希望我们的班级达到的目标如下
>>> A = Matrix(dims=(3,3), fill=1.0)
>>> print( A )
------------- output -------------
| 1.000, 1.000, 1.000|
| 1.000, 1.000, 1.000|
| 1.000, 1.000, 1.000|
----------------------------------
因此,我们想要创建一个带有参数dims
和fill
的Matrix
对象。
class Matrix:
def __init__(self, dims, fill):
self.rows = dims[0]
self.cols = dims[1]
self.A = [[fill] * self.cols for i in range(self.rows)]
我们使用__init__
作为构造函数来初始化我们类的属性(行、列和矩阵 A)。行和列由矩阵的第一和第二维指定。用fill
作为值,用self.cols
和self.rows
作为矩阵的形状来创建矩阵 A。
我们还应该创建一个__str__
方法,使我们能够打印如上所示的可读格式。
def __str__(self):
m = len(self.A) # Get the first dimension
mtxStr = '' mtxStr += '------------- output -------------\n'
for i in range(m):
mtxStr += ('|' + ', '.join( map(lambda x:'{0:8.3f}'.format(x), self.A[i])) + '| \n') mtxStr += '----------------------------------' return mtxStr
缩放器和矩阵加法
目标:
标准矩阵-矩阵加法
>>> A = Matrix(dims=(3,3), fill=1.0)
>>> B = Matrix(dims=(3,3), fill=2.0)
>>> C = A + B
>>> print( C )
------------- output -------------
| 3.000, 3.000, 3.000|
| 3.000, 3.000, 3.000|
| 3.000, 3.000, 3.000|
----------------------------------
标量矩阵加法(逐点)
>>>A = Matrix(dims=(3,3), fill=1.0)
>>> C = A + 2.0
>>> print( C )
------------- output -------------
| 3.000, 3.000, 3.000|
| 3.000, 3.000, 3.000|
| 3.000, 3.000, 3.000|
----------------------------------
我们使用__add__
方法来执行正确的加法。
由于加法是可交换的,我们也希望能够在矩阵的右边进行加法。这可以通过调用左边的加法很容易地完成。
def __radd__(self, other):
return self.__add__(other)
逐点乘法
目标:
矩阵-矩阵逐点乘法
>>> A = Matrix(dims=(3,3), fill=1.0)
>>> B = Matrix(dims=(3,3), fill=2.0)
>>> C = A * B
>>> print( C )
------------- output -------------
| 2.000, 2.000, 2.000|
| 2.000, 2.000, 2.000|
| 2.000, 2.000, 2.000|
----------------------------------
标量矩阵逐点乘法
>>> A = Matrix(dims=(3,3), fill=1.0)
>>> C = 2.0 * A
>>> C = A * 2.0
>>> print( C )
------------- output -------------
| 2.000, 2.000, 2.000|
| 2.000, 2.000, 2.000|
| 2.000, 2.000, 2.000|
----------------------------------
使用__mul__
方法和__rmul__
方法进行左右点动
标准矩阵-矩阵乘法
目标:
>>> A = Matrix(dims=(3,3), fill=1.0)
>>> B = Matrix(dims=(3,3), fill=2.0)
>>> C = A @ B
>>> print( C )
------------- output -------------
| 6.000, 6.000, 6.000|
| 6.000, 6.000, 6.000|
| 6.000, 6.000, 6.000|
----------------------------------
矩阵乘法可以通过矩阵乘法专用的__matmul__
方法实现。
具有访问和设置条目的权限
目标:
>>> A = Matrix(dims=(3,3), fill=1.0)
>>> A[i,j]
>>> A[i,j] = 1.0
使用__setitem__
方法设置矩阵索引值,使用__getitem__
方法获取矩阵索引值。
把所有东西放在一起
创建和使用模块
创建完类矩阵,就该把它变成一个模块了。将包含该类的文本重命名为__init__.py
。创建一个名为Matrix
的文件夹。将main.py
和另一个名为linearAlgebra
的文件放在这个文件夹中。将__init__.py
文件放入linearAlgebra
文件中。
文件夹矩阵包含 main.py 和 linear 代数
文件夹 linearAlgebra 包含 init。巴拉圭
使用main.py
导入并使用我们的矩阵类。
结论
厉害!你已经学会了如何从头开始创建一个矩阵类。Python 类中还有其他方法可以让您为矩阵添加更多要素。因为你有创建一个类的基本知识,你可以创建你自己的符合你兴趣的 Matrix 版本。在这个 Github repo 中,您可以随意派生和使用本文的代码。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
创意是创造伟大产品的关键,但并不总是容易的。这个博客会给你策略去克服…
medium.com](https://medium.com/@khuyentran1476/how-to-produce-creative-product-without-hitting-your-head-to-the-wall-86eb93207058) [## 用美丽的声音抓取维基百科
关于如何使用 Beautiful Soup 的分步教程,这是一个用于 web 抓取的简单易用的 Python 库
towardsdatascience.com](/step-by-step-tutorial-web-scraping-wikipedia-with-beautifulsoup-48d7f2dfa52d) [## 用 Python 模块 Newspaper 和 NLTK 查找文章中的常用词
使用 newspaper3k 和 NLTK 从报纸中提取信息和发现见解的分步指南
towardsdatascience.com](/find-common-words-in-article-with-python-module-newspaper-and-nltk-8c7d6c75733) [## 用 Python 选择要投资的股票
您计划在未来 3 年投资几只股票,每只股票的每一美元都有不同的预期回报…
towardsdatascience.com](/choose-stocks-to-invest-with-python-584892e3ad22) [## 为您的数据科学项目提供 Numpy 技巧
创建数组、矩阵、执行矩阵运算、解决线性代数问题和常见数据科学的技巧…
medium.com](https://medium.com/@khuyentran1476/comprehensive-numpy-tutorials-for-beginners-8b88696bd3a2)
如何使用 Python Surprise 构建基于内存的推荐系统
使用 Python 中的 Surprise 库实现 kNN 风格推荐引擎的分步指南,从数据准备到预测。
啊,我们舒适的现代生活的极度痛苦,纸杯蛋糕看起来都很诱人,但你不可能都尝一尝,所以你应该吃哪一个?不管平台如何,您的选择通常几乎是无限的,但遗憾的是,作为消费者,您的资源并非如此。不要担心,推荐系统会来救你!
框架推荐系统是,我们有一组用户和一组项目,对于给定的用户,我们希望筛选出该用户可能喜欢的项目子集(评价高、购买、观看等。取决于确切的问题)。推荐系统无处不在,所有基于内容的非常成功的科技公司,如网飞、亚马逊、脸书,都非常依赖复杂的推荐系统来增加他们产品的消费。
在作为这篇文章基础的特定项目中,我专注于来自 boardgamegeek (截至 2020 年 3 月 31 日)的前 100 款游戏,处理我从该网站收集的 230 万个人用户评级。我主要使用了 surprise ,这是一个专注于推荐系统的 Python scikit 库,其结构非常类似 scikit-learn。
在这篇文章中,我们将讨论基于记忆的模型。我们将讨论如何导入和准备数据,使用什么相似性度量,如何实现三个内置的 kNN 模型,如何应用模型验证,最后进行预测。关于这个项目的细节,请参考我的 GitHub 库,我们将在这篇文章中涉及的工作大致对应于文件02_modelling_neighbours.ipynb
中的代码。
目录
推荐系统
数据导入
数据准备
模型参数
KNN 模型
测试
预测
交叉验证
综合考虑
可能的改进
推荐系统
让我们从推荐系统家族的快速总结开始,这样我们就可以放置最近邻模型。
有两条主要的推荐路线可供您选择:
- 基于内容的过滤模型基于项目的描述和用户的历史偏好,我们不需要其他用户的意见来推荐。
举例:用户喜欢 Vlaada Chvátil 设计的三款游戏,所以我们推荐他设计的第四款游戏。 - 协同过滤模型试图通过共同评级/拥有的项目来发现项目/用户之间的相似性。
例如:用户喜欢卡文纳,从我们对人群的分析中我们知道,那些喜欢卡文纳并知道奥丁盛宴的用户也倾向于喜欢第二款游戏,所以我们向用户推荐 FfO。
在这个项目中,我们将采用协同过滤。在协作过滤组中,两种最著名的不同方法是:
- 基于记忆的模型根据用户-项目评分对计算用户/项目之间的相似度。
- 基于模型的模型(不可否认,一个古怪的名字)使用某种机器学习算法来估计收视率。一个典型的例子是用户项目评分矩阵的奇异值分解。
在这篇文章中,我们主要关注基于记忆的模型。所以再一次,推荐系统内部是协同过滤,而协同过滤内部是基于记忆的模型。
数据导入
首先,我们需要安装surprise
包:
pip install scikit-surprise
完成后,您需要一个数据集,其中包含三个变量:用户 id、商品 id 和评级。这一点很重要,不要试图以用户项目评分矩阵的形式传递评分。从有 3 列且行数等于个人评分总数的数据开始。
如果你只是想练习,可以随意使用我的 GitHub 上的数据集。我个人将这些收集在一个csv
文件中,分为三列,但是您也可以使用其他数据结构,或者直接从pandas
T3 加载。
为了导入数据,您需要库中的这些类:
from surprise import Dataset, Reader
然后定义file_path
(显然改成你的文件了):
file_path = './data_input/games_100_summary_w_testuser.csv'
最后,我们创建一个Reader
对象,具有以下属性:
line_format
:确保订单与您的文件匹配。- 如果我们使用 csv,这是一个逗号。
rating_scale
:具有最低和最高可能范围的元组。获得正确的参数很重要,否则部分数据将被忽略。如果你使用的是二进制数据,例如,用户喜欢/不喜欢的物品,你可以放入(0,1)。
reader = Reader(
line_format='user item rating', sep=',', rating_scale = (1,10)
)
要导入数据,使用load_from_file
方法:
data = Dataset.load_from_file(file_path, reader=reader)
就这样,你应该有一种surprise
可以使用的数据格式!现在,您可以将数据想象成一个稀疏矩阵,其中用户/项目是行/列,个人评级是该矩阵中的元素。大多数单元格可能是空的,但这完全没问题。在我使用的数据中,我有 230 万的收视率,大约 23 万用户,这意味着用户平均对 100 个游戏中的 10 个进行评分,因此矩阵中大约 90%是空的。
数据准备
这就是surprise
第一次变得有点奇怪的地方,这个过程与scikit-learn
中的分类器模型不相似,在那里你有一个大矩阵,你可以将它分成训练/验证/测试集,以你认为合适的方式进行交叉验证,因为它们本质上仍然是相同类型的数据。不,在surprise
中,有三种不同的数据类,每一种都有其独特的用途:
Dataset
:用于直接或通过交叉验证迭代器分割成训练集和测试集。后者意味着如果您在交叉验证中将一个Dataset
作为参数传递,它将创建许多训练测试分割。Trainset
:作为模型fit
方法中的一个参数。Testset
:作为模型test
方法中的一个参数。
在我看来,surprise
是一个相对有据可查的库,但还是有些古怪。例如,Dataset
对象有一个方法construct_testset
,但是除了在以前版本的文档页面中找到代码之外,没有解释它做什么,或者参数应该是什么。
我坚持在我的项目中使用记录良好的方法。我们正在准备两种不同的方法,它们的目的将在下面的章节中变得更加清楚。
我们将使用model_selection
包中的以下内容:
from surprise.model_selection import train_test_split
首先,我们将数据分为trainset
和testset
,test_size
设置为 20%:
trainset, testset = train_test_split(data, test_size=0.2)
同样,它与分类器/回归模型的工作方式有点不同,testset
包含随机选择的用户/项目评级,而不是完整的用户/项目。一个用户可能在数据中有 10 个评级,其中 3 个现在被随机选择用于testset
,不用于拟合模型。当我第一次注意到这一点时,我发现这很奇怪,但不完全忽略某些用户是完全合理的。
第二种方法是使用完整的数据和交叉验证进行测试。在这种情况下,我们可以通过build_full_trainset
方法使用所有评级构建一个Trainset
对象:
trainsetfull = data.build_full_trainset()
您可以使用n_users
和n_items
方法获得项目/用户的数量(同样的方法适用于trainsetfull
,因为它们是同一类型的对象):
print('Number of users: ', trainset.n_users, '**\n**')
print('Number of items: ', trainset.n_items, '**\n**')
当 surprise 创建一个 Trainset 或 Testset 对象时,它获取raw_id
(您在导入的文件中使用的那些),并将它们转换成所谓的inner_id
(基本上是一系列整数,从 0 开始)。你可能需要追溯到最初的名字。以条目为例(您可以对用户使用相同的方法,只需在代码中用uid
替换iid
,要获得inner_iid
的列表,您可以使用all_items
方法。要从原始 id 转换到内部 id,您可以使用to_inner_iid
方法,然后使用to_raw_iid
转换回来。
如何保存内部和原始项目 id 列表的示例:
trainset_iids = list(trainset.all_items())
iid_converter = lambda x: trainset.to_raw_iid(x)
trainset_raw_iids = list(map(iid_converter, trainset_iids))
我们的数据准备工作到此结束,接下来是时候研究一些模型参数了!
模型参数
当我们使用 kNN 类型的推荐算法时,有两个超参数可以调整:k 参数(是的,与模型类型名称中的 k 相同)和相似性选项。
k 参数相当简单,类似于它在一般 k 近邻模型中的工作方式:它是我们希望算法考虑的类似项目的上限。例如,如果用户评价了 20 个游戏,但是我们将 k 设置为 10,当我们估计对新游戏的评价时,将只考虑 20 个游戏中最接近新游戏的 10 个游戏。您还可以设置 min_k ,如果用户没有足够的评分,将使用全局平均值进行评估。默认情况下,它是 1。
我们在上一段中提到了彼此靠近的项目,但是我们如何确定这个距离呢?第二个超参数相似性选项定义了计算方法。
先来看看sim_option
配置。该参数是一个字典,具有以下键:
shrinkage
:不需要基本的 kNN 型号,只在KNNBaseline
型号中出现。user_based
:基本上,当你想要估计相似性时,有两条不同的路线。您可以计算每个项目与其他项目的相似程度,也可以对用户进行同样的计算。对于我的项目,我使用了False
,考虑到我有 100 个项目和 23 万个用户。min_support
:相似度设为 0 的最小公共点数。例如:如果 min_support 为 10,并且有两个游戏,只有 9 个用户对这两个游戏进行了评级,则无论评级如何,这两个游戏的相似度都将为 0。我没有在我的项目中试验过这个,考虑到数据的范围,它似乎并不重要,所以我使用了默认的 1。name
:公式的类型,将在下面进一步讨论。
所有的相似性函数将返回一个介于 0 和 1 之间的数字给一个特定的 (i,j) 项目对。1 表示评级完全一致,0 表示两个项目之间没有联系。公式中, rᵤᵢ 是用户 u 对项目 i 的评分, μᵢ 是项目 i 的平均评分, Uᵢⱼ 是对项目 i 和 j 都进行了评分的用户集合。以下是surprise
相似性模块中的三个相似性指标:
cosine
:
MSD
:
其中 msd(i,j) 为:
pearson
:
没有明确的好与坏的选择,虽然我很少看到MSD
在例子中被使用,而且在我的数据中,pearson
和cosine
的表现确实明显更好。可以看出,pearson
公式基本上是cosine
公式的均值中心版本。
如何定义sim_option
参数的示例:
my_sim_option = {
'name':'MSD', 'user_based':False, min_support = 1
}
现在我们做了所有的准备工作,我们终于可以训练一些模型了。
KNN 模型
在surprise
中有三种基本 KNN 模型(我们不考虑第四种,KNNBaseline
在这篇文章中)。它们定义了如何在预测中估计用户 u 对项目 i 的评价 rᵤᵢ 。下面的公式主要使用我们在上一节中讨论过的符号,一些新符号: σᵢ 是项目 i 的标准偏差, Nᵤᵏ(i) 是来自用户 u 评价的最接近项目 i 的项目的最大 k 项目。
事不宜迟,公式如下:
KNNBasic
:
估计评级基本上是用户给相似项目的评级的加权平均值,由相似性加权。
KNNWithMeans
:
根据项目的平均评分调整KNNBasic
公式。
KNNWithZScore
:
更进一步,也根据评分的标准偏差进行调整。
在下面的例子中,我们用三个 my_ parameters 来拟合一个KNNWithMeans
模型。根据我的经验,如果你的项目的平均评分不同,你几乎不会想用KNNBasic
。您可以随意更改这些参数,所有三种型号都使用完全相同的参数。您可以在下面的代码中将KNNWithMeans
更改为KNNBasic
或KNNWithZScore
,其工作方式是一样的。
from surprise import KNNWithMeansmy_k = 15
my_min_k = 5
my_sim_option = {
'name':'pearson', 'user_based':False,
}algo = KNNWithMeans(
k = my_k, min_k = my_min_k, sim_option = my_sim_option
)algo.fit(trainset)
因此,我们的模型是合适的。从技术上讲,在这一点上发生的所有事情就是模型计算相似性矩阵和均值/标准差(如果您选择它的话)。
您可以使用 sim 方法请求相似性矩阵,如下所示:
algo.sim()
它将是一个numpy
数组格式。除非你想自己做一些预测,否则你可能不需要这个矩阵。
测试
一旦你训练好了你的模型,就该测试了,对吧?性能指标保存在surprise
的精度模块中。这里有四种度量标准(RMSE、FCP、MAE、MSE),但据我所知,行业标准是均方根误差,所以我们只使用这一个。这是我们今天最后的数学公式:
这个分数大致告诉你你的估计评分与实际评分的平均偏差。要获得测试分数,您所要做的就是使用您已经适应的算法上的测试方法创建一个predictions
对象:
from surprise import accuracypredictions = algo.test(testset)accuracy.rmse(predictions)
假设根据我的数据,测试数据中的 RMSE 分数是 1.2891。这意味着在 1 到 10 的范围内,估计的评分平均比实际评分高或低约 1.2891。不伟大,也不可怕。
交叉验证
在前两节中,我们遵循了一种非常直接的方法:我们留出一个测试数据,训练模型,然后测试其性能。但是,如果您进行多次运行,使用交叉验证来测试您的模型的性能以及它是否过度拟合可能是一个更好的主意。
如前所述,在surprise
中,测试和验证的工作方式有点不同。你只能在你的原始数据集对象上进行交叉验证,而不能为最终测试留出单独的测试部分,至少我找不到这样做的方法。所以我的过程基本上是这样的:
- 用不同的参数交叉验证了许多模型类型,
- 选择具有最低平均测试 RMSE 分数的配置,
- 整体训练那个模型
Dataset,
- 用它来预测。
让我们讨论一下cross_validate
方法的几个参数:
cv
是我们定义模型使用的折叠类型的地方,类似于scikit-learn
中的工作方式。我将通过输入一个整数来使用一个基本的 K 倍,你可以在文档中读到不同的 K 倍。n_jobs
:并行计算的折叠次数,如果你能腾出内存,将其设置为-1 将意味着所有的 CPU 并行工作。然而,当我在本地机器上这样做时,它崩溃了。我最终将它保留为默认的 1。
在下一部分中,我们将交叉验证的结果保存在results
变量中:
from surprise.model_selection import cross_validateresults = cross_validate(
algo = algo, data = data, measures=['RMSE'],
cv=5, return_train_measures=True
)
请注意,运行这个可能需要几分钟,测试需要一段时间,交叉验证需要 5 次。
一旦完成,您就可以挖掘results
变量来分析性能。例如,要得到平均测试 RMSE:
results['test_rmse'].mean()
很自然,你会这样做一段时间,尝试不同的模型,并以较低的RMSE
分数为目标。一旦你对性能感到满意,并创建了一个你满意的algo
模型,就该在整个数据集上训练算法了。你需要这一步,因为正如我提到的,你不能从交叉验证中做出预测。与上述未满车列相同的代码:
algo.fit(trainsetfull)
下一步,我们继续预测!
预言
最后,这就是我们做整个项目的原因,对吗?关于surprise
的两个重要注意事项可能与您的期望不符:
- 只能对已经在数据集中的用户进行预测。这也是为什么我认为在过程结束时根据整个数据训练模型是有意义的原因。
- 您不会直接从模型中获得输出列表,只需一次调用,您就可以请求一个特定用户对一个特定项目的估计评级。但是,有一个解决方法,我们稍后将回到这个问题。
为了进行一次预测,您可以使用原始 id,因此要获得 id 为161936
的游戏的TestUser1
(在数据中至少有min_k
个其他评分的用户)的估计评分,您需要您训练过的算法的predict
方法:
algo.predict(uid = 'TestUser1', iid = '161936')
predict
方法将返回这样一个字典:
Prediction(uid='TestUser1', iid='161936', r_ui=None, est=6.647051644687803, details={'actual_k': 4, 'was_impossible': False})
r_ui
是None
,因为用户没有该项目的实际评分。我们最终感兴趣的是第est
项,即估计评分,在我们的例子中,我们估计评分为 6.647。
这一切都很好,但是我们如何为用户获得前 N 个推荐呢?您可以在文档中找到详细的解决方案,我不打算在此复制,这些是基本步骤:
trainsetfull
上的列车模型。- 用
build_anti_testset
方法创建一个“反测试集”。这基本上是我们原始数据集的补充。因此,如果用户对 100 款游戏中的 15 款进行了评级,我们的testset
将包含用户没有评级的 85 款游戏。 - 用
test
方法在anti_testset
上运行预测(其结果与predict
方法的结构相似)。通过这一步,我们得到了数据中缺失的所有用户-项目评分对的估计评分。 - 对每个用户的估计评分进行排序,列出具有最高估计评分的 N 个项目。
把所有的放在一起
我认为将我们讨论的内容包含在一个连续的块中是有意义的。我们在下面的代码中采用的方法是交叉验证途径,因此我们使用交叉验证来测试性能,然后在整个数据集上拟合模型。
请注意,你很可能不会停止一个交叉验证,应该尝试不同的模型,直到你找到最好的。您可能还想促进前一节中链接的前 N 条建议。
从数据准备到惊喜预测
后续步骤
当谈到与surprise
合作时,你有许多额外的选择,我打算在未来的博客文章中探索它们。
一个相当明显的下一步是用SVD
和SVDpp
方法探索基于模型的方法。这些使用矩阵分解来估计评级。此外,您可能已经注意到,在这个场景中,我没有使用GridSearchCV
进行超参数调优。我发现使用cross_validate
就足够了,考虑到我们只有几个参数,但是当涉及到更复杂的模型时,你肯定想使用GridSearchCV
。
*更新:*如果你对实现基于模型的方法感兴趣,可以看看我系列的下一篇文章 如何使用 Python Surprise 构建基于模型的推荐系统。
另一个有趣的领域是预测。有时,您只想在一些用户评级上运行您的模型,而不将它们集成到底层数据库中。例如,我从 boardgamegeek 收集数据,当我只想快速向某人展示模型时,我不希望那些评级与“官方”评级混在一起。为一个用户重新运行整个模型也是没有效率的。现在,对于我们讨论的三个 KNN 模型,完全可以仅从相似性矩阵、平均值和标准偏差进行预测。我会在以后的文章中描述这个过程,或者你可以看看我的 GitHub 中的recomm_func.py
脚本。
*更新:*如果你想了解更多关于这个自定义预测代码,看看 我的 Python 代码灵活推荐 。
参考
协同过滤(CF)是推荐系统使用的一种技术。协同过滤有两个含义,一个是…
en.wikipedia.org](https://en.wikipedia.org/wiki/Collaborative_filtering)
接下来的三个链接是scikit-surprise
的不同文档,我主要用的是第一个。
[## 欢迎使用“惊喜”文档!-惊喜 1 文档
如果您对惊喜感到陌生,我们邀请您看一看入门指南,在那里您会找到一系列…
surprise.readthedocs.io](https://surprise.readthedocs.io/en/stable/index.html) [## scikit-惊喜
Surprise 是一个 Python scikit,它构建并分析处理显式评级数据的推荐系统。惊喜…
pypi.org](https://pypi.org/project/scikit-surprise/) [## 主页
Surprise 是一个 Python scikit,它构建并分析处理显式评级数据的推荐系统。惊喜…
surpriselib.com](http://surpriselib.com/)
如何使用 Python Surprise 构建基于模型的推荐系统
使用 Python 中的 Surprise 库实现潜在因素推荐引擎的分步指南。
德瓦·威廉姆森在 Unsplash 上的照片
这篇文章是我的 Python 惊喜推荐系列的最后一篇,在这篇文章中,我展示了我在 boardgame 推荐引擎项目中使用的技术。(整个项目看我的 GitHub 回购。)
该系列的前几篇文章:
第 1 部分 : 如何使用 Python Surprise 构建基于内存的推荐系统:如果您不熟悉这个主题,尤其是数据导入和数据准备步骤,我建议您先阅读这篇文章,因为它们对于基于内存和基于模型的方法是相同的。
第 2 部分 : 我的灵活推荐 Python 代码:这篇文章包含了我为推荐框架编写的额外代码,它使人们无需重新训练整个模型就能创建预测。然而,这种方法只适用于 KNN 风格的简单模型。
我还愚蠢地用纸杯蛋糕做了一个类比,以封面图片为依据。在第一个帖子中,我们有许多纸杯蛋糕,不知道如何选择,在第二个帖子中,我们有一个奇怪的新来的纸杯蛋糕,努力整合它,现在…我想现在纸杯蛋糕排成一行,代表矩阵分解。这是我能想到的最好的了。
在这篇文章中,我们将讨论潜在因素模型如何工作,如何通过超参数调整来训练这样一个模型,以及我们可以从结果中得出什么其他结论。
基于模型的推荐系统
快速回顾一下我们的现状。在推荐系统中,有一组被称为协同过滤的模型,它试图根据记录的用户项目偏好或评级来发现用户之间或项目之间的相似之处。在我之前的文章中,我们讨论了协作系统的一个子群,称为基于记忆的模型。它们被称为基于内存的,因为算法并不复杂,但需要大量内存来跟踪结果。
在这篇文章中,我们将讨论协作过滤模型的另一个子群:基于模型的模型(这是一个相当愚蠢的名字)。与基于记忆的方法相反,这种方法使用某种机器学习算法。这一组中有许多不同的变体,我们将集中讨论奇异值分解方法。
在惊喜中,有三种这样的模式:SVD
、SVDpp
、NMF
,其中我只打算讨论SVD
。NMF
是一个简化版本,忽略了用户和项目的偏见。SVDpp
增加了一个非常酷的功能,你还可以单独跟踪用户是否对项目进行了评级,这当然也应该是相关的信息,但我发现它并没有提高我的效率,同时增加了大量的计算时间。
数学公式
在 SVD 模型中,用户 u 对项目 i 的估计评分计算如下:
其中 μ 是总体平均评级,其他所有参数通过梯度下降法从模型中计算得出。因此,该模型将尝试在所有已知评级上拟合该估计评级,最小化 MSE,并返回最接近的拟合。
bᵤ 和 bᵢ 是标量,它们代表用户 u 或项目 i 的偏向。例如,用户 u 倾向于 bᵤ 偏离大平均评级。这些偏差可以在拟合模型时关闭,这基本上就是NMF
模型。
pᵤ 和 qᵢ 是向量,它们的长度是模型的超参数 n 。它们是模型的实际矩阵分解部分,这就是神奇之处。每个用户和物品将由他们的向量来表示,向量试图用 n 个数字来捕捉他们的本质。我们通过乘以项目-用户对(当然,加上平均值和偏差)来获得评级。
把这 n 个维度看作是人类可以理解的东西可能很有诱惑力。例如,如果我们处理棋盘游戏,第一个维度可以衡量规则手册有多复杂。现在,一个高度重视复杂性的用户(这意味着他们在qᵢ[1)会给一个高复杂性的游戏一个高评级(这意味着高 pᵤ [ 1 )。然而,根据我的经验,这种情况很少发生,很难将意义与模型中的各个坐标相匹配。
在惊讶中训练模型
假设您已经导入并设置了数据库(同样,如果您不确定如何做,请参考我的上一篇文章),使用SVD
类似于在 Surprise 中使用其他模型。首先,您需要导入模型:
*from surprise import SVD*
然后,您可以在列车组上安装模型,并使用RMSE
分数(代表均方根误差,越低越好)测试模型性能:
*SVD_model = SVD()
SVD_model.fit(trainset)
predictions = SVD_model.test(testset)
accuracy.rmse(predictions)*
与基于内存的模型类似,为了预测特定用户的评级,您可以使用 predict 方法,但该用户需要在您的数据库中:
*SVD_model.predict(uid = 'TestUser1', iid = '161936')*
分析模型
如您所见,拟合 SVD 模型很简单,但是分析结果有点复杂。
使用一个SVD
对象的pu
、qi
、bu
和bi
方法,可以从数学公式中得到相应的值。在我的项目中,我发现qi
是最有趣的:在已经拟合的SVD
模型上调用qi
方法将返回一个二维数组,其中高度是项目的数量,宽度是模型的n_factor
参数(我们将在下一节讨论这些参数)。每行代表一个有n_factor
个因素的项目,这些是模型发现的所谓潜在因素,它们代表评级计算中的项目。
正如我之前提到的,试图在这些坐标中找到容易理解的意义是很诱人的,但以我的经验来看,这并不会真的发生。然而,我认为一个真正有趣的方法是将这些潜在的因素作为聚类分析的基础,看看是否能从中发现什么有趣的东西。您可以使用n_factor
潜在因素作为特征,并基于它们计算项目的距离,就像您在任何常规聚类分析中所做的那样。
我们的超参数是什么?
任何机器学习过程的一个重要部分是调整超参数。在本节中,我们将惊奇地看一下SVD
参数。
在我们开始之前,请注意,对于我的项目,默认参数下的 RMSE 分数是 1.332,在我的 GCP 虚拟机运行了几个小时之后,我设法将 RMSE 分数降到了 1.3210。这不是一个很大的改进…可能只是我的数据库,可能是因为默认参数在惊奇中被有效地设置。尽管如此,我认为考虑超参数仍然很重要。
我优化了四个超参数:
n_factors
:我们在上一节已经简单的提到了这一点,这个参数决定了你的 pᵤ 和 qᵢ 向量的大小。这决定了模型将试图找到多少潜在因素。该数字越高,模型的能力越强,但也有更高的过度拟合几率。n_epochs
:该因子决定梯度下降计算重复的次数。增加该数值会使预测更加准确,但需要更长的计算时间。lr_all
:所有参数的学习率系数。这些是模型将用于最小化成本函数的步长,请参见惊喜文档中的更多信息。reg_all
:所有参数的正则化因子。Surprise 使用 L2 正则化,这大致意味着它将尝试最小化参数平方值之间的差异。(参数为所有 bᵤ、bᵢ、pᵤ 和 qᵢ 。)
还有许多其他参数可以使用,其中大多数是不同的学习率或调整参数设置。从技术上讲,您可以为每四种模型参数设置不同的学习率或正则化,而…_all
参数涵盖了所有这些参数。我认为没有必要详述这些细节。
超参数调谐
我们将使用GridSearchCV
来调整超参数。它的工作方式很像它在 scikit-learn 中的对应物,顾名思义,它将使用交叉验证在超参数网格上搜索所有可能的组合。
首先,我们需要一个字典,其中的键是超参数名称,值是您想要检查的不同项目的列表:
*param_grid = {
'n_factors':[5, 10,20],
'n_epochs': [5, 10, 20],
'lr_all': [0.002, 0.005],
'reg_all': [0.4, 0.6]}*
你必须小心设置这些参数,因为每个可能的组合都会被检查。在我们的例子中,有 3 * 3 * 2 * 2 = 36 种不同的组合,对于其中的每一种,模型将运行多次,这取决于您选择的交叉验证。我将cv
参数设为 5 倍,这意味着总共 36 * 5 = 180 次模型运行。
这可能需要很长时间,现在可能是开始考虑使用云计算的时候了。我最近写了一篇关于如何在 Google 云平台上快速设置免费虚拟机的帖子。
一旦你有了你的参数网格,你可以像这样设置GridSearchCV
对象:
*gs_model = GridSearchCV(
algo_class = SVD,
param_grid = param_grid,
n_jobs = -1,
joblib_verbose = 5)*
第一个参数algo_class
是您想要使用的模型类型。n_jobs
= -1 简单地告诉模型它可以使用所有可用的处理器,当您有一个可并行化的操作时,这是非常理想的。您可能想要更改的另一个参数是cv
,我只是将其保留为默认值,它进行 5 重交叉验证。
然后你可以简单地拟合数据:
*gs_model.fit(data)*
请再次注意,surprise 处理数据库的方式有点不同,您只能将您的GridSearchCV
放在整个数据集上,不能将其分为训练和测试。
最后,您可以从参数网格中获得导致最佳RMSE
分数的参数列表:
*gs_model.best_params*
我的过程
我和GridSearchCV
一起工作的过程如下:
- 在整个数据上拟合一个
GridSearchCV
模型,其参数网格覆盖范围很广 - 计算交叉验证的
RMSE
分数 - 对不同的参数网格重复该过程,如果最佳参数似乎在中间,则向下钻取到较低的级别,或者如果最佳参数似乎在前一参数网格的边界,则探索较高/较低的参数
- 一旦
RMSE
得分下降最小,保存最佳超参数并在接下来的步骤中用于SVD
模型 - 因为我想将结果与我之前的 KNN 型模型进行比较,所以我在
trainset
上运行了SVD
模型,并在testset
上计算了测试RMSE
分数
另一种方法是简单地依赖 GSCV 分数,并在整个训练集中仅拟合最终模型一次。
结论
我的推荐系统项目系列到此结束。
与所有这些不同的方法一起工作是很有趣的,尤其是他们的表现是如此的接近。我花了很多时间来确定最好的模型,但事实是,即使是最简单的 KNN 模型表现也相对较好。
我认为这部分是因为我的数据远没有推荐系统通常的那么稀疏。10%的可能用户项目评级被填充,这被认为是非常高的比率。想象一下,如果人们平均购买了亚马逊上所有产品的 10%!这个比例很高是因为我只关注了有史以来最受欢迎的 100 款桌游,这些游戏自然都很受欢迎。作为一个额外的奖励,平均评分非常接近和高,因为这些都是普遍认为是好游戏。
参考
惊喜文档:
*[## 欢迎使用“惊喜”文档!-惊喜 1 文档
如果您对惊喜感到陌生,我们邀请您看一看入门指南,在那里您会找到一系列…
surprise.readthedocs.io](https://surprise.readthedocs.io/en/stable/index.html)*
我在该系列中的前几篇文章:
* [## 如何使用 Python Surprise 构建基于内存的推荐系统
使用 Python 中的 Surprise 库实现 kNN 风格推荐引擎的分步指南,来自 data…
towardsdatascience.com](/how-to-build-a-memory-based-recommendation-system-using-python-surprise-55f3257b2cf4) [## 我的灵活推荐 Python 代码
我的额外定制 Python 代码,使您能够基于惊喜库运行更灵活的推荐…
towardsdatascience.com](/my-python-code-for-flexible-recommendations-b4d838e9e0e0)*
如何建立电影推荐系统
Noom Peerapong 在 Unsplash 上拍摄的照片
动手教程,机器学习
构建简单推荐系统的逐步指南
你有没有想过 YouTube 是怎么推荐内容的,或者脸书是怎么推荐你的,新朋友?也许你已经注意到了 LinkedIn connections 的类似推荐,或者当你在浏览时亚马逊将如何推荐类似的产品。所有这些推荐都是通过推荐系统的实现而成为可能的。
推荐系统包括一类可以向用户建议“相关”项目的技术和算法。他们通过包括矩阵分解在内的多种技术,根据过去的数据预测未来的行为。
在这篇文章中,我将看看为什么我们需要推荐系统和不同类型的在线用户。然后,我将向您展示如何使用开源数据集构建您自己的电影推荐系统。
内容
- 为什么我们需要推荐系统?
- 推荐系统的类型
A)基于内容的电影推荐系统
B)协同过滤电影推荐系统 - 数据集
- 电影推荐系统的设计
- 实现
步骤 1:基于矩阵分解的算法
步骤 2:创建手工制作的特征
步骤 3:为我们的电影推荐系统创建最终模型 - 性能指标
- 摘要
为什么我们需要推荐系统?
我们现在生活在一些人称之为“富足时代”的时代。对于任何给定的产品,有时有成千上万的选项可供选择。想想上面的例子:流媒体视频、社交网络、网上购物;这样的例子不胜枚举。推荐系统有助于个性化平台,帮助用户找到他们喜欢的东西。
最简单易行的方法就是推荐最受欢迎的单品。然而,要真正通过个性化推荐提升用户体验,我们需要专门的推荐系统。
从商业角度来看,用户在平台上找到的相关产品越多,他们的参与度就越高。这通常会增加平台本身的收入。各种消息来源称,多达 35-40%的科技巨头的收入仅来自推荐。
现在我们已经了解了推荐系统的重要性,让我们来看看推荐系统的类型,然后用开源数据构建我们自己的推荐系统!
推荐系统的类型
推荐系统中的机器学习算法通常分为两类:基于内容的系统和协同过滤系统。现代推荐系统结合了这两种方法。
让我们看看他们是如何使用电影推荐系统作为基础的。
a)基于内容的电影推荐系统
基于内容的方法是基于电影属性的相似性。使用这种类型的推荐系统,如果用户观看一部电影,相似的电影被推荐。例如,如果用户观看亚当·桑德勒主演的喜剧电影,系统将向他们推荐相同类型或相同演员主演的电影,或者两者都推荐。考虑到这一点,构建基于内容的推荐系统的输入是电影属性。
图 1:基于内容的推荐系统概述(图片由作者创建)
b)协同过滤电影推荐系统
通过协同过滤,该系统基于用户和电影之间过去的交互。考虑到这一点,协同过滤系统的输入由用户与他们观看的电影的交互的过去数据组成。
例如,如果用户 A 观看 M1、M2 和 M3,而用户 B 观看 M1、M3、M4,我们向相似的用户 c 推荐 M1 和 M3。为了更清楚地参考,您可以在下图中看到这种情况。
图 2:协同过滤电影推荐系统的示例(由作者创建的图像)
这些数据存储在一个名为用户-电影交互矩阵的矩阵中,其中行是用户,列是电影。
现在,让我们使用上面讨论的概念来实现我们自己的电影推荐系统。
数据集
对于我们自己的系统,我们将使用来自 GroupLens 的开源 MovieLens 数据集。这个数据集包含各种电影和用户的 100K 个数据点。
我们将使用三列数据:
- 使用者辩证码
- 电影 Id
- 等级
您可以在下面的图 3 中看到数据的快照:
图 3:数据快照(图片由作者提供)
设计我们的电影推荐系统
为了获得对用户的推荐,我们将预测他们对尚未观看的电影的评分。然后基于这些预测的评级,电影被编入索引并被推荐给用户。
为此,我们将使用电影和用户评级的过去记录来预测他们未来的评级。在这一点上,值得一提的是,在现实世界中,我们很可能会遇到没有历史的新用户或电影。这种情况被称为冷启动问题。
让我们简单看看如何解决冷启动问题。
冷启动问题
冷启动问题可以通过基于元信息的建议来处理,例如:
- 对于新用户,我们可以使用他们的位置、年龄、性别、浏览器和用户设备来预测推荐。
- 对于新电影,我们可以用流派、演员、剧组来推荐给目标用户。
履行
对于我们的推荐系统,我们将使用上面提到的两种技术:基于内容的和协同过滤。对于我们的基于内容的方法,为了找到电影之间的相似性,我们将使用余弦相似性函数。对于我们的协作过滤方法,我们将使用矩阵分解技术。
第一步是创建一个基于矩阵分解的模型。我们将使用这个模型的输出和一些手工制作的功能来为最终的模型提供输入。基本流程如下所示:
- 步骤 1:构建基于矩阵分解的模型
- 步骤 2:创建手工制作的特征
- 步骤 3:实现最终模型
我们将在下面详细讨论这些步骤。
步骤 1:基于矩阵分解的算法
矩阵分解是一类用于推荐系统的协同过滤算法。由于非常有效,这一系列方法在网飞奖挑战赛期间广为人知。
矩阵分解算法通过将用户-电影交互矩阵分解成两个较低维度的矩形矩阵(比如 U 和 m)的乘积来工作。分解以这样的方式完成,使得乘积产生与用户-电影交互矩阵几乎相似的值。这里 U 代表用户矩阵,M 代表电影矩阵,n 代表用户数,M 代表电影数。
用户矩阵的每一行代表一个用户,电影矩阵的每一列代表一部电影。
图 4:矩阵分解(图片由作者创建)
一旦我们获得了 U 和 M 矩阵,基于用户-电影交互矩阵中的非空单元,我们执行 U 和 M 的乘积,并预测用户-电影交互矩阵中非空单元的值。
为了实现矩阵分解,我们使用了一个简单的名为 Surprise 的 Python 库,用于构建和测试推荐系统。数据帧被转换成训练集,一种被惊喜库接受的数据集格式。
from surprise import SVD
import numpy as np
import surprisefrom surprise import Reader, Dataset
# It is to specify how to read the data frame.
reader = Reader(rating_scale=(1,5))# create the traindata from the data frame
train_data_mf = Dataset.load_from_df(train_data[['userId', 'movieId', 'rating']], reader)# build the train set from traindata.
#It is of dataset format from surprise library
trainset = train_data_mf.build_full_trainset()svd = SVD(n_factors=100, biased=True, random_state=15, verbose=True)
svd.fit(trainset)
现在模型准备好了。我们将存储这些预测,并作为附加特征传递给最终模型。这将有助于我们将协同过滤整合到我们的系统中。
#getting predictions of train set
train_preds = svd.test(trainset.build_testset())
train_pred_mf = np.array([pred.est for pred in train_preds])
请注意,我们还必须对测试数据执行上述步骤。
步骤 2:创建手工特征
让我们将数据帧格式的数据转换成用户-电影交互矩阵。在这类问题中使用的矩阵通常是稀疏的,因为用户很可能只对几部电影进行评级。
数据的稀疏矩阵格式(也称为 CSR 格式)的优点如下:
- 高效的算术运算:CSR + CSR,CSR * CSR 等。
- 高效的行切片
- 快速矩阵向量乘积
scipy.sparse.csr_matrix 是一个实用函数,可以有效地将数据帧转换为稀疏矩阵。
# Creating a sparse matrix
train_sparse_matrix = sparse.csr_matrix((train_data.rating.values, (train_data.userId.values, train_data.movieId.values)))
‘train _ sparse _ matrix’是 train_data 数据帧的稀疏矩阵表示。
我们将使用此稀疏矩阵创建 3 组特征:
- 代表全球平均值的特征
- 代表前五名相似用户的功能
- 代表前五部类似电影的特写
让我们更详细地了解一下如何准备每一项。
1.代表全球平均值的特征
我们将采用的三个全球平均值是:
- 所有用户对所有电影的平均评分
- 所有用户对特定电影的平均评级
- 特定用户给出的所有电影的平均评级
train_averages = dict()
# get the global average of ratings in our train set.
train_global_average = train_sparse_matrix.sum()/train_sparse_matrix.count_nonzero()
train_averages['global'] = train_global_average
train_averagesOutput: {‘global’: 3.5199769425298757}Next, let’s create a function which takes the sparse matrix as input and gives the average ratings of a movie given by all users, and the average rating of all movies given by a single user.# get the user averages in dictionary (key: user_id/movie_id, value: avg rating)
def get_average_ratings(sparse_matrix, of_users):# average ratings of user/axes
ax = 1 if of_users else 0 # 1 - User axes,0 - Movie axes# ".A1" is for converting Column_Matrix to 1-D numpy array
sum_of_ratings = sparse_matrix.sum(axis=ax).A1# Boolean matrix of ratings ( whether a user rated that movie or not)
is_rated = sparse_matrix!=0# no of ratings that each user OR movie..
no_of_ratings = is_rated.sum(axis=ax).A1# max_user and max_movie ids in sparse matrix
u,m = sparse_matrix.shape
# create a dictionary of users and their average ratings..
average_ratings = { i : sum_of_ratings[i]/no_of_ratings[i]for i in range(u if of_users else m)
if no_of_ratings[i] !=0}#return that dictionary of average ratings
return average_ratings
平均评级由用户给出:
train_averages['user'] = get_average_ratings(train_sparse_matrix, of_users=True)
电影的平均评级如下:
train_averages['movie'] = get_average_ratings(train_sparse_matrix, of_users=False)
2.代表前 5 名相似用户的功能
在这组功能中,我们将创建对特定电影进行评级的前 5 名相似用户。使用用户之间的余弦相似度来计算相似度。
# compute the similar Users of the "user"user_sim = cosine_similarity(train_sparse_matrix[user], train_sparse_matrix).ravel()
top_sim_users = user_sim.argsort()[::-1][1:] # we are ignoring 'The User' from its similar users.# get the ratings of most similar users for this movie
top_ratings = train_sparse_matrix[top_sim_users, movie].toarray().ravel()# we will make it's length "5" by adding movie averages to
top_sim_users_ratings = list(top_ratings[top_ratings != 0][:5])
top_sim_users_ratings.extend([train_averages['movie'][movie]]*(5 -len(top_sim_users_ratings)))
3.代表前 5 部相似电影的特征
在这组特征中,我们获得了由特定用户评价的前 5 部相似电影。使用电影之间的余弦相似度来计算该相似度。
# compute the similar movies of the "movie"
movie_sim = cosine_similarity(train_sparse_matrix[:,movie].T,
train_sparse_matrix.T).ravel()
top_sim_movies = movie_sim.argsort()[::-1][1:]# we are ignoring 'The User' from its similar users.
# get the ratings of most similar movie rated by this user
top_ratings = train_sparse_matrix[user, top_sim_movies].toarray().ravel()# we will make it's length "5" by adding user averages to
top_sim_movies_ratings = list(top_ratings[top_ratings != 0][:5])
top_sim_movies_ratings.extend([train_averages['user'][user]]*(5-len(top_sim_movies_ratings)))
我们为每个电影用户对添加所有这些特征,并创建一个数据框。图 5 是我们的数据框的快照。
图 5:包含 13 个特性的数据概述
以下是其内容的更详细的分类:
- GAvg:所有评分的平均评分
- 这部电影的相似用户评分:sur1、sur2、sur3、sur4、sur5(对这部电影评分的前 5 名相似用户)
- 此用户评价的相似电影:smr1、smr2、smr3、smr4、smr5(用户评价的前 5 部相似电影)
- 用户平均评分
- MAvg:这部电影的平均评分
- 分级:此用户对此电影的分级。
一旦我们准备好这 13 个特性,我们将添加矩阵分解输出作为第 14 个特性。在图 6 中,您可以看到添加步骤 1 的输出后的数据快照。
图 6:具有 13 个特征和矩阵分解输出的数据概述(图片由作者提供)
最后一列名为 mf_svd,是包含步骤 1 中执行的模型输出的附加列。
步骤 3:为我们的电影推荐系统创建一个最终模型
为了创建我们的最终模型,让我们使用 XGBoost ,一个优化的分布式梯度增强库。
# prepare train data
x_train = final_data.drop(['user', 'movie','rating'], axis=1)
y_train = final_data['rating']
# initialize XGBoost model
xgb_model = xgb.XGBRegressor(silent=False, n_jobs=13,random_state=15,n_estimators=100)
# fit the model
xgb_model.fit(x_train, y_train, eval_metric = 'rmse')
性能指标
评价推荐系统的性能主要有两种方法:均方根误差(RMSE)和平均绝对百分比误差(MAPE)。RMSE 衡量的是平方损失,而 MAPE 衡量的是绝对损失。较低的值意味着较低的错误率,因此性能更好。
两者都很好,因为它们允许简单的解释。让我们来看看它们分别是什么:
均方根误差(RMSE)
RMSE 是误差平方平均值的平方根,由下式给出。
其中:
r 是实际收视率,
r^是预测收视率,
N 是预测总数
平均绝对百分比误差(MAPE)
MAPE 用百分比来衡量误差。它由下面的公式给出:
其中:
r 是实际收视率,
r^是预测收视率,
N 是预测总数
#dictionaries for storing train and test results
test_results = dict()
# from the trained model, get the predictions
y_est_pred = xgb_model.predict(x_test)
# get the rmse and mape of train data
rmse = np.sqrt(np.mean([ (y_test.values[i] - y_test_pred[i])**2 for i in
range(len(y_test_pred)) ]))
mape = np.mean(np.abs( (y_test.values- y_test_pred)/y_true.values )) * 100
# store the results in train_results dictionary
test_results = {'rmse': rmse_test, 'mape' : mape_test, 'predictions' : y_test_pred}
我们的模型在看不见的测试数据上得到 0.67 的 RMSE 和 19.86 的 MAPE,这是一个很好的模型。小于 2 的 RMSE 值被认为是好的,小于 25 的 MAPE 是极好的。也就是说,这种模式可以通过添加功能来进一步增强,这些功能将根据位置或流派的最佳选择进行推荐。我们还可以通过 A/B 测试实时测试各种模型的功效。
摘要
在本文中,我们学习了推荐系统的重要性,正在实现的推荐系统的类型,以及如何使用矩阵分解来增强系统。然后,我们构建了一个电影推荐系统,它考虑了用户-用户相似性、电影-电影相似性、全局平均值和矩阵分解。这些概念可以应用于任何其他用户-项目交互系统。
感谢阅读!如果你想自己试验这个自定义数据集,你可以在 GroupLens 下载带注释的数据,并在 Github 查看我的代码。
谢谢你的阅读。本文原载此处。我也将在未来写更多初学者友好的帖子。请在媒体上关注我,以便了解他们。我欢迎反馈,可以通过 Twitter ramya_vidiyala 和 LinkedIn RamyaVidiyala 联系我。快乐学习!
如何用 std::async 在 C++中构建多线程管道
今天我们要弄清楚如何在 C++11 中创建一个能够处理流数据的多线程应用程序。更重要的是,我们不会自己创建任何 std::thread-s,相反,我们将采用未来和异步调用的新功能范式。
本教程的代码可以在 Github 上找到:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/Obs01ete/async_pipeline)
这项工作的动机是,当您想要在 CPU 上执行一些繁重的计算时,您不可避免地想要在多线程中运行它。否则,数据研磨的带宽将被限制在一个线程内。例如,如果你有一个来自网络摄像头的图像流,你想对它应用几个过滤器:颜色调整,调整大小,甚至可能是一些人工智能,比如人脸检测,运行所有这些处理步骤可能需要数百毫秒。当源帧速率为 30 fps 时,您可能会在可视化窗口中得到令人失望的 5–10 fps。我们通过 header 和 stdlib 的内容来看看如何处理。
然后让我们创建我们的处理函数func1
和func2
。在本教程中,我们不会运行真正的计算,因此作为概念验证,让我们设置模拟线程繁忙的睡眠。
最后,我们希望可视化处理的结果,比如调用 OpenCV 的 cv::showImage()。为了简单起见,我们在这里放置了一个简单的 std::cout 打印输出。
现在我们准备好准备我们的主要应用程序。
是时候推出我们的应用程序,看看我们有什么。
Enqueued sample: 0
Enqueued sample: 1
Sample 0 output: ‘input_string_0 func1 func2’ finished at 1851
Sample 1 output: ‘input_string_1 func1 func2’ finished at 2851
Enqueued sample: 2
Sample 2 output: ‘input_string_2 func1 func2’ finished at 3851
Enqueued sample: 3
Sample 3 output: ‘input_string_3 func1 func2’ finished at 4851
Enqueued sample: 4
Enqueued sample: 5
Sample 4 output: ‘input_string_4 func1 func2’ finished at 5851
Sample 5 output: ‘input_string_5 func1 func2’ finished at 6851
Enqueued sample: 6
...
Enqueued sample: 98
Sample 98 output: 'input_string_98 func1 func2' finished at 99862
Enqueued sample: 99
Waiting to finish...
Sample 99 output: 'input_string_99 func1 func2' finished at 100862
Finished!
太神奇了,管道成功了!请注意样本可视化时刻之间的时间差:为 1000 毫秒。如果没有多线程管道,则为 900+950=1850 毫秒。
std::async 的一个优点是它在幕后管理一个线程池。所以不用担心每次我们调用 std::async 时都会启动一个新线程。后续调用会重用线程,因此操作系统不会因为启动和终止大量线程而过载。
人们可能会注意到代码中的一些缺陷:
- std::cout 不受互斥体保护。
- 异常不由处理函数处理。
- 我们正在向
func#
,尤其是visualize
传递大量的争论。值得考虑将代码组织成一个类。
这些我将在下一篇文章中讨论。
谢谢你看我的教程,下期再见!
从数据抓取到结果分析,构建完整的容器化机器学习解决方案
这篇文章解释了如何一步一步地构建一个 ML 项目
介绍
在这里,我将描述如何建立一个系统来从维基百科中检索数据,将它们存储在 Postgres 数据库中,最后执行线性回归算法来预测城市人口和游客涌入之间的相关性。
在我的工作面试过程中,我被要求建立一个系统,从维基百科收集数据,并将其存储在数据库中。最后,我被要求执行一个小的线性回归 ML 算法来检查数据之间的关系。我正在分享我的过程和想法,关于我如何为任何可能有兴趣做类似项目的人建立这个系统。该项目可以很容易地扩展或适用于任何其他领域。
我为采访准备的演示文稿和代码可以在我的 Github 上找到。
系统管道
Docker 编写模块
- 提供一致的执行环境
- 启动系统的单一命令
- 包含多个容器
- 在容器之间提供一个隔离的网络环境
- 根据依赖关系按顺序管理启动容器的协调
docker-compose 环境包含 4 个容器: Postgres 数据库、 PgAdmin 、 Python 3.7.6 应用主机和 Jupyter 笔记本
要查看最终的 docker-compose 文件,请参考我的 Github 。
数据提取器模块
数据提取是从万维网上自动挖掘数据或收集信息的过程。
数据提取器模块-类图
本项目中使用的两个数据来源是世界城市文化论坛 wiki 页面和参观最多的博物馆 wiki 页面。
结合使用 wiki-api 和 Python 请求库来访问维基百科页面和检索信息。请求库以 wikitext 的形式获取文本。需要对 Wikitext 进行解析,以便从中提取完整信息的含义。
有许多库可用于解析 wikitext。在分析了几种之后,我决定使用 wikitextparser 。然而,并不是所有的文本在经过解析器后都会变得非常清晰。为了解决这个问题,我编写了一个后处理模块,使用 Wiki 表规则和正则表达式在将文本存储到数据库之前对其进行更多的清理。
这些数据总共包括了 33 个城市和 46 个博物馆的信息。检索每个城市的以下特征:名称、人口、规模、报告年份、游客总数。对于每个博物馆,检索以下特征:名称、游客、类型、公共交通、位置、建立、建造、报告年份。
然而,一些维基页面有丢失的数据。下图显示了检索到的数据。缺失的数据用紫色标记。
检索到的数据和缺失的数据
数据库模块
数据库模块-类图
我使用了 postgres 开源数据库,它与 SQLAlchemy 一起支持这个项目。SQLAlchemy 是一个 Python SQL 工具包,它为开发人员提供了 SQLRelational Mapper 的全部功能和灵活性。如果你需要了解更多关于 AQLAlchemy 的知识,请参考他们的教程。
因为数据是为博物馆和城市提取的,所以这个项目只需要 2 个表。每个博物馆和城市都有一个唯一的 id 作为主键。城市 id 是博物馆表中的外键。
数据库表
有关该项目的数据库的详细信息,请参考我的 Github 。
记录器、配置和可视化模块
记录器记录应用程序中发生的详细事件。日志有助于解决应用程序和基础设施性能问题。用户可以设置不同的日志记录级别。logger 模型在内部使用 python 日志库。
该配置用于设置系统不同模块的参数,并避免使用任何硬编码设置。当前系统使用的一些配置包括主维基页面名称、数据库配置以及维基页面和数据库字段之间的翻译器。
可视化工具是一个独立的模块。它的工作是为了可视化的目的生成不同的图。
关于记录器、配置和可视化器的详细信息可以在我的 Github 中找到。
机器学习模块
机器学习模块的目标是创建一个线性回归算法,以探索城市人口和游客涌入之间的相关性。该模块由以下几个阶段组成:
- 数据清理
- 评估数据分布
- 线性回归
- 相互关系
- 模型评估
- 实验
- 结果分析
关于机器学习模块的详细信息可以在我的 Github 中找到。
数据清理
为了获得更准确的预测结果,数据质量很重要。数据清理用于改善和提高数据质量。在本项目中,执行以下清理程序来清理和纠正数据,使其可用:
1-使用正则表达式从总体中删除不需要的字符,如逗号(“,”)
2-使用正则表达式从年份中提取数字
3-将数字数据从字符串转换为数字
4-删除丢失的数据
评估数据分布
数据分布是更好地理解数据的一个很好的工具。有许多方法可以评估数据分布,如直方图、箱线图等。在这个项目中,数据的分布用直方图和概率图来表示。
博物馆游客数据分布
城市人口数据分布
概率图博物馆游客
城市人口概率图
线性回归
L 线性回归是一种线性方法,用于模拟一个因变量和一个或多个自变量之间的关系。下图说明了线性回归方程和图表。
线性回归公式
相互关系
当两组数据具有高相关值时,它们被强有力地链接在一起。
相关性可能有价值:
- 1 是完美的正相关
- 0 表示没有相关性(这些值看起来根本没有关联)
- -1 是完美的负相关
- 当值一起增加时,相关性为正,并且
- 当一个值随着另一个值的增加而减少时,相关性为负
模型评估
模型评估是用于选择最佳模型的方法,而不考虑架构或参数。估计模型对样本外数据的推广程度的过程称为模型评估过程。
最常见的模型评估程序是训练/测试分割程序。这是一个非常快速和简单的程序,并提供了非常精确的样本外性能估计。最佳实践之一是将数据随机分为训练集和测试集。我使用 Scikit 包中的 train_test_split 函数进行数据拆分。我选择 80%的数据用于训练,20%用于测试。
您总是需要一个评估指标来配合您选择的过程。度量标准的选择取决于您要解决的问题的类型。对于回归问题,平均绝对误差、均方误差和均方根误差通常用作评估指标。
实验
因为我的数据的值的范围是变化的,所以我也执行了数据标准化。
当要素具有不同的范围以将数据的值更改为通用范围时,数据规范化是一种经常作为机器学习的数据准备的一部分而应用的技术。
为了验证因变量(博物馆游客)和自变量(城市人口/城市游客)之间的相关性,散点图生成如下:
城市人口与博物馆游客
城市游客与博物馆游客
下图显示了模型评估程序:
城市人口实际值与预测值对比结果
城市游客实际值与预测值对比结果
预测数据与测试数据
预测数据与测试数据
城市人口残差图
城市游客残差图
城市人口和城市游客误差评估
C.p:城市人口
C.v:城市游客
博物馆参观者
EV:解释的差异
PCC:皮尔逊相关系数
MSLE:均方对数误差
R2:决定系数®
平均绝对误差
MSE:均方差
RMSE:均方根误差
进行的另一个实验是为了找出最大游客量的城市人口、每个城市博物馆的所有游客和每个博物馆的游客之间的相关性。用城市游客代替城市人口进行了同样的分析。
实验结果
Li 回归分析,最差结果用黄色标记,最佳结果用橙色标记
结果分析
- 人口与博物馆参观者没有显著的相关性。
- 单一的特征并不能很好地预测博物馆的参观者。
- 由于需要更多的数据、错误的假设或较差的特征,模型不符合数据。
- 数据收集不符合真实的数据分布,例如数据年份不匹配,博物馆游客仅涵盖 2018 年,但城市人口和城市游客属于任何一年。
改进
我认为将来可以对该项目进行一些改进:
系统级
- 使用 NLP 为 wiki 提取信息
- 拥有一个交互系统来获取用户输入以进行关联
- 使用 JSON 文件而不是配置文件
- 使用 Django 和 Flask python 库添加 web 界面,以通过浏览器显示用户界面
- 分析数据库以提高性能
数据等级
- 为了有效地提取特征,回顾关于游客和博物馆之间关系的现有方法
- 获取更多数据,如博物馆到公共交通、餐馆等的距离。
- 有了更多的数据,通过使用多元线性回归找到最相关的要素
- 执行决策树以找到访客最多时最有效的功能
结论
希望这篇文章对那些想知道如何做类似项目的人有所帮助。帖子的代码可以在我的 Github 上找到。