了解 Flask vs FastAPI Web 框架
两种不同 RestAPI 框架的比较
Daria Shevtsova 拍摄于 Unsplash
简介
作为一名数据科学家并不仅仅局限于模型构建,而是朝着下一步,模型部署,努力。模型部署对于向其他人展示您的最终结果很重要*(或者可能是您的客户)*。这群人可能不懂编码,也不会通过查看您的代码来理解;他们更喜欢查看最终的应用程序,这样可以立即查看结果。一种常见的方法是将您的代码包装在 RestAPI 中,并作为微服务进行部署。
在本文中,我将介绍两种不同的可以快速搭建 web 服务器的框架: Flask 和 FastAPI 。Flask 和 FastAPI 都是用于构建小规模网站和应用程序的框架。
砂箱框架
Flask 于 2010 年发布,这是一个用 python 编写的微型 web 框架,以最少的代码支持 web 应用程序的部署。它旨在作为微服务轻松、灵活且快速地部署。Flask 构建在 WSGI (Python Web 服务器网关接口)之上,服务器将为每个请求绑定一个工作器。
下面是一个使用 Flask 进行部署的示例(这是一个使用“GET”方法获取用户输入并将值插入到 Google Big Query 中的示例——该示例部署在 Google Cloud Run 上)
使用 Flask 部署示例程序
使用 Flask 完成部署后,您可以运行 URL 并在 URL 中传递输入,将会返回一条消息。
使用 Flask 调用微服务部署
FastAPI 框架
FastAPI 比 Flask 更新,发布于 2018 年。它的工作方式类似于 Flask,后者用最少的代码支持 web 应用程序的部署。然而,FastAPI 比 Flask 更快,因为它建立在 ASGI(异步服务器网关接口)之上,从而支持并发/异步代码。这是通过用异步定义语法声明端点来实现的。
对于 FastAPI 来说,值得强调的一点是文档。在使用 FastAPI 框架进行部署时,它将生成文档并创建一个交互式 GUI (Swagger UI ),允许开发人员更方便地测试 API 端点。
下面是一个使用 FastAPI 进行部署的示例(这是一个使用“GET”方法获取用户输入并将值插入到 Google Big Query 中的示例—该示例部署在 Google Cloud Run 上)
使用 Flask 完成部署后,您可以运行 URL 并在 URL 中传递输入,将会返回一条消息,其工作方式与 Flask 类似。
使用 FastAPI 调用微服务
现在,真正让我兴奋的是 FastAPI 的附加功能,即自动生成文档(Swagger UI) 。要访问 Swagger UI,请输入 API endpoint /docs,这就是测试 API 端点的交互式 GUI。拥有一个大摇大摆的用户界面也更容易向他人解释你的程序。
FastAPI — Swagger UI
通过使用 Swagger UI,您可以轻松地测试您的 API 端点并通过接口指定参数。例如,在下图中,您可以在提供的列中轻松指定“书名”和“作者”。
从 Swagger UI 测试“获取请求”
除了 Swagger UI,FastAPI 还附带了另一个文档——“ReDoc”。该文档包含列出的所有端点,如果在同一个服务中部署了许多端点,这将非常有用。要访问文档页面,请输入 API 端点/redoc。
FastAPI — ReDoc
烧瓶和 FastAPI 的对比:
HTTP 方法:
HTTP 方法
- 在 Flask 和 FastAPI 中指定“GET”或“POST”方法是不同的。
传递参数&数据验证:
使用 Get 方法
- Flask 不提供对数据格式的验证,这意味着用户可以传递任何类型的数据,如字符串或整数等。(或者,可以将输入数据接收的验证脚本内置到脚本中,但这需要额外的工作)
- FastAPI 允许开发人员对收到的参数声明额外的标准和验证。
错误信息显示:
烧瓶& FastAPI 的错误消息
- 默认情况下,Flask 中显示的错误消息是 HTML 页面,而 FastAPI 中显示的错误消息是 JSON 格式的。
异步任务:
比较异步任务
- 正如本文前面提到的,Flask 部署在不支持异步任务的 WSGI (Python Web 服务器网关接口)上,而 FastAPI 部署在支持异步任务的 ASGI(异步服务器网关接口)上。
结论:
在研究了 Flask 和 FastAPI 之后,我会考虑将来采用 FastAPI,因为它有异步功能和自动生成的非常详细和完整的文档。此外,使用 FastAPI 进行部署所需的工作量与 Flask 相同。
如果您一直在使用 Flask,您可以考虑试用 FastAPI 并观察比较。
参考文献&链接:
[1]https://flask.palletsprojects.com/en/2.0.x/
[2]https://fastapi.tiangolo.com/
[3]https://analyticsindiamag . com/fastapi-vs-flask-comparison-guide-for-data-science-发烧友/
https://www.programmersought.com/article/60453596349/
[5]https://www . analyticsvidhya . com/blog/2020/11/fastapi-the-right-replacement-for-flask/
了解谷歌的 Switch Transformer
谷歌如何无成本地创建了世界上最大的语言模型
乔纳森在 Unsplash 上拍摄的照片
当 GPT-3 在 2020 年 5 月由 OpenAI 在推出时,消息不胫而走。不仅在人工智能社区,甚至在主流媒体上也有类似于“一个机器人写了这篇文章”和“你读过 GPT 3 写的东西吗?”。人们很兴奋!
在 GPT-3 之前,最大的语言模型是 2020 年 2 月发布的拥有 170 亿个参数的图灵-NLG 模型。那年晚些时候,OpenAI 用 1750 亿个参数把它炸出了公园。突然,出现了一种语言模型,它可以产生与人类通常无法区分的内容。
2021 年初,谷歌发布了一篇论文,题为“开关变压器:用简单高效的稀疏性扩展到万亿参数模型”。虽然这确实得到了一些人工智能社区之外的小媒体报道,但它并不接近 GPT-3,尽管创建了一个参数几乎十倍多的模型!
这种覆盖面的缺乏并不令人惊讶。谷歌的 1.6 万亿参数模型在性能方面不是最先进的,也没有一个整洁的 API 前端来演示人工智能的未来。然而,这并不意味着动机或结果对未来的人工智能和机器学习研究不重要。
在本文中,我们将尝试理解开关变压器背后的动机,它是如何工作的,以及为什么这些结果对机器学习研究和应用的未来如此重要。
动机
经验表明语言模型的性能随着参数数量(模型大小)、数据集大小和计算预算的增加而呈幂律增长。
然而,随着这些费用的增加,培训的财务费用也在增加。这导致开源、预先训练的语言模型越来越受欢迎,如谷歌的 BERT ,它可以在特定的下游语言任务上进行微调,如分类、问答或总结,使数据科学从业者受益于谷歌、脸书和 OpenAI 等公司掌握的大量资源。
然而,GPT-3 证明,如果这些模式继续增长,即使是大型企业和组织也可能开始陷入困境。GPT-3 花费了据称1200 万美元训练。这个包括近 5000 个模型版本的训练,使用了将近 10000 天的 GPU 时间。
同样值得注意的是,成本不仅是经济的。据估计,这次培训产生了 78,000 磅的二氧化碳排放量。类似于一个美国成年人两年生产的东西!
照片由i̇smail·埃内斯·艾汉在 Unsplash 上拍摄
OpenAI 的研究表明,用于训练人工智能模型的计算量每 3.4 个月翻一番,这在经济和环境方面产生了令人担忧的预测。
这就是 Switch Transformer 背后的动机,在不增加计算成本的情况下,创建更大的机器学习模型。
开关变压器
开关变压器是一个开关前馈神经网络(FFN)层,取代了变压器架构中的标准 FFN 层。关键区别在于,每个交换层包含多个 FFN,称为专家,而不是包含单个 FFN。
当每个令牌通过这一层时,它首先通过路由器功能,然后将令牌路由到特定的 FFN 专家。
由于每个令牌只通过一个专家 FFN,浮点运算(FLOPS)的数量保持不变,而参数的数量随着专家数量的增加而增加。
这创建了一个稀疏模型,其中不是每个参数都用于每个令牌。在这方面,开关变压器解决了上述动机,在不增加计算量的情况下增加模型参数的数量,计算量以 FLOPs 计。
在开关变压器前馈神经网络层中,每个令牌通过路由器功能将其发送到单个前馈神经网络,称为专家。由于每个令牌仅通过单个 FFN,所以计算不会增加,但是参数的数量随着专家的数量而增加。图片来自原开关变压器纸。
专家混合
使用专家来增加模型参数数量的概念对于开关变压器来说并不新奇。一篇描述专家混合层的论文于 2017 年发布,其架构与开关变压器几乎相同。
关键区别在于路由器功能会将每个令牌发送给多个 FFN 专家。作者假设路由器功能将无法学习如何路由令牌,除非它可以通过路由到 k>1 个专家来比较至少两个令牌。
另一方面,开关变压器仅使用 k=1,提供三大优势:
- 路由器的计算量减少了,因为它只路由到一个 FFN 专家。
- 每个专家的批量至少减半(即 k=1 而不是 k ≥2)。
- 降低了设备之间(即路由器到专家)的通信成本。
功率
第二个好处需要进一步分析。每个专家的批量大小也称为专家能力,即专家在给定通道中能够处理的令牌数量。
理想情况下,这将等于令牌数除以专家数,称为容量因子 1。这样,在给定的步骤中不会浪费专家的能力。
然而,这假设路由器功能在专家之间平均分配令牌。实际上,一些专家 FFN 会溢出,导致某些令牌在该步骤中没有被 FFN 专家处理。
为了避免溢出,导致令牌在该步骤中不被任何 FFN 处理,必须增加容量因子。这增加了计算和通信成本,因此辅助损耗不利于不平衡路由。图片来自原开关变压器纸。
容量因子可以增加,但是这将导致一些专家具有未使用的容量,增加计算和通信成本。
作为一种折衷,作者在总损失函数中增加了一个辅助损失,通过路由器功能来惩罚令牌的不平衡路由。他们根据经验发现,容量系数为 1.25 时性能最佳。
结果
为了衡量 Switch Transformer 的性能,他们在庞大干净的爬虫语料库()上训练了几个模型,用 T5 语言模型作为基准,对比了负 log 困惑度。
考虑到动机,为了在不增加所需计算的情况下增加参数的数量(从而增加性能),他们训练的模型与 T5 相当,即每个令牌的计算量保持相等。
他们发现了以下情况:
- 经过 100,000 步后,开关变压器模型比触发器匹配的 T5 等效模型具有更大的负损耗困惑。
- 开关变压器达到质量阈值(负。日志 perp。=-1.495)比同等的 T5 更快。在 T5 基的情况下,这个阈值从未达到过!
- 开关变压器每秒能够处理更多的样本。
缩放属性
除了分析整体性能,作者还在预训练期间查看了缩放属性。也就是说,给定一个无限的计算预算,如何缩放模型是最好的?
对三个不同的维度进行了分析,以了解模型的缩放特性:步骤、时间和针对更大的密集模型。
**步长调整:**对于固定数量的训练步长,开关变压器将优于触发器匹配的 T5 模型。增加专家数量将进一步提高性能,而不会增加失败次数。拥有 64 名专家的开关变压器模型在 60k 步中实现了与 T5 基模型在 450k 步中相同的性能,相当于 7.5 倍的加速。
T5 基极与触发器匹配的等效开关变压器模型的步进调整,专家人数不同。图片来自原开关变压器纸。
**时间缩放:**直观上,时间缩放应该等同于步长缩放。然而,跨设备的额外通信成本和路由器功能的计算意味着这需要被明确地分析。结果显示,对于固定的训练时间,开关变压器优于触发器匹配的等效 T5 基极模型。在 T5 基模型和 64 专家开关变压器模型之间观察到 7 倍的加速。
**更大的密集模型:**作者最后考虑了这样一种情况,为了匹配开关变压器的性能,使用 T5-大模型代替 T5-Base。然而,他们表明,这导致每个令牌的 FLOPS 增加了 3.5 倍,并且 FLOPS 匹配的开关变压器模型将优于此。
这些缩放结果表明,对于任何可用的计算增加,较大的开关变压器模型将优于较大的密集模型。考虑到减少语言模型的计算足迹的动机,这是一个极其重要的结果。
下游结果
为了测量下游性能,将 T5-Base(223m 参数和 124B 触发器)和 T5-Large (739M 参数和 425B 触发器)与匹配开关-Base (7.4B 参数)和开关-Large (26.3B 参数)模型的触发器进行了比较。
这些模型在 11 个不同的语言任务中进行了比较,包括分类、问题回答和总结等任务。除了电弧之外,开关变压器模型在所有任务中都优于触发器匹配的等效 T5 模型。
多语言学习
作为性能的最终测试,作者在 101 种不同语言的预训练中测量了模型的质量。在所有语言中,基于开关的模型比基于 T5 的模型具有更大的负对数困惑,并且观察到平均 5 倍的训练加速。
万亿参数模型
在文章的结尾,作者介绍了两个大型开关变压器模型 Switch-XXL 和 Switch-C 的设计和训练,它们分别具有 3950 亿和 1.571 万亿个参数。
开关-XXL 模型与 T5-XXL 模型是匹配的。但是,由于 Switch-C 模型的大小,架构大小(层大小、深度、注意力头数量等。),因此一偏,是减少了。
照片由 Unsplash 上的 Kaffeebart 拍摄
这可能是 Switch-XXL 模型的负对数困惑度大于 Switch-C 的部分原因,表明增加专家人数的收益递减,特别是以牺牲其他模型维度为代价。然而,在预训练后,两种模型都优于 T5-XXL。
这些模型也在下游任务中被测量。然而,这些结果都不是最先进的。
这也许是为什么开关变压器没有得到与 GPT-3 相同的媒体曝光率的原因。万亿参数模型虽然令人印象深刻,但也需要有令人印象深刻的性能做后盾。
然而,这并没有降低这项研究的重要性。迄今为止,增加密集语言模型的规模是生产最先进模型阻力最小的途径;但很明显,这在经济和环境上都是不可持续的。
谷歌已经表明,可以创建创新的模型架构,在不增加计算成本的情况下提高模型性能,这是数据科学和人工智能社区在不久的将来肯定会看到的事情。
五分钟了解 GPT-3
人工智能
有数百篇关于 GPT 3 号的文章。这里有一个 5 分钟的关于它的所有信息的汇编。
GPT 3 号是什么
GPT-3 是 OpenAI 的生成式预训练模型家族的第三个版本。 GPT-1 和 GPT-2 为 GPT-3 奠定了基础,证明了两个关键假设的成功:变形金刚+无监督预训练工作正常(GPT-1)和语言模型可以多任务处理(GPT-2)。
GPT-3 是一种基于 transformer 架构的语言模型,以一种生成式、无监督的方式进行预训练,在零/一/少量多任务设置中显示出良好的性能。它的工作原理是在给定一系列标记的情况下预测下一个标记,并且可以对尚未训练过的 NLP 任务进行预测。在看过几个例子后,它在一些基准测试中达到了最先进的水平,如机器翻译、问答和完形填空任务。
GPT-3 是用巨大的互联网文本数据集训练的——总共 570GB。当它发布时,它是最大的神经网络,有 1750 亿个参数(100 倍 GPT-2)。即使在今天,它也是最大的密集神经网络,仅次于像开关变压器或悟道 2.0 这样的稀疏模型。
GPT-3 最令人印象深刻的特点是它是一个元学习者;它学会了学习。你可以用自然语言要求它执行一项新任务,它“理解”它必须做什么,就像人类一样(保持距离)。
GPT 3 号能做什么
在论文中,OpenAI 的研究人员使用标准基准将 GPT-3 与之前的模型进行了比较。它表现出比以前的类似系统更好的性能,但对于一些任务来说,受监督的系统——为特定任务而训练——要好得多。与监督系统相比,GPT-3 的主要贡献是普及了一条通往 AGI 的新的有前途的道路。
除了枯燥的基准测试,OpenAI 还提供了另一种测试 GPT 3 语言技能的方法。他们发布了测试版 API ,鼓励开发者寻找新的令人兴奋的用例。该 API 的工作原理是向基线 GPT-3 输入文本,即所谓的提示,使其专注于特定的任务。如果你输入:这个女人正在遛狗→ La mujer está paseando a su perro。孩子在公园里玩耍→ ____ ,GPT-3 会知道你在要求英语-西班牙语翻译,并将能够做到这一点。
GPT-3 的这个特殊版本将不同于任何其他用户的 GPT-3。那就是提示+元学习的力量;在不改变原始模型的情况下,用户可以让 GPT-3 成为不同任务的专家。基线 GPT-3 不知道如何执行任何任务,它知道如何学习去做,这使它更加强大和多才多艺。
这里有一个 GPT-3 能做什么的列表,还有链接(特别提到 Gwern Branwen ,他做了一个很棒的例子汇编):
- 非小说类: 对话,模仿类,杂文类,新闻类文章,剧情总结,推文类,教学类。
- 专业: 广告,邮件,文案, CV 生成,团队管理,内容营销,记笔记。
- 代码: Python , SQL , JSX , React app , Figma , javascript , CSS , HTML , LaTeX
- 创意: 小说,诗歌,歌曲,幽默,网游,桌游,迷因,烹饪菜谱,吉他标签,写出你独特的风格。
- 理性技巧: 逻辑,不确定性,常识,类比,概念交融,计数,字谜,预测。
- 哲学: 人生意义,数字 42 ,回应哲学家。
GPT 3 号现在和将来对世界的影响
疯狂炒作
GPT-3 从根本上改变了工业和学术界的人工智能格局,人们为之疯狂。一些人将人类特征归因于该系统,另一些人在此基础上建立产品和公司,它成为各地的头条新闻,一批批研究人员开始构建类似的人工智能。以下是一些炒作的例子:
- **归因:**人们说 GPT-3 是"有自我意识的,“一个"一般智力的”(或者至少是一个"无脑路径的"对它来说),并且能够"理解【和】推理"
- **杂志:**上了纽约时报,福布斯,麻省理工科技评论,连线,卫报,财富, The Verge ,科技危机,数字趋势。
- 创业公司: 可行的,寓言工作室,阿哥利亚,复制匠,纬度,他方,调试,热兹,总库,布罗卡,认为合流
- AI 后继者: 开关变压器, DALL E ,夹子, UC , LaMDA , MUM ,武道 2.0 。
潜在的危险
GPT-3 是一个令人惊叹的人工智能,但像任何其他强大的技术一样,可以被恶意使用。这是它造成伤害的许多方式中的一些:
- 偏见: OpenAI 发现 GPT-3 存在种族、性别和宗教偏见,这可能反映了训练数据中的偏见,因此该系统受到了强烈批评。OpenAI 最近发表了 PALMS ,这是一种使用小型精选数据集来减少 GPT-3 偏差的方法。
- **假新闻:**因为 GPT-3 写得太好了,它可以写出可以被当做人造的虚假文章,正如博主利亚姆·波尔和卫报所证明的那样。OpenAI 在论文中强调,人类法官只能识别 52%的 GPT-3 文章,略高于纯粹的概率。
- **环境成本:**训练 GPT 3 号产生的碳足迹与“驾驶汽车往返月球产生的碳足迹大致相同“越大越好”的趋势只有在环境没有受到威胁的情况下才会继续。
- 无用的信息:GPT 3 号不对它吐出的文字负责。这些低质量的数据在互联网上积累,用哲学家香农·瓦勒的话说,“人们越来越无法使用,甚至有害。”
- 工作损失:像 GPT-3 这样的系统威胁到基于计算机的、非常规的认知工作,甚至比机器人威胁蓝领工作更甚。目前的报告估计大约 40-50%的工作会在 15-20 年内被取代。
批评和辩论
- 在疯狂的宣传之后,人们开始寻找 GPT-3 的弱点。发现系统缺少逻辑、常识、和文本理解。加里·马库斯写了一篇对这一体系的很有说服力的评论:“[GPT-3]对世界的理解经常严重偏离,这意味着你永远不能真正相信它所说的。”
- **反批评:**格温根据一个前提对大多数批评者提出了很好的论点:“抽样可以证明知识的存在,但不能证明知识的缺失。”他发现大多数 GPT 3 号的失败是由于人们有缺陷的提示。
- **实践与哲学的争论:**实践的争论关系到 GPT-3 的局限性:我们无法确定它什么时候会正确回答,什么时候不会,这使得它不可靠。哲学辩论涉及通往 AGI 的道路:GPT-3——或任何其他基于计算机的人工智能——可能永远无法到达 AGI,除非它们“T22 长大,属于一种文化,并在世界上行动 T23。”
如果你想了解更多关于 GPT 3 号的信息,我推荐你阅读完整的概述
[## GPT-3 —全面概述
towardsdatascience.com](/gpt-3-a-complete-overview-190232eb25fd)
其他推荐阅读
</4-things-gpt-4-will-improve-from-gpt-3-2b1e7a6da49f>
跟我一起旅行到未来 了解更多关于人工智能、哲学和认知科学的内容!如有任何疑问,欢迎在评论中提问或联系LinkedIn或Twitter!😃
理解图形挖掘
图形挖掘学习指南
你学习深度学习的第一步
来源( Unsplash )
想象一下脸书:你如何在朋友层内建立联系?
想象一下推荐系统:你怎么知道一个人的偏好与其聚类密切相关?
欢迎来到图形挖掘
理解图形分类
图形分类在大量连接的数据(例如:社交、生物和支付)中生成图形,并使用图形来识别标签(监督的)或聚类(非监督的)。
如果这听起来对你来说很难,你可以看看你的大脑,因为它是快速推断图表之间联系的大师。
例如,你的大脑知道如何从 A 点到 B 点,甚至不需要思考。想象一下你在长时间购物后的最后一次购物之旅。带着孩子去购物时,你可以浏览商品目录,并在不同类别的过道中画出多条直线来比较产品。
所有这些行动都需要整合,根据一系列子行动来推断最佳行动,以达到最终目标。尽可能以最轻松的方式完成。
一个简单的 DAG(有向无环图)流程图(来源于作者)
同样地…
图挖掘使用特征来查看一组观察结果如何与用户面临的相似性信号相关联。
图形表示基于距离公式化的实体(节点)之间的关系(边)。
有两个常见的特征:
- 来自外部来源的自然图表。例如支付网络、社交媒体和道路。
- 来自节点间相似距离度量的相似图。例如,元数据的 blob 然后通过图形表示共享 blob 结构。
根据图形的特征,我们可以将每个图形分类为
- 具有一种节点和边的简单齐次
- 复杂异构,具有多种类型的节点、多种类型的边。这些可以是有向的,也可以是无向的。
图的基本组成部分(在这种情况下是无向的)是节点和边。(来源:geeksforgeeks.org)
为什么图表很重要?
图表无处不在
图表是多用途的。您可以通过对周围的邻域进行分类来构建监督模型,通过将现有标注传播到邻域中缺失的标注来构建半监督模型,以及通过训练节点级嵌入来描述数据的结构角色来构建非监督模型
您可以使用图表完成各种各样的任务。例如,您可以根据您的用例对节点、图形和链接进行分类。
其中包括多项突破,如:谷歌 DeepMind 的超级分段图预测到达时间(ETA) 和来自 Pinterest 的PinSage。
图形计算具有关系和同一性的抽象概念
图形抽象出局部信息,从数据中提取有用的全局信息。图形结构/拓扑可以告诉我们很多关于数据的信息,比如发现数据点的聚类,或者为无形的概念提供距离度量。
图表是灵活的数据类型
图形通过嵌入和组合多种类型的特征灵活地工作,例如:文本(例如:Jaccard 相似性)、视觉(例如:图像相似性)和其他语义属性。例如,当分析师处理活动规则检测时,他们会使用几个特性,如正则表达式设置之间的相似性,并部署正则表达式来捕获这些站点。
图表是可缩放的
图形采样用于寻找感受野上的子图。这些子图可以在工人的异步分布之上公式化:获取模型参数的专用服务器。
这种可扩展性也提供了在图中使用联合学习来保护用户隐私的方法(例如基于 Android 的 ML 训练)
带有分布式警告的可伸缩性图表(来源于作者)
图形挖掘和网络入门
案例研究:GNNs 与 Cora
在这个案例研究中,我们将使用 Cora 数据,它提供了一个引用网络基准,具有 2708 个节点,5429 条边,7 个主题单词包,140 个训练,500 个验证和 1000 个测试观察。
请随意使用下面的 colab 作为你的教程。
https://colab.sandbox.google.com/drive/1d8YEErtymICvB_-YjFRH8LfUgtw-8YWW?usp=sharing
使用的库
Spektral 作为图形深度学习的开源 Python 库,基于 Keras 和 Tensorflow2。这个项目的主要目标是为创建图形神经网络(GNNs)提供一个简单而灵活的框架。
Spektral 有一个方便的功能,允许我们快速加载和预处理标准图形表示学习。它还允许函数访问特征矩阵、邻接、矩阵和掩码数组,以了解哪个节点属于训练、验证和测试集。
TF GNN 库对模型 API 是不可知的,集成了 DeepMind graphNet、Google 神经结构化学习、基于 MPNN 论文的 TF GNN API。该图还可以通过 TPU 支持的批处理/序列化在 Keras 层之间传递。
在 colab 中安装和导入这些库非常简单
!pip install numpy
!pip install tensorflow
!pip install spektral==0.6.1
import numpy as np
import tensorflow as tf
import spektral
从频谱生成形容词矩阵和特征矩阵
adj 指的是邻接矩阵,features 指的是 feature_matrix,labels 指的是论文的主题,mask sets 指的是哪个节点属于哪个数据集。
adj, features, labels, train_mask, val_mask, test_mask = spektral.datasets.citation.load_data(dataset_name=’cora’)
然后,我们替换密集特征,将特征转换为邻接矩阵,同时在 float32 中将它们设置为标准的机器学习输出格式。
features = features.todense()
adj = adj.todense() + np.eye(adj.shape[0])
features = features.astype(‘float32’)
adj = adj.astype(‘float32’)
培训和评估流程
我们按照以下步骤在 Cora 数据集上训练我们的 GNN:
- 定义 2 个 GNN 图层(输入-单位,输出-7 个主题,带 sumpooling)
- 用 Relu 激活函数实现定义层的 GNN。
- 使用 tf。GradientTape() 记录自动微分的操作。这有助于我们观察变量和梯度,以捕捉回传播错误
- 将优化器应用于渐变(Adam opt 是标准)
- 基于特征和邻接矩阵指定逻辑和精度计算
评估流程
您可以使用参数(单位、时期和学习率)进行实验,以定义验证准确性和训练/val 损失。
train_cora(features,adj,gnn,32,200,0.01)## Results ##
Epoch 0 | training loss: 1.9685615301132202 | val_accuracy: 0.20399999618530273 | test_accuracy:0.20799998939037323
Epoch 1 | training loss: 2.1238698959350586 | val_accuracy: 0.30399999022483826 | test_accuracy:0.3100000023841858
Epoch 2 | training loss: 1.6506766080856323 | val_accuracy: 0.6060000061988831 | test_accuracy:0.6150000095367432
Epoch 3 | training loss: 1.504560112953186 | val_accuracy: 0.628000020980835 | test_accuracy:0.6359999775886536
…
Epoch 129 | training loss: 0.004622798413038254 | val_accuracy: 0.7520000338554382 | test_accuracy:0.7450000047683716
正如你所看到的,似乎越多的纪元和训练步骤运行,我们可以从中获得更多的准确性。
搞定了。祝贺你的第一个图表网络!
关于作者
文森特用 ML @ Google 对抗网络滥用。文森特使用高级数据分析、机器学习和软件工程来保护 Chrome 和 Gmail 用户。
除了在谷歌的工作,Vincent 还是佐治亚理工学院计算机科学硕士校友、三项全能运动员和面向数据科学媒体的特约作家,为有抱负的 ML 和数据从业者提供指导,在全球拥有 100 多万观众。
最后,请通过 LinkedIn ,Medium或 Youtube 频道 联系文森特
索利·德奥·格洛丽亚
参考
理解和实现图形神经网络
图形数据结构介绍,如何定义自己的简单图形,以及如何建立一个简单的图形神经网络
来源:图片来自 Upsplash
图形神经网络(GNNs)在许多项目相互关联的预测任务中变得越来越流行(例如,药物发现中的分子结构、路径投影等)。然而,与其他公知的深度学习数据源(如图像、文本和/或表格)相比,图形数据结构可能更难掌握。
在这篇文章结束时,希望你已经熟悉了图形结构,以及如何使用 PyTorch Geometric (PyG) 库实现你自己的 GNNs。
目录:
- 装置
- 图表介绍
- 使用 PyG 构建简单图形
- 使用 PyG 训练和评估简单的 gnn
装置
注意:安装要求您至少安装 PyTorch 1.4.0。如果你还没有,请点击这个链接。
- 检查您是否已经有 PyTorch > 1.4.0
$ python -c "import torch; print(torch.__version__)"
>>> 1.8.0
2.找到 PyTorch 安装的 CUDA 版本:
$ python -c "import torch; print(torch.version.cuda)"
>>> 11.1
3.导出特定 torch 和 CUDA 版本的变量。
export TORCH=1.8.0
export CUDA=11.1
4.安装软件包,其中 T O R C H 和 {TORCH}和 TORCH和{CUDA}是之前定义的版本。
pip install torch-scatter -f [https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html](https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html)pip install torch-sparse -f [https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html](https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html)pip install torch-cluster -f [https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html](https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html)pip install torch-spline-conv -f [https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html](https://pytorch-geometric.com/whl/torch-${TORCH}+${CUDA}.html)pip install torch-geometric
图表介绍
图形通常具有以下基本结构,包括:
图形结构
- 节点:编号为 0、1、2 的三个圆圈
- 属性:每个节点的值( x1 )
- 边:连接不同节点的线
更复杂的图形结构(不在本文讨论范围之内)将具有额外的特性,例如:
- 邻接矩阵:定义节点如何在一个 n 乘 n 的矩阵中相互连接,其中 n 是图中节点的数量
- 边属性:边的值(例如,从 A 点到 B 点所需的米距离)
使用 PyG 构建简单图形
使用 PyG,我们可以使用简单的函数调用来重现上面的图形,如下所示:
import torch
from torch_geometric.data import Data
edge_index = torch.tensor([[0, 1, 1, 2],
[1, 0, 2, 1]], dtype=torch.long)
x = torch.tensor([[-1], [0], [1]], dtype=torch.float)
data = Data(x=x, edge_index=edge_index)
这看起来很复杂,但是让我们来分解代码:
- edge_index :是一个 2D 数组,其中第二维由 2 个子数组组成,分别表示源节点和目的节点(例如,从节点 0 到节点 1,从节点 1 到节点 0,从节点 1 到节点 2,以及从节点 2 到节点 1)
- x :三个节点的值属性
- 数据:当您提供 x 属性和 edge_index 时,构建图形数据结构
您可以找到图表的元数据详细信息,数据,,格式如下
# The number of nodes in the graph
data.num_nodes
>>> 3# The number of edges
data.num_edges
>>> 4# Number of attributes
data.num_node_features
>>> 1# If the graph contains any isolated nodes
data.contains_isolated_nodes()
>>> False
使用 PyG 训练和评估简单的 gnn
现在让我们尝试使用 PyG 实现我们自己的 GNN。让我们使用 PyG 库提供的基准数据集 Cora。
from torch_geometric.datasets import Planetoid
dataset = Planetoid(root='/tmp/Cora', name='Cora')
>>> Cora()
我们可以使用 PyG 提供的模块定义一个简单的 GNN。你可以在 PyTorch 这里了解更多关于定义 NN 网络的知识。
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = GCNConv(dataset.num_node_features, 16)
self.conv2 = GCNConv(16, dataset.num_classes)
def forward(self, data):
x, edge_index = data.x, data.edge_index
x = self.conv1(x, edge_index)
x = F.relu(x)
x = F.dropout(x, training=self.training)
x = self.conv2(x, edge_index)
return F.log_softmax(x, dim=1)
上面的代码简单地表示:初始化第一个图卷积层,然后是 ReLU 激活函数和 dropout 层,接着是第二个图卷积层,然后将输出馈送到 log_softmax 激活函数。
类似地,培训脚本可以编写如下:
model = Net()
data = dataset[0]
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
model.train()for epoch in range(200):
optimizer.zero_grad()
out = model(data)
loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
首先,我们初始化预定义的 Net()类和相应的优化器。我们将训练 200 个时期或步骤,其中我们将在调用 backward()函数进行反向传播之前应用负对数似然损失函数。
评估脚本将会是:
model.eval()_, pred = model(data).max(dim=1)
correct = int(pred[data.test_mask].eq(data.y[data.test_mask]).sum().item())acc = correct / int(data.test_mask.sum())
print('Accuracy: {:.4f}'.format(acc))>>> Accuracy: 0.8150
在这里我们调用 eval()方法来防止反向传播,并将模型设置为评估模式。此后,我们传递我们想要使用的测试数据,并计算正确答案的比例,以获得准确性分数。
就是这样!希望你能学到新东西。
结论
这篇文章简要总结了一个简单的图形结构,以及如何使用 PyG 来构造它。接下来,我们实现了一个简单的 GNN,其中我们使用了流行的 GNN 的变体,称为图形卷积网络(GCN)。我们还成功地训练和评估了我们的基准数据集。如果您想修改网络,请在 PyG 文档中找到更多详细信息。
做订阅我的邮件简讯:【https://tinyurl.com/2npw2fnz】在这里我定期用通俗易懂的语言和漂亮的可视化方式总结 AI 研究论文。
了解梯度增强树如何进行二叉分类
使用银行券数据集,在 LightGBM 和 R 中从数据到预测的逐步重新计算
由 Eric Prouzet 在 Unsplash 上拍摄的照片
几周前,我深入研究了梯度增强模型如何工作。我提出的主要观点是,人们不必对梯度增强模型(如 XGBoost 和 LightGBM)的“黑盒”感到恐惧,理解模型正在做什么实际上是相当简单的。
在我的例子中,我使用泊松回归作为目标,现在我认为用不同的、更常用的目标:二元分类来重复这个练习会很有趣。
在这篇文章中,我们将:
- 训练二元分类模型,
- 重新计算二进制日志丢失和二进制错误度量,
- 检查预测概率和原始模型结果之间的联系,
- 根据梯度和 hessian 公式重新计算模型结果。
我们不会深入到每一个小细节,相反,我们将把重点放在二元分类目标的特殊性上。我推荐我之前关于这个话题的帖子作为起点。
环境
我们将在 LightGBM 包中工作,编程在 r 中完成。(不用说,我们讨论的所有内容都应该适用于 Python,以及其他梯度增强包,如 XGBoost。)
完整脚本请参考我的 GitHub 。
我们将要使用的数据是钞票数据集,可以在 UCI 机器学习库上免费获得。该数据包含 1372 张钞票的信息,目标是找出它们是否是伪造的。
数据
假设数据存储在data
文件夹中,为 LightGBM 准备数据的脚本:
我们有一个 LightGBM 可以使用的dtrain
对象。
训练模型
我们现在可以这样训练模型:
注意事项:
- 在现实生活中,你永远不会训练出这样的分类模型。我们有连续的特征,这意味着模型可以完美地过度拟合,除非我们用某种形式的正则化来限制它。然而,在这个项目中,我们不关心这个,我们只是想理解计算。
- 超参数应该非常简单,除了
sigmoid
。这是一个特定于二进制分类的参数,我们将很快看到它是如何在计算中使用的。
预言
我们将需要使用下面的模型预测,让我们来解决这个问题。
注意事项:
num_iteration
定义我们希望包含在预测中的树的数量。通过将它设置为 1,我们在仅一轮后就获得了模型预测的结果。rawscore = TRUE
在将原始模型结果转换为预测之前返回它们。在二元分类的情况下,这意味着返回的是对数几率而不是概率。- 来自分类模型的预测将是概率,而不是实际的标签。现在,我们将通过选择 0.5 作为阈值来进行预测,即 1 的概率超过 0.5 的每个观察值都将被预测为 1。在实际项目中,您会花很多心思来选择正确的阈值。
误差度量
我们在模型中要求两个错误度量:二进制错误和二进制日志丢失。这些指标是在模型训练期间打印出来的。我们也可以使用lgb.get.eval.result
函数来获取它们(用booster = bst
、data_name = “train”
、eval_name = “binary_logloss”
或eval_name = “binary_error”
)。
二进制错误
第一轮后的binary_error
度量是0.08454810
,第二轮后是0.07069971
。这只是数据中不正确预测的比率。例如,在第二轮中,这是实际与预测表:
predict label N
1: 0 0 734
2: 0 1 69
3: 1 0 28
4: 1 1 541
我们可以将误差计算为:
(69 + 28) / 1372 = 0.07069971
我们希望这个度量是一个低值,如果我们让模型训练更多轮次,最终,它将达到 0。(同样,这是因为我们现在没有使用任何规范化。)
binary_logloss
该指标的值在第一轮后为0.5102119
,在第二轮后为0.3966159
。binary_logloss
计算为观察值的单个二元对数损失的负平均值。观察值的二进制对数损失就是目标值为 1 的观察值的预测概率的对数,以及 1 的对数减去目标值为 0 的观察值的预测概率。
我们可以将二进制对数丢失列添加到data.table
中,就像第二轮那样:
然后像这样计算 binary_logloss:
-sum(dt[,binary_logloss_nround2])/dt[,.N] = 0.3966159
当然,第一轮结果也可以重复同样的过程。
预测的叶分数
让我们来看看叶子分数是如何变成预测的!如果我们在第一轮后打印出独特的预测(原始分数和概率),我们会得到以下结果:
predict_proba_nround1 predict_rawscore_nround1 N
1: 0.3373846 -0.9642444 679
2: 0.3702293 -0.7589048 105
3: 0.5472543 0.2708327 37
4: 0.5905585 0.5232494 551
第一列包含实际预测,即概率,第二列是原始分数。
公式
为了将原始分数转换为预测值,我们使用所谓的 sigmoid 函数:
1 / (1 + exp(-0.5232494 * 0.7)) = 0.5905585
与此相反的是 logit 函数:
log(0.5905585 / (1 - 0.5905585)) / 0.7 = 0.5232495
注意事项:
- 上面公式中的
0.7
是我们在训练模型时设置的sigmoid
参数。它在 LightGBM 参数文档中被称为“sigmoid”,但是我找不到关于这个参数的任何数学背景。如果你知道这个参数来自哪里,请给我发消息。嗯,我们知道它的用法,这很了不起!
术语摘要
我发现这里的术语有些混乱,让我快速回顾一下:
- 概率:
p
- 赔率 :
(p/(1–p))
- 对数赔率 :
log((p/(1–p)))
模型中的原始分数是对数比除以 sigmoid 参数。
在树形图中查找分数
这些都很好,但是这些价值实际上来自哪里呢?
如果我们查看lgb.model.dt.tree(bst)
表,可以很容易地通过叶子计数得到它们:这些是第一棵树中的值(tree_index = 0
)。打印出树形图表格的一部分,我们得到这个:
tree_index leaf_index leaf_value leaf_count
1: 0 0 0.5232494 551
2: 0 2 -0.7589048 105
3: 0 1 0.2708327 37
4: 0 3 -0.9642444 679
这与我们之前看到的原始分数一致。
叶分数计算
现在我们知道了指标是如何计算的,分数是从哪里来的,以及分数是如何转换成概率的。我们唯一缺少的是分数本身。
出发点
在模型甚至开始考虑分裂之前,它将计算一个基线预测,所有观察都将从该基线预测开始。
运行lgb.train
功能后,您可以在终端输出中注意到这一行:
[LightGBM] [Info] [binary:BoostFromScore]: pavg=0.444606 -> initscore=-0.317839
我们需要的线索差不多就这些了。训练数据中标签的平均值为0.444606
,因此预测将从以下位置开始:
log(0.4446064 / (1 - 0.4446064)) / 0.7 = 0.3178395
我认为这很简单:在做任何分割之前,任何一个单独的观察都有一个属于第一组的0.4446064
概率。哦,0.7
又是那个讨厌的sigmoid
参数。
第一轮目标
如果我们观察第一棵树,我们会看到它在:
variance = 0.30942
skewness = 7.60565
curtosis = -4.48625
所以在第一轮之后,我们会有四组不同的观察,每组都有不同的预测,基于上面的三个分裂。让我们试着重现这些叶子是如何得分的!
第一轮有点特殊,因为所有的观察仍然有相同的起始分数:我们在上一节看到的0.3178395
。
先说variance <= 0.30942
和skewness <= 7.60565
所在的组。该组有551
个观察值,其中512
的标签为 1,39
的标签为 0。我们还知道这组得到了一个0.5232494
的预测。这就是我们想要复制的数字。
第一轮梯度和 Hessian
我们不能再推迟了,我们必须钻研梯度和黑森计算。你可以从理论的角度试一试,但是我有点困惑。梯度和 hessian 被认为是目标函数相对于预测的一阶和二阶导数,但是我很难将sigmoid
参数放入混合中…
所以我认为最直接的方法就是看一下 LightGBM 的 源代码 。我们需要的位在GetGradients
函数中。(一要小心,label
这里跟你原来的目标不一样。对于目标为 1 的观测值,label
为 1,但对于目标为 0 的观测值,label
为-1。为了避免混淆,我将我们想要预测的值称为目标。)
在这个术语中,这个分数是当前的预测,将是我们在第一轮所有观察中的0.3178395
。梯度和 hessian 的主要构件是所谓的response
值。
- 对于目标为 1 的观测值,
response
计算如下:
-1 * 0.7 / (1 + exp(1 * 0.7 * -0.317839)) = - 0.3887755
- 对于目标为 0 的观察值,响应计算如下:
1 * 0.7 / (1 + exp(-1 * 0.7 * -0.317839)) = 0.3112245
从这些值中,我们可以很容易地计算出这个组的梯度和 hessian:我们只需将观察值的各个梯度和 hessian 相加。
根据这些值,新的叶子分数是这样计算的:
- (gradient / hessian) * 0.3 + (-0.317839) = 0.5232497
注意:
- 上面公式中的
0.3
就是learning_rate
。 512
和39
是受检组中目标值为 1 和 0 的观察次数。- 注意我们是如何将起始共享预测
-0.317839
添加到leaf_score
中的。这可能是特定于 LightGBM 的事情…本质上,总分数因此在第一轮中被滚动到叶分数中。在随后的回合中,不需要公式的这一部分。我们将为第 2 轮做一个例子来看看区别。 - 如果我们有权重,这些公式会更长。我不想让事情变得不必要的复杂,请参考源代码进行计算。请记住,这里有两个可能的权重:
label_weight
是您对正确预测目标 1 或 0 的重视程度,而weights[i]
是指您通过观察定义的权重。
第二轮目标
看看能不能把这个知识转移到第二棵树上。如果我们观察树的结构,我们可以看到,与第一个不同,这个结构是不对称的:第一次分裂的一个节点变成了一片叶子。我们会观察这个群体。通过打印variance > 0.77605
所在组中唯一的predict_rawscore_nround1
和predict_rawscore_nround2
,这就是我们得到的表格(具体如何得到表格,请参考我的 GitHub repo ):
label predict_rawscore_nround1 predict_rawscore_nround2 N
1: 0 -0.9642444 -1.4947746 575
2: 0 0.2708327 -0.2596975 8
3: 1 -0.9642444 -1.4947746 23
4: 1 0.2708327 -0.2596975 20
第一列和第二列之间的差异是一个常数,-0.5305303
,这就是我们在树形图中看到的该组的叶分数。这就是我们想要复制的数字。
第二轮渐变和 Hessian
与第一轮计算的唯一区别是,我们不再有一个共同的预测(如上表所示),因此计算时间有点长。请注意,我按组重新计算一切,以更好地展示正在发生的事情,但从技术上讲,您可以单独计算每个观察值的梯度和 hessian,然后将它们相加。
根据这些值,叶分数值计算如下:
- (gradient / hessian) * 0.3 = -0.5305302
摘要
在这篇文章中,我们重新计算了 LightGBM 在进行二进制分类时计算的指标、得分和预测。
我认为从这样的练习中得到的主要收获是对超参数如何影响模型训练的更深理解。对learning_rate
做了什么有一个大概的了解是一回事,自己重新计算这些结果是完全不同的另一回事。
我不是说像 XGBoost,LightGBM 等型号。在任何方面都很简单。我们没有触及非常重要的一点:分割点是如何确定的。这些决定是最重要的,这些车型在这方面实现的性能提升确实令人惊讶**。然而,我的观点是,一旦这些分裂点被确定,大部分的工作基本上只是一长串的算法,基于你,用户设置的超参数。**
https://matepocs.medium.com/membership
理解假设检验
假设检验
假设检验的基本原则
假设检验是数据科学领域中使用的一个重要的数学概念。虽然从 python 库中调用一个随机方法来为您执行测试确实很容易,但是了解幕后实际发生的事情既必要又有趣!什么是假设检验,我们为什么需要它,它有什么作用,它是如何工作的?让我们来了解一下!
什么是假设检验?
假设检验是一种统计方法,用来确定你的假设是否成立。假设可以是关于数据集中的两个变量、两个群体之间的关联或一种情况。
该方法评估两个互斥的语句(不能同时发生的两个事件),以确定哪个语句最受样本数据支持,并做出明智的决策。
假设检验的组成部分是:
- 假设—无效和替代
- 统计测试
- 概率分布
- 检验统计量
- 临界值
- 显著性水平(α)
- p 值
假说。
假设检验的过程包括两个假设——一个无效假设和一个替代假设。
- 零假设是一种假设两个变量之间没有关系、两个组之间没有关联或者当前情况没有变化的陈述,因此为“零”。它由 H0 表示。
- 替代假设与零假设相反,因为它假设两个变量之间存在某种关系,或者当前情况中存在某种变化——因此称为“替代”。用哈或 H1 来表示。
假设检验的结论是零假设是不被拒绝(所以你接受没有变化)还是被拒绝(所以你拒绝没有变化)。
我们用一个例子来理解这个。
有一个数据集包含了过去一年中住院病人的信息。该数据集中的两个特征是位置和总花费。我们相信住院期间的总花费会因一个人的居住地而有所不同——这种说法是你的替代假设,因为它假设了地点和住院花费之间的关系。
因此,无效假设将是基于一个人的居住地在医院花费的金额没有差异。
现在我们需要弄清楚我们是否应该拒绝零假设。我们如何做到这一点?
统计检验。
统计检验通过为我们提供来自样本数据的检验统计量(单个数值)来帮助我们拒绝或不拒绝我们的零假设。该检验统计量测量样本数据与原假设一致的程度——如果一致,则原假设不被拒绝,否则被拒绝。
对于不同的用例,存在多种统计测试。选择哪种统计检验来使用假设检验取决于几个因素,例如样本的分布(是否正态分布(遵循正态分布))、样本大小、方差是否已知、您拥有的数据类型以及其他一些因素。基于测试遵循的概率模型/分布,每个测试都有自己的测试统计。
比如说—
作者图片
统计检验有两种类型——单尾和双尾。
尾部的概念与概率分布相关。
概率分布是一种统计函数,其输出是实验中不同事件(可能的结果)发生的概率。
例如:
概率分布有数据和图表两种形式,但图表是理解尾部的更好方式。
作者图片
分布的尾部是分布两侧曲线/图形的末端。右端称为右尾或上尾,左端称为左尾或下尾。
一个分布没有必要在所有情况下都有两个尾部,它可以只有一个。尾巴的数量取决于你的无效假设和替代假设。
因此我们有单尾分布和双尾分布。
作者图片
因此,单尾统计检验是指其分布只有一个尾——要么是左尾(左尾检验),要么是右尾(右尾检验)。双尾统计检验是指其分布有两个尾部——左边和右边。
统计检验中尾部的目的是看得到的检验统计量是在尾部之内还是之外。尾部区域被称为拒绝的区域——这是有意义的,因为如果测试统计落在这个区域内,则零假设被拒绝。如果它落在它之外,则接受零假设。
确定尾部从哪里开始是借助于一个称为显著性水平的参数——alpha。它是当零假设为真时拒绝零假设的概率。alpha 最常见的值是 5%,即 0.05。这意味着你面临着拒绝零假设的 5%的风险,也就是说,当实际上没有差异时,你相信当前的情况有差异。
就我们的分布而言,alpha 是构成拒绝区域的分布区域,因此它是尾部。因为尾部在分布的末端,我们实际上是在说,在显著性水平下,我们的检验统计量离零假设有多远,才能真正拒绝零假设。
因此,如果我们选择一个右尾检验,我们的显著性水平是 5%,那么曲线右端分布区域的 5%将是我们的拒绝区域。利用这一点,我们计算 x 轴上的值,该值界定了剔除区域的开始位置,这就是所谓的临界值。
作者图片
在双尾检验的情况下,我们将显著性水平除以 2.5%,这将是曲线两侧每个拒绝区域的面积。那么我们将有两个临界值,拒绝区域也称为临界区域。
作者图片
单尾检验是有方向性的,因为只有一个拒绝区域。您正在寻找一个属于唯一拒绝区域的测试统计。双尾检验是无方向性的,因为有两个拒绝区域,如果检验统计量落入其中任何一个区域,则零假设被拒绝。
例如,牙医诊所患者的样本平均年龄是 18 岁,这是您的零假设(因为这是当前的情景,并且您假设没有变化)。另一位牙医加入了这一行业,你认为现在患者的样本均值将大于 18——这是你的另一种假设。由于“大于”,它是有方向性的——只有当样本均值大于 18 时,您才会拒绝零假设。如果小于 18,你的零假设不会被拒绝。
如果这是一个双尾检验,你的另一个假设就是患者的样本平均年龄不等于 18 岁。样本均值可能大于或小于 18,无论哪种情况,零假设都会被拒绝,因此它是无方向性的。
到目前为止,我们已经有了假设,一个基于概率分布的统计测试,一个我们从中获得临界值的拒绝区域的显著性水平,以及一个落在我们分布中某处的测试统计。
但是, p 值从何而来?
如前所述,测试统计与临界值进行比较。p 值与显著性水平进行比较。
p 值是您获得的检验统计数据是随机的概率,这意味着零假设实际上是正确的。如果 p 值低于显著性水平,则它在统计上是显著的,无效假设被拒绝,因为这意味着无效假设为真的概率小于 5%。如果它高于显著性水平,则不拒绝零假设。
根据概率分布的图形表示,p 值是检验统计右侧的曲线面积。
作者图片
p 值的一个很大的优点是,它不需要针对不同的显著性水平进行重新计算,因为它是直接可比的。在检验统计的情况下,必须将它们与临界值进行比较,临界值需要针对不同的显著性水平进行重新计算(如果剔除区域的面积发生变化,则区域起点的 x 轴值也将发生变化)。这就是为什么在假设检验中使用 p 值总是更好的原因。
尽管有上述优点,p 值仍然是概率性的。因此,即使无效假设是真的,也有可能被拒绝。Alpha(显著性水平)是这种情况发生的可能性。这种错误被称为I 型错误或假阳性。
与上述错误相反的是,即使无效假设不成立,也不拒绝它。这被称为类型 II 错误或假阴性。
这两个误差是反向相关的,也就是说,如果你试图减少一个,另一个就会增加。那么如何获得完美的情境呢?你不需要,你只需要决定在你的情况下哪个错误风险更小。例如,当他们实际上是阴性的时候,告诉某人他们是新冠肺炎阳性的风险较小——这是假阳性(I 型错误)。假阴性是说某人没有表现出欺诈行为,而实际上他们表现出了欺诈行为。
最后,我们对假设检验的所有构件进行了分类,因此这个过程也非常清楚。假设检验的步骤是:
- 阐明你的假设
- 选择重要程度
- 识别统计测试
- 计算测试统计量
- 计算 p 值并将其与显著性水平进行比较
- 结论——拒绝或不拒绝你的无效假设
让我们通过上面使用的关于看牙医的患者平均年龄的例子来完成这些步骤。我们知道平均年龄是 18 岁,标准差是 8。让我们假设数据服从正态分布。我们认为平均年龄不是 18 岁,但实际上比 18 岁大。
作者图片
因为我们的替代假设有方向性,我们需要一个右尾统计检验。
我们正在比较样本的平均年龄,因此我们可以使用 z 检验来确定我们的替代假设是否可能。
我们的显著性水平是 5%,我们正在进行右尾检验,因此曲线的右尾是拒绝区域。
我们的样本数据是随机抽取的 30 名患者,我们计算样本的平均年龄。
所以我们有:
作者图片
现在我们需要计算我们的测试统计量,在 z 测试的情况下是 z 得分。z 得分的公式为
作者图片
所以我们的 z 值是
作者图片
α为 0.05 的正态分布的临界值为 1.645。因此,我们已经可以看到,我们的测试统计大于我们的临界值,因此,我们可以拒绝零假设。但如前所述,p 值是确定我们是否应该拒绝零假设的最佳方法,所以我们也来计算一下。
在正态分布上得到值 2.05 的概率是 0.9798。由于 p 值是检验统计数据右侧的区域,因此可以通过从 1 中减去 0.9798 来计算。
作者图片
p 值 0.0202 小于 0.05,因此我们可以拒绝零假设,并得出结论,样本均值更有可能大于 18。
现在让我们用 python 来实现所有这些吧!
我们将使用新冠肺炎数据集。它包含 2020 年初以来的区域确诊病例和死亡病例。它还包含温度、湿度、纬度和经度。
第一步是导入所有必需的包:
import pandas as pd
import statsmodels.api as sm
我们将使用熊猫来读取我们的数据和 stasmodels 来实际执行我们的假设检验。
将数据读入数据帧:
covid = pd.read_csv(“Covid-19.csv”)
covid = covid.drop([“Unnamed: 0”], axis=1)
covid.head()
我认为温度和确诊的新冠肺炎病例数之间有关系——确诊新冠肺炎病例的地区的平均温度是否高于 12 度?
假设检验的第一步是阐明假设。我们的会是:
作者图片
由于我们的替代假设是方向性的,我们将进行单尾统计检验(右尾检验),即 z 检验。
显著性水平为 0.05,检验统计量为 z 得分,临界值为 1.645。
使用 statsmodels ztest 方法计算测试统计量和 p 值非常简单。让我们将这个功能包装在我们自己的函数中,并添加 alpha 和 p 值之间的比较。
def hyptest(data, value, alternate, alpha):
z, p = sm.stats.ztest(data, value=value, alternative=alternate)
result = 'null hypothesis rejected!' if p < alpha else 'null hypothesis not rejected'
return z, p, result
现在我们调用我们的函数—传递温度列(因为这是我们的假设中存在的变量),数字 12 作为值,因为这是我们要测试的度量,以及“更大”的替代参数,因为我们的替代假设是平均值大于值。
hyptest(covid.Temperature, 12, "larger", 0.05)**(1.0033989581188794, 0.15783420330389508, 'null hypothesis not rejected')**
结果分别是 z 得分、p 值和结论。由于 p 值大于 0.05,我们不拒绝零假设。
这就是你如何进行假设检验!
需要记住的一些关键事项是:
- 假设检验是对总体样本进行的,因此是样本数据。(如果你想确定年龄是否会影响入院的可能性,那么“人口”就是地球上的每一个人。“样本”是从这个群体中随机选择的一些人)
- 假设检验的输出是概率性的,因为它是对样本而不是总体进行的。所以你永远不能完全肯定地说零假设是真的还是假的。你干脆拒绝或者不拒绝。
- 零假设总是被写成等式=,≤或≥
- 备择假设总是写着≦,< or >
- p 值受样本大小的影响-如果样本大小增加,那么如果要拒绝零假设,p 值将会降低。
请在下面的评论中告诉我你对这篇文章的想法,或者通过林克丁联系我,我们可以联系上!
原始数据取自约翰·霍普斯金的 GitHub 库,然后经过修改得到该地区的温度。用于执行测试和创建图表的代码可以在这里找到。
理解卡夫卡,就像是你设计的一样——第二部分
从内向外理解阿帕奇卡夫卡式建筑
这是旨在解释卡夫卡的关键建筑元素系列的第二部分。这个想法是提供一个关于它的设计决策和内部的直觉。与消息传递语义、生产者和消费者的配置、安装、安全性等相关的细节将留到以后的文章中讨论。
还有一个重点是可读性和趣味性,所以如果你想在学习的同时获得乐趣,请继续读下去。
以前,你负责开发一个解决方案,帮助你的一个朋友将一个革命性的天气预报模型变成现实。他有了模型,但接收测量数据、存储、处理和大规模管理它们仍然是一项艰巨的任务。尽管如此,您还是推出了一款工具,它能够:
1。 接收包含任何格式的记录并无缝存储它们
2。 几乎实时地按到达顺序处理这些记录
3。 以非常高效的方式从任何特定偏移或时间查找记录
4。 以最佳方式删除记录
你甚至去度假庆祝这一壮举。然而,你朋友的一些问题仍然悬而未决,当你欣赏阿尔卑斯山倒映在湖面上的可塑性时,你几乎无法将它们从脑海中抹去:
- 如何支持多个城市?是否有必要为每个新城市准备一个基础设施?
- 如何支持多个并行读取器和写入器?
- 如何确保数据的持久性,例如在磁盘出现故障后?
- 如何在读写器全速运行的情况下备份数据?
我们回去工作吧。
继续分…
现在,您必须为多个城市提供您的解决方案,每个城市都有自己的一套测量方法。
第一个显而易见的解决方案是多次部署您的平台,每个城市一次。但是它足够可维护吗?大概不会。让每个城市给它产生的记录附上一个 id 怎么样?听起来很有趣,但是这需要截取所有记录,分配 id,然后分派修改后的记录,由于错误的 id、失败的截取等原因,这很容易出错。这个问题开始看起来很棘手。
然而,你已经学到了“分而治之”这一永恒的教训,也许这能有所帮助。如果分储呢?更确切地说,在当前状态下,所有记录都被发送到同一个目录。如果您可以创建多个目录,并决定消息应该发送到哪个目录,会怎么样?如果能够做到这一点,每个城市的名称都可以成为一个目录,这将使相同的部署服务于多个城市成为可能,并具有适当的数据隔离。
您匆忙实现了这个非常有前途的想法,但是在编码时,您意识到“目录”是一个已经使用的名称,也就是说,它在多种上下文中使用,如果有一个专门为这个工具定制的概念就好了。您也开始考虑该工具能够服务于大量用例,因此需要一个更通用的名称。你的推理是这样的:
人们或其他系统向平台发送消息,而平台则充当将消息传递给接收者的管道。这些消息与一个主题相关联,所以我可以将每个存储目录命名为 主题 。但是这个名字也在其他语境中被广泛使用。所以也许是一个相关的术语,比如话题?哎…是啊, 题目 !
从尤利卡来的!瞬间,你准备好了更多:
好吧,但是无论是谁向一个主题发送消息,都不仅仅是简单地写它,更一般地说,是产生它。同样,一个人不只是阅读信息,而是消费它。所以实际上,我们确实有 生产者 向主题发送消息和 消费者 获取它们。
您如何通过一个部署支持多个流程(图片由作者提供)
…一次又一次…
方向看起来完全正确。您刚刚创建了 3 个新概念:生产者、消费者和主题。通过允许在同一个平台上有多个主题,您还允许多个生产者和消费者共享同一个部署。然而,还有两个问题需要解决。首先还是和命名有关。第二个与可伸缩性有关。你从最简单的开始。
将整个系统称为“平台”听起来不太合适。你希望是通用的,但不要太多。平台可以是任何东西,你所拥有的是一个促进双方交流的系统。在“现实世界”中,这种类型的服务被称为经纪。那么,为什么不把保存主题的系统称为代理?问题一解决了。
第二个问题有点复杂。在大多数情况下,消费消息可能比生成消息更耗时。因此,能够横向扩展消费至关重要。
一个可能的解决方案是多线程消费者。一个线程(让我们称之为分配器)将使用来自代理的消息,并将负载分配给其他线程。但是如果单个节点不够呢?如果需要在多台机器之间分配负载,该怎么办?在这种情况下,您需要更改分发服务器以支持网络通信。然而,如果添加了足够多的节点,以至于分发服务器本身成为瓶颈,该怎么办?
避免诸如分发服务器这样的瓶颈的最直接的方法之一是允许消费者节点直接从主题消费,而不需要任何类型的中介。然而,主题中的条目是通过偏移量来处理的(这里是您开发的)。如何协调每个消费者正在处理哪些补偿?分而治之再次拯救(你开始认为这种方法是一个真正的银弹)。
您将代理划分为多个主题,每个主题都有其偏移量跟踪。如果你将主题进一步划分为子主题,并允许每个子主题都有自己的偏移量,会怎么样呢?然后,每个消费者可以从不同的子主题中获取,扩展系统就像创建更多的子主题和添加更多的消费者一样简单。另一方面,生产者将向代理查询主题的子主题,并以循环方式向每个子主题发送消息。美女!
但是,命名还是一件事。将新概念命名为副主题有点误导,因为它暗示了一个新的主题,尽管仍然与主主题相关。但是你没有一个新的主题,你只有相同主题的划分或分割。由于你有很多事情要做,你决定尽快解决它,并将子主题重命名为分区。
可以并行使用的分区主题(图片由作者提供)
最后,举一个反例来各个击破
那栋建筑看起来真的很棒,你为此感到骄傲。但是,如果一个消费者死了会怎么样呢?!因为生产者在分区之间平均分配数据,所以如果一个消费者死亡,消息的处理就变得不完整,因为整个分区都没有被处理。
你的思维习惯于分而治之,你会挣扎一会儿,直到这次决定走相反的路。如果消费者在元数据级别上分组会怎样?更具体地说,如果系统的另一个组件开始跟踪每个分区分配了哪些用户,以及最后使用了哪个偏移量,会怎么样?然后,如果一个消费者死亡,可以查询相同的组件,新的消费者可以从上一个消费者停止的地方开始。有了这个新概念,消费者不再单独工作,而是分组工作。那么,还有什么比消费者群体更好的名字呢?当然,如果一个使用者组中的使用者比主题中的分区多,一些使用者将被闲置。
现在消费者可以被无缝替换(图片由作者提供)
但是哪个组件会跟踪消费者群体呢?是否应该添加一些外部的东西,以便消费者在工作时可以接触到它?听起来不错,但是这样做也增加了新的依赖性,需要维护、监控等。但是将这种能力添加到代理本身怎么样呢?消费者已经联系了代理,所以它知道哪个消费者正在每个分区上工作。然而,经纪人不太了解消费者本身,也就是说,他们正在用他们正在消费的记录做什么。如果消费者从一个给定的偏移量读取一条记录,然后立即死亡,那该怎么办?代理可以假定记录已被处理,还是应该将相同的记录交给新的消费者来代替死去的消费者?
为了解决这个难题,您决定为消费者和代理增加一个额外的可选步骤。获取记录后,消费者可以通过发送提交消息来告诉代理消费是否成功。如果没有收到消息,代理将假设处理失败,因此,当要求新记录时,它将返回与以前相同的记录。另一种可能性是让经纪人设定提交的最后期限。如果消费者在给定的时间内没有联系,代理将认为消费是成功的,当被要求提供新的记录时,将提供与下一个补偿相关的记录。
当考虑这种机制时,你会意识到它实际上可以被设计成一种配置。在上面提到的第二种情况下,代理会自动提交。在第一种情况下,你有相反的行为。因此,您决定添加一个名为使能的配置。自动提交,可以设置真或假。
最后一次分裂
这真是太棒了。该系统现在可以平衡多个代理之间的负载,从而允许并行消费。现在,扩展系统就像添加一个新的代理一样简单。更好的是,如果一个消费者死了,一个新的可以开始,在被添加到消费者组后,它将准确地从上一个停止的地方开始。但是如果整个经纪人都死了会怎么样呢?我的意思是,从制作人的角度来看,这很容易,制作人可以从目的地列表中删除该代理,并继续向剩余的代理发送记录。但是,如果记录仍然没有被使用,会发生什么情况呢?太棒了,一个系统不能容忍数据丢失的可能性。
你首先想到的是这样一个想法:弹性通常来自冗余。如果你想承受失去某样东西,你首先需要它的复制品。在这种情况下,你希望能够失去经纪人,并保持系统的工作。然而,如果您失去了一个代理,您并没有失去一切,只是失去了一些分区。因此,如果您复制一个代理中的所有分区,该代理很容易被包含副本的分区所取代。为此,您需要做的就是决定哪个代理拥有主分区,哪个包含副本分区。一旦有了这些,生产者就可以写入主分区,另一种机制可以将记录复制到副本。前途无量!
但是,在开始编码之前,和往常一样,您需要为新概念指定合适的名称。乍一看,“主分区”和“副本分区”听起来还行。然而,副本是与其他东西相同的东西,在这种情况下,副本在某些时候可能不相同。当主分区接收到记录时,它变得不同于它的副本。没错,复制品将会跟上,但在赶上之前,它们不再是复制品。但是,嘿…这些复制品实际上是在跟随,所以把他们每个人命名为追随者听起来是对的。追随者跟随一个领导者,所以你有它。主分区,从现在开始,将被称为首领。
主题分布在分区中,分布在领导者和追随者中(图片由作者提供)
这个解决方案看起来像万灵药,但是有一个潜在的问题:如果复制过程失败了会发生什么?当然,您已经添加了消息提交的概念,但是如果您提交了书面记录,但是将记录复制到跟随者失败了,会发生什么呢?
与你已经经历的相比,这是一个更容易的问题。在这种情况下,您只需要添加一个规则,指定记录写入只能在成功复制到最小数量的跟随者之后提交。
这种设计还有一个优点。如果您将关注者添加到一个组(类似于消费者组),您可以跟踪他们中哪些人落后了。然后,如果一个领导者失败了,你可以从与前一个领导者同步的群体成员中选出新的领导者。这个组甚至可以作为系统的标尺,例如,通过指定至少一定百分比的追随者在任何时候都应该同步。这个小组的好名字是什么?好吧,这一次,既然它们是同步的,就可以认为是复制品。那么,为什么不使用同步副本集,或者简称为 ISR ?
开个玩笑,这是真正的最后一次分裂
你完成了实现,并急于向你的朋友展示新的特性,他完全被你的独创性惊呆了。你感到受宠若惊和兴奋,但当他告诉你其他城市的研究人员刚刚告诉他,今天早些时候,他们需要按街道分类的测量值时,这种感觉几乎消失了。
当前的解决方案不玩排序,你也看不到一个简单的方法来做到这一点。你的第一个想法是让每条街道上的传感器给它们产生的信息附加一个 id 和一个序列号,然后在读取时对记录进行分类。但是,这将需要大量的内存用于聚合,或者如果磁盘必须用于随机访问,则会影响性能。还有,散兵游勇怎么办?您应该保持这种状态并等待他们到达多长时间?事情开始变得过于复杂。要是你能保证同一条街上的传感器产生的记录最终能在同一个分区里就好了……但是,嘿,你不能吗?
如果记录按 id 和值划分,您可以更改生成器,强制它们将包含相同 id 的所有记录发送到同一个分区。这样,无论它们产生的顺序是什么,它们都将在同一个分区中进行排序。这样做还有更大的好处,因为通过对消息进行自然分组可以加快处理速度。在定义键没有意义的场景中,生产者只是继续做他们已经在做的事情,以循环的方式向主题分区发送消息。答对了。除了名字,一如既往。将成对的事物称为 id 和值并不常见,但是键和值完全没问题,所以就这样了。
包含相同键的记录现在被发送到相同的分区(按作者排序)
完整的画面
这就是了!将您之前开发的与您现在创建的结合起来,您将获得以下结果:
- 能够随着新硬件的增加而水平扩展的分布式平台
- 与模式无关的存储系统
- 一种通过利用页面缓存和顺序读写来支持极高吞吐量同时仍使用磁盘的机制
- 通过将主题分割成分区并创建消费者组,并行消费记录并在失败时平滑追赶的策略
- 通过在代理之间复制分区来避免数据丢失的弹性方法
- 一种通过监控同步副本来保证系统健康的机制
- 一种通过使用键来保证分区内记录顺序的方法
每一个听到这个解决方案的人都同时感到吃惊和惊讶。每一个人也询问它将被赋予的名字,而这一次你不知道。只有你,命名怪胎!
你必须承认的一点是,你创建的解决方案看起来有点超现实。它与模式无关,在使用磁盘时提供高吞吐量,在作为分布式系统时保证某种排序,允许代理无缝地来回切换,等等。
你记得你坐在阿尔卑斯山上读的一本书,关于一个人试图到达一个不可能的城堡,一个永远无法到达的遥远的愿景,正如你的设计元素。无论你如何达到它。此外,你喜欢那本书,因为它给了你一段美好的时光,那么为什么不用它来命名系统呢?但是将一个平台命名为城堡并不合适。那么作者呢?是啊,听起来绝对不错。从现在开始,当人们问起,你会告诉他们你的系统叫做卡夫卡。
参考
2.本系列的第一部分
你给这个世界的能力
通过 Python 中的情感分析理解语言
使用 TextBlob 的自然语言处理能力
普里西拉·杜·普里兹在 Unsplash 上的照片
为了将数据转化为信息并发现原始数据的真正好处,开发人员、企业、机构、大学甚至犯罪分子对数据科学领域产生了浓厚的兴趣。在我们知道之前,通过代码模仿人脑功能的能力已经向我们展示了分析语言的能力是推动社会和人类进步的最重要的方法之一。由此,数据科学、机器学习和自然语言处理的发展已经被实施到消费者每天使用的工具中,如 Siri、Alexa 和 Cortana。
开源数据和数据处理的发展已经引导了稳健方法的使用,以将数据用于自然语言处理、情感分析和确定情绪。数据现在是下一个淘金热,因为我们开始了解数据需要如何提取、转换、加载,并为了充分的利益而转化为信息。理论上,和黄金一样,数据也是一种商品。
在本文中,我计划让您对利用编程工具在社交媒体网络中使用 Python 进行情感分析有一个基本的了解,以及如何利用基于文本的数据分析能力,使用基于计算机的代码来理解真实的情感。
介绍 TextBlob
作为 Python 的 NLP 库, TextBlob 已经成为开发人员的首选包之一。
TextBlob 是一个用于处理文本数据的 Python (2 和 3)库。它提供了一个简单的 API,用于处理常见的自然语言处理(NLP)任务,如词性标注、名词短语提取、情感分析、分类、翻译等。— TextBlob 的网站
使用 TextBlob 理解情感分析
[**sentiment**](https://textblob.readthedocs.io/en/dev/api_reference.html#textblob.blob.TextBlob.sentiment)
属性返回一个形式为Sentiment(polarity, subjectivity)
的命名元组。极性分数是[-1.0,1.0]范围内的浮点数。主观性是在[0.0,1.0]范围内的浮动,其中 0.0 非常客观,1.0 非常主观。— TextBlob 快速入门教程
TextBlob 返回句子的极性和主观性。
极性是介于[-1.0,1.0]范围之间的浮动值。
- -1 表示负面情绪
- 1 定义积极的情绪。
主观性介于[0.0,1.0]之间。
- 0.0 很客观,1.0 很主观。
- 如果主观性< 0.5, the sentence is more subjective than objective and vice versa.
TextBlob also provides labels for semantics which allow for the detection and analysis of emojis, tones of sentence like exclamation marks, and more.
Installing From the Python PyPI
$ pip install -U textblob
TextBlob is available as a 康达包。要使用conda
安装,请运行
$ conda install -c conda-forge textblob
Python 中的 TextBlob
首先,在你首选的笔记本编译器中导入*textblob.*
。
然后,我们需要做的就是使用TextBlob(text)
,以便利用 TextBlob 的许多不同方法。
**from** textblob **import** TextBlobblob = TextBlob(text)
一旦你创建了一个 textblob 对象,你就可以访问普通的文本处理操作。我将把 TextBlob 对象视为 Python 字符串,以展示 NLP 在情感分析等方面的强大功能。
使用 textblob 分析情感
为了使用 textblob 执行情感分析,我们必须使用如下所示的*情感()*方法;
blob = TextBlob('companies that specialize in sentiment analysis are least likely to look at for data.')
blob.sentiment
结果
Sentiment(polarity=-0.15, subjectivity=0.7)
有益的补充
拼写纠正
在我们分析大型数据集的用例中,单词的拼写错误可能会对数据产生不利影响,因此纠正单个单词的拼写非常重要。
tutorial = TextBlob("in sentimet analyss, which of the following is an implicit opinion?")print(tutorial.correct())
结果
in sentiment analysis, which of the following is an implicit opinion?
名词短语抽取
名词短语是以一个名词为中心的一组两个或多个单词(例如,“狗”、“女孩”、“男人”),包括修饰语(例如,“the”、“a”、“None of”)。例如,“男孩”不是一个名词短语,但“一个乐于助人的男孩”是一个名词短语。
通常在数据科学中,研究人员可能希望提取一个句子中的所有名词短语,而不是确定单个名词。
blob = TextBlob('I wanted to learn cryptocurrency sentiment analysis.')
blob.noun_phrases
结果
WordList(['cryptocurrency sentiment analysis'])
正如我们所看到的,只有“情感分析”是从句子中提取的,因为它是句子中唯一的名词短语。
结论
最常见的情况是,公司雇佣开发人员来分析大量数据,以获得原始数据,这些数据随后可以发现诸如消费者或客户对产品或体验的感受之类的事情。公司可以通过基于正则表达式的文本识别和抓取基本网页来识别电话号码、个人姓名、位置和其他特定实体。对语言学概念有更深入的理解,如部分词、中间词、原生词、同义词、词干、词汇化、词类、词义消歧和类似领域。
我希望这篇文章能帮助您发现自然语言处理(NLP)的强大之处,并提供一些在您自己的环境中进行测试的例子。
感谢您阅读
欢迎建设性的批评和反馈。可以在 Instagram @nirholas 、LinkedIn **、**和 Twitter @nickresendez 上联系到 nickresendez,了解新文章的更新。
理解 R 中的线性回归输出
自信地解释您的模型与数据的吻合程度
克里斯·利维拉尼在 Unsplash 上的照片
无论是业余爱好者还是专业人士,回归都是一种非常普遍的分析形式。这是为什么呢?因为它是理解变量之间关系的最强大的工具之一。此外,它还允许我们对以前看不见的数据进行预测。大多数人都上过统计学课程,运行过简单的线性回归模型。我猜想,大多数人,给定一些模型输出,可以挑选出 y 截距和可变系数。虽然这些信息非常重要,但是我们运行模型时返回的所有其他数据呢?
还有其他我们应该考虑的事情吗?其他值告诉我们什么?
数据
我们将深入研究上述每个指标,目的是深入理解每个指标告诉我们关于模型的什么,而不是如何计算每个数字的具体细节。为此,我们将使用来自国家篮球协会(NBA)的数据集。该数据集包括 2017-2018 赛季每个球员的工资信息和赛季得分。我们将调查一个赛季的得分和球员工资之间的关系。我们可以通过看一个球员得了多少分来预测他的薪水吗?下面是数据集的预览:
简单线性回归输出
我们首先运行一个简单的回归模型,将薪水作为我们的因变量,将积分作为我们的自变量。该回归模型的输出如下:
现在我们有了模型和输出,让我们一步一步地浏览这个输出,这样我们可以更好地理解每个部分,以及它如何帮助我们确定模型的有效性。
打电话
call 部分向我们展示了 R 用来拟合回归模型的公式。薪水是我们的因变量,我们使用来自 NBA 数据集的点数作为预测值(自变量)。
残差
残差是实际值和预测值之间的差值。我们可以通过获取薪金的实际值并将其从模型的预测值中减去来生成这些相同的值:
summary(nba$salary - model$fitted.values)
那么我们该如何解读这一点呢?嗯,仔细想想,我们肯定希望我们的中值以零为中心,因为这将告诉我们,我们的残差在某种程度上是对称的,并且我们的模型在数据集的高端和低端都进行了均匀的预测。看看上面的输出,看起来我们的分布并不完全对称,并且稍微向右倾斜。这告诉我们,我们的模型在高工资范围的预测不如在低工资范围的预测好。我们可以用分位数-分位数图来形象化这一点。看下面的图表,你可以看到图表两端都有异常值,但是上端的异常值比下端的异常值更严重。总体而言,残差看起来具有相当正态的分布。
系数
为了理解系数是什么,我们需要回到我们在建立线性模型时实际尝试做的事情。我们希望以 y=mx+b 的形式建立一个通用模型,其中 b 是截距, m 是直线的斜率。因为我们通常没有足够的信息或数据来知道实际存在的确切方程,所以我们必须通过对斜率和截距进行估计来建立这个方程。这些估计值通常是通过普通的最小二乘法生成的,这是一种奇特的说法,即回归模型以这样一种方式找到拟合这些点的直线,即最小化每个点和直线之间的距离(最小化实际值和预测值之间的平方差总和)。
系数—估计值
我们就是从上面这条线得到系数的。直线与 y 轴相交的地方是我们的截距( b ),直线的斜率是我们的 m 。使用我们到目前为止获得的理解,以及上面输出中提供的系数估计,我们现在可以为我们的模型建立方程。我们将用点代替 m ,用*(截距)*代替 b:
y = 10,232.50 美元(x)+1,677,561.90 美元
现在我们有了这个等式,它告诉了我们什么?嗯,作为一个基线,如果一个 NBA 球员在一个赛季中得分为零,那么这个球员的平均收入是 1,677,561.90 美元。然后,他们在赛季中每获得一个额外的分数,他们将获得 10,232.50 美元。
让我们将此应用于数据集中的一个数据点。詹姆斯·哈登是我们数据集中的第一名球员,得到了 2376 分。使用我们的公式,我们得到一个估计值:
$10,232.50(2,376)+$1,677,561.90 = $25,989,981.90
詹姆斯·哈登实际上赚了 2830 万美元,但你可以看到,通过使用模型中的系数估计,我们在这里的方向是准确的。
系数—标准。错误
系数的标准误差是系数的标准偏差的估计值。实际上,它告诉我们,系数有多少不确定性。标准误差通常用于创建置信区间。例如,我们可以在斜率周围设定 95%的置信区间,点:
$10,232.50 ± 1.96($724.90) =($8,811.70,$11,653.30)
查看置信区间,我们可以说我们 95%确信实际斜率在$8,811.70 和$11,653.30 之间。
除了有助于计算置信区间和 t 值之外,它还是检查系数对模型是否重要的快速方法。如果该系数与标准误差相比很大,那么从统计上看,该系数最有可能而不是为零。
系数— t 值
t 统计量就是系数除以标准误差。一般来说,我们希望我们的系数具有较大的 t 统计量,因为这表明我们的标准误差与我们的系数相比较小。简而言之,我们说系数是远离零的 X 标准误差(在我们的示例中,点系数是远离零的 14.12 个标准误差,从统计上看,这相当远)。我们的 t 统计量越大,我们就越能确定系数是而不是零。然后使用 t 统计量来查找 p 值。
系数— Pr( > |t|)和 Signif。代码
p 值是使用 t 分布的 T 统计量计算的。p 值与 t 统计量相关联,有助于我们理解我们的系数对模型的重要性。实际上,任何低于 0.05 的 p 值通常被认为是显著的。当我们说重要时,我们的意思是什么?这意味着我们确信该系数是而不是零,这意味着该系数实际上通过帮助解释因变量内的方差而增加了模型的价值。在我们的模型中,我们可以看到 In cept和点的 p 值非常小。这让我们得出结论,有强有力的证据表明这个模型中的系数是而不是零。
系数代码为我们提供了一种快速的方式来直观地查看哪些系数对模型是重要的。在 p 值的右侧,您会看到几个星号(如果系数对模型不重要,则没有星号)。星号的数量对应于系数部分下图例中所述的系数的重要性。星号越多,意义越大。
剩余标准误差
残差标准误差是模型与数据拟合程度的度量。让我们回到上一节中显示的示例:
如果我们查看最小二乘回归线,我们会注意到该线并没有完全穿过每个点,并且在点和线之间存在“残差”(显示为蓝色线)。残差标准差告诉我们 Y 的实际值(点)与 Y 单位的预测值(线)之间的平均值。通常,我们希望残差标准差尽可能最小,因为这意味着我们模型的预测线平均起来非常接近实际值。
对于我们当前的模型,我们可以看到,平均而言,实际值与预测值(回归线)相差 630 万美元。现在,了解我们的数据集,知道最大的工资是 2830 万美元,让我们所有的预测平均相差 630 万美元不会产生一个非常准确的模型。
多重 R 平方和调整 R 平方
多重 R 平方值最常用于简单线性回归(一个预测值)。它告诉我们自变量在因变量中解释了多大比例的变化。换句话说,这是确定我们的模型与数据拟合程度的另一种方法。在上面的例子中,点数解释了我们的因变量——薪金中约 37.37%的变化。这意味着点数有助于解释工资的一些变化,但没有我们希望的那么多。最终,我们的模型并没有很好地拟合数据(我们在研究残差标准误差时看到了这一点)。
运行多元线性回归时会使用调整后的 R 平方值,从概念上来说,可以用我们描述多元 R 平方的相同方式来考虑。调整后的 R 平方值显示了所有预测因子解释的因变量中的变异百分比。这两个指标之间的差异是计算中的细微差别,我们通过添加多个变量来调整方差。
需要注意的是,R 值(倍数或调整值)并不简单,不应该仅仅根据值的计算方式来单独使用。例如,当您添加额外的预测值时,调整后的 R 平方值可能会增加,即使它们与您的因变量没有任何关系。
f 统计量和 p 值
当运行简单或多重回归模型时,假设检验正在全局模型上运行。零假设是因变量和自变量之间没有关系,另一个假设是有关系。换句话说,零假设是模型中所有变量的系数都为零。另一个假设是,它们中至少有一个是而不是零。F 统计量和总体 p 值帮助我们确定该测试的结果。根据测试中有多少变量,只看 F 统计量可能会有一点误导。如果有许多独立变量,那么 F 统计量接近于 1,但仍然产生 p 值(我们会拒绝零假设)是很常见的。然而,对于较小的模型,较大的 F-统计量通常表明应该拒绝零假设。更好的方法是利用与 F 统计量相关的 p 值。同样,在实践中,p 值低于 0.05 通常表示模型中至少有一个非零系数。
从我们的模型中可以看出,F 统计量非常大,而 p 值非常小,基本上为零。这将导致我们拒绝零假设,并得出结论,有强有力的证据表明,工资和积分之间确实存在关系。
结论
虽然在试图预测一名 NBA 球员的薪水时,在一个赛季中获得的积分是有用的信息,但我们可以得出结论,单凭这一点还不足以做出准确的预测。
已经完成了 R 中线性回归模型输出的每一部分,您现在已经准备好自信地投入到任何回归分析中。祝你好运!
谢谢大家的支持!
感谢您阅读本文!如果你觉得有帮助,请给我一两下掌声:)
参考
理解 Python 中的列表理解
本文讨论了 Python 中的列表理解,以及如何使用它们来提高代码的效率和 Python 性。
列表理解帮助你用最少的代码(通常只有一行代码)执行基本的列表操作。这使得您的代码高效且 Pythonic 化。让我们看一个例子来使列表理解的概念更清楚。
让我们创建一个从 0 到 9 的整数列表,并将列表中的每个元素乘以 2。这可以通过使用 for 循环遍历列表中的每个元素并将其乘以 2 并将其追加到一个空列表中来实现。
x = list(range(10))
x
作者图片
x_doubled = []for i in x:
x_doubled.append(i * 2)
x_doubled
作者图片
让我们尝试将变量‘x’乘以 2,就像我们处理 Numpy 数组和 Pandas 系列一样。将 Numpy 数组或 Pandas 系列乘以一个数字,再将它们的每个元素乘以该数字。
x_arr = np.array(x)
list(x_arr * 2)
作者图片
如上所述,将数组乘以 2 等于将数组的每个元素乘以 2。但是列表不会发生这种情况,因为数组和序列的行为类似于向量。让我们将列表乘以 2,看看会发生什么。
x * 2
作者图片
将列表乘以 2 没有得到预期的结果。它通过将列表“x”追加到自身来扩展它。这和 x + x 是一样的。让我们用一行代码来执行这个操作。
[i * 2 for i in x]
作者图片
使用 list comprehension 已经用一行代码执行了所需的操作。让我们看看每个方法执行所花费的时间。我们可以使用“timeit”魔法命令来实现。
%%timeit
x_doubled = []for i in x:
x_doubled.append(i * 2)
作者图片
%%timeit
[i * 2 for i in x]
作者图片
%%timeit
x_arr = np.array(x)
x_doubled = list(x_arr * 2)
作者图片
我们可以看到,与其他两种方法相比,列表理解减少了代码行数和执行时间。让我们更深入地理解列表,看看一些高级操作。
在下面的例子中,我们将尝试只将偶数乘以 2 并返回它们。我们将看到如何使用 for 循环和列表理解来实现这一点。
x_even_doubled = []for i in x:
if i % 2 == 0:
x_even_doubled.append(i * 2)
x_even_doubled
使用 for 循环输出
[i * 2 for i in x if i % 2 == 0]
使用列表理解输出
让我们试着把偶数乘以 2,奇数原样返回。我们将看到如何使用 for 循环和列表理解来实现这一点。我们将在 list comprehension 中使用三元运算符来执行这个操作。
x_new = []for i in x:
if i % 2 == 0:
x_new.append(i * 2)
else:
x_new.append(i)
x_new
使用 for 循环输出
[i * 2 if i % 2 == 0 else i for i in x]
使用列表理解输出
我们试着把偶数乘以 2,奇数原样返回,去掉 0。我们将看到如何使用 for 循环和列表理解来实现这一点。
x_new = []for i in x:
if i % 2 == 0:
x_new.append(i * 2)
else:
x_new.append(i)
x_new.remove(0)x_new
使用 for 循环输出
[i * 2 if i % 2 == 0 else i for i in x if i != 0]
使用列表理解输出
让我们看看嵌套列表的理解。我们将尝试将列表中的偶数乘以 2,并保留奇数。然后,我们将偶数除以 2,得到原始列表。
[int(i / 2) if i % 2 == 0 else i for i in
[i * 2 if i % 2 == 0 else i for i in x]]
嵌套列表理解的输出
让我们试着将两个列表组合成如下所示的嵌套列表。我们将看到如何使用 for 循环、zip 函数和 list comprehension 来实现这一点。
# Method 1: Using for loop
nested_list = []for i in x:
nested_list.append([i,i])
nested_list
使用 for 循环输出
# Method 2: Using zip function
list(zip(x, x))
使用 zip 函数输出
# Method 3: List comprehension
[[i,i] for i in x]
使用列表理解输出
我们可以看到 for 循环和 list comprehension 返回的是列表的列表,而 zip 函数返回的是元组的列表。让我们尝试使用 for 循环和列表理解来创建列表元素的排列。
x = list(range(3))
x
作者图片
perm_list = []for i in x:
for j in x:
perm_list.append([i, j])
perm_list
使用 for 循环输出
[[i,j] for i in x for j in x]
使用列表理解输出
我个人在从网上搜集数据时使用列表理解。让我们尝试从网页中提取 URL 及其文本。我们将创建一个名为“html”的变量来存储一小段 html 代码,并从“HTML”变量中提取 URL 及其文本,然后将它们存储在一个以文本为键、以 URL 为值的字典中。
from bs4 import BeautifulSoup# Variable storing a small HTML code snippet
html = '''
<html>
<head>head</head>
<body>
<a href="[https://www.abc.com](https://www.abc.com)">abc.com</a>
<a href="[https://www.xyz.com](https://www.xyz.com)">xyz.com</a>
<a href="[https://www.qwerty.net](https://www.qwerty.net)">qwerty.com</a>
</body>
</html>
'''bs = BeautifulSoup(html)# Finding the 'a' tags
a = bs.find_all('a')# Using list comprehension to extract 'href'
url = [i.get('href') for i in a]# Using list comprehension to extract text
txt = [i.text for i in a]# Using dictionary comprehension to store output
{i: j for i,j in zip(txt, url)}
作者图片
这些是列表理解的一些应用。与 for 循环相比,它们效率更高,并且还减少了执行操作所需的代码行数。
聪明地理解损失函数
了解损失函数如何用于理解模型的性能及其在 Python 中回归和分类问题中的实现。
弗朗西斯科·卡里法诺在 Unsplash 上拍摄的照片
在这篇文章中,我们将深入探讨用于提高机器学习算法性能的不同类型的损失函数。任何经典机器学习问题的主要动机都只是这两件事:
- 提高模型的精确度以及
- 减少与此相关的损失。
我们可以说损失是你为你的模型坏预测付出的代价。
在这一系列的三篇文章中,我们将讨论什么是损失函数,以及如何使用各种类型的损失函数来优化我们模型的性能。在第 2 部分,我们将研究回归损失函数,在第 3 部分,我们将探索分类函数。
本系列充满了 Python3 中的实际操作代码块,给你一个很好的实践经验,告诉你如何用 Python 实现它们。
要跳到任何部分:
- 第 1 部分:了解损失函数。
- 第二部分:回归损失函数。
- 第三部分:分类损失函数。
什么是损失函数?
准确地说,如果你的模型预测完美,那么你的损失会很少,否则损失会很大。不管问题陈述如何,通过将 预测值 与 实际值 进行比较,总会有性能评估。这就是损失函数发挥作用的地方。
损失函数的真正应用不仅仅是它的内在 值,而是它如何被用来改善模型的性能,因此使用了成本函数。损失函数将在每个训练示例中计算,而成本函数将是损失函数的平均值。因此,在单个训练数据集中,损失函数将被计算多次,而成本函数将仅被计算一次。
我们如何利用损失函数的力量?
所以我们的最终目标是最小化成本函数。让我们用一个简单的实时例子来理解它。想象一下,在一个黑暗的夜晚,你站在一座山脉的顶峰。当你完成了一整天的徒步旅行,现在想下去,但外面很黑,你会怎么做?你肯定会试图用最少的能量下山,而你想到的第一件事就是环顾四周,试图用你的脚找到最短的路径。你不可能上山,因为你的目标是下山,所以唯一的选择是选择下山的路。所以这就是你估计的计算,如果你走下坡路,将会花费少得多的能量和努力,这正是损失函数所做的。因此,我们的目标是“找到模型的权重和偏差,使损失最小。”
在自然界中,我们看到许多遵循这种方法的例子。
不管路上遇到什么,一条河总是走最短的下坡路。
分类和回归的损失函数:
现在,既然我们知道有两种类型的监督的机器学习问题:回归(连续目标值)和分类(离散目标值),那么让我们来探究它们中的每一个,看看所有类型的损失函数用于它们中的每一个,并基于它们对于提高机器学习模型的性能的效率来比较它们。要了解更多关于您可以使用一些技术来测量性能的信息,请在完成这篇文章后访问这篇文章以了解更多信息。
回归中使用的损失函数:
回归或回归分析是一种监督 学习 技术,可以定义为统计技术,用于对因变量实数变量 y 和自变量**【Xi】之间的关系进行建模。根据不同的场景及其重要性,我们使用不同类型的*回归方法。让我们来了解各种可能的损失函数,这些函数可用于改进回归分析中的性能评估。*
1.均方差/L2 损耗:
这是最常用的损失函数,因为它非常容易理解和实现。它适用于几乎所有的回归问题。顾名思义,均方差就是平方误差的平均值。这里的误差是模型预测值“ y_hat 与实际值“ y_real 之差。
在上图中,小圆圈是不同的 y 的 x 的实际值。这条线代表我们通过模型预测的最佳拟合**。因此,这里的虚线是误差, MSE 是所有误差的平方平均值。**
平方是为了放大大的误差,也是为了观察我们损失的方差。
均方误差/L2 损耗
Python3 中 MSE 丢失的代码实现
2.平均绝对误差/ L1 损耗:
平均绝对误差是模型预测值和实际值之间的绝对差值的平均值**。 平均绝对误差在异常值的情况下更稳健 因为不适合求差的平方,因为差太大了。**
不管误差的方向是正还是负,因为它们可能会相互抵消,从而不会产生误差,在这种情况下,我们只考虑其大小**。**
平均绝对误差/ L1 损耗
Python3 中 MAE 丢失的代码实现
3.均方根误差:
均方根误差是均方差的平方根。当我们将平方根应用于 MSE 时,RMSE 归结为与目标变量相同的单位。RMSE 的损失函数与 MSE 完全相同,我们只是将阶数从 2 降低到 1,这样就可以轻松地进行相关运算。MSE 中的相关性变得困难,因为它严重惩罚大误差。****
均方根误差
Python3 中 RMS 损耗的代码实现
4.平均偏差误差:
平均偏差误差是预测值和实际值 之间的精确差,没有应用任何数学函数,如绝对值或平方根。MBE 的主要局限是正负误差有机会 抵消*。这就是为什么这是很少使用和不太流行的损失函数。*****
平均偏差误差
Python3 中均值偏差损失的代码实现
5.胡伯损失:
正如我们已经讨论过的, MSE 和 MAE 都有缺点,所以这就是胡贝尔的损失所在。它只从 MSE 和 MAE 中提取有益的东西,并将它们纳入一个损失函数。
使用 MSE 和 MAE 的 Huber 损失
所以简而言之, Huber 损失对于小误差接近 MSE,对于异常值接近 MAE。
胡伯损失
Python3 中 Huber 丢失的代码实现
二元分类损失函数;
1.二元交叉熵损失;
顾名思义,二元分类是指将一个物体分为两类中的任何一类。例如,将电子邮件分类为垃圾邮件或非垃圾邮件。 熵基本上是不确定性的度量。 交叉熵是 两个随机变量 的不确定性之差。**
所以, 如果实际类与预测概率的差异越多,那么交叉熵损失就越多。 例如,我们假设一个数据样本的实际类别为 1,,预测概率为 0.2 。这导致高损耗值*。***
二元交叉熵损失
Python3 中二值交叉熵损失的代码实现
2.铰链损耗:
铰链损耗主要用于支持向量机*。任何分类问题中的最佳可能行都会尽可能少地犯分类错误。为了通过计算来表达这一思想,铰链损失惩罚每一个不正确的分类。***
例如,假设的实际值是 1* ,而的预测值是-1 ,那么“ l(y) 就变成了 2 ,这是一个更高的损失。而如果预测值和实际值都是 1 并且匹配,那么“ l(y) 就变成了 0 也就是没有损失。***
铰链损耗
铰链损失在[-1,1]值范围内工作良好,有时甚至比二元熵损失更好。
Python3 中铰链损耗的代码实现
3.平方铰链损耗:
铰链损耗的平方就是铰链损耗的平方。I f 你想惩罚这个大错误,平方铰链损失进入画面 。除了输出将被平方之外,最大值功能将是相同的。铰链损耗的平方与误差的平方成正比。****
平方铰链损耗
Python3 中平方铰链损耗的代码实现
多项式分类损失函数;
1.分类交叉熵损失:
这是 最常用的损失函数 ,因为它非常容易理解和实现。它几乎适用于所有的分类问题。分类交叉熵损失可以认为是二元交叉熵损失的推广。 当班级数量超过 2 个 时使用。为了计算分类交叉熵损失,最佳实践是对分类进行一次性编码*。需要为每个观察的每个类别标签计算损失,并对结果 求和 。***
Python3 中分类交叉熵损失的代码实现
2.KL (Kullback-Leibler)散度:
KL 散度衡量任意两个分布之间的差异。它帮助我们理解,“ 当我们追求一个近似值 ”时,我们丢失了多少信息。
是对熵的轻微修改。除了我们的概率分布,我们还添加了一个近似分布,并计算两个分布之间的差异。**
通过这种方式,我们可以追踪我们因近似而丢失的信息。
KL(库尔巴克-莱布勒)散度
Python3 中 KL 发散损失的代码实现
希望你喜欢阅读!
你可以在这里阅读更多我的文章:
***
谢谢!***
用可解释的人工智能更好地理解机器学习模型
使用 ExplainerDashboard 用几行代码构建一个交互式仪表板
图片来源:作者(Canva 设计)
通过基于网络的仪表板来解读机器学习的工作是很有趣的。想象一下,访问显示模型性能、特征重要性以及假设分析信息的交互式图表。令人兴奋的是,构建这样一个信息丰富的仪表板不需要任何 web 开发专业知识,但简单的几行 python 代码就足以生成一个令人惊叹的交互式机器学习仪表板。这可以通过使用一个名为“解释器仪表板”的库来实现。
ExplainerDashboard 是一个 python 包,它生成交互式仪表板,允许用户理解并解释模型如何工作以及它如何决定结果。没有这样的工具,机器学习模型就是“黑盒模型”。因此,很难解释模型做出决策背后的原因,以及哪些因素影响了模型的决策。使用 Python 中的这个包,我们可以在 Jupyter 或 Colab 笔记本中轻松地将仪表板部署为 web 应用程序。这个 web 应用程序提供了几个交互式图表来解释它所应用的机器学习模型的工作原理。这些模型可以基于 Scikit-learn、XGBoost、CatBoost、LightGBM 和其他一些模型。仪表板通过各种交互式
图提供对模型性能的洞察,如用于理解特征相关性的 SHAP 值图、用于特征交互的
SHAP 交互图、用于选定特征影响的部分相关性图(PDP)以及通过决策树可视化决策路径等。还可以通过“假设”分析来评估改变特征值对模型性能的影响。此外,高级用户也可以使用一些额外的代码来定制这些仪表板。对于本教程,我们将只探索该包的功能,我将带您一步一步地用 Python 创建您自己的机器学习模型仪表板,只需几行代码。在教程的最后,我们将看到我们可以从这个机器学习仪表板中收集到什么样的见解。
解释器仪表板库
这个 Python 包为机器学习模型构建了一个基于 web 应用程序或内嵌笔记本的可解释仪表板。仪表板的默认组件是不言自明的,不需要额外的定义函数。这些图表或仪表板的交互组件基于另一个名为 Dash 的库,该库以网络应用和图表而闻名,使用了一个名为“Plotly”的库。最后,整个仪表板实际上是一个使用 flask server 在本地机器上运行的 web 应用程序。你可以在这里找到所有官方文档。
让我们从 ExplainerDashboard 库的 pip 安装开始本教程
pip install explainerdashboard
您可以在 Colab 或 Kaggle 笔记本中直接使用该命令。然而,如果使用在本地机器上运行的 Jupyter 笔记本,使用虚拟环境可能是一个更好的选择,以避免由于包依赖性而导致的任何冲突。
方法:
- 导入库和样本数据集
- 从数据集创建 DataFrame(本演示教程不需要数据预处理)
- 拆分数据并训练模型
- 在本地端口上部署仪表板
回归示例机器学习仪表板
对于本教程,让我们使用 sklearn 库中的样本玩具数据集“糖尿病”数据集来构建回归问题的机器学习仪表板。
导入库
#Importing Libraries & Packagesimport pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from explainerdashboard import RegressionExplainer, ExplainerDashboard
我们需要 Pandas 库用于数据框架,而 ExplainerDashboard 和 Dash Bootstrap 库用于构建仪表板。sklearn 库将用于获取玩具数据集,分割它并导入 RandomForestRegressor 来为这个回归示例训练模型。
导入数据集
#Import the Diabetes Dataset
from sklearn.datasets import load_diabetesdata= load_diabetes()#print the dataset
data
加载数据集
我们需要将数据集加载到 X 和 y 变量上,以创建熊猫数据框架。x 将保存要素,y 将保存目标值。
#create a DataFrame from the dataset
X=pd.DataFrame(data.data,columns=data.feature_names)#Printing first five rows of the DataFrame
X.head()
#Load target values in y
y=pd.DataFrame(data.target,columns=[“target”])
y.head()
现在我们的数据准备好了,我们可以使用 RandomForestRegressor 来训练模型。
分割数据集
让我们使用 sklearn 的 train-test split 函数将数据集按 80–20 的比例拆分。
#Splitting the Dataset
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
print(X_train.shape,y_train.shape,X_test.shape,y_test.shape)
训练模型
我们现在可以用随机选择的估计值的 RandomForestRegressor 来训练模型。您也可以尝试使用不同的值或使用 XGBoost 来训练模型并进行比较。
#Training the model
model = RandomForestRegressor(n_estimators=50, max_depth=5)model.fit(X_train, y_train.values.ravel())
注意:在这一步中,我们使用推荐的命令“ravel()”将“y_train”转换为一维数组。这种对列向量 y 的整形将避免由 RandomForestRegressor 生成的 DataConversionWarning。
使用训练模型设置仪表板实例
explainer = RegressionExplainer(model, X_test, y_test)#Start the Dashboard
db = ExplainerDashboard(explainer,title=”Diabetes Prediction”,whatif=False)#Running the app on a local port 3050
db.run(port=3050)
该仪表板将在端口为 3050 的本地服务器上运行(如果您选择另一个端口号,比如 8080 或 3000,那么您的情况可能会有所不同)
单击该链接将在 web 浏览器的单独选项卡中打开仪表板。
您的完全交互式机器学习仪表板已经准备好了!
你可以在我的 GitHub 库上找到这个笔记本的完整代码。
按作者分类的仪表板 GIF
仪表板中的见解
有了这个仪表板,我们可以得到一些见解,比如-
- Shap 值表示每个单独的特征如何影响预测
- 置换重要性,它允许我们更深入地挖掘,以可视化模型性能如何随着特征的改变而恶化
- 在使用类似于本教程的 XGBoost 或 RandomForestRegressor 的回归模型的情况下,我们可以可视化单个决策树,而在分类器模型的情况下,我们可以获得混淆矩阵、ROC-AUC 曲线等。更好地理解模型的决策。
- 假设分析(在启动仪表板时打开的情况下)有助于了解在我们修改特征或部分数据时模型行为的变化。它还允许我们比较不同的模型。
然而,对上面的图和它们包括的参数有一些基本的理解对于理解来自这样的机器学习仪表板的洞察力也是有帮助的。对于任何寻找本教程主题的理论详细信息的人,我建议阅读克里斯托夫·莫尔纳尔的书’可解释的机器学习。
本教程到此为止。希望作为读者的你能学到一些新的有趣的东西。
直到下一篇文章,快乐阅读!
理解自然语言处理中的掩蔽语言模型(MLM)和因果语言模型(CLM)
自然语言处理中的语言模型(视觉和例子)
来自源的修改图像
大多数现代 NLP 系统都遵循一个非常标准的方法来训练各种用例的新模型,即 首先预训练,然后微调 *。*这里,预训练的目标是利用大量的未标记文本,建立一个语言理解的通用模型,然后在机器翻译、文本摘要等各种特定的 NLP 任务上进行微调。
在这篇博客中,我们将讨论两种流行的预训练方案,即掩蔽语言建模 (MLM)和因果语言建模 (CLM)。
没时间看完整个博客?然后看这个快速的< 60 秒的 YouTube 短片—
屏蔽语言模型解释
在屏蔽语言建模下,我们通常屏蔽给定句子中的某个百分比的单词,并且该模型被期望基于该句子中的其他单词来预测那些被屏蔽的单词。这样的训练方案使得这个模型在本质上是双向的,因为屏蔽单词的表示是基于出现在它左右的单词而学习的**。你也可以把这想象成 一种填空题式的问题陈述 。**
下图显示了同样的—
蒙版语言模型|作者图片
下图显示了损失计算步骤的更详细视图—
有损失的掩蔽语言模型|作者图片
这里,屏蔽词的表示可以是基于注意力的,如 BERT 及其变体,或者你也可以设计成没有注意力。基于 Alpha (注意力权重)的分布,您可以对每一个其他输入单词的表示进行加权,以学习被屏蔽单词的表示,例如—Alpha = 1 将对周围的单词给予相等的权重(这意味着每个单词在屏蔽表示中具有相等的贡献)。
因果语言模型解释
在因果语言模型下,这里的想法仍然是预测给定句子中的屏蔽标记,但与 MLM 不同,该模型被允许只考虑出现在它左边的单词来做同样的事情*(理想情况下,这可能只是左边或右边,想法是使它单向)*。这样的训练方案使得这个模型本质上是单向的。
正如您在下图中看到的,该模型预计会根据出现在句子左侧的单词来预测出现在句子中的掩码标记。并且基于模型对实际标签所做的预测,我们计算交叉熵损失并将其反向传播以训练模型参数。
因果语言模型|作者图片
下图显示了损失计算步骤的更详细视图—
带有损失的因果语言模型|作者图片
在这里,屏蔽词的表达可以是基于注意力的,就像在 GPT 和变体中一样,或者你也可以设计成没有注意力,就像我们过去在 LSTM 时代那样。基于 Alpha (见图)的分布,您可以对每隔一个输入单词的表示进行加权,以学习屏蔽单词的表示,例如—Alpha = 1 将对周围的单词给予相等的权重(意味着每个单词将对学习的屏蔽表示具有相等的贡献)。
这些系统也被称为仅解码器模型,因为在典型的编码器-解码器架构中,如在机器翻译、文本摘要等中,解码器*(文本生成器)*的工作方式类似。
什么时候用什么?
当目标是学习输入文档的良好表示时,MLM 损失是优选的,而当我们希望学习生成流畅文本的系统时,CLM 是最优选的。此外,直觉上这是有意义的,因为当学习每个单词的良好输入表示时,你会想知道它出现的单词是左还是右,而当你想学习一个生成文本的系统时,你只能看到你到目前为止生成的所有内容*(就像人类如何书写一样)*。因此,制造一个在生成文本的同时也能窥视另一面的系统会引入偏见,限制模型的创造能力。
虽然在训练包含编码器和解码器的整个架构时,您会经常发现 MLM 和 CLM 损失。两者都有各自的优点和局限性,一种叫做 XLNet 的新模式使用了一种置换技术来利用两者的优点(MLM 和 CLM)。
我希望你喜欢读这篇文章。如果你愿意支持我成为一名作家,可以考虑注册成为中的一员。每月只需 5 美元,你就可以无限制地使用 Medium
我希望你喜欢阅读这篇博客。谢谢大家!
用 6 个代码片段理解 Matplotlib
Matplotlib 是 python 中用于数据可视化的常用库。你探索过这个库提供的所有功能了吗?如果没有,我来帮你!
在 Unsplash 上由 Adeolu Eletu 拍摄的照片
我是亚马逊的一名应用科学家。信不信由你,我不记得有哪一周我没有在工作中使用过这个图书馆。有时我们需要可视化数据以便理解,有时在演示文稿/文档中需要可视化。因此,根据需要,我们也必须担心视觉化的直觉和美化。现在,我的朋友们,matplotlib 是我所有这些任务的首选库。在这篇博客中,我们将了解如何使用 matplotlib 来绘制以下变体:
- 用一组连续数据绘制一个正态图
- 绘图分类变量
- 使用散点图绘制图形,找出两个变量之间的关系
- 用不同的格式样式绘制图形
- 创建由多个图形组成的图形—一个图形有多条曲线,或者不同的图形有不同的曲线
- 使用图形中的文本注释。
以上并不是一个详尽的列表,但是它们应该足以理解这个库。开始吧!!!
具有连续数据数组的正常图
最直接的用例之一是在训练机器学习模型时,理解随着时期/迭代的损失行为。
import matplotlib.pyplot as plt
import numpy as nploss = np.array([1, 0.95, 0.92, 0.89, 0.83, 0.76, 0.70, 0.63, 0.54, 0.48])
epochs = np.array(list(range(10)))plt.plot(loss, epochs)
plt.show()
作者图片
是的,我知道上面的图表看起来很标准。别担心,我们会在“格式样式”部分美化它。
绘制分类变量/数据
首先,什么是分类变量?这些变量占用离散和有限数量的值。例如,在分类任务中,描述类的变量是分类变量。一个具体的例子是考虑一个图像分类问题,其中我们必须分类图像是否有狗(1)或没有(0)。那么这个代表有或没有狗出现的图像数量的变量就是一个分类变量。这些变量可以用柱状图或饼状图来表示。
条形图-:
import matplotlib.pyplot as plt
import numpy as np
class_variable = ["dog", "cat", "cow", "fish"]
number_of_image = [10, 15, 8, 20]
plt.bar(class_variable, number_of_image)
plt.show()
饼状图-:
import matplotlib.pyplot as plt
import numpy as np
class_variable = ["dog", "cat", "cow", "fish"]
number_of_image = [10, 15, 8, 20]
plt.pie(number_of_image, labels = class_variable)
plt.show()
以上两个片段的可视化(图片由作者提供)
使用散点图绘制图形,找出两个变量之间的关系
正如你可能已经猜到的,散点图是用来找出两个变量之间的关系。在这里,我们设想一个变量的变化如何影响另一个变量,或者换句话说,我们试图理解两个变量之间的相关性。
import matplotlib.pyplot as plt
import numpy as np
variable_a = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6])
variable_b = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86])plt.scatter(variable_a, variable_b)
plt.show()
作者图片
从上图我们可以得出,当变量 _a 增加时,变量 _b 减少。
绘图的格式样式
这是其中重要的一条。在这里,我们将看到我们可以添加到一个情节的各种美化。我们将在这里看到如何添加以下内容:
- 轴标签-有助于描述 x 轴和 y 轴在图上代表的内容。
- 图例-当我们在图形中绘制多个图时非常有用。它告诉我们哪种颜色代表图中的哪种数据。
- 标题-图的标题
- 网格—在图形中添加网格有助于获得更好的推断
- 颜色-根据您的要求设置曲线的颜色。
- 虚线-设置曲线是实线还是虚线
- 标记-设置如何表示每个数据点
因此,许多新功能正在被引入。为了理解每一个的效果,我在不同的代码行上绘制了多个图,如下面的代码片段所示。
注意— 为了便于说明,我只使用了一种标记或一种颜色。您可以查看每种类型的其他可用选项。
import matplotlib.pyplot as plt
import numpy as np
loss = np.array([1, 0.95, 0.92, 0.89, 0.83, 0.76, 0.70, 0.63, 0.54, 0.48])
epochs = np.array(list(range(10)))
plt.plot(loss, epochs, label="Loss Curve 1", linestyle="dashed", marker='*', color='red')#Plot 1
plt.xlabel("Epochs")
plt.ylabel("Loss") #Plot 2
plt.title("Loss - Epoch Curve")#Plot 3
plt.grid("on") #Plot 4
plt.legend()#Plot 5
plt.show()
作者图片
为了比较,我还展示了第一个片段中的一个图(图 6)。现在,就我们的理解而言,图 6 就足够了,因为我们只需要看到损耗如何随着每个时期而变化。尽管如此,对于新手来说,Plot 5 更适合表示所有必要的信息。
创建由多个图形组成的图形
当我们需要绘制多个支线剧情时,我们可以使用下面的片段。我还添加了不同格式样式的例子,这样你就可以更清楚地理解了。
import matplotlib.pyplot as plt
import numpy as npt1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.2)plt.figure()
plt.subplot(2,2,1)
plt.plot(t1, np.sin(2*np.pi*t1), color = 'black', marker='^', linestyle='solid')
plt.title("Sin Curve")
plt.grid("on")plt.subplot(2,2,2)
plt.plot(t2, np.tan(2*np.pi*t2), color = 'blue', marker='*', linestyle='dashed')
plt.title("Tan Curve")
plt.grid("on")plt.subplot(2,2,3)
plt.plot(t1, np.cos(2*np.pi*t1), color = 'green', marker='o', linestyle='dotted')
plt.title("Cos Curve")
plt.grid("on")plt.subplot(2,2,4)
plt.plot(t2, np.exp(t2), color = 'red', marker='*', linestyle='dashdot')
plt.title("Exponential Curve")
plt.grid("on")
plt.show()
作者图片
当在同一个图中需要多条曲线时,我们可以使用下面的代码片段。
import matplotlib.pyplot as plt
import numpy as npt1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.2)plt.plot(t1, np.sin(2*np.pi*t1), color = 'black', marker='^', linestyle='solid', label = "Sin Curve")
plt.plot(t1, np.cos(2*np.pi*t1), color = 'green', marker='o', linestyle='dashed', label="Cos Curve")
plt.legend()
plt.grid("on")
plt.show()
作者图片
正如我们在这里看到的,图例有助于直观显示哪条曲线对应于哪种函数,即 sin 或 cos。
使用图形中的文本注释。
我们可以使用测试注释来指出图中的特定点,并描述该点的含义。例如,我在下面的代码中注释了正弦曲线和余弦曲线的最大值。
import matplotlib.pyplot as plt
import numpy as npt1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.2)plt.plot(t1, np.sin(2*np.pi*t1), color = 'blue', marker='^', linestyle='solid', label = "Sin Curve")
plt.plot(t1, np.cos(2*np.pi*t1), color = 'green', marker='o', linestyle='dashed', label="Cos Curve")
plt.annotate('Sin max', xy=(1.25, 1), xytext=(1.5, 1.15),
arrowprops=dict(facecolor='black', shrink=0.05),
)
plt.annotate('Cos max', xy=(2, 1), xytext=(2.25, 1.15),
arrowprops=dict(facecolor='black', shrink=0.05),
)
plt.ylim([-1.5, 1.5])
plt.legend()
plt.grid("on")
plt.show()
我在这里增加的另一件事是定义 y 轴的极限。同样,您可以更改 x 轴的限制。
结论
以上只是几个例子,我试图尽可能地涵盖更多的内容。现在,您可以使用多种工具来创建一个伟大的可视化。如果我错过了任何重要的例子,请让我知道,以便我可以在这里添加它们。
谢谢你的来访!
关注我们的 medium 了解更多此类内容。
成为 介质会员 解锁并阅读介质上的许多其他故事。
了解混合精度训练
训练神经网络的混合精度可以在不影响模型性能的情况下减少训练时间和内存需求
帕特丽夏·塞尔纳在 Unsplash 上的照片
随着深度学习方法的发展,人们普遍认为增加神经网络的规模可以提高性能。但是,这是以内存和计算需求为代价的,这些需求也需要增加以训练模型。
这可以通过比较谷歌预先训练的语言模型 BERT 在不同架构规模下的性能来理解。在最初的论文中,谷歌的研究人员报告称,BERT-Base 的平均得分为 79.6,BERT-Large 的平均得分为 82.1。2.5 的小幅增加带来了额外的 2.3 亿个参数(110 亿对 340 亿)!
粗略计算一下,如果每个参数都以单精度存储(下面更详细),也就是 32 位信息,那么 230M 个参数相当于内存 0.92Gb。这本身看起来并不大,但是考虑到在每个训练迭代期间,这些参数每个都经过一系列矩阵运算,每个都具有额外的相关值,例如梯度。所有这些额外的价值会很快变得难以管理。
2017 年,英伟达的一组研究人员发布了一篇论文,详细介绍了如何降低训练神经网络的内存需求,使用了一种叫做混合精度训练的技术:
我们介绍了使用半精度浮点数训练深度神经网络的方法,而不会损失模型精度或必须修改超参数。这几乎将内存需求减半,并且在最新的 GPU 上加快了运算速度。
在本文中,我们将探索混合精度训练,了解它如何适应深度学习的标准算法框架,以及它如何能够在不影响模型性能的情况下减少计算需求。
浮点精度
用于以二进制格式表示浮点数的技术标准是 IEEE 754 ,由电气和电子工程协会于 1985 年制定。
如 IEEE 754 中所述,浮点精度有各种级别,从二进制 16(半精度)到二进制 256(八倍精度),其中“二进制”后的数字等于可用于表示浮点值的位数。
与整数值不同,在整数值中,位只是表示数字的二进制形式,可能只有一位保留给符号,浮点值也需要考虑指数。因此,这些数字的二进制表示形式更加微妙,会显著影响精度。
历史上,深度学习一直使用单精度(binary32,或 FP32)来表示参数。在这种格式中,1 位保留给符号,8 位保留给指数(-126 到+127),23 位保留给数字。另一方面,半精度(FP16)为符号保留 1 位,为指数(-14 到+14)保留 5 位,为数字保留 10 位。
FP16(上)和 FP32(下)浮点数格式的比较。出于说明的目的,所示的数字是每种格式可以表示的小于 1 的最大数字。作者插图。
然而,这是有代价的。每个的最小和最大正正常值如下:
除此之外,可以表示更小的非规格化数,其中所有的指数位都设置为零。对于 FP16,绝对极限是 2^(-24)然而,随着非规格化数变小,精度也会降低。
在本文中,我们不会进一步深入理解不同浮点精度的定量限制,但是 IEEE 提供了全面的文档供进一步研究。
混合精确训练
在神经网络 FP32 的标准训练期间,以增加存储器需求为代价来表示模型参数。在混合精度训练中,FP16 用于存储训练迭代期间的权重、激活和梯度。
然而,正如我们在上面看到的,这产生了一个问题,因为 FP16 可以存储的值的范围小于 FP32,并且随着数量变得非常小,精度也会降低。这样做的结果将会降低模型的准确性,与计算出的浮点值的精度一致。
为了解决这个问题,在 FP32 中存储了重量的主副本。这在每个训练迭代(一次正向传递、反向传播和权重更新)的一部分期间被转换成 FP16。在迭代结束时,权重梯度用于在优化器步骤期间更新主权重。
混合精度训练将权重转换为 FP16 并计算梯度,然后将它们转换回 FP32,再乘以学习率并在优化器中更新权重。作者插图。
在这里,我们可以看到保留权重的 FP32 副本的好处。由于学习率通常很小,当乘以权重梯度时,它们通常是很小的值。对于 FP16,幅度小于 2^(-24 的任何数字)将被视为零,因为它不能被表示(这是 FP16 的反规格化极限)。因此,通过完成 FP32 中的更新,可以保留这些更新值。
FP16 和 FP32 的使用是这种技术被称为混合-精确训练的原因。
损耗缩放
虽然混合精度训练在很大程度上解决了保持精度的问题,但实验表明,即使在乘以学习率之前,也会出现小梯度值的情况。
NVIDIA 团队表明,尽管低于 2^-27 的值基本上与训练无关,但在[2-27,2-24]范围内有一些值得保留的值,但在 FP16 的限制之外,在训练迭代期间将它们等同于零。由于精度限制,梯度等于零,这个问题称为下溢。
因此,他们建议进行损耗缩放,即在正向传递完成之后、反向传播之前,损耗值乘以一个比例因子。链规则规定所有的梯度随后被相同的因子缩放,这使得它们在 FP16 的范围内移动。
一旦计算出梯度,它们就可以除以相同的比例因子,然后用于更新 FP32 中的主权重,如前一节所述。
在损耗调整过程中,在正向传递之后,损耗会按预定义的因子进行调整,以确保它在可表示的 FP16 值范围内。由于链规则,这通过相同的因子来缩放梯度,因此实际的梯度可以在反向传播之后被检索。作者插图。
在 NVIDIA“深度学习性能”文档中,讨论了比例因子的选择。理论上,选择一个大的缩放因子没有坏处,除非它大到足以导致溢出。
当梯度乘以比例因子超过 FP16 的最大限值时,就会发生溢出。出现这种情况时,梯度变得无限大,并设置为 NaN。在神经网络训练的早期,通常会看到以下消息:
Gradient overflow. Skipping step, loss scaler 0 reducing loss scale to…
在这种情况下,该步骤被跳过,因为权重更新不能使用无限梯度来计算,并且损失比例对于未来的迭代被减小。
自动混合精度
2018 年,NVIDIA 发布了一个名为 Apex 的 PyTorch 扩展,其中包含 AMP(自动混合精度)能力。这为在 PyTorch 中使用混合精度训练提供了一个简化的解决方案。
只需几行代码,训练就可以从 FP32 转移到 GPU 上的混合精度。这给带来了两个关键的好处:
- 减少训练时间 —训练时间减少了 1.5 倍到 5.5 倍,模型性能没有显著降低。
- 减少内存需求 —这释放了内存以增加其他模型元素,如架构大小、批量大小和输入数据大小。
从 PyTorch 1.6 开始,NVIDIA 和脸书(PyTorch 的创造者)将这一功能转移到 PyTorch 的核心代码中,命名为 torch.cuda.amp 。这解决了围绕 Apex 包的几个痛点,例如版本兼容性和构建扩展的困难。
虽然本文不会进一步深入 AMP 的代码实现,但是可以在 PyTorch 文档中看到示例。
结论
虽然浮点精度经常被忽视,但它在深度学习模型的训练中起着关键作用,在深度学习模型中,小梯度和学习速率相乘,以创建需要更多位来精确表示的梯度更新。
然而,随着最先进的深度学习模型在任务性能方面突破界限,架构不断发展,精度必须与训练时间、内存要求和可用计算相平衡。
因此,混合精度训练在保持性能的同时基本上将内存使用减半的能力是深度学习的一个重大进步!