每天 10 分钟学习 Python # 5
每天 10 分钟 Python 速成班
变量:使用字符串表示文本
这是一个系列10 分钟的简短 Python 文章,帮助您提高 Python 知识。我试着每天发一篇文章(没有承诺),从最基础的开始,到更复杂的习惯用法。如果您想了解关于 Python 特定主题的问题或请求,请随时通过 LinkedIn联系我。
在第一节课中,我们已经看到了内置的“ print ”功能,它可以将文本、数字,实际上是任何东西打印到屏幕上。要调用一个函数,我们需要输入它的名字(在这个例子中是 print ),后面跟着一组括号。在括号之间,传递函数的参数。内置函数不需要任何参数,可以使用:" print() "调用,将一个空行打印到屏幕上。我们给它传递了一个参数,那就是一个简短的句子“hello world”。这个参数是一个“ str 类型的对象,在 Python 中是一个字符串。使用“?”检查内置打印功能的文档字符串我们以前学过。
可以使用单引号、双引号或由三次引号符号表示的多行变体来分配字符串。我们在创建文档字符串时已经看到了最后一个方法:
变量名可以是任何东西,除了像“是”或“不是”这样的关键字。函数“print”不是这些关键字中的一个,而是一个标准函数,从技术上讲,您可以用一个新变量覆盖这个函数。Python 中的一切都由对象表示,变量只是一个标签。通过声明一个名为“print”的字符串,标签现在指向新创建的字符串,这在 Python 中完全有效。当然,这可能不是你想要的,所以要小心。选择名字时,尽量简短,所有小写字母和单词用下划线隔开。虽然这样定义变量不是强制性的,但这是一个标准,在著名的 pep8 (Python 增强提案#8)中有记载。有许多 pep 文档,但 pep8 是一个帮助提高可读性的样式指南。这个文档太长了,很难记住,因此,有一些助手可以帮助你设计代码,比如黑色或者薄片 8 ,但是我们将在稍后的阶段研究这些。由于这些自动格式化程序不能帮助您处理变量名,所以请尝试使用建议的约定。它将帮助你使你的代码可读。
来自文档:字符串是 Unicode 码位的不可变序列。这意味着一个字符串是一个字符序列,由于它是不可变的,赋值后不能改变。当我开始学习 Python 时,术语“不可变的”让我很困惑。为什么我们要有一个字符串,它在定义后不能改变。他们所说的不可变是指,如果我们在一个字符串上做一个操作,原来的字符串不会改变,但是会从结果中创建一个新的字符串。如果您将结果放入原始变量(具有相同的名称,即引用),当变量指向您的新结果时,您将覆盖对旧对象的引用。如果原始对象没有被任何其他变量引用,Python 使用其垃圾收集器从内存中删除该字符串。
有相当多的操作可以在字符串上执行。最常见的操作之一是连接字符串,就像我们将两个数字相加一样(使用“+”运算符)。虽然不太常见,但您也可以使用’ * '操作符将字符串相乘,以从原始字符串的倍数创建新字符串。我猜用途很具体。
因为它是一个字符序列,所以它有一个序列长度。为了得到长度,有内置的“len”函数。序列的另一个特性是我们可以非常容易地迭代字符。我们还没有讨论 for-loops,但是它的语法可读性很强,所以我很确定你会看到下一个例子。
Python 中的一切都是对象,因此每个对象都打包了相关的方法。str 类有很多方法可以选择,我一般都会忘记,不得不 Google。在 Jupyter 或 IPython 中,你可以用点号键入变量的名称,然后按获得建议。
制表符补全是每个人的朋友!
这些方法中的大多数都有不言自明的名称,如“lower”、“upper”和“isdigit”。其他人可能需要一些解释,我们可以随时查看 DocString。我经常使用的方法是。split(),。替换()和。加入()。当这些方法返回一个新的字符串时,你可以链接多个命令。所以做 *my_string.lower()是非常有效的。分裂()*和更长的链甚至是可能的。创建链时,请记住可读性。
另一种可能用于字符串(因为它们是序列)的技术是切片。虽然分割字符串可能有非常具体的用例,但过程与其他类似序列的数据类型是相同的。通过切片,您可以选择序列的 iᵗʰ元素,方法是在方括号中指明它的序号。Python 从零开始计数,所以第一个元素表示为“my_string[0]”。您也可以选择一个元素,从后面用负数计数。字符串的最后一个元素是“my_string[-1]”。如果您想选择多个字符,您可以使用一个由[start : end : step]构成的范围。在 Python 中,范围包括“开始”元素,但不包括“结束”元素。步长表示您跳过了多少个字符。如果将步长设置为 1,则选择所有字符,如果步长为 2,则跳过第二个字符。没有必要提供全部(开始、结束、步骤)。如果没有提供步长,则使用默认值 1。只提供结尾,所以分号前没有值,意味着到结尾为止的所有内容(但不包括)。这里有几个例子:
一个重要的主题是字符串的格式化,即创建在变量中显示动态内容的字符串。在过去的几年里,这样做的方式已经改变了几次。首先,有 percent 操作符,它起作用,但不是很好。后来。format()方法被添加到字符串中,以提供更大的灵活性。从 Python3.6 开始,f 字符串变得可用,这增加了更多的灵活性。f 字符串是一种特殊类型的字符串,由字符串前的“f”表示。在我看来,f 字符串也比。format()方法,因此,我建议尽可能使用 f 字符串。格式化选项不仅仅限于显示变量本身,还可以添加填充、数字精度、类型等等。这里有一个很棒的小抄。
有许多关于字符串的细节,我的一般建议是当你需要它们的时候研究一下。随着时间的推移,你甚至可能记得一对夫妇;-).
今天的练习:
- 在一个新的笔记本中,分配不同的字符串。
- 使用方法更改下面的字符串:“我叫吉米,我 100 岁了”,以获得您的姓名和年龄。(提示:replace())
- 从以下字符串中切分并组合出’我很快乐’:
c,d = ‘我很悲伤’,‘我希望我很快乐’ - 摆弄几根 f 弦,例如显示不同精度的数值,创建不带小数的百分比。
如有任何问题,欢迎通过 LinkedIn 联系我。
下一期(#6)将于周一发布。
每天 10 分钟学习 Python # 6
每天 10 分钟 Python 速成班
Python 中的条件句:if、elif 和 else 结构
这是一个系列10 分钟的简短 Python 文章,帮助您提高 Python 知识。我试着每天发一篇文章(没有承诺),从最基础的开始,到更复杂的习惯用法。如果您想了解关于 Python 特定主题的问题或请求,请随时通过 LinkedIn联系我。
您可能已经知道缩进是 Python 中的一个特性。代码块不是使用花括号或 begin/end 语句,而是按照缩进级别进行分组。一开始,这可能看起来有点奇怪,但这是 Python 可读性如此之好的核心机制之一。你可以自由使用任何数量的缩进,例如两个空格,但是,在默认样式中( pep8 )推荐使用四个空格。如果你选择不同的金额(你不应该这样),或者如果你想使用标签(请不要),这是好的(只有有充分的理由),但你必须保持一致。如果不一致,会出现缩进错误。为了最大限度地减少这些错误,我强烈建议使用一个可以将 tab 键设置为四个空格而不是制表符的编辑器。几乎任何文本编辑器都有这个特性。
既然我们知道缩进很重要,我们可以讨论一个实际使用不同级别嵌套代码块的构造:if-else 语句。这些条件语句在几乎任何编程语言中都是非常常见的构造。它们测试某个条件,如果成功,则运行一条或多条语句,如果条件不成功,则可能运行一组不同的语句。下面是 Python 中一个简单的条件结构:
语法并不难理解。if 语句以 if 关键字开始,后跟一个条件。该条件可以是任何呈现为布尔值的条件。在 Python 中属于 bool 类型的布尔值要么为真(注意大写),要么为假。就像我已经说过很多次一样,Python 中的一切都由一个对象来表示,True 和 False 是已经为您创建的特殊对象。如果变量为真,则该变量指向这个特殊的真实例。在前面的例子中,我们使用双等号()测试变量 a 是否等于 30。如果是这种情况,这将导致 True,否则将导致 False,因此,总是呈现为布尔值。其他条件运算符有大于 ( >)、大于等于 ( > =)、小于 ( <)、小于等于 ( < =),或者不等于(!=).在条件语句之后,分号表示代码块的开始(后面是缩进的语句)。还有一个常用的特殊关键字:是。 is 关键字类似于,但是它测试两个变量(引用)是否指向同一个对象。在 Python 中只有一个真对象,如果两个变量都为真,那么它们指向内存中的同一个对象。我们稍后会了解到,当是时,语句相当有用。
Python 以线性方式运行代码,这意味着它从顶部开始,并顺序执行每条语句。这也是我们上面构建的 if-else 语句的情况。首先,它将测试第一个条件,如果这是真的,它将运行缩进的代码块中的代码,但 if-else 构造的其余部分将被忽略。只是,如果 if 语句的第一部分为假,它将继续到下一部分。这可以是 and elif 或一个‘else’。eli 是 else if 的一个更短的符号,在这里你可以测试另一个条件,如果条件为真,后面是另一个缩进的代码块。可以有任意数量的 elif 语句。if-else 结构中最后一个可选语句是 else 语句。这不测试任何条件,但如果 if 或 elif 语句都不为真,则执行该操作。执行 else 语句后缩进代码块中的所有语句。
如果要测试多个条件呢?对此,有几个逻辑运算符,类似于其他语言,即: and,或、、和 not 。这些逻辑运算符“组合”两个布尔值来创建一个新条件。和操作符要求两个值都为真,以产生真,而或操作符只要求其中一个值为真,以产生真本身。 not 运算符反转布尔值,即 not(True) == False。如果一个变量本身是布尔值,你可以直接测试它的条件。测试像 raining_outside == True 这样的条件是完全有效的,但是,如果布尔变量为 True,则测试 True == True。因为 if 语句需要一个布尔值,所以有一个就足够了。
这就是你需要了解的关于 if 语句流的全部内容。有几个细节很好了解,但不经常使用,但在特定情况下可能有用。例如,对象的一些方法返回一个布尔值,这些可以用 if 语句来测试。上周五我们学习的 str 对象有许多可以直接测试的方法。这些方法通常以 is…()开头,例如: isdigit() 、 isspace() 或 isupper() 。当然,你可以使用任何方法,使用条件运算符创建一个布尔:my _ string . count(’ ABC ')= = 2。
字符串,实际上是 Python 中的任何序列类型,也可以测试列表中的子序列。这是使用关键字中的完成的,对我来说这感觉很自然。例如,要测试单词“python”是否在字符串“I think python is easy”中,您需要做的唯一事情就是将放在字符串之间的中:“python”放在“I think python is easy”中。如果子字符串(左边部分)在主字符串(右边部分)中,则返回 True。我们将在后面看到类似的模式被用于列表数据类型。
与其他语言类似,也可以创建单行和内嵌 if 语句。这些有时是有用的,但是尽量限制它们,因为它们会扰乱可读性。我认为它们在列表理解中最有用,我们稍后会看到,但是单行/内联 if 语句在 Python 中是有效的。
需要记住的一个细节是,有两种方式来书写和和或,即仅仅是单词(就像我们刚刚做的那样)和分别使用符号 & 和 | 的简写符号。官方说法是,第一种方式是“逻辑与”和“逻辑或”,而后者是“按位与”和“按位或”。对于布尔值,它们在普通 Python 中的含义是相同的,但是在使用其他包时,它们的定义是不同的。例如,像 Numpy 和 Pandas 这样的包创建了由数组或表格数据组成的新数据结构。通常,您希望一次测试多个布尔值(例如熊猫数据帧的所有行)。在这些包中,和和或仍然被定义为测试单个布尔值,而 & 和 | 被定义为测试整个列表,从而用条件测试列表的每一项。如果这还不清楚,我们将在讨论列表时举例说明,所以现在不要担心。
最后一个可能有用的东西是 pass 语句。这只是一个不做任何事情的占位符,用于创建一个空的代码块。当您创建一个 if 语句时,如果条件为真,它至少需要一个语句。如果你还不确定它是什么,你不能让它为空,因为你会得到一个缩进错误。对于这种情况,您可以使用 pass 语句。
今天的练习:
- 在一个新的笔记本中,分配各种数字字符串,并测试这些字符串是否是数字。
- 测试字符串“3.1415826535897”的前八位是否至少与圆周率的实际值相同。(提示:要获得 pi,首先要有“import math”和“pi = math.pi”。您可能需要将其转换为字符串;-))
- 创建 if 语句异常:if 语句中的 if 语句,因此是嵌套的 if 语句。
如果你有任何问题,请随时通过 LinkedIn 联系我。
每天 10 分钟学习 Python # 7
每天 10 分钟 Python 速成班
Python 中最全面的数据类型:列表
这是一个系列10 分钟的简短 Python 文章,帮助您提高 Python 知识。我试着每天发一篇文章(没有承诺),从最基础的开始,到更复杂的习惯用法。如果您想了解关于 Python 特定主题的问题或请求,请随时通过 LinkedIn联系我。
今天我们来看看列表,就数据类型而言,它可以说是 Python 的主力。列表可以被看作是一种容器,可以存储各种信息。这可以是数字、字符串、函数,也可以是任何其他对象(还记得在 Python 中,一切都表示为一个对象吗?).Python 列表是非常通用的,你可以混合和匹配任何你喜欢的对象。如果你愿意,你可以在你的列表中加入几个数字,三个字符串,甚至其他的列表。
是的,列表可以嵌套来创建你自己的“列表异常”。
列表中的列表中的列表中的列表(图片来源:knowyourmeme.com)
列表是使用内置类 list()或较短的括号符号创建的。需要记住的一件重要事情是列表是有序的。这意味着您向列表中添加项目的顺序是存储的。顾名思义,当我们将()项添加到列表中时,这些项会被添加到列表的末尾。使用 pop()方法,我们可以从列表中移除/获取最后一项。Pop 还可以通过索引删除特定项目。这里有几个例子。
列表是 Python 中少数几种可变的数据类型之一。Float、int 甚至 Strings 都是不可变的,每个操作都会创建一个新的实例。但是列表可以更改。例如,您可以将项目添加到列表中,但对象实际上是变化的。我们可以通过使用内置 id()函数来证明这一点。id 函数返回对象在内存中的地址,并且是对象的唯一标识符。让我们研究一下 int 和 list 的这些标识符:
我们刚刚看到,我们可以弹出()列表中的最后一项。为了在不删除项目的情况下获取项目,我们可以使用括号符号的索引方法。Python 从零开始索引,因此,列表中的第一项是 my_list[0] ,第二项是 *my_list[1],等等。与字符串切片类似,索引必须存在,否则将出现“索引超出范围”错误。当您选择范围时,您将获得一个带有子列表的新列表实例。与字符串一样,我们也可以使用负值向后索引,并提供一个步骤。也许你对字符串没有意识到这一点,但是通过一个负的步骤,你可以反转一个字符串或列表:my_list[::-1]或 my_string[::-1]。列表的基本算法如预期的那样工作:用+将两个列表相加,而用将两个列表相乘。两者都返回一个新的列表实例。要创建一个列表的副本,仅仅写 my_new_list = my_old_list 是不够的。这只是复制引用,而不是列表本身。要创建一个真正的新列表,您需要使用 list 类:*my _ new _ list = list(my _ old _ list)*显式地创建一个列表。切片也返回一个新的 list 实例,所以你也可以通过简单的切片‘all’生成一个副本:my _ new _ list = my _ old _ list[:]。然而,最后一个切片技巧不适用于字符串。字符串是不可变的,因此不会改变。Python 足够聪明,可以检查具有所需特征的不可变对象是否已经存在于内存中,并将返回一个指向该对象的指针。因此,my_string[:]将返回对同一对象的引用。
列表有许多内置方法。例如,要检查一个列表的长度,即列表中有多少项,我们可以使用 len() 方法。这也非常类似于字符串,其中 len() 返回序列长度(字符数)。两者如此相似的原因是两种类型都是从 iterable 类构建的。其他内置方法例如有 *min()、max()、sum()、count()、find()、reversed()、remove()、*和 sort() 。如果您想使用这些方法,可以随意检查它们的文档字符串,或者检查带有制表符结束的方法的完整列表。
在上一篇文章中,我们学习了关键字中的*,它可以用来在字符串中查找子字符串。这也适用于在列表中查找项目。列表是引用的有序容器,将检查列表中是否有该项目的引用。如果是这样,它将返回 True,否则返回 False。测试列表是否为空的 Pythonic 方法不是检查列表的长度是否为 0。虽然这样做了,但是列表有一个属性,当它们为空时为 False,如果它们至少包含一项,则为 True。这使得空头支票非常方便。*
我们已经提到过元组解包。这是一种从列表或元组等可迭代对象中系统地获取值的技术。之所以这样称呼它,是因为它通常用于元组,但它也同样适用于列表。最显式的方法是在等号前有变量名,用逗号分隔,右边有 iterable:a,b = [1,2] 。Python 还有一个针对列表的 unpack 操作符,它是放在列表前面的操作符。解释器对链表的解包方式如下:【1,2,3】->1,2,3* 。如果您想将列表中的单个参数传递给函数,这很有用。反之亦然: a,b,c,d = [1,2,3,4,5] 。在此设置中,列表的前两个值将被解包到变量 a 和 b ,最后一个值将被解包到 d 。中间的所有值在 c 中解包。这种情况最常见于带有args 和**kwargs 的函数,这些函数可以接受任意数量的参数。在这里,* *操作符是映射(字典)的解包。这到底意味着什么是另一个时间。
今天的练习:
a. 在一个新的笔记本中,创建一个空列表,并追加几个值(数值、字符串和列表)。你还能在列表中追加一个值吗?提示:索引+追加()
b. 用以下列表:
my_list = ['This', 'is', 'a', 'list', 'of', 'words', '!']
加入列表以创建字符串。提示:string 有一个排除列表的 join()方法。
c. 有以下列表:
coffee_drinkers = [“Alfred”, “Dennis”, “Rob”, “Coen”, “Coen”, “Alfred”, “Jeroen”, “Hendri”, “Alfred”, “Coen”, “Rob”, “Dennis”, “Rob”, “Dennis”, “Rob”, “Coen”, “Rob”, “Alfred”, “Jeroen”, “Hendri”, “Alfred”, “Coen”, “Rob”, “Dennis”, “Coen”, “Dennis”, “Rob”, “Jeroen”, “Jeroen”, “Alfred”, “Jeroen”, “Hendri”, “Alfred”, “Coen”, “Rob”, “Dennis”]
a)供应了多少咖啡?谁喝了最多的咖啡?c)以相反的顺序对列表进行排序。
如有任何问题,欢迎通过 LinkedIn 联系我。
每天 10 分钟学习 Python # 8
每天 10 分钟 Python 速成班
Python 中的循环:暂时
这是一个系列10 分钟的简短 Python 文章,帮助您提高 Python 知识。我试着每天发一篇文章(没有承诺),从最基础的开始,到更复杂的习惯用法。如果您对 Python 的特定主题有任何问题或要求,请随时通过 LinkedIn 联系我。
今天我们将讨论循环。有两种不同类型的循环,即 for 循环和 while 循环。两者在几乎任何编程语言中都被大量使用,当然这是有原因的。代码中经常出现重复。例如,您有一个值列表,需要过滤低于某个阈值的值。解决这个问题的一种方法是使用 for 循环遍历所有值,并测试该值是否低于设置的阈值。当然,还有其他方法来解决这个问题。
for 循环和 while 循环都有不同的用例。和前面的例子一样,for 循环用于迭代某些东西。这可以是一个简单的计数器(一个值列表),一个字符串列表,实际上是放入 iterable 的任何对象。因此 for-loops 将遍历一个固定的列表。while 循环有不同的目的,因为它不遍历列表,但有一个在每次迭代前测试的条件。如果条件为真,它将执行代码块,当条件为假时,while 循环结束。while 循环用于继续迭代,直到例如用户按下按键或者最小化过程已经达到某个精度。
在许多其他语言中,创建 for 循环来拥有一个计数器是很常见的,然后用它来遍历一个数据结构。在 Python 中,你可以直接遍历一个可迭代对象,比如一个列表。让我们来看一些例子:
for 循环以关键字的开始,后跟一个变量名。这个变量名将被用来引用列表的当前项。因为 Python 是动态类型化的,所以您不必为分配类型而烦恼。这将由 Python 解释器动态设置。变量名后面是关键字中的*,后面是 iterable 和分号。与 if 语句类似,循环中的代码通过缩进来标识。缩进加上清晰的语义,使得这些循环可读性极强。*
Python 已经打包了一些方便的工具来操作列表。例如,要反转一个列表,可以使用内置的 reversed()方法。这些类型的方法接受一个 iterable(比如一个 list ),并通过应用的操作返回一个新的 iterable。其中一些方法比如 reversed()也内置在列表类本身中( my_list.reverse() )。不同之处在于这些方法改变了列表本身(因为列表是可变的),并且不返回新的列表。
有时候,迭代的时候需要有一个计数器或者索引。对于这种特殊情况,Python 提供了 enumerate,它用连续增加的数字“压缩”你的列表。Zip 可以将多个列表组合成一个单独的 iterable,这个 iterable 可以在一个循环中解包。当然,所有列表的长度必须相同,否则会出现错误。
range()方法用于创建一个数字列表。它接受一个或两个边界值和一个步长。Range()返回一个特殊类型的 iterable,它是一个 range 对象(它不是一个生成器)。在前面的例子中,我们为 for 循环提供了一个列表。这个列表完全定义在内存中的某个地方,等待 for 循环引用。如果我们要对大范围的数字做同样的事情,我们首先需要创建这个列表。这可能会占用大量内存,尤其是当我们迭代数百万个值时( range(1e12) )。对于这种情况,Python 有生成器,这是一种“懒惰执行”的形式。仅当需要该值时,即当 for 循环请求该值时,才会生成该值。虽然 range 与 generators 有一些微妙的区别,但其思想有些类似:只在需要时获取您需要的值,而无需先创建完整的数字列表。
与 if 语句类似,for 循环也可以嵌套。当然,您需要确保两个 for 循环的变量是唯一的。第二个 for 循环从下一个缩进级别开始,并且随着每个 for 循环的增加而增加。虽然 Python 可以接受添加大量嵌套的 for 循环,但一般来说,三个是最大值。如果你最终使用了更多,是时候考虑减少循环次数的策略了。循环很慢,如果你需要大量的循环,矢量化可能是你想要的。
for 循环和 while 循环都有 break 和 continue 关键字来对循环流进行额外的控制。发出中断时,当前循环停止,即如果处于 for 循环,循环停止。如果您在嵌套的 for 循环中,则 for 循环的当前级别将停止。使用 continue 可以进入下一次迭代,忽略 continue 关键字之后的任何语句。这些关键字为流程提供了额外的控制,但是针对特定的用例。
我从未使用过 for-else 或 while-else 结构,但它可能有一些用例。然而,这很容易理解。如果 for 循环或 while 循环成功,即没有中断或错误,则执行 else 之后定义的代码块。
While-loops 是 Python 中创建循环的另一种构造。while 循环测试条件,而不是循环遍历 iterable。while 循环以关键字开始,而后面是条件和分号。
在基于 Python 的 restFul API 服务器中常见的一个构造是一个永无止境的 while 循环。这些循环会一直运行下去(或者直到用户中止程序(或者出现错误)),因为所提供的条件是常量 True。真,永远不会变成假,因为它是真中最真的,而只有通过 break 关键字或神的干预才能停止。while 循环中的条件类似于 if 语句中的条件。因此,您可以使用逻辑运算符( and、not 或)来组合多个。此外,如果您想稍后对循环进行编码,您可以使用 pass 占位符。
今天的练习:
a. 在一个新的笔记本中,创建一个 for-loop,对 1 到 100(包括 100)之间的所有值进行循环,并对所有值求和。(应该是 5050)
b. 循环下面的列表:
my_list = ['Alfred', 'Dennis', 'Rob', 'Coen', 'Coen', 'Alfred', 'Jeroen', 'Hendri', 'Alfred', 'Coen', 'Rob', 'Dennis', 'Rob', 'Dennis', 'Rob', 'Coen', 'Rob', 'Alfred', 'Jeroen', 'Hendri', 'Alfred', 'stop', 'Coen', 'Rob', 'Dennis', 'Dennis', 'Rob', 'Jeroen', 'Jeroen', 'Alfred', 'Jeroen', 'Hendri', 'Alfred', 'Coen', 'Rob', 'Dennis']
a)统计所有名字
b)统计所有名字,但使用继续关键字
跳过“Dennis ”; c)统计所有名字,但使用中断关键字在“stop”处停止
c. 使用 while 循环创建所有值的和,即 1 + 2 + 3 + … + n,其中 n 每一步增加 1。继续,直到总和大于 10000,并返回当前步骤 n。在 while 循环结束后,使用 else 构造打印该值。(应该是 142)
如果您有任何问题,欢迎通过 LinkedIn 联系我。
每天 10 分钟学习 Python # 9
每天 10 分钟 Python 速成班
定义函数,停止重复你自己
这是一个系列10 分钟的简短 Python 文章,帮助您提高 Python 知识。我试着每天发一篇文章(没有承诺),从最基础的开始,到更复杂的习惯用法。如果您对 Python 的特定主题有任何问题或要求,请随时通过 LinkedIn 联系我。
你们中的许多人可能已经听说过 DRY 缩写:不要重复自己。有时你的同事开玩笑说你提供了一个湿的解决方案:我们喜欢打字。玩笑归玩笑,当你不得不重复某个步骤时,尽量减少复制和粘贴代码是一个好习惯。在一个富有成效的早晨之后,我们可能都有过这样的时刻,回顾我们的代码,一遍又一遍地看到相同的代码块。这通常是你意识到创建一个函数是个好主意的时候。
使用imgflip.com生成的迷因
函数是可重用的、专门为完成某项任务而设计的代码块。你已经看到了相当多的函数,即内置的 print() 和 len() 。要调用一个函数,你必须键入它的名字,并在后面加上括号。不带括号的名字是指向函数的引用(Python 中的一切都是对象,记得吗?).可以不带参数调用函数,这意味着括号之间没有任何内容。然而,许多函数确实接受一个或多个参数,这些参数可以作为变量或值提供,用逗号分隔。使用 def 关键字,后跟一个函数名、一组括号和一个分号来定义函数。所有代码都是缩进的,类似于 for-loop 或 if-else 语句。按照惯例,函数的命名类似于变量名:所有小写字母用下划线分隔。尽量简短但简洁。为您创建的函数创建 DocString 是一个很好的做法。当然,当非常清楚函数的作用时,这不是必需的,所以在创建 DocString 或注释时使用常识来防止过度注释。
一个函数不仅仅要做一些事情,它还可以返回一个结果。在这个例子中,我们创建了一个给定值加 1 的函数。使用 return 关键字返回结果。要使用返回值,可以将结果赋给一个新变量,或者直接在函数中使用它。您可以返回任何类型的对象,例如 int、float、string、list、functions 或 multiple using tuples。Python 函数总是返回一个对象,但是,如果您没有“捕获”该对象,结果会被解释器垃圾收集并从内存中消失。如果您确实捕捉到了结果,并且该函数没有返回关键字,则返回 Nonetype 的一个对象。这是一个“无”的物体,也就是说,它什么也不代表。我们可以通过使用内置的 type() 函数来测试这一点,该函数返回对象的类。
函数采用的参数是动态类型的,不受 Python 本身的限制。当然,如果您提供了一个字符串,而函数试图对该字符串求平方,它将引发一个错误。类型检查是用户的责任,对于在多个用户之间共享的代码来说是一种很好的做法,尤其是在代码库很大的情况下。对于较小的项目和只为您编写的代码,您可以选择不进行类型检查,这样有可能会出现错误。开发人员喜欢更进一步,将 Python 的默认动态类型行为改为静态类型。从 Python 3.6 开始,您可以在函数定义中提供每个参数的类型和返回值。虽然我理解调试的好处,但我从来没有为此烦恼过。我想对于更大的程序来说,这可能更重要,但是对于数据科学来说,我认为数据完整性比静态类型更重要。另一方面,申请并不需要太多的工作。这里有一个关于 Python 中静态类型化的很好的指南。有时动态类型被用作一个好处。一个函数可以测试它的类型,并为不同的对象做不同的事情。像 Numpy 或 Pandas 这样的许多包使用它来从不同的数据类型创建数组或数据帧。如果我们将它静态地输入,我们将不得不为每个数据类型创建不同的函数。动态打字可能是一种福气,但对其他人来说也可能是一种负担。我从未发现它对我的任何用例有问题。
向函数提供参数的另一种方式是使用命名参数。这些也称为键值参数,以定义的参数名开始,后面跟一个等号和分配的值或变量。对于短函数,即只有一个或两个参数的函数,人们并不真的很麻烦,但当它是一个有许多参数的复杂函数时,这对可读性有很大的帮助。由于参数是显式分配的,因此它们的提供顺序并不重要。如果出于某种原因,您将命名参数与标准的顺序参数相匹配,那么顺序确实很重要,因此在这样做时应该小心。以类似的方式,也可以在函数的定义中提供默认值。这使该参数成为可选参数,因为它已经有一个赋值。所有没有默认值的参数都被自动标记为必需参数,如果不提供这些参数,将会引发错误。另一种常见的做法是将 None 类型指定为默认值。这使得参数是可选的,在函数本身中,您可以测试参数是 None 类型还是其他类型,并相应地采取行动。当然,这样的习惯用法在它们的用例中是相当具体的。
有时你可能会看到一个方法,让函数接受任意数量的参数。这个方法使用 unpack 操作符并收集另一个变量中的所有参数。它区分了命名参数和未命名参数。对于未命名的参数,它使用单星号*操作符,并将它们打包到一个元组中。命名参数是键值参数,是所谓的映射,使用**操作符收集并放入字典中。所以当你看到一个函数是用 *args 和 kwargs 定义的,意思是参数和关键字参数,你就知道这个函数可以接受任意(或灵活)数量的参数。print()函数就是这样一个函数的例子。
今天的练习:
- 在一个新的笔记本中,创建你自己的正方形函数。该值接受一个数字,对其求平方,然后返回结果。
- 对于下面的列表:
my_list = ['Alfred', 'Dennis', 'Rob', 'Coen', 'Coen', 'Alfred', 'Jeroen', 'Hendri', 'Alfred', 'Coen', 'Rob', 'Dennis', 'Rob', 'Dennis', 'Rob', 'Coen', 'Rob', 'Alfred', 'Jeroen', 'Hendri', 'Alfred', 'stop', 'Coen', 'Rob', 'Dennis', 'Dennis', 'Rob', 'Jeroen', 'Jeroen', 'Alfred', 'Jeroen', 'Hendri', 'Alfred', 'Coen', 'Rob', 'Dennis']
创建一个函数,从列表中删除某个名字。该函数接受一个列表和一个名称,遍历列表,如果名称不是给定的名称,则将名称添加到一个新列表中,然后返回列表。 - 创建一个函数,返回数字列表中的所有偶数。该函数只接受最大值,默认设置为 100。在函数中,它在一个范围内循环,只将偶数放入一个新的列表中。它返回偶数的列表。你能用一个可选参数 odd 来扩展这个函数吗?提示:当取 2 的模(值% 2)时,偶数返回 0。
如有任何问题,欢迎通过 LinkedIn 联系我。
实践中的学习率计划:以 Keras 和 TensorFlow 2.0 为例
添加和定制学习率计划的教程
训练神经网络的一个令人痛苦的事情是我们必须处理的超参数的绝对数量。例如
- 学习率
- Adam 优化算法的动量或超参数
- 层数
- 隐藏单元的数量
- 小批量
- 激活功能
- 等等
其中,最重要的参数是学习率。如果你的学习率设置得很低,训练将会进展得很慢,因为你对你的网络中的权重做了非常微小的更新。然而,如果你的学习率设置得太高,它会在你的损失函数中引起不希望的发散行为。
作者使用https://excalidraw.com/创建的图像
在训练神经网络时,随着训练的进行降低学习速率通常是有用的。这可以通过使用学习速率表或自适应学习速率来完成。在本文中,我们将重点关注在我们的机器学习模型中添加和定制学习率计划,并查看我们如何在 Keras 和 TensorFlow 2.0 中实践它们的示例
学习费率表
学习率时间表根据预定义的时间表,通过降低学习率来调整训练期间的学习率。流行的学习率计划包括
- 恒定学习速率
- 基于时间的衰减
- 阶跃衰减
- 指数衰减
出于演示目的,我们将构建一个图像分类器来处理时尚 MNIST,这是一个具有 10 个类的 70,000 个 28×28 像素的灰度图像的数据集。
请查看我的 Github repo 获取源代码。
使用 Keras 加载数据集
Keras 提供了一些实用函数来获取和加载公共数据集,包括时尚 MNIST。让我们装载时尚 MNIST
fashion_mnist = keras.datasets.fashion_mnist
**(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()**
数据集已经分为训练集和测试集。以下是定型集的形状和数据类型:
>>> X_train_full.shape
**(60000, 28, 28)**
>>> X_train_full.dtype
**dtype('uint8')**
我们将使用梯度下降来训练神经网络,我们必须将输入特征缩小到 0-1 范围。为了在本地机器上进行更快的训练,我们只使用前 10,000 幅图像。
X_train, y_train = **X_train_full[:10000]/255.0**, **y_train_full[:10000]**
创建模型
现在让我们建立神经网络!用 Keras 和 TensorFlow 2.0 创建机器学习模型有 3 种方法。由于我们正在构建一个简单的全连接神经网络,为了简单起见,让我们使用最简单的方法:带有Sequential()
的顺序模型。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flattendef create_model():
model = Sequential([
Flatten(**input_shape=(28, 28)**),
Dense(300, activation='relu'),
Dense(100, activation='relu'),
Dense(**10, activation='softmax'**),
])
return model
我们的型号有以下规格:
- 第一层(也称为输入层)有
input_shape
来设置匹配训练数据的输入大小(28, 28)
。输入层是一个Flatten
层,它的作用只是将每个输入图像转换成一个 1D 数组。 - 然后是 2 个
Dense
层,一个 300 单位,另一个 100 单位。两者都使用relu
激活功能。 - 输出
Dense
层有10
单元和softmax
激活功能。
model = create_model()
model.summary()
1.恒定学习速率
恒定的学习速率是所有 Keras 优化器的默认时间表。例如,在 SGD 优化器中,学习率默认为0.01
。
要使用定制的学习率,只需实例化一个 SGD 优化器并传递参数learning_rate=0.01
。
sgd = tf.keras.optimizers.SGD(**learning_rate=0.01**)model.compile(
**optimizer=sgd,**
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
并使模型符合训练数据:
history_constant = model.fit(
X_train,
y_train,
epochs=100,
validation_split=0.2,
batch_size=64
)
让我们画出模型的准确性,这可以作为我们实验其他学习率计划的基线。
恒定学习率——准确度图
2.基于时间的衰减
基于时间的衰减是最流行的学习速率计划之一。形式上,基于时间的衰减定义为:
learning_rate = lr * 1 / (1 + decay * epoch)
其中lr
是以前的学习率,decay
是超参数,epoch
是迭代次数。当decay
为零时,这对改变学习率没有影响。当decay
被指定时,它将按照给定的固定量减少前一时期的学习率。decay
的值通常实现为
decay = initial_learning_rate / num_of_epoches
在 Keras 中,实现基于时间的衰减的一种方式是通过定义基于时间的衰减函数**lr_time_based_decay()**
并将其传递给LearningRateScheduler
回调。
initial_learning_rate = 0.01
epochs = 100
decay = initial_learning_rate / epochs**def lr_time_based_decay(epoch, lr):
return lr * 1 / (1 + decay * epoch)**history_time_based_decay = model.fit(
X_train,
y_train,
epochs=100,
validation_split=0.2,
batch_size=64,
**callbacks=[LearningRateScheduler(lr_time_based_decay, verbose=1)],**
)
下面是准确率和学习率的曲线图。
基于时间的衰减——精度图
基于时间的衰减—学习率图
3.阶跃衰减
另一种流行的学习率计划是在训练期间的特定时间系统地降低学习率。在形式上,它被定义为:
learning_rate = initial_lr * drop_rate^floor(epoch / epochs_drop)
其中initial_lr
是初始学习率,例如 0.01,drop_rate
是每次改变学习率时修改的量,epoch
是当前的历元数,epochs_drop
是改变学习率的频率,例如 10 个历元。类似地,我们可以通过定义一个阶跃衰减函数**lr_step_decay()**
并将其传递给LearningRateScheduler
回调函数来实现这一点。
initial_learning_rate = 0.01**def lr_step_decay(epoch, lr):
drop_rate = 0.5
epochs_drop = 10.0
return initial_learning_rate * math.pow(drop_rate, math.floor(epoch/epochs_drop))**# Fit the model to the training data
history_step_decay = model.fit(
X_train,
y_train,
epochs=100,
validation_split=0.2,
batch_size=64,
**callbacks=[LearningRateScheduler(lr_step_decay, verbose=1)],**
)
下面是准确率和学习率的曲线图。
基于步进的衰减——精度图
基于步长的衰减—学习速率
4.指数衰减
另一种流行的学习率计划是以指数速度降低学习率。在形式上,它被定义为:
learning_rate = initial_lr * e^(−k * epoch)
其中initial_lr
是初始学习率,例如 0.01,k
是超参数,epoch
是当前纪元编号。类似地,我们可以通过定义一个指数衰减函数**lr_exp_decay()**
并将其传递给LearningRateScheduler
回调函数来实现这一点。
initial_learning_rate = 0.01**def lr_exp_decay(epoch, lr):
k = 0.1
return initial_learning_rate * math.exp(-k*epoch)**# Fit the model to the training data
history_exp_decay = model.fit(
X_train,
y_train,
epochs=100,
validation_split=0.2,
batch_size=64,
**callbacks=[LearningRateScheduler(lr_exp_decay, verbose=1)],**
)
指数衰减—精度图
指数衰减——学习率图
比较模型准确性
最后,让我们使用不同的学习率时间表来比较模型的准确性。
看起来常数和基于时间的学习率比阶跃衰减和指数衰减对于这个特定的教程有更好的性能。请记住,本教程仅使用前 10,000 幅图像,其中的initial_learning_rate=0.01
、validation_split=0.2
和batch_size=64
具有任意值。
在现实世界的应用程序中,为了调整学习速率,需要考虑更多的因素。请查看“深度架构基于梯度的培训实用建议”一文,了解一些最佳实践。
好了
感谢阅读。这篇文章涵盖了最流行的学习进度计划。下次我们就来看看自适应学习率。
源代码请查看我的 Github 上的笔记本。
如果你对机器学习的实用方面感兴趣,请继续关注。
您可能对我的其他 TensorFlow 文章感兴趣:
- 谷歌机器学习实践的 7 个步骤:结构化数据的 TensorFlow 示例
- 用 Keras 和 TensorFlow 2.0 创建机器学习模型的 3 种方法
- 批量规范化实践:以 Keras 和 TensorFlow 2.0 为例
- 实践中的提前停止:以 Keras 和 TensorFlow 为例
通过将 Python 转换成 Rust 来学习 Rust
Rust 基础入门教程
[更新于 2021 年 2 月 18 日。代码更改为要点并添加了链接]
**Table of Contents**[**Introduction**](#3cb0)🦀 [Leetcode Unique Paths](#d2b3)
🦀 [Python Code](#2aa3)
🦀 [First Step in Rust](#f513)
🦀 [Examples of Primitive Data Types:](#5358)
🦀 [Functions](#3775)
🦀 [Statements and Expressions](#edb9)
🦀 [Variables](#0f1c)
🦀 [Macros](#dc94)
🦀 [if-else Statement (Step 2)](#5346)
🦀 [Calling a Function](#f27b)
🦀 [Range](#5d38)
🦀 [Arrays, Tuples, and Vectors](#52ef)
🦀 [as Keyword](#6263)
🦀 [Final Code](#4543)
🦀 [Struct and Impl](#27aa)
🦀 [Trait](#2c53)
🦀 [Runtime & Memory Usage](#7e45)[**Conclusion**](#d7d1)
介绍
Rust 是一种静态和强类型的系统编程语言。Rust 是为渴望语言速度和稳定性的人准备的。
我认为自己是一个新手。写完这篇文章后,我开始学习 Rust,并且我每天都在学习新的东西。我现在可以将简单的 Python 代码转换成 Rust,并且能够解释我正在编写的代码。
在本文中,我们将使用一个 Leetcode 问题的 Python 解决方案,并将其转换为 Rust 代码。你不仅会发现两种语言之间的相似之处,还会学到 Rust 编程。
您可以运行代码,也可以在本文的示例中对其进行调整。
Rust 初学者的完整资源
towardsdatascience.com](/you-want-to-learn-rust-but-you-dont-know-where-to-start-fc826402d5ba)
Leetcode 唯一路径
LeetCode 是一个非常受欢迎的网站,在那里你可以提高你的编码技能。其中一个问题叫做“唯一路径”,机器人位于一个 m x n 网格的左上角。您只能将向下或向右移动,并且您需要找出两点之间有多少条唯一的路径。
图片来自 LeetCode
例如,在下图中,从 A 点到 B 点有 3 条唯一路径,从 A 点到 C 点有 5 条唯一路径。下图帮助您找到从 A 到每个区域的唯一路径的数量。由于您只需将向下移动或向右移动,您可以将上面和左边的数字相加,找到到达该点的唯一路径的数量。
作者图片
Python 代码
用 Python 解决这个问题肯定有很多方法,但是我们打算用下面的解决方案。
使用 Python 的解决方案。在线尝试这段 Python 代码。
LeetCode 上 Python 代码的结果。
在这段 Python 代码中:
- 我们创建了一个名为 Solution 的类。我们定义了一个叫做“唯一路径”的方法。(是起始码。)
- 它将 self、integer
m
和 integern
作为参数,并返回一个整数。它使用类型注释。 - 如果
m
或n
等于 1,那么它返回 1。 - 如果
n
小于m
,则它会切换位置并运行相同的方法。 dp = [1]*n
将创建一个n
编号为 1 的列表。例如[1]*3
将创建[1,1,1]
。- 我们使用双 for 循环和 range 将前一个数字添加到下一个项目中。
[1,2,3]
、[1,3,6]
等。您可以通过将返回值更改为return dp
来检查这一点。 - 我们使用
[-1]
返回列表中的最后一个数字。
在本文中,我们将逐步将 Python 代码转换为 Rust。
上面的结果显示运行时间为 36 ms,内存使用量为 13.6 MB。我们稍后会将这个结果与 Rust 结果进行比较。
(当我再次提交给 LeetCode 时,结果发生了变化,上面的那个是最好的。你可能会有不同的结果。)
生锈的第一步
我们将从一个基本锈码开始。
生锈的第一步。试试这个锈郎在线。
Rust 总是在你运行程序的时候先运行**main**
功能。我们创建一个名为unique_paths
的函数,带有两个参数m
和n
,它们是**i32**
类型。
在函数中,我们必须声明每个参数的类型。Rust 程序中的每个变量、项目和值都有一个类型。
值的类型定义了保存它的内存的解释。**i32**
是原语数据类型之一,是一种 32 位固定大小的有符号整数类型。**i32**
可以容纳-(2)和 2 -1 之间的数字,前者为-2147483648,后者为 2147483647。
原始数据类型的示例:
更多的原始数据类型在这个链接中。
顺便说一下,Rust 的默认整数类型是**i32**
,所以如果你没有在一个 闭包 中标注参数的类型,并且如果它们是整数,Rust 将使用**i32**
。Rust 的编译器能够推断出参数和大多数变量的类型。
功能
功能 使用**fn**
关键字启动。Rust code 使用蛇形外壳作为功能的传统样式。函数可以返回值,我们使用箭头**->**
为返回值声明它们的类型。unique_paths 返回数据类型**i32**
。Rust 使用{}
作为函数体。
网上试试这个锈郎代码。返回字符串的函数示例。
陈述和表达
我们返回总和,m + n
。函数体由一系列语句组成,并以表达式结尾。
语句是执行一些动作并且不返回值的指令。表达式计算出结果值。— 函数体包含语句和表达式
表达式m + n
不包括结束分号,因为我们想返回它。如果你在一个表达式的末尾加一个分号,你就把它变成了一个语句,它不会返回值。
变量
在**main()**
函数中,我们用一个关键字来表示一个变量。默认情况下,局部变量是不可变的,但是您可以使用**mut**
使它们可变。对于我们的例子,我们不需要使它可变。
试试锈变量示例在线。
宏指令
**println!()**
是标准宏之一。它解析格式字符串并将其转换为打印格式。
pritln!
按顺序填充占位符。可以用索引和name="vale"
。
试试 println!宏在线。
if-else 语句(步骤 2)
下一步,我们添加一个**if-else**
语句。**expr == expr**
(其中expr
表示表达式)是相等比较。**expr || expr**
是逻辑还是。
如果m
或n
中的任何一个等于 1,那么我们返回 1。请注意 1 后面没有分号,因为我们要返回 1。
Rust 对于**if**
语句中的条件不使用括号。
在线使用 if 语句尝试 Rust 中的步骤 2。
**if-else if-else**
有如下形式。
在线尝试 if-else if-else 示例。
调用函数
在线尝试 Rust 中的步骤 3。
这可能不是传递 LeetCode 所必需的,但是这向您展示了如何从内部调用自己的函数。
如果**n**
小于**m**
,我们交换变量位置,调用自己的函数。
范围
在 Python 中,我们使用range
作为:
for i in range(1,m):
for j in range(1,n):
在 Rust 中,我们使用..
或..=
。
尝试范围…在锈线上。
上面的代码将输出 1 到 9。
尝试范围…= in Rust online。
上面的代码将输出 1 到 10。
数组、元组和向量
在 Python 中我们使用了dp = [1]*n
。这将创建[1]的n
个数字。例如当 n=3 时,[1, 1, 1]
。
让我们看看 Rust 中的数组、元组或向量中哪一个可以用于此目的。
阵列
数组必须有同类型定长。默认情况下,数组是不可变的,我们不能用**mut**
来改变它的元素。
在线试用锈阵。
输出:
one: 1, b: [4, 5, 6], c: [1, 1, 1, 1, 1], d: [1, 1, 1, 1, 1]
我们也不能动态设置数组长度。因为数组的长度是在编译时定义的。变量在编译时是未知的,因为它可以改变。编译器不知道在堆栈上分配多少空间来为数组提供存储。
如果你试图在没有[{:?}](https://doc.rust-lang.org/std/fmt/index.html)
的情况下打印,你将会失败。我们不能用默认格式化程序格式化数组。所有 Rust 数据类型都实现了Debug
特征,您可以使用{:?}
来格式化它并打印一个数组。
所以我们不能在这里使用数组。
元组
元组可以有不同的类型,并且有固定的长度。
let tup: (i32, f64, u8) = (100, 2.32, 4);
为了从元组中获取单个值,我们可以使用模式匹配来析构元组值。
在线试试锈元组。
输出:
x = 500, y = 6.2, z = 4
1
同样,我们不能使用元组,因为它们必须有固定的长度**。**
矢量
Rust 向量在内存中存储多个相邻的值。向量存储相同类型的值。
具有堆分配内容的连续可增长数组类型。——doc.rust-lang.org
vec!
宏创建一个新的向量,保存您给它的值。
let v = vec![1, 1, 1];
// this is the same as above
let u = vec![1; 3];
一个向量就像一个可调整大小的数组,但是所有的元素必须是同一类型。
所以我们需要使用一个向量。
dpm = vec![1; m as usize];
dpn = vec![1; n as usize];
作为关键字
第二个数字,长度,在vec![1; 3]
中需要是[**usize**](https://doc.rust-lang.org/std/primitive.usize.html)
类型。我们将**m**
和**n**
标注为**i32**
,但是我们可以使用**as**
关键字来转换类型。我们可以只使用as
表达式在原始类型之间转换。
最常用于将基元类型转换为其他基元类型。——鲁斯特郎博士
**vectors**
有一个方法[**len**](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.len)
返回向量中元素的数量。
最终代码
我们使用上述所有代码来获得唯一路径的数量。
在线尝试Rust的最后一步。
你可能注意到了#[allow(unused_variables)]
。没有它也能工作,但是编译器会给你一个警告:
warning: unused variable: `i`
--> main.rs" data-line="11" data-column="13">main.rs:11:13
|
11 | for i in 1..dpm.len() {
| ^ help: if this is intentional, prefix it with an underscore: `_i`
|
= note: `#[warn(unused_variables)]` on by default
warning: 1 warning emitted
为了消除这个警告,我们添加了#[allow(unused_variables)]
。
稍后我们将看到 LeetCode 的最终代码。在此之前,我们对这段代码进行了更深入的研究。
结构和实现
我们将对上述解决方案应用struct
、impl
和trait
。请注意,这在 LeetCode 上不起作用。
结构用于将相关属性封装成一个统一的数据类型。结构不同于其他语言中的类。例如,Rust 的 struct 不支持继承。
元组很方便,但是使用像t.0
这样的索引并跟踪每个部分的含义并不简单。
Rust 结构包含命名字段。我们使用关键字struct
并在花括号内设置字段类型。结构将数据定义为键值对。
struct Name_of_struct {
field1:data_type,
field2:data_type,
...
}
在线试试的防锈结构。
&字符串和字符串
你可能想知道为什么我们需要to_string()
。Rust 主要有两种类型的弦:[&str](https://doc.rust-lang.org/std/primitive.str.html)
和[String](https://doc.rust-lang.org/book/ch08-02-strings.html#what-is-a-string)
。&str
叫做‘串片’。字符串切片具有固定的大小,不能变异。一个String
被存储为一个矢量。String
是堆分配的,可增长的,并且不是空终止的。(更多信息请点击链接。)
"John"
是一个&str
,由于我们在struct
中将first_name
的类型定义为字符串,我们需要使用[to_string](https://doc.rust-lang.org/std/string/trait.ToString.html)
将其转换为字符串。
我们将关联函数放入impl
块中。
在线试用 Rust impl 。
方法参数 self、& self 和& mut self
我们在impl Person
中增加了一个方法 full_name
。该函数将&self
作为第一个参数。它可以采用self
、&self
或&mut self
中的一种。
- 当我们想要读取结构中的数据,而不是写入数据时,我们使用
&self
。 - 当我们希望方法获得所有权时,我们使用
self
。 - 当我们想让方法写入时,我们使用
&mut self
。
我们在p.full_name()
中的实例方法中使用点运算符来访问该字段。
让我们将struct
和impl
用于我们的代码:
使用 struct 和 impl 在线尝试。
特点
特性 类似于 OOP 语言中的和接口**。它们用于定义一个类型必须提供的功能。**
尝试使用 trait 在线编写代码。
我们用函数unique_paths
添加了一个名为 UiquePaths 的特征。我们需要使用impl trait-name for struct-name
更新impl
。
我们使用::
符号在main
函数中实例化它。
现在我们实现了struct
、impl
和trait
。
运行时和内存使用
以下解决方案针对 LeetCode 环境进行了调整。
impl Solution {
pub fn unique_paths(m: i32, n: i32) -> i32 {
if m == 1 || n == 1 {
1
} else {
let mut dpm = vec![1; m as usize];
let mut dpn = vec![1; n as usize];
for i in 1..dpm.len() {
for j in 1..dpn.len() {
dpn[j] += dpn[j-1];
}
}
*dpn.last().unwrap()
}
}
}
铁锈的 LeetCode 结果
请注意运行时间 0 毫秒。Rust 没有运行时间。Rust 适用于零** - 成本抽象。你不必为某些强大的抽象或安全特性付出额外的运行时开销,而在其他语言中你必须为此付出运行时成本。**
…迭代器虽然是一种高级抽象,但会被编译成大致相同的代码,就好像您自己编写了低级代码一样。迭代器是 Rust 的零成本抽象之一,我们的意思是使用抽象不会带来额外的运行时开销。— Rust 编程语言
Rust 的内存使用量为 2.1 MB,而 Python 的内存使用量为 13.8 MB。
Rust 有独特的管理堆内存的方式,它使用一个叫做所有权的概念来管理它。我建议您深入阅读 Rust 文档以了解更多信息。
结论
我们接触了原始数据类型、类型注释、函数、语句、表达式、变量、宏、if 语句、范围、数组、元组、向量、struct
、impl
和trait
的表面。你可以在官方书籍中找到更多信息。
正如我之前所说,我仍然在学习生锈。我感谢专家对如何改进解决方案的反馈。
我希望你学到了一些东西,并为下一步做好了准备。有许多关键概念我们无法在本文中涵盖。请继续关注下一篇文章。
通过 成为 的会员,可以完全访问媒体上的每一个故事。
https://blog.codewithshin.com/subscribe
理解符号和幅度、一的补码和二的补码
towardsdatascience.com](/unsinged-signed-integers-and-casting-in-rust-9a847bfc398f) [## 学习生锈:常见概念
查看更多关于 Rust String 的信息。
medium.com](https://medium.com/series/101-rust-tips-8f361510683c) [## 学习生锈:所有关于字符串
字符串和&str
medium.com](https://medium.com/series/learning-rust-all-about-strings-c7666812d893)**
有风格地学习 TensorFlow 2
通过实现快速神经风格转换来熟悉 TensorFlow 2
个人照片的雾滚过塔马尔派斯山风格化的尖叫由爱德华蒙克
背景
在过去的几年里,我对计算机视觉产生了浓厚的兴趣,并花了相当多的时间参加课程、阅读论文和做与该领域相关的兼职项目。这和我对摄影的热情有很大的重合。引起我注意的一个计算机视觉项目是神经风格转移,首先由莱昂·加蒂斯等人提出,后来由贾斯廷·约翰逊等人提出了特别快速的风格转移。最近在处理代码时,我发现它的大部分都过时了,缺乏支持,因此很难处理。在对其他机器学习框架有所熟悉的同时,我以此为契机更新了代码,对 TensorFlow 2 有了更深入的了解。由于快速风格转换包括生成网络和更复杂的损失函数,它为学习张量流的一些细微差别提供了一个很好的项目,而不是基本的分类模型。
本文的目标是强调使用 TensorFlow 2 的一些核心功能和主要经验,以及它们如何应用于快速风格转换。我将参考与神经类型转移相关的核心概念,但会浏览其他概念,因此熟悉一些会有所帮助。如果你对这个问题完全陌生,我在下面提供了一些有用的资源。
简要概述
样式转移的目标是获取一个内容图像,我们称之为 C ,和一个带有样式的图像, S ,创建一个转换后的图像, Y ,将 S 的样式应用于 C 。换句话说,我们想要混合 C 的内容和 S 的样式,如下图所示。
表示将一个图像的样式应用到另一个图像的内容,以创建一个经过变换的、风格化的图像。
在 Gatys 等人的原始风格转移工作中,具有随机噪声的基础图像 X 被传递到训练过的 VGG 网络中,并且来自指定层的输出被用于提取哪些特征被激活。从在 X 和 C 之间的指定内容层输出的 L2 损失可以帮助学习内容。在 X 和 S 之间来自指定风格滤波器的输出的格拉姆矩阵的 L2 损失可以帮助学习风格。这个图层提取可以在下图右侧的损失网络中看到。当 X 以随机噪声和多次迭代开始一幅图像时,它被训练并转换为 Y 。TensorFlow 团队在这里有一个关于这个原始实现的教程。
Johnson 等人提出的快速风格转换网络架构,注意:我的实现使用 VGG19 vs VGG16,来源:实时风格转换和超分辨率的感知损失 (2016)
在快速风格转换的情况下,生成神经网络被训练来转换任何图像,而不是单个图像被训练和转换。在上面由 Johnson 等人提出的架构中,内容图像被传递到变换网络中,然后变换的输出图像被传递到 VGG 网络中。从那里,使用提取的样式和内容层,变换的图像经历与原始样式转移模型类似的损失。为了训练生成网络,将在多次迭代中使用许多不同的内容图像。训练的时间要长很多,但是推理时间最多可以快 1000 倍!
在下面的例子中,相同的内容图像在不同的训练点通过网络传递。随着训练迭代次数的增加, i ,变换后的图像, X ,向我们期望的内容和风格的混合方向收敛。
创成式模型如何在多次迭代中发生变化的示例。随着时间的推移,通过优化损失函数 L(Cᵢ、s、Xᵢ).,模型训练和转换后的输出 Xᵢ将收敛到我们期望的风格和内容的混合 Yᵢ
模型
为了构建模型,我重点使用了 TensorFlow 的内置特性。在 TensorFlow 核心、Keras 和 TensorFlow 附加组件之间,有许多工具可以利用。
自定义图层
TensorFlow 2 使得将这些层组合成自定义层变得非常容易。在下面的例子中,你将看到我如何建立一个 2D 卷积层和模型的残差块。这些层将用于构建生成性转换网络。
当从 Layer 类继承时,属性和方法将被递归调用。例如,当访问自定义层中的可训练变量时,将返回组成该自定义层的所有层的所有可训练变量。这也适用于使用自定义层从模型中调用可训练变量,使得变量访问非常直观。
变压器网络
Keras 函数模型用于构建生成神经网络。Keras 中的功能模型比顺序模型更加通用,因为后者主要用于堆叠层。使用功能模型,可以构建更复杂的神经架构,如共享层模型和多输出模型。与单独堆叠的层相比,这些模型类似于图上的节点。这在 ResNet、InceptionNet 和 MobileNets 等其他体系结构中有所体现。由于我们有一些剩余的模块,功能模型使用起来会更友好一些。
由于我们组成了我们的自定义层,它仍然是非常直观的遵循。首先,实例化层,然后通过层的可调用性传递 Keras 输入,以期望的方式建立模型下面的图。最后,所有层的输出和原始输入用于创建一个tf.keras.Model
实例并构建图表。我创建了一个泛型类作为模型的包装器。*如上所述,对model.trainable_variables
的调用将从我们的自定义层返回所有可训练变量。
*请注意,您也可以创建继承模型,并仍然使用功能模型,这有一些好处,但我发现它更脊和难以工作。
VGG19 型号
Keras 图书馆包含一些公共网络,包括 VGG 网络。可以使用某些预先训练的权重和修剪最后完全连接的层的选项来创建它们。如果你想对一个给定的问题使用迁移学习,并且需要使用不同的分类层,这是非常有用的。我的实现使用 VGG19 网络,而不是 VGG16,尽管可以使用各种图像识别网络。
正如在概述中提到的,我们将通过 VGG 网络传递我们转换的图像, Xᵢ ,并提取一些输出层。我们将希望有多个层作为输出,而不是一个分类器作为输出。还记得功能模型是如何适用于多输出的吗?可以使用 VGG 网络创建新的功能模型。VGG 输入被传递到新模型中,并且期望层的输出作为模型输出被传递。当新模型被调用时,张量将通过 VGG 模型传递,这些层的输出将被返回。
下面是输出第一个卷积模块第一层(conv1_1)和第三个卷积模块第二层(conv3_2)的示例代码片段,以及几个通道的样本输出。这些输出表示当预训练滤波器在图像上卷积时激活的图像部分。conv1_1 的浅层输出激活边缘等简单特征来分解图像,而深层输出激活更复杂的信息(在这种情况下,它看起来像阴影)。
从 VGG 网络提取输出的例子。注意:在实际实现中,提取了更多指定的过滤器。
a)通过 VGG19 的原始 RGB 图像,conv1 _ 1 层的第 17 个滤波器(索引为 1)的输出,conv3 _ 2 层的第 6 个滤波器(索引为 1)的输出;注意图像 c 由于汇集而变小
层间的 L2 损失然后用于比较 Xᵢ 和 Cᵢ 的内容输出。关于内容,如果汽车图像的“车轮”特征在我们的原始图像上激活,并且也在转换的图像上激活,则内容仍然是强匹配的,并且损失将会更低。同样,gram 矩阵之间的 L2 损失用于比较 Xᵢ 和 S 的哪些样式特征是激活的特征。
用梯度带训练重量
TensorFlow 2 中较大的变化之一是切换到急切执行。在 TensorFlow 的早期版本中,计算图将首先手动构建,只有在之后才会传入和调用张量。这不太直观,也很难操作。现在,电子传感器可以通过计算来设置和调用。计算图被抽象出来,并在引擎盖下设置和执行。就像在其他很多懒惰与渴望评估的例子中一样,我发现这更容易理解,也更容易调试。
在 TensorFlow 2 中,分类问题在 Keras API 中得到很好的支持。然而,如果您需要创建一个定制的训练循环,训练特定的变量,或者为损失函数提供特殊的输入,这就不那么友好了。在这些情况下,GradientTape 可用于在执行期间跟踪可训练变量,获取梯度,然后通过优化器应用梯度。这非常适合于快速风格转移模型,因为训练涉及具有定制训练循环的生成网络,并且将几个不同的输入传递给损失函数。
监控变量
在 GradientTape 上下文中,可训练变量在向前传递期间被“监视”和跟踪。当稍后执行反向传播时,被监视的变量将根据它们的梯度应用各自的变化。虽然可训练变量可以在训练中被跟踪和调整,但被监视变量将被跟踪*,因此它们可以被优化器调整。*
无需在 GradientTape 上下文中调用watch
,即可隐式观察可训练变量。对于更细粒度的控制,您可以在 GradientTape 的构造函数中关闭它,只有块中显式“监视”的变量才会被跟踪以进行自动区分。GradientTape 上下文还允许您获取被监视的变量,这有助于调试。对于多模型,我发现自己设置被监视的变量比依赖隐式调用更实际。
一旦变量被观察到,张量通过模型并计算损失。对于快速风格转换,我们将通过我们的生成器网络进行正向传递,获得转换后的图像, Xᵢ ,然后将该图像通过 VGG 分类网络。在下面的例子中,我创建了渐变磁带上下文,观察适当的变量,然后向前传递。之后,我使用损失来获得适当的梯度,然后优化器使用这些梯度来调整可训练变量。
结果
现在是有趣的部分!在对每种风格的网络进行数小时的训练后,这些都用默认的训练参数作为通用基线。请注意,为了获得更好的结果,有些模型可以调整得更细或更长。例如,在“尖叫”实现中,有几个未经训练的点是从白色开始的。
具有 1)用于训练的风格图像,2)通常用作基线的芝加哥图像,以及 3)显示变化的个人图像的网络的结果
对摄影、艺术和深度学习充满热情,这是一个非常有趣的项目。我发现 TensorFlow 2 比以前的版本更容易使用。尽管有几个模型和几个活动部件,调用组件并深入了解网络还是很容易的。我发现在 Colab 笔记本中进行开发时,记录变量非常有用。我知道一些工程师也会基于变量名和张量形状来建立单元测试,我可能会在更正式的环境中这样做。
当然,大多数调试都伴随着与数据相关的问题。一个错误是由于不正确地缩小图像,提取了太多的“风格”。本页顶部的照片是一个令人高兴的小意外,尽管当时网络的性能与以前的实现方式不同。另一个错误仅仅是因为我使用了一张非常高分辨率的艺术品照片,而网络实际上把画布的纹理作为风格!
我希望你学到了一些关于深度学习和使用 TensorFlow2 的知识。如果你有兴趣看更多我使用的代码,或者其他与风格转移、深度学习或计算机视觉相关的主题,我在下面提供了一些资源。
额外资源
项目
- 本项目 colab:快速风格转换 TensorFlow 2
- 该项目的 github Repo:altonelli,fast-style-transfer-tf2
外部的
几年前,我用这两门课程向自己介绍了 ML 和计算机视觉
除了原始的研究文章之外,这些文章和实现是神经风格转移的重要资源
- 艺术风格转移,媒介上的 F D
- TensorFlow 关于传统风格转移的教程,在简介中提到
- lengstrom,fast-style-transfer,Github
- hwalsuklee,tensorflow-fast-style-transfer,Github
参考
[1] L. Gatys,A. Ecker,M. Bethge,一种艺术风格的神经算法(2015):https://arxiv.org/abs/1508.06576
[2]约翰逊,阿拉希,飞飞,实时风格转换和超分辨率的感知损失(2016):https://arxiv.org/abs/1603.08155
通过聚类发现自然群体
通过简单的实验学习流行的聚类算法
自然群体。图片来源
集群具有跨应用领域的重要用例,以了解数据的整体可变性结构。无论是客户、学生、基因、心电图信号、图像、股票价格的聚类。在我们的一些研究文章中,我们已经在特性选择和投资组合的优化中成功地使用了这一点,但在后面的故事中会有更多。聚类属于无监督类别,根据定义,它试图在数据中找到自然的组。
即使在深度学习时代,聚类也可以为您提供关于数据分布的宝贵见解,从而影响您的设计决策。
在这篇文章中,我们给你一些关于五种聚类算法和 Kaggle 笔记本链接的概述,我们在那里做了一些实验。目的是给你一个快速总结,让你开始申请。
K-表示:
这是所有基于距离的算法中最简单的一种。这是一种分割算法。从 k 个随机点作为聚类中心开始,然后将其余的点分配到最近的聚类中心。完成此分配后,重新计算聚类中心。这个过程一直持续到集群分配没有太大变化。
其中一些问题是:
a)我们需要知道“k”的值
b)受离群值影响。
c)取决于初始化
有些实验是在下面的 Kaggle 笔记本上做的:【https://www.kaggle.com/saptarsi/kmeans-dbs-sg
我们首先解释 k-means 是如何工作的,然后举例说明为什么需要缩放。我们还讨论了如何使用肘图绘制误差平方和,以找到 k 的最佳值。一个简单的例子是“iris”
对应视频:
理论:https://www.youtube.com/watch?v=FFhmNy0W4tE
动手:https://www.youtube.com/watch?v=w0CTqS_KFjY
K-Medoid:
K-Means 对异常值并不稳健。当我们有一个以上的属性时,我们要找到一个整体的中值,这就是所谓的中值。有些实验是用下面的笔记本做的。为了演示异常观测值的问题,我们做了一个添加三个异常观测值的小实验。如你所知,iris 有三个类(Setosa、Verginca 和 Versicolor ),它们被绘制成两个特性。这些由蓝色圆圈标记的特征的添加完全扭曲了聚类。原来的三个类合并成两个聚类,离群点在一个聚类中。
图 1:受极值影响的 K 均值。图片来源:作者笔记本https://www.kaggle.com/saptarsi/kmedoid-sg
Kaggle 笔记本链接:
https://www.kaggle.com/saptarsi/kmedoid-sg
我们讨论的主要问题是,当标签不可用(轮廓宽度)和可用(纯度)时,如何测量聚类的质量。下面是一个比较 K 均值和 K 中值的极端观测值(异常值)的简短示例。
对应视频:
理论:【https://www.youtube.com/watch?v=q3plVFIgjGQ】T4
动手:https://www.youtube.com/watch?v=L1ykPtlonAU
数据库扫描:
DBSCAN 最有前途的一点是,它将一些点识别为噪声点,这些点如果包含在聚类练习中,将会扭曲整个聚类。该算法可以找到基于密度的聚类,而不是距离。
在卫星和同心圆上比较了 DBSCAN、K-Medoid、K-Means。下面的笔记本包含简单的实验。
https://www.kaggle.com/saptarsi/dbscan-sg
在一个这样的实验中,我们创造了一个噪音点。
X = np.array([[1,2],[4,2],[3,3],[8,7],[9,9],[10,10],[25,80]])
散点图如下所示:
图 2:具有一个噪声点的 2D 数据的散点图
图片来源:作者笔记https://www.kaggle.com/saptarsi/dbscan-sg
直观上,我们理解可能有三个集群和一个外围点。
下图比较了聚类结果
图 3: DBSCAN、K-Means 和 PAM 处理噪声点的能力
图片来源:作者笔记https://www.kaggle.com/saptarsi/dbscan-sg
我们可以看到 DBSCAN 清楚地将点置于另一种颜色中,因为 k-意味着原始的两个聚类丢失,对于 k-medoid 更好,但是它没有单独识别外围点。
下面的 make circle 提供了算法的另一个比较,它清楚地显示了 DBSCAN 如何正确地识别属于两个集群的两个圆。
图 4:同心圆上的 DBSCAN、K-Means 和 K-Medoid
图片来源:作者笔记 https://www.kaggle.com/saptarsi/dbscan-sg
对应视频:
理论:【https://www.youtube.com/watch?v=RZg51WH7caQ】T4
动手:https://www.youtube.com/watch?v=A8OnRH42hWE
凝聚聚类:
以上三种是基于分区的聚类,而这是一种分层聚类,通过树状图揭示了关于整体结构的更多语义。层次聚类中的一个重要考虑是当合并到聚类中时要考虑哪个距离,是最小距离、最大距离还是平均距离。在同心圆、半月形、高斯数据和种子数据集上进行了各种实验。包含一些实验的笔记本如下:
使用 Kaggle 笔记本探索和运行机器学习代码|使用 UCISeeds 的数据
www.kaggle.com](https://www.kaggle.com/saptarsi/agglomarative-clustering-sg)
笔记本上的符号图
图 5:简单的 dendorgam
图片来源:作者笔记https://www.kaggle.com/saptarsi/agglomarative-clustering-sg
对应视频:
理论:https://www.youtube.com/watch?v=RZg51WH7caQ
动手:https://www.youtube.com/watch?v=z7jXh_RzL_k
预期最大化:
其实现方式是通过高斯混合建模。基本上,每个聚类可以被认为来自不同的多元正态分布。与所有 4 种方法相比,这是一种软聚类方法。下面是一个笔记本,里面有一些简单的实验。这是一种更灵活的方法,允许圆具有非球形形状。
https://www.kaggle.com/saptarsi/gmm-sg
对应视频:
理论:【https://www.youtube.com/watch?v=LaL0BfvUurs】T2
动手:【https://www.youtube.com/watch?v=A8OnRH42hWE
总结:
在下表中,我们总结了算法的优缺点
聚类算法的比较。作者创建的图像
何时使用哪种算法?
我总是从 K-Means 开始,观察聚类质量,只有当数据非常随机,密度变化时,我才会选择 DBS can。如果我想应用聚类进行总结 k-medoid 是一个不错的选择。凝聚聚类因其呈现的可视化而具有吸引力。EM 是一个理论上非常健壮的算法,它有许多其他的应用,在有重叠簇的地方 EM 可能是一个很好的选择。
参考资料:
[1]https://towards data science . com/clustering-clearly-explained-5561642 ec20c
[3] Tan PN,Steinbach M,Kumar V .数据挖掘导论。培生教育印度公司;2016.
学习用 Tableau 在 5 分钟或更短时间内预测
(舞台上由人扮的)静态画面
谁知道会这么容易?
由 @softie__arts 创作
使用 Python 和 R 有很多方法来可视化您的数据,但是强大的商业智能工具 Tableau 使得创建可视化成为一个简单的拖放过程。
Tableau 也不仅仅是一个可以用来创建图表的工具。您可以编写自定义 SQL,创建复杂的计算字段,甚至生成高质量的预测!
请继续阅读,快速了解 Tableau 如何通过一次点击自动生成预测(包括 gif)。
Tableau 如何创建预测的简要说明
Tableau 使用指数平滑法创建预测。虽然 Tableau 也支持使用整数值而不是日期,但是您可以在绘制度量值随时间变化的可视化图形上这样做。
预测的好坏取决于它们所依据的数据。
一般来说,在尝试创建预测之前,您应该包括尽可能多的数据。Tableau 在技术上只需要五个数据点就可以创建一个时间序列预测,尽管它甚至会尝试预测一个没有足够数据的视图来建立一个准确的模型,但预测的好坏取决于它们所基于的数据。
如果您基于太少的数据或受外部变量影响太大的数据创建预测,您的预测很可能不适合未来。您还应该注意 Tableau 在每次预测时自动生成的Prediction Intervals
,以了解预测值可能落入的范围。
Tableau 将从八个指数平滑模型中选择一个来创建最佳预测(有关更多信息,请参见最后一节)。指数平滑模型也可以考虑Trend
(模型的总体上升或下降方向)和Seasonality
(特定时期的波动模式)。
在 Tableau 中创建预测
在本演练中,我们将使用 Tableau 附带的“样本超市”数据集。如果您想继续操作,请打开一个新工作簿并连接到此数据源。
我们从一个简单的图表开始,它显示了一段时间内每月的平均销售额:
显示一段时间内平均销售额的基本图表
预测和趋势/季节性定制选项
要使用预测扩展图表,右键单击视图上的任意位置(图表所在的位置)并单击Show Forecast
。
您可以选择自定义预测的提前期,但是 Tableau 设置了一个默认值(在本例中为 13 个月),这通常是合适的。
在 Tableau 中创建和定制预测
首先,预测只是一条直线,它来自 Tableau 创建的初始模型。这不是默认的,因为 Tableau 会尽最大努力生成一个适合现有数据的模型。但是,有时您必须定制模型,以使其更准确地描绘未来的数据点。为此,再次右键单击视图,单击Forecast Options
,然后在Forecast Model
面板中选择“自定义”。
在这种情况下,当定制模型Trend
和Seasonality
时,我循环通过不同的选项(加法和乘法)。有关这些如何工作的更多信息,请查看 Tableau 文档中的“模型类型”部分。
我决定将Seasonality
调整为乘法并保持Trend
不变。
预测描述
要了解您的预测是如何生成的,右键单击视图并选择Describe Forecast
。这将弹出一个包含预测模型信息的弹出窗口。下面,您可以看到哪些数据用于创建预测,以及对您预测的变量(在本例中为“平均销售额”)的影响。
表格预测汇总说明
如果您点击Model
选项卡,您将看到与计算 Tableau 使用的指数平滑预测模型相关的值。
Tableau 预测模型描述
预测化妆品和预测间隔
您可以将图表的预测部分更改为不同的颜色,使其与图表的其他部分更加突出。为此,只需点击Marks
卡中的Color
按钮,并选择您想要的预测模型颜色。
这也将影响图表中的“预测区间”,该区间显示了预测值可能位于的值范围。您可以通过右击并再次转到Forecast Options
来设置预测(置信)区间百分比。默认情况下,预测间隔设置为 95%,但是如果您想查看没有预测间隔的图表,可以自定义该值或将其完全删除。
在 Tableau 中自定义预测
预测与趋势线
除了指数平滑模型,您还可以为图表创建趋势线。只需转到Analytics
选项卡,然后双击Trend Line
或将其拖至视图。Tableau 将为您提供线性、对数、指数、多项式或幂型趋势线的选项。
如果您将鼠标悬停在趋势线上,您将会看到 Tableau 使用了哪些值来在图表上显示它。对于完整的方程和系数,你可以右键点击趋势线,并前往Describe Trend Line
进行总结。
在 Tableau 中创建趋势线
有趣的是,您可以看到原始图表和我们生成的指数平滑预测模型之间的线性趋势线略有不同。
我希望你发现这个快速介绍 Tableau 预测有用!Tableau 是一个非常强大的数据可视化工具,它不仅仅可以创建图表。
和往常一样,理解Tableau(和你使用的其他程序)如何创建预测模型是很重要的,因为你做出的决定不仅要基于数据,还要基于你理解的数据。
通读 Tableau 的预测文档,你也可以浏览 Tableau 可以使用的所有指数平滑模型这里。
此外,在可视化数据之前需要采取的一些重要步骤是收集和预处理。您可以利用 Python 中的 Pandas 库来开始这项工作。请随意查看我以前在这些主题上的一些工作:
正确探索、理解和组织您的数据。
towardsdatascience.com](/4-different-ways-to-efficiently-sort-a-pandas-dataframe-9aba423f12db) [## 如何使用 Python 和 Xpath 抓取网站
用 XPath 表达式从 HTML 中收集星球大战引言
towardsdatascience.com](/how-to-use-python-and-xpath-to-scrape-websites-99eaed73f1dd) [## 一种快速重新格式化熊猫数据框中的列的方法
使用 df.melt 将多个列压缩成一列。
towardsdatascience.com](/a-quick-way-to-reformat-columns-in-a-pandas-dataframe-80d0b70de026)
感谢您的阅读,祝您的 Tableau 冒险之旅好运!
学会学习更多:元强化学习
建造一个人造大脑
摄影爱好在 Unsplash 上
强化学习的 ELI5 定义是通过从以前的错误中反复学习来训练一个模型表现得更好。强化学习为代理提供了一个框架来解决现实世界中的问题。他们能够学习规则(或策略)来解决特定的问题,但是这些代理的主要限制之一是他们不能将学习到的策略推广到新的问题。先前学习的规则只适合特定的问题,对于其他(甚至类似的)情况通常是无用的。
另一方面,一个好的元学习模型被期望推广到模型在训练中没有遇到的新任务或环境。适应这种新环境的过程可以被称为小型学习会议,在有限接触新配置的情况下进行测试。在缺乏明确的微调模型的情况下,可以观察到元学习能够自主地调整内部状态以推广到更新的环境。
元强化学习就是应用于强化学习的元学习
此外, Wang et al. 将元学习描述为“使用递归模型的元学习的特殊类别,应用于学习”,这似乎比上面的定义更全面。
我们开始吧
在强化学习中,代理在每一步接收观察 (例如视频游戏中角色的位置),并基于这些观察输出动作 ,如“向前移动”或“向右转”。基于这些行动的结果,代理会收到奖励或惩罚,这将在培训中进一步指导它,帮助它为以后的步骤做出更有意义的观察。该模型的目标是最大化奖励和最小化惩罚。
在元强化学习中,训练和测试任务是不同的,但是来自同一个问题家族。一个很好的例子是不同布局的迷宫,或者不同概率的多臂强盗问题(解释如下)。
一个简单的实验
多臂强盗问题是一个经典问题,它很好地展示了探索与开发的两难困境。你可以想象自己在一个只有两个杠杆的房间里,其他什么都没有。
多兵种土匪问题;作者图片
- 拉左边的杠杆 A1 会给你一个概率 p 来获得奖励 r 。
- 拉右边的杆 A2 会给你一个概率 (1-p) 来获得奖励 r.
为了从逻辑上回答这个问题,你必须知道概率 p 的值。较高的 p 值将保证从杠杆 A1 获得奖励的较高机会,而较低的 p 值将保证从杠杆 A2 获得奖励的较高机会。这正是 meta-RL 如此有趣的原因。如果你在你的模型上投入足够的价值,在与环境互动并从中学习之后,它会越来越好地选择正确的杠杆。传统的基于 RL 的方法将无法处理变化的概率,并且通常会因不同的 p 值而失败。
实际上,受过不同概率的两级问题训练的 meta-RL 智能体能够从两级中选择正确的一级,从而使用非常少量的数据点获得最高的回报。它使用对联*(行动,回报)*来计算每个杠杆的“风险与回报”因子。
下面是一个示例,左边是一个未经过训练的代理(pp= 0.92),右边是一个经过训练的meta-RL 代理(pp= 0.76)****
未经训练和经过训练的 meta-RL 代理;来源
关键组件
meta-RL 涉及三个关键组件。下面将对它们进行详细描述。
**有记忆的模型:**没有记忆,meta-RL 模型就没用了。它需要记忆来从直接环境中获取和存储关于当前任务的知识,这将有助于它更新其隐藏状态。一个递归神经网络维护 meta-RL 模型的隐藏状态。
创建一个好的 RNN 的动力对于本文的范围来说太宽泛了。然而,meta-RL 和 meta-RL 都使用 LSTM 来管理它们的隐藏状态。
**元学习算法:**元学习算法将定义我们如何根据它所学习的内容来更新模型的权重。该算法的主要目标是帮助优化模型,以在最短的时间内解决一个看不见的任务,应用它从以前的任务中学到的东西。以往的研究通常使用普通梯度下降更新的 LSTM 细胞。
和爬行动物,都是经过验证的方法,能够更新模型参数,以便在新的和未知的任务上实现良好的泛化性能。
**一个合适的 MDPs 分布:**一个马尔可夫决策过程(MDP) 是指智能体观察环境输出的整个过程,由一个奖励和下一个状态组成,然后在此基础上做出进一步的决策。由于代理在其培训期间会暴露于许多不同类型的环境和任务,因此它需要能够快速适应不断变化的条件和不同的 MDP。
在这三个组件中,这是研究最少的组件,也可能是关于 meta-RL 的最具体的组件。由于每个任务都是一个 MDP,我们可以通过修改奖励配置或环境来构建 MDP 的分布。
进化算法是保证生成良好环境的一个很棒的方法。它们通常是启发式的,受自然选择过程的启发。一群随机产生的解会经历一个评估、选择、突变(如果我们把遗传算法也加入进来)和繁殖的循环;好的解决方案能坚持到最后。王等人的《诗人》是一个基于进化算法的框架的好例子。
下面展示了一对开放式开拓者(POET ),最初从一个普通的环境和一个随机初始化的代理开始。然后,它增长并维护一对一配对环境和代理的群体。根据作者的观点,POET 旨在实现两个目标,进化环境的多样性和复杂性;以及优化代理以解决它们的并行环境。
POET:基于进化算法的框架;来源
没有奖励函数的 MDP 被称为受控马尔可夫过程 (CMP)给定预定义的 CMP,我们可以通过生成奖励函数 R 的集合来学习关于各种任务的知识,这鼓励了有效的元学习策略的训练。
Gupta 等人提出了两种在 CMP 环境下增长任务分布的无监督方法。假设有一个潜在的潜在变量与每个任务相关联,它将奖励函数参数化为潜在变量的函数以及鉴别器函数(用于从状态中提取潜在变量)。 — 来源
研究论文描述了构造鉴别器函数的两种主要方法:
- 对鉴别器的随机权重进行采样
- 学习鉴别器功能以鼓励多样性驱动的探索。如果你正在寻找关于这个主题的更全面的分析,你可以参考他们的另一篇论文,“DIAYN(多样性是你所需要的)。
在 CMP 环境中增加任务分布的复杂性超出了本文的范围,我强烈推荐任何感兴趣的人深入研究这篇文章以获得更深入的观点。
与强化学习的比较
meta-RL 系统非常类似于普通 RL 算法的系统,除了最后的奖励以及最后的动作也包括在策略观察中,以及当前状态。这种改变的目的是馈送并跟踪所有任务和观察的历史,以便模型可以基于当前 MDP 在内部更新状态、动作和奖励之间的动态,并相应地调整其针对其他 MDP 的策略。
meta-RL 和 meta-RL 都实现了 LSTM 策略,其中 LSTM 的隐藏状态作为记忆来跟踪特征的变化。该策略本质上是循环的,不需要显式输入最终值。
meta-RL 论文中使用的不同演员-评论家架构(全部都是循环模型);来源
训练程序如下进行:
- 品尝新 MDP
- 重置模型的隐藏状态
- 收集多个轨迹并更新模型的权重
- 从步骤 1 开始重复
超出
训练 RL 算法有时会很困难。如果一个元学习代理能够变得如此聪明,以至于它能够根据在特定任务上接受训练时推断出的知识来解决的任务分布变得非常广泛,那么我们将朝着广义智能(或新的流行词——人工通用智能{AGI})的方向前进,本质上是建立一个能够解决所有类型的 RL 问题的大脑。
作为旁注,我还想指出 meta-RL 和AI-GAs(J . Clune 著)之间毫不奇怪的相似之处,后者提出通往 AGI 的有效途径是让学习自主。它基于三个支柱:元学习架构、元学习算法和有效学习的自动生成算法。
参考资料和进一步阅读
没有亚瑟·朱利安尼的努力,这项工作是不可能完成的。你可以在这里查看他对 meta-RL 算法的出色实现。王等人的这篇研究论文也让我对 meta-RL 的一些核心概念有了很好的见解,如果你想有一个更全面的看法,我强烈推荐阅读它。最后,Clune、Stanley、Lehman 和 Wang 在工程博客上发表的这篇文章也帮助我理解了这些领域的开放性,以及如何克服极其困难的挑战。Lilian Weng 的这个博客也是一个很好的进一步阅读的资源。
学习为信息检索排序:深入研究 RankNet。
深入了解可用于信息检索的最新排名系统。
文档排序是信息检索中的一项重要任务。(图片由弗洛里安·施梅兹在 Unsplash 上拍摄)
机器学习和人工智能目前正在推动计算机科学领域的创新,它们正被应用于跨学科的多个领域。然而,传统的最大似然模型仍然可以大致分为两类问题的解决方案。
- 分类——旨在根据各种特征将特定的数据实例标记到桶中。
- 回归——我们希望得到一个连续的实数作为给定特性集的输出。
机器学习的一个相对较少探索的应用是根据相关性对数据进行排序,这在搜索引擎等信息检索系统中变得有用。这些类型的模型更关注项目的相对排序,而不是单个标签(分类)或分数(回归),并被归类为 学习排序 模型。
rank net 简介
2005 年,克里斯·伯格斯等人。艾尔。微软研究院推出了一种新颖的方法来创建学习排名模型。他们的方法(在这里可以找到)采用了一个概率成本函数,该函数使用一对样本项目来学习如何对它们进行排序。该功能主要是尝试最小化纠正所选项目的错误排序所需的交换次数。他们的论文进一步探索了这种方法,通过神经网络实现这种成本函数,并通过梯度下降进行优化。这个名为“ RankNet ”的网络也在一些真实世界的数据上进行测试,以显示其有效性。
如本文中所实现的,RankNet 的工作总结如下。
训练网络
- 构造了一个具有一个输出节点的两层神经网络。输出值对应于该项与集合的相关性,并且输入层可以基于特征向量的大小而具有多个节点。
- 从数据集中选择两个随机样本,并分别对这两个样本进行前向传播,从而产生两个输出值,每个项目一个。
- 确定成本,该成本是这两个输出值之差的激活函数(e g: sigmoid )。假设第一个样本的排名高于第二个样本,并计算适当的损失。
- 这种损失被反向传播到网络中以学习所选择的例子。
- 执行步骤 2-4,直到训练完成(基于时期数)。
图片:所描述网络的前向传播演示。(礼貌:自己)
步骤 3 中提到的假设只不过是确定两个选定项目的预期等级。该等级可以通过比较特定项目相对于整个集合的相关性评级来确定。此外,可以通过基于一些假设手动设置相关性,或者通过使用人工评级者基于相关性对结果进行分类,来确定该相关性评级。
检索一个集合的排名
- 为了对特定集合中的项目进行排序,每个项目的特征向量通过网络传播,并且输出被存储。
- 现在,只需按照输出的降序排列项目,就可以对项目进行排序。
Burges 和他的同事用一些人工数据证明了这种方法的有效性。他们采用了两个排序函数,一个随机 2 层神经网络,以及一个随机多项式函数。在对 5000 个特征向量输入运行 100 个时期的数据后,他们获得了如下所示的结果。
图:两个排名函数的成对%正确结果。(礼貌:学习使用梯度下降法排名)
此外,这种方法在真实世界的数据——给定查询的搜索引擎结果——上进行了测试。需要注意的一点是,由于该模型不执行传统的分类或回归,因此其准确性必须基于排名质量的度量来确定。现实世界示例的排名准确度度量被选择为“NDCG”(正常贴现累积收益),这是用于评估特定排名集合的有效性的流行方法。NDCG 得出一个介于 0 和 1 之间的结果,1 代表项目的最佳排序。在训练了六个不同的排名函数(包括 RankNet 的两个不同实现)之后,对于搜索结果的给定查询/文档特征向量,在测试集上获得了以下结果。
图片:RankNet 与其他系统的比较。(礼貌:学习使用梯度下降法排名)
因此,我们看到 RankNet(一层和两层)的平均 NDCG 明显高于其他排名函数。
未来的改进
Burges 和他的同事进一步将 RankNet 的概念发展成训练时间更短、精度更高的模型。
λrank
在原始 RankNet 的训练过程中,发现不需要计算成本本身。相反,成本的梯度足以确定这对项目的预测排名。这可以被视为指示特定项目的移动方向的箭头。
图像:成本梯度的可视化,用箭头指示移动方向。(承蒙:从兰克内到兰达兰克到兰达玛特:概述)
此外,当他们通过交换文件导致的 NDCG 变化来放大这个梯度时,他们获得了更好的结果。因此,LambaRank 被证明训练速度更快,准确性也有所提高。
λmart
在另一个版本中,引入了渐变增强的树版本的 LambaRank】。在实验数据集中,这已被证明比前两个模型更加准确。
结论
因此,我们已经看到了一些最先进的排序技术,当我们想要在信息检索系统中对一组项目进行排序时,这些技术非常有用。所以问题来了,是什么阻止我们去实现这些模型呢?答案很简单——没事!
(对于感兴趣的人,我自己使用 Keras 和 TensorFlow 实现的 RankNet 可以在https://github.com/devanshgoenka97/RankNet)
参考
【2】:布格斯,c,拉格诺,r . j .&勒,Q.V. (2007)。学习用非光滑代价函数排序。
【3】:吴,q,Burges,c,Svore,k,&高,J. (2009)。适应信息检索措施的助推。信息检索,13 ,254–270。
【4】:布格斯,C. (2010)。从兰克内到兰达兰克再到兰达玛特:概述。
用迷你批处理学习 Wasserstein
迷你批次 Wasserstein 距离属性简介
当我们对迷你批次使用 Wasserstein 距离时会发生什么?论文的结果来自 minibatch Wasserstein 的学习:渐近和梯度性质,发表在 AISTATS 2020 会议上。
对于许多机器学习应用,如生成模型[1]或领域适应[2],最优传输已经变得非常流行。在这些应用中,人们希望最小化源数据和目标数据之间的统计距离。为此,瓦瑟斯坦距离成了一项基本资产。它既可以用原始公式[2,3]计算,也可以用对偶公式[1]计算,并依靠小批量进行优化。不幸的是,由于连续函数的梯度计算,对偶可能导致数值不稳定,因此使用原始公式。出于学习目的,可以在[第 9.4、4 节]中找到对每种配方的利弊的综述。
然而,计算迷你批次之间的初始 Wasserstein 距离 并不等同于计算完全测量之间的初始 wasser stein 距离 。在这个故事中,我将描述minibatch wasser stein distance,其中 mini batch 范式对损失的后果被 置之不理。 对所呈现结果的全面回顾可见于我们的 AISTATS2020 论文【5】。
声明:为了简单起见,我将给出 Wasserstein 距离的结果,但所有这些结果都可以扩展到所有最优运输变量。我们也考虑一般地面成本。此外,我们将不区分元素集和它们的度量。
瓦瑟斯坦距离
基于 Kantorovich 问题, **Wasserstein 距离通过根据地面度量寻找测量值α和测量值β之间的最小位移成本来测量两个分布之间的**距离。设α (size n )和β (size n )为两个离散有界一致测度设 C 为一个度量(size n × n )。瓦瑟斯坦距离定义为:**
情商。(1):瓦瑟斯坦距离
⟨在哪里?,.⟩是 Frobenius 乘积和 E ( α,β)约束的集合。Wasserstein 距离必须在完全测量值α和β之间进行计算。U 不幸的是,它在数据数量上有一个立方复杂度 O(n^3) ,使它不适合大数据应用。OT 问题的变体出现了,如熵 OT 或 Sinkhorn 散度,但它仍然具有平方复杂度。为了克服这种复杂性,可以依靠计算源和目标测量的小批次之间的 Wasserstein 距离。
小批量 Wasserstein 距离
使用小批量策略很有吸引力,因为它在小批量大小上给出了一个立体的复杂性 O(m^3) 。然而,最优运输的原始公式不是一个总和,使用小批量并不等同于等式。(1).实际上,它 并不计算瓦瑟斯坦距离 ,而是计算从输入测量中采样的迷你批次 上瓦瑟斯坦距离的 期望值。形式上,它计算:
情商。(2) : 批次间 Wasserstein 距离的期望值
其中 m 为批量。由于它不等同于原始问题,所以理解这个新的损失是很有趣的。我们将回顾运输计划的结果,渐近统计特性,最后,一阶优化方法的梯度特性。
估计小批量 Wasserstein
让我们首先设计一个估计量。情商。(2)可以用以下估算器进行估算:
情商。(3):Eq 的估计量。(2)
**其中总和取自源和目标测量中所有可能的小批测量 A 和 B。然而,要计算的小批量项目太多了。幸运的是,我们可以依赖子样本量。我们注意到 D_k,一组基数 k ,其元素是均匀绘制的小批偶。我们定义:
情商。(4):方程的不完全估计量。(2)
其中 k 是 minibatch 对的数量。我们还可以为运输计划计算一个类似的估计量,以估计小批量范式对运输计划的影响,更具体地说,是样本之间的联系(本文将详细介绍这一结构)。这个想法是平均样本之间的连接,以获得平均运输计划。
情商。(5):小批量 Wasserstein 计划估计量
当然,存在一个不完全的估计量,它遵循与等式 1 相同的结构。(4):
情商。(6):小批量 Wasserstein 计划不完全估计量
由于现在我们可以估计我们的数量,我们将给出一个小而有用的例子。
1D 案例:迷你批次切片 Wasserstein
1D 案例是一个有趣的特例。这很有趣,因为当数据位于 1D 时,我们可以获得接近形式的瓦瑟斯坦距离,然后,我们可以很容易地计算加班计划。1D 案例也是一个广泛使用的距离的基础,切片瓦瑟斯坦距离。计算全部最小批量 OT 计划的公式(等式。(5))可以在论文中找到。
我们考虑了源域和目标域的 20 个数据,采用统一的权重,并绘制了几个 ot 场景的平均运输计划。实验显示了不同批量的小批量 OT 计划 m 和正则化变量(熵+ L2)的 OT 计划之间的差异。
1D 措施的不同 OT 问题之间的运输计划[5]
我们在小批量瓦瑟斯坦距离和正则化瓦瑟斯坦变异之间看到了相似的效应。我们得到个样本之间的非最优连接**。对于小批量 Wasserstein 距离,当 m 减少时**连接数量增加。当正则化系数变大时,它类似于熵 OT 变量。人们还可以注意到,当批量减少时,连接的最高强度降低,这是由于约束。既然我们已经看到了小批量对运输计划的影响,让我们回顾一下损失的性质。****
基本属性
由于我们有了一个新的损失函数,有必要回顾它的优点和缺点,以比较概率分布。它具有以下属性:
- 对于 iid 数据, U 和ũ是 Eq 的无偏估计量。(2)
- U 和ũ的论点是对称的
- 严格来说是 阳性
- U( α,α)和*ũ(*α,都是 严格意义上的 正
这里有趣的属性是最后一个。对于非平凡测度,我们打破了可分性距离公理。因此, minibatch Wasserstein 距离不是距离 。这是获得数字速度的代价。我们将在梯度流实验中强调这种效应。
**由于我们不知道分布 α 和 β,我们想知道等式。(2)可以用等式有效地估计。(4).
统计特性
我们的不完全估计器定义了一个 不完全双样本 U-统计量 。U-statistics 是由 Hoeffding 在 60 年代开发的[6]。使用 Hoeffding 不等式,可以得到我们的估计量围绕其期望值的偏差界限,概率为 1-δ:
其中 M 是支撑α和β的大小。这个偏差范围表明,如果我们增加数据 n 和批次 k 的数量,同时保持小批次大小固定,误差会以指数速度收敛到 0。值得注意的是,界限不依赖于数据的维度,这在高维优化时是一个有吸引力的属性。
对于生成模型,我们发现小的 k 足以获得有意义的结果,但是使用小批量会导致更长的训练时间。
运输计划和边际利润也有类似的性质。预计运输计划与 1/n 之间的偏差概率为 1-δ:
到边缘的距离
因为我们知道我们的损失有很大的统计性质,我们知道要研究是否可以用现代优化框架将其最小化。
SGD 的无偏梯度
众所周知,经验 Wasserstein 距离相对于连续测量值之间的 Wasserstein 距离具有偏差梯度[7]。这种偏差使得最小化经验 Wasserstein 距离不会导致连续测量之间的 Wasserstein 距离最小化。
与 Wasserstein 距离不同,mini batch wasser stein具有很好的属性,即具有 无基底梯度 ,因此我们可以使用 SGD 和我们的不完全估计量来最小化连续测量之间的损失。
这一结果在熵 OT 变体中得到了证明。与瓦瑟斯坦距离不同,熵 OT 处处可微。这是我们证明的一个基本要素。它允许我们使用无偏估计量和微分引理来证明无偏梯度。然而,我们在实践中使用迷你批次 Wasserstein 距离时没有遇到任何问题。
生成模型
我们举例说明了生成模型的小批量 Wasserstein 损失的使用。目标是学习生成模型以生成接近目标数据的数据。我们绘制了 8000 个点,它们遵循 2D 的 8 个不同的高斯模式(每个模式 1000 个点),其中模式形成一个圆。生成数据后,我们使用迷你批次 Wasserstein 距离和迷你批次 Sinkhorn 散度作为平方欧几里德成本的损失函数,并将它们与 WGAN [1]及其具有梯度惩罚 WGAN-GP [8]的变体进行比较。
高斯模式生成[5]
我们看到,我们能够按照不同的模式生成数据。在 CIFAR 10 数据集上使用 minibatch Sinkhorn 散度的更广泛的结果可在[3]中获得。
梯度流量
在本节中,我们将介绍小型 Wasserstein 梯度流的使用。我们考虑来自名人数据集的 5000 张男性和 5000 张女性图像,并希望在男性和女性图像之间应用梯度流。梯度流的目的是模拟一个分布,该分布在每次迭代中遵循梯度方向,使最小批次 Wasserstein 距离最小化。形式上,我们对以下等式进行积分:
对于这个实验,我们将批量大小 m 设置为 500,将批量对数量 k 设置为 10。
5000 个男性图像和 5000 个女性图像之间的梯度流实验[5]
我们看到沿着梯度流动的图像的自然演变。然而最后的结果有点模糊。这是因为事实上小批量 Wasserstein 距离不是距离**,并且我们与目标分布不匹配。**
大规模颜色转移
我们还用我们的方法进行了颜色转移实验。颜色传输的目的是转换源图像的颜色,使其符合目标图像的颜色。最优运输是解决这个问题的一个众所周知的方法[9]。两点云图像之间的传输计划通过使用重心投影给出了传输颜色映射。其思想是使用已开发的小批量运输计划估计器,该估计器具有较小的内存和计算成本。我们使用了两张各为 1M 像素的图像,几个批次大小和批次数量。据我们所知,这是第一次有一种方法能够处理大规模的颜色转换。
大规模彩色转印[5]
我们可以看到,当批量太小时,颜色的多样性下降,就像熵解算器对大正则化参数所做的那样。这显然是由于源样本和目标样本之间的大量连接。然而,即使对于 100 万像素,1000 的批量大小也足以保持良好的颜色多样性。
结论
在这篇文章中,我们描述了小批量 Wasserstein 距离。一个 Wasserstein 距离变量,其目的是计算迷你批次上的原始 Wasserstein 距离。我们描述了一个形式,这个损失函数的基本性质,渐近性质,最后,优化程序。然后我们通过三个不同的实验研究了它的用途。关于我们如何改善迷你批次 Wasserstein 距离还有许多问题,我们将在未来的博客帖子中详细介绍。
文献学
[1]马丁·阿尔乔夫斯基,苏史密斯·钦塔拉,莱昂·博图。瓦瑟斯坦甘,ICML 2017
[2] BB Damodaran,B Kellenberger,R Flamary,D Tuia,N Courty,“ DeepJDOT:无监督域适应的深度联合分布最优传输”,ECCV 2018。
[3]奥德·热纳维、加布里埃尔·佩尔、马尔科·库图里。用 sinkhorn 分歧学习生成模型,AISTATS 2018
[4]加布里埃尔·佩尔,马尔科·库图里。计算最优运输,基础与趋势
[5]基利安·法特拉斯、尤尼斯·津、雷米·弗拉芒里、雷米·格里邦瓦尔、尼古拉斯·库蒂。用 minibatch Wasserstein 学习:渐近和梯度性质,AISTATS 2020
[6]瓦西里·赫夫丁。有界随机变量和的概率不等式,美国统计协会杂志 1963 年
[7]马克·贝勒马尔、伊沃·达尼埃尔卡、威尔·达布尼、沙基尔·穆罕默德、巴拉吉·拉克什米纳拉亚南、斯蒂芬·霍耶、雷米·穆诺斯。Cramer 距离作为有偏 wasserstein 梯度的解决方案。
[8]伊桑·古尔拉贾尼、法鲁克·艾哈迈德、马丁·阿尔约夫斯基、文森特·杜穆林、亚伦·库维尔。wasser stein GANs 的改进培训,NIPS 2017
[9]西拉·费拉丹斯、尼古拉·帕帕达基斯、朱利安·拉宾、加布里埃尔·佩雷、让-弗朗索瓦·奥约尔。正则化离散最优运输。计算机视觉中的尺度空间和变分法。
在不确定中学习
强化学习导论
现代强化学习背后的思想建立在试错学习和计算自适应控制的理论之上。这些方法的总体目标是构建一个代理,当它在反馈循环中与随机环境交互时,它可以最大化某个行为的回报。在面对不确定性时,代理通过从环境中接收到的响应来更新其决策策略。
**通用强化学习框架。**智能体在反馈配置中与环境交互,并根据它从环境中获得的响应更新其选择动作的策略。
试错搜索是从动物心理学领域学习行为的一种方法。桑代克、巴甫洛夫和斯金纳是这个学习领域的主要支持者。试错学习理论关注的是主体如何通过加强或削弱心理联系来学习行为,这种心理联系是基于主体在执行特定动作后从环境中感知到的满意或不满意(Thorndike,1898)。这种学习的想法被称为“效果法则”,其中“满意”是基于“奖励”的伴随行为的强化,“不适”导致由于“惩罚”而导致的行为中止。B.F. Skinner 在他关于操作性条件作用的工作中进一步探讨了这些奖励和惩罚的想法,该工作假设代理人根据刺激或行动自愿加强其行为,导致来自环境的反应(Skinner,1938)。另一方面,巴甫洛夫的经典条件作用认为,刺激的配对(其中第一个是无条件刺激)通过主体的行为产生了非自愿的反应(巴甫洛夫,1927)。这两种学习的行为理论都涉及到某种刺激对反应的联合配对的概念,由此一个主体的行为受到反馈循环中重复动作的制约。
**T 型迷宫。**T 型迷宫用于条件反射实验,以检查啮齿动物在使用不同时间表的连续试验中学习寻找食物的行为。
试错学习和效果法则有两个不同的特性影响了现代强化学习技术,因为它们是选择性的和联想性的。现代 RL 是选择性的,因为对于环境的特定状态,一个动作是从一组动作中取样的,它是关联的,因为有利的动作及其关联状态被记住(即存储在记忆中)(萨顿和巴尔托,1998)。
自适应控制领域涉及学习复杂动态系统中控制器(或代理)的行为,其中受控系统的参数中存在不确定性。(Bellman,1961)将控制问题分为确定性、随机性和适应性。在自适应控制系统中,系统中存在相当大的不确定性,对环境的结构或参数的分布知之甚少。虽然可以使用实验来获得关于系统的一些信息,但是所花费的时间将使得这种方法不可行。因此,需要在“在线”配置中学习控制器的行为。(Bellman,1957a)将 Bellman 方程展示为捕捉动态系统的状态和值函数的函数,并引入动态规划作为寻找自适应控制问题的最优控制器的一类方法。(Bellman,1957b)将马尔可夫决策过程(MDP)公式化为离散时间随机控制过程,用于建模强化学习框架,其中主体在反馈回路中与环境交互。Markov 属性假设当前状态获取了预测下一个状态及其预期响应所需的所有信息,而不依赖于先前的状态序列。换句话说,马尔可夫性质是环境的未来状态仅依赖于当前状态的条件概率。因此,如果我们知道当前状态,它就有条件地独立于过去的状态。MDP 基于环境状态具有马尔可夫性的理论假设。
文献学
- 巴甫洛夫 IP (1927)。由 Anrep GV 翻译。“条件反射:大脑皮层生理活动的调查”。大自然。121 (3052): 662–664.Bibcode:1928Natur.121…662D。doi:10.1038/121662a0。
- 斯金纳 B. F. (1938)。生物行为:实验分析。阿普尔顿世纪。
- 萨顿和巴尔托(1998 年)。强化学习导论(第 2 卷第 4 期)。剑桥:麻省理工学院出版社。
- 桑代克,E. L. (1898)。动物智力:动物联想过程的实验研究。心理学评论:专论增刊,2(4),i-109。https://doi.org/10.1037/h0092987.
- 萨顿和巴尔托(1998 年)。强化学习:导论。麻省理工出版社。
- 贝尔曼,R. E. (1961)。自适应控制过程——指南之旅。普林斯顿:普林斯顿大学出版社。
- 贝尔曼,R. E. (1957a)。动态编程。普林斯顿:普林斯顿大学出版社。
- 贝尔曼,R. E. (1957b)。马尔可夫决策过程。数学与力学杂志,6(5),679–684。从 www.jstor.org/stable/24900506.取回
最初发表于【https://ekababisong.org】。
学习 Wolfram:从零到英雄
通过这一快速介绍,快速启动您的数据科学能力
Wolfram 语言提供了一个超级丰富的笔记本界面和数千个精心设计的函数,让数据科学项目变得令人愉悦。借助优秀的教程、详尽的参考文档和上下文相关的帮助,现在开始使用 Wolfram 语言比以往任何时候都更容易。
作为一名长期使用 Wolfram 语言的程序员,我认为分享一下我对如何将这个强大的应用程序添加到您的数据科学技能组合中的看法会很有用。
(图片由作者提供)
开始的最好地方是“程序员快速入门”教程。它出色地介绍了从定义函数的基础到更高级的主题,如构建界面和将笔记本、图形和图像部署到 Wolfram Cloud。每个部分都有一个视频,涵盖了相同的主题,这给了你另一个很好的学习选择。
花几分钟时间阅读本教程,快速掌握 Wolfram 语言的基础。几乎…
www.wolfram.com](https://www.wolfram.com/language/fast-introduction-for-programmers/en/)
您可以通过点击教程中的“入门”按钮立即开始使用 Wolfram 语言。这将在 Wolfram Cloud 中打开一个笔记本,不需要下载或注册任何东西。只需尝试教程中的例子,并开始尝试!
磨练编程技能的下一步是访问“Wolfram U”学习网站。
为 Mathematica、Wolfram 语言、数据科学、机器等开放课程、课堂、培训、免费视频和活动
www.wolfram.com](https://www.wolfram.com/wolfram-u/)
对于数据科学家来说,在“数据科学&统计学”部分有一些非常非常好的课程和视频。这里的主要课程“多层面数据科学”是一门在线课程,包含带解说的视频、带代码的笔记本,可用于临时编码区:
(图片由作者提供)
每个部分都有一个小测验,所以你可以很容易地测试自己。
在这一点上,我建议注册一个免费的基本 Wolfram 云。这使您能够在学习语言的同时进行疯狂的编程实验:
[## 沃尔夫拉姆云
Wolfram 语言的云访问。
www.wolframcloud.com](https://www.wolframcloud.com/)
另一个选择是为开发者下载免费的 Wolfram 引擎。这使您可以访问本地机器上的核心语言。它不包括漂亮的 Wolfram 笔记本接口,但是你可以把这个引擎挂在 Jupyter 笔记本接口上。
此外,许多人已经为各种 ide 开发了插件,这些插件都列在开发人员的主资源页面上:
Wolfram 技术堆栈使您能够在几分钟内开发和部署一个有用的应用程序,并构建…
www.wolfram.com](https://www.wolfram.com/developer/)
如果你想体验完整的 Wolfram 笔记本界面(而且我强烈推荐这个!)您可以从 Wolfram 桌面试用版开始,此处提供:
云+桌面上的高级混合计算平台。利用以下各项的全部功能,让您的发展更进一步
www.wolfram.com](https://www.wolfram.com/wolfram-one/)
最后,在 Wolfram 语言中保持高效的最佳长期资源是广泛的参考文档,它们被深度集成到笔记本中。至少有两个很棒的用户社区,你可以在那里提问。首先是 Wolfram 社区,这不仅是一个提问的好地方,也是一个与他人分享你的工作的好地方。其次,Mathematica&Wolfram Language Stack Exchange网站非常适合提问(和回答)问题。
(图片由作者提供)
我希望这对您有用,并祝您在下一个数据科学项目中好运!
学习 Wolfram:与 Web 浏览器交互
自动化您的 Web 测试和浏览工作流
杰克·范德斯波尔在 Unsplash 上的照片
当我在质量保证部门工作时,我的工作之一是在一些网站上做回归测试。那时只有有限的自动化工具,所以我的解决方案包含了发布鼠标和按键事件的 Java 机器人类。这当然非常脆弱。
最近,自动化 web 浏览器交互变得更加容易。WebDriver 协议指定了一组非常好的命令和交互来与浏览器对话。Wolfram 语言(WL)使用这个协议让你直接从一个笔记本会话中控制浏览器。笔记本的交互特性非常适合增量开发 web 自动化工作流。在这个故事中,我将向您展示如何开始使用这个功能。
要启动一个新的 web 浏览器会话,您可以使用 StartWebSession 命令。如果没有函数参数,它将在你的电脑上启动一个 Chrome 浏览器。也支持 Firefox 浏览器。
(图片由作者提供)
要打开网页,您可以使用 WebExecute 命令。此命令是您用来控制 web 浏览器的主要功能。您指定会话,并给它一个命令,如“open 网页”、“ClickElement”或“JavascriptExecute”:
(图片由作者提供)
例如,要单击搜索图标(右上角的放大镜),您可以通过它的 XPath 或 CSS 选择器来引用它。你可以在浏览器的开发者控制台找到这些。一个写得好的 web 页面为特定的元素使用惟一的 ID,在这种情况下,ID 是“_nav-search”。执行该命令会在浏览器中显示搜索栏:
(图片由作者提供)
同样,您可以在输入栏中键入文本。例如,如果我想搜索“数据集”,我可以指定元素和搜索文本。通过在字符串末尾添加一个“\n”(换行符)来模拟按 enter 键:
(图片由作者提供)
为了检索和检查页面内容,我通常使用一小段 javascript。例如,检索页面上所有链接的 URL 的一种方法是,我使用下面的 javascript 代码片段:
(图片由作者提供)
结果是一个 WL 字符串列表,该列表可用于回归测试或进一步的导航步骤。同样,您可以收集网页上所有图像的 URL,然后将这些图像直接导入到您的 WL 会话中以供进一步检查。
当您完成浏览器会话时,您可以使用 DeleteObject 命令来结束它。这将关闭 web 浏览器并结束连接:
有关这一有用功能的更多信息,请查看 Web 浏览器自动化的指南页面。要了解更多关于 WL 的入门知识,请查看我最近的一篇名为“学习 Wolfram:从零到英雄”的文章。上面图片中显示的完整代码的笔记本可以从这里获得。只需点击该链接,然后将笔记本下载到您的桌面上。
学习 Wolfram:增强机器学习项目的资源
使用 Wolfram 改善您的工作流程和工作效率
照片由 Cookie 在 Unsplash 上的 Pom 拍摄
《纽约客》上一幅著名的漫画曾调侃道:“在互联网上,没人知道你是一只狗”。这个笑话背后的想法是,在互联网上,任何人都可以伪装成任何人,并且很难验证某人就是他们所说的那个人。
但那幅漫画发表于 1993 年,远在人工智能和机器学习算法出现之前。今天,这幅漫画的标题可能是“在互联网上,没有人知道你是一台电脑”,电脑盯着屏幕:我们与互联网服务和应用程序的交互如此之多,以至于我们经常无法立即分辨我们是在和一个真人还是一个机器人说话。
位于所有这些人工智能核心的机器学习算法是复杂的数值优化,旨在生成极有可能的输出(数字、文本、图像、语音等)。)基于某些输入(还有数字、文本、图像、语音等)。)
一个好的机器学习框架提供了这些类别的算法,但是一个伟大的机器学习框架也使它易于学习、易于使用和易于开发。有很多好的机器学习框架(也许太多了?)在那里,任何人都可以通过大量的努力,对它们进行编程,以慢慢获得有用的结果。但不幸的是,没有太多伟大的机器学习框架。
Wolfram 语言确实提供了一个很好的机器学习框架,因为它易于学习和使用,而且——因为它是基于 Apache MXNet 的——非常适合研究和开发。
适用于各种应用的自动化、最先进的机器学习…
www.wolfram.com](https://www.wolfram.com/featureset/machine-learning/)
Wolfram 语言本身非常容易学习,因为它丰富的笔记本界面鼓励交互式实验。要了解更多关于学习 Wolfram 语言的基础知识,请查看我最近的帖子:
通过这一快速介绍,快速启动您的数据科学能力
towardsdatascience.com](/learning-wolfram-from-zero-to-hero-2ac4fd6914d9)
接下来,为了学习如何使用机器学习框架进行开发, WolframU 学习资源网站有大量的视频教程和 mooc来帮助你入门。这些教程和工作流程,以及包含数千个真实示例的大量参考文档,使学习和使用变得非常容易:
Wolfram 语言包括广泛的最先进的集成机器学习能力,从高度…
reference.wolfram.com](https://reference.wolfram.com/language/guide/MachineLearning.html)
Wolfram 机器学习框架的另一个主要特征是包含了 Wolfram 神经网络库,这是一个预先训练好并可立即使用的神经网络的大型库。这意味着您可以用不到一行代码访问大量高性能的神经网络:
(图片由作者提供)
最后,因为这个框架是建立在 Apache 的 MXNet 之上的,所以它有很好的性能。对来自 Nvidia 的 GPU 硬件的支持是内置的,即使在多个 GPU 板上也可以快速训练。
最好的开始方式是使用 Wolfram|One 平台进行尝试,该平台包括此处描述的一切,包括访问 Wolfram 云。
云+桌面上的高级混合计算平台。利用以下各项的全部功能,让您的发展更进一步
www.wolfram.com](https://www.wolfram.com/wolfram-one/)
使用 Wolfram technologies 会给你带来巨大的竞争优势,你的秘密开发者酱,或者像我喜欢想的那样:“在互联网上,没有人知道你在使用 Wolfram”。
图片由作者根据 Cookie 在 Unsplash 上的 Pom 照片制作
学习 Wolfram:使用时间序列
一个关于美国煤炭生产的计算思维故事
作者在 Unsplash 上使用paweczerwiński的照片制作的图片
在世界范围内,煤炭正被其他形式的能源慢慢取代,以帮助减少地球大气中的温室气体。在美国,美国能源信息署(EIA)收集能源信息,如一段时间内的煤炭产量。这个故事使用这个时间序列数据来说明 Wolfram 语言中的一些时间序列功能。
开始之前,我们需要访问 EIA 的数据。我写了一个名为UnitedStatesCoalProduction的简单函数来访问他们的数据 API,并按地区导入煤炭产量数据:
UnitedStatesCoalProduction = ResourceFunction[
"user:arnoudb/DeployedResources/Function/UnitedStatesCoalProduction"]
调用这个函数有两个参数。第一个参数使用类似“KY”的代码来指定区域。第二个论据是 API key 可以从 EIA 网站获取。如何操作的详细信息记录在功能参考文件页。调用该函数的典型方法如下:
kentucky = UnitedStatesCoalProduction["KY",key]
这将返回一个 TimeSeries 对象。在 Wolfram 笔记本中,总是大量使用排版显示格式,它用一个摘要框来格式化:
(图片由作者提供)
时序对象可直接用于可视化功能,如日期列表图:
(图片由作者提供)
时序对象可以直接用于常见的统计函数。请注意所有煤炭产量数字是如何与其单位(短吨)一起存储的。跟踪单位总是有用的,尤其是在处理多个数据集和不同单位时:
In[.]:= Mean[kentucky]Out[.]= Quantity[9.53609*10^7, "ShortTons"]
让我们来看另一个数据集,伊利诺伊州的:
illinois = UnitedStatesCoalProduction["IL", key]
我们可以将这两个数据集绘制在一起,并用清晰的标签来修饰这个图。我将 y 轴换算成百万短吨,让人类更容易读懂:
DateListPlot[
{kentucky, illinois}/10^6,
PlotLegends -> {"Kentucky", "Illinois"},
FrameLabel -> {"Year", "Short Tons (millions)"}
]
(图片由作者提供)
显然,肯塔基州的煤炭产量下降了很多,而伊利诺伊州在过去十年中实际上增加了产量。
Wolfram 语言中的时序对象处理基本的数学运算,如加、减、除和乘。例如,为了比较肯塔基州和伊利诺伊州的煤炭产量比率,简单地划分时间序列:
DateListPlot[kentucky / illinois,
FrameLabel -> {"Year", "Ratio Kentucky vs Illinois"},
GridLines -> Automatic
]
(图片由作者提供)
时间序列可以在其他时间间隔重新采样。当您需要比较两个具有不相容间隔的时间序列时,这可能会很有用。对时间序列进行重采样可以使数据标准化。以下示例显示了如何以三个月的时间间隔进行重新采样。
TimeSeriesResample[kentucky, Quantity[3, "Months"]]
(图片由作者提供)
要提取具有开始和结束日期/时间的时间序列的一部分,可以使用 TimeSeriesWindow 函数。例如,这将提取 2005–2010 年的时间序列数据:
TimeSeriesWindow[kentucky, {
DateObject[{2005}, "Year"],
DateObject[{2010}, "Year"]
}]
(图片由作者提供)
有关时序操作相关函数的完整列表,您可以阅读时序处理文档指南页面。
要制作一个类似于本文顶部所示的非常漂亮的可视化图形,您可以使用我之前关于如何装扮您的数据可视化的文章中的提示和技巧。
us = UnitedStatesCoalProduction["US", key];
DateListPlot[us/10^6,
PlotRangePadding -> None, PlotRange -> {0, 1400},
PlotStyle -> Directive[White, AbsoluteThickness[5]],
PlotLabel -> Style["United States Total Coal Production (2000-2020)", "Subsection"],
FrameLabel -> {"Year", "Short Tons (millions)"},
ImageSize -> Large, Prolog -> Inset[image, Center, Center, Scaled[{1.2, 1.2}]]]
作者在 Unsplash 上使用了paweczerwiński的照片
离开学术界进入工业界,优化你的学习方式
苹果 | 谷歌 | SPOTIFY | 其他 | 剪辑
伊恩·哈洛在 TDS 播客
编者按:迈向数据科学播客的“攀登数据科学阶梯”系列由 Jeremie Harris 主持。Jeremie 帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:
我不记得有多少次我忘记了一些重要的事情。
不过,我确信这是经常发生的事情:我经常忘记宝贵的生活经验、技术概念和有用的统计理论。更糟糕的是,我经常在努力学习之后忘记这些事情,所以我的健忘完全是浪费时间和精力。
这就是为什么我抓住机会与 Cerego 的科学副总裁 Iain Harlow 聊天的原因,Cerego 是一家通过优化信息提供方式来帮助企业为员工建立培训课程的公司,以最大限度地保留和学习成果。
Iain 非常了解学习,并就如何优化自己的学习提出了一些很好的见解,但他在解决数据科学问题和聘用数据科学家方面也有很多专业知识,这是他在 Cerego 工作中关注的两件事。他也是学术界的资深人士,并分享了一些关于学术界研究和工业界研究之间差异的有趣观察。
我们的谈话涵盖了很多领域,但以下是我最喜欢的一些要点:
- 当你忘记某件事时,通常并不是你的大脑失去了曾经拥有的关于那件事的信息。相反,我们容易忘记事情,因为我们的大脑不记得我们在寻找的信息存储在哪里。打个计算机科学的比方,遗忘有点像失去了指向包含我们试图检索的信息的内存地址的指针:数据存储在某个地方,只是我们想不起在哪里。
- 这种观点的一个含义是,一旦学会了信息,练习检索信息比大多数人认为的更重要。虽然这与直觉相反,但定期测试你的知识与一开始就学习它一样重要,因为它迫使你练习回忆。
- 学术界的生活和工业界的生活最大的区别之一就是批评。在学术界,所有的想法都倾向于被怀疑和批判,而在工业界,大多数人会认为他们周围的人知道他们在做什么。虽然这可以在短期内让事情变得更令人愉快,但这也意味着行业数据科学家、分析师或机器学习工程师有更多的责任来确保他们的工作检查无误。
- 产品直觉真的很重要,它通常伴随着专业知识。你对你的客户、你的用户群或你的问题空间了解得越多,你就越能更好地识别你的模型成功所需的特征。
- 因此,公司非常关心你培养产品意识的能力。最好的方法是通过个人项目,使用独特或不寻常的数据集,迫使你问这样的问题,“我能从这些数据中获得什么价值?”
- 求职者通常认为面试就像是大学考试,被设计成一系列“抓住你”的问题。在现实中,面试官通常利用工作面试来筛选解决问题的能力,而不在乎“得分”表现。这似乎是一个微妙的区别,但它很重要:这意味着在许多情况下,你的分析和沟通能力比原始的技术能力更重要。因此,实践你的数据故事和产品直觉是一个很大的优势。
夹子
你可以在 Twitter 上关注伊恩,你也可以在 Twitter 上关注我在这里。
你也可以在 Twitter 、脸书或 LinkedIn 上查看 Cerego。
我们正在寻找能与我们的观众分享有价值的东西的客人。如果你碰巧认识一个合适的人选,请在这里告诉我们:publication@towardsdatascience.com。
战争的教训:作为一名军事分析家我学到了什么
作为一名军事数据分析师,我学到了什么,你如何应用它,以及我为什么离开。
我在美国海军做了 6 年的情报专家,在阿富汗和非洲服过预备役和现役。军事情报训练是学习分析推理、数据分析以及如何发展向大群人展示你的发现的能力的最佳途径之一。
我们被教导成为顶级分析师所需要的一切。以下是我一直铭记在心的三条重要经验。这些经验适用于数据分析领域的所有人。
坐在后面手里拿着 kindle 的是我。
分析师的三个教训
第一课:人可能会死
在军事领域,如果你犯了一个错误,有人可能会死。所以,你最好确保你的信息是正确的。在平民世界里,赌注没有那么高,但这个教训是正确的。这可能不是一个人的生命受到威胁,但它可能是一笔重要的销售交易或一次成功的营销活动。仔细检查所有东西。
如何申请:
- **精神饱满地回来:**完成项目后,去散散步或分散注意力,这样你就能精神饱满地回来。当我们在一个项目上工作时,我们的大脑开始疲劳,我们更容易错过小错误。
- **询问好友:**在分享你的项目之前,把它带给你的好友,询问他们的意见。让他们尽可能批判性地评估它——如果你能给他们一个提示列表就更好了——比如“我的视觉效果清楚吗?”或者“你能仔细检查我的数学吗?”
- **检查你的资料来源:**对数据来源持批评态度。探索他们的方法,并确保您传达的是来自该数据的正确信息。
第二课:没人在乎
注意力是有限的。在军事领域,当我给海军上将或舰长们做报告时,我能看出他们的心思在别处。他们在考虑下一个任务。你组织中的首席执行官和其他领导人也在做同样的事情。他们在考虑下一笔交易或收到的最后一封电子邮件。**你必须能够吸引并保持利益相关者的注意力。**你需要让的人关心。
如何申请:
- 让他们震惊:当你开始交流你的数据分析时,你应该总是以这样的问题开始:“他们不知道什么?”并以此为线索。你希望能够震撼听众,让他们全神贯注于你。如果你用他们已经熟悉的信息来引导,你最终可能会失去他们。
- 当利益相关者提问时,如果你不知道答案,你可以说:“我会研究一下,然后再给你答复。”这有两个目的——吸引利益相关者的注意力,因为他们希望在未来与你交谈;防止你的陈述偏离轨道。
- **让它变得别致:**你的图表需要漂亮。花额外的时间学习如何有效地可视化您的数据。想想你的颜色,以及它们是如何搭配的。了解最佳数据可视化实践。我强烈推荐用 Dat a 讲故事作为学习数据可视化最佳实践的良好起点。
第三课:那是什么鬼东西
你一定很好奇。在军事分析领域,我们不断地查看数据并相互询问“这是什么?为什么会这样?这是什么意思?”—我们需要理解数据中更奇怪的部分。在平民世界里,当你在数据中看到一些东西,你会说,“那到底是什么?”不要就此打住——挖到你知道为止。
如何申请:
- **大声说出来:**当你在数据集中遇到异常时,比如地图中的异常值或异常点,你不应该隐瞒。深入探究,试着理解它是如何发生的。询问这对您的其他数据意味着什么。
- **讲故事:**数据在给你讲故事,你只需要找到它。当回顾数据时,实际上写下你开始看到的故事。在军事领域,这是一份报告。在平民世界,它可能是一个单独的文件,详细说明您的发现。
- **承认自己的无知:**有时候你只需要承认自己不知道。带着你的问题或疑问去找一个值得信赖的顾问或同事,请他们帮忙解决这个难题。
为什么我决定离开
作为一名军事分析家有巨大的压力。人们期望我们做到完美,因为错误的后果可能会导致生命损失。在我们的分析和陈述中,我们不能马虎。我们需要确保自己无可指责。当你面临如此多的风险时,它会从你的工作和生活中吸取快乐。我发现自己在应对焦虑。我变成了一个不健康的完美主义者。我对工作失去了热情。
随着我与军方的合同即将结束,我面临着一个艰难的选择。我应该在这个已经成功的世界里继续吗?或者我应该离开并可能失败?
我对自己的军事记录非常自豪。我获得了三枚军用航空奖章,和我的机组人员一起被提名参加著名的 T2 麦凯奖。我所做的工作拯救了许多人的生命,让我真正感觉到我在为这个世界做出有意义的贡献。
最终,我决定离开军队,加入平民世界,这样我就可以重拾对数据分析的热情。
下一步是什么?
我目前正在为 WeWork 学习分析,我发现让我在军队中取得成功的经验也适用于我目前的职业生涯。我希望我对数据分析的热情能够为个人和公司解决现实世界的挑战。
如果你想听更多,想看我的旅程, 和我在 twitter 联系。
激活、卷积和池化—第 1 部分
FAU 讲座笔记深度学习
经典激活
FAU 大学的深度学习。下图 CC BY 4.0 来自深度学习讲座
这些是 FAU 的 YouTube 讲座 深度学习 的讲义。这是与幻灯片匹配的讲座视频&的完整抄本。我们希望,你喜欢这个视频一样多。当然,这份抄本是用深度学习技术在很大程度上自动创建的,只进行了少量的手动修改。如果你发现了错误,请告诉我们!
航行
欢迎回到深度学习!所以在今天的讲座中,我们想谈谈激活和卷积神经网络。我们把它分成几个部分,第一部分是关于经典激活函数。后面会讲到卷积神经网络,池化,之类的。
人造神经元和它们的生物对应物有一些微弱的相似之处。 CC 下的图片来自深度学习讲座的 4.0 。
让我们从激活功能开始,你可以看到激活功能可以追溯到生物动机。我们记得到目前为止我们所做的一切,不知何故,我们也以生物学的实现为动机。我们看到生物神经元通过突触与其他神经元相连。这样,他们实际上可以互相交流。突触有髓鞘,有了髓鞘,它们实际上可以电绝缘。它们能够与其他细胞交流。
当超过某个阈值时,生物神经元就会激活。 CC 下的图片来自深度学习讲座的 4.0 。
当他们交流时,他们不仅仅是发送他们收到的所有信息。他们有一个选择机制。因此,如果你有一个刺激,它实际上不足以产生一个输出信号。总信号必须高于阈值,然后发生的是动作电位被触发。之后,它重新极化,然后回到静止状态。有趣的是,细胞被激活的程度有多强并不重要。它总是返回相同的动作电位,回到静止状态。
轴突的详细视图。来自深度学习讲座的 CC BY 4.0 下的图片。
实际的生物激活甚至更复杂。你有不同的轴突,它们与其他神经元的突触相连。在路径上,它们被许旺细胞覆盖,然后许旺细胞可以将这种动作电位传递给下一个突触。有离子通道,它们实际上用于稳定整个电过程,并在激活脉冲后使整个事情再次进入平衡。
生物神经元观察综述。 CC 下的图片来自深度学习讲座的 4.0 。
所以,我们可以看到知识本质上存在于神经元之间的连接中。我们有抑制性和兴奋性连接。突触在解剖学上加强了前馈处理。所以,这和我们目前看到的非常相似。然而,这些连接可以是任何方向的。因此,它们也可以形成循环,你有完整的神经元网络,它们与不同的轴突相连,以形成不同的认知功能。关键是激活的总数。只有当激活的总数超过阈值时,你才会真正的激活。这些激活是具有特定强度的电尖峰,老实说,整个系统也是时间相关的。因此,它们也随时间对整个信息进行编码。所以,不只是我们有一个单一的事件通过,而是整个过程以一定的频率运行。这使得整个处理过程能够持续一段时间。
符号函数不适合梯度下降。 CC 下的图片来自深度学习讲座的 4.0 。
到目前为止,人工神经网络中的激活都是非线性激活函数,主要由通用函数逼近来驱动。所以如果我们没有非线性,我们就不能得到一个强大的网络。如果没有非线性,我们只能以矩阵乘法结束。所以与生物学相比,我们有一些符号函数可以模拟全有或全无的反应。通常,我们的激活没有时间成分。也许,这可以通过符号函数的激活强度来建模。当然,这在数学上也是不可取的,因为正弦函数的导数在任何地方都是零,除了在无穷远处的零点。所以这绝对不适合反向传播。因此,我们一直使用 sigmoid 函数,因为我们可以计算解析导数。现在的问题是“我们能做得更好吗?”。
线性激活在建模方面没有太大帮助,但是它们在数学上很容易掌握。来自深度学习讲座的 CC BY 4.0 下的图片。
那么,我们来看看一些激活函数。我们能想到的最简单的方法是线性激活,我们只是复制输入。我们可能希望用某个参数α对其进行缩放,然后得到输出。如果我们这样做了,我们就得到了α的导数。这非常简单,它会将整个优化过程转化为一个凸问题。如果我们不引入任何非线性,我们基本上就陷入了矩阵乘法。因此,我们在这里列出它只是为了完整性。它不允许你建立我们所知的深度神经网络。
sigmoid 函数是可微分的,并且可以用于神经网络建模。 CC 下的图片来自深度学习讲座的 4.0 。
现在,sigmoid 函数是我们开始的第一个函数。它本质上具有朝向 1 和 0 的饱和度。因此,它有一个非常好的概率输出。然而,当 x 值变得很大或很小时,它会饱和。你可以看到,导数已经在 3 或-3 左右,很快接近零。另一个问题是它不是以零为中心的。
Sigmoid 激活将输出分布移向正值。来自深度学习讲座的 CC BY 4.0 下的图片。
零点对中的问题是,你总是产生正数,与你得到的输入无关。您的输出将总是正的,因为我们映射到 0 和 1 之间的值。这意味着,如果我们有一个零均值的信号作为激活函数的输入,它将总是向一个大于零的均值移动。这被称为连续层的内部协变量移位。因此,各层必须不断适应不断变化的分布。因此,批量学习减少了更新的方差。
tanh 激活解决了内部协变量移位的问题。 CC 下的图片来自深度学习讲座的 4.0 。
那么我们能做些什么来弥补这一点呢?嗯,我们可以用其他的激活功能,一个非常流行的是 tangens 双曲线。这里用蓝色显示。你可以看到它有非常好的特性。例如,它以零为中心,乐存从 1991 年就开始使用。所以,你可以说它是 sigmoid 函数的一个移位版本。但是一个主要的问题仍然存在。这就是饱和。所以你可以看到,也许在 2 或-2,你已经看到导数非常接近于零。所以还是会造成渐变消失的问题。
消失和爆炸梯度给训练网络带来了很大的问题。 CC 下的图片来自深度学习讲座的 4.0 。
嗯,现在这里的本质是 x 如何影响 y 。我们的 sigmoid 和双曲线正切,它们将 x 的大区域映射到 y 的非常小的区域。因此,这意味着 x 的大变化将导致 y 的小变化。所以梯度消失了。这个问题被反向传播放大了,在反向传播中,你有很多非常小的步骤。如果它们接近于零,并且你将这些更新步骤彼此相乘,你会得到一个指数衰减。网络建立得越深,梯度消失得越快,更新下层就越困难。所以,一个非常相关的问题当然是爆炸梯度。所以,这里我们有一个问题,我们有高价值,这些高价值互相放大。结果,我们得到一个爆炸梯度。
不仅η的选择对训练网络至关重要。来自深度学习讲座的 4.0CC 下的图片。
因此,我们可以考虑一下我们之前见过的反馈环路和控制器。我们已经看到,如果您在训练迭代中测量损失,您的损失曲线会发生什么。如果你不适当地调整学习率,你会得到爆炸梯度或消失梯度。但不仅仅是学习率η。这个问题也可能被激活函数放大。特别地,消失梯度是那些饱和激活函数出现的问题。所以我们可能想考虑一下,看看我们是否能得到更好的激活函数。
在这个深度学习讲座中,更多令人兴奋的事情即将到来。 CC 下的图片来自深度学习讲座的 4.0 。
所以,下一次深度学习,我们将确切地看看这些激活功能。你今天所看到的基本上是经典的,在下一节课,我们将讨论为了建立更稳定的激活函数所做的改进。这将使我们能够进入真正的深层网络。非常感谢您的收听,下次再见!
如果你喜欢这篇文章,你可以在这里找到更多的文章,在这里找到更多关于机器学习的教育材料,或者看看我们的深度 学习 讲座。如果你想在未来了解更多的文章、视频和研究,我也会很感激你在 YouTube、Twitter、脸书、LinkedIn 或 T21 上的掌声或关注。本文以 Creative Commons 4.0 归属许可发布,如果引用,可以转载和修改。
参考
[1] I. J. Goodfellow、d . ward-Farley、M. Mirza 等人,“最大网络”。载于:ArXiv 电子版(2013 年 2 月)。arXiv:1302.4389[统计 ML】。
[2],,,,任等,“深入挖掘整流器:在 ImageNet 分类上超越人类水平的表现”。载于:CoRR abs/1502.01852 (2015)。arXiv: 1502.01852。
[3]君特·克兰鲍尔(Günter Klambauer),托马斯·安特辛纳(Thomas Unterthiner),安德烈亚斯·迈尔(Andreas Mayr),等,“自归一化神经网络”。在:神经信息处理系统的进展。abs/1706.02515 卷。2017.arXiv: 1706.02515。
【四】、和水城颜。“网络中的网络”。载于:CoRR abs/1312.4400 (2013 年)。arXiv: 1312.4400。
[5]安德鲁·马斯、奥尼·汉南和安德鲁·吴。“整流器非线性改善神经网络声学模型”。在:过程中。ICML。第 30 卷。1.2013.
[6] Prajit Ramachandran,Barret Zoph,和 Quoc V. Le。“搜索激活功能”。载于:CoRR abs/1710.05941 (2017 年)。arXiv: 1710.05941。
【7】Stefan elf wing,内池英治,多亚贤治。“强化学习中神经网络函数逼近的 Sigmoid 加权线性单元”。载于:arXiv 预印本 arXiv:1702.03118 (2017)。
[8]克里斯蒂安·塞格迪、、·贾等,“用回旋深化”。载于:CoRR abs/1409.4842 (2014 年)。arXiv: 1409.4842。
激活、卷积和池化—第 2 部分
FAU 讲座笔记深度学习
现代激活
FAU 大学的深度学习。下图 CC BY 4.0 来自深度学习讲座
这些是 FAU 的 YouTube 讲座 深度学习 的讲义。这是与幻灯片匹配的讲座视频&的完整抄本。我们希望,你喜欢这个视频一样多。当然,这份抄本是用深度学习技术在很大程度上自动创建的,只进行了少量的手动修改。如果你发现了错误,请告诉我们!
航行
整流线性单元(ReLU)是最早的现代激活之一。 CC 下的图片来自深度学习讲座的 4.0 。
欢迎回到激活函数和卷积神经网络的第 2 部分!现在,我们想继续讨论激活函数和深度学习中使用的新函数。最著名的例子之一是整流线性单元(ReLU)。现在 ReLU,我们前面已经遇到过了,它的思想是简单地将负半空间设置为 0,将正半空间设置为 x,这就导致了整个正半空间的导数为 1,而其他地方的导数为 0。这很好,因为这样我们得到了一个很好的概括。由于分段线性,有一个显著的加速。该函数可以很快求值,因为我们不需要在实现方面通常有点慢的指数函数。我们没有这个消失梯度的问题,因为这个函数的导数有很大的高值区域。缺点是它不是以零为中心的。现在,这还没有用整流线性单元解决。不管怎样,这是一大进步。有了 ReLUs,你可以第一次建立更深层次的网络,更深层次的网络,我指的是隐藏层多于三层的网络。
经典的机器学习总是使用三个隐藏层。用imgflip.com创造的迷因。
通常在经典的机器学习中,神经网络被限制在大约三层,因为在这一点上你已经得到了消失梯度问题。较低的层从未看到任何梯度,因此从未更新它们的权重。因此,ReLUs 实现了深度网络的训练,而无需无监督的预训练。你已经可以建立深度网络,但你必须进行无人监督的预训练,有了 ReLUs,你可以直接从零开始训练,只需把你的数据放进去,这是一大进步。此外,实现非常容易,因为如果单元被激活,一阶导数为 1,否则为 0。所以没有二阶效应。
一个大问题是死亡关系。来自深度学习讲座的 CC BY 4.0 下的图片。
还有一个问题:垂死的瑞卢斯。如果你的权重和偏差被训练成对 x 产生负的结果,那么你最终只会得到一个零导数。ReLU 总是产生一个零输出,这意味着它们不再对你的训练过程有所贡献。所以,它就停在这一点,因为 0 阶导数,不可能更新。这个宝贵的 ReLU 突然总是 0,再也无法训练了。如果你的学习率太高,这种情况也会经常发生。在这里,你可能要小心设置学习率。有几种方法可以解决这个问题,我们将在接下来的几个视频中讨论。
泄漏的 ReLUs 有助于避免死亡 ReLUs。 CC 下的图片来自深度学习讲座的 4.0 。
缓解这个问题的一个方法是不仅使用一个 ReLU,而且使用一种叫做泄漏或参数 ReLU 的东西。这里的方法是,不要将负半空间设置为零,而是将其设置为一个小数值。所以,你用α乘以 x,把α设为一个小数字。然后,你有一个与 ReLU 非常相似的效果,但你不会以垂死的 ReLU 问题结束,因为导数从来不是零,而是α。我首先通常将值设置为 0.01。参数 ReLU 是进一步的扩展。在这里,你使α成为一个可训练的参数。所以你可以知道每个激活函数应该有多大。
指数线性单位。来自深度学习讲座的 CC BY 4.0 下的图片。
也有指数线性单位,这里的想法是,你在负半空间上找到一个缓慢衰减的光滑函数。你可以看到,我们把它设为α乘以 x 的指数减 1。这就产生了导数 1 和α指数 x,这也是获得饱和效应的一种有趣方式。这里,我们没有消失梯度,它也减少了激活的变化,因为我们也可以得到负输出值。
比例指数线性单位旨在保持 0 均值和 1 方差。 CC 下的图片来自深度学习讲座的 4.0 。
如果您选择指数线性单位的这种变体,您将增加一个额外的标度λ。如果您的输入具有零均值和单位方差,您可以选择α和λ以及此处报告的这两个值,它们将保持零均值和单位方差。因此,这种比例指数函数(SELU)也摆脱了内部协变量移位的问题。所以这是 ReLU 的另一个变体,好的性质是,如果你有这些零均值单位方差输入,那么你的激活将保持在相同的规模。你不必关心内部协变量的变化。关于内部协变量移位,我们可以做的另一件事是巴赫归一化。这是我们将在几个视频中讨论的内容。
更多激活功能。 CC 下的图片来自深度学习讲座的 4.0 。
好的,还有哪些激活功能?有学习激活功能的 maxout。可以使用径向基函数。有一种 softplus,它是 1 的对数加上 e 的 x 次方,被发现不如 ReLU 有效。
我们完成新的激活功能了吗?用 imgflip.com创造的迷因。
这真的越来越荒谬了,不是吗?那么,我们现在应该用什么呢?人们甚至走得更远,试图找到最佳的激活函数。为了找到它们,他们使用了强化学习搜索。
寻找最佳激活确实是计算密集型的。来自深度学习讲座的 4.0CC 下的图片。
我们将在后面的课程中讨论强化学习。我们在这里只讨论结果。这种强化学习类型的设置的一个问题是它在计算上非常昂贵。在强化训练程序的每一步,你都必须从头开始训练整个网络。所以,你需要一台超级计算机来做这样的事情,搜索激活函数就是参考文献[6]。谷歌已经在 2017 年发布了它,他们实际上做到了这一点。因此,策略是定义搜索空间,然后使用带有强化学习的递归神经网络执行搜索,最后,他们希望使用最佳结果。
参考文献[6]中使用的搜索空间。 CC 下的图片来自深度学习讲座的 4.0 。
他们使用的搜索空间是,他们把 x 放入一些一元函数中。然后这些一元函数使用一个二进制核心单元组合起来。然后,这可以再次与 x 的另一个实例一元合并,然后使用二元函数产生最终输出。所以,这基本上是他们所经历的搜索空间。当然,他们采用这种模型,因为你可以用这种表达式解释很多典型的激活函数,比如 sigmoid,等等。
谷歌发现的“新”激活功能。来自深度学习讲座的 CC BY 4.0 下的图片。
我们看到这些是他们认为有用的激活功能。所以,我们不能自己做手术,但是我们当然可以看看他们的结果。这里有一些例子,有趣的是,你可以看到他们得到的一些结果,甚至不再是凸函数。他得出的一个普遍结论是,复杂的激活功能通常表现不佳。他们发现了 x 乘以σ(β x)的东西,他们称之为 swish 函数。因此,这似乎表现得很好,实际上他们使用搜索确定的这个函数之前已经被提议为 sigmoid 加权线性单元[7]。
在搜索激活时是否发现任何显著的差异? CC 下的图片来自深度学习讲座的 4.0 。
所以让我们来详细看看结果。现在声明:永远不要在你的结果中显示表格。努力寻找更好的代表!然而,我们没有找到更好的代表。这里我可以展示的是,这些是他们获得的最高精度。这是在 ImageNet 上训练的 inception Resnet v2 架构中完成的。在倒数第三行,您可以看到 ReLU 的结果,然后下面两行显示 swish-1 和 swish 的结果。现在,我们想问的问题是:“这些变化实际上意义重大吗?”所以,显著性意味着你要计算观察结果是随机结果的概率,你要确保你报告的结果不是随机的。在整个处理链中,我们经常会遇到随机初始化,我们有很多步骤可能会引入采样误差,等等。所以你真的要确保结果不是随机的。因此,我们有显著性检验。如果你仔细观察,你真的会问自己:“这里报道的变化实际上有意义吗?”。
我们对激活函数的观察总结。 CC 下的图片来自深度学习讲座的 4.0 。
因此,我们的建议是去 ReLU。它们工作得非常好,如果您有一些问题,您可以选择使用批处理规范化,我们将在下一个视频中讨论。另一个有趣的事情是缩放的指数线性单元,因为它具有这种自适应属性。所以,这真的很吸引人,但是先试试 ReLU 吧。这真的是你想走的典型道路。对最佳激活函数的搜索是一个困难且广泛的优化问题,它并没有给我们带来更好的激活函数。所以我们这里已经有的足够解决你的大部分问题了。关于好的激活函数,我们所知道的是我们从这些观察中所知道的:它们具有几乎线性的区域以防止梯度消失,它们具有饱和区域以提供非线性,并且它们应该是单调的,因为这对我们的优化非常有用。
在这个深度学习讲座中,更多令人兴奋的事情即将到来。来自深度学习讲座的 CC BY 4.0 下的图片。
这已经把我们带到了下节课的预习。在下一堂课中,我们真的想研究卷积神经网络,看看我们如何减少连接数量和参数数量,以构建真正深度的网络。所以,我希望你喜欢这个讲座,我期待着在下一个讲座中见到你!
如果你喜欢这篇文章,你可以在这里找到更多的文章,或者看看我们的讲座。如果你想在未来了解更多的文章、视频和研究,我也会很感激你在 YouTube、Twitter、脸书、LinkedIn 上的鼓掌或关注。本文以 Creative Commons 4.0 归属许可发布,如果引用,可以转载和修改。
参考
[1] I. J. Goodfellow、d . ward-Farley、M. Mirza 等人,“最大网络”。载于:ArXiv 电子版(2013 年 2 月)。arXiv:1302.4389[统计 ML】。
[2],,,,任等,“深入挖掘整流器:在 ImageNet 分类上超越人类水平的表现”。载于:CoRR abs/1502.01852 (2015)。arXiv: 1502.01852。
[3]君特·克兰鲍尔(Günter Klambauer),托马斯·安特辛纳(Thomas Unterthiner),安德烈亚斯·迈尔(Andreas Mayr),等,“自归一化神经网络”。在:神经信息处理系统的进展。abs/1706.02515 卷。2017.arXiv: 1706.02515。
【四】、和水城颜。“网络中的网络”。载于:CoRR abs/1312.4400 (2013 年)。arXiv: 1312.4400。
[5]安德鲁·马斯、奥尼·汉南和安德鲁·吴。“整流器非线性改善神经网络声学模型”。在:过程中。ICML。第 30 卷。1.2013.
[6] Prajit Ramachandran,Barret Zoph,和 Quoc V. Le。“搜索激活功能”。载于:CoRR abs/1710.05941 (2017 年)。arXiv: 1710.05941。
【7】Stefan elf wing,内池英治,多亚贤治。“强化学习中神经网络函数逼近的 Sigmoid 加权线性单元”。载于:arXiv 预印本 arXiv:1702.03118 (2017)。
[8]克里斯蒂安·塞格迪、、·贾等,“用回旋深化”。载于:CoRR abs/1409.4842 (2014 年)。arXiv: 1409.4842。
激活、卷积和池化—第 3 部分
FAU 讲座笔记深度学习
卷积层
FAU 大学的深度学习。下图 CC BY 4.0 来自深度学习讲座
这些是 FAU 的 YouTube 讲座 深度学习 的讲义。这是与幻灯片匹配的讲座视频&的完整抄本。我们希望,你喜欢这个视频一样多。当然,这份抄本是用深度学习技术在很大程度上自动创建的,只进行了少量的手动修改。如果你发现了错误,请告诉我们!
航行
欢迎回到深度学习!今天,我们想继续讨论卷积神经网络。在这个讲座中,我们真正想看到的是构建深层网络的基石。所以我们今天要学习的是卷积神经网络,这是深度网络最重要的组成部分之一。
到目前为止,我们所有的层是完全连接的。 CC 下的图片来自深度学习讲座的 4.0 。
到目前为止,我们已经有了这些完全连接的层,其中每个输入都连接到每个节点。这是非常强大的,因为它可以表示输入之间的任何线性关系。本质上在每一层之间,我们有一个矩阵乘法。这实质上意味着从一层到另一层,我们可以有一个完整的表现变化。这也意味着我们有很多联系。
图像具有高维数,这对神经网络是一个挑战。 CC 下的图片来自深度学习讲座的 4.0 。
所以让我们想想图像,视频,声音,机器学习。然后,这是一个缺点,因为他们通常有巨大的输入大小。您需要考虑如何处理这些大的输入量。让我们说,我们假设我们有一个 512 乘以 512 像素的图像,这意味着一个具有八个神经元的隐藏层已经具有 512 个^ 2 + 1,用于偏差乘以 8 的可训练权重。仅一个隐藏层就有超过 200 万个可训练权重。当然,这不是办法,规模确实是个问题。还有更多。
机器学习的一个典型案例:给猫和狗分类。来自深度学习讲座的 CC BY 4.0 下的图片。
假设我们想在猫和狗之间进行分类。如果你看这两张图片,你会发现这些图片的大部分都是空白区域。因此,它们不是很相关,因为像素通常是非常糟糕的特征。它们高度相关,依赖于尺度,并有强度变化。因此,它们是一个巨大的问题,从机器学习的角度来看,像素是一种糟糕的表示。你想创造一些更抽象的东西,更好地总结信息。
像素不是我们作为特征的首选。 CC 下的图片来自深度学习讲座的 4.0 。
所以,问题是:“我们能找到一个更好的代表吗?”在图像中,我们当然有一定程度的局部性。因此,我们可以尝试在不同的位置找到相同的宏功能,然后重用它们。理想情况下,我们希望构建类似于特征层次的东西,其中我们有边和角,然后形成眼睛。然后,我们有眼睛、鼻子和耳朵组成一张脸,然后脸、身体和腿将最终形成一只动物。所以,构图很重要,如果你能学会更好的表达,那么你也能更好地分类。
好的特征能够描述一个组合层次。 CC 下的图片来自深度学习讲座的 4.0 。
所以这真的很关键,我们在卷积神经网络中经常看到的是,在早期的层上可以找到非常简单的描述符。然后,在中间层,你会发现更多的抽象表示。在这里,我们发现眼睛、鼻子等等。在更高的层次,你会发现真正的受体,比如这里的脸。因此,我们希望具有本地敏感性,但我们希望将它们扩展到整个网络,以便对这些抽象层进行建模。我们可以通过在神经网络中使用卷积来做到这一点。
为了对特征层次进行建模,我们以交替的方式组合卷积和下采样/池。来自深度学习讲座的 CC BY 4.0 下的图片。
这是这些架构的总体思路。他们不是将一切与一切完全联系起来,而是为每个神经元使用一个所谓的感受野,就像一个过滤器内核。然后,他们在整个图像上计算相同的权重——本质上是卷积——并产生不同的所谓特征图。接下来,要素地图将转到池化图层。然后,汇集试图引入抽象,并缩小图像。接下来,我们可以再次进行卷积和合并,然后进入下一阶段。你继续下去,直到你有一些抽象表示,然后抽象表示被送到一个完全连接的层。最终,这个完全连接的层映射到最终的类,即“汽车”、“卡车”、“货车”等等。这就是分类结果。因此,我们需要卷积层、激活函数和池来获得抽象并降低维度。在最后一层,我们找到完全连接的分类。
卷积利用图像的局部结构。 CC 下的图片来自深度学习讲座的 4.0 。
让我们从卷积层开始。因此,这里的想法是,我们希望通过仅连接邻域中的像素来利用空间结构。然后,这可以在全连接层中表示,除非我们想要在全连接层中表示,否则我们可以将矩阵中的每个条目设置为零,除非它们通过本地连接来连接。这意味着我们可以忽略空间距离上的很多联系。另一个技巧是你使用大小为 3 x 3,5 x 5 和 7 x 7 的滤镜,你希望它们在整个图层上都是一样的。因此,小邻域内的权重是相同的,即使你移动它们。他们被称为捆绑或共享的重量。如果你这样做,你实际上是在模拟一个卷积。如果你有相同的权重,那么这和你在任何图像处理课上学到的过滤器模板的概念是完全一样的,所以,我们本质上在这里构建了具有可训练过滤器模板的网络。
动画显示了过滤遮罩如何在输入(底部)上移动以产生最终的要素地图(顶部)。来自深度学习讲座的 CC BY 4.0 下的图片。
这里,我们看到了这个过程的放大图。因此,如果你上过信号处理课,卷积本质上可以表示为对两个函数的积分,其中一个函数对另一个函数进行移位,然后对最终结果进行积分。
信号处理中的卷积。 CC 下的图片来自深度学习讲座的 4.0 。
互相关是一个相关概念,您会看到互相关和卷积之间的唯一区别是τ的符号。在卷积中,你向负方向移动,在互相关中,你向正方向移动。我们经常看到的是,人们谈论卷积,但他们实际上实现了互相关。所以他们基本上翻转了面具的方向。所以,如果你正在创造一些可以训练的东西,这实际上并不重要,因为你无论如何都会学习这个函数的符号。所以,两种实现都很好。互相关实际上经常在实际的深度学习软件中实现。通常,您无论如何都会随机初始化权重。因此,差异没有影响。
填充解决了边界处缺少观察值的问题。来自深度学习讲座的 CC BY 4.0 下的图像。
我们需要讨论的另一件事是不同的输入大小,以及卷积实际上是如何被用来处理的。因此,这个感受野意味着输出实际上更小,因为我们只能接近最接近的边界。所以,如果你想计算你的感受野边界上的卷积核,你实际上可以到达视野之外。处理这种情况的一种方法是减小要素图和下一个相应图层的大小。您也可以使用填充。许多人所做的只是零填充。因此,所有未观察到的值实际上都设置为零,然后您可以保持相同的大小,只需卷积整个图像。还有其他策略,如镜像等,但零填充可能是最常见的一种。
使用多通道卷积的前向通过。来自深度学习讲座的 CC BY 4.0 下的图像。
这样,你得到了前进的路径。你实际上不必用小的卷积核来实现它。您也可以使用傅立叶变换来实际执行卷积。所以,你有一个二维输入图像。假设这是一个多通道图像,例如 S 是颜色的数量。然后,应用三维滤镜。这里可以看到,在空间域中有卷积核,但在 S 方向上,它与通道完全相连。如果你这样做了,你可以应用这个内核,然后你得到一个输出遮罩(蓝色显示),内核显示在这里。然后,我们在这里返回这个输出字段。现在,如果你有另一个内核,那么你可以用同样的填充产生第二个遮罩,如绿色所示。这样,您就可以开始构建一个又一个的要素地图。
卷积只是矩阵乘法的一种。 CC 下的图片来自深度学习讲座的 4.0 。
让我们谈一谈卷积实现和反向传递。卷积表示为矩阵乘法 W 和 W 是一个托普利兹矩阵。因此,这个 Toeplitz 矩阵是一个循环矩阵,因为它是通过权重共享构造的。这意味着,如果您实际上正在构建这个矩阵,那么在被训练的每一行中,您具有本质上相同数量的权重,但是它们被移动了一个,这仅仅是因为它们在每一行中使用相同的权重来计算不同的本地字段。这给了我们一个循环矩阵,这意味着我们停留在矩阵乘法的领域。因此,我们的卷积也可以实现为矩阵乘法,因此我们简单地继承了与全连接层相同的公式。因此,如果我们想要反向传播误差,只需用 W 乘以反向传播的输入误差。如果您想要计算权重的更新,它实际上是误差项乘以我们从正向转置的输入中获得的值。因此,这与我们之前看到的完全连接图层的更新公式完全相同。这很好,没有那么多需要记住的。当然,你必须确保你能正确地分担重量,我们会在练习中展示给你看。对我们来说,现在,我们可以把它当作矩阵乘法,你在练习中看到的一个有趣的事情是,反向传递也可以表示为卷积。
卷积使我们能够显著减少权重的数量,并处理任意大小的图像。 CC 下的图片来自深度学习讲座的 4.0 。
现在,我们从卷积层中获得了什么?如果现在堆叠多个滤波器,我们就可以得到一个可训练的滤波器组。比方说,我们有八个过滤器,产生八个节点和一个 5x5 的邻域。然后,我们突然有了 5 乘 8 = 200 的权重。200 磅比我们之前见过的 200 万磅要小得多。此外,卷积可以独立于图像大小应用。所以,我们能做的就是用这些滤镜来转换任何类型的图像。这意味着当我们有不同的输入量时,我们产生的激活图也会改变。我们会在下一节课中看到更多。这里非常好的一点是,我们有更多的数据来训练单个重量。
使用步进和扩张卷积,各种有趣的处理步骤都是可能的。使用 gifify 生成的图像。点击查看完整视频。
也有像交错回旋这样的东西。这是当你试图将池化机制和降维机制结合到卷积中的时候。就像每个点都跳过一步。因此,对于步距 s,我们描述一个偏移,然后我们本质上产生一个激活图,该激活图具有依赖于该步距的较低维度。
使用 s = 2 跨越卷积。来自深度学习讲座的 4.0CC 下的图片。
因此,我们将输出大小减少了 1/s,因为跳过了这么多步骤,从数学上来说,这就是同时进行卷积和二次采样。我们这里有一个小动画,向你展示这是如何实现的。
扩张的卷积通过在输入域中跳跃来增加卷积的感受野。 CC 下的图片来自深度学习讲座的 4.0 。
也有像扩张我们萎缩的脑回这样的事情。这里的想法是,我们不是在增加步幅,而是在增加输入空间的间距。现在,感受野不再连接,但我们看到的是分布在一个邻域内的单个像素。这就给了我们一个参数更少的更宽的感受域。
1x1 卷积支持按像素完全连接的层。 CC 下的图片来自深度学习讲座的 4.0 。
另一个非常有趣的概念是 1x1 卷积。到目前为止,我们有这些邻域的 H 滤波器,在深度方向上,我们有 s,记住,它们在深度方向上是完全连接的。这也很有趣,因为现在如果你有一个 1x1 卷积,那么这本质上是一个完全连接的层。所以你只需要在一维上有一个完全连接的层,然后你可以输入任意的数据。它所做的是在通道的方向上计算数量减少的特征图,因为在那里我们有完整的连接。本质上,我们现在可以进行通道压缩。使用 1x1 卷积,我们可以将输入展平到一维,并将所有内容映射到通道方向。因此,1x1 卷积是完全连接的层。因此,如果我们以适当的方式安排输出,我们基本上也可以用它们来表达完全连接的层的整个概念。
在[4]中介绍了 1x1 卷积。来自深度学习讲座的 CC BY 4.0 下的图片。
这是在网络论文[4]中首次描述的,我们在讨论不同的架构时也会谈到。这些 1x1 卷积减小了网络的大小,特别是压缩了信道,并且它本质上学习了维数减少。因此,它们帮助您减少特征空间中的冗余。等效的,但更灵活的当然是 NxN 卷积。
在这个深度学习讲座中,更多令人兴奋的事情即将到来。 CC 下的图片来自深度学习讲座的 4.0 。
所以下一次在深度学习中,我们将讨论池机制以及如何减少特征图的大小,而不是使用卷积步长或 atrous 卷积。你也可以在我们将在下节课讨论的池化步骤中对此进行显式建模。非常感谢大家的聆听,下节课再见!
如果你喜欢这篇文章,你可以在这里找到更多的文章,或者看看我们的讲座。如果你想在未来了解更多的文章、视频和研究,我也会很感激你在 YouTube、Twitter、脸书、LinkedIn 上的鼓掌或关注。本文以 Creative Commons 4.0 归属许可发布,如果引用,可以转载和修改。
参考
[1] I. J. Goodfellow、d . ward-Farley、M. Mirza 等人,“最大网络”。载于:ArXiv 电子版(2013 年 2 月)。arXiv:1302.4389[统计 ML】。
[2],,,,任等,“深入挖掘整流器:在 ImageNet 分类上超越人类水平的表现”。载于:CoRR abs/1502.01852 (2015)。arXiv: 1502.01852。
[3]君特·克兰鲍尔(Günter Klambauer),托马斯·安特辛纳(Thomas Unterthiner),安德烈亚斯·迈尔(Andreas Mayr),等,“自归一化神经网络”。在:神经信息处理系统的进展。abs/1706.02515 卷。2017.arXiv: 1706.02515。
【四】、和水城颜。“网络中的网络”。载于:CoRR abs/1312.4400 (2013 年)。arXiv: 1312.4400。
[5]安德鲁·马斯、奥尼·汉南和安德鲁·吴。“整流器非线性改善神经网络声学模型”。在:过程中。ICML。第 30 卷。1.2013.
[6] Prajit Ramachandran,Barret Zoph,和 Quoc V. Le。“搜索激活功能”。载于:CoRR abs/1710.05941 (2017 年)。arXiv: 1710.05941。
【7】Stefan elf wing,内池英治,多亚贤治。“强化学习中神经网络函数逼近的 Sigmoid 加权线性单元”。载于:arXiv 预印本 arXiv:1702.03118 (2017)。
[8]克里斯蒂安·塞格迪、、·贾等,“用回旋深化”。载于:CoRR abs/1409.4842 (2014 年)。arXiv: 1409.4842。
前馈网络—第 1 部分
FAU 讲座笔记深度学习
我们为什么需要深度学习?
FAU 大学的深度学习。来自深度学习讲座的 CC BY 4.0 下的图片。
这些是 FAU 的 YouTube 讲座 深度学习 的讲义。这是与幻灯片匹配的讲座视频&的完整抄本。我们希望,你喜欢这个视频一样多。当然,这份抄本是用深度学习技术在很大程度上自动创建的,只进行了少量的手动修改。如果你发现了错误,请告诉我们!
航行
这堂课的大纲。 CC 下的图片来自深度学习讲座的 4.0 。
欢迎大家参加我们的深度学习讲座!今天,我们要深入这个话题。我们想介绍一些对该领域至关重要的重要概念和理论。今天的主题是前馈网络,前馈网络本质上是我们今天使用的神经网络的主要配置。所以在接下来的几个视频中,我们想谈谈第一批模型和它们背后的一些想法。我们也介绍一些理论。一个重要的模块是关于通用函数逼近,我们将从本质上说明神经网络能够逼近任何类型的函数。随后将引入 softmax 功能和一些激活。最后,我们想谈一谈如何优化这些参数,特别是反向传播算法。
感知器描述了一个线性决策边界。 CC 下的图片来自深度学习讲座的 4.0 。
让我们从模型开始,你已经听到的是感知器。我们已经讨论过这个,它本质上是一个函数,将任何高维输入映射到权重向量和输入的内积。然后,我们只对计算出的带符号距离感兴趣。你可以从本质上理解这一点,就像你在右边看到的那样。决策边界以红色显示,您用内积计算的实际上是新样本到决策边界的带符号距离。如果我们只考虑标志,我们就能决定我们是站在一边还是另一边。
经典的模式识别流水线。来自深度学习讲座的 CC BY 4.0 下的图片。
现在,如果你看看经典的模式识别和机器学习,我们仍然会遵循所谓的模式识别管道。我们对一些测量值进行了转换和预处理,以提高质量,例如降低噪声。在预处理中,我们基本上停留在与输入相同的域中。因此,如果您有一个图像作为输入,预处理的输出也将是一个图像,但对于分类任务可能具有更好的属性。然后,我们要做特征提取。你还记得苹果和梨的例子。从这些特征中,我们提取特征,然后产生一些高维向量空间。然后,我们可以继续进行分类。
感知器不能解决非线性可分问题,如逻辑异或任务。 CC 下的图片来自深度学习讲座的 4.0 。
现在,我们在感知器中看到的是,我们能够模拟线性决策边界。这立即导致了感知机不能解决逻辑异或(即所谓的异或)的观察。你可以在左边看到上面 XOR 问题的可视化。想象一下,你有一些类的分布,左上角和右下角是蓝色的,另一个类是左下角和右上角的。这是受逻辑异或函数的启发。你将不能用一个单一的线性决策边界来分离这两个点云。所以,你要么需要曲线,要么使用多条线。用一个单一的感知器,你将无法解决这个问题。因为人们一直在争论:“看,我们可以用感知器来模拟逻辑功能。如果我们在感知器的基础上构建感知器,我们基本上可以构建所有的逻辑!”
2004 年,人工智能冬季项目的资金被大幅削减.图片来自 Giphy 。
现在,如果你不能构建 XOR,那么你很可能无法描述整个逻辑,因此,我们永远不会实现强大的人工智能。这是一段时间,人工智能研究的所有资金都被大幅削减,人们将不会获得任何新的拨款。他们不会得到资金来支持这项研究。因此,这一时期被称为“艾冬天”。
从感知机到多层感知机。来自深度学习讲座的 4.0CC 下的图片。
随着多层感知器的引入,事情发生了变化。这是感知器的扩展。你不只是做一个单一的神经元,而是使用多个这样的神经元,并把它们排列成层。这里你可以看到一个非常简单的草稿。所以,它非常类似于感知器。你基本上有一些输入和权重。现在,你可以看到,它不仅仅是一个简单的总和,我们还有几个非线性的总和。然后,它们再次分配权重并再次汇总,以进入另一个非线性。
深层网络将许多层排列在彼此之上。 CC 下的图片来自深度学习讲座的 4.0 。
这非常有趣,因为我们可以使用多个神经元。我们现在还可以模拟非线性决策边界。你可以继续,然后把这个分层排列。所以你通常做的是,你有一些输入层。这是我们的向量 x。然后,你有几个感知器,你安排在隐藏层。它们被称为隐藏的,因为它们不会立即观察到输入。他们分配权重,然后计算一些东西,只有在最后,在输出端,你又有一个层,你可以观察实际发生了什么。所有这些隐藏层之间的重量,都是无法直接观察到的。在这里,只有当你输入一些信息,计算激活时,你才能观察到它们,然后在最后,你可以获得输出。所以,在这里你可以观察到你的系统中发生了什么。
泛逼近允许我们学习紧集上的任何连续函数 f( x )。来自深度学习讲座的 4.0CC 下的图片。
现在,我们将研究所谓的通用函数逼近器。这实际上只是一个只有一个隐藏层的网络。通用函数逼近是一个基本的理论,因为它告诉我们,用一个单一的隐藏层,我们可以逼近任何连续函数。那么,让我们深入研究一下这个定理。它从一个正式的定义开始。我们有一些𝜑(x)和𝜑(x)是非常数的,有界的,单调递增的函数。存在一些大于零的𝜀,并且对于定义在某个高维空间ℝᵐ的紧子集上的任何连续函数 f( x ),存在整数和实常数𝜈和 b,以及实向量 w ,在这里可以找到近似。在这里,你现在可以看到近似值是如何计算的。你有一个输入加上一些偏差的权重的内积。这进入一些激活函数𝜑(x).这是一个非常数、有界、单调递增的函数。然后你有另一个使用这些𝜈的线性组合,然后产生输出资本 F( x )。所以 F( x )是我们的近似值,近似值是由线性组合计算出的非线性的线性组合。如果你这样定义,你可以证明,如果你从真函数 F( x )中 f( x ),两者之间的绝对差由一个常数𝜀.限定𝜀大于零。
我们真的需要深度学习吗?图片来自knowyourmeme.com。
这已经是非常有用的近似值了。有一个上限𝜀,但现在它没有告诉我们𝜀实际上有多大。所以,𝜀可能真的很大。通用逼近定理也告诉我们,如果我们增加 n,那么𝜀下降。如果你用 n 趋近于无穷大,𝜀会趋近于零。所以,我们在这个隐藏层中采用的神经元越多,我们的逼近就越好。这意味着,我们可以用一个隐藏层来逼近任何函数。所以你可能会说,如果你可以用一个层来近似所有的东西,为什么人们要做深度学习呢?
分类树可以细分ℝᵐ.的任何分区来自深度学习讲座的 4.0CC 下的图片。
如果一层就够了,深度学习就没有任何意义。我刚刚向你证明了这一点。所以可能不需要深度学习?让我们看一些例子:我在这里取了一个分类树,分类树是一种细分空间的方法。这里我举一个二维例子,我们有一些输入空间 x₁和 x₂.这很有用,因为我们可以在幻灯片上非常有效地将其可视化。我们的决策树做以下事情:它决定 x₁是否大于 0.5。请注意,我在右边展示的是决策边界。在下一个节点,如果你走到左边,你看着 x₂,决定它是大于还是小于 0.25。在另一边,你简单地再看一下 x₁,决定它是大于还是小于 0.75。现在,如果你这样做了,你可以在叶节点中分配类。在这些叶子中,你现在可以,例如,赋值 0 或 1,这给出了这个地方的一个细分,它具有镜像 l 的形状。
尝试将分类树转换为单个隐藏层网络。 CC 下的图片来自深度学习讲座的 4.0 。
这是一个函数,这个函数现在可以用一个通用函数近似器来近似。所以让我们试着去做。我们可以把它变成一个网络。让我们使用下面的想法:我们的网络有 2 个输入神经元,因为它是一个二维空间。有了我们的决策边界,我们也可以形成这些决策 x₁大于或小于 0.5。所以,我们可以立即采用这个。我们实际上也可以采用所有其他内部节点。因为我们在这个例子中使用了一个 sigmoid,所以我们也使用了内部节点的逆节点,并将它们作为额外的神经元放入。当然,在这里我不需要学习任何东西,因为对于第一个隐藏层的连接,我可以从树的定义中得到它们。它们已经预先定义好了,所以这里不需要学习。在输出端,我必须学习一些权重,这可以通过使用最小二乘近似来完成,然后我可以直接计算这些权重。
在每个节点中创建的输入空间的空间细分的可视化。我们无法使用这种单层网络找到精确的解决方案。来自深度学习讲座的 CC BY 4.0 下的图片。
如果我真的这样做了,我们也可以找到一个很好的可视化。你可以看到,通过我们的决策边界,我们实际上是在隐藏层中构建一个基础。你可以看到,如果我用 0 和 1 作为黑白,对于每个隐藏节点,我都在构造一个基向量。然后,它们基本上被线性加权以用于输出。你可以这样做,把每个像素乘以每个像素,然后简单地求和。这就是隐藏层的作用。然后,我主要对组合这些空间向量感兴趣,这样它将产生期望的 y,现在,如果我在最小二乘意义上这样做,我得到右边的近似值。所以还算不错。我把这个放大了一点。这就是我们想要的。这是镜像的 L,这是我刚刚提出的近似值。现在,你可以看到它有点像 l 形,但这里的值在[0,1]之间,我的六神经元近似的𝜀可能在 0.7 的范围内。所以它确实有点作用,但是近似值不是很好。在这种特殊的配置中,你必须大大增加神经元的数量,以降低误差,因为这是一个非常困难的问题。几乎无法近似。
使用两层,任何分类树都可以映射到神经网络,而不会丢失信息。 CC 下的图片来自深度学习讲座的 4.0 。
那么,我们还能做什么?好吧,如果我们想要这样,我们可以,例如,增加第二个非线性。然后,我们会得到我们想要的解决方案。所以你可以看到,也许一层在表现方面不是很有效率。有一种算法可以将任何决策树映射到神经网络上。算法如下:你取所有的内部节点,这里是 0.5,0.25 和 0.75 之间的决定。这些是内部节点,然后你把它们适当地连接起来。你把它们连接起来,这样你就能准确地形成子区域。这里你可以看到这是我们的 L 形,为了构建左上角的区域,我们需要访问第一个决策。它将空间分为左半空间和右半空间。接下来,我们可以访问第二个决策。这样,我们可以使用这两个决定,以便在左上角形成这个小补丁。对于从决策边界出现的所有四个补丁,我们基本上得到一个节点。这仅仅意味着对于每个叶节点,我们在第二层得到一个节点。因此,第一层中的每个内部节点一个节点,第二层中的每个叶节点一个节点。然后,在输出中组合它们。在这里,你甚至不需要计算任何东西,因为我们已经知道为了达到正确的决策界限,这些必须如何合并。通过这种方式,我们设法将你的决策树转换成神经网络,它会按照我们希望的那样进行正确的逼近。
我们反复出现的座右铭:我们需要更深入!图片来自knowyourmeme.com。
我们从这个例子中学到了什么?我们可以用一个只有一个隐藏层的通用函数逼近器来逼近任何函数。但是如果我们深入研究,我们可能会发现一个更有效的问题分解。所以这里的分解首先是内部节点,然后是叶节点。这使我们能够推导出一个只有七个节点的算法,并且可以精确地近似这个问题。所以你可以说,通过建立更深的网络,你增加了额外的步骤。在每一步中,你都试图简化功能和表达的力量,这样你最终能更好地处理决策。
在这个深度学习讲座中,更多令人兴奋的事情即将到来。来自深度学习讲座的 CC BY 4.0 下的图片。
现在,让我们回到我们的通用函数逼近定理。所以,我们已经看到了它的存在。它告诉我们,我们可以用一个隐藏层来逼近一切。这已经是一个很酷的观察结果,但是它没有告诉我们如何选择 n,也没有告诉我们如何训练。所以通用逼近定理有很多问题。这就是我们为什么要进行深度学习的本质原因。然后,我们可以建立系统,通过不同的步骤开始解开表象。如果我们这样做,我们可以建立更高效、更强大的系统,并对它们进行端到端的培训。这是我们走向深度学习的主要原因。我希望任何从事深度学习的人都知道通用近似,以及为什么深度学习实际上有意义。好了,今天就到这里。下一次,我们将讨论激活函数,并在下一组视频中开始介绍反向传播算法。敬请关注!我希望你喜欢这个视频。期待在下一部中见到你!
如果你喜欢这篇文章,你可以在这里找到更多的文章,或者看看我们的讲座。如果你想在未来了解更多的文章、视频和研究,我也会很感激你在 YouTube、Twitter、脸书、LinkedIn 上的鼓掌或关注。本文以 Creative Commons 4.0 归属许可发布,如果引用,可以转载和修改。
参考
[1] R. O .杜达,P. E .哈特和 D. G .斯托克。模式分类。约翰威利父子公司,2000 年。
[2]克里斯托弗·m·毕晓普。模式识别和机器学习(信息科学和统计学)。美国新泽西州 Secaucus 出版社:纽约斯普林格出版社,2006 年。
[3]f·罗森布拉特。"感知器:大脑中信息存储和组织的概率模型."摘自:《心理评论》65.6 (1958),第 386-408 页。
[4] WS。麦卡洛克和 w .皮茨。"对神经活动中固有思想的逻辑演算."发表于:数学生物物理学通报 5 (1943),第 99-115 页。
[5]d . e .鲁梅尔哈特、G. E .辛顿和 R. J .威廉斯。"通过反向传播误差学习表征."载于:自然 323 (1986),第 533-536 页。
[6] Xavier Glorot,Antoine Bordes,Yoshua Bengio。“深度稀疏整流器神经网络”。《第十四届国际人工智能会议论文集》第 15 卷。2011 年,第 315-323 页。
[7]威廉·h·普雷斯、索尔·a·特乌考斯基、威廉·t·维特林等《数值计算方法》第三版:科学计算的艺术。第三版。美国纽约州纽约市:剑桥大学出版社,2007 年。