数据集生成:用 GANs 绘制照片草图
数据是我们 ML 和 DL 模型的核心。如果我们没有合适的数据集来训练我们的算法,我们就无法构建强大的程序。大多数时候,拥有一个足以训练模型的数据集是一个瓶颈,并且很难处理,特别是当我们需要一个由图像组成的数据集时。为了解决这个问题,我们需要在增强中执行一些变形机制、图像处理技术和其他技术。然而,我们可能仍然会因为某个特定问题而缺少数据集。一种解决方案可能是创建数据集,就像用 GAN 手绘形状一样。今天,我想用 Python 和 GAN 来研究照片素描,它有助于创建这样的新图像。
首先,这篇论文最近发表在卡耐基梅隆大学上,研究照片素描并获得惊人的结果。类边缘视觉表示就是简单的图像边缘、物体边界和线条画。主要重点是在突出的边界,以操纵图像在这个稀疏的空间,以产生更多。因此,我们可以从给定的输入图像中创建轮廓图(草图)作为输出。轮廓绘制基本由物体边界、突出的内边缘如遮挡轮廓组成;以及显著的背景边缘。
此外,本文还提高了生成轮廓图和指向对象边界的类边界图的性能。如果你在手绘图像数据集上工作,它有很大的潜力,因为我们可以尽可能多地扩展我们的数据集,而且我们仍然可以得到非常逼真的图形。
等高线图可以被描述为现实世界的物体,因此可以被认为是人类视觉的一种表达。与图像边界相比,轮廓图往往具有更多的特征,如眼睛、嘴巴、鼻子等。我在下面的文章中举了一个例子,展示了不同检测机的特性:
轮廓绘画善于采用所需的艺术风格。所以可以看做一个风格转移的应用。此外,文中还提到轮廓图是介于图像边界和抽象精细图之间的一种中间表示。
为了使用这个模型,首先要下载这个回购。你可以从这里在线探索更多,它创建随机样本,该项目的网页是这里。您可以检查示例数据集等。
下载回购后,除非想自己训练,否则需要下载预先训练好的模型。由于在我们的问题中,我们有一个稀缺的数据,我们不能训练这个模型。我提到的这个预训练模型是这里的和来自 Adobe Stock 的 1000 幅户外图像数据集上的 5000 幅高质量图片。
在这一步之后,您需要编辑 PhotoSketch→Scripts→test _ pre trained . sh 并删除这些行:
results_dir ${dataDir}/Exp/PhotoSketch/Results/ \
checkpoints_dir ${dataDir}/Exp/PhotoSketch/Checkpoints/ \
创建一个文件,并将你的预训练文件添加到 PhotoSketch→check points→pre trained。您需要将源图像数据添加到 PhotoSketch→示例中,生成的图像将位于 PhotoSketch→results 中的同一目录下。我将使用这样的数据集,因为我需要创建更多的手绘绘图:
要从 shell 运行,请分别执行以下命令:
cd 目录/to/PhotoSketch
。/scripts/test_pretrained.sh
你可以在你的终端上看到这个过程。我将这个模型应用到我的 matplot 条形图数据中。我需要有很多手绘的情节,这是产生一些好方法。
Results
从结果中,我们可以看到预训练的模型从输入源生成了很好的绘图。你可以用它们来进一步训练你自己的模型,比如物体检测,图像分类等等。
数据集不可用?没问题!
source: https://pixabay.com/photos/code-code-editor-coding-computer-1839406/
互联网上有大量可用的数据集,但我还是要提到一些令人惊叹的资源。如果你已经在 LinkedIn 上与我联系,那么你可能已经看到这个列表,如果你没有联系,请点击这个 链接 **。**以下网站可能有助于您获得所需的数据集:
流行的开放式数据仓库:
- 加州大学欧文分校机器学习资源库:https://lnkd.in/f5CUDj7
- Kaggle 数据集:https://lnkd.in/fKWqVbw
- 亚马逊的 AWS 数据集:https://lnkd.in/fnVYnm5
元门户(他们列出了开放的数据仓库):
其他页面列出了许多流行的开放数据仓库:
- 维基百科的机器学习数据集列表:https://lnkd.in/f6wujcB
- Quora.com提问:https://lnkd.in/fuHPfFF
- 数据集子编辑:https://lnkd.in/fmqkisy
如果你甚至在这些网站上都找不到数据集呢?你会怎么做?
你会责怪互联网没有给你你需要的数据集,或者你会诅咒整个宇宙吗?
嗯,我不会做上面提到的两件事。我会创建我自己的数据集,相信我,不会超过 5 分钟。
现在,让我向您展示如何快速创建自己的数据集。我们将使用名为 Faker 的 python 包。
- 安装 Faker 库。
pip install faker
2.导入库。
from faker import Faker
3.将 Faker 函数赋给一个变量,并设置一些随机种子。
fake = Faker()
fake.seed(4321)
我们将创建一个酒店数据集。为了保持本教程的简短和精确,我们将总共创建 8 列,您可以创建任意多的列。
4.给每个变量分配一个空数组,这样我们可以向其中插入一些数据。
ID=[]
name=[]
phone=[]
age=[]
gender=[]
address=[]
checkin=[]
checkout=[]
5.让我们插入 100 个电话、地址、入住、结账和姓名条目。
for _ in range(100):
phone.append(fake.phone_number())
address.append(fake.country())
checkin.append(fake.date_time_ad())
checkout.append(fake.date_time_ad())
name.append(fake.name())
6.我们必须分别插入 ID、年龄和性别的值。ID 将包含范围从 1 到 100 的数据。在年龄变量中,我们将最小年龄设置为 8 岁,最大年龄设置为 80 岁,并且我们将大小指定为 100。性别部分相当复杂,因为你不能随机分配男性和女性。此外,一些 ML 算法只接受数字。我们将为男性和女性分配 0 和 1,比例分别为 60:40。
ID=pd.Series(range(1,101))
age=np.random.randint(8, high=80, size=100)
gender=np.random.choice([0, 1], size=100, p=[.6, .4])
7.现在,我们已经成功地将数据插入到那些空数组中。让我们将数据分配给 dataframe 列。
data= pd.DataFrame({‘Timestamp’:timestamp,’ID’:ID,’name’:name,’Phone number’:phone,’age’:age, ‘gender’:gender, ‘address’:address,’checkin’:checkin,’checkout’:checkout})
就是这样!我们已经成功创建了自己的自定义数据集。
First 10 rows of our custom dataset
您可能需要在这里执行探索性数据分析,因为数据集不够干净,您必须找到变量关系、异常值等等。但在那里你可以学到数据科学最重要的部分,即 EDA。
让我们做一些基本的 EDA。
8.分别用男性和女性替换 0 和 1。
data.gender.replace(1,’female’,inplace=True)
data.gender.replace(0,’male’,inplace=True)
9.将签入和签出转换为日期时间格式。
data[‘checkin’]=pd.to_datetime(data.checkin, errors=’coerce’)
data[‘checkout’]=pd.to_datetime(data.checkout, errors=’coerce’)
10.现在让我们将退房日期设置为入住+ 3 天
import datetime
enddate = pd.to_datetime(data.checkin) + pd.DateOffset(days=3)
data[‘checkout’]=enddate
11.让我们策划一些事情。
plt.figure(figsize=(20,20))
plt.scatter(list(data.checkin.values),list(data.address.values),color=’r’)
Scatter plot
我们可以在这里执行大量的 EDA,但是为了保持文章简短和精确,我把接下来的步骤留给你。祝 EDA 和本教程愉快。
以下是完整代码的链接:【https://github.com/kunaldhariwal/Building-a-custom-dataset
我希望这已经帮助你增强了你的知识基础:)
关注我了解更多!
感谢您的阅读和宝贵时间!
数据共享和工作台:试验数据新闻应用
本周,我有一点时间来玩两个新的(对我来说)数据新闻工具,它们已经在我的雷达上了。
第一个是名为 Workbench 的现成数据新闻应用。这个项目是计算记者 Jonathan Stray 的创意,得到了哥伦比亚新闻学院的支持。那么它是做什么的呢?嗯,据开发者称,这是一个内置培训的数据新闻平台:
“Workbench 是一个集成的数据新闻平台,无需任何编码即可轻松完成数据搜集、清理、分析和可视化任务。您可以从已经使用的来源加载实时数据,包括开放的政府门户、社交媒体、SQL 数据库和 Google Drive。完成的工作流可以与故事一起共享,以向观众展示图表背后的故事。”
该应用程序可在 https://app.workbenchdata.com在线下载。一旦你注册(你可以使用谷歌或脸书以及电子邮件)并登录,你就可以直接进入教程了。
教程遵循一种读>做>下一步的格式,鼓励你完成各个步骤,建立一个收集、清理、过滤和可视化数据的“工作流程”。这是令人印象深刻的东西,教程涵盖了很多领域非常快。它还有助于建立工作流的概念,这是 Workbench 的核心。
有一些预定义的“配方”,你可以复制并建立你的工作流程,如果你不怕做一点点击(并使用几次撤销功能!)来学习应用程序喜欢的工作方式,然后你可以从教程中学习你需要的东西。一旦你创建了一个工作流,平台的开源方法意味着原则上应该很容易共享它。现在,观众可以看到你的工作,同事可以从你的过程中学习。
我对这里的游戏有点晚了,因为 Workbench 已经开发了一段时间。但是感觉很成熟稳重。当我玩 Workbench 的时候,当我现在写这篇文章的时候,我有一种似曾相识的感觉。Workbench 是“有点像 google sheets”和“有点像 google fusion”。它有 IFTTT 的迹象,其中一些是“一个小而笨重的笔记本”。但这并不是一件坏事,恰恰相反。这意味着它正在成为数据驱动报告的一站式商店。我可以很容易地看到我推荐这是一个很好的工具,为学生和记者们提供一些自我导向的学习。
我本周试用的第二个应用是由国际调查记者联盟[开发的](https://andydickinson.net/2019/02/14/datashare_and_workbench/(http://www.icij.org/) Datashare 。来自 ICIJ 的 Will Fitzgibbon 很好地概括了创建 Datashare 的动机:
Datashare 是一个允许你有效地搜索和组织你的文档的应用程序
Datashare 建立在帮助 ICIJ 制作其最大项目的一些相同技术之上,如 巴拿马论文 和 天堂论文——但 Datashare 可以安装在你自己的电脑上,而不是依赖 ICIJ 的服务器。
Julien Martin 提供了更多关于 Datashare 如何做到这一点的背景(和一个很好的案例研究)
Datashare 集成了 ICIJ 久经考验的提取技术,该技术从文件中提取机器可读的文本(使用 Apache Tika ),对图像应用光学字符识别(tesserac OCR),然后将信息添加到搜索引擎(elastic search)。)
换句话说,您得到了一个“开箱即用”的 ICIJ 工具包,用于处理和管理大量文档。
与 Workbench 不同,Datashare 是一个驻留在您的计算机上而不是 web 上的应用程序。有道理;为什么潜在的敏感文件会在网上流传?事实上,该平台的未来计划包括记者安全合作的方式。这在技术上是很严肃的事情,这里的工作流程很大程度上是调查性新闻的多机构深潜模式。但是不要被推迟。安装程序很棒,支持文档也很棒。
安装完成后——这可能需要一段时间——您需要将所有调查文档移动到一个文件夹中。然后你从菜单中选择分析我的文档就可以了。
你能做的第一件事是提取文本。如果您有一堆 PDF 文件要处理,并且可以选择使用 OCR(光学字符识别,虽然更耗时,但也意味着您可以从扫描的文档图像中获取文本),这将非常有用。即使在我的旧 mac book air 上,小小的进度条也飞快地滑过。
您还可以在文档中查找人员、组织和地点。这使用自然语言处理(NLP),一种机器学习技术,可以从“自然语言”中分析和提取信息。这里真正发生的是一种叫做“实体提取”的东西,它提取大块的内容,并试图对它们进行分类。
这对于文档级别的工作非常有用,但是如果能够跨文档连接名称、地点和组织就更好了。谁出现在哪里,在什么样的背景下,有助于建立一个联系的画面。
很明显,Datashare 的目标是那些有更多内容需要研究的人。对于不经意的实验者来说,这是一个有趣的窗口,可以看到像 ICIJ 这样的组织的交易工具。我希望工具包中未来的工具将包括可视化这些联系的方法。但最终,Datashare 的核心功能是将文档转化为结构化数据。
如果我对应用程序的开发有更直接的建议,那就是在菜单上添加一个“我的文档”按钮。很难找到你添加的文档。我知道这将是铁杆用户不需要的东西——它是关于文档中的内容而不是文档本身。我也知道,对于像我这样直到被卡住才使用 RTFM 的人来说,这是一种安慰。但它将有助于浏览器的易用性,实际上不会超过一个链接,比如说“我的文档库”或“文件”。
结论
如果你对计算、数据新闻或调查性新闻有短暂的兴趣,我真的会向你推荐一部两者兼具的剧。我想很多人可能会对您使用 Workbench 所能做的事情感到惊喜。这绝对不仅仅是一个边做边学的平台。这里有一些可用的工具和足够的可扩展性来分散“但我可以在 python 中这样做”的批评者的注意力。不要让 DataShare 稍微简单一点的技术性让你分心。正如我所说的, Datashare 用户指南非常出色。
这两款应用在技术和复杂性方面为数据新闻实践的发展提供了一个有趣的高峰——仅此一点就是玩的好理由。他们或许也代表了对这一点的某种认可,以及我们对新闻业能否跟上时代步伐的担忧。但这是另一篇博文要思考的问题。现在,给他们一个机会,给他们你的反馈。
数据故事如何通过设计帮助您解决分析挑战并推动影响
结合设计和数据驱动的问题解决方法,在分析学和民族志的交叉点上展现故事。他们是你的向导,确保你在正确的问题上工作,你的解决方案实际上导致了期望的结果。
当我们最近举行一场旨在帮助一家非政府组织打击人口贩运的 24 小时黑客马拉松时,参赛团队的构成有着天壤之别:有许多团队具有分析咨询方面的丰富背景。然后是我的团队:一名数据工程师和三名设计师。该活动的目标是从客户提供的数据中获得见解。游戏开始了。客户简报是最重要的。每个队都有自己的锤子。
可能很难看出这样一个重设计的团队与看似明显的数据分析挑战有什么关系,但我们没有绝望。相反,这一挑战让我们能够展示一些有价值的经验,说明结合数据驱动和设计驱动的方法如何能够产生以前被忽视的独特见解。到达那里的关键是在分析学和人种学的交汇处寻找浮出水面的故事。
忽略锤子——从问题开始,而不是数据
设计思维成功的一个原因是它是解决方案不可知的,这让问题解决者比使用其他方法(如假设驱动的问题解决方法)探索更广阔的解决方案空间。在以数据为中心的环境中工作时仍然如此。当我们被要求在数据中寻找一个问题的答案时,我们有义务首先确保这个问题是正确的。这通常被认为是对任何项目资源的额外消耗,尤其是当团队已经处于交付模式时,设计才被引入到项目中。然而,当早期引入时,设计有助于与业务一起驱动问题定义过程。潜在地,而团队中的其他人则忙于后勤工作——尤其是臭名昭著的“获取数据”步骤。通过这种方式,一旦一个团队致力于解决一个问题,就可以确定解决这个问题将会给业务和用户带来影响。
在我们的项目室中,我们确保数据在我们需要时可用,但随后就不管它了。我们大多数人甚至没有在最长的时间里看一眼数据字典,以便不使我们的思维产生偏见,从而导致潜意识中消除潜在的丰富研究领域。相反,我们从两个熟悉的问题开始:真正的潜在问题是什么?我们到底在解决谁的问题?试图回答这些问题成为实际的初步目标,这使我们沿着著名的双钻石的第一个斜坡踏上了探索之旅。
使用数据作为约束
有许多工具可以用来定义问题空间和目标人物角色:利益相关者映射、服务蓝图、我们可能做的事情、五个为什么、通过视频会议的专家访谈,以及许多许多的便利贴(仔细想想,你真的可以在一个晚上安排很多发现——我们的开球时间是下午 4 点)。分析项目与不同类型的转型项目的不同之处在于,对特定数据和信息及其流动的关注提供了额外的限制;创造力喜欢约束。
想想这种认识如何影响任何设计活动。服务蓝图增加了一个数据层,关于该主题的访谈问题被添加到脚本中,重点领域和想法不仅根据业务、技术和人力约束进行优先排序,还根据与数据相关的约束进行优先排序。在这一阶段,数据成为推动研究和想法向前发展的推动者,并增加了关注可以实现什么和缺少什么的额外镜头。
在数据中寻找人类的故事
当面对关于人类的数据时,非人化的风险是非常真实的。这里是散点图,那里是钟形曲线,在你知道之前,你已经做出了对任何人都不适用的概括陈述。取而代之的是,思考通过把数据点看作人类可以学到什么,并尝试想象它们在现实世界中可能意味着什么。通常,你会发现一些人类故事的元素,这些元素可以作为研究的输入并被进一步研究。浏览关键参数列表,问问自己,如果是,这对一个人来说意味着什么。此外,如果您对一系列指标进行这样的操作,这会告诉您关于个人的什么故事呢?你可能会开始看到,这是数据注入人物角色的基础,可以通过进一步的研究来建立。
在我们打击人口贩运的黑客马拉松中,幸存者的年龄分布给了我们这样一个故事中最引人注目和悲惨的例子。当观察年龄和缓解干预的有效性之间的相关性时,我们可以看到最年轻的受害者只有几个月大。这不仅是一个可怕的见解,而且它也为我们的研究提供了信息,因为我们现在知道要与我们的客户进行一系列非常不同的对话,其中包括最年轻的受害者的特殊需求。
听听关于数据的故事
当你围绕数据和人们与数据的互动、他们的数据需求等进行一些研究时,要特别注意哪里遗漏了。虽然这可能与手头的分析项目没有太大的相关性,但在定义未来的数据战略时,这是非常宝贵的。
通过揭示一个组织目前在数据方面正在进行或考虑的其他事情,您可以构建一个各种计划交叉点的路线图,利用连接而不是完全孤立地构建另一个东西。
在研究过程中,这两点都有助于在服务蓝图或流程图中捕捉,并在设计多接触点干预时占据中心位置。
在黑客马拉松的第一个晚上,客户已经对我们提出的一系列问题留下了深刻的印象,说我们这半天的工作已经产生了令人兴奋的和非常不同的见解,这些见解是由纯粹的分析团队在两个月的工作后发现的。他们也很高兴能够着手其中的一些工作,最终形成了一份路线图提案。我们进一步了解了正在进行的旨在收集全新数据的工作,并准备制定一项战略,在这些新数据流准备就绪后充分利用(但不依赖)它们。
在黑客马拉松期间,有人可能会担心,在我们的团队真正开始“真正的工作”之前,所有这些设计活动都浪费了很多时间。然而,当我们已经产生了一系列人种学的、数据引导的见解,并针对特定的高价值业务问题设定了目标时,其他团队已经深陷在数据中,仍然在广泛地探索可用的东西,一些团队仍然在努力在他们的笔记本电脑上设置他们的环境。有了一个能让我们阐明这些见解的概念,我们感觉很好,可以回家睡一觉,然后第二天早上重新开始。
通过洞察力推动行动
如果最先进的分析结果没有被采用并推动产生结果的行动,那么它们就毫无价值。事实上,随着围绕分析和人工智能的技术得到更广泛的理解,并走向商业主流,模型采用和其他人为因素成为主要障碍和机会。因此,了解用户的背景和需求(如果你愿意,也可以说是他们自己的故事)并在叙述中提供分析见解,从而使他们能够采取行动,这一点至关重要。以结果为中心的分析团队的工作在交付模型时不能被认为已经完成。
通过应用设计思维和以人为中心的人工智能方法,团队可以发明令人信服的解决方案,帮助用户理解分析见解的真正含义,并在正确的上下文中为他们提供正确的信息。值得注意的是,这可能不需要最先进的技术。
对于我们的项目,我们创建了一个概念,说明了整个用户旅程的未来状态,在此期间,社会工作者开始与一系列干预措施进行互动,以帮助他们做好工作——其中一些是低技术和基于纸张的。至关重要的是,这不是一个天马行空的艺术概念,而是一个非常基于现实并在今天可以执行的概念,同时强调了未来的潜力。
A regular war room? Not so much, once you notice the smileys used in our prioritisation matrix.
包扎
当然,在一场黑客马拉松的结尾,来自不同背景的许多有才华的人讨论了一个非常真实和令人感动的话题,每个人都是赢家。我们离开时不仅对我们所有新产生的见解的潜在影响感到高兴,而且还兴奋地将一个图画书案例研究放入口袋,该案例研究介绍了不同解决问题方法的组合如何能够在不消耗资源的情况下提高团队的效率。
顺便说一下,今天生活在奴隶制下的人比以往任何时候都多。如果你想帮助打击侵犯人权的行为,向国际特赦组织捐款。
这篇文章的节略版已经发表在 QuantumBlack 博客 上。
原载于www.thomasessl.com。本文表达的观点纯属个人观点,不一定代表我的雇主的观点。
日期、时间、日历——数据科学创伤的普遍来源
一份列出了大多数主要疯子的名单,以及一些生存建议
Just some clock in the city. (Photo: Randy Au)
很少有所有数据人共享的共性。我们称之为“数据科学”的领域非常广泛,从人工智能和机器学习的理论工作,一直到个人数据分析师被迫在 Excel 中进行前/后分析以“发现变化的影响”。
但是总有一个话题能让我和一个数据人员建立联系:日期、时间和日历。开始讲一个你在工作中发现的疯狂的故事,你会立刻产生共鸣和友情。每个人以前都被烧伤过。很糟糕。
所以让我们来谈谈好的,坏的,完全疯狂的,以及一些应对的方法。
部分列表
ISO 8601 是希望,ISO 8601 是爱
YYYY-MM-DDThh:mm:ss.sTZ (and many variations thereof)
ISO 8601 很神奇,每个人在交流日期和时间时都应该默认使用它。句号。
如果每一个数据导出、数据输入 UI、手写表单、过去和现在都符合这一标准,我们就不会陷入今天这个畸形的地狱般的境地。这太重要了,这是我唯一记得的 ISO 标准编号。还有一个专门的子编辑,荣耀归于 ISO8601 。
ISO 8601 有很多部分,但人们通常认为它是 YYYY-MM-DD 日期和 hh:mm:ss.s 时间格式,以及连接两者以创建日期时间、表示时区、小数秒等的无数变体…标准中甚至有更多关于时间间隔和周数的内容,但是这些内容并没有被经常使用。
需要说明的是,关于 ISO 8601 最重要的部分是术语的顺序,它明确且易于记忆和解析。正是这种易于理解的特点使它成为交流日期和时间的首选方法。它还可以很好地进行字符串排序。
当你输出日期时,帮大家一个忙,输出一个 ISO 8601 标准输出,不要发明你自己的格式顺序。总有一天,当您必须解析匿名用户的日期输出时,您会感谢他们。
热爱你的图书馆
谈到日期、时间和日历,你绝对不想重新发明轮子。有很多边缘情况并不明显。除非你已经穷尽了重用别人代码的所有可能性,否则不要去做。
有很多可供你选择语言的库。我说的不仅仅是标准的日期/时间库,这些库可能内置在您的核心语言中,帮助您进行基本的日期计算并输出到特定的格式。通常有一些工具可以帮助你在不发疯的情况下阅读和破译格式。
例如,Python 有 dateparser ,这是一个很棒的小库,它试图为您解析最常见的日期格式,并将它们转换成 datetime 对象。它还有许多其他有趣的功能,比如时区处理、相对时间(“1 分钟前”等)和一些备用日历。很酷的东西。
也许你选择的语言有这样一个库。一定要看。
随着理智的高点(仅有的一点点)消失,让我们慢慢陷入疯狂。
—来自工程/数据同行的痛苦—
这部分是我们最能控制的。这是我们或我们的同事建立的东西。这是我们自己造成的痛苦。在很大程度上,当我们了解问题并避免不良做法时,我们可以将损害最小化。我们可以更少地伤害他人,希望它会回来。
日/月/YY 对日/月/YY 对年/月/日等等。
Cyan: DMY, Yellow: YMD, Magenta: MDY, Green: DMY/YMD, Blue:DMY/MDY, Red:MDY/YMD, Grey: MDY/YMD/DMY ( https://en.wikipedia.org/wiki/Date_format_by_country)
看看这张按国家划分的日期格式图。任何必须与多个国家合作的人,尤其是在美国(MDY)和欧洲(DMY)之间工作的人,都面临着可怕的“8/5/14 是什么意思?”。
您可能认为这种格式与我们电子数据人无关,但这并不是因为您可能要处理本地化的日期时间标记。也许你正在使用网络抓取工具。我从根据我的语言环境“有益地”导出数据的东西那里得到了数据导出,而不是一些理智的东西。我不知道他们为什么这样做,这没有帮助。
从各种传统系统输入的数据和手写笔记可以使用传统的本地格式。真正疯狂的系统(就像收购后拼凑在一起的两个不相交的系统)甚至可能给你不同国家的本地化时间戳,所以你会想知道日期来自哪个国家。
**尤羞调出电子表格。**为什么电子表格都试图使用 DD/MM/YYYY 类型的格式?!Excel,Google Sheets,Libreoffice,唉!最重要的是,他们有胆量在大量数据导入时不一致地这样做!
**处理这个问题的技巧:**除了找出数据的正确解析方案之外,别无选择,有时通过痛苦的分析和反复试验,有时通过询问某人或阅读文档。理想的情况是说服数据源输出更干净的东西。尽你所能去做。
12 小时与 24 小时时钟
24 小时时钟格式最常见于日志记录等,但 12 小时格式在人机界面中仍然很常见。当您必须解析/打印供人们使用的数据时,您必须处理这个问题。
不得不解析 AM/P.M .的各种写法实在是太难看了。不要忘记它也因地区而异。不同的国家会用不同的方式来表示上午和下午。请参考您的区域设置以了解预期情况。
说到这里,你知道正午和午夜的命名会因你遵循的习俗而不同吗?因为它当然会。
https://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
生成输出时,坚持使用 24 小时时钟。大多数人都能理解 24 小时时钟,所以数据输出没什么大不了的。为实际的时钟 ui 保存 AM/PM。
Unix/POSIX 时代——还不算太糟,但仍然有缺陷
Unix 时间定义为从*1970–01–01t 00:00:00Z 开始的秒数。*每天正好有 86400 秒,忽略闰秒。这是一个平稳增长的计数器,是一个很好的属性。这意味着使用 unix 时间的软件不太可能会感到惊讶,也不太可能会有与日历相关的奇怪错误。(我们稍后将回到闰秒,因为那是一个完全独立的主题)。
Unix 时间忽略闰秒的问题在于它是如何实现忽略的…通过重用相同的时间戳 23:59:60(闰秒)作为 00:00:00,因为它认为它们相等。这意味着时差计算将会偏离闰秒数,除非你考虑到它们(如果你在乎的话)。当然,这还会让人觉得一秒钟内发生了 2 秒钟的活动,这也是不可逆的数据丢失,因为您无法仅从时间戳区分这 2 秒钟内的事件。对于人类规模的使用来说,这并不太疯狂,但这几秒钟对于分析来说很重要。对于更敏感的应用程序,有一些方法可以让抹去一天中的闰秒。
Unix 时间通常存储为 32 位整数,尤其是在较旧的系统上。这当然意味着当整数溢出时,还有 2038 年的问题在等待。希望到那时,大多数系统都将转换到 64 位整数,这可以覆盖比当前宇宙年龄更多的秒。
尽管对闰秒的处理不可靠,**如果由于各种原因,ISO 8601 由于某种原因不切实际,Unix time 是一个很好的替代选择,**尤其是在闰秒模糊的情况下。与紧凑 ISO 8601 的 18+字符串相比,它是一个相对紧凑的 32 或 64 位整数(或 10 个字符串),通常表现良好。
主要的缺点是它不是人类可读的,但是大多数编程语言都很容易处理它。不断地调用 unixtime_to_timestamp() 类型转换函数是一件令人烦恼的事情,但是我很乐意接受它,而不是其他选择。
如果你关心时钟、秒和时间,POSIX 选择(不)处理闰秒的方式显然有点疯狂。这是对历史的一次惊人解读,它是如何变成那样的。
处理毫秒和微秒
大多数时间规范对小数秒的规定相当简单:添加一个小数点,然后是小数的数字:例如 hh:mm:ss.sss 。
需要知道的是,因为计算机上的浮点数学可能有准确性问题,分数通常在内部用整数或类似结构表示,小数点是为了显示的目的。
这方面的一个例子是 NTP 协议实际上使用一个定点 64 位 int 来表示时间。它使用最高有效的 32 位来表示自纪元以来的秒,剩余的 32 位来表示小数秒。
“这种表示的精度大约是 200
皮秒,这应该足以满足最奇特的
需求。”— RFC-1305 ,NTP v3 规范
因为大多数系统不需要 200 皮秒的时间表示,所以大多数系统不遵循这样的设置。相反,您会更常见地看到将 Unix 时间扩展为带点的整数分数秒参数的变体, s.ssss 。另一种常见的格式是将时间存储为一个大整数,称为“自(Unix)纪元以来的毫秒/微秒”。这是可行的,因为毫秒和微秒分别是 10⁻和 10 ⁻⁶,所以用milliseconds_since_epoch/1000 = unixtime.milliseconds
除以/乘以正确的因子来来回转换是微不足道的
其他奇怪的时代
Epoch 是任意的构造,所以除了 POSIX Epoch 之外,当您在环境之间移动时,还会遇到许多其他的 Epoch。
最著名的可能是 Excel,它开始于 1900 年 1 月 0 日(是的,1 月 0 日…),并计算自纪元开始以来的天数。但是其他有趣的包括从 1980 年 1 月 6 日开始的 GPS 时间,以及从 1900 年 1 月 1 日开始的 NTP 纪元。Twitter 开始了他们的 twepoch 他们的第一条 tweet。
99.99999%的时间里,您可以生活和做日期计算,而不必过多考虑纪元,这要感谢良好的库和接口(以及有多少事情默认为 unix 时间)。
但是偶尔你会遇到突然变得重要的情况。我最近不得不与 Excel 的时间格式作斗争,因为将 1 小时添加到时间戳涉及到将超级直观的 1/24 添加到时间戳(因为它是以天为单位的)。
在其他情况下,时代很重要,当你超越它们的时候(通常是在它们之前)。例如,TAI 原子刻度的纪元是 1961–01–01t 20:00:00(UT2),也就是我们称为 TAI 的原子秒基本上开始累积的时间。如果你以某种方式在纪元前进行推断,这就提出了你到底在用什么的问题。如果提到 1600 年这个原子秒尺度的时间,这又意味着什么呢?那实际上是一派胡言。
解析/输出日期/时间—处理 strptime/strftime 等。
ISO8601 仅仅定义了日期/时间的各个部分应该出现在哪里,以什么方式出现。它不是一种定义处理这类事情的语法的语言规范。因此,我们必须把这个问题作为一个单独的问题来处理。
我不知道出于输出或解析目的表示日期/时间部分的标准方法。最接近标准的东西来自于 POSIX 和基于它的 C 库。
*#The familiar %-based syntax we all know and constantly forget
%Y-%m-%d*
由于 POSIX 标准中的一些不明确的地方,尤其是在混合了各种语言环境设置的情况下,不同种类的系统之间的语法有时会略有不同,从这个意义上说,这并不是一个严格的“标准”。但这是我能找到的最接近的东西了。
POSIX 库的好处在于它已经过实战测试,可以正常工作。由于日期/时间代码如此混乱,很少有人想重新发明轮子。最终发生的情况是,许多植根于 C 和 Unix 的语言广泛使用这种格式,因为 C 系统 API 可供使用。例如,Python、R、Ruby 和 Golang 就是这样,因此它们的日期格式字符串非常相似。
但是并不是所有的语言都使用 strftime! Java 有自己的 SimpleDateFormat,Javascript 有 Intl。DateTimeFormat , C#自带东西。他们彼此都非常不同。有时在库中有一个单独的 strftime 选项会很方便,例如 Perl 有一个本机的 time 格式,但是可以选择使用 time_strftime。
一般都是巨乱。当你需要它们时,只需查找函数,尤其是当你跨越不同的系统和语言时。如果你必须熟悉一个,POSIX one 可能是最普遍的。但是要知道总有一些东西可以利用,永远不要发明自己的东西。
时钟同步
每个时钟都是独一无二的。它们可以比旁边的一个完全相同的时钟跳动得稍快或稍慢。所有的时钟也不是完全稳定的。由于包括温度、方向、电压甚至相对论在内的大量影响,时钟将会失去同步。即使对于超精确和昂贵的原子钟来说也是如此,更不用说你主板上的便宜的水晶钟了,它们的精确度要差很多倍。
有一个精确的时钟对这个世界没有用,关于时钟最重要的事情是时钟应该同意高精度。在分布式系统、云计算、虚拟机和物联网的现代世界中,到处都有不同质量的多个时钟需要在时间上达成一致。
通常,作为一名数据科学家,你不会涉及到确保连接到你的系统的每台计算机都有一个准确的时钟这一困难的工程问题。但是,你很可能是所有这些时钟数据的主要消费者之一,如果这些时钟出了问题,你将受到严重影响。
当时钟不同步时,你就无法说事件 A 发生在事件 b 之前。根据时钟不同步的严重程度,你甚至可以看到事件和因果关系似乎在时间上倒流。像 Cloud Spanner 和 cocroach DB这样的数据库花费了大量精力(在 Spanner 的情况下,还有原子钟)来保持时间准确,以便 db 事件对于可以跨越全球的系统来说保持正确的时间顺序(可串行化和可线性化)。
虽然我们大多数人都不必过多考虑时钟误差,但如果您正在构建数据库系统,或者在一个时间尺度非常精确的领域中工作,或者远程传感器可能存在严重的时钟误差,您就需要注意了,因为时钟误差可能非常细微,并会导致非常奇怪的错误。认真考虑使用哪个时钟来设置时间戳变得很重要(服务器端还是客户端,等等)。
顺便提一下,计算机使用 NTP 协议来相互同步时钟已经有很长时间了。NTP 通常被认为能够在时间服务器的毫秒内获得时钟(多少毫秒,是 10ms 还是 100ms,取决于你的网络和设置)。为了获得更高的精度,还有 PTP ,它允许额外的(昂贵的)硬件时钟辅助来提高性能(我经常看到提到在微秒内同步到的时钟 orb 更好)。
—来自自然的痛苦—
这一部分是关于痛苦和复杂的事情,仅仅是因为大自然没有完全符合人类的习俗。我们能做的不多,只能生活在我们所拥有的一切中,并充分利用它。
闰年
人们熟悉公历,但闰年的规则比简单地每四年有一个闰年要复杂得多。还有 100/400 法则。
除了能被 100 整除的年份之外,每一个能被 4 整除的年份都是闰年,但如果能被 400 整除,这些百年就是闰年。— 维基百科
这很难实现,也很容易忘记,请在处理日期时重用一个正确实现的日历模块,即使是简单的事情,比如增加未来的日期。
日历已经有几个世纪没有改变了,现有的代码已经稳定运行了几十年。
闰秒
闰秒的存在是因为我们的原子钟与地球的自转相比太精确了。这让生活变得复杂。
这个迷人的时间兔子洞的简短故事是,如果我们将一天精确地定义为 86,400 秒,那么使用 SI 秒的原子时间刻度比地球自转速度略快,可以将太阳放在中午天空的同一部分。我们的日常民用时标(UTC)由于使用原子定义的 SI 秒,比太阳秒(平均太阳日的 1/86,400,这是 UT1 时标)略快。由于民用时间是基于太阳中午在格林威治上空的正确位置,无论何时|UT1-UTC|> 0.9s
,我们都故意通过增加 1 秒来延迟 UTC,并使一天为 86,401 秒,以保持两个时标同步。
如果地球转得更快,我们一天也需要 86,399 个小时,也有类似的协议。显然,有过这样的例子,地质构造显著地加速了地球的自转,但不足以跨越 0.9 秒的门槛来触发闰秒。想象一下,当 23:59:59 一天都不存在的时候,我们会发现多么疯狂的错误。
Changes of the length of day (ms difference per day) over history, colored bands are integral sums of 1 or 0.5hrs over the year
闰秒的主要问题是它们不可预测。地球的自转受到潮汐、月亮、地幔和地核中发生的事情等的影响…这是一个测量值。所以,你唯一能做的就是查看官方公告中的闰秒公告。
在大多数应用中,闰秒是我们可以安全忽略的东西。大多数日期计算都基于日历天数的差异,然后根据 86,400 秒/天计算秒数。
例如,从 12 月 31 日开始到 1 月 3 日开始有 3 天的时间(不包括 1 月 3 日)。我们会计算 3 个日历日,31 日,1 日和 2 日。我们通常不会说有 259,200 秒(或 259,201 w/ a 闰秒),除非明确要求该值。失去 1 闰秒将代表该时间长度中 0.00038%的误差。
对于那些重要的实例,有所有闰秒的列表,比如这个。这样,您可以返回并更正任何受影响的数据。
闰秒错误
闰秒经常会导致意想不到的错误,因为时钟的行为可能不直观,人们很少会忘记它们。一个令人难忘的例子发生在 2012 年,当时一个 6 月 30 日的闰秒触发了 Linux 内核和 Java 中的错误,导致各地一系列系统瘫痪。我记得看到停电,第二天每个人都对即将到来的闰秒保持警惕。
尽管如此,错误仍然会发生。2017 年 1 月, Cloudflare 的 DNS 也遭到了一次。这种东西很微妙,也很罕见,你可能没有测试来防范它。所以请放心,每一个闰秒,在某个地方,都会有人度过一个糟糕的夜晚。
—来自社会的痛苦—
这类问题可能是所有问题中最糟糕的。整个社会充满了矛盾、不一致和武断的规则。他们也可以在任何时候,以任何理由改变技术标准。
非公历日历
我们大多数人都生活在公历中,这很好,因为虽然我们不得不处理闰年之类的事情,但我们至少都同意使用相同的日历。
直到我们没有。
我甚至没有谈论有趣的替代日历,比如没有人真正使用的修订儒略历,或者在伊朗使用的 T2 波斯历。历史上有很多不同的日历需要被调和。历史学家和天文学家最敏锐地意识到这一点,因为他们最有可能不得不使用追溯到几个世纪或几千年前的记录。我们其余的人是幸运的。
相反,我说的是用于零售/商业目的的人造日历,比如4–4–5 日历。这些在世界上被广泛使用!我在一个必须处理商品生产和装运的电子商务网站工作时遇到了这个问题,所以他们用它来跟踪他们的工厂工作。
我仍然没有完全理解 4–4–5,而是依靠转换函数/表格来处理它。
处理这种情况的技巧 : 在没有精确算法的情况下,永远不要试图自己复制另一种日历的逻辑。他们是神秘的!自己写就像发明自己的加密算法。不要这样做。你会搞砸的。必须有一个库可以做到这一点,即使它是针对另一种语言或者只是一个查找表。使用现有资源。
事实上,永远不要试图实施自己的日历,即使是公历。
月球…任何东西
阴历(目前使用的大多数是阴阳历)的表现与我们熟悉的阳历非常不同。
我是出生在纽约的华裔。我和其他人一样是用公历长大的。但在一年中看似随机的时间点,直到很久以后才会明朗,我的家人会在农历庆祝节日。有些年份的农历新年是在二月初,有时迟至三月。
这有什么关系?因为世界上的一些主要节日都与月亮挂钩。中国新年是其中之一,超过 10 亿人为了它去度假一个多星期。
不在亚洲?还有复活节,这是许多受基督教影响的国家的主要节日。这种东西会导致你的时间序列数据出现各种异常。
幸运的是,有代码和表格可以找出这些重要的日期在哪里!你只需要知道这些东西的存在,这样你就可以补偿。
时区
没有关于日期和时间的疯狂的讨论,就不能提到时区概念这种特殊的疯狂。它考虑了所有复杂的计时问题(闰秒、闰日等),并加入了明显的人为(和政治)因素。
简言之,避免像对待瘟疫一样使用当地时间。因为最终会溃烂成伤害你的东西。用 UTC 存储所有东西,或者像 Unix 时间一样与 UTC 挂钩的东西(没有夏令时诡计,没有政府改变他们的时间规则)。必要时,您可以随时以当地时间显示事物。
我们为什么要和他们打交道呢?因为人类。一些分析使用当地时间效果更好,例如测量早餐活动。人们使用本地时间来协调他们的个人生活,但这是计算机的噩梦。
One of my favorite videos on YouTube, ever. The pain and frustration is real.
时区变化相当频繁。不是每个时区都是整数小时,印度标准时间是 UTC+5:30,新西兰的查塔姆岛标准时间是 UTC+12:45。夏令时/夏令时规则可能会增加/减少一个小时(或更多),在某些地区可能会遵守,也可能不遵守。我甚至不确定我是否理解由于政治原因,一个地方有两个当地时间的情况。
如果你想了解世界上所有时区的变化,你可以下载时区数据库,订阅 IANA 的公告和讨论。事情经常出人意料地变化。
夏令时,时间序列的破坏者
夏令时涉及整个国家或地区,让他们的时钟改变到另一个小时,通常(但不总是!)以 1 小时为增量完成。这只是一群人可以决定去做的事情。更糟糕的是,政府可以随时改变其发生的细节。如果你幸运的话,他们会提前宣布,这样人们就可以做好准备。这是一件不得不处理的可怕事情。
What Daylight Saving shuffles look like to an analyst working in local time who forgets it happened
这就是为什么总是存储带有 UTC 时间戳的数据被认为是最佳做法,因为 1)它没有夏令时,2)它不能在未来的任何时候决定采用夏令时。
第二点很重要。仅仅因为一个地方现在没有日光节约问题,并不保证将来也不会有。只有 UTC 真正拥有这种保证。
假日
作为一名网络分析和广告出身的人,我对我经常打交道的市场(主要是美国)的假日越来越敏感。主要原因是因为当有一个影响整个国家的重大节日时,网络流量、广告点击量、小配件销售量都会下降。
复活节尤其令人头疼,因为它是由月亮的位置决定的,这意味着它每年都在不同的日期,中国的农历新年也是如此。在我职业生涯的早期,我数不清有多少次年度或月度报告显示巨大的变化数字,仅仅是因为复活节在现在或过去已经到来。现在我知道要检查它(如果有必要,事件会提前发出警告)。
关于假期的另一件令人沮丧的事情是,它们对不同的事情有不同的影响。如果每个人都回家度假,车流量就会增加,但如果他们都呆在原地,车流量就会减少。购买一些东西(如玉米糖)在特定季节会增加,而其他物品则不是季节性的。你的特定目标人群可能会庆祝其他人不庆祝的事情。不仅仅取决于外界,还取决于你在乎什么。
你如何处理这个?你唯一能做的就是看看过去的行为,认真思考你的用户群,并询问主题专家。
外面可能还有更多
一篇稍长的中篇文章可能涵盖所有时空的错乱。我已经尽了最大努力来涵盖你应该注意的真正大的领域,但是具体的要由你来决定。如果我错过了什么,让我知道!
谢谢
感谢所有善良的人们,是他们温柔地鼓励我敢于写下这种疯狂。
你知道你是谁。
参考资料,链接到关于时间的酷东西
对于任何想深入研究计时历史的人来说,这里有一些参考资料,是我在理解 unixtime 如何处理闰秒以及闰秒是什么时使用的
- 维基百科页面为 UT 、 Unixtime 、 UTC 、地球自转、时间格式化/存储 bug
- POSIX UNIX 时间标准形成的历史
- 一个非常深入的时标列表,不仅仅是史蒂夫·艾伦的 UT1 和 UTC
- 史蒂夫·艾伦 w/r/t 更多关于时间的东西,取消 UTC 的闰秒意味着什么
- IERS 的网站关于 UT1 和一天的长度
- PHP 时间表的解释终于让我明白了
- 我发现 unix time(不)如何处理闰秒,另一页解释 PHP 的时间
- Chrono:用于日期计算的低级算法 —它们从 3 月 1 日开始计算日历,以更优雅地处理闰年(闰年会将一天添加到一年的最后一天,而不是随机放入其中)
- NIST 闰秒常见问题和 UT1-UTC 信息
- 泰纪元是 1961 年 01 月 01 日 20:00:00 UT2
- 一份假期清单,其中只有极小的一部分会困扰你,直到更多的假期突然来临。
- 程序员相信的关于时间的错误列表——有人在我写这篇文章的中途把它和我联系起来,我想我的例子涵盖了很多这样的例子,但不是所有
- 您是否知道 ntpd 并不是 NTP 的唯一实现?还有 Chrony ,和 openntpd ,针对不同需求设计。
交友 App 匹配优化开发
来自 Dine 首席执行官在 PyCon JP 2019 的体验
Photo by freestocks.org on Unsplash
PyCon JP 2019 于 2019/9/16~ 2019/9/17 举办,为期两天。这个帖子是关于第二天的一个讲座。
盛冈孝是约会应用 Dine 的首席执行官。他的演讲主要集中在他在匹配优化方面的开发经验。数据处理在 Google Cloud data flow(Apache Beam Python SDK)下,ML 部分由 Cloud ML 引擎完成。幻灯片在这里。
背景
约会应用的目标是匹配两个用户。主要的匹配方法有两种,搜索法和推荐法。
- 搜索方法:用户根据自己的喜好进行搜索
- 推荐方式:交友网站会为用户推荐候选人
盛冈隆告诉 Dine 主要采用推荐法。
为了解决 稳定婚姻问题 ,Dine 采用了 大风–沙普利算法 **。**但是这个算法有一个很大的问题,计算成本巨大。
降低计算成本
看,我们总共有 N 个用户。我们首先计算用户 A 相对于其他 N-1 个用户的得分。然后,我们对 N 个用户进行排名,为用户 a 进行推荐,运行时间将为 O(N log N)。
为了降低计算成本,Dine 将用户分成不同的组(演讲者没有提到划分标准)。分组的优点是可以将用户数从 N 个减少到组数,并可以并行计算得分。
但还是有两个问题。
- 匹配性能低
- 分组方法对少数用户产生了不良影响
但是演讲者没有提到如何解决这些问题。他在接下来的演讲中主要讲了如何提高计算能力。
基础设施改善
原来的系统架构如下图所示。
AppEngine 正在进行匹配过程计算。但是对于 GAE 实例的 CPU 和内存来说,计算量仍然很大。
来解决问题。Dine 用提供 Java 和 Python SDK 的 Could Dataflow 代替 GAE。
这个系统的容量比以前增加了 10 倍。
与 can AutoML 表集成
数据流的一个优势是与其他机器学习服务的高度兼容性。Dine 使用 Cloud AutoML Table 从用户配置文件中构建表格功能。它会自动调整参数。第一天介绍 AutoML,业界使用的 AutoML 库概述。
根据 AB 测试的结果,匹配分数(f1)增加了 20%。以下是演讲者在使用 Cloud AutoML Table 时的一些想法。
优点:
- 使用 BigQuery 很容易
- 批量预测和在线预测非常容易部署
缺点:
- 无法下载模型
- 不知道模型架构
查看我的其他帖子 中等 同 一分类查看 !
GitHub:bramble Xu LinkedIn:徐亮 博客:bramble Xu
参考
- https://pycon.jp/2019/schedule/?sessionId=207
- https://drive . Google . com/file/d/1 sxn 7 _ u 9 oddell pq 0 jn 2h 8 vfll-HBP 2 az/view
用决策树确定文本的日期
使用决策树来估计一本书的写作年份
介绍
如果我们使用机器学习来估计这本书的写作时间会怎么样?
想象你自己在你祖父母的旧阁楼里,仔细阅读布满灰尘的旧书架,这时一本不熟悉的大部头吸引了你的目光。这似乎是一部小说。你不知道它是什么时候写的,这个答案似乎连谷歌自己都不知道。你拍下其中一页的照片,然后使用文本提取器,获得文本的数字表示。如果我们使用机器学习来估计这本书的写作时间会怎么样?
在这篇文章中,我们将讨论和实现一些方法来尝试确定文本的日期。除了所述的例子,这种类型的模型在一系列领域中有应用,从检测假新闻,到打击历史修正主义,到改善历史上准确的小说,到确定死后文学作品的真实性。
这项任务最容易解释的模型之一是决策树,它可以提供关于哪些类型的单词可以用来区分文本日期的有趣推论。
资料组
我们的首要任务是收集数据进行训练。我们将要使用的数据集包含大约 200 个文本,按时间分布如下
Time spread of the dataset
我们将自己限制在这个时间段内,因为在一个更长的时间段内,英语的不同状态之间的绝对语言距离太大了(对于一个说现代英语的人来说,14 世纪的英语是难以理解的)。查找较旧的文本数据也是乏味的,因为许多较旧文本的可用版本只是用更近的、可理解的英语转录的(使用这些版本而没有适当的谨慎相当于给数据贴错标签)。我们的优先任务之一是确保在每个时间段,文本包括不同的主题,以防止我们的模型识别文本主题或格式,而不是纪元。
特征选择
作为健全性检查,为了确保文本可以可行地聚类,我们可以执行主成分分析 (PCA):
PCA on the dataset
我们有相当多的聚类,然而,蓝点的聚类(17 世纪晚期)可能是因为这一时期的文本处理了类似的主题。这凸显了从多个来源获取数据的重要性,因为它显示了模型倾向于选择最简单的要素。
表示文本
有无数种方式来表示文本数据,最常见的一种是 **word2vec,将所有的单词放在一个 n 维向量空间中,相似的单词在空间上很接近。**然而,出于我们的目的,我们注意到基于 word2vec 的模型对于决策树来说既慢又低效。
相反,让我们使用一种单词袋方法,其中每本书是一个向量,其索引代表一个唯一的单词,包含该单词在书中的实例数量。向量的大小将取决于词汇的大小(即,如果你的书中有 1000 个独特的单词,则输入的大小将是 1000)。
例如,假设您的词汇量为 5,表示为列表
Vocabulary for the example
我们想把“我饿了”这句话编码产生的向量将简单地是:
Encoding for “I am hungry” with the given vocabulary
相反,如果是“我饿了,我”,我们就会
Encoding for “I am hungry, I” with the given vocabulary
既然我们有了对每本书进行编码的方法,那么让我们定义一个准确性度量。
定义准确性
假设我们的模型声称一本书写于 1527 年,但它实际上写于 1531 年。模型准确吗?
尽管均方误差(MSE)是确定模型准确性的显而易见的方法,但它不是很直观。为了产生更简单的度量,让我们定义 𝛿 、容差(以年为单位),以获得以下精度函数:
换句话说,如果模型在 𝛿 年内猜测正确,那么我们说它是准确的;否则就是不准确的。在下文中,我们将使用𝛿的**= 50 年和𝛿的**= 100 年。****
什么是决策树?
让我们来看看我们将要用来学习将文本与日期相关联的模型,决策树。
决策树分类器类似于流程图,终端节点代表分类输出/决策。从一个数据集开始,测量熵以找到一种分割数据集的方法,直到所有数据都属于同一个类。
Basic example of a decision tree¹
点击此处查看更多关于决策树的文章。
结果和解释
模型的准确性
一旦对决策树进行了训练,我们就可以在测试集上绘制一些预测:
Results for a decision tree with pruning
上面的图是线性的,表明决策树的性能很好。事实上,在我们的测试集上,我们获得了 84%的准确率(对于𝛿100 年)和 82%的准确率(对于𝛿50 年)。
解释和可视化
在这里,我们可以看到树的顶部本身的可视化。
树的许多分裂决定是智能的,对我们人类来说是有意义的。例如,电话在 1900 年开始流行,珀西这个名字的流行程度在 19 世纪晚期达到顶峰,从那以后稳步急剧下降。这里有一个表格,包含一些有趣的分裂,可以在树中找到。
Some interesting splits in the decision tree
结论
我希望您现在对如何使用决策树来估算一本书的写作日期有了更好的了解。
感谢
我要感谢保罗·塞格斯和法布里斯·海底平顶山-西奥内斯对本文的贡献。
参考
[1] Chirag Sehra,决策树易解释(2018),https://medium . com/@ Chirag se HRA 42/Decision-Trees-Explained-Easily-28f 23241248
David Duvenaud 谈学术界人工智能和机器学习的未来
在成为多伦多大学机器学习助理教授之前,David Duvenaud 曾在剑桥、牛津和谷歌大脑工作过。
他在神经颂歌方面做了开创性的工作,并且在过去十年的大部分时间里一直处于该领域的前沿。
大卫和我们坐下来谈论机器学习的发展方向,以及学术生涯是如何形成的。
在 Twitter 上关注他: @DavidDuvenaud 点击这里查看这一集:
注意— Seq2Seq 型号
序列间 (abrv。Seq2Seq)模型是深度学习模型,在机器翻译、文本摘要和图像字幕等任务中取得了很多成功。谷歌翻译在 2016 年末开始在生产中使用这样的模型。这两个开创性的论文中解释了这些模型( Sutskever 等人,2014 , Cho 等人,2014 )。
Seq2Seq 模型是什么?
Seq2Seq 模型是一种模型,它接受一系列项目(单词、字母、时间序列等)并输出另一系列项目。
Seq2Seq Model
在神经机器翻译的情况下,输入是一系列单词,输出是翻译的一系列单词。
现在让我们努力减少我们的黑匣子的黑色。该模型由一个编码器和一个解码器组成。编码器以隐藏状态向量的形式捕获输入序列的上下文,并将其发送至解码器,解码器随后产生输出序列。由于任务是基于序列的,编码器和解码器都倾向于使用某种形式的 RNNs、LSTMs、GRUs 等。隐藏状态向量可以是任何大小,尽管在大多数情况下,它被视为 2 的次方和一个大数字(256,512,1024 ),这在某种程度上可以表示完整序列和域的复杂性。
我们再深入一点!RNNs!
RNN Cell
根据设计,RNNs 有两个输入,一个是它们看到的当前示例,另一个是以前输入的表示。因此,时间步长 t 处的输出取决于当前输入以及时间 t-1 处的输入。这就是为什么他们在面对与序列相关的任务时表现得更好。顺序信息保存在网络的隐藏状态中,并在下一个实例中使用。
由 RNNs 组成的编码器将序列作为输入,并在序列的末尾生成最终嵌入。然后将它发送到解码器,解码器使用它来预测序列,在每次连续预测之后,它使用前一个隐藏状态来预测序列的下一个实例。
Encoder-Decoder Model for Seq2Seq Modelling
缺点: 输出序列严重依赖编码器最终输出中隐藏状态定义的上下文,使得模型处理长句具有挑战性。在长序列的情况下,初始上下文很可能在序列结束时丢失。
解决方案: Bahdanau 等人,2014 和 Luong 等人,2015 论文介绍了一种称为“注意力”的技术,它允许模型在输出序列的每个阶段关注输入序列的不同部分,从而允许上下文从头到尾得到保留。
现在我引起你的注意了!;P
简而言之,由于问题是编码器末端的单个隐藏状态向量是不够的,我们发送与输入序列中实例数量一样多的隐藏状态向量。这是新的表现形式:
Seq2Seq with Attention — incomplete
听起来很简单,不是吗?让我们引入更多的复杂性。解码器到底是如何使用隐藏状态向量集的?到目前为止,这两个模型之间的唯一区别是在解码阶段引入了所有输入实例的隐藏状态。
创建基于注意力的模型的另一个有价值的补充是上下文向量。这是为输出序列中的每个时间实例生成的。在每一步,上下文向量是输入隐藏状态的加权和,如下所示:
Context Vector
但是如何在预测中使用上下文向量呢?而权重 a1,a2,a3 又是如何决定的呢?让我们一次解决一个问题,先解决一个简单的问题——上下文向量。
所生成的上下文向量通过级联与隐藏状态向量相结合,并且这个新的注意力隐藏向量用于预测该时刻的输出。注意,这个注意力向量是为输出序列中的每个时间实例生成的,并且现在取代了隐藏状态向量。
Attention hidden state
现在我们到了谜题的最后一块,注意力得分。
同样,简单地说,这些是另一个神经网络模型比对模型的输出,该模型最初与 seq2seq 模型联合训练。对齐模型对输入(由其隐藏状态表示)与先前输出(由注意力隐藏状态表示)的匹配程度进行评分,并对每个输入与先前输出进行匹配。然后对所有这些分数取一个最大值,得到的数字就是每个输入的注意力分数。
Attention scoring
因此,我们现在知道输入的哪一部分对于输出序列中每个实例的预测是最重要的。在训练阶段,模型已经学会了如何将各种实例从输出序列对齐到输入序列。下面是一个机器翻译模型的示例,以矩阵形式显示。注意,矩阵中的每个条目都是与输入和输出序列相关联的注意力分数。
French to English conversion. Notice how the model weighted the input sequence while outputing European Economic Area”
所以现在我们有了最终完整的模型
Seq2Seq Attention Based Model
如我们所见,我们开始使用的黑盒现在变成了白色。以下是图片摘要:
我希望你觉得这很有用并且容易理解。如果有任何更正或任何形式的反馈,我希望收到您的来信。请在这里评论,让我知道。
参考资料:
[## 可视化神经机器翻译模型(Seq2seq 模型的机制,注意)
翻译:中文(简体),韩文观察:麻省理工学院的深度学习艺术讲座引用此贴…
jalammar.github.io](https://jalammar.github.io/visualizing-neural-machine-translation-mechanics-of-seq2seq-models-with-attention/) [## LSTMs 和递归神经网络初学者指南
数据只能反向理解;但它必须向前看。-索伦·克尔凯郭尔,日记内容实际上…
skymind.ai](https://skymind.ai/wiki/lstm) [## 通过联合学习对齐和翻译的神经机器翻译
神经机器翻译是最近提出的机器翻译方法。不同于传统的统计…
arxiv.org](https://arxiv.org/abs/1409.0473) [## 张量流/nmt
TensorFlow 神经机器翻译教程。通过在…上创建帐户,为 tensorflow/nmt 的发展做出贡献
github.com](https://github.com/tensorflow/nmt) [## 在翻译中发现:谷歌翻译中更准确、流畅的句子
在 10 年里,谷歌翻译已经从仅仅支持几种语言发展到 103 种,连接陌生人,达到…
博客.谷歌](https://blog.google/products/translate/found-translation-more-accurate-fluent-sentences-google-translate/) [## Seq2seq 关注自我关注:第 1 部分
Seq2seq 与注意模型
medium.com](https://medium.com/@bgg/seq2seq-pay-attention-to-self-attention-part-1-d332e85e9aad) [## 基于注意力的神经机器翻译的有效方法
一种注意力机制最近被用于通过选择性地聚焦于……来改善神经机器翻译(NMT)
arxiv.org](https://arxiv.org/abs/1508.04025) [## 为什么神经元或卷积的数量选择 2 的等幂?
感谢您为 Data Science Stack Exchange 贡献答案!请务必回答问题。提供…
datascience.stackexchange.com](https://datascience.stackexchange.com/questions/16416/why-the-number-of-neurons-or-convolutions-chosen-equal-powers-of-two)
Sutskever,Ilya,Oriol Vinyals 和 Quoc V. Le。"用神经网络进行序列对序列学习."神经信息处理系统的进展。2014.
使用统计机器翻译的 RNN 编码解码器学习短语表达 arXiv 预印本 arXiv:1406.1078 (2014)。
https://www . guide light . com . au/the-guide light-psychology-blog/?类别=生活+教练
DBSCAN 算法:Python Scikit 的完整指南和应用-学习
聚类空间数据库
Density Based Clustering ? (Picture Credit: Adil Wahid)
我打算在这篇文章中讨论的是—
- DBSCAN 算法步骤,遵循 Martin Ester 等人的原始研究论文[1]
- 直接密度可达点的关键概念,用于划分集群的核心点和边界点。这也有助于我们识别数据中的噪声。
- 使用 python 和 scikit 的 DBSCAN 算法应用示例——通过根据每年的天气数据对加拿大的不同地区进行聚类来学习。学习使用一个奇妙的工具-底图,通过 python 在地图上绘制 2D 数据。所有的代码(用 python 写的),图片(用 Libre Office 制作的)都可以在 github 中找到(文章末尾给出了链接)。
在处理不同密度、大小和形状的空间聚类时,检测点的聚类可能是具有挑战性的。如果数据包含噪声和异常值,任务会更加复杂。为了处理大型空间数据库,Martin Ester 和他的合著者提出了带噪声的应用程序的基于密度的空间聚类 (DBSCAN),这仍然是引用率最高的科学论文之一。3 使用 Ester 等人的算法的主要原因是
1.它需要最少的领域知识。
2.它可以发现任意形状的星团。
3.适用于大型数据库,即样本数量超过几千个。
1.DBSCAN 算法中的定义:
为了更详细地理解 DBSCAN,让我们深入了解一下。DBS can 算法的主要概念是定位被低密度区域彼此隔开的高密度区域。 那么,我们如何测量一个地区的密度呢?以下是两个步骤—
- 点 p 处的密度:从点 P 开始半径为 Eps (ϵ) 的圆内的点数。
- 密集区域:对于簇中的每个点,半径为ϵ的圆至少包含最小数量的点( MinPts )。
数据库 D 中的点 P 的ε邻域被定义为(遵循 Ester 等人的定义)
N § = {q ∈ D | dist(p,q) ≤ ϵ} …(1)
根据密集区域的定义,如果|N §|≥ MinPts,则一个点可以被分类为 核心点 *。*顾名思义,核心点通常位于集群内部。一个 边界点 在其ϵ-neighborhood (N)内的个数少于 MinPts,但它位于另一个核心点的邻域内。 噪声 *是既不是核心点也不是边界点的任意数据点。*见下图更好理解。
Core and Border Points in a Database D. Green data point is Noise. (Source: Re-created by Author, Original Reference [2])
这种方法的一个问题是,边界点的ϵ-neighborhood 包含的点的数量明显少于核心点。由于 MinPts 是算法中的一个参数,将其设置为较低的值以包括聚类中的边界点会导致消除噪声的问题。这里出现了密度可达和密度连接点的概念。
直接密度可达:数据点 a 是从点 b 直接密度可达,如果—
- | N(b)|≥min pts;即 b 是核心点。
- a ∈ N(b)即 a 在 b 的ε邻域内
考虑到边界点和核心点,我们可以理解直接密度可达的概念是不对称的,因为即使核心点落在边界点的ε邻域中,边界点也没有足够的 *MinPts,*因此不能满足这两个条件。
**密度可达:**点 a 是从 b 点相对于ϵ和 MinPts 可达的密度,如果—
密度可达本质上是传递的,但是,就像直接密度可达一样,它是不对称的。
**密度连通:**可能存在这样的情况,当两个边界点将属于同一个聚类,但是它们不共享一个特定的核心点,那么我们说它们是密度连通的,如果存在一个公共核心点,从该公共核心点这些边界点是密度可达的。如你所知,密度连接是对称的。Ester 等人论文中的定义如下—
“点 a 是相对于ϵ和 MinPts 连接到点 b 的密度,如果有一个点 c 使得 a 和 b 都是从 c w.r.t .到ϵ和 MinPts 的可达密度。”
Two border points a, b are density connected through the core point c. Source: Created by Author
2.DBSCAN 算法的步骤:
有了上面的定义,我们可以按如下步骤进行 DBSCAN 算法—
- 该算法从没有被访问过的任意点开始,并且从ϵ参数中检索其邻域信息。
- 如果该点包含ϵ邻域内的 MinPts ,则集群形成开始。否则该点被标记为噪声。稍后可以在不同点的ϵ邻域内找到该点,因此可以使其成为聚类的一部分。密度可达和密度连接点的概念在这里很重要。
- 如果发现一个点是核心点,那么ϵ邻域内的点也是聚类的一部分。因此,在ϵ邻域内找到的所有点都将被添加,如果它们也是核心点的话,还有它们自己的ϵ邻域。
- 上述过程继续,直到完全找到密度连接的聚类。
- 该过程从新的点重新开始,该新的点可以是新群的一部分或者被标记为噪声。
从上面的定义和算法步骤,你可以猜出DBS can 算法的两个最大缺点。
- 如果数据库中的数据点形成了不同密度的聚类,那么 DBSCAN 无法很好地对数据点进行聚类,因为聚类取决于ϵ和 MinPts 参数,不能为所有聚类单独选择它们。
- 如果数据和特征没有被领域专家很好地理解,那么设置ϵ和 MinPts 可能会很棘手,可能需要用不同的ϵ和 MinPts 值进行几次迭代比较。
一旦基本原理清楚了一点,现在将看到一个使用 Scikit-learn 和 python 的 DBSCAN 算法的例子。
3.带 Scikit-Learn 的 DBSCAN 算法示例:
为了查看 DBSCAN 算法的一个实际例子,我使用了加拿大 2014 年的天气数据来对气象站进行聚类。首先让我们加载数据—
数据帧由 1341 行和 25 列组成,为了理解列名代表什么,让我们看看下面最重要的特性
因为,我想使用不同的温度作为第一次尝试聚类气象站的几个主要特征,首先,让我们删除“平均温度™”、“最低温度(Tn)”和“最高温度(Tx)”列中包含 NaN 值的行。
在删除上述列中包含 NaN 值的行后,我们剩下 1255 个样本。尽管这几乎是 7%的数据损失,但是考虑到我们仍然有超过 1000 个样本,让我们继续进行聚类。
由于我们要进行空间聚类,并在地图投影中查看聚类,以及不同的温度(“Tm”、“Tn”、“Tx”)、“Lat”、“long”也应作为特征。在这里,我使用了底图工具包,这是一个用于绘制 2D 数据的库,以便用 Python 可视化地图。如底图文档中所述— *“底图本身不进行任何绘制,但提供了将坐标转换为 25 种不同地图投影之一的工具”。*底图的一个重要属性是-使用参数纬度/经度(以度为单位,如我们的数据框中所示)调用底图类实例,以获取 x/y 地图投影坐标。
让我们使用底图绘制气象站,以便熟悉它。从导入必要的库开始
from mpl_toolkits.basemap import Basemap
import matplotlib
from PIL import Image
import matplotlib.pyplot as plt
from pylab import rcParams
%matplotlib inline
rcParams['figure.figsize'] = (14,10)
我们现在准备调用底图类—
让我简单解释一下代码块。我开始使用墨卡托投影 ( 投影=‘merc’)、低分辨率(分辨率=‘l’)调用一个底图类实例,地图域的边界由 4 个参数给出 llcrnrlon、llcrnrlat、urcrnrlon、urcrnrlat、,其中 llcrnrlon 表示所选地图域左下角的经度,依此类推。绘制海岸线,绘制国家顾名思义,绘制遮罩绘制高分辨率陆海遮罩图像,将陆地和海洋颜色指定为橙色和天蓝色。使用下面的命令将纬度和经度转换为 x/y 地图投影坐标—
xs, ys = my_map(np.asarray(weather_df.Long), np.asarray(weather_df.Lat))
这些地图投影坐标将用作要素,以便在空间上将数据点与温度一起聚类。首先让我们看看下面的气象站—
Weather Stations in Canada, plotted using Basemap. Source: Author
3.1.对天气数据进行聚类(温度和坐标作为特征)
对于聚类数据,我遵循了sci kit-DBS can的学习演示中所示的步骤。
选择温度(’ Tm ‘,’ Tx ‘,’ Tn ‘)和坐标的 x/y 映射投影(’ xm ‘,’ ym ')作为特征,并将ϵ和 MinPts 分别设置为 0.3 和 10,给出 8 个唯一的聚类(噪声标记为-1)。您可以随意更改这些参数来测试集群会受到多大的影响。
让我们使用底图来可视化这些集群—
8 Unique Clusters in Canada Based on Few Selected Features in the Weather Data. ϵ and MinPts set to 0.3 and 10 Respectively. Source: Author
最后,我将降水(“p”)包含在特征中,并重复相同的聚类步骤,将ϵ和 MinPts 设置为 0.5 和 10。我们看到了与以前的聚类的一些不同,因此它给了我们一个思路,当我们缺乏领域知识时,即使使用 DBSCAN 也可以对无监督数据进行聚类。
4 Unique Clusters in Canada Based on Selected Features (now included precipitation compared to previous case) in the Weather Data. ϵ and MinPts set to 0.5 and 10 Respectively. Source: Author.
您可以尝试重复这个过程,包括一些更多的功能,或者,改变聚类参数,以获得更好的整体知识。
最后,我们介绍了 DBSCAN 算法的一些基本概念,并测试了该算法对加拿大气象站的聚类。详细代码和所有图片将在我的 Github 中提供。可以与 K-均值聚类进行直接比较,以便更好地理解这些算法之间的差异。希望这将有助于您开始使用一种最常用的聚类算法来处理无监督问题。
分享你的想法和主意,保持坚强。干杯!
页(page 的缩写)s:另一个无监督聚类方法高斯混合和详细的期望最大化算法在另一个帖子中讨论。
参考文献:
[1] “一种基于密度的带噪声大型空间数据库聚类发现算法”;马丁·埃斯特等人 KDD-96 会议录。
[2]基于密度的聚类方法;高,j。布法罗大学副教授。演示链接。
[3] 链接到 Github !
如果你对更深入的基础机器学习概念感兴趣,可以考虑加盟 Medium 使用 我的链接 。你不用额外付钱,但我会得到一点佣金。感谢大家!!
DCGANs —使用 Tensorflow 和 Keras 生成狗的图像
基于 Kaggle 的生殖狗比赛(2019)
DCGAN Dog Generation over epochs (~8 hours of runtime on Kaggle)
这篇文章是一个关于 DCGANs 有效性背后的基本思想的教程,以及一些提高他们性能的方法/技巧。这些方法都是我在 Kaggle 的 生财狗大赛 期间的经验。教程也有,要么在我原来的 内核 上笔记本格式,要么在GitHub上。
要在本地或使用 Colab 运行这个例子,您将需要一个 Kaggle 帐户,以便检索其 API 密钥并使用所提供的数据集。完整教程可在 这里 。
生成对抗网络
与大多数流行的神经网络架构不同, GANs 被训练来同时解决两个问题——辨别(有效地将真实图像与虚假图像分开)和**“真实的”虚假数据生成**(有效地生成被认为是真实的样本)。正如我们所看到的,这些任务是完全对立的,但是如果我们把它们分成不同的模型,会发生什么呢?
嗯,这些模型的通称是发生器(G) 和鉴别器(D) ,被认为是甘斯理论背后的构建模块。
发生器网络将简单的随机噪声 N 维向量作为输入,并根据学习的目标分布对其进行转换。其输出也是 N 维的。另一方面,鉴别器模拟概率分布函数(类似于分类器)并输出输入图像是真实还是虚假的概率**【0,1】**。考虑到这一点,我们可以定义生成任务的两个主要目标:
1。训练 G 使 D 的最终分类误差最大化。(使得生成的图像被感知为真实)。
2。训练 D 以最小化最终分类误差。(以便正确区分真实数据和虚假数据)。
为了实现这一点,在反向传播期间, G 的权重将使用梯度上升进行更新,以使误差最大化,而 D 将使用梯度下降将其最小化。
GAN inputs and outputs — (note that the two networks don’t use the true distribution of images directly during training but instead use each other’s outputs to estimate their performance)
那么我们如何定义一个损失函数来估计两个网络的累积性能呢?嗯,我们可以使用绝对误差来估计 D 的误差,然后我们可以对 G 重复使用相同的函数,但要最大化:
Mean Absolute Error — the distance between the real and fake distributions of images
在这种情况下, p_t 代表图像的真实分布,而 p_g 则是由 G 创建的分布。
我们可以观察到这个理论是基于强化学习的一些关键概念。它可以被认为是一个两个玩家的极大极小游戏,其中两个玩家相互竞争,从而在各自的任务中逐步提高。
我们看到了甘斯理论背后的基本思想。现在让我们更进一步,通过应用来自卷积神经网络的思想和方法来学习DC gan是如何工作的。
深度卷积生成对抗网络
DC gan利用了CNN的一些基本原理,并因此成为实践中使用最广泛的架构之一,这是因为它们收敛速度快,并且还因为它们可以非常容易地适应更复杂的变体(使用标签作为条件,应用残差块等等)。以下是 DCGANs 解决的一些更重要的问题:
- D 是这样创建的,它基本上解决了一个有监督的图像分类任务。(对于这种情况狗还是不狗)
- GAN 学习的过滤器可用于在生成的图像中绘制特定对象。
- G 包含可以学习对象非常复杂的语义表示的矢量化属性。
以下是创建稳定的 DCGAN 时要考虑的一些核心准则,与标准的 CNN (摘自官方论文)相对照:
- 用步长卷积代替池函数。(这允许 D 学习其自己的空间下采样和 G 其各自的上采样,而不会给模型增加任何偏差)
- 使用 BatchNorm (它通过标准化每个单元的输入来稳定学习,使平均值和单元方差为零,这也有助于创建更健壮的深度模型,而不会出现梯度发散)
- 避免使用全连接隐藏层(非输出)。(这方面的例子是全局平均池,这似乎会影响收敛速度)
- 对于 G-使用 ReLU 激活和 Tanh 进行输出。(当您将图像作为输出时,tanh 通常是更优选的激活,因为它的范围为[-1,1])
- 对于 D-使用泄漏激活(以及输出概率的 sigmoid 函数)。(这是经过经验测试的,似乎适用于更高分辨率的建模)
下面是一个 DCGAN 发生器的最标准结构:
DCGAN Generator structure
正如我们所看到的,它的初始输入只是一个 (1,100) 噪声向量,它通过 4 个卷积层进行上采样,并以 2 的步长产生大小为(64,64,3)的结果 RGB 图像。为了实现这一点,输入向量被投影到 1024 维的输出上,以匹配第一个 Conv 层的输入,我们将在后面看到更多。
标准的鉴别器是什么样的?好吧,关于你所期望的,让我们来看看:
DCGAN Discriminator structure
这一次我们有一个 (64,64,3) 的输入图像,与 G 的输出相同。我们将它传递给 4 标准下采样 Conv 层,再次以 2 的步幅。在最终的输出层中,图像被展平为一个向量,该向量通常被馈送给一个 sigmoid 函数,然后该函数输出该图像的 D 的预测**(一个表示概率在[0,1] — dog = 1 或 no dog = 0)** 范围内的单个值)。
好了,现在你看到了 GANs 和 DCGANs 背后的基本思想,所以现在我们可以继续使用 Tensorflow 和 Keras :)生成一些狗。
图像预处理和 EDA(探索性数据分析)
在我们继续创建甘模型之前,让我们先快速浏览一下我们将要使用的斯坦福狗数据集。因为我们也有每张图片的注释,我们可以用它们将每张狗图片映射到它各自的品种。为此,我们可以首先创建一个字典,将文件名中的品种代码映射到实际的品种名称。
接下来,我将使用 OpenCV 作为快速函数来读取图像并将其转换为 RGB 。
如果我们查看数据集,我们可以看到每个注释文件夹都包含一个 xml 文件列表。这些文件与特定的图像相关联,包含非常有用的信息,主要是图像中每只狗周围的边界框。也有图像中有多只狗,这使我们能够准确地裁剪它们,并制作一个只包含单身狗的数据集图像。
在这里,我们可以利用 xml 库来创建一个树,并为该注释找到相关的元素。对于每个对象,我们可以提取边界框坐标,裁剪图像,并根据结果图像宽度通过收缩或扩展来标准化裁剪。最后,我们将图像保存在一个 numpy 数组中。
Use ET to find the annotations of each dog in the image
for o in objects:
bndbox = o.find('bndbox')
xmin = int(bndbox.find('xmin').text)
ymin = int(bndbox.find('ymin').text)
xmax = int(bndbox.find('xmax').text)
ymax = int(bndbox.find('ymax').text) .. Add some margins and adjust the width .. # Crop the image
img_cropped = img[ymin:ymin+w, xmin:xmin+w, :] # [h,w,c] .. Interpolation step ..
# Resize the image
img_cropped = cv2.resize(img_cropped, (image_width, image_height), interpolation=interpolation)
# Save the images and labels
dog_images_np[curIdx,:,:,:] = np.asarray(img_cropped)
dog_breed_name = dog_breed_dict[dog_ann.split('_')[0]]
breeds.append(dog_breed_name)
curIdx += 1
return dog_images_np, breeds
加载这些功能大约需要 2-3 分钟。
通过试验,我们可以得出只有单条狗的结果图像是 22125 ,因此我们可以指定 numpy 数组的确切大小。有 120 不同的狗品种。
现在我们有了要素和标注,我们可以在正方形网格中绘制它们,以查看作物的外观并确保它们的标注正确。
def plot_features(features, labels, image_width=image_width, image_height=image_height, image_channels=image_channels,
examples=25, disp_labels=True):
if not math.sqrt(examples).is_integer():
print('Please select a valid number of examples.')
return
imgs = []
classes = []
for i in range(examples):
rnd_idx = np.random.randint(0, len(labels))
imgs.append(features[rnd_idx, :, :, :])
classes.append(labels[rnd_idx]) fig, axes = plt.subplots(round(math.sqrt(examples)), round(math.sqrt(examples)),figsize=(15,15),
subplot_kw = {'xticks':[], 'yticks':[]},
gridspec_kw = dict(hspace=0.3, wspace=0.01))
for i, ax in enumerate(axes.flat):
if disp_labels == True:
ax.title.set_text(classes[i])
ax.imshow(imgs[i])
注意,我们需要归一化像素值,以确保狗被正确绘制。
plot_features(dog_images_np / 255., breeds, examples=25, disp_labels=True)
Visualizing the image features
模型超参数列表
这里我们有一个超参数的完整列表,您可以调整并尝试改进该模型。我主要是从研究论文中收集这些值,并对它们进行了一点调整,这就是我最终得到的结果。以下是你可以尝试的一些事情:
- 样本大小 —特征的数量
- 批量大小 — 64 或 32 可以提高性能,但是计算量很大,并且只能运行少量时期的模型
- 权重初始标准值和平均值 —这些值来自研究论文,似乎可以稳定模型训练
- 泄漏 ReLU 斜率—D激活的阈值看起来也很稳定
- 缩小因子和比例因子——设置使得 G 的噪声向量可以被整形为(4,4,512),其他组合也可能有效
- 漏失 —漏失层的数量、位置和速率可以提高性能。
- 学习率和学习率衰减 —对模型收敛非常重要,很难精确调整, G 和 D 可以有不同的学习率。
- 噪声矢量形状——通常 128 或 100 似乎就足够了
创建影像数据集
现在让我们使用我们的 numpy 特性数组来构造一个 Tensorflow dataset 对象。首先,我们可以将数据类型转换为 float32 ,这总是有助于保留一些内存。
dog_features_tf = tf.cast(dog_images_np, 'float32')
我们还可以将数据扩充应用到我们的数据集。这包括随机的水平翻转,在随机区域缩放和裁剪图像。在这些方法中,我发现只有第一种方法对向数据集添加更多的方差有点用,因为其他方法会引入很多噪声。
因此,在这种情况下,我们数据集中的图像将有 50% 的机会从左向右翻转。
def flip(x: tf.Tensor) -> (tf.Tensor):
x = tf.image.random_flip_left_right(x)
return x
现在,我们可以使用 Tensorflow 来创建数据集,方法是将它混洗,应用一些增强,最后将它分成指定批处理大小的批处理。
dog_features_data = tf.data.Dataset.from_tensor_slices(dog_features_tf).shuffle(sample_size).map(flip).batch(batch_size, drop_remainder=True)
标准化技术
在我们实际制作生成器之前,让我们来看看一些可以逐渐加快 DCGAN 收敛速度的规范化。
重量初始化
其中一种方法是权重初始化。原来训练稳定的甘斯还是蛮重要的。首先,模型重量需要在零中心上稍微增加标准值 (0.02)。这在训练期间稳定了 D 和 G ,并防止模型梯度消失或爆炸。这是每种情况下的关键一步,我们必须在模型中使用随机变量(随机噪声向量)。
下面是一个关于权重初始化如何严重影响神经网络学习过程的例子。
The impact of weight initialization on model training
我们也可以使用 Keras 应用截尾正态分布,这将丢弃超过平均值 2 个标准偏差的值。这也许可以在训练期间消除一些异常点。
weight_initializer = tf.keras.initializers.TruncatedNormal(stddev=weight_init_std, mean=weight_init_mean, seed=42)
光谱归一化
谱归一化是一种新型的权重初始化,专门针对 GANs 设计的,似乎可以进一步稳定模型训练(你可以从这篇论文中读到更多)。关于光谱归一化的更详细的解释以及它为什么工作也值得看看这篇文章,它有非常直观的例子。
我们网络中单个权重的频谱归一化可定义如下:
Applying Spectral Normalization on a single weight
这里 u 和 v 是相同大小的简单随机向量。对于每个学习步骤,它们被用来对特定权重执行所谓的幂迭代操作,并且它被证明比简单地惩罚梯度在计算上高效得多。
之后,在反向传播步骤中,我们使用 WSN(W) 代替 W 来更新权重。
对于这个项目,我将重用一些由 IShengFang ( 官方代码)实现的自定义 Keras 图层,在 Conv 和密集图层之上应用光谱归一化。
这里还有一个关于光谱归一化效果的好例子:
DCGAN training with SN
DCGAN training with standard layers
让我们也在 Keras 中定义一些模板层,以便我们稍后可以更容易地创建 G 和 D 。各层的标准模式将是:
TP _ conv _ Block =[(conv(SN)2d transpose(上采样))->(batch norm)->(ReLU)]
conv _ 布洛克= [(Conv(SN)2D(下采样))->(batch norm)->(leaky relu)】
对于本例,我将在 G 中使用标准Conv2D 转置模块,在 d 中使用频谱归一化 conv 2d 层
def transposed_conv(model, out_channels, ksize, stride_size, ptype='same'):
model.add(Conv2DTranspose(out_channels, (ksize, ksize),
strides=(stride_size, stride_size), padding=ptype,
kernel_initializer=weight_initializer, use_bias=False))
model.add(BatchNormalization())
model.add(ReLU())
return model
def convSN(model, out_channels, ksize, stride_size):
model.add(ConvSN2D(out_channels, (ksize, ksize), strides=(stride_size, stride_size), padding='same',
kernel_initializer=weight_initializer, use_bias=False))
model.add(BatchNormalization())
model.add(LeakyReLU(alpha=leaky_relu_slope))
#model.add(Dropout(dropout_rate))
return model
发电机
我们终于可以定义我们的生成器了。模型结构主要基于官方的 DCGAN 论文,做了一些我认为对性能有益的调整。总体结构如下:
【输入(128,1)——>密集(2048,)——>重塑(4,4,128)——>TP _ conv _ Block(深度=512,K = 5×5,S = 1×1)——>Dropout(0.5)->TP _ conv _ Block(深度=256,K = 5×5,S = 2×2)->Dropout(0.5)->TP _ conv _ Block(深度=128,K
def DogGenerator():
model = Sequential()
model.add(Dense(image_width // scale_factor * image_height // scale_factor * 128,
input_shape=(noise_dim,), kernel_initializer=weight_initializer))
#model.add(BatchNormalization(epsilon=BN_EPSILON, momentum=BN_MOMENTUM))
#model.add(LeakyReLU(alpha=leaky_relu_slope))
model.add(Reshape((image_height // scale_factor, image_width // scale_factor, 128)))
model = transposed_conv(model, 512, ksize=5, stride_size=1)
model.add(Dropout(dropout_rate))
model = transposed_conv(model, 256, ksize=5, stride_size=2)
model.add(Dropout(dropout_rate))
model = transposed_conv(model, 128, ksize=5, stride_size=2)
model = transposed_conv(model, 64, ksize=5, stride_size=2)
model = transposed_conv(model, 32, ksize=5, stride_size=2)
model.add(Dense(3, activation='tanh', kernel_initializer=weight_initializer))
return model
鉴别器
鉴别器相对更容易实现,因为它基本上是一个小型的 CNN 两类分类器。我们可以选择是否应用光谱归一化并观察性能效果。对于这个例子,我将尝试只在 D 中应用 SN 。下面是 D 的结构:
【输入(128,128,3)——>conv(SN)2D(深度=64,K=5x5,S=1x1,相同)——>leaky relu——>conv _ Block(深度=64,K=5x5,S = 2 x2)——>conv _ Block(深度=128,K=5x5,S = 2 x2)——>conv _ Block(深度=256,K=5x5,S = 2 x2)——【T57
还要注意,所有的 Conv 和密集层都用上面定义的截断正态分布初始化。另一件事是偏差项从 Conv 层中移除,这也稍微稳定了模型。
def DogDiscriminator(spectral_normalization=True):
model = Sequential()
if spectral_normalization:
model.add(ConvSN2D(64, (5, 5), strides=(1,1), padding='same', use_bias=False,
input_shape=[image_height, image_width, image_channels],
kernel_initializer=weight_initializer))
#model.add(BatchNormalization(epsilon=BN_EPSILON, momentum=BN_MOMENTUM))
model.add(LeakyReLU(alpha=leaky_relu_slope))
#model.add(Dropout(dropout_rate))
model = convSN(model, 64, ksize=5, stride_size=2)
#model = convSN(model, 128, ksize=3, stride_size=1)
model = convSN(model, 128, ksize=5, stride_size=2)
#model = convSN(model, 256, ksize=3, stride_size=1)
model = convSN(model, 256, ksize=5, stride_size=2)
#model = convSN(model, 512, ksize=3, stride_size=1)
#model.add(Dropout(dropout_rate))
model.add(Flatten())
model.add(DenseSN(1, activation='sigmoid'))
else:
...
return modeldog_discriminator = DogDiscriminator(spectral_normalization=True)
标签平滑
一种可以在训练期间应用的正则化方法被称为标签平滑。这实际上是防止了 D 在其预测中过于自信或过于自信。如果 D 变得过于确定在特定的图像中有一只狗,那么 G 可以利用这个事实,并且持续地开始只生成那个种类的图像,并且依次停止改进。我们可以通过将负类的类标签设置在范围**【0,0.3】内,将正类的类标签设置在范围【0.7,1】**内来解决这个问题。
这将防止总体概率非常接近两个阈值。
# Label smoothing -- technique from GAN hacks, instead of assigning 1/0 as class labels, we assign a random integer in range [0.7, 1.0] for positive class
# and [0.0, 0.3] for negative class
def smooth_positive_labels(y):
return y - 0.3 + (np.random.random(y.shape) * 0.5)
def smooth_negative_labels(y):
return y + np.random.random(y.shape) * 0.3
将噪声引入标签
这种技术也被称为实例噪声。通过给标签增加少量误差(假设 5%),这往往会使真实分布和预测分布更加分散,从而开始相互重叠。这反过来使得在学习过程中拟合生成图像的定制分布更加容易。
下面是一个很好的例子,展示了使用这些技术时这两个发行版的样子:
The impact of Insance Noise and Label smoothing on the real and fake distributions of images
下面是我们如何实现实例噪声:
# randomly flip some labels
def noisy_labels(y, p_flip):
# determine the number of labels to flip
n_select = int(p_flip * int(y.shape[0]))
# choose labels to flip
flip_ix = np.random.choice([i for i in range(int(y.shape[0]))], size=n_select)
op_list = []
# invert the labels in place
#y_np[flip_ix] = 1 - y_np[flip_ix]
for i in range(int(y.shape[0])):
if i in flip_ix:
op_list.append(tf.subtract(1, y[i]))
else:
op_list.append(y[i])
outputs = tf.stack(op_list)
return outputs
优化者
这个任务的最佳验证优化算法是 Adam ,两个模型的标准学习率为 0.0002 ,beta 为 0.5 。
generator_optimizer = tf.train.AdamOptimizer(learning_rate=lr_initial_g, beta1=0.5)
discriminator_optimizer = tf.train.AdamOptimizer(learning_rate=lr_initial_d, beta1=0.5)
定义损失函数
最近优化 GANs 的另一个新趋势是应用相对论损失函数,而不是标准损失函数。这些函数测量真实数据比生成的数据更“真实”的概率。其中比较流行的相对论函数选择有 RaLSGAN(相对论平均最小二乘法)、RaSGAN(相对论平均标准)和 RaHinge(相对论铰链损耗)。
但在这一切之前,让我们先定义一下标准甘损失:
正如我们所观察到的,这基本上是用于分类任务的标准二元交叉熵损失或者真实分布和生成分布之间的逻辑损失。在张量流中,这可以定义如下:
相比之下,这里是一个 **RSGAN(相对论标准)**损耗的样子:
在这种情况下,任务是不同的,测量真实® 和虚假(f) 数据分布之间的相似性。RSGAN 在 D(x) = 0.5 时达到最优点(即C(xr)=C(xf))。有许多相对论损失函数变体,它们都包含不同的方法来测量这种相似性。在这个项目中,我尝试了似乎拥有最佳记录的 MIFID 得分 (RaLSGAN、RaSGAN 和 RaHinge) 的 3 。随意给自己尝试不同的损耗,看看是否能提高性能;).
这里有一个最常用的列表:
Standard and Relativistic GAN losses
在针对这个特定问题的多次试验中,我没有发现通过切换到相对论性损耗来提高性能,所以我决定坚持使用标准的 GAN 损耗函数,因为它更容易估计,尽管在某些情况下,这些损耗确实可以加快模型的收敛。
这里是应用了标签平滑和实例噪声的鉴别器损失函数的样子。它基本上是两个子损失的总和**(假的——根据 G 的图像,真实的——根据实际的训练图像)**。
def discriminator_loss(real_output, fake_output, loss_func, apply_label_smoothing=True, label_noise=True):
if label_noise and apply_label_smoothing:
real_output_noise = noisy_labels(tf.ones_like(real_output), 0.05)
fake_output_noise = noisy_labels(tf.zeros_like(fake_output), 0.05)
real_output_smooth = smooth_positive_labels(real_output_noise)
fake_output_smooth = smooth_negative_labels(fake_output_noise)
if loss_func == 'gan':
real_loss = cross_entropy(tf.ones_like(real_output_smooth), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output_smooth), fake_output) else:
... other loss function variants loss = fake_loss + real_loss
return loss
而生成器损失函数**(应用了标签平滑)只是一个标准的物流损失**:
def generator_loss(real_output, fake_output, loss_func, apply_label_smoothing=True):
if apply_label_smoothing:
fake_output_smooth = smooth_negative_labels(tf.ones_like(fake_output))
if loss_func == 'gan':
return cross_entropy(tf.ones_like(fake_output_smooth), fake_output)
else:
... other loss function variants return loss
主训练循环
让我们也确定用于训练的时期的数量和提供给生成器用于可视化中间结果的图像的数量。
EPOCHS = 280
num_examples_to_generate = 64
seed = tf.random.normal([num_examples_to_generate, noise_dim])
DCGAN 的一个训练步骤由三个标准步骤组成:
- 前进道具 — G 制造一批假像;这和一批真实图像一起被传送到 D 。
- 计算 G 和 D 的损失函数。
- 反投影 —计算 G 和 D 的梯度,优化权重。
def train_step(images, loss_type='gan'):
noise = tf.random.normal([batch_size, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = dog_generator(noise, training=True)
real_output = dog_discriminator(images, training=True)
fake_output = dog_discriminator(generated_images, training=True)
gen_loss = generator_loss(real_output, fake_output, loss_type, apply_label_smoothing=True)
disc_loss = discriminator_loss(real_output, fake_output, loss_type,
apply_label_smoothing=True, label_noise=True)
gradients_of_generator = gen_tape.gradient(gen_loss, dog_generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, dog_discriminator.trainable_variables)
generator_optimizer.apply_gradients(zip(gradients_of_generator, dog_generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, dog_discriminator.trainable_variables))
return gen_loss, disc_loss
让我们也定义一些函数来直观显示按时期和作为一个整体的模型损失。
def plot_losses(G_losses, D_losses, all_gl, all_dl, epoch):
plt.figure(figsize=(10,5))
plt.title("Generator and Discriminator Loss - EPOCH {}".format(epoch))
plt.plot(G_losses,label="G")
plt.plot(D_losses,label="D")
plt.xlabel("Iterations")
plt.ylabel("Loss")
plt.legend()
ymax = plt.ylim()[1]
plt.show()
plt.figure(figsize=(10,5))
plt.plot(np.arange(len(all_gl)),all_gl,label='G')
plt.plot(np.arange(len(all_dl)),all_dl,label='D')
plt.legend()
#plt.ylim((0,np.min([1.1*np.max(all_gl),2*ymax])))
plt.title('All Time Loss')
plt.show()
我们还可以使用下面的函数来绘制生成图像的网格。
def generate_and_save_images(model, epoch, test_input, rows, cols):
# Notice `training` is set to False.
# This is so all layers run in inference mode (batchnorm).
predictions = model(test_input, training=False)
fig = plt.figure(figsize=(14,14))
for i in range(predictions.shape[0]):
plt.subplot(rows, cols, i+1)
plt.imshow((predictions[i, :, :, :] * 127.5 + 127.5) / 255.)
plt.axis('off')
plt.subplots_adjust(wspace=0, hspace=0)
plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()
为了生成单个测试图像,我们也可以重用相同的方法。
def generate_test_image(model, noise_dim=noise_dim):
test_input = tf.random.normal([1, noise_dim])
# Notice `training` is set to False.
# This is so all layers run in inference mode (batchnorm).
predictions = model(test_input, training=False)
fig = plt.figure(figsize=(5,5))
plt.imshow((predictions[0, :, :, :] * 127.5 + 127.5) / 255.)
plt.axis('off')
plt.show()
评估 GANs
我们还没提到一个甘平时是怎么评价的。大多数使用基准来评估一个甘表现如何的研究论文通常基于所谓的初始分数。这测量输入图像的两个主要特征:
- 品种(例如生成不同类型的狗品种)
- 图像的区别(或质量)
如果两件事都是真的,分数就高。如果其中一个或两个都是错误的,分数将会很低。分数越高越好。这意味着您的 GAN 可以生成许多不同的清晰图像。最低分可能是零分。数学上的最高可能分数是无穷大,尽管实际上可能会出现一个非无穷大的上限。
Inception Score 来源于 Google 的 Inception Network ,是最先进的图像分类深度架构之一。通过将来自我们的 GAN 的图像通过分类器,我们可以测量我们生成的图像的属性。为了产生分数,我们需要计算图像的真实和虚假分布之间的相似性/距离。这是使用KL(kull back–lei bler)散度公式完成的:
这里, P 和 Q 是两个测量分布。在这种情况下,更高的 KL 散度意味着更好的结果——图像的质量是相似的,并且存在各种各样的标签。在相反的情况下,低 KL 偏差可能是由于标签质量低或种类少:
Measuring the performance with KL divergence
弗雷歇起始距离
的一个缺点是如果每个类只生成一个图像,它可能会歪曲性能。为了解决这个问题,我们可以使用 FID(弗雷歇初始距离)。这种方法将前面两种类型的图像定义为具有平均值 μ 和协方差σ(适马)的多元高斯分布。让我们看看这个距离是如何计算的:
这里, x 和 g 代表图像的真假分布,而 Tr 是结果的对角元素之和。
FID 值越低,图像质量和多样性越好。
Example of how the diversity factor impacts the scoring on different datasets
以下是一些有用的注释,说明为什么 FID 是一个好的衡量标准:
- FID 比 is 对噪声更鲁棒。
- 如果模型每类只生成一个图像,距离会很高。因此,FID 是一种更好的图像多样性度量。
- 通过计算训练数据集和测试数据集之间的 FID,我们应该期望 FID 为零,因为两者都是真实图像。(虽然通常会有少量误差)
- FID 和 IS 基于特征提取(特征的有无)。
记忆通知弗雷歇起始距离
以下是 Kaggle 对生殖狗比赛的官方评估工作流程:
There are two stages of evaluation — public and private on different datasets. The MIFID metric is calculated using the standard FID, combined with a Memorization Score.
正如我们所看到的,除了 FID 指标之外,还有一个额外的记忆分数被添加到计算中。这基本上是一个余弦距离公式,用于测量真实(来自私人数据集的图像)和虚假图像之间的相似性。我猜这样做是为了确保提供给评估内核的图像实际上是由 GAN 生成的,而不仅仅是从真实数据集复制或修改的。
谢天谢地, MIFID 赋值器已经被 Kaggle 团队(此处)实现了,我们不用担心这个问题。
图像压缩和保存功能
我将再添加两个,用于压缩最终的 10K 图像以供提交,并生成临时图像以计算训练期间特定时期之间的 MIFID 。
def zip_images(filename='images.zip'):
# SAVE TO ZIP FILE NAMED IMAGES.ZIP
z = zipfile.PyZipFile(filename, mode='w')
for k in range(image_sample_size):
generated_image = dog_generator(tf.random.normal([1, noise_dim]), training=False)
f = str(k)+'.png'
img = np.array(generated_image)
img = (img[0, :, :, :] + 1.) / 2.
img = Image.fromarray((255*img).astype('uint8').reshape((image_height,image_width,image_channels)))
img.save(f,'PNG')
z.write(f)
os.remove(f)
#if k % 1000==0: print(k)
z.close()
print('Saved final images for submission.')
def save_images(directory=OUT_DIR):
for k in range(image_sample_size):
generated_image = dog_generator(tf.random.normal([1, noise_dim]), training=False)
f = str(k)+'.png'
f = os.path.join(directory, f)
img = np.array(generated_image)
img = (img[0, :, :, :] + 1.) / 2.
img = Image.fromarray((255*img).astype('uint8').reshape((image_height,image_width,image_channels)))
img.save(f,'PNG')
#if k % 1000==0: print(k)
print('Saved temporary images for evaluation.')
终于到了实现最终培训功能的时候了,该功能总结了整个过程。这里还有一些我还没有提到的技巧。让我们看看它们是什么。
学习率衰减
这是一个实验,并不总是有助于提高性能,但我不认为它会伤害任何一种方式。这里的想法是,对于每个训练步骤,将学习率降低一个非常小的量,以便稳定训练过程并加速收敛(并逃离局部最小值)。对于这个项目,我使用 Tensorflow 中的 余弦学习率衰减 来降低每decay_step
次迭代的学习率。
处理模式崩溃
除了不收敛和消失和爆炸梯度之外, GANs 有时还会遇到另一个主要问题,叫做模式崩溃。当开始生产有限种类的样品时,就会发生这种情况。下面是一个在 MNIST 数据集上训练的甘的模式折叠的好例子,其中 G 连续地仅产生单个类标签的图像:
G learns to fool D by only generating samples from a single class, which causes the model to lose diversity
我们已经看到了一些可能消除模式崩溃的方法,如标签平滑、实例噪声、权重初始化等等。我们可以在培训中应用的另一种方法叫做体验回放。
体验回放在内存中保存一些最近生成的图像。对于每一次replay_step
迭代,我们在那些先前的图像上训练 D ,以“提醒”先前世代的网络,从而减少在训练期间过度拟合到数据批次的特定实例的机会。在这个例子中,我使用了稍微不同形式的经验重放,在这个意义上,我为每个训练步骤生成一个新的额外图像以存储在一个列表中,而不是从以前的迭代中馈送实际生成的图像,因为在急切执行期间存储数据不是一件容易的任务。
'''
generated_image = dog_generator(tf.random.normal([1, noise_dim]), training=False)
exp_replay.append(generated_image)
if len(exp_replay) == replay_step:
print('Executing experience replay..')
replay_images = np.array([p[0] for p in exp_replay])
dog_discriminator(replay_images, training=True)
exp_replay = []
'''
由于 Kaggle 在运行了大约 7-8 个小时后遇到了内存问题,我决定不使用体验回放。如果您找到了解决方法,请告诉我
训练功能
总而言之,训练过程相当简单。显示中间结果(如图像、损耗)和计算 MIFID 还有其他步骤。在学习过程的最后,我们打印出最终评估和最终图像的较大网格。
display_results = 40
calculate_mifid = 100
replay_step = 50
decay_step = 50
def train(dataset, epochs):
all_gl = np.array([]); all_dl = np.array([])
for epoch in tqdm(range(epochs)):
G_loss = []; D_loss = []
start = time.time()
new_lr_d = lr_initial_d
new_lr_g = lr_initial_g
global_step = 0
for image_batch in dataset:
g_loss, d_loss = train_step(image_batch)
global_step = global_step + 1
G_loss.append(g_loss); D_loss.append(d_loss)
all_gl = np.append(all_gl,np.array([G_loss]))
all_dl = np.append(all_dl,np.array([D_loss]))
if (epoch + 1) % display_results == 0 or epoch == 0:
plot_losses(G_loss, D_loss, all_gl, all_dl, epoch + 1)
generate_and_save_images(dog_generator, epoch + 1, seed, rows=8, cols=8)
if (epoch + 1) % calculate_mifid == 0:
OUT_DIR.mkdir(exist_ok=True)
save_images(OUT_DIR)
evaluator = MiFIDEvaluator(MODEL_PATH, TRAIN_DIR)
fid_value, distance, mi_fid_score = evaluator.evaluate(OUT_DIR)
print(f'FID: {fid_value:.5f}')
print(f'distance: {distance:.5f}')
print(f'MiFID: {mi_fid_score:.5f}')
shutil.rmtree(OUT_DIR)
print('Removed temporary image directory.')
# Cosine learning rate decay
if (epoch + 1) % decay_step == 0:
new_lr_d = tf.train.cosine_decay(new_lr_d, min(global_step, lr_decay_steps), lr_decay_steps)
new_lr_g = tf.train.cosine_decay(new_lr_g, min(global_step, lr_decay_steps), lr_decay_steps)
generator_optimizer = tf.train.AdamOptimizer(learning_rate=new_lr_d, beta1=0.5)
discriminator_optimizer = tf.train.AdamOptimizer(learning_rate=new_lr_g, beta1=0.5)
print('Epoch: {} computed for {} sec'.format(epoch + 1, time.time() - start))
print('Gen_loss mean: ', np.mean(G_loss),' std: ', np.std(G_loss))
print('Disc_loss mean: ', np.mean(D_loss),' std: ', np.std(D_loss))
# Generate after the final epoch and repeat the process
generate_and_save_images(dog_generator, epochs, seed, rows=8, cols=8)
checkpoint.save(file_prefix = checkpoint_prefix)
OUT_DIR.mkdir(exist_ok=True)
save_images(OUT_DIR)
evaluator = MiFIDEvaluator(MODEL_PATH, TRAIN_DIR)
fid_value, distance, mi_fid_score = evaluator.evaluate(OUT_DIR)
print(f'FID: {fid_value:.5f}')
print(f'distance: {distance:.5f}')
print(f'MiFID: {mi_fid_score:.5f}')
shutil.rmtree(OUT_DIR)
print('Removed temporary image directory.')
print('Final epoch.')
以下是在训练过程中生成的一些狗的图像:
Epoch 120 — MIFID ~ 90.5
Epoch 200 — MIFID ~ 64.8
Epoch 280 (final) — MIFID ~ 60.99
正如我们所观察到的,MIFID 在 280 个周期(约 8 小时)内稳步提高。我在比赛中使用这个模型取得的最好成绩是 55.87 。学习过程确实有点随机,所以我认为在**【50,65】**附近的分数应该是现实的。如果你有时间的话,可以继续训练这个模型,因为它有不断改进的潜力:)。
最后,我将向您展示如何制作一个有趣的 GIF 来查看 DCGAN 学习过程的一个漂亮的小模拟(代码来自 Tensorflow 的 DCGAN 教程)。
anim_file = 'dcgan.gif'
with imageio.get_writer(anim_file, mode='I') as writer:
filenames = glob.glob('image*.png')
filenames = sorted(filenames)
last = -1
for i,filename in enumerate(filenames):
frame = 1*(i**2)
if round(frame) > round(last):
last = frame
else:
continue
image = imageio.imread(filename)
writer.append_data(image)
image = imageio.imread(filename)
writer.append_data(image)
import IPython
if IPython.version_info > (6,2,0,''):
IPython.display.Image(filename=anim_file)
结论
综上所述, DCGANs 似乎对超参数选择极其敏感,在训练过程中会出现很多问题,包括模式崩溃。它们也是非常计算密集型的,并且难以置信地难以为运行时间 ~9 小时构建高分模型。幸运的是,有一个完整的列表,列出了可能的方法和技术,这些方法和技术都有很好的文档记录,可以很容易地应用到您的模型中,以稳定培训过程。
对我个人来说,试验这些技术并在这个过程中打破一些核心真的很有趣:d .随时在评论中留下任何建议(改进模型或修复我搞砸的东西)。
非常感谢克里斯·德奥特、那那西、查德·马拉和尼尔哈·罗伊在比赛中提供的 Kaggle 内核和示例。我会在下面留下他们的链接。
总的来说,这是我参加的第一个卡格尔比赛,这是一个非常有趣的了解甘斯并和他们一起玩耍的方法。这似乎是 Kaggle 的第一次涉及生成建模的比赛,让我们希望未来会有更多类似这样令人兴奋的挑战出现;).
参考
有用的内核和笔记本
[1].我之前在 EDA 上的内核和图像预处理
[2]. Xml 解析并裁剪到指定的边界框
[3].带插值的图像裁剪方法
[4].Chad Malla 的另一个基于 Keras 的 DCGAN 方法
[5]. DCGAN 帮助您提高模型性能的方法
[6]. Tensorflow DCGAN 教程
[7].那那西拍摄的 DCGAN 狗图片
[8].甘犬首发 24-7 月 Nirjhar Roy 定制层
[9].克里斯·德奥特监督的生殖狗网
[10].我的最佳参赛作品
研究论文、帖子和讨论
[1].Ian Goodfellow,J. Pouget-Abadie,M. Mirza,B. Xu,S. Ozair,Y. Bengio , 生成性对抗网络 (2014)
[2].罗卡,理解生成性对抗网络
[3].J. Brownlee,生成性对抗网络的温和介绍
[4].a .拉德福德,l .梅斯,s .钦塔拉,深度卷积生成对抗网络的无监督表示学习 (2015)
[5].惠, GAN — DCGAN(深度卷积生成对抗网络)
[6].S. Yadav,神经网络中的权重初始化技术
[7].T. Miyato,T. Kataoka,M. Koyama,Y. Yoshida,生成性对抗网络的谱归一化 (2018)
[8].IShengFang,在 Keras 实现光谱归一化
[9].c .科斯格罗维,光谱归一化解释
[10].许军,氮化镓——提高氮化镓性能的方法
[11].J. Brownlee,如何在 Keras 中实现 GAN Hacks 来训练稳定的模型
[12].焦裕禄,GANS 的诡计
[13].C. K. Sø nderby,实例噪声:稳定 GAN 训练的技巧
[14].J. Hui, GAN — RSGAN & RaGAN(新一代代价函数。)
[15].D. Mack,对初始得分的简单解释
[16].J. Hui,GAN——如何衡量 GAN 的性能?
[17].你所需要的就是甘的黑客
[18].如何训练你的敏感神经——这些方法似乎很有效。
[19].解释公制 FID
从头开始消除重复记录
Photo credit: Trivago
识别相似记录,稀疏矩阵乘法
网络世界充满了重复的列表。特别是,如果你是一个在线旅行社,你接受不同的供应商为你提供相同的财产信息。
有时重复的记录是显而易见的,这让你想:这怎么可能?
Photo credit: agoda
还有一次,两个记录看起来像是重复的,但是我们不确定。
Photo credit: expedia
或者,如果您为一家拥有大量公司或客户数据的公司工作,但是因为这些数据来自不同的源系统,而这些系统通常以不同的方式写入。那么您将不得不处理重复的记录。
Photo credit: dedupe.io
数据
我觉得最好的数据集是用我自己的。使用我不久前创建的西雅图酒店数据集。我去掉了酒店描述特征,保留了酒店名称和地址特征,并特意添加了重复记录,数据集可以在这里找到。
两个酒店如何重复的例子:
duplicate.py
Table 1
最常见的复制方式是街道地址的输入方式。一些人使用缩写,另一些人没有。对于人类读者来说,很明显上面的两个列表是同一个东西。我们将编写一个程序来确定并删除重复的记录,只保留一个。
TF-IDF + N-gram
- 我们将使用名称和地址作为输入要素。
- 我们都熟悉 tfidf 和 n-gram 方法。
- 我们得到的结果是一个稀疏矩阵,每行是一个文档(name_address),每列是一个 n 元文法。为每个文档中的每个 n 元语法计算 tfidf 分数。
tfidf_ngram.py
稀疏点顶
我发现了一个由 ING 批发银行、 sparse_dot_topn 开发的优秀的库,它只存储每个项目的前 N 个最高匹配,我们可以选择显示高于阈值的最高相似性。
它声称,它提供了一种更快的方法来执行稀疏矩阵乘法,然后是 top-n 乘法结果选择。
该函数将以下内容作为输入:
- a 和 B:两个 CSR 矩阵
- ntop: n 个最佳结果
- lower _ bound:A * B 的元素必须大于输出的阈值
输出是一个结果矩阵。
awesome_cossim_top.py
运行该功能后。该矩阵仅存储前 5 个最相似的酒店。
以下代码解包生成的稀疏矩阵,结果是一个表,其中每个酒店将与数据中的每个酒店匹配(包括其自身),并计算每对酒店的余弦相似性得分。
get_matches_df.py
除了它本身,我们只对顶级赛事感兴趣。因此,我们将视觉检查结果表排序相似性得分,其中我们确定一个阈值一对是相同的属性。
matches_df[matches_df['similarity'] < 0.99999].sort_values(by=['similarity'], ascending=False).head(30)
Table 2
我决定我的安全赌注是删除任何相似性得分高于或等于 0.50 的配对。
matches_df[matches_df['similarity'] < 0.50].right_side.nunique()
在那之后,我们现在还有 152 处房产。如果你记得,在我们的原始数据集中,我们有 152 个属性。
Jupyter 笔记本和数据集可以在 Github 上找到。祝你一周工作顺利!
参考资料:
[## Python 中的超快速字符串匹配
传统的字符串匹配方法,如 Jaro-Winkler 或 Levenshtein 距离度量,对于…
bergvca.github.io](https://bergvca.github.io/2017/10/14/super-fast-string-matching.html)
去谷歌搜索巴赫
将巴赫的《公路规则》与美国民粹主义音乐进行对比
“Thnking Outside the Bachs” by Max Harper Ellert
本周,我玩谷歌涂鸦从简单的旋律中创造巴赫和声,玩得很开心。浏览一些关于融合人工智能的过程和对位原则的文章也很有趣,比如艾丽莎·拜尔娜的这篇综合文章和微软音乐理论工程师丹尼尔·汤普金斯的一篇精心构思的文章中对涂鸦的回应:谷歌的巴赫人工智能。
这些文章和许多其他文章倾向于解释和批评这一过程,对人工智能在正确遵循对位规则方面缺乏成功提出了一些批评。虽然对我来说,一些由音乐家和非音乐家创作的曲调充斥着交叉的声音、平行的音程、怪异的节奏和狂野的跳跃,有些甚至相当俏皮,但你仍然有一个古老的难题,即猴子与打字机和莎士比亚的比例。没有一台机器,无论程序设计得多么好,多么聪明,能够重现巴赫的天才。
为了证明这一点,我在 Feste Burg 涂鸦了*——最熟悉的巴赫合唱曲之一。所以我可以分享它,我把它写到我的符号程序中,作为下面的 png 图形导入。这是最初的协调:*
Ein Feste Burg, Bach’s original harmonization
我不能准确地把它写进去——涂鸦不允许记笔记。但由于涂鸦似乎存在于节拍之外,并且使用节拍作为地标,而不是形成节拍逻辑,这似乎并不重要。涂鸦带回来的东西和原来的没什么不同,但显然没有魔力。你可能会认为这是一个展示它的东西的绝佳机会,但我猜 Ein Feste Burg 不是用来载入电脑的 360 首曲子之一(她说,双臂交叉,双脚轻敲)。
Ein Feste Burg as seen by Google Doodle
然后我肯定去了一个野毛。我放了两小节 1941 年的流行蓝调音乐,约翰尼·默瑟和哈罗德·阿伦的《夜色中的蓝调》(我妈妈告诉我的),看看会发生什么。这两个小节来自合唱部分的火车汽笛部分— *“一声呜呼,一声呜呼”*其中旋律跳过一个三度音,滑到第五度,然后在第二个“汽笛”时回到第四度。这与《西区故事》中伯恩斯坦的《玛丽亚》的开场有着相同的动机。像这样:
最初的动机有一个引出三连音的八分音符。我看不出涂鸦中的三连音,所以我只能用第八个音符。我想看看如果我强制拾音会有什么效果,所以我在第一小节结束时将第八个音符拾音。然后点击“和声”,让它进城。AI doodle 疯了!它不能解析这个信号,所以它写了一堆笔记来引导我的动机。以下是巴布亚新几内亚:
Google Doodle can’t handle the blues
正如你所看到的,它看起来很奇怪——如果你能在钢琴上演奏它,它听起来很奇怪。它一点也不像巴赫,但听起来也不像《夜色中的布鲁斯》。我非常震惊。
然后,当我着手沏一杯茶时,我有了一点小小的惊喜(这是一个人神志不清后所必需的)。降半音——蓝调——三度、五度、六度,在我们所有的民粹主义文学中都能立即辨认出来:蓝调、节奏蓝调、爵士、摇滚,而不仅仅是美国歌曲集的一点点。为什么蓝调音乐会让巴赫在坟墓里翻身?啊哈!
因为规则不一样。
巴赫的对位法到今天有 250 年的演变时间。与美国民粹主义联系最紧密的音乐的大部分基本构件都是在 1940 年建立的,在摇滚乐的发展过程中加入了一些曲折和呐喊。随着规则的改变,对这种演变最有责任的一群美国人可能是非裔美国人,他们从战前在田间劳作时用来保持理智的蓝调圣歌开始。
请注意,野外圣歌和夜晚的布鲁斯没有太多相似之处。现场喊叫声、圣歌和回应叫喊声是非常典型的。来自锡盘巷的蓝调歌曲非常自然。换句话说,他们遵循我们所遵循的以欧洲为基础的作曲规则。学生们在音乐学院里汗流浃背,但有点反常。20 世纪,无论是在艺术音乐还是迅速发展的民粹主义形式中,大多数对位法的规则都变得宽松了(很多)。然而,我们的流行音乐,尤其是 20 世纪上半叶的流行音乐,与古典主义(想想:莫扎特)相比,与巴洛克音乐有更多的共同点,但即使它坚持现在几乎是我们音乐 DNA 一部分的古老的六二五一和声结构,流行音乐也遵循着自己的逻辑,将它与古典主义区分开来。那么,巴洛克对位只是和声结构中的低语。一个重要的耳语,是的,但消退了。
许多民粹主义逻辑与非裔美国人、拉丁美洲人和各种移民对我们音乐的贡献有关。田野圣歌的蓝调,桑巴舞的交叉节奏,阿巴拉契亚民歌曲调的圣歌般的形式都演变成了我们称之为我们自己的音乐,并从那里继续演变。如今,蔓越莓的印地音乐与吉米·罗杰斯音乐并没有太大的不同——在大多数情况下,它归结于添加的层次、复杂的和声和大量的机械处理。理解不同的文化对音乐有不同的理解并不是一个很大的飞跃,但是亲爱的读者们,我希望你们考虑的是进化是如何发生的。
当然,通过打破规则。
显而易见,20 世纪美国民粹主义音乐与巴洛克对位法存在(认知)不和谐,所以有人会问,我们为什么还要为巴赫的谷歌涂鸦这样的东西费心。我有几句话要说,但我现在的观点是,如果我们把一首田间圣歌演变成一首流行歌曲,或者一首调式民谣演变成一首印度流行歌曲,那么我们为什么不能把所有这些音乐的基本 DNA 转变成一种新的古典形式呢?
戏弄、强迫或哄骗平民音乐成为一套新的对位规则?嗯…
这丝毫无损巴赫的贡献!如果他今天突然还活着(在一个主要的文化调整期之后),他会对我们可以获得的各种音乐感到惊讶,我相信他会喜欢的。你知道,他是个吉格斯爵士。在为他的周日演出创作清唱剧的间隙,他在咖啡馆演奏——如果你愿意的话,也可以说是巴洛克鸡尾酒大键琴。他利用现有的材料,并将其与自己的即兴才华相结合。纵观历史,作曲家都知道天才的标志是能够窃取音乐并使其焕然一新(而不会被起诉)。
那么,如果我们的音乐不符合巴洛克式对位法的限制,那又怎么样呢?谷歌涂鸦很有趣,音乐制作也应该有趣,并以允许人们探索的方式呈现。我度过了一个愉快的周六下午,摆弄着涂鸦,把我的努力改写成西贝柳斯。对于新手来说,我可以看到这可以让创作音乐的神秘过程变得不那么神秘(尽管正如我们从结果中看到的那样,并不那么复杂)——有点像科学探索博物馆。音乐是一门科学,所以应该有音乐探索馆,你觉得呢?
然而,如果谷歌推出这些有趣的小应用有什么好处的话,必须有两种理解。
一是巴赫给我们留下了一份巨大的遗产,我们当前音乐文化的一切都依赖于此。从我们西方文化教育音乐的角度来看,我们整个音乐知识的很大一部分来源于他的变革性作品。每当我的学生领会到五度圈近乎形而上学的意义,并意识到如果没有巴赫,音乐物理学的原始真理就不会在我们的掌握之中时,我都会泪眼模糊。
其次,考虑到我们现在已经在这条路上走了 250 多年,我们是不同时代的不同文化和不同的人,音乐必须继续发展的方式取决于我们现在是谁,在我们丰富多彩的多样性中。学校教育是一个传统问题,也是如何呈现的问题。这让我想到了苏族萨满要学习 10 年或更长时间才能成为强大的治疗师——这与通过塔夫茨医学院所花的时间差不多。
无论如何,不管我们喜不喜欢,文化转型就在此时此地发生,所以让我们顺其自然吧。但是,仅仅因为我们停止抵制文化变革,并不意味着我们要抛弃好的旧方式。事实上,我们依靠这种智慧来保持我们对艺术的诚实。这可以追溯到亚里士多德。
这里有几个听力选择给你。首先,一个神话般的版本蓝调之夜,由艾拉·费兹杰拉献上
然后,你可以在威廉·格兰特·斯蒂尔的《非裔美国人交响曲》第一乐章中听到一些相同的降蓝音符。这是一部伟大的作品,你不必太费力就能看出音乐中固有的古典结构。
在 R 中处理应用函数
Apply functions in R
迭代控制结构(像 for、while、repeat 等循环。)允许多次重复指令。然而,在大规模数据处理中,使用这些循环会消耗更多的时间和空间。借助于 Apply 函数,r 语言有一种更高效、更快速的方法来执行迭代。
在这篇文章中,我将从视觉角度讨论**apply**
函数在循环中的效率,然后进一步讨论**apply**
家族的成员。
在进一步讨论**apply**
函数之前,让我们先看看与基本循环相比,使用**apply**
函数的代码执行如何花费更少的迭代时间。
考虑在 r 的gamclass
包中可用的**FARS(Fatality Analysis Recording System)**
数据集。它包含 17 个不同特征的 151158 个观察值。该数据集包括至少有一人死亡的所有事故,数据仅限于前排乘客座位有人的车辆。
现在让我们假设我们想要计算年龄列的平均值。这可以使用传统的循环和应用函数来完成。
方法 1:使用 for 循环
library("gamclass")
data(FARS)
mean_age <- NULL
total <- NULL
for(i in 1:length(FARS$age)){
total <- sum(total, FARS$age[i])
}
mean_age <- total/length(FARS$age)
mean_age
方法 2:使用 apply()函数
apply(FARS[3],2, mean)
现在让我们借助Profvis
包,通过可视化模式来比较这两种方法。
Profvis
是一个代码剖析工具,它提供了一个交互式图形界面,用于可视化指令在整个执行过程中的内存和时间消耗。
为了使用profvis
,把指令放在profvis()
中,它在 R studio 的一个新标签中打开一个交互式的 profile visualizer。
#for method 1
profvis({
mean_age <- NULL
total <- NULL
for(i in 1:length(FARS$age)){
total <- sum(total, FARS$age[i])
}
mean_age <- total/length(FARS$age)
mean_age
})
使用方法 1 输出
在火焰图选项卡下,我们可以检查指令所用的时间(毫秒)。
#for method 2
profvis({
apply(FARS[3],2, mean)
})
使用方法 2 输出
在这里,人们可以很容易地注意到,使用方法 1 所花费的时间几乎是 1990 ms (1960 +30),而对于方法 2,它仅仅是 20 ms。
应用函数优于传统循环的优势
- 执行起来更有效率和更快。
- 易于遵循的语法(而不是使用 apply 函数只编写一行代码来编写一组指令)
在 R 中应用系列
Apply family 包含各种不同的函数,适用于不同的数据结构,如列表、矩阵、数组、数据框等。apply 家族的成员有apply()
、lapply()
、sapply()
、tapply()
、mapply()
等。这些函数是循环的替代品。
每个应用函数至少需要两个参数:一个对象和另一个函数。该函数可以是任何内置的(如平均值、总和、最大值等。)或用户自定义函数。
浏览成员
1.apply()函数
apply()
的语法如下
其中X
是一个输入数据对象,MARGIN
表示函数如何适用于行或列,margin = 1 表示行,margin = 2 表示列,FUN
指向一个内置或用户定义的函数。
输出对象类型取决于输入对象和指定的函数。apply()
可以返回不同输入对象的向量、列表、矩阵或数组,如下表所述。
#---------- apply() function ----------
#case 1\. matrix as an input argument
m1 <- matrix(1:9, nrow =3)
m1
result <- apply(m1,1,mean) #mean of elements for each row
result
class(result) #class is a vector
result <- apply(m1,2,sum) #sum of elements for each column
result
class(result) #class is a vector
result <- apply(m1,1,cumsum) #cumulative sum of elements for each row
result #by default column-wise order
class(result) #class is a matrix
matrix(apply(m1,1,cumsum), nrow = 3, byrow = T) #for row-wise order
#user defined function
check<-function(x){
return(x[x>5])
}
result <- apply(m1,1,check) #user defined function as an argument
result
class(result) #class is a list#case 2\. data frame as an input
ratings <- c(4.2, 4.4, 3.4, 3.9, 5, 4.1, 3.2, 3.9, 4.6, 4.8, 5, 4, 4.5, 3.9, 4.7, 3.6)
employee.mat <- matrix(ratings,byrow=TRUE,nrow=4,dimnames = list(c("Quarter1","Quarter2","Quarter3","Quarter4"),c("Hari","Shri","John","Albert")))
employee <- as.data.frame(employee.mat)
employee
result <- apply(employee,2,sum) #sum of elements for each column
result
class(result) #class is a vector
result <- apply(employee,1,cumsum) #cumulative sum of elements for each row
result #by default column-wise order
class(result) #class is a matrix
#user defined function
check<-function(x){
return(x[x>4.2])
}
result <- apply(employee,2,check) #user defined function as an argument
result
class(result) #class is a list
2.lapply()
功能
lapply()
总是返回一个列表,lapply()
中的‘l’指的是‘list’。lapply()
处理输入中的列表和数据帧。MARGIN
此处不需要参数,指定的函数仅适用于列。请参考下表了解输入对象和相应的输出对象。
#---------- lapply() function ----------
#case 1\. vector as an input argument
result <- lapply(ratings,mean)
result
class(result) #class is a list#case 2\. list as an input argument
list1<-list(maths=c(64,45,89,67),english=c(79,84,62,80),physics=c(68,72,69,80),chemistry = c(99,91,84,89))
list1
result <- lapply(list1,mean)
result
class(result) #class is a list
#user defined function
check<-function(x){
return(x[x>75])
}
result <- lapply(list1,check) #user defined function as an argument
result
class(result) #class is a list#case 3\. dataframe as an input argument
result <- lapply(employee,sum) #sum of elements for each column
result
class(result) #class is a list
result <- lapply(employee,cumsum) #cumulative sum of elements for each row
result
class(result) #class is a list
#user defined function
check<-function(x){
return(x[x>4.2])
}
result <- lapply(employee,check) #user defined function as an argument
result
class(result) #class is a list
**apply()**
vs**lapply()**
lapply()
总是返回一个列表,而apply()
可以返回一个向量、列表、矩阵或数组。lapply()
中没有MARGIN
的范围。
3.sapply()
功能
sapply()
是lapply()
的简化形式。它有一个额外的参数**simplify**
,默认值为 true **,**如果simplify = F
那么sapply()
返回一个类似于lapply()
的列表,否则返回最简单的输出形式。
请参考下表了解输入对象和相应的输出对象。
#---------- sapply() function ----------
#case 1\. vector as an input argument
result <- sapply(ratings,mean)
result
class(result) #class is a vector
result <- sapply(ratings,mean, simplify = FALSE)
result
class(result) #class is a list
result <- sapply(ratings,range)
result
class(result) #class is a matrix#case 2\. list as an input argument
result <- sapply(list1,mean)
result
class(result) #class is a vector
result <- sapply(list1,range)
result
class(result) #class is a matrix
#user defined function
check<-function(x){
return(x[x>75])
}
result <- sapply(list1,check) #user defined function as an argument
result
class(result) #class is a list#case 3\. dataframe as an input argument
result <- sapply(employee,mean)
result
class(result) #class is a vector
result <- sapply(employee,range)
result
class(result) #class is a matrix
#user defined function
check<-function(x){
return(x[x>4])
}
result <- sapply(employee,check) #user defined function as an argument
result
class(result) #class is a list
4.tapply()
功能
tapply()
在处理分类变量时很有用,它将一个函数应用于分布在不同类别中的数字数据。tapply()
最简单的形式可以理解为
tapply(column 1, column 2, FUN)
其中column 1
是函数应用的数字列,column 2
是因子对象,FUN
是要执行的函数。
#---------- tapply() function ----------
salary <- c(21000,29000,32000,34000,45000)
designation<-c("Programmer","Senior Programmer","Senior Programmer","Senior Programmer","Manager")
gender <- c("M","F","F","M","M")
result <- tapply(salary,designation,mean)
result
class(result) #class is an array
result <- tapply(salary,list(designation,gender),mean)
result
class(result) #class is a matrix
5.by()函数
by()
执行与tapply()
类似的工作,即对分布在不同类别中的数值向量值进行运算。by()
是tapply()
的一个包装函数。
#---------- by() function ----------
result <- by(salary,designation,mean)
result
class(result) #class is of "by" type
result[2] #accessing as a vector element
as.list(result) #converting into a list
result <- by(salary,list(designation,gender),mean)
result
class(result) #class is of "by" type
library("gamclass")
data("FARS")
by(FARS[2:4], FARS$airbagAvail, colMeans)
6.mapply()
功能
mapply()
中的“m”是指“多元”。它将指定的函数逐个应用于参数。请注意,在这里,function 被指定为第一个参数,而在其他应用函数中,它被指定为第三个参数。
#---------- mapply() function ----------
result <- mapply(rep, 1:4, 4:1)
result
class(result) #class is a list
result <- mapply(rep, 1:4, 4:4)
class(result) #class is a matrix
结论
我相信我已经介绍了所有最有用和最流行的 apply 函数以及所有可能的输入对象组合。如果你认为缺少了什么或者需要更多的输入。在评论里告诉我,我会加进去的!
快速处理分类数据—一个例子
早上 9 点你在办公室。你的老板进来,给了你一些数据,并要求你在中午 12 点之前创建一个模型。将召开一次会议,会上将展示这个模型。你是做什么的?
我们将查看来自私人 Kaggle 竞赛的示例数据集,创建一些快速模型并选择一个。完整的 github 库在这里是。
我们得到了训练数据集(特征和目标)。我们还获得了测试特性数据集,并被要求预测测试目标。为了测试您的预测,您创建了一个预测文件并将其上传到 Kaggle。然后 Kaggle 会给你一个分数(数值从 0 到 1)。值越高,你的预测就越好。我们将重点关注准确度分数,因为这是 Kaggle 将在本次比赛中测试的分数。
在 Jupyter 笔记本中首先导入您需要的课程。保持这个块是独立的,因为您可以向它添加更多的库并单独执行它。
import numpy as np
import pandas as pdfrom sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifierpd.set_option('display.max_columns', None) # Unlimited columns.
pd.options.mode.use_inf_as_na = True # Any inf or -inf is
# treated as NA.
读入训练特征数据:
X_train_original = pd.read_csv('./train_features.csv',
header = [0], # Top row is header.
index_col = 0) # First col is index.
X_train_original.head()
读入训练目标数据:
y_train_original = pd.read_csv('./train_labels.csv',
header = [0], # Top row is header.
index_col = 0) # First col is index.
y_train_original.head()
你的目标很明确。让我们看看它有多少个类别:
pd.value_counts(y_train_original.status_group, normalize = True)
由于一半以上只是一个类别,我们可以预测我们所有的目标值都是“功能性的”。这将在训练数据集上给我们 0.54 的准确度。让我们看看它在测试数据集上做了什么。
多数类预测
我们进行多数类预测的原因是为了衡量我们未来的预测分数应该有多好。这给了我们一个基线,我们希望在下一个模型中跨越它。
让我们先来看看测试特性:
X_test_original = pd.read_csv('./test_features.csv',
header = [0],
index_col = 0)
X_test_original.shape
(14358, 39)
此形状显示我们在预测输出中需要 14358 个值(输入的每行一个)。因此,我们创建了一个具有所需行数的数组,值为“functional”:
y_pred = ['functional'] * len(X_test_original)
y_pred = pd.DataFrame(data = y_pred,
index = X_test_original.index.values,
columns = ['status_group'])
y_pred.head()
然后我们把它写到一个文件中,并导入到 Kaggle 中。Kaggle 的准确率为 0.53(这和我们的预期差不多)。区别只是因为测试数据集不包含与训练数据集完全相同的目标类值比例。
仅使用数字特征进行预测
X_train_numerical = X_train_original.select_dtypes(
include = np.number).copy()
将“日期 _ 记录”字段转换为“天数 _ 自 _ 纪元”。在计算机编程中,对于 unix 计算机,纪元被认为是 1970 年 1 月 1 日。这只是一个通常使用的惯例——我们可以在这里的任何一天使用。对于机器学习,我们只关心值的相对比例是否相同。
days_since_epoch = \
pd.to_datetime(X_train_original['date_recorded']) \
- pd.datetime(1970, 1, 1)
X_train_numerical['days_since_epoch'] = days_since_epoch.dt.days
X_train_numerical.head()
X_train_numerical_indices = X_train_numerical.index.values
y_train_numerical = y_train_original[y_train_original.index. \
isin(X_train_numerical_indices)]
逻辑回归
让我们尝试一个逻辑回归分类器:
cv_score = cross_val_score(LogisticRegression(),
X_train_numerical, y_train_numerical,
scoring = 'accuracy',
cv = 3,
n_jobs = -1,
verbose = 1)
cv_score
逻辑回归给我们的分数是 0.55。与多数班级模式没有太大区别。
决策图表
决策树分类器怎么样:
clf = DecisionTreeClassifier()
cv_score = cross_val_score(clf,
X_train_numerical, y_train_numerical,
scoring = 'accuracy',
cv = 3,
n_jobs = -1,
verbose = 1)
cv_score
这个分数在 0.65 就好多了。让我们获得测试数据集的预测,并将其写出到文件中。然后我们可以提交给 Kaggle:
clf.fit(X_train_numerical, y_train_numerical)
X_test_numerical = X_test_original.select_dtypes(include = \
np.number).copy()
days_since_epoch = pd.to_datetime(X_test_original['date_recorded'])
- pd.datetime(1970, 1, 1)
X_test_numerical['days_since_epoch'] = days_since_epoch.dt.daysy_pred = clf.predict(X_test_numerical)y_pred = pd.DataFrame(data = y_pred,
index = X_test_numerical.index.values,
columns = ['status_group'])y_pred.to_csv('./decision_tree_pred.csv',
header = ['status_group'],
index = True,
index_label = 'id')
检查数据中是否有缺失值或异常值
X_train_original.isnull().sum()
39 个要素中有 7 个要素的值为空。让我们放弃这些功能:
X_non_nulls = X_train_original.dropna(axis = 1)
让我们找出每个特征中有多少个唯一值:
X_non_nulls.nunique().sort_values(ascending = True)
根据这篇文章,当分类值编码为数字或二进制时,决策树分类器更快。
让我们对具有< 50 个唯一值的非空列进行编码,将数字列添加到数据帧中,并运行不同深度的决策树分类器。
X_selected = X_non_nulls.loc[:, X_non_nulls.nunique().sort_values()\
< 50]
cat_cols = list(X_selected.select_dtypes(['object']).columns.values)X_categorical = X_selected[cat_cols]. \
apply(lambda x: x.astype('category').cat.codes)
X_train_selected = X_train_numerical.join(X_categorical)clf = DecisionTreeClassifier()
cv_score = cross_val_score(clf,
X_train_selected, y_train_original,
scoring = 'accuracy',
cv = 3,
n_jobs = -1,
verbose = 1)
cv_score
这给了我们 0.75 分。这是训练分数,所以我们应该将相同的分类器应用于测试数据,并请 Kaggle 评估其准确性:
clf.fit(X_train_selected, y_train_original)X_test_non_nulls = X_test_original.dropna(axis = 1)
X_test_selected = X_test_non_nulls.loc[:, \
X_test_non_nulls.nunique().sort_values() < 50]cat_cols = list(X_test_selected.select_dtypes(['object']). \
columns.values)
X_test_categorical = X_test_selected[cat_cols]. \
apply(lambda x: \
x.astype('category').cat.codes)X_test_selected = X_test_numerical.join(X_test_categorical)y_pred = clf.predict(X_test_selected)
y_pred = pd.DataFrame(data = y_pred,
index = X_test_selected.index.values,
columns = ['status_group'])
测试数据集给我们的分数是 0.76,这个分数更高,因为我们的模型对测试数据集的拟合程度肯定比训练数据集好一点。仍然在相同的值左右,这是意料之中的。
既然我们的决策树给了我们一个好的结果,让我们试试随机森林分类器
随机森林分类器适用于多项目标(具有多个分类值的目标)。该分类器从训练数据集中随机抽取样本,因此不需要对其进行交叉验证。我们可能会做 GridSearchCV 来尝试不同的 n_estimators 和 max_depth(如果我们的分数不是很好的话)。
随机森林分类器由许多决策树组成。通过从整个特征列表中随机选择树的每个节点处的特征来创建每个树。与单个决策树分类器相比,树的数量给予随机森林分类器更少的偏差。
X_train, X_test, y_train, y_test = train_test_split(
X_train_selected, y_train_original, test_size=0.2)clf = RandomForestClassifier()
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
决策树分类器给我们的分数是 0.79。很好,但是没有以前跳得高。这是我们通常会发现的情况——早期的模型通常得分较低,可以很容易地被击败,但后期的模型很难被击败。我们还没完呢。我们将使用网格搜索来搜索最佳随机森林分类器:
param_grid = {
'n_estimators': [10, 20, 30],
'max_depth': [6, 10, 20, 30]
}gridsearch = GridSearchCV(RandomForestClassifier(n_jobs = -1),
param_grid=param_grid,
scoring='accuracy', cv=3,
return_train_score=True, verbose=10)gridsearch.fit(X_train, y_train)
param_grid 是分类器所需参数的字典。如果您不确定在这个字典中放入什么,可以使用这个函数调用,它会给出您可以使用的参数列表:
RandomForestClassifier().get_params().keys()
在 GridSearchCV 函数内部,我们用 n_jobs = -1 创建了一个 RandomForestClassifier 对象。这将允许我们使用机器上的所有内核,从而使这个作业运行得更快。
变量“cv”给出了该网格搜索应该使用的交叉验证折叠数。cv = 3 会将我们的数据分成 3 个相等的部分,然后使用其中的两个部分来训练 RandomForest 分类器,并使用剩余的数据进行测试。它会一直这样做,直到所有的组合都用完为止。
verbose 值将告诉 grid search 要打印多少信息。值越大,打印的信息越多。值为 10 时,您将会看到在 param_grid 字典中指定的变量值的每个组合与测试/训练分割的迭代编号一起打印出来。您还将看到在数据的测试部分获得的分数。您不必阅读所有内容,我们会打印出一份更易于阅读的摘要:
pd.DataFrame(gridsearch.cv_results_).sort_values( \
by='rank_test_score')
此数据帧的顶行显示了 param_grid 选项,这些选项在数据的测试部分给出了最好的分数。这显示在 mean_test_score 列中,我们的分数是 0.79。这与决策树分类器相同。
让我们在 Kaggle 测试集上运行这个:
clf = RandomForestClassifier(max_depth = 20,
n_estimators = 30,
n_jobs = -1)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
我们得到了 0.81 分。这与决策树分类器得分 0.79 没有太大的不同。不同的是决策树有偏差,而随机森林没有。如果你在多组新的测试数据上测试这个随机森林分类器,你会发现它比决策树分类器做得更好。
结论
既然你知道随机森林比决策树更好,也许你可以使用以下步骤更快地找到解决方案:
- 永远,永远,先做个快速预测。对于像这样的分类问题,如果目标中有一个多数类,多数类预测将是一个好的开始。
- 如果有很少的空值(或者如果它们只存在于某些特征中),则丢弃观察值/特征。
- 删除具有大量值的分类要素。他们可能不会做出好的特写。此外,删除具有单一值的要素,因为它们无法区分不同的类。
- 将日期转换为天或秒(为了更精确)。大多数分类器都是和数字一起工作的,所以最好都给它们数字。将分类列转换为数字。
- 不要运行决策树分类器,因为它是有偏见的,只需用随机森林分类器运行网格搜索。