TowardsDataScience-博客中文翻译-2020-九十七-

龙哥盟 / 2024-10-14 / 原文

TowardsDataScience 博客中文翻译 2020(九十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

Python 堆栈框架和尾部调用优化

原文:https://towardsdatascience.com/python-stack-frames-and-tail-call-optimization-4d0ea55b0542?source=collection_archive---------10-----------------------

使用尾递归避免 Python 中的堆栈溢出

计算机科学中的递归是一种解决问题的方法,在这种方法中,函数从自己的代码中调用自己。这种方法非常有用,可以应用于许多类型的问题,但是,它有一个限制。函数使用堆栈来保存它们的局部变量,堆栈的大小是有限的。因此,如果递归太深,最终会耗尽堆栈空间,这被称为堆栈溢出。然而,一些编译器实现了尾调用优化,允许无限递归发生而不会出现堆栈溢出。在本文中,我将首先讨论 Python 堆栈框架,然后解释尾调用优化的概念,并向您展示如何在 Python 中实现它。

堆叠帧

堆栈是一种采用 LIFO(后进先出)顺序的数据结构,它有两个主要操作:

  • push:将一个元素添加到堆栈中
  • 弹出:删除最近添加的元素

因此,添加或推入堆栈的最后一个元素是要移除或弹出的第一个元素。使用堆栈存储数据的优点是内存是为您管理的。从堆栈中读取和写入非常快,但是堆栈的大小是有限的。Python 解释器使用调用栈来运行 Python 程序。当在 Python 中调用一个函数时,一个新的被推送到调用栈上用于它的本地执行,并且每次函数调用返回时,它的帧被弹出调用栈。程序运行的模块有最底层的框架,称为全局框架模块框架。这些帧保存了函数本地执行所需的数据,即函数参数及其本地变量。

例如,考虑以下源代码:

# Listing 1
a = 1
def f(x):
    b = 2
    return b+x
y = f(a)
print(y)

运行该程序的模块在调用堆栈中有一个最底层的框架,即模块框架。全局变量a存储在该帧中。当我们调用f(a)时,f的一个新框架被推到调用栈顶,并且f的参数和它的局部变量b被存储在那里。当f返回它的值时,这个帧被弹出调用堆栈(图 1)。

图 1

在 Python 中,可以很容易地访问这些帧。Python 将调用栈中每个帧的所有信息存储在一个 frame 对象中。frame 对象包含帧地址、它所属文件的名称、源代码中访问该帧的行号以及它所属函数的名称。我们可以使用inspect模块来检查帧。

我们可以使用inspect.**stack(context=1)**来获取当前存在于调用堆栈中的所有帧。可选参数context指定要返回的代码 上下文的行数,其中一些是源代码中以当前行为中心的代码行。当前行是源代码中访问或检查该帧的行,当前行号是源代码中该行的编号。当程序执行时,调用栈中的帧是动态变化的,知道栈在哪一行被检查是很重要的。功能:

inspect.**stack(context=1)**

返回名为元组的列表:

FrameInfo(frame, filename, lineno, function, code_context, index)

其中frame是框架对象,filename是该框架所属文件的名称,lineo是源代码中的当前行号,function是该框架所属函数的名称,code_context是源代码中上下文行的列表,index是该列表中当前行的索引。

现在我们来试试这个函数。如果你跑步:

>>>import inspect
>>>inspect.stack()

在 Python 控制台中,您将获得:

[FrameInfo(frame=<frame at 0x000001A97248EAD8, file ‘<stdin>’, line 1, code <module>>, filename=’<stdin>’, lineno=1, function=’<module>’, code_context=None, index=None)]

所以返回的列表只有一个名为 tuple 的FrameInfo元素。因为我们在模块级,所以我们在调用栈上只有模块级,并且这个列表只有一个元素对应于模块框架。元组的第一个元素是帧对象:

<frame at 0x000001A97248EAD8, file ‘<stdin>’, line 1, code <module>>

帧地址在那里,文件名是stdin。因为我们只有一行代码,所以该帧是在第 1 行被访问的,很明显该帧属于<module>。值得注意的是,如果您从像 Anaconda 这样的 Python 平台上运行这段代码,您可能会得到更长的输出。您会注意到在模块框架下有更多的框架,这些是 Anaconda 自己正在执行的模块,我们不需要担心它们。Python 控制台为code_context返回None,为index。要获得它们的值,您应该在终端中将这段代码作为 Python 脚本运行。例如,您可以在 Python 脚本中存储以下代码:

import inspect
print(inspect.stack())

并在终端中运行它。输出将是:

[FrameInfo(frame=<frame at 0x000002A7EEBFE9F8, file ‘t1.py’, line 2, code <module>>, filename=’t1.py’, lineno=2, function=’<module>’, code_context=[‘print(inspect.stack())\n’], index=0)]

由于stack()中参数context的默认值是 1,所以只显示检查该帧的代码行及其索引也是0

如前所述,该列表的第一个元素与调用堆栈顶部的当前框架相关。我们可以通过inspect.stack()[0]访问它。Inspect模块也为此提供了特定的功能:

inspect.**currentframe**()

它给出了列表中第一个元素(当前帧)的 frame 对象。所以相当于inspect.stack()[0][0]

现在让我们在清单 1 的源代码中尝试这个函数:

# Listing 2
import inspect
a = 1
print(inspect.stack()[0], "\n")
def f(x):
    b=2
    print(inspect.stack()[0:2], "\n")
    return x+b
y = f(a)
print(inspect.stack(3)[0])
print(y)

输出将是:

FrameInfo(frame=<frame at 0x000001FDB52AC908, file '<ipython-input-34-3128af912523>', line 3, code <module>>, filename='<ipython-input-34-3128af912523>', lineno=3, function='<module>', code_context=['print(inspect.stack()[0], "\\n")\n'], index=0) 

[FrameInfo(frame=<frame at 0x000001FDB520E1C8, file '<ipython-input-34-3128af912523>', line 6, code f>, filename='<ipython-input-34-3128af912523>', lineno=6, function='f', code_context=['    print(inspect.stack()[0:2], "\\n")\n'], index=0), FrameInfo(frame=<frame at 0x000001FDB50C4938, file '<ipython-input-34-3128af912523>', line 8, code <module>>, filename='<ipython-input-34-3128af912523>', lineno=8, function='<module>', code_context=['y = f(a)\n'], index=0)] 

FrameInfo(frame=<frame at 0x000001FDB52AFD88, file '<ipython-input-34-3128af912523>', line 9, code <module>>, filename='<ipython-input-34-3128af912523>', lineno=9, function='<module>', code_context=['y = f(a)\n', 'print(inspect.stack(3)[0])\n', 'print(y)\n'], index=1)
3

最初,我们在模块级添加了inspect.stack()[0](因为我在 Anaconda 中运行它,所以我只获得了返回列表的一部分,没有显示模块框架下的 Anaconda 框架)。inspect.stack()返回的列表头显示当前帧的FrameInfo元组,当前帧是模块帧。

接下来,我们在函数f中添加了print(inspect.stack()[0:2], "\n")。当f被调用时,f的一个新框架被推到模块框架的顶部,因此显示在列表顶部的当前框架对应于该功能:

[FrameInfo(frame=<frame at 0x00000240610678E0, file '<ipython-input-237-6d513e166f46>', line 7, code f>, filename='<ipython-input-237-6d513e166f46>', lineno=7, function='f', code_context=['    print(inspect.stack()[0:2], "\\n")\n'], index=0), FrameInfo(frame=<frame at 0x00000240610B2378, file '<ipython-input-237-6d513e166f46>', line 9, code <module>>, filename='<ipython-input-237-6d513e166f46>', lineno=9, function='<module>', code_context=['y = f(a)\n'], index=0)]

框架对象和FrameInfo都显示f的名称。这个框架下面是模块框架,所以它是列表的下一个元素。

最后,我们放置print(inspect.stack(3)[0])以在调用函数后获取当前帧的FrameInfo。现在函数框架从调用堆栈中弹出,所以当前框架又是模块框架。这里我们用了stack()中的context=3。所以输出是:

FrameInfo(frame=<frame at 0x000002405E787678, file '<ipython-input-237-6d513e166f46>', line 10, code <module>>, filename='<ipython-input-237-6d513e166f46>', lineno=10, function='<module>', code_context=['y = f(a)\n', 'print(inspect.stack(3)[0])\n', 'print(y)\n'], index=1)

现在我们有 3 行代码用于代码上下文,带有index=1的中间一行是检查当前帧的代码。

框架对象属性

框架对象有一些有用的属性,这里我们只讨论其中的一部分(你可以查看[inspect](https://docs.python.org/3/library/inspect.html) 模块网页查看全部):

f_back:这个属性给出了下一个外部框架对象(它是这个框架的调用者,在调用堆栈中位于它的下面)

f_code:该帧中正在执行的代码对象

f_locals:这个框架看到的本地命名空间。它是一个字典,给出了这个框架中存在的所有局部变量的名称和值。这包括该帧所属的函数的自变量)。如果这是模块框架,那么所有的全局变量都将包含在内。

例如,如果我们将清单 2 改为:

# Listing 3
a = 1
def f(x):
    b=2
    print(inspect.currentframe().f_code, "\n")
    print(inspect.currentframe().f_back, "\n")
    print(inspect.currentframe().f_locals)
    return x+b
y = f(a)

我们得到:

<code object f at 0x00000240610A4D20, file "<ipython-input-236-6a2fc4ce42af>", line 3> 

<frame at 0x000002405E556C28, file '<ipython-input-236-6a2fc4ce42af>', line 9, code <module>> 

{'x': 1, 'b': 2}

所以f_code返回f的代码对象,f_back返回它的调用方框架,也就是模块框架。最后,f_locals返回一个字典,其中包含函数的参数(x)及其本地值b以及它们在帧被检查时的值。

现在使用这些属性,我将定义一个新的函数print_frames来打印帧的局部范围:

该函数获取FrameInfo元组列表(由inspect.stack()返回)并打印当前帧及其下所有帧的局部范围,直到它到达模块帧,我们将使用该函数在接下来的部分中检查这些帧。下面是使用该函数的一个示例:

# Listing 4
a = 1
def f(x):
    b=2
    print_frames(inspect.stack())
    return b*x
def g(x):
    return 2*f(x)
y = g(a)------------------------------------------------------------
Output:
  [Frame 2 'f': {'x': 1, 'b': 2}]
  [Frame 1 'g': {'x': 1}]
  [Frame '<module>']

如你所见,当g调用f时,当前帧属于f,而gmodule的帧在它的下面。

在闭包中堆叠框架

如清单 4 所示,当一个函数调用另一个函数时,它的框架保持在被调用函数的框架之下。对于闭包来说,这是不正确的。闭包是由外部函数返回的内部函数。它还有一个扩展的范围,包含了外部函数的非局部变量(你可以参考这篇文章来了解更多关于闭包的知识)。所以当一个闭包被调用时,调用者函数的框架已经从调用栈中弹出。让我给你举个例子:

# Listing 5
def f(x):
    def g(y):
        def h(z):
            print_frames(inspect.stack())
            return x+z
        print_frames(inspect.stack())
        return h
    print_frames(inspect.stack())
    return g

这里我们在f中有两个闭包定义。现在如果我们写:

f(1)(2)(3)

输出将是:

 [Frame 1 'f': {'g': <function f.<locals>.g at 0x00000240610617B8>, 'x': 1}]
  [Frame '<module>'] [Frame 1 'g': {'y': 2, 'h': <function f.<locals>.g.<locals>.h at 0x00000240610F1158>, 'x': 1}]
  [Frame '<module>'] [Frame 1 'h': {'z': 3, 'x': 1}]
  [Frame '<module>']4

所以当f返回g时,f的框架被弹出调用堆栈,而g的框架被推到module框架的顶部。g的框架不仅存储其参数(y),还存储x,它是f的参数,也是g的一个非局部变量。类似地,当调用h时,g的帧已经被弹出调用堆栈。h的框架也存储了非局部变量x

使用系统模块检查堆栈框架

到目前为止,我们使用了inspect模块来检查堆栈框架。我们也可以使用sys模块来做同样的事情。功能:

 sys.**_getframe**(*depth=0*)

从调用堆栈返回一个 frame 对象。如果给定了可选的整数深度,它将返回堆栈顶部以下许多调用的框架对象。例如,depth=1返回当前帧下面的下一个外框对象。深度的默认值为零,它返回位于调用堆栈顶部的当前帧。所以我们可以使用这个函数重写清单 3:

# Listing 6
import sys
a = 1
def f(x):
    b=2
    print(sys._getframe().f_code, "\n")
    print(sys._getframe().f_back, "\n")
    print(sys._getframe().f_locals)
    return x+b
y = f(a)

并获得与清单 3 相同的输出。此外,代替sys._getframe().f_back,我们可以写sys._getframe(1)来获得下一个外部框架对象。

递归函数

在递归函数中,函数从自己的代码中调用自己。这里有一个简单的例子。一个数 n 的阶乘可以定义为:

阶乘(n) = n × (n-1) × (n-2) × … × 1

我们可以定义一个递归函数来计算阶乘:

# Listing 7
def fact(n):
    if n==0:
        return 1
    else:
        return n*fact(n-1)

函数fact(n)通过将n乘以n-1的阶乘来计算n的阶乘。当n等于 1 时,它简单地返回 1,因此n=1定义了递归函数的基本情况。基本情况是返回值,而不进行任何后续的递归调用。

现在我们可以将print_frames添加到这个函数中,看看堆栈帧是如何生成的。

# Listing 8
def fact(n):
    if n==0:
        print("fact({}) called:".format(n))
        print_frames(inspect.stack())
        print("fact({}) returned {}".format(n, 1))
        return 1
    else:
        print("fact({}) called:".format(n))
        print_frames(inspect.stack())
        result = n*fact(n-1)
        print_frames(inspect.stack())
        print("fact({}) returned {}".format(n, result))
        return result

我们也想看到函数的返回值,所以先把返回值存储在result里,打印出来,然后由函数返回。来试试n=3:

fact(3)
-----------------------------------------------------------------
Output:fact(3) called:
  [Frame 1 'fact': {'n': 3}]
  [Frame '<module>']

fact(2) called:
  [Frame 2 'fact': {'n': 2}]
  [Frame 1 'fact': {'n': 3}]
  [Frame '<module>']

fact(1) called:
  [Frame 3 'fact': {'n': 1}]
  [Frame 2 'fact': {'n': 2}]
  [Frame 1 'fact': {'n': 3}]
  [Frame '<module>']

fact(0) called:
  [Frame 4 'fact': {'n': 0}]
  [Frame 3 'fact': {'n': 1}]
  [Frame 2 'fact': {'n': 2}]
  [Frame 1 'fact': {'n': 3}]
  [Frame '<module>']

fact(0) returned 1
  [Frame 3 'fact': {'n': 1, 'result': 1}]
  [Frame 2 'fact': {'n': 2}]
  [Frame 1 'fact': {'n': 3}]
  [Frame '<module>']

fact(1) returned 1
  [Frame 2 'fact': {'n': 2, 'result': 2}]
  [Frame 1 'fact': {'n': 3}]
  [Frame '<module>']

fact(2) returned 2
  [Frame 1 'fact': {'n': 3, 'result': 6}]
  [Frame '<module>']

fact(3) returned 66

如你所见,每次函数调用自己时,f的一个新帧被推到前面的帧之上,直到它到达递归的底部(fact(0))。然后基础用例返回1,它的框架被弹出调用堆栈。然后fact(1)返回1×1,弹出其框架。剩余的fact帧被逐个弹出,直到fact(3)返回最终值(6)并被弹出调用栈。因此很明显,对于fact(3),我们将 4 个框架放在模块框架的顶部,以达到基本情况。一般来说,为了计算fact(n),最多会将n+1帧推到堆栈顶部。但是如果n变成 5000 这样的大数字会怎么样呢?

如前所述,调用堆栈的大小是有限的。因此,通过增加n的值,我们最终达到了调用堆栈的极限,并且不能在此基础上推进更多的帧。这被称为堆栈溢出,这意味着我们用尽了内存来保存调用堆栈中的帧。为了避免堆栈溢出,Python 为调用堆栈可以容纳的总帧数设置了限制。限制是 999(调用堆栈可以容纳的最大帧数)。现在记住fact(n)需要n+1帧。此外,还应包括模块框架。所以,其实我们需要n+2帧。因此,我们可以使用的n的最大值是 997,需要 999 帧。现在我们来试试n=998。如果我们将清单 9 存储在 Python 脚本中,并在终端中运行它:

# Listing 9
def fact(n):
    if n==0:
        return 1
    else:
        return n*fact(n-1)
print(fact(998))

我们得到这个错误:

Traceback (most recent call last):
  File "fact.py", line 6, in <module>
    print(fact(998))
  File "fact.py", line 5, in fact
    return n*fact(n-1)
  File "fact.py", line 5, in fact
    return n*fact(n-1)
  File "fact.py", line 5, in fact
    return n*fact(n-1)
  [Previous line repeated 995 more times]
  File "fact.py", line 2, in fact
    if n==0:
RecursionError: maximum recursion depth exceeded in comparison

这意味着我们已经超过了极限。在像 Anaconda 这样的平台中,您可能对总帧数有更高的限制。比如在我的系统里,是 3000。

您也可以更改此限制,以便拥有更多帧。功能:

sys.**getrecursionlimit()**

给出递归和函数的当前限制

sys.**setrecursionlimit(limit)** 

用于设置调用堆栈可以容纳的最大帧数的限制。

请注意,最大递归深度的限制只是防止程序进入堆栈溢出,并不改变调用堆栈的实际大小。所以更改限制应该小心,因为过高的限制会导致 Python 崩溃。

另一个解决方案是减少递归函数需要推送的帧数,并使其表现得像非递归函数一样。这样,函数永远不会达到那个极限,无论输入数据有多大,都不会出现堆栈溢出。正如您将在下一节中看到的,这可以通过使用尾部递归和尾部调用优化来实现。

尾递归

当递归调用是函数执行的最后一件事时,递归函数是尾递归的。例如,清单 7 中的函数fact(n)不是尾递归的。在这个函数中,递归调用是fact(n-1)。然而,这并不是这个函数在返回结果之前做的最后一件事。它将fact(n-1)乘以n,然后返回结果。所以乘以n不允许它是尾递归的。

可以将fact(n)修改为尾递归。清单 10 显示了fact的尾递归版本:

# Listing 10
def fact1(n, acc=1):
    if n == 0:
        return acc
    else:
        return fact1(n-1, n*acc) fact1(4, 1)

这个函数有第二个参数acc,它扮演累加器的角色。当我们调用fact1时,我们用 1 初始化它。在fact1的每次调用中,n乘以acc,当到达基例时,只需要返回acc。现在我们可以将print_frames添加到这个函数中,看看帧是如何生成的。我们还将返回值存储在result中,并打印出来以查看函数的返回值。

# Listing 11
def fact1(n, acc=1):
    if n == 0:
        print("fact1({},{}) called:".format(n, acc))
        print_frames(inspect.stack())
        print("fact1({0},{1}) returned {1}".format(n, acc))
        return acc
    else:
        print("fact1({},{}) called:".format(n, acc))
        print_frames(inspect.stack())
        result = fact1(n-1, n*acc) 
        print_frames(inspect.stack())
        print("fact1({},{}) returned {}".format(n, acc, result))
        return result

你应该注意到这不是一个真正的尾递归函数,因为递归调用不是函数执行的最后一件事。实际上,在执行递归调用fact1(n-1, n*acc)后,我们将其返回值赋给result,然后调用print_functionprint,最后返回result。然而,我们只是出于调试的目的添加它们,以展示这个函数是如何工作的,并且我们在递归调用之后不改变result的值,所以对返回值没有计算。因此,这个函数给出了与清单 10 中尾递归函数相同的结果。

现在,如果我们执行fact1(3,1),输出将是:

fact1(3,1) called:
  [Frame 1 'fact1': {'n': 3, 'acc': 1}]
  [Frame '<module>']

fact1(2,3) called:
  [Frame 2 'fact1': {'n': 2, 'acc': 3}]
  [Frame 1 'fact1': {'n': 3, 'acc': 1}]
  [Frame '<module>']

fact1(1,6) called:
  [Frame 3 'fact1': {'n': 1, 'acc': 6}]
  [Frame 2 'fact1': {'n': 2, 'acc': 3}]
  [Frame 1 'fact1': {'n': 3, 'acc': 1}]
  [Frame '<module>']

fact1(0,6) called:
  [Frame 4 'fact1': {'n': 0, 'acc': 6}]
  [Frame 3 'fact1': {'n': 1, 'acc': 6}]
  [Frame 2 'fact1': {'n': 2, 'acc': 3}]
  [Frame 1 'fact1': {'n': 3, 'acc': 1}]
  [Frame '<module>']

fact1(0,6) returned 6
  [Frame 3 'fact1': {'n': 1, 'acc': 6, 'result': 6}]
  [Frame 2 'fact1': {'n': 2, 'acc': 3}]
  [Frame 1 'fact1': {'n': 3, 'acc': 1}]
  [Frame '<module>']

fact1(1,6) returned 6
  [Frame 2 'fact1': {'n': 2, 'acc': 3, 'result': 6}]
  [Frame 1 'fact1': {'n': 3, 'acc': 1}]
  [Frame '<module>']

fact1(2,3) returned 6
  [Frame 1 'fact1': {'n': 3, 'acc': 1, 'result': 6}]
  [Frame '<module>']

fact1(3,1) returned 66

你可以看到,当这个递归函数达到它的基本情况时,acc等于阶乘(3) 。然后相同的值被返回给调用者函数 fact1(1,6),fac1(2,3) …最后fact1(3,1)返回acc作为这个函数的最终结果。所以当它到达基本用例时,它不需要将结果返回给前面的调用方,函数执行可以通过返回acc来终止。

这与清单 8 中的输出fact(n)完全不同。那个函数不是尾递归的,所以当它到达基本情况(f(0))时,它只得到基本情况返回值1。这个值需要返回给调用者f(1)乘以 1,然后返回给f(2)乘以 2,依此类推。1,2, ...是f的参数,并且存储在它的框架中,所以当我们想要将这些参数乘以函数的返回值时,我们需要前面的框架能够访问这些参数。

在尾递归的fact1中,我们不需要在调用栈中保存之前的帧。我们只需要当前帧,直到我们到达基本情况,然后我们可以终止函数。因此尾递归函数不需要其当前帧下面的帧。然而,正如输出所示,Python 仍然像递归函数一样执行它,并保留所有帧。当尾部递归函数调用自身时,我们需要 Python 丢弃前一帧。

尾音优化

尾调用优化是一种允许尾递归函数无限递归而不发生堆栈溢出的方法。尾部调用优化将递归调用转换为循环。递归调用被替换为以下代码:

  • 首先用递归调用的相应参数值替换函数的参数
  • 跳回到函数的开头。

尾调用优化只能用于尾递归函数。原因是新代码在递归调用后跳回到函数的开头,并忽略其后的任何语句。

让我用一个例子来展示它。假设我们想对清单 10 中的fact1函数应用尾调用优化。我们可以使用清单 12 中的伪代码来实现。

# Listing 12
def fact1(n, acc=1):
    START: 
    if n == 0:
        return acc
    else:
        #  return fact1(n-1, n*acc) is replaced with:
        acc = n * acc
        n = n -1
        goto START

这里我们用一个新的代码替换递归调用fact1(n-1, n*acc)。如果n不等于零,函数nacc的参数将被替换为递归调用n-1n*acc的相应参数的值,然后跳转到函数的开头。这是一个伪代码,因为 Python 没有goto关键字。然而,我们可以使用一个while循环来实现它:

# Listing 13
def fact2(n, acc=1):
    while True:
        if n == 0:
            return acc
        else:
            acc = n * acc
            n = n - 1

Python 中不支持尾部调用优化,但是我们可以通过更改函数内部的代码来应用它,然而,我们更喜欢使用装饰器自动完成它,而不更改函数的代码。我们可以用两种不同的方法来做这件事,这将在下一节中解释。

使用堆栈帧进行尾部调用优化

第一种方法使用inspect模块并检查堆栈帧以防止递归和新帧的创建。清单 14 显示了一个装饰器,它可以将尾调用优化应用到目标尾递归函数:

现在我们可以用tail_rec来装饰fact1:

@tail_rec
def fact1(n, acc=1):
    if n == 0:
        return acc
    else:
        return fact1(n-1, n*acc)

fact1(4)

让我解释一下这个装饰器是如何工作的。当我们用tail_rec修饰fact1时,变量rec_flagtargstkwargs被初始化。现在fact1指的是helper的定义。所以当我们调用fact1(4)时,helper被调用(这里我们称之为第一个helper)。变量rec_flagtargstkwargshelper的非局部变量,所以它可以访问它们。helper首先获取当前帧。然后检查f.f_code是否等于f.f_back.f_back.f_code。如前所述,属性f_code指的是框架中正在执行的代码对象,它指示哪个函数创建了这个框架。所以f.f_code指的是当前帧的编码对象,也就是helper的编码对象。另外,f.f_back.f_back是指比当前帧低两级的帧(当前帧的祖辈)。

最初,helper的帧在module帧的上面,module帧是最下面的帧,所以当前帧的祖辈是None并且if块中的测试失败。(在 Anaconda 这样的平台中,祖父不是None,但是它的代码对象与helper不同,所以测试再次失败)。所以它进入else块并执行while循环。在while循环中func(*args, **kwargs)被调用。func是指fact1的定义。所以fact1(4)被执行,因为它是一个递归函数,它调用fact1(3, 4)

但是,fact1是修饰函数,所以引用的是helper。所以调用fact1(3, 4)再次执行helper,如图 2 所示(我们称之为第二个helper)。这次当前帧属于第二个helper,当前帧的祖辈是第一个helper。因此两个帧具有相同的代码对象(属于相同的函数)并且if部分中的条件为真。现在,helper 的参数存储在非局部变量targstkwargs中。此外,标志rec_flag被设置为True

事实上,if语句通过检查帧的代码对象来检测目标函数中递归的开始。当检测到递归的开始时,应该抑制附加帧的创建。因此,第二个helper存储递归调用的参数,并更改rec_flag以通知递归调用的存在,然后返回None。它的调用者是fact1,它将同样的内容返回给第一个helper函数,它的框架也被弹出调用堆栈(图 2)。

在第一个helper中,if语句的条件为真,所以它将targstkwargs的值赋给argskwargs。while 循环再次调用func(*aregs, **kwargs),因此fact1(3, 4)会用它最后一次递归调用的参数再次被调用。然而,这不是递归调用。事实上,当调用fact1(3, 4)时,调用堆栈中没有先前的fact1帧。

图 2

这个过程继续下去,每次当fact1试图递归调用自己时,helper就把它变成一个具有相同参数的非递归调用。最后,当我们到达基本情况(n=0)时,将不会有递归调用,fact1返回存储在while循环内的result中的acc。因为没有递归调用发生,rec_flag保持False。当rec_flagFasle时,执行else块,helper返回result

让我们看看如果我们使用一个非尾部递归函数和这个装饰器会发生什么。例如,我们可以使用清单 7 中的 factorial 函数:

@tail_rec
def fact(n):
    if n==0:
        return 1
    else:
        return n*fact(n-1)
print(fact(3))

您将得到以下异常输出:

**Exception**: It is possible that the decorated function is not tail recursive

因此,如果您修饰一个函数,对递归调用的返回值进行一些计算,它可以检测到它。语句result=func(*args,**kwargs)位于捕获TypeError异常的try块内。如果用这个装饰器来装饰一个非尾部递归函数,将会引发一个TypeError异常。原因是当helper检测到递归时,它返回None,所以在fact的最后一行,fact(n-1)也返回None。当该函数试图将n乘以None时,会引发TypeError。当然,有可能在函数的其他地方有一个TypeError,所以得到这样一个异常并不一定意味着你有一个非尾递归的函数。

重要的是要注意,如果你使用的函数不是尾递归的,但是对递归调用的返回值不做任何计算,装饰器不会抱怨。例如,它可用于正确修饰以下函数:

# Listing 15
def fact2(n, acc=1):
    if n == 0:
        return acc
    else:
        result = fact2(n-1, n*acc)
        return result

这个装饰器有一个不同的版本,其中递归的检测是通过引发一个异常来发出信号的,并且在装饰器中没有使用非局部变量。然而,在这种情况下,如果您对非尾递归函数使用装饰器,它将不会检测到它,而是返回错误的结果。

使用字节码注入的尾部调用优化

我们还可以通过改变函数的字节码来防止递归(如果你不熟悉字节码注入,可以参考这篇文章)。还记得清单 12 中的伪代码吗,我们用新代码替换了递归调用。新代码首先用递归调用的相应参数的值替换函数的参数,然后跳回到函数的开头。我们可以用类似的方式编写一个装饰器来改变目标函数的字节码。让我们看看清单 10 中fact1的字节码是什么样子的。我们使用dis模块来反汇编这个函数的字节码:

import dis
dis.dis(fact1)

输出是:

 2           0 LOAD_FAST                0 (n)
              2 LOAD_CONST               1 (0)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       12

  3           8 LOAD_FAST                1 (acc)
             10 RETURN_VALUE

  5     >>   12 LOAD_GLOBAL              0 (fact1)
             14 LOAD_FAST                0 (n)
             16 LOAD_CONST               2 (1)
             18 BINARY_SUBTRACT
             20 LOAD_FAST                0 (n)
             22 LOAD_FAST                1 (acc)
             24 BINARY_MULTIPLY
             26 CALL_FUNCTION            2
             28 RETURN_VALUE
             30 LOAD_CONST               0 (None)
             32 RETURN_VALUE

在第 5 行,函数调用自己。使用LOAD_GLOBALfact1的可调用对象推送到堆栈上。然后将函数的参数一个接一个地推到堆栈上,最右边的参数放在最上面。最后,使用CALL_FUNCTION调用该函数。它的 oparg(它的参数是2)表示位置参数的数量。它首先将所有参数和可调用对象弹出堆栈,然后用这些参数调用可调用对象,最后将返回值推到堆栈顶部。指令RETURN_VALUE将栈顶返回给函数的调用者。

现在我们应该用一些新的字节码指令替换CALL_FUNCTIONRETURN_VALUE。新代码应该从堆栈中弹出递归调用的所有参数,并将它们存储在当前函数的参数中,然后跳回到函数的开头。所以它应该是这样的:

 2     >>    0 LOAD_FAST                0 (n)
              2 LOAD_CONST               1 (0)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       12

  3           8 LOAD_FAST                1 (acc)
             10 RETURN_VALUE

  5     >>   12 LOAD_GLOBAL              0 (fact1)
             14 LOAD_FAST                0 (n)
             16 LOAD_CONST               2 (1)
             18 BINARY_SUBTRACT
             20 LOAD_FAST                0 (n)
             22 LOAD_FAST                1 (acc)
             24 BINARY_MULTIPLY
             26 STORE_FAST               1 (acc)
             28 STORE_FAST               0 (n)
             30 POP_TOP
             32 JUMP_ABSOLUTE            0
             34 LOAD_CONST               0 (None)
             36 RETURN_VALUE

此处CALL_FUNCTION 2在偏移量 26 处被移除。此时,递归调用的所有参数都在栈顶。所以用STORE_FAST把它们一个一个弹出来,存储在fact1的参数里。然后使用POP_TOP将它们下面的函数的可调用对象弹出堆栈,因为我们不再需要它了。之后,使用JUMP_ABSOLUTE 0跳回到偏移量 0 处的函数开始处。所以递归调用被转换成一个循环。

清单 16 中的装饰器采用了一个尾递归函数,并以类似的方式更改了它的字节码。

它使用在名为tailrec的模块中定义的两个函数disassemble_to_listassemble(这些函数已经在本文中详细讨论过了)。disassemble_to_list取函数的代码对象,将字节码反汇编成一些人性化的指令。它返回一个列表,其中每个元素显示一个字节码指令及其参数。这个列表可以很容易地用新指令替换字节码指令。修改指令后,assemble将这个列表转换成字节码。带有这个新字节码的新代码对象被创建并分配给目标函数。

在装饰器内部,有一个 while 循环,它搜索字节码列表以找到所有的递归调用。事实上,它搜索的是后面跟有RETURN_VALUECALL_FUNCTIONCALL_FUNCTION_KW指令。CALL_FUNCTION_KW用于有关键字参数的函数。在这种情况下,堆栈顶部的元素包含一组关键字参数名,它们是由之前的指令推入的。下面是关键字参数,下面是位置参数,最右边的参数在最上面。参数下面是要调用的可调用对象。

当它发现一个递归调用时,它会添加STORE_FAST指令,将递归调用参数存储在函数的参数中。它使用代码对象的co_varnames属性来获取函数参数的名称。然后将添加一个JUMP_ABSOLUTE 0以跳回到函数的开头。因为我们在原始函数中添加了更多的字节码指令,所以我们也应该更新具有相对或绝对跳转目标的指令(它们的操作码在dis.**hasjrel**dis.**hasjabs**中),并且我们应该修正它们跳转目标的偏移量。

这个装饰器还可以检测一个非尾递归的函数,并引发一个异常。然而,与清单 14 中的装饰器不同,该函数应该是真正的尾递归,并且在递归调用之后不允许任何指令。所以清单 15 中的函数不能用这个装饰器来装饰,并引发了一个异常。

尾调用优化可以使用这两种装饰器中的任何一种来完成,但是第二种要快得多。我们来比较一下他们的表现。我们首先定义两个设置函数s1s2来使用每个装饰器计算15的阶乘。

@tail_rec
def fact_1(n, acc=1):
    if n == 0:
        return acc
    else:
        return fact_1(n-1, n*acc)

@tail_recursion
def fact_2(n, acc=1):
    if n == 0:
        return acc
    else:
        return fact_2(n-1, n*acc)def s1():
    return fact_1(15)def s2():
    return fact_2(15)

现在我们可以使用timit模块比较这些设置的运行时间:

import timeit
t1 = timeit.timeit(s1, number=100000)
t2 = timeit.timeit(s2, number=100000)
print("Running time (tail-call optimization using stack frames)=", t1)
print("Running time (tail-call optimization using bytecode injection)=", t2)

输出是:

Running time (tail-call optimization using stack frames)= 6.038
Running time (tail-call optimization using bytecode injection)= 0.367

正如你看到的,使用字节码注入的装饰器快了 10 倍以上。原因是它消除了函数调用,用一个循环代替了它们。

我希望你喜欢阅读这篇文章。本文的所有代码清单都可以从以下网址下载:

【https://github.com/reza-bagheri/tail-rec

Python 股票分析-资产负债表趋势分析

原文:https://towardsdatascience.com/python-stock-analysis-balance-sheet-trend-analysis-18e6eb63cdc?source=collection_archive---------13-----------------------

为了分析我感兴趣的任何公司的股票,我曾经在 Excel 中预先构建了大量的 Excel 模板。我的旧流程简单但效率低下,我会选择一些公司,查看年度报告,或者在最好的情况下,查看雅虎财经,以获取我的 Excel 模板所需的所有输入。最终,这是一个非常耗时的手动过程。

手动收集财务数据需要大量的工作,这使我无法在分析部分花费更多的时间。这是学习 Python 和 Pandas 的原因之一,并且尽可能地自动化所有的数据收集过程。

来源:Pixabay,via Pexels

在我能够完全自动化我所有的股票分析之前,我花了几个月的时间达到 Python 和 Pandas 的良好水平。然而,这完全值得。现在,我只需写下我想分析的任何公司的名称,几秒钟后,结果就显示在我眼前。

现在,我想与你分享所有这些分析,并希望你能从中受益。在这篇文章中,我将向你展示如何用 Python 进行资产负债表趋势分析。

资产负债表趋势分析将显示每个资产负债表项目占总资产的百分比。这是比较资产负债表项目变化和分析长期趋势的非常有用的工具。

除了中等的故事,我在 Youtube 上还有一个视频教程系列( Python for Finance ),在那里我一行一行地解释如何为一些分析开发代码。如果对下面分享的代码有什么不清楚的地方,请随时访问 Youtube 上的视频教程:

现在让我们来看代码。下面的脚本非常简单,只需要我们几分钟就可以写完。我们将使用 Pandas、Requests 和 Json 库。我们将从名为金融建模准备的 API 中提取数据。它是一个免费的 API,拥有丰富的财务数据。

我们首先导入 Pandas、request 和 JSON ,并对财务建模准备 API 端点进行 get 调用,这将返回我们通过 get 请求传递的任何公司的资产负债表信息。在这个例子中,我们将通过传递苹果公司的股票代码从苹果公司获取数据。然后,我们将数据转换成 Json 格式:

import pandas as pd
import requests
import jsonbs = requests.get(f'https://financialmodelingprep.com/api/v3/financials/balance-sheet-statement/AAPL?period=quarter')bs = bs.json()

接下来,我们需要解析存储在变量 bs 中的字典。所需数据包含在一个名为 financials 的字典中。然后,我们使用Pandas DataFrame . from _ dict方法将我们的字典转换成 Pandas data frame。因为我们希望 Pandas 数据框架将数据资产负债表项目显示为行项目,所以我们将转置它。

bs = bs[‘financials’]bs = pd.DataFrame.from_dict(bs)bs = bs.Tbs.columns = bs.iloc[0]

Python for Finance —检索资产负债表

现在,我们的熊猫数据框看起来和上面的图片一模一样。我们还需要再写几行代码来清理它,并显示资产负债表中每个项目占总资产的百分比。

让我们用下面的代码来做这件事。首先,我将所有的列存储在一个名为 cols 的变量中。然后,我删除重复的第一行,仅保留 4 列和 12 行(即,前 4 列是 4 个最近的财务季度,并将删除第 12 行之后的负债和权益)。

最后,我们将列从对象转换为浮动,以便能够执行数学运算。你可以随时参考上面的 Youtube 视频,以防万一。

cols = bs.columnsbs = bs.iloc[1:,]bs = bs.iloc[:12,:4]cols = bs.columnsbs[cols] = bs[cols].apply(pd.to_numeric, errors=’coerce’)

我们的熊猫数据框架开始变得更好看了。我们拥有苹果公司过去 4 个季度的所有资产:

为了完成我们的脚本,我们只需要将一个季度的每个资产负债表项目除以该季度的总资产。让我们快点做那件事。

首先,我们使用 iloc[row,col]从每个季度提取总资产数字。然后,我们创建一个名为 allassets 的列表。最后,我们将资产负债表的每个项目除以总资产再乘以 100,以百分比的形式显示出来:

assetsQ1 = bs.iloc[11,0]assetsQ2 = bs.iloc[11,1]assetsQ3 = bs.iloc[11,2]assetsQ4 = bs.iloc[11,3]allassets = [assetsQ1,assetsQ2,assetsQ3,assetsQ4]bs[cols] = (bs[cols] / allassets)*100pd.options.display.float_format = ‘{:.2f}%’.format

最后,我们的分析完全自动化。现在,通过查看资产负债表趋势分析,我们可以很快看到,苹果去年一直在积累现金。从 6 月到 9 月,应收账款几乎翻了一番,长期投资有所减少。你可以根据这个趋势得出自己的结论。

用 Python 进行资产负债表趋势分析

请注意,我们可以为任何其他公司重用该分析,只需在代码开头将传递给 get 请求的 url 中的 url ticker 从 AAPL 替换为任何其他公司。

Python 股票分析—使用 Python 和 Plotly 的蜡烛图

原文:https://towardsdatascience.com/python-stock-analysis-candlestick-chart-with-python-and-plotly-e619143642bb?source=collection_archive---------9-----------------------

蜡烛图是一种非常常见和有用的股票价格表示法。通过查看蜡烛图,我们可以直观地看到任何给定股票的开盘价、收盘价、最低价和最高价。

在本文中,我将向您展示如何使用 Python、Pandas 和 Plotly 来构建您自己的蜡烛图。

M. B. M. 在 Unsplash 上的照片

我们将从导入所有需要的包开始。我们将需要熊猫PlotlyJson请求。需要 Requests 和 Json 从免费的金融 API Ffinancialmodelingprep中检索股票信息。

import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import requests
import jsonquote = 'AAPL'days = 300def candlestick(quote,days):
    r =     requests.get(f'[https://financialmodelingprep.com/api/v3/historical-  price-full/{quote}?timeseries={days}'](https://financialmodelingprep.com/api/v3/historical-price-full/{quote}?timeseries={days}')) r = r.json() return r

我们将 API 端点 url 作为 get 函数的一个参数进行传递,该 URL 存储在一个名为 r 的变量中。然后,我们把 API 响应转换成 Json,这样我们 Python 就可以很容易地用 Python 操纵它了。变量 r 将包含我们传递给函数烛台的任何股票的历史价格数据。在上面的例子中,我们传递了函数' AAPL '(即苹果)和 300 天作为参数。因此,结果是,我们得到一个代表 300 个数据点的列表。列表中的每个元素都是一个字典,包含我们的蜡烛图所需要的信息,即开市、高、低和收市的。

“symbol” : “AAPL”, “historical” : [ { “date” : “2015–01–20”, “open” : 107.84, “high” : 108.97, “low” : 106.5, “close” : 108.72, “volume” : 4.98999E7, “unadjustedVolume” : 4.98999E7, “change” : -0.88, “changePercent” : -0.816,....

如果可能的话,我总是喜欢把我的数据转换成熊猫数据框架,因为它让我以一种非常容易和方便的方式处理、操作和清理我的数据。这就是我们用下面两行代码实现的。

首先,我们解析变量 r 来提取与历史键相关的值,并将其存储在名为 stockdata 的变量中。通过这种方式,我们保留了一个字典列表。每个字典包含一天的股票价格数据。然后,我们可以简单地将 stockdata 列表转换成 Pandas 数据帧。

stockdata = r[‘historical’]
stockdata_df = pd.DataFrame(stockdata)

如果我们打印出我们的 Pandas data framestock data _ df,我们会得到类似下面的内容,其中每行代表一天,每列为我们提供股票价格信息:

到目前为止一切顺利。现在,让我们进入有趣的部分。我们可以开始用 Plotly 构建我们的图表。Plotly 是一个非常强大的图形库。非常容易使用 Python 和熊猫。

首先,我们需要创建一个包含数据点的 fig 对象。在我们的数字中,我们定义了我们的日期,开盘价、最高价、最低价和收盘价。每个数据点都将是我们的股票数据 _df 熊猫数据框架中的一列。例如,open 将包含我们的 Pandas 数据框架中包含的每个日期的开盘价。因为,我们在函数中传递了 300 天,open 将包含苹果最近 300 天的公开股票价格。

我们还可以使用 Plotly fig.update_layout 命令更新图表的布局,为图表添加一些漂亮的标题,更改字体,并确保文本位于图表区域的顶部中央。

最后,我们可以使用 fig.show() 来显示蜡烛图:

fig = go.Figure(data=[go.Candlestick(x=stockdata_df['date'],
                open=stockdata_df['open'],
                high=stockdata_df['high'],
                low=stockdata_df['low'],
                close=stockdata_df['close'])])fig.update_layout(
    title= {
        'text': quote,
      'y':0.9,
        'x':0.5,
        'xanchor': 'center',
        'yanchor': 'top'},
      font=dict(
        family="Courier New, monospace",
        size=20,
        color="#7f7f7f"
    )
    )fig.show()

现在神奇的是,或者说是因为我们的编码技能,一个令人印象深刻的烛台图表显示在我们面前,包括一个滑块来放大和缩小任何特定的时间段。自己尝试一下吧!

用 Python 和 Plotly 制作的烛台图

我已经包括了下面的整个脚本供您参考。只需替换报价天数变量来选择想要的股票和你想在图表中显示的天数。

注意:如果您在运行下面的代码时遇到任何问题,请确保您已经安装了所有必需的库。

import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import requests
import jsonquote = ‘AAPL’
days = 300def candlestick(quote,days):
  r = requests.get(f’[https://financialmodelingprep.com/api/v3/historical-price-full/{quote}?timeseries={days}'](https://financialmodelingprep.com/api/v3/historical-price-full/{quote}?timeseries={days}'))
  r = r.json()

  stockdata = r[‘historical’]
  stockdata_df = pd.DataFrame(stockdata)

  fig = go.Figure(data=[go.Candlestick(x=stockdata_df[‘date’],
  open=stockdata_df[‘open’],
  high=stockdata_df[‘high’],
  low=stockdata_df[‘low’],
  close=stockdata_df[‘close’])]) fig.update_layout(
    title= {
     ‘text’: quote,
     ‘y’:0.9,
     ‘x’:0.5,
     ‘xanchor’: ‘center’,
     ‘yanchor’: ‘top’},
   font=dict(
    family=”Courier New, monospace”,
    size=20,
    color=”#7f7f7f”
 )) fig.show()

candlestick(quote,days)

Python 股票分析-损益表瀑布图

原文:https://towardsdatascience.com/python-stock-analysis-income-statement-waterfall-chart-ffb7f9a4687f?source=collection_archive---------13-----------------------

在这篇文章中,我展示了如何使用 Python、Pandas 和 Plotly 以瀑布图的形式显示损益表。

在这篇文章中,我将向你展示如何使用 Python、Pandas 和 Plotly 以瀑布图的形式显示损益表。

一个 瀑布图 是一种表示数据的方式,以便可视化不同项目的累积效果。在我们的例子中,我们将能够看到每一个收益表行从收入到净收入的影响。

照片由 Pixabay 来自 像素

我将文章分为两部分,在第一部分,我们将从免费的财务 APIFinancialmodelingprep中检索我们感兴趣的任何公司的损益表数据。然后,我们将使用 Pandas 来清理数据并执行一些基本操作。

在文章的第二部分,我们将使用 Plotly 将损益表转换成一个漂亮的瀑布图表示。

如果你跟随这篇文章并和我一起编码,你将能够为你感兴趣的任何上市公司建立瀑布图。

瀑布图形式的损益表— Python for Finance

好了,让我们开始编码第一部分。我们需要导入 json请求库,以便向 API 端点发出 GET 请求。我们还导入 Pandas 来将请求的数据转换成 Pandas DataFrame ,最后,我们将导入 Ploty ,这是我们将用来创建图表的库。

对于所有不熟悉 Plotly 的人来说,这是一个免费的 Python 图形库,它提供了创建非常有吸引力的交互式图表的可能性。

import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import requests
import json

导入所需的包后,我们开始创建我们的函数 selectquote ,我们将在其中添加将我们的数据转换成 Pandas 数据帧的逻辑。我们发出一个 http 请求,并将数据转换成 Json ,这样我们就可以用 PythonPandas 轻松处理它。

然后,从通过 API 检索的信息中,我们将关键字 financials 的值存储到 stock 变量中。Financials 包含一个字典列表,其中每个字典代表一个季度的损益表。

接下来,我们从股票字典中创建一个 Pandas 数据框架,并对其进行转置,以便将损益表项目作为行,将日期作为标题。

def selectquote(quote): 
   r= requests.get(f"https://financialmodelingprep.com/api/v3/financials/income-statement/{quote}?period=quarter")    r = r.json() 
   stock = r['financials'] stock = pd.DataFrame.from_dict(stock) 
   stock = stock.T   
   stock.columns = stock.iloc[0]
   stock.reset_index(inplace=True) 
   return  stock selectquote('AAPL')

到目前为止,如果我们打印出我们的库存熊猫数据帧,我们会得到类似于下面的苹果的结果,因为我们已经将 AAPL 传递给了我们的选择报价函数。请注意,我们需要作为参数传递股票的代码,而不是全名

在我们创建图表之前,还有一些东西需要清理。

首先,我们将熊猫数据框切片,只保留上一季度的损益表。然后,我们将列名重命名为股票名称,而不是日期。最后,我们将损益表信息(即列)转换成一个数字。

stock = stock.iloc[:,0:2]stock.rename(columns={ stock.columns[1]: quote }, inplace = True)cols = stock.columns.drop('index')stock[cols] = stock[cols].apply(pd.to_numeric, errors='coerce')stock = stock.iloc[1:,]

重新运行我们的代码后,我们可以看到苹果公司最新的季度收入报表:

incomeStatement = selectquote('AAPL')
print(incomeStatement)

Python for Finance —损益表

我们还需要提取收入、销货成本等的价值。从我们的库存熊猫数据框架以便在瀑布图中使用。我们可以很容易地用 iloc 找到损益表项目的价值。我们将费用乘以 -1 ,使其符合负号约定:

Revenue = incomeStatement[incomeStatement['index'] == 'Revenue'].iloc[0][1]COGS = incomeStatement[incomeStatement['index'] == 'Cost of Revenue'].iloc[0][1]*-1grossProfit = incomeStatement[incomeStatement['index'] == 'Gross Profit'].iloc[0][1]RD = incomeStatement[incomeStatement['index'] == 'R&D Expenses'].iloc[0][1]**-1**GA =* incomeStatement*[*incomeStatement*['index'] == 'SG&A Expense'].iloc[0][1]**-1operatingExpenses = incomeStatement[incomeStatement['index'] == 'Operating Expenses'].iloc[0][1]**-1**interest =* incomeStatement*[*incomeStatement*['index'] == 'Interest Expense'].iloc[0][1]**-1EBT = incomeStatement[incomeStatement['index'] == 'Earnings before Tax'].iloc[0][1]incTax = incomeStatement[incomeStatement['index'] == 'Income Tax Expense'].iloc[0][1]*-1netIncome = incomeStatement[incomeStatement['index'] == 'Net Income'].iloc[0][1]

太好了,我们已经完成了代码的第一部分。现在,让我们进入有趣的部分,我们将创建一个瀑布图

首先,我们将创建一个 对象,它将包含构建图表所需的数据点。对于瀑布图**,我们将需要一个度量列表,其中我们将指出每个变量是相对度量还是总度量。这将影响图表的构建方式。例如,毛利将是一个总的衡量标准,它将从图表的底部开始。****

然后,我们将有我们的 x 轴,它将包含损益表项目的名称,最后,我们的 y 将包含与每个 x 相关联的值。此外,我们将有一个文本列表,其中包含我们希望在图表中显示的每个项目的值。

我们将文本除以 100,000 来表示以百万计的数字。

fig = go.Figure(go.Waterfall(name = "20", orientation = "v",measure = ["relative", "relative", "total", "relative", "relative", "total","relative","total","relative","total"],x = ["Revenue", "COGS", "Gross Profit", "RD", "G&A", "Operating Expenses","Interest Expense", "Earn Before Tax","Income Tax","Net Income"],textposition = "outside",text = [Revenue/100000, COGS/100000, grossProfit/100000, RD/100000, GA/1000000, operatingExpenses/1000000,Interest/100000, EBT/100000,incTax/100000, NetIncome/100000],y = [Revenue, COGS, grossProfit, RD, GA, operatingExpenses, Interest,EBT, incTax, NetIncome], connector = {"line":{"color":"rgb(63, 63, 63)"}}, )) fig.update_layout( 
title = "Profit and loss statement",
showlegend = **True** ) fig.show()

现在,如果我们运行代码,我们可以看到苹果公司的收益表是一个瀑布图:

自己尝试使用不同的股票,只需将所需公司的股票代码作为参数传递给 selectquote 函数。

下面看到完整的代码供你参考。请注意,您需要安装教程中使用的所有库。否则,代码将不起作用。

import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import requests
import json

def selectquote(quote):
     r= requests.get(f"https://financialmodelingprep.com/api/v3/financials/income-statement/{quote}?period=quarter")
     r = r.json()
    stock = r['financials']
    stock = pd.DataFrame.from_dict(stock)
    stock = stock.T
    stock.columns = stock.iloc[0]
    stock.reset_index(inplace=True)
    stock = stock.iloc[:,0:2]
    stock.rename(columns={ stock.columns[1]: quote }, inplace = True)
    cols = stock.columns.drop('index')
    stock[cols] = stock[cols].apply(pd.to_numeric, errors='coerce')
    stock = stock.iloc[1:,]
    return stock

incomeStatement = selectquote('AAPL')Revenue = incomeStatement[incomeStatement['index'] == 'Revenue'].iloc[0][1]COGS = incomeStatement[incomeStatement['index'] == 'Cost of Revenue'].iloc[0][1]*-1grossProfit = incomeStatement[incomeStatement['index'] == 'Gross Profit'].iloc[0][1] RD = incomeStatement[incomeStatement['index'] == 'R&D Expenses'].iloc[0][1]*-1 GA = incomeStatement[incomeStatement['index'] == 'SG&A Expense'].iloc[0][1]*-1operatingExpenses = incomeStatement[incomeStatement['index'] == 'Operating Expenses'].iloc[0][1]*-1 interest = incomeStatement[incomeStatement['index'] == 'Interest Expense'].iloc[0][1]*-1EBT = incomeStatement[incomeStatement['index'] == 'Earnings before Tax'].iloc[0][1]incTax = incomeStatement[incomeStatement['index'] == 'Income Tax Expense'].iloc[0][1]*-1netIncome = incomeStatement[incomeStatement['index'] == 'Net Income'].iloc[0][1] 

fig = go.Figure(go.Waterfall(
    name = "20", orientation = "v", measure = ["relative", "relative", "total", "relative", "relative", "total","relative","total","relative","total"],

    x = ["Revenue", "COGS", "Gross Profit", "RD", "G&A", "Operating Expenses","Interest Expense", "Earn Before Tax","Income Tax","Net Income"], textposition = "outside",

    text = [Revenue/100000, COGS/100000, grossProfit/100000, RD/100000, GA/1000000, operatingExpenses/1000000,Interest/100000, EBT/100000,incTax/100000, NetIncome/100000],

     y = [Revenue, COGS, grossProfit, RD, GA, operatingExpenses, Interest,EBT,incTax,NetIncome],

    connector = {"line":{"color":"rgb(63, 63, 63)"}},

fig.update_layout(
         title = "Profit and loss statement",
         showlegend = True
 )))

fig.show()

我创建了下面的 Youtube 视频,我一步一步地建立瀑布图。如果您在理解本文中的代码时遇到困难,这是值得一查的。

损益表的瀑布图— Python for Finance

原载于【https://codingandfun.com】

Python 字符串插值

原文:https://towardsdatascience.com/python-string-interpolation-829e14e1fc75?source=collection_archive---------19-----------------------

了解用 Python 实现字符串插值的方法。

字符串插值是将值注入字符串文字中的占位符(占位符只是一个变量,您可以稍后将数据/值赋给它)的过程。它有助于以一种更好的方式动态格式化输出。Python 支持多种格式化字符串文字的方式。所有字符串插值方法总是返回新值,并且不处理原始字符串。

作者图片

使用%(模)运算符

除了其正常的计算用法, % 运算符在 str 类中被重载以执行字符串格式化,它将各种类类型插入到字符串文字中。下面是一些常用的格式说明符,

作者图片

使用 % 的一般格式为:

作者图片

这里, % 表示使用 Python 的字符串格式化功能时字符串的转换类型。

使用%的字符串插值

Output:
-------
Result of calculation is 4.38

Hey! I'm Emma, 33 years old and I love Python Programing

Hey! I'm Emma and I'm 33 years old.

作者图片

在上面的例子中,我们使用了两个 %s 和一个 %d 格式说明符,它们只是元组 ('Emma ',33,' Python') 值的占位符。 % 只接受一个参数,因此元组用于多值替换。请注意,元组的值是按照指定的顺序传递的。如果一个值的类型和对应的格式说明符的类型不匹配,Python 会抛出 类型错误 异常。

在下面的代码中, %d 是字符串的第一个值 'Emma' 的格式说明符,因此引发了 TypeError 异常。

print("\nHey! I'm **%d**, %d years old and I love %s Programing"%('**Emma**',33,'Python')) **--------------------------------------------------------------------**
**TypeError**                                 Traceback (most recent call last)
<ipython-input-5-f2e9acc11cdb> in <module>
      1 
----> 2 print("\nHey! I'm **%d**, %d years old and I love %s Programing"%(**'Emma'**,33,'Python'))  

**TypeError**: %d format: a number is required, not str

尽管 % -formatting 在 Python 中从一开始就是可用的,但是当单个字符串中有多个替换时,它就显得笨拙了。

使用。format()方法

Str.format()用于位置格式化,这允许重新排列字符串中占位符的顺序,而不改变它们的排列顺序。格式()。{ }用作占位符,并且是方法传递的唯一值。format()将被替换为{ },其余的字符串文字将在输出中保持不变。

位置格式可以通过在占位符中使用索引或关键字来实现。如果它们都没有被指定,对象将按照它们在中被提到的顺序被注入。默认情况下,格式为()。

字符串插值使用。格式( )

Output:
-------
Hey! I'm Emma, 33 years old, and I love Python Programming.

33 years old Emma loves Python programming.

Emma loves Python programming and she is 33 years old.

使用索引/关键字或者保持黑色,你不能将它们混合在一起,因为 Python 会抛出 值错误 异常在下面的代码中,两个占位符保持空白,一个具有索引“2”引用,因此引发了异常。

name="Emma"
age=33
lan="Python"print("**{}** years old **{2}** loves **{}** programming.".format(age,lan,name))**--------------------------------------------------------------------**
**ValueError**                                Traceback (most recent call last)
<ipython-input-15-b38087c9c415> in <module>
      5 lan="Python"
      6 
----> 7 print("**{}** years old **{2}** loves **{}** programming."**.**format**(**age**,**lan**,**name**))**
      8 

**ValueError**: cannot switch from automatic field numbering to manual field specification

。format()方法是通用的,可以很容易地用于所有的数据结构。

字符串插值使用。格式( )

Output:
-------
Hey! My name is Emma, I'm 33 years old, currently living in UK and love Python programming

Person info from List: Emma from UKPerson info from Tuple: Emma 33 UK

Person info Set: 33 Python Emma

如上面的代码所示,从 dictionary person_dict 接收的值将键值作为字符串中的占位符。 (双星号)用于解包映射到键值的字典值。

作者图片

对于列表和元组,在占位符中使用了索引。

使用 f 字符串格式

f-string 或格式化字符串文字提供了一种使用最小语法格式化字符串的方法。(在 Python 3.6 中引入)。代码可读性很高,因此它通常是格式化字符串的首选方式。“F”或“F”用作前缀,{}用作占位符。不像。format(),f-string 不允许空括号{}。f 字符串表达式在运行时计算。

string 比两种最常用的字符串格式化机制更快,%-formatting 和。格式( )

使用 f 弦的字符串插值

Output:
-------
Emma is 33 years old currently living in UK and loves Python programming.
33 years old  Emma lives in UK and loves Python programming
'Emma' is a python developer from UK

Date in default format: 2020-06-04 17:01:31.407452 
Date in custom format: 04/06/20

如上面的代码所示,我们只需在字符串的开头附加' f ',并将变量名直接放入占位符{ }。f-string 格式可以无缝地用于类及其对象。

Output:
-------
Person Details: Emma is 33 years old Python developer.

[Name: Emma, Age: 33, Programming_language: Python]

这里,我们在 Person 类中定义了两个方法。开头附加“f ”,类/对象引用变量放在{ }中。

作者图片

干净精准!

使用模板类

字符串模块的模板类有助于字符串插值的另一种方式。它允许您使用映射对象替换字符串。这里,前面带有符号' $ '的有效 python 标识符被用作占位符。

作者图片

基本上,我们可以使用模板类对象定义一个字符串文字,然后通过 substitute()或 safe_substitute()方法映射占位符的值。

作者图片

$ '符号执行实际的替换,标识符用于映射方法 substitute()或 safe_substitute()中指定的替换关键字。

substitute( ) —如果没有提供相应占位符的值,则会引发错误。

safe_substitute( ) —当用户提供的数据可能不完整时,此选项更合适。(当数据丢失时,占位符保持不变。)

使用模板类的字符串插值

Output:
-------
Emma is 33 years old and loves Python programming!

$age years old Emma loves Python programming

$age years old Harry loves Java programming

在上面的例子中,我们已经创建了保存字符串的模板类的对象 person_info 。然后使用 substitute( ) 方法注入实际值,它将值映射到占位符名称。

作者图片

如果没有提供相应占位符的值,substitute( ) 将引发错误。这里, substitute( ) 中没有提供 $years 的值,因此它引发 KeyError 异常。

person_info=Template('\n$name is $years years old and loves $lan programming!')   
print(person_info.substitute(name='Emma',lan='Python'))**--------------------------------------------------------------------**
**KeyError**                                  Traceback (most recent call last)
<ipython-input-7-2b10997fef23> in <module>
      4 #creating object of Template class
      5 person_info=Template('\n$name is $years years old and loves $lan programming!')
----> 6 print(person_info.substitute(name='Emma',lan='Python'))  #substitute()
      7 
      8 

~\Anaconda3\lib\string.py in substitute(*args, **kws)
    130             raise ValueError('Unrecognized named group in pattern',
    131                              self.pattern)
--> 132         return self.pattern.sub(convert, self.template)
    133 
    134     def safe_substitute(*args, **kws):

~\Anaconda3\lib\string.py in convert(mo)
    123             named = mo.group('named') or mo.group('braced')
    124             if named is not None:
--> 125                 return str(mapping[named])
    126             if mo.group('escaped') is not None:
    127                 return self.delimiter

**KeyError**: 'years'

在同一个示例中,我们还使用了 safe_substitute(),其中我们没有为占位符 $age 分配任何值,因此它在输出中保持不变,不会引发任何异常。

作者图片

**$age** years old Emma loves Python programming

**$age** years old Harry loves Java programming

该方法被考虑用于复杂的定制字符串操作;但是,str 最大的局限性。模板类是,它只接受字符串参数。

结论

我个人大部分时间使用 f-string,因为它简洁,编写非常方便,同时代码可读性很高。该主题的一些重要资源是,

  • PEP 498 —字符串插值
  • 格式化的字符串文字

本文中使用的代码可以从我的 GitHub 库中获得。

Python 字符串方法

原文:https://towardsdatascience.com/python-string-methods-7ac76ed7590b?source=collection_archive---------40-----------------------

看看最常用的操作 Python 字符串的内置方法

在 Unsplash 上由 Hitesh Choudhary 拍摄的照片

什么是字符串,什么是字符串方法?

在 Python 中,文本形式的数据被称为字符串。要将数据指定为字符串,文本必须用单引号(')或双引号(" ")括起来。名字,地点,物体,句子,甚至数字都可以是字符串,只要用引号括起来。一旦你有了一个字符串,你就可以使用所谓的“方法”来操作这个字符串。要使用一个方法,您只需编写字符串,后跟。【方法】()。例如,要对字符串“pizza”运行 upper() 方法,只需编写“pizza”即可。upper();如果将“pizza”设置为一个变量,您应该使用 variable.upper()。一些方法接受所谓的“参数”,这些参数放在括号中,进一步定义了该方法将做什么。Python 语言有很多内置的方法,比如 upper() ,允许你轻松地改变字符串。

upper()、lower()、title()和大写()

这些方法都在字符串上执行简单的操作,并且不带参数

upper() 方法将字符串中的每个字母转换成大写:

Syntax: *string*.upper()'pizza'.upper() --> 'PIZZA'

lower() 方法将字符串中的每个字母转换成小写:

Syntax: *string*.lower()'PIZZA'.lower() --> 'pizza'

title() 方法将字符串中每个单词的首字母大写,就像标题一样:

Syntax: *string*.title()'i love to eat pizza'.title() --> 'I Love To Eat Pizza'

capital()方法类似于 title()方法;但是,只有字符串中第一个单词的第一个字母大写,就像一个句子:

Syntax: *string*.capitalize()'i love to eat pizza'.capitalize() --> 'I love to eat pizza'

记住,所有这些例子也可以用变量来写:

food = 'pizza'
food.upper() --> 'PIZZA'

拆分()

方法将一个字符串转换成一个列表。该方法可以带两个可选参数。第一个参数是分隔符,它告诉代码如何拆分字符串。默认情况下,字符串在任何空格处被分割,但是如果您选择,您可以决定用任何字符来分割字符串。第二个参数指定要完成的最大拆分次数。默认情况下,该数字设置为-1,即所有事件。

Syntax: *string*.split(*separator, maxsplit*)'I like to eat pizza'.split() --> 
['I', 'like', 'to', 'eat', 'pizza']'I like to eat pizza'.split('e') -->
['I lik', ' to ', 'at pizza']'I like to eat pizza'.split('e', 1) -->
['I lik', ' to eat pizza']

分区()

与 split()类似, partition() 方法按照指定的字符分割字符串。但是,使用这种方法,字符串被分割成一个由三项组成的元组:匹配之前的所有内容、匹配本身和匹配之后的所有内容。通过使用 partition()而不是 split(),您可以保留用来拆分字符串的字符。这个方法使用了一个必需的参数——分隔符,它告诉代码你希望如何分割字符串。如果在字符串中没有找到分隔符,仍然返回一个包含三项的元组;但是,它由整个字符串、一个空字符串和另一个空字符串组成。

Syntax: *string*.partition(*value*)'I like to eat pizza'.partition(' to ') 
--> ('I like', ' to ', 'eat pizza')'I like to eat pizza'.partition(' drink ')
--> ('I like to eat pizza', '', '')

加入()

join() 方法用于将 iterable (列表、字典、元组、集合,甚至另一个字符串)中的所有项目连接到一个字符串。该方法使用一个必需的参数,即 iterable。在应用该方法之前使用的字符串被输入到 iterable 中的每一项之间。通常,仅由一个空格(' "或" ")组成的字符串与 join()方法一起使用,在 iterable 中的单词之间创建空格。

Syntax: *string*.join(*iterable*)' '.join(['I', 'like', 'to', 'eat', 'pizza']) 
--> 'I like to eat pizza''x'.join(['I', 'like', 'to', 'eat', 'pizza'])
--> 'Ixlikextoxeatxpizza''yummy'.join({'food':'Pizza', 'topping': 'pepperoni'})
--> 'foodyummytopping'

替换()

replace() 方法允许你用另一个值替换一个字符串的指定值。这个方法有三个参数,其中前两个是必需的。第一个参数是您希望替换的字符串的值,第二个参数是将取代它的值。第三个参数是一个整数,指定要替换的值的出现次数,默认为所有出现次数。

Syntax: *string*.replace(*oldvalue, newvalue, count*)'I like to eat pizza'.replace('pizza', 'burgers')
--> 'I like to eat burgers''I really really like to eat pizza'.replace('really', 'kind of', 1)
--> 'I kind of really like to eat pizza'

条状()

方法从一个字符串的开头或结尾删除你选择的任何字符。该方法可以带一个可选参数。默认情况下,任何前导空格或尾随空格都将从字符串中删除,但是如果您将所选的任何字符作为参数包含在字符串中,您也可以选择删除这些字符。

Syntax: *string*.strip(*characters*)'   pizza   '.strip() --> 'pizza''..rjq,,pizza.rq,j.r'.strip('rjq.,') --> 'pizza'

startswith()和 endswith()

如果字符串以指定的值开始,则 startswith() 方法返回 True,否则返回 False。endswith() 方法以同样的方式工作,但是使用了字符串的结尾。这些方法有三个参数,第一个是必需的,后两个是可选的。第一个参数是您要查看字符串是否以此开头/结尾的值。第二个参数是一个整数值,指定要从哪个索引开始搜索,第三个参数是要结束搜索的索引的值。

Syntax: *string*.[starts/ends]with(*value, start, end*)'I like to eat pizza'.startswith('I like') --> True'I like to eat pizza'.endswith('drink pizza') --> False'I like to eat pizza'.startswith('to', 7, 18) --> True'I like to eat pizza'.endswith('eat', 7, 18) --> False

计数()

方法告诉你一个指定的值在一个字符串中出现了多少次。这个方法需要三个参数,其中第一个是必需的。第一个参数是要在字符串中搜索的值。第二个和第三个参数分别是希望开始和结束搜索的位置。默认情况下,第二个参数是 0,即字符串的开头,第三个参数是-1,即字符串的结尾。

Syntax: *string*.count(*value, start, end*)'I really really like to eat pizza'.count('really') --> 2'I really really like to eat pizza'.count('really', 8, 20) --> 1

查找()和索引()

find()index() 方法几乎完全相同,因为它们都返回指定值的第一个匹配项的索引(如果给定值是多个字符,则返回指定值的第一个字符的索引)。两者之间的唯一区别是,如果在字符串中没有找到该值,find()方法将返回-1,而如果该值没有出现,index()方法将导致错误。两种方法都有三个参数,其中第一个是必需的。与 count()方法一样,第一个参数是要在字符串中搜索的值,第二个和第三个参数分别是要开始和结束搜索的位置。默认情况下,第二个参数是 0,即字符串的开头,第三个参数是-1,即字符串的结尾。

Syntax: *string*.[find/index](*value, start, end*)'I really really like to eat pizza'.find('really') --> 2'I really really like to eat pizza'.index('really') --> 2'I really really like to eat pizza'.find('really', 8, 20) --> 9'I really really like to eat pizza'.index('really', 8, 20) --> 9'I really really like to eat pizza'.find('hamburger') --> -1'I really really like to eat pizza'.index('hamburger') --> ERROR

现在你知道了。使用上述这些方法,您应该能够执行 Python 字符串上的大多数操作。要查看所有可用的字符串方法,请确保查看 w3schools 列表。

快乐编码,请务必查看我的博客,下面列出了 Python 方法!

[## Python 列表方法

Python 中最常用列表方法的总结

towardsdatascience.com](/python-list-methods-fa7c53010300)

参考资料:

[## Python 字符串方法

Python 有一组内置的方法,可以用在字符串上。注意:所有的字符串方法都返回新值。他们确实…

www.w3schools.com](https://www.w3schools.com/python/python_ref_string.asp)

Python 字符串

原文:https://towardsdatascience.com/python-strings-38c3d74c236a?source=collection_archive---------26-----------------------

他们比你想象的要多得多

照片由 Aditya Wardhana 在 Unsplash 上拍摄

在 Python 中,字符串似乎是简单的数据类型。但是你知道str.format_map是做什么的吗?还是str.expandtabs?没有吗?好的,str.isidentifier怎么样?

直到最近,我才听说过他们中的任何一个——如果你听说过,公平地说,我印象深刻。

Python 的字符串方法多种多样,有些非常有用,有些则不那么有用。但是它们都有自己独特的用例。我们将看看一些不太常见但仍然有用的特性。

所以,让我们来看看 Python 的字符串方法。

案件卷宗

更激进的版本lower。鉴于lower只适用于 ASCII 字符[A-Z] -> [a-z]casefold试图标准化非标准字符,例如拉丁语或希腊语。

这真的很好用——希腊神话中的奥林匹斯山神赫尔墨斯,在希腊语中被写成ἑρμῆς,在小写中,这是ἑρμῆς.

"Ἑρμῆς".casefold() == "ἑρμῆς".casefold()

**[Out]:** True

相比之下,lower在这点上就失败了。

"Ἑρμῆς".lower() == "ἑρμῆς".lower()

**[Out]:** False

中心

这里,我们在字符串的两边添加填充,使其居中对齐,如下所示:

"hello there".center(20, ".")

[Out]: "....hello there....."

第二个参数是可选的,默认为" "

ljust 和 rjust

center类似,这些函数向字符串添加填充,并分别对其进行左对齐或右对齐。同样,默认参数是" "

"hello there".ljust(20, ".")
"hello there".rjust(20, ".")[Out]: "hello there........."
       ".........hello there"

数数

这让我们可以计算一个模式在一个句子中重复的次数。

"Lets count how many spaces are in this string".count(" ")

**[Out]:** 8

"You cannot end a sentence with because because because is a conjunction.".count("because")

**[Out]:** 3

编码

允许我们指定或改变字符串的编码类型,默认为utf-8。如果字符串包含编码集之外的字符,我们将收到一个UnicodeEncodeError

"ἑρμῆσ".encode('ascii')

**[Out]:** UnicodeEncodeError

我们可以调整错误处理方案的行为——默认为'strict''ignore'会忽略错误,'replace'会替换它们。

"ἑρμῆσ".encode('ascii', 'ignore')

**[Out]:** b''

"ἑρμῆσ".encode('ascii', 'replace')

**[Out]:** b'??????'

扩展表

这将所有制表符替换为空格,默认为八个空格。

print("\tthis is tabbed".replace(" ", "-"))  # without expandtabs
print("\tthis is too"**.expandtabs()**.replace(" ", "-"))  # with**[Out]:** "        this-is-tabbed"
       "--------this-is-too"

格式 _ 地图

类似于format方法,但是允许我们使用字典。例如:

mappings = {'x': 'mapped', 'y': 'values', 'z': 'dictionary'}
print("We have {x} the {y} from the {z}".format_map(mappings))

**[Out]:** "We have mapped the values from the dictionary"

分区和 rpartition

partition类似于split,但是在给定模式的第一个实例上拆分字符串,并且也返回给定模式。rpartition做同样的事情,但是从字符串的右边开始。

"lets split this".split(" ")
"and this too".partition(" ")
"to the right".rpartition(" ")**[Out]:** ['lets', 'split', 'this']
       ('and', ' ', 'this too')
       ('to the', ' ', 'right')

rfind

其中find返回满足给定模式的字符串中第一个字符的索引,rfind做同样的事情,但是从字符串的右边开始。

"l**e**ts search this string".find("e")
"lets s**e**arch this string".rfind("e")**[Out]:** 1
       6

如果没有找到模式,findrfind返回-1

"this string does not contain the pattern".rfind("ABC")**[Out]:** -1

rindex

indexrindex几乎总是以与findrfind相同的方式工作:

"search me".rfind("e")
"search me".rindex("e")**[Out]:** 8
       8

除了模式是而不是的地方。indexrindex将返回一个ValueError:

"another string".rfind("X")
"another string".rindex("X")**[Out]:** -1
       *ValueError: substring not found*

rsplit

split相同,但从右侧开始。这仅在指定了最大分割数时才有所不同,如下所示:

"there are six spaces in this string".split(" ", 3)
"there are six spaces in this string".rsplit(" ", 3)**[Out]:** ['there', 'are', 'six', 'spaces in this string']
       ['there are six spaces', 'in', 'this', 'string']

分割线

也与split相同,但由换行符\n分开。

"""This is a
multi-line\nstring""".splitlines()

**[Out]:** ['This is a', 'multi-line', 'string']

交换情况

交换字符的大小写,也适用于非英语字母。

"CamelCase".swapcase()
"Ἑρμῆς".swapcase()[Out]: 'cAMELcASE'
       'ἑΡΜΗΣ'

翻译

允许我们使用用maketrans方法构建的翻译字典来交换模式。

translate = "".maketrans("e", "3")
"lets translate this string".translate(translate)**[Out]:** 'l3ts translat3 this string'

零填充

用零填充字符串的开头,直到达到给定的长度。如果给定的长度小于字符串的长度,则不进行填充。

"test one".zfill(5)
"two".zfill(5)**[Out]:** 'test one'
       '00two'

格式检查

我们也有许多格式检查方法,它们返回TrueFalse。大多数都是不言自明的,所以我们将保持简短。

伊萨勒姆

字符串是字母数字的,只包含字母和数字的混合字符。

"a1phanum3r1c".isalnum()  # contains no space character

**[Out]:** True

"a1pha num3r1c".isalnum()  # contains a space character

**[Out]:** False

伊萨法

字符串是按字母顺序排列的,只包含字母吗?

"ABC".isalpha()

**[Out]:** True

"A/B/C".isalpha()

**[Out]:** False

伊萨西

字符串是否只包含 128 字符 ASCII 编码标准中的字符?

"Pretty much anything on your English language keyboard! Punctuation included ~#!$%^&*()|\<>,.?/:;@'{}[]-=_+ and numbers too 1234567890".isascii()

**[Out]:** True

"We are not allowed Ἑρμῆς, nor £, €, and ¬".isascii()

**[Out]:** False

isdecimal

这一个在行为上更有趣,也更出乎意料。它识别只包含 Unicode 十进制数字字符的字符串。这并不意味着像3.142这样的十进制数。

在许多不同的脚本中,Unicode 十进制数字字符涵盖了数字本身。例如,它涵盖了0-9,它还涵盖了孟加拉数字三৩,以及藏语数字五༥.

"123".isdecimal()

[Out]: True

"3.142".isdecimal()

[Out]: False

"༥৩".isdecimal()

[Out]: True

Unicode 十进制数字的完整列表可在此处找到。

标识符

这告诉我们这个字符串是否是一个有效的 Python 标识符,这意味着我们可以将它指定为变量、函数、类等的名称。它包括字母、数字和下划线。但是,它不能以数字开头。

"valid_identifier".isidentifier()
"9_is_not_valid".isidentifier()
"CamelCaseIsOkay".isidentifier()
"no spaces".isidentifier()**[Out]:** True
       False
       True
       False

可打印

检查我们的字符串中是否有没有直接打印到控制台的字符,比如换行符\n或制表符\t

"line 1".isprintable()

**[Out]:** True

"line 1\nline 2".isprintable()

**[Out]:** False

isspace

检查字符串是否只包含空格字符(包括换行符\n和制表符\t)。

" \n \t".isspace()

**[Out]:** True

ist title

这个检查每个新单词是否以大写字母开头。它忽略数字和标点符号:

"A Typical Title Of Something".istitle()
"123 Still A Title".istitle()
"IT DOES NOT WORK IF WE SHOUT".istitle()
"But not if we don't capitalize everything".istitle()**[Out]:** True
       True
       False
       False

仅此而已!Python 中字符串方法的数量(至少对我来说)非常惊人。虽然我很难看到zfill的用例(如果你用过,请告诉我),但其余的看起来确实有用。

我希望这篇文章至少向您介绍了一些 Python 的字符串方法。

如果您有任何问题或建议,请随时通过 Twitter 或在下面的评论中联系我们。

感谢阅读!

其他方法

我已经排除了更多的字符串方法,因为它们要么是不言自明的,要么是常识。这些是:

capitalize
endswith
find
format
isdigit
islower
isnumeric
isupper
lower
lstrip
replace
rstrip
strip
startswith

如果您喜欢这篇文章,您可能会喜欢我写的另一篇文章,它讲述了 Python 的四个不太为人所知但却非常有用和有趣的特性:

[## 4 个非常有用的 Python 特性

四个不太知名但非常有用的 Python 功能

towardsdatascience.com](/4-super-useful-python-features-993ae484fbb8)

用 Schrutepy 包进行 Python 文本分析

原文:https://towardsdatascience.com/python-text-analysis-with-the-schrutepy-package-234bc70f3916?source=collection_archive---------13-----------------------

这个新的 python 包让文本分析变得有趣

By: 布拉德·林德布拉德
LinkedIn|Github|博客 | 推特

随着 {schrute} R 包的成功,许多请求都是针对移植到 Python 的同一个数据集。 schruteschrutepy 包只有一个目的:从Office加载整个脚本,这样你就可以用这个有趣的数据集执行自然语言处理、文本分析或其他什么。

快速启动

使用 pip 安装软件包:

pip install schrutepy

然后将数据集导入数据框架:

from schrutepy import schrutepy df = schrutepy.load_schrute()

就是这样。现在你准备好了。

长示例

现在,我们将快速学习一些常见的基本文本分析功能。

**from** schrutepy **import** schrutepy
**from** wordcloud **import** WordCloud, STOPWORDS, ImageColorGenerator
**import** matplotlib.pyplot **as** plt
**from** matplotlib.pyplot **import** figure
**import** nltk
**from** nltk.corpus **import** stopwords
**from** PIL **import** Image
**import** numpy **as** np
**import** collections
**import** pandas **as** pd

用 load_schrute 函数加载整个脚本

df = schrutepy.load_schrute()

有些唱片没有对白

df = df.dropna()

为整个系列中的所有文本创建一个单词云

text = " ".join(review for review in df.text)print ("There are {} words in the combination of all review.".format(len(text)))#There are 3001517 words in the combination of all review.# Create stopword list:
nltk.download(**'stopwords'**)
stopWords = **set**(stopwords.words(**'english'**))

# Generate a word cloud image
wordcloud = WordCloud(stopwords=stopWords, background_color=**"white"**).generate(text)

# Display the generated image:
# the matplotlib way:
plt.figure(figsize=[**30**,**15**])
plt.imshow(wordcloud, interpolation=**'bilinear'**)
plt.axis(**"off"**)
plt.show()

让我们对一些角色做同样的事情。函数会有所帮助。

**def** plotDunder(character, df):
    mydf = df[df.character == character]
    text1 = **" "**.join(review **for** review in mydf.text)
    # Generate a word cloud image
    wordcloud = WordCloud(stopwords=stopWords, background_color=**"white"**).generate(text1)

    # Display the generated image:
    # the matplotlib way:
    plt.figure(figsize=[**15**,**7**])
    plt.imshow(wordcloud, interpolation=**'bilinear'**)
    plt.title(character)
    plt.axis(**"off"**)
    plt.show()fav = [**"Michael"**, **"David Wallace"**, **"Dwight"**, **"Jim"**, **"Pam"**, **"Oscar"**, **"Phyllis"**, **"Creed"**, **"Ryan"**,]for i in fav: 
  plotDunder(i, df)

让我们做一个德怀特大头的形状

dwight_mask = np.array(Image.open("schrutepy.png"))# Create a word cloud image
wc = WordCloud(background_color=**"white"**, max_words=**1000**, mask=dwight_mask,
               stopwords=stopWords, contour_width=**1**, contour_color=**'grey'**)

# Generate a wordcloud
wc.generate(text)

# show
plt.figure(figsize=[**30**,**15**])
plt.imshow(wc, interpolation=**'bilinear'**)
plt.axis(**"off"**)
plt.show()

wc.to_file(**"final_schrute.png"**)

现在让我们找出并画出我最喜欢的角色最常用的单词

**def** commonWord(character, df):
    mydf = df[df.character == character]
    text = **" "**.join(review **for** review in mydf.text)
    wordcount = {}
    # To eliminate duplicates, remember to split by punctuation, and use case demiliters.
    **for** word in text.lower().split():
        word = word.replace(**"."**,**""**)
        word = word.replace(**","**,**""**)
        word = word.replace(**":"**,**""**)
        word = word.replace(**"\""**,**""**)
        word = word.replace(**"!"**,**""**)
        word = word.replace(**"“"**,**""**)
        word = word.replace(**"‘"**,**""**)
        word = word.replace(**"*"**,**""**)
        **if** word not in stopWords:
            **if** word not in wordcount:
                wordcount[word] = **1**
            **else**:
                wordcount[word] += **1**

    # Print most common word
    n_print = **int**(**10**)
#     print("\nOK. The {} most common words are as follows\n".format(n_print))
    word_counter = collections.Counter(wordcount)
    **for** word, count in word_counter.most_common(n_print):
        **pass**
    # Close the file
    # Draw a bar chart
    lst = word_counter.most_common(n_print)
    df = pd.DataFrame(lst, columns = [**'Word'**, **'Count'**])
    df.plot.bar(x=**'Word'**,y=**'Count'**, title=character)for i in fav: 
  commonWord(i, df)

如果你喜欢这个包,就在 Github 上开始吧,这样更多的人可以从中得到乐趣。

想要更多这样的内容? 订阅此处

原载于 2020 年 1 月 18 日 https://technistema.com

Python:布尔混淆

原文:https://towardsdatascience.com/python-the-boolean-confusion-f7fc5288f0ce?source=collection_archive---------7-----------------------

if valif val is not None不一样!

照片由 Hitesh Choudhary 在 Unsplash 上拍摄

哇,什么???Python(不)疯狂。

当你做if val is None时,你调用操作员is,它检查x的身份。即if val is value在这里,运算符检查两个操作数是否指向同一个对象。

*None*在 Python 中是单例的,所有的*None*值也是完全相同的实例。

但是…

你说if val,python 的表现就不一样了。如果期望一个布尔值,并且假设val不是布尔值,Python 自动调用val__bool__方法。

if val实际上是作为if val.__bool__执行的

令人困惑的是,bool(None)返回False,所以如果val为 None。这按预期工作,但还有其他值被评估为False.

最重要的例子是空列表。bool([])同样返回False。通常情况下,空单和None有不同的含义;None 表示没有值,而空列表表示零值。语义上,他们是不同的。

例子

为了更好地理解,这里有一些例子。

我们将针对不同的值执行以下条件块:

**if** val:
    print(**'if val'**)**if not** val:
    print(**'if not val'**)**if** val **is not None**:
    print(**'if val is not None'**)**if** val **is None**:
    print(**'if val is None'**)

1。无

val = Noneif not val
if val is None

2。【(空单)

val = []if not val
if val is not None

3。[27,37] (非空列表)

val = [27, 37]if val
if val is not None

4。0 (数字—零)

val = 0if not val
if val is not None

5。1 (数字—1/非零)

val = 1if val
if val is not None

6。一个物体

val = object()if val
if val is not None

很有趣,对吧?试试看!

Python:面向(有抱负的)数据科学家的(非官方)OOP 速成班!

原文:https://towardsdatascience.com/python-the-unofficial-oop-crash-course-for-aspiring-data-scientists-ed2ed3789bcf?source=collection_archive---------7-----------------------

类、属性、方法、继承、封装和所有其他你不确定是否真的需要的晦涩难懂的东西(剧透:为了你的理智,你需要!)

信用:Pixabay

背景

Python 正经历着市场需求和用户基础的巨大增长;无论您是开发人员、分析师、研究人员还是工程师,python 都有可能在您的领域中得到应用。这么多免费的教育资料(比如自动化枯燥的东西)进入门槛不能再低了。)奇怪的是,然而,我们看到了一个不寻常的结果:python 爱好者过早地停滞不前,终止了他们对 python 基础的研究,转而支持特定领域的库和框架学习。对于当今的数据科学文化来说尤其如此;在对字符串、列表和字典有了中等程度的熟悉之后,未来的 python 爱好者可以直接进入 Numpy、Pandas 和 Scikit-Learn(如果不是 Keras、TensorFlow 或 PyTorch 的话)。)

那么应该学习数据结构与算法(DS&A),从零开始实现二叉树或链表吗?开始特定领域的旅程,什么是合适的“我已经学了足够多的 python 基础知识”门槛?没有“一刀切”的答案;然而,我建议在进入回归、分类和聚类等主题之前,至少要熟悉 OOP。

什么是面向对象编程(OOP)?

为了理解 OOP,我们需要简单讨论一下函数式编程;无需深究这种范式,只需说,函数式编程将函数(动作)与数据(信息)分开,而 OOP 认为这是一种错误的二分法。你以前用过 python 的内置列表吗?()想必你已经注意到了append方法允许你在当前最后一个元素之后插入一个元素,自动增加列表的长度?(

恭喜恭喜!你喜欢 OOP。数据类型及其方法(附属于对象的函数)是一个内聚的整体,这是 OOP 的核心“本质”。

在下面的代码片段中,我将定义一两个类,并围绕一个已建立的机器学习任务演示一些基本的 OOP 概念,以便您可以看到 OOP 如何为数据科学家带来好处。我们将使用各种 ML 算法对 Yelp 评论进行分类。事实上,这个类将只接收两个强制参数,数据和您希望使用的模型。(当然还会有其他几种算法),但是,这个类架构将允许我们做以下事情:

(1)将数据分为训练集和测试集,(2)将数据预处理并向量化为 TF-IDF 令牌,(3)训练模型,(4)计算测试集性能的准确性和相关度量,以及(5)通过 pickle 保存模型。

这条管道将大大提高你的效率。和许多其他人一样,我在对基础知识有一个坚实的掌握之前就一头扎进了数据科学的材料中。我会上下滚动笔记本,寻找我之前定义的变量。如果我想比较 2 或 3 个模型的性能,这将成为一个噩梦,确保我引用了适当的变量名。您将会看到,使用下面的方法,比较模型性能将是一项微不足道的任务,并且(如果幸运的话)我将会把您变成一名 OOP 的传道者!

面向对象的情感分析

关于显示问题,见本 GitHub 要诀。

GitHub Gist

正如你在上面注意到的,我定义了两个类:DataSplitterClassifier。首先,让我们看看 DataSplitter 稍后我们将访问分类器。

DataSplitter 接收一个 dataframe、文本列的名称和情感列的名称。然后,train_test_split 用于为类分配以下属性:x_train、x_test、y_train 和 y_test。请注意,random_state 和 test_percent 参数有默认值。这意味着——除非您特别更改这些参数中的任何一个,否则两个类实例将具有相同的 x_train、x_test、y_train 和 y_test 属性。这将是有用的,因为我们可以直接比较 ML 模型,而不用担心它们是在(稍微)不同的数据集上训练的。

当 DataSplitter 类对象被实例化时,您可以非常简单地访问这些值:

import pandas as pd
data = pd.read_csv('yelp_samples_500k.csv')
d = data.sample(n=1000)ds = DataSplitter(data=d,x_var='text',y_var='sentiment')
ds.x_test>>>
267157    This place always has a line so I expected to ...
197388    Would give them a zero stars if I could. They ...

如您所见,self关键字将这些属性绑定到对象上。使用点符号,检索这些属性很简单。

继续我们的下一个 OOP 概念:继承!对于这个例子,我们将进入第二个类,分类器。继承仅仅意味着一个类从以前定义的类继承功能。在我们的例子中,分类器将继承 DataSplitter 的所有功能;请注意两点:(1) DataSplitter 在其定义DataSplitter()中没有接收任何要继承的类,而Classifier(DataSplitter) 接收了一个要继承的类。(2)分类器的__init__方法中使用了super关键字。这样做的效果是执行 DataSplitter 的 init 方法,然后继续执行所有其他特定于分类器自己的 init 方法的指令。底线是,我们训练/测试/分割我们的数据,而不需要重新输入所有代码!**

在 init 方法之后,你会看到__vectorize。注意定义前面的双下划线。这就是在 python 中实现封装的方式。封装意味着~对象拥有程序员不具备的访问属性和方法。换句话说,为了不分散程序员的注意力,它们被抽象(或封装)了。

from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()
c = Classifier(data=d,model_instance=nb,
x_var='text',y_var='sentiment')c.__vectorize('some text')
>>>
AttributeError: 'Classifier' object has no attribute '__vectorize'

换句话说,对象可以访问这些方法,但是,我们不能!

说到方法,如果你没有猜到的话——方法只是内置于类对象中的函数。我们上面定义的方法包括 _ _ 矢量化、_ _ 拟合、_ _ 评估准确性、度量、预测和保存。从向量化开始,我们使用 NLTK 的 word_tokenize 函数将所有单词和标点符号标记为一元语法。接下来,我们使用 NLTK 的 ngrams 函数来创建二元模型和三元模型

'I like dogs' #text
['I', 'like', 'dogs'] #unigrams
['I like', 'like dogs'] #bigrams
['I like dogs'] #trigram 

这种方法大大改进了 unigrams,因为它允许 ML 模型学习“好”和“不好”之间的区别。请注意,我没有删除停用词或标点符号,也没有词干标记。如果你对扩展功能感兴趣,这可能是一个很好的家庭作业!__vectorize 方法被传递给由 __fit 方法创建的管道属性。同样,__fit 方法训练提供给 init 方法的 ML 模型,并创建预测(preds。)下面的方法 __evaluate_accuracy 确定模型的二进制精度,并赋值为一个类属性,以便以后访问(无需多次重新计算。)接下来,我们的度量方法将为我们检索二进制准确性,或者打印分类报告(精度、召回等)。)我们的预测方法简单地产生了这个代码。向方法调用提供文本,要么分配一个类,要么返回属于类 1 的概率。(如果你对多类分类感兴趣,一些调整将是必要的——我将把它作为家庭作业留给你!)最后,save 方法接收一个文件路径并挑选整个对象。这很简单——我们所要做的就是打开 pickled 文件来访问所有的类方法和属性,包括完全训练好的模型!

from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()
nb_model = Classifier(data=data,model_instance=nb
,x_var='text',y_var='sentiment')nb_model.metrics()
>>>
'92.77733333333333 percent accurate'

让我们将它与随机森林分类器进行比较!

from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier()
rf_model = Classifier(data=data,model_instance=rf,
x_var='text',y_var='sentiment')rf_model.metrics()
>>>
'86.29666666666667 percent accurate'

似乎我们的朴素贝叶斯模型优于我们的随机森林分类器。保存我们的对象(并再次打开它以备后用)非常简单:

## saving
nb_model.save('nb_model') #will append .pkl to end of input## opening for later use
with open('nb_model.pkl','rb') as f:
    loaded_model = pickle.load(f)loaded_model.predict("This tutorial was super awesome!",prob=True)
>>>
0.943261472480177 # 94% certain of positive sentiment

我希望你喜欢这个教程;我的目标是——简洁、信息丰富、实用。为了满足所有这三个目标,一些 OOP 主题没有入选(例如,多态性)。)如果您觉得这有帮助,请发表评论。同样,如果你想让我探索类似的概念,请发表评论,我会看看是否能在未来的文章中有所体现。

感谢您的阅读——如果您认为我的内容没问题,请订阅!😃

给初学者的 10 个有用的 Python 技巧和窍门

原文:https://towardsdatascience.com/python-tips-and-tricks-for-beginners-62473d569d0a?source=collection_archive---------24-----------------------

有了实际例子

来源:像素

在这篇文章中,我们想和你分享 10 个对初学者有用的 Python 技巧和窍门。

Python 是数据科学家最需要的技能之一。除了为初学者提供免费的 Python 课程之外,我们还总结了这 10 个提示和技巧,应该可以帮助你完成日常的数据科学任务。

通过本初学者教程,您将学会如何:

  • 格式化字符串
  • 使用枚举、排序函数
  • 从函数中返回多个值
  • 使用 lambda 表达式,列出理解
  • 更多!

如果你想让你的 Python 编码更高效,不要错过这些技巧/窍门!

我们开始吧。

提示#1:显示方法/功能

Python 如此强大,有许多不同的方法和函数。对于初学者来说,要记住所有内容尤其困难。

我们如何找出 Python 对象的可用方法/函数?

针对初学者的第一个 Python 技巧是关于两种快速实现这一点的方法。

方法 1:代码完成特性

许多 ide(集成开发环境)或 Python 的代码编辑应用程序可以在您键入时自动完成代码。这个特性有助于加速编程。

例如,在 Jupyter Notebook 中,你可以输入一个函数/文件的前几个字符,等等。,然后按下标签的键填写剩余的项目。下面的屏幕截图显示了以字母“p”开头的可用自动完成选项。

方法 2: dir 函数

dir 函数在其参数中返回对象的有效属性列表,这意味着我们可以用它来返回对象的方法。

例如,让我们运行下面的 Python 代码,将 dir 应用于 string 对象。

这将返回一长串名字。

忽略列表开头带' __ '的那些特殊方法,可以找到一些有趣的方法,比如大写、查找、lower。

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

注意 : [dir](https://docs.python.org/3/library/functions.html#dir)只提供了一组有趣的名字,而不是完整的名单。但是当你不能回忆起一个你意识到的方法时,使用起来很方便。
除了 dir,还可以试试帮助功能。例如,help(str)将打印出关于 string 对象的帮助页面,其中包含了比 dir 更多的方法细节。

技巧#2:格式化字符串

打印字符串是数据科学项目中的一项常见任务。str 的 format 方法可以把多个变量和字符串拼凑在一起,用特定的方式格式化。

让我们看一个例子。

我们将定义三个变量价格已付变更(=已付—价格)。如果我们想打印出一个包含这些变量的完整句子,格式为美元呢?

我们可以使用下面的格式方法。

The item cost $11.29\. I paid $20.00\. I received $8.71 in change

技巧 3:枚举函数

当迭代一个对象,比如一个列表、字典或文件时, enumerate 是一个有用的函数。该函数返回一个元组,其中包含通过迭代对象获得的值和循环计数器(从 0 的起始位置开始)。当您想根据索引编写代码时,循环计数器尤其方便。

让我们看一个例子,我们可以为第一个和最后一个元素做一些特殊的事情。

我们用 enumerate 方便地打印了指示第一个和最后一个元素的字符串。

0: J
The first element!
1: u
2: s
3: t
4:  
5: I
6: n
7: t
8: o
9:  
10: D
11: a
12: t
13: a
The last element!

枚举功能也可以用于文件。

在下面的例子中,我们可以在跳出循环之前打印出 csv 文件的前 10 行。由于结果太长,我们就不在这里复制了。但是你可以在你所有的文件上试试。

技巧 4:在一个函数中返回多个值

在定义函数时,我们经常希望返回多个值。在这个 Python 技巧/窍门中,我们将在下面介绍三种常见的方法。

方法 1:返回一个元组

首先,我们来看看最方便的方法:返回一个元组。我们通常只在有 2 或 3 个值要返回时使用这种方法。当值的数量更多时,很容易忘记元组中值的顺序。

下面是一个示例函数 get_employee,它根据雇员的 ID 号,以元组的形式返回雇员的名和姓。

如果我们调用值为 0 的函数,您可以看到该函数返回具有两个值的元组:“Jane”和“Doe”。

first_name: Jane, last_name: Doe

这很好,但是当有更多的值要返回时,我们该怎么办呢?

让我们继续下一个方法。

方法 2:返回一个字典

第二种方法是返回字典。字典可以被认为是键:值对,所以我们可以命名返回的值,这比元组更清楚。

下面的例子与上一个相似。但是我们要求函数返回一个字典。

我们可以调用 id_num = 0 的函数。有了作为字典的结果,用它的键调用特定的值就更容易了。

first_name: Jane,
last_name: Doe,
title: Data Scientist,
department: A,
date_joined: 20190808

方法 3:返回一个命名的元组

我们讨论的最后一种方法是返回一个命名的元组。命名元组是具有命名字段的元组。它们像元组一样是不可变的,但也像字典一样提供命名。

命名元组为元组中的每个位置赋予意义,并允许更具可读性、自文档化的代码。它们可以在使用正则元组的任何地方使用,并且它们增加了通过名称而不是位置索引来访问字段的能力。

Python 文档

与字典相比,命名元组的主要优势是字段保证在对象中。对于字典,我们不确定是否所有的键:值对都在那里。

让我们看一个命名元组的例子。

我们必须在使用 namedtuple 对象之前定义它。在下面的代码中,我们创建了一个名为雇员的 namedtuple 对象,然后用它来保存值。

我们可以再次调用 id_num = 0 的函数来返回命名元组。

first_name: Jane,
last_name: Doe,
title: Data Scientist,
department: A,
date_joined: 20190808

我们还可以仔细检查返回对象的变量类型,即前面定义的 Employee namedtuple。

__main__.Employee

技巧 5: Lambda 表达式

λ表达式用于创建匿名函数,通常是单行的。

下面的例子展示了 lambda 如何缩短创建简单函数的代码。

下面两种方法返回相同的结果。

0     first
1    second
2     third
3     first
4     third
5     first
Name: address, dtype: object

技巧 6:排序函数

在这篇 Python 技巧文章中,我们将介绍有用的排序函数,以及列表和字典的例子。这是一项常见的任务,因为我们经常希望看到数据集中的顶部/底部值。

排序列表

让我们看一个使用 sorted 函数的列表的简单例子。

[1, 3, 5, 5, 8, 9]

注意:还有 list.sort()方法,但是我们更喜欢 sorted,因为它更通用并且创建一个新的列表。参见 Python 文档中更详细的比较。

分类词典

对于字典,排序稍微复杂一些,因为有键和值。

我们可以将 sorted 函数直接应用于字典,它将对字典的键进行排序。

['A', 'B', 'G', 'Q', 'T']

或者将字典的值排序如下。

[0, 3, 7, 8, 9]

或者按照下面 Python 代码中的键或值对整个字典进行排序。

{'A': 9, 'B': 8, 'G': 0, 'Q': 7, 'T': 3}
{'G': 0, 'T': 3, 'Q': 7, 'B': 8, 'A': 9}

技巧 7:条件表达式

如果你学过 Python 的基础知识,你应该熟悉 if-else 语句。当逻辑简单时,我们也可以在一行中使用条件表达式(或者三元运算符)。

让我们看一个基于下面布尔变量 is_raining 的例子。

下面的 Python 代码展示了这样做的传统方式。

Stay at home

然而,我们也可以使用下面的表达式。短多了!

表达式*x if C else y*首先计算条件,C 而不是 x,如果 C 为真,则计算 x 并返回其值;否则,计算 y 并返回其值。

Python 文档

技巧 8:列出理解

我们可以使用列表理解来创建列表,这比传统的方法要简洁得多。当新列表中的每个元素都是对另一个 iterable 对象的元素的一些操作的结果时,通常会使用它。

列表理解由括号组成,括号包含一个表达式,后跟一个*for*子句,然后是零个或多个*for**if*子句。结果将是一个新的列表,该列表是通过在表达式后面的*for**if*子句的上下文中评估该表达式而得到的。

Python 文档

这个例子展示了如何使用它来计算字符串中单词的长度,并将结果作为一个列表。

下面两种方法返回相同的结果,而列表理解代码要短得多。

[3, 3, 3, 3, 6, 2, 3, 3, 3, 2, 5, 2, 2, 3, 2, 3, 5, 3, 4, 5, 5, 3, 4, 1, 3, 5, 4, 6, 2, 3, 6, 2, 4, 3, 1, 5, 4, 1, 4, 2, 3, 9, 2, 4, 3, 3, 2, 2, 3, 3, 3, 4, 2, 4, 5, 2, 2, 3, 2, 3, 6, 2, 3, 7, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 4, 4, 4, 3, 2, 3, 3, 4, 3, 3, 3, 6, 4]

技巧 9:所有/任何功能

我们还想涵盖 Python 中的所有和任何函数。在进行多重比较时,它们很方便。

如果 iterable 对象的任何元素为 true,则 any 函数返回 True。下面的例子显示了它如何使编码更简单。

Toronto, Ontario

同样,如果 iterable 对象的所有元素都为 true(或者 iterable 为空),all 函数返回 True。下面是一个比较 all 函数和常用方法的例子。

Just Into Data

技巧#10:使用虚拟环境

如果您同时从事多个数据科学项目,学习和使用虚拟环境至关重要。

Python 的虚拟环境有哪些?

虚拟环境是一种 Python 环境,其中安装的 Python 解释器、库和脚本与其他虚拟环境中安装的解释器、库和脚本以及(默认情况下)安装在“系统”Python(即作为操作系统的一部分安装的系统)中的任何库相隔离。

Python 文档

可以使用 Anaconda(康达环境)、 virtualenv 、 pipenv 等工具创建和管理虚拟环境。

虚拟环境允许每个项目使用不同的 Python 环境,因此我们可以使用不同版本的 Python 和/或不同的库。

例如,假设我们使用 plotly 版本 3 在项目中创建图表。几个月后,plotly 版本 4 推出了新功能。由于该项目的代码在生产中运行良好,我们可以继续使用 Plotly 版本 3。但是对于任何新项目,我们希望使用 Plotly 版本 4 中的新功能。

在这种情况下,我们可以分别使用 plotly v3 和 v4 创建两个虚拟环境,并将它们用于旧项目和新项目。

问题解决了!

就是这样!希望这些 Python 提示和技巧对初学者有用。

你想先试试这篇文章中的哪个技巧?

请留下您的任何问题或任何其他问题的评论。

相关资源:

对于 Python 初学者,请查看我们的 pandas 和 numpy 教程继续学习:

[## 学习 Python 熊猫数据科学:快速教程-数据入门

这是一本帮助你快速学习和使用 Python 熊猫进行数据科学、机器学习的指南。熊猫是最…

www.justintodata.com](https://www.justintodata.com/learn-python-pandas-for-data-science-tutorial/) [## Python NumPy 教程:数据科学实用基础——深入数据

在本 Python 教程中,我们将向您展示数据科学和机器学习的 NumPy 基础知识。NumPy 是…

www.justintodata.com](https://www.justintodata.com/python-numpy-tutorial-basics-for-data-science/)

或者查看我们最近为初学者撰写的其他数据科学文章:

[## 成功的数据科学管道的 7 个步骤——直接进入数据

在本指南中,我们将讨论在实践中构建数据科学管道的步骤。数据科学有助于…

www.justintodata.com](https://www.justintodata.com/data-science-pipeline-steps/) [## 初学者的机器学习:算法类型概述——数据

在这个初学者教程中,我们将解释机器学习算法的类型和一些流行的算法。机器…

www.justintodata.com](https://www.justintodata.com/machine-learning-algorithm-types-for-beginners-overview/)

要获得更多有用的数据科学文章,注册我们的简讯!

从 Excel 过渡到 Python 的技巧

原文:https://towardsdatascience.com/python-tips-for-someone-transiting-from-excel-ce16c5f9cbac?source=collection_archive---------40-----------------------

关于提高生产率的最佳做法的讨论

米卡·鲍梅斯特在 Unsplash 上的照片

Excel 是最常用的数据分析应用程序之一。然而,由于其明显的局限性(即性能、行和列的限制),它可能不足以应付现代的需求。Python 狂热者经常推荐使用 pandas 库作为 Excel 的替代品来自动化/加速数据分析。然而,如果不熟悉这个库和一些最佳实践,它不一定能节省时间。本文旨在强调使用 Excel 进行数据分析的一些挑战,以及使用 python(和 pandas 库,以及一些附加库)和采用最佳实践如何有助于提高整体效率。假设有一些 python 和熊猫的基础知识。

典型的数据分析师可能夹在业务职能部门和应用程序所有者之间。这种职责分离的结果是,可能没有一个人会理解数据的所有用例。热心的数据分析师有责任对数据进行切片和切块,形成基于输入的假设,并寻找反例来反驳假设。这种递归过程需要数据分析师手动重复过滤、排序、分组、连接、透视、vlookup、拆分 Excel 上的数据。这些步骤可能会导致人工错误,特别是当操作没有往返时(即结果取决于操作的顺序)。解决这个问题的关键是从根本上改变方法:不要机械地操纵数据来准备输出,而应该进行战略性的思考,并准备一个生成输出的程序/脚本。对我来说,这是使用 python 的最大动机,因为重复一系列动作的纯手工和令人麻木的任务不适合我,基于 Excel 的宏太慢了。

这篇文档/文章涵盖了一些有用的想法,通过使用 python,特别是 pandas 库,人们也许可以利用这些想法来节省一些时间并潜在地避免重复工作。

一些警告

在以下情况下,使用 pandas 几乎没有什么价值:
(a)数据量相当小(少于 50000 行),
(b)涉及的文件非常少(少于 10 个),
(c)为将来使用而准备的输出数据不太可能频繁重复或手动重复。

在这些情况下,Excel 将是首选工具,这取决于您对 python 和 pandas 的熟悉程度。

Python 和 pandas 的图形用户界面非常有限,因此大多数人可能会发现,与 Excel 相比,它的使用不够直观。例如,重新排序列和查找值是否存在这样的琐碎操作在 Excel 中是微不足道的,但是这些操作需要 python 上的几行代码,并且对于第一次接触 python 和 pandas 的初学者来说不那么直观。

在 ide 上,文本编辑器…

虽然有一些不错的工具(PyCharm 和 Jupyter 笔记本)可用,但是在我参与的项目中,这些工具是不可用的或者不可行的(工具是可用的,但是没有足够的 RAM 来使用它)。因此,我经常只使用命令提示符或 Powershell 来运行代码。任何带语法高亮的文本编辑器都可以,我用的是 Notepad++。

1.一次读文件,泡菜,以后读泡菜。

虽然这听起来微不足道,但数据分析师的首要任务是打开并读取数据文件。pandas 将 Excel 文件作为数据帧读取所花费的时间比 Excel 打开相同的 Excel 文件所花费的时间要长。当文件很大(尤其是 Excel 文件)时,将数据文件作为数据帧读取可能需要几分钟时间。如果每次执行脚本时都需要读取数据集,那么执行和编辑脚本的重复过程将大大降低效率。克服瓶颈的方法是读取数据并将其作为 pickle 写入(即.pkl)。读泡菜快 100 倍左右。通过获取数据样本(例如大约 10,000 行)并将其作为 pickle 写入,可以进一步提高效率。还应注意,读取平面文件(.csv和带分隔符的文本文件)比读取 excel 文件快得多,因此,如果可能,应检索/请求此类平面文件格式的文件,假设这些数据文件不打算由用户在 Excel 中打开。

2.规划好功能和代码结构

每个开发人员在开始一个全新的项目时都会说:“这一次,我会把事情做好。”。

在每个项目的开始,自然倾向于编写硬编码脚本来快速生成必要的报告。在这个阶段,可能会有一个单独的.py文件,它有一个巨大的功能,可以创建一个单独的报告。抵制匆忙生成输出报告的冲动,因为长函数很难维护,而且技术债务积累得非常快。

因为可读性很重要,所以每个函数在逻辑上应该代表一个预期的操作。下面的示例显示了读取、清理、修改数据以及将两列相乘以在另一列中给出结果的推荐方法。

示例 1.1(推荐):

def read_trade_data():
   return pd.read_excel('data.xlsx')def clean_trade_data(df):
    # PRICE turns out to be text with comma as decimals point.
    df['PRICE'] = df['PRICE'].str.replace({",", "."})
                             .astype('float64')
    return dfdef add_col_total(df):
    df['TOTAL'] = df['QTY']*df['PRICE']
    return dfdef df_output():
    df = read_trade_data()
    df1 = clean_trade_data(df)
    df2 = add_col_total(df1)
    return df2

示例 1.2

def df_output():
    df = pd.read_excel('data.xlsx')
    df['PRICE'] = df['PRICE'].str.replace({",","."})
                             .astype('float64')
    df['TOTAL'] = df['QTY']*df['PRICE']
    return df

虽然示例 1.1 看起来更冗长,但有几个好处。由于这种分离,df_output在 1.1 中可读性更好,因为在读取修改数据的代码之前,不必通读所有代码来清理数据。此外,如果期望其他数据帧具有类似的十进制格式,可以重用 1.1 中的clean_trade_data函数。另一方面,在 1.2 中,人们可能会求助于复制和粘贴代码。此外,如果在df_output上发生错误,在 python shell 中调试 1.1 的小函数会更容易。

应使用可选参数和关键字参数为数据分析师提供灵活性;应该避免执行类似操作的小函数。

如果预期只有一个版本的data.xlsx,这就足够了:

def read_trade_data():
    return pd.read_excel('data.xlsx')

然而,如果有多个版本的data.xlsx可能需要单独分析,那么编写一个函数来从命令行快速读取这些文件而不是重新编译代码是有意义的。

例 2.1(反例):

def read_trade_data1():
    return pd.read_excel('data1.xlsx')

将会有许多这样的函数,每个映射到不同的文件(例如data2.xlsxdata3.xlsx、…),随着时间的推移,它会变得非常混乱。

示例 2.2(推荐):

def read_trade_data(file=None):
    d_file = {1: 'data.xlsx',
              2: 'data1.xlsx'}
    file = file if file else d_file[max(d_files.keys())]
    return pd.read_excel(file)

例 2.2 有几个优点:
(a)所有文件都在同一个字典里(可读性很重要!),
(b)如果没有任何东西作为文件参数传递,它读取最新的dataN.xlsx
(c)如果文件没有被添加到files字典,它允许用户显式地传递文件的路径。

许多编码教程都包含类似于示例 2.1 的代码,因为教程的上下文可能不支持使用推荐的最佳实践。

这些功能中的一些可以被做得足够通用,以至于它可以被应用于其他项目。例如,输出数据摘要(即每列唯一值的数量)并打印一小份数据样本通常非常有用。这样的函数应该作为 dataframe 类的方法编写(这样它就可以作为方法 df.summarize()而不是 mymodule.summarize(df)被调用)。此外,如果这样的函数一般适用于任何数据集,那么应该将它重构到一个单独的 python 文件中,并导入到主项目文件的名称空间中。

下面是我的工具箱中的一个函数(pypo.py)。

有时在 Excel 中查看和执行分析会更快。但是导航到文件然后双击它会很麻烦。该功能通过在 Excel 文件被写入后打开该文件来升级to_excel()方法。

#pypo.pyfrom pandas.core.base import PandasObjectdef to_excelp(df, *arg, **kw):

    def xlopen(path):
        import win32com.client as w32
        from pywintypes import com_error
        import os #Opens the file (.csv, .xls, .xlsx) in Excel
        xl = w32.Dispatch('Excel.Application') try:
            wb = xl.Workbooks.Open(path)
        except com_error:
            print('Check if file exists in current working directory…', end='')
            if path in os.listdir():
                print('found!')
                path = '{}\{}'.format(os.getcwd(), path)
                wb = xl.Workbooks.Open(path)
            else:
                print('not found! Please check file path!')
        else:
            pass
        finally:
            xl.Visible = True
        return path, xl, wb

    df.to_excel(*arg, **kw)
    path, xl, wb = xlopen(arg[0])
    return path, xl, wbPandasObject.to_excelp = to_excelp

将它添加到主工作文件的名称空间中:

#main.pysys.path.insert(1, 'C:/Documents/pyprojects/pypo.py')
import pypo

在命令行界面中编写脚本时,可以简单地键入df.to_excelp('test.xlsx')来将数据帧写成 Excel 文件,该文件将在编写后打开。该函数还返回 Excel 应用程序xl和工作簿wb对象,可以在以后使用(也许是为了在 MsExcel 中自动格式化和创建表格?).

评估器可用于扩展 python,虽然这些可以简化pypo.py中的代码,但会导致主文件中的语法稍微冗长一些(例如df.myfunc.to_excel())。回想一下“平的比嵌套的好”。

3.过滤和选择数据

直观的方法是编写在过滤和/或选择相关行或列后返回数据帧的函数。然而,追加或连接这些数据帧是缓慢的。目前发现的最佳实践是编写返回布尔掩码的函数。组合其他掩码可以使用二进制操作完成,获得行数将成为 simpler⁴,尽管不太直观。

示例 3.1:返回数据帧

# Returns a dataframe
def apples(df):
    return df[df['Fruits']=='Apples']df_apples = apples(df)

如果不需要进一步的转换(如合并、排序、过滤),直观的方法会更快。

示例 3.2:返回布尔掩码

def select_apples(df)
    return df['Fruits']=='Apples'mask_apples = select_apples(df)

获取苹果的行数:sum(mask_apples)
获取包含苹果的数据帧:df[mask_apples]

这种方法避免了数据帧的设置成本,直到它真正需要时。使用布尔掩码的操作比数据帧上的操作快得多。

4.使最佳化

尽可能避免在数据帧的行或列中显式循环;这就是熊猫的全部意义。在大多数情况下,np.wherenp.selectnp.cutnp.vectorizedf.apply应该可以做到。其中一些方法本质上仍然是循环,但通常比显式循环快。

5.避免在 Excel 中使用 vlookups,而是合并表格

Excel 的 vlookup 函数从来不是用来合并两个表格的。Quoting docs.microsoft , vlookup “在第一列中搜索,并从表数组中的另一列返回同一行中的值”。当表数组的第一列包含多个查找值时,将使用找到的第一个值的行号。与查找值匹配的后续值将被忽略。因此,使用pandas.merge来代替,并处理重复条目(如果有的话)。如果首选 vlookup ,确保查找值是唯一的。

Vlookup 看表的右边;“向左看”需要结合使用 choose() 和数组公式,如果不熟悉数组公式的行为,这很容易出错。有些人可能会通过插入一个辅助(重复)列来避免“向左看”,这会引入某种形式的冗余。

这篇文章的动机是整合作者在各种项目中的学习,以便读者在工作中处理大量数据时不必经历类似的痛苦。在撰写本文时,互联网上有大量关于数据科学、大数据等方面的信息。然而,在实现用于数据分析的开源框架时,几乎没有关于最佳项目实践的指导。

[1]在某种程度上,这不是一个公平的比较,因为 Excel 文件被设计为在 MsExcel 中打开,pandas 在幕后做了很多神奇的事情,将数据准备为 dataframe。

[2]从 12 个 excel 文件中读取 1 张需要 30 分钟。总共 170 万行。读取泡菜当量需要 15 秒。

[3]如果你对此一笑置之,是因为你理解或看到了这样的恐怖,那很好。如果你想知道这有什么问题,请继续阅读。

[4] sum(mask),因为True值在 python 中被求值为 1。

Python 交易工具箱:回溯测试的温和介绍

原文:https://towardsdatascience.com/python-trading-toolbox-05-backtesting-84266edb1d59?source=collection_archive---------8-----------------------

交易工具箱

从头开始测试交易策略

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

我们通过引入一些基于价格的指标开始了这个系列。我们的目标是使用指标、价格和交易量来做出投资决策:选择何时买入或卖出金融资产。在我们的投资决策过程中,我们可以用不同的方法来整合价格、交易量和指标。第一种,也是最传统的一种,是以任意的方式解释他们的模式,就像技术分析的追随者所做的那样。指标也可以在一个更量化的方法中使用,作为交易系统的组成部分,消除投资过程中的人为判断。算法交易尤其是一种基于交易策略的方法,这种方法在没有人工干预的情况下自行建立金融工具的头寸。我们还可以使用价格、交易量和指标作为更复杂的机器学习模型的一部分来进行投资决策。

一个明显的免责声明:这个帖子的内容仅用于教育目的。这里的所有例子都是作为学习练习提出的,它们绝不应该作为投资建议。

无论我们选择以何种方式使用我们的指标,我们都需要回答一个重要的问题:我们的指标或指标组合对我们的投资决策有多好?换句话说,使用任何指标会比根本不使用它们产生更好的结果吗?

可以帮助我们回答这个问题的过程被称为 回溯测试 。通过回溯测试,我们将交易或投资策略应用于历史数据,以生成假设的结果。然后,我们可以分析这些结果,以评估我们战略的盈利能力和风险。

这个过程有其自身的缺陷:不能保证在历史数据上表现良好的策略在现实交易中也会表现良好。真实的交易涉及很多因素,这些因素无法用历史数据来模拟或测试。此外,由于金融市场持续快速发展,未来可能会出现历史数据中没有的模式。然而,如果一个策略不能在回溯测试中证明自己是有效的,它很可能永远不会在真实交易中发挥作用。回溯测试至少可以帮助我们剔除那些没有价值的策略。

几个框架使得使用 Python 回溯测试交易策略变得很容易。两个流行的例子是Backtrader 。像 ZiplineBacktrader 这样的框架包含了设计、测试和实现算法交易策略所需的所有工具。他们甚至可以自动将真实订单提交给执行经纪人。

在这篇文章中,我们采用了一种不同的方法:我们想研究如何使用 Python、 pandas、和 NumPy 作为我们仅有的工具,从头开始构建和测试一个交易系统。我们为什么要这么做?首先,从头开始构建回溯测试是一个很好的练习,有助于详细理解策略是如何工作的。此外,我们可能会发现自己需要实现现有框架中没有的解决方案。或者,您可能想要开始创建自己的回溯测试框架的旅程!

回溯测试我们的第一个系统

我们可以使用 Python 和 NumPy 以及 pandas 创建一个基本的回溯测试。举个例子,我们将使用在纽约证券交易所交易的金宝汤公司股票的价格。我从雅虎下载了五年的交易历史。财务:文件在这里有。

我们首先设置我们的环境,并将价格系列加载到数据框中:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
pd.plotting.register_matplotlib_converters()# This is needed if you're using Jupyter to visualize charts:
%matplotlib inlinedatafile = 'data/CPB.csv'
data = pd.read_csv(datafile, index_col = 'Date')
# Converting the dates from string to datetime format:
data.index = pd.to_datetime(data.index)data

原始数据帧

基本策略

在我们的例子中,我们将测试一个基本的移动平均线交叉系统,该系统基于每日收盘价的 20 天指数移动平均线(EMA)和 200 天简单移动平均线(SMA)(在本例中使用调整收盘价)。只要 20 日均线从下方穿过 200 日均线,我们就会买入该股(持有多头仓位)。

我们将带有移动平均线的列添加到数据框中:

df = data.copy()sma_span = 200
ema_span = 20df['sma200'] = df['Adj Close'].rolling(sma_span).mean()
df['ema20'] = df['Adj Close'].ewm(span=ema_span).mean()df.round(3)

增加了 sma200 和 ema20

正如我们所看到的,通过使用 200 天的 SMA,我们在相应列的前 199 行中获得了 NaN 值。这只是一个练习,我们可以删除这些行来执行回溯测试。在实际操作中,我们可以考虑使用不同的指标来避免丢失大量数据。清除 NaN 值:

df.dropna(inplace=True)df.round(3)

移除了 NaN 行

让我们看看图表中的数据:

plot_system1(df)

移动平均价格

为了跟踪我们在数据框中的头寸,我们添加了一列,每一行都包含有多头头寸时的数字 1 和没有头寸时的数字 0 :

# Our trading condition:
long_positions = np.where(df['ema20'] > df['sma200'], 1, 0)
df['Position'] = long_positionsdf.round(3)

添加了位置列

无论我们试图实现什么规则,检查我们的信号是一个好主意以确保一切按预期工作。讨厌的错误喜欢隐藏在这种计算中:开始测试一个系统,然后发现我们没有正确地实现我们的规则,这太容易了。特别是,我们需要警惕引入任何形式的前瞻偏差:当我们在交易规则中包含在规则评估时实际不可用的数据时,就会出现这种情况。如果系统回溯测试产生的结果好得令人难以置信,那么前瞻偏差是最有可能的罪魁祸首。

我们可以通过检查数据框中的数字变量并在图表上绘制信号来检查信号。

要选择触发交易信号的日期:

buy_signals = (df['Position'] == 1) & (df['Position'].shift(1) == 0)df.loc[buy_signals].round(3)

信号被触发时的行

在这种情况下,我们只有三个信号。为了确保我们正确应用交叉交易规则,我们可以在选择中包括信号之前的日子:

buy_signals_prev = (df['Position'].shift(-1) == 1) & (df['Position'] == 0)df.loc[buy_signals | buy_signals_prev].round(3)

包括信号触发前的几天

到目前为止,一切看起来都很好:在信号出现之前的日子里,ema20sma200下方,在信号出现的日子里,它在上方交叉。我们可以对退出我们头寸的信号进行类似的检查:我把这个练习留给你。

我们可以在图表上标出信号的标记:

plot_system1_sig(df)

带信号标记的图表

从图表中,我们可以看到,买和卖的信号是不匹配的。第一个信号是卖出(没有买入),因为我们从系列开始时的多头头寸开始。最后一个信号是买入(没有卖出),因为我们在系列结束时保持多头头寸。

战略回报

我们现在可以用初始投资的百分比来计算我们策略的回报,并将其与买入并持有策略的回报进行比较,后者只是在期初买入我们的股票,并一直持有到期末。

我们将用来计算回报的价格序列是调整收盘价格:通过使用调整后的价格,我们可以确保在我们的计算中考虑到股息、股票分割和其他公司行为对回报的影响。

# The returns of the Buy and Hold strategy:
df['Hold'] = np.log(df['Adj Close'] / df['Adj Close'].shift(1))# The returns of the Moving Average strategy:
df['Strategy'] = df['Position'].shift(1) * df['Hold']# We need to get rid of the NaN generated in the first row:
df.dropna(inplace=True)df

添加了返回列

整个周期的回报只是每日对数回报的总和(我将在后面解释数学原理):

returns = np.exp(df[['Hold', 'Strategy']].sum()) - 1print(f"Buy and hold return: {round(returns['Hold']*100,2)}%")
print(f"Strategy return: {round(returns['Strategy']*100,2)}%")

输出:

Buy and hold return: -5.83%
Strategy return: 10.3%

这些回报涉及 1060 天的时间。如果我们想将它们与其他时期的回报进行比较,我们需要将它们按年计算:

n_days = len(df)# Assuming 252 trading days in a year:
ann_returns = 252 / n_days * returnsprint(f"Buy and hold annualized return: {round(ann_returns['Hold']*100,2)}%")print(f"Strategy annualized return:{round(ann_returns['Strategy']*100,2)}%")

输出:

Buy and hold annualized return: -1.39%
Strategy annualized return: 2.45%

除非你熟悉对数回报,否则你可能想知道为什么以及如何在回报计算中使用对数。这里有一点数学来解释这一点,如果你听起来很陌生的话。否则,请随意跳到下一部分。

在定量金融学中,使用对数来计算回报是很常见的:它们使得一些计算更容易处理。如果日收益率 R_t 定义为:

其中 P_t 是𝑡日的价格,对数收益𝑟_𝑡定义为:

通过应用一些基本代数,可以将每日日志回报计算为:

为什么对数回归如此方便?如果我们有一系列的日收益,我们需要计算整个周期的收益,用对数收益我们可以把它们加起来。相比之下,对于定期回报,我们需要一个乘法:

其中 T 是我们考虑的时间段的天数。计算年化回报率也变得更加容易。

更复杂的策略

我们刚刚测试的策略只有两种可能的头寸:我们要么多头(持有股票)要么空头(仅持有现金)。尝试和测试一种增加空头头寸可能性的策略(卖出借入的股票,并在退出头寸时买回)会很有趣。为了建立这个策略的例子,我们包括两个简单的移动平均线,一个是日高点,一个是日低点。我们还添加了 15 天指数移动平均线。我们根据以下规则建立头寸:

  • 当均线高于较高的均线时(加上 2%的偏移),我们做多(买入)
  • 当均线低于较低的均线时(减去 2%的偏移),我们做空(卖空)
  • 在所有其他情况下(均线和均线之间),我们不参与市场

我将失调添加到 SMAs 中,以减少错误信号的数量。让我们准备一个新的数据框:

df2 = data.copy()sma_span = 40
ema_span = 15df2['H_sma'] = df2['High'].rolling(sma_span).mean()
df2['L_sma'] = df2['Low'].rolling(sma_span).mean()
df2['C_ema'] = df2['Close'].ewm(span=ema_span).mean()df2.dropna(inplace=True)df2.round(3)

添加移动平均线

在这里,除了收盘价之外,我们还利用了最高价和最低价。为了在图表上绘制这些值,使用烛台**** 是个好主意。为此,我们将使用 mplfinance 库。如果您还没有这样做,您可以使用以下命令轻松安装 mplfinance :

pip install --upgrade mplfinance

为了将烛台图表与我们现有的样式相结合,我将应用 外部轴方法mplfinance :

**plot_system2(df2)**

带移动平均线的蜡烛图

我们可以更详细地检查任何特定的日期范围:

**plot_system2(df2['2019-07-01':'2019-12-31'])**

日期范围详细信息

然后,我们应用我们的交易规则并添加头寸列:

**offset = 0.02
long_positions = np.where(df2['C_ema'] > df2['H_sma']*(1+offset), 1, 0)
short_positions = np.where(df2['C_ema'] < df2['L_sma']*(1-offset), -1, 0)
df2['Position'] = long_positions + short_positionsdf2.round(3)**

添加了位置列

我们可以在图表上标出我们的信号:

**plot_system2_sig(df2)**

带信号标记的图表

这个系统比前一个系统有更多的信号,图表看起来很拥挤。我们可以详细查看任何日期范围:

**plot_system2_sig(df2['2018-12-01':'2019-05-30'])**

日期范围详细信息

我们应用与之前相同的计算来获得策略的回报:

**# The returns of the Buy and Hold strategy:
df2['Hold'] = np.log(df2['Adj Close'] / df2['Adj Close'].shift(1))# The returns of the Moving Average strategy:
df2['Strategy'] = df2['Position'].shift(1) * df2['Hold']# We need to get rid again of the NaN generated in the first row:
df2.dropna(inplace=True)returns2 = np.exp(df2[['Hold', 'Strategy']].sum()) -1print(f"Buy and hold return: {round(returns2['Hold']*100,2)}%")
print(f"Strategy return: {round(returns2['Strategy']*100,2)}%")**

输出:

**Buy and hold return: 17.04%
Strategy return: -5.25%**

和以前一样,我们可以按年计算回报率:

**n_days2 = len(df2)# Assuming 252 trading days in a year:
ann_returns2 = 252 / n_days2 * returns2print(f"Buy and hold annualized return: {round(ann_returns2['Hold']*100,2)}%")
print(f"Strategy annualized return: {round(ann_returns2['Strategy']*100,2)}%")**

输出:

**Buy and hold annualized return: 3.52%
Strategy annualized return: -1.09%**

在这种情况下,我们的策略实际上不如买入并持有策略。**

你可能已经注意到,我使用未调整的价格序列来评估信号,而我一直使用调整后的价格来计算回报。每当股息、拆分或其他公司行为造成价格缺口时,使用未经调整的价格评估信号有引入错误触发的风险。在这里,我只是用了一个价格系列,这个价格系列很常见,每个人都可以免费下载。如果我们只有未调整的价格,我们应该使用所有关于公司行为的信息来修正我们的回溯测试。

结论

这就是我们执行回溯测试和选择可以依赖的策略所需要的吗?肯定不是:在我们的回溯测试中,我们做了(尽管是隐含的)一些假设和简化,这些假设和简化会极大地影响我们的结果。首先,我们假设一只股票可以在信号触发当天的收盘价买入。实际上,这是不能保证的:实际价格将在信号发生后一天的范围内。那么,交易成本就不得不包括在内了。例如:

  • 支付经纪费是为了执行和清算我们的订单。
  • 买价和卖价之间的价差是成本的一部分。
  • 如果我们利用杠杆买入,我们需要支付利息。同样,如果我们借入股票卖空,我们需要支付贷款利息。

其中一些因素比其他因素更容易理解并包含在模型中。

当我们想要评估一个系统的性能并将其与其他系统的性能进行比较时,给定期间的回报只是我们想要考虑的众多性能和风险指标中的一个。一些例子是:

  • **成功交易与失败交易的百分比
  • 最大提款:我们累积利润的最高点和最低点之间的差额。
  • 收益的标准差夏普比率。****
  • 风险/回报比,这是我们每投资一美元,从一笔交易中可以获得的预期回报。

我们刚刚介绍的准系统回溯测试为计算所有这些指标和构建更现实的系统提供了起点。

出于所有这些原因,除非我们想从零开始建立一个完整的系统,如果我们需要实际地回溯测试一个策略,最好的选择很可能是使用一个完整的解决方案,比如 ZiplineBacktrader 。然而,当我从头开始编写回溯测试时,我仍然不断地学习很多关于指标和策略的知识,这是我绝对推荐的练习。

跟踪数据的 Python 技巧

原文:https://towardsdatascience.com/python-tricks-for-keeping-track-of-your-data-aef3dc817a4e?source=collection_archive---------11-----------------------

如何用列表、字典计数器和命名元组来跟踪信息

动机

在您的数据科学项目中,有时您希望跟踪数据中的信息,或者灵活地快速轻松地更新新输入的数据。了解如何使用 Python 的一些数据对象将使您在数据科学职业生涯中处理大量数据时保持条理并避免错误。

在本文中,您将学习如何:

  • 循环时跟踪索引
  • 更新新词典项目
  • 用可重复使用的对象记录新信息

我将从这些问题开始,然后详细介绍如何用 Python 工具解决它们。我希望这种方法能帮助你想象这些工具在你的代码中的应用。

照片由埃德加·恰帕罗在 Unsplash 上拍摄

循环时保持跟踪

假设你有一个朋友的名单。您希望在跟踪计数的同时遍历列表。你怎么能这样做?这可以通过enumerate轻松完成

>>> friends = ['Ben', 'Kate', 'Thinh']
>>> for i, item in enumerate(friends):
>>>     print(f'{i}: {item}')
0: Ben
1: Kate
2: Thinh

或者简单地用字典理解

>>> {i: friends[i] for i in range(len(friends))}
{0: 'Ben', 1: 'Kate', 2: 'Thinh'}

更新新词典项目:

你在用字典记录第一句话中的单词及其数量。

sent1 = {'love': 1, 'hate': 3}

但是当你读到第二个句子时,你想用新的句子更新你的旧字典。

sent2 = {'love': 2, 'flower': 1}

所以你新更新的单词包会变成这样:

{'love': 3, 'hate': 3, 'flower': 1}

这怎么可能呢?如果有一些工具可以让你轻松做到这一点,那不是很好吗?如果这就是你想要的,collections.Counter会支持你。collections.Counter该类允许集合中的元素出现不止一次

from collections import Counter
bag_words = Counter()sent1 = {'love': 1, 'hate': 3}bag_words.update(sent1)sent2 = {'love': 2, 'flower': 1}bag_words.update(sent2)bag_words

结果:

Counter({'love': 3, 'hate': 3, 'flower': 1})

不错!现在,随着你从其他句子中收集更多的信息,你可以很容易地更新你的单词包。要找出句子中有多少个独特的单词,你可以使用len

>>> len(bag_words)
3

或者想知道句子中的单词总数,你可以用sum

>>> sum(bag_words.values())
7

用命名元组定义可重用对象

你想记录朋友的信息列表,为他们的生日做准备。因为您现在还没有可用的信息,所以您想先创建一个占位符,以便以后输入信息。如果你想记录凯特的生日,喜欢的食物,颜色,以及她是否内向,你可以这样做:

>>> Kate = Friend('Feb', 'cake', 'pink', True)

当你不记得她的生日时,你可以打电话

>>> Kate.birthday
'Feb'

Python 中的 class 对象将使您能够实例化 Kate,但是您发现创建一个Friend类来保存简单信息需要时间。如果是这样的话,namedtuple就是你的 to-go 函数。nametuples允许您为您的记录定义一个可重用的对象,以确保使用正确的文件名

from collections import namedtupleFriend = namedtuple('Friend' , 'birthday food color introvert')Kate = Friend('Feb', 'cake', 'pink', True)Ben = Friend('Jan', 'fish', 'red', False)

显示关于 Kate 的信息:

>>> Kate
Friend(birthday='Feb', food='cake', color='pink', introvert=True)

如果你想知道本是内向还是外向,你可以打电话

>>> Ben.introvert
False

使用nametuples,您可以轻松地重用同一个对象来实例化新信息。

结论

厉害!你已经学会了如何使用enumerate、集合理解、Counternamedtuple来跟踪信息。我希望本教程能够为您提供额外的有用知识,将它们添加到您的数据科学工具包中。

在这个 Github repo 中,您可以随意派生和使用本文的代码。

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上联系我。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

[## 高效 Python 代码的计时

如何比较列表、集合和其他方法的性能

towardsdatascience.com](/timing-the-performance-to-choose-the-right-python-object-for-your-data-science-project-670db6f11b8e) [## 如何使用 Lambda 获得高效的 Python 代码

lambda 对元组进行排序并为您创造性的数据科学想法创建复杂函数的技巧

towardsdatascience.com](/how-to-use-lambda-for-efficient-python-code-ff950dc8d259) [## 使用 Python 最大化您的生产力

你创建了一个待办事项清单来提高效率,但最终却把时间浪费在了不重要的任务上。如果你能创造…

towardsdatascience.com](/maximize-your-productivity-with-python-6110004b45f7) [## cy thon——Python 函数的加速工具

当调整你的算法得到小的改进时,你可能想用 Cython 获得额外的速度,一个…

towardsdatascience.com](/cython-a-speed-up-tool-for-your-python-function-9bab64364bfd) [## 如何从头开始构建矩阵模块

如果您一直在为矩阵运算导入 Numpy,但不知道该模块是如何构建的,本文将展示…

towardsdatascience.com](/how-to-build-a-matrix-module-from-scratch-a4f35ec28b56)

您应该开始在 Python 中使用类型注释

原文:https://towardsdatascience.com/python-type-annotations-and-why-you-should-use-them-6f647c6b4e9c?source=collection_archive---------5-----------------------

照片由拉杜·弗罗林在 Unsplash 拍摄

因为动态类型化,你花了多少时间调试?

我刚开始学 Python 的时候,是 C 背景。我当时没有丰富的软件开发经验,动态打字提供的自由的滋味是如此甜蜜。利用多态和鸭类型的函数允许我用很少的资源做很多事情。

后来,随着我的经验增长,我开始参与大型项目,我逐渐明白这种自由是福是祸。随着贡献者的增加,代码越来越接近产品级,没有静态类型或类型检查会导致令人讨厌的意外。

这是 Python 生态系统中许多人的共同感受。然而,保持动态类型所允许的自由,但减轻其负面影响是困难的。

输入类型注释

如果 Python 中有一个特性可以最大限度地增加对代码的积极影响,同时只需要最少的努力,那就是类型注释。它允许开发人员与解释器(以及其他开发人员)有效地交流预期的参数类型和返回值,同时保持动态类型的优势。

Python 中的动态类型

那么,什么是动态类型呢?为了看到这一点,让我们来玩一玩。

在幕后,Python 中类似上面a的变量是指针,指向某一类型的对象。然而,指针并不局限于表示给定名称的固定类型的对象。这给了我们很大的自由。例如,函数可以接受任何类型作为参数,因为在后台会传递一个指针。

不幸的是,如果没有适当的照顾,这可能会很快出错。

动态类型是一把双刃剑

想想下面的例子。

这个简单的统计函数也适用于列表和 NumPy 数组!多牛逼啊!

嗯,不完全是。有很多方法可以烧伤你自己。例如,您可能会无意中传递一些东西,导致函数崩溃。如果我们用一个字符串调用函数,就会发生这种情况。

没有在字符串和整数之间定义除法运算符,因此错误。因为 Python 是一种解释型语言,所以这个问题不会出现,直到函数被错误的参数调用。这可能需要几周的运行时间。像 C 这样的语言可以在编译时间捕捉这些错误,以免出错。

事情可能会变得更糟。例如,让我们用字典调用函数。

执行成功,但是结果错误。当一个字典被传递时,minmax函数计算的最小值和最大值,而不是我们想要的值。这种错误可能会在很长一段时间内不被发现,同时,你会觉得一切都很好。

让我们看看我们能做些什么来避免这样的问题!

输入函数注释和类型提示

2006 年, PEP 3107 引入了函数注释,在 PEP 484 中扩展了类型提示。(PEP 是 Python Enhancement Proposal 的简称,是 Python 建议和讨论新语言特性的方式。)

函数注释就是“一种向 Python 函数添加任意元数据注释的语法”,如 PEP 3107 所述。实际情况如何?

类型可以用argument: type = default_value提示,返回值可以用def function(...) -> type提示。

这些根本不会被强制执行,并且会被解释器忽略。然而,这并不意味着它们没有惊人的用处!来说服你,看看我们能有什么收获!

通过代码完成加快开发速度

你有没有尝试过在记事本这样的准系统文本编辑器中开发?你必须输入所有的东西,并且时刻记住什么是什么。如果 IDE 不知道您正在使用的对象,它也帮不了您。

看看下面的例子。

PyCharm 中的自动完成

通过函数注释,IDE 知道data对象的类型,它是preprocess_data函数的返回值。因此,您得到了自动完成,这节省了大量的时间。

这也有助于使用函数。大多数情况下,定义是在一个完全不同的模块中,远离你调用它的地方。通过告诉 IDE 参数的类型,它将能够帮助您以正确的格式传递参数,而不必手动检查文档或实现。

代码作为文档

开发人员花在阅读代码上的时间比写代码多得多。我坚信伟大的代码是自文档化的。通过适当的结构和变量命名,很少需要注释。函数注释对此有很大贡献。只要浏览一下定义,就会发现很多关于如何使用它的信息。

类型检查

注释可以从函数外部访问。

这不仅对程序员有用,对程序本身也有用!在调用函数之前,如果需要,可以在运行时检查其参数的有效性。

基于 PEP 484,类型检查被带到了下一个级别。它包括typing模块,“为类型注释提供标准语法,开放 Python 代码以更容易进行静态分析和重构,潜在的运行时类型检查,以及(也许,在某些上下文中)利用类型信息的代码生成”,如 PEP 中所述。

举一个更具体的例子,typing模块包含List,所以通过使用注释List[int],您可以知道该函数期望(或返回)一个整数列表。

更进一步:用 pydantic 进行数据验证

类型检查带来了很多机会。但是,一直手动做就没那么方便了。

如果你想要一个稳定的解决方案,你应该试试 pydantic ,一个数据验证库。使用它的BaseModel类,您可以在运行时验证数据。

您甚至可以超越这个例子,例如为 pydantic 模型提供定制的验证器。

pydantic 是 FastAPI 的支柱之一,是 Python 中 web 开发框架的后起之秀。在那里, pydantic 可以轻松地为端点定义 JSON 模式。

如果您感兴趣,可以在下面找到这些框架的文档。(它们是很好的读物,所以我强烈推荐它们。)

[## pydantic

版本文档:1.6.1 使用 python 类型注释的数据验证和设置管理。pydantic…

pydantic-docs.helpmanual.io](https://pydantic-docs.helpmanual.io/) [## FastAPI

FastAPI 框架,高性能,简单易学,快速编码,准备生产文档…

fastapi.tiangolo.com](https://fastapi.tiangolo.com/)

结论

所以,我希望我已经说服你了。类型注释需要最小的努力,但是它们对你的代码有巨大的积极影响。它

  • 让您和您的团队更容易阅读代码,
  • 鼓励你心中有类型,
  • 帮助识别与类型相关的问题,
  • 启用正确的类型检查。

如果你还没有使用它,你应该现在就开始使用它!这是你只用少量工作就能做的最大的代码改进之一。

如果你喜欢把机器学习概念拆开,理解是什么让它们运转,我们有很多共同点。看看我的博客,我经常在那里发表这样的技术文章!

Python 单元测试指南——避免开发期间和之后的错误!

原文:https://towardsdatascience.com/python-unittest-avoid-bugs-during-development-any-beyond-99b85c176727?source=collection_archive---------74-----------------------

因为如果你在写代码的时候已经在测试了,那就把它做好

在 Unsplash 上由 Carlos Muza 拍摄的照片

为什么要测试?

单元测试是开发的一个重要方面。没有代码的基本运行,你如何知道它是否工作?此外,由于单元测试通常快速而简短,通过正确地管理单元测试,您还可以知道您是否破坏了旧代码或其他人的代码!

此外,提供如何使用您的代码的示例会容易得多,因为您已经有一个了!

错误而常见的方式

让我们从展示错误的和常见的方法开始。这里有一个简单的类对象:

正如你所看到的,这个类做的不多,它在给定的输入上需要一个数的幂。
现在,一个标准的开发人员会如何验证这段代码呢?

别这样。

这是不对的,为什么?原因很多。

  • 这是不可持续的——只是分散在许多文件中的小测试,没有任何方法来跟踪甚至重用这些测试。
  • 这很混乱,并且给我们的程序增加了未使用的代码块
  • 这确保了单元测试只发生一次,而不是每个新版本发布时被验证。

你可能会说——他可以删除它,但是他所有的单元测试工作都是徒劳的,你不会在每个版本中重用它来改进你未来的代码。

正确的方法

尼克·莫瑞森在 Unsplash 上拍摄的照片

有许多 python 测试模块,你可以为它们中的许多给出有力的论据,我喜欢 python unittest 有许多原因,它的简单快速有效,并且在标准库中。

如何让它工作

  • 测试类必须继承自 unittest。测试案例
  • 每个测试功能必须在测试 _ 中开始
  • unittest.main()运行来测试结果的函数。

3 个小小的规则,你就有了一个合适的单元测试,很简单,不是吗?

安特·汉默斯米特在 Unsplash 上的照片

选项

您不必使用 assert equal,您可以检查任何您喜欢的东西,从数字到类型,到引发正确的异常,任何您可以想象的东西,都在一个简单的标准库模块中,由 python 开发团队维护。

最后的想法

Python 单元测试模块是每个开发人员的必备工具,它将确保您编写良好和适当的单元测试,使它们可重用,这样您就不会用新的开发来破坏旧的代码,并保持代码的整洁和维护,添加 CI 或任何自动化,并且您会得到一个维护良好的程序,只需付出较少的努力,所以开始使用 python 单元测试模块吧,它太棒了!

如果你喜欢这些文章,请发表评论,关于 Python 标准库的更多内容,请就你关心的主题发表评论,我会写的

我希望你喜欢!

计算机视觉的 Python 实用函数

原文:https://towardsdatascience.com/python-utility-functions-for-computer-vision-640a7302eeb0?source=collection_archive---------45-----------------------

如何在处理图像分类时节省一些管理图像的时间。

如果你花足够的时间使用深度学习研究计算机视觉,你会知道在管理图像时,有些代码片段会反复出现。如果你像我一样,厌倦了总是编写 python 代码来遍历数据集中按类别划分的图像列表,或者从图像构建 NumPy 数组,这篇文章将对你有用。我还将解释如何构建一个模块,该模块可以用您最常用的函数从您机器上的任何脚本中导入。

向 PYTHONPATH 添加模块

在描述我认为在处理图像时有用的函数之前,我想解释一下你如何能够从任何脚本中调用它们而不必重写代码。这将通过在脚本中编写您的所有函数并将该脚本的位置添加到您计算机的环境变量中来完成。更具体地说,您希望添加一个名为 PYTHONPATH 的新变量。当您导入模块时,您为此变量指定的路径将包含在 python 搜索的目录中。如果您想查看这些目录的列表,您可以在脚本或笔记本中使用以下代码

import sys
print(sys.path)

第一步是在脚本中编写所有的函数和类,包括导入。例如,让我们将这个脚本称为 myutils.py。其次,您必须将这个文件的位置添加到您的环境变量中。我并没有重新发明轮子,而是把你引向这个来源的,它详细解释了对于任何操作系统,如何做这件事。最后一步是从另一个脚本中导入新创建的模块,就像处理任何其他模块一样:

import myutils
myutils.some_function()

或者

from myutils import another_function
another_function()

请注意,在这种情况下,为您的函数编写一些文档非常重要。在导入模块时,您将无法访问代码,并且很难记住函数的确切作用和参数的含义。我不会在这里包括我的文档,只是为了避免弄乱代码,避免与文本中的解释重复。

要求

在直接进入函数之前,列出运行这些函数所需的包是很重要的。它们都是标准的数据科学包,应该在实用程序脚本的顶部导入。当您导入模块时,不必再次导入它们。

将图像加载到 NumPy 数组

我想介绍的第一个函数非常简单,但对其他函数也很有用,可以很容易地避免代码中的大量重复。代码如下

此函数用于加载图像,并使用特定的插值方法调整其大小。插值部分听起来不是很有用,但事实证明 tensorflow 和 tf.keras 在默认情况下并不使用相同的方法,因此跟踪使用了哪一种方法会变得很有用。然后,图像被转换成一个 numpy 数组,它的像素被归一化为 0 到 1 之间的值,并且增加了一个维度。这些最后的步骤是使用这些图像作为神经网络的输入所必需的。

将数据集拆分为定型集和验证集

所有好影像分类项目的第一步都是获取一个数据集,并将其组织到不同的文件夹中,每个文件夹对应一个类别。完成后,您会希望将这些数据分成训练集和值集,以便能够评估深度学习模型的性能。这可以通过使用 sklearn 提供的 train_test_split 函数来实现。不过,这仅适用于 numpy 阵列,但直接在您的映像上进行更永久的分割会很有用。这就是下面的函数所做的。

这个函数只是将图像的路径作为输入。这些需要放在名为“train”的目录中,其中每个文件夹代表一个类。该函数在每个类中提取一定比例的图像,由参数“split”确定,并将它们移动到一个新创建的名为“val”的目录中。

从图像目录创建 numpy 数据集

一旦有了数据集,通常有三种方法可以将图像输入到 tf.keras 神经网络中:作为 numpy 数组、使用 keras 生成器或使用 tfrecords。第一个选项可能很烦人,所以下面的函数会处理必要的步骤。

同样,要使用该功能,必须将图像分离到文件夹中,每个文件夹专用于一个类别。用作输入的目录示例是使用前面的函数“train_split”创建的 train 和 val。使用“load_image”将所有图像加载到 numpy 数组中,并组合成一个大数组。从文件夹的划分中也创建了一系列标签。类别按照文件夹的顺序进行编号(通常是按字母顺序)。如果“onehot”参数为 False,则每个标签就是类别的编号。如果“onehot”为真,则每个标签被转换为一个独热编码向量。

寻找重复的图像

有时,当您自己制作数据集时,最终会得到同一图像的多个副本。如果数据集很小,这可以手动处理,但如果有成千上万的图像,这就成问题了。这就是为什么我构建了一个类来查找重复项并删除它们。网上已经有很多这样做的脚本了。这肯定不是最好的,但我喜欢的一点是,如果你的数据已经被分割,这也没关系。即使一个图像在 train 目录中,而另一个在 val 中,也会发现重复的图像。此外,图像之间的比较是以 numpy 数组的形式进行的,因此即使它比较慢,也比使用 hash 更准确。代码如下:

这个类的唯一输入是图像所在的目录。将检查目录中的所有图像,无论它们位于哪个级别。当类被实例化时,将会创建一个字典,其中图像根据它们的大小被分开。然后,您可以使用“find_duplicates()”方法来获取出现多次的图像列表。完成后,方法“show_duplicates()”允许您可视化多次出现的图像,并且您可以使用方法“delete_duplicates()”删除这些图像。

将通用函数应用于图像

我想讨论的最后一个函数乍一看似乎很简单,但是非常通用,所以这个策略可以变得非常强大。这个想法是再次工作的图像是分开的文件夹根据类别。该函数遍历所有图像,并对它们应用一个通用函数。

第一个参数是图像所在目录的路径。又一个例子是训练或阀。参数“do”是您想要的任何函数,它将图像的路径作为第一个参数。args 部分是指您想要的任何参数。使用函数时你在那里写的任何东西都将被用作函数 do 的参数。这些必须作为未命名的参数给出。如果想要指定命名参数,就必须使用kwargs。这种区别归结为“函数(2)”和“函数(数=2)”之类的东西的区别。

我认为这里需要一个例子。假设我想打印文件名和关键字。我将定义一个函数,并将其作为“work_with_files”的参数,如下所示:

该调用的输出如下所示:

filename: image1.jpg
filename: image2.jpg
filename: image3.jpg
...

我认为这些功能很简单,但是经常使用,值得与人分享。希望有人能从我花大量时间用 python 处理图像中学到的东西中受益。如果您有任何意见或问题,请告诉我:)

Python 变量赋值

原文:https://towardsdatascience.com/python-variable-assignment-9f43aed91bff?source=collection_archive---------5-----------------------

解释 Python 编程语言最基本的概念之一

本文旨在解释 Python 变量赋值是如何工作的。

阿里安·达尔维什在 Unsplash 上拍摄的照片

基础知识:变量—对象类型和范围

  • 变量存储可以在程序中使用和/或更改的信息。该信息可以是整数、文本、集合等。
  • 变量用于保存用户输入、程序的本地状态等。
  • 变量有一个,这样它们就可以在代码中被引用。
  • 要理解的基本概念是,在 Python 中,一切都是对象。

Python 支持数字、字符串、集合、列表、元组和字典。这些是标准的数据类型。我将详细解释它们中的每一个。

声明变量并赋值

赋值给变量设置一个值。

要给变量赋值,请使用等号(=)

myFirstVariable = 1
mySecondVariable = 2
myFirstVariable = "Hello You"
  • 在 Python 中,赋值被称为绑定。在上面的例子中,我们将值 2 赋给了 mySecondVariable。

注意我是如何将一个整数值 1 和一个字符串值“Hello You”赋给同一个 myFirstVariable 变量的。 这是可能的,因为数据类型在 python 中是动态类型化的。

这就是 Python 被称为动态类型编程语言的原因。

如果您想将相同的值赋给多个变量,那么您可以使用链式赋值:

myFirstVariable = mySecondVariable = 1

数字的

  • 支持整数、小数、浮点数。
value = 1 #integer
value = 1.2 #float with a floating point
  • 也支持长整型。它们的后缀是 L,例如 999999999999L

用线串

  • 文本信息。字符串是一系列字母。
  • 字符串是字符的数组
  • 字符串值用引号括起来:单引号、双引号或三引号。
name = 'farhad'
name = "farhad"
name = """farhad"""
  • 字符串是不可变的。一旦创建,就不能更改,例如
a = 'me'Updating it will fail:
a[1]='y'It will throw a Type Error
  • 当变量被赋予一个新值时,Python 会在内部创建一个新对象来存储这个值。

因此,创建了对对象的引用/指针。然后将这个指针赋给这个变量,这样,这个变量就可以在程序中使用了。

我们也可以将一个变量赋给另一个变量。它所做的只是创建一个指向同一对象的新指针:

a = 1 #new object is created and 1 is stored there, new pointer is created, the pointer connects a to 1
b = a #new object is not created, new pointer is created only that connects b to 1

变量可以有局部或全局范围。

局部范围

  • 例如,在函数中声明的变量只能存在于块中。
  • 一旦块存在,变量也变得不可访问。
def some_funcion():
  TestMode = Falseprint(TestMode) <- Breaks as the variable doesn't exist outside

在 Python 中,if-else 和 for/while 循环块不创建任何局部范围。

for i in range(1, 11):
    test_scope = "variable inside for loop"print(test_scope)

输出:

variable inside for loop

使用 if-else 块

is_python_awesome = Trueif is_python_awesome:
    test_scope = "Python is awesome"print(test_scope)

输出:

Python is awesome

全球范围

  • 可以从任何函数中访问的变量都具有全局范围。它们存在于 main 框架中。
  • 您可以在函数外部声明一个全局变量。需要注意的是,要给一个全局变量赋一个新值,你必须使用" global "关键字:
TestMode = True
def some_function():
  global TestMode
  TestMode = Falsesome_function()
print(TestMode) <--Returns False

删除“全局测试模式”这一行只会将 some_function()函数中的变量设置为 False。

注意:虽然我稍后会写更多关于模块的概念,但是如果你想在多个模块间共享一个全局变量,你可以创建一个共享的模块文件,比如 configuration.py,并在那里定位你的变量。最后,在您的消费者模块中导入共享模块。

查找变量类型

  • 如果要查找变量的类型,可以实现:
type('farhad')
--> Returns <type 'str'>

整数变量中的逗号

  • 逗号被视为一系列变量,例如
9,8,7 are three numeric variables

3.操作

  • 允许我们对变量进行计算

数字运算

  • Python 支持基本的 *、/、+、-
  • Python 也支持楼层划分
1//3  #returns 0
1/3 #returns 0.333
  • 此外,python 支持通过**运算符求幂:
2**3 = 2 * 2 * 2 = 8
  • Python 也支持模数(余数)运算符:
7%2 = 1

还有一个 divmod 内置方法。它返回除法器和模数:

print(divmod(10,3)) #it will print 3 and 1 as 3*3 = 9 +1 = 10

字符串操作

串联字符串:

'A' + 'B' = 'AB'

记住字符串是不可变的数据类型,因此,连接字符串会创建一个新的字符串对象。

重复字符串:

‘A’*3 will repeat A three times:  AAA

切片:

y = 'Abc'
y[:2] = ab
y[1:] = bc
y[:-2] = a
y[-2:] = bc

反转:

x = 'abc'
x = x[::-1]

负指数:

如果你想从最后一个字符开始,那么使用负索引。

y = 'abc'
print(y[:-1]) # will return ab

它还用于移除任何新的线路托架/空间。

数组中的每个元素都有两个索引:

  • 从左到右,索引从 0 开始,增量为 1
  • 从右到左,索引从-1 开始,递减 1
  • 因此,如果我们做 y[0]和 y[-len(y)]那么两者都将返回相同的值:“a”
y = 'abc'
print(y[0])
print(y[-len(y)])

寻找指数

name = 'farhad'
index = name.find('r')#returns 2name = 'farhad'
index = name.find('a', 2) # finds index of second a#returns 4

对于正则表达式,使用:

  • split():通过正则表达式将一个字符串拆分成一个列表
  • sub():通过正则表达式替换匹配的字符串
  • subn():通过正则表达式替换匹配的字符串,并返回替换次数

铸造

  • str(x):到字符串
  • int(x):到整数
  • 浮动(x):到浮动
  • 元组(列表):To 元组:print(元组([1,2,3]))
  • list(tuple(1,2,3)): To list: print(list((1,2,3)))

记住列表是可变的(可以更新),元组是不可变的(只读)

集合操作

  • 该集合是没有任何重复的无序数据集合。我们可以将集合变量定义为:
a = {1,2,3}

相交集

  • 得到两组的共同点
a = {1,2,3}
b = {3,4,5}
c = a.intersection(b)

器械包差异

  • 要检索两个集合之间的差异:
a = {1,2,3}
b = {3,4,5}
c = a.difference(b)

集合的联合

  • 以获得两个集合的不同组合集合
a = {1,2,3}
b = {3,4,5}
c = a.union(b)

三元运算符

  • 用于在单行中编写条件语句。

语法:

【If True】If【表情】Else【If False】

例如:

Received = True if x == 'Yes' else False

对象身份

我现在将尝试解释客体同一性的重要主题。

每当我们在 Python 中创建一个对象,比如变量、函数等,底层的 Python 解释器都会创建一个唯一标识该对象的编号。一些对象是预先创建的。

当一个对象在代码中不再被引用时,它就会被删除,它的标识号可以被其他变量使用。

  • Python 代码被加载到位于堆栈中的帧中。
  • 函数连同它们的参数和变量一起被加载到一个框架中。
  • 随后,帧以正确的执行顺序加载到堆栈中。
  • 堆栈概述了函数的执行。在函数外部声明的变量存储在 main
  • Stacks 首先执行最后一帧。

如果遇到错误,您可以使用回溯来查找函数列表。

dir()和 help()

  • dir()-显示定义的符号
  • help()-显示文档

我们来详细了解一下:

考虑下面的代码:

var_one = 123
def func_one(var_one):
    var_one = 234
    var_three = 'abc'var_two = 456
print(dir())

var_one 和 var_two 是上面代码中定义的两个变量。与变量一起,还定义了一个名为 func_one 的函数。需要记住的重要一点是,在 Python 中,一切都是对象,包括函数。

在该函数中,我们将值 234 赋给了 var_one,并创建了一个名为 var_three 的新变量,并将值“abc”赋给了它。

现在,让我们借助 dir()和 id() 来理解代码

上述代码及其变量和函数将被加载到全局框架中。全局框架将包含其他框架需要的所有对象。例如,Python 中加载了许多内置方法,可用于所有框架。这些是功能框架。

运行上面的代码将会打印:

*[‘__annotations__’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’,* ***‘func_one’, ‘var_one’, ‘var_two’****]*

以 __ 为前缀的变量称为特殊变量。

请注意,var_three 还不可用。让我们执行 func_one(var_one)然后评估 dir():

var_one = 123
def func_one(var_one):
    var_one = 234
    var_three = 'abc'var_two = 456
func_one(var_one)
print(dir())

我们将再次看到相同的列表:

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'func_one', 'var_one', 'var_two']

这意味着 func_one 内的变量只在 func_one 内。执行 func_one 时,会创建一个帧。Python 是自顶向下的,因此它总是从上到下执行代码行。

功能框架可以引用全局框架中的变量,但任何其他功能框架都不能引用在自身内部创建的相同变量。举个例子,如果我创建一个新的函数 func_two,试图打印 var_three,那么它将会失败:

var_one = 123
def func_one(var_one):
    var_one = 234
    var_three = 'abc'var_two = 456 def func_two():
    print(var_three)func_one(var_one)
func_two()
print(dir())

我们得到一个错误,即名称错误:名称“var_three”未定义

如果我们在 func_two()内部创建一个新变量,然后打印 dir()会怎么样?

var_one = 123
def func_one(var_one):
    var_one = 234
    var_three = 'abc'var_two = 456 def func_two():
    var_four = 123
    print(dir())func_two()

这将打印 var_four,因为它是 func_two 的本地变量。

Python 中赋值是如何工作的?

这是 Python 中最重要的概念之一。Python 有一个 id()函数。

当一个对象(函数、变量等。)时,CPython 会在内存中为它分配一个地址。id()函数返回一个对象的“身份”。它本质上是一个唯一的整数。

例如,让我们创建四个变量并为它们赋值:

variable1 = 1
variable2 = "abc"
variable3 = (1,2)
variable4 = ['a',1]#Print their Ids
print('Variable1: ', id(variable1))
print('Variable2: ', id(variable2))
print('Variable3: ', id(variable3))
print('Variable4: ', id(variable4))

id 将按如下方式打印:

变量 1: 1747938368
变量 2: 152386423976
变量 3: 152382712136
变量 4: 152382633160

每个变量都被赋予一个新的整数值。

第一个假设是,每当我们使用赋值“=”时,Python 就会创建一个新的内存地址来存储变量。是不是百分百真实,不是真的!

我将创建两个变量,并将它们赋给现有的变量。

variable5 = variable1
variable6 = variable4print('Variable1: ', id(variable1))
print('Variable4: ', id(variable4))
print('Variable5: ', id(variable5))
print('Variable6: ', id(variable6))

蟒蛇皮印花:

变量 1:1747938368变量 4: 819035469000
变量 5:1747938368变量 6: 819035469000

注意 Python 没有为这两个变量创建新的内存地址。这一次,它将两个变量指向同一个内存位置。

让我们为变量 1 设置一个新值。记住 2 是整数,整数是不可变的数据类型。

print('Variable1: ', id(variable1))
variable1 = 2
print('Variable1: ', id(variable1))

这将打印:

变量 1: 1747938368
变量 1: 1747938400

这意味着每当我们使用=并给一个不是变量引用的变量赋值时,就会在内部创建一个新的内存地址来存储该变量。让我们看看它能不能坚持住!

当变量是可变数据类型时会发生什么?

变量 6 是一个列表。让我们给它添加一个条目并打印它的内存地址:

print('Variable6: ', id(variable6))
variable6.append('new')
print('Variable6: ', id(variable6))

注意,变量的内存地址保持不变,因为它是可变数据类型,我们只是更新了它的元素。

变量 6: 678181106888
变量 6: 678181106888

让我们创建一个函数,并向它传递一个变量。如果我们在函数内部设置变量的值,它在内部会做什么?让我们评估一下

def update_variable(variable_to_update):
 print(id(variable_to_update)) update_variable(variable6) print(’Variable6: ', id(variable6))

我们得到:

678181106888
变量 6: 678181106888

注意 variable_to_update 的 id 指向变量 6 的 id。

这意味着如果我们在一个函数中更新变量 _to_update,并且如果变量 _to_update 是可变数据类型,那么我们将更新变量 6。

variable6 = [’new’]
print(’Variable6: ', variable6def update_variable(variable_to_update):    variable_to_update.append(’inside’)update_variable(variable6)
print('Variable6: ', variable6)

这是打印的:

变量 6: ['新']
变量 6: ['新','内部']

它向我们展示了同一个对象在函数中按照预期被更新,因为它们有相同的 ID。

如果我们给一个变量赋一个新值,不管它的数据类型是不可变的还是可变的,一旦我们从函数中出来,这个变化就会丢失:

print('Variable6: ', variable6)def update_variable(variable_to_update):
    print(id(variable_to_update))
    variable_to_update = ['inside']update_variable(variable6)
print('Variable6: ', variable6)

变量 6: ['新']
344115201992
变量 6: ['新']

现在有一个有趣的场景:Python 并不总是为所有新变量创建一个新的内存地址。让我解释一下。

最后,如果我们给两个不同的变量赋值一个字符串值,比如' a '会怎么样。会不会产生两个内存地址?

variable_nine = "a"
variable_ten = "a"
print('Variable9: ', id(variable_nine))
print('Variable10: ', id(variable_ten))

注意,这两个变量有相同的内存位置:

变量 9: 792473698064

如果我们创建两个不同的变量并给它们分配一个长字符串值会怎么样:

variable_nine = "a"*21
variable_ten = "a"*21
print('Variable9: ', id(variable_nine))
print('Variable10: ', id(variable_ten))

这次 Python 为两个变量创建了两个内存位置:

变量 9: 541949933872
变量 10: 541949933944

这是因为 Python 在启动时会创建一个值的内部缓存。这样做是为了提供更快的结果。它为-5 到 256 之间的小整数和更小的字符串值创建了一些内存地址。这就是为什么我们例子中的两个变量有相同的 ID。

本文解释了变量是如何赋值的。

如果您想详细了解 Python,请阅读本文:

[## 关于 Python 的一切——从初级到高级

在一篇文章中你需要知道的一切

medium.com](https://medium.com/fintechexplained/everything-about-python-from-beginner-to-advance-level-227d52ef32d2)

Python vs(和)R 用于数据科学

原文:https://towardsdatascience.com/python-vs-and-r-for-data-science-4a32580846a4?source=collection_archive---------23-----------------------

用一组预定义的因素详细比较 Python 和 R

照片由来自佩克斯的克里斯蒂娜·莫里洛拍摄

当承担一个新项目时,选择正确的编程语言可能是程序员经常做出的最令人生畏的决定之一。

在为数据科学项目选择编程语言时, Python R 无疑是首选。多年来,R 和 Python 都从开发人员和用户那里获得了许多针对各种现代任务的积极反馈。乍一看可能很难决定两者中哪一个更好,但让我告诉你一些事情,即使它们在某些领域相似,例如是免费和开源的,它们都可以提供一些独特的和改变游戏规则的功能

使用 Python/R 的子社区示例:

  • 深度学习
  • 机器学习
  • 高级分析
  • 预测分析
  • 统计数字
  • 勘探和数据分析
  • 学术科学研究

在这篇文章的帮助下,我们想阐明一下 Python 和 r 的区别

Python 和 R 简介

● Python

Python 是程序员需要多大自由的一个实验。太多的自由,没人能读懂别人的代码;太少,表现力受到威胁。

吉多·范·罗苏姆

Python 作为一种高级通用编程语言1989 以来就一直存在,它是为了强调代码可读性而构建的。Python 鼓励开发人员为各种规模的项目编写清晰且符合逻辑的代码。Python 具有极强的可扩展性,附带了数百个库,扩展了它的核心功能,同时它的开源特性允许开发人员自由构建和共享定制库。

Python 还作为数据科学 、机器学习和深度学习的例外、工具,因为有几个包和库可用,例如 TensorFlow 熊猫 Keras NumPy

优点

●由于其易于使用的特性,在开发人员中非常受欢迎。

●支持多种编程范式,如面向对象和过程化。

●执行时间相对较短。

●拥有大量第三方库。

缺点

● Python 可能缺少 r 中一些流行库的替代品。

●动态类型有时会导致难以正确跟踪故障。

**** [## 面向数据科学的顶级 Python 库

面向数据科学的流行 Python 库概述

towardsdatascience.com](/top-python-libraries-for-data-science-c226dc74999b)

●R

Ross IhakaRobert Gentleman、 R 于 1993 年首次推出,旨在将无与伦比的统计计算和图形功能交给开发人员、统计人员、分析师和数据挖掘人员。它有一个命令行界面。

当谈到数据科学时,许多研究人员仍然更喜欢 R 而不是 Python** ,因为它具有强大的面向统计的特性交互式可视化能力。此外,使用 R 的框架,您可以为可操作的见解创建仪表板和交互式可视化。**

作为一种过程化语言,r 允许开发人员将问题的复杂部分分解成更小的块,从而使问题的解决更容易。

优势

●配备了一套强大的分析工具。

●有一系列用于增强其核心行为和能力的软件包。

RStudio IDE 和 Jupyter 等图形用户界面可以为已经强大的工具添加图形界面,同时添加更多功能,如集成帮助、代码调试器和代码完成。

●允许强大的数据导入选项,包括文件,如 Microsoft Excel。

●支持各种第三方包的扩展性。

劣势

● R 很难学,如果不小心使用会让事情走下坡路。

●某些库缺乏适当的文档会浪费开发人员的努力。

●比 Python 表现相对较慢。

** [## 数据科学应该选择 R 的 6 个理由?

最近更新由克莱尔 d。人工智能创新的加速增长已经导致几个…

blog.digitalogy.co](https://blog.digitalogy.co/r-for-data-science/)**

Python 与 R——详细比较

为您的下一个数据科学项目选择一种语言可能具有挑战性,尤其是当两种语言可以执行相同的任务时。既然介绍已经结束,我们将在接下来的部分中讨论这两种语言之间的比较,记住一组显著的特性,大多数开发人员会发现这些特性非常有用。

1。数据收集的差异

为了方便数据收集,Python 可以支持各种常用的数据格式,比如CSV、JSON 文件,甚至 SQL 文件。数据科学家在 Python 中广泛使用的另一个数据源是数据集。Python 还可以让你借助合适的库直接从互联网上提取数据。

尽管不像 Python 那样灵活,R 允许您通过 Excel、CSV 和文本文件导入数据。使用 Minitab 或 SPSS 等软件包构建的文件也可以转化为 r 中使用的数据框,使用Rvestmagritter等软件包可以帮助你从网上刮取并清理数据。****

2.数据探索的差异

Python 的各种库可以帮助你非常容易地分析结构化和非结构化数据。诸如熊猫、NumPy、 PyPI 等库无疑是数据探索的佼佼者。 Pandas 例如,允许您将数据组织到数据框中,使清理更简单。此外,熊猫甚至可以保存大量数据,同时提供额外的好处。

专为数据探索而构建的 R 提供了出色的结果,因为它是专为统计学家和数据挖掘者而构建的。使用 R,您可以应用一系列的测试和技术,比如概率分布,对您的数据进行数据挖掘。r 可以执行数据优化、随机数生成、信号处理,甚至提供对第三方库的支持。

3.数据可视化的差异

使用 Python,您可以以图形和图表的形式创建有效且可定制的可视化效果。像 IPythonmatplotlib这样的库的存在是为了帮助开发者和研究人员创建强大的交互式可视化。虽然 Python 生态系统包含更多的库,但最常用的是 matplotlib。

另一方面,R 可以提供高级可视化,因为它是编程语言提供的核心功能之一。r 自带了对许多标准图形的内置支持,对于更复杂的可视化,您可以使用库,如gg plot 2Plotly,以及Lattice**。**********

**** [## 你应该在 R 语言中学习的 7 种可视化

随着数据量的不断增加,没有可视化就不可能讲述故事。数据可视化是一种…

www.tatvic.com](https://www.tatvic.com/blog/7-visualizations-learn-r/)

4.数据建模的差异

对于数据建模,Python 提供了几个库来满足所需的建模类型。比方说,对于数值建模,Python 提供了其 NumPy 库,同样,对于科学计算,我们有 SciPy 。各种其他库和技术允许 Python 中有更多的数据建模选项。

在 R 中,由于编程语言提供了强大的统计能力,您可以高效地进行统计建模。它附带了大量的支持包来帮助您进行统计建模,甚至是特定的分析,如泊松分布、线性&逻辑回归。

5.表演

性能是任何编程语言的一个关键方面,它经常成为选择一种语言而不是另一种语言的主要原因。大多数程序员甚至数据科学家开始喜欢 Python 而不是 R 的一个关键原因是,Python 能够相对容易地快速执行大多数数据科学任务。Python 胜过 R 的另一个地方是它的执行速度相对更快。其他不利于 R 的因素可能包括缺乏特性,比如单元测试和代码可读性不够。

Python 性能提示—

https://wiki.python.org/moin/PythonSpeed/PerformanceTips

https://stack ify . com/20-simple-python-performance-tuning-tips/

[## Python vs. Node。JS:哪个最适合你的项目?

使用一组预定义的因素对 Python 和 Node.js 进行详细比较

towardsdatascience.com](/python-vs-node-js-which-one-is-best-for-your-project-e98f2c63f020)

6.图书馆

当谈到这些编程语言提供的包和库时,它们都为几乎每种情况提供了数千个有用的包。

PyPI 托管和管理 Python 的包,而 R 这边的事情则由 CRAN 处理。如果你对数字更感兴趣,Python 有超过25.7 万个包,而 CRAN 有一点超过 1.6 万个。太多了!

尽管 Python 确实提供了比 R 多 10 倍的包,但并不是所有的包都对数据科学有用。在阅读这些数字时,不要忘记 Python 是一种通用编程语言,而 R 不是。

[## 2020 年你必须使用的最好的 Python IDEs 和代码编辑器

具有显著特性的顶级 Python IDEs 和代码编辑器

towardsdatascience.com](/best-python-ides-and-code-editors-you-must-use-in-2020-2303a53db24)

7.流行

这两种编程语言在开发人员和数据科学家中相当受欢迎,是在他们的命令下添加的好选择。Python 似乎在这方面处于领先地位,因为它的通用性质和围绕数据科学的几个库的可用性,但是 R 紧随其后。

根据 StackOverflow 的说法,Python 是发展最快的主要编程语言。

[## 堆栈溢出开发者调查 2020

每个月,大约有 5000 万人访问 Stack Overflow 来学习、分享和建立他们的职业生涯。行业估计…

insights.stackoverflow.com](https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages-wanted)

一些统计学家和数据挖掘者仍然更喜欢 R,因为它具有强大的数字处理和可视化功能。此外,R 提供了对数据分析的更好的控制,因为它倾向于统计和数值计算,并收集了库,提供了更高级和更深入的结果来证实这一说法。

编程语言 R 持续上升,并有望成为 TIOBE 的 2020 年编程语言。

2020 年 8 月 TIOBE 指数(来源)

[## 10 家使用 Python 的世界级公司

Python 正被 IT 界的一些巨头积极使用

towardsdatascience.com](/10-world-class-companies-using-python-26cde24919a8)

8.工作机会

数据科学领域的工作机会正在增加,统计数据显示更多的工作需要 Python 而不是 R 。由于数据科学的快速发展,现在比以往任何时候都更需要这两种编程语言。

Python 作为一种全能的编程语言,可以成为一个可靠的总体选择,因为它可以让您进行软件工程,并提供一个进入数据科学的良好切入点。然而,如果您想在短时间内提取有价值的统计数据,制作漂亮的可视化数字,并为 web 应用程序创建图形界面,R 将是更好的选择。

9.社区

一个社区为开发者提供支持和指导,可以说它是开发者访问次数第二多的地方,仅次于项目代码。它在快速找到问题的根本原因和解决方案方面具有重要价值,同时提供了许多有用的提示。

当我们谈到一种编程语言的社区时,首先想到的是它的目标用户。通常,它包括开发人员,但是我们的案例也包括统计人员和数据挖掘人员。Python 被各种各样的受众使用,包括各种应用程序。另一方面,r 主要由企业和主要研究统计数据的研究人员使用。

不用说,这两种编程语言都提供了一个活跃的开发人员和贡献者社区,定期为其他人和语言提供宝贵的见解。

Python 社区—

[## 我们的社区

Python 编程语言的官方主页

www.python.org](https://www.python.org/community/)

RStudio 社区—

[## RStudio 社区

一个面向所有 R 和 RStudio 的社区

community.rstudio.com](https://community.rstudio.com/)

结论

这两种语言的竞争性质可能有助于我们为我们的目的产生最简单和最有效的代码。

在整篇文章中,我们讨论了 Python 和 R 之间的一些决定性因素,这些因素在选择一种编程语言时起着主导作用。我们可以得出结论,尽管这两种语言对于数据科学来说都是值得尊敬的选择,但它们仍然各有利弊。学习 Python 可以让您灵活地处理大多数以数据科学为中心的项目,而学习 R 可以让您更好地掌握数据科学中的统计学。学习这两者无疑会让您在即将到来的数据科学项目中占据上风,但我们希望让您来做最终决策。

注: 为了消除各种各样的问题,我想提醒你一个事实,这篇文章仅代表我想分享的个人观点,你有权不同意它。

更多有趣的阅读—

我希望这篇文章对你有用!下面是一些有趣的读物,希望你也喜欢

[## 面向所有人的顶级谷歌人工智能工具

使用谷歌人工智能中心将想法变为现实

towardsdatascience.com](/top-google-ai-tools-for-everyone-60346ab7e08) [## 机器学习和深度学习的最佳 Python 库

现代机器学习模型和项目的 Python 库

towardsdatascience.com](/best-python-libraries-for-machine-learning-and-deep-learning-b0bd40c7e8c) [## 用于自然语言处理的 Python 库

用于自然语言处理的流行 python 库概述

towardsdatascience.com](/python-libraries-for-natural-language-processing-be0e5a35dd64) [## 2020 年必读的数据科学书籍

看看吧,你为什么要读它们?

towardsdatascience.com](/data-science-books-you-must-read-in-2020-1f30daace1cb) [## 2020 年必读的机器学习书籍

看看吧,你为什么要读它们?

towardsdatascience.com](/machine-learning-books-you-must-read-in-2020-d6e0620b34d7)

关于作者

克莱尔 D 。在digital ogy是一个内容制作者和营销人员。这是一个技术采购和定制匹配市场,根据全球各地的特定需求,将人们与预先筛选的&顶尖开发人员和设计师联系起来。连接DigitalogyonLinkedinTwitterinsta gram*。***********

巨蟒大战山羊:蒙蒂霍尔重访

原文:https://towardsdatascience.com/python-vs-goats-monty-hall-revisited-548f4fbe036b?source=collection_archive---------44-----------------------

在熊猫的帮助下!

伊洛娜·弗罗利希在 Unsplash 上的照片

问题是

尽管有打熊猫的照片,我们还是要在这里讨论一下天魔堂问题。这个问题在统计学中很有名,可能已经在大学、网上或任何地方介绍了大多数统计学课程。包括我在内的大多数人从这个问题中学到的是,统计或概率思维不幸是违反直觉的,因此必须加以训练。或者换句话说:仅仅学习公式并不能让你通过这门课程。

但是现在回到实际问题:你是参加游戏节目的幸运儿。主要价格是昂贵的跑车,现在可能是电池电动的。在你和开着一辆崭新的汽车离开之间有三扇门。两扇门后是山羊,一扇门后是你的梦想之车。你如何打败山羊并得到汽车?

游戏规则是:你选择一扇门。然后,脱口秀主持人蒙蒂选择了另一扇门,打开门,门后总是一只山羊。所以其中一只山羊已经不碍事了。现在你做什么?换到第二个关闭的门或者坚持让你选择你的门的幸运感觉。我的第一直觉告诉我没关系:三扇门,一辆车有三分之一的机会。蒙蒂的行动无关紧要,他总是选择一只山羊。是吗?

战略

现在数学说它有,特别是考虑到一些托马斯·贝叶斯的工作。我看了不少关于这个问题的解释,也是在媒体上。没有一个能让我立即理解。那么为什么不试一试呢?由于没有无限数量的山羊和跑车,我选择在熊猫的帮助下模拟使用 Python。

这个问题可以很容易地分解成以下几个步骤:

  1. 我们把一辆汽车藏在其中一扇门后,两只山羊藏在另外两扇门后。
  2. 我们选择一扇门,由于缺乏进一步的随机信息,只是跟随我们的幸运感觉。
  3. 友好的智力竞赛节目主持人蒙蒂从另外两扇门中选择一扇门,打开门,总是有一只山羊走出来。
  4. 他让我们换门。我们该怎么办?
  5. 我们的大门敞开了。我们要么开着一辆崭新的汽车驶向夕阳,要么不得不接纳一只新宠物。

步骤 4 中只有一个决策点。在这一步中,我们将通过定义三个场景并模拟所有三个场景来帮助我们的 Python:

  • 场景 A :常变。
  • 场景 N :永远不变,坚持那种幸运的感觉。
  • 场景 S :有时会改变,因为缺乏对问题的更好理解,两个未打开的门之间有 50:50 的几率。

在什么情况下我们可以打败山羊?

解决方案

显然,我们在这里谈论的是概率。所以不幸的是,数学和 Python 都不能帮助幸运的智力竞赛节目嘉宾总是得到跑车。但是 Python 可以帮助我们获得概率,并真正相信它们。数学至少对我来说总是有困难。我将在这里解释一下代码。

目标是使代码尽可能地可读和可理解,并为此牺牲内存和性能的效率。所以你将能够找到更简洁有效的问题模拟。我们首先用熊猫数据框架来模拟蒙蒂霍尔问题。注意 number_games 是一个整数,它定义了我们想要玩的游戏的数量。

all_games = pd.DataFrame(index=range(0, number_games), 
                         columns=['Door_A', 'Door_B', 'Door_C',
                            'Choice_1', 'Choice_Monty', 
                            'Choice_A', 'Choice_N', 'Choice_S',
                            'Success_A', 'Success_N', 'Success_S'])for game_idx in all_games.index:
    # we put goats behind all doors
    all_games.loc[game_idx, ["Door_A", "Door_B", "Door_C"]] = 
                            ["Goat", "Goat", "Goat"]
    # oops, we forgot the car
    all_games.loc[game_idx, random.choice(["Door_A", "Door_B", "Door_C"])] = "Car"
    # the first choice is purely random
    all_games.loc[game_idx, "Choice_1"] = random.choice(["Door_A", "Door_B", "Door_C"])

如果你看一下生成的熊猫数据帧,你会发现在所有的游戏中,我们在门 A 到 C 中的一个门后面藏了一辆汽车,在另外两个门后面藏了两只山羊。我们已经选了一扇门。下一步是蒙蒂的选择,这就是奇迹发生的地方。他的选择绝非随意。

for game_idx in all_games.index:
    game = all_games.iloc[game_idx]
    # Monty will not open the door we chose
    monty_choices = {"Door_A", "Door_B", "Door_C"} - {game["Choice_1"]}
    monty_choices = list(monty_choices)
    # Monty will not open the door with the car
    if(game[monty_choices[0]] == "Car"):
        monty_choice = monty_choices[1]
    elif(game[monty_choices[1]] == "Car"):
        monty_choice = monty_choices[0]
    else:
        monty_choice = random.choice(monty_choices)
    # We store Monty's choice
    all_games.loc[game_idx, "Choice_Monty"] = monty_choice

在 Monty 做出选择后,我们应用了上面定义的三个策略:A 代表总是改变,N 代表从不改变,S 代表有时以 50:50 的概率改变。

# never change
all_games["Choice_N"] = all_games["Choice_1"]for game_idx in all_games.index:
    game = all_games.iloc[game_idx]
    # always change
    all_games["Choice_A"][game_idx] = ({"Door_A", "Door_B", "Door_C"} - {game["Choice_1"], game["Choice_Monty"]}).pop()
    # sometimes change
    all_games["Choice_S"][game_idx] = random.choice(list({"Door_A", "Door_B", "Door_C"} - {game["Choice_Monty"]}))

即使您不是 Python 爱好者,代码也应该是可读的。在总是变化的情况下,我们需要 pop() 从集合中获取唯一剩余的元素。最后一步是给我们的场景打分,如果我们真的有车,就给 1 分;如果我们的家里又多了一只有点难闻的四条腿,就给 0 分。我将把这个留给读者,或者你可以在我的 GitHub 上获得源代码。

一旦我们对模拟评分,我们可以简单地在数据帧上使用 describe() 来获得结果。在我运行的 10,000 个游戏中,我得到了以下值:

  • 场景 A:如果我们总是改变,获得汽车的概率为 0.6659。
  • 场景 N: 0.3341 概率如果我们从不改变,
  • 情景 S: 0.4980 概率如果我们有时改变。

摘要

查看我们代码的结果,我们可以看到,场景 A 总是切换车门,这让我们有三分之二的概率得到跑车。所以从长远来看,3 次中有 2 次我们会得到一辆车,而不是一只没用的山羊。为什么会这样呢?

我们坚持三扇门的情况,没有先前的信息,三分之一的机会。但是蒙蒂给我们看了一扇有山羊的门。所以他把这个问题转化为一个双门 50:50 机会得到汽车的问题。这反映在场景 S 中,有时会更换门。

但是在场景 A 中还有第三种选择,那就是永远改变。在我们的选择中,我们将车门分成两组:我们的车门有三分之一的机会挡住汽车,另外两个车门有三分之二的机会挡住汽车。因此,我们的选择正是通过这种划分来添加信息。

现在蒙蒂只用三分之二的机会看着两扇门的隔板。经过蒙蒂的选择,我们可以肯定的是,如果汽车一开始就在那里,那扇未打开的门就藏着汽车。所以我们总是选择那扇门,继承了三分之二的概率。

还是难以理解?是的,我同意。但是看看模拟给我们的数字。用 Python 模拟帮助我打败了山羊,并对问题有了更好的理解。希望它也能为你做同样的事情!只需获得代码,然后自己尝试一万次。这会给你一个合理准确的概率估计。有多精确?为此我不得不回到我的统计学课程上。

Python vs. Node。JS:哪个最适合你的项目?

原文:https://towardsdatascience.com/python-vs-node-js-which-one-is-best-for-your-project-e98f2c63f020?source=collection_archive---------1-----------------------

使用一组预定义的因素对 Python 和 Node.js 进行详细比较

蒂姆·范德奎普在 Unsplash 上拍摄的照片

当承担一个新项目时,选择正确的编程语言可能是程序员经常做出的最令人生畏的决定之一。

这一挑战背后的原因是,每个新项目都有一个独特的问题,在编程领域,没有什么大师是万能的。不同的编程语言有它们的优势和劣势,这使得它们适用于某些情况,但不是所有情况,这是众所周知的事实,有经验的程序员会同意这一点。在整篇文章中,我们将比较 Python 和 Node.js,以确定哪种情况下哪种更好。

Python 和 Node.js 是什么?

Python

GitHub 第二受欢迎的语言,也是机器学习最受欢迎的语言。

Python ( 前端和后端编程语言)是由开发的通用、面向对象的编程语言,它是动态类型化的,支持多种编程范式。****

Python 可以用来创建桌面、web、移动应用程序,并且附带了一个详尽的包和库集合来简化开发。Python 的便利特性使其成为后端开发的完美选择,使其成为最常用的编程语言之一。

编写 Python 代码的乐趣应该在于看到短小、简洁、易读的类,这些类用少量清晰的代码表达了大量的动作——而不是让读者厌烦得要死的大量琐碎代码。

吉多·范·罗苏姆

Python 初学者教程—

Python 初学者教程

Node.js-

Node.js( 主要用作后端框架)本质上是在 Google Chrome 的高性能 V8 JavaScript 引擎 上创建的 JavaScript 运行时环境,是提升 Node.js 代码效率的主要贡献者。

Node.js 由 Ryan Dahl 开发,可用于构建基于 JavaScript 的高效且可扩展的 web 应用程序,可用于前端和后端开发。

Node.js 初学者教程—

Node.js 初学者教程

Python 与 Node.js 详细比较

在本节中,我们将对 Python 和 Node.js 进行详细的比较,并记住一组预定义的因素,以便让您更好地了解这两个竞争对手的

1.速度和性能****

Python

Python 在性能上相对较慢,因为它在单个流中处理请求,不像 Node.js,在 node . js 中可以实现高级多线程。使用 Django 或许可以在一定程度上处理高负载,但这仍不能使其成为移动应用的可行选项。

Node.js

底层的 snappy V8 引擎极大地提高了 Node.js 代码的效率和速度,将 JavaScript 代码解释为机器语言,提供了出色的性能。

在开发实时 web 解决方案时,Node.js 应该是您的实际选项,例如需要生成和共享关键任务更新的通知系统。此外,由于其中的一些优化,Node.js 缩短了应用程序的加载时间。

赢家— Node.js 在这里领先。

** [## 2020 年你必须使用的最好的 Python IDEs 和代码编辑器

具有显著特性的顶级 Python IDEs 和代码编辑器

towardsdatascience.com](/best-python-ides-and-code-editors-you-must-use-in-2020-2303a53db24)

2.趋势科技

Giphy Source — SXSW 大会&节日

Python

在这个智能技术的时代,Python 已经成为趋势技术的首选,如机器学习、数据科学和物联网。对于 ML,社区中有一些工具和库以及专家为那些寻求帮助的人提供帮助。对于物联网,存在一个 Python 的口袋变体,称为 MicroPython ,它使 Python 能够支持物联网设备。

[## 机器学习和深度学习的最佳 Python 库

现代机器学习模型和项目的 Python 库

towardsdatascience.com](/best-python-libraries-for-machine-learning-and-deep-learning-b0bd40c7e8c)

Node.js

Node.js 也能够为物联网设备构建,但它更受欢迎的是开发包括实时通信在内的 web 应用程序。由于 JavaScipt 的流行,Node.js 越来越多地被用作服务器端框架

赢家— 选择取决于程序员和项目类型。

3.架构

Python

Python 没有内置对异步编程的支持,需要特殊的工具来启用该功能。一个这样的库是asyncio,开发者可以用它来创建异步事件驱动的应用。

Node.js

Node.js 架构包含一个单线程事件循环,当与 Node.js 的非阻塞特性结合时,允许它处理数千个并发连接,而不会导致线程上下文切换。这个特性使得 Node.js 成为实时 web 应用程序的最佳选择之一。

赢家— Node.js 比 Pythos 赢得更多积分,因为它提供了事件驱动的架构。

4.语法****

Python

Python 的语法让编写更少的代码行实现更多成为可能。Python 没有花括号,更容易理解和调试。凭借一点技术诀窍,大多数人可以轻松阅读 Python 代码,这使它成为非常适合初学者的编程语言。

Node.js

在语法方面,它与浏览器的 JavaScript 语法非常相似,在使用 Node.js 时,JavaScript 的先验知识应该不会构成挑战。

赢家— Python 因其用户友好的语法而胜出。

5.普遍性****

巨蟒

Python 可以用于前端和后端跨平台开发,并且与 macOS 和 Linux 捆绑在一起。Python 对于 web 和桌面开发来说是一种强大的编程语言,但是它在移动开发方面有所欠缺,这使得它成为一种不切实际的选择。

Node.js

Node.js 也常用于 web 应用程序的前端和后端开发,转化为两端对 JavaScript 的一致使用。Node.js 使得在 web、移动、物联网和云上开发各种跨平台应用成为可能,降低了开发成本和工作量。

赢家— 两者都是有能力的选项,在这里都是赢家。

6.可量测性

计算机编程语言

由于一些原因,Python 缺乏适当的可伸缩性支持。Python 代码的运行时解释使其成为一种较慢的编程语言。Python 也不支持多线程,原因是内部锁机制阻止它同时运行多个任务。但是这些限制可以通过使用 Python 实现来克服,如 CPython 或 Jython、更好的架构和负载平衡机制。****

节点. js

Node.js 很自然地提供了可伸缩性,因为它被构建到运行时环境中,并且带有一个集群模块,它能够处理你的机器的全部能力。Node.js 通过为垂直伸缩添加额外的资源和为水平伸缩添加更新的节点,使得 web 应用程序的垂直和水平伸缩更加容易。此外,可以使用下面列出的策略来管理扩展:

  • 克隆
  • 分解
  • 剧烈的

赢家— 与 Python 相比,Node.js 具有高度的可伸缩性,这一点很明显。

7.展开性

计算机编程语言

Python 可以通过几个框架轻松扩展,如 Django 、T3、Flask、 web2py ,以及更多用于全栈和纯 web 开发的框架。Python 也允许用 C 源文件中提供的 API 来扩展 C/C++编程语言的特性。Java 的python 实现,称为 Jython** ,可以简化脚本编写,轻松实现快速应用程序开发。**

节点. js

Node.js 附带了一个能够扩展其特性的框架池,其中一些是:****

  • Loopback.js —创建连接到另一个服务器的服务器 API
  • DerbyJS——创建成熟的实时网络应用程序
  • KOA . js——作为中间件,为开发者提供几个功能,以加快应用程序开发
  • Hapi.js —允许开发人员创建 JSON APIs

赢家——双方都赢了,因为他们能力相当。

8.错误处理****

Python

Python 的可读性和紧凑的语法使开发人员更容易找到和调试错误。还有一个普遍的观点是,Python 通常更容易也更擅长异常处理,与 Node.js 相比,这使它具有优势。

Node.js

每种编程语言或运行时都提供不同程度的错误处理,因为错误可能发生在应用程序生命周期的任何一点。类似地,Node.js 具有足够的调试错误的能力,并提供了优秀的异常处理选项。

****赢家——Python 和 Node.js 都在这方面胜出。

Giphy Source — RedBul l

9.图书馆****

计算机编程语言

Python 的包和库由 Python 默认的包安装程序 pip、 处理。谈到包的数量,Python 有超过 22 万个包,分布在数据科学、计算、图像处理等广泛的类别中,涵盖了您的大部分需求。

** [## 用于自然语言处理的 Python 库

用于自然语言处理的流行 python 库概述

towardsdatascience.com](/python-libraries-for-natural-language-processing-be0e5a35dd64)

Node.js

Node.js npm 是节点包管理器的缩写,处理 Node.js 中的包。npm 拥有超过130 万个包,以惊人的数量击败了 pip,证明了 Node.js 使用正确的包是多么可扩展。npm 上的所有软件包都可以通过简单的搜索方便地获得。

赢家— Node.js 凭借其庞大的软件包数量摘得桂冠。

10.最适合的应用

Python

Python 适用于小型和大型项目,更重要的是,它是数据科学产业的重要组成部分,其中大多数工具都依赖于 Python。除了应用程序开发之外,Python 还用于多个领域,例如:

  • 数据可视化
  • 图像处理
  • 神经网络
  • 机器学习
  • 语音和面部识别

节点. js

Node.js 基于事件的特性使其成为需要实时处理并发请求的应用程序的完美组成部分,包括从客户端到服务器的频繁数据传输。Node.js 还可以在实时解决方案中使用,例如:

  • 信使
  • 聊天机器人
  • 复杂的单页应用程序
  • 物联网实施
  • 协作系统
  • 流媒体平台

赢家— 两个竞争对手都因各自不同的实现而胜出。

11.社区

计算机编程语言

当谈到在线寻找支持和有用的资源时,Python 有一个大而有用的社区。论坛提供了一个很好的地方来发布你的问题,并从其他开发者那里获得建议,同时也为合作打开了新的大门。熟练的开发人员可以自由地为 Python 及其包做出贡献。对于企业来说,这是一个与顶级开发人员联系的绝佳场所。

节点. js

Node.js 社区拥有一群积极友好的开发人员,他们总是渴望为您提供帮助。该社区通过对 Node.js 及其各种软件包的开源特性做出贡献,甚至帮助您找到更好的职业机会,从而更容易展示您的才华。

赢家— 两者都有一个活跃且乐于助人的社区。

12.用例

巨蟒

Python 已经被部署到众多流行的项目中,下面是其中的一个简要列表:

  • Instagram 使用 Python 作为其后端服务
  • Google 从早期就开始使用 Python
  • Spotify 应用程序使用 Python 进行后端服务和数据分析
  • 网飞使用 Python,因为他们的开发者是这种编程语言的超级粉丝

Node.js

Node.js 是一个同样强大的平台,已经在无数的项目中使用,其中一些项目是业界知名的,例如:

  • LinkedIn 使用 Node.js 是因为它的可伸缩性和高效性带来的性能提升
  • PayPal 喜欢 Node.js,因为它可以让他们为超过 2 亿的用户提供服务,几乎可以使用任何一种货币,而不会有任何减速
  • Ube r 在 Node.js 上运行,因为它的伸缩性很好,可以毫不费力地处理越来越多的请求

13.初学者最佳书籍

计算机编程语言

  • Python 速成班
  • Head-First Python(第二版)
  • 艰难地学习 Python(第三版)
  • Python 编程:计算机科学导论(第三版)

节点. js

  • Basarat Ali Syed 的 starting node . js
  • Mario Casciaro 的 Node.js 设计模式
  • 实用 Node.js:构建真实世界的可伸缩 Web 应用程序
  • 西蒙·霍姆斯的《蒙哥、快车、角马和节点》**

结论

Python 和 Node.js 是程序员开发 web 应用的非常强大的选项。选择其中任何一个取决于两个核心因素,项目的目的和开发人员的技能。当然,它们都提供了优于另一个的特定优势,但是这些优势不应该超过项目的需求,因为总有办法解决缺点。此外,了解您的团队在这两种技术上工作的舒适程度总是有帮助的。我们希望这能消除您对 Python 和 Node.js 的一些疑虑,并让您更接近为下一个项目选择正确的选项。

注: 为了消除各种各样的问题,我想提醒你一个事实,这篇文章仅代表我想分享的个人观点,你有权不同意它。

更多有趣的阅读—

我希望这篇文章对你有用!以下是一些有趣的读物,希望你也喜欢

** [## 面向所有人的顶级谷歌人工智能工具

使用谷歌人工智能中心将想法变为现实

towardsdatascience.com](/top-google-ai-tools-for-everyone-60346ab7e08) [## 2020 年必读的机器学习书籍

看看吧,你为什么要读它们?

towardsdatascience.com](/machine-learning-books-you-must-read-in-2020-d6e0620b34d7) [## 数据科学家的最佳数据科学工具

数据科学工具,使任务可以实现

towardsdatascience.com](/best-data-science-tools-for-data-scientists-75be64144a88) [## 选择 PyTorch 进行深度学习的理由

PyTorch 可以为深度学习程序员提供很多东西

towardsdatascience.com](/reasons-to-choose-pytorch-for-deep-learning-c087e031eaca) [## 数据科学如何助推网飞

当有效使用时,数据可以神奇的方式改变您的业务,并将它带到新的高度。

towardsdatascience.com](/how-data-science-is-boosting-netflix-785a1cba7e45)

关于作者

克莱尔丁。是Digitalogy的内容制作者和营销人员,这是一个技术采购和定制匹配市场,根据全球各地的特定需求,将人们与预先筛选的&顶尖开发人员和设计师联系起来。在 LinkedinTwitterInstagram 上连接 Digitalogy 。****

Python 与 R 在数据科学中的对比

原文:https://towardsdatascience.com/python-vs-r-for-data-science-6a83e4541000?source=collection_archive---------16-----------------------

两大语言的特点、群体和行业地位的比较分析。

张杰瑞在 Unsplash 上拍照

Python 和 R 的比较多年来一直是业界的热门话题。r 已经存在了二十多年,专门用于统计计算和图形,而 Python 是一种通用编程语言,与数据科学和统计学一起有许多用途。许多初学者都有同样的问题:我应该从这两种伟大的语言中选择哪一种来开始学习数据科学?

计算机编程语言

Python 于 1991 年发布,作为一门极其简单的语言,它让你可以做几乎任何你能想象到的事情,它已经为自己建立了强大的声誉。它为网站后端服务本机桌面应用程序、图像处理系统、机器学习管道、数据转换系统等提供支持,并且它以语言的简单性而闻名,这使得它成为任何人最容易开始使用的编程语言之一。

这种语言的主要优点是:

  • 它的语法与本土英语非常相似,如此相似以至于大多数写得好的脚本大声读起来都有意义。
  • 它有一个巨大的社区围绕着它;对于你遇到的任何问题,可能有成百上千的人在网上问了同样的问题并得到了答案。
  • 它为你能想到的任何应用程序提供了大量的第三方模块和库。
  • 围绕这种语言有一个非常大的数据科学社区,这意味着有许多工具和库可以解决数据科学问题。
  • 它既支持面向对象编程又支持过程编程范例,让您根据自己的需要自由选择。

得益于这些优势,Python 成为业界最流行的语言之一也就不足为奇了。据 Ncube 报道,谷歌、Dropbox、网飞、Stripe 和 Instagram 等大型科技公司也使用这种方法。

r 项目

R Project 是一个 GNU 项目,由 R 语言、运行时和用它们构建应用的实用程序组成, R 是在这个环境中使用的解释语言。这种语言专门围绕统计计算和图形,这意味着它直接适合许多数据科学问题,而通过内置工具和围绕它的第三方库简化了数据科学项目

R 语言的优势是:

  • 它有许多专门用于数据操作的库和工具。这种语言和这些工具允许你容易地修改你的数据结构,将它们转换成更有效的结构,或者为你的特定用例清理它们。
  • 有很多非常流行的包和库,比如 tidyverse 负责端到端的数据操作和可视化。这些库允许您轻松开始数据科学任务,而无需从头开始编写所有算法。
  • 它有一个设计非常好的 IDE,叫做 RStudio。与语言本身集成,RStudio 提供了语法高亮、代码完成、集成帮助、文档、数据可视化和调试器,允许您在不离开屏幕的情况下开发 R 项目。
  • R 背后的团队一直非常专注于确保这些工具能够在所有平台上工作,由于这些努力 R 可以在 Windows、macOS 和 Unix 类操作系统上运行
  • 它拥有围绕构建基于网络的仪表盘进行数据分析和可视化的工具,比如 Shiny 允许直接从 r

除了这些优点及其在数据科学社区中的广泛使用,R 在数据科学项目中是 Python 的有力替代。

比较:Python 与 R

由于两种语言在理论上提供了相似的优势,其他因素可能会影响选择哪种语言的决定。

流行

这两种语言在数据科学界都很流行;然而,当谈到选择一种语言添加到您的工具链和经验中时,选择一种在行业中受欢迎的语言可能是有意义的,并且可能允许您过渡到您专业领域内的不同职位。

根据 Stack Overflow 的 2019 年开发者调查, Python 是 72525 名专业开发者中第 4 受欢迎的编程语言,最近甚至比 Java 还受欢迎。在同一次调查中, R 位于第 16 位。

Stack Overflow 2019 在专业开发者中的开发者调查结果

关于这些调查结果,需要记住的一点是,它们代表了栈溢出的开发人员社区,显然,这些数据并不特定于数据科学家;然而,这可能有助于更好地了解该行业的现状。

在同一个调查中查看全球范围内的工资,在 55,639 名参与者中,Python 和 R 似乎都站在同一点,R 的平均水平略高。

根据 Stack Overflow 2019 开发者调查的工资语言

除了调查结果,通过查看堆栈溢出趋势可以看出,就提问的问题数量而言, Python 比 R 更受欢迎。

堆栈溢出趋势

通过查看这些数据,在整个开发者社区中,Python 似乎比 R 更受欢迎;然而,重要的是要记住 Python 是一种通用编程语言,而 R 是专门用于统计计算的,这意味着这种比较在数据科学家中的受欢迎程度并不相同。

为了更好地了解数据科学,我们可以看看 2019 年 Kaggle 用户调查。事实上,他们在仪表盘上有一个专门的页面,是关于 Python 和 R T21 的。

根据 2019 年 Kaggle 用户调查,编程语言分布情况

正如在 Kaggle 数据中所看到的,在数据科学社区中,Python 比 R 有更大的用途,尽管这两种语言在用法上都有重要的地位。

图书馆

谈到数据科学,第三方库的可用性对于帮助您轻松入门非常重要。这两种语言周围都有非常活跃的社区,以及值得一看的丰富的软件包生态系统。

Python

  • NumPy: Numpy 是在数组数据结构之上实现各种数据操纵操作的基础包。它包含这些数据结构的高效实现,以及用于许多统计计算任务的通用功能,由于其高效的基础,它允许加速许多复杂任务
  • Pandas:Pandas是一个强大且易于使用的开源库,用于表格数据操作任务。它包含高效的数据结构,非常适合直观地处理带标签的数据。
  • Matplotlib:Matplotlib是一个用于创建静态或交互式数据可视化的库。由于它的简单性,您可以用几行 Python 代码创建非常详细的图形。
  • Scikit-learn: 作为 Python 生态系统中最受欢迎的库之一, scikit-learn 包含基于 Numpy、Pandas 和 Scipy 构建的工具,这些工具专注于各种机器学习任务,例如分类、回归和聚类
  • Tensorflow: 最初由 Google 开发并开源, Tensorflow 是一个非常受欢迎的开源库,用于开发和训练机器学习和深度学习模型。

R

  • Dplyr: Dplyr 是一个用于在内存中和内存外轻松处理表格数据的库。
  • Ggplot2: Ggplot2 是一个专注于的库,它以《图形的语法一书为基础,以声明方式构建数据可视化
  • data.table :类似于 dplyr, data.table 是一个为数据操作设计的包,具有表达性语法。它实现了高效的数据过滤、选择和整形选项,使您能够在将数据输入模型之前以所需的形状获取数据。
  • tidy verse:tidy verse是为数据科学设计的 R 包的集合。它包括许多流行的库,仅举几个例子: ggplot2 用于数据可视化, dplyr 用于直观的数据操作,以及 readr 用于从各种来源读取矩形数据。
  • Shiny: Shiny 是一个允许你从 R 构建高度交互的网页的包,它使构建仪表板变得轻而易举。
  • Caret: Caret 是专门用于预测模型和机器学习以及数据操作和预处理的工具和函数的集合。

看看库的数量和这些包的功能,似乎两种语言都有相似的包来简化许多数据科学任务。总而言之,对于许多任务来说,当一个任务在 Python 中可行时,它在 R 中也是可行的,并且需要非常相似的努力。

结论

尽管它们似乎提供了不同的东西,但这两种语言都有优缺点,需要仔细了解需求。

  • 如果你想从总体上进入编程领域,并寻找可以在软件开发的其他领域使用的东西,比如 T2、web 开发,那么 T4、Python、T7 是一种通用编程语言,T8 似乎是一个更好的选择。
  • 如果你熟悉其他科学编程语言,比如 MATLAB你可能会更容易学习 R 并利用它提高效率。这些语言之间有许多相似之处,特别是向量运算和关于矩阵运算的一般思维模式,而不是过程方法。
  • 如果你需要做特别的分析,并偶尔与其他数据科学家/技术人员分享,使用 PythonJupyter Notebooks可能会很好。如果您正在寻找为非技术利益相关者和内部使用构建快速仪表板的方法,那么利用 R 和令人惊叹的 闪亮的 库可能是个好主意。
  • 如果您需要开发 API来公开您的模型,或者您需要其他软件来与您的模型进行交互,那么投资于 Python 可能会对您有所帮助,因为它拥有围绕各种编程任务的巨大工具。你可以用一个非常简单的 API 用 Flask 或 FastAPI 来暴露你的模型,或者你可以用 Django 来构建成熟的生产就绪的 web 应用程序
  • 如果你想把所有的软件包都放在手边,主要关注于你的分析来做决策,并寻找最简单的设置来开始, R 可能是那里的首选工具。由于有了 RStudio 及其集成功能,无需离开窗口即可轻松从原始数据转换到可视化分析。尽管 Python 也很容易上手并且默认安装在许多系统,但多年来,它已经发展成不同的版本和不同的设置,因此,在您的计算机上建立一个功能良好的数据科学堆栈非常重要。

就像任何其他问题一样,解决方案主要取决于问题的需求并且除了“这取决于”之外,这个问题没有正确的答案这两种语言都非常强大,无论你将时间投入到哪一种语言中,如果你正在长期寻找数据科学的职业生涯,没有错误的答案;学习这两种语言中的任何一种都会在未来给你带来好处,因此,不要陷入分析瘫痪,只需选择一种,继续你的工作。众所周知,这两种语言都能够处理大多数数据科学问题,剩下的归结为方法学、团队的能力和手头的资源,它们大多独立于语言。

既然你已经设法读到这里,这里有一张可爱的浣熊的照片给你,保重。

照片由加里·本迪格在 Unsplash 上拍摄

Python vs R:基础知识

原文:https://towardsdatascience.com/python-vs-r-the-basics-d754c45c1596?source=collection_archive---------37-----------------------

一位有抱负的数据科学家关于在两种流行语言中做出选择的指南。

【图片信用】

大多数数据科学家将 Python 或 R 称为他们的“首选”编程语言。两者都有庞大的软件生态系统和社区,所以两种语言都适合几乎任何数据科学任务。

所以问题来了,一个有抱负的数据科学家应该先学哪种语言?长话短说,答案通常是 Python。然而,在潜水之前,每种语言都有自己的优缺点。

此外,值得注意的是,Python 和 R 并不是唯一可用于数据科学的编程语言或工具。其他一些包括 Scala、SAS、Julia、MATLAB 等等。

Python 是什么?

Python 是一种面向对象的高级编程语言,语法简单易学。它于 1991 年推出。Python 3.0 的 2008 修订版使得许多基于 Python 2 的旧库不再向前兼容。大多数数据科学工作现在可以通过五个主要的库来完成: Numpy,Pandas,Scipy,Scikit-learnSeaborn

Python 的主要优势是大规模的机器学习的植入。此外,它使可复制性和可访问性变得容易。如果您需要在应用程序或网站中使用您的分析结果,Python 是最佳选择。

R 是什么?

r 是一种用于统计计算和图形的编程语言和环境。r 基于 1976 年推出的 S。所以 R 有时候可以认为是过时的。然而,每天都有新的包被开发出来,使得这种语言能够赶上更“现代”的 Python。

R 与其他统计产品的最大区别是输出。r 有先进的工具来交流结果。例如, Rstudio 附带了库 ggplot2 ,这是一个可视化工具,可以生成盒状图、小提琴图、点状图、带状图等等。

数据科学家用的最多的语言是什么?

[我法师信用

如果你是这个领域的新手,很难知道是使用 Python 还是 r。然而,对于数据科学来说,没有“正确的”编程语言。一种语言并不比另一种更好——这完全取决于您的用例以及您试图回答的问题。

从 Kaggle 在 2019 年的研究中,很明显 Python 是目前数据科学的领先编程语言。SQL 是一种为关系数据库管理设计的语言,排在第二位。但是,在进行数据分析时,它可能会受到限制。由于 R 语言广泛的统计能力,它排在第三位。

Python vs R

[图片由作者提供]

这张图表远非详尽无遗,专家们对哪种语言是“高级”语言争论不休。但是最后,Python 程序员和 R 程序员经常互相借鉴很棒的想法。比如 Python 的 plotnine 数据可视化包,灵感来源于 R 的 ggplot2 包。而 R 的 rvest 网页抓取包的灵感来源于 Python 的 BeautfiulSoup 包。

同样值得注意的是 Python 和 R 之间的互操作性。您可以使用 rpy2 包从 Python 运行 R 代码。您可以使用 reticulate 从 R 运行 Python 代码。这意味着一种语言中的所有特性都可以从另一种语言中访问。

语法上的差异

Python 的“禅”在于有一种合适的方式来编写任何东西。r 没有这一套约定,所以对于初学者来说,编程可能不太结构化,也很难学。让我们来看四个基本的例子,看看它们的异同。

变量:

我们已经知道,我们可以用' = '操作符在 Python 中给一个变量赋值。

python_apples = 3

在 R 中,我们必须使用一个箭头操作符来给变量赋值。

r_apples <- 3

列表:

类似地,Python 中的列表可以赋给变量,括号内可以是任何数据类型。这些列表可以用元素的索引号进行索引,从 0 开始。

python_list = [1, 'two', 3.0]
python_list[1]
> 'two'

R 中的列表有点不同。它们被括在括号中,每个元素都有一个名称。这些元素可以用“$name”进行索引,但也可以用元素位置进行索引。请注意,R 中的索引从 1 开始,而不是通常的 0。

r_list <- list(num=42, greeting='Hello')
r_list$greeting
> 'Hello'

矢量:

Python 向量只是数字数组。我们可以使用 NumPy 执行算术和附加操作,例如标量乘法和点积。

python_vec = [10,20,30,40,50]

在 R 中,我们用' c()'而不是括号来包装向量,每个元素都可以通过索引来访问。可以通过给向量赋值“空”来删除向量。

r_vec <- c(10,20,30,40,50)

导入 CSV 文件:

对于 Python,我们必须导入流行的 pandas 库来访问数据帧。一般来说,Python 严重依赖库来执行特定的任务。

import pandas as pd
df = pd.read_csv('file_name.csv')

r 内置了 readr 库函数,优点是速度更快,数据类型解释一致。

library(readr)
r_df <- read_csv('file_name.csv')

这些只是 Python 和 r 中不同语法的基本例子。当你更深入地使用矩阵、数据帧和其他数据结构时,事情显然会变得复杂得多。尤其是 r。

但是从表面上看,每种语言的语法都有很多相似之处。例如,两种语言都使用“#”来注释。此外,单引号和双引号在两种语言中可以互换。

哪种语言最适合数据科学初学者?

对于初学者,建议使用 Python,因为这种语言的语法“可读”。它也比 R,甚至其他编程语言更有表现力,更简洁。

最终,Python 似乎是当今数据科学家最广泛使用的编程语言。它允许集成 SQL、TensorFlow 和许多其他用于数据科学和机器学习的有用函数和库。拥有超过 70,000 个 Python 库,可能性是无限的。

也就是说,数据科学家(或者一般的程序员)不应该局限于一种语言。在一个项目中结合两个领先的软件生态系统来获得一组独特的互补功能是可能的。

虽然 Python 和 R 都是数据科学的绝佳选择,但员工背景、您从事的项目以及您所在行业的文化等因素会影响您的偏好。我鼓励所有有抱负的数据科学家对这两种语言保持开放的心态。在学习 Python 时,请留意 R 可以派上用场的例子。

Python vs R:如何使用 Shiny &机器学习分析 4000 份招聘广告

原文:https://towardsdatascience.com/python-vs-r-what-i-learned-from-4-000-job-advertisements-ab41661b7f28?source=collection_archive---------26-----------------------

来源: iStockphoto

一个带有源代码的端到端 ML 系统

作为一名 10 多年的承包商,我总是盯着招聘信息板,问自己

  • 有多少角色符合我的技能组合?
  • 这些角色工资多少?
  • 什么技能是需要的?
  • 我应该把学习的重点放在哪里?

2018 年 KDnuggets 发布了文章“Python 蚕食 R:2018 年用于分析、数据科学、机器学习的顶级软件:趋势和分析”。在我的职业生涯中,我已经转换过一次语言——也许是时候把 Python 加入进来了?

我发现自己在查看招聘信息。每一个。单身。日。

这种方法有两个问题。

  • 这不太科学。
  • 我想更谨慎地使用技术,减少我的屏幕时间。

因此,我在 JobServe 上建立了一个搜索“数据科学家”的网页抓取器。我安排这每天发生,处理数据(删除重复,特征工程,可视化,模型)并将输出发送到一个闪亮的应用程序,该程序可在https://apps.statcore.co.uk/trends-in-data-science/获得

追踪数据科学趋势的闪亮应用

在编写这个应用程序时,它有 4000 份工作的数据,并允许我

  • 对古老的 Python vs R 问题做出数据驱动的决策。
  • 使用应用于职位描述的主题建模来确定需求技能。

该项目的完整源代码和 docker 图片可以在https://github.com/tonyjward/trends-in-data-sciencehttps://hub.docker.com/r/tonyjward/trends-in-data-science找到

我是这样做的。

数据收集

在 JobServe 上搜索“数据科学家”,目前会给出 35 个结果,并提供大量我们想要捕捉的有趣信息。

在 JobServe 上搜索“数据科学家”

不幸的是,由于网站动态显示内容,并不是所有的搜索结果都能立即看到。用户首先必须从边栏中选择一个作业,然后边栏会更新包含作业详细信息的主面板。这使得 web 抓取变得复杂,因为我们不能简单地抓取和解析 html——这样做只会返回当前所选作业的数据。

因此,我们使用 Selenium——一种自动化的网络浏览器——我们指示它像人类一样与网页进行交互,即在框中输入文本,按 return 键,使用 down 键浏览搜索结果并保存结果。

用硒酸盐刮 Jobserve

特征工程

工资信息是以自由格式文本的形式提供的,其中包含一些需要处理的奇怪和不一致的地方。

问题:提供的薪资范围(50k—70k)
解决方法:取最大值

问题:单位不一致( 50k 对 50000)
解决方法:如果字符数大于 4,则除以 1000

问题:非常大的薪水( 每年 60k—900k)
解决方法:将所有超过 3300k 的薪水除以 100,将所有超过 3300k 的薪水除以 10。

问题:奴工( )高达 0.00 年息 )
解决方法:视为 NA

复制

由于一个广告显示了 7 天,我们每天都在刮,我们将有重复的广告在我们的数据。另外

  • 可以重新公布角色
  • 招聘人员可以多次发布虚假的角色来获取简历

因此,我决定使用自由格式的工作描述作为唯一标识符来删除重复的内容。

探索性数据分析

随着数据准备工作的结束,是时候进入正题了。

使用闪亮的应用程序可以访问所有 4000 份工作的数据。用户可以在永久雇佣和合同雇佣之间进行选择,执行关键字搜索或按最高工资或发布日期对数据进行排序。

作业类型

首先,我对合同工和固定工之间的区别感兴趣。

按工作类型划分的角色数量

正如你所料,大多数职位都被宣传为永久性的。不过,我很惊讶地看到,合同工占了全部工作岗位的近 40%。然而,重要的是要记住,JobServe 通常是许多承包商寻找下一个角色的地方,所以我不认为这种分裂代表了整个市场。

工具

根据工作描述中是否提到,我将每份工作分为四类

  • Python 而不是 R
  • r 但不是 Python
  • Python 还是 R
  • 两种语言都没有

然后,我观察了这一类别的工作总数是如何变化的。首先是永久性角色:

永久角色的工具流行度

然后,对于合同角色:

合同角色的工具流行度

哇!Python 刚刚在人气上狠批了 R!

薪酬呢?下面的方框图显示了每个组的几个汇总统计数据:

  • 工资中位数(水平线)
  • 第 25 和第 75 百分位(彩色框的底部和顶部)

对于指定 Python 而非 R 的角色,永久员工的工资中值最高(75k)。指定 R 但不指定 Python 的角色大约少付 20%(60k)。

对于指定 Python 而不是 R ( 600)的角色,承包商的平均日工资最高。指定 R 但不指定 Python 的角色工资大约低 8%(550)。

所以学习 Python 的理由很充分…

主题建模

之前,我将一种叫做潜在狄利克雷分配(LDA)的主题模型应用于客户评论和保险损失理算员笔记,取得了良好的效果,所以我很高兴将这种技术应用于工作描述,看看我能发现什么。

以下内容摘自该算法的共同发明人 David Blei 撰写的主题建模概述。

主题模型是一种算法,用于发现弥漫在大量非结构化文档集合中的主题。主题模型可以根据发现的主题组织系列。

LDA 背后的直觉是文档展示了多个主题。例如,考虑图 1 中的文章。这篇题为“寻找生命最基本的(基因)必需品”的文章是关于使用数据分析来确定一个有机体生存所需的基因数量(从进化的角度来说)。我们用手突出了文章中使用的不同单词。关于数据分析的单词,如“计算机”和“预测”,用蓝色突出显示;关于进化生物学的单词,如“生命”和“有机体”,用粉红色突出显示;关于遗传学的单词,如“测序”和“基因”,用黄色突出显示。

来源:主题建模概述

主题建模允许您“逆向工程”我们假设生成文档的潜在主题。首先,我们必须指定我们希望模型找到多少主题。因为我们正在进行无监督学习,所以这里没有正确的答案——一个好的选择往往是反复试验的结果。

20 个主题

从用 20 个主题训练的主题模型开始,我们使用 LDAvis 来理解每个主题的相关单词。我们发现话题可以按以下方式组织。

  • 沟通和商业敏锐度
  • 技术
  • 招聘人员评论
  • 角色优势和要求
  • 合同角色
  • 数据工程

沟通和商业敏锐度

产生洞察力和有效沟通以交付业务价值的能力是数据科学的核心能力,也是其自身的主题。

技术

该模型确定了一些技术技能,如机器学习和报告。

招聘人员评论

接下来是招聘人员在许多招聘信息中使用的各种主题。其中包括与歧视有关的免责声明,以及几项行动呼吁。

角色优势和要求

该模型已将安全审查确定为一个主题,以及员工可获得的福利(养老金计划、弹性工作制、免费健身房)。

合同角色

该数据包含永久和合同角色的混合,并且该算法已经识别出这一点。

数据工程

有趣的是,数据工程有自己的主题,相关的术语有云计算(aws,azure)和各种技术(sql,hadoop,spark,nosql,kafka)

60 个主题

如果我们想要更精细的视图,我们可以将主题的数量增加到 60 个。当我们这样做时,我们发现“数据工程”被分成了 4 个子主题。

我们也开始看到特定的数据科学应用领域/行业出现,如生物信息学和反洗钱。

整理系列

我们可以使用我们的主题模型来检索与某个主题相关的工作描述。为此,我们从下拉列表中选择一个主题,然后在概率栏中进行过滤。

我们只是带回了所有大概率话题为“aws 工程大数据架构设计”的职位描述。但是请注意,由于模型每天晚上都要重新培训,所以每天的确切主题都会发生变化。

自己试试吧,如果在评论里发现什么有趣的东西,请告诉我!

包扎

这个项目让我能够在不经常检查手机的情况下监测数据科学就业市场的趋势——事实上,在 2019 年 4 月,在阅读了加州大学新港分校的数字极简主义之后,我完全摆脱了我的智能手机。

很早就很清楚,我应该开始学习 Python。根据凯尔·麦基乌的推荐,我通过泽德·肖的艰难地学习 Python,这给了我足够的知识来开始构建东西。

我现在已经在两个客户项目(可再生电力预测和资产监控)中使用了 Python,目前我正在从事一个个人项目,以预测灰狗赛跑的结果!

找到学习一门新语言的时间和动力并不容易——但当我需要一些额外的动力时,我会加载应用程序并研究最新趋势。希望你觉得有用。

Python:学什么,学多少?

原文:https://towardsdatascience.com/python-what-to-learn-and-how-much-fb61bdef7b3a?source=collection_archive---------26-----------------------

命令式、函数式、面向对象:哪种编程范式非常适合我的工作?

JESHOOTS.COM在 Unsplash 上拍照

Python 编程就像是无限的可能性。Web 和互联网开发、基于桌面 GUI 的应用程序、机器学习、深度学习、软件开发、商业应用程序开发 Python 编程的用例有很多。考虑到有这么多东西要学,很容易迷路,不知道多少是太多。

在本文中,我将带您了解 Python 支持的不同编程范例,并帮助您决定应该首先学习哪一种。

编程范式是对编程语言进行分类的一种风格或方式。

Python 支持三种编程范式:命令式、函数式和面向对象。图 1 显示了这些编程范例的特性。

图 1: Python 编程范例

必要的

命令式编程使用语句、循环等向计算机提供明确的步骤。

图 2:命令式编程范例的例子

下面是命令式编程的一个例子。这是一个简单的计算机指令,如何连接列表中提供的字符,并生成一个字符串。

如果运行上面的语句和循环,它将打印

sanrusha

在命令式编程中,命令集可以分组到一个代码块中,也称为过程

图 3:过程化编程范例的一个例子

上述代码中的语句和循环可以分组到一个过程 (Python 方法)中,如下所示。

现在,可以使用一组字符作为参数来调用 stringc 方法。

stringc([‘s’,’a’,’n’,’r’,’u’,’s’,’h’,’a’])

上述调用将返回

sanrusha

现在,这种方法比以前更加模块化。虽然过程使命令式编程模块化,但状态更改要么局限于过程,要么局限于过程的显式参数或返回。

功能的

函数式编程范式是一种使用内置高阶函数的编程风格。高阶函数将其他函数作为参数,或者将它们作为结果返回。

Python 内置函数像 lambda、map、filter、generators、decorators、recursion都是高阶函数。只要知道自己想要什么,就可以使用最合适的内置 python 函数,用最少的代码行来完成。

图 4:函数式编程范例的例子

下面的代码展示了如何通过使用 Python 高阶函数(如 reduce 和 lambda)在不到三行的代码中将字符组合成字符串。

参考下面的链接,获得 Python 中可用的内置高阶函数的完整列表

[## functools 可调用对象上的高阶函数和运算-Python 3 . 8 . 5 文档

源代码:Lib/functools.py 该模块用于高阶函数:作用于或返回其他函数的函数…

docs.python.org](https://docs.python.org/3/library/functools.html)

面向对象

面向对象编程(OOP)范式如此著名,以至于你不得不尽最大努力去忽视它。面向对象编程如此著名的原因之一是它使代码可重用。这就像拿着一栋建筑的蓝图,按照你自己的喜好建造这栋建筑,比如层数、颜色、地板等等。

面向对象的蓝图叫做类。您可以根据自己的喜好将该类实例化为一个对象。虽然你不能用汽车的蓝图来制造自行车,但是你可以在使用蓝图的时候决定汽车的许多变化。

Python 充满了您将在程序中使用的内置类。虽然从第一天开始就掌握 OOP 并不容易,但好消息是你可能不需要在工作的第一天就从头开始编写 OOP 程序。对于一些工作,您可能永远不需要编写 OOP,您的工作可能通过使用内置类就足够了。

图 Python 面向对象编程的一个例子

下面是一个例子,如何用 OOP 来完成创建一个字符串的任务。

从哪里开始?

现在你的问题来了。

从哪里开始?

答案取决于你将要从事的工作。图 6 显示了基于您的工作应该关注的 python 编程范例。

图 6:掌握工作职能的编程范例

结论

如果你对编程一无所知,对 Python 也有一点了解,那就从命令式范式开始,了解函数式编程的知识。内置高阶 python 函数的知识会给你很大帮助。一旦您熟悉了这些范例,就开始探索面向对象编程。

参考资料:

[## Python+SQL+Oracle

欢迎来到 Udemy 上最受欢迎的 Python、SQL 和 Oracle 数据库课程。Python 是三大编程之一…

www.udemy.com](https://www.udemy.com/course/python-with-oracle-developer-course/?referralCode=6F68B20A8FB16A20BFE1)

Python:编写定制异常比您想象的要简单

原文:https://towardsdatascience.com/python-writing-custom-exceptions-is-easier-than-you-might-think-14df327e503e?source=collection_archive---------47-----------------------

Python 提供了许多内置的异常,然而,有时自己创建异常更有意义

在 Python 中处理运行时错误非常容易。您只需将代码放在 try-except 块中,并处理 Python 提供的许多内置异常类型中的异常。下面是一个用 Python 处理简单错误的例子。

number_followers = 40
"Hello, My name is Shivam and I have " + number_followers + " followers on Medium."

这段代码会产生一个错误,特别是 TypeError,因为+运算符不能将字符串和整数类型相加,我已经在关于魔法方法的独立文章中解释了这些运算符是如何工作的,如果你想阅读相关内容,请查看。让我们试一试这段代码。

try:
   number_followers = 40
   "My name is Shivam and I have " + number_followers + "on medium."
except TypeError:
   print("A string and integer cannot be concatenated")

看,简单明了,但是只有我们需要写代码来连接一个字符串和一个整数。在现实生活中,管理大型代码库,许多团队使用您的代码,引发您的代码中出现的异常,这不是一个好的做法。

尽管 Python 提供了许多内置的异常类型,但有时我们不得不编写定制的异常来满足我们的目的。让我们假设一个程序来计算一个圆的面积,周长和其他一些东西,我们为此创建一个类圆。

编写新的定制异常非常简单。我们需要从内置模块中扩展异常类。

CircleException 现在可以像任何其他内置异常一样被引发

现在,我们可以在代码中出现错误的情况下抛出这个异常,而不必用确切的错误来麻烦客户团队。这样,客户端团队只需处理 CircleException,无论 Circle 类中有什么错误,都会引发该异常。除此之外,我们还可以在客户端修改错误消息,以获得更健壮的日志记录系统。

在客户端,我们只需要捕捉 CircleException 类型。

所以与其这样,

s = Circle(5)try:
    s.area()
except TypeError:
    print("Something wrong with the values sent")
except AttributeError:
    print("Something wrong with the attributes of the class")
except KeyError:
    print("Something went wrong with the keys in shape class")

我们将只处理 CircleException,这样客户端就不必处理所有可能与我们的 CircleClass 有关的错误。下面大概是这样的。

我们还可以修改它,并通过引入实例变量向自定义异常添加属性。

让我们通过在 Circle 类中引发 CircleException 时初始化方法名变量来使用它。

现在,客户端将所有的方法放在一个 try-except 下,将能够获得准确的错误消息,该错误消息可以记录在日志系统中。

s = Shape(shape_name='circle', radius=5)try: area = s.area()
    circumference = s.circumference()
    diameter = s.diameter()except CircleException as e:
    print(e)

让我们假设 Circle 类的 area 方法出了问题,没有权限修改 Circle 类的客户端可以很好地处理它,输出将是这样的。

本文帮助您理解如何以及为什么用 Python 编写自定义异常。在很多情况下,编写自定义异常比内置异常更有意义,读完本文后,我希望您对自定义异常有更好的理解,并知道如何抓住这些机会。

如果你喜欢这篇文章并学到了一些东西,请在 twitter 和 medium 上关注我,阅读更多类似的内容。我写关于 Python,数据科学,ML,AI 等等。

使用 Julia 的 Pythonic 大数据?

原文:https://towardsdatascience.com/pythonic-big-data-using-julia-f06dec411a95?source=collection_archive---------47-----------------------

车床. jl

Python 可以在 Julia 的帮助下处理大堆数据吗?

最近,我一直在尝试用 Julia 作为 Python 的后端。虽然这在理论上肯定是一个伟大的概念,但有些东西似乎在翻译中丢失了。这是我和几位 Julia 和 Python 科学家一直在进行的讨论,因为从 Python 中使用 Julia 的道路目前走得很少。我在另一篇文章中也提到了这个概念,您可以在这里阅读:

[## Julia 的位置与 Python 共存吗?

Julia 是未来的 ML 语言,还是 Python 不太为人所知的伙伴?

towardsdatascience.com](/is-julias-place-co-existence-with-python-eff2cb834f41)

在本文中,我讨论了 Julia 作为 Python 后端取代 C 的潜力。虽然在某种程度上,我确实认为这是一个奇怪的想法——因为 Julia 并不太难,并且在语法上与 Python 非常相似,这也有一个显著的优势。我认为使用 Python 与 Julia 的兼容层可能有用的原因是,Python 有更成熟的 web 框架,更容易部署,而且 Python 有更多为它创建的高级 API。能够将这些强大的工具与 Julia 的速度结合起来处理更多的数据和更大的算法将是梦想成真。

顶部加载器

作为 Python 和 Julia 共生技术的一部分,我们需要一个名为 TopLoader 的包。TopLoader 是一个 Pythonic 接口,用于管理 Julia 虚拟环境和包。您可以使用 Pip 添加 TopLoader,如下所示:

sudo pip3 install TopLoader

你可以在 Github 上找到 TopLoader:

[## emmettgb/TopLoader

你好,装载愉快!您可以使用以下命令安装 TopLoader:sudo pip 3 立即从 TopLoader 安装 TopLoader 您可以…

github.com](https://github.com/emmettgb/TopLoader)

要设置 TopLoader,首先需要进入 Python REPL 并导入 TopLoader。这将安装和更新 PyCall,并将 Julia 可执行文件设置为从 Python 运行。当然,你也需要正确安装 Julia 来完成这项工作。Apt 软件包管理器安装 Julia 时存在一个已知问题,TopLoader 将无法找到 Julia。

为了解决这个问题,

不要通过 APT 安装 JULIA!

以下是 Julia Computing 给出的一些 Julian 建议:直接从 Julia Computing 安装 Julia 二进制文件,或 http://julialang 。org。使用 Julia 的包管理器版本会产生以下问题:

  • 你可能会有一个过时版本的朱莉娅。例如,我使用 Fedora,Dnf 只有 1.2 版的 Julia,而当前版本是 1.4。如果没有最新版本的 Julia,您将无法使用新功能,例如使用@spawn 进行并行计算,这意味着除了多行 for 循环之外,您将无法运行任何线程。
  • 您的 Julia 可执行文件可能会出现在错误的位置。您的包管理器以一种特殊的方式在您的系统上放置文件,拥有一个到/opt 的可执行链接肯定比必须预测每个包管理器将 Julia 二进制文件放在哪里,然后必须通过~/在 Bash 中导出它们更好。或者在每次启动 Julia 之前运行该命令。

当您最终可以导入 TopLoader 时,请确保它是通过 REPL 来完成的。跨越 Julia 和 Python 的解释所涉及的设置不能在其他任何地方发生。

添加车床

(笔记本)

[## 车床

车床使用更快的方法和简单的方法。使得包装快速、简单且轻便。许多工具和模型…

车床. ai](http://lathe.ai/)

L athe 使用 Julia 的语法表达式来模拟 Python 和 C++等典型鸭式语言中的类。这很好,因为这意味着模型在通过预测方法运行之前得到了初始化。这意味着我们可以像这样拟合一个模型:

model = LogisticRegression(trainX, trainy)

这个表达式将继续计算逻辑回归模型预测所需的任何值。然后我们可以调用 predict 方法,它是我们新模型对象的子对象,来获取我们的 ŷ.

**ŷ** = model.predict(testX)

为了演示如何使用 Python 中的 Lathe,我们将首先创建一个带有 TopLoader 的环境,然后向其中添加 Lathe:

这个环境已经存在,并且已经有车床在里面了。然而,我们将需要安装车床的不稳定分支,以便我们可以修改和测试它。虽然我们可以从 Python 中做到这一点,但我也想演示一下我们如何从朱莉娅·REPL 中做到这一点,在我看来,这要容易得多,也更有意义。我们需要做的就是在当前目录中输入朱莉娅·REPL:

然后用]进入 Pkg REPL,用激活命令激活我们的环境:

现在我们可以添加或删除任何我们想要的包。

现在,如果我们在 TopLoader 中重新加载我们的环境,并使用 List()函数,我们将获得新的更新版本的 Lathe。

我们可以从我们的环境中使用 using()方法将 Lathe 导入 Python:

每当我们创建一个模型类型的时候,一切都完全按照预期运行。

同样,predict()方法也是如此:

尝试大型数据集

为了确定我们是否真的从使用 Julia 而不是 Python 中获得了性能提升,我们需要一个基线。为了做到这一点,我把同样的线性回归函数翻译成 Python。这是一个相当基本的机器学习模型,所以幸运的是 Python 将能够自己完成我们的许多测试。

def dot(x,y):
    lst = []
    for i,w in zip(x,y):
        lst.append(i * w)
    return(lst)
def sq(x):
    x = [c ** 2 for c in x]
    return(x)
class LinearRegression:
    def __init__(self,x,y):
        # a = ((∑y)(∑x^2)-(∑x)(∑xy)) / (n(∑x^2) - (∑x)^2)
        # b = (x(∑xy) - (∑x)(∑y)) / n(∑x^2) - (∑x)^2
        if len(x) != len(y):
            pass
        # Get our Summations:
        Σx = sum(x)
        Σy = sum(y)
        # dot x and y
        xy = dot(x,y)
        # ∑dot x and y
        Σxy = sum(xy)
        # dotsquare x
        x2 = sq(x)
        # ∑ dotsquare x
        Σx2 = sum(x2)
        # n = sample size
        n = len(x)
        # Calculate a
        self.a = (((Σy) * (Σx2)) - ((Σx * (Σxy)))) / ((n * (Σx2))-(Σx**2))
        # Calculate b
        self.b = ((n*(Σxy)) - (Σx * Σy)) / ((n * (Σx2)) - (Σx ** 2))
        # The part that is super struct:
    def predict(self,xt):
        xt = [self.a + (self.b * i) for i in xt]
        return(xt)

我还导入了熊猫并读入了我的机器上的两个 CSV 文件,我用它们来进行速度测试。数据集有 10,000,000 个观察值,这可能仍然很容易执行,但几乎肯定会给 Python 一个很好的锻炼。为了给这些计算计时,我将使用 IPython 神奇的命令 timeit。

%timeit LinearRegression(df["X"],df["Y"]).predict(testdf["X"])

我们的结果是 8.78 秒。

与 Julia 在这个精确计算中的速度相比,结果是 0.362333 秒

但是使用 Python 的 TopLoader 呢?我们能期待类似于 Julia、Python 或介于两者之间的性能吗?在最坏的情况下,由于转换层的原因,性能也会受到严重影响。不幸的是,这似乎正是 TopLoader 以 1 分 10 秒的成绩出现时发生的情况。

糟糕透了。

结论

我不得不接受这个无效假设。

简短的回答是否定的,我的估计是正确的,兼容性层导致了太多的速度下降。然而,我认为这个观察可以洞察现实,如果有一个更好的系统,Python 中的 Julia 可能会很棒。TopLoader 的问题是,它需要将 Pythonic 数据转换成 Julian 数据,然后再将这些数据转换回 Julian 数据。也许如果有一种方法可以在后台运行 Julia 实例,从而更有效地从 Python 接收大量数据,那么可能有一种方法可以实现这一点。

随着对一个更好的系统的需求被清楚地阐明,也许我会开始一个新的项目来实现这个目标。然而,与此同时,在处理数据方面,Julia 肯定对 Python 没有任何帮助,而且使用兼容层似乎比使用 Python 本身要慢。虽然这个概念的未来可能是大胆的,甚至可能意味着用更少的努力获得更快的 Python 包,但未来肯定不是现在。

不管 TopLoader 在这方面有什么缺点,我仍然认为它是一个非常酷的包。用 Python 搭建 Julia 虚拟环境当然是一个很酷的想法,我只能希望最终能够确定一种方法来实现这个概念的全部功能。

具有元类的 Pythonic 元编程

原文:https://towardsdatascience.com/pythonic-metaprogramming-with-metaclasses-19b0df1e1760?source=collection_archive---------45-----------------------

你的装饰和元类生存指南。

介绍

元编程是操作代码的代码构造,可以在语法上产生一些非常有趣和富有表现力的结果。元编程并不是一个通常与 Python 联系在一起的概念,但事实是 Python 和 meta 实际上以许多很酷且独特的方式结合在一起。如果你曾经在 Python 中使用过元类或装饰器,那么你实际上是在用 Python 进行元编程。

像大多数高级编程语言一样,在 Python 中,每个单独的变量或对象都有一个类型。类型很重要,因为它们决定了每个变量的数据在内存中的存储方式。Python 中的每种类型都由一个类定义,我们可以通过使用标准库中的 type()函数来说明这一点:

string = "example"
print(type(string))<class 'str'>

string 类型的类是“str”类,它包含 split()之类的方法,这些方法通常用于解析字符。对于任何 Python 程序员来说,这可能都不是一个新概念,但是我们可以更进一步,使用元类来操纵类,而不需要编写它们。

基本用法

与 C、Java 甚至 C++中 int、char 和 float 是主要的数据类型不同,在 Python 中,它们是类中的一个对象。这对 Python 有利也有弊,因为对象是通常不可通用读取的数据类型。这意味着数据经常会卡在 Python 中,但也意味着 Python 中使用的数据是一个类的实例,也就是一个元类的实例。

我们首先需要的是一名室内装潢师。Python 中装饰者的工作是创建一个设计模式,允许我们在不修改类的情况下向类中添加新功能。使用 decorators 将使我们的类保持静态变得非常容易,同时还可以根据需要添加额外的特性。我喜欢让我的类装饰者继承函数装饰者,因为它在我们的代码之间创建了一个更大的边界,这是我们试图避免变异的。

from functools import wrapsdef debug(func): 
 '''decorator; debugs function when passed.'''

 [@wraps](http://twitter.com/wraps)(func) 
 def wrapper(*args, **kwargs): 
  print("Full name of this method:", func.__qualname__) 
  return func(*args, **kwargs) 
 return wrapper

现在我们可以添加实际的类装饰器了:

def debugmethods(cls): 
 '''class decorator to use debug method'''

 for key, val in vars(cls).items(): 
  if callable(val): 
   setattr(cls, key, debug(val)) 
 return cls

因此,我们的元类:

class debugMeta(type): 
 '''meta class which feed created class object 
 to debugmethod to get debug functionality 
 enabled objects'''

 def __new__(cls, clsname, bases, clsdict): 
  obj = super().__new__(cls, clsname, bases, clsdict) 
  obj = debugmethods(obj) 
  return obj 

现在,我们可以通过使用参数(metaclass=)创建一个新类来继承这个新类的数据。

# now all the subclass of this 
# will have debugging applied 
class Base(metaclass=debugMeta):pass

现在我们可以使用面向对象的继承将元类的属性继承到我们的新类中。每次修改“debugMeta”时,依赖于它的所有子元素都将继承这些更改。这产生了一些有趣的语法,我们可以在不需要修改它的情况下,自动向我们的类添加功能。

# inheriting Base 
class Calc(Base): 
 def add(self, x, y): 
  return x+y

现在使用计算器:

我们的 calculator 类实际上使用了我们用元类设置的调试行为,但是注意数据不是我们对象的子对象:

结论

虽然 Python 通常不会与元编程的概念联系在一起,但它不仅能够通过其独特的装饰器完美地实现这一概念,而且还在很多方面使用它。我认为 Python 相对于元编程为标准的许多其他语言的最大优势是 Python 的继承性。对于我上面所做的,如果没有继承,这一切都是不可能的,而且在我看来 Python 继承得非常好。由于我们经常将元编程与 Lisp 或 Scheme 之类的语言联系在一起,所以看到元编程以完全不同的方式完成是非常有趣的——而且是以完全不同的语言和完全不同的范式以非常酷的方式完成。最重要的是,decorators 肯定是一个很酷的概念,我真的希望更多的语言会采用它,因为它使元编程的过程变得非常简单和直接。

当将 Python 与其他语言进行比较时,尤其是与我用于元编程的函数式语言进行比较时,情况更是如此。经常折腾宏和表达式可能是强大的,但同时它可能令人难以置信地混乱,我认为 Python 在面对那个确切的问题时很好地利用了它的语法。

Python 的到期日期

原文:https://towardsdatascience.com/pythons-expiration-date-b1a55f368f1a?source=collection_archive---------0-----------------------

Python 这个“大男孩”还要当多久?在脚本中?

(Python 徽标由https://www.python.org/提供)

在过去的十年里,有一种语言的受欢迎程度飙升,并且超过了它所有的前辈,那就是 Python。Python 是一种易于使用、易于阅读、易于修改的面向对象编程语言,由 c 解释。Python 最近成为世界上最受欢迎的编程语言有许多原因,但也有许多原因可能会使它失去这个头衔。

Python 是用来做什么的?

Python 是一种解释型语言,这意味着没有编译器或汇编器能够将这种语言转换成机器代码。相反,另一种语言,在 Python 的情况下是 C,用于解释带有 Python.h 头的语言。通常,这会将 Python 归入脚本类,然而,我认为不要忽视 Python 作为编程主流的地位是很重要的。

可执行?

Python 的一个巨大缺点是 Python 代码不能被编译成可执行文件。任何用 Python 编写的应用程序或工具都需要将 Python 及其依赖项安装在最终用户的系统上。与此同时,每个包的所有正确版本都与用于开发所述应用程序的包相对应。

Web?

Python 已经证明了自己惊人的天赋,那就是运行网站的后端。这也是 Python 相对于很多其他语言的一个优势。Python 有大量不可思议的包来部署 API,甚至设计全功能的网络应用。

机器学习

机器学习是 Python 的另一个突出用途,这无疑有助于近年来 Python 的人气飙升。机器学习是技术的前沿,Python 以及它与 C 的密切关系对于机器学习来说是惊人的有效和有用。虽然 Python 确实缺乏一些类似语言(如 Nim、Julia 和 Go)的性能,但在很多方面,它通过快速、简单、简洁弥补了这一点,但也许更重要的是,

通用。

Unix 操作系统

如果不谈论现代的类 Unix 系统,就很难谈论 Python。Python 2.7 已经被弃用了两个月,我的桌面环境以及我的操作系统中的许多特性仍然使用 Python 2.7。Bash 和 Python 的结合可以产生一些非常有用的脚本来运行服务器、填充数据、完成请求、编辑文件等等。

Python 的缺点

尽管 Python 确实是一门伟大的语言,并对整个世界产生了巨大的影响,但每种语言都有其缺点,Python 也不例外。首先,也是最值得注意的:

Python 很慢。

表演

虽然我当然同意使用日志和 for each 方法,一般来说只是编写更好的代码肯定可以加快 Python 的速度,但在某些情况下,代码肯定会很慢。机器学习是一个很好的例子,因为训练神经网络经常需要递归的致命使用。我无法告诉你有多少次我用 Python 写了一个拉数据的脚本,然后在我的命令行界面(CLI)中经历了超时和变慢。)

通过令人印象深刻的 Cython,人们正在努力缓解这一问题,但在尝试使用 Cython 时,过渡通常并不像苹果和橙子那样简单。尽管在很多场景中 Python 的局限性是不明显的,但如果我说我从来没有为了完成某件事而不得不转向 Julia、Nim 或 C 语言,那我就是在撒谎。

属国

Python 的另一个显著缺点是依赖性和虚拟环境。关于 Python 领域的脚本语言,我认为 Python 在依赖性和虚拟环境方面做得很好。我喜欢把 Python 比作我用来做类似事情的两种语言,Julia 和 Nim,Julia 的依赖世界和 Python 的非常相似。Julia 确实有一个优势,在我看来,虚拟环境要好得多,也更容易使用。

然而,随着 Nim 的加入,Python 在水中被遗弃了。使用 Nim,您可以创建一个包含所有必需依赖项的编译后的可执行文件。在如何为最终用户和部署处理依赖和包的问题上,Nim 轻而易举地胜过了 Python 和 Julia。

Python 的优势

尽管我对 Python 有所抱怨,但我确实喜欢 Python 的许多方面。

庄严的

Python 是一种非常常用的编程语言,它有许多优点,例如:

  • 易于谷歌搜索
  • 容易谈论
  • 伟大的包裹
  • 频繁补丁

不仅如此,Python 被证明是值得尊敬和可靠的。这使得 Python 成为初学者的绝佳选择,同时也是那些希望以令人难以置信的速度构建稳定、持久的技术,同时又可以令人难以置信地轻松使用的人的绝佳选择。

看起来像英语

像 Python 这样的语言和类似的脚本语言之间的一个很大的区别就是 Python 的可读性和易理解性。通常阅读 Python 就像阅读一本奇怪的、抽象的关于变量等于数字的书。这不仅使初学者更容易,也使变异、修改和解密更容易,这些都是非常重要的,尤其是对于有数千名开发人员在其中工作的大型代码池。

说实话,我们都宁愿写 Python,也不愿写 c。

是福斯

虽然对一些人来说,他们的语言是否免费和开源并不重要,但对我来说,这当然很重要。Python 基金会的功能主要来自捐赠和教育证书,这意味着 Python 是一个完全免费和开放的软件,有人为你编写代码供你使用,这很好。

Python 要过期了吗?

我经常被问到的一个问题是“你认为 Python 会存在多久?”这个问题经常出现在机器学习领域,因为 Python 有一大堆与机器学习相关的问题。想到任何语言都可能是静态的,并在很长一段时间内保持为最常用的编程语言,这很有趣。

Fortran 是 C 之前的大事件,C 是 C++之前的大事件,C++是 Java 之前的大事件,这个列表还在继续,并将永远继续。计算机是令人兴奋的,因为它们在不断进化,它们所研究的技术也在不断进化。就在 30 年前,16GB 内存的想法还是一个完全古怪的概念,所以根本不知道编程语言的未来会如何。

虽然 Python 最终很可能会被另一种编程语言所取代,但我认为重要的是要记住,人们仍然在编写 Fortran、C、Java 和 c++;因此,Python 本身很可能会伴随我们,并在很长一段时间内被普遍使用,不管它可能变得多么不受欢迎。

其他语言

(src =http://julialang.org/

对 Python 的流行构成最大威胁的可能是其他新的编程语言。我已经讨论过的语言,Julia 和 Nim,只是我认为目前 Python 的两大竞争对手。Julia 肯定会改变我们进行机器学习的方式,而 Nim 肯定会改变高级脚本的游戏规则。

(【https://nim-lang.org/】T2

这些语言可能还没有获得那么多的动力,但是它们展示了一件我认为很重要的事情:

进步是可能的。

也就是说,Python 不是一堵坚固的砖墙,编程语言、机器学习和脚本编写的进步肯定不会就此止步。我认为这些语言不成功的部分原因是因为它们都是统计类型的,而且在大多数情况下

函数式语言,

我知道对于那些坚持使用和热爱 Python 的人来说,这可能是一个非常不和谐的概念。

结论

该不该学 Python?

我对这个问题的回答永远是肯定的。Python 是一门很棒的语言,可以让你入门并熟悉如何解决问题和开始编程。与包括 Julia 和 Nim 在内的其他语言相比,Python 还拥有大量的资源,这使得它更容易学习。

我的目标是提供一个关于编程概念的非常客观的观点,我不认为 Python 会很快消失。然而,我确实认为作为一名程序员不断成长很重要,更重要的是开阔你的视野。如果你已经使用 Python 有一段时间了,也许可以再学一门外语。尝试一种不同范式的语言,并理解这种范式和您所选择的语言的泛型编程方法是如何利用这些概念的。我认为这是有帮助的,因为你不仅可以学会使用更快的方法,还可以学到很多你以前可能不会想到的东西。

Python 的地理编码-将地址列表转换为地图

原文:https://towardsdatascience.com/pythons-geocoding-convert-a-list-of-addresses-into-a-map-f522ef513fd6?source=collection_archive---------1-----------------------

如何使用地理定位 API 接收绘制客户、工厂、车队和其他主题的地图所需的数据。

照片由捕捉人心。 on Unsplash

我们每个人都有一个数据库,里面有客户的地址,工厂或商店的名单,我们销售产品或合作的地区。在地图上显示这些数据是现代仪表盘的必备功能。为了做到这一点,我们必须将地址(城市名称或地区)转换成地理空间数据——纬度和经度。然后我们可以在引人注目的地图上展示它们。

你会学到什么

在本教程中,我们将探索如何:

  • 选择理想的地图服务
  • 使用 [requests](#c8f7)连接 API(老式方式)
  • 用 Python 的 [geopy](#3128) 库获取位置(及更多)
  • 将地址列表转换为地理点
  • [folium](#03c6)``[plotly](#03c6)将采集的数据显示在地图上
  • 将地图保存成。html 文件

你可以通过 Github-Address to location . ipynb下载这款 jupyter 笔记本,跟随我的脚步。

谁提供制图数据?

要将地址转换成坐标,你可以购买一个带有地理位置的地址数据库,或者查询一些地理定位服务提供商。任何提供地图的人通常都会提供一个地理定位 API(通常需要付费)。让我们回顾一下一些主要的提供商:

  • 谷歌地图平台
  • 地图框 API
  • 必应 API
  • OpenStreetMap API——(免费)
  • Yandex 地图 API

本文回顾了一些主要的提供商:

[## 十大地图应用程序接口:谷歌地图、必应地图、地图盒子和其他地理定位应用程序接口

地图已经成为你离不开的必备追踪工具。它们是任何公司不可或缺的一部分…

medium.com](https://medium.com/rakuten-rapidapi/top-10-map-apis-google-maps-bing-maps-mapbox-and-other-geolocation-apis-cb442fec07ed)

如何连接到映射 API

大多数 API 都允许直接调用。你把你的地址发送到一个网址,然后接收地理位置信息。多年来,我们使用 python requests库来做这件事。让我们探索对 Google API 的请求。

要连接到 Google API,您需要一个 API 密钥。你必须提出要求,然后保守秘密,因为你为服务付费。谷歌每月免费提供 200 美元,足以完成 40000 次地理编码请求。

地理编码调用通常非常简单:

Base address: [https://maps.googleapis.com/maps/api/geocode/json](https://maps.googleapis.com/maps/api/geocode/json)?
Parameters:
   # blank spaces are turned to %20
   address=1 Apple Park Way, Cupertino, CA
   key=API_KEY

你准备好你想要搜索的数据。在urllib的帮助下,你可以很容易地将参数转换成一个 URL,你调用requests.get().得到的响应要么是 XML,但更常见的是由json.loads处理的json

调用 Google API 将一个地区转换成地理位置

使用 GeoPy 简化您的工作

您可以努力利用映射提供者 API 的文档来充分利用它,但是利用像 GeoPy 这样的现有库更容易。使用 Geopy,您只需几行代码就可以完成上述操作。

Geopy 为流行的地图服务提供了一个类。nomim是广受欢迎的 OpenStreetMap 背后的服务,它允许你免费进行地理编码。但是你应该遵守的使用政策以便让每个人都能使用它:

一般来说,不鼓励对大量数据进行批量地理编码。如果遵循这些附加规则,较小的一次性批量任务是允许的

  • 将您的请求限制在单线程
  • 仅限 1 台机器,无分布式脚本(包括多个 Amazon EC2 实例或类似实例)
  • 结果必须缓存在你这边。重复发送相同查询的客户端可能被分类为有故障和被阻止。

你是有一个小项目还是只想给老板留下好印象?让我们用 geo py . Nominatim 进行地理编码,首先,我们将 nominam 初始化为geolocator变量:

from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="example app")

然后你需要一个地区的信息,我选择了意大利的托斯卡纳区:

[In]: geolocator.geocode("**Tuscany, Italy**").raw[Out]: {'place_id': 232933113,
 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0\. [https://osm.org/copyright',](https://osm.org/copyright',)
 'osm_type': 'relation',
 'osm_id': 41977,
 'boundingbox': ['42.237615', '44.4725419', '9.6867692', '12.3713544'],
 **'lat': '43.4586541'**,
 **'lon': '11.1389204'**,
 'display_name': 'Toscana, Italia',
 'class': 'boundary',
 'type': 'administrative',
 'importance': 0.6870417219974091,
 'icon': 'https://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png'}

从响应中,您可以获得纬度和经度

[In]: geolocator.geocode("**Tuscany, Italy**").point
[Out]: Point(43.4586541, 11.1389204, 0.0)

类似地,你可以从一个完整的地址请求地理定位,例如,苹果公司的总部。

# request 
geolocator.geocode("1 Apple Park Way, Cupertino, CA")# extract the coordinates:
('37.3337572', '-122.0113815')

Geopy 允许geocode操作将地址转换为坐标,而相反的reverse操作将位置转换为地址:

[In]: geolocator.reverse('37.3337572, -122.0113815')[Out]: Location(Apple Park, 1, Apple Park Way, Monta Vista, Cupertino, Santa Clara County, California, 94087, United States of America, (37.3348469, -122.01139215737962, 0.0))

完整教程使用 GeoPy 收集苹果总部的位置,然后。倒转来确认它是正确的地方

谷歌地理公园

您可以通过 Geopy 使用许多地图服务,只需提供 API 密钥或用户凭据。例如,使用谷歌地图 API:

geolocator = GoogleV3(api_key=AUTH_KEY)

请求地理位置使用与上面相同的代码:

[In]: geolocator.geocode("1 Apple Park Way, Cupertino, CA").point
[Out]: Point(37.3337572, -122.0113815, 0.0)

尽管响应 JSON 的结构将反映所使用的服务。Google 响应中的 Lat 和 Lon 存储在"geometry"."location"中。但是 Geopy 为您做了这项工作,您只需读取.point属性。

将地址列表转换为地理坐标

在地图上显示带有地址的数据集之前,您需要找到这些点的地理位置。让我们来看看这份地址名单:

data = """Name,Address
EU,"Rue de la Loi/Wetstraat 175, Brussel, Belgium"
Apple,"1 Apple Park Way, Cupertino, CA"
Google,"1600 Amphitheatre Parkway Mountain View, CA 94043"
UN,"760 United Nations Plaza; Manhattan, New York City"
"""

我们知道我们可以使用 GeoPy 的geolocator.geocode,但是将熊猫数据帧转换成坐标的最有效方法是什么呢?我喜欢 Abdishakur 在这篇文章中描述的方法

[## 使用 Python 进行地理编码

如何将物理地址转换为地理位置→经纬度

towardsdatascience.com](/geocode-with-python-161ec1e62b89)

  1. 我将从我们的列表中创建一个熊猫数据框架
df = pd.read_csv(io.StringIO(data))
df

2.将geolocator.geocode应用于地址栏

df["loc"] = df["Address"].apply(geolocator.geocode)

3.从地理编码的响应中获取包含纬度和经度的.point,如果它不是None

df["point"]= df["loc"].apply(lambda loc: tuple(loc.point) if loc else None)

4.将.point拆分成单独的列'lat' 'lon''altitude'

df[['lat', 'lon', 'altitude']] = pd.DataFrame(df['point'].to_list(), index=df.index)

步骤 2-4 将从地理编码的响应中挖掘地理位置

数据集中有'lat''loc'列就足以在任何背景图上显示这些点。

将地理点转换为地图

地理坐标是没有用的,除非你把它们显示在地图上。Python 提供了几个库来使这项任务变得足够简单。

  • leave库,使用leave . js在 OpenStreetMap 或任何其他地图数据提供者的顶部创建典型地图
  • plotly 使用其固有的地图数据

用叶子创建地图

树叶地图是交互式的,它们包含带有弹出窗口的标记,可以聚集在具有高密度点的区域中,您可以使用图层并选择不同的源地图。这很容易编码:

# import the library and its Marker clusterization service
import folium
from folium.plugins import MarkerCluster# Create a map object and center it to the avarage coordinates to m
m = folium.Map(location=df[["lat", "lon"]].mean().to_list(), zoom_start=2)# if the points are too close to each other, cluster them, create a cluster overlay with MarkerCluster, add to m
marker_cluster = MarkerCluster().add_to(m)# draw the markers and assign popup and hover texts
# add the markers the the cluster layers so that they are automatically clustered
for i,r in df.iterrows():
    location = (r["lat"], r["lon"])
    folium.Marker(location=location,
                      popup = r['Name'],
                      tooltip=r['Name'])\
    .add_to(marker_cluster)# display the map
m

一张有叶子的简单地图。硅谷聚集了太多的点。

使用 Plotly 创建地图

Plotly 越来越受欢迎。因为它引入了 Plotly express 接口,所以创建图表是一个单独的线性过程,这也适用于地图图表。下面我们来看看如何用 Plotly 创建一个类似的地图。

# import the plotly express
import plotly.express as px# set up the chart from the df dataFrame
fig = px.scatter_geo(df, 
                     # longitude is taken from the df["lon"] columns and latitude from df["lat"]
                     lon="lon", 
                     lat="lat", 
                     # choose the map chart's projection
                     projection="natural earth",
                     # columns which is in bold in the pop up
                     hover_name = "Name",
                     # format of the popup not to display these columns' data
                     hover_data = {"Name":False,
                                   "lon": False,
                                   "lat": False
                                     }
                     )

.scatter_geo(df)根据测向数据帧中的数据在地图上创建点,如位置或弹出窗口。我们可以为每个数据点设置不同的大小或颜色,但我们在 df 数据帧中没有这样做,所以我们可以使用.update_traces()为所有数据点指定相同的大小或颜色。

fig.update_traces(marker=dict(size=25, color="red"))

你不必像在 leav 中那样确定理想的缩放比例,只需将fitbounds设置为“位置”即可。您还可以显示国家、河流、海洋、湖泊,并用.update_geos指定它们的颜色。

fig.update_geos(fitbounds="locations", showcountries = True)

最后,让我们使用.update_layout添加一个标题,并通过fig.show()显示最终的图表。

fig.update_layout(title = "Your customers")
fig.show()

一个简单的地图。

将地图导出到 html

欣赏笔记本中的地图可能会令人满意,但偶尔您希望与没有安装 python 的人共享您的工作。幸运的是将地图导出到。html 只有一行代码。

Folium 的.save方法创建了一个文件,该文件从 CDN 获得所有必要的资源,如 jquery、bootstrap、leaflet,并且该文件只有 8 KB。

m.save("folium_map.html")

Plotly 使用带有几个可选参数的.write_html命令。最重要的是include_plotlyjs。您可以设置输出文件是否将包含超过 3 MB 的 plotly 库,以实现那些漂亮的图表效果,或者您是否只想要可以包含在另一个 plotly 项目中的数据点。在第二种情况下,大小将是 9 KB。

fig.write_html("plotly_map.html", include_plotlyjs=True)

结论

在地图上显示数据是展示公司数据的有效方式。Python 的库帮助我们快速收集必要的地理空间数据,并使用它们绘制地图。您可以使用开源数据或付费服务来完成这项任务。

你想寻找更多的灵感吗,试试 plotly 和 leav 地图集

[## 地图

Plotly 的 Python 图形库可以制作交互式、出版物质量的在线地图。如何使用…制作地图的示例

plotly.com](https://plotly.com/python/maps/) [## Jupyter 笔记本浏览器

编辑描述

nbviewer.jupyter.org](https://nbviewer.jupyter.org/github/python-visualization/folium/tree/master/examples/)

您喜欢本教程吗,请查看我的其他关于各种数据相关主题的文章:

[## python 交叉验证的完整指南及示例

sklearn 交叉验证的示例和使用案例解释了 k 折叠、洗牌、分层以及它如何影响…

towardsdatascience.com](/complete-guide-to-pythons-cross-validation-with-examples-a9676b5cac12) [## 阅读熊猫 CSV 时处理多余的空格

为什么我们关心空白?内置熊猫功能,自定义处理。创建 1M 测试数据和…

towardsdatascience.com](/dealing-with-extra-white-spaces-while-reading-csv-in-pandas-67b0c2b71e6a)

pytolemaic——模型质量工具箱

原文:https://towardsdatascience.com/pytolemaic-package-for-model-quality-analysis-2b7bea751cfd?source=collection_archive---------28-----------------------

图像由 PxFuel 提供

Pytolemaic 软件包简介

这篇博文简要介绍了 Pytolemaic 包( github )及其功能。该员额包括以下组成部分:

模型分析技术

  1. 特征敏感度
  2. 得分和置信区间
  3. 协变量移位测量

模型预测分析

  1. 预测的不确定性
  2. 石灰解释

介绍

如今,构建机器学习(ML)模型相当容易。然而,构建一个好的模型仍然需要经验来避免过程中的许多陷阱。幸运的是,有几种技术可以用来识别这些陷阱。

Pytolemaic 软件包使用这样的技术来分析 ML 模型并测量它们的质量。考虑到方便性,该软件包有一个简单的界面,使其易于使用。

该员额组织如下:

  1. 入门 —介绍如何安装 Pytolemaic 包和初始化 PyTrust 对象
  2. 特征敏感度 —涵盖特征敏感度报告和漏洞报告。
  3. 评分报告 —该部分分为两个部分:一部分是关于回归任务的评分报告,包括置信区间和协方差偏移,随后是关于分类任务的类似部分
  4. 预测的不确定性 —软件包支持的不确定性方法概述
  5. 石灰说明 —石灰整合部分。

入门指南

Pytolemaic 软件包是为基于结构化数据(泰坦尼克号在,MNIST 号在)训练的监督模型(回归和分类)而构建的。该模型被视为黑盒,因此不需要关于该模型的额外信息。如果在估算器之前有一个预处理阶段(如插补),则需要将其封装到一个单一的预测函数中,如通过使用 Sklearn 的管道类。

分析本身是相对轻量级的。但是,一些分析技术需要创建新的预测(例如 Lime),这一过程可能非常耗时且计算量大,具体取决于模型的复杂性和数据集的大小。

这种包装不适合搬运重物。如果它需要更多的肌肉,请让我知道。

装置

Pytolemaic 包托管在 github 上,并且在 Pypi.org 上可用,因此只需使用 pip:

pip install pytolemaic

启动 PyTrust 对象

要分析模型,只需使用以下信息启动一个 PyTrust 对象:

  1. [必需]定型模型、定型集、维持测试集和用于评估模型的度量。
  2. [可选]类别标签,拆分策略
  3. [可选]列的元数据:例如,功能名称、功能类型、功能编码
  4. [可选]样品的元数据:例如样品重量

虽然启动 PyTrust 对象非常简单,但它是包中最复杂的部分。因此,在第一次使用时,考虑只提供所需的信息。

让我们看看它是怎么做的

示例#0:启动 PyTrust

使用加州住房数据集启动 Pytrust

分析报道

A .特征敏感度(FS)
Pytolemaic 包实现了 FS 的 2 种变化——对混洗的敏感度,对缺失值的敏感度。调用py trust . sensitivity _ report()将计算这两种类型并返回一个 SensitivityFullReport 对象。

注意:如果你不熟悉特征敏感度方法,请看这篇伟大的文章。

检索 FS 信息有两种方式:
1 .API-sensitivity _ report . to _ dict()将报告导出为字典。
2。图形化-sensitivity _ report . plot()将绘制任何可绘制的信息。

示例
对于这个示例,我们将使用在数据集加州住房上训练的随机森林回归器(完整示例此处为)。加州住房数据集将一个地区的特征与该地区的中值房价相关联。

例#1:特征灵敏度分析

将敏感度完整报告导出为字典

像大多数报告一样,有些字段不清楚。因此,为了提供方便的文档,这个包提供了一个 to_dict_meaning() 功能。

示例#2:检索字典字段的文档:

敏感度报告字典中项目的说明

我们通过调用 to_dict() 看到了 FS 报告,并通过 to_dict_meaning()看到了可用的文档。现在我们通过调用 plot() 来图形化的看看。

示例#3:为特性敏感度报告创建图表

由创建的图表。plot()功能。

注意:函数 to_dict() 、to_dict_meaning()、a nd plot() 在 Pytolemaic 的所有报告中都有。

正如您所看到的,在特性敏感度报告中有 3 个质量度量:

  1. 插补—通过测量对洗牌的敏感度和对缺失值的敏感度之间的差异来测量插补的脆弱性。
  2. 泄漏—通过比较敏感度得分来衡量数据泄漏的可能性。
  3. Too _ many _ features 通过计算低敏感度要素的数量来测量是否使用了过多的要素。

注意:漏洞报告背后的逻辑将在另一篇文章中解释。

B .评分报告对于一个与上述 pytrust 对象相同的回归任务
,我们调用 pytrust.scoring_report() 来分析评分质量并创建一个 ScoringFullReport 对象。

和以前一样,我们将对加州住房数据集使用随机森林回归变量。

示例#4:创建评分报告

评分报告信息,回归任务

置信区间 metric _ scores为每个指标提供模型的性能(值)以及置信区间限制( ci_low & ci_high )。此外,它还提供了 ci_ratio —一个无量纲值,表示分数计算中的不确定性(越低越好)。在大多数情况下,性能评估的质量可以通过扩大测试集来提高。

协方差移位
分离 _ 质量 m 使用 ROC-AUC 测量训练集和测试集是否来自同一分布(也称为协变量移位)。因为这是一个“质量”指标,所以值越高越好。此处和处可以找到进一步的解释。

如前所述,使用为评分报告创建图表。剧情()

示例#5:为评分报告创建图表

评分报告图表,回归任务

可以看出,散点图包含误差线。这些误差线代表模型预测的不确定性。有关模型预测分析部分中不确定性计算的更多信息。

C .分类任务评分报告 分类任务评分报告结构相同,但提供的信息不同。

对于这个例子,我们将使用在 UCI 的成人数据集上训练的随机森林分类器。

例 6:创建评分报告

评分报告信息,分类任务

注意:在该数据集中,训练集和测试集具有不同的分布。在现实生活的数据集中,如此低的值会引起关注。

和以前一样,我们可以绘制一些图表。

示例#7:为评分报告创建图表

评分报告图表,分类任务

模型预测分析

A .预测的不确定性 Pytolemaic 软件包可以对模型预测中的不确定性进行估计。在回归任务的情况下,不确定性值代表误差条,与目标变量具有相同的标度。另一方面,在分类任务的情况下,不确定性值表示模型在预测中的不确定性,范围为 0(最大置信度)到 1(无置信度)。

该包支持几种技术,如下所列。因此,不确定度值的确切含义取决于所使用的方法。

回归:

  • MAE :基于对测试集预测的绝对误差进行训练的回归量的绝对误差估计。
  • RMSE :根据测试集预测的平方误差训练的回归量对绝对误差的估计。

分类:

  • 概率:基于第一和第二最可能类的概率值之比的不确定性度量。
  • 置信度:基于测试集预测训练的分类器的不确定性度量。

示例
对于不确定性示例,我们将像以前一样使用成人数据集。然而,这一次我们将只使用测试集的一半来初始化 PyTrust 对象,并使用另一半(姑且称之为预测集)来查看不确定性度量与预测误差之间的关系。

例 8:根据“置信度”计算不确定度

完整代码在此

我们预计,不确定性较高的样本被错误分类的几率较高。现在,我们将根据预测集的样本各自的不确定性,通过宁滨验证这一点,然后测量每个箱中样本的召回率。

按照这个过程(代码,此处为)我们得到了下图,它的行为就像我们预期的那样。

整个数据集的召回分数是 0.762。频段为[0–0.2,0.2–0.4,0.4–0.6,0.6–0.8,0.8–1.0]

B .石灰说明

对模型预测的时间解释是一种众所周知的方法。Pytolemaic 包本质上包装了 lime 的功能,同时在两个重要方面对其进行了改进:

  1. 引入一种简单的插补方法来克服 lime 对缺失值的脆弱性。
  2. 引入收敛机制以克服 lime 对生成的样本的敏感性。

摘要

该包实现了帮助验证模型是否按预期工作的技术。该软件包易于使用,旨在模型构建阶段使用,所以试试吧,让我知道你的想法。

在以后的文章中,我将详细阐述各种质量度量背后的逻辑,以及该软件包如何帮助您识别错误。

未来的工作将侧重于模型的预测(解释和不确定性)和测量数据集的质量。

我希望你喜欢这篇文章,并且发现 Pytolemaic 软件包很有趣。

PyTorch 3D 在 CVPR 2020

原文:https://towardsdatascience.com/pytorch-3d-in-cvpr-2020-ea7849dfd6e8?source=collection_archive---------46-----------------------

脸书如何利用 PyTorch 框架实现三维目的

计算机视觉和模式识别大会( CVPR )是计算机视觉和模式识别领域最新进展和趋势的顶级会议之一。它每年举行一次,由独立研究人员的高质量论文和大公司研发实验室的突破组成。由于今年新冠肺炎造成的疫情,组织者决定从 6 月 14 日至 19 日虚拟举办 CVPR 2020。尽管可能有所不同,但虚拟方面并没有在任何意义上降低会议上提交的论文和研究的绝对质量。

在今年的 CVPR 会议上,脸书人工智能的研究人员成功地在计算机视觉的许多重要和相对较新的领域推进了最先进模型的边界,其中主要是推理常规 2D 图像中显示的 3D 对象的新方法。利用公平的开源机器学习框架 PyTorch 3D 的众多功能,这项工作可以帮助解锁众多 AR/VR 增强功能,并成为在不久的将来塑造其他技术的关键。

在复杂的真实世界场景中,从单一图像获得不同的视角

脸书人工智能研究中心(FAIR)的研究人员建造了 SynSin,这是一个最先进的模型,它从完全不同的角度拍摄一张 RGB 图像,并生成同一场景的新图像。所提出的系统通过使用 PyTorch 3D 中实现的新颖的可区分渲染器,将预测的 3D 点云投影到场景的另一个视图上来工作。基于绘制的点云输入,使用生成式对抗网络(GAN)来合成输出图像。

由怀尔斯等人提供。来源

由于 SynSin 依赖于渲染的 3D 点云作为其场景生成的基础,因此由于渲染的点云的灵活性,它能够生成不同分辨率的图像,与当代方法相比效率更高。

我们可以生成高分辨率图像,并推广到其他输入分辨率。

怀尔斯等人— 来源

3D 点云的投影特征也由作者提出的细化网络解码,以帮助填充缺失区域并生成更真实的最终输出图像。与最近经常使用密集体素网格的方法相比,本文作者的方法能够扩展到复杂现实场景中的合成场景生成。这是目前大多数方法都无法达到的相当高的精确度。

由 Wiles、Gkioxari、Szeliski 和 Johnson 撰写的完整论文可在此处查看,更深入地了解他们的可区分渲染器和 GAN 的细微差别。

从单幅图像中以前所未有的细节和质量水平重建三维人体模型

脸书人工智能的研究人员基于 Saito 等人在 2019 年发布的像素对齐隐函数(皮夫)方法,创建了一个多层神经网络,以开发一种从 2D 图像中生成人的 3D 重建的方法,这种方法能够捕捉最先进的复杂性和细节,并将其渲染为 3D 模型。高度具体的细节,如手指,面部特征和衣服褶皱,都是使用高分辨率照片作为网络的输入来捕捉的。

斋藤等人提供。来源

根据其功能,创建的网络可以大致分为两个主要部分。第一部分(或第一级网络)通过利用较低分辨率的图像来重建人类的 3D 结构。这部分类似于研究人员建立的皮夫方法。第二个网络在本质上更加轻量级,并利用更高分辨率的图像来捕捉和呈现人类更精细的方面。

通过允许从第一级访问全局 3D 信息,我们的系统可以有效地利用局部和全局信息进行高分辨率 3D 人体重建。

Saito 等人来源

研究人员为这种精细的 3D 人类重建所采用的方法可能会被证明是 AR/VR 体验以及几个电子商务应用程序的一个重大推动。

论文全文由斋藤、黄、、森岛、金泽和在这里找到。

融合图像中的 2D 投票和云中的 3D 投票

脸书人工智能的研究人员发表了一篇更多地涉及理论方法而不是应用的论文 ImVoteNet,这是一种改进的 3D 对象检测架构,专门用于 RGB-D 场景。他们探索了来自 2D 图像的数据如何帮助基于投票的 3D 检测管道。一些最近的作品(如 VoteNet )展示了仅利用点云的艺术表演状态。ImVoteNet 论文建立在 VoteNet 的体系结构上,并将点云提供的 3D 几何图形与图像的高分辨率和纹理相融合,以从 2D 图像中提取几何和语义特征。

由齐等人提供— 来源

尽管点云在有效检测三维物体方面是有用的,但是它们的数据通常具有固有的局限性。由点云获得的数据通常是稀疏的,缺乏颜色信息,有时会受到传感器噪声的影响。通过使用多塔训练方案融合 2D 图像和 3D 点云的特征,帮助研究人员从两种源图像中提取最佳效果。研究人员成功地从 2D 图像中提取了几何和语义特征,并利用相机参数将其提升到 3D。所建立的系统依靠一种旋转机制来有效地聚集点云中的几何信息。ImVoteNet 负责在点云本质上稀疏或分布不利的设置中,使用具有梯度混合的多模态训练来显著提高 3D 对象检测。

齐、陈、利塔尼和吉巴斯的论文全文可以在这里找到。

尽管这些论文很吸引人,但 CVPR 拥有的远不止上面展示的。竞赛、专家讲座和研讨会是一个人需要留意的。从 去像素化图像到潜在自动编码器,每年都会展示一些最具创新性和最令人兴奋的论文。更多关于 CVPR 的信息,接受的论文,比赛和演讲可以在它的主页上找到。

PyTorch 和 GANs:微型教程

原文:https://towardsdatascience.com/pytorch-and-gans-a-micro-tutorial-804855817a6b?source=collection_archive---------28-----------------------

在 PyTorch 建造最简单的 GANs

图片来源: Pixabay

在tensor flow/Keras中花了很长时间制作 GANs 。太久了,说实话,因为改变很难。这需要一些说服,但我最终咬紧牙关,并交换到 PyTorch。不幸的是,我遇到的大多数 PyTorch GAN 教程都过于复杂,更多地关注 GAN 理论而不是应用,或者奇怪地不和谐。为了补救这一点,我写了这个用 PyTorch 制作香草 GAN 的微型教程,重点是 PyTorch。代码本身可以在这里获得(注意 github 代码和本教程中的 gists 略有不同)。我建议在两个窗口中打开本教程,一个窗口显示代码,另一个窗口显示解释。

要求

  1. Python 3.7 以上版本。再低的话,你就要重构 f 弦了。
  2. PyTorch 1.5 不确定怎么安装?这可能会有所帮助。
  3. 你十七八分钟的时间。如果你够聪明的话,可以少到 12 个。

手头的任务

创建一个函数 G: Z → X 其中 Z~U(0,1)和 X~N(0,1)。

用英语来说,就是“在给定均匀随机噪声作为输入的情况下,生成一个近似于正态分布的 GAN”。这意味着 GAN 的输入将是一个单一的数字,输出也是如此。注意,为了简单起见,我们将使用数据生成函数,而不是训练数据集。

图 1:输入“噪声”(左图)和目标输出样本(右图)的分布。

让我们开始吧

确保您已经安装了 Python 的正确版本,并安装 PyTorch。然后,创建一个新文件vanilla_GAN.py,并添加以下导入:

import torch
from torch import nn
import torch.optim as optim

我们的 GAN 脚本将有三个组件:一个生成器网络、一个鉴别器网络和 GAN 本身,它容纳并训练这两个网络。让我们从发电机开始:

发电机

将以下内容添加到您的脚本中:

我们的生成器类继承自 PyTorch 的nn.Module类,它是神经网络模块的基类。简而言之,它告诉 PyTorch“这是一个神经网络”。我们的生成器类有两个方法:

发电机。init

初始化对象。首先,使用super调用nn.Module __init__方法。然后,它创建子模块(即层)并将它们作为实例变量分配。其中包括:

  • 具有输入宽度latent_dim和输出宽度 64 的线性(即全连接,即密集)层。
  • 具有输入宽度 64 和输出宽度 32 的线性层。
  • 输入宽度为 32、输出宽度为 1 的线性图层。
  • LeakyReLU 激活。
  • 指定的输出激活。

因为这些模块作为实例变量保存到从nn.Module继承的类中,PyTorch 能够在训练网络时跟踪它们;稍后会详细介绍。

发电机.前进

forward方法对于任何从nn.Module继承的类都是必不可少的,因为它定义了网络的结构。PyTorch 使用一个运行定义框架,这意味着神经网络的计算图是在你将简单计算链接在一起时自动构建的。都很有 Pythonic 的味道。在我们的forward方法中,我们遍历生成器的模块,并将它们应用于前一个模块的输出,返回最终输出。当你运行网络时(例如:prediction = network(data)),forward方法被调用来计算输出。

发电机向后

没有。PyTorch 使用自动识别;当您运行forward方法时,PyTorch 自动跟踪计算图形,因此您不必告诉它如何反向传播梯度。这在实践中是什么样的?继续读。

图 2:生成器架构。输入(潜在)维度为 1,内部全连接层的维度为 64 和 32,输出(样本)维度为 1。泄漏 ReLU 激活应用于两个内层(红色箭头)。没有激活功能应用于输出。

鉴别器

将以下内容添加到您的脚本中:

我们的鉴别器对象将与我们的生成器几乎相同,但是查看该类,您可能会注意到两个不同之处。首先,网络已经参数化,并稍微进行了重构,以使其更加灵活。第二,输出函数已经固定为Sigmoid,因为鉴别器将负责将样本分类为真实(1)或生成(0)。

鉴别器。init

鉴别器__init__ 方法做三件事。同样,它使用super调用nn.Module__init__方法。然后,它将输入维度保存为对象变量。最后,它调用_init_layers方法。作为参数,__init__ 接受一个输入维度和一个名为layers的整数列表,它描述了 nn 的宽度。线性模块,包括输出层。

鉴别器。_ 初始化 _ 层

该方法实例化网络模块。这个方法的主体可以放在__init__中,但是我发现将对象初始化样板文件从模块构建代码中分离出来会更干净,尤其是随着网络复杂性的增加。该方法迭代layers参数,并实例化一个适当大小的nn.Linear模块列表,以及每个内部层之后的泄漏 ReLU 激活和最后一层之后的 Sigmoid 激活。这些模块存储在一个ModuleList对象中,该对象的功能类似于一个常规的 Python 列表,只是 PyTorch 在训练网络时将它识别为一个模块列表。还有一个ModuleDict类服务于同样的目的,但是功能类似于 Python 字典;稍后将详细介绍这些内容。

鉴别器.转发

forward方法与其在生成器中的对等方法功能相同。然而,由于我们将模块保存为一个列表,我们可以简单地遍历该列表,依次应用每个模块。

图 3:我们将最终使用的鉴别器架构,input_dim=1,layers=[64,32,1]。它与发生器相同,只是它使用 Sigmoid 作为其输出激活函数,此处用绿色箭头表示。

香草甘

将以下内容添加到您的脚本中:

我们的 VanillaGAN 类包含生成器和鉴别器对象,并处理它们的训练。

香草甘。init

作为输入,VanillaGAN 构造函数接受:

  • 生成器对象。
  • 鉴别器对象。
  • 噪声函数。这是用于对潜在向量 Z 进行采样的函数,我们的生成器会将其映射到生成的样本 x。该函数必须接受一个整数num作为输入,并返回一个形状为(num, latent_dim)的 2D 火炬张量。
  • 数据函数。这是我们的生成器要学习的功能。这个函数必须接受一个整数num作为输入,并返回一个形状为(num, data_dim)的 2D 火炬张量,其中data_dim是我们试图生成的数据的维度,我们的鉴别器的input_dim
  • 可选,训练迷你批次大小。
  • 可选地,设备。这可以是cpu或者cuda如果你想用 GPU 的话。
  • 可选地,发生器和鉴别器的学习率。

在适当的情况下,这些参数被保存为实例变量。

GAN 的目标是二进制交叉熵损失 ( nn.BCELoss),我们将其实例化并指定为对象变量criterion

我们的 GAN 使用两个优化器,一个用于生成器,一个用于鉴别器。让我们分解生成器的优化器,一个[Adam](https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Adam)实例。给定梯度,优化器管理神经网络参数的更新。为了做到这一点,优化器需要知道它应该关注哪些参数;在这种情况下,那就是discriminator.parameters()。几分钟前我告诉过你

PyTorch 能够在训练网络时跟踪[模块]。

因为 Discriminator 对象继承自nn.Module,所以它继承了parameters方法,该方法返回所有模块中的所有可训练参数,设置为 Discriminator 的实例变量(这就是为什么我们必须使用nn.ModuleList而不是 Python 列表,以便 PyTorch 知道检查每个元素的参数)。优化器还被赋予一个特定的学习率和 beta 参数,这些参数对 GANs 很有效。生成器的优化器以同样的方式工作,只是它跟踪生成器的参数,并使用稍小的学习速率。

最后,我们存储一个 1 的列向量和一个 0 的列向量作为训练的类标签,这样我们就不用重复地重新实例化它们了。

VanillaGAN.generate_samples

这是一个从生成器中获取随机样本的辅助函数。在没有任何参数的情况下调用,它生成batch_size个样本。这可以通过指定num参数来产生num样本,或者通过为其提供包含指定潜在向量的 2D PyTorch 张量来覆盖。上下文管理器告诉 PyTorch 不要在这里跟踪梯度,减少计算量。

香草根. train_step_generator

该函数在生成器上执行一个训练步骤,并以浮点形式返回损失。除了鉴别器训练步骤之外,这也是算法的关键,因此让我们一行一行地来看一下:

self.generator.zero_grad()

清除渐变。PyTorch 最酷的地方在于,随着网络的使用,梯度会在每个参数中自动累积。然而,我们通常希望在优化器的每个步骤之间清除这些梯度;zero_grad方法就是这么做的。

latent_vec = self.noise_fn(self.batch_size)

来自噪声生成函数的样本batch_size潜在向量。简单。

generated = self.generator(latent_vec)

将潜在向量送入生成器,并将生成的样本作为输出(此处称为generator.forward方法)。记住,PyTorch 是由 run 定义的,所以这是构建生成器计算图的地方。

classifications = self.discriminator(generated)

将生成的样本输入鉴别器,并获得它对每个样本都是真实的信心。记住,鉴别器试图将这些样本归类为假的(0),而生成器试图欺骗它认为它们是真的(1)。正如上一行一样,这是构建鉴别器计算图的地方,因为它被给定生成的样本generated作为输入,所以这个计算图被固定在生成器计算图的末端。

loss = self.criterion(classifications, self.target_ones)

计算发电机的损耗。我们的损失函数是二进制交叉熵,因此计算每个batch_size样本的损失并平均为单个值。loss是一个 PyTorch 张量,其中只有一个值,所以它仍然连接到完整的计算图。

loss.backward()

这就是奇迹发生的地方。或者更确切地说,这是声望发生的地方,因为魔术一直在无形中发生。这里,backward方法为计算图中的每个参数 x 计算梯度 d_loss/d_x。

self.optim_g.step()

应用优化器的一个步骤,将每个参数向下渐变。如果你以前在 Keras 中构建过 GAN,你可能对设置my_network.trainable = False很熟悉。PyTorch 的优势之一是您不必为此烦恼,因为optim_g被告知只关心我们的生成器参数。

return loss.item()

退回损失。我们将把这些存储在一个列表中,以便以后可视化。然而,重要的是我们使用item方法将它作为浮点数返回,而不是作为 PyTorch 张量。这是因为,如果我们在一个列表中保存一个对张量对象的引用,Python 也将保留整个计算图。这是对内存的极大浪费,所以我们需要确保只保留我们需要的东西(值),以便 Python 的垃圾收集器可以清理其余的。

vanillagan . train _ step _ discriminator

像前面的方法一样,train_step_discriminator为鉴别器执行一个训练步骤。让我们一行一行地浏览一下:

self.discriminator.zero_grad()

你知道这个!

# real samples
real_samples = self.data_fn(self.batch_size)
pred_real = self.discriminator(real_samples)
loss_real = self.criterion(pred_real, self.target_ones)

从目标函数中抽取一些真实的样本,让鉴别者确信它们是真实的(鉴别者想要最大化这个!),并计算损失。这与生成器的训练步骤非常相似。

# generated samples
latent_vec = self.noise_fn(self.batch_size)
with torch.no_grad():
    fake_samples = self.generator(latent_vec)
pred_fake = self.discriminator(fake_samples)
loss_fake = self.criterion(pred_fake, self.target_zeros)

从生成器中采样一些生成的样本,让鉴别器确信它们是真实的(鉴别器想要最小化这一点!),并计算损失。因为我们在这里训练鉴别器,我们不关心生成器中的梯度,因此我们使用no_grad上下文管理器。或者,你可以放弃no_grad,代之以行pred_fake = self.discriminator(fake_samples.detach()),然后在事后将fake_samples从生成器的计算图中分离出来,但是为什么要首先计算它呢?

# combine
loss = (loss_real + loss_fake) / 2

对真实样本和生成样本的计算图进行平均。是的,真的是这样。这是整个脚本中我最喜欢的一行,因为 PyTorch 能够使用简单的 Python 算法将计算图的两个阶段结合起来。

loss.backward()
self.optim_d.step()
return loss_real.item(), loss_fake.item()

计算梯度,应用梯度下降的一个步骤,并返回损失。

VanillaGAN.train_step

该方法仅应用鉴别器的一个训练步骤和生成器的一个步骤,将损失作为元组返回。

图 4:整体 GAN 架构

把所有的放在一起

将以下内容添加到您的脚本中:

main函数非常简单明了,但是为了完整起见,让我们一起来看一下。

  • 我们导入time是因为对神经网络训练计时通常是个好主意。
  • 我们将有 600 个纪元,每个纪元有 10 个批次;批处理和历元在这里是不必要的,因为我们使用的是 true 函数而不是 dataset,但是为了方便起见,我们还是坚持这个约定。
  • 我们实例化生成器和鉴别器。记住,我们必须指定鉴频器的层宽。
  • 我们将噪声函数定义为[0,1]中的随机均匀值,表示为列向量。我们将设备指定为“cpu ”,但如果设置好了,也可以是“CUDA”。注意,如果您在这里使用 cuda,请将其用于目标函数和 VanillaGAN。
  • 我们将目标函数定义为表示为列向量的随机、正常(0,1)值。同样,我们将设备指定为“cpu”。
  • 我们实例化香草醛。
  • 我们设置列表来跟踪损失并运行训练循环,在每个时期后打印训练统计数据。

就是这样!恭喜,你在 PyTorch 写了你的第一篇 GAN。我没有包括可视化代码,但下面是每个训练步骤后学习到的分布 G 的样子:

图 vanilla GAN 学习在 600 个时期内从 U(0,1)输入产生 N(0,1)个样本的动画。蓝色条是描述分布的直方图,灰色曲线是目标分布。

这是每个时期的损失:

图 6:培训过程中的各种损失。

结束语

因为本教程是关于在 PyTorch 中构建 GAN 类和训练循环的,所以很少考虑实际的网络架构。没有使用现代的“GAN hacks ”,因此最终分布只是粗略地类似于真正的标准正态分布。如果你有兴趣学习更多关于 GANs 的知识,试着调整超参数和模块;结果与你的预期相符吗?

我们通常无法访问真正的生成数据的发行版(如果可以,我们就不需要 GAN 了!).在本教程的后续教程中,我们将实现一个卷积 GAN,它使用真实的目标数据集而不是函数。

所有未引用的图片都是我自己的。请随意使用它们,但请引用❤️的这篇文章

[## ConorLazarou/py torch-生成模型

VanillaGAN——py torch 中实现的最简单的 GAN

github.com](https://github.com/ConorLazarou/pytorch-generative-models/blob/master/GAN/vanilla_GAN/vanilla_GAN.py)

Pytorch【基础】CNN 简介

原文:https://towardsdatascience.com/pytorch-basics-how-to-train-your-neural-net-intro-to-cnn-26a14c2ea29?source=collection_archive---------1-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

这篇博文将带你了解 PyTorch 中不同类型的 CNN 操作。

在这篇博文中,我们将使用torch.nn实现 1D 和 2D 卷积。

什么是 CNN?

卷积神经网络是一种主要用于图像处理应用的神经网络。CNN 的其他应用包括音频、时间序列和 NLP 等序列数据。卷积是 CNN 的主要组成部分之一。术语卷积是指两个函数的数学组合产生第三个函数。它融合了两组信息。

我们不会在这里讨论很多理论。关于这一点,网上有很多精彩的材料。

CNN 运作的类型

CNN 主要用于围绕图像、音频、视频、文本和时间序列建模的应用。有 3 种卷积运算。

  • 1D 卷积 —主要用于文本或音频等连续输入的场合。
  • 2D 卷积 —主要用于输入为图像的情况。
  • 3D 卷积 —主要用于 3D 医学成像或检测视频中的事件。这超出了这篇博文的范围。我们将只关注前两个。

1D 输入的 1D 卷积

滤波器沿一维滑动以产生输出。以下图表摘自这个 Stackoverflow 答案。

1D 输入的 1D 卷积[图像[1] 信用点

2D 输入的 1D 卷积

2D 输入的 1D 卷积[图像[2] 信用点

2D 输入的 2D 卷积

2D 输入的 2D 卷积[图像[3] 信用点

查看这个 Stackoverflow 回答以获得更多关于不同类型的 CNN 操作的信息。

几个关键术语

术语解释了 2D 卷积和 2D 输入即。图像,因为我找不到 1D 卷积的相关可视化。所有的可视化都是从到这里的。

卷积运算

要计算卷积运算后的输出维度,我们可以使用以下公式。

卷积输出公式[图像[4]]

内核/滤波器滑过输入信号,如下所示。你可以看到滤波器(绿色方块)滑过我们的输入(蓝色方块),卷积的总和进入特征图(红色方块)。

卷积运算[图像[5]]

过滤器/内核

使用滤波器对输入图像执行卷积。卷积的输出被称为特征图。

过滤器[图像[6]]

在 CNN 术语中,3×3 矩阵被称为“滤波器”或“内核”或“特征检测器”,通过在图像上滑动滤波器并计算点积而形成的矩阵被称为“卷积特征”或“激活图”或“特征图”。重要的是要注意,过滤器充当来自原始输入图像的特征检测器。

更多过滤器=更多特征地图=更多特征。

过滤器只不过是一个数字矩阵。以下是不同类型的过滤器—

不同类型的过滤器[图片[7]]

进展

步幅指定我们在每一步移动卷积滤波器的程度。

步幅为 1[图片[8]]

如果我们想减少感受野之间的重叠,我们可以有更大的进展。这也使得生成的特征地图更小,因为我们跳过了潜在的位置。下图演示了步幅为 2。请注意,特征图变小了。

步幅为 2[图片[9]]

填料

这里我们保留了更多的边界信息,也保留了图像的大小。

填充[Image [10]]

我们看到特征映射的大小小于输入,因为卷积滤波器需要包含在输入中。如果我们想要保持相同的维度,我们可以使用填充用零包围输入。

联营

我们应用池来降低维度。

最大池[Image [11]]

  • 汇集减少了输入的大小,并使特征尺寸更小。
  • 由于空间尺寸减小,网络中的参数数量减少。这有助于防止过度拟合。
  • 池化使网络对图像中的失真具有鲁棒性,因为我们采用了集合(最大值、总和、平均值等)。)的像素值。

导入库

import numpy as npimport torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

输入数据

首先,我们定义几个输入张量,我们将在这篇博文中使用它们。

input_1d是 1 维浮点张量。input_2d是一个二维浮点张量。input_2d_img是一个表示图像的三维浮动张量。

input_1d = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype = torch.float)input_2d = torch.tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]], dtype = torch.float)input_2d_img = torch.tensor([[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]], dtype = torch.float) ###################### OUTPUT ######################Input 1D:input_1d.shape:  torch.Size([10])input_1d: 
 tensor([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
====================================================================Input 2D:input_2d.shape:  torch.Size([2, 5])input_2d:
 tensor([[ 1.,  2.,  3.,  4.,  5.],
        [ 6.,  7.,  8.,  9., 10.]])
====================================================================input_2d_img:input_2d_img.shape:  torch.Size([3, 3, 10])input_2d_img:
 tensor([[[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.],
         [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.],
         [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]], [[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.],
         [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.],
         [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]], [[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.],
         [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.],
         [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]]])

1D 卷积

nn.Conv1d()对输入应用 1D 卷积。nn.Conv1d()期望输入是[batch_size, input_channels, signal_length]的形状。

您可以在官方 PyTorch 文档中查看完整的参数列表。所需的参数是—

  • in _ channels(python:int)—输入信号的通道数。这应该等于输入张量中的通道数。
  • out _ channels(python:int)—卷积产生的通道数。
  • kernel _ Size(python:int 或 tuple ) —卷积内核的大小。

Conv1d —输入 1d

Conv1d-Input1d 示例[图像[12] 信用点

输入是由 10 个数字组成的 1D 信号。我们将把它转换成大小为[1,1,10]的张量。

input_1d = input_1d.unsqueeze(0).unsqueeze(0)
input_1d.shape ###################### OUTPUT ######################torch.Size([1, 1, 10])

CNN 输出带**out_channels=1****kernel_size=3****stride=1**

cnn1d_1 = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=3, stride=1)print("cnn1d_1: \n")
print(cnn1d_1(input_1d).shape, "\n")
print(cnn1d_1(input_1d)) ###################### OUTPUT ######################cnn1d_1: torch.Size([1, 1, 8]) tensor([[[-1.2353, -1.4051, -1.5749, -1.7447, -1.9145, -2.0843, -2.2541, -2.4239]]], grad_fn=<SqueezeBackward1>)

CNN 用**out_channels=1****kernel_size=3****stride=2**输出。

cnn1d_2 = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=3, stride=2)print("cnn1d_2: \n")
print(cnn1d_2(input_1d).shape, "\n")
print(cnn1d_2(input_1d)) ###################### OUTPUT ######################cnn1d_2: torch.Size([1, 1, 4]) tensor([[[0.5107, 0.3528, 0.1948, 0.0368]]], grad_fn=<SqueezeBackward1>)

**out_channels=1****kernel_size=2****stride=1**的 CNN 输出。

cnn1d_3 = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=2, stride=1)print("cnn1d_3: \n")
print(cnn1d_3(input_1d).shape, "\n")
print(cnn1d_3(input_1d)) ###################### OUTPUT ######################cnn1d_3: torch.Size([1, 1, 9]) tensor([[[0.0978, 0.2221, 0.3465, 0.4708, 0.5952, 0.7196, 0.8439, 0.9683, 1.0926]]], grad_fn=<SqueezeBackward1>)

CNN 用**out_channels=5****kernel_size=3****stride=2**输出。

cnn1d_4 = nn.Conv1d(in_channels=1, out_channels=5, kernel_size=3, stride=1)print("cnn1d_4: \n")
print(cnn1d_4(input_1d).shape, "\n")
print(cnn1d_4(input_1d)) ###################### OUTPUT ######################cnn1d_4: torch.Size([1, 5, 8]) tensor([[[-1.8410e+00, -2.8884e+00, -3.9358e+00, -4.9832e+00, -6.0307e+00,-7.0781e+00, -8.1255e+00, -9.1729e+00],
         [-4.6073e-02, -3.4436e-02, -2.2799e-02, -1.1162e-02,  4.7439e-04,1.2111e-02,  2.3748e-02,  3.5385e-02],
         [-1.5541e+00, -1.8505e+00, -2.1469e+00, -2.4433e+00, -2.7397e+00, -3.0361e+00, -3.3325e+00, -3.6289e+00],
         [ 6.6593e-01,  1.2362e+00,  1.8066e+00,  2.3769e+00,  2.9472e+00, 3.5175e+00,  4.0878e+00,  4.6581e+00],
         [ 2.0414e-01,  6.0421e-01,  1.0043e+00,  1.4044e+00,  1.8044e+00,2.2045e+00,  2.6046e+00,  3.0046e+00]]], 
grad_fn=<SqueezeBackward1>)

Conv1d —输入 2d

要对 2d 输入信号应用 1D 卷积,我们可以执行以下操作。首先,我们定义大小为[1,2,5]的输入张量,其中batch_size = 1input_channels = 2signal_length = 5

input_2d = input_2d.unsqueeze(0)
input_2d.shape ###################### OUTPUT ######################torch.Size([1, 2, 5])

CNN 输出有**in_channels=2****out_channels=1****kernel_size=3****stride=1**

cnn1d_5 = nn.Conv1d(in_channels=2, out_channels=1, kernel_size=3, stride=1)print("cnn1d_5: \n")
print(cnn1d_5(input_2d).shape, "\n")
print(cnn1d_5(input_2d)) ###################### OUTPUT ######################cnn1d_5: torch.Size([1, 1, 3]) tensor([[[-6.6836, -7.6893, -8.6950]]], grad_fn=<SqueezeBackward1>)

CNN 输出有**in_channels=2****out_channels=1****kernel_size=3****stride=2**

cnn1d_6 = nn.Conv1d(in_channels=2, out_channels=1, kernel_size=3, stride=2)print("cnn1d_6: \n")
print(cnn1d_6(input_2d).shape, "\n")
print(cnn1d_6(input_2d)) ###################### OUTPUT ######################cnn1d_6: torch.Size([1, 1, 2]) tensor([[[-3.4744, -3.7142]]], grad_fn=<SqueezeBackward1>)

CNN 输出有**in_channels=2****out_channels=1****kernel_size=2****stride=1**

cnn1d_7 = nn.Conv1d(in_channels=2, out_channels=1, kernel_size=2, stride=1)print("cnn1d_7: \n")
print(cnn1d_7(input_2d).shape, "\n")
print(cnn1d_7(input_2d)) ###################### OUTPUT ######################cnn1d_7: torch.Size([1, 1, 4]) tensor([[[0.5619, 0.6910, 0.8201, 0.9492]]], grad_fn=<SqueezeBackward1>)

CNN 输出有**in_channels=2****out_channels=5****kernel_size=3****stride=1**

cnn1d_8 = nn.Conv1d(in_channels=2, out_channels=5, kernel_size=3, stride=1)print("cnn1d_8: \n")
print(cnn1d_8(input_2d).shape, "\n")
print(cnn1d_8(input_2d)) ###################### OUTPUT ######################cnn1d_8: torch.Size([1, 5, 3]) tensor([[[ 1.5024,  2.4199,  3.3373],
         [ 0.2980, -0.0873, -0.4727],
         [ 1.5443,  1.7086,  1.8729],
         [ 2.6177,  3.2974,  3.9772],
         [-2.5145, -2.2906, -2.0668]]], grad_fn=<SqueezeBackward1>)

2D 卷积

nn.Conv2d()对输入应用 2D 卷积。nn.Conv2d()期望输入为[batch_size, input_channels, input_height, input_width]的形状。

您可以在官方 PyTorch 文档中查看完整的参数列表。所需的参数是—

  • in _ channels(python:int)—2d 输入(如图像)中的通道数。
  • out _ channels(python:int)—卷积产生的通道数。
  • kernel _ Size(python:int 或 tuple ) —卷积内核的大小

Conv2d —输入 2d

与 3 个通道卷积[图像[13] 信用点

要对 2d 输入信号(例如图像)应用 2D 卷积,我们可以执行以下操作。首先,我们定义大小为[1,3,3,10]的输入张量,其中有batch_size = 1input_channels = 3input_height = 3input_width = 10

input_2d_img = input_2d_img.unsqueeze(0)
input_2d_img.shape ###################### OUTPUT ######################torch.Size([1, 3, 3, 10])

CNN 输出有**in_channels=3****out_channels=1****kernel_size=3****stride=1**

cnn2d_1 = nn.Conv2d(in_channels=3, out_channels=1, kernel_size=3, stride=1)print("cnn2d_1: \n")
print(cnn2d_1(input_2d_img).shape, "\n")
print(cnn2d_1(input_2d_img)) ###################### OUTPUT ######################cnn2d_1: torch.Size([1, 1, 1, 8]) tensor([[[[-1.0716, -1.5742, -2.0768, -2.5793, -3.0819, -3.5844, -4.0870,-4.5896]]]], grad_fn=<MkldnnConvolutionBackward>)

CNN 输出有**in_channels=3****out_channels=1****kernel_size=3****stride=2**

cnn2d_2 = nn.Conv2d(in_channels=3, out_channels=1, kernel_size=3, stride=2)print("cnn2d_2: \n")
print(cnn2d_2(input_2d_img).shape, "\n")
print(cnn2d_2(input_2d_img)) ###################### OUTPUT ######################cnn2d_2: torch.Size([1, 1, 1, 4]) tensor([[[[-0.7407, -1.2801, -1.8195, -2.3590]]]],
       grad_fn=<MkldnnConvolutionBackward>)

CNN 输出有**in_channels=3****out_channels=1****kernel_size=2****stride=1**

cnn2d_3 = nn.Conv2d(in_channels=3, out_channels=1, kernel_size=2, stride=1)print("cnn2d_3: \n")
print(cnn2d_3(input_2d_img).shape, "\n")
print(cnn2d_3(input_2d_img)) ###################### OUTPUT ######################cnn2d_3: torch.Size([1, 1, 2, 9]) tensor([[[[-0.8046, -1.5066, -2.2086, -2.9107, -3.6127, -4.3147, -5.0167, -5.7188, -6.4208],
          [-0.8046, -1.5066, -2.2086, -2.9107, -3.6127, -4.3147, -5.0167,-5.7188, -6.4208]]]], grad_fn=<MkldnnConvolutionBackward>)

CNN 输出有**in_channels=3****out_channels=5****kernel_size=3****stride=1**

cnn2d_4 = nn.Conv2d(in_channels=3, out_channels=5, kernel_size=3, stride=1)print("cnn2d_4: \n")
print(cnn2d_4(input_2d_img).shape, "\n")
print(cnn2d_4(input_2d_img)) ###################### OUTPUT ######################cnn2d_4: torch.Size([1, 5, 1, 8]) tensor([[[[-2.0868e+00, -2.7669e+00, -3.4470e+00, -4.1271e+00, -4.8072e+00, -5.4873e+00, -6.1673e+00, -6.8474e+00]], [[-4.5052e-01, -5.5917e-01, -6.6783e-01, -7.7648e-01, -8.8514e-01, -9.9380e-01, -1.1025e+00, -1.2111e+00]], [[ 6.6228e-01,  8.3826e-01,  1.0142e+00,  1.1902e+00,  1.3662e+00,1.5422e+00,  1.7181e+00,  1.8941e+00]], [[-5.4425e-01, -1.2149e+00, -1.8855e+00, -2.5561e+00, -3.2267e+00, -3.8973e+00, -4.5679e+00, -5.2385e+00]], [[ 2.0564e-01,  1.6357e-01,  1.2150e-01,  7.9434e-02,  3.7365e-02, -4.7036e-03, -4.6773e-02, -8.8842e-02]]]],
       grad_fn=<MkldnnConvolutionBackward>)

感谢您的阅读。欢迎提出建议和建设性的批评。:)你可以在 LinkedIn 和Twitter上找到我。

你也可以在这里查看我的其他博客。

Pytorch【基础】—RNN 简介

原文:https://towardsdatascience.com/pytorch-basics-how-to-train-your-neural-net-intro-to-rnn-cb6ebc594677?source=collection_archive---------0-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

这篇博文将带你了解 PyTorch 中不同类型的 RNN 操作。

这篇博文通过预测一个数字序列,带您了解 PyTorch 中普通 rnn、堆叠 rnn、双向 rnn 和堆叠双向 rnn 的实现。

RNN 迷因[图片[1]]

导入库

import numpy as np

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

rnn 的类型

rnn 主要用于时序数据,如时间序列或 NLP。有多种不同类型的 rnn 用于不同的应用。

不同类型的 RNNs 图片[2]]

对于时间序列-

  • 预测-多对多或多对一
  • 分类-多对一

对于 NLP -

  • 文本分类:多对一
  • 文本生成:多对多
  • 机器翻译:多对多
  • 命名实体识别:多对多
  • 图像字幕:一对多

堆叠 rnn

为了获得更好的性能,我们经常将 rnn 堆叠在一起。

堆叠 RNNs[图像[3]]

双向 RNN

双向 RNN 基本上使用 2 个 RNN,其中输入序列按正常顺序馈送到 1 个 RNN,反向馈送到另一个 RNN。

双向 RNNs [Image [4]]

输入数据

这里的数据是:

【1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20】

我们把它分成 4 批,序列长度= 5。

[[1,2,3,4,5],T15,[6,7,8,9,10],
,[11,12,13,14,15],T17,[16,17,18,19,20]]

批量大小 = 4
序列长度 = 5
输入大小 = 1(因为,只有一维)

在我们的例子中,我们查看 5 (seq_len)的前一个值来预测接下来的 2 个值。

data = torch.Tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])print("Data: ", data.shape, "\n\n", data)###################### OUTPUT ###################### Data:
 tensor([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20.]) Data Shape: 
 torch.Size([20])

香草 RNN

# Number of features used as input. (Number of columns)
INPUT_SIZE = 1# Number of previous time stamps taken into account.
SEQ_LENGTH = 5# Number of features in last hidden state ie. number of output time-
# steps to predict.See image below for more clarity.
HIDDEN_SIZE = 2# Number of stacked rnn layers.
NUM_LAYERS = 1# We have total of 20 rows in our input. 
# We divide the input into 4 batches where each batch has only 1
# row. Each row corresponds to a sequence of length 5\. 
BATCH_SIZE = 4

投入

torch.nn.RNN有两个输入- inputh_0 ie。t=0 时的输入序列和隐藏层。如果我们不初始化隐藏层,它将被 PyTorch 自动初始化为全零。

  • **input**是输入网络的序列。它的大小应该是(seq_len, batch, input_size)。如果batch_first=True,则输入大小为(batch, seq_len, input_size)
  • **h_0**是网络的初始隐藏状态。它的大小为(num_layers * num_directions, batch, input_size),其中num_layers是堆叠 rnn 的数量。num_directions =双向 rnn 为 2,否则为 1。

输出

torch.nn.RNN有两个输出- outhidden

  • **out**是来自最后一个 RNN 层的所有时间步长的 RNN 的输出。它的大小和(seq_len, batch, num_directions * hidden_size)差不多。如果batch_first=True,则输出大小为(batch, seq_len, num_directions * hidden_size)
  • **h_n**是所有 RNN 层的最后一个时间步长的隐藏值。它的尺寸是(num_layers * num_directions, batch, hidden_size)h_n不受batch_first=True影响。 Github 问题。

下图解释得更清楚。这里的batch=1。该图是一个 LSTM,它有两个隐藏参数(h, c)。RNN 和 GRU 都只有h

RNN 输入输出【图片[5] 信用点】

重申一下—

***out*** 是 RNN 的 输出 最后一个 RNN 层 ***h_n*** 是将 隐藏 的值从 最后一步*的所有 RNN 图层 *

*# Initialize the RNN.
rnn = nn.RNN(input_size=INPUT_SIZE, hidden_size=HIDDEN_SIZE, num_layers = 1, batch_first=True)# input size : (batch, seq_len, input_size)
inputs = data.view(BATCH_SIZE, SEQ_LENGTH, INPUT_SIZE)# out shape = (batch, seq_len, num_directions * hidden_size)
# h_n shape  = (num_layers * num_directions, batch, hidden_size)
out, h_n = rnn(inputs)*

**input**形状= [4, 5, 1]
**out**形状= [4, 5, 2]
**h_n**形状= [1, 4, 2]

**input**中,我们有 4 个批次作为输出,因为我们设置了BATCH_SIZE=4。每批包含 5 行,因为出了SEQ_LENGTH = 5。我们只使用一个特征作为输入INPUT_SIZE = 1

**out**中,我们从所有 4 个批次中获取值,其中时间步数(seq_len)为 5,预测数为 2。对于每一批,我们预测 2 个输出。

**h_n**中,我们从单个 RNN 层的最后一个时间步长的 4 个批次中的每一个批次中获取值。

*print('Input: ', inputs.shape, '\n', inputs)
print('\nOutput: ', out.shape, '\n', out)
print('\nHidden: ', h_n.shape, '\n', h_n) ###################### OUTPUT ######################Input:  torch.Size([4, 5, 1]) 
 tensor([[[ 1.],
         [ 2.],
         [ 3.],
         [ 4.],
         [ 5.]],

        [[ 6.],
         [ 7.],
         [ 8.],
         [ 9.],
         [10.]],

        [[11.],
         [12.],
         [13.],
         [14.],
         [15.]],

        [[16.],
         [17.],
         [18.],
         [19.],
         [20.]]])

Output:  torch.Size([4, 5, 2]) 
 tensor([[[-0.0819,  0.8100],
         [-0.4311,  0.9332],
         [-0.3162,  0.9748],
         [-0.3979,  0.9875],
         [-0.3675,  0.9944]],

        [[-0.1081,  0.9953],
         [-0.5145,  0.9986],
         [-0.3269,  0.9995],
         [-0.4254,  0.9997],
         [-0.3820,  0.9999]],

        [[-0.1342,  0.9999],
         [-0.5245,  1.0000],
         [-0.3458,  1.0000],
         [-0.4382,  1.0000],
         [-0.3982,  1.0000]],

        [[-0.1601,  1.0000],
         [-0.5328,  1.0000],
         [-0.3648,  1.0000],
         [-0.4506,  1.0000],
         [-0.4143,  1.0000]]], grad_fn=<TransposeBackward1>)

Hidden:  torch.Size([1, 4, 2]) 
 tensor([[[-0.3675,  0.9944],
         [-0.3820,  0.9999],
         [-0.3982,  1.0000],
         [-0.4143,  1.0000]]], grad_fn=<StackBackward>)*

在上面的输出中,注意每批out中的最后一行出现在h_n中。

  • out是每批最后一个 RNN 层所有时间步的输出值。
  • h_n是每批所有 RNN 层在最后一个时间步的隐藏值。

堆叠 RNN

如果我改变num_layers = 3,我们将有 3 个 RNN 层堆叠在一起。看看下面的例子中outh_n张量是如何变化的。

我们现在在h_n张量中有 3 个批次。最后一批包含out张量中每批的结束行。

*# Initialize the RNN.
rnn = nn.RNN(input_size=INPUT_SIZE, hidden_size=HIDDEN_SIZE, num_layers = 3, batch_first=True)# input size : (batch_size , seq_len, input_size)
inputs = data.view(BATCH_SIZE, SEQ_LENGTH, INPUT_SIZE)# out shape = (batch, seq_len, num_directions * hidden_size)
# h_n shape  = (num_layers * num_directions, batch, hidden_size)
out, h_n = rnn(inputs)*

**input**形状= [4, 5, 1]
**out**形状= [4, 5, 2]
**h_n**形状= [3, 4, 2]

**input**中,我们有 4 个批次作为输出,因为我们设置了BATCH_SIZE=4。每批包含 5 行,因为 out SEQ_LENGTH = 5。我们只使用一个特征作为输入INPUT_SIZE = 1

**out**中,我们从所有 4 个批次中获取值,其中时间步长数(seq_len)为 5,预测数为 2。对于每一批,我们预测 2 个输出。

**h_n**中,我们从 3 个堆叠的 RNN 层的最后时间步的 4 个批次中的每一个中获得值。

*print('Input: ', inputs.shape, '\n', inputs)
print('\nOutput: ', out.shape, '\n', out)
print('\nHidden: ', h_n.shape, '\n', h_n) ###################### OUTPUT ######################Input:  torch.Size([4, 5, 1]) 
 tensor([[[ 1.],
         [ 2.],
         [ 3.],
         [ 4.],
         [ 5.]],

        [[ 6.],
         [ 7.],
         [ 8.],
         [ 9.],
         [10.]],

        [[11.],
         [12.],
         [13.],
         [14.],
         [15.]],

        [[16.],
         [17.],
         [18.],
         [19.],
         [20.]]])

Output:  torch.Size([4, 5, 2]) 
 tensor([[[ 0.3144, -0.7527],
         [-0.0597, -0.6038],
         [ 0.0896, -0.7646],
         [ 0.0608, -0.6358],
         [ 0.1084, -0.6783]],

        [[ 0.4442, -0.6350],
         [ 0.0949, -0.3948],
         [ 0.2715, -0.5962],
         [ 0.1819, -0.4580],
         [ 0.2529, -0.5213]],

        [[ 0.4907, -0.5688],
         [ 0.1671, -0.2976],
         [ 0.3462, -0.4922],
         [ 0.2388, -0.3768],
         [ 0.3078, -0.4418]],

        [[ 0.5041, -0.5466],
         [ 0.1883, -0.2675],
         [ 0.3684, -0.4576],
         [ 0.2572, -0.3502],
         [ 0.3238, -0.4167]]], grad_fn=<TransposeBackward1>)

Hidden:  torch.Size([3, 4, 2]) 
 tensor([[[-0.6480, -0.4044],
         [-0.8912, -0.7801],
         [-0.9808, -0.9366],
         [-0.9975, -0.9836]],

        [[-0.7848, -0.0118],
         [-0.8707, -0.1721],
         [-0.8955, -0.2411],
         [-0.9016, -0.2605]],

        [[ 0.1084, -0.6783],
         [ 0.2529, -0.5213],
         [ 0.3078, -0.4418],
         [ 0.3238, -0.4167]]], grad_fn=<StackBackward>)*

双向 RNN

对于双向 RNN,我们设置了bidirectional=True

*rnn = nn.RNN(input_size=INPUT_SIZE, hidden_size=HIDDEN_SIZE, batch_first=True, num_layers = 1, bidirectional = True)# input size : (batch_size , seq_len, input_size)
inputs = data.view(BATCH_SIZE, SEQ_LENGTH, INPUT_SIZE)# out shape = (batch, seq_len, num_directions * hidden_size)
# h_n shape  = (num_layers * num_directions, batch, hidden_size)
out, h_n = rnn(inputs)*

**input**形状= [4, 5, 1]
**out**形状= [4, 5, 4]
**h_n**形状= [2, 4, 2]

**input**中,我们有 4 个批次作为输出,因为我们设置了BATCH_SIZE=4。每批包含 5 行,因为 out SEQ_LENGTH = 5。我们只使用一个特征作为输入INPUT_SIZE = 1

**out**中,我们从所有 4 个批次中获取值,其中时间步长数(seq_len)为 5,预测数为 2。对于每一批,我们预测 2 个输出。因为这是一个双向 RNN,我们得到了 2 组预测。因此,形状是[4, 5, 4]而不是[4, 5, 2](我们在上述的单向 RNN 的情况下观察到的)。**

**h_n**中,我们从单个 RNN 层的最后时间步的 4 个批次中的每一个批次中获取值。因为这是一个双向 RNN,我们得到了 2 组预测。因此,形状是[2, 4, 2]而不是[1, 4, 2](我们在以上单向 RNN 的情况下观察到的)。**

*print('Input: ', inputs.shape, '\n', inputs)
print('\nOutput: ', out.shape, '\n', out)
print('\nHidden: ', h_n.shape, '\n', h_n) ###################### OUTPUT ######################Input:  torch.Size([4, 5, 1]) 
 tensor([[[ 1.],
         [ 2.],
         [ 3.],
         [ 4.],
         [ 5.]],

        [[ 6.],
         [ 7.],
         [ 8.],
         [ 9.],
         [10.]],

        [[11.],
         [12.],
         [13.],
         [14.],
         [15.]],

        [[16.],
         [17.],
         [18.],
         [19.],
         [20.]]])

Output:  torch.Size([4, 5, 4]) 
 tensor([[[ 0.2184,  0.4086,  0.6418, -0.1677],
         [-0.0222, -0.0095,  0.8794, -0.4927],
         [-0.6716, -0.2802,  0.9585, -0.7248],
         [-0.9387, -0.4152,  0.9846, -0.8646],
         [-0.9841, -0.6164,  0.9789, -0.9192]],

        [[-0.9813, -0.8829,  0.9979, -0.9721],
         [-0.9986, -0.8902,  0.9992, -0.9877],
         [-0.9995, -0.9449,  0.9997, -0.9946],
         [-0.9998, -0.9729,  0.9999, -0.9977],
         [-0.9999, -0.9868,  0.9998, -0.9987]],

        [[-0.9999, -0.9968,  1.0000, -0.9996],
         [-1.0000, -0.9969,  1.0000, -0.9998],
         [-1.0000, -0.9985,  1.0000, -0.9999],
         [-1.0000, -0.9993,  1.0000, -1.0000],
         [-1.0000, -0.9997,  1.0000, -1.0000]],

        [[-1.0000, -0.9999,  1.0000, -1.0000],
         [-1.0000, -0.9999,  1.0000, -1.0000],
         [-1.0000, -1.0000,  1.0000, -1.0000],
         [-1.0000, -1.0000,  1.0000, -1.0000],
         [-1.0000, -1.0000,  1.0000, -1.0000]]], grad_fn=<TransposeBackward1>)

Hidden:  torch.Size([2, 4, 2]) 
 tensor([[[-0.9841, -0.6164],
         [-0.9999, -0.9868],
         [-1.0000, -0.9997],
         [-1.0000, -1.0000]],

        [[ 0.6418, -0.1677],
         [ 0.9979, -0.9721],
         [ 1.0000, -0.9996],
         [ 1.0000, -1.0000]]], grad_fn=<StackBackward>)*

现在让我们试着更详细地理解输出。根据文档,为了分离方向(向前和向后),我们可以做以下事情-

  • out.view(seq_len, batch, num_directions, hidden_size)向前和向后分别为方向 0 和 1。请记住,如果您使用了batch_first=True,那么它将是out.view(batch, seq_len, num_directions, hidden_size)
  • h_n.view(num_layers, num_directions, batch, hidden_size)向前和向后分别为方向 0 和 1。

BiRNN 分离**out**

让我们重塑 BiRNN 的输出,使用out.view(batch, seq_len, num_directions, hidden_size)来分离前向和后向值。

*out_reshaped = out.view(BATCH_SIZE, SEQ_LENGTH, 2, HIDDEN_SIZE)print("Shape of the output after directions are separated: ", out_reshaped.shape) ###################### OUTPUT ######################Shape of the output after directions are separated:  torch.Size([4, 5, 2, 2])*

现在的形状是— (batch, seq_len, num_directions, hidden_size)

num_directions是二次元。为了获得前向和后向输出,我们可以做以下事情

  • out_forward = (batch, seq_len, 0, hidden_size)
  • out_backward = (batch, seq_len, 1, hidden_size)
*out_forward = out_reshaped[:, :, 0, :]
out_backward = out_reshaped[:, :, 1, :]print("Forward output: ", out_forward.shape, "\n", out_forward)
print("\n\nBackward output: ", out_backward.shape, "\n", out_backward) ###################### OUTPUT ######################Forward output:  torch.Size([4, 5, 2]) 
 tensor([[[ 0.2184,  0.4086],
         [-0.0222, -0.0095],
         [-0.6716, -0.2802],
         [-0.9387, -0.4152],
         [-0.9841, -0.6164]],

        [[-0.9813, -0.8829],
         [-0.9986, -0.8902],
         [-0.9995, -0.9449],
         [-0.9998, -0.9729],
         [-0.9999, -0.9868]],

        [[-0.9999, -0.9968],
         [-1.0000, -0.9969],
         [-1.0000, -0.9985],
         [-1.0000, -0.9993],
         [-1.0000, -0.9997]],

        [[-1.0000, -0.9999],
         [-1.0000, -0.9999],
         [-1.0000, -1.0000],
         [-1.0000, -1.0000],
         [-1.0000, -1.0000]]], grad_fn=<SliceBackward>)

Backward output:  torch.Size([4, 5, 2]) 
 tensor([[[ 0.6418, -0.1677],
         [ 0.8794, -0.4927],
         [ 0.9585, -0.7248],
         [ 0.9846, -0.8646],
         [ 0.9789, -0.9192]],

        [[ 0.9979, -0.9721],
         [ 0.9992, -0.9877],
         [ 0.9997, -0.9946],
         [ 0.9999, -0.9977],
         [ 0.9998, -0.9987]],

        [[ 1.0000, -0.9996],
         [ 1.0000, -0.9998],
         [ 1.0000, -0.9999],
         [ 1.0000, -1.0000],
         [ 1.0000, -1.0000]],

        [[ 1.0000, -1.0000],
         [ 1.0000, -1.0000],
         [ 1.0000, -1.0000],
         [ 1.0000, -1.0000],
         [ 1.0000, -1.0000]]], grad_fn=<SliceBackward>)*

BiRNN 分离的h_n

让我们用h_n.view(num_layers, num_directions, batch, hidden_size)重塑隐藏的 BiRNN 来分离向前和向后的值。

*h_n_reshaped = h_n.view(1, 2, BATCH_SIZE, HIDDEN_SIZE)print("Shape of the hidden after directions are separated: ", h_n_reshaped.shape) ###################### OUTPUT ######################Shape of the hidden after directions are separated:  torch.Size([1, 2, 4, 2])*

现在的形状是— (num_layers, num_directions, batch, hidden_size)

num_directions是第一维度。要获得向前和向后隐藏,我们可以做以下事情-

  • hidden_forward = (num_layers, 0, batch, hidden_size)
  • hidden_backward = (num_layers, 1, batch, hidden_size)
*h_n_forward = h_n_reshaped[:, 0, :, :]
h_n_backward = h_n_reshaped[:, 1, :, :]print("Forward h_n: ", h_n_forward.shape, "\n", h_n_forward)
print("\n\nBackward h_n: ", h_n_backward.shape, "\n", h_n_backward) ###################### OUTPUT ######################Forward h_n:  torch.Size([1, 4, 2]) 
 tensor([[[-0.9841, -0.6164],
         [-0.9999, -0.9868],
         [-1.0000, -0.9997],
         [-1.0000, -1.0000]]], grad_fn=<SliceBackward>)

Backward h_n:  torch.Size([1, 4, 2]) 
 tensor([[[ 0.6418, -0.1677],
         [ 0.9979, -0.9721],
         [ 1.0000, -0.9996],
         [ 1.0000, -1.0000]]], grad_fn=<SliceBackward>)*

堆叠双向 RNN

对于堆叠式双向 RNN,我们设置bidirectional=Truenum_layers = 3。

*rnn = nn.RNN(input_size=INPUT_SIZE, hidden_size=HIDDEN_SIZE, batch_first=True, num_layers = 3, bidirectional = True)# input size : (batch_size , seq_len, input_size)
inputs = data.view(BATCH_SIZE, SEQ_LENGTH, INPUT_SIZE)# out shape = (batch, seq_len, num_directions * hidden_size)
# h_n shape  = (num_layers * num_directions, batch, hidden_size)
out, h_n = rnn(inputs)*

**input**形状= [4, 5, 1]
**out**形状= [4, 5, 4]
**h_n**形状= [6, 4, 2]

**input**中,我们有 4 个批次作为输出,因为我们设置了BATCH_SIZE=4。每批包含 5 行,因为 out SEQ_LENGTH = 5。我们仅使用单一特征作为输入INPUT_SIZE = 1

**out**中,我们从所有 4 个批次中获取值,其中时间步长数(seq_len)为 5,预测数为 2。对于每一批,我们预测 2 个输出。因为这是一个双向 RNN,我们得到了 2 组预测。因此,形状是[4, 5, 4]而不是[4, 5, 2](我们在上方的堆叠单向 RNN 的情况下观察到的)。**

**h_n**中,我们从单个 RNN 层的最后时间步的 4 个批次中的每一个批次中获取值。因为这是一个双向 RNN,我们得到了 2 组预测。因此,形状是[6, 4, 2]而不是[3, 4, 2](我们在上方的堆叠单向 RNN 的情况下观察到的)。**

*print('Input: ', inputs.shape, '\n', inputs)
print('\nOutput: ', out.shape, '\n', out)
print('\nHidden: ', h_n.shape, '\n', h_n) ###################### OUTPUT ######################Input:  torch.Size([4, 5, 1]) 
 tensor([[[ 1.],
         [ 2.],
         [ 3.],
         [ 4.],
         [ 5.]],

        [[ 6.],
         [ 7.],
         [ 8.],
         [ 9.],
         [10.]],

        [[11.],
         [12.],
         [13.],
         [14.],
         [15.]],

        [[16.],
         [17.],
         [18.],
         [19.],
         [20.]]])

Output:  torch.Size([4, 5, 4]) 
 tensor([[[-0.4175, -0.6278, -0.0101, -0.4025],
         [ 0.1271, -0.5579,  0.2162, -0.4832],
         [-0.2557, -0.6714,  0.3084, -0.4927],
         [ 0.0556, -0.6295,  0.3194, -0.4467],
         [-0.1510, -0.6863,  0.3917, -0.6299]],

        [[-0.4311, -0.6939, -0.2381, -0.6894],
         [ 0.1423, -0.5335, -0.0872, -0.6471],
         [-0.2943, -0.6468,  0.0076, -0.6274],
         [ 0.0392, -0.5691,  0.0595, -0.5576],
         [-0.2070, -0.6238,  0.2187, -0.6570]],

        [[-0.4458, -0.6581, -0.6259, -0.8299],
         [ 0.0999, -0.4501, -0.5715, -0.8090],
         [-0.3441, -0.5669, -0.4723, -0.7729],
         [-0.0133, -0.4705, -0.3131, -0.6745],
         [-0.2617, -0.5444,  0.0042, -0.6820]],

        [[-0.4556, -0.6330, -0.7035, -0.8531],
         [ 0.0780, -0.4118, -0.6690, -0.8358],
         [-0.3608, -0.5393, -0.5730, -0.7989],
         [-0.0285, -0.4442, -0.3958, -0.6973],
         [-0.2739, -0.5259, -0.0447, -0.6868]]], grad_fn=<TransposeBackward1>)

Hidden:  torch.Size([6, 4, 2]) 
 tensor([[[ 0.9455,  0.5653],
         [ 0.9986, -0.1385],
         [ 1.0000, -0.7900],
         [ 1.0000, -0.9272]],

        [[ 0.1570,  0.2765],
         [ 0.9959,  0.9972],
         [ 1.0000,  1.0000],
         [ 1.0000,  1.0000]],

        [[-0.6463,  0.5301],
         [-0.5393,  0.6556],
         [-0.4089,  0.7277],
         [-0.3732,  0.7372]],

        [[ 0.0474, -0.5973],
         [ 0.0082, -0.9715],
         [-0.1373, -0.9681],
         [-0.2362, -0.9658]],

        [[-0.1510, -0.6863],
         [-0.2070, -0.6238],
         [-0.2617, -0.5444],
         [-0.2739, -0.5259]],

        [[-0.0101, -0.4025],
         [-0.2381, -0.6894],
         [-0.6259, -0.8299],
         [-0.7035, -0.8531]]], grad_fn=<StackBackward>)*

现在让我们试着更详细地理解输出。根据文档,为了分离方向(向前和向后),我们可以做以下事情-

  • out.view(seq_len, batch, num_directions, hidden_size)向前和向后分别为方向 0 和 1。请记住,如果您使用了batch_first=True,那么它将是out.view(batch, seq_len, num_directions, hidden_size)
  • h_n.view(num_layers, num_directions, batch, hidden_size)向前和向后分别为方向 0 和 1。

堆叠 BiRNN 分离out

让我们使用out.view(batch, seq_len, num_directions, hidden_size)对堆叠的 BiRNN 输出进行整形,以分离出正向和反向值。

*out_reshaped = out.view(BATCH_SIZE, SEQ_LENGTH, 2, HIDDEN_SIZE)print("Shape of the output after directions are separated: ", out_reshaped.shape) ###################### OUTPUT ######################Shape of the output after directions are separated:  torch.Size([4, 5, 2, 2])*

现在的形状是— (batch, seq_len, num_directions, hidden_size)

num_directions是二次元。为了获得前向和后向输出,我们可以做以下事情

  • out_forward = (batch, seq_len, 0, hidden_size)
  • out_backward = (batch, seq_len, 1, hidden_size)
*out_forward = out_reshaped[:, :, 0, :]
out_backward = out_reshaped[:, :, 1, :]print("Forward output: ", out_forward.shape, "\n", out_forward)
print("\n\nBackward output: ", out_backward.shape, "\n", out_backward) ###################### OUTPUT ######################Forward output:  torch.Size([4, 5, 2]) 
 tensor([[[-0.4175, -0.6278],
         [ 0.1271, -0.5579],
         [-0.2557, -0.6714],
         [ 0.0556, -0.6295],
         [-0.1510, -0.6863]],

        [[-0.4311, -0.6939],
         [ 0.1423, -0.5335],
         [-0.2943, -0.6468],
         [ 0.0392, -0.5691],
         [-0.2070, -0.6238]],

        [[-0.4458, -0.6581],
         [ 0.0999, -0.4501],
         [-0.3441, -0.5669],
         [-0.0133, -0.4705],
         [-0.2617, -0.5444]],

        [[-0.4556, -0.6330],
         [ 0.0780, -0.4118],
         [-0.3608, -0.5393],
         [-0.0285, -0.4442],
         [-0.2739, -0.5259]]], grad_fn=<SliceBackward>)

Backward output:  torch.Size([4, 5, 2]) 
 tensor([[[-0.0101, -0.4025],
         [ 0.2162, -0.4832],
         [ 0.3084, -0.4927],
         [ 0.3194, -0.4467],
         [ 0.3917, -0.6299]],

        [[-0.2381, -0.6894],
         [-0.0872, -0.6471],
         [ 0.0076, -0.6274],
         [ 0.0595, -0.5576],
         [ 0.2187, -0.6570]],

        [[-0.6259, -0.8299],
         [-0.5715, -0.8090],
         [-0.4723, -0.7729],
         [-0.3131, -0.6745],
         [ 0.0042, -0.6820]],

        [[-0.7035, -0.8531],
         [-0.6690, -0.8358],
         [-0.5730, -0.7989],
         [-0.3958, -0.6973],
         [-0.0447, -0.6868]]], grad_fn=<SliceBackward>)*

堆叠 BiRNN 分离h_n

让我们使用h_n.view(num_layers, num_directions, batch, hidden_size)将堆叠的 BiRNN 隐藏起来,以分离出向前和向后的值。

*h_n_reshaped = h_n.view(3, 2, BATCH_SIZE, HIDDEN_SIZE)print("Shape of the hidden after directions are separated: ", h_n_reshaped.shape)###################### OUTPUT ######################Shape of the hidden after directions are separated:  torch.Size([3, 2, 4, 2])*

现在的形状是— (num_layers, num_directions, batch, hidden_size)

num_directions是第一维度。要获得向前和向后隐藏,我们可以做以下事情-

  • hidden_forward = (num_layers, 0, batch, hidden_size)
  • hidden_backward = (num_layers, 1, batch, hidden_size)
*h_n_forward = h_n_reshaped[:, 0, :, :]
h_n_backward = h_n_reshaped[:, 1, :, :]print("Forward h_n: ", h_n_forward.shape, "\n", h_n_forward)
print("\n\nBackward h_n: ", h_n_backward.shape, "\n", h_n_backward) ###################### OUTPUT ######################Forward h_n:  torch.Size([3, 4, 2]) 
 tensor([[[ 0.9455,  0.5653],
         [ 0.9986, -0.1385],
         [ 1.0000, -0.7900],
         [ 1.0000, -0.9272]],

        [[-0.6463,  0.5301],
         [-0.5393,  0.6556],
         [-0.4089,  0.7277],
         [-0.3732,  0.7372]],

        [[-0.1510, -0.6863],
         [-0.2070, -0.6238],
         [-0.2617, -0.5444],
         [-0.2739, -0.5259]]], grad_fn=<SliceBackward>)

Backward h_n:  torch.Size([3, 4, 2]) 
 tensor([[[ 0.1570,  0.2765],
         [ 0.9959,  0.9972],
         [ 1.0000,  1.0000],
         [ 1.0000,  1.0000]],

        [[ 0.0474, -0.5973],
         [ 0.0082, -0.9715],
         [-0.1373, -0.9681],
         [-0.2362, -0.9658]],

        [[-0.0101, -0.4025],
         [-0.2381, -0.6894],
         [-0.6259, -0.8299],
         [-0.7035, -0.8531]]], grad_fn=<SliceBackward>)*

感谢您的阅读。欢迎提出建议和建设性的批评。😃你可以在 LinkedIn 和 Twitter 上找到我。如果你喜欢这个,看看我的其他博客。**

py torch[基础知识] —数据加载器和损失函数简介

原文:https://towardsdatascience.com/pytorch-basics-intro-to-dataloaders-and-loss-functions-868e86450047?source=collection_archive---------14-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

这篇博文将带您了解 PyTorch 中的数据加载器和不同类型的损失函数。

在这篇博文中,我们将看到自定义数据集和数据加载器的简短实现,以及一些常见的损失函数。

数据集和数据加载器

数据加载器 meme [Image [1]]

使用 3 个主要组件创建一个定制的数据集类。

  • __init__
  • __len__
  • __getitem__
class CustomDataset(Dataset):
    def __init__(self):
        pass def __getitem__(self, index):
        pass def __len__(self):
        pass

__init__:用于执行读取数据、预处理等初始化操作。
__len__:返回输入数据的大小。
__getitem__:批量返回数据(输入输出)。

然后在这个数据集类上使用一个数据加载器来批量读取数据。

train_loader = DataLoader(custom_dataset_object, batch_size=32, shuffle=True)

让我们实现一个基本的 PyTorch 数据集和数据加载器。假设你有输入和输出数据-

X : 1,2,3,4,5,6,7,8,9,10

y : 0,0,0,1,0,1,1,0,0,1

让我们定义数据集类。我们将返回一个元组(输入,输出)。

class CustomDataset(Dataset):

    def __init__(self, X_data, y_data):
        self.X_data = X_data
        self.y_data = y_data

    def __getitem__(self, index):
        return self.X_data[index], self.y_data[index]

    def __len__ (self):
        return len(self.X_data)

初始化数据集对象。输入必须是张量类型。

data = CustomDataset(torch.FloatTensor(X), torch.FloatTensor(y))

让我们使用方法__len__()__getitem__()__getitem__()将索引作为输入。

data.__len__()################### OUTPUT ##################### 10

从输出数据中打印出第 4 个元素(第 3 个索引)。

data.__getitem__(3)################### OUTPUT ##################### (tensor(4.), tensor(1.))

让我们现在初始化我们的数据加载器。在这里,我们指定批量大小和洗牌。

data_loader = DataLoader(dataset=data, batch_size=2, shuffle=True)data_loader_iter = iter(data_loader)
print(next(data_loader_iter))################### OUTPUT ##################### [tensor([3., 6.]), tensor([0., 1.])]

让我们使用带有 for 循环的数据加载器。

for i,j in data_loader:
    print(i,j)################### OUTPUT #####################tensor([ 1., 10.]) tensor([0., 1.])
tensor([4., 6.]) tensor([1., 1.])
tensor([7., 5.]) tensor([1., 0.])
tensor([9., 3.]) tensor([0., 0.])
tensor([2., 8.]) tensor([0., 0.])

损失函数

损失函数 meme [Image [2]]

以下是不同深度学习任务常用的损失函数。

回归:

  • 平均绝对误差— torch.nn.L1Loss()
  • 均方差— torch.nn.MSELoss()

分类:

  • 二元交叉熵损失— torch.nn.BCELoss()
  • 具有罗吉斯损失的二元交叉熵— torch.nn.BCEWithLogitsLoss()
  • 负对数可能性— torch.nn.NLLLoss()
  • 交叉斜视— torch.nn.CrossEntropyLoss()

从官方 PyTorch 文档了解更多损失函数。

导入库

import torch
import torch.nn as nn

回归

为了计算损失,让我们从定义实际和预测输出张量开始。

y_pred = torch.tensor([[1.2, 2.3, 3.4], [4.5, 5.6, 6.7]], requires_grad=True)print("Y Pred: \n", y_pred)
print("\nY Pred shape: ", y_pred.shape, "\n")print("=" * 50)y_train = torch.tensor([[1.2, 2.3, 3.4], [7.8, 8.9, 9.1]])
print("\nY Train: \n", y_train)
print("\nY Train shape: ", y_train.shape) ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [4.5000, 5.6000, 6.7000]], requires_grad=True)Y Pred shape:  torch.Size([2, 3]) ==================================================Y Train: 
 tensor([[1.2000, 2.3000, 3.4000],
        [7.8000, 8.9000, 9.1000]])Y Train shape:  torch.Size([2, 3])

平均绝对误差— torch.nn.L1Loss()

输入和输出必须与尺寸相同,并且具有数据类型浮动

y_pred = (batch_size, *)y_train = (batch_size, *)

mae_loss = nn.L1Loss()print("Y Pred: \n", y_pred)print("Y Train: \n", y_train)output = mae_loss(y_pred, y_train)
print("MAE Loss\n", output)output.backward() ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [4.5000, 5.6000, 6.7000]], requires_grad=True)
Y Train: 
 tensor([[1.2000, 2.3000, 3.4000],
        [7.8000, 8.9000, 9.1000]])
MAE Loss
 tensor(1.5000, grad_fn=<L1LossBackward>)

均方差— torch.nn.MSELoss()

输入和输出必须与尺寸相同,并且具有数据类型浮动

y_pred = (batch_size, *)y_train = (batch_size, *)

mse_loss = nn.MSELoss()print("Y Pred: \n", y_pred)print("Y Train: \n", y_train)output = mse_loss(y_pred, y_train)
print("MSE Loss\n", output)output.backward() ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [4.5000, 5.6000, 6.7000]], requires_grad=True)
Y Train: 
 tensor([[1.2000, 2.3000, 3.4000],
        [7.8000, 8.9000, 9.1000]])
MSE Loss
 tensor(4.5900, grad_fn=<MseLossBackward>)

二元分类

y_train有两个类——0 和 1。当网络的最终输出是介于 0 和 1 之间的单个值(最终密集层的大小为 1)时,我们使用 BCE 损失函数。

如果网络的输出是长度为 2 的张量(最终密集层的大小为 2 ),其中两个值都位于 0 和 1 之间,则二进制分类可以重新构造为使用 NLLLoss交叉熵损失。

让我们定义实际和预测的输出张量,以便计算损失。

y_pred = torch.tensor([[1.2, 2.3, 3.4], [7.8, 8.9, 9.1]], requires_grad = True)
print("Y Pred: \n", y_pred)
print("\nY Pred shape: ", y_pred.shape, "\n")print("=" * 50)y_train = torch.tensor([[1, 0, 1], [0, 0, 1]])
print("\nY Train: \n", y_train)
print("\nY Train shape: ", y_train.shape) ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [7.8000, 8.9000, 9.1000]], requires_grad=True)Y Pred shape:  torch.Size([2, 3]) ==================================================Y Train: 
 tensor([[1, 0, 1],
        [0, 0, 1]])Y Train shape:  torch.Size([2, 3])

二元交叉熵损失— torch.nn.BCELoss()

输入和输出必须与尺寸相同并具有浮动的数据类型。

y_pred = (batch_size, *),浮点型(值应通过一个 Sigmoid 函数传递,其值介于 0 和 1 之间)

y_train = (batch_size, *),浮动

bce_loss = nn.BCELoss()y_pred_sigmoid = torch.sigmoid(y_pred)print("Y Pred: \n", y_pred)print("\nY Pred Sigmoid: \n", y_pred_sigmoid)print("\nY Train: \n", y_train.float())output = bce_loss(y_pred_sigmoid, y_train.float())
print("\nBCE Loss\n", output)output.backward() ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [7.8000, 8.9000, 9.1000]], requires_grad=True)Y Pred Sigmoid: 
 tensor([[0.7685, 0.9089, 0.9677],
        [0.9996, 0.9999, 0.9999]], grad_fn=<SigmoidBackward>)Y Train: 
 tensor([[1., 0., 1.],
        [0., 0., 1.]])BCE Loss
 tensor(3.2321, grad_fn=<BinaryCrossEntropyBackward>)

具有罗吉斯损失的二元交叉熵— torch.nn.BCEWithLogitsLoss()

输入和输出必须是相同尺寸的和浮动的。该类将s 形b 形组合成一个类。这个版本在数值上比单独使用 Sigmoid 和 BCELoss 更稳定。

y_pred = (batch_size, *),浮动

y_train = (batch_size, *),浮动

bce_logits_loss = nn.BCEWithLogitsLoss()print("Y Pred: \n", y_pred)print("\nY Train: \n", y_train.float())output = bce_logits_loss(y_pred, y_train.float())
print("\nBCE Loss\n", output)output.backward() ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [7.8000, 8.9000, 9.1000]], requires_grad=True)Y Train: 
 tensor([[1., 0., 1.],
        [0., 0., 1.]])BCE Loss
 tensor(3.2321, grad_fn=<BinaryCrossEntropyWithLogitsBackward>)

多类分类

让我们定义实际和预测的输出张量,以便计算损失。

y_train有 4 个等级——0、1、2 和 3。

y_pred = torch.tensor([[1.2, 2.3, 3.4], [4.5, 5.6, 6.7], [7.8, 8.9, 9.1]], requires_grad = True)
print("Y Pred: \n", y_pred)
print("\nY Pred shape: ", y_pred.shape, "\n")print("=" * 50)y_train = torch.tensor([0, 1, 2])
print("\nY Train: \n", y_train)
print("\nY Train shape: ", y_train.shape) ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [4.5000, 5.6000, 6.7000],
        [7.8000, 8.9000, 9.1000]], requires_grad=True)Y Pred shape:  torch.Size([3, 3]) ==================================================Y Train: 
 tensor([0, 1, 2])Y Train shape:  torch.Size([3])

负对数可能性— torch.nn.NLLLoss()

y_pred = (batch_size, num_classes),Float(值应传递使用 log_softmax 函数获得的对数概率。

y_train = (batch_size),长整型(取值范围= 0,num_classes-1)。类别必须从 0、1、2 开始,...

nll_loss = nn.NLLLoss()y_pred_logsoftmax = torch.log_softmax(y_pred, dim = 1)print("Y Pred: \n", y_pred)print("\nY Pred LogSoftmax: \n", y_pred_logsoftmax)print("\nY Train: \n", y_train)output = nll_loss(y_pred_logsoftmax, y_train)
print("\nNLL Loss\n", output)output.backward() ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [4.5000, 5.6000, 6.7000],
        [7.8000, 8.9000, 9.1000]], requires_grad=True)Y Pred LogSoftmax: 
 tensor([[-2.5672, -1.4672, -0.3672],
        [-2.5672, -1.4672, -0.3672],
        [-2.0378, -0.9378, -0.7378]], grad_fn=<LogSoftmaxBackward>)Y Train: 
 tensor([0, 1, 2])NLL Loss
 tensor(1.5907, grad_fn=<NllLossBackward>)

交叉斜视— torch.nn.CrossEntropyLoss()

这个类将 LogSoftmaxNLLLoss 组合成一个类。

y_pred = (batch_size, num_classes)、Float
y_train = (batch_size)、Long(取值范围= 0,num_classes-1)。类别必须从 0、1、2 开始,...

ce_loss = nn.CrossEntropyLoss()print("Y Pred: \n", y_pred)print("\nY Train: \n", y_train)output = ce_loss(y_pred, y_train)
print("\nNLL Loss\n", output)output.backward() ###################### OUTPUT ######################Y Pred: 
 tensor([[1.2000, 2.3000, 3.4000],
        [4.5000, 5.6000, 6.7000],
        [7.8000, 8.9000, 9.1000]], requires_grad=True)Y Train: 
 tensor([0, 1, 2])NLL Loss
 tensor(1.5907, grad_fn=<NllLossBackward>)

感谢您的阅读。欢迎提出建议和建设性的批评。:)你可以在 LinkedIn 和 Twitter 找到我。如果你喜欢这个,看看我的其他博客。

py torch[基础知识] —取样取样器

原文:https://towardsdatascience.com/pytorch-basics-sampling-samplers-2a0f29f0bf2a?source=collection_archive---------4-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

本笔记本将带您了解使用 PyTorch 对自然图像数据执行random_splitSubsetRandomSamplerWeightedRandomSampler的过程。

导入库

import numpy as np
import pandas as pd
import seaborn as sns
from tqdm.notebook import tqdm
import matplotlib.pyplot as plt import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import transforms, utils, datasets
from torch.utils.data import Dataset, DataLoader, random_split, SubsetRandomSampler, WeightedRandomSampler

设置随机种子。

np.random.seed(0)
torch.manual_seed(0)

设置Seaborn样式。

%matplotlib inline
sns.set_style('darkgrid')

定义数据路径

设置数据集的根目录。

root_dir = "../../data/computer_vision/image_classification/natural-images/"
print("The data lies here =>", root_dir) ###################### OUTPUT ######################We're using => cpu
The data lies here => ../../data/computer_vision/image_classification/natural-images/

定义转换

将图像裁剪为(224, 224)大小,并将它们转换为张量。

image_transforms = {
    "train": transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ])
}

初始化数据集

使用ImageFolder,我们将创建我们的数据集。对于这篇博文,我们将只使用 train 文件夹。

natural_img_dataset = datasets.ImageFolder(
                              root = root_dir,
                              transform = image_transforms["train"]
                       )natural_img_dataset

类别<=> ID 映射

.class_to_idx方法返回数据集中的类映射标签。

natural_img_dataset.class_to_idx###################### OUTPUT ######################{'airplane': 0,
 'car': 1,
 'cat': 2,
 'dog': 3,
 'flower': 4,
 'fruit': 5,
 'motorbike': 6,
 'person': 7}

我们将创建一个名为idx2class的字典,它是 PyTorch 中class_to_idx方法的逆方法。

idx2class = {v: k for k, v in natural_img_dataset.class_to_idx.items()}idx2class ###################### OUTPUT ######################{0: 'airplane',
 1: 'car',
 2: 'cat',
 3: 'dog',
 4: 'flower',
 5: 'fruit',
 6: 'motorbike',
 7: 'person'}

观察班级分布

为了观察 dataset 对象中不同类的分布,我们创建了一个名为get_class_distribution()的函数。该函数将数据集作为输入参数,并返回一个字典,其中包含 dataset 对象中所有类的计数。

  1. 为此,我们首先初始化我们的count_dict,其中所有的类计数都是 0。
  2. 然后我们迭代数据集对象来提取类标签。dataset 对象包含元组(x,y)形式的元素。因此,我们需要从元组中提取位置 1 的项。
  3. 然后我们使用idx2class从类 id 中获取类名。
  4. 最后,我们为相关的 class-key 将count_dict中的计数更新 1。
def get_class_distribution(dataset_obj):
    count_dict = {k:0 for k,v in dataset_obj.class_to_idx.items()}

    for element in dataset_obj:
        y_lbl = element[1]
        y_lbl = idx2class[y_lbl]
        count_dict[y_lbl] += 1

    return count_dictprint("Distribution of classes: \n", get_class_distribution(natural_img_dataset)) ###################### OUTPUT ######################Distribution of classes: 
 {'airplane': 727, 'car': 968, 'cat': 885, 'dog': 702, 'flower': 843, 'fruit': 1000, 'motorbike': 788, 'person': 986}

为了绘制我们的字典,我们使用 Seaborn 库。我们首先将字典转换成数据帧,然后将其融化。最后,我们使用函数sns.barplot()来构建我们的情节。

plt.figure(figsize=(15,8))sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(natural_img_dataset)]).melt(), x = "variable", y="value", hue="variable").set_title('Natural Images Class Distribution')

类别分布[图片[1]]

从上图中,我们观察到这些类是不平衡的。

随机 _ 分割

random_split(dataset, lengths)直接作用于数据集。该函数需要两个输入参数。第一个参数是数据集。第二个是一组长度。如果我们想把数据集分成两部分,我们将提供一个包含两个数字的元组。这些数字是分割后相应数据集的大小。

我们的数据集有 6899 张图片。如果我们想把它分成大小为(6000,899)的两部分( train/test,train/val ),我们就把随机分割称为random_split(6000, 899)

让我们将数据集分成训练集和赋值集。

train_dataset, val_dataset = random_split(natural_img_dataset, (6000, 899))

将数据传递给数据加载器。

train_loader = DataLoader(dataset=train_dataset, shuffle=True, batch_size=1)val_loader = DataLoader(dataset=val_dataset, shuffle=False, batch_size=1)print("Length of the train_loader:", len(train_loader))
print("Length of the val_loader:", len(val_loader)) ###################### OUTPUT ######################Length of the train_loader: 6000
Length of the val_loader: 899

注意,我们使用了一个batch_size = 1。如果我们增加batch_size,图像的数量会相同,但是火车/val 装载器的长度会改变。

让我们看看 train 和 val 加载器中的类分布。

def get_class_distribution_loaders(dataloader_obj, dataset_obj):
    count_dict = {k:0 for k,v in dataset_obj.class_to_idx.items()}

    for _,j in dataloader_obj:
        y_idx = j.item()
        y_lbl = idx2class[y_idx]
        count_dict[str(y_lbl)] += 1

    return count_dict

让我们构建情节。

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(18,7))sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution_loaders(train_loader, natural_img_dataset)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[0]).set_title('Train Set')sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution_loaders(val_loader, natural_img_dataset)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[1]).set_title('Val Set')

random_split 后的类分布[Image [2]]

水下取样器

SubsetRandomSampler(indices)将数据的索引作为输入。

我们首先创建我们的采样器,然后将它传递给我们的数据加载器。

  1. 创建索引列表。
  2. 打乱索引。
  3. 根据列车价值百分比拆分指数。
  4. 创造SubsetRandomSampler

创建从 0 到数据集长度的索引列表。

dataset_size = len(natural_img_dataset)
dataset_indices = list(range(dataset_size))

使用np.shuffle打乱索引列表。

np.random.shuffle(dataset_indices)

创建拆分索引。我们选择拆分索引为数据集大小的 20% (0.2)。

val_split_index = int(np.floor(0.2 * dataset_size))

将列表切片以获得 2 个索引列表,一个用于训练,另一个用于测试。

*0*--*val_split_index*---*n*-

列车=> val_split_indexn

Val => 0val_split_index

train_idx, val_idx = dataset_indices[val_split_index:], dataset_indices[:val_split_index]

最后,创建采样器。

train_sampler = SubsetRandomSampler(train_idx)val_sampler = SubsetRandomSampler(val_idx)

现在,我们将把采样器传递给我们的数据加载器。请注意,当您使用SubsetRandomSampler时,不能使用shuffle=True

train_loader = DataLoader(dataset=natural_img_dataset, shuffle=False, batch_size=1, sampler=train_sampler)val_loader = DataLoader(dataset=natural_img_dataset, shuffle=False, batch_size=1, sampler=val_sampler)

现在,我们将在数据加载器中绘制类分布。

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(18,7))sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution_loaders(train_loader, natural_img_dataset)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[0]).set_title('Train Set')sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution_loaders(val_loader, natural_img_dataset)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[1]).set_title('Val Set')

SubsetRandomSampler 之后的类分布[Image [3]]

正如我们所观察到的,验证集中每类的样本数与训练集中的样本数成正比。

加权随机取样器

random_splitSubsetRandomSampler不同,WeightedRandomSampler用于确保每批看到所有类别的比例数量。

  1. 获取所有目标类。
  2. 获取类权重。类别权重是每个类别中项目数量的倒数。
  3. 获得每个目标样品的相应重量。

获取目标类列表并洗牌。

target_list = torch.tensor(natural_img_dataset.targets)

获取类计数,并通过取其倒数来计算权重/类。

class_count = [i for i in get_class_distribution(natural_img_dataset).values()]class_weights = 1./torch.tensor(class_count, dtype=torch.float) class_weights ###################### OUTPUT ######################tensor([0.0014, 0.0010, 0.0011, 0.0014, 0.0012, 0.0010, 0.0013, 0.0010])

将每一类的权重分配给所有样本。

class_weights_all = class_weights[target_list]class_weights_all ###################### OUTPUT ######################tensor([0.0010, 0.0012, 0.0014,  ..., 0.0010, 0.0014, 0.0010])

weightnumber of samples传递给WeightedRandomSampler

weighted_sampler = WeightedRandomSampler(
    weights=class_weights_all,
    num_samples=len(class_weights_all),
    replacement=True
)

将取样器传递给数据加载器。

train_loader = DataLoader(dataset=natural_img_dataset, shuffle=False, batch_size=8, sampler=weighted_sampler)

这就是了。您现在可以使用您的数据加载器来训练您的神经网络模型!

感谢您的阅读。欢迎提出建议和建设性的批评。😃

这篇博文是“如何训练你的神经网络”系列的一部分。你可以在这里找到系列

你可以在 LinkedIn 和 Twitter 找到我。如果你喜欢这个,看看我的其他博客。

PyTorch + BentoML + Heroku:简单的堆栈

原文:https://towardsdatascience.com/pytorch-bentoml-heroku-the-simple-stack-65196aad181c?source=collection_archive---------30-----------------------

来源:我

以及如何训练和部署一个 ML 模型到产品中。

介绍

“做简单的东西需要很多努力”

老实说,我对极简设计很着迷。它们既实用又有自己的美丽。这就是我如此热爱 UNIX 哲学的原因。它谈到了创建漂亮的工具,做一件事,做得很好,并与其他程序无缝地工作。一个设计良好的软件工具可以帮助你在创建产品时走得更远,并带来编程的艺术,我觉得这些工具接近于干净的设计,使得使用这些工具进行开发非常有趣和令人满意。

简单的工具也易于学习和教授,因为您可以从上到下地了解正在发生的事情。这就是为什么我使用这些工具向你展示如何制作深度学习模型,并将其放在云上供每个人使用。在本教程中,我们将应用迁移学习来训练 PyTorch 模型,如果给定一幅图像,它可以分辨出是蚂蚁还是蜜蜂。这是一个简单的模型,取自官方 Pytorch 教程。然后,我们将使用 BentoML 获取我们训练的模型,将其打包到一个容器中,并为其创建一个 API 端点。最后,我们将使用 Heroku(一个托管应用程序的云平台)来部署我们的模型,以便其他人可以使用它。

我不能深入到每一步,但我会确保你了解基本知识,这样,如果出现问题,你将能够找到自己的解决方案。

注意: 有一个附带的GitHub repo里面有本教程的所有代码,请下载并跟随。

让我们开始吧!

在 PyTorch 中训练模型。

我假设您熟悉 PyTorch,并使用它训练过模型。如果没有,可以看看这些很棒的教程,更好地了解 Pytorch 端发生了什么。

  1. py torch 60min Blitz——非常适合那些了解 ML 并且之前使用过另一个框架的人。
  2. Pytorch Zero to All——全面的 py torch 教程。
  3. 里奇的《不可思议的 PyTorch》-一份其他令人敬畏的 py torch 资源列表。

培训是脚本化的,即使你没有编写 PyTorch,你也可以逃脱,但是我强烈建议你查看一下提到的资源。

正如我前面提到的,这一部分是建立在 PyTorch 关于迁移学习的官方教程之上的。基本上,我们采用在 ImageNet 数据集上预先训练的 ResNet 模型,并修改最后几层,以便它可以区分蚂蚁或蜜蜂的图片。

为了训练一个模型,我们需要数据。Pytorch 开发人员使用这个数据集进行训练。您可以下载、提取数据集并将其重命名为数据,也可以运行 get_data.sh 脚本来执行相同的操作

$ sh get_data.sh

现在我们可以训练模型了。我写了一个脚本来加载数据,定义模型并训练它。它是基于 PyTorch 教程的,所以请查看该教程以获得有关步骤的详细解释。奔跑

$ python train.py

这将在 GPU(或 CPU,如果它不可用)上运行脚本 25 个时期。该脚本将训练模型,并将其作为检查点保存在 saved_models 文件夹中。在 GPU 上训练大约需要 10 分钟,在 CPU 上需要 30 分钟。如果你没有 GPU,减少历元的数量。

训练后,脚本将询问您是否要将模型保存到便当中。暂时跳过这一步。

使用便当包装和上菜

BentoML 是一个帮助您为 ML 模型构建生产就绪的 API 端点的工具。所有的 ML 框架都被支持,像 TF,Keras,Pytorch,Scikit-learn 等等。它仍然相对较新,正在积极开发中,尽管当前的 API 对于生产使用来说已经足够稳定。因为这是一个新工具,我会告诉你更多。

核心概念

因此,BentoML 的主要思想是帮助数据科学团队更快地提供预测服务,同时根据 DevOps 的最佳实践轻松测试和部署模型。它几乎就像是您的模型的包装器,创建了一个 API 端点,使其更容易部署。以下是使用 BentoML 创建任何预测服务时需要用到的重要概念。

这是构建预测服务的基类,我们编写的所有服务都继承了这个类的所有属性。我们定义特定于 API 的属性,如端点期望什么类型的数据,依赖关系是什么,模型如何处理我们从端点获得的数据等等。简而言之,所有关于如何创建端点和打包模型的信息都是从这个类的属性中推断出来的。

模型工件:工件是经过训练的模型,使用 bento 打包。BentoML 支持不同的 ML 框架,这些框架将以它们自己的方式处理。模型工件根据对应于所使用的 ML 框架选择的工件自动处理序列化和反序列化。关于支持的工件的完整列表,请查看文档

处理程序:这些处理程序指定模型期望的数据类型。我可以是 JSON,熊猫数据帧,张量流张量,图像等。在文件中给出了处理者的完整列表

保存 BentoService: 这就是神奇的地方。“便当”这个词来自日语,它是一个包装良好的饭盒,里面有各种不同的食物,如米饭、鸡肉、鱼、泡菜等,都整齐地装在一个盒子里,这完美地表示了便当。事实上,这基本上就是便当的全部功能。它会根据你的便当服务将你的模型打包成集装箱,准备好提供给全世界消费。

将模型保存在便当中

现在模型被装箱并保存到 saved_path 中。记下路径,因为我们将使用它为模型服务。现在我们可以检查我们通过在 bento 提供的开发环境中服务模型而创建的服务。现在您可以通过调用saveToBento.py脚本来保存模型。

$ python saveToBento.py *checkpoint_file*

这将打包并保存由训练脚本创建的检查点。训练脚本还有一个提示,询问您是否要在每次训练后保存。

你所要做的就是跑

$ bentoml serve IrisClassifier:latest

或者如果您知道 saved_path

$ bentoml serve *saved_path*

在 Heroku 上托管

Heroku 是一个云平台,让你不用太担心基础设施的细节就能交付你的应用。这是一个很棒的工具,因为它能让你做你喜欢的事情,也就是开发你的应用程序,Heroku 负责为数百万用户提供服务。

BentoML 目前没有对 Heroku 的内置支持(将在下一个版本中推出!)但只要稍加调整,我们就能实现它。BentoML 将我们的模型打包到 docker 容器中,我们可以使用 Heroku 的容器注册表部署 docker 容器。

首先,确保您安装了 Heroku-cli 和 Docker,并且您已经登录到 heroku-cli。现在导航到$saved_path,bento 在这里打包了我们的模型。就像我说的,它已经是集装箱化的了,但是 Heroku 要求我们对 Dockerfile 做一些修改,以便使用它的集装箱注册。

在此之前,先说一下 docker。Docker 可以轻松打包你的应用程序及其所有依赖项,这样它就可以轻松地在不同的系统中运行,而无需设置和环境。这使得开发人员非常容易,因为他们可以确保他们在开发系统中编写的应用程序将在生产系统中运行,而无需额外的设置,因为 docker 将所有这些依赖项打包到其中。这基本上定义了在 docker 文件中设置环境所需的所有步骤。该文件包含设置和运行应用程序的所有说明。此外,需要注意的是,这款应用并不能直接在你的设备上运行。它运行在 Linux 容器上,这些容器位于主机系统的 Linux 内核之上,并在它自己的隔离环境中运行应用程序。要了解更多关于 docker 的信息,请查看这个令人敬畏的博客。

回到我们的例子,如果您检查\(saved_path 中的 Dockerfile,我们可以看到它公开了端口 5000。这是为了让我们的服务器 Gunicorn 监听来自这个端口的请求。另一方面,Heroku 要求使用其容器注册中心部署的容器监听 Heroku 设置的端口。使用环境变量\)PORT 将端口号传递给它。所以我们的脚本必须在$PORT 而不是 5000 上运行我们的服务器。

为此,我们将最后一个 CMD 命令从

CMD ["bentoml serve-gunicorn /bento $FLAGS"]

CMD bentoml serve-gunicorn /bento --port $PORT

另外,注释掉 EXPOSE 5000 和 ENTRYPOINT 来处理 Heroku。最终的 docker 文件将是这样的

现在让我们在我们的系统上运行它,检查是否一切正常。首先,通过运行以下命令构建 docker 映像

$ docker build . -t resnet_heroku

这将用标记名 resnet_heroku 构建图像。现在运行 docker 容器

docker run -p 5000:5000 -e PORT=5000 resnet_heroku
  • -p 将我们的 5000 端口绑定到容器的端口
  • -e 将环境变量$PORT 传递给 docker

转到 localhost:5000 并检查是否一切正常。如果很棒,就推给 Heroku 吧!

使用以下命令首次登录 Heroku 的容器注册表

$ heroku container:login

Heroku 将所有内容存储为应用程序,以创建 Heroku 应用程序运行

heroku create -a *app_name*

现在,您可以构建 BentoService 并将其推送到您的 Heroku 应用程序。

$ heroku container:push web --app *app_name*

并发布应用程序

$ heroku container:release web --app *app_name*

恭喜你刚刚向全世界发布了你的第一个 ML API!让我们试一试。从互联网上下载任何显示蚂蚁和蜜蜂的图片,并使用 curl 将其传递给 API。

$ curl POST https://ant-bee-classifier.herokuapp.com/predict -H "Content-Type: image/*" --data-binary @image.jpg -v

这将从 API 中输出结果。如果没有,并且出现了问题,尝试通过运行以下命令来检查日志

$ heroku logs --tail -a *app_name*

结论

至此,您已经学会了使用“简单堆栈”来训练和推动一个模型进入生产。这对于简单的设置来说是完美的,这样你就可以用最少的工作把你的 ML 模型放到世界上。

矩阵分解的 PyTorch 实现

原文:https://towardsdatascience.com/pytorch-implementation-of-matrix-factorization-391bfc003e43?source=collection_archive---------21-----------------------

用 Python 从头开始实现矩阵分解

尼克·希利尔在 Unsplash 上的照片

什么是矩阵分解

矩阵分解(MF)(例如,概率矩阵分解和非负矩阵分解)技术已经成为许多现实场景的关键,包括图形表示和推荐系统(RecSys),因为它们是发现数据背后隐藏属性的强大模型。更具体地说,非负矩阵分解(NNMF)是多元分析和线性代数中的一组模型,其中矩阵 A(维数 BC)被分解为 B(维数 Bd)和 C(维数 C*d)

矩阵分解公式

其中 F 表示 Frobenius 范数。

矩阵分解背后的思想是在一个低维的潜在空间中表示用户和项目。广泛应用于推荐系统和降维中。尽管有许多 Python 库可以执行矩阵分解,但从头构建算法可能有助于理解基础知识。此外,有许多复杂的情况时,矩阵分解库不能处理。在这篇文章中,我将展示如何在 PyTorch 中用不同的用例实现矩阵分解,这些用例是普通 MF 库不能很好执行的

示例 1:

图形表示:

给定一个图 G = (E,V),V 是节点的集合,E 是边的集合。G 的邻接矩阵用 V * V 矩阵 a 表示,其中如果节点 Vi 与节点 Vj 之间有边 E,则 Xij = 1,否则 Xij =0。按照前面的公式,我们可以通过最小化损耗将节点 V 表示成 V * d 矩阵

示例 1 损失公式

在这种情况下,我们将矩阵分解成一个单一的矩阵,这通常是大多数库不支持的。为了解决这个问题,我们使用 PyTorch 构建一个只有一层的神经网络模型,并将 SGD 优化器应用于反向传播梯度。损失函数可以用神经网络来表示。ms loss(reduction = ' sum '),即 Frobenius 范数和。重要的是要确保分解矩阵和矩阵的乘积没有非正值,因为非负矩阵分解(NNMF)。您可以应用 torch Threshold(0,0)函数将每个历元中的所有负值标记为 0,或者添加一个 Relu 输出图层。

代码实现:

例 2:

带有电影标签的用户电影记录系统:

在现实应用中,处理 RecSys 中的冷启动问题非常重要,因为每天都有新电影问世。用户电影 RecSys 不能很好地处理这个问题,因为没有用户以前看过新电影。这是 MF 技术的变体,通过结合电影标签的信息来缓解冷启动问题。

给定用户 U、电影 V 和电影标签 t 的集合。设 X 是 UV 矩阵,表示评级项目的用户历史,其中如果第 I 个用户 Ui 没有对第 j 部电影 Vj 进行评级,则 Xij = 0,否则 Xij =(0,5)表示 Ui 对电影 Vj 的评级范围从 1 到 5。设 Y 是表示电影标签信息 TV 矩阵,其中如果电影 Vj 用 Ti 标记,则 Yij = 1,否则 Yij = 0。因此,您可以将用户 U、电影 V 和标签 T 表示为 U、V 和 T,其中

示例 2 损失公式

在这种情况下,我们正在进行乘法矩阵分解,并使总和的总损失最小化。这也很难用普通的 MF 库来实现。然而,使用 PyTorch,我们只需要重新定义损失函数。

代码实现:

感谢您的阅读,我期待听到您的问题和想法。如果你想了解更多关于数据科学和云计算的知识,可以在 Linkedin 上找我。

照片由阿尔方斯·莫拉莱斯在 Unsplash 上拍摄

参考

https://developers . Google . com/machine-learning/recommendation/collaborative/matrix

https://en . Wikipedia . org/wiki/Matrix _ factorization _(recommender _ systems)#:~:text = Matrix % 20 factorization % 20 is % 20a % 20 class,two % 20 lower % 20 dimensionality % 20 rectangular % 20 matrices。

Python 中的 PyTorch

原文:https://towardsdatascience.com/pytorch-in-python-77ab9fab1b39?source=collection_archive---------39-----------------------

对深度学习和人工智能感兴趣?PyTorch 是一个基于 Python 的计算库,它使用图形处理单元的能力。当谈到深度学习研究平台时,它受到许多人的青睐。这里有一些关于 PyTorch 和一些可能的真实应用程序的见解。

照片由 Maximalfocus 在 Unsplash 上拍摄

首先,让我从解释 PyTorch 将如何对您有用开始。PyTorch 有许多不同的用途,但主要用于替代 NumPy 使用 GPU 的能力,以及提供灵活性和速度的深度学习研究平台。

机器学习/深度学习

人工智能本质上是构建智能机器,这些机器能够执行通常需要人类智能才能完成的任务。它包括机器学习和深度学习。机器学习为计算机系统提供了从经验中学习和改进的能力,但无需显式编程,即开发可以自行访问数据并从中学习的计算机程序。深度学习是机器学习、算法和神经网络的子集,其灵感来自人类从大量数据中学习。深度学习算法重复执行一项任务,并在每次迭代中进行调整,以提高结果的成功程度。术语“深度学习”指的是神经网络中能够进行学习的深层。深度学习的一个流行例子是虚拟助手,如 Siri 或 Alexa,它们从你使用的语音和语言中学习,以更好地提高性能。你可能听说过谷歌的人工智能,用于增强人的能力。请看一下的关于页面这里

“谷歌的使命是组织世界上的信息,并使其普遍可用和有用。人工智能正在以令人兴奋的新方式帮助我们做到这一点,为我们的用户、客户和世界解决问题。”

入门指南

说到使用 PyTorch,我建议从学习 Python 编程语言和面向对象编程的基础开始。面向对象编程提供了一种结构化程序的方法,将属性和行为捆绑到对象中。一个对象可以代表一个人,其属性包括姓名和年龄,行为包括走路和跳舞。和往常一样,最好看一下文档,以获得函数及其参数的参考,您可以在这里找到

当谈到创建模型时,比如下面的线性回归模型,你需要掌握张量,以便在深度学习中使用。张量是线性代数中使用的一种数据结构类型。你可以用张量计算算术运算,就像用向量和矩阵一样。假设您正在使用如下所示的 NumPy 数组。

作者图片

最右边的三维数组是一个张量。标量有零维,向量有一维,矩阵有二维,张量有三维或更多维。然而,向量和矩阵也是张量,任何东西不是标量就是张量。这些是用于数据的张量,它们也可以用作可训练参数。用作可训练参数或权重的张量需要计算其梯度。PyTorch 支持 GPU 加速张量计算来解决此类问题。它通过许多不同的包来做到这一点,如自动签名、动态计算图、优化器、损失计算和模型。

  1. ****亲笔签名:一个自动微分包,让我们能够忽略偏导数和链式法则。它会为我们计算所有的梯度。
  2. 动态 计算图:深度学习框架维护一个计算图,显示需要执行的计算顺序。能够使用动态图是一件大事,因为不同的框架(如 Tensorflow 和 Theano)只允许您创建静态图。这意味着在运行模型之前,您必须定义整个图形。在 PyTorch 中,您可以在运行时定义或调整您的图形。
  3. ****优化算法:获取我们想要更新的参数、期望的学习率,并使用其 step()方法执行这些更新。这通常通过使用计算的梯度手动更新参数来完成。这对于只有几个变量的情况来说还不算太糟,但是对于多个参数来说肯定会变得更加困难。
  4. 损失计算 : PyTorch 包括许多损失函数,因为下面的例子是一个回归,我们将使用 MSE(均方差)损失来计算给定预测和标签的损失。
  5. **模型:py torch 中的模型用继承自模块**类的 Python 类来表示。***__init__(self)***:定义了组成模型的不同部分——在我们的例子中,有两个参数, ab

线性回归是一种非常常用的预测分析形式。它用于显示或预测两个变量之间的关系,方法是检查一组预测变量,看它们是否能成功预测因变量的结果。此外,通过帮助确定哪些变量特别是结果变量的重要预测因素。这是一个来自《走向数据科学》的线性回归模型的例子。

图片来自丹尼尔·戈多伊/走向数据科学

为了训练一个模型,有两个初始化步骤:****

参数/权重的随机初始化(我们只有两个, ab ) —第 3 行和第 4 行;

超参数的初始化(在我们的例子中,只有学习率周期数* ) —第 9 行和第 11 行;***

确保始终初始化您的随机种子,以确保结果的再现性**

对于每个时期,有四个训练步骤:**

计算模型的预测—这是正向传递* —第 15 行;***

使用预测标签以及手头任务的适当损失函数计算损失——第 18 行和第 20 行;**

计算每个参数的梯度——第 23 行和第 24 行;**

更新参数—第 27 行和第 28 行

现在来看看使用 PyTorch 的众多好处之一。下面的代码创建了一个随机梯度下降优化器来更新 a 和 b 参数。由于优化器接受学习率和参数,它将简单地用 step() 方法执行更新。

图片来自丹尼尔·戈多伊/走向数据科学

戈多伊特区(2020 年 6 月 2 日)。通过示例了解 PyTorch:分步教程。检索自https://towardsdatascience . com/understanding-py torch-with-a-a-a-example-a-step-tutorial-81 fc 5 f 8 C4 E8 e

结论

虽然不使用 PyTorch 也能完成上述任务,但该库将为您提供更快、更直观的体验。这是可能的,因为 PyTorch 的框架开销很小。当与 NVIDIA 或英特尔 MKL 等加速库集成时,它可以快速处理小型和大型神经网络。除了速度更快和用户友好之外,由于为 GPU 编写的自定义内存分配器,它在内存使用方面非常高效。总的来说,这是一个非常有用的 Python 包

参考资料:

美国首都戈多伊(2020 年 6 月 2 日)。通过示例了解 PyTorch:分步教程。检索自https://towards data science . com/understanding-py torch-with-a-a-a-a-example-a-step-tutorial-81 fc 5 f 8 C4 E8 e

布朗利,J. (2019 年 12 月 5 日)。NumPy 机器学习张量的温和介绍。检索自https://machine learning mastery . com/introduction-to-tensors-for-machine-learning/

佩雷斯,C. E. (2019 年 3 月 1 日)。PyTorch,动态计算图和模块化深度学习。检索自https://medium . com/intuition machine/py torch-dynamic-computational-graphs-and-modular-deep-learning-7e 7 f 89 f 18d 1

真正的蟒蛇。(2020 年 5 月 22 日)。Python 3 中的面向对象编程(OOP)。检索自https://real python . com/python 3-面向对象编程/# what-is-object-oriented-programming-OOP

Shetty,s .,Shetty,s .,Gall,r .,Lobo,s .,Davis,v .,Sunith ShettyData Science。(2018 年 9 月 20 日)。PyTorch 是什么,它是如何工作的?检索自https://hub . packtpub . com/what-is-py torch-and-how-do-it-work/

PyTorch JIT 和 TorchScript

原文:https://towardsdatascience.com/pytorch-jit-and-torchscript-c2a77bac0fff?source=collection_archive---------1-----------------------

PyTorch 模型的生产之路

来源

我们都喜欢 PyTorch 的动态性和易用性。但是当涉及到部署时,这些品质不如性能和可移植性那样令人满意。

在过去,部署需要迁移到不同的框架。从研究到生产的转变需要两个拥有不同技能的独立团队来实现。随着 TorchScript 的引入,这一过程发生了变化。

PyTorch 旨在通过 TorchScript 创建一个从研究到生产的统一框架。TorchScript 会将您的 PyTorch 模块作为输入,并将其转换为适合生产的格式。

它将更快地运行您的模型,并且独立于 Python 运行时。

PyTorch 生态系统

PyTorch 支持两种不同的模式来处理研究和生产环境。

首先是 急切模式。 它是为了更快的原型制作、训练和实验而建造的。

其次是 脚本模式。 它专注于生产用例。它有两个组件 PyTorch JITtorch script

为什么我们需要脚本模式?

一句话,它摆脱了 Python 的 GIL 和对 Python 运行时的依赖。一个微妙的解释如下

  1. 可移植性
    可移植性允许模型部署在多线程推理服务器、手机和汽车中,这对于 Python 来说是很困难的。为了实现这一点,PyTorch 模型需要与任何特定的运行时解耦。
  2. 性能
    PyTorch JIT 是一款针对 PyTorch 的优化 JIT 编译器。它使用运行时信息来优化 TorchScript 模块。它可以自动优化,如层融合,量化,稀疏化。

脚本模式

脚本模式创建 PyTorch Eager 模块的中间表示(IR)(通过torch.jit.trace/torch.jit.script)。IR 经过内部优化,并在运行时利用 PyTorch JIT 编译。PyTorch JIT 编译器使用运行时信息来优化 IR。这个 IR 与 Python 运行时是分离的。

脚本模式通过利用 PyTorch JIT 和 TorchScript 来工作。

PyTorch JIT 是什么?

PyTorch JIT 是一个为 PyTorch 程序优化的编译器。

  1. 它是一个轻量级的线程安全解释器
  2. 支持易于编写的自定义转换
  3. 这不仅仅是为了推断,因为它有自动差异支持

TorchScript 是什么?

TorchScript 是 Python 语言的静态高性能子集,专门用于 ML 应用程序。它支持

  1. 复杂的控制流
  2. 公共数据结构
  3. 用户定义的类

我们已经介绍了 PyTorch JIT、TorchScript 和脚本模式。
我们讨论了它们是什么,以及我们为什么需要它们?在下面的片段中,我们将了解如何使用它们?

脚本模式由torch.jit.tracetorch.jit.script调用。

JIT 跟踪

torch.jit.trace取一个数据实例和你训练好的 eager 模块作为输入。tracer 运行提供的模块并记录执行的张量运算。这段录音被转换成一个 TorchScript 模块。

跳到示例 1,查看它的运行情况

它可以重用现有的 eager 模型代码,并且可以处理几乎任何具有独占 torch 张量/操作的程序。

它的主要缺点是省略了所有的控制流、数据结构和 python 结构。它也可以在没有任何警告的情况下产生不忠实的陈述。总是检查它的 IR,看它是否正确地解析了 PyTorch 模型的结构。

JIT 脚本

torch.jit.script允许你直接将代码写入 TorchScript。它更加冗长,但是更加通用,稍加调整就可以支持大多数 PyTorch 模型。

与跟踪模式相反,你只需要将你的模型/模块的一个实例传递给torch.jit.script.,而不需要数据样本。

跳到实例 2 的实际应用

它保留了控制流和其他 python 结构,看起来更像 Python。它对列表/字典有一流的支持。

它省略了常量值,并且需要类型转换。如果没有提供类型,则默认为张量

对于一次会议来说,这是太多的理论了,让我们转到代码上,看看运行中的跟踪/脚本。

例子 1:伯特

BERT(来自变压器的双向编码器表示)是由谷歌人工智能的研究人员开发的。在下面的例子中,我们利用了 HuggingFace 提供的 transformer 库中的 BERT。

该示例分为 3 个部分

  • 第 1 部分
    初始化 BERT 模型/记号化器,并创建一个用于推理的样本数据

第 1 部分:实例化 BERT/Tokenizers

  • 第 2 部分:
    为 CPU/GPU 上的推理准备 PyTorch 模型。

模型/数据应该在同一设备上,以便进行训练/推理。cuda()将模型/数据从 CPU 传输到 GPU。

第 2 部分:在 CPU/GPU 上对 PyTorch 模型进行基准测试

  • 步骤 3:
    准备 TorchScript 模块,以便在 CPU/GPU 上进行推理

第 3 部分:CPU/GPU 上 TorchScript 模块的基准测试

伯特的 PyTorch vs TorchScript

在 CPU 上运行时间相似,但在 GPU 上 TorchScript 明显优于 PyTorch。

TorchScript 创建 PyTorch 模型的 IR,可以在运行时由 PyTorch JIT 进行优化编译。可以使用traced_model.code查看该红外图像。

示例 2: ResNet

ResNet 是残差网络的缩写,是一种经典的神经网络,用作许多计算机视觉任务的主干。这款车型是 2015 年 ImageNet 挑战赛的冠军。

以下示例有两个部分

  • 示例 1.1 / 1.2:
    PyTorch ResNet 在 CPU/GPU 上的初始化和推理
  • 示例 2.1/2.2: torch.jit.scriptCPU/GPU 上的初始化和推理

PyTorch 和脚本的 ResNet 示例

TorchScript 明显优于 PyTorch 在 GPU 上的实现。

PyTorch vs 的 TorchScript

辅助部件

如何保存/加载 TorchScript 模块?

TorchScript 将模块保存/加载为归档格式。这个档案是模型的独立表示,可以加载到一个完全独立的进程中。

  • 保存模块torch.jit.save(traced_model,’traced_bert.pt’)
  • 加载模块loaded = torch.jit.load('traced_bert.pt')

如何查看 TorchScript 捕捉到的 PyTorch IR?

  • 示例 1:使用traced_model.code查看 PyTorch IR
    跳过这一步,因为它非常冗长
  • 示例 2:使用script_cell_gpu.code查看 PyTorch IR
def forward(self,
    input: Tensor) -> Tensor:
  _0 = self.fc
  _1 = self.avgpool
  _2 = self.layer4
  _3 = self.layer3
  _4 = self.layer2
  _5 = self.layer1
  _6 = self.maxpool
  _7 = self.relu
  _8 = (self.bn1).forward((self.conv1).forward(input, ), )
  _9 = (_5).forward((_6).forward((_7).forward(_8, ), ), )
  _10 = (_2).forward((_3).forward((_4).forward(_9, ), ), )
  input0 = torch.flatten((_1).forward(_10, ), 1, -1)
  return (_0).forward(input0, )

结合跟踪和脚本

torch.jit.trace 和 torch.jit.script 可以结合起来弥补各自的不足。看这个官方举例。他们通过内嵌代码来做到这一点。

其他好处

除了改进推理时间,使用 TorchScript 还有其他好处

  • TorchScript 将您的模型从任何运行时环境中分离出来。它摆脱了 Python 的 GIL,这是多线程推理的一个主要瓶颈。
  • TorchScript 专注于整个程序的优化。
  • TorchScript 自动优化神经网络中的常见模式,以改善延迟和吞吐量。
  • TorchScript 模块可以导出到各种环境中,从 C++服务器到移动设备。

摘要

这个博客引入了脚本模式。从功能上来说,脚本模式提供了将您的研究模型投入生产的工具,同时仍然处于 PyTorch 生态系统中。

重申一下,这是模型开发和部署的典型路径。

  • 步骤 0:开发你的模型,最好是在 PyTorch 渴望模式下
  • 步骤 1:在 PyTorch 模型上使用torch.jit.trace或/和torch.jit.script创建 TorchScript 模块
  • 步骤 2:使用这种格式的torch.jit.save/torch.jit.load.将这些模块转移到生产环境中,它们可以在从服务器到边缘设备的任何地方运行
  • 步骤 3:使用 PyTorch JIT 编译器在推理时优化这些程序,以最小的努力享受更快的推理。

参考

  1. Torchscript + PyTorch JIT
  2. 从研究到生产
  3. PyTorch 文档
  4. 为变压器模型使用 TorchScript】

PyTorch 图层尺寸:让您的图层每次都能正常工作(完整指南)

原文:https://towardsdatascience.com/pytorch-layer-dimensions-what-sizes-should-they-be-and-why-4265a41e01fd?source=collection_archive---------1-----------------------

第一次,每一次,让你的层平滑地适合。PyTorch 中的张量和层维度入门指南。

用这些宝贵的知识,第一次就能让你的衣服层很好的贴合。—Clark Van Der Beken在 Unsplash 上拍摄的照片

前言

本文涵盖了定义张量、在 PyTorch 中正确初始化神经网络层等等!(原标题为 PyTorch 图层尺寸:什么尺寸,为什么?)

简介

你可能会问:“我如何在 PyTorch 中初始化我的层尺寸而不被吼?”这一切都只是试错吗?不,真的…它们应该是什么?首先,您是否知道第一个**和第二个需要一个torch.nn.Conv2d层的参数,而一个torch.nn.Linear层需要完全不同方面的完全相同的张量数据?如果你不知道这些,请继续阅读。

例 1:相同,相同,但不同。

构造卷积图层和线性图层在语法上是相似的,但是尽管能够对完全相同的输入数据进行操作(尽管数据的大小应该不同),但参数并不期望得到相似的东西。

***# The __init__ method of a nn.Module class:**...def __init__(self):
"""Initialize neural net layers.""" super(Net, self).__init__()

    # Intialize my 2 layers here: self.conv = **nn.Conv2d(1, 20, 3)** *# Give me* ***depth*** *of input.*self.dense = **nn.Linear(2048, 10)** *# Give me* ***features*** *of input.*...*

在将数据集放入某些网络层之前,您需要了解 PyTorch 模型如何使用数据。

第一课:如何在 PyTorch 中读取张量大小

下面是 PyTorch 中遇到的一些常见张量大小以及何时使用它们的典型示例。知道你在看什么很重要,因为它们的结构并不像你希望的那样可预测(这种有些不直观的设计选择主要是为了性能利益而实现的,这是好的… 我猜是)。

当把张量输入卷积或线性层(虽然* 不是 RNN 的)时,我们有一个心理锚,那就是第一维总是批量 大小(N) 。然而,剩下的维度取决于月亮的相位和你所在地区月亮升起时蟋蟀的间歇频率。开玩笑,没那么简单。你必须死记硬背。所以开始*旋转(下面的例子 2)。**

吃他们说的红色药丸。他们说再深入一点。这个张量的第三维度应该是什么?!?—照片由 Tim Gouw 在 Unsplash 上拍摄

了解 PyTorch 期望它的张量如何成形是很重要的——因为你可能非常满意你的 28 x 28 像素图像显示为 Torch 的张量。大小([28,28])。而 PyTorch 则认为你希望它查看你的 28 批 28 个特征向量。我只想说,在你学会如何用她的方式看待事情之前,你们不会成为朋友——所以,不要成为那样的人。研究你的张量维度!

例 PyTorch 喜欢的张量维数。

***"""Example tensor size outputs, how PyTorch reads them, and where you encounter them in the wild.** *Note: the values below are only examples. Focus on the rank of the tensor (how many dimensions it has).****"""*****>>> torch.Size([32])**
 *#* ***1d:*** *[batch_size] 
    # use for target labels or predictions.***>>> torch.Size([12, 256])**
    *#* ***2d:*** *[batch_size, num_features (aka: C * H * W)]
    # use for* ***nn.Linear****() input.***>>> torch.Size([10, 1, 2048])**
   *#* ***3d:*** *[batch_size, channels, num_features (aka: H * W)]
    # when used as* ***nn.Conv1d****() input.
    # (but [seq_len, batch_size, num_features]
    # if feeding an* ***RNN****).***>>> torch.Size([16, 3, 28, 28])**
    *#* ***4d:*** *[batch_size, channels, height, width]
    # use for* ***nn.Conv2d()*** *input.***>>>  torch.Size([32, 1, 5, 15, 15])**
    *#* ***5D:*** *[batch_size, channels, depth, height, width]
    # use for* ***nn.Conv3d****() input.**

注意到 Conv2d 层想要一个 4d 张量吗?1d 或 3d 图层怎么样?

因此,如果您想将一幅灰度为 28 x 28 像素的图像加载到 Conv2d 网络图层中,请在上面的示例中找到图层类型。因为它需要一个 4d 张量,而你已经有一个具有高度和宽度的 2d 张量,只需添加 batch_size 和 channels (参见下面 channels 的经验法则)来填充额外的维度,就像这样:[1,1,28,28]。那样的话,你和 PyTorch 可以和好如初。

第二课:初始化 torch.nn.Conv2d 层

文档描述了这样一个 Conv2d 层:

*"""
*Class*torch.nn.Conv2d(***in_channels***, ***out_channels***, *kernel_size*, *stride=1*, *padding=0*, *dilation=1*, *groups=1*, *bias=True*, *padding_mode='zeros'*)*Parameters****in_channels***(int) - Number of channels in the input image
***out_channels*** (int) - Number of channels produced by the convolution
"""*

还记得你的输入通道是哪个维度吗?如果你忘了,请看第一课。只需在第一个卷积层中使用该数字作为您的in_channels参数。搞定。**

第一个 Conv2d 层上“in_channels”的经验法则:

—如果您的图像是黑白的,则为 1 通道。(您可以通过在 dataloader 的 transforms 参数中运行transforms.Grayscale(1)来确保这一点。)

—如果您的图像是彩色的,则它是 3 个通道(RGB)。

—如果有 alpha(透明)通道,则它有 4 个通道。

这意味着对于您的第一个 Conv2d 层,即使您的图像大小很大,如 1080 像素乘 1080 像素,您的in_channels通常会是 1 或 3。

注意: 如果你用一些随机产生的张量测试这个,它仍然对你呕吐,你现在对着你的电脑大喊,深呼吸。没关系。确保它有正确的尺寸。你有没有 *unsqueeze()* 这个张量?Pytorch 想要批量。unsqueeze()函数将添加一个表示批量为 1 的维度 1。**

但是,out_channels 呢?

你说的out_channels怎么样?你希望你的人际网络有多深,这是你的选择。基本上,Pytorch 定义的您的out_channels维度是:

out _ channels(int)—卷积产生的通道数

对于您使用的每个卷积核,当通过该层时,您的输出张量变得更深一个通道。如果你想要大量的内核,把这个数字设得高一些,比如 121,如果你只想要一些,把这个数字设得低一些,比如 8 或者 12。您在这里选择的任何数字都将是下一个卷积层channels_in值,以此类推。**

注意:kernel_size的值是自定义的,虽然很重要,但不会导致令人头疼的错误,因此在本教程中省略。取一个奇数即可,通常在 3-11 之间,但大小可能因应用程序而异。

通常,网络前半部分的卷积层越来越深,而网络末端的全连接(又名:线性或密集)层越来越小。这里有一个来自 60 分钟初学者闪电战的有效例子(注意self.conv1出频道变成了self.conv2入频道):

****class** **Net**(nn**.**Module):

    **def** __init__(self):
        super(Net, self)**.**__init__()
        *# 1 input image channel, 6 output channels, 3x3 square convolution*
        *# kernel*
        self**.**conv1 **=** nn**.**Conv2d(1, **6**, 3)
        self**.**conv2 **=** nn**.**Conv2d(**6**, 16, 3) *# an affine operation: y = Wx + b*
        self**.**fc1 **=** nn**.**Linear(16 ***** 6 ***** 6, 120)  *# 6*6 from image dimension*
        self**.**fc2 **=** nn**.**Linear(120, 84)
        self**.**fc3 **=** nn**.**Linear(84, 10)**

现在来说说全连接层。

第三课:完全连接(torch.nn.Linear)图层

线性层的文档告诉我们以下内容:

****"""** *Class*  
torch.nn.Linear(***in_features***, ***out_features***, *bias=True*)*Parameters***in_features** – size of each input sample
**out_features** – size of each output sample"""**

我知道这些看起来很相似,但是不要混淆:“in_features”和“in_channels”是完全不同的,初学者经常混淆它们,认为它们是相同的属性。

****# Asks for in_channels, out_channels,** kernel_size, etc
self.conv1 = nn.Conv2d(1, 20, 3)**# Asks for in_features, out_features**
self.fc1 = nn.Linear(2048, 10)**

计算尺寸。

对于所有的nn.Linear层网络,有两个特别重要的论点,无论你的网络有多深,你都应该知道。第个参数和第个参数。你有多少个完全连接的层在中间并不重要,这些维度很容易,你很快就会看到。

如果你想把你的 28 x 28 的图像传入一个线性层,你必须知道两件事:

  1. **您的 28 x 28 像素图像不能作为[28,28]张量输入。**这是因为nn.Linear 会将其读取为 28 批 28 个特征长度的向量。既然它期望一个[batch_size, num_features]的输入,你必须以某种方式转置它(见下面的视图())。
  2. ****您的批次大小不变地通过您的所有层。不管你的数据在网络中如何变化,你的第一维最终将是你的batch_size,即使你从未在网络模块的定义中看到这个数字。

使用 视图 ()来改变你的张量的维度。

image = image.view(**batch_size**, -1)

您提供您的 batch_size 作为第一个数字,然后“-1”基本上告诉 Pytorch,“您为我算出另一个数字…请。”你的张量现在将正确地馈入任何线性层。现在我们正在谈话!

因此,要初始化线性图层的第一个参数,请向其传递输入数据的要素数量。对于 28×28,我们的新视图张量大小为[1,784] (1 * 28 * 28)😗***

示例 3:使用视图调整大小()以适合线性图层

**batch_size = 1# Simulate a 28 x 28 pixel, grayscale "image"
input = torch.randn(1, 28, 28)# Use view() to get [batch_size, num_features].
# -1 calculates the missing value given the other dim.
input = input.view(batch_size, -1) # torch.Size([1, **784**])# Intialize the linear layer.
fc = torch.nn.Linear(**784**, 10)# Pass in the simulated image to the layer.
output = fc(input)print(output.shape)
>>> torch.Size([1, 10])**

R 记住这个——如果你曾经从卷积层输出转换到线性层输入,你必须使用 view 将其从 4d 调整到 2d,如上面的图像示例所述。

所以,[32,21,50,50]的 conv 输出应该被“展平”成为[32,21 ***** 50 ***** 50]张量。并且线性图层的 in_features 也要设置为[21 ***** 50 ***** 50]。

线性层的第二个参数如果你把它传递给更多的层,被称为隐藏层的 H 。你只需要和 H 打定位乒乓球,让它成为上一个的最后一个和下一个的第一个,就像这样:****

****"""The in-between dimensions are the hidden layer dimensions, you just pass in the last of the previous as the first of the next."""**fc1 = torch.nn.Linear(784, 100) *# 100 is last.*
fc2 = torch.nn.Linear(100, 50) *# 100 is first, 50 is last.*
fc3 = torch.nn.Linear(50, 20) *# 50 is first, 20 is last.*
fc4 = torch.nn.Linear(20, 10) *# 20 is first.* *"""This is the same pattern for convolutional layers as well, only it's channels, and not features that get passed along."""***

****最后的输出,也就是你的输出层取决于你的模型和你的损失函数。如果你有 10 个类,像在 MNIST,你正在做一个分类问题,你希望你的所有网络架构最终合并成最后的 10 个单元,这样你就可以确定你的输入预测的是这 10 个类中的哪一个。

最后一层取决于您想从数据中推断出什么。为了得到您需要的答案,您可以做的操作是另一篇文章的主题,因为有很多内容需要讨论。但是现在你应该已经掌握了所有的基础知识。

就是这样!

你现在应该能够建立一个网络,而不用挠头或者被翻译吼了。请记住,您的 batch_size 或 dim 0 始终是相同的。卷积图层关注深度(通道),线性图层关注要素数量。并学习如何阅读那些张量!

请留下评论,或者分享这篇文章,如果你喜欢它,并发现它很有帮助!

PyTorch 用 TorchServe 升级了它的发球游戏

原文:https://towardsdatascience.com/pytorch-levels-up-its-serving-game-with-torchserve-3d97ac502364?source=collection_archive---------42-----------------------

通过 TorchServe,脸书和 AWS 继续缩小机器学习研究和生产之间的差距。

PyTorch 标志

近年来,PyTorch 已经在很大程度上超过 Tensorflow,成为研究型数据科学家的首选的机器学习模型训练框架。这有几个原因,但主要是 Pytorch 是为 Python 构建的,作为其首选语言,而 Tensorflow 的架构更接近其 C/C++核心。尽管这两个框架都有 C/C++内核,Pytorch 确实加载了更多的来使其接口“python 化”。对于那些不熟悉这个术语的人来说,它基本上意味着代码很容易理解,不会让你感觉像是一个敌对的老师为一道考试题而写的。

Pytorch 更简洁的界面已经被那些主要优先考虑将他们的计划分析快速转化为可操作结果的人们广泛采用。我们没有时间玩静态计算图 API,它让我觉得自己像个罪犯,因为我想设置一个断点来调试张量的值。我们需要做一些原型,然后进入下一个实验。

不全是阳光和彩虹

然而,这种优势是有代价的。使用 Pytorch,我们可以轻松地完成一个又一个实验,将结果以类似的速度投入生产。可悲的是,由于缺乏封装 API 复杂性的生产就绪框架,使这些模型服务于生产比实验吞吐量要慢。至少,Pytorch 缺少这些框架。

Tensorflow 早就有了一个真正令人印象深刻的模型服务框架, TFX 。事实上,如果你对 Tensorflow 生态系统了如指掌,它的框架并没有太多缺失。如果你有一个 TF 模型,并且正在使用谷歌云,那就使用 TFX 直到它咽下最后一口气。如果你不在那个阵营,结合 TFX 和 PyTorch 已经不是即插即用。

新的希望

不再害怕!PyTorch 的 1.5 版本带来了 TorchServe 的初始版本以及 TorchElastic 与 Kubernetes 的实验支持,用于大规模模型训练。软件巨头脸书和 AWS 继续增强 PyTorch 的能力,并为谷歌基于 Tensorflow 的软件管道提供了一个有竞争力的替代方案。

TorchServe 是一个灵活易用的库,用于在大规模生产中高效地服务 PyTorch 模型。它不受云和环境的限制,支持多模型服务、日志记录、指标以及为应用集成创建 RESTful 端点等特性。

  • PyTorch 文档

让我们来解析其中的一些品质,因为它们值得重点重复:

  • 在大规模生产中高效服务 PyTorch 模型
  • 与云和环境无关
  • 多模式服务
  • 记录
  • 韵律学
  • RESTful 端点

任何生产中的 ML 模型具有这些特征,一定会让你的工程师同事非常高兴。

让我们点燃火炬吧

TorchServe 提供了您期望从现成的服务 API 中得到的大部分东西。除此之外,它还为基于图像和基于文本的模型提供了默认推理处理器。由于我的音频背景,我不能不抱怨基于音频的模型再次被排除在一流的支持之外。目前,音频仍然是机器学习领域的害群之马!唉,我跑题了……

在图像分类任务中产生良好结果的 DenseNet 架构在 TorchServe 中作为默认模型提供。PyTorch 工程师提供了一个预建的 docker 图像,但是我为下面的演示添加了一些东西。如果你想为自己重建,在这里运行四个命令。

运行正常的

通过向torchserve命令传递一些命令行参数,我们就有了一个准备好提供模型预测的生产服务。在不使文章陷入细节的情况下,您可以使用一个快速的驱动程序脚本从 URL 或本地路径通过服务器抽取图像。

陈词滥调时间!猫和狗,开始了。

./[query.sh](https://github.com/aagnone3/pytorch-serve-overview/blob/master/query.sh) images/puppy.jpg

GDragon612 在 Fanpop [ 来源上的照片

Blenheim_spaniel: 97.00%

Papillon: 1.40%

Japanese_spaniel: 0.94%

Brittany_spaniel: 0.41%

完全正确!前四名中的三个是西班牙狗的亚种,大约 1%的巴比隆狗没什么可大惊小怪的。

一只小小的小猫怎么样,但是 多了一顶毛皮帽子?我猜他天生的皮毛和耳朵不适合这张照片?不管怎样,就网络的预测而言,这肯定不会改变太多…

./[query.sh](https://github.com/aagnone3/pytorch-serve-overview/blob/master/query.sh) images/kitten-2.jpg

照片由 sillymonkeyart 在易贝来源

Egyptian_cat: 24.90%

Marmoset: 20.75%

Tabby: 11.22%

Patas: 10.17%

好吧,那么…

当然,大多数人投票支持猫进入前 4 名,但是这些预测几乎没有超过狨猴和狨猴的预测,这两者都是对…猴子的分类。

Andrew Sit 在 Unsplash 上拍摄的照片

很可爱,但没那么可爱。

我猜是为了努力。出于好奇,现在有一个很好的机会来应用注意力技术来调试图像的哪些部分在影响分类中被大量使用。

火炬服务在行动

再往下一步,这是一些原始 cURL 命令的演示。请记住,所有这些 API 行为都是现成的。我提供的只是模型和输入图像的选择。

驾驶我的本地火炬服务器实例

结论

我几乎没有接触过 TorchServe 的表面,这是一件好事。如果我可以在一篇简单的博客文章中演示它的所有功能,它还远远不能投入生产。也许在把它放在任何任务关键的东西后面之前,允许更多的版本翻转。但是脸书和 AWS 为润滑 PyTorch 研究和生产之间的滑道建立了一个令人兴奋的基础。

保持最新状态

在学术界和工业界,事情发展得很快!用一般的 LifeWithData 博客和 ML UTD 时事通讯让自己保持最新状态。

如果你不是时事通讯的粉丝,但仍然想留在圈子里,考虑将 lifewithdata.org/blog 的和 lifewithdata.org/tag/ml-utd 的添加到 Feedly 聚合设置中。

原载于 2020 年 5 月 24 日 https://lifewithdata.org

Pytorch 闪电机器学习零到英雄 75 行代码

原文:https://towardsdatascience.com/pytorch-lightning-machine-learning-zero-to-hero-in-75-lines-of-code-7892f3ba83c0?source=collection_archive---------12-----------------------

Pytorch 闪电正席卷全球。不要错过这 75 行代码,它们开启了你掌握机器学习的道路。我们将涵盖提前停止、自动批量缩放、自动学习率查找、动态批量大小、Pytorch 中的数据集、保存您的模型和可视化。都在 75 行以下。

我们的掌握之路,作者图片

基础知识

Pytorch Lightning 具有显著减少开发时间的特性。一旦你理解了基础知识,你的效率就会神奇地提高。在引擎盖下,闪电仍然是 Pytorch,但更容易和更快地工作。你不必担心大多数事情。坐下来放松,闪电开始工作了。

注意:我将在本文中展示基于 CPU 的代码,但会在本文的底部添加一个基于 GPU 的版本(微小的差异)

进口和基础

正如我们所看到的,我们简单地导入了所有很酷的特性,我们已经准备好了。一旦我们使用了它,我们将逐一查看它们

1.数据集

我们现在将定义我们自己的数据集,Lightning 从那里处理它。我们也可以从互联网上加载一个,但是知道如何将我们自己的数据放入模型是关键!

9 行,这基本上还是普通的 Pytorch。dataset 是 PyTorch 的一个类,它允许我们利用许多好处。我们要做的就是让我们的类继承“数据集”。一个数据集类至少需要这三个方法。

今天我们将构建一个非常简单的模型,它将学习预测所有输入元素的总和是否大于 0,或者是 0 和 0。最终,我们返回一个元组(特征,标签),这样 Lightning 就可以完成它的工作。

len: Pytorch 想知道数据集中有多少样本。self.dataset.size()返回我们表的维度(42,21000)[0]取元组的 42 our,这是我们的长度。

2.模型

让游戏开始吧。我们现在将定义一个非常简单但功能强大的模型!Lightning 中的一个模型需要继承“LightningModule”。为了让 Lightning 模型工作,我们至少需要定义 init()、forward()、train_dataloader()和 training_step()函数。我们甚至还会定义一些比宙斯本人更耀眼的特性。

我们的模型

init():这里我们基本上只是定义了一个线性层模型,每个模型有 2048 个节点,我们将在后面的步骤中使用它们。此外,我们定义了 self.lr 和 self.batch_size,以便稍后使用 Lightning 自动学习特性。是的,你没听错,我们将在训练时学习它们!

forward():这是我们模型的核心和灵魂。在这里,我们定义了信息如何流经我们的模型。我们基本上只是按顺序调用我们的层,并在最后做一个简单的整形以保持一致。基本上,这里的输入是我们的电子表格中的一行,然后通过我们的管道,最终输出将在 0 和 1 之间。

configure_optimizer:我们定义了一个 adam 优化器,这是帮助我们学习的东西。我们使学习速度可调,这样我们也可以学习那个

train_dataloader():这个函数必须返回一个数据加载器。数据加载器是一个帮助 Pytorch 将训练样本输入模型的对象,它处理使用的批量大小并节省大量代码。我们简单地抛出我们在 1 中定义的数据集。并说我们将生成 43210 个样本。啊,生活有时会比在隔离区无聊更简单。

接下来的 for 函数对我们来说基本上是一点重复的工作。我们定义了一个损失函数,这样亚当就知道优化什么。验证数据加载器与我们刚刚编程的是同一个东西,它帮助我们简单地获得一个好的比较集,这样我们就可以稍后实现早期停止。剩下的只是为我们超级酷的可视化汇总统计数据。

3.自动学习你的学习速度

恭喜你,最后一段代码将是你的奖励。我们将定义一个简单的 main 函数来启动杀手管道。

我们的主要

让我们一起来!首先,我们使用了很酷的函数 seed_everything(42 ),这将极大地帮助我们提高运行的可重复性。虽然它不能使机器学习完全可预测,但它肯定有助于消除大多数随机性。

在这个例子中,我们将使用我们的 CPU,所以我们不需要启动我们的机器。但是用 GPU 运行它也一样容易,我会把 GPU 代码留在最下面(4 行不同)

早期停止是避免过度拟合的一个很好的机制。通过这个简单的调用,我们可以监控验证损失。一旦训练损失和验证损失出现分歧,模型将停止训练。我们可以假设我们既没有过拟合也没有欠拟合机器学习中的两个主要问题。

在我们将模型投射到 CPU 上之后,我们就可以启动我们的训练器了!这基本上是所有的,我们现在可以开始学习和适应我们的模型。在这之后,我们简单地保存我们的模型,我们就完成了。让我们来看看这两个节省时间的功能,我爱这两个功能胜过我心爱的猫,它们可以让我每周节省一整天的工作时间。

培训过程

提前停止

正如我们可以看到在大约 21 个时期的早期停止命中,这是进一步训练不能进一步减少验证损失的点。培训师只需停在那里并选择该型号。

自动学习你的学习速度

这可能是迄今为止发现的最有用的机器学习功能之一。不再需要调整学习速度,不再需要网格搜索,一切尽在掌握。那么它是如何工作的呢?我们只需打开训练器中的“auto_lr_find = True”标志,就可以开始了。这基本上做什么它测试几个学习率,并检查哪一个是国王。它通过测试选择的批次的损失减少了多少来做到这一点。要了解更多细节,我可以推荐优秀的论文“训练神经网络的循环学习率”或者更简单、更实用的解释,https://github.com/davidtvs/pytorch-lr-finder非常直接地解释了一个非常相似的过程。

学习率是习得的

4.自动学习您的批量大小

另一个可以节省大量思考能力的杀手锏。当我在一个有几个不同类型的集群上工作时,它帮助我计算出 GPU 可以运行多少个批处理,这是一个天赐的发明。要打开它,我们只需设置“auto_scale_batch_size=True”。

危险警告:当您同时运行自动批处理和自动学习率时,这些过程将花费很长时间(但它会工作)。所以首先找到理想的批量,然后使用学习率查找器。

了解批量大小

在进行任何训练之前,自动批量缩放会自动尝试找到适合内存的最大批量。虽然这个简单的例子不会让我们碰到天花板,但是一旦你运行了你的严肃模型,这个收敛会非常快。

5.形象化

还有一件事。我们在这 75 行中编码的内容足以用 tensorboard 进行简单的可视化。运行代码后,将创建一个名为 lightning_logs 的文件夹,并使用我们的运行结果进行填充。我们所要做的就是运行带有 logdir 标志的 tensorboard 命令,我们就可以很好地分析我们所做的事情了!

如我们所见,我们记录了验证损失和培训损失。我们只是通过将它返回到 validation_step/training_step 函数中来实现这一点。这些图是用 X 轴上的批次绘制的,我们可以通过在 validation_epoch_end 函数中只返回各时期的聚合值来改变这一点。

结论

tensorboard --logdir=lightning_logs/

我们看到了生活是多么简单。Pytorch Lightning 可能是最简单的功能模块之一。虽然我们没有涵盖所有功能,但我们从我的角度看了最重要的功能。另外值得一提的是半精度训练、多 GPU 支持以及各种日志记录和数据集缩减功能。

你现在有技能和知识来训练你的第一个模型,用你翅膀下的闪电速度运载。请确保继续关注我,了解您的机器学习模型和管道中更多的生产力和性能技巧。

如果你喜欢这篇文章,我会很高兴在 Twitter 或 LinkedIn 上联系你。

一定要看看我的 YouTube 频道,我每周都会在那里发布新视频。

完整代码

Make sure to check out my YouTube channel, where I will be publishing new videos every week.

Full Code

深度 Java 库中的 Pytorch 模型

原文:https://towardsdatascience.com/pytorch-model-in-deep-java-library-a9ca18d8ce51?source=collection_archive---------20-----------------------

从 PyTorch 模型构建到 DJL 推理的简单端到端工作流程

在 Unsplash 上由 Max Duzij 拍照

PyTorch 是一个快速发展且非常流行的开源机器学习框架。它的命令式设计与类似“numpy”的工作流程相结合,使它成为初学者和专业人士的首选。然而,在生产中服务这些模型并不简单,如果目标是在 Java 中本地服务它们,事情会特别困难。

亚马逊的 Deep Java Library (DJL)旨在通过提供高级 API 来解决这个特殊的痛点,这些 API 可以用很少的代码在 PyTorch 模型上运行推理。我最近与 DJL 的试驾告诉我,它可能是一个非常强大的工具,但现有的示例集和社区指南(也称为 stackoverflow help:)可能会让新手感到有点害怕,特别是那些来自 python 背景并且不熟悉 java 风格的人。希望这个简单的演示能让他们的工作更轻松。所有的脚本也可以在我的 git repo 这里获得。

让我们首先用 PyTorch 创建一个简单的线性回归模型。

import torch
import torch.nn as nnX = torch.tensor([[1],[2],[4],[7],[9]], dtype = torch.float32)
Y = torch.tensor([[2],[4],[8],[14],[18]], dtype = torch.float32)X_test = torch.tensor([[5]], dtype=torch.float32)n_sample, n_features = X.shapemodel = nn.Linear(n_features, n_features)learn_rate = 0.01
n_epochs = 500loss = nn.MSELoss()
optimizer = torch.optim.SGD(lr = learn_rate, params=model.parameters())for i in range(0,n_epochs):
    y_pred = model(X)
    ls = loss(y_pred,Y) ls.backward()
    optimizer.step()
    optimizer.zero_grad() [w,b] = model.parameters() traced_cell = torch.jit.trace(model, X_test) #print(traced_cell(X_test).item()) print(f"{ls.item():0.3f}, {w[0][0].item():0.3f {model(X_test).item():0.3f}")traced_cell.save('./model1.zip')

模型(模型 1)学习获取输入 n 并预测 2 * n。

上面的脚本中有两行代码是 PyTorch 脚本中没有的。

1\. traced_cell = torch.jit.trace(model, X_test) 
2\. traced_cell.save('./model1.zip')

这两行是创建一个 TorchScript 模型(序列化 pytorch 模型)所必需的,该模型可以在高性能环境中以与语言无关的方式使用。我们现在将使用这个模型通过 DJL 来进行推论。我用了一个运行 Java 内核的 Jupyter 笔记本来做这个演示。

1:获得所有需要的 maven 依赖项。DJL 支持 pytorch 模型开箱即用。SLF4J 用于伐木。

%maven ai.djl.pytorch:pytorch-engine:0.8.0
%maven ai.djl.pytorch:pytorch-native-auto:1.6.0
%maven org.slf4j:slf4j-simple:1.7.26

如果出现“未知解析器为空”的错误,现在可以忽略它们。

2:导入所需的模块

import ai.djl.*;
import ai.djl.inference.*;
import ai.djl.ndarray.*;
import ai.djl.translate.*;
import java.nio.file.*;

这里值得强调一下 ndarray 的导入。n 维数组是许多语言中流行的数据结构,numpy(python 中的 ndarray 库)在深度学习领域或几乎任何涉及数值计算的应用程序中都非常普遍。

Java 没有类似的 ndarray 实现。DJL 通过上述导入提供了 ndarray 功能。这是允许 DJL 使用 PyTorch 模型的关键因素之一。

3.声明模型路径和模型名称。这假设您正在使用一个本地生成的模型。DJL 也提供了一些可以直接使用的标准模型。

Path modelDir = Paths.get("Path_To_Your_Model_Folder/");
Model model = Model.newInstance("model name");
model.load(modelDir);
model

当前的 DJL 文档说模型可以。zip,。tar,. tar.gz,.tgz 或. tar.z

如果模型加载正确,您应该会看到类似 ai . djl . py torch . engine . pt model @ 4c EFA 68 f 的输出

4.翻译器处理预处理和后处理步骤。

Translator<Float, Float> translator = new Translator<Float, Float>(){ @Override
    public NDList processInput(TranslatorContext ctx, Float input) {
        NDManager manager = ctx.getNDManager();
        NDArray array = manager.create(new float[] {input});
        return new NDList (array);
    }

    @Override
    public Float processOutput(TranslatorContext ctx, NDList list) {
        NDArray temp_arr = list.get(0);
        return temp_arr.getFloat();
    }

    [@](http://twitter.com/Override)Override
    public Batchifier getBatchifier() {
        // The Batchifier describes how to combine a batch together
        // Stacking, the most common batchifier, takes N [X1, X2, ...] arrays to a single [N, X1, X2, ...] array
        return Batchifier.STACK;
    }
};

这是最让大多数人困惑的一步。翻译器定义了输入和输出数据类型的签名。在我们的例子中,它们都是浮点数。确保使用 float,而不是 Float,因为显式需要引用。这个翻译步骤不是可选的。

此外,如果您查看预处理器的 DJL 文档,您会发现 NDList 是提供给模型的预处理输出的预期类型。

因此,即使假设您的输入已经是 NDList 类型,您仍然需要执行这一步。

后处理的情况类似。它使用 NDList 并输出您的翻译器描述的类型。

以上三条也是强制性的。当然,DJL 提供了预实现的翻译器,我推荐你去探索它们,尤其是图像处理的。

5.做出预测

祝贺您,您已经完成了艰苦的工作,离做出推论只有几行代码了。他们在这里

Predictor<Float, Float> predictor = model.newPredictor(translator);
predictor.predict(Your_Test_Float)

对于我的测试用例,我做了 predictor.predict(2.9f),它输出 5.799781。啊,纯粹的快乐!

因此,我们看到了如何创建 PyTorch 模型,并在 Java 环境中使用该模型进行推理。尽管这是一个非常简单的用例。对于更高级的用例,让翻译人员正确地进行预处理和后处理可能需要一些迭代。此外,内置模块(如图像处理模块)的数量目前很少。

不过,我相信 DJL 将会迅速发展,并可能成为在 Java(或 C++)环境中利用 PyTorch 模型的完美库,实现开发速度和高性能的完美结合。

使用 TensorBoard 的 PyTorch 性能分析

原文:https://towardsdatascience.com/pytorch-performance-analysis-with-tensorboard-7c61f91071aa?source=collection_archive---------33-----------------------

深度强化学习讲解— 05

如何在 Colab 中运行 PyTorch 的 TensorBoard

去年,脸书宣布py torch的 1.1 版本支持 TensorBoard (TensorFlow 的可视化工具包)。TensorBoard 提供了深度学习实验所需的可视化和工具。毫无疑问,TensorBoard 是一个非常有用的工具,可以用来理解神经网络的行为,并在训练过程中帮助我们处理超参数。

TensorBoard 是 TensorFlow 生态系统中的一个可视化工具,可用于绘制各种定量指标和几个中间计算的结果。在这篇文章中,我们将强调我们需要开始使用它的主要功能。在以后的文章中,我们会根据需要引入新的特性。PyTorch 文档中说明其功能的详细教程可以在这里找到。

接下来,我们将对在 Colab 中设置 Tensorboard 的步骤做一个大致的描述和总结,这在这一系列的文章中肯定会对我们非常有用。

Colab 环境

在这篇文章中,我们假设你想在谷歌研究项目 合作实验室 (Colab)中运行 TensorBoard。正如我们在之前的帖子中所描述的,它基本上由一个 Jupyter 笔记本环境组成,不需要任何配置,完全在云中运行,允许使用不同的深度学习库,如 PyTorch 和 TensorFlow 。关于该服务的详细信息可以在常见问题页面上找到。

跟随这个链接你可以把这篇文章的代码当作一个谷歌笔记本来执行。

设置张量板

要设置 TensorBoard,您只需遵循以下步骤:

1-torch.utils导入tensorboard定义一个SummaryWriter,我们向 TensorBoard 写入信息的关键对象:

**from** torch.utils.tensorboard **import** SummaryWriter

2-tensor board 将在其中查找要消费的记录的默认日志文件夹是runs。我们可以说得更具体些:

writer **=** SummaryWriter('runs/working_directory')

注意,这一行单独创建了一个runs/working_directory文件夹。

为了表明我们已经完成注册,我们可以调用对象的close方法:

writer.close()

3- 加载 TensorBoard 笔记本扩展:

%load_ext tensorboard

4- 启动张量板:

tensorboard  --logdir=runs

使用示例

为了展示 TensorBoard 的用法,我们建议使用上一篇文章中的相同代码:

import torch
import torchvisionimport numpy as np
EPOCHS = 10
BATCH_SIZE= 64xy_trainPT = torchvision.datasets.MNIST(root=’./data’, train=True, download=True,transform=torchvision.transforms.Compose([torchvision.transforms.ToTensor()]))xy_trainPT_loader = torch.utils.data.DataLoader(xy_trainPT, batch_size=BATCH_SIZE)def model(hidden):
    model= torch.nn.Sequential(
           torch.nn.Linear(784,hidden),
           torch.nn.Sigmoid(),
           torch.nn.Linear(hidden,10),
           torch.nn.LogSoftmax(dim=1)
           )
    return modelmodelPT = model(10)
criterion = torch.nn.NLLLoss()
optimizer = torch.optim.SGD(modelPT.parameters(), lr=0.01)

运行下面两行代码后:

from torch.utils.tensorboard import SummaryWriter%load_ext tensorboard

我们已经可以像上一篇文章那样进行第一次训练,但现在我们还将查看损失图表。以下代码突出显示了添加的行:

**writer = SummaryWriter()**for e in range(EPOCHS):
    running_loss = 0
    for images, labels in xy_trainPT_loader:
        images = images.view(images.shape[0], -1)
        output = modelPT(images)
        loss = criterion(output, labels)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        running_loss += loss.item()
    print(“Epoch {} — Training loss: {}”.format(e,   
           running_loss/len(xy_trainPT_loader)))
    **writer.add_scalar(“loss x epoch”, 
           running_loss/len(xy_trainPT_loader), e)****writer.close()**

我们可以通过调用 TensorBoard 来查看结果:

tensorboard  --logdir=runs

记住,包含数据的文件存储在runs文件夹中。通过选择左侧菜单中的文件夹图标,我们可以看到这一点:

但是,如果我们需要知道每次迭代(超过 9000 次迭代)的损失是如何下降的,我们无法通过简单的打印来完成,我们需要一个更图形化的表示:

modelPT = model(10)
criterion = torch.nn.NLLLoss()
optimizer = torch.optim.SGD(modelPT.parameters(), lr=0.01)def training_loop():
 **writer = SummaryWriter()**  iter_no=0
  for e in range(EPOCHS):
    running_loss = 0
    for images, labels in xy_trainPT_loader:
        images = images.view(images.shape[0], -1)
        output = modelPT(images)
        loss = criterion(output, labels)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        running_loss += loss.item()
 **writer.add_scalar("loss x iter", loss.item(), iter_no)**        iter_no += 1
 **writer.close()**training_loop()

本次运行的信息已保存在runs文件夹下的新目录中。我们可以通过再次运行以下命令来查看结果:

tensorboar --logdir=runs

但是也许我们感兴趣的是比较,例如,具有更多神经元的神经网络的行为。让我们试试这个有 32 个神经元的神经网络的例子:

modelPT = model(32)
criterion = torch.nn.NLLLoss()
optimizer = torch.optim.SGD(modelPT.parameters(), lr=0.01)
training_loop()

当记录数据时,我们可以使用相同的图形名称(在本例中为“loss x ITER”)在相同的图形上绘图。这种情况下的结果将是:

我们可以看到两种网络中的损耗行为的比较。或者说,用另一个优化器来代替 SGD 这个 Adam 怎么样?

modelPT = model(32)
criterion = torch.nn.NLLLoss()
optimizer = torch.optim.Adam(modelPT.parameters(), lr=0.01)
training_loop()

在这种情况下我们可以看到它收敛得更好!

这只是 TensorBoard 提供的机会的一小部分。我邀请读者自己探索这个强大的工具,它也为其他中间件服务,特别是 TensorFlow,TensorBoard 就是在这个框架中创建的。

下一个帖子见!

这篇文章的全部代码可以在 GitHub 上找到

深度强化学习讲解系列

UPC 巴塞罗那理工 巴塞罗那超级计算中心

一个轻松的介绍性系列以一种实用的方式逐渐向读者介绍这项令人兴奋的技术,它是人工智能领域最新突破性进展的真正推动者。

** [## 深度强化学习解释-乔迪托雷斯。人工智能

本系列的内容](https://torres.ai/deep-reinforcement-learning-explained-series/)

关于这个系列

我是在五月份开始写这个系列的,那是在巴塞罗那的封锁期。老实说,在业余时间写这些帖子帮助了我 #StayAtHome 因为封锁。感谢您当年阅读这份刊物;它证明了我所做的努力。

免责声明 —这些帖子是在巴塞罗纳被封锁期间写的,目的是分散个人注意力和传播科学知识,以防对某人有所帮助,但不是为了成为 DRL 地区的学术参考文献。如果读者需要更严谨的文档,本系列的最后一篇文章提供了大量的学术资源和书籍供读者参考。作者意识到这一系列的帖子可能包含一些错误,如果目的是一个学术文件,则需要对英文文本进行修订以改进它。但是,尽管作者想提高内容的数量和质量,他的职业承诺并没有留给他这样做的自由时间。然而,作者同意提炼所有那些读者可以尽快报告的错误。**

py torch:CRAFT 和四阶段网络的场景文本检测和识别

原文:https://towardsdatascience.com/pytorch-scene-text-detection-and-recognition-by-craft-and-a-four-stage-network-ec814d39db05?source=collection_archive---------5-----------------------

用 Python 实现成熟的文本检测和识别管道

疫情已经把我们锁在家里好几个月了。但是请记住,当生活正常的时候,我们会去购物,和朋友出去玩,看电影,在购物中心享受美食!

还有,你还记得每家商店都有独特的奇特的写法吗?像古驰、西尔斯、Pantaloons 和 Lifestyle 这样的流行品牌在他们的标志中使用弯曲或圆形字体。虽然所有这些都吸引了客户,但它确实对执行文本检测和识别的深度学习(DL)模型构成了挑战。

照片由泰勒·辛普森在 Unsplash 上拍摄

当你阅读横幅上的文字时,你会做什么?你的眼睛首先检测文本的存在,找出每个字符的位置,然后你识别这些字符。这也正是 DL 模型需要做的!

最近,OCR 已经成为深度学习中的一个热门话题,其中每个新的架构都在尽力超越其他架构。

广受欢迎的基于深度学习的 OCR 模块,Tesseract 在文档等结构化文本上表现出色,但在花式字体的弯曲、不规则形状的文本上表现不佳。

幸运的是,我们有 Clova AI 的这些令人惊叹的网络,它们在现实世界中出现的各种文本外观上胜过宇宙魔方。在这篇博客中,我们将简要讨论这些架构,并学习如何集成它们。

用 CRAFT 进行文本检测

场景文本检测是一项在复杂背景中检测文本区域并用包围盒标记它们的任务。

于 2019 年提出的 CRAFT:用于文本检测的字符区域意识的主要目标是定位单个字符区域,并将检测到的字符链接到文本实例。

在论文中给出的工艺网络

CRAFT 采用基于 VGG-16 的全卷积网络架构作为主干。简而言之,VGG16 本质上是一种特征提取架构,用于将网络输入编码为某种特征表示。工艺网的解码段类似于 UNet。它跳过了聚合低级功能连接。

CRAFT 为每个角色预测了两个分数:

  • 区域评分:顾名思义,给出了人物所在的区域。它将角色本地化。
  • 亲和力得分:‘亲和力’是一种物质倾向于与另一种物质结合的程度。因此,相似性分数将字符合并成单个实例(一个单词)。

CRAFT 生成两个地图作为输出:区域级别地图和亲缘关系地图。
让我们通过一个例子来理解它们的含义:

输入图像

人物出现的区域在区域图中标记出来:

区域地图

亲和度图是相关字符的图像化表示。红色象征着人物有很高的亲和力,必须合并成一个词:

相似性地图

最后,结合相关度和区域分数来给出每个单词的包围盒。坐标的顺序是:
(左上),(右上)(右下),(左下),其中每个坐标是一个(x,y)对。

为什么不遵循四点格式?
查看下图:你能把“爱”仅仅局限在 4 种价值观中吗?

布鲁诺·菲格雷多在 Unsplash 上拍摄的照片

CRAFT 是多语言的,这意味着它可以检测任何脚本编写的文本,而不仅仅是拉丁文!自己去看看吧:

作者照片:红色边框

文本识别:F 我们的舞台场景文本识别框架

2019 年,Clova AI 发表了一篇关于与现有场景文本识别(STR)数据集不一致的研究论文,并提出了一个大多数现有 STR 模型都适合的统一框架。

网络提出的四个阶段

让我们来讨论这四个阶段中的每一个:

  1. 变换:记住我们处理的是风景文字,是任意形状的,有曲线的。如果我们直接执行特征提取,还需要学习输入文本的几何形状,这是特征提取模块的额外工作。因此,STR 网络应用薄板样条(TPS)转换,并将输入文本规范化为矩形。
  2. 特征提取:将变换后的图像映射到一组与字符识别相关的特征。字体、颜色、大小和背景被删除。作者在不同的主干网上进行了实验,即 ResNet、VGG 和 RCNN。
  3. 序列建模:如果我写‘ba _ ’,你很有可能会猜测像‘d’,‘g’,‘t’这样的字母可能会填充空白,而不是‘u’,‘p’。我们如何教会网络捕捉上下文信息?比尔斯特姆斯!但是,BiLSTMs 占用内存,因此可以根据用户需要选择或取消选择该阶段。
  4. 预测:这个阶段从图像的识别特征中估计输出字符序列。

作者进行了几个实验。他们为每个阶段选择了不同的网络。下表总结了精确度:

不同型号精度参考:https://arxiv.org/pdf/1904.01906.pdf

此外,请注意,这个四阶段网络仅在拉丁脚本的语料库上进行训练。

现在我们清楚了 CRAFT 和四阶段 STR 是如何工作的,让我们来看看代码吧!

代码

CRAFT 预测每个单词的边界框。四阶段 STR 将单个单词(作为图像)作为输入,并预测字母。如果您正在处理像 CUTE80 这样的单个单词的图像,那么使用这些 DL 模块进行 OCR 将是一件轻而易举的事情。

但这种可能性有多大呢?我们的大多数用例涉及对大量图像的预测,如果没有,我们最有可能有带有多个文本外观的图像。如果我们有一个将两者结合起来的单一管道,那不是很好吗?

让我们编写自己的代码来组合它们!

第一步:安装要求

第二步:Git 克隆存储库:

第三步:修改返回检测框分数 CRAFT 返回超过一定分数阈值的包围盒。如果您想查看每个边界框的分值,我们需要对原始存储库进行一些更改。在克隆的 craft 存储库中打开 craft_utils.py。您需要将第 83 行和第 239 行改为如下所示。

第四步:从 CRAFT 中移除参数解析器打开 test.py,修改如下图。我们移除了参数解析器。

步骤 5:编写一个单独的脚本,将图像名称和检测框坐标保存到一个 CSV 文件 这将帮助我们裁剪需要作为四阶段 STR 输入的单词。它还帮助我们将所有与边框和文本相关的信息保存在一个地方。
创建一个新文件(我将其命名为 pipeline.py)并添加以下代码。

pandas DataFrame(可变数据)将图像名称和其中出现的单词的边界框存储在单独的列中。我们删除了图像的整个路径,只保留图像以避免笨拙。你显然可以根据自己的需求进行定制。您现在可以运行脚本了:

这个阶段的 CSV 是这样的。对于每个检测,我们存储一个 python 字典,其中存储了 score: coordinates。

CSV 文件

第六步:裁剪单词 现在我们已经有了每个盒子的坐标和分数。我们可以设置一个阈值,并裁剪我们希望识别字符的单词。创建一个新的脚本 crop_images.py。记住在提到的地方添加你的路径。裁剪后的单词保存在“dir”文件夹中。我们为每张图片创建一个文件夹,并以这种格式保存从其中裁剪下来的单词:
<父图片> _ < 8 坐标由下划线>
分隔这也将帮助您跟踪每个裁剪的单词来自哪张图片。

运行脚本!

第六步:认可(终于!) 你现在可以对你裁剪出来的单词盲目运行识别模块了。但是如果你想让事情更有条理,请修改以下内容。我们在每个图像文件夹中创建一个. txt 文件,并将识别出的单词与裁剪图像的名称一起保存。
除此之外,预测的单词保存在我们维护的 CSV 中。

从 Clova AI STR Github 存储库下载权重后,可以运行以下命令:

我们选择这种网络组合是因为它们的高精度。

CSV 现在看起来如下。pred_words 具有检测框坐标和预测字,由冒号分隔。

结论

我们已经集成了两个精确的模型来创建单个检测和识别模块。既然预测的单词和它们的边界框都在一列中,那么您就可以按照您想要的任何方式对齐文本了!

Pytorch:逐步实现三维卷积神经网络

原文:https://towardsdatascience.com/pytorch-step-by-step-implementation-3d-convolution-neural-network-8bf38c70e8b3?source=collection_archive---------1-----------------------

深度学习

了解如何编写 3d CNN 的 PyTorch 实现

照片由来自佩克斯的埃伯哈德·格罗斯加斯泰格拍摄

在本文中,我们将简要解释什么是 3d CNN,以及它与普通的 2d CNN 有何不同。然后我们会一步步教你如何用 Pytorch 实现自己的 3D 卷积神经网络。

这篇文章的一个非常重要的部分可以在我的另一篇关于 3d CNN 在 Keras 的实现的文章中找到。所以如果你倾向于用 Tensorflow/Keras 编码,那么这个链接可能是合适的。

本文将围绕这 4 部分展开:

  • 1】什么是 3D 卷积神经网络?
  • 2】3d 数据是什么样子的?(例如 MNIST)
  • 3】现在如何实施?!
  • 4】但是然后一个 3d?为什么

[## 一步一步实现:Keras 中的 3D 卷积神经网络

了解如何实现您自己的 3D CNN

towardsdatascience.com](/step-by-step-implementation-3d-convolutional-neural-network-in-keras-12efbdd7b130)

1]什么是 3D 卷积神经网络?

不管我们怎么说,3d CNN 仍然是一个与 2d CNN 非常相似的 CNN。不同之处在于以下几点(非穷尽列举):

3d 卷积层

最初,2d 卷积层是输入和不同滤波器之间的逐条目乘法,其中滤波器和输入是 2d 矩阵。(图 1)

图 1(权利:拥有)

在 3d 卷积层中,使用相同的操作。我们在多对 2d 矩阵上进行这些操作。(图 2)

图 2(权利:拥有)

填充选项和幻灯片步长选项的工作方式相同。

3d 最大池图层

2d Maxpool Layers (2x2 filter)是从输入中提取一个 2x2 小正方形的最大元素。(图 3)

图 3(权利:拥有)

现在,在 3d Maxpool (2x2x2)中,我们寻找宽度为 2 的立方体中的最大元素。此立方体代表由输入的 2x2x2 区域界定的空间。(图 4)

图 4(权限:拥有)

请注意,操作的数量(与 2d CNN 层相比)乘以所使用的过滤器的大小(不管该层是最大池还是卷积层),还乘以输入本身的大小。

2]3d 数据是什么样子的?

那么 3d CNN 的数据点是什么样的呢?

描绘它的一种方法是使用下面的图像(图 5):

图 5(权限:拥有)

其他可用于 CNN 的现有数据集有:

  • RGB-D 设备:谷歌 Tango ,微软 Kinect 等。
  • 激光雷达
  • 多幅图像的三维重建

3] 现在如何实现?!

您可以亲自尝试我们正在使用的来自 Kaggle 的数据集上的代码。

笔记本中将使用多个库。这是它的清单。

首先,由于数据集有点特殊,我们使用下面的 to helper 函数在将它们提供给网络之前对它们进行处理。

另外,数据集存储为 h5 文件,因此要提取实际的数据点,我们需要从 h5 文件中读取数据,并使用 to _ categorical 函数将其转换为向量。在这一步中,我们还要准备交叉验证。

假设变量 X_train/X_test 应该分别具有 shape (10000,16,16,16,3)和(2000,16,16,16,3)以及 targets _ train/targets _ test(10000,)(2000,)。但是我们现在再一次把它们转换成 PyTorch 张量格式。我们是这样做的。

这里的模型是我们将使用的架构:

2 套 ConvMake:

  • 一个 3d 卷积层,两个集合的过滤器大小为 3x3x3,跨距为 1x1x1
  • 泄漏的 Relu 激活功能
  • 具有过滤器大小(2x2x2)和跨距(2x2x2)的 3d MaxPool 层

2 个 FC 层,分别有 512128 个节点。

第一 FC 层后的 1 个脱落层

然后,按照以下方式将模型转换成代码:

就参数而言,注意你的第一个完全卷积层的输入节点数。我们的数据集的形状是(16,16,16,3),这就是我们如何得到大小为(2x2x2)的过滤输出。

如果您不熟悉 PyTorch 上的 CNN(即模型的参数或训练),那么可以考虑阅读 PyTorch 上的 CNN 简介!

[## py torch:CNN 在 MNIST 的一步一步的实施

这里有一个关于如何在 PyTorch 中实现 CNN 及其优点的快速教程。我们一行一行地检查,以便您…

medium.com](https://medium.com/@michaelchan_2146/pytorch-real-step-by-step-implementation-of-cnn-on-mnist-304b7140605a)

这是训练的代码。没什么特别的,你显然可以优化(更多!)例如,通过将优化器改为 Adam,调整学习速率(有一些动力)等等…

供您参考,经过小样本训练,我们得到了以下准确度和损耗。(图 6 和图 7)

图 6(权限:拥有)

图 7(权限:拥有)

4]但是然后一个 3d?为什么

3d CNN 碰巧有许多应用,例如:

  • IRM 的数据处理和由此得出的推论
  • 自驾
  • 距离估计

好了,差不多就这些了。我希望你尝试一下这项技术!源代码在这里结束!

感谢您的阅读,如果您喜欢,请关注我、我的网站和我的脸书页面!

PyTorch:切换到 GPU

原文:https://towardsdatascience.com/pytorch-switching-to-the-gpu-a7c0b21e8a99?source=collection_archive---------2-----------------------

如何以及为什么在 GPU 上训练模型——包括代码。

与 TensorFlow 不同,PyTorch 没有为 GPU 用户提供专用的库,作为开发人员,您需要在这里进行一些手工操作。但是最后,它会节省你很多时间。

照片由 Artiom Vallat 在 Unsplash 上拍摄

如果你想知道,在你的机器上安装 CUDA 或者在 Colab 上切换到 GPU 运行时还不够。不要误解我的意思,这仍然是必要的第一步,但是仅仅这样做并不能充分利用 GPU 的能力。

在本文中,您将了解如何针对以下场景从 CPU 切换到 GPU:

  1. 训练/测试分离方法
  2. 数据装载方法

第一个最常用于表格数据,而你每次处理图像数据时都会用到第二个(至少根据我的经验)。

这两种方法之间有相当多的差异,因此将对每一种方法进行深入解释。我还应该提到,我将在这篇文章中使用 Google Colab 。如果你还没有,你可以在这里阅读我的观点和更多相关内容:

[## Google Colab:它与支持 GPU 的笔记本电脑相比如何?

Colab 简介、运行时、性能比较…以及疑难解答

towardsdatascience.com](/google-colab-how-does-it-compare-to-a-gpu-enabled-laptop-851c1e0a2ca9)

这篇文章的结构如下:

  1. 为什么要转 GPU?
  2. 训练/测试分离方法
  3. 数据加载器方法
  4. 结论

所以事不宜迟,让我们开始吧!

为什么要转 GPU?

在你使用真正深度神经网络的情况下——例如ResNet152 进行迁移学习——对 CPU 的训练会持续很长时间。如果你是一个理智的人,你就不会试图那样做。

线性代数运算在 GPU 上并行完成,因此您可以将训练时间减少大约100 倍。不用说,但它也是在多个 GPU 上执行训练的一个选项,这将再次减少训练时间。

你不必相信我的话。我已经决定基于这个数据集做一个猫和狗的分类器。该模型基于 ResNet50 架构——首先在 CPU 上训练,然后在 GPU 上训练。

以下是训练时间:

GPU 运行时间:00:11:57h;CPU 运行时间:06:08:40 小时

自己判断吧,不过我还是坚持 GPU 运行时。它在 Colab 上是免费的,所以没有理由不这样做。好了,我们现在知道 GPU 应该用于模型训练,现在让我们看看如何进行切换。

训练/测试分离方法

如果你在 Scikit-Learn 中用 Python 做了一些机器学习,你肯定对训练/测试分割很熟悉。简而言之,这种想法是在数据集的一部分(假设 80%)上训练模型,并在剩余部分(假设 20%)上评估模型。

训练/测试分割仍然是深度学习中的一种有效方法——特别是对于表格数据。首先要做的是声明一个变量,它将保存我们正在训练的设备(CPU 或 GPU):

device = torch.device(**'**cuda**'** if torch.cuda.is_available() else **'**cpu**'**)
device**>>> device(type='cuda')**

现在我将声明一些虚拟数据,它们将作为 X_train 张量:

X_train = torch.FloatTensor([0., 1., 2.])
X_train**>>> tensor([0., 1., 2.])**

酷!我们现在可以检查张量是否存储在 GPU 上:

X_train.is_cuda**>>> False**

正如所料——默认情况下,数据不会存储在 GPU 上,但将数据移动到那里相当容易:

X_train = X_train.to(device)
X_train**>>> tensor([0., 1., 2.], device='cuda:0')**

干净利落。可以再次执行相同的健全性检查,这一次我们知道张量被移动到 GPU:

X_train.is_cuda**>>> True**

很好,但是模型声明呢?

我很高兴你问了。同样,这是一件非常简单的事情:

model = MyAwesomeNeuralNetwork()
model.to(device)

就这样,你现在可以开始训练了。简单回顾一下,这里有一个代码应该如何构建的总结:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
**X_train, X_test = X_train.to(device), X_test.to(device)
y_train, y_test = y_train.to(device), y_test.to(device)**class MyAwesomeNeuralNetwork(nn.Module):
    # your model heremodel = MyAwesomeNeuralNetwork()
**model.to(device)**# training code here

让我们继续使用数据加载器方法。

数据加载器方法

数据加载器方法在 CNN 中更常见,在这一节中,我们将了解如何将数据(图像)放在 GPU 上。第一步保持不变,因此你必须声明一个变量来保存我们正在训练的设备(CPU 或 GPU):

device = torch.device(**'**cuda**'** if torch.cuda.is_available() else **'**cpu**'**)
device**>>> device(type='cuda')**

现在,我们将声明我们的模型,并将其放在 GPU 上:

model = MyAwesomeNeuralNetwork()
model.to(device)

你可能已经注意到我们还没有在 GPU 上放置数据。直接传输数据加载器是不可能的,所以我们在这里必须更聪明一点。我们将在培训过程中传输图像,如下所示:

for epoch in range(epochs):
    for inputs, labels in train_loader:
        inputs, labels = inputs.to(device), labels.to(device)

因此,代码的整体结构应该如下所示:

class MyAwesomeNeuralNetwork(nn.Module):
    # your model heremodel = MyAwesomeNeuralNetwork()
**model.to(device)**epochs = 10
for epoch in range(epochs):
    for inputs, labels in train_loader:
        **inputs, labels = inputs.to(device), labels.to(device)**
        # backpropagation code here # evaluation
        with torch.no_grad():
            for inputs, labels in test_loader:
                **inputs, labels = inputs.to(device), labels.to(device)**
        # ...

而这就是你要做的一切——数据和模型都放在 GPU 上。

结论

这就是你想要的——两个步骤可以大大减少培训时间。起初,它看起来像是你需要执行的许多额外的步骤,但是一旦你掌握了它的要点,它就简单了。

我永远不会建议你在 CPU 上进行训练,感谢 Google Colab,你不必这样做——因为你可以免费使用 GPU 运行时。

感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

[## 通过我的推荐链接加入 Medium-Dario rade ci

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@radecicdario/membership)

py torch[表格]-二进制分类

原文:https://towardsdatascience.com/pytorch-tabular-binary-classification-a0368da5bb89?source=collection_archive---------1-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

这篇博文将带您了解使用 PyTorch 对表格数据进行二进制分类的实现。

我们将使用 Kaggle 上可用的下背部疼痛症状数据集。该数据集有 13 列,其中前 12 列是要素,最后一列是目标列。数据集有 300 行。

二元分类模因[图片[1]]

导入库

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

from sklearn.preprocessing import StandardScaler    
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report

读出数据

df = pd.read_csv("data/tabular/classification/spine_dataset.csv")df.head()

EDA 和预处理

阶级分布

这里存在阶级不平衡。虽然有很多方法可以解决阶级不平衡问题,但这超出了本文的范围。

sns.countplot(x = 'Class_att', data=df)

等级不平衡条形图[图片[2]]

编码输出类别

PyTorch 支持从 0 开始的标签。那就是【0,n】。我们需要从 0 开始重新映射我们的标签。

df['Class_att'] = df['Class_att'].astype('category')encode_map = {
    'Abnormal': 1,
    'Normal': 0
}

df['Class_att'].replace(encode_map, inplace=True)

创建输入和输出数据

最后一列是我们的输出。输入是除最后一列之外的所有列。这里我们使用 Pandas 库中的.iloc方法来选择我们的输入和输出列。

X = df.iloc[:, 0:-1]
y = df.iloc[:, -1]

列车测试分离

我们现在将数据分为训练集和测试集。我们选择了 33%的数据作为测试集。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=69)

标准化输入

为了正确训练神经网络,我们需要标准化输入值。我们通过去除平均值并缩放到单位方差来标准化特征。平均值为u且标准差为s的样本x的标准分数计算如下:

z = (x — u) / s

你可以在这里找到关于神经网络标准化/规范化的更多信息。

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

模型参数

为了训练我们的模型,我们需要设置一些超参数。请注意,这是一个非常简单的神经网络,因此,我们没有调整很多超参数。目标是了解 PyTorch 是如何工作的。

EPOCHS = 50
BATCH_SIZE = 64
LEARNING_RATE = 0.001

定义自定义数据加载器

这里我们定义了一个数据加载器。如果这对您来说是新的,我建议您阅读下面关于数据加载器的博客文章,然后再回来。

[## py torch[基础知识] —数据加载器和损失函数简介

这篇博文将带您了解 PyTorch 中的数据加载器和不同类型的损失函数。

towardsdatascience.com](/pytorch-basics-intro-to-dataloaders-and-loss-functions-868e86450047)

## train dataclass TrainData(Dataset):

    def __init__(self, X_data, y_data):
        self.X_data = X_data
        self.y_data = y_data

    def __getitem__(self, index):
        return self.X_data[index], self.y_data[index]

    def __len__ (self):
        return len(self.X_data)

train_data = TrainData(torch.FloatTensor(X_train), 
                       torch.FloatTensor(y_train)) ## test data class TestData(Dataset):

    def __init__(self, X_data):
        self.X_data = X_data

    def __getitem__(self, index):
        return self.X_data[index]

    def __len__ (self):
        return len(self.X_data)

test_data = TestData(torch.FloatTensor(X_test))

让我们初始化我们的数据加载器。我们将使用一个batch_size = 1作为测试数据加载器。

train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)test_loader = DataLoader(dataset=test_data, batch_size=1)

定义神经网络架构

这里,我们定义了一个具有批处理和丢失的 2 层前馈网络。

使用前馈网络的二元分类示例[图像[3] 信用点

在我们的__init__()函数中,我们定义我们想要使用的层,而在forward()函数中,我们调用已定义的层。

由于数据集中输入要素的数量为 12,因此第一个nn.Linear图层的输入将为 12。输出可以是你想要的任何数字。

您唯一需要确保的是一个图层的输出要素数量应等于下一个图层的输入要素数量。

在文档中阅读更多关于nn.Linear的信息。类似地,我们定义 ReLU、Dropout 和 BatchNorm 层。

一旦我们定义了所有这些层,就该使用它们了。在forward()函数中,我们将变量inputs作为我们的输入。我们通过我们初始化的不同层传递这个输入。

forward()函数的第一行接受输入,通过我们的第一个线性层,然后在其上应用 ReLU 激活。然后我们对输出应用 BatchNorm。看看下面的代码可以更好地理解它。

注意,我们在训练中没有在最后一层中使用s 形激活。这是因为,我们使用了自动应用 Sigmoid 激活的nn.BCEWithLogitsLoss()损失函数。

class BinaryClassification(nn.Module):
    def __init__(self):
        super(BinaryClassification, self).__init__() # Number of input features is 12.
        self.layer_1 = nn.Linear(12, 64) 
        self.layer_2 = nn.Linear(64, 64)
        self.layer_out = nn.Linear(64, 1) 

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(p=0.1)
        self.batchnorm1 = nn.BatchNorm1d(64)
        self.batchnorm2 = nn.BatchNorm1d(64)

    def forward(self, inputs):
        x = self.relu(self.layer_1(inputs))
        x = self.batchnorm1(x)
        x = self.relu(self.layer_2(x))
        x = self.batchnorm2(x)
        x = self.dropout(x)
        x = self.layer_out(x)

        return x

一旦我们定义了我们的架构,我们检查我们的 GPU 是否是活动的。PyTorch 的神奇之处在于它使用 GPU 超级简单。

变量device要么会说cuda:0如果我们有 GPU 的话。如果没有,它会说cpu。即使您没有 GPU,也可以在不修改代码的情况下遵循本教程。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print(device) ###################### OUTPUT ######################cuda:0

接下来,我们需要初始化我们的模型。初始化后,我们将其移动到device。现在,这个设备是一个 GPU,如果你有,或者它是 CPU,如果你没有。我们使用的网络相当小。所以,在 CPU 上训练不会花很多时间。

在这之后,我们初始化优化器并决定使用哪个损失函数。

model = BinaryClassification()
model.to(device)print(model)criterion = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE) ###################### OUTPUT ######################BinaryClassification(
  (layer_1): Linear(in_features=12, out_features=64, bias=True)
  (layer_2): Linear(in_features=64, out_features=64, bias=True)
  (layer_out): Linear(in_features=64, out_features=1, bias=True)
  (relu): ReLU()
  (dropout): Dropout(p=0.1, inplace=False)
  (batchnorm1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (batchnorm2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)

二元分类模因[图片[4]]

训练模型

在开始实际训练之前,让我们定义一个函数来计算精度。

在下面的函数中,我们将预测和实际输出作为输入。预测值(概率)被四舍五入,以将其转换为 01

一旦完成,我们只需将我们预测的 1/0 的数量与实际存在的 1/0 的数量进行比较,并计算准确度。

请注意,输入y_predy_test是针对一个批次的。我们的batch_size是 64。所以,这个精度是一次为 64 个预测(张量)计算的。

def binary_acc(y_pred, y_test):
    y_pred_tag = torch.round(torch.sigmoid(y_pred))

    correct_results_sum = (y_pred_tag == y_test).sum().float()
    acc = correct_results_sum/y_test.shape[0]
    acc = torch.round(acc * 100)

    return acc

我们期待已久的时刻到来了。让我们训练我们的模型。

你可以看到我们在循环之前放了一个model.train()model.train()告诉 PyTorch 你正处于训练模式。

为什么我们需要这么做?如果您使用的层(如 Dropout 或 BatchNorm)在训练和评估期间表现不同,您需要告诉 PyTorch 相应地采取行动。而 PyTorch 中的默认模式是火车,所以,您不必显式地编写它。但这是很好的练习。

同样,当我们测试我们的模型时,我们将调用model.eval()。我们将在下面看到。

回到训练;我们开始一个循环。在这个 for 循环的顶部,我们将每个历元的损失和精度初始化为 0。在每个时期之后,我们将打印出损失/精度并将其重置回 0。

然后我们有另一个 for 循环。这个 for 循环用于从train_loader中批量获取我们的数据。

在我们做任何预测之前,我们先做optimizer.zero_grad()。由于backward()函数累加梯度,我们需要为每个小批量手动将其设置为 0。

从我们定义的模型中,我们获得一个预测,得到小批量的损失(和精度),使用loss.backward()optimizer.step()进行反向传播。最后,我们将所有小批量损失(和精度)相加,以获得该时期的平均损失(和精度)。

该损耗和精度在外部for循环中打印出来。

model.train()
for e in range(1, EPOCHS+1):
    epoch_loss = 0
    epoch_acc = 0
    for X_batch, y_batch in train_loader:
        X_batch, y_batch = X_batch.to(device), y_batch.to(device)
        optimizer.zero_grad()

        y_pred = model(X_batch)

        loss = criterion(y_pred, y_batch.unsqueeze(1))
        acc = binary_acc(y_pred, y_batch.unsqueeze(1))

        loss.backward()
        optimizer.step()

        epoch_loss += loss.item()
        epoch_acc += acc.item()

    print(f'Epoch {e+0:03}: | Loss: {epoch_loss/len(train_loader):.5f} | Acc: {epoch_acc/len(train_loader):.3f}') ###################### OUTPUT ######################Epoch 001: | Loss: 0.04027 | Acc: 98.250
Epoch 002: | Loss: 0.12023 | Acc: 96.750
Epoch 003: | Loss: 0.02067 | Acc: 99.500
Epoch 004: | Loss: 0.07329 | Acc: 96.250
Epoch 005: | Loss: 0.04676 | Acc: 99.250
Epoch 006: | Loss: 0.03005 | Acc: 99.500
Epoch 007: | Loss: 0.05777 | Acc: 98.250
Epoch 008: | Loss: 0.03446 | Acc: 99.500
Epoch 009: | Loss: 0.03443 | Acc: 100.000
Epoch 010: | Loss: 0.03368 | Acc: 100.000
Epoch 011: | Loss: 0.02395 | Acc: 100.000
Epoch 012: | Loss: 0.05094 | Acc: 98.250
Epoch 013: | Loss: 0.03618 | Acc: 98.250
Epoch 014: | Loss: 0.02143 | Acc: 100.000
Epoch 015: | Loss: 0.02730 | Acc: 99.500
Epoch 016: | Loss: 0.02323 | Acc: 100.000
Epoch 017: | Loss: 0.03395 | Acc: 98.250
Epoch 018: | Loss: 0.08600 | Acc: 96.750
Epoch 019: | Loss: 0.02394 | Acc: 100.000
Epoch 020: | Loss: 0.02363 | Acc: 100.000
Epoch 021: | Loss: 0.01660 | Acc: 100.000
Epoch 022: | Loss: 0.05766 | Acc: 96.750
Epoch 023: | Loss: 0.02115 | Acc: 100.000
Epoch 024: | Loss: 0.01331 | Acc: 100.000
Epoch 025: | Loss: 0.01504 | Acc: 100.000
Epoch 026: | Loss: 0.01727 | Acc: 100.000
Epoch 027: | Loss: 0.02128 | Acc: 100.000
Epoch 028: | Loss: 0.01106 | Acc: 100.000
Epoch 029: | Loss: 0.05802 | Acc: 98.250
Epoch 030: | Loss: 0.01275 | Acc: 100.000
Epoch 031: | Loss: 0.01272 | Acc: 100.000
Epoch 032: | Loss: 0.01949 | Acc: 100.000
Epoch 033: | Loss: 0.02848 | Acc: 100.000
Epoch 034: | Loss: 0.01514 | Acc: 100.000
Epoch 035: | Loss: 0.02949 | Acc: 100.000
Epoch 036: | Loss: 0.00895 | Acc: 100.000
Epoch 037: | Loss: 0.01692 | Acc: 100.000
Epoch 038: | Loss: 0.01678 | Acc: 100.000
Epoch 039: | Loss: 0.02755 | Acc: 100.000
Epoch 040: | Loss: 0.02021 | Acc: 100.000
Epoch 041: | Loss: 0.07972 | Acc: 98.250
Epoch 042: | Loss: 0.01421 | Acc: 100.000
Epoch 043: | Loss: 0.01558 | Acc: 100.000
Epoch 044: | Loss: 0.01185 | Acc: 100.000
Epoch 045: | Loss: 0.01830 | Acc: 100.000
Epoch 046: | Loss: 0.01367 | Acc: 100.000
Epoch 047: | Loss: 0.00880 | Acc: 100.000
Epoch 048: | Loss: 0.01046 | Acc: 100.000
Epoch 049: | Loss: 0.00933 | Acc: 100.000
Epoch 050: | Loss: 0.11034 | Acc: 98.250

测试模型

训练完成后,我们需要测试我们的模型进展如何。注意,在运行测试代码之前,我们已经使用了model.eval()。为了告诉 PyTorch 我们不希望在推断过程中执行反向传播,我们使用了torch.no_grad()来减少内存使用并加快计算速度。

我们首先定义一个包含我们预测的列表。然后我们使用test_loader循环遍历我们的批处理。对于每一批—

  • 我们使用训练好的模型进行预测。
  • 将概率四舍五入为 1 或 0。
  • 将批处理从 CPU 移动到 GPU。
  • 将张量转换为 numpy 对象,并将其添加到我们的列表中。
  • 将列表展平,这样我们可以将它用作confusion_matrixclassification_report的输入。
y_pred_list = []model.eval()
with torch.no_grad():
    for X_batch in test_loader:
        X_batch = X_batch.to(device)
        y_test_pred = model(X_batch)
        y_test_pred = torch.sigmoid(y_test_pred)
        y_pred_tag = torch.round(y_test_pred)
        y_pred_list.append(y_pred_tag.cpu().numpy()) y_pred_list = [a.squeeze().tolist() for a in y_pred_list]

混淆矩阵

一旦我们有了所有的预测,我们使用 scikit-learn 的confusion_matrix()函数来计算混淆矩阵。

confusion_matrix(y_test, y_pred_list) ###################### OUTPUT ######################array([[23,  8],
       [12, 60]])

分类报告

为了获得具有精确度、召回率和 F1 分数的分类报告,我们使用函数classification_report

print(classification_report(y_test, y_pred_list)) ###################### OUTPUT ######################precision    recall  f1-score   support 0       0.66      0.74      0.70        31
           1       0.88      0.83      0.86        72 accuracy                           0.81       103
   macro avg       0.77      0.79      0.78       103
weighted avg       0.81      0.81      0.81       103

感谢您的阅读。欢迎提出建议和建设性的批评。😃

这篇博客是“如何训练你的神经网络”系列的一部分。你可以在这里找到系列。

你可以在 LinkedIn 和 Twitter 上找到我。如果你喜欢这个,看看我的其他博客。

py torch[表格]-多类分类

原文:https://towardsdatascience.com/pytorch-tabular-multiclass-classification-9f8211a123ab?source=collection_archive---------1-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

这篇博文将带您了解使用 PyTorch 对表格数据进行多类分类的实现。

我们将使用 Kaggle 上的葡萄酒数据集。该数据集有 12 列,其中前 11 列是要素,最后一列是目标列。数据集有 1599 行。

导入库

我们使用tqdm来启用训练和测试循环的进度条。

import numpy as np
import pandas as pd
import seaborn as sns
from tqdm.notebook import tqdm
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader, WeightedRandomSampler

from sklearn.preprocessing import MinMaxScaler    
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report

读出数据

df = pd.read_csv("data/tabular/classification/winequality-red.csv")df.head()

输入数据[图像[2]]

EDA 和预处理

为了使数据适合神经网络,我们需要对其进行一些调整。

阶级分布

首先,我们绘制输出行来观察类分布。这里有很多不平衡。类别 3、4 和 8 的样本数量很少。

sns.countplot(x = 'quality', data=df)

类别分布条形图[图像[3]]

编码输出类别

接下来,我们看到输出标签从 3 到 8。这需要改变,因为 PyTorch 支持从 0 开始的标签。也就是【0,n】。我们需要从 0 开始重新映射我们的标签。

为此,让我们创建一个名为class2idx的字典,并使用 Pandas 库中的.replace()方法来修改它。让我们也创建一个名为idx2class的反向映射,它将 id 转换回它们原来的类。

为了创建反向映射,我们创建一个字典理解并简单地反转键和值。

class2idx = {
    3:0,
    4:1,
    5:2,
    6:3,
    7:4,
    8:5
}

idx2class = {v: k for k, v in class2idx.items()}

df['quality'].replace(class2idx, inplace=True)

创建输入和输出数据

为了使用来自 Sklearn 的train_test_split将我们的数据分成训练、验证和测试集,我们需要分离出我们的输入和输出。

输入X是除最后一列之外的所有列。输出y是最后一列。

X = df.iloc[:, 0:-1]
y = df.iloc[:, -1]

培训-验证-测试

为了创建 train-val-test 分割,我们将使用来自 Sklearn 的train_test_split()

首先,我们将把我们的数据分成 train+val 和 test 集。然后,我们将进一步分割我们的 train+val 集合来创建我们的 train 和 val 集合。

因为存在类不平衡,所以我们希望在我们的训练、验证和测试集中,所有输出类的分布是均等的。为此,我们使用函数train_test_split()中的stratify选项。

# Split into train+val and test
X_trainval, X_test, y_trainval, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=69)

# Split train into train-val
X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval, test_size=0.1, stratify=y_trainval, random_state=21)

标准化输入

神经网络需要介于(0,1)范围内的数据。网上有很多关于我们为什么需要这么做的资料。

为了衡量我们的价值,我们将使用 Sklearn 中的MinMaxScaler()MinMaxScaler通过将每个特征缩放到给定的范围(在我们的例子中是(0,1 ))来转换特征。

x _ scaled =(x-min(x))/(max(x)-min(x))

注意,我们在X_train上使用.fit_transform(),而在X_valX_test上使用.transform()

我们这样做是因为我们希望使用与训练集相同的参数来扩展验证和测试集,以避免数据泄漏。fit_transform计算并应用缩放值,而.transform仅应用计算值。

scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_val = scaler.transform(X_val)
X_test = scaler.transform(X_test)X_train, y_train = np.array(X_train), np.array(y_train)
X_val, y_val = np.array(X_val), np.array(y_val)
X_test, y_test = np.array(X_test), np.array(y_test)

可视化培训、评估和测试中的类别分布

一旦我们将数据分成训练集、验证集和测试集,让我们确保类在所有三个集中的分布是相等的。

为此,让我们创建一个名为get_class_distribution()的函数。该函数将 obj y即。y_trainy_valy_test。在函数内部,我们初始化一个字典,其中包含作为键的输出类和作为值的输出类计数。计数都被初始化为 0。

然后我们循环遍历我们的y对象并更新我们的字典。

def get_class_distribution(obj):
    count_dict = {
        "rating_3": 0,
        "rating_4": 0,
        "rating_5": 0,
        "rating_6": 0,
        "rating_7": 0,
        "rating_8": 0,
    }

    for i in obj:
        if i == 0: 
            count_dict['rating_3'] += 1
        elif i == 1: 
            count_dict['rating_4'] += 1
        elif i == 2: 
            count_dict['rating_5'] += 1
        elif i == 3: 
            count_dict['rating_6'] += 1
        elif i == 4: 
            count_dict['rating_7'] += 1  
        elif i == 5: 
            count_dict['rating_8'] += 1              
        else:
            print("Check classes.")

    return count_dict

一旦我们有了字典计数,我们就使用 Seaborn 库来绘制条形图。为了进行绘图,我们首先使用pd.DataFrame.from_dict([get_class_distribution(y_train)])将字典转换成数据帧。随后,我们.melt()将数据帧转换成长格式,最后使用sns.barplot()构建绘图。

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(25,7))# Train
sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(y_train)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[0]).set_title('Class Distribution in Train Set')# Validation
sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(y_val)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[1]).set_title('Class Distribution in Val Set')# Test
sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(y_test)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[2]).set_title('Class Distribution in Test Set')

训练集、val 集和测试集中的类分布[图片[4]]

神经网络

我们现在达到了我们一直在等待的目标!

自定义数据集

首先,让我们定义一个自定义数据集。数据加载器将使用该数据集将我们的数据传递到模型中。

我们通过传递 X 和 y 作为输入来初始化数据集。确保 X 是float而 y 是long

class ClassifierDataset(Dataset):

    def __init__(self, X_data, y_data):
        self.X_data = X_data
        self.y_data = y_data

    def __getitem__(self, index):
        return self.X_data[index], self.y_data[index]

    def __len__ (self):
        return len(self.X_data)

train_dataset = ClassifierDataset(torch.from_numpy(X_train).float(), torch.from_numpy(y_train).long())val_dataset = ClassifierDataset(torch.from_numpy(X_val).float(), torch.from_numpy(y_val).long())test_dataset = ClassifierDataset(torch.from_numpy(X_test).float(), torch.from_numpy(y_test).long())

加权抽样

因为存在类别不平衡,所以我们使用分层分割来创建我们的训练、验证和测试集。

虽然它有所帮助,但它仍然不能确保我们模型的每个小批量都能看到我们的所有类。我们需要对值数量较少的类进行过采样。为此,我们使用WeightedRandomSampler

首先,我们获得一个名为target_list的列表,其中包含我们所有的输出。这个列表然后被转换成张量。

target_list = []for _, t in train_dataset:
    target_list.append(t)

target_list = torch.tensor(target_list)

然后,我们获得训练集中所有类的计数。我们使用每个计数的倒数来获得它的重量。现在我们已经计算了每个类的权重,我们可以继续了。

class_count = [i for i in get_class_distribution(y_train).values()]
class_weights = 1./torch.tensor(class_count, dtype=torch.float) print(class_weights) ###################### OUTPUT ######################tensor([0.1429, 0.0263, 0.0020, 0.0022, 0.0070, 0.0714])

WeightedRandomSampler期望每个样品有一个重量。我们使用下面的方法来实现。

class_weights_all = class_weights[target_list]

最后,让我们初始化我们的WeightedRandomSampler。在下面的数据加载器中,我们称之为。

weighted_sampler = WeightedRandomSampler(
    weights=class_weights_all,
    num_samples=len(class_weights_all),
    replacement=True
)

模型参数

在我们继续下一步之前,让我们定义几个我们将使用的参数。

EPOCHS = 300
BATCH_SIZE = 16
LEARNING_RATE = 0.0007NUM_FEATURES = len(X.columns)
NUM_CLASSES = 6

数据加载器

现在让我们初始化我们的数据加载器。

对于train_dataloader ,我们将使用batch_size = 64,并将我们的采样器传递给它。注意,我们没有在train_dataloader中使用shuffle=True,因为我们已经使用了一个采样器。这两个是互斥的。

对于test_dataloaderval_dataloader,我们将使用batch_size = 1

train_loader = DataLoader(dataset=train_dataset,
                          batch_size=BATCH_SIZE,
                          sampler=weighted_sampler
)val_loader = DataLoader(dataset=val_dataset, batch_size=1)test_loader = DataLoader(dataset=test_dataset, batch_size=1)

定义神经网络架构

让我们定义一个简单的三层前馈网络,具有漏失和批量范数。

class MulticlassClassification(nn.Module):
    def __init__(self, num_feature, num_class):
        super(MulticlassClassification, self).__init__()

        self.layer_1 = nn.Linear(num_feature, 512)
        self.layer_2 = nn.Linear(512, 128)
        self.layer_3 = nn.Linear(128, 64)
        self.layer_out = nn.Linear(64, num_class) 

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(p=0.2)
        self.batchnorm1 = nn.BatchNorm1d(512)
        self.batchnorm2 = nn.BatchNorm1d(128)
        self.batchnorm3 = nn.BatchNorm1d(64)

    def forward(self, x):
        x = self.layer_1(x)
        x = self.batchnorm1(x)
        x = self.relu(x)

        x = self.layer_2(x)
        x = self.batchnorm2(x)
        x = self.relu(x)
        x = self.dropout(x)

        x = self.layer_3(x)
        x = self.batchnorm3(x)
        x = self.relu(x)
        x = self.dropout(x)

        x = self.layer_out(x)

        return x

检查 GPU 是否处于活动状态。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print(device) ###################### OUTPUT ######################cuda:0

初始化模型、优化器和损失函数。将模型传输到 GPU。我们使用nn.CrossEntropyLoss是因为这是一个多类分类问题。我们不需要在最后一层之后手动添加一个log_softmax层,因为nn.CrossEntropyLoss已经为我们做了。然而,我们需要应用log_softmax进行验证和测试。

损失函数 meme [Image [5]]

model = MulticlassClassification(num_feature = NUM_FEATURES, num_class=NUM_CLASSES)model.to(device)

criterion = nn.CrossEntropyLoss(weight=class_weights.to(device))
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)print(model) ###################### OUTPUT ######################MulticlassClassification(
  (layer_1): Linear(in_features=11, out_features=512, bias=True)
  (layer_2): Linear(in_features=512, out_features=128, bias=True)
  (layer_3): Linear(in_features=128, out_features=64, bias=True)
  (layer_out): Linear(in_features=64, out_features=6, bias=True)
  (relu): ReLU()
  (dropout): Dropout(p=0.2, inplace=False)
  (batchnorm1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (batchnorm2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (batchnorm3): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)

训练模型

在我们开始训练之前,让我们定义一个函数来计算每个历元的精度。

该函数将y_predy_test作为输入参数。然后,我们将log_softmax应用于y_pred,并提取出具有较高概率的类别。

之后,我们比较预测类别和实际类别来计算准确度。

def multi_acc(y_pred, y_test):
    y_pred_softmax = torch.log_softmax(y_pred, dim = 1)
    _, y_pred_tags = torch.max(y_pred_softmax, dim = 1)    

    correct_pred = (y_pred_tags == y_test).float()
    acc = correct_pred.sum() / len(correct_pred)

    acc = torch.round(acc * 100)

    return acc

我们还将定义 2 个字典,用于存储训练集和验证集的准确度/时期和损失/时期。

accuracy_stats = {
    'train': [],
    "val": []
}loss_stats = {
    'train': [],
    "val": []
}

让我们来看看我们的模型吧!

训练迷因[图片[6]]

print("Begin training.")for e in tqdm(range(1, EPOCHS+1)):

    # TRAINING
    train_epoch_loss = 0
    train_epoch_acc = 0model.train()
    for X_train_batch, y_train_batch in train_loader:
        X_train_batch, y_train_batch = X_train_batch.to(device), y_train_batch.to(device)
        optimizer.zero_grad()

        y_train_pred = model(X_train_batch)

        train_loss = criterion(y_train_pred, y_train_batch)
        train_acc = multi_acc(y_train_pred, y_train_batch)

        train_loss.backward()
        optimizer.step()

        train_epoch_loss += train_loss.item()
        train_epoch_acc += train_acc.item()

    # VALIDATION    
    with torch.no_grad():

        val_epoch_loss = 0
        val_epoch_acc = 0

        model.eval()
        for X_val_batch, y_val_batch in val_loader:
            X_val_batch, y_val_batch = X_val_batch.to(device), y_val_batch.to(device)

            y_val_pred = model(X_val_batch)

            val_loss = criterion(y_val_pred, y_val_batch)
            val_acc = multi_acc(y_val_pred, y_val_batch)

            val_epoch_loss += val_loss.item()
            val_epoch_acc += val_acc.item()loss_stats['train'].append(train_epoch_loss/len(train_loader))
    loss_stats['val'].append(val_epoch_loss/len(val_loader))
    accuracy_stats['train'].append(train_epoch_acc/len(train_loader))
    accuracy_stats['val'].append(val_epoch_acc/len(val_loader))

    print(f'Epoch {e+0:03}: | Train Loss: {train_epoch_loss/len(train_loader):.5f} | Val Loss: {val_epoch_loss/len(val_loader):.5f} | Train Acc: {train_epoch_acc/len(train_loader):.3f}| Val Acc: {val_epoch_acc/len(val_loader):.3f}') ###################### OUTPUT ######################Epoch 001: | Train Loss: 1.38551 | Val Loss: 1.42033 | Train Acc: 38.889| Val Acc: 43.750Epoch 002: | Train Loss: 1.19558 | Val Loss: 1.36613 | Train Acc: 59.722| Val Acc: 45.312Epoch 003: | Train Loss: 1.12264 | Val Loss: 1.44156 | Train Acc: 79.167| Val Acc: 35.938.
.
.Epoch 299: | Train Loss: 0.29774 | Val Loss: 1.42116 | Train Acc: 100.000| Val Acc: 57.812Epoch 300: | Train Loss: 0.33134 | Val Loss: 1.38818 | Train Acc: 100.000| Val Acc: 57.812

你可以看到我们已经在循环之前放置了一个model.train()model.train()告诉 PyTorch 你正处于训练模式。

为什么我们需要这么做?如果您使用在训练和评估期间表现不同的层,如DropoutBatchNorm(例如;评估期间不使用 dropout),您需要告诉 PyTorch 采取相应的行动。

同样,当我们测试我们的模型时,我们将调用model.eval()。我们将在下面看到。

回到训练;我们开始一个循环。在这个 for 循环的顶部,我们将每个历元的损耗和精度初始化为 0。在每个时期之后,我们将打印出损失/精度并将其重置回 0。

然后我们有另一个 for-loop 。这个 for 循环用于从train_loader批量获取我们的数据。

在我们做任何预测之前,我们做optimizer.zero_grad()。由于backward()函数累加梯度,我们需要为每个小批量手动将其设置为 0。

从我们定义的模型中,我们获得一个预测,得到小批量的损失(和精度),使用loss.backward()optimizer.step()执行反向传播。

最后,我们将所有小批量损失(和精度)相加,以获得该时期的平均损失(和精度)。我们将每个小批量的所有损耗/精度相加,最后除以小批量的数量,即。train_loader的长度,以获得每个历元的平均损失/精度。

我们遵循的训练程序与验证程序完全相同,除了我们用torch.no_grad将它包装起来,并且不执行任何反向传播。torch.no_grad()告诉 PyTorch 我们不想执行反向传播,这样可以减少内存使用并加快计算速度。

可视化损失和准确性

为了绘制损耗和精度线图,我们再次从accuracy_statsloss_stats字典中创建一个数据帧。

# Create dataframes
train_val_acc_df = pd.DataFrame.from_dict(accuracy_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"})train_val_loss_df = pd.DataFrame.from_dict(loss_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"})# Plot the dataframes
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(20,7))sns.lineplot(data=train_val_acc_df, x = "epochs", y="value", hue="variable",  ax=axes[0]).set_title('Train-Val Accuracy/Epoch')sns.lineplot(data=train_val_loss_df, x = "epochs", y="value", hue="variable", ax=axes[1]).set_title('Train-Val Loss/Epoch')

损失和精度图[图 7]]

测试模型

训练完成后,我们需要测试我们的模型进展如何。注意,在运行测试代码之前,我们已经使用了model.eval()。为了告诉 PyTorch 我们不希望在推断过程中执行反向传播,我们使用了torch.no_grad(),就像我们对上面的验证循环所做的那样。

我们首先定义一个包含我们预测的列表。然后我们使用test_loader循环遍历我们的批处理。对于每一批—

  • 我们将输入小批量数据转移到 GPU。
  • 我们使用训练好的模型进行预测。
  • log_softmax激活应用于预测,并选择概率最高的指数。
  • 将批处理从 CPU 移动到 GPU。
  • 将张量转换为 numpy 对象,并将其添加到我们的列表中。
  • 将列表展平,这样我们可以将它用作confusion_matrixclassification_report的输入。
y_pred_list = []with torch.no_grad():
    model.eval()
    for X_batch, _ in test_loader:
        X_batch = X_batch.to(device)
        y_test_pred = model(X_batch)
        _, y_pred_tags = torch.max(y_test_pred, dim = 1)
        y_pred_list.append(y_pred_tags.cpu().numpy())y_pred_list = [a.squeeze().tolist() for a in y_pred_list]

混淆矩阵

我们从混淆矩阵中创建一个数据框架,并使用 seaborn 库将其绘制为热图。

confusion_matrix_df = pd.DataFrame(confusion_matrix(y_test, y_pred_list)).rename(columns=idx2class, index=idx2class) sns.heatmap(confusion_matrix_df, annot=True)

混淆矩阵[图片[8]]

分类报告

最后,我们打印出包含精确度、召回率和 F1 分数的分类报告。

print(classification_report(y_test, y_pred_list)) ###################### OUTPUT ######################precision    recall  f1-score   support

           0       0.00      0.00      0.00         2
           1       0.14      0.27      0.19        11
           2       0.70      0.65      0.67       136
           3       0.63      0.57      0.60       128
           4       0.49      0.60      0.54        40
           5       0.00      0.00      0.00         3

    accuracy                           0.59       320
   macro avg       0.33      0.35      0.33       320
weighted avg       0.62      0.59      0.60       320

感谢您的阅读。欢迎提出建议和建设性的批评。😃

这篇博客是“如何训练你的神经网络”系列的一部分。你可以在这里找到系列。

你可以在 LinkedIn 和 Twitter 上找到我。如果你喜欢这个,看看我的其他博客。

py torch[表格]-回归

原文:https://towardsdatascience.com/pytorch-tabular-regression-428e9c9ac93?source=collection_archive---------6-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

这篇博文将带您了解如何使用 PyTorch 实现对表格数据的回归。

我们将使用 Kaggle 上的红酒质量数据集。该数据集有 12 列,其中前 11 列是要素,最后一列是目标列。数据集有 1599 行。

导入库

我们使用tqdm来启用训练和测试循环的进度条。

import numpy as np
import pandas as pd
import seaborn as sns
from tqdm.notebook import tqdm
import matplotlib.pyplot as pltimport torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoaderfrom sklearn.preprocessing import MinMaxScaler    
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

读出数据

df = pd.read_csv("data/tabular/classification/winequality-red.csv")df.head()

输入数据帧[图像[2]]

EDA 和预处理

首先,我们绘制输出行来观察类分布。这里有很多不平衡。类别 3、4 和 8 的样本数量很少。

这里我们不会将输出变量视为类,因为我们正在执行回归。我们将把输出列,也就是所有的integers,转换成float值。

sns.countplot(x = 'quality', data=df)

输出分布[图像[3]]

创建输入和输出数据

为了将数据分成训练集、验证集和测试集,我们需要将输入和输出分开。

输入X是除最后一列之外的所有列。输出y是最后一列。

X = df.iloc[:, 0:-1]
y = df.iloc[:, -1]

培训-验证-测试

为了创建 train-val-test 分割,我们将使用 Sklearn 的train_test_split()

首先,我们将数据分成train+valtest组。然后,我们将进一步分割我们的train+val集合,以创建我们的trainval集合。

因为存在“类”的不平衡,所以我们希望在我们的训练、验证和测试集中,所有输出类的分布是均等的。

为此,我们使用函数train_test_split()中的stratify选项。

记住分层只对类有效,对数字无效。因此,一般来说,我们可以使用四分位数、十分位数、直方图(np.histogram())等将我们的数字分类。因此,您必须创建一个包含输出及其“类”的新数据帧。这个“类”是使用上述方法获得的。

在我们的例子中,让我们按原样使用这些数字,因为它们已经和类一样了。拆分数据后,我们可以将输出转换为 float(因为回归)。

# Train - Test
X_trainval, X_test, y_trainval, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=69)# Split train into train-val
X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval, test_size=0.1, stratify=y_trainval, random_state=21)

标准化输入

神经网络需要介于(0,1)范围内的数据。网上有很多关于我们为什么需要这么做的资料。

为了衡量我们的价值,我们将使用 Sklearn 中的MinMaxScaler()MinMaxScaler通过将每个特征缩放到给定的范围(在我们的例子中是(0,1 ))来转换特征。

x _ scaled =(x-min(x))/(max(x)-min(x))

注意,我们在X_train上使用.fit_transform(),而在X_valX_test上使用.transform()

我们这样做是因为我们希望使用与训练集相同的参数来扩展验证和测试集,以避免数据泄漏。fit_transform()计算并应用缩放值,而.transform()仅应用计算值。

scaler = MinMaxScaler()X_train = scaler.fit_transform(X_train)
X_val = scaler.transform(X_val)
X_test = scaler.transform(X_test)X_train, y_train = np.array(X_train), np.array(y_train)
X_val, y_val = np.array(X_val), np.array(y_val)
X_test, y_test = np.array(X_test), np.array(y_test)

可视化培训、评估和测试中的类别分布

一旦我们将数据分成训练集、验证集和测试集,让我们确保类在所有三个集中的分布是相等的。

为此,让我们创建一个名为get_class_distribution()的函数。该函数将 obj y即。y_trainy_valy_test。在函数内部,我们初始化一个字典,其中包含作为键的输出类和作为值的输出类计数。计数都被初始化为 0。

然后我们循环遍历我们的y对象并更新我们的字典。

def get_class_distribution(obj):
    count_dict = {
        "rating_3": 0,
        "rating_4": 0,
        "rating_5": 0,
        "rating_6": 0,
        "rating_7": 0,
        "rating_8": 0,
    }

    for i in obj:
        if i == 3: 
            count_dict['rating_3'] += 1
        elif i == 4: 
            count_dict['rating_4'] += 1
        elif i == 5: 
            count_dict['rating_5'] += 1
        elif i == 6: 
            count_dict['rating_6'] += 1
        elif i == 7: 
            count_dict['rating_7'] += 1  
        elif i == 8: 
            count_dict['rating_8'] += 1              
        else:
            print("Check classes.")

    return count_dict

一旦我们有了字典计数,我们就使用 Seaborn 库来绘制条形图。

为了进行绘图,我们首先使用pd.DataFrame.from_dict([get_class_distribution(y_train)])将字典转换成数据帧。

随后,我们将数据帧转换成格式,最后使用格式来构建图表。

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(25,7))# Train
sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(y_train)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[0]).set_title('Class Distribution in Train Set')# Val
sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(y_val)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[1]).set_title('Class Distribution in Val Set')# Test
sns.barplot(data = pd.DataFrame.from_dict([get_class_distribution(y_test)]).melt(), x = "variable", y="value", hue="variable",  ax=axes[2]).set_title('Class Distribution in Test Set')

训练值测试分割后的输出分布[图片[4]]

将输出变量转换为Float

y_train, y_test, y_val = y_train.astype(float), y_test.astype(float), y_val.astype(float)

神经网络

初始化数据集

class RegressionDataset(Dataset):

    def __init__(self, X_data, y_data):
        self.X_data = X_data
        self.y_data = y_data

    def __getitem__(self, index):
        return self.X_data[index], self.y_data[index]

    def __len__ (self):
        return len(self.X_data)train_dataset = RegressionDataset(torch.from_numpy(X_train).float(), torch.from_numpy(y_train).float())val_dataset = RegressionDataset(torch.from_numpy(X_val).float(), torch.from_numpy(y_val).float())test_dataset = RegressionDataset(torch.from_numpy(X_test).float(), torch.from_numpy(y_test).float())

模型参数

EPOCHS = 150
BATCH_SIZE = 64
LEARNING_RATE = 0.001NUM_FEATURES = len(X.columns)

初始化数据加载器

train_loader = DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE, shuffle=True)val_loader = DataLoader(dataset=val_dataset, batch_size=1)test_loader = DataLoader(dataset=test_dataset, batch_size=1)

定义神经网络架构

我们这里有一个简单的 3 层前馈神经网络。我们使用ReLU作为所有层的激活。

class MultipleRegression(nn.Module):
    def __init__(self, num_features):
        super(MultipleRegression, self).__init__()

        self.layer_1 = nn.Linear(num_features, 16)
        self.layer_2 = nn.Linear(16, 32)
        self.layer_3 = nn.Linear(32, 16)
        self.layer_out = nn.Linear(16, 1)

        self.relu = nn.ReLU()def forward(self, inputs):
        x = self.relu(self.layer_1(inputs))
        x = self.relu(self.layer_2(x))
        x = self.relu(self.layer_3(x))
        x = self.layer_out(x)return (x)def predict(self, test_inputs):
        x = self.relu(self.layer_1(test_inputs))
        x = self.relu(self.layer_2(x))
        x = self.relu(self.layer_3(x))
        x = self.layer_out(x)return (x)

检查 GPU

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print(device) ###################### OUTPUT ######################cuda:0

初始化模型、优化器和损失函数。将模型传输到 GPU。

我们使用均方误差损失。

model = MultipleRegression(NUM_FEATURES)
model.to(device)print(model)criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE) ###################### OUTPUT ######################MultipleRegression(
  (layer_1): Linear(in_features=11, out_features=16, bias=True)
  (layer_2): Linear(in_features=16, out_features=32, bias=True)
  (layer_3): Linear(in_features=32, out_features=16, bias=True)
  (layer_out): Linear(in_features=16, out_features=1, bias=True)
  (relu): ReLU()
)

火车模型

在我们开始训练之前,让我们定义一个字典,它将存储训练集和验证集的丢失/时期。

loss_stats = {
    'train': [],
    "val": []
}

开始训练吧。

print("Begin training.")for e in tqdm(range(1, EPOCHS+1)):

    # TRAINING
    train_epoch_loss = 0model.train()
    for X_train_batch, y_train_batch in train_loader:
        X_train_batch, y_train_batch = X_train_batch.to(device), y_train_batch.to(device)
        optimizer.zero_grad()

        y_train_pred = model(X_train_batch)

        train_loss = criterion(y_train_pred, y_train_batch.unsqueeze(1))

        train_loss.backward()
        optimizer.step()

        train_epoch_loss += train_loss.item()

    # VALIDATION    
    with torch.no_grad():

        val_epoch_loss = 0

        model.eval()
        for X_val_batch, y_val_batch in val_loader:
            X_val_batch, y_val_batch = X_val_batch.to(device), y_val_batch.to(device)

            y_val_pred = model(X_val_batch)

            val_loss = criterion(y_val_pred, y_val_batch.unsqueeze(1))

            val_epoch_loss += val_loss.item()loss_stats['train'].append(train_epoch_loss/len(train_loader))
    loss_stats['val'].append(val_epoch_loss/len(val_loader))                              

    print(f'Epoch {e+0:03}: | Train Loss: {train_epoch_loss/len(train_loader):.5f} | Val Loss: {val_epoch_loss/len(val_loader):.5f}')###################### OUTPUT ######################Epoch 001: | Train Loss: 31.22514 | Val Loss: 30.50931Epoch 002: | Train Loss: 30.02529 | Val Loss: 28.97327.
.
.Epoch 149: | Train Loss: 0.42277 | Val Loss: 0.37748
Epoch 150: | Train Loss: 0.42012 | Val Loss: 0.37028

你可以看到我们在循环之前放了一个model.train()。告诉 PyTorch 你正处于训练模式。

为什么我们需要这么做?如果使用在训练和评估期间表现不同的层,如DropoutBatchNorm(例如;评估期间不使用 dropout),您需要告诉 PyTorch 采取相应的行动。

同样,当我们测试我们的模型时,我们将调用model.eval()。我们将在下面看到。

回到训练;我们开始一个循环。在这个 for 循环的顶部,我们将每个时期的损失初始化为 0。在每个时期之后,我们将打印出损失并将其重置回 0。

然后我们有另一个 for 循环。这个 for-loop 用于从train_loader批量获取我们的数据。

在我们做任何预测之前,我们做optimizer.zero_grad()。由于backward()函数累加梯度,我们需要为每个小批量手动将其设置为 0。

从我们定义的模型中,我们获得一个预测,得到小批量的损失(和精度),使用loss.backward()optimizer.step()执行反向传播。

最后,我们将所有小批量损失相加,以获得该时期的平均损失。我们将每个小批量的所有损失相加,最后除以小批量的数量,即。train_loader的长度,以获得每个历元的平均损失。

我们遵循的训练程序与验证程序完全相同,除了我们用torch.no_grad将它包装起来,并且不执行任何反向传播。torch.no_grad()告诉 PyTorch 我们不想执行反向传播,这样可以减少内存使用并加快计算速度。

可视化损失和准确性

为了绘制损失线图,我们再次从“loss_stats”字典中创建一个数据帧。

train_val_loss_df = pd.DataFrame.from_dict(loss_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"})plt.figure(figsize=(15,8))sns.lineplot(data=train_val_loss_df, x = "epochs", y="value", hue="variable").set_title('Train-Val Loss/Epoch')

列车价值损失曲线[图片[6]]

试验模型

训练完成后,我们需要测试我们的模型进展如何。注意,在运行测试代码之前,我们已经使用了model.eval()。为了告诉 PyTorch 我们不希望在推断过程中执行反向传播,我们使用了torch.no_grad(),就像我们对上面的验证循环所做的那样。

y_pred_list = []with torch.no_grad():
    model.eval()
    for X_batch, _ in test_loader:
        X_batch = X_batch.to(device)
        y_test_pred = model(X_batch)
        y_pred_list.append(y_test_pred.cpu().numpy())y_pred_list = [a.squeeze().tolist() for a in y_pred_list]

让我们检查 MSE 和 R 平方指标。

mse = mean_squared_error(y_test, y_pred_list)
r_square = r2_score(y_test, y_pred_list)print("Mean Squared Error :",mse)
print("R^2 :",r_square) ###################### OUTPUT ######################Mean Squared Error : 0.40861496703609534
R^2 : 0.36675687655886924

感谢您的阅读。欢迎提出建议和建设性的批评。😃

这篇博客是专栏“如何训练你的神经网络”的一部分。你可以在这里找到专栏。

你可以在 LinkedIn 和 Twitter 上找到我。如果你喜欢这个,看看我的其他博客。

PyTorch 花絮:测量溪流和时间

原文:https://towardsdatascience.com/pytorch-tidbits-measuring-streams-and-times-de0c7bae85da?source=collection_archive---------45-----------------------

统计数据收集的简单实用程序

典型的 PyTorch 训练循环包含跟踪训练和测试指标的代码;他们帮助我们监控进度并绘制学习曲线。

假设对于一个分类任务,我们使用二元交叉熵作为训练损失,我们也对准确性感兴趣。在每一个时期之后,我们在一个持续的验证数据集上测量相同的值。我们希望将定期进度信息写入控制台以及 Tensorboard(或任何其他您喜欢的仪表板工具)。

请注意记录大量连续值,然后对其进行统计的模式。虽然这个片段足够简单,但是有一些重复,并且日志开始模糊主要的算法核心。我们想要跟踪的度量和统计数据(比如,除了平均值之外的标准偏差)就越多。

这已经在 ImageNet 演示代码中得到认可,它包含了一个用于此目的的辅助类:

使用起来非常简单:我们创建一个AverageMeter实例m,反复调用m.update(loss),并在一个时期结束时检索平均值为m.avg

诚然,这是一个相当谦虚的班级;然而,在用 Python 编写机器学习应用程序时,像这样跟踪统计数据是普遍存在的。因此,仔细研究一下这个概念可能是值得的,因为它有助于多种扩展。

聚合函数

您肯定已经注意到,在第一个清单中,存储每个损失值有点天真;相反,我们只需要递增地更新元素的总数和数量。大多数数据处理语言,如 SQLPigLatin 通常称之为可分解聚合函数;它只记录恒定数量的足够的统计数据,并以交换和关联的方式更新。常见的聚合函数有最小值、最大值和标准差(为此我们需要平方和)。很容易得出其他有用的度量:最近的值、零或非常小的值的百分比、绝对最小值和最大值。我们的更新功能变成了:

平均值和标准偏差可通过以下简单属性提取:

如果我们不局限于恒定的空间和时间要求,我们当然可以实现更复杂的聚合函数,或者作为精确的或者许多已知的近似算法之一,用于 k-最频繁、k-最小和唯一的元素。

句法糖

作为一种快捷方式,增量添加操作符+=可以作为 update 的别名。添加意味着更新副本。标准的__len__函数默认为计数。以字典的形式检索值并拥有合适的字符串表示很方便。

与其他电表、NumPy 阵列和 PyTorch 张量聚合

两个Meters可以在两个并发线程中聚合指标,我们希望将它们汇总在一起。更新功能的扩展很简单:

到目前为止,我们假设我们的值是本地 Python 数字。但是有时我们想要查看 PyTorch Tensors上的统计数据(比如模型激活、权重和它们的梯度)。我们将用数组更新Meter的语义定义为用每个单独的元素更新它。

回想一下,PyTorch Tensors可以通过共享底层内存转换成NumPyndarrays;所以当我们两个都想要的时候,一个可以简化为另一个:

我们希望为用户提供一个update函数,而不必记住选择update_scalar, update_tensor,update_meter. 不幸的是,与 C++等强类型语言不同,Python 不允许参数重载,但我们可以将通用功能移到抽象基类并创建一个分派:

仪表词典

当跟踪多个指标时,可以通过使用字典来简化代码,字典的键和值分别是指标的名称和聚合器。聚合两个MeterDicts或一个MeterDict和一个Meter的操作非常简单。

当我们作为软件工程师成长时,对全局变量的恐惧已经被灌输到我们的头脑中。但有时这让我们忘记了全局变量在少数情况下确实是有用的;它们可以让我们避免在函数中传递相同或不重要的参数,或者避免重复拼写长的类引用链。Python 中现有的例子有日志和随机数生成器。在训练循环的实现中,我们开始使用我们的Engine类的成员变量来跟踪统计数据,但是结果证明这太麻烦了——例如,PyTorch DataLoader中的工作线程不能直接访问引擎,那么我们如何才能轻松地共享度量呢?我们认为,共享一个单一的、全局的MeterDict对于跨应用程序的度量跟踪是有用的。

格式化

对于人类可读的输出,数字应该总是被适当地格式化,例如,通过舍入到有效数字。此外,日志文件可以包含大量输出;我们可以使用 grep 之类的工具搜索我们感兴趣的东西,但对于这项工作来说,使用统一的格式并将相关信息放在同一行也很有帮助。我们定义一个 formatter 类,它注册一个或几个仪表,其字符串表示是所需的日志记录输出。

将所有内容放在一起,我们最初的、简单的训练循环现在可以写成如下形式:

定时

出于诊断和监控目的,默认情况下,将执行时间记录到 Tensorboard 是很有用的。通常,这样的度量是在多次调用中平均的,这意味着使用一个Meter进行聚合。查看挂钟时间和 CPU 时间可能会有所帮助,因此我们的计时函数将同时提供这两种时间(通过后缀来区分)。有时我们想对代码段计时,这需要一个上下文管理器(类Timing);对于完整的函数调用,我们更喜欢装饰器,@timed。所有这些函数都允许一个可选的MeterDict参数,但是它们默认为全局的MeterDict

代码

本文的完整代码可在这里获得;我希望它对你的下一个 python ML 项目有用!

py torch[视觉] —二值图像分类

原文:https://towardsdatascience.com/pytorch-vision-binary-image-classification-d9a227705cf9?source=collection_archive---------8-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

本笔记本将带您使用 PyTorch 上的热狗/非热狗数据集,通过 CNN 实现二值图像分类。

导入库

import numpy as np
import pandas as pd
import seaborn as sns
from tqdm.notebook import tqdm
import matplotlib.pyplot as plt import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import transforms, utils, datasets
from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler from sklearn.metrics import classification_report, confusion_matrix

设置随机种子。

np.random.seed(0)
torch.manual_seed(0)

设置Seaborn样式。

%matplotlib inline
sns.set_style('darkgrid')

定义路径并设置 GPU

让我们定义数据的路径。

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("We're using =>", device)root_dir = "../../../data/computer_vision/image_classification/hot-dog-not-hot-dog/"
print("The data lies here =>", root_dir) ###################### OUTPUT ######################We're using => cuda
The data lies here => ../../../data/computer_vision/image_classification/hot-dog-not-hot-dog/

定义转换

让我们定义一个字典来保存训练/测试集的图像转换。我们将调整所有图像的大小为大小(224,224 ),并将图像转换为张量。

PyTorch 中的ToTensor操作将所有张量转换到(0,1)之间。

*ToTensor* 将范围[0,255]内的 PIL 图像或 numpy.ndarray (H x W x C)转换为火炬。形状(C x H x W)在[0.0,1.0] 范围内的浮点数

image_transforms = {
    "train": transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ]),
    "test": transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
}

初始化数据集

训练+验证数据集

我们随身带着 2 个数据集文件夹— 训练测试

我们将进一步划分我们的火车集合为火车+ Val

hotdog_dataset = datasets.ImageFolder(root = root_dir + "train",
                                      transform = image_transforms["train"]
                                     )hotdog_dataset ###################### OUTPUT ######################Dataset ImageFolder
    Number of datapoints: 498
    Root location: ../../../data/computer_vision/image_classification/hot-dog-not-hot-dog/train
    StandardTransform
Transform: Compose(
               Resize(size=(224, 224), interpolation=PIL.Image.BILINEAR)
               ToTensor()
           )

输出的类<=> ID 映射

PyTorch 内置了class_to_idx功能。它返回数据集中的类 ID。

hotdog_dataset.class_to_idx ###################### OUTPUT ######################{'hot_dog': 0, 'not_hot_dog': 1}

我们现在将构建该字典的反向;ID 到类的映射。

idx2class = {v: k for k, v in hotdog_dataset.class_to_idx.items()}

让我们还编写一个函数,它接受一个 dataset 对象并返回一个包含类样本计数的字典。我们将使用这个字典来构建图,并观察我们的数据中的类分布。

get_class_distribution()接受了一个名为dataset_obj的论点。

  • 我们首先初始化一个count_dict字典,其中所有类的计数都被初始化为 0。
  • 然后,让我们遍历数据集,并为循环中遇到的每个类标签将计数器加 1。

plot_from_dict()接受 3 个参数:一个名为dict_objplot_title**kwargs的字典。我们传入**kwargs是因为稍后,我们将构建需要在 Seaborn 中传递ax参数的支线剧情。

  • 首先将字典转换为数据框。
  • 融合数据框和绘图。
def get_class_distribution(dataset_obj):
    count_dict = {k:0 for k,v in dataset_obj.class_to_idx.items()} for _, label_id in dataset_obj:
        label = idx2class[label_id]
        count_dict[label] += 1
    return count_dict def plot_from_dict(dict_obj, plot_title, **kwargs):
    return sns.barplot(data = pd.DataFrame.from_dict([dict_obj]).melt(), x = "variable", y="value", hue="variable", **kwargs).set_title(plot_title)plt.figure(figsize=(15,8))
plot_from_dict(get_class_distribution(hotdog_dataset), plot_title="Entire Dataset (before train/val/test split)")

整个数据集上的类分布[图像[1]]

获取训练和验证样本

我们使用SubsetRandomSampler来制作我们的训练和验证加载器。SubsetRandomSampler用于使每批接收一个随机分布的类。

我们也可以将我们的数据集分成两部分— train 和 val ie。制作 2 个Subsets。但是这更简单,因为我们的数据加载器现在几乎可以处理所有的事情。

SubsetRandomSampler(indices)将数据的索引作为输入。

我们首先创建我们的采样器,然后将它传递给我们的数据加载器。

  • 创建索引列表。
  • 打乱索引。
  • 根据列车价值百分比拆分指数。
  • 创造SubsetRandomSampler

创建从 0 到数据集长度的索引列表。

hotdog_dataset_size = len(hotdog_dataset)
hotdog_dataset_indices = list(range(hotdog_dataset_size))

使用 np.shuffle 打乱索引列表。

np.random.shuffle(hotdog_dataset_indices)

创建拆分索引。我们选择拆分索引为数据集大小的 20% (0.2)。

val_split_index = int(np.floor(0.2 * hotdog_dataset_size))

将列表切片以获得 2 个索引列表,一个用于训练,另一个用于测试。

0-----------val_split_index------------------------------n

train = >val _ split _ indexn

Val => 0val_split_index

train_idx, val_idx = hotdog_dataset_indices[val_split_index:], hotdog_dataset_indices[:val_split_index]

最后,创建采样器。

train_sampler = SubsetRandomSampler(train_idx)
val_sampler = SubsetRandomSampler(val_idx)

试验

既然我们已经完成了训练和赋值数据,让我们加载测试数据集。

hotdog_dataset_test = datasets.ImageFolder(root = root_dir + "test",
                                            transform = image_transforms["test"]
                                           )hotdog_dataset_test ###################### OUTPUT ######################Dataset ImageFolder
    Number of datapoints: 500
    Root location: ../../../data/computer_vision/image_classification/hot-dog-not-hot-dog/test
    StandardTransform
Transform: Compose(
               Resize(size=(224, 224), interpolation=PIL.Image.BILINEAR)
               ToTensor()
           )

培训、验证和测试数据加载器

现在,我们将把采样器传递给我们的数据加载器。请注意,当您使用SubsetRandomSampler时,不能使用shuffle=True

train_loader = DataLoader(dataset=hotdog_dataset, shuffle=False, batch_size=8, sampler=train_sampler)val_loader = DataLoader(dataset=hotdog_dataset, shuffle=False, batch_size=1, sampler=val_sampler)test_loader = DataLoader(dataset=hotdog_dataset_test, shuffle=False, batch_size=1)

探索数据

为了研究我们 train 和 val 数据加载器,让我们创建一个新函数,它接收一个数据加载器并返回一个包含类计数的字典。

  • 将字典count_dict初始化为全 0。
  • 如果dataloader_obj的 batch_size 为 1,则循环通过dataloader_obj并更新计数器。
  • 否则,如果dataloader_obj的 batch_size 是而不是 1,则循环通过dataloader_obj获得批次。循环遍历批以获得单个张量。现在,相应地更新了计数器。
def get_class_distribution_loaders(dataloader_obj, dataset_obj):
    count_dict = {k:0 for k,v in dataset_obj.class_to_idx.items()} if dataloader_obj.batch_size == 1:    
        for _,label_id in dataloader_obj:
            y_idx = label_id.item()
            y_lbl = idx2class[y_idx]
            count_dict[str(y_lbl)] += 1
    else: 
        for _,label_id in dataloader_obj:
            for idx in label_id:
                y_idx = idx.item()
                y_lbl = idx2class[y_idx]
                count_dict[str(y_lbl)] += 1 return count_dict

为了绘制类分布,我们将使用前面定义的带有ax参数的plot_from_dict()函数。

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(18,7))plot_from_dict(get_class_distribution_loaders(train_loader, hotdog_dataset), plot_title="Train Set", ax=axes[0])plot_from_dict(get_class_distribution_loaders(val_loader, hotdog_dataset), plot_title="Val Set", ax=axes[1])

训练和值集的类分布[图像[2]]

既然我们已经看了类分布,现在让我们看一个单一的图像。

single_batch = next(iter(train_loader))

single_batch是 2 个元素的列表。第一个元素(第 0 个索引)包含图像张量,而第二个元素(第 1 个索引)包含输出标签。

这是列表的第一个元素,它是一个张量。这个张量的形状是(batch, channels, height, width)

single_batch[0].shape ###################### OUTPUT ######################torch.Size([8, 3, 224, 224])

这是该批次的输出标签。

print("Output label tensors: ", single_batch[1])
print("\nOutput label tensor shape: ", single_batch[1].shape) ###################### OUTPUT ######################Output label tensors:  tensor([1, 1, 1, 1, 1, 1, 1, 1])Output label tensor shape:  torch.Size([8])

为了绘制图像,我们将使用 matloptlib 中的plt.imshow。它期望图像尺寸为(height, width, channels)。我们将.permute()我们的单个图像张量来绘制它。

# Selecting the first image tensor from the batch. 
single_image = single_batch[0][0]single_image.shape ###################### OUTPUT ######################torch.Size([3, 224, 224])

让我们来看图像。

plt.imshow(single_image.(1, 2, 0))

数据集[图像[3]]中的单个样本

PyTorch 使我们更容易直接从批次中绘制网格图像。

我们首先从列表中提取图像张量(由我们的数据加载器返回)并设置nrow。然后我们使用plt.imshow()函数来绘制网格。记住.permute()张量维度!

# We do single_batch[0] because each batch is a list 
# where the 0th index is the image tensor and 1st index is the output label.
single_batch_grid = utils.make_grid(single_batch[0], nrow=4)plt.figure(figsize = (10,10))
plt.imshow(single_batch_grid.permute(1, 2, 0))

来自数据集的多个样本[图像[4]]

定义 CNN 架构

我们的建筑很简单。我们使用 4 块 Conv 层。每个区块由Convolution + BatchNorm + ReLU + Dropout层组成。

我们不会在最后使用一个FC层。我们将坚持使用Conv层。

class HotDogClassifier(nn.Module):
    def __init__(self):
        super(HotDogClassifier, self).__init__()
 self.block1 = self.conv_block(c_in=3, c_out=256, dropout=0.1, kernel_size=5, stride=1, padding=2)
        self.block2 = self.conv_block(c_in=256, c_out=128, dropout=0.1, kernel_size=3, stride=1, padding=1)
        self.block3 = self.conv_block(c_in=128, c_out=64, dropout=0.1, kernel_size=3, stride=1, padding=1)
        self.lastcnn = nn.Conv2d(in_channels=64, out_channels=2, kernel_size=56, stride=1, padding=0) self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2) def forward(self, x):
        x = self.block1(x)
        x = self.maxpool(x) x = self.block2(x) x = self.block3(x)
        x = self.maxpool(x) x = self.lastcnn(x) return x def conv_block(self, c_in, c_out, dropout,  **kwargs):
        seq_block = nn.Sequential(
            nn.Conv2d(in_channels=c_in, out_channels=c_out, **kwargs),
            nn.BatchNorm2d(num_features=c_out),
            nn.ReLU(),
            nn.Dropout2d(p=dropout)
        ) return seq_block

现在我们将初始化模型、优化器和损失函数。

然后我们将模型传输到 GPU。

尽管这是一个二元分类问题,我们还是使用了nn.CrossEntropyLoss。这意味着,我们将处理0 and 1的返回 2 个值,而不是返回1/0的单个输出。更具体地说,输出的概率是10

我们不需要在最后一层之后手动添加一个log_softmax层,因为nn.CrossEntropyLoss已经为我们做了。

然而,我们需要应用log_softmax进行验证和测试。

model = HotDogClassifier()
model.to(device)
print(model)criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.008) ###################### OUTPUT ######################HotDogClassifier(
  (block1): Sequential(
    (0): Conv2d(3, 256, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout2d(p=0.1, inplace=False)
  )
  (block2): Sequential(
    (0): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout2d(p=0.1, inplace=False)
  )
  (block3): Sequential(
    (0): Conv2d(128, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout2d(p=0.1, inplace=False)
  )
  (lastcnn): Conv2d(64, 2, kernel_size=(56, 56), stride=(1, 1))
  (maxpool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)

在我们开始训练之前,让我们定义一个函数来计算每个历元的精度。

该函数将y_predy_test作为输入参数。然后,我们将 softmax 应用于y_pred并提取具有较高概率的类别。

之后,我们比较预测类别和实际类别来计算准确度。

def binary_acc(y_pred, y_test):
    y_pred_tag = torch.log_softmax(y_pred, dim = 1)
    _, y_pred_tags = torch.max(y_pred_tag, dim = 1) correct_results_sum = (y_pred_tags == y_test).sum().float() acc = correct_results_sum/y_test.shape[0]
    acc = torch.round(acc * 100) return acc

我们还将定义 2 个字典,用于存储训练集和验证集的准确度/时期和损失/时期。

accuracy_stats = {
    'train': [],
    "val": []
}loss_stats = {
    'train': [],
    "val": []
}

让我们训练我们的模型!

你可以看到,我们在循环之前已经在。model.train()告诉 PyTorch 你正处于训练模式。为什么我们需要这么做?如果你使用了像DropoutBatchNorm这样在训练和评估过程中表现不同的层(例如;评估期间不使用dropout,您需要告诉 PyTorch 采取相应的行动。而 PyTorch 中的默认模式是火车,因此,您不必显式地编写它。但这是很好的练习。

同样,当我们测试我们的模型时,我们将调用model.eval()。我们将在下面看到。回到训练;我们开始一个循环。在这个 for 循环的顶部,我们将每个历元的损失和精度初始化为 0。在每个时期之后,我们将打印出损失/精度并将其重置回 0。

然后我们有另一个 for 循环。这个 for 循环用于从train_loader中批量获取我们的数据。

在我们做任何预测之前,我们先做optimizer.zero_grad()。由于.backward()函数累加梯度,我们需要为每个小批量手动将其设置为 0。然后,从我们定义的模型中,我们获得一个预测,获得该小批量的损失(和准确性),使用 loss.backward()和 optimizer.step()执行反向传播。

最后,我们将所有小批量损失(和精度)相加,以获得该时期的平均损失(和精度)。我们将每个小批次的所有损耗/精度相加,最后除以小批次的数量,即。为获得每个时期的平均损失/精确度,列车装载器的长度。

我们遵循的培训程序与验证程序完全相同,除了我们在torch.no_grad中对其进行了包装,并且不执行任何反向传播。torch.no_grad()告诉 PyTorch 我们不想执行反向传播,这样可以减少内存使用并加快计算速度。

print("Begin training.")for e in tqdm(range(1, 21)): # TRAINING train_epoch_loss = 0
    train_epoch_acc = 0 model.train()
    for X_train_batch, y_train_batch in train_loader:
        X_train_batch, y_train_batch = X_train_batch.to(device), y_train_batch.to(device)
        optimizer.zero_grad() y_train_pred = model(X_train_batch).squeeze() train_loss = criterion(y_train_pred, y_train_batch)
        train_acc = binary_acc(y_train_pred, y_train_batch) train_loss.backward()
        optimizer.step() train_epoch_loss += train_loss.item()
        train_epoch_acc += train_acc.item() # VALIDATION
    with torch.no_grad():
        model.eval()
        val_epoch_loss = 0
        val_epoch_acc = 0
        for X_val_batch, y_val_batch in val_loader:
            X_val_batch, y_val_batch = X_val_batch.to(device), y_val_batch.to(device) y_val_pred = model(X_val_batch).squeeze()
            y_val_pred = torch.unsqueeze(y_val_pred, 0) val_loss = criterion(y_val_pred, y_val_batch)
            val_acc = binary_acc(y_val_pred, y_val_batch) val_epoch_loss += val_loss.item()
            val_epoch_acc += val_acc.item() loss_stats['train'].append(train_epoch_loss/len(train_loader))
    loss_stats['val'].append(val_epoch_loss/len(val_loader))
    accuracy_stats['train'].append(train_epoch_acc/len(train_loader))
    accuracy_stats['val'].append(val_epoch_acc/len(val_loader)) print(f'Epoch {e+0:02}: | Train Loss: {train_epoch_loss/len(train_loader):.5f} | Val Loss: {val_epoch_loss/len(val_loader):.5f} | Train Acc: {train_epoch_acc/len(train_loader):.3f}| Val Acc: {val_epoch_acc/len(val_loader):.3f}')###################### OUTPUT ######################Begin training.Epoch 01: | Train Loss: 113.08463 | Val Loss: 92.26063 | Train Acc: 51.120| Val Acc: 29.000
Epoch 02: | Train Loss: 55.47888 | Val Loss: 50.39846 | Train Acc: 63.620| Val Acc: 57.000
Epoch 03: | Train Loss: 33.44443 | Val Loss: 20.69457 | Train Acc: 70.500| Val Acc: 71.000
Epoch 04: | Train Loss: 18.75201 | Val Loss: 1.50821 | Train Acc: 77.240| Val Acc: 71.000
Epoch 05: | Train Loss: 12.88685 | Val Loss: 26.62685 | Train Acc: 75.480| Val Acc: 71.000
Epoch 06: | Train Loss: 9.70507 | Val Loss: 3.25360 | Train Acc: 81.080| Val Acc: 86.000
Epoch 07: | Train Loss: 11.04334 | Val Loss: 0.00000 | Train Acc: 79.320| Val Acc: 100.000
Epoch 08: | Train Loss: 7.16636 | Val Loss: 10.48954 | Train Acc: 83.300| Val Acc: 71.000
Epoch 09: | Train Loss: 4.32204 | Val Loss: 0.00001 | Train Acc: 86.400| Val Acc: 100.000
Epoch 10: | Train Loss: 2.03338 | Val Loss: 0.00000 | Train Acc: 91.720| Val Acc: 100.000
Epoch 11: | Train Loss: 1.68124 | Val Loss: 3.65754 | Train Acc: 92.320| Val Acc: 71.000
Epoch 12: | Train Loss: 1.27145 | Val Loss: 5.52111 | Train Acc: 93.320| Val Acc: 86.000
Epoch 13: | Train Loss: 0.42285 | Val Loss: 0.00000 | Train Acc: 97.600| Val Acc: 100.000
Epoch 14: | Train Loss: 1.03441 | Val Loss: 0.00000 | Train Acc: 94.840| Val Acc: 100.000
Epoch 15: | Train Loss: 0.76563 | Val Loss: 0.00000 | Train Acc: 96.340| Val Acc: 100.000
Epoch 16: | Train Loss: 0.16889 | Val Loss: 0.00000 | Train Acc: 98.040| Val Acc: 100.000
Epoch 17: | Train Loss: 0.42046 | Val Loss: 4.02560 | Train Acc: 96.560| Val Acc: 86.000
Epoch 18: | Train Loss: 0.57535 | Val Loss: 0.00000 | Train Acc: 95.640| Val Acc: 100.000
Epoch 19: | Train Loss: 0.40181 | Val Loss: 0.00000 | Train Acc: 96.620| Val Acc: 100.000
Epoch 20: | Train Loss: 0.92207 | Val Loss: 0.00000 | Train Acc: 95.360| Val Acc: 100.000

可视化损失和准确性

为了绘制损耗和精度线图,我们再次从accuracy_statsloss_stats字典中创建一个数据帧。

train_val_acc_df = pd.DataFrame.from_dict(accuracy_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"})train_val_loss_df = pd.DataFrame.from_dict(loss_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"})fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(30,10))
sns.lineplot(data=train_val_acc_df, x = "epochs", y="value", hue="variable",  ax=axes[0]).set_title('Train-Val Accuracy/Epoch')sns.lineplot(data=train_val_loss_df, x = "epochs", y="value", hue="variable", ax=axes[1]).set_title('Train-Val Loss/Epoch')

列车和阀门组的 Acc/loss 曲线[图片[5]]

试验

训练完成后,我们需要测试我们的模型进展如何。注意,在运行测试代码之前,我们已经使用了model.eval()。为了告诉 PyTorch 我们不希望在推断过程中执行反向传播,我们使用了torch.no_grad(),就像我们对上面的验证循环所做的那样。

  • 我们首先定义一个包含我们预测的列表。然后我们使用test_loader循环遍历我们的批处理。对于每一批-
  • 我们将输入小批量数据转移到 GPU。
  • 我们使用训练好的模型进行预测。
  • 将 log_softmax 激活应用于预测,并选择概率最高的索引。
  • 将批处理从 CPU 移动到 GPU。
  • 将张量转换为 numpy 对象,并将其添加到我们的列表中。
y_pred_list = []
y_true_list = []
with torch.no_grad():
    for x_batch, y_batch in tqdm(test_loader):
        x_batch, y_batch = x_batch.to(device), y_batch.to(device) y_test_pred = model(x_batch)
        _, y_pred_tag = torch.max(y_test_pred, dim = 1) y_pred_list.append(y_pred_tag.cpu().numpy())
        y_true_list.append(y_batch.cpu().numpy())

我们将使列表变平,这样我们就可以将它用作confusion_matrixclassification_report的输入。

y_pred_list = [i[0][0][0] for i in y_pred_list]y_true_list = [i[0] for i in y_true_list]

分类报告

最后,我们打印出包含精确度、召回率和 F1 分数的分类报告。

print(classification_report(y_true_list, y_pred_list)) ###################### OUTPUT ######################precision    recall  f1-score   support 0       0.90      0.91      0.91       249
           1       0.91      0.90      0.91       249 accuracy                           0.91       498
   macro avg       0.91      0.91      0.91       498
weighted avg       0.91      0.91      0.91       498

混淆矩阵

让我们使用confusion_matrix()函数来制作一个混淆矩阵。

print(confusion_matrix(y_true_list, y_pred_list)) ###################### OUTPUT ######################[[226  23]
 [ 24 225]]

我们从混淆矩阵中创建一个数据框架,并使用 seaborn 库将其绘制为热图。

confusion_matrix_df = pd.DataFrame(confusion_matrix(y_true_list, y_pred_list)).rename(columns=idx2class, index=idx2class)fig, ax = plt.subplots(figsize=(7,5))         
sns.heatmap(confusion_matrix_df, annot=True, ax=ax)

混淆矩阵热图[图片[6]]

感谢您的阅读。欢迎提出建议和建设性的批评。😃

这篇博文是专栏“如何训练你的神经网络”的一部分。你可以在这里找到系列

你可以在 LinkedIn 和 Twitter 找到我。如果你喜欢这个,看看我的其他博客。

py torch[视觉] —多类图像分类

原文:https://towardsdatascience.com/pytorch-vision-multiclass-image-classification-531025193aa?source=collection_archive---------9-----------------------

如何训练你的神经网络[图片[0]]

如何训练你的神经网络

本笔记本将带您使用 PyTorch 上的石头剪刀布数据集,通过 CNN 实现多类图像分类。

导入库

import numpy as np
import pandas as pd
import seaborn as sns
from tqdm.notebook import tqdm
import matplotlib.pyplot as plt
import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import transforms, utils, datasets
from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler
from sklearn.metrics import classification_report, confusion_matrix

设置随机种子。

np.random.seed(0)
torch.manual_seed(0)

设置Seaborn样式。

%matplotlib inline
sns.set_style('darkgrid')

定义路径并设置 GPU

让我们定义数据的路径。

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")print("We're using =>", device)root_dir = "../../../data/computer_vision/image_classification/hot-dog-not-hot-dog/"
print("The data lies here =>", root_dir)###################### OUTPUT ######################We're using => cuda
The data lies here => ../../../data/computer_vision/image_classification/hot-dog-not-hot-dog/

定义转换

让我们定义一个字典来保存训练/测试集的图像转换。我们将调整所有图像的大小为大小(224,224 ),并将图像转换为张量。

PyTorch 中的ToTensor操作将所有张量转换到(0,1)之间。

ToTensor将范围为[0,255]的 PIL 图像或numpy.ndarray(高 x 宽 x 高)转换为范围为[0.0,1.0]的形状的torch.FloatTensor

image_transforms = {
    "train": transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ]),
    "test": transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
}

定义转换

让我们定义一个字典来保存训练/测试集的图像转换。所有图像的尺寸都是 (300,300) 。我们仍将调整所有图像的大小(以防止错误)为大小 (300,300) ,并将图像转换为张量。PyTorch 中的ToTensor操作将所有张量转换到(0,1)之间。

*ToTensor* 将范围[0,255]中的 PIL 图像或 *numpy.ndarray* (H x W x C)转换为范围[0,1.0] 中的形状的 *torch.FloatTensor*

*image_transforms = {
    "train": transforms.Compose([
        transforms.Resize((300, 300)),
        transforms.ToTensor(),
        transforms.Normalize([0.5, 0.5, 0.5],
                             [0.5, 0.5, 0.5])
    ]),
    "test": transforms.Compose([
        transforms.Resize((300, 300)),
        transforms.ToTensor(),
        transforms.Normalize([0.5, 0.5, 0.5],
                             [0.5, 0.5, 0.5])
    ])
}*

初始化数据集

训练+验证数据集

我们带着 2 个数据集文件夹— 训练测试

我们将进一步划分我们的列车集合为列车+ Val

*rps_dataset = datasets.ImageFolder(root = root_dir + "train",
                                   transform = image_transforms["train"]
                                  )rps_dataset ###################### OUTPUT ######################Dataset ImageFolder
    Number of datapoints: 2520
    Root location: ../../../data/computer_vision/image_classification/rock-paper-scissor/train
    StandardTransform
Transform: Compose(
               Resize(size=(300, 300), interpolation=PIL.Image.BILINEAR)
               ToTensor()
               Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
           )*

输出的类<=> ID 映射

PyTorch 内置了class_to_idx函数。它返回数据集中的类 ID。

*rps_dataset.class_to_idx ###################### OUTPUT ######################
{'paper': 0, 'rock': 1, 'scissors': 2}*

我们现在将构建该字典的反向;ID 到类的映射。

*idx2class = {v: k for k, v in rps_dataset.class_to_idx.items()}
idx2class ###################### OUTPUT ######################{0: 'paper', 1: 'rock', 2: 'scissors'}*

让我们还编写一个函数,它接受一个 dataset 对象并返回一个包含类样本计数的字典。我们将使用这个字典来构建图,并观察我们的数据中的类分布。

get_class_distribution()接受一个名为dataset_obj的参数。

  • 我们首先初始化一个count_dict字典,其中所有类的计数都被初始化为 0。
  • 然后,让我们遍历数据集,并为循环中遇到的每个类标签将计数器加 1。

plot_from_dict()接受 3 个参数:一个名为dict_objplot_title**kwargs的字典。我们传入**kwargs是因为稍后,我们将构建需要在 seaborn 中传递ax参数的支线剧情。

  • 首先,将字典转换成数据帧。
  • 熔化数据框并绘图。
*def get_class_distribution(dataset_obj):
    count_dict = {k:0 for k,v in dataset_obj.class_to_idx.items()} for _, label_id in dataset_obj:
        label = idx2class[label_id]
        count_dict[label] += 1
    return count_dict def plot_from_dict(dict_obj, plot_title, **kwargs):
    return sns.barplot(data = pd.DataFrame.from_dict([dict_obj]).melt(), x = "variable", y="value", hue="variable", **kwargs).set_title(plot_title)plt.figure(figsize=(15,8))
plot_from_dict(get_class_distribution(rps_dataset), plot_title="Entire Dataset (before train/val/test split)")*

数据分布[图像[1]]

获取训练和验证样本

我们使用SubsetRandomSampler来制作我们的训练和验证加载器。SubsetRandomSampler用于使每批接收一个随机分布的类。

我们也可以将数据集分成两部分——train 和 val,即。使 2 Subsets。但是这更简单,因为我们的数据加载器现在几乎可以处理所有的事情。

SubsetRandomSampler(indices)将数据的索引作为输入。

我们首先创建我们的采样器,然后将它传递给我们的数据加载器。

  • 创建索引列表。
  • 打乱索引。
  • 根据列车价值百分比拆分指数。
  • 创建SubsetRandomSampler

创建从 0 到数据集长度的索引列表。

*rps_dataset_size = len(rps_dataset)
rps_dataset_indices = list(range(rps_dataset_size))*

使用 np.shuffle 打乱索引列表。

*np.random.shuffle(rps_dataset_indices)*

创建拆分索引。我们选择拆分索引为数据集大小的 20% (0.2)。

*val_split_index = int(np.floor(0.2 * rps_dataset_size))*

将列表切片以获得 2 个索引列表,一个用于训练,另一个用于测试。

0-----------val_split_index------------------------------n

Train => val_split_index 到 n

Val => 0 到 val_split_index

*train_idx, val_idx = rps_dataset_indices[val_split_index:], rps_dataset_indices[:val_split_index]*

最后,创建采样器。

*train_sampler = SubsetRandomSampler(train_idx)
val_sampler = SubsetRandomSampler(val_idx)*

试验

既然我们已经完成了训练和赋值数据,让我们加载测试数据集。

*rps_dataset_test = datasets.ImageFolder(root = root_dir + "test",
                                        transform = image_transforms["test"])rps_dataset_test ###################### OUTPUT ######################Dataset ImageFolder
    Number of datapoints: 372
    Root location: ../../../data/computer_vision/image_classification/rock-paper-scissor/test
    StandardTransform
Transform: Compose(
               Resize(size=(300, 300), interpolation=PIL.Image.BILINEAR)
               ToTensor()
               Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
           )*

培训、验证和测试数据加载器

现在,我们将把采样器传递给我们的数据加载器。请注意,当您使用SubsetRandomSampler时,不能使用shuffle=True

*train_loader = DataLoader(dataset=rps_dataset, shuffle=False, batch_size=8, sampler=train_sampler)val_loader = DataLoader(dataset=rps_dataset, shuffle=False, batch_size=1, sampler=val_sampler)test_loader = DataLoader(dataset=rps_dataset_test, shuffle=False, batch_size=1)*

探索数据

为了研究我们 train 和 val 数据加载器,让我们创建一个新函数,它接收一个数据加载器并返回一个包含类计数的字典。

  • 将字典count_dict初始化为全 0。
  • 如果dataloader_obj的 batch_size 为 1,则循环通过dataloader_obj并更新计数器。
  • 否则,如果dataloader_obj的 batch_size 是而不是 1,则循环通过dataloader_obj以获得批次。循环遍历批以获得单个张量。现在,相应地更新了计数器。
*def get_class_distribution_loaders(dataloader_obj, dataset_obj):
    count_dict = {k:0 for k,v in dataset_obj.class_to_idx.items()} if dataloader_obj.batch_size == 1:    
        for _,label_id in dataloader_obj:
            y_idx = label_id.item()
            y_lbl = idx2class[y_idx]
            count_dict[str(y_lbl)] += 1
    else: 
        for _,label_id in dataloader_obj:
            for idx in label_id:
                y_idx = idx.item()
                y_lbl = idx2class[y_idx]
                count_dict[str(y_lbl)] += 1 return count_dict*

为了绘制类分布,我们将使用前面用ax参数定义的plot_from_dict()函数。

*fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(18,7))plot_from_dict(get_class_distribution_loaders(train_loader, rps_dataset), plot_title="Train Set", ax=axes[0])plot_from_dict(get_class_distribution_loaders(val_loader, rps_dataset), plot_title="Val Set", ax=axes[1])*

列车价值等级分布[图片[2]]

既然我们已经看了类分布,现在让我们看一个单一的图像。

*single_batch = next(iter(train_loader))*

single_batch是两个元素的列表。第一个元素(第 0 个索引)包含图像张量,而第二个元素(第 1 个索引)包含输出标签。

这是列表的第一个元素,它是一个张量。这个张量的形状是(batch, channels, height, width)

*single_batch[0].shape ###################### OUTPUT ######################torch.Size([8, 3, 300, 300])*

这是该批次的输出标签。

*print("Output label tensors: ", single_batch[1])
print("\nOutput label tensor shape: ", single_batch[1].shape) ###################### OUTPUT ######################Output label tensors:  tensor([2, 0, 2, 2, 0, 1, 0, 0])Output label tensor shape:  torch.Size([8])*

为了绘制图像,我们将使用 matloptlib 中的plt.imshow。它期望图像尺寸为(height, width, channels)。我们将.permute()我们的单个图像张量来绘制它。

*# Selecting the first image tensor from the batch. 
single_image = single_batch[0][0]
single_image.shape ###################### OUTPUT ######################torch.Size([3, 300, 300])*

让我们绘制图像。

*plt.imshow(single_image.permute(1, 2, 0))*

单一图像样本[图像[3]]

PyTorch 使我们更容易直接从批次中绘制网格图像。

我们首先从列表中提取图像张量(由我们的数据加载器返回)并设置nrow。然后我们使用plt.imshow()函数来绘制网格。记住.permute()张量维度!

*# We do single_batch[0] because each batch is a list 
# where the 0th index is the image tensor and 1st index is the
# output label.single_batch_grid = utils.make_grid(single_batch[0], nrow=4)plt.figure(figsize = (10,10))
plt.imshow(single_batch_grid.permute(1, 2, 0))*

图像样本网格[图像[4]]

定义 CNN 架构

我们的建筑很简单。我们使用 4 块 Conv 层。每个区块由Convolution + BatchNorm + ReLU + Dropout层组成。

我们不会在最后使用一个FC层。我们将坚持使用Conv层。

将 FC 图层转换为 CONV 图层— 来源

值得注意的是,FC层和CONV层之间的唯一区别在于,CONV层中的神经元仅连接到输入中的局部区域,并且CONV体积中的许多神经元共享参数。然而,两层中的神经元仍然计算点积,因此它们的功能形式是相同的。因此,事实证明在FCCONV图层之间转换是可能的。

对于任何CONV层,都有一个FC层实现相同的转发功能。权重矩阵将是一个大矩阵,除了在某些块(由于局部连通性)处,其中许多块中的权重是相等的(由于参数共享),该大矩阵大部分为零。

相反,任何FC层都可以转换成CONV层。例如,具有K=4096FC层正在查看大小为7×7×512的某个输入体积,可以等效地表示为具有F=7,P=0,S=1,K=4096CONV层。

换句话说,我们将过滤器大小设置为输入体积的大小,因此输出将简单地为1×1×4096,因为只有一个深度列“适合”输入体积,给出与初始FC层相同的结果。

*class RpsClassifier(nn.Module):
    def __init__(self):
        super(RpsClassifier, self).__init__()
 self.block1 = self.conv_block(c_in=3, c_out=256, dropout=0.1, kernel_size=5, stride=1, padding=2)
        self.block2 = self.conv_block(c_in=256, c_out=128, dropout=0.1, kernel_size=3, stride=1, padding=1)
        self.block3 = self.conv_block(c_in=128, c_out=64, dropout=0.1, kernel_size=3, stride=1, padding=1)
        self.lastcnn = nn.Conv2d(in_channels=64, out_channels=3, kernel_size=75, stride=1, padding=0) self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2) def forward(self, x):
        x = self.block1(x)
        x = self.maxpool(x) x = self.block2(x) x = self.block3(x)
        x = self.maxpool(x) x = self.lastcnn(x) return x def conv_block(self, c_in, c_out, dropout, **kwargs):
        seq_block = nn.Sequential(
            nn.Conv2d(in_channels=c_in, out_channels=c_out, **kwargs),
            nn.BatchNorm2d(num_features=c_out),
            nn.ReLU(),
            nn.Dropout2d(p=dropout)
        ) return seq_block*

现在我们将初始化模型、优化器和损失函数。

然后我们将模型传输到 GPU。

尽管这是一个二元分类问题,我们还是使用了nn.CrossEntropyLoss。这意味着,我们将处理0 and 1的返回 2 个值,而不是返回1/0的单个输出。更具体地说,产出的概率是10

我们不需要在最后一层之后手动添加一个log_softmax层,因为nn.CrossEntropyLoss已经为我们做了。

然而,我们需要应用log_softmax来进行验证和测试。

*model = RpsClassifier()
model.to(device)
print(model)criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.005) ###################### OUTPUT ######################RpsClassifier(
  (block1): Sequential(
    (0): Conv2d(3, 256, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout2d(p=0.1, inplace=False)
  )
  (block2): Sequential(
    (0): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout2d(p=0.1, inplace=False)
  )
  (block3): Sequential(
    (0): Conv2d(128, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout2d(p=0.1, inplace=False)
  )
  (lastcnn): Conv2d(64, 3, kernel_size=(75, 75), stride=(1, 1))
  (maxpool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)*

在我们开始训练之前,让我们定义一个函数来计算每个历元的精度。

该函数将y_predy_test作为输入参数。然后,我们将softmax应用于y_pred,并提取概率较高的类别。

之后,我们比较预测类别和实际类别来计算准确度。

*def multi_acc(y_pred, y_test):
    y_pred_softmax = torch.log_softmax(y_pred, dim = 1)
    _, y_pred_tags = torch.max(y_pred_softmax, dim = 1)        correct_pred = (y_pred_tags == y_test).float()
    acc = correct_pred.sum() / len(correct_pred) acc = torch.round(acc * 100) return acc*

我们还将定义 2 个字典,用于存储训练集和验证集的准确度/时期和损失/时期。

*accuracy_stats = {
    'train': [],
    "val": []
}loss_stats = {
    'train': [],
    "val": []
}*

让我们训练我们的模型!

你可以看到我们在循环之前放了一个model.train()model.train()告诉 PyTorch 你正处于训练模式。为什么我们需要这么做?如果您使用的是DropoutBatchNorm等在训练和评估期间表现不同的层(例如;评估期间不使用dropout),您需要告诉 PyTorch 采取相应的行动。而 PyTorch 中的默认模式是火车,因此,您不必显式地编写它。但这是很好的练习。

类似地,当我们测试我们的模型时,我们将调用model.eval()。我们将在下面看到。回到训练;我们开始一个循环。在这个 for 循环的顶部,我们将每个历元的损失和精度初始化为 0。在每个时期之后,我们将打印出损失/精度并将其重置回 0。

然后我们有另一个 for 循环。这个 for 循环用于从train_loader中批量获取我们的数据。

我们在做任何预测之前都会做optimizer.zero_grad()。由于.backward()函数累加梯度,我们需要为每个小批量手动将其设置为 0。从我们定义的模型中,我们获得一个预测,获得这个小批量的损失(和准确性),使用 loss.backward()和 optimizer.step()执行反向传播。

最后,我们将所有小批量损失(和精度)相加,以获得该时期的平均损失(和精度)。我们将每个小批量的所有损耗/精度相加,最后除以小批量的数量,即。获得每个历元的平均损失/精度的train_loader的长度。

我们遵循的训练程序与验证程序完全相同,除了我们将其封装在torch.no_grad中,并且不执行任何反向传播。torch.no_grad()告诉 PyTorch 我们不想执行反向传播,这样可以减少内存使用并加快计算速度。

*print("Begin training.")for e in tqdm(range(1, 11)): # TRAINING train_epoch_loss = 0
    train_epoch_acc = 0 model.train()
    for X_train_batch, y_train_batch in train_loader:
        X_train_batch, y_train_batch = X_train_batch.to(device), y_train_batch.to(device) optimizer.zero_grad() y_train_pred = model(X_train_batch).squeeze() train_loss = criterion(y_train_pred, y_train_batch)
        train_acc = multi_acc(y_train_pred, y_train_batch) train_loss.backward()
        optimizer.step() train_epoch_loss += train_loss.item()
        train_epoch_acc += train_acc.item() # VALIDATION
    with torch.no_grad():
        model.eval()
        val_epoch_loss = 0
        val_epoch_acc = 0
        for X_val_batch, y_val_batch in val_loader:
            X_val_batch, y_val_batch = X_val_batch.to(device), y_val_batch.to(device) y_val_pred = model(X_val_batch).squeeze() y_val_pred = torch.unsqueeze(y_val_pred, 0) val_loss = criterion(y_val_pred, y_val_batch)
            val_acc = multi_acc(y_val_pred, y_val_batch) val_epoch_loss += train_loss.item()
            val_epoch_acc += train_acc.item() loss_stats['train'].append(train_epoch_loss/len(train_loader))
    loss_stats['val'].append(val_epoch_loss/len(val_loader))
    accuracy_stats['train'].append(train_epoch_acc/len(train_loader))
    accuracy_stats['val'].append(val_epoch_acc/len(val_loader)) print(f'Epoch {e+0:02}: | Train Loss: {train_epoch_loss/len(train_loader):.5f} | Val Loss: {val_epoch_loss/len(val_loader):.5f} | Train Acc: {train_epoch_acc/len(train_loader):.3f}| Val Acc: {val_epoch_acc/len(val_loader):.3f}')###################### OUTPUT ######################Begin training.Epoch 01: | Train Loss: 33.38733 | Val Loss: 10.19880 | Train Acc: 91.667| Val Acc: 100.000Epoch 02: | Train Loss: 6.49906 | Val Loss: 41.86950 | Train Acc: 99.603| Val Acc: 100.000Epoch 03: | Train Loss: 3.15175 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 04: | Train Loss: 0.40076 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 05: | Train Loss: 5.56540 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 06: | Train Loss: 1.56760 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 07: | Train Loss: 1.21176 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 08: | Train Loss: 0.84762 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 09: | Train Loss: 0.35811 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000Epoch 10: | Train Loss: 0.01389 | Val Loss: 0.00000 | Train Acc: 100.000| Val Acc: 100.000*

可视化损失和准确性

为了绘制损耗和精度线图,我们再次从accuracy_statsloss_stats字典中创建一个数据帧。

*train_val_acc_df = pd.DataFrame.from_dict(accuracy_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"})train_val_loss_df = pd.DataFrame.from_dict(loss_stats).reset_index().melt(id_vars=['index']).rename(columns={"index":"epochs"}) # Plot line charts
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(30,10))sns.lineplot(data=train_val_acc_df, x = "epochs", y="value", hue="variable",  ax=axes[0]).set_title('Train-Val Accuracy/Epoch')sns.lineplot(data=train_val_loss_df, x = "epochs", y="value", hue="variable", ax=axes[1]).set_title('Train-Val Loss/Epoch')*

train 和 val 的精度损失曲线[图片[5]]

试验

训练完成后,我们需要测试我们的模型进展如何。注意,在运行测试代码之前,我们已经使用了model.eval()。为了告诉 PyTorch 我们不希望在推断过程中执行反向传播,我们使用了torch.no_grad(),就像我们对上面的验证循环所做的那样。

  • 我们首先定义一个包含我们预测的列表。然后我们使用test_loader遍历我们的批处理。对于每一批-
  • 我们将输入小批量数据转移到 GPU。
  • 我们使用训练好的模型进行预测。
  • log_softmax激活应用于预测,并选择概率最高的指数。
  • 将批处理从 CPU 移动到 GPU。
  • 将张量转换为 numpy 对象,并将其添加到我们的列表中。
*y_pred_list = []
y_true_list = []
with torch.no_grad():
    for x_batch, y_batch in tqdm(test_loader):
        x_batch, y_batch = x_batch.to(device), y_batch.to(device) y_test_pred = model(x_batch)
        _, y_pred_tag = torch.max(y_test_pred, dim = 1) y_pred_list.append(y_pred_tag.cpu().numpy())
        y_true_list.append(y_batch.cpu().numpy())*

我们将把这个列表展平,这样我们就可以把它作为confusion_matrixclassification_report的输入。

*y_pred_list = [i[0][0][0] for i in y_pred_list]
y_true_list = [i[0] for i in y_true_list]*

分类报告

最后,我们打印出包含精确度、召回率和 F1 分数的分类报告。

*print(classification_report(y_true_list, y_pred_list)) ###################### OUTPUT ######################precision    recall  f1-score   support 0       0.71      0.85      0.77       124
           1       0.70      0.65      0.67       124
           2       0.82      0.73      0.77       124 accuracy                           0.74       372
   macro avg       0.74      0.74      0.74       372
weighted avg       0.74      0.74      0.74       372*

混淆矩阵

*print(confusion_matrix(y_true_list, y_pred_list)) ###################### OUTPUT ######################[[105  10   9]
 [ 33  80  11]
 [ 10  24  90]]*

我们从混淆矩阵中创建了一个数据框,并使用 seaborn 库将其绘制为热图。

*confusion_matrix_df = pd.DataFrame(confusion_matrix(y_true_list, y_pred_list)).rename(columns=idx2class, index=idx2class)fig, ax = plt.subplots(figsize=(7,5))         
sns.heatmap(confusion_matrix_df, annot=True, ax=ax)*

混淆矩阵的热图[图片[6]]

感谢您的阅读。欢迎提出建议和建设性的批评。😃

这篇博文是专栏— 如何训练你的神经网络 的一部分。

你可以在 LinkedIn 和 Twitter 找到我。如果你喜欢这个,看看我的其他博客。

2020 年 Pytorch vs Tensorflow

原文:https://towardsdatascience.com/pytorch-vs-tensorflow-in-2020-fe237862fae1?source=collection_archive---------4-----------------------

两种流行的框架是如何融合的

Pytorch 和 Tensorflow 是目前最流行的两个深度学习框架。学习和适应一个新的框架总是需要做大量的工作,所以很多人面临着从两个框架中选择哪一个的困境

这两个框架在设计、范式、语法等方面有很大的不同,但是它们已经发展了很多,都从对方那里吸取了好的特性,不再有那么大的不同。

很多比较这两者的网上文章都有点过时了,而且没有恰当地抓住当前的情况。事实上,PyTorch 的原始作者之一 Soumith Chintala 最近也在推特上说这两个框架现在非常相似。

简史

Tensorflow 来自谷歌,于 2015 年发布,PyTorch 由脸书于 2017 年发布。

Tensorflow 出现较早,因此在用户数量、采用率等方面领先,但 Pytorch 在过去几年中大大缩小了差距

来源:谷歌趋势

两者都致力于被称为张量的基本数据类型,它只不过是多维数组,适合高性能计算。两者都将计算表示为有向无环图,通常称为计算图。

功能比较:过去和现在

让我们来看看这些框架的一些重要方面,开始时的主要区别以及目前的情况。

编程 API

Tensorflow API 一开始非常神秘,感觉就像在学习一种新的编程语言,此外,由于其静态计算图方法,它也很难调试(下面将详细介绍)。另一方面,Pytorch (python) API 从一开始就非常 python 化,感觉就像编写原生 Python 代码一样,非常容易调试。

Tensorflow 使用 Tensorflow 2.0 对其 API 进行了重大清理,并将高级编程 API Keras 集成到主 API 本身中。这两者的结合极大地减少了过去编写张量流代码时必须承受的认知负荷:-)。

编程 API(tensor flow 和 PyTorch)实际上现在看起来非常相似,以至于两者很多时候都无法区分(见最后的例子)

计算图

计算图是两个框架之间的主要设计差异。

Tensorflow 采用了一种静态计算图方法,在这种方法中,我们定义了想要执行的计算序列,并为数据预留了占位符。之后,为了训练/运行模型,你输入数据。静态计算图对于性能和在不同设备(cpu / gpu / tpu)上运行的能力非常重要,但是调试起来非常麻烦。

另一方面,Pytorch 采用了动态计算图方法,在解释代码时逐行进行计算。这使得调试代码变得容易得多,并且还提供了其他好处——例如在像 RNN 这样的模型中支持可变长度输入。

快进到今天,Tensorflow 引入了通过其“渴望”模式构建动态计算图的工具,PyTorch 允许构建静态计算图,所以现在两个框架中都有静态/动态模式。

分布式计算

这两个框架都提供了在单/多/分布式 CPU 或 GPU 上运行的工具。在早期,让 Tensorflow 在多个 GPU 上工作是一件痛苦的事情,因为人们必须在多个设备上手动编码和微调性能,从那时起事情发生了变化,现在使用这两个框架进行分布式计算几乎毫不费力。

谷歌定制的硬件加速器张量处理单元(TPU)可以以极快的速度运行计算,甚至比 GPU 快得多,将于 2018 年供第三方使用。由于 Tensorflow 和 TPU 都来自谷歌,使用 Tensorflow 在 TPU 上运行代码要比使用 PyTorch 容易得多,因为 PyTorch 使用第三方库(如 XLA )在 TPU 上运行代码的方式有点混乱

部署/生产

与 PyTorch 相比,Tensorflow 在部署方面有很多优势,部分原因是其静态计算图方法带来了更好的性能,但也是因为有助于在云、浏览器或移动设备上快速部署的包/工具。这就是很多公司在生产时更喜欢 Tensorflow 的原因。

PyTorch 试图在 1.5+版本中用 TorchServe 来弥补这一差距,但它尚未成熟

代码比较

有趣的是,很多东西的 API 是如此的相似,以至于代码几乎无法区分。下面是 Tensorflow 和 Pytorch 的 MNIST 数字识别(计算机视觉中众所周知的“Hello World”问题)的核心组件的代码片段,试着猜猜哪个是哪个

完整的 Tensorflow 和 Pytorch 代码可在我的 Github Repo 获得

数据加载器

*# Download the MNIST Data* (x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
*# Add a channels dimension* x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(32)
test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)

哪一个是 PyTorch 代码-上面还是下面?

*# Download the MNIST Data and create dataloader* transform = transforms.Compose([transforms.ToTensor()])
xy_train = datasets.MNIST(**'./'**, download=True, train=True, transform=transform)
xy_test = datasets.MNIST(**'./'**, download=True, train=False, transform=transform)

train_ds = DataLoader(xy_train, batch_size=32, shuffle=True)
test_ds = DataLoader(xy_test, batch_size=32, shuffle=True)

好的,加载数据的方法看起来有点不同,但是我保证从现在开始会变得相似:-)

模型定义

*# Model Definition* class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.conv1 = Conv2d(in_channels=1, out_channels=32, kernel_size=3)
        self.flatten = Flatten()
        self.d1 = Linear(21632, 128)
        self.d2 = Linear(128, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = self.flatten(x)
        x = F.relu(self.d1(x))
        x = self.d2(x)
        output = F.log_softmax(x, dim=1)
        return output

哪一个是 PyTorch 码——上面还是下面?

*# Model Definition* class MyModel(Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.conv1 = Conv2D(filters=32, kernel_size=3, activation=**'relu'**)
        self.flatten = Flatten()
        self.d1 = Dense(128, activation=**'relu'**)
        self.d2 = Dense(10)

    def call(self, x):
        x = self.conv1(x)
        x = self.flatten(x)
        x = self.d1(x)
        output = self.d2(x)
        return output

实例化模型、损失、优化器

*# Instantiate Model, Optimizer, Loss* model = MyModel()
optimizer = Adam()
loss_object = SparseCategoricalCrossentropy(from_logits=True, reduction=**'sum'**)

哪个是 PyTorch 码——上面还是下面?

*# Instantiate Model, Optimizer, Loss* model = MyModel()
optimizer = Adam(model.parameters())
loss_object = CrossEntropyLoss(reduction=**'sum'**)

训练循环

for epoch in range(2):
    *# Reset the metrics at the start of the next epoch* train_loss = 0
    train_n = 0
    for images, labels in train_ds:
        with GradientTape() as tape:
            *# training=True is only needed if there are layers with different
            # behavior during training versus inference (e.g. Dropout).* predictions = model(images, training=True)
            loss = loss_object(labels, predictions)
            train_loss += loss.numpy()
            train_n += labels.shape[0]
        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    train_loss /= train_n

哪个是 PyTorch 码——上面还是下面?

for epoch in range(2):
    *# Train* model.train()
    train_loss = 0
    train_n = 0
    for image, labels in train_ds:
        predictions = model(image).squeeze()
        loss = loss_object(predictions, labels)
        train_loss += loss.item()
        train_n += labels.shape[0]
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
    train_loss /= train_n

真的很有趣(而且方便!)现在的 API 看起来是多么的相似。

结论

Tensorflow 和 PyTorch 是深度学习应用研发的两个优秀框架。他们在计算、管理底层硬件方面做着繁重的工作,并拥有庞大的社区,这使得站在巨人的肩膀上开发定制应用程序变得容易得多。

直到不久前,这两个框架还存在一些主要的差异,从那以后,它们都从对方那里吸取了好的特性,并在这个过程中变得更好。仍然有一些东西在一个中比另一个稍微容易一些,但是由于相似性的增加,现在在两者之间来回切换也比以前更容易了。

参考

  • https://github.com/moizsaifee/TF-vs-PyTorch
  • https://www.tensorflow.org/guide/effective_tf2
  • https://pytorch.org/docs/stable/index.html

深度学习 TensorFlow vs PyTorch

原文:https://towardsdatascience.com/pytorch-vs-tensorflow-in-code-ada936fd5406?source=collection_archive---------3-----------------------

下面是 TensorFlow 和 PyTorch 中编码神经网络的样子

cloudvisual.co.uk在 Unsplash 上拍照

Y 你可能想知道是学习 PyTorch 还是 TensorFlow (2.0)。如果是这样,希望这篇博文能有所帮助。这个想法并不是在这里给出一个绝对的答案,而是仅仅展示在这两种情况下开发和训练神经网络是什么样子。

在研究代码之前,需要知道一些事情:TensorFlow 和 PyTorch 都是专门为开发深度学习算法而设计的机器学习框架,这些算法具有处理大量数据所需的计算能力(例如,并行计算、GPU 上的训练等)。

来自谷歌的 TensorFlow 于 2015 年在 Apache 2.0 许可下发布。2019 年十月,TensorFlow 2.0 发布,据说是一个巨大的进步。它通常在 Python 中使用。另一方面,PyTorch 来自脸书,于 2016 年在类似的许可开源许可证下发布。顾名思义,它也是一个 Python 库。

模型定义

回到这篇博文的主要原因。我们的计划是在 TensorFlow 和 PyTorch 中实现一个简单的神经网络架构,以了解其中的一些相似之处和不同之处。

神经网络模型由三层组成:嵌入层→全局平均池层→密集层。就是基于这个例子。

数据集和预处理

这里使用的数据集由 40,000 条推文及其情绪组成(0 =负面,1 =正面)。首先,从 CSV 文件加载数据,并显示数据框的一些行,以了解数据的概念。

import numpy as np
import pandas as pddata = pd.read_csv("./data/tweets.csv", encoding=’utf-8')
data[:10]

为了对推文进行矢量化,我在这里使用了 Keras 的 tokenizer,但还有无数其他人可以做同样的事情,甚至更多。

下面是上面代码片段中发生的情况。我们给推文中 20,000 个最常见的单词分配一个整数,然后将推文转换成整数序列。我们用零填充较短的 1,并截掉较长的 1,强制序列长度为 42。最后,我们应该有一个 40,000 x 42 (tweets x sequence length)的矩阵。

张量流 2.0

import tensorflow as tf

创建张量流模型通常是使用 Keras 完成的。Keras 建立在 TensorFlow 之上,可以轻松快速地构建原型,因为它内置了许多层,每次从头开始编写代码会很繁琐,甚至令人望而却步。

在 Keras 中有三种方法可以建立神经网络模型。

1.模型子类化

您可以通过子类化tf.keras.Model类并在call方法中实现向前传递来创建您自己的完全可定制的模型。换句话说,在__init__()方法中定义了层,在call方法中定义了向前传递的逻辑。

使用这种面向对象方法的好处是,您可以在call方法中多次重用层,或者定义一个更复杂的正向传递。但是在这个例子中没有发生这样的事情,它只是一个线性的层叠。

2.功能 API

在函数式 API 中,给定一些输入张量和输出张量,您还可以实例化一个Model。这是一种用户友好的构建神经网络的方式,Keras 甚至推荐它优于模型子类化。使用这种方法,您实际上定义了一个层,并立即将前一层的输入传递给它。因此,在获得相同结果的情况下,它需要的编码稍微少一些。

3.顺序模型 API

顺序 API 是定义模型的最紧凑的方式,对于某些(简单的)神经网络来说足够了,通常只由几个公共层组成——这是一种可训练模型的捷径。它确实很方便,也很好用,但是如果你想实现更复杂的想法,它就太不灵活了。

不管您如何构建 Keras 模型,我都想向您展示两个功能。首先,调用model.summary()打印模型和参数数量的紧凑摘要,超级有用。

第二,通过调用tf.keras.utils.plot_model(),您可以获得模型的图形化摘要。然而,它在我的 Jupyter 笔记本上不起作用。所以我不能在这里给你看,很遗憾。

在 Keras 中训练神经网络

在训练 Keras 模型之前,必须通过运行model.compile()函数对其进行编译,这也是指定损失函数和优化器的地方。

model.compile(loss='binary_crossentropy', optimizer='Adam', metrics=['accuracy'])

Keras 模型有一个方便的 fit 函数用于训练模型(就像 Scikit-Learn 一样),它还负责批处理,甚至在运行中评估模型(如果您告诉它这样做的话)。

model.fit(x=X, y, batch_size=32, epochs=5, verbose=2, validation_split=0.2)

注意:可以将 Numpy 数组作为输入传递给 fit 函数,即使 TensorFlow (PyTorch 也是如此)只对 tensors 进行操作,这是一种类似的数据结构,但针对矩阵计算进行了优化。Keras 负责转换引擎盖下的阵列。

PyTorch

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

在 PyTorch 中有两种方法可以建立神经网络模型。

1.子类

类似于 TensorFlow,在 PyTorch 中,你子类化nn.Model模块,并在__init__()方法中定义你的层。唯一的区别是你在一个名为forward的方法中创建了向前传递,而不是调用。

注意:与 Keras 模型的区别:PyTorch 中只有一个平均池层,因此它需要有合适的内核大小,以便使它成为全局平均池。

2.连续的

PyTorch 还提供了一个Sequential模块,看起来几乎相当于 TensorFlow 的。

注意:我发现很多层和 PyTorch 的*nn.Sequential*不兼容,比如很多循环层(rnn,LSTMS 等。).事实上,PyTorch 根本不想实现顺序模块,因为它希望开发人员使用子类化。

在 PyTorch 中训练神经网络

PyTorch 模型没有预制的 fit 函数,因此训练循环需要从头开始实现。下面是 PyTorch 中一个典型的训练循环。

注意:为了批量处理数据,必须创建一个数据加载器(此处省略,参见 GitHub 代码)。数据加载器以字典格式一次返回一批数据。

训练循环的简短描述:对于每一批,我们计算损失,然后调用loss.backward()通过层反向传播梯度。此外,我们调用optimizer.step()来告诉优化器更新参数。关于如何训练 PyTorch 模型的更多详细描述,请参见此处。

如何学习 PyTorch & TensorFlow

以下是我所知道的关于 TensorFlow 和/或 PyTorch 入门的最佳资源。

学习 PyTorch 的一些资源

PyTorch 网站上的官方教程很棒,在我看来比 TensorFlow 的要好。在 Udacity 上还有一个非常好的免费 PyTorch 课程。

  • py torch 网站的教程
  • 在 Udacity 上用 PyTorch 介绍深度学习
  • 深度学习用 PyTorch (书)

学习 TensorFlow 2.0 的一些资源

TensorFlow 在 Coursera 上有一些非常好的课程,还有一本由 Keras 的创始人 Franç ois Chollet 写的很棒的书。TensorFlow 的,尤其是 Keras 的官方网站也是重要的来源。

  • TensorFlow 网站
  • Keras Dokumentation
  • 深度学习 TensorFlow 简介在 Udacity 上
  • TensorFlow in Practice 来自 Coursera 上的 deeplearning.ai
  • Franç ois Chollet 的 Python 深度学习(书)

那么哪个更好呢?

我真的觉得无法回答这个问题,因为到目前为止我只触及了表面。但是这里有一些我注意到的事情。

TensorFlow 非常像 Scikit-Learn,这要归功于它的 fit 功能,这使得训练模型变得超级简单快捷。但是即使你需要自己在 PyTorch 中构建每一个训练循环,我还是有点喜欢它,因为它让你更仔细地思考你在做什么。所以这不像是一个黑箱,这总是好的。我第一次学习 PyTorch,我认为这是一个非常好的主意。

注意:当然,TensorFlow 也允许你建立定制的训练循环,但它不是那么整洁。另外,PyTorch 有第三方库可以自动完成训练循环,所以这里的差别可能不会太大。

总而言之,在 TensorFlow 中从一个空白脚本到一个经过训练的神经网络肯定比在 PyTorch 中更容易——这主要是由于 TensorFlow 的fit方法。但是另一个不同点在这里也起了作用。我注意到 Keras 层通常不需要你指定输入维度,而在 PyTorch 中你需要更明确。很容易混淆尺寸,尤其是如果你想尝试不同的尺寸。除此之外,如前所述,Keras' model.summary()tf.keras.utils.plot_model()是非常有用的函数。没有标准的 PyTorch 函数(据我所知)可以做到这一点。

这些绝对是 TensorFlow 的一些加分项。但总的来说,两个库感觉很像。看起来他们已经通过互相学习和吸收对方的优点而融合了很多。这并不是一件坏事,只是他们成熟了许多的一个标志。因此,转换起来也很容易——所以不要担心选择了“错误”的库。当然,还有很多方面我没有考虑到,尤其是任何高级特性,比如并行计算、GPU 上的训练等等。

反正 2020 年 TensorFlow 和 PyTorch 的表现会很有意思。虽然 PyTorch 最近在研究人员中更受欢迎,但 TensorFlow 是该行业的领跑者。感谢 TensorFlow 和 PyTorch,深度学习比以往任何时候都更容易获得,更多的人会使用它。从来没有这么简单过。

代码也可以在 Jupyter 笔记本这里找到。

与数据科学家的问答

原文:https://towardsdatascience.com/q-a-with-a-data-scientist-1f872518315f?source=collection_archive---------32-----------------------

我从物理学到数据科学的转变

来源: Pixabay

在与其他几家公司的合作中,我最近参与了创建一个旨在提高员工数字素养的在线学习项目。在这种背景下,我在几周前就各种数据科学相关主题进行了采访/Q &会话。

作为这项计划的一部分,我想我也可以分享视频记录,以及我关于这些话题的谈话记录,供感兴趣的人参考。问答环节涵盖的主题包括我从物理学到数据科学的转变、作为一名数据科学家的工作是什么以及在该领域工作的许多其他方面。

希望你觉得有趣!

介绍

成绩单: 大家好!我是 Vegard,目前在一家名为 Axbit 的软件公司担任首席数据科学家。除此之外,我还在莫尔德大学学院兼职机器学习副教授。今天,我很高兴回答几个与数据科学相关的问题,数据科学是什么,以及在这个领域工作是什么样的。

问:你是如何成为数据科学家的?你的教育背景和工作经历是什么样的?

成绩单: 我是如何成为数据科学家的?首先,我认为与许多其他数据科学家相比,我的背景可能有点不同。我的职业生涯始于 2004 年的石油和天然气行业。那时,我是一名石油钻塔的自动化技术员,从事过程控制、仪器仪表、电子和各种传感器数据方面的工作。在这样做了几年后,我决定重返校园,最终获得了物理学硕士和博士学位。

但是,你如何从一个物理学学位成为一名数据科学家呢?在我的情况下,这是一种巧合。在我博士快结束时,我的研究兴趣开始转向神经科学,试图了解我们的大脑是如何处理信息的。我博士的主题是纳米物理学,但我们正在研究的一个研究主题是如何利用大脑如何处理信息的信息,然后尝试使用各种纳米尺度的积木来复制这一点,以构建一个“人工大脑”。(你也可以在下一篇文章的中了解更多信息)。

这是我对人工智能、机器学习、神经网络和所有这些酷术语感兴趣的途径。获得博士学位后,在我决定回到工业界之前,我曾短暂地在计算神经科学领域做了一名研究员。从那以后,我一直在许多不同的领域从事应用数据科学和机器学习的工作,如石油和天然气、能源、水产养殖,基本上是从数据中提取信息很重要的任何领域。

问:优秀的数据科学家需要具备哪些技能?

抄本: 试图理解数据科学是怎么一回事,以及成为一名优秀的数据科学家需要什么样的技能,这确实不是一个容易回答的问题。这也取决于你在什么样的公司工作,是小公司还是大公司。您可能听说过,要成为一名优秀的数据科学家,您需要具备不同领域的专业知识,例如统计或数学、编程技能、计算机科学、可视化、软件工程等……拥有所有这些不同领域的专业知识并不容易。这种罕见的“神奇独角兽数据科学家”可以做任何事情,可能并不代表我们大多数人实际做的事情。为了构建好的数据科学项目,你可能更需要一个具有互补背景的团队。有数据科学背景的人,有软件工程技能的人等等。,然后结合这一点,实际建立良好的解决方案。

当谈到什么样的技能或性格特征构成一名优秀的数据科学家时,我可能会强调好奇心。在我看来,数据科学就是使用科学方法从数据中提取信息。所以,为了从数据中提取知识,你需要有好奇心。你需要这种动力来理解产生这些数据的潜在过程是什么,以及我们可以从中提取什么样的知识。

我认为这也是我从物理学背景中发现的最有用的技能之一,这听起来可能不太像数据科学。但是,实际上他们之间有很多共同点。在我攻读物理学期间,我们使用算法和计算机来处理数据和提取有意义的信息,并试图通过这些观察来了解世界。这在很大程度上也是数据科学的意义所在。分析数据,提取信息,然后做出某种最终(希望)有用的预测。因此,以我的经验来看,也许我从自己的背景中获得的最重要的技能就是保持好奇心。我喜欢解决问题,喜欢理解事物,这也是我作为一名数据科学家发现非常有用的东西。

问:你最喜欢数据科学工作流程的哪个部分,为什么?

文字记录: 说到数据科学工作流程,从有了想要解决哪种问题的最初想法,到有了最终的解决方案,实际上有很多步骤。第一步,在我看来是至关重要的,是从确定正确的商业案例开始。你还需要找到对你要解决的问题有专业知识的人。这将有助于您确定这个问题是否真的值得解决,以及您正在尝试开发的解决方案实际上对最终用户有一些真正的价值。这是至关重要的第一步,因为如果建立一个花哨的机器学习模型不能给用户带来任何信息或价值回报,那就没有意义。

当谈到您需要经历的所有这些步骤时,最重要的(在我的经验中也是最耗费人力的部分)实际上是访问数据,并将其清理/转换为您可以实际开始探索它的格式。在一个典型的项目中,这可能会占用你大部分的时间。一旦你完成了这一部分,前进的道路也取决于你试图开发什么样的解决方案。它是某种仪表板可视化,还是需要服务于来自各种来源的实时数据的在线预测模型?这些选择将对未来的工作流程以及获得最终解决方案的必要步骤产生重大影响。

无论如何,当谈到所有这些步骤时,我觉得哪一个最有趣?在我的情况下,我会说,这可能是你已经能够收集所有数据的阶段,现在你已经准备好开始探索。所以,开始问正确的问题,探索我们实际上可以提取什么样的知识,这有助于我们解决什么样的问题?在我看来,好奇心驱动的数据探索阶段可能是最有趣的阶段。这也有助于我更好地掌握处理这些数据的最佳方式。

问:您想分享什么具体的使用案例/体验吗?

文字稿: 我前段时间在做的一个有趣的项目是和一家叫“Nofence”的公司合作的。因此,他们开发了一个物联网(或者在这种情况下实际上是“山羊互联网”)解决方案,当山羊外出放牧时,他们会戴上这个 GPS 项圈。这个项圈然后通过 GPS 定位和加速度计数据跟踪他们的运动模式。我们在这里想做的是,看看我们是否能从这些数据中提取一些有用的信息,这些信息可以告诉我们一些关于山羊健康状况的信息。

这实际上非常类似于你们很多人都知道的东西,也就是我们现在拥有的智能手表。他们主要通过心率、运动模式或睡眠周期等来跟踪我们的行为,然后试图预测我们的健康和幸福。所以,这基本上也是我们想为山羊做的。

举个例子,假设你的山羊在牧场上吃草,不小心伤了脚。当然,能够检测到这类事件,然后提醒农民山羊受伤是非常有用的信息。这不仅从经济角度来看是有用的,你可以更好地照顾你的山羊,而且从动物福利的角度来看也是有用的。

更具体地说,我们在这个项目中所做的是通过无监督学习来分析 GPS 和加速度计数据。这些数据代表一个时间序列,其中所有这些数据点都是随着时间的推移而累积的。这意味着你也有关于一天中时间的信息,是在半夜还是白天。然后,您可以开始对这些数据进行聚类,以确定不同的运动模式以及它们在一天中的变化,这反过来可以告诉您一些关于山羊健康状况的信息。

从技术角度来看,这是一个非常有趣的项目,分析山羊的物联网数据,但我认为这也是一个很好的例子,说明我们如何利用技术来改善动物福利。至少对我来说,从个人的角度来看,在这个项目上工作是非常值得的。

问:你如何看待数据科学的未来,你对一个有抱负的数据科学家有什么建议?

文字记录: 鉴于过去 10 年里发生了多少事情,预测数据科学的未来将会是什么样子,真的不是一件容易的事情。但是,如果我们从回顾开始,推动数据科学和机器学习向前发展的关键驱动因素之一是对大量数据的访问。我认为这是一个关键因素,随着所有这些通过物联网(以及越来越多的工业物联网)连接的设备,这一因素在未来也将继续增长。我认为我们将会看到越来越多的联网设备,它们可以共享信息和数据。当然,这也给了我们新的机会来利用这些数据提取有用的信息。

另一个关键因素是计算能力的进步,我认为这也将至少在未来几年内持续下去。所以,假设 10-15 年前,你可能需要一个超级计算设备来解决你的问题。而现在,要获得基本相同的计算能力,你只需花几千美元就能买到一台像样的游戏电脑。

另一个重要的贡献来自科技巨头,如谷歌、脸书、微软、亚马逊等。他们实际上非常开放,分享他们的研究成果,开源模型,并为我们这些用户提供工具。这对构建最先进的解决方案需要多少人员和资源产生了很大的影响。你可以利用这些科技巨头以前做过的事情,而不是从头开始创造一切。然后,您可以在此基础上进行构建,并使其适应您的领域,以便用有限的资源构建真正好的解决方案。这使得即使是非常小的公司也有可能为他们的特定领域开发非常令人印象深刻的机器学习解决方案。

展望未来,我还认为我们会看到越来越多的任务自动化,如建立机器学习模型。你可能听说过“AutoML”的这种趋势,其中你有自动化的软件为你建立机器学习模型。接下来还有这样的说法:它将取代数据科学家,夺走我们的工作等等。我并不担心我们会因为“AutoML”而丢掉工作。然而,我认为我们通常执行什么样的任务,以及我们花费最多时间做什么,将会有一点变化。

虽然有可能自动化的任务可能会被软件自动化,但我们更应该专注于我们更擅长解决的任务。根据我的经验,成功的数据科学项目最重要的部分不是机器学习模型或数据分析本身。而是理解你试图解决的问题的领域,提出正确的问题并识别正确的用例。这将帮助您构建真正有价值的解决方案,而不仅仅是从技术角度构建令人印象深刻的解决方案。

当谈到我认为哪种技能在未来最重要时,以我的经验来看,最关键的不能自动化的技能是数据科学的“科学家”方面。使用科学的方法,对数据告诉你的东西持怀疑态度,并提出正确的问题。这些任务可能不会被自动化,至少现在还不会。

总结:

如果你有兴趣了解更多与人工智能/机器学习和数据科学相关的主题,你也可以看看我写的其他一些文章。你会发现它们都列在我的中型作者简介中,你可以在这里找到。

  1. 什么是图论,你为什么要关心它?
  2. 用于图像分类的深度迁移学习
  3. 建造一个能读懂你思想的人工智能
  4. 人工智能和大数据隐藏的风险
  5. 如何使用机器学习进行异常检测和状态监控
  6. 如何(不)使用机器学习进行时间序列预测:避免陷阱
  7. 如何利用机器学习进行生产优化:利用数据提高绩效
  8. 如何向 AI 系统教授物理?
  9. 我们能否利用纳米级磁铁构建人工大脑网络?
  10. 供应链管理中的人工智能:利用数据推动运营绩效

我还在下面的研讨会演示中讨论了与 AI/机器学习相关的各种主题:“从炒作到现实世界的应用”。我希望这些资源对您有所帮助!当然,欢迎在下面的评论中提出任何反馈和意见。

q 学习

原文:https://towardsdatascience.com/q-learning-a4f1bcec58be?source=collection_archive---------18-----------------------

强化学习之旅

强化学习的早期突破——非策略时差控制方法

欢迎来到我的强化学习专栏,在这里我花了一些时间来回顾一些非常有趣的概念,这些概念围绕着用计算方法学习的本质。正如大多数学习一样,它与环境有互动,正如萨顿和巴尔托在强化学习:介绍中所说的那样,“从互动中学习是几乎所有学习和智力理论的基础思想。”

在我的上一篇文章中,我们回顾了在时差 (TD)学习中的政策控制方法,特别是——Sarsa,在这种情况下,我们在一个步骤之后朝着估计的回报更新我们的价值函数,从一个状态-动作对移动到下一个。今天,我们将学习 Q-Learning ,一种非策略 TD 控制方法。一些概念在之前的帖子里解释过,你可以在这里找到。我用来学习这个很酷的话题的资源链接会在文章的底部。

-学习的概念很容易理解:我们根据我们的行为政策选择下一步行动,但是我们也会考虑如果我们遵循我们的目标政策,我们可能会采取的替代行动。这允许行为和目标策略改进,利用动作值 Q(s,a) 。该流程的工作方式类似于不符合政策的蒙特卡罗方法。更新如下所示:

如您所见,我们的 Sarsa 更新被替换为q-值 Q(Sₜ,Aₜ】,在目标策略下朝着奖励加上备选行动的下一个状态的贴现值的方向更新了一点。这是之前的贝尔曼方程,但是现在对于 Q 来说,不需要重要性抽样。

正如我们对 MC 方法的偏离策略控制所做的那样,我们将使目标策略π贪婪关于我们的价值函数 Q(s,a) 在每一步:

我们的行为策略 bε-贪婪关于我们的价值函数 Q(s,a) ,允许一点探索,同时仍然大致遵循一条明智的道路。

通过将此代入我们的贝尔曼方程,我们可以看到Q-学习目标简化为以下内容:

这种算法也可以称为 SARSAMAX ,因为它选择在采取步骤后具有最大可用值的动作。这个简单的图表清楚地显示了这一点。

下面是算法伪代码:

http://incompleteideas.net/book/RLbook2018.pdf

像往常一样,让我们把这个例子翻译成另一个 OpenAI Gym 环境 Cliffwalking

与 gridworld 场景一样,目标是在给定特定起点( S )的情况下达到特定目标( G )。代理人可以在四个基本方向上移动,代理人每走一步,就会获得 -1 的奖励。因此,就像我们的 gridworld 示例一样,存在着更快达到目标的动机。这个环境的变化包括增加了悬崖,如果代理人踩到它所占据的任何空间,它会将代理人重置回左下角,并给予一个重大惩罚,奖励 -100 。从开始到结束显然有多条安全路线,但只有一条最优路线(巧合的是,这条路线是最“危险”的)。使用非策略时间差控制来控制我们的代理为我们提供了一些很好的结果——我们可以观察到良好而快速地收敛到最优策略,因为代理学习通过优化动作值函数来最大化回报。

对我来说,这些可视化的美妙之处在于,即使在找到最优目标策略之后,你也能看到ε-贪婪行为策略在起作用。当然,代理会继续采取行动,探索它认为是最佳的路径,偶尔会跌入悬崖,不得不重新设置。但这个过程让我想象了一个未来,这个智能体所在的世界经历了某种破坏性的重大变化,也许目标空间被移动到了环境的其他某个部分,或者悬崖以不同的方式分散。我们的代理可能会困惑一会儿,但肯定会再次找到最佳路径。

非常感谢你加入我对强化学习知识的讨伐。下一次,我们将看一看价值函数逼近,给我们一个更新价值函数的更复杂的方法!

资源

强化学习:简介萨顿和巴尔托

YouTube 上大卫·西尔弗的 RL 课程

强化学习 Github 由 dennybritz

OpenAI 旗下健身房

Q-Q 图解释

原文:https://towardsdatascience.com/q-q-plots-explained-5aa8495426c0?source=collection_archive---------0-----------------------

统计数据

“理解 Q-Q 图的概念”

来源:图片链接

在统计中,Q-Q(分位数-分位数)图在通过绘制两个概率分布的分位数来图形化分析和比较两个概率分布方面发挥着非常重要的作用。如果我们比较的两个分布完全相等,那么 Q-Q 图上的点将完全位于直线 y = x 上。

“画图,画线,你说你觉得好不好!”乔希·斯塔默

是的,就是这么简单。作为一名数据科学家,或者总的来说是一名统计学家,了解分布是否正态非常重要,这样才能对数据应用各种统计方法,并以更容易理解的可视化方式进行解释,这样 Q-Q 图就出现了。Q-Q plot 回答的最根本的问题是:

这条曲线是正态分布吗?

来源:维基百科 Q-Q 图正态分布

正态分布,但是为什么呢?

Q-Q 图用于寻找随机变量的分布类型,无论是高斯分布、均匀分布、指数分布还是甚至帕累托分布等。你可以通过 Q-Q 图的功效来判断分布的类型。一般来说,我们谈论正态分布只是因为我们有一个非常漂亮的概念68–95–99.7 规则 ,它完全符合正态分布,因此我们知道有多少数据位于平均值的第一标准差、第二标准差和第三标准差的范围内。因此,知道一个分布是否是正态分布为我们打开了一扇新的大门,让我们可以很容易地用数据进行实验。其次,正态分布在大部分范围广泛的自然事件中出现得非常频繁。

它是如何工作的?

我们在 x 轴上绘制理论分位数或基本上称为标准正态变量(均值=0 且标准差=1 的正态分布),在 y 轴上绘制我们想要确定其是否为高斯分布的随机变量的有序值。其从图上绘制的每个点给出了非常漂亮且平滑的直线状结构。

现在我们必须关注直线的两端。如果由这些点形成的曲线末端的点没有落在一条直线上,而是确实明显偏离这些位置,那么我们不能得出 x 轴和 y 轴之间的关系,这清楚地表明我们想要计算的有序值不是正态分布的。

如果图上绘制的所有点完全位于一条直线上,那么我们可以清楚地说这个分布是正态分布,因为它与标准正态变量均匀对齐,这是 Q-Q 图的简单概念。

来源:Sherrytowers Q-Q 图示例

偏斜 Q-Q 图

Q-Q 图也用于寻找一个分布的(不对称的度量)。当我们在 x 轴上绘制理论分位数,在 y 轴上绘制我们想要知道其分布的样本分位数时,我们会看到一个非常特殊的正态分布 Q-Q 偏度图。如果 Q-Q 图的底端偏离直线,但上端没有偏离,那么我们可以清楚地说,分布向左有一个较长的尾部,或者简单地说,它是左偏(或 负偏 )但是,当我们看到 Q-Q 图的上端偏离直线,下端沿着直线,那么曲线向右有一个较长的尾部,它是右偏(或

正态分布的左偏斜 Q-Q 图

正态分布的右偏斜 Q-Q 图

有尾 Q-Q 图

同样,我们可以通过简单地看它的 Q-Q 图来谈论分布的(衡量“”)峰度。具有厚尾的分布将使 Q-Q 图的两端偏离直线,并且其中心沿着直线,而薄尾分布将形成 Q-Q 图,其两端的偏差非常小或可以忽略,从而使其非常适合正态分布。**

正态分布的厚尾 Q-Q 图

正态分布的细尾 Q-Q 图

我们需要多少数据?

请注意,当数据点非常少时,Q-Q 图不会非常精确,它无法给出结论性的答案,但是当我们有足够数量的数据点,然后我们使用大型数据集绘制 Q-Q 图时,它会给我们一个重要的结果,以总结关于分布类型的任何结果。

Python 中 Q-Q 图的实现

下面是一个用 python 绘制 Q-Q 图的简单实现。

使用 statsmodels api 实现 QQ 绘图

使用 Scipy 库的 Q-Q 图的另一个实现。

使用 script.stats 实现 Q-Q 图

来源:StackExchange 输出 Q-Q 图

探索更多关于 Q-Q 图的信息

我强烈建议你去查看一下 Q-Q 图的维基百科页面,它有一个非常漂亮的解释,关于在它背后工作的数学的完整概念,这在这篇介绍性的文章中是相当令人震惊的。此外,看看 youtube 上由 Josh Starmer 制作的视频,它以一种很好的可视化方式展示了这个概念。

更多关于数据科学的文章由 Paras Varshney 撰写:

****** [## 如何评价 Python 中机器学习模型性能?

一个实用的方法来计算模型的性能和在 Python 中的实现,涵盖了所有数学…

medium.com](https://medium.com/@pv009/how-to-evaluate-machine-learning-model-performance-in-python-135b4ae27f7e) [## 如何建立一个能让你找到工作的数据科学投资组合?

学会制作一个关于你的强有力的作品集!

medium.com](https://medium.com/datadriveninvestor/how-to-build-a-data-science-portfolio-that-can-get-you-a-job-9f8d113739b3) [## k-最近邻解释-第 1 部分

KNN 算法背后的科学解释!

medium.com](https://medium.com/analytics-vidhya/k-nearest-neighbour-explained-part-1-5e5e9192050)

还有更多在 Paras Varshney 。

我希望你从这篇文章中学到了新的东西!

下载 Q-Q 剧情实现的 Jupyter 笔记本。

我写关于数据科学和机器学习的博客。有兴趣和我喝杯咖啡,在 上关注我,在 LinkedIn 上联系我。

谢谢你!******

在几秒钟内部署和共享您的 R 代码,而不是几周。

原文:https://towardsdatascience.com/qbits-workspace-a-new-online-editor-to-share-and-deploy-r-code-48c46f3394c2?source=collection_archive---------60-----------------------

QBits Workspace:一个新的无服务器数据科学编辑器,专为无缝部署和协作而构建。

今天,我们很高兴地宣布在浏览器中运行和部署 R 代码的 QBits 工作区。QBits 使您能够在无服务器的云环境中运行 R,并提供一种简单且经济高效的方式来大规模开发、运行、部署和共享数据科学项目,而无需管理服务器、软件设置和软件包安装。它们可以立即启动,部署速度非常快,并且可以处理各种数据科学项目。事实上,量子比特已经为我们的在线课程平台提供了动力,更令人兴奋的用例将很快出现。

我们创建 QBits 是为了让数据科学家的部署体验更加轻松。太多的项目失败了,因为数据科学家很难部署他们的成果。想一个简单的 ggplot2 的例子来重现汉斯·罗斯林精彩演讲中的gapminder情节:

library(ggplot2) 
library(dplyr) 
library(gapminder) gapminder_2007 <- filter(gapminder, year == 2007) 
gapminder_2007$pop <- gapminder_2007$pop/1e6 ggplot(gapminder_2007) + 
  geom_point(aes(x = gdpPercap, 
                 y = lifeExp, 
                 color = continent, 
                 size = pop), 
                 alpha = 0.7) + 
  scale_size_area(max_size = 15)

这个情节在当地运行良好。然而,为了在一些交互式 web 应用程序中再现绘图,允许用户通过例如year == 1952过滤数据集,我们需要

  1. 选择右边的操作系统创建一个 docker 容器。
  2. 安装正确的语言运行时,例如 R 4.0.0。
  3. 安装所有软件包依赖项(例如 ggplot2dplyrgapminder )
  4. 创建一个闪亮的应用程序或水管工 API,供交互式或编程使用。

您可以看到,即使对于这个简单的示例,部署开销也是相当大的。这导致了部署瓶颈,使许多数据科学项目未完成,并使数据科学家感到沮丧。与 QBits 最大的不同是,它们已经提供了正确的容器、语言运行时和包。你唯一要做的就是把你的代码放在上面。就是这样。

qp its 工作区提供了一个开发环境来快速开发您的定制 qp its。因为你已经在你的定制容器中工作,最终的部署只是几秒钟的事情,而不是几周。

下一步是什么

我们正在努力扩展编辑器以适应更多的工作流程并实现新的功能。进一步的更新将引入以下可能性

  • 创建您自己的量子比特
  • 添加和删除软件包(所有 15,000 多个 CRAN 软件包都可用)
  • QBit 部署,包括版本控制
  • …等等(是的,Python 也即将推出)

现在,去我们的游乐场试一试。

我们希望听到您的反馈和功能需求:

干杯,

你的 Quantargo 团队

最初发表于T5【https://www.quantargo.com】

QGIS 制图 101:使用 shapefiles 制作您的第一个分类地图

原文:https://towardsdatascience.com/qgis-mapping-101-importing-shapefiles-categorial-symbology-labeling-cities-simple-tools-5cba8aa2868b?source=collection_archive---------62-----------------------

继续学习如何导入 shapefiles、使用分类符号系统、标注城市以及使用简单的工具!

当调查一组新的 GIS 或环境数据时,在开始任何机器学习之前,绘制出您的要素并了解任何潜在的潜在关系通常是有用的。由于与 ArcMap 相似,QGIS 很快成为我的首选制图软件,最重要的是,它是免费的!通过我学习这个新工具的努力,我认为它可能对你们中的一些刚刚开始使用它的人有所帮助。如果你以前用过 QGIS,老实说,这个博客可能不适合你,因为它更侧重于非常基本的入门级技能。但是,如果这是你的第一张地图,我想你会发现这非常有用!

如果您还没有下载 QGIS,现在就开始下载吧,这里。在大学期间,我在本科研究中广泛使用了 ArcMap。现在我独自一人,没有那种奢侈,我在寻找一些便宜的,或者更好的免费的东西,让我的地图绘制技能达到标准。这时我偶然发现了 QGIS。它与 ArcMap 惊人地相似,我很高兴看到它提供的所有功能。用户界面也非常漂亮,我对这个开源软件非常满意。这样的话,就像任何事情一样,会有一个轻微的学习曲线,但是一旦你掌握了要点,事情就简单多了。今天,我希望通过使用来自各种资源的数据制作一个简单的北卡罗莱纳州地质图,来帮助你们减轻一些学习曲线。就这样,我们开始吧!

要开始跟随,我建议你从我的 GitHub,这里下载需要的数据。您将找到一个包含 shapefiles 的 zip 文件、一个包含北卡罗来纳州一些城市坐标的 CSV 文件,以及一个最终结果地图的 QGIS 文件。数据来自北卡罗莱纳 DoT 和美国农业部地理空间数据网关,两者都是非常有用的数据资源,包含各种 GIS 数据!

让我们进入正题。首先,打开 QGIS 并创建一个新项目。您应该会看到一个普通的白色屏幕。让我们添加第一点数据,地图形状文件。QGIS 很棒,因为它可以处理各种数据类型,其中最有用的是 shapefiles。在顶部工具栏选择图层→添加图层→添加矢量图层。从那里你会看到一个弹出窗口。在“源类型”下,保持“文件”处于选中状态,并让编码自动进行。在源下,单击带有三个点的按钮,导航到标记为“NC_County_Boundaries”的文件夹,并从文件列表中选择 shapefile。Shapefiles 用。在他们名字的末尾 shp。按“添加”,您将被要求指定特定的转换。有多个与此 shapefile 相关联,但 2/3 仅用于军事目的。鉴于这是一个博客,我们将使用它已经选择的一个,为平民设计的一个。点击应用,现在你应该看到一个美丽的北卡罗莱纳州县界地图。干得好!

我们的第一张底图!|(图片由作者提供)

现在你已经有了你的地图,并且知道了如何导入图层,让我们添加一张地质图。也是同样的思路:图层→添加图层→添加矢量图层。这一次,导航到“NC_geology”文件夹并选择您的 shapefile。一旦它被添加,它应该看起来像下面的图片。

NC 地质图|(图片由作者提供)

看一看,所有这些橙色并不真正有用,我们再也看不到县边界了。发生的事情是 QGIS 导入了新的 shapefile,但是把它放在县界地图上,它认为我们只是想看到区分不同地质类型的线。但是,如果我们可以对地图进行颜色编码,使每种颜色代表不同的岩石类型,这不是很酷吗?或者,我们甚至可以改变颜色来代表地质形成的时间!让我们采用第一个想法,看看我们能从地图上得到什么信息。

在地图的左侧,您应该会看到一个弹出框。在该框架中,应该有一个显示“层”的部分。在这里,您可以切换层的顺序,如果他们甚至显示在地图上!不过,现在我们需要了解地质图的图层属性,以便进行必要的调整。找到“NC_geology”图层,点击右键,选择“属性”。应弹出一个新窗口,其中符号系统选项卡打开。在页面的顶部,应该有一个下拉菜单,选择“单一符号”。点击它,并将其更改为“分类”。这将解决我们的一个颜色问题,并允许我们根据我们想要的任何属性进行颜色编码!就像我上面说的,我会根据岩石类型分类,但是你可以选择你喜欢的。对于值,我选择“岩石类型 _1”,保留符号为,并选择“光谱”为色带。你可以为色带选择任何颜色,但我认为光谱会很好,因为我对岩石类型的不同感兴趣,不希望颜色暗示它们之间存在某种连续体或关系(即选择蓝色渐变)。在弹出窗口底部附近,我们可以选择对列出的所有岩石类型进行分类,或者只对少数几种进行分类。同样,我对整个地图感兴趣,所以我所要做的就是按分类,QGIS 将自动为该属性的每个标注分配颜色。按应用→确定,现在你应该有一个精彩的彩色地图探索!

北卡罗来纳州地质图|(图片由作者提供)

这张地图很棒,但如果能看到主要城市在地图上的位置,以及它们是否可能与特定的地质类型相关联,那就更好了。幸运的是,这非常简单,只需导入一个带有纬度、经度和城市名称的. csv 文件。我已经在我的 GitHub 上提供过了,这里。

要导入. csv,请转到图层→添加图层→添加分隔文本图层。像往常一样导航到您的文件,给它一个有用的名称,这样您就知道它是什么了,对于我们今天的目的,这就是您所要做的!继续按添加,然后你应该会遇到一些非常小,很难看到城市所在的点。让我们改变这些,让他们更容易发现,并给他们标签,让我们知道他们是什么城市。右键单击城市图层,然后转到属性。我将使标记为白色,并将它们的大小更改为 5。然后,在弹出的左侧选择“标签”。按下下拉菜单并选择“单个标签”。QGIS 应该自动从。csv 作为值。我要先把样式改成粗体,字体大小改成 13。按应用,但停留在弹出窗口中。

如果你在地图上取一个顶点,这些名字并不在标记周围的理想位置。它们需要被上移,看起来更干净。通过更改距离,您可以将标签上移。在同一窗口中,点击“位置”选项卡。我把我的设置为 2mm,我对这个位置很满意,但是这取决于你!

我们标注的地质图|(图片由作者提供)

背景中有各种颜色,文字还是有点看不清。返回到您的地质图层属性,选择符号系统,然后按下“图层渲染”旁边的小箭头。这里我们可以改变层的不透明度。我把我的设置为 68%,这样我仍然可以很好地看到颜色,也可以阅读文字。你可能会注意到我们使用的基本地图,县界地图,可能会或可能不会用所有额外的线打扰你。通过选中和取消选中图层名称旁边的白色框,可以随意打开和关闭该图层。我发现保留这些层很有用,以防我想检查其他可能的关系,但有些人更喜欢为此制作一个全新的地图。每个人都有自己的想法,随着时间的推移,你会明白的!

我们关闭了县界地图的标注地质图|(图片由作者提供)

现在我们已经有了一个基本的地图轮廓,你可以利用这个时间做一些探索!一些有用的工具包括“识别”工具(位于上方工具条中,带有一个蓝色圆圈和一个“I”),“平移地图”工具(上方工具条,白色手形)和放大功能(上方工具条,放大镜)。识别工具对于快速查找特定区域的属性非常有用,只需确保您选择了感兴趣的图层,否则单击某个区域时将一无所获。

环顾地图,并使用可靠的识别工具,似乎没有任何明显的迹象表明,人们选择定居和创建这些城市是基于他们的岩石类型和地质。该死,谁会想到呢!

比方说,我们可能想要找出有多少不同的岩石类型,或者绘制了多少不同的剖面。使用方便的统计工具,我们可以很容易,很快找到答案!导航到上部工具栏,并单击带有求和符号的按钮。现在,检查屏幕的左侧,应该会有一个新的统计窗口。只需选择您想要了解更多信息的图层和属性,简单的统计分析就会出现!您还可以通过按下属性选择旁边的求和符号并创建一个简单的查询来获得特定的属性统计信息。

这是一个如何加载和操作地图的简单例子,但是可能性是无穷的!有这么多工具可以使用,所以尽情发挥吧。如果你打算创建一个以数据为中心的机器学习模型,做这种初步分析非常有益,可以节省你很多时间。如果你遇到任何有趣的事情,请在评论中告诉我,并关注更多 QGIS 博客!

QR 矩阵分解

原文:https://towardsdatascience.com/qr-matrix-factorization-15bae43a6b2?source=collection_archive---------5-----------------------

最小二乘法和计算(用 R 和 C++)

本·丹尼斯·谢弗

数据科学和矩阵分解

有几种矩阵分解,也称为分解,每个数据科学家都应该非常熟悉。这些很重要,因为它们有助于为我们使用的模型和算法找到实际计算和估计结果的方法。在某些情况下,因式分解的一种特殊形式是算法(例如 PCA 和 SVD)。在所有情况下,矩阵分解都有助于发展直觉和分析能力。

QR 因式分解就是这些矩阵因式分解中非常有用的一种,在数据科学、统计学和数据分析中有非常重要的应用。这些应用之一是计算最小二乘(LS)问题的解。

议程

  • 重述最小二乘问题
  • 介绍 QR 矩阵分解
  • 使用 QR 求解 LS
  • 用 R 和 C++实现 QR 计算并比较。

LS 问题

QR 矩阵分解允许我们计算最小二乘问题的解。我强调计算是因为 OLS 给出了正规方程形式的封闭解。这很好,但是当你想找到实际的数值解时,它们并不真的有用。

以下是最小二乘问题的概述。我们想解下面的方程

问题是我们无法求解 β 因为通常情况下如果我们的观测值多于变量X 没有一个逆,下面就不能做了:

相反,我们试图找到一些 β̂ 来解这个方程,虽然不完美,但误差尽可能小。一种方法是最小化下面的目标函数,它是 β̂ 的函数。

最小化这个平方偏差的和就是为什么这个问题被称为最小平方问题。对 β̂ 求导并设为零将会引导你到正规方程并为你提供一个封闭形式的解。

这是做这件事的一种方法。但是我们也可以只用线性代数。这就是 QR 矩阵分解的用武之地。

QR 分解

首先,让我们继续描述这个分解是什么。 QR 矩阵分解允许将矩阵表示为两个独立矩阵 QR 的乘积。

Q 中的一个正交矩阵和 R 是一个正方形的上/右三角矩阵。

这意味着

而且由于 R 是正方形,只要对角线上的条目没有零,也是可逆的。如果 X 的列是线性独立的,那么情况将总是如此。尽管如果数据中存在共线性,问题仍然会出现。撇开这个不谈,这个 QR 因式分解意味着一个矩形且不可逆的 X 可以表示为两个可逆矩阵!这一定会有用的。

利用 QR 分解求解 LS 问题。

既然我们已经知道了 QR 因式分解,一旦我们能够实际找到它,我们将能够以下面的方式解决 LS 问题:

因此

这意味着我们需要做的就是找到 R 的逆,转置 Q ,取乘积。这将产生 OLS 系数。我们甚至不需要计算方差-协方差矩阵及其逆,这是 OLS 解通常是如何提出的。

执行 QR 分解。

求一个矩阵的 QR 因子的方法是使用 Gram-Schmidt 过程先求Q* 。然后要求 R 我们只要把原矩阵乘以 Q 的转置即可。让我们继续使用在RC++中实现的函数来执行 QR 。稍后我们可以查看这些函数的内部,以便更好地了解正在发生的事情。*

计算系数

我正在加载两个函数。myQRRmyQRCpp使用格拉姆-施密特过程进行 QR 因式分解。一个函数写在R中,另一个写在C++中,并通过Rcpp加载到R环境中。稍后我会比较他们的表现。

*library(Rcpp)
library(tidyverse)
library(microbenchmark)sourceCpp("../source-code/myQRC.cpp")
source("../source-code/myQRR.R")*

让我们从一个小例子开始,我们模拟 yX ,然后使用 QR 分解来解决它。我们还可以仔细检查一下 QR 分解是否真的有效,并返回我们模拟的 X

这是我们模拟的反应变量。

*y = rnorm(6)
y## [1] 0.6914727 2.4810138 0.4049580 0.3117301 0.6084374 1.4778950*

这是我们将用来求解 LS 系数的数据。我们有 3 个变量可以支配。

*X = matrix(c(3, 2, 3, 2, -1, 4,
             5, 1, -5, 4, 2, 1,
             9, -3, 2 , -1, 8, 1), ncol = 3)
X##      [,1] [,2] [,3]
## [1,]    3    5    9
## [2,]    2    1   -3
## [3,]    3   -5    2
## [4,]    2    4   -1
## [5,]   -1    2    8
## [6,]    4    1    1*

现在我将使用myQRCpp找到QR

  1. 可以看到 R 确实是上三角。
*Q = myQRCpp(X)$Q
R = t(Q) %*% X %>% round(14)R##          [,1]     [,2]      [,3]
## [1,] 6.557439 1.829983  3.202470
## [2,] 0.000000 8.285600  4.723802
## [3,] 0.000000 0.000000 11.288484Q##            [,1]        [,2]        [,3]
## [1,]  0.4574957  0.50241272  0.45724344
## [2,]  0.3049971  0.05332872 -0.37459932
## [3,]  0.4574957 -0.70450052  0.34218986
## [4,]  0.3049971  0.41540270 -0.34894183
## [5,] -0.1524986  0.27506395  0.63684585
## [6,]  0.6099943 -0.01403387 -0.07859294*

2.这里我们可以验证 Q 事实上是正交的。

*t(Q)%*%Q %>% round(14)##      [,1] [,2] [,3]
## [1,]    1    0    0
## [2,]    0    1    0
## [3,]    0    0    1*

3.而那个 QR 确实把原来的 X 矩阵给还原了。

*Q %*% R##      [,1] [,2] [,3]
## [1,]    3    5    9
## [2,]    2    1   -3
## [3,]    3   -5    2
## [4,]    2    4   -1
## [5,]   -1    2    8
## [6,]    4    1    1*

现在,让我们计算实际系数。

*beta_qr = solve(R) %*% t(Q) %*% ybeta_qr##             [,1]
## [1,]  0.32297414
## [2,]  0.07255123
## [3,] -0.02764668*

为了检查这是否是正确的解,我们可以将计算出的 β̂lm函数给出的结果进行比较。

*coef(lm(y ~ -1 + ., data = data.frame(cbind(y,X))))##          V2          V3          V4 
##  0.32297414  0.07255123 -0.02764668*

显然,对于估计的系数,我们得到了完全相同的解。

实施快速反应计划

格拉姆-施密特过程

Gram–Schmidt 过程是一种计算正交矩阵的方法,该矩阵由正交/独立的单位向量组成,并且跨越与原始矩阵 X 相同的空间。

  • 这种算法包括选择一个列向量 X ,比如说 x1 = u1 作为初始步骤。
  • 然后,我们通过将X** 的下一列投影到它上面,比如说 x2 并从中减去投影U2 = x2 proj u1 x2,找到一个与 u1 正交的向量。现在我们有了一组两个正交的向量。在之前的一篇文章中,我详细介绍了这种方法的工作原理。**
  • 下一步以同样的方式进行,但是减去正交向量组中每个向量的投影和 英国

我们可以这样表达:

参考。一旦我们有了完整的正交向量集,我们只需将每个向量除以其范数,然后将它们放入一个矩阵中:

一旦我们有了 Q 我们就可以很容易地解出 R

用 R 和 C++实现

当然,R中有一个内置函数会帮你做 QR 矩阵分解。因为上面的 GS 算法本质上是迭代的,所以我决定在C++中实现它,这是一个很好的工具,并将其与一个等价的R函数进行比较。下面是我的R版本的样子:

**myQR = function(A){
  dimU = dim(A)
  U = matrix(nrow = dimU[1], ncol = dimU[2])
  U[,1] = A[,1]
  for(k in 2:dimU[2]){
    subt = 0
    j = 1
    while(j < k){
      subt = subt + proj(U[,j], A[,k])
      j = j + 1
    }
    U[,k] = A[,k] - subt
  }
  Q = apply(U, 2, function(x) x/sqrt(sum(x^2)))
  R = round(t(Q) %*% A, 10)
  return(list(Q = Q, R = R, U = U))
}**

它是非常字面的。for 循环里面有一个 while 循环,被调用的投影函数也是用R写的函数。

这是我的C++版本的样子。逻辑基本上是相同的,只是有另一个 for 循环来规范化正交列。

**// [[Rcpp::export]]
List myQRCpp(NumericMatrix A) {
  int a = A.rows();
  int b = A.cols();
  NumericMatrix U(a,b);
  NumericMatrix Q(a,b);
  NumericMatrix R(a,b);
  NumericMatrix::Column Ucol1 = U(_ , 0);
  NumericMatrix::Column Acol1 = A(_ , 0);

  Ucol1 = Acol1;

  for(int i = 1; i < b; i++){
    NumericMatrix::Column Ucol = U(_ , i);
    NumericMatrix::Column Acol = A(_ , i);
    NumericVector subt(a);
    int j = 0;
    while(j < i){
      NumericVector uj = U(_ , j);
      NumericVector ai = A(_ , i);
      subt = subt + projC(uj, ai);
      j++;
    }
    Ucol = Acol - subt;
  }
  for(int i = 0; i < b; i++){
    NumericMatrix::Column ui = U(_ , i);
    NumericMatrix::Column qi = Q(_ , i);

    double sum2_ui = 0;
    for(int j = 0; j < a; j++){
      sum2_ui = sum2_ui + ui[j]*ui[j];
    }
    qi = ui/sqrt(sum2_ui);
  }

  List L = List::create(Named("Q") = Q , _["U"] = U);
  return L;
}**

比较 R 与 C++的实现

除了上面的两个函数之外,我还有第三个函数,除了它调用了projC而不是proj之外,它和R完全相同。我把这个函数命名为myQRC。(projC写在C++,而proj写在R)。否则,我们有一个纯C++函数myQRCpp和一个纯R函数myQR

为了比较这三个函数执行 QR 因式分解的速度,我将它们放在一个函数QR_comp中,用相同的矩阵参数调用和计时。

**QR_comp = function(A){
  t0 = Sys.time()
  myQR(A)
  tQR = Sys.time() - t0
  t0 = Sys.time()
  myQRC(A)
  tQRC = Sys.time() - t0
  t0 = Sys.time()
  myQRCpp(A)
  tQRCpp = Sys.time() - t0

  return(data.frame(tQR = as.numeric(tQR), 
                    tQRC = as.numeric(tQRC),
                    tQRCpp = as.numeric(tQRCpp)))
}**

我们可以通过m随机矩阵在n的网格上比较它们的性能。这些矩阵是在调用QR_comp函数时生成的。

**grid = expand.grid(n = seq(10, 3010, 500), 
                   m = seq(50, 600, 50))tvec = map2(grid$n, 
            grid$m, 
            ~QR_comp(matrix(runif(.x*.y), ncol = .y)))**

最后,我们可以直观地评估这些变化。

**plotly::ggplotly(
bind_rows(tvec) %>%
  gather("func","time") %>%
  mutate(n = rep(grid$n, 3),
         m = rep(grid$m, 3)) %>%
  ggplot(aes(m, n, fill = time)) + 
  geom_tile() + 
  facet_grid(.~func) +
  scale_fill_gradientn(colours = rainbow(9)) +
  theme(panel.background = element_blank(),
        axis.ticks.y = element_blank(),
        axis.text.y = element_text(angle = 35, size = 5),
        axis.text.x = element_text(angle = 30, size = 5)), width = 550, heigh = 400)**

显然,涉及的C++越多,计算 QR 因式分解的速度就越快。all C++ 函数在不到一分钟的时间内求解最多有250列和3000行或600列和500行的矩阵。R功能慢 2-3 倍。

结论

QR 只是一种矩阵分解,LS 只是 QR 的一种应用。希望上面的讨论证明了线性代数对于数据科学是多么重要和有用。在未来,我将讨论 QR 分解的另一个应用,并讨论一些其他重要的分解,如特征值和 SVD 分解。

另外,你可以看出我正在使用RC++来计算实现这些方法。我希望这是有用的,并将激励像我一样的其他R用户学习C++Rcpp,并在他们的工具包中放入它们,使他们的R工作得更加强大。

感谢阅读!

QRNN:变形金刚的潜在竞争对手

原文:https://towardsdatascience.com/qrnn-a-potential-competitor-to-the-transformer-86b5aef6c137?source=collection_archive---------23-----------------------

用准 RNN 训练更快的 RNNs

布拉登·科拉姆在 Unsplash 拍摄的照片

递归神经网络(RNNs)在序列建模业务中已经有很长时间了。但是 RNNs 很慢;他们一次处理一个令牌。此外,递归结构增加了对完整序列的固定长度编码向量的限制。为了解决这些问题,类似 CNN-LSTM、Transformer、QRNNs 这样的架构应运而生。

在本文中,我们将讨论在论文“准递归神经网络中提出的 QRNN 模型它本质上是一种把卷积加到递归上,把递归加到卷积上的方法。当你继续阅读这篇文章时,你会得到这个。

长短期记忆(LSTM)

LSTM via QRNN 论文

LSTM 是 RNNs 最著名的变体。红色模块是线性函数或矩阵乘法,蓝色模块是无参数的元素式模块。LSTM 单元应用门控功能(输入、遗忘、输出)来获得输出和称为隐藏状态的存储元件。这个隐藏状态包含整个序列的上下文信息。由于单个载体编码了完整的序列,LSTMs 不能记住长期依赖性。此外,每个时间步长的计算取决于前一个时间步长的隐藏状态,即 LSTM 一次计算一个时间步长。因此,计算不能并行进行。

到目前为止,Colah 的博客是对 RNNs 最好的解释之一(在我看来)。如果你有兴趣了解 LSTM 背后的数学,可以考虑读一读。

卷积神经网络(CNN)

CNN via QRNN 论文

而 CNN 则捕捉空间特征(多用于图像)。红色块是卷积运算,蓝色块是无参数汇集运算。CNN 使用核(或过滤器)通过滑动窗口捕捉特征之间的对应关系。这克服了固定长度的隐藏表示(以及因此的长期依赖性问题)以及 rnn 缺乏并行性的限制。但是,CNN 没有考虑序列的时间性质,即时间不变性。池层只是减少了通道的维数,而没有考虑序列顺序信息。

深度学习卷积运算指南是 DL 中涉及卷积运算最好的论文之一。值得一读!

准递归神经网络

QRNN via QRNN 论文

QRNN 解决了这两种标准架构的缺点。它允许并行处理和捕获长期依赖关系,如 CNN,还允许输出依赖于序列中的标记顺序,如 RNN。

因此,首先,QRNN 架构有 2 个组件对应于 CNN 中的卷积(红色)和汇集(蓝色)组件

卷积分量

卷积组件的工作原理如下:

  1. 形状的输入顺序: (批量 _ 大小,顺序 _ 长度,嵌入 _ 尺寸)
  2. 【hidden _ dim】形状的一个‘bank’:(batch _ size,kernel_size,embed_dim) 各一个。
  3. 输出的是一个形状序列: (batch_size,sequence_length,hidden_dim) 。这些是序列的隐藏状态。

卷积运算并行应用于序列和小批量。

为了保持模型的因果关系(即,只有过去的表征应该预测未来),使用了一个称为掩蔽卷积的概念。也就是说,输入序列通过'kernel _ size-1 '零填充到左边。所以,只有'sequence _ length-kernel _ size+1 '过去的记号才有可能预测给定的记号。为了更好的直观,请参考下图:

作者的掩蔽卷积动画

接下来,我们基于我们的池函数(将在下一节讨论)使用额外的内核库,来获得像 LSTM 中那样的门控向量:

通过 QRNN 纸输出卷积组件

这里, ******* 是卷积运算; Z 是上面讨论的输出(称之为'输入门输出); F 是使用额外内核库 W_f 获得的“遗忘门”输出; O 是使用额外内核库 W_o 得到的'输出门输出。

趣闻:如上所述,这些卷积仅应用于过去的'sequence _ length-kernel _ size+1'记号。因此,如果我们取内核大小 = 2,我们得到类似 LSTM 的方程:

通过 QRNN 纸张实现类似 LSTM 的输出

共用组件

总的来说,池是一个无参数的函数,它从错综复杂的特性中捕捉重要的特性。在图像的情况下,通常使用最大池和平均池。然而,在序列的情况下,我们不能简单地取特征之间的平均值或最大值。它需要有一些复发。因此,QRNN 论文提出了受传统 LSTM 单元中元件式门控结构启发的池功能。它本质上是一个无参数的函数,将在时间步长上混合隐藏状态。

最简单的选项是“动态平均池”,它仅使用遗忘门(因此称为f-池 ):

动态平均池(f-池 )通过 QRNN 纸

其中 是逐元素矩阵乘法。

如您所见,它或多或少是以遗忘门为参数的输出的'移动平均值'。

另一种选择是使用遗忘门和输出门(因此, fo-pooling ):

fo-pooling 经 QRNN 纸

或者池可以另外具有专用输入门( ifo-pooling ):

ifo-poolingviaQRNN 论文

正规化

在检查了各种经常性辍学方案后,QRNN 使用了一个名为' zone out '的扩展方案。'它本质上是在每个时间步长选择一个随机的通道子集进行丢弃,对于这些通道,它只是将当前通道值复制到下一个时间步长,而不做任何修改。

方便地说,这相当于将 QRNN 的遗忘门通道子集随机设置为 1,或者对 1 f 施加压差。

— QRNN 论文

因此,

通过 QRNN 论文退学

来自 DenseNet 的想法

DenseNet via DenseNet 纸

DenseNet 架构建议在每一层和它之前的每一层之间有跳跃连接,这与在后续层上有跳跃连接的惯例相反。因此,对于具有 L 层的网络,将有 L(L - 1) 个跳过连接。这有助于梯度流动和收敛,但会占用二次空间。

带 QRNN 的 seq2seq

QRNN seq2seq via QRNN 论文

在常规的基于 RNN 的 seq2seq 模型中,我们简单地用编码器的最后隐藏状态初始化解码器,然后针对解码器序列进一步修改它。好吧,我们不能对循环池层这样做,因为在这里,编码器状态不会对解码器的隐藏状态有太大贡献。因此,作者提出了一种改进的解码器结构。

来自编码器的最后隐藏状态(最后令牌的隐藏状态)被线性投影(线性层),并且在应用任何激活之前被添加(广播,因为编码器向量更小)到解码器层的每个时间步长的卷积输出:

解码器层通过 QRNN 纸

~ 表示属于编码器; V 是应用于最后一个编码器隐藏状态的线性权重。

注意力

注意力仅应用于解码器的最后隐藏状态。

通过 QRNN 论文关注 QRNN

其中 s 是编码器的序列长度, t 是解码器的序列长度, L 表示最后一层。

首先,将解码器的 未选通 最后一层隐藏状态与最后一层编码器隐藏状态进行点积。这将产生一个形状为 (t,s) 的矩阵。Softmax 接管了 s ,用这个 分数 获取注意力总和,形状【t,hidden _ dim】k_tk_t 然后与 c_t 一起使用,为解码器获得 门控 最后一层隐藏状态。

结果

通过 QRNN 纸进行速度比较

QRNN 的计算速度比 LSTM 架构快 17 倍,在某些情况下,其结果与前者相当,甚至略好于后者。

最近,基于 QRNN 的模型 pQRNN 仅用 1.3M 个参数就在序列分类方面取得了与 BERT 相当的结果(与 BERT 相反,它有 440M 个参数):

pQRNN vs BERT via 谷歌人工智能博客

结论

我们深入讨论了新颖的 QRNN 架构。我们看到了它如何将递归添加到基于卷积的模型中,从而加速序列建模。QRNN 的速度和性能肯定会让我们重新考虑一些 NLP 任务的变压器。

参考

[## 准递归神经网络

递归神经网络是一个强有力的工具,用于建模序列数据,但每个时间步的依赖…

arxiv.org](https://arxiv.org/abs/1611.01576)

人工智能的质量保证

原文:https://towardsdatascience.com/quality-assurance-for-artificial-intelligence-d935fc6b238?source=collection_archive---------23-----------------------

数据的一般思想和质量保证

这是我关于如何测试利用机器学习的系统系列的第一篇文章。

关于人工智能如何工作你需要知道的事情

下面介绍一个例子 AI 项目来解释相关术语(数据科学的人可以跳过)。想象你经营一家面包店。所以你想知道每天早上要烤多少面包。为此,您可以使用以前收集的历史数据,例如:

仅通过日期进行预测几乎是不可行的,但是您注意到您的需求取决于:

  • 天气条件(在寒冷或下雨的天气,很少有人出去购物)
  • 月(在夏天,您的客户去度假)
  • 一周中的某一天(因为周日不营业,所以人们会在前后购买更多商品)
  • 附近的一些事件(想象你的面包店靠近一个足球场)

因此,您添加了包含每天值的列。您将预测的列称为“目标,将用于预测的列称为“特征”。

问题

所以你把你的数据交给一个自称为数据科学家的人,让他们创造一些预测需求的东西——数据科学家称之为“模型”。你可以把一个模型想象成一棵决策树,它将由一个算法使用你的数据来创建。在模型交付后,你需要知道你是否可以信任它(盲目地)并在日常工作中使用预测。

为了回答这个问题,我们将重点放在方法部分,并将模型本身视为一个黑盒。这应该给 QA、经理、开发者和基础设施人员提供指导,他们需要确保使用人工智能的软件的质量。我确信,如果你学习了人工智能模型是如何开发的剧本,那么对质量保证和常识的理解是足够的先决条件。你不需要额外上数学、统计或深度学习的课。

我还想为数据科学家提供想法和最佳实践,因为他们通常必须自己确保数据和模型的质量。特别是如果你的经验主要来自于 ML 课程、研究或 Kaggle 挑战,它们只涵盖了模型生命周期的一部分,你可能会对在这个行业工作时需要的其他 QA 技术感兴趣。

AI 模型生命周期

从生命周期的角度来看,人工智能项目通常包括 3 个阶段:

  • 收集和准备数据
  • 训练模型
  • 在生产中部署模型以进行预测、监控模型

因此,QA 根据阶段执行不同的检查或审查:

  • 将用于模型训练的数据质量是否足够好?这就是这篇博文的主题。
  • 投入使用的构建模型是否具有足够的质量?这将是下一篇文章的主题。
  • 在生产中运行时,模型是否能保持足够的质量?我会在这个系列的最后一篇中处理这个问题。

一些 QA 步骤归结为将数据或模型的指标与预定义的值或阈值进行比较的检查。其他的则是分析或评论,需要时间、体力、领域知识和常识。

关于自动化

一旦您的模型上线,即使方法和工具保持不变,您也不需要重新培训它的可能性接近于零。除了技术变化和新的需求,还有一些人工智能特有的原因:

  • 以后你很可能会获得更多或者更高质量的数据。由于数据量和质量是你在人工智能中的主要优势,你不会错过这个机会。
  • 如果你的挑战是关于预测,你需要今天/本周/本月的数据来预测明天/下周/下个月的事情。所以你需要用最近的数据重新训练模型。
  • 随着时间的推移,世界在变化,随着时间的推移,你的模型曾经很好地反映了现实。想想估计房价和不断变化的房地产市场。
  • 您的系统的用户很可能会发现您的模型做出错误决策的条件。尤其是如果你的系统值得欺骗(比如保险索赔的自动处理),期待坏人会尝试它。所以你需要调整你的模型来消除或者至少减少这种影响。

模型更新的频率取决于用例,可能从几小时到几年不等。这个频率对从原始数据到新的生产阶段模型的流程的自动化水平有很大的影响。您可以用不同的方式实现这种自动化,这是一个单独的主题,不在这里考虑。但是,在任何情况下,你必须为 QA 检查提供相同级别的自动化。这样,您就使 QA 成为模型开发过程中不可避免的一步。

假设您有一个每夜运行的脚本流,它从不同来源收集数据,开始模型训练,用新模型构建服务,并替换旧版本的服务。在这种情况下,对数据和模型质量的检查必须是作为您的流程的一部分运行的脚本,如果没有满足定义的条件,就会发出红色标记。

检测数据收集错误

在极少数情况下,如我们的小面包店,模型中使用的所有数据可能来自同一个来源。对于至少是中等规模的公司来说,用于预测的数据可能分布在多个系统中。在一个普通的电子商务公司中,CRM 知道客户的人口统计数据,支付系统知道他们的购买行为,web 跟踪系统知道他们搜索什么,库存系统知道你提供的产品,此外,你想使用外部服务的天气数据。因此,在数据科学触及数据之前,大部分数据都经历了一个非同寻常的多步过程。无论这条路径涉及手动操作还是使用 ETL 工具完全自动化,都可能出错。并且源系统也可能包含错误。所以在训练模型之前,我们需要一套“入口检查”。

让我们假设数据收集运行已经完成(我们认为它是一个黑盒,将细节留给数据工程师),并且您有一个新更新的客户表或一个包含图像的文件夹。

最简单的检查类型是基于统计的。一旦实施了数据收集过程,手动验证最终结果的正确性,即通过跟踪源和目标系统中选定的条目(如果您的数据工程同事已经这样做了,那么您很幸运)。一旦验证了这一点,就要计算对模型训练很重要的实体(帐户、图像、客户评论、音频文件)。引入阈值,确定必须有多少个阈值可用,以假设数据收集工作正常,没有重大错误。

考虑计算进一步的统计数据,从长远来看,这些数据应该不会发生重大变化:您的帐户如何按性别分布,平均审查长度是多少,最常见的图像类型是什么,等等。对于每个统计数据,定义一个阈值或范围,该阈值或范围将在模型训练之前进行验证。

您可能想知道有多少记录(至少是一个数量级)足以训练一个足够好的模型。仅仅从问题描述和数据中很难得到这个数字。先来判断训练后模型的准确性;我们将在下一篇博文中解决这个问题。

检测数据质量问题

下一步,想想你是否真的想在你的模型中使用所有可用的记录。在训练模型之前,您最有可能修复或删除的条目有:

  • 复制
  • 为测试创建的条目
  • 无意中输入了错误日期的条目(如出生年份 1897,而不是 1987)
  • 覆盖您的模型不应该服务的用例的条目(比如公司帐户,如果您想要一个仅用于个人帐户的模型)

进行一些探索性的数据分析。对于模型中使用的每个列/特征,您应该知道它的确切含义以及它可以取什么值。也许会有一份文档,也许是最新的。但是不要假设一切都是正确的,并且与文档保持一致。

对于像“年龄”这样的数字列,构建一个直方图。对于像“教育”这样的分类列,列出所有可能的值。要发现重复项,请检查技术键的唯一性,也可以检查一些功能集的唯一性(如客户的姓名和地址)。

另一种更高级的发现奇怪记录的方法是“异常检测”想象一个 21 岁的客户,拥有博士学位和 10 年的工作经验。这 3 个数据点本身都很好,但它们的组合是不太可能的;我们可以假设有人进入了错误的年龄。像这样发现异常是一个纯粹的机器学习主题,有很多关于它的伟大论文。

寻找解释

如果您的数据环境足够陈旧,您可能会发现许多问题。对他们中的一些人来说,解释会很容易找到或者显而易见——但很可能不是对所有的问题。很多类似“带有这个日期的记录看起来很奇怪,我应该修复它还是过滤掉它还是保持原样?”我知道的唯一方法就是从你的椅子上站起来,去问一些问题。好的候选人是:

  • 处理边缘案例的同事:客户支持、运营
  • 非常了解系统使用方法的同事:客户支持(再次),QA
  • 刚进公司不久的同事

修复数据质量问题

我们只是简单地触及这个主题,因为它主要是关于数据科学,而不是关于 QA。一旦发现问题,考虑从源头上解决它——重复的用户记录应该直接在 CRM 中修复,错误的支付汇总必须在 ETL 部分修复。但是您仍然应该假设上游系统有数据问题(一个将被修复,但另一个将通过实现一个新特性而出现),并对它们进行检查和/或修复。

如果问题有一个解释,但不可修复(在短期内),这是常见的情况,您应该修复它,以防止错误数据对模型的影响。修复通常是关于

  • 过滤记录:例如,您可以通过 ID 删除重复的记录
  • 从另一个来源丰富缺失的数据点:例如,缺失的性别可以通过在字典中查找名字来猜测
  • 输入缺失数据点:即,如果年龄缺失,则使用数据集的中值年龄

在任何情况下,清理每个数据问题并不是继续进行的强制要求;您的数据子集也可以用于训练。因此,首先解决以下问题:( 1)关于重要特性;( 2)经常出现;( 3)容易解决——然后训练你的候选模型。如果它不能表现出足够的质量,你可以追求更难的问题。

接下来是什么

现在,我们已经完成了数据集的 QA,可以继续构建模型了。模型的 QA 将是我们下一篇文章的主题。

图片来自生物多样性遗产图书馆

使用 Python 制作质量控制图表

原文:https://towardsdatascience.com/quality-control-charts-guide-for-python-9bb1c859c051?source=collection_archive---------3-----------------------

面向工业工程师的 Python

使用 Python 库创建质量控制图

图片由 Sara Kurfe 拍摄,可在 Unsplash 获得

质量控制图

质量控制图是工程师监控一个过程是否在统计控制下的重要工具。它们有助于可视化变化,发现并纠正出现的问题,预测预期的结果范围,并分析特殊或常见原因导致的流程变化模式。质量控制图常用于精益六适马项目和 DMAIC 项目的控制阶段,被认为是过程改进的七个基本质量工具之一。

要使用的适当控制图由数据类型(即测量)、缺陷数量和样本大小决定。下面的决策树分别说明了每个控制图的条件:

质量控制图决策树

控制极限

在质量控制图中,控制极限是绘制在中心线(即平均值)上方和下方的线,其功能是作为识别信号(即超出控制极限的点)的阈值,并帮助确定过程是否处于统计控制之下。它们是由特定于每种类型控制图的公式定义的,这些公式包括常数,这些常数根据样本大小而变化。下表列出了这些常数:

控制极限常数

对于以下示例,将使用 Python 从头开始构建每种类型的质量控制图。同样,将提供每个控制图的简要描述。让我们来看看 Python 代码。

连续数据的控制图

x 图和 mR 图

x 图(也称为个体图)和 mR 图用于根据给定时间内采集的个体样本监控过程的均值和变异。为了将 mR 图与 x 图一起使用,样本大小 n 必须等于 1。在 x 图上, y 轴显示平均值和控制限值,而 x 轴显示样本单位。在 mR 图上, y 轴显示移动范围的平均值和控制极限,而 x 轴显示样本单位。

x 条形图和 R 图

x-bar 和 R-chart 用于根据给定时间内采集的样本监控过程的平均值和变化量。为了将 R 图与 x 条形图一起使用,样本大小 n 必须大于 1 且小于 11。在 x-条形图上, y 轴显示总体平均值和控制限值,而 x 轴显示样本组。在 R 图上,y-轴显示范围的平均值和控制限,而x-轴显示样本组。

x 条形图和 s 图

同样,x-bar 和 s 图用于根据给定时间内采集的样本监控过程的均值和变化。为了将 s 图与 x 条形图一起使用,样本大小 n 必须大于 10 个单位。在 x-条形图上, y 轴显示总平均值和控制限,而 x 轴显示样本组。在 s 图上,y-轴显示标准偏差平均值和控制限,而 x 轴显示样本组。

离散数据的控制图

c 图

c 图用于监控尺寸为 n 的固定样本中缺陷总数y 轴显示每个样品的不合格数量,而 x 轴显示样品组。

u 形图

u 形图用于监控尺寸为 n 的不同样品中每单位的缺陷总数;它假设单元可以有一个以上的缺陷。 y 轴显示每个单元的缺陷数量,而 x 轴显示样本组。

p 图

p 图用于监控不同尺寸 n 样本中不合格品单位的比例;它基于二项式分布,每个单位只有两种可能性(即有缺陷或无缺陷)。 y 轴显示不合格单位的比例,而 x 轴显示样本组。

np 图表

np-chart 用于监控大小为 n. 的固定样本中不合格单位的计数y轴显示不合格单位的总数,而 x 轴显示样本组。

总结想法

质量控制图是分析流程稳定性和获取重要统计信息的重要工具,可用于精益六适马和 DMAIC 项目的流程改进。质量和工业工程师必须能够准确地解释它们,以识别可能导致潜在不合格的不良趋势,从而采取预防措施而不是纠正措施,从而减少废料和浪费。

本指南涵盖了如何使用多个 Python 库从头构建质量控制图的分步教程。考虑到 Python 在专业人士和学者中的高受欢迎程度,Python 是一个很好的工具,能够为统计目的构建质量控制图表。虽然也有其他程序和软件可以构建它们(例如 Minitab、R、Microsoft Excel),但质量和工业工程师应根据他们的编码和分析技能决定使用哪一种,同时符合组织的目标和客户的规格。

如果你觉得这篇文章有用,欢迎在 GitHub 上下载我的个人代码。也可以直接发邮件到rsalaza4@binghamton.edu找我,在LinkedIn找我。有兴趣了解工程领域的数据分析、数据科学和机器学习应用的更多信息吗?通过访问我的媒体 简介 来探索我以前的文章。感谢阅读。

——罗伯特

数据的质量、完整性和可利用性

原文:https://towardsdatascience.com/quality-integrity-and-exploitability-of-data-9f75c39d384d?source=collection_archive---------56-----------------------

上的数据值

价值创造的三个关键驱动因素

在 关于数据价值的这一系列文章的第一部分中,我们看到数据不仅具有内在的市场价值,而且还“间接”参与为公司创造价值,考虑到数据的使用方式及其对服务质量、卓越运营和决策的影响。

但是,仅仅理解用途(“什么”)和相关的业务价值(“为什么”)是不够的,我们还需要定义如何最大限度地从数据中创造价值。我考虑两种不同的策略:

  • 数据管理和治理策略,主要是保存其“名义”或“单位”价值,以便不限制其使用。
  • 分析策略,特别是利用超出其运营用途的数据,并通过合并不同的数据源、分析、汇总、可视化等来创造超出其“单位”价值的价值。

这两个战略包括实施工具和技术、实践和流程,旨在引导三个关键驱动因素创造价值,这次不是从用户方面,而是作为实现价值的手段:

  1. 质量,在数据创建期间
  2. 完整性,在存储和复制数据期间
  3. 可开发性,在使用数据期间

事实上,说数据是通过计算机应用程序产生的并不意味着它是可用的,我们都知道应用程序包含不正确或不完整的数据(质量和/或完整性问题),或难以用于分析的数据(我不谈论甚至不存在的数据,这些应用程序甚至不允许获取)。

因此,本系列的第二部分将致力于这三个特征,它们是从数据中创造价值的关键驱动因素,它们的影响往往不为人所知,有时甚至被低估。

照片由埃维拉尔多·科埃略在 Unsplash 上拍摄

质量和诚信,这两个概念经常被相互混淆

数据质量和完整性对依赖于数据使用的流程有直接影响,因此也影响服务质量、卓越运营和决策制定,进而影响价值创造。然而,这两个概念并不意味着相同的原因,或者相同的过程(或者相同的团队),因此它们必须分开考虑。

数据质量是链中的第一个环节,翻译过来就是“在正确的时间、正确的地点获得好的数据”。我们在这里讨论的是,在数据创建的那一刻,符合一定数量的规则。这些规则几乎总是相同的,数据必须在你需要的时候就在那里,它必须是完整的,它必须是准确的(这三个标准必须被精确地定义)。

与数据质量不同,完整性与数据创建无关,而只与数据创建后的存储和拷贝有关(除非在极端情况下,应用程序会在数据创建时“创建不正确”并破坏数据)。因此,数据必须保持不变,复制的数据必须与原始数据完全相同。

数据完整性主要是一个技术问题,基础设施和工具,以及围绕数据存储、复制或传输的不良流程和做法的实施,都可能导致完整性的丧失。

另一方面,数据质量通常是运营团队感到沮丧的原因,也是管理层关心的问题,尤其是当数据对公司的活动至关重要时。这是许多企业的情况,许多计算机应用程序(包括最近的应用程序)在设计时没有将数据质量作为一个关键要素。

提高数据质量:约束、自动化、管理

在我看来,数据质量主要是一个方法论的问题。首先,在我看来,只有三种方法可以保证高质量的数据:

  1. 约束到应用程序中,并在数据产生时正确显示。例如,您可以强制某些数据字段,防止“自由”输入,并强制用户从列表中选择值,在允许验证页面之前检查数据一致性等。
  2. 自动化数据输入,尽可能避免手动输入,手动输入是错误的来源。这包括复制数据并将其从一个应用程序传输到另一个应用程序,以避免在两个不同的系统中手动输入相同的数据。
  3. 管理,提高负责生产数据的团队对拥有高质量数据的重要性的认识,在可能的情况下激励他们,必要时可能还会在一定程度上约束他们。

那么,糟糕的数据质量本身并不是真正的问题。真正的问题是它对服务质量和客户体验、卓越运营、新业务机会、合规性等的负面影响。因此,影响是一个关键方面。

识别、测量、分析和理解这种影响将首先使情况变得真实,而且还能确定哪些数据是关键的,并因此为每个数据、应用程序(约束和/或自动化)或管理定义要实施的策略。

保证数据可用于分析

数据可利用性是指允许按照预期对数据进行操作和/或分析。但是,如果运营使用主要依赖于保证数据质量和完整性的数据管理和治理流程,那么分析使用在很大程度上依赖于分析策略。

不言而喻,今天,能够分析数据变得越来越重要。如果数据质量和完整性允许保留数据的“单位”价值,则分析允许在“单位”价值之外通过以下方式创造额外的价值:

  • 描述发生的事情并从数据中获得证据来支持事实,而不是依赖于意见、假设或教条,从而促进决策。
  • 解释一种情况,通过引出相互关系和因果联系,允许理解为什么会发生,已经发生了什么,并在可能的情况下对不同的变量采取行动以改善结果。
  • 预测未来的情况,特别是当你被迫适应它,因为你无法控制导致它的环境(天气就是一个很好的例子,作为一个人,你不能准确预测天气,也不能影响它)

(关于解释性分析和预测性分析之间的区别的更详细的解释, 参见我以前关于这个主题的文章 )

为了通过分析最大限度地创造价值,数据质量和完整性是不够的。数据还需要可用于分析。这种可利用性已经变得越来越重要,并且必须成为应用程序设计中不可或缺的一部分。数据的定义、编码和建模方式是可以简化数据分析的关键驱动因素,或者相反会使数据分析变得更加复杂和昂贵。

因此,数据必须易于业务团队理解。数据模型和将数据创建到业务应用程序中的方式必须考虑到业务领域和流程,以便轻松地将数据“转换”为有形和可用的信息。

分析的可利用性要求不仅适用于业务应用。它还适用于存储数据并可用于报告和分析目的的分析平台。这些平台和相关工具将允许通过使这些数据可访问、通过合并来自不同业务应用程序的数据、聚合它们、关联它们等来创造额外的价值。但这些分析生态系统的设计也一定是为了简化用户对数据的使用。

首先,数据的获取、存储、转换、建模和提供给用户的方式必须从一开始就用于分析用途,从原始数据到细化数据的这些过程具有完整和可理解的可追溯性,模型适合业务用户并为其所理解。它必须满足服务级别、安全性和性能方面的要求。

然后,提供给用户的工具不仅必须简化用于接收、转换和建模数据的不同管道的构建和维护,还必须简化业务用户对数据的使用和管理,业务用户必须了解数据、数据的接收、转换和建模方式、可用性、分析和显示方式。

因此,可利用性也是关于特定工具的选择和实现,例如:

  • 数据目录,定义数据以及与数据质量规则的符合性
  • 数据准备工具和 ELT/ETL(提取、转换、加载)简化数据接收和转换
  • 数据可视化工具简化分析并帮助用户更好地展示数据
  • 等等。

总之,如下图所示,数据的质量、完整性和可利用性是三个关键驱动因素,它们直接影响价值创造,以及这些数据用于服务质量、卓越运营和决策的方式。总而言之:

感谢您的阅读,请让我知道您的想法,并毫不犹豫地分享。

用固定效应量化 NBA 教练的贡献

原文:https://towardsdatascience.com/quantifying-the-contribution-of-nba-coaches-using-fixed-effects-56f77f22153a?source=collection_archive---------26-----------------------

实践教程

一些教练改变了体育运动的方式,而另一些教练尽管有巨大的天赋,却缺乏领导。使用追溯到 1980 年的数据,我探索了高层管理人员的频繁变动,以理清 NBA 教练在名册特征之上产生的胜利。我发现对一些教练的影响,像格雷格波波维奇,尼克护士和史蒂夫·科尔,比全明星赛季的超级明星更大。

为什么是教练?

想想一个成功的体育王朝,你会发现背后有一个超水平发挥的教练。例如,在美国国家橄榄球联盟,如果不提到比尔·贝利奇克的天才,就很难谈论汤姆·布拉迪的成功。在足球方面,正是佩普·瓜迪奥拉精湛的战术释放了梅西的天赋,将巴塞罗那足球俱乐部变成了欧洲巨人。很少有人质疑尤尔根·克洛普对利物浦的变革性影响,利物浦几乎在一夜之间成为 EPL 历史上最强大的攻击之一。如果没有菲尔杰克逊的三角进攻,谁能说迈克尔乔丹会不会成为六次冠军得主

如果你仍然持怀疑态度,考虑一下在过去的 40 个赛季中,只有 4 名教练赢得了超过 60%的 NBA 总冠军。

我在黎巴嫩作为职业篮球运动员亲身体验了教练效应。当我通过训练进入国家队选拔时,我注意到一些教练能够从我身上得到最好的东西。他们让比赛变得更简单(也更有趣),不管环境和阵容如何,他们都能带来好的结果。我也曾在一些团队中表演过,尽管有着非凡的天赋,但我们的表现和一个小分队没什么区别。

菲尔·杰克逊被广泛认为是有史以来最伟大的篮球教练,他赢得的冠军(11 个)超过了 28 个。图片来自体育图片|Dreamstime.com 获得了拉姆济-阿米的许可。

更系统的方法

因此,根据我多年作为团队运动员的表现,我确信,教练提供的价值远比分析师和体育行业人士所认为的更大。当我开始我的经济学研究生学习时,我着手开发一种系统的方法来测量这种效应。我的目标是让教练分析不那么“黑箱化”,并给予教练应有的评价。

我将我的研究问题表述如下:篮球教练重要吗?如果是的话,他们对名册特征的相对贡献是什么?我通过对主教练身份的团队胜利进行回归来评估这一点,同时考虑其他可能有影响的因素。特别是,我开发了名册质量和伤病的控制变量。最终目标是为过去和现在的 NBA 教练提供可靠的衡量管理贡献的指标。

我的方法遵循了关于经理人的经济学文献,包括 Abowd et。Al (1999) 研究公司部门的员工工资, Bertrand 和 Schoar (2003) 研究首席执行官身份对资产负债表表现的影响, Muelheusser 等人 (2012)研究德国德甲联赛中的教练效应。这种方法特别适合 NBA,因为相当一部分教练在不止一个球队被观察。这种高周转率可以很准确地跟踪他们的影响。

它是如何工作的

本次分析使用的数据集涵盖了 1979-80 赛季到 2018-19 赛季之间的所有常规赛。除去部分教练赛季,剩下 808 个个人赛季,包括总共 163 名主教练。所有数据来源于 篮球参考

NBA 是 30 支球队的主场,每个常规赛都有超过 82 场比赛。我用总胜场数作为某个赛季团队表现的指标。然后将每个观察结果与主教练的信息、球员过去的表现指标以及其他统计数据(如总上场时间和错过的比赛)进行匹配。

然后,我使用上个赛季替代球员(VORP)的价值来构建一个天赋的衡量标准。为了简单起见,我根据上场时间将名单限制为 5 名球员,并计算前一赛季的平均 VORP。这个想法是,这个变量将捕获教练在赛季开始时可用的人才数量。

如何计算天赋变量,以波士顿凯尔特人队目前的名单为例。

第二个控制变量是伤病,衡量的是球队主要球员在整个赛季中错过的比赛总数。这背后的直觉是捕捉关键执行者错过的时间的影响,这预计会使教练的工作更加艰难。

最后,模型包括教练固定效应——利益变量。简而言之,固定效应是在时间或空间上不变的变量。比如说,在衡量“住在某个地区”或“属于某个种族”对预期收入的影响时,通常会用到它们。由于主教练身份是一个分类变量,我使用一键编码将其转换为模型可以解释的二进制形式。

主要结果

固定效应对一些教练来说相当大——比全明星赛季超级明星的胜率还要大。值的范围从-25 到+21,平均值为 0.15。这意味着+10.0 的值表示教练每个赛季相对于平均值多赢得 10 场比赛。从这个角度来看,这样的金额足以将平庸的球队推入季后赛的争夺。

从一般的主教练到尼克护士(1)的变化产生了大约 20 个额外的胜利,当花名册的特征保持在他们的平均水平时。换句话说,护士团队倾向于持续超额完成 20 场胜利。当然,护士的估计是有噪声的——这意味着精度较低——因为模型只观察了他两个季节。类似地,史蒂夫·科尔(3)与+ 17 效应和相当大的标准误差相关联。

对菲尔杰克逊(8),格雷格波波维奇(4)和帕特·莱利(14)的影响估计更精确。任期越长,观察次数越多,获胜次数越多,标准误差越低。拉里·伯德(第 5 名)和丹尼·安吉(第 2 名)都名列前茅,尽管(或者可能是由于)执教时间很短。考虑到他们都很快晋升到总经理的职位,也许他们也成为有效的教练并不完全令人惊讶。

布鲁克斯(9)被模型确定为超过他的平均名册建议的胜利数约 12 胜。虽然一些 NBA 篮球的密切关注者认为这难以置信,但这一结果可能受到了他在雷霆队的影响,在那里他带着一个令人惊讶的年轻阵容进入了 NBA 总决赛。这也可能突出模型中的一个缺点。由于它使用以前的表现作为名册质量的衡量标准,该模型可能有利于崭露头角的超级明星教练。我稍后会回到这个问题。

如需教练指标的完整表格,请访问我的个人网站这里。

被低估的教练

这种方法的最终好处是发现隐藏的教练能力。如果好的教练被定义为用,那么获胜最多的教练不一定是最有效的。尤其是在 NBA,球员选拔发生在总经理层面,一些教练可能会发现自己比其他人更有前途的阵容。因此,传统的看待胜率的方式是不够的;基于模型的方法通过控制名册效应提供了一个更好的选择。

以现任湖人主教练弗兰克·沃格尔为例,他的胜率大约在第 75 百分位,而根据教练效应,模型将他排在第 89 百分位。在印第安纳的 6 个赛季中,有 5 个赛季他至少获得了东部第二。然后在奥兰多的两个赛季,他没能取得有意义的成绩。

总的来说,该模型发现沃格尔做得比他的胜率显示的要好,尽管他在这方面已经排名很好。这很可能是因为当他掌舵时,步行者队见证了一夜之间的转变。现在,在湖人队,沃格尔似乎正在兑现这一评估。他能否像他之前的一些伟人那样建立一个王朝,还有待观察。无论如何,拥有三届冠军勒布朗詹姆斯不太可能有助于他的固定效果。

结果有多可靠?

我通过观察模型的样本外表现来评估准确性。对于样本开始以来的每一个赛季,我都根据该赛季之前的数据拟合模型,然后观察一个赛季之前对球队获胜的预测与实际数据的匹配程度。我选择均方根误差(RMSE)作为评估标准:预测误差越低,教练的估计就越可靠。

与只有球员和伤病的模式相比,教练模式总体上减少了 15%的 RMSE。这意味着,实际上,教练效应正在捕捉价值,否则错过了。然而,偶尔,无教练模式(通过完全放弃固定效果)表现更好。例如,在 2018-19 年,教练模式下降了 20%以上。

作为健壮性检查,我考虑了模型的替代规范,包括 blench 播放器的包含。然而,这并没有导致评估的任何改进。我对除 VORP 之外的玩家指标的使用进行了进一步的稳健性检查,如获胜份额、盒子正负和玩家效率评级。然而,我发现 VORP 的预测表现最好。

局限性和后续步骤

我的分析并非没有挑战,其中一些可能需要一个单独的研究项目。我在这里讨论了其中的一些挑战,包括我目前如何应对这些挑战或计划在未来如何应对。总的来说,虽然我确实相信还有改进的空间,但我也重视简单性(但不幼稚)和可解释性的首要原则。

  1. 不包括季后赛。也许最终的指标是团队在最重要的时候的表现。季后赛是战术得到检验的时候,鼓舞人心的领袖得到了一个锻炼自己影响力的机会。但是由于每个赛季只有表现最好的球队才有资格参加,所以对季后赛的分析会受到选择偏差的影响。
  2. ****这个模型没有考虑到新手的素质。由于缺乏新加入球员的历史指标,该模型无法像对经验丰富的球员那样控制新秀的质量。相反,所有的新人都被认为是平等的。解决这个问题的一个潜在方法是控制汇票订单。换句话说,高选秀的新秀会比那些后来被选中的新秀得到更高的价值。
  3. ****这种模式可能有利于拥有崭露头角的超级明星的教练。由于过去的表现被用作质量的代表,每年的球员发展都归功于主教练。这在像斯科特·布鲁克斯这样的情况下可能会有问题,在那里他发现自己有高成长球员(杜兰特,威斯布鲁克和哈登)。在未来的工作中,我计划从两个方面解决这个问题。第一种是通过使用过去赛季的移动平均值来控制球员的老化曲线,第二种是通过潜在地将后面的赛季纳入球员变量。
  4. 结果可能会受到遗漏变量偏倚的影响。人们可能会想到潜在的混淆因素,如团队化学或特许权固定效应,这些因素会影响团队胜利,但目前却归咎于教练。特许经营效应的论点意味着,一些教练可能会从招聘能力强的“大”市场团队中受益。此外,有吸引力的自由球员可能会选择位于天气条件好的城市的球队。然而,这些球队通常会签下久经考验的教练,他们的固定效应在很大程度上已经被抓住了。一个潜在的解决方案是对球员类型进行建模。
  5. 玩家的分配可能不是完全随机的。问题在于,好教练是不是因为带走球员,让他们打得更好才是好教练;或者他们挑选他们知道在一起会打得更好的球员。如果他们选择了自己的球队,那么怎么能真正区分球员和教练呢?这是评论我工作的经济学家向我提出最多的一个问题。幸运的是,NBA 是这样的,教练不负责球员的获得;签约、交易和挑选球员是总经理的工作。例如,这与欧洲足球不同,在欧洲足球中,教练(经理)既负责战术,也负责球员的引进。此外,有人可能会说像菲尔杰克逊和格雷格波波维奇这样的“更好的教练”可能会吸引更高质量的人才(反之亦然),因为球队需要最大限度地利用他们的存在。这些情况发生在教练已经证明了他们的能力的时候,通常是在辉煌的职业生涯之后,这意味着他们的固定效果在那时已经基本形成。

结论

经理是组织成功的关键。他们制定战略,激励员工,并建立文化。他们也是吸引和留住优秀人才的关键。像玩家一样,他们可以以不同的方式影响游戏。有些人是具有出色人际交往技巧的激励型领导者(如帕特·莱利、尤尔根·克洛普、何塞·穆里尼奥),而其他人则以象棋般的战术才华而闻名(如布拉德·史蒂文斯、格雷格·波波维奇和比尔·贝利奇克)。最优秀的人能够凭借天赋和风格的结合超越体育(例如菲尔杰克逊、佩普瓜迪奥拉、约翰·克鲁伊夫和约翰·麦登)。

尽管如此,很少有人非常严谨地研究过篮球教练。除了这篇斯隆的论文,分析社区继续关注球员作为价值的主要来源,而教练仍然是一个黑箱。因此,任何分析都无法超越猜想和轶事证据。

尽管存在缺点,即球员发展被分配给教练,忽略了可变偏差,以及潜在的不那么随机的名册分配,基于模型的方法提供了几个重要的见解。首先,它证实了伟大的教练和伟大的球员一样重要。其次,相对于历史上的同行,它提供了对当前教练的影响的公平和稳健的量化评估。第三,它揭示了传统方式所没有的价值。

就现实世界决策的适用性而言,该模型是否能够特别好地预测新分配的教练团队配对的成功还有待观察。

分位数回归

原文:https://towardsdatascience.com/quantile-regression-ff2343c4a03?source=collection_archive---------1-----------------------

分位数回归拟合的数据

当进行回归分析时,对一个问题给出一个数字预测是不够的,你还需要表达你对这个预测有多有信心。例如,如果您正在查看某个特定市场的房价,而您的模型预测某栋房子的售价为 262,458.45 美元,那么您对模型的预测完全正确有多大把握呢?但愿你的直觉告诉你,这是真的可能性极小,但也许你的模型有可能接近实际答案。我们需要一种方法来预测一个数值范围,同时对该范围有一定的把握。

输入分位数回归。不同于使用最小二乘法来计算跨越不同特征值的目标的条件 均值 的常规线性回归,分位数回归估计目标的条件 中值 。分位数回归是线性回归的扩展,在不满足线性回归的条件(即线性、同方差、独立性或正态性)时使用。

传统上,用于计算平均值的线性回归模型采用以下形式

线性回归模型方程

其中 p 等于等式中特征的数量,而 n 是数据点的数量。最佳线性回归线是通过最小化均方误差找到的,这是通过以下等式找到的

线性回归的均方误差

现在,对于分位数回归,您不仅限于查找中位数,还可以计算特征变量中特定值的任何分位数(百分比)。例如,如果我们要找到特定房屋价格的第 25 个分位数,这将意味着有 25%的可能性房屋的实际价格低于预测,而有 75%的可能性价格高于预测。

采用与线性回归模型类似的结构,𝜏th 分位数的分位数回归模型方程为

分位数回归模型

这意味着β系数不再是常数,而是依赖于分位数的函数。在特定分位数上寻找这些 betas 值的过程与常规线性量化几乎相同,只是现在我们必须降低中值绝对偏差。

分位数回归的中位数绝对偏差

这里,函数𝜌是检查函数,它根据误差的分位数和总体符号对误差赋予不对称的权重。数学上,𝜌采取的形式

检查函数方程

检查功能图

在这种情况下, u 是单个数据点的误差,max 函数返回括号中的最大值。这意味着,如果误差为正,则检查函数将误差乘以𝜏,如果误差为负,则检查函数将误差乘以(1- 𝜏).

例如,如果您想要第 10 个百分位数的中间值,这意味着您想要 90%的误差为正,10%为负。为了找到最小的 MAD,同时使该陈述为真,必须将权重添加到误差中。在第 10 个分位数的情况下,权重 0.9 被添加到负权重,而权重 0.1 被添加到正权重。

让我们看看分位数回归的作用。让我们检查一下 QuantReg 的 python statsmodels 示例,该示例查看了 1857 年比利时工人阶级家庭样本的收入和食品支出之间的关系,并查看我们可以进行何种统计分析。

import statsmodels.api as sm
import statsmodels.formula.api as smf
data = sm.datasets.engel.load_pandas().data
data.head()

1857 年贝尔加因家庭数据

mod = smf.quantreg('foodexp ~ income', data)
res = mod.fit(q=.5)
print(res.summary())

如您所见,您可以为特定的分位数创建一条回归线,并对其执行统计分析,就像使用常规线性回归模型一样。

如果你想了解更多,这里有一个链接到 SAS 写的一篇关于分位数回归的论文。下文提供了其他来源。

[## 分位数回归

分位数回归是统计学和计量经济学中使用的一种回归分析。而最小二乘法…

en.wikipedia.org](https://en.wikipedia.org/wiki/Quantile_regression) [## 分位数回归-统计模型

此示例页面显示了如何使用 statsmodels 的类来复制 Koenker,Roger…

www.statsmodels.org](https://www.statsmodels.org/devel/examples/notebooks/generated/quantile_regression.html)

Python 中的量化金融

原文:https://towardsdatascience.com/quantitative-finance-in-python-e8d48e6b9c23?source=collection_archive---------30-----------------------

用代码介绍货币的时间价值

照片由 Alexander Mils 从 Pexels 拍摄

普通金融和量化金融专业的第一堂量化课都与金钱的时间价值有关。本质上,这是一个长达一学期的课程,将今天的 100 美元从今天起一年后价值超过 100 美元的观念灌输到大学生的头脑中,并让他们手工解决令人痛苦的算术题,以确定他们今天需要投资多少才能在未来达到某种价值。这是与作为时间价值差异应用的永久财产和年金的介绍同时进行的。虽然我不喜欢手工计算,但我非常喜欢用 Python 来编写代码以便于使用。

金钱的时间价值

无风险利率——在实践中,由美国国库券、票据和债券代表的利率是货币价值随时间变化的原因。涵盖衍生品和证券定价等科目的高级课程总是在定价公式中考虑货币的时间价值,这使得这个主题就像代数之于微积分一样。

考虑到目前的无风险利率是每年 8%。今天收到 100 美元意味着可以立即以无风险利率进行投资…

一年后,100 美元的无风险利率是 108 美元。因此,在其他条件相同的情况下,今天收到 100 美元比一年后收到 100 美元更有价值。

在前面的例子中,复利(全年定期利息支付的再投资)被完全忽略。让我们看看另一个例子,我们考虑了复合效应。

考虑目前的无风险利率是每年 8%,复利频率是每月一次。今天收到 100 美元意味着可以以无风险利率进行每月复利的直接投资…

年度至月度汇率转换

第一个月的利息

第二个月的利息

第三个月的利息

希望到目前为止,每个月都有一个模式被认可…

第一个月的利息

第二个月的利息

第三个月的利息

m 月的利息

一年后,按无风险利率计算,100 美元约为 108.34 美元。通常用“时间段美元”来表示该值。例如,今天的 100 美元在时间段 1 美元中价值 108.34 美元…

关于时间的无风险投资寿命

因此,今天收到 100 美元将比今天收到 100 美元更有价值,如果没有每月复利的话,其他一切都等于

从上面的例子可以构造出一个通用公式…

货币时间价值公式

  • P —初始金额或本金(100 美元)
  • A —时间段内的结果金额为新台币(108 美元或 108.34 美元)
  • r —无风险利率(8%)
  • n —时间段 t (1 或 12)的复合频率
  • t —经过的时间段(1)

现在让我们用 Python 写这个等式…

为了测试这个函数,使用上面的参数写一个调用…

108.29995068075098

注:得出的值不同,更准确。

值得一提的是,当复利频率的极限接近无穷大时,在时间段 t 美元中产生的金额就越高。

108.29995068075098 # Compounding period of 12
108.31429591590663 # Compounding period of 24
108.31909221757883 # Compounding period of 36
108.32149310823426 # Compounding period of 48

证明超出了本文的范围,但结果是一个不断复合的方程。在未来,我将讨论连续复利、交易和时间的重要性,以及它们与包括布莱克-斯科尔斯方程在内的高级模型推导的关系。

定量的,定性的,或者两者都有?

原文:https://towardsdatascience.com/quantitative-qualitative-or-maybe-both-542553d09918?source=collection_archive---------19-----------------------

监督学习

当分析数据时,许多问题自然地落入监督或无监督的学习范例中。在这篇博客中,我们回顾监督学习,其中你会有一个输入变量,如 x 和一个输出变量,如 Y ,你使用一种算法来学习从输入到输出的映射函数 Y = f(x)

分类与回归

深入挖掘监督学习,我们有两组问题,分类和回归。分类问题的输出变量是分类的(定性的),而回归问题的输出往往是数值的(定量的)。在统计学中,定性方法是描述性的,而定量方法是数值性的。虽然这可能不像将这两个问题分解成分类和回归那么简单,但它可以变得稍微复杂一些。最小二乘线性回归问题用于定量响应,而逻辑回归用于定性响应(0 和 1 之间的二进制结果)。对于分类问题,我们经常使用逻辑回归。想象你的 Y 变量或输出有两个结果,比如 yes 或 no(二进制结果)。Boosting 和 KNN 是两个可用于定性或定量反应的例子,但本质上是定量方法。

当数据科学中的模型本质上是定量的,但我们想要一个分类的响应时,我们使用分类方法。如上所述,二进制结果可用于数字,如 0 和 1。0 代表假,1 代表真。如果有两个以上的类别输出,我们使用虚拟变量来表示我们的输出。

一个虚拟变量(又名指标变量)是一个数字变量,代表分类数据,如性别、种族、政治派别等。从技术上讲,虚拟变量是二分的,定量变量

混合方法

定性和定量方法的选择取决于你认为什么能为你的研究目标提供最好的证据。一些数据科学家甚至使用两者来正确解决他们的业务问题。我们称之为混合方法!

让我们以银行数据为例,我们希望标记具有欺诈信用卡行为的客户。我们有 5 列数据,包括唯一的 id、日期、姓名、位置和交易金额。我们知道,我们的数据包含为我们提供定量数据和定性数据的交易。作为一名数据科学家,我们决定对这个问题使用逻辑回归方法(二进制), 0 表示没有欺诈,1 表示有欺诈。

但是等等,我们还没有完成…我们有位置作为定性数据。我们希望使用经度和纬度来更改我们的位置,从而将我们的位置数据转换为定量结果。我们可以用<2 transactions outside a certain milage of their other transactions. Creative right?

How about we flag customers from a quantitative perspective and monitor spikes in transaction amounts. These are two simple examples of how we can solve problems from cleaning our data from turning some variables to a quantitative approach. We can even use categorial or “dummy variables” in order to solve more problems with fraudulent behavior.

O 标记客户。总的来说,数据科学家和数据分析师会使用不同的方法,以便更有效地解决问题。答案是使用定量方法还是定性方法取决于所提问题的性质。记住你清理和评估数据的过程,并保持解决问题的创造性是任何商业决策的关键。为了有效地交付带有解决方案的模型,您必须了解应该需要什么样的输出。

量化你的深度学习模型在 NPU 上运行

原文:https://towardsdatascience.com/quantize-your-deep-learning-model-to-run-on-an-npu-2900191757e5?source=collection_archive---------24-----------------------

准备 TensorFlow 模型,以在集成在 phyBOARD-Pollux 中的 i.MX 8M Plus NPU 上运行推理

图片由加拿大 CC Liam Huang 和 mikemacmarketing 提供

目录

  • 简介
    ——为什么大多数人工神经网络都是在 float32 中训练的,NPU 却使用 int8?
    -先决条件
  • 使用 TensorFlow 版本 2.x
    进行训练后量化——第一种方法——直接量化已训练的模型
    ——第二种和第三种方法——量化来自.h5 或的已保存模型。pb 文件
  • 使用 TensorFlow 以下版本转换

介绍

在本文中,我们将在本文中解释使用不同的 TensorFlow 版本对您的模型进行变换和量化需要采取哪些步骤。我们只关注培训后的量化。

我们使用 phyBOARD-Pollux 来运行我们的模型。phyBOARD-Pollux 集成了一个 i.MX 8M Plus ,它具有来自 VeriSilicon (Vivante VIP8000)的专用神经网络加速器 IP。

phyBOARD pollux [ 图片经由 phytec.de 授权给 Jan Werth】

恩智浦 i.mx8MPlus 框图[图片来自 phytec.de,经恩智浦许可]

由于恩智浦的神经处理单元(NPU)需要完全 int8 量化模型,我们必须研究 TensorFlow lite 或 PyTorch 模型的完全 int8 量化。恩智浦的 eIQ 库支持这两个库。这里我们只研究张量流变体。

关于如何进行训练后量化的概述可以在 TensorFlow 网站上找到。

为什么 NPU 使用 int8,而大多数人工神经网络都是用 float32 训练的?

浮点运算比整数运算更复杂(算术运算,避免溢出)。这导致只能使用更简单和更小的算术单元,而不是更大的浮点单元。​

float32 操作所需的物理空间比 int8 大得多。这导致:

  • 更低的功耗,
  • 较少的热量产生,
  • 加入更多计算单元的能力减少了推理时间。

先决条件

要首先在您的 PC 上创建代码,我们建议在运行 Python 3.6、TensorFlow 2.x、numpy、opencv-python 和 pandas 的虚拟环境中使用 Anaconda。

克隆环境的环境文件可以在这里找到。

tensor flow 2 . x 版训练后量化

如果您通过 tf.keras 创建并训练了一个模型,那么有三种类似的方法来量化这个模型。

第一种方法——直接量化训练好的模型

经过训练的 TensorFlow 模型必须转换为 TFlite 模型,并且可以直接量化,如以下代码块中所述。对于训练的模型,我们示例性地使用基于 rcmalli 的工作的更新的 tf.keras_vggface 模型。转换从第 28 行开始。

在加载/训练您的模型后,您首先必须创建一个代表性数据集。转换器使用代表性数据集来获取最大值和最小值,以便估算比例因子。这限制了从 float32 到 intX 的量化所引入的误差。误差来源于浮点数和整数的不同的数空间。从 float 到 int8 的转换将数字空间限制为-128 到 127 之间的整数值。根据输入的动态范围校准模型可以限制这种误差。

在这里,你可以像我们的例子一样循环遍历你的图像或者创建一个生成器。我们使用TF . keras . preprocessing . image . imagedata generator()生成图像,并对图像进行必要的预处理。作为发电机,你当然也可以使用 tf。数据。dataset . from _ tensors()from . tensor _ slices()。请记住,在这里对您的数据进行与您训练网络所用的数据相同的预处理(标准化、调整大小、去噪等)。这些都可以打包到生成器的预处理 _ 函数调用中(第 19 行)。**

转换从第 28 行开始。简单的 TensorFlow lite 转换如下所示:

量化部分介于两者之间:

  • 第 3 行:不推荐使用默认优化以外的优化。目前(2020 年)没有其他选择。
  • 第 4 行:这里我们设置了代表性的数据集。
  • 第 5 行:在这里,我们确保完全转换为 int8。如果没有此选项,只有权重和偏差会被转换,而不是激活。当我们只想减小模型尺寸时,使用这种方法。然而,我们的 NPU 需要完整的 int8 量化。激活仍处于浮点状态会导致整体浮点,并且无法在 NPU 上运行。
  • 第 6 行:启用基于 MLIR 的转换,而不是 TOCO 转换,这使得 RNN 支持,更容易的错误跟踪,和更多的。
  • 第 7 行:将内部常量值设置为 int8。target_spec 对应于第 5 行的 TFLITE_BUILTINS。
  • 第 9 行和第 10 行:也将输入设置为 int8。这在 TF 2.3 中是完全可用的

现在,如果我们使用 TF2.3 转换模型

  • experimental _ new _ converter = True
  • 推论 _ 输入 _ 类型=tf.int8
  • 推论 _ 输出 _ 类型=tf.int8

我们收到以下模型:

作者图片

然而,如果我们不设置推理 _ 输入 _ 类型推理 _ 输出 _ 类型,我们将收到以下模型:

作者图片

因此,效果是您可以确定模型接受和返回哪种输入数据类型。如果您使用嵌入式摄像机,这一点可能很重要,如 pyhBOARD-Pollux 所包含的。mipi 摄像头返回 8 位值,所以如果你想转换成 float32 int8 输入会很方便。但是请注意,如果您使用没有预测层的模型来获得(例如嵌入),int8 输出将导致非常差的性能。这里推荐使用 float32 输出。这说明每个问题都需要特定的解决方案。

第二种和第三种方法—从.h5 或量化保存的模型。pb 文件

如果你已经有了你的模型,你最有可能把它保存在某个地方,作为一个 Keras h5 文件或者一个 TensorFlow 协议缓冲 pb。我们将使用 TF2.3 快速保存我们的模型:

下面的转换和量化与方法一非常相似。唯一的区别是我们如何用转换器加载模型。要么加载模型,然后像方法一那样继续。

或者直接加载 h5 模型。使用 TensorFlow 版本 2 及更高版本时,您必须使用兼容的转换器:

如果从 TensorFlow pb 文件加载,请使用:

使用低于 2.0 的 TensorFlow 版本进行转换

如果你想转换一个用 TensorFlow 版本编写的模型< 1.15.3 using Keras, not all options are available for TFlite conversion and quantization. The best way is to save the model with the TensorFlow version it was created in (e.g., rcmalli keras-vggface was trained in TF 1.13.2). I would suggest not using the “保存和冻结图形的方法来创建一个 pb 文件,因为 TF1 和 TF2 的 pb 文件不同。tflite converter . from _ saved _ model不工作,为实现量化制造了相当大的麻烦。我建议使用 Keras 的上述方法:

然后,从 1.15.3 开始,使用 TensorFlow 版本转换和量化您的模型。在这个版本的基础上,增加了很多功能,为 TF2 做准备。我建议使用最新版本。这将导致前面介绍的相同模型。

祝你好运,玩得开心。

2020 年你应该读的量子书

原文:https://towardsdatascience.com/quantum-books-you-should-read-in-2020-e58c34befbf?source=collection_archive---------22-----------------------

量子计算机和量子计算来了!

量子计算机已经问世,因此值得进一步了解这项即将上市的令人惊叹的技术。因为它太新了,现在市场上只有少数几本书。如果你想了解等待我们的是什么,这里列出了非数学的概述和更多的量子计算实践编码方法。

2020 年你应该读的量子书

要读的量子书

在用量子猫计算:从巨像到量子位中,约翰·格里宾提出了一个由许多平行世界组成的宇宙,其中“一切都是真实的”。回顾艾伦·图灵在恩尼格玛机和第一台电子计算机上的工作,格里宾解释了量子理论是如何发展到使量子计算机在理论上和实践中都能工作的。他带领我们超越理论物理领域,探索它们的实际应用——从通过“直觉”和反复试验学习的机器到无法破解的笔记本电脑和智能手机。他研究了这种非凡的科学创造一个世界的潜力,在这个世界里,交流比光还快,传送是可能的。

面向每个人的量子计算是对计算中一个令人兴奋的新领域的通俗易懂的介绍,为普通读者解释了量子比特、纠缠和量子隐形传态等主题。

在这本书里,Chris Bernhardt 介绍了量子计算,任何熟悉高中数学的人都可以阅读。他解释道:

  • 量子位,
  • 纠结,
  • 量子隐形传态,
  • 量子算法

和其他与量子有关的主题。

到本书结束时,读者明白量子计算和经典计算并不是两个截然不同的学科,量子计算是计算的基本形式。计算的基本单位是量子位,而不是比特。

给大家说一本书,婴儿量子计算是个好去处。这本书是对量子计算机神奇世界的简单介绍。婴儿(和成人!)将发现比特和量子比特的区别,以及量子计算机将如何改变我们的未来。

与量子位共舞:量子计算如何工作以及它如何改变世界是一本量子计算教科书,它从概述为什么量子计算与经典计算如此不同开始,并描述了它可以产生重大影响的几个行业用例。从那里,它移动到一个经典计算和数学基础的必要理解叠加,纠缠和干涉等概念的更全面的描述。接下来是电路和算法,既有基础的也有更复杂的。然后,它很好地继续提供了一个如何建立量子计算硬件背后的物理和工程思想的调查。最后,这本书展望未来,并在理解进一步的发展将如何影响你方面给你指导。

真正理解量子计算需要大量的数学知识,这本书不会回避你需要的必要的数学概念。每个主题都有详尽的介绍和解释,用清晰的英语和有用的例子。

与量子位共舞是一本量子计算教科书,面向那些想要深入探索量子计算内部工作原理的人。这需要一些复杂的数学解释,因此最适合那些对数学、物理、工程和计算机科学感兴趣的人。

自德谟克里特斯以来的量子计算带领读者穿越数学、计算机科学和物理学的一些最深奥的概念。这本书充满了真知灼见、论点和哲学观点,涵盖了一系列令人惊叹的主题。从古代的德谟克里特斯开始,它通过逻辑和集合论、可计算性和复杂性理论、量子计算、密码学、量子态的信息内容和量子力学的解释而发展。也有关于时间旅行、纽康悖论、人择原理和罗杰·彭罗斯观点的广泛讨论。

阿伦森的非正式风格使这本引人入胜的书容易被有科学背景的读者,以及从事物理、计算机科学、数学和哲学的学生和研究人员所理解。

量子计算机编程:基本算法和代码样本为程序员理解量子计算提供了实践指南。您将直接使用示例来展示这项技术的独特能力,而不是通过数学和理论来劳动。

这本书有三个部分:

  • QPU 编程——探索量子处理单元编程的核心概念,包括如何描述和操纵量子位以及如何执行量子隐形传态。
  • QPU 原语—学习算法原语和技术,包括振幅放大、量子傅立叶变换和相位估计。
  • QPU 应用——研究如何使用 QPU 原语构建现有应用,包括量子搜索技术和 Shor 因子分解算法。

量子计算:一种实用的方法将量子计算的基础与动手编程方法相结合。

这本书有三个部分:

  1. 概述量子计算和量子电路的必要基础。
  2. 走过量子计算算法的经典,并提供当前使用的一系列量子计算方法的代码。
  3. 涵盖掌握量子计算所需的数学工具包。

其他资源包括操作器和电路元件表,以及提供代码和更新的 GitHub 网站。

这是所有的乡亲,这份名单将与市场上新的量子计算书籍更新。

量子计算快乐!

免责声明:以上链接均为附属链接,感谢您的支持

量子计算和金融科技应用

原文:https://towardsdatascience.com/quantum-computing-fintech-applications-6bed5415306a?source=collection_archive---------54-----------------------

奇点研究

套利机会&格点规范理论

量子计算机已经达到了一个复杂的水平,这使得开发者有很多机会在近期应用。谷歌和 IBM 等科技巨头使用的最新技术现在都在云中提供,摩根大通(J.P. Morgan)等企业正在投资未来的金融应用,希望量子计算机能够产生优于标准方法的效益。在本文中,我们将讨论一个似乎被忽视的近期应用,即汇率和套利机会。

很长一段时间以来,人们已经知道汇率和套利行为很像量子色动力学(QCD)中的格点规范理论。如果你不知道那是什么意思,没关系,我们会在这里解释,你不需要知道很多数学或物理知识就能明白基本的意思。

由 Austin Distel 在 Unsplash 上拍摄的照片

首先,如果你不是金融大师或日内交易者,让我们谈一谈汇率和套利。万一你对我们将要讨论的背后的数学和物理学感兴趣,我强烈推荐这本书,《金融物理学:规范理论、基本相互作用和纤维束的温和介绍》。

假设我们有三种货币,美元、欧元和日元。现在,假设在某个时刻,汇率是:

美元/欧元 1.710,

欧元/日元 71.90,

日元兑美元汇率为 1 美元兑 123.2 日元。

现在,假设兑换货币时没有费用或任何其他意外支出,我们会期望这三个数字的乘积等于 1。然而,智能手机计算器中的一些简单数学会显示:

(1.710)(71.90)(1/123.2) = 0.998.

这意味着可以通过美元兑换欧元,然后欧元兑换日元,再将日元兑换成美元,在这些货币之间进行交易来获利。简单的通过兑换货币,就可以赚取利润。这种情况每天都在发生,通常在电脑上使用算法交易来执行。一般来说,费用和其他对策被用来试图防止这种情况,但如果你的计算机算法可以模拟这些汇率差异,并在某种程度上预测它们,那么你可以比对策实施更快地启动交易,在这个过程中赚取利润。我们也可以进行时变交易,实现类似的目标。另一个例子是 K. Young 的一篇研究文章:

“作为另一个例子,考虑两种货币在两个时间。假设美元和日元的汇率现在是 130,一个季度后是 128(假设提前知道)。此外,假设利率为美元每季度 1.5%,日元每季度 0.25%。同样,我们假设没有交易成本,因此存款利率与借款利率相同。四个数字,即 1.015、128、1/1.0025 和 1/130,再次“应该”具有等于 1 的乘积。但是,事实上,

(1.015)(128)(1/1.0025)(1/130) = 0.997.

该产品与 unity 的背离再次允许通过套利交易获得无风险利润。"

因此,正如我们所见,汇率和套利提供了一个通过算法交易获利的绝佳机会。更令人着迷的是,这些汇率随时间的动态可以用一种叫做晶格规范理论的东西来建模。晶格规范理论是首先在量子物理背景下发展起来的理论,特别是在量子色动力学(QCD)中,这是一种最初用于描述亚原子物理行为的理论。格点规范理论可以被编码成一个叫做哈密顿量的东西,它告诉我们系统如何随时间演化。来自 QCD 的哈密顿量通常是无限维的,但是它们可以由有限维的哈密顿矩阵很好地近似。然后这些可以在量子计算机中进行 T4 编码。在这篇论文中,Byrnes 和 Yamamota 研究了如何在当前的量子硬件上实现模拟,例如 IBM 通过云访问提供的硬件。他们在论文中指出,

“我们方法的基本策略是将哈密尔顿公式中的晶格规范理论转换成只涉及泡利自旋算符的哈密尔顿公式,从而可以在量子计算机上仅使用一个和两个量子位操作来进行模拟。”

对于那些不熟悉“泡利自旋算符”等术语的人来说,查看我的关于量子计算的 Github 教程可能会有所帮助,在那里我讨论了如何对实际的量子计算机进行编程,并解释了使用 Jupyter 笔记本和 Python 所涉及的数学。

已经开发了大量的软件来在标准计算机上模拟这些晶格规范理论,但是计算开销可能会变得昂贵,并且在量子计算机上模拟这样的系统可能会被证明是更精确和更经济的。这就是为什么在当前量子硬件上投资研究算法和机器学习模型的应用和开发如此重要的一个原因。许多近期的应用正在等待调查,现在投资这项技术可能会有回报。许多人都有这样的误解,认为硬件能力离应用还有几年的时间,但随着机器学习、优化和建模任务的性能改进研究的发表,这已经被证明是错误的。

如果你有兴趣了解更多关于量子计算和量子机器学习的知识,欢迎随时联系。我总是很乐意讨论当前的应用和技术。你可以在 LinkedIn 上找到我,当然,也可以查看我的 Github 教程,关于如何为机器学习任务编写实际的量子计算机。

23 美分的量子计算。

原文:https://towardsdatascience.com/quantum-computing-for-23-cents-917e1f664cea?source=collection_archive---------51-----------------------

AWS 量子计算的简单而实惠的介绍现已推出。今天就开始吧。

迈克尔·泽兹奇在 Unsplash 上的照片

2020 年 8 月 13 日, AWS 宣布亚马逊 Braket 全面上市。Braket 是他们完全托管的量子计算服务。它是按需提供的,很像 SageMaker。这意味着日常的开发人员和数据科学家可以四处修补和踢轮胎。

我想看看 Braket 控制台,看看这种体验有多直观。易用性和友好的用户界面对我来说至关重要。我是量子计算领域的新手,所以我有很多东西要学。可用的入门笔记本使学习基础知识变得容易。我应该说是最基本的。魔术背后的科学变得相当复杂,但是很有趣。

费用

我的第一个任务是检查成本。当我玩一些 SageMaker 项目时,我的 AWS 预算警报提前响起。我不想要任何 100 美元的惊喜,我相信你也不想。从我收集的情况来看,费用在四个方面有所不同。这些笔记本电脑基于 SageMaker 的定价,并按此计费。实例越大,成本越高。AWS 模拟器每小时收费 4.50 美元。量子设备在它们自己的“发射”基础上运行。最后,您需要将输出发送到 S3,因此会收取象征性的存储费用。在这个过程中,我小心翼翼地检查我的账单。我知道我运行的是一个小样本,所以成本会很低——不到 1 美元。最后花了我 23 美分。

23 美分运行在这个极客的眼睛糖果?还不错。

准备

以下所有信息都假设您已经设置了 AWS 帐户,包括账单。如果你是第一次设置你的账户,有一些自由层选项,虽然不是针对量子设备。

一旦你有一个帐户,你需要为你的输出命名一个 S3 桶。除此之外,你已经准备好了。

运行示例

有时候,直接进入代码是了解硬件如何工作的最好方式。在这个早期阶段,实践教程可能很难找到。我从 AWS 博客本身开始。它有一步一步的屏幕截图指南。

https://AWS feed . com/whats-new/AWS/Amazon-braket-go-hands-on-with-quantum-computing

就运行示例笔记本而言,这非常简单。

我只打了一个小嗝;我忽略了更新 S3 桶变量。如果不使用您选择的 S3 时段更新该值,您将会得到以下错误:

“ValidationException: An error occurred (ValidationException) when calling the CreateQuantumTask operation: Caller doesn’t have access to amazon-braket-<##########> or it doesn’t exist.”

如果您想运行一个快速测试来确保您的所有配置都是正确的,下面是一个简单的单细胞脚本。基于超密集的例子,它涵盖了使用本地模拟器(随 SDK 一起提供)、AWS 模拟器和 one quantum 设备( ionQ ),在我测试的时候在我所在的地区是可用的。

# !pip show amazon-braket-sdk | grep Version# Import Braket librariesfrom braket.circuits import Circuit, Gate, Momentsfrom braket.circuits.instruction import Instructionfrom braket.aws import AwsDeviceimport matplotlib.pyplot as pltimport boto3import timefrom braket.devices import LocalSimulator# Please enter the S3 bucket you created during onboarding in the code below,# or create a new bucket named as ‘amazon-braket-<your account number>’ to run the below code without changes.aws_account_id = boto3.client(“sts”).get_caller_identity()[“Account”]my_bucket = f”amazon-braket-{aws_account_id}” # the name of the bucketmy_prefix = “simulation-output” # the name of the folder in the buckets3_folder = (my_bucket, my_prefix)# Run local simulatordevice = LocalSimulator()bell = Circuit().h(0).cnot(0, 1)print(bell)print(‘local simulator results: ‘ + str(device.run(bell, shots=100).result().measurement_counts))# Run AWS simulatordevice = AwsDevice(“arn:aws:braket:::device/quantum-simulator/amazon/sv1”)bell = Circuit().h(0).cnot(0, 1)print(‘aws simulator results: ‘)get_result(device, bell, s3_folder)

我的输出:

作者图片

Braket 示例笔记本

高级电路算法

  • 格罗弗的量子算法
  • 量子振幅放大(QAA)
  • 量子傅立叶变换(QFT)
  • 量子相位估计(QPE)

混合量子算法

  • 量子近似优化算法
  • 具有变分量子本征值解算器的横向伊辛模型(VQE)

量子退火

  • D 波量子退火剖析
  • d-波图划分—二次无约束二元优化(曲波)
  • d 波最大割问题(MaxCut)
  • d 波最小顶点覆盖问题

简单电路算法++从这里开始

  • 量子电路剖析
  • 后端设备— GHZ 状态准备
  • 后端设备—响铃状态准备
  • 超密集编码

全部的

我很高兴在笔记本实例中看到如此详细且有教育意义的笔记本。这些不是入门级的概念,但是很好地分解了工作流程。这是一个很好的平台介绍。上面,我列出了提供的例子的不同文件夹。我建议从简单的电路算法开始。这些说明会引导你完成电路的构建。电路可视化选项不如其他一些平台好,但我想这将随着时间的推移而改善。众所周知,AWS 将产品的核心推向市场,然后在后续版本中添加更友好的功能。价格合适,就试试吧。

一如既往—记得停止并删除您的笔记本实例。

作者图片

面向新生儿的量子计算

原文:https://towardsdatascience.com/quantum-computing-for-the-newb-5e0737e3ca4?source=collection_archive---------49-----------------------

奇点研究

介绍性概念

在这篇文章中,我们将基本介绍我们的 免费课程材料 用于量子计算。这是为绝对初学者开设的入门课程。如果你不是计算机编程或数学方面的专家,这门课程就是为你开设的!它介绍了量子计算所需的所有基本数学,还将介绍你需要的基本编程技能。最棒的是,一切都在互动、在线的 Jupyter 笔记本中。所以不需要下载任何东西,也不需要学习终端。如果您已经对 Python 感到满意,那么这将是轻而易举的事情!你可以更多地关注量子物理学中的概念,如状态向量、用矩阵表示的量子门和量子电路图。要获取资料,请点击Github 页面链接。

我们使用的主要工具是在线交互式 Jupyter 笔记本中的 Python、NumPy、Qiskit 和 PennyLane。所有这些都是免费的,并且已经设置好可以在笔记本中使用。在这十次讲座中,既有例子,也有大量的练习。这是一个初步的测试,旨在发现对该主题的兴趣,并激发那些对量子计算感兴趣的人之间的合作。

如果你对量子计算了解不多,让我们稍微讨论一下。

由 Karlis Reimanis 在 Unsplash 上拍摄的照片

量子计算的线性代数

这是线性代数和量子计算的数学先决条件课程的材料。它包含 Jupyter 笔记本,可以作为在线交互式笔记本下载或在 Binder 中打开。

为什么是线性代数?

线性代数是矩阵和向量的语言,是量子计算和量子信息的基础语言。通过线性代数来处理量子计算是在关于这个主题的被引用最多的教科书中采用的方法:Nielsen & Chuang 的《量子计算和量子信息》。这种方法在数学上也是完全初学者最容易接近的方法。如果你在数学、物理和计算机编程方面没有什么背景知识,这种方法很适合你!

量子计算是什么,不是什么

量子计算是一种处理信息的方式,与笔记本电脑或智能手机处理数据的方式截然不同。处理信息的“经典”方式是以比特为单位,即 0 和 1 的二进制值。量子计算利用量子物理的三个不同属性来处理信息。

[## 2019 年即将改变世界的技术|数据驱动的投资者

很难想象一项技术会像去年的区块链一样受到如此多的关注,但是……

www.datadriveninvestor.com](https://www.datadriveninvestor.com/2019/01/17/the-technologies-poised-to-change-the-world-in-2019/)

叠加

叠加是一个用来描述量子比特的奇特词汇,经典版本的比特可以以零和一的组合存在。量子位也可以在不同的中测量,允许像状态的自适应测量,以及基于量子计算的测量。由于量子位可以处于 0 和 1 的混合状态,这赋予了它们经典位所不具备的计算特性。

纠缠

纠缠这个词用来描述量子比特如何能够拥有与其他量子比特状态相关的状态。量子位相互作用,变成纠缠态,这意味着它们的状态现在在统计上是相关的,关于一个量子位的信息有时可以揭示另一个量子位的信息。多个量子位可以纠缠在一起,一次和复杂的行为可以出现。科学家们非常感兴趣的一个研究领域是多体纠缠。这是一种复杂的纠缠形式,表现出高度非经典的行为。这可以用于量子隐形传态和量子密码协议。它也是一个以爱因斯坦、波多尔斯基和罗森命名的著名实验的来源,以及约翰·贝尔的著名实验,这些实验表明局部隐变量理论极不可能是真的。

干扰

干涉就像我们在光波、声波和水波中看到的干涉一样,由量子计算机展现出来。量子计算机可以有建设性(加性)干涉,也可以有破坏性干涉,波不同步,相互抵消。在讨论波粒二象性和量子物理学中著名的测量问题时,这种有趣而奇怪的行为常常成为争论的话题。在计算中,干涉可以用来增强“好信息”和消除“坏信息”。这使得我们能够以巧妙的方式进行计算,并利用干涉作为一种信息处理技术来解决问题。

作为专用集成电路的量子计算机

ASICs 或 专用集成电路 是为非常特殊的计算任务而制造的专用计算机芯片。这方面的一个很好的例子是 GPU 或 图形处理单元 ,或谷歌的 TPU(张量处理单元),它们在近年来大大增强了机器学习。其他例子可能是英特尔设计的 神经形态计算芯片 。这些都是为了解决特定类型的问题而设计的,例如增强机器学习任务。量子计算机可以用非常相似的方式来思考。有一个经典的计算机接口将某些计算任务委托给量子硬件。量子硬件然后处理计算任务,然后经典计算机接收输出数据。量子计算机可以解决的问题有很多种。事实上,任何经典计算机可以解决的计算任务,量子计算机也可以解决。然而,经典计算机在某些事情上仍然更有效率,所以它们不会去任何地方。与所有已知的经典方法相比,有许多算法和问题可以在量子计算机上以指数级更快地解决。

找出这种计算优势从何而来,是量子计算研究的中心任务之一。有一个量子算法动物园,它列出了许多已知的比经典方法加速的算法。奇点的大部分重点是研究这些不同的应用和算法,并用不同的量子计算语言实现它们。一些应用包括使用 Shor 算法来破解 RSA 公钥密码和其他算法来破解椭圆曲线密码。如果你有兴趣发现量子算法的内部工作原理,并试图了解这种量子优势或优势来自哪里,请查看这些讲座。

Python 中的量子计算

原文:https://towardsdatascience.com/quantum-computing-in-python-5e7e8efd0edf?source=collection_archive---------34-----------------------

奇点研究

量子计算的线性代数

在这篇文章中,我将介绍理解量子计算所需的基本线性代数。在本文中,我们将只使用 NumPy,在文章的最后,您会得到一些交互式 Jupyter 笔记本的介绍,因此您不需要下载任何东西或学习终端来开始。你所需要的只是一个网络浏览器。如果你愿意,你可以下载笔记本并在本地运行。

由沙哈达特拉赫曼在 Unsplash 上拍摄的照片

如果您想在 Jupyter 笔记本上本地运行这些命令,首先您需要下载 Anaconda 的最新(免费)发行版,可以从这里下载。下载 Anaconda 后,打开 Anaconda navigator。下载后,图标将出现在您的应用程序中:

Anaconda 导航图标

一旦你打开 Anaconda Navigator,你可以选择打开一个“Jupyter 笔记本”或者“Jupyter 实验室”。两者都可以,但我个人更喜欢 Jupyter Lab。

巨蟒领航员

如果你不想下载任何东西,你可以通过点击这个链接在你的浏览器中尝试 Jupyter Lab。一旦你打开 Jupyter 实验室,点击小“+”按钮打开一个新的 Python 3 内核。可以在 Jupyter 图标旁边的“文件”和“编辑”下找到。

单击“+”按钮会出现以下窗口。单击橙色笔记本图标下的 Python 3 图标。

这将导致一个空白的单元格(如果你没有切换到“设置”下的“黑暗模式”,它可能是白色的,这是我的设置,并导致你在这里看到的光滑的黑色美学。

在此窗口的空白单元格中,键入以下命令按住“shift”键,然后按“enter”键运行单元格。这将加载允许我们做线性代数的 NumPy 库。

导入数字

创建复数和算术运算

在 Python 中使用 NumPy 创建复数非常容易。我们可以使用 NumPy 库创建两个复数𝑧=3+4𝑗和𝑤=1−1𝑗。这里,记住" j" 是虚数单位,在大多数数学课程中用" i 表示,复数通常写成"𝑎+𝑏𝑖"而不是" 𝑎+𝑏𝑗". "我们将从头到尾使用这两种符号,因此熟悉这两种符号并在两种符号之间流畅地转换是很重要的。

在 Python 中定义复数

我们可以把𝑧和𝑤:这两个复数相加

在 Python 中添加两个复数

我们也可以减去两个复数:

减去复数

python 中复数相乘的过程如下:

复数相乘

我们可以为𝑧=3+4𝑖:打印复数的实部和虚部,如下所示

打印实部和虚部

向量

向量将是我们研究量子计算的基础。量子计算机中最基本的计算单位是一个量子位,它可以表示为一个长度为 1 的二维复数向量。所以理解向量对于我们在这本书里将要做的大部分事情来说是基础和必要的。向量可以有多种理解方式,其中最基本的一种就是简单的数字数组,我们通常将它表示为一列数字,称为列向量,但在某些情况下,我们还需要行向量:

行和列向量

我们可以用 Python 创建一个列向量和一个行向量:

Python 中的行和列向量

这应该让你开始学习你需要的线性代数,但是你肯定需要更深入地了解这些在量子计算中是如何使用的。为了继续下去,我们有一个交互式笔记本,你可以在你的浏览器中访问它,你可以点击这个链接。对于整个线性代数系列的笔记本请查看本页。

如果你对深入学习量子计算感兴趣,考虑支持这个项目或者为它做贡献!奇点研究正在不断生产量子计算笔记本,将教科书和研究论文翻译成交互式笔记本,以学习和使用量子计算和量子机器学习。联系

thesingularity.research@gmail.com

并包含主题“黑客宇宙”如果你有问题,想投稿,或者对使用量子计算和量子机器学习有兴趣。

量子因子分解

原文:https://towardsdatascience.com/quantum-factorization-b3f44be9d738?source=collection_archive---------30-----------------------

肖尔算法

图像通过 Unsplash

因式分解问题是每个人都能理解的问题之一,我们大多数人都觉得这是他们的机会:数学名人堂的神圣突破口:

设 N,一个具有相对大的二进制表示(例如几百位)的奇整数和 p,q 个素数,使得:

1。 p 和 q 的大小差不多(这并不重要,但显然你不能用一个容易实现的质数来构造 N)

2。 N 已知

3 N =pq

在 log(n)的多项式时间内求 p & q。

的确,极其容易理解。然而,不太容易实现。它是如此的“不容易”,以至于大量的加密产业依赖于利用这种特殊方式的传奇算法 RSA。

处理因式分解问题的主要算法有:

  1. GNFS(一般数域筛)的复杂度为:

2.复杂性为的 QS(二次筛)

这个算法是否有多项式解的问题仍然没有答案。

然而,随着数论的发展,似乎可以找到因式分解的等价问题。这样的问题就是“周期寻找”

期寻

正如我们在“超越周期”一节中所描述的,寻找周期是因式分解的一个等价问题。

N 为超出部分(即可以写成两个素数p&q的乘积的奇数)。

定义:a,整数 s . t . 0<a<Na可逆N (即存在 0<b<N 和 K 个整数 s . ta** b = KN+1*

主张: a 是可逆 modN<>GCD(a*, N ) =1*

符号:我们用 b( N )来表示 b 模 N

费马小定理暗示着

这暗示我们,这样的指数可能有周期。

定义:一个函数 f 有一个周期* r 如果:*

r 是一个整数

对于所有 xf(x+r)=f(x)

没有整数m<rs . tf(x+m)=f(x)

a 一个可逆数 mod N

我们定义一个函数 F

假设 F 有一个周期 r ,很容易看出 F ( r ) =1,那么

第二个因子是 N 的乘法,即我们有一个平凡的因子分解。

示例

N =65, a =47。这个环中 47 的周期是 4。我们观察得到的等式:

可以看出,第二项是 65 的倍数。因此,因子分解还没有实现

让我们重新观察一下这个公式。我们可以看到:

1 没有一项是 N 的乘法

2N的每个质因数都存在于左手边。

我们可以得出这样的结论:

因此,当我们有一个周期时,我们可以因式分解 N

例子

N =15 ,a =7。7 (15)的周期是 4。

因此我们有

GCD(49–1,15) = 3 GCD (49+1,15) = 5

3,5 是 15 的质因数,所以我们得到了所需的

我们可以把这个过程总结如下:

酷!我们可以分解…一件小事:

怎么才能找到周期!!?!

量子部分

对于不熟悉量子计算的读者,我在下面添加了教程。我将简要描述基本对象,如量子位和量子门,但我建议你在更广泛的框架中学习它们。基本上,量子计算机出现在 80 年代初,当时传奇物理学家理查德·费曼“挑战”计算机科学家开发一种基于量子物理的计算机。主要动机是理解模拟量子系统是不可能的:过程总是粒子数量的指数。费曼还担心量子计算是否至少和经典计算一样普及。当工程师了解到晶片微型化开始遭受量子效应时,另一个动机出现了。在过去的几十年中,量子计算经常与 NP 问题以及量子计算是否使它们易于处理联系在一起。

基本量子实体

量子位——量子计算的基本单位。与经典比特相反,量子比特的一般结构具有以下形式

q= a|0> +b|1 >

量子位被认为处于叠加态。a 和 b 是复数,它们的幅度之和是 1。

|0 >,|1 >是我们测量的基中的向量

张量积

由两个较小向量空间 X,Y 组成的向量空间表示为

这个空间上的算子是每个子空间上的双线性算子。就矩阵而言,张量积具有以下形式:

量子门

对一个或一组量子位进行操作的逻辑门。在量子计算的世界里,这些门是用酉矩阵实现的

普通门

量子傅立叶变换

量子傅立叶变换是众所周知的 FFT 的量子模拟。在量子态 x 上,QFT 定义如下:

其中 N2 的幂(log( N )=n) n 因此是量子位的个数。很容易看出,QFT 是一个酉算子:**

|QFT|x>| =|x|

=0 => ,QFT|y> = 0

我们可以把 QFT 观察成一个维数为NxN(一个巨型的)的矩阵,它的(x,y)项是超过 的系数。当 Shor 发表他的算法时,他的成就之一集中在改进比特串输入的 QFT。

二进制字符串的 QFT

“传统上”FFT 将复数转换为复数,但这里我们考虑输入 x s.t. x 是一个整数,x<N因此它具有唯一的二进制表示,在 1994 年 Coppersimth 的一篇论文中使用了 N 位,他已经表明我们可以将 QFT 分解为 N 个量子位上的张量积[CopperSmith]。如果我们用 D、定义大矩阵,我们可以重写变换:

其中 b 是量子位

显然,我必须在这里展示整个严谨的开发过程,但我希望读者能留下来。这种表示的主要好处是,我们可以使用 n(n-1)/2 哈达玛和相移运算来写 QFT,即多项式次数的运算:

****

在 Shor 的术语中,他使用了以下符号:

****

对于

该算法变成:

因此,我们有动力开发一种用于周期查找的算法,以及将 QFT 用于该算法的好处(自然,每个工程师都知道 FFT 用于查找频率,因此这是一个自然的步骤)。现在让我们来组合这个包。

肖尔算法

你可能会猜测 Shor 算法的目的是找到我们在第一节中讨论过的周期 r 。可以观察到:

其中 Hn 是 n 阶哈达玛矩阵。设 N 我们要因式分解的数。我们选择 q 使得:

即 q 是 2 的幂,这是 QFT 矩阵的阶,l 是量子比特的数量

我们也将定义一个函数 f 如下 :

回想一下, N 是要因式分解的数字,而xIs 是我们要搜索其周期的数字。我们需要两个寄存器第一个包含 l 量子位,第二个需要足够的空间来获得整个似是而非的结果 mod N (即超过 log(N**))我们用表示它因此寄存器具有以下形式

在某些地方,右寄存器的概念是“输出寄存器* r”***

为了使量子位叠加,我们执行 l 阶哈达玛矩阵

下一步只是计算第二个寄存器的函数 f

请注意,现在寄存器是纠缠在一起的(很明显,每个 a 都映射到它自己的函数值)

在肖尔的原始论文中表现了 QFT 这一阶段。然而,在后来的版本中,通常的方法是在第二寄存器上执行测量。右(输出)寄存器随后被压缩为单个值,并且在第一个寄存器中只留下 f 将其映射到该值的状态,我们用 A 表示这些状态的数量。我们用 m 来表示这些值,其中 m 是整数,m < N . 系统现在具有以下形式:

假设 x0 是 a 态这样的f(x0)=mr是 x0 的周期我们可以把我们的量子态写成:

在哪里

显然 x0 < r (否则 r 不是句号)

我们现在对第一个寄存器执行顺序为 q 的 QFT。

下一步将是测量这个量子态中的一个状态。我们希望验证我们有很高的概率得到一个结果,将导致一个正确的时期。因此,我们需要展示概率分析。

作为第一步,我们可以看到,我们可以重写当前的量子态为

我们希望估计某个状态的概率 |y >

我们有:

那留给我们

我们对满足的 y 感兴趣

(在一些书中它们被称为“好 y”)我们可以看到,概率项实际上是一个几何数列。因此,我们可以做到以下几点:

该序列的总和由下式给出:

余弦定律意味着

对于提名者,我们有:

因此,我们估计的概率是

既然我们有关于ry’s(| ry(q)|<0.5r)** 的概率是**

大约是 0.4。

为什么我们关心这样的 y?

****

索赔?!现在吗?让我们做一些代数

我们有

由于 y 和 q 已知,我们可以从权利要求中推断出,在 y/q 附近只有一个分数具有如此小的分母(在比 1/(2q)更低的距离上小于 N )。

我们现在必须提供一种有效的方法来找到这样一个分母(即 r)

连分式展开式

连分数展开是一种通过分数计算使用一系列整数来表示一个数的方法。如果 x 是要近似的数字,一般形式将是:

其中 a 的是整数。注意,当最后一个分数有一个指定者 1 时,过程总是终止

一个很好的结果表明有理数有一个有限的展开,而无理数有一个无限的展开。

例子

我们希望得到分数 867/42 的近似值

结果是这样的

分数 79/14 有膨胀

有时我们对一个分数的第 n 次收敛感兴趣,也就是只进行第一步。例如,在我们的示例中,我们将取 j=4 ,因此我们将取向量[5,1,1,1]并重构一个接近原始分数的有理数。

我们如何利用它?

所以我们有了分数 y/q ,我们希望找到一个分数,它既:

近似 y/q (距离小于 1/(2q) )

有一个分母小于N。因此,只要分母小于 N ,我们将展开一个不同阶的连分式展开式

文献学

https://arxiv.org/pdf/quant-ph/0201067.pdf【铜匠】

https://en . Wikipedia . org/wiki/File:Quantum _ circuit _ for _ Quantum-Fourier-Transform _ with _ n _ qubits

https://arxiv.org/pdf/quant-ph/9508027.pdfT3【肖尔】

**【https://www.quantiki.org/wiki/shors-factoring-algorithm **

https://gadial.net/2014/08/24/shor_algorithm/

https://www.youtube.com/watch?v=_zTY_Rhb2Js

https://cosmos magazine . com/physics/quantum-computing-for-the-qubit-curious/

http://mmrc . amss . cas . cn/TLB/201702/w 020170224608149125645 . pdf

https://academic.oup.com/nsr/article/5/4/598/4987212

http://www.math.umd.edu/~lcw/three68.pdf

https://leftasexercise . com/2018/11/26/shors-quantum-factoring-algorithm/

http://www . maths . surrey . AC . uk/hosted-sites/R .克诺特/斐波那契/cfCALC.html

https://interest ing engineering . com/how-Peter-shors-algorithm-dooms-RSA-encryption-to-failure

https://interesting engineering . com/how-Peter-shors-algorithm-dooms-RSA-encryption-to-failure[图片]

https://images . unsplash . com/photo-1477244075012-5cc 28286 e465?IX lib = r b-1 . 2 . 1&ixid = eyjhchbfawqiojeymdd 9&auto = format&fit = crop&w = 334&q = 80

连分数展开

**【https://www.youtube.com/watch?v=R5HhNmFPLPQ **

https://en.wikipedia.org/wiki/Continued_fraction

https://www.math.u-bordeaux.fr/~pjaming/M1/exposes/MA2.pdf

https://planetcalc.com/8456/

量子计算教程

https://www . coursera . org/learn/quantum-computing-algorithms/home/welcome

https://courses . EDX . org/courses/course-v1:MITx+8 . 370 . 1 x+1t 2018/course/

https://medium.com/qiskit/hello-quantum-2c1c00fe830c

https://python programming . net/quantum-computer-programming-tutorial/

https://www-users.cs.york.ac.uk/schmuel/comp/comp.html

Python 包

https://github.com/Qiskit

一维量子引力

原文:https://towardsdatascience.com/quantum-gravity-in-one-dimension-456dc83c59f9?source=collection_archive---------24-----------------------

为什么 1D 的量子引力描述了 4D 时空的粒子

图片由皮克斯拜的 Gerd Altmann 提供

量子引力可以说是理论物理的圣杯。虽然一个彻底发展的量子引力理论仍然超出了我们的理解范围,物理学家相信它可能包含一些基本的成分。在这篇文章中,我将展示一维量子引力理论也描述了四维时空中的粒子。

图 1:这个图显示了量子引力在物理理论层次中的位置(来源)。

其中一个要素是费曼历史总和形式主义。后者将跃迁振幅(或传播子)表示为系统从某个初态到某个终态的所有可能时空路径的加权和。在量子引力的背景下,路径积分的平均值不是在时空的路径上,而是在时空的几何上(然而,正如 Witten 所指出的,这种描述在微观层面可能是无效的)。

图 2:左边显示了量子力学历史总和方法的路径。(来源)。右边是量子引力中相应的和,其中时空 g 是类似于路径 x[t]。时空 g 连接两个三维空间 h (基于源)。

你可以看我的上一篇文章,了解关于量子力学历史求和方法的更多细节。

[## 量子引力、永恒和复数

宇宙的波动方程是永恒真实的吗?

towardsdatascience.com](/quantum-gravity-timelessness-and-complex-numbers-855b403e0c2f)

要建立一维量子引力和四维时空的量子场论(QFT)之间的对应关系,我们首先需要了解这两个成分中的每一个。让我们从后者开始。这里遵循的方法论通常被称为 QFT 的世界观(参见斯金纳讲座)。

标量场理论和克莱因-戈登方程

量子场是电磁场、爱因斯坦引力场等经典场在量子框架下的延伸。

图 3:这张图片说明了行星地球的磁场对抗太阳太阳风的情况(bykoya 979/shutterstock . com)。

在 Zee 中,作者将量子场论定义如下:

"量子场论是对生命短暂本质的回应."

—阿·齐

这个定义来自于下面的观察:结合狭义相对论和量子力学,暗示了粒子“可以生与死”。

让我们明白为什么。能量-时间不确定性原理指出人们无法确定只存在短暂时间的量子态的能量。在数学上,这可以表示为:

等式 1:时间-能量不确定关系。

事实上,真空能量剧烈波动,如下图所示,允许创造成对的(粒子-反粒子)虚粒子。

图 4:根据能量-时间不确定性原理,真空波动是一个空间区域中能量数量的短暂而强烈的变化。(来源)

作为狭义相对论的一个结果,这导致了能量转化为质量的可能:新的粒子可以“诞生”,现有的粒子可以“消失”。问题是标准的量子力学框架无法解释这一现象,因此需要修改。这个量子力学的修正版就是 QFT。

此外,QFT 使我们能够解释,除了其他事情之外,为什么在宇宙中有基本粒子的不可区分的副本:粒子仅仅是量子场的激发。

图 5:图片显示了一个粒子碰撞的模拟,产生了一个希格斯玻色子。后者是 QFT 的著名预言(来源)。

热身:简单的古典弦乐

量子场相当抽象。在这个主题的几个处理之后,为了给读者一些直觉,我将使用一串相互耦合的质量为 mN 谐振子的类比。仅考虑垂直 qᵢ ( t )位移。

的相应操作是:

等式 2:N 个谐振子的经典作用,其中 M 是对称且正定的矩阵。

其中矩阵 M 对称且正定:

等式 3:矩阵既对称 M 又正定。

对角化 M 我们得到正常模式的动作:

等式 4:正常模式的动作。

ω s 是质量振荡的固有频率。

下一步是获得连续标量经典场 ϕ ( xt )的作用。标量场QFT 最简单的场类型。解读ϕ(xt )的一个有用的方法是,把它看成是每个点上的一组无限的振子 x (参见穆哈诺夫和 Winitzki )。注意,当我们取连续极限时,振荡器的指数 i 变成空间坐标 x 。连续体极限中的作用变为:

方程式 5:自由大质量标量场的作用。

根据量子力学的路径积分公式,系统在两个状态 I 和 F 之间跃迁的概率为

方程 6:量子力学路径积分公式中的跃迁振幅。

在这种情况下,拉格朗日密度由下式给出:

方程式 7:大质量标量场的拉格朗日密度。

其中:

生成泛函或配分函数,等式的一个特例。6 量子场论中的一个关键对象,是从真空态回到自身的跃迁振幅,它由下式给出:

方程式 8:生成泛函或配分函数,量子场论中的一个关键对象。

真空状态在上面的图 4 中示出。

抛开关于量子化方案的 在本文中,我将遵循量子化的路径积分方法(而不是规范的一)。正如我们刚才看到的,这种方法是基于拉格朗日,它是相对论不变的,使得路径积分也明显不变。此外,积分内的对象是经典量(这是非常方便的,因为在这个公式中没有令人讨厌的非交换算子,例如)。

我们注意到,与规范量子化相比,该方法涉及到视角的转变。系统的哈密顿动力学由等式定义。6(参见销套)。因此拉格朗日量成为量子场论的“最基本规范”(见 Peskin )。

让我们回到 Eq。4 对于真正的标量场理论。对动作应用最速下降法近似得到欧拉-拉格朗日运动方程:

等式 9:求动作的极值。

图 6:展示最速下降法的动画(来源)。

结果就是无处不在的克莱因-戈登方程:

方程 10:大质量标量场服从的克莱因-戈登方程。

跃迁振幅的显式计算

重写拉格朗日显式我们得到,方程。8:

方程 11:方程 8 中的生成泛函或配分函数,明确地写出拉格朗日密度。

其中包括外部电势或源电流 J 。包含 J 的项是与标量场 ϕ ( x )和源电流 J ( x )相互作用相关的势能项。

这个积分只是平凡的高斯积分的复杂版本:

并且可以使用高斯积分的标准程序来评估。我们获得:

等式 12:在ϕ.上积分后生成泛函的表达式

函数 D ( x )是自由传播子,它是以下微分方程的解:

方程式 13:自由传播子遵守的方程式。

来自 Eq。13、我们可以计算动量为 p、的粒子在动量空间中的费曼传播子,其中是对象乘以被积函数中的指数,对于D(x-y)如下:

方程 14:自由传播子写成动量空间传播子的傅立叶变换。

其中 ε 是一个小的正数。据我们所知,一个无声的假设是我们正在洛伦兹签名中工作。给定时空的签名是对角化后度量张量的矩阵表示上的正值和负值的个数。对于一个 n 维洛伦兹流形,

等式 15:n 维洛伦兹流形。

签名是(-、+、+、+、…、+)其中有一个 0 和 n 个 - 1 个个(在我们的讨论中, n 个 = 4 个)。

相反,如果我们在欧几里得签名 (+,+,…,+)中工作,正如我们在本文的其余部分中经常会做的那样,我们在质量项变为正之前会丢失 ε 和符号。传播方然后读取:

方程 16:作为欧几里德动量空间传播子的傅立叶变换的自由传播子的欧几里德版本。

现在让我们深入讨论第二个要素(见引言的最后一段),即一维时空中的量子引力。

一维量子引力

我们现在的目标是建立一个量子场论或一维 QFT,在那里场与引力相互作用正如斯金纳所说,要建设我们的 QFT,我们需要两个基本要素:

  • 理论“生存”的时空(“T6”)我们的宇宙(“T7”)
  • 将被研究的对象,即在这种情况下的场。这些可以有几种类型,但是这里我只考虑标量字段。

一维紧致流形

一维上只有两种可能的紧致流形(闭有界流形),即圆 S 和区间 I :

图 7:一维中仅有的两种可能的紧致流形。圆圈上的点表示点 0 和 t 之间的标识。

我们可以用一个实数来标注这两个流形的点:区间 I 和圆 S 可以用T∈【0,T】来参数化。不同的是,在 S 中,我们识别出 tt +T (参见斯金纳)。每个流形都有其关联的度量 g ,在一维中,它是一个 1 x 1 张量。我们将度量和逆度量分别表示为:

等式 17:1D 流形的 1x1 度量(下指数)和 1x1 逆度量(上指数)。

数学插曲

本节将简要介绍回拉的概念,以备将来使用。

回调 微分几何中一个有用的概念是回调(更多细节见贝兹和穆尼阿因和卡罗尔)。考虑两个流形 MN,ϕf,以及它们的组成,都在下图中说明。

图 8:回调(基于卡罗尔)。

从图中我们看到

函数 ϕ 将函数 fN 拉回 M 由以下复合函数定义:

等式 18:函数 ϕ.对函数 f 的拉回

图 7 显示了地图 ϕ.将 fN 拉回 M 的过程

引入标量字段

现在让我们用( Mg )来表示上面两个一维流形中的一个,即区间 I,并让它成为场“存在”的时空。我们还介绍了另一种尺寸为 n 的歧管( NG)。后者称为目标空间,是要研究的物体的空间,即标量场(参见斯金纳)。

现在考虑这两个流形之间的映射

让我们在 N 上引入坐标并选择一个开放的面片u⊂n带有 n 局部坐标**

其中对于每个 tM 都有一个**×t17t)在 U. 上面的 n 对象×中的每一个都是一个标量字段(虽然这类字段的标准表示法是 ϕʲ,这样选择会更方便)。******

下图使这些定义更加清晰:

图 9:从度量为 g 的流形 M(两个可能的 1D 流形之一)到目标空间 N 的映射,目标空间 N 是一个 N 维流形。对 U 中的每个点,我们关联一个标量场 x (基于这个源)。

基于前面的讨论,我们通过函数 x 将局部坐标 xⁱ ( t )标识为拉回至开面片 u 内的坐标 M

广义相对论作用

我们现在将写下我们理论的广义相对论作用。一般来说,广义相对论的作用是:

方程 19:包括物质场的广义相对论作用。

在这个表达式中,有四个重要的量:

  • 平方根内的 g 是度量张量的行列式:

  • R 是 Ricci 曲率标量其中描述了时空在每个点附近的几何形状(见下图)。

图 10:该图显示了三个表面。第一种是具有负局部曲率 R<0 的双曲面;第二个是圆柱体,零曲率的曲面,第三个是球体,正曲率的曲面。

  • λ是宇宙常数,与真空的能量相关,与暗能量的概念相关
  • 时空的体积元素是符号

  • 括号内的第三项是物质场的拉格朗日密度(稍后将更详细地讨论)。

当我们只有一维时, R 消失(曲线的固有曲率不存在)。 R ≡ 0 的一个结果是,在一维空间中,重力是非动态的(参见斯金纳)。对于 1D 流形,上面的操作比方程 1 简单得多。19,可以写成:

方程 20:1D 流形的广义相对论作用。注意 Ricci 标量的缺失和度量 g 的存在表明标量场与引力相互作用。

括号内的第一项代表标量场,其中 G 是对 N 的度量的拉回M注意没有该项**

尽管如此,我们仍然可以建立一个量子引力理论,将度规和物质场联系起来。度量 g 的存在表明标量场与引力相互作用。选择目标空间中的洛伦兹度规

方程 21:四维闵可夫斯基度规张量。

动作 S 变为:

等式 22:具有上面给出的闵可夫斯基度量 G 的动作 S。

如果我们相对于**改变 S ,则爱因斯坦场方程 (EFE)可以从该作用中导出。 定义 2λ≦m“EFE”读作:****

方程 23:我们理论的“爱因斯坦场方程”。

这个方程可以用“共轭动量”来表示:

使用引号的原因是为了提醒我们, x 是标量字段,而不是位置坐标(到目前为止):

方程 24:用共轭动量表示的“爱因斯坦场方程”。

遵循规范量化的标准过程(并设置 ħ = 1)

方程 25:共轭动量由微分算子代替。

情商。24 变成了:

方程 26:正则量子化过程后量子波函数的微分算子。

其中我们看到系统的量子波函数ψ被与方程关联的微分算符湮灭。24.如果我们重新参数化流形 M 或者等效地,如果我们执行一般的坐标变换

度量张量变换如下:

现在,我们可以按照我们想要的任何方式重新调整度量。选择:

将符号从 x ⁰改为 t 并设置 n=d= 4 我们得到克莱因-戈登方程 (KGE):

方程 27:量子化一维经典引力得到的克莱因-戈登方程。

KGE 描述自旋为-0 的粒子。著名的希格斯玻色子是第一个也是唯一一个观测到的自旋为-0 的粒子。

我们得出结论,在一维空间中存在一个非平凡的量子引力理论,它描述了一个大质量粒子在四维闵可夫斯基时空中运动的动力学。注意,在新的解释中,标量场变成了时空坐标。

过渡幅度

现在让我们计算一个粒子从时空中的一点 x 传播到另一点 y 的跃迁振幅(或传播子)。参见图 1 的左侧,其示出了构成通用传播器的路径的示例。

为了方便起见(遵循斯金纳和威滕)我们将选择 n 维流形 N 为一个具有欧几里得度量的四维欧几里得时空:

我们将评估传播子 G 也使用一维流形中的欧几里德签名 M. 在海森堡图中,传播子读取:

等式 28:跃迁幅度。它的平方模数给出了一个粒子在τ=0 的 x 处和τ= 0 的 y 处被发现的概率。

关于欧几里得时空的全面讨论,请看我以前的一篇文章:

***** [## 循环虚时间和温度之间的神秘联系

一个显而易见的事故可能蕴含着物理学上的一个巨大谜团

towardsdatascience.com](/the-mysterious-connection-between-cyclic-imaginary-time-and-temperature-c8fb241628d9)

这个表达式可以写成由下面的积分给出:

其中 T≦τ。固定 τ 处的传播子可以表示为所有可能路径上的路径积分:

在量子引力中,为了计算跃迁振幅,我们必须对一维 M 上所有可能的度量进行额外的积分,直到微分同胚。然而,请注意,上述重新参数化的自由度受到以下事实的限制:对于任意坐标变换, M 的总长度不变:

长度是 M 的唯一不变量。因此,在这种情况下,在所有可能的度量上积分直到微分同胚,意味着简单地在所有可能的长度上积分(可以解释为适当的时间)。因此:

除了因子 2 之外,这是我们之前见过的欧几里得四维空间中质量为 m 的粒子的标准标量场传播子。因此,通常的传播子可以表示为一维量子引力流形上的积分。我们再次看到,一维的量子引力也描述了四维时空中自旋为 0 的粒子。换句话说,我们可以用一维的量子引力理论来解释一个在四维时空中运动的自由粒子!*****

一如既往,感谢您的阅读,再见!建设性的批评和反馈总是受欢迎的!

我的 Github 和个人网站 www.marcotavora.me ,有一些关于理论物理和深度学习和金融等其他主题的其他酷东西。看看他们!

量子引力、永恒和复数

原文:https://towardsdatascience.com/quantum-gravity-timelessness-and-complex-numbers-855b403e0c2f?source=collection_archive---------4-----------------------

宇宙的波动方程是永恒真实的吗?

图片由 Bruno /德国来自 Pixabay

从我们宏观人类的角度来看,宇宙似乎不是量子力学的。在我们看来,宏观物体在空间和时间上基本上是局部的。这与它们的量子本质形成了鲜明的对比,在量子本质中,纠缠、非定域性和干涉等属性被证明是持续发生的。只有极少数情况下量子现象可以被宏观地观察到,例如在超导体和超流体中。但是量子特性确实在能量和物质的微观尺度上占主导地位。

图 1:悬浮在液氮冷却的超导体上方的磁性物体的概念图(作者kts design/shutterstock . com)。

在量子力学中,正如将要详细描述的,物质具有波动性。更具体地说,一个质量 m 动量 p = mv (其中 v 是速度)的粒子,其波长 λ = h / p 其中 h 是所谓的普朗克常数。为了说明宏观物体的量子特性是如何不可探测的,考虑一下科恩-坦努吉、迪乌和拉罗在本书中给出的例子。与以每秒 1 毫米的速度运动的尘埃粒子(其直径约为 0.001 毫米)相关的波长约为10⁻ ⁶米(或 1/1 万万亿),这在尘埃粒子的长度尺度上实际上是可以忽略的。

图 2:现代物理学主要领域的四个领域。量化过程对应于虚线框内的箭头(基于来源)。

因此,物理学家首先发展了经典或非量子理论。上图说明了物理学的四个可能领域,它们取决于被研究系统的大小和速度。

为什么我们需要引力的量子理论?

将引力,更具体地说是广义相对论(GR)统一到量子力学(QM)框架中,可以说是当今物理学中最重要的未解决问题。难点在于广义相对论和量子力学有着极其不同的数学和概念结构。前者是基于光滑时空的几何特性,而后者是基本概率性的,并基于测不准原理,根据测不准原理,测量某些物理量的精度有一个基本限制。

继基弗之后,我们可以将量子化引力的主要动机分成三个副主题:其他统一方案的成功,关于黑洞和宇宙学的物理学的未解问题,以及所谓的时间的问题。

一致

量子场论 (QFT)是经典场论、狭义相对论和量子力学的结合体(见图 2),是现代物理学中实验最成功的理论之一。QFT 将所有的非引力统一到一个框架中。既然所有已知的粒子都与引力耦合,那么把它包含在这个框架中似乎至少是合理的。

图 3:粒子物理学标准模型的基本粒子。它们包括六个夸克,四个规范玻色子,以及最近测量的 希格斯玻色子 ( 来源)。

在给他的朋友、数学家马塞尔·格罗斯曼的一封信中,爱因斯坦做了如下评论:

"发现一系列起初似乎完全分离的现象的统一是一种光荣的感觉……"

——阿尔伯特·爱因斯坦

黑洞和宇宙学

根据著名的彭罗斯-霍金定理,奇点在 GR 中是不可避免的(在相当温和的条件下——参见巴罗)。奇点的例子有黑洞内部的奇点和宇宙诞生中的奇点。GR 的量化可以消除这种差异。

图 4:黑洞奇点的 2D 类比图(作者 vchal /shutterstock.com)。

时间的问题

普通量子力学中的时间是绝对的,在物理系统之外。量子场论结合了特殊的相对论,平的闵可夫斯基时空代替了标准量子理论的绝对外部时间。

图 5:块状闵可夫斯基宇宙,一堆空间切片。根据狭义相对论,切片并不是唯一的:时空可以以几种方式切片,这取决于参考系的选择(图基于巴罗)。

相比之下,广义相对论中的时间(更准确地说是时空)不是固定的,而是动态的。因此,将 GR 和 QM 统一起来很可能会导致一个新的、不同的时间概念。

图 6:左图显示了狭义相对论的固定(非动态)闵可夫斯基时空和相应的光锥。右边的一个说明了广义相对论的时空,它是动态的,并且由于物质和能量的存在而被扭曲。

由于统一量子力学和广义相对论将迫使我们在量子力学框架中表达引力(或者相反),我们面临着所谓的时间的问题。

图 7:量子力学和广义相对论的时间概念不同。把两种理论统一起来,就产生了所谓的“时间问题”。

因此,为了理解引力在原则上是如何被量子化的,我们必须首先研究经典的(图 2 中最上面一行的方框)和量子力学的(图 2 中最下面一行的方框)的数学和物理结构。

经典力学

1687 年,艾萨克·牛顿出版了他的哲学自然数学原理,更广为人知的名字是《原理》,可以说是科学史上最有影响力的书。在 1905-1915 年期间,阿尔伯特·爱因斯坦通过发表他的狭义和广义相对论论文推广了牛顿力学。

图 8:左边是《原理》第一版的 t* itle 页(* 来源 ) 。右边是爱因斯坦手稿的第一页,在那里他解释了他的广义相对论(来源)。

拉格朗日和哈密顿方法

正如我以前的一篇文章中详细解释的那样,牛顿力学有两种重新表述,使它在数学上更加优雅和强大:朗格方法和哈密顿方法。

拉格朗日函数 L 定义在所谓的组态空间 C 、和*C*中的每个点都与某个 N 粒子组态相关联。 C 中各点的动力学服从一组微分方程称为欧拉-拉格朗日方程,由拉格朗日函数 L:

方程 1:具有 N 个粒子的系统的朗格函数,欧拉-拉格朗日方程从该函数中导出。虚线变量是广义速度。

L 的自变量包含广义坐标及其关联的广义速度(详见本文)。

*

图 9:广义坐标的例子。左边可以选择椭球面上的广义坐标为( uv )。在右边,双摆的广义坐标可以是两个角度θ₁和θ₂ ( 来源)。*

拉格朗日量是系统的动能 K (由于其运动)和势能 V 之差:

方程 2:N 个粒子系统的拉格朗日量是动能和势能之差。

使用变分法的基本引理从 L 中导出欧拉-拉格朗日方程。它们由以下各项给出:

方程 3:欧拉-拉格朗日运动方程。

与拉格朗日情形一样,哈密尔顿方法基于多元函数,即哈密尔顿函数

方程 4:具有 N 个粒子的系统的哈密尔顿函数,从中获得哈密尔顿运动方程。自变量中分号后的 p 变量是与分号前的 q 变量相关联的共轭动量。

由此得到哈密顿的运动方程。函数 H 定义在相空间而不是配置空间。

*

图 10:都柏林扫帚桥上的著名牌匾,汉密尔顿在这里用他的小刀雕刻了乘法四元数规则(来源)。*

相空间可以定义为“一个系统所有可能状态的空间,每个可能状态对应一个唯一的点。”相空间中的一个点由系统所有粒子的位置和(共轭)动量决定。

*

图 11:在实空间和相空间运动的摆(来源)。*

你可以查看我的上一篇文章(如下)以获得更详细的解释。

* [## 循环虚时间和温度之间的神秘联系

一个显而易见的事故可能蕴含着物理学上的一个巨大谜团

towardsdatascience.com](/the-mysterious-connection-between-cyclic-imaginary-time-and-temperature-c8fb241628d9)

汉密尔顿运动方程:鸟瞰

而不是求解等式给出的 N 个耦合二阶微分方程。3 可以用 2N 个一阶方程代替。这可以显示如下。

首先,我们定义对应于每个广义坐标的共轭动量:

方程 5:与第 I 个广义坐标 qᵢ.相关的第 I 个共轭动量 pᵢ

然后,根据广义坐标、共轭动量和拉格朗日量显式写出哈密顿函数:

等式 6:哈密顿量的定义。

H 可以快速推导出2N哈密尔顿的运动方程,如下一小节所示(更多细节见戈尔茨坦、普尔和萨夫科)。

推导哈密顿的运动方程

我们从定义动作开始。它由以下对时间的积分给出:

方程 7:作用量 S,它是拉格朗日函数的时间积分。

图 12:与系统的给定运动相对应的时空路径是由粗线给出的路径。

S 用于场理论时,它还包含了对空间变量的积分。现在,为了简单起见,我们设置 N =1。改变 qp 我们得到:

等式 8:哈密顿量相对于 q 和 p 的变化。

其中广义速度的增量重新表示为:

方程 9:用增量广义坐标表示的广义速度增量。

对于独立变量 δqδp 和固定端点,动作的平稳性 δS =0 导致哈密顿运动方程:

方程式 10:汉密尔顿运动方程式。

例如,对于简谐运动的情况(如弹簧-质量系统),求解方程可以绘制相应的相图,如下所示。

图 13:这个动画在顶部显示了简谐振子的相空间运动(或相图),在底部显示了物理运动(图由加布使用他的 Python 脚本生成)。

典范变换

一个正则变换取一组坐标和共轭动量

方程式 11:原始广义坐标和共轭动量。

另一组坐标和共轭动量

方程 12:新坐标和共轭动量。

或者写得更简洁些:

方程式 13:接触转换。如果哈密顿的运动方程保持不变,这叫做正则变换。

用不同的(但相关的)变换后的哈密顿量 K 保持哈密顿运动方程的结构:

方程 14:新的坐标和共轭动量服从哈密顿量为 k 的哈密顿运动方程。

这是一个简单的任务派生一个算法生成规范的转换。

为了保持运动方程,我们需要下面的变化也为零(对于任何 δPδQ ):

方程 15:新哈密顿量相对于 Q 和 p 的变化。

然后,让我们首先做出下面的"猜测,并假设足以使上面的等式消失:

这相当于:

等式 16: 根据新的规范变量,新的、变换的拉格朗日量的定义

这里的“~”表示我们不仅仅是在新的坐标中重写拉格朗日函数

但是相反,我们正在 定义 一个新的、变换的拉格朗日量。

注意,尽管通过构造,在原始动作的初始和最终积分极限处的增量 δq 为零,但是增量δQ不一定在那里消失。现在,改变与 Eq 相关的新旧动作。16(分别对应于 HK

等式 17:新动作,通过积分等式 17 获得。16.

人们很容易发现,新的汉密尔顿运动方程成立的唯一方法是,如果满足下列条件:

方程 18:新的运动方程对哈密顿量 k 成立的必要条件。

但是如果我们现在假设运动方程服从于 K,我们就可以反演正则变换

方程 19:从新的广义坐标和动量回到原始的广义坐标和动量。

其中:

等式 20:逆规范变换。

重复上述过程,得到从( p,q )到( P,Q )的变换为规范的第二个条件,即:

等式 21:变换(Q,p) → (Q,P)为正则变换的必要额外条件。

等式和等式之间符号差异的来源。18 和情商。21 是等式中 F 的时间导数前的负号。16.这个函数 F 被称为生成函数。上面的论证表明,对于( p,q ) → ( P,Q )为规范变换,存在生成函数 F ( qQt )就足够了,使得方程。16、18、21 满足。

一个也可以证明每一个正则变换都可以从母函数导出。换句话说, F 的存在是变换( p,q ) → ( P,Q )规范的必要

考虑最后一个要点。在新拉格朗日函数中包含 F 的时间导数也改变了哈密顿量,其变为:

方程 22:在重新定义方程中的拉格朗日量后获得新的哈密顿量。16.

简单的例子

现在让我们简要分析一个例子,使事情不那么抽象。考虑一个简单的谐振子(SHO),例如弹簧质量系统。

图 14:动画显示了弹簧质量系统的简谐运动(来源)。

质量 m =1,以频率 ω 振荡的哈密顿量为:

方程 23:频率为ω的简谐振子的经典哈密顿量。

应用等式。将图 18 和 21 转换为下面的生成函数

方程 24:选择用于求解 SHO 方程的生成函数。

和反相等式。18,一个获得:

等式 25:等式中给出的两个逆变换。20 为 SHO 的情况。

图 15:简谐运动(来源)。

新的哈密顿量和相应的运动方程获得了极其简单的形式,即:

方程 26:新规范变量 Q 和 p 的哈密顿运动方程。

这些是微不足道的集成。将结果代入方程。25 我们得到:

方程 27:相空间中熟悉的 SHO 方程运动。

这些是相空间中常见的 SHO 方程运动。

其他生成函数

还有其他形式的 F 具有不同的变量依赖关系。更具体地,在当前情况下 F 取决于 qQ ,但是有三种其他可能的组合,即

等式 28:新旧相空间坐标的三种其他组合,给出了三种其他类型的生成函数的原点。

更详细的分析见戈尔茨坦、普尔和萨夫科。

汉密尔顿-雅可比方程(HJE)

在特殊情况下,有一个特殊的生成函数,通常用 S 表示,这样新的哈密顿量

等式 29:对于某个母函数 s,变换后的哈密顿量等于零。

情商。22 就变成了所谓的汉密尔顿-雅可比方程 (HJE):

方程式 30:汉密尔顿-雅可比方程式。

其中 Eq。21 被用来代替 H 内的动量。需要注意的是:

  • 由于新的哈密顿量 K =0,相应的哈密顿运动方程意味着 Q s 和 P s 是常数:

方程 31:当变换的哈密顿量 K=0 时,新的相空间变量的哈密顿运动方程。

  • 如前所述,生成函数可以有四种不同类型的变量依赖。这个特殊的生成函数 S 必须只依赖于 q s 和 P s(而不是依赖于 p s 和 Q s)。
  • HJE,一个具有 N + 1 个独立变量和 N + 1 个任意常数(由初始条件固定)的单个偏微分方程,解决与哈密顿运动方程相同的力学问题。
  • 人们可以很快地向表明生成函数就是动作。当我们比较经典方程和量子方程时,这个观察将是至关重要的。

图 16:数学家威廉·罗恩·汉密尔顿爵士(左)和卡尔古斯塔夫雅各布雅各布(右),HJE 就是以他们的名字命名的。

具体来说,考虑质量为 m 的粒子在与时间无关的势 V 中运动时的 HJE。它由下式给出:

等式 32:质量为 m 的牛顿粒子在与时间无关的势 v 中运动时的 HJE

让我们知道换挡和潜入量子力学。*

量子力学中重要概念的鸟瞰图

在短短的 1925-1926 年间,随着奥地利物理学家埃尔温·薛定谔和德国物理学家沃纳·海森堡的开创性论文的发表,量子力学的时代开始了,新理论取代了经典理论成为描述自然的更基本的框架。他们的工作基于其他几位物理学家之前的发现,包括普朗克、爱因斯坦、玻尔和德布罗意。

图 17:量子力学的两位主要创始人,物理学家沃纳·海森堡(左)和埃尔温·薛定谔(右)。

量子力学框架中的基本概念是量子物理状态的概念(参见弗拉德金)。数学上,它由量子态的希尔伯特空间中的向量空间中的射线来表示。如果满足以下条件,两个归一化矢量或纯态 ψ ₁和 ψ ₂属于同一光线:

方程 33:希尔伯特状态空间中属于同一光线的状态所服从的条件。

(例如,参见温伯格)。量子系统的希尔伯特空间中的单位球(例如布洛赫球)包含范数为 1 的所有纯态的集合。

*

图 18:量子力学中的布洛赫球代表一个二能级量子力学系统的所有纯态的空间(来源)。*

维数为 N 的希尔伯特空间中的一组状态

等式 34:N 维希尔伯特空间中的一组 N 个量子态。

跨越整个希尔伯特空间的称为基。一个态 ψ 在基础 ϕᵢ 上的扩展变成了:

等式 35:状态ψ扩展到一组完整的状态ϕᵢ.

在上图中,基只包含两个向量:

等式 36:图 18 中ψ的基本向量。

两态量子系统被称为量子位。纯量子位状态 ψ 是基态的叠加,可以写成如下:

等式 37:量子位是两个基本向量的线性组合。

量子比特的重要性在于,它是量子信息的基本单位(量子计算机将信息编码为量子比特)。

*

图 19:量子计算芯片阵列(来源)。*

所谓的经典和量子理论之间的对应原理,陈述了物理系统遵守量子力学定律,经典物理学是对大型物体有效的近似,或者更具体地说,是对大型量子数有效的近似。两个框架之间的转换过程被称为量化。

图 20:左边是诺贝尔奖获得者尼尔斯·玻尔的照片,他对量子理论包括对应原理做出了基础性贡献(来源)。右边是玻尔的手稿,包含与量子力学相关的笔记(来源)。

在正则量子化过程中,经典力学中的正则坐标对( qp )成为希尔伯特空间中的位置和动量算符:

等式 38:在正则量子化中,相空间中的经典坐标变成希尔伯特状态空间中的线性算子。

其对量子态线性作用, qp 之间的泊松括号成为相应算符之间的正则对易关系:

方程式 39:当一个经典理论被量子化时,泊松括号变成了交换子。

对于给定的算子 A ,存在一组状态 ψ ᵢ,使得 ᵢ与 ψ ᵢ.成比例这个集合的成员是所谓的 A 的本征态。换句话说如果 ψ ᵢ是 A 的本征态:

等式 40:态 ψ ᵢ是算符 a 的一个本征态,数 a 是其对应的本征值。

集合{ a ᵢ}的成员是算子 A 的特征值(更多细节参见例如 Sakurai )。本征态和本征值的两个重要性质如下:

  • 与物理可观测量相关的算符是厄米算符,因此只有实特征值
  • 希尔伯特空间中ψ的任何态都可以表示为本征态的线性组合。这意味着本征态集构成了希尔伯特空间的基础。

方程 41:任何任意态ψ都可以分解成某个给定算符的本征态的基。

一个特别重要的基础是位置算符的本征态的(无限维)基础。在这种情况下,我们得到:

等式 42:位置本征态的基础。态ψ展开成位置本征态的系数是量子波函数。

*

图 21:一维系统的典型波函数示例(基于 Griffiths )。箭头表示波函数是平方可积的,这意味着其绝对值平方的积分是有限的。*

量子态 ψ 到位置本征态的展开系数给出了量子波函数的值。在以 q 为中心的小间隔 dq 内的某个时间 t 发现粒子的概率由下式给出:

等式 43:在时间 t 在间隔 dq 内发现一个在 1D 运动的粒子的概率。

量子系统的哈密顿量是通过在方程中进行替换而获得的。经典哈密顿量中的 38。对于势 V 中的单个粒子,量子哈密顿量为:

等式 44:势能 v 中的单个粒子的哈密顿量。注意,势能被选择为仅取决于 q

人们可以使用任何一组基向量来表示希尔伯特空间中的算子。例如,考虑由等式给出的位置算符的本征态的基础。42.在此基础上如何表示动量算符?不难看出(详见 Fradkin ):

方程式 45:动量算符的坐标表示。

因此,在位置基础上工作,通过用以下厄米算子代替坐标和动量来执行量化:

方程式 46:为了量子化经典力学,我们进行这些替换。

经典哈密顿量中的经典能量被替换为

方程式 47:量子化后,经典能量变成能量算符。

你可以看到泡利或彭罗斯对上述等式的详细论证。

正如我们将在下一节看到的,经过这些替换,哈密顿量变成了薛定谔方程,量子力学的主要结果之一。

一个简单的例子 为了说明量子化的概念,让我们考虑一个简单的一维系统:一维盒子里的一个粒子,这个模型描述了一个粒子在被不可穿透的屏障包围的空间(在 1D 是一条线)中自由移动。

*

图 23:无限深势阱(来源)。*

该系统的本征态由下式给出:

方程 48:盒子模型中 1D 量子粒子的本征态。

粒子的经典运动如下图的方框 A 所示(跳动的红球)。剩下的方框(从 B 到 F)显示了不同波函数的实部和虚部的动力学。方框 B、C、D 中的波函数是对应于 n =1、2、3 的哈密顿量 H (或能量本征态)的本征态。

*

图 24:经典运动显示在方框 a 中,其他方框显示了遵守薛定谔方程的量子波函数的实部和虚部。只有 B、C、D 是能量本征态(来源)。*

薛定谔方程

薛定谔方程 (SE)最早出现在埃尔温·薛定谔【1926 年的著名论文之一中,它描述了量子波函数的动力学。它是一个线性 偏微分方程包含一个因子 i 乘以波函数的时间导数。

*

图 25:论文“量子化作为本征值问题”发表在期刊Annalen der Physik作者 埃尔温·薛定谔。本文包含薛定谔方程的首次出现(来源)。*

如上所述,可以通过应用等式中的替换来获得 SE。46 和 Eq。47 进入哈密顿量:

方程 49:描述量子力学系统波函数的薛定谔方程。

(注意我们选择自己的坐标为笛卡尔q=x=(xyz ))。这里,哈密顿算符具有以下形式:

等式 50:等式中的哈密顿算符 H。49.

图 26:安妮玛丽和埃尔温·薛定谔的墓碑。薛定谔方程内接(来源)。

有趣的是,薛定谔方程并不局限于非相对论量子力学(正如人们通常认为的那样)。你可以看这篇博文获得启发性的解释。例如,在量子场论中,坐标被场配置代替,波函数被波函数代替,薛定谔方程变成了标量场 ϕ ( x )的简单情况:****

方程 51:描述标量场的波函数ψ[ϕ,t 的薛定谔方程。

其中被积函数中的 K 是空间坐标的函数(更多详细信息见 Mukhanov 和 Winitzki ):

作为题外话,实验发现的唯一基本量子标量场是著名的希格斯场。

图 27:大型强子对撞机一部分的照片,2012 年在这里探测到希格斯玻色子(图片由D-VISIONS/shutterstock . com 提供)。

应该注意的是,SE 的这种普遍性仅限于纯态。对于一个混合态,它是一个纯态的统计系综,SE 由冯诺依曼方程代替。在希尔伯特空间中,混合态由一个密度算符 ρ 表示

等式 52:密度算符ρ,它是纯态的外积之和。

它是状态的外积之和,其中系数是系统处于纯态的概率 p₁,p₂,p₃,…,ψ₁,ψ₂,ψ₃等等。冯·诺依曼方程用密度算符写成如下:

方程 53:当描述的态是混合态而不是纯态时,代替 SE 的冯诺依曼方程。

*

图 28:光子在(2)中的混合状态的例子,其在穿过偏振器(3)后变成纯态(4) ( 源)。*

薛定谔方程的起源

在“费曼物理学讲座中,诺奖得主理查德·费曼做了如下声明:

我们是从哪里得到[薛定谔方程]的?哪儿也不去。不可能从你所知道的任何事情中推导出来。它源于薛定谔的想法,是他在努力寻求对现实世界的实验观察的理解时发明的。

—理查德·费曼

然而,人们可以建立一些方程应该遵守的性质。这至少会给我们一些关于它的形式的限制(更详细的描述见弥赛亚)。

线性和齐次性 让我们考虑某个函数 f 的一个线性微分方程。它具有一般形式:

方程 54:线性微分方程的一般形式,其中 L 是线性微分算子。

其中 L 为线性微分运算器。它具有一般形式:

方程式 55:线性微分算子的一般形式。

一个简单的例子是:

操作符 L 遵守以下性质:

方程式 56:线性微分算子所服从的性质。

因此,如果 fg 是由 Lf =0 给出的微分方程的解,那么它们的线性组合也是解:

方程 57:线性微分方程的解的叠加也是一个解。

线性偏微分方程(PDE)也是线性微分方程的一个例子。薛定谔方程(SE)是复波函数ψ的线性 PDE 的一个例子。其对应的微分运算符为:

方程式 58:与薛定谔方程相关的微分算子。

因此,如果下列波函数是 SE 的解

方程 59:SE 的一组解。

它们的叠加

方程 60:上述解的叠加也是 SE 的一个解。

也是。线性是使 T2 波包的形成成为可能的原因之一。下图显示了一个波包的例子。

图 29:代表量子力学的波包(源)。

方程的阶 微分方程的阶是方程中出现的最高求导阶。例如:

图 30:一个偏微分方程的例子。指出了方程在时间和空间上的顺序。由于常数-1,这个方程不是齐次的。

薛定谔方程在时间中的顺序必须是 1。换句话说,方程必须是时间变量的一阶。如果假设波函数ψ在某个初始时间的规范唯一地定义了它随后的时间演化(不需要它的初始时间导数的值),这必然发生。

经典极限 当普朗克常数 h → 0 时,该方程必然退化为某种形式上类似的经典运动方程。这是上述对应原则的结果。让我们更详细地探讨这一点。

遵循樱井,我们可以把任何波函数写成:

方程 61:用概率密度ρ=|ψ|和一般实函数 s 表示的波函数。

其中函数 S 是未指定的实函数。然后我们将 ψ 代入薛定谔方程,取经典极限 h → 0。更具体地说,我们作出以下近似,即所谓的短波极限:

等式 62:短波极限。

在目前的上下文中,“短波长”的含义是粒子的量子波长比作用在粒子上的电势变化的典型距离小得多。收集不包含普朗克 h 的项,我们得到:

方程 63:薛定谔方程的短波长极限(用 h → 0)就是(经典的)哈密顿-雅可比方程。

这就是汉密尔顿-雅可比方程,而 S ( xt )竟是经典动作。

用费曼路径积分推导薛定谔方程

如果我们包括一个额外的成分,即量子力学的路径积分公式,有一个简单的方法来获得薛定谔方程。这个公式首先由 n .维纳构思,后来由狄拉克扩展到量子力学。完整的量子力学处方是由费曼开发的。

图 22:狄拉克的论文,他确定了为什么汉密尔顿原理控制着量子力学的经典极限(来源)。然而,狄拉克没有提供精确的数学公式来计算路径上的和。在他 1948 年著名的论文中,费曼提供了这样一个处方。

在他著名的论文“非相对论量子力学的时空方法”中,费曼用两个假设总结了他的观点:

I .如果执行理想测量,以确定粒子是否具有位于时空区域中的路径,则结果为肯定的概率是复数贡献之和的绝对平方,来自该区域中的每个路径。

二。这些路径的贡献大小相等。但是他们贡献的阶段是经典动作[……];即拉格朗日函数沿路径的时间积分。

—费曼(1948)

因此,根据费曼的说法,系统进行转变的概率幅度

等式 64:从初始位置 I 到最终位置 f 的转换。

经由路径 j

等式 65:来自给定路径 j 的贡献与经典作用 s 的(复)指数成比例。

通过对连接初始点和最终点的所有可能路径求和来获得过渡幅度:

方程 66:传播子 K,由 I 和 f 之间所有可能路径的经典作用的指数和给出。

下图显示了对上述量子振幅有贡献的一些路径。

*

图 23:量子力学历史总和方法中对传播子 K 有贡献的几条路径(来源)。*

正如狄拉克首先指出的,使用量子力学的这个公式,通过设置 h → 0,可以推导出所有经典力学(通过最小作用原理和欧拉-拉格朗日方程)。

简单明了地表明费曼传播子服从薛定谔方程(这个论证遵循樱井)。

首先,如下图所示,将每条路径分成离散的无穷小段。

图 24:量子力学路径积分公式中的离散路径示例。

传播子 K 遵循以下合成性质:

等式 67:传播子 K. 的合成性质

两个无限接近的时空点的费曼传播子 K 的离散版本由以下表达式给出:

等式 68:两个相邻时空点的传播子 K 的离散形式,其中δt 被选择为无穷小。

插入等式。68 成情商。67,进行替换

然后将积分变量从 x 变为 ξ 得到:

等式 69:等式中的传播子积分。68 用 x ,t,和 ξ 来写。

由于δt无穷小,所以除了 ξ ≈0 的地方,指数是高度振荡的。因此,我们可以将被积函数展开成 ξ 的幂。我们还可以将两边展开为δt 的幂。展开后,等式的左手边。第 69 条写道:

等式 70:等式左侧的线性展开。65 为δt→0。

手手面变成:

等式 71:等式右边的展开式。69 为δt→0 和T21【ξ≈0。

注意,上面的展开式在 中是二次的,因为包含线性项的积分在中消失了。最后,在收集了δt中的线性项之后,对 ξ 执行平凡的高斯积分,得到费曼传播子 K 的以下方程:********

方程 72:费曼传播子 K 服从薛定谔方程。

更多中间步骤见樱井。现在,我们可以将薛定谔波函数表达如下:

方程 73:薛定谔的波函数写成核为 k 的积分方程。

其中 K ,在这种情况下,是薛定谔波动力学中的传播子,它不一定与方程中的 K 是同一个物体。72.但如果两个Ks都是同一个对象,我们很快就能看出中的ψEq 73 的确满足薛定谔方程,因为它应该:**

方程 74:方程中的波函数。73 满足薛定谔方程,前提是费曼传播子与薛定谔传播子相同。

图 25:费曼图的一个例子,来自费曼自己的一篇论文(来源)。

测不准原理与交换子

量子力学的另一个基本方程(除了薛定谔方程)是位置-动量转换器

等式 75:众所周知的正则对易关系。

这与位置-动量测不准原理有关。后者是一种数学不等式,它限制了已知位置和动量的精度。例如,位置-动量不确定性原理是:

方程式 76:位置-动量不确定原理。

其中:

方程 77:算子 x 和 p 的色散。

是算子 xp 的离差。

量子力学中的复数

复数在经典物理学中已经被使用(见杨和巴伯),但仅仅是作为一种方便的工具。在计算结束时,总是取结果的实部或虚部。正如杨 T11 所观察到的,“物理学在概念上是以实数为基础的。”

图 26:阻抗衡量施加电压时电路对电流的阻抗。这是在经典物理学中使用复数的一个例子(来源)。

然而,在量子力学中,复数是基础。许多量子力学定律本质上是复杂的。我现在将描述几个重要的例子。

动量和能量

考虑一个沿 x 轴移动的经典电磁平面波(见本节所基于的堆栈)。

图 27:沿 x 方向传播的电磁波,其中电场(蓝色)与磁场(红色)同相振荡。注意两者是正交的(来源)。

振荡频率为 ω 的电场(分量)读数为:

等式 78:频率为ω的平面波沿 x 轴移动时的电场。

其中ω > 0。按照惯例,第一项和第二项分别被视为“正频率”和“负频率”部分。

与经典电磁学相反,在量子力学中,频率的符号必须是正的(没有“负频率”分量)。让我们明白为什么。在奇迹年的一篇论文中,阿尔伯特·爱因斯坦解释了光电效应,并展示了光光子的能量和频率之间的关系

方程 79:量子力学中能量和频率的关系。

图 28:在这个实验装置中,灯发出的光撞击电极,产生电子发射。通过调节电压来控制电流。在某个“停止电压”时,电流停止。“停止电压”不取决于入射光的强度,而仅取决于其频率(光源)。

这个关系后来被证明不仅仅是光子所遵循的。例如,考虑与一个自由的大质量粒子,比如一个电子相关的行波。因为自由电子的能量总是正的,

方程式 80:爱因斯坦质能等效公式。

它的频率也是如此。下图是质能等效关系的一个例子。光子(在这种情况下,是不可见的伽马射线)的能量根据等式转换成质量。80.

图 29:配对产生事件的气泡室图像。一个不可见的伽马射线光子产生一个电子-正电子对,由螺旋轨迹显示(由磁场引起)。光子的部分能量最终取代了一个移动到图片左下方的原子电子。第二个电子-正电子对,在图像的底部,使用伽马射线的所有能量(源)。

此外, Louis De Broglie 在他的 1924 年论文中提出大质量粒子也表现为波,并展示了波矢和粒子动量之间的以下关系:

方程 90:波矢和大质量粒子动量之间的德布罗意关系。

用德布罗意自己的话说:

“[我 1924 年的论文]的基本思想是这样的:在爱因斯坦在光波中引入光子之后,人们知道光包含粒子,这些粒子是并入波中的能量集中,这表明所有粒子,像电子一样,必须通过它被并入的波来传输……我的基本思想是将爱因斯坦 1905 年在光和光子的情况下发现的波和粒子的共存扩展到所有粒子。"

—路易·德布罗意

这种现象被称为波粒二象性。正如引言中简要提到的,这种二元性通常对于宏观物体来说是不可探测的,因为它们的德布罗意波长极短。

图 30:这个动画展示了使用电子的双缝实验中波粒二象性的表现。久而久之,穿过狭缝的电子撞击探测器并积聚,使得波干涉图案可见(来源)。

Davis son–Germer 实验,在该实验中,当电子被晶体表面散射时观察到衍射图案,证实了 Broglie 的波粒二象性假说。

图 31:戴维森-格默实验的实验装置(来源)。

因此,描述单个自由粒子的量子波将具有以下形式:

方程 78:单个物质粒子的波函数。因为只有正频率部分,所以函数必须是复数。

图 32:单个自由粒子波函数的实部和虚部(来源)。

虽然指数的频率部分的符号不变,但是动量部分的 x 依赖性不变。它决定运动的方向。第二指数因子中的加号对应于正 x 轴上传播的波。

方程 79:向右传播的平面波的动量指数因子。

相应地,负号与沿负 x 轴传播的波相关联。

方程 80:向左传播的平面波的动量指数因子。

这些关于运动方向的观察和 ω 为正的事实暗示着自由粒子的波函数ψ在本质上复数。

注意波函数方程。78 在物理上不可实现,因为它不是平方可积的。波函数必须是平方可积的,因为在宇宙中任何地方找到粒子的总概率必须是 1:

等式 81:在宇宙中任何地方找到粒子的总概率一定是 1。

波包,是方程形式的波函数的叠加。78,是平方可积的。

图 33:该图显示了不同定位水平的波包(源)。

薛定谔方程和概率守恒

薛定谔方程以更简洁的形式写道:

方程 81:薛定谔方程,没有明确写下哈密顿量。

正如在 Barbour 、量子力学的先驱之一 Wolfgang Pauli 、注意到薛定谔方程复杂本质的两个方面。首先是组件数量翻倍。第二个是存在时间导数乘以虚数单位 i. 这些性质允许人们仅从ψ(不使用它的时间导数)建立正量ψψ*,它测量发现粒子的概率(在给定的时间和位置)并且在时间上守恒。

图 34:奥地利出生的瑞士和美国理论物理学家沃尔夫冈·泡利,量子力学之父之一(来源)。

为了证明虚数单位 i 的出现对于概率守恒的必要性,出于教学原因,我们可以用某个原则上未知的复数z来代替 i ,然后 SE 将读出:

方程 82:一个有意修改的薛定谔方程,用一个未指定的复数 z 代替左边的 I。

现在,对时间求导并使用表达式

方程 83:重新排列薛定谔方程(左)和它的复共轭(右)中的项。

我们获得:

等式 84:在宇宙中任何地方找到粒子的总概率的时间导数。

因为这必须为零,所以必须遵守以下条件:

方程 84:概率守恒意味着薛定谔方程中的前因子一定是虚数单位 I。

换句话说,如果前因子是除了 i 之外的任何东西,概率守恒就会被违反。

图 35:复杂平面内的单位圆。横轴(Re)包含实数,纵轴(Im)包含纯虚数。还显示了假想单元 I(基于此源的图)。

交换子是反埃尔米特的

如前所述,在量子力学中,位置和动量由厄米算子或自伴算子表示

等式 85:位置和动量算符是厄米的。

算子的性质之一是它们乘积的厄米共轭交换它们的次序

方程 86:取两个算子乘积的厄米共轭,交换它们的顺序。

这意味着:

方程式 87:位置-动量换位子是反厄米的。

因此,换位子方程的左边是反埃尔米特的。这意味着虚单元 i 必须出现在右侧。

对于自旋也发生了类似的争论。自旋可以被定义为一种“内禀角动量”,它有三个分量:

方程式 88:自旋算符的三个组成部分。

自旋算符有两个特征值

方程 89:自旋算符的两个特征值。

图 36:自旋粒子的示意图(来源)。

参见我以前的一篇文章(下面的链接)了解更多关于旋转的细节。

[## 深度学习的可解释性:来自物理学的提示

从物理学角度看深层神经网络

towardsdatascience.com](/deep-learning-explainability-hints-from-physics-2f316dc07727)

自旋算符服从下面的换向关系

等式 90:自旋换向关系。索引可以是 x、y 或 z。

其中对象 ε 为 Levi-Civita 符号,由下图所示数组表示。

图 37:代表 Levi-Civita 符号 ( 来源)的 3 x 3 x 3 数组。

出于与上面讨论的位置和动量情况相同的原因,虚部必须出现在右手侧,以确保换向器的气密性。

斯图克伯格的真实量子力学

在 20 世纪 60 年代发表的三篇论文中(见 Mehra ),瑞士数学家和物理学家 E. Stueckelberg 和几个同事成功地用真实的希尔伯特空间建立了量子力学的数学框架。然而,为了获得正确的量子力学测不准关系,额外的超选择规则是必要的。根据这个超选择规则,所有的可观测量都应该用一个符合以下条件的算子 J 来交换:

方程 91:与 Stueckelberg 的实希尔伯特空间量子力学框架中的超选择规则相关的算符 J 的性质。

正如 Jauch 所指出的,这有两层含义:

  • 运算符 J 总是可以表示为

  • 这个补充了超选择规则的实希尔伯特空间和复希尔伯特空间之间是完全等价的。

图 38:1934 年的物理学家 e .斯特克尔伯格。他和他的合作者一起展示了在真实的希尔伯特空间上建立量子力学框架是可能的,只要包含一个额外的(超级选择)算符规则(来源)。

现在让我们讨论“量子引力谜题”的第二块,即经典引力。

爱因斯坦引力理论的鸟瞰图

我将首先定义经典重力理论中的几个概念,这些概念对于理解量子化重力的问题是必要的。

逆变和协变向量

一个流形是一个(拓扑)空间,类似于欧几里德(或平坦)空间在其每个点附近(你可以在这里找到更多细节)。下图(左侧)说明了流形的局部平坦特性。

图 39:在左边,显示了一个二维球体,这是一个简单的流形的例子(源)。流形是拓扑空间,类似于欧几里德或平坦空间(显示在图的右手边),靠近它的每个点(源)。

在讨论广义相对论之前,我们先来快速看一下爱因斯坦的狭义相对论 (SR)的数学结构。在随机共振中,事件,可以定义为“与时空中的点相关的瞬时物理情况或事件”。如果两个事件 A 和 B 发生在非常接近的时空点

方程式 92:时空中两个无限接近的点。

它们之间的时空间隔

方程式 93:平坦时空中不变区间的定义。

是一个不变量当从不同的参考系测量时(常数 c 是光速)。为了便于说明,请考虑下面的动画。

图 40:显示事件顺序对参考帧的依赖性的动画。详细解释见正文(来源)。

动画显示了三个事件,即从不同参考帧测量的 A、B 和 C。对于一个静止的参考系 S 中的观察者(对应动画中的 v =0),可以任意选择,事件是同时发生的(三个都发生在 t =0)。

图 41:在这个参考系中,事件是同时发生的。

对于不同的移动帧,事件的顺序会发生变化:

  • 在相对于 S 移动且 v = 0.3 c 的参考系中的观察者以 C → B → A 的顺序看到事件

图 42:在第二个参考系中,在 v = 0.3 c,相对于 S 移动,事件以 C → B → A 的顺序发生

  • 相对于 Sv = -0.5 c 移动的参考系中的另一个观察者以 A → B → C 的顺序观察事件

图 43:在第三个参考坐标系中,相对于 Sv = -0.5 c 处移动,事件以 A → B → C 的顺序发生

考虑一个 4 维时空流形和其上的一条曲线 γ 。由于坐标“只是手段,并不存在先验的性质”(见彭罗斯),我们可以选择任何坐标系来描述曲线。

图 44:圆柱体是 2D 流形的另一个简单例子。曲线γ中的点 x 由λ ( 源)参数化。

然后,我们可以使用任意实参数 λ 写出 γ 的方程:

方程 92:定义曲线 γ的四个方程。

为了具体起见,考虑一个 3D 平面空间中的例子(为了简单起见,而不是时空)。沿着圆柱表面的螺旋曲线由以下参数方程给出:

方程 93:沿圆柱表面盘旋的曲线的参数方程。圆柱体的轴沿着 x 轴。

图 45:等式给出的曲线。93.

在流形的每个点 P 处,都有一个包含与曲线 γ相切的向量的切平面。

图 46:与一个流形相切的平面和 P 处的切向量(基于这个源)。

一种方法是将这些切向量表示为:

方程 94:与曲线γ相切的向量,由λ参数化。

为了具体起见,让我们考虑 2D 平面中圆形路径的简单例子。(参数)方程是:

方程 95:描述 2D 平面中的圆的参数方程。

因此,切线向量的分量为:

等式 96:在 2D 平面中与圆相切的向量分量。

在任意坐标变换之后

方程式 97:任意坐标变换。

例如,举例来说,

方程式 98:从笛卡尔坐标到球坐标的转换。

切线向量的新分量可以通过直接应用链规则获得:

方程 99:矢量分量在任意坐标变换后如何变化。

这样变换的物体称为逆变矢量。请注意,我们使用所谓的求和约定(或爱因斯坦符号),根据该约定,每当重复一个索引时,对该索引的所有可能值的求和是隐式的。例如:

等式 100:求和约定(或爱因斯坦符号)的一个例子。

广义相对论中另一个重要的数学对象是协变矢量,它转化为(参见泊松):

方程 101:任意坐标变换后,对偶矢量分量如何变化。

由于对偶向量不如向量那样广为人知,所以让我们考虑一个例子,并计算一个协变向量在球坐标中的分量,给定其在笛卡尔坐标中的分量:

方程式 102:笛卡尔坐标中共变向量的分量。

为了找到球坐标中的分量,我们使用等式。101,具有以下变换方程:

方程式 103:球坐标和笛卡尔坐标之间的坐标转换。

其中使用了以下名称:

A 在新坐标系中的第一个分量是:

等式 104:协变向量 a 的等式 75。

快速计算得出:

等式 105:协变向量的第一个分量 A

类似地得到 A 的另外两个分量。

图 47:动画显示了非正交基下对偶矢量和矢量的分量是如何变化的(源)。

注意,我们用上标来表示反变向量的分量,而用下标来表示协变向量的分量。

张量

我现在将介绍张量的概念(参见狄拉克)。向量是一阶张量。让我们考虑更高阶的张量。我们首先建立数量

方程 106:二阶逆变张量的简单例子。

这是一种特殊的张量。因为它有两个上指标,它被称为第二秩 逆变张量。

图 48:柯西应力张量是二阶张量的一个例子。它描述了材料对力的反应,有两个方向与力的法向和剪切分量相关联(源)。

将几个以与 T 相同方式构造的张量相加,可以得到一个一般张量:

方程 107:一般二阶逆变张量的一个例子。

在坐标变化下, T 变换为:

方程 108:坐标变换后二阶逆变张量如何变化。

如果我们有 2 个较低(而不是较高)的指数,张量被称为是一个二阶协变张量。张量也可以有混合指数。一个例子是:

方程 109:坐标变换后混合张量的分量如何变化。

一个一般的( nm)-张量有 n 个上索引和 m 个下索引和变换,就像逆变向量和协变向量的乘积。方程中混合张量的变换。109 就是一个例子。另一个例子如下

方程式 110:坐标变换后(2,2)-张量如何变化。

其中( nm )=(2,2)。

张量场 如果与流形上的每个点相关联的都有一个张量,则定义一个 张量场 。下图显示了一个简单的例子,欧几里得空间中球体上的向量场。

图 49:一个球上的张量场(更确切地说是一个向量场)。

协变导数

流形点 P 处的张量未在流形上定义。相反,它被限定在与歧管P 处相切的平面中。因此,在弯曲时空中,不同点的张量不能被张量相减,因为它们“生活”在不同的切面中(更多细节见泊松)。****

图 50:该图显示了歧管不同点的不同切面(来源)。

换句话说,减去不同点上的张量不会产生张量。因此,分化的概念必须重新定义。新型导数称为协变导数,它涉及到平行传送的操作。

图 51:左边是一个向量在球面上绕一个闭环(A → N → B → A)的平行输运(来源)。在右边,一个切线向量沿着球面上的一条曲线平行移动(来源)。

现在让我们把这些陈述用数学形式表达出来。首先,假设我们有一个定义在流形上一条曲线周围的向量场 V ,它由 λ 参数化。设 u 为与曲线相切的向量(如图 41)。现在,考虑曲线上坐标为 xx + dx 的两点。如上所述,以下目的

等式 111:一个矢量在 x 的分量与另一个矢量在x+dx的分量之差。

图 55:流形上两个不同点上的张量场 V。分量的简单减法不是张量对象(基于来源)。

不是张量,因为经过坐标变换后,它变成:

方程 112:因为第二项的存在,偏导数不是张量。

为了获得适当的张量导数,首先需要将在点 x 定义的向量 V 并行传输到点 x + dx ,然后将其从在 x + dx 定义的向量 V 中减去。这个减法的结果必须在 Vdx 上是线性的,这导致了被称为连接γ的(非张量)对象的定义

等式 113:向量 V 的分量在并行传输后的变化以及连接γ的隐式定义。****

现在将成为张量对象的协变导数包括沿曲线相对于参数 λ 的两个导数之和:

等式 114:协变导数包括等式 114 给出的变量之和。112 和 Eq。113.

其中协变导数定义为:

方程 115:协变导数的定义,一个张量对象。

撇开
不谈,这个讨论可以用更抽象的表述来完成(例如,参见【史高斯】)。相反,我选择使用一种更简单的方法(例如在狄拉克、泊松或朗道和利夫希兹中),其中张量以“老式方式定义。

爱因斯坦场方程

在广义相对论 (GR)中,时空的几何就是引力。具有平坦闵可夫斯基度规线元素的狭义相对论的刚性时空变成了动态的。这是因为物质和能量扭曲了时空的几何。广义度量元素可以写成如下形式:

方程 116:线元素的一般表达式。

其中 g 为度规张量。在平坦闵可夫斯基空间的情况下,度量张量 g 表示:

方程 117:对应于平坦闵可夫斯基线元素的度量张量。

为了看得更清楚,我们写出等式。116 显式使用等式中给出的 g 。117:

等式 118:对于等式给出的 g 。117,情商。116 成为闵可夫斯基线元素。

爱因斯坦场方程 (EFE)建立了时空几何与其中物质和能量分布之间的联系,用张量来表示。张量在广义相对论中很重要,有几个原因,例如在彭罗斯中讨论过。主要的一个事实是,EFE 方程服从一般协方差的原理。根据这个原理,物理定律的形式(在目前的情况下,广义相对论的方程)在任意坐标变换下应该是不变的。这是有意义的,因为如上所述,坐标只是人工标注。因此它们不是基本的,在这样的重新标记下,物理定律应该是不变的。

图 56:爱因斯坦广义相对论手稿中的一页(来源)。

张量是编写广义相对论方程的关键,因为张量方程具有这样的性质,如果它在一个坐标系中成立,它在所有坐标系中都成立。引用爱因斯坦在他的一篇广义相对论论文中的话:

“一般协变理论的基本思想是这样的:—关于任何坐标系,让某些东西(“张量”)由若干被称为张量分量的坐标函数来定义。现在有了某些规则,根据这些规则,当原来的坐标系已知时,以及当连接两个坐标系的变换已知时,可以在新的坐标系中计算出这些分量。此后称为张量的事物还具有这样的性质,即它们的分量的变换方程是线性的和齐次的;因此,如果新系统中的所有元件在原系统中都为零,那么它们就会消失。因此,自然法则可以通过将张量的所有分量都等于零来表述,因此它是一个一般的协变方程;因此,当我们寻求张量的形成定律时,我们也达到了建立一般协变定律的方法。”

爱因斯坦(1916 年)

爱因斯坦场方程,包括宇宙常数,读作:

方程 119:爱因斯坦场方程。

张量 G 被称为爱因斯坦张量,它与瑞西曲率张量 R 有关。后者测量一个公制球(由一个球包围的空间)的体积在一个流形中增长的速率(见此链接和下面的讨论)。换句话说, G 代表了时空的几何性质(比如它的曲率)。左手边的λ就是所谓的宇宙常数、真空能量或者空间的能量密度。右手边是能量动量张量与物质(或能量)的分布有关,它使时空变形。

GR 的关系为:

方程 120:用里奇曲率张量T42 曲率张量和标量曲率 R 表示的爱因斯坦张量

其中 R 是标量曲率,它是黎曼流形中一个小(测地线)球的体积与欧几里德空间中一个球的体积相差多少的度量(参见这个链接)。

图 57:地球和太阳质量引起的时空变形的二维类比(图片由 vchal 提供)。

右手边是能量动量张量与物质(或能量)的分布有关,它使时空变形。

图 58:应力能量张量 T 的分量。

下面是张量在广义相对论中重要性的一个简单例子。考虑能量动量 T =0 的空间区域,为简单起见,设λ= 0。很容易看出 EFE 简化为:

情商。121:真空中的爱因斯坦场方程。

在任何新的参考系中,Ricci 张量保持等于零,因为:

情商。122:真空中爱因斯坦场方程的协方差性质。

为了更好地理解 R 的几何意义,理解局部平坦的概念是有用的,根据这一概念,对于时空中的任意点 P ,总是可以建立一个坐标系,使得:

  • 公制 gP 变为闵可夫斯基

等式 123:时空时间点 P 处局部平坦的第一个条件是度量变成局部闵可夫斯基。

  • P 处连接也为零

等式 124:点 P 处局部平坦的第二个条件,连接消失。

现在,使用费米正常坐标,度规张量 g 的空间分量可以写成如下:

等式 125:费米正常坐标的度量张量 g 的空间分量的泰勒展开。

对于 g 的其他组件也存在类似的表达式。从这个表达式中,很明显T5【R测量了某种偏离时空平直度的情况。****

图 59:其他天体的引力导致行星的椭圆轨道旋转或进动。爱因斯坦引力理论最初的主要成功之一是解释了水星的异常进动(来源)。

引力波 广义相对论的非线性意味着两个解的线性组合不一定是解:

方程 125:EFE 的非线性意味着两个解的线性组合可能不是一个解。

图 60:动画展示了两颗中子星相互绕转时产生的引力波的 2D 表示(来源)。

这意味着时空也与自身相互作用,并可能成为重力波的来源。这是爱因斯坦在 1916 年预测的(见下图)。为了得到引力波方程,我们假设引力场是弱的,这意味着 g 近似为常数。根据在狄拉克中的推导,我们得到下面的方程,它的解将包括行波(以光速):

方程 126:引力场较弱时引力波的运动方程,这意味着 g 近似为常数。

图 61:左边是 1916 年爱因斯坦预言引力波存在的论文(来源)。在右边,来自 LIGO 的文章描述了他们对来自一个双星黑洞合并的引力波的观察。

2016 年, LIGO 和处女座国际科学合作组织宣布他们已经直接观测到引力波。

图 62:LIGO 天文台测量引力波的结果。LIGO 在利文斯顿和汉福德都有探测器。这些图表将结果与广义相对论的预测进行了比较。

爱因斯坦-希尔伯特作用 广义相对论的爱因斯坦-希尔伯特作用(包括吉本斯-霍金-约克边界项)由下式给出:

方程 127:广义相对论的爱因斯坦-希尔伯特效应。

EFE 通过要求动作相对于度量 g 的变化是稳定的来获得。第二项是流形(对于紧致流形)的边界∂ M 上的积分,其被包括以允许在∂ M 上为零但其法向导数不为零的gg的变化。这个积分中的符号 Kh 代表外曲率和诱导度量的迹,这两个都将很快解释。

经典宇宙学

现代宇宙学的诞生发生在爱因斯坦发表了他的论文“广义相对论的宇宙学考虑”,该论文考虑了整个宇宙的物理学。在这篇文章中,爱因斯坦假设宇宙有一个封闭的空间几何,更具体地说是一个三维球体,在空间中是有限的,但却是无界的。然而,为了找到他的场方程的解,他不得不引入一个新项,EFE 中的λ项。

图 63:爱因斯坦的论文《关于广义相对论的宇宙学思考》催生了现代宇宙学(来源)。

膨胀的宇宙 如今,人们普遍认为我们的宇宙正在膨胀。下图显示了扩展的各个阶段(左边的)。下图显示了膨胀宇宙的膨胀气球类比,我们可以将星系的运动与其表面的点的运动进行比较。必须指出的是,“气球宇宙”是一个二维的模型,它并不存在于一个“宇宙之外”的三维空间中换句话说,气球不是从一个中心点扩展到它以外的区域。

图 64:左边显示了宇宙膨胀的阶段(来源)。右边是“气球类比”,根据这一类比,星系的行为类似于正在膨胀的气球表面上的点。

Friedmann-lematre-Robertson-Walker 度量 如果考虑足够大的区域(在星系团的水平上),宇宙的几何形状(度量的空间部分)基本上是均匀的(在所有位置都是相同的)和各向同性的(在所有方向上都是相同的)。这些假设构成了所谓的宇宙学原理。

图 65:各向同性和同质性的概念(来源)。

基于这些性质,人们可以选择常曲率空间。相应的度量是所谓的Friedmann-lema tre-Robertson-Walker或 FLRW 度量,它是 EFE 的精确解。它的空间部分可以用不同的方式书写,其中之一是

等式 128:写出 FRW 度量空间部分的一种可能方式。

其中 a ( t )是宇宙标度因子,是一种衡量宇宙大小的方式。在这个表达式中,参数 k 定义了 FLRW 宇宙的形状,并取决于宇宙的曲率类型,如下图所示。 k 的三个可能值是+1、0 或 1,分别对应正、零和负曲率。

图 66:FRW 宇宙可以是封闭的(正弯曲),像马鞍一样开放的(负弯曲)或者平坦的(零曲率)(来源)。

人们可以引入球坐标

方程式 127:球坐标。

并定义无量纲径向坐标:

方程 128:用通常的 r 表示的量纲参数 r,r 的上面有~。

其中~表示通常的径向变量。现在包括时间部分的 FLRW 度量变为:

等式 129:另一种写 FLRW 线元素的方法。

图 67:类空的均匀和各向同性超曲面的选择,它们是具有恒定空间曲率的三个空间,形成 FLRW 时空(来源)。

为了简单起见,让我们选择一个球形的、封闭的宇宙,其中 k =1。然后可以重写线元素的空间部分,定义:

该指标变为:

等式 130:正弯曲宇宙的 FLRW 度量的空间部分。

上述等式中的变量具有以下范围:

等式 131:等式中变量的范围。130.

完整的行元素,包括时间部分,如下所示:

等式 132:包括时间部分的完整 FLRW 线元素。

正如我们之前已经提到的,时空中的点和时空坐标不是一回事。时空坐标是一个分配给实际点标签,因此他们的选择不会改变物理学。方程给出的线元中的空间坐标 rθϕ 。132 据说是共动。见下图。随着宇宙比例因子的增加,点之间的(适当的)距离增加,但是网格坐标保持不变(为了简单起见,角坐标被省略)。

图 68:一片膨胀宇宙的示意图。宇宙尺度 facto,在图中用 R(t)表示,对应于文中的函数 a(t),l(t)是适当的距离,R 是移动距离(源)。

基于各向同性和均匀性的假设,爱因斯坦场方程右边的能量动量张量 T 变得简单多了(变成了“完美流体”)。更具体地说,物质可以仅用其密度和压力来描述,密度和压力分别由下式给出:

换句话说,两者都只取决于宇宙比例因子 a ( t ),它只是时间的函数。EFE 于是变成了弗里德曼方程:

方程 133:当能量-动量张量是各向同性和均匀的时,弗里德曼方程是 EFE。

图 69:俄罗斯物理学家亚历山大·弗里德曼(来源)。在右边,弗里德曼陨石坑,以他的名字命名,位于月球的远端(来源)。

进行以下替换:

等式 134:通过这些替换,λ可以被解释为来源于暗能量,一种具有负压的能量形式。

人们可以将λ解释为来自一种叫做暗能量的能量密度形式,它具有负压:

方程式 135:负压下的真空能量。

正则量子引力

规范量子引力 (CQG)是发展广义相对论的哈密顿公式的量子版本的一个总体努力。在这个过程中,可以使用不同的规范变量。在这里,我将讨论最直接的策略,即所谓的几何动力学或广义相对论的初始值公式(环圈量子引力是一个流行的选择)。它可以被定义为“试图仅仅用几何学的术语来描述时空和相关的现象[……]并将广义相对论重新表述为一个三度规的构型空间”。

但是在全面考虑地质动力学之前,我将考虑一个非常简单的例子。

量子宇宙起源的简单模型

考虑一个封闭的宇宙( k =1),“从无到有”,只有恒定的真空能量密度。通过选择这些参数,弗里德曼方程描述了所谓的膨胀阶段。在这个阶段,宇宙以指数速度膨胀。

第一个弗里德曼方程变成(设置 c =1):

方程 136:仅含真空能(设 c=1)的封闭宇宙(k=1)的第一个弗里德曼方程。

其中“vac”代表真空。注意等式。136 具有与能量为 E =0 的哈密顿量相同的形式结构。因此,我们可以把方程的第一项联系起来。136 与“种”动能相对应的“速度”(这是宇宙尺度因子的膨胀率):

第二项是“势能”(在括号内)。遵循规范的量化协议,我们定义:

方程 137:与动量共轭 a 相关的量子算符。

(其中简化普朗克常数 t 被设置为 1)。要写 Eq。根据动量算符,我们必须用膨胀率来表示共轭动量。这可以用最小作用原理来完成。在这种情况下,该动作可以表示为等于:

方程式 138:封闭的空 FLRW 宇宙的作用。

其中:

等式 139:常数因子 a₀.的定义

S 我们得到:

方程 140:与 a 共轭的动量。

情商。136 然后变成:

方程式 141:方程式。136 用对 a 的共轭动量表示。

我们现在准备好利用方程来量子化这个简单情况下的爱因斯坦方程。137.我们得到了著名的惠勒-德威特方程 (WdW)的一个简单版本:

方程 142:封闭的空 FRW 宇宙的惠勒-德威特方程。

其解是“宇宙波函数”。在下一节中,我们将看到这个过程如何从线元素仅依赖于比例因子 a ( t )的简单情况扩展到广义时空。这种简化的情况被称为微超空间近似。

但在此之前,我先说几句重要的话(它们大多是基于穆哈诺夫和 Winitzki 和阿特卡兹)。

图 70:WdW 方程的一个可能的初始条件。参见下面的讨论(来源)。

时空的概念消失 在目前的语境中,量子化意味着时空本身的概念变得模糊不清。为了更好地理解这一点,考虑以下类比:

方程 143:宇宙尺度因子及其共轭动量的正则对易关系和不确定关系。

连同情商。140 它们意味着比例因子及其变化率

方程式 144:宇宙比例因子及其随时间的变化率。

不能同时知道。但是为了完全确定在任何时间 t 的 FLRW 度量,并因此刻画时空的结构,我们需要这两个量。因此,时空的经典概念在量子力学中消失了。

图 71:模糊的量子时空有时被称为量子泡沫。参见下面的讨论(来源)。

初始条件 为了找到 WdW 方程的解,我们需要初始或边界条件。正如我以前的一篇文章中的所讨论的,两个最著名的例子是量子隧道和 Hartle-Hawking(或无边界提议)边界条件。这些条件的详细讨论超出了本文的范围,但可以在章节中找到。下图说明了这两种情况。

图 72:对应于 Hartle-Hawking (HH)和 tunneling (T)边界条件的解(来源来源中的图的修改版本)。

数学上,情商。142、描述了一个“粒子”用“位置”一个 ( t )隧穿一个势垒。下图是亚原子粒子的量子隧穿。

图 73:部分隧穿势垒的电子波包(来源)。

最初在“原点” a =0 处的“粒子”的隧穿对应于从零尺寸宇宙(“无”)的演化,该宇宙自发地非奇异地出现(尺寸为 a = a ₀ > 0)。这个事件发生的概率可以用基本的量子力学来计算。结果是:

方程 145:通过隧道量子宇宙发生的概率。

这个表达式可以使用退相干历史的概念来解释,其中概率被分配给宇宙的可能历史(更多细节见 Atkatz 和其中的参考文献)。这种方法将量子力学规则应用于单一系统,与标准解释形成对比,根据标准解释,宇宙被分为一个系统集合,即“经典观测系统”和“量子观测系统”,当进行测量时,这两个子系统相互作用,导致量子波函数的所谓坍缩。

图 74:布鲁塞尔索尔维会议(1927)因其关于量子力学正确解释的讨论而闻名(来源)。

在一般情况下,度规的空间部分,或三维几何,并不具有 FLRW 型宇宙的所有对称性。因此,宇宙波函数ψ的论点不是一个单一的函数——在 FRW 的例子中是一个一个 ( t ),而是一组描述三维几何的函数。更具体地说,ψ变成了一个泛函,而不是定义在超空间上的一个函数(一个泛函取一个整函数并返回一个数),超空间是广义相对论的构型空间,所有三维几何的空间。在最小超空间近似的最大对称情况下,我们只需要考虑超空间的一个子空间。

图 75:经典超空间,其中三个几何图形 h ⁽ ⁾和 h ⁽ ⁾(两个超曲面)之间的轨迹是两个时空。

Eq 的一个结果。143 是在量子宇宙学中,超空间轨迹(如上图所示)变得“泡沫化”,通常被称为“时空或量子泡沫”。

参数化粒子动力学:热身

我们可以通过首先检查一个非相对论性粒子的动力学的参数化来使(规范地)量子化引力的问题变得更容易理解(这一节紧跟在 D'Eath 中的分析之后)。由于重新参数化不变性,约束的存在(类似于规范量子引力,将在后面讨论)也将被触及。

人们可以通过使用任意参数 τ 而不是时间 t. 时间 t ( τ ) 来标记理论的方程,从而将变成动态变量。

让我们再次考虑最小作用的原理,或者更具体地说,它的修改版本,其中将作用表示为在相空间而不是配置空间中对系统轨迹的积分。

在哈密顿形式中(在文章的第一部分中讨论),坐标和它们的共轭动量是独立的坐标,人们可以独立地改变它们(详情见 Goldstein )。非相对论粒子的作用如下:

方程式 146:最小作用原理的修改版本,用相空间变量而不是构型空间变量来表示。

使用标签 τ 对相空间中的路径进行参数化,并将时间变量 t 提升到动态变量的等级,定义了以下四分量向量

其中 p 是与x正则共轭的动量,那么一个可以重写动作如下

方程 147:作用表示为对参数τ的积分。

从等式中注意到。对应于 t 的共轭动量由下式给出:

方程 148:动量与时间变量 t 共轭。

并且改变动作 p ₒ,可以获得以下约束:

方程 149:动态变量服从的约束。

对于动态变量,

方程 150:包括时间 t(τ)的动态变量。

等式中的数量。149 被称为“超哈密顿量”(基弗)。一般来说,在重新参数化下不变的理论中,约束方程。149 是服从的。

图 76:诺贝尔奖获得者 P. M .狄拉克(来源)和他的系列讲座,其中他扩展了泊松括号以包括具有第二类约束的系统,允许它们经历规范量子化 ( 来源)。

遵循受约束系统的狄拉克哈密顿形式体系,取约束等式。149 考虑在内,一个拉格朗日乘数项被添加到动作中:

方程 151:这个动作,现在包括拉格朗日乘数 n。

通过相对于 p ₒ改变动作 s,可以容易地显示拉格朗日乘数 N ,给出:

等式 152:给定拉格朗日乘数 n 的含义,相对于 p ₒ改变动作

然后可以用通常的方法将理论量子化:

方程 153:用于量化约束粒子动力学的定义。

(除了动量的时间分量中的负号)。然后(沿着另一条路线——见文章的第二部分)得到薛定谔方程:

方程 154:通过狄拉克公式获得的薛定谔方程。

ADM 形式主义

广义相对论的哈密尔顿公式涉及所谓的时空 3+1 分裂(或叶理),遵循 ADM 形式主义。在广义相对论的这个公式中,度量的空间分量变成了动态自由度或“变化的东西”(见巴伯尔)。本节内容大多基于泊松和基弗和这两本专著的本套讲座。

图 77: 理查德·阿诺维特、斯坦利·德塞尔和查尔斯·米斯纳和他们的著名论文之一(来源)。

在继续之前,让我提供一个什么是嵌入超曲面的快速非严格定义。在一个 d 维流形中(在当前情况下 d =4),超曲面是一个( d - 1 )维子流形,它可以由一个标量场方程定义如下:

方程 155:一般超曲面的定义。

超曲面的一个简单例子是嵌入在三维欧几里得空间中的二维球体。在这种情况下,Eq。第 155 条写道:

方程 156:超曲面的例子,三维欧几里得空间中的 2-球面。

为了我们目前的目的,也就是将时空叶形化,我们首先引入一个任意标量场,它被方便地表示为(在泊松之后)

方程式 157:遵循 ADM 形式的 3+1 时空 ADM 切片中使用的类时标量场。

并构建了一堆类空超曲面,将时空叶形化:

方程 158:必须不相交的类空超曲面的定义。

ADM 结构如下图所示(参见泊松、科尔布和特纳或米斯纳、索恩和惠勒)

图 78:ADM 结构由两个超曲面或三维几何组成,嵌入一个四维几何(时空),即时空。显示了延时函数和位移矢量(基于 Kolb 和 Turner 和 Poisson )。

两个相邻的超曲面可以表示为

方程 159:由类时变量 t 参数化的超曲面。

并且被选择为类空的和不相交的。这些超曲面的类时单位法线 n

方程 160:垂直于超曲面的单位所服从的要求。

然后在每个超曲面上建立一个坐标系,并用来表示它们:

方程 161:超曲面上坐标的选择。

然后使用一组曲线或曲线的同余关系(图中未示出)来关联不同超曲面上的坐标。

图 79:曲线的一致性。沿着每条曲线的点在不同的超曲面中具有相同的 y 值。

然后我们强加(再次遵循泊松)属于同一曲线的不同超曲面中的点具有相等的 y 坐标。因此,时空的坐标系是由方程建立的。161.

我们现在分解与同余曲线相切的向量 t ,其分量为:

等式 162:同余曲线的正切向量。

转化为以下向量

方程 163:分别是超曲面上的单位法向量和切向量。

它们分别是超曲面的法线和切线(包含失效函数 N 以归一化 n )。分解如下:

方程 164:将与同余曲线相切的矢量 t、分解成垂直于超曲面的矢量和与超曲面相切的矢量 e 。****

在矢量 N 处,引入了移位矢量(见下图,基于第 4 章由泊松)。

图 80:等式中的基本向量。164(基于来源)。

用新坐标系中的超曲面坐标表示的微分线元素 dx 然后表示为:

方程式 165:新坐标的微分 dx

然后,直接表明用 ADM 形式语言编写的 ds 由下式给出:

方程 166:超曲面坐标中的线元素(t, y )。

三维超曲面相对于其所嵌入的四维时空的曲率由以下表达式给出的非本征曲率来测量:

方程 167:超曲面的外曲率。

“|”符号表示我们之前见过的协变微分,但现在它仅限于空间度量hT7。****

图 81:外部曲率的几何表示(基于源)。

Ricci 标量 R (我们之前也讨论过)可以用外曲率 K其迹 K,3 曲率R(Ricci 标量的三维版本):

等式 168:用外在曲率、其迹和这三个曲率来表示的 Ricci 标量。

下一步是用同样的三个量写出拉格朗日密度。这是一个精心的计算,详细给出,例如在泊松中。人们获得:

方程 168:用三曲率、外曲率及其迹线表示的引力拉格朗日密度。

具有相应的动作:

方程 169:上面拉格朗日对应的作用。

从拉格朗日密度,我们发现对应于推移函数和位移矢量的共轭动量为零:

方程 170:与推移和位移相关的共轭动量消失。

换句话说, ππ ⁱ不是动力学的(它们不会由哈密顿演化方程决定)。它们只是超曲面σ变形的度量。导数的消失是因为这个动作不包含任何一个量

等式 170:这两个量都不存在于 ADM 动作中,这解释了为什么时间推移和偏移向量不是动态量。

这些条件被称为初级约束(更多细节见泊松、狄拉克或基弗)。唯一的动态量(如前所述)是 3-公制的 h 。它有一个关联动量,由下式给出:

方程 171:三度规 h 对应的共轭动量。

哈密顿量以通常的方式建立:

方程 172:使用 ADM 形式变量的引力哈密顿量。

这个哈密顿量可以重新表示如下

等式 173:用 ADM 量表示的相同的标准引力哈密顿量。

被积函数中的两个物体

并且:

方程 174:超空间中的德威特度规

对于时间 t 的所有值,两个共轭动量都为零,这意味着:

等式 175:由于主要约束始终为零,因此它们的时间导数也为零。

从具有哈密顿量的共轭动量的泊松括号中,可以得到:

等式 176:πt20*t22 和T25*t27】t28】πⁱ和 h 的泊松括号都消失,给出了次级约束的原点。

第二约束是:

方程式 177:理论量子化所需的次级约束。

惠勒-德威特(WdW)方程

我们现在(终于)可以完整地写下 WdW 方程了。类似于在微超空间情况下所做的,我们将两个次级约束中的第一个的量化版本确定为惠勒-德威特方程

方程 178:惠勒-德威特方程这是一个零能量的类似薛定谔的宇宙波函数方程。

对于宇宙的波函数ψ。为了明确地写下来,我们应用规范的量化过程,在本例中为:

等式 179:在正则量子化之后,用关于 h 的导数代替 π

将上述等式放在一起,我们得到:

方程 180:一般惠勒-德威特方程。

操作符的排序有一些微妙之处,但为了简洁起见,我将在这里省略它们(参见 Kolb 和 Turner )。

正则量子引力中的时间

惠勒-德威特方程有一些显著的特征,使它不同于薛定谔方程。

永恒性 第一个是时间坐标的缺失:宇宙的波函数ψ仅依赖于类空三维几何(这就给引言中提到的“时间问题”以起源)。这是由于广义相对论的微分同胚不变性(或等价地,它的广义协方差),这意味着一个特定的(外部)时间坐标不应该出现在方程中(见 Hamber )。

人们还可以如下理解ψ依赖性(参见基弗)。在量子力学中,粒子具有明确定义的轨迹 q ( t )的概念毫无意义,取而代之的是在特定位置 q 在时间t发现粒子的概率。因此,在薛定谔方程中,波函数不应该取决于 q ( t ),而只取决于 qt 类比hqgq(t)并回忆 GR 中不存在绝对时间,用 qt 替换 q ( t 这就证明了为什么ψ的论证中只剩下 3 维空间(而不是 4 维时空)。**

WdW 方程是实数而不是复数,因为 SE 第二个显著的区别是惠勒-德威特方程是实数而薛定谔方程(SE)是复数。问题是,根据我们在本文第二部分的讨论,时间和虚数单位 i 本质上是不可分的。这让 i 的失踪相当令人费解。****

在描述微型超空间的情况时,我已经提到了一些关于这种永恒性的本质和解释的要点。这里我再补充几个:

  • 人们可以将 WdW 推广到包括物质场。这激发了对ψ的如下解释:它测量和物质场之间的相关性。当然,理论的量子本质只允许我们知道物质和能量的统计分布。在数学上,当我们包括物质场时,WdW 方程发生的唯一变化是左手边的表达式获得了一个额外的项,即:**

情商。181:物质场的包含导致了这个新术语的出现,其中 T 是物质场应力-动量张量。

  • 作为第一点的推论,如果ψ的当前形式已知,那么 WdW 方程就给了我们宇宙具有某些大尺度性质的概率。如果理论是正确的,像我们观察到的配置(如宇宙加速膨胀)应该有很高的概率。
  • 正如我们所见,由于空间和时间是由时空的物质和能量属性决定的,我们可以(见 Barrow )根据每个“时间片”的几何属性来定义“时间”。它是“宇宙的物质内容及其结构的构造”。
  • WdW 告诉我们在第二和第三点中描述的“内在”时间变量的不同值下波函数ψ之间的关系。
  • 为了求解ψ,我们需要初始条件,在这种情况下,宇宙的初始波函数。
  • 有可能基本的量子引力方程(不是 WdW 方程)实际上是复杂的。参见巴布及其参考文献。

WdW 的完全精确解自然是无法得到的。然而,如上所述,可以找到简化版方程的解(见迪思或阿特卡兹)。

时间的再现:半经典体制

通过将系统分成两个子部分,并用一个子部分的参数作为时间来表示其中一个子部分的时间演化,可以恢复时间。

图 167:英国物理学家内维尔·弗朗西斯·莫特爵士,1977 年诺贝尔物理学奖获得者。

时间和 α- 粒子 这是由英国物理学家【Neville Francis Mott 爵士在 1931 年一篇关于原子物理学的论文中首次注意到的,在这篇论文中研究了α粒子和原子之间的碰撞。在他的论文中,莫特使用了下面的程序。他首先把整个系统(原子和碰撞的α粒子)分成两个子部分。然后用其中一个子部分,即 α 粒子的状态来定义时间。利用这个时间参数,他推导出了另一个子部分原子的完整的含时薛定谔方程。在数学术语中,完整的量子态表示为:**

方程 182:原子+ α粒子量子系统的态。子索引代表原子,α代表α粒子。

为了获得原子的薛定谔方程,莫特把时间定义为

方程 183:用方向导数定义时间。

然后得到薛定谔方程。(上述半经典分析部分基于这篇文章)。

半经典近似和 WdW 方程 同样的半经典分析可以类似地讨论完整的 WdW 方程(基弗)。为此,人们围绕经典引力理论展开,将ψ写成一种玻恩-奥本海默近似

方程 184:WdW 方程解的波恩-奥本海默型近似。

其中 S₀是经典理论的哈密尔顿-雅可比方程的解,即

方程 185:经典引力的 Thes-汉密尔顿-雅可比方程。

像在标准量子力学中一样进行,通过几个简单的步骤(见 Hamber ),可以得到:

方程 186:从 WdW 方程经过一系列近似后得到的阿瑟型方程。

其中 ϕ 上标表示物质场被包括在内,并使用了以下时间定义:

等式 187:使用关于 h 的函数导数的时间定义。

因此,只有在半经典状态下,时间和复数才能被恢复。

我希望你觉得这篇文章很有趣。我解释了许多不同的概念,其中一些很简单,一些很有挑战性。但是我希望主要的想法足够清楚。

一如既往,感谢您的阅读,再见!一如既往,我们随时欢迎建设性的批评和反馈!

我的 Github 和个人网站 www.marcotavora.me ,都有一些其他关于理论物理的很酷的东西。看看他们!

量子机器学习 101

原文:https://towardsdatascience.com/quantum-machine-learning-101-1058f24c3487?source=collection_archive---------43-----------------------

如果你认为 ML 很有趣,那么 Q ML 来了!

照片由马库斯·斯皮斯克在 Unsplash 拍摄

在量子机器学习(QML)领域已经做了很多工作,这个博客只是给你一个简短的 10 分钟介绍 QML 的世界。因此,这本书对你来说应该只是一个有趣的读物,而我计划写一个关于 QML 的更系统更深入的系列教程。

经典计算机是通用的,因为它们可以实现任何逻辑运算,而量子计算机是通用的,因为一组量子位上的任何量子态都可以转换成任何其他量子态。量子(电路)计算可行性的许多良好的早期希望来自索洛维-基塔耶夫定理,在非常广泛的条件下(神经元/门&普适函数/状态近似的有限性)可以等同于深度学习的普适近似定理。

伊辛模型和哈密顿量

简单来说,伊辛模型是一种表示体系内相变和相互作用的数学模型【1】【2】。这种系统在任一点的能量由该点的各自的哈密顿量(方程式)给出。

比如说在图 1 中。下面,我们有一个 4X4(点阵)矩阵系统。每个单元处于两种可能状态之一(比如+1,-1)。假设每个单元只有四个相邻(上、左、右、下)单元,如果存在的话。

所以 6 的相邻单元格是 3,5,7 和 10。

图 1 。随机系统(同一系统的两种不同表示)

现在每个细胞都拥有一些能量,这是由于它与邻居的相互作用,还有一些是由于它自己的状态。每个系统都想达到(过渡到)最低能量状态。

Eq1 。哈密顿函数

所以 Eq1。以上是这种系统在任一点的能量方程

  • M :表示上述任一点的系统/矩阵
  • < i,j > :所有对代表相邻单元格 I,j
  • J i,j :表示相邻细胞间的相互作用能量系数
  • Mi :表示每个单元格的值
  • 嗨:代表每个的能量系数
  • μ :表示每个电池的总(外)能量系数

现在,虽然上面的等式甚至对于简单的物理磁体(它最初是为其提出的)来说不够准确,但是它很好地模拟了由于每个粒子的状态、它与其他粒子的相互作用以及每个粒子上的任何外部因素而产生的系统能量。

图 2 开始状态(左)过渡状态(中)结束状态(右)

图二。上面显示了这样一个系统(200X200)从初始状态(左)到最终状态(右)的转变。

量子 ML 的方法

虽然总结量子最大似然的所有可能的方法对我来说是不可能的,主要是因为我对我读过的一些算法的理解有限,但是因为一般来说任何量子优化/最小化算法都可以有一个等价的 QML 用例。

在下面提供的 3 种方法中,我们将只关注第一种模型,在本博客后面的讨论中,其他两种方法放在这里只是为了给出 QML 算法的更大的图像

量子电路/门模型

  • 将经典问题映射到量子算法。
  • 相应的量子算法然后被转换成量子电路
  • 量子电路然后在量子处理器上运行

绝热量子计算[暂时忽略]

  • H1 代表一个系统的能量纯粹由于单个细胞&而不是相互作用(即 Jij = 0)。
    (注意:这里的基态/最小能态是所有比特的叠加)如果你不知道什么是量子比特的叠加,现在忽略这一点
  • H2 代表我们系统的一般能量状态,如上面等式 1 给出的
  • 现在随着时间的推移(每一步),我们想要从 H1 的基态转移到 H2 的基态
  • 在任何时候,系统的状态将是 H1 对 H2 的凸组合,t ∈ [0,1]
    H (t) = (1-t) * H1 + t * H2
  • 注意,这个问题不容易解决,也很难解决,因为我们不知道在所有状态之间移动的正确转换速度(类似于在梯度下降中选择学习速率)

量子退火【简化绝热量子计算】

  • 这是绝热量子计算的更可行/实用的版本,其中我们不是找到我们的目标系统的基态,而是开始在每次跃迁中找到许多不同的低能态(基于不同的跃迁速度)。我们希望能量最低的状态是目标系统的基态

如果您想了解更多相关信息,请查看以下链接:

  • 量子退火更
  • 量子绝热计算
  • 量子近似优化算法

基本上,以上所有方法不知何故都会变成,我们得到一个等价的量子电路&通过一个量子处理单元(QPU)结合我们的标准 CPU 来解决它。

在这篇博客中,我们将假设我们被神奇地给予了一个量子电路来解决我们的问题&我们想在一个有编程接口的 QPU 上解决它。

让我们编码

现在,我们将尝试构建一个简单的回归暨分类问题,在下面的笔记本中提供,但在这里我们将一点一点地打破它。

[## AbhishekAshokDubey/量子计算-101

github.com](https://github.com/AbhishekAshokDubey/quantum-computing-101/blob/master/qml-101/tfq_ml_101.ipynb)

同样,我们已经假设了问题的电路,主要由最后的 MNIST 问题电路驱动,如法尔希等人 &所建议的,也被谷歌 TFQ MNIST 的例子所使用。

在开始之前,我们需要在 tensorflow (TF)上安装 tensorflow-quantum (TFQ)和 Cirq。

  • tensorflow-quantum :作为任何底层 QPU 的编程语言(接口)。(很像 GPU 的 CUDA)
  • Cirq :作为定义量子电路的编程语言
tensorflow & pip install -q **tensorflow**==2.1.0
pip install -q **tensorflow-quantum**
pip install -q **cirq**

让我们导入所有我们需要的包

**import** **numpy** **as** **np**
**import** **sympy**

*# For Quantum ML*
**import** **cirq**
**import** **tensorflow** **as** **tf**
**import** **tensorflow_quantum** **as** **tfq**

*# For Visualization*
%matplotlib inline
**import** **matplotlib.pyplot** **as** **plt**
**from** **cirq.contrib.svg** **import** SVGCircuit

让我们为我们的问题制作一个虚拟玩具数据

**def** get_data(l):
    data = np.random.randint(0,1,(l,2))
    label = np.ones(l)
    data[0::2,0]=1
    data[1::2,1]=1
    label[0::2] = -1
    p = np.random.permutation(l)
    **return** data[p], label[p]

x_train, y_train = get_data(5000)
x_test, y_test = get_data(200)print(pd.DataFrame(np.concatenate((x_train, np.reshape(y_train, (-1,1))), axis=1) , columns=["x1", "x2", "y"]))

生成的数据如下所示,上面打印语句的输出。基本上 x1 =1 → y = -1,x2 =1 → y = +1。不要问我为什么,但这看起来像是解决简单问题的方法:)

 x1   x2    y
0     1.0  0.0 -1.0
1     1.0  0.0 -1.0
2     1.0  0.0 -1.0
3     *0.0  1.0  1.0*
4     1.0  0.0 -1.0
...   ...  ...  ...
4995  1.0  0.0 -1.0
4996  *0.0  1.0  1.0*
4997  1.0  0.0 -1.0
4998  1.0  0.0 -1.0
4999  1.0  0.0 -1.0

关于量子数据的一个有趣的事实是,它不能被存储&它只能作为量子电路本身产生/提供。多检查几个

因此,我们上面生成每个数据点都应该被转换成等效的量子电路。所以基本上对于 0 位我们什么都不做,但是我们通过一个 1 位虽然不是门。下面是一个简单的函数来做同样的事情,将 0/1 上的数据转换成等效电路

**def** convert_to_circuit(x):
    qubits = cirq.GridQubit.rect(1, 2)
    circuit = cirq.Circuit()
    **for** i, val **in** enumerate(x):
        **if** val:
            circuit.append(cirq.X(qubits[i]))
    **return** circuit

所以,让我们得到经典数据集中每个数据点的等效量子数据(电路)。

x_train_circ = [convert_to_circuit(x) **for** x **in** x_train]
x_test_circ = [convert_to_circuit(x) **for** x **in** x_test]

为了将上面用 CIRQ 生成的电路/数据点传递到 TFQ,我们需要将每个电路/数据点转换成 TFQ 张量。

x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)

现在我们已经准备好了我们的量子数据,我们现在需要的是解决我们问题的量子电路&一个解决它的方法:)

如上所述,让我们假设一个电路,即使它对于手头的问题不是最好的,但是我们相信如果一个类似的电路可以解决简化的 MNIST 二进制(3 对 6)数字分类,它应该很容易解决我们的问题

图三问题电路模型

这里(-1,-1)行/量子位是我们的最终输出类预测,而(0,0)和(1,0)量子位是我们的输入数据。

那么,让我们在 CIRQ 中构建上述电路。

input_qubits = cirq.GridQubit.rect(2, 1)  *# 2x1 grid.*
readout = cirq.GridQubit(-1, -1)   *# a qubit at [-1,-1]*
model_circuit = cirq.Circuit()

model_circuit.append(cirq.X(readout))
model_circuit.append(cirq.H(readout))

alpha1 = sympy.Symbol('a1')
model_circuit.append(cirq.XX(input_qubits[0], readout)**alpha1)

alpha2 = sympy.Symbol('a2')
model_circuit.append(cirq.XX(input_qubits[1], readout)**alpha2)

beta1 = sympy.Symbol('b1')
model_circuit.append(cirq.ZZ(input_qubits[0], readout)**beta1)

beta2 = sympy.Symbol('b2')
model_circuit.append(cirq.ZZ(input_qubits[1], readout)**beta2)

model_circuit.append(cirq.H(readout))
model_readout = cirq.Z(readout)SVGCircuit(model_circuit)

SVGCircuit(model_circuit)代码/命令应该能够在嵌入式控制台上绘制电路图像。

从这里开始,事情会变得和经典的机器学习一样有意义

现在更像 Keras 序列神经网络模型,我们将建立一个模型来解决我们的问题电路。

*# Build the model.*
model = tf.keras.Sequential([
    *# The input is the data-circuit, encoded as a tf.string*
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    *# PQC layer returns the expected val of the readout gate @[-1,1]*
    tfq.layers.PQC(model_circuit, model_readout),
])

最后,我们需要定义损失函数、优化器和指标来跟踪。

model.compile(
    loss=tf.keras.losses.MeanSquaredError(),
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[accuracy])

下面是我们目前构建的模型包含的内容(来自模型摘要)

图 4 。模型摘要

让我们用量子数据来训练我们的量子电路模型。

model_history = model.fit(
      x_train_tfcirc, y_train,
      batch_size=200,
      epochs=5,
      verbose=1,
      validation_data=(x_test_tfcirc, y_test))

results = model.evaluate(x_test_tfcirc, y_test)

以下是您的训练和验证数据丢失(&准确性)将如何随着每个历元而变化。

图 5 。量子训练历史

希望一切顺利,试着从测试数据中预测值。

print(list(zip(model.predict(x_test_tfcirc).ravel()[:10], y_test[:10]))) [(-0.7765335, -1.0),
(0.77620333, 1.0),
(0.77620333, 1.0),
(0.77620333, 1.0),
(-0.7765335, -1.0),
(0.77620333, 1.0),
(-0.7765335, -1.0),
(0.77620333, 1.0),
(0.77620333, 1.0),
(0.77620333, 1.0)]

基于以上,你会认为我们已经解决了一个经典的分类问题,即回归问题,所以请继续改进它
提示:铰链丢失?

最后,一旦你理解了这一点,对你来说,编写你自己的简单的 MNIST 两类分类器就容易多了,而不需要参考谷歌的教程:)

如果您想知道量子处理单元如何与当前的处理单元结合使用,以及我们上面使用的所有东西在更大的画面中适合什么位置(?).
谷歌 TFQ 团队的以下架构将为你提供一些快速的提示。

张量流量子栈:https://youtu.be/-o9AhIz1uvo?t=1247

现在是时候从一个更小的玩具例子转移到一个更好的玩具例子了,试试下面。

[## 用量子神经网络在近期处理器上进行分类

我们介绍了一个量子神经网络,QNN,它可以代表标记数据,经典或量子,并训练…

arxiv.org](https://arxiv.org/abs/1802.06002) [## MNIST 分类|张量流量子

更高分辨率的输入和更强大的模型使 CNN 很容易解决这个问题。而一个经典的模型…

www.tensorflow.org](https://www.tensorflow.org/quantum/tutorials/mnist)

干杯&快乐学习!!!

giphy.com:https://gph.is/g/ap0YjGy

参考资料:

  1. 【https://www.youtube.com/watch?v=16ZfkPRVf2w
  2. https://github . com/AbhishekAshokDubey/quantum-computing-101/tree/master/qml-101
  3. https://www.tensorflow.org/quantum/tutorials/mnist
  4. https://www.youtube.com/watch?v=-o9AhIz1uvo
  5. https://medium . com/@ adubey 40/quantum-computing-101-1ed 742540 ba 2

量子机器学习——超越宣传

原文:https://towardsdatascience.com/quantum-machine-learning-e1955476a6ad?source=collection_archive---------12-----------------------

本帖是本书的一部分: 用 Python 动手做量子机器学习。

如果计算机科学中有两个术语被我描述为过度宣传和缺乏理解,我会说机器学习量子计算

也就是说,量子机器学习就是利用量子计算进行机器学习算法的计算。还能更糟吗?

关于这两种技术有很多奇闻轶事。它们始于理解我们人类自然语言的机器。它们终结于人工智能的出现,要么表现为终结者式的天启,要么表现为瓦力式的乌托邦。

作者弗兰克·齐克特的图片

不要上当炒作!

不带偏见地详细观察一项技术有助于不被天花乱坠的宣传和民间传说所迷惑。先说机器学习。

什么是机器学习?

“机器学习本质上是一个事物标签器”

——谷歌首席决策科学家凯西·科济尔科夫 来源——

通过机器学习,我们的目标是给尚未标记的东西贴上标签。主要有三种方法:分类、回归和分段。

分类中,我们尝试预测一个实例的离散标签。给定输入和一组可能的标签,它是哪一个?这里有一张图片。它是一只猫还是一只狗?

作者弗兰克·齐克特的图片

回归是关于寻找一个函数来预测某个输入和依赖的连续输出值之间的关系。假设你知道你朋友的收入和有效税率,即使你不知道实际的计算,你能估计你的收入的税率吗?

作者弗兰克·齐克特的图片

分割是将群体划分为具有相似特征的群体的过程,这些群体因此可能表现出相似的行为。假设你生产一种昂贵的产品,比如游艇,以及一群潜在客户,你想把产品卖给谁?

作者弗兰克·齐克特的图片

什么是量子计算?

量子计算是一种不同形式的计算。它利用了量子物理的三个基本性质:叠加、干涉和纠缠。

叠加是指一个量子系统可以同时存在多个态的量子现象。

作者弗兰克·齐克特的图片

实际上,量子系统并不同时存在于多个状态中。它存在于一个状态 *0* 和一个状态 *1* 的复杂线性组合中。它是一种不同的组合,既不是“或”,也不是“和”。

量子干涉让我们可以将量子系统偏向想要的状态。这个想法是创造一种干涉模式,其中导致错误答案的路径破坏性地干涉并抵消,但是导致正确答案的路径相互加强。

作者弗兰克·齐克特的图片

纠缠是量子粒子之间极强的关联。即使相隔很远,纠缠的粒子仍然保持完美的关联。

作者弗兰克·齐克特的图片

你已经看过《终结者》了吗? 没有?

也许是瓦力? 又没有?

也许看看这些东西是如何工作的会有帮助。

机器学习是如何工作的?

有无数的机器学习算法。但是每一种算法都有三个组成部分:

  • 表示描述了算法用来表示知识的内部架构。它可能由一组规则、实例、决策树、支持向量机、神经网络等组成。
  • 评估是评估候选算法参数化的函数。例子包括准确性、预测和召回、平方误差、后验概率、成本、边际、熵等等。
  • 优化描述了生成候选算法参数化的方式。这就是所谓的搜索过程。例如,组合优化、凸优化和约束优化。

机器学习的第一步是架构的开发,即表示。该架构指定了参数,这些参数的值保存了知识的表示。这一步确定解决方案对于解决某个问题的适合程度。参数越多并不总是越好。如果我们的问题可以用一个线性函数来解决,那么试图用一个包含数百万个参数的解来解决它很可能会失败。另一方面,参数很少的架构可能不足以解决自然语言理解等复杂问题。

作者弗兰克·齐克特的图片

一旦我们满足于用架构来表示知识,我们就用例子来训练我们的机器学习算法。根据参数的数量,我们需要大量的例子。该算法尝试预测每个示例的标签。我们使用评估函数来衡量算法的执行情况。

最后,优化器调整表示参数,以保证测量的评估有更好的性能。它甚至可能涉及到改变表示的体系结构。

学习不会一蹴而就。而是小步前进。为了获得良好的性能,根据问题的复杂程度,这个一般过程需要重复几次,直到机器能够在一个物体上贴上正确的标签。

量子计算机擅长哪些任务?

量子力学的世界不同于我们在日常生活中经历的物理学。那么量子计算的世界与经典(数字)计算有什么不同呢?

让量子计算如此强大的不是它的处理速度。其实挺慢的。让量子计算如此强大的也不是它的内存。事实上,它小得荒谬。我们谈论的是几个量子比特。

让量子计算如此强大的是它所实现的算法。这些算法表现出不同于经典算法的复杂性特征。

为了理解这意味着什么,让我们简单看一下复杂性理论。复杂性理论是对运行算法所需的计算工作的研究。

例如,加法的计算量是O(n)。这意味着两个数相加的工作量随着数的大小(位数)线性增加。乘法的计算量是O(n^2)。努力随着数量大小的平方而增加。

据说这些算法在多项式时间内是可解的。但是这些问题相对简单。解决因式分解问题(即寻找一个 n 位数的质因数)的最佳算法是O(e^n^(1/3))。这意味着努力随着位数的增加而呈指数增长。

作者弗兰克·齐克特的图片

不要低估O(n^2)O(e^n^(1/3))复杂性之间的差异。虽然你的智能手机能够在几秒钟内将 800 位数字相乘,但在超级计算机上,这种数字的因式分解需要大约 2000 年。

一个精明的量子算法(比如 Shor 的算法)可以用叠加法同时求出一个数所有可能的因子。它不是计算结果,而是使用干涉来组合所有可能的答案,从而得出正确的答案。该算法解决了一个复杂度为O((log n)^2 (log log n)(log log log n))的因式分解问题。这是多项式复杂度!乘法也是。

量子计算是强大的,因为它承诺以降低的复杂性解决某些类型的数学计算。

你现在看的是终结者还是瓦力? 还没有?

量子机器学习的案例

量子机器学习是利用量子计算进行机器学习算法的计算。

我们已经知道,机器学习算法包含三个组成部分:表示、评估和优化。

当我们看到这种表示时,当前的机器学习算法,如 2020 年发表的生成式预训练变压器 3 (GPT-3)网络,浮现在脑海中。GPT 3 号产生了类似人类的文本。它有 1750 亿个参数。

IBM Q 量子计算机有 27 个量子位。尽管量子比特存储的信息比经典比特多得多(因为它既不是0也不是1),但量子计算机的表示能力还远远不能推进机器学习。

在评估过程中,机器学习算法试图预测一个事物的标签。传统上,这包括测量和转换数据点。例如,神经网络依赖于矩阵乘法。这些都是经典计算机擅长的任务。然而,如果您有 1750 亿个参数,那么计算结果预测需要相当多的矩阵乘法。

最后,算法需要以有意义的方式改进参数。问题是找到一组能产生更好性能的参数值。175 亿个参数,组合的数量是无穷的。

经典的机器学习采用启发式方法,利用问题的结构在合理的时间内收敛到足够的解决方案。尽管使用了甚至更先进的启发式方法,训练 GPT-3 将需要 355 年才能在单个 GPU(图形处理单元)上训练,并花费 460 万美元。只是为了感受一下在这种情况下合理是什么意思。

量子计算的主要特点是能够同时计算多个状态。量子优化算法可以结合所有可能的候选者,并产生那些有希望得到好结果的候选者。因此,量子计算在算法优化方面有望比经典计算机快出指数级。

但这并不意味着我们只关注优化。因为优化建立在运行评估的基础上。并且评估建立在表示的基础上。因此,挖掘量子计算的全部潜力来解决机器学习优化问题需要评估和表示来与量子优化器集成。

考虑到经典机器学习算法今天可以做什么,如果我们希望量子计算能够大幅降低训练这种算法的复杂性,那么炒作就变得可以理解了。因为我们离人工通用智能“只有”几个数量级的距离。

当然,构建人工智能需要的不仅仅是计算。它需要数据。它需要算法。这种算法的开发是量子机器学习中当前的挑战之一。

本帖是本书的一部分: 用 Python 动手做量子机器学习。

免费获取前三章点击这里。

量子机器学习:神经网络学习

原文:https://towardsdatascience.com/quantum-machine-learning-learning-on-neural-networks-fdc03681aed3?source=collection_archive---------30-----------------------

分析梯度计算、Hadamard 测试等等

我的上一篇文章讨论了量子计算机上的贝叶斯网络(在这里阅读!),以及 k-means 聚类,我们进入量子机器学习这个怪异而奇妙的世界的第一步。

这一次,我们将深入兔子洞,看看如何在量子计算机上构建神经网络。

作者图片

如果你不了解神经网络,不要担心——我们从神经网络 101 开始。

什么是(经典)神经网络?

几乎每个人都听说过神经网络——它们被用来运行我们今天拥有的一些最酷的技术——自动驾驶汽车、语音助手,甚至是生成名人做可疑事情的超现实照片的软件。

它们与常规算法的不同之处在于,我们不需要写下一套规则,而是需要向网络提供我们希望它解决的问题的例子。

我们可以用 IRIS 数据集中的一些数据输入一个网络,这个数据集中包含了三种花的信息,它可能会猜出这是哪一种花:

作者图片

所以现在我们知道了神经网络是做什么的——但是它们是如何做到的呢?

重量、偏见和积木

神经网络由许多称为神经元的小单元组成,看起来像这样:

作者图片

大多数神经元接受多个数字输入(蓝色圆圈),并将每个数字输入乘以一个权重(wᵢs),该权重表示每个输入的重要性。权重的大小越大,相关的输入就越重要。

偏差被视为另一个权重,只是它所乘以的输入值始终为 1。当我们将所有加权输入相加,我们得到神经元的激活值,用上图中的紫色圆圈表示:

然后,激活值通过一个函数(蓝色矩形)传递,结果是神经元的输出:

我们可以通过改变神经元用来转换其激活值的函数来改变神经元的行为,例如,我们可以使用一个超级简单的转换,就像这样:

然而,在实践中,我们使用更复杂的函数,如 sigmoid 函数:

神经元有什么用?

他们可以根据他们收到的输入做出决定——例如,我们可以使用神经元来预测我们下次在意大利餐馆吃饭时是吃披萨还是意大利面,方法是向它提供三个问题的答案:

  • 我喜欢这家餐馆的意大利面吗?
  • 餐馆有香蒜沙司吗?
  • 这家餐馆有三层奶酪比萨饼吗?

抛开可能的健康问题,让我们看看神经元可能是什么样的——我们可以用 0 代表 no,1 代表 yes 来编码输入,并通过将 0 和 1 分别映射到 pasta 和 pizza 来对输出进行同样的处理:

作者图片

让我们使用阶跃函数来转换神经元的激活值:

仅仅使用一个神经元,我们就可以捕捉多种决策行为:

  • 如果我们喜欢餐馆里的意大利面,我们会选择点意大利面,除非香蒜沙司卖完了,他们会提供三层奶酪披萨。
  • 如果我们不喜欢餐馆里的意大利面,我们会点一份比萨饼,除非有香蒜沙司,而三层奶酪比萨饼没有。

我们也可以用另一种方式来做事情——我们可以对神经元进行编程,使其对应于一组特定的偏好。

如果我们想做的只是预测我们下次外出时会吃什么,那么为一个神经元计算出一组权重和偏好可能会很容易,但如果我们不得不对一个完整规模的网络做同样的事情呢?

这可能需要一段时间。

幸运的是,我们可以创建算法来改变网络的参数——权重、偏差甚至结构,而不是猜测我们需要的权重值,这样它就可以学习我们想要解决的问题的解决方案。

向下到达顶端

理想情况下,网络的预测应该与我们提供给它的输入相关联的标签相同——因此,预测和实际输出之间的差异越小,网络学习的权重集就越好。

我们使用损失函数来量化这种差异,损失函数可以是我们想要的任何形式,就像这个,它被称为二次损失函数:

y(x) 是期望的输出,而 o(x,θ) 是当输入带有参数θ 的数据 x 时网络的输出。由于损耗总是非负的,一旦它呈现接近于 0 的值,我们就知道网络已经学习了好的参数集。当然,还有其他可能出现的问题,比如过度拟合,但我们现在忽略这些。

使用损耗函数,我们可以计算出网络的最佳参数集:

因此,我们需要做的不是猜测权重,而是根据参数θ最小化 C ,这可以使用一种称为梯度下降的技术来实现:

我们在这里所做的是,如果我们增加θᵢ的值,看看损失是如何变化的,然后更新θᵢ,使损失减少一点点。η是一个很小的数字,它控制着我们每次更新θᵢ时要改变多少。

为什么我们需要η变小?我们可以调整它,使电流 x 的损耗在一次更新后接近于零——大多数情况下这不是一个好主意,因为虽然它会减少电流 x 的损耗,但它通常会导致我们提供给网络的所有其他数据样本的性能更差。

厉害!

现在我们已经有了基本的东西,让我们弄清楚如何建立一个量子神经网络。

进入量子宇宙

我们将建立的量子神经网络与我们迄今为止研究的经典网络的工作方式并不完全相同——我们没有使用具有权重和偏见的神经元,而是将输入数据编码成一串量子位,应用一系列量子门,并改变门参数以最小化损失函数:

作者图片

虽然这听起来很新鲜,但想法是一样的-更改参数集以最小化网络预测和输入标注之间的差异。

为了简单起见,我们将构建一个二元分类器——这意味着输入到网络的每个数据点都必须有一个 0 或 1 的关联标签。

它是如何工作的?

我们首先将一些数据 x 输入到网络,这些数据通过特征图传递,特征图是一种将输入数据转换成我们可以用来创建输入量子态的形式的功能:

我们使用的特征地图看起来几乎可以是任何东西——这里有一个二维向量 x ,并给出一个角度:

一旦 x 被编码成量子态,我们就应用一系列量子门:

网络输出,我们称之为π( x ,θ),是在|1〉状态下被测量的最后一个量子位的概率,加上一个经典添加的偏置项:

Zₙ ₋ ₁ 代表应用于最后一个量子位的 Z 门。

最后,我们获取与 x 相关联的输出和标签,并使用它们来计算样本上的损耗——我们将使用上面相同的二次损耗。我们为网络提供的整个数据集的成本就变成了:

网络 p 的预测可以从输出中获得:

现在我们需要做的就是弄清楚如何计算损失函数 l ( x ,θ)的梯度。虽然我们可以经典地做到这一点,但那会很无聊——我们需要的是一种在量子计算机上计算它们的方法。

计算梯度的新方法

让我们从关于参数θᵢ:的损失函数的微分开始

让我们扩展最后一项:

我们可以很快去掉常数项——在θᵢ = b 的情况下,我们知道梯度就是 1:

现在,使用乘积法则,我们可以进一步展开:

读起来可能有点痛苦,但多亏了厄米共轭(+),它有一个简洁的表示:

由于 U (θ)由多个门组成,每个门由不同的参数(或参数集)控制,因此求u【uᵢ【θᵢ】的偏导数只涉及对依赖于θᵢ:的门进行微分**

这就是我们为每个uᵢ选择的形式变得重要的地方。我们将对每个使用相同的形式,我们称之为 G 门——形式的选择是任意的,因此您可以使用您能想到的任何其他形式来代替:**

既然我们知道了每一个的样子,我们就可以找到它的派生物:****

幸运的是,我们可以用 G 门来表达这一点:

所以剩下的就是找出如何创建一个电路,给我们提供我们需要的内在产品形式:

获得与之成比例的可测量值的最简单方法是使用哈达玛测试——首先,我们准备输入量子态,并将一个辅助态推入叠加态:

现在将 Zₙ ₋ ₁B 应用到ψ上,条件是辅助处于 1 状态:

然后翻转安西拉,对 做同样的操作:

最后,将另一个哈达玛门应用到辅助设备上:

现在将辅助设备测量为 0 的概率是

所以如果我们用(θ)代替,并用(θ)替换掉的一个副本,那么安西拉量子位的概率就会给我们梯度**********

太好了!

我们找到了一种在量子计算机上分析计算梯度的方法——现在剩下的就是构建我们的量子神经网络了。

构建量子神经网络

让我们导入开始工作所需的所有模块:

现在让我们来看看我们的一些数据(你可以在这里得到正确的数据!)—这是 IRIS 数据集的处理版本,删除了一个类:

我们需要将特征(前四列)与标签分开:

现在让我们构建一个函数,它将为我们进行特征映射。

由于输入向量是归一化的和 4 维的,因此有一个超级简单的映射选项——使用 2 个量子位来保存编码数据,并使用一个将输入向量重新创建为量子态的映射。

为此,我们需要两个函数,一个用于从向量中提取角度:

另一个是把我们得到的角度转换成量子态:

这可能看起来有点令人困惑,但是理解它是如何工作的对于建造 QNN 来说并不重要——如果你喜欢,你可以在这里阅读它。

现在我们可以编写实现(θ)所需的函数了,这些函数将采取 RYCX 门交替层叠的形式。

为什么我们需要 CX 层?

如果我们不包括它们,我们将无法执行任何纠缠操作,这将限制我们的网络可以到达的希尔伯特空间内的区域——使用门,网络可以捕获量子位之间的相互作用,没有它们它将无法实现。

我们先从 G 门开始:

接下来,我们就做 CX 盖茨:

现在我们把它们放在一起得到 U (θ):

接下来,我们创建一个函数,它允许我们获得网络的输出,另一个函数将这些输出转换为类预测:

现在,我们可以构建一个在网络上执行正向传递的函数——向其提供一些数据,对其进行处理,并向我们提供网络输出:

之后,我们可以编写测量梯度所需的所有函数—首先,我们需要能够应用(θ)的受控版本:

使用它,我们可以创建一个计算期望值的函数:

现在我们可以算出损失函数的梯度——我们最后做的乘法是考虑梯度中的 π ( x 【T43,θ】)-y(x)项:**

一旦我们有了梯度,我们就可以使用梯度下降来更新网络参数,以及一种称为动量的技巧,这有助于加快训练时间:

现在,我们可以构建成本和准确度函数,这样我们就可以看到我们的网络如何响应训练:

最后,我们创建训练网络的函数,并将其命名为:

我们传递给 np.random.sample()方法的数字决定了参数集的大小——第一个数字(5)是我们想要的 G 层的数量。

这是我对一个五层网络进行十五次迭代训练后得到的输出:

看起来非常好——我们在验证集上达到了 100%的准确率,这意味着网络成功地推广到了看不见的例子!

包扎

所以我们建立了一个量子神经网络——太棒了!

有几种方法可以进一步降低损失——训练网络进行多次迭代,或者调整超参数,如批量大小和学习速率。

向前推进的一个很酷的方法是为(θ)尝试不同的门选择——你可能会找到一个更好的!**

你可以在这里抓取整个项目。如果您有任何问题,请在此留言或联系我们——我很乐意帮助您!

量子 MNIST

原文:https://towardsdatascience.com/quantum-mnist-f2c765bdd478?source=collection_archive---------30-----------------------

作者在 IBM Q 体验中生成的图像。

用量子计算模拟器对手写数字进行分类

我最近发表了一篇名为“ 130,780 点量子分类的文章,讨论了一个基本上使用 20 个数据量子位来表示整个数据集的电路。它实际上只重置和重新使用了 5 个模拟数据量子位,但它这样做了 4 次,因此需要 20 个数据量子位才能在真实的量子处理器上运行特定的电路。令我惊讶的是,两个 Twitter 用户对我如何编码数据集完全不感兴趣。

所以,我继续研究数据集,然后写了一篇名为“经典数据的超密集编码”的文章那一次,我讨论了一个不可用的两量子位版本的电路,但也讨论了一个可用的四量子位版本的电路。令我惊讶的是,同样的 Twitter 用户仍然不为所动。

当有人评论说数据集的大小与高度使用的 MNIST 数据集相比微不足道时,后者仍然相对较小,我决定 MNIST 数据集将是增加复杂性的下一个逻辑婴儿步骤。

介绍

MNIST 数据集是图像的集合。每张图片显示一个从 0 到 9 的一位数手写数字。一个经过训练的机器学习模型有望获得一组测试图像,并根据图像显示的数量正确地对每张图像进行分类。

MNIST 数据集是众所周知的,并且经过了良好的测试,所以如果你开始学习经典的机器学习,几乎可以保证你会使用它。

CSV 数据

我承认跳过了与 MNIST 合作的一个重要步骤,部分原因是代码已经可以在互联网上找到,部分原因是我致力于将 OpenQASM 专门用于这个项目。我没有先将图像转换成数值,而是下载了一个已经确定了数值的 CSV 文件。请注意我在本文末尾的致谢。

虽然 CSV 文件不包含所有的 MNIST 训练和测试数据,但它包含了足够的数据来执行这个分类实验。无论所用数据集的大小如何,算法都将保持不变。我们可以推测一个更大的数据集如何影响结果的准确性,但是任何这样的差异都可能被运行这个实验所需的维度减少量所抵消。

算法

大部分工作实际上是经典的预处理。我给自己设定了一个挑战——完全不使用 Python 外加在智能手机上准备和运行这个实验的额外挑战。如果您尝试使用 Python 来复制这个实验,我承认这应该容易得多。

  1. 对于 10 个类别(0 到 9)中的每一个,通过将每列的平均值除以 255 来计算每个像素的归一化平均值;通常这个计算稍微复杂一点,但是最小值是 0,所以我们可以删除 2 个减去 0 的实例
  2. 对于 10 个类别中的每一个,以卷积神经网络(CNN)启发的模式对归一化均值求和;我选择了一个 49 像素的 4x4 网格
  3. 将 160 个总和(16 个正方形 x 10 个类)除以 49,使其标准化
  4. 重新排列对角线模式中的值;仅出于编码目的,这将把我想要映射到每个量子位的向量彼此相邻,这样我就不必做太多的剪切和粘贴

最后一步是在量子位上混合数字的不同区域。这个想法是为了避免,例如,水平地映射量子位,用八个量子位中的四个来代表 0、3 和 8 的类似弯曲的顶部和底部。

CNN 启发

卷积神经网络(CNN)扫描图像的特征,如眼睛、鼻子和嘴巴的形状,以识别可能的人脸。我没有按行、列甚至对角线来浏览 MNIST 数据集,而是选择了一种受 CNN 启发的方块模式,希望这有助于辨别每个数字的不同特征。

作者创建的图像。

字母表示数据集中数值数据的列。

顺便说一下,这在 Python 中会容易得多。虽然我能够自动化其中的一部分,但我自己造成的 Python 限制导致这个实验的这一部分需要相当多的手动干预。

作者创建的图像。

基于之前给出的原因,新添加的颜色表明了这些块是如何映射到量子位的。例如,两个黄色块被映射到数据量子位 0,在电路上显示为 data[0]。

测试零点

在创建完整的电路之前,由于在智能手机上编写 OpenQASM 已经具有挑战性,我测试了一个零的图像和零的数据量子位。零测试电路在本文的最顶端。

作者在 IBM Q 体验中生成的图像。

结果表明,零有合理的概率被归类为零。然而,考虑到前面提到的 3 和 8 的相似性,我不能假设 92.261%会是与其他数字相比的最高结果,特别是。就此而言,由于所需的降维量,我无法假设所有的数字不会模糊在一起并测量相同的值。

完整的电路

使用 OpenQASM 子例程编写代码会容易得多,但他们过去收到过负面反馈,因为他们的电路只是显示出难以描述的块。相反,在下面,你可以看到许多 U3 门,弗雷德金门,复位门,加上一点点的哈达玛门和 x 测量。

作者在 IBM Q Experienc e 中生成的图像。

要放大,只需查看本文最上方的零测试电路。这里唯一增加的是屏障,使电路看起来更好,重置门,所以我可以重复使用 17 个量子位。IBM Q 量子计算模拟器只允许使用最多 32 个量子位,随着更多量子位的添加,运行时明显变慢,因此您可以通过重置和重用更少的量子位来提高性能。

结果

下面的直方图是将之前使用的相同图像 0 与所有 10 个数字 0 到 9 进行比较的结果。

作者在 IBM Q 体验中生成的图像。

直方图中只显示了顶部的结果,因此需要进行一些经典的后处理。

  • 0–92.517%
  • 1–91.15%
  • 2–84.619%
  • 3–84.741%
  • 4–90.625%
  • 5–90.491%
  • 6–81.128%
  • 7–86.633%
  • 8–89.783%
  • 9–90.027%

即使进行这个实验需要所有的维数减少,零的测试图像与零的数据最接近。顺便说一下,这条完整线路的运行时间是 4 分 45 秒。

结论

这个实验表明,使用量子计算模拟器对手写数字的图像进行准确分类实际上是可能的。虽然这种电路不能在 NISQ 处理器上运行——由于量子位连接和电路深度问题,即使你有足够的量子位来一次一位地进行比较——但这一结果有望在未来的量子处理器上得到实际应用。

未来的工作

如果我有 15,690 个容错量子位,我会喜欢运行这种算法的逐像素版本。在那之前,我只能等待这个实验的反馈,以确定接下来会有什么样的挑战。

这种算法在发表前面临的一个挑战是,在量子处理器上运行这种算法相对于传统的运行算法是否有任何实际优势。由于它甚至不能在量子处理器上运行,我们将看到这个论点走向何方。

承认

一如既往,感谢 IBM Q Experience 免费使用其量子计算模拟器,该模拟器生成了本文中的电路和直方图图像。感谢量子直觉 ( @explore_quantum )启发我尝试用 MNIST 数据集做量子分类。感谢http://yann.lecun.com/exdb/mnist/为机器学习社区提供了 MNIST 数据集这样的资源。还要特别感谢https://www.kaggle.com/oddrationale/mnist-in-csv#分享 CSV 格式的 MNIST 测试数据集。

量子并行性——量子计算机的魔力从何而来

原文:https://towardsdatascience.com/quantum-parallelism-where-quantum-computers-get-their-mojo-from-66c93bd09855?source=collection_archive---------20-----------------------

量子计算机如何利用量子叠加来同时执行许多计算路径。

图片来自 Pixabay 的 MonikaP

量子计算机是在 20 世纪 80 年代提出的。从那以后,物理学家一直在努力利用自然的力量来满足计算需求。物理上实现量子计算机没有单一的最佳方法;该领域被分割成几个相互竞争的方法,如离子阱,光学晶格,光子量子位,核磁共振等。但是这些不同的方法都致力于在硬件中实现相同的量子计算模型。只要我们正确地使用这个模型,我们就不需要关心它是如何在硬件中实现的。公平地说,量子计算也有不止一种模型,但是在本文中我们将只考虑标准的量子电路模型。

我们把这篇文章分成四个部分。在第一部分,我们将了解什么是单个量子位。然后,我们将会看到多重量子位元是如何被表现的。在第三部分,我们介绍作用于量子位的变换。最后,我们将所有这些放在一起,解释量子计算机如何能够并行执行多条计算路径。

单量子位

经典的计算机按位操作,每一位可以处于 0 或 1 的状态。量子计算机运行在量子位或量子位上。一个量子位不是以 0 或 1 的形式存在,而是以两者的叠加形式存在(还记得薛定谔的猫同时是死的和活的吗?).

让我们用数学来表示。量子位ψ的状态可以写成:

上面的符号被称为 braket 符号,但我们不会在这里讨论它。这个量子位以 0 和 1 两种形式存在,但是如果测量它,我们会得到或者 0 或者 1 而不是两者。我们得到 0 的概率由|α|给出,我们得到 1 的概率由|β|给出。一旦测量完成,量子位就失去了叠加态,继续以 0 或 1 的状态存在。由于许多原因,braket 符号非常有用,但是同样的方程也可以使用矩阵和向量方便地写成:

免责声明: 文章使用了技术上不完全正确的松散批注。例如,上述状态 0 和 1 应该写成基向量|0 >和|1 >。采用松散符号的原因是不幸的事实,即 medium 不允许在文本中使用数学符号,并且将每个实例转换成图像会使文章难以阅读。

多量子位

两个未纠缠的量子位的状态可以表示为它们各自状态的张量积。如果第一量子位具有振幅 ab ,而第二量子位具有振幅 cd,它们的组合状态可以写成:

一些两个量子位的状态不能分解成两个单个量子位的张量积。这样的量子比特被称为纠缠量子比特。纠缠在许多量子算法中扮演着重要的角色,尤其是在量子密码领域。经典物理学中没有量子纠缠的对应物。我们不会在这篇文章中回顾纠缠。

量子变换

如果我们不能用量子比特做任何事情,那就没有乐趣了。但是我们可以——在转变的帮助下。经典计算机借助于逻辑门如“非”、“与”、“或”、“与非”、“或非”、“异或”等对比特进行运算。同样,量子计算机使用量子门对量子位进行操作。由于量子力学的假设,所有在量子位元上执行的操作必须是线性且可逆的。因此,所有的量子门都必须是线性且可逆的。

“非”门是其中最简单的一种。它只是反转 0 和 1。

遍及量子计算的一个门是哈达玛门。它将一个仅作为 0 存在的量子位转换成 0 和 1 ie 的相等叠加。如果我们测量量子位,50%的概率会得到 0,50%的概率会得到 1。正如我们将看到的,这是许多量子算法的一个很好的起点。

量子并行性

现在,有了上述知识,让我们来揭开量子计算机是如何施展魔法的。我们将使用一个处理两位的简单示例来实现这一点。假设给你一个经典函数 f ,它取两位作为输入,返回一位作为输出。

为了在两位的所有四种排列上评估 f ,我们将需要调用 f 四次: f (0,0)、 f (0,1)、 f (1,0)、 f (1,1)。利用量子并行性,我们可以在对f的一次调用中评估所有四个输入。然而,请注意,我们的函数 f 是不可逆的,所有对量子位的操作都必须是可逆的。所以我们首先定义一个可逆版本的 f 如下:

带圆圈的加号表示 XOR 运算。量子预言机 U 接受两个输入 xy 并输出两个值。首先是 x 本身。第二个是值: y XOR f(x) 。很容易看出,当 y=0,第二个输出等于 f(x)

我们将输入ϕ设置为四个两位输入 00、01、10、11 的相等叠加。为此,我们将两个量子位初始化为 0,并对它们应用哈达玛门。这表现为:

接下来,我们通过设置 y=0 将量子函数 U 应用于该状态。这给了我们:

如果我们分离出两个量子位,我们看到第二个输出量子位包含了我们感兴趣的所有四个结果的叠加。因此,我们使用目标函数的单一应用评估了四个输入。

或者更详细但仍然温和的量子计算介绍,请参见 https://arxiv.org/abs/2006.12025的评论论文

讨论

量子并行形成了许多量子算法的核心。然而,我们需要考虑一些重要的警告。虽然输出包含我们感兴趣的结果的叠加,但是我们不能直接读取它们。任何直接形式的测量都会给我们一个结果,其他三个结果都会丢失。因此,需要使用巧妙的技巧来读出我们感兴趣的最终答案,并且这些技巧在计算上可能不是微不足道的。第二,对于我们的例子,准备输入状态很简单,但是对于其他问题可能不是这样。使用量子并行性最终是否会比传统的替代方案更快取决于这些警告。

量子计算机已经在这里了,是我们开始认真对待量子的时候了。越多来自不同领域的人接受它,它就会越快成为现实。

如果你喜欢这篇文章,可以在 上查看我的其他作品,在LinkedInTwitter,查看我的 个人网页 ,或发邮件给我【viraj@berkeley.edu】

“量子至上”

原文:https://towardsdatascience.com/quantum-supremacy-ed0ef5571fff?source=collection_archive---------50-----------------------

奇点研究

去量子还是不去量子?

在这篇文章中,我们将非常冷静和平衡地看待“量子优势”的想法,以及与经典计算机相比,在不久的将来,我们可以从量子计算中获得什么样的性能。寻找量子计算未来应用的很大一部分将是比较量子方法和经过充分研究的经典方法,这些方法有几十年的经验证据支持。在许多情况下,有启发式经典算法开发的各种特殊情况下的问题,将优于量子方法。然而,也有量子方法优于经典方法的情况,决定哪些情况适合量子方法而不是经典方法是一个非常重要的问题。

此外,在某些情况下,当应用一种方法而不是另一种方法时,性能的微小改进可能不值得使用资源。也许这个问题用经典算法解决会更准确,但是量子算法可能更高效并且可能占用更少的资源。也许在另一个问题上,恰恰相反。在某些情况下,资源成本可能意味着牺牲准确性,而在其他情况下,准确性可能是最重要的,例如当涉及到人的生命和健康时。使用一种方法可能需要更多的资源来解决问题,但性能的改善可能意味着预测癌症风险的比率将提高几个百分点,这将挽救数千甚至数百万人的生命。

照片由 Michael Dziedzic 在 Unsplash

与其他方法一样,解决何时使用量子计算的问题是一个持续的问题,其答案会随着时间的推移而变化,这取决于可用的资源、提出的问题、可用的其他方法、准确性的重要性以及我们可用的资源的重要性。下面的文章从方法的有效性方面解决了这个问题:

去量子化还是不去量子化:面向近期量子优化的算法选择

关于定义量子霸权的重要性和意义的真正深度和复杂性的一个很好的简短播客,请参见麻省理工学院技术评论关于谷歌和 IBM“量子霸权”争执的文章。在谷歌发布了一个关于他们宣称的“量子霸权”的简短视频后,我们将在下面讨论这个“世仇”。

硬件限制

另一个考虑因素是硬件限制,这种限制在不久的将来可能不会出现。虽然一些电路算法可能优于当前的经典算法和模型,但是随着电路深度的增加,可以显示 QAOA 渐进地达到正确答案。电路深度是指可以应用的门操作次数,与算法的运行时间有关。由于退相干的限制存在于当前的硬件中,但是随着量子计算硬件的改进,以及随着使用不同材料和硬件几何和拓扑的更多量子计算方法的发现,这种限制可能在近期急剧增加。更高保真度的门操作和量子纠错将最终提供容错量子计算机,它可以无限长时间运行计算。在这种情况下,渐近最优的答案将优于经典的 Goemans-Williamson 方法。

在上面的论文中,显示了即使电路深度为 10,QAOA 方法也可以在某些类的问题上胜过 Goemans-Williamson 方法。因此,很明显,对量子方法的进一步研究是绝对必要的,以确定什么时候量子方法是必要的或有益的,什么时候它们不是……这是一个元优化问题。随着时间的推移,我们将发现自己面临越来越多的这种元优化问题,人类最大的问题将不是如何解决问题,而是如何最有效地解决问题,以及效率的定义是什么是合适的。换句话说,我们必须问这个问题,

"对于一些固定的环境,什么是正确的元目标函数?

可验证性

即使在近期的问题中,也存在这样的情况,当前的量子计算机可以执行经典计算机无法执行的计算,无论我们的经典计算机有多好,它们永远无法执行这些计算。这就带来了可核查性的问题。举个例子,想想机器学习中的经典例子支持向量机。在非常低的维度上,比如说二维数据,支持向量机通常比其他方法表现得更好,这可以在 scikit-learn 的分类器比较中看到。然而,在高维数据中,比如大于 12 维的数据,支持向量机的性能要好得多。

在量子计算机中也可以观察到类似的现象,在经典计算机上可以执行的计算可能是一个案例一个案例地分析哪种方法在给定的问题上表现得更好,但是一旦我们进入经典计算机由于涉及到难以置信的高维希尔伯特空间而无法使用的领域,量子计算机将是必要的。然而,我们遇到了一个有趣的问题,使用经典计算机无法验证结果。有些问题和计算无法在经典计算机上运行,在这些情况下,无法将经典计算机与量子计算机进行比较。一旦谷歌声称“量子至上”在 2019 年末出现,我们就进入了量子计算机正在执行计算的领域,这些计算要么非常困难,要么无法使用经典计算机进行验证。

尽管 IBM 声称已经找到了一种方法,在他们的一台超级计算机上用大约 2.5 天的时间启发式地模拟计算,但谷歌使用的量子计算机花了大约 200 秒。这相当于增加了大约 1000 的时间资源。现在,想象一下在量子计算机上运行一个计算需要 10 天,这是训练机器学习模型时的典型情况。这相当于在经典计算机上进行一次计算,大约需要 30 年才能完成。随着量子计算技术的改进,计算时间的差距只会增加,除非发生令人震惊的事情,复杂性类别崩溃(例如 P=NP),我们可以找到如何有效地将量子算法转化为经典算法。

彼得·肖尔为 QAOA 辩护

数学家 Peter Shor 在量子计算行业因其因数分解算法而闻名,该算法被吹捧为破解 RSA 公钥密码的一种方法,他做了一个关于 QAOA 算法的讲座。在他的演讲中,他为一般量子算法的可用性适应性辩护。他的主张本质上是,(至少是一些)量子算法实际上可能更适用于更广泛的问题,也可能更容易被那些希望应用优化或机器学习方法来解决他们感兴趣的问题的非专家使用。显然,这一论点取决于要解决的问题、可用于该类问题的算法或机器学习选项、这些选项的简单性、它们是否以用户友好的格式在记录良好的代码中实现,以及理解如何有效使用这些方法的最小障碍。在 QAOA 的例子中,当然有很多实现都有很好的文档记录,但是有人可能会说 Goemans-Williamson max cut 算法也是如此。这个论点高度依赖于关于机器学习模型和优化技术的“用户友好的”和“适应性的”的含义。

在奇点研究,我们正致力于让普通人更容易获得量子计算,为非程序员和非数学家提供广泛的交互式 Jupyter 笔记本。我们希望这些想法和应用程序可以广泛测试、访问和使用。我们的目标是实现一般无法访问的研究论文中的应用程序和算法。我们正在使用 Python 和量子计算以及量子机器学习库和软件来创建用户友好的交互式笔记本。我们正试图以一种教育和信息的方式做到这一点,这样随着时间的推移,了解这项技术以及如何以一种有意义的方式使用它将变得更加容易。如果你有问题或者你想成为这个项目的一部分,去看看 Github 或者联系 thesingularity.research@gmail.com,考虑支持 Github 项目。如果你对量子计算如何帮助你感到好奇,我们很乐意咨询并帮助你开始!

量子数学——你准备好吃红色药丸了吗?

原文:https://towardsdatascience.com/quantumic-math-are-you-ready-for-the-red-pill-4860f0ad79bf?source=collection_archive---------25-----------------------

探索哈达玛门的美丽

在这篇文章之后,矩阵不再是从顶部落下的神秘符号,而是你会看到一个穿红色裙子的女人……

…至少关于哈达玛门。

本帖是本书的一部分: 用 Python 动手做量子机器学习

“你吃了蓝色药丸——故事结束了,你在床上醒来,相信你想相信的一切。你吃红色药丸——你呆在仙境,我让你看看兔子洞有多深。”
莫菲斯矩阵

作者弗兰克·齐克特的图片

量子位类似于电子自旋的概念。它处于叠加状态。电子的叠加态由上下态组成,而量子位的叠加态由|0⟩态和|1⟩.态组成

一个流行的叠加概念是系统同时处于不同的状态,除非你测量它。但是,当你看电子的时候,你会发现它不是向上就是向下。你看量子位,不是0就是1。另一个概念是,系统是真正随机的,对初始条件没有敏感的依赖性。但是叠加并不意味着。也不是指。它是不映射到经典概念的状态的组合。

这是你最后的机会。这之后,就没有回头路了。”
莫菲斯矩阵

叠加的基本模型由向量空间给出。向量空间是所有有效量子位状态向量以及你可以对它们执行的操作的集合。我们通过下面的等式知道了量子位状态向量:

在 Python 中,数组[alpha, beta]表示这个向量。

alpha and beta are the probability amplitudes. They are not probabilities. They can be positive or negative. But their squares alpha**2 and beta**2 denote the probabilities.

当我们测量一个量子比特时,它会坍缩到可能的测量值中的任何一个。可能测量的数量决定了这个潜在向量空间的维度。一个量子位有两种可能的测量方式,01。因此,向量空间是二维的。这个向量空间中的所有向量都由两个数字组成。这些是概率振幅α和β。

当我们测量一个量子位时,我们观察它是0还是1。我们知道|0⟩说我们的量子比特在被观察时会产生值0。|1⟩说我们的量子位在被观察时会产生值1。总的来说,|ψ⟩=α|0⟩+β|1⟩说我们的量子位会以α2 的概率产生值0,以β2 的概率产生值1

概率是一个单一的数字,称为标量。我们如何从一个量子位态获得这个标量呢?矢量乘法有一种方法可以产生标量。这叫内积。它是由一个列向量乘以一个行向量得到的。

在之前的帖子中,我们介绍了 Dirac 符号及其“ket”——表示列向量的构造。举个例子,

现在,我们介绍“胸罩”结构(⟨0|).胸罩是一个行向量,例如

内积定义为:

我们可以用内积来获得从一个量子比特态测量某个值的概率幅度。它的平方表示概率。

那么,从|0⟩测量出1的概率有多大呢?让我们构建内积来找出答案:

而且测到0的概率有多大?

这也适用于任意状态向量|ψ⟩=α|0⟩+β|1⟩.衡量1的概率是:

而测得|ψ⟩为0的概率是多少?

太好了!尽管这是非常数学化的,但它说明了我们如何从量子态中获得一个值。用状态向量乘以行向量。通俗地说,“bra-ket”⟨e|ψ⟩表示测量|ψ⟩为 e 的概率幅度,它的平方表示概率。

在上一篇帖子中,我们了解了矩阵乘法。我们知道,当我们将一个矩阵乘以一个向量时,结果是另一个向量:

我们看到 X 门量子算符改变了量子态的振幅。

X-gate 应用于|1⟩:的|0⟩结果

X-gate 应用于|0⟩:的|1⟩结果

在狄拉克符号中,像|a⟩⟨b|那样排列的胸衣和胸罩表示外部产品。外积可以理解为矩阵乘法:

因此,术语|a⟩⟨b|表示矩阵。我们可以用向量来表示矩阵:

通俗地说(仅针对特定的基本情况),“ket-bra”|a⟩⟨b|将你的|b⟩变成了|a⟩.

因此,X-gate 把|1⟩变成了|0⟩(因为|0⟩⟨1| ),把|0⟩变成了|1⟩(因为|1⟩⟨0|).

量子叠加态我们讲了很多。但是每当我们在这种状态下处理量子位时,我们用相应的概率振幅α和β来初始化量子位。但是,如果我们想把一个曾经测量过的量子位放回叠加态呢?

现在,我们有办法做到这一点。对此你怎么看?

根据我们的想法,这意味着我们把|0⟩变成|+⟩,把|1⟩变成|−⟩.

你记得|+⟩和|−⟩吗?我们在这里介绍他们。它们被定义为:

这些状态产生测量01的相同概率。它们位于水平轴。尽管这些状态有相同的概率,但它们是不同的。它们的概率幅度是不同的。如上所述,这个幅度可以是负的。

让我们来看看这个运算符。

这种操作器被称为哈达玛门或 H 门。它让我们远离基本状态向量|0⟩和|1⟩.它将量子位置于平衡的叠加状态。

简而言之,它有矩阵:

Why do we need to distinguish these two states?

量子计算机必须以高精度工作,因为量子算法建立在连续变化参数的精确操作之上。即使是由热量引起的噪音也会破坏计算。

这是有问题的,因为到目前为止我们能够制造的计算机本质上是昂贵的电加热器,其副作用是碰巧执行少量的计算。

我们的计算机以一种依赖于故意丢失某些信息的方式运行。当我们查看and操作符时,如果两个输入值都是1,我们会得到1的输出。在所有其他情况下,我们得到一个0。给定0的输出,我们无法知道输入是什么。

作者弗兰克·齐克特的图片

在执行这种操作的过程中,计算机破坏性地覆盖它的输入。它通过将旧信息推到计算机的热环境中,以物理方式破坏旧信息。它变成熵,表现为热。

量子计算机在极低的温度下运行——低于 1 开尔文或-273°c,量子计算机必须非常节能。不是因为能源是一种宝贵的资源。而是因为任何能量损失都不可避免地使计算机过热。

事实上,进行计算而不丢失信息,因而不产生热量是可能的。这就是所谓的可逆计算。

通过使我们的 h-算符能够区分输入态|0⟩和|1⟩,它变得可逆,因此适合量子计算机。

活动中的哈达玛门

让我们看看 Hadamard 门的运行情况。

作者弗兰克·齐克特的图片

你应该注意到,在 Python [1, 0]中,我们用状态|0⟩初始化我们的量子位。唯一的新东西是我们在位置0(第 13 行)应用于量子位的哈达玛门。

我们可以看到,即使我们用状态|0⟩初始化了量子位,我们对01的测量概率都是 50%。

我们提到了哈达玛门的可逆性。事实上, Hadamard 门会自行反转。

作者弗兰克·齐克特的图片

在这段代码中,我们用|1⟩状态初始化量子位(第 9 行)。我们应用哈达玛门两次。结果是有 100%的机会测量到1。这正是|1⟩州的含义。

Hadamard 门是一种基本的量子门。它在量子计算中无处不在。它将|0⟩的一个量子位转换成|+⟩,将|1⟩的一个量子位转换成|−⟩.它逆转了这些转变。

结论

这篇文章带你深入量子数学的兔子洞。我们探索了两种结构。首先,您可以使用“bra-ket”来获得某个度量的概率幅度。第二,你可以使用“ket-bra”来形成矩阵,并指定如何转换现有的量子位状态。

我们已经学了相当多的数学。令人惊讶的是,不需要混淆视听。事实上,我们在量子计算中使用的数学主要由线性代数组成。这一点也不难。不幸的是,许多关于这个话题的资源都假设你有数学学位。例如,他们没有解释什么是内积,以及你如何计算它。

我的经验告诉我,当我们使用它们时,解释基本的东西并不可耻。对于那些已经很熟悉的人来说,这是一个巩固知识的机会。但是对于不熟悉的人来说,根本就是理解学科的基础。

本帖是本书的一部分: 用 Python 动手做量子机器学习

在这里免费获得前三章。

在 Python 中查询和分析条带数据

原文:https://towardsdatascience.com/query-and-analyze-stripe-data-in-python-e6b4c3dd349?source=collection_archive---------12-----------------------

MRR 和流失计算

来源:https://unsplash.com/photos/ZVprbBmT8QA

Stripe 是一家在线支付公司,提供用于处理支付和业务管理的软件和 API。我喜欢 Stripe 为不同的语言提供不同的 API,这让人们的生活变得轻松了许多。

我主要使用 Stripe Python API。要安装:

pip install --upgrade stripe

也可以做conda install stripe。但是该库的最新版本似乎还没有在 Conda 上提供。我使用的版本是 Stripe 2.55.0。

接下来,您将需要一个 API 密钥来访问条带 API。转到 stripe.com—开发者— API 密钥,然后点击“+创建密钥”获取 API 密钥。

资料来源:stripe.com

stripe.api_key = YOUR_API_KEY
stripe.api_version = "2020-08-27"

然后,您可以定义条带 api_key,并且不要忘记定义 api_version。我使用的是最新版本。如果您使用不同版本的 API,某些功能/数据格式会有所不同。

获取条带数据

很好,现在一切都设置好了,我们可以开始查询条带数据了。这里我写了一个函数来获取数据。

  • 条带 API 包含许多资源(数据集)。在这个函数中,我们可以传入资源的名称来获取相应的数据。例如,获取订阅数据的基本格式是stripe.Subscription.list()。我们在函数中使用了getattr,以便将资源作为参数包含在函数中。
  • 我们使用list API 批量获取资源。
  • 每个 API 调用最多返回 100 个对象。这就是为什么我们使用auto_page_iter()来自动给列表结果分页。

有了这个函数,我们可以简单地调用函数来获取所有时间或定义的时间段的不同资源。例如:

  • 客户数据:stripe_get_data('Customer')
  • 事件数据:stripe_get_data('Event')(仅返回过去 30 天的事件)
  • 发票数据:stripe_get_data('Invoice')
  • 余额交易数据:stripe_get_data('BalanceTransaction')
  • 订阅数据:stripe_get_data('Subscription', start_date=datetime(2020,9,1), end_date=datetime(2020,10,1))(如果没有指定,只返回“活动”和“过期”状态的订阅)。

其他资源可以在 Stripe API 文档中找到。您也可以使用这个函数查询其他资源。

条带计费分析仪表板

stripe Billing Analytics Dashboard 提供了您的帐户的汇总视图,其中提供了许多有用的信息,如 MRR、客户流失等。不幸的是,没有用于条带计费分析仪表板的 API(我实际上联系了支持人员并询问了这个问题)。所以,我不能把仪表板直接放到 Python 上。但是 Stripe 确实就他们如何计算这些指标提供了一些指导。我按照说明,计算了 MRR 和流失率。

资料来源:stripe.com/docs/dashboard

维护、修理和更换

每月经常性收入(MRR)可以被认为是您可以可靠地预期在经常性基础上收到的每月收入总额。这是 SAAS 企业要跟踪的最重要的指标之一,因为它提供了对增长和预测收入的前瞻性衡量…您可以通过对当时收取费用的所有订阅的每月标准化金额求和来计算大致的 MRR。

计算 MRR 的语法如下所示。

  • 首先,我们需要获得所有的订阅数据。这里我设置status="all"来获取所有订阅数据,包括取消的订阅。我们也不能包含此参数来仅获得“活动”和“过期”状态,因为我们在此计算中不使用“已取消”状态。
  • 然后我们得到关于订阅计划的信息,即金额和间隔(年度或月度计划)。
  • 如果有人的计划有折扣,我们会得到有关折扣的信息。
  • 接下来,我们通过标准化年度金额并应用折扣来计算标准化的月度计划金额。
  • 最后,MRR 被计算为那些具有“活动”或“过期”状态的人的标准化每月计划金额的总和。

这个计算是基于条纹文章和一篇博客文章。结果值看起来与仪表板上显示的值略有不同。但是已经很近了。

流失率

流失率的计算方法是,将过去 30 天内流失的用户总数除以 30 天前的活跃用户数,再加上这 30 天内新增的用户数。

下面是计算流失率的代码。

  • 首先,我们计算过去 30 天内的用户数量。我们可以使用事件数据,也可以使用订阅数据,查看在过去 30 天内谁取消了订阅。
  • 其次,我们计算活动订阅或过期订阅的数量。
  • 然后我们可以根据这两个数字计算出流失率。

该计算基于这篇文章中的描述。

现在您知道了如何查询条带数据并计算 MRR 和流失率。希望你喜欢这篇文章。谢谢!

参考

[## 条带 API 参考

Stripe API 的完整参考文档。包括我们的 Python 的代表性代码片段和示例…

stripe.com](https://stripe.com/docs/api)

https://support . stripe . com/questions/billing-analytics-dashboard

在 Python 中查询元数据库数据

原文:https://towardsdatascience.com/query-metabase-data-in-python-ea7f866e6782?source=collection_archive---------13-----------------------

元数据库 rest API 和 Python API

Metabase 是一个开源 BI 工具,您可以在其中存储数据、连接到外部数据源、查询和可视化数据。本文讨论当我们将数据存储在元数据库中时,我们如何在 Python 中查询数据并获得结果。本文的第一部分讨论了如何使用 Metabase rest API 来查询数据。第二部分尝试 Python API。让我们开始吧。

元数据库 rest API

获取会话令牌

要使用元数据库 rest API,我们首先需要获得一个会话令牌。在命令行中键入以下内容:

输出将是您的令牌。然后将这个令牌保存在 Python 脚本或 Jupyter 笔记本中。

token = "YOUR_TOKEN"

或者,您可以在 Python 脚本中运行以下内容来获取令牌:

查询卡片

有两种方法可以查询元数据库数据。第一个选项是从卡中查询数据,或者在 UI 上询问问题。在 UI 中,我们可以直接通过 UI 提出问题并查询数据:

来源:元数据库

一旦我们创建了问题,我们可以检查这个问题是否出现在api/card中:

输出显示了一个字典列表,每个字典对应于用户通过 UI 提出的一个问题。然后我们可以用下面的代码找到卡的 ID 号并得到卡的结果。这基本上是用你在卡中定义的查询(问题)来查询数据库。

直接查询数据库

第二种选择是直接查询数据库,而不是首先定义卡并查询卡。

假设我们想从数据库1中查询表users,并输出 2020-10-15 之后创建的所有行。这里我们需要在 SQL 本地查询中定义这个查询。然后将查询添加到requests.post函数中。由此产生的res.json()是一本字典。读入数据和列。我们可以从res.json()['data']['row']获取所有数据,然后从res.json()['data']['results_metadata']['columns']获取列名。

现在您应该能够在df中看到您的查询结果。

Python API

我还尝试了一个 Python API 元库。类似于我们上面提到的查询卡片部分,这里我们可以用下面的代码查询卡片 1:

from metabasepy import Client, MetabaseTableParsercli = Client(username=username, password=password, base_url=endpoint)cli.authenticate()query_response = cli.cards.query(card_id="1")data_table = MetabaseTableParser.get_table(metabase_response=query_response)df = pd.DataFrame(data_table.__dict__['rows'])
df

我感觉 Python API 不太灵活。但是当然欢迎你去尝试。

结论

在这里,我向您展示了三种从元数据库查询数据的方法:使用元数据库 rest API 从卡中查询,使用元数据库 rest API 通过用户定义的查询直接从数据库中查询,以及使用 Python API 元数据库从卡中查询。

我更喜欢使用 Metabase rest API 通过用户定义的查询直接从数据库中查询。使用这种方法,我们可以在 Python 脚本中包含所有的查询,并且我们确切地知道我们在查询什么。

在元数据库中还有许多其他可用的 API 调用,我们没有介绍。本文中的代码应该是您学习和探索其他 API 函数的良好起点。希望你喜欢它!谢谢!

参考

[## 元数据库/元数据库

本指南应该有助于回答一些关于使用元数据库 REST API 的常见问题。如果您有任何问题,请随时提出…

github.com](https://github.com/metabase/metabase/wiki/Using-the-REST-API) [## 有可能通过 REST API 从元数据库 MBQL / SQL 查询中获取原始数据吗?

有没有一个元数据库 REST API 接受一个 MBQL/SQL 查询并返回原始数据?我可以通过…执行 MBQL 查询

stackoverflow.com](https://stackoverflow.com/questions/50620095/is-it-possible-to-get-raw-data-from-a-metabase-mbql-sql-query-via-the-rest-api)

查询您的 GitHub 帐户的统计数据

原文:https://towardsdatascience.com/query-stats-for-your-github-account-48ad0269aee5?source=collection_archive---------32-----------------------

快速找出您提交和/或审核的拉动式请求的数量!

照片由马库斯·温克勒在 Unsplash 上拍摄

比方说,你想展示一下你今年提交了多少拉取请求,或者你审查了多少。在 GitHub 上手动计数会很困难…但是有一种更容易、更简单、更有效的方法。

先决条件

在 Python3 虚拟环境中,用pip安装requests:

生成访问令牌

在你的 GitHub 账户上,进入“设置”

作者图片

然后,“开发者设置”

作者图片

然后点击“个人访问令牌”,并点击“生成新令牌”

作者图片

向“用户:读取用户”提供访问权限,并生成:

作者图片

将您的令牌导出为环境变量

有许多关于如何做到这一点的博客帖子——为了与我下面提供的脚本一起工作,你应该将其命名为GITSTATS_TOKEN

查询 GitHub 统计数据

要获得您已经提交和审查的 pr 的数量,您可以运行下面的脚本(添加您自己的用户名和您感兴趣的存储库—在本例中,我查询我在pandas-dev/pandas中提交和审查了多少 pr):

您的输出将如下所示:

Merged PRs: 45
Number of reviews: 100

您可以在这里尝试查询:https://developer.github.com/v4/explorer/

参见这里的获得一个带有源代码的存储库。

用 Python 查询 Microsoft Graph API

原文:https://towardsdatascience.com/querying-microsoft-graph-api-with-python-269118e8180c?source=collection_archive---------1-----------------------

使用微软的 REST API 和 Python 请求库获取 Azure 上的 O365 数据

图片来源:周杰伦 K 上像素

介绍

为了向用户提供丰富的数据驱动体验,您可能需要将定制的应用程序与您组织的数据集成在一起。Microsoft Graph 是一个 REST API,它提供了与 Office 365 中的数据进行交互的能力。在这篇文章中,我将展示如何使用 python 连接到你的 Azure Active Directory (Azure AD)。主要步骤是在 Azure 上设置一个企业应用程序,并编写代码来处理数据。

点击此处获取此服务

创建企业应用程序

使用全局管理员帐户登录到 Azure 。

在左侧窗格中,导航至>> Azure Active Directory > >企业应用> >+新应用> >添加您自己的应用(您正在开发的应用)> >+新注册

成功注册应用程序后,您将看到以下内容;

快照

在右边,用下面的模板添加一个重定向 URL

[https://login.microsoftonline.com/<DIRECTORY (tenant) ID>/oauth2/token](https://login.microsoftonline.com/0ce08dd4-2728-49fa-8e71-acb4a3c3d145/oauth2/token')

证书&秘密

创建一个客户端密码并记录下来。

API —权限

默认情况下,Microsoft Graph 用户。添加了读取权限。为了进行演示,我将查询 O365 Planner(相当于 Trello)数据。以下是许可授予2 号 MFA 的账户。更多信息见文档

获取数据

让我们深入研究 python 脚本;

*#import libraries*
import requests
import json
import pandas as pd
import pyodbc

创建在请求中使用的令牌数据字典

app_id = 'xxxxxx' #Application Id - on the azure app overview page
client_secret = 'xxxxx' #Use the redirect URL to create a token url
token_url = '[https://login.microsoftonline.com/<DIRECTORY (tenant) ID>/oauth2/token](https://login.microsoftonline.com/0ce08dd4-2728-49fa-8e71-acb4a3c3d145/oauth2/token')'token_data = {
 ‘grant_type’: ‘password’,
 ‘client_id’: app_id,
 ‘client_secret’: client_secret,
 ‘resource’: ‘[https://graph.microsoft.com'](https://graph.microsoft.com'),
 ‘scope’:’[https://graph.microsoft.com'](https://graph.microsoft.com'),
 ‘username’:’[john.doe@companyxxx.com](mailto:helpdesk@centum.co.ke)’, #Account with no 2MFA
 ‘password’:’Happy2020’,
}token_r = requests.post(token_url, data=token_data)
token = token_r.json().get(‘access_token’)

测试令牌

token_r.content

在 Office 365 Planner 上创建一个计划,并创建一个组字典,如下所示:

#groups list
group_ids = {‘plannergroup01’:’xxxx-xxx–xxx-xxxx–xx',’test’:’xxxx-xxx–xxx-xxxx–xx’}#define required lists
userId,displayName,mailAddress,plans_data,planId,PlanGrpOwnerId,PlanTitle,PlanCreatedBy,bucketId,bucketName,bucketPlanId,taskId,taskPlanId,taskBucketId,taskName,taskPercentComplete,taskStartDateTime,taskDueDateTime,taskCompleteDateTime,taskIdAssignment,taskUserIdAssignment = [],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]

用户查询:

# Use the token using microsoft graph endpoints
users_url = ‘[https://graph.microsoft.com/v1.0/users?$top=500'](https://graph.microsoft.com/v1.0/users?$top=500')

headers = {
 ‘Authorization’: ‘Bearer {}’.format(token)
}user_response_data = json.loads(requests.get(users_url, headers=headers).text)
# user_response_data[‘[@odata](http://twitter.com/odata).nextLink’]#initial user data
#get all users
for user in user_response_data[‘value’]:
 userId.append(user[‘id’])
 displayName.append(user[‘displayName’])
 mailAddress.append(user[‘userPrincipalName’])

users_dict = {‘userId’:userId,’displayName’:displayName,’mailAddress’:mailAddress}
users_df = pd.DataFrame(data=users_dict)#additional user query for paging
while ‘[@odata](http://twitter.com/odata).nextLink’ in user_response_data:
 user_response_data = json.loads(requests.get(users_url, headers=headers).text)
 if ‘[@odata](http://twitter.com/odata).nextLink’ in user_response_data: 
 users_url = user_response_data[‘[@odata](http://twitter.com/odata).nextLink’]

 for user in user_response_data[‘value’]:
 userId.append(user[‘id’])
 displayName.append(user[‘displayName’])
 mailAddress.append(user[‘userPrincipalName’])users_dict = {‘userId’:userId,’displayName’:displayName,’mailAddress’:mailAddress}
users_df = pd.DataFrame(data=users_dict)
users_df.head()

计划员计划查询:

#get all plansfor key, value in group_ids.items():

 plans_url = ‘[https://graph.microsoft.com/v1.0/groups/'+](https://graph.microsoft.com/v1.0/groups/'+) value +’/planner/plans?$top=500' 
 plans_response_data = json.loads(requests.get(plans_url, headers=headers).text)for plan in plans_response_data[‘value’]:
 planId.append(plan[‘id’])
 PlanGrpOwnerId.append(plan[‘owner’])
 PlanTitle.append(plan[‘title’])
 PlanCreatedBy.append(plan[‘createdBy’][‘user’][‘id’])

plans_dict = {‘planId’:planId,’PlanGrpOwnerId’:PlanGrpOwnerId,’PlanTitle’:PlanTitle,’PlanCreatedBy’:PlanCreatedBy}
plans_df = pd.DataFrame(data=plans_dict)

计划员时段查询:

#get all buckets
for plan_id in planId:
 buckets_url = ‘[https://graph.microsoft.com/v1.0/planner/plans/'+](https://graph.microsoft.com/v1.0/planner/plans/'+) plan_id +’/buckets’
 buckets_response_data = json.loads(requests.get(buckets_url, headers=headers).text)

 for bucket in buckets_response_data[‘value’]:
 bucketId.append(bucket[‘id’])
 bucketName.append(bucket[‘name’])
 bucketPlanId.append(bucket[‘planId’]) 

bucket_dict = {‘bucketId’:bucketId,’bucketName’:bucketName,’bucketPlanId’:bucketPlanId} 
bucket_df = pd.DataFrame(data=bucket_dict)

计划员任务查询:

#get all tasksfor plan_id in planId:
 tasks_url = ‘[https://graph.microsoft.com/v1.0/planner/plans/'+](https://graph.microsoft.com/v1.0/planner/plans/'+) plan_id +’/tasks’
 tasks_response_data = json.loads(requests.get(tasks_url, headers=headers).text)

 for task in tasks_response_data[‘value’]:
 taskId.append(task[‘id’])
 taskPlanId.append(task[‘planId’])
 taskBucketId.append(task[‘bucketId’])
 taskName.append(task[‘title’])
 taskPercentComplete.append(str(task[‘percentComplete’]))
 if task[‘startDateTime’] is not None:
 taskStartDateTime.append(datetime.strptime(task[‘startDateTime’], ‘%Y-%m-%dT%H:%M:%SZ’))
 else:
 taskStartDateTime.append(0)
 if task[‘dueDateTime’] is not None:
 taskDueDateTime.append(datetime.strptime(task[‘dueDateTime’], ‘%Y-%m-%dT%H:%M:%SZ’))
 else:
 taskDueDateTime.append(0)
# if task[‘completedDateTime’] is not None:
# complete_time = task[‘completedDateTime’].replace(task[‘completedDateTime’].split(‘:’)[-1],’00Z’)
# taskCompleteDateTime.append(datetime.strptime(complete_time, ‘%Y-%m-%dT%H:%M:%S%z’))
# else:
# taskCompleteDateTime.append(str(0))

 for assignment in task[‘assignments’]:
 taskIdAssignment.append(task[‘id’]) 
 taskUserIdAssignment.append(assignment)tasks_dict = {‘taskId’:taskId,’taskPlanId’:taskPlanId,’taskBucketId’:taskBucketId,’taskName’:taskName,’taskPercentComplete’:taskPercentComplete,’taskStartDateTime’:taskStartDateTime,’taskDueDateTime’:taskDueDateTime,’taskCompleteDateTime’:0} 
task_df = pd.DataFrame(data=tasks_dict)

将用户数据插入数据库:

下面的函数可以对上面创建的所有数据框进行复制。就我而言,我使用的是 SQL Server 2017。

server = 'SERVERNAME\INSTANCEID,64346'
database = 'GRAPHAPI' 
username = 'uname' 
password = 'Happy2020'def insert_user_db(users_df,server,database,username,password):#Create a connection string
cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
    cursor = cnxn.cursor()

 for index in range(users_df.shape[0]): 
 #insert into table 
 try:
 insert_query = “INSERT INTO GRAPHAPI.dbo.[DIM_User] ([userId],[displayName],[mailAddress]) VALUES (?,?,?)”
 cursor.execute(insert_query,users_df[‘userId’][index],users_df[‘displayName’][index],users_df[‘mailAddress’][index])except:cnxn.rollback()finally:cnxn.commit()
 cnxn.close()#Call the function
insert_user_db(users_df,server,database,username,password)

结论

随着微软套件服务的广泛使用,对于图形 API 请求,还有许多其他应用场景。如此巨大的数据量为组织提供了商业智能,并使开发人员能够构建数据驱动的应用程序。

喜欢你读的书吗?关注我上LinkedInMedium

使用 Python 和熊猫下载像老板一样的 Tableau 交叉表

原文:https://towardsdatascience.com/querying-tableau-view-data-as-a-crosstab-using-python-and-pandas-ca71a4be5be0?source=collection_archive---------11-----------------------

TABLEAU REST API: TABLEAU-API-LIB 教程

如何下载 Tableau 视图数据而不丢失原始的表结构

有时你只需要行和列。(照片由尼克·希利尔在 Unsplash 拍摄)

在的另一篇文章中,我们介绍了如何像老板一样查询 Tableau 视图数据,我收到了一个人的反馈,他们想往兔子洞的更深处滚。

这就引出了本文,在本文中,我们将演示如何在 Tableau 中下载表(交叉表)的视图数据,并重建该数据在 Tableau 中的形状。

本教程介绍了如何使用 Python tableau-api-lib 包,并且是如何像使用小桶一样使用 tableau 服务器的系列教程的一部分,让您能够控制 Tableau 服务器的 REST API。

这些教程假设您已经安装了 Python 3。如果您还没有 Python 3,这将帮助您入门:安装 Python 的指南。

搭建舞台

假设您在 Tableau 中有一个交叉表,出于某种原因您需要下载它。如果你用 Tableau REST API 做这件事,你会得到你想要的数据…只是不一定是你需要的格式。

主要问题是,当您下载视图数据时,您不会以“数据透视表”或交叉表的形式获得数据。您将拥有一个数据库格式的原始表,其中所有维度都显示为列。

让我们看看如何下载数据,并把它重新做成您想要的表格或交叉表。

下面快速浏览一下 Tableau 视图,我们将下载它的数据:

我们希望下载它并保留它的行/列结构。

步骤 1:确保你已经安装了 tableau-api-lib

即使你是这些教程的专家,帮自己一个忙,下载最新版本的库。

pip install --upgrade tableau-api-lib

不熟悉 Python 这个东西?别担心,你会很快明白的。遵循本入门教程。该教程将引导您使用 tableau-api-lib 连接到 Tableau 服务器。

步骤 2:进入 Tableau 服务器环境

使用下面的代码作为连接到您的服务器的模板。在接下来的步骤中,我们将使用一次性代码行来构建这个样板文件。在本文的最后,您会发现一个合并的代码块,为了方便起见,您可以复制/粘贴它。

from tableau_api_lib import TableauServerConnection
from tableau_api_lib.utils import queryingtableau_server_config = {
        'my_env': {
                'server': '[https://YourTableauServer.com'](https://tableaupoc.interworks.com%27/),
                'api_version': '<YOUR_API_VERSION>',
                'username': '<YOUR_USERNAME>',
                'password': '<YOUR_PASSWORD>',
                'site_name': '<YOUR_SITE_NAME>',
                'site_url': '<YOUR_SITE_CONTENT_URL>'
        }
}conn = TableauServerConnection(tableau_server_config, env='my_env')
conn.sign_in()

有趣的事实:你也可以使用个人访问令牌,假设你在 Tableau Server 2019.4 或更新版本上。如果你对访问令牌很感兴趣,可以看看我的文章,了解如何使用它们的细节。

第三步:找到你要找的机器人

我们想要查询一个视图,所以首先我们需要获取所需视图的 ID 值。

site_views_df = querying.get_views_dataframe(conn)

下面是我生成的数据帧的样子:

这是一个熊猫的数据框架,包含了我们所有观点的细节。

如果我们愿意,我们可以手动搜索并复制/粘贴相关的“id”值。但是,如果您在一个大的 Tableau 环境中工作,那么您可能会有许多同名的视图。

出于这个原因,让我们以正确的方式来做这件事,并在我们的“工作簿”列中打开细节,这样我们就可以根据工作簿名称进行过滤,以确保我们确实得到了我们正在寻找的 droid。

步骤 4:提取工作簿的详细信息,这样我们可以缩小范围

使用一个方便的 tableau-api-lib 函数,我们可以很容易地解开工作簿的细节。通过参考文章末尾提供的完整代码,可以看到这个函数是如何导入的。

site_views_detailed_df = flatten_dict_column(
site_views_df, keys=['name', 'id'], col_name='workbook')

现在,我们可以快速验证生成的数据帧是否包含工作簿的详细信息:

存在两个新列:“工作簿名称”和“工作簿标识”

现在,我们可以轻松地过滤我们关心的工作簿名称。在我的例子中,工作簿被命名为“query_view_as_crosstab ”,我想下载的视图被命名为“Visual C”。为了重申之前的观点,我们希望对工作簿进行过滤,因为视图名称在整个 Tableau 站点中很可能不是唯一的,但是在工作簿中它们总是唯一的。

relevant_views_df = site_views_detailed_df[site_views_detailed_df['workbook_name'] == 'query_view_as_crosstab']

第 5 步:查询视图数据

既然我们已经将我们的视图隔离到我们感兴趣的工作簿,让我们获得目标视图的视图 ID。

visual_c_id = relevant_views_df[relevant_views_df['name'] == 'Visual C']['id'].to_list()[0]

有了视图 ID,我们现在可以查询视图数据:

view_data_raw = querying.get_view_data_dataframe(conn, view_id=visual_c_id)

tableau-api-lib 查询函数' get_view_data_dataframe()'返回一个 Pandas DataFrame,我的是这样的:

这是一个好的开始,但是我们需要透视这些数据,以便“子类别”定义我们的行,“订单日期年”定义我们的列,并且“销售”填充透视表的值。

步骤 6:旋转数据框以生成所需的表格

在我们执行旋转表的最后一步之前,让我们快速地对数据应用一个可选的改进:将奇怪命名的“订单日期年份”列重命名为“年份”。

view_data_raw.rename(columns={'Year of Order Date': 'Year'}, inplace=True)

啊,这样好多了。现在让我们结束这一切吧!

view_data_raw.pivot_table(values=['Sales'], index=['Sub-Category'], columns=['Year'])

在我们结束今天的工作之前,让我们快速剖析一下这最后一点代码。Pandas DataFrame 'pivot_table()'函数为我们提供了一种重新定义数据结构的简单方法。

“值”参数定义了什么将填充表格的值,而“索引”值定义了行,“列”定义了…列!

这是我们现在的样子:

现在我们有了一张和最初在 Tableau 中看到的一样的桌子。

这是一个总结!

合并代码

使用 GitHub gist 作为修改我们在本教程中介绍的步骤的起点,供您自己使用。

在“数据科学家”工作面试中要问的问题

原文:https://towardsdatascience.com/questions-to-ask-during-your-data-scientist-job-interviews-6df7ccd839d5?source=collection_archive---------55-----------------------

这是一个双向过程,不要错过你的机会

不溅

工作面试是一个双向过程。鉴于当前市场上平均任期较短,数据科学工作尤其如此。面试不仅是回答问题,也是提问。

不管这是你唯一的面试机会,还是你手中的众多机会之一,在面试中提问可以帮助你做出更好的决定,可以帮助你给面试官留下更深刻的印象,从而抓住你的机会。如果你完全不提问,面试官往往会有一种你不太投入的印象,或者你对这份工作太绝望了。所以,你要时刻准备几个问题!

过去几年我一直在面试很多应聘者,我相信大多数人问的问题都不够多。以下是我可能建议的一系列问题。希望其中一些对你下次面试有用。

有一点要记住:这些问题都是你应该了解雇主的事情;但是有些问题你应该间接问。哪些问题属于这一类取决于你的面试官、公司和个人风格。常识和经验仍然很重要。

策略相关问题:

  • 有没有针对数据科学、机器学习和高级分析的战略?
  • 战略实施的高级路线图是什么?
  • 数据科学团队的长期愿景(例如 3 年计划)是什么?
  • 近期的短期计划是什么(例如 6-12 个月)?目前正在进行的主要计划是什么?
  • 短期和长期计划是否与具体的美元价值和硬性的最终交付成果相关联或联系在一起?
  • 数据科学团队的主要职责是什么?
  • 数据科学团队目前面临的最大挑战是什么?

组织相关问题:

  • 组织结构图上的团队结构是怎样的?在项目可交付性、绩效等方面是否有其他报告结构??
  • 团队成员的背景一般是怎样的?比如学历,技术技能,行业经验或者相关?
  • 公司内部有针对数据科学家的社区或行会吗?
  • 数据科学家有什么职业发展路径吗?有人成功走过旅程吗?
  • 有培训机会或预算吗?在团队中是如何运作的?

技术相关问题:

  • 编码和技术开发的环境是什么?有没有可以使用的平台或者只有笔记本电脑?
  • 在技术和工具方面有什么偏好或限制吗?获得我想使用但不包括在技术堆栈中的特定工具、包或库的过程是什么?
  • 团队常用的语言和工具有哪些?熟练程度如何?
  • 如果我有任何技术问题,可以向技术负责人或专家寻求建议,还是只向我的经理寻求建议?

数据相关问题:

  • 有数据目录可以搜索吗,或者有元数据可以用于 ETL 吗?
  • 通常用于构建和训练模型的数据在哪里?这些数据是位于孤岛中还是集中的数据平台/湖/仓库中?
  • 结构上的团队(例如,数据工程、开发运维以及软件开发)在哪里?他们如何与数据科学家合作?
  • 你能给我一个项目团队的例子吗,比如他们是谁,他们做什么,从项目开始到结束的过程是什么?
  • 公司内部跨团队、跨部门的协作工作流程和文化是怎样的?

项目相关问题:

  • 有没有已经投入生产的数据科学项目?
  • 生命周期管理的操作和监控模型是什么?
  • 是否有现有的数据治理框架可以遵循或参考?是否有任何分析建模治理流程和指南?
  • 数据分析、商业智能或报告团队在哪里?数据科学团队和这些团队的边界或关系是什么?
  • 数据科学团队提供什么服务或产品?未来有没有优化工作的计划?

团队合作相关问题:

  • 数据科学团队与其他部门或团队合作的运营模式是什么?
  • 项目是由业务专家发起或请求的,还是来自数据科学团队本身?
  • 公司内部是否有数据科学团队需要关注或仅限于的特定业务领域?
  • 在交付模式下,我将花费在项目上的平均时间百分比是多少?
  • 如果我有一些商业发展机会的想法,我是否有时间将它理想化并证明其价值,以便将其推进到项目管道中?
  • 数据科学团队成员之间如何分配项目?

以上这些问题仅供大家参考。有些问题在面试中已经被提到了。虽然我很想给你做一份小抄,但是不会有现成的小抄适合你去某个公司面试。你需要做好功课,迅速选择要问的问题。尽量不要问一个你可以通过谷歌搜索公司网站或一些文章来回答的问题。

“祝你在数据科学家的面试中好运,别忘了提问!”

这篇文章在 LinkedIn 帖子 上也有。

在数据科学面试中应该问的问题

原文:https://towardsdatascience.com/questions-you-should-ask-in-a-data-science-interview-379ec068bbd2?source=collection_archive---------21-----------------------

询问关于人工智能阶梯的问题

如果你从未做过数据科学家,并试图进入该领域,可能很难判断一个机会是好是坏。这是一个问各种问题的指南,可以帮助你找到答案。

人工智能阶梯

罗布·托马斯(IBM 数据和沃森人工智能总经理)将采用人工智能的路径定义为人工智能阶梯,它包含 4 个不同的部分。如果要在一个组织中恰当地采用人工智能,梯子上的每一步都必须在下一步之前完成。这个阶梯代表了 AI 所依赖的信息架构。我相信你的问题应该围绕人工智能阶梯的每个部分,因为理解阶梯(以及公司在阶梯上的位置)对于理解你可能在公司做什么是至关重要的。这里有一个人工智能阶梯的概述和一些你可以问的非威胁性问题来衡量这一点。

https://www.ibm.com/downloads/cas/3QLWB4QK

收集

没有数据就没有数据科学!有趣的是,公司并不总是拥有大量简单且可访问的数据,但仍在寻求雇佣数据科学家。我认为他们沉浸在人工智能的兴奋中,忘记了基础是数据。要通过第一关,公司必须拥有易于访问的数据。

几年前,我申请了一家大宗商品交易公司的数据科学职位,当时我还年轻,对数据科学还不熟悉,我问了一个问题,一开口就觉得很愚蠢——“你们都有什么样的数据?”这实际上是一个非常好的问题。事实证明,这家大约有 40 人的公司处理的是保存在一个位置的许多 excel 文件,没有版本控制,没有编程,每个表格都需要根据其他表格的变化频繁更新。我不确定有多少公司是这样运作的,但我确信即使是最没有经验的人读到这里也会担心。这家公司确实存在与数据相关的问题,但这并不意味着他们现在需要一名数据科学家。我认为很大一部分问题是数据科学家被视为可以解决任何数据或技术问题的独角兽,但他们通常不是公司解决数据相关问题所需的第一人。毕竟,如果没有道路,你就不会想要一辆汽车。

对于一家希望更好地利用数据的公司来说,首要任务是建立一个集中的事实来源。你实际上应该问“真理是否有一个中心来源?”或者“有数据湖吗?”这将帮助您确定该公司是否拥有开始从事数据科学所需的数据。在我前面提到的商品交易公司的例子中,答案是否定的。即使有,你可能还是想问一些额外的问题来确定你不负责保证数据的存在。我建议也问“有数据库管理员吗?”回答其中一个问题会让你更好地了解公司希望你做什么。在某些情况下,这些问题是没有用的。如果你在谷歌面试,问他们“有数据库管理员吗?”你会得到一些有趣的表情。如果你问一家咨询公司这个问题,它也可能没有意义,因为这个答案因公司的特定客户而异。

组织

一旦明确了数据的存在和可访问性,接下来您需要询问的是数据质量和组织。人工智能依赖于大量干净的数据,询问关于数据组织的问题不仅可以帮助你确定公司是否准备好进行机器学习,还可以帮助你确定公司是否准备好进行基础分析。简单分析是最容易实现的,如果你不确定公司有足够干净的数据,那么你可能会陷入困境。虽然您可能必须完成许多涉及数据清理和组织的任务,但其中一些应该由其他人来处理。像“有数据管家吗?”将帮助您确定是否要执行大量与数据科学无关的数据相关任务,如治理、保护和法规遵从性。

分析

这是你应该完全参与的人工智能阶梯的一步。它有有趣的 ML 内容,可能最初会引起你对数据科学的兴趣。在这一部分,我会问你将建立什么样的模型,我会让你的主题知识发光。你对公司业务的兴奋和好奇在这里很重要。虽然我建议你问的其他问题是为了让你了解公司,但你在这里问的问题实际上是为了让公司了解你。如果你能问一些有趣的问题,或者对公司的流程有新的想法,你就是最棒的。不幸的是,这是我在这一部分能给你的最具体的建议,因为最佳问题因公司而异。

鼓舞

最后,在现有的过程和软件中注入你的工作是很重要的,没有注入的步骤,你的工作将永远不会被使用,或者只是停留在报告中。为了确定你可能参与其中,我会问“有机器学习工程师吗?”机器学习工程师协助你的模型生产化。如果没有人做这件事,那么这通常是数据科学家的责任。这对你的工作来说可能是也可能不是一件有趣的事情,但是它绝对需要你额外的技能。另一个问题是“你如何将模型投入生产?”或者“模型是如何部署的?”最后,在这一部分,我建议问“如何监控模型?”在许多情况下,受监控和可解释的人工智能不仅在信任方面,而且在法律合规性方面都至关重要。

结论

总的来说,你可能已经注意到询问公司的其他数据工作是一个好主意。从“数据团队中还有哪些职位”开始是一个好问题,但如果你没有得到一个直截了当的答案,我会深入问一些更具体的问题,以帮助你找出该公司在人工智能阶梯上的位置。一家公司在阶梯上的位置和其他与数据相关的角色为你提供了最好的线索,让你知道你需要什么样的技能以及你将做什么。记住面试应该是双向的——你给一家公司留下多少印象,它就应该给你多少印象。

在数据科学面试中你应该问他们的问题。

原文:https://towardsdatascience.com/questions-you-should-ask-them-in-a-data-science-interview-1288c754ca51?source=collection_archive---------6-----------------------

如何减少获得数据科学工作时的意外…

Pixabay 许可证

使用关键字“数据科学采访”在 Medium 上进行快速搜索,可以找到数百篇 Medium 文章,帮助引导读者了解涵盖的概念,甚至是特斯拉、沃尔玛、Twitter、苹果、AWS 等具体公司的采访。

W 更多的文章是关于你作为受访者如何确定公司的数据科学环境、期望,以及你是否能在这家公司成长以避免噩梦般的工作。

不是每个人都会在像亚马逊这样的大公司找到数据科学的工作,你可能不得不在一些数据科学刚刚起步的公司工作,但你应该对你在那里工作期间的预期有一个很好的想法,并避免任何令人讨厌的意外。

仅通过 3 个主题,你应该能够辨别这是一份噩梦般的工作,还是你将在数据科学家的职业道路上获得支持。有些人最不希望的事情就是开始一份工作,然后离开几个月。同样,这并不意味着每份工作都很容易,你的工作应该对你有挑战性,但是是以一种好的方式。如果你喜欢真正艰难的挑战,包括改变一些公司的遗留系统/流程和官僚作风,这很好,但至少你知道自己将面临什么。

主题一:数据

我首先问的一些问题是关于数据的。作为一名数据科学家,你显然需要争论、探索、转换数据,并为你的模型提供数据。

  1. 您的数据是如何存储的?您的数据有多大?如果你的数据都在一个数据湖中,你需要创建表格,现在你要知道你也是数据库专家,而不是数据科学家。确保您进入的至少是一个已经设置好的数据库。有时,公司可能正在从数据库中迁移数据,这需要一些时间。在我工作的一家初创公司,他们在不到 3 个月的时间内将全部数据从 Redshift 迁移到 Snowflake,而我工作的一家大型国际公司需要一年多的时间。
  2. 它多久更新一次,高层次的更新如何?我需要管理数据管道吗?(如果处理大量数据,他们应该有一名数据工程师,如果没有,你应该身兼两职)。除了您之外,应该有其他人来确保数据库得到维护。
  3. 数据的数据来源有哪些?您会惊讶于一些数据是如何更新的,甚至可能是通过 Excel 上传的。理想情况下,如果你有一个产品,它会通过一个 API 或者来自一个 web 应用程序的日志。我曾在一家公司工作,那里的数据有很多来源:调查表、短信数据、电子邮件数据、CRM、API、excel 上传。确保您对数据来源的深度和多样性有所了解。

主题二:你的经理。

这个主题非常重要,也将是较长段落的主题。经理真的是你在一家公司获得成功的大门,如果你有一个糟糕的经理,但却热爱这家公司,那就更难驾驭了。我读过一本名为定位:你的思想之战的书,主要是关于市场营销和品牌形象,然而;有一段谈到了经理们是如何真正成为公司品牌形象培育的一部分的。他们会给你一些符合你兴趣的项目,或者给你一个你不知道你会喜欢但最终会喜欢的项目,因为他们理解你。一个好的经理希望你成功。我有过四个经理,让我告诉你,这是一种非常不同的体验:一个经理看到了你的价值,希望你超越,即使这意味着有时比他们自己更突出;另一个经理看到了你的价值,感到害怕,并试图阻止你获得任何项目。然而,也要小心那些相信“雇用比你聪明的人”的经理,他们把这变成了让他们的直接下属做所有艰苦的工作,而经理只是袖手旁观。确保你的经理参与其中,希望你表现出色。没有什么比经理对自己在公司扮演的角色不感兴趣更糟糕的了。

  1. 你的主管的背景和头衔是什么?理想情况下,你的主管应该和你说同一种语言,尽管你的头衔越高,比如说经理、总监、副总裁或首席数据官,有时你会向更懂业务的人汇报。初创公司时,我所在的首席数据官会自己从数据库中查询,也会使用 Python。管理层理解数据和策略,但他们可能不知道为什么你的支持向量模型比你的逻辑回归模型差。如果你是一名初级甚至有经验的数据科学家,你可能更希望你的主管能指导你。这也有助于当一个项目遇到障碍时,他们将迅速彻底地了解是什么导致了延迟,并可以将信息传达给上层管理人员。
  2. 他们以前管理过别人吗?注意,这不是人们天生就应该偏向的东西。我以前遇到过一个经理,他从来没有管理过,花时间阅读关于管理的书籍,真正关心我的成长和发展,没有微观管理,而我曾经向一个有大量管理经验的经理汇报过,但从来没有管理过分析人员,不知道如何最好地利用我的技能。然而,这两个人之前都没有管理过数据科学家,我之前说过,当你能和你的主管说同一种语言时,会容易得多,尤其是在入门级的职位上。
  3. 他们有多少直接下属,他们是谁?我不知道是否有向每位经理汇报多少名直接下属的经验法则,但经理应该能够管理每位直接下属的每周考核。这也有助于了解其他下属是谁,从而了解你的经理需要应付的工作量有多大。一个管理人员指挥一群数据科学家或分析师,与管理一个由通信、运营、财务等主题专家组成的更广泛的团队是非常不同的。与管理分析师团队的经理相比,第二个经理将不得不在团队中进行更多的上下文切换。然而,第一个经理可能在每个部门都有一名分析师,他必须理解与每个部门相关的数据中的细微差别,但他们不必像第二个经理知道得那么多,因为分析师是每个部门的中间人,每个人都将共享类似的工具,例如 SQL 和 Python 或 r。
  4. 经理给团队带来了哪些直接影响?任何一个上层管理头衔都会让你瞬间联想到会议。所以,有时很难看到直接的影响,除非你和那个人在一起开会。然而,管理一个技术团队,他们应该为你的团队产生/促进过程、项目、数据产品、模型、案例研究、网络研讨会等等。就像我之前提到的那本书一样,经理不仅是帮助你树立品牌形象的重要角色,也是代表你团队品牌形象的重要角色。他们可能自己不做分析,但他们应该帮助提供一个环境,在这个环境中,通过投资于正确的工具、人员和创建那些过程,可以执行前面提到的所有事情。
  5. 他们心目中的团队和你的路线图是什么?这与之前关于经理如何为团队生态系统做出贡献的问题相结合。然而,有时经理从零开始构建团队,并且仍然定义其中的一些过程;他们至少应该有一个最终目标的路线图。如果经理不能清晰地描述这个路线图,即使有很多东西需要维护,这也是一个巨大的危险信号。有时候,一个经理继承了遗留系统,并试图将它们优化或移植到一个新的世界,但是他或她的团队为什么关注 X,以及你的角色如何在其中发挥作用,都应该有一个清晰的路线图。这个问题会引出现在的障碍是什么,以及他们如何看待他们的团队为公司增加价值。
  6. 你职业生涯的路线图是什么?公司有年度或半年的评估吗?在我所在的一家公司,你的奖金与你的年度评估挂钩,我喜欢这一点,因为这将迫使你的经理认识到你的弱点和优势,并为此奖励你。这也有助于保持记录,使促销更加顺畅。与另一家公司相比,我在他们那里有六个月的试用期,之后就没有年度评估了。后者看起来更像是公司关心他们自己,而不是员工。不是每个公司都有明确的晋升计划,包括日期和期望(这是理想的),但应该有某种对职业发展的强调,无论是学费报销、职业发展课程、奖金、加薪等。

主题 3:协作/团队环境。

  1. 你们公司有代码审查吗?过程是怎样的?如果你与代码、管道、模型、分析等一起工作,绝对应该有一个代码审查。我有两种类型的经历,一种是代码评审被大量实现,另一种是我实现代码评审过程的公司。两家公司都有一个 Slack 频道,当代码被推向生产时,我们会收到警报。如果一家公司将代码从开发→阶段化→产品化,那么这是一个很好的迹象,表明他们知道他们在做什么。
  2. 我会使用哪些类型的工具?有工具太多和工具太少的平衡。至少对于一个分析数据的团队来说,应该有一个数据库(Redshift,Postgres,Snowflake 等),一个面向非数据分析股东的可视化工具,也称为商业智能工具(Sisense,Looker,Domo 等)——这不同于数据人在探索性数据分析期间使用开源可视化数据,一个版本控制(Github,Gitlab),用于软件构建的容器(如 Docker),管道的数据管理(Airflow 或 Luigi),通信工具(email,Slack,Zoom,JIRA 等)。
  3. 我是否与多名数据科学家或分析师合作,还是只有我一个人?这个问题非常重要,尤其是如果你是一名初级员工,因为你不仅希望你的上司和你说同样的语言,还希望有其他人可以让你学习。你可能会专注于不同的部门/利益相关者/项目,但事实上,拥有类似技能的其他人是令人欣慰的,特别是如果你的团队相信交叉培训,并留出时间来合作,分享项目成果,并一起回顾过去。
  4. 有主题专家吗?这非常非常重要。我怎么强调都不为过。是的,作为一名数据科学家,您可以构建一个具有高准确性、高 AUC 分数或 F1 分数的模型,但它是否可用/可实施?重要的是,数据科学家要有尽可能多的数据背景,无论是他们的背景还是对主题专家的访问。在一天结束时,我们为公司或利益相关者建立模型。此外,就训练时间/金钱而言,一个复杂的模型可以被一个更简单、更容易解释的模型击败,比如逻辑回归,只是因为使用了一些伟大的特征工程。
  5. 文化和工作生活平衡是什么样的?这个问题并不专门针对数据科学团队,但了解以下情况很重要:不仅是向同一位经理汇报的团队内部,而且您如何与您将共事的其他部门沟通?我曾在一家初创公司工作,在那里,我们会与来自不同团队的其他人安排两周一次的咖啡约会,而在一家公司工作,人们彼此不认识,除非他们在那里工作了近十年,不同部门之间没有社交,这有时肯定会妨碍公司,特别是如果你们都在为一个共同的具体目标工作。我相信一个好的数据科学团队已经做了一些社交活动,因为从流程中获取数据的上下文会有很大的帮助。

感谢您的宝贵时间!我希望这是有帮助的,如果你有任何想法请在下面讨论和/或想要联系,你可以在www.monicapuerto.com。

带 R 的排队模型

原文:https://towardsdatascience.com/queueing-models-with-r-a794c78e6820?source=collection_archive---------12-----------------------

r 代表工业工程师

探索“排队”R 包

图片由 Hal Gatewood 拍摄,可在 Unsplash 获得

排队论:导论

排队论是对排队等候的数学研究。排队论和模拟一样,是最广泛使用的运筹学和管理科学技术。其主要目标是建立一个模型来预测队列长度和等待时间,以做出与资源管理和分配相关的有效业务决策,从而提供给定的服务。

排队系统的组成部分

排队系统由三部分组成:到达过程、服务机制和排队规则。

  • 到达流程:描述客户如何到达系统,以及客户到达的分布
  • 服务机制:由服务器数量、每个服务器是否有自己的队列或有一个队列为所有服务器提供服务以及客户服务时间的分布来表达
  • 排队规则:指服务员完成当前顾客的服务后,从队列中选择下一个顾客的规则(如 FIFO :先进先出;后进先出:后进先出;基于优先级;随机选择)

排队模型符号

任何队列系统的两个主要输入是:

  • λ(λ):每个时间段的平均到达人数(即平均到达率)
  • ( 管理部门):每个时间段平均服务的客户数(即平均服务率)

对排队系统进行分类的标准符号系统是 A/B/c/k/m,其中:

  • A 表示到达过程的概率分布
  • B 表示服务流程的概率分布
  • c 代表通道(服务器)的数量
  • k 表示排队系统中允许的最大顾客数
  • m 代表客户总数的最大值

AB 的常见选项有:

  • M 为泊松到达分布(即指数到达间隔分布)或指数服务时间分布
  • D 为确定性或常量值
  • Ek 为一阶的厄朗分布 k
  • G 对于均值和方差已知的一般分布

当没有指定 km 时,假设它们是无穷大。

排队 R 包包含多个用于分析排队系统的函数。对于下面的例子,让我们考虑最简单的排队系统:M/M/1,泊松到达率为每分钟 3 个顾客,指数服务时间为每分钟 4 个顾客,只有一个服务员。我们来看看 R 代码!

M/M/1 结果

汇总输出术语

  • RO(ρ):整体系统利用率
  • P0 :所有服务器空闲的概率
  • :长期平均排队顾客数
  • 【Wq】:长期平均排队时间
  • X :系统吞吐量
  • L :系统长期平均客户数
  • W :系统长期运行的平均时间
  • 【Wqq】:排队模型中有队列时的长期平均排队时间
  • Lqq :排队模型中有队列时的长期平均排队顾客数

既然我们已经用报告函数获得了最相关的性能度量,那么绘制到达和服务流程的分布将会很有意思。再来看看下面的 R 代码!

到达过程的泊松分布图

间隔时间的指数分布图

服务流程的指数分布图

总结想法

根据所研究系统的复杂性,建立数学模型来预测队列长度和等待时间可能是一项具有挑战性的任务。

与前面分析的例子一样, queueing R 包只需要几行代码就可以从多个排队系统中研究和获得结果。虽然有其他可用的模拟软件可能需要您购买许可证(例如竞技场、西米奥),但 R 代表了执行基本模拟研究的另一种有效工具。

如果你觉得这篇文章有用,欢迎在GitHub上下载我的个人代码。你也可以直接在 rsalaza4@binghamton.edu 给我发邮件,在LinkedIn上找到我。有兴趣了解工程领域的数据分析、数据科学和机器学习应用的更多信息吗?通过访问我的媒体 简介 来探索我以前的文章。感谢阅读。

——罗伯特

快速算法查找 101

原文:https://towardsdatascience.com/quick-algorithm-lookup-101-c5520c6daa02?source=collection_archive---------48-----------------------

看看其他一些常见的数据科学算法。

如果你想到数据科学,你的思维往往会转向神经网络,但实际上,数据科学家会想到大量其他算法,它们有自己的优势或劣势。

当然,这些提供了一些更常用的版本,在许多情况下,还有更复杂的版本来解决问题或增加功能。然而,为了保持这篇文章简短,我只包括了更基本的版本。

我不会涵盖神经网络,因为有数百篇关于它们的文章,但是如果你想要一个快速介绍,我可以推荐我的快速介绍文章。

由格伦·卡斯滕斯-彼得斯在 Unsplash 上拍摄的照片

在我们开始之前,我最好先看一下我将在括号中使用的几个术语的标题,以及我使用它们时的含义:

  • 监督学习—数据被标记
  • 无监督学习-数据未标记
  • 白盒方法——可以询问算法以帮助解释其决策过程的结果
  • 黑盒方法——该算法采用输入变量并提供输出(或决策),但不容易解释它是如何做到这一点的
  • 回归方法——使用输入提供一个连续的输出(例如 0.0 到 2.5)
  • 分类方法——使用输入提供一个离散输出(例如“红色”、“黄色”、“蓝色”)

注意当谈论带标签的数据时,这意味着我们知道结果(即,一张照片是“狗”或“猫”),并且是我们试图让机器学习算法复制的结果。对于无监督的,相当于它在数据中寻找分组和区别,但它不知道答案(即,它将所有图片分组为不同的动物类型,但它不知道一个组是“猫”,另一个是“蛇”)。

照片由 Isaac Smith 在 Unsplash 上拍摄

1.线性回归

(监督学习、白盒方法、回归方法)

用于估计基于连续变量的真实值。它通过拟合数据点的最佳拟合线来建立自变量和因变量之间的关系。这条“最佳拟合”线被称为回归线,并表示为(对于一个自变量而言):

其中:

  • y-因变量
  • x —独立变量
  • a-回归线斜率的系数
  • b —回归线截距的系数

如果要使用多个独立变量,那么就要使用多元线性回归。也可以拟合多项式和曲线。

为了拟合回归线,可以使用不同的策略,但最常见的是:

  • 最小平方-这是误差平方和最小的地方
  • 最大似然-这是一种概率方法,对于给定的系数(相对于其他选择),看到观察结果的可能性最大

作者注:如果工具有限,在 Excel 中单一和多重线性回归都是可能的

2.逻辑回归

(监督学习、白盒方法、分类方法)

线性回归的分类版本。这采用离散变量(如“是”或“否”),而不是连续变量(如 0.0 到 1.0)。它通过将逻辑函数(也称为 Logit 函数)拟合到独立变量来对其是否属于某一类进行概率评估。因此,输出将在 0.0 和 1.0 之间,这可以解释为概率。

为了快速说明其工作原理,我们将使用两个分类问题(“事件发生”和“事件未发生”)和一个独立变量 x,则 Logit 函数σ看起来像:

其中:

  • Logit 函数
  • y —任何实际输入

如果我们假设 y 来自独立变量的线性组合,那么在这种情况下,它看起来像:

因此,Logit 函数将变为:

F(x)可以解释为这种情况下事件发生的概率。通常,然后利用临界值来确定事件被归类为“已经发生”的概率水平,一种方法是使用 ROC 曲线和混淆矩阵进行优化。

此示例是二元逻辑回归的一个示例,其中只有两个分类可用,但是,还有许多其他形式可以执行更多分类(多项式逻辑回归),并且也能够接受更多输入变量。也存在关心分类是否有序的形式。

作者注:可以在 Excel 中执行二元逻辑回归(有一个或多个自变量)。然而,预计这将是一项艰巨的调试工作!

一个决策树的例子,如果它的分裂规则被画出

3.决策图表

(监督学习、白盒方法、分类方法、回归方法)

该算法的工作方式是选择最能拆分数据的输入变量(与其他输入变量相比,错误选择最少),然后根据规则(例如“如果时间大于 5 小时”)拆分数据。然后,它依次获取每组拆分的数据,然后找到下一个变量(或再次使用同一个变量)来再次拆分数据。它会一直这样做,直到形成一个决策“树”,从而产生具有相同标签的同类数据集的输出。数据科学家能够制作和研究决策流程图。这使得它不仅有助于解释结果,而且有助于嵌入到不能容纳机器学习算法的设备中。

总的来说,该算法根据最重要的输入变量不断地将数据分割成 2 个或更多的同类数据集,以尽可能多地形成不同的组。这种分割可以通过各种方法来完成,例如:

  • 基尼
  • 信息增益
  • 卡方检验
  • 更多

但是,你会问,使用这些方法之一,如何决定拆分是好的呢?通常,分割是通过度量的变化来衡量的。通常这可能是由于杂质,其中合并的分离样品的纯度比未分离的数据小得多,并且选择降低纯度最多的分离。如果你有兴趣的话,我这里有一篇关于它的更深入的文章。

该算法能够执行回归和分类分析。

作者注:由于其灵活性和研究决策如何达成的能力,这种算法(以及决策森林)往往是数据科学家最常用的算法之一。它经常给出令人惊讶的好结果,可以作为以后算法的基准。

照片由塞巴斯蒂安·恩劳在 Unsplash 上拍摄

4.随机森林

(监督学习、黑盒方法、分类方法、回归方法)

在决策树之后,值得一提的是随机森林。最简单地说,随机森林就是决策树的集合,其中每棵树根据给定的数据做出自己的决定,然后进行集体投票。获胜的决策将作为输出给出。就像决策树一样,它能够进行回归和分类。

“种植”和“培育”一片森林的基本方法是“装树袋”:

  1. 如果训练案例的数量为 N,则通过替换抽样获得大小为 N 的样本。每次为种植的每棵树构建该样本。
  2. 如果有 M 个输入变量,那么这些变量中的一个随机子集(m <
  3. This set of m input variables and the samples data points are used to grow each tree to its fullest extent without pruning (this is where weak decisions are removed or decisions that result in a low number of resulting samples in one group are deleted from the trees decision flowchart).

The results of this method is that not every tree will see the same input data and input variables which results in a reduction in the chances of over-fitting, but also improved model performance. The disadvantages is that many trees (100s to 1000s or more) are often required and takes a large computation time as a result. One advantage is that the optimal number of trees can be found by looking at the “out-of-bag-error”. This is where for each tree is it is given the input data that was not part of the original random sample that was used to create it. The result performance of each tree is then aggregated and by looking at the error against the number of trees “planted” there will often be a plateau where no further model improvement is gained by adding more trees.

作者注:在过去几年中,已经开展了一些工作来提高随机森林的可解释性。其中包括功能重要性和决策路径(查看每个功能的平均响应)。

5.支持向量机(SVM)

(监督学习、黑盒方法、分类方法、回归方法)

这种算法传统上用于分类问题,但也能够回归(在这些领域中使用越来越多)。该算法的目的是插入一个平坦的边界,将类分成各自的组。在最基本的层次上,如果在一个图形上,一个类的点在左下方,而另一个类的点在右上方,该算法将插入一条从左上到右下方的线。这条线是“决策边界”,新点的分类是根据这些点落在线的哪一侧进行的。对于不容易分离的数据,SVM 会增加数据的维数(每个要素都绘制为一个坐标),直到它可以插入一个平坦的超平面边界来分隔各个类。

通常会插入边界,以便最大化每类离线最近的点之间的距离(等距),并且所涉及的数学往往会给给出可解释的结果带来很大困难。将数据(这是核函数本质上所做的)映射到更高维度使用了“核技巧”,这是一种可以拟合边界的方法,而不必经历将所有数据转换成高阶维度中的坐标的全部昂贵的计算成本。此外,因为只有靠近决策边界的数据点(这些点被称为支持向量)被用于决定在哪里放置边界,所以不需要其他点。这可以使 SVM 在能够紧凑地存储和训练模型方面具有优势。

SVM 可以是一个非常强大的工具,已经在几个不同的领域使用。比如:

  • 文本分类——在识别语言的同时,也根据主题对内容进行分类
  • 基因研究——基因表达的分类
  • 事件检测—检测罕见事件,如安全漏洞、引擎故障等。

作者注意到:可以执行无监督学习的 SVM 版本是可用的,并被称为“支持向量聚类”

作者注 2: 迄今为止,机器学习的一些最强大的成果来自 SVM 和神经网络(有时在类似的挑战中竞争)

照片由 Riho Kroll 在 Unsplash 上拍摄

6.朴素贝叶斯

(监督学习、白盒方法、分类方法)

该模型使用输入变量之间的独立性假设,并利用贝叶斯定理产生一种数据分类方法。

贝叶斯定理的基本方程是:

其中:

  • P(A) —独立于事件 B 观察到事件 A 的概率
  • P(B) —独立于事件 A 观察到事件 B 的概率
  • P(A|B) —观察到事件 A 的概率给定事件 B 已经发生
  • P(B|A) —假设事件 A 已经发生,观察到事件 B 的概率

注意:P(B) > 0 和 A & B 是事件

它具有强大的优势,因为它是可解释的,易于构建,快速训练,并且可以非常快速地做出决策。这对于非常大的数据集和某些行业(如试图量化风险的行业)非常有用。众所周知,它甚至优于复杂得多的模型和方法。

作者注:因为该算法假设输入变量是独立的,如果它们不是独立的,那么这会强烈影响最终的模型。

照片由捕捉人心。 on Unsplash

7.k-最近邻(KNN)

(监督学习、黑盒方法、分类方法、回归方法)

这被广泛用于执行分类,但是回归问题也是可能的。

使用的方法是分组的方法之一。通过在特征空间中绘制点,并且添加新的数据点,在空间上为其找到最近的 K 个邻居。这种分类在最近的邻居中是最常见的。

距离度量不必是纯欧几里得的(也使用了 Manhattan 和 Minkowski),但是为 K 选择正确的值可能是该模型的挑战之一。幸运的是,模型训练非常快,但是因为它们存储所有的数据,所以它们可能很大,并且为了获得结果,计算上可能非常昂贵,因为每次都必须找到 K 个最近的邻居。

作者指出:在使用该模型时,所有输入变量都要标准化,这一点很重要,否则该模型会受到某些变量的更大影响,而其他变量只受其值域的影响。

8.k 均值聚类

(无监督学习、盒子方法、分类方法)

这是一个无监督机器学习算法的例子。基本方法是:

  1. 所有数据点都绘制在特征空间中(类似于 KNN)
  2. 在特征空间内植入 k 个点(随机或由数据科学家选择)
  3. 然后,数据点围绕这些点形成聚类。他们成为最接近他们的点的成员
  4. 对于每个聚类,计算中心点
  5. 基于它们与这些聚类中心点的距离来重新确定数据点成员资格
  6. 步骤 4 和 5 被重新计算,直到中心点的移动低于设定的阈值。即收敛已经发生

从表面上看,这似乎与 KNN 相似,但它以不同的方式运作。它确实存在确定要放置的初始 K 点的数量的问题,但是可以对这些进行估计。

帮助优化 K 值的方法是对每个聚类将每个聚类数据点到聚类质心的平方差相加。当这些值相加后,我们就有了每个模型的集群解决方案。通过改变 K 的值并重新计算集群解决方案,我们可以看到该值应该随着 K 值的增加而减小,但是将会有一个点,在该点处增加 K 的增益减小。这为 k 的最佳值提供了指导。

作者注:混合良好的数据可能很难分开。考虑两个相连的螺旋,一个标准的 K-均值将不能分开这两个。然而,有不同的方法可以解决这个问题。

照片由格雷格·希尔在 Unsplash 上拍摄

结束了…或者是?

我希望这个列表已经提供了关于一些常见的数据科学算法是如何工作的(至少在高层次上)以及它们的优缺点的信息。

当然,该领域一直在不断学习和发展,这不可能是一个详尽的列表,但了解这些将有助于你了解他人。

参考源材料:

  1. 机器学习算法基础
  2. 线性回归
  3. 逻辑回归
  4. 决策树
  5. 随机森林
  6. 支持向量机
  7. 黑盒方法—神经网络和支持向量机
  8. 天真的贝叶斯
  9. KNN
  10. K-均值聚类

快速脏数据可视化工具箱

原文:https://towardsdatascience.com/quick-and-dirty-data-visualization-toolbox-a2e24f201e29?source=collection_archive---------35-----------------------

我给有抱负的数据科学家和分析师的建议

用这个工具箱开始你的探索

来源: Unsplash

我们大多数人需要听音乐才能理解它有多美。但通常这就是我们展示统计数据的方式:我们只展示音符,不演奏音乐。—汉斯·罗斯林

数据可视化对于理解数据分析之美至关重要。通过数据可视化,我们的利益相关者了解我们分析的影响。这有助于他们集中注意力,做出明智的决定。

然而,尽管它很重要,我总是收到有抱负的数据科学家关于他们如何开始探索数据分析的问题。

简单,数据可视化。

快速脏数据可视化工具箱

由于存在适用于各种数据集类型的常见场景,我将重点演示这些代码片段,以便您可以轻松地即插即用。

每当我进行新的数据探索和深入研究时,这些代码片段都来自我在 Google 和 Visa 的个人工作经验——这是我快速和肮脏数据可视化的工具箱。

到本文结束时,您应该开始实现这些代码,并更快、更有效地可视化您的数据。不管您的数据有多干净,您都可以直接运行这些代码并提取见解。

如果您想在 Iris 数据集和 HR 保留数据上进一步试验这些工具,请随时访问 Colab 链接并运行它们。

[## 谷歌联合实验室

编辑描述

colab.research.google.com](https://colab.research.google.com/drive/1_5JgUkvrJKZwYGoNrKjpqonu5IBJL0hN?authuser=1#scrollTo=evx30_CG-pA-)

就这样,让我们开始吧!

图书馆要求

我总是推荐 Matplotlib 和 Seaborn 来完成 80%以上的典型数据可视化。如果您使用 Colab,这些库已经为您预先安装好了。如果没有,请在您的包管理器上运行以下命令。

pip install matplotlib
pip install seaborn

圆形分格统计图表

饼图对于显示要素间的计数类分布非常有用。这对于研究目标变量类的分布和根据我们的先验知识进行健全性检查非常有用。

例如,如果我们分析 Iris 数据集中的三个类,我们期望每个类的计数相似。但是在 Gmail 垃圾邮件/垃圾邮件检测中,我们预计会出现偏斜的类别分布。垃圾邮件的数量应该少于火腿的数量。

当… 存在分类目标变量时,您应该使用此选项。

实施饼图

分布图

分布图显示连续的特征分布,以直观地检测异常值、偏斜度和峰度。

它不仅帮助您了解当前分布,还帮助您了解异常值或边缘情况(如负值)。这有助于您理解数据集并设计后续的数据清理和转换。

当……有 n 个连续特征时,你应该用这个。

分层箱线图

分层箱线图是另一种分布可视化工具,可以直观显示每个类别的不同分布,并快速找到关键模式。

在下面的可视化中,只需快速浏览一下,您就知道class ‘setosa’ 在萼片 _ 长度、花瓣 _ 长度和花瓣 _ 宽度中的分布非常低。

这使您能够快速了解什么是重要的,并允许您对数据清理和转换工作进行优先级排序。

当……有 n 个连续特征和分类目标特征时,应使用此选项。

因子图

Factorplot 可视化每个类别的分类计数分布,并快速找到关键模式。这是一个全新的可视化工具,不需要定制。

当… 存在一个带有分类目标特征的分类特征时,应使用此选项。

热图

热图显示了 n 个连续特征之间的相关性。与 pd 结合。DataFrame.corr(),它可视化了哪些功能相互关联。

相关性分析的目标是让您了解哪些特征相互关联,哪些特征可能会妨碍洞察生成和模型学习。

在下面的例子中,最后数字 _ 项目最后 _ 评估平均 _ 每月 _ 小时高度相关。这是有道理的,因为你的项目越多,你需要工作的时间就越多。

如果您将这些特征转储到回归学习模型(GLM)中,而没有正确处理它们的相关性,回归将夸大这两个特征的重要性,这两个特征本质上发送相同的信号。

当… 有 n 个连续特征时,应使用此选项

配对图

Seaborn pairplot 在每个要素对中运行类分布。Pairplot 是一种直观显示多个要素之间分布的快速方法。

潜在的警告是,如果你有许多功能,它可能是嘈杂的。但是您可以使用诸如主成分分析(PCA)之类的特征选择技术来修复和过滤特征。

当… 目标变量有 n 个连续特征时,应使用此选项

Seaborn 配对图

箱线图和散点图

这种集成进一步促进了特征到特征的关系,以影响目标变量。基于您的假设(来自 pairplot 或您自己的观察),您可以选择这些特征并快速绘制。

在下面的示例中,您可以很快看到有 3 个决定员工离职的聚类:

  • 平均每月工作时间长,满意度低→过度工作和不满足
  • 平均月小时数低,满意度中等→表现不佳
  • 高平均月小时数,高满意度→超额完成

即使没有聚类 ML 模型,散点图也为我们提供了关于员工辞职原因的见解和假设:

  • 更好的工作生活平衡(过度工作和不满足)
  • 工作中更好的挑战(后进生)
  • 更好的职业机会(超额完成者)

当… 有两个连续特征和目标变量时,你应该使用这个。这应该与你的假设相匹配。

最后的想法

来源 Unsplash

我相信这些代码很容易在您的第一次数据探索中直接实现。这个可视化工具箱将为您提供一个良好的开端,让您在探索和可视化数据时变得更加自信。

一如既往,如有任何问题,请通过 Linkedin 联系我。如果时间允许,我很乐意回答你的问题。

索利·德奥·格洛丽亚

关于作者

Vincent Tatan 是一名数据和技术爱好者,拥有在 Google LLC、Visa Inc .和 Lazada 开展微服务架构、商业智能和分析管道项目的相关工作经验。

Vincent 是土生土长的印度尼西亚人,在解决问题方面成绩斐然,擅长全栈开发、数据分析和战略规划。

他一直积极咨询 SMU BI & Analytics Club,指导有抱负的数据科学家和工程师,并为企业开发产品开放他的专业知识。

最后,请通过LinkedInMedium Youtube 频道 联系文森特

用于分类问题的快速脏 ML 工具箱

原文:https://towardsdatascience.com/quick-and-dirty-ml-toolbox-3650d34cf0e0?source=collection_archive---------37-----------------------

我给有抱负的数据科学家和分析师的建议

用这个工具箱启动你的 ML

来源: Unsplash

就像 100 年前电力几乎改变了一切一样,今天我实际上很难想到一个我认为 AI(人工智能)在未来几年内不会改变的行业。”~ 吴恩达

机器学习始终是成为一名成功的数据科学家和分析师的重中之重。通过机器学习,我们学会了如何运行程序,将原始数据转化为见解和预测。

然而,当你开始分析某个案例时,你会从哪里开始呢?

在这篇文章中,我想写一篇关于 ML 工具箱的文章,让你开始在一个新的数据集中探索 ML。ML 是一个迭代的过程;大多数时候,你需要通过商业问题来细化你的 ML 操作。

我真诚地希望这篇文章能够成为您对任何数据集进行初步探索的起点。我还根据自己的经验和反复探索创建了数据可视化工具箱,帮助您启动数据项目。

[## 快速脏数据可视化工具箱

用这个工具箱开始你的探索

towardsdatascience.com](/quick-and-dirty-data-visualization-toolbox-a2e24f201e29)

快速和肮脏的 ML 工具箱

这些是我在 Google 和 Visa 的数据探索中反复出现的代码片段——我的快速和肮脏的 ML 探索工具箱。

到本文结束时,您应该实现这些代码并运行 ML exploration 的初始迭代。你可以按照这些步骤,提取见解。

以下是初步探索的流程:

  1. 数据准备
  2. ML 培训
  3. ML 评估
  4. (奖金)多分类器实验

在这种情况下,我们将使用人力资源保留数据 t 来模拟典型的分类目标。

谁会离开你的公司

请随意访问这个 Colab 链接并运行它们。

[## 谷歌联合实验室

编辑描述

colab.research.google.com](https://colab.research.google.com/drive/1HCvusxHM-m4TJBS7VK7dDkfVyCYg6weU?authuser=1#scrollTo=nRvAoF-7B_Sl)

就这样,让我们开始吧!

图书馆要求

导入 Matplotlib 和 Seaborn 来讲述故事

对于那些不了解 Seaborn 的人来说,我很惭愧没有早点告诉你们。在我看来,这是每个数据分析师和科学家都需要熟悉的最大宝藏之一!(不开玩笑!).

Seaborn 将允许您在几秒钟内创建惊人的图形和可视化。因此,作为有效的数据科学家/分析师,这是一个非常重要的工具,可以帮助你提高讲故事的技巧。

Seaborn 是基于matplotlib的 Python 数据可视化库。它提供了一个高层次的界面来绘制有吸引力的和信息丰富的统计图形。— Seaborn Pydata Org

导入 SkLearn for ML 工具包

Scikit Learn (SkLearn)是一个用于 python 的 ML 库。它具有模块化的功能,您需要这些功能来运行各种 ML 特性,如分类、聚类和回归。

SkLearn 使用 Python 科学库,如 NumPy 和 SciPy,并支持算法,如 SVM(支持向量机)、决策树和逻辑回归。

对于本教程,我们将重点讨论决策树和逻辑回归,因为它们提供了直观的可视化表示和简单的实现。大多数时候,他们已经足够“好”了。

我的建议

我总是推荐,SkLearn,Matplotlib 和 Seaborn 来完成> 80%的典型数据可视化。如果你使用 Colab,这些库都为你准备好了。如果没有,请在您的包管理器上运行以下命令。

pip install matplotlib
pip install seaborn
pip install sklearn

数据准备

虚拟化你的分类变量

假设您有一个“大学名称”专题,其中包括诸如“哈佛”、“乔治亚理工学院”、“新加坡国立大学”等标签。对于基于距离的最大似然法,如逻辑回归和 K 近邻法(KNN),你是如何表示它们的?

当您有一个分类变量(非数字)时,您将需要创建多个虚拟特征来将分类替换为数字特征。

实体模型化删除了编码变量,并将唯一的整数值添加到 is_[feature]变量中(例如:is_harvard_graduate,is_yale_graduate)。这些用 0(假)或 1(真)表示,用于基于距离的 ML。

当……您将分类目标变量用于基于回归/距离的 ML 时,您应该使用这种方法。

虚拟销售和工资分类特征

特征选择

想象一下小型创业公司的人力资源保留问题。你有责任预测谁将离开你的公司。你收集了所有你能想到的数据:他们的家庭背景、工作经历和过去一年的表现。

你很高兴,因为你有 100 多个特征,并觉得足以将它们输入神经网络。

这是维度诅咒的陷阱( 理查德·e·贝尔曼 ) 实际上,当你有一个宽数据集但数据集短(n_features 大但数据量小)的时候。很容易使你的 ML 模型过拟合,因为它会记住给定唯一特征集的所有观察值。

解决方案是减少特性集的大小。这允许模型忽略不相关的特征并学习相关的特征。

现在,选择哪些功能?

我们使用一种简单的算法,称为递归特征消除(RFE)。该方法逐个重新运行特征的过滤,直到集合被替换为给出最佳可学习性的集合。

想象一下你的学校篮球俱乐部。每年,教练都会召集感兴趣的学生,并在每次季后赛中淘汰一些。最终的结果是一组具有一致的技能和团队合作的玩家来执行。

您可以根据功能集在一个集合中的重要性对其进行排序。

当… 有大量低观测值的特征倾向于过度拟合您的 ML 模型时,您应该使用此方法。

列车测试分离

想象一下你最近的学术考试。你是怎么准备的?有没有把所有模拟考试答案都背下来但期末考试表现不好的情况?如果是,你需要理解你的模拟考试(训练)以产生期末考试(测试)的结果。

拆分训练和测试数据集对于评估模型准确表示现实的能力非常重要。

这是一个试金石,我们用它来知道模型是欠拟合还是过拟合。

  • 训练集提供了一个已知输出和一般化数据。
  • 测试集评估我们的模型在未知数据集上的表现。
  • 验证集测试调优评估和再培训。

当… 您评估任何 ML 模型时,您都应该使用这个。

ML 培训

逻辑回归

想象一下你现在办公室里的同事。你能选出 3 个最有可能离开小公司的人吗?

你选择了千禧一代(20-30 岁)吗?

如果我根据过去 1 年的表现向您展示谁表现出色,您觉得如何?你能猜得更好吗?

逻辑回归是一种基于概率的模型,它使用最大似然作为距离函数。它计算在给定输入的情况下预测某个值的概率。

在我看来,最好的用例之一是确定适当的权重。它们最好地解释了特征 X 的权重,这对于基于评分的检测系统(信用风险或逃跑风险)来说是方便的。

警告:小心道德问题(阅读数学毁灭武器

在下面的结果中。我们观察到,满意度 _ 结果、工作 _ 事故和工资水平对预测员工外逃风险有重要影响。

当… 运行基于回归的模型、分析其系数以及预测分类问题时,您应该使用此工具。

决策图表

决策树的目标是将数据分成组,从而最大限度地获取信息。

决策树的最大优势是直观和容易获得洞察力,即使是未经清理和非标准化的数据。它不受离群值和具有规则的分割顶部特征的影响。

在我看来,它的最佳用例是找到最能解释某一类的概要。比如离职员工的症状有:低 _ 满意度,低 _ 末位 _ 评价。

当… 您运行基于规则的模型,分析其分类剖析规则,并预测分类问题时,您应该使用此工具。

ML 评估

混淆度量和初步评估统计

混淆度量将模型预测与现实进行比较,这使我们能够了解模型性能并回答业务问题—数据学校:

  • 真阳性(TP): 我们预测他会离开(他确实离开了)。
  • 真正的否定(TN): 我们预测他不会离开(他没有离开)
  • 假阳性(FP): 我们预测他会离开(但他没有离开)——第一类错误。
  • 假阴性(FN): 我们预测他不会离开(但他确实离开了)——第二类错误。
  • 进一步统计:准确率、误分类率、精确度和召回率

当… 你评估任何模型的分类问题时,你应该使用这个。

AUC ROC 曲线

AUC(曲线下面积)ROC(最佳曲线)在设置模拟中设置阈值,以测量分类判断的置信度。它通过给出一个百分比来说明模型能够在多大程度上区分类别。

AUC 越高,模型的预测就越有把握。从逻辑上讲,对于医疗和高风险环境,您希望 AUC 很高,以显示对每个判决的高度信任

而在垃圾邮件检测中,这种高标准可能是不必要的。

当… 您在将模型投入生产之前分析模型的分类问题并确定可信度时,您应该使用此工具。

具有精确回忆曲线的 ROC-AUC

(奖金)多分类器实验

在大多数情况下,您希望试验各种类型的分类模型,并确定性能最佳的模型。有许多基本款和套装款可供选择,例如:

  1. KNeighborsClassifier —基于实例的 ML,根据观察与其他实例的接近程度来预测类。
  2. 支持向量分类器-尝试基于具有最大分离距离的核来分离类。
  3. 决策树分类器——基于规则的 ML,通过分割特征来基于规则预测类别,启发式地给出最佳信息增益。
  4. 随机森林分类器-决策树集成,依赖于在不同数据和特征集上运行的多个模型。
  5. Ada Boost 分类器——决策树集成,它混合多个特征树桩,从头开始形成树集成。
  6. 等等

当… 您想要试验哪个模型给出最高的准确性(预测质量)和最小的对数损失(信息增益)时,应使用此选项。

最后的想法

来源: Unsplash

我相信这些代码很容易在您的第一次数据探索中直接实现。这个可视化工具箱将为您提供一个良好的开端,让您在探索和可视化数据时变得更加自信。

一如既往,如果你有任何问题,请通过 Linkedin 联系我。如果时间允许,我很乐意回答你的问题。

索利·德奥·格洛丽亚

关于作者

文森特用 ML @ Google 对抗网络滥用。文森特使用高级数据分析、机器学习和软件工程来保护 Chrome 和 Gmail 用户。

除了在谷歌的工作,Vincent 还是《走向数据科学媒体》的特约撰稿人,为全球 50 万以上的观众提供有抱负的 ML 和数据从业者的指导。

在空闲时间,文森特在佐治亚理工学院攻读硕士学位,并为铁人三项/自行车旅行进行训练。

最后,请通过 LinkedIn Medium Youtube 频道 联系文森特