TowardsDataScience-博客中文翻译-2020-六十九-
TowardsDataScience 博客中文翻译 2020(六十九)
原文:TowardsDataScience Blog
协议:CC BY-NC-SA 4.0
最后,对 ReLU 工作原理的直观解释
原文:https://towardsdatascience.com/if-rectified-linear-units-are-linear-how-do-they-add-nonlinearity-40247d3e4792?source=collection_archive---------8-----------------------
来源: Pixabay
分段线性函数如何增加非线性
几乎所有使用过神经网络的人都用过 ReLU 函数。这是激活功能的现代标准,但是关于它是如何工作的还有一些问题。神经网络需要非线性来处理非线性问题,通常唯一可以注入非线性的地方是激活函数。然而,尽管严格地说,ReLU 在数学上不是线性函数,但它是由两个线性函数(分段线性)组成的。那么,直观地说,ReLU 如何保持并超越真正非线性的曲线函数的性能呢?
首先,重要的是要充分理解为什么需要在神经网络中加入非线性。在标准的神经网络中,每一层都将前一层乘以一组权重,并添加一个偏差。为了将长度为 m 的向量转换为长度为 n 的向量,权重采用了一个 n 乘以 m 矩阵(乘以一个 m 乘以一个矩阵,或者一个向量)的形式。
由作者创建。
即使在输入通过网络的几层之后,所应用的变换也是纯线性的;也就是说,只通过乘以一个系数并加上一个偏差来允许自由移动。因此,网络将只能解决不能被严格线性函数的有限运动所分离的问题。由于支配神经网络应用的许多复杂问题,线性神经网络的有限能力根本没有用武之地。
作为一个更严格的必要性的证明,普适近似定理需要非线性激活函数才能有效。本质上,该定理指出,在一些条件下,任何连续函数 f 都可以用具有一个隐藏层和足够数量单元的神经网络来建模。要求之一是激活函数必须具有某种形式的非线性。概括地说,为了处理非线性问题,网络中必须存在某种非线性。这是足够直观的理解。
ReLU 已经成为神经网络世界的宠儿激活函数。整流线性单元的简称,它是一个分段线性函数,对于 x 的所有负值定义为 0,否则等于 a × x ,其中 a 为可学习参数。
用德斯莫斯画的。
这种分段线性函数如何给网络增加非线性,这可能看起来不直观。毕竟还是线性的。虽然它显然不是完全线性的,但非线性的数学定义并不令人满意或直观。与激活函数宝座的其他竞争者相比——soft max、sigmoid 和 tanh——ReLU 缺乏干净的曲线,相反似乎只是线性函数的兄弟。然而,ReLU 成功的关键在于它如何解决环空问题:
每个神经网络有三个隐藏层,每层有三个单元。唯一的区别是激活功能。学习率:0.03,正规化:L2。由作者创建。
tanh 是一个平滑的曲线函数,它在圆的周围画出一个干净的包络线(而 linear 完全失败),而 ReLU 画出一个六边形,有几个尖角。其实这就是 ReLU 的优势所在:可以在某一点弯曲线性函数,到一定程度。结合前一层的偏差和权重,ReLU 可以在任何位置以任何角度弯曲。
用德斯莫斯画的。
这些小弯曲构成了近似法的基础。任何关系或函数都可以通过将许多 ReLU 函数聚合在一起来粗略估计,这发生在神经元在下一层中被折叠和组合的时候。这已经在数学上得到证明,例如,用正弦波或指数函数,很像低阶泰勒级数如何拟合函数。
酷似乙状结肠。用德斯莫斯画的。
人们可能倾向于指出,ReLUs 不能外推;也就是说,拟合为类似于-4< x4 的正弦波的一系列 ReLUs 将不能在这些界限之外继续正弦波的 x 的值。然而,重要的是要记住,神经网络的目标不是外推,而是概括。例如,考虑一个基于浴室数量和卧室数量来预测房价的模型。如果模型很难将该模式应用到浴室数量的负值或卧室数量超过 500 的负值,这并不重要,因为这不是模型的目标。(你可以在这里阅读更多关于一般化 vs 外推。)
ReLU 功能的强大不在于它本身,而在于一整支 ReLU 大军。这就是为什么在神经网络中使用几个 ReLUs 不会产生令人满意的结果;相反,必须有大量的 ReLU 激活,以允许网络构建一个完整的点地图。在多维空间中,校正的线性单元沿着类边界组合形成复杂的多面体。
ReLU 工作得如此好的原因在于:当有足够多的 ReLU 时,它们可以近似任何函数,就像其他激活函数一样,如 sigmoid 或 tanh,就像堆叠数百个乐高积木一样,而没有缺点。平滑曲线函数有几个问题是 ReLU 不会出现的,其中之一是计算导数或变化率,即梯度下降背后的驱动力,ReLU 比任何其他平滑曲线函数都要便宜得多。
另一个是 sigmoid 和其他曲线有一个消失梯度的问题;因为对于更大的绝对值 x ,sigmoid 函数的导数逐渐变缓。因为输入的分布可能在训练期间较早地偏离 0,所以导数将非常小,以至于没有有用的信息可以反向传播来更新权重。这通常是神经网络训练中的一个主要问题。
用德斯莫斯画的。
另一方面,ReLU 函数的导数很简单;它是输入所在直线的斜率。它将可靠地返回有用的梯度,尽管x= 0 {x0 }有时可能会导致“死神经元问题”,但总体而言,ReLU 仍然表现出不仅比曲线函数(sigmoid,tanh)更强大,而且比试图解决死神经元问题的 ReLU 变体(如 Leaky ReLU)更强大。
ReLU 设计用于大量工作;在大容量的情况下,它近似得很好,并且在近似得很好的情况下,它的表现与任何其他激活功能一样好,没有缺点。
如果你喜欢这个,
你可能也会喜欢我关于深度学习的一些其他文章。
[## 批量规范化:深度学习的最大突破
它是如何工作的——又是如何如此有效的?
towardsdatascience.com](/batch-normalization-the-greatest-breakthrough-in-deep-learning-77e64909d81d) [## 每个人的 GANs
革命性人工智能概念的直观解释
medium.com](https://medium.com/analytics-vidhya/gans-for-everyone-an-intuitive-explanation-of-the-revolutionary-concept-2f962c858b95)
如果你没有发现任何新的东西,你就没有做好 EDA
原文:https://towardsdatascience.com/if-you-dont-find-anything-new-you-don-t-do-eda-right-d356f9995098?source=collection_archive---------72-----------------------
更好的 EDA 指南
进行探索性数据分析或 EDA 的目的是在数据中发现新信息。从业者可能没有意识到的对 EDA 的理解是,EDA 使用直观检查的数据集来理解和总结数据集的主要特征,而无需事先假设或依赖统计模型。
照片由 Unsplash 上的 Aswathy N 拍摄
EDA 是制定新假设之前的关键步骤,然后可以使用后续分析对新数据集进行测试。做 EDA 的最大好处只有当它从数据集中检测到预期的并发现意外的时候才能实现。
最有趣的问题是如何解释可视化中的有趣模式,这些模式可以是图表、图形或地图的形式,并描述是什么导致了您观察到的模式。
考虑名为“对抗 20 世纪传染病:疫苗的影响的互动图片:
照片由华尔街日报
首先,我们马上注意到的是配色方案。只要看看颜色和写着疫苗引入的线,就已经提供了疫苗起作用的证据。
这就是我们在 EDA 阶段对可视化的期望,它是对数据的表示、探索和呈现,以促进理解。使一个 EDA 与另一个不同的是,它产生的可视化可以使理解变得容易。
照片由伯纳德·赫尔曼在 Unsplash 上拍摄
就像我说过的,可视化只是 EDA 的产物。更好的可视化形式是交互式的。单个可视化一次只能表示一个方面。在包含多个交互式可视化的仪表板中,用户可以使用多个表示来工作和指导、选择、筛选。只需点击链接,就可以使用简单的指示板进行操作,该指示板允许您交互式地播放各种时间方面的记录。
为什么想出有效的 EDA 这么难?你可以说,好吧,让我们导入一些库,然后画些东西。
能有多难?
因为数据有无数种可能的可视化表示。你的目标应该是创建有效的可视化效果,让用户无需更多解释就能真正理解。
制定 EDA 战略
制定 EDA 策略的方法有很多,取决于你的创造力水平。然而,良好的开端是检查您的数据并理解组成数据集的不同数据类型。
那么,到底是什么数据类型呢?它可以解释为以结构化的方式解释数据的方式。这里的数据类型不是指我们从编程中知道的数据类型的定义。
其次是任务抽象。让我给你一个例子来解释“任务”在可视化中意味着什么,以及为什么要抽象。“任务”做的第一件事是,当我们执行 EDA 时,它证明了使用可视化的合理性。有些人可能认为,当它勾选了指南中出现的所有检查清单时,就创建了一个完美的可视化,但是如果它没有提供洞察力并回答用户最初提出的问题,那么无论它有多漂亮,它对用户都没有多大用处。
这就是用户的“任务”的重要性。当用户想要分析数据时,考虑用户来自的领域,确定用户如何思考
我希望你能从这篇文章中学到一些新东西。
感谢您的阅读。对于任何想法或反馈,请随时通过给我的 Linkedin 发送消息来与我分享!
https://www.linkedin.com/in/faizfablillah/➡️
如果你认为 GPT-3 让编码员过时了,你可能就不会写代码了
原文:https://towardsdatascience.com/if-you-think-gpt-3-makes-coders-obsolete-you-probably-do-not-write-code-25fbc6461362?source=collection_archive---------14-----------------------
意见
反驳数据科学家和开发人员过时
来自 Pexels 的 Andrea Piacquadio 的照片
这是一篇观点文章。我很想在下面的评论中听到你的反驳。
乍一看,GPT 3 号可能是人类编写代码的终结。
虽然具有开创性,但得出软件工程师已经过时的结论还为时过早。
但是那些以写代码为生的人已经知道这一点了。
以下是开发人员留下来的几个原因。
开发人员写代码没有报酬
我们解决问题是有报酬的。
代码是解决问题的一种手段,但它也是一种繁重的工作和不可避免的罪恶。
代码不是解决方案,它是解决方案的实现
- 大卫·威廉姆斯
编写代码是开发人员最容易的部分。鉴于多层次的限制,澄清和解决一个问题要困难得多。
这就是写代码和写软件的区别。
GPT-3 太复杂,无法扩展
GPT-3 需要大量的专业知识,对个人和小企业来说运行起来太昂贵了。
API 底层的模型非常庞大,需要大量专业知识来开发和部署,并且运行起来非常昂贵。这使得除了大公司之外的任何人都很难从底层技术中受益。
- OpenAI
OpenAI 给出了这个模型只能通过 API 获得而不能开源的三个原因之一。
它是巨大的。在1750 亿个参数,GPT-3 让“只有”15 亿个参数的 GPT-2 相形见绌。
鉴于穆雷斯定律的死亡,在不久的将来,我们不会看到这样的模型在个人电脑上运行。
也就是说,我认为这是一个暂时的问题。模型只能变得如此之大,但仍然具有成本效益。在某种程度上,我们将不得不开发更小/更智能的模型,即使这需要完全不同的硬件。
我们还不信任人工智能
银行或军事技术中的错误代码显然是有问题的。
但是你的普通网络应用程序中的安全漏洞也可能是灾难性的(比如 T4 的 Equifax T5)。
正如我们在自动驾驶汽车上看到的那样,在我们信任人工智能之前,它必须比最好的人类更好。
围绕自动驾驶汽车的最大问题之一不是技术问题,而是哲学问题:有多安全才算足够安全?
- 重新编码
人类通常将这种约束应用于高风险领域。但是考虑到 2020 年代码会渗透到大部分技术中,有很大的下降空间。
从技术上讲,我们可以通过审查 AI 的代码来克服这一点。但是当人类再也看不懂代码的时候会发生什么呢?
没有“软件工程师”,所有技术都是黑箱
如果不再有人编码,我们能理解 AI 写的代码吗?
有了 AI 写软件,我们就不会投入擅长它所需的数千小时。如果不擅长,怎么复习。
在这个知识丢失的场景中,所有技术本质上都变成了一个黑盒。
作为一个社会,我们是否愿意走到这一步?
在一个不太遥远的场景中,第三方拥有的人工智能和人类无法维护的代码的组合感觉像是一个巨大的商业风险。
GPT-3 创建了无代码编码的文本接口
GPT 三号创建了一个布局生成器。
虽然在无监督学习的背景下令人印象深刻,但通过文本放置按钮的能力感觉像是人工智能编写代码的一个非常小的胜利。
- 自 90 年代以来,我们就有了产生 HTML 和 CSS 的拖放式 web 编辑器。
- 代码自动化自从软件出现以来,代码就一直存在并不断发展。
甚至像 Ruby on Rails 这样的框架也是为了节省时间而设计的,它们在应用程序开发过程中尽可能多地构建通用代码。
因此,虽然我怀疑 GPT 3 号很快会有更复杂的用例,但我们目前看到的并不完全是软件工程师的工作。
结论
首先,撇开反驳不谈,GPT-3 是不可思议的。如果我们没有不断提高标准,我们可能已经认为这是“真正的人工智能”了。
其次,虽然我相信写代码会一直存在,但是花更少的时间写代码是一件很棒的事情。
不像盖房子,有无限多的代码要写,有无限多的问题要用技术来解决。
将编码委托给人工智能的能力给了工程师处理更多问题的带宽。
如果您的 Python 代码抛出错误,请先检查这些内容
原文:https://towardsdatascience.com/if-your-python-code-throws-errors-check-these-things-first-a93d8a9036f1?source=collection_archive---------26-----------------------
忘记关闭大括号或添加缩进?许多错误都有一个简单的修复方法
Python 不需要成为你的敌人。作者图片
F 快失败,早失败——我们都听说过这句格言。尽管如此,当你写了一段漂亮的代码,却发现它并不像你预期的那样工作时,还是会很沮丧。
这就是单元测试的用武之地。检查你的每一段代码有助于你定位和修复你的错误。
但并非所有的缺陷都是一样的。有些 bug 是意想不到的,根本看不出来,即使是有经验的开发人员也很难修复。这些更有可能发生在大型复杂的项目中,尽早发现它们可以为您节省大量的时间。
其他错误是微不足道的,比如当你忘记了一个右括号或者弄乱了一些缩进。它们很容易修复,但是很难发现,特别是当你已经在代码上工作了一段时间或者在深夜的时候。
一旦你发现了这样一个 bug,这就有点像掌脸时刻了。你可能会因为没有早点发现这一点而自责不已——并且你会想当初为什么会犯下如此愚蠢的错误。他们也不是你希望你的同事在你之前发现的那种错误。
[## Python 初学者应该避免的 4 个常见错误
我很艰难地学会了,但你不需要
towardsdatascience.com](/4-common-mistakes-python-beginners-should-avoid-89bcebd2c628)
首先要检查的愚蠢但常见的错误
我不能说这个列表涵盖了你可能会犯的所有愚蠢的错误。然而,定期使用它至少可以帮助您消除最常见的错误。
忘记右括号了吗?
每个人都会遇到这种情况——你快乐地编码,在编码的过程中,你忘记了关闭数组、参数列表或任何你正在处理的东西。
一些开发人员一打开右大括号就键入右大括号,然后填充中间的空格。大多数现代 IDE 也自动关闭牙套——因此,如果忘记牙套是你的一种慢性病,你可能会考虑放弃你的旧 IDE。
你的结肠呢?
嘿,我们每个人都会遇到这种事。您正在构造一个新的类,由于它很复杂,您在键入代码时已经在考虑类的内容了。哎呀,你忘了结尾的冒号:
class SillyMistake():
def AvoidThis():
print("Don't do silly mistakes!")
一个很好的经验是,如果你要增加一行的缩进量,你需要在它前面的那一行加一个冒号。
你是否混淆了等式和赋值运算符?
什么时候用=
,什么时候用==
?根据经验,如果你正在检查或比较两个值,你将使用==
。另一方面,如果你给一个变量赋值,你将使用=
。
在你对错误感到恐慌之前,检查一下那些微不足道的错误。作者图片
你是否混淆了易变的表达式?
看看这个:
def NotRecommendedCode(item, new=[]):
new.append(item)
这本身没有错,但是如果你不小心的话,你会遇到麻烦的。将参数列表中的数组[]
定义为可变的意味着它将被修改以用于将来的调用。
但是你可能每次都想从一个空数组开始。这更好:
def RecommendedCode(item, new=None):
if new is None:
new **=** []
new.append(item)
Python 是区分大小写的,记得吗?
这是非常琐碎的事情,但却经常发生。假设您已经定义了变量CamelBucket
,稍后在代码中调用camelbucket
。
没用,是吧?很有可能,您非常清楚 Python 是区分大小写的,但是您只是忘记了按 shift 键。
大多数现代的 ide 可以通过提出聪明的建议来帮助你避免这个错误。因此,如果你容易打字错误,你可以考虑升级你的文本编辑器。
[## 为什么开发人员会爱上函数式编程
从 Python 到 Haskell,这种趋势不会很快消失
towardsdatascience.com](/why-developers-are-falling-in-love-with-functional-programming-13514df4048e)
你在迭代列表的时候修改列表了吗?
这可能发生在每个初级开发人员身上:你已经建立了一个列表,现在你想改变一些事情。没什么大不了的,对吧?
不对。考虑一下这个:
mylist = [i for i in range(10)]
for i in range(len(mylist)):
if i%2==0: del mylist[i]
剧透:这抛出了一个错误,因为你最终迭代了一个不再存在的列表中的条目。与其从现有列表中删除,不如考虑写入一个新列表。
你有循环模块依赖吗?
这是如此琐碎,但也是常见的:在一个文件中,比如说bug.py
,你写import anotherbug
。在另一个文件anotherbug.py
中,你写import bug
。
这是行不通的——计算机怎么知道哪个文件包含在哪个文件中呢?你可能知道这一点,但这样做是偶然的。没关系,但是尽快修理它!
你还没有像 Python 的标准库那样命名你的模块,对吧?
关于模块的另一件事:Python 附带了大量惊人的库模块。但是如果你需要创建你自己的,一定要给他们一个原始的名字。
例如,定义一个名为numpy.py
的模块会导致混乱。如果您不确定可以使用哪些名称,这里有 Python 标准库的完整列表。
一天一个好习惯,代码医生远离我。作者图片
如何避免愚蠢的错误
你从错误中学习。作为一名初级开发人员,您可能每天都在犯这样的错误。但是随着时间的推移,它们变得越来越少。
你也可以更进一步,养成一些健康的习惯,这样你就不会犯小错误了。这些只是许多技巧中的一小部分,但是它们可以帮助你避免许多错误。
总是初始化变量
如果你定义了一个变量并且直到后来才赋值,Python 代码可以完美地工作。因此,每次在定义时初始化它们似乎很麻烦。
但是不要忘记你也是人,因此你很容易忘记哪个变量被赋值了,哪个没有。此外,初始化变量迫使您考虑您正在处理的变量的类型,这可能会防止以后出现错误。
这就是为什么许多经验丰富的开发人员用默认值初始化每个变量,如0
、None
、[]
、""
等等。
当然,如果你只是想绑定一个小的独立脚本,而不是在一个庞大的项目上工作,你可以允许自己快速草率地做事。但是当事情变得复杂时,记得要勤奋。
用大括号调用函数,避免麻烦
又是牙套。不知何故,它们与人类大脑的迟钝不相容。
在 Python 中,你总是需要像这样调用函数:callingthisfunction()
,而不是像这样:notcallingthisfunction
,不管有没有参数。听起来微不足道,但并不总是如此!
例如,如果您有一个名为file
的文件,并且您想用 Python 关闭它,那么您将编写file.close()
。但是,如果您编写file.close
,代码将运行而不会抛出错误——除了它不会关闭文件。试着在一个有数千行代码的项目中找出这样的错误…
经过几天的练习,它变得相当自动,所以从今天开始:函数总是用括号调用!
虫子可能会令人生畏,但随着时间的推移,你会学会避开它们。作者图片
导入模块时不要使用扩展
同样,这是相当琐碎的,但经常被初学者忘记。如果你正在调用一个模块,你将永远不会使用一个扩展,不管它是来自 Python 标准库还是其他地方。
因此,请注意类似这样的语句:
import numpy.py *# don't do that!!!*
删除那个.py
扩展名,并像避开臭味一样避开它。扩展和导入不能一起工作。
一致缩进
同样,您可能知道自己需要在整个文件中以同样的方式建立索引。但是,当你与他人合作或在深夜时,这仍然是一个常见的陷阱。
在开始编码之前,决定是使用制表符还是空格,以及使用多少空格。最常见的约定是四个空格,但是您可以做任何最适合您的事情——只要您在整个文档中保持它。
另一方面,如果您在一行中使用空格,在另一行中使用制表符,有时 Python 不会像您预期的那样处理它。我知道这看起来像是一个愚蠢的工作,但是尽你所能努力去做。
[## 面向对象编程已经死了。等等,真的吗?
函数式编程的鼓吹者们,你们把枪口对准了错误的敌人
towardsdatascience.com](/object-oriented-programming-is-dead-wait-really-db1f1f05cc44)
底线:为你自己和你的同事避免面部表情
快速失败,尽早失败——但不要愚蠢地失败。每个人都会犯错误,但还是要避免那些愚蠢的错误,或者至少快速改正它们。
不仅仅是为了进步。这也是为了避免 face palm-时刻,你会因为没有早点发现错误而自责。
这也是为了不失去你的名声——谁愿意带着一个看似复杂的问题跑去找同事或经理,却发现这个问题其实很容易解决?
很有可能你已经犯了我在过去提到的一些错误。冷静,伙计,我也有——我们是人。
关键是,如果您的代码以意想不到的方式运行,您将希望首先检查琐碎的事情。然后,如果您的代码仍然不能正常工作,您可以随时 ping 您的同事。
如果你对 GPT-3 编写代码感到兴奋,那你一定没听说过 NAS
原文:https://towardsdatascience.com/if-youre-hyped-about-gpt-3-writing-code-you-haven-t-heard-of-nas-19c8c30fcc8a?source=collection_archive---------15-----------------------
来源: Unsplash
自动化人工智能发现和研究
GPT-3 最近因展示人类语言代码翻译的病毒视频而成为机器学习社区的头条新闻。该模型可用于自动化许多冗余和重复的编码,例如在 HTML/CSS 中或在简单神经网络的构造中。正如许多其他人所指出的,GPT-3 只是一个工具,并且受到其训练数据的限制。
开发人员想做的许多更复杂的事情——例如,给网站添加一些特定的基于动量的动画——GPT-3 都做不到,因为它 a)不够先进,b)没有足够的训练数据,c)毫无疑问不是“创造性的”。GPT-3 可能会成为一个有用的工具,帮助开发人员花更少的时间重新输入旧命令,花更多的时间集思广益创造性的基础设施设计和调试复杂的跨系统错误,但它绝不是对程序员生计的威胁。
特别令人感兴趣的是一个演示,其中 GPT-3 在流行的高级深度学习库 Keras 中为一个神经网络编写了代码,给定一个自然文本输入,如“我有一个 60 乘 60 像素图像的数据集,想将它们分为 10 类”。然后,GPT-3 会吐出默认代码来构建一个基本的模板卷积神经网络。
GPT-3 为神经网络“编写代码”很像一个初学者试图打出他们从教程中记住的样本代码:它不关心它的性能或使它的架构适合数据集,它只是试图根据它早先在训练数据集中看到的内容来预测下一个最有可能的字符。尽管这是一种直白的说法,但使用 GPT-3 编写代码就像在 StackOverflow 上自动搜索简单的代码片段一样。也许它会在代码自动完成中获得商业上的成功。
然而,这个想法,人工智能学习创造人工智能,并不新鲜,在 2002 年就有了第一个正式的概念。虽然 GPT-3 将以前在广泛的代码处方中看到的代码回流,但神经架构搜索(简称 NAS)通过实际搜索数据集的最佳神经网络,将这一想法推向了一个新的水平。机器学习工程师的大部分工作都依赖于基于直觉和经验测试一些万亿级的潜在神经网络结构,而 NAS 可以大大降低这一成本。
NAS 通过智能搜索人类从未想到的潜在结构,利用 AI 创造更好的新 AI。不是概括,是发现。
通常,NAS 的实施有三个组件:
- 一个搜索空间。这是神经结构搜索可以探索潜在结构的空间。该空间由作者定义的一组可能的层(回旋、汇集、密集、下降等)界定。)以及它们如何连接(跳过、层大小等。).搜索空间的轮廓需要一些人类经验。
- 一种搜索算法。NAS 算法首先对几个候选网络架构进行采样,并根据这些“子模型”的性能,通过准确性或延迟(运行时间)等指标,获得奖励或惩罚。人类可以指定哪些度量更重要;例如,如果需要一个小而轻的模型,即使以精度为代价,更小的尺寸和更好的延迟可能会获得更高的回报。
- 一种进化策略。NAS 必须测量和预测许多提议的子模型的性能,以获得反馈供其学习。这可以通过训练每个子模型来完成,但是这显然是非常昂贵的,并且已经提出了新的方法来节省计算和时间资源。
作者创建的图像。
从这个意义上说,NAS 类似于一个人解决搜索问题的典型 RL 方法,但是做了一些修改以适应其任务。
首先,神经网络结构有许多潜在的表现形式。由于 NAS 围绕着这种复杂结构的选择和进化,所以找到一种合适的方法来呈现它是非常重要的。因为将神经网络表示为实际的、物理的神经网络是非常低效的,所以已经有许多对架构的描述,这些架构额外支持更容易的进化。
一种方法是基于细胞的方法,受现代图像识别模型(如 Inception)的设计启发,在该方法中,人类决定堆叠两种类型细胞的一般架构:正常细胞(输入和输出具有相同的维度)和缩减细胞(输出是输入的 1/2)。在网络框架(细胞序列,可能是多个背靠背的同一个细胞)设置好之后,NAS 创建这两个细胞的结构。
来源: Zoph 等人 2018 ,图片免费分享。
该模型具有高度的可移植性,因为架构不是特定于数据集的,可以用其他实现来实现,并且在训练时速度更快。然而,基于细胞的表示高度依赖于良好的人类细胞构造。
另一种方法是将神经网络表示为计算图或“主题”。1 级操作——原始的 1x1 卷积或汇集,可以组装成 2 级主题,并递归到更高级别的网络结构上。这种方法类似于基于单元的结构,因为它聚合了几个更小的结构,但它允许更大的复杂性。
来源: 刘等人 2017 ,图片免费分享。
这种表示方法可以被认为更“自然”,因为神经网络本质上是单向图。此外,它可以产生有趣的非对称神经网络。然而,这种表示的存储和操作成本很高。
对于已建立的神经网络表示,仍然存在许多算法来(有时)创建和选择架构。令人惊讶的是,随机搜索——从空间中随机选择一个网络——是设计良好的搜索空间难以超越的基准。
强化学习常用于 NAS 方法。基于强化学习的“控制器”生成/建议要评估的子模型架构。这个控制器是一个递归神经网络,它学习生成潜在神经网络的基于令牌的顺序表示。RNN 随着潜在架构的性能而更新,并被训练以输出更好且性能更好的网络。
来源:Zoph&Le 2017,图片免费分享。
这至少在概念上类似于 GPT 3 号正在做的事情:模拟一个序列。然而,在 NAS 中,RNN 连接到强化学习系统,因此它根据数据集学习生成性能良好的网络。可以认为是自动化人类研究人员所做的事情——利用已知的现有想法并探索新想法。
另一种搜索算法是基于进化的,其中架构被表示为“基因”。突变包括单独添加连接和节点;来自父体系结构的两个体系结构可以“配对”或“交叉”以形成子结构。通过巧妙的方法来减少搜索空间并控制种群数量,如淘汰旧模型,NAS 的进化方法已被证明是相对有效的,尽管比 RL 方法更昂贵。
来源: 斯坦利&米库莱宁 。图片免费分享。
虽然 NAS 仍处于早期,其沉重的计算成本限制了广泛的访问,但人工智能指导的人工智能研究很可能会变得更加流行。尤其是像上面讨论的基于主题(图形)的流体表示,AI 可能会在 LSTMs 或 Dropout 这样的聪明机制中找到新的发现。另一个有趣的思想实验是考虑如果 NAS 系统的发现被用来改进发现它的系统会发生什么。
此外,神经架构搜索正在扩展到深度无监督学习,由于标签和监督学习的成本,这是非常重要的。此外,在神经网络的框架之外, Auto-ML Zero 将 NAS 扩展到发现新的机器学习算法,如 SVM 或决策树,使用简单的数学运算作为构建模块和老化进化策略。
NAS 不会是机器学习研究人员的棺材上的钉子,就像 GPT-3 不会是开发人员工作的死刑判决一样。当自动计算器被发明时,人类计算器开始使用计算器来提高他们在金融机构和会计的新工作中的效率,人类总是会有所贡献。同样,技术发展的道路将继续向前,突破是帮助进一步增长的工具。
人工智能发现的自动化令人难以置信地有前途,并有可能使机器学习的能力远远超出人类思维的限制。
点燃火花!
原文:https://towardsdatascience.com/ignite-the-spark-68f3f988f642?source=collection_archive---------7-----------------------
使用 PySpark 在 Kubernetes 上运行 Apache Spark
来源:免费图片来自 Pixabay
简介
在这本初级读本中,您将首先了解一些 Apache Spark 的集群管理器是如何工作的,然后了解如何在现有的 Kubernetes (k8s)集群上以交互方式在 Jupyter 笔记本中运行 PySpark。
完成本文后,您应该能够自信地在任何 Kubernetes 集群上开发 Spark 应用程序,并对两者之间的联系有更深的理解。
这都是关于环境的
来源:Apache 文档
Spark 是一个快速通用的集群计算系统 ,这意味着根据定义,计算是以分布式方式在许多互连的节点上共享的。
但是 Spark 实际上是如何在集群中分配给定的工作负载的呢?
Spark 采用主/从方式,其中一个驱动程序(“主”)创建一个 SparkContext 对象,该对象连接到一个集群管理器。
创建的 SparkContext(由您喜欢的语言绑定以编程方式创建,或者在您提交作业时以您的名义创建)将集群抽象为一个大型计算节点,您的应用程序使用它来执行工作。
另一方面,集群管理器负责生成和管理大量工作节点(“从节点”),每个节点代表 SparkContext 运行一个执行器进程来完成实际工作。
当 Spark 应用程序操作弹性分布式数据帧(RDD) 形式的数据时,RDD 被分割成多个分区,分布在这些工作节点/执行器组合上进行处理。最终结果在节点间汇总并发送回驱动程序。
选择一个管理器,任何集群管理器
这种设计的一个主要优点是集群管理器与应用程序是分离的,因此可以互换。
传统上,Spark 支持三种类型的集群管理器:
- 独立
- 阿帕奇 Mesos
- Hadoop YARN
独立集群管理器是默认的集群管理器,随 Spark 的每个版本一起提供。这是一个没有虚饰,有能力的经理,这意味着让你尽快启动和运行。
Apache Mesos 本身就是一种集群技术,旨在将集群的所有资源抽象化,就像一台大型计算机一样。Mesos 附带了一个集群管理器,您可以在 Spark 中利用它。
Hadoop YARN (“又一个资源谈判者”)是作为 Apache Hadoop 项目的副产品开发的,主要专注于分发 MapReduce 工作负载。因此,它也是 Spark 可以与之进行本地对话的集群管理器。
进入 Kubernetes
来源:Apache 文档
从 2.3.0 开始,Spark 现在支持直接使用 Kubernetes 作为集群管理器。
但是那个到底是什么意思呢?
这取决于你想如何在 Kubernetes 上运行 Spark。
在集群模式中,在您使用 spark-submit 提交应用程序之后,代表您的应用程序创建的 SparkContext 将要求 kube-apiserver 设置一个驱动程序节点和一些相应的工作节点(Pods ),并在它们之上运行您的工作负载。
一旦所有数据处理完成,在拆除时,临时工作节点窗格将自动终止,但驱动程序节点窗格将保留,以便您可以在手动删除它之前检查任何日志。
在客户端模式中,您创建 Spark driver 节点作为 Pod,然后在最终提交工作之前,使用您最喜欢的语言的 API 绑定创建 SparkContext。
出于几个原因,本初级读本将着重于配置客户端模式:
- web 上的大多数文档都是以集群模式提供的
- 使用 spark-submit 提交应用程序的步骤与使用现有的集群管理器没有什么不同
- 大多数 Spark 开发人员都希望使用客户机模式,这样他们就可以开发新的应用程序并交互式地探索数据
先决条件
以下说明假设如下:
- 您对 Kubernetes 集群或该集群中的专用名称空间拥有管理权限
我正在运行一个小型的 1.17.2 八节点集群,它是使用 kubeadm 工具安装的。我相信任何集群 1.15+都应该工作得很好。
如果你没有集群,我强烈建议在你的桌面上安装 MiniKube 作为一种跟进方式。
- 您对 Kubernetes 的核心概念有些熟悉,比如 pod、部署和服务
- 您已经安装了 Docker,理解了容器背后的概念,并且能够轻松地使用 Docker 文件构建自定义映像
- 您可以访问内部、云中或外部的 Docker 注册中心,如 Docker Hub 。
- 在撰写本文时,您使用的是 Spark 的最新稳定版本 v2.4.4
集群设置
在我们开始构建映像和部署 Pods 之前,让我们设置一个专用的名称空间,我们的 combo Jupyter notebook/Spark driver 节点以及所有对应的 worker 节点都将部署在这个名称空间中。
$ kubectl create namespace spark
namespace/spark created
为了让你的驱动程序 Pod 从集群中请求资源,它必须使用某种类型的服务帐户,该帐户拥有正确的角色基础访问控制(RBAC) 。
Spark 文档建议创建一个角色绑定或集群角色绑定来完成这个任务。选择实现哪种方法完全取决于集群的安全策略。
对于本例,我们将在 spark 名称空间 中创建一个名为 spark 的 ServiceAccount,并使用 ClusterRoleBinding 赋予该帐户适当的权限:
$ kubectl create serviceaccount spark -n spark
serviceaccount/spark created$ kubectl create clusterrolebinding spark-role --clusterrole=edit --serviceaccount=spark:spark --namespace=spark
clusterrolebinding.rbac.authorization.k8s.io/spark-role created
Docker 设置
如上所述,由于每个 worker 节点实际上只是我们集群中的一个 Pod,所以我们需要一个 docker 映像和正确的 Spark 运行时。
幸运的是,Spark 团队提供了一组 Dockerfiles 和一个工具( docker-image-tool.sh )来创建映像,这些映像可以部署在任何 Kubernetes 集群上。
每个 docker 文件都要与 Spark 支持的三种主要语言绑定一起使用——Scala、Python 和 r。
让我们构建默认图像,这样我们至少有一些基础图像可以使用:
$ wget -qO- [http://mirrors.gigenet.com/apache/spark/spark-2.4.4/spark-2.4.4-bin-hadoop2.7.tgz](http://mirrors.gigenet.com/apache/spark/spark-2.4.4/spark-2.4.4-bin-hadoop2.7.tgz) | tar -xzf -$ cd spark-2.4.4-bin-hadoop2.7 && bin/docker-image-tool.sh build
Sending build context to Docker daemon 262.2MB
...
Successfully built 02fb36ac3ee0
Successfully tagged spark:latest
Sending build context to Docker daemon 262.2MB
...
Successfully built 0d33be47d094
Successfully tagged spark-py:latest
Sending build context to Docker daemon 262.2MB
...
Successfully built dc911ac3678e
Successfully tagged spark-r:latest
docker-image-tool.sh 脚本完成后,您应该有三个新的映像准备好部署在 Kubernetes 集群上:
$ docker images spark:latest
REPOSITORY TAG IMAGE ID CREATED SIZE
spark latest 02fb36ac3ee0 7 days ago 344MB$ docker images spark-py:latest
REPOSITORY TAG IMAGE ID CREATED SIZE
spark-py latest 0d33be47d094 7 days ago 432MB$ $ docker images spark-r:latest
REPOSITORY TAG IMAGE ID CREATED SIZE
spark-r latest dc911ac3678e 7 days ago 539MB
由于本教程将集中使用 PySpark,我们将为我们的 worker Pod 使用 spark-py 图像。通常,您只需将这些映像推送到集群使用的任何 docker 注册表中。但是,我们将创建它们的自定义版本,以便解决一个 bug。
通信故障
在撰写本文时,由于最近修补了一个安全 CVE,在较新的集群上使用现有的 Spark v2.4.4 Kubernetes 客户端 jar 文件存在问题。
你可以在这里和这里阅读更多关于这个问题的信息。
解决方法是构建一个启用 Spark 的 docker 映像,但是其中包含较新的客户机 jar 文件。
让我们使用我们刚刚构建的基础映像来实现这一点。因为我们关注的是使用 PySpark,所以我们将只重建 spark-py 映像,但是相同的步骤适用于所有映像。
$ mkdir my-spark-py && cd my-spark-py$ wget -qO- [http://apache.mirrors.hoobly.com/spark/spark-3.0.0-preview2/spark-3.0.0-preview2-bin-hadoop2.7.tgz](http://apache.mirrors.hoobly.com/spark/spark-3.0.0-preview2/spark-3.0.0-preview2-bin-hadoop2.7.tgz) | tar -xzf -$ cp spark-3.0.0-preview2-bin-hadoop2.7/jars/kubernetes*jar . && rm -f [spark-3.0.0-preview2/spark-3.0.0-preview2-bin-hadoop2.7.tgz](http://apache.mirrors.hoobly.com/spark/spark-3.0.0-preview2/spark-3.0.0-preview2-bin-hadoop2.7.tgz)$ cat << EOF > Dockerfile
FROM spark-py:v2.4.4COPY *.jar /opt/spark/jars/
RUN rm /opt/spark/jars/kubernetes-*-4.1.2.jar
EOF$ docker build --rm -t my-spark-py:v2.4.4 .
...
Successfully built 29abceec5cb2
Successfully tagged my-spark-py:v2.4.4
所以现在 my-spark-py:v2.4.4 包含了相同的官方 spark 运行时,但是更新了来自 v3.0.0-preview2 版本的 Kubernetes 客户端 jar 文件。
每当我们生成 SparkContext 时,Kubernetes 会根据需要创建一个 worker Pod,并提取该图像。
创建“驱动程序”窗格图像
因为我们的最终目标是在 Jupyter 笔记本中的 Kubernetes 上交互开发 PySpark 应用程序,所以这个 Pod 也将充当我们的“驱动程序”节点。
因此,您有两种选择:
- 使用官方 Jupyter PySpark 图片
- 打造自己的定制形象
PySpark docker 官方图片是一个很好的起点,我强烈推荐它。
我创建了一个自定义的 docker 图片,你可以点击上面的链接找到它。我把这张图片叫做我的笔记本:最新的。
您应该构建、标记和推送您最终选择的任何映像到您的集群使用的 docker 注册表。
设置您的部署
正如 Kubernetes 的所有事情一样,我们需要编写一个 YAML 文件来部署我们的驱动程序 Pod,它将用于登录 Jupyter 并创建一个 SparkContext。
以下是我的部署情况:
apiVersion: apps/v1
kind: Deployment
metadata:
**namespace: spark**
name: my-notebook-deployment
labels:
app: my-notebook
spec:
replicas: 1
selector:
matchLabels:
app: my-notebook
template:
metadata:
labels:
app: my-notebook
spec:
**serviceAccountName: spark**
containers:
- name: my-notebook
image: pidocker-docker-registry.default.svc.cluster.local:5000/my-notebook:latest
ports:
- containerPort: 8888
volumeMounts:
- mountPath: /root/data
name: my-notebook-pv
workingDir: /root
resources:
limits:
memory: 2Gi
volumes:
- name: my-notebook-pv
persistentVolumeClaim:
claimName: my-notebook-pvc
---
apiVersion: v1
kind: Service
metadata:
namespace: spark
name: my-notebook-deployment
spec:
selector:
app: my-notebook
**ports:
- protocol: TCP
port: 29413
clusterIP: None**
突出显示的文本是您应该注意的指令:
- 我们希望我们的部署存在于 spark 名称空间中
- 我们希望将这个部署的 ServiceAccount 设置为 spark ,它具有我们在上面创建的 ClusterRoleBindingspark-role
- 根据 Spark 客户端模式文档,我们希望通过将服务的 ClusterIP 设置为 None 来将我们的部署公开为一个无头服务。这允许工人舱通过端口 29413 与司机舱通信。您选择的端口号可以是任何有效的未使用的端口,但是我在这个例子中选择 29413 作为对这个有用帖子的敬意。
创建部署
当您对部署感到满意时,通过以下方式创建它:
$ kubectl create -f k8s/deploy.yaml
deployment.apps/my-notebook-deployment created
service/my-notebook-deployment created
检查您的部署是否正在运行:
$ kubectl get all -n spark
NAME READY STATUS RESTARTS AGE
pod/my-notebook-deployment-7bf574447c-pdn2q 1/1 Running 0 19sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/my-notebook-deployment ClusterIP None <none> 29413/TCP 19sNAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-notebook-deployment 1/1 1 1 19sNAME DESIRED CURRENT READY AGE
replicaset.apps/my-notebook-deployment-7bf574447c 1 1 1 19s
现在将端口转发到 8888,这样您就可以登录 Jupyter:
$ kubectl port-forward -n spark deployment.apps/my-notebook-deployment 8888:8888
Forwarding from 127.0.0.1:8888 -> 8888
Forwarding from [::1]:8888 -> 8888
现在,您应该能够将您最喜欢的浏览器指向 http://localhost:8888 并登录 Jupyter(如果您使用我的图像作为参考,密码是‘Jupyter’,否则您将需要复制/粘贴在容器的日志文件中显示的生成的 auth token)。
点燃它!
启动一个新的 Python3 笔记本,并在单元格中键入以下内容:
import osfrom pyspark import SparkContext, SparkConf
from pyspark.sql import SparkSession# Create Spark config for our Kubernetes based cluster manager
sparkConf = SparkConf()
sparkConf.setMaster("**k8s://**[**https://kubernetes.default.svc.cluster.local:443**](https://kubernetes.default.svc.cluster.local:443)**"**)
sparkConf.setAppName(**"spark"**)
sparkConf.set(**"spark.kubernetes.container.image", "pidocker-docker-registry.default.svc.cluster.local:5000/my-spark-py:v2.4.4"**)
sparkConf.set(**"spark.kubernetes.namespace", "spark"**)
sparkConf.set(**"spark.executor.instances", "7"**)
sparkConf.set("spark.executor.cores", "2")
sparkConf.set("spark.driver.memory", "512m")
sparkConf.set("spark.executor.memory", "512m")
sparkConf.set("spark.kubernetes.pyspark.pythonVersion", "3")
sparkConf.set(**"spark.kubernetes.authenticate.driver.serviceAccountName", "spark"**)
sparkConf.set(**"spark.kubernetes.authenticate.serviceAccountName", "spark"**)
sparkConf.set(**"spark.driver.port", "29413**")
sparkConf.set(**"spark.driver.host", "my-notebook-deployment.spark.svc.cluster.local**")# Initialize our Spark cluster, this will actually
# generate the worker nodes.
spark = SparkSession.builder.config(conf=sparkConf).getOrCreate()
sc = spark.sparkContext
让我们来分解一下上面的 Spark 配置:
- 我们将主 URL 设置为"k8s://https://Kubernetes . default . SVC . cluster . local:443",这告诉 Spark 我们的集群管理器是 Kubernetes (k8s)并且在哪里可以找到请求资源的 kube-apiserver
- 我们将应用程序名称设置为“spark”,它将被添加到 worker Pod 名称的前面。你可以随意使用任何你认为合适的名字
- 我们将 worker Pods 使用的容器映像设置为我们在上面构建的自定义映像,其中包含更新的 Kubernetes 客户端 jar 文件
- 我们指定希望所有工作节点都创建在 spark 名称空间中
- 我将工作节点的数量设置为“7”,因为我在八个节点上(一个节点是主节点,七个可用于工作)。您需要选择一个对您的集群有意义的数字。
- 我们将驱动程序和工人的服务帐户名称设置为我们在上面创建的服务帐户(“spark”)
- 最后,我们将端口号设置为 29413 ,并将集群内服务的完全限定域名指定给工作节点,以便联系驱动节点。
在执行这个单元之后,您应该会看到 spark 名称空间中产生了许多 worker Pods。
让我们检查一下:
$ kubectl get pods -n spark
NAME READY STATUS RESTARTS AGE
my-notebook-deployment-7bf574447c-pdn2q 1/1 Running 0 5m6s
spark-1581043649282-exec-1 1/1 Running 0 76s
spark-1581043651262-exec-2 1/1 Running 0 75s
spark-1581043651392-exec-3 1/1 Running 0 75s
spark-1581043651510-exec-4 1/1 Running 0 75s
spark-1581043651623-exec-5 1/1 Running 0 75s
spark-1581043656753-exec-6 1/1 Running 0 70s
spark-1581043656875-exec-7 1/1 Running 0 70s
您现在有一个运行在 Kubernetes 上的 Spark 集群等待工作!
按照传统,让我们在新的单元格中使用我们的集群来计算数字 Pi:
from random import random
from operator import addpartitions = 7
n = 10000000 * partitionsdef f(_):
x = random() * 2 - 1
y = random() * 2 - 1
return 1 if x ** 2 + y ** 2 <= 1 else 0count = sc.parallelize(range(1, n + 1), partitions).map(f).reduce(add)
print("Pi is roughly %f" % (4.0 * count / n))
您的输出应该是:
Pi is roughly 3.141397
熄灭火花
完成开发后,只需执行以下命令就可以关闭 Spark 集群:
sc.stop()
这将指示您的 SparkContext 通过 kube-apiserver 拆除上面创建的 worker Pods。
要验证:
$ kubectl get pods -n spark
NAME READY STATUS RESTARTS AGE
my-notebook-deployment-7bf574447c-pdn2q 1/1 Running 0 20m
spark-1581043649282-exec-1 0/1 Terminating 0 16m
spark-1581043651262-exec-2 1/1 Terminating 0 16m
spark-1581043651392-exec-3 1/1 Terminating 0 16m
spark-1581043651510-exec-4 0/1 Terminating 0 16m
spark-1581043651623-exec-5 0/1 Terminating 0 16m
spark-1581043656753-exec-6 0/1 Terminating 0 16m
spark-1581043656875-exec-7 0/1 Terminating 0 16m
现在您只剩下 spark 名称空间中的部署/驱动程序 Pod。
结论
我希望您现在更有信心使用 PySpark 在 Kubernetes 上创建 Spark 集群,并欣赏其背后的架构。
我知道在我早期的 Spark 开发工作中,大部分时间我都是在独立模式下运行的,但是现在有了 Kubernetes 的支持,它要么集群,要么破产!
欢迎在下面发表任何问题、更正或担忧,祝您愉快!
“我会在半路上遇见你”——使用数据科学来揭露你的朋友告诉你的谎言
原文:https://towardsdatascience.com/ill-meet-you-halfway-using-data-science-to-expose-the-lies-your-friends-are-telling-you-c028b4262091?source=collection_archive---------60-----------------------
使用 API 和空间分析来优化和规划你的“新常态”社交生活
马克·克劳斯在 Unsplash 上拍照
一款应用程序,使用 API 和空间分析来优化与朋友见面时的旅行
回到 2019 年,我开发了一个应用程序,它很快给了我和我的朋友们一个“最好的”见面地点的列表。这包括了地点的类型,每个人的旅行时间,评论,价格等等。虽然当时我把它作为“一点乐趣”和在空间分析中更深入地使用 API 的有用尝试,但我希望随着我们进入“新常态”,它可能会比以往任何时候都更有用,作为一种减少每个人旅行并找到新的和不那么拥挤的见面地点的手段。
这是我在 Alteryx 的开发过程中写的,作为我如何构建应用程序的指南,这是在一个预先锁定的世界里……所以,让我们回到 2019 年,一个更好的时间,那时我们可以出去,感觉安全地去见朋友。
(虽然重读了一些,但我当时似乎还是那么消极。直到失去了,你才真正懂得珍惜你所拥有的…)
【2019 年 11 月
我很恼火;我已经在一个破旧的伦敦酒吧里独自等了 15 分钟,这时我收到一条短信说,“对不起,伙计,我要走了……给我拿杯酒,我一分钟就回来”。我仍然很恼火,但不再是因为他迟到了;也不是说酒吧很糟糕;我甚至不用请他喝一杯。我很恼火,因为我们选择见面的地方显然就在他家附近,尽管离我家有几英里远。这可能会让你觉得我是一个很难相处的人。你绝对是对的。无论如何,这是我认为这种问题将来可以解决的方法…
像任何理性分析师一样,我和我的(同样脏兮兮的)同伴喝完一品脱酒后,离开了这个脏兮兮的酒吧,开始思考如何解决这个问题。在处理手头的挑战之前,我想了解具体的问题是什么,以及定义它的可能约束。我把它们总结为:
问题:两个(或更多)人需要决定他们想在哪里见面
约束:
- 双方都必须有可能在每个人规定的旅行时间内到达这个地方(显然我的同伴的最大旅行时间是 1 分钟,我应该明白这一点)
- 每个人都可以选择开车、步行、骑自行车或乘坐公共交通工具到达目的地
- 这个地方应该在平均旅行时间和场地质量之间取得平衡
- 场所类型可以预先定义(咖啡馆、酒吧、餐馆等)。)
- 在你去之前,你应该能看到这个地方和评论
这是一个位置问题,尽管我知道我需要位置信息来解决它——而不是预先选择数千个地点,我想要一种方法来动态访问任何位置的大量位置数据,无论应用程序何时运行。
维克多·罗德里格兹在 Unsplash 上的照片
所以——除了我明显的个性问题(有些事情即使复杂的算法也无法解决)——很明显这是一个空间和 API 的两难问题,我可以使用 Alteryx 来解决。
本博客的其余部分致力于应用程序的用户界面;从技术角度看它是如何构建的;以及我接下来的步骤。我在这篇文章的底部添加了 Alteryx 社区上的应用程序链接。请随意下载并亲自尝试,或者只是评论反馈—链接在本文的底部。欢迎所有反馈。
如何使用该应用程序
像许多 API 一样,您需要生成一个访问令牌来使用 Google API。你可以在这里注册。
请务必检查路线和地点:
我选择将一张卡与我的 API 相关联(因为这样做可以让你免费打很多电话),但是请务必通读这里的所有文档。
该应用的用户界面分为三个选项卡:
Alteryx 设计器中 UI 的第一次迭代
结果
结果在报告中提供,显示:您的旅行多边形重叠的地方,该景点中排名最高的地方,以及其他一些有趣的点和图像…
它是如何工作的?
有几个关键工具是该应用程序不可或缺的。在调试模式下运行应用程序可以让你确切地看到这些工具在做什么。
- 在应用程序开始时,您在地图上放置了两个(或更多)大头针;这些大头针需要转换成空间对象,这样我们就可以对每个人的位置进行空间分析。这是使用“创建点”工具,通过 XY 坐标和附加一个空间对象来完成的。
- 丢弃的点被馈送到 Route 360 API 宏中;该 API 利用步骤 1 中创建的空间点,在每个人周围创建一个多边形,显示他们在选择的交通类型和时间内可以到达的区域。(请注意,当您远离时,斑点的大小会减小;公共交通经常出现这种情况,其中每个斑点(在这种情况下)是一个地铁站,围绕它们的圆环是人们在剩下的分配时间内可以从每个车站走多远)
- 上面的步骤 2 是为每个人单独运行的,但我们真正想要的是将这两个多边形结合起来,看看这两个旅程是否可以重叠(你将去参加舞会)。“空间处理”工具允许您创建显示重叠区域的多边形,如下所示:
- 如果——如上所述,我们有多个重叠的区域呢?为了最大限度地找到一个好的地点(并最大限度地减少我们必须在下游进行的 API 调用的数量),我们可以使用“空间信息”工具来计算这四个多边形的面积,并只选择面积最大的一个进行地点分析。
- 现在我们有了“完美的会合点”(或者有中心点的多边形),但是我们仍然需要在其中搜索符合我们标准的地方(在这个例子中,是一个酒吧)。您在应用程序开始时定义的搜索半径在这里发挥作用,在我们的谷歌地图 API 调用中充当“距离”中心点的角色。
- 这些信息,以及中心点和地点类型的 X 和 Y,使我们能够构建一个搜索 URL,如下所示:
[https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=518746104,-0.110362243&radius=500&keyword=Bar&key=**yourGoogleAPIkey**](https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=518746104,-0.110362243&radius=500&keyword=Bar&key=yourGoogleAPIkey)
- 这个网址实际上是你在谷歌地图中搜索过的。我们需要 Alteryx 预先做好工作,以便在稍后找到完美的会议,但是,通过 Alteryx 这样做,我们可以返回比我们在 Google 中直接看到的更多的结果,更不用说看到关于每个地方的更多信息。
- 我们可以将新构建的 URL 输入到“下载”工具中,该工具与“JSON parse”一起允许我们以结构化的方式查看所有数据:
- 额外的报告和 blob 转换工具允许我们为用户构建标签,点击通过谷歌评级排序的地点列表,查看显示其位置的地图,并查看一些示例图片:
- 然后使用报告工具生成您在最终输出中看到的地图和表格:
后续步骤
最近,除了“中间”之外,我拒绝在任何地方与任何人见面,并且被封锁,这意味着我有很多空闲时间来测试、改进和添加功能。我在锁定期间添加的几个功能包括但不限于:
- 用户关键字搜索。例如,您可能想要一家餐馆,但是添加到 google URL 的自由文本字段也允许您声明该餐馆提供“鱼和薯条”
- 每个人的实际旅行时间;google maps API 允许您返回以分钟和英里为单位的旅行时间,还可以返回一步一步的路线
- 多人(> 2 人)功能。显然,有些人有不止一个朋友,所以我添加了在 UI 中声明多人位置的功能
- 评论排名理论上是好的,但实际上很差。如果我要会见工作中的朋友(假设每个人都在市中心工作),应用程序通常会建议在里兹酒店(Ritz)见面喝杯啤酒……所以我创建了一个指数,将所有这些变量都考虑在内,包括价格
- UI 的一般整理:例如,最初,在下拉选择中,你可以看到谷歌将为你搜索的一系列商业类型;很自然,这非常适合酒吧和餐馆:
…我认为(也希望)没有人会想用这个来寻找他们之间评价最高的牙医或动物园。但我有什么资格评判?
更新后的新应用程序用户界面如下所示,可以在任何设备上通过浏览器运行:
Alteryx 服务器中的新应用程序 UI
…输出包括更多细节,包括:
- 每个人到达每个目的地需要多长时间(见下面的安迪、克里斯和戴夫专栏)
- 行程时间的均值和标准差(保证公平!)
- 价格、评级、评级数量
- 一个排名指数,基于我创建的评分系统,加权并结合了上述所有指标
如果我和两个(虚构的)朋友想在一个高档酒吧见面
最后订单
我发现,尽管用例最初是用来学习 Google APIs 并继续使用 Alteryx 中的空间分析工具,但从 2020 年开始,我们相遇的地方以及我们如何到达那里,是一个比以往任何时候都更相关的问题。
正如你所看到的,即使是现在这也是一项正在进行的工作;我欢迎对这个应用程序的任何反馈,或者如果你想在这个或类似的事情上合作,请听听你的意见。我很乐意在附近的动物园见面,假设它符合我的评分高于 4.7 和旅行时间少于 27 分钟的标准。
我欢迎任何反馈或想法!
威尔·斯图尔特在 Unsplash 上拍摄的照片
干杯
伊利诺伊州——你为什么不把曲线变平?
原文:https://towardsdatascience.com/illinois-why-arent-you-flattening-the-curve-9e19b71ab453?source=collection_archive---------47-----------------------
还是你?让我们看看数据
索耶·本特森在 Unsplash 上的照片
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
图片作者。查看伊利诺伊州新冠肺炎仪表盘上的实时图像。
伊利诺伊州的“呆在家里”命令,相当于该州的就地避难所,于 3 月 21 日生效,并持续到今天。但是尽管隔离,伊利诺伊州的新冠肺炎病例似乎仍在加速。这与旧金山湾区的明显成功形成鲜明对比。
确诊病例总数
图片作者。查看伊利诺伊州新冠肺炎仪表盘上的实时图像。
关于伊利诺伊州,首先要注意的是,总病例数继续呈上升趋势。这对于大多数州来说是真实的,并不意味着他们没有拉平曲线。问题是,病例总数的增长速度有多快?
增长曲线—每日新病例
每天新增病例的数量是决定一个州是否在拉平曲线的最重要的统计数据。它代表了感染的加速。
生长曲线的快速解释
为了便于说明,让我们用一个假设的例子来说明一周的时间跨度。
如果一个州的新病例数每两天翻一番,这意味着周一有 400 个新病例,周三有 800 个新病例,周五有 1600 个新病例,周日有 3200 个新病例,这意味着一周总共有 6000 个新病例。这很糟糕,表明感染率在上升。
相比之下,如果增长曲线是平坦的,则他们可能会在周一显示 40 个新病例,周三显示大约 40 个新病例,周五显示大约 40 个新病例,周日显示 40 个新病例——这几周总共有 160 个新病例。
前一种情况是那种可以迅速压垮医疗系统的增长曲线。后一种情况是曲线变平后的样子。仍然有新的病例,但是每天的新病例数量没有增长。因此,病例总数并没有增加。
伊利诺伊州的增长曲线
图片作者。查看伊利诺伊州新冠肺炎仪表盘上的实时图像。
从这张图表中我们可以看出,伊利诺伊州的增长曲线似乎没有变平。每天都有一些变化,但如果你看一下 7 天移动平均线(绿线),你会发现每天的新病例数量仍呈上升趋势。这是怎么回事?
库克县有绝大多数的病例
图片作者。在伊利诺斯州新冠肺炎仪表盘上查看实时可视化。
这张图表中最突出的一点是,伊利诺伊州的绝大多数病例都发生在库克县。库克县是芝加哥的所在地,芝加哥是该州最大的都会区,也是人口密度最大的地区。
但是等等,他们在进行更多的测试吗?
我们需要考虑的一点是,确诊新增病例的数量可能并不总是代表实际新增病例的数量。检测激增会导致确诊新病例激增。
因此,伊利诺伊州(甚至芝加哥)每天都在做越来越多的测试,这是理所当然的,他们会显示每天的病例数在增加,即使实际病例数持平。我们来看看测试数据。
现在看这个图表,叙述开始改变。在这里,我们看到 4 月 24 日进行的测试大幅增加。如果我们回到每日新增病例图表,我们也看到 4 月 24 日确诊病例大幅增加。
测试曲线解释了增长曲线中所有的增长吗?看起来没有,没有。但它确实消除了一些更令人担忧的峰值,并且在报道伊利诺伊州就地安置的有效性时需要额外的谨慎。
数据源和参考
这篇文章的所有数据和可视化来自 Knowi 冠状病毒数据中心。那里使用的数据来自约翰·霍普斯金,世卫组织,疾病控制中心。在这里查看他们的数据来源列表。
可以在这里找到各州的测试数据。
图片作者。伊利诺伊州的新冠肺炎仪表盘。
暹罗网络图解指南
原文:https://towardsdatascience.com/illustrated-guide-to-siamese-network-3939da1b0c9d?source=collection_archive---------25-----------------------
使用三重损失进行一次性学习
马尔科·祖彭在 Unsplash 上的照片
建立一个准确的机器学习模型通常需要大量的训练数据,这仍然可能导致训练数据过度拟合,或者只适用于有限数量的训练数据类别。但是,如果一台机器能够“学会学习,例如,如果我们向我们的机器展示一只鹦鹉的单一图像,它可以通过理解它与作为参考展示的鹦鹉的图像如何相似来准确地识别另一只鹦鹉的图像。这个设计一个可以“学会学习”的机器的问题叫做元学习。
在本文中,我们将关注一个著名的元学习模型——暹罗网络。如何运作,如何实施?
Siamese,顾名思义,来自“Siamese Twins”,其中我们使用两个或更多的网络(这里,在图像的情况下是 CNN),该网络使用共享的权重来学习图像之间的相似性和不相似性。网络输出 n 维嵌入,其中每个方向代表图像的一些视觉模式。
了解嵌入
假设,我们使用 n 维空间来映射我们的图像,其中每个维度对应于特定特征/或模式的值。每个维度描述了输入图像的独特视觉特征。查看 Jay alam mar的这篇令人惊叹的文章以获得更好的嵌入直觉。例如,在不同动物图像的情况下,输出嵌入可能如下所示(颜色强度表示其值在 0 到 1 之间)—
作者图片
狗的前两个图像输出相似的嵌入,而猫和美洲驼的第三和第四输出嵌入与狗非常不同,因为它们具有非常不同的视觉特征包。
在本文中,我们将使用 Omniglot 数据集。该数据集包含世界各地不同语言中使用的各种字母的字符图像。
Ominglot 数据集
工作
美国有线新闻网;卷积神经网络
CNN 架构的灵感来自这篇论文
我们的 CNN 输出一个期望嵌入大小的一维数组。我们可以看到最后一层执行 L2 归一化 ,这将归一化输出向量并将其映射到半径为 1 的 n 维超球的表面。这样做是为了确保可以通过计算两个嵌入之间的距离来比较图像之间的相似性值,因为所有的嵌入都将位于将给出更好结果的表面上。我们的模型有三个这样的 CNN,所有的重量都一样。这将帮助我们的模型学习每个样本中的一个相似性和一个不相似性。每个样本将包含三个组成锚、阳性和阴性的样本图像。稍后会有更多的介绍。
最终连体网络架构
损失函数
现在是构建这个网络最重要的部分。在这里,我们将编写它如何比较输出嵌入的定义,并理解它们之间的相似性/不相似性,从而完成元学习的任务。
由于我们使用之前讨论的 L2 归一化将输出嵌入映射到表面上,我们可以使用 L2 距离或余弦相似度。使用三重损失将允许我们的模型映射两个彼此接近的相似图像,并且远离不相似的样本图像。这种方法通过供给三个一组来实现,包括:
1。锚点图像 —这是一个样本图像。
2。正面形象——这只是主播形象的另一种变体。这有助于模型学习两个图像之间的相似性。
3。负像 —这是与上面两幅相似图像不同的图像。这有助于我们的模型学习锚图像的不同之处。
为了增加相似和不相似输出向量之间的距离,并进一步映射彼此接近的相似图像,我们引入了另一个术语,称为边距。这增加了相似和不相似向量之间的分离,也消除了任何平凡解的输出。由于人类很难想象一个嵌入映射到 N 维球体上,从而理解这种损失是如何工作的,我做了下面的渲染来建立对 N=3(我们非常熟悉的 3D)这种东西如何工作的直觉。
此图描述了我们的模型在训练后的输出。标记为绿色点的相似图像相互靠近映射,标记为红色映射的不相似图像相距较远,边距大小的最小距离显示为黄色。在训练后的理想情况下,不应在锚图像的黄色区域中映射任何点。然而,由于表面上的空间非常有限,点往往会有重叠区域。稍后将对此进行更多讨论。
我们的模型使用了在论文中使用的 0.2 的边距大小。
这种相似性/不相似性由使用 L2 距离和余弦距离的两个向量之间的距离来定义。
所以我们的损失定义如下—
loss(a,p,n) = max(0,d(a,p) -d(a,n)+余量)
余弦三重损失
L2 三重损失
履行
实现这个网络的棘手部分是定义损失函数。我们通过对所需批量的三元组进行采样来生成输入数据,并定义网络和我们的损失函数,然后进行训练!!!完整的实现可以在 这里找到 。
结果和观察
在用不同的超参数和损失函数训练网络,并在 n 路一次分类(即网络从未见过的不同类别的“n”个单个图像)上测试网络准确度之后。可以观察到—
- 对于较小的嵌入,余弦损失往往表现良好。
- 增加嵌入的大小可以提高精确度,因为更多的维度现在可以表示图像的更多独特特征。
- 随着 n 向一次发射中的“n”增加,模型精度略微降低,因为网络难以在超球体上映射更多数量的相距较远的嵌入。
不同大小的输出向量之间的精确度比较
后续的文章可以阅读,使用对比损失使暹罗网络仅学习相似性,使用一个角色的两个不同图像。毫无疑问,它确实表现不佳,往往在训练中过度适应,而三连音缺失根本不会过度适应。基于对比损失的连体网络的准确性随着 nway 一次性分类的增加而急剧下降。
不同大小输出向量下对比损失和三重损失的准确性比较
应用
这个网络用来比对指纹,检测签名伪造,人脸识别。这可以应用于任何相似和相异分数有用的地方。
参考
[1]https://www.cs.cmu.edu/~rsalakhu/papers/oneshot1.pdf
[2]https://en.wikipedia.org/wiki/Siamese_neural_network
https://jalammar.github.io/illustrated-word2vec/
[3]https://towards data science . com/one-shot-learning-with-siamese-networks-using-keras-17 f 34 e 75 bb 3d
[4]https://Neptune . ai/blog/content-based-image-retrieval-with-siamese-networks
变压器图解指南
原文:https://towardsdatascience.com/illustrated-guide-to-transformer-cf6969ffa067?source=collection_archive---------20-----------------------
逐个组件的细分分析
变压器模型是编码器-解码器架构的演变,在论文中提出注意力是你所需要的一切。虽然编码器-解码器架构一直依赖递归神经网络(RNNs)来提取序列信息,但转换器不使用 RNN。基于变压器的模型已主要取代了 LSTM,并已被证明在许多序列间问题的质量优越。
Transformer 完全依赖注意力机制,通过并行化来提高速度。它在机器翻译方面产生了最先进的性能。除了语言翻译方面的重大改进之外,它还提供了一种新的体系结构来解决许多其他任务,如文本摘要、图像字幕和语音识别。
在 Transformer 模型之前,递归神经网络(RNNs)一直是顺序数据的首选方法,其中输入数据具有定义的顺序。RNNs 的工作方式就像一个前馈神经网络,将输入一个接一个地展开。
展开递归神经网络[ 来源
展开输入中每个符号的过程由编码器完成,其目的是从顺序输入中提取数据,并将其编码为一个向量,即输入的表示。
顺序输入数据的例子是产品评论中的单词(或字符),其中 RNN 将顺序提取每个单词,形成一个句子表示。这种表示将被用作分类器输出固定长度向量的特征,例如指示正面/负面的情感标签,或者在 5 分尺度上。
在机器翻译和图像字幕中,我们可以用解码器代替输出固定长度向量的分类器。如同编码器单独消耗输入中的每个符号一样,解码器在几个时间步长上产生每个输出符号。
使用编码器-解码器结构的机器翻译。
例如,在机器翻译中,输入是英语句子,输出是法语翻译。编码器将按顺序展开每个单词,并形成输入英语句子的固定长度向量表示。然后,解码会以定长向量表示为输入,一个接一个地产生每个法语单词,形成翻译后的英语句子。
基于 RNN 的编码器-解码器的问题
然而,RNN 模型有一些问题,它们训练缓慢,并且它们不能处理长序列。
输入数据需要一个接一个地顺序处理。这种循环过程没有利用现代图形处理单元(GPU ),它是为并行计算而设计的。rnn 非常慢,以至于引入了截断反向传播来限制反向传递中的时间步长数——估计梯度来更新权重,而不是完全反向传播。即使采用截短的反向传播,rnn 的训练仍然很慢。
其次,RNNs 也不能很好地处理长序列,因为如果输入序列太长,我们会得到消失和爆炸梯度。一般在训练过程中会在损耗中看到 NaN(不是一个数字)。这些也被称为 RNNs 中的长期依赖性问题。
如果输入序列太长,则渐变消失。
1997 年,hoch Reiter&schmid Huber引入了长短期记忆(LSTM)网络,这种网络被明确设计用来避免长期依赖问题。每个 LSTM 单元允许过去的信息跳过当前单元的所有处理并移动到下一个单元;这允许存储器保留更长时间,并且将使数据不变地随其流动。LSTM 由一个决定存储什么新信息的输入门和一个决定删除什么信息的遗忘门组成。
长短期记忆[ 来源
当然,LSTMs 比 rnn 有更好的记忆,能够处理更长的序列。然而,LSTM 网络更复杂,速度更慢。
基于 RNN 的编码器-解码器架构的另一个缺点是固定长度的向量。使用固定长度的向量来表示输入序列以解码一个全新的句子是困难的。如果输入序列很大,上下文向量不能存储所有信息。此外,区分单词相似但意思不同的句子是一项挑战。
使用固定长度的向量来表示输入序列。
想象一下,选择上面的一段话(输入)并记忆它(定长向量)。然后,翻译整个段落(输出),不要参考它。这很难,我们不是这样做的。相反,当我们把一个句子从一种语言翻译成另一种语言时,我们一部分一部分地看这个句子,每次都注意这个句子中的一个特定短语。
Bahdanau 提出了一种在编码器-解码器模型中搜索与预测目标单词相关的源句子部分的方法。这就是注意力机制的美妙之处;我们可以使用注意力翻译相对较长的句子,而不会影响它的表现。比如翻译成“noir”(法语是“黑”的意思),注意机制会把注意力集中在“black”这个词上,可能还有“cat”,而忽略句子中的其他词。
测试集上生成的翻译相对于句子长度的 BLEU 分数。[ 来源
注意机制提高了编码器-解码器网络的性能,但是速度的瓶颈仍然是由于 RNN 必须一个字一个字地顺序处理。对于序列数据,我们能去掉 RNN 吗?
我们如何对顺序数据使用并行化?
是啊!你需要的只是关注。变压器架构于 2017 年推出。就像编码器-解码器架构一样,输入序列被送入编码器,解码器将逐个预测每个字。该转换器通过消除 RNN 和利用注意机制来改善其时间复杂度和性能。
RNN 一个一个地打开每个单词。变压器并行处理输入。
考虑把一个句子从英语翻译成法语。在 RNN,每个隐藏状态都依赖于前面单词的隐藏状态。因此,当前步骤的嵌入一次生成一个时间步骤。在 Transformer 中,没有时间步长的概念;输入序列可以并行传递到编码器。
变压器
假设我们正在训练一个将英语句子翻译成法语的模型。转换器架构有两部分,编码器(左)和解码器(右)。让我们来看看变压器架构。
变压器模型架构。[ 来源
在编码器中,它输入一个英语句子,输出将是每个单词的一组编码向量。将输入的英语句子中的每个单词转换成嵌入来表示意思。然后我们添加一个位置向量来添加单词在句子中的上下文。这些单词向量被馈送到编码器注意块,编码器注意块计算每个单词的注意向量。这些注意力向量通过前馈网络并行传递,输出将是每个单词的一组编码向量。
解码器接收法语单词的输入和整个英语句子的注意力向量,以生成下一个法语单词。它用嵌入层编码每个单词的意思。然后添加位置向量来表示单词在句子中的上下文。这些单词向量被送入第一个注意块,即掩蔽注意块。掩蔽注意块计算当前和先前单词的注意向量。来自编码器和解码器的注意力向量被馈送到下一个注意力块,该注意力块为每个英语和法语单词生成注意力映射向量。这些向量被传递到前馈层线性层和 softmax 层,以预测下一个法语单词。我们重复这个过程来生成下一个单词,直到生成“句子结束”标记。
这是变压器如何工作的高级细节。让我们更深入地研究一下每个组件。
嵌入
因为计算机不像我们一样理解单词——它的意思以及单词之间的关系;我们需要用向量代替单词。单词向量(或嵌入)允许每个单词被映射到高维嵌入空间中,其中具有相似含义的单词彼此更接近。
尽管我们可以用向量来引用和表示每个单词的含义,但单词的真正含义取决于句子中的上下文,因为不同句子中的同一个单词可能有不同的含义。由于 RNN 被设计用来捕获序列信息,没有 RNN,转换器如何处理词序?我们需要位置编码器。
位置编码器
位置编码器从输入嵌入层接收输入,并应用相对位置信息。这一层输出带有位置信息的单词向量;这就是单词的意思和它在句子中的上下文。
考虑下面的句子,“狗咬了约翰尼”和“约翰尼咬了狗。”如果没有上下文信息,两个句子会有几乎相同的嵌入。但我们知道这不是真的,对约翰尼来说肯定不是真的。
单词嵌入和位置编码产生带有上下文的单词向量。
作者建议使用多个正弦和余弦函数来生成位置向量。这样,我们可以使用这个位置编码器来处理任何长度的句子。波的频率和偏移对于每个维度是不同的,代表每个位置,值在-1 和 1 之间。
使用多个正弦和余弦函数的位置编码。
这种二进制编码方法还允许我们确定两个单词是否彼此接近。例如,通过参考低频正弦波,如果一个单词具有“高”,而另一个单词具有“低”,我们知道它们相距更远,一个位于开头,另一个位于结尾。
编码器的多头注意力
注意力的主要目的是回答“我应该关注输入的哪一部分?”如果我们对一个英语句子进行编码,我们想要回答的问题是,“英语句子中的一个单词与同一个句子中的其他单词有多相关?”这表现在注意力向量中。对于每个单词,我们可以生成一个注意力向量来捕捉句子中单词之间的上下文关系。例如,对于单词“黑色”,注意力机制集中在“黑色”和“猫”上
多个注意力向量正常化。
由于我们对不同单词之间的交互感兴趣,每个单词的注意力向量可能对自己的权重过高。因此,我们需要一种方法来规范化向量。注意模块接受输入 V、K 和 Q——这些是提取输入单词不同成分的抽象向量。我们用这些来计算每个单词的注意力向量。
为什么叫“多头关注”?这是因为我们对每个单词使用多个注意力向量,并采用加权平均来计算每个单词的最终注意力向量。
编码器的前馈
由于多头注意块输出多个注意向量,我们需要将这些向量转换成每个单词的单个注意向量。
该前馈层接收来自多头注意力的注意力向量。我们应用标准化将它转换成一个单一的注意力向量。因此,我们得到可被下一个编码器块或解码器块消化的单个向量。在该论文中,作者在输出到解码器模块之前堆叠了六个编码器模块。
解码器的输出嵌入和位置编码器
因为我们是从英语翻译成法语,我们输入法语单词到解码器。我们用单词嵌入替换单词,然后添加位置向量来获得单词在句子中的上下文概念。我们可以将这些包含单词含义及其在句子中的上下文的向量输入解码器模块。
解码器的掩蔽多头注意
类似于编码器模块中的多头注意力。注意块为法语句子中的每个单词生成注意向量,以表示每个单词与同一个输出句子中的每个单词的相关程度。
与接收英语句子中每个单词的编码器中的注意块不同,只有法语句子中的前几个单词被输入到解码器的注意块中。因此,我们使用向量屏蔽稍后出现的单词,并用零表示,这样注意力网络在执行矩阵运算时就无法使用它们。
多个注意力向量被屏蔽。
解码器的多头注意力
这个注意块充当编码器-解码器,它从编码器的多头注意和解码器的掩蔽多头注意接收向量。这个注意块将决定每个单词向量彼此之间的相关程度,这就是从英语单词到法语单词的映射发生的地方。这个模块的输出是英语和法语句子中每个单词的注意力向量,其中每个向量代表两种语言中与其他单词的关系。
将注意力向量输入到解码器的多头注意力中
解码器的前馈
像编码器的前馈层一样,该层将由多个向量组成的每个词归一化为下一个解码器块或线性层的单个注意力向量。在本文中,作者在输出到线性层之前堆叠了六个解码器块。
解码器的线性层和 softmax
由于解码器的目的是预测下一个单词,所以这个前馈层的输出大小是词汇表中法语单词的数量。Softmax 将输出转换为概率分布,输出与下一个单词的最高概率相对应的单词。
把英语翻译成法语,解码器预测下一个单词。
对于每一个生成的单词,我们重复这个过程,包括法语单词,并用它来生成下一个直到句子结束的令牌。
开始
TensorFlow 和 PyTorch 都有一个循序渐进的教程,可以帮助你理解和训练一个使用 Transformer 的序列到序列模型。如果你需要快速制作的东西,可能最受欢迎的选择是通过拥抱脸。
张量流教程:
[## 用于语言理解的转换器模型| TensorFlow 核心
本教程训练一个 Transformer 模型将葡萄牙语翻译成英语。这是一个高级示例,假设…
www.tensorflow.org](https://www.tensorflow.org/tutorials/text/transformer)
PyTorch 教程:
[## 基于神经网络的序列间建模。变压器和火炬文本- PyTorch 教程 1.5.0…
PyTorch 1.2 版本包含了一个标准的 transformer 模块,该模块基于一篇名为“你只需要关注”的文章。的…
pytorch.org](https://pytorch.org/tutorials/beginner/transformer_tutorial.html)
PyTorch 中的变压器架构:
生产包装:
[## 快速入门-变压器 2.10.0 文档
Transformers 是一个为寻求使用/研究/扩展大型变压器的 NLP 研究人员而构建的自以为是的库…
huggingface.co](https://huggingface.co/transformers/quickstart.html) [## 深度学习神经网络中卷积层是如何工作的?
理解卷积及其参数的生动方式
towardsdatascience.com](/how-convolutional-layers-work-in-deep-learning-neural-networks-2913af333b72)
变压器图解指南-逐步解释
原文:https://towardsdatascience.com/illustrated-guide-to-transformers-step-by-step-explanation-f74876522bc0?source=collection_archive---------0-----------------------
变形金刚正在席卷自然语言处理世界。这些令人难以置信的模型打破了多项 NLP 记录,推动了技术的发展。它们被用于许多应用程序,如机器语言翻译、会话聊天机器人,甚至是更好的搜索引擎。变形金刚是当今深度学习的热门,但它们是如何工作的呢?为什么它们胜过了之前的序列问题之王,比如递归神经网络、GRU 和 LSTM?你可能听说过不同的著名变形金刚模型,如伯特、GPT 和 GPT2。在本帖中,我们将重点关注引发这一切的一篇文章,“你所需要的是注意力”。
如果你想看视频版本,请点击下面的链接。
注意机制
为了理解变形金刚,我们首先必须理解注意力机制。注意机制使变形金刚具有极长的记忆。转换器模型可以“关注”或“关注”所有先前已经生成的令牌。
让我们看一个例子。比方说我们要用生成式变形金刚写一篇短篇科幻小说。使用的拥抱脸的用变形金刚写的应用程序,我们就可以做到这一点。我们将用我们的输入来填充模型,模型将生成其余的。
我们的输入: “随着外星人进入我们的星球”。
变形金刚输出: “开始殖民地球,某一群外星人开始操纵我们的社会,通过他们对一定数量精英的影响来保持对平民的铁腕控制。”
好吧,这个故事有点黑暗,但有趣的是这个模型是如何产生它的。当模型逐字生成文本时,它可以“注意”或“关注”与生成的单词相关的单词。知道参加什么单词的能力也是通过反向传播在训练中学会的。
在一个接一个地生成单词时关注不同标记的注意机制
递归神经网络(RNN)也能够查看以前的输入。但是注意力机制的力量在于它不会受到短期记忆的影响。RNN 的参考窗口更短,所以当故事变长时,RNN 不能访问序列中更早生成的单词。对于门控循环单位(GRU 氏)和长短期记忆(LSTM 氏)网络来说仍然如此,尽管它们实现长期记忆的能力更大,因此具有更长的参考窗口。从理论上讲,注意机制在给定足够的计算资源的情况下,具有无限的参考窗口,因此能够在生成文本时使用故事的整个上下文。
假设的注意参照窗,RNN 的,GRU 的和 LSTM 的
注意力是你所需要的-一步一步的演练
在论文“注意力是你所需要的全部”中展示了注意力机制的力量,其中作者介绍了一种新的新型神经网络,称为变压器,这是一种基于注意力的编码器-解码器类型的架构。
变压器模型
在高层次上,编码器将输入序列映射到抽象的连续表示中,该表示保存该输入的所有学习信息。然后,解码器获取该连续表示,并逐步生成单个输出,同时还被馈送先前的输出。
让我们看一个例子。本文将变压器模型应用于神经机器翻译问题。在本帖中,我们将展示它如何适用于对话聊天机器人。
我们的输入: 【嗨你好】
变压器输出: 【我很好】
输入嵌入
第一步是将输入输出到单词嵌入层。单词嵌入层可以被认为是获取每个单词的学习矢量表示的查找表。神经网络通过数字进行学习,因此每个单词都映射到一个具有连续值的向量来表示该单词。
将单词转换为输入嵌入
位置编码
下一步是在嵌入中注入位置信息。因为变换器编码器不像递归神经网络那样具有递归性,所以我们必须将一些关于位置的信息添加到输入嵌入中。这是使用位置编码完成的。作者想出了一个使用正弦和余弦函数的聪明绝招。
我们不会深入位置编码的数学细节,但这里有一些基础知识。对于输入向量的每个奇数索引,使用 cos 函数创建一个向量。对于每个偶数索引,使用 sin 函数创建一个向量。然后将这些向量添加到它们相应的输入嵌入中。这成功地给出了关于每个矢量位置的网络信息。选择正弦和余弦函数是因为它们具有线性属性,模型可以很容易地学会处理。
编码器层
现在我们有了编码器层。编码器层的工作是将所有输入序列映射到一个抽象的连续表示中,该表示保存整个序列的学习信息。它包含 2 个子模块,多头关注,其次是一个完全连接的网络。在两个子图层的每一个周围还有剩余连接,随后是图层归一化。
编码器层子模块
要分解这一点,我们先来看看多头注意力模块。
多头注意力
编码器中的多头注意力应用了一种称为自我注意力的特定注意力机制。自我注意允许模型将输入中的每个单词与其他单词联系起来。所以在我们的例子中,有可能我们的模型可以学会将单词“你”,与“怎么样”和“是”联系起来。也有可能模型知道以这种模式构建的单词通常是一个问题,因此做出适当的响应。
编码器自关注操作。参考下面的插图。
查询、键和值向量
为了实现自我关注,我们将输入提供给 3 个不同的完全连接的层,以创建查询、键和值向量。
这些向量到底是什么?我在栈交换上找到了一个很好的解释。
“查询关键字和值概念来自检索系统。例如,当你键入一个查询来搜索 Youtube 上的一些视频时,搜索引擎会将你的 查询 映射到一组 键 (视频标题、描述等)。)与数据库中的候选视频相关联,然后呈现给你最匹配的视频( 值 )。
查询和关键字的点积
在通过线性层馈送查询、关键字和值向量之后,查询和关键字经历点积矩阵乘法以产生得分矩阵。
查询和密钥的点积乘法
得分矩阵决定了一个单词在其他单词上的关注程度。因此每个单词都有一个与时间步长中的其他单词相对应的分数。分数越高,注意力越集中。这就是查询映射到键的方式。
点积的注意力分数。
降低注意力分数
然后,分数通过除以查询和键的维度的平方根而缩小。这是为了获得更稳定的渐变,因为倍增值可能会产生爆炸效果。
降低注意力分数
标度分数的最大值
下一步,你取标度分数的 softmax 来得到注意力权重,它给你 0 到 1 之间的概率值。通过做 softmax,高分得到提高,低分受到抑制。这允许模型对哪些单词也要参加更有信心。
取标度分数的软最大值以获得概率值
将 Softmax 输出乘以值向量
然后你把注意力权重乘以你的价值向量,得到一个输出向量。softmax 分数越高,模型学习的单词值就越重要。较低的分数会淹没无关的单词。然后你把它的输出输入到一个线性层进行处理。
计算多头注意力
要使这成为多头注意力计算,您需要在应用自我注意力之前将查询、键和值分成 N 个向量。分裂的向量然后单独经历自我注意过程。每一个自我关注的过程被称为一个头。每个头产生一个输出向量,在通过最终的线性层之前,该向量被连接成一个向量。理论上,每个头将学习不同的东西,因此给编码器模型更多的表示能力。
在应用自我关注之前,分裂 Q,K,V,N 次
总而言之,多头注意力是 transformer 网络中的一个模块,它计算输入的注意力权重,并产生一个输出向量,其中包含关于每个单词应该如何关注序列中所有其他单词的编码信息。
剩余连接、层标准化和前馈网络
多头注意力输出向量被添加到原始位置输入嵌入中。这被称为剩余连接。剩余连接的输出经过层标准化。
位置输入嵌入与多头注意输出的剩余连接
归一化残差输出通过逐点前馈网络进行投影,以便进一步处理。点式前馈网络是一对线性层,中间有一个 ReLU 激活。然后,其输出再次添加到逐点前馈网络的输入,并进一步归一化。
逐点前馈层的输入和输出的剩余连接。
剩余连接通过允许梯度直接流过网络来帮助网络训练。使用层标准化来稳定网络,从而大大减少所需的训练时间。逐点前馈层用于投射注意力输出,潜在地给予它更丰富的表示。
编码器总结
这就完成了编码器层。所有这些操作都是将输入编码成具有注意力信息的连续表示。这将有助于解码器在解码过程中关注输入中的适当单词。您可以将编码器堆叠 N 次,以进一步对信息进行编码,其中每一层都有机会学习不同的注意力表示,因此有可能提高 transformer 网络的预测能力。
解码器层
解码器的工作是生成文本序列。解码器具有与编码器相似的子层。它有两个多头注意力层,一个逐点前馈层,和残差连接,以及每个子层之后的层归一化。这些子层的行为类似于编码器中的层,但是每个多头注意力层有不同的工作。解码器以线性层作为分类器,以 softmax 获得单词概率。
解码器层。阅读时参考这张图表。
解码器是自回归的,它以一个开始标记开始,并接受一个先前输出的列表作为输入,以及包含来自输入的注意信息的编码器输出。解码器在生成令牌作为输出时停止解码。
解码器是自回归的,因为它在先前的输出中被馈送时一次生成一个令牌 1。
让我们来看一下解码步骤。
解码器输入嵌入和位置编码
解码器的开始与编码器非常相似。输入经过嵌入层和位置编码层以获得位置嵌入。位置嵌入被送入第一个多头注意力层,该层计算解码器输入的注意力分数。
解码器第一多头关注
这个多头注意力层的运作方式略有不同。由于解码器是自回归的,并且一个字一个字地生成序列,所以需要防止它适应未来的标记。例如,当计算单词“am”的注意力分数时,您不应该访问单词“fine ”,因为该单词是在之后生成的未来单词。单词“am”应该只能访问它自己和它前面的单词。这对于所有其他的词都是如此,在那里他们只能注意前面的词。
对解码器的第一个多头注意力标度注意力分数的描述。“am”这个词,不应该为“fine”这个词取任何值。其他所有单词都是如此。
我们需要一种方法来防止计算未来单词的注意力分数。这种方法叫做掩蔽。为了防止解码器查看将来的令牌,您可以应用一个先行掩码。在计算 softmax 之前和缩放分数之后添加掩码。让我们来看看这是如何工作的。
前瞻掩模
掩码是一个矩阵,其大小与填充有 0 值和负无穷大值的注意力分数相同。当你把面具加到缩放的注意力分数上时,你会得到一个分数矩阵,右上角的三角形填充了否定的无穷大。
向缩放后的分数添加前瞻掩码
屏蔽的原因是因为一旦你取屏蔽分数的软最大值,负的无穷大被清零,为未来的标记留下零注意分数。正如您在下图中所看到的,“am”的关注度得分本身及其之前的所有单词都有值,但单词“fine”的关注度得分为零。这实际上是告诉模型不要关注这些单词。
这种屏蔽是在第一个多头注意力层中如何计算注意力分数的唯一区别。这一层仍然有多个头部,蒙版被应用,然后连接并通过一个线性层进行进一步处理。第一多头注意力的输出是带有关于模型应该如何关注解码器输入的信息的屏蔽输出向量。
带掩蔽的多头注意力
解码器二次多头关注,逐点前馈层
第二个多头关注层。对于这一层,编码器的输出是查询和键,第一个多头关注层的输出是值。这个过程将编码器的输入与解码器的输入相匹配,允许解码器决定关注哪个编码器输入。第二多头注意力的输出通过逐点前馈层进行进一步处理。
输出概率的线性分类器和最终 Softmax
最终的逐点前馈层的输出通过最终的线性层,该线性层充当分类器。分类器和你拥有的类的数量一样大。例如,如果您有 10,000 个包含 10,000 个单词的类,则该分类器的输出大小为 10,000。分类器的输出然后被输入到 softmax 层,该层将产生 0 到 1 之间的概率分数。我们取最高概率分数的索引,它等于我们预测的单词。
使用 Softmax 获得输出概率的线性分类器
然后,解码器获取输出,将其添加到解码器输入列表中,并再次继续解码,直到预测到令牌。对于我们的情况,最高概率预测是分配给结束令牌的最后一个类。
解码器也可以堆叠 N 层,每层接收来自编码器及其前几层的输入。通过堆叠这些层,该模型可以学习从其注意力头部提取并关注不同的注意力组合,从而潜在地提高其预测能力。
堆叠编码器和解码器
就是这样!
就是这样!这就是变形金刚的机制。变形金刚利用注意力机制的力量做出更好的预测。递归神经网络试图实现类似的事情,但因为它们患有短期记忆。如果您想要编码或生成长序列,转换器可能会更好。因为有了 transformer 架构,自然语言处理行业才能取得前所未有的成果。
查看michaelphi.com了解更多类似的内容。
✍🏽想要更多内容?在 https://www.michaelphi.com 查看我的博客
📺喜欢看基于项目的视频?看看我的 Youtube !
🥇注册我的 电子邮件简讯 ,了解最新文章和视频!
💡示出了重整器
原文:https://towardsdatascience.com/illustrating-the-reformer-393575ac6ba0?source=collection_archive---------11-----------------------
🚊高效变压器️
参见:🇫🇷 法文 的翻译
🎥如果你一直在开发用于处理顺序数据的机器学习算法——如语言处理中的文本、语音信号或视频——你可能听说过或使用过Transformermodel,,你可能知道它与 twitter 认为的不同:
图一。破冰者,摘自克里斯·曼宁教授的推特
🔊🗞最近,谷歌推出了 重整器 架构,这是一个转换器模型,旨在高效处理超长序列的数据(例如一种语言处理多达 100 万个单词)。Reformer 的执行需要低得多的内存消耗,即使只在一个 GPU 上运行也能获得令人印象深刻的性能。论文改革者:高效的变革者 r 将于 2020 年在 ICLR 亮相(并在评论中获得近乎完美的分数)。改革家模型有望通过超越语言应用(如音乐、语音、图像和视频生成)对该领域产生重大影响。
💡在本帖中,我们将尝试深入到 重整器模型 中,并尝试用一些视觉向导来理解它。准备好了吗?💪
为什么是变形金刚?
🎬NLP 中的一类任务(如机器翻译、文本生成、问题回答)可以公式化为序列到序列学习问题。长短期记忆(LSTM)神经网络,后来配备了一个[注意力机制](http://Neural machine translation by jointly learning to align and translate.),是一个用于建立此类问题预测模型的突出架构——例如在谷歌的神经机器翻译系统。然而,LSTMs 中递归固有的顺序性质是对数据序列进行并行计算的最大障碍(就速度和消失梯度而言),因此,这些架构无法利用长序列的上下文。
🚀最近的变压器型号——在论文中介绍的“注意力就是你所需要的全部”——通过消除递归并引入多头自我注意力机制,在许多任务中实现了最先进的性能。transformer 的主要新颖之处在于它的并行处理能力,这使得能够处理长序列(具有数千个单词的上下文窗口),从而以更少的训练时间产生卓越的模型,如著名的 Open AI 的 GPT2 语言模型。🤗Huggingface 的变形金刚库——拥有 100 多种语言的超过 32 个预训练模型以及 TensorFlow 和 PyTorch 之间的互操作性——是构建最先进的 NLP 系统的一项出色的开源工作。🎮用变形金刚写作和与变形金刚对话是一些有趣的演示。这个转换器已经被用于文本之外的应用,例如生成音乐和图像。
变形金刚少了什么?
🚦在深入研究重整器之前,让我们回顾一下 transformer 模型的挑战性。这需要对 transformer 体系结构本身有所了解,这一点我们在本文中无法详述。然而,如果你还不知道,Jay Alamar 的The Illustrated Transformer帖子是迄今为止最伟大的视觉解释,我强烈建议在阅读这篇帖子的其余部分之前阅读他的帖子。
🤔虽然 transformer 模型在越来越长的序列上使用时产生了很好的结果,例如(Liu 等人,2018 年)中的 11K 长的文本示例,但许多这样的大型模型只能在大型工业计算平台上训练,甚至不能在单个 GPU 上进行微调,即使是针对单个训练步骤也是如此,因为它们需要内存。例如,完整的 GPT-2 模型包含大约 1.5 亿个参数。(Shazeer 等人,2018 年)中报告的最大配置中的参数数量超过每层 0.5B,而层的数量高达 64(Al-Rfou 等人,2018 年)。
💡让我们看一下变压器模型的简化概述:
图 2:标准变压器模型的简化总结[图片灵感来自‘图解变压器’]
😕如果这个模型看起来不熟悉或似乎难以理解,我敦促你在这里停下来,并审查➡️ 的插图变压器职位。
你可能会注意到有一些👓在图表中用三种不同的颜色。每个都是独一无二的👓s 代表了 Transformer 模型的一部分,Reformer 作者将其视为计算和内存问题的来源:
👀问题 1(红色👓):注意力计算
计算长度为 L 的序列上的注意力是 O(L )(时间和内存都有)。想象一下,如果我们有一个长度为 64K 的序列,会发生什么。
👀问题 2(黑色👓):大量的层
具有 N 层的模型消耗的内存是单层模型的 N 倍,因为每一层中的激活都需要存储用于反向传播。
👀问题 3(绿色👓):前馈层的深度
中间前馈层的深度通常比注意激活的深度大得多。
重整器模型解决了上述转换器中内存消耗的三个主要来源,并对它们进行了改进,使得重整器模型可以处理高达100 万字的上下文窗口,所有这些都在单个加速器上,并且仅使用 16GB 内存。
简而言之,重整器模型结合了两种技术来解决注意力和内存分配的问题:位置敏感哈希 (LSH)来降低处理长序列的复杂性,以及可逆剩余层来更有效地使用可用内存。
下面我们将进一步探讨细节。
💥 1.区分位置哈希(LSH)注意
💭注意和最近的邻居
深度学习中的注意力是一种机制,它使网络能够根据上下文的不同部分与当前时间步长的相对性,专注于上下文的不同部分。变压器模型中存在以下三种类型的注意机制:
图 3:变形金刚模型中的三种注意力
变压器中使用的标准注意力是缩放的点积,公式如下:
从上式和下图可以看出,乘法 QKᵀ (形状为[ L,L )的计算和内存开销都在 O(L),这是主要的内存瓶颈。
图 4:(左):点积注意力的主要计算,(右)一个标记(‘它’)注意序列中其他标记的子集(‘the’,‘animal’,‘street’,‘it’,’.’)
❓But 是否有必要计算和存储全矩阵 QKᵀ ?答案是不,因为我们只对 softmax ( QKᵀ )感兴趣,它由典型稀疏矩阵中的最大元素支配。因此,正如你在上面的例子中看到的,对于每个查询 q ,我们只需要注意最接近 q 的键 k 。例如,如果 K 的长度为 64K,对于每个 q ,我们只能考虑 32 或 64 个最接近的键的一个小子集。因此,注意力机制找到了查询的最近邻关键字,但是效率很低。💡这是否让你想起了最近邻搜索?
重整器的第一个新颖之处来自于用局部敏感散列(LSH) 代替点积关注,将复杂度从 O ( L )变为 O( L log L )。
最近邻搜索的🗒 LSH
LSH 是在高维数据集中最近邻搜索的一种高效和近似方式的著名算法。LSH 背后的主要思想是选择散列函数,使得对于两点' p' 和' q' ,如果' q' 接近' p' ,那么以足够好的概率我们得到' hash(q) == hash(p)'。
实现这一点的最简单的方法是通过随机超平面保持切割空间,并附加 sign(pᵀH) 作为每个点的散列码。让我们看下面的例子:
图 5:用于最近邻居搜索的位置敏感散列的简化动画
一旦我们找到所需长度的哈希码,我们就根据它们的哈希码将这些点划分到桶中——在上面的例子中,‘a’和‘b’属于同一个桶,因为 hash(a) == hash(b) 。现在,用于查找每个点的最近邻居的搜索空间从整个数据集到其所属的桶中急剧减少。
🗒角度 LSH: 普通 LSH 算法的一种变体,称为角度 LSH,将点投影到单位球上,该单位球已被分成预定义的区域,每个区域具有不同的代码。然后,点的一系列随机旋转定义了点所属的桶。让我们通过一个简化的 2D 例子来说明这一点,这个例子摘自《改革家》一文:**
图 6:最近邻搜索的角度 LSH 的简化动画:两个点是分开的[基于论文中的例子创建的动画]
这里我们有两个点被投影到一个单位圆上,并以不同的角度随机旋转 3 次。我们可以观察到它们不太可能共享同一个哈希桶。然而,在下一个示例中,我们看到彼此非常接近的两个点将在 3 次随机旋转后共享相同的哈希桶:
图 7:最近邻居搜索的角度 LSH 的简化动画:两个点接近[基于论文中的例子创建的动画]
🚀LSH 注意了
现在 LSH 关注背后的基本想法如下。回过头来看上面的标准注意力公式,不是计算 Q 和 K 矩阵中所有向量的注意力,而是进行以下操作:
- 找到矩阵 Q 和 K 的 LSH 散列。
- 仅为相同哈希桶内的 k 和 q 向量计算标准注意力。
****多轮 LSH 注意:重复上述步骤几次,以增加相似物品不落入不同桶中的概率。
下面的动画根据论文中的图片展示了简化版的 LSH 注意力。
图 6:LSH 注意力机制的简化示意图[根据论文中的例子制作的动画]
💥 2.可逆变压器和组块
现在我们准备解决变压器中的第二和第三个问题,即大量( N )编码器和解码器层以及前馈层的深度。
🗒可逆残差网络
密切关注图 2 中的编码器和解码器块,我们意识到每个关注层和前馈层都被包装到一个残差块(类似于我们在图 6(左)中看到的)。残差网络(ResNets)——在这篇论文中介绍——是 NN 架构中使用的强大组件,用于帮助解决深度网络(具有许多层)中的消失梯度问题。然而,ResNets 中的内存消耗是一个瓶颈,因为需要将每层中的激活存储在内存中,以便在反向传播期间计算梯度。存储成本与网络中单元的数量成正比。
为了解决这个问题,由一系列可逆模块组成的可逆残差网络(RevNet )。在 Revnet 中,每一层的激活都可以从后续层的激活中精确重建,这使我们能够执行反向传播,而无需将激活存储在内存中。图 6。说明了残差块和可逆残差块。请注意,我们如何从模块的输出( Y₁,Y₂ )计算模块的输入( X₁,X₂ )。
图 6:残差网络块(左)和可逆残差块(右)示意图
🚀可逆变压器
回到我们的第二个问题,这个问题是处理 N 层变压器网络的内存需求——N 可能相当大。
Reformer 通过将 RevNet 块内的注意和前馈层结合起来,将 RevNet 思想应用于变压器。在图 6 中,现在 F 成为关注层, G 成为前馈层:
y₁=x₁+attention(x₂)
y₂= x₂+feedforward(y₁)
🎉现在使用可逆残差层代替标准残差使得在训练过程中只存储一次激活而不是 N 次T42。
🚀组块
重整器中效率改进的最后一部分处理第三个问题,即前馈层的高维中间向量,其可以达到 4K 或更高维。
由于前馈层中的计算独立于序列中的各个位置,因此前向和后向传递的计算以及反向计算都可以分成块。例如,对于向前传球,我们将有:
前向传递计算中的分块[图片取自 Reformer 论文]
🚀实验结果
作者对两个任务进行了实验:图像生成任务 imagenet64 (序列长度为 12K)和文本任务 enwik8 (序列长度为 64K),并评估了可逆变换和 LSH 哈希对内存、准确性和速度的影响。
🎉可逆变压器匹配基线:他们的实验结果表明,可逆变压器节省内存而不牺牲准确性:
可逆性对 enwik8 和 imagenet64 训练表现的影响[图片和说明取自 Reformer 论文]。
🎉LSH 关注度与基线相符:📔注意,由于 LSH 注意力是完全注意力的近似,其准确性随着哈希值的增加而提高。当哈希值为 8 时,LSH 注意力几乎等同于完全注意力:
LSH 注意力在 imagenet64 上作为散列回合的函数的效果[图像和说明取自 Reformer 论文]。
🎉他们还证明,随着序列长度的增加,常规注意力会变慢,而 LSH 注意力速度保持稳定,它在 8GB GPUs 上以通常的速度运行于长度约为 100K 的序列上:
注意力评估的速度是完全注意力和 LSH 注意力输入长度的函数。
与变压器模型相比,最终的重整器模型表现类似,但是在长序列上显示出更高的存储效率和更快的速度。
💻Trax:代码和示例
🤖重整器的代码已经作为新的 Trax 库的一部分发布。Trax 是一个模块化的深度学习训练和推理库,旨在让您从头开始理解深度学习。重整器代码包括几个例子,您可以在图像生成和文本生成任务中训练和推断。
🙏承认
我要感谢祖卡斯·凯泽生动地介绍了改革者并提供了补充材料。我还要感谢 Abraham Kang 的深刻回顾和建设性反馈。
📚参考资料和相关链接:
** [## 改革家:高效的变压器
理解连续数据——如语言、音乐或视频——是一项具有挑战性的任务,尤其是当有…
ai.googleblog.com](https://ai.googleblog.com/2020/01/reformer-efficient-transformer.html) [## Transformer:一种用于语言理解的新型神经网络结构
神经网络,特别是递归神经网络(RNNs),现在处于领先方法的核心…
ai.googleblog.com](https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html)
- 改革家:高效的改革家
- Google/Trax 深度学习库
- 图示变压器
- 拥抱脸/变形金刚 NLP 库
- 你所需要的只是注意力
- 开放 AI 的 GPT2 语言模型
- 用变压器写字
- 与变形金刚对话
- 谷歌的神经机器翻译系统**
我要去(数据)冒险了!—霍比特文本分析
原文:https://towardsdatascience.com/im-going-on-a-data-adventure-hobbit-text-analysis-a0a5cb116ed1?source=collection_archive---------50-----------------------
解释自然语言处理技术的整本书的数据分析。
在这篇文章中,我分析了 J. R. R .托尔金的书《霍比特人》的全文。我主要关注的是提取关于书籍情感的信息,以及熟悉 NLP 的技术之一——文本分块。我们将回答谁是这本书的主角,情绪如何随着每一章而变化,以及-哪一对矮人在一起出现的次数最多!
《霍比特人:意外之旅》
探索性分析
快速浏览一下数据总是应该的。它有助于了解我们在处理什么,验证数据质量,但也允许一些初步的见解。通过对句子进行分词,我得到它们的总数是 4915。它们分为 18 章。
每章的句子数量变化很大。最短的一章有 150 个句子长,最长的一章有三倍多。我们可以看到,当章节从第三章到第十章变得更长时,“情节变得越来越复杂”。然后,随着两个急剧的变化,它们的长度逐渐变短。从句子长度来看,稳定性要高得多。最低平均句子长度和最高平均句子长度的最大差异在 30%左右(句子最短的章节平均在 45 左右,最长的在 60 字左右)。如果我们仔细观察,细微的差别显示了一个类似的章节长度模式——最长的句子在第八章到第十章。最短的——第三、第十一、第十四和最后一个。我对散文的句子长度做了一个小小的研究,建议(例如这里的)平均长度应该在 15-20 个单词之间。如果我们把这作为一个参考,托尔金的写作风格是明显不同的。
情感分析
我开始为文本中的每一个句子获得一个情感分数。为了这个目的,我使用了一种叫做 VADER 的方法,它可以在 nltk . perspective 包中找到。通过调用sentimentintensitiyanalysizer上的 polarity_scores ,我们得到 4 个分数:复合、正、负和新。复合分数是一个一维的标准化句子分数,基于词典中单词的等级。它的取值范围是-1 到 1。根据标准,一个句子被认为是中性的,复合得分在-0.05 到 0.05 之间——高于 0.05 的都是积极的,低于 0.05 的都是消极的。Pos、neg 和 neu 是可以被分配到每个类别中的文本的比例,并且总和为 1。
出于这个分析的目的,我将使用复合分数,但在分析之前,让我们看看它在 Hobbit 文本上是如何工作的(这四个数字分别是:复合分数、正分数、负分数和新分数):
《霍比特人》中最多的肯定句和否定句
我觉得下面的配乐很搞笑:
句子及其复合词、词性、否定词和 neu 分值。
我完全支持,每个人都应该有选择。但另一方面,它显示了这种方法的缺陷。似乎因为这个句子太短,所以它受到了“不”的严重影响,而句子本身是一个完全中立的陈述。知道这些句子平均至少有 40 个单词长,我有足够的信心在分析中使用这种方法。
看上面的情节,我们可以看到小说以积极的情绪开始,可能是对开始旅程的兴奋。然后,它大部分保持在中性范围内,只达到一次负分,然后再次以正数结束。
让我们更详细地看看每一章和句子的百分比。一般来说,肯定句占所有句子的 25%到 40%,否定句占 30%左右。虽然每一个情绪组按章节划分的比例没有强烈的波动,但我在肯定句的百分比中发现了一个有趣的模式。除了一个例外,每一次增加都伴随着肯定句百分比的下降,反之亦然。会不会是整本书不断变化的氛围在戏弄读者?这是一个很长的镜头,但情节讲述了一个很好的故事。
文本分块
根据定义,组块需要多音序列来分割和标记它们。这个想法变得简单时,显示了一个例子。
1.命名实体
在第一次尝试中,我们将以自动方式从文本中提取命名实体——我知道书中的人物,并且可以列出他们,但是很容易错过一些重要的东西。这个任务变得更加简单,因为在库中提供了一个 nltk.ne_chunk() 函数来实现这个目的。
对于输入,我们需要准备一个句子,其中每个单词都转化为一个单词及其词性标签的元组,例如:
多音标记句
当把句子的每个单词输入到 nltk.ne_chunk() 时,我们得到一个 nltk。树对象,它以清晰的图形方式显示了哪个部分匹配块标准。在这个例子中,这意味着成为一个 NNP——单数专有名词,由“Oin”和“Gloin”满足。
nltk。树对象
我发现 nltk.ne_chunk 能够很好地处理我的数据。尽管还需要做更多的工作。许多常用词被认为是命名实体,因为它们是用大写字母书写的,例如“Hill”。我限制列表的想法是删除英语单词语料库中包含的所有以小写字母书写的单词词条。
为此我使用了nltk . corpus . words . words()。这仍然有点棘手,需要一些领域知识(当然是阅读这本书),例如,这种方法会将比尔博从命名实体中排除。
当时只有几个词,我决定手动删除,例如:“Tralalalally”和“Blimey”。整本书中前 15 个命名实体的结果列表(基于它们的出现次数)如下所示。
《霍比特人》中的 15 大命名实体
我们可以更进一步,按章节分析出现次数最多的主要人物。三个主要角色——比尔博、索林和甘道夫,毫无疑问,贯穿了整个故事。虽然在第一章中甘道夫的出现肯定更多。咕鲁是一个独一无二的角色。另一方面,史矛革在书的结尾得到了最多的关注,我们仍然可以看到他已经在第一章被宣布了。
我们还可以将提取的命名实体与前面分析部分的句子情感得分进行匹配,并查看某些字符是否在肯定句中出现的次数多于否定句。
除了埃尔隆德,情绪的分布在人物之间没有很大的不同。从前面的情节中,我们知道埃尔隆德在书中出现的次数相对较少,所以他的结果可能会有偏差。
事实证明,这并不一定意味着性格是好是坏。这也是有意义的,因为情感分数是根据角色自己说的话以及他被描述的时候来计算的。一个快乐的坏人可能不会在这样的情节中浮出水面。不过我们确实知道,没有一个霍比特人的角色仅仅被放在好/坏的环境中。
2.命名实体之间的关系
出于学习的目的,我想定义我自己的组块规则。在第一次尝试中,它是由已经存在的函数 nltk.ne_chunks 自动完成的,但是我们可以选择任何配置的任何词性标签集来从文本中提取。
下面的语法由 4 个 nnp 组成——单数专有名词。中间的两个是必需的,剩下的两个是可选的,用“?”表示。它们都由可选的 CC(并列连词)、TO、VBD(动词、过去式)或 IN(介词或从属连词)连接。
我的目标是让任何一组实体通过动词“talked to”以及简单的“and”连接起来。实际上,它导致从下面的句子中提取这样的 4 个组块:
列表中提取的块
很快,德瓦林躺在巴林旁边,菲利和基利躺在一起,多丽、诺里和奥里躺在一起,欧因、格洛因、比弗、博弗和邦布尔不舒服地堆在火边。
“lay”在词性方面被认为是一个 NN,这就是为什么 Dwalin 不幸被这个逻辑遗漏的原因。我计算了这些命名实体块一起出现的数量。
命名实体对及其出现次数
我们现在知道哪对矮人在一起出现的次数最多了——是基利和菲利,没有接近的亚军!我们也看到只有一组矮人和非矮人角色——比尔博和巴林。同样的角色也有成对的名和姓(例如比尔博·巴金斯),这是因为在我上面的定义中,名词之间的任何单词都是可选的。
摘要
借助一些工具,我们可以对整本书有一个有趣的了解。虽然它们可能不太准确——我认为用数据分析来总结艺术是非常困难的——但这是学习新技术的一个好方法。
分析的代码可以在这里找到。
这篇文章主要发表在数据部维基上。
如何提高数据科学家的工作效率
原文:https://towardsdatascience.com/im-how-to-be-more-productive-as-a-data-scientist-d6f75163527f?source=collection_archive---------16-----------------------
如果你想获得报酬并改变世界,为什么你应该掌握不性感的技能
喷火摄影在 Unsplash 上拍摄的照片
数据科学家操纵数据
如果你的数据没有良好的结构,你就无法建立有效的机器学习管道。如果您的数据状态不佳,您就无法构建一个像样的可视化。如果您的数据一片混乱,您就无法交付太多东西。
数据科学绝对是一个很酷的工作领域。这个领域正在发生惊人的进步,它正在改变世界。我们经常听到 ML、AI 和 NLP 等热门缩写词。这些先进的技术确实令人敬畏,但我们世界的现实是,数据科学家工作的很大一部分是用来清理和处理数据的。
在你可以创建一个很酷的可交付产品之前,你需要收集数据,整理数据,清理垃圾。正如迷因所说:就像冰山一样,大部分进入可交付物的工作并不是你的客户或用户所看到的。
根据一些调查,数据科学家多达 60%的时间都花在清理和组织数据上。需要明确的是,许多数据科学家所做的大部分工作都是操纵数据,这对于初级角色来说尤其如此。
数据科学不仅仅是 ML 和高级分析,数据科学是处理数据的科学。建模技术和洞察力很重要,解释直方图或散点图的能力也很重要,要知道你应该警惕从小样本中得出结论……这些都很重要,是我们工作的价值得以实现的地方,但为了分析数据,它必须处于可用状态。
面对这一事实,你可以采取一些心态。一是真正的工作是进行机器学习和高级分析,我们必须进行枯燥的数据处理才能得到结果。另一个观点(也是我推荐的观点)是,数据操作是我们交易的基础,我们应该致力于掌握它。我认为这不仅从态度的角度来看是好的,从经济的角度来看也是好的。掌握数据操作和清理从根本上来说是有价值的,并且将在早期推动你的职业发展。
生产力和价值
下一节将深入探讨一点基础经济学,并提供一个关于就业的市场驱动视角。我认为这既有趣又是求职者的宝贵视角。
工作就是你为了钱出卖你的技能/时间。雇主付给你薪水不是因为他们想让你过上好日子,而是因为他们需要你为他们工作。
在一个完全信息的自由市场中,没有一个理性的利己主义的市场参与者会为一件商品或服务支付比他们所获得的价值更高的价格。考虑到现实世界的问题,需要做一些说明。市场是受管制的,所以它们不是完全自由的。雇主在招聘时肯定没有完整的信息,所以他们可能会判断失误,支付过高的工资。以我的经验来看,自利和理性的部分通常适用于雇主和雇员之间的互动,这一点通常会变得更加真实,公司越大,公司越小,所有者的同理心就越能发挥作用。
这种交易观的重要结果是,你的生产率是你报酬的上限。也就是说,从长远来看,你得到的报酬不会超过你能为雇主创造的价值。
你越有效率,你的时间就越有价值。因此,你的生产力越高,你得到的报酬就越多。如果你在一个工作稀缺的竞争市场中,最有生产力的人将得到这份工作(或者在不完全信息的情况下被认为是最有生产力的人)。
不幸的是,你可能不会得到你时间的全部价值。你的公司会想从你的劳动中获利,也就是说,他们愿意付给你的报酬会少于或等于你提供给他们的价值。谈判和供求关系会让你的薪水降到这个水平以下。生产力只是设定了上限,但却极其重要。幸运的是,对于 2020 年的数据科学家来说,存在巨大的技能短缺,因此我们能够获取大量的劳动力价值。
这种生产力观念是你应该专业化的原因之一。擅长某事需要时间。你的时间有限,所以你应该把精力集中在某个特定领域,这样你卖给雇主的时间就更有价值。
专业化的另一个主要原因是减少竞争。更少的供应意味着你可以在谈判阶段获得更多的价值。这种生产力的想法也是为什么一个公司会投资工具,但这是另一个时间的讨论。
这种手工经济学的关键要点是:如果你想得到更多的报酬,考虑一下你的生产率。
对于那些没有经济动机的人来说,生产力对于项目的成功也很重要。如果你在有限的时间内完成一个项目,你完成工作的速度越快,就越有可能实现你的目标。
作为一名数据科学家,如何提高工作效率?
到目前为止,我概述了两点:
- 数据科学家花费大量时间处理数据
- 提高你的生产力符合你的利益
如果您的大部分时间都花在操作数据上,并且您想要提高您的生产率,那么提高您操作数据的能力是有意义的。
据我所知,提高生产率最有效的方法是提高你的技能。根据我的经验,处理数据的主要工具和技巧是:
- 结构化查询语言
- 操作系统和外壳
- Python / R
- 电子表格
- 文本编辑器
- 正则表达式
我还介绍了其他一些基本技能,这些技能与数据处理没有直接关系,但对数据科学家的工作效率也很重要。
- 搜索引擎和研究技巧
- 写作和文件
- 测试和检查您的工作
数据处理技能
结构化查询语言
SQL 是用于操作数据库中数据的工具。这是数据科学家的和基本技能。大多数结构化数据保存在数据库中。如果你想分析数据库中的数据,你需要擅长 SQL。
示例:
想象你在一家营销公司工作。你有一个客户来找你,他想要一个仪表盘来显示他们最近活动的效果。如果幸运的话,您需要的所有数据都在一个数据库中,可能存储在多个表中。
您可能需要为您的仪表板创建一些图表来显示:按天销售、按地区销售、销售来源。您需要能够计算出您的可视化套件的数据需要什么样的结构,然后尽快将数据从数据库中取出。
您可能需要使用一些公共表表达式(cte)、一些连接,并使用一些函数来转换数据。这只是一个简单的查询,这些查询可能很复杂,需要数百行 SQL。这是这项工作的基本部分之一。您越快整理好您的数据,您就能越早开始构建您的仪表板。几乎所有的数据科学任务都需要这些技能,因此掌握这些技能是必不可少的,也是很有价值的。
根据我的经验,许多数据分析师认为 SQL 是核心技能。Python 和 R 等语言被视为数据科学家的专业工具。如果你是一名学生,读到这里,你可能会想“但我想成为一名数据科学家!”。现实情况是,成为数据科学家和从事机器学习等高级课题工作的最快、最赚钱的途径可能是从一家尚未完全接受现代数据科学的公司的数据分析师开始。
如果你擅长 SQL,你可以得到这份工作,然后一旦你适应了自己的角色,你就可以开始介绍你的 Python 技能,给你的老板留下深刻印象,也许你会有机会参与任何出现的高级分析项目。否则,经过几年的工作和一点业余学习,你将拥有令人敬畏的 SQL 技能,学会基本的 ML 技能,并能够获得一份扎实的数据科学工作,从事你想做的高级工作。
你真的需要精通 SQL。
操作系统和外壳
操作系统为完成工作提供了一套强大的工具。使用鼠标完成的单调而耗时的任务可以通过几个命令在 shell 中编写脚本。任务可以通过计划任务或 cron 作业实现自动化。可以使用 choco(windows 上)、home brew(MAC OS)和 apt(Ubuntu)等软件包管理器来安装和更新程序。操作系统中有许多功能,虽然作为数据科学家您不需要掌握这些工具,但您应该很熟悉。
Shell 脚本语言值得多解释一下。Bash、windows cmd 和 PowerShell 是 Shell 脚本环境的一些例子。它们非常强大,您应该熟悉这些工具。除此之外,你可以用它们来重命名、移动和编辑大量的文件,只需要几行文字。
一个简单的 shell 脚本如何帮助您完成工作的例子是,如果您在多个目录中有 1000 个相似的文件,您希望将它们合并到一个文件中。您可以编写一个脚本来读取所有文件的内容,将输出合并成一个文档,然后将其写回磁盘。显然,这比手工操作要好得多。
你可以用像 Python 或 R 这样的语言来完成很多这样的任务,但是外壳将一直存在,因为它是操作系统。
Python / R
这些语言非常适合内存中的数据操作、处理 API 和 web 抓取,以及。csv 格式,可用于在操作系统上操作文件。
这些语言是魔法发生的地方,互联网上对训练机器学习等时尚话题给予了很多关注,所以我不打算在这里讨论这些。为了完成工作,您应该熟悉许多数据操作任务。
数据帧操作—这与 SQL 中的操作相同,您需要能够快速重塑数据并构建新列来显示数据的特征—深入研究这一点
数据不仅仅存储在平面文件和数据库中,还有其他数据格式,比如 XML 和 JSON,您应该能够处理它们。能够反序列化和序列化 JSON 对于处理 API 非常重要。
互联网上有很多来自 twitter 等服务的 API 形式的数据。这些 API 允许您通过互联网查询数据集。数据通常以 JSON 等结构化格式返回给您,然后您需要将其转换成数据帧进行分析。库的存在是为了处理流行的 API,但是你应该熟悉使用没有包的 API。
除了可以在 shell 中进行简单的文本操作之外,这些语言对于脚本编写非常有用,我处理过的一个例子是:我们收到了一个包含 1000 个电子表格的文件夹,每个表格都有一台机器上的一份单独的日报告。我们需要将这些放在一个文件中进行分析,使用 R I 编写了一个脚本来导入各个电子表格,然后遍历文件夹,将所有文件读入一个列表,然后组合各个数据帧。
电子表格
适用于小型数据集和数据输入。有利于跟踪工作。非数据科学家使用它,所以你需要知道它。大多数商务人士可以使用电子表格,它们非常便携。Excel 只能容纳一百万行数据,因此它是有限的。电子表格操作不像编写好的代码那样可重用,而且很难通过编写工具来提高生产率。
你需要擅长电子表格,因为其他人知道如何使用电子表格。对于大多数人来说,数据分析意味着电子表格,所以如果你与非数据科学家互动,你很可能不得不使用电子表格作为交流的媒介。人们会给你电子表格,你需要了解他们做了什么。你需要能够清楚地展示你的工作,因为客户和经理可能希望看到计算是如何进行的。
电子表格也是你解决问题的好工具。例如,假设您有一个包含姓名和年龄列表的电子表格,您必须将其添加到数据库中。您应该能够快速地将这些姓名和年龄转换成正确格式的列(例如,(‘name’,‘age’),以便您可以将其复制到 SQL insert 查询中的 values 语句中。
你应该擅长使用 excel,但你不应该生活在 excel 中,电子表格是你工具箱中的强大工具,但数据科学家主要生活在代码中。
文本编辑器
文本编辑器是一个强大的东西。它们是你用来检查原始数据文件的工具,是操纵这些原始数据文件的方法。文本编辑器是处理数据任务的重要工具。
我上面的电子表格例子很好地使用了文本编辑器。您可以在 excel 中创建格式为(‘姓名’,‘年龄’)的列,但是如果您将它直接复制到文本文件或 SQL 编辑器中,它将复制“(‘姓名’,‘年龄’)”,因为它是一个字符串。使用文本编辑器,您需要能够使用类似 find 的工具删除外部的双引号,并用空白替换双引号,或者垂直选择双引号并手动删除它们。
有许多文本编辑器,你真的需要熟悉一个。windows 默认记事本并不是一个好的选择,因为它非常有限,但许多 ide,如 VS Code 和 RStudio,都有很好的文本编辑功能。对于下一级的文本操作,看看 vim 和 emacs,它们在终端中可用,许多图形文本编辑器支持这些程序提供的一些功能。
正则表达式
这里特别提到了 Regex。它们本身并不是一套独立的工具,但是它们非常有用,你应该知道它们的基本知识。
正则表达式基本上允许你以一种通用的方式描述字符串,允许你指示计算机给你带来一个字符串的变体。
正则表达式有用的一个简单例子是替换. csv 文件中的日期。假设您有一个日期格式为 yyyy-MM-dd hh:mm:ss 的平面文件,例如 2020–01–25 20:13:00,并且您想要将日期更改为 1 月 28 日,您可以对[0–9]{ 4 }-[0–9]{ 2 }-[0–9]{ 2 }执行正则表达式匹配,并将其替换为您的日期。也就是 4 位数,然后是“-”,然后是 2 位数,然后是“-”然后是 2 位数。这是一个简单的例子,但它应该展示 regex 的强大功能,以及为什么您应该花一些时间学习基础知识。
关于自动化的一个注记
工具是自动化。您可能会想,这项工作将会自动化,而且会的,有了更好的工具。例如,在 VBA,我们过去必须为数据分析编写循环,现在有了 pandas 和 dplyr 这样的包,我们可以编写列表达式并调用 mutate()。
这些工具将不断进步,变得更加强大,关键是你必须学会如何使用它们。把这些工具想象成神奇的语言,你在告诉计算机你想做什么,你做得越好,你就越容易表达你的愿望。
变得更好需要练习和对学习的承诺。在编程中,您经常会面临一个选择,要么编写复杂而强大的东西,要么手动重复任务,随着时间的推移,您将更擅长编写复杂的东西,这将是优雅的,但您经常会发现,在编写优雅的脚本和用电子表格、文本编辑器和一点编程拼凑一个数据处理解决方案之间有一个权衡。
其他重要技能
搜索引擎和研究技巧
“如果你给一个饥饿的人一条鱼,你可以喂他一天,如果你教他如何捕鱼,你可以喂他一辈子,如果你教他如何谷歌,他可以学会如何喂饱他的整个村庄,给他衣服和房子。”—欧文·弗拉纳根,2020[有问题的引用]
你需要擅长使用搜索引擎。大多数编码问题都可以通过正确表达您的问题来解决,这种表达方式会给您带来文档、论坛帖子和堆栈溢出响应。为了做到这一点,您需要了解术语并练习阅读技术文档。从更广泛的意义上来说,这也涵盖了学术研究和 google scholar。
写作和文件
作为一名数据科学家,你需要写作。你需要写电子邮件,你需要解释你自己和你所做的工作。基本上,你需要能够写出人们能读懂的句子,并且能够组织一个段落。这是任何职业的基本技能,需要练习。你写得越好,你要做的工作就越少,如果你给你的同事、经理或客户发的是没有上下文的冗长混乱的电子邮件,你将不得不写更多来解释这一点。你应该善于提炼你试图传达的关键点,然后围绕它们来组织你的文章。
除了优秀写作的基本要素之外,围绕文档的良好习惯还有一个生产力的争论。向人们解释事情需要时间;如果你记录下获得结果的步骤,你可以与需要知道的人分享,而不是单独解释。这对加入你团队的人尤其重要;如果你有很好的文档,当一个新人加入时,你可以给他们看文档,并给他们一些关于流程的培训,之后他们应该能够查阅文档,了解他们在你的培训中记不起的任何事情。
投资于好的文档可以节省您的时间。从长远来看,培养记录过程的习惯和技能将会节省时间。这一点也适用于代码中的注释。
“文档是你写给未来自己的情书”——达米安·康威
测试和检查您的工作
数据科学家承担着重要的工作。我们必须提供见解来帮助决策者确定最佳前进道路,我们建立机器学习模型来做出预测,这些预测可以帮助决定人们是被雇佣还是让从监狱获释。甚至像数据输入和配置数据库中的东西这样的小任务也留给我们来检查。测试和检查你的工作是工作的一大部分。
出错可能会很严重,如果你在为销售团队制作仪表板,你可能会让人们困惑或紧张,直到问题出现。如果你向一位 CEO 展示数字,让他做出紧急决策,那么数百万美元可能会丢失。如果你正在制造无人驾驶汽车,人们可能会死亡。
在现实世界中,除了你和你的团队,没有人检查事情是否做得正确。检查你的工作可能是工作中最重要的部分,交付糟糕的工作会导致严重的伤害和损害你的声誉。虽然这是枯燥乏味的,但应该尽最大努力注意细节。
当我们谈论生产力时,把事情做好将是重中之重。
结论
我们听到很多关于对数据科学家的巨大需求,我们看到很多课程教人们成为数据科学家。这些课程中有很多侧重于高级技术,任何数据科学新手都可能认为工作主要是构建和优化模型。当他们听说未来几年存在巨大的技能赤字并且需要数百万数据科学家时,他们认为这意味着数百万人需要培训和部署机器学习模型并推进该领域。
我会认为,这个世界不需要数百万人工智能研究人员,这个世界需要数百万能够让数据变得有用的人。这就是这些技能的意义所在,让数据变得有用。在交付任何项目之前,需要做大量的工作来使数据处于良好的状态。很多工作都是单调乏味的,但是没有这些工作,我们就无法取得任何成果。对于有抱负的数据科学家来说,事情并不是这样描述的,我知道这不是我刚开始涉足该领域时所想的。
通过投入时间掌握操纵数据所需的技能,你可以完成更多的工作,增加你的价值。这就是机会所在,也是人们受雇要做的事情。数据处理得越快,就有越多的时间来构建酷的东西。然而在另一个层面上,许多工具都是基于语言的,所以更多的技能意味着使用更优雅更周到的解决方案,这意味着工作更有趣。
作为一名数据科学家,无论你从事什么工作,这些技能总是有用的。我的观点不仅仅是善于使用这些工具会帮助你更好地完成工作,而且我们的态度应该是大声宣传处理数据的好方法。我们应该大声宣传测试和检查你工作的好方法。我们应该大声疾呼将文档集成到我们的工作流程中的好方法。我们应该大声疾呼,让这些技能得到应有的尊重,让我们都能变得更有效率。
我没有图层了——如何定制 TensorFlow 2 图层。
原文:https://towardsdatascience.com/im-out-of-the-layers-how-to-make-a-custom-tensorflow-2-layer-9921942c88fc?source=collection_archive---------17-----------------------
有时需要编写自己的神经网络层,接受非标准数据或以不寻常的方式处理它们。
TensorFlow 2 使机器学习框架更容易使用,但仍保留了构建模型的灵活性。它的一个新特性是通过集成的 Keras API 构建新的层,并使用 eager-execution 轻松调试这个 API。
在本文中,您将学习如何在 TensorFlow 2 框架中构建自定义神经网络层。撰写本文时,我假设您对 Python 3 中的面向对象编程有基本的了解。最好是在阅读本文之前先回顾一下__init__
、__call__
、类继承和方法重写。
用于构建图层的模板
让我们从一个模板开始,基于它你将建立你的大部分层。
新 TensorFlow 2 层的模板,根据需要使用它。
如你所见,结构很简单。该模板由两个方法组成,__init__
和 call。第一个方法是一个类的标准 Python 3 构造函数,我们在其中初始化所有对象和字段。因此,在层的情况下,您在这里初始化将在训练中使用的所有变量。还要记住通过调用super(YourClassName, self).__init__()
来调用图层类的__init__
方法,否则图层类的构造函数不会被初始化。方法调用是基于给定的输入和变量定义所有向前和向后的地方。你唯一要做的就是定义正向传播步骤,反向传播步骤由 TF2 自动执行。
此外,还有两个强制参数—一个是x_input
,另一个是training
。输入可以是任何类型,如果需要,还可以添加更多的输入参数。然而,必须至少有一个输入,否则正向和反向传播步骤是不可能的。此外,还有训练增强,我将它设置为默认的False
(测试期间默认没有False
)。它用于某些层,在测试和训练阶段的行为是不同的。比如 dropout 就是这样一个层,用在这里是因为它只在训练阶段丢弃神经元。
如果你很好奇为什么用call
而不是 pythonic 式的__call__
,那么看看文章的最后。
自定义层的示例
让我们定义一个更复杂的层,这一次它将执行一个非常复杂的操作,那就是……两个实数的乘法。
允许两个标量数相乘的层
这一次,这些层由call
方法中的两个输入和一个将两个数相乘的tf.math.multiply
方法组成。要使用该层,您只需定义创建多层对象,并通过向其传递值来调用它,如下所示:
一个如何使用我们创建的多层的例子。
先自己试试,再往前跑。下一个更复杂,这次我不玩文字游戏了。但你会熬过去的,相信我。
用 TF2 建立残差层神经网络
让我们假设,你想要创建一个神经网络来预测几个类。为此,您将在流行的 CIFAR-10 数据集上训练网络,该数据集有 10 个不同的类。为了解决这个问题,你选择了残差神经网络(简称 ResNet),但它要求你建立自己的层,因为它们不是在 TF2 实现的,或者你想玩不同的架构。ResNet 由剩余块构成,这些块具有跳跃连接,抵消了消失梯度问题。
ResNet 块
这里有两种类型的块,一种保持输入形状(因此输出形状与输入形状相同),另一种改变。ResNet 块具有如下所示的体系结构,其中最重要的是跳过连接(有时称为快捷连接)。跳过连接只不过是复制输入并将其添加到输出的末尾,这并不神秘,但它工作得很好。
ResNet 块的一般视图
将 ResNet 定义为 TF2 的自定义图层
让我们现在尝试建立一个剩余块。我不会根据一些架构来创建层,而是根据一些直觉。此外,我会尽量让事情简单,但它会占用更多的空间(不必要的重复),我会预约如何做得更好。
有了构建 TF2 层的基础,让我们尝试一下构建 ResNet 的新技能,在 CIFAR10 基准数据集中把自己放在靠前的位置。在这里,您可以找到一个带有相应文件的排行榜:
[## CIFAR-10 排行榜|带代码的论文
CIFAR-10 目前的最新水平是 BiT-L (ResNet)。查看 91 篇论文与代码的完整对比。
paperswithcode.com](https://paperswithcode.com/sota/image-classification-on-cifar-10)
让我们首先画出我们的标识块,我们将使用它来构建我们的自定义层(即 ResNet 块)。
首先,我们将构建一个身份块,我称之为ResidualLayer
在代码中,这一层由两条路径和三个卷积块以及一条快捷路径组成(见下图)。在训练过程中,宽度通常会提高准确性[1]。随意修改路径的数量,并检查它如何影响结果的准确性,训练时间和其他参数。值得注意的是,两条路径应该有不同的超参数集,以学习如何提取不同的特征集。
在我们的例子中使用的剩余块,代码如下。快捷方式块表示前面提到的跳过连接。
第二个残差块不保留输入形状,因此跳过连接必须重新缩放输入。这是用卷积层完成的。其余的几乎与前面显示的剩余块相同。该层将在代码中以与上一个类似的方式调用,但使用 Scal 后缀,所以ResidualLayerScal
。
快捷路径上带有缩放层的剩余块。
残余块的代码
剩余块的代码在这里,用于ResidualLayer
和ResidualLayerScal
。代码有点长,所以你不能在中型网页上看到它的全部。你必须点击它,直接进入要点。
保留输入形状的剩余图层
不保留输入形状的剩余图层
注意评论和training=False
。
使用剩余层
所有的代码看起来都很复杂,但是大部分代码都是某种模式的重复。大部分代码可以(甚至是)通过一个或两个 for 循环重复(一个用于控制深度,另一个用于控制宽度)。但是为了使代码更简单,我认为最好不要在这里使用它。
要创建这样一个层并使用它,您只需以与MultiplyLayer
相同的方式创建该类的一个实例。
请随意修改这些参数以获得更好的结果。
将其分组为块
从“纸”入手,总比没有任何计划就匆忙投入工作要好。因此,让我们制作一个块的模式,块将由五个剩余层组成,如下图所示,并被进一步称为…猜怎么着,一个剩余块!
一个由 5 个定制剩余层组成的剩余块。
在这样的块中有两种类型的层,一层不保留输入形状— ResidualLayerScal
,四层保留— ResidualLayer
。定义好模块后,让我们构建一个模型并测试这些层。
建立模型
整个模型的模式不会更复杂,它是四个剩余块的重复,在开始时具有批量标准化、激活函数和卷积层,在结束时具有密集输出的另一个批量标准化。
整个模型的模式,看起来很简单,你不觉得吗?
该模型将通过创建一个继承自tf.keras.Model
类的类来实现。姑且称模型类为FunnyResNet
(听起来一点都不好笑)。代码来了:
这个模型实现了前面展示的模式,它是如何工作的你可以在我的另一个故事中了解到。
[## 如何从头开始创建 TensorFlow 2 模型——一个“无用”的例子。
想象一下,您想要构建一个可以在项目中使用的模型。你选择做张量流 2…
towardsdatascience.com](/creating-a-trashy-model-from-scratch-with-tensorflow-2-an-example-with-an-anomaly-detection-27f0d1d7bd00)
培训和评估
定义好你的模型后,你需要做的一件事就是用model.compile()
定义它,用model.fit()
训练模型。这里展示的整体 Resnet 模型你可以在我的知识库中找到,还有图像标准化、规范化和增强。
在编译之前将两幅图像传递给模型的目的是初始化权重形状。这是在 TF2 自动完成的,基于第一次出现的输入。
在这里,您可以找到一个 Jupyter 笔记本,上面有所有代码:
[## 丹尼尔维奇/搞笑网
TowardDataScience-DanielWicz/funny resnet 上的教程示例
github.com](https://github.com/DanielWicz/FunnyResNet)
或者直接在谷歌的 colab:
[## DanielWicz/FunnyResNet 谷歌联合实验室
TowardDataScience 教程的 resnet 示例](https://colab.research.google.com/github/DanielWicz/FunnyResNet/blob/master/SimpleResNet.ipynb)
摘要
在 TF2 创建图层并不是一件难事,您需要记住以下几点:
- 它将复杂的结构简化成一个可用的“块”,可以很容易地重复多次。
- 该层必须从
tf.keras.Layer
继承才能工作。 - 您必须定义两种方法,
__init__
和call
。 - 在
__init__
方法中,你定义了所有将在调用方法中进一步使用的变量,如对象(如层)、常量。 - 在
call
中,您定义了应用于数据的所有操作,如分层、乘法、加法、蒙版、转置等。 - 记得在
call
中传递training
参数,否则,dropout 和 batch normalization 不起作用。
此外,记得使用专用的张量流函数
总是在层中使用 TF 函数,否则你将不能用model.fit()
来编译它们或者用@tf.function
语句来使用它们(这意味着低得多的性能)。在 TF2,你需要的几乎每一个 pythonic 函数都有替代品。例如,在一个层中执行一个 For 循环,你将使用for i in tf.range(N)
或者使用你使用的数组tf.TensorArray
。
说明
- 定义了
call
方法而不是__call__
,因为__call__
在调用call
之前会调用一些额外的东西。这些东西包括初始化权重的方法,以及确保该层正常工作的所有其他东西。这是一个与Model
相似的案例,你可以在我的故事中读到。
附加阅读
- 谷歌自定义图层指南:此处
- TF2 图层类文档:此处
参考资料:
[1]s . Zagoruyko 和 n . Komodakis(2016 年)。广泛的剩余网络。 arXiv 预印本 arXiv:1605.07146 。
图像增强掌握:15+技术和 Python 代码的有用功能
原文:https://towardsdatascience.com/image-augmentation-mastering-15-techniques-and-useful-functions-with-python-codes-44c3f8c1ea1f?source=collection_archive---------32-----------------------
平滑的 python 代码可自行扩充您的影像数据集。
无论我们是在享受 Keras 还是 Pytorch 我们都可以使用美妙的图书馆来有效地提升我们的形象。但是那些特例呢,其中:
- 我们的数据集不具有平凡的结构(例如,3 个输入图像和 1-2 个分段输出)。
- 在我们所做的事情上,我们想要完全的自由和透明。
- 我们希望增加这些库没有提供的功能。
对于所有这些情况和许多其他情况,我们必须能够掌握我们的图像增强。此外,我每次都使用自己的函数,而且我不是唯一一个这样做的人(我想)。因此,在这篇文章中,我将向您介绍并我的功能来执行图像增强。
举个例子,我会用一个朋友的照片来提醒我在加拿大度过的美好时光。我希望你喜欢这张照片,因为这是你在这篇文章中看到的唯一一张照片...🍁👼🍁
我将分段它,以便有一个实际案例使用我们的图像和目标函数。这会让你对将要向你描述的方法的灵活性有一个概念:
- 翻转
- 农作物
- 内核过滤和锐化
- 虚化
- 旋转、平移、剪切、缩放
- 断流器
- 色彩空间
- 聪明
- 对比
- 均匀和高斯噪声注入
- 渐晕
- 透镜畸变
⚠️:这篇文章的目的是不是证明增强技术是如何设计的,而是如何理解使用它们。⚠️
一些有用的功能
在我开始之前,我想解释一下的常见结构 每个方法都会有。它实际上将是一个对象来初始化。这个对象将用我们的样本作为参数被调用,并将返回我们的扩充样本。就这些吗?是的,就这些!那么,在我们的增强功能之前,让我们先从一些有用的功能开始。
调整大小
第一个有用的函数允许我们用(width, height)
形状来调整图像的大小。这个类允许我们看到所有其他的类将如何被初始化。我们用上游参数化的实例化一个大小的对象。在我们的例子中,所有的样本(图像和目标)都将以这个相同的尺寸返回。有了这种类型的功能,我们可以轻松地同时以我们想要的方式处理我们的图像和目标。
夹子
剪辑功能是一个非常有用的功能,尤其是当你需要从一个色彩空间切换到另一个色彩空间或者在 0 和 1 或者 0 和 255 之间重置图像时。
默认情况下,如果只指定了一个阈值,最小阈值为 0。
使正常化或标准化
在传递模型中的输入之前,我们通常希望标准化或标准化我们的数据。当然,这些操作可以在特定的轴上完成。
默认情况下,对整个图像执行规范化和标准化。
托特索尔
最后,如果你使用的是 Pytorch,你需要将你的图片转换成 T1。唯一需要注意的一点是,使用 Pytorch,我们的图像是先通道后通道,而不是最后一个通道。最后,我们还可以留下选择张量的输出类型的可能性。
怎么用?
也许在这一点上,你没有看到的设置有多简单。然而它是。我们所要做的就是定义一个我们想要在样本上做的转换列表,就这样。之后我们不碰任何其他东西。注意转换的顺序有其重要性。由你决定。
我们现在可以深入文章的目的,看看图像增强技术。
翻转
第一种,也是中最简单的一种,由随机在图像的水平和垂直轴上执行翻转组成。换句话说,有 50/50 的机会进行垂直翻转,有 50/50 的机会进行水平翻转。
农作物
为了进行图像增强,通常的做法是随机裁剪图像。换句话说,我们裁剪一部分随机 大小的图像,并覆盖一个随机 区域。
裁剪图像的尺寸可以从尺寸(height, width)
的 比率中选择。如果没有指定裁剪的比例最大尺寸,那么我们将默认认为它是图像的尺寸。
内核过滤器
一般情况
我们将开始一些更有趣的事情。滤镜是伟大的经典作品,但我认为能够轻松创建我们自己的卷积滤镜是很重要的。如果您不知道滤波器的工作原理,请参考我关于 Conv2d 的文章。
[## Conv2d:终于明白了向前传球会发生什么
2D 卷积层及其参数的视觉和数学解释
towardsdatascience.com](/conv2d-to-finally-understand-what-happens-in-the-forward-pass-1bbaafb0b148)
所以我想做一个通用的函数来使用我们自己的过滤器。
尖锐
就过滤器而言,通过选择上游的过滤器并应用随机加权,可以走得更远。例如,我向你介绍用于锐化我们图像的滤镜。
value of center
从 0 到 65。
虚化
为了完成过滤器,最流行的是用来随机模糊我们的图像。有很多方法可以模糊我们的形象。最著名的是平均、中值、高斯或双边滤波器。
平均模糊度
内核大小从 1 到 35
关于平均滤波器。顾名思义:它允许我们对给定中心的值进行平均。这是由一个内核做出的。它的大小可以指定或多或少的模糊。为了用平均过滤器增加我们的图像,我们只需要用随机大小的内核来过滤我们的输入图像。
高斯模糊
内核大小从 1 到 35
最后,以同样的方式平均模糊。高斯模糊不使用平均滤波器,而是使用滤波器,因此值对应于中心的高斯曲线。注意,内核维度必须只包含奇数。
视角转换
到目前为止应用最广泛的图像增强技术是透视变换。有旋转、平移、剪切、和缩放。这些转换可以在一个 3D 维度中执行。通常,他们只在 2D 使用,这是一个遗憾。让我们利用我们所拥有的一切,对吗?
旋转
翻译
剪羊毛
缩放比例
结合一切
我不会在 2D 图像的 3D 变换上花更多的时间,因为我写了一整篇文章。所以我选择了本文末尾的函数。如果你想了解更多关于齐次坐标和三维变换矩阵的信息,我邀请你去看一下。
** [## 如何将 2D 图像转换成三维空间?
用 python 执行变换矩阵。数学和视觉解释视频游戏和深…
towardsdatascience.com](/how-to-transform-a-2d-image-into-a-3d-space-5fc2306e3d36)
应该注意的是,这个函数允许我们根据 4 个建议的矩阵随机执行转换。秩序有它的重要性。这里有剪切,然后是旋转,然后是缩放,最后是平移。注意,平移是通过图像尺寸的比例来完成的。****
结合随机旋转平移剪切和缩放
断流器
剪切块替换为 0,在整个输入上同时裁剪目标
断流漂亮直观。它包括随机移除输入图像的区域。它在中的工作方式与我们之前谈到的裁剪相同。但是我们没有返回相关的区域,而是删除了它们。因此,我们可以再次允许用户提供要删除的区域的每比例的最小和最大尺寸,区域的最大数量,以便同时或不同时从目标中剪切区域,我们可以对每个通道执行此剪切,并且还可以选择已删除区域的默认替换值。****
剪切替换为 1,输入通道尺寸,不裁剪目标
色彩空间
现在我们到了我觉得最有趣的部分。很很少考虑的一部分。如果我们知道色彩空间,我们可以利用它们的特性来增强我们的图像。给你一个简单的例子,有了 HSV 颜色空间,我们可以尽情地提取叶子,感谢它的颜色和根据我们的愿望随机改变它的颜色。这是一件非常酷的事情!我们理解拥有自己的图像增强功能的好处。当然,这需要多一点创造力。因此了解我们的色彩空间以充分利用它们是很重要的。特别是因为它们在我们的(深度)机器学习模型的预处理中可能是至关重要的。
** [## 了解并可视化色彩空间,以改进您的机器学习和深度学习模型
解释、分析和实验 14 种流行的颜色空间以及它们对我们模型的准确性的影响。
towardsdatascience.com](/understand-and-visualize-color-spaces-to-improve-your-machine-learning-and-deep-learning-models-4ece80108526)
聪明
亮度从-100 到 100
让我们再坚持一会儿。图像增强的一个伟大经典就是能够玩亮度。有几种方法可以做到这一点最简单的是简单地添加一个随机偏差。
对比
对比度从-100 到 100
同理,玩对比也很简单。这也可以随机进行。
噪声注入
最后一种相当常见的图像增强技术是噪声注入。实际上,我们只添加了一个与输入大小相同的矩阵。该矩阵由随机分布的个元素组成。可以从任何随机分布中进行噪声注入。实际上,我们只看到其中的两个。但是可以随意走得更远😃
制服
高斯的
渐晕
最后,用得少但不是没用。有些相机有渐晕效果。想想我们如何通过随机模仿这种现象来增加我们的形象也是很有趣的。我们也将尝试给予用户灵活性。我们将能够决定从效果可以随机开始的最小距离,决定它的强度,甚至决定它是一个走向黑色还是走向白色的效果。
透镜畸变
最后最好的最后。我很惊讶它没有被更多地使用。但是它可以模仿相机镜头的扭曲。就像透过圆玻璃看一样。我们看到的是扭曲的,因为镜片(玻璃)是圆的。所以,如果我们的图像是从一个有镜头的相机中拍摄的,为什么我们不模拟它们呢?这应该是默认用于图像 s 的,至少我是这么认为的。
因此,我建议在最后一个函数中,通过利用径向系数k1, k2, k3
和切向系数p1, p2
来随机模拟我们的镜头失真。在该方法中,系数的顺序如下:k1, k2, p1, p2, k3
。我邀请你看一看关于这个主题的 OpenCV 文档。
[## 使用 OpenCV 进行摄像机校准——OpenCV 2.4.13.7 文档
照相机已经存在很长时间了。然而,随着 20 世纪 90 年代后期廉价针丨孔丨摄像机的出现…
docs.opencv.org](https://docs.opencv.org/2.4/doc/tutorials/calib3d/camera_calibration/camera_calibration.html)
知识就是分享。
支持我,获取访问 中我所有文章的点击此处 。
来源和参考
一切都是自制和免费使用(包括图片和 gif)
特别感谢 Elise 授权使用此图片👋****
使用 skimage 增强图像— Python
原文:https://towardsdatascience.com/image-augmentation-with-skimage-python-7f53f1b0eeb5?source=collection_archive---------22-----------------------
个人电脑:Flickr
嘿伙计们,最近我在解决一个图像分类问题。但不幸的是,其中一个班级没有足够的样本。我在网上搜索,了解到一种叫做图像增强的技术。在这里,我分享了我对这种技术的理解,并分享了一些使用 skimage 的代码。你可以在底部找到 jupyter 笔记本。
什么是图像增强?
影像增强是一种用于人为增加影像数据集大小的技术。这可以通过对图像进行随机变换来实现。
我们知道,当深度学习模型能够看到更多数据时,它们能够很好地进行归纳。数据扩充可以创建现有图像的变体,这有助于更好地进行归纳。
但是你也需要小心。我们可以使用增强图像进行训练,但不能用于测试。因此,在应用任何增强之前,请将您的数据分成训练集和测试集。使用图像增强功能扩展您的训练数据。
图像增强主要应用于图像的两个领域
- 位置增强
- 增色
什么是位置增强?
位置增强很简单,我们在像素位置上应用不同的变换。
缩放、旋转、裁剪、翻转、填充、缩放、平移、剪切和其他仿射变换是位置增强的示例。让我们尝试应用其中的一些转换。
import numpy as np
from skimage.io import imread, imsave
import matplotlib.pyplot as plt
from skimage import transform
from skimage.transform import rotate, AffineTransform
from skimage.util import random_noise
from skimage.filters import gaussian
from scipy import ndimage# load Image
img = imread('./butterfly.jpg') / 255# plot original Image
plt.imshow(img)
plt.show()
原始图像 PC: Flickr
# image rotation using skimage.transformation.rotate
rotate30 = rotate(img, angle=30)
rotate45 = rotate(img, angle=45)
rotate60 = rotate(img, angle=60)
rotate90 = rotate(img, angle=90)fig = plt.figure(tight_layout='auto', figsize=(10, 7))fig.add_subplot(221)
plt.title('Rotate 30')
plt.imshow(rotate30)fig.add_subplot(222)
plt.title('Rotate 45')
plt.imshow(rotate45)fig.add_subplot(223)
plt.title('Rotate 60')
plt.imshow(rotate60)fig.add_subplot(224)
plt.title('Rotate 90')
plt.imshow(rotate90)plt.show()
# image shearing using sklearn.transform.AffineTransform
# try out with differnt values of shear
tf = AffineTransform(shear=-0.5)
sheared = transform.warp(img, tf, order=1, preserve_range=True, mode='wrap')sheared_fig = plot_side_by_side(img, sheared, 'Original', 'Sheared')
# Image rescaling with sklearn.transform.rescale
rescaled = transform.rescale(img, 1.1)rescaled_fig = plot_side_by_side(img, rescaled, 'Original', 'Rescaled')
plt.show()print('Original Shape: ',img.shape)
print('Rescaled Shape: ',rescaled.shape)Output:
Original Shape: (684, 1024, 3)
Rescaled Shape: (752, 1126, 3)
# flip up-down using np.flipud
up_down = np.flipud(img)fig_updown = plot_side_by_side(img, up_down, 'Original', 'Up-Down')
plt.show()
# flip up-down using np.flipud
left_right = np.fliplr(img)fig_lr = plot_side_by_side(img, left_right, 'Original', 'Up-Right')
plt.show()
什么是增色?
色彩增强是一种我们利用像素强度值的技术。
我们通过调整亮度、对比度、饱和度来再现不同的图像,我们还可以给图像添加随机噪声。
# Apply Random Noise to image using skimage.utils.random_noise
noised = random_noise(img, var=0.1**2)fig_noised = plot_side_by_side(img, noised, 'Original', 'Noised')
plt.show()
# Increasing the brighness of the Image
# Note: Here we add 100/255 since we scaled Intensity values of Image when loading (by dividing it 255)
highB = img + (100/255)fig_highB = plot_side_by_side(img, highB, 'Original', 'highB')
plt.show()
# Increasing the contrast of the Image
# Note: Here we add 100/255 since we scaled Intensity values of Image when loading (by dividing it 255)
highC = img * 1.5fig_highB = plot_side_by_side(img, highC, 'Original', 'highC')
plt.show()
你有没有注意到一件事,我们已经从一个图像创建了 11 个不同的图像。请注意,我们仍然可以使用参数并创建更多的参数。
当训练神经网络时,我们可以向图像加载器添加随机变换。还有其他高级技术,比如使用 GAN 进行数据扩充,我们可以在另一篇文章中看到。我希望你现在明白什么是图像增强。
你可以在https://github . com/mathan Raj-Sharma/sample-for-medium-article/blob/master/image-augmentation-skimage/image-augmentation . ipynb找到笔记本
使用深度学习的医疗报告生成
原文:https://towardsdatascience.com/image-captioning-using-deep-learning-fe0d929cf337?source=collection_archive---------16-----------------------
利用编解码模型和注意机制生成胸部 x 光医学报告。
由拉蒙·萨利内罗在 Unsplash 上拍摄
内容:
- 介绍——理解问题
- 先决条件
- 数据
- 获取结构化数据
- 准备文本数据— 自然语言处理
- 获取图像特征 — 迁移学习
- 输入管道 — 数据生成器
- 编解码模型 — 训练、贪婪搜索、波束搜索、BLEU
- 注意机制——训练、贪婪搜索、光束搜索、BLEU
- 总结
- 未来工作
- 参考文献
1.介绍
图像字幕是一个具有挑战性的人工智能问题,它是指根据图像内容从图像中生成文本描述的过程。例如,请看下图:
由 Anthony Tran 在 Unsplash 上拍摄的照片|你会如何描述这张照片?
一个常见的答案是“一个弹吉他的女人”。我们人类可以看着一幅画,用适当的语言描述里面的任何东西。这很简单。让我给你看另一个:
嗯,你怎么描述这个?
对于我们这些“非放射科医生”来说,一个常见的答案是“胸透”。嗯,我们没有错,但是放射学家可能会有一些不同的解释。他们撰写文字报告,叙述成像研究中检查的身体各部位的发现,特别是各部位是否发现正常、异常或潜在异常。他们可以从一幅这样的图像中获得如此有价值的信息并做出医疗报告。
对于缺乏经验的放射科医生和病理学家,尤其是那些在医疗质量相对较低的农村地区工作的放射科医生和病理学家来说,撰写医学成像报告是一项艰巨的任务,或者另一方面,对于有经验的放射科医生和病理学家来说,撰写成像报告可能是乏味且耗时的。
因此,为了解决所有这些问题,如果一台计算机能够像放射科医生那样将如上图所示的胸部 x 光片作为输入,并将结果以文本形式输出,岂不是很棒?
图片作者|我们现在是在要求机器做医生吗?!!
但是,你真的能写出这样的计算机程序吗?如果答案是否定的,你就不会读到这个故事。
2.先决条件
这项工作假设一些深度学习熟悉的主题,如神经网络,CNN,RNNs,迁移学习,Python 编程和 Keras 库。下面提到的两个模型将用于解决我们的问题,稍后将在本博客中简要说明:
- 编码器-解码器模型
- 注意机制
对它们有足够的了解将有助于你更好地理解模型。
3.数据
您可以从以下链接获得解决此问题所需的数据:
- 图像 - 包含所有胸部 x 光片。
- 报表--包含了上图对应的报表。
图像数据集包含一个人的多张胸部 x 光照片。例如:x 射线的侧视图、多个正视图等。正如放射科医师使用所有这些图像来编写发现一样,模型也将一起使用所有这些图像来生成相应的发现。数据集中有 3955 个报告,每个报告都有一个或多个相关联的图像。
3.1.从 XML 文件中提取所需的数据
数据集中的报告是 XML 文件,每个文件对应一个人。与该人相关联的图像 id 和相应的发现包含在这些文件中。下面显示了一个示例:
作者图片|左图显示文件中的“发现”,而右图显示同一文件的给定图片 id
突出显示的信息是您需要从这些文件中提取的内容。这可以在 python 的 XML 库的帮助下完成。
注:调查结果也将被称为报告。它们将在博客的其余部分互换使用。
从 XML 文件中提取结果和报告
4.获取结构化数据
从 XML 文件中提取所需数据后,数据被转换成结构化格式,以便于理解和访问。如前所述,有多个图像与单个报告相关联。因此,我们的模型在生成报告时也需要看到这些图像。但是有些报告只有 1 个相关联的图像,而有些报告有 2 个,最多 4 个。
按作者分类的图片|显示每份报告相关图片数量的图表
所以问题来了,我们应该一次向模型中输入多少图像来生成一个报告?为了使模型输入一致,一次选择成对的图像,即两幅图像作为输入。如果一个报表只有一个图像,那么同一个图像将作为第二个输入被复制。
作者图片|结构化数据外观一览
现在我们有了一个合适的、可理解的结构化数据来处理。图像以其绝对地址的名称保存。这将有助于加载数据。
训练-测试 _ 拆分
在最后一步之后,我们将多次获得一个人的数据点。例如,如果一个人有四个与报告相关联的图像,并且由于我们正在拍摄成对的图像,将会生成该人的多个数据点。因此,有必要将该数据集分割成个体,而不是生成的数据点,以避免数据泄露问题。
在制作图像对之前,确保使用独特的“person_id”功能将数据分为 train、cv 和 test,然后训练模型。
5.准备文本数据
从 XML 文件中获得结果后,在将结果输入模型之前,应该对它们进行适当的清理和准备。下图显示了清理前的一些结果示例。
作者图片|清理前的示例调查结果
我们将通过以下方式清理文本:
- 将所有字符转换成小写。
- 执行基本的解收缩,即像不会、不能等单词将分别转换为不会、不能等。
- 删除文本中的标点符号。请注意,句号不会被删除,因为调查结果包含多个句子,所以我们需要模型通过识别句子以类似的方式生成报告。
- 删除文本中的所有数字。
- 删除所有长度小于或等于 2 的单词。例如,删除了“是”、“到”等。这些话没有提供太多信息。但是“不”这个词不会被删除,因为它增加了价值。在句子中加上“不”会完全改变它的意思。因此,在执行这些清洁步骤时,我们必须小心。你需要确定哪些词要保留,哪些词要避免。
- 还发现一些文本包含多个句号、空格或重复多次的“X”。这样的字符也被删除。
我们将开发的模型将生成一个报告,给出两个图像的组合,该报告将一次生成一个单词。将提供先前生成的单词序列作为输入。因此,我们将需要一个'首字来开始生成过程,并需要一个'尾字'来表示报告的结束。为此,我们将使用字符串' startseq' 和' endseq' 。这些字符串被添加到我们的发现。现在这样做很重要,因为当我们对文本进行编码时,我们需要这些字符串被正确编码。
编码文本的主要步骤是创建从单词到唯一整数值的一致映射,称为标记化。为了让我们的计算机理解任何文本,我们需要以我们的机器能够理解的方式来分解那个单词或句子。如果不执行标记化,我们就无法处理文本数据。标记化是一种将一段文本分成称为标记的更小单元的方法。记号可以是单词或字符,但在我们的例子中是单词。Keras 为此提供了一个内置的库。
from tensorflow.keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(filters='!"#$%&()*+,-/:;<=>?@[\\]^_`{|}~\t\n')
tokenizer.fit_on_texts(reports)
现在,我们已经对文本进行了适当的清理和标记,以备将来使用。在我的 GitHub 账户中可以找到所有这些的完整代码,在这个故事的结尾提供了它的链接。
6.获取图像特征
图像和部分报告是我们模型的输入。我们需要将每张图像转换成固定大小的向量,然后作为模型的输入。为此,我们将使用迁移学习。
“在迁移学习中,我们首先在一个基本数据集和任务上训练一个基本网络,然后我们重新调整已学习的特征,或者将它们转移到第二个目标网络,以便在目标数据集和任务上进行训练。如果特性是通用的,即既适合基本任务又适合目标任务,而不是特定于基本任务,那么这个过程将会起作用。”
VGG16、VGG19 或 InceptionV3 是迁移学习常用的 CNN。这些是在像 Imagenets 这样的数据集上训练的,这些数据集的图像与胸部 x 光照片完全不同。所以从逻辑上来说,他们似乎不是我们任务的好选择。那么,我们应该使用哪个网络来解决我们的问题呢?
如果你不熟悉,我给你介绍一下 CheXNet 。CheXNet 是一个在 ChestX-ray14 上训练的 121 层卷积神经网络,ChestX-ray 14 是目前最大的公开可用胸部 X 射线数据集,包含 14 种疾病的 100,000 多幅正面 X 射线图像。然而,我们在这里的目的不是对图像进行分类,而仅仅是获得每个图像的瓶颈特征。因此,不需要该网络的最后一个分类层。
你可以从 这里 下载 CheXNet 训练好的权重。
from tensorflow.keras.applications import densenetchex = densenet.DenseNet121(include_top=False, weights = None, input_shape=(224,224,3), pooling="avg")X = chex.output
X = Dense(14, activation="sigmoid", name="predictions")(X)model = Model(inputs=chex.input, outputs=X)model.load_weights('load_the_downloaded_weights.h5')chexnet = Model(inputs = model.input, outputs = model.layers[-2].output)
作者图片 CheXNet 的最后几层
如果你忘了,我们有 2 张图片作为模型的输入。下面是瓶颈特征是如何获得的:
每个图像的大小被调整为(224,224,3)并通过 CheXNet,获得 1024 长度的特征向量。稍后,这两个特征向量被连接以获得 2048 个特征向量。如果你注意到,我们已经添加了一个平均池层作为最后一层。这有一个具体原因。因为我们要连接两个图像,所以模型可能会知道一些连接的顺序。例如,image1 总是在 image2 之后,反之亦然,但这里不是这样。在连接它们时,我们不保持任何顺序。这个问题是通过创建位置差异的池来解决的。
作者图片|获取图片特征
这方面的代码如下:
这些特性以 pickle 格式存储在一个字典中,可以供将来使用。
7.输入管道
考虑这样一种情况,您有大量的数据,多到无法一次将所有数据都存储在 RAM 中。购买更多 RAM 显然不是每个人的选择。
解决方案可以是将我们的小批量数据动态地输入到模型中。这正是数据生成器要做的事情。它们可以动态生成模型输入,从而形成从存储器到 RAM 的管道,以便在需要时加载数据。这种管道的另一个优点是,在准备将数据输入模型时,可以很容易地对这些小批量数据应用预处理例程。
我们将使用TF . data来解决我们的问题。
我们首先将数据集分为两部分,一个训练数据集和一个验证数据集。在划分的时候,要确保你有足够的数据点用于训练,也有足够的数据点用于验证。我选择的比例允许我在训练集中有 2560 个数据点,在验证集中有 1147 个数据点。
现在是时候为数据集创建生成器了。
数据生成程序
这里我们创建了两个数据生成器,用于训练的 train_dataset 和用于验证的 cv_dataset。create_dataset 函数获取 id(这是字典的键,用于前面创建的瓶颈特性)和预处理报告,并创建生成器。生成器一次生成 BATCH_SIZE 数量的数据点。
如前所述,我们将要创建的模型将是一个逐字逐句的模型。该模型将图像特征和部分序列作为输入,以生成序列中的下一个单词。
例如:假设对应于“图像 _ 特征 _1”的报告是“开始序列心脏轮廓和纵隔尺寸在正常范围内结束序列”。
那么输入序列将被分成 11 个输入-输出对来训练模型:
作者图片|模型将图片和部分报告作为输入,并输出输出单词
注意我们是而不是通过生成器创建这些输入输出对。生成器一次只为我们提供 BATCH_SIZE 数量的图像特征及其相应的完整报告。输入-输出对是在训练过程中稍后生成的,稍后将对此进行解释。
8.编码器-解码器模型
照片由 Sergi Kabrera 在 Unsplash |让我们解码一些序列!!
序列到序列模型是一种深度学习模型,它采用一个项目序列(在我们的情况下,是图像的特征)并输出另一个项目序列(报告)。
编码器处理输入序列中的每一项,它将捕获的信息编译成一个称为上下文的向量。处理完整个输入序列后,编码器将上下文发送给解码器,解码器开始逐项产生输出序列。
在我们的例子中,编码器是一个 CNN,它通过获取我们的图像特征来产生一个上下文向量。解码器是一个递归神经网络。
Marc Tanti 在他的论文 中介绍了许多架构,如 init-inject、par-inject、pre-inject 和 merge,指定了在创建图像标题生成器时应该在哪里注入图像。对于我们的问题,我们将使用他的论文中指定的合并架构。
作者图片|建筑
在“合并”架构中,RNN 在任何点都不暴露于图像向量(或从图像向量导出的向量)。相反,在前缀被 RNN 完整编码后,图像被引入语言模型。这是一个后期绑定架构,它不会在每个时间步修改图像表示。
他的论文中的一些重要结论被用于我们实现的架构中。它们是:
- RNN 输出需要与辍学正规化。
- 图像向量不应该具有非线性激活函数,也不应该用丢失来正则化。
- 图像输入向量在被馈送到神经网络之前必须被归一化,这是在从 CheXNet 获得特征时完成的。
嵌入层:
单词嵌入是一类使用密集向量表示来表示单词和文档的方法。Keras 提供了一个 嵌入 层,可用于文本数据上的神经网络。它也可以用一个从别处学来的单词嵌入。在自然语言处理领域中,学习、保存和自由使用单词嵌入是很常见的。
在我们的模型中,利用嵌入层,每个单词已经使用预先训练的 手套 模型被映射成 300 维表示。使用预训练嵌入时,请记住,应通过设置参数“trainable = False”来冻结层的权重,以便权重在训练时不会更新。
型号代码:
型号汇总:
模型参数汇总
8.1 培训
损失函数:
为此问题创建了掩蔽损失函数。例如:
如果我们有一个令牌序列- [3]、[10]、[7]、[0]、[0]、[0]、[0]、[0]
我们在这个序列中只有 3 个单词,零对应于实际上不是报告一部分的填充。但是模型会认为零也是序列的一部分,并开始学习它们。当模型开始正确预测零点时,损失将会减少,因为对于模型来说,它正在正确地学习。但对我们来说,只有当模型正确预测实际单词(非零)时,损失才会减少。
因此,我们应该屏蔽序列中的零,这样模型就不会关注它们,只学习报告中需要的单词。
隐蔽损失
输出字是一个热编码,因此分类交叉熵将是我们的损失函数。
optimizer = tf.keras.optimizers.Adam(0.001)
encoder_decoder.compile(optimizer, loss = maskedLoss)
还记得我们的数据生成器吗?现在是时候使用它们了。
这里,生成器提供的批次不是我们用于训练的实际数据批次。请记住,它们不是逐字的输入输出对。他们只是返回图像及其相应的整个报告。
我们将从生成器中检索每一批数据,并从该批数据中手动创建输入-输出序列,也就是说,我们将为训练创建我们自己的定制数据批。所以在这里,BATCH_SIZE 逻辑上是模型在单个批次中看到的图像对的数量。我们可以根据我们的系统能力改变它。我发现这种方法比其他博客中提到的传统定制生成器要快得多。
由于我们正在创建自己的训练数据批次,因此我们将使用“train_on_batch”来训练我们的模型。
培训步骤
代码中提到的 convert 函数将来自生成器的数据转换为一个字一个字的输入输出对表示。然后,部分报告被填充到报告的最大长度。
转换功能:
Adam optimizer 的学习率为 0.001。该模型被训练了 40 个时期,但是在第 35 个时期获得了最好的结果。由于随机性,您得到的结果可能会有所不同。
图片由作者| Tensorboard 提供,显示了模型的损耗图
注:以上培训已在 Tensorflow 2.1 中实现。
8.2 推理
既然我们已经训练了我们的模型,是时候准备我们的模型来预测报告了。
为此,我们必须对我们的模型进行一些调整。这将为我们节省一些测试时间。
首先,我们将编码器和解码器部分从模型中分离出来。编码器预测的特征将与部分报告一起用作解码器的输入。
推理设置
通过这样做,我们将只需要预测编码器特征一次,同时我们将它用于我们的贪婪搜索和波束搜索算法。
我们将实现这两种生成文本的算法,并看看哪一种效果最好。
8.3 贪婪搜索算法
贪婪搜索是一种算法范式,它一点一点地构建解决方案,总是选择下一个提供最明显好处的方案。
贪婪搜索步骤 :
- 编码器输出我们图像的特征。编码器的工作到此结束。一旦我们有了我们需要的特性,我们就不需要关心编码器了。
- 这个特征向量连同开始标记-‘start seq’(我们的初始输入序列)作为第一个输入提供给解码器。
- 解码器预测整个词汇的概率分布,具有最大概率的单词将被选为下一个单词。
- 这个预测的字连同先前的输入序列将是我们对解码器的下一个输入序列。
- 继续步骤 3-4,直到我们遇到结束标记,即‘end seq’。
让我们检查一下在使用 greedysearch 生成报告之后,我们的模型表现如何。
BLEU 评分—贪婪搜索:
双语评估替角评分,简称 BLEU,是一个将生成的句子评估为参考句子的度量。
完全匹配的得分为 1.0,而完全不匹配的得分为 0.0。该方法通过计算候选文本中的 n 元语法与参考文本中的 n 元语法的匹配来工作,其中 1 元语法或单元语法将是每个单词,双元语法比较将是每个单词对。
通过贪婪搜索生成报告后的 BLEU 分数
满分在实践中是不可能的,因为译文必须与参考文献完全匹配。这甚至是人类翻译所做不到的。用于计算 BLEU 分数的参考文献的数量和质量意味着跨数据集比较分数可能会很麻烦。
想了解更多关于 BLEU 的信息,点击 这里 。
8.4 波束搜索
波束搜索是一种算法,它扩展了贪婪搜索并返回最可能的输出序列列表。每个序列都有一个与之相关的分数。得分最高的序列作为最终结果。
在构建序列时,波束搜索不是贪婪地选择最有可能的下一步,而是扩展所有可能的下一步,并保持最有可能的 k ,其中 k(称为波束宽度)是用户指定的参数,控制波束的数量或概率序列中的并行搜索。
波束宽度为 1 的波束搜索只不过是你的贪婪搜索。常见的波束宽度值为 5-10,但甚至高达 1000 或 2000 以上的值也用于研究,以从模型中挤出最佳性能。点击 此处 阅读更多关于光束搜索的内容。
但是请记住,随着波束宽度的增加,时间复杂度也会增加。因此,这些比贪婪搜索慢得多。
波束搜索
波束搜索并不总是保证更好的结果,但在大多数情况下,它给你一个。
您可以使用上面给出的函数检查光束搜索的 BLEU 分数。但是请记住,评估它们需要一段时间(几个小时)。
8.5 示例
现在让我们来看一些胸部 x 光的预测报告:
训练图像对 1
图像对 1 的原始报告:心脏正常大小。纵隔不明显。肺部没问题。
预测的报告为图像对 1:心脏正常大小。纵隔不明显。肺清。”
对于这个示例,模型预测的是完全相同的报告。
训练图像对 2
图像对 2 的原始报告:“心脏大小和肺血管分布在正常范围内。未发现局部浸润性胸腔积液。
图像对 2 的预测报告:“心脏大小和肺部血管分布在正常范围内。肺部是自由的局灶性空域疾病。没有胸腔积液。”
虽然不完全相同,但预测结果与原始报告几乎相似。
训练图像对 3
图像对 3 的原始报告:“肺部过度膨胀但清晰。没有局灶性浸润渗出。心脏和纵隔轮廓在正常范围内。发现钙化的纵隔。
图像对 3 的预测报告:“心脏大小正常。纵隔轮廓在正常范围内。肺部没有任何病灶浸润。没有结节肿块。没有可见的气胸。没有明显的胸腔积液。这是非常正常的。横膈膜下无可见的腹腔内自由空气
你并不期望这个模型完美无缺,对吧?没有一个模型是完美的,这个也不是。尽管从图像对 3 中可以正确识别出一些细节,但是产生了许多额外的细节,这些细节可能是正确的,也可能是不正确的。
我们创建的模型绝不是完美的,但它确实为我们的图像生成了不错的报告。
现在让我们来看一个高级模型,看看它是否提高了当前的性能!!
9。注意机制
保罗·斯科鲁普斯卡斯在 Unsplash 拍摄的照片|现在让我们来关注一下吧!!
注意机制是对编解码模型的一种改进。上下文向量被证明是这类模型的瓶颈。这对他们处理长句子来说是一个挑战。在 Bahdanau 等人,2014 和 Luong 等人, 2015 中提出了解决方案。这些论文介绍并完善了一种叫做“注意力”的技术,这种技术极大地提高了机器翻译系统的质量。注意力允许模型根据需要关注输入序列的相关部分。后来这个想法被实现为图像字幕在论文中, 展示,出席,讲述:视觉注意的神经图像字幕生成。
那么,我们如何为图像的注意力机制建模呢?
马库斯·温克勒在 Unsplash 上拍摄的照片
在文本的情况下,我们对输入序列的每个位置都有一个表示。但是对于图像,我们通常使用来自网络的完全连接层之一的表示,但是该表示不包含任何位置信息(想想看,它们是完全连接的)。我们需要查看图像的特定部分(位置)来描述那里有什么。例如,要从 x 光片上描述一个人心脏的大小,我们只需要看他的心脏区域,而不是他的手臂或任何其他部位。那么注意力机制的输入应该是什么呢?
好吧,我们使用具有空间信息的卷积层之一(迁移学习)的输出,而不是完全连接的表示。
图片作者| Conv 图层包含空间信息
例如,假设最后一个卷积层的输出是一个(7141024)大小的特征图。这里,“714”是对应于图像中某些部分的实际位置,1024 是通道。我们不是关注通道,而是关注图像的位置。因此,这里我们有 714 = 98 个这样的位置。我们可以把它想象成 98 个位置,每个位置都有一个 1024 维的表示。
作者图片|关注组件
现在我们有 98 个时间步,每个时间步有 1024 个维度表示。我们现在需要决定模型应该如何关注这 98 个时间步长或位置。一个简单的方法是给每个位置分配一些权重,得到所有这 98 个位置的加权和。如果特定的时间步长在预测输出时非常重要,则该时间步长将具有较高的权重。让这些重量被表示为阿尔法。
现在我们知道,阿尔法决定了一个特定位置的重要性。阿尔法值越高,重要性越高。但是我们如何找到α的值呢?没有人会给我们这些值,模型本身应该从数据中学习这些值。为此,我们定义了一个函数:
这个量捕获了第 j 个输入对于解码第 t 个输出的重要性。h_j 是第 j 个位置表示,s_t-1 是解码器直到该点的状态。我们需要这两个提到的量来确定 e_jt。ATT 只是一个函数,我们将在后面定义。
在所有输入中,现在我们希望这个量(e_jt)的总和为 1。这就像一个概率分布,其中输入的重要性是多少。通过取 softmax 将 e_jt 转换成概率分布。
使用 softmax 将 e_jt 转换为概率分布
现在我们有阿尔法战士了。!阿尔法是我们 e _ jts 的软 max。α_ JT 表示聚焦于第 j 个输入以产生第 t 个输出的概率。
是时候定义函数 f_ATT 了。许多其他可能的选择之一如下:
v、U 和 W 是将在训练期间学习的参数,以确定 e_jt 的值。
我们有阿尔法,我们有输入,现在我们只需要得到加权和,以产生新的上下文向量,它将被馈送到解码器。实际上,这些模型比编码器解码器模型工作得更好。
模型实现:
像上面提到的编码器-解码器模型一样,这个模型也将由两部分组成,一个编码器和一个解码器,但这次解码器将有一个额外的注意力组件,即一步注意力解码器。为了更好地理解,现在让我们用代码编写上面解释的注意步骤:
***The onestep attention layer will consist of the attention part which
will be calculated for each time step of the decoder.******# Calculating e_jts***
score = self.Vattn(tf.nn.tanh(self.Uattn(features) + self.Wattn(hidden_with_time_axis)))***# Converting our scores to probability distributions using softmax***
attention_weights = tf.nn.softmax(score, axis=1)***# Calculating the context vector(weighted sum)***
context_vector = attention_weights * features
我们将使用 keras 的子类 API,这给了我们更多的可定制性和对我们架构的控制。你可以从文档本身 中阅读更多关于子类 API 这里 。
我们将实现教师强制训练我们的模型,这一次我们不必将我们的文本转换成逐字逐句的模型。但是对于图像特征,我们将从 CheXNet 网络的最后一个 conv 层获取特征。
作者图片|提取图像特征以引起注意
组合我们的两个图像后的最终输出形状将是(无,7,14,1024)。因此,整形后编码器的输入将是(None,98,1024)。为什么要整形?嗯,这已经在注意介绍中解释过了,如果你有任何疑问,请确保你再次阅读解释。
型号:
- 编码器
编码器层只是对我们的图像执行一些操作,并输出将作为输入提供给解码器的特征。
2.解码器
解码器为每个解码器时间步长调用单步关注层,并计算分数和关注权重。每个时间步的所有输出都存储在“所有输出”变量中。每个解码器步骤的输出是序列中的下一个字。“全力输出”将是我们的最终输出。
3.单步解码器
4.创建模型
**model1 = Attention_Model(vocab_size, units, max_capt_len, att_units, BATCH_SIZE)**
9.1 培训
如前所述,我们将使用教师强制来训练我们的模型。因此,我们将不需要我们用于编码器解码器模型的额外功能。我们可以直接使用来自生成器的自定义报告。
“train_dataset”是我们的生成器,它为我们提供图像特征和相应的 155 维填充报告。
**# teacher forcing
res = model1.train_on_batch([img, rep[:,:-1]], rep[:,1:])**
可以看到,解码器的输入比输出落后一个时间步长。我们不希望模型预测的输入与输出相同。我们希望它能预测序列中的下一个单词。
为了引起注意,Adam optimizer 以 0.001 的学习率使用。该模型仅用 10 个时期的训练就给出了不错的结果。由于随机性,您得到的结果可能会有所不同。稀疏分类交叉熵是在这种情况下使用的损失函数,因为我们没有将输出转换为 OHE 向量。
图片由作者| Tensorboard 提供,显示了损失图
所有东西的代码都可以从我的 GitHub 上获取。它的链接已经在这个博客的末尾提供了。
9.2 推理
9.3 贪婪搜索
现在我们已经建立了模型,让我们检查一下获得的 BLEU 分数是否确实比以前的模型有所改进:
贪婪搜索后注意模型的 BLEU 分数
我们可以看到,它比带有贪婪搜索的编码器-解码器模型具有更好的性能。因此,这肯定是对前一个的改进。
9.4 波束搜索
现在让我们看看光束搜索的一些分数:
波束搜索后的 BLEU 分数
BLEU 分数低于 greedy 分数,但相差不远。但是值得注意的是,随着波束宽度的增加,分数实际上也在增加。因此,可能存在一些 beam_width 值,分数实际上与贪婪值交叉。
9.5 示例
以下是模型使用贪婪搜索生成的一些报告:
训练图像对 1
图像对 1 的原始报告:“心脏大小和肺血管分布在正常范围内。未发现局部浸润性胸腔积液。
图像对 1 的预测报告:“心脏大小和纵隔轮廓在正常范围内。肺部没问题。没有胸腔积液。没有急性骨质发现。
这些预测几乎与原始报告相似。
训练图像对 2
图像对 2 的原始报告:“心脏大小和肺部血管分布在正常范围内。肺部是自由的局灶性空域疾病。没有胸腔积液。”
图像对 2 的预测报告:“心脏大小和肺部血管分布在正常范围内。肺部是自由的局灶性空域疾病。没有胸腔积液。
预测的报告一模一样!!
训练图像对 3
图像对 3 的原始报告:心脏正常大小。纵隔不明显。肺部没问题。
预测的图像对 3 的报告:心脏正常大小。纵隔不明显。肺部没问题。
在这个例子中,模型也做得非常好。
训练图像对 4
图像对 4 的原始报告:“双肺清晰。具体来说,没有证据表明局灶性实变气胸胸腔积液。心脏纵隔轮廓不起眼。可视化骨骼结构胸部无急性异常。”
图像对 4 的预测报告:“心脏大小和纵隔轮廓在正常范围内。肺部没问题。没有胸腔积液。”
你可以看到这个预测并没有真正的说服力。
" But本例中的波束搜索预测了完全相同的报告,尽管它对整个测试数据组合产生了较低的 BLEU 分数!!!"
那么,选哪个呢?好吧,这取决于我们。只需选择一个通用的方法。
在这里,即使我们的注意力模型也不能准确预测每一幅图像。正如我们从示例中看到的,这一对没有侧视图像,或者如果我们查看原始报告中的单词,会发现有些复杂的单词通过一些 EDA 并不经常出现。这些可能是我们在某些情况下没有一个好的预测的一些原因。请记住,我们只是在 2560 个数据点上训练这个模型。为了了解更复杂的特征,模型将需要更多的数据。
10.摘要
既然我们已经结束了这个项目,让我们总结一下我们所做的一切:
- 我们刚刚看到了图像字幕在医学领域的应用。我们理解这个问题和对这种应用的需要。
- 我们看到了如何为输入管道使用数据生成器。
- 创建了一个编码器-解码器模型,给了我们不错的结果。
- 通过建立注意力模型改进了基本结果。
11.未来的工作
- 正如我们提到的,我们没有一个大的数据集来完成这项任务。更大的数据集将产生更好的结果。
- 没有对任何模型进行主要的超参数调整。因此,更好的超参数调整可能会产生更好的结果。
- 利用更先进的技术,如变压器或 BERT,可能会产生更好的结果。
12.参考
- https://www.appliedaicourse.com/
- https://arxiv.org/abs/1502.03044
- 【https://www.aclweb.org/anthology/P18-1240/
- https://arxiv.org/abs/1703.09137
- https://arxiv.org/abs/1409.0473
- https://machine learning mastery . com/develop-a-deep-learning-caption-generation-model-in-python/
这个项目的完整代码可以从我的GitHub中访问。
也可以在我的LinkedIn上和我联系。
希望你喜欢这个项目。感谢阅读:)
用 Tensorflow 和 FastAPI 构建影像分类 API
原文:https://towardsdatascience.com/image-classification-api-with-tensorflow-and-fastapi-fc85dc6d39e8?source=collection_archive---------7-----------------------
从零开始学习用 Tensorflow 和 FastAPI 构建图像分类 API。
资料来源:aniketmaurya
FastAPI 是一个高性能的异步框架,用于在 Python 中构建 API。
这个博客也有视频教程
这个博客的源代码是可用的aniketmaurya/tensor flow-fastapi-starter-pack
让我们从一个简单的 hello-world 示例开始
首先,我们导入FastAPI
类并创建一个对象app
。这个类有有用的参数,比如我们可以为 Swagger UI 传递标题和描述。
from fastapi import FastAPI
app **=** FastAPI**(**title**=**'Hello world'**)**
我们定义一个函数并用@app.get
来修饰它。这意味着我们的 API /index
支持 GET 方法。这里定义的函数是异步的,FastAPI 通过为普通的 def 函数创建一个线程池来自动处理异步和非异步方法,并为异步函数使用一个异步事件循环。
**@**app**.**get**(**'/index'**)**
**async** **def** **hello_world():**
**return** "hello world"
图像识别 API
我们将创建一个 API 来对图像进行分类,我们将其命名为predict/image
。我们将使用 Tensorflow 创建图像分类模型。
tensor flow图像分类教程
我们创建一个函数load_model
,它将返回一个带有预训练权重的 MobileNet CNN 模型,也就是说,它已经被训练来分类 1000 个独特的图像类别。
import tensorflow **as** tf**def** **load_model():**
model **=** tf**.**keras**.**applications**.**MobileNetV2**(**weights**=**"imagenet"**)**
**print(**"Model loaded"**)**
**return** modelmodel **=** load_model**()**
我们定义了一个predict
函数,它将接受一幅图像并返回预测结果。我们将图像的大小调整为 224x224,并将像素值归一化为[-1,1]。
from tensorflow.keras.applications.imagenet_utils import decode_predictions
decode_predictions
用于解码预测对象的类名。这里我们将返回前 2 个可能的类。
**def** **predict(**image**:** Image**.**Image**):** image **=** np**.**asarray**(**image**.**resize**((**224**,** 224**)))[...,** **:**3**]**
image **=** np**.**expand_dims**(**image**,** 0**)**
image **=** image **/** 127.5 **-** 1.0 result **=** decode_predictions**(**model**.**predict**(**image**),** 2**)[**0**]** response **=** **[]**
**for** i**,** res **in** *enumerate***(**result**):**
resp **=** **{}**
resp**[**"class"**]** **=** res**[**1**]**
resp**[**"confidence"**]** **=** f"{res[2]*100:0.2f} %" response**.**append**(**resp**)** **return** response
现在我们将创建一个支持文件上传的 API /predict/image
。我们将过滤文件扩展名,仅支持 jpg、jpeg 和 png 格式的图像。
我们将使用 Pillow 来加载上传的图像。
**def** **read_imagefile(***file***)** **->** Image**.**Image**:**
image **=** Image**.***open***(**BytesIO**(***file***))**
**return** image**@**app**.**post**(**"/predict/image"**)**
**async** **def** **predict_api(***file***:** UploadFile **=** File**(...)):**
extension **=** *file***.**filename**.**split**(**"."**)[-**1**]** **in** **(**"jpg"**,** "jpeg"**,** "png"**)**
**if** **not** extension**:**
**return** "Image must be jpg or png format!"
image **=** read_imagefile**(await** *file***.**read**())**
prediction **=** predict**(**image**)** **return** prediction
最终代码
import uvicorn
from fastapi import FastAPI**,** File**,** UploadFilefrom application.components import predict**,** read_imagefileapp **=** FastAPI**()****@**app**.**post**(**"/predict/image"**)**
**async** **def** **predict_api(***file***:** UploadFile **=** File**(...)):**
extension **=** *file***.**filename**.**split**(**"."**)[-**1**]** **in** **(**"jpg"**,** "jpeg"**,** "png"**)**
**if** **not** extension**:**
**return** "Image must be jpg or png format!"
image **=** read_imagefile**(await** *file***.**read**())**
prediction **=** predict**(**image**)** **return** prediction **@**app**.**post**(**"/api/covid-symptom-check"**)**
**def** **check_risk(**symptom**:** Symptom**):**
**return** symptom_check**.**get_risk_level**(**symptom**)** **if** __name__ **==** "__main__"**:**
uvicorn**.**run**(**app**,** debug**=***True***)**
FastAPI 文档是了解框架核心概念的最佳地方。
希望你喜欢这篇文章。
欢迎在评论中提出你的问题,或者亲自联系我
👉推特:https://twitter.com/aniketmaurya
👉领英:【https://linkedin.com/in/aniketmaurya
2020 年影像分类基线模型
原文:https://towardsdatascience.com/image-classification-baseline-model-for-2020-1d33f0986fc0?source=collection_archive---------22-----------------------
如何使用 fastai 建立强基线模型进行图像分类?
TL;DR —要使用的代码片段
有一些基本的想法,这里只是一段代码来构建您的基线模型?用这个:
from fastai.vision import ***# Defining the data and the model** path = untar_data(URLs.CIFAR)
tfms = get_transforms(do_flip=False)
data = ImageDataBunch.from_folder(path, valid='test', ds_tfms=tfms, size=128)
learn = cnn_learner(data, models.resnet50, metrics=accuracy)**# Estimate learning rate**
learn.lr_find()
learn.recorder.plot()**# Training** learn.fit_one_cycle(3, max_lr=1e-3)**# Finetuning** learn.unfreeze()
learn.fit_one_cycle(3, max_lr=slice(1e-6, 1e-4))**# Test Time Augmentation** preds,targs = learn.TTA()
accuracy(preds, targs).item()
介绍
自 20 世纪 50 年代以来,计算机视觉就一直存在,但直到最近十年,该领域才完全改变了自己(事实上,特殊的时刻发生在 2012 年,当时 AlexNet 赢得了 ImageNet 挑战赛)。
在过去的几年中,出现了许多强大的框架。我们将使用 fastai,因为在编写本文时,它提供了最简单的 API 和最强的默认设置。这是 PyTorch 的高级包装。我们的目标是到 2020 年建立一个通用的图像分类基准。
图片来自 Pixabay 的Gerhard ge linger
图像分类任务
让我们使用流行的 CIFAR-10 数据集,它包含 10 个不同类别的 60,000 幅 32x32 彩色图像。它分为 50,000 个训练样本和 10,000 个测试样本。fastai 库中已经提供了 CIFAR 数据集作为样本数据集。
from fastai.vision import *
path = untar_data(URLs.CIFAR)
让我们从直接来自文档的一个最小的例子开始,并随着我们的进展修改它
tfms = get_transforms(do_flip=False)
data = ImageDataBunch.from_folder(path, valid='test', ds_tfms=tfms)
learn = cnn_learner(data, models.resnet50, metrics=accuracy)
learn.fit(3)
让我们来分解一下这里发生的 3 件最重要的事情:
- 数据扩充:在训练用于图像分类的大型神经网络时,通常使用诸如裁剪、填充和水平翻转等技术。这有助于避免过度拟合和更好地概括,而不必实际收集任何新数据。最简单的方法是使用 fastai 中的
get_transforms
,它允许你从一组标准的变换中进行选择,其中默认的是为照片设计的。 - 将数据集分割成训练验证测试集:我们应该总是将数据集分割成训练验证测试集。如果我们使用验证集来调优任何超参数(比如模型架构、学习率等),那么如果您想要报告最终度量(比如准确性),也需要有一个测试集。在这个例子中,我们没有使用验证集来调优任何超参数,所以我们没有使用测试集。实际上,你可以用 50000 个例子来训练,5000 个例子来验证,5000 个例子来测试。fastai 中的 ImageDatabunch API 提供了一种简单的方法来加载你的数据,并在数据已经以一些标准格式存储时将其拆分。如果你想对如何选择、分割和标记数据进行更多的自定义控制,你也可以使用数据块 API 。
- 迁移学习:迁移学习包括采用在一项任务中训练过的模型,然后将其用于另一项任务。这里我们使用的是在 ImageNet 上训练的名为 ResNet50 的架构,它包含大约 1400 万张图像。一旦有了基线,您还可以尝试其他架构,包括更深的 ResNet 模型。迁移学习背后的想法是,网络的大多数早期层将识别对任何图像的分类都有用的一般特征,如边缘。当我们为新任务进行训练时,我们将保持所有卷积层(称为模型的主体或主干)的权重在 ImageNet 上预先训练,但将定义一个随机初始化的新头部。该头部适应于新分类任务所需的类别数量。通过默认调用
cnn_learner
上的fit
,我们保持身体冻结,只训练头部。
3 个时代的训练
经过 3 个时期的训练,我们在这一点上获得了大约 70%的准确率。
修改输入图像尺寸
ResNet 最初是在 224x224 图像上训练的,我们的数据集有 32x32 个图像。如果您使用的输入图像与原始大小相差太大,那么它对于模型来说不是最佳的。让我们来看看将这些图像的大小调整为 128x128 后,对精确度的影响。ImageDataBunch API 允许您传入一个大小。
tfms = get_transforms(do_flip=False)
data = ImageDataBunch.from_folder(path, valid='test', ds_tfms=tfms, size=128)
learn = cnn_learner(data, models.resnet50, metrics=accuracy)
learn.fit(3)
调整输入大小后训练 3 个时期
准确率高达 94%。在这项任务中,我们已经达到了人类的水平。我们也可以将尺寸调整到 224x224,但这将增加训练时间,而且由于这些图像分辨率很低,当我们放大很多时,可能会留下伪像。一旦你有了一个基线,你可以根据需要在以后进行试验。
修改学习率
学习率通常是训练神经网络时要调整的最重要的超参数。它影响网络的权重随每个训练批次而修改的速率。非常小的学习率会导致训练缓慢。另一方面,非常大的学习率会导致损失函数在最小值附近波动,甚至发散。传统上,它是一个超参数,使用网格搜索和可选公式进行调整和设置,使用特定策略(如基于时间的衰减、阶跃衰减等)降低学习速率。
莱斯利·史密斯在 2015 年提出了一种设定学习率的新方法,称为循环学习率 (CLR)。这种方法不是单调地降低学习率,而是让学习率在合理的边界值之间循环变化。这消除了寻找学习率的最佳值的需要。Fastai 有一个单周期 CLR 策略的实现,其中学习速率从一个低值开始,增加到一个非常大的值,然后降低到一个比其初始值低得多的值。
Fastai 还提供了一种简便的方法来估计单周期策略中使用的最大学习速率。为此,它开始训练模型,同时将学习率从非常低的值增加到非常大的值。最初,损失会下降,但随着学习率的不断上升,损失开始增加。当损失在最小值左右时,学习率已经过高。经验法则是将最大学习率设置为比最小值小一个数量级。
learn = cnn_learner(data, models.resnet50, metrics=accuracy)
learn.lr_find()
learn.recorder.plot()
使用 lr_find 估计学习率
在这里,您可以尝试将学习率设置在 1e-03 和 1-e02 之间。目前,我们只使用 1e-03。
learn = cnn_learner(data, models.resnet50, metrics=accuracy)
learn.fit_one_cycle(3, max_lr=1e-3)
使用单周期策略训练 3 个时期
在 3 个时期内,我们在验证集上达到了大约 95%的准确率。
微调
我们之前只训练了新的随机初始化的头部,保持身体的重量不变。我们现在可以解冻身体,训练整个网络。在此之前,让我们使用lr_find
并估计要使用的新学习率。
learn.lr_find()
learn.recorder.plot()
使用 lr_find 再次估计学习率
训练曲线似乎已经从之前的曲线移动了一个数量级,并且看起来 1e-4 似乎是一个合理的训练速率。与以前不同,我们可能不想对身体的所有层使用相同的最大学习速率。我们可能希望使用区别学习率,其中早期层获得小得多的学习率。这个想法是,早期的层将学习更多的基本特征,如边缘(这对您的新任务也很有用),而后期的层将学习更复杂的特征(如可能识别头部,这对您的新任务不一定有用)。Fastai 允许您发送一个切片,其中第一个值将用作第一层的学习率,第二个值将是最后一层的学习率,中间各层的值介于两者之间。
learn.unfreeze()
learn.fit_one_cycle(3, max_lr=slice(1e-6, 1e-4))
微调 3 个以上的时代
我们可以看到精度还在提高。
测试时间增加
您可以为通用基准测试做的最后一件事是测试时间增加(TTA)。TTA 是在测试/验证集上完成的,以提高预测质量-它包括首先使用数据增强创建每个图像的多个版本,然后我们通过我们训练的模型获得多个预测,最后我们取这些预测的平均值以获得最终预测。TTA 经常给我们一个更好的表现,而不需要任何额外的训练,但它对最终的预测时间有影响。
preds,targs = learn.TTA()
accuracy(preds, targs).item()
TTA 后的精确度
我们达到了超过 96%的准确率。太好了。另一个可以提高我们准确度的简单方法就是针对更多的时期进行训练和微调。但是我们暂时就此打住。
我们的立场是什么?
查看 CIFAR-10 上实现的最先进的基准。96%以上只是最近几年才达到的。那里的大多数模型训练的时间要长得多,使用的资源比 collab 笔记本更多,参数也更多——因此我们可以确信,我们对这种通用图像分类的 96%是非常好的。
结论
在一个新的机器学习项目开始时,你可以做的最有用的事情之一就是建立一个基线。对于图像分类,fastai 帮助您快速创建一个强基线,几乎不需要调整超参数。
你可以在这个 T4 笔记本中找到这篇文章的完整代码。请随意使用它并在您的项目中使用它。如果您有任何想法或问题,我很乐意与您讨论。
基于 CNN 的图像分类
原文:https://towardsdatascience.com/image-classification-cnn-cf53e5e5c176?source=collection_archive---------41-----------------------
使用 CNN 和代码对漫威字符进行分类
数据集中使用的漫威人物(来源: Instagram ,作者创建的拼贴)
我们怎么看?
在深入探讨背后的技术细节之前,让我们先来谈谈生物。他们是如何识别和感知图像的。我们每秒钟看到多幅图像,我们能够处理它,我们能够在未来识别它。所有这一切都归功于人脑,世界上最强大的机器。让我们来谈谈我们是如何感知图像的,以便更好地理解 CNN 。看看下面的图片
来源: Pinterest
我们可以看到两幅图像
- 右脸的男人
- 一个面向我们的男人
基于我们对图像的聚焦我们看到不同的视图,我们的大脑太强大了,它自动识别模式并帮助我们识别物体。这是 CNN 背后的基本原理,它搜索模式并区分图像。
狮子狗(图片来源:Fredrik hlander 在 Unsplash 拍摄)狮子(图片来源:Arleen wiese 在 Unsplash 拍摄)
考虑上面的图像,我们能够通过观察一些模式来识别 A 和 B。我们能够做到这一点是因为我们在过去看到过这些图像,我们的大脑观察到了其中的一些模式,这有助于我们区分它们。如果我们问一个孩子或一个一生中从未见过狮子或狗的人。他能对上述图像进行分类吗?肯定没有。
上图不仅适用于 CNN 也普遍适用于机器学习。让我们关注 CNN,因为这篇文章就是关于它的。我们向 CNN 发送一堆图像进行训练, CNN 在其中寻找与人类相似的模式,因此当我们要求 CNN 识别这些图像时,它将能够识别其中的模式。因此,如果我们将图像发送给未经训练的 CNN,它也会像人类一样失败。
CNN 步行道:
如何让机器读取图像?是困扰我们大多数人的常见问题,这是图像处理的第一步。
计算机视觉如何工作。在最左边我们有一个图像,像素值是根据亮度分配的
机器通过像素值读取图像。每幅图像都是像素值的组合,改变像素值会改变图像。这些像素值在输入 CNN 之前必须经过处理。像素值的数量取决于图像的大小,因此如果图像的大小太大,又会出现问题。一个完全连接的网络会将二维像素阵列展平为一维阵列,并根据像素值识别图像。
卷积层:
卷积层与汇集来拯救我们。它从图像中提取某些特征并缩小图像的尺寸。
一个过滤器在没有填充的二维图像上的卷积(来源:Pinterest
我们把 66 矩阵的图像转换成 33 矩阵。我们的权重初始化为 33 矩阵。加权矩阵穿过我们图像,并产生 33 矩阵的卷积输出。权重覆盖我们的图像中的所有像素值至少一次,并且执行逐元素乘法。
当您查看元素乘法以获得卷积层时,另一个问题出现了,边缘处的像素值仅被覆盖一次。借助衬垫可以解决这个问题。
2D 图像上的滤波器与填充的卷积
当我们应用填充时,图像的大小保持不变。类似于【MLP】学习权重,即最小化损失函数或帮助模型正确预测图像。我们不会在实际应用中只使用一个滤镜,滤镜数量的使用会影响旋绕层的深度。卷积输出的深度取决于我们使用的滤波器的数量。
彩色图像的卷积层:
每幅彩色图像都可以用红色、蓝色和绿色的组合来表示。我们把它分成三层,并遵循同样的程序应用于灰度图像的情况。****
用 3x3x3 内核对彩色图像矩阵进行卷积运算
汇集层:
池化的唯一目的是减小图像的大小。大多数情况下首选最大池。池在每个层上独立运行,因此卷积层的深度保持不变。您可以观察到我们一次应用两个步长。尺寸随着步数的增加而减小。
Gif 由谷歌开发者
输出层:
我们的最终目标是预测一幅图像是否属于某个特定的类别。但是卷积层给了我们一个 3 D 激活图,使用卷积层不容易获得输出类,所以我们需要将其展平以产生所需数量的输出类。进行前向和反向传播以减少损耗。
来源:研究门
正如我们可以观察到的,我们在开始时应用卷积以从中提取有用的特征,随后是最大汇集以减小图像的大小,随后是展平,最后输出预测图像属于特定类别的概率。
使用 CNN 对图像进行分类:(代码)
五个漫威角色被选中,分别是黑寡妇、钢铁侠、雷神、美国队长和绿巨人。按照 70: 30 的比例划分训练和测试装置,并观察性能。
钢铁侠(来源: Pinterest
from zipfile import ZipFile
filename= "marvel.zip"
with ZipFile(filename,'r')as zip:
zip.extractall()
您可以忽略前三个块,因为它处理图像加载和导入依赖项。由 5 个漫威角色组成的 zip 文件在上面的代码块中被加载和提取。
#Importing
import os
import numpy as np
import glob
import shutil
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
base_dir = os.path.join(os.path.dirname(filename), 'marvel')
所有必需的依赖项都会被导入,如果在导入这些依赖项时出现错误,您必须安装它。使用 pip 安装(Jupyter 笔记本)
# Accessing the images and setting 0.7 of images for training and the rest for testingclasses=['Black Widow','Captain America','Hulk','Iron Man','Thor']
for m in classes:
img_path = os.path.join(base_dir, m)
images = glob.glob(img_path + '/*.jpg')
num_train = int(round(len(images)*0.7))
train, val = images[:num_train], images[num_train:]# Creating separate directories for training datafor t in train:
if not os.path.exists(os.path.join(base_dir, 'train', m)):
os.makedirs(os.path.join(base_dir, 'train', m))
shutil.move(t, os.path.join(base_dir, 'train', m))# Creating separate directories for validating datafor v in val:
if not os.path.exists(os.path.join(base_dir, 'val', m)):
os.makedirs(os.path.join(base_dir, 'val', m))
shutil.move(v, os.path.join(base_dir, 'val', m))
在上面的代码块中,为训练和验证数据创建了单独的目录。训练和验证数据按照 70:30 的比例分割。所有的图像扩展名都被转换成 jpg 。
train_dir = os.path.join(base_dir, 'train')
val_dir = os.path.join(base_dir, 'val')
为培训和验证创建了两个目录。
# Setting batch size and a constant image shapebatch_size = 130IMG_SHAPE = 150# Rescaling the images so all the values lie between 0 and 1 and applying horizontal flip and training the dataimage_gen = ImageDataGenerator(rescale=1./255, horizontal_flip=True)train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,directory=train_dir,shuffle=True,target_size=(IMG_SHAPE,IMG_SHAPE))# Rescaling the images so all the values lie between 0 and 1 and rotating and training the dataimage_gen = ImageDataGenerator(rescale=1./255, rotation_range=45)train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,directory=train_dir,shuffle=True,target_size=(IMG_SHAPE, IMG_SHAPE))#Rescaling and zooming the dataimage_gen_train = ImageDataGenerator(rescale=1./255,rotation_range=45,width_shift_range=.15,height_shift_range=.15,horizontal_flip=True,zoom_range=0.5)train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,directory=train_dir,shuffle=True,target_size=(IMG_SHAPE,IMG_SHAPE),class_mode='sparse')
由于收集的图像大小不同,因此对图像进行调整大小。选择 150 * 150 的图像尺寸。图像的像素值范围从 0 到 255,为了提供更好的结果,像素值被重新缩放,因此所有值都位于 0 和 1 之间。不同的技术如水平翻转、缩放、旋转都是在现有的一组图像上进行。
image_gen_val = ImageDataGenerator(rescale=1./255)val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,directory=val_dir,target_size=(IMG_SHAPE, IMG_SHAPE),class_mode='sparse')model = Sequential()model.add(Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_SHAPE,IMG_SHAPE, 3)))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Conv2D(32, 3, padding='same', activation='relu'))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Conv2D(64, 3, padding='same', activation='relu'))model.add(MaxPooling2D(pool_size=(2, 2)))# Adding dropout to turn down some neuronsmodel.add(Flatten())model.add(Dropout(0.2))model.add(Dense(512, activation='relu'))model.add(Dropout(0.2))model.add(Dense(5, activation='softmax'))
卷积和最大池应用于数据集,在将其发送到输出层之前,模型被展平。剔除用于防止图像的过拟合。
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])epochs = 120history = model.fit_generator(train_data_gen,steps_per_epoch=int(np.ceil(train_data_gen.n / float(batch_size))),epochs=epochs,validation_data=val_data_gen,validation_steps=int(np.ceil(val_data_gen.n / float(batch_size))))
模型编译观察性能。您可以使用超参数来获得最佳结果。
github(代码)的链接附在下面
** [## VishnuBhaarath/计算机视觉项目
permalink dissolve GitHub 是 4000 多万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/VishnuBhaarath/Computervisionprojects/blob/master/Imageclassification.ipynb)**
数据科学中的图像分类
原文:https://towardsdatascience.com/image-classification-in-data-science-422855878d2a?source=collection_archive---------26-----------------------
什么是数据科学中的图像分类和用 Python 构建我们自己的图像分类器
在 Unsplash 上由 René Peters 拍摄的照片
欢迎阅读“Python 数据科学”迷你系列的第 2 部分!在这一课中,我们将讨论什么是图像分类,并训练一个图像分类器,它可以相当准确地识别猫和狗(反之亦然)。
什么是图像分类?
照片由乔伊·班克斯在 Unsplash 拍摄
考虑上面的图像。我相信我们都同意这是一辆车。但是退一步,分析一下你是如何得出这个结论的——你看到了一张图片,你将归类到它所属的类别(在这个例子中是一辆汽车)。简而言之,这就是图像分类的全部内容。
潜在地,给定图像可以被分类成无数种类(例如,汽车可以被分类成轿车、掀背车、SUV 等。反过来,它们又可以被分为奥迪、玛莎拉蒂甚至丰田。手动检查和分类图像是一个非常繁琐的过程,当你面对大量的图像,比如 10,000 甚至 100,000 张时,这几乎是不可能的。
图像分类是获取输入(如图片)并输出类别(如“猫”)或输入是特定类别的概率(该输入有 90%的概率是猫)的过程。
你可以看着一张照片,知道你在看一只猫,但计算机如何才能学会这样做呢?
用卷积神经网络!
我已经在 CNN 上写了一篇文章,值得一读,尤其是如果你是数据科学的新手。
事实证明,
计算机不像人类那样看图像。他们看到的是一个像素矩阵,每个像素有三个分量:红、绿、蓝(听说过 RGB 吗?)
因此,对我们来说,1000 像素的图像对计算机来说将有 3000 像素。这 3000 个像素中的每一个都将被赋予一个值或强度。结果是 3000 个精确像素强度的矩阵,计算机必须以某种方式将其解释为一个或多个对象。
对于黑白图像,像素被解释为 2D 阵列(例如,2x2 像素)。每个像素的值都在 0 到 255 之间。(零全黑,255 全白。灰度存在于这些数字之间。)基于这些信息,计算机可以开始处理这些数据。
对于彩色图像(红色、绿色和蓝色的组合),这是一个 3D 阵列。每种颜色的值都在 0 到 255 之间。可以通过组合三层中每一层的值来找到颜色。
设置图像数据的结构
在数据科学中,我们构建的图像分类器必须经过训练才能识别对象/模式——这向我们的分类器显示了准确(或大致)要识别的内容。
想象一下我给你看下面的图像:
由卢卡·布拉沃在 Unsplash 上拍摄
有一个船屋,一艘小船,小山,一条河和其他物体。你会注意到它们,但你不会确切知道我指的是什么对象。现在,如果我给你看另一张照片:
本杰明·沃罗斯在 Unsplash 上拍摄的照片
另一个:
Kurt Cotoaga 在 Unsplash 上拍摄的照片
你会认识到我想让你认识一座“山”。通过展示前两张图片,我基本上训练了你的大脑在第三张图片中识别一座山。
对计算机来说,这个过程(几乎)是一样的——我们的数据需要有特定的格式。我们将在本文的后面看到这一点,但是请记住这些要点。
我们的模型将在“训练”集中存在的图像上训练,并且标签预测将在测试集图像上发生。
构建我们的模型
对于本文,我将使用 Google Colab——一种基于 Jupyter 笔记本的免费云服务,支持免费 GPU。
Colab 提供了一个 GPU,而且完全免费。说真的!
这篇文章的全部代码可以在这里找到。
导入包和加载数据
在本文中,我们将利用 Kaggle 的狗对猫数据集的过滤版本。你可以从这里下载数据集的存档版本。
数据集具有以下目录结构:
**cats_and_dogs_filtered**
|__ **train**
|______ **cats**: [cat.0.jpg, cat.1.jpg, cat.2.jpg ....]
|______ **dogs**: [dog.0.jpg, dog.1.jpg, dog.2.jpg ...]
|__ **validation**
|______ **cats**: [cat.2000.jpg, cat.2001.jpg, cat.2002.jpg ....]
|______ **dogs**: [dog.2000.jpg, dog.2001.jpg, dog.2002.jpg ...]
接下来,我们将为训练集和验证集分配具有适当文件路径的变量。
如果您想知道猫和狗的图像在训练和验证目录中,请使用以下代码:
如果您得到了不同的输出,不要担心——您可能使用了不同的数据集
准备我们的数据
我们的图像必须被格式化成经过适当预处理的浮点张量,然后才能提供给网络。
可视化训练图像
我们现在将从训练生成器中提取一批图像,然后用matplotlib
绘制其中的五幅。
5 幅训练图像
创建模型
编译模型
在本文中,我们将使用 ADAM 优化器和二元交叉熵损失函数。
训练我们的模型
我们将使用ImageDataGenerator
类的fit_generator
方法来训练我们的网络。
让它运行—这需要一些时间。要有耐心!
用图可视化训练结果
培训和验证图
从图中可以看出,训练精度和验证精度相差很大,模型在验证集上仅达到大约 70% 的精度。
过度拟合
在上面的图中,训练精度随时间线性增加,而验证精度在训练过程中停滞在 70%左右。此外,训练和验证准确性之间的差异也很明显——这是过度拟合的迹象。
当训练样本数量较少时,模型有时会从训练样本的噪声(不需要的细节)中学习。这对新示例的模型性能有负面影响。这种现象被称为过度拟合。
过度拟合选举优先级(来源: XKCD
这意味着模型将很难对新数据集进行概化。
在训练过程中有多种方法来对抗过度适应。在本文中,我们将使用数据扩充并将退出添加到我们的模型中。
数据扩充
过度拟合通常发生在训练样本数量很少的时候。解决这个问题的一个方法是增加数据集,使其具有足够数量的训练示例。
我们的目标是,在训练阶段,我们的模型永远不会看到同一个图片两次。这有助于将它暴露给数据的更多方面,这将最终提高它的识别准确性。
扩充和可视化数据
在本文中,我们将对数据集应用随机旋转 和 缩放 增强,并查看单个图像在转换后的样子。
创建验证数据生成器
一般来说,我们只对训练样本应用数据扩充。在这种情况下,我们将重新缩放验证图像,并将其转换为批次。
拒绝传统社会的人
另一种减少过拟合的技术是将下降引入网络。
Dropout 是正则化的一种形式,它从你的神经网络中随机剔除单元(即神经元),因此在每次迭代中,你都以一个更小的神经网络结束工作。
这里的直觉是:不能依赖任何一个特征,要把权重分散开来。****
当您对图层应用“丢弃”时,它会在训练过程中随机丢弃(设置为零)输出单位的数量。它接受一个分数作为输入,比如 0.1、0.3 等。(这意味着从它所应用的层中随机丢弃 10%或 30%的输出单元)。
创建一个有辍学者的新网络
可视化新模型
你会注意到过度拟合比以前明显减少了。在对模型进行更多时期的训练后,精确度应该会上升。
更精确的模型
就是这样!我们已经成功地建立了一个图像分类器来识别图像中的猫和狗。与所有神经网络一样,我们无法达到 100%的准确性,但我们能够通过将模型暴露于相同数量的图像来提高模型的准确性水平,但使用了增强功能,如旋转、翻转和缩放。
** [## jasmcaus/图像分类器-python
训练一个神经网络,以相当高的准确度识别猫和狗,整个细节都在本…
github.com](https://github.com/jasmcaus/image-classifier-python)**
一如既往,非常感谢您的阅读!请告诉我你的想法或者希望我接下来在评论中写些什么。我也乐于接受批评!
下期帖子再见!😄
X 射线扫描的图像分类
原文:https://towardsdatascience.com/image-classification-of-x-ray-scans-ffaf970783f9?source=collection_archive---------33-----------------------
通过人工智能检测新冠肺炎症状
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
医疗保健行业正处于巨大的范式转变之中,技术是驱动力。在这一转变中,尽管人工智能已经取得了巨大的进步,但它还没有成熟到可以进行最终的诊断;它仅仅是为了帮助医疗专业人员给出他们的最终诊断。根据世界卫生组织(世卫组织),重症新冠肺炎最常见的诊断是重症肺炎。这是这个项目背后的关键驱动力。为了帮助对新冠肺炎的研究,我使用了我在哥伦比亚大学的第一门人工智能课程中学到的关于数据集的概念,将肺部 X 射线扫描分类为正常或肺炎感染的肺部。
来源:作者
外行人不可能如上所述区分正常人的肺和肺炎感染者的肺。
让我们戴上博士帽,深入研究数据集吧!
资料来源: Giphy
数据文件包含以下文件夹:
Train: 这个文件夹有 5309 张图片,每张图片都以图片 id 命名,例如 IM-0131–0001.jpeg
测试:这个文件夹以类似的方式存储了 624 张图像
Chest _ x ray _ Corona _ metadata . CSV:这具有从图像 id 到标签的映射,即从 IM-0131–0001.jpeg 到正常。有些图像没有贴图,因此被丢弃
Chest _ x ray _ Corona _ dataset _ summary . CSV:该 excel 文件包含属于每个类别的图像数量的明细
来源:作者
使用 python 对图像进行了处理并存储在本地。通过改变尺寸(300 x 300)和格式(PNG)使它们变得一致。
来源:作者
该标签用于在 python 中重命名图像,以便于分类。选择这些维度是为了平衡信息损失(太小)和训练模型时更高的计算时间(太大)之间的权衡。
处理后的图像被推送到 Google drive,因为模型训练必须在 Collab (GPU 支持和 25 GB RAM)上完成。正常图像标记为 1,肺炎图像标记为 0。训练图像被转换成从 0 到 255(像素)的数字数组,并与它们的标签一起存储在一个列表中,以传递给神经网络。20%的训练数据被保留用于验证(超参数调整)。
遵循的网络 架构是一个流行的、有效的、简单的架构,叫做 VGGnet 。
来源:快照(爱思唯尔)
这里使用的架构是图中所示的较小版本,但有趣的是,随着我们越往下,滤波器的尺寸越大。使用了以下过滤器:
32 → 64 → 128 → 512 → 1
隐藏层均由 Relu(校正线性单位)激活函数激活。二维卷积网络使用大小为[3,3]的核。这指定了二维卷积窗口的高度和宽度。滤波器大小[32]是输出维度的大小(卷积中输出滤波器的数量)。输入形状为[300,300,1]。前两个维度是图像的大小,最后一个维度是图像中通道的数量(1 表示图像为灰度)。
在编译网络之前,进行数据扩充以增加输入图像的数量。Keras 有一个图像数据生成器,这是一个标准的工具。生成器有助于重新缩放像素值以及生成新形式的图像。例如,放大和缩小、按特定角度旋转会创建同一图像的相似版本,并增加数据的大小。x 射线图像水平翻转的想法在这里得到了验证。使用生成器,像素值也被缩放到 0 和 1 之间。
损失函数被设置为二进制交叉熵当输出取二进制值时,选择的优化器是学习率为 0.0001 的亚当,选择的度量是准确度。网络的其他规范在 GitHub 上的代码中。
来源:作者
历元的数量被认为是 60。可以看到,精度在大约 50 个时期后趋于稳定,我们可以认为权重已经得到了相当好的优化。可以通过像提前停止这样的方法来进一步微调这些时期。
使用这些权重,在 624 幅测试图像上的准确率为 92.1% 。下面是结果的快照,其中每个图像的标题显示了实际的和预测的标签(代码中有更多的结果)。
来源:作者
来源:作者
TL;速度三角形定位法(dead reckoning)
对于未来的范围,如果有更多的新冠肺炎病例和其他病毒的标记图像,可以进行多级分类。神经网络是数据饥渴模型,有了海量的数据集,可以建立更深层次的网络,结果会好得多。此外,使用交叉验证(5 倍)来监控样本外性能可能是一个好主意,因为这将更能代表学习。也可以对时期的数量进行微调,以提高测试集的准确性。
在结束语中,我要再次强调这样一个事实,即人工智能算法应该作为做出重要决策的补充支持,特别是在医疗保健等领域(至少在我们到达终结者时代之前)!
来源: Giphy
基于 gRPC 或 REST 调用推理的张量流图像分类
原文:https://towardsdatascience.com/image-classification-on-tensorflow-serving-with-grpc-or-rest-call-for-inference-fd3216ebd4f3?source=collection_archive---------13-----------------------
通过 gRPC 或 rest API 调用为生产和推理中的影像分类模型提供服务
使用后墙创建的图像
Tensorflow 提供了多种部署模型的方法。我已经探索了为生产中的模型服务的大多数方法。我一直在寻找一个简单、安全、健壮的解决方案,它应该既能在边缘设备上访问,也能在用其他编程语言编写的服务器上访问。模型服务器非常适合我的需求。我通过 REST API 连接边缘设备,并对我的另一台服务器进行内部 RPC 调用。我还能够为不同的设备提供不同的版本,我可以很容易地从中央服务器控制这些设备。在本文中,我演示了如何以两种方式调用模型服务器,并找出哪种方法适合您的问题陈述。
我们开始吧
我们将部署一个在 Keras 上训练的图像分类器。为了让我们的模型在模型服务器上运行,我们需要以下面提到的特定格式导出我们的权重。
model_name/
version/
saved_model.pb
variables/
variables.index
variables.data-000-of-n
在 TensorFlow 2 中我们可以用tf.keras.models.save_model
函数直接导出训练好的模型。
MODEL_DIR = './cat_dog_classifier'
version = 1
export_path = os.path.join(MODEL_DIR, str(version))tf.keras.models.save_model(
model,
export_path,
overwrite=True,
include_optimizer=True,
save_format=None,
signatures=None,
options=None
)
验证元图和签名定义
我们将使用saved_model_cli
工具来检查图形。这将有助于找出输入和输出节点名称。
saved_model_cli show --dir ./cat_dog_classifier/1/ --all
安装模型服务器
*echo "deb [arch=amd64]* [*http://storage.googleapis.com/tensorflow-serving-apt*](http://storage.googleapis.com/tensorflow-serving-apt) *stable tensorflow-model-server tensorflow-model-server-universal" | sudo tee /etc/apt/sources.list.d/tensorflow-serving.list && \
curl* [*https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg*](https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg) *| sudo apt-key add -*sudo apt-get updatesudo apt-get install tensorflow-model-server
运行模型服务器
我们将在 grpc 的默认端口 8500 和 HTTP 的默认端口 8501 上运行模型服务器。如果需要,您还可以指定不同的端口。
假设您的终端位置与模型文件夹位于同一文件夹中。
export MODEL_DIR=$(pwd)/cat_dog_classifiertensorflow_model_server --port=8500 --rest_api_port=8501 --model_name=cat_dog_classifier --model_base_path="${MODEL_DIR}"
在使用您保存的模型成功运行模型服务器之后,您将获得与您的模型类似的输出。
2020–06–30 15:22:57.309568:I tensor flow _ serving/core/loader _ harness . cc:87]成功加载可服务版本{ name:cat _ dog _ classifier version:1 }
2020–06–30 15:22:57.311296:I tensor flow _ serving/model _ servers/server . cc:355]运行 gRPC model server 0 . 0 . 0:8500
图像推理
拨打 GRPC 电话
必要进口:
import grpc
import numpy as np
import nsvision as nv
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
设置 gRPC 通道:
label = ['cat', 'dog']
GRPC_MAX_RECEIVE_MESSAGE_LENGTH = 4096 * 4096 * 3
channel = grpc.insecure_channel('localhost:8500', options=[('grpc.max_receive_message_length', GRPC_MAX_RECEIVE_MESSAGE_LENGTH)])
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
grpc_request = predict_pb2.PredictRequest()
grpc_request.model_spec.name = 'cat_dog_classifier'
grpc_request.model_spec.signature_name = 'serving_default'
预测图像的输出:
确保为 gRPC 请求对象指定了正确的输入和输出。如果你想知道你的模型的输入输出键是什么,你可以使用元数据来检查,元数据可以通过在浏览器中调用一个系统化的 URL 来找到。在这种情况下,它是:
[http://localhost:8501/v1/models/cat_dog_classifier/metadata](http://localhost:8501/v1/models/cat_dog_classifier/metadata)
我的输入层名称是conv2d_input
和输出层名称是dense_1
,但它会因型号而异。
image = nv.imread('golden-retriever-royalty-free-image-506756303-1560962726.jpg',resize=(150,150),normalize=True)
image = nv.expand_dims(image,axis=0)
grpc_request.inputs['conv2d_input'].CopyFrom(tf.make_tensor_proto(image, shape=image.shape))
result = stub.Predict(grpc_request,10)
result = int(result.outputs['dense_1'].float_val[0])
print(label[result])
#This printed 'dog' on my console
进行 REST API 调用
Rest API 比 grpc 需要更少的库和设置。
进口:
import json
import requests
import nsvision as nv
为 REST 调用创建 JSON:
label = ['cat','dog']
image = nv.imread('cat.2033.jpg',resize=(150,150),normalize=True)
image = nv.expand_dims(image,axis=0)
data = json.dumps({
"instances": image.tolist()
})
headers = {"content-type": "application/json"}
预测图像的输出:
REST API 调用需要一个 URL 用于 POST 调用,模型服务器已经为 API 调用指定了一个 URL 格式:
http://host_ip:port/version_number/models/model_name:predict
在这种情况下,API URL 将是:
[http://localhost:8501/v1/models/cat_dog_classifier:predict](http://localhost:8501/v1/models/cat_dog_classifier:predict)
response = requests.post('[http://localhost:8501/v1/models/cat_dog_classifier:predict'](http://localhost:8501/v1/models/cat_dog_classifier:predict'), data=data, headers=headers)
result = int(response.json()['predictions'][0][0])
print(label[result])
#This printed 'cat' on my console
由于它是一个 REST API,您可以将端点连接到 javascript 等 web 客户端或 Android、Ios 等边缘设备或 Raspberry Pi 或 Jetson Nano 等物联网设备。
Github 资源库:
[## 生产中的图像分类器
该存储库包含在模型服务器上部署生产中的 Keras 模型的代码,以及使用 GRPC 或 HTTP…
github.com](https://github.com/novasush/image-classifier-in-production)
这是所有的乡亲
采用英特尔神经计算棒 2 (NCS2)的图像分类管道
原文:https://towardsdatascience.com/image-classification-pipeline-with-intel-neural-compute-stick-2-ncs2-2a69aab8b570?source=collection_archive---------27-----------------------
借助这份全面的指南,使用 NCS2 从数据集到影像分类
图片由 Oleksii Sheremet 使用 Adobe Photoshop 创建
简介
我们将在这个故事中讲述的内容:
- 安装 OpenVINO toolkit for Ubuntu。
- 用 Google Colab 进行数据预处理和模型训练。
- 将张量流模型保存为协议缓冲区(pb)格式。
- 将张量流模型转换成中间表示。
- 使用 NCS2 设备运行训练好的模型并获得预测。
在过去的几年里,机器学习已经被积极地引入到工业任务解决方案中。机器学习可以让你解决人类无法应对的问题。这可以在难以到达地方、在危险的化学生产中、在辐射增加的条件下等工作。机器学习适用于人类智能也可以应用,但无效的领域:预测关键故障、预防突发设备故障、视情维修、预测设备剩余寿命。
通常,在工业企业中,没有机会使用工作站进行数据分析和处理。因此,需要能够容易地连接到现有设备(通常是工业微型计算机)的特殊装置。计算模块 NCS2 就是这样的设备之一。
我们公司 AI Labs 定期收到解决一些与机器学习相关的工业问题的请求。不久前,我们有机会利用第二次全国人口普查提供的机会。因此,我使用 NCS2 开发了一个图像分类管道。我愿意与读者分享这项工作的成果。
安装 OpenVINO toolkit for Ubuntu
首先你需要下载并安装当前版本的 OpenVINO 。你可以从这里得到 OpenVINO。你可以在这里找到详细的安装指南。快速安装指南:
- 转到包含下载的档案的文件夹(例如, Downloads ):
cd ~/Downloads/
2.打开包装。tgz 文件(其中 <版本> 是下载存档的版本,你会在文件名中看到):
tar -xvzf l_openvino_toolkit_p_<version>.tgz
3.进入 l_openvino_toolkit_p_ <版本> 目录:
cd l_openvino_toolkit_p_<version>
4.以 root 用户身份运行安装脚本:
sudo ./install.sh
5.按照屏幕上的说明进行操作。
6.通过编辑设置环境变量。bashrc 文件。转到用户目录并打开。bashrc 文件进行编辑(例如,用 nano ):
cd ~
nano .bashrc
将下面一行添加到。bashrc 文件,保存并重新加载:
source /opt/intel/openvino/bin/setupvars.sh
source .bashrc
仅此而已。已经准备好了使用 NCS2 进行本地工作的所有必要功能。您可以执行数据预处理和模型训练。
使用 Google Colab 进行数据预处理和模型训练
Animals-10 数据集用于解决分类问题。为了简化问题,从动物中选择 5 个类别(动物种类)-10:猫、狗、鸡、马、羊。与每个类别相关的图像被分组到相应的文件夹中(“猫”、“狗”、“鸡”、“马”、“羊”)。模型是在 Google Colab 中训练出来的。让我们仔细看看。
首先,您需要安装一个适合与 NCS2 配合使用的 TensorFlow 版本:
!pip install tensorflow==1.15.2
导入库:
import tensorflow.compat.v1 as tf
import tensorflow_hub as hub
import matplotlib.pylab as plt
import numpy as np
将 Google Drive(包含数据集)安装到 Google Colab:
from google.colab import drive
drive.mount(‘/content/drive’)
转到上传数据的目录(“动物”):
%cd ‘drive/My Drive/animals’
文件夹结构应该如下所示:
Oleksii Sheremet 使用树命令创建图像
创建类别(标签)列表:
image_dir = 'animals'
import osclass_labels = []
for x in tf.gfile.Walk(image_dir):
try:
y = os.path.basename(x[0])
if y != 'animals':
class_labels.append(y)
except:
pass
print(class_labels)
我们得到以下列表:
[‘cat’, ‘chicken’, ‘dog’, ‘horse’, ‘sheep’]
基于这个列表,索引“0”对应于猫的图像,“1”对应于鸡,“2”对应于狗,“3”对应于马,“4”对应于羊。
设置图像的尺寸(224 x 224)。在训练模型时,我们使用 ImageDataGenerator 从文件夹中创建图像流。
IMAGE_SIZE = (224,224)image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1/255, validation_split=0.2)training_set = image_generator.flow_from_directory(str(image_dir), target_size=IMAGE_SIZE, subset='training')validation_set = image_generator.flow_from_directory(str(image_dir), target_size=IMAGE_SIZE, subset='validation')
正在检索预训练的 MobileNet 网络:
feature_extractor = tf.keras.applications.MobileNet(weights='imagenet',
include_top=False,
input_shape=(IMAGE_SIZE+(3,)))
feature_extractor.trainable=False
构建模型:
try:
del model
except:
passx=feature_extractor.output
x=tf.keras.layers.GlobalAveragePooling2D()(x)classifier=tf.keras.layers.Dense(label_batch.shape[1],activation='softmax')(x)model=tf.keras.Model(inputs=feature_extractor.input,outputs=classifier)model.build((None,)+IMAGE_SIZE+(3,))
model.summary()
编译模型:
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
metrics=['acc'])
收集日志的回调方法:
class CollectBatchStats(tf.keras.callbacks.Callback):
def __init__(self):
self.batch_losses = []
self.batch_acc = []
self.validation_losses = []
self.validation_acc = []def on_train_batch_end(self, batch, logs=None):
self.batch_losses.append(logs['loss'])
self.batch_acc.append(logs['acc'])
try:
self.validation_losses.append(logs['val_loss'])
self.validation_acc.append(logs['val_acc'])
except:
self.validation_losses.append(None)
self.validation_acc.append(None)
self.model.reset_metrics()
训练模型:
steps_per_epoch = np.ceil(training_set.samples/training_set.batch_size)batch_stats_callback = CollectBatchStats()history = model.fit_generator(training_set, epochs=5,
steps_per_epoch=steps_per_epoch,
validation_data=validation_set,
callbacks = [batch_stats_callback])
检查预测:
for image_batch, label_batch in validation_set:
print("Image batch shape: ", image_batch.shape)
print("Label batch shape: ", label_batch.shape)
break
我们有以下形状:
Image batch shape: (32, 224, 224, 3)
Label batch shape: (32, 5)
我们可以得到预测和它们的类别:
predictions = model.predict(image_batch)
predicted_class = np.argmax(predictions, axis=-1)
将结果可视化:
plt.figure(figsize=(12,10))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
plt.subplot(6,5,n+1)
plt.imshow(image_batch[n])
plt.title(f'pred: {class_labels[predicted_class[n]]}\norig: {class_labels[np.array(label_batch[n]).argmax()]}')
plt.axis('off')
_ = plt.suptitle("Animals")
图像由 Oleksii Sheremet 使用 matplotlib 模块创建
将 TensorFlow 模型保存为协议缓冲区(pb)格式
冻结并保存模型:
from tensorflow.compat.v1.keras import backend as Ksession = K.get_session()
graph = session.graph
with graph.as_default():
freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference([]))
output_names = [out.op.name for out in model.outputs]
output_names += [v.op.name for v in tf.global_variables()]
input_graph_def = graph.as_graph_def()
for node in input_graph_def.node:
node.device = ""
frozen_graph = tf.graph_util.convert_variables_to_constants(session, input_graph_def, output_names, freeze_var_names)
tf.train.write_graph(frozen_graph, "animals", "animals.pb", as_text=False)
因此,训练好的模型被保存到 animals.pb 文件中。
将张量流模型转换为中间表示法
有详细的教程关于转换张量流模型。若要转换训练好的模型,您需要执行下面描述的操作。
进入模型 _ 优化器文件夹(需要 mo_tf.py ):
cd ~/intel/openvino/deployment_tools/model_optimizer
使用以下命令运行 mo_tf.py (请注意,您必须使用 255 的缩放因子,与图像预处理阶段相同):
python mo_tf.py --input_model <path_to_model>/animals.pb --batch 1 --scale 255
如果转换成功,您将在控制台中看到以下消息:
[ SUCCESS ] Generated IR version 10 model.
[ SUCCESS ] XML file: /home/user_name/intel/openvino_2020.4.287/deployment_tools/model_optimizer/./animals.xml
[ SUCCESS ] BIN file: /home/user_name/intel/openvino_2020.4.287/deployment_tools/model_optimizer/./animals.bin
[ SUCCESS ] Total execution time: 31.81 seconds.
[ SUCCESS ] Memory consumed: 370 MB.
因此,作为转换的结果,我们得到 3 个文件: animals.xml 和 animals.bin,animals.mapping 。建议将这些文件复制到一个单独的文件夹中(例如, test_folder )。
用 NCS2 设备运行训练好的模型并获得预测
要在 NCS2 上检查模型的性能,可以使用classification _ sample . py脚本,它包含在 open vino(/home/user _ name/Intel/open vino/deployment _ tools/inference _ engine/samples/python/classification _ sample/classification _ sample . py)中。将这个脚本复制到包含 3 个训练好的模型文件的文件夹中( animals.xml,animals.bin,animals.mapping )。此外,为了检查功能,您可以将几幅图像(例如,001.jpeg和002.jpeg)复制到同一个文件夹中。
将 NCS2 连接到 USB,进入 test_folder 并运行( MYRIAD 键在 NCS2 上运行模型):
python classification_sample.py -m animals.xml -i 001.jpeg -d MYRIAD
在控制台中测试图像和 NCS2 的输出:
图片由 Oleksii Sheremet 使用 Adobe Photoshop 创建
python classification_sample.py -m animals.xml -i 002.jpeg -d MYRIAD
图片由 Oleksii Sheremet 使用 Adobe Photoshop 创建
从给出的例子中可以看出,分类是正确的。图像001.jpeg显示一匹马(classid=3),图像002.jpeg显示一只鸡(classid=1)。
结论
所展示的管道允许您快速通过从数据预处理和模型训练到基于 NCS2 模块的测试的所有阶段。实践表明,NCS2 很好地处理了图像分类任务,可以推荐用于解决简单的工业分类任务。
参考文献
英特尔神经计算棒 2
OpenVINO 工具包
MobileNets:用于移动视觉应用的高效卷积神经网络
MobileNet
在 Colab 上使用 Fastai v2 进行图像分类
原文:https://towardsdatascience.com/image-classification-using-fastai-v2-on-colab-33f3ebe9b5a3?source=collection_archive---------18-----------------------
一步一步的指导训练和分类图像,只需几行代码
瑞安·卡彭特在 Unsplash 上拍摄的照片
介绍
Fastai 是一个基于 PyTorch 构建的库,用于深度学习应用。他们的使命是让深度学习更容易使用,并让更多来自各种背景的人参与进来。他们还为 Fastai 提供免费课程。
Fastai v2 于 8 月发布,我将使用它来构建和训练一个深度学习模型,在 Colab 上对不同的运动领域进行分类,只需几行代码。
数据收集
首先,我需要收集图像供模型学习。我希望有一个模型来分类不同的运动领域:棒球,篮球,足球,网球和美式足球。我搜索并下载了这些图片,保存在不同的文件夹中,然后上传到 Google Drive。
文件夹包含运动场图片。(图片由作者提供)
设置 Colab 环境
一旦数据集准备好了,我就可以开始 Colab 的工作了。
首次升级 fastai,
!pip install fastai --upgrade -q
并导入fastai.vision
,
from fastai.vision.all import *
然后安装 Google Drive 并设置路径
from google.colab import drive
drive.mount(‘/content/gdrive’, force_remount=True)
root_dir = ‘gdrive/My Drive/Colab Notebooks/’
base_dir = root_dir + ‘ball_class’
path=Path(base_dir)
现在我们准备好出发了。
数据块和数据加载器
Fastai 提供了一个中级 API: DataBlock 来处理数据,它非常容易使用。
fields = DataBlock(blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
get_y=parent_label,
splitter=RandomSplitter(valid_pct=0.2, seed=42),
item_tfms=RandomResizedCrop(224, min_scale=0.5),
batch_tfms=aug_transforms())
可以使用不同的blocks
,在这种情况下,我们使用ImageBlock
作为 x,使用CategoryBlock
作为标签。对于不同的应用,也可以使用其他块,例如 MultiCategoryBlock、MaskBlock、PointBlock、BBoxBlock、BBoxLblBlock。
我使用 fastai 中的get_image_files
函数获得图像的路径作为 x,并使用parent_label
方法找到文件夹名称作为标签。fastai 中有一些内置函数,你也可以自己写函数来做这个。
然后我用RandomSplitter
分割训练和验证数据集。
然后使用RandomResizedCrop
将图像调整为 224,使用aug_transforms()
进行数据扩充。
这是 Fastai 数据块的一种模板,它非常灵活,你可以根据你的情况改变它。你可以在这里找到详细的 tutoria l。
准备好数据块后,我们可以创建数据加载器:
并使用vocab
检查标签
展示一些带有标签的图片
火车
现在我们准备好训练了。
我使用 resnet34 创建了一个 CNN 学习者:
learn = cnn_learner(dls, resnet34, metrics=error_rate)
并使用lr_find()
找到合适的学习率
然后我们可以使用fit_one_cycle
训练模型
只有经过 5 个历元,我们才能达到> 90%的准确率。
现在我们可以解冻网络,训练整个网络。
让我们来看看,仅仅经过几个纪元,我们就达到了 95%的准确率。
解释
我们也可以解释这个模型。混淆矩阵可以用以下公式绘制:
我们也可以用这个来看损失最高的图像。
本文展示了一个使用 fastai 进行图像分类的简单快捷的例子。使用这个强大的库可以创建更复杂和有趣的应用程序。
感谢阅读,编码快乐。
基于 GluonCV 的图像分类
原文:https://towardsdatascience.com/image-classification-using-gluoncv-f6ae5401d6ae?source=collection_archive---------59-----------------------
使用 GluonCV 的预训练图像分类网络实现网球检测器
在的上一篇文章中,我们讨论了使用 GluonCV 的物体检测。在本文中,我们将讨论如何实现一个二值图像分类器,使用 GluonCV 的预训练图像分类网络对给定图像是否是网球进行分类。我们一步一步地实现机器学习管道,从加载和转换输入图像,到加载和使用预训练的模型。
- 导入库
为了开始一些初始设置,我们将导入包并设置数据的路径。
import mxnet as mx
import gluoncv as gcv
import matplotlib.pyplot as plt
import numpy as np
import os
from pathlib import Path
2。加载图像
为了加载图像,让我们实现一个函数,在给定文件路径的情况下从磁盘加载图像。该函数应该返回一个 8 位图像数组,这是在 MXNet 的 NDArray 格式和 HWC 布局(即高度,宽度,然后通道)。
def **load_image**(filepath):
image = mx.image.imread(filepath)
return image
3。变换图像
加载图像后,我们应该转换图像,以便它可以用作预训练网络的输入。我们计划在 ImageNet 上使用预先训练好的网络。因此,图像转换应遵循用于 ImageNet 预训练的相同步骤。图像应通过以下方式进行转换:
- 将最短尺寸调整为 224。例如(448,1792) -> (224,896)。
- 裁剪到尺寸为(224,224)的中心正方形。
- 将图像从 HWC 布局转换为 CHW 布局。
- 使用 ImageNet 统计数据标准化图像(即每个颜色通道的平均值和方差)。
- 创建一批 1 个图像。
这可以使用下面的函数来实现。
def **transform_image**(array):
image = gcv.data.transforms.presets.imagenet.transform_eval(array)
return image
4。加载模型
我们将使用已经在 ImageNet 上预先训练好的 MobileNet 1.0 图像分类模型。该模型可以从 GluonCV 模型动物园加载,如下所示:
def **load_pretrained_classification_network**():
model = gcv.model_zoo.get_model('MobileNet1.0', pretrained=True, root = M3_MODELS)
return model
5。使用型号
加载图像后,下一个任务是通过预训练的网络传递变换后的图像,以获得所有 ImageNet 类的预测概率(现在忽略网球类)。
提示 1 :不要忘记你通常是在处理一批图像,即使你只有一张图像。
提示 2:记住我们网络的直接输出不是概率。
def **predict_probabilities**(network, data):
prediction = network(data)
prediction = prediction[0]
probability = mx.nd.softmax(prediction)
return probability
6。查找类别标签
在 imageNet 中有 1000 个类。对于网球分类,我们需要找到与网球相关的概率(在 1000 个类别中)。这可以通过实现返回给定类标签的索引的函数来完成(例如,admiral
是索引321
)。
def **find_class_idx**(label):
for i in range(len(network.classes)):
if label == network.classes[i]:
return i
使用上面的函数来查找网球的正确索引,您应该实现一个函数来从网络计算的 1000 个类概率中分割网球的计算概率。它还应该将概率从 MXNet NDArray
转换为 NumPy float32
。我们将使用它作为图像是一个网球的置信度得分。
def **slice_tennis_ball_class**(pred_probas):
tennis_prob = pred_probas[find_class_idx('tennis ball')]
return tennis_prob.astype('float32').asscalar()
7。网球图像分类
把所有的碎片放在一起,我们就可以对一个图像进行分类,可以分辨出一个图像是不是网球。这可以通过创建一个名为TennisBallClassifier
的类来实现整个分类管道来实现。您应该注意到,预训练模型在初始化期间被加载一次,然后它应该在classify
方法中使用。
class **TennisBallClassifier**():
def **__init__**(self):
self._network = load_pretrained_classification_network()
def **classify**(self, filepath):
image = **load_image**(filepath)
transformed_image = **transform_image**(image)
self._visualize(transformed_image)
probabilities = **predict_probabilities**(self._network, transformed_image)
pred_proba = **slice_tennis_ball_class**(probabilities)
print('{0:.2%} confidence that image is a tennis ball.'.format(pred_proba))
return pred_proba
def **_visualize**(self, transformed_image):
chw_image = transformed_image[0].transpose((1,2,0))
chw_image = ((chw_image * 64) + 128).clip(0, 255).astype('uint8')
plt.imshow(chw_image.asnumpy())classifier = TennisBallClassifier()
filepath = Path(M3_IMAGES, 'erik-mclean-D23_XPbsx-8-unsplash.jpg')
pred_proba = classifier.classify(filepath)
np.testing.assert_almost_equal(pred_proba, 2.0355723e-05, decimal=3)
filepath = Path(M3_IMAGES, 'marvin-ronsdorf-CA998Anw2Lg-unsplash.jpg')
pred_proba = classifier.classify(filepath)
np.testing.assert_almost_equal(pred_proba, 0.9988895, decimal=3)
因此,使用预先训练的模型,我们可以正确地分类图像是否是网球。如上所示,在对足球进行分类时,我们看到该模型给出了 0%的置信度认为它是网球,而对网球图像进行了 99.9%置信度的正确分类。
成为媒体会员这里支持独立写作,每月 5 美元,获得媒体上的所有故事。
利用 Python 中的张量流进行图像分类
原文:https://towardsdatascience.com/image-classification-using-tensorflow-in-python-f8c978824edc?source=collection_archive---------33-----------------------
什么是图像分类,如何使用 Python 中的 TensorFlow 进行图像分类?
来源: Pixabay
简介
在之前的帖子中,我们看到了如何在一个简单的数据集上使用 TensorFlow。在这个例子中,我们将使用 TensorFlow 进行图像分类。我们将要使用的数据集是 MNIST 数据集,它是张量流数据集的一部分。
什么是图像分类?
图像分类是指计算机视觉中可以根据图像的视觉内容对图像进行分类的过程。例如,图像分类算法可以被设计成辨别图像是否包含人形。
导入库
首先,我们需要导入所需的库。在本例中,我们需要:
- Numpy—Python 编程语言的库,增加了对大型多维数组和矩阵的支持,以及对这些数组进行操作的大量高级数学函数。
- TensorFlow —一个免费的开源软件库,用于数据流和跨一系列任务的差异化编程。
- TensorFlow 数据集— 一组随时可用的数据集,使用 TensorFlow 或其他 Python ML 框架。
加载和解包数据
现在,我们需要加载数据。MNIST 数据库由具有手写数字的 28×28 大小的图像组成。
此外,我们需要解开数据——我们这样做是因为我们需要培训、验证和测试集。
缩放图像
我们将定义一个函数来缩放图像。我们这样做是因为 RGB 值(红、绿、蓝)每个都是 8 位。每种颜色的范围是 0-255(2⁸= 256 种可能性)。组合范围是 256256256。通过除以 255,0–255 范围可以描述为 0.0–1.0 范围,其中 0.0 表示 0 (0x00),1.0 表示 255 (0xFF)。
洗牌数据
因为数据可能在一个部分中包含相同的值,所以我们需要对其进行混排。缓冲区大小是一个标量,它影响转换的随机性。它维护一个 buffer_size 元素的缓冲区,并从该缓冲区中随机选择下一个元素(如果下一个输入元素可用,则用下一个输入元素替换它),而不是对整个数据集进行洗牌。洗牌后,我们需要批量数据集。批处理将数据集的 n 个连续元素堆栈到单个元素中的最简单形式是使用 batch 方法。Dataset.batch()转换正是这样做的。
概述模型
我们需要定义输出大小—在我们的例子中,我们期望输出从 0 到 9,所以大小将是 10。
接下来,我们需要定义隐藏层的大小。隐藏层的大小应介于输入层和输出层的大小之间。在这种情况下,隐藏层大小将为 50。
唯一剩下的事情就是定义模型。本例中的模型是使用 keras 定义的。顺序模型是层的线性堆叠。当我们定义模型时,我们需要为隐藏层和输出层选择激活函数。如果你想了解更多关于激活功能的内容,在之前的帖子里有完整的解释—https://towardsdatascience . com/deep-learning-with-tensor flow-5d 3a 7 a 8 c 55 CD
编译模型
在这一步中,我们将为培训配置模型。Compile 定义了损失函数、优化器和指标。我们需要一个编译好的模型来训练(因为训练使用了损失函数和优化器)。
- 优化器:优化算法或策略负责减少损失,并尽可能提供最准确的结果。在这个例子中,我们使用了一种叫做 Adam 的优化算法。Adam 是一种优化算法,可以用来代替经典的随机梯度下降过程,根据训练数据迭代地更新网络权重。
- 损失:这是一种评估特定算法对给定数据的建模程度的方法。如果预测与实际结果相差太多,损失函数就会产生一个非常大的数字。分类交叉熵和稀疏分类交叉熵具有相同的损失函数,唯一的区别是,当输入是一次热编码时,我们使用分类交叉熵,当输入是整数时,我们使用稀疏分类交叉熵。
- 指标:指标是用来判断模型性能的函数。
训练模型
当我们拟合模型时,我们实际上是在为固定数量的历元(数据集上的迭代)训练模型。我们需要定义批量大小、最大时期和提前停止。
- 批量 —一次正向/反向传递中训练样本的数量
- 时期数 —训练模型的时期数。一个历元是对所提供的整个 x 和 y 数据的迭代。
- 提前停止 —当监测的数量停止提高时,停止训练。耐心是一个数字,它定义了产生无改善的监控量的时期数,在此之后训练将停止。
结果
我们可以通过 epoch 监控训练过程。使用训练和验证集,每个历元给我们提供关于准确性和损失的信息。
评估模型
该函数返回测试集上模型的损失值和度量值。在这个函数中,输入数据将是我们的测试集。我们可以打印测试损失和测试准确度——在这个例子中,我们的准确度接近 97%。
结论
这只是图像分类的一个简单例子。我们可以对图像进行许多更复杂的修改。例如,我们可以对图像应用各种过滤器,这些过滤器使用数学算法来修改图像。有些过滤器非常容易使用,而有些则需要大量的技术知识。
如果你对这个话题感兴趣,请随时联系我。
领英简介:https://www.linkedin.com/in/ceftimoska/
博文原文可从以下链接获得:https://interworks . com . MK/image-class ification-using-tensor flow-in-python/
另外,你可以在下面的链接中找到另一篇类似的博文:https://interworks.com.mk/focusareas/data-management/
时尚 MNIST 图像分类:为什么卷积神经网络优于传统的机器学习算法
原文:https://towardsdatascience.com/image-classification-with-fashion-mnist-why-convolutional-neural-networks-outperform-traditional-df531e0533c2?source=collection_archive---------14-----------------------
在最近十年,随着深度学习的发现,图像分类领域经历了一次复兴。传统的机器学习方法已经被更新更强大的深度学习算法所取代,例如卷积神经网络。然而,要真正理解和欣赏深度学习,我们必须知道为什么它在其他方法失败的地方成功了。在本文中,我们试图通过在时尚 MNIST 数据集上应用各种分类算法来回答其中的一些问题。
因贾·帕夫里奇在 Unsplash 拍摄的照片
数据集信息时尚 MNIST 于 2017 年 8 月由 Zalando Fashion 的研究实验室推出。它的目标是作为测试机器学习算法的新基准,因为 MNIST 变得太容易了,被过度使用了。MNIST 由手写数字组成,而时尚 MNIST
则由 10 种不同的服装物件的图像组成。每个图像具有以下属性:
- 其尺寸为 28 × 28 像素。
- 相应地旋转并以灰度表示,整数值范围从 0 到 255。
- 用黑色表示的空白区域,值为 0。
在数据集中,我们区分以下服装对象:
- t 恤/上衣
- 裤子
- 套衫
- 连衣裙
- 外套
- 凉鞋
- 衬衫
- 运动鞋
- 包
- 踝靴
探索性数据分析由于数据集是作为 Keras 库的一部分提供的,并且图像已经过处理,因此我们不需要进行太多的预处理。我们所做的唯一改变是将图像从 2D 阵列转换成 1D 阵列,因为这使得它们更容易处理。
数据集由 70000 幅图像组成,其中 60000 幅作为训练集,10000 幅作为测试集。像在原始 MNIST 数据集中一样,项目是均匀分布的(每个训练集 6000 个,测试集中 1000 个)。
不同服装的图片示例。作者照片。
然而,一幅图像仍然有 784 个维度,所以我们转向主成分分析(PCA),看看哪些像素是最重要的。我们设置了 80%累积方差的传统基准,该图告诉我们,仅用大约 25 个主成分(PC 总数的 3%)就可以做到这一点。然而,这并不奇怪,因为我们可以在上面的照片中看到,在每个图像中有许多共享的未使用空间,不同类别的服装有不同的黑色图像部分。后者可能与以下事实有关:大约 70%的累积方差仅由 8 个主成分解释。
解释的累积差异百分比。作者照片。
我们将在逻辑回归、随机森林和支持向量机中应用主成分。
图像分类问题只是分类问题的一小部分。使用最多的图像分类方法是深度学习算法,其中一种是卷积神经网络。其余采用的方法将是常用分类方法的一个小集合。由于分类标签是均匀分布的,没有错误分类的惩罚,我们将使用准确性度量来评估算法。
卷积神经网络(CNN) 我们采用的第一种方法是 CNN。由于图像是灰度的,我们只应用了一个通道。我们选择了以下架构:
- 两个卷积层,32 和 64 个滤波器,3 × 3 内核大小,以及 relu 激活。
- 选择轮询层来操作尺寸为 2 × 2 的瓦片,并选择其中的最大元素。
- 两组密集层,第一组选择 128 个特征,具有 relu 和 softmax 激活。
这个架构没什么特别的。事实上,这是我们可以用于 CNN 的最简单的架构之一。这向我们展示了这类方法的真正威力:用一个基准结构获得很好的结果。
对于损失函数,我们选择分类交叉熵。为了避免过度拟合,我们从训练集中选择了 9400 幅图像作为我们参数的验证集。我们使用了新型优化器 adam,它改进了
标准梯度下降法,并对每个参数使用不同的学习率,批量大小等于 64。该模型被训练了 50 个时期。我们在下图中展示了精确度和损耗值。
作者照片。
作者照片。
我们看到该算法在 15 个周期后收敛,它没有被过度训练,所以我们对它进行了测试。获得的测试精度等于
89%,这是所有方法中获得的最好结果!
在讨论其他方法之前,让我们解释一下卷积层的作用。一个直观的解释是,第一层捕捉直线,第二层捕捉曲线。在这两层上,我们应用了最大池,它选择内核中的最大值,将衣服部分与空白空间分开。通过这种方式,我们获得了数据的代表性。在其他情况下,神经网络自己执行特征选择。在最后一个池层之后,我们得到一个人工神经网络。因为我们正在处理分类问题,最后一层
使用 softmax 激活来获得类别概率。由于类别概率遵循一定的分布,交叉熵表示与网络优选分布的距离。
多项式逻辑回归由于像素值是分类变量,我们可以应用多项式逻辑回归。我们以 one vs rest 的方式应用它,训练十个二元逻辑回归分类器,我们将使用它们来选择项目。为了不过度训练,我们使用了 L2 正则化。我们在这个算法上得到 80%的准确率,比卷积神经网络低 9%。但我们必须考虑到,这种算法适用于居中且正常旋转的灰度图像,有很多空白空间,因此它可能不适用于更复杂的图像。
最近邻居和质心算法我们使用了两种不同的最近距离算法:
- k-最近邻
- 最近质心
最近质心算法找到每一类元素的平均值,并将测试元素分配给被分配了最近质心的类。这两种算法都是针对 L1 和 L2 距离实现的。k-最近算法的准确率为 85%,而质心算法的准确率为 67%。这些结果是在 k=12 时获得的。k-最近邻的高精度告诉我们,属于同一类的图像往往在图像上占据相似的位置,并且也具有相似的像素强度。虽然最近邻获得了良好的结果,但它们的性能仍然比 CNN 差,因为它们不在每个特定特征的邻域中操作,而质心失败,因为它们不能区分相似外观的对象(例如套头衫与 t 恤/上衣)
随机森林为了选择用于估计的最佳参数,我们使用平方根(bagging)和全部数量的特征、基尼和熵标准以及具有最大深度 5 和 6 的树来执行网格搜索。网格搜索建议我们应该使用具有熵标准的特征的平方根数量(两者都是分类任务所期望的)。然而,所获得的准确性仅等于 77%,这意味着随机森林对于该任务来说不是特别好的方法。失败的原因是主成分不代表图像可以拥有的矩形分区,随机森林在其上操作。同样的推理也适用于全尺寸图像,因为树会太深,失去可解释性。
支持向量机(SVM) 我们使用径向和多项式核来应用 SVM。径向核有 77%的准确率,而多项式核失败得很惨,只有 46%的准确率。虽然图像分类不是他们的强项,但对于其他二元分类任务仍然非常有用。他们最大的警告是,他们需要特征选择,这会降低准确性,如果没有它,他们的计算成本会很高。此外,他们以一对一的方式应用多类分类,这使得有效地创建分离超平面变得更加困难,从而在处理非二进制分类任务时失去了价值。
结论在本文中,我们对一个图像分类问题应用了多种分类方法。我们已经解释了为什么 CNN 是我们可以采用的最佳方法,以及为什么其他方法会失败。CNN 是最实用且通常是最准确的方法的一些原因是:
- 他们可以通过层次转移学习,保存推论,并在随后的层次上做出新的推论。
- 在使用该算法之前不需要特征提取,它是在训练期间完成的。
- 它识别重要的特征。
然而,他们也有自己的警告。众所周知,它们在旋转和缩放不同的图像上是失败的,但在这里不是这样,因为数据是经过预处理的。尽管其他方法无法在该数据集上给出良好的结果,但它们仍然用于与图像处理相关的其他任务(锐化、平滑等)。).
代码:https://github . com/raden jeci 153/Stat _ ML/blob/master/project . ipynb
影像分类器-使用 Monk 库进行房屋类型分类
原文:https://towardsdatascience.com/image-classifier-house-room-type-classification-using-monk-library-d633795a42ef?source=collection_archive---------24-----------------------
关于如何使用 Monk 库解决计算机视觉分类问题的文章。
这篇文章是一个关于如何使用 Monk 库来分类房间类型的教程,比如客厅、餐厅等等。
详细教程可在 GitHub 上获得。
数据集中的一些样本图像
关于数据集
- 该数据集包含总共 145436 幅图像,分为七类,即“外部”、“卧室”、“厨房”、“起居室”、“内部”、“浴室”、“餐厅”。
- 可以从这里下载。
关于 Monk 库和 Monk 如何让图像分类变得简单
- 编写更少的代码,创建端到端的应用程序。
- 只学习一种语法,使用任何深度学习库——py torch、Mxnet、Keras、TensorFlow 等,创建应用。
- 通过多个实验轻松管理您的整个项目。
这座图书馆是为谁而建的
- 学生:使用我们的综合学习路线图无缝学习计算机视觉。
- 研究人员和开发人员:创建和管理多个深度学习项目。
- Kaggle、Codalab、HackerEarth、AiCrowd 等上的比赛参与者。
目录
- 安装 Monk
2.将预训练模型用于部分房屋分类数据集
3.从头开始训练分类器
- 使用 ResNet 变体训练部分 house 分类器
- 了解在 ResNets 变体之间切换时发生的所有差异。
- 理解更大更深的网络并不总是意味着更好的结果
- 在这个实验中,您将使用 mxnet 后端
4.结论。
1.安装 Monk
*#using cuda10.0 download monk using following command***pip install -U monk-gluon-cuda100**
更多安装方法请访问 Monk 库。
2.使用预先训练好的模型进行演示
首先,从 google drive 下载预先训练好的模型。
**! wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=10SrowcOJp8GWqEB21BfCIinqUCHS7PMv' -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=10SrowcOJp8GWqEB21BfCIinqUCHS7PMv" -O cls_house_scene_trained.zip && rm -rf /tmp/cookies.txt**
上面会下载预训练模型的 zip 文件,命名为 cls_house_scene_trained.zip,现在我们要解压文件了。
**! unzip -qq cls_house_scene_trained.zip**
导入monk . glon _ prototype用于使用和尚库。
**#Using mxnet-gluon backend*
*# When installed using pip*
**from** **monk.gluon_prototype** **import** **prototype***
让我们以推断模式加载模型,对演示数据进行分类。
**# Load project in inference mode*
**gtf = prototype(verbose=1);
gtf.Prototype("Task", "gluon_resnet18_v1_train_all_layers", eval_infer=True);***
根据一些样本推断
***img_name = "workspace/test/2.jpg"
predictions = gtf.Infer(img_name=img_name);
from IPython.display import Image
Image(filename=img_name, height=300, width=300)***
***img_name = "workspace/test/3.jpg"
predictions = gtf.Infer(img_name=img_name);
from IPython.display import Image
Image(filename=img_name, height=300, width=300)***
***img_name = "workspace/test/6.jpg"
predictions = gtf.Infer(img_name=img_name);
from IPython.display import Image
Image(filename=img_name, height=300, width=300)***
更多例子请访问笔记本。
3.从头开始训练自定义分类器
什么是 ResNet?
来自https://towards data science . com/an-overview-of-resnet-and-its-variants-5281 e2f 56035
*- The core idea of ResNet is introducing a so-called “identity shortcut connection” that **skips one or more layers**
- The deeper model should not produce a training error higher than its shallower counterparts.
- solves the problem of **vanishing gradients** as network depth increased*
ResNet18 架构
近期内容
- 加载数据集
- 训练模型采用 resnet18_v1(只训练最后一层)架构进行迁移学习。
- 训练模型采用 resnet18_v1(训练所有层)架构进行迁移学习。
- 训练模型采用 resnet18_v2(只训练最后一层)架构进行迁移学习。
- 训练模型采用 resnet34_v1(只训练最后一层)架构进行迁移学习。
- 比较所有的型号。
1.加载数据集
数据集学分:https://omidpoursaeed . github . io/publication/vision-based-real-estate-price-estimate/
步骤 1:下载数据集。
***! wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=0BxDIywue_VABY1dRcFVvZ3BodnM' -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=0BxDIywue_VABY1dRcFVvZ3BodnM" -O dataset.zip && rm -rf /tmp/cookies.txt***
步骤 2:解压缩数据集。
***! unzip -qq dataset.zip***
2.训练模型采用 resnet18_v1(只训练最后一层)架构进行迁移学习。
步骤 1:加载实验并插入数据。更多详情请访问。
**# Load experiment*
**monk_gln = prototype(verbose=1);
monk_gln.Prototype("Task", "gluon_resnet18_v1");** *# Insert data and set params in default mode*
**monk_gln.Default(dataset_path="Train",
model_name="resnet18_v1",
freeze_base_network=True,
num_epochs=10);***
步骤 2:训练模型。
**#Start Training*
**monk_gln.Train();***
3.训练模型采用 resnet18_v1(训练所有层)架构进行迁移学习。
步骤 1:加载实验并插入数据。更多详情请访问。参数freeze _ base _ network**将被设置为 false 以训练所有层。**
***# Load experiment*
**monk_gln = prototype(verbose=1);
monk_gln.Prototype("Task", "gluon_resnet18_v1_train_all_layers");**
*# Insert data and set params in default mode*
**monk_gln.Default(dataset_path="Train",
model_name="resnet18_v1",
freeze_base_network=False,
num_epochs=10);****
步骤 2:训练模型。
***#Start Training*
**monk_gln.Train();****
4.训练模型采用 resnet18_v2(只训练最后一层)架构进行迁移学习。
步骤 1:加载实验并插入数据。欲了解更多详情,请访问。
***# Load experiment* **monk_gln = prototype(verbose=1);
monk_gln.Prototype("Task", "gluon-resnet18_v2");**
*# Insert data and set params in default mode*
**monk_gln.Default(dataset_path="Train",
model_name="resnet18_v2",
freeze_base_network=True,
num_epochs=10);****
步骤 2:训练模型。
***#Start Training*
**monk_gln.Train()****
5。训练模型采用 resnet34_v1( 只训练最后一层)**架构进行迁移学习。**
步骤 1:加载实验并插入数据。更多详情请访问。
***# Load experiment* **monk_gln = prototype(verbose=1);
monk_gln.Prototype("Task", "gluon-resnet18_v2");***# Insert data and set params in default mode*
**monk_gln.Default(dataset_path="Train",
model_name="resnet34_v1",
freeze_base_network=True,
num_epochs=10);****
步骤 2:训练模型。
***#Start Training*
**monk_gln.Train()****
6.比较所有的模型。
步骤 1:使用 Monk 库的比较类。更多详情请访问。
***# Invoke the comparison class*
**from** **monk.compare_prototype** **import** **compare****
步骤 2:创建和管理比较实验。提供项目名称。
****gtf = compare(verbose=1);
gtf.Comparison("Comparison-1");****
这将按照以下结构创建文件和目录
**workspace
|
|--------comparison
|
|
|-----Compare-1
|
|------stats_best_val_acc.png
|------stats_max_gpu_usage.png
|------stats_training_time.png
|------train_accuracy.png
|------train_loss.png
|------val_accuracy.png
|------val_loss.png
|-----comparison.csv (Contains necessary details of all experiments)**
步骤 3:将实验添加到比较对象中。
- 第一个参数——项目名称。
- 第二个参数——实验名称。
***#add the models trained above.* **gtf.Add_Experiment("Task", "gluon_resnet18_v1");
gtf.Add_Experiment("Task", "gluon_resnet18_v1_train_all_layers");
gtf.Add_Experiment("Task", "gluon-resnet18_v2");
gtf.Add_Experiment("Task", "gluon-resnet34_v1");****
步骤 4:运行分析。该步骤生成七幅图像。
****gtf.Generate_Statistics();****
步骤 5:可视化研究比较指标。
- 训练准确度曲线
****from IPython.display import Image
Image(filename="workspace/comparison/Comparison-1/train_accuracy.png")****
- 训练损失曲线
****Image(filename="workspace/comparison/Comparison-1/train_loss.png")****
- 验证准确度曲线
****Image(filename="workspace/comparison/Comparison-1/val_accuracy.png")****
- 验证损失曲线
****Image(filename="workspace/comparison/Comparison-1/val_loss.png")****
- 培训时间被占用
****Image(filename="workspace/comparison/Comparison-1/stats_training_time.png")****
- 最大 GPU 使用率
****Image(filename="workspace/comparison/Comparison-1/stats_max_gpu_usage.png")****
- 最佳验证准确性
****Image(filename="workspace/comparison/Comparison-1/stats_best_val_acc.png")****
结论
- Monk library 让学生、研究人员和竞争对手可以非常轻松地创建深度学习模型,并尝试不同的超参数调整来提高模型的准确性。
- 预先训练的模型可以直接下载使用,而无需进入模型创建部分。
- 从上面的图表中,我们可以看到,在这个数据集中,所有层都是可训练的模型比其他模型更准确。但这并不总是正确的,所以直到明智的尝试两者。
- 此外,与仅最后一层可训练的模型相比,所有层可训练时所花费的时间大约多 40%。
感谢阅读。请给出你的建议。
Rohit 是一个 BTech。最后一年学生。他有机器学习和深度学习的知识。他有兴趣在人工智能和人工智能领域工作。目前,他在 Tessellate Imaging 担任计算机视觉实习生。在 LinkedIn 上和他联系。
使用张量流的图像分类器
原文:https://towardsdatascience.com/image-classifier-using-tensorflow-a8506dc21d04?source=collection_archive---------56-----------------------
如何创建图像分类器的分步指南
杰瑞米·托马斯在 Unsplash 上拍照
如果你不知道卷积神经网络是如何工作的,看看我下面的博客,它解释了 CNN 中的层和它的目的
[## 卷积神经网络简介
关于卷积神经网络如何工作的直觉
medium.com](https://medium.com/dataseries/introduction-to-convolutional-neural-networks-5a227f61dd50)
我们将在猫狗数据集上工作,这意味着我们的网络将预测提供的图像是猫还是狗。您可以在下面找到数据集和代码的链接
[## 猫和狗
训练 DL 模型的猫狗数据集
www.kaggle.com](https://www.kaggle.com/tongpython/cat-and-dog) [## ajaymuktha/深度学习
这个知识库包含了神经网络算法——ajaymuktha/deep learning
github.com](https://github.com/ajaymuktha/DeepLearning/blob/master/Convolutional Neural Networks/cnn.py)
为了便于计算,我从数据集中提取了大约 10,000 张图像,制作了一个验证文件夹,并放置了一些猫和狗的随机图像,以测试训练后的分类器。
数据集包含两个文件夹 Train 和 T est。训练文件夹包含两个文件夹,由用于训练的猫和狗的图像组成。测试文件夹也由两个文件夹组成,这两个文件夹由用于评估模型的猫和狗的图像组成。训练集由 8000 幅猫狗图像组成,测试集由 2000 幅猫狗图像组成
安装 Tensorflow
如果你的 PC 或笔记本电脑没有 GPU,你可以使用 Google Colab,或者使用 Jupyter Notebook。如果您使用您的系统,请升级 pip,然后安装 TensorFlow,如下所示
tensorflow.org
导入依赖项
在上面几行代码中,我只是导入了这个过程中需要的所有库
数据再处理
训练集的预处理
我们将在训练图像上应用一些变换以避免过度拟合,如果我们不执行,我们将在训练集和测试集的准确性之间得到很大的差异。
我们执行一些几何变换,如水平翻转图像,垂直翻转,放大,缩小和许多其他可以执行的操作,我们应用它,这样我们的模型就不会过度学习我们的训练图像。我们使用 ImageDataGenerator 类执行上述方法。
train_datagen 是 ImageDataGenerator 类的实例,它将对训练集中的图像应用所有变换,rescale 方法将对图像中的所有像素应用特征缩放,使其在 0 到 1 的范围内。现在我们需要将它连接到我们的训练数据集。
flow_from_directory 将把图像增强过程连接到我们的训练集。我们需要提到我们的训练集的路径。目标尺寸是应该输入神经网络的图像尺寸。批量大小被定义为一批中图像的数量,类模式将是二进制的,因为我们只有两个输出,要么是猫,要么是狗。
预处理测试集
我们不为测试集应用变换,因为我们只使用它们来评估,对我们的测试集做的唯一任务是重新缩放图像,因为在训练部分,我们定义了可以馈入网络的图像的目标大小。
现在我们定义从目录中导入测试图像的测试集。我们定义的批量大小、目标大小和班级模式与训练集中提到的相同。
照片由文森特·所罗门在 Unsplash 上拍摄
构建 CNN
卷积神经网络是一系列连续的层,所以这里我们用一个连续的类来定义它
卷积层
我们使用添加一个方法来添加层,首先我们添加卷积层 Conv2D 类,我们需要在其中传递三个参数
filters →图像需要应用的特征检测器的数量,也称为内核。
内核大小 →需要根据行数和列数为您的特征检测器定义一个内核大小。
input_shape →每当我们在任何深度学习模型中添加第一层时,我们都需要指定 input_shape,因为我们在处理图像时需要提供我们上面在训练集中提到的目标大小,3 是指 RGB 通道。
激活 _ 功能 →我们需要指定触发机制的激活功能。
联营
这里,我们考虑最大池类,我们需要指定两个参数
pool_size →这是我们为卷积层中获得的特征检测器申请的帧的大小。
步幅→帧在所有四个方向上应该移动的量
现在,我们将第二个卷积层应用于我们的网络
变平
这里,我们将所有卷积和最大池层转换为一维,这将成为全连接层的输入。
全连接层
我们传递到稠密层的参数是作为单元的隐藏神经元的数量,我们使用的激活函数是 relu。
输出层
输出单元的数量将是 1,因为我们将只预测一个类,并且激活函数将是 sigmoid 激活函数。
训练 CNN
编译 CNN
我们考虑采用两个参数的编译方法,一个是编译器 adam,一个是损失函数 binary_crossentropy,我们选择度量作为精确度。
训练 CNN
我们将使用 fit 方法通过训练集进行训练,并使用测试集来评估模型。我们将运行 25 个时期。
预言;预测;预告
让我们通过拍摄一个随机图像来进行预测,为此,我们导入基本的库 NumPy …和 image 来执行图像操作。我们使用 load_img 函数来加载 PIL 格式的图像,我们提到了测试图像的路径,并且它应该与训练图像具有相同的大小。然后,我们使用 img_array 函数将图像转换为模型期望的数组进行预处理。
我们的 CNN 模型被训练成一批图像,而不是一个单独的图像,所以我们需要增加一个额外的维度。我们将增加一个额外的维度来访问单独的图像。我们使用 NumPy 中的 expand_dim 函数来实现。
我们对测试图像使用 predict 方法,还从批量大小中访问图像的标签,并使用 if-else 条件来检查图像是猫还是狗。
这就是现在,希望你喜欢这个关于猫和狗分类的教程,在我的下一个博客中,我将向你解释递归神经网络的重要性,并从头实现它的一个应用。
如果你想知道人工神经网络如何与应用程序一起工作,请查看我下面的博客:
[## 人工神经网络导论
你深度学习的第一步
towardsdatascience.com](/introduction-to-artificial-neural-networks-ac338f4154e5) [## 用 Tensorflow 构建人工神经网络
如何使用 TensorFlow 构建神经网络的分步教程
towardsdatascience.com](/building-an-ann-with-tensorflow-ec9652a7ddd4)
用 PyTorch 实现图像聚类
原文:https://towardsdatascience.com/image-clustering-implementation-with-pytorch-587af1d14123?source=collection_archive---------6-----------------------
报纸上的蘑菇,作者图片
用于蘑菇照片聚类的深度卷积神经网络的逐行教程实现
使用深度卷积神经网络(DCNN)的监督图像分类现在是一个既定的过程。通过预训练的模板模型加上微调优化,许多有意义的应用都可以获得非常高的精确度,例如最近对医学图像的研究,该研究使用模板 Inception v3 模型,对日常物体的图像进行预训练,在前列腺癌诊断上达到 99.7%的精确度。
对于无监督的图像机器学习来说,目前的技术水平远没有解决。
聚类是无监督机器学习的一种形式,其中项目的集合——在这种情况下是图像——根据数据集合本身中的某种结构进行分组。最终出现在同一聚类中的图像应该比不同聚类中的图像更相似。
图像数据可能很复杂——变化的背景、视野中的多个对象——所以一对图像比另一对图像更相似意味着什么并不明显。没有一个基础事实标签,通常不清楚是什么让一种聚类方法比另一种更好。
一方面,无监督的问题因此比有监督的问题更模糊。优化没有既定的正确答案。另一方面,最有趣的东西是从模糊的问题、假设的产生、问题的发现、修补中产生的。为数据和分析工作流的这些领域提供新功能的工具值得我们花费时间和精力。
我将描述一种最近的图像聚类方法的实现(庄等 自 2019 年起 局部聚集)。这是近年来发表的许多可能的 DCNN 聚类技术之一。
我使用 PyTorch 库来展示这个方法是如何实现的,并在整篇文章中提供了几个详细的代码片段。完整的代码可在回购中获得。
尽管图像聚类方法不像它们的监督兄弟一样在标准库中容易获得,PyTorch 仍然能够顺利实现这个非常复杂的方法。因此,我能够探索、测试和轻轻戳一下 DCNNs 在应用于集群任务时能做什么这个谜一样的问题。
我的目标是展示如何从几个概念和方程出发,使用 PyTorch 得出可以在计算机上运行的非常具体的东西,并指导进一步的创新和对任何任务的修补。
我将把这个应用到真菌的图像上。为什么是真菌?你以后会明白的。
但是首先…实现一个 VGG 自动编码器
在讨论聚类方法之前,我将实现一个自动编码器(AE)。AEs 有各种各样的应用,包括降维,而且本身也很有趣。它们在图像聚类中的作用将在后面变得清楚。
用 PyTorch 库实现基本 AEs 并不困难(参见的和的中的两个例子)。我将实现特定的 AE 架构,这是 SegNet 方法的一部分,它建立在 VGG 模板卷积网络之上。VGG 定义了一种架构,最初是为监督影像分类而开发的。
AE 的架构如下图所示。
作者图片
图像自动编码的步骤是:
- 输入图像(左上)通过以下方式处理
- 一个编码器,由带归一化和 ReLU 激活的卷积层(绿色)和最大池层(紫色)组成,直到
- 获得一个较低维度的代码,然后由
- 一个解码器,由带归一化和 ReLU 激活的转置卷积层(浅绿色)和非卷积层(浅紫色)加上一个不带归一化或激活的最终卷积层(黄色)组成,直到
- 获得与输入尺寸相同的输出图像。
是时候将这种设计写入代码了。
我首先创建一个编码器模块。第一行,包括初始化方法,如下所示:
编码器的架构与 VGG-16 卷积网络的特征提取层相同。因此,该部分在 PyTorch 库,torchvision.models.vgg16_bn
中很容易获得,参见代码片段中的第 19 行。
与 VGG 的规范应用不同,代码是而不是进入分类层的。最后两层vgg.classifier
和vgg.avgpool
因此被丢弃。
编码器的层需要一次调整。在解码器的非池化层中,来自编码器的最大池化层的池化索引必须可用,这在前面的图像中用虚线箭头表示。VGG-16 的模板版本不生成这些指数。然而,可以重新初始化池层来实现这一点。这就是EncoderVGG
模块的_encodify
方法所要完成的。
由于这是一个 PyTorch 模块(从nn.Module
继承而来),所以需要一个forward
方法通过EncoderVGG
的实例来实现小批量图像数据的向前传递:
该方法按顺序执行编码器中的每一层,并在创建池索引时收集它们。在执行编码器模块之后,代码连同一个有序的池索引集合一起被返回。
接下来是解码器。
这是 VGG-16 网络的“转置”版本。我使用惊吓引号,因为解码器层看起来很像反向的编码器,但严格来说,它不是一个逆或转置。
解码器模块的初始化有点复杂:
_invert_
方法在编码器的层上反向迭代。
编码器中的卷积(图像中的绿色)被解码器中相应的转置卷积(图像中的浅绿色)替换。nn.ConvTranspose2d
是 PyTorch 中的库模块,它对数据进行上采样,而不是像更广为人知的卷积运算那样进行下采样。更多解释见此处。
编码器中的 max-pooling(紫色)被相应的 unpooling(浅紫色)替换,或者nn.MaxUnpool2d
表示 PyTorch 库模块。
解码器forward
是:
代码是输入,还有编码器创建的池索引列表。每当执行非池化层时,池化索引一次取一个,相反。这样,关于编码器如何执行最大池化的信息被传送到解码器。
因此,在镜像编码器层的转置层之后,forward
的输出是与输入到编码器的图像的张量形状相同的张量。
完整的自动编码器模块实现为编码器和解码器实例的基本组合:
产生与相应输入非常相似的输出的 AE 的一组参数是一组好的参数。我使用 AE 的输入和输出之间每个像素的每个通道的均方误差来量化这一点,作为目标函数,或 PyTorch 库中的nn.MSELoss
。
使用定义的 AE 模型加上可微分的目标函数,PyTorch 的强大工具被部署用于反向传播,以获得梯度,随后是网络参数优化。培训是如何实施的我就不赘述了(好奇的读者可以看看回购中的ae_learner.py
)。
编码器按特征压缩图像,并且是聚类的起点
在训练 AE 之后,它包含可以在较低维度中近似表示图像数据集的重现的较高级特征的编码器。对于真菌的图像数据集,这些特征可以是在几个蘑菇图像之间共享的形状、边界和颜色。换句话说,编码器体现了蘑菇形加上典型背景的紧凑表示。
因此,就这些高级特征而言非常相似的两幅图像应该对应于比任何一对随机码都更接近的码,例如通过欧几里德距离或余弦相似性来测量。
另一方面,图像向低维的压缩是高度非线性的。因此,大于某个相当小的阈值的两个代码之间的距离被认为对相应的图像没有什么影响。这对于创建定义明确、清晰的集群来说并不理想。
作为 AE 一部分的编码器是一个起点。编码器接下来将被改进,通过利用学习到的蘑菇状和来创建代码,从而将图像压缩成代码,这些代码也形成固有的良好集群。
关于局部聚集损失的几个词和方程
局部聚集 (LA)方法定义了一个目标函数来量化一组代码的聚集程度。目标函数不像有监督的机器学习方法那样直接引用关于图像内容的基本事实标签。更确切地说,目标函数量化了编码图像数据本质上如何服从明确定义的聚类。
以这种方式获得的明确定义的聚类应该创建有意义的聚类,这不是不言而喻的,也就是说,看起来相似的图像通常是同一聚类的一部分。这就是为什么需要实现和测试。
首先,从 LA 出版物中获得一些关于实现什么的定义。
洛杉矶的集群目标是:
该方程中的 xᵢ 为图像张量, θ 表示编码器的参数。右手边的 vᵢ 是对应 xᵢ 的代码。两组 Cᵢ 和 Bᵢ 由集合中其他图像的编码组成,分别命名为近邻和背景近邻到 vᵢ 。
对于一组代码 A ,概率 P 被定义为:
换句话说,指数势定义了概率,其中一个代码 vⱼ 贡献的概率密度越大,与 vᵢ 的点积越大。因此,由与 vᵢ 相似(在点积意义上)的大部分其他代码组成的集合 A 定义了 vᵢ 可能是其成员的集群。
标量τ被称为温度,并且定义了点积相似性的标度。
对于给定的真菌图像集合, {xᵢ} ,目标是找到使集合的聚类目标最小化的参数 θ 。洛杉矶论文的作者提出了为什么这个目标有意义的论点。我不会在这里重复这个论点。简而言之,与该集群的互补码相比,分配给一个集群的代码越清晰,该集群目标的值就越低。
如何将 LA 目标实现为自定义损失函数?
在上面关于 AE 的部分中,描述了自定义编码器模块。缺少的是 LA 的目标函数,因为它不是 PyTorch 中库损失函数的一部分。
需要实现自定义损失函数模块。
损失函数模块的初始化将初始化多个 scikit-learn 库函数,这些库函数需要在forward
方法中定义背景和近邻集合。
NearestNeighbors
实例提供了计算数据点最近邻的有效方法。这将用于定义集合 B 。KMeans
实例提供了计算数据点集群的有效方法。这些将用于定义集合 C 。一旦处理了模块的执行,这将变得更加清楚。
说到这里:LocalAggregationLoss
的必用forward
法。
正向方法接受当前版本的编码器产生的小批量代码,加上所述代码在完整数据集中的索引。由于在创建小批量时通常会打乱数据,因此索引可以是一个不连续的整数列表,尽管其数量与小批量代码的大小相同(由assert
语句检查)。
forward
有两个主要部分。首先,评估相邻集 B、C 及其交集。第二,为给定的一批代码和集合计算概率密度,然后将概率密度聚集成如上定义的 LA 群集目标函数的对数概率的比率。
《前进》中的“记忆库”是什么?
LA 的创造者们采用了一种记忆库的技巧,他们将其归因于吴等人的另一篇论文。这是处理 LA 目标函数的梯度依赖于数据集的所有代码的梯度的一种方式。
所述函数的适当梯度将必须计算如下各项:
右手边所有代码的总和意味着必须计算大量的张量,并且为了反向传播而一直保存这些张量。迭代小批量图像对效率没有帮助,因为无论如何都必须计算代码相对于解码器参数的复杂梯度。
因为聚类的质量将一个图像与数据集的所有其他图像联系起来,而不是一个固定的基本事实标签,所以这种纠结是可以理解的。
记忆库技巧相当于将当前小批量中的代码之外的其他代码视为常量。因此,与其他代码的衍生物的纠缠消失了。只要近似的梯度足够好以引导优化朝向最小值,这就是有用的近似。
memory bank 类实现为:
它由与要聚类的数据集相同维数和相同数量的单元数据向量组成(由 Marsaglia 的方法在超球面上统一初始化)。因此,涉及具有生成 512 维代码的编码器的一千个图像的任务,意味着在 512 维的实坐标向量空间中的一千个单位向量的存储体。一旦一组新的向量连同相应的索引被提供给存储体,存储器就以某种混合速率memory_mixing_rate
被更新。该类还包含一个方便的方法,用于将整数索引集合转换为整个数据集的布尔掩码。
请注意,记忆库只处理数字。记忆库无法连接到 PyTorch 张量的反向传播机器。存储体被更新,但是通过运行平均值,而不是直接作为反向传播的一部分。
它是存储在LocalAggregationLoss
的memory_bank
属性中的MemoryBank
的一个实例。
背景邻居和近邻集合是如何创建的?
又回到了LocalAggregationLoss
的forward
法。我使用先前初始化的 scikit-learn 类实现了邻居集的创建。
_nearest_neighbours
和_intersecter
相当简单。前者依赖于寻找最近邻居的方法。它会考虑存储体中的所有数据点。
_close_grouper
对存储体中的数据点进行多次聚类。作为感兴趣点 vᵢ的相同聚类的一部分的那些数据点定义了近邻集合 Cᵢ.洛杉矶论文的作者鼓励使用多重聚类运行,其中聚类包含随机成分,因此通过执行多重运行,他们消除了噪声。
举例来说,下图中的红点是其他代码海洋中感兴趣的代码。记忆库当前状态的聚类将兴趣点放在其他点的聚类中(中间图像中的绿色)。最近邻定义了另一组相关数据点(右图中的紫色部分)。_nearest_neighbours
和_close_grouper
为小批量中的每个代码创建这两个集合,并将集合表示为布尔掩码。
作者图片
计算概率密度,使得 PyTorch 反向传播机器可以计算梯度
对于批中每个代码 vᵢ的两个集合(Bᵢ和 Bᵢ与 Cᵢ相交),是时候计算概率密度了。这个密度应该也可以用 PyTorch 方法来区分。
它的实现方式如下:
在第 14–16 行中,所有不同的点积都是在小批量的代码和存储体子集之间计算的。np.compress
将掩码应用于存储体向量。
torch.matmul
计算所有的点积,将小批量维度考虑在内。还要注意张量codes
包含了编码器数学运算的记录。因此,随着额外 PyTorch 操作的执行,该记录被扩展,最终,这使得 PyTorch 的反向传播机制、autograd
能够相对于编码器的所有参数来评估损耗标准的梯度。
从概念上讲,第 25–27 行也发生了同样的操作,但是在这个子句中,mini-batch 维被显式迭代。当 numpy 数组不能被广播时,这是需要的,这是粗糙数组的情况(至少目前是这样)。
将模型和损失放在一起
综上所述,类似于下面的代码可以让特定数据集、VGG 编码器和 LA 得到训练。
我在讨论中省略了如何准备数据(我放在fungidata
文件中的操作)。详情可在回购中找到。对于本讨论,将dataloader
视为返回真菌inputs['image']
的小批量图像以及它们在更大数据集inputs['idx']
内的相应索引就足够了。用来自作为自动编码器一部分的预训练编码器的标准化代码初始化存储体代码。
训练循环是功能性的,虽然被简化了,细节见la_learner
文件,虽然没有什么不寻常的被使用。
我使用稍微修改过的编码器版本,EncoderVGGMerged
。它是EncoderVGG
的子类。
这个类在编码器的结尾附加了一个应用于代码的合并层,所以它是一个一维向量。
我展示了应用于一个 RGB 64x64 图像作为输入的聚类的编码器模型。
作者图片
接下来,我展示了创建output
和loss
变量的模型的一个小批量图像的向前传递。
作者图片
图中的 LALoss 模块与存储体交互,考虑了小批量图像在大小为 N 的总数据集中的索引。它构建存储体的当前状态的聚类和最近邻,并将小批量代码与这些子集相关联。
backward
通道执行反向传播,从 LA 标准的损耗输出开始,然后遵循涉及代码的数学运算,通过链式法则,获得 LA 目标函数相对于编码器参数的近似梯度。
关于真菌图像的一句话
我将把这种方法应用于真菌的图像。我的理由:
- 我使用的软件库不是为这个特定任务开发或预先训练的。我希望测试使用通用库工具处理特定图像任务的场景。
- 真菌的外观在形状、颜色、大小、光泽、结构细节以及它们的典型背景(秋叶、绿色苔藓、土壤、采摘者的手)方面各不相同。信号和噪声都是变化的。
- 真菌图像位于明显物体和图像之间的最佳位置,人类凭直觉识别这些明显物体的原因我们很少能清楚地表达出来(例如狗、猫和汽车),而图像的信息内容需要深厚的领域专业知识才能掌握(例如肿瘤活检、锂电极形态学)。我相信这有助于理解方法。
- 丹麦真菌学会(2016 )提供了非常好的带注释的众包开放数据。(丹麦真菌记录数据库,由 Frø slev,t .,Heilmann-Clausen,J .,Lange,c .,ssø,t .,Petersen,J.H .,Sø chting,u .,Jeppesen,T.S .,Vesterholt,J。,在线www . svampeaatlas . dk)。
作为一个额外的奖励,真菌的生物学和文化是显著的——一个有趣的文化组成部分是决策启发法如何在蘑菇觅食者中进化,以便在可食用和致命之间导航。我可以想象一些非常有趣的机器学习的测试案例,这些测试案例基于从真菌照片中创建的图像数据。
数据库中的三幅图像如下所示。
从左到右:桌上的姬松茸(王子);叶间三条鸡油菌(金鸡油菌);草中鹅膏睫毛膏(飞木耳)。图片来自丹麦真菌记录数据库(见上文)。
说明性测试运行和探索
LA 的一个缺点是它涉及几个超参数。遗憾的是,我没有足够的 GPU 待命,所以我必须将自己限制在超参数和真菌图像选择的许多可能变化中的极少数。
我在这篇文章中的重点是概念和方程的实现(加上一个真菌图像数据的插件)。因此,我在这里追求例证和灵感,我将把进一步的结论留给高层次的观察。
我在鸡油菌和木耳蘑菇上训练 AE,裁剪为 224x224。利用随机梯度下降优化器,AE 最终收敛,尽管对于某些优化参数,训练陷入次优。经过训练的 AE 的输入和输出的一个例子如下所示。
作者图片
保真度有明显的损失,尤其是在周围的草地中,尽管在解码输出中清晰的红色帽子被粗略地恢复。
以 AE 的编码器为起点,针对 LA 物镜进一步优化编码器。使用相同的一组蘑菇形图像,温度为 0.07,混合率为 0.5(如在原始论文中),并且聚类的数量设置为要聚类的图像数量的大约十分之一。因为我的图像数据集相当小,所以我设置背景邻居来包括数据集中的所有图像。具有 LA 目标的编码器的训练最终收敛。
下面显示了一组说明性的图像:
来自丹麦真菌记录数据库的图片。
这是直观的,明确的白点帽飞木耳集群。但是,该群集也包含外观差异很大的图像。和检查其他集群,白点飞木耳帽偶尔出现在其他集群。
下面显示了另一个说明性的集群。
图片来自丹麦真菌记录数据库。
这些图像有一些共同之处,将它们与典型的图像区分开来:较暗的颜色,主要来自背景中的棕色树叶,尽管右下角较暗的蘑菇(黑色鸡油菌或黑色小号)很突出。
但同样,符合这一粗略标准的图像也出现在其他聚类中,这表明还有其他非线性关系编码,这使得上述图像对应于相对接近和不同的代码,而其他图像则没有。可解释性比平常更难。
我还注意到许多集群只包含一个图像。改变进入 k-means 聚类的聚类质心的数量会对此产生影响,但随后也会出现非常大的图像聚类,对于这些图像聚类,很难提供对共有特征的直观解释。
这些是其他运行生成的说明性结果。至少在我这里进行的几次有限的运行中,LA 的最小化创建了最多适度对应的图像簇,至少在我看来是自然的分组。
鉴于深度神经网络的灵活性,我希望有很多方法可以将图像压缩到清晰的聚类中,就我的眼睛而言,不能保证这些方法体现了有用的意义。与基础事实标签的情况不同,在基础事实标签的情况下,神经网络的灵活性在优化之前被引导向我们定义为有用的目标,优化器在这里可以自由地找到要利用的特征,以使聚类质量高。
也许需要一个不同的归纳偏差来更好地限制灵活性的部署,以便最小化 LA 目标函数?就我的视觉认知而言,也许 LA 目标函数应该与一个附加目标相结合,以防止它偏离一些可感知的范围?也许我应该使用标准化的图像,如某些医学图像、护照照片或固定透视相机,将图像中的变化限制在较少的高级特征上,这样编码就可以在聚类中利用这些特征?或者,我所担心的问题的真正答案可能是在这个问题上投入更多的 GPU,并找出超参数的完美组合?
当然都是猜测。不过,多亏了 PyTorch,从概念和方程到原型和创作的道路上的障碍比固定的模板解决方案要低。
脚注
- 常规的警告:我对 LA 的实现旨在与原始出版物一样,但是误解或错误的可能性永远不可能完全为零。
- 我没有花费任何精力来优化实现。我可能忽略了 PyTorch 和/或 NumPy 技巧,它们可以提高 CPU 或 GPU 的速度。
使用卷积自动编码器的图像彩色化
原文:https://towardsdatascience.com/image-colorization-using-convolutional-autoencoders-fdabc1cb1dbe?source=collection_archive---------8-----------------------
来源: unsplash
使用 Python 中的深度学习对来自老派视频游戏的图像进行着色的案例研究
最近,我完成了 Udacity 的机器学习工程师 Nanodegree 的顶点项目。因为我知道这个项目需要花费大量的时间和精力,所以我想做一些我真正感兴趣的事情。很长一段时间以来,我一直打算熟悉计算机视觉的一个领域,即图像彩色化。我是(从我记事起就是)一个电子游戏的狂热爱好者,这就是为什么在这个项目中,我想做一些贴近我内心的东西。
最近,我在互联网上看到一个的帖子,显示通过使用深度学习可以提高仿真视频游戏的质量。所谓仿真,我指的是在一个不同于游戏最初设计的系统上使用专用软件运行游戏。一个例子可能是在 PC 上玩任天堂 64 游戏。通过在仿真器软件中嵌入预先训练的神经网络,可以将分辨率升级到 4K 或者增加纹理的质量/清晰度。真正令人惊讶的是,这些解决方案适用于所有游戏,而不仅仅是他们直接接受训练的一两个游戏。
当然,对于顶点来说,做这么大规模的项目太过雄心勃勃,所以我不得不把它缩小一点。当我开始我的视频游戏冒险时,我玩的是 Game Boy Color,除了新的彩色游戏外,它也适用于上一代产品——灰度游戏男孩。从那个时代获得灵感,我试图使用深度学习来给灰度图像着色,因此模拟器(结合在许多类似游戏上训练的网络)可能会接近实时地给输出着色。
来源: unsplash
Nanodegree 专注于在使用 AWS 基础设施(SageMaker)的同时使用 PyTorch 构建和部署模型,但是本文中描述的所有模型也可以在本地或使用 Google Colab 进行训练。
在这篇文章中,我描述了我解决着色问题的方法,以及为了完成这个项目我必须采取的步骤。由于代码库相当长,我不会在文章中包含代码,而是让您参考包含复制项目所需的所有文件的 GitHub 库。
1.方法学
在展示实际的实现之前,我想对我在项目中遵循的方法提供一个高层次的概述。这就是为什么我认为首先熟悉以下概念是有意义的。
Lab 色彩空间
在我的上一篇文章中,我提供了用 Python 处理图像和不同颜色空间的快速介绍。我强烈建议在继续之前快速阅读,因为这将使理解一切变得更容易。为了以防万一,我也将在这里快速回顾一下。
到目前为止,最流行的图像表现方式是 RGB 。但是也有不同的方法可用,其中之一是 Lab 颜色空间(也称为 CIELAB)。
简而言之, Lab 颜色空间将颜色表示为三个值:
- L :从 0(黑色)到 100(白色)范围内的亮度,这实际上是一幅灰度图像——它不完全等同于将 RGB 转换为灰度,但已经足够接近了
- a :绿-红色谱,数值范围为-128(绿色)~ 127(红色)
- b :蓝黄色光谱,数值范围为-128(蓝色)~ 127(黄色)
从上面的描述中我们可以得出的直接结论是,通过使用 Lab 颜色空间,我们可以同时解决两个问题——我们将灰度图像作为输入,颜色通道的复杂性从三个减少到两个(与 RGB 相比)。
自动编码器
自动编码器是一种神经网络架构,类似于主成分分析等技术,它们都试图降低输入数据的维度。然而,正如我们将看到的,自动编码器可以做得更多。它们由两部分组成:
- 编码器-将输入数据转换为低维表示(也称为潜在向量/表示)。为了实现这个目标,编码器必须只学习数据中最重要的特征。
- 解码器—从低维表示中恢复输入。
下图显示了自动编码器网络的基本架构。
自动编码器的基本模式。来源:维基百科
在这个项目中,我训练自动编码器最小化损失函数,这是输入数据和解码输出之间的差异的度量。通常,潜在表示可以用作提取的特征,类似于来自 PCA 的主成分。然而,在我们的例子中,我们感兴趣的是解码输出。
训练自动编码器时要注意的一件重要事情是,当潜在表示大于输入数据时,它们有记住输入的倾向。
卷积神经网络
卷积层的目标是通过应用卷积滤波来提取潜在的特征。卷积层读取输入(例如 2D 图像)并将(特定形状的)内核拖到图像上。内核表示我们想要在图像中定位的特征。对于每一步,输入乘以核的值,然后对结果应用非线性激活函数。通过这种方式,原始输入图像被转换为过滤图。
来源: GitHub
卷积和池(聚合)层可以相互堆叠,以提供多层抽象。许多流行的图像分类架构都是以类似的方式构建的,如 AlexNet、VGG-16 或 ResNet。
另一方面,我强烈推荐下面的资源,用于交互式可视化卷积如何根据各种参数工作,如步幅、填充内核大小和膨胀。此外,安德烈·卡帕西的这段视频对 CNN 做了很好的介绍。
对于 capstone 项目,我将 CNN 与自动编码器结合起来,并有效地使用了一类称为卷积自动编码器的架构。这种网络的输入是灰度图像(1 个通道),而输出是表示颜色的 2 层( a / b 层的 Lab 表示)。
2.构建数据集
对于着色项目,我使用了我从小最喜欢的游戏之一— Wario Land 3 。为了获得数据集,我从 YouTube上截取了一段视频。我找到了一个 longplay (整个游戏的完整播放,只显示实际的游戏屏幕),所以下载后,我可以很容易地从视频中提取每 x- 帧,以获得完整的彩色图像数据集。我在之前的文章中详细记录了这个过程。
来源:维基百科
视频每秒播放 24 帧,我每 58 帧提取一次。这样做总共产生了 7640 幅图像。提取的帧存储为 JPG 图像,每个图像的大小为 288x320 像素。
在对图像进行更多的技术分析之前,值得一提的是,目前收集数据的方法存在哪些潜在问题。通过从显示整个游戏的视频中提取每第 x 帧,我们还可以得到:
- 所有游戏内菜单
- 介绍/标题屏幕
- 信用
- 画面转换——在游戏中切换画面/阶段时,下一个画面/阶段加载时通常会有一个短暂的淡出画面
总之,这会给数据集带来一些噪声。这个问题的一个潜在解决方案是剪掉视频的第一个/最后一个 X 秒,以删除介绍/演职员表。为了简单起见,我没有这样做,而是将所有帧留在数据集中。
是时候更详细地描述数据集了。首先,我呈现一个提取图像的预览:
捕获的图像为 288x320
Game Boy Color 的屏幕分辨率为 144x160(因此拍摄的视频已经放大了 2 倍)。转到调色板,Game Boy Color 的系统使用 15 位 RGB 调色板(多达 32,768 种可能的颜色)。实际上,不使用任何特殊的编程技术就可以呈现多达 56 种颜色,而完整的 32,768 种颜色需要一些技巧。
当前的标准是 24 位 RGB 颜色空间,其中每个颜色通道都表示为 0 到 255 之间的数字。这种表示总共产生大约 1670 万种颜色组合。
GBC 的颜色有点“有限”是我选择它作为目标平台的原因之一,因为这种图像比新一代游戏机或真实照片的图像更容易着色。
3.数据准备
在这一部分中,我主要关注应用于数据的转换。再次重申,下载的数据被编码为 RGB 图像。因此,每个图像都被表示为一个维数为[3,288,320]的数组,其中 0-255 范围内的数字描述了给定通道的强度。正如我之前提到的,使用实验室颜色空间的想法是为了降低问题的复杂性,所以自动编码器的实际输出是一个大小为[2,288,320]的数组。
在下图中,我展示了我之前展示的图像的实验室表示:
在讨论了 Lab 色彩空间之后,我描述了准备数据的两个步骤。
培训/验证分割
对于这个项目,我做了 90-10 的训练-验证分割。验证数据不用于训练,仅用于评估每个时期后的网络性能。
预处理步骤
所有的预处理步骤都在一个定制的 PyTorch ImageFolder
中完成,以使过程尽可能简单高效。我采取的步骤是:
- 将图像裁剪为 224 x224——在我尝试的一个架构中,ResNet-18 的前几层被用作编码器。为了将更改保持在最低限度,我将所有图像裁剪为 224x224(因为这是该架构中使用的图像大小)。然而,网络的输入必须改变以接受单通道图像。为了训练,我随机截取了一些图像。对于验证,中间裁剪,保证验证样本始终相同。
- 此外,训练图像有 50%的概率被水平翻转。
- 我基于 Lab 色彩空间将 RGB 输入图像转换为两幅图像——大小为 224x224 的灰度级( L 层)输入和作为输出的 a/b 层(形状:[2,224,224])。
- 在将图像编码为 Lab 之后,我将图层标准化,类似于在[1]和[2]中所做的。我将 L 通道除以 100,因此值在[0,1]的范围内。然后,我将 a/b 通道中的值除以 128,结果在[-1,1]范围内,适合 tanh 激活函数(网络最后一次激活)。我还试验了一种不同的变换——通过先加 128 然后除以 255,将 a/b 层归一化到范围[0,1]。在这种情况下, ReLU 或 Sigmoid 激活功能适用于最后一层。然而,从经验上看,第一种方法效果更好。
上述步骤中的所有数据分割和预处理都是使用预先指定的种子完成的,以确保项目的可重复性。
4.模型和培训
在这个项目中,我训练了三个不同的模型(名为 V0,V1,V2,尽管它们不是前面模型的明确扩展),尽管我也试验了许多不同的模型。下面我提供了模型的简短描述。
v 0 型—基准
该模型的架构基于[1]中提出的架构。作为一个例子,作者用它来说明一个关于给一个图像着色的观点。我决定使用这个模型作为我的基准,因为它是我在互联网上找到的最简单的彩色自动编码器。我相信这将是一个很好的基准,类似于评估高级分类器与基本分类器(如逻辑回归或决策树)的性能。
下图总结了该模型的架构:
模型 V0 的架构
在网络中,我没有使用池层来减少输入的维数。相反,我在一些卷积层中使用了步长 2。原因是池层增加了信息密度,但同时扭曲了图像。这在图像分类任务中可能无关紧要,在这些任务中,我们只关心图像中某些特征的存在,然而,对于着色网络来说,这是有区别的。此外,我使用填充来控制图像尺寸的减小。
上采样是使用不可学习的插值完成的。在这种情况下,这是一个 2 倍的上采样(实际上是图像大小的两倍),并使用了最近邻插值算法。
模特 V1
该模型基于[1]中介绍的“beta”架构。在满足于这种架构之前,我试图一字不差地重建“beta”版本,然而,该模型根本没有学习。我怀疑潜在的表现形式太大了,模型没有学习到任何有用的特征。这就是为什么我把它的通道数缩小了 2 倍。下图展示了该架构的细节:
我没有描述该网络的更多细节,因为它类似于 V0 模型。
模特 V2
最后一个模型是对[3]中提出的模型的一个微小修改。作者将 ResNet-18 的前几层组合起来作为编码器,它从灰度图像中提取特征(网络的输入也必须被修改以接受 1 通道图像)。为了简洁起见,我没有包括详细的规格,因为图像会很大。
然后,作者使用了一个类似于我们在[1]和[2]中已经看到的解码器。摘要如下。与以前的模型相比,该解码器的不同之处在于卷积后增加了批量归一化层。
训练详情
我通过寻找和实现一些着色的自动编码器开始了这个项目,然而,我后来不得不创建一个我将坚持的框架。原因是我有效地结合了一些不同的方法,每种方法都使用了稍微不同的方式来预处理图像,应用变换等。
为了使结果具有可比性,我满足于框架的一个变体,并以类似的方式训练所有的模型。
我在途中做出的一些决定包括:
- 统一的训练方法—相同损失(MSE)、优化器(RMSprop)、学习率(0.001)、最大历元数(30)等。
- 在实验室转换的基础上增加一个额外的标准化步骤,这就是为什么我不得不将网络中的一些最终激活函数修改为 tanh
- 使用一组变换(随机裁剪、水平翻转、居中裁剪)来扩充数据集。我的项目所基于的一些例子不包括任何转换。
我试图使代码尽可能灵活,以便于实验。这就是为什么为了方便起见, Lab 图像的归一化被参数化。
评估指标
很难选择一个好的评价标准,因为颜色在很大程度上是主观的,不同的版本可以被认为是可接受的。这就是为什么经常需要人工观察的原因。在文献中([1],[3]),用于训练彩色化神经网络的流行解决方案是使用均方误差(MSE)作为损失函数。在这个项目中,我遵循了同样的方法。
MSE 损失函数带来了一些问题,这是由彩色化问题是多峰的事实引起的——单个灰度图像可能对应于许多可接受的彩色图像。这导致当模型更喜欢选择不饱和的颜色时,因为它们比明亮、鲜艳的颜色更不容易出错(这会导致高 MSE 的惩罚)。
这就是为什么研究不同的评价函数绝对是一个值得探索的领域。一些论文[4]还探索了将图像彩色化作为分类问题(而不是建议的回归)来构建的想法,并使用诸如准确度之类的度量作为损失函数。
在本文中,我给出了不同时期(15、30 和最好的一个,以防不是最后一个)所选网络架构的 MSE。此外,我还展示了彩色图像以供视觉检查。
5.结果
为了评估彩色化自动编码器的性能,我首先分别针对 3 个模型中的每一个,检查训练/验证损失在各个时期的演变。
v 0 型
在图像中,我们可以看到损失开始趋于平稳,但是,如果训练更多的时期,损失可能仍然会减少。此外,在验证损失中有两个奇怪的峰值,这是[1]的作者在使用 ADAM 优化器时也注意到的。对他来说,使用 RMSprop 优化器解决了这个问题。
型号 V1
这些模型比 V0 模型开始时损失更高,可能是由于可学习参数的数量更多。不过最终还是在 30 个纪元内取得了较好的表现。此外,我们可以观察到随着训练时间的推移,验证损失有一个较小的峰值。
模特 V2
对于最复杂的模型(就可学习参数的数量而言),上图中唯一突出的是验证损失的巨大峰值。我没有太注意它,因为它只发生了一次,其余的训练是稳定的。虽然,知道它的确切原因会很有趣。
作为下一步,我比较所有模型在第 15、30 个时期和最佳时期(如果不是最后一个时期)的验证集 MSE。
总的来说,在考虑的模型中,V1 模型的验证损失最低。此外,V1 和 V2 模型都优于基准,这是有道理的,因为它的架构简单。
最后,我展示了视觉检查的结果。这些图像来自验证集(在训练期间没有看到),并且是从各个模型的最佳时期获得的。
V2 模型设法捕捉到了宝箱周围方块的颜色,然而,却错误地给宝箱本身着色。
V1 模型在地图着色方面做得最好,V2 的图像有点“恍惚”,图像中有各种亮点。
V1 模型在捕捉背景方面做得很好,而 V2 模型正在努力决定使用哪种颜色。
所有的模型都设法捕捉到了紫色的碎片。
最后一个比较棘手,因为地图的碎片实际上接近灰度。模特 V2 试图给地图上色,而 V1 基本上保持了原样,只正确地给框架上色。
V1 和 V2 非常接近捕捉森林的绿色,而记住破碎的块是紫色的。模型还正确地将树干染成了棕色。
模特们试图给上面的图像着色,但结果并不那么完美。一个潜在的原因是,这是一种独一无二的图像,所以很难使用任何潜在的特征来获得正确的颜色。
总而言之,这些模型在给灰度图像着色方面做得相当不错。神经网络似乎已经获得了一些图案,如紫色的破块、宝箱周围的紫色瓷砖、黄色的硬币、绿色的森林等。此外,更先进的模型(V1/V2)明显优于基准。然而,V2 模型是相当不稳定的,并且经常导致明亮和不匹配的颜色。
在 GitHub repo 中,我也尝试将 Wario Land 3 上训练的模型应用于来自 Wario Land 2 的图像,然而性能相当差。原因可能是游戏的风格有点不同,而且开发者没有重用以前游戏中的很多精灵/模型——这对玩家来说当然很好:)你可以在下面看到一些例子。
6。结论
总之,我真的很喜欢在这个项目中工作,我学到了很多东西。这个项目决不是完整和详尽的。图像着色绝对不是一件容易的事情,我没有从所有的角度去探索,主要是因为时间的限制。因此,我列出了一些进一步扩展的想法:
- 应用数据清理,例如,过滤掉具有高百分比纯白色/黑色像素的图像。这可能表明这些是一些屏幕转换,将它们包含在数据集中可能会给模型带来不必要的噪声。
- 该项目可以扩展到捕获一个以上的视频游戏,可能来自同一流派(例如,像 Wario Land 这样的平台游戏),以提高性能和覆盖潜在的过度拟合。
- 使用可训练的放大层(转置卷积层)而不是不可训练的层。
- 从预先训练好的网络中进行迁移学习。
- 用甘斯做实验。
我真的希望将来回到这个项目,至少尝试一下我上面提到的一些想法。
你可以在我的 GitHub 上找到本文使用的代码。一如既往,我们欢迎任何建设性的反馈。你可以在推特或评论中联系我。
参考
[1]https://blog . Floyd hub . com/colorizing-b-w-photos-with-neural-networks/
[2]f .巴尔达萨雷、D. G .莫林和 l .罗德斯-吉拉奥(2017 年)。深度考拉化:使用 CNN 和 inception-resnet-v2 的图像彩色化。arXiv 预印本 arXiv:1712.03400。
https://lukemelas.github.io/image-colorization.html
https://ezyang.github.io/convolution-visualizer/index.html
https://github.com/vdumoulin/conv_arithmetic
基于 K-均值聚类的图像压缩
原文:https://towardsdatascience.com/image-compression-using-k-means-clustering-aa0c91bb0eeb?source=collection_archive---------8-----------------------
使用 k 均值聚类算法的图像压缩的概述和实现,以及不同 k 值的压缩图像的比较
在 Unsplash 上拍摄的 ThisisEngineering RAEng
图像压缩是一种应用于数字图像的数据压缩,不会将图像质量降低到不可接受的水平。文件大小的减小允许在给定数量的磁盘或内存空间中存储更多的图像。它还减少了通过互联网发送图像或从网页下载图像所需的时间。
图像压缩是如何工作的?[1]
I 图像压缩算法利用视觉感知和图像数据的统计特性来提供卓越的结果。有两种图像压缩方法—无损和有损。让我们快速浏览一下它们。
无损压缩:
这是一种用来减小文件大小的方法,同时保持与压缩前相同的质量。无损压缩不会损害数据的质量,并且文件可以恢复到原始形式。在这种类型的压缩中,文件的大小不会改变。用于无损压缩的算法有:
- 行程编码
- 霍夫曼编码
- 算术编码
有损压缩:
有损压缩是一种消除不明显数据的压缩方法。为了使照片更小,有损压缩会丢弃照片中不太重要的部分。压缩文件无法完全还原为原始形式。在这种类型的压缩中,数据质量会受到影响,数据大小也会发生变化。有损压缩主要用于图像、音频和视频压缩,不同的有损压缩算法有:
- 离散余弦变换
- 分形压缩
- 变换编码
我们将使用 K 均值聚类技术进行图像压缩,这是一种压缩的变换方法。使用 K 均值聚类,我们将对图像中存在的颜色进行量化,这将进一步帮助压缩图像。
什么是 K-Means 聚类?
K-Means 算法是一种基于质心的聚类技术。这种技术将数据集聚类成 k 个不同的聚类。k-means 聚类算法中的每个聚类都由其质心点表示。
左图:数据集图,右图:三均值聚类结果图,(图 1)
上图(图 1)描述了如何使用 k=3 的 k 均值聚类算法为给定数据集形成 3 个聚类。
此外,阅读本文了解更多关于 k-Means 聚类算法的知识。
[## 了解 K-means、K-means++和 K-medoids 聚类算法
了解 K-means、K-means++和 K-Medoids 聚类算法及其关系的概述。这篇文章…
towardsdatascience.com](/understanding-k-means-k-means-and-k-medoids-clustering-algorithms-ad9c9fbf47ca)
K-Means 聚类技术是如何压缩图像的?
在彩色图像中,每个像素的大小为 3 字节(RGB),其中每种颜色的强度值可以从 0 到 255。根据组合学,可以表示的颜色总数是 256256256(等于 16,777,216)。实际上,我们在一幅图像中只能看到比上述数字少得多的几种颜色。因此 k-Means 聚类算法利用了人眼的视觉感知,并且使用很少的颜色来表示图像。具有不同强度值(RGB 值)的颜色对于人眼来说似乎是相同的。K-Means 算法利用了这一优势,并剔除了相似的颜色(在一个聚类中靠得很近)。下面是这种工作方式的一个例子:
从输入图像中选择一些像素,(图像 2)
在上面的图像(图像 2)中,选取了几个像素,并在后续图像中进行了扩展,以继续说明。
左:上面拾取的像素的最大化图像,右:两个附近的像素 x 和 y ,(图像 3)
从输入图像(图像 2)中拾取的像素在(图像 3)的左侧部分被扩展。在(图 3)的右侧,选取了两个邻近的像素(名称为“x”和“y”)。
如果“x”和“y”像素的 RGB 值分别为(130,131,140)和(127,132,137 ),那么下面是人眼如何看到这些两像素颜色的图示。下图显示的是从 w3schools 得到的 RGB 色彩强度。
上图:颜色为 RGB(130,131,140)下图:颜色为 RGB(127,132,137)(图 4)
在上面的图像(图像 4)中,观察到对于 RGB 值的一些变化,颜色对于人眼来说是相似的。因此,k-Means 聚类可以将这两种颜色结合在一起,并可以用一个与人眼几乎相同的质心点来表示。
图像的初始尺寸是 7501000 像素。对于每个像素,图像具有表示 RGB 强度值的三维。RGB 亮度值的范围从 0 到 255。由于亮度值有 256 个值(2 * 8),所以存储每个像素值所需的存储量是 38 位。
最后,图像的初始大小为(75010003*8)比特。
颜色组合的总数等于(256256256)(等于 16,777,216)。由于人眼无法一次感知如此多的颜色,所以我们的想法是将相似的颜色组合在一起,用更少的颜色来表现图像。
我们将使用 k-Means 聚类来寻找 k 个颜色,这些颜色将代表其相似的颜色。这些 k 色将是算法中的质心点。然后我们将每个像素值替换为它的质心点。与总的颜色组合相比,仅使用 k 值形成的颜色组合将非常少。我们将尝试不同的 k 值,并观察输出图像。
如果 k=64,则输出图像的最终大小将是(75010006 + 6438)位,因为强度值的范围是 2**6。
如果 k=128,则输出图像的最终大小将是(75010007 + 12838)位,因为强度值的范围是 2**7。
因此,观察到图像的最终尺寸在很大程度上比原始图像减小。
实施:
(作者代码)
实施的逐步演练:
- 图像输入(第 6-8 行) : 从磁盘加载图像。
- 重塑输入图像(第 15 行) : 输入图像的大小为(rows,cols,3),将所有像素值扁平化为大小为(rowscols)的单一维度,每个像素的维度为 3 代表 RGB 值。展平图像的大小将为(rowscols,3)。
- 聚类(第 18–19 行) : 实现 k-Means 聚类算法,找到代表其周围颜色组合的 k-形心点。
- 将每个像素替换为其质心点(第 22–23 行) : (行*列)数量的像素的所有颜色组合现在由其质心点表示。将每个像素的值替换为其质心点。
- 重塑压缩图像(第 26 行) : 将(rows*cols,3)维度的压缩图像重塑为原始(rows,cols,3)维度。
- 输出压缩图像(第 29–31 行) : 显示输出图像并保存到磁盘。
结果和观察:
原图:
原始图像
不同“k”值的压缩图像:
以上 8 幅图像描述了不同 k 值的压缩图像的结果
以下是所有输入和输出图像的详细信息:
输入图像( original_image.png )和输出压缩图像( compressed_image_k.png )的细节
从上图中观察到:
- 所有压缩图像( compressed_image_k.png )的尺寸与输入图像( original_image.png )的尺寸相同。
- 压缩图像的尺寸随着 k 的减小而减小。
- 对于 k=32,64,128,256 的值,输出的压缩图像看起来相当好并且失去颜色,并且人眼不可见。与 k=32 的原始图像相比,压缩图像的大小减小了几乎 3 倍。
- 对于 k=16,8 的值,输出的压缩图像丢失了许多颜色,并且有损压缩对于人眼是可见的。
- 对于 k=4,2 的值,输出的压缩图像丢失了几乎所有的颜色,并且图像的内容也丢失了。
参考文献:
[1]keycdn.com,(2018 . 11 . 21),什么是图像压缩?htps://www . key dn . com/support/what-is-image-compression
感谢您的阅读!
图像数据标签和注释—您需要知道的一切
原文:https://towardsdatascience.com/image-data-labelling-and-annotation-everything-you-need-to-know-86ede6c684b1?source=collection_archive---------1-----------------------
了解不同类型的注释、注释格式和注释工具
贴有标签的蓝莓瓶(照片由王思然·哈德森在 Unsplash 上拍摄)
数据标记是监督机器学习任务中的一个重要步骤。 Garbage In Garbage Out 是机器学习社区常用的一个短语,意思是训练数据的质量决定了模型的质量。用于数据标记的注释也是如此。如果你给一个孩子看一个西红柿,并说这是一个土豆,下一次孩子看到一个西红柿,很可能他会把它归类为土豆。正如机器学习模型以类似的方式学习一样,通过查看示例,模型的结果取决于我们在训练阶段输入的标签。
数据标记是一项需要大量手工工作的任务。如果你能为你的项目找到一个好的开放数据集,那就是标签,运气在你这边!但大多数情况下,情况并非如此。很有可能要自己去经历数据标注的过程。
在本帖中,我们将探讨图像的注释类型、常用的注释格式以及一些可用于图像数据标注的工具。
图像注释类型
在开始图像注释之前,了解现有的不同注释类型是很有用的,这样您就可以为您的用例选择正确的类型。
以下是几种不同类型的注释:
包围盒:包围盒是计算机视觉中最常用的注释类型。边界框是用于定义目标对象位置的矩形框。它们可以由矩形左上角的𝑥和𝑦轴坐标以及右下角的𝑥和𝑦轴坐标来确定。边界框通常用于对象检测和定位任务。
被检测汽车的边界框(原始照片由 Patricia Jekki 在 Unsplash 上拍摄)
边界框通常由两个坐标(x1,y1)和(x2,y2)表示,或者由一个坐标(x1,y1)和边界框的宽度(w)和高度(h)表示。(见下图)
显示坐标 x1、y1、x2、y2、宽度(w)和高度(h)的边框(图片由 Unsplash 上的 an_vision 拍摄)
多边形分割:物体并不总是矩形的。根据这一想法,多边形分割是另一种类型的数据注释,其中使用复杂的多边形而不是矩形来以更精确的方式定义对象的形状和位置。
来自 COCO 数据集的图像的多边形分割(来源)
语义分割:语义分割是一种逐像素的标注,其中图像中的每个像素都被分配到一个类别。这些类别可以是行人、汽车、公共汽车、道路、人行道等。,且每个像素携带一个语义含义。
语义分割主要用于环境背景非常重要的情况。例如,它被用于自动驾驶汽车和机器人,因为模型可以了解他们正在操作的环境。
来自 Cityscapes 数据集的图像的语义分割(来源
3D 长方体: 3D 长方体类似于具有关于对象的附加深度信息的边界框。因此,有了 3D 长方体,你可以获得物体的 3D 表示,允许系统在 3D 空间中区分体积和位置等特征。
3D 长方体的一个用例是在自动驾驶汽车中,它可以使用深度信息来测量物体与汽车的距离。
图像上的三维长方体标注(原图由何塞·卡巴贾尔在 Unsplash 上拍摄)
关键点和界标:关键点和界标注释用于通过在图像上创建点来检测小物体和形状变化。这种类型的注释对于检测面部特征、面部表情、情绪、人体部位和姿势是有用的。
COCO 数据集的关键点注释示例(来源
直线和样条:顾名思义,这种类型的注释是通过使用直线和样条来创建的。它通常用于自动驾驶汽车的车道检测和识别。
道路上的线标注(原图由卡斯滕·沃思在 Unsplash 上拍摄)
图像注释格式
谈到图像注释,没有单一的标准格式。以下是几种常用的注释格式:
COCO: COCO 有五种注释类型:用于对象检测、关键点检测、素材分割、全景分割、图像字幕。使用 JSON 存储注释。
对于对象检测,COCO 遵循以下格式:
annotation{"id" : int,"image_id": int,"category_id": int,"segmentation": RLE or [polygon],"area": float,"bbox": [x,y,width,height],"iscrowd": 0 or 1,}categories[{"id": int,"name": str,"supercategory": str,}]
Pascal VOC: Pascal VOC 在 XML 文件中存储注释。下面是一个用于对象检测的 Pascal VOC 注释文件的例子。
<annotation>
<folder>Train</folder>
<filename>01.png</filename>
<path>/path/Train/01.png</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>224</width>
<height>224</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>36</name>
<pose>Frontal</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<occluded>0</occluded>
<bndbox>
<xmin>90</xmin>
<xmax>190</xmax>
<ymin>54</ymin>
<ymax>70</ymax>
</bndbox>
</object>
</annotation>
YOLO: 在 YOLO 标注格式中,为同一目录下的每个图像文件创建一个同名的.txt
文件。每个.txt
文件包含相应图像文件的注释,即对象类别、对象坐标、高度和宽度。
<object-class> <x> <y> <width> <height>
对于每个对象,创建一个新行。
以下是 YOLO 格式的注释示例,其中图像包含两个不同的对象。
0 45 55 29 67
1 99 83 28 44
图像注释工具
以下是可用于注释图像的工具列表:
- 有道理。艾
- 贴标
- VGG 图像注释器
- 拉贝梅
- 可扩展
- 矩形标签
摘要
在这篇文章中,我们讨论了什么是数据注释/标签,以及为什么它对机器学习很重要。我们查看了 6 种不同类型的图像注释:包围盒、多边形分割、语义分割、3D 长方体、关键点和界标、线条和样条线,以及 3 种不同的注释格式:COCO、Pascal VOC 和 YOLO。我们还列出了一些可用的图像注释工具。
在下一篇文章中,我们将详细介绍如何注释图像数据。
敬请期待!
你常用什么图像标注类型?你用哪种格式来注释你的图像? 在下面留下你的想法作为评论。
原载于www.xailient.com/blog。**
找一个预先训练好的人脸检测模型。点击这里下载。
查看这篇文章了解更多关于创建一个健壮的对象检测模型的细节。
参考文献:
* [## 计算机视觉的图像标注类型及其用例
有许多类型的计算机视觉图像注释,每一种注释技术…
hackernoon.com](https://hackernoon.com/illuminating-the-intriguing-computer-vision-uses-cases-of-image-annotation-w21m3zfg) [## 计算机视觉的最佳图像注释平台(对每个平台的诚实评论)
需要标注计算机视觉数据集?我们人类在循环中可以分享一些关于最佳注释的见解…
hackernoon.com](https://hackernoon.com/the-best-image-annotation-platforms-for-computer-vision-an-honest-review-of-each-dac7f565fea) [## 24 款最佳计算机视觉图像注释工具| Lionbridge AI
图像注释是在图像中手动定义区域并为这些区域创建基于文本的描述的过程
lionbridge.ai](https://lionbridge.ai/articles/image-annotation-tools-for-computer-vision/)
关于作者
Sabina Pokhrel 在 Xailient 工作,这是一家计算机视觉初创公司,已经建立了世界上最快的边缘优化物体探测器。*
基于吉布斯采样的图像去噪
原文:https://towardsdatascience.com/image-denoising-with-gibbs-sampling-mcmc-concepts-and-code-implementation-11d42a90e153?source=collection_archive---------20-----------------------
概念和代码实现
作者照片
在本教程中,我将向你展示如何实现吉布斯采样算法去噪图像。
我们的目标是通过使用吉布斯采样(一种马尔可夫链蒙特卡罗(MCMC)方法)从受损图像(右)中去除噪声来恢复真实图像(左)。
本教程分为三个部分:
- MCMC 和 Gibbs 抽样的理论概念
- 这个问题的数学推导
- Python 中的代码实现
MCMC 和 Gibbs 抽样
马尔可夫链蒙特卡罗(MCMC)是一种采样方法,用于通过从概率分布中随机采样并构建马尔可夫链来近似感兴趣参数的后验分布。
Gibbs 采样是一种 MCMC 算法,它生成样本的马尔可夫链,每个样本都与其直接邻居一起计算。例如,在贝叶斯网络中,每个样本仅依赖于其父节点、共同父节点和子节点;在马尔可夫随机场中,每个样本都与其马尔可夫毯相关联。这个独立性属性简化了问题,因为为了获得状态 s 的样本值,我们只需要条件概率 P(s|s_neighbors) 。
MCMC 算法通常有一个预烧期,在此期间样本可能不准确。因此,在预烧期后收集样本,并使用蒙特卡罗方法估计后验概率。
数学演绎
吉布斯采样的应用之一是图像去噪。对于我们的图像去噪问题,给定一个有噪声的图像 X ,目标是将其恢复到原始图像 Y ,未知。
我们知道一个噪声图像数组 X = {xij} ,其中xij∈{-1,+1} 表示第 I 行第 j 列的像素,图像是黑白的, xij = 1 对应黑色, xij = -1 对应白色。
原始图像(无噪声) Y 与有噪声图像 X 大小相同,其中yij∈{-1,+1} 表示添加噪声前 xij 的值。
去噪可以被视为概率推断,其中我们通过最大化后验分布 p(Y|X) 来执行最大后验(MAP)估计。由贝叶斯定理我们得到:p(Y | X)= p(X | Y)p(Y)/p(X)。在日志空间,这可以重写为:
既然给定了 X,最大化 log p(Y|X) 本质上等价于最小化—log p(X | Y)—log p(Y),这就是这个问题中的损失函数。
用于图像去噪的经典结构是成对 MRF,如下图所示。每个节点 yij 与其对应的输出 xij 和 4 个直接邻居(上、下、左、右)相连。因此,给定像素 yij、 的 5 个邻居,我们可以确定 yij 的概率分布,而无需查看其他像素。请注意,边缘上的像素具有较少的邻居。例如,y11 的邻居是 y12、y21 和 x11。为了方便起见,我们可以在实现算法时用 0 填充边缘,这样所有像素都有 5 个邻居。
假设我们的后验偏好是黑色,我们想要最大化的后验是 p(Y=1|Y_neighbors) ,其中 Y= { yij }对于 i = 1,…,N,j = 1,…,M 。 Y 和 X 的联合概率为:
其中 ita( η )和 beta( β )是我们的超参数,Z 是归一化常数。 N(ij) 是 yij 除 xij 外的对应邻居。
使用贝叶斯规则,我们可以从联合分布中得到后验分布,如下所示:
我们还可以得到损失函数—log p(X | Y)——log p(Y),写为:
我们称之为能量,因为损失相当于玻尔兹曼分布中的能量,其中具有较低能量的状态总是具有较高的概率。
有了所有的数学方程式,现在我们可以开始了!
在下一节中,我们将学习如何在 Python 中实现 Gibbs 采样来恢复图像。
代码实现
首先,我编写了一些伪代码来演示吉布斯采样的高级逻辑:
现在让我们深入研究代码!
函数denoise_image()
是入口函数。它调用另一个函数 get_posterior 来获得估计的后验概率 p(Y=1|Y_neighbor) 。将阈值设置为 0.5,我们可以从后验得到恢复的图像数组 Y。接下来,我们剥离图像数组的边缘并返回它。下面的 load_image 函数解释了为什么我们需要剥离数组。
在load_image()
中,我们首先将 PNG 图像读入一个 numpy 数组,然后将 RGB 图像转换为灰度,并将像素重新缩放为{-1,1}。在这里,我们添加 0 填充到边缘,以帮助我们在以后搜索每个像素的邻居时处理拐角情况。
现在我们可以跳到get_posterior
函数中的主要逻辑实现。
在加载噪声图像 X 并随机初始化恢复图像 Y 之后,我们开始循环采样 Y 并计算后验概率 P(Y|Y_neighbor) 。
在每一步,我们迭代 I,j,通过调用函数sample_y()
对 Y 中的每个像素 yij 进行采样,并用采样值 yij 更新 Y 。我们还跟踪能量(等式 6),以便稍后可以可视化收敛。当老化期结束时,我们对 Y 中的 yij 的事件 yij = 1 的总发生次数进行求和。
一旦采样完成,我们使用蒙特卡罗方法来获得后验概率,这实质上是将 Y 的聚合值除以总样本数。
每个样品 yij 是怎么得到的?我们来看看函数sample_y()
。
sample_y()
功能需要 4 个输入:行和列索引 I 和 j、恢复图像阵列 Y 和噪声图像阵列 X 。该函数搜索 yij 的邻居yij _ neighbors并根据等式(5)* 计算条件概率P(yij = 1 | yij _ neighbors)。*
yij (1 或-1)的值用条件概率采样并由函数返回。
最后,我们有两个助手函数。
plot_energy()
将节省的能量可视化,save_image()
帮助将去噪后的图像 numpy 数组转换为 PNG 图像。
现在,让我们把所有东西放在main
模块中。
我们指定超参数 ita( η )和 beta( β )、总样本数和老化步骤。我们还设置了一个日志路径来记录能量。
来自denoise_image()
的输出降噪 _img 是恢复的图像阵列 Y 。
经过 100 步老化和 1000 步采样,我们可以看到我们的吉布斯采样做了一个体面的工作恢复损坏的图像,如下所示。
我们也可以检查能量汇聚。从下图中,我们发现虽然图像 Y 被随机初始化,但是能量很快收敛到一个低值。
就是这样!你可以在我的 Github 上找到完整的代码实现。
我们从这篇文章中学到了什么
- 马尔可夫链蒙特卡罗(MCMC)和吉布斯抽样;
- 基于成对马尔可夫随机场模型的地图推理:
- 如何在 Python 中对图像去噪应用 Gibbs 采样
我希望你喜欢这篇文章:)
参考
- 马尔可夫随机场和吉布斯采样用于图像去噪
- 采样方法
基于 OpenCV 和 Python 的图像增强技术
原文:https://towardsdatascience.com/image-enhancement-techniques-using-opencv-and-python-9191d5c30d45?source=collection_archive---------5-----------------------
作者图片
在这篇博文中,我将展示如何使用图像处理技术从低分辨率/模糊图像/低对比度图像中提高质量并提取有意义的信息。
让我们开始这个过程:
我有一个在传送带上运行的从仓库中取出的液化石油气钢瓶的样本图像。我的目的是找出石油气瓶的批号,以便我可以更新有多少石油气瓶已经过质量检查。
步骤 1:导入必要的库
**import** cv2
**import** numpy **as** np
**import** matplotlib.pyplot **as** plt
步骤 2:加载图像并显示示例图像。
img= cv2.imread(**'cylinder1.png'**)
img1=cv2.imread(**'cylinder.png'**)
images=np.concatenate(img(img,img1),axis=1)
cv2.imshow(**"Images"**,images)
cv2.waitKey(0)
cv2.destroyAllWindows()
作者提供的(a)批次-D26 (b)批次 C27 的液化石油气钢瓶图片
正如你所看到的,图像的对比度很差。我们几乎认不出批号。这是雷电条件不合适的仓库常见的问题。进一步将讨论对比度受限的自适应直方图均衡化,并尝试在数据集上实验不同的算法。
步骤 3:将图像转换成灰度图像
gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray_img1=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
步骤 4:现在我们找出灰度图像的直方图,并寻找强度的分布。
hist=cv2.calcHist(gray_img,[0],**None**,[256],[0,256])
hist1=cv2.calcHist(gray_img1,[0],**None**,[256],[0,256])
plt.subplot(121)
plt.title(**"Image1"**)
plt.xlabel(**'bins'**)
plt.ylabel(**"No of pixels"**)
plt.plot(hist)
plt.subplot(122)
plt.title(**"Image2"**)
plt.xlabel(**'bins'**)
plt.ylabel(**"No of pixels"**)
plt.plot(hist1)
plt.show()
步骤 5:现在我们将使用 cv2.equalizeHist()函数来均衡给定灰度图像的对比度。cv2.equalizeHist()函数使亮度正常化,同时增加对比度。
gray_img_eqhist=cv2.equalizeHist(gray_img)
gray_img1_eqhist=cv2.equalizeHist(gray_img1)
hist=cv2.calcHist(gray_img_eqhist,[0],**None**,[256],[0,256])
hist1=cv2.calcHist(gray_img1_eqhist,[0],**None**,[256],[0,256])
plt.subplot(121)
plt.plot(hist)
plt.subplot(122)
plt.plot(hist1)
plt.show()
步骤 6:显示灰度直方图均衡化的图像
eqhist_images=np.concatenate((gray_img_eqhist,gray_img1_eqhist),axis=1)
cv2.imshow(**"Images"**,eqhist_images)
cv2.waitKey(0)
cv2.destroyAllWindows()
灰度直方图均衡化
让我们进一步深入了解 CLAHE
第七步:
对比度受限的自适应直方图均衡化
该算法可用于提高图像的对比度。该算法通过创建图像的几个直方图来工作,并使用所有这些直方图来重新分配图像的亮度。CLAHE 可以应用于灰度图像和彩色图像。有两个参数需要调整。
- 设定对比度限制阈值的限幅限制。默认值为 40
- tileGridsize 设置行和列中标题的数量。在应用 cla 时,图像被分成称为图块(8*8)的小块,以便执行计算。
clahe=cv2.createCLAHE(clipLimit=40)
gray_img_clahe=clahe.apply(gray_img_eqhist)
gray_img1_clahe=clahe.apply(gray_img1_eqhist)
images=np.concatenate((gray_img_clahe,gray_img1_clahe),axis=1)
cv2.imshow(**"Images"**,images)
cv2.waitKey(0)
cv2.destroyAllWindows()
克拉赫
第八步:
阈值技术
阈值化是一种简单而有效的方法,用于将图像分割成前景和背景。最简单的阈值处理方法是,如果像素强度小于某个预定义的常数(阈值),则用黑色像素替换源图像中的每个像素,如果像素强度大于阈值,则用白色像素替换。不同类型的阈值是:-
cv2。THRESH_BINARY
cv2。阈值 _ 二进制 _INV
cv2。TRUNC 阈值
cv2。阈值为零
cv2。THRESH_TOZERO_INV
cv2。OTSU 阈值
cv2。阈值三角形
尝试更改阈值和 max_val 以获得不同的结果。
th=80
max_val=255
ret, o1 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_BINARY)
cv2.putText(o1,**"Thresh_Binary"**,(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)
ret, o2 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_BINARY_INV)
cv2.putText(o2,**"Thresh_Binary_inv"**,(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)
ret, o3 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TOZERO)
cv2.putText(o3,**"Thresh_Tozero"**,(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)
ret, o4 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TOZERO_INV)
cv2.putText(o4,**"Thresh_Tozero_inv"**,(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)
ret, o5 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TRUNC)
cv2.putText(o5,**"Thresh_trunc"**,(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)
ret ,o6= cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_OTSU)
cv2.putText(o6,**"Thresh_OSTU"**,(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)
final=np.concatenate((o1,o2,o3),axis=1)
final1=np.concatenate((o4,o5,o6),axis=1)
cv2.imwrite(**"Image1.jpg"**,final)
cv2.imwrite(**"Image2.jpg"**,final1)
Thresh_Binary_inv,Thresh_Binary_inv,Thresh_Tozero
Thresh_Tozero_inv,Thresh_trunc,Thresh_OSTU
步骤 9:自适应阈值
在上一节中,我们已经使用全局阈值应用了 cv2.threshold()。正如我们所看到的,由于图像不同区域的光照条件不同,获得的结果不是很好。在这些情况下,您可以尝试自适应阈值处理。在 OpenCV 中,自适应阈值处理由函数 cv2.adapativeThreshold() 执行
该功能将自适应阈值应用于 src 阵列(8 位单通道图像)。maxValue 参数设置满足条件的 dst 图像中的像素值。adaptiveMethod 参数设置要使用的自适应阈值算法。
cv2。ADAPTIVE _ THRESH _ MEAN _ C:T(x,y)阈值计算为(x,y)的 blockSize x blockSize 邻域的均值减去 C 参数。
cv2。ADAPTIVE _ THRESH _ GAUSSIAN _ C:T(x,y)阈值计算为(x,y)的 blockSize x blockSize 邻域的加权和减去 C 参数。
blockSize 参数设置用于计算像素阈值的邻域的大小,它可以取值 3、5、7…等等。
C 参数只是从平均值或加权平均值中减去的常数(取决于 adaptive method 参数设置的自适应方法)。通常,该值为正值,但也可以为零或负值。
gray_image = cv2.imread(**'cylinder1.png'**,0)
gray_image1 = cv2.imread(**'cylinder.png'**,0)
thresh1 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
thresh2 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 31, 3)
thresh3 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 13, 5)
thresh4 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 4)
thresh11 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
thresh21 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 31, 5)
thresh31 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21,5 )
thresh41 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 5)
final=np.concatenate((thresh1,thresh2,thresh3,thresh4),axis=1)
final1=np.concatenate((thresh11,thresh21,thresh31,thresh41),axis=1)
cv2.imwrite(**'rect.jpg'**,final)
cv2.imwrite(**'rect1.jpg'**,final1)
自适应阈值
自适应阈值
第十步:
OTSU 二值化
Otsu 的二值化算法,这是一种处理双峰图像的好方法。双峰图像可以通过包含两个峰值的直方图来表征。Otsu 的算法通过最大化两类像素之间的方差来自动计算分离两个峰值的最佳阈值。等效地,最佳阈值使类内方差最小化。Otsu 的二值化算法是一种统计方法,因为它依赖于从直方图中获得的统计信息(例如,平均值、方差或熵)
gray_image = cv2.imread(**'cylinder1.png'**,0)
gray_image1 = cv2.imread(**'cylinder.png'**,0)
ret,thresh1 = cv2.threshold(gray_image,0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
ret,thresh2 = cv2.threshold(gray_image1,0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite(**'rect.jpeg'**,np.concatenate((thresh1,thresh2),axis=1))
OTSU 二值化
现在,我们已经从低对比度图像中清楚地识别出批号。希望你觉得这篇博客内容丰富且有趣。
谢谢大家!!
请继续关注我的下一篇博客…
你可以通过下面的链接访问我以前的博客
在我的 Youtube 频道上关注我
https://www.youtube.com/channel/UCSp0BoeXI_EK2W0GzG7TxEw
在此与我联系:
领英:https://www.linkedin.com/in/ashishban...
github:https://github.com/Ashishb21
中:https://medium.com/@ashishb21
网址:http://techplanetai.com/
电子邮件:techplanetai@gmail.com ashishb21@gmail.com
关注我的 Youtube 频道,观看与技术相关的视频
https://www.youtube.com/channel/UCSp0BoeXI_EK2W0GzG7TxEw
[## 用 OpenCV 和 OpenCV Contrib 构建 Python 源代码
从源代码在 Ubuntu 14.0 LTS 上安装 Python 3.7
towardsdatascience.com](/building-python-source-with-opencv-and-opencv-contrib-ba95d709eb) [## 使用深度神经网络的印度演员分类
如果你需要使用深度网络对你最喜欢的演员进行分类,该怎么办?
towardsdatascience.com](/indian-actors-classification-using-deep-neural-networks-8552573f39aa)
图像特征提取:传统和深度学习技术
原文:https://towardsdatascience.com/image-feature-extraction-traditional-and-deep-learning-techniques-ccc059195d04?source=collection_archive---------1-----------------------
简要的综述着重于给出用于特征提取的传统和深度学习技术的概述
特征提取是计算机视觉中的一项重要技术,广泛用于以下任务:
- 物体识别
- 图像对齐和拼接(创建全景)
- 三维立体重建
- 机器人/自动驾驶汽车导航
- 还有更多…
什么是特性?
特征是图像中帮助识别对象的部分或模式。例如,一个正方形有四个角和四条边,它们可以被称为正方形的特征,它们帮助我们人类识别它是一个正方形。特征包括像角、边、感兴趣点的区域、脊等属性。
如下图所示,黄色点表示使用哈里斯检测技术检测到的特征。
(src:https://commons . wikimedia . org/wiki/File:Writing _ Desk _ with _ Harris _ detector . png)
传统特征检测技术一瞥
用于特征检测的传统计算机视觉技术包括:
- Harris 角点检测— 使用高斯窗口函数来检测角点。(阅读更多)
- Shi-Tomasi 角点检测器— 作者修改了 Harris 角点检测中使用的评分函数,以实现更好的角点检测技术(阅读更多信息)
- 尺度不变特征变换(SIFT)——与前两种不同,这种技术是尺度不变的。(阅读更多)
- 加速鲁棒特征(SURF)——顾名思义,这是 SIFT 的更快版本。(阅读更多)
- 加速分段测试(FAST)的特征— 这是一种比 SURF 更快的角点检测技术。(阅读更多)
- 二元鲁棒独立基本特征(BRIEF)——这只是一个特征描述符,可用于任何其他特征检测器。这种技术通过将浮点数形式的描述符转换为二进制字符串来减少内存使用。(阅读更多)
- 定向快速旋转简报(ORB) — SIFT 和 SURF 已获专利,OpenCV 实验室的这种算法是它们的免费替代品,它使用快速关键点检测器和简报描述符。(阅读更多)
深度学习特征提取技术一瞥
传统的特征提取器可以由卷积神经网络(CNN)代替,因为 CNN 具有提取复杂特征的强大能力,这些复杂特征更详细地表达了图像,学习特定于任务的特征,并且更有效。在这方面已经做了许多工作。下面列出了其中的一些:
- SuperPoint:自我监督的兴趣点检测和描述 ( 论文 ) —作者提出了一个完全卷积的神经网络,它在单次正向传递中计算 SIFT 样的兴趣点位置和描述符。它使用 VGG 式编码来提取特征,然后使用两个解码器,一个用于点检测,另一个用于点描述。
超点架构(src:https://arxiv.org/pdf/1712.07629.pdf
- D2 网:一个可训练的 CNN,用于局部特征的联合描述和检测 【论文】——作者提出了一个单一的卷积神经网络,它既是密集特征描述符,又是特征检测器。
检测并描述 https://arxiv.org/pdf/1905.03561.pdf 的 D2 网络
- LF-Net:从图像中学习局部特征 ( 论文 ) — 作者建议使用稀疏匹配深度架构,并在具有相对姿态和深度图的图像对上使用端到端训练方法。他们在第一幅图像上运行检测器,找到最大值,然后优化权重,以便在第二幅图像上运行时,产生清晰的响应图,在正确的位置具有尖锐的最大值。
LF-Net(src:https://papers . nips . cc/paper/7861-LF-Net-learning-local-features-from-images . pdf)
- 基于深度学习的图像特征匹配 ( 论文)——他们采用一种深度卷积神经网络(CNN)模型,在图像特征点匹配中关注图像块。
与 SIFT 和 SURF 相比的结果(src:https://ieeexplore.ieee.org/abstract/document/8780936)
- 特征匹配问题的深度图形特征学习 ( 论文 ) —他们建议使用图形神经网络将特征点的坐标转换为局部特征,这样就可以很容易地使用简单的推理算法进行特征匹配
图神经网络(src:https://open access . the CVF . com/content _ ICCV _ 2019/papers/张 _ Deep _ Graphical _ Feature _ Learning _ for _ the _ Feature _ Matching _ Problem _ ICCV _ 2019 _ paper . pdf
结论
虽然看起来用于特征提取的深度学习技术对缩放、遮挡、变形、旋转等更加鲁棒,并且已经突破了使用传统计算机视觉技术的极限,但这并不意味着计算机视觉技术已经过时。
放弃
这是一篇简短的文章,重点是概述传统的和深度学习的特征提取技术。如果你认为我可能错过了一个应该提到的算法,请在评论中留下它(将在这里加上适当的学分)。
参考文献
https://www . cs . UBC . ca/research/FLANN/uploads/FLANN/FLANN _ manual-1 . 8 . 4 . pdf
https://arxiv.org/pdf/1712.07629.pdf
https://arxiv.org/pdf/1905.03561.pdf
https://arxiv.org/ftp/arxiv/papers/1910/1910.13796.pdf
https://papers . nips . cc/paper/7861-lf-net-learning-local-features-from-images . pdf
【https://ieeexplore.ieee.org/abstract/document/8780936
https://open access . the CVF . com/content _ ICCV _ 2019/papers/张 _ Deep _ Graphical _ Feature _ Learning _ for _ the _ Feature _ Matching _ Problem _ ICCV _ 2019 _ paper . pdf
https://docs . opencv . org/3.4/DC/d0d/tutorial _ py _ features _ Harris . html
https://docs . opencv . org/3.4/D4/d8c/tutorial _ py _ Shi _ tomasi . html
https://docs . opencv . org/3.4/da/df5/tutorial _ py _ sift _ intro . html
https://docs . opencv . org/3.4/da/df5/tutorial _ py _ sift _ intro . html
https://docs . opencv . org/3.4/df/dd2/tutorial _ py _ surf _ intro . html
https://docs.opencv.org/3.4/df/d0c/tutorial_py_fast.html
https://docs.opencv.org/3.4/dc/d7d/tutorial_py_brief.html
https://docs.opencv.org/3.4/d1/d89/tutorial_py_orb.html
利用生成性对抗网络在 10 分钟内生成图像
原文:https://towardsdatascience.com/image-generation-in-10-minutes-with-generative-adversarial-networks-c2afc56bfa3b?source=collection_archive---------0-----------------------
深度学习案例研究
与 VIZIO AI 合作,使用 TensorFlow 和 MNIST 数据集,使用无监督深度学习生成具有深度卷积 gan 的手写数字
如今机器正在生成完美的图像,区分机器生成的图像和原件变得越来越困难。
如果你正在阅读这篇文章,我确信我们有着相似的兴趣,并且正在/将要从事相似的行业。那么我们就通过Linkedin来连线吧!请不要犹豫发送联系请求!Orhan g . Yal n—Linkedin
图一。Nvidia 的 StyleGAN 生成的图像示例【2】
图二。机器生成的数字使用 MNIST [ 3
在我的文章用 MNIST 数据集在 10 分钟内进行图像分类获得了超过 30 万的浏览量后,我决定准备另一个关于深度学习的教程。但这一次,我们不是对图像进行分类,而是使用相同的 MNIST 数据集来生成图像,该数据集代表修改后的国家标准与技术研究所数据库。它是一个手写数字的大型数据库,通常用于训练各种图像处理系统[1]。
生成对抗网络
为了用机器学习生成——基本上——任何东西,我们必须使用生成算法,至少目前,用于图像生成的最好的生成算法之一是生成对抗网络(或 GANs)。
生成对抗网络的发明
图 3。维基百科 [ 4 ]上伊恩·古德菲勒的照片
GANs 的发明出乎意料。著名的人工智能研究人员,当时是蒙特利尔大学的博士研究员,伊恩·古德菲勒,当他在一个朋友的告别派对上与他的朋友讨论其他生成算法的缺陷时,偶然想到了这个想法。聚会结束后,他满怀希望地回到家中,实施了他心中的构想。令人惊讶的是,在第一次试验中,一切都如他所愿[ 5 ],他成功地创建了生成性对抗网络(简称 GANs)。根据脸书人工智能研究主任、纽约大学教授 Yann Lecun 的说法,GANs 是“过去 10 年机器学习中最有趣的想法”。
GANs 的大致结构如下所示:
图 4。利用 CNN 的生成对抗网络(GANs)
在一个普通的 GAN 结构中,有两个相互竞争的代理:一个生成器和一个鉴别器。它们可以使用不同的网络来设计(例如,卷积神经网络(CNN)、递归神经网络( RNNs ),或者仅仅是常规神经网络(ann或常规网络)。由于我们将生成图像,CNN 更适合这项任务。因此,我们将用卷积神经网络来构建我们的代理。
我们的 GAN 模型是如何运作的?
图 5。GAN 网络中的生成器和鉴别器关系|(图表由作者提供)
简而言之,我们将要求生成器生成手写数字,而不给它任何附加数据。同时,我们将获取现有的手写数字到鉴别器,并要求它决定由生成器生成的图像是否是真实的。起初,生成器会生成糟糕的图像,这些图像会立即被鉴别器标记为假的。在从鉴别器获得足够的反馈后,由于与真实图像的差异减少,生成器将学习欺骗鉴别器。因此,我们将获得一个非常好的生成模型,可以给我们非常现实的输出。
构建 GAN 模型
GANs 经常使用复杂的计算,因此,支持 GPU 的机器将使你的生活变得容易得多。所以我会用 Google Colab,用 GPU 加速来减少训练时间。
使用 Google Colab 进行支持 GPU 的培训
对于机器学习任务,在很长一段时间里,我几乎专门通过 Anaconda 发行版使用-iPython- Jupyter Notebook 进行建模、训练和测试。不过,最近我因为几个好的原因转而使用 Google Colab。
Google Colab 在 Jupyter 笔记本的基础上提供了几个附加功能,例如(I)与其他开发人员的协作,(ii)基于云的托管,以及(iii) GPU 和 TPU 加速培训。你可以用免费版的 Google Colab 完成所有这些工作。Python、Jupyter Notebook 和 Google Colab 之间的关系可以形象化如下:
图 6。iPython、Jupyter Notebook、Google Colab |(图由作者提供)之间的关系
Anaconda 为科学计算提供免费和开源的 Python 和 R 编程语言发行版,包括 Jupyter Notebook (iPython)或 Jupyter Lab 等工具。在这些工具之上,Google Colab 允许其用户使用 iPython 笔记本和实验室工具及其服务器的计算能力。
现在,我们已经对作为我们的神经网络架构的生成式对抗网络和作为我们的编程环境的 Google Collaboratory 有了大致的了解,我们可以开始构建我们的模型了。在本教程中,我们将从一个官方的 TensorFlow 教程 [ 7 ]中获取我们自己的内容。
初始进口
Colab 已经预装了大多数机器学习库,因此,您可以通过以下共享方式导入它们:
TensorFlow、Keras 图层和 Matplotlib 导入
为了代码更短,我更喜欢单独导入层,如上所示。
加载并处理 MNIST 数据集
对于本教程,我们可以使用 MNIST 数据集。MNIST 数据集包含 60,000 张训练图像和 10,000 张测试图像,分别来自美国人口普查局员工和美国高中生[ 8 ]。
幸运的是,我们可以直接从 TensorFlow 库中检索 MNIST 数据集。我们从 Tensorflow 中检索数据集,因为这样,我们可以得到它的已处理版本。我们仍然需要做一些准备和处理工作,以使我们的数据适合 GAN 模型。因此,在第二行中,我们将这两组分为训练组和测试组,还将标签和图像分开。
x_train 和 x_test 零件包含灰度 RGB 代码(从 0 到 255),而 y_train 和 y_test 零件包含从 0 到 9 的标签,代表它们实际上是哪个数字。由于我们正在进行无监督的学习任务,我们将不需要标签值,因此,我们使用下划线(即 _)来忽略它们。我们还需要使用 reshape 函数将数据集转换为 4 维。最后,我们将 NumPy 数组转换为 TensorFlow Dataset 对象,以便更有效地进行训练。下面的代码行完成所有这些任务:
我们的数据已经处理完毕,是时候建立我们的 GAN 模型了。
建立模型
如上所述,每个 GAN 必须至少有一个发生器和一个鉴别器。由于我们正在处理图像数据,我们需要从这些网络中的卷积和转置卷积(逆卷积)层中受益。下面我们来定义一下我们的发生器和鉴别器网络。
发电机网络
我们的生成器网络负责从随机噪声中生成 28×28 像素的灰度假图像。因此,它需要接受一维数组并输出 28x28 像素的图像。对于这项任务,我们需要转置卷积层后,重塑我们的 1 维阵列到 2 维阵列。转置卷积层会增加较小数组的大小。我们还利用了 BatchNormalization 和 LeakyReLU 层。以下代码行创建了一个函数,该函数将使用 Keras Sequential API 生成一个发电机网络:
我们可以用下面的代码调用生成器函数:
图 7。我们发电机网的总结|(图由作者提供)
现在我们有了生成器网络,我们可以用下面的代码轻松地生成一个示例图像:
看起来像这样:
图 8。未经训练的生成器网络生成的样本图片|(图片由作者提供)
这只是普通的噪音。但是,它可以从随机噪声阵列中创建图像的事实证明了它的潜力。
鉴别器网络
对于我们的鉴别器网络,我们需要遵循我们的发生器网络的逆版本。它获取 28×28 像素的图像数据并输出单个值,表示真实性的可能性。因此,我们的鉴别器可以检查由生成器生成的样本图像是否是假的。
我们使用与创建生成器网络相同的方法,以下代码行创建了一个函数,该函数将使用 Keras Sequential API 创建一个鉴别器模型:
我们可以用下面的代码调用函数来创建我们的鉴别器网络:
图 9。我们的鉴频器网络总结|(图由作者提供)
最后,我们可以检查未经训练的鉴别器对未经训练的生成器生成的样本说了什么:
输出: tf。张量([[ -0.00108097 ]),shape=(1,1),dtype=float32)
负值表明我们未经训练的鉴别器断定图 8 中的图像样本是假的。目前,重要的是它可以检查图像并提供结果,经过训练后结果会可靠得多。
配置模型
由于我们在 GAN 网络中训练两个子网络,因此需要定义两个损失函数和两个优化器。
损失函数: 我们从从 tf.keras.losses 模块创建一个二元交叉熵对象开始。我们还将 from_logits 参数设置为 True 。创建对象后,我们用定制的鉴别器和生成器损耗函数填充它们。我们的鉴别器损耗计算为(I)鉴别器对真实图像的预测为一组 1,以及(ii)鉴别器对生成图像的预测为一组 0 的组合。我们的发电机损耗是通过测量它欺骗鉴别器的能力来计算的。因此,我们需要将鉴别器对生成图像的决策与 1 的数组进行比较。
优化器: 我们还为发生器和鉴别器网络分别设置了两个优化器。我们可以使用来自TF . keras . optimizer模块的亚当优化器对象。
下面几行配置了我们的损失函数和优化器
设置检查点
我们希望访问之前的训练步骤,TensorFlow 为此提供了一个选项: 检查点 。通过设置一个检查点目录,我们可以保存每个时期的进度。当我们从上一个纪元恢复我们的模型时,这将特别有用。以下代码行通过使用 os 库设置保存所有培训步骤的路径来配置培训检查点
训练模型
现在我们的数据准备好了,我们的模型也创建并配置好了。是时候设计我们的训练循环了。请注意,目前,甘需要定制的训练循环和步骤。我会尽量让你明白。确保阅读了 Github Gists 中的代码注释。
让我们用下面几行创建一些变量:
我们的种子是我们用来生成图像的噪音。下面的代码生成一个形状为(16,100)的正态分布的随机数组。
定义培训步骤
这是我们教程中最不寻常的部分:我们正在设置一个定制的训练步骤。通过注释 tf.function 模块定义自定义 train_step() 函数后,我们的模型将基于我们定义的自定义 train_step() 函数进行训练。
下面带有过多注释的代码是针对培训步骤的。请仔细阅读评论:
现在我们已经用 tf.function 注释创建了我们的定制训练步骤,我们可以为训练循环定义我们的训练函数了。
定义训练循环
我们为训练循环定义了一个名为 train 的函数。我们不仅运行 for 循环在 MNIST 上迭代我们的自定义训练步骤,还使用单个函数执行以下操作:
培训期间:
- 开始记录每个时期开始时花费的时间;
- 制作 GIF 图像并显示它们,
- 每五个时期保存一个模型作为检查点,
- 打印出完整的历元时间;和
- 训练完成后,最终生成最终图像。
以下带有详细注释的行执行所有这些任务:
图像生成功能
在训练函数中,有一个我们还没有定义的自定义图像生成函数。我们的图像生成功能执行以下任务:
- 通过使用模型生成图像;
- 使用 matplotlib 在 4x4 网格布局中显示生成的图像;
- 最后保存最后的数字
以下部门负责这些任务:
开始训练
在训练了三个复杂的函数之后,开始训练是相当容易的。只需使用以下参数调用 train 函数:
如果您使用支持 GPU 的 Google Colab 笔记本电脑,培训将需要大约 10 分钟。如果你用的是 CPU,可能要多花很多。让我们看看 60 个纪元后的最终产品。
图 10。我们的 GAN 在 60 个纪元后生成的数字。请注意,我们看到的是 16 个样本,因为我们是这样配置输出的。|(图片由作者提供)
生成数字
在生成新映像之前,让我们确保使用以下代码行从最新的检查点恢复值:
我们还可以通过查看生成的 4x4 网格来查看我们的生成式 GAN 模型的演变,该网格包含任何时期的 16 个样本数字,代码如下:
这段代码为我们提供了最新生成的网格。在 display_image(函数)中传递一个 0 到 60 之间的不同数字
或者更好的是,让我们用下面的代码创建一个 GIF 图像,可视化我们的 GAN 生成的样本的演变:
我们的输出如下:
图 11。GIF 图像显示了我们的 GAN 生成的样本数字随时间的演变|(图片由作者提供)
如图 11 所示,随着时间的推移,GAN 产生的输出变得更加真实。
恭喜
您已经建立并训练了一个生成式对抗网络(GAN)模型,它可以成功地创建手写数字。显然有一些样本不是很清楚,但对于仅在 6 万个样本上训练的 60 个纪元,我会说结果非常有希望。
一旦你能建立并训练这个网络,你就能生成更复杂的图像,
- 通过处理具有高清晰度彩色图像的较大数据集;
- 通过创建更复杂的鉴别器和发生器网络;
- 通过增加历元的数量;
- 通过在支持 GPU 的强大硬件上工作
最后,你可以创作艺术作品,如诗歌、绘画、文字或现实照片和视频。
订阅邮件列表获取完整代码
如果你想在 Google Colab 上获得完整的代码,并获得我的最新内容,请订阅邮件列表:✉️
立即订阅
喜欢这篇文章吗?
如果你喜欢这篇文章,可以考虑看看我的其他类似文章:
[## 使用 MNIST 数据集在 10 分钟内完成图像分类
towardsdatascience.com](/image-classification-in-10-minutes-with-mnist-dataset-54c35b77a38d) [## TensorFlow 和 VGG19 可以帮助您将照片转换成美丽的波普艺术作品
神经风格转移基于安迪沃霍尔的门罗双联画与预训练的计算机视觉网络 VGG19,转移…
towardsdatascience.com](/tensorflow-and-vgg19-can-help-you-convert-your-photos-into-beautiful-pop-art-pieces-c1abe87e7e01) [## 伯特和拥抱脸 10 分钟情感分析
学习预训练的自然语言处理模型的基础,伯特,并建立一个使用 IMDB 电影评论的情感分类器…
towardsdatascience.com](/sentiment-analysis-in-10-minutes-with-bert-and-hugging-face-294e8a04b671) [## 使用递归神经网络预测比特币(BTC)价格
如果你能以某种方式预测明天的比特币(BTC)价格,这不是很棒吗?加密货币市场有…
towardsdatascience.com](/using-recurrent-neural-networks-to-predict-bitcoin-btc-prices-c4ff70f9f3e4)
资源
[1]奥尔汉·g·亚尔琴,用 MNIST 数据集在 10 分钟内进行图像分类,走向数据科学,https://towardsdatascience . com/Image-Classification-in-10-Minutes-with-Mn ist-Dataset-54c 35 b 77 a 38d
[2]莱蒂宁法官(未注明日期)。分析并改善 StyleGAN Tero Karras NVIDIA samu Li Laine NVIDIA Miika Aittala NVIDIA Janne hells ten NVIDIA的图像质量。从 https://github.com/NVlabs/stylegan2取回
[3]或 Sharir & Ronen Tamari & Nadav Cohen & am non sha shua,Tensorial Mixture Models,https://www . research gate . net/profile/Or _ Sharir/publication/309131743。
[4]维基百科,文件:伊恩 Goodfellow.jpg,https://upload . wikimedia . org/Wikipedia/commons/f/Fe/Ian _ good fellow . jpg
同步,甘斯之父伊恩·古德费勒为苹果拆分谷歌,https://medium . com/SYNCED review/甘斯之父-伊恩·古德费勒-Splits-Google-For-Apple-279 FCC 54 b 328
[5] YOUTUBE,深度学习的英雄:https://www.youtube.com/watch?v=pWAc9B2zJS4 吴恩达采访伊恩·古德菲勒
[6] George Lawton,Generative adversarial networks 可能是 AI 中最强大的算法,https://search enterprise AI . tech target . com/feature/Generative-adversarial-networks-can-be-most-powerful-algorithm-in-AI
[7]深度卷积生成对抗网络,TensorFlow,可在https://www.tensorflow.org/tutorials/generative/dcgan获得
[8]维基百科,https://en.wikipedia.org/wiki/MNIST_database MNIST 数据库,
用一行代码修复图像
原文:https://towardsdatascience.com/image-inpainting-with-a-single-line-of-code-c0eef715dfe2?source=collection_archive---------25-----------------------
使用 Python 中的 OpenCV,使用四种不同的技术和一行代码,使用图像修复来恢复图像。
杰德·斯蒂芬斯在 Unsplash 上的照片
修复是一个过程,其中艺术品的损坏或缺失部分被填充以完成它。使用 GANs 来执行图像修复的现有技术方法,其中复杂模型在大量数据上被训练。如果呈现的数据完全不同,那么模型也可能受到影响。与此相反,图像修复也可以通过图像处理操作来完成,这也只需要使用 OpenCV 的一行代码。在本文中,我们将实现四种不同类型的图像修复方法,并基于 PSNR 和 SSIM 比较它们的结果。此外,我将在本文中遵循一种不同的方法,即首先呈现代码和结果,然后描述方法。
我们将使用三种算法,即基于 Navier-Stokes 的算法、Alexandru Telea 基于快速行进法的方法以及快速频率选择性重建算法的快速和最佳迭代。
目录
- 要求
- 密码
- 结果
- 方法概述
要求
其中两种方法需要 OpenCV Contrib,以便可以使用 pip 安装:
pip install opencv-contrib-python==4.3.0.36
密码
我们需要扭曲的图像和需要修补的蒙版。对于 Navier-Stokes 和 Telea 方法,蒙版上的白色部分代表要修复的区域,而对于 FSR 方法,黑色像素则是在修复的像素。
图像修复所需的单行代码是:
import cv2#distorted_img: The image on which inpainting has to be done.
#mask: Black mask with white pixels to be inpainted*res_NS = cv2.inpaint(distort_img, mask, 3, cv2.INPAINT_NS)
res_TELEA = cv2.inpaint(distort_img, mask, 3, cv2.INPAINT_TELEA)*res_FSRFAST = distorted_img.copy()
res_FSRBEST = distorted_img.copy()
mask1 = cv2.bitwise_not(mask)*cv2.xphoto.inpaint(distort, mask1, res_FSRFAST, cv2.xphoto.INPAINT_FSR_FAST)
cv2.xphoto.inpaint(distort, mask1, res_FSTBEST, cv2.xphoto.INPAINT_FSR_BEST)*
结果
我用了三张大小为 756x1008 的图片。所有四种方法的结果如下所示。
结果 1
结果 2
结果 3
每幅图像的每种方法所用的时间(秒)为:
花费的时间
PSNR 和 SSIM 的值也是使用以下代码根据重建图像与原始图像进行计算的:
import cv2
from skimage.metrics import structural_similarity as SSIMpsnr = cv2.PSNR(img, recon, 255)
ssim = SSIM(img, recon, multichannel=True)
结果是:
PSNR 价值观
SSIM 价值观
此外,Genser 等人发表了一篇论文,对这些方法进行了比较,其中他们使用了 Kodak 和 Tecnick 图像集以及五种不同的错误屏蔽,其结果如下所示:
结果
下面给出了使用的完整代码,其中显示了一个样本遮罩,其中白色区域必须在原始图像上进行修复,并叠加在原始图像上。
面具
方法概述
泰莱亚
Alexandru Telea 在 2004 年的论文“基于快速行进法的图像修复技术”中介绍了这种技术。我在这里引用的 OpenCV 文档对此做了很好的概述。
考虑图像中要修补的区域。算法从该区域的边界开始,进入该区域,首先逐渐填充边界中的所有内容。需要在要修复的邻域的像素周围有一个小的邻域。该像素被邻域中所有已知像素的归一化加权和所代替。砝码的选择是一件重要的事情。对那些靠近该点、靠近边界法线的像素和那些位于边界轮廓上的像素给予更大的权重。一旦像素被修复,它就使用快速行进方法移动到下一个最近的像素。FMM 确保已知像素附近的像素首先被修复,因此它就像一个手动启发式操作。
纳维尔-斯托克斯
由 Bertalmio 等人在他们的论文中介绍,“纳维尔-斯托克斯,流体动力学,以及图像和视频修复”早在 2001 年就介绍过了。再次引用 OpenCV 文档:
该算法基于流体动力学并利用偏微分方程。基本原理是启发式的。它首先沿着边缘从已知区域行进到未知区域(因为边缘应该是连续的)。它继续等照度线(连接具有相同强度的点的线,就像轮廓连接具有相同海拔的点),同时在修复区域的边界匹配梯度向量。为此,使用了流体动力学的一些方法。一旦它们被获得,颜色被填充以减少该区域的最小变化。
快速频率选择性重建算法
使用已知的样本和已经重构的像素作为支持来外推失真块的信号。该算法迭代地产生信号的一般复值模型,该模型被外推为傅立叶基函数的加权叠加。FSR 的一个重要特征是计算是在傅立叶域中进行的,这导致了快速实现。
总之,FSR 的快速实施提供了速度和精度之间的巨大平衡,即使在高度失真的情况下,这些方法也能提供良好的结果,因此它们可以为数据密集型 GAN 提供合理的替代方案,后者在不可预见的数据上可能表现不佳。
参考
- https://docs . opencv . org/master/df/d3d/tutorial _ py _ inpainting . html
- https://docs . opencv . org/4 . 2 . 0/DC/d2f/tutorial _ x photo _ inpainting . html
- https://github . com/opencv/opencv _ contrib/files/3730212/inpainting _ comparison . pdf
如上所述,我在本文中使用了不同的策略。下面给出的一篇文章采用了传统的方法。如果能知道你更喜欢哪种策略就太好了。
[## 人脸检测模型:使用哪种模型,为什么?
一个关于用 Python 实现不同人脸检测模型的完整教程,通过比较,找出最好的…
towardsdatascience.com](/face-detection-models-which-to-use-and-why-d263e82c302c)
使用脸书探测器的图像标记🤖🤖
原文:https://towardsdatascience.com/image-labelling-using-facebooks-detectron-4931e30c4d0c?source=collection_archive---------55-----------------------
你好,脸书探测器世界
Canva.com 地平线|免费媒体许可证
训练一个模型从头开始检测文本可能是一项非常困难和令人沮丧的任务。传统的方法是使用具有特征金字塔网络的 R-CNN 或者使用像 YOLO 这样的算法。
如果你不知道这两种方法背后的数学和逻辑,那么这两种方法都很难实现。
由脸书人工智能研究团队开发的 Detectron 2 是一个基于 mask-r-CNN 基准的最新目标检测模型。它的动力来自 Pytorch 深度学习框架。主要功能包括
来源
- 全景分割:FAIR 的另一个产品,是一种将语义分割(给每个像素分配一个类标签)和实例分割(检测和分割每个对象实例)这两种典型的不同任务统一起来的分割类型。
FAIR GITHUB
2.密集姿态:用于将 RGB 图像的所有人体像素映射到人体的 3D 表面。这是由咖啡 2 驱动的。
“这种模式旨在通过提供快速培训和解决公司在从研究走向生产时面临的问题来推进物体检测”
我们开始吧!
Detectron 2 可以使用 Google Colab Notebook 实现对象检测。我们选择 Google Colab 而不是本地系统,以利用 GPU 进行更快的训练。
步骤 1:安装并导入 Detectron 2
我们将在 google colab 上编写这些代码,或者您可以在这里 获得整个笔记本 。
为了开始,我们将安装一些依赖项,如 COCO API,CUDA(获取关于 GPU 的信息),Tourch Visison
导入实用程序和公共库
步骤 2:运行预训练的检测器 2 模型
我们将使用来自 COCO 数据集的图像,并将运行一个预训练的模型,如果你想在客户数据集上运行这个模型,请参见这里。
在 COCO 的图像上运行模型的代码:
你已经成功地用 detectron 实现了你的第一个项目。
结果:可视化前后
下面列出了来源。
来源
资源:
- 脸书探测仪人工智能研究页面
- 论文讲解全景分割
Python 中的图像处理
原文:https://towardsdatascience.com/image-manipulation-in-python-cbb86a61cf0?source=collection_archive---------22-----------------------
常用图像处理技术逐步指南
来自的照片
在这篇文章中,我将向你展示如何使用 Python 编辑图像。编辑图像的过程称为图像处理。您可能想知道为什么在项目中使用图像之前需要对它们进行一些润色。这有许多原因,但几个主要原因可以列为节省存储空间、提高培训质量和更快的运行时间。这篇文章中涉及的操作技术包括图像大小调整、图像亮度,以及最后将图像颜色转换成灰度。我们将通过测试每一张图片来进行一些实际操作。
图像大小调整
对于一个图像来说,这没有太大的区别,但是当你考虑在你的程序中处理成千上万的图像时,很小的尺寸变化将会节省你大量的时间和存储空间。
图像亮度
当我们看一幅图像时,很容易理解我们看到的东西,但对于机器来说,这可能有点挑战性。因此,为了帮助机器更好地处理图像,一点亮度可以帮助提高精确度。
图像灰度
当训练机器时,灰度图像做得更好。原因是因为机器将图像视为阵列的矩阵,所以存储黑白图像比存储多幅彩色图像更容易。
这些只是我们将在本帖中介绍的一些图像处理技术。还有很多,但如前所述,这些是最常用的技术,可以应用于任何格式的图像。如果你准备好了,我们开始吧!
图像
在我们开始编码之前,让我们选择一个图像来测试我们的代码。这张图片可以是任何格式,但我建议使用“png”或“jpg”。这是我选择玩的图像。这张图片的好处是没有使用滤镜和特效。
照片由贝希克·居文从派克斯拍摄
包装
我们总共只用一个包。我们将用于图像操作的主要库称为 PIL,它是图像处理库。PIL 将被安装为“枕头”,不要混淆,他们是一回事。
让我们从安装包开始吧。
pip install pillow
是时候将包作为库导入了,这样我们就可以使用它们了。
from PIL import Image, ImageEnhance
导入图像
我已经将我的图像重命名为“testme.jpg”,在这一步中,我通过将它分配给一个名为“img”的变量名来导入图像。我们将做最常用的三种手法。为了使事情变得简单明了,我将为每种技术使用相同的图像,但是可以随意为每种操作技术定义不同的图像。
img = Image.open("testme.jpg")
技巧 1 —调整大小
我们将对这张图片做的第一件事是调整它的大小。使用 PIL 库调整大小是非常容易的,这是一个图像处理库,如前所述。调整图像大小后,我想保存它,以便我们运行程序后可以看到图像。而最后要把图像保存为图像文件,我们就要用'保存'的方法。
在我们调整大小之前,您可以通过下面的代码打印图像的当前大小。
print(img.size)
调整图像大小:
newsize = (300, 300)
img_resized = img.resize(newsize) print(img_resized.size)
现在,我们可以将图像文件保存在我们的文件夹中。
img_resized.save("resized.jpg")
结果
resized.jpg
技术 2 —亮度
其次,让我们给我们的形象增加一些光亮。尤其是有阴影的图像,增加亮度有助于机器更好地看到图像中的线条。这在训练机器时非常重要,因为机器接受事物的本来面目,很少的错误信息都会导致错误的训练。不管怎样,下面是增加一些亮度的代码:
# Define enhancer
enhancer = ImageEnhance.Brightness(img) img_light = enhancer.enhance(1.8) img_light.save("brightened.jpg")
结果
Brightened.jpg
对于光线太强的图像,我们能做些什么吗?当然,和加光一样,也有一种方法可以让一些光出来。这将有助于在晴天拍摄的图像。要做到这一点非常容易,我们只需改变括号之间的值。如果数量大于 1,增强器将增加光线。如果该值介于 0 和 1 之间,增强器会发出一些光,这将使图像变得更暗。
值越小,颜色越深。你可以玩这个值,并观察它的行为。
enhancer = ImageEnhance.Brightness(img)
img_light = enhancer.enhance(0.8) img_light.save("darkened.jpg")
结果
darkened.jpg
技术 3 —灰度
作为我们的第三种图像处理技术,我们将看到如何将图像转换成灰度。换句话说,我们可以说非黑即白。存储灰度图像比存储多种颜色的图像更有效,机器也更容易理解。特别是,当训练机器学习图像中的特定对象时,灰度是最常用的技术之一。
img = Image.open('testme.jpg')
img = img.convert('L') img.save('grayscaled.jpg')
结果
grayscaled.jpg
这些是一些最常用的图像处理技术。这些技巧将帮助你更快更容易地编辑图像。在这个练习中,我们只编辑了一个图像,但是也可以在一个循环中运行相同的代码,这样你就可以用几行代码编辑成千上万的图像。
我是贝希克·居文,我喜欢分享关于创造力、编程、动力和生活的故事。
跟随我的 博客 和 走向数据科学 留下灵感。
你可能喜欢的计算机视觉项目
[## Python 中的简单人脸检测
如何使用 OpenCV 库检测图像中的人脸
towardsdatascience.com](/simple-face-detection-in-python-1fcda0ea648e)
利用 OpenCV 的模板匹配进行图像匹配
原文:https://towardsdatascience.com/image-matching-with-opencvs-template-matching-5df577a3ce2e?source=collection_archive---------23-----------------------
现实世界问题的高效计算解决方案
来自 icons8.com 的图片
介绍
作为 VATBox 的一名数据科学家,我主要从事核心涉及构建机器学习模型的项目。这个项目的好处在于,它纯粹包括建立一个算法来解决给定的任务。定制解决方案的现实问题。
问题定义
手头的问题本质上是两个接近相同的图像之间的匹配(例如,变化可能是由于图像大小)。这里的目标是实时地这样做,因此我们需要算法相对较快。
在 VATBox,我们的世界是一个发票的世界。用户将包含发票图像的报告上传到我们的平台。一份报告包含两组图像,一组是单独发票的集合,另一组是所有发票的集合。由于某些原因,我们不会深入探讨,一个组中的图像可能不会出现在另一个组中。目标是检测没有出现在另一组中的图像(如果有的话)。如果我们在本质上是相同图像的所有图像之间进行匹配,我们也可以识别备用图像。
我们想要检测备用图像(在左边用红色标记,在右边用蓝色标记)
解决办法
因此,为了找到备用图像,我们需要在每两个图像之间进行比较(或者..?我们会谈到这一点..),一个图像来自第一组,另一个来自第二组。一旦完成,我们就可以在图像之间进行匹配,然后查看哪些图像没有匹配。我们可以构建一个包含这些比较的比较矩阵。
比较矩阵
在我的研究中,我遇到的大多数解决方案都是基于从图像中提取的特征来匹配图像,但对于我们的情况来说,这在计算上是昂贵的,因为我们对每对图像都感兴趣。因此,我们需要一种不同的方法。
如上所述,实际匹配的图像几乎完全相同。所以我们可以预期它们的像素值相对相似。那么我们是否可以计算两个给定信号之间的相关性?但是如何用图像有效地做到这一点呢?
OpenCV 有一个模板匹配模块。这个模块的目的是在一个(更大的)图像中找到一个给定的模板。该模块使我们能够在图像(I)上“滑动”模板(T)并有效地执行计算(类似于在 CNN 中卷积核如何在图像上滑动)。
照片来自pexels.com
这对我们的需求很有帮助!我们可以使用它来有效地将第一组中的图像滑动到另一组的图像上,因为 OpenCV 的实现是最佳的。当然,由于我们寻找的是两个匹配的图像(而不是另一个图像中的模板),所以滑动只进行一次。
模板匹配模块包括计算相关性的不同方法,其中一种是归一化计算,返回值介于 0(无相似性)到 1(完全相同的图像)之间:
corr = cv2.matchTemplate(group1_image, group2_image,
cv2.TM_CCOEFF_NORMED)[0][0]
索引显示我们正在访问一个矩阵。如前所述,我们本质上是执行一次滑动,因此我们只是从具有单个元素的矩阵中获取单个值。
关于计算的一句话。在实际的相关性计算之前,图像被归一化,产生具有负值和正值的矩阵(或张量)。当执行相关性计算时,相似的区域将在相关性计算中获得正值,而不同的区域将获得负值(导致较低的相关性)。
复杂性
因为我们正在比较每一对图像,所以算法的复杂度是 O(n*m),n 是第一组中图像的数量,m 是第二组中图像的数量。能不能让算法更高效?
由于上面的函数为相似的图像返回接近 1 的值,我们可以设置一个阈值(例如 0.9),如果找到一个匹配,就不需要继续搜索。此外,如果找到匹配,我们将从搜索中删除匹配的图像。例如,如果在第一次迭代中,我们将第一组中的一幅图像与第二组中的 4 幅图像进行比较,那么在第二次迭代中,我们将只与 3 幅图像进行比较,依此类推。
摘要
在本文中,我们回顾了一个有点不同寻常的现实世界的数据科学问题,它需要一个定制的解决方案。我们深入研究了 OpenCV 的模板匹配,了解了如何使用它来满足手头问题的需求。希望你喜欢它!
使用深度卷积自动编码器在 10 分钟内降低图像噪声
原文:https://towardsdatascience.com/image-noise-reduction-in-10-minutes-with-convolutional-autoencoders-d16219d2956a?source=collection_archive---------7-----------------------
深度学习案例研究
借助时尚 MNIST |使用 TensorFlow 的无监督深度学习,使用自动编码器清理(或去噪)有噪声的图像
如果你正在阅读这篇文章,我确信我们有着相似的兴趣,并且现在/将来会从事相似的行业。那么我们就通过 Linkedin 来连线吧!请不要犹豫发送联系请求!Orhan g . yaln—Linkedin
图一。一只贪玩的狗的图像降噪前后(由 Anna Dudkova 在 Unsplash 上拍摄)
如果你在这个页面上,你也可能对不同的神经网络架构有些熟悉。你可能听说过前馈神经网络,CNN,RNNs,这些神经网络非常适合解决监督学习任务,如回归和分类。
但是,在无监督学习领域,我们有大量的问题,例如降维、特征提取、异常检测、数据生成、增强以及降噪。对于这些任务,我们需要专门为无监督学习任务开发的特殊神经网络的帮助。因此,他们必须能够在不需要监督的情况下解数学方程。这些特殊的神经网络架构之一是自动编码器。
获取最新的机器学习教程
自动编码器
什么是自动编码器?
自动编码器是由两个子网络组成的神经网络架构,即编码器和解码器网络,它们通过潜在空间相互联系。自动编码器最初是由人工智能界最受尊敬的科学家之一 Geoffrey Hinton 和 PDP 小组在 20 世纪 80 年代开发的。Hinton 和 PDP 小组旨在通过将输入作为教师来解决“无教师反向传播”问题,也称为无监督学习。换句话说,他们只是将特征数据用作特征数据和标签数据。让我们仔细看看自动编码器是如何工作的!
图二。具有编码器和解码器网络的自动编码器网络
自动编码器架构
自动编码器由一个编码器网络组成,它获取特征数据并对其进行编码以适应潜在空间。该编码数据(即代码)被解码器用来转换回特征数据。在编码器中,模型学习的是如何有效地编码数据,以便解码器可以将其转换回原始数据。因此,自动编码器训练的基本部分是生成优化的潜在空间。
现在,我们知道,在大多数情况下,潜在空间中的神经元数量比输入和输出层要少得多,但也不一定是这样。有不同类型的自动编码器,如欠完整、过完整、稀疏、去噪、收缩和变化自动编码器。在本教程中,我们只关注用于去噪的欠完整自动编码器。
自动编码器中的图层
构建自动编码器时的标准做法是设计一个编码器,并创建该网络的反向版本作为该自动编码器的解码器。因此,只要编码器和解码器网络之间存在反向关系,您就可以自由地向这些子网添加任何层。例如,如果您正在处理图像数据,您肯定会需要卷积和池层。另一方面,如果您正在处理序列数据,您可能需要 LSTM、GRU 或 RNN 单位。这里重要的一点是,你可以自由地建造任何你想要的东西。
图 3。欠完整自动编码器中的潜在空间通常比其他层窄
现在,您已经有了可以为图像降噪构建自动编码器的想法,我们可以继续学习教程,并开始为我们的图像降噪模型编写代码。对于本教程,我们选择做我们自己对 TensorFlow 的官方教程之一,自动编码器简介,我们将使用人工智能社区成员中非常受欢迎的数据集: 时尚 MNIST 。
下载时尚 MNIST 数据集
时尚 MNIST 由位于德国柏林的欧洲电子商务公司 Zalando 设计和维护。时尚 MNIST 由 60,000 幅图像的训练集和 10,000 幅图像的测试集组成。每个示例都是 28×28 灰度图像,与来自 10 个类别的标签相关联。“时尚 MNIST”包含服装商品的图像(如图 4 所示),它被设计为包含手写数字的 MNIST 数据集的替代数据集。我们选择时尚 MNIST 仅仅是因为 MNIST 已经在许多教程中被过度使用。
下面几行导入 TensorFlow 和 load Fashion MNIST:
现在,让我们用数据集的样本生成一个网格,代码如下:
我们的输出显示了测试数据集中的前 50 个样本:
图 4。显示时尚 MNIST 测试数据集中前 50 个样本的 5x10 网格
处理时尚 MNIST 数据
为了计算效率和模型可靠性,我们必须对我们的图像数据应用最小最大归一化,将值范围限制在 0 和 1 之间。由于我们的数据是 RGB 格式的,最小值是 0,最大值是 255,我们可以用下面几行进行最小最大规范化操作:
我们还必须调整 NumPy 数组的形状,因为数据集的当前形状是(60000,28,28)和(10000,28,28)。我们只需要添加一个具有单一值的第四维(例如,从(60000,28,28)到(60000,28,28,1))。第四维几乎可以证明我们的数据是灰度格式的,用一个值表示从白色到黑色的颜色信息。如果我们有彩色图像,那么我们就需要第四维中的三个值。但是我们所需要的是包含单一值的第四维,因为我们使用灰度图像。以下代码行可以做到这一点:
让我们用下面几行来看看 NumPy 数组的形状:
输出: (60000,28,28,1)和(10000,28,28,1)
向图像添加噪声
请记住,我们的目标是建立一个能够对图像进行降噪的模型。为了做到这一点,我们将使用现有的图像数据,并将它们添加到随机噪声中。然后,我们将原始图像作为输入,含噪图像作为输出。我们的自动编码器将学习干净的图像和有噪声的图像之间的关系,以及如何清洁有噪声的图像。因此,让我们创建一个噪声版本的时尚 MNIST 数据集。
对于此任务,我们使用 tf.random.normal 方法向每个数组项添加一个随机生成的值。然后,我们将这个随机值乘以一个 noise_factor,你可以随意使用它。以下代码向图像添加了噪声:
我们还需要确保数组项的值在 0 到 1 的范围内。为此,我们可以使用 tf.clip_by_value 方法。 clip_by_value 是一种张量流方法,它剪切最小-最大范围之外的值,并用指定的最小或最大值替换它们。以下代码将值截取到范围之外:
现在我们已经创建了数据集的正则化和噪声版本,我们可以检查它的外观:
图 5。显示清晰和嘈杂图像样本的 2x5 网格
正如你所看到的,几乎不可能理解我们在嘈杂的图像中看到的东西。然而,我们的自动编码器会神奇地学会清理它。
构建我们的模型
在 TensorFlow 中,除了顺序 API 和函数 API 之外,还有第三种构建模型的选择:模型子类化。在模型子类化中,我们可以自由地从头开始实现任何东西。模型子类化是完全可定制的,使我们能够实现我们自己的定制模型。这是一个非常强大的方法,因为我们可以建立任何类型的模型。但是,它需要基本的面向对象编程知识。我们的自定义类将继承 tf.keras.Model 对象。它还需要声明几个变量和函数。不过,这没什么可怕的。
另请注意,由于我们处理的是图像数据,因此构建卷积自动编码器会更有效,如下所示:
图 6。卷积自动编码器示例
要构建模型,我们只需完成以下任务:
- 创建一个扩展 keras 的类。模型对象
- 创建一个 init 函数来声明用顺序 API 构建的两个独立的模型。在它们内部,我们需要声明可以相互反转的层。一个 Conv2D 层用于编码器模型,而一个 conv 2d 转置层用于解码器模型。
- 创建一个调用函数,告诉模型如何通过 init 方法使用初始化的变量处理输入:
- 我们需要调用初始化的编码器模型,该模型将图像作为输入
- 我们还需要调用初始化的解码器模型,该模型将编码器模型(已编码)的输出作为输入
- 返回解码器的输出
我们可以用下面的代码实现所有这些:
让我们用一个对象调用来创建模型:
配置我们的模型
对于这项任务,我们将使用 Adam 优化器和模型的均方误差。我们可以很容易地使用编译功能来配置我们的自动编码器,如下所示:
最后,我们可以通过输入有噪声和干净的图像来运行我们的模型 10 个时期,这将需要大约 1 分钟来训练。我们还使用测试数据集进行验证。以下代码用于训练模型:
图 7。深度卷积自动编码器训练性能
使用我们训练有素的自动编码器降低图像噪音
既然我们已经训练了我们的自动编码器,我们可以开始清理噪声图像。注意,我们可以访问编码器和解码器网络,因为我们是在 NoiseReducer 对象下定义它们的。
因此,首先,我们将使用一个编码器来编码我们的噪声测试数据集(x _ test _ noisy)。然后,我们将把编码器的编码输出送入解码器,以获得干净的图像。以下代码行完成这些任务:
让我们绘制前 10 个样本,进行并列比较:
第一行是有噪声的图像,第二行是干净的(重建的)图像,最后,第三行是原始图像。查看清理后的图像与原始图像有何相似之处:
图 5。一个 3x10 的网格,显示干净和有噪声的图像样本以及它们的重建副本
恭喜
你已经建立了一个 autoencoder 模型,它可以成功地清理非常嘈杂的图像,这是它以前从未见过的(我们使用了测试数据集)。显然有一些未恢复的失真,例如右边第二个图像中缺失的拖鞋底部。然而,如果你考虑噪声图像的变形程度,我们可以说我们的模型在恢复失真图像方面相当成功。
在我的脑海中,你可以-例如-考虑扩展这个自动编码器,并将其嵌入到照片增强应用程序中,这可以增加照片的清晰度和清晰度。
订阅邮件列表获取完整代码
如果你想访问 Google Colab 的全部代码,并访问我的最新内容,请订阅邮件列表:
现在就订阅
喜欢这篇文章
如果你喜欢这篇文章,可以考虑看看我的其他类似文章:
[## 使用 MNIST 数据集在 10 分钟内完成图像分类
towardsdatascience.com](/image-classification-in-10-minutes-with-mnist-dataset-54c35b77a38d) [## 利用生成性对抗网络在 10 分钟内生成图像
使用无监督深度学习生成手写数字与深度卷积甘斯使用张量流和…
towardsdatascience.com](/image-generation-in-10-minutes-with-generative-adversarial-networks-c2afc56bfa3b) [## 伯特和拥抱脸 10 分钟情感分析
学习预训练的自然语言处理模型的基础,伯特,并建立一个使用 IMDB 电影评论的情感分类器…
towardsdatascience.com](/sentiment-analysis-in-10-minutes-with-bert-and-hugging-face-294e8a04b671) [## TensorFlow 和 VGG19 可以帮助您将照片转换成美丽的波普艺术作品
神经风格转移基于安迪沃霍尔的门罗双联画与预训练的计算机视觉网络 VGG19,转移…
towardsdatascience.com](/tensorflow-and-vgg19-can-help-you-convert-your-photos-into-beautiful-pop-art-pieces-c1abe87e7e01)
计算机视觉的图像处理和数据增强技术
原文:https://towardsdatascience.com/image-processing-techniques-for-computer-vision-11f92f511e21?source=collection_archive---------14-----------------------
来源
图像处理是计算机视觉的一个组成部分。我们几乎总是想要调整图像的大小,增加数据,查看网格中的图像,等等。 OpenCV (开源计算机视觉)、 scikit-image 、 Pillow 是 Python 中比较流行的一些图像处理库。在本文中,我介绍了一些最常用的图像处理技术。
这是我在这篇文章中使用的 Jupyter 笔记本:https://jovian.ml/aakanksha-ns/image-processing
1)读取图像
图像表示为由像素值组成的数组。8 位图像的像素值范围从 0(黑色)到 255(白色)。根据色阶的不同,图像中有不同的通道,每个通道代表一种特定颜色的像素值。RGB(红、绿、蓝)是最常用的色标,我在示例中使用的所有图像都是 RGB 图像。
我们可以使用 OpenCV 中的imread
函数轻松读取图像数组。这里需要记住的一点是,OpenCV 默认按照 BGR 顺序读取图像。
2)裁剪
裁剪是一种广泛使用的增强技术。然而,注意不要裁剪图像的重要部分(非常明显,但是当你有太多不同大小的图像时容易被忽略)。因为图像是用数组表示的,所以裁剪相当于从数组中取出一个切片:
中心裁剪:
随机裁剪:
3)调整大小
大多数深度学习模型架构期望所有输入图像都具有相同的维度。
4)翻转图像
这是另一种非常流行的图像增强技术。这里唯一要记住的是,翻转应该对您的用例有意义。例如,如果您正在对建筑类型进行分类,您不会在测试集中遇到任何倒置的建筑,因此在这种情况下进行垂直翻转是没有意义的。
垂直翻转:
水平翻转:
5)旋转图像:
在大多数情况下,可以将图像旋转一个小角度。在某些情况下,这种天真的做法可能会改变图像的整个方向,就像这样:
因此,更好的旋转方式是使用 OpenCV 进行仿射变换。仿射变换保留了共线性和距离比(例如:即使在变换后,线段的中点仍然是中点)。您也可以使用BORDER_REFLECT
标志来填充边框。
6)更改亮度和对比度:
这包括对每个像素应用以下函数:
这里α(> 0)称为增益,β称为偏置,这些参数据说分别控制对比度和亮度。因为我们使用数组表示图像,所以可以通过遍历数组将该函数应用于每个像素:
然而,对于较大的图像(如下图所示),这可能需要一段时间,因此您需要使用优化的库函数:
增加对比度
增加亮度
7)显示包围盒:
目标检测是一个非常流行的计算机视觉问题,涉及到寻找包围感兴趣的目标的包围盒。在图片上显示包围盒可以帮助我们直观地检查问题和需求。在处理这些问题时要记住的一件事是,如果你打算翻转图像,确保你也相应地翻转了框坐标。这里有一个简单的方法来显示图像及其边界框。
8)在网格中显示多个图像:
我们通常希望一次检查多幅图像。使用 matplotlib 中的子情节可以很容易地做到这一点。
9)将图像转换为黑白图像:
虽然没有在计算机视觉中广泛使用,但知道如何将彩色图像转换为灰度图像还是很不错的。
10)模糊:
这项技术有助于使您的模型对图像质量问题更加稳健。如果一个模型可以在模糊的图像上表现良好,这可能表明该模型总体上表现良好。
当然,还有很多针对特定问题的图像处理技术。例如,对于自动驾驶汽车,您可能想要标记道路上的其他汽车,并从各种角度查看它。但是,对于大多数问题,上述函数应该是有用的!
使用 Streamlit 进行图像处理
原文:https://towardsdatascience.com/image-processing-using-streamlit-d650fb0ccf8?source=collection_archive---------16-----------------------
使用 OpenCV 了解 Streamlit
作者图片
在本文中,我们将了解如何将 Streamlit 与图像处理技术和对象检测算法结合使用。我假设您已经在系统上安装并运行了 Streamlit。如果你不知道如何从 streamlit 开始,那么你可以参考我的以前的文章,在那里我简单地解释了一下。
我还在 streamlit 共享上部署了这个 Streamlit 应用程序。在进一步阅读这篇文章之前,你可以先看一下。
以下是链接:使用 Streamlit 的图像处理
现在让我们深入研究代码。
首先导入必要的库
import streamlit as st
from PIL import Image
import cv2
import numpy as np
我已经创建了 main 函数,这是代码的起点。在这个函数中,你会看到一个我创建的侧边栏。Streamlit 为您提供了边栏选择框功能,可以轻松创建一个。在侧边栏中,我添加了一些值,每个值都与一个函数相关联。当用户点击它们中的任何一个时,相应的功能被触发。默认情况下,选择第一个值,即 welcome 字符串,if 语句调用 Welcome()函数。
def main():selected_box = st.sidebar.selectbox(
'Choose one of the following',
('Welcome','Image Processing', 'Video', 'Face Detection', 'Feature Detection', 'Object Detection')
)
if selected_box == 'Welcome':
welcome()
if selected_box == 'Image Processing':
photo()
if selected_box == 'Video':
video()
if selected_box == 'Face Detection':
face_detection()
if selected_box == 'Feature Detection':
feature_detection()
if selected_box == 'Object Detection':
object_detection()if __name__ == "__main__":
main()
作者图片
下图显示了欢迎页面。让我们看看欢迎函数。
作者图片
def welcome():
st.title('Image Processing using Streamlit')
st.subheader('A simple app that shows different image processing algorithms. You can choose the options'
+ ' from the left. I have implemented only a few to show how it works on Streamlit. ' +
'You are free to add stuff to this app.')
st.image('hackershrine.jpg',use_column_width=True)
用 st.title 你可以创建一个粗体标题,用 st.subheader 你可以有一个较低字体大小的粗体。使用 st.image,您可以在 streamlit 应用程序上显示任何图像。请确保将列宽设置为 true,以使其合适。
接下来,我们在侧边栏中有图像处理部分。在这里,我们将看到阈值,边缘检测和轮廓。为了方便用户,我使用了一个滑块来改变阈值。要制作一个交互式滑块,只需在 streamlit 中编写 st.slider。在 OpenCV 的帮助下,我们正在进行 RGB 到灰度的转换,然后使用 OpenCV 中的阈值函数。我们在阈值函数中传递滑块的值。所以当我们移动滑块时,值会改变并存储在 thresh1 中。然后我们使用 st.image 来显示 thresh1 图像。确保将 clamp 设置为 True。
接下来,我们有一个显示该图像的条形图。您可以使用 streamlit 中的条形图功能来完成此操作。传递的值是用 cv2.calchist 函数计算的。您可以使用不同的直方图和图来分析您的图像。
然后,我们有精明的边缘检测技术。我做了一个按钮,当用户点击它时,它运行算法并显示输出。您可以简单地通过编写 st.button 和 if 语句来创建一个按钮。如果你写了边缘检测代码。
对于轮廓,我再次使用滑块来改变图像,使轮廓发生变化。在 OpenCV 中,您可以在传递图像的地方找到轮廓和绘制轮廓。
def photo():st.header("Thresholding, Edge Detection and Contours")
if st.button('See Original Image of Tom'):
original = Image.open('tom.jpg')
st.image(original, use_column_width=True)
image = cv2.imread('tom.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
x = st.slider('Change Threshold value',min_value = 50,max_value = 255)ret,thresh1 = cv2.threshold(image,x,255,cv2.THRESH_BINARY)
thresh1 = thresh1.astype(np.float64)
st.image(thresh1, use_column_width=True,clamp = True)
st.text("Bar Chart of the image")
histr = cv2.calcHist([image],[0],None,[256],[0,256])
st.bar_chart(histr)
st.text("Press the button below to view Canny Edge Detection Technique")
if st.button('Canny Edge Detector'):
image = load_image("jerry.jpg")
edges = cv2.Canny(image,50,300)
cv2.imwrite('edges.jpg',edges)
st.image(edges,use_column_width=True,clamp=True)
y = st.slider('Change Value to increase or decrease contours',min_value = 50,max_value = 255)
if st.button('Contours'):
im = load_image("jerry1.jpg")
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,y,255,0)
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
img = cv2.drawContours(im, contours, -1, (0,255,0), 3)
st.image(thresh, use_column_width=True, clamp = True)
st.image(img, use_column_width=True, clamp = True)
我们接下来要看的是人脸检测。对于人脸检测,我使用的是哈尔级联文件。使用 cascadeClassifier,我们将加载 XML 文件。我们有 detectMultiScale 函数,我们传递图像来寻找图像中的人脸。如果我们找到图像,然后在脸部周围画一个矩形。如果您希望保存图像,那么您可以使用写入功能。然后,我们使用 Streamlit 使用 st.image 显示图像。
def face_detection():
st.header("Face Detection using haarcascade")
if st.button('See Original Image'):
original = Image.open('friends.jpeg')
st.image(original, use_column_width=True)
image2 = cv2.imread("friends.jpeg")face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
faces = face_cascade.detectMultiScale(image2)
print(f"{len(faces)} faces detected in the image.")
for x, y, width, height in faces:
cv2.rectangle(image2, (x, y), (x + width, y + height), color=(255, 0, 0), thickness=2)
cv2.imwrite("faces.jpg", image2)
st.image(image2, use_column_width=True,clamp = True)
我想展示的最后一部分是对象检测。我已经使用挂钟和眼哈尔级联文件做对象检测。与人脸检测类似,我们将加载 XML 文件并使用检测多尺度函数。如果找到了相应的对象,我们将在该对象周围绘制一个矩形。下图显示了输出。
def object_detection():
st.header('Object Detection')
st.subheader("Object Detection is done using different haarcascade files.")
img = load_image("clock.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
clock = cv2.CascadeClassifier('haarcascade_wallclock.xml')
found = clock.detectMultiScale(img_gray,
minSize =(20, 20))
amount_found = len(found)
st.text("Detecting a clock from an image")
if amount_found != 0:
for (x, y, width, height) in found:
cv2.rectangle(img_rgb, (x, y),
(x + height, y + width),
(0, 255, 0), 5)
st.image(img_rgb, use_column_width=True,clamp = True)
st.text("Detecting eyes from an image")
image = load_image("eyes.jpg")
img_gray_ = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
img_rgb_ = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
eye = cv2.CascadeClassifier('haarcascade_eye.xml')
found = eye.detectMultiScale(img_gray_,
minSize =(20, 20))
amount_found_ = len(found)
if amount_found_ != 0:
for (x, y, width, height) in found:
cv2.rectangle(img_rgb_, (x, y),
(x + height, y + width),
(0, 255, 0), 5)
st.image(img_rgb_, use_column_width=True,clamp = True)
检测挂钟|作者图片
检测眼睛|作者图片
你可以在我的 Github 上找到代码。我还为此制作了一个视频。
关于图像处理和机器学习的进一步阅读,可以参考这篇资料丰富的文章。
https://Neptune . ai/blog/what-image-processing-techniques-is-actual-used-in-the-ml-industry
和平!
用 Python 进行图像处理
原文:https://towardsdatascience.com/image-processing-with-python-5b35320a4f3c?source=collection_archive---------13-----------------------
字节、采样和过滤器——编码图像处理介绍
照片由 Giulio Magnifico 在 Unsplash 上拍摄
图像处理通常是一项不太频繁的任务。当然,这将在很大程度上取决于我们的角色——但即使在数据科学或机器学习等繁重的分析角色中,图像处理也很常见。
在机器学习中,我们可能需要拍摄一些图像,并根据它们的内容对它们进行分类——我们可能需要对图像进行下采样——将它们从原始的 2,4,8K 分辨率图像转换为更易于管理的大小。
还有许多其他学科需要少量的图像处理,比如 web 开发,或者更多,比如图像处理工程师。
我们将触及图像数据在计算机中如何组织的基础知识,然后探索几种不同的图像处理方法:
**Images as Arrays** - intro to images in code and their representation in bits and bytes **Resize and Scaling** - resizing our images, save on compute with downsampling
**Filters** - how to build an overly complicated Photoshop with significantly less features
我们将在整篇文章中使用 Numpy 为了快速简便地处理图像,我建议使用另一个名为 PIL 的库。
PIL 代码也将包括在内,但我们不会关注它——因为它将一切都抽象到我们无法关注实际过程的程度。
图像作为数组
在整篇文章中,我们将使用文章封面图像。
在原始格式中,此图像由 7207 x 4801 像素组成。
我们将把这个图像表示为一个数组。在这个阵列中,我们将有三个“通道”(层)——代表红色、绿色和蓝色。
所有这些给了我们图像数组中包含的总共7207*4801*3 = **103,802,421**
个数值。
现在,让我们编写一些代码来将我们的图像转换成一个 Numpy 数组:
from PIL import Image
import numpy as npimg = Image.open(PATH)
img.load()
**img_array** = np.asarray(img, dtype='int32')
我们的数组形状匹配我们预期的宽度和高度,加上三个颜色通道 RGB。请注意,尽管我们将图像维度称为宽度高度,但在数组维度中,我们交换了顺序—给出高度宽度。
我们还可以看看组成数组的值:
我们数组中的所有值从最小值的零变化到最大值的 255。使用这个范围是因为我们的每种颜色都是一个 8 位字节。每个位可以是 0 或 1。
这给了我们总共2^8 = **256**
种可能的组合,范围从 0 到 255。
哪里—
0 = 00000000
1 = 00000001
2 = 00000010
3 = 00000011
...
254 = 01111111
255 = 11111111
正如我们所见,0
显示所有字节关闭,而255
显示所有字节打开——这是有意义的,因为 0 对应于图像中相应颜色的最小值,255 对应于最大值。让我们试着打开和关闭我们的颜色。
red1 = np.copy(img_array)
**red1[:,:,*0*] = 255** # this is **on** (top row below)plt.imshow(red0)blue0 = np.copy(img_array)
**blue0[:,:,*2*] = 0** # this is **off** (bottom row below)
plt.imshow(blue0)
当设置为 255 【红色 1、绿色 1、蓝色 1】时,顶行显示我们的 RGB 颜色。当设置为 0 [red0,green0,blue0]时,最下面一行是我们的 RGB 颜色。
调整大小和缩放
在机器学习应用中这样做有两个主要原因:
- 下采样节省计算成本——用 8K 分辨率的图像训练图像分类器将需要令人印象深刻的设置——360 p 更真实一些。
- 增加数据集的大小—(我们必须在分类中坚持相同的图像大小,但这并不意味着我们不能使用不同分辨率的图像)
这两个原因需要相同的方法,我们采用原始阵列,以最大限度地保留信息的方式将像素组压缩为单个像素。
这里,我们将通过取多个像素的平均值来创建一个新像素,从而对图像进行缩减采样。
这里,我们通过用一个像素替换九个像素来减少值的总数,这是前九个像素的平均值。
我们使用sklearn.measure.block_reduce
来执行这个操作,使用np.mean
来指定使用平均值的归约操作。
为了看出明显的区别,我们将图像缩减采样 20 倍。
downsample = 20# first, change to 0-1
ds_array = img_array/255r = skimage.measure.block_reduce(ds_array[:, :, 0],
(downsample, downsample),
np.mean)
g = skimage.measure.block_reduce(ds_array[:, :, 1],
(downsample, downsample),
np.mean)
b = skimage.measure.block_reduce(ds_array[:, :, 2],
(downsample, downsample),
np.mean)ds_array = np.stack((r, g, b), axis=-1)
我们可以使用imshow
比较原始图像和缩减像素采样后的图像,这样我们可以得到:
原始图像(左上)和缩减像素采样图像,使用因子 20(右上)、100(左下)和 500(右下)。
再一次,从 ML/数据科学的角度来说,(对于任何一台普通的计算机来说)在超过 100 万个数组的大集合上训练一个分类器是不可能的。
但是经过 20 倍的缩减采样后,我们的数组总共只有241 * 361 * 3 = **261,003**
个值——这是一个易于管理的大小。
过滤
这是有趣的部分——我们将编写一些代码来修改我们的图片——比如 Instagram 滤镜。
小插图
晕影本质上是像素向图像外边缘变暗,看起来像这样:
约翰·埃尔南德斯在 Unsplash 上拍摄的照片
不久前,这是急躁的青少年试图掩盖他们平庸的摄影技巧的最佳效果。
这是一个有趣的效果。我们所需要的是一个函数,我们可以应用到我们的数组,将减少向外边缘的值。
为了达到这个效果,我们从一个高斯函数开始——我们将为每个整数的行和列输入一个值。
这段代码生成两个如下所示的高斯分布:
两个高斯函数,较暗的线代表行值,其中较大的 x 值意味着较少的晕影。对于较亮的线条,较大的 y 值会导致列方向应用较少的晕影。
然后,我们将这些分布标准化,并将其应用到我们的图像中。将应用于白色正方形的两个分布可视化,可以得到以下结果:
纯高斯晕影效果(左)和应用平方根函数的相同函数(右)——我们将使用平方根晕影滤镜。
这种效果可以很容易地应用于阵列:
将这些代码应用到我们的不整洁的图片上,我们就能得到前卫的青少年/潮人 Instagram 效果:
色彩平衡
我们可以做的另一件很酷的事情是单独修改图像中的颜色层。
通过单独访问每个颜色层并增加/减少层值,可以轻松实现简单的平衡。
当我们这样做的时候,我们会不经意地创建超出 0-255 范围的数字。为了处理这些问题,我们使用一个简单的clamp
函数将数组压缩回正确的范围,如上所示。
摆弄col_bal
中的r, g, b
值可以产生一些有趣的效果:
聪明
我们使用完全相同的方法来修改图像亮度。我们需要做的就是用完全相同的值修改我们的 RGB 值:
调整图像亮度,原稿,+50,+100(从左至右)。
把所有的放在一起
最后,我们可以把所有这些放在一起。一些带有晕影效果的色彩平衡可以非常容易地建立一些很酷的效果。
向所有摄影师/设计人员道歉——请在评论中随意抨击我的图像编辑技巧。
最后几点
在本文中,我们一直避免使用 PIL 库。PIL 是 Python 中图像处理的专家——所以不提到它,这篇文章就不完整。
PIL 是一个优秀的库,专门用于 Python 中的图像处理。有了它,我们可以将需要几行 Numpy 代码的工作压缩到一个函数中。
然而,PIL 把一切都抽象到了不可能理解其机制的程度——因此我们大量使用 Numpy。
我们已经介绍了以位/字节为单位的图像背后的基础知识,如何对图像进行缩减采样,以及如何编辑带有晕影和色彩平衡的图像。
如果你有兴趣学习更高级的概念,我强烈推荐 Datacamp 上的这门课程(第一部分是免费的)。
我希望你从文章中学到了一些东西,如果你有任何问题或建议,请在 Twitter 或在下面的评论中联系!
感谢阅读!
进一步阅读
ML 行业实际使用的图像处理技术有哪些 (2020),Neptune.ai
OpenCV,Tensorflow,Pillow 之间的切换?等等!!!
原文:https://towardsdatascience.com/image-read-and-resize-with-opencv-tensorflow-and-pil-3e0f29b992be?source=collection_archive---------8-----------------------
深入探讨 OpenCV、Tensorflow 和 Pillow 在 JPEG 图像读取和大小调整方面的差异,以及如何使它们保持一致。
现代计算机视觉(CV)目前是一个热门的研究领域,主要涉及图像处理。为此,需要使用一个框架来打开这些图像,对它们进行一些处理。
在框架快速发展的今天,每个框架都有自己处理图像的方式,每个框架都有自己的规范。因此,在一个框架中开发的 CV 解决方案在另一个框架中可能不会像预期的那样工作。博客“Tensorflow 的 tf.image.resize 如何偷走了我生命中的 60 天”(https://hacker noon . com/How-tensor flow-TF-image-resize-skeet-60-days-of-my-life-ABA 5 EB 093 f 35)就是这类情况的绝佳例子。找出问题所在可能需要几天的时间,而且可能会大大延迟项目。
本博客将详细讨论一个这样的用例,其中 OpenCV 和 Tensorflow 展示了读取 JPEG 图像和调整其大小的差异。它还将展示一种让它们持续工作的方法。此外,感谢 Tejas Pandey 帮助实现 OpenCV 和 Tensorflow 之间的一致性。
请注意,我是英特尔的员工,博客中的所有信息和观点都是我个人的,并不代表我的雇主。
我们先用两个框架 Tensorflow 1.x 和 OpenCV 打开一个简单的狗的 JPEG 图像。
让我们开始吧。我们将用 python 导入一些基本的库。
%tensorflow_version 1.x
import cv2
import matplotlib.pyplot as plt
import numpy as np
from skimage import io
import tensorflow as tf
让我们使用 OpenCV 打开一个图像。
image_path = 'dog2.JPG'def plt_display(image, title):
fig = plt.figure()
a = fig.add_subplot(1, 1, 1)
imgplot = plt.imshow(image)
a.set_title(title)image = cv2.imread(image_path)
plt_display(image, 'cv2-BGR')
上图有些奇怪。它看起来不像原来的。嗯,是的,确实如此。在深入研究这个之前,首先让我们了解图像中的颜色是如何表现的。通常,图像有三个颜色通道红色、绿色和蓝色(RGB ),这产生了像素中的颜色。这些通道的顺序会改变像素的颜色,因为像素总是将第一个通道解释为红色,第二个通道解释为绿色,第三个通道解释为蓝色。
现在我们已经了解了颜色通道在图像中是如何工作的,让我们看看为什么我们在上面看到了不同的颜色图像。当我们使用 OpenCV 打开图像时,默认情况下 OpenCV 会以蓝色、绿色和红色通道(BGR)打开图像。这很好,但当我们显示图像时,像素误解了通道(即像素混淆,将蓝色解释为红色,反之亦然)。这就是为什么我们在图像中得到上述错误的颜色。
那么如何解决这个问题呢?打开图像后,我们将图像从 BGR 转换为 RGB。我们通过使用 OpenCV 标志 COLOR_BGR2RGB 来实现这一点。下面的代码展示了如何用 OpenCV 正确地打开一个图像。
image_cv = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
print(image_cv.dtype)
print(np.max(image_cv))
plt_display(image_cv, 'cv2-RGB')
让我们现在用 Tensorflow 打开一个图像,看看是否得到相同的结果。在 Tensorflow 中打开 JPEG 图像需要两步。第一步是打开图像,第二步是执行 JPEG 解码,因为 JPEG 是压缩图像。
print("Tensorflow version: ", tf.__version__)
image_tf = tf.io.read_file(image_path)image_tf = tf.image.decode_jpeg(image_tf, channels=3)with tf.Session() as sess:
image_tf = image_tf.eval()print(image_tf.dtype)
plt_display(image_tf, 'TF')
目测,从 OpenCV 和 Tensorflow 读取的图像看起来是一样的。但是他们是吗?要看有什么区别,让我们把两者相减。如果两者相同,我们应该看到一个完全黑色的图像。如果不是,那么我们会看到一些不同颜色的像素。
image_diff = np.abs(image_cv- image_tf)
plt_display(image_diff, 'OpenCV-TF')
哇哦。这是一个巨大的差异。哪里出了问题?这种差异是由于 OpenCV 默认使用 JPEG 图像的整数精确解压缩。相比之下,TensorFlow 默认使用离散余弦变换。这种类型的解码是不准确的,所以为了使它和 OpenCV 一样,我们需要使用整数精确解压缩来解码它。这可以通过设置参数 dct_method='INTEGER_ACCURATE '来实现,如下所示。
image_tf = tf.io.read_file(image_path)image_tf = tf.image.decode_jpeg(image_tf, channels=3, dct_method='INTEGER_ACCURATE')with tf.Session() as sess:
image_tf = image_tf.eval()plt_display(image_tf,'TF_INT_ACC')
现在减去 OpenCV 图像,我们得到一个黑色的图像。这意味着我们的 TF 和 OpenCV 现在在读取图像时是一致的。这是一个重要的步骤,因为你阅读图像的方式的一个小的改变将会在随后的图像处理中产生显著的差异。
image_diff = np.abs(image_cv- image_tf)
plt_display(image_diff, 'OpenCV-TF')
现在,既然我们已经用 OpenCV 和 TensorFlow 一致地读取了图像,让我们用这些框架来尝试调整图像的大小。我们将首先从 OpenCV resize 开始。
# resizing the image with OpenCV
print("image shape before resize:", image_cv.shape)
image_cv_resized = cv2.resize(image_cv,(300,300))
print("image shape after resize:", image_cv_resized.shape)
print("image dtype: ", image_cv_resized.dtype)
我们已经完成了 OpenCV 的大小调整。现在让我们调整张量流的大小。张量流与张量一起工作。它的调整大小方法需要一个 4D 张量并返回一个 4D 张量输出。因此,我们必须首先将图像从三个扩展到四个。然后调整图像大小,将尺寸压缩回 3。由于我们的图像仍然是一个张量,我们将创建并运行一个 Tensorflow 会话,以 NumPy 格式获取调整后的图像。一旦我们有了 NumPy 映像,我们就将其转换为 uint8 类型,因为默认情况下,session 会将 NumPy 转换为 float32 类型。这使得类型与 OpenCV 不一致。如下图所示-
# resizing the image with tensorflow 1.x
print("image shape before resize:", image_tf.shape)
print("image dtype: ", image_tf.dtype)#This function takes in a 4D input. Hence we will have to expand the image and then squeeze back to three dimensions before we can use it as an image.
image_tf_4D= tf.expand_dims(image_tf,0)# doing bilinear resize
image_tf_resized_4D = tf.image.resize_bilinear(
image_tf_4D,
(300,300)
)#squeezing back the image to 3D
image_tf_resized = tf.squeeze(image_tf_resized_4D)#Above is still a tensor. So we need to convert it to numpy. We do this by using tf session.
with tf.Session() as sess:
image_tf_resized = image_tf_resized.eval()print("image shape after resize:", image_tf_resized.shape)
print("image dtype: ", image_tf_resized.dtype)#Since it is in float32 format, we need to convert it back to uint8.
image_tf_resized = image_tf_resized.astype(np.uint8)
print("image dtype after conversion uint8: ", image_tf_resized.dtype)
现在让我们来看看 OpenCV 和 TF 的调整大小的图像之间的区别。
image_resized_diff = np.abs(image_cv_resized- image_tf_resized)
plt_display(image_resized_diff, 'Resized OpenCV2 - Resized TF')
我们再次看到两个框架在调整大小方法上的巨大差异。即使我们在两个框架中一致地打开图像,resize 方法给出了不同的结果。默认情况下,OpenCV resize 方法使用双线性变换。我们对 Tensorflow 使用了相同的双线性方法。尽管如此,我们最终得到了不同的结果。
发生这种情况是因为 OpenCV 在调整大小时向图像添加了半像素校正。而 Tensorflow 默认不会。这增加了调整大小方法输出的差异。
为了解决这个问题,TensorFlow 双线性调整大小中有一个参数可以进行半像素校正。如下图所示-
image_tf_4D= tf.expand_dims(image_tf,0)# doing bilinear resize with half pixel correction
image_tf_resized_hpc_4D = tf.image.resize_bilinear(
image_tf_4D,
(300,300),
half_pixel_centers=True
)image_tf_resized_hpc = tf.squeeze(image_tf_resized_hpc_4D)with tf.Session() as sess:
image_tf_resized_hpc = image_tf_resized_hpc.eval()image_tf_resized_hpc = image_tf_resized_hpc.astype(np.uint8)
image_resized_diff = np.abs(image_cv_resized- image_tf_resized_hpc)
plt_display(image_resized_diff, 'Resized OpenCV2 - Resized TF')
现在我们有了预期的结果。最后,我们让 Tensorflow 和 OpenCV 在读取和调整图像大小方面保持一致。
一个计算机视觉领域的新人尝试这些功能将更有可能犯这些错误,并会有一个糟糕的开始。网上的教程不详细讨论这个。我创建这个博客是为了向人们展示,从一个框架到另一个框架,像我讨论的这样的小细节需要检查并保持一致。否则,它们将在不同的框架中产生不同的结果,并对它们的工作产生不利影响。
另一个广泛使用的框架是 Pillow (PIL)。让我们看看 PIL 和 OpenCV 的区别。我们将首先使用 PIL 读取图像。
from PIL import Imageorig_image_pil = Image.open(image_path)
image_pil = np.asarray(orig_image_pil)
print(image_pil.dtype)
plt_display(image_pil, 'PIL')
现在我们来看看 PIL 打开的 JPEG 图像和 OpenCV 有什么不同。
image_cv_pil_diff = np.abs(image_cv - image_pil)
plt_display(image_cv_pil_diff, 'CV-PIL-DIFF')
正如我们从 TF 和 OpenCV 中获得的经验所预期的那样,这两个框架再次以不同的方式打开图像。默认情况下,PIL 不提供任何使用整数精确解压缩的方法。然而,人们可以自己编写解码器来解码图像。但是在这个博客中,我只是展示了不同之处,如果可能的话,如果图书馆提供支持的话,让它们保持一致。
由于这些框架对图像的读取方式不同,所以当我们调整大小时,这种差异将会扩大。如下图所示-
image_pil_resized = orig_image_pil.resize((300,300), resample=Image.BILINEAR)
image_pil_resized = np.asarray(image_pil_resized)#making sure we have the same dtype as OpenCV image.
print(image_pil_resized.dtype)
image_cv_pil_resize_diff = np.abs(image_cv_resized - image_pil_resized)
plt_display(image_cv_pil_resize_diff, 'Resized CV- Resized PIL')
因此,我们得到了调整大小的预期差异。PIL 库不提供任何对半像素校正的支持,因此默认情况下不支持半像素校正。因为我们知道 TF 和 OpenCV 是一致的,所以当考虑到 PIL 和 TF 之间的差异时,我们可以期待相同的结果。
因此,当使用 PIL 框架时,必须记住,如果工作依赖于上述图像的预处理,则不能直接切换到另一个框架。这将导致意想不到的结果。
我希望这篇博客对你有用,能够让你意识到框架依赖处理。
图像识别与其他技术在预测金融市场中的比较
原文:https://towardsdatascience.com/image-recognition-vs-other-techniques-in-predicting-the-financial-market-55548d4cda4?source=collection_archive---------34-----------------------
使用 CNN 进行上电时间序列分析,以及它与其他技术的比较
这项工作是与@Vesko Karadjov 合作完成的,他总能迸发出绝妙的想法。
金融市场主要处理时间序列,围绕时间序列预测有大量的技术和工具。我们要做的是使用 CNN 进行基于回归的预测,并与一些传统方法进行比较,看看它的表现如何。
在金融领域,金融产品价格或回报预测一直是任何定量分析的共同目标。这是几十年来反复研究的。我们这里关注的是市场的波动性,具体来说,就是股市开盘前后的波动性。
问题
我们怀疑的是,盘前时段的波动性(vol)可能是开市后可能立即发生的事情的一个很好的指标。如果我们能准确地预测体积,我们也许能利用它。例如,我们从 OANDA 获取了 FTSE 100 CFD 的价格数据。
准备数据
我们要求从 2016 年 1 月到 2020 年 3 月按分钟提取汇总价格数据,其格式如下。
整个时期的时间序列都是这样的。
我们感兴趣的数据是价格的波动性,本质上是价格的标准差。由于我们使用的是有高低点数据的烛台数据,我用分钟棒线内的最高价减去最低价来估算波动率,并应用大小为 n 的滚动平均窗口。
原则上,当价格波动较大时,成交量应该较高,作为一项明智的检查,我们随机选取了一个交易日,对照当天的价格查看其成交量,以确认这一点。
为了比较所有交易日的成交量,我绘制了基于时间的成交量图。vol 是根据 5 分钟的时间窗口计算的。每个点代表过去 5 分钟的 vol,它是在 1070 天内每分钟计算的。
我们可以看到,峰值似乎以一小时为一段复制,例如 0000 和 0100、0700 和 0800 处的峰值。让我们绘制热图来检查这些值。
我们可以看到夏令时的影响,所以我们需要通过将夏令时改为 GMT 来使这种影响正常化,如下所示。
有些时期 vol 最大。热图中的实线表示在某些时候持续的高波动性,所有这些都是合理的。
- 0100:开始差价合约交易
- 0700:欧盟盘前开盘
- 0800:英国市场开盘
- 中午 12 点
- 14:30:美国市场开盘
- 1630:英国市场收盘
我们还可以看到 2020 年 3 月全天的高波动性。
如果我们想利用波动性,越高越好,因此利息最高的时间是开市时间,即 0800。如果我们可以使用开市前一小时的成交量来预测开市后 5 分钟的成交量,而开市后 5 分钟的成交量通常是一天中最高的,我们就可以围绕这个成交量进行交易。
上图显示了 2016 年至 2020 年间所有交易日的综合成交量。我们可以看到,在 0800 点开盘后,成交量往往会上升,几分钟后就会冷却下来。
我们假设开市前后的成交量之间存在相关性。因此,如果我们在开市前一小时画出成交量的平均值,在开市后 5 分钟画出成交量的平均值,我们应该有一个总体上升的趋势,事实也确实如此。
x 轴是开市前 1 小时的平均成交量,y 轴是开市后 5 分钟的平均成交量。这两者之间有相关性,但有很多差异,因此它不是一个可靠的预测。例如,基于此图表,如果我们在开市前的平均成交量为 7.5,则有理由假设我们在开市后的成交量将在 5 到 25 之间,这不是很有帮助。
有趣的是
我们基本上已经对开市前后的成交量做了线性回归分析。然而,公平地说,更复杂的技术,并证明这些技术确实是可预测的。我们将 1070 个样本分成三组:训练组、验证组和测试组。
- 列车:0–699(2018–10–09 结束)
- 验证:700–899(2019 年 7 月 25 日结束)
- 测试:900–1070(结束于 2020 年 3 月 25 日)
经典方法 1 —线性回归
由于现在有一个分割,线性回归的统计数据将与使用完整数据集生成的统计数据略有不同。
根据上面的图表,我们可以看到强相关性仍然存在,但决定系数很低,我们不能用这种技术解释开市后成交量的变化。
要提高预测,方法之一就是增加特征的数量,这就引出了多元回归。在这种情况下,我们可以利用开市前一小时的每一分钟来预测开市后的成交量。这应该会给我们一个比开市前波动率简单均值更好的结果。
所有的统计数据都有轻微的改善,但是,它仍然不是很有用。这里所有的线性回归都使用 statsmodel 库中的 OLS。
经典方法 2 —梯度推进法
执行预测(尤其是分类)时,梯度增强是标准方法,它将为您提供一个非常好的进一步改进的基准。尽管在预测连续表面时,基于梯度推进的回归结果可能不稳定。它通常以最小的努力给出合理的性能。
这里我们选择了 LightGBM 作为库。用于输入模型的特征基本上与多变量回归相同。
对于 5 个不同的交叉验证评估,该模型在我们数据样本的不同部分不是非常稳定,这表明在我们数据的不同部分中存在某种异常样本的不平衡。虽然当我们更深入地研究预测过程时,这可能是一个问题,但是对于本文的范围来说,只要我们有相同的训练集、验证集和测试集,我们就不需要担心那么多,因为这本质上是第一轮预测方法选择过程。
如前所述,我们需要在不同的技术中保持样本的一致性,因此我们只训练前 700 个样本,并预测 900 个以上的样本。
这是我们设计训练集和测试集的结果。
训练结果是令人鼓舞的,r 平方得分为 0.94,但测试集中的预测失败了。尽管它在测试集中做得不太好,但它仍然优于我们使用纯线性回归数据所做的预测。
经典方法 3 — MLP(神经网络)
当我们在预测中有如此高的方差时,神经网络可能能够描述输入特征之间的复杂关系,并发现它们之间的任何隐藏关系。
我们正在使用 FastAI 作为深度学习库来构建底层网络,目前来看,FastAI 是建立在 PyTorch 之上的。您可以描述定制的 PyTorch 模型,并将其传递给 FastAI,以获得 FastAI 提供的培训小工具。
我们使用与 LightGBM 相同的一组输入和输出,并将数据输入到一个有两个隐藏层的 MLP 网络中,每个隐藏层有 300 个神经元。
经过几次迭代训练后,我们可以得到一个与此类似的预测结果。
我们可以看到,由于市场最近的波动性,测试集中的波动性最高,因此我们的验证数据集对我们的测试数据来说不是很有代表性。然而,与以前的技术相比,它在统计方面仍然做得很好。
基于图像识别的回归
在我们继续进行基于图像的回归之前,我们的目标参数需要稍微改变一下,因为在转换过程中我们会丢失数值。因为在转换过程中,在转换成图像之前,这些值在每个时间窗口内被归一化。
为了补偿它,我们使用开市前和开市后的平均成交量之比作为我们的目标。这样我们就向神经网络提出了一个稍微不同的问题。相对于开市前的成交量,开市后的市场波动有多大?例如,如果开市前的平均成交量为 10,开市后的成交量为 50,我们要预测的目标将是 5,而不是原始的 50。
为了防止拿苹果和橙子做比较,我训练了一个 MLP 网络,它以前有相同的结构,使用相同的数据。
注意:vol 的上限是 30,因为在除法计算后会出现巨大的峰值,如下所示。
原始卷后/卷前
封顶@30 卷 _ 后/卷 _ 前
与原始值预测相比,MLP 间接预测的结果稍微差一些,但差别不大。现在我们有了一个 CNN 网络的基线来比较。
图像转换
在 Gramian Angular Field 和 pyts 库的帮助下,我们现在可以根据点之间的极坐标关系将任何时间序列转换为图像。[ 原文
在这段代码中,它每天创建一个图像,每个图像描述开市前 60 分钟的 vol。我们还将目标体积比导出为 CSV 格式,以便在 FastAI 中导入标签。
对于 CNN 网络,它实际上是使用 ResNET34 作为底层,并在顶层安装一个[1024,512]密集层,并使用简单的线性激活节点执行最终回归。
经过几次迭代训练,我们得到了这样的结果。
我们可以看到,即使预测相同的目标,基于图像的回归比 MLP 的回归执行得好得多。
下面是所有不同技术的统计数据汇总,以供比较。
我们可以看到,在预测绝对音量值时,MLP 优于所有其他方法,而在预测相对音量比时,CNN 在各个方面都优于相同的网络。
因此,当进行时间序列预测时,CNN 技术是一个突出的候选选择,尽管它在图像转换和训练方面确实需要更多的计算能力。
如果有人感兴趣,我们可以将原始的波动性结果提供给 CNN 的最后几层,但这不是本文的范围。如果你有任何其他想法或想法,请告诉我,希望你喜欢。
基于 Python、卷积神经网络的机器学习图像识别
原文:https://towardsdatascience.com/image-recognition-with-machine-learning-on-python-convolutional-neural-network-363073020588?source=collection_archive---------15-----------------------
来源:爱尔兰时间 s,波尔缪里
在预处理数据之后,是时候构建我们的模型来执行图像识别任务了。其中一种方法是使用卷积神经网络。
这篇文章是我写的关于图像处理的文章的后续。在使数据可用于图像识别任务之后,是时候创建将执行该任务的算法了。在许多用于多层感知器模型识别图像的技术中,卷积神经网络(CNN) 是一种非常有效的技术。在这篇文章中,我们将看到如何建立一个 CNN,以及如何将它应用于一个图像数据集。
当我们第一次开始构建图像识别模型时,在相对简单的数据集上训练和评估它通常是一个好主意。
我们可以执行的最简单的任务之一是手写数字识别。给定一个手写数字的图像(例如,0,1,…,9),我们希望我们的模型能够正确地分类它的数值。虽然这个任务看起来相对简单,但它实际上在现实生活中经常使用,比如从一张照片中自动提取信用卡号码。我们将用于数字识别的数据集是 MNIST 数据集,这是用于基于机器学习的数字识别的数据集。
MNIST (修改后的国家标准与技术研究所)数据库包含60000 个训练实例和10000 个测试实例。该数据库包含灰度手写数字,这些数字被调整大小以适合 20x20 像素的框,然后在 28x28 的图像中居中(用空白填充)。MNIST 数据库可以通过 Python 访问。
在本文中,我将向您展示如何使用 TensorFlow 的高级 APIkeras对您的卷积神经网络进行编码。我在本文中使用的是 tensorflow 2.0 。
预处理
1-初始化
由于每个灰度图像的尺寸为 28×28,因此每个图像有 784 个像素。因此,每个输入图像对应于 0.0 和 1.0 之间的 784 个归一化浮点值的张量。图像的标签是一个有 10 个类别的单热张量(每个类别代表一个数字)。就我们的代码而言,我们有 img_rows = 28 、 img_cols = 28 和 num_classes = 10 。因此,输入的形状为 (number_examples,img_rows,img_cols) ,因此为 60000x28x28。
另一个需要设置的重要元素是随机种子,因为我们希望在计算机生成随机数序列时保留起始点。
我们还导入了 MNIST 数据集。
**import tensorflow as tf** # tensorflow 2.0 **from** **keras.datasets** **import** mnist
import numpy as npseed=0
np.random.seed(seed) # fix random seed
tf.random.set_seed(seed)*# input image dimensions*
num_classes = 10 *# 10 digits*
img_rows, img_cols = 28, 28 *# number of pixels*
*# the data, shuffled and split between train and test sets*
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
2-重塑和重新缩放
如前所述,输入具有形状 (number_examples,img_rows,img_cols) 。然而,为了将数据用于我们的卷积神经网络,我们需要将它转换成 NHWC 格式。
NHWC 格式有一个四维的形状:
- 图像数据样本的数量(批量)
- 每个图像的高度
- 每个图像的宽度
- 每个图像的通道数
来自数据集的每个图像的高度和宽度是 img_rows 和 img_cols,而通道的数量是 1(因为图像是灰度的)。
此外,每个像素包含一个由 0 到 255 之间的整数量化的灰度值。因此,数据库被规范化为 0.0 到 1.0 之间的浮点值。在这种情况下, 0.0 对应灰度像素值 255 ( 纯白),而 1.0 对应灰度像素值 0 ( 纯黑)。
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)input_shape = (img_rows, img_cols, 1)# cast floats to single precision
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')*# rescale data in interval [0,1]*
X_train /= 255
X_test /= 255
3-投射标签向量 Y
我们需要将我们的类转换成向量。我们通过点击以下线路来实现这一点:
Y_train = keras.utils.to_categorical(Y_train, num_classes)
Y_test = keras.utils.to_categorical(Y_test, num_classes)
要对这一步有更好的解释,你应该看看这篇文章。
既然我们已经处理了数据,我们可以开始构建模型了。
卷积神经网络模型
盘旋
正如我在关于图像处理的文章结尾提到的,滤镜在图像识别中发挥着巨大的作用。我们使用过滤器来转换输入,提取特征,使我们的模型能够识别某些图像。一个非常高级的例子是曲线检测过滤器,它允许我们的模型区分有曲线的数字和没有曲线的数字。
1-过滤器
像所有的神经网络权重一样,滤波器的权重是可训练变量。我们训练我们的神经网络(通过核矩阵权重)来产生能够提取最有用的隐藏特征的过滤器。
当输入数据具有多个通道时,滤波器将为每个通道提供一个单独的内核矩阵。MNIST 数据集只有一个通道,但对于其他类型的图像数据(如 RGB),我们将训练模型以获得每个通道的核矩阵的最佳权重。
2-卷积
我们现在已经到达卷积神经网络的焦点:卷积。卷积表示我们如何将滤波器权重应用于输入数据。卷积使用的主要运算是矩阵点积,即两个矩阵的元素乘积之和。
卷积中矩阵点积的数量取决于输入数据和内核矩阵的维数,以及 步长 。步长是核矩阵沿输入数据移动时的垂直/水平偏移。
3-填充
有时,当我们像前面看到的那样做点积运算时,我们不使用行或列。为了避免这种现象,我们可以使用填充。
因此,如果我们想在卷积中使用所有的输入数据,我们可以用 0 填充输入数据矩阵。这意味着我们将完全由 0 组成的行/列添加到输入数据矩阵的边缘。因为 0 乘以任何数字都会得到 0,所以填充不会影响矩阵点积。这很重要,因为我们不想在卷积中增加任何失真。
4-卷积层
CNN 中的卷积层将多个过滤器应用于输入张量。虽然每个滤波器对每个输入通道都有一个单独的核矩阵,但滤波器卷积的总体结果是所有输入通道的卷积的总和。
向卷积图层添加更多过滤器可使图层更好地提取隐藏要素。然而,这是以额外的训练时间和计算复杂度为代价的,因为滤波器给模型增加了额外的权重。输出数据的通道数等于卷积层使用的过滤器数。
联营
虽然卷积图层提取了重要的隐藏要素,但要素的数量仍然很大。我们可以使用池来减少数据在高度和宽度维度上的大小。这允许模型执行更少的计算,并最终训练得更快。它还通过仅提取最显著的特征并忽略仅在少数例子中发现的潜在失真或不常见特征来防止过度拟合。
共用是如何工作的?
类似于卷积,我们在池中使用过滤器矩阵。然而,池过滤器没有任何权重,也不执行矩阵点积。相反,它对输入数据的子部分应用归约运算。
CNN 中通常使用的池类型被称为 最大池 。max pooling 的过滤器使用 max 运算来获得输入数据的每个子矩阵中的最大数。
多层
1-添加额外的层
像所有神经网络一样,CNN 可以受益于附加层。附加层允许 CNN 将多个滤波器堆叠在一起,用于图像数据。然而,与构建任何神经网络类似,我们需要小心添加多少额外的层。如果我们向一个模型添加太多的层,我们就有可能使模型过度适应训练数据,从而导致泛化能力非常差。此外,每增加一层都会增加计算的复杂性,并增加模型的训练时间。
2-增加过滤器
在我们的模型中,卷积层越深,我们通常会增加其过滤器的数量。在这种情况下,我们的第二卷积层有 64 个过滤器,相比之下,第一卷积层有 32 个过滤器。卷积层越深,提取的特征就越详细。例如,第一卷积层可以具有提取诸如线、边和曲线等特征的过滤器。当我们到达第二层时,卷积层的过滤器现在可以提取更多有区别的特征,例如 77°的锐角或 88°的相交曲线。
全连接层
1-全连接层
我们将大小为 1024(即层中神经元的数量)的全连接层应用于第二池层的输出数据。单位的数量多少有些随意。足够强大,但不至于过于耗费资源。全连接层的目的是在我们将数据特征转换成类之前聚集数据特征。这使得模型能够做出比我们直接将池输出转换成类更好的预测。
2-展平
我们在模型中使用的数据是 NHWC 格式的。然而,为了使用全连接层,我们需要数据是一个矩阵,其中行数代表批量大小,列数代表数据特征。这一次,我们需要在相反的方向重塑,将 T2 从 NHWC 转换成二维矩阵。
拒绝传统社会的人
1-合作改编
协同适应指的是一层中的多个神经元从输入数据中提取相同或非常相似的隐藏特征。当两个不同神经元的连接权重几乎相同时,会发生这种情况。
当全连接层有大量神经元时,更有可能发生共同适应。这可能是一个问题,原因有两个。首先,当我们有冗余的神经元计算相同的输出时,这是一种计算的浪费。第二,如果许多神经元正在提取相同的特征,它为我们的模型增加了这些特征的重要性。如果重复提取的特征仅特定于训练集,这将导致过度拟合。
2-辍学
我们最小化具有许多神经元的全连接层的共同适应的方法是在训练期间应用丢弃。在 dropout 中,我们通过将神经元值清零,在每个训练步骤中随机关闭一层神经元的一部分。
软最大层
由于 MNIST 图像可能有 10 个可能的数字,我们使用一个 10 神经元全连接层来获得每个数字类别的类别。Softmax 函数应用于类,将它们转换为每个类的概率。
构建模型
现在我们已经准备好构建我们的模型了。代码如下:
**from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten
from keras.layers import MaxPooling2D, Dropout**model = Sequential()#add model layers
model.add(Conv2D(32, kernel_size=(5, 5),
activation='relu',
input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))# add second convolutional layer with 20 filters
model.add(Conv2D(64, (5, 5), activation='relu'))
# add 2D pooling layer
model.add(MaxPooling2D(pool_size=(2, 2)))
# flatten data
model.add(Flatten())
# add a dense all-to-all relu layer
model.add(Dense(1024, activation='relu'))
# apply dropout with rate 0.5
model.add(Dropout(0.5))
# soft-max layer
model.add(Dense(num_classes, activation='softmax'))
我们将使用的型号是序列。顺序是在 Keras 中建立模型的最简单的方法。它允许你一层一层地建立模型。
我们使用 add() 方法将层附加到我们的模型上。出于介绍性示例的目的,为了简单起见,只关注密集层就足够了。每个 Dense()层都接受一个整数作为其第一个必需参数,该整数指定了神经元的数量。使用激活可选参数定义层的激活函数的类型,其输入为字符串格式的激活函数名称。例子包括 relu,tanh,elu,sigmoid,softmax。
在这个神经网络中,我们有两个卷积层,每次都有一个汇集层。然后,我们将数据展平,添加一个密集层,我们以 0.5 的速率对其应用 dropout。最后,我们添加一个密集层来为每个图像分配正确的类别。
编译模型
接下来,我们需要编译我们的模型。编译模型需要三个参数:优化器、损失和指标。
优化器控制学习速率。我们将使用' adam '作为我们的优化器。在许多情况下,Adam 通常是一个很好的优化器。亚当优化器在整个训练过程中调整学习率。
学习率决定了计算模型最佳权重的速度。较小的学习率可能会导致更准确的权重(直到某一点),但减少的是计算时间。
我们将使用‘分类交叉熵’作为我们的损失函数。这是最常见的分类选择。分数越低,表示模型的性能越好。
为了使事情更容易解释,当我们训练模型时,我们将使用‘准确性’度量来查看验证集的准确性分数。
#compile model *using accuracy to measure model performance*
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
训练模型
现在我们将训练我们的模型。为了进行训练,我们将在我们的模型上使用带有以下参数的‘fit()’函数:训练数据(X_train)、目标数据(Y_train)、验证数据和时期数。
对于我们的验证数据,我们将使用我们的数据集中提供给我们的测试集,我们已经将它分为 X_test 和 Y_test。
历元数是模型在数据中循环的次数。在一定程度上,我们运行的时代越多,模型就会改进得越多。此后,模型将在每个时期停止改进。对于我们的模型,我们将把历元的数量设置为 3。
#train the model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=3)
评估模型
现在我们已经训练了我们的模型,我们可以评估它的性能:
*# evaluate the model*
score = model.evaluate(X_test, Y_test, verbose=1)
*# print performance*
print()
print('Test loss:', score[0])
print('Test accuracy:', score[1])
因此,我们在测试集上有 99.3%的准确度和 0.025 的损失,这是非常好的。我们还可以通过增加历元数和引入批量大小来改进模型。
做预测
如果您想要查看我们的模型对测试数据做出的实际预测,我们可以使用 predict_classes 函数。我们也可以通过使用 predict 函数给出一个包含 10 个数字的数组。这些数字是输入图像代表每个数字(0-9)的概率。编号最高的数组索引表示模型预测。每个数组的总和等于 1(因为每个数字都是一个概率)。
为了显示这一点,我们将显示测试集中前 4 幅图像的预测。
注意:如果我们有新数据,我们可以将新数据输入到 predict 函数中,以查看我们的模型对新数据做出的预测。由于我们没有任何新的未知数据,我们现在将使用测试集显示预测。
#predict first 4 images in the test set
model.predict_classes(X_test[:4])
#predict first 4 images in the test set
model.predict(X_test[:4])
我们可以看到,我们的模型预测了前四幅图像的 7、2、1 和 0。
我们来对比一下这个和实际结果。
#actual results for first 4 images in test set
y_test[:4]
实际结果显示,前四个图像也是 7、2、1 和 0。我们的模型预测正确!
整体模型
import tensorflow as tf # tensorflow 2.0
from keras.datasets import mnist
import numpy as np
seed=0
np.random.seed(seed) # fix random seed
tf.random.set_seed(seed)
# input image dimensions
num_classes = 10 # 10 digitsimg_rows, img_cols = 28, 28 # number of pixels# the data, shuffled and split between train and test sets
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)# cast floats to single precision
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')# rescale data in interval [0,1]
X_train /= 255
X_test /= 255Y_train = keras.utils.to_categorical(Y_train, num_classes)
Y_test = keras.utils.to_categorical(Y_test, num_classes)from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten
from keras.layers import MaxPooling2D, Dropout
model = Sequential()#add model layers
model.add(Conv2D(32, kernel_size=(5, 5),
activation='relu',
input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
# add second convolutional layer with 20 filters
model.add(Conv2D(64, (5, 5), activation='relu'))
# add 2D pooling layer
model.add(MaxPooling2D(pool_size=(2, 2)))
# flatten data
model.add(Flatten())
# add a dense all-to-all relu layer
model.add(Dense(1024, activation='relu'))
# apply dropout with rate 0.5
model.add(Dropout(0.5))
# soft-max layer
model.add(Dense(num_classes, activation='softmax'))#compile model using accuracy to measure model performance
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])#train the model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=3)# evaluate the model
score = model.evaluate(X_test, Y_test, verbose=1)# print performance
print()
print('Test loss:', score[0])
print('Test accuracy:', score[1])#predict first 4 images in the test set
model.predict(X_test[:4])model.predict_classes(X_test[:4])#actual results for first 4 images in test set
Y_test[:4]
未完待续…
在本文中,我解决了图像识别的第二部分,即构建卷积神经网络。
我希望你在这篇文章中找到了你来这里的目的,并在接下来的图像识别之旅中与我在一起!
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名灵媒成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你使用我的链接注册,我将赚取一小笔佣金,你仍需支付 5 美元。谢谢大家!!
[## 通过我的推荐链接加入媒体-乔纳森·莱班
阅读乔纳森·莱班的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
medium.com](https://medium.com/@jonathan_leban/membership)
PS:我现在是柏克莱大学的工程硕士,如果你想讨论这个话题,请随时联系我。 这里的 是我的邮箱。
基于 Python 的机器学习图像识别,图像处理
原文:https://towardsdatascience.com/image-recognition-with-machine-learning-on-python-image-processing-3abe6b158e9a?source=collection_archive---------1-----------------------
来源:在线斯坦福课程
作为图像识别的第一步,图像处理对于创建可用于操作图像识别的神经网络的数据集是必不可少的
计算机视觉,即机器能够理解图像和视频的领域,是科技行业最热门的话题之一。机器人技术、自动驾驶汽车和面部识别都依赖于计算机视觉来工作。计算机视觉的核心是图像识别,即识别图像代表什么的任务。
在执行任何与图像相关的任务之前,几乎总是需要首先处理图像,以使它们更适合作为输入数据。在这篇文章中,我将重点介绍图像处理,特别是我们如何将图像从 JPEG 或 PNG 文件转换为我们的神经网络可用的数据。然后,在其他文章中,我将集中讨论经典卷积神经网络或 ResNet 和 SqueezeNet 等特定网络的实现。
我将使用的库是 TensorFlow 2.0,因为它提供了各种实用函数来从文件中获取图像数据,调整图像大小,甚至一次性转换大量图像。
图象档案
在我们进行任何图像处理之前,我们需要了解图像文件是如何工作的。具体来说,我们将讨论这些文件如何使用字节数据和像素来表示图像。
如果你以前看过一个图像文件的属性,它会显示图像的尺寸,即图像的高度和宽度。高度和宽度基于像素数。例如,如果图像的尺寸为 400x300(宽 x 高),则图像中的像素总数为 120000。
来源:足球,网球
足球图像的尺寸为 1710x1980(宽 1710 像素,高 1980 像素),而网球图像的尺寸为 1024x1024(宽 1024 像素,高 1024 像素)。
函数 tensorflow.io.read_file 将文件名作为其必需的参数,并以张量的形式返回文件内容,类型为 tensorflow.string 。当输入文件是图像时,tensor flow . io . read _ file的输出将是图像文件的原始字节数据。虽然原始字节输出代表图像的像素数据,但不能直接使用。让我们首先看看使用足球图像的 Python 实现。
import tensorflow values = tf.io.read_file('soccer_ball.jpg')
像素到底是什么?
像素本质上只是图像上的一个点,具有特定的阴影、颜色和/或不透明度。我们通常将一个像素表示为一个整数或多个整数。基于对图像的解释,像素采用特定的形式,通常是以下形式之一:
- :以黑白阴影的形式查看图像。每个像素都是 0-255 之间的整数,其中 0 表示全黑,255 表示全白。
- RGB :彩色图像的默认解释。每个像素由 0-255 之间的 3 个整数组成,其中整数分别代表像素的红色、绿色和蓝色强度。
- RGBA :增加了 alpha 字段的 RGB 扩展。alpha 字段表示图像的不透明度,在本实验中,我们将像素的 alpha 值表示为 0-255 之间的整数,其中 0 表示完全透明,255 表示完全不透明。
我们可以选择以任何我们想要的方式来解释一幅图像,但是通常有一种解释是最佳的。例如,我们可以用 RGB 像素值解释黑白图像,但更有效的方法是将其视为灰度图像(使用 3 倍的整数)。另一方面,用灰度像素解释彩色图像是不明智的,因为这些像素无法捕捉任何实际的颜色。
图像类型
既然已经学习了如何加载图像,那么是时候使用 TensorFlow 将图像数据解码为像素数据了。
我们使用的解码功能取决于图像的格式。对于一般解码(即解码任何图像格式),我们使用tensor flow . image . decode _ image,但是如果输入是 JPEG 图像,我们使用tensor flow . image . decode _ JPEG。
既然tensor flow . image . decode _ image可以解码任何类型的图像,你可能会奇怪为什么我们还要为其他两个解码函数费心。一个原因是,您可能希望只使用特定的图像格式,在这种情况下,只使用特定格式的解码函数会更有效,对代码清晰度也更好。
另一个原因是tensor flow . image . decode _ image支持 GIF 解码,结果输出形状为(num_frames,height,width,channels。由于函数可以返回不同形状的数据,当我们还需要用tensor flow . image . resize _ images调整图像大小时,我们就不能用tensor flow . image . decode _ image(见下一节)。
在上一节中,我们讨论了图像解释。如果我们只传递值(见前面的代码)作为一个解码函数的必需参数,我们使用的是原始图像数据中指定的解释。通常这样做是没问题的,但有时我们希望对像素使用特定的格式。
我们可以通过通道关键字参数改变解码图像的像素格式。通道参数表示每个像素的整数数量。通道的默认值为 0 ,这意味着解码功能使用原始数据中指定的解释。将通道设置为 1 指定灰度图像,而将通道设置为 3 指定 RGB 图像。对于 PNG 图像,我们也可以设置通道到 4 ,对应于 RGBA 图像。将通道设置为 2 无效。
有时,我们需要调整图像的大小来增加数据。我们用来调整像素数据大小的函数是tensor flow . image . resize _ images。它接受两个必需的参数:原始图像的解码数据和图像的新大小,图像的新大小是两个整数的元组/列表,依次表示 new_height 和 new_width 。
*def **decode_image**(filename, image_type, resize_shape, channels):
value = tensorflow.io.read_file(filename)
if image_type == 'png':
decoded_image = tensorflow.image.decode_png(value, channels=channels)
elif image_type == 'jpeg':
decoded_image = tensorflow.image.decode_jpeg(value, channels=channels)
else:
decoded_image = tensorflow.image.decode_image(value, channels=channels)
if resize_shape is not None and image_type in ['png', 'jpeg']:
decoded_image = tf.image.resize(decoded_image, resize_shape)
return decoded_image*
资料组
通常,当我们执行与图像相关的任务时,我们会处理大量的图像数据。在这种情况下,最好使用 TensorFlow 数据集,即tensor flow . data . dataset来存储所有图像。我们可以使用 from_tensor_slices 函数创建数据集。
数据集类使得使用所有图像文件执行任务变得更加容易和高效。在我们用图像文件创建数据集之后,我们需要将每个文件的内容解码成可用的像素数据。由于 decode_image 函数适用于单个图像文件,我们将需要使用数据集对象的映射*函数来将 decode_image 应用于我们数据集中的每个图像文件。*
map 函数的输出是一个新的数据集,其中每个元素现在都从原始图像文件转换为其相应的像素数据。我们使用 map 而不是使用 for 循环来手动转换每个图像文件,因为 map 跨文件并行进行图像解码,这使它成为更有效的解决方案。
*import tensorflow as tfdef **get_dataset**(image_paths, image_type, resize_shape, channels):
filename_tensor = tf.constant(image_paths)
dataset = tf.data.Dataset.from_tensor_slices(filename_tensor)
def **_map_fn**(filename):
decode_images = decode_image(filename, image_type, resize_shape, channels=channels)
return decode_images
map_dataset = dataset.map(_map_fn) # we use the map method: allow to apply the function _map_fn to all the
# elements of dataset
return map_dataset*
迭代程序
我们从数据集中提取解码图像数据的方法是通过一个 tensorflow.data.Iterator 。我们使用 get_next 函数获得一个下一个元素张量,用于数据提取。
*def get_image_data(image_paths, image_type, resize_shape, channels):
dataset = get_dataset(image_paths, image_type, resize_shape, channels)
iterator = tf.compat.v1.data.make_one_shot_iterator(dataset)
next_image = iterator.get_next()
return next_image*
执行
现在,如果我们将所有步骤收集在一起,并将其应用于足球图片,我们最终会得到以下结果:
在这种情况下,数据集仅由一幅图像组成。get_image_data 的输出是一个大小为 1980x1710x3 的数组,因为它是像素为 1980x1710 的 RGB 图片。然后我们得到每个像素的值。
一旦我们为数据集的所有图像完成了这一处理步骤,下一步就是创建我们的神经网络,并用我们的数据集来填充它,以便学习如何识别一些模式、对象、现象…
未完待续…
在本文中,我解决了图像识别的第一部分,即图像处理。这是一项重要但并不容易实现的任务。我们数据集的质量对我们神经网络算法的性能有很大的影响。
我希望你在这篇文章中找到了你来这里的目的,并在接下来的图像识别之旅中与我在一起!
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名灵媒成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,你仍然要支付 5 美元。谢谢大家!!
* [## 通过我的推荐链接加入媒体-乔纳森·莱班
阅读乔纳森·莱班的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
medium.com](https://medium.com/@jonathan_leban/membership)
PS:我目前是伯克利的工程硕士学生,如果你想讨论这个话题,请随时联系我。 这里的 是我的邮箱。*
py torch+Flask+PostgreSQL+Heroku 部署的映像建议
原文:https://towardsdatascience.com/image-recommendations-with-pytorch-flask-postgresql-heroku-deployment-206682d06c6b?source=collection_archive---------20-----------------------
用 Flask 封装一个基于 PostgreSQL/ PyTorch 的图像推荐系统,导入数据并在 Heroku 云应用平台上运行。
标识的选定图像和用于图像推荐应用的其他技术的自动检测的标识(来源:M. D. Korzec烧瓶标志许可证;Python 标志 PSF 商标使用政策; html logo cc by 3.0
在过去的几周里,我实现了一个项目到项目的图像推荐系统,该系统使用 PyTorch 预先训练的 Resnet18 卷积神经网络,通过特征向量、管理图像和 top-k 列表的数据库以及用户界面来生成可比性。
本文介绍了开发的解决方案和在 Heroku 上部署的步骤,代码可从 GitHub 获得。
我已经 为该项目设定并满足 以下 要求
1。该网络应用程序可在互联网上访问。
2。该应用程序有一个带有分页的图库,只加载页面的相关数据。
3。图库和推荐的数据是从数据库中提取的。
4。这些推荐是来自可能很大的图像集中的最相似的图像。他们必须立即出现;这意味着相似性计算离线进行。
5。画廊有回应。
6。Flask 应用程序被适当地打包,以便将来扩展/重用。
在我的上一篇文章中,部分需求已经实现为原型,尤其是第 2 和第 4 部分。GitHub为 Resnet18 PyTorch 推荐器提供了一个 Jupyter 笔记本,它将图像作为输入,并从同一图像集中给出最相似的图像。要使用 Flask 应用程序,该笔记本应该用于生成存储为 pickle 文件的 Pandas 数据帧。这些在应用程序中用作静态内容。
在这篇文章中,我们将展示如何实现所有其他需求,如何在本地使用 SQLite 数据库,如何在 Heroku 上使用 PostgreSQL,如何使用它进行分页,如何清理工作并将其部署到 Heroku,以便通过 PC 或智能手机上的浏览器在互联网上使用。
您可以通过以下链接在 Heroku 上使用一些图片访问和测试演示应用程序:
这篇文章由以下几个部分组成:
1。应用程序的功能描述
2。重新讨论图像间相似性的计算
3。描述了应用程序的封装结构
4。SQL 模型呈现
5。后台应用逻辑介绍
6。模板介绍
7。解释 Heroku 部署的所有步骤
8。得出结论
注意,对于这篇文章,我使用的是 Windows 10 机器和 Python 3.5.4。我没有提到任何异常处理。
1。应用程序的特性
为了更好地理解实现的目的,让我简单解释一下应用程序的特性。
图片集:
用户可以在自己编写的图库中浏览一组图像,显示来自初始输入集(从我的私有图像存储库中生成的一个小型测试集)的所有图像,如下所示:
分页图片库(来源:M. D. Korzec)
图库有一个用样式表创建的自定义设计。它定义了标题、鼠标反应、图像、文本覆盖和页码外观的大小。
分页:
您可以在图库中的图片下方看到数字。这些允许选择带有图像的页面。为了不一次加载所有数据,我放弃了基于 JavaScript 的方法,这种方法会导致隐藏更多信息,但它仍然会加载所有图像。对于给出的实现,只加载相关的数据。
图片推荐:
对于可能较大的图像集中的每个图像,具有相似性值的相似图像被取出并以第二路线呈现。为此,使用一对多关系来显示与所选输入最相似的图像。该方法允许改变所寻求的推荐的数量。我们使用四个,由于小,人工图像集。
里约热内卢,来自《阿德邮报》,应用程序找到的类似图片(来源:M. D. Korzec)
由于图像被截断,您可以在一个模式中进一步单击并放大它们,以查看完整的图像,该模式使用一些基本的 JavaScript。
放大图像(来源:M. D. Korzec)
响应度:
画廊和建议是有反应的,这意味着你可以在智能手机上使用它,图像选择和建议在这样的设备上是用户友好的。
智能手机上的画廊(来源:M. D. Korzec)
数据库命令:
我准备了一些测试数据,并使用字典在一个早期原型中使用。这将在导入命令中重复使用。用户可以使用控制台创建或清除数据库,并导入这种字典中描述的数据。这不仅适用于 SQLite,也适用于 Heroku 的 PostgreSQL 数据库。这些命令包含在蓝图模块中。
2。离线计算建议
在之前的一篇文章中,我详细解释了如何容易地生成相似性矩阵和前 k 个列表(对于每张图片,k 个最相似的其他图片)。在另一篇文章中介绍了一个实现。因此,检查这些职位的细节。
简而言之,我们要做的是:
- 预处理图像输入文件夹中的图像
- 用 PyTorch 的 Resnet18 为每个图像创建特征向量
- 比较特征向量并存储 top-k 列表
这是在一个 Jupyter 笔记本中完成的,我现在在其中添加了两行来存储与 pickle 文件具有相似图像名称和相似值的数据帧。这些被用作推荐应用程序的静态内容。您可以使用测试图像来检查存储库,它应该可以轻松运行。
我们需要添加的是将这些结果——实际的建议——导入到 SQL 数据库中。
对于在线处理,后端进程只查询最相似的图像来呈现正确的 html。
3。打包应用程序
使用了以下应用程序结构:
~/imageRecommender
|-- requirements.txt # All packages to run the app
|-- Procfile # file needed to use gunicorn
|-- run.py # starts the application
|-- .flaskenv # sets environment variables
|__ env # virtual environment – not in vc
|__ /imageRecommender # The application package
|-- __init__.py # Application factory method
|-- config.py # Configuration file for dev/prod
|-- models.py # Database models
|__ /commands # For import to database
|-- __init__.py # Application factory
|-- commands.py # Commands for DB creation/import
|__ /main # For routes
|-- __init__.py # Application factory
|-- routes.py # Business logic, fctns for routes
|__ /static # All static data
|__ /css # Stlyesheets
|__ /javascript # Java Script code for modal
|__ /pickles # The top-k lists in pickle format
|__ /site_imgs # The images for the app
|-- favicon.ico # The favicon
|__ /templates # All templates
|-- home.html # Home route
|-- layout.html # General layout
|-- recommend.html # Recommend route
init。py 文件使它们包含在模块中的文件夹。这些模块可以注册为蓝图。只有 init。在此应用程序中,imageRecommender 级别上的 py 文件为非空。
4。数据模型
我们将图像信息存储在 SQL 数据库中。因此,我们使用 Flask-SQLAlchemy ,这是一个帮助使用 Flask 和 SQLAlchemy 的库——它也是一个 Flask SQL 工具包,让您可以使用 SQL 和 Flask。
3.1。开始使用 SQL for Flask
支持 SQL 数据库的包是 flask-sqlalchemy ,版本 2.4.4 在 requiremets.txt 文件中。这样,我们可以将它导入到 imageRecommender 应用程序工厂 init 的工厂应用程序中。py 。
我们在本地为 Flask 应用程序配置 SQLite 数据库,并在 config.py 文件中为 Heroku 配置 Postgres 数据库(所有内容都存储在一个文件中)
根据用例(在 Heroku 上开发或生产)选择正确的配置。为了安全起见,生产中的密钥需要是唯一的,并且需要设置数据库的 URL。
3.2。数据模型
我们为图库的图像创建一个类,并为要推荐的图像创建一个相关的类。
对该模型的一些说明:
- 两个表的 id 标识符被自动且唯一地设置为默认值
- imageName 和 imageDescription 是图像元数据的字符串
- imageRecs 是与推荐的关系,这些是推荐路线所需要的
- Imagerecommendations 类包含来自查询图像的相关 ID、推荐图像的名称及其相似性值。它将图像名称的范式反规范化
我们现在可以创建数据库并导入数据。
3.3 数据库命令
有三个简单的命令:
- 删除数据库
- 来创造它
- 要导入数据
该应用程序如上所述进行了打包,以便 commands.py 脚本可以访问该应用程序——我们将在稍后讨论蓝图时看到这一点。
在运行导入之后,用户已经有了可以测试的数据库,例如通过创建查询。
5。后端应用逻辑
在我的上一篇文章中已经描述了什么是烧瓶以及如何设置它。
该解决方案被打包到 imageRecommender 包中,我们用 run.py 脚本调用它
createApp 方法在 init 中。py 应用程序工厂,在扩展应用程序时使用它变得很有用,例如,通过需要应用程序的几个实例的测试。
- 对于加载配置的导入,这些在前面已经描述过了。需要在此 init 中创建应用程序。py 文件 via app = Flask(name)。
- 创建对象后,将验证环境变量是否适用于正确的用例,并加载合适的配置。
- 创建了数据库对象,并为主应用程序和数据库命令注册了两个蓝图。cli_group=None 将命令合并到应用程序级别,这样它们就可以在 Heroku 控制台中被 flask
调用。
主蓝图在 routes.py 文件中包含主要的后端应用程序逻辑。让我们一步一步地浏览这个文件:
from flask import render_template, request, Blueprint
from imageRecommender.models import Galleryimages
- 我们通过 render_template 包使用用于 html 页面生成的模板。
- 通过请求,我们得到所选图像的名称和我们在图库中的页面。
- 当我们使用蓝图来模块化应用程序时,蓝图是导入的。
- 对于查询的图像,我们通过 Imagerecommendations 相关类获得相似的图像,因此我们使用我们的 Galleryimages 类。
main = Blueprint('Fmain', __name__)
- 蓝图主已注册。
@main.route("/")
@main.route("/home")
def home():
page = request.args.get('page', 1, type=int)
gImages = Galleryimages.query.paginate(page=page, per_page=8)
return render_template('home.html', images = gImages)
- 主路和回家的路通向画廊。
- 通过home.html模板,我们渲染 render_template 调用中给出的图像。
- 它将来自 gImages 的图像作为输入。这里使用了分页特性。使用 per_page 可以设置图库的一页上显示多少幅图像,page 是打开的默认页面,在这种情况下是图库的第一页,包含前八幅图像。
@main.route("/recommend")
def recommend():
selectedImage = request.args.get('selectedImage')
imageEntry = Galleryimages.query.filter_by(imageName=selectedImage)
images = []
values = []
for image in imageEntry:
for recommendation in image.imageRecs:
images.append(recommendation.recommendedName)
values.append(recommendation.similarityValue) return render_template('recommend.html', title='Recommendations', customstyle='recommend.css', inputImage=selectedImage, similarImages=images, similarityValues = values)if __name__ == '__main__':
app.run(debug=True)
- 使用请求用户所选图像的相应推荐函数来调用推荐路线。
- 它是从我们的数据库中查询的,我们迭代相关类中的所有推荐。
- 图像和值数组填充有相应的信息。
- 传递所有必需的信息来呈现 recommend.html 页面。
6。模板
我们看到了两个从模板呈现 html 页面的调用:
render_template('home.html', images = gImages)render_template('recommend.html', title='Recommendations', customstyle='recommend.css', inputImage=selectedImage, similarImages=images, similarityValues = values)
让我们看看 home.html 文件和 recommend.html 文件的相关部分
应用程序使用布局模板layout.html来最大化 html 代码的重用。重要的部分是模板的主体有一个在另外两个 html 文件中指定的块内容。
<body>
…
{% block content %} {% endblock %}
</body>
home.html文件用于图库
它主要由两部分组成:
1.通过{ % for images . items % }迭代的图库中的图像以及
2.分页 div 部分中的分页元素。这里最有趣的部分是 iter_pages 方法,它允许设置在数字块的左右边缘和中间显示多少数字
{ % for page _ num in images . ITER _ pages(left _ edge = 2,right_edge=2,left_current=1,right_current=2) %}
recommend.html 模板用于显示查询和最相似的图像:
结尾的模态和这个应用程序中唯一的 JavaScript 代码是为弹出窗口准备的,如果需要,可以放大类似的图像。
响应度:
为了在智能手机上正确显示图库,一些设计元素需要不同的大小,图库应该只有一列。这都可以通过样式表添加相应的部分来实现
*@media only screen and (max-device-width : 640px) {*
例如,对于图库内容,我们在此部分将列计数设置为 1
.gallery .galleryContent .item{
float: left;
width: 100%;
height: 100%;
position: relative;
cursor: pointer;
column-count: 1;
}
7。Heroku 部署
。flaskenv 文件包含两个条目,它在安装了 python-dotenv 时工作
FLASK_APP=run.py
FLASK_ENV=development
因此,flask run 允许您在本地测试应用程序(在 http://localhost:5000/ 上)
现在是时候部署这个包并使它可以通过互联网访问了。
Heroku 是一个允许轻松部署和托管应用程序的平台。它不遗余力,没有通常的系统管理,网络服务器,防火墙设置任务等。都是需要的。免费层允许简单的测试,所展示的演示在这样一个免费的主机上运行。我们希望将该应用程序部署到 Heroku,因为它提供免费的云虚拟主机服务,非常适合像这样的演示。
以下步骤于 2020 年 8 月在 Heroku.com 进行了测试和工作。
对于 Heroku 的简单部署,将您的代码推送到 GitHub。创建存储库。使用您收到的 URL 将存储库推送到 GitHub。
因此,你需要访问https://www.heroku.com/并用主要开发语言 Python 创建一个账户。
按照 https://devcenter.heroku.com/articles/heroku-cli 上的说明安装 Heroku CLI。
您可以使用 Heroku CLI 执行以下许多步骤,但是,如果可能的话,我将介绍基于 UI 的配置步骤。
在 Heroku 点击“创建一个新的应用程序”。它要求你给它一个名字,让我们在这里设置图像推荐演示和欧洲地区。
确认后,您将进入下一个屏幕,在这里您可以选择您的部署方法
当我们将代码推送到 GitHub 时,我们选择 GitHub 选项,之后我们需要“连接到 GitHub”并授权 Heroku 访问您的 GitHub 存储库。
接下来,我们输入相应存储库的名称 image-recommender-demo ,搜索它并连接到它。
您可以选择自动部署每个变更,例如,到您的主分支,这对于这样的演示用例来说非常方便,
开始时,我们手动部署一次(为此需要按另一个按钮)。现在,您可以看到您的解决方案正在构建中,并且处于成功状态
在网站的顶部,你可以找到一个按钮
这使我们在这种情况下,我们的推荐系统
https://image-recommender-demo.herokuapp.com/
到目前为止,日志将显示没有 web 进程正在运行,因此需要创建一个 Procfile ,Heroku 使用它来了解如何运行应用程序。它只包含一行
web: gunicorn run:app
web 指定使用 web worker。需要一个应用服务器,因此使用 gunicorn 并需要安装它——版本 20.0.4 在 requirements.txt 文件中引用。 gunicorn 是运行 web 进程所需的命令,以便 python 代码可以与 web 服务器通信。 run 是指包含应用对象的 run.py 文件, app 是指应用对象的名称。
提交之后,需要设置 FLASK_ENV。如上所述,我使用了。本地保存文件。在生产中,通过 Heroku CLI 手动设置配置
heroku config:set FLASK_ENV=production --app image-recommender-demo
如果您尚未登录,Heroku 将首先请求登录。
现在运行应用程序仍然会给你留下一个内部服务器错误,因为数据库还没有设置好。
我们希望使用 PostgreSQL。在 Heroku UI 中点击“概览”,选择“配置附加组件”,搜索“Postgres”;选择“Heroku Postgres”。这个演示我用的是免费版。
现在这个数据库已经连接到应用程序图像推荐演示!你现在可以在“设置”、“配置变量”下看到它的网址。
如果 psycopg2 尚未安装,Heroku 将再次告诉我们一个错误,日志显示“psycopg2”模块未找到。因此, requirements.txt 文件中的 psycopg2==2.8.5 得到正确的包。
这些表仍然不存在,所以在 Heroku 上进行调整后,我们仍然会看到一个错误。
为了创建缺失的表格并导入一些数据,我们使用前面解释过的 command.py 文件中的命令。
我们将设置 FLASK_APP 变量,以便在 Heroku con 中识别命令
heroku config:set FLASK_APP=run.py --app image-recommender-demo
现在我们可以在 Heroku 中调用控制台,并使用前面给出的命令
flask createDB
flask importDB
就这样,应用程序部署完毕,上线,数据库包含相关数据!
8。结论
有了这个和 PyTorch 推荐人的帖子,你手头就有了一切
- 离线运行机器学习代码并存储结果
- 创建一个基于 Flask 的 web 应用程序
- 在 web 应用程序中使用机器学习结果
- 使用带有 Flask 的 PostgreSQL 数据库
- 将您的应用程序部署到 Heroku
你不一定要在这个特定的演示应用程序中使用这些编程见解,一旦你理解了所有的步骤,你可以用其他的想法变得有创造性。
正如我在的上一篇文章中所描述的,还有更多方面需要考虑,以实现生产目标。
我们可以投入更多的工作,使推荐本身成为一个 API 服务,具有合适的端点来添加、删除和更新条目。
这种服务可以是生产系统的一部分,比如大型的网上商店。注意,在这种情况下,图像应该从静态文件夹移动到例如某个对象存储器。
感谢阅读!喜欢这个话题吗?
如果你觉得这本书很有趣,你可能想补上我以前的文章,这篇文章的主题是:
[## 轻松推荐相似图片
使用来自训练的卷积神经网络的特征来产生可比性
towardsdatascience.com](/effortlessly-recommending-similar-images-b65aff6aabfb) [## 使用 PyTorch 推荐相似图片
使用 Resnet18 实现完全迁移学习
towardsdatascience.com](/recommending-similar-images-using-pytorch-da019282770c) [## 一个推荐图片的 Flask 应用程序
PyTorch 中基于卷积神经网络的相似图像推荐网站原型
towardsdatascience.com](/a-flask-app-for-image-recommendations-a865e1496a0d)
图像分割——选择正确的指标
原文:https://towardsdatascience.com/image-segmentation-choosing-the-correct-metric-aa21fd5751af?source=collection_archive---------8-----------------------
Adrian Williams 在 Unsplash 上的原始照片
比较图像分割任务的度量和损失函数作为不平衡数据问题的例子。
从机器人到自动驾驶,图像分割任务有各种应用,这使其成为计算机视觉和机器学习的当前研究领域。由于其在架构上的灵活性,卷积神经网络(CNN)已被证明是该领域中最先进的算法。与普通分类相比,监督图像分割具有一些基于数据中不平衡类别分布的特殊特征。本文说明了重新审视一下模型评估所使用的评分标准可能是有用的,并介绍了 Jaccard 指数和 F1 分数作为最常用的准确性分数的替代。此外,它将显示如何开发损失函数,直接优化这些分数。最后,我们将基于引入的度量将开发的损失函数的结果与交叉熵进行比较。
图象分割法
基本上,图像分割就是分类。但是,不是给定的输入图像只有一个标签,而是该图像中的每个像素都有一个标签。因此,分类器需要输出与输入图像维数相同的矩阵。
在通常的分类任务中,应该由分类器分离的类通常均匀地分布在训练集中。如果不是这种情况,很容易减少一个类别的样本,或者收集/生成另一个类别的更多样本,从而使这些类别均匀分布。这在使用 CNN 的图像分类任务中是不可能的。在这里,为了减少一个类的频率,像素必须被丢弃,这导致了不完整的图像。
准确性有什么问题?
想象一幅包含繁华城市场景的图像。分类器的目标是识别显示街道标志的所有像素。在这张图片中,可能有许多其他物体,如汽车、人、房子等。因此,与整个图片大小相比,街道标志的总面积可能非常小。如果我们将除街道标志之外的所有其他对象标记为“背景”,那么我们只剩下两个标记,“背景”类的像素数量将比“街道标志”类的像素数量大得多。
Adrian Williams 在 Unsplash 上的原始照片
现在让街道标志的面积只占整个图像面积的 10%,考虑一个简单的分类器,它总是预测整个图像中每个像素的“背景”。因此,这个分类器根本不能识别任何街道标志像素。
对于这个二元问题,精度定义为
因此,该分类器的最终准确度为 90%。这听起来是一个很好的分数,尽管从直觉上来看,这绝对不是描述分类结果的理想值。
因此,准确性似乎与正确标记物体的目标并不一致。至少,如果这些物体与图像尺寸相比非常小。这意味着我们必须考虑其他得分指标。
替代指标
作为准确性的替代,Jaccard 指数或 F1 分数可用作评分标准:
Jaccard index,也称为 IoU score(交集除以并集),定义为由并集定义的两个集合的交集。
基本思想是将图像遮罩视为集合。这些集合可以在图片中重叠。如果两个遮罩完全相同,则两个集合具有完全相同的大小,并且 100%重叠,因此交集等于并集。在这种情况下,IoU 得分为 1,为最佳值。另一方面,如果与原始掩码相比,预测掩码的大小发生了移动或变化,则并集将大于交集。IoU 分数降低。
再次以街道标志为例,图像的 10%显示街道标志,剩余区域只是背景。对于这个二元示例,主要目标是正确分类街道标志。因此,让街道标志像素为正值,背景为负值。因为分类器将图像中的每个像素标记为“背景”,所以真阳性的总和( TP )必须为 0。因此,得到的 Jaccard 指数也是 0。
相对于准确率分数,即 90%,这个分数表示分类结果是完全错误的。关于分类器没有正确分类街道标志的任何像素的事实,Jaccard 指数的分数比 accuracy 更直观地描述了该分类器的结果。
F1 分数,也称为骰子分数,与 Jaccard 指数相关,定义为
F1 分数是精确度和召回率的调和平均值,顾名思义非常适合不平衡数据集。关于该公式,可以看出,对于给定的示例,F1 分数的结果也必须是 0。
有关 F1 分数和其他不平衡数据评分指标的更多信息,请参见https://towardsdatascience . com/handling-unbalanced-datasets-in-machine-learning-7a0e 84220 f 28
开发成本函数
上一节介绍了图像分割任务的两种可能的度量。在本节中,我们将展示如何开发成本函数,直接优化这些分数。此处,显示了 Jaccard 指数的过程,但对于基于 F1 得分的成本函数,步骤是相同的。
如果我们将 Jaccard 指数引入反向传播所需的可微分形式,并且让𝑦、𝑦̂是一个热编码向量,其中类的数量 C 是它们的长度,我们得到公式
遗憾的是,由此产生的梯度并没有交叉熵产生的梯度那么简单,而且容易出现“梯度爆炸”。因此,在等式中添加额外的平滑因子以获得更稳定的训练结果是有用的。此外,我们希望有一个应该最小化的成本函数。因此,因为 Jaccard 指数自然地从 0 扩展到 1,所以我们从 1 中减去结果。这导致以下损失函数:
在 Python 和 TensorFlow 中实现,这会产生以下函数:
实验——损失函数的比较
下面,我们将训练 3 个相同的 CNN 模型。一个将使用交叉熵来训练,而另一个使用 Jaccard 损失和骰子损失来训练。之后,我们将使用准确性分数和 F1 分数作为评估指标来查看结果。
这里使用的神经网络架构基本上来自 https://www.tensorflow.org/tutorials/images/segmentation 的,略有改动。基本架构称为 U-Net,这是一个基于编码器-解码器的网络,在各个层之间有额外的快捷方式。主要思想是首先压缩图像信息,然后结合像素特定知识应用该压缩信息来创建输出掩模。
我们将在这个实验中使用的数据集是牛津-IIIT Pets 数据集。它由 7349 张图片组成,展示了不同种类的宠物。对于每个图像,都有一个给定的遮罩,将每个像素分类为宠物、背景或边界。所以,总共有 3 节课。
在完全相同的情况下训练 CNN 20 个时期后,我们可以看到以下结果:
看一下条形图可以看出,考虑哪个指标真的很重要。如果仅使用准确度分数来比较模型,基于 F1 损失的模型将被判断为所有 3 个变量中最差的。相反,使用 F1 分数,F1 损失模型比用交叉熵训练的模型取得了明显更好的结果。
结论
我们已经看到,对于有用的模型评估,评分标准的选择可能是至关重要的,尤其是在处理不平衡的数据集时。因此,在开始调优超参数和优化给定指标的结果之前,质疑这个指标并考虑它是因为它是最适合的还是因为它是默认选项而被使用可能是有用的。
此外,我们还看到了如何创建自己的成本函数来直接优化特定的指标。对于合适的问题和数据集,这可能比交叉熵获得更好的结果。
包含全部代码的 Jupyter 笔记本可以在这里找到:https://gist . github . com/LaurenzReitsam/05 E3 bb 42024 ff 76955 adbf 92356d 79 f 2
你可能会觉得这很有趣:
- 图像数据中不同不平衡问题的极好概述:https://Neptune . ai/blog/unbalanced-data-in-object-detection-computer-vision
参考
- 范·比尔斯,f .和林德斯特伦,阿维德和奥卡弗,艾曼纽和威林,马尔科,用于二值图像分割的交集超过联合损失的深度神经网络,2019 年。
- https://www.tensorflow.org/tutorials/images/segmentation
- https://www.robots.ox.ac.uk/~vgg/data/pets/
Apache Spark 上的图像分割
原文:https://towardsdatascience.com/image-segmentation-on-apache-spark-46164dd53c73?source=collection_archive---------18-----------------------
基于 Apache Spark 和 Scala 的 K-means 图像分割
资料来源:联合国人类住区规划署
计算机视觉是数据科学中最令人兴奋的分支之一。应用机器学习算法和技术有许多可能的应用,图像分割是其中的第一步。
图像分割是计算机视觉处理的主要发展方向之一。许多计算机视觉任务需要对图像进行分割,以理解每个部分并更容易整体理解。每一段包含一组像素,可能代表一些东西。分割是图像分析的两个重要应用的基础:
- 物体检测:识别图像中的物体、汽车、人或动物等。这里的目标是仅仅识别在该组像素中有一个对象,而不是正确地分类它是哪个对象。
- 分类:目标检测后,目标是将每个检测到的目标归为一类。下图说明了这个应用程序,其中每个对象都被检测和分类。
图像来源:使用 3D 激光雷达和相机制作有意义的 3D 地图。https://www . researchgate . net/publication/326875064 _ forward _ a _ 隽永 _ 3D _ Map _ Using _ a _ 3D _ Lidar _ and _ a _ Camera
分割技术
通过深度学习、基于区域的方法(其将尝试检测对象的边界)、通过聚类算法的应用等等,有许多方式来实现图像的分割。每种技术都有其优点和缺点。深度学习技术已经证明了迄今为止最好的结果,但实现往往很复杂,需要大量数据来训练模型。当然,可以使用预先训练好的可用模型,但是对于在特定应用上的使用,将需要训练,这可能是费时和费工的。另一种可能的方法是应用聚类算法。目标是识别数据中的组,分配来自每个像素的所需信息(x 和 y 位置,颜色信息),根据特征相似性将它们分类到其中一个组中。与深度学习相比,它的效率较低,因为聚类不是分析预定义的组,而是迭代地工作以有机地形成组,这将需要更多的人类干预。然而,由于聚类是无监督的,所以它不需要太多的数据来进行训练,与深度学习的同行相比,这可以减少开发时间并节省处理时间。
K 均值聚类
在将数据组织成组之前,我们需要理解解析几何中两个数据点之间距离的概念。计算距离的方法有很多种,这里我将重点介绍一种最常用的方法:欧氏距离。根据百科,两点 p 和 q 之间的欧氏距离就是连接它们的线段的长度。如果 p = ( p 1、 p 2、…、 pn )和 q = ( q 1、 q 2、…、 qn )是欧几里德n-空间中的两点,则可以利用勾股定理
让我们用一个实际的例子。给定两点 p 和 q ,其中 p=(5,3) 和 q = (4,2)。以下图为例, x1 = 5, y1 =3, x2 = 4, y2 = 2。应用该公式,我们得到 d( p ,q)=(4–5)2+(2–3)2 = 2。
现在我们已经了解了距离,我们可以继续进行 K-Means 计算。首先,首先定义簇的最终数量 K 。应该考虑每种情况的应用环境来定义集群的数量。之后,有必要为集群定义质心。质心被定义为给定聚类中所有数据点的平均值。所以,如果一个数据点 P 更靠近质心 K1 而不是 K2,我们可以说这个点属于组 K1。有很多方法可以初始化质心。以下是当时的一些情况:
- RP — 随机选取点。随机选择 k 个不同的数据案例作为初始中心。
- KMPP——随机最远点数,或 k-means++。从数据集中随机选择第一个中心。第二个中心也是随机选择的,但是案例选择的概率与它到第一个中心的距离(平方欧几里德距离)成比例。第三个中心也是随机选择的,选择的概率与病例到这两个中心中最近的中心的距离成比例,等等。
- SIMFP — 最远点(简单选择)。从数据集中随机选择第一个中心。第二个中心被选为离该中心最远的情况。第三个中心被选为离这两个中心最远的中心(离这两个中心最近的中心),依此类推。
- RUNFP — 最远点(运行选择)。首先将 k 个病例作为中心,然后在运行数据集的其余病例期间,在中心之间进行渐进替换;替换的目的是最终获得变量空间中彼此相距最远的 k 个点。这些在数据云中占据外围位置的点(例)就是产生的初始中心。
- GREP — 小组代表点。方法思路——以 k 个最具代表性的“代表”案例为中心进行收集。第一个中心被认为是最接近一般数据中心的情况。然后,以这样的方式从数据点中选择其余的中心,即考虑每个点是否比每个点更接近一组点(以及根据平方欧几里德距离的多少)到任何已经存在的中心。即,每个点被作为候选点来检查,以代表由已经收集的中心还不够好地代表的某一组点。在这方面最有代表性的点被选为下一个中心。
现在我们已经巩固了距离和质心的初始化,让我们看看 K-Means 算法是如何工作的:
如果你对任何一步仍有疑问,我强烈推荐由桑奈纳在 datasciencecentral 上撰写的这一步一步计算。同样,这个视频展示了每一步的可视化。
利用火花上的 K 均值实现图像分割
好了,是时候开始用 scala 的 apache spark 上的 k-means 聚类算法解决图像分割问题了。等等,但是为什么是 scala 呢?Python 是目前数据科学家最喜欢的语言,不仅因为它容易学习和实现,还因为它丰富的库和框架。在数据科学和机器学习项目中,它包括大量有用的库 SciPy、NumPy、Matplolib、Pandas 等,而对于深度学习中更复杂的项目,Python 提供了 Keras、Pytorch 和 TensorFlow 等库。有了这些优势,为什么还要选择 scala 呢?Spark 是用 Scala 编写的,因为它可以非常快,因为它是静态类型的,并且以 JVM 已知的方式编译。我来自 Java 世界,其中一个优势是使用 Java 的全部遗产,比如库和框架。
我不会深究语言之间的差异,因为这不是这篇文章的重点(有数百个优点和缺点,google 是你的朋友),但正如之前所说,一个事实是 Python 社区非常庞大,提供了许多库和框架来与 Spark 一起使用,还有许多可用的教程。正因为如此,我写这篇文章的主要动机是为 scala 社区做贡献。
将图像载入 Spark 数据框
从 2.4 版本开始,Spark 内置了一个新的数据源,可以读取压缩格式(jpg、png 等)。将使用 ImageIO Java 库读取图像,它有一个特殊的数据帧模式。读取一个图像文件(或几个,只是指向一个只包含图像的目录)可以通过以下方式完成:
该模式包含一个 StructType 列“Image”,其中包含所有关于读取数据的信息。在 StructType 中,可以找到以下列:
- 原点:
StringType
(代表图像的文件路径) - 高度:
IntegerType
(图像的高度) - 宽度:
IntegerType
(图像的宽度) - n 通道:
IntegerType
(图像通道数) - 模式:
IntegerType
(OpenCV 兼容型) - 数据:
BinaryType
(OpenCV 兼容顺序的图像字节:大多数情况下是按行排列的 BGR)
对我们来说,重要的数据是 spark 为我们生成的字节数组,代表原始图像数据。该数组包含我们可以用于聚类的所有功能。
数字图像由像素组成,每个像素由颜色组合组成,用代码表示。例如,一幅灰度图像只有一个颜色通道。对于计算机显示器,最常用的标准是 RGB。RGB 图像有三个通道,每个通道表示相应颜色的亮度:红色、绿色和蓝色。因此,基本上,一个 RGB 图像是三个图像的组合(每个通道一个),每个图像包含各自颜色的信息。
你有没有尝试过近距离观察老式 CRT 电视的屏幕?如果是这样,你可能记得你可以看到三个颜色通道。ideia 在这里是相同的,如图像所示。
火花如何解码图像
为了构建我们的特征图,我们需要了解 spark 如何读取和解码图像。潜入 spark 代码,我们有image schema . Scala对象,更确切地说,是解码方法。****
该方法主要检查图像的通道数,并根据该数量进行解码。让我们举一个例子:考虑一个 640 x 480 的图像,它将有 307200 个像素。如果图像是灰度的(只有一个通道),最终的数组大小将是 307200。但是,如果是 RGB 图像,最终的数组将有 307200 * 3 = 921600。通道顺序被定义为 BGR(也有用于定义透明度的 alpha 通道的空间)。对于相同的 640 x 480 RGB 图像示例,数组的前三个位置将包含来自高度= 0 且宽度= 0 的像素的颜色信息(蓝色、绿色和红色)。接下来的三个位置将包含来自高度= 0 和宽度= 1 的像素的信息,依此类推。
创建特征数据框
现在我们知道了 spark 如何存储图像信息,我们可以读取数据数组来构建我们的特征数据框,该数据框将用于 K-Means 算法。对于每个像素,我们应该有 w 位置,h 位置和每一个颜色通道。
为了开始这个任务,我们将使用两个图像,如下所示,一个风景和一个人(我模糊了女人的脸,只是为了防止任何问题)。
全尺寸图片可在:https://ibb.co/d4xWxvw找到
全尺寸图片可在:https://ibb.co/d4xWxvw找到
提取信息的逻辑是按照 decode 方法上的逻辑读取数组,然后将其转置到数据帧列中。让我们一步一步来:
我们首先读取图像,提取字节数组,并为其创建一个新的 RDD,应用 flatMap 转换,将每个数组位置作为 RDD 上的一个条目返回。我们可以直接在图像数组上工作,但是我们会失去使用 spark 的一个优势:分布式数据结构。通过始终使用 RDD 结构,我们可以确保我们的代码始终是分布式的。
下一步是提取有关渠道,高度,宽度和启动累加器的信息。累加器是集合所有 spark 执行器值的变量。因为我们使用的是 RDD 的,所以我们不能使用常规的聚合变量,因为当 Spark 将这些代码发送给每个执行器时,这些变量就变成了这个执行器的本地变量,并且它的更新值不会传回给驱动程序。因此,如果我们想要我们的计数器正确工作(对于 h 和 w 位置和颜色偏移),我们需要使用累加器。
最后的处理从ziptwithindex转换开始,它将返回一个新的 RDD 对,一个包含元素和索引的元组,这对字节偏移量很有用。ziptwithindex将有助于为 RDD 上的每个条目分配一个索引,该索引将用于偏移量。最终,RDD 将被转换成一个有三列的数据帧,代表颜色信息以及 x 和 y 位置。需要注意的是,每个像素都有模式中描述的通道数。因此,将某个像素的所有颜色信息压缩到数据集的一个条目中是很重要的。为此,在数据框上使用聚合,按像素的 X 和 Y 位置分组。接下来,应用org . Apache . spark . SQL . functions . collect _ list函数,该函数将从 color 列中获取所有聚合值,并返回包含这些值的列表。最后,返回一个带有 X 和 Y 位置以及蓝、绿、红颜色信息的数据帧。
上面的实现只考虑了具有三个通道(RGB)的图像,因为它是图像中最常见的。具有 4 个通道的图像(RGBa)将第四个通道用于透明度信息,并且几乎不需要调整像素的偏移位置。
Spark 上的机器学习流水线
一般来说,大多数机器学习模型的实现都可以设计为一些算法的有序序列,如下所示:
- 特征提取、变换和选择。
- 基于这些向量和标签训练预测模型。
- 使用生成的模型进行预测。
- 评估模型(性能和准确性)。
Spark MLib 提供了两个顶级抽象来促进该管道的开发:变压器和估算器。转换器实现了一个方法 transform(),该方法将一个数据帧转换成另一个数据帧,通常追加一个或多个新列。例如,转换器将获取数据框中每个条目的所有列要素,并将其映射到新列(要素向量)。估计器将负责应用适合或训练数据的学习算法。它实现了 fit()方法,该方法接受一个数据帧并生成一个模型,该模型是一个转换器。作为一个例子,学习算法如决策树是一个估计器,调用 fit()训练一个决策树分类模型,它是一个模型,因此是一个转换器。
准备好带有特性列(b,g,r,w,h)的数据框架后,我们可以通过应用 transformervector assembler来启动我们的管道。这个转换器将给定的列列表组合成一个向量列。我们使用方法 setInputCols 来传递一个数组,指向我们想要组合的列。方法 setOutputCol 用于指向将包含组合向量的列。
最初,只有颜色信息将被用作我们模型的特征。我们可以稍后测试和讨论 x 和 y 位置的包含。这将是生成的数据帧:
现在,我们可以创建 Kmeans 估计器并训练我们的模型。变量 K 将定义将要使用的簇的数量。 setSeed 方法用于随机初始化集群。如果您不提供种子,spark 将在内部生成一个种子,这会影响测试比较,因为每次运行的性能都不同,因为每次运行的集群初始点都不同。方法 setFeaturesCol 接收包含特征向量的列。
通过调用 fit()方法并传递由 Vector Assembler 转换的 DataFrame 来创建模型。该模型将通过调用 transform()方法给出结果,返回包含新列 prediction 的 DataFrame。
最后,生成的数据帧将如下所示:
预测列将告知条目属于哪个组。注意集群编号总是从零开始。
写入分割图像。
Spark 没有提供写图像的方法,所以我们需要实现一个。此外,决定如何在新的渲染图像上表示聚类颜色也很重要。在这种情况下,我决定使用从每个聚类中找到的第一种颜色来表示它自己。
这个想法很简单。对于每个聚类,我们使用聚类编号过滤数据集,并获得找到的第一个条目,我们将聚类编号作为键添加到映射中,并将颜色通道作为值添加到 Scala 元组(https://docs.scala-lang.org/tour/tuples.html)中。
现在终于可以写出最终的图像了。为此,我们将使用类 javax.imageio.ImageIO 。首先,我们选择 w、h 和 prediction 列,并使用 collect()函数将数据帧转换成一个数组。然后,我们创建一个缓冲图像,传递宽度、高度和图像类型。最后,只需迭代数组,将像素位置和相应的彩色图像写入缓冲图像。注意,BufferedImage 类只有 setRGB 方法,所以我们必须在使用类 java.awt.Color 时改变颜色的顺序。
测试实施
好了,现在我们可以用之前展示的图片来测试这个解决方案了。但是首先,我们必须决定我们需要多少个集群来进行细分?看下面的图片,我将使用 K =5,基于我肉眼可以观察到的不同部分的数量(红色的裙子,皮肤,海洋,天空和地面)。
这是 K = 5 时的结果:
我们可以看到衣服和皮肤被分割得几乎完美。有些部分被放进了同样颜色的岩石中,因为它们的光谱更接近。天空也几乎是一个独特的部分。在下一张图中,我也考虑 K = 5。
这是结果:
海滩上的三个人被归类在同一群山上和海滩的一部分,因为由于阴影,他们的色谱是相似的。
这是其他不同 K 的跑步记录。注意,通过增加 K,我们更接近原始图像。这对压缩图像很有用。试着用 K = 128 和 K = 256 运行。
K=3、K=4 和 K=6
K=3、K=4 和 K=6
寻找 K 的最佳值
对于某一幅图像,如何才能确定 K 的最佳值?之前我说我用自己的感知来用一个数字,但是我们不能依赖人类的感知,这是数据科学!
这是我们将如何进行的:我们将为 K 定义一个范围,比如从 2 到 20。然后,对于每个 K 值,我们将计算 K 均值函数的成本,并将成本绘制在图上。最小化后获得很少
回报的值将是最佳选择。为了绘图,我将使用 scala 的 vegas-viz 绘图库(https://github.com/vegas-viz/Vegas)。让我们在 pom.xml 中包含以下依赖项:
**<dependency> <groupId>org.vegas-viz</groupId> <artifactId>vegas_2.11</artifactId> <version>0.3.11</version></dependency><dependency> <groupId>org.vegas-viz</groupId> <artifactId>vegas-spark_2.11</artifactId> <version>0.3.11</version></dependency>**
我们将在包含我们的特征向量的 Spark 数据帧中使用 eMLlib 的 KMeans()估计器,在范围(2,20)中迭代 k 的值。然后我们在一个 html 页面上绘制结果(您可以在一个弹出窗口上绘制,查看 vegas-viz 文档,但是您需要 javafx 包,它不包含在 openjdk 中)。注意,我们不需要在整个数据帧中运行估计器,只需抽取 10%的样本,以节省运行时间。
因此,正如我们在结果中看到的,在海滩图像的 K=14 和女人图像的 K=12 之后,成本不会改变太多。新渲染的图像如下
K = 12
K = 14
我们可以看到,女人的皮肤和衣服仍然在一个集群中,这对检测图片上的人非常有用。然而,天空不仅仅是一个独特的星团。在海滩图像中,我们可以从沙滩上的倒影清楚地辨认出这三个人。
结论
希望对你学习 K-Means 算法,Apache Spark,ML 有所贡献。Spark 是一个为批处理设计的框架。它扩展了 MapReduce 模型,以有效地将其用于更多类型的计算,包括交互式查询和流处理。
尽管 Spark 有许多现成的 ML 算法实现,但除了 ImageSchema 之外,它与计算机视觉没有任何关系,image schema 使得将图像加载到数据帧成为可能。虽然 Python 开发人员可以找到大量关于不同问题的不同方法和混合框架的库和文档,但我感觉 Scala/Java 社区需要更多关于数据科学的内容。这是我这次开发的动机。
你可以在这里查看完整代码:【https://github.com/gsjunior86/SIS。如果你有任何疑问,请写信给我。
图像分割:用 Carvana 数据预测图像掩模
原文:https://towardsdatascience.com/image-segmentation-predicting-image-mask-with-carvana-data-32829ca826a0?source=collection_archive---------25-----------------------
本文使用 CNN 和 U-Net 模型演示了使用 Carvana 数据进行图像分割的任务。
信用:@自省 dsgn
在我之前的博客中,我们冒险进入了图像分类领域,任务是为输入图像识别标签或类别。在我们之前的上下文中,我们对狗的照片进行了分类,人们可以了解算法识别的是哪种类型的狗。然而,假设一个人想知道狗是否由它的主人陪伴,或者在这些物体的图像和位置中是否有两只狗等等。在这种情况下,需要使用图像分割来代替。
想象一下,图像的每个像素都被赋予了特定的类别。以汽车图片为例,可以有三个类别:汽车、道路和背景的其余部分。这些类“告诉”计算机哪个像素属于哪个类。图像分割的任务是训练神经网络,该神经网络能够预测输入图像的逐像素类别。预测的输出被称为图像的“掩模”。该技术在物体识别、人脸识别、医学图像分析和卫星图像分析等方面都有应用。
在这个项目中,我们将使用两种不同的方法训练神经网络来预测 Carvana 数据的图像掩模,代码库可以在这里找到。
#数据
数据集来自 Carvana 的 Kaggle 上举办的图像屏蔽挑战。
来自数据集的样本图片。左图是原始照片,中间显示面具,右图显示原始减去背景,只保留汽车。
目前只有两个班级:
- 类别 0:背景
- 类别 1:前景,汽车
每辆车都有 16 张不同位置的 1918*1280 像素的高质量照片。在列车组中,有 318 节车厢,总共 5088 幅图像;和 6254 辆汽车,总共 100,064 幅图像。我们将使用训练集来分割训练和验证数据,而模型将完全看不到测试集。
对于数据预处理,我们选择使用 Keras 图像处理简单地水平翻转图像。
#引入骰子系数
在制定任务时,我们认识到生成图像掩模类似于一个分类任务,因为模型为每个像素预测一个类别标签。因此,用于分类任务的评估度量,例如准确性,似乎是该任务的自然选择,不是吗?然而,情况并非如此,我们需要为此任务引入一个新的指标。在这里,我将使用数据集举例说明。
真实面具
掩模预测
左边的图像是从训练集中随机选择的真实遮罩。黄色区域是汽车,紫色是背景。我们还观察到前景占总像素的 18%,而背景占 82%。一般来说,汽车(前景)只占图像总尺寸的不到 20%。
假设我们有一个完全紫色的遮罩预测,换句话说,它预测只有背景出现在图像中,而没有检测到前景。
如果我们评估准确,它给我们一个体面的 82%的分数,因为背景是正确预测的。听起来很棒?
然而,图像分割不仅学习图像中的内容,还学习图像中的位置。准确性作为一种衡量标准将无法捕捉“在哪里”的信息。在这种情况下,我们引入骰子系数来同时捕捉“什么”和“哪里”。
Dice 系数逐类地度量两幅图像之间的相似性。1 分表示两幅图像完全相同,0 分表示完全不同。如果我们拿上面同样的例子,dice coefficient 给出的分数是 41%。
计算如下:
两幅图像的总像素数合计= 200(为简单起见,我们假设每张图片是 100 个像素,而不是实际的像素数)
前景(汽车):重叠区域= 0
*(2 重叠面积)/(总像素组合)= 0/200 = 0
背景:重叠面积= 82
(2 重叠面积)/(总像素组合)= 822/200 = 0.82
骰子=(前景+背景)/2 =(0%+82%)/2 =**41%**
我们将使用 dice 系数作为主要指标来评估我们的模型性能,并将准确性作为一个指标进行监控。下面的代码片段定义了项目使用的 dice 系数。骰子系数可用于骰子损失,即(1-骰子系数)。平滑变量用于通过不使项接近零来帮助反向传播,并防止过拟合相对较大的值。
#型号:CNN
我们的第一个模型是一个简单的三层卷积模型,作为一个简单的基线。输入图片是具有 3 个通道的原始图片,并且为了更快的训练而调整为较小的尺寸。
使用的损失函数是二元交叉熵,监控的度量是 dice 系数和准确度。
#型号:U-Net
然后,经过一些研究,我们转向了一种叫做 U-Net 的模型结构。它是一种有效的图像分割模型结构。U-Net 网络有两部分:编码器(下采样器)和解码器(上采样器)。它看起来像原始符号上的 U 形。
U-Net 架构
左侧是编码器,数据被压缩成一个更小但更深的矩阵。然后,右侧使用编码器的输出以及编码器各自的输入进行解码(跳过连接)。你可以在这里了解更多关于优信网的信息。
使用的损失函数是二元交叉熵,监控的度量是 dice 系数和准确度。
#结果
来自训练 40 个验证时期的结果
结果表明,U-Net 模型远远优于朴素模型,这是意料之中的。
这个模型对于看不见的数据也有很好的推广。下面是一些看不见的图像和它的预测遮罩。
具有来自 U-Net 模型的预测掩码的测试集图像
#讨论
这个项目在两周内完成了,还有改进的余地。
选择损失函数(二元交叉熵)是为了简单和方便。它不一定是这类任务的最佳损失函数。可以考虑混合使用骰子损失和二元交叉熵或骰子损失。
当我们可视化来自训练集的具有最高误差的图像时,我们意识到发现的常见问题是缺少边界线、缺少靠近阴影的部分以及缺少天线等精细细节。
汽车的黑色部分与地板上的阴影混为一谈;顶部的天线丢失
车的边缘线没抓拍到
我希望你喜欢阅读它!呆在家里,注意安全。
基于 K-均值的图像分割
原文:https://towardsdatascience.com/image-segmentation-using-k-means-6b450c029208?source=collection_archive---------30-----------------------
使用 NumPy/SciPy 从头开始图像分割
虽然预先存在的库(如 OpenCV)节省了时间和精力,但从头开始实现基本算法是另一种乐趣。
在这篇文章中,我将展示在 python 中使用 k-means 一步步实现图像分割。我们在从太阳数据库中采样的 8 个类别的 1100 幅图像上训练管道。图像分割是将相似类型的像素组合在一起。流水线可以进一步扩展以对图像进行分类。例如,公园的图像将比高速公路的图像具有更多数量的绿色像素。我们将在以后的文章中讨论分类。
我们发现一个公园的图像会有更多的绿色像素。虽然颜色是区分像素的好方法,但它有时会产生问题(例如,水和天空具有相同的颜色)。因此,我们需要其他方法来表征像素。换句话说,我们需要从图像中提取有用的特征。深度学习算法,如 CNN 的自动为我们找到有用的功能,但这超出了本文的范围。
为了提取特征,我们将使用四种具有多尺度的图像滤波器-高斯、高斯在 x 方向上的导数、高斯在 y 方向上的导数以及高斯的拉普拉斯算子。每个滤波器从图像中提取不同的特征集。较小的比例拾取较窄的要素,较大的比例拾取较宽的要素(想想森林和树木)。我们将不同尺度的滤波器集合称为“滤波器组”。
对滤波器组的图像响应(作者)
上图中的每一行代表一个特定的刻度,每一列代表一种不同类型的过滤器。高斯滤波器(第一列)模糊了图像并忽略了较高的频率。X 方向的高斯导数(第二列)拾取垂直边缘。Y 方向上的高斯导数(第三列)拾取水平边缘。高斯的拉普拉斯算子(第四列)检测快速强度变化的区域。
我们在图像上运行滤波器组(这是一个大小为 HW3 的数组,其中 H 和 W 分别是图像的高度和宽度,3 是通道的数量)。我们输出一个大小为(HW3F)的数组,其中 F 是滤波器组的大小。如果我们在 5 个不同的比例下使用 4 个滤波器,F 就是 4*5=20
从 T 个训练图像中,我们得到大小为 THW3F 的滤波器响应向量。为了节省计算资源,我们从每个图像中选择阿尔法随机滤波器响应。因此,滤波器响应向量的最终大小是 T * F。
我们将使用这个滤波器响应向量作为 k-means 算法的输入。它将输出 K 个(超参数)聚类中心。然后,测试图像的每个像素将被映射到最近的聚类中心,并涂上相应的颜色以形成“视觉图”。产生的“视觉地图”将具有相似的像素组合在一起,并且将是我们的“图像分割”算法的最终输出。
太棒了,让我们看看整个管道的运作。我们将从定义超参数的值开始,这些值可以在以后进行调整以提高性能。
filter_scales=[1,1.5,2]
K=10
alpha=25
我们现在定义函数 extract_fiter_responses,它为大小为 HW3 和滤波器组大小为 f 的图像返回大小为 HW3F 的向量。我们使用 scipy 中的内置函数来生成 filter_responses。
def extract_filter_responses(filter_scales, img):
if len(img.shape)==2: #convert image into 3 channel
img=np.dstack((img,img,img))
modified_img=skimage.color.rgb2lab(img) r_channel=modified_img[:,:,0]
g_channel=modified_img[:, :,1]
b_channel=modified_img[:, : ,2] g=[]
for i in range(len(filter_scales)):
modified_r_channel=scipy.ndimage.gaussian_filter(r_channel, filter_scales[i])
modified_g_channel=scipy.ndimage.gaussian_filter(g_channel, filter_scales[i])
modified_b_channel=scipy.ndimage.gaussian_filter(b_channel, filter_scales[i])
modified_r_dog_X=scipy.ndimage.gaussian_filter(r_channel, filter_scales[i],(0,1))
modified_g_dog_X=scipy.ndimage.gaussian_filter(g_channel, filter_scales[i], (0,1))
modified_b_dog_X=scipy.ndimage.gaussian_filter(b_channel, filter_scales[i], (0,1))
modified_r_dog_Y=scipy.ndimage.gaussian_filter(r_channel, filter_scales[i],(1,0))
modified_g_dog_Y=scipy.ndimage.gaussian_filter(g_channel, filter_scales[i], (1,0))
modified_b_dog_Y=scipy.ndimage.gaussian_filter(b_channel, filter_scales[i], (1,0))
modified_r_log=scipy.ndimage.gaussian_laplace(r_channel, filter_scales[i])
modified_g_log=scipy.ndimage.gaussian_laplace(g_channel, filter_scales[i])
modified_b_log=scipy.ndimage.gaussian_laplace(b_channel, filter_scales[i])
a=np.dstack((modified_r_channel, modified_g_channel, modified_b_channel))
b=np.dstack((modified_r_dog_X, modified_g_dog_X, modified_b_dog_X))
c=np.dstack((modified_r_dog_Y, modified_g_dog_Y, modified_b_dog_Y))
d=np.dstack((modified_r_log, modified_g_log, modified_b_log))
filter_response=np.dstack((a,b,c,d))
g.append(filter_response)
filter_responses=np.dstack(g)
return filter_responses
下一个函数将从 extract_filter_responses 返回的向量中提取 alpha 随机过滤器响应
def compute_dictionary_one_image(alpha, img):
response=extract_filter_responses(filter_scales, img)
d=response.shape[0]*response.shape[1]
response=response.reshape((d,-1))
alphas=np.random.choice(d, alpha) alphaed_response=response[alphas]
return alphaed_response
下一个函数将来自每个图像的 alpha 响应汇集到一个向量中,并应用来自 scipy 的 k-means 函数。该函数将训练文件路径作为参数。
def compute_dictionary(K, alpha, train_files):
m=[]
for i in range(len(train_files)):
img_path = train_files[i]
img = Image.open(img_path)
img = np.array(img).astype(np.float32)/255
re=compute_dictionary_one_image(alpha, img)
m.append(re)
m=np.array(m)
n=m.shape[0]*m.shape[1]
final_response=m.reshape((n,-1))
kmeans=KMeans(n_clusters=K).fit(final_response) dictionary=kmeans.cluster_centers_
return dictionary
酷毙了。所以现在我们有 K 个聚类中心。对于一个测试图像,我们需要用最近的聚类中心映射图像的每个像素,并绘制结果可视化。我们使用 scipy.spatial.distance.cdist 来查找最近的聚类中心的索引。下面的 get_visual_maps()函数说明了这一点
def get_visual_words(filter_scales, img, dictionary):
response=extract_filter_responses(filter_scales, img)
response=response.reshape(response.shape[0]*response.shape[1],-1)
dist=scipy.spatial.distance.cdist(response, dictionary)
visual_words=np.argmin(dist, axis=1)
visual_words=visual_words.reshape(img.shape[0],img.shape[1])
return visual_words
把所有的加在一起
我们现在将使用这些函数来创建一个管道,该管道在 1177 个图像的训练集上运行 k-means,并在一个测试图像上检查性能。
#compute cluster centers
compute_dictionary(K, alpha, train_files)#test on a image
img_path = 'image path'
img = Image.open(img_path)
img = np.array(img).astype(np.float32)/255
wordmap = visual_words.get_visual_words(filter_scales, img, dictionary)plt.imshow(wordmap)
结果
以下是该算法的一些可视化示例。我用的滤镜尺度=[1,1.5,2],K=25,alpha=10。我们能够识别图像的不同区域。在(厨房的)第一张图片中,我们可以识别不同的区域,桌子、炉子等等。请随意调整这些参数,看看您是否能获得更好的结果!
在下一篇文章中,我们将扩展按类型分类图像的管道(第一个是厨房的图像,第二个是高速公路的图像,等等..)
k-means 后的输出(由作者提供)
基于 Keras 和 W&B 的图像分割
原文:https://towardsdatascience.com/image-segmentation-using-keras-and-w-b-98223c38e4d4?source=collection_archive---------28-----------------------
该报告利用 Keras 中类似 UNET 的架构探索语义分割,并交互式可视化模型对权重偏差的预测&。
点击查看互动报道。这里有的 Colab 笔记本。
介绍
您是否有兴趣了解图像中某个对象的位置?这个物体的形状是什么?哪些像素属于对象?为了实现这一点,我们需要分割图像,即将图像的每个像素分类到它所属的对象,或者给图像的每个像素一个标签,而不是给图像一个标签。
因此,图像分割是为图像中的每个对象学习逐像素掩模的任务。与为图像中出现的每个对象给出边界框坐标的对象检测不同,图像分割对图像中的对象给出了更精细的理解。
图 1 :语义切分和实例切分。(来源)
图像分割可以大致分为两种类型:
- 语义分割:这里,每个像素属于一个特定的类。图 1 中的左图是语义分割的一个例子。像素或者属于人(一个类别),或者属于背景(另一个类别)。
- 实例分割:这里,每个像素属于一个特定的类。但是,属于离散对象的像素用不同的颜色(蒙版值)标记。图 1 中的右图是一个实例分割的例子。属于该人物类别的像素被不同地着色。
该报告将建立一个语义分割模型,并在牛津-IIIT Pet 数据集上对其进行训练。我们将交互可视化我们模型的预测权重&偏差。
数据集
我们将使用牛津-IIIT Pet 数据集来训练我们的类 UNET 语义分割模型。
数据集由图像及其像素式掩膜组成。逐像素遮罩是每个像素的标签。
- 第 1 类:属于宠物的像素。
- 第 2 类:属于宠物轮廓的像素。
- 第三类:属于背景的像素。
图 2 :宠物和它们的像素式遮罩。
下载数据集
!curl -O [http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz](http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz)!curl -O [http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz](http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz)!tar -xf images.tar.gz
!tar -xf annotations.tar.gz
数据集准备
images/
和annotations/trimaps
目录包含提取的图像及其注释(按像素的遮罩)。所需图像为.jpg
格式,而注释为.png
格式。但是,在这些目录中有一些我们不需要的文件。因此,我们将准备两个列表- input_img_paths
和annotation_img_paths
,其中包含所需图像和注释的路径。
IMG_PATH = 'images/'
ANNOTATION_PATH = 'annotations/trimaps/'
input_img_paths = sorted(
[
os.path.join(IMG_PATH, fname)
for fname in os.listdir(IMG_PATH)
if fname.endswith(".jpg")
]
)
annotation_img_paths = sorted(
[
os.path.join(ANNOTATION_PATH, fname)
for fname in os.listdir(ANNOTATION_PATH)
if fname.endswith(".png") and not fname.startswith(".")
]
)
print(len(input_img_paths), len(annotation_img_paths))
总共有 7390 张图片和注释。我们将使用 1000 幅图像及其注释作为验证集。
使用tf.data
的数据加载器
我们将使用tf.data.Dataset
构建我们的输入管道。
IMG_SHAPE = 128
AUTO = tf.data.experimental.AUTOTUNE
BATCH_SIZE = 32
def scale_down(image, mask):
# apply scaling to image and mask
image = tf.cast(image, tf.float32) / 255.0
mask -= 1
return image, mask
def load_and_preprocess(img_filepath, mask_filepath):
# load the image and resize it
img = tf.io.read_file(img_filepath)
img = tf.io.decode_jpeg(img, channels=3)
img = tf.image.resize(img, [IMG_SHAPE, IMG_SHAPE])
mask = tf.io.read_file(mask_filepath)
mask = tf.io.decode_png(mask, channels=1)
mask = tf.image.resize(mask, [IMG_SHAPE, IMG_SHAPE])
img, mask = scale_down(img, mask)
return img, mask
# shuffle the paths and prepare train-test split
input_img_paths, annotation_img_paths = shuffle(input_img_paths, annotation_img_paths, random_state=42)
input_img_paths_train, annotation_img_paths_train = input_img_paths[: -1000], annotation_img_paths[: -1000]
input_img_paths_test, annotation_img_paths_test = input_img_paths[-1000:], annotation_img_paths[-1000:]
trainloader = tf.data.Dataset.from_tensor_slices((input_img_paths_train, annotation_img_paths_train))
testloader = tf.data.Dataset.from_tensor_slices((input_img_paths_test, annotation_img_paths_test))
trainloader = (
trainloader
.shuffle(1024)
.map(load_and_preprocess, num_parallel_calls=AUTO)
.batch(BATCH_SIZE)
.prefetch(AUTO)
)
testloader = (
testloader
.map(load_and_preprocess, num_parallel_calls=AUTO)
.batch(BATCH_SIZE)
.prefetch(AUTO)
)
模型
这里使用的模型是香草 UNET 建筑。它由编码器和解码器网络组成。这个架构的输入是图像,而输出是逐像素的贴图。您可以通过 W & B 报告在深度生成建模中了解更多关于编码器-解码器(Autoencoder)网络的信息。
类似 UNET 的架构在自我监督的深度学习任务中很常见,如图像修复。
你可以在这个逐行解释中了解更多关于 UNET 建筑的信息。
图 3 :典型的 UNET 建筑。(来源)
下面显示的代码片段构建了我们的语义分割模型架构。
class SegmentationModel:
'''
Build UNET like model for image inpaining task.
'''
def prepare_model(self, OUTPUT_CHANNEL, input_size=(IMG_SHAPE,IMG_SHAPE,3)):
inputs = Input(input_size)
# Encoder
conv1, pool1 = self.__ConvBlock(32, (3,3), (2,2), 'relu', 'same', inputs)
conv2, pool2 = self.__ConvBlock(64, (3,3), (2,2), 'relu', 'same', pool1)
conv3, pool3 = self.__ConvBlock(128, (3,3), (2,2), 'relu', 'same', pool2)
conv4, pool4 = self.__ConvBlock(256, (3,3), (2,2), 'relu', 'same', pool3)
# Decoder
conv5, up6 = self.__UpConvBlock(512, 256, (3,3), (2,2), (2,2), 'relu', 'same', pool4, conv4)
conv6, up7 = self.__UpConvBlock(256, 128, (3,3), (2,2), (2,2), 'relu', 'same', up6, conv3)
conv7, up8 = self.__UpConvBlock(128, 64, (3,3), (2,2), (2,2), 'relu', 'same', up7, conv2)
conv8, up9 = self.__UpConvBlock(64, 32, (3,3), (2,2), (2,2), 'relu', 'same', up8, conv1)
conv9 = self.__ConvBlock(32, (3,3), (2,2), 'relu', 'same', up9, False)
# Notice OUTPUT_CHANNEL and activation
outputs = Conv2D(OUTPUT_CHANNEL, (3, 3), activation='softmax', padding='same')(conv9)
return Model(inputs=[inputs], outputs=[outputs])
def __ConvBlock(self, filters, kernel_size, pool_size, activation, padding, connecting_layer, pool_layer=True):
conv = Conv2D(filters=filters, kernel_size=kernel_size, activation=activation, padding=padding)(connecting_layer)
conv = Conv2D(filters=filters, kernel_size=kernel_size, activation=activation, padding=padding)(conv)
if pool_layer:
pool = MaxPooling2D(pool_size)(conv)
return conv, pool
else:
return conv
def __UpConvBlock(self, filters, up_filters, kernel_size, up_kernel, up_stride, activation, padding, connecting_layer, shared_layer):
conv = Conv2D(filters=filters, kernel_size=kernel_size, activation=activation, padding=padding)(connecting_layer)
conv = Conv2D(filters=filters, kernel_size=kernel_size, activation=activation, padding=padding)(conv)
up = Conv2DTranspose(filters=up_filters, kernel_size=up_kernel, strides=up_stride, padding=padding)(conv)
up = concatenate([up, shared_layer], axis=3)
return conv, up
注意对于我们的数据集来说OUTPUT_CHANNEL
是 3。这是因为有三类像素,如数据集部分所述。考虑我们正在进行多类分类,其中每个像素可以属于三类中的任何一类。
还有,注意由于是每像素多类分类问题,所以输出激活函数是softmax
。
OUTPUT_CHANNEL = 3
model = SegmentationModel().prepare_model(OUTPUT_CHANNEL)
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
最后用sparse_categorical_crossentropy
编译模型。稀疏,因为按像素的遮罩/注释是整数。
SemanticLogger
回调-预测的交互式可视化
在进行语义分割时,您可以在权重和偏差中交互式地可视化模型的预测。如果您的图像带有用于语义分段的遮罩,您可以记录遮罩并在 UI 中打开和关闭它们。点击查看官方文档。
Stacey Svetlichnaya的报告语义分割的图像遮罩将带您了解该工具的交互控件。它涵盖了日志图像和遮罩的各种麻烦。
下面显示的代码片段是我们的SemanticLogger
回调的助手函数。函数labels
返回一个字典,其中key
是类值,value
是标签。函数wandb_mask
以所需的格式返回图像、预测掩码和基本事实掩码。
segmentation_classes = ['pet', 'pet_outline', 'background']
# returns a dictionary of labels
def labels():
l = {}
for i, label in enumerate(segmentation_classes):
l[i] = label
return l
# util function for generating interactive image mask from components
def wandb_mask(bg_img, pred_mask, true_mask):
return wandb.Image(bg_img, masks={
"prediction" : {
"mask_data" : pred_mask,
"class_labels" : labels()
},
"ground truth" : {
"mask_data" : true_mask,
"class_labels" : labels()
}
}
)
我们的SemanticLogger
是一个定制的 Keras 回调函数。我们可以将它传递给model.fit
来记录我们的模型在一个小型验证集上的预测。权重和偏差将自动覆盖图像上的蒙版。
class SemanticLogger(tf.keras.callbacks.Callback):
def __init__(self):
super(SemanticLogger, self).__init__()
self.val_images, self.val_masks = next(iter(testloader))
def on_epoch_end(self, logs, epoch):
pred_masks = self.model.predict(self.val_images)
pred_masks = np.argmax(pred_masks, axis=-1)
# pred_masks = np.expand_dims(pred_masks, axis=-1)
val_images = tf.image.convert_image_dtype(self.val_images, tf.uint8)
val_masks = tf.image.convert_image_dtype(self.val_masks, tf.uint8)
val_masks = tf.squeeze(val_masks, axis=-1)
pred_masks = tf.image.convert_image_dtype(pred_masks, tf.uint8)
mask_list = []
for i in range(len(self.val_images)):
mask_list.append(wandb_mask(val_images[i].numpy(),
pred_masks[i].numpy(),
val_masks[i].numpy()))
wandb.log({"predictions" : mask_list})
我们很快就会看到结果。
结果
现在到了激动人心的部分。我已经训练了 15 个纪元的模型。损失和验证损失指标如下图所示。随意训练更长时期的模型,并使用其他超参数。
[## 谷歌联合实验室
编辑描述
colab.research.google.com](https://colab.research.google.com/drive/1rXV31gdyqEiXCtmSgff-H-VRuOSzv7IH?usp=sharing)
培训和验证损失如图图 3 所示。经过一些时期后,模型开始过度拟合。
图 4 :培训和验证损失指标。(点击这里查看互动报道。)
SemanticLogger
的结果如下所示。点击下面 媒体面板 中的⚙️图标(SemanticLogger 的结果)查看交互控件。您可以分别可视化图像和遮罩,并可以选择要可视化的语义类。
观察
- 该模型学习很好地预测
pet
和background
类。 - 我们可以看到模型很难细分
pet_outline
类。这是因为高等级的不平衡,并且模型没有被正则化以对抗这种不平衡。
图 5 :语义记录器回调结果。(点击此处查看互动报道。)
结论和最终想法
我希望你喜欢这篇关于语义分割的报告。这份报告有两个目的:
- 让感兴趣的人更容易使用语义分割技术。
- 展示权重和偏差如何帮助交互式地可视化模型的预测和度量。此外,展示人们可以从这些可视化中得到的观察结果。
最后,以下是一些值得一读的资源:
- 语义图像分割概述
- 图像分割
- 驾驶座上的视野
我很想在评论区得到你的反馈。😄
基于掩模 R-CNN 的图像分割
原文:https://towardsdatascience.com/image-segmentation-using-mask-r-cnn-8067560ed773?source=collection_archive---------10-----------------------
使用 Python 和 OpenCV 执行实例分割的简单教程
计算机视觉作为一个研究领域,近年来有了很大的发展。自从引入卷积神经网络以来,在诸如分类、对象检测、图像分割等领域中的技术发展水平。不断受到挑战。在提供非常高计算能力的复杂硬件的帮助下,这些神经网络模型正被实时应用于新兴领域,如自主导航。
我们今天关注的主题是计算机视觉的一个子领域,称为图像分割。更准确地说,我们将对图像或视频执行实例分割。好吧,那是一大堆技术术语。让我们倒回去一点,理解这些术语的意思。每一个都需要一个帖子,我们现在不会深入讨论。在不久的将来,我可能会分别就它们写一些文章。现在,让我们以一种简化的方式来理解什么是图像分割。此外,如果你不知道什么是对象检测,我建议你仔细阅读它,因为它会使你更容易理解即将到来的概念。我还写过一篇关于实现对象检测算法的简明文章。如果你对它感兴趣,你可以在我的个人资料中找到它。
什么是图像分割?
图像分割是将图像中的每个像素分类为属于特定类别的过程。虽然有几种类型的图像分割方法,但在深度学习领域,两种类型的分割占主导地位:
- 语义分割
- 实例分割
让我对两者做一个简明的比较。在语义分割中,属于特定类别的对象的每个像素被赋予相同的标签/颜色值。另一方面,在实例分割中,一个类的每个对象的每个像素被给予单独的标签/颜色值。看看下面的图片,把前面的句子再读一遍,就能理解清楚了。希望现在有意义:)
来源— 物质港
实施!
Mask R-CNN(区域卷积神经网络)是一个实例分割模型。在本教程中,我们将看到如何在 OpenCV 库的帮助下用 python 实现这一点。如果你有兴趣了解更多关于这个模型的内部工作原理,我在下面的参考部分给出了一些链接。这将帮助您更详细地理解这些模型的功能。
我们首先克隆(或下载)给定的存储库:-
[## matterport/Mask_RCNN
这是 Mask R-CNN 在 Python 3、Keras 和 TensorFlow 上的实现。该模型生成边界框和…
github.com](https://github.com/matterport/Mask_RCNN)
确保在 python 环境中安装了**requirements.txt**
中列出的所有依赖项。之后,不要忘记运行命令**python setup.py install**
。我们将使用在 coco 数据集上预先训练的模型。权重可以从这里的下载,类名可以从我的库的**coco_classes.txt**
文件中获得。现在让我们开始在克隆或下载的存储库中创建一个名为**mask.py**
的文件,并导入所需的库。
我们将使用自己的自定义类,因此将继承现有的**CocoConfig**
类并覆盖其变量值。请注意,您可以根据您的 GPU 的能力来设置这些值。
现在我们将创建一个名为**prepare_mrcnn_model()**
的函数,它负责读取类标签,并根据我们的自定义配置初始化模型对象。我们还指定了颜色到对象的映射来实现语义分割(如果需要的话)。我们稍后会谈到这一点。我们模型的模式应该设置为“推理”,因为我们将直接使用它进行测试。提供预训练权重的路径,以便可以将其加载到模型中。
下一个函数将是本教程最关键的部分。不要被它的长度吓到,它是迄今为止最简单的一个!我们现在将测试图像传递给模型对象的 detect 函数。这将为我们执行所有的对象检测和分割。我们现在有两个选择——我们可以选择为该类的每个对象分配相同的颜色,或者为每个对象分配不同的颜色,而不管其属于哪个类。因为这篇文章坚持实现实例分段,所以我选择了后者。我给了你灵活性去做这两件事,这样你可以加强你对同一件事的理解。这可以通过切换instance_segmentation
参数来完成。
上面的代码绘制了对象的边界框,同时也在像素级别对其进行了分割。它还提供了对象所属的类以及分数。我还为您提供了使用 Matterport 的 MRCNN 实现中的内部可视化功能来可视化输出的选项。这可以使用上面显示的mrcnn_visualize
布尔参数来访问。
既然我们已经有了模型和代码来处理它产生的输出,我们只需要读取并传入输入图像。这是通过下面给出的perform_inference_image()
函数完成的。save_enable
参数允许我们保存对象被分割的处理过的图像。
这现在也可以很容易地扩展到视频和网络直播,如这里的perform_inference_video()
函数所示。
基础图像源— 可可女士数据集
正如你在上面的图片中看到的,每个物体都被分割了。该模型检测类别 book 的多个实例,因此为每个实例分配一种单独的颜色。
这是另一个对象被分割的例子。基础图片来源:可可女士 D ataset
就是这样,伙计们!您现在有了一个可以使用的实例分段管道。这个项目的全部代码以及一个干净易用的界面可以在我的知识库中找到。还可以在那里找到使用它的其他步骤。我确实意识到你们中的一些人可能没有兼容 CUDA 的 GPU,或者根本没有 GPU。所以,我还提供了一个可以用来运行代码的 Colab 笔记本。在 Colab 中集成网络摄像头的使用仍然困扰着我,因此目前只能对图像和视频文件进行推断。 Matterport 的 Mask R-CNN 代码默认支持 Tensorflow 1.x。如果你正在使用 TF2.x,你最好直接派生/克隆我的库,因为我已经移植了代码来支持 TF2.x
我建议您阅读 R-CNN 架构(尤其是更快的 R-CNN ),以完全理解 Mask R-CNN 的工作原理。我在下面的参考资料部分给出了一些好的资源的链接。此外,对对象检测如何工作有一个良好的理解将使理解图像分割的关键变得容易。
~ ~**SowmiyaNarayanan G
心灵字节:-
“取得成功的秘诀在于开始行动。” —马克·吐温。
PS:-
如果你有任何疑问,请随时联系我,我会帮助你的。我乐于接受各种批评,以改进我的工作,这样我就能更好地满足未来像你这样的探险家的需求。不要犹豫,让我知道你的想法。也可以在 LinkedIn 上和我联系。
参考资料:-
- 最初的“面具 R-CNN”论文——【https://arxiv.org/pdf/1703.06870.pdf
- 马特波特的面具 R-CNN 资源库<——“超级重要😃"
- R-CNN,快速 R-CNN,更快 R-CNN,YOLO——目标检测算法
- 我的代码 YOLOv3 实现
用 5 行代码实现图像分割
原文:https://towardsdatascience.com/image-segmentation-with-six-lines-0f-code-acb870a462e8?source=collection_archive---------4-----------------------
用 PixelLib 进行语义和实例分割。
计算机视觉每天都在发展。图像分类和目标检测等流行的计算机视觉技术已被广泛用于解决许多计算机视觉问题。在图像分类中,对整个图像进行分类。对象检测通过检测图像中存在的单个对象的位置来扩展图像分类。
图像分割:
一些计算机视觉问题需要更深入地理解图像中的内容。分类和对象检测可能不适合解决这些问题。对解决一些关键的计算机视觉问题的有效技术的需求导致了图像分割的发明。
每个图像都由一组像素值组成。图像分割是在像素级别对图像进行分类的任务。通过根据分配给图像中存在的每个像素值的类别将图像分成不同的片段,机器能够更有效地分析图像。
图像分割中采用的独特技术使其适用于解决关键的计算机视觉问题。这些问题需要关于图像中存在的对象的详细信息,这些细节不能通过对整个图像进行分类或为图像中存在的对象提供边界框来提供。图像分割的一些主要贡献包括:
-无人驾驶汽车的高效视觉系统,用于有效理解道路场景。
-医学图像分割为执行诊断测试提供身体部分的分割。
- 卫星图像分析。
有两种主要类型的图像分割:
语义分割:用相同的像素值分类的对象用相同的颜色图分割。
实例分割:它不同于语义分割,因为同一对象的不同实例是用不同的颜色图分割的。
来源:Wikicommons.com(CC0)
语义分割
实例分割
PixelLib: 是一个为在现实生活问题中轻松实现图像分割而构建的库。PixelLib 是一个灵活的库,可以集成到需要应用图像分割的软件解决方案中。
用五行代码就可以实现语义和实例分割。
安装 PixelLib 及其依赖项:
安装最新版本的 tensorflow(tensorflow 2.0)和:
- pip3 安装张量流
安装 opencv-python 与:
- pip3 安装 opencv-python
使用以下内容安装 scikit-image:
- pip3 安装 scikit-image
安装枕头,包括:
- pip3 安装枕头
安装 Pixellib:
- pip3 安装 pixellib
用 PixelLib 实现语义分割:
用 deeplabv3+模型实现语义切分的代码在 pascal voc 数据集上进行训练。
我们将观察每一行代码:
import pixellib
from pixellib.semantic import semantic_segmentation segment_image = semantic_segmentation()
执行语义分割的类是从 pixelLib 导入的,我们创建了该类的一个实例。
segment_image.load_pascalvoc_model(“deeplabv3_xception_tf_dim_ordering_tf_kernels.h5”)
在上面的代码中,我们加载了在 pascal voc 上训练的用于分割对象的 xception 模型。模型可以从这里下载。
segment_image.segmentAsPascalvoc(“path_to_image”, output_image_name = “path_to_output_image)
我们加载了该函数来对图像执行分割。该函数有两个参数…
- 路径至图像:- 这是待分割图像的路径。
- 输出图像名称:- 这是保存分割图像的路径。它将保存在您当前的工作目录中。
sample1.jpg
来源:pxhere.com(CCO)
分割后保存的图像,图像中的物体被分割。如果您愿意,可以在图像上应用分段覆盖。
segment_image.segmentAsPascalvoc("sample1.jpg", output_image_name = "image_new.jpg", overlay = True)
我们添加了额外的参数叠加并将其设置为真,我们获得了一个在物体上有分段叠加的图像。
这个异常模型是在 pascal voc 数据集上训练的,这个数据集有 20 个对象类别。
对象及其相应的颜色映射
PixelLib 的特殊用途可能需要您返回分段输出的数组:
使用代码获得分段输出的数组
output, segmap = segment_image.segmentAsPascalvoc()
您可以测试获取数组的代码,并通过修改下面的语义分段代码打印出输出的形状。
使用代码获得输出和分割覆盖图的数组
segmap, segoverlay = segment_image.segmentAsPascalvoc(overlay = True)
使用 PIXELLIB 进行实例分割:
使用 PixelLib 的实例分割基于 Mask R-CNN 框架。
实现实例分段的代码:
观察每一行代码
import pixellibfrom pixellib.instance import instance_segmentationsegment_image = instance_segmentation()
执行实例分段的类被导入,我们创建了该类的一个实例。
segment_image.load_model("mask_rcnn_coco.h5")
这是加载 mask r-cnn 模型以执行实例分割的代码。从这里下载面具 r-cnn 模型。
segment_image.segmentImage("path_to_image", output_image_name = "output_image_path")
这是对图像执行实例分割的代码,它有两个参数:
- path_to_image: 模型预测的图像的路径。
- 输出图像名称:保存分割结果的路径。它将保存在您当前的工作目录中。
sample2.jpg
来源:wikicommons.com(CC0)
这是保存在当前工作目录中的图像。
您可以使用边界框实现分段。这可以通过修改代码来实现。
segment_image.segmentImage("path_to_image", output_image_name = "output_image_path", show_bboxes = True)
我们添加了一个额外的参数 show_bboxes 并将其设置为 true ,使用边界框生成分割蒙版。
Mask R_CNN 模型是在微软 Coco 数据集上训练的,这个数据集有 80 个常见的对象类别。该模型可以对这些对象类别执行实例分割。
Coco 数据集中存在的对象类别列表:
['BG ','人','自行车','汽车','摩托车','飞机','公共汽车','火车','卡车','船','交通灯','消防栓','停车计时器','长凳','鸟','猫','狗','马','羊','牛','大象','熊','斑马','长颈鹿','背包','雨伞','手提包','领带','手提箱','飞盘','滑雪板','运动球','风筝','棒球棍','棒球手套','滑板','冲浪板','网球拍','瓶子','酒杯','杯子','
PixelLib 用于实例分割的特殊用途。
获取以下数组:
-检测到的物体阵列
-对象'对应 class _ ids '数组
-分段屏蔽阵列
-输出的数组
通过使用此代码
segmask, output = segment_image.segmentImage()
您可以通过修改下面的实例分段代码来测试获取数组的代码并打印出输出的形状。
通过包含参数show _ bbox,获得带有边界框的分段数组。
segmask, output = segment_image.segmentImage(show_bboxes = True)
安装 PixelLib,并使用您想要的任意数量的图像进行测试。
访问 PixelLib 的官方 github 库。
访问 PixelLib 的官方文档
通过以下方式联系我:
电子邮件:olafenwaayoola@gmail.com
推特: @AyoolaOlafenwa
脸书:阿尤拉·奥拉芬娃
领英:阿尤拉·奥拉芬娃
基于卷积神经网络和自动编码器的图像超分辨率
原文:https://towardsdatascience.com/image-super-resolution-using-convolution-neural-networks-and-auto-encoders-28c9eceadf90?source=collection_archive---------8-----------------------
深度学习提升图像质量指南!
(来源)
问题陈述挺熟悉的。你们可能都在某个时候遇到过图像失真的问题,因此会试图提高图像质量。嗯,由于深度学习技术的进步,我们将尝试通过训练卷积神经网络和使用自动编码器来提高图像的分辨率!
先决条件
- 基本了解卷积神经网络(CNN)
- TensorFlow、Keras 和其他一些强制性 python 库的工作。
什么是自动编码器?
自动编码器是一种用于无监督学习的生成模型。
通俗地说,可以说这些模型获取一些输入 x ,试图学习一些潜在特征,然后在这些学习到的特征的帮助下重构输入 x 以给出一些期望的输出 X
在这里,图像输入被重建(源)
我们将使用自动编码器模型的概念来增加图像的分辨率。关于自动编码器的详细了解,点击 这里 。
实施:
库导入
让我们打开 Jupyter 笔记本,导入一些需要的库。
import numpy as np
import cv2
import glob
import tensorflow as tf
from tensorflow.keras import Model, Input, regularizers
from tensorflow.keras.layers import Dense, Conv2D, MaxPool2D, UpSampling2D, Add, Dropout
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.preprocessing import image
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
import pickle
下载数据集
我们将在野生家园 数据集中对 标记的人脸进行处理。该数据集包含标记人脸的数据库,通常用于人脸识别和检测。然而,我们的目的不是检测人脸,而是建立一个模型来提高图像分辨率。
点击此处 下载数据集。
数据集由多个子目录组成,这些子目录包含该人的各种图像。因此,从这些目录中捕获图像路径非常重要。
face_images = glob.glob('lfw/lfw/**/*.jpg') #returns path of images
print(len(face_images)) #contains 13243 images
加载并预处理图像
原始图像的尺寸为 250 x 250 像素。然而,在普通计算机上处理这些图像需要很大的计算能力。因此,我们将把所有图像的尺寸缩小到 80 x 80 像素。
由于大约有 13,000 张图像,如果我们单独处理它们,将会花费很多时间。因此,我们利用 python 中提供的 多处理 库来简化执行。
tqdm 是一个进度库,我们用它来获得工作完成的进度条。
from tqdm import tqdm
from multiprocessing import Poolprogress = tqdm(total= len(face_images), position=0)
def read(path):
img = image.load_img(path, target_size=(80,80,3))
img = image.img_to_array(img)
img = img/255.
progress.update(1)
return imgp = Pool(10)
img_array = p.map(read, face_images)
为了将来节省时间,让我们在 pickle 库的帮助下存储我们的 img_array (包含图像):
with open('img_array.pickle','wb') as f:
pickle.dump(img_array, f)print(len(img_array))
模型训练的数据准备
现在,我们将把数据集分成训练集和验证集。我们将使用训练数据来训练我们的模型,验证数据将用于评估模型。
all_images = np.array(img_array)#Split test and train data. all_images will be our output images
train_x, val_x = train_test_split(all_images, random_state = 32, test_size=0.2)
由于这是一个图像分辨率增强任务,我们将扭曲我们的图像,并把它作为一个输入图像。原始图像将被添加为我们的输出图像。
#now we will make input images by lowering resolution without changing the size
def pixalate_image(image, scale_percent = 40):
width = int(image.shape[1] * scale_percent / 100)
height = int(image.shape[0] * scale_percent / 100)
dim = (width, height) small_image = cv2.resize(image, dim, interpolation = cv2.INTER_AREA)
# scale back to original size
width = int(small_image.shape[1] * 100 / scale_percent)
height = int(small_image.shape[0] * 100 / scale_percent)
dim = (width, height) low_res_image = cv2.resize(small_image, dim, interpolation = cv2.INTER_AREA) return low_res_image
我们的想法是将这些扭曲的图像输入到我们的模型中,让模型学会恢复原始图像。
train_x_px = []for i in range(train_x.shape[0]):
temp = pixalate_image(train_x[i,:,:,:])
train_x_px.append(temp)train_x_px = np.array(train_x_px) #Distorted images# get low resolution images for the validation set
val_x_px = []for i in range(val_x.shape[0]):
temp = pixalate_image(val_x[i,:,:,:])
val_x_px.append(temp)val_x_px = np.array(val_x_px) #Distorted images
输入图像
原象
模型构建
让我们定义模型的结构。此外,为了克服过度拟合的可能性,我们在卷积层中使用了 l1 正则化技术。
Input_img = Input(shape=(80, 80, 3))
#encoding architecture
x1 = Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(Input_img)
x2 = Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x1)
x3 = MaxPool2D(padding='same')(x2)x4 = Conv2D(128, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x3)
x5 = Conv2D(128, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x4)
x6 = MaxPool2D(padding='same')(x5)encoded = Conv2D(256, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x6)
#encoded = Conv2D(64, (3, 3), activation='relu', padding='same')(x2)# decoding architecture
x7 = UpSampling2D()(encoded)
x8 = Conv2D(128, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x7)
x9 = Conv2D(128, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x8)
x10 = Add()([x5, x9])x11 = UpSampling2D()(x10)
x12 = Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x11)
x13 = Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(10e-10))(x12)
x14 = Add()([x2, x13])# x3 = UpSampling2D((2, 2))(x3)
# x2 = Conv2D(128, (3, 3), activation='relu', padding='same')(x3)
# x1 = Conv2D(256, (3, 3), activation='relu', padding='same')(x2)
decoded = Conv2D(3, (3, 3), padding='same',activation='relu', kernel_regularizer=regularizers.l1(10e-10))(x14)autoencoder = Model(Input_img, decoded)
autoencoder.compile(optimizer='adam', loss='mse', metrics=['accuracy'])
您可以根据自己的选择和要求修改该模型,以获得更好的结果。你也可以改变层数、单位数或一些正则化技术。暂时先往前走,看看我们的模型是什么样子的!
autoencoder.summary()
模型摘要的屏幕截图
模特培训
我们将首先定义一些回调,以便将来模型可视化和评估变得容易。
early_stopper = EarlyStopping(monitor='val_loss', min_delta=0.01, patience=50, verbose=1, mode='min')model_checkpoint = ModelCheckpoint('superResolution_checkpoint3.h5', save_best_only = True)
让我们训练我们的模型:
history = autoencoder.fit(train_x_px,train_x,
epochs=500,
validation_data=(val_x_px, val_x),
callbacks=[early_stopper, model_checkpoint])
在 12GBNVIDIATesla K80GPU 上,每个纪元的执行时间约为 21 秒。65 世达到早期停止。
现在,让我们在测试数据集上评估我们的模型:
results = autoencoder.evaluate(val_x_px, val_x)
print('val_loss, val_accuracy', results)
val_loss,val _ accuracy[0.002111185426451,0.265356767
我们从我们的模型中得到了一些非常好的结果,大约 93%的验证准确性和 0.0021 的验证损失。
做出预测
predictions = autoencoder.predict(val_x_px)n = 4
plt.figure(figsize= (20,10))for i in range(n):
ax = plt.subplot(3, n, i+1)
plt.imshow(val_x_px[i+20])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False) ax = plt.subplot(3, n, i+1+n)
plt.imshow(predictions[i+20])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)plt.show()
第一行—输入图像,第二行—输出图像
结尾注释
在这个故事中,我们学习了自动编码器的基本功能,并实现了一个图像超分辨率增强任务。这项任务在日常生活中可能有多个用例。例如,我们也可以使用这种技术来提高低分辨率视频的质量。因此,即使没有标签,我们也可以利用图像数据解决一些现实世界的问题。
如果您有任何其他使用案例或技术来处理图像数据,并且如果您发现了更多改进的图像增强模型,请在下面的响应块中分享!
这篇文章的全部代码可以在 这里 找到。
参考
- Snehan Kekre 在 Coursera 上的一门课程。
使用 Javascript 进行图像放大
原文:https://towardsdatascience.com/image-upscaling-with-javascript-836445267496?source=collection_archive---------48-----------------------
实践教程
我最近发布了一个工具,upscaler js,它可以用 Javascript 在你的浏览器中放大图片,并将图片尺寸缩小 1/16。它的设计是与模型无关的——你可以即插即用任何可以转换为 Tensorflow.js 的训练过的模型
在这篇文章中,我想展示我认为的浏览器中神经网络的一个杀手级用例,以及我如何发现这项研究,将其转换为 Javascript,以及未来改进它的方法。
https://upscaler.ai //作者动画
假设你在做一个电子商务平台。你的用户上传产品照片来销售。
你设计了一个看起来很棒的网站,用来突出你的用户手工制作的美丽和奇妙的产品。只有一个问题——一旦你启动,你发现你的用户正在上传小的像素化的图片,突然之间你美丽的网站看起来不那么漂亮了。
(我是从经验中得出的结论——这种情况在我身上发生过不止一次。)
你可以回头向用户唠叨要更好的图像——有时这是可行的。但通常情况下,他们所能得到的只有他们提供的图片。也许图像是从 pdf 截屏的,或者也许图像是旧的,用户没有更好的。即使他们有更好的图像,让你的用户回去帮你修改他们的图像也是一件费力的事情,即使这是为了他们的利益。
有没有我们可以探索的技术解决方案?当然有,它叫做超分辨率。
你所说的“超级分辨率”是什么?
假设有人上传了一张 150 像素的照片到我们的电子商务网站:
merec0 的快乐狗
我们想在我们的主页上展示这张图片,因为它是一只漂亮的狗,但我们的设计要求图片为 300 像素。我们能做什么?如果我们将每个像素加倍,我们会得到看起来像素化的更大图像:
通过复制像素放大图像
你必须努力在浏览器中实现像素化的外观;默认情况下,大多数浏览器会对图像应用某种缩放算法,通常是双三次插值,如下所示:
使用双三次插值放大图像。
使用双三次插值放大的图像看起来肯定没有第一幅图像像素化,我敢打赌大多数人会觉得它更有美感,但它很模糊,没有人会误认为是高分辨率图像。
超分辨率是一种机器学习技术,用于从较低分辨率的图像重建较高分辨率的图像。你可以把这个过程想象成在图像中绘制新像素,实现比双三次插值算法更高的保真度。
使用 GAN 放大图像。
有许多不同的方法可以用来实现超分辨率,也有一些很棒的博客文章描述了基本的理论这里和这里。
超分辨率通常使用 Python 在后端实现。以这种方式建造它有很好的理由。在后台运行它可以让你接触到强大的硬件,而这些硬件可以让你使用最新、最精确的模型。如果获得最高分辨率的图像很重要,后端部署是一个不错的选择。此外,许多用例是“一次缩放,经常显示”——如果放大一幅图像需要更长的时间,没什么大不了的。
另一方面,在后端实现这一点也有缺点。一个是即时反馈——你需要将图像上传到服务器,进行处理,然后发送回来,这可能需要一段时间,取决于你的用户连接和你的模型大小。这可能不是小事,特别是因为如此多的前沿实现处于最前沿,具有不稳定的依赖性和不断变化的需求。而且,如果您的部署需要 GPU,这可能会成为一个昂贵的提议,并且难以扩展。
事实证明,我们可以在浏览器中运行,这样做有一些明显的好处。
第一,部署问题?完全消失了。用 Javascript 运行神经网络意味着不需要安装任何东西,不需要配置任何 GPU——tensor flow . js 会处理所有这些事情。模型直接在用户的浏览器中运行。
其次,你的用户会看到更多即时反馈。特别是对于带宽可能较慢的连接,如电话,直接在设备上执行推理可以省去昂贵的往返步骤。
第三,也是我认为最有说服力的论点——你可以提供更小的图片。有时候,小得多的图像。例如,上面的那些图片?300px 是 724kb。150px 版本?是 9kb 。这是一张原始文件大小的百分之六的图像。这是一个巨大的减少!
当然,在浏览器中运行有一些明显的缺点。最大的问题是你受到用户硬件的限制。这体现在两个方面。一个是,如果你想部署最新最棒的型号,你可能就要倒霉了。特别是如果他们是 GPU 饥饿,他们只是可能无法在浏览器中运行。近年来,包括苹果和谷歌在内的硬件制造商已经投入了巨额资金来提高他们的设备上芯片的性能,特别关注提高在设备上运行神经网络的能力。好消息是,年复一年,这项技术的性能会越来越好;坏消息是,对于使用旧设备的用户来说,性能差异将变得更加显著。如果您想要跨平台的一致体验,服务器端解决方案可能是更好的选择。
最终,尽管精确的权衡将取决于用例,但 Javascript 绝对是考虑该技术应用的一个有价值的竞争者。让我们看看如何评估那里有什么,看看什么对我们的目的有用。
通过小道消息听到的
如果你来自 Javascript 世界,你脑海中的一个问题是——你最初是怎么听说这项研究的?
大多数前沿的机器学习研究都张贴在 arxiv.org 的网站上,在那里可以免费搜索和下载 PDF 格式的研究成果。这是学术研究,论文可能倾向于理论和数学,很难理解。这可以吓跑很多人——一开始当然是把我吓跑了。
我不想低估充分理解研究的重要性——深入理解理论通常可以带来与你的领域相关的新见解和发展——但你不一定需要对使用它的技术有深刻的理解。特别是如果你专注于推理,就像我们在这种情况下,你可以依靠他人来评估研究,以及实现训练代码,在某些情况下,提供训练模型。
有一个网站就是这么做的,叫做论文,代码:
https://paperswithcode.com截图
研究按主题领域分类,并根据其相对于公认指标的表现进行排名。甚至有一个专门针对这个领域的特定类别。
https://paperswithcode.com 的截图
您可以根据标准数据集查看每个实现的性能,并查看如何根据不同的指标对它们进行测量。PSNR 和 SSIM 是测量超分辨率任务性能的两种常用方法; PSNR 可以测量噪声, SSIM 测量两幅图像的相似度。
来自“图像间相似性度量方法快速概述”
指标可能有点棘手。你可以在上面的图像中看到,相同的 PSNR 分数可以有完全不同的 SSIM 分数,具有相应不同的视觉表现。
PSNR 和 SSIM 都是衡量一幅图像彼此差异的标准,但都不能代替人类的评价。作为人类,我们感知图像的方式与计算机不同。比如说,饱和度不同但也更清晰的一组像素可能导致较低的度量分数,但从人的角度来看更美观的分数。
SR 算法通常由几种广泛使用的失真度量来评估,例如 PSNR 和 SSIM。然而,这些度量从根本上与人类观察者的主观评价不一致。感知质量评估采用非参考指标,包括马评分和 NIQE,两者均用于计算 SR 挑战赛中的感知指数。在最近的一项研究中,Blau 等人发现失真和感知质量是相互矛盾的。— 王等
除了判断模型准确性的主观性,准确性不是我们最关心的还有其他原因。记住,我们的最终目标是一个用 Javascript 运行的模型。考虑以下因素也很重要:
- 一篇好论文。我们想要一个健康的建筑。我们可能需要对基础理论有所了解,所以论文清晰易懂、严谨是很重要的;一篇论文被引用的频率也可以很好地反映其整体质量。
- 性能良好。速度和准确性一样重要。运行一分钟的模型不适合浏览器。
- 可保存,可转换。实现的模型必须与 Javascript 兼容。我们将很快触及细节,但最重要的是坚持 Tensorflow 实现,因为 Tensorflow.js 是在浏览器中进行机器学习的主要方式,所以 Pytorch 实现是不可能的。
我最终选择了 ESRGAN 。
我开始看按分数排序的论文。一些得分较高的实现要么没有链接的代码实现,要么代码实现完全在 Pytorch 中。(并不是所有的代码实现都会在 paperswithcode.com 上展示,所以自己去谷歌一下是个好主意。)
ESRGAN 在指标上排名很高,并在 Tensorflow 中实现了很多。这篇论文本身相当清晰易懂。ESRGAN 基于以前的架构, SRGAN ,它本身是一个健壮的架构,但 ESRGAN 进行了许多改进,包括改进了生成器的构建模块,改进了预测图像显示逼真程度的鉴别器,以及更有效的感知损失。
在我能找到的实现中,我觉得有三个满足了我的标准,看起来代码质量不错,文档也不错。
- idealo/图像超分辨率
- krasserm/超分辨率
- peteryuX/esrgan-tf2
如果不下载并运行代码,很难确定一个实现是否适合。如果你习惯于安装一个npm
库并直接进入,请做好准备:使用机器学习代码通常是一种沮丧的练习。解决依赖性挑战、环境问题和内存瓶颈可能会将评估变成一件多天的事情。
出于这个原因,包含Dockerfile
s 或 Google Colab 链接的回购通常是一个非常好的迹象。这也是一个好迹象,当作者包括预训练的重量,以及这些重量是如何训练的文件。如果您能够直接跳到推论,它有助于更快地评估模型;同样,关于如何训练这些权重的信息使您能够测试自己的实现,这为您提供了一个坚实的基准。在回购中忽略这些不会破坏交易,但会让你的日子更难过。
不管作者是否提供 Dockerfile,我通常会建立自己的docker file,因为当我探索 repo 时,我会安装自己的依赖项并编写探索性代码,我希望能够以可复制的方式运行这些代码。几乎在每一种情况下,当我玩了几个星期的机器学习代码后,当我回来时,我会遇到一些深奥的错误,这些错误是由一些包过时或升级引起的。固定您的版本,从一开始就获得一个可复制的环境!
我最终选定了由 idealo 实现。代码易于阅读,提供了预先训练的模型,作者提供了他们探索太空之旅的精彩记录。然而,真正的关键是我只需要做一些修改就可以将 RDN 模型转换成 Javascript。转换 RRDN 模型有点棘手——稍后会详细介绍。
转换为 Javascript
Tensorflow.js 提供了一个方便的命令行工具,用于将模型转换为 Javascript,称为 TFJS converter。您可以使用类似下面的内容来转换模型:
tensorflowjs_converter --input_format=keras --output_format=tfjs_layers_model ./rdn-model.h5 rdn-tfjs
我已经整合了一个谷歌实验室来证明这一点。
为了将模型干净地转换成 Javascript,需要注意一些事情:
- 模型必须以 Keras 或与 Tensorflow 转换器兼容的其他格式保存。此外,确保将模型转换成 Javascript,而不仅仅是权重。如果是后者,您可能会收到一个含糊不清的错误,没有任何关于正在发生什么的指导。
- 变量不能引用
self
——这让我对 idealo 的实现产生了误解。(参考本 Github 问题、或我的 PR ,寻求解决方案) - 所有张量运算都必须用 Javascript 实现。除了试错法,我不知道还有什么更好的方法来检查这个问题(也就是转换模型,看看它是否能运行)。
- 如果实现了自定义层,则必须用 Javascript 重新实现。例如,RRDN 模型的定制层必须重新实现,以便干净地保存。在这篇文章的后面,我将讨论如何处理自定义层。
- 在 Tensorflow.js 转换器的输出中,我必须手动将模型的
class_name
从Functional
更改为Model
。(在 Google Colab 中,这是实现这个的单元格。)不知道为什么会这样,也不知道是不是 bug 欢迎评论! - 图像的任何预处理和后处理都需要用 Javascript 再现。
拉你的体重,好神经元
性能是基于浏览器的应用程序的关键。更瘦的模特表现更好。
有两种方法可以提高 Javascript 的性能。
首先,我们可以量化我们的模型。量化模型意味着降低模型权重的精度。这可能会导致较低的准确性,但是可以显著减小模型的大小(并且具有使模型比 gzip 更可压缩的附带好处)。
我们可以在 tensorflow js 转换器中直接量化:
tensorflowjs_converter \
--input_format tfjs_layers_model \
--output_format tfjs_layers_model \
--quantize_uint8 \
original_model/model.json
quantized_model/
在这种特定情况下,最大量化量uint8
,对最终模型的性能没有显著影响。
第二,我们可以修剪我们的模型。修剪是我们在训练中剔除表现不佳的权重的过程。我还没有亲自探索过这条途径,但是如果你感兴趣的话你可以在这里阅读更多相关内容。对于在前端挤出额外的性能来说,这无疑是一个很有前途的策略。
浏览器中的推理—给我看看代码!
我们已经转换并量化了 RDN 模型。成功!现在,我们如何让它在浏览器中运行?我们可以用以下内容加载我们的模型:
import * as tf from "@tensorflow/tfjs";
tf.loadLayersModel("./rdn-tfjs/model.json");
确保你加载的是model.json
、而不是、bin
文件。
然后,我们可以得到作为张量的图像:
const img = new Image();
img.crossOrigin = "anonymous";
img.src = "your-image";
img.onload = () => {
const tensor = tf.browser.fromPixels(img).expandDims(0);
};
张量是几乎所有神经网络中使用的一种数字数据结构,你可以在这里阅读更多关于它们的内容。
在上面的代码中需要注意两件事:
- 如果你正在处理来自其他领域的图像,你可能会遇到 CORS 问题。将
crossOrigin
设置为anonymous
会有所帮助。 - 你需要调用
expandDims
从你的图像中获取的张量。你需要给你的模型传递一个四维张量;要了解更多原因,你可以点击这里查看我关于图像分类的文章。
现在我们有了一个张量,我们可以在模型中运行它:
import tensorAsBase64 from 'tensor-as-base64';
const prediction = model.predict(tensor).squeeze();
还有维奥拉。你有一个升级的张量,准备在你的浏览器中显示!
下面是 CodeSandbox 上所有代码实现:
这仍然需要一段时间——在我的例子中,大约 2.5 秒——这对于生产来说是不可接受的。此外,它还有一个令人讨厌的副作用,就是在工作时冻结了用户界面。让我们来看一些不同的提高性能的策略。
热身
Tensorflow.js 中神经网络的初始调用将花费很长时间,但是随后的调用将会快得多。
TensorFlow.js 通过运行 WebGL 着色器程序在 GPU 上执行操作。当用户要求执行操作时,这些着色器被缓慢地组装和编译。着色器的编译发生在 CPU 的主线程上,可能会很慢。TensorFlow.js 将自动缓存已编译的着色器,从而使第二次调用具有相同形状的输入和输出张量的相同操作的速度快得多。— Tensorflow.js 文档
我们可以利用这一点,通过传递一个虚拟张量来“预热”我们的模型。这里有一些你可以使用的代码(查看 CodeSandbox 上的代码):
const dummyTensor = tf.zeros([1, img.height, img.width, 3]);
model.predict(dummyTensor);
在这种情况下,我的推理时间下降到 150 毫秒。好多了!然而,这只有在张量大小完全匹配的情况下才有效。我们显然不能依赖这个——用户可以上传任何大小和比例的照片。此外,当模型运行其预测时,用户界面上仍有明显的滞后。
让我们先试着解决第二个问题。如果我们把计算从主线程转移到一个网络工作者身上会怎么样?
网络工作者
这里有一个 CodeSandbox 链接,演示了 Web Workers 的使用。(这个例子使用了 UpscalerJS,而不是手工写出 TFJS 代码,但是概念是一样的。)
将代码转移到 web worker 可以让我们将处理从主线程中转移出来,从而让我们以更流畅的速度运行动画。然而,它不是万灵药;动画中仍有一些起伏。我相信这种波动来自 GPU 本身锁定线程,这在旧设备上比新设备上表现得更糟。网络工作者绝对有帮助,但他们不能完全解决问题。
将图像分割成块
如果我们不是一次处理完整的图像,而是将图像细分为多个部分分别处理,会怎么样?
作者制作的动画
如果我们将图像细分成几个部分,我们可以将一个长任务分成 4 个任务,每个任务之后我们可以释放 UI 线程:
const tensor = tf.browser.fromPixels(img);
const [height, width] = tensor.shape;
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
const slicedTensor = tensor.slice(
[(i * height) / 2, (j * width) / 2],
[height / 2, width / 2]
);
const prediction = model.predict(slicedTensor.expandDims(0)).squeeze();
}
}
这里有一个 CodeSandbox 链接演示这个。
这极大地提高了我们代码的响应能力,但现在又出现了一个新问题:
作者制作的动画
这些放大的图像往往在边缘有伪像。这是许多放大算法中固有的一个相当常见的问题,但它通常不是一个问题,除非你密切关注放大图像的边缘。然而,在这种情况下——因为我们将许多图像拼接成一幅图像——问题就更加明显了。
修复方法是给我们的每个图像切片添加填充——类似这样:
作者制作的动画
然后我们可以切掉多余的像素,组合成一幅没有任何伪像的图像。这里有一个代码沙箱,演示了端到端的。
最好的一点是,只要你把补丁的大小设置得足够小——比你期望得到的最小图像小——你就能得到大小一致的图像。还记得我们在预热部分提到的要求图像大小一致以获得速度提升的好处吗?这个解决方案双管齐下!
RRDN 和寻找定制层
到目前为止,我们一直在处理 RDN 模型。RRDN 模型是更强大的版本,它依赖于定制层,需要用 Javascript 重新实现。
我在 Tensorflow.js 中没有找到大量关于自定义图层的文档。有官方文档,还有蔡的这个要点,这是我能找到的大部分内容。
Python 中的两个自定义层定义为:
class PixelShuffle(tf.keras.layers.Layer):
def __init__(self, scale, *args, **kwargs):
super(PixelShuffle, self).__init__(*args, **kwargs)
self.scale = scale
def call(self, x):
return tf.nn.depth_to_space(x, block_size=self.scale, data_format='NHWC')
def get_config(self):
config = super().get_config().copy()
config.update({
'scale': self.scale,
})
return config
class MultiplyBeta(tf.keras.layers.Layer):
def __init__(self, beta, *args, **kwargs):
super(MultiplyBeta, self).__init__(*args, **kwargs)
self.beta = beta
def call(self, x, **kwargs):
return x * self.beta
def get_config(self):
config = super().get_config().copy()
config.update({
'beta': self.beta,
})
return config
在 Javascript 中,这些看起来像:
class MultiplyBeta extends tf.layers.Layer {
beta: number;
constructor() {
super({});
this.beta = BETA;
}
call(inputs: Inputs) {
return tf.mul(getInput(inputs), this.beta);
}
static className = 'MultiplyBeta';
}
class PixelShuffle extends tf.layers.Layer {
scale: number;
constructor() {
super({});
this.scale = SCALE;
}
computeOutputShape(inputShape: number[]) {
return [inputShape[0], inputShape[1], inputShape[2], 3];
}
call(inputs: Inputs) {
return tf.depthToSpace(getInput(inputs), this.scale, 'NHWC');
}
static className = 'PixelShuffle';
}
您还需要显式注册每个自定义层:
tf.serialization.registerClass(MultiplyBeta);
tf.serialization.registerClass(PixelShuffle);
这里需要指出一些事情:
- 确保您在层上定义了一个静态的
className
,它与层的名称完全匹配 call
是您进行大量计算的地方。computeOutputShape
我相信你只需要定义它是否不同,这个函数被调用来告诉 TFJS 你的输出张量的形状- 你可能需要把函数调用从 Python 翻译成 Javascript 例如,Python 中的
tf.nn.depth_to_space
变成了 Javascript 中的tf.depthToSpace
训练您的模型
超分辨率技术的一个挑战是它们的规模是固定的。
这意味着,一个经过训练可以将图像放大 2 倍的模型,将无法放大 3 倍或 4 倍。它只能将图像放大到 2 倍。
要改变规模,你需要从头开始训练一个模型。你可以想象,支持不同的规模可以大大增加你必须做的训练量。
此外,有一些迹象表明,针对特定数据集的进一步培训会产生与您的领域相关的特定益处。
首先,我们表明,较大的数据集导致更好的面向 PSNR 的方法的性能。我们使用一个大模型,其中 23 个残差中残差块(RRDB)放置在上采样层之前,随后是两个卷积层用于重建…一个广泛使用的训练数据集是 DIV2K,包含 800 个图像。我们还探索了其他具有更多样化场景的数据集——Flickr 2K 数据集,由 Flickr 网站上收集的 2650 张 2K 高分辨率图像组成。据观察,具有 DIV2K 和 Flickr2K 的合并数据集,即 DF2K 数据集,提高了 PSNR 性能。
图片来自 ESRGAN paper
针对特定领域的数据集进行训练可能会提高准确性。
去年,我花了一些时间和 RAISR 一起工作。该论文中的一个关键见解是,压缩低分辨率图像会产生一个更具弹性的模型,能够更好地处理伪像,而锐化高分辨率图像会产生更具美感的放大图像(代价是相对于度量标准而言性能更差)。我怀疑——虽然我不确定——类似的技术可能在这里的训练中产生类似的好处,我目前正在试验寻找答案。
升级。射流研究…
我已经将所有这些打包成一个名为 Upscaler.js 的 npm 模型。
它不知道正在使用的升级模型,这意味着在未来,我将能够改进模型,并可能引入适应各种用例(人脸、插图)的模型。我目前通过 JS CDNs 服务模型,并期待在未来增加更多的模型。
作者制作的动画
我认为有很多改进的机会,特别是在性能方面,但坦白地说,我很高兴这是可能的。
想象一下能够将此应用于视频流。想象一下,如果您能以普通视频流的形式提供文件大小为 6%的视频,会怎么样?我们还没有到那一步——我们必须将工作速度提高 10 倍才能处理实时视频——但是离已经不远了。想想真的很令人兴奋!
使用 Google Colab 在 Python 中将图像和蒙版分割成多个部分
原文:https://towardsdatascience.com/images-and-masks-splitting-into-multiple-pieces-in-python-with-google-colab-2f6b2ddcb322?source=collection_archive---------10-----------------------
图像和蒙版分割成更小部分的实际例子
数据标注者使用特殊的标注工具来标注对象。例如,计算机视觉标注工具(CVAT) 在计算机视觉领域广为人知。自然,贴标机使用高分辨率图像会更方便。当您需要标记大量对象时尤其如此。
在我参与的一个屋顶分割任务中,需要突出屋顶的三角形段、四边形段、其他段和边缘。下图显示了此类标记的一个示例(白色表示边,红色表示三角形,绿色表示四边形,蓝色表示其他多边形):
图像由 Oleksii Sheremet 使用 matplotlib 模块创建
原始图像是从谷歌地球获得的,像素为 2048x1208。数据标签员用 CVAT 以同样的分辨率对掩膜进行了标注。为了训练模型,图像和蒙版应该采用较低的分辨率(从 128x128 到 512x512 像素)。众所周知,图像分割是一种最常用于将大图像分割成较小部分的技术。因此,合理的解决方案是将图像及其对应的蒙版分割成具有相同分辨率的部分。
所有用于拆分的代码都是在 Google Colab 中实现的。让我们仔细看看。导入库:
import os
import sys
import shutil
import glob
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from PIL import Image
将 Google Drive(包含图片和蒙版)安装到 Google Colab:
from google.colab import drive
drive.mount('/content/gdrive')
%cd "gdrive/My Drive/File Folder"
一个用于创建新目录和递归删除现有目录内容的有用函数:
def dir_create(path):
if (os.path.exists(path)) and (os.listdir(path) != []):
shutil.rmtree(path)
os.makedirs(path)
if not os.path.exists(path):
os.makedirs(path)
覆盖原始图像的裁剪功能被调整到原始图像限制,并包含原始像素:
def crop(input_file, height, width):
img = Image.open(input_file)
img_width, img_height = img.size
for i in range(img_height//height):
for j in range(img_width//width):
box = (j*width, i*height, (j+1)*width, (i+1)*height)
yield img.crop(box)
将图像和蒙版分割成较小部分的功能(裁剪窗口的高度和宽度以及起始数字作为输入参数):
def split(inp_img_dir, inp_msk_dir, out_dir, height, width,
start_num):
image_dir = os.path.join(out_dir, 'images')
mask_dir = os.path.join(out_dir, 'masks')
dir_create(out_dir)
dir_create(image_dir)
dir_create(mask_dir)
img_list = [f for f in
os.listdir(inp_img_dir)
if os.path.isfile(os.path.join(inp_img_dir, f))]
file_num = 0
for infile in img_list:
infile_path = os.path.join(inp_img_dir, infile)
for k, piece in enumerate(crop(infile_path,
height, width), start_num):
img = Image.new('RGB', (height, width), 255)
img.paste(piece)
img_path = os.path.join(image_dir,
infile.split('.')[0]+ '_'
+ str(k).zfill(5) + '.png')
img.save(img_path)
infile_path = os.path.join(inp_msk_dir,
infile.split('.')[0] + '.png')
for k, piece in enumerate(crop(infile_path,
height, width), start_num):
msk = Image.new('RGB', (height, width), 255)
msk.paste(piece)
msk_path = os.path.join(mask_dir,
infile.split('.')[0] + '_'
+ str(k).zfill(5) + '.png')
msk.save(msk_path)
file_num += 1
sys.stdout.write("\rFile %s was processed." % file_num)
sys.stdout.flush()
让我们设置必要的变量:
inp_img_dir = ‘./input_dir/images’
inp_msk_dir = ‘./input_dir/masks’
out_dir = ‘./output_dir’
height = 512
width = 512
start_num = 1
让我们用原始图像和蒙版组成一个文件列表,并对它们进行拆分:
input_images_list = glob.glob(inp_img_dir + ‘/*.jpg’)
input_masks_list = glob.glob(inp_msk_dir + ‘/*.png’)
split(inp_img_dir, inp_msk_dir, out_dir, height, width, start_num)
例如,使用以下代码显示了两个原始图像和遮罩:
for i, (image_path, mask_path) in enumerate(zip(input_images_list,
input_masks_list)):
fig, [ax1, ax2] = plt.subplots(1, 2, figsize=(18, 9))
image = mpimg.imread(image_path)
mask = mpimg.imread(mask_path)
ax1.set_title(‘Image ‘ + str(i+1))
ax1.imshow(image)
ax2.imshow(mask)
ax2.set_title(‘Mask ‘ + str(i+1))
图像由 Oleksii Sheremet 使用 matplotlib 模块创建
使用以下功能,您可以显示分割图像的所有部分(分为 8 个部分):
def image_part_plotter(images_list, offset):
fig = plt.figure(figsize=(12, 6))
columns = 4
rows = 2
# ax enables access to manipulate each of subplots
ax = []
for i in range(columns*rows):
# create subplot and append to ax
img = mpimg.imread(images_list[i+offset])
ax.append(fig.add_subplot(rows, columns, i+1))
ax[-1].set_title(“image part number “ + str(i+1))
plt.imshow(img)
plt.show() # Render the plot
让我们看看我们得到了什么作为图像和面具分裂的结果。对于第一幅图像:
image_part_plotter(output_images_list, 0)
image_part_plotter(output_masks_list, 0)
图像由 Oleksii Sheremet 使用 matplotlib 模块创建
image_part_plotter(output_images_list, 8)
image_part_plotter(output_masks_list, 8)
图像由 Oleksii Sheremet 使用 matplotlib 模块创建
结论
使用所提出的方法分割的图像和掩模以相同的文件名保存在不同的目录中,即,如果文件’。/output _ dir/images/1 _ 00001 . png '在有图像的文件夹中,然后是文件。/output _ dir/masks/1 _ 00001 . png '会在带有掩码的目录中对应它。分割图像和遮罩后,您可以对其每个部分应用增强(例如,更改亮度、对比度、旋转或翻转)。为此,只需添加增强功能。
参考文献
枕头(PIL 叉)
计算机视觉标注工具(CVAT)
本笔记本提供从外部来源加载和保存数据的方法
想象一个结合了深度学习能力和统计可解释性的单一模型
原文:https://towardsdatascience.com/imagine-a-single-model-combining-the-power-of-deep-learning-and-the-interpretability-of-statistics-d4f0c964b0d1?source=collection_archive---------40-----------------------
在 Unsplash 上由duanghorn Wiriya拍摄的照片
回归或分类问题通常用两种模型中的一种来分析:简单的统计模型或机器学习。尽管相对简单,但前者具有明显的可解释性优势(如所用变量的重要性)。相比之下,后者往往更强大,但由于其不透明性,通常被称为“黑盒模型”。
任何给定模型的性能都与其模块化程度直接相关。模块化程度越高,通常意味着整体精度越高(暂且忽略过度拟合)。然而,模块性越高也意味着我们越不知道一个给定的变量是如何解释目标的。换句话说,性能和可解释性似乎与它们的本质是对立的。但是如果事实并非总是如此呢?
在这篇文章中,我将首先从头构建一个强大的深度学习模型,其次,以一种允许我们解释某些解释变量是否可以说对目标变量产生了显著(积极或消极)影响的方式来调整它。在本文的其余部分,这个模型将被称为“SDL 模型”(用于重要的深度学习)。将使用前馈信息和反向传播误差(通过梯度下降)等概念,如果你还不熟悉这些概念,解释神经网络和深度学习的文章可能会帮助你理解文章。
加载用于训练 SDL 模型的数据
该模型将使用一些公开可用的数据进行训练。选择一个分类问题,IBM 股票(公开信息)的月对数收益的符号被选为目标变量。Fama & French 五因素将被用作解释变量。这些变量是 Rm-rf(市场相对于无风险利率的收益率差)、SMB(最大和最小公司之间的业绩差异)、HML(绝对股票最高和最低公司之间的业绩差异)、RMW(最赚钱和最不赚钱公司之间的业绩差异)和 CMA(保守投资公司和激进投资公司之间的业绩差异)。这些不同的数据集可以在我的 Github 上找到。首先导入包 Pandas 和 Numpy,然后运行以下代码以正确的格式加载所有数据。
用于加载数据的 Python 代码
理解 SDL 模型的大致结构
现在让我们仔细看看 SDL 模型的逻辑结构。一个好的起点是将其视为各种人工神经网络的组合,或者视为某些权重被约束为零的单个人工神经网络。为了说明一些可能的自相关性,我们对上述每个变量使用 t 时刻的回归值及其四个第一滞后值。然后,具有一个隐藏层的第一个人工神经网络可以应用于与同一变量相关的每组五个输入(在下图中用蓝色、橙色和黄色表示)。优化后(见下文),每个系统都给出一维输出。然后,五个获得的输出的系列可以用作没有隐藏层(即简单的逻辑回归)的最后一个人工神经网络(用绿色表示)的输入层,并且其 y 标签等于 1 或 0,这取决于 IBM 股票回报的符号。
逻辑结构的示意图(I =中小企业、HML、RMW 和 j=1,2,3)
然而,仅仅通过组合这些不同的子模型来构建最终模型是不可能的。开始时,与五个神经元的最佳值相关的信息,即前五个 ANN 的各自输出,是未知的。因此,不可能调整权重来最大程度地降低成本函数,因为该函数本身是使用输出层的实际值和这些未知最优值之间的差来计算的。为了克服这个问题,我们将建立 SDL 模型作为一个单一的监督模型,具有五乘五个特征(Fama 和 French 五个因子的滞后值)和一个输出(IBM 股票回报的符号)。与此同时,必须对模型的大多数权重添加一些约束,以符合上述逻辑。在下图中,虚线表示约束为零的权重。
模型权重的图示-显示了约束和非约束
用 Python 实现 SDL 模型
步骤 1:初始化
现在让我们转到 Python 实现。在初始化下面介绍的 NeuralNetwork 类之前,已经执行了一些必要的步骤(训练测试分割和一些数据预处理,以确保数据格式是正确的)。也可以在我的 Github 上查看这段代码。我们用四个不同的特征初始化一个新的模型。x 和 y 分别表示输入训练矩阵和目标训练向量。Lamb1 是正则化参数,类似于线性回归中的 Ridge,learningr 是模型的学习速率(反向传播误差时校正权重的力度)。然后用-15 和 15 之间的一些随机数初始化所有非约束权重。这个宽范围用于引入显著的可变性。这里值得一提的最后一点是,输出向量(用零值初始化)旨在包含在整个模型中“前馈”输入后获得的结果。误差向量将被构建为该向量和 y 向量之间的差。
用于模型类初始化的 Python 代码
第二步:前馈
推理的下一步包括通过各层前馈输入。首先,输入矩阵(大小为 n * 25-n 是数据集的长度)乘以第一个权重矩阵(大小为 25*10)。每个第一隐藏层的神经元值最终等于 sigmoid 函数的输出,以矩阵乘法的相关边际结果作为变量。使用相同的方法来处理下面的层。一切都用数学方法总结如下。
前馈过程背后的数学。
在 Python 中,我们的 NeuralNetwork 类中的前馈方法可以通过以下方式实现。
前馈的 Python 代码
步骤 3:错误反向传播
一旦获得了输出层,就可以计算成本函数。这用于衡量预测输出与实际目标的差距。推理的下一步在于计算该成本函数相对于不同权重的导数(从权重 3 开始,然后是权重 2,最后是权重 1)。这旨在理解成本函数的哪个部分与哪个权重相关联。然后有可能以最小化成本函数的方式更新权重(根据所选择的学习速率,只有一部分导数被减去到当前权重)。下面总结了最初的数学步骤——计算基于链式法则原理和符号”。*”用于表示逐元素的矩阵乘法。注意,从第二个隐藏层开始,我们再次从误差向量的边缘元素开始推理。
误差反向传播第一步背后的数学
最相关的代码行如下所示。请再次注意,许多不太重要的行在这里没有显示,但是可以在 my Github 上找到。
反向传播的 Python 代码-仅最相关
步骤 4:优化和选择λ和学习率参数的最佳值
推理的最后一步包括重复步骤 2 和步骤 3(即,具有不断发展的权重和反向传播误差的前馈信息)。当成本函数不再降低时,该过程最终停止。我们使用简化的交叉验证来选择 lambda 和学习率参数的最佳值(即,针对测试的所有 lambda 学习率对,计算给定测试集的分类准确度,然后保留模型表现最佳的对)。在前面介绍的金融数据上,使用等于(0.15,0.3)的元组(lambda,学习率)可以获得测试集上的最佳分类精度(大约 70% —可以添加一些 IBM 特质变量来增加这个数字)。
“交叉验证”选择最佳参数值
步骤 5:变量的重要性
现在证明,SDL 模式运行良好。在大多数情况下,它能够根据市场相关变量的当前和过去的值,正确地对 IBM 收益的符号进行分类。工作完成了一半!
下面的方法将解释所选择的变量如何帮助解释 IBM 收益为正的概率。你可能已经注意到,我们的 SDL 本质上是两种模式的结合。第一种是五个神经网络与一个隐藏层的组合。优化 SDL 模型的方式确保了这些子模型中的每一个的最终神经元处的值是相同外生变量的五个时间输入的最优变换的结果(显然可能不是每个单个数据行的情况,而它在考虑所有数据时检查出来)。然后,这些经过优化转换的变量被用作第二个模型组件(最后一个没有隐藏层的神经网络,基本上是逻辑回归)的输入。从第二个模型中可以得出一定的意义。—对于每个外部变量,通过将相关子模型的最后一个神经元值(向量)乘以应用于该最后一个神经元的权重(标量)获得的向量平均值将用于该目的。这给出了插入 sigmoid 函数的平均绝对数字的概念,以最终估计 IBM 股票收益为正的概率。这一估计应该是准确的,而不仅仅是 SDL 模型幸运收敛的结果。因此导出了该估计量的非参数分布。该分布建立在运行相同代码 100 次所获得的一组值的基础上。如果这个分布的 2.5%和 97.5%的分位数要么都是正的,要么都是负的,那么就可以断定这个外生变量是显著的(α=5%),分别是正的或负的!
如下图所示,变量(Mkt-rf)与 IBM 收益的符号正相关。换句话说,市场价差越大,IBM 的回报率越有可能为正,这似乎是合乎逻辑的。类似地,CMA 变量似乎对 IBM 收益为正数的概率产生了负面影响。换句话说,保守投资公司和激进投资公司之间的业绩差异越大,IBM 取得正回报的可能性就越低。由于长的右尾,这种关系是显著的,但仅在任何置信水平 1-α-α> 1%时。
这一部分的代码可以在下面找到。
Python 代码推导出一些变量的意义
模型的结论和已知限制
总之,在运行 SDL 模型的 Python 实现时获得的结果表明,性能和可解释性在一定程度上是可以结合的。绩效与适当的 IBM 股票回报分类相关联,可解释性与所使用的外生变量的正或负显著性相关联。
然而,这种方法并不完美。例如,同一个模型必须运行多次才能获得最终估计值的分布,这一事实耗费时间和 CPU。从方法学的角度来看,选择最佳参数值的交叉验证只在一个样本上进行。最后,可以认为,在输入可以取正值和负值的情况下,上一步中得出的重要性可能不再准确,但这可以通过对输入值进行适当的初始标准化来避免。
这只是我对这种创新的意义重大的深度学习模型研究的起点。我期待着关于如何使这项研究更上一层楼的反馈和想法,我很乐意讨论你可能有的任何想法,关于本文中解释的模型或其他可以调和性能和可解释性的逻辑结构。
想象一下,你的“数据”文件夹里没有…混乱!
原文:https://towardsdatascience.com/imagine-theres-no-mess-in-your-data-folder-859135bd1262?source=collection_archive---------61-----------------------
如果你尝试,它是容易的。想办法改善你与数据集的互动。
有时很难在你自己的数据中导航。艾萨克·奎萨达在 Unsplash 上的照片
首先我将介绍狐猴先生,他将伴随我们的思考过程。
狐猴先生已经准备好冒险了!杜安·斯美塔纳在 Unsplash 拍摄的照片
我们走吧!
假设你和你的团队成员一直在做一个名字很酷的项目“客户流失探测器 X”。剩下的就是用一个合适的数据集来拟合我们同样酷的模型,然后部署它。
你看着“数据”目录,它立刻让你充满困惑。
你的项目某处:
data/
├──user _ activity _ April _ 2020 . CSV
├──user _ activity _ may _ 2020 . CSV
├──user _ activity _ April _ 2020 _ cleaned _ df _ v1 . CSV
├──user _ activity _ may _ 2020 _ broken . CSV
├──user _ activity _ may _ 2020 _ v2 . CSV
├──user _ activity _ may _ 2020看着眼熟?我知道那种感觉,兄弟。
想象一下这样一个美好的世界,在那里再也不会出现如此混乱的局面。即使你只对四月份的数据集感兴趣,在过滤掉其他数据后,你会得到:
data/
├──user _ activity _ April _ 2020 . CSV
├──user _ activity _ April _ 2020 _ cleaned _ df _ v1 . CSV
├──user _ activity _ April _ 2020 _ cleaned _ df _ the _ best . CSV
├──user _ activity _ April _ 2020 _ cleaned _ df . CSV
└──user _ activity _ April _ 2020 _ cleaned _ df _ improved . CSV
现在清楚该选哪一个了吗?嘿,狐猴先生,你说呢?
别担心,狐猴先生,我们会解决的!Amy Reed 在 Unsplash 上拍摄的照片
混乱的根源。
嗯,通常我们在做令人兴奋的事情时总是很匆忙。有一天,你向自己承诺,记住哪个数据集是哪个数据集是很容易的。尽管如此,时间飞逝,你知道!截止日期已经悄悄逼近你了。
绝对必要的是,必须使用正确的生产数据。垃圾进——垃圾出,记得吗?你现在做什么?
那么…哪个数据集是正确的呢?
首先,你检查所有的笔记本,比较指标或其他计算,你花一些时间,然后再多一点..咻!你几乎 100%确定那个user _ activity _ April _ 2020 _ cleaned _ df _ improved . CSV数据集就是你要找的那个。
没那么难。哦,等一下…
您刚刚想起需要使用最新的可用数据重新生成数据集。唯一的障碍是找到你使用的代码。谢天谢地,你一直在使用 git 并且只花了大约一个小时就找到了根并测试了一切。是的,我是在讽刺。最坏的情况——您在 git 历史中没有任何相关内容。
有哪些不同的做法?
狐猴先生也在寻找答案。布兰登·泰特在 Unsplash 上拍摄的照片
我已经介绍了两个问题:
- 很难理解不同的变换数据集实际上代表了什么
- 毕竟很难跟踪这些数据集是如何生成的
作为奖励:如何使从任何文件或笔记本中读取数据集变得容易?
有一种简单的方法可以解决第一个问题,那就是提供一个详细的文件名。
第二个要棘手得多。显然,负责转换的代码总是分离的,数据本身无法告诉我们在哪里可以找到它。
我们将在本文的剩余部分尝试找到所有这些问题的解决方案。
做一个数据集的小梦。
你可能会说我是一个梦想家,但让我们想出摆脱这种状态的选择:
呀,看起来真恶心…太不可移植了,太容易出错了。但是:我们可以做得更好!
我呼吁大家集思广益!现在重要的是接口,而不是实现。
我不想直接处理文件路径。
我们通过隐藏所有获取数据集的逻辑来指定我们正在处理的项目和文件名。感觉好多了,虽然我仍然需要知道确切的文件名。不太方便。
去掉文件名。
文件名暗示数据集与user _ activity _ April _ 2020 . CSV数据集相关。的确,user _ activity _ April _ 2020 _ cleaned _ df _ improved . CSV只是它的一个改造版本。在签名中把它们分开是个好主意。
我不想关心文件格式!
很公平。
这么一件小事,却造成了如此大的差别!现在,我可以左右阅读数据集,一切都会正常工作。
普通函数不是一个可伸缩的解决方案。我需要一张“脸”来代表它!我们需要一些神奇的东西…
最后,我们来到了这里:
在我的想象中,勇敢的魔术师来拯救我们的时间和理智了!看起来像魔术,真的。在项目中,我不想处理存储数据集的绝对或相对路径,我不关心使用哪种格式保存它。我只是需要它留在记忆里。我现在就需要!从存储库中的任何位置。当在同一个存储库中与许多团队成员一起工作时,这是特别需要的。注意转换 id 参数的值。是“model _ ready”,比“cleaned _ df _ improved”更直观。
不可避免的时刻已经到来。是时候回到现实,考虑一个可能的实现了。
它怎么知道从哪里获取数据?
如果我是 MagicDfReader ,我更希望有一些描述数据和相关转换的配置文件。配置是伟大的,他们可以记住任何事情,让我们有时间做更重要的事情。
它能包含什么有用的信息呢?稍加思考后,我可以勾勒出以下几点:
df_id 这里只是提醒一下配置属于哪个数据集。然后我们可以在元数据部分放置任何东西。知道谁创建了配置以及何时收集了数据是非常有价值的。 initial_df_format 信息将被 MagicDfReader 用来知道如何读取它。
我们谈了很多转变,不是吗?它们也必须在配置中找到。我一般能想到两种类型的转换:
- 内存中的——每当我们想要访问数据时,它们就会运行
- 永久 —它们运行一次,对磁盘进行以下序列化
例如,我们可能希望在每次读取数据集时将日期列转换为日期时间类型,但是也有一些列我们希望永久删除。它在配置中会是什么样子?
你觉得怎么样?看起来不言自明。
我们可以通过添加注释来澄清所有字段,使其更加详细:
如果在某个时间点,您想知道数据集是如何生成的——只需查看相关的配置文件。你想让它重生?没问题,只需删除旧文件并让 MagicDfReader 为您获取即可。它将遵循您在配置中提供的配方。如果你的同事负责一些转变,你甚至不需要知道它是如何完成的。 MagicDfReader 送来,宝贝。然而,仍有差距需要填补。比如具体有哪些动作? to_date 和 drop_cols,在哪里实现?
是的,我承认…是时候进行无耻而谦逊的自我宣传了!
我就像这里的狐猴先生一样谦逊。照片由乌列尔·索伯兰斯在 Unsplash 上拍摄
一旦我的想象力最终引导我找到了键盘,我就做了一个几乎按照我描述的方式工作的库。我准备了一个相当详细的 Jupyter 笔记本,里面有它的内部细节,所以我不想在这里用很多代码打扰你。
这个库叫做 df-and-order 。就像法律&命令一样。你知道,我喜欢各种形式的有序,我的“数据”文件夹中的有序是必须的,因为我有这样一个库。
让我向您展示这个库的主要组件以及真正的配置是什么样子的。
DfReader
真正的 DfReader 也很不起眼,名字里没有任何与魔法相关的东西。
创建 DfReader 的实例需要两个参数。如你所见,它对你的项目一无所知,它期望得到一个【数据】的文件夹路径。但是不要担心,没有什么比创建自己的 DfReader 子类更容易的了,它可以动态地生成 dir_path 。这么几行代码和你得到的 MagicDfReader 我们前面讲过的! format_to_cache_map 参数引出另一个类。
DfCache
应该被子类化,包含一个如何读取和保存特定格式的数据帧的逻辑。只要看一看示例子类,一切就都清楚了!
是的,它最终成为了熊猫的内置方法的简单包装。下面是一个格式 _ 到 _ 缓存 _ 映射参数值的例子:
现在 DfReader 知道如何处理这些格式。
lib 的真实配置如何?
看起来几乎和以前一样! module_path 是对具有负责转换的代码的文件的引用。您编写一些代码,然后将所有需要的参数暴露给配置文件,一切都变得清晰和可重复。查看上述深度笔记本了解详情。
lib 是用熊猫写的。DataFrame 记住了,但是如果需要的话,它可以很容易地成为与框架无关的工具。也可以对其进行修改,以处理非本地文件。 DfCache 子类可以访问一些外部资源等。
lib 正处于发展的早期阶段,所以旅程才刚刚开始。
重要提示:
df-and-order是关于组织你的数据文件并使用基本转换简化对它们的访问,而不是一个真正的数据管道框架。
良好做法:
- 您有一个庞大的原始数据框架,并且希望保留它的预处理版本,以便能够快速适应模型。您使用 df-and-order 来描述所有需要的转换。
- 您希望在后台将所有日期列从 str 转换为 datetime 类型。
- 您希望对类型进行优化以满足数据需求(比如对小范围数据使用 8 位整数而不是 64 位整数)。类型优化可以作为单独的转换来实现。
- 您希望为同一个数据集计算不同的目标。例如,您想从二元分类切换到多分类。
不良做法:
- 你使用 df 和 order 进行缩放、虚拟编码等操作。没人能阻止你这么做,但是这样的操作有更好的选择。
- 您在转换中编写一些复杂的逻辑,如发出请求、获取和推送数据等。
- 您在转换中安排了一些外部依赖项。所有的参数都应该放在配置文件中,以保持可再现性。
结论
该冷静了,狐猴先生!拉明·尼乌文休斯在 Unsplash 上的照片
即使你不愿意尝试我试图驯服混乱的结果,也许这篇文章能帮助你正确看待事情。再现性在数据业务中非常重要,所以不要忽视这个方面,否则就太晚了。
我希望有一天你会加入回购!随时留下评论,开放的问题等!我很乐意与你交流。
注意安全,祝你好运!
想象一个没有变形金刚的世界——单头关注 RNN
原文:https://towardsdatascience.com/imagining-a-world-without-transformers-single-headed-attention-rnn-844cca2580f9?source=collection_archive---------18-----------------------
从一篇最有趣的 NLP 论文中提取关键思想,描绘一个没有伯特模型家族的世界
TL;速度三角形定位法(dead reckoning)
2019 年是变革之年——在不同领域完成了大量工作(超过 5000 次引用),并解决了扩展和应用 BERT 的 NLP 问题。2019 年 11 月发表的一篇论文带回了不久前 RNNs 统治几乎所有 NLP 任务的旧记忆。这篇博文总结了《单头注意力 RNN:停止用你的头脑思考》一文的主要观点,作者是 T2。作者分享了一些非常有趣的想法——这篇论文以讽刺的方式写成,通篇引用了许多微妙的内容,读起来很有趣。另一方面,对于一些人来说,持续地跟随和吸收变得有点困难(我读了几遍以完全理解)。我将尽力总结论文传达的要点。我还会试着为那些可能不太了解必要背景的读者设置一些背景,以使这篇文章对所有人都有用。以下是我们将深入探讨的要点:
- 背景:赫特奖和理解为什么语言建模在 NLP 领域仍然是一个有趣的任务。
- 模型架构:仔细观察具有基于指针的注意机制和某个“boom”前馈层的 SHA-RNN 模型架构,两者都伴随着层规范化。
- 标记化攻击&教师强制:变化的标记化方案如何影响模型训练和评估行为。
- 资源节约:构建可以在单个 GPU 机器上实际训练的模型,并且不严重依赖大量的超参数调优。
- 想象一个没有变形金刚的世界:单向研究追求的后果,以及为什么采取反向立场可能是推动研究和工程领域整体发展的关键。
背景
语言建模是在给定之前出现过的单词的情况下,预测序列中的下一个单词的任务。包括机器翻译、语音识别等许多自然语言处理任务的核心都是语言模型。作者有趣地引用了通用近似定理,该定理指出,一个神经网络,当配备了足够的参数时,能够处理手头任何任务的所有更精细的复杂性。从理论上讲,语言模型仅仅是人类语言书写方式的反映,本身没有任何重要的原始智能。
赫特奖鼓励压缩自然语言文本的任务,以尽可能最有效的方式学习和再现文本序列,具体来说,就是来自维基百科的 100 MB 文本文件(enwik8)可以压缩多少。在本文中,作者演示了一个简单的基于 LSTM 的模型(经过一些修改),具有单个注意力头,与许多高级的基于 transformer 的模型相比,尽管内存和资源消耗较低,但性能非常接近。
模型架构
作者将沙-RNN 模型的架构描述为梅里蒂等人(2018a)介绍的 AWD-LSTM 模型的升级版。值得一提的是,快速回顾一下 AWD-LSTM 模型,以便更好地理解沙-RNN 模型可能是个好主意。ASGD 权重下降 LSTM 模型对 lstm 应用了几种正则化技术,使其成为强大的语言建模标准,但这里有两个最突出的:
- drop connect:drop connect 是一种正则化技术,由 Yann LeCunn 在 NYU 的团队于 2013 年首次推出,基于辍学正则化策略的思想。在 DropConnect 设置中,随机选择的权重子集为零,这与 dropout 相反,dropout 在给定的训练迭代中随机关闭一些隐藏单元激活。请看下图,它使这一解释更加直观:
丢弃和丢弃连接正则化技术的比较
- NT-ASGD :顾名思义,非单调触发平均随机梯度下降在两个意义上是传统 SGD 的变体。首先,它通过将来自先前迭代的权重与反向传递期间返回的当前训练步长权重进行平均来考虑这些权重。第二,只有当模型的主要评估度量在一定的训练迭代周期内没有改善时,才执行这种平均。
在此背景下,我们可以看看沙-RNN 模型架构的一些主要组件:
如图所示, 每个 SHA-RNN 层只包含一个注意力头,通过消除更新和维护多个矩阵 的需要,帮助保持模型的内存消耗最小。另一方面,多头注意力需要融合每个头的计算,以产生变压器层的最终输出。SHA-RNN 还主要利用文本中的顺序性,而不是变压器,变压器仅通过时间和位置编码信号微妙地跟踪序列的概念,这可能会引入更多的参数供模型跟踪。
SHA-RNN 图层在多个点加载了图层归一化步骤,以在每个要素维度上对要素进行归一化,这在之前已被证明可减少训练时间并提高稳定性。有一个被称为“boom”层的前馈层,它基本上使用 GeLU(高斯误差线性单位)激活来创建最终输出向量。尽管 AWD-LSTM 使用了 NT-ASGD 优化器,但作者报告的结果与之前使用 LAMB 优化器(用于批量训练的分层自适应矩优化器)的实验一致,该优化器提高了较大批量的收敛性。在这种情况下,深入 LAMB 优化器的细节可能会很棘手,但我喜欢这篇博文,如果你有兴趣了解更多,这篇博文很好地解释了它。
标记化攻击和教师强迫
作者指出,不同的标记化方案在我们如何训练、评估和比较不同的语言模型中起着重要作用。特别是,比较单词和子单词级别模型之间的困惑分数可能不是很简单。单词块标记化器通常将单词分割成更小的复合子词,这样可以得到更紧凑的词汇表和更好的模型参数利用率——这很好。此外,在训练过程中,目标令牌作为下一个输入(教师强制)被传递到该层,使得该模型在单词的初始部分严重影响给定单词的后续部分的情况下获得轻微优势。在实践中说明这一点的一个很好的例子是,当模型必须预测“邓布利多”时,从单词 pieces【D】【umble】【dore】中的第一个标记“D”可以获得大量信息,而单词标记化模型只能预测一次。如果你想知道这些单词块标记化算法是如何工作的,我的上一篇博客文章是关于将字节对编码重新用于子单词标记化。
资源节约
在过去的几年里,我们在深度学习领域取得了巨大的突破,这主要归功于大规模计算资源(GPU/TPU 集群)的可用性以及我们高效利用这些资源的能力(分布式机器学习)。虽然大多数大型组织都有无限的计算能力,但独立研究人员很难重现结果。例如,对 BERT 进行预训练需要 4 天时间,16 个 Google cloud TPUs 很容易就花费了几万美元——不确定你的,但那肯定会在我的口袋里烧一个大洞。
"无论如何,我所有最好的作品似乎都来自相对较少的资源和创造性."
值得称赞的是,作者能够在不到 24 小时的时间内,在单个 GPU 机器上训练沙-RNN 模型,并且没有进行大量的超参数调优。
想象一个没有变形金刚的世界
"为什么要像飞蛾扑向灯泡一样,只向一个方向推进呢?"
自从引入 transformer 层以来,研究界在去年取得了惊人的进步。作者在沙-RNN 论文中所做的许多工作也受到了这项工作和过去许多聪明的工作的启发。但是,作者提出了在研究人员的工具包中具有多样性和竞争性的模型架构的重要性,以便能够为手头的给定任务选择合适的模型,而不是仅仅依赖于一种类型的模型。这也需要更好的工具和效率的提高,而不仅仅是选择几个架构,而是共同推进研究流的所有方面。但是最后,很难说会发生什么,但是永远不要忘记你从哪里来,因为有一天你可能不得不回去!
参考
- 梅里蒂,S. (2019)。单头注意力 RNN:停止用你的头脑思考。 arXiv 预印本 arXiv:1911.11423 。
- 梅里蒂,s .,凯斯卡尔,N. S .,&索彻,R. (2017)。规范和优化 LSTM 语言模型。 arXiv 预印本 arXiv:1708.02182 。
- 万,李,,米,张,s,乐存,y,&弗格斯,R. (2013 年 2 月)。使用 dropconnect 正则化神经网络。在机器学习国际会议(第 1058–1066 页)。
- Vaswani,a .、Shazeer,n .、Parmar,n .、Uszkoreit,j .、Jones,l .、Gomez,A. N .、… & Polosukhin,I. (2017)。你需要的只是关注。在神经信息处理系统的进展(第 5998–6008 页)。
- Smerity/sha-rnn 代码库—https://github.com/Smerity/sha-rnn
- TWIML AI 播客—https://soundcloud.com/twiml/single-headed-attention-rnn
根据类和物体想象世界
原文:https://towardsdatascience.com/imagining-the-world-in-terms-of-classes-and-objects-fe04833a788c?source=collection_archive---------39-----------------------
面向对象编程的直觉
凯利·西克玛在 Unsplash 上的照片
世界一片混乱!为了理解它,人类倾向于对事物进行组织、分类和结构化。分类帮助我们将复杂的事物分解成更小的部分。它塑造了我们的思维,给了我们方向。让我们想想世界上的“一切”。理解这个想法非常复杂。但是我们可以将它分成如下几个样本类别:
分类给了我们一些控制来更好地管理复杂性。它使我们能够识别、区分和理解。属于每个类的事物或 【对象】 可以共享共同的属性或 【属性】 并且可以以共同的方式表现。我们可以说,属于某个特定类的对象可能以类似的方式 【功能】 。比方说,我们把“动物”作为一门课。我们可以想象大象、兔子、奶牛、猴子等属于这一类,这些动物执行一些共同的功能,如吃、睡、跑等。事实上,就类和函数而言,你可以想象世界上几乎任何概念。让我们看看下面的句子
《罗汉正在访问纽约》
识别句子中的名词——Rohan 和 New York。你可以把“罗汉”想象成一个属于“游客”类的物体。类似地,“纽约”是属于类别“旅游目的地”的对象。
现在识别句子中的 动词 。“拜访”是罗汉的一个动作。所以,你可以想象“拜访”是“罗汉”的功能。
背后的直觉是——当你在为旅游行业编写软件时,你可以用“类”(游客和旅游目的地)的形式来构造你的代码。“参观”是“游客”类的一个功能(所有游客都会参观某个地方)。“Rohan”是“Tourist”类的一个 实例 ,而“New York”是“Tourist Destination”类的一个实例。这些被称为“对象”。每个游客和每个目的地都可以表示为这些通用类的对象。另外,请注意,每个对象都有共同的属性。在我们的示例中,来自旅游类的对象可以具有像姓名、年龄、地址这样的属性,而来自旅游目的地类的对象可以具有像位置地址这样的属性。
类和对象的区别在于——类是定义或模板,而对象是定义的实例。
课堂是一个食谱。该物体是由该食谱制成的一道菜。
几乎每个现实世界的概念都可以用类、对象和函数来表达。事物是类,事物执行的动作是函数,事物的实际值是对象。
动物是一个类。猴子是一个物体。跳跃是功能。
车辆是一个类。奔驰是一个物体。加速度是函数。
Python 中的类和对象
让我们看看 Python 3 中是如何定义一个类的
# Class for animals
class Animal:
category = “pets”
def __init__(self, name, age):
self.name = name
self.age = age
def printname(self):
print(“Name of this animal is: “, self.name)
def printage(self):
print(“Age of this animal is: “, self.age)
def move(self):
print(“Running”)# Instantiate Animal objects
dog = Animal(“Titu”, 5)
cat = Animal(“Sarah”, 6)# Call Instance Methods
dog.printname()
dog.printage()
dog.move()cat.printname()
cat.printage()
cat.move()
输出:
我们来解剖一下:
- 类的声明:我们使用“Class”关键字定义了一个名为“Animal”的类。
class Animal:
2。对象:类将通过它的“对象”来实现。这些被称为这个类的“实例”。
dog = Animal(“Titu”, 5)
cat = Animal(“Sarah”, 6)
3。实例属性:一个类的对象有一定的属性。在我们的例子中,动物有年龄和名字。**__init__()**
方法用于通过分配默认值(或状态)来初始化一个对象的属性。这个方法至少有一个参数和“self”。
def __init__(self, name, age):
self.name = name
self.age = age
“自我”意味着属性是对象本身的一部分。变量“name”和“age”只属于这个对象,独立于程序中的任何其他对象或通用变量。
***__init__()***
方法不是专门调用的。一旦对象被声明,它就会被自动调用。
4。类别属性:可能有一些类别共有的属性,即该类别的所有对象。在我们的例子中,category 就是这样一个属性
category = “pets”
所以在这个例子中,所有的对象(猫和狗)都是“宠物”。
5。实例方法:这些函数定义对象的行为。像**__init__()**
方法一样,实例方法的第一个参数也是 Self。在我们的例子中,“printname”、“printage”和 move 是实例方法
def printname(self):
print(“Name of this animal is: “, self.name)
def printage(self):
print(“Age of this animal is: “, self.age)
def move(self):
print(“Running”)
关于 Python 数据类型的一句话
让我们用 Python 声明两个变量,看看它们的类型:
上面的输出显示‘a’属于一个名为‘int’的类。这也暗示‘a’是‘int’类的对象(实例)。同样,‘name’属于一个叫做‘str’的类。Name 实际上是这个 String 类的一个实例。
有趣的是,python 的内置数据类型实际上是类。这种数据类型的变量(如整数或字符串)实际上是该类的实例。实际上,Python 中的一切都是某个类的对象。 (是的……连函数)。您可以使用 help()函数来检查这些类的定义
这个关于 Python 内置数据类型的有趣事实给了我们一个关于类和对象真正用途的线索。
类用于创建新的用户定义的数据类型。
使用自己的数据类型增强了软件程序的可重用性和可维护性。
我们的世界由实体组成,这些实体相互作用。类和对象对现实世界的实体以及它们之间的关系进行建模。因此,围绕他们构建你的软件,增加了人性化的一面。这使得程序更容易阅读和理解。
乔布斯 曾被要求描述什么是面向对象软件?他的回答是:
物如其人。他们是有生命的,会呼吸的东西,体内有如何做事情的知识,体内有记忆,所以他们能记住事情。你不是在很低的层次上与他们互动,而是在很高的抽象层次上与他们互动,就像我们现在做的一样。举个例子:如果我是你的洗衣对象,你可以把你的脏衣服给我,然后给我发一条信息,说“你能帮我把衣服洗了吗?”我碰巧知道旧金山最好的洗衣店在哪里。我会说英语,口袋里也有钱。所以我出去叫了一辆出租车,告诉司机带我去旧金山的这个地方。我去洗你的衣服,跳上出租车,回到这里。我把你的干净衣服给你,并说:“这是你的干净衣服。”你不知道我是怎么做到的。你对洗衣店一无所知。也许你会说法语,但你甚至不会叫出租车。你不能为一个付钱,你口袋里没有美元。然而,我知道如何做到这一切。你不需要知道这些。所有的复杂性都隐藏在我的内心,我们能够在一个非常高的抽象层次上进行互动。物体就是这样。它们封装了复杂性,而这种复杂性的接口是高层次的。
分类中的不平衡数据:一般解决方案和案例研究
原文:https://towardsdatascience.com/imbalanced-data-in-classification-general-solution-case-study-169f2e18b017?source=collection_archive---------20-----------------------
在错误分类一个二进制类比另一个二进制类代价更高的情况下,找到最佳分类器和重采样技术来处理不平衡数据
作者:哈德尔·哈马德& 闵周 ,博士
图片启发:https://www . vector stock . com/royalty-free-vector/balance-scale-isolated-icon-design-vector-9656446
在处理分类问题时,如何处理一个数据集,该数据集的一个二进制类中的数据点比另一个二进制类中的数据点多得多?这样的数据集被称为不平衡。简单地在不平衡的数据上实现一个分类模型并期望最好的结果是明智的吗?有许多重采样技术可以“平衡”不平衡的数据集和大量的分类模型。我们如何为我们的问题选择一个最优的呢?
本文旨在为数据科学领域的非技术从业者[https://arxiv.org/abs/2002.04592]总结以下学术研究“不平衡分类:面向目标的综述”。这篇文章对于至少具备机器学习基础知识的读者来说是容易理解的。
一、简介
不平衡的数据集在所有行业中都非常普遍。假设您为一家银行工作,并且您有一份带有欺诈标签的信用卡交易数据。您发现欺诈类别非常不平衡(0.17%的数据是欺诈=0,99.83%的数据是非欺诈=1)。在这种不平衡的数据上天真地实现分类模型可能会导致非常低的预测准确性。在分类问题中,几乎不可能完美地同时预测两个类别(欺诈和非欺诈)。这是因为分类模型带有两种类型的错误,类型 I 和类型 II,并且本质上在这两者之间存在权衡。第一类错误表示将欺诈交易误归类为非欺诈交易的可能性。第二类错误的意思正好相反:将非欺诈交易误归类为欺诈的概率。通常情况下,第一类错误比第二类错误的代价更高。我们的案例就是一个例子,因为你可能会因为错误地将客户的交易标记为欺诈而激怒他们,从而有可能失去他们。然而,一般来说,根据问题的性质,您可以选择不同地对待类型 I 和类型 II 错误的权重,这相应地决定了操作的分类范例。
我们将简要概述分类范例和重采样技术。然后,我们将使用上面的信用卡欺诈数据的例子,这是一个真实的案例,将每种重采样技术与不同分类范例下的分类模型配对。目标是为每个范例找到重采样技术与分类模型的最佳组合。在本文的最后,我们提供了应用分类范例和重采样技术编写 R 代码的例子。
二。三种分类范式
每种分类模式对第一类和第二类错误的权重处理不同:
1.经典分类(CC)范式将总体分类误差降至最低,这意味着 I 类和 II 类误差的权重相等。在第一类错误可能比第二类错误更严重的情况下,或者相反的情况下,它不能很好地服务。因此,我们接下来将介绍另外两个解决不对称错误重要性问题的范例。
2.成本敏感(CS)学习范式为第一类和第二类错误分配不同的成本,然后最小化它们的总量。它有多种方法,但是我们将把后处理方法应用到我们的案例研究中。CS 学习的缺点是为 I 型和 II 型错误分配成本值,因为它们通常是未知的。另一个缺点是,即使我们将经验 I 型误差调整到等于预先指定的水平(α),真实的总体水平 I 型误差仍然有超过α的一些可能性。然而,尼曼-皮尔逊处理这个问题。
- Neyman-Pearson 范式是一种新兴的统计框架,通过旨在将第二类误差最小化,同时将第一类误差控制在理想水平之下,来控制不对称误差。要了解更多,请阅读以下论文:尼曼-皮尔逊分类算法和 NP 接收机操作特性
表 1:每个分类范式的学习目标
三。重采样技术
我们利用重采样技术来减轻不平衡的类大小对分类模型预测能力的影响。重采样技术通过平衡少数类和多数类中的数据点数量来创建新的训练数据集。我们将讨论三种不同的技术:欠采样、过采样和混合技术。
1.欠采样方法使用两种通用方法消除多数类中数据点的子集。第一个是随机欠采样,随机丢弃多数类的数据点,以平衡其大小与少数类的大小。虽然这是一种简单省时的方法,但它可能会产生数据偏差,因为它会抛出一些信息,使数据样本不太能代表真实的总体。第二种技术,基于聚类的欠采样,可以减弱这种偏差问题。它对多数类应用聚类算法,以便获得与少数类的大小相平衡的几个聚类。如果多数类的规模很大,这种技术可能会很慢。欠采样的一个普遍缺点是,如果大多数类的大部分数据被丢弃,它可能会导致关键信息丢失。
2.过采样增加少数类中数据点的数量,以平衡多数类的大小。随机过采样通过随机复制少数类中的数据点来实现这一点,直到它们与多数类的大小成同等比例。合成少数过采样技术 (SMOTE)利用 k 最近邻为少数类生成新的数据点,这些数据点与多数类的大小成正比。过采样方法可能导致过拟合,并且相对于欠采样,它通常需要更长的训练时间。
3.混合技术仅仅是欠采样和过采样技术的结合。它同时应用过采样来增加少数类中数据点的数量,同时应用欠采样来减少多数类中数据点的数量。
四。真实案例分析:信用卡欺诈数据
数据集描述
- 数据集来源:卡格尔【https://www.kaggle.com/mlg-ulb/creditcardfraud】T2
- 预测值个数: 30
- 记录/交易笔数:284807 笔
- 响应变量:“Class”,在欺诈的情况下取值 1,否则取值 0。在我们的数据处理中,我们在研究中颠倒了标签,所以如果欺诈,0 类;否则 1。
- 不平衡率:0.17%(284,807,492 ÷ 284807 中的 492 笔欺诈交易)
数据处理
由于我们的计算能力有限,我们从上述大型数据集中提取了一个子样本。我们指定不平衡比(IR)为 100;即 IR=100。我们为类别 0(欺诈)随机选择 n0=300 个数据点,为类别 1 选择 n1=n0IR=30,000。这创建了我们的训练集。测试集由剩余的 0 级 m0=192,[492–300]和 1 级 m1=m0IR=19,200 组成。这种分割机制保证训练集和测试集的 IR 相等。
下面是我们使用的重采样技术
- 无重采样:使用训练集,不对原始数据进行任何修改(在图表中,我们称之为“原始数据”)
- 随机欠采样(我们称之为“欠采样”)
- 过采样(“SMOTE”):我们使用 R 包 smotefamliy,v1.3.1,Siriseriwan 2019,默认选择最近邻
- 混合:结合随机欠采样和 SMOTE
以下是我们应用的分类模型,后面是 R 包(注意:我们使用包的默认参数):
- 逻辑回归(以 R 为基数的 glm 函数)
- 支持向量机(R 包 e1071,v1.7.2,Meyer 等人 2019)
- XGBoost (R 包 XGBoost,v0.90.0.2,陈等 2019)
对于分类范例,我们指定以下参数:
- CS 学习范式:我们指定成本 C0=IR=100,C1 = 1;阈值=C0/(C0+C1)=100/101
- NP 范式:我们指定α = 0.05,违反率δ = 0.05
表示|S|为集合 S 的基数,设 O = {CC,CS,NP},T = {Original,Under,SMOTE,Hybrid},C = {LR,SVM,XGB}。因此,它需要|O=3| × |T=4| × |C=3| = 36 个元素,如下面的图 1 所示,我们将在后面进行研究。
图 1:在我们的案例研究中检查了上述所有要素
五、评价指标
在评估我们的结果之前,我们需要首先通过回顾几个用于评估模型性能的评估指标来奠定基础。请注意,我们将少数类(欺诈)标记为 0,多数类(无欺诈)标记为 1。我们称 0 类(负面类,欺诈)和 1 类(正面类,欺诈)。
分类模型通常总结在一个叫做混淆矩阵的表格中,如下图所示。它被称为“混淆”,因为分类模型在从负面类别中识别正面类别时从来都不是完美的(也就是说,它们混淆了它们)。右边的对角线代表被错误地预测的标签,而左边的对角线代表被正确地预测的标签。
表 2:混淆矩阵 — 来源于 CS 范式下对原始数据的逻辑回归
混淆矩阵包括以下内容:
- 真阴性(TN): 被正确预测为阴性(欺诈)的数据点数
- 假阴性(FN): 被错误预测为阴性的数据点数
- 真阳性(TP): 正确预测为阳性(无欺诈)的数据点数
- 假阳性(FP): 被错误地预测为阳性的数据点数
—第一类错误:将欺诈交易误归类为非欺诈交易的比例:
= FP/(TN+FP)
= 22/(170+22)= 0.1146
—第二类错误:将非欺诈交易误归类为欺诈交易的比例:
= FN/(FN+TP)
= 477(477+18723)= 0.0248
下面我们计算每一类在计算经验风险和分类成本时使用该信息的比例。接下来:
—总体分类误差(或“经验风险”):两个类别中错误分类数据点的百分比。表示如下:
= prop 0 * Type I+prop 1 * Type II
=(FP+FN)/(TP+FP+TN+FN)
= 0.0257
— 在将成本分配给第一类和第二类错误(C0 分别=100 和 C1=1)的情况下,经验分类成本*将计算如下:
= C0 * prop 0 * type 1+C1 * type II
= 100 * 0.0099 * 0.1146+1 * 0.9901 * 0.0248
= 0.1380*
— F-score 是处理不平衡分类问题时的常用指标。它来源于测量精度和召回率;他们的计算见下表 3。如果相应的精度或召回率未定义或等于 0,则 F-score 被设置为 0。
o Precision: 测量您的模型根据其预测的标识(即正确和不正确的预测)对每个类做出正确预测的次数。
o 回忆: 测量你的模型在每一类的真识别中做出正确预测的次数。
表 3:混淆矩阵–与表 2 相同,只是它显示了如何计算精确度、召回率、&风险**
使用上述精度和召回率的计算,我们计算每个类的 F 分数:
接收器工作特性(ROC)曲线是分类模型在区分阈值变化时识别阳性和阴性类别的性能的图示(见下图 2)。此外,它反映了第一类和第二类误差之间的权衡。随着我们成功地在数据中识别出更多的真阴性(欺诈),代价是将非欺诈记录误识别为欺诈(犯假阳性)。ROC 曲线下面积(AUC)是一个单一数字的汇总,它量化了模型在区分阳性和阴性类别方面的表现。它通常介于 0 和 1 之间。该值越大,模型的性能越好。值 0.5 并不比随机猜测更好。如下图 2 所示,ROC-AUC 为 0.9734907,这表明该模型在识别欺诈交易和非欺诈交易方面做得很好。
图 2:CS 范式下对原始数据进行逻辑回归得到的 ROC-【LR+原始】-
精确召回(PR) 曲线及其 AUC(PR-AUC)是处理不平衡数据时的替代指标。在图 3 中,欺诈类的 PR-AUC 为 0.857943,非欺诈类的 PR-AUC 为 0.9995953。
****图 3: PR 曲线-“LR+原始”- 源自 CS 范式下对原始数据的逻辑回归
总之,对于第一类和第二类错误、经验风险和经验分类成本,较小的值表明分类模型的性能较好。然而,对于 F-score、ROC-AUC 和 PR-AUC,较大的值表明性能较好。
六。结果和解释
为了稳定我们的结果,该过程重复 50 次,并报告三种分类范例下所有分类器的平均性能(评估度量的平均值)。结果总结在图 4 至 7 和表 4 至 6 中。
经典分类(CC)范式
图 4:经典分类范式下的度量结果
图 4(上图)展示了在 CC 范式下将每种重采样技术与分类模型配对的结果。我们观察到,没有重采样的所有分类器的经验风险比所有重采样技术的风险更小(更好)。此外,对于没有重采样技术的所有分类器,F 分数也更好(更高)。因此,我们得出结论,如果我们的学习目标是最小化风险(在 CC 范式下操作),则对原始数据应用 XGBoost 分类模型而不执行重采样技术是最佳选择(“XG b+原始”)。
值得注意的是,最小化不平衡数据上的总体分类误差可能会导致大的 I 类误差,这在上面清楚地示出了。然而,通过利用重采样技术,我们可以更好地控制 I 型误差。
为了方便读者,我们根据几个评估指标总结了 CC 范式下重采样技术与分类模型的最佳组合:
表 4:CC 范式下的最佳组合
图 5(下图)中的 ROC-AUC 和 PR-AUC 测量了整体模型的性能,无需指定分类范例。这些指标表明 XGBoost 在所有重采样技术中效果最好。然而,根据 ROC-AUC,重采样技术不能改善逻辑回归的性能。然而,在 ROC-AUC 和 PR-AUC(非欺诈类)下,它们可以显著地有益于支持向量机。最后,以 ROC-AUC 和 PR-AUC(非欺诈类)为标准,“XGB+Hybrid”为最优组合,以 PR-AUC(欺诈类)为评估标准,“XGB+SMOTE”为最佳组合。
图 5: ROC-AUC、PR-AUC(欺诈)和 PR-AUC(非欺诈)
成本敏感(CS)学习范式
图 6:CS 学习范式下的指标结果
如果我们的目标是最小化总的错误分类成本,那么我们应该在 CS 学习范式下操作。从上面的图 6 中,我们发现,通过使用 I 型误差作为评估指标,所有模型都显著受益于重采样技术。下表通过对不同指标的评估,总结了 CS 学习范式下重采样技术与分类模型的最佳组合。
表 5:CS 学习范式下的最优组合
尼曼-皮尔逊范式
图 7:尼曼-皮尔逊范式下的指标结果
如果我们的目标是最小化第二类错误,同时将第一类错误控制在目标水平之下,那么我们应该在 NP 范式下操作。其结果如图 7 所示。我们观察到,在重采样技术与分类模型的所有组合中,I 型误差在α下得到很好的控制。最后,我们在下表中总结了 NP 范式的最佳组合。
表 6:NP 范式下的最优组合
七世。最后备注&案例研究的局限性
当处理不平衡数据时,关键是要先问自己,在你的问题中,哪种错误的代价更大:第一类还是第二类?这个问题将帮助您确定合适的分类模式。一旦您确定了合适的范例,请从这里给出的图表中选择重采样技术和分类模型的最佳组合以应用于您的数据集。您的选择应该基于您想要用来评估分类器性能的评估度量。有几个可用的评估指标;选择一个你认为代价相对较高的错误。尽管如此,请记住,在我们的案例研究中,我们仅考虑了重采样技术和三种分类模型的选择性列表。还有很多其他的组合值得探索。另外请注意,在我们的案例研究中,我们随机选择了非欺诈类的子样本来构建我们的分类器,这对于我们的问题来说可能不具有代表性。
技术教程— R 代码
应用分类范例和重采样技术: 我们应用逻辑回归仅仅作为一个例子。
分类范例
1。经典分类范式
**## Training set
data_trainxy <- data.frame(train_x,train_y)## Testing set
data_testxy <- data.frame(test_x,test_y) ## Fit the model on the training set
model_cc = glm(train_y~.,family = “binomial”,data=data_trainxy)## Generate predictions on the testing set
prep_cc = predict(model_cc,test_x)
p_cc = 1/(1+exp(-prep_cc))## probability of Y=1 given X=x under testing set## Calculate the overall classification error (risk) on the testing set; set threshold=0.5threshold <- 0.5
pred_class <- ifelse(p_cc>threshold,”1",”0")
risk <- 1-mean(pred_class==test_y)**
2。 代价敏感学习范式(后处理方法)
**## Fit the model on the training set
model_cs = glm(train_y~.,family = “binomial”,data=data_trainxy)
prep_cs = predict(model_cs,test_x)
p_cs = 1/(1+exp(-prep_cs)) ## the probability of Y=1 given X=x under the testing set## Generate predictions on the testing set
threshold_cs = IR/(IR+1) ## IR is the imbalance ratio
pred_cs = ifelse(p_cs>threshold_cs,”1",”0")## Type I error on the testing set
ind0 = which(test_y == 0)
typeI = mean(pred_cs[ind0] != test_y[ind0])## Type II error on the testing set
ind1 = which(test_y == 1)
typeII = mean(pred_cs[ind1] != test_y[ind1]) ##Empirical misclassification cost on the testing set
m0 = length(which(test_y==0)) # samples size of Class 0 in test data
m1 = length(which(test_y==1)) # sample size of Class 1 in test data
cost = IR*(m0/(m0+m1))*typeI+1*(m1/(m0+m1))*typeII**
3。 Neyman-Pearson 范式(NP 伞状算法) 值得注意的是,在构造 NP 分类器时,我们只能对训练集的部分样本应用重采样技术。是因为背后的算法(NP 伞)把训练集的 0 类分成两部分:一部分拟合模型,一部分选择阈值。NP 伞状算法的细节可以在下面的论文“Neyman-Pearson 分类算法和 NP 接收器操作特征”中找到
**## Install library “nproc” package
install.packages(“nproc”, repos = “http://cran.us.r-project.org")
library(nproc)## Fit the model on the training set
fit = npc(train_x, train_y, method = “logistic”)## Generate predictions on the testing set
pred_np = predict(fit, test_x)## Compute type I error on the testing set
ind0 = which(test_y == 0)
typeI = mean(pred_np[ind0] != test_y[ind0])## Compute type II error on the testing set
ind1 = which(test_y == 1)
typeII = mean(pred_np[ind1] != test_y[ind1])**
重采样技术
1。 随机欠采样(“欠采样”)
**id0_train = which(train_y==0) ## index of class 0 in the training set
id1_train = which(train_y==1) ## index of class 1 in the training set
n0_train = length(id0_train) ## sample size of class 0 in the training set## Randomly sample observations without replacement from class 1
indexMaj = sample(id1_train,n0_train)## Obtain new training set
newtrain_under = data.frame(rbind(train_x[id0_train,],train_x[indexMaj,]), y=c(rep(0,n0_train),rep(1,n0_train)))**
2。 【过采样(SMOTE)】
**## Install library “smote” package
install.packages(“smote”)
library(smote)## Use SMOTE function to construct a new training set
geneData = SMOTE(train_x,train_y)## Obtain the new training set
newtrain_smote = geneData$data**
3。 混合: 这里它结合了“欠采样”和 SMOTE 重采样技术,最终训练集由以下内容组成:
**## Calculate the new sample size of the new training set
n0_train = length(id0_train) ## sample size of class 0 in the training set
n1_train = length(id1_train) ## sample size of class 0 in the training set
n_h = floor(n1_train/n0_train)*n0_train## Use SMOTE function to generate new data for class 0
M = n_h/n0_train-1
geneData_h = SMOTE(train_x,train_y,dup_size = M)
newtrain_hybrid0 = geneData_h$data[which(geneData_h$data$class==”0"),]
d=dim(newtrain_hybrid0)[2]## Randomly sample observations without replacement from class 1
indexMaj_h = sample(id1_train, n_h)## Obtain the new training set with hybrid method
newtrain_hybrid =
data.frame(rbind(newtrain_hybrid0[1:d,],train_x[indexMaj_hybrid,]),
y=c(rep(0,n_h),rep(1,n_h))) #the final training set consisting of n_h class 0 and n_h class 1 observations**
最后请注意,ROC-AUC 和 PR-AUC 可以分别通过“PRROC”包中的“roc.curve”和“pr.curve”函数计算。对于其他评估指标,您可以简单地使用这里包含的公式来计算它们。
非常感谢辛彤博士教授的友好和持续支持!
当细节很重要时,不平衡的数据
原文:https://towardsdatascience.com/imbalanced-data-when-details-matter-16bd3ec7ef74?source=collection_archive---------51-----------------------
当在你的数据集中找到一个值得信赖的政治家就像大海捞针一样困难时,你可以这么做。
标准技术的插图,作者提供的图像
定义不平衡数据
当我们谈到不平衡的数据时,我们的意思是至少有个 级 被低估。例如,在考虑构建分类器的问题时,我们称之为 idealistic-Voter。我们给它的任务是找出美国公众认为值得信任的政治家。当查看数据集时,我们意识到几乎每个样本都非常不受欢迎。
在术语分类中,我们称样本很少的类为少数类,样本很多的类为多数类。
那么问题是什么呢?
让我们假设有 100 个政治家,其中只有一个被广大公众认为是值得信任的;我们就叫他亚伯拉罕吧。我们现在可以迅速建立一个理想的选民。通过简单地说“所有的政客都是骗子”,即把所有的政客都归类为不可信的,这就达到了 99%的准确率。损失会很低,我们可以做一个很好的演示,展示我们的理想主义者在最常见的指标上的得分有多高。
PRECISION = TP / (TP+FP) = 99%
ACCURACY = TP / (TP + FN) = 99 %
F1 = (2*PRECISION*RECALL)/(PRECISION+RECALL) = 99.5 %
但这不是目标!我们想要的是把好的和坏的分开,我们真的没有分开任何东西。那么我们如何发现我们有问题呢?我们对亚伯拉罕不公,我们都知道他不喜欢不公…
平衡指标
每当您处理不平衡的数据时,也要养成查看平衡指标的习惯。他们做的和你熟悉的那些一样,但是另外,他们取所有类的平均值。
BALANCED_PRECISION=(P_TRUSTWORTHY + P_UNTRUSTWORTHY)/2=50%
在这种情况下,另一个非常有效的指标是与真阳性率相比的假阳性率。在多标签的情况下,你通常会选择一些宏观平均分数。
我们意识到我们有一个问题,我们如何解决它?
上采样和下采样
让理想主义选民自己解决问题的最简单的方法可能就是给他提供更合理的选择。这意味着要么收集更多的数据,要么使用可爱的上采样技术。
这项技术的工作原理是,我们克隆我们敬爱的亚伯拉罕,这意味着我们复制了少数民族的成员。一旦我们将数据集平衡到一个足够的程度,我们会注意到我们的模型实际上必须学习一些东西来获得一个好的分数!
向下采样
与上采样相反的是下采样,也称为欠采样。如果您有足够多的数据来完成您的工作,这种技术是非常棒的(这从未发生在我身上,但我假设这个世界的大数据收集者有时可能处于这种位置)。
在这种情况下,您可以消除一些多数类的实例。你不必对半分,但足以让理想主义者有动力去学习一些东西。这同样适用于我们今天讨论的所有技术。
生成合成样本
生成合成示例,图片由作者提供
好了,现在我们开始讨论一些很酷的事情。我们凭空生成数据怎么样?我们加点亚伯拉罕和酷酷的电子眼镜怎么样?他肯定会同样值得信赖,而且还会赢得年轻人的选票!
加入一点ϵps
假设我们的数据集具有特征年龄。我们可以清楚地看到,唯一值得信赖的政治家是2020–1809 = 211岁,1809 年是亚伯拉罕的出生年份。但是那些有着同样成就但年龄分别为 211.001 岁和 210.999 岁的政治家呢?
大概一样吧!但是使用这种技术时要非常小心,因为你实际上是在伪造数据。这种技巧很容易适得其反,只有当你非常确信这是正确的选择时,才应该使用它。但是这给我们带来了一个稍微复杂一点的自动化技术。
重击
SMOTE 代表合成少数过采样技术。它通过仔细观察所有样本的属性来构造新样本。然后,它继续修改这些值,使它们保持在我们的少数类中观察到的范围内。听起来很复杂,我们来看一个简单的例子,它应该会变得像你的鼠标点击拍手一样清晰。让我们假设把 JFK 加入到数据集中,再来看看值得信赖的政治家的时代。
SMOTE 插图,作者图片
如我们所见,标示为 SMOTE SAFE 的区域是我们可以自由生成新合成样本的区域!你可以在这里找到更深入的解释。这很吸引人,但我认为最重要的是知道这种方法的存在。
发挥创造力
我们可以在自动驾驶场景中找到一些令人难以置信的这种技术的例子。虽然该领域的所有主要竞争对手都已经收集了数 Pb 的数据,但像孩子跑在汽车前面这样的情况应该在实际遇到之前进行测试。这些情况也可能很少发生,以至于你永远找不到足够的训练数据。
必须生成合成数据,以便在许多不同的光照和天气条件下测试此类和各种其他边缘情况。人们经常使用 GAN 或类似的网络将已经存在的样本转换成各种其他形式,例如在冬天或下雨时。有多种方法可以做到这一点。可以在这里看一个说明这一点的短视频(我不隶属于他们也不知道他们有多好,但是他们有一个很优秀的短视频;)
演示合成数据生成的视频
改变损失函数
但是我们还有一样东西!如果我告诉你,我们可以在算法的核心——损失函数——处理问题,而不是在数据层面。
多年来,人们提出了许多方法,但没有一种方法是放之四海而皆准的。这通常很大程度上取决于识别少数类、的重要性,以及当你将样本错误分类为多数类时会产生什么样的成本。
加权损失函数,自己定义
混合这种损失的一种方法是简单地将发生少数类的项乘以常数> 1。这实际上是鼓励模型更多地关注少数民族的实例。在 Keras 中,这是内置的,可以轻松应用
class_weights = {0: 1.,
1: 50.}
model.fit(X_train, Y_train,class_weights=class_weights)
在这个特殊的例子中,它告诉 Keras,应该将类 1 的一个实例视为类 0 的 50 个实例。在一些简单的 python 伪代码中,我们可以把它想成这样
def loss_weighted(x,y_true):y_pred=model.forward(x)
if y_true==1:
return 50*loss(y_pred,y_true)
elif y_true==0:
return 1*loss(y_pred,y_true)
类别平衡损失
随着类的数量变得越来越大,显式地定义东西就变得很乏味,就像我们上面做的那样。为此,提出了多种解决方案,其中最著名的是“基于有效样本数的类平衡损失”
他们实际上提出的是重新加权损失函数。作者这样做是为了考虑每类样本的有效数量。为了控制损失在平衡的哪一侧结束,可以另外用超参数来调整它。
虽然这是对他们过程的一个非常简单的看法,但我觉得它抓住了最重要的方面,值得你记住。如果您曾经遇到过这样的情况,您有许多类,并且这些类之间存在明显的不平衡,那么一定要尝试一下这种方法。
结论
恭喜你,你现在知道当你遇到不平衡的数据时你能做什么了。有几种方法,为您的问题选择正确的方法并不总是一件容易的事情。这将在很大程度上取决于你的业务需求,以决定你想要投入多少时间和资源来更公平地平衡事情。意识到潜在的解决方案是解决它们的第一步!
如果你喜欢这篇文章,我会很高兴在 Twitter 或 LinkedIn 上联系你。
一定要看看我的 YouTube 频道,我每周都会在那里发布新视频。
电影数据科学-使用 Python 提取和分析 IMDb 数据
原文:https://towardsdatascience.com/imdb-data-science-pull-analyze-movies-data-using-python-b59dc8511157?source=collection_archive---------14-----------------------
雅各布·欧文斯的电影来自 Unsplash
IMDb (互联网电影数据库)是最被认可的名字之一,因为其全面的在线数据库收集了电影、电影、电视剧等。截至今天(2020 年 7 月),你将通过以下数据看到 IMDb 数据库拥有大约700 万本图书。在本文中,我将使用 Jupyter 笔记本 中的 Python 来演示从哪里拉数据,如何快速解读数据,并回答一些有趣的问题,包括
kind 数据库涵盖哪类数据?
-一般情况下人们会给出什么样的评分?真的是正态分布吗?哪些电影收视率最高?
-根据我喜欢的电影,我还应该检查哪些电影?
一些主要的 IMDb 维度
加载 IMDb 数据库
IMDb 已经在 IMDb 网站上向公众及其客户提供了其数据库的基本子集供非商业使用,在那里你也可以找到相应的 IMDb 数据字典中描述的所有相关细节。在这个分析中,我主要关注 2 个数据集( title.basics 和 title.ratings ),它们分别提供了 9 个和 3 个特征,包括标题 ID、名称、类型、成丨人丨电丨影标志、类型、评级和标题获得的投票数。
basics_tsv_file =”C:\\Users\....\Downloads\\basics.tsv”
basics = pd.read_csv(basics_tsv_file, sep=’\t’,low_memory=False)ratings_tsv_file = "C:\\Users\....\Downloads\\ratings.tsv"
ratings = pd.read_csv(ratings_tsv_file, sep='\t',low_memory=False)data = pd.merge(basics, ratings, on ="tconst")
当您通过唯一的 IMDb 头衔 id(t const)提取这两个数据集时,您会注意到这两个数据集的大小相差 7 倍:
- title.basics 有700 万个头衔,但是
- 片酬只有100 万片酬
但是当你合并这两个数据集时,你会发现合并后标题的数量并没有减少。这意味着 title.ratings 中包含的所有标题实际上都是 title.basics 中标题的 子集;这是一个好消息,因为这意味着所有带有评级的图书都有基本信息。在这里,我不会进入细节,但我正在进行另一项工作,我将应用主成分分析,这是最有用的无监督机器学习技术之一,以深入了解什么样的电影往往没有评级数据。
这样,如下所示的合并数据集有100 万个标题和 11 个维度,可以应用各种分析工具:
按标题合并基本数据和评级数据集
标题的类型和流派
IMDb 瓷砖类型的饼图分布
顶级类型(电视剧、电影和短片)的帕累托 80/20 法则
对于瓷砖类型,帕累托 80/20 法则在这里绝对成立。总共有 10 种不同的标题类型,其中标题数量最多的是电视剧集(几乎占标题的一半),其次是电影和短片。这三种类型合计占总标题数的 80%以上。另一方面,总片头数最少的后 3 种类型(电视短片、电视特辑和电视迷你剧)占片头总数的比例不到 10%。
IMDb 流派的饼图分布
在过于复杂的分类中,顶级类型是喜剧、戏剧和纪录片
对于 T21 的瓷砖种类,IMDb 有一个长长的单子,上面有将近 2000 种不同的种类。正如你所看到的,类型的分布有一条长尾,其中只有前三种类型(喜剧、戏剧和纪录片)的份额超过 5%,但所有其他类型的份额都不超过 3%。
注意 长尾可能是由于在不同流派中过于复杂的分类造成的。例如,虽然“喜剧”占 9%,但有许多相似但略有不同的类型,如“喜剧,戏剧”(2%),“喜剧,短片”(2%),甚至“冒险,动画,喜剧”(1%)等,每个都占据了一些可以被归类为“喜剧”的份额。更不用说一部电影如何能被严格归类为“冒险、动画、喜剧”,而不是简单的“动作、冒险、动画”或“喜剧”
x 级电影
成丨人丨电丨影与否的饼状图分布
IMDb 数据库里没有多少 X 级电影
IMDb 有一个“成人”因子,它是基本数据集中的一个布尔(0/1)变量,标记出 18 岁以上的成丨人丨电丨影。从上面的饼状图来看,IMDb 数据库中的成丨人丨电丨影数量最少,仅占总数量的 1.8%。
(注意在进行分析之前将 dtype 从对象转换为布尔作为默认 dtype 可能会误导进一步的分析)*
等级
按瓷砖类型划分的平均 IMDb 评级分布
平均评分是 7 分左右。人家在给评分方面很大方!
下面的方框图有助于描述每种标题类型之间和内部评级分布的差异。首先,有趣的是,我们看到的不是正态的高斯分布,而是总体上非常向右倾斜。典型的中等平均评级是 7 左右,而不是 5 的中等评级。
电视剧的收视率比电影高得多
此外,在十个标题类型中,与电影相比,电视剧的收视率总体较高,变化较小,这带来了一个有趣的问题:这真的是因为电视剧的质量明显优于电影吗?人们倾向于对电影持更高的标准和更挑剔的态度吗?这些都有待回答,但可以肯定的是,如果你看到一集收视率为 7,它可能只是一个平均系列。另一方面,一定要去看一部评分为 7 的电影!
来自 Unsplash 的 Florian Olivo 的 HTML 代码
我对 IMDb 数据的初步探索到此结束,它涵盖了我在开始分析时想到的大多数问题。下一步,我会做进一步的分析,看看我上一个关于哪些电影会让我更感兴趣的问题,一旦有了,我会和你分享。
你可以在 Python Jupyter 笔记本的 GitHub 上的这里找到完整的代码。
我希望你喜欢这篇文章,并从中学到一些有趣的东西。如果你有任何问题或想法,关于什么可能是有趣的进一步挖掘,请随时鼓掌和评论。谢谢!
照片由Courtney hedge在 Unsplash 上拍摄
对电影评论进行情感分析
原文:https://towardsdatascience.com/imdb-reviews-or-8143fe57c825?source=collection_archive---------10-----------------------
TD-IDF 和 scikit 令牌化的要点-学习
照片由杰克·希尔斯在 Unsplash 拍摄
IMDb:****一个与电影、电视节目、家庭视频、视频游戏和在线流媒体内容相关的在线数据库,包括演员、制作人员和个人传记、情节摘要、琐事、粉丝和评论以及评级。
1.简介和导入数据
在本文中,我将使用 IMDB 电影评论数据集进行研究。该数据集包含 50,000 条评论,其中包括 25,000 条正面评论和 25,000 条负面评论。在图 1 的、中可以看到评论的示例,其中用户给奥斯卡获奖电影《寄生虫》(2020)给出了 10/10 的评分和书面评论。
星星的数量可以很好地代表情感分类。例如,我们可以预先分配以下内容:
- 10 颗星中至少有 7 颗= >阳性(标签=1)
- 10 颗星中最多 4 颗= >阴性(标签=0)
IMDB 用户评论对电影《寄生虫》( 2020)给予了积极评价
对我们来说幸运的是,斯坦福大学的研究人员已经完成了对评论数据集进行情感分类的“繁重”工作(详情请参考引文&参考文献)。这是数据集外观的剪贴画:
包含“文本”和“情感”列的数据集
为了训练,我们的特征矩阵非常稀疏,这意味着在这个矩阵中有许多零,因为有 25,000 行和大约 75,000 列。因此,我们要做的是找出每个特征的权重,并将其乘以相应的 TD-IDF 值;将所有的值相加,通过一个 sigmoid 激活函数,这就是我们最终得到逻辑回归模型的方法。在这种情况下应用逻辑函数的优点是:该模型可以很好地处理稀疏矩阵,并且权重可以被解释为情感的概率。
逻辑回归模型的 Sigmoid 函数
2.将文档转换成特征向量
下面,我们将调用 CountVectorizer 上的fit_transform
方法。这将构建单词袋模型的词汇,并将下面的示例句子转换为稀疏特征向量。
import numpy as npfrom sklearn.feature_extraction.text import CountVectorizercount = CountVectorizer()docs = ([‘The sun is shining’,‘The weather is sweet’,‘The sun is shining, the weather is sweet, and one and one is two’])bag = count.fit_transform(docs)print(count.vocabulary_)print(bag.toarray())
请注意词汇表现在是如何存储在 Python 字典中的,每个惟一的单词都映射到惟一的整数索引。该数组显示每个唯一单词的词频。
3.使用术语频率-逆文档频率的单词相关性(TD-IDF)
TD-IDF 可用于降低特征向量中频繁出现的单词的权重,例如,上面示例句子中的单词“is”。TD-IDF 可以通过术语频率与逆文档频率的乘积来计算。计算 TD-IDF 的公式:
来源于 hackernoon
我们现在将通过实例化TdidfTransformer
方法来转换上一节的原始频率输入,以获得我们的 TD-IDF 值。
from sklearn.feature_extraction.text import TfidfTransformernp.set_printoptions(precision=2)tfidf = TfidfTransformer(use_idf=True, norm=’l2', smooth_idf=True)print(tfidf.fit_transform(bag).toarray())
显然,从示例句子来看,单词“is”在文档的第三个句子中出现频率最高。在该值被转换为 TD-IDF 值后,您会注意到,现在不是夸大的值 3,而是 0.45。这是因为‘is’这个词也包含在文档的句子 1 和 2 中;因此不太可能包含任何对我们的模型有用的或歧视性的信息。
这就是 TD-IDF 值的核心思想——它们将文本数据转换为数值,并根据文本数据语料库中单词的频率对单词进行适当加权。
4.数据准备:预处理
在本节中,我们将定义一个助手函数来预处理文本数据,因为我们的评论文本可能包含特殊字符— html 标签、表情符号—我们希望在训练模型时考虑这些字符。
import redef preprocessor(text):text =re.sub(‘<[^>]*>’, ‘’, text)emoticons = re.findall(‘(?::|;|=)(?:-)?(?:\)|\(|D|P)’, text)text = re.sub(‘[\W]+’, ‘ ‘, text.lower()) + ‘ ‘.join(emoticons).replace(‘-’, ‘’)return textpreprocessor(“This is a :) test :-( !”)
如上图、所示,在应用了定义好的preprocessor
函数后,、示例语句被去除了特殊字符;表情符号也被移到了句子的末尾。这是为了让我们的模型可以利用文本的顺序,还可以确定句子结尾的表情符号的情感。
5.文档的标记化
在本节中,我们将把我们的数据表示为单词或标记的集合;我们还将执行单词级预处理任务,如词干提取。为了实现这一点,我们将利用自然语言工具包,或 nltk。
词干分析是一种将一个普通单词的屈折形式,有时是派生相关形式,简化为基本形式的技术。例如,单词“organizer”和“organizing”源于基本单词“organize”。因此,词干提取通常被称为一种粗略的启发式过程,即“砍掉”词尾,希望在大多数情况下正确实现目标——这通常包括去除派生词缀。
from nltk.stem.porter import PorterStemmerporter = PorterStemmer()def tokenizer(text):return text.split()tokenizer('runners like running thus they run')
tokenizer
函数将文本转换成令牌
def tokenizer_stemmer(text):return[porter.stem(word) for word in text.split()]tokenizer_stemmer(‘runners like running thus they run’)
tokenizer_stemmer
功能去掉了派生词缀
6.将文本数据转换为 TD-IDF 向量
我们已经在之前的示例文档中完成了这种 TD-IDF 转换,但是现在我们将fit_transform
我们的电影评论数据集填充到 TD-IDF 值中。然而,我们没有将这个过程分成几个步骤——将文档分成词频,然后将它们转换成 TD-IDF 值——而是实现了一个方法TfidfVectorizer
,该方法在一个单独的步骤中执行上述所有步骤。
from sklearn.feature_extraction.text import TfidfVectorizertfidf = TfidfVectorizer(strip_accents=None,lowercase=True,preprocessor=preprocessor, # defined preprocessor in Data Cleaningtokenizer=tokenizer_stemmer,use_idf=True,norm=’l2',smooth_idf=True)y = df.sentiment.valuesX = tfidf.fit_transform(df.review)
7.使用逻辑回归的文档分类
使用 X 和 y 分别作为我们的 TD-IDF 值的特征矩阵和情感值的目标向量,我们准备将我们的数据集分成训练集和测试集。然后,我们将把我们的训练集放入逻辑回归模型中。
请注意,我们不是手动调整我们的模型的超参数,而是使用LogisticRegressionCV
来指定我们想要调整超参数的交叉验证折叠数——即 5 重交叉验证。
from sklearn.model_selection import train_test_splitimport picklefrom sklearn.linear_model import LogisticRegressionCVX_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1, test_size=0.5, shuffle=False)clf = LogisticRegressionCV(cv=5,scoring=’accuracy’,random_state=0,n_jobs=-1,verbose=3,max_iter=300).fit(X_train, y_train)
8.模型评估
最后,我们将传递我们的测试数据——我们的模型以前没有见过的数据——来推断我们的模型做得有多好。
准确度分数
89.6% —考虑到我们在研究中使用了一个相对简单的模型,这已经很不错了!
混淆矩阵
另一种观察我们的分类器准确性的方法是通过混淆矩阵。
观察第一排。第一行是测试集中实际情感值为 1 的评论。你可以计算出,在 25000 条评论中,12473 条评论的情感值是 1;在这 12,473 个中,分类器正确地预测了其中的 11,209 个为 1。
这意味着,对于 11,209 条评论,测试集中的实际情感值为 1,分类器也正确地预测了这些值为 1。然而,虽然 1264 条评论的实际标签是 1,但分类器预测它们是 0,这相当不错。
情感值为 0 的评论怎么办?让我们看看第二排。看起来总共有 12,527 条评论的实际情感值为 0。
分类器将其中的 11,193 个正确预测为 0,将其中的 1,334 个错误预测为 1。因此,它在预测情感值为 0 的评论方面做得很好。
混淆矩阵的一个好处是它显示了模型正确预测或区分类别的能力。在二元分类器的特定情况下,比如这个例子,我们可以将这些数字解释为真阳性、假阳性、真阴性和假阴性的计数。
9.结论
在这项研究中,我们涵盖了以下主题:
- 清理和预处理文本数据
- 使用计数矢量化、术语频率和逆向文档频率、自然语言工具包等方法执行特征提取
- 使用 scikit-learn 构建并采用了一个逻辑回归模型
- 使用 Javan 的准确度分数和混淆矩阵进行模型评估
10.引用和参考文献
[1]安德鲁·马斯、雷蒙德·戴利、彼得·范、黄丹、安德鲁·吴和克里斯托弗·波茨。(2011).学习用于情感分析的词向量。 计算语言学协会第 49 届年会(ACL 2011)。
[2]用于 CSV 文件和笔记本的 Github 库:【https://github.com/TheClub4/IMDB_Sentiment_Analysis】T5
IMDB 电视节目数据分析:第 2 部分
原文:https://towardsdatascience.com/imdb-television-show-data-analysis-part-2-39ebf47977ff?source=collection_archive---------44-----------------------
IMDB 的电视节目分析。随着时间的推移,哪些流派流行?电视节目存在年龄歧视吗?节目评分存在性别偏见吗?现在有比以前更多的新节目吗?
要查看故事的原始版本,请点击此处。
这篇文章是 IMDB 电视节目分析的第二部分。分析的第一部分涉及被高估/低估的电视节目,电视节目的一致性,以及节目是否被提前取消或走得太远,可以在这里找到。
在这篇文章中,我主要致力于回答 4 个问题——随着时间的推移,哪些类型变得越来越受欢迎或越来越不受欢迎,新电视节目的比例是否随着时间的推移而变化,领先年龄是否随着时间的推移而变化,以及在节目投票中是否存在性别偏见——更具体地说,针对女性的节目的投票是高于还是低于男性。
介绍
我先看一些探索性的图表,看看一段时间内的演出次数。在下面的图 1 中,可以看到这些年的节目数量一直增长到 2017 年。然而,有趣的是,2017 年之后,节目数量出现了小幅下降。直到 1990 年左右,增长趋势可以被看作是线性的,但此后演出的数量一直在以越来越快的速度增长。考虑到人们越来越容易接触到电视,以及网飞、亚马逊 Prime、Hulu 等流媒体服务的增加,这并不令人惊讶。
随着时间的推移,哪些类型的节目增加了或减少了?
在这里,我想看看哪些类型在流行程度上发生了变化——根据那个时期电视节目总数的比例。在该数据集中,每个电视节目标题最多可以有 3 种类型。在下面的图 2a 中,箭头图示出了流派在 2000 年前后是如何变化的。真人秀从 2000 年前占全部节目的 1%左右增长到 2000 年后的 12%以上。纪录片和喜剧节目也增加了——尽管喜剧一开始就很高。家庭、戏剧、音乐流派按比例减少。
在下面的图 2b 中,我只比较了 2000 年之前和之后的几十年中最大的几十年,即 1990 年和 2010 年的几十年。我们看到类似于图 2a 中的真人秀、喜剧、家庭、戏剧和音乐的模式。但是,在这个对比中,我们可以看到纪录片也在往下走。动画和游戏类节目在总节目中的比例也有所下降。
随着时间的推移,新节目的比例有变化吗?
接下来,我会看看随着时间的推移是否会有更多的新节目出现,或者旧节目(前几年开始的)是否在数量上占主导地位。为此,我们可以使用电视节目数据或电视剧集数据。电视节目数据对每个节目的权重是相等的——而不是根据它们的集数,而电视集更多地捕捉了电视上的内容——就时间而言。新节目或新剧集被定义为第一年的电视节目或在节目开始的第一年播出的剧集。因此,举例来说,对于《绝命毒师》来说,2008 年是开始的一年,因此 2008 年出现的所有剧集都是新的。在这种情况下,这也正好是第一季,尽管这并不总是可以保证的——同一季可以跨越多年,也可以在同一年播出两季。对于 2009 年播出的剧集,《绝命毒师》不再算新剧,其剧集也不是新剧集。
在下面的图 3 中,我们可以看到新节目和剧集的比例。新节目的比例更加稳定,大部分保持在 45-55%的范围内。另一方面,新的剧集变化很大。
除了 1980 年代早期和 1990 年代早期,新剧集的比例远远低于新剧集的比例。这表明,除了 20 世纪 80 年代初至 20 世纪 90 年代期间,第一年的节目与非第一年的节目相比,往往具有较低的集数。为了证实这一点,我在图 4 中将第一年的节目和非第一年的节目分组,绘制了平均剧集数。通过该图可以看出,除了在上述期间之外,第一年的节目确实比非第一年的节目具有更低的平均集数。一般来说,人们会认为第一年的剧集数量会更少,因为那时很多剧集可能会被取消。如果一个节目不好,很多时候它会被提前取消——所以这应该会降低第一年节目的平均水平。这也可能是因为电视迷你剧,因为它们只有一季长,而且往往比电视剧集数少。
最后,另一种查看电视节目在特定时间有多老的方法是绘制剧集年份和节目年份的平均差异。这用于定义季节编号,因为通常的季节编号在一年中可能会发生变化。同样,这可以使用剧集和电视节目数据来可视化。在下面的图 5 中,我们可以看到这个图。平均剧集年龄几乎一直呈上升趋势,2019 年是第六季(或 5 岁)。另一方面,一个普通的电视节目在 1990 年左右达到顶峰,最近在 2019 年,在第四季(或 3 岁)。同样,这种差异的原因可以基于图 4 来解释,因为第一季中的电视节目倾向于平均具有较少的剧集,较大的季剧集在剧集数据中过多地出现。从某种意义上说,电视剧数据每年只统计每部电视剧一集。
随着时间的推移,提前期是否发生了变化?
在这一部分,我会看看男主角/女主角的年龄是否随着时间而改变。我们经常听到或读到好莱坞的年龄歧视。之前,已经使用 IMDB 数据集在这里寻找电影。
在下面的图 6 中,我们可以看到一些年龄歧视的证据,从 20 世纪 80 年代中期到 2000 年代末,随着年龄中位数的持续下降。通常情况下,随着电视节目年龄的增长,主角的年龄也会增加。因此,理论上,没有新的节目,铅的年龄应该增加。然而,在上面的图 3 中,我们注意到在任何一年都有大约 45-50%的新节目。自 2000 年代末以来,我们看到中值年龄的增长——尽管我们需要补充一点关于演员重用的警告,因为这些年来通常相同的演员是多个节目的一部分。因此,需要对趋势进行更长时间的观察。
接下来,在图 7 中,我比较了男主角和女主角的年龄差异。这些年来,男女主角的平均年龄相差 5-6 岁。2010 年,差距似乎在缩小。
IMDB 评分是否存在性别偏见?
在此之前,在 IMDB 评级中寻找性别偏见的证据,首先我看了电视节目和剧集的数量及其在下图 8 中按领导性别划分的份额。电视节目的年轴是节目的开始年,而剧集的年轴是剧集的播出年。根据这两项指标,可以看出,自 20 世纪 60 年代以来,女性主演的电视节目比例一直在增加。有趣的是,根据剧集数据,由女性主演的节目几乎接近 50%,但根据电视节目数据,这一比例约为 40%。这表明,要么有女性主角的电视剧往往会有更多集,要么电视剧和各自剧集中定义的主角存在差异——这可能是像电视剧这样每集都有不同主角的情况。
在下面的图 9 中,我绘制了一段时间内(电视剧开始年份)按主角性别分组的电视剧集平均数量。我们可以看到,确实有女性主角的电视剧往往有更多的剧集。
最后,我研究了 IMDB 评级中是否存在性别偏见的证据。以前,人们曾讨论过男性会破坏针对女性的电视节目的收视率,而针对女性观众的电视节目在 IMDB 上的收视率往往较低。在这里,我控制其他变量的主机——如流派、季节数、集数、投票数、电视节目开始年份;寻找这种偏见的证据。
为了找出针对女性或男性的节目,我通过查看演员的排序,为女性导向的节目和男性导向的节目创建了一个代理变量。IMDB 数据集提供了这些信息。以女性为导向的节目被分类为主要演员为女性,或者前 2 名演员中的两名演员为女性,或者前 4 名演员中的大部分(即> = 3 名)为女性的任何节目。类似地,一个面向男性的节目被归类为应用相同的规则,但有男性演员。不符合这两种分类的电视节目被归类为中性的。这个规则被实验来复制寻找女性导向的节目组,在这里被识别为。虽然这一规则并不完美,但它可以消除很多误报(仅仅因为男主角是女性就被错误地归类为女性节目)。
我对收视率运行了一个线性回归模型,控制了一堆变量,除了这个节目是中性的,女性的,还是男性导向的。限于至少有 1,000 票的电视节目,针对女性的节目比男性节目低 0.19 分,中性节目低 0.11 分。看看至少有 5000 票的电视节目,女性节目的评分低 0.26 分,而中性节目低 0.16 分。最后,对于至少有 2 万票的节目,女性节目的评分低 0.34 分,中性节目低 0.17 分。所有这些影响都具有统计学意义(置信区间-99%)。
图 10 是一个交互式方框图(用点表示电视节目),按上述节目的性别分类分组。此图表仅包括投票数至少为 5000 且剧集评分中值大于 250 的剧集。可以看出,男性节目的平均收视率高于中性和女性节目——证实了女性节目收视率中的一些性别偏见。
注意:对于上面的互动图,将鼠标悬停在各点上以查看更多信息(如果在移动设备上,则点击)。框选允许放大,双击可将缩放重置为默认值。
IMDB 电视节目分析的第 2 部分到此结束。用于该分析的代码(和第 1 部分)可以在这里找到。分析的第一部分涉及被高估/低估的电视节目,电视节目的一致性,以及节目是否被提前取消或走得太远,可以在这里找到。
最初发布于https://sabnanih . github . io/blog/2020/06/26/IMD b-TV-Show-Analysis-part 2。
IMDB 电视节目数据分析:第 1 部分
原文:https://towardsdatascience.com/imdb-tv-show-data-analysis-4961ef39d174?source=collection_archive---------57-----------------------
IMDB 电视收视率分析。哪些节目评价过高/过低,并且始终如一?哪部电视连续剧提前停播或播出时间过长?
这篇文章已被修改,以更好地适应中等。要查看故事的原始版本,请点击此处。
在这篇文章中,我展示了使用 IMDB 数据对电视节目进行分析的结果。IMDB 是一个关于电影、电视节目、视频游戏等内容的大型在线数据库。对于这个分析,我主要关注电视连续剧、电视迷你剧和剧集的收视率。数据下载于 2020 年 5 月 14 日。完整的数据集文档可以在这里找到。在这篇文章的其余部分,我主要讨论 3 个问题——某些电视节目是被高估了还是被低估了,哪些电视节目是最一致或最不一致的,哪些电视连续剧取消得太早,哪些放得太久。
这是分析的第一部分。对于第二部分,其中包括对各种变量的分析,如流派、新剧集、年龄歧视和性别歧视的证据测试(男主角和女主角的节目),请点击此处。
介绍
我首先看一些关于收视率分布的探索性图表。在下面的图 1 中,通过评级直方图可以看出,它们有些负偏,但大多数都在 6 到 9 之间。在受欢迎的电视节目(至少有 5000 票)中,分布更集中在 6-9 范围。
某些电视节目被高估/低估了吗?
首先,我想比较节目收视率和剧集之间的差异。除了看节目收视率,我们还可以看他们剧集的收视率中值。理论上,这两个评级应该是接近的。因此,在下面的图 2 中,我创建了一个电视节目评分与剧集评分中位数的互动图,这些节目的投票数至少为 5000,剧集投票中位数至少为 250。这允许我们创建被低估和被高估的节目的定义——大部分剧集的评级低于节目评级的节目被高估,类似地,如果大部分剧集的评级高于节目评级,则被低估。在图 2 中,被高估的节目在绿色等价线上,被低估的系列在下面。我定义了一个简单的衡量节目收视率和剧集收视率之间偏差的标准(相对收视率偏差)。
δ (%) =(节目评级-中值剧集评级)/节目评级* 100 %
δ的较高正值意味着更多的高估,较低负值意味着低估。根据这个衡量标准,我们可以看到一些被严重低估的节目——蝙蝠女侠(δ=-71.43)罗斯威尔,新墨西哥(δ=-36.07)暗影猎手 (δ = -34.85)。同样,最被高估的剧集还有《辛普森一家》(δ= 19.54)《恐怖大师》(δ= 16)《暮光之城》 (δ = 15)。
注意:对于所有交互式图,将鼠标悬停在各点上以查看更多信息(如果在移动设备上,则点击)。框选允许放大,双击可将缩放重置为默认值。
从图 2 中还可以看出,2000 年之前的电视节目具有更高的δ。在下面的图 3a 中,我观察了不同季节和年份组的δ变化。可以看出,在 2000 年之前开始的节目往往比最近的系列有更高的正偏差,而在几个季节中没有明显的偏差差异。2000 年之前,更高比例的节目被高估,2015 年之后,更高比例的节目被低估,而 2000-2014 年组似乎在δ = 0 左右对称。
有趣的是,较老的节目往往显示更高的δ值。尚不清楚为什么会出现这种情况,但为了更仔细地观察,我在图 3b 中分别绘制了δ的两个分量。此外,由于在上面的图 3a 中没有明显的季数偏差,我将重点放在节目开始年份的偏差上。我们可以看到,在 2000-2014 年 post 2015 年后组中,电视节目收视率和中值剧集收视率都略高。然而,与其他年龄组相比,2000 年以前的节目具有最高的电视节目收视率和最低的剧集收视率中值。此外,多年来,剧集收视率中值似乎比电视剧收视率更稳定(总体变化较小)。也许老节目收视率更高的一个原因可能是怀旧——人们会更积极地记得以前看过的节目吗?IMDB 成立于 1993 年,互联网在 20 世纪 90 年代变得更加普及,因此一些老系列的投票者可能不会在投票时重新观看它。另一方面,剧集投票不会受到这种影响,因为人们通常不会像记住电视节目那样记住单个剧集。然而,这只是一种推测,基于现有的数据,它不是一个可检验的假设。
我还观察了不同类型的δ变化。IMDB 数据集中的每个标题最多可以与 3 个流派相关联。在下面的图 4a 中,我们可以看到动画和纪录片节目与其他类型相比往往具有更高的δ,因此被高估了。请注意,由于每个电视节目可以有 3 种类型,异常电视节目在该图上最多可以用 3 个点来表示。
同样,不清楚为什么这两种类型的节目被高估了。所以,我想按流派来看看δ的成分。在图 4b 中,可以看出,跨流派的中值剧集评级相当相似,但是电视节目评级对于动画和纪录片更高。因此,他们被高估的原因更多的是较高的电视节目收视率,而不是较低的剧集收视率中值。
最后,我创建了一个交互式的情节,通过图 5 中的流派和电视节目开始年份来可视化δ。该图应用了抖动。我们可以将鼠标悬停在这些点上来查看有关电视节目的信息。类似于上面的图 4a,电视节目可以由该图上的最多 3 个点来表示。
哪些节目是最一致/最不一致的?
接下来,我看电视剧集的收视率一致性。为了衡量评级的一致性,我定义了一个相对标准差的简单度量。
η (%) =标准偏差事件评级/平均事件评级* 100 %
η值越高,意味着一致性越差。从下面的图 6 可以看出,剧集越多,η越高。除了迷你剧,最不一致的剧集还有《你怕黑吗?(η= 24.72)《纸牌屋》(η= 18.56)《芝加哥医学》 (η = 15.97)。最符合的电视剧是像宇宙 (η = 1.14)和吉普赛 (η = 1.24)这样的较短的剧集。在至少 100 集的剧集中,交战规则 (η = 2.34)和 2 破财少女 (η = 2.66)最为一致。绿线具有回归斜率(总集数的η v/s 对数),而白线忽略倾向于具有较少集数的类型,如传记、纪录片和历史。我们可以看到,即使没有这些体裁,积极的关系仍然存在。这并不完全令人惊讶,更长的系列将有更多的机会出现异常事件(无论是积极的还是消极的),这往往会降低节目的一致性。
在下面的图 7a 中,我们可以看到根据评级和年份组的η变化。2000 年至 2014 年似乎与 2015 年至 2020 年非常相似。在 2000 年以前的群体中,我们看到更高的评级显示相对更一致。跨年度组比较,中低评级显示 2000 年前和 2000 年后的一致性较差。
2000 年前群体的模式看起来与其他群体不同。所以,我想检查一下剧集数量的差异是否能解释这种差异。在下面的图 7b 中,我们可以看到,图 7a 中具有较高η的这些组往往也具有较高的总发作值。
跨流派比较η,我们没有看到非常显著的变化,但是动画确实比其他流派有更高的η中值,并且传记、纪录片和历史更趋于一致。
一些类型更加一致的原因之一可能是这些系列通常比其他系列更短。因此,我通过类型和集数来观察电视节目的一致性。图 8b 是一个交互图,其中圆圈的大小与总集数成比例。我们可以看到传记、纪录片、历史往往也是集数较少。
这个系列是提前取消了还是持续了太久?
在这一部分,我通过电视连续剧来过滤标题,排除了只有一季的迷你剧。为了查看哪些剧集取消了早期 v/s 那些走得太远的剧集,我首先根据至少有 250 票的剧集的季数绘制了剧集评级。在下面的图 9 中,我们可以看到中值评级在 2-4 季左右最高,之后开始下降。第八季之后,降幅逐渐加大。基于此,一般来说,第二季和第四季之间的任何地方都是一部电视剧的理想长度。该图与基于 2013 年数据的图非常相似。
我们也可以分别查看它们的峰值,而不是为所有系列定义一个共同的峰值。在下面的图 10 中,我创建了一个最终赛季中值收视率与旺季之前所有赛季中值收视率之间的互动图。一个系列的高峰被定义为具有最高中值收视率的季节。根据定义,因此所有一个赛季的系列都将位于绿色直线上,这表示最终赛季收视率和之前的最高收视率相等。图表上圆圈的大小与前后峰值额定值之间的绝对差值成比例。所以,持续时间太长的系列,要么有很大的圆圈,要么远远低于等值线。前一类中的一些系列是你怕黑吗?、致命武器和坏分子。来自后一类的有两个半男人、 Scrubs 和权力的游戏。《纸牌屋》属于这两类。《权力的游戏》第四季被热切期待,但最终令人失望。《纸牌屋》在最后一季的糟糕收视率可能与凯文·史派西扮演的角色在最后一季被移除有关——因为他被指控性行为不端。对于《Scrubs》,最后一季摆脱了前几季的大部分全职演员,更适合作为一个独立的衍生系列。关于结束得太早的系列,我们可以把它们算作有 1-2 个季节落在该线上或在峰值前大于 8.5 的范围内的系列。其中一些系列是宇宙:时空奥德赛、福尔蒂塔、宇宙(1980) 和萤火虫。怪胎和极客也是在这种讨论中经常出现的系列之一。
注:本情节仅包括之前最高收视率至少为 7 的电视剧
最后,我查看了 100 个最受欢迎的完整系列的收视率范围。过去曾创建过类似的图此处的数据最后更新于 2017 年。这也可以让我们看到系列之间的一致性(如这里使用的)以及比较不同的系列和它们的结局如何。红点表示中间值、第一和第三四分位数,而蓝叉表示最终评级。大部分的系列都有很好的结局,但也有一些非常糟糕。中集与大结局收视率相差较大(大结局差于中集收视率)的剧集有德克斯特、权力的游戏、纸牌屋、真爱如血、好汉两个半。
绝命毒师是为数不多的所有剧集评分都在 7.5 以上的剧集,也是唯一一集评分在 10 的剧集!《权力的游戏》实际上总体来说相当不错(最大值和第一个四分位数之间的差异非常小),但在最后一季中有一些非常差的剧集。部分全集都在 8 集以上的剧集有大谎言、宇宙:时空奥德赛、萤火虫、怪胎和极客、憨豆先生、意中人、罗马。在这组最受欢迎的节目中,穹顶之下是唯一一集收视率不高于 8 的剧集。
IMDB 电视节目分析的第一部分到此结束。用于该分析的代码(和第 2 部分)可以在这里找到。第二部分包括随着时间的推移对各种变量的分析——比如流派、新剧集,以及对年龄歧视和性别分级歧视证据的测试(男主角和女主角的剧集)。
原载于https://sabnanih . github . io/blog/2020/05/25/IMDB-TV-Show-Analysis。
冠状病毒对股票价格的影响
原文:https://towardsdatascience.com/impact-of-coronavirus-on-stock-prices-41c108c1ec65?source=collection_archive---------34-----------------------
了解一下武汉和欧洲爆发病毒以来,冠状病毒对股价的影响。
在这个故事中,我们将构建一个 Python 脚本来计算冠状病毒对股票价格的影响。我们将把意大利病毒爆发前的股票价格作为参考,并与几周后的相同股票价格进行比较。
我们将通过使用 Python 和 Pandas 计算多只股票的每日价格变化来实现这一点。我们的最终结果将显示在下面的数据框中,该数据框包含了相对于 2 月 21 日的股价百分比变化,2 月 21 日是意大利病毒大爆发的前一天。
Python for Finance —冠状病毒对股票价格的影响
冠状病毒对股票市场的影响
我们都知道这种病毒确实起源于 201 年 12 月的武汉。它迅速发展到其他国家,最终已经成为一个全球性的疫情。
就股市影响而言,我认为股市只是在意大利疫情爆发后才受到病毒的影响。让我们看看我的理解是否正确。
冠状病毒对股票市场的真正影响是什么?让我们构建一个简单的 Python 脚本来找出答案。
首先,我将选择 10 家公司进行分析。我选择了 10 家公司,这些公司是我之前一直在跟踪的潜在未来投资对象。自由地将你自己的公司添加到列表中。
我已经将这 10 只股票添加到一个名为 公司 的列表中。我们将对每个公司做的是:
- 向财务 API 发出 http 请求 ,下载历史价格数据。API 通过创建一个免费账户提供 500 次免费的每日通话。
- 提取每家公司的 n 天历史价格,并将它们添加到熊猫数据框架中。注意:检查 API 历史价格端点以查看 API 响应的结构。
- 将每个股票数据帧连接成一个 s 单个数据帧 。
- 以冠状病毒在欧洲爆发的前一天为参考,计算价格变化。
import pandas as pd
import requests
#Store your API Key in demo. 500 free daily API calls
demo ='YourAPIKEY'
companies = ['HEAR','F', 'GOOG', 'MSFT', 'AAPL', 'IBM', 'ORCL','AMZN','BABA','JNJ']
#Create a empty list to later add each of the stock prices DataFrame
listofdf = []
# We will extract the latest 16 days of stock prices since it was when the virus outbreak started in Europe (i.e. 21st of February)
days = -16
#(1)
for item in companies:
histprices = requests.get(f"https://fmpcloud.io/api/v3/historical-price-full/{item}?serietype=line&apikey={demo}")
histprices = histprices.json()
#Parse the API response and select only last n days of prices. 16 days in our example
histprices = histprices['historical'][days:]
上面的代码所做的是提取包含在 公司 列表中的每只股票的历史价格。然后,我们解析 API 响应并提取最近的 16 天。
我们可以很容易地做到这一点,因为历史键中包含的响应是一个列表。因此,我们可以简单地对列表进行切片,以保留最新的 16 个元素,这 16 个元素代表最近的 16 个每日价格。下面是 API 响应的摘录,便于参考。
{
"symbol": "AAPL",
"historical": [
{
"date": "1989-09-19",
"close": 1.54
},
{
"date": "1989-09-20",
"close": 1.59
},
{
"date": "1989-09-21",
"close": 1.6
},...
从 API 中提取最近 n 天的 。我们可以 为每只股票 ( 2)创建一个熊猫数据帧。我们使用 pd。DataFrame.from_Dict 方法在我们的字典之外创建一个包含每日价格的 DataFrame。
最后,我们将每个股票数据帧添加到一个列表中。
for item in companies:
histprices = requests.get(f"https://fmpcloud.io/api/v3/historical-price-full/{item}?serietype=line&apikey={demo}")
histprices = histprices.json()
histprices = histprices['historical'][days:]
#2
histpricesdf = pd.DataFrame.from_dict(histprices)
#rename column to the name of the stock
histpricesdf = histpricesdf.rename({'close': item}, axis=1)
#append all dfs to list
listofdf.append(histpricesdf)
将所有股票价格放在一起
现在,我们已经将我们的每只股票的价格放入一个熊猫数据框架中。但是,我们希望将所有公司的价格放在一个数据框架中。
我们可以很容易地将它们连接起来。首先,我们将日期设置为每个数据帧的索引。然后,我们可以使用 pd.concat 方法将它们放在一起:
#set index of each DataFrame by common column before concatinatinghtem
dfs = [df.set_index('date') for df in listofdf]
histpriceconcat = pd.concat(dfs,axis=1)
现在,如果我们打印histpricecontat,我们就有了一个漂亮的熊猫数据框架,其中包含了所选股票 的所有 日价格。
通过考察个股价格,很难发现冠状病毒对股价的影响是什么。我们看到价格一直在下降,但看看正常化的结果会更有意思。
我们要做的是用第一行数据除每只股票的每日价格。在我们的例子中,这是 2 月 21 日,因为这是所有冠状病毒病例开始在意大利出现的前一天,这将是我们的参考:
histpriceconcat = histpriceconcat/histpriceconcat.iloc[0] histpriceconcat['mean'] = histpriceconcat.mean(axis=1) histpriceconcat
酷!现在,我们可以很容易地看到电晕病毒对股票价格的影响百分比。例如,我们看到像苹果这样的公司,在 3 月 13 日的收盘价比 2 月 21 日的价格低 12%(即 1–0.887941)。
同样,亚马逊的股价有 15%的折让,而中国公司阿里巴巴的股价只有 9%的折让。
意大利爆发前冠状病毒对股价的影响
显然,冠状病毒在欧洲的爆发对股票价格产生了影响。但是中间的那段时间呢?那是危机第一次从武汉开始,直到病毒到达欧洲?
我们可以通过重现 12 月 27 日到 2 月 21 日的分析来轻松检查影响。我们可以通过修改下面两行代码来实现这一点:
#-53 days is the 27th of December 2019
days = -53
#-15 days is the 21st of February
histprices = histprices['historical'][days:-15]
对于大多数股票来说,在欧洲几乎没有病例的情况下,病毒的爆发似乎没有对股价产生影响。在此期间,股票价格平均上涨了 2%。
包扎
在这篇文章中,我们构建了一个很好的 Python 工具来计算多只股票的价格变化。我们这样做是为了分析冠状病毒对股票价格的影响。
如所选股票所示,似乎只有当病毒爆发到达意大利时,股票价格才开始下跌。然而,我想指出这种分析的一个局限性。我几乎只选择了美国股票。
对亚洲股票重复这一分析,看看结果是否会有所不同,将会很有意思。我把这个任务留给你。
很高兴在我的 Twitter 账户中听到您的回复。参见以下脚本供您参考。
原载于 2020 年 3 月 15 日【https://codingandfun.com】。
#Price change
import pandas as pd
import requests
companies = ['HEAR','F', 'GOOG', 'MSFT', 'AAPL', 'IBM', 'ORCL','AMZN','BABA','JNJ']
listofdf = []
days = -16
for item in companies:
histprices = requests.get(f"https://fmpcloud.io/api/v3/historical-price-full/{item}?serietype=line&apikey={demo}")
histprices = histprices.json()
#Parse the API response and select only last n days of prices
histprices = histprices['historical'][days:]
histpricesdf = pd.DataFrame.from_dict(histprices)
histpricesdf = histpricesdf.rename({'close': item}, axis=1)
#append all dfs to list
listofdf.append(histpricesdf)
#set index of each DataFrame by common column before concatinatinghtem
dfs = [df.set_index('date') for df in listofdf]
histpriceconcat = pd.concat(dfs,axis=1)
#divide all dataframe by first line of data to enable comparison
histpriceconcat = histpriceconcat/histpriceconcat.iloc[0]
histpriceconcat['mean'] = histpriceconcat.mean(axis=1)
histpriceconcat
新冠肺炎的影响-使用 Python 进行数据可视化
原文:https://towardsdatascience.com/impact-of-covid-19-data-visualization-using-python-6f8e3bdc860b?source=collection_archive---------43-----------------------
使用 python 在印度地图上可视化冠状病毒爆发的初级方法。当你读完这篇文章时,你将能够生成一个如下图所示的美丽的地图
印度地图,每个邦都有标记,显示 COVID19 的最新统计数据。(图片由作者提供)
A.介绍
世界正在与一个看不见的致命敌人搏斗,试图了解如何应对冠状病毒带来的威胁。对于一些作家来说,唯一的出路是动笔,试图概念化和记录在国家被封锁、正常生活似乎已经停止的情况下继续生活的感觉。
“一幅画胜过千言万语”
正如引用的话所说,数据可视化有助于以更好的方式从数据源中获得信息洞察力。因此,通过这篇文章,我想展示一个人如何想象自己国家的地图,以及不同的行政区和社区如何受到这种可怕病毒的影响。
B.数据描述
为了开发这个应用程序,我们需要一些需要从网站废弃的数据
- 我们需要印度所有邦的经度和纬度坐标,我们可以使用 python 从 quicksgs.com 的中删除这些数据。
- 我们需要冠状病毒在该国各州的当前状态,我们可以使用 python 从维基百科中删除这些动态数据。
C.目标
显示印度各邦的住院人数、死亡人数、康复人数以及当前活跃病例。
D.建筑模型
- 数据收集
- 数据清理和准备
- 使用 folio(python 库)的数据可视化
让我们开始构建我们的应用程序...
D.1 .数据收集
- 导入所有重要的 python 库
2.从quicksgs.com刮取经纬度
上述代码片段的输出(图片由作者提供)
3.从维基百科抓取印度各邦的实时电晕统计数据
上述代码片段的输出(图片由作者提供)
如您所见,数据并不干净,不能直接用于构建应用程序,因此需要对其进行清理和格式化,以便与我们将要构建的应用程序兼容。
D.2 .数据清理和准备
清理坐标和 covid19 表,使其与应用程序兼容。
已清理的坐标数据表(删除了度数符号)(图片由作者提供)
清理了 covid19 表(更改了列名并删除了不必要的行)(图片由作者提供)
到目前为止,我们有两个表,一个包含纬度和经度坐标,另一个包含印度每个邦的 covid19 状态,所以最后我们在 state 列上创建了一个连接这两个表的表。
最终的表格看起来像
这是在印度地图上绘图所需的数据(图片由作者提供)
D.3 .可视化
现在我们开始最后一步,可视化这些数据!
我们使用叶子 (python 库)在印度地图上描绘以上所有数据。
万岁!我们已经用一种简单得多的方式完成了可视化,所以看一下我们的应用程序的输出。
作者图片
因为我是印度的居民,我从各种网站上研究了印度各州和冠状病毒的统计数据,如果你不是印度人,你可以用你自己国家的数据替换数据来源。
结论
这篇文章给出了最好的和聪明的方法来形象化新冠肺炎是如何影响印度各邦的。由于我们将从维基百科中删除数据,我们将获得实时数据,所以我们的应用程序不会是静态的,而是每次维基百科更新他们关于冠状病毒的网站时都会发生变化。
参考文献:
- 维基百科(一个基于 wiki 技术的多语言的百科全书协作计划ˌ也是一部用不同语言写成的网络百科全书ˌ 其目标及宗旨是为全人类提供自由的百科全书)ˌ开放性的百科全书
- quickgs.com
你可以在这里 找到详细的代码
编者注: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
感谢阅读!😄
*** [## 我附近的热门地点—使用 Python 和 FourSquare API 的数据可视化
这篇文章教你如何在规定的范围内,将你附近所有受欢迎的地方形象化
towardsdatascience.com](/popular-places-near-me-data-visualization-using-python-and-foursquare-api-4d1683cd62d1)***
新冠肺炎对英国 BAME 人口的影响
原文:https://towardsdatascience.com/impact-of-covid-19-on-the-uk-bame-population-cc09244c5d63?source=collection_archive---------59-----------------------
使用公开数据描述新冠肺炎时期影响英国 BAME 人口的社会经济因素
“…病毒不分种族和国籍;它不能偷看你的驾照或人口普查表来检查你是不是黑人。社会检查它,并代表病毒提供鉴别。——游戏木——大西洋
照片由塞缪尔·莱德在 Unsplash 拍摄
人们越来越担心冠状病毒对少数民族的影响更大。黑人、亚洲人和少数民族(BAME)社区占总人口的 14%,但占医院中重症冠状病毒患者的三分之一。英国公共卫生报告发现,新冠肺炎对所有非白人少数民族的影响不成比例。
这篇文章将超额死亡统计数据与公开的社会经济数据进行了比较,以描述英格兰和威尔士的情况。描述性分析使用 2011 年人口普查数据来确定地方当局的民族构成;一种类似于国家统计局使用的方法。因此,观察单位将是地方当局。可以在 GitHub 上找到并复制原始数据、转换和分析的完整分类。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
这是怎么回事?
地方当局种族占超额死亡的比例资料来源
上图显示了某个地方当局内发生的死亡人数与该地方当局中特定族裔的比例代表人数的对比。从左到右向上倾斜的线条表明,BAME 人口比例较高的地方当局的超额死亡人数较多。而向下倾斜的紫色线表明,种族多样性较低的地方当局看到的超额死亡人数较少。值得注意的是,数据确实聚集在接近 0 的位置,拟合这种聚集数据的回归线本身并不是关系的证据。这不是直接衡量对 BAME 社区的影响,而是比较受病毒负面影响更大的地理区域及其相应的民族构成。
年龄
在我们研究影响病毒影响的特定地点因素之前,我们必须考虑患者的年龄,我们认为这是新冠肺炎死亡率的一个促成因素。国家统计局公布的数据包含英格兰和威尔士种族群体冠状病毒相关死亡的统计数据,并显示 88%的冠状病毒相关死亡是 64 岁以上的人。
COVID 死亡人数和总人口资料来源
上图显示了所有记录的 covid 死亡的种族分类。有趣的是,这一分类与 2011 年人口普查统计非常相似,该统计显示 14%的人口是 BAME 人,covid 数据显示 16.2%的 COVID 死亡是 BAME 人。对 BAME 人口似乎没有不成比例的影响。
为了解决这个问题,我们可以使用 Dana Mackenzie 的博客帖子,通过辛普森悖论来研究美国的种族和 covid。在提出下面的因果模型时,他假设种族会影响活到 65 岁或更老的几率。
这就形成了一个链条,因果可以通过这个链条。如果我们要问,种族【仅 对 Covid 死亡率有什么影响,那么我们必须保持所有其他变量不变。因此,我们应该控制年龄并按年龄划分,创建两个组“0-64”和“65+”:
不同年龄组按种族划分的 Covid 死亡人数资料来源
现在我们按年龄划分 COVID 死亡人数;这显示了 65 岁以上人群的类似分布。按照种族划分的 COVID 死亡人数大致符合 2011 年英国人口普查的种族划分;86%的英国人口是白人,65 岁以上的 Covid 死亡者中 85.7%也是白人。然而,对于 65 岁以下的年龄段,与联合王国人口相比,BAME 人的死亡率更高;64 岁以下的 covid 死亡中,30.7%来自 BAME 社区,相比之下,只有 14%来自人口。
不同年龄组按种族划分的人口资料来源
最后,我们按照种族和年龄来划分人口,现在我们可以看到两个年龄段的种族之间的明显差异。现在,65 岁以下的 BAME 人口占了 30.7%的 covid 死亡,但只占总人口的 14.3%。2011 年人口普查显示,BAME 人口仅占 65 岁以上人口的 2.3%,但却占 COVID 死亡人数的 14.3%。这似乎表明对 BAME 人口的影响非常大。如图所示,不同的数据分组会导致不同的结果;在做出这些分组决策时,关于因果路径的假设是必不可少的。
地点:生活在城市
可能有一些内在/生理条件导致 BAME 人具有更高的风险,但这可能不是全部。多项流行病学研究承认,恶劣的生活环境会导致健康风险。Centric Lab 发布了一份深入研究人类健康和城市环境的报告。经济观察组织还详细描述了城市成为冠状病毒热点的机制,包括职业结构。这些研究支持下面列出的因果结构,其中种族将影响位置决策(移民定居在就业机会增加的密集地区),并且这些历史位置模式将代代相传。
为了解开“高风险城市环境”的谜团,以下是使用公开可用数据获得的区域特定属性的相关矩阵。使用 Spearman 值和 p 值计算的相关性显示在这里。
相关矩阵(Spearman) 来源
- 超额死亡与 BAME 人口的增加呈正相关,与白人人口的增加呈负相关。和我们在第一张散点图中看到的一样。
- BAME 种群的同处一地:图右下角的红色聚类表示 BAME 种群的同处一地。在少数民族比例较高的地方当局,其他少数民族的比例也往往较高。
- 人口密度和 BAME 人口:人口密度是使用国家统计局人口估计值和地方当局的规模(然后与 GLA 人口密度估计值交叉引用)计算的。显示 BAME 族裔倾向于居住在人口更密集的地区。当结合政府发现少数民族居住在拥挤的住房;一幅 BAME 人口密集生活的画面出现了。
- 超额死亡与 65 岁以上的人数较多呈负相关,而 65 岁以上的人往往人口密度较低,种族也较少。
- 休假比例和多重剥夺指数:这两个指标都没有显示任何关系的真实迹象。
到目前为止,我们只看到了可能让我们了解高风险地方当局类型的相关性。到目前为止,没有使用花哨的建模或加权指标,这来自汤姆·福斯写的一篇关于有用的统计数据和误导性指标的文章。他研究了一个邻近的问题——最贫困的地区受到的影响更严重——但没有找到证据。我也发现贫困和过度死亡之间没有很强的联系。但是汤姆也强调了关于社会公正元素的重要一点;BAME 人正在受到影响,但为了揭示真正的潜在因素,我们必须在分析中不带偏见,而不是试图将数据纳入我们的信念,并松散地持有我们的假设。
到目前为止,数据收集已经优先用于监控,当试图“实时”发现因果联系时,这可能是一个问题;2011 年的人口普查数据离正式过时还有一年。收集和发布关于冠状病毒的数据是一个持续的、反复的过程;收集方法和数据质量可以每周改变。比如说;第二支柱测试的省略向我们展示了“数据”可以发生多么剧烈的变化。英国《金融时报》的克里斯·贾尔斯将新的快速经济数据比作快餐——对你来说很诱人,但很糟糕。依赖数据而不了解因果结构可能会产生误导。
地理空间分析
不缺少 COVID 地图仪表板;空间分析已经成为试图控制和减少传播的一个重要工具。下面的地图使用了当地的莫兰统计数据,即:“变量(如超额死亡)与其周围值之间关系的相关系数”。我们用这种方法来比较地方当局和邻近地方当局的超额死亡率。
使用当地莫兰统计数据的超额死亡人数来源
左侧的散点图显示了地方当局及其与邻居的关系,彩色圆点在统计学上显著性 p=0.05。右上角的红色象限显示,超额死亡人数高的 LA 被其他超额死亡人数类似的 LA 包围。而左下角的蓝色象限显示的是低超额死亡,周围是其他低超额死亡的 LA。
在地图上,红色区域表示“covid 热点”,我们看到利物浦和曼彻斯特周围的伦敦地方当局和地区集群被突出显示为与类似高邻居的高超额死亡人数。莱斯特和伯明翰附近也有一个集群,当地已经实施了封锁。
结论
相关矩阵显示了 BAME 人口在密集区域的共同位置,空间分析显示了城市地区高超额死亡的相关性-表明了城市在增加 COVID 中的作用以及更多的 BAME 人生活在城市中。然而,这种推理忽略了城市内部的不平等和两极分化。城市是非常不同的社会经济群体共同生活的地方。仅关注地区整体的分析和政策将忽略这些差异,而更精细的数据将有助于这一努力。
在这样的不确定时期,我们经常寻找数据来抓住不放,并提供一些确定性;但是更多关于我们世界的数据并不一定意味着我们更了解我们的世界。复杂的模型和机器学习不会清除数据中的错误和遗漏的测试数据,而是强化它们。这是第一次尝试了解 COVID 对英国 BAME 人口的影响。
在此期间,代码和数据(原始的和干净的)都是在线的。我知道所用的统计方法非常简单,并且相信这将有助于更好地传达信息,而不是隐藏意思(以及我的错误!)复杂建模和警告的背后。有时候简单比复杂的建模方法更清晰,在这个不确定的时代,清晰是供不应求的。
汇率对公司的影响
原文:https://towardsdatascience.com/impact-of-exchange-rates-in-companies-93cc151bda4f?source=collection_archive---------40-----------------------
汇率风险可能是大公司最头疼的问题之一。
从投资者的角度来看,理解汇率变化对公司财务的影响非常重要。在本帖中,我们将首先了解汇率如何影响公司利润。然后,我们将用 Python 分析并绘制多种货币汇率。
照片由 迪莎摄塔 来自 像素
汇率如何影响公司
在分析一家公司时,汇率是一个需要考虑的重要因素。一家公司可能以美元作为记账本位币(即公司准备财务结果时使用的货币),但也有业务(如销售、贷款、供应商等。)换一种货币。
在提交财务结果时,公司必须区分货币和非货币资产/负债:
- 货币性资产/负债是那些随时可以转换为现金的资产。这些资产在报告日从其他货币重新转换为公司的功能货币。重估的任何收益或损失都会影响公司的利润。例如,应收账款、应付账款、现金余额和贷款被视为货币资产/负债。
- 非货币性资产/负债是指在报告日没有重新折算的资产/负债。它们在财务报表中使用历史汇率(即购置资产时使用的汇率)列示。例如,存货、PP 和无形资产被视为非货币性资产。
汇率对应付账款的影响
应付账款是公司欠其债权人或供应商的金额。为了理解汇率对公司应付账款的影响,让我们以 ABCD 公司为例。
假设 ABCD 公司以美元报告他们的数字:
- 从欧洲供应商处购买 10,000€的材料。
- 交易日的汇率为欧元兑美元= 1.10。这意味着需要 1.10 美元来购买 1€。
- 这笔货款只能在 4 个月后支付
在购买时,ABCD 公司在资产负债表中将 11,000 美元(10,000€ *1,10)显示为应付账款。
假设三个月后,当 ABCD 公司准备财务报表时,欧元兑美元汇率从 1.10 下跌到 0.90。这意味着我们只需要 0.90 美元就可以买到 1€。由于汇率变化,公司 ABCD 底线将受到影响。现在,只需要 9000 美元(10000€* 0.90)就可以偿还最初的 10000€。
因此,ABCD 公司将把资产负债表中的应付账款减少 2,000 美元,并通过未实现收益增加利润 2,000 美元。
汇率对贷款的影响
同样,如果一家公司有一笔以外币计价的贷款,并且汇率波动,将会对公司的损益产生影响。
假设 ABCD 公司有 10,000€贷款。当欧元兑美元汇率为 1.10 时,该公司在资产负债表中将 11,000 美元显示为贷款。然而,当欧元兑美元汇率从 1.10 升至 0.90 时,ABCD 公司将在资产负债表中报告一笔仅为 9,000 美元的贷款。与此同时,它将在损益中报告 2000 美元的未实现收益。
一旦贷款偿还给银行,收益就会实现。
对销售汇率的影响
很有趣,对吧?根据前两个例子,当美元对其他货币升值时,以美元报告的公司似乎总是会受益。
这并不总是对的。公司向货币贬值(即贬值)的国家销售会降低它们的竞争力。一个公司失去竞争力的影响可能会转化为销售量的显著下降。
想象一下 ABCD 公司在美元对欧元升值的情况下。现在,一家欧洲公司需要支付更高的欧元价格从 ABCD 购买 1 件产品。这可能导致欧洲公司停止从 ABCD 公司购买,并在欧洲寻找替代供应商。
使用 Python 的汇率
现在我们明白了为什么在分析一家公司时汇率如此重要。接下来,我们来学习如何用 Python 计算和绘制汇率。
我们将构建一个 Python 脚本来检索多种货币的历史外汇数据,并使用 matplotlib 绘制它们。我将使用一个免费的财务 API, financialmodelingprep ,提供各种各样的免费财务数据。
在下面的链接中,可以找到所有有数据的货币。对于我们的示例,我们将提取以下四种货币的汇率:
首先,我们导入所有 require 包,并向 API 端点发出 get 请求,返回过去几年的历史汇率。请注意,我们遍历了每种货币。在每个循环中,我们得到单一货币对的历史价格。
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','GBPUSD']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
print(forex)
#outcome example of USDGBP:
{'historical': [{'adjClose': 0.8029,
'change': -0.00032,
'changeOverTime': -0.0004,
'changePercent': -0.04,
'close': 0.8029,
'date': '2020-04-10',
'high': 0.80324,
'label': 'April 10, 20',
'low': 0.80076,
'open': 0.80258},
{'adjClose': 0.8069,
'change': 9e-05,
'changeOverTime': 0.00011,
'changePercent': 0.011,
...
响应包含一个带有历史价格的字典。请注意,关键字 historical 包括过去几年所有收盘价的列表。因此,我们可以对其进行循环,以便解析每个货币对的日期和调整关闭。我们将这些值存储在一个名为 exchange_rates_Python 的空字典中:
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','USDGBP']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
exchange_rates_Python[currency] = {}
for item in forex['historical']:
adj_close = item['adjClose']
trade_date = item['date']
exchange_rates_Python[currency][trade_date] = adj_close
最后,我们将字典转换成 Pandas 数据框架,以便使用 matplotlib 绘制数据。此外,我们将索引(即日期)转换为日期时间对象:
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','GBPUSD']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
exchange_rates_Python[currency] = {}
for item in forex['historical']:
adj_close = item['adjClose']
trade_date = item['date']
exchange_rates_Python[currency][trade_date] = adj_close
currencies_df = pd.DataFrame.from_dict(exchange_rates_Python, orient='index')
currencies_df=currencies_df.T
currencies_df.index = pd.to_datetime(currencies_df.index)
用 Python 绘制汇率
现在我们有了一个漂亮的熊猫数据框架中的汇率,我们准备绘制它们。我们将使用库 matplotlib 为每种货币创建一个子图。
我们将只绘制最近 90 天的汇率:
#List of symbols https://financialmodelingprep.com/api/v3/quotes/forex
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','GBPUSD']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
exchange_rates_Python[currency] = {}
for item in forex['historical']:
adj_close = item['adjClose']
trade_date = item['date']
exchange_rates_Python[currency][trade_date] = adj_close
currencies_df = pd.DataFrame.from_dict(exchange_rates_Python, orient='index')
currencies_df=currencies_df.T
currencies_df.index = pd.to_datetime(currencies_df.index)
#take last 30 days
currencies_df = currencies_df.iloc[:90,:]
fig, axes = plt.subplots(nrows=2, ncols=2)
currencies_df[currencies[0]].plot(ax=axes[0,0])
axes[0,0].set_title(currencies[0])
currencies_df[currencies[1]].plot(ax=axes[0,1])
axes[0,1].set_title(currencies[1])
currencies_df[currencies[2]].plot(ax=axes[1,0])
axes[1,0].set_title(currencies[2])
currencies_df[currencies[3]].plot(ax=axes[1,1])
axes[1,1].set_title(currencies[3])
plt.tight_layout()
plt.show()
包扎
只需几行代码,我们就能够用 Python 检索和绘制汇率。
我们可以看到,在过去的三个月里,美元比英镑要强劲得多。在 2019 年 12 月,我们需要大约 1.33 美元来购买 1,然而,在 2020 年 4 月,我们只需要 1.22 美元来购买 1。
这意味着拥有大量未结应付款项或贷款的公司比 4 个月前更有能力偿还这些款项。然而,如果英国公司由于货币升值而决定不购买美元产品,销售可能会受到影响。
正如我们在这篇文章中看到的,在做出投资决策之前,了解一家公司的货币敞口非常重要。我建议你从货币敞口的角度来分析公司的销售、贷款和应付款。
如果你喜欢这篇文章,可以随意看看我在 Python for Finance 上的其他帖子。
原载于 2020 年 4 月 30 日https://codingandfun.comT22。
正则化对深度神经网络的影响
原文:https://towardsdatascience.com/impact-of-regularization-on-deep-neural-networks-1306c839d923?source=collection_archive---------37-----------------------
使用 TensorFlow 2.0 强调重量衰减
一个高方差的模型(图片由作者提供)
简介
在这个信息高速公路的时代,我们周围的世界正在向好的方向变化,可以毫不夸张地说,深度学习是下一次变革。深度学习是一套强大的数学工具,使我们能够表示、解释和控制我们周围的复杂世界。
编程范式正在改变:我们不再给计算机编程,而是教它学习一些东西,它做我们想做的事情。
这个特殊的概念非常吸引人,并驱使机器学习实践者开发模型,以进一步发展这些概念和想法,并将它们应用于现实世界的场景中。
然而,建立复杂的机器学习模型的基本问题是如何使架构不仅在训练数据上,而且在测试数据上,即在以前看不到的特征上做得好。为了克服这个中心问题,我们必须通过应用某些策略来修改模型,以减少测试误差,这可能是以增加训练误差为代价的。这些策略统称为正则化技术。然而,只有当模型遭受 高方差 问题时,正则化才是有用的,在该问题中,网络过度适应训练数据,但是不能概括新特征(验证或测试数据)。
L2 范数或重量衰减
最广泛使用的正则化策略之一是利用 L2 范数。这可以通过向网络的成本函数添加正则化项来实现。
带有正则项的成本函数(作者要点)
等式的第一部分对应于净网络损耗的计算。术语 w & b 表示模型已经学习的权重和偏差。第二部分对应于计算权重向量的范数( w )的正则化项。这个正则项被明确地称为著名的 L2 范数或权重衰减。这样的最终结果是,网络的权重矩阵根据正则化参数 lambda (λ) 的值而受到惩罚。因此,正则化参数 λ 可以被认为是需要微调的另一个超参数。
了解工作
通过举一个极端的例子,可以理解 L2 规范的规则化影响背后的直觉。让我们将正则化参数 λ 的值设置在较高端。这将严重惩罚网络的权重矩阵( w ),它们将被赋予接近于零的值。其直接影响是神经网络的净激活减少,前向传递效应减弱。现在,有了简化得多的神经网络体系结构,该模型将无法过度适应训练数据,并将能够对新数据和特征进行更好的概括。
正则网络与非正则网络的分析(图片由作者提供)
这种直觉基于正则化参数 λ 的值被设置得非常高的事实。但是,如果选择该参数的中间值,将会提高测试集上的模型性能。
什么时候调整你的模型?
弄清楚正则化概念背后的直觉当然很好,但是,更重要的一点是要知道你的网络是否真的需要正则化。在这里,绘制学习曲线起着至关重要的作用。
绘制出模型在多个时期的训练和验证损失是确定模型是否过度拟合的最广泛使用的方法。该方法背后的概念基于这样一个事实,即如果模型过度拟合训练数据,那么训练损失和验证损失将相差很大,而且验证损失将总是高于训练损失。其原因是,如果模型不能很好地概括以前未见过的数据,那么相应的损失或成本价值将不可避免地很高。
过度拟合模型的学习曲线(图片由作者提供)
从上图可以明显看出,训练损失和验证损失之间存在巨大的差距,这表明模型明显过度适合训练样本。
使用 TensorFlow 2.0 实现 L2 范数
L2 规范的实际实施很容易证明。为此,让我们从 sklearn 获取糖尿病数据集,首先绘制非正则化模型的学习曲线,然后绘制正则化模型的学习曲线。
该数据集有 442 个实例,并接受 10 个基线变量:年龄、性别、身体质量指数、平均血压和 6 个血清测量值(S1 到 S6) 作为其训练特征(称为 x)和一年后疾病进展的测量值作为其标签(称为 y)。
非规范化模型
让我们使用 TensorFlow 和 sklearn 导入数据集。
导入数据集(按作者列出要点)
我们现在将数据分成训练集和测试集。测试集将保留 10%的训练数据。这可以使用 sklearn 中的 train_test_split() 函数来完成。
将数据集分成训练集和测试集(作者要点)
现在,我们仅使用 Keras 的顺序 API 中的密集层创建一个非规范化模型。这个非正则化模型将有 6 个隐藏层,每个层都有一个 ReLU 激活功能。
非规范化的模型架构(作者要点)
在设计了模型架构之后,我们现在用 亚当 优化器和 均方误差 损失函数来编译模型。训练在 100 个时期内进行,度量存储在一个变量中,该变量可用于绘制学习曲线。
编译和训练模型(作者要点)
我们现在绘制训练数据和验证数据中观察到的模型损失:
过度拟合的图表(图片由作者提供)
这里的结果是,验证损失在大约 10 个时期后持续上升,而训练损失持续下降。这种相反的趋势在两个损失之间产生了巨大的差距,这表明模型过度拟合了训练数据。
用 L2 范数正则化模型
我们已经从前面的学习曲线中得出结论,非正则化模型会遭受过拟合,为了解决这个问题,我们引入了 L2 范数。
密集层和卷积层都有一个可选的kernel _ regulator关键字参数。为了加入权重衰减或 L2 正则化,我们设置核正则化参数等于TF . keras . regulators . L2(参数) 。这个参数对象是用一个必需的参数创建的,该参数是乘以权重平方和的系数或正则化参数 λ。
我们现在创建一个类似的网络架构,但是这一次,我们包括了kernel _ regulator参数。
正则化模型架构(作者要点)
我们使用相同的优化器、损失函数、度量和时期数来编译和训练模型,因为这使得两个模型之间的比较更容易。
训练后,当训练和验证损失相对于时期数作图时,观察到以下学习曲线:
正确匹配(图片由作者提供)
正则化模型的学习曲线更加平滑,训练和验证损失彼此更加接近,这表示网络中的变化更小。现在,训练损失和验证损失都可以相互比较,这表明模型泛化新特征的信心增加了。
结论
在这个启发性的旅程中,我们通过比较两个模型的性能进行了广泛的分析,其中一个模型遭受过拟合,另一个模型解决了前一个模型的缺点。机器学习中的 偏差和方差 问题不容忽视,必须应用策略来克服这些非平凡的复杂性。我们今天讨论了一个这样的策略,以避免我们的模型出现偏差。
然而,旅程并没有就此停止,因此,我们必须把目光放得更远,必须顺风而行。
这里是作者的 GitHub 库的链接,可以参考完整的代码。
[## ujjwalkumar 2607/正则化-技术-张量流
通过在 GitHub 上创建一个帐户,为 ujjwalkumar 2607/regularization-techniques-tensor flow 开发做出贡献。
github.com](https://github.com/ujjwalkumar2607/Regularisation-techniques-tensorflow)
道路安全深度学习
原文:https://towardsdatascience.com/imperial-college-london-1c9bb442926?source=collection_archive---------59-----------------------
使用 YOLOv5 识别伦敦骑自行车者的危险因素
介绍
作为 ICL MRes 生物医学研究(数据科学)学位的一部分,学生将参与两个项目。项目 1 历时 5 个月,2019 年 10 月开工。项目 2 将于 2020 年 8 月完成。
第一个项目
一篇关于第一个项目成果的文章可以通过下面的链接查看。
[## 建立食物推荐系统
机器学习通过营养预防和治疗癌症
towardsdatascience.com](/building-a-food-recommendation-system-90788f78691a)
第二个项目
第二个项目的目的是使用谷歌街景图像来估计伦敦所有道路的安全分数(从自行车手的角度)。它将被训练成一个深度学习模型,使用 YOLOv5 来识别图像上的风险因素(狭窄的道路,缺乏自行车道……)。这种模式最终可能会推广到其他城市。并且因此,使得有可能估计谷歌街景可用的所有地理位置的得分。
从道路安全的角度来看,该项目旨在降低伦敦骑自行车者的死亡率和受伤率(最终也将扩展到其他地区)。建议骑自行车的人选择最安全的道路,同时指导政府在有危险信号的地方实施额外的安全措施。
从环境的角度来看,通过为人们提供更安全的通勤方式,预计可持续交通方式的采用率将会增加:自行车、踏板车……从而有助于解决我们社会最普遍的问题之一:全球变暖。
人们越来越意识到,公共交通是导致传染病(例如——新冠肺炎)传播的主要因素之一。此外,就呼吸系统疾病而言,发现空气污染程度较高的地区对其居民构成额外风险。
谦虚地说,我希望这个项目至少能在这些方向上迈出一小步。
项目的 GitHub 资源库中提供了所有实现和实现既定目标的路线图。
监督人
马吉德·埃扎提 | 里基·纳特瓦尼
用 Pytorch 从头开始实现 Canny 边缘检测
原文:https://towardsdatascience.com/implement-canny-edge-detection-from-scratch-with-pytorch-a1cccfa58bed?source=collection_archive---------17-----------------------
Canny 边缘检测
Canny 滤波器无疑是边缘检测中最知名和最常用的滤波器。我将逐步解释用于轮廓检测的 canny 滤波器。一步一步因为 canny 滤波器是一个多级滤波器。Canny 过滤器很少被集成到深度学习模型中。所以我会描述不同的部分,同时用 Pytorch 实现。它几乎可以无限制地定制,我允许自己有一些偏差。
输入图像和内核之间的卷积
我来介绍一下什么是卷积矩阵(或者说内核)。卷积矩阵描述了我们将要在输入图像上通过的滤波器。简单来说,内核将通过应用卷积乘积在整个图像上从左到右、从上到下移动。该操作的输出被称为滤波图像。
高斯滤波
首先,我们通常通过应用模糊过滤器来移除输入图像中存在的噪声。这个滤镜的选择由你决定,但是我们大多用的是高斯滤镜。
高斯核
可以制作不同大小的高斯核,或多或少居中或展平。显然,核越大,输出图像越模糊。
索贝尔滤波
为了检测边缘,必须对图像应用过滤器以提取梯度。
X 上的 Sobel 核
最常用的滤波器是索贝尔滤波器。分解成两个滤波器,第一个内核用于水平提取梯度。粗略地说,右边的像素与左边的像素相比越亮,过滤图像的结果就越高。反之亦然。这一点在莉娜的帽子左侧可以清晰的看到。
Y 上的 Sobel 核
第二个内核用于垂直提取梯度。一个是另一个的转置。这两个核心具有相同的作用,但是在不同的轴上。
计算梯度
嗯,我们在图像的两个轴上都有梯度。为了检测轮廓,我们想要梯度的大小。我们可以使用绝对值范数或欧几里德范数。
梯度的大小和方向
使用我们的梯度的大小,边缘现在被完美地检测到了。但是他们很粗。如果能只保留轮廓的细线就太好了。因此,我们同时计算我们的梯度的方向,这将被用来只保留这些细线。
在 Lena 的图片中,渐变是由强度来象征的,因为渐变的角度很重要。
非最大抑制
为了细化边缘,可以使用非最大抑制方法。在此之前,我们需要创建 45×45 方向的内核。(可以参考本帖了解旋转矩阵)
方向核
因此,该过程将需要检查8-邻域(或称为摩尔邻域)。这个概念很容易理解。对于每个像素,我们将检查方向。我们将会看到这个像素是否比它的梯度方向的邻居更强烈。如果是,那么我们将该像素与其相反方向的邻居进行比较。如果该像素与其双向邻居相比具有最大强度,则它是局部最大值。这个像素将被保持。在所有其他情况下,它不是局部最大值,并且像素被移除。
阈值和迟滞
最后,只剩下应用阈值了。有三种方法可以做到这一点:
- 低-高阈值:强度高于阈值的像素设置为 1,其他设置为 0。
- 低-弱和弱-高阈值:我们将具有高强度的像素设置为 1,将具有低强度的像素设置为 0,并且在两个阈值之间我们将它们设置为 0.5。他们被认为是弱者。
- 低-弱和弱-高带滞后:同上。弱像素通过它们的滞后水平进行评估,并被重新分配为高或低。
阈值
滞后是系统状态对其历史的依赖性。—维基百科
在我们的例子中,滞后可以理解为一个像素对其邻居的依赖性。在 canny 滤波器的滞后步骤中,我们说,如果弱像素在其 8 个邻居中有一个高邻居,则该弱像素将被分类为高。
我更喜欢用不同的方法,用最终的卷积滤波器对弱像素进行分类。如果它的卷积乘积大于 1 ,那么我将其归类为高。高邻比 0.25 分,弱半 0.25。
滞后内核
“你说 Pytorch?”
的确是的,现在是 Pytorch 代码的时候了。一切都组合成一 nn。模块。我不能保证实现会得到优化。使用 OpenCV 的特性将加快处理速度。但是这种实现方式至少具有以下优点:灵活性,可参数化,并且根据需要容易改变。
知识就是分享。
支持我,一键获取 中我所有文章的访问 。
来源
各向同性 3×3 图像梯度算子,I. Sobel
卷积,ESIEE
Canny 边缘检测器,维基百科
Canny 边缘检测,OpenCV
所有的图像都是自制的(除了莉娜和指南针)
在 Python 中从头开始用 L2 正则化实现逻辑回归
原文:https://towardsdatascience.com/implement-logistic-regression-with-l2-regularization-from-scratch-in-python-20bd4ee88a59?source=collection_archive---------2-----------------------
构建自己的逻辑回归分类器的分步指南。
由马库斯·斯皮斯克在 Unsplash 拍摄的照片
目录:
- 简介
- 先决条件
- 幕后数学
- 正规化
- 代码
- 结果和演示
- 未来工作和结论
- 参考文献
1.简介:
逻辑回归是用于分类的最常见机器学习算法之一。它是一种统计模型,使用逻辑函数来模拟二元因变量。本质上,它预测了某个观察值属于某个类别或标签的概率。例如,这是一张猫的照片还是一张狗的照片?
注意:虽然逻辑回归可以扩展到多类分类,但在本文中我们将只讨论二元分类设置。
2.先决条件:
读者应理解以下内容:
- 什么是数据集?
- 什么是特性?
- 什么是多重共线性?
- 什么是 sigmoid 函数?
3。幕后数学
假设:逻辑回归在开始其建模过程之前做出某些关键假设:
- 标签几乎是线性可分的。
- 观察结果必须是相互独立的。
- 独立变量之间的多重共线性很小或没有。
- 自变量与对数概率呈线性相关。
假设:我们希望我们的模型预测某个观察值属于某个类别或标签的概率。因此,我们想要一个假设 h 满足下面的条件0 <= h(x) <= 1
,其中 x 是一个观察值。
我们定义h(x) = g(w
ᵀ * x)
,其中 g 为 sigmoid 函数 w 为可训练参数。因此,我们有:
观察的成本:既然我们可以预测观察的概率,我们希望结果具有最小的误差。如果类标签是 y ,则与观察值 x 相关联的成本(误差)由下式给出:
成本函数:因此,数据集中所有 m 个观察值的总成本为:
我们可以将成本函数 J 改写为:
逻辑回归的目标是找到参数 w ,使得 J 最小。但是,我们怎么做呢?
梯度下降:
梯度下降是一种优化算法,用于通过在最陡下降方向迭代移动来最小化某个函数,该方向由梯度的负值定义。
我们将使用以下模板更新每个参数 wᵢ :
以上步骤将帮助我们找到一组参数【wᵢ】, ,然后这些参数将帮助我们提出【x】来解决我们的二元分类任务。
但是也存在与上述梯度下降步骤相关的不期望的结果。在寻找最佳 h(x)的尝试中,会发生以下情况:
CASE I: For class label = 0
h(x) will try to produce results as close 0 as possible
As such, wT.x will be as small as possible
=> Wi will tend to -infinityCASE II: For class label = 1
h(x) will try to produce results as close 1 as possible
As such, wT.x will be as large as possible
=> Wi will tend to +infinity
这导致了一个称为过度拟合的问题,这意味着,模型将无法很好地概括,即,它将无法正确预测未知观察的类标签。所以,为了避免这种情况,我们需要控制 wᵢ.参数的增长 但是,我们怎么做呢?
4。正规化:
正则化是一种通过惩罚成本函数来解决机器学习算法中过拟合问题的技术。这是通过在成本函数中使用附加的惩罚项来实现的。有两种类型的正则化技术:
- 拉索或 L1 正则化
- 岭或 L2 正则化(我们将在本文中只讨论这一点)
那么,L2 正则化如何帮助防止过度拟合呢?让我们先来看看我们新的成本函数:
λ 称为正则化参数。它控制两个目标之间的权衡:很好地拟合训练数据 vs 保持参数较小以避免过度拟合。
于是, J(w) 的梯度变成:
正则项将严重惩罚大的 、wᵢ 。对更小的的影响会更小。这样, w 的增长受到控制。用这些受控参数 w 得到的 h(x) 将更具普适性。
注意: λ 是一个超参数值。我们必须通过交叉验证来找到它。
- 较大的值 λ 会使 wᵢ 收缩得更接近 0,这可能会导致欠拟合。
- λ = 0, 将没有正则化效果。
选择λ时,我们必须妥善处理偏差与方差的权衡。
你可以在这里找到更多关于正规化的信息。
我们完成了所有的数学运算。让我们用 Python 实现代码。
5.代码:
注意:尽管我们在上面将正则化参数定义为 λ ,我们在代码中使用了 C = (1/λ) 以便与 sklearn 包相似。
5.结果和演示:
让我们在虚拟数据集上安装分类器,并观察结果:
决策边界图:
正如我们所见,我们的模型能够很好地对观察结果进行分类。界限就是决策线。
在这里获得模型的沙盒体验:现场预览。
6.未来工作和结论:
通过实现其他算法,如随机平均梯度、有限记忆 BFGS,来解决优化问题,有提高分类器性能的余地。
我们也可以实现套索或 L1 正则化。
仅此而已。谢谢你阅读我的博客。如果你有任何想法,请留下评论、反馈和建议。
通过我的作品集联系我,或者在 LinkedIn 上找到我。
7.参考资料:
[1]https://www.coursera.org/learn/machine-learning
[2]https://sci kit-learn . org/stable/modules/generated/sk learn . linear _ model。LogisticRegression.html
[3]https://www . geeks forgeeks . org/understanding-logistic-regression
前往顶级^
用 PyTorch 在 5 分钟内用 Java 实现物体检测
原文:https://towardsdatascience.com/implement-object-detection-with-pytorch-in-java-in-5-minutes-c3ba5769e7aa?source=collection_archive---------15-----------------------
DJL,一个引擎无关的深度学习库
PyTorch 是最流行的机器学习框架之一。其易于使用的 Pythonic 界面和生产力增强特性,如数据并行性和动态计算图,简化了计算机视觉(CV)和自然语言处理(NLP)中应用的 ML 模型的开发。
虽然有多种选项来为生产中的 PyTorch 模型提供服务,但是在 Java 中本地部署 PyTorch 模型的选项却很少。以前,用户可以围绕 PyTorch C++ API 编写一个 Java 包装器,或者使用实验性的 PyTorch Java 绑定。亚马逊的深度 Java 库 (DJL)现在通过其易于使用的高级 API 为 PyTorch 和 Java 社区提供了一个更简单的选择。通过抽象 ML 中涉及的复杂性和捆绑乏味的数据处理例程,DJL 用 PyTorch 模型将运行预测简化到几行代码。在我们的两部分博客系列中,我们展示了用户如何使用 PyTorch 进行推理。首先是预训练的 PyTorch 模型,然后是用户生成的 PyTorch 模型。在这两篇博客的第一篇中,我们展示了如何在不到 5 分钟的时间内使用预先训练好的 PyTorch 模型实现物体检测。那么,我们开始吧。
设置
要开始使用 DJL,请使用命令行或您选择的 IDE 创建一个项目。将以下代码添加到您的build.gradle
配置文件中。在这个例子中,我们使用 PyTorch 1.4.0 C++发行版。
https://gist . github . com/lank 520/7787 C2 D2 b5 FB 304 CBE 60360139 e 8760 f # file-build-gradle
使用预训练 PyTorch 模型的对象检测
在本节中,我们使用 NVIDIA 的预训练 PyTorch 跟踪模型来实现对象检测。该任务识别下方的图像中嵌入的物体(狗、自行车、卡车):
使用 DJL,您可以用下面的代码块在几行代码中运行推理:
https://gist . github . com/lank 520/74 EC 6b 35 ca 6076 fa 3c be 2848869 F7 a1f # file-od-Java
运行 PyTorch 代码会产生以下输出。输出列出了检测到的对象及其相对概率。
然后你可以使用我们的图像可视化功能来定义这个图像的边界框。
有关如何加载 PyTorch 模型的更多信息,请查找最新解决方案的文档。
什么是 DJL?
完成本教程后,你可能想知道 DJL 是什么。深度 Java 库(DJL)是一个开源库,用于在 Java 中构建和部署深度学习。该项目于 2019 年 12 月启动,在亚马逊的团队中广泛使用。这一努力受到了其他 DL 框架的启发,但是是从底层开始开发的,以更好地适应 Java 开发实践。DJL 与框架无关,支持 Apache MXNet、PyTorch、TensorFlow 2.x 和 ONNXRuntime。
PyTorch 支持范围
[更新]:现在我们在最新的 0.10.0 版本中支持 PyTorch 1.7.1
我们用 PyTorch (1.4.0) C++ API 构建了 DJL PyTorch 引擎,它允许我们运行操作符和推理代码。所有采用 TorchScript 格式的模型都可以导入并在 DJL 上运行。为了实现这一点,我们编写了一个定制的 JNI 层,在 C++和 Java 之间进行交互。目前,DJL 覆盖了 60 多家 PyTorch 运营商。DJL 本身具有以下固有特性:
- NDManager:高效的垃圾收集系统
- 多线程支持
- 模型动物园:PyTorch 模型动物园支持图像分类和对象检测模型
- 还有更多…
支持的平台:
- Mac: CPU
- Linux: CPU 和 GPU (CUDA 9.2 和 CUDA 10.1)
- Windows: CPU 和 GPU (CUDA 9.2 和 CUDA 10.1)
要了解更多信息,请查看我们的网站、 Github 资源库和 Slack channel。请继续关注我们的下一篇博文,我们将展示如何在 Java 中使用自己的 PyTorch 模型运行推理。
用 Python 实现 SVM..两分钟后。
原文:https://towardsdatascience.com/implement-svm-with-python-in-2-minutes-c4deb9650a02?source=collection_archive---------24-----------------------
有可能在两分钟内编写一个简单的 SVM 版本吗?
https://unsplash.com/photos/BXOXnQ26B7o
在本文中,我将使用 Python 来实现支持向量机分类器,它被认为是最复杂的基本机器学习算法之一。尽管如此,在我的代码中,与线性回归实现唯一真正不同的是所使用的损失函数。为了更好地理解 SVM 使用的损失函数,我也推荐你看一下这个很棒的视频和解释。
【https://www.youtube.com/watch?v=VngCRWPrNNc
从视频中你可以理解,SVM 损失函数的核心是这个公式,它描述了点到超平面的距离。
通过执行梯度下降和减少损失函数,SVM 算法试图最大化决策边界和两类点之间的差距。SVM 损失函数可以写成如下形式:
现在,让我们转到实现本身,只需要几分钟的时间来编码梯度下降,以最小化这个损失函数。
用 Python 实现 SVM
- 首先,我将创建数据集,使用 sklearn.make_classification 方法,我还将做一个训练测试拆分,以衡量模型的质量。
2.现在,我将实现上面描述的损失函数,以在训练模型时意识到损失在下降。
如您所见,我还创建了一个损失计算的小示例,位于函数本身的下方。
3.让我们写一个在随机梯度下降过程中,计算损失梯度的函数。这可以通过简单地将损失函数相对于 W(超平面的坐标)进行微分来实现
4.最后,让我们实现梯度下降本身(注意,我给 X_train 变量增加了一个额外的项,代表模型偏差)。
当训练完成时,可以通过简单地取权重和点坐标之间的点积来执行预测。
为了便于比较,我也将安装一个 sklearn SVM 模型。
如您所见,sklearn 实现的结果更好,但是对于一个非常简单的模型,我们的结果仍然很好。
你可以在我的 网站 上查看其他帖子
你可能会对我的其他中型职位感兴趣。
用 Python 在 2 分钟内实现朴素贝叶斯
在 2 分钟内解释并实施 PCA
借助 RapidsAI ,让您的机器学习速度提高 300 倍
如果这个故事对你有帮助,也许对别人也有帮助,别忘了分享一下:)
使用 Scikit-learn 和 ML 实现朴素贝叶斯分类器。网
原文:https://towardsdatascience.com/implementation-of-naive-bayes-classifier-with-the-use-of-scikit-learn-and-ml-net-d1012e105fbc?source=collection_archive---------28-----------------------
当我们想到机器学习时,首先想到的语言是 Python 或 r,这是可以理解的,因为它们为我们提供了实现这些算法的许多可能性。
然而,我每天都在 C#中工作,我的注意力被一个非常新鲜的库所吸引,这个库就是ML.NET。在本文中,我将展示如何使用 Scikit-learn 在 Python 语言中实现朴素贝叶斯分类器,以及如何使用前面提到的 ML.NET 在 C#中实现朴素贝叶斯分类器。
朴素贝叶斯分类器
朴素贝叶斯分类器是一个简单的概率分类器,它假设独立变量相互独立。它基于贝叶斯定理,其数学表达如下:
数据集
我使用了来自 UCI 机器学习知识库的葡萄酒质量数据集进行实验。所分析的数据集具有 11 个特征和 11 个类别。等级决定了葡萄酒的质量,数值范围为 0-10。
ML.NET
第一步是创建一个控制台应用程序项目。然后你必须从 NuGet 包中下载 NuGet 库。现在,您可以创建与数据集中的属性相对应的类。清单中显示了创建的类:
然后,您可以继续加载数据集,并将其分为训练集和测试集。我在这里采用了标准结构,即 80%的数据是训练集,而其余的是测试集。
var dataPath = "../../../winequality-red.csv";var ml = new MLContext();var DataView = ml.Data.LoadFromTextFile<Features>(dataPath, hasHeader: true, separatorChar: ';');
现在有必要使模型结构适应 model 图书馆采用的标准。这意味着指定类的属性必须称为 Label。其余的属性必须压缩在名称 Features 下。
var partitions = ml.Data.TrainTestSplit( DataView,
testFraction: 0.3);var pipeline = ml.Transforms.Conversion.MapValueToKey(
inputColumnName: "Quality", outputColumnName: "Label")
.Append(ml.Transforms.Concatenate("Features", "FixedAcidity", "VolatileAcidity","CitricAcid", "ResidualSugar", "Chlorides", "FreeSulfurDioxide", "TotalSulfurDioxide","Density", "Ph", "Sulphates", "Alcohol")).AppendCacheCheckpoint(ml);
完成上述步骤后,您可以继续创建培训渠道。在这里,您可以选择朴素贝叶斯分类器形式的分类器,并在参数中为其指定标注和要素的列名。您还指示了表示预测标签的属性。
var trainingPipeline = pipeline.Append(ml.MulticlassClassification.Trainers.
NaiveBayes("Label","Features"))
.Append(ml.Transforms.Conversion.MapKeyToValue("PredictedLabel"));
最后,您可以继续训练和测试模型。一切都在两行代码中结束。
var trainedModel = trainingPipeline.Fit(partitions.TrainSet);var testMetrics = ml.MulticlassClassification.
Evaluate(trainedModel.Transform(partitions.TestSet));
Scikit-learn
在 Python 实现的情况下,我们也从数据集文件的处理开始。为此,我们使用 numpy 和 pandas 库。在清单中,您可以看到用于从文件中检索数据并从中创建 ndarray 的函数,这些函数将用于算法。
from sklearn.naive_bayes import GaussianNB
from common.import_data import ImportData
from sklearn.model_selection import train_test_splitif __name__ == "__main__":
data_set = ImportData()
x = data_set.import_all_data()
y = data_set.import_columns(np.array(['quality']))
下一步是创建一个训练和测试集。在这种情况下,我们还对测试集使用 20%的划分,对训练集使用 80%的划分。我使用了 train_test_split 函数,它来自库 sklearn。
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
现在,您可以继续使用朴素贝叶斯分类器了。在这种情况下,训练和测试也在几行代码中完成。
NB = GaussianNB()
NB.fit(X_train, y_train.ravel())
predictions = NB.predict(X_test)
print('Scores from each Iteration: ', NB.score(X_test, y_test))
结果和总结
Scikit-learn 实现的朴素贝叶斯分类器的准确率为 56.5%,而 ML.NET 的准确率为 41.5%。这种差异可能是由于算法实现的其他方式,但仅基于精度,我们不能说哪个更好。然而,我们可以说,机器学习算法的实现的一个有前途的替代方案正在开始出现,这就是 C#和 ML.NET 的使用。
Python 中两个堆栈队列的实现:数据结构
原文:https://towardsdatascience.com/implementation-of-queue-from-two-stacks-in-python-data-structure-c35da025eaf6?source=collection_archive---------60-----------------------
了解堆栈和队列。然后使用两个堆栈实现一个队列。
栈和队列都是列表。但是他们有不同的更严格的规则。本文将讨论堆栈和队列。首先,将有一个堆栈实现,然后是一个带有堆栈的队列。
堆
堆栈是具有后进先出(LIFO)规则的列表。最后进去的元素先出来。
来源:作者
你可能会想这有什么用。编辑器中的撤销操作就是一个很好的例子。当我们“撤销”时,它会撤销上一次操作。编译器使用堆栈检查匹配的括号。以下是堆栈所需的功能:
- 空列表的初始化。
- 向列表中添加元素。
- 从列表中弹出元素。
- 检查列表是否为空。
- 确定列表的顶部元素。
- 拿到名单。
以上功能的 Python 实现:
class Stack():
def __init__(self):
self.elements = []def push(self, element):
self.elements.append(element)def pop(self):
return self.elements.pop()def is_empty(self):
return self.elements == []def peek():
if not self.elements.is_empty():
return self.elements[-1]def get_elements(self):
return self.elements
长队
队列也是一个列表或容器。它遵循先进先出(FIFO)规则。一个很好的例子是在杂货店排队。先排队的人有机会先付钱后离开。当一个新来的人想排队时,她/他必须排在队伍的后面。队列有两个操作。入队和出队。其中 enqueue 表示添加列表或容器中的元素。出列意味着从列表底部删除一个元素。
下图清楚地显示了堆栈和队列的区别:
我们可以使用两个堆栈来实现一个队列。
- 将 stack1 中的元素排队。
- 出列可能很棘手。因为 stack 从顶部移除了一个元素。但是 queue 从底部移除了一个元素。我们需要弹出 stack1 中的所有元素,并不断将它们添加到 stack2 中。因此,stack1 的底部元素将是 stack2 的顶部元素。现在,如果我们从 stack2 中弹出一个元素,我们就弹出了 stack1 的底部元素。这就是我们的出列操作。操作完成后,我们应该再次将元素放回 stack1 中。下面是代码的样子:
class Queue:
def __init__(self):
self.stack_1 = Stack()
self.stack_2 = Stack()def enqueue(self, item):
self.stack_1.push(item)def dequeue(self):
if not self.stack_1.is_empty():
while self.stack_1.size()> 0:
self.stack_2.push(self.stack_1.pop())
res = self.stack_2.pop()
while self.stack_2.size()>0:
self.stack_1.push(self.stack_2.pop())
return res
我想分享这个,因为它是学习算法和数据结构的一个很好的练习材料。
下面是一篇关于使用排序算法解决一些问题的文章:
- 求两组坐标的交集,按颜色对桶进行排序。
半监督生成对抗网络在 Keras 中的实现
原文:https://towardsdatascience.com/implementation-of-semi-supervised-generative-adversarial-networks-in-keras-195a1b2c3ea6?source=collection_archive---------30-----------------------
使用半监督学习构建强大的分类器
穆罕默德·阿里扎德在 Unsplash 上的照片
每个人都听说过监督学习和非监督学习,但在它们之间还有另一套学习技术,称为半监督学习。
监督学习是机器学习中最常用的技术,但它有一个缺点,即它需要大量的标记数据。给数据贴标签需要花费大量的精力和时间。这就是半监督学习发挥作用的地方。
什么是半监督学习?
半监督学习是一种技术,我们只使用大量未标记数据中的一小部分标记数据来训练我们的模型。
这似乎是一个有趣的方法,而且标记数据的成本也大大降低了。
什么是生成性对抗网络?
生成对抗网络(GAN)是由 Ian Goodfellow 及其同事在 2014 年设计的一类生成模型。就生成模型而言,这是一个突破。
在甘的研究中,有两个神经网络试图击败对方(即一个网络的损失是另一个网络的收益)。这两个神经网络被称为生成器和鉴别器。
生成器模型试图生成图像(类似于训练数据),鉴别器的工作是将图像分类为真实的(来自训练数据)或虚假的(来自生成器)。所以基本上鉴别器试图不被发生器愚弄,而发生器试图愚弄鉴别器,这就是为什么这被称为博弈论方法,因为发生器和鉴别器都在博弈中试图战胜对方。
如何使用 GAN 进行半监督学习?
下面是半监督 GAN 的模型
半监督甘,来源:图片由作者提供
让我们来了解一下模型
鉴别器通过三种类型的图像,即有标签的训练图像、无标签的训练图像和生成器生成的假图像。它的工作不仅是区分真实/虚假图像,而且将标记的训练图像分类到它们正确的类别中。
鉴频器有两种不同的输出:
- Softmax 激活,用于将标签数据分类到正确的类别,即这是受监督的鉴别器。
- 自定义激活分类为真或假。我们将在实现中看到自定义激活。
这种方法的强大之处在于,鉴别器不仅要训练有标签的数据,还要训练大量无标签的数据,因为它还必须区分真实/伪造的图像,因此对于这项任务,鉴别器需要学习提取用于将图像分类为真实或伪造的特征。这种对抗性训练将有助于鉴别器更准确地对标记数据进行分类,因为它在学习对真实/伪造图像进行分类的同时,还会识别其他模式,而这通常不会在一小组标记数据上进行。
让我们来看看上述模型在 Keras
中的实现。我将使用的数据集是古老的 MNIST 数据集(每个人的最爱)
设置
首先导入所有必需的包
z_dim 是我们将传递给生成器的随机正态变量的维数。
资料组
我们需要准备如下两个数据集:
- 监督鉴别器:数据集将是完整训练集的一个小子集。
- 无监督鉴别器:数据集将是完整的训练集。
这里只需要解释batch _ label()函数
batch_labeled() 通过选择样本子集及其标签,为监督鉴别器准备数据集。我们将使用 100 个带标签的例子(即每类 10 个例子)来训练我们的分类器。
batch_unlabeled() 从数据集中随机抽取图像,图像数量等于给定的 batch_size。
发电机网络
发电机网络
使用 Keras 顺序 API 构建我们的生成器模型。这是一个基本的发电机模型,因为我们的任务并不复杂。
密集的层用于对我们的尺寸 z_dim 进行整形,其形状为 (batch_size,100) 到 (batch_size,7,7,256) ,这样我们就可以在其上应用 Conv2DTranspose 层来生成图像。
鉴别器网络
鉴别器网络
这是我们的鉴别器,它输入一个图像(假的或真的),输出图像是真的还是假的,即“1”或“0”。
这里的输出一直是密集层的输出,没有任何激活,因此这些值也可以是负的。下一步就会明白为什么要这样做了。
监督鉴别器
监督鉴别器
build _ discriminator _ supervised()将我们在上述步骤中创建的鉴别器模型作为输入,并使用 softmax 激活将我们的输入图像分类为 10 个类别之一(针对 MNIST)。
无监督鉴别器
build _ discriminator _ unsupervised()接受我们之前创建的鉴别器的输入,并对鉴别器的输出应用一个 custom_activation() 。
当我们创建 discriminator 时,我们将它的输出保存为一个密集层的输出,该层在没有任何激活的情况下给出值。因此,自定义激活会执行以下操作
自定义激活功能
这里 z 是没有任何激活的密集层的输出, k 对于我们的情况是 10, y 在 0 和 1 之间。
由于图像真实或虚假的概率将是所有类别的概率之和,因此激活函数基本上是所有类别的和,并且在[0,1]之间缩放输出,以获得图像真实或虚假的概率。这个技巧比使用乙状结肠有效得多。
监督和非监督鉴别器使用的权重相同,只是两者使用的输出节点不同。
建筑甘
build_gan() 基本上接受生成器和鉴别器的输入,并将它们合并形成一个模型。这样做是为了训练发电机网络。这将被称为复合模型。
注意,这里的鉴别器是无监督的鉴别器,因为 GAN 仅用于未标记的图像。
训练半监督 GAN
首先,让我们通过编译上面创建的所有模型函数来构建我们的模型。
定义模型
请注意,在构建 GAN ( 复合模型)时,我们将Discriminator _ unsupervised保持为不可训练(第 7 行),因为 GAN 的训练是在训练鉴别器然后训练生成器的步骤中完成的,所以在训练生成器时,我们不希望我们的鉴别器被更新。
使用的成本函数是用于无监督鉴别器的二元交叉熵和用于有监督鉴别器的分类交叉熵。
现在主要的训练循环
主训练循环
因此,我们通过在一批真实的标记图像上训练监督鉴别器来开始我们的训练,然后我们在真实的未标记图像(标记为“1”)和由生成器生成的假图像(标记为“0”)上训练无监督鉴别器,最后,我们使用我们之前定义的复合模型通过鉴别器来训练我们的生成器。
因此,发电机工作的培训方式如下:
- 生成器生成一批假图像。
- 这些生成的假图像通过鉴别器进行分类,鉴别器的目标标签为真,即“1”(第 31 行)。注意:鉴别器在此步骤中不会更新。
- 因此,如果图像看起来不真实,损失会非常高,因此为了最小化损失,生成器将开始生成看起来真实的图像,这是我们的目标。
因此,随着生成器在更多图像上得到训练,它开始生成更好的图像,并且鉴别器也开始变得更好,因为它不希望在对假图像或真图像进行分类时其损失变高。因此,这就是为什么鉴别器和生成器都在相互竞争,如果一个变得更好,另一个也需要改进。
训练后,我们只需使用鉴别器并丢弃生成器,因为我们的主要目的是为 MNIST 构建一个分类器。
结果
来源:作者图片
因此,在测试集上获得的准确度约为 90.67 %,这是非常令人印象深刻的,因为我们刚刚使用了 100 幅标记图像(每类 10 幅)来训练我们的鉴别器。因此,这是一个强大的分类器,不需要大量的标记数据。当然,如果我们使用 1000 个标记图像,性能会提高更多,与现代数据集相比,这仍然很低。
由生成器生成的假图像,来源:作者提供的图像
所以,这些是由生成器生成的假图像,除了少数例外,都相当真实。
您还可以尝试在 100 张带标签的图像上训练没有 GAN 的相同鉴别器模型,看看它的表现如何。无疑会比用甘训练出来的鉴频器差。
这是与此实现相关联的 Github Repo
https://github.com/KunalVaidya99/Semi-Supervised-GANT4
如果你想了解更多关于 GAN 的知识,你可以试着阅读这本书Jakub Langr,Vladimir Bok 所著的《行动中的 GAN 与生成性对抗网络深度学习》。
这是我第一篇关于 Medium 的文章,如果你喜欢,请告诉我。感谢阅读!。
参考
https://arxiv.org/pdf/1606.01583.pdf
用 JavaScript 实现 2D 物理学
原文:https://towardsdatascience.com/implementing-2d-physics-in-javascript-860a7b152785?source=collection_archive---------7-----------------------
让我们在实现真实的 2D 物理模拟和可视化的同时享受一下 JavaScript 的乐趣吧!
照片由莫赫达姆·阿里在 Unsplash 上拍摄
真实动画的物理和实现可能看起来非常复杂和困难,但事实并非如此。这些算法可以非常简单,并可以产生各种物理概念的现实模拟,包括速度,加速度或重力。
所以,让我们看看在用 JavaScript 实现 2D 物理模拟时,这些算法是如何工作的!
你可以看看这里的动画和例子:https://martinheinz.github.io/physics-visual/
TL;博士:源代码可以在我这里的知识库中找到:https://github.com/MartinHeinz/physics-visual
匀速和加速运动
让我们从最基本的事情开始——移动东西。
如果我们只想匀速运动,那么我们可以使用这样的代码:
在上面的代码中,x
和y
是一个对象的坐标,例如椭圆,接下来的vx
和vy
分别是水平轴和垂直轴上的速度,dt
(时间增量)是定时器的两个滴答之间的时间,在的情况下,JavaScript 是对requestAnimationFrame
的两次调用。
举例来说,如果我们想要移动位于(150, 50)
的对象并向西南方向移动,那么我们会有如下结果(单次滴答后的移动):
不过匀速运动很无聊,所以让我们加速物体的运动:
在这段代码中,我们添加了ax
和ay
,它们分别代表 x 和 y 轴上的加速度。我们用加速度来计算速度或速率的变化(vx/vy
),然后我们用它来像以前一样移动物体。现在,如果我们复制前面的例子,只在 x 轴上增加加速度(向西),我们得到:
重力
既然我们可以移动物体,那么把物体向其他物体移动怎么样?嗯,那就叫引力。为了实现这一点,我们需要添加什么?
只是为了让你知道我们想要达到的目的:
首先,让我们回忆一下高中时的几个等式:
力的方程式:
如果我们现在想把它扩展到两个物体相互作用的力,我们得到:
强制均衡器
这变得有点复杂了(至少对我来说),所以让我们把它分解一下。在这个等式中|F|
是力的大小,对于两个物体来说是一样的,只是方向相反。这些物体用它们的质量来表示- m_1
和m_2
。k
这里有一个引力常数和r
是这些物体重心的距离。如果还是没有太大意义,那么这里有一张图:
物体拉动
如果我们想创建一些可视化的东西,我们最终会有两个以上的对象,对吗?那么,当我们有更多的物体相互作用时会发生什么呢?
多物体力
看上面的图片,我们可以看到两个橙色物体用力F_1
和F_2
拉着一个黑色物体,但我们感兴趣的是最终力F
,我们可以这样计算:
- 我们首先使用上面的等式计算力
F_1
和F_2
- 然后我们把它分解成向量:
好了,我们有了所有需要的数学知识,现在代码看起来怎么样?我将省去所有的步骤,只给你看带注释的最终代码,如果你需要更多的信息,请随时联系我。🙂
碰撞
当物体运动时,它们也会在某一点发生碰撞。我们有两个解决碰撞的选项——将物体推出碰撞或弹开,让我们先看看推动的解决方案:
在我们能够解决碰撞之前,我们需要首先检查两个对象是否确实在碰撞:
我们首先声明代表两个碰撞物体的Collision
类。在checkCollision
功能中,我们首先计算物体距离的x
和y
分量,然后计算它们的实际距离d
。如果它们的半径之和小于它们的距离d
,那么它们一定会发生碰撞,所以我们返回新的Collision
对象。
冲突
现在,为了解决它们的碰撞,我们需要知道位移的方向和大小:
碰撞后的运动
因此,在 JavaScript 代码中应该是:
您可以在https://martinheinz.github.io/physics-visual/查看这种碰撞解决的交互示例(点击穿过物体)
用力解决碰撞
这个难题的最后一块——通过反弹物体来解决碰撞。在这种情况下,最好省略所有的数学,因为这会使文章变得两倍长,所以我要告诉你的是,我们需要考虑动量守恒定律和能量守恒定律,这有助于我们建立和解决以下神奇的方程:
那么,这个神奇的k
如何帮助我们呢?我们知道物体移动的方向(我们可以像之前用n_x
和n_y
一样使用特征向量来计算),但是我们不知道移动了多少,这就是T3。所以,这就是我们如何计算矢量(z
,它告诉我们将这些物体移动到哪里:
动力
现在是最后的代码:
结论
这篇文章包含了很多数学知识,但是大部分都很简单,所以我希望这能帮助你理解和熟悉这些物理概念。如果你想看更多的细节,那么你可以在我的库这里查看代码,在这里查看交互演示。
本文最初发布于martinheinz . dev
从头开始实现象棋引擎
原文:https://towardsdatascience.com/implementing-a-chess-engine-from-scratch-be38cbdae91?source=collection_archive---------2-----------------------
教我的电脑和我一起玩
国际象棋是一种古老的双人战略棋类游戏。有大量的可能性,因为在每走 5 步之后,有 69,352,859,712,417 种可能的游戏。因此,几乎不可能预测每一步棋。
冰岛举办了 1972 年国际象棋世界锦标赛,Fischer vs spas sky——图片由 Mike Swigunski 在 Unsplash 上拍摄
作为一名谦逊的国际象棋业余爱好者,我给自己提出了这样的挑战:开发一个简单、好看、有人工智能、能打败我的国际象棋游戏,没有机器学习。这篇文章是关于我实现它的旅程,由 4 部分组成:规则、计算、策略和游戏。
为了把事情弄清楚,我也决定不去阅读象棋引擎上的理论或算法解释,我想基于我的常识和个人经验,建立我自己的算法。
我给它取名为鲍比,以此向罗伯特“鲍比”詹姆斯菲舍尔致敬,他是国际象棋世界冠军,也是我年轻时的偶像之一。
1.规则
片
国际象棋是由两个对手玩的,每个对手一种颜色,白棋和黑棋。然后,每一面都有以下部分:
- ♔——一个国王
- ♕——一位女王
- ♖——两辆车
- ♗—两位主教
- ♘——两位骑士
- ♙——八枚棋子
初始位置
游戏开始时,初始位置总是一样的,严格定义的。其他棋子前面的一系列棋子,皇家夫妇在中间,然后对称地出现主教、骑士,最后是车。
棋盘是 8×8 格的正方形格子(即二维阵列),明暗背景交替。现在是时候对该板进行可视化表示了。
直观显示
当我考虑画这些作品时,我首先开始考虑免费的图形资源。但是这将迫使我管理图片,这对于快速开发来说是不可取的。幸运的是,我发现每个符号都是 Unicode 的一部分:一个基本的文本标签就足以画出一幅作品!太好了,我已经能够在控制台日志中打印出板子来可视化它了:
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
有趣,但没有我想象中的好看,所以我决定创建一个基本的 GUI:棋盘是一个单一的框架,具有网格布局,由 MVC 架构驱动。
我从暗背景和亮背景的定义开始,其中每个方块都是一个标签,包含一个空值或一块的 Unicode 符号。
如果你熟悉国际象棋,你可能知道棋盘网格有一个由字母(横轴,或文件)和数字(纵轴,排名)组成的坐标系统,用于游戏符号,即编写移动脚本。我最终将它们添加到我的板的侧面,这是结果,一个简单的 10x10 网格:
我的象棋游戏的基本图形用户界面
移动
我们继续。规则是什么?允许哪些操作?基本上有 4 种类型的移动,可以根据棋子组合:
- 顺子(用于车、皇后和国王)
- 对角线(主教、女王、国王)
- 骑士
- 棋子移动
注意,特殊招式顺道,卒晋升,以及阉割为了简单起见被故意忽略,但后来已经实现。关于招式的详尽描述可以在维基百科上找到。
实现移动的一些附加限制:一个棋子不能跳过另一个棋子,除了骑士,当一个棋子到达敌人的棋子时,它可以抓住它并在棋盘上占据它的位置。
我还设计了一个可选的彩色边框来突出移动:
- 红色-当前选定的部分
- 蓝色-所选棋子的可能目的地
- 绿色——对手的最后一步棋
方块周围的彩色边框可以更好地观察移动
规则
既然我们可以移动棋子,我们必须考虑游戏规则:有些事情可以做,有些事情必须做(通常是逃避检查)。
在这里解释每一个规则超出了范围,但是我尝试实现了大多数约束,其中包括:
- 玩家的回合;
- 检测一个王是否在检查中,当无法逃脱时,确定该王是将死(游戏失败);
- 两个国王总是相隔一个广场;
游戏结束
现在游戏如何结束?有两种情况:要么一方赢,要么双方都不赢(平局)。
如前所述,当你的国王被牵制时,你就输了,而且没有可能逃脱被牵制。
但是你画如果:
- 没有可能的举动但你的国王不在检查(相持);或者
- 两位玩家连续重复 3 个相同的动作;或者
- 两个国王都单独在棋盘上(或者没有足够的材料获胜)
我还实现了另一个规则来避免永无止境的游戏:如果在最后 50 步内既没有棋子移动,也没有俘获,那么这个游戏就是平局。请注意,这条规则在很大程度上被国际象棋界所接受。
有了上面的规则,两个人类可以玩一局,但是我们要和电脑对战,那就说核心吧。
2.计算
愚蠢的人工智能
可能的最低智能是什么?选择一个没有任何策略的允许移动。这是我作为机器人实现的第一级“智能”:计算所有可能的移动,并随机选择其中一个。好吧,那很有趣,但是现在让我们严肃点。
评估形势
人类如何决定该走哪一步?他/她选择能带来优势的行动,这可以转化为形势的改善。因此,我们需要一个函数来评估当前位置,尝试移动,然后重新评估。
基本上,根据游戏规则,当一步棋导致将死,那么停止搜索,因为这是最好的一步棋。另一方面,如果移动导致平局,我实施了一个惩罚分数,因为我想让 AI 尽可能具有侵略性。
那么,如何计算分数呢?一般来说,对于初学者来说,最简单的策略是捕捉片段。实现是给每种类型的棋子打分,所以你可以通过将你所有棋子的分数相加来计算你当前的分数,对对手的棋子也是如此。然后,通过比较两个分数,就可以很容易地估计出你在多大程度上(或不在多大程度上)超过了你的对手。
块的任意值
天真的评价
但不需要成为国际象棋大师就能明白事情没这么简单。在你移动之后,将轮到你的对手。假设你用你的皇后俘获了一个棋子,然后你的对手俘获了你的皇后,你失去了最重要的棋子,所以最后,这是一个可怕的举动。
为了避免这种情况,移动选择函数必须是递归的,以便在尝试移动后,该函数会被再次调用,但对对手来说:这个想法是猜测答案 s 应该是什么。并评估由此产生的情况。
由于这是一个递归函数,这可以应用几次,让我们尝试想象在 2,3 或更多的回合中可能出现的情况。但这里的成本是指数级的,因为我们计算了所有可能的移动,在实践中,我不得不将它限制在 3 个移动的深度,否则它会花太多时间来玩。
注:我稍后会了解到,这种计算是极小极大算法家族的一部分——考虑到对手的反应,尽量使你的最小收益最大化。
除此之外,预测 1、2 或 3 步棋允许 AI:
- 玩一个赢得游戏的移动导致立即将死(深度 1)
- 在(深度为 2)之后立即下一步防止损失的棋
这正是我用来测试的:设置一个游戏,可以在 1 或 2 步内导致将死,并让它分别找到获胜和避免失败的最佳步骤。
上面的逻辑在很多情况下都很有效,但是通过将死对手的王来最终获胜是没有效率的。评估功能需要一些额外的智能。
3.战略
我经常问自己,当我开始玩游戏时,我得到了什么提示,也就是说,什么使一步棋变好或变坏。
控制中心
看似简单,但取胜的关键之一是控制棋盘的中心。把你的棋子放在中间,或者至少让它们覆盖中间,有助于发展军队,进攻和防守。
我用热图的概念实现了这一点:
- 4 个最居中的方块各值 2 分;
- 他们周围的 12 个方块值 1 个点;
- 所有剩余的方块值 0 点。
然后,一个人必须计算每一个棋子的移动,并根据他们可以到达的位置,将上面所有的点加起来,得到最后的分数。
控制电路板中心的热图
汇聚到将死
我注意到我的 AI 在游戏的开始和中间都能玩得很好,但在结束游戏时却面临困难,因为它没有启发性地去玩那些导致攻击对手国王的移动。
我决定重新使用上面的想法来解决这个问题,也就是说,定义一个专注于对手王的热图:
- 打王的时候 3 pts,因为有王在牵制非常好;
- 王身边 2 pts,因为你阻止他逃跑;
- 国王区周围 1 pt,因为最终导致限制他的移动;
- 其余所有方块为 0。
以国王为中心的热图,其中国王将位于广场 g1 上(通常在王车易位后)
空缺
像在很多游戏或者运动中,有一个理论部分是一定不能忽视的。基于上述启发的策略是好的,但可能不足以有一个好的开始,而象棋中不完美的开始可能会很快导致失败。为了避免早期的糟糕举动,玩家用心学习开局,这就是我实现的。当然,有很多库有大量的空缺,但是因为我的目标是让一个人工智能可以和我相比,我选择只让它知道 15 个主要的空缺,每个空缺有 2-3 步。
我使用了一个树形结构来对开口进行分类:一个移动导致一个节点,一片叶子是给定开口的最后一个已知移动。
开口树的摘录
当人工智能必须从给定的节点中选择一条路径时,它会随机选择一条。我后来注意到这种随机选择是多么明显,因为它在每个游戏中引入了变量,避免了无聊的可预测路径。当树不知道刚刚下的棋时,人工智能会退回到默认的计算。
发展奖励和惩罚
给新手一些提示,避免早期游戏出现大的错误。例如,强烈推荐阉割(一种同时移动国王和车的特殊移动),因为它在保护国王的同时激活靠近中心的车。因此,我根据游戏历史实施奖励和惩罚。
在以下情况下给予奖金:
- 阉割完成了
在下列情况下给予处罚:
- 国王在阉割之前已经移动了,因为阉割的权利已经丧失
- 国王、王后或车(阉割除外)在开局时移动(前 5 步)
- 同一个棋子(卒除外)在开局时被移动了不止一次
4.(演奏等的)表现,风格;(乐曲)演奏
测试游戏
虽然我在开发阶段玩了很多次,但我最终决定创建一个简短的锦标赛,反对机器人和它们的创造者:
- 一个愚蠢的机器人,随机播放
- 机器人分别计算深度为 1、2 和 3 的移动
- 另一个机器人计算移动深度为 3,但也经历了一些开放
- 我,一个业余棋手
所有的参与者都是面对面的,每场比赛都是白棋两次,黑棋两次。这是最后的记分牌:
不确定比开发出来的人工智能更强是好是坏,但可以肯定的是,我赢了大部分与人工智能的比赛。然而,我对三个最好的机器人都丢了一分,可能是因为注意力不集中和/或玩得太快,这仍然是一种满足感,因为它证明了人工智能可以打败我。
不出所料,计算水平最高的机器人是亚军,随机的最后完成。一个有趣的事实是,后者能够通过与更强的机器人打平来获得 0.5 分。至于两个等级为 3 的机器人,他们几乎击败了所有其他机器人,他们的对决以 2 比 2 的比分结束,这也很符合逻辑。最后,人们不能说掌握理论上的开口会带来显著的优势,至少不会对其他采用相同策略的机器人产生影响,但我相信当面对人类时,它确实会产生影响。
试一试
现在你知道我是如何建立自己的象棋智能的,你可能希望尝试一下。当然,代码是公开的,可以在我的 GitHub 库中找到。
在你的电脑上本地运行它会给你最好的体验,但也提供了一个网页版,尽管有一些限制:一次只有一个会话(即一个玩家),因为资源有限,移动棋子时会有一些图形延迟。但是它是免费的,在线的:
[## 博比象棋游戏
这个基本的国际象棋游戏是一个谦虚的敬意,著名的国际象棋选手罗伯特詹姆斯'鲍比'菲舍尔,世界国际象棋冠军。它…
www.bobby-chess.com](https://www.bobby-chess.com/bobby/)
注:如果你有兴趣阅读我是如何通过一个网站展示一个 Java Swing 桌面应用的,我写了另一篇文章专门讨论这个技术方面:
[## 在不改变代码的情况下,将 Java Swing 应用程序增强为一个干净、优雅的 Web 应用程序
据说 Java Swing 是一种设计桌面应用的过时技术。然而,有一些简单的方法可以改变…
codeburst.io](https://codeburst.io/enhancing-a-java-swing-app-to-a-clean-elegant-web-app-without-changing-the-code-a69d7c1c2781)
结论
人工智能的局限性和优势
我意识到我的象棋引擎还有改进的空间,离最好的计算机引擎还差得很远。但是正如我在介绍中所说的,我想用我自己的见解对它进行编程。这就是为什么我拒绝使用理论材料来加强它,但是如果你有兴趣阅读这个主题,我推荐你看一看国际象棋编程。
此外,我完全相信,对于这种游戏来说,没有什么比机器学习更好的了,或者也许是机器学习和传统计算引擎的微妙结合。AlphaGo 是第一个击败围棋世界冠军的基于 ML 的引擎。然后在 2017 年推出了 AlphaZero ,并在很大程度上击败了最好的传统象棋引擎,即 StockFish。
但是当我父亲好心地测试我的象棋引擎时,他说了一句有趣的话:
最先进的象棋引擎很无聊。
这尤其正确,原因有二:首先,他们系统地击败了我们这些业余玩家。第二,他们中的一些人可能总是在给定的情况下采取相同的行动,所以他们实际上是可以预测的。
即使在最高的计算水平上,Bobby 也可能被业余玩家打败
博比引擎试图逃避这一点,由于在开放的随机性,以及当计算等效移动。
根据三步棋深度的合理限制,一个业余棋手在想象陷阱时有一点创造力就有机会获胜。
我的引擎的一个弱点是它在游戏开始时的糟糕开发,就在开场后:它试图尽快攻击,就像一个新手,如果对手用正确的防守进行抵抗,这可能会很快变成对 AI 的一个强大的劣势,因为这种贪婪行为的结果是一个重要的棋子可能会被提前捕获。
最终考虑
我最初的挑战是建立我自己的国际象棋游戏,有一个漂亮的用户界面和击败我的能力,我认为公平地说这个目标已经实现了。
我对最终的结果很满意,尽管我相信未来还有很多地方需要改进。我特别感兴趣的是开发一个深度学习的人工智能,并让他们一决雌雄。
资源
- GitHub 上的 Bobby—代码库
- https://www.bobby-chess.com/bobby/—在我的象棋引擎上玩在线游戏
- 在不改变代码的情况下,将一个 Java Swing 应用程序增强为一个干净、优雅的 Web 应用程序——我的象棋引擎技术方面的另一篇文章
- 国际象棋编程维基 —为国际象棋引擎解释理论和算法
- alpha zero——一个机器学习象棋引擎
- Unicode 中的国际象棋符号 —国际象棋棋子的字符
- 象棋/运动 —维基百科关于象棋的页面
从头开始实现连通分量标记算法
原文:https://towardsdatascience.com/implementing-a-connected-component-labeling-algorithm-from-scratch-94e1636554f?source=collection_archive---------9-----------------------
我将向你展示如何在 MATLAB 中实现这个算法
Guillaume Bourdages 在 Unsplash 拍摄的照片
目录
- 什么是连通分量标记?
- 一个例子的可视化
- MATLAB 中的代码
什么是连通分量标记?
这是一种计算机视觉算法,用于检测和计数二进制图像中相连区域的数量,也称为斑点。通常,这是在分割算法之后完成的。得到的分割(二进制)掩模然后通过连通分量标记算法运行,以计算不同区域的数量。
目标
该算法的输入是二值图像。目标是用相同的唯一标签标记每个连接的组件(或 blob)。因为每个斑点都会被标记,所以我们可以推断出单个斑点的总数。需要注意的是,输出会因您使用的表示/连接而有所不同。
不同的连接性
有两种常见的方法来定义组件是否连接。一种是说一个像素只有 4 个邻居(有时称为 4-连通性)。另一种是说明一个像素有 8 个邻居。参见图 1。
图一。左图:4 邻居表示法。右图:8 邻居表示法。
样品
让我们看一个这个算法做什么的例子。注意两种表示之间的不同结果(图 2 和图 3)。
图二。假设采用4-连通性表示的算法结果。在这种情况下,“2”不是“3”的邻居。
图 3。假设一个8-连通性表示的算法的结果。“2”是邻居。
算法是如何工作的?
该算法包括两次通过图像中的每个像素。
第一遍:
对于每个非零像素,我们检查它的邻居。
- 如果它没有非零邻居——我们知道它是一个新组件——那么我们给它一个新标签。
- 如果它有一个非零邻居——这些像素是相连的——我们给它与邻居相同的标签。
- 如果它有不止一个非零邻居,有两种情况:
- 邻居都有相同的标签。所以我们给当前像素同样的标签。
- 邻居有不同的标签。这是棘手的部分。我们知道所有这些像素现在都是相连的,所以标签应该都是一样的。我们如下解决这个问题。首先,我们将当前像素设置为邻居的最低标签。然后我们记下所有相连标签的等价性,也就是说,哪些不同的标签实际上应该是相同的。我们将在第二遍中解决这些问题!
每个非零像素现在将有一个标签。然而,一些连接的区域将具有不同的标签。所以我们需要再次检查图像来纠正这一点。我们通过使用等价类的记录来做到这一点:所有等价的标签(即,指代相同的斑点)将获得相同的标签。
第二遍:
对于每个带标签的像素:
- 检查此标签是否有等价标签,并解决它们。有几种方法可以解决这个问题。关于我的实现,请参见 MATLAB 部分。
逐步可视化
让我们来看一个算法做什么的详细例子。这个例子使用了一个8-连通性表示法。
第一关。注意,图像在边框周围有一个填充。这只是为了使计算变得简单一点。通过填充,您在每个像素处考虑的窗口可以保持一致的大小。正如你所看到的,在每个像素上,它只考虑它左边的像素,以及它上面的三个像素。它不考虑所有的 8 个邻居,因为这样你就不必在整个过程中多次比较相同的像素对。
算法的第一遍。由 Dokyoung Kim 为本博客制作
第二遍。填充被移除,因为我们在这里不需要它。它检查每个像素,检查等价列表,并在必要时更新标签。
算法的第二遍。由 Dokyoung Kim 为本博客制作
MATLAB 中的代码
以下代码是该算法在 MATLAB 中使用 8-连通性表示的实现。通过仅考虑其左侧和上方的像素,可以容易地将其改变为 4-连通性表示。
MATLAB 中的代码。
我将解释每一部分。
第 1 部分:我们读取二进制图像,并给它一个零填充(大小=1)。
第 2 部分:这是算法的第一遍。它循环遍历每个像素并给它一个标签;它还记录等同物。这些等价关系记录在一个散列表中,这样可以快速方便地查找标签。
第 3 部分:散列表需要一些重组,所以第二遍会更容易。散列表产生如下结果:
5 -> 6 - 7
6 -> 8
我们重组了散列表,这样它会产生如下结果:
8 -> 5
7 -> 5
6 -> 5
第 4 部分:我们可以删除我们的填充,因为我们不再需要它。
第 5 部分:现在我们可以做第二遍。我们检查每个非零像素。如果它是散列表中的一个键,我们只需查看值就可以知道它应该是哪个标签。
现在我们完成了,我们返回斑点的数量和带标签的图像。
备注:
- 填充图像的 MATLAB 代码可以在这里找到。运行示例(GIF)的脚本可以在这里找到。
- Matlab 还有一个内置的函数来做连通分量标注。
为云实施非接触式按需触发
原文:https://towardsdatascience.com/implementing-a-contactless-on-demand-trigger-for-the-cloud-656bcb6495eb?source=collection_archive---------75-----------------------
照片由lÿv·贾恩在 Unsplash 拍摄
使用电子邮件让人们远离你的基础设施
在这篇博文中,我将向您展示 AWS 上按需触发器的一种可能实现。为了让你能够修补架构,我在 GitHub 上公布了代码来重现例子。我还在整篇文章中包含了代码片段,以帮助您了解实现细节。
注意:这种方法并不是蓝图,更不是最佳实践架构。相反,它旨在提供一些关于云的可能性以及实际实现情况的见解。建造它也很有趣。
即将到来的模式涵盖了两个主要的功能需求:
- 我们的客户 希望在他们认为有必要时触发计算。然而,他们拒绝等待人工支持,既不是联系人的形式,也不是售票系统。另一方面,我们绝对无意让他们直接访问我们宝贵的云。因此,我们需要实现一种允许自动触发而无需访问实际基础设施的机制。
- 此外,我们希望每次客户触发计算时都能得到通知。根据经验,当人们使用我们的产品时,问题往往会出现,如果有必要,我们希望做好灭火的准备。
在这篇博文中,我展示了一个使用电子邮件端点来满足这两种需求的解决方案。客户可以向 AWS 管理的地址写一条消息,然后触发计算。让我给你看看细节。
注意:在 AWS 中验证一个域超出了本文的范围,但是需要使用 AWS 电子邮件服务。有关如何操作的信息,请参见此处的和此处的和。
整体架构
为了满足需求,我们需要实现三个功能:
- 我们需要实现一个接收邮件的电子邮件地址。
- 我们需要一个协调机制,它对收到的邮件做出反应,并触发所需的计算。我们使用相同的机制来满足监控需求。
- 我们需要一个实际的计算发生的地方。
这些功能都转化为 AWS 生态系统中的一项服务:
- 简单电子邮件服务(SES) 接收并转发来自外部的邮件。为此,我们需要实现一个合适的规则。
- 简单通知服务(SNS)主题接受转发的邮件并继续传递。由于 SNS 向所有订阅者推送消息,我们不需要实现轮询机制。
- Lambda 的无服务器函数处理我们想要用我们的架构触发的任何事情。
以下是这种简单架构的图形概述:
接收端和处理逻辑通过 SNS 主题连接。SNS 主题通过发布/订阅模式工作。即 SES 将收到的邮件发布到 SNS 主题;Lambda 函数通过订阅自动接收它们。
在这篇博文中,我展示了在 Terraform 中的实现,但是你也可以通过 web 接口或者 CloudFormation 来实现这个架构。如果您以前没有使用 Terraform 的经验,请查看他们的介绍材料,或者忽略细节。请注意,当您使用 web 界面时,AWS 会在后台配置一些细节。这很方便,但是一旦需要调试基础设施,可能会有问题。
实施细节
在开始定义资源之前,我们需要向 Terraform 提供提供者的详细信息。也就是说,我们需要定义一个轮廓和一个区域。在撰写本文时,只有三个地区允许通过 SES 接收电子邮件。
我还为这个脚本定义了两个局部变量: common_tags 和 open_email 。 open_email 是稍后可用于触发计算的那个。这些主要是为了方便和清晰。
让我们深入研究我们架构的三个服务。
简单电子邮件服务
让 SES 工作的两个必要组件是一个规则集和一个规则。请将规则集视为系统使用的所有规则的总称。作为旁注,确保规则集是活动的。根据您的设置,可能已经有了默认的规则集。Terraform 有两个不同的资源,所以请确保使用正确的资源,即名称中带有活动的资源。
规则定义接收电子邮件地址、它所属的规则集以及它触发的操作类型。在我们的例子中,我们使用一个 SNS 动作,它将邮件转发给一个 SNS 主题。
还有 Lambda 操作,我们可以用它来创建一个直接链接,而不需要使用 SNS。然而,这些动作不接收消息体,只接收消息头。如果你需要身体,或者如果你预计在某个时候你需要它,SNS 行动是正确的选择。
正如您可以从位置参数中推断出的,在一个规则中应用几个不同的动作是可能的。
简单通知服务(SNS)
我们的设计使用两个 SNS 主题。SES 使用第一个来发布收到的电子邮件。第二个通知产品团队谁触发了新的计算。创建它们的唯一必要参数是主题的名称:
SNS 在设计上是非常轻量级的。也就是说,配置工作主要发生在发布或订阅其主题的服务上。
注意:您不能使用 Terraform 订阅 SNS 主题的电子邮件地址,因为它们必须经过验证(详情)。
希腊字母的第 11 个
对于我们的目的,单个λ函数就足够了。无服务器功能在计算能力方面有限制,所以您可以考虑切换到其他选项。但是,使用它们作为工作流的入口点通常是一种好的做法。考虑到这一点,让我们来看看启动和运行该功能的不同部分。
我们以 ZIP 文件的形式为该功能提供了代码。幸运的是,Terraform 有一个数据类型来涵盖这一点。我们所要做的就是定义源文件和目标文件。我在 GitHub repo 中提供了一个基本函数。
该功能还需要一个角色和一个附属策略才能工作。在这个例子中,我们希望它向 CloudWatch 写入日志,并向一个 SNS 主题发布信息,以通知产品团队。正如您在代码中看到的,将策略附加到角色是一个单独的资源。该策略需要涵盖 Lambda 函数完成工作所需的一切。例如,如果您想将信息写入 S3 存储桶,您需要扩展策略。
函数资源的规范看起来相当吓人。让我依次向您介绍每个参数:
- 文件名指向包含代码的 ZIP 文件。
- Terraform 比较源代码散列来检测代码库的变化。幸运的是,数据资源自动提供了这个散列。
- 处理程序是 Lambda 函数在被触发时执行的代码中的函数名。这里的是关于 Python 的信息,但是文档也涵盖了 Node.js、Ruby、Java 等等。
- 角色是我们上面定义的执行角色。
- 运行时告诉 AWS 你的代码想要使用哪种编程语言和版本标识符所有可用的运行时都在官方文档中列出。
- 环境变量为你的函数代码提供额外的信息。在我们的示例中,我们使用这样一个变量来传递 SNS 主题的 ARN 进行监控。
所有这些参数的组合如下所示:
剩下的就是触发 SNS 主题和 Lambda 函数之间的连接。要建立这个链接,我们需要实现两个资源:SNS 执行 Lambda 的权限和对 SNS 主题的 Lambda 函数的一个订阅。权限是允许 Lambda 订阅 SNS 主题的先决条件。请将许可视为一种特殊类型的策略。
权限是一个独特的资源,在 Terraform 中是这样的:
另一方面,订阅是一个通用资源,我们定义它应用于 Lambda。也就是说,我们将 Lambda 函数配置为其端点 ,并相应地调整协议:
这些资源涵盖了整个架构。你可以从这里克隆必要的代码。如果你已经在 AWS 注册了一个域名,启动一切需要几秒钟。现在,您可以通过向指定地址发送电子邮件来开始触发流程。确保事后拆除所有东西以避免成本。
如果您正在寻找如何在 AWS 上实现架构的其他示例,您可能会对我以前关于无服务器 Fargate 架构的博客文章感兴趣:
- 全面了解提议的架构及其动机。
- 详细看看它的联网方面。
- 详细了解 IAM 角色和涉及的策略。
- 详细看看实现的服务。
请在评论中告诉我你的想法和经历。我也很乐意在 Twitter 和 LinkedIn 上联系。感谢您的阅读!
为商业智能实现数据湖或数据仓库架构?
原文:https://towardsdatascience.com/implementing-a-data-lake-architecture-for-business-intelligence-f2c99551db1a?source=collection_archive---------4-----------------------
解释什么是商业智能,并比较用于商业智能的数据仓库和数据湖架构。
1。什么是商业智能?
作者插图基于塞巴斯蒂安·赫尔曼在 Unsplash 上的照片
在数据行业工作了一段时间后,我觉得每个人对商业智能都有不同的理解。然而,用简单的语言来说,商业智能实际上是利用昨天和今天的数据来对明天做出更好的决策。它可以被理解为确保原始数据被转换成有意义的信息的功能,这些信息提供了洞察力并使决策成为可能。如果我们想一想公司长期以来一直试图做的事情,我们经常会听到投资于技术和工具,这些技术和工具本应通过数据和分析来解决业务问题。然而,商业智能不仅仅是技术和工具。BI 的最终目标是使用数据、技术和工具来创造商业洞察力。
简而言之,BI 涉及收集功能性业务需求,并通过设计数据模型、执行 ETL(提取、转换和加载流程)将它们转化为技术解决方案,从而将来自运营源系统的原始数据转换为有意义的信息,并将该信息带到分析/目标数据库,该数据库可用于以实时自动仪表板的形式进行可视化。在企业中,这是为了根据过去的数据而不是“直觉”做出明智的决策。
得到💬任何数据科学或编程问题的 GPT 式答案。为成千上万的人生成摘要和学习笔记📚只需一次点击即可获得学习资源。👉
[## 面向数据科学家和开发人员的免费学习资源。精选的博客、教程、书籍和…
机器学习和人工智能工程师的培训课程、黑客马拉松、活动和工作
aigents.co](https://aigents.co/learn)
BI 实现的最终输出
毕提供了真相的单一版本
单一版本真理的故事是一个数据字典和数据源的故事,它是关于数据的来源和意义的,应该在整个企业中达成一致。我给你举个例子。我曾经为一家银行工作,这家银行无法回答一些简单的问题,比如:它有多少独立客户?根据您询问的对象,您会收到不同的号码。因此,拥有一个独特的真理来源是必须的。BI 产品为用户提供了实时、自动更新且一致的报告,而不是查看多个编号不同的电子表格并进行对账。
BI 提供描述性分析 —商业智能是描述性的,因为它告诉你现在发生了什么,过去发生了什么。它显示了公司在设定的 KPI 和指标方面的表现。例如,BI 产品可以告诉经理公司的销售情况以及距离既定目标还有多远。这些信息通常以仪表盘的形式提供,包括条形图、折线图等,让用户一眼就能看到最重要的信息。
BI 提供诊断分析— 诊断分析是关于提供深入的见解和回答问题:为什么会发生什么?BI 仪表板提供了深入查看功能,这意味着,从高层次的角度来看,用户可以从不同的角度对信息进行细分,以找出事情发生的原因。
Bi 不会告诉你未来会发生什么。
四种类型的数据分析。来源原理
2。数据仓库、数据湖:写模式和读模式
读取模式与写入模式。来源:intrinity—Hadoop 和 SQL 对比。
数据仓库架构师
商业智能是一个通常与数据仓库联系在一起的术语。如果 BI 是前端,那么数据仓库系统就是后端或实现商业智能的基础设施。因此,我们将首先在使用数据仓库视角的上下文中讨论 BI。
BI 实施的最终目的是将运营数据转化为有意义的信息。原始数据分散在不同的操作数据库中,这些数据库是为应用程序的运行而设计和优化的,而不是用于分析目的。有时,为了得到一个数据字段,您必须进行十次连接!开始了。人们提出了一种叫做中央数据存储的解决方案——数据仓库。
数据仓库解决方案出现于 20 世纪 80 年代,它是为提供信息或见解而优化的。数据仓库是一个目标数据库,它集中了来自所有源系统的企业数据。它是一个关系数据库,因为我们可以使用所谓的物理数据模型中的联合字段连接不同表中的数据。数据库模式定义了不同表之间的关系。典型的 SQL 数据库包括 MySQL 和 PostgreSQL。
数据源通过一个 ETL 过程连接到数据仓库,这个过程被称为提取、转换和加载。 数据仓库遵循写模式,其中的设计符合预期问题的答案。换句话说,数据仓库收集来自具有预定结构和模式的主要应用程序的数据。在数据仓库体系结构中,当我们将数据从数据库 A 转移到数据库 B 时,我们需要预先了解数据库 B 的结构以及如何调整数据库 A 的数据以适应数据库 B 的结构,例如了解数据库 B 中字段的命名约定和数据类型等。
三层数据仓库架构
数据仓库工程师可以使用各种架构来构建数据仓库。常见的数据仓库体系结构基于分层方法。Inmon 推出的众所周知的三层架构包括以下组件:
中转区/登陆区: 这是一个用于从源系统加载批量数据的数据库。其目的是从源系统/主要应用程序中提取源数据,以减少操作系统的工作量。临时区域由反映源系统结构的表组成,包括源系统的所有表和列,包括主键。这里没有应用功能性业务规则。但是,在这一阶段会实施一些硬的技术业务规则,例如,数据类型匹配(字符串长度或 Unicode 字符)。这些技术业务规则并没有改变数据的含义,而只是改变了数据的存储方式。
数据仓库层: 这是应用功能性业务规则的转换发生的地方。这些功能性业务规则修改传入的数据以适应业务需求。业务规则在数据仓库体系结构中实现得越早,它对数据仓库上层的依赖性就越强。在数据仓库建模中,您会经常听到维度建模或数据仓库建模。
数据仓库建模技术是由 Dan Linstedlt 在 20 世纪 90 年代发明的。它基于三种基本的实体类型,包括集线器、链路和卫星。hub 是 Data Vault 模型的主要支柱,它提供了业务用户用来识别业务对象的业务键。商业密钥的例子包括客户标识符、产品标识符、员工工号等。链接将中心联系在一起,并存储不同中心(业务对象)之间的关系。卫星被认为是存储元数据的标准场所。除此之外,卫星存储描述业务对象或关系的所有属性。他们将一段时间内的业务上下文添加到集线器和链路中。然而,这种商业环境会随着时间的推移而变化,拥有卫星的目的也是为了跟踪这些变化并存储历史数据。
在数据仓库之上, 可以有一个数据集市层——用于向最终用户呈现数据的层。我认为称之为信息集市比称之为数据集市更有意义。数据集市的一种常见建模技术是维度建模。维度建模的关键概念包括事实实体和维度实体,这是构建信息集市的标准技术,因为最终用户很容易理解。在维度建模中,您会经常听到星型模式,即一个事实表引用任意数量的维度表。事实表表示发生的事情、订单和事务,同时,维度实体包含构成事实的不同属性/字段。
来源:“什么是湖畔小屋?”通过数据块
加入 中等会员 计划继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入媒体——蓝初
阅读兰楚的每一个故事(以及媒体上成千上万的其他作家)。你的会员费直接支持兰初…
huonglanchu.medium.com](https://huonglanchu.medium.com/membership)
数据湖架构
因为进入数据仓库的数据在存储之前需要经过严格的治理过程,所以向数据仓库添加新的数据元素意味着改变数据的设计、实现或重构结构化存储以及相应的 ETL 来加载数据。由于数据量巨大,这一过程可能需要大量的时间和资源。这就是数据湖概念出现的原因,它将改变大数据管理的游戏规则。
数据湖的概念出现在 2010 年代,用简单的语言来说,就是所有企业的结构化、非结构化和半结构化数据可以而且应该存储在同一个地方。Apache Hadoop 是数据基础设施的一个例子,它允许存储和处理大量结构化和非结构化数据;这实现了数据湖体系结构。
DWH 和数据湖架构的示例。作者根据 MS Azure 文档和 Daniel Linstedt 的 b ook 的插图。
数据湖有一种读取模式的方法。它存储原始数据,并以不需要首先定义数据结构和模式的方式进行设置。换句话说,当我们将数据移动到数据湖时,我们只是在没有任何把关规则的情况下引入数据,当我们需要读取数据时,我们将规则应用于读取数据的代码,而不是提前配置数据的结构。与数据仓库中典型的提取、转换和加载不同,在数据湖的世界中,该过程是提取、加载和转换。数据湖用于成本效率和探索目的。因此,数据湖体系结构使企业不仅能够从经过处理和治理的数据中获得洞察力,还能够从以前无法用于分析的原始数据中获得洞察力。由此,原始数据探索可能会引发业务问题。然而,数据湖最大的问题是,如果没有适当的治理,数据湖会很快变成无法管理的数据沼泽。换句话说,如果不知道湖里的水是怎样的,谁会想去湖里游泳呢?如果业务用户不信任数据湖的数据质量,他们就不能利用数据湖。
来源:Datavercity
最近出现了这样一种趋势,即公司希望以更保守的方式从数据湖架构中获益。这些公司正在摆脱不受监管的“自由进入”方法,转而开发一个更受监管的数据湖。
数据湖可以包含两种环境:勘探/开发环境和生产环境。数据将被探索、清理和转换,以便构建机器学习模型、构建函数和其他分析目的。由转换过程生成的数据(如度量和函数)将存储在数据湖的生产部分。
另一个趋势是,治理数据湖只允许“经过验证的”数据进入,而不是将所有原始数据都倒入湖中。从本质上讲,受治理的数据湖体系结构不限制存储在其中的数据类型,这意味着受治理的数据湖仍然包含多种数据类型,包括非结构化和半结构化数据,如 XML、JSON、CSV。然而,关键是要确保没有数据存储在湖中,而没有在业务术语表中描述和记录,这将使用户对数据的质量和意义有一些信心。
为了提供这一层的治理,必须有一个业务术语表工具来记录数据的含义。更重要的是,需要有一个围绕这一点的治理过程——这完全是关于角色和职责的,例如,谁拥有数据,谁定义数据,以及谁将对任何数据质量问题负责。采用这种方法将非常耗时,因为定义数据本身可能是一个漫长的过程,因为它涉及到企业中不同学科的人员。
数据湖和数据仓库的比较。作者插图。
3。内部部署和云服务
在过去,公司通常依靠自有主机数据中心。在数据处理期间,必须处理大量数据,并且数据处理同时在一组机器上运行,而不是在一台计算机上运行。这个内部存储设置中心需要大量的服务器和大型计算机,以及足够的处理能力来应对高峰时刻。这也意味着,在较安静的时候,大部分处理能力都没有被使用。因此,内部部署系统具有巨大的前期安装成本,但同时,其容量有时只能得到充分利用。不仅如此,维护这一点需要整个专门的应用程序团队来照顾这些主要系统,解决生产问题,并减少停机时间。
imgix 在 Unsplash 上的照片
本地存储的这些巨大缺点使得使用云的想法变得如此吸引人。许多公司已经将云作为成本优化的一种方式。例如,使用 Azure 和 AWS 在云上构建数据基础设施的兴起完全改变了企业的大数据能力。公司无需在数据中心维护 CPU 和存储,只需租用云资源,例如在需要时租用所需的存储。云提供商负责维护底层基础设施。当云服务用户使用完这些资源后,他们可以归还这些资源,并且只为他们使用的资源付费。这使得扩展变得非常容易,因为如果速度变慢,公司可以分配更多的虚拟机来处理数据。有了云存储,企业不必投资新机器或基础设施,也不必更换老化的服务器。使用云存储的另一个原因是数据库的可靠性。例如,在最坏的情况下,您的数据中心可能会发生火灾。为了安全起见,您需要在不同的地理位置复制您的数据。这带来了一系列的物流问题。基于这些需求,专门从事这类服务的公司诞生了。我们称他们为“云服务提供商”。目前云服务提供商市场有三大巨头。首先也是最重要的,亚马逊网络服务(AWS)是市场领导者,在 2018 年占据了 32%的市场份额。位居第二的是拥有 17% 市场份额的微软 Azure,第三受欢迎的是拥有 10% 市场份额的谷歌云服务。
这些云服务提供商能提供什么?
有很多云服务都有不同的使用案例。最简单的服务是存储,它允许云服务用户上传所有类型的文件到云中。存储服务通常很便宜,因为除了可靠地存储文件之外,它们不提供太多功能。AWS 将 S3 托管为存储服务。Azure 有 Blob 存储,google 有云存储。第二个服务是计算服务,它允许你在云上执行计算。本质上,你可以启动一个虚拟机,并随意使用它。例如,计算服务通常用于托管 web 服务器和执行计算。AWS 提供 EC2 作为计算服务,微软 Azure 使用虚拟机,谷歌有计算引擎。云服务提供商也托管数据库。例如,公司可以通过将他们现有的应用程序转移到运行在 Azure 上的虚拟机来开始探索云。云服务的其他用例是人工智能和机器学习。例如,Azure 提供 Azure 机器学习服务,这是一个基于云的环境,可以用来开发、训练、测试和部署机器学习模型。
4。对于 BI 来说,数据湖可以取代数据仓库吗?
毫无疑问,数据湖比数据仓库有多种优势,特别是在处理大数据和成本效率方面。然而,如果不了解数据湖中的数据质量,业务用户就无法信任这些数据。许多数据科学家通常没有意识到从多个来源获取原始数据是准备好的——干净和高质量的建模通常需要花费 80%的时间。与此同时,不能忽视提供受治理的高质量数据的数据仓库的体系结构和原则。对于定义企业经营状况的一套核心 KPI 仍然有着强烈而持久的需求,这些 KPI 也是报告(尤其是监管报告)所需要的。这种信息需要高度管控的信息。
归根结底,关于公司希望通过数据和技术实现其商业战略的最关键问题必须非常清楚。例如,如果一家公司在其业务和数据战略中不打算涉足数据科学,那么投资允许人工智能和机器学习发生的数据科学/分析平台就没有意义。或者公司可以在一个业务术语表平台中管理他们所有的数据,然后用直升机将它们送入数据湖,但如果他们不知道为什么,我怀疑这是否会创造价值。
感谢您的阅读。我希望这个高层次的概述能帮助您更多地了解 BI 及其背后的架构。
参考资料:
[1]使用 Data Vault 2.0 构建可扩展的数据仓库
[2] 1keydata —数据仓库概念
[3]https://databricks . com/blog/2020/01/30/what-is-a-data-lake house . html
[4].由黄家仪在媒体上发布。
[5].让数据湖发挥作用。
通过链接 Python 迭代器实现数据管道
原文:https://towardsdatascience.com/implementing-a-data-pipeline-by-chaining-python-iterators-b5887c2f0ec6?source=collection_archive---------23-----------------------
干净、快速、可扩展和简单的数据处理设计模式
图片来源于罗迪昂·库察耶夫https://unsplash.com/photos/xNdPWGJ6UCQ
在本文中,我将讨论如何通过几个步骤以相对高效和灵活的方式处理 Python 中的项目集合,同时保持代码的整洁。我们将通过创建迭代器来支持其他迭代器,以此类推,从而创建迭代器链。
如果你读过四人帮的书或类似的材料,你可能已经发现了迭代器设计模式。迭代器只是一个带有方法的对象,允许客户端代码使用它来遍历一个项目集合。迭代器的美妙之处在于它们封装(隐藏)了集合的本质。集合可以是数据库表上的一组行,或者是一个复杂的图形模型上的节点,或者只是内存数组中的元素,但是迭代器的用户并不知道它。他们只需要知道他们可以一个接一个地获得元素,直到集合用尽。
Python 中的迭代器和生成器
Python 对迭代器有很好的支持,为了理解它是如何工作的,让我们讨论几个概念。
首先,Python 中的迭代器是具有 next 方法的任何对象,该方法返回集合的下一个元素,直到集合结束,之后,每次调用时都会引发 StopIteration 异常。出于教学目的,让我们做一个非常简单的迭代器
然而,如果你试图在 for 循环中使用这个迭代器,你会得到一个“类型错误:‘我的迭代器’对象是不可迭代的”。这是因为 for 循环需要 iterables ,它们是带有 iter 方法的对象,该方法返回迭代器。大多数集合(列表、字典、集合等。)都是可迭代的。它们不是迭代器(实际上,对它们调用 next()会抛出一个错误),但是它们可以创建一个迭代器,这样客户机代码就可以遍历它们。在我们的迭代器中,只需添加一个返回 self 的 iter 方法就可以使它成为可迭代的。
但是迭代器并不都是这样。另一个很棒的 Python 语法是生成器。生成器是一种迭代器,可以像函数一样创建,只需用 yield 替换 return。让我们看看我们的迭代器示例如何作为一个生成器来翻译
生成器也是可迭代的,可以直接在 for 循环中使用。需要记住的重要一点是,任何地方任何具有单一产出的函数(或方法)在被调用时都不会做任何事情。我的意思是,函数中没有一行代码会被执行。相反,该函数将创建并返回生成器对象。只有当你开始迭代生成器时,代码才会被执行。当您在生成器上调用 next 时,代码将一直执行到第一个 yield。然后它会返回收益率上的值并停止。保存状态,因此 next time:)执行将在该 yield 之后立即恢复,并在到达下一个 yield 语句时停止。与 return 不同,你可以将一个收益率写在另一个收益率之下,两个收益率都会达到。最后,当生成器到达代码末尾时,它将引发 StopIteration 异常。如果 return 语句到达末尾,返回值将包含在异常中,不会被输出。
数据管道
假设我们有一个需要进行一些处理的项目集合。现在,如果处理非常简单,你可以用一个方法完成,使用一些辅助方法等等。然而,在一个地方做所有的事情是一个非常糟糕的软件设计方法,这就是你如何得到有数百行代码的功能,当你不得不阅读它们时,你会想哭
避免这种痛苦的一种方法是让项目通过一个简单的管道。在一个简单的管道中,每个方法接收一个项目,对其进行一些更改,然后返回转换后的项目。每种方法都可以保持简短。此外,它们共享相同的接口(item in,item out ),并且不需要知道它们在管道中的位置,或者在它们到达方法之前发生了什么。这允许您改变转换的顺序,或者甚至客户端可以在管道上挂钩新的方法(向开闭原则致敬)。
定量
我们刚刚介绍的简单管道的一个限制是,转换是在逐项的基础上进行的。但是,许多操作通过对批处理进行操作来提高效率。例如,假设一个远程 API 调用为您提供了处理项目所需的信息。如果 API 提供一个批量端点来发送一组项目并接收一组结果,这将比逐项调用 API 快得多。想想发送 HTTP 请求和等待响应需要多长时间,然后才能继续处理下一项。在我们的简单管道中,我们不能处理批处理,因为该方法接收单个项目,并且必须立即返回转换后的项目。
解决这个问题的一个方法是让每个转换接收一个批处理并返回一个批处理。问题是最方便的批量可能因方法而异。方法 A 可能更适合 500 个批次的产品,但方法 B 只能吞下 100 个。此外,通过将批处理强加给每个人,简单的方法(如正则表达式替换)将变得更加复杂,因为必须解包输入批处理,完成它们的工作,然后组装输出批处理。
这就是为什么迭代器链在这种情况下如此方便。在迭代器链中,每个转换接收一个 iterable 项,并返回一个 iterable 转换项。我们将看到这种模式是如何在保持代码整洁的同时实现批处理的。
让我们从一个现实生活中简单的例子开始。如果逐项读取,从远程数据库服务器读取是一项开销很大的操作。分批提取要快得多。在阅读该示例之前,您可能想知道我正在使用 psycopg 库游标来访问 PostgreSQL 数据库。另外,我假设有一个 format_item 函数将从表数据构建 item 对象。
如果你以前没见过,“yield from iterable”语句是“for element in iterable:yield element”的简称。这个例子函数是…你猜对了,一个生成器函数。它返回一个项目生成器。这个生成器的客户端将一个接一个地获取项目,不需要知道任何关于数据库、批量大小或任何细节的信息。如果你想改变批量大小,什么也不会发生(除了性能问题)。如果你想改变数据库软件,没有人需要知道,如果你甚至想从文件中读取项目,这一切都没问题。这就是迭代器的美妙之处。
你可能已经注意到了这个方法,它不是我描述的迭代器链式链接:它不接收 iterable,尽管它返回一个。这是因为它是数据链的第一环,是数据的原始来源。这个链的最后一个链接也是特殊的,它接收一个 iterable,但不返回一个。因此,让我们展示一个调用 REST API 的普通中间链接。我假设您有一个名为 get_api_results 的方法,它从 api 获取一批结果,并返回这批转换后的项目。
此方法接收一个项目生成器并返回另一个项目生成器。我敢打赌,在阅读代码之后,您可以看到批处理是如何工作的,但是没有一个八卦客户端会知道。
我们说过,链的最终方法也是特殊的。这将只是通过迭代直到耗尽来消耗生成器,并将结果写到某个地方(例如另一个 PostgreSQL DB)。这个方法上的批处理也是一样的。
同时,使用这种方法,不需要批处理的方法可以保持简单:
内存使用
我们之前没有提到它,但是迭代器管道在内存使用方面非常有效。为了理解这一点,你可以把你头脑中的管道描绘成层叠排列的水容器(就像喷泉一样)。上面有一个大水箱(DB 中的项目),下面有一个大水箱(另一个 DB 中的转换项目),水从一个容器流到另一个容器。每个容器的大小是生成器保存的批次。管道使用的内存大约是所有容器中水的总和。通常,这将比上面的大容器少很多,但是当容器真的很大时,这意味着加载所有的项目并在内存中处理它们会有天壤之别。
我想到了一个问题,您应该注意,如果您在一个中间转换中用尽了迭代器,例如,如果您编写了类似于 list(items) 的内容,那么所有的项都将在那个位置加载到内存中。看到许多项正在经历前面的转换,但没有超过那个点,直到所有项都被加载,只有这样它们才能继续向下游移动,这可能会令人困惑。你会在你的喷泉中间创造一个无限大的容器,一个吸啊吸,直到没有其他东西可吸,然后才打开让水继续流动的容器。
其他性能改进
显然,我们的迭代器管道还有很大的改进空间。管道可能会变得复杂,同时将功能分成简单的生成器。您可以想象如何链接生成器,甚至可以根据处理的结果交换它们。然而,最后我将只提到您可以添加的其他性能技巧。请记住,不必要的优化是编程中非常邪恶的根源。只有当性能成为问题时才进行优化,并始终确保优化对性能产生真正的、相当大的影响,否则就不值得。
选择
如果您经常对管道进行更改,尝试这种或那种方法来获得您想要的最终结果,一些繁重的操作可能会降低您的速度。也许你引入了一个新的字符串转换,它只影响 1%的项目,但是你每次运行都要处理所有的项目。一个简单的方法是使第一个生成器具有选择性,这样它只生成可能需要重新处理的项目。我的意思是,如果你知道如何选择这些项目。
缓存
假设您有同样的问题(管道上的更改只影响少数项目),但是您不知道如何选择这些项目。那么你可以使用缓存。在缓存中,您将保存繁重操作的结果,您不想白白运行这些操作。
首先,你需要一种方法来检查你的项目的平等性。一般来说,它可能是所有项目内容的散列。但是你可能有一个特别的,更好的解决方案。您可以在管道上使用较慢的方法,将输入项的散列与构建输出所需的数据存储在一起。当从 DB 中读取时,可以将缓存包含到项中,当项到达 slow 方法时,该方法可以检查该项的哈希是否与缓存的哈希相同。如果是,您可以使用缓存的结果,而不是执行缓慢的操作。
并行化(只是一瞥)
一些 Python 库提供了并行化迭代器的方法。老实说,我还没有认真尝试过,只是用一个名为 joblib 的库做了一点实验,这个库似乎拥有最简单的接口。像这样的代码应该在 6 个处理器上执行。试试看它是否适合你。
这种特殊方法的一个缺点是将所有的项都加载到内存中,所以如果您正在处理一个大的数据池,您可能需要对整个管道本身进行批处理。
埃尔芬!
您刚刚学习了使用迭代器在 Python 中实现数据管道的基础,恭喜您!!!同样,你设法应付我糟糕的写作风格,赞美你。
在这之后,你可能想学习更多关于 Python 中迭代器的知识,但是最重要的是,我邀请你来玩这个奇妙的语言特性,并自己犯一些错误。
为 CIFAR-10 数据集实现深度神经网络
原文:https://towardsdatascience.com/implementing-a-deep-neural-network-for-the-cifar-10-dataset-c6eb493008a5?source=collection_archive---------22-----------------------
使用 PyTorch 和 CIFAR-10 数据集对初学者友好的图像分类
约书亚·索蒂诺在 Unsplash 上拍摄的照片
神经网络:它们是如何工作的?
神经网络是多功能模型,可以学习任何复杂的模式。这些强大的模型是由多层感知器、卷积网络、序列模型等组成的深度学习的核心。在这个简短的项目中,我将探索 CIFAR-10 数据集并实现一个简单的神经网络(多层感知器)。
神经网络的概念其实很简单。类似于神经元在人脑中如何激发或激活,神经网络中一层内的神经元通过激活函数被激活。这个过程返回输出,该输出将被传递到神经网络的下一层,并且循环重复,直到神经网络结束。这一过程被称为前向传递,即在应用权重和激活函数后,您的数据通过网络向前传递。根据您要解决的是回归问题还是分类问题,神经网络的最终输出层将为回归问题输出一个节点,为分类问题输出多个节点。在这个项目中,我们将对图像进行分类,因此神经网络的输出将在每个类中有一个节点,该节点将通过 softmax 函数获得最终预测。
神经网络的本质不是向前传递期间发生的事情,而是向前传递之后发生的事情。前向传递的最终输出值用于通过在网络中反向传递来更新神经网络的权重。这被称为反向传播,这是一种非常强大的算法,它从最后一层到第一层迭代地更新权重。当使用随机梯度下降优化器时,这是训练神经网络的极其有效的方式。
准备模型训练
如 CIFAR-10 信息页面所述,该数据集由 10 类 60,000 幅 32x32 彩色图像组成,每类 6,000 幅图像。有 50,000 个训练图像和 10,000 个测试图像。由于我们正在处理彩色图像,我们的数据将由基于 RGB 比例分割的数值组成。
来自 CIFAR-10 数据集的样本图像
对于这个项目,我们将使用数据集的 10%作为验证集,90%作为训练集。损失函数将是交叉熵损失,因为这是一个分类问题。优化器将是随机梯度下降,梯度下降的批量将是 128。随机梯度下降是梯度下降的近似。损失函数的梯度被应用于一批所有的训练点,而不是整个集合,这样计算起来快得多。这种对训练样本的随机批量采样引入了大量噪声,实际上有助于防止算法陷入狭窄的局部极小值。
基于 GPU 的基本模型和训练
首先,我们为我们的神经网络创建基础模型,其中我们将为训练过程和验证过程定义函数。
图像分类基础模型
然后,我们将定义 evaluate 函数,以在每个时期后返回我们的模型的进度,并定义 fit 函数,用于更新每个时期的权重。
模型评估函数和拟合函数
由于我们使用 PyTorch,我们可以选择使用 GPU 来训练和评估我们的模型。GPU 在更新和计算权重方面效率更高,因为它们针对矩阵计算进行了优化,而不是 CPU。因此,如果数据可用,我们将把数据转移到 GPU。
将数据移动到 GPU
现在我们准备定义我们的神经网络。对于这个例子,我将为我的神经网络使用 4 个隐藏层,每个层的输入节点分别为 1536、768、384 和 128。正向传递函数将使用整流线性单元激活函数(ReLu ),这是一个将 max(x,0)应用于输入 x 的转换函数。该转换函数对于神经网络很流行,因为它不会激活层内的所有节点,从而使模型成为非线性的。
创建隐藏层和正向传递函数
最后,我们准备对模型进行训练和评估。初始化随机权重后,我们将迭代通过神经网络,并根据我们的学习速率(步长)和时期进行反向传播。这里的目标是获得最终使损失最小化的重量。我们将通过梯度下降来大步前进,直到我们收敛到更接近最小值。一旦我们收敛到更接近最小值,我们将采取更小的步长,直到我们达到尽可能低的最小值。
学习率为 0.1 时的 15 个时期
学习率为 0.01 时的 5 个时期
学习率为 0.001 时的 5 个时期
学习率为 0.0001 时的 5 个时期(损失似乎在这一点上收敛)
我们知道我们已经收敛到最小值,因为我们的精度没有进一步提高,我们的损失没有进一步减少。这是我们的神经网络能想到的最好的模型。这意味着我们的模型有一半以上的时间是正确的。这可能是因为我们的模型着眼于每个像素,而不是整个画面。当我们看图像时,如果我们只看一个像素,相对来说很难概括图像是什么。
损失与时代
准确度与纪元
现在我们知道我们已经得到了可能的最佳模型,让我们用测试集来测试它。
使用测试集进行评估
55%的准确率!这表明,为了找到数据集的最佳模型,选择正确的历元数和学习率是至关重要的,但在这种情况下,使用该模型对任何事物进行分类都是不实际的,因为精度非常低。如前所述,这很可能是因为模型正在逐个查看每个像素。这就是卷积网络的闪光点。这些网络是利用卷积层的深度神经网络,卷积层使用卷积滤波器来处理和产生图像。当我们查看完整的图片或图片的一部分时,人类可以更好地了解图像是什么,卷积网络能够查看图片的一部分,使其保留更多关于图像的信息,而不是查看像素。
参考
- CIFAR-10 数据集
- 我的 Jovian notebook
理解和实施全卷积网络(FCN)
原文:https://towardsdatascience.com/implementing-a-fully-convolutional-network-fcn-in-tensorflow-2-3c46fb61de3b?source=collection_archive---------1-----------------------
使用 Keras 在 TensorFlow 中构建、训练和部署小型灵活的 FCN 影像分类模型的教程
由大卫·特拉维斯在 Unsplash 上拍摄的照片
卷积神经网络(CNN)非常适合计算机视觉任务。使用在 ImageNet、COCO 等大型数据集上训练的预训练模型。我们可以快速专门化这些架构,使之适用于我们独特的数据集。这个过程被称为迁移学习。然而,有一个陷阱!用于图像分类和对象检测任务的预训练模型通常在固定的输入图像尺寸上训练。这些通常从224x224x3
到512x512x3
左右,并且大多具有 1 的纵横比,即图像的宽度和高度相等。如果它们不相等,那么图像被调整到相等的高度和宽度。
较新的架构确实具有处理可变输入图像大小的能力,但与图像分类任务相比,它更常见于对象检测和分割任务。最近,我遇到了一个有趣的用例,其中我有 5 个不同类别的图像,每个类别都有微小的差异。此外,图像的长宽比也比平时高。图像的平均高度约为 30 像素,宽度约为 300 像素。这是一个有趣的问题,原因如下:
- 调整图像大小很容易扭曲重要的特征
- 预先训练的架构非常庞大,并且总是过度适应数据集
- 这项任务要求低延迟
需要具有可变输入维度的 CNN
我尝试了 MobileNet 和 EfficientNet 的基本模型,但都不起作用。需要一种对输入图像大小没有任何限制并能立即执行图像分类任务的网络。首先打动我的是全卷积网络(fcn)。FCN 是一种不包含任何“密集”层(如传统 CNN)的网络,而是包含执行完全连接层(密集层)任务的 1x1 卷积。虽然密集层的缺失使得可变输入成为可能,但有两种技术可以让我们在珍惜可变输入维度的同时使用密集层。本教程描述了其中的一些技术。在本教程中,我们将经历以下步骤:
- 使用 Keras 在 TensorFlow 中构建全卷积网络(FCN)
- 下载和分割样本数据集
- 在 Keras 中创建一个生成器来加载和处理内存中的一批数据
- 用可变批量维度训练网络
- 使用 TensorFlow 服务部署模型
更新:从头开始建造和训练 FCN 时,你会遇到许多超参数。我写了另一篇文章,其中我给出了超参数优化的一个演练,包括数据扩充,使用了本文中讨论的相同的 FCN 架构。你可以在这里阅读。
[## 使用 Keras 和光线调节进行超参数调节
使用贝叶斯优化为机器学习模型选择最佳超参数的实用教程。
towardsdatascience.com](/hyperparameter-tuning-with-keras-and-ray-tune-1353e6586fda)
去拿圣经
在我的教程中,这里是上传到 GitHub 的项目链接。请克隆回购,并按照教程一步一步地更好地理解。注意:本文中的代码片段只突出了实际脚本的一部分,完整代码请参考 GitHub repo。
[## himanshurawlani/全卷积网络
该库中的代码是在使用 Python 3.6.7 的 Ubuntu 18.04.3 LTS 上开发和测试的。以下是软件包…
github.com](https://github.com/himanshurawlani/fully_convolutional_network.git)
1.设计发动机(model.py)
我们通过堆叠由 2D 卷积层(Conv2D
)和所需的正则化(Dropout
和BatchNormalization
)组成的卷积块来构建我们的 FCN 模型。正则化可以防止过度拟合,并有助于快速收敛。我们还添加了一个激活层来整合非线性。在 Keras 中,输入批次维度是自动添加的,我们不需要在输入层中指定它。由于输入图像的高度和宽度是可变的,我们将输入形状指定为(None, None, 3)
。3 代表我们图像中的通道数,对于彩色图像(RGB)来说是固定的。
最小图像尺寸要求
对输入应用卷积块后,输入的高度和宽度将根据kernel_size
和strides
的值减小。如果输入图像尺寸太小,那么我们可能达不到下一个卷积块所需的最小高度和宽度(应该大于或等于内核尺寸)。确定最小输入尺寸的试错法如下:
- 决定要堆叠的卷积块的数量
- 选择任意输入形状,比如说
(32, 32, 3)
,将卷积块与越来越多的通道进行堆叠 - 尝试建立模型并打印
model.summary()
以查看每层的输出形状。 - 确保从最后一个卷积块中得到
(1, 1, num_of_filters)
作为输出尺寸(这将输入到完全连接的层)。 - 尝试减少/增加输入形状、内核大小或步幅,以满足步骤 4 中的条件。满足条件的输入形状以及其他配置是网络所需的最小输入维度。
还有一种数学方法来计算输出体积的空间大小,作为输入体积的函数,如这里的所示。找到最小输入维度后,我们现在需要将最后一个卷积块的输出传递给完全连接的层。但是,任何维度大于最小输入维度的输入都需要汇集起来,以满足步骤 4 中的条件。我们知道如何用我们的主要原料做到这一点。
主要成分
完全连接的层(FC 层)将为我们执行分类任务。我们可以通过两种方式构建 FC 层:
- 致密层
- 1x1 卷积
如果我们想要使用密集层,那么模型输入尺寸必须是固定的,因为作为密集层输入的参数的数量必须被预定义以创建密集层。具体来说,我们希望最后一个卷积块输出的(height, width, num_of_filters)
中的高度和宽度为常数或 1。滤波器的数量总是固定的,因为这些值是由我们在每个卷积模块中定义的。
1x1 卷积的输入维度可以是(1, 1, num_of_filters)
或(height, width, num_of_filters)
,因为它们沿着num_of_filters
维度模拟 FC 层的功能。然而,在 1x1 卷积之后,对最后一层(Softmax 激活层)的输入必须是固定长度的(类的数量)。
主要成分:GlobalMaxPooling2D()/GlobalAveragePooling2D()。Keras 中的这些层将尺寸为(height, width, num_of_filters)
的输入转换为(1, 1, num_of_filters)
的输入,实质上是沿num_of_filters
尺寸的每个过滤器沿高度和宽度尺寸取最大值或平均值。
密集层与 1x1 卷积
代码包括密集层(注释掉)和 1x1 卷积。在使用这两种配置构建和训练了模型之后,以下是我的一些观察:
- 两种模型包含相同数量的可训练参数。
- 相似的训练和推理时间。
- 密集层比 1x1 卷积更容易概括。
第三点不能一概而论,因为它取决于数据集中的图像数量、使用的数据扩充、模型初始化等因素。然而,这些是我在实验中观察到的。您可以通过触发命令$python model.py
来独立运行脚本,以测试模型是否构建成功。
2.下载燃料(data.py)
本教程中使用的 flowers 数据集主要是为了理解我们在训练具有可变输入维度的模型时所面临的挑战。一些测试我们的 FCN 模型的有趣数据集可能来自医学成像领域,其中包含对图像分类至关重要的微观特征,以及其他包含几何图案/形状的数据集,这些图案/形状在调整图像大小后可能会扭曲。
提供的脚本(data.py)需要独立运行($python data.py
)。它将执行以下任务:
- 下载包含 5 个类别的花卉数据集(“雏菊”、“蒲公英”、“玫瑰”、“向日葵”、“郁金香”)。关于数据集的更多细节请点击。
- 将数据集拆分为定型集和验证集。您可以设置要复制到训练集和验证集中的图像数量。
- 给出数据集的统计数据,如图像的最小、平均和最大高度和宽度。
这个脚本下载.tar
文件,并使用keras.utils.get_file()
提取当前目录中的内容。如果你想使用 TensorFlow 数据集(TFDS ),你可以查看这篇教程,它展示了 TFDS 和数据扩充的用法。
3.专用化油器(generator.py)
我们希望在不同的输入维度上训练我们的模型。给定批次和跨批次的每个图像都有不同的尺寸。那么问题出在哪里?让我们后退一步,重新审视我们如何训练传统的图像分类器。在传统的图像分类器中,图像被调整到给定的维度,通过转换成 numpy 数组或张量打包成批,并且这批数据通过模型向前传播。指标(损失、准确性等。)在该批次中进行评估。基于这些度量来计算要反向传播的梯度。
我们不能调整图像的大小(因为我们会失去微观特征)。现在,由于我们不能调整图像的大小,将它们转换成 numpy 数组就变得不可能了。这是因为如果你有一个包含 10 幅尺寸为(height, width, 3)
的图像的列表,它们的height
和width
的值不同,并且你试图将它传递给np.array()
,那么得到的数组将是(10,)
的形状,而不是(10, height, width, 3)
!然而,我们的模型期望输入维度是后一种形状。解决此问题的方法是编写一个执行以下操作的自定义训练循环:
- 我们通过使用
np.expand_dims(img, axis=0)
将(height, width, 3)
转换为(1, height, width, 3)
来传递列表(批处理)中的每个图像。 - 在 python 列表(批处理)中累积每个图像的度量。
- 使用累积的指标计算损耗和梯度。将渐变更新应用于模型。
- 重置指标值,并创建新的图像列表(批次)。
我尝试了上述步骤,我的建议是不要采用上述策略。这很费力,导致复杂和不可持续的代码,并且运行非常慢!人人都爱优雅而经典的model.fit()``model.fit_generator()
。我们将在这里使用后者!但是首先,化油器。
化油器是一种将内燃机的空气和燃料以适当的空燃比混合以进行燃烧的装置。这就是我们需要的,空气!我们找到一批图像中最大的高度和宽度,然后每隔一个图像用零填充,这样批中的每个图像都有相等的尺寸。现在我们可以很容易地将其转换为 numpy 数组或张量,并将其传递给fit_generator()
。该模型自动学习忽略零(基本上是黑色像素),并从填充图像的预期部分学习特征。这样,我们就有了一批具有相同图像尺寸的图像,但每批都有不同的形状(由于不同批次图像的最大高度和宽度不同)。您可以使用$python generator.py
独立运行generator.py
文件,并交叉检查输出。
在 Keras 中创建生成器非常简单,这里有一个很好的入门教程。对generator.py
的一个很好的补充是包括对数据扩充的支持,你可以在这里获得一些灵感。
4.点火到认知(train.py)
训练脚本导入并实例化以下类:
- 生成器:我们需要指定由
data.py
创建的train
和val
目录的路径。 - FCN 模型:我们需要指定最终输出层所需的类的数量。
上述对象被传递给train()
函数,该函数使用 Adam 优化器和分类交叉熵损失函数编译模型。我们创建一个检查点回调来保存训练期间的最佳模型。基于在每个时期结束时对验证集计算的损失值来确定最佳模型。我们可以看到fit_generator()
函数在很大程度上简化了代码,令人赏心悦目。
我建议在 Google Colab 上进行训练,除非你的本地机器上有 GPU。GitHub repo 包括一个 Colab 笔记本,它将培训所需的所有内容放在一起。您可以在 Colab 本身中修改 python 脚本,并在您选择的数据集上训练不同的模型配置。完成培训后,您可以从 Colab 的“文件”选项卡下载最佳快照到您的本地机器。
5.使用 TensorFlow 服务部署模型(inference.py)
下载完模型后,您需要使用export_savedmodel.py
将其导出为 SavedModel 格式。在主函数中指定下载模型(.h5
文件)的路径,并使用命令$python export_savedmodel.py
执行脚本。该脚本使用 TensorFlow 2.0 中的新功能,从.h5
文件加载 Keras 模型,并将其保存为 TensorFlow SavedModel 格式。SavedModel 将被导出到脚本中指定的export_path
。TensorFlow 服务 docker 映像需要此 SavedModel。
要启动 TensorFlow 服务服务器,请转到导出 SavedModel 的目录(在本例中为./flower_classifier
)并运行以下命令(注意:您的计算机上必须安装 Docker):
*$ docker run --rm -t -p 8501:8501 -v "$(pwd):/models/flower_classifier" -e MODEL_NAME=flower_classifier --name flower_classifier tensorflow/serving*
上述命令执行以下步骤:
- 如果本地没有
tensorflow/serving
docker 图像,则提取该图像。 - “-p”标志将本地机器上的端口 8501 映射到 docker 容器中的端口 8501。
- “-v”标志将当前目录(由
$(pwd)
指定)装载到 docker 容器中的/models/flower_classifier
中。 - “-e”标志设置 docker 容器中的环境变量,TensorFlow 服务服务器使用该变量来创建 REST 端点。
- “-RM”标志在删除容器时删除与容器关联的任何匿名卷。
- “-t”显示当前终端中的容器日志。您可以按 CTRL+C 返回到您的终端,容器将继续在后台运行。
您可以使用$ docker ps
命令来验证您的容器是否在后台运行。您还可以使用$ docker logs your_container_id
查看容器日志。inference.py
脚本包含构建统一图像尺寸批次并将这些批次作为 POST 请求发送到 TensorFlow 服务服务器的代码。从服务器接收的输出在终端中被解码和打印。
梦想的传递
在本教程中,我们了解了以下内容:
- 为具有可变输入维数的图像分类建立一个标准的完全卷积网络。
- 用一批中相同的图像形状和不同的批形状训练 FCN 模型。
- 使用 TensorFlow 服务 docker 图像部署训练好的模型。
请注意,本教程仅介绍了机器学习工作流程中的单个组件。ML 管道由大量的特定于组织及其用例的训练、推理和监控周期组成。建立这些管道需要更深入地了解司机、乘客和车辆的路线。只有这样才有可能交付梦想的运输工具!
我希望这篇教程对你构建下一个令人敬畏的机器学习项目有所帮助。我很乐意听取您对资源库的建议和改进,也可以随时提出 GitHub 的问题。如果你发现文章中有任何错误或遗漏的信息,请在评论区告诉我。谢谢!
实现朴素贝叶斯分类器
原文:https://towardsdatascience.com/implementing-a-naive-bayes-classifier-f206805a95fd?source=collection_archive---------19-----------------------
在 Python 中使用真实世界的数据集
凯文·Ku 在 Unsplash 上的照片
在的监督学习(分类)的背景下,朴素贝叶斯或者更确切地说贝叶斯学习作为评估其他学习算法的黄金标准,同时作为一种强大的概率建模技术。
在这篇文章中,我们将讨论朴素贝叶斯分类器的工作原理,通过将它应用于真实世界的数据集,用 Python 实现。
T 岗位更宽泛地分为以下几个部分:
- 数据预处理
- 训练模型
- 预测结果
- 检查模型的性能
上述部分可以进一步划分如下:
→ 数据预处理
- 导入库
- 导入数据集
- 将数据集分成训练集和测试集
- 特征缩放
→训练模型
- 在训练集上训练朴素贝叶斯模型
→预测结果
- 预测测试集结果
→检查模型的性能
- 制作混淆矩阵
→可视化
- 可视化混淆矩阵
在我们开始深入研究代码本身之前,我们需要谈谈数据集本身。对于这个实现,我们将使用 这 20 个新闻组的文本数据集 。这个数据集是公开的,目的是人们可以学习和磨练他们的机器学习技能。
我们将使用 scikit-learn (sklearn)作为机器学习库和数据集本身的存储库。这就是 sklearn 的网站告诉你的数据集。
20 个新闻组数据集包含大约 18000 个关于 20 个主题的新闻组帖子,分为两个子集:一个用于培训(或开发),另一个用于测试(或性能评估)。训练集和测试集之间的划分基于在特定日期之前和之后发布的消息。
数据集特征:
https://scikit-learn . org/stable/datasets/index . html # news groups-dataset
注意:由于原始数据是自然语言文本,我们不能直接处理它。在开始处理数据之前,我们需要将数据转换成数字。有多种方法可以做到这一点,即:
计数矢量器
tfidf 矢量器
哈希矢量器
选择矢量器取决于解决方案的设计者和问题本身。出于本文的目的,我将使用 CountVectorizer。我已经为所有矢量器提供了 sklearn 文档的链接。你当然可以在那里和网上详细看看。
现在,我们已经谈了一点数据集,让我们从代码本身开始,一步一步来。
→ 数据预处理
- 导入库
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import confusion_matrix, plot_confusion_matrix
import matplotlib.pyplot as plt
上述库的使用/要求(按顺序解释)如下:
a)从 sklearn 本身导入数据集
b)导入计数矢量器以将原始自然语言文本转换成机器可理解的数字
c)导入朴素贝叶斯分类器,在这种情况下,我们使用高斯朴素贝叶斯
d)引入混淆矩阵方法来检查模型的性能并将其可视化。
e)用于混淆矩阵的可视化
2 & 3.导入数据集并将数据集分成训练集和测试集
data_train = fetch_20newsgroups(subset='train', categories=None,
remove=('headers', 'footers',
'quotes'))data_test = fetch_20newsgroups(subset='test', categories=None,
remove=('headers', 'footers',
'quotes'))X_train = data_train.data
y_train = data_train.targetX_test = data_test.data
y_test = data_test.target
data_train
包含来自数据本身的训练集。fetch_20newsgroups
中传递的参数可以理解为:
I)子集-定义训练或测试集
ii)类别—数据集包含 20 个类别或分类标签。通过在参数中提供以下类别的列表,可以使用数据集的子集:
from pprint import pprint
pprint(list(newsgroups_train.target_names))
输出是:
['alt .无神论',
'comp.graphics ',
'comp.os.ms-windows.misc ',
'comp.sys.ibm.pc.hardware ',
'comp.sys.mac.hardware ',
'comp.windows.x ',
'misc.forsale ',
'rec.autos ',
' rec . motors ',
'rec.sport.baseball ',
'rec
iii)移除—数据集中的文本包含页眉、页脚和引号,但我们希望对数据主体应用模型。
通过调用data_train
和data_test
上的.data
和.target
可以分离自变量和因变量,得到分叉的训练和测试数据。
类别/y_train
数组中的值表示上述类别列表的索引。
在应用 CountVectorizer 之前进行 X_train
4.特征缩放
vectorizer = CountVectorizer()
X_train = vectorizer.fit_transform(X_train)
X_test = vectorizer.transform(X_test)
创建 CountVectorizer 类的对象,然后在X_train
和X_test
数据上拟合矢量器对象。
应用计数矢量器后的 X_train
→训练模型
- 在训练集上训练朴素贝叶斯模型
classifier = GaussianNB()
classifier.fit(X_train.toarray(), y_train)
制作 GaussianNB 类的对象,然后根据X_train
和y_train
数据拟合分类器对象。这里的.toarray()
与X_train
用于将稀疏矩阵转换为密集矩阵。
→预测结果
- 预测测试集结果
y_pred = classifier.predict(X_test.toarray())
在分类器对象上调用.predict
方法,并传递X_test
来预测之前未见过的数据上的训练模型的结果。这里的.toarray()
与X_test
用于将稀疏矩阵转换为密集矩阵。
→检查模型的性能
- 制作混淆矩阵
cm = confusion_matrix(y_test, y_pred)
print(cm)
→可视化
- 可视化混淆矩阵
plot_confusion_matrix(classifier, X_test.toarray(), y_test, display_labels=['alt.atheism',
'comp.graphics',
'comp.os.ms-windows.misc',
'comp.sys.ibm.pc.hardware'], cmap=plt.cm.Blues)
为了形象化的目的,我把类别的数量限制在 4 个,这样它就能被正确地看到。
现在,机器学习模型的整个管道已经完成,我希望我能够分享一些知识。这是一个非常基本的机器学习管道,但当你想建立更好更复杂的机器学习模型时,它在建立基础方面相当重要。我希望在未来带来更多动态和复杂的模型,敬请期待。
这里有一个链接到完整的 jupyter 笔记本。
[## tarunlnmiti/机器学习
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/tarunlnmiit/machine_learning/blob/master/naive_bayes-20newsgroup.ipynb)
我正在免费赠送一本关于一致性的电子书。在这里获得你的免费电子书。
感谢您的阅读。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,不需要你额外付费。
[## 加入我的推荐链接-塔伦古普塔
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
tarun-gupta.medium.com](https://tarun-gupta.medium.com/membership)
你可以在这里阅读我的更多帖子:
[## 标记故事列表的快速链接—感谢您的访问
我也有一份以快节奏出版为目标的出版物。读书成为作家。
tarun-gupta.medium.com](https://tarun-gupta.medium.com/thank-you-for-visiting-my-profile-9f708062c75e)
使用朴素贝叶斯和 TextAnalysis.jl 创建垃圾邮件过滤器
原文:https://towardsdatascience.com/implementing-a-spam-filter-using-naive-bayes-and-textanalysis-jl-on-julia-e321180ea301?source=collection_archive---------23-----------------------
Christopher Gower 在 Unsplash 上拍摄的照片
你好!今天,我将告诉你更多关于我做了什么来制作一个垃圾邮件过滤器,使用朴素贝叶斯从 UCI 机器学习的 kaggle 上的数据集中检测垃圾邮件数据,以及在 Julia 上使用 TextAnalysis.jl 。
我从查看 TextAnalysis.jl 的文档开始,以了解更多关于 NaiveBayes 分类器的工作原理。
using TextAnalysis: NaiveBayesClassifier, fit!, predict
m = NaiveBayesClassifier([:legal, :financial])
fit!(m, "this is financial doc", :financial)
fit!(m, "this is legal doc", :legal)
predict(m, "this should be predicted as a legal document")
我运行了文档中的示例,并了解到函数 NaiveBayesClassifier 接受一组可能的类的参数,这些数据可能属于这些类。
在这种情况下,是:legal
和:financial
。我还了解到,我们通过用fit!
函数拟合相关数据来训练模型,该函数接受模型本身的参数、我们试图训练的数据串以及数据所属的类。这里的数据是一个字符串,例如“this is financial doc”
,在本例中,它所属的类是:financial
。
最后,我了解到predict
函数允许我们输入一串数据,并使用 NaiveBayesClassifier 算法,根据使用fit!
函数之前训练的数据串来预测该字符串属于哪个类。predict
函数接受模型本身的参数以及我们试图预测的数据字符串。
我解决这个问题的第一个方法是,我认为首先导入所有的数据是一个好主意。因为我有使用 CSV.jl 和 DataFrames.jl 软件包的经验,所以我熟悉数据的导入。
using CSV, DataFrames
spamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))---------------------------------------------------------------julia> showall(spamdata)
5572×5 DataFrame
│ Row │ v1 │
│ │ String │
├──────┼────────┤
│ 1 │ ham │
│ 2 │ ham │
│ 3 │ spam │
│ 4 │ ham │
│ 5 │ ham │
│ 6 │ spam │
│ 7 │ ham │
│ 8 │ ham │
│ 9 │ spam │
│ 10 │ spam │
下图显示了包含数据的原始.csv
文件的结构。
csv 文件有两列。v2
是我们想要用来训练的数据串,v1
是特定数据串的类,对应于v2
。
我想要一种方法来循环遍历文件的每一行,并在一个条件下拆分 ham 数据,在另一个条件下拆分 spam 数据,这样,当进行训练时,我将能够使用fit!
函数来训练ham
数据,这需要我指定该数据的类别。
for row in eachrow(spamdata)
if row.v1 == "ham"
println("ham")
elseif row.v1 == "spam"
println("spam")
end
end
这是一个成功,我最终有火腿和垃圾邮件被打印!
ham
spam
ham
spam
ham
ham
spam
spam
ham
spam
⋮
既然已经解决了,我可以使用 NaiveBayesClassifier 函数来定义我的模型了。我想定义两个类,:ham
和:spam
。
using TextAnalysis: NaiveBayesClassifier, fit!, predict
m = NaiveBayesClassifier([:ham, :spam])
接下来,我想开始训练我的模型。正如我们从最初的.csv
文件的结构中看到的,v2
是我们试图训练的字符串。将for
循环与fit!
函数结合起来,我做了以下工作来尝试训练我们所有可用的数据。
using CSV, DataFrames
using TextAnalysis: NaiveBayesClassifier, fit!, predictspamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))
global m = NaiveBayesClassifier([:ham, :spam])
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, row.v2, :ham)
elseif row.v1 == "spam"
fit!(m, row.v2, :spam)
end
end
但是当我运行它时,我得到了下面的错误:LoadError: Base.InvalidCharError{Char}('\xe5\xa3')
就在那时,我意识到数据集在v2
列的某些字符串中有无效字符。为了消除这个错误,我们需要使用下面的函数过滤掉不支持的字符:filter(isvalid, <string>)
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, filter(isvalid, row.v2), :ham)
elseif row.v1 == "spam"
fit!(m, filter(isvalid, row.v2), :spam)
end
end
当我将字符串row.v2
替换为filter(isvalid, row.v2)
并再次运行程序时,没有出现错误。因此,该模型被成功训练,到目前为止一切顺利!
在 REPL 中,实际上什么都没有打印出来,因为没有错误,程序中没有任何东西被打印出来。为了全面测试模型是否有效,我们可以尝试创建一个预测,并打印出预测的结果,以查看训练是否真正完成。
prediction1 = predict(m, "hello my name is kfung")
prediction2 = predict(m, "text 31845 to get a free phone")
println(prediction1)
println(prediction2)
在这里,我创建了两个预测,其中第一个看起来像一个火腿消息(因为它看起来不可疑),第二个看起来像一个垃圾邮件,以测试模型。
我得到的结果如下:
Dict(:spam => 0.013170434049325023, :ham => 0.986829565950675)
Dict(:spam => 0.9892304346396908, :ham => 0.010769565360309069)
正如我们所知,预测非常准确,因为第一个预测的:ham
值接近 1,这意味着它最有可能是一封垃圾邮件,第二个预测的:spam value
值接近 1,这意味着它最有可能是一封垃圾邮件。正如我们所料。
但是对于不熟悉字典或 Julia 语法的用户来说,他们可能会对上面的字典的含义感到困惑。我修改了代码,以便它检查字典中的:spam
和:ham
值,并打印出这两个值中较大值的类。
prediction = predict(m, "hello my name is kfung")
if prediction[:spam] > prediction[:ham]
println("spam")
else
println("ham")
end
正如我们所料,这个程序的结果是字符串“ham”
,因为它预测的:ham
值大于:spam
值,因此它更有可能是一个火腿消息。
最后,我将所有内容都包装在一个函数中,该函数以一个字符串作为参数,这样当您用一个字符串调用该函数时,如果模型预测它是垃圾邮件,它将输出“spam”
,如果模型预测它不是垃圾邮件,则输出“ham”
。
using CSV, DataFrames
using TextAnalysis: NaiveBayesClassifier, fit!, predictfunction checkspam(msg::String)
spamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))
m = NaiveBayesClassifier([:ham, :spam])
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, filter(isvalid, row.v2), :ham)
elseif row.v1 == "spam"
fit!(m, filter(isvalid, row.v2), :spam)
end
end
prediction = predict(m, msg)
if prediction[:spam] > prediction[:ham]
println("spam")
else
println("ham (not spam)")
end
end
最后,我意识到每次对信息进行分类时,我是如何训练模型的。这使得程序在运行时效率不高,因为每次我们试图使用模型进行预测时,它都要从头到尾检查 5600 行数据。相反,我们可以将模型置于函数之外(这样它只在开始时运行一次),并将模型存储在一个全局变量中,这样以后它就可以使用存储在全局变量中的预训练模型来分类任何其他消息。
using CSV, DataFrames
using TextAnalysis: NaiveBayesClassifier, fit!, predictspamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))
global m = NaiveBayesClassifier([:ham, :spam])
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, filter(isvalid, row.v2), :ham)
elseif row.v1 == "spam"
fit!(m, filter(isvalid, row.v2), :spam)
end
endfunction checkspam(msg::String)
prediction = predict(m, msg)
if prediction[:spam] > prediction[:ham]
println("spam")
else
println("ham (not spam)")
end
end
总的来说,我认为这是创建新的垃圾邮件过滤器的一次非常成功的尝试。我学到了更多关于如何应用朴素贝叶斯为概率分类的独立假设建模的知识。朴素贝叶斯目前仍然是文本分类的一个相当受欢迎的选项,这就是一个例子,并且可以与其他模型训练方法形成对比,例如逻辑回归,其中数据是有条件训练的。这个模型还有其他应用,比如情绪分析,这是我将在另一篇文章中重点介绍的,所以请继续关注!
非常感谢您的阅读!
使用 Streamlit 实现有状态架构
原文:https://towardsdatascience.com/implementing-a-stateful-architecture-with-streamlit-58e52448efa1?source=collection_archive---------16-----------------------
使用 PostgreSQL 通过 Streamlit 创建有状态的多页面应用程序
照片由米切尔罗在 Unsplash
细流
Streamlit 自 2019 年 10 月成立以来已经走过了漫长的道路。它增强了软件开发社区的能力,并有效地使我们开发和部署应用程序到云的方式民主化。然而,与所有新工具一样,仍有一段路要走,当 Streamlit 团队不知疲倦地处理新功能的请求时,我们开发人员自己可以同时创建临时工作。
Streamlit 目前缺乏的一个功能是为其应用程序实现可编程状态的能力。在其当前形式中,没有内部状态可以存储易变数据,如用户输入、数据帧和输入到小部件中的值。考虑到当用户在按下按钮或在页面之间切换的情况下触发一个动作时,Streamlit 固有地重新运行整个脚本,应用程序将重置所有输入和数据帧。虽然对于许多应用程序来说,这不是问题,但对于其他应用程序来说,这可能是一个障碍。想象一下,如果您试图使用增量步骤构建一个具有顺序逻辑的应用程序,缺少有状态架构会使 Streamlit 成为一个不合适的框架。创始人承诺在不久的将来发布一个有状态版本,但在此之前,人们可以使用开源数据库如 PostgreSQL 来开发一个 hack,我将在下面解释。
一种数据库系统
PostgreSQL 或简称 Postgres 是一个免费的开源关系数据库系统,由于其易用性和扩展能力,它通常是大多数开发人员的首选数据库。虽然它表面上是一个结构化的数据库管理系统,但它也可以存储非结构化的数据,如数组和二进制对象,这使得它对开放式项目非常有用。此外,它的图形用户界面非常直观和简单,确保学习曲线非常浅。
在我们的有状态架构的实现中,我们将使用本地 Postgres 服务器来存储我们的状态变量,例如我们的 Streamlit 应用程序中生成的用户输入和数据帧。在我们继续之前,请使用此链接下载并安装 Postgres。在安装过程中,系统会提示您配置用户名、密码和本地 TCP 端口,以便将数据库服务器转发到。默认端口是 5432,您可以保持不变。安装完成后,您将能够通过运行“pgAdmin 4”应用程序登录到您的服务器,该应用程序将在您的浏览器上打开您的服务器门户,如下所示。
PostgreSQL 门户。图片作者。
默认情况下,还应该有一个名为“Postgres”的数据库显示在左侧栏中,但是,如果没有,您可以右键单击“Databases”并选择“Create”来提供一个新的数据库。
履行
现在我们已经解决了后勤问题,让我们转向 Python 来实现。除了通常的嫌疑人(熊猫和显然 Streamlit 本身),你还需要下载以下软件包。
Sqlalchemy 我们将使用这个包来创建和执行我们的 SQL 查询。Sqlalchemy 使编写复杂的查询变得相当简单,如果您将查询字符串作为参数传递(而不是连接),它具有反 SQL 注入功能,可以确保您的查询是安全的。您可以使用以下命令下载它。
pip install sqlalchemy
此外,我们需要从 Streamlit 库中导入一个名为“get_report_ctx”的方法。每次运行我们的应用程序时,这个函数都会创建一个唯一的会话 ID。这个 ID 将与我们的每个状态变量相关联,以确保我们从 Postgres 数据库中检索到正确的状态。
继续将以下包导入 Python 脚本。
首先,我们将创建一个提取应用程序实例的会话 ID 的函数。请注意,ID 会在每次应用程序刷新时更新,即当您点击 F5 时。由于会话 ID 将被用作存储我们的状态变量的表的名称,我们将需要遵守 Postgres 的表命名约定,该约定规定名称必须以下划线或字母(不是数字)开头,不能包含破折号,并且长度必须少于 64 个字符。
接下来,我们将创建四个函数,用于从 Streamlit 到 Postgres 读取和写入用户输入和数据帧的状态,如下所示。
现在,我们将创建多页 Streamlit 应用程序的主要功能。首先,我们使用包含用户名、密码和数据库名称的连接字符串来设置 PostgreSQL 客户端。请注意,存储您的数据库凭证的更安全的方法是将它们保存在一个配置文件中,然后在您的代码中将它们作为参数调用。然后,我们获取我们的会话 ID,它应该类似于下面这样:
_id_bd3d996d_d887_4ce6_b27d_6b5a85778e60
随后,我们将使用当前会话 ID 创建一个表,其中包含一个名为“size”的列,数据类型为“text”。我们需要确保每次更新状态变量时,它都被之前的状态覆盖。因此,我们将查询表的长度,如果它是零,我们将插入一个带有 state 的新行。否则,如果当前会话 ID 中的先前状态已经存在,我们将只更新现有的行。
最后,我们将创建两个页面,可以使用侧边栏中的“st.selectbox”进行切换。第一页包含文本“Hello world ”,第二页包含一个文本输入小部件,用于生成具有用户指定的相应大小的稀疏矩阵。文本输入和生成的数据帧的状态保存在我们的 Postgres 数据库中,每次 Streamlit 本身重新运行脚本时都会对其进行查询。如果脚本在相同的会话 ID 中找到现有状态,它将相应地更新输入和数据帧。
结果
使用无状态实现自然运行应用程序,每次我们切换页面时都会重置文本输入和数据帧,如下所示。
无状态实现。图片作者。
但是,使用有状态实现运行应用程序会获得一个持久状态,即使在我们切换页面之后,文本输入和数据帧仍会被存储和检索,如下所示。
有状态实现。图片作者。
同时,您还可以看到我们的 Postgres 数据库正在用我们的状态变量和数据帧进行实时更新,如下所示。
状态变量数据库。图片作者。
任何其他变量都可以使用以下命令保存和读取其状态:
write_state('*column name', value, connection string, session ID*)read_state('*column name', connection string, session ID*)
类似地,可以使用以下命令保存和读取任何数据帧:
write_state_df(dataframe*, connection string, session ID*)read_state_df(*connection string, session ID*)
结论
这个方法可以扩展到其他小部件,也可以用来存储生成或上传到 Streamlit 的二进制文件。此外,如果想要跟踪他们的用户的输入或者想要记录每个动作的时间戳,那么该方法可以进一步扩展以处理这样的特征。这里唯一需要注意的是,并非 Streamlit 中的所有小部件都有可以存储的值,例如,“st.button”仅用作事件触发器,没有可以保存为状态的相关值。
如果您想了解更多关于数据可视化、Python 以及将 Streamlit web 应用程序部署到云中的信息,请查看以下(附属链接)课程:
使用 Streamlit 开发 Web 应用程序:
[## 使用 Streamlit 开发 Web 应用程序
使用纯 Python 框架开发安全且可扩展的 Web 应用程序并将其部署到云中
www.amazon.com](https://www.amazon.com/Web-Application-Development-Streamlit-Applications/dp/1484281101/)
使用 Python 实现数据可视化:
[## 用 Python 实现数据可视化
“一图胜千言”。我们都熟悉这个表达。它尤其适用于试图…
www.coursera.org](https://www.coursera.org/learn/python-for-data-visualization?irclickid=xgMQ4KWb%3AxyIWO7Uo7Vva0OcUkGQgW2aEwvr1c0&irgwc=1&utm_medium=partners&utm_source=impact&utm_campaign=3308031&utm_content=b2c)
面向所有人的 Python 专业化:
[## 面向所有人的 Python
学习用 Python 编程和分析数据。开发收集、清理、分析和可视化数据的程序…
www.coursera.org](https://www.coursera.org/specializations/python?irclickid=xgMQ4KWb%3AxyIWO7Uo7Vva0OcUkGQgW16Ewvr1c0&irgwc=1&utm_medium=partners&utm_source=impact&utm_campaign=3308031&utm_content=b2c)
使用 Streamlit 和 Python 构建数据科学 Web 应用程序:
[## 使用 Streamlit 和 Python 构建数据科学 Web 应用程序
在 2 小时内完成这个指导项目。欢迎参加这个构建您的第一个数据科学网站的动手项目…
www.coursera.org](https://www.coursera.org/projects/data-science-streamlit-python?irclickid=xgMQ4KWb%3AxyIWO7Uo7Vva0OcUkGQgTzrEwvr1c0&irgwc=1&utm_medium=partners&utm_source=impact&utm_campaign=3308031&utm_content=b2c)
GitHub 资源库:
[## mkhorasani/streamlit_state
使用 PostgreSQL 的 Streamlit 的有状态实现
github.com](https://github.com/mkhorasani/streamlit_state)
新到中?您可以在此订阅和解锁无限文章。
用 R 实现一个交易算法
原文:https://towardsdatascience.com/implementing-a-trading-algorithm-with-r-315a175538bd?source=collection_archive---------25-----------------------
克里斯·利维拉尼在 Unsplash 上的照片
这个故事解释了如何用 r 实现移动平均线交易算法。如果你对建立你的自动交易管道感兴趣,你应该首先阅读这篇文章。这个故事是一个纯粹的技术指南,侧重于编程和统计,而不是财务建议。
在整个故事中,我们将构建一个 R 函数,它将历史股票数据和任意阈值作为输入,并基于它来决定是否是购买给定股票的好时机。我们会看看苹果股票。这篇文章可能需要一定程度的统计知识。大学水平的统计学模块介绍应该足够了。
1。移动平均算法
移动平均交易算法利用了股票趋势的波动。我们首先确定给定时间序列的斜率是否为正。为了简单起见,我们设计的这个算法只适用于积极趋势的股票。然后我们取消历史时间序列的趋势,检查最近的波动是高于还是低于移动平均线。如果当前价格低于移动平均线,并且我们手中没有该股票,我们就买入该股票。如果它高于移动平均线,并且股票目前在我们的持股范围内,我们就卖出股票。
图 1 代表苹果股票在过去 50 天的收盘价。我们对这个时间序列进行去趋势化,这样红色虚线就与 x 轴对齐了。
图 1:苹果股票收盘价(美元)
图 2 显示了苹果股票在过去 50 天的走势。去趋势时间序列看起来是平稳的,即它具有恒定的均值和方差。如果我们想要非常严格,我们可以测试平稳性,以确保它具有理想的属性。我们选择任意阈值为+-5%。如果股票的 5 天平均价格比去趋势时间序列低 5%,我们就买入,如果比去趋势时间序列高 5%,我们就卖出。如果交易成功,在此期间,我们将获得 10%以上的收益。
图 2:去除趋势后的 APPL 股票时间序列残差
2.代码实现
凯文·Ku 在 Unsplash 上的照片
该函数有两个输入。雅虎 API 的历史数据和买卖股票的任意阈值。
moving_average_model <- function(data,trend_deviation_treshold = -5) {…}
然后,我们访问当前持有的股票,以检查我们在过去购买了哪些股票。下面,我们检查一下我们的投资组合中目前是否持有苹果股票。
holdings <- current_holdings()if ((holdings %>% filter(stock == "APPL"))$stock == "APPL") {stock_in_portfolio = TRUE} else {stock_in_portfolio = FALSE}
然后,我们将历史数据声明为一个数据框,将包含日期的行名称转换为一个单独的列,只考虑过去 70 天(股票市场开放时大约 50 个工作日)的收盘价和日期。最后,我们创建一个额外的列,为每一天提供一个数字。最近一天的数字最高。
data <- as.data.frame(data) %>%tibble::rownames_to_column("date")data_close <- data %>%select(date, close = AAPL.Close) %>%filter(date > as.Date(Sys.Date() - 70)) %>%arrange(date) %>%mutate(day_number = row_number())
下一步是消除时间序列的趋势。我们已经为上面的每个日期创建了一个数字递增的变量。如果我们把它作为一个独立的变量,用日期来绘制,这将会形成一条直线。因此,我们可以使用这个变量来消除趋势。如果我们回归这个变量上的股票价格,这将使时间序列趋向。然后,我们可以处理回归的残差,以确定当前价格是高于还是低于移动平均线。
formula <- close ~ day_numberregression_model <- lm(formula = formula,data = data_close)
你可能会注意到,我们不抑制常数项,也不考虑在进一步的分析。我们只关心当前价格相对于平均价格的位置。因此,我们只能分析残差。
recent_5 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number > max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))
首先,我们计算最近 5 个工作日的平均收盘均值。
recent_5_50 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number <= max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))
然后,我们计算过去 5 到 50 个工作日的平均值。接下来,我们计算最近 5 天的平均值与 6 到 50 个工作日平均值的偏差。
trend_deviation <- ((recent_5$close_mean - recent_5_50$close_mean) /recent_5_50$close_mean) * 100
如果过去 5 天的当前偏差低于 5 到 50 天,我们就买入股票。如果我们已经持有苹果股票,如果偏离超过阈值,我们就卖出。我们也检查每一次给定股票的时间序列有一个正斜率。
if (trend_deviation < trend_deviation_treshold ®ression_model$coefficients[[2]] > 0 &stock_in_portfolio == FALSE) {decision_sell <- FALSEdecision_buy <- TRUE} else if (trend_deviation > -trend_deviation_treshold ®ression_model$coefficients[[2]] > 0 &stock_in_portfolio == TRUE) {decision_sell <- TRUEdecision_buy <- FALSE}
最后一步是创建一个数据框,记录算法所做决策的细节。我们创建一个唯一的 id,包括股票名称和日期,记录运行日期,访问股票价格的日期,股票名称,给定股票的收盘价,计算的趋势偏差,我们选择的阈值,5 天和 45 天的平均值,买入决定和卖出决定。
output <- data.frame(id = paste0("APPL-", Sys.Date()),run_time = as.character(Sys.time()),stock_date = (data %>% filter(date == max(date)))$date,stock = "APPL",close_price = (data %>% filter(date == max(date)))$AAPL.Close,trend_deviation = trend_deviation,threshold = trend_deviation_treshold,recent_5_avg = recent_5$close_mean,recent_5_50_avg = recent_5_50$close_mean,decision_buy = decision_buy,decision_sell = decision_sell)
3.摘要
照片由马库斯·斯皮斯克在 Unsplash 拍摄
我们已经使用 r 实施了移动平均线交易策略。我们只是查看给定股票的历史数据,检查它是否有正趋势,计算当前平均趋势偏差,并基于此做出决策。
然后,我们在主交易管道中调用这个算法,并将这个决定的细节记录到 google sheets 中。
将来,我会将上述算法扩展到多只股票,这样我们就可以提供我们感兴趣的股票列表,算法/管道会自动交易它们。我们还可以对输入进行额外的健全性检查,并进行平稳性检查,以确保消除趋势的时间序列具有理想的属性。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
附录
移动平均交易函数的完整代码
moving_average_model <- function(data,trend_deviation_treshold = 3) {holdings <- current_holdings()if ((holdings %>% filter(stock == "APPL"))$stock == "APPL") {stock_in_portfolio = TRUE} else {stock_in_portfolio = FALSE}data <- as.data.frame(data) %>%tibble::rownames_to_column("date")data_close <- data %>%select(date, close = AAPL.Close) %>%filter(date > as.Date(Sys.Date() - 70)) %>%arrange(date) %>%mutate(day_number = row_number())formula <- close ~ day_numberregression_model <- lm(formula = formula,data = data_close)residual_values <- bind_cols(as.data.frame(regression_model$residuals),data_close)recent_5 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number > max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))recent_5_50 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number <= max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))trend_deviation <- ((recent_5$close_mean - recent_5_50$close_mean) /recent_5_50$close_mean) * 100if (trend_deviation < trend_deviation_treshold ®ression_model$coefficients[[2]] > 0 &stock_in_portfolio == FALSE) {decision_sell <- FALSEdecision_buy <- TRUE} else if (trend_deviation > -trend_deviation_treshold ®ression_model$coefficients[[2]] > 0 &stock_in_portfolio == TRUE) {decision_sell <- TRUEdecision_buy <- FALSE}output <- data.frame(id = paste0("APPL-", Sys.Date()),run_time = as.character(Sys.time()),stock_date = (data %>% filter(date == max(date)))$date,stock = "APPL",close_price = (data %>% filter(date == max(date)))$AAPL.Close,trend_deviation = trend_deviation,threshold = trend_deviation_treshold,recent_5_avg = recent_5$close_mean,recent_5_50_avg = recent_5_50$close_mean,decision_buy = decision_buy,decision_sell = decision_sell)output}
使用 TensorFlow 2.0+和 Keras 实现 AlexNet CNN 架构
原文:https://towardsdatascience.com/implementing-alexnet-cnn-architecture-using-tensorflow-2-0-and-keras-2113e090ad98?source=collection_archive---------1-----------------------
由乌列尔 SC 在 Unsplash 上拍摄的照片
技术的
了解如何实施 2012 年开启深度卷积神经网络革命的神经网络架构。
在我主持的这个现场培训环节,用 TensorFlow 和 Keras 学习 AI 和深度学习的基础知识。
介绍
本文的主要内容将介绍如何使用 TensorFlow 和 Keras 实现 AlexNet 卷积神经网络(CNN)架构。
但首先,请允许我提供一个 AlexNet CNN 架构背后的简短背景。
AlexNet 在赢得 ImageNet 大规模视觉识别挑战赛(ILSSVRC 2012 竞赛)时首次用于公共场合。正是在这次比赛中,AlexNet 展示了深度卷积神经网络可以用于解决图像分类。
AlexNet 以微弱优势赢得了 ILSVRC 2012 大赛。
详细介绍 CNN 架构内部组件的研究论文也介绍了一些新颖的技术和方法,如高效的计算资源利用;数据扩充、GPU 训练和防止神经网络内过度拟合的多种策略。
我写了一篇文章,介绍了 AlexNet 带给计算机视觉和深度学习世界的关键思想和技术。
以下是本文中的一些关键学习目标:
- 介绍用 Keras 和 TensorFlow 实现神经网络
- 用 TensorFlow 进行数据预处理
- 用冲浪板训练可视化
- 标准机器学习术语和术语的描述
AlexNet 实现
AlexNet CNN 可能是理解深度学习概念和技术的最简单的方法之一。
与最近几年出现的一些最先进的 CNN 架构相比,AlexNet 并不是一个复杂的架构。
AlexNet 对于初学者和中级深度学习从业者来说足够简单,可以获得一些关于模型实现技术的良好实践。
本文中的所有代码都是使用 Jupyter 实验室编写的。在本文的最后是一个 GitHub 链接,其中包含了实现部分的所有代码。
那么我们开始吧。
1.工具和库
我们从导入以下库开始实施:
- tensor flow:机器学习模型实现、训练、部署的开源平台。
- Keras:一个开源库,用于实现运行在 CPU 和 GPU 上的神经网络架构。
- Matplotlib:可视化 python 工具,用于演示交互式图表和图像。
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import os
import time
2.资料组
CIFAR-10 数据集包含 60,000 幅彩色图像,每幅图像的尺寸为 32x32px 。数据集内的图像内容是从 10 个类别中取样的。
CIFAR-10 数据集中的类
CIFAR-10 图像由 AlexNet 网络的一些创建者、 Alex Krizhevsky 和 Geoffrey Hinton 聚合而成。
深度学习 Keras 库通过其数据集模块,相对容易地提供对 CIFAR10 数据集的直接访问。使用 Keras,访问通用数据集,如 CIFAR10 或 MNIST ,变成了一项微不足道的任务。
(train_images, train_labels), (test_images, test_labels) = keras.datasets.cifar10.load_data()
为了在可视化阶段引用图像的类名,用变量名*CLASS_NAMES*.
初始化包含类的 python 列表
CLASS_NAMES= ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
默认情况下,CIFAR 数据集分为 50,000 个训练数据和 10,000 个测试数据。我们需要的数据集的最后一个分区是验证数据。
通过获取训练数据中的最后 5000 个图像来获得验证数据。
validation_images, validation_labels = train_images[:5000], train_labels[:5000]
train_images, train_labels = train_images[5000:], train_labels[5000:]
训练数据集:这是我们用来直接训练神经网络的一组数据集。训练数据是指在训练期间暴露给神经网络的数据集分区。
验证数据集:这组数据集在训练期间被用来评估网络在不同迭代中的性能。
测试数据集:数据集的这个分区在训练阶段完成后评估我们网络的性能。
TensorFlow 提供了一套功能和操作,可通过定义的输入管道轻松操作和修改数据。
为了能够访问这些方法和过程,我们需要将数据集转换为 TensorFlow 熟悉的高效数据表示。这是使用TF . data . datasetAPI 实现的。
更具体地说,TF . data . Dataset . from _ tensor _ slices方法采用训练、测试和验证数据集分区,并返回相应的 TensorFlow 数据集表示。
*train_ds = tf.data.Dataset.from_tensor_slices((train_images, train_labels))
test_ds = tf.data.Dataset.from_tensor_slices((test_images, test_labels))
validation_ds = tf.data.Dataset.from_tensor_slices((validation_images, validation_labels))*
3.预处理
任何机器学习中的预处理都与数据从一种形式到另一种形式的转换相关联。
通常,进行预处理是为了确保所使用的数据符合适当的格式。
首先,让我们将 CIFAR-10 数据集中的图像可视化。
下面的代码片段使用 Matplotlib 库将来自五幅训练图像的数据的像素信息呈现到实际图像中。还存在图像中每个描绘的内容所属的类别的指示符。
原谅图像的模糊;CIFAR-10 图像尺寸较小,这使得实际图片的可视化有点困难。
*plt.figure(figsize=(20,20))
for i, (image, label) in enumerate(train_ds.take(5)):
ax = plt.subplot(5,5,i+1)
plt.imshow(image)
plt.title(CLASS_NAMES[label.numpy()[0]])
plt.axis('off')*
将对呈现给网络的数据施加的主要预处理转换是:
- 标准化和规范化图像。
- 将图像尺寸从 32x32 调整到 227x227 。AlexNet 网络输入需要 227x227 的图像。
我们将创建一个名为process_images.
的函数
该函数将执行我们需要的所有数据预处理工作。这个函数在机器学习工作流程中被进一步调用。
*def process_images(image, label):
# Normalize images to have a mean of 0 and standard deviation of 1
image = tf.image.per_image_standardization(image)
# Resize images from 32x32 to 277x277
image = tf.image.resize(image, (227,227))
return image, label*
4.数据/输入管道
到目前为止,我们已经获得并分区了数据集,并创建了一个函数来处理数据集。下一步是构建输入管道。
输入/数据管道被描述为一系列连续调用的函数或方法。输入管道是一系列函数,这些函数或者作用于数据,或者对流经管道的数据执行操作。
让我们获得我们创建的每个数据集分区的大小;数据集分区的大小是必需的,以确保数据集在通过网络之前被彻底混洗。
*train_ds_size = tf.data.experimental.cardinality(train_ds).numpy()
test_ds_size = tf.data.experimental.cardinality(test_ds).numpy()
validation_ds_size = tf.data.experimental.cardinality(validation_ds).numpy()
print("Training data size:", train_ds_size)
print("Test data size:", test_ds_size)
print("Validation data size:", validation_ds_size)*
对于我们的基本输入/数据管道,我们将进行三个主要操作:
- 预处理数据集内的数据
- 打乱数据集
- 数据集内的批量数据
*train_ds = (train_ds
.map(process_images)
.shuffle(buffer_size=train_ds_size)
.batch(batch_size=32, drop_remainder=True))test_ds = (test_ds
.map(process_images)
.shuffle(buffer_size=train_ds_size)
.batch(batch_size=32, drop_remainder=True))validation_ds = (validation_ds
.map(process_images)
.shuffle(buffer_size=train_ds_size)
.batch(batch_size=32, drop_remainder=True))*
5.模型实现
在本节中,我们将从头开始实现 AlexNet CNN 架构。
通过利用 Keras Sequential API ,我们可以在彼此堆叠的模型中实现连续的神经网络层。
以下是 AlexNet CNN 架构组成的层类型,并附有简要说明:
卷积层:卷积是一个数学术语,描述两组元素之间的点积相乘。在深度学习中,卷积运算作用于卷积层中的滤波器/内核和图像数据阵列。因此,卷积层只是一个包含滤波器和通过卷积神经网络的图像之间的卷积运算的层。
批量标准化层 :批量标准化是一种技术,它通过引入一个附加层来减轻神经网络内不稳定梯度的影响,该附加层对来自前一层的输入执行操作。这些操作对输入值进行标准化和规范化,然后通过缩放和移位操作转换输入值。
下面的 max-pooling 操作有一个 2x2 的窗口,并滑过输入数据,输出内核感受域内像素的平均值。
展平图层:取一个输入形状,将输入图像数据展平成一维数组。
密集层:密集层内部嵌入了任意数量的单元/神经元。每个神经元都是一个感知器。
Alex net CNN 中值得一提的一些其他操作和技术有:
激活函数%2C%20depending%20on%20input.) :将神经元的结果或信号转化为归一化输出的数学运算。作为神经网络组件的激活函数的目的是在网络中引入非线性。包含激活函数使神经网络具有更大的表示能力和解决复杂的函数。
整流线性单元激活函数(ReLU) :一种对神经元的值结果进行转换的激活函数。ReLU 对来自神经元的值施加的变换由公式 y=max(0,x) 表示。ReLU 激活函数将来自神经元的任何负值钳制为 0,而正值保持不变。这种数学变换的结果被用作当前层的输出,并被用作神经网络内的连续层的输入。
Softmax 激活函数:一种激活函数,用于导出输入向量中一组数字的概率分布。softmax 激活函数的输出是一个向量,其中它的一组值表示一个类或事件发生的概率。向量中的值加起来都是 1。
辍学 : 辍学技术通过随机减少神经网络内互连神经元的数量来工作。在每一个训练步骤中,每个神经元都有可能被遗漏,或者更确切地说,被排除在连接神经元的整理贡献之外。
代码片段代表 AlexNet CNN 架构的 Keras 实现。
*model = keras.models.Sequential([
keras.layers.Conv2D(filters=96, kernel_size=(11,11), strides=(4,4), activation='relu', input_shape=(227,227,3)),
keras.layers.BatchNormalization(),
keras.layers.MaxPool2D(pool_size=(3,3), strides=(2,2)),
keras.layers.Conv2D(filters=256, kernel_size=(5,5), strides=(1,1), activation='relu', padding="same"),
keras.layers.BatchNormalization(),
keras.layers.MaxPool2D(pool_size=(3,3), strides=(2,2)),
keras.layers.Conv2D(filters=384, kernel_size=(3,3), strides=(1,1), activation='relu', padding="same"),
keras.layers.BatchNormalization(),
keras.layers.Conv2D(filters=384, kernel_size=(3,3), strides=(1,1), activation='relu', padding="same"),
keras.layers.BatchNormalization(),
keras.layers.Conv2D(filters=256, kernel_size=(3,3), strides=(1,1), activation='relu', padding="same"),
keras.layers.BatchNormalization(),
keras.layers.MaxPool2D(pool_size=(3,3), strides=(2,2)),
keras.layers.Flatten(),
keras.layers.Dense(4096, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(4096, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation='softmax')
])*
6.张量板
至此,我们已经实现了自定义的 AlexNet 网络。
在我们继续用数据对网络进行训练、验证和评估之前,我们首先必须设置一些监控设施。
TensorBoard 是一款提供一套可视化和监控机制的工具。对于本教程中的工作,我们将利用 TensorBoard 来监控网络训练的进度。
更具体地说,我们将监控以下指标:t 培训损失、培训准确度、验证损失、验证准确度。
在下面的代码片段中,我们创建了一个目录的引用,我们希望所有的 TensorBoard 文件都存储在这个目录中。函数get_run_logdir
返回根据训练阶段开始的当前时间命名的确切目录的位置。
为了完成当前的过程,我们将存储特定训练课程的 TensorBoard 相关文件的目录传递给 TensorBoard 回调。
*root_logdir = os.path.join(os.curdir, "logs\\fit\\")def get_run_logdir():
run_id = time.strftime("run_%Y_%m_%d-%H_%M_%S")
return os.path.join(root_logdir, run_id)run_logdir = get_run_logdir()
tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)*
7.培训和结果
为了训练网络,我们必须编译它。
编译过程包括指定以下项目:
损失函数:一种量化机器学习模型表现好坏的方法。量化是基于一组输入的输出(成本),这些输入被称为参数值。参数值用于估计预测,而“损失”是预测值和实际值之间的差异。**
****优化算法:神经网络内的优化器是一种算法实现,它通过最小化损失函数提供的损失值来促进神经网络内的梯度下降过程。为了减少损失,适当地选择网络内的权重值是至关重要的。
****学习率:神经网络实现细节的一个组成部分,因为它是一个因子值,决定了对网络权值的更新水平。学习率是一种超参数。
**model.compile(loss='sparse_categorical_crossentropy', optimizer=tf.optimizers.SGD(lr=0.001), metrics=['accuracy'])
model.summary()**
我们还可以通过运行model.summary()
函数来提供网络摘要,以便更深入地了解网络的层组成。
**Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 55, 55, 96) 34944 _________________________________________________________________ batch_normalization (BatchNo (None, 55, 55, 96) 384 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 27, 27, 96) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 27, 27, 256) 614656 _________________________________________________________________ batch_normalization_1 (Batch (None, 27, 27, 256) 1024 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 13, 13, 256) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 13, 13, 384) 885120 _________________________________________________________________ batch_normalization_2 (Batch (None, 13, 13, 384) 1536 _________________________________________________________________ conv2d_3 (Conv2D) (None, 13, 13, 384) 147840 _________________________________________________________________ batch_normalization_3 (Batch (None, 13, 13, 384) 1536 _________________________________________________________________ conv2d_4 (Conv2D) (None, 13, 13, 256) 98560 _________________________________________________________________ batch_normalization_4 (Batch (None, 13, 13, 256) 1024 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 6, 6, 256) 0 _________________________________________________________________ flatten (Flatten) (None, 9216) 0 _________________________________________________________________ dense (Dense) (None, 4096) 37752832 _________________________________________________________________ dropout (Dropout) (None, 4096) 0 _________________________________________________________________ dense_1 (Dense) (None, 4096) 16781312 _________________________________________________________________ dropout_1 (Dropout) (None, 4096) 0 _________________________________________________________________ dense_2 (Dense) (None, 10) 40970 ================================================================= Total params: 56,361,738 Trainable params: 56,358,986 Non-trainable params: 2,752 _________________________________________________________________**
至此,我们已经准备好训练网络了。
通过 TensorFlow 启用 Keras 模块,训练自定义 AlexNet 网络非常简单。我们只需调用fit()
方法并传递相关参数。
Epoch: 这是一个数值,表示网络暴露于训练数据集中所有数据点的次数。
**model.fit(train_ds,
epochs=50,
validation_data=validation_ds,
validation_freq=1,
callbacks=[tensorboard_cb])**
在笔记本中执行这个代码单元后,网络将开始根据所提供的数据进行训练和验证。您将开始看到如下所示的培训和验证日志:
**Train for 1562 steps, validate for 156 steps
Epoch 1/50
1/1562 [..............................] - ETA: 3:05:44 - loss: 5.6104 - accuracy: 0.0625WARNING:tensorflow:Method (on_train_batch_end) is slow compared to the batch update (0.168881). Check your callbacks.
1562/1562 [==============================] - 42s 27ms/step - loss: 2.0966 - accuracy: 0.3251 - val_loss: 1.4436 - val_accuracy: 0.4920
Epoch 2/50
1562/1562 [==============================] - 39s 25ms/step - loss: 1.5864 - accuracy: 0.4382 - val_loss: 1.2939 - val_accuracy: 0.5447
Epoch 3/50
1562/1562 [==============================] - 39s 25ms/step - loss: 1.4391 - accuracy: 0.4889 - val_loss: 1.1749 - val_accuracy: 0.5859
Epoch 4/50
1562/1562 [==============================] - 39s 25ms/step - loss: 1.3278 - accuracy: 0.5307 - val_loss: 1.0841 - val_accuracy: 0.6228
Epoch 5/50
1562/1562 [==============================] - 39s 25ms/step - loss: 1.2349 - accuracy: 0.5630 - val_loss: 1.0094 - val_accuracy: 0.6569
Epoch 6/50
1562/1562 [==============================] - 40s 25ms/step - loss: 1.1657 - accuracy: 0.5876 - val_loss: 0.9599 - val_accuracy: 0.6851
Epoch 7/50
1562/1562 [==============================] - 39s 25ms/step - loss: 1.1054 - accuracy: 0.6128 - val_loss: 0.9102 - val_accuracy: 0.6937
Epoch 8/50
1562/1562 [==============================] - 40s 26ms/step - loss: 1.0477 - accuracy: 0.6285 - val_loss: 0.8584 - val_accuracy: 0.7109
Epoch 9/50
1562/1562 [==============================] - 39s 25ms/step - loss: 1.0026 - accuracy: 0.6461 - val_loss: 0.8392 - val_accuracy: 0.7137
Epoch 10/50
1562/1562 [==============================] - 39s 25ms/step - loss: 0.9601 - accuracy: 0.6627 - val_loss: 0.7684 - val_accuracy: 0.7398
Epoch 11/50
1562/1562 [==============================] - 40s 25ms/step - loss: 0.9175 - accuracy: 0.6771 - val_loss: 0.7683 - val_accuracy: 0.7476
Epoch 12/50
1562/1562 [==============================] - 40s 25ms/step - loss: 0.8827 - accuracy: 0.6914 - val_loss: 0.7012 - val_accuracy: 0.7702
Epoch 13/50
1562/1562 [==============================] - 40s 25ms/step - loss: 0.8465 - accuracy: 0.7035 - val_loss: 0.6496 - val_accuracy: 0.7903
Epoch 14/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.8129 - accuracy: 0.7160 - val_loss: 0.6137 - val_accuracy: 0.7991
Epoch 15/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.7832 - accuracy: 0.7250 - val_loss: 0.6181 - val_accuracy: 0.7957
Epoch 16/50
1562/1562 [==============================] - 40s 25ms/step - loss: 0.7527 - accuracy: 0.7371 - val_loss: 0.6102 - val_accuracy: 0.7953
Epoch 17/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.7193 - accuracy: 0.7470 - val_loss: 0.5236 - val_accuracy: 0.8327
Epoch 18/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.6898 - accuracy: 0.7559 - val_loss: 0.5091 - val_accuracy: 0.8425
Epoch 19/50
1562/1562 [==============================] - 40s 25ms/step - loss: 0.6620 - accuracy: 0.7677 - val_loss: 0.4824 - val_accuracy: 0.8468
Epoch 20/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.6370 - accuracy: 0.7766 - val_loss: 0.4491 - val_accuracy: 0.8620
Epoch 21/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.6120 - accuracy: 0.7850 - val_loss: 0.4212 - val_accuracy: 0.8694
Epoch 22/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.5846 - accuracy: 0.7943 - val_loss: 0.4091 - val_accuracy: 0.8746
Epoch 23/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.5561 - accuracy: 0.8070 - val_loss: 0.3737 - val_accuracy: 0.8872
Epoch 24/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.5314 - accuracy: 0.8150 - val_loss: 0.3808 - val_accuracy: 0.8810
Epoch 25/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.5107 - accuracy: 0.8197 - val_loss: 0.3246 - val_accuracy: 0.9048
Epoch 26/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.4833 - accuracy: 0.8304 - val_loss: 0.3085 - val_accuracy: 0.9115
Epoch 27/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.4595 - accuracy: 0.8425 - val_loss: 0.2992 - val_accuracy: 0.9111
Epoch 28/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.4395 - accuracy: 0.8467 - val_loss: 0.2566 - val_accuracy: 0.9305
Epoch 29/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.4157 - accuracy: 0.8563 - val_loss: 0.2482 - val_accuracy: 0.9339
Epoch 30/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.3930 - accuracy: 0.8629 - val_loss: 0.2129 - val_accuracy: 0.9449
Epoch 31/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.3727 - accuracy: 0.8705 - val_loss: 0.1999 - val_accuracy: 0.9525
Epoch 32/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.3584 - accuracy: 0.8751 - val_loss: 0.1791 - val_accuracy: 0.9593
Epoch 33/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.3387 - accuracy: 0.8830 - val_loss: 0.1770 - val_accuracy: 0.9557
Epoch 34/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.3189 - accuracy: 0.8905 - val_loss: 0.1613 - val_accuracy: 0.9643
Epoch 35/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.3036 - accuracy: 0.8969 - val_loss: 0.1421 - val_accuracy: 0.9681
Epoch 36/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.2784 - accuracy: 0.9039 - val_loss: 0.1290 - val_accuracy: 0.9736
Epoch 37/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.2626 - accuracy: 0.9080 - val_loss: 0.1148 - val_accuracy: 0.9762
Epoch 38/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.2521 - accuracy: 0.9145 - val_loss: 0.0937 - val_accuracy: 0.9828
Epoch 39/50
1562/1562 [==============================] - 42s 27ms/step - loss: 0.2387 - accuracy: 0.9190 - val_loss: 0.1045 - val_accuracy: 0.9768
Epoch 40/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.2215 - accuracy: 0.9247 - val_loss: 0.0850 - val_accuracy: 0.9860
Epoch 41/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.2124 - accuracy: 0.9274 - val_loss: 0.0750 - val_accuracy: 0.9862
Epoch 42/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.1980 - accuracy: 0.9335 - val_loss: 0.0680 - val_accuracy: 0.9896
Epoch 43/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.1906 - accuracy: 0.9350 - val_loss: 0.0616 - val_accuracy: 0.9912
Epoch 44/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.1769 - accuracy: 0.9410 - val_loss: 0.0508 - val_accuracy: 0.9922
Epoch 45/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.1648 - accuracy: 0.9455 - val_loss: 0.0485 - val_accuracy: 0.9936
Epoch 46/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.1571 - accuracy: 0.9487 - val_loss: 0.0435 - val_accuracy: 0.9952
Epoch 47/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.1514 - accuracy: 0.9501 - val_loss: 0.0395 - val_accuracy: 0.9950
Epoch 48/50
1562/1562 [==============================] - 41s 26ms/step - loss: 0.1402 - accuracy: 0.9535 - val_loss: 0.0274 - val_accuracy: 0.9984
Epoch 49/50
1562/1562 [==============================] - 40s 26ms/step - loss: 0.1357 - accuracy: 0.9549 - val_loss: 0.0308 - val_accuracy: 0.9966
Epoch 50/50
1562/1562 [==============================] - 42s 27ms/step - loss: 0.1269 - accuracy: 0.9596 - val_loss: 0.0251 - val_accuracy: 0.9976<tensorflow.python.keras.callbacks.History at 0x2de3aaa0ec8>**
为了更好地可视化和监控训练表现,我们将使用 TensorBoard 功能。
在 TensorBoard 日志文件夹所在的目录级别打开一个终端,并运行以下命令:
**tensorboard --logdir logs**
TensorBoard 日志文件所在的目录级别
按照终端上的指示,导航到' localhost:6006 '(这可能是一个不同的端口号)。
唉,您将看到一个类似于下图的页面:
张量板工具
下面是 TensorBoard 提供的完整培训和验证阶段的可视化片段。
TensorBoard 培训和验证监控
8.估价
最后一个官方步骤是通过网络评估对训练好的网络进行评估。
评估阶段将根据看不见的数据提供训练模型的性能分数。对于模型的评估阶段,我们将利用在早期步骤中创建的一批测试数据。
评估一个模型非常简单,你只需调用evaluate()
方法并传递批量测试数据。
**model.evaluate(test_ds)**
在执行了上面的单元块之后,我们看到了一个分数,它表明了模型在看不见的数据上的性能。
**312/312 [==============================] - 8s 27ms/step - loss: 0.9814 - accuracy: 0.7439
[0.9813630809673132, 0.7438902]**
返回结果的第一个元素包含评估损失:0.9813,第二个元素指示评估准确度 0.74389。
定制实施的 AlexNet 网络在 CIFAR-10 数据集上进行训练、验证和评估,以创建一个在包含 5000 个数据点的测试数据集上具有 74%评估准确性的模型。
奖金(可选)
本节包括补充 AlexNet 卷积神经网络实现的一些信息。
虽然这些附加信息对于理解实现过程并不重要,但是这些部分将为读者提供一些在未来工作中可以利用的附加背景知识。
所涵盖的部分如下:
- 局部响应归一化
- 信息转化为为什么我们在训练之前对数据集进行批处理和洗牌
局部反应标准化
许多人都熟悉批量标准化,但 AlexNet 架构在网络内使用了一种不同的标准化方法:本地响应标准化(LRN)。
LRN 是一种最大限度地激活邻近神经元的技术。相邻神经元描述共享相同空间位置的若干特征地图上的神经元。通过标准化神经元的激活,具有高激活的神经元被突出显示;这基本上模仿了神经生物学中发生的侧抑制。
LRN 在现代 CNN 架构中没有被广泛使用,因为还有其他更有效的归一化方法。尽管 LRN 实现仍然可以在一些标准的机器学习库和框架中找到,所以请随意试验。
我们为什么要打乱数据集?
在训练之前混洗数据集是典型的机器学习项目中的传统过程。但是我们为什么要这样做呢?
进行数据聚合时,通常会连续累积对应于相同类别和标签的图像或数据点。在加载用于训练和验证网络的数据之后,典型的最终结果是按照相应类别的顺序排列的一组图像/数据点。
神经网络在深度学习中学习的方法是通过检测图像内空间信息之间的模式。
假设我们有一个包含 10,000 张图片的数据集,分为五类。前 2000 个图像属于类别 1;第二个 2000 个图像属于类别 2,依此类推。
在训练阶段,如果我们向网络提供未受干扰的训练数据,我们会发现神经网络将学习与类别 1 密切相关的模式,因为这些是神经网络首先暴露的图像和数据点。这将增加优化算法为整个数据集发现最优解的难度。
通过改组数据集,我们确保了两件关键的事情:
1.数据集中有足够大的差异,使得训练数据中的每个数据点对网络都有独立的影响。因此,我们可以拥有一个能够很好地概括整个数据集的网络,而不是数据集的一个子部分。
2.我们的数据集的验证分区是从训练数据中获得的;如果我们不能适当地打乱数据集,我们会发现我们的验证数据集将不能代表训练数据中的类。例如,我们的验证数据集可能只包含来自训练数据的最后一个类的数据点,而不是用数据集平等地表示每个类。
为什么我们要在训练前对数据集进行批处理?
出于内存优化的原因,数据集分区通常是批处理的。有两种方法可以训练网络。
- 将所有训练数据一次呈现给网络
- 将训练数据分批成更小的分段(例如,8、16、32、64),并且在每次迭代中,将单个批次呈现给网络。
方法#1 适用于小型数据集,但是当您开始处理大型数据集时,您会发现方法#1 消耗了大量内存资源
通过对大型数据集使用方法#1,图像或数据点保存在内存中,这通常会在训练期间导致“内存不足”错误。
方法#2 是一种在考虑有效内存管理的同时,用大数据集训练网络的更保守的方法。通过对训练数据进行批处理,我们在任何给定时间都只能在内存中保存 16、32 或 128 个数据点,而不是整个数据集。
结论
这篇详细的文章涵盖了一些围绕深度学习项目中典型过程的主题。我们已经讨论了以下主题领域:
- 机器和深度学习工具和库
- 数据划分
- 使用 TensorFlow 创建输入和数据管道
- 数据预处理
- 卷积神经网络实现(AlexNet)
- 使用 TensorBoard 监控模型性能
- 模型评估
将来,我们将介绍另一个众所周知的卷积神经网络架构的实现:GoogLeNet。
想要更多吗?
- 成为推荐媒介会员,支持我的写作
- 订阅 在我发表文章时得到通知
- 通过 LinkedIn 联系我
- 在 奥莱利 跟我学
** [## AI 里有黑人吗?
我们仍然是人工智能中代表最少的种族之一,然而我们可能会因为它的利用和滥用而遭受最大的痛苦。
towardsdatascience.com](/are-there-black-people-in-ai-fb6928166d73) [## AlexNet 给深度学习世界带来了什么
花一分钟来了解琐碎的技术和神经网络架构,革命性的如何深入…
towardsdatascience.com](/what-alexnet-brought-to-the-world-of-deep-learning-46c7974b46fc) [## 理解和实现 LeNet-5 CNN 架构(深度学习)
在本文中,我们使用定制实现的 LeNet-5 神经网络对 MNIST 数据集进行图像分类
towardsdatascience.com](/understanding-and-implementing-lenet-5-cnn-architecture-deep-learning-a2d531ebc342)**
在 R 中实现 XGBoost 模型
原文:https://towardsdatascience.com/implementing-an-xgboost-model-in-r-59ee1892be2f?source=collection_archive---------18-----------------------
使用 XGBoost 预测酒店取消预订
来源:图片来自 Pixabay
在这个例子中,在 R 中构建了一个 XGBoost 模型来预测客户取消酒店预订的发生率。该分析基于来自 Antonio、Almeida 和 Nunes (2019):酒店预订需求数据集的数据。
H1 数据集用于训练和验证,而 H2 数据集用于测试目的。
背景
为了预测将取消预订的客户(其中变量is cancelled = 1表示取消,而is cancelled = 0表示客户坚持预订),在 R 中构建了一个 XGBoost 模型,具有以下特性:
- 研制周期
- 原产国
- 细分市场
- 存款类型
- 客户类型
- 所需的停车位
- 抵达周
数据操作
为了使数据适合用 R 中的 XGBoost 模型进行分析,需要一些数据操作过程。
首先,加载 xgboost 和矩阵库:
require(xgboost)
library(Matrix)
通过将变量定义为. numeric ,并在适当的时候以因子格式定义,形成特征的数据框架。然后数据帧被转换成矩阵格式。
leadtime<-as.numeric(H1$LeadTime)
country<-as.numeric(factor(H1$Country))
marketsegment<-as.numeric(factor(H1$MarketSegment))
deposittype<-as.numeric(factor(H1$DepositType))
customertype<-as.numeric(factor(H1$CustomerType))
rcps<-as.numeric(H1$RequiredCarParkingSpaces)
week<-as.numeric(H1$ArrivalDateWeekNumber)df<-data.frame(leadtime,country,marketsegment,deposittype,customertype,rcps,week)
attach(df)
df<-as.matrix(df)
标签(取消)被定义为一个因素。
IsCanceled<-as.numeric(factor(H1$IsCanceled))
然而,R 将 0 值标记为 1 ,将 1 值标记为 2。
为了使数据与 XGBoost 一起工作,必须定义 0 和 1 标签。
因此,标签值相应地替换如下:
IsCanceled[IsCanceled == "1"] <- "0"
IsCanceled[IsCanceled == "2"] <- "1"
训练集和验证集是分开的,每个都被定义为一个dgc matrix——这是一种特殊类型的矩阵,与 r 中的 XGBoost 模型一起工作。
train <- df[1:32000,]
val <- df[32001:40060,]train=as(train, "dgCMatrix")
trainval=as(val, "dgCMatrix")
valIsCanceled_train=IsCanceled[1:32000]
IsCanceled_val=IsCanceled[32001:40060]
模型预测
定义增压模型并生成预测:
bst <- xgboost(data = train, label = IsCanceled_train, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic")pred <- predict(bst, val)
以下是培训和验证错误:
> bst <- xgboost(data = train, label = IsCanceled_train, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic")
[1] train-error:0.252000
[2] train-error:0.222188> pred <- predict(bst, val)
> print(length(pred))
[1] 8060
> print(head(pred))
[1] 0.31375486 0.31375486 0.31375486 0.31375486 0.01498148
[6] 0.01498148
> prediction <- as.numeric(pred > 0.5)
> print(head(prediction))
[1] 0 0 0 0 0 0
> err <- mean(as.numeric(pred > 0.5) != IsCanceled_val)
> print(paste("val-error=", err))
[1] "val-error= 0.1287841191067"
从上面的输出中,我们可以看到 0.12 的验证误差明显低于 0.25 和 0.22 两个报告的训练误差。
还会生成一个重要性矩阵,以确定对酒店取消最重要的影响因素:
# Importance Matrix
importance_matrix <- xgb.importance(model = bst)
print(importance_matrix)
xgb.plot.importance(importance_matrix = importance_matrix)
资料来源:RStudio
下面是对重要性矩阵的更详细的介绍:
> print(importance_matrix)
Feature Gain Cover Frequency
1: country 0.3589183 0.2266091 0.3333333
2: leadtime 0.2767732 0.2807021 0.1666667
3: rcps 0.2198606 0.2967366 0.3333333
4: marketsegment 0.1444479 0.1959522 0.1666667
如下文所述堆叠交换后——增益、覆盖和频率说明如下:
- 增益:说明了模型中每棵树的特征贡献,较高的值说明对预测结果变量更重要。
- Cover: 与所讨论的特征相关的相对观察值的数量。
- 频率:一个特征在模型树中出现的次数。
增益是评估特性对模型的相对贡献时最重要的特性。由于国家变量的增益为 0.35 ,这被表示为模型中最重要的特征,即客户的原籍国将对决定他们最终是否取消酒店预订产生重大影响。
解释
通过观察训练和验证错误,看起来该模型在预测该特定验证集的取消方面表现得相当好,但是不能保证它在其他数据集上也能如此。
因此,我们使用了一个独立的测试集(H2)来验证 XGBoost 模型是否仍能很好地预测取消发生率。
H2 集合中的变量再次被定义:
leadtime<-as.numeric(H2$LeadTime)
country<-as.numeric(factor(H2$Country))
marketsegment<-as.numeric(factor(H2$MarketSegment))
deposittype<-as.numeric(factor(H2$DepositType))
customertype<-as.numeric(factor(H2$CustomerType))
rcps<-as.numeric(H2$RequiredCarParkingSpaces)
week<-as.numeric(H2$ArrivalDateWeekNumber)test<-data.frame(leadtime,country,marketsegment,deposittype,customertype,rcps,week)
attach(test)
test<-as.matrix(test)test=as(test, "dgCMatrix")
testIsCanceled_H2<-as.numeric(factor(H2$IsCanceled))
IsCanceled_H2[IsCanceled_H2 == "1"] <- "0"
IsCanceled_H2[IsCanceled_H2 == "2"] <- "1"
IsCanceled_H2<-as.numeric(IsCanceled_H2)
使用 H1 数据集构建的 XGBoost 模型现在用于合并 H2 的要素数据:
pred <- predict(bst, test)
下面是产生的测试错误:
> print(length(pred))
[1] 79330
> print(head(pred))
[1] 0.3137549 0.7707853 0.7707853 0.7707853 0.7707853 0.7707853
>
> prediction <- as.numeric(pred > 0.5)
> print(head(prediction))
[1] 0 1 1 1 1 1
>
> err <- mean(as.numeric(pred > 0.5) != IsCanceled_H2)
> print(paste("test-error=", err))
[1] "test-error= 0.274335056094794"
0.27 的测试误差高于 0.12 的验证误差,这是我们在预测新数据时可以合理预期的。
也就是说,0.25 和 0.22 的原始训练误差仅略低于测试误差——这表明该模型没有显示出过度拟合的证据。如果我们遇到训练误差远低于测试误差的情况,那么这显然是一个问题,因为它表明模型在预测用于建立模型的数据而不是新数据方面表现良好。
结论
在这个例子中,您已经看到了如何使用 r 运行 XGBoost 模型。
具体来说,您已经看到:
- 如何将数据转换成 dgCMatrix 格式,以便与 R 的 XGBoost 模型一起工作
- 如何通过重要性矩阵可视化特征的重要性
- 对测试数据运行 XGBoost 模型,以验证模型的准确性
非常感谢您的阅读,非常感谢您的任何问题或反馈。您可以在下面找到这个例子的相关存储库、R 代码和数据集,以及其他有用的参考资料。
参考
- 安东尼奥、阿尔梅达和努内斯(2019):酒店预订需求数据集
- 数据科学 StackExchange:如何解读 XGBoost 重要性的输出?
- GitHub:MGCodesandStats/酒店建模
- XGBoost 文档:XGBoost R 教程
在 Python 中实现批处理规范化
原文:https://towardsdatascience.com/implementing-batch-normalization-in-python-a044b0369567?source=collection_archive---------1-----------------------
为什么以及如何在神经网络中实现批处理规范化
我最近正在上斯坦福大学在线提供的用于视觉识别的卷积神经网络,并且刚刚开始着手这门课程的第二个作业。在这项作业的一部分中,我们被要求在一个完全连接的神经网络中实现批量标准化。
向前传递的实现相对简单,但是处理起来更具挑战性的向后传递,花了我相当长的时间才完成。经过几个小时的工作和斗争,我终于克服了这个挑战。在这里,我想分享一些我对批处理规范化的笔记和想法。
那么……什么是批量规格化呢?
批处理规范化处理神经网络初始化不良的问题。可以解释为在网络的每一层做预处理。它迫使网络中的激活在训练开始时呈现单位高斯分布。这确保了所有神经元在网络中具有大致相同的输出分布,并提高了收敛速度。
要了解为什么激活在网络中的分布很重要,你可以参考课程提供的讲座幻灯片的第 46-62 页。
假设我们在一个层上有一批激活 x ,零均值单位方差版本x的 x̂ 为
这实际上是一个可微分的操作,这就是为什么我们可以在训练中应用批量标准化。
在实现中,我们在完全连接层或卷积层之后,非线性层之前插入批量标准化层。
批处理规范化的正向传递
从原稿批量规格化转换的算法
让我们从最初的研究论文来看一下要旨。
正如我前面说过的,批处理规范化的整个概念非常容易理解。在计算了一批激活 x 的均值和方差后,我们可以通过要点第三行的运算来归一化 x 。还要注意,在零均值和单位方差约束对于我们的网络太难的情况下,我们引入了可学习的比例和移位参数 γ 和 β 。
所以向前传递的代码看起来像这样:
需要注意的一点是,均值和方差的估计取决于我们发送到网络中的小批量,我们不能在测试时这样做。因此,测试时归一化的均值和方差实际上是我们在训练期间计算的值的移动平均值。**
这也是为什么批量规格化具有规格化效果。我们在训练时添加了某种随机性,并在测试时平均掉这种随机性,以减少泛化错误(就像辍学的效果一样)。
这是我对这门课作业 2 中向前传球的完整实现:
反向传播
现在我们想推导出一种计算批量标准化梯度的方法。具有挑战性的是,本身是 x 和 σ 是 μ 和 x 的函数。因此,当我们对这个归一化函数执行链式法则时,我们需要非常小心和清楚。
我发现参加课程时非常有用的一件事是计算图的概念。它将一个复杂的函数分解成几个小而简单的操作,并帮助您以一种整洁、有组织的方式执行反向传播(通过推导每个简单操作的局部梯度并将它们相乘以获得结果)。
Kratzert 的帖子详细解释了使用计算图计算批量归一化梯度的每个步骤。查看一下,了解更多。
在 Python 中,我们可以编写这样的代码:
这种分阶段计算的一个缺点是,由于我们计算了大量的“中间值”,这些中间值在相乘时可能会被抵消,因此导出最终梯度需要更长的时间。为了让一切都变得更快,我们需要自己对函数求导来得到一个简单的结果。
当我写这篇文章的时候,我从 Kevin 的博客上找到了一篇的帖子,讲述了通过链式法则获得梯度的每一个步骤。它已经非常清楚地解释了细节,所以如果你对推导感兴趣,请参考它。
最后,这是我的实现:
摘要
在本文中,我们了解了批处理规范化如何提高收敛以及为什么批处理规范化作为一种规范化。我们还在 python 中实现了前向传递和反向传播,用于批处理规范化。
虽然你可能不需要担心实现,因为所有的东西都已经在那些流行的深度学习框架中了,但我始终相信,自己做事情可以让我们有更好的理解。希望你看完这篇文章后有所收获!
在 TensorFlow 中实现胶囊网络
原文:https://towardsdatascience.com/implementing-capsule-network-in-tensorflow-11e4cca5ecae?source=collection_archive---------11-----------------------
胶囊网络实现及其特性可视化指南
在 Unsplash 上的照片
我们很清楚,卷积神经网络(CNN)在许多计算机视觉任务中已经超过了人类。所有基于 CNN 的模型具有相同的卷积层的基本架构,其后是具有中间批次归一化层的池层,用于在正向传递中归一化批次并控制反向传递中的梯度。
然而,在 CNN 中有几个缺点,主要是最大池层,因为它没有考虑具有最大值的像素与其直接邻居之间的关系。为了解决这个问题,Hinton 提出了胶囊网络的概念和一种叫做“胶囊间动态路由的算法。许多资源已经解释了模型的直觉和架构。你可以在这里的系列博文里看看。
在这篇文章中,我解释了模型的实现细节。它假设对 Tensors 和 TensorFlow 自定义层和模型有很好的理解。
这一职位的结构如下:
- 基本张量流运算
- 胶囊层类别
- 杂项详细信息
- 结果和特征可视化
张量流运算
在 TensorFlow 2.3 中使用函数式 API 或顺序模型构建模型非常容易,只需几行代码。然而,在这个 capsule 网络实现中,我们利用了函数式 API 以及一些自定义操作,并用@tf.function
对它们进行了优化。在本节中,我将重点介绍针对更高维度的tf.matmul
函数。如果您对此很熟悉,那么您可以跳过这一部分,进入下一部分。
tf.matmul
对于 2D 矩阵,matmul 运算执行矩阵乘法运算,前提是考虑形状签名。然而,对于秩为(r > 2)的张量,该运算变成 2 个运算的组合,即,元素式乘法和矩阵乘法。
对于一个秩(r = 4)的矩阵,它首先沿轴= [0,1]进行广播,并使每个矩阵形状相等。并且当且仅当第一张量的最后一个维度和第二张量的倒数第二个维度应该具有匹配的维度时,最后两个轴([2,3])经历矩阵乘法。下面的例子将解释它,为了简洁起见,我只打印了形状,但是可以在控制台上随意打印和计算数字。
>>> w = tf.reshape(tf.range(48), (1,8,3,2))
>>> x = tf.reshape(tf.range(40), (5,1,2,4))
>>> tf.matmul(w, x).shape
TensorShape([5, 8, 3, 4])
w 沿轴=0 传播,x 沿轴=1 传播,其余两维矩阵相乘。我们来看看 matmul 的 transpose_a/transpose_b 参数。在张量上调用 tf.transpose 时,所有的维度都是相反的。举个例子,
>>> a = tf.reshape(tf.range(48), (1,8,3,2))
>>> tf.transpose(a).shape
TensorShape([2, 3, 8, 1])
所以让我们看看它是如何工作的
>>> w = tf.ones((1,10,16,1))
>>> x = tf.ones((1152,1,16,1))
>>> tf.matmul(w, x, transpose_a=True).shape
TensorShape([1152, 10, 1, 1])
等等!!!我原以为会有错误,但结果很好。怎么会???
TensorFlow 首先沿着前两个维度广播,然后假设它们是一堆 2D 矩阵。你可以把它想象成只应用于第一个数组的最后两个维度的转置。转置操作后第一个数组的形状是1152,10,1,16,现在应用矩阵乘法。顺便说一下,transpose_a = True
表示上述转置操作将应用于 matmul 中提供的第一个元素。更多细节请参考文档。
好吧!!这就足够打通这个帖子了。我们现在可以检查胶囊层的代码。
胶囊层类别
让我们看看代码中发生了什么。
注:所有超参数的使用与论文中的相同。
卷积运算
我们已经使用tf.keras
功能 API 来创建主要的胶囊输出。这些只是在输入图像input_x
的正向传递中执行简单的卷积运算。到目前为止,我们已经完成了 256 幅(32 * 8)的地图,每幅的尺寸都是 6 x 6。
现在,我们不再将上面的特征图可视化为卷积输出,而是将它们重新想象为沿最后一个轴堆积的 32- 6 x 6 x 8 个向量。因此,我们可以很容易地获得 6 * 6 * 32 = 1152,8D 个向量,只需对它们进行整形。这些向量中的每一个都乘以一个权重矩阵,该权重矩阵封装了这些较低级特征和较高级特征之间的关系。主胶囊层中输出要素的维度为 8D,数字胶囊层的输入维度为 16D。所以基本上我们必须用一个 16×8 的矩阵乘以它们。好吧,那很简单!!但是等等,在主胶囊中有 1152 个向量,这意味着我们将有 1152–16 x 8 个矩阵。
我们现在没事了吗??不,你忘了胶囊的数字
我们在下一层有 10 个数字胶囊,因此我们将有 10 个这样的 1152–16 x 8 矩阵。所以基本上我们得到一个形状为[1152,10,16,8]的权重张量。主胶囊输出的 1152–8D 向量中的每一个都对 10 个数字胶囊中的每一个有贡献,因此我们可以简单地对数字胶囊层中的每个胶囊使用相同的 8D 向量。更简单地说,我们可以在 1152,8D 向量中添加一个 2 轴,从而将它们转换成[1152,1,8,1]的形状。好吧!我知道你在那里做了什么,你要去你上面描述的tf.matmul
广播。
太好了!!没错。
准备数字胶囊的输入
注意:变量 W 的形状在第一个轴上有一个额外的维度 1,因此必须为整个批次广播相同的重量。
在u_hat
中,最后一个维度是无关的,是为了矩阵乘法的正确性而添加的,因此现在可以使用挤压功能将其删除。以上形状中的(无)是针对batch_size
的,它是在训练时确定的。
删除无关尺寸
让我们进入下一步。
动态路由——这就是神奇的开始!
挤压功能- 纸张
在探索算法之前,让我们先创建挤压函数,并保留它以备将来使用。我增加了一个很小的ε值,以避免梯度爆炸,以防分母总和为零。
挤压功能的代码。
在这一步中,数字胶囊的输入是 16D 向量(u_hat
),路由迭代的次数(r = 3)按照论文的规定使用。
来自论文的路由算法
在动态路由算法中没有太多的调整,代码几乎是本文中算法的直接实现。看看下面的片段。
动态路由
应该强调一些关键点。
c
表示u_hat
值的概率分布,对于主胶囊层中的特定胶囊,其总和为 1。简单地说,u_hat
的值根据路由算法中训练的变量 c 分布在数字胶囊中。- σcij | j | I是输入到数字胶囊的所有低级向量的加权和。由于有 1152 个较低级别的向量,所以 reduce_sum 函数应用于该维度。设置
keep_dims=True
,只是使进一步的计算更容易。 - 挤压非线性被应用于数字胶囊的 16D 向量,以归一化这些值。
- 下一步有一个微妙的实现,计算数字胶囊层的输入和输出之间的点积。这个点积决定了较低和较高水平胶囊之间的“协议”。你可以在这里了解这一步背后的推理和直觉。
上述循环迭代 3 次,由此获得的 v 值然后用于重构网络中。
哇!!太好了。你刚刚完成了大部分困难的部分。现在,相对简单。
重建网络
重建网络是一种从数字胶囊层的特征重新生成图像的正则化器。反向传播会对整个网络产生影响,从而使特征有利于预测和再生。在训练过程中,模型使用输入图像的实际标签将数字大写值屏蔽为零,与标签对应的数字大写值除外(如下图所示)。
重构网络来自论文
来自上述网络的v
张量的形状为(无,1,10,16),我们沿着数字帽层的 16D 向量进行广播和标记,并应用遮罩。
注:一个热编码标签用于屏蔽。
这个v_masked
然后被发送到重建网络,并且用于整个图像的再生。重建网络只是下面要点中所示的 3 个密集层
特征重构
我们将把上面的代码转换成一个继承自tf.keras.Model
的 CapsuleNetwork 类。您可以将该类直接用于您的自定义训练循环和预测。
胶囊网络类
你可能已经注意到,我添加了两个不同的函数predict_capsule_output()
和regenerate_image()
,它们分别预测数字大写向量和重新生成图像。第一个函数将有助于预测测试期间的数字,第二个函数将有助于根据一组给定的输入要素重新生成图像。(将在可视化中使用)
模型使用的参数
最后还有一件事,那就是损失函数。本文使用边际损失进行分类,并使用权重为 0.0005 的平方差进行重建,以重建损失。参数 m+、m-、λ在上面的要点中描述,损失函数在下面的要点中描述。
利润损失来自的论文
损失函数实现
v
是未屏蔽的数字大写向量,y
是标签的 one_hot_encoded 向量,y_image
是作为输入发送到模型的实际图像。安全范数函数只是一个类似于张量流范数函数的函数,但包含一个ε以避免值变为精确的 0。
安全范数函数
让我们检查一下模型的概要。
模型概述
恭喜你!!!我们已经完成了模型架构。该模型具有 8215568 个参数,这证实了论文中他们所说的重建模型具有 8.2M 个参数。不过这个 博客 有 8238608 参数。差异的原因是 TensorFlow 仅考虑可训练参数中的tf.Variable
资源。如果我们认为 1152 * 10 b 和 1152 * 10 c 是可训练的,那么我们得到相同的数字。
8215568 + 11520 + 11520 = **8238608**
这是同一个数字。耶!!
杂项详细信息
我们将使用tf.GradientTape
来寻找梯度,我们将使用 Adam 优化器。
训练步骤
因为我们已经用tf.keras.Model
子类化了类,我们可以简单地调用model.trainable_variables
并应用渐变。
预测函数
我制作了一个自定义的预测函数,它将输入图像和模型作为参数。将模型作为参数发送的目的是检查点模型可以在以后用于预测。
唷!!!我们完了。恭喜你!
所以,你现在可以试着用这个解释写你的代码,或者在我的库上使用它。你可以简单地在本地系统或 google colab 上运行笔记本。为了仅获得预测精度,甚至 10 个时期就足够了。在存储库中,我只添加了一个笔记本来训练 50 个纪元的特性。但是,为了调整和可视化特征,您可能需要训练它们多达 100 个历元。
注:模型的训练即使在 Google Colab 的 GPU 上也需要花费大量的时间。所以让模型接受训练,休息一下。
结果和特征可视化
该模型的训练准确率为 99%,测试准确率为 98%。然而,在一些检查站,准确率为 98.4%,而在其他一些检查站,准确率为 97.7%。
在下面的要点中,index_
表示测试集中的特定样本号,index
表示样本y_test[index_]
所代表的实际数目。
每个提取特征的重建图像
下面的代码调整了每个特性,并在[-0.25,0.25]的范围内以 0.05 的增量调整它们。在每一点,图像被生成并存储在一个数组中。因此,我们可以看到每个特征是如何有助于图像的重建。
调整用于重建的特征
请看下图中的一些重建样本。正如我们所见,一些特征控制亮度、旋转角度、厚度、倾斜等。
通过调整特征重建图像(图片由作者提供)
结论
在这篇文章中,我们试图重现的结果,以及可视化的特点,在论文中描述的。训练准确率为 99%,测试准确率接近 98%,这真是太棒了。虽然,模型需要大量的时间来训练,但功能非常直观。
Github 库:https://github.com/dedhiaparth98/capsule-network
参考
南 Sabour,N. Frost,G. Hinton,胶囊间动态路由 (2017),arXiv。
[## 理解辛顿的胶囊网络。第一部分:直觉。
理解 Hinton 的胶囊网络系列的一部分:
medium.com](https://medium.com/ai³-theory-practice-business/understanding-hintons-capsule-networks-part-i-intuition-b4b559d1159b) [## 理解辛顿的胶囊网络。第二部分:胶囊如何工作。
理解 Hinton 的胶囊网络系列的一部分:
medium.com](https://medium.com/ai³-theory-practice-business/understanding-hintons-capsule-networks-part-ii-how-capsules-work-153b6ade9f66) [## 理解辛顿的胶囊网络。第三部分:胶囊之间的动态路由。
理解 Hinton 的胶囊网络系列的一部分:
medium.com](https://medium.com/ai³-theory-practice-business/understanding-hintons-capsule-networks-part-iii-dynamic-routing-between-capsules-349f6d30418) [## 理解辛顿的胶囊网络。第四部分:CapsNet 架构
理解 Hinton 的胶囊网络系列的一部分:
medium.com](https://medium.com/ai³-theory-practice-business/part-iv-capsnet-architecture-6a64422f7dce) [## ageron/handson-ml
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/ageron/handson-ml/blob/master/extra_capsnets.ipynb)
因果推理:试图理解为什么的问题
原文:https://towardsdatascience.com/implementing-causal-inference-a-key-step-towards-agi-de2cde8ea599?source=collection_archive---------0-----------------------
你为什么阅读这篇文章?你为什么选择学习因果推理?你为什么认为这是一个非常奇怪的文章开头方式?谁知道呢。一个更有意思的问题是,为什么我们作为人类,首先能够思考和理解“为什么”这个问题!?如果我们曾经想要创造一个具有人工一般智能的系统,或者 AGI,我们需要回答这个问题。
理解为什么需要理解什么,在哪里,什么时候。然而,“如何”似乎是“为什么”的一种实现。图片来源。
你和这个星球上的每个人都能够理解因果关系,这种能力在很大程度上仍然是机器所缺乏的。在我们可以考虑创建一个可以普遍理解因果关系的系统之前,我们应该从统计学的角度来看因果关系:因果演算和因果推理。统计学是因果关系诞生的地方,为了创建一个高层次的因果系统,我们必须回到根本。
因果推断是从数据中推断原因的过程。任何种类的数据,只要有足够的数据。(是的,甚至是观测数据)。这听起来很简单,但也可能变得复杂。作为人类,我们每天都在这样做,我们用从因果推理中学到的知识在世界上导航。我们不仅使用因果推理来导航世界,我们还使用因果推理来解决问题。
每年有 1500 万早产儿出生。图片来源。
每年有 110 万早产儿死亡。换句话说,每年有 7.3%的早产儿死亡。成千上万的父母不得不在失去孩子的悲痛、折磨和痛苦中挣扎,因为这是他们无法控制的过程。这是个问题。假设我们想弄清楚早产儿出生后的综合治疗是否会影响其存活机会。为了解决这个问题,我们需要使用因果推断。
我们将用来执行因果推理来解决这个问题的 python 库叫做,这是由微软的研究人员创建的一个记录良好的库。
关于因果关系的快速课程
首先是因果关系的快速课(如果你已经了解了基础知识,可以跳过这一节;如果你更喜欢看视频,幸运的你,我做了一个,你可以在这里看)。
因果关系是关于干预,关于做。标准统计都是关于相关性的,这很好也很有趣,但是相关性会导致错误的假设,从而导致更糟糕的事情。**
这是一个图表,显示了运动和胆固醇之间的相关关系(这看起来像是因果关系,但不是)。如果我们只看胆固醇和运动之间的相关关系,这两者之间似乎存在因果关系。但是这种相关性实际上发生了,因为胆固醇和锻炼有一个共同的原因或混杂因素:年龄。图片来源。**
在相关性中,符号是 P(x|y ),即给定 y 时 x 的概率:例如,给定活性基因时疾病的概率。然而,在因果演算中,做了一个非常小但重要的改变。不是 P(x|y)而是 P(x|do(y))* 即给定 y 的 x 的概率:例如,给定我开始节食的疾病概率。“做”是非常重要的:它代表干预,实际做一些会产生效果的事情。***
如果这个“做”的东西仍然没有太多意义,让我带你看另一个例子:
带上气压和气压计。气压计上的读数和气压之间存在相关性,但是,在标准的相关性(P(x|y))中,我们无法判断哪一个导致了哪一个。然而,如果我们把它切换到因果演算,或者称为 do-calculus(是的,do 无处不在),我们可以问这样一个问题,“给定压力增加,高气压计读数的概率是多少?”增加压力是做的行为,通过做和干预,可以看出两个变量之间是否有明确的因果关系。(显然,如果我们增加压力,我们会看到气压计读数增加)。**
反之亦然。如果我们改变气压计上的读数(通过扭转旋钮或其他什么,这是做的一个动作),我们不会看到气压的变化,因为气压计读数不会引起气压。气压影响气压计的读数。**
气压->气压计读数。出处。
然而,气压和气压计的例子非常简单;只有两个因素。在现实生活中,有数不清的因素,每个因素都与其他因素有某种因果关系。
因果图的一个例子。图片来源。
在图中,“交通堵塞”是“上学迟到”和“排长队”的混杂或常见原因。混杂因素是指与我们想要测试因果关系的两个变量之间存在因果关系的变量。如果我们想测试“上学迟到”和“排长队”之间的因果关系,我们必须考虑“交通堵塞”,以确保“上学迟到”和“排长队”之间因果关系的有效性,就像上面胆固醇的例子一样。*在因果推断中,我们总是需要考虑混杂因素,因为它们引入了混淆因果图的相关性。*******
IHDP 数据集
好了,现在我们对基本的因果关系有了很好的理解,让我们实际上进入代码并测试早产双胞胎的健康和干预之间的因果关系。我们将使用婴儿健康和发展项目(IHDP)的数据集,该数据集收集了 1985 年至 1988 年美国随机试验中早产儿的数据。随机化是关键,因为它提供了对世界的无偏见的描述。因为这些数据是在 RCT 中收集的,所以没有必要进行因果推断,但是我们仍然会进行因果推断来展示它是如何工作的。
1985 年至 1988 年期间,在八个不同的地点进行了从出院到 36 个月校正年龄的强化干预。婴儿研究样本按出生体重(2000 克或以下,2001-2500 克)分层,并按随机* 分组至干预组或随访组。干预组接受家访、特殊儿童发展中心的护理和儿科随访。随访组仅接受该计划的儿科随访部分。在预定的时间点从两组中收集认知发展、行为状态、健康状态和其他变量的测量值。数据收集中的许多其他变量和指数包括地点、妊娠并发症、儿童出生体重和妊娠年龄、出生顺序、儿童性别、家庭组成、日托安排、卫生保健来源、家庭环境质量、父母的种族和民族、母亲年龄、教育、智商和就业。——摘自 HMCA 档案 。***
代码
首先,让我们导入所需的包并加载数据。
***import dowhy
from dowhy import CausalModel
import pandas as pd
import numpy as npdata= pd.read_csv(“[https://raw.githubusercontent.com/AMLab-Amsterdam/CEVAE/master/datasets/IHDP/csv/ihdp_npci_1.csv](https://raw.githubusercontent.com/AMLab-Amsterdam/CEVAE/master/datasets/IHDP/csv/ihdp_npci_1.csv)", header = None)col = [“treatment”, “y_factual”, “y_cfactual”, “mu0”, “mu1” ,]for i in range(1,26):
col.append(“x”+str(i))data.columns = col
data = data.astype({“treatment”:’bool’}, copy=False)
print(data.head())
____________________________________________________________________ treatment y_factual y_cfactual mu0 … x22 x23 x24 x250 True 5.599916 4.318780 3.268256 … 0 0 0 01 False 6.875856 7.856495 6.636059 … 0 0 0 02 False 2.996273 6.633952 1.570536 … 0 0 0 03 False 1.366206 5.697239 1.244738 … 0 0 0 04 False 1.963538 6.202582 1.685048 … 0 0 0 0***
这里的治疗就是干预。Y_factual 是通过结合婴儿的精神、行为和健康状况来量化的结果。所有的 x(x1 到 x25)都是结果和干预的混杂因素:如性别、种族、家庭护理质量等变量。我们试图找出治疗和结果之间的因果关系,同时考虑混杂因素。(从技术上来说,我们不必考虑这些混杂因素,因为这些数据是通过随机试验收集的,任何由此产生的偏见都将被消除。然而,考虑它们仍然是一个好主意,并且当数据是观察性的时,考虑它们是绝对必要的)。
我们不关心 y_cfactual、mu0 和 mu1(它们被代码中链接的 GitHub 的创建者使用(一个关于因果效应变化自动编码器的超级酷的项目,或 CEVAE ,你应该完全检查一下))
如果你对它们感兴趣:
y_cfactual 是一个反事实,是一个关于没有发生的事情的问题,比如“如果我…,会发生什么?“在这种情况下,这是一个关于如果有或没有干预(取决于上下文)会发生什么的预测。反事实在因果关系中极其重要,因为大多数时候我们并不总能得到所有的数据。例如,如果我们想在一个人身上测试两种不同治疗方法的有效性,我们不可能同时测试两种方法。反事实解决了“想象的”治疗,但实际上并没有实施,而我们,作为人类,一直在使用反事实(每当你想象另一种情况时)。如果你对它们更感兴趣,在这里阅读这篇伟大的博文。**
Mu0 和 mu1 是条件均值,换句话说,y_factual 在处理和不处理时的期望值或平均值。GitHub 的创建者使用这些变量(y_cfactual、mu0 和 mu1)来测试 CEVAE 的强度。
好了,现在我们有了所有的数据设置,以一种便于因果推理的方式组织起来。是时候实际做因果推断了。
用 DoWhy 进行因果推断!
DoWhy 将因果推理分解为四个简单的步骤:建模、识别、估计和反驳。在进行因果推理时,我们将遵循这些步骤。**
模型
***# Create a causal model from the data and given common causes.
xs = ""
for i in range(1,26):
xs += ("x"+str(i)+"+")model=CausalModel(
data = data,
treatment='treatment',
outcome='y_factual',
common_causes=xs.split('+')
)***
这段代码实际上获取了数据,并利用这些数据建立了一个因果模型或因果图。
由因果模型的道伊创建的(视觉上令人不快的)图形
即使我们没有打印出任何东西,DoWhy 仍然会给我们关于因果推理的警告和信息更新(这对初学者来说非常好)。在这种情况下,它给我们:
***WARNING:dowhy.causal_model:Causal Graph not provided. DoWhy will construct a graph based on data inputs.
INFO:dowhy.causal_model:Model to find the causal effect of treatment ['treatment'] on outcome ['y_factual']***
识别
***#Identify the causal effect
identified_estimand = model.identify_effect()
print(identified_estimand)***
识别步骤使用从建模步骤创建的因果图,并识别所有的因果关系。这段代码打印出来:
***INFO:dowhy.causal_identifier:Common causes of treatment and outcome:['', 'x6', 'x3', 'x14', 'x10', 'x16', 'x9', 'x17', 'x13', 'x4', 'x11', 'x1', 'x7', 'x24', 'x25', 'x20', 'x5', 'x21', 'x2', 'x19', 'x23', 'x8', 'x15', 'x18', 'x22', 'x12']WARNING:dowhy.causal_identifier:If this is observed data (not from a randomized experiment), there might always be missing confounders. Causal effect cannot be identified perfectly.WARN: Do you want to continue by ignoring any unobserved confounders? (use proceed_when_unidentifiable=True to disable this prompt) [y/n] yINFO:dowhy.causal_identifier:Instrumental variables for treatment and outcome:[]Estimand type: nonparametric-ate### Estimand : 1Estimand name: backdoorEstimand expression:d(Expectation(y_factual|x6,x3,x14,x10,x16,x9,x17,x13,x4,x11,x1,x7,x24,x25,x20,x5,x21,x2,x19,x23,x8,x15,x18,x22,x12)) / d[treatment]Estimand assumption 1, Unconfoundedness: If U→{treatment} and U→y_factual then P(y_factual|treatment,x6,x3,x14,x10,x16,x9,x17,x13,x4,x11,x1,x7,x24,x25,x20,x5,x21,x2,x19,x23,x8,x15,x18,x22,x12,U) = P(y_factual|treatment,x6,x3,x14,x10,x16,x9,x17,x13,x4,x11,x1,x7,x24,x25,x20,x5,x21,x2,x19,x23,x8,x15,x18,x22,x12)### Estimand : 2Estimand name: ivNo such variable found!***
因为我们没有试图找到 2 个因果关系(只是试图找到干预对结果的影响),所以没有找到 estimand 2。
估计
使用 estimand(确定的因果关系),我们现在可以估计这种因果关系的强度。有许多方法可供我们使用(基于倾向的匹配,加性噪声建模),但在本教程中,我们将坚持使用传统的线性回归。
***# Estimate the causal effect and compare it with Average Treatment Effectestimate = model.estimate_effect(identified_estimand, method_name="backdoor.linear_regression", test_significance=True)print(estimate)print("Causal Estimate is " + str(estimate.value))data_1 = data[data["treatment"]==1]
data_0 = data[data["treatment"]==0]
print("ATE", np.mean(data_1["y_factual"])- np.mean(data_0["y_factual"]))***
使用线性回归,该模型对原因(干预)和结果(早产儿的健康)进行训练,并确定两者之间关系的强度。我还打印出了 ATE(平均治疗效果(接受治疗的一组与未接受治疗的一组的平均值之间的差异))。在这种情况下,ATE 是一个很好的指标/数字,我们可以用它来检查因果关系,因为数据是在随机试验中检索的,而不是从观察数据中检索的。观察数据会有 ATE 无法解释的未观察到的混杂因素(影响治疗和结果的因素)或工具变量(仅影响治疗并因此与结果相关的因素)。ATE 与通过线性回归发现的因果效应不同,因为它没有考虑混杂因素。
***INFO:dowhy.causal_estimator:INFO: Using Linear Regression EstimatorINFO:dowhy.causal_estimator:b: y_factual~treatment+x6+x3+x14+x10+x16+x9+x17+x13+x4+x11+x1+x7+x24+x25+x20+x5+x21+x2+x19+x23+x8+x15+x18+x22+x12*** Causal Estimate ***--prints out the estimand again––## Realized estimandb:y_factual~treatment+x6+x3+x14+x10+x16+x9+x17+x13+x4+x11+x1+x7+x24+x25+x20+x5+x21+x2+x19+x23+x8+x15+x18+x22+x12## EstimateValue: 3.928671750872714## Statistical Significancep-value: <0.001Causal Estimate is 3.928671750872714ATE 4.021121012430832***
如你所见,ATE 非常接近我们得到的因果估计。大约 4 是接受护理的早产儿与未接受护理的早产儿在健康方面的差异。数字 4 在语义上没有太多意义,因为它是两组之间结果的字面差异,在这种情况下,结果是各种其他因素的组合。如果你回头看一下打印出来的 5 个案例,两者的差异大约是 4。
驳斥
反驳步骤测试由估计步骤发现的因果效应的强度和有效性。有各种不同的反驳方法,例如子集验证(仅使用数据的子集来估计因果关系)或安慰剂治疗(将治疗变成安慰剂并观察其对结果的影响(安慰剂治疗反驳预计因果关系会下降))在这种情况下,我们将添加一个不相关的共同原因来测试治疗和结果之间的因果关系的强度。这是有用的,因为它改变了因果模型,但没有改变 y_factual 和 treatment 之间的关系。
***refute_results=model.refute_estimate(identified_estimand, estimate,
method_name="random_common_cause")
print(refute_results)
____________________________________________________________________INFO:dowhy.causal_estimator:INFO: Using Linear Regression EstimatorINFO:dowhy.causal_estimator:b: y_factual~treatment+x22+x4+x24+x7+x20+x19+x21+x1+x23+x2+x12+x18+x3+x10+x14+x11+x16+x9+x13+x5+x17+x15+x6+x8+x25+w_randomRefute: Add a Random Common CauseEstimated effect:(3.928671750872715,)New effect:(3.9280606136724203,)***
添加一个随机的共同原因对因果关系没有太大的影响(如预期的那样),所以我们可以更确定因果关系的强度。
好的,我们刚刚发现在干预早产儿和他们的健康之间有明确的因果关系。在未来,我们要的不是像这样在狭窄的例子中进行因果推断,而是一个不需要太多数据就能大致理解因果关系的系统。目前,我们甚至还不明白大脑是如何理解因果关系的,但既然我们已经从统计学的角度理解了如何进行因果推断,我们就能更好地应对这个问题。为了创建人工通用智能,因果关系需要被普遍理解和实现,我希望在不久的将来从事这样的项目!**
我希望你喜欢这篇文章,并理解因果关系的力量以及如何用 DoWhy 进行因果推断!如果你有任何问题或想要交谈,请随时在 kevn.wanf@gmail.com 给我发电子邮件或通过LinkedIn联系我!**
在 Keras 中实现自定义数据生成器
原文:https://towardsdatascience.com/implementing-custom-data-generators-in-keras-de56f013581c?source=collection_archive---------7-----------------------
如何实现自定义数据生成器以在 Keras 模型中启用动态数据流
亚历山大·辛恩在 Unsplash 上的照片
数据生成器是 Keras API 最有用的特性之一。考虑这样一种情况,您有大量的数据,多到无法一次将所有数据都存储在 RAM 中。Wyd?购买更多的内存显然不是一个选项。
这个问题的解决方案可以是动态地装载小批量数据给模型。这正是数据生成器的工作。它们可以动态生成模型输入,从而形成从存储器到 RAM 的管道,以便在需要时加载数据。这种管道的另一个优点是,在准备向模型提供数据时,可以很容易地对这些小批量数据应用预处理例程。
在本文中,我们将看到如何子类化 tf.keras.utils.Sequence 类来实现定制数据生成器。
图像数据生成器
首先,我们现在将了解如何使用 ImageDataGenerator API 进行动态图像流水线操作,从而满足实现自定义 API 的需求。
datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
ImageDataGenerator API 提供了从目录以及数据帧中提到的路径中管道传输图像数据的功能。其中一个可能包括预处理步骤,如图像的缩放、增强,这些步骤将直接实时应用于图像。
那么,为什么要定制呢?
模型训练不限于单一类型的输入和目标。有时一个模型会同时被输入多种类型的输入。例如,假设您正在处理一个多模态分类问题,您需要同时处理文本和图像数据。在这里,显然不能使用 ImageDataGenerator。而且,一次加载所有数据是不可承受的。因此,我们通过实现自定义数据生成器来解决这个问题。
实现自定义数据生成器
我们最后从实现开始。
这将是一个非常通用的实现,因此可以直接复制。你只需要用自己的逻辑来填空/替换某些变量。
如前所述,我们将子类化TF . keras . utils . sequenceAPI。
def __init__(
self,
df,
x_col,
y_col=None,
batch_size=32,
num_classes=None,
shuffle=True
):
self.batch_size = batch_size
self.df = dataframe
self.indices = self.df.index.tolist()
self.num_classes = num_classes
self.shuffle = shuffle
self.x_col = x_col
self.y_col = y_col
self.on_epoch_end()
首先,我们定义构造函数来初始化生成器的配置。注意,这里我们假设数据的路径在 dataframe 列中。因此,我们定义 x_col 和 y_col 参数。这也可以是您可以从中加载数据的目录名。
on_epoch_end 方法是在每个 epoch 之后调用的方法。我们可以在这里加入洗牌之类的套路。
def on_epoch_end(self):
self.index = np.arange(len(self.indices))
if self.shuffle == True:
np.random.shuffle(self.index)
基本上,我们在这个代码片段中打乱了数据帧行的顺序。
我们的另一个实用方法是 len 。它实际上使用样本和批量大小返回一个时期中的步骤数。
def __len__(self):
**# Denotes the number of batches per epoch**
return len(self.indices) // self.batch_size
接下来是 getitem 方法,使用批号作为参数调用该方法以获得给定的一批数据。
def __getitem__(self, index):
**# Generate one batch of data
# Generate indices of the batch**
index = self.index[index * self.batch_size:(index + 1) * self.batch_size] **# Find list of IDs**
batch = [self.indices[k] for k in index] **# Generate data**
X, y = self.__get_data(batch)
return X, y
基本上,我们只是获得混洗的索引,并从不同的方法调用数据集,然后将其返回给调用者。数据集生成的逻辑可以在这里自己实现。但是,将它抽象到其他地方是一个很好的做法。
最后,我们在 __ get_data 方法中编写数据生成的逻辑。既然这个方法要被我们调用,我们可以随便取什么名字。此外,这个方法没有理由是公共的,因此我们将其定义为私有的。
def __get_data(self, batch): **# X.shape : (batch_size, *dim)
# We can have multiple Xs and can return them as a list** X = **# logic to load the data from storage** y = **# logic for the target variables** **# Generate data**
for i, id in enumerate(batch):
**# Store sample**
X[i,] = **# logic** **# Store class**
y[i] = **# labels**
return X, y
此外,我们可以添加预处理/增强例程来实时启用它们。在上面这段代码中,X 和 y 是根据方法中传递的批处理索引参数从数据源加载的。这可以是从加载图像到加载文本或同时加载两者或任何其他类型的数据。
合并所有方法后,完整的生成器如下所示:
完整的自定义数据生成器类
结论
在本文中,我们看到了数据生成器在用大量数据训练模型时的用处。我们查看了 ImageDataGenerator API,以了解它是什么,并解决对自定义 API 的需求。然后,我们最终了解了如何通过子类化TF . keras . utils . sequenceAPI 来实现定制数据生成器。
请随意复制这段代码,并在其中添加您自己的生成器逻辑。
参考
[## TF . keras . utils . sequence | tensor flow Core v 2 . 2 . 0
通过 TensorFlow 学习 ML 基础知识的教育资源
www.tensorflow.org](https://www.tensorflow.org/api_docs/python/tf/keras/utils/Sequence) [## TF . keras . preprocessing . image . imagedata generator
通过 TensorFlow 学习 ML 基础知识的教育资源
www.tensorflow.org](https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image/ImageDataGenerator) [## 使用 Keras 的数据生成器的详细示例
python keras 2 fit _ generator Afshine Amidi 和 Shervine Amidi 的大型数据集多重处理您是否曾经不得不…
stanford.edu](https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly)
实施数据诊所将节省时间并完成更多工作
原文:https://towardsdatascience.com/implementing-data-clinics-will-save-time-and-get-more-done-e1f003e1c5ee?source=collection_archive---------51-----------------------
数据团队生产力
这个策略将帮助你的团队更好地完成你的临时工作和计划工作
数据诊所的标准周,内部利益相关者可以提出他们的数据请求。当时,我们实际上是通过视频电话运行所有的数据诊所,但标准是尽可能在厨房运行。
数据诊所是我们团队每天留出的时间,用于与利益相关者就任何预约请求进行合作,为数据团队的时间提供了最佳的投资回报。这是因为,平均而言,这些是我们在一周内完成的复杂度最低但效率最高的任务。
- 通过回答销售团队的关键数据问题,我们可以帮助达成交易。
- 通过将产品经理导航到正确的数据集,我们可以消除功能开发的障碍。
- 通过验证医疗操作实验的效果,我们可以帮助改善我们平台上成千上万患者的结果。
在我们有数据诊所之前,我们的工作到处都是:一天完全专注于临时项目,第二天完全专注于长期项目。我们没有适当的系统来管理小的请求,就我个人而言,当许多天没有按计划进行时,我感到沮丧——即使我完成的工作是有价值的!
我们需要一种方法来区分两方面的优先级:临时的和计划的。
在过去两年半的时间里,随着我们将数据团队成员从 1 人扩大到 4 人,将非医疗团队成员从 50 人扩大到 150 人,并将 5K 扩大到 2M 保险覆盖的加拿大人,数据诊所是我们应对不断增长的临时工作需求的方式。
那么,什么是数据诊所呢?
数据诊所——或者对所有非医疗公司来说是办公时间——是一天中你把一周的计划放在一边,欢迎任何类型的数据请求的时候。
数据诊所并不是一个新的想法,这个想法已经被许多公司采用,比如 Ro! 在 Dialogue,我们花了一些时间来完善我们*运营这些诊所的方式——这篇文章不是数据诊所的权威指南,而是我们一路走来所学的收集。
当 Dialogue 开始运营数据诊所时,数据团队只有一个人,Matt 每天根据需要运营 30 分钟的诊所。现在数据团队有四个人,每个团队成员每周运行 2 个一小时的数据诊所,总共 8 个小时的数据诊所,将他们分散开来,这样每天至少有一个。
常见数据诊所请求的一些示例包括:
- 导航至现有指标或数据集
“我们在阿尔伯塔省的患者 NPS 是什么?” - 关于如何最好地使用我们的数据工具的培训
“我如何使用元数据库来了解不同组的等待时间?” - 将业务问题转化为数据问题
“在注册流程中,我们最大的产品机会是什么?” - 对潜在数据项目的战略建议
“我们如何才能更好地了解护理经理团队的人员配备情况?”
目标并不总是完全回答这些问题,但更多的时候,目标是让球滚动起来。在不到 15 分钟的时间里,我该如何让他们更接近自己的目标?或者,如果这是不可能的,我怎样才能让他们有一个实现目标的计划?
在 Dialogue,我们坚信应该将 Scrum 软件开发原则应用到我们的工作中,并且延伸开来,我们相信 80/20 法则就在我们身边:第一点工作,前 20%,将是项目的最高价值,占收益的 80%。当然,并不是所有事情都这么简单——例如,有些项目只有在完全完成后才能带来收益——我们从事的许多项目都遵循这一原则。在上面所有的问题中,花 15 分钟来分析、训练或头脑风暴会有很多好处。
虽然数据团队的大部分时间都花在了大型项目上——这些项目具有更大的不确定性和复杂性以及更高的战略价值——但这是一个全新的机会,可以让我们专注于那些有助于推动进展的小事。
为什么我的公司应该运行数据诊所?
现在介绍完毕,这些是我们经营数据诊所的更具体的原因,也是我们认为你也应该这样做的原因。
1.高杠杆、低复杂性的机会
如上所述,数据诊所非常适合在少量时间内获得大回报。即使任务不能完全完成,或者即使只是到了计划任务的时候,你已经从零到有计划了。这是我们运营数据诊所的首要原因。
2.时间盒中断
干扰对生产力来说是可怕的,数据诊所是我们对这些干扰进行计时的便捷方式。我们没有处理大量的直接消息,而是将它们全部路由到 Slack 上的一个通道,作为数据诊所请求进行异步处理。
对我来说,我知道我在早上的分析任务中效率最高,所以数据诊所是保存时间的好方法,并给我一个更人际化的任务来打破下午的剩余时间。
3.数据需求的反馈回路
当积压的任务被大型项目填满时,它们很容易让人感觉脱节。相比之下,数据诊所让你知道今天的数据需求是什么。我们需要更多的文档吗?我们需要更多的培训吗?某些报道过时了吗?
在某些情况下,数据请求甚至帮助我们理解我们应该如何改变长期计划。数据团队有什么方法可以帮助自动化工作流,从而最大限度地减少这些临时请求?我们需要修改我们的报告来满足新客户的需求吗?
4.它们鼓励降低数据管道的复杂性和可维护性
数据诊所的一个不太明显的好处是,它促使我们的数据团队让非作者更容易维护我们的工作。在数据诊所中,请求者可以就业务的任何部分提出问题,因此根据当天的情况,你可能会回答关于你工作过的主题或你几乎没有接触过的主题的问题。
现在,在审查代码时,我们比以往任何时候都要牢记这一点。这种改变有助于自助服务吗?如果这个坏了的时候你在度假,我们就完了吗?
推而广之,这也是吸纳新团队成员的好方法。数据诊所上的配对是一种很好的方式,可以拓宽但不会太深入数据管道,有助于解释一般方法,而不会太纠缠于细节。
5.更具包容性的团队服务方式
有时候,我们对于一个给定的 sprint,或者甚至一个季度的优先级,偏向于一个或者几个团队。例如,如果我们正在接纳许多新客户,我们将需要在医疗运营和客户成功方面获得更多支持。即使在这样的时代,像 Talent 这样的小部门仍然可以在数据诊所中提出请求。
如何才能有效运行这些?
现在,我们已经运行这些数据诊所两年多了,我们有一些关于什么对我们最有效以及为什么的建议。
1.让他们经常出现——如果可能的话,让他们每天都出现
数据诊所应该经常发生。请求可能是及时的,因此不要在一天内加载所有的数据诊所时间是很重要的。此外,这是一个分解更大任务的好工具,因此它们可以用来帮助管理个人生产力。
2.让他们对你公司的每个人开放
日历,如果可能的话还有空间,应该是公开的,容易被所有人获取和知晓。我们有一个谷歌数据诊所日历,你可以找到它,它会让你知道谁在做数据诊所,什么时候,在哪里。我们还试图让他们保持大致相同的时间(通常是在一天结束时),以帮助最大限度地减少沟通失误,并帮助设定一个问题需要多长时间才能得到回答的预期。
3.在整个数据团队中分担责任
当每个团队成员都尽到了自己的一份责任时,我们的数据诊所就能发挥最大的作用。这有助于我们工作的交叉可维护性,有助于保持对管道各个部分的熟悉,也有助于保持对特定时间和中断的期望。
不幸的是,一些团队成员总是更频繁地被拉进临时项目,但以这些方式构建数据诊所至少可以帮助最小化这些干扰的总体影响。理想情况下,为了计划和内心的平静,干扰和分心应该在整个团队中共享。
4.允许异步和同步请求
我们已经尝试过只面对面做,但没有效果。如果您希望能够处理所有请求,那么您的数据诊所需要足够灵活来处理同步(也就是说,面对面)和异步请求。
为此,我们保持了一个宽松的渠道,任何人都可以给我们留下问题,我们将在我们的数据诊所中做出回应。如果可以,我们有时会在数据诊所期间安排一个时间段来实时处理问题,但有时这是不可能的,我们将在一个线程中处理请求。这有助于确保每个人,不管他们的日程安排如何,都能得到他们需要的帮助。
通过使用公共空间,这也有助于其他团队成员的可见性。你可以标记可能也感兴趣的人,或者你可以标记可能与新问题相关的旧主题。
另一个提示是,为了确保某个问题得到完全解决,一旦请求结束,我们会留下一个✅。扫描频道时,这有助于您识别哪些问题仍未解决。有时我们也会留下👀请求让我们的其他数据团队成员知道,虽然尚未解决,但有人正在研究它。
5.遵守时间限制
这是最难的一个。我们尽最大努力遵守每次请求 15 分钟的时间限制,但有时我们会长达 30 分钟。我们这样做是为了能够服务尽可能多的人,同样,也是为了公平对待我们的其他请求者。
如果我们在一个请求上花了太多时间,那就会占用别人的时间。在这些情况下,重点应该是制定计划,而不是制定可交付成果本身。
我们下一步计划如何改进我们的数据诊所
数据诊所在对话方面做得很好,但总有改进的空间!我们正在考虑的几项改进是如何:
1.根据我们的数据诊所要求收集更多数据
我们希望自动跟踪谁在提出请求,什么类型的请求,以及我们解决这些请求需要多长时间。理想情况下,我们将使用它来改进文档,并在简单的请求被带到数据诊所之前解决它们。
2.促进更多的自助服务,以最大限度地减少数据门诊请求的数量
我们将利用这些数据来确定改善自助服务的最佳机会。这方面的简单机会包括更好地记录关键表格和更好地培训常见的自助主题,我们只需要知道从哪里开始才能产生最大的影响。
3.鼓励代表性不足的部门更多地使用数据诊所
我们还将使用这些数据来确定谁使用数据诊所最少。我们希望鼓励代表性不足的部门提出更多问题,从而从数据团队中获得更多益处。
之前,我们每月都会收到来自 3 个部门的请求——产品、客户成功和医疗运营。现在,随着我们新的规模和改进的数据诊所流程,我们每月都有来自 12 个部门的请求——产品、客户成功、医疗运营、设计、技术、财务、营销、销售、合作伙伴、新服务、机器学习和人才——并且这些部门的每个人都知道他们可以在 24 小时内从数据中获得答案!
然而,到目前为止,最大的好处是数据诊所在深度和广度上都提高了数据素养。人们更有可能在日常生活中使用数据,更有可能理解数据,也更有可能帮助他们的团队成员理解数据。 ******
如果你最终采用了数据诊所,我希望它们能像帮助我们一样帮助你。当然,如果您有任何改进,请告诉我们!
*早在 2017 年秋天,我的同事 Matt 就想出了将我们的办公时间称为“数据诊所”的主意。从那以后,他们改变了很多,我们也从 Ro 这样的例子中学到了很多,但这还是我们的做法:接受对你有用的,改变无用的!
**基于轶事证据,但我们实施了一项数据调查,以帮助我们跟踪这些主题的发展!
实现深度卷积生成对抗网络(DCGAN)
原文:https://towardsdatascience.com/implementing-deep-convolutional-generative-adversarial-networks-dcgan-573df2b63c0d?source=collection_archive---------28-----------------------
我如何使用 DCGAN 从随机数据生成新图像
作者在 MNIST 培训 DCGAN
深度卷积生成对抗网络是 GANs 最基本实现的“镜像版本”。这种架构本质上利用深度卷积神经网络,使用生成器-鉴别器框架从噪声数据中生成属于给定分布的图像。
生成式对抗网络使用生成器网络来生成新的数据样本,使用鉴别器网络来评估生成器的性能。因此,从根本上说,GANs 的新颖之处在于评估器而不是生成器。
这是 GANs 区别于其他生成模型的地方。将生成模型与鉴别模型结合起来就是甘斯的全部内容
— 生成性对抗网络(GANs)综合指南
我已经在的另一篇文章中讨论了 GANs 背后的理论和数学,如果你有兴趣知道 GANs 是如何工作的,可以考虑读一读!
在本文中,我们将使用 TensorFlow 实现 DCGAN,并观察两个著名数据集的结果:
- MNIST 手写数字数据集和
- CIFAR-10 图像识别数据集
加载和预处理数据
在本节中,我们为模型加载和准备数据。
我们从 tensorflow.keras datasets 模块加载数据,该模块提供了一个 load_data 函数,用于获取一些众所周知的数据集(包括我们需要的数据集)。然后,我们将加载的图像归一化为具有从-1 到 1 的值,因为这些图像具有从 0 到 255 的像素值。
为培训准备数据
发电机
发生器模型主要由反卷积层组成,或者更准确地说,由转置卷积层组成,它们基本上执行卷积运算的逆运算。
转置卷积,无填充,无跨越。铝
在相邻的图中,描绘了在 4x4 图像上卷积 3×3 核的转置。
该操作相当于在具有 2x2 零边界的 2x2 图像上卷积 3x3 内核。
深度学习卷积运算指南是目前为止 DL 中涉及卷积运算的最好的论文之一。值得一读!
转到生成器,我们采用一个 128 维向量,并使用一个完全连接的层将其映射到一个 8x8x256 维向量。这个向量被整形为(8,8,256)。这些基本上是 256 个大小为 8×8 的激活图。此外,我们应用几个反卷积层,最终获得一个“大小为 32×32 的 3 通道图像”。这是生成的图像。
发电机模型
鉴别器
鉴别器只不过是一个由几个卷积层组成的二元分类器(像任何其他图像分类任务一样)。最后,展平的激活图被映射到概率输出,以预测图像是真的还是假的。
鉴别器模型
界定损失
由于这是一个二元分类问题,最终的损失函数将是二元交叉熵。然而,这种损失被调整并分别应用于两个网络,以便优化它们的目标。
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
- 发电机损耗
生成器本质上是试图生成鉴别器会认可为真实图像的图像。因此,所有生成的图像必须被预测为“1”(真实的),并且必须因未能这样做而受到惩罚。
发电机损耗
因此,我们训练生成器预测“1”作为鉴别器的输出。
- 鉴频器损耗
与生成器相反,鉴别器希望自己预测生成的输出是假的,同时,它必须预测任何真实的图像是真实的。因此,鉴别器训练这两种损耗的组合。
鉴频器损耗
我们训练鉴别器对生成的图像预测“0”(假的),对来自数据集的图像预测“1”(真的)。
训练 GAN
在训练时期,我们一起处理生成器和鉴别器模型。然而,我们分别应用梯度,因为两种模型的损耗和架构不同。
训练时期
经过训练,我得到了以下结果,
- 关于 MNIST:
作者 50 个时期的结果
- 在 CIFAR-10 上:
作者在 100 个时期的结果
结论
我们看到了如何实现生成式对抗网络。我们用 GANs 的深度卷积风格介绍了这个实现。还有其他风格的 gan 产生条件输出,因此可以证明是非常有用的。
这里的是指向 GitHub 代码库的链接。放心叉吧!
参考
原甘斯论文:https://arxiv.org/abs/1406.2661
DCGAN 论文:https://arxiv.org/abs/1511.06434
GANs 博客:https://towards data science . com/a-comprehensive-guide-to-generative-adversarial-networks-GANs-fcfe 65 D1 cfe 4
本指南中使用的代码来自官方 TensorFlow 文档:
[## 深度卷积生成对抗网络|张量流核心
这个教程演示了如何生成图像的手写数字使用深度卷积生成…
www.tensorflow.org](https://www.tensorflow.org/tutorials/generative/dcgan)
TensorFlow 官方文档:
[## 教程| TensorFlow 核心
完整的端到端示例,帮助 ML 初学者和专家了解如何使用 TensorFlow。尝试谷歌教程…
www.tensorflow.org](https://www.tensorflow.org/tutorials)
在遗传算法中实现基因驱动
原文:https://towardsdatascience.com/implementing-gene-drives-into-genetic-algorithms-d245a5cc989f?source=collection_archive---------46-----------------------
探索基因工程的新技术
人类已经转向大自然去寻找优化的系统,去解决复杂的问题。就遗传算法而言,人类已经在一个程序中模拟了进化,以优化神经网络的权重。
随着最近遗传工程的兴起,我相信遗传算法可以通过这些技术得到改进。在这篇文章中,我将介绍一下基因驱动的实现
什么是基因驱动?
图片来自维基百科。版权所有
基因驱动允许基因以更高的速度传递给下一代生物。看上面的图表:图像的左边代表一个基因从父母传给下一代。由于一半的遗传信息来自父亲,一半来自母亲,所以后代有 50%的机会继承这一基因。
然而,在一种昆虫的情况下,其遗传信息已经被基因驱动改变,该昆虫的所有后代都将获得这种基因,因此允许这种改变影响未来的所有世代。
基因驱动是如何工作的?
在正常的 CRISPR-Cas9 基因工程中,CRISPR 蛋白被编程为针对特定基因,该基因被新的序列取代。
对于基因驱动,过程是不同的。CRISPR 蛋白将自身插入到基因序列中,同时插入目标基因的信息。当序列的这一部分被激活时,CRISPR 蛋白将搜索这个基因,并用“正确的”(插入基因驱动的同一基因)替换所有“错误的”(不是插入基因驱动的同一基因),导致该基因有可能传播。
既然你对基因驱动有了基本的了解,让我们从创建一个简单的遗传算法开始,它能够训练神经网络。
遗传算法:
正如我前面提到的,遗传算法模拟进化。生成具有特定遗传信息(在这种情况下,是神经网络的权重)的代理群体(在这种情况下,是神经网络)。一个算法计算每个代理的适应值,并计算适应值(在这种情况下,损失)。损失最低的前 20%的人口然后随机选择一个父母并产生两个后代,每个后代包含每个父母的遗传信息。重复该循环,直到产生具有令人满意的损失值的代理。
让我们试着用 Python 来创建这个:
步骤 1|先决条件:
import random
import numpy as np
from IPython.display import clear_outputdef sigmoid(x):
return 1/(1+np.exp(-x))
先决条件是程序运行所需的基本依赖关系。Random 用于代理的随机生成,numpy 用于矩阵的初始化和操作,IPython display 用于消除屏幕上的混乱。
为了简单起见,我将在这个项目中使用的唯一激活函数是 sigmoid 函数。
步骤 2|代理蓝图:
class genetic_algorithm:
def execute(pop_size,generations,threshold,X,y,network):
class Agent:
def __init__(self,network):
class neural_network:
def __init__(self,network):
self.weights = []
self.activations = []
for layer in network:
if layer[0] != None:
input_size = layer[0]
else:
input_size = network[network.index(layer)-1][1]
output_size = layer[1]
activation = layer[2]
self.weights.append(np.random.randn(input_size,output_size))
self.activations.append(activation)
def propagate(self,data):
input_data = data
for i in range(len(self.weights)):
z = np.dot(input_data,self.weights[i])
a = self.activations[i](z)
input_data = a
yhat = a
return yhat
self.neural_network = neural_network(network)
self.fitness = 0
self.gene_drive = []
def __str__(self):
return 'Loss: ' + str(self.fitness[0])
这是程序的开始,创建了遗传算法类和执行函数。
代理具有包含网络传播指令的蓝图。在代理的 init 中,初始化一个神经网络类,并根据给定的矩阵结构随机生成其权重。
步骤 3|创建群体:
def generate_agents(population, network):
return [Agent(network) for _ in range(population)]
这个函数,给定群体大小和网络结构作为参数,生成代理群体,具有随机生成权重的神经网络。
第 4 步|计算健康度:
def fitness(agents,X,y):
for agent in agents:
yhat = agent.neural_network.propagate(X)
cost = (yhat - y)**2
agent.fitness = sum(cost)
return agents
这是该遗传算法的基本适应度函数。它只是使用 MSE 公式来计算损失。
第五步|选择:
def selection(agents):
agents = sorted(agents, key=lambda agent: agent.fitness, reverse=False)
print('\n'.join(map(str, agents)))
agents = agents[:int(0.2 * len(agents))]
return agents
程序的这一部分是选择算法,它根据智能体的适应值以相反的顺序对智能体进行排序。然后,它会清除不在此列表前五分之一的所有代理。
第六步|交叉:
def crossover(agents,network,pop_size):
offspring = []
for _ in range((pop_size - len(agents)) // 2):
parent1 = random.choice(agents)
parent2 = random.choice(agents)
child1 = Agent(network)
child2 = Agent(network)
shapes = [a.shape for a in parent1.neural_network.weights]
genes1 = np.concatenate([a.flatten() for a in parent1.neural_network.weights])
genes2 = np.concatenate([a.flatten() for a in parent2.neural_network.weights])
split = random.randint(0,len(genes1)-1)child1_genes = np.array(genes1[0:split].tolist() + genes2[split:].tolist())
child2_genes = np.array(genes1[0:split].tolist() + genes2[split:].tolist())
for gene in parent1.gene_drive:
child1_genes[gene] = genes1[gene]
child2_genes[gene] = genes1[gene]
for gene in parent2.gene_drive:
child1_genes[gene] = genes2[gene]
child2_genes[gene] = genes2[gene]
child1.neural_network.weights = unflatten(child1_genes,shapes)
child2.neural_network.weights = unflatten(child2_genes,shapes)
offspring.append(child1)
offspring.append(child2)
agents.extend(offspring)
return agents
从人口的前 20%中随机选择两个父母。然后它们繁殖。这是如何做到的:
- 它们的重量变平了
- 找到一个随机的交点。这个点是一个父母的遗传信息结束的地方,也是一个父母的遗传信息开始的地方。
- 创建两个子代,然后将它们添加到代理列表中。这些孩子彼此不同,因为他们有不同交叉点。
这有望让好父母的优良品质传递给他们的后代。
第七步|突变:
def mutation(agents):
for agent in agents:
if random.uniform(0.0, 1.0) <= 0.1:
weights = agent.neural_network.weights
shapes = [a.shape for a in weights]flattened = np.concatenate([a.flatten() for a in weights])
randint = random.randint(0,len(flattened)-1)
flattened[randint] = np.random.randn()newarray = []
indeweights = 0
for shape in shapes:
size = np.product(shape)
newarray.append(flattened[indeweights : indeweights + size].reshape(shape))
indeweights += size
agent.neural_network.weights = newarray
return agents
每一种药剂都有 10%的几率会发生变异。在这种情况下,突变是指某个权重值被随机的浮点值替换。这是通过展平权重、找到要改变的随机权重,然后最后重新整形要重新插入代理的权重来完成的。
第八步|基因驱动:
def gene_drive(agents):
for agent in agents:
if random.uniform(0.0, 1.0) <= 0.1:
weights = agent.neural_network.weights
shapes = [a.shape for a in weights]flattened = np.concatenate([a.flatten() for a in weights])
target_gene = random.randint(0,len(flattened)-1)
if not(target_gene in agent.gene_drive):
agent.gene_drive.append(target_gene)newarray = []
indeweights = 0
for shape in shapes:
size = np.product(shape)
newarray.append(flattened[indeweights : indeweights + size].reshape(shape))
indeweights += size
agent.neural_network.weights = newarray
return agents
尽管这个脚本将基因驱动插入到代理中,但是如果您仔细查看代码,您已经可以看到对基因驱动框架的规避,从而影响程序的其他部分。比如在交叉指令中,其实解释了基因驱动是如何实现的。
for gene in parent1.gene_drive:
child1_genes[gene] = genes1[gene]
child2_genes[gene] = genes1[gene]
for gene in parent2.gene_drive:
child1_genes[gene] = genes2[gene]
child2_genes[gene] = genes2[gene]
就这么简单!如果基因存在于父母的基因驱动中,孩子会将自己的基因改变为父母的基因,即具有基因驱动的基因。
第 9 步|执行:
for i in range(generations):
print('Generation',str(i),':')
agents = generate_agents(pop_size,network)
agents = fitness(agents,X,y)
agents = selection(agents)
agents = gene_drive(agents)
agents = crossover(agents,network,pop_size)
agents = mutation(agents)
agents = fitness(agents,X,y)
if any(agent.fitness < threshold for agent in agents):
print('Threshold met at generation '+str(i)+' !')
if i % 100:
clear_output()
return agents[0]
将最后一段代码粘贴到函数中,函数应该在被调用时运行。
X = np.array([[0,0,1], [1,1,1], [1, 0, 1], [0, 1, 1]])
y = np.array([[0,1,1,0]]).T
network = [[3,20,sigmoid],[None,1,sigmoid]]
ga = genetic_algorithm
agent = ga.execute(10000,1000,0.1,X,y,network)
weights = agent.neural_network.weights
agent.fitness
agent.neural_network.propagate(X)
神经网络的任务很简单。结构为(3,20,1)的网络,每层都有 sigmoid 激活函数。对于每一代新的群体,会产生 10000 个新的因子,这样重复 1000 代。
结论:
基因驱动遗传算法实际上比普通遗传算法更差。我的假设是,基因驱动将应用于早期的代理人,从定义上来说,没有未来的代理人好。
然后,我决定实现一个阈值,在这个阈值中,只有低丢失值才会有随机归因于其基因组中某个基因的基因驱动。
新代码:
def gene_drive(agents):
for agent in agents:
if agent.fitness <= 0.1:
weights = agent.neural_network.weights
shapes = [a.shape for a in weights]flattened = np.concatenate([a.flatten() for a in weights])
target_gene = random.randint(0,len(flattened)-1)
if not(target_gene in agent.gene_drive):
agent.gene_drive.append(target_gene)newarray = []
indeweights = 0
for shape in shapes:
size = np.product(shape)
newarray.append(flattened[indeweights : indeweights + size].reshape(shape))
indeweights += size
agent.neural_network.weights = newarray
break
return agents
这比普通的遗传算法工作得更好,产生的结果也更好。
我的链接:
如果你想看更多我的内容,点击这个 链接 。
使用 Python 实现 gRPC 服务器
原文:https://towardsdatascience.com/implementing-grpc-server-using-python-9dc42e8daea0?source=collection_archive---------6-----------------------
你的下一个 API 不需要用 REST 和 JSON 来构建。gRPC 和协议缓冲区如何更好的性能和结构?
如今,当人们想要实现后端 API 时,他们会直接使用 RESTful API 创建使用 JSON 通信的应用程序,甚至不考虑其他选项。然而最近几年,gRPC 和它的 protobufs 由于它们的许多优点开始得到一些关注和欢迎。因此,让我们来看看有什么值得关注的,并使用 Python 实现 gRPC 服务器!
TL;DR:这里是我的带有 *grpc*
分支的存储库,包含本文的所有源代码:https://github . com/MartinHeinz/python-project-blue print/tree/grpc
Zak Sakata 在 Unsplash 上的原始照片
gRPC 到底是什么?
gRPC 是远程过程调用(RPC)协议,利用协议缓冲区(protobufs) 作为其消息格式。使用 gRPC,客户端应用程序可以使用方法存根直接调用远程服务器上的可用方法。只要有客户端语言的存根(生成),服务器端应用程序用什么语言实现并不重要。gRPC 支持多种语言,包括 Go、Java、Ruby、C#或者我们选择的语言——Python。你可以在这个概述中找到更多信息。
现在,什么是协议缓冲区(protobufs) ?Protobufs 是 JSON 或 XML 等格式的替代品。它们是一种更小、更简单、更有效的序列化数据的方式。要使用 protobufs ,您需要定义您希望交换的消息看起来是什么样子,例如(更多细节参见本语言指南):
除了消息,我们还需要定义将在服务器端实现并从客户端调用的service
及其rpc
方法:
我为什么要在乎呢?
与 REST 相比,使用 gRPC 有很多优点。首先,gRPC 在性能方面要好得多,这要归功于 protobufs 的紧密封装,它减少了发送的有效载荷的大小。它还使用 HTTP/2,而不是 REST 使用的 HTTP/1.1。由于这些原因,它是物联网、移动设备或其他受限/低功耗环境的绝佳选择。
选择 gRPC 而不是 REST 的另一个原因是 REST 不要求任何真正的结构。您可以使用 OpenAPI 定义请求和响应的格式,但是这是松散的和可选的。另一方面,gRPC 合同更加严格且定义明确。
如前所述,gRPC 使用 HTTP/2,值得一提的是它充分利用了它的特性。举几个例子:并发请求,流而不是请求-响应,对延迟的敏感度更小。
也就是说,也有缺点,最大的缺点是采用。并非所有的客户端(浏览器)都支持 HTTP/2 的使用,这使得它在外部使用时存在问题。考虑到性能优势,它显然是您可以控制的内部服务和通信的最佳选择。
安装
要使用 gRPC 和 protobufs 做任何事情,我们需要安装它的编译器:
考虑到我们使用 python 来构建我们的应用程序,我们还需要grpcio
和grpcio-tools
库:
让我们建造一些东西
准备好所有工具后,我们现在可以开始构建应用程序了。对于这个例子,我选择了一个简单的 echo 服务器,它可以向您发回自己的消息。
不过,我们首先应该讨论的是项目布局。我选择了以下目录/文件结构:
这种布局帮助我们清楚地分离 protobuf 文件(.../proto
)、生成的源代码(.../generated
)、实际的源代码和我们的测试套件。要了解更多关于如何用这种布局设置 Python 项目的信息,可以查看我以前的文章:
[## 下一个 Python 项目的最终设置
从零开始任何项目都可能是一项艰巨的任务…但如果您有这个最终的 Python 项目蓝图就不会了!
towardsdatascience.com](/ultimate-setup-for-your-next-python-project-179bda8a7c2c)
因此,要构建 gRPC 服务器,我们首先需要定义消息和服务,它将使用您与客户端进行通信:
在这个echo.proto
文件中,我们可以看到一个非常简单的消息类型定义——一个用于请求(EchoRequest
),一个用于来自服务器的回复(EchoReply
)。然后这些消息被由一个名为Reply
的 RPC 方法组成的Echo
服务使用。
为了能够在 Python 代码中使用这些定义,我们需要生成服务器和客户端接口。为此,我们可以运行以下命令:
我们指定了相当多的参数。第一个——-I blueprint/proto
,告诉grpc_tools
在哪里寻找我们的.proto
文件(它定义了PATH
)。接下来的两个,--python-out
和--grpc_python_out
分别指定将生成的*_pb2.py
和*_grpc_pb2.py
文件输出到哪里。最后一个参数——./blueprint/proto/*.proto
是指向.proto
文件的实际路径——这看起来有些多余,因为我们用-I
指定了PATH
,但是你需要两者来完成这项工作。
然而,当您运行这一个命令时,您将在这些生成的文件中结束一些中断的导入。在grpc
和protobuf
存储库中有多个提出的问题,最简单的解决方案是用sed
修复那些导入。
写出这个命令不会很愉快,也没有效率,所以我把它包装在make
target 中,让你(和我)的生活更轻松。你可以在我的资源库找到完整的Makefile
这里。
有相当多的代码是使用这个命令生成的,所以我不会一一查看,但是有一些重要的事情需要注意。这些*_pb2_grpc.py
文件中的每一个都有以下三样东西:
- 存根——第一个是存根,在我们的例子中是
EchoStub
——是客户端用来连接 gRPC 服务的类 - 服务器——在我们的例子中是
EchoServicer
——被服务器用来实现 gRPC 服务 - 注册功能 —最后一项,
add_EchoServicer_to_server
需要向 gRPC 服务器注册服务器。
所以,让我们检查一下代码,看看如何使用这个生成的代码。
我们要做的第一件事是实现实际的服务。我们将在grpc.py
文件中这样做,在那里我们将保存所有 gRPC 特定的代码:
上面,我们创建了从生成的EchoServicer
类继承而来的Echoer
类,它包含了.proto
文件中定义的所有方法。所有这些方法都旨在我们的实现中被覆盖,这里我们就是这么做的。我们通过返回之前在.proto
文件中定义的EchoReply
消息来实现唯一的方法Reply
。我想指出的是,这里的request
参数是EchoReply
的一个实例——这就是为什么我们可以从中取出message
。这里我们不使用context
参数,但是它包含一些有用的特定于 RPC 的信息,比如超时限制。
我还想提到的一个巧妙的特性是,如果您想利用响应流,您可以用yield
替换return
,并返回多个响应(在for
周期中)。
既然我们已经实现了 gRPC 服务,我们想运行它。为此,我们需要一台服务器:
所有这些都只是一个静态方法。它用几个工人从grpc
库创建服务器——在本例中是 10 个。之后,它使用前面提到的注册函数(add_EchoServicer_to_server
)将我们的Echoer
服务绑定到服务器。剩下的 3 行只是添加监听端口,启动服务器并等待中断。
对于服务器端来说,剩下的就是__main__.py
,因此我们可以将它作为 Python 模块启动:
这样,我们就可以启动服务器了。你可以用python -m blueprint
来做,或者如果你用的是我的模板,那么就用make run
。
我们有一个正在运行的服务器,但是我们没有办法调用它…这就是客户端调用它的地方。出于演示目的,我们将使用为我们生成的存根用 Python 创建客户机,但是您可以用完全不同的语言编写客户机。
对于客户端,我们只需要一个函数,我们称之为run
。在连接服务器之后,它会创建一个存根,允许我们调用服务器方法,这是下一步。它通过传入带有一些有效载荷的EchoRequest
消息来调用在服务器端实现的Reply
方法。剩下的就是打印了。
现在,让我们运行客户端,看看是否一切正常:
而且确实有效!
使用 Pytest 进行测试
正如我所有的小项目和文章一样,在对所有代码进行单元测试之前,我们还没有完成。为了编写这个 gRPC 服务器的示例测试,我将使用 Pytest 及其插件pytest-grpc
。
让我们首先来看看用于模拟客户机和服务器之间的请求-响应交换的装置:
我认为这些都很简单。只要确保为这些设备使用这些特定的名称,因为这是插件所寻找的。需要注意的一点是grpc_stub
中的grpc_channel
论证。这是pytest-grpc
插件提供的假频道。要了解更多信息,我建议直接去pytest-grpc
源代码,因为这个插件非常简单。现在,让我们继续实际的测试:
我们通过利用在上一步中编写的grpc_stub
fixture 来创建这个测试。我们创建了传递给grpc_stub.Reply
的EchoRequest
,后面是简单的assert
。并且,当我们运行测试(make run
)时:
我们通过了!我们完成了!
结论
如果你从这篇文章中只学到一样东西,那么我认为应该是这样一个事实:当我们决定我们想要用于某个项目的解决方案/技术时,我们应该总是考虑可能的替代方案。并不总是需要 REST 和 JSON。有时 gRPC 可能更符合要求。这种想法也适用于任何其他技术或工具。要查看使用Makefile
自动化的完整代码清单、准备好的 Docker 映像,甚至是部署到 Kubernetes 的设置,请查看我的存储库中的grpc
分支:https://github . com/Martin Heinz/python-project-blue print/tree/master。感谢任何反馈,如果你喜欢这类内容,star 或 fork 也是如此。😉
本文最初发布于martinheinz . dev
使用 DAX 实现 K-最近邻分类算法
原文:https://towardsdatascience.com/implementing-k-nearest-neighbor-classification-algorithms-using-dax-a52607b1a825?source=collection_archive---------44-----------------------
带 DAX 的 KNN 算法
本文将介绍如何使用数据分析表达式(DAX)实现 KNN(K 近邻)分类。您可以在下图中看到彩色散点图,其中每个散点图代表一种产品。横轴代表销量,纵轴代表利润,剩下的 7 个白色三角形是待分类的测试数据。接下来,我将使用 KNN 算法对这些测试数据进行分类。
作者图片
关于本文的数据集:
训练数据集(部分):
作者图片
作者图片
测试数据集:
TestingDataSet = DataTable("_ID", STRING,
"_Sales", INTEGER,
"_Profit",INTEGER
,{
{" ID501",15,23},
{" ID502",25,7},
{" ID503",45,3},
{" ID504",18,8},
{" ID505",56,9},
{" ID506",60,-5},
{" ID507",30,21}
}
)
作者图片
让我们开始吧:
首先,我们需要计算每个测试点和训练集的数据点之间的距离。我们可以用欧几里德距离公式计算如下:
图片由维基百科
在此之前,我采用的方法是将测试数据集作为主表,让它与训练数据集生成一个笛卡尔积,这样我们就可以针对每个测试点计算出它与所有训练集数据点之间的距离。另外,我们需要计算所有训练数据中哪些数据点是被划分为测试点的相邻点。这里我取 K 等于 9,将数据集中距离测试点距离最小的前 9 个训练集数据进行标记,然后过滤掉没有标记的数据点。总之,执行以下代码来生成表:“合并数据集”:
MergeDataSet =
VAR VT_1 =
ADDCOLUMNS(
GENERATEALL('TestingDataSet','TrainingDataSet'),
"DISTANCE",
CEILING(
SQRT(('TestingDataSet'[_Sales]-'TrainingDataSet'[Sales])^2+
('TestingDataSet'[_Profit]-'TrainingDataSet'[Profit])^2),
0.01)) VAR VT_2 =
ADDCOLUMNS(VT_1,
"IsKNN",
VAR K = 9
VAR v_id = 'TestingDataSet'[_ID]
return
IF(
rankx(
filter(VT_1,'TestingDataSet'[_ID] = v_id),
[DISTANCE],,ASC,Skip)<=K,
"T","F")) VAR VT_3 =
FILTER(VT_2,[IsKNN] = "T") RETURN VT_3
结果如下所示(部分):
作者图片
现在我们已经确定了所有的邻居,是时候分析它们了。对于每个测试点,找到其所有邻居的类别中最多的,然后将类别分配给该测试点,根据多数投票完成分类,最终结果在名为“结果集”的表中返回:
ResultSet =
VAR VT_4 =
ADDCOLUMNS('MergeDataSet',
"_Class",
VAR CLASSNUM_MAX =
CALCULATE(
MAXX('MergeDataSet',
CALCULATE(
COUNT('MergeDataSet'[Class]),
ALLEXCEPT('MergeDataSet',
'MergeDataSet'[_Profit],
'MergeDataSet'[Class]))),
ALLEXCEPT('MergeDataSet','MergeDataSet'[_ID]))
RETURN
IF( CLASSNUM_MAX = CALCULATE( COUNT('MergeDataSet'[Class]),
ALLEXCEPT('MergeDataSet', 'MergeDataSet'[_Profit], 'MergeDataSet'[Class])),
CALCULATE( FIRSTNONBLANK('MergeDataSet'[Class],1),
ALLEXCEPT('MergeDataSet', 'MergeDataSet'[_ID], 'MergeDataSet'[Class])
))) VAR VT_5 =
CALCULATETABLE(
GROUPBY(
FILTER(VT_4,[_Class]<>BLANK()),
'MergeDataSet'[_ID],
'MergeDataSet'[_Profit],
'MergeDataSet'[_Sales],
[_Class],
"Distance",
SUMX(CURRENTGROUP(),'MergeDataSet'[DISTANCE])
)) RETURN VT_5
执行的结果如下,“__Class”是 K 个最近邻的分类结果:
作者图片
您可能会发现,结果集中有一列没有出现在最后一个公式中:“KNN _ 结果”(带绿色标记)。这是因为我们还需要考虑一种特殊情况(虽然在这种情况下没有出现):如果测试点有相同数量的 A 类邻居和 B 类邻居,那么测试点应该属于哪个类?这里,通过比较所有 A 类和 B 类点离测试点的实际距离,然后选择距离值较小的类别。因此,使用以下公式创建一个名为“KNN _ 结果”的新计算列,以便我们在遇到这种特殊情况时可以通过筛选空行来实现这一点:
KNN_Result =
VAR Class_ =
CALCULATE(
MIN('ResultSet'[Distance]),
ALLEXCEPT('ResultSet','ResultSet'[MergeDataSet__ID]))
RETURN
IF(
class_ = 'ResultSet'[Distance],
'ResultSet'[_Class],
BLANK())
最后,我们使用 DAX 成功完成了 KNN 分类,如下所示:
作者图片
(我相信 DAX 有能力实现一些简单的机器学习算法,而不是什么都依赖 R 或者 PYTHON。如果你有更好的方法来使用 DAX 或 M 实现 KNN,我希望得到你的反馈。)
END~
从头开始实现线性和多项式回归
原文:https://towardsdatascience.com/implementing-linear-and-polynomial-regression-from-scratch-f1e3d422e6b4?source=collection_archive---------13-----------------------
在本教程中,我们将介绍如何使用最小二乘法拟合数据来实现简单的线性回归模型。之后,我们将模型扩展到多项式回归模型,以便捕捉更复杂的信号。我们将使用均方差来衡量我们生成的每个模型的拟合质量。你可以从我的 Github repo 下载我用来写这篇文章的所有资源👍。
加载和绘制数据
让我们首先将训练数据加载到内存中,并将其绘制成图表,以了解我们正在处理的内容。将train_features
视为 x 值,将train_desired_outputs
视为 y 值。下图是所有值的散点图。
我们训练数据的散点图
现在是时候写一个简单线性回归模型来尝试拟合数据了。我们的目标是找到一条与图中所示的训练数据的基本模式最相似的线。我们将使用最小二乘法用最能描述训练集的系数来参数化我们的模型,然后再看看该模型对以前从未见过的数据的概括程度。回想一下,简单的线性回归模型是由 y 轴截距和回归线的斜率来参数化的。
简单线性回归模型
训练我们的线性回归模型
寻找参数以使我们的模型符合训练数据的过程称为“训练”我们的模型。给定一个设计矩阵 X 和一个目标输出的列向量 y ,我们可以使用下面的等式通过最小二乘回归找到我们的线性模型的最佳截距和斜率系数(要深入了解该等式背后的线性代数概念,请参见本文):
我们是这样编码的。我们首先创建一个设计矩阵 X ,其中包含一列 1(为了估计 y 轴截距)和另一列 1,用于保存我们的解释变量 x 的值。然后,我们取 X 与其转置的点积的倒数,并将其与 X 转置和 training _ desired _ outputs 的 y 值)的点积进行点积。
我们的设计矩阵 X
下面是我们的模型如何拟合训练数据。
我们可以通过使用均方误差 (MSE)来计算我们的线和训练集中实际数据点之间的平均平方差,从而量化我们的模型与数据的拟合程度。MSE 计算如下:
Mean squared error on the training set: 2.1739455790492586
还不错!均方误差是对估计量质量的一种度量,在我们的例子中,估计量是我们的线性回归模型。MSE 总是非负的,值越接近零越好。
测试我们的线性回归模型
现在让我们看看我们的模型对 T2 以前没有见过的数据预测得有多好。我们称这一步为测试阶段。记住,我们的模型是由返回的系数定义的
因此,上面的函数返回 w,其中包含 y 轴截距和斜率的系数。我们将使用用于训练模型的相同值来测试我们的模型。
下面是我们的模型如何拟合测试数据:
同样,我们将使用 MSE 来评估我们的模型与数据的拟合程度(我使用的是前面 MSE.py gist 中提供的相同代码)。
Mean squared error on the testing set: 2.3118753456727985
我们的测试误差略高于我们的训练误差。这是有意义的,因为训练误差通常应该低于测试误差。一个模型很少会根据它以前没有见过的数据比它接受训练的数据预测得更准确。
多项式回归
要实现多项式回归,我们需要做的就是采用我们的线性回归模型并添加更多的特性。回想一下线性回归模型的形式,并将其与多项式回归模型的形式进行比较
多项式回归模型的形式
你可以看到,我们需要一个额外的系数为每一个额外的功能,表示为 x …xᵐ.多项式回归模型的阶数取决于模型中包含的特征数量,因此具有 m 个特征的模型是一个 mᵗʰ- 次或 mᵗʰ- 阶多项式回归。我们将从二阶多项式回归开始,您会注意到增加回归模型的复杂性非常容易(增加模型复杂性并不总是一件好事,可能会导致 过度拟合 !!!).
二阶多项式回归
因为我们在模型中加入了另一个特性,所以我们必须通过在设计矩阵中加入另一个术语来说明这一点。具有 m- 度的设计矩阵的一般形式如下
多项式回归设计矩阵的矩阵形式
让我们继续用代码构建它:
请注意,我们是如何在设计矩阵 X 的右侧包含一列 X 特性的。产生的三个系数存储在系数中。让我们将模型应用于我们的训练数据,并打印出回归线。
下面是我们的二阶多项式回归模型如何拟合训练数据:
二阶多项式回归拟合散点图。
Mean squared error on the training set: 0.4846845031271553
现在让我们将我们的模型应用于测试数据。
这是它的合适之处。
Mean squared error on the testing set: 0.7573635655518132nd-order polynomial regression is a better fit than linear regression.
当比较我们的线性回归模型和二阶多项式回归模型的 MSE 时,我们看到后者比前者更适合测试集。万岁!我们能够通过增加复杂性来提高模型的准确性。然而,要注意的是,增加模型的复杂性并不总是会带来更好的准确性。
为了进一步扩展该模型,尝试通过在设计矩阵 X 中为特征 x 添加一个立方项来实现三阶多项式回归,如下所示:
X = np.c_[np.ones(N), train_features_vals, np.square(train_features), np.power(train_features, 3)]
确保在绘制回归线时,修改 y_pred 来考虑这个额外的项。
y_pred = b[3]*np.power(x_line, 3) + b[2]*np.square(x_line) + b[1]*x_line + b[0]
我希望这篇教程对你有所帮助!我很乐意回答任何问题或评论,所以请在下面留下你的想法或私人笔记。干杯!
用神经网络实现逻辑门(下)
原文:https://towardsdatascience.com/implementing-logic-gates-using-neural-networks-part-2-b284cc159fce?source=collection_archive---------3-----------------------
与、与非和异或门
大家好!!在开始使用神经网络实现逻辑门的第 2 部分之前,您可能想先浏览一下第 1 部分。
从第 1 部分中,我们已经知道我们有两个输入神经元或 x 向量,值为 x1 和 x2,1 是偏差值。输入值,即 x1、x2 和 1,乘以它们各自的权重矩阵,即 W1、W2 和 W0。相应的值然后被馈送到求和神经元,在那里我们得到求和值,即
作者图片
现在,这个值被馈送到一个神经元,该神经元具有一个非线性函数(在我们的例子中为 sigmoid ),用于将输出缩放到期望的范围。如果输出小于 0.5,则 sigmoid 的比例输出为 0,如果输出大于 0.5,则为 1。我们的主要目的是找到权重值或权重向量,这将使系统能够充当特定的门。
实现与门
与门运算是输入之间的简单乘法运算。如果任何输入为 0,则输出为 0。为了实现 1 作为输出,两个输入都应该是 1。下面的真值表传达了同样的信息。
“与”门的真值表和使系统充当“与”和“与非”门的权值,由作者形象
由于我们有 4 个输入选择,权重必须满足所有输入点的与门条件。
(0,0)案例
考虑输入或 x 向量为(0,0)的情况。在这种情况下,Z 的值就是 W0。现在,W0 必须小于 0,因此 z 小于 0.5,输出或ŷ为 0,满足与门的定义。如果大于 0,则 Z 通过 sigmoid 函数后的值将为 1,这违反了与门条件。因此,我们可以说,对于分辨率,W0 必须是负值。但是 W0 值多少?继续阅读…
(0,1)案例
现在,考虑输入或 x 向量为(0,1)的情况。这里 Z 的值将是 W0+0+W2*1。这是 sigmoid 函数的输入,应该具有小于 0 的值,使得输出小于 0.5 并被分类为 0。从今以后,W0+W2<0。如果我们把 W0 的值取为-3(记住 W0 的值必须是负的),把 W2 的值取为+2,那么结果出来就是-3+2,也就是-1,这似乎满足上面的不等式,与 and 门的条件相当。
(1,0)案例
类似地,对于(1,0)的情况,W0 的值将是-3,W1 的值可以是+2。请记住,只要不等式不变,您可以采用权重 W0、W1 和 W2 的任何值。
(1,1)案例
在这种情况下,输入或 x 向量是(1,1)。在这种情况下,Z 的值就是 W0+W1+W2。现在,总输出必须大于 0,以便输出为 1,满足与门的定义。从前面的场景中,我们发现 W0,W1,W2 的值分别为-3,2,2。将这些值放入 Z 等式中会产生输出-3+2+2,它是 1 且大于 0。因此,在通过 sigmoid 函数后,这将被分类为 1。
关于“与”和“与非”实现的最后一点说明
因此,分隔上述四点的直线是一个等式 W0+W1x1+W2x2=0,其中 W0 是-3,W1 和 W2 都是+2。因此,四点分隔线的等式为 x1+x2=3/2。因此,或非门的实现将类似于将权重改变为 W0 等于 3,以及 W1 和 W2 等于-2
继续讨论异或门
对于 XOR 门,下图左侧的真值表显示,如果有两个补码输入,则只有输出为 1。如果输入相同(0,0 或 1,1),则输出将为 0。绘制在右侧 x-y 平面上的点给我们的信息是,它们不像 OR 和 and 门那样是线性可分的(至少在二维上)。
XOR 门真值表和 x-y 平面上数值的绘制,图片由作者提供
两种可能解决方案
为了解决上述可分离性问题,可以采用两种技术,即添加非线性特征(也称为核心技巧)或添加额外的层(也称为深度网络)
XOR(x1,x2)可以被认为是 NOR(或非(x1,x2)和(x1,x2))
实现异或门的解决方案
异或网络的权重
在这里,我们可以看到该层从 2 增加到 3,因为我们添加了一个正在计算 AND 和 NOR 运算的层。输入保持不变,外加偏置输入 1。右下方的表格显示了作为输入的 4 个输入的输出。这里需要注意的一个有趣的事情是,权重的总数增加到了 9。从输入层到第二层共有 6 个权重,从第二层到输出层共有 3 个权重。第二层也称为隐藏层。
网络的权重使其充当异或门,图片由作者提供
谈到整个网络的权重,从上面和第 1 部分的内容中,我们已经推导出系统作为与门和或非门的权重。我们将使用这些权重来实现 XOR 门。对于层 1,总共 6 个权重中的 3 个将与或非门的权重相同,剩余的 3 个将与与门的权重相同。因此,NOR 门的输入权重将是[1,-2,-2],and 门的输入权重将是[-3,2,2]。现在,从第 2 层到最后一层的权重将与或非门的权重相同,即[1,-2,-2]。
通用逼近定理
它指出,任何函数都可以表示为具有一个隐藏层的神经网络,以达到期望的精度
几何解释
两类 3D 图像的线性可分性
有了这个,我们可以把增加额外的层想象成增加额外的维度。在三维可视化后,X 和 O 现在看起来是可分的。红色平面现在可以分隔这两个点或类。这样的平面叫做超平面。综上所述,以上几点在更高维度上是线性可分的。
交叉:资产价格的有利可图的交叉点
原文:https://towardsdatascience.com/implementing-ma-strategy-a6457f3ebbb9?source=collection_archive---------51-----------------------
使用 Python 创建移动平均线交易策略
作为一个经济学出身的人,我不禁从金融的角度来看待这个世界。当我决定转向技术分析时,我仍然希望将我的金融领域知识融入到技术部分。交易中的技术分析对我这个位置的很多人来说是一个非常热门的领域,所以我想建立一个带回溯测试的量化交易策略来结合这两个领域。这个项目的完整存储库可以在这里找到。
由杰森·布里斯科在 Unsplash 上拍摄的照片
- 免责声明:本博客和交易策略用于教育目的,我不建议用于专业投资*
建立投资组合
我们的交易策略将采用各种版本的移动平均线公式,使我们的交易决策适应投资组合中资产价格的行为。我们将从选择多样化的资产组合开始:特斯拉(TSLA)、强生(JNJ)和苹果(AAPL)。这三种资产是蓝筹股,分别是汽车、医疗保健和科技行业的主要驱动力。我们首先使用 yfinance 库从一个确定的起点和终点收集历史价格数据。这些作为单独的序列引入,然后合并到一个具有日期时间索引的数据帧中。
我们现在有超过五年的收盘价(1377 个条目)用于我们的策略。
简单移动平均线
因为这是我建立的第一个技术交易策略,我想从一个最古老和直接的方法开始,我们将使用价格的移动平均时间序列来对抗实际价格的趋势。通过对一个时间序列进行移动平均,我们能够大大减少序列中的噪声或方差。这将允许我们以更简单的方式理解资产的长期行为。
使用高频算法,交易可以在几分之一秒内发生,或者以长期方式持续多年。我们将采用短期和长期的移动平均线。
让我们来看看,对于我们投资组合中的一种资产:特斯拉,同一时间序列的这些变化是如何随着时间的推移而变化的。注意简单移动平均线(SMA)是如何随着时间的增长而变得平滑的,这表明价格变动的可变性减少了。
重要的是要记住,当我们使用 SMA 时间序列时,我们实际上落后于原始价格序列。注意橙色的 20 天 SMA 是如何在灰色的 TSLA 价格线的 01/15 开始日期之后开始的。蓝绿色 100 天 SMA 开始得更晚。滞后是由于简单移动平均线的数学本质,但我们不能简单地忽略这种滞后的存在,因为当谈到交易时,时间是最敏感的。那么我们如何解释这种滞后呢?指数移动平均线(EMA)。
指数移动平均线
EMA 和 SMA 非常相似,除了它有一个额外的衰减参数,α (alpha)。均线有助于限制滞后,因为它更重视最近的观察。SMA 只是对观察值进行平均加权。
我们可以看到,使用均线可以更同步、更精确地捕捉方差减少的时间序列。现在我们有了一个好的数据结构,我们将继续发展我们的交易策略。
制定贸易战略
如上所述,均线和均线都滞后于资产的实际价格。换句话说,资产的实际价格比移动平均价格变化快得多。因为实际价格更敏感,我们可以用两者的交叉点(实际价格和均线)来表示做空(卖出)或做多(买入)。
- 当原始资产价格从低于的穿过均线系列时,我们将卖出所有空头头寸,买入一个单位的多头头寸
- 当原始资产从上方的穿过均线系列时,我们会平仓,通过卖出一个单位的资产来做空
注意:我们不考虑仓位大小。我们假设我们的可用资本被完全使用,并且在我们投资组合的三种资产中平均分配(1/3)。在多头条件下,每项资产的权重为 1/3,而空头条件下的权重为-1/3。
我们用原始价格和均线序列之间的差异来表示我们所有的资金都被使用了,不管是多头还是空头。
这将为我们提供一个包含价格差异的新数据框架,但我们确实希望将价格差异反映为“买入”或“卖出”的信号。我们可以使用一个 numpy 方法来分配这些。
重要提示:因为我们使用的是原始时间序列中资产的“收盘”价格,我们实际上并不知道该价格高于均线价格。假设我们有一个多头头寸实际上与我们模型的数学不一致。只能假设收盘时我们会做多,也就是说明天的开仓也会做多。明天做多可以通过将我们的头寸向前移动一天来表示。
让我们看看我们的均线和原始资产价格是什么样的。我们还将绘制信号的时间序列,看看我们的头寸在日期窗口的过程中是如何变化的。
记住,我们的多头买入头寸用正的 1/3 (0.333)来表示,而我们的卖出头寸用负的 1/3(0.333)来表示(T2)。我们现在有了整个系列的投资组合交易头寸的可视化表示。
回溯测试我们的策略
我们的策略独立对待每项资产,我们对一项资产的头寸不会影响其他资产的头寸。我们将使用这些信息,通过连续对数观测值的差异来获得我们的投资组合回报。
我们在整个时间段的总回报要求我们将持仓数据框架乘以资产的对数回报数据框架。
我们的数据框架现在包含了每个单个条目的回报,但是我们必须使用累计总和来查看总回报。然后,我们使用 numpy 将这些回报转换成相对回报。然后,我们将绘制出我们每项资产的累积对数收益以及它们的相对收益。
我们将把所有的对数收益相加,然后把它们转换成相对收益,从而得到一个统计上精确的收益近似值。绘制时,请记住这些值代表百分比,因此不要忘记将系列 y 值乘以 100 以获得准确的数字。
决赛成绩
现在我们有了回报,我们现在可以使用复合回报假设来返回我们的终身投资组合回报以及我们的平均年回报。
- 终身投资组合回报率= 79.31%
- 平均。年回报率= 11.66%
自 1962 年以来,美国股票的平均年回报率约为 11%。这意味着我们的移动平均策略将允许您的投资组合每年获得“平均”回报。现在,这些只是市场上市值最大的三只股票。一个真正的投资组合将更加多样化,并需要尽职调查来保证高回报。我们的交易策略也做了很多假设,可以改进,但希望这是对金融工具进行技术分析的一个强有力的起点。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
从零开始实现朴素贝叶斯算法。
原文:https://towardsdatascience.com/implementing-naive-bayes-algorithm-from-scratch-python-c6880cfc9c41?source=collection_archive---------5-----------------------
在本文中,我将展示朴素贝叶斯分类器的一个基本实现,以及它是如何工作的。
图片来自 www.i2tutorials.com
简介
朴素贝叶斯分类器是一种非常流行的基于贝叶斯定理的监督机器学习算法。这是一种简单但非常强大的算法,适用于大型数据集和稀疏矩阵,如预处理的文本数据,它根据字典中的单词数创建了数千个向量。它在文本数据项目(如情感数据分析)中工作得非常好,在文档分类项目中表现良好,并且在预测分类数据(如垃圾邮件分类)中也非常出色。
它用于解决许多不同的问题陈述,并且在训练模型时非常快,因为朴素贝叶斯分类器完全在概率上工作,所以转换发生得很快。
理解背后的数学和统计学
贝叶斯定理描述了一个事件发生的概率,基于可能与该事件相关的条件的先验知识。
首先,我们来看一个条件概率的公式,并尝试推导贝叶斯定理:
P(B|A) = P(A∩B)/P(B),
其中,给定 A 的 B 的概率,即事件 A 已经发生时事件 B 的概率,等于交集 B 的概率(即 A 和 B 事件都发生的概率)除以 B 的概率。
或者 P(A|B) = P(B∩A)/P(A),
其中,给定 B 的概率,即事件 B 已经发生时事件 A 的概率,等于 B 交集 A 的概率(即 B 和 A 事件都发生的概率)除以 A 的概率。
我们再仔细看看,我们看到 P(A∩B)和 P(B∩A)基本相同,可以写成 P(A∩B) = P(B∩A)。由于它们是相同的,我们可以得到两个公式,并将分母移到等式的左边:
P(A∩B) = P(A|B) * P(B),P(B∩A) = P(B|A) * P(A)并使它们相等:
P(A|B) * P(B) = P(B|A) * P(A)。
所以,当我们想找到给定 B 的概率时,我们可以这样写方程:
P(A|B) = P(B|A) * P(A) / P(B) ,这是贝叶斯定理的方程式。
贝叶斯定理方程在算法中的应用
让我们打破我们的等式,了解它是如何工作的:
- P(A|B) 是给定预测器 ( 属性)的类 ( 目标)的后验概率。
- P(B) 是类的先验概率。
- P(B|A) 为似然性,即预测器给定类的概率。
- P(A) 是预测器的先验概率。
为了计算后验概率,首先我们需要计算每个属性相对于目标的频率。然后,将频率转换为似然值,最后使用朴素贝叶斯方程计算每类的后验概率。具有最高后验概率的类是预测的结果。
在我的例子中,我使用了两个数据集:
- Iris 数据集预测花的类型
- 垃圾邮件数据库用于检测电子邮件是否包含垃圾邮件的数据集
由于两个数据集都有连续的属性,我选择了高斯分布来估计朴素贝叶斯分类器中似然概率的参数。该分布由两个参数表征,均值和方差。高斯密度函数的公式,来源于维基百科,看起来是这样的:(1/√2pi σ) exp((-1/2)*((x-μ))/(2 *σ)),其中μ是均值,σ是方差,σ是方差的平方根(标准差)。
首先,我们需要计算每一列的平均值和方差,并将其转换为 numPy 数组,以便将来计算:
计算每列的平均值和方差
接下来,让我们将高斯密度函数转换为代码:
从高斯密度函数计算概率
最后一步是计算先验和后验概率:
做预测
最后,所有的助手方法现在都可以在 fit 和 predict 方法中使用了:
拟合和预测方法
在对鸢尾花分类数据训练我的模型后,我得到了非常好的 92%的准确率。
鸢尾花分类中的真实与预测
我将它与 scikit-learn 高斯贝叶斯分类器进行了比较,得到了相同的结果,这非常令人印象深刻,因为我的算法只是准系统,需要很多改进。
对于电子邮件垃圾分类问题,我的模型仅执行了 78%的准确率,然而它与 scikit-learn 高斯贝叶斯分类器的准确率 81.8%相差不远。
垃圾邮件分类中的真实值与预测值
结论
我的朴素贝叶斯分类器算法实现远非理想,它需要许多改进和修改才能做出更好的预测,尤其是对文本数据,然而,与 sklearn 库的算法相比,它的性能仍然非常好。
注意:
这个项目是为 Lambda 学校(Bloom Technology now) 做的,作为计算机 科学课程的一部分。
代码实现的 Github 链接:从头开始的朴素贝叶斯分类器
用 Python 在 2 分钟内实现朴素贝叶斯
原文:https://towardsdatascience.com/implementing-naive-bayes-in-2-minutes-with-python-3ecd788803fe?source=collection_archive---------6-----------------------
我不知道这么容易!
来源
在本文中,我将提供著名的朴素贝叶斯算法的一个非常简短和直观的实现。为了理解这个简单的概念,理解下图的意思就够了:)满足贝叶斯定理!
在实现高斯朴素贝叶斯分类器之前,我们应该写 2 个简单的假设:
- 我们的数据呈正态分布
- 我们希望我们的数据列有条件地相互独立
这个模型最好的一点就是朴素贝叶斯效果很好,即使这些条件都不满足!让我们通过 3 个简单的步骤最终实现这个模型!
- 准备数据集
首先,和往常一样,我会准备资料。今天,我将使用一个众所周知的虹膜数据集,我们将使用它进行多类分类。
2。计算列车组统计数据
现在,我将按类对我们的训练集进行分组,并计算按类分组的每一列的平均值和标准差。此外,我将计算类先验概率,它简单地是类元素的数量除以训练集中的元素总数。
哇,这似乎是我们的适应功能。
3。计算验证集的标签
最后一步是计算贝叶斯定理的概率。对于每个元素,我将计算它属于每个类的概率。注意,这里我没有除以 P(B),因此,我没有得到真实的概率值。
另外,请注意,我在以下面的方式执行乘法时使用了独立性假设。
现在让我们看看我们的预测的准确性和来自 Sklearn 库的 GaussianNB 分类器的预测的准确性。
哇,看起来完全一样!感谢您的阅读:)
推荐书籍
使用 Scikit-Learn 进行机器实践学习
用 Python 进行机器学习和深度学习
Python 机器学习简介:数据科学家指南
你可以在我的 网站 上查看其他帖子
使用 keras 实现神经机器翻译
原文:https://towardsdatascience.com/implementing-neural-machine-translation-using-keras-8312e4844eb8?source=collection_archive---------5-----------------------
在 keras 中使用序列到序列算法逐步实现神经机器翻译。
在本文中,您将逐步了解如何使用英语作为源语言,西班牙语作为目标语言来构建神经机器翻译器。我们将在 keras 中使用长短期记忆(LSTM)单位
先决条件:
[## 神经机器翻译的直观解释
简单解释用于神经机器翻译的序列到序列模型(NMT)
towardsdatascience.com](/intuitive-explanation-of-neural-machine-translation-129789e3c59f)
在本文中,我们将使用编码器和解码器框架,以 LSTM 为基本块,创建一个序列到序列模型
序列到序列模型将源序列映射到目标序列。源序列是机器翻译系统的输入语言,目标序列是输出语言。
序列对序列模型
使用无注意机制的教师强制逐步实现神经机器翻译(NMT)。实现是使用 keras 库,以 LSTM 为基本块
实施 NMT 的高级步骤包括
- 从包含源句子和目标句子的文件中读取数据
- 通过转换为小写、删除空格、特殊字符、数字和引号来清除数据
- 分别使用 START_ 和 _END 标记目标句子的开头和结尾,用于训练和推理
- 创建唯一源词和目标词的字典,以进行向量转换,反之亦然
- 打乱数据以便更好地归纳
- 将数据集拆分为训练和测试数据
- 创建数据;我们将使用 fit_generator()将数据拟合到模型中
- 使用嵌入和 LSTM 图层构建编码器
- 使用嵌入层和 LSTM 层构建解码器,并从嵌入层和编码器状态获取输入。
- 编译模型并训练模型
- 根据模型进行预测
导入所需的库
**import pandas as pd
import numpy as np
import string
from string import digits
import matplotlib.pyplot as plt
%matplotlib inline
import re
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
from keras.layers import Input, LSTM, Embedding, Dense
from keras.models import Model**
从文件中读取数据
在这里 阅读包含我们从 下载的英语-西班牙语翻译的文件
# Path to the data txt file on disk.**data_path = "\\NMT\\spa-eng\\spa.txt"**# open the file eng-spa.txt and read
**lines= pd.read_table(data_path, names =['source', 'target', 'comments'])**#printing sample data from lines
lines.sample(6)
清理源句和目标句。
我们应用以下文本清理
- 将文本转换为小写
- 删除引号
- 删除所有特殊字符,如“@,!, *, $, #, ?、%等。”
- 清除源句子和目标句子中的数字。如果源语言或目标语言对数字使用不同的符号,那么删除这些符号
- 删除空格
# convert source and target text to Lowercase
**lines.source=lines.source.apply(lambda x: x.lower())
lines.target=lines.target.apply(lambda x: x.lower())**# Remove quotes from source and target text
**lines.source=lines.source.apply(lambda x: re.sub("'", '', x))
lines.target=lines.target.apply(lambda x: re.sub("'", '', x))**# create a set of all special characters
**special_characters= set(string.punctuation)**# Remove all the special characters
**lines.source = lines.source.apply(lambda x: ''.join(char1 for char1 in x if char1 not in special_characters))
lines.target = lines.target.apply(lambda x: ''.join(char1 for char1 in x if char1 not in special_characters))**# Remove digits from source and target sentences
**num_digits= str.maketrans('','', digits)
lines.source=lines.source.apply(lambda x: x.translate(num_digits))
lines.target= lines.target.apply(lambda x: x.translate(num_digits))**# Remove extra spaces
**lines.source=lines.source.apply(lambda x: x.strip())
lines.target=lines.target.apply(lambda x: x.strip())
lines.source=lines.source.apply(lambda x: re.sub(" +", " ", x))
lines.target=lines.target.apply(lambda x: re.sub(" +", " ", x))**
给目标句子添加开始和结束标记。
向目标句子添加 START_ 和 _END 标记对于训练和推理非常有用。这些标签有助于知道何时开始翻译,何时结束翻译。
# Add start and end tokens to target sequences
**lines.target = lines.target.apply(lambda x : 'START_ '+ x + ' _END')**
lines.sample(6)
START_ tag 标记目标句子的开始,而 _END 标记标记目标句子的结束。
从数据集中为源语言和目标语言创建一组独特的单词,并按字母顺序对它们进行排序
# Find all the source and target words and sort them
# Vocabulary of Source language
**all_source_words=set()
for source in lines.source:
for word in source.split():
if word not in all_source_words:
all_source_words.add(word)**# Vocabulary of Target
**all_target_words=set()
for target in lines.target:
for word in target.split():
if word not in all_target_words:
all_target_words.add(word)**
# sort all unique source and target words
**source_words= sorted(list(all_source_words))
target_words=sorted(list(all_target_words))**
未排序的源词汇集
找出数据集中源句子和目标句子的最大长度
#Find maximum sentence length in the source and target data
**source_length_list=[]
for l in lines.source:
source_length_list.append(len(l.split(' ')))
max_source_length= max(source_length_list)**
**print(" Max length of the source sentence",max_source_length**)**target_length_list=[]
for l in lines.target:
target_length_list.append(len(l.split(' ')))
max_target_length= max(target_length_list)****print(" Max length of the target sentence",max_target_length**)
为数据集中所有唯一的源词和目标词创建词索引词典和词索引词典。
要向量的单词的大小将基于源和目标词汇的长度
# creating a word to index(word2idx) for source and target
**source_word2idx= dict([(word, i+1) for i,word in enumerate(source_words)])
target_word2idx=dict([(word, i+1) for i, word in enumerate(target_words)])**
索引词典的源词
#creating a dictionary for index to word for source and target vocabulary
**source_idx2word= dict([(i, word) for word, i in source_word2idx.items()])** print(source_idx2word)**target_idx2word =dict([(i, word) for word, i in target_word2idx.items()])**
源词汇的词典索引
混洗数据
洗牌有助于
- 减少方差
- 确保模型保持通用,减少过度拟合
- 不同时期之间的批次看起来不一样
- 使模型更加健壮
#Shuffle the data
**lines = shuffle(lines)**
创建训练和测试数据集
# Train - Test Split
**X, y = lines.source, lines.target**
**X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.1)**
X_train.shape, X_test.shape
训练和测试数据集中的样本
创建用于训练编码器-解码器模型的数据。
我们将使用fit _ generator()而不是 fit() 方法,因为我们的数据太大,无法放入内存。 fit_generator() 需要一个底层函数来生成数据。
我们创建底层函数generate _ batch()用于批量生成数据
fit _ generator()将从底层函数 接受一批数据,generate_batch()
为了训练序列到序列模型,我们需要为
- 编码器输入:2D 数组的形状为 (batch_size,最大源语句长度)。对于 128 的 batch_size 和 47 的最大源句子长度, encoder_input 的形状将是(128,47)
- 解码器输入:2D 数组将为 (batch_size,最大目标句子长度)的形状。对于 128 的 batch_size 和 55 的最大目标句子长度, 解码器输入的形状将是(128,55)
- 解码器输出:3D 数组的形状为 (batch_size,最大目标句子长度,目标句子中唯一单词的数量)。对于 128 的 batch_size 和 55 的最大目标句子长度, 解码器输出的形状将是(128,55, 27200 )。
target_sentence 中唯一单词的数量是 27199,我们用零填充,因此解码器输出中的第三个参数是 27200
# Input tokens for encoder
**num_encoder_tokens=len(source_words)**# Input tokens for decoder zero padded
**num_decoder_tokens=len(target_words) +1**
我们现在创建 生成器 _ 批处理 函数()
**def generate_batch(X = X_train, y = y_train, batch_size = 128):**
''' Generate a batch of data '''
** while True:
for j in range(0, len(X), batch_size):**
**encoder_input_data** = np.zeros(**(batch_size, max_source_length)**,dtype='float32')
**decoder_input_data** = np.zeros((**batch_size, max_target_length**),dtype='float32')
**decoder_target_data** = np.zeros((**batch_size, max_target_length, num_decoder_tokens**),dtype='float32')
**for i, (input_text, target_text) in enumerate(zip(X[j:j+batch_size], y[j:j+batch_size])):**
**for t, word in enumerate(input_text.split()):****encoder_input_data[i, t] = source_word2idx[word]**
for t, word in enumerate(target_text.split()):
**if t<len(target_text.split())-1:
decoder_input_data[i, t] = target_word2idx[word]** # decoder input seq
if t>0:
# decoder target sequence (one hot encoded)
# does not include the START_ token
# Offset by one timestep
#print(word)
**decoder_target_data[i, t - 1, target_word2idx[word]] = 1.**
**yield([encoder_input_data, decoder_input_data], decoder_target_data)**
我们将使用教师强制来训练序列到序列模型,以便更快更有效地训练解码器。
教师强制算法通过提供前一个时间戳的实际输出而不是上一个时间步的预测输出作为训练期间的输入来训练解码器。
解码器学习在 t+1 时间步长生成一个字,考虑时间步长 t 的实际输出和编码器的内部状态;因此,我们将解码器输出偏移一个时间步长。
构建序列对序列模型
设置基本参数
我们设置必要的参数,如
- 训练样本数量
- 验证样本的数量
- 用于创建训练数据的 batch_size
- 训练的时代
- 编码空间的潜在维度
**train_samples = len(X_train)
val_samples = len(X_test)
batch_size = 128
epochs = 50
latent_dim=256**
构建模型
使用 LSTM 构建编码器和解码器。编码器将对源语言的输入句子进行编码。编码器的隐藏状态和单元状态将作为输入与实际目标序列一起传递给解码器。
构建编码器
编码器将对输入序列进行编码。我们通过输入层传递输入。第一个隐藏层将是嵌入层。嵌入将大的稀疏向量转换成密集的低维空间,保持语义关系。
将三个参数传递给Embedding();第一个参数是词汇量的大小;第二个参数是密集嵌入的维度。我们将 mask_zero 设置为 True ,因为这意味着输入值 0 是一个特殊的“填充”值,应该被屏蔽掉。
创建 LSTM 层,仅将 return_state 设置为 True ,因为我们希望保留编码器的隐藏状态和单元状态。我们丢弃 encoder_output,并保留要传递给解码器的 LSTM 的隐藏状态和单元状态
# Define an input sequence and process it.
**encoder_inputs = Input(shape=(None,))**
**enc_emb = Embedding(num_encoder_tokens, latent_dim, mask_zero = True)(encoder_inputs)**
**encoder_lstm = LSTM(latent_dim, return_state=True)****encoder_outputs, state_h, state_c = encoder_lstm(enc_emb)**# We discard `encoder_outputs` and only keep the states.
**encoder_states = [state_h, state_c]**
编码器
构建解码器
我们为 decoder_inputs 创建输入层;嵌入又是解码器中的第一个隐藏层。
LSTM 层将返回输出序列以及内部状态。内部状态仅在推断阶段使用,在训练阶段不使用。
解码器中的 LSTM 从嵌入层和编码器状态获取输入。我们将 softmax 激活应用于密集层,然后最终生成解码器输出
# Set up the decoder, using `encoder_states` as initial state.
**decoder_inputs = Input(shape=(None,))**
**dec_emb_layer = Embedding(num_decoder_tokens, latent_dim, mask_zero = True)
dec_emb = dec_emb_layer(decoder_inputs)**# We set up our decoder to return full output sequences,
# and to return internal states as well. We don't use the
# return states in the training model, but we will use them in inference.
**decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)**
**decoder_outputs, _, _ = decoder_lstm(dec_emb,
initial_state=encoder_states)**
**decoder_dense = Dense(num_decoder_tokens, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)**
定义模型
序列到序列模型将编码器和解码器输入转换为解码器输出
# Define the model that takes encoder and decoder input
# to output decoder_outputs
**model = Model([encoder_inputs, decoder_inputs], decoder_outputs)**
训练模型
为了训练模型,我们首先编译模型,然后将数据拟合到模型中
我们使用“rmsprop”优化器编译模型;使用 categorical _ crossentropy,就像我们使用 categorial 标签一样,这是一个热编码的向量
model.compile(optimizer=’rmsprop’, loss=’categorical_crossentropy’, metrics=[‘acc’])
设置参数以适应模型
train_samples = len(X_train) # Total Training samples
val_samples = len(X_test) # Total validation or test samples
batch_size = 128
epochs = 100
使用【fit _ generator()拟合模型。我们已经创建了用于生成数据的底层函数, generate_batch() 用于生成训练和测试数据集。
steps_per_epoch 通过将训练样本总数除以批量大小来计算。当我们达到步数时,我们开始一个新的纪元
**model.fit_generator(generator = generate_batch(X_train, y_train, batch_size = batch_size),
steps_per_epoch = train_samples//batch_size,
epochs=epochs,
validation_data = generate_batch(X_test, y_test, batch_size = batch_size),
validation_steps = val_samples//batch_size)**
保存权重,以便以后加载它们进行推理
model.save_weights(‘nmt_weights_100epochs.h5’)
可以从保存的权重文件中加载权重
model.load_weights('nmt_weights_100epochs.h5')
从模型中做出推论
在推理过程中,我们希望解码未知的输入序列来预测输出。
推理步骤
- 将输入序列编码成 LSTM 的隐藏状态和单元状态
- 解码器将一次预测一个序列。解码器的第一个输入将是编码器的隐藏状态和单元状态以及 START_ tag
- 如下图所示,解码器的输出将作为下一时间步的输入提供给解码器
- 在每个时间步,解码器输出一个我们应用 np.argmax 的独热编码向量,并将该向量转换为存储单词索引的字典中的单词
- 不断追加在每个时间步生成的目标词
- 重复这些步骤,直到我们遇到 _END 标签或单词限制
定义推理模型
# Encode the input sequence to get the "Context vectors"
**encoder_model = Model(encoder_inputs, encoder_states)**# Decoder setup
# Below tensors will hold the states of the previous time step
**decoder_state_input_h = Input(shape=(latent_dim,))
decoder_state_input_c = Input(shape=(latent_dim,))
decoder_state_input = [decoder_state_input_h, decoder_state_input_c]**# Get the embeddings of the decoder sequence
**dec_emb2= dec_emb_layer(decoder_inputs)**# To predict the next word in the sequence, set the initial states to the states from the previous time step
**decoder_outputs2, state_h2, state_c2 = decoder_lstm(dec_emb2, initial_state=decoder_state_input)
decoder_states2 = [state_h2, state_c2]**
# A dense softmax layer to generate prob dist. over the target vocabulary
**decoder_outputs2 = decoder_dense(decoder_outputs2)**# Final decoder model
**decoder_model = Model(
[decoder_inputs] + decoder_state_input,
[decoder_outputs2] + decoder_states2)**
创建一个用于推理查找的函数
**def decode_sequence(input_seq):**
# Encode the input as state vectors.
**states_value = encoder_model.predict(input_seq)**
# Generate empty target sequence of length 1.
** target_seq = np.zeros((1,1))**
# Populate the first character of
#target sequence with the start character.
** target_seq[0, 0] = target_word2idx['START_']**# Sampling loop for a batch of sequences
# (to simplify, here we assume a batch of size 1).
**stop_condition = False
decoded_sentence = ''
while not stop_condition:
output_tokens, h, c = decoder_model.predict([target_seq] + states_value)**# Sample a token
**sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_word =target_idx2word[sampled_token_index]
decoded_sentence += ' '+ sampled_word**# Exit condition: either hit max length
# or find stop character.
**if (sampled_word == '_END' or
len(decoded_sentence) > 50):
stop_condition = True**# Update the target sequence (of length 1).
**target_seq = np.zeros((1,1))
target_seq[0, 0] = sampled_token_index**# Update states
**states_value = [h, c]****return decoded_sentence**
对训练数据集进行预测
**train_gen = generate_batch(X_train, y_train, batch_size = 1)
k=-1**
传递一个源句子,然后将预测输出与实际输出进行比较
**k+=1
(input_seq, actual_output), _ = next(train_gen)
decoded_sentence = decode_sequence(input_seq)
print(‘Input Source sentence:’, X_train[k:k+1].values[0])
print(‘Actual Target Translation:’, y_train[k:k+1].values[0][6:-4])
print(‘Predicted Target Translation:’, decoded_sentence[:-4])**
对测试数据集进行预测
**test_gen = generate_batch(X_test, y_test, batch_size = 1)**
k=10
k+=1
**(input_seq, actual_output), _ = next(test_gen)
decoded_sentence = decode_sequence(input_seq)**
**print('Input Source sentence:', X_test[k:k+1].values[0])
print('Actual Target Translation:', y_test[k:k+1].values[0][6:-4])
print('Predicted Target Translation:', decoded_sentence[:-4])**
有些预测很好,有些合理,有些不正确。
模型的其他增强功能
我们可以替换 LSTM 的威尔·GRU,增加更多的 LSTM/GRU 节点,为更多的纪元训练并使用注意机制
参考:
[## 序列到序列—培训— Keras 文档
这个脚本演示了如何实现一个基本的字符级序列到序列模型。我们将它应用于…
keras.io](https://keras.io/examples/lstm_seq2seq/)
https://blog . keras . io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras . html
用 Tensorflow 实现带注意机制的神经机器翻译
原文:https://towardsdatascience.com/implementing-neural-machine-translation-with-attention-using-tensorflow-fc9c6f26155f?source=collection_archive---------5-----------------------
使用 Bahdanau 注意力的神经机器翻译(NMT)的 Tensorflow 实现的逐步解释。
在本文中,您将学习如何使用 Bahdanau 的注意力机制实现序列到序列(seq2seq)神经机器翻译(NMT)。我们将使用门控递归单元(GRU)在 Tensorflow 2.0 中实现代码。
照片由 Aaron Burden 在 Unsplash
先决条件
使用注意机制的序列对序列模型
神经机器翻译的直观解释
神经机器翻译(NMT)是使用深度神经网络将源语言(如英语)的单词序列转换为目标语言(如印地语或西班牙语)的单词序列的任务。
使用由编码器和解码器组成的序列到序列(seq2seq)模型来实现 NMT。编码器将源序列的完整信息编码成单个实值向量,也称为上下文向量,传递给解码器以产生输出序列,该输出序列是类似于印地语或西班牙语的目标语言。
上下文向量负责将整个输入序列总结成单个向量,这是低效的,所以我们使用注意机制。
注意机制的基本思想是避免试图学习每个句子的单一向量表示;相反,它基于关注权重关注输入序列的特定输入向量。
出于实施目的,我们将使用英语作为源语言,西班牙语作为目标语言。代码将使用 TensorFlow 2.0 实现,数据可以从这里下载。
实现具有注意机制的 NMT 的步骤
- 加载数据并通过删除空格、特殊字符等对其进行预处理。
- 创建数据集
- 创建编码器、注意力层和解码器
- 创建优化器和损失函数
- 训练模型
- 做出推论
导入所需的库
import pandas as pd
import numpy as np
import string
from string import digits
import matplotlib.pyplot as plt
%matplotlib inline
import tensorflow as tf
import matplotlib.ticker as ticker
from sklearn.model_selection import train_test_split
import re
import os
import io
import time
从文件中读取数据
阅读可以从这里下载的英语-西班牙语翻译文件。
我已将文件存储在“spa.txt”中
**data_path = "spa.txt"**#Read the data
**lines_raw= pd.read_table(data_path,names=['source', 'target', 'comments'])
lines_raw.sample(5)**
对源语句和目标语句进行清洗和预处理。
我们应用以下文本清理
- 将文本转换为小写
- 删除引号
- 清除源句子和目标句子中的数字。如果源语言或目标语言对数字使用不同的符号,那么删除这些符号
- 删除空格
- 在单词和标点符号之间加一个空格,比如“?”
- 在句首添加“start_”标记,在句尾添加“_end”标记
**def preprocess_sentence(sentence):**
num_digits= str.maketrans('','', digits)
**sentence= sentence.lower()
sentence= re.sub(" +", " ", sentence)
sentence= re.sub("'", '', sentence)
sentence= sentence.translate(num_digits)
sentence= re.sub(r"([?.!,¿])", r" \1 ", sentence)
sentence = sentence.rstrip().strip()
sentence= 'start_ ' + sentence + ' _end'**
**return sentence**
让我们选取英语中的一个句子并对其进行预处理
print(preprocess_sentence(“Can you do it in thirty minutes?”))
预处理源句子和目标句子,使单词对的格式为:[英语,西班牙语]
**def create_dataset(path, num_examples):**
**lines = io.open(path, encoding='UTF-8').read().strip().split('\n')**
** word_pairs = [[preprocess_sentence(w) for w in l.split('\t')] for l in lines[:num_examples]]**
**return zip(*word_pairs)****sample_size=60000
source, target = create_dataset(data_path, sample_size)**
标记源句子和目标句子
我们需要对文本语料库进行矢量化,将文本转换成整数序列。
我们首先创建分词器,然后在源句子上应用分词器
# create a tokenizer for source sentence
**source_sentence_tokenizer= tf.keras.preprocessing.text.Tokenizer(filters='')**# Fit the source sentences to the source tokenizer
**source_sentence_tokenizer.fit_on_texts(source)**
我们现在将源句子中的每个单词转换成一个整数序列,用相应的整数值替换这个单词。
只有分词器知道的单词才会被考虑
#Transforms each text in texts to a sequence of integers.
**source_tensor = source_sentence_tokenizer.texts_to_sequences(source)**
我们需要创建长度相同的序列,所以我们用“0”来填充长度较短的序列
#Sequences that are shorter than num_timesteps, padded with 0 at the end.
**source_tensor= tf.keras.preprocessing.sequence.pad_sequences(source_tensor,padding='post' )**
以类似的方式标记目标句子
# create the target sentence tokenizer **target_sentence_tokenizer= # Fit tf.keras.preprocessing.text.Tokenizer(filters='')**# Fit the tokenizer on target sentences **target_sentence_tokenizer.fit_on_texts(target)**#conver target text to sequnec of integers **target_tensor = target_sentence_tokenizer.texts_to_sequences(target)**# Post pad the shorter sequences with 0 **target_tensor= tf.keras.preprocessing.sequence.pad_sequences(target_tensor,padding='post' )**
创建训练和测试数据集
将数据集拆分为测试和训练。80%的数据用于训练,20%用于测试模型
**source_train_tensor, source_test_tensor, target_train_tensor, target_test_tensor= train_test_split(source_tensor, target_tensor,test_size=0.2)**
当数据集很大时,我们希望在内存中创建数据集以提高效率。我们将使用TF . data . dataset . from _ tensor _ slices()方法以对象的形式获取数组的切片。
数据集以 64 个为一批创建。
#setting the BATCH SIZE
**BATCH_SIZE = 64**#Create data in memeory **dataset=tf.data.Dataset.from_tensor_slices((source_train_tensor, target_train_tensor)).shuffle(BATCH_SIZE)**# shuffles the data in the batch
**dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)**
我们遍历数据集中的所有元素。返回的迭代器实现了 Python 迭代器协议,因此只能在急切模式下使用
#Creates an Iterator for enumerating the elements of this dataset.
#Extract the next element from the dataset
**source_batch, target_batch =next(iter(dataset))
print(source_batch.shape)**
每批源数据的大小为 (BATCH_SIZE,max_source_length), ,目标数据的批量为 (BATCH_SIZE,max_target_length)。 在我们的例子中,最大源长度是 11,最大目标长度是 16
在 Bahdanau 的关注下,使用门控循环单元(GRU)创建序列对序列模型
有注意的 seq2seq 模型和无注意的 seq2seq 模型的区别
- 编码器和解码器的所有隐藏状态(向前和向后)用于生成上下文向量,不像 eq2seq 没有注意,它使用最后的编码器隐藏状态。
- 注意机制利用由前馈网络参数化的比对分数来比对输入和输出序列。它有助于注意源序列中最相关的信息。
- Seq2Seq 注意模型基于与源位置相关联的上下文向量来预测目标单词,并且与 seq2seq 不同,先前生成的目标单词在没有注意的情况下将所有源序列编码到单个上下文向量中
为模型设置一些参数
**BUFFER_SIZE = len(source_train_tensor)
steps_per_epoch= len(source_train_tensor)//BATCH_SIZE
embedding_dim=256
units=1024
source_vocab_size= len(source_sentence_tokenizer.word_index)+1
target_vocab_size= len(target_sentence_tokenizer.word_index)+1**
创建编码器
编码器将输入作为源令牌,将它们传递给嵌入层以获得矢量的密集表示,然后传递给 GRU。
为 GRU 设置返回序列和返回状态为真。默认情况下,return _ sequneces 设置为 False。当 return_sequences 设置为真时,则返回编码器中所有单元的整个输出序列。当 return_sequences 设置为 False 时,我们只返回最后一个编码器单元的隐藏状态。
seq2seq 在没有注意的情况下会将编码器的 return_sequences 设置为 False。Seq2seq 将把编码器的 return_sequences 设置为 True。
为了返回 GRU 的内部状态,我们将 retrun_state 设置为 True
**class Encoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, encoder_units, batch_size):
super(Encoder, self).__init__()
self.batch_size= batch_size
self.encoder_units=encoder_units
self.embedding=tf.keras.layers.Embedding(vocab_size, embedding_dim)
self.gru= tf.keras.layers.GRU(encoder_units,
return_sequences=True,
return_state=True, recurrent_initializer='glorot_uniform'
)**
**def call(self, x, hidden):**
#pass the input x to the embedding layer
** x= self.embedding(x)**
# pass the embedding and the hidden state to GRU
**output, state = self.gru(x, initial_state=hidden)
return output, state**
**def initialize_hidden_state(self):
return tf.zeros((self.batch_size, self.encoder_units))**
测试编码器类并打印编码器输出和隐藏状态的尺寸
**encoder = Encoder(source_vocab_size, embedding_dim, units, BATCH_SIZE)****sample_hidden = encoder.initialize_hidden_state()****sample_output, sample_hidden= encoder(source_batch, sample_hidden)**print ('Encoder output shape: (batch size, sequence length, units) {}'.format(sample_output.shape))
print ('Encoder Hidden state shape: (batch size, units) {}'.format(sample_hidden.shape))
创建 Bahdanau 注意力图层
注意力层包括
- 比对分数
- 注意力权重
- 上下文向量
我们将在注意力层实现这些简化的等式
巴赫达瑙注意方程式
**class BahdanauAttention(tf.keras.layers.Layer):
def __init__(self, units):
super( BahdanauAttention, self).__init__()
self.W1= tf.keras.layers.Dense(units) # encoder output
self.W2= tf.keras.layers.Dense(units) # Decoder hidden
self.V= tf.keras.layers.Dense(1)**
**def call(self, query, values):**
#calculate the Attention score
**score= self.V(tf.nn.tanh(self.W1(values) + self.W2(hidden_with_time_axis)))**
# attention_weights shape == (batch_size, max_length, 1)
** attention_weights= tf.nn.softmax(score, axis=1)**
#context_vector
**context_vector= attention_weights * values**
#Computes the sum of elements across dimensions of a tensor
** context_vector = tf.reduce_sum(context_vector, axis=1)
return context_vector, attention_weights**
用十个单位测试 Bahdanau 注意层
a**ttention_layer= BahdanauAttention(10)**
**attention_result, attention_weights = attention_layer(sample_hidden, sample_output)**print("Attention result shape: (batch size, units) {}".format(attention_result.shape))
print("Attention weights shape: (batch_size, sequence_length, 1) {}".format(attention_weights.shape))
创建解码器
解码器具有嵌入层、GRU 层和全连接层。
来预测解码器使用的目标字
- 上下文向量:注意力权重和编码器输出的总和
- 前一时间步的解码器输出和
- 前一个解码器的隐藏状态
**class Decoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, decoder_units, batch_sz):
super (Decoder,self).__init__()
self.batch_sz= batch_sz
self.decoder_units = decoder_units
self.embedding = tf.keras.layers.Embedding(vocab_size,
embedding_dim)
self.gru= tf.keras.layers.GRU(decoder_units,
return_sequences= True,
return_state=True,
recurrent_initializer='glorot_uniform')**
# Fully connected layer
**self.fc= tf.keras.layers.Dense(vocab_size)**
# attention
**self.attention = BahdanauAttention(self.decoder_units)**
** def call(self, x, hidden, encoder_output):**
**context_vector, attention_weights = self.attention(hidden,
encoder_output)**
# pass output sequnece thru the input layers
**x= self.embedding(x)**
# concatenate context vector and embedding for output sequence
**x= tf.concat([tf.expand_dims( context_vector, 1), x],
axis=-1)**
# passing the concatenated vector to the GRU
** output, state = self.gru(x)**
# output shape == (batch_size * 1, hidden_size)
** output= tf.reshape(output, (-1, output.shape[2]))**
# pass the output thru Fc layers
**x= self.fc(output)
return x, state, attention_weights**
测试解码器
**decoder= Decoder(target_vocab_size, embedding_dim, units, BATCH_SIZE)
sample_decoder_output, _, _= decoder(tf.random.uniform((BATCH_SIZE,1)), sample_hidden, sample_output)**
print ('Decoder output shape: (batch_size, vocab size) {}'.format(sample_decoder_output.shape))
定义优化器
我们在这里使用 Adam 优化器;你也可以试试 Rmsprop
#Define the optimizer and the loss function
optimizer = tf.keras.optimizers.Adam()
定义损失函数
使用SparseCategoricalCrossentropy计算实际和预测输出之间的损失。
如果输出是一次性编码的向量,那么使用 categorical _ crossentropy。对包含整数的 word2index 向量使用SparseCategoricalCrossentropyloss。
稀疏分类交叉熵在计算和内存上是高效的,因为它使用单个整数,而不是整个向量[0 0 1]
**loss_object = tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=True, reduction='none')****def loss_function(real, pred):
mask = tf.math.logical_not(tf.math.equal(real, 0))
loss_ = loss_object(real, pred)****mask = tf.cast(mask, dtype=loss_.dtype)
loss_ *= mask****return tf.reduce_mean(loss_)**
训练数据集
使用编码器-解码器模型训练数据集
- 将编码后的源句子通过编码器,并返回编码器输出序列和隐藏状态
- 编码器输出、编码器隐藏状态和解码器输入被传递给解码器。在时间步长=0 时,解码器将“start_”作为输入。
- 解码器返回预测字和解码器隐藏状态
- 解码器隐藏状态被传递回模型,并且预测的字被用于计算损失
- 为了训练,我们使用教师强制,在每个时间步将实际单词传递给解码器。
- 在推断过程中,我们将前一时间步的预测单词作为输入传递给解码器
- 计算梯度下降,将其应用于优化器并反向传播
注意机制
Tensorflow 跟踪每个 tf.Variable 上每个计算的每个梯度。为了训练,我们使用梯度带,因为我们需要控制需要梯度信息的代码区域。对于具有注意机制的 seq2seq,我们仅计算解码器输出的梯度。
**def train_step(inp, targ, enc_hidden):
loss = 0
with tf.GradientTape() as tape:**
#create encoder
** enc_output, enc_hidden = encoder(inp, enc_hidden)
dec_hidden = enc_hidden**
#first input to decode is start_
**dec_input = tf.expand_dims(
[target_sentence_tokenizer.word_index['start_']] * BATCH_SIZE, 1)**
# Teacher forcing - feeding the target as the next input
**for t in range(1, targ.shape[1]):**
# passing enc_output to the decoder
**predictions, dec_hidden, _ = decoder(dec_input, dec_hidden, enc_output)**
# calculate loss based on predictions
**loss += tf.keras.losses.sparse_categorical_crossentropy(targ[:, t], predictions)**
# using teacher forcing
**dec_input = tf.expand_dims(targ[:, t], 1)
batch_loss = (loss / int(targ.shape[1]))
variables = encoder.trainable_variables + decoder.trainable_variables
gradients = tape.gradient(loss, variables)
optimizer.apply_gradients(zip(gradients, variables))
return batch_loss**
使用多个历元集中训练编码器-解码器模型
**EPOCHS=20
for epoch in range(EPOCHS):
start = time.time()****enc_hidden = encoder.initialize_hidden_state()
total_loss = 0**
# train the model using data in bataches
f**or (batch, (inp, targ)) in enumerate(dataset.take(steps_per_epoch)):
batch_loss = train_step(inp, targ, enc_hidden)
total_loss += batch_loss****if batch % 100 == 0:
print('Epoch {} Batch {} Loss {}'.format(epoch + 1,
batch,
batch_loss.numpy()))
print('Epoch {} Loss {}'.format(epoch + 1,
total_loss / steps_per_epoch))
print('Time taken for 1 epoch {} sec\n'.format(time.time() - start))**
对测试数据进行推断
进行推理类似于训练,只是我们不知道教师强制中使用的实际单词,所以我们将来自前一时间步的预测单词作为输入传递给解码器。
我们计算每个时间步的注意力权重,因为它有助于关注用于进行预测的源序列中最相关的信息。
当我们达到最大目标句子长度时,或者当我们遇到“stop_”标签时,我们停止预测单词。
#Calculating the max length of the source and target sentences
**max_target_length= max(len(t) for t in target_tensor)
max_source_length= max(len(t) for t in source_tensor)**
做出这样的推断
- 传源句,
- 预处理句子以转换成小写,删除空格,特殊字符,在单词和标点符号之间加一个空格,等等。
- 对句子进行标记以创建 word2index 词典
- Post 用 0 填充源序列,使其长度与最大源句子的长度相同
- 创建输入张量
- 创建编码器并传递输入向量和隐藏状态。初始隐藏状态被设置为零
- 解码器的第一个输入将是“start_”标签。解码器的初始隐藏状态是编码器隐藏状态
- 创建解码器,向其传递解码器输入、解码器隐藏状态和编码器输出
- 存储注意力权重,并使用解码器输入、隐藏和上下文向量,找到具有最大概率的单词的整数。
- 将整数转换为单词,并不断追加预测的单词以形成目标句子,直到我们遇到“end_”标签或达到最大目标序列长度
**def evaluate(sentence):**
**attention_plot= np.zeros((max_target_length, max_source_length))**
#preprocess the sentnece
**sentence = preprocess_sentence(sentence)**
#convert the sentence to index based on word2index dictionary
**inputs= [source_sentence_tokenizer.word_index[i] for i in sentence.split(' ')]**
# pad the sequence
**inputs = tf.keras.preprocessing.sequence.pad_sequences([inputs], maxlen=max_source_length, padding='post')**
#conver to tensors
**inputs = tf.convert_to_tensor(inputs)**
**result= ''**
# creating encoder
**hidden = [tf.zeros((1, units))]**
**encoder_output, encoder_hidden= encoder(inputs, hidden)**
# creating decoder
**decoder_hidden = encoder_hidden**
**decoder_input = tf.expand_dims([target_sentence_tokenizer.word_index['start_']], 0)**
**for t in range(max_target_length):**
**predictions, decoder_hidden, attention_weights= decoder(decoder_input, decoder_hidden, encoder_output)**
# storing attention weight for plotting it
** attention_weights = tf.reshape(attention_weights, (-1,))
attention_plot[t] = attention_weights.numpy()
prediction_id= tf.argmax(predictions[0]).numpy()**
**result += target_sentence_tokenizer.index_word[prediction_id] + ' '
if target_sentence_tokenizer.index_word[prediction_id] == '_end':
return result,sentence, attention_plot**
# predicted id is fed back to as input to the decoder
** decoder_input = tf.expand_dims([prediction_id], 0)**
**return result,sentence, attention_plot**
函数来绘制源单词和目标单词之间的注意力权重。该图将帮助我们理解哪个源词在预测目标词时被给予了更多的关注
**def plot_attention(attention, sentence, predicted_sentence):
fig = plt.figure(figsize=(10,10))
ax= fig.add_subplot(1,1,1)
ax.matshow(attention, cmap='Greens')
fontdict={'fontsize':10}
ax.set_xticklabels([''] + sentence, fontdict=fontdict, rotation=90)
ax.set_yticklabels([''] + predicted_sentence, fontdict=fontdict)****ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(1))****plt.show()**
将源句翻译成目标句
为了将源句子翻译成目标语言,我们调用 evaluate 函数来创建编码器、解码器和关注层
**def translate(sentence):
result, sentence, attention_plot = evaluate(sentence)
print('Input : %s' % (sentence))
print('predicted sentence :{}'.format(result))
attention_plot= attention_plot[:len(result.split(' ')), :len(sentence.split(' '))]
plot_attention(attention_plot, sentence.split(' '), result.split(' '))**
最后的预测
**translate(u'I am going to work.')**
翻译句子的注意情节
注意力图
在翻译过程中,我们发现“去”比“去”更能预测“去”,同样,“工作”比“工作”更能预测“去”
可从 Github 获得的代码
参考
巴丹瑙注意了-【https://arxiv.org/pdf/1409.0473.pdf
https://www . tensor flow . org/tutorials/text/NMT _ with _ attention
https://www.tensorflow.org/guide/keras/rnn
使用 PyTorch 的神经类型转移
原文:https://towardsdatascience.com/implementing-neural-style-transfer-using-pytorch-fd8d43fb7bfa?source=collection_archive---------14-----------------------
简介
神经风格转移是基于深层神经网络生成艺术图像的人工系统。这种方法使用两个随机图像,内容和样式图像。它从内容图像中提取结构特征,而从风格图像中提取风格特征。
神经风格转移(作者 GIF)
内容和风格表现
内容表示
卷积神经网络沿着处理层次开发图像的表示。随着我们向网络的更深处移动,表示将更关心结构特征或实际内容,而不是详细的像素数据。为了获得这些表示,我们可以使用该层的特征图来重建图像。从较低层的重建将再现精确的图像。相反,较高层的重建将捕获高级内容,因此我们将来自较高层的特征响应称为内容表示。
ConvNet 不同层次的内容重构(图片来自艺术风格的神经算法(2015) )
上图显示了从层“conv1_1”、“conv2_1”、“conv3_1”、“conv4_1”和“conv5_1”重建输入图像。我们发现,从较低层重建的图像几乎与输入图像相同,但随着我们深入网络,详细的像素信息会丢失。相比之下,图像的高级内容被保留。
风格表现
为了提取样式内容的表示,我们在每个网络层的过滤器响应的顶部构建了一个特征空间。它包括在特征图的空间范围内不同滤波器响应之间的相关性。不同层的滤波器相关性捕获输入图像的纹理信息。这在丢弃全局排列的信息的同时,创建了与给定图像的风格越来越匹配的图像。这种多尺度表示被称为风格表示。
ConvNet 不同层次的风格重构(图片来自艺术风格的神经算法(2015) )
上图显示了代表 CNN 不同层中不同特征之间相关性的每个卷积层上方的特征空间。随着我们深入到网络中,我们可以看到全局排列或结构特征被丢弃了。
模型架构
论文《艺术风格的神经算法》中提出的模型的体系结构是
模型架构(图片来自艺术风格的神经算法(2015) )
这里,我们使用预训练的 VGG19 网络的卷积神经网络,并执行内容和风格重建。通过将来自内容表示的结构信息和来自风格表示的纹理/风格信息纠缠在一起,我们生成了艺术图像。我们可以强调重建风格或内容。对风格的强烈强调将导致图像与艺术品的外观相匹配,有效地给出了它的纹理版本,但几乎没有显示照片的任何内容。当把重点放在内容上时,人们可以识别照片,但绘画风格并不匹配。我们对生成的图像执行梯度下降,以找到与原始图像的特征响应相匹配的另一个图像。
实施
导入库
我们将从导入所需的库开始。我们将进口火炬,火炬视觉和 PIL,以实现使用 PyTorch 的风格转移。
加载模型
在这种情况下,我们将从 torchvision.models()加载预训练的 VGG19 模型。vgg 19 模型有三个组件特性,avgpool 和分类器。
- 该功能包含所有卷积层、最大池层和 ReLu 层
- avgpool 保存平均池层。
- 分类器保存密集层。
我们将只使用卷积神经网络来实现风格转换,因此导入 vgg19 特性。如果可能的话,不要忘记使用 GPU。这会节省训练时间。
图像预处理
为了使图像与模型兼容,必须对图像进行预处理。使用 torch.transforms() 我们将执行一些基本的预处理,包括以下步骤:
- 调整大小:将所有图像的大小调整为 512 x 512
- 将图像转换成张量
对于预训练的 vgg19 模型,还可以使用平均值(0.485,0.456,0.406)和标准差(0.229,0.224,0.225)对张量进行归一化。但是别忘了把它转换回原来的比例。因此,定义一个使用 PIL 库加载图像并对其进行预处理的函数。在第 0 个索引处添加一个额外的维度,使用 unsqueeze() 表示批量大小,然后将其加载到设备并返回。
现在,使用 image_loader 函数从本地磁盘加载样式和内容图像。通常的做法是使用内容映像克隆作为输入基础映像或生成的映像。由于梯度下降会改变生成图像的像素值,我们将为 require_grads()传递参数 true。
获取特征表示
让我们定义一个类,它将提供中间层的特征表示。使用中间层是因为这些层充当复杂的特征提取器。因此,这些可以描述输入图像的风格和内容。在本课程中,我们将通过删除 vgg19 模型中未使用的层(conv5_1 以外的层)来初始化模型,并提取“conv1_1”、“conv2_1”、“conv3_1”、“conv4_1”和“conv5_1”层的激活或特征表示(索引值[0,5,10,19,28])。将 5 个卷积层的激活存储在一个数组中,并返回该数组。
定义损失
风格转移的净损失定义为:
全损(图片来自一种艺术风格的神经算法(2015) )
在上面的等式中,L ₜₒₜₐₗ 是总损失,l𝒸ₒₙₜₑₙₜ是所有中间层的内容损失,lₛₜᵧₗₑt13】是所有中间层的风格损失。这里,α和β分别是内容和风格损失的加权系数。p、a 和 x 是内容图像、风格图像和生成图像或基本输入图像。我们对损失函数执行梯度下降,并且代替模型参数,我们更新输入图像 x 的像素值以最小化损失。这将使输入图像与内容和样式图像相似。我们可以通过改变α和β的值来强调风格或内容的损失。对风格的强烈强调将导致图像与艺术品的外观相匹配,有效地给出了它的纹理版本,但几乎没有显示照片的任何内容。当把重点放在内容上时,人们可以识别照片,但绘画风格并不匹配。****
内容丢失
内容图像和输入基本图像被传递到我们的模型,中间层的输出(上面列为“conv1_1”、“conv2_1”、“conv3_1”、“conv4_1”和“conv5_1”)使用上面定义的类提取。然后,我们计算内容图像的中间表示和输入基础图像之间的欧几里德距离。因此,层 l 的内容损失由下式定义:
内容丢失(图片来自艺术风格的神经算法(2015) )
在上面的等式中,我们计算内容图像(p)的特征表示和层(l)的输入基础图像(x)之间的平方误差。这里,nˡₕ、nˡ𝓌、nˡ𝒸是层 l 的高度、宽度和通道。为了计算内容损失,维度 nˡₕ x nˡ𝓌 x nˡ𝒸的中间表示被展开成维度 nˡ𝒸 x nˡₕ ***** n 的向量。展开要素制图表达不是强制步骤,但这是一个很好的做法。下图将有助于我们将这种转变形象化。
向 2D 展开 3D 特征地图(图片由作者提供)
Fˡᵢⱼ 和 Pˡᵢⱼ是代表输入基础图像和内容图像的中间表示的 nˡ𝒸×nˡₕ*****nˡ𝓌维度向量。
风格丧失
我们在网络的每一层上构建一个特征空间,表示不同滤波器响应之间的相关性。Gram matrix 计算这些特征相关性。
gram 矩阵表示中间表示中每个过滤器之间的相关性。通过取展开的中间表示及其转置的点积来计算格拉姆矩阵。格拉姆矩阵 g 的维数是 nˡ𝒸×nˡ𝒸,其中 nˡ𝒸是层 l 的中间表示中的通道数
Gram Matrix(图片来自一种艺术风格的神经算法(2015) )
上式中,Gˡᵢⱼ 是 l 层的矢量化特征图 I 和 j 的内积,一个 gram 矩阵的矢量化方程如下图所示,其中 g 是中间表示 a 的 gram 矩阵
Gram 矩阵计算(图片由作者提供)
Gram 矩阵矢量化方程(图片来自一种艺术风格的神经算法(2015) )
层 l 的风格损失是风格图像的中间表示和输入基础图像的 gram 矩阵之间的平方误差。
l 层的风格损失(图片来自艺术风格的神经算法(2015) )
其中 Eₗ 是层 l 的风格损失,Nₗ和 Mₗ分别是层 l 的特征表示中的通道数和高度乘以宽度。Gˡᵢⱼ和 Aˡᵢⱼ 分别是风格图像(a)和输入基础图像(x)的中间表示的克矩阵。
总风格损失为:
总体风格损失(图片来自艺术风格的神经算法(2015) )
其中 wˡ是每层对总风格损失的贡献的权重因子。
这里,我们没有将样式损失乘以常数,因为它使损失非常小。合计所有中间层的内容和样式损失,并计算总损失。
现在让我们继续训练模型。
训练
初始化我们训练模型所需的变量。因此,在继续训练之前,我们需要设置超参数。
在这里,为类 VGG 创建一个对象。初始化对象将调用构造函数,它将返回前 29 层的模型并将其加载到设备。Epoch 为 1000,学习率为 0.01,alpha(内容损失加权系数)为 1,beta(风格损失加权系数)为 0.01。
Adam 被用作优化器。生成的图像的像素数据将被优化,以将生成的图像作为优化器参数传递。
使用 for 循环迭代历元数。使用模型提取内容、风格和生成的图像的中间层的特征表示。在向模型传递图像时,它将返回一个长度为 5 的数组。每个元素对应于每个中间层的特征表示。
使用上面定义的函数计算总损耗。用 optimizer.zero_grads(),将渐变设置为零,用 total_loss.backward() 反向传播总损失,然后用 optimizer.step()更新生成图像的像素值。我们将在每 100 个历元后保存生成的图像,并打印总损失。
经过训练,你可以在你当前的工作目录中找到艺术形象。你的图像会像这样。
内容图片(左:图片来自维基百科 ) +风格图片(中:图片来自USGSon UNSP lash)=生成的图片(右:图片来自作者)
请随意使用超参数。有时你可能会得到预期的结果,但有时你可能会努力实现预期的结果。玩得开心!!!
结论
在本教程中,您已经学习了神经类型转移的基础知识,并建立了一些直觉。您已经加载了一个预训练的 vgg19 模型,冻结了它的重量并定制了模型层。加载图像并执行一些基本的预处理。然后定义了内容损失和风格损失,两者结合起来计算总损失。最后你跑了模型,做了一个艺术形象,是内容和风格形象的交融。
你可以在这里找到完整的代码。
参考
- 莱昂·A·加蒂丝,亚历山大·s·埃克,马蒂亚斯·贝奇,一种艺术风格的神经算法 (2015),arXiv