追逐数据:冠状病毒
冠状病毒,里里外外。来源:疾控中心;在维基百科上引用(http://ow.ly/H1aD50ym8XB)
关于冠状病毒大数据分析的效用(或无用性)(迄今为止)
序言:埃博拉
当我在社交媒体应急管理(SMEM)项目埃博拉病毒信息和埃博拉社交媒体联盟(特别是我保存的文件)上投入大量时间时,我经常被留下来追踪数据。报道,甚至来自官方的政府来源,经常是相互矛盾的,姗姗来迟的,或者完全错误的。当我于 2015 年 3 月 28 日提交上一份报告时,已有 24,907 例确诊病例和 10,326 例死亡归因于埃博拉——这听起来约为 40%的死亡率。
尽管我转向了其他项目,但这种疾病仍在非洲肆虐。根据世界卫生组织(世卫组织)一年后于 2016 年 6 月 10 日公布的最终分析,共有 28616 例埃博拉病例,11310 例死亡,死亡率为 39.5%。我离得不远。
在漫长的考验中,世卫组织有时不得不收回数字。有关于这种疾病的虚假报道,后来证明是拉萨热、疟疾或其他疾病。
这导致了各种阴谋论和非理性的恐惧。特别是在非洲,埃博拉是对公共卫生的真正威胁,然而即使在世界各地,病例很少或根本不存在,人们也普遍感觉到了埃博拉。
埃博拉病毒触动了很多人的神经。感谢《连线》杂志激起了人们最大的恐惧。
连线撰稿人玛丽恩·麦肯纳 的 细菌女孩 的进一步冒险,回想起来特别有启发性(目前是前五页的结果)。它记录了人们把自己逼入的非理性的疯狂状态。例如,美国海关对那些从哥斯达黎加丹尼尔·奥杜伯·基罗斯国际机场出发的人来说是个问题,仅仅因为它位于哥斯达黎加利比里亚镇。
(CBP 提示:利比里亚,哥斯达黎加≠利比里亚蒙罗维亚)
在全球范围内,2014 年至 2016 年期间,全球总人口为 74 亿,其中发现了 28,616 例埃博拉病例。这是一个只有 0.00038%的发病率(感染埃博拉病毒的几率),即每 10 万人中约有 0.38 人。与日常癌症相比,发病率为十万分之439.2,你比埃博拉 多了 x1,135 个患癌几率。
这并不是要低估疫情的严重性,也不是要低估任何实际接触或感染这种疾病的人,或任何认识受感染者的人的困境,无论是经济损失、情感创伤、痛苦还是死亡。
然而,对世界其他地区来说,这是一个我称之为“显微镜/望远镜”的非理性放大问题:那些远离疾病中心的人将它视为直接临近并威胁着他们的生命。就好像,通过望远镜观察,你认为木星在你的膝盖上。(提示:如果是的话,你可能已经因为 2.5 克的重力而喘不过气来,并被可怕的低温瞬间冻结了。)
新型冠状病毒(又名 2019-nCov 又名 COVID19)
目前 2019 年中国爆发的冠状病毒也出现了同样的模式。起初很难掌握这些数字。由于方法的改变,最近的病例在一夜之间从大约 45,000 例增加到 60,000 例。他们一直使用实验室确认的病例来计算疾病。这是“准确的”数据,但不是“及时的”实验室处理的滞后使得计数少得可怜。因此,对于每日更新来说,这最终是不准确的。当他们最终揭示了这种滞后,并转而转向基于临床症状的诊断时,这就是导致人数激增的原因。根据症状,一天内报告了 13,332 例额外病例,另有 1,820 例实验室确认病例。这是 15,152 次跳跃,其中只有 12%是实验室确认的。
其中一些病例最终会被认为是误诊而不予考虑。但他们宁愿在谨慎和数据的即时性方面出错,也不愿在计算潜在病例时多花几天或几周时间。
本质上——请原谅这种类比——中国和世界卫生组织不得不将公共卫生突发事件从高度一致但无法跟上病例吞吐量的数据集转变为高度可用、最终一致的数据集(最终将排除误报)。这是我现在的雇主 ScyllaDB 恰好是理解的专家。
就公共数据集而言,公众必须意识到你无法看到原始信息。想象一下,在美国,HIPAA 合规性会带来怎样的隐私权问题。公共卫生官员不会分享受影响者的确切姓名和身份,更不用说他们数周内的所有可疑接触。公众所能期望的最好结果是分析汇总的、轶事的或偶然的数据和元数据——比如社交媒体帖子或全球航班信息。一些公司如 T2 蓝点 T3 和 T4 meta biota T5 已经有了提供这类数据分析的服务。但是,这些都不是公开的数据集。
你也不会看到任何一种像魔法间谍电影一样的电脑特效,直接放大到谁被确诊的记录,过去几天或几周内他们所有接触者的霓虹色图表,然后快进到他们在那段时间可能在哪里的所有监控录像。(“看!那里!厨子没洗手!”)虽然关于中国政府监控的算法监控已经讨论了多年,但很明显,这种系统无法像某种超级科学一样阻止疾病爆发。
科罗纳诺亚已经来了
没过多久。科罗纳诺亚已经来了。灾难奸商已经在试图让你购买他们的产品或服务作为灵丹妙药或灾难预防。例如,今天在我公司的收件箱里出现了这个:
不,这是真的。我希望不是。由于冠状病毒,今年在巴塞罗纳举行的世界移动通信大会刚刚收起了帐篷。
- 西班牙人口:4666 万
- 西班牙迄今冠状病毒病例总数:2
- 西班牙冠状病毒发病率:4.28632662e-8,即十万分之 0.00428
- 2019 年世界移动通信大会与会者总数:109,500 人
- MWC 感染冠状病毒的估计人数:0.0046
那么,为什么要取消一项几乎没有人会感染冠状病毒的活动呢?请注意,参加会议或在飞机上生病仍然有更大的统计概率感染基本的流感——流感波动很大,美国每年有 3%-20%的人感染流感。然而,很少有人因为害怕流感而放弃某项活动。
那么,为什么取消冠状病毒?好吧,科罗纳诺亚。西班牙的那两起案件,再加上游客从中国来西班牙的可能性,导致整个节目崩盘。一个接一个的多米诺骨牌倒下了。诺基亚、沃达丰、德国电信退出了。爱立信、索尼、英特尔、LG、亚马逊……
具有讽刺意味的是,中国巨头华为想要坚持到底。地区卫生官员 Alba Verges 说“没有公共卫生理由取消任何活动”并且该地区“冠状病毒的风险非常低”,这并不重要
主要的主播赞助商已经放弃了。嘣。表演结束了。
这场演出预计将为当地经济带来大约 5 亿美元的收入(确切地说是 5 . 13 亿美元,以及 13,900 个临时工作岗位)。但是波及范围将远远超出加泰罗尼亚。联合航空公司专门为 WMC 增加了航班,将观众带到西班牙,并向 T4 提出接受取消航班并免除他们的费用。更不用说全球业务因重大产品发布中断而搁置,无法捕捉的线索和密室谈判现在被扼杀,这通常发生在这样一个重大的贸易活动中。
由于担心中国游客,这不是唯一取消的活动。耶鲁大学的模拟联合国活动最近也被取消了。在他们的活动取消五天后,结果是学生得了…你猜对了,流行性感冒。
更新[2020 年 2 月 14 日]: IBM 刚刚退出 RSA 会议。正如他们在《双峰》中所说,“又发生了。”
虽然科罗纳诺亚将对许多人的生命和死亡产生实际影响,但它对中国经济的负面影响超出了其实际比例,对整个全球经济的影响较小。由于其影响,中国自身在一个季度内的 GDP 增长将减少 1.5%(全年 0.6%,因为经济学家预计会出现反弹)。
最佳信息来源
因此,尽管冠状病毒是的一个重大健康风险,并且如果你生活在受影响的地区或与来自那里的人接触过,有的理由担心,但重要的是每个人都不要因为无知和过于频繁的种族主义冠状病毒而陷入膝跳反应。
那么,从哪里获得你的事实呢?
鉴于我在 SMEM 的背景,在 Twitter 上,像 @COVID19Info 和 @DataCoronavirus 这样的弹出柄让我深受鼓舞。干得好,伙计们!
然而,我自己的最新和最权威的统计数据网站是基于 Esri GIS 系统的约翰霍普金斯 CSSE 冠状病毒新冠肺炎全球病例 仪表盘。
截至 2020 年 2 月 13 日 09:14 太平洋时间的冠状病毒数据
在一家大数据公司工作时,我想知道是什么样的系统在驱动这一切。一定很大,对吧?
没有。
对我来说,值得注意的是用于构建它的数据集的大小是如此之小(你可以在 Github 这里找到它)。下载整个回购文件会产生一个巨大的 158kb 的未压缩文件夹。每天的案例更新大约为 1-2kb。确诊病例、死亡病例、康复人员的时间序列文件分别为 11kb、8kb、9kb。总共有 58 份文件。
这不是大数据。这是非常小的数据。它能装在 1976 年的 5 英寸 360 千字节软盘上。
非常抱歉。
是啊。很明显,这不是原始数据,而是汇总的数据。比如说。time _ series _ 2019-ncov-confirmed . CSV 文件的前几行如下所示:
Province/State,Country/Region,Lat,Long,1/21/20 22:00,1/22/20 12:00,1/23/20 12:00,1/24/20 0:00,1/24/20 12:00,1/25/20 0:00,1/25/20 12:00,1/25/20 22:00,1/26/20 11:00,1/26/20 23:00,1/27/20 9:00,1/27/20 19:00,1/27/20 20:30,1/28/20 13:00,1/28/20 18:00,1/28/20 23:00,1/29/20 13:30,1/29/20 14:30,1/29/20 21:00,1/30/20 11:00,1/31/20 14:00,2/1/20 10:00,2/2/20 21:00,2/3/20 21:00,2/4/20 9:40,2/4/20 22:00,2/5/20 9:00,2/5/20 23:00,2/6/20 9:00,2/6/20 14:20,2/7/20 20:13,2/7/20 22:50,2/8/20 10:24,2/8/20 23:04,2/9/20 10:30,2/9/20 23:20,2/10/20 10:30,2/10/20 19:30,2/11/20 10:50,2/11/20 20:44,2/12/20 10:20,2/12/20 22:00,2/13/20 10:00,2/13/20 21:15
Anhui,Mainland China,31.82571,117.2264,,1,9,15,15,39,39,60,60,70,70,70,106,106,106,152,152,152,200,200,237,297,408,480,480,530,530,591,591,591,665,733,733,779,779,830,830,830,860,889,889,910,910,934
Beijing,Mainland China,40.18238,116.4142,10,14,22,26,36,36,41,51,68,68,72,80,80,91,91,91,111,111,111,114,139,168,191,212,212,228,253,274,274,274,297,315,315,326,326,337,337,337,342,342,352,366,366,366
Chongqing,Mainland China,30.05718,107.874,5,6,9,27,27,57,57,75,75,110,110,110,132,132,132,147,147,147,165,182,211,247,300,337,337,366,376,389,400,400,415,426,428,446,450,468,473,486,489,505,509,518,525,529
实际上,在 Github 中查看它更容易,看它告诉你什么:
它基本上是一个简单的电子表格格式的 CSV 文件,包含每个城市的每个报告期。1 月 21 日 22:00 更新。1 月 22 日和 1 月 23 日,中午 12:00 收到报告。然而,1 月 24 日有两份报告;一个从 0 点(午夜)开始,一个从中午 12 点开始。有些日子有三天(1 月 25 日有 0:00、12:00、22:00)。其他人有字面上(数字上)的奇数偏移(1 月 26 日有 11:00,23:00)。不幸的是,没有元数据指示使用什么时区偏移(GMT?中国标准时间?).
在像 Scylla 或 Cassandra 这样的现代数据库上,这些数据库可以扩展到 TB 和 Pb,这个数据集很容易作为一个 blob 放入一个小记录中。尽管这可能不是最好的数据模型。你肯定想把它分成几列,这样才有用。但是,如果这就是您要向其提供的所有数据,那么甚至不值得启动集群。
然而,这一小块微小的数据是约翰霍普金斯仪表板需要告知世界传染病的传播。
公平地说,你仍然可以用它做一些分析,即使是对这一点点数据。普华永道的数据科学家 Kamran Ahmad 前几天写了一篇很棒的博客,用 NumPy 分析了这一小部分数据,并通过 Matplotlib 和 Seaborn 将其可视化。编辑:[2020 年 2 月 14 日 Lewis Chou 的另一篇博客刚刚发表在走向数据科学上,其中他用各种数据来源整理了一些非常方便的图表,其中一些可能会让那些分析行为模式的人感兴趣。迁移和旅行数据分析、远程办公模式和一些医院仪表板。虽然没有直接使用新的冠状病毒数据集,但这种分析是 BlueDot 和 Metabiota 为他们的客户做的事情。
但是……人们仍然需要寻找关于新型冠状病毒本身的真正大数据。千兆字节在哪里?兆字节在哪里?
当你搜索“冠状病毒”时,HDX 只有约翰·霍普金斯大学的数据(在 COVID19 或 2019-nCov 上搜索,结果除了大量的误报之外什么都没有。)
Data.gov搜索结果出现 6 个“冠状病毒”数据集不幸的是,这些结果都是针对我们已知的另一种主要冠状病毒——SARS。不适合新的、独立的 2019 菌株。
再说一次,我确信像世卫组织这样的政府间机构,各种政府和公共卫生组织,医院,实验室和非政府组织有更多的数据,他们不能,也永远不会公开分享。但是希望,随着时间的推移,可以有一些更丰富的数据集可以与世界共享。或者,至少,与数据科学家和数据工程师专业人士在一些分析公私合作伙伴关系。
对于联系人追踪,我个人倾向于像 JanusGraph 这样的图形数据库。如果你有成千上万个案例,每个案例可能有数百个联系人要跟踪数周,这很容易导致数千万条边。您可以构建许多 Gremlin/Tinkerpop 查询。
对于各种时间序列分析,你可以看看锡拉。但是,同样,只有当你当前或预期的数据集是巨大的时。坦率地说,即使 Scylla 中有 100,000 条记录,考虑到我们一秒钟可以扫描 10 亿行,还是有点令人失望。
目前,那些拥有大数据背景、希望进行分析并从数字方面贡献自己的技能来解决这一问题的人需要有一点耐心。原始数据集对公众来说并不存在。但我确信,在适当的时候,你的技能和才能会对疫情前线的当权者有用。
聊天图像到文本对话
使用基于深度学习的光学字符识别(Tesseract 4)从聊天图像中提取 JSON 格式的消息
最近,社交媒体上流传着许多聊天图像,可能是与客户服务的聊天图像,或者只是来自 WhatsApp 或脸书的有趣聊天图像,也可能是任何重要的群体对话。这些数据在互联网上随处可见,却没有得到很好的利用。
Messagink 向我介绍了这个问题的一个有趣的用例。任务是将聊天图像中的信息提取成 JSON 格式。所以,我想为什么不探索这个领域,看看是否能在这个特定的领域有所成就。
我开始的任务是接收聊天图像并从中提取 JSON 对象。这个看似简单的任务包含了许多错综复杂的问题。每一个都要求我迭代我的方法,并使用机器学习概念来解决它们。让我们详细研究一下这些问题。
首先,我必须找到一个好的库,能够以非常高的精度检测图像中的文本。我搜索了很多,通读了很多库的利弊,最终敲定了谷歌的 tesseract 库(从 2006 年开始由谷歌赞助,在此之前由惠普实验室开发)。传统宇宙魔方是一个多阶段的过程。首先使用阈值对图像进行二值化,随后确定连接的部分和它们之间的连接,字符分类和聚集字符以形成单词、行、段落。更多细节可以在这篇论文中读到。
2018 年 10 月,他们推出了 tesseract 4,该产品在底层使用双向 LSTMs 来完成 OCR 任务。此后,他们进行了多次改进,最新版本于 2019 年 12 月 26 日发布。要了解更多关于宇宙魔方 4 如何使用深度神经网络的信息,请阅读这里的。我们将在所有任务中使用宇宙魔方 4。
宇宙魔方(谷歌照片)
最初,我只是输入一些聊天图像来吐槽文字。我用“宇宙魔方”包装宇宙魔方,因为使用它的结果是精确和准确的。我从图像中获取文本,但最难的部分是从这些文本中获取一些意义。例如,如果是两个人之间的聊天,我们如何分离文本信息,我们如何从图像中删除不想要的文本,我们如何检测信息的时间。所以这是接下来需要关注的几个挑战。
为了能够解决所有这些问题,主要问题是检测文本行周围的边界框。下图显示了文本周围的红色边框
红色边框(作者照片)
理想情况下,我想检测每个消息周围的边界框,但很难找到这样的工具。而“pytesseract”正在检测字符周围的单个边界框,这不符合我的目的。我在 tesseract 周围找到了另一个包装器“tesserocr ”,它展示了检测每行文本周围的边界框的功能,但是“tesserocr”的文本识别不如 pytesseract 准确,因此我决定使用 tesserocr 进行边界框检测,并使用 pytesseract 的 image_to_string 函数来识别该边界框中的文本。下面的代码片段显示了 tesserocr 和 pytesseract 的基本功能
final = cv2.imread(img, cv2.IMREAD_UNCHANGED)
# since tesserocr accepts PIL images, converting opencv image to pil
pil_img = Image.fromarray(cv2.cvtColor(final, cv2.COLOR_BGR2RGB))
#initialize api
api = tesserocr.PyTessBaseAPI()try: # set pil image for ocr
api.SetImage(pil_img) boxes = api.GetComponentImages(tesserocr.RIL.TEXTLINE,True) height, width, c = final.shape
print(height, width)
inc = int(0.01*width) for i, (im,box,_,_) in enumerate(boxes): x,y,w,h = box['x'],box['y'],box['w'],box['h']
crop_img = final[max(0, y-inc):min(y+h+inc,height), max(0, x-inc):min(x+w+inc, width)]
cv2_imshow(crop_img)
cv2.waitKey(0)
extractedInformation = pytesseract.image_to_string(crop_img)
print(extractedInformation)
最后,我们有了边界框和文本。现在必须好好利用这些边界框。考虑两个人聊天,第一个主要任务是准确分离他们之间的消息。我只是使用了每个边界框的起始边缘的 X 坐标,并将其与阈值进行了比较。阈值根据图像的宽度决定,以处理不同分辨率的图像。所以我希望 threshold 是这样一个值,即左边消息的边界框起始边缘的 X 坐标小于 threshold,而右边消息的边界框起始边缘的 X 坐标大于 threshold。在对 whatsapp 图像进行大量实验后,发现将阈值保持为图像宽度的 10%比较合适。这有助于隔离两个人之间的信息。下图显示了阈值如何映射到图像上。蓝点表示阈值,而红框是文本上的边界框。
隔离邮件的阈值(蓝色)(作者提供图片)
因为“tesserocr”每行检测一个边界框,所以长于一行的消息被分成多行。我不得不把属于同一信息的所有行整理在一起。文本周围的边框在这里也扮演了重要的角色。所以我注意到,同一封邮件中的边框的 Y 坐标之间的差异小于不同邮件中的边框。因此,我们可以再次使用一个阈值来决定是否对文本行进行排序。对不同分辨率的 whatsapp 图像进行实验,结果表明使用阈值作为图像宽度的 1%是最佳选择。在下图中,你可以看到同一封邮件中的行是如何足够近,而不同邮件中的行又是如何的远。绿色注释显示两个不同消息边界框之间的 Y 坐标差异,蓝色注释显示同一消息各行之间的 Y 坐标差异。
将文本行整理成消息的逻辑(作者提供照片)
不错!所以现在我们把文本行整理成信息,并在两个人之间分开。输出的下一个问题是在图像的页眉或页脚检测到了不必要的噪声。例如,在 whatsapp 聊天图像中,如果全屏拍摄,会在图像的顶部或底部检测到一些东西,如下所示。现在困难的部分是我们如何知道图像是裁剪过的还是完整的图像。我试着从字体大小中找出标题,或者试着把名字映射到头像图片附近,但是都没有给出有希望的结果。然后,我突然想到,来自同一来源的图像会有完全相同的模板。也就是说,如果截图来自特定人的 IOS 设备,它们将具有相同的标题和底部模板。我肯定可以使用图像减法来检测差异,并只取包含所有差异的最大边界框。但是实现比这个想法更难,我使用 opencv 的功能来提取这样一个边界框。我将使用来自同一来源的 2 个聊天截图来解释以下过程:
左:图片 1,右:图片 2(作者照片)
- 首先,我们逐像素地减去两幅图像。这有助于突出两幅图像的差异。然后,我们将这些图像的大小调整为 1/4,以加快处理速度。
两幅图像的差异(作者提供照片)
- 然后我们执行形态学闭运算。它在关闭前景对象内的小孔或对象上的小黑点时很有用。从而允许将附近的差异聚集在一起。我们执行 20 次闭合操作迭代两次,以完全聚集差异。
形态学封闭操作:先扩张后侵蚀(作者摄影)
- 然后,我们对从步骤 2 获得的结果应用阈值来加强差异。
使用阈值像素值 20 进行阈值处理(图片由作者提供)
- 之后,我们从结果图像中提取连接的组件,并在每个连接的组件周围绘制实体边界框。这会使图像中被主要差异包围的整个区域变亮。
连接组件分析后的输出(作者提供照片)
- 在此之后,我们运行最终的连接组件分析,以提取每个区域的边界框,在我们的情况下,我们希望提取中间的白色大框,因为这将有实际的聊天内容。
- 我们裁剪从图像中获得的第一个区域,因为它将包含人们之间的实际聊天,消除图像顶部或底部的任何噪声。在我们的例子中,结果如下:
左:裁剪后的图像 1,右:裁剪后的图像 2(作者提供的照片)
因此,最后我们能够裁剪图像,如果任何页眉或页脚存在。下一步是消除从边框中识别文本时遇到的一些噪声。在某些行中,“pytesseract”也探测到了时间,它被附加在文本的末尾。我们不得不把这段时间从文本中删除。 我们使用 12 小时和 24 小时格式的时间正则表达式来检测文本中的时间,并相应地将其去除。
在执行了所有这些步骤之后,算法就准备好了,并且对测试图像给出了良好的结果。我们在 messagink.com 部署了它,你可以在下面的网站上看到它正在工作。(您必须创建一个故事并选择导入选项。)
发现有趣的故事。使用我们的编辑器创建您自己的文本故事。免费发布。
messagink.com](https://messagink.com/)
然而,当我们开始在这上面扔随机图像时,出现了几个问题。我们的目标是尽快解决下面列出的所有这些问题:
- 脸书/电报图像—除了消息之外,他们还有头像,所以我们必须相应地改变我们的 X 轴阈值。
- 启用黑暗模式的截图效果不佳。
- 表情符号检测是一个巨大的挑战,这是我们接下来要做的。
- 需要处理来自连续图片的消息之间的重叠。
- WhatsApp 图片需要去除蓝色勾号。
我将在下一篇博客中公布我找到的解决这些问题的方法。让我开始黑这些,回头见…
感谢 Akshat Sharma 在这项工作中的合作:)!
更新:我也发表了博客的第二部分,请在这里阅读:
所以我又来了,第二部分是从聊天图像到文本对话。如果您还没有阅读第 1 部分,我…
towardsdatascience.com](/chat-images-to-textual-conversation-part-2-8260c09a032e)
聊天图像到文本对话:第 2 部分
在识别聊天图像中的文本消息时集成表情检测和识别
丹尼尔·科尔派在 Unsplash 上拍摄的照片
因此,我在这里再次与第二部分的聊天图像到文本对话。如果你还没有读过《T4》第一部,我强烈推荐你点击下面的链接阅读:
最近,社交媒体上流传着许多聊天图像,可能是与客户的聊天图像…
towardsdatascience.com](/chat-images-to-textual-conversation-c44aadef59fe)
问题与探索
这篇博客专门解决了第 1 部分没有解决的一个主要问题——“聊天图像中表情符号的检测和识别”。
这对我们来说只是一个开放性的问题,所以我和 Akshat Sharma 尝试了几种不同的方法。显然,这看起来像是物体检测和识别问题,所以我们想先检测表情符号,然后将它们分成几个类别。我们研究了解决这类问题的几种方法,发现人们通常使用卷积神经网络(CNN)。同样在这个领域,我们有大量的选择。这篇名为“深度学习的对象检测:综述”的论文对从基于区域的卷积神经网络 RCNNs 到只看一次(YOLO)的方法进行了很好的综述,这些方法可以专门用于对象检测和识别。通过几个博客和研究结果,我们得出结论,更快的 RCNN 是最准确的。虽然 YOLO 和单次检测器足够快,但在这些博客中有几次提到它们不适合小物体检测:( 1 , 2 )引用了几篇研究论文。
方法一:训练更快——RCNN
现在我们想训练一个深度神经网络,我们需要大量的训练数据。在这种情况下,训练数据将只是使用边框和表情符号的 unicode 来注释表情符号的聊天图像,以确定它们属于哪个类别。我们在互联网上找不到任何这样的数据集,决定编写自己的脚本来生成假的聊天图像以及表情符号注释信息。受到计算的限制,我们能够生成大约 1500 个聊天图像及其相应的表情符号注释。我们首先想尝试仅使用这么多训练数据。我们在训练(1300 幅图像)和测试(200 幅图像)之间划分数据集。下面是使用我们的脚本生成的示例图像:
左:使用我们的脚本生成的样本图像,右:生成的图像的注释
总共有 88 个表情类,每个类在训练集中有超过 350 个样本。
我基本上搜索了一下,找到了两个 Faster-RCNN 的实现。第一个是由 Kbardool 创作的,另一个是由 eleow 创作的。我不能让第一个实现在 GPU 上工作,因为它需要旧的 CUDA 和 CUDNN 版本,而 Google Colab 不允许降级。使用第二种方法,我能够在训练图像上训练网络,在 60 个时期的 1300 幅图像上训练几乎需要一天时间。最后,我对结果感到兴奋,但是,它没有像预期的那样工作,并且在聊天图像中检测到随机的表情符号。我认为这可能是因为我们的数据集非常小,聊天图像中可以检测到的表情符号的大小几乎是恒定的,非常小(15x24 / 24x24)。
我们回到绘图板,开始探索更多可以解决表情识别问题的方法。我在 CNN 领域做了更多的研究,发现 YOLOv3 和 RetinaNet 是单级检测器,并在以前的实现基础上进行了改进,用于更小的物体检测。然而,受计算能力和培训时间的限制,我们决定在再次探索这一领域之前,先坚持并尝试一些经典方法。
方法 2:经典图像处理和机器学习
我们再次从头开始,阅读并研究了现有的经典图像处理方法,这些方法可能对我们的问题有所帮助。现在我将讨论我们如何使用一些经典的图像处理以及机器学习来实现表情检测和识别的目标。
因此,首先让我们谈谈为了实现我们的目标需要解决的子问题。我们发现了以下子问题:
- 最初,我们希望准确地检测聊天图像中的表情符号。
- 下一步将是识别检测到的表情符号,并为它们分配正确的标签/类别。
- 然后,我们希望成功地将识别的表情符号与聊天图像中现有的文本识别相结合。
- 最后一步是最终模型的部署。
表情检测
**让我们从第一步开始,**检测聊天图像中的表情符号。为了检测表情符号,我们基本上使用图像处理技术来绘制感兴趣区域周围的边界框,其中存在表情符号/图标的可能性。因此,这里的基本思想是能够使用颜色/亮度来检测表情符号存在的可能区域。
- 首先,我们将输入图像转换为灰度图像,以便进一步处理。
2.下一步是降低聊天图像中物体的亮度,使它们看起来更暗,而背景仍然是亮的。这是为了能够区分物体和背景。下面是步骤 2 的代码片段:
maxIntensity = 90.0 # depends on dtype of image data# Parameters for manipulating image data
phi = 1.5
theta = 0.9# Decrease intensity such that
# dark pixels become much darker,
# bright pixels become slightly dark
newImage = (maxIntensity/phi)*(inputImage/(maxIntensity/theta))**1.5
newImage = array(newImage,dtype=uint8)
以下是步骤 1 和 2 的结果:
左:原始图像,中:灰度图像(步骤 1),右:降低强度后的图像(步骤 2)
3.现在,我们在图像中有了更暗的点,但是为了能够清楚地将物体从背景中分离出来,我们想要对图像进行阈值处理,并将每个像素转换为白色/黑色。
# image manipulation to change every as black/white pixel value
# based on some threshold value (here 140)for x in newImage:
for pixel in range(0,len(x)):
if(0 < x[pixel] < 140):
x[pixel] = 0
if(x[pixel] >= 140):
x[pixel] = 255
4.我们想使用*cv2 . connectedcomponentswithstats()*来找到聊天图像中对象周围的边界框,这个 cv2 函数只有在黑色背景和白色对象在它上面的情况下才能很好地工作。因此,我们使用高斯自适应阈值和反向二进制阈值将步骤 3 的结果图像转换为我们想要的形式。
binaryImg = cv2.adaptiveThreshold(newImage, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 131, 15)
5.现在,在从步骤 4 获得的结果图像中,我们可以使用*cv2 . connectedcomponentswithstats()*来查找图像中对象周围的边界框。具体来说,在提取盒子时,我们设置了一些阈值,以便根据表情符号的大小进行过滤。
_, _, boxes, _ = cv2.connectedComponentsWithStats(binaryImg)# first box is the background
boxes = boxes[1:]
filtered_boxes = []for x,y,w,h,pixels in boxes:
if(pixels < pixelsthresh and h < heightupperthresh and w < widthupperthresh and h > heightlowerthresh and w > widthlowerthresh):
#we only want boxes which contain emoji in them
filtered_boxes.append((x,y,w,h,pixels))
步骤 3、4、5 的输出显示如下:
左图:根据阈值更改单个像素值后的图像(步骤 3),右图:自适应阈值处理后的图像(步骤 4)
表情符号检测阶段的最终输出(步骤 5)
表情识别
现在,一旦我们从图像中获得了所有的对象,我们的第二步就是能够对它们进行分类。由于表情符号图像永远不会与地面真相有所不同,在方向或方向上不会有任何变化,所以逐个像素地比较它们是有意义的。我们研究了可用于此任务的几种方法,其中一种是使用 python 中的 Imagehash 模块,我们使用它进行了几次实验,但是对于不同的相似图像集,散列值变化很大,我们无法确定能够更好地概括所有相似图像集的单个阈值。
因此,我们想出了另一种方法来比较使用尺度不变特征变换(SIFT)算法生成的表情图像的 SIFT 特征。我们决定使用这种算法,因为它最适合我们的表情识别用例。因为在我们的例子中,我们只有不同大小的表情符号,而没有任何几何形状或方向的变化,并且 SIFT 特征对于图像的平移、旋转和缩放是不变的,所以我们预计它可能会解决我们的问题。现在,我们需要一个基本事实表情数据库,这样我们就可以找到检测到的表情符号的可能匹配。我收集了 29 个不同的流行表情图像,并使用 opencv 中可用的 SIFT 算法提取 SIFT 关键点和描述符进行初始测试。接下来,我们使用 David G. Lowe 在他的论文中描述的 K 近邻 SIFT 匹配算法,将第一步中检测到的每个对象的 SIFT 特征与表情数据库中的每个图像进行比较(地面真实 SIFT 关键点和描述符特征)。我们使用从 github 报告中获取的基本评分标准来返回提取图像和真实图像之间的最终得分。
def calculateScore(matches,keypoint1,keypoint2):
return 100 * (matches/min(keypoint1,keypoint2))
最后,我们挑选得分高于 8 的最高得分表情图像作为输入图像的最佳可能匹配。如果没有分数大于 8 的表情图像,则不存在可能的匹配,这有助于过滤掉在第一步中提取的非表情图像。这样,我们在步骤 1 中为每个提取的图像分配标签(表情 unicode)。
表情识别与现有文本聊天识别代码的集成
所以在第二步之后,我们已经成功地检测和识别了聊天图像中的表情符号。现在**下一步/第三步是将前两步的结果整合到我们现有的文本聊天识别代码中。**因此,为了在识别的文本中嵌入表情符号,我们使用了在步骤 2 中提取的表情符号边界框和在本博客第 1 部分中为每一行文本提取的文本边界框。这个想法是,文本的边界框应该几乎包含表情符号的边界框,缓冲区为+5/-5 px,以包括一些边缘情况。虽然这无助于确定表情符号在线与线之间的位置,但是我们可以成功地将表情符号添加到线的末尾。假设大多数人在队伍的最后使用表情符号,我们认为这将是一个很好的开始。将来为了使它更精确,我们可以寻找其他方法来处理这种情况。在步骤 3 之后,我们得到了一个成功运行的模型,在输出中,我们可以看到表情符号嵌入在输出 JSON 中。
部署
现在,第四步是在 Messagink 的应用程序( ios ,Android)和 网站 上部署这个模型,并在聊天故事格式中看到一些令人兴奋的结果。我们只是更新了服务器上的代码,并将 Messagink 的后端代码配置为在 Conda 环境中运行模型。我只是附上原始聊天图像和相应的聊天从这些图像生成的截图。可能会有一些差异,因为我们不能声称我们的模型是 100%准确的,但对高质量的图像来说,结果是惊人的,如下所示:
原始 Whatsapp 聊天图像(出于显示目的降低了质量等级)
由 Messagink 应用程序上的表情符号+聊天识别模型使用以上两张原始图像创建的故事。
正如我们在这里看到的,图像导入看起来非常好。要尝试此功能,请点击以下链接,使用 Messagink 网站编辑器中的导入按钮:
阅读你最喜欢类型的对话和文本故事。免费写你自己的文字故事。
messagink.com](https://messagink.com/editor/new)
我们成功地将表情识别集成到了现有的文本聊天识别模型中。万岁!!
但是我觉得这个模块还有很多地方需要改进。我们的下一个目标将按照优先顺序。
- 处理图像中的噪声,正如我们在上面生成的文本中看到的,每行的末尾都有很多噪声。我们将在这种情况下寻找噪声处理的方法。
- 此外,我们将在下一版本中加入对黑暗模式图像的支持。
到那时再见!再见!
聊天图像到文本对话:第 3 部分
处理聊天图像中的噪声,并为黑暗模式图像添加功能
感觉很好,回到这个项目的最后一部分:“聊天图像到文本对话”。但是在深入研究这篇文章之前,我建议先阅读前两部分:分别是聊天识别(第一部分)和表情检测(第二部分)。
我要感谢 Akshat Sharma 为这个项目做出的所有贡献。
现在让我们来看看我们的目标,然后我们来看看我用来实现这些目标的方法:
1.处理黑暗模式图像
第一步是识别图像是暗模式还是亮模式。为此,我们使用一个非常简单的技术来寻找聊天图像的平均像素值,如果图像大于阈值,那么我们将其标记为“亮”,否则为“暗”。对于我们的用例,我们使用阈值 127。
numpyImg = cv2.imread(img)
if(np.mean(numpyImg) > 127):
mode = 'Light'
else:
mode = 'Dark'
现在,对于第二步,我们知道 tesseract 检测白色背景上的黑色文本具有 100%的准确性,但是它在检测黑色背景上的白色文本时无情地失败了。我们在我们的管道中使用了一个简单的技巧,如果图像是“暗的”,在第二个博客中进行强度处理后(表情检测中的步骤 2)屏蔽当前图像。
#Masking
#newImage1 is obtained from step 2 in [Emoji Detection Blog](/chat-images-to-textual-conversation-part-2-8260c09a032e)
mask = cv2.bitwise_not(newImage1)
2.从输出中消除噪声
输出中出现的大部分噪声是因为 tesseract 将表情符号、蓝色记号、时间戳识别为一些特殊字符,如@、#、W、Y、(、$。时间戳已经在聊天识别(第 1 部分)中进行了处理,使用正则表达式去除行尾的匹配部分。
当 tesseract 检测到聊天图像中的文本时,它也会检测到蓝色勾号,并在其周围绘制一个边界框,但是与其他边界框的面积相比,该边界框的面积非常小。因此,我们只计算每个边界框的面积,如果它小于阈值,就忽略它。我们用于聊天图像的阈值根据图像大小进行缩放,我们将其设置为
blueTickArea = 0.00128 * width * height
下一步是消除表情符号引起的噪声,我们已经有了另一种方法来检测第二部分中的表情符号,所以这里我们不希望 tesseract 检测表情符号。基本上,我们可以让表情符号在图像中不可见,然后输入宇宙魔方。我们使用一个非常简单的技术来实现这个想法。
在第 2 部分中进行表情符号检测时,我们已经发现了表情符号周围的边界框。对于浅色模式的图像,我们只是用白色填充这些表情符号边框,而深色模式的图像则使用黑色。这项技术阻止了 tesseract 检测图像中表情符号的任何特殊字符。
下面是实现上述想法后的输出。黑暗模式不是很准确,因为它错过了 2 心,但仍然表现很好。
表情符号的去噪——左:用白色填充的表情符号(浅色模式),右:用黑色填充的表情符号(深色模式)
因此,我们最终消除了聊天图像中的噪声,最终输出非常流畅。下面是在结束时第二部分中图像的输出。现在,您可以在下面的输出中看到改进,没有不必要的字符,这些字符以前大多出现在文本行的末尾。
故事由最终聊天+表情符号识别模型创建,Messagink 应用程序使用来自第二部的两幅输入图像去除了噪声。
这就是聊天图像到文本对话的全部内容。希望你喜欢看我的博客。
您可以通过点击以下链接,使用 Messagink 网站编辑器中的导入按钮,尝试将聊天图像导入文本故事的最新功能:
阅读你最喜欢类型的对话和文本故事。免费写你自己的文字故事。
messagink.com](https://messagink.com/editor/new)
也可以尝试一下 Messagink 的 app 中可用的编辑器中的功能( ios ,Android)
再见
与前政府人工智能顾问聊天
转行的主要收获
付费墙后面的股票?点击这里和我的 好友链接 一起看故事。
注意:这篇文章没有任何美国政府的秘密,工具,或者任何类似的东西。这是一个关于真正的摇滚明星“幕后”数据科学家的故事。
华盛顿到处都是在政府部门工作的人,他们的工作需要高度保密。当你问一个经典的*“你是做什么的?”时,不要问更多关于工作的事情,这是一个无声的社会规则而他们的回答是“我在政府机关工作”*,仅仅是因为他们不能说,如果说了,那很可能就是表面文章或者谎言。
几天前,我们邀请了一些朋友和家人来吃早午餐。赶上这一年,我的丈夫提到今年我过渡到了数据科学,由于这一点,我最终与约翰(化名)进行了一次关于人工智能的惊人的职业导向的对话。
我发现他的故事引人入胜,鼓舞人心。我请他给我这样的人工智能新从业者提建议。我将与你分享我们谈话的一部分,但首先,让我告诉你一些关于他的事情。
约翰正处于职业生涯的最后十年,他计划在 7 年左右退休。在美国国防部和中情局的一些项目中工作了几年后。他获得了侧重于人工智能的计算机科学硕士学位和计算机科学博士学位。他的博士研究重点是使用 C 和 C++进行图像识别。
他认识到,与我们现在拥有的工具相比,20 年前他不能充分发挥他以前知识的潜力,因为计算机能力如此之小,如此之贵。每个项目都是一个挑战。现在,他对计算能力如何改变人工智能的方式感到惊讶,特别是在过去的 3 年里,它彻底改变了这个行业。
在接近退休时进行职业变动并不常见,也不被视为明智之举,但他觉得他工作的政府部门在采用这些新的强大工具方面行动太慢,他不想在朝九晚五的时间里呆在那里,等待批准做他的工作。
他转到了私营部门,现在他很高兴拥有一个部门,但在他职业转变的开始,他遇到了一个问题:人工智能工具在过去 3 年中进步如此之大,即使他对行业和数学的整体理解很强,他也需要赶上新的工具和趋势。
他对继续保持行业相关性的建议总结为三点
永不停止学习
太明显了?
过去的 5 年改变了数据科学的研究方式,新的工具、新的编程语言、新的预训练算法以及更多的数据。该行业的所有这些进步都将继续推进,尽管他预测在未来 8 年内我们的处理能力将达到一个上限,但他几次提到了永不停止学习的重要性。
“在科技行业,你能做的最糟糕的事情就是相信自己什么都懂,并且只对一种编程语言感到舒服。我们这些技术人员往往对自己的知识有很强的自我意识,这种自我意识让我们处于权力的位置,也可能把我们踢出这个行业。”
我问他是否有一个他喜欢学习东西的地方,他提到他不是只和一个学习风格的平台“结婚”。博士后,在这个家校上过课,在 Coursera 上刷新过 SQL 技能,在 DataCamp 上刷新过 Python 3 和面向对象编程,在 MIT 上用 Python 深度学习,在华盛顿大学上在线课,在 YouTube 上,在 StackOverflow 上,在 Quora 上解决具体问题。
对于项目管理,他参加了 PMP,重点是敏捷和其他工作所需的培训和认证。
尽管他没有特别推荐,但他喜欢以演讲者或与会者的身份参加与行业相关的会议。他发现这是一个惊人的工具,可以有条理地了解“那里有什么”,并与新老行业参与者交谈。
与人交谈
“抱歉,如果我对技术人员有成见,但我们不倾向于与人交谈!”他说。“你不知道工具进步的速度有多快,等到做了一些教程,写了一篇论文,或者组织了一个研讨会,新的“东西”已经过时了。与人交谈是了解如何改进工作的最快方式”。
“我仍然一个月至少去两次聚会。这很奇怪,因为大部分时间都是年轻人,而我是白人少数群体中的一员
我想补充一个额外的好处:建立专业社区是无价的。除了拥有第一手的知识,你还可以从“一直在那里”的人那里得到支持和指导。如果你在华盛顿特区,想成为 女性 in Data 或/和 拉丁裔 in Tech 的一员,请给我发邮件至 hola[at]brendahali.com
学会有效沟通
有效的沟通技巧被低估了。新【人工智能】从业者需要学会,没有人在乎你的过程,没有人在乎你是怎么得到那个 p 分的。人们和管理者希望看到什么结果。尽可能高效地传达这些结果是你的责任。试图理解你不是他们的责任。”
“但是你如何提高你的沟通技巧呢?你是如何提高自己的能力的,有什么具体的方法吗?”——我问。
“读书,批判性地大量阅读。不仅仅是关于这个行业,而是阅读小说、传记、文章和明显有效的沟通书籍。 记录下你自己的谈话或陈述,然后分析你需要改进的地方。有效的沟通是需要练习的,也是可以学习的。”
总结
约翰几次提到他有多高兴,因为我决定现在开始这个职业运动,以及一个年轻的拉丁人在构建算法和收集数据方面有多重要。用他的话说,“我们生活在人工智能的黄金时代,我们需要更多的人进入这个领域”。
人工智能正在快速发展,无论你的资历、你应用技能的领域,还是你开发项目的预算,都无关紧要。为了确保你的工作和技能是相关的,你应该永不停止学习,建立一个网络,学习如何有效地沟通。
使用 Rasa 2.0 轻松构建聊天机器人:一步一步的教程
实践教程
从安装到与之聊天
什么是 Rasa?
Rasa 是一个开源的机器学习框架,用于自动化的文本和基于语音的对话。理解消息,进行对话,并连接到消息传递通道和 API。
Rasa 2.0 有什么新功能?
易于初学者使用,用 YAML 替代 markdown。
Rasa 的安装
这里的基本先决条件是 Anaconda 。它有助于处理包并防止不同的代码依赖相互干扰。
无论您使用的是 Windows 还是 Linux 系统,这些命令都应该有效:
conda create -n rasa python=3.6
conda activate rasa# pip install rasa[full] # adds all dependencies of rasa
pip install rasa
就是这个!
如果你使用pip install rasa[full]
,它将为每个配置安装 Rasa 的所有依赖项(spaCy 和 MITIE ),让你的生活更轻松。
注意:
在安装过程中,Tensorflow 和 Rasa(即 NumPy)的依赖关系存在冲突。Tensorflow 需要 Numpy <0.19, while Rasa requires 0.19.4.
If you try running Rasa, you will run into a runtime error.
To solve that, just downgrade your Numpy:
pip install numpy==0.19
Before We Start
Let’s understand some basic concepts in Rasa.
Source: Author
Intent
What is the user intending to ask about?
Entity
What are the important pieces of information in the user’s query?
Story
What is the possible way the conversation can go?
Action
What action should the bot take upon a specific request?
Let’s Start
The first thing to do is:
rasa init
You will then be prompted to create a sample Rasa project in the current folder.
Source: Author
This is what the sample project will look like. 模型文件是在我们训练模型后创建的。
第一站:nlu.yml
nlu.yml(来源:作者)
这是文件的样子。一开始是很直观的。
为了创建你自己的定制聊天机器人,你必须把一些意图编码进去。如果你正在为一个城市制作聊天机器人,目标应该是旅游目的地、餐馆等。
确保意图名称不重复。
为了放置一个新的意图,我们只需要跟随上面的任何一个意图。意图告诉我们意图的名称。例子告诉我们关于这个意图的语言是什么样的。确保至少给出 2 个例子。
第二站:actions.py
actions/actions.py(来源:作者)
为了创建您自己的自定义操作,可以遵循此示例。
你必须创建一个动作的子类,它将有两个功能,名字和运行。函数 name 将返回聊天机器人将使用的动作名称。函数 run 将保存您的动作所执行的功能,例如计算一些数字、从互联网上获取数据等。使用 **dispatcher.utter_message,**您可以通过聊天机器人发送回复,例如计算的答案。
确保任何两个动作的名称不冲突。
第三站:domain.yml
domain.yml(来源:作者)
domain.yml 包含聊天机器人的领域知识,即它需要操作的信息(人们会问什么,它必须回复或做什么?).
如果您想在您的聊天机器人中使用动作,那么您还需要将动作部分添加到该文件中。如果没有,跳过这一部分。
actions:
- action_1
- action_2
在动作部分下,输入您在上一步中创建的动作的名称。
在意图部分,你必须输入你的定制意图。
下一步将是在响应部分下创建话语,即当用户提出关于意图的问题时,你的聊天机器人将给出的答案。
确保响应名称不重复。
给出独特的回答名称,并在它们下面给出当被问及某个特定意图的问题时你想给出的答案。
第四站:stories.yml
stories.yml(来源:作者)
这是我们把所有东西缝合在一起的地方。
在故事部分下面是用户和聊天机器人之间对话的各种方式。对于你的聊天机器人,你把你的故事放在这里。
按照上面的例子,你将首先把一个故事名称和步骤放在里面。你可以在故事的动作部分输入话语和动作的名称。
因此,你的意图进入故事的意图部分,而你的行动和话语进入故事的行动部分。
训练聊天机器人
现在,您已经将关于您的自定义聊天机器人的所有信息放在了不同的文件中。让我们来训练你的聊天机器人。
rasa train
简单地说,输入上面的命令,让奇迹发生吧!
运行聊天机器人
现在,你可以将这个聊天机器人集成到你自己的网站中,Facebook Messenger,Slack,Telegram,Twilio,Microsoft Bot Framework,Cisco Webex Teams,RocketChat,Mattermost 和 Google Hangouts Chat。你也可以将聊天机器人连接到任何其他平台,但你必须创建自己的定制连接器。
现在,让我们在 localhost 上测试它。
**注意:**如果你已经在你的聊天机器人中使用了 actions ,那么,你需要取消注释 endpoints.yml 中的 action_endpoint 部分。然后,打开一个单独的终端。确保您在 Rasa 文件夹中。然后,你必须运行rasa run actions
现在,关键时刻到了。
有两种方式运行 Rasa:在 shell 中和在本地主机上。
在酝酿中
要在 shell 中运行 Rasa,只需在 Rasa 文件夹中打开的终端中键入命令:
rasa shell
这个命令将启动 Rasa 服务器,您将可以在终端中与聊天机器人对话。
Rasa Shell(来源:作者)
在本地主机上
另一种方法是在本地主机服务器上运行 Rasa。为此,只需从 Rasa 文件夹中打开的终端运行以下命令:
rasa run
这将在您的本地系统上运行 Rasa,并在本地主机的 5000 端口上公开一个 REST 端点。
与聊天机器人交谈
在酝酿中
如果你正在运行rasa shell
命令,那么你可以直接在终端中与聊天机器人进行交互。像这样:
Rasa Shell(来源:作者)
机器人回答“嘿!“你好吗,”向我问好。
在本地主机上
为了与 Rasa 服务器公开的 REST 端点通信,您可以使用 cURL 命令(针对 Linux 爱好者)或 Postman。我更喜欢邮差,因为它好用。
来源:作者
您必须向 URL 发出一个 POST 请求:
http://localhost:5005/web hooks/rest/web hook
在请求的正文中,选择 Postman 中的 raw 选项。
在 JSON 格式中,你要指定两个键:发送方和消息。
在发送者中输入一个虚拟值,这个消息将成为你对聊天机器人的查询。
您将返回一个 JSON,包含关键字: recipient_id 和文本。文本将有聊天机器人对您的查询的回复,并且收件人 id 将与您在请求中发送的发件人密钥相同。
摘要
我们知道使用 Rasa 创建聊天机器人是多么容易。我们理解了 Rasa 框架中的基本概念,即意图、实体、行动和故事。我们知道如何创建一个适合我们自己需求的定制聊天机器人。我们知道如何在 shell 和本地主机上运行 Rasa(并与之交互)。我们也了解了安装和运行 Rasa 时可能出现的各种陷阱。
聊天机器人和自然语言搜索
入门
集成搜索的三层聊天机器人架构
这是对我们在设计聊天机器人时经常面临的技术选择的一种非正式看法,即我们应该构建聊天机器人还是自然语言搜索(NLS),或者两者兼而有之?两者的主要动机都是让企业数据和应用程序(更)易于被每个公司员工访问,以促进知识共享和协作。为此,我们探索了两种集成架构:
- 支持搜索的聊天机器人
- 对话式搜索
介绍
关于聊天机器人已经说了很多,也写了很多。然而,大多数讨论都集中在面向消费者的机器人上——这种价值数百万美元的机器人将从根本上改变你公司的形象,并让你在场外节省几百万美元。不用说,建造这样一个独特的机器人也需要几十万美元的投资。尽管如此,如果数学加起来;收获就是收获,不要问任何问题!
在本帖中,我们将关注另一种慢慢抬头的机器人——企业内部面对机器人的“内部”员工。
如果你在任何一家拥有 10000 多名员工的公司工作,你会看到一些机器人在你的公司里到处涌现,让你(或至少承诺让你)可以轻松访问财务、人力资源、IT 服务台等。系统。我们将这些机器人称为面向员工的机器人。Gartner 在其研究报告中也强调了这一趋势:“聊天机器人的部署不再只是面向客户,而是越来越多地面向企业内部的员工”
这些面向员工的机器人不同于上述面向消费者的机器人,至少有以下 4 个特征:
- 这种机器人的主要动机是让公司的每个员工都可以(更)访问企业数据和应用程序,促进协作,等等。问一问一家相当大的公司的任何员工,他们都会一致同意所有的企业数据都会进入“黑洞”。您会听到这样的说法,“企业搜索不起作用,不可用”,“数据分布在各个小仓库中”,等等。
- 一个组织内的多名员工面对机器人,而“一个”消费者面对机器人,这是公司的形象。
- 这种机器人的投资回报率通常很低,这意味着他们的预算有限。因此,我们基本上需要以更低的价格提供相同或相似质量的机器人。当然,有人可能会说,很难量化这种员工面对机器人的“创新”方面,这通常是员工的满意度和/或通过使用新的创新技术获得的知识。
- 每个面向 bot 的员工所使用的技术是不同的。因为它们很可能是由组织内的不同团队开发的,所以它们偏向于单个团队的技术偏好。这导致了一个由 IBM Watson Assistant、Google DialogFlow、Amazon Lex 组成的分散世界——再加上一些更专业的初创公司——你就明白了。
这种分散景观的问题是,虽然它在实验水平上工作,但操作和维护它是相当不可能的。多样化的技术堆栈进一步暗示,作为一个组织,您将不会从商业产品的任何批量折扣中受益。因此,这种方法根本不可伸缩;机器人死亡率将很快开始与您组织中新出现的机器人数量竞争。
现在,让我们把重点放在一个有趣的技术选择上,这是我们在研究/设计这类机器人时经常面临的,也就是说,
我们应该建立一个聊天机器人还是一个自然语言搜索(NLS)工具,或者两者兼而有之?
定义
让我们尝试定位这两种技术:
IT 服务台聊天机器人(图片由作者提供)
聊天机器人 :对话型。
在其非常基本的形式中,给定一个由人类用自然语言提出的问题(我们离机器人对机器人的交流有点远),机器人用其相应的答案来回答,最好也是用自然语言。
(还有其他类型的机器人,它们更加面向操作,例如提出服务请求或采购订单。在本文中,我们主要关注以信息检索为主要目标的机器人和搜索工具。
自然语言搜索(NLS) :介绍搜索最好的方式当然是想到 Google。我们每天都用谷歌搜索,但是今天我们基本上是利用“基于关键词的搜索”。稍微思考一下,你就会想起我们是如何在谷歌中输入关键词之前将我们的查询分成关键短语/关键词的。
有了 NLS,我们谈论的世界将不再需要使用它,而是以句子的形式直接在谷歌中输入查询,就像你今天和朋友聊天一样。如果你没有尝试过,谷歌今天确实支持 NLS。它基本上使用了被称为“知识图”的东西,这是一个巨大的人工管理的知识库,捕捉真实工作实体和事件之间的关系;因此很快就达到了极限。例如,尝试以下查询序列:
自然语言搜索的局限性(作者截图)
从截图中可以注意到,第二个后续查询回复到通常的基于网页的列表响应,尽管后续问题的答案“烟雾和有毒空气”清楚地存在于其知识库(KB)中。(免责声明:谷歌的内部工作每天都在变化,但我希望基于关键词的搜索和 NLS 的区别从叙述中清晰可见。)到目前为止,我们只探索了 NLS 的查询部分。
对于答题部分,答案也应该是自然语言的句子,而不是谷歌目前显示的段落摘录。生成这样的答案/句子包含在自然语言处理的一个子领域中,称为自然语言生成(NLG)。
最先进的
在一个理想的世界里,没有技术限制,就没有必要进行这种讨论;即需要区分聊天机器人和 NLS。给定自然语言的用户查询,机器人能够做以下 4 件事:
- 理解用户意图;
- 从知识库中检索相关内容(实时);
- 合成答案并回复用户(用自然语言);
- 保留对话内容,以回答用户的任何后续问题。
问题是,目前在实现上述工作流程时存在多种技术限制。下图试图对不同的技术进行定位。
聊天机器人的智商(图片来自作者)
当前的聊天机器人引擎在第二列,我们需要提供一组问题、问题变体和它们相应的答案来构建一个机器人。
这些问题可以分为“意图”几类。问题变体,在机器人术语中称为“话语”,指的是最终用户可以提出相同问题的样本变体。这个想法是提供 5-10 个这样的话语(每个问题)作为输入,基于此,机器人将有希望能够理解问题的 50 种不同的变化。大多数 bot 引擎使用统计 NLP 技术,例如 tf-idf、n-grams、词袋、文本分类(SVM),来将最终用户的查询映射到意图。
这在合理的程度上是可行的;然而,主要的挑战是关于硬编码的答案,需要提前提供,这反过来限制了机器人可以回答的问题的范围。
更具伸缩性的方法是在这里使用 NLS。有了这样的集成,只需将机器人指向一个 KB(文档存储库)就足够了,机器人将能够在运行时通过搜索 KB 文档(第 3 列)来检索用户查询的答案。
这里的技术限制是,搜索文档可能比将用户查询映射到意图的准确度低得多。此外,大多数当前的搜索技术致力于相关性度量(相似性分数)。因此,搜索输出是相关段落的列表,而不是自然语言中的“精确”答案——NLG 还没有出现。
总而言之,聊天机器人和 NLS 目前都还没有成熟到可以互相替代的程度。这两者是互补的,在续集中,我们将重点介绍一些有趣的方法来集成它们。
聊天机器人-NLS 集成架构
支持搜索的聊天机器人
一个独立的聊天机器人通常是不够的。扩展了搜索功能的聊天机器人在大多数情况下是有意义的。
计划一个三层聊天机器人架构,如下图所示:
1.带有预定义问答集的聊天机器人仍然是切入点,可以把它看作是第一道防线。
2.如果机器人遇到无法映射到其预先配置的意图之一的用户查询,它会对其知识库执行 NLS。这是第二道防线。
3.如果用户甚至对搜索结果都不满意,请计划最终移交给现场代理。
三层聊天机器人架构(图片由作者提供)
三层策略非常强大,我们发现它在客户服务、帮助台等方面非常有用。用户查询的范围可能非常一般的场景类型。保留聊天机器人作为入口点是有意义的,至少对于那些用户查询可以匹配现有意图的情况是如此;答案将是精确而中肯的——提高用户满意度。与此同时,我们不能无休止地向聊天机器人的问答库添加问答,因为这样会影响意图识别的准确性。因此,建议采用如上所示的分层架构。
我们将在随后的博客文章中提供我们的 NLS 堆栈的更多细节。就目前而言,足以说明我们正在使用一个整合了 ElasticSearch 和 DRQA 的定制解决方案(在 2019 年柏林 Buzzwords 上展示, link ),这是一个神经网络架构,可以回答脸书研究公司的开放领域问题。
对话搜索
对话式搜索(图片由作者提供)
在这种情况下,聊天机器人的主要目标是“搜索”。将聊天机器人与搜索整合在一起允许用户以更对话的方式进行搜索。
用户可以与机器人交互以:
- 获取有关可用搜索过滤器的更多详细信息。当用户是该领域的新手时,这可能非常有帮助,并且机器人可以帮助用户导航搜索过滤器,例如标签、本体、文档类型。
- 逐步细化搜索结果,直到他们找到他们正在寻找的文档/报告。
对话式搜索作为一种查询 SQL 数据库的方式越来越受欢迎,例如 Power BI — NLP 搜索、 Salesforce Photon 。
进一步阅读
- E.里恰尔代利·比斯瓦斯。基于强化学习的自我改进聊天机器人。第四届强化学习与决策多学科会议( RLDM ,2019 ( 论文)。
- D.比斯瓦斯。保护隐私的聊天机器人对话。关于保护隐私的机器学习的第 34 届 NeurIPS 研讨会( PPML ),2020 年 12 月(论文)。
- 长度比斯瓦斯·巴利甘德。用 ElasticSearch 和脸书的 DrQA 构建企业自然语言搜索引擎。2019 年第十届柏林流行语大会。
作弊代码,以更好地与 Plotly Express 可视化
使用 Python 的 Plotly Express 库创建各种交互式数据可视化的快速指南。
Matplotlib 和 Seaborn 是数据科学中用于可视化的两个最流行的库。我想展示如何使用 Plotly 库中的 Plotly Express 模块,我们可以获得 Seaborn 和 Matplotlib 的简单性,但实现更深入和交互式的可视化。
Plotly Express 是一个简单易用的 Plotly 库的高级接口。那么,让我们看看如何开始。
进口
import plotly.express as px
让我们看看 Plotly Express 模块中已有的一些数据集:
tips = px.data.tips()
#total_bill, tip, sex, smoker, day, time, sizegapminder = px.data.gapminder()
#country, continent , year, lifeExp, pop, gdpPercap, iso_alpha, iso_numgapminder_canada = gapminder.query("country=='Canada'")
gapminder_oceania = gapminder.query("continent=='Oceania'")
gapminder_continent = gapminder.query("continent != 'Asia'")iris = px.data.iris()
#sepal_length, sepal_width, petal_length, petal_width, species, species_idelection = px.data.election()
#district, Coderre, Bergeron, Joly, total, winner, result, district_id
柱状图
让我们来看看使用直方图的 tips 数据集,因为该数据包含许多分类变量。对于第一个示例,我将使用总账单作为变量来绘制直方图。
fig = px.histogram(tips, #dataframe
x = "total_bill", #x-values column
nbins = 30 #number of bins
)
fig.show()
总账单价格直方图
使用 Plotly Express,我们可以创建一个交互式直方图。我们可以看到 x 轴上的 bin 范围、total_bill 值以及光标悬停时每个点的计数。
让我们通过考虑第二个变量——性别来提升直方图:
fig = px.histogram(tips, #dataframe
x = ”total_bill”, #x-values column
color = ”sex” #column shown by color
)
fig.show()
与性别相关的总账单价格直方图
现在,我们可以看到男性和女性在总账单中的数量,但是除了悬停交互之外,我们还可以隔离直方图以显示男性、女性或两者。
直方图的另一个小更新可以是添加地毯图、箱线图或小提琴图:
fig = px.histogram(tips, #dataframe
x = ”total_bill”, #x-values column
color = ”sex”, #column shown by color
marginal = ”rug”, #plot type (eg box,rug)
hover_data = tips.columns #extra info in hover
)
fig.show()
与性别相关的总账单价格直方图,包括数据点的地形图
在这里,我使用了一个地毯图,现在我们可以在直方图中看到与下面的条块相关的数据点。正如你可能想知道的,你也可以和地毯图互动。
散点图
散点图是另一种流行且有效的可视化形式,它不仅简化了表达,还增强了表达。这里我们看到的是虹膜数据集;不同植物种类的大小。
fig = px.scatter(iris, #dataframe
x = "sepal_width", #x-values column
y = "sepal_length" #y-values column
)
fig.show()
间隔宽度和间隔长度散点图
我们可以通过添加颜色、大小和更多的交互数据,使这个散点图更加动态,信息更加丰富。
fig = px.scatter(iris, #dataframe
x = ”sepal_width”, #x-values column
y = ”sepal_length”, #y-values column
color = ”species”, #column shown by color
size = ’petal_length’, #col shown by plotsize
hover_data = [‘petal_width’] #extra info in hover
)
fig.show()
间隔宽度和间隔长度散点图(颜色=种类,大小=花瓣长度)
能够在一组轴上通过添加不同的颜色将植物隔离或分组,有助于我们不必创建三个单独的地块来进行比较。
线图
传统的线图也很简单,用 Plotly 表示。
fig = px.line(gapminder_canada, #dataframe
x = "year", #x-values column
y = "lifeExp" #y-values column
)
fig.show()
60 年间加拿大人预期寿命的线形图
折线图对于比较数据集中的两行非常有用,这可以通过向图中再添加一个属性来实现。在这种情况下,颜色代表国家。
fig = px.line(gapminder_oceania, #dataframe
x = "year", #x-values column
y = "lifeExp", #y-values column
color = 'country' #column shown by color
)
fig.show()
澳大利亚人和新西兰人 60 年间的预期寿命线图
这里我们有两个来自大洋洲的国家,所以它们都被分配了一种颜色,而不必提及国家的名称,只是颜色参数的列名。
除了大洋洲之外,我们还可以添加更多的洲,但是我们如何避免图表变得过于庞大呢?
将洲显示为颜色,然后将线组设置为国家,这样我们就可以直观地看到不同洲的预期寿命,而不必计算该洲内所有国家的平均预期寿命。
fig = px.line(gapminder_continent, #dataframe
x = ”year”, #x-values column
y = ”lifeExp”, #y-values column
color = ”continent”, #column shown by color
line_group = ”country”, #group rows of a column
hover_name = ”country” #hover info title
)
fig.show()
60 年间世界各国预期寿命的折线图(不包括亚洲)
如果个人观察是首选的,我们可以再次隔离大陆的线,以及检查每条线所代表的国家的悬停能力。
条形图
更简单的条形图代码。
fig = px.bar(gapminder_canada, #dataframe
x = ’year’, #x-values column
y = ’pop’ #y-values column
)
fig.show()
加拿大 60 年人口柱状图
我们可以添加更多关于加拿大人口随时间变化的信息,包括使用颜色的预期寿命。使用条形图的标签属性将 axis 标签 pop 升级为 population of Canada,并为悬停功能添加更多详细信息。
fig = px.bar(gapminder_canada, #dataframe
x = ’year’, #x-values column
y = ’pop’, #y-values column
hover_data = [‘lifeExp’, ‘gdpPercap’],#extra hover info
color = ’lifeExp’, #column by color
labels = {‘pop’:’population of Canada’}#label change
)
fig.show()
加拿大 60 年人口柱状图(带颜色=预期寿命)
上面,我们可以用一张图表有效地描绘出加拿大人口随时间变化的两种趋势。
回头看看 tips 数据集,我们可以将更多的信息叠加到一个柱状图可视化中。
fig = px.bar(tips, #dataframe
x = ”sex”, #x-values column
y = ”total_bill”, #y-values column
color = ”smoker”, #column shown by color
barmode = ”group”, #separate filter (smoker)
facet_row = ”time”, #name of grid row
facet_col = ”day”, #name of grid column
category_orders= {“day”: [“Thur”, “Fri”, “Sat”, “Sun”],
“time”: [“Lunch”, “Dinner”]}
#grid arrangement
)
fig.show()
男性和女性在周四至周日午餐和晚餐期间支付的总账单的条形图(带颜色=吸烟者)
我们可以观察数据集的五列,并伴有一些交互性。通过使用行和列方面,我们有一个网格布局,用于一周中的日期和用餐时间;由较小的条形图组成,其中包含人员类型(吸烟者和性别)及其总账单的摘要。
三维散点图
来个 3D 吧。在这里,我们正在查看选举数据集,其中按地区比较了蒙特利尔市三名市长候选人的投票和结果。
fig = px.scatter_3d(
election, #dataframe
x = ”Joly”, #x-values column
y = ”Coderre”, #y-values column
z = ”Bergeron”, #z-values column
color = ”winner”, #column shown by color
size = ”total”, #column shown by size
hover_name = ”district”, #hover title
symbol = ”result”, #column shown by shape
color_discrete_map = {“Joly”: “blue”, “Bergeron”: “green”,
“Coderre”:”red”}
#specific colors for x,y,z values )fig.show()
蒙特利尔不同地区市长候选人票数的三维散点图(颜色=候选人获胜,形状=获胜类型
结合 Plotly 的悬停交互性,该图可以显示选举数据集中的所有信息。
地理学的
地理图表用于更独特的情况,但也可以使用 Plotly Express 轻松完成。
fig = px.scatter_geo(gapminder, #dataframe
locations = ”iso_alpha”, #location code
color = ”continent”, #column shown by color
hover_name = ”country”, #hover info title
size = ”pop”, #column shown by size
animation_frame = ”year”,#column animated
projection = ”orthographic”#type of map
)
fig.show()
一段时间内各国人口变化的正射曲线图
我们对观想有一个非常有用的补充;也就是动画交互性。对于这个数据集,我选择了年份作为动画帧;因此,我们可以自动播放所有年份,也可以手动选择年份。
虽然上面的球形可视化看起来很有趣,但下面的地图可视化对一个人观察和分析信息会更好。
fig = px.choropleth(gapminder, #dataframe
locations = ”iso_alpha”, #location code
color = ”lifeExp”, #column shown by color
hover_name = ”country”, #hover info title
animation_frame = ”year”, #column animated
range_color = [20,80] #color range
)
fig.show()
各国预期寿命随时间变化的 Choropleth 图
现在,我们有了一个非常清晰的图像,显示了随着时间的推移,世界各地的预期寿命。
动画散点图
动画散点图(气泡图)是我最喜欢的可视化,因为它很好地解释了信息,并且非常动态。
px.scatter(gapminder, #dataframe
x = "gdpPercap", #x-values column
y = "lifeExp", #y-values column
animation_frame = "year", #column animated
animation_group = "country", #column shown as bubble
size = "pop", #column shown by size
color = "continent", #column shown by color
hover_name = "country", #hover info title
log_x = True, #use logs on x-values
size_max = 55, #change max size of bubbles
range_x = [100,100000], #axis range for x-values
range_y = [25,90] #axis range for y-values
)
各国人均国内生产总值、预期寿命和人口随时间变化的气泡图
因此,使用 Plotly Express,我们可以实现具有交互性的可视化,只需要几行直观的代码。
Google Colab 的备忘单
在本教程中,您将学习如何充分利用 Google Colab。
形象演职员表: 中等
介绍
Google Colab 是一个了不起的工具,它让我们建立并执行一个杰出的数据科学模型,并为我们提供了一个记录我们的旅程的机会。由于 Google Colab 为我们提供了代码单元格来键入代码,它还为我们提供了文本单元格来添加文本。在本教程中,我们将更多地关注文本单元格,看看我们如何通过使用一些我将在本教程中讨论的简单命令来掌握它。如果你喜欢记录(像我一样),那么你会喜欢阅读本教程。你可以从下面的链接开始探索 Google Colab。相信我,这是一个神奇的工具。
编辑描述
colab.research.google.com](https://colab.research.google.com/)
小抄
下面我将讨论一些主要的便利技巧和快捷方式,它们可以被使用并成为文档记录的专家。如果你知道 Markdown 、 XML 和 HTML 编码,那么这可能是一件轻而易举的事情,或者如果你对其中任何一个都不熟悉,那么今天是学习它们的好时机。Google Colab 支持 Markdown 和 HTML 文档。你可以把其中的任何一个记录下来。只需注意,本教程的完整代码也可以在下面我的 GitHub 资源库 中找到:
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/Tanu-N-Prabhu/Python/blob/master/Cheat_sheet_for_Google_Colab.ipynb)
好了,我们开始吧。
使用“文本单元格”来试验所有这些命令
图片来源于作者(塔努·南达·帕布)
标题
下面是标题的快捷命令。从标题 1 到标题 6 有不同类型的标题。
降价
使用**# heading-name**
,添加的**#**
越多,标题的尺寸越小,如下图所示:
图片来源于作者(Tanu Nanda Prabhu)
超文本标记语言
类似地,您可以使用 HTML 标签,如**h1, h2, h3, h4, h5 and h6**
作为标题,如下所示:
图片来源于作者(Tanu Nanda Prabhu)
大胆的
加粗使文本加粗并增加文本的可见性。
降价
用******
(两颗星)将文本包围,使其在 markdown 中加粗,例如****Text-to-be-bold****
图片来源于作者(Tanu Nanda Prabhu)
超文本标记语言
使用 HTML,我们可以通过使用**b**
标签来加粗文本,如下所示:
图片来源于作者(Tanu Nanda Prabhu)
在字下划横线
与粗体相似,文本也可以是斜体
降价
使 markdown 中的文本变为斜体,用*****
(一颗星)将其包围,例如***Text-to-be-italicize***
图片来源于作者(Tanu Nanda Prabhu)
超文本标记语言
同样,这也可以使用如下所示的**i**
标记在 HTML 中编写:
图片来源于作者(Tanu Nanda Prabhu)
删除线
用于删除文本。一条水平线画在文字中间。
降价
为了删除线,markdown 中的文本用两个波浪号字符**~~**
包围,例如**~~Text to be striked~~**
。
图片来源于作者(塔努·南达·帕布)
超文本标记语言
在 HTML 中我们可以使用**s**
标签来删除文本。
图片来源于作者(塔努·南达·帕布)
结合
此外,我们可以组合所有的格式命令并设置文本样式,如下所示。
降价和 HTML
图片来源于作者(Tanu Nanda Prabhu)
列表
众所周知,有两种类型的列表:
- 有序列表
- 无序列表
顾名思义,有序列表有一个顺序(1,2,3,…或其他)。但是无序列表没有顺序,如下所示。
Markdown 中的有序和无序列表
在有序列表的 markdown 中,你可以直接输入类似于**1, 2, 3, and so on**
的数字。但是对于无序列表,你可以从一个*****
开始,这个实习生创建一个项目列表。
图片来源于作者(Tanu Nanda Prabhu)
超文本标记语言
我们可以使用 HTML 标签来处理列表,如下所示:
有序列表
有序列表中有普通列表,类型 1,A,A,I,I 类型如下图所示:
正常列表
使用**ol**
标签,列表内容使用**li**
标签,如下所示:
图片来源于作者(Tanu Nanda Prabhu)
Type = “1”
只需在**ol**
标签中添加**type = "1"**
,这将创建一个由 **1、2、3 等等组成的有序列表。**列表项将用数字编号(默认)。
图片来源于作者(Tanu Nanda Prabhu)
Type = “A”
只需在**ol**
标签内添加**type = "A"**
,这将创建一个由 **A、B、C 等等组成的有序列表。**列表项目将用大写字母编号。
图片来源于作者(塔努·南达·帕布)
Type = “a”
只需将**type = "a"**
添加到**ol**
标签中,这将创建一个由 **a、b、c 等等组成的有序列表。**列表项目将用小写字母编号。
图片来源于作者(塔努·南达·帕布)
Type = “I”
只需将**type = "I"**
添加到**ol**
标签中,这将创建一个有序的列表,包含 **I、II、III 等等。**列表项将用大写罗马数字进行编号。
图片来源于作者(塔努·南达·帕布)
Type = “i”
只需将**type = "i"**
添加到**ol**
标签中,这将创建一个有序的列表,包含 **i、ii、iii 等等。**列表项将用小写罗马数字进行编号。
图片来源于作者(Tanu Nanda Prabhu)
无序列表
有序列表中有正常列表、圆盘、正方形、圆形和无类型,如下图所示:
正常列表
使用**ul**
标签,列表内容使用**li**
标签,如下所示:
图片来源于作者(Tanu Nanda Prabhu)
圆盘
只需在**ul**
标签内添加**type = "disc"**
即可创建一个圆盘形列表。
图片来源于作者(塔努·南达·帕布)
圈子
只需在**ul**
标签内添加**type = "circle"**
即可创建一个圆形列表。
图片来源于作者(塔努·南达·帕布)
方形
只需在**ul**
标签内添加**type = "square"**
即可创建一个方形列表。
图片来源于作者(Tanu Nanda Prabhu)
无
只需在**ul**
标签内添加**type = "none"**
即可创建一个无形状列表。在这种情况下,列表将没有点,如下所示
图片来源于作者(塔努·南达·帕布)
描述列表
描述列表是一个术语列表,包含每个术语的描述。**dl**
标签由定义列表名称的**dt**
和描述每个列表的**dd**
标签组成。
图片来源于作者(塔努·南达·帕布)
嵌套列表
嵌套列表基本上是列表中的列表。
图片来源于作者(Tanu Nanda Prabhu)
控制列表计数
有序列表将从 1 开始计数。如果要从指定的数字开始计数,可以使用如下所示的**start**
属性:
图片来源于作者(塔努·南达·帕布)
要更深入地了解 HTML 中的列表,请参考下面的文章:
自己试试”无序列表以标签开始。每个列表项都以标签开始。
www.w3schools.com](https://www.w3schools.com/html/html_lists.asp)
链接
链接或超链接允许用户从一页点击到另一页。
降价
在 markdown 中,将链接的标题写在方括号**[ ]**
内,将网页地址写在圆括号或圆括号**( )**
内。确保先写标题,再写链接。
图片来源于作者(塔努·南达·帕布)
超文本标记语言
在 HTML 中对于超链接,可以使用**a**
和**href**
标签如下所示:
图片来源于作者(Tanu Nanda Prabhu)
形象
有时候一张图片胜过千言万语。人们可以很容易地通过看到图像来更好地理解这些概念。
降价
与链接类似,您需要在括号内插入图像的链接。一定要在开头加上**!**
。如果你的链接被破坏或者无效,那么**alt text**
将被显示。
图片来源Makeameme.org
图像悬停
您可以同时将鼠标悬停在文本上来查看图像的标题。为此,您可以将您选择的文本放入命令中,如下所示:
![alt text](https://media.makeameme.org/created/online-class-cant.jpg "**Online Class Memes**")
超文本标记语言
在 HTML 中,我们可以使用**img**
标签来包含图像,并且您需要向**src**
标签提供图像的来源。
调整高度和宽度
您可以调整图像的高度和宽度。同样,你也可以嵌入一个 GIF 图片,如下图所示。
图片来源于Giphy.com
图片和说明文字
这是一个很好的实践,给来自不同来源的图片(我的意思是引用图片或提供说明)以信任。在**p**
标签的帮助下,我们可以为图片提供标题。
【Makeameme.org】图片来源 图片来源
您可以尝试使用**align**
标签,并根据您的喜好调整标题。
格式化代码
这里有两件事你需要明白:
- 内嵌代码
- 语法高亮显示
内嵌代码
有时,您可能希望插入一些可以使用内联代码的代码示例。要使用内联代码,可以使用**反斜杠(`` `)。**如上图用反勾将其包围。
图片来源于作者(Tanu Nanda Prabhu)
语法突出显示
当你在 Google Colab 的文本编辑器中编写多行代码时,就会用到这个。有时使用内联代码包含巨大的 python 代码片段并不是一个好主意,在这种情况下使用语法高亮。您必须将代码嵌入到````**`中,如下所示
默认语法高亮显示
这与任何编程语言无关。
图片来源于作者(塔努·南达·帕布)
Python 语法高亮显示
这可以明确地用于 python 编程。你应该在开头加上**python**
这个名字。
图片来源于作者(塔努·南达·帕布)
Javascript 语法高亮显示
这可以明确地用于 JavaScript 编程。你应该在开头加上**javascript**
这个名字。
图片来源于作者(塔努·南达·帕布)
C 编程语法高亮
这可以明确用于 C 编程。你应该在开头加上名字**c**
。
图片来源于作者(Tanu Nanda Prabhu)
如上所述,您可以突出显示基于不同编程语言的代码片段。
桌子
有些时候,您可能希望以表格的形式表示信息。
降价
您必须使用**|**
作为不同列的运算符。默认情况下,表格标题会以粗体显示。
图片来源于作者(塔努·南达·帕布)
冒号可以用来对齐列。
如果您需要创建任何表格,那么使用下面的 工具 为您生成表格。你所要做的就是选择表格生成器的类型,比如 Latex、HTML、Markdown 等等。输入表格中的内容,然后点击生成。然后,您可以在文本编辑器中复制粘贴生成的代码,并毫不费力地看到一个漂亮的表格。
[## 在线创建 LaTeX 表格—TablesGenerator.com
使用表格/设置尺寸菜单选项设置所需的表格尺寸。将表格数据输入表格:复制…
www.tablesgenerator.com](https://www.tablesgenerator.com/)
超文本标记语言
在 HTML 的情况下,你必须使用**table**
标签和**tr**
,后者用于表格行,而**th**
用于表格标题(在这种情况下是公司名称和创始人)。**td**
为表格描述。
图片来源于作者(塔努·南达·帕布)
您也可以使用**align**
标签并相应地对齐内容。
缩进
当您需要区分一些文本,如注释、提示或任何额外的有用信息时,这非常有用。
降价
你需要使用**>**
进行缩进。
图片来源于作者(塔努·南达·帕布)
超文本标记语言
同样,在 HTML 中,你可以使用**blockquote**
标签来达到同样的目的。
图片来源于作者(Tanu Nanda Prabhu)
水平标尺
在你写作的时候,在每一章或者任何一个概念后面画一个横坐标通常是一个很好的练习。这有助于区分不同的事物。
降价
在降价的情况下,你只需要使用 3 个**---**
(减号)。
图片来源于作者(Tanu Nanda Prabhu)
超文本标记语言
对于 HTML,您需要使用标签**hr**
来插入一个水平标尺。
图片来源于作者(Tanu Nanda Prabhu)
证明合法
很多时候你会在笔记本上写下大段文字,但有时你可能想调整它们,只是为了让它看起来整洁。在这里,可以使用**align**
标签,用**justify**
作为值。
图片来源于作者(塔努·南达·帕布)
图片来源于作者(Tanu Nanda Prabhu)
同样,你可以用**right**
**left**
**center**
的值和的值相应地对齐的段落。****
换行符
有时你可能想开始一个新的段落,所以你需要在它们之间换行。这里有两个选项,要么你可以直接按下回车键,在中间留一行空格,要么你可以使用**br**
标签,也称为换行符。
图片来源于作者(Tanu Nanda Prabhu)
数学公式
如果你用笔记本做研究,那么你需要写很多方程式和数学符号。
符号
确保您在第一个**$**
之后的**\**
之间书写符号名称。
图片来源于作者(塔努·南达·帕布)
方程式
遵循相同的规则,即围绕**$**
之间的等式,从第一个**$**
后的**/**
开始。
图片来源于作者(Tanu Nanda Prabhu)
要查看更多关于数学方程式和符号的内容,请参考下面的 链接 :
****[## 在 Markdown 中编写数学公式
在这篇文章中,我将向你展示如何在 markdown 中编写数学符号。因为我在写博客,所以…
csrgxtu.github.io](https://csrgxtu.github.io/2015/03/20/Writing-Mathematic-Fomulars-in-Markdown/)****
好了,各位,教程到此结束。我希望你今天学到了很多新东西。我试图保持这篇教程的简短,但是因为有很多概念,我不得不把它最大化。但是这份备忘单在大多数面试(技术文档)中或者当你记录你的木星笔记本时会很有用。如果我找到更多的提示和技巧,那么我会在这里提供细节。在那之前,注意安全,祝你有美好的一天。下次见。
NLP 备忘单:迄今为止我的 NLP 学习历程总结
说实话,我一开始也没想到要学 NLP。但是由于它广泛而有趣的应用,NLP 一直吸引着我去更深入地挖掘和探索更多的乐趣。与许多机器学习算法不同,NLP 在可视化方面特别丰富,因此易于理解、解释和应用于现实生活中的问题。在本文中,我将介绍 NLP 中的几个领域,并分享它们背后的思想(以及代码和视觉效果!).以下是你应该期待的:
tweet 数据集包含 2292 条 Twitter 用户的推文。尽管这个数据集不能代表 Twitter 上的所有人,但我们的结论仍然很有见地。代码都上传在 Github 里。
情感分析
我总是觉得情绪分析很有趣,因为它可以嵌入到任何地方。我们,人类,与情感相连,与观点进步。更具体地说,商业是以客户为中心的。分析公众对一种产品或一家公司的看法有助于公司定位和改进。因此,情感分析可以用作商业中的绩效指标/反馈。
- 幼稚情绪分析
让我们从简单的方法开始,了解情感分析背后的思想。在朴素情感分析中,我们将每个词编码为正面或负面,然后遍历整个文本,计算正面词和负面词。
这个代码片段展示了如何为肯定/否定单词生成单词列表,这些单词列表以后可以作为字典使用,我们可以在其中查找单词。Github 中包含完整版本的代码。
import requests
def sentiment_words(url):
request = requests.get(url)
print("retriving data >>> status code: ",request.status_code)
text = request.text
word_list = text[text.find("\n\n")+2:].split("\n")
return word_listpos_url = '[http://ptrckprry.com/course/ssd/data/positive-words.txt'](http://ptrckprry.com/course/ssd/data/positive-words.txt')
neg_url = '[http://ptrckprry.com/course/ssd/data/negative-words.txt'](http://ptrckprry.com/course/ssd/data/negative-words.txt')
pos_list = sentiment_words(pos_url)[:-1]
neg_list = sentiment_words(neg_url)[:-1]
下图是 Twitter 用户的情绪分布。总的来说,Twitter 用户积极的多于消极的。有些用户甚至有接近 0 的负分的高正分。
Twitter 用户的情感分布,来源于作者
仔细观察一个用户:我们能够察觉这个用户情绪的变化。不幸的是,我们没有更准确的时间信息,否则我们甚至可能发现一些情绪变化的周/季模式。
一个用户的情感分数的变化,按作者来源
- NRC 情感词典
与朴素情绪分析的机制相似,NRC 提取了另外 8 种情绪,如喜悦、信任、悲伤等。有了更丰富的信息,可以添加更多的视觉效果/功能。例如,雷达图是情绪诊断的潜在视觉效果。用户 1(蓝色)似乎比用户 2(红色)更积极,在积极情绪(喜悦、信任、期待)上得分更高,在消极情绪(悲伤、愤怒、厌恶)上得分更低。
用于情绪诊断的雷达图,来源于作者
- VADER
VADER(化合价感知词典和情感推理机)的功能超出了单词级别。相反,它在句子级别/内容级别上分析情感。此外,它提供了情绪的极性(积极/消极)和强度。在 Python Vader perspection 库中,它返回 4 个分数——正、负、中性和复合。
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()
pos=neg=neu=compound=0
sentences = nltk.sent_tokenize(text.lower())
for sentence in sentences:
vs = analyzer.polarity_scores(sentence)
pos += vs["pos"]/len(sentences)
neg += vs["neg"]/len(sentences)
neu += vs["neu"]/len(sentences)
compound += vs["compound"]/len(sentences)
“中性”与“积极”显著负相关,这是有道理的,因为当我们表达情绪时,很难保持中立。然而,大多数用户被检测为高度中立。是我们的分析仪不够灵敏,还是现实生活中的真实情况?
VADER 分数分布,按作者来源
VADER 情绪得分的雷达图,来源于作者
WordCloud
词云是很多人说起 NLP 时想到的。它计算单词的频率,并根据它们的频率调整每个单词的大小。一个词出现的频率越高,在词云中就越突出。这个想法简单而有效。
from wordcloud import WordCloud, STOPWORDS
wordcloud = WordCloud(stopwords=STOPWORDS,background_color='white',width=3000,height=3000).generate(text)
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
Wordcloud 覆盖整个数据集,按作者排序
命名实体识别
命名实体识别使我们能够理解谈论的是什么或谁,它反映了人类用语言语法处理句子的方式。该代码片段逐句扫描文本,如果单词被识别为“实体”,则标记每个单词。完成后,我们在大块中搜索树,并询问“你被贴上标签了吗?”— hasattr(树,“标签”)。如果树回答是,我们将在 tree.leaves() 中获取实体,并存储标签 tree.label() 。
def entity_identity(text):
sentences = nltk.sent_tokenize(text)
entity = []
for sentence in sentences:
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
chunked = nltk.ne_chunk(tagged)
for tree in chunked:
if hasattr(tree,"label"):
entity.append([tree.label()," ".join(c[0] for c in tree.leaves())])
entity = pd.DataFrame(entity,columns=['label','entity'])
return entity
实体快照,按作者排序的来源
如果将命名实体与情感分析/词云相结合,事情会变得更有趣。我们先来看云这个词里的一个例子。该函数可以获取任何类型的实体,并抓取该类型中的所有实体以形成单词云。它可以作为一个监视器,监视人们在谈论什么/谁。
def wordcloud_entity(entity,label="PERSON"):
text = " ".join(list(entity[entity["label"]==label]["entity"]))
wordcloud = WordCloud(stopwords=STOPWORDS,background_color='white',width=3000,height=3000).generate(text)
fig,ax = plt.subplots(1,1,figsize=(8,8))
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
分别代表 GPE(地缘政治实体)和个人的 Wordcloud,来源于作者
好了,现在我们意识到 twitter 用户正在大量谈论香港、美国、特朗普、宝琳娜·辛基娜……进一步分析一下与这些词相关的情绪如何?接下来,我搜索包含这些单词的句子,并使用 VADER 情感分析器。
def sentiment_entity(text,entity="New York"):
sentences = nltk.sent_tokenize(text)
analyzer = SentimentIntensityAnalyzer()
pos=neg=neu=compound=count=0
for sentence in sentences:
if entity.lower() in sentence.lower():
vs = analyzer.polarity_scores(sentence)
pos += vs["pos"]
neg += vs["neg"]
neu += vs["neu"]
compound += vs["compound"]
count += 1
return pos/count,neg/count,neu/count,compound/count
这个数据集在它所代表的用户群(例如大学生)和我提取它的时间方面是有偏见的,但至少它表明了我们如何能够将情感分析纳入命名实体识别。
美国、纽约和香港的情感比较,按作者分类
特朗普和宝琳娜·辛基娜之间的情感比较,由作者提供
文本摘要
在前三部分中,我们主要集中在单词级和句子级的分析。现在我们来分析段落中的文本。通常,一篇文章包括开头、正文和结尾。开头或结尾的一些句子是总结文章主题的关键句子。文本摘要对每个句子进行排名,并挑选排名靠前的句子。天真地说,我们可以计算每个单词的频率,并以此为标尺对句子进行排序。最后,我们挑选词频最高(最有代表性)的句子。还有另一种计算 TextRank 的复杂方法,它被打包在一个名为 gensim 的包中。看看这段代码!推文比文章短,因此可能不太适合摘要。我将 ratio 参数设置为小至 0.003,以挤压 tweets 的输出大小。
import gensim
gensim.summarization.summarize(text,ratio=0.003)
一个文本摘要的例子,作者来源
主题分析和相似性
现在事情越来越抽象了。主题分析是一种无监督的学习技术,我们试图从文本中提取维度。我这里介绍的技术是 LDA(潜在狄利克雷分配)。LDA 需要词典和语料库为主题抽取做准备。单词字典对文本中的每个代码进行编码。语料库是一个列表的列表,其中文本中的单词存储在一个列表中,所有文本分别存储在不同的列表中(“单词包”)。
words_list = []
users = []
for user,text in tweets.items():
users.append(user)
words = nltk.word_tokenize(text.lower())
words = [word for word in words if word not in STOPWORDS and word.isalnum() and len(word)>=2]
words_list.append(words)num_topics = 3 #self-defined
dictionary = corpora.Dictionary(words_list)
corpus = [dictionary.doc2bow(words) for words in words_list]
lda = LdaModel(corpus, id2word=dictionary, num_topics=num_topics)
现在我们得到 3 个主题及其相应的代表词。要显示特定主题的样子,请尝试以下代码:
# Topic is 0 indexed, 1 indicates the second topic
lda.show_topic(1)
某个主题的热门词汇,来源于作者
要获得用户/文档的主题组件,请尝试以下代码:
# corpus[2] refers to the tweets of a user
sorted(lda.get_document_topics(corpus[2],minimum_probability=0,per_word_topics=False),key=lambda x:x[1],reverse=True)[output] [(0, 0.51107097), (1, 0.48721585), (2, 0.0017131236)]
比较不同用户之间的主题相似性,这是 LSI 的亮点。LSI 接受与 LDA 相同的输入—字典、语料库—并将一个新文档与现有的语料库进行比较。
lsi = models.LsiModel(corpus,id2word=dictionary, num_topics=3)
words_new = nltk.word_tokenize(doc.lower())
words_new = [word for word in words if word not in STOPWORDS and word.isalnum() and len(word)>=2]
vec_bow = dictionary.doc2bow(words_new)
vec_lsi = lsi[vec_bow]
index = similarities.MatrixSimilarity(lsi[corpus])
sims = index[vec_lsi]
sims = sorted(enumerate(sims), key=lambda item: -item[1])
我没有输入新的推文。相反,我随机选择了一个用户。这就是为什么我们得到一个相似性分数 1。
相似性表(语料库 id,相似性得分),来源按作者
pyLDAvis 包为 LDA 提供了很好的视觉效果,通过它我们能够知道主题如何与关键词相关联,以及如何解释主题。这需要相当长的时间,因此我只为一个用户绘图。
lda_display = pyLDAvis.gensim.prepare(lda, corpus, dictionary, R=15, sort_topics=False)
pyLDAvis.display(lda_display)
LDA 可视化示例(一个用户),按作者排序
语言模型(文本生成)
通过以上步骤,我们开始了解文本的情感、实体、关键词、主题。现在让我们教我们的机器如何像人一样说话。我们将为一个 twitter 用户建立一个简单的 RNN 模型,并模仿这个用户如何说话。
对系列数据应用机器学习有两个主要障碍。首先,序列的顺序不能反映在传统的 DNN 模型中。这就是 RNN 的用武之地。RNN 非常适合系列数据,就像 CNN 适合图像一样。多对一 RNN 结构将文本中的前 n-1 个单词作为输入,第 n 个单词作为输出。RNN 可以传递以前位置的信息,从而可以连续保留订单。第二,系列数据可以有不同的长度。推文可以短至 3 个字符,也可以长达几个句子。Padding 就是专门针对这个问题设计的。
首先,我们对每个单词进行标记,因为机器不能直接识别单词,然后每个句子被编码成一个整数列表。例如,“现在”在句子中被编码为 198。word_index 是解密的字典。
tokenizer = Tokenizer() # can set num_words to tokenize
tokenizer.fit_on_texts(sentences)
sequences = tokenizer.texts_to_sequences(sentences)
word_index = tokenizer.word_index
index_word = {index:word for word,index in word_index.items()}
接下来,为了统一长度,每个句子要么添加 0 来弥补空格,要么将其本身截断以适应框。前 n-1 项取 X,每句最后一项取 y。
max_length = 15
trunct_type = "post"
padding_type = "pre"padded = pad_sequences(sequences,padding=padding_type,truncating=trunct_type,maxlen=max_length)vocab_size = len(word_index)+1
X = padded[:,:-1]
Y = padded[:,-1]
Y = tf.keras.utils.to_categorical(Y,num_classes=vocab_size)
在构建 RNN 模型时,我们添加了一个嵌入层。这是一种表示单词的方法,并且比一键编码表示法有一定的优势。在一个热编码中,每个字是相互独立/正交的。单词嵌入为每个单词生成一个向量,并支持非正交关系。有些单词可以彼此更接近和相似。比如“猫”比“西边”更接近“狗”。因此,我们能够将我们的知识移植到我们以前没有见过的句子中。机器能够从原来的句子“猫在跑”中学习“狗在跑”。
LSTM 是一种解决消失梯度和加强长期串联连接的技术。
model = tf.keras.Sequential([
tf.keras.layers.Embedding(vocab_size,embedding_dim,input_length=max_length-1),
tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(512,return_sequences=True)),
tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(256)),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.3),
tf.keras.layers.Dense(vocab_size,activation="softmax")
])model.compile(loss='categorical_crossentropy',optimizer="adam",metrics=["accuracy"])model.fit(X,Y,epochs=100,verbose=1)
让我们生成文本来看看我们会说话的机器人是如何工作的。
choice = random.randint(0,len(padded))
seed = padded[choice,1:].reshape(1,max_length-1)
tweet_robot = sentences[choice]
for i in range(5):
predicted = model.predict_classes(seed,verbose=0)
seed = np.append(seed,[int(predicted)])[1:].reshape(1,max_length-1)
tweet_robot = tweet_robot + " " + str(index_word[int(predicted)])
一个会说话的机器人的例子,由作者提供
原始句子是一条完整的推文,我们的说话机器人通过挑选适当的单词来帮助扩展句子。结果不如我期望的那样令人满意。我们的会说话的机器人似乎只是在扔单词,而不是产生一个组织良好的句子。但是,所选的单词显示出与主题的一些一致性。
要提高性能,还有很多工作要做。文本清理是我的首要任务。Tweets 很短,不正式,这导致了很多用词和句子组织的不优雅。数据集中有很多噪音。
我正站在 NLP 宏伟宫殿的门口,敲门。我前面的路还很长。我写下这篇文章来提醒我走过的路,并鼓励我继续冒险。
github:https://github.com/JinPu-dududu/NLP
在 Python 中实现选择最佳集群数量的 7 种方法的备忘单
基于多个聚类验证指标(如间隙统计、轮廓系数、Calinski-Harabasz 指数等)选择最佳聚类数。
Mehrshad Rajabi 在 Unsplash 上拍摄的照片
细分为检查有意义的细分提供了一个数据驱动的角度,高管可以使用它来采取有针对性的行动和改善业务成果。许多高管冒着基于过度概括做出决策的风险,因为他们利用一刀切的方法来评估他们的商业生态系统。然而,分段通过提供多个有意义的透镜来分解数据并采取行动,从而改善了决策。
我们在试图对客户或产品进行细分时面临的最令人困惑的问题之一是选择理想的细分数量。这是 K 均值、凝聚聚类和 GMM 聚类等多种聚类算法的关键参数。除非我们的数据只有 2 或 3 维,否则不可能直观地理解数据中存在的聚类。而在大多数实际应用中,我们会有 3 个以上的维度。本博客将帮助读者理解并快速实现选择最佳集群数量的最流行技术:
- 差距统计
- 肘法
- 轮廓系数
- 卡林斯基-哈拉巴斯指数
- 戴维斯-波尔丁指数
- 系统树图
- 贝叶斯信息准则(BIC)
在这个练习中,我们将使用一家为孕妇提供服装的在线商店的点击流数据。它有从 2008 年 4 月到 2008 年 8 月的数据,包括产品类别、图片在网页上的位置、IP 地址的来源国以及产品的美元价格等变量。在选择最佳的聚类数之前,我们需要准备数据进行分割。
我建议您在继续下一步之前,阅读下面的文章,深入了解为分段准备数据的不同步骤:
One Hot 编码、标准化、PCA:python 中分割的数据准备
差距统计
差距统计是由斯坦福大学的研究人员蒂布拉尼、瓦尔特和哈斯蒂在他们 2001 年的论文中提出的。他们的方法背后的想法是找到一种方法来比较聚类紧密度与数据的零引用分布,即没有明显聚类的分布。他们对最佳聚类数的估计是原始数据上的聚类紧密度落在该参考曲线之下最远的值。此信息包含在以下差距统计公式中:
作者图片
其中,Wk 是基于类内误差平方和(WSS)的聚类紧密度的度量:
作者图片
误差平方和的类内计算由KMeans
函数的inertia_
属性进行,如下所示:
- 每个点到聚类中心的距离的平方(误差平方)
- WSS 分数是所有点的这些平方误差的总和
在 python 中计算 k 均值聚类的间隙统计包括以下步骤:
- 将观察到的数据在不同数量的聚类上进行聚类,并计算我们的聚类的紧密度
- 生成参考数据集,并用不同数量的聚类对每个数据集进行聚类。参考数据集是使用
random_sample
函数根据“连续均匀”分布创建的。 - 计算参考数据集上聚类的平均紧密度
- 根据参考数据和原始数据的聚类紧密度差异计算间隙统计数据
# Gap Statistic for K meansdef optimalK(data, nrefs=3, maxClusters=15):
"""
Calculates KMeans optimal K using Gap Statistic
Params:
data: ndarry of shape (n_samples, n_features)
nrefs: number of sample reference datasets to create
maxClusters: Maximum number of clusters to test for
Returns: (gaps, optimalK)
"""
gaps = np.zeros((len(range(1, maxClusters)),))
resultsdf = pd.DataFrame({'clusterCount':[], 'gap':[]})
for gap_index, k in enumerate(range(1, maxClusters)):# Holder for reference dispersion results
refDisps = np.zeros(nrefs)# For n references, generate random sample and perform kmeans getting resulting dispersion of each loop
for i in range(nrefs):
# Create new random reference set
randomReference = np.random.random_sample(size=data.shape)
# Fit to it
km = KMeans(k)
km.fit(randomReference)
refDisp = km.inertia_
refDisps[i] = refDisp# Fit cluster to original data and create dispersion
km = KMeans(k)
km.fit(data)
origDisp = km.inertia_# Calculate gap statistic
gap = np.log(np.mean(refDisps)) - np.log(origDisp)# Assign this loop's gap statistic to gaps
gaps[gap_index] = gap
resultsdf = resultsdf.append({'clusterCount':k, 'gap':gap}, ignore_index=True)return (gaps.argmax() + 1, resultsdf)score_g, df = optimalK(cluster_df, nrefs=5, maxClusters=30)plt.plot(df['clusterCount'], df['gap'], linestyle='--', marker='o', color='b');
plt.xlabel('K');
plt.ylabel('Gap Statistic');
plt.title('Gap Statistic vs. K');
图 1:不同聚类值的差距统计(图片由作者提供)
如图 1 所示,29 个聚类的差距统计最大,因此,我们可以选择 29 个聚类作为 K 均值。
肘法
这是确定最佳聚类数的最常用方法。该方法基于计算不同数量的组(k)的组内误差平方和(WSS ),并选择 WSS 变化首先开始减小的 k。
肘形法背后的思想是,对于少量的聚类,所解释的变化迅速变化,然后它变慢,导致曲线中形成肘形。拐点是我们可以用于聚类算法的聚类数。关于这种方法的更多细节可以在袁春辉和杨海涛的论文中找到。
我们将使用 YellowBrick 库,它可以用几行代码实现 elbow 方法。它是 Scikit-Learn 的包装器,并且有一些很酷的机器学习可视化!
# Elbow Method for K means# Import ElbowVisualizer
from yellowbrick.cluster import KElbowVisualizer
model = KMeans()
# k is range of number of clusters.
visualizer = KElbowVisualizer(model, k=(2,30), timings= True)
visualizer.fit(cluster_df) # Fit data to visualizer
visualizer.show() # Finalize and render figure
图 2:肘法结果(图片由作者提供)
对于介于 2 到 30 之间的聚类值范围,KElbowVisualizer
函数适合于KMeans
模型。如图 2 所示,拐点是用 8 个集群实现的,它由函数本身突出显示。该函数还通过绿线告知我们为不同数量的聚类绘制模型需要多少时间。
轮廓系数
点 i 的轮廓系数定义如下:
作者图片
其中 b(i) 是点 i 到任何其他聚类中所有点的最小平均距离,而 a(i) 是 i 到其聚类中所有点的平均距离。例如,如果我们只有 3 个聚类 A、B 和 C,并且 I 属于聚类 C,那么通过测量 i 到聚类 A 中每一点的平均距离,即 I 到聚类 B 中每一点的平均距离,并取最小结果值来计算 b(i) 。数据集的轮廓系数是单个点的轮廓系数的平均值。
轮廓系数告诉我们各个点是否被正确地分配到它们的簇中。使用轮廓系数时,我们可以使用以下经验法则:
- S(i) 接近 0 表示该点在两个聚类之间
- 如果它更接近-1,那么我们最好将它分配给其他集群
- 如果 S(i) 接近 1,则该点属于“正确的”聚类
关于这种方法的更多细节,请参考 N. Kaoungku,K. Suksut,R. Chanklan 和 K. Kerdprasop 的这篇 2018 年论文。我们将使用KElbowVisualizer
函数来实现 K 均值聚类算法的轮廓系数:
# Silhouette Score for K means# Import ElbowVisualizer
from yellowbrick.cluster import KElbowVisualizer
model = KMeans()
# k is range of number of clusters.
visualizer = KElbowVisualizer(model, k=(2,30),metric='silhouette', timings= True)
visualizer.fit(cluster_df) # Fit the data to the visualizer
visualizer.show() # Finalize and render the figure
图 3:剪影评分结果(作者图片)
基于轮廓得分的最佳聚类数是 4。
卡林斯基-哈拉巴斯指数
Calinski-Harabasz 指数是基于这样一种想法,即(1)本身非常紧凑的聚类和(2)彼此间隔良好的聚类是好的聚类。该指数是通过将单个对象到其聚类中心的距离的平方和的方差除以聚类中心之间的距离的平方和来计算的。卡林斯基-哈拉巴斯指数值越高,聚类模型越好。卡林斯基-哈拉巴斯指数的公式定义为:
作者图片
其中 k 是聚类数,n 是数据中的记录数,BCSM(聚类间散布矩阵)计算聚类间的分离度,WCSM(聚类内散布矩阵)计算聚类内的紧密度。
KElbowVisualizer
函数还能够计算卡林斯基-哈拉巴斯指数:
# Calinski Harabasz Score for K means# Import ElbowVisualizer
from yellowbrick.cluster import KElbowVisualizer
model = KMeans()
# k is range of number of clusters.
visualizer = KElbowVisualizer(model, k=(2,30),metric='calinski_harabasz', timings= True)
visualizer.fit(cluster_df) # Fit the data to the visualizer
visualizer.show() # Finalize and render the figure
图 4:卡林斯基哈拉巴斯指数(图片由作者提供)
如图 4 所示,对于 K 均值聚类算法,当聚类数为 2 时,Calinski Harabasz 指数最大。要更深入地了解这个指数,请参考和徐玉生的论文。
戴维斯-波尔丁指数
戴维斯-波尔丁(DB)指数定义为:
作者图片
其中 n 是聚类的计数,而 σi 是聚类 i 中所有点距聚类中心 ci 的平均距离。
像剪影系数和 Calinski-Harabasz 指数一样,DB 指数同时捕获了聚类的分离度和紧密度。这是因为测量的“最大值”语句重复选择平均点距离其中心最远的值和中心最接近的值。但与剪影系数和 Calinski-Harabasz 指数不同,随着 DB 指数的下降,聚类得到改善。
# Davies Bouldin score for K meansfrom sklearn.metrics import davies_bouldin_scoredef get_kmeans_score(data, center):
'''
returns the kmeans score regarding Davies Bouldin for points to centers
INPUT:
data - the dataset you want to fit kmeans to
center - the number of centers you want (the k value)
OUTPUT:
score - the Davies Bouldin score for the kmeans model fit to the data
'''
#instantiate kmeans
kmeans = KMeans(n_clusters=center)# Then fit the model to your data using the fit method
model = kmeans.fit_predict(cluster_df)
# Calculate Davies Bouldin scorescore = davies_bouldin_score(cluster_df, model)
return scorescores = []
centers = list(range(2,30))for center in centers:
scores.append(get_kmeans_score(cluster_df, center))
plt.plot(centers, scores, linestyle='--', marker='o', color='b');
plt.xlabel('K');
plt.ylabel('Davies Bouldin score');
plt.title('Davies Bouldin score vs. K');
图 5:戴维斯·波尔丁得分(图片由作者提供)
如图 5 所示,Davies Bouldin 分数在 4 个聚类中最小,可以考虑用于 k 均值算法。关于 DB 分数的更多细节可以在斯洛博丹·佩特罗维奇的一篇论文中找到。
系统树图
这种技术是特定于凝聚层次聚类方法的。聚类的凝聚层次方法首先将每个点视为一个单独的聚类,然后开始根据点和聚类的距离以层次方式将点连接到聚类。在另一篇博客中,我们将重点介绍这种方法的细节。为了获得分层聚类的最佳聚类数,我们使用了一个树形图,这是一个显示聚类合并或分裂顺序的树形图。
如果两个聚类合并,树状图会将它们连接成一个图,连接的高度就是这些聚类之间的距离。我们将使用scipy
库中的dendogram
函数来绘制图表。
# Dendogram for Heirarchical Clustering
import scipy.cluster.hierarchy as shc
from matplotlib import pyplot
pyplot.figure(figsize=(10, 7))
pyplot.title("Dendrograms")
dend = shc.dendrogram(shc.linkage(cluster_df, method='ward'))
图 6:树状图(图片由作者提供)
如图 6 所示,我们可以根据树状图的层次结构选择最佳的集群数量。正如其他聚类验证指标所强调的,4 个聚类也可以被考虑用于聚集层次结构。
贝叶斯信息准则
贝叶斯信息准则(BIC)评分是一种对使用最大似然估计框架的模型进行评分的方法。BIC 统计数据的计算方法如下:
BIC = (kln(n)) — (2ln(L))*
其中 L 是模型的似然函数的最大值,k 是参数的数量,n 是记录的数量
BIC 分数越低,模型越好。我们可以将 BIC 分数用于聚类的高斯混合建模方法。我们将在单独的博客中讨论这个模型的细节,但这里需要注意的关键是,在这个模型中,我们需要选择聚类的数量以及协方差的类型。我们尝试了参数的各种组合,并选择了具有最低 BIC 分数的模型。
# BIC for GMMfrom sklearn.mixture import GaussianMixture
n_components = range(1, 30)
covariance_type = ['spherical', 'tied', 'diag', 'full']
score=[]
for cov in covariance_type:
for n_comp in n_components:
gmm=GaussianMixture(n_components=n_comp,covariance_type=cov)
gmm.fit(cluster_df)
score.append((cov,n_comp,gmm.bic(cluster_df)))
score
BIC 评分(图片由作者提供)
结论
在这篇文章中,我们学习了 7 种不同的方法来为不同的聚类算法选择最佳的聚类数。
具体来说,我们了解到:
- 如何计算用于选择最佳集群数量的各种指标
- 使用这些指标选择最佳集群数量的经验法则
- 如何在 python 中实现集群验证方法
- 如何解释这些方法的结果
最后,给定用于选择最佳集群数量的多个指标,我们可以将各种指标的平均值/中值/众数作为最佳集群数量。
你对这个博客有什么问题或建议吗?请随时留言。
感谢您的阅读!
如果你和我一样,对人工智能、数据科学或经济学充满热情,请随时添加/关注我的 LinkedIn 、 Github 和 Medium 。
参考
- Tibshirani R,Walther G 和 Hastie T,通过间隙统计估计数据集中的聚类数,统计学家杂志。社会主义者 B (2001) 63,第二部分,第 411-423 页
- 袁 C,杨 H,K-Means 聚类算法的 K 值选取方法研究,,2019 . 6 . 18
- Kaoungku 和 Suksut,k .和 Chanklan,r .和 Kerdprasop,k .和 Kerdprasop,Nittaya。(2018).用于聚类和关联挖掘选择图像特征的轮廓宽度准则。国际机器学习和计算杂志。8.69–73.10.18178/ijmlc.2018.8.1.665
- 王 X &徐 Y,基于剪影指数和 Calinski-Harabasz 指数的聚类验证改进指数,2019 IOP Conf。爵士。:板牙。Sci。英语。569 052024
- 佩特罗维奇 S,“剪影指数”和“戴维斯-波尔丁指数”在标记 IDS 聚类中的比较
检查表:为 Google BigQuery 选择正确的数据转换服务
哪种 Google 云服务可以让您的数据为分析做好准备?
凯尔·格伦在 Unsplash 上的照片
好消息是,在移动和转换基于 BigQuery 的分析和 AI/ML 数据时,谷歌云提供了过多的原生选择。坏消息是,你必须在许多选项中做出选择,以确保该技术符合你的需求,并证明是可持续的。
本文是一份帮助您选择最适合您的技术(或技术组合)的清单。如果您计划将 BigQuery 用于您的分析和数据科学项目,这意味着您将需要批量和/或微批量地清理、转换和组合数据。为此,我已经排除了 Google BigQuery 数据传输服务,它只能将数据移入 BigQuery,而不能进行转换。此外,我将我的研究仅限于原生谷歌云数据转换服务,包括云数据处理、云数据流、云数据融合和云数据准备。当然,使用 SQL 也可以在 BigQuery 中进行转换,这是一种非常强大的语言(我花了很多年来构建利用 SQL 和存储过程的应用程序);然而,由于多种原因,SQL 并没有为企业数据转换服务提供一个可持续的解决方案,而且也有很好的理由说明为什么有这么多技术被发明出来用于转换数据(这本身就需要一篇完整的文章)。
那么,为了选择正确的技术,你应该问自己什么问题呢?
问题 1:您是否希望用编程语言为您的解决方案编写代码?
如果软件工程师或开发人员必须开发和集成各种组件来创建一个包括大规模数据转换的应用程序,那么您应该考虑云数据流或云数据块。这些是基于代码的解决方案,需要 Java、Python、RestAPI 或 Apache Spark 等编程技能。这些都需要相当的技术知识。有了这些服务,您已经知道需要创建哪些转换,并且您需要一个开发工具来编码这些转换。为了弄清楚是选择云数据流还是云数据块,阅读这篇文章,它提供了使用其中一个的很好的论据。
问题 2:你的数据已经在 Google Cloud 了吗?
如果你的数据还不在谷歌云中,那么谷歌基于开源项目 CDAP 开发的云数据融合(Extract,Transform,and Load,ETL)解决方案提供了许多连接器,可以将数据导入谷歌云和 BigQuery。或者,您可以利用您内部已有的其他 ETL 解决方案,将数据引入 Google Cloud。谷歌推荐这些 ETL 厂商,包括 Matillion、Fivetran、Informatica,以及更多。云数据融合等 ETL 解决方案最适合开发大规模数据管道,从本地和云资源移动数据,以“水合”数据湖或数据仓库(DWH)。数据工程师、ETL 开发人员和数据架构师将从云数据融合中受益最多。他们将能够开发可靠的实时和批量数据转换管道。云数据融合和 ETL 在构建稳定的数据管道以定期将数据移动到 DWH 或数据湖方面表现出色。它们并不意味着创建经常变化的管道,也不意味着将管道交给某些业务用户。
问题 3:您的数据计划是否需要灵活性来经常适应业务需求?除了数据工程师之外,您的项目是否需要业务用户或不太懂技术的专业人员进行自助式数据转换?
数据准备是数据转换技术的最新发展,将这些功能交给了技术含量较低且通常位于业务线的数据驱动型专业人员(除了数据工程师等常见的技术数据专家之外)。这些专业人员比任何人都更了解数据,但是,他们可能缺乏清理、组合和丰富数据以满足其需求的技术技能。通常他们会知道 Excel、Google Sheets 和 SQL。Cloud Dataprep(与 Trifacta 合作开发)提供了一个基于网格的、机器学习指导的可视化界面,以实现敏捷的数据探索和评估,从而为 BigQuery for analytics 提炼、标准化和组合数据。用户与数据内容进行交互,以反复提炼和汇集数据,用于下游业务驱动的分析。
云数据准备或云数据融合的附加问题:你更倾向于云数据处理还是云数据流?
云数据融合生成云 Dataproc 代码来转换数据,而云 Dataprep 生成一些数据流代码来转换数据。基于您的用例以及您以前的经验,两者都有各自的优势。这也可能是支持在 BigQuery 之上提供分析的特定技术的一个因素。
最终,您可能需要结合多种解决方案来为 BigQuery 创建端到端的分析或机器学习解决方案。我们经常看到 ETL 将数据放入云 DWH 或数据湖,然后使用数据准备技术将数据公开给消费者。
下表总结了这些技术的特征,以指导您的选择。
原载于【www.trifacta.com】
用 Pylint 检查你的代码质量
PEP 8 指南的 10 条规则将使你的代码更容易阅读
罗纳河上的星夜——文森特·梵高
Pylint 是 Python 编程语言的质量检查器,遵循 PEP 8 推荐的风格。本文提供了用 Python 编写清晰代码的指南,主要目标是提高代码的可读性和一致性。代码被阅读的次数要比被编写的次数多得多,这就是为什么遵守约定是如此重要,这将帮助我们或其他人更容易地理解可用的代码。在本文中,我们解释了 PEP 8 的 10 条规则,它们可以让你的 Python 代码更容易被其他人阅读,以及我们如何用 Pylint 检查它们。我们开始吧!💪
安装 Pylint
Pylint 是第三方库,在 Python 中默认不可用。由于 Pylint 不是 Python 标准库的一部分,我们需要单独安装它。这可以通过使用 pip 组件轻松实现。 Pip 是 Python 的标准包管理器,允许安装和管理不包含在 Python 标准库中的包。要检查是否安装了 pip ,您可以在 Windows 终端中执行以下命令:
**pip --version**
如上图所示,版本 10.0.1 在我们的系统中已经有了。现在,我们可以安装 pylint ,在命令行运行:
**pip install pylint**
还安装了 pylint 所需的所有依赖项。注意 pip 下载了最新版本的包。
使用 Pylint
一旦我们安装了 pylint ,我们可以通过运行命令 pylint 和文件名来轻松使用它,如下所示:
**pylint filename.py**
现在!我们已经准备好看到一些代码示例💪。在本文中,我们将详细解释用 Python 编码时的 10 个编码约定。此外,我们将证明 pylint 如何检测它们。
编码约定(PEP 8)
PEP 8 是一个风格指南,它定义了你的 python 代码应该如何被格式化以最大化它的可读性。在这篇文章中,我们将只涉及一些关键点;因此,如果你想更详细地研究这个话题,我建议你看一下指南。
Python 编程语言的官方主页
www.python.org](https://www.python.org/dev/peps/pep-0008/)
1.多行语句
在 Python 中,我们在排比句( () )、中括号( [] )、大括号( {} )内部使用隐式延伸线。隐式意味着我们不写行继续符( ** )来表示我们将一个语句扩展到多行。
当使用隐式延续行时,被包裹的元素应该垂直对齐,或者使用悬挂缩进。在 Python 的上下文中,悬挂缩进意味着带括号的语句的左括号是该行的最后一个非空白字符,随后的行缩进直到右括号。
不良做法
括号内的参数没有垂直对齐,或者使用悬挂缩进。
Pylint 输出
Pylint 检测到一个错误的缩进。
良好实践
括号内的参数垂直对齐。
括号内的参数使用悬挂缩进对齐。
2。操作员
始终用一个空格将这些运算符括起来:
- 赋值 (=)
- 扩充赋值 (+=,-=,等等)
- 比较运算符 ( <,>,< =,> =,==,!=)
- 逻辑运算符(与、或、非)
- 隶属运算符(在,不在)
- 身份运算符(是,非是)
不良做法
运算符没有被空格包围。
我们可以很容易地禁用 pylint 中的警告,在代码顶部添加一个注释,如上所示( #pylint: disable=C0114 )
C0114 警告表示模块文件串丢失。docstring 是作为模块、函数、类或方法定义中的第一条语句出现的字符串。根据 PEP 257(包含 docstring 约定的指南),所有模块的开头都应该有一个 docstring,描述该模块做什么。
在本文中,为了简单起见,我们不打算在每个模块的顶部添加一个 docstring。但是,强烈建议在实践中编写 doctrings。和以前一样,如果你想了解更多关于如何在 Python 中使用 doctrings,我推荐你仔细看看 PEP 257 指南。
这个 PEP 的目的是标准化文档字符串的高层结构:它们应该包含什么,以及如何表达…
www.python.org](https://www.python.org/dev/peps/pep-0257/)
Pylint 输出
如下所示,pylint 检测到比较运算符
但是,pylint 没有检测到 if 语句中的成员资格操作符也应该被空格包围。Pylint 并不完美!😢
良好实践
运算符两边各有一个空格。
3。逗号、分号或冒号后面的空格(但不是在前面!)
在逗号、分号或冒号之后,我们必须使用空格。然而, PEP 8 建议在即将到来之前避开它们。
不良做法
在下面的代码中,我们可以观察到以下不良做法:
- 分隔列表中每个元素的逗号后面缺少空格。
- 字典中分隔每个键值对的冒号(:)后面缺少空格。
- 在分隔元组中每个元素的逗号之前有一个空格。
Pylint 输出
如上所示, pylint 检测到在分隔列表元素(数字)的每个逗号后面需要一个空格。另外, pylint 检测到元组(坐标)中逗号前有一个空格。但是,它无法检测到分隔每个键-值对的冒号后面缺少的空格。
我想澄清的是,PEP8 指南并没有提到逗号、冒号或分号后需要空格。然而,这是一种普遍的做法,被大多数样式检查器检测为 pylint 。
良好实践
每个逗号、冒号和分号后面有空格,但前面没有。
4.圆括号、方括号或大括号中的空格。
不良做法
在圆括号、方括号或大括号中会立即使用空格。
Pylint 输出
Pylint 检测圆括号、中括号或大括号前后不允许有空格。
良好实践
圆括号、中括号或大括号内没有直接使用空格。
5.关键字和默认参数
关键字参数
参数是在调用函数时传递给它的值。Python 函数接受两种类型的参数:(1) 位置参数,以及(2) 关键字参数。使用位置参数调用函数时,参数必须以正确的顺序包含。相反,关键字参数可以以任意顺序提供,因为这些参数前面有一个标识符(关键字=值)。根据 PEP8 ,当使用关键字参数调用函数时,我们不必在=符号周围使用空格。
默认参数
函数参数可以有默认值。如果在调用函数时没有提供参数,参数将采用默认值。和以前一样,在定义默认参数时,等号两边不使用空格。
不良做法
当使用关键字和默认参数时,在=符号周围使用空格。
Pylint 输出
Pylint 检测关键字参数周围不允许有空格。
良好实践
等号两边不使用空格。
6.捕捉异常
Python 中的错误消息可以分为两种:语法错误和异常。语法错误Python 无法解释代码时出现,说明程序语法有问题。另一方面,异常发生在代码执行过程中发生意外的事情时。
在下面的代码块中,Python 提供了一条消息,表明我们在尝试将字符串转换为十六进制数时遇到了类型错误,因为十六进制函数需要一个整数作为输入。
为了在 Python 中处理异常,我们使用了 try 和 except 语句。 try 子句包含可以引发异常的操作, except 子句包含处理异常的代码。 try 子句逐个语句执行。但是,如果出现异常,则停止执行 try 语句,然后执行除语句之外的**。**
不良做法
这里,except 块中的代码在 try 块执行期间发生任何类型的异常时都会被执行。根据 PEP8 指南,不建议使用除 子句之外的裸**,因为它们会捕捉所有异常,包括 SystemExit 和 KeyboardInterrupt 异常,使得用 Control-C 中断程序更加困难**
Pylint 输出
Pylint 检测到使用了除条款之外的空白**。**
良好实践
我们应该在 except 块中指定要处理的错误,如下所示:
现在,程序捕捉到了类型错误异常,但没有捕捉到其他异常。
7.布尔变量
不良做法
在 if 语句中,使用**=或 is 比较布尔变量与真或假**是不正确的。
Pylint 输出
Pylint 检测到与 True 的比较应该只是表达式( is_raining )。
良好实践
我们必须在 if 语句中直接使用布尔变量,如下所示:
8.检查前缀和后缀
不良做法
使用字符串切片来检查前缀或后缀是一种不好的做法。
Pylint 输出
然而, pylint 并没有检测到这个风格问题,虽然它包含在 PEP 8 guide 中。
良好实践
根据样式指南 PEP8 ,我们必须使用 str.startswith() 和 str.endswith() 来检查前缀或后缀。
str.startswith(prefix,start,end) 函数检查字符串是否以给定的前缀开头。如果字符串以指定的前缀开始,函数返回True;否则,返回假。开始和结束是可选参数,指定检查开始和结束的索引。
如果字符串( str )以指定后缀结尾,函数将返回 True 。如果不是,则返回假。与之前一样,该函数有两个可选参数 start 和 end 来指定测试开始和结束的索引。
9。进口
一个模块是由 Python 代码组成的文件。如果您正在处理大型项目,将您的代码组织到多个文件中,并在必要时将它们导入到其他 Python 文件中是有意义的。要导入一个 Python 模块,我们只需输入语句 import ,后跟文件名。除了导入我们自己的模块,我们还可以导入 python 标准库中以及第三方库中可用的内置模块。根据 PEP8 准则,导入写在 Python 脚本的顶部,每一个都在一个单独的行上。此外,进口应按以下顺序分组:
- 标准库导入
- 第三方进口
- 本地脚本导入
不良做法
导入没有写在单独的行中,也没有按正确的顺序分组。
Pylint 输出
Pylint 检测到模块 pandas 和 matplotlib 没有写在单独的行上。此外, Pylint 检测到 Python 标准库模块(csv)应该放在第三方导入(pandas 和 matplotlib)之前。正如我们在下面所看到的,Pylint 也认识到所有的导入都是未使用的,因为为了简单起见,我们没有添加更多的代码。
良好实践
导入写在单独的行中,并按正确的顺序分组。
10.λ表达式
Lambda 表达式是匿名短期函数,语法如下:
λ自变量:表达式
这些函数可以有多个参数,返回由表达式 *提供的值。*我们不仅可以在 Python 中找到它们;其他编程语言也支持 lambda 函数。
根据 PEP8 指南,不建议将 lambda 函数直接分配给标识符。在这种情况下,建议使用常规函数(用 def 关键字定义)。
不良做法
lambda 表达式直接分配给标识符(乘法)。
Pylint 输出
Pylint 没有检测到这个问题,尽管它包含在 PEP 8 guide 中。
良好实践
我们用关键字 def 创建函数。
Pylint 并不完美,但它确实有助于提高代码质量!请记住,代码被阅读的次数要比被编写的次数多得多,这就是为什么坚持有助于他人理解您的代码的约定如此重要。此外,清晰的代码显示了专业性😉。这就是为什么我鼓励你使用像 Pylint 这样的质量检查器(还有很多!)来检查你的代码。看起来会更清晰,更专业👌