@[TOC]一、pytorch入门
以下面推荐的链接为例,推荐的链接主要是利用谷歌colab跑github代码AttnGAN详细步骤 深度学习实验。
可以教你如何更好的利用谷歌的GPU来进行深度学习的训练,因为平时实验室里是没有服务器的,而自己GPU显存也是比较小的。
所以就更需要借用免费的GPU来使用,这里就推荐colab。
链接:https://blog.csdn.net/air__Heaven/article/details/122660676?spm=1001.2014.3001.5501
在单元格输入并运行以下代码
import torch
import torch
开始学习pytorch的一个好方法是,将它的基本信息单元与普通python进行类比。在普通python种,我们使用变量(variable)来存储数字。
我们可以像使用数学符号一样使用这些变量进行计算,并将结果赋值给新的变量。
下面是一段普通python代码的示例。
//普通python变量
x = 3.5
y = x * x + 2
print( x , y)
//普通python变量
x = 3.5
y = x * x + 2
print( x , y)
首先我们创建了一个变量x,并对它赋值3.5.接着,我们再创建一个名为y的新变量,并将表达式x*x+2的结果赋值给它,即(3.5×3.5)+2=14.25 最后,我们输出x和y的值。
在一个新单元种输入代码并运行,我们应该能得到同样的结果:
pytorch使用一种独特的变量存储数字,我们称他为pytorch张量。让我们创建一个非常简单的张量。
//简单pytorch张量
x = torch.tensor(3.5)
print(x)
//简单pytorch张量
x = torch.tensor(3.5)
print(x)
在上面的代码种,我们创建了一个变量x,它的类型是pytorch张量,初始值为3.5。
输入并运行以上代码,让我们看一下x的输出值。
以上输出的意思是,该变量的值是3.5000,同时它被包装在一个pytorch张量种。了解一个数值的包容器对我们很有用。
让我们对这个张量进行一些简单的计算。在下一个单元格类型种 ,运行一下代码。
# 简单的张量计算
y = x + 3
print(y)
// 简单的张量计算
y = x + 3
print(y)
这里,我们将表达式x+3的结果赋值给一个新变量y。我们刚创建的x是一个数值为3.5的pytorch张量。那么,y的值是什么?
我们可以看到y的值是6.5000,因为3.5+3=6.5,完全合理。同时,我们看到y也是一个pytorch张量。
这种工作与numpy相同。这种一致性有助于我们更容易地实现pytorch。
现在,让我们来看一下pytorch与普通python和numpy的区别,以及pytorch的独特之处。
在下面的代码种,我们用同样的方式创建一个张量x,不过这次我们给了pytorch一个额外的参数(option) requires_grad = True。我们很快就会看到这个参数的作用。
//pytorch张量
x = torch.tensor(3.5,requires_grad = True)
print(x)
//pytorch张量
x = torch.tensor(3.5,requires_grad = True)
print(x)
运行并观察x的输出值
我们看到x的值为3.5000,类型为张量。于此同时,输出种也显示x的requires_grad参数被设置为True。
我们再次用x创建一个新的变量y,不过这次使用不同的表达式。
#y以x的函数表示
y = (x - 1) *(x - 2) *(x - 3)
print(y)
#y以x的函数表示
y = (x - 1) *(x - 2) *(x - 3)
print(y)
现在,y的值由表达式y = (x - 1) *(x - 2) *(x - 3) 的结果赋值。下面我们运行代码
不出所料,y的值是1.8750.这是因为x的值等于3.5,且(3.5 - 1) *(3.5 - 2) *(3.5 - 3) =1.8750
下图中y = (x - 1) *(x - 2) *(x - 3) 的曲线可以更直观地解释我们地计算过程。
事实上,pytorch在幕后做了一些额外地工作。它不仅计算出结果等于1.8750.而且把它加入张量y中。
事实上,pytorch记录了y在数学上由x来定义地。
如果只是普通的变量或者numpy数组,python并不会也没有必要记录y由x定义。一旦x的值计算出y值,唯一重要的只是结果。将结果赋值给y,任务便完成了。
pytorch张量的工作方式有所不同,他们记录了自己是由那个张量计算而得,以及如何计算的 。在这里,pytorch记录y由x定义。
但是这样子做又有什么用呢??请继续往下看。
我们知道,训练神经网络的计算需要使用微积分计算误差梯度。也就是说,输出误差改变的速率睿哲网络链接权重的改变而改变。
神经网络的输出用链接权重计算得出。输出依赖于权重,就像y依赖于x。下面,我们来看看pytorch如何计算y随x变化的速率。
我们计算当x = 3.5时y的梯度,即dy/dx。
要计算y的梯度,pytorch需要知道它依赖于哪个张量以及依赖关系的数学表达式。之后便能计算出dy/dx。
#计算梯度
y.backward()
#计算梯度
y.backward()
上面的代码完成所有这些步骤。通过y,pytorch发现它来自y = (x - 1) (x - 2) (x - 3) ,并自动计算出梯度dy/dx = 3x^2 -12x + 11
同时,这行代码也计算出梯度的数值,并于x的实际值一同存储在张量x里。因为x是3.5,所以梯度是3(3.53.5)-12*(3.5)+11=5.75
虽然只有区区一行代码,但y.backward完成了大量的工作。我们可以看一下张量x里梯度的数值。
# x = 3.5 时的梯度
x.grad
//x = 3.5 时的梯度
x.grad
通过x的参数requires_grad=True 告诉pytorch我们希望得到一个关于x的梯度。
至此,可以看出pytorch的张量比python变量和numpy数组的功能更加丰富。一个pytorch张量可以包含以下内容
上一节的自动梯度计算看似很神奇,其实它很简单的。
它背后的原理值得深入了解,这些知识将帮助我们构建更大规模的网络。
看看下面这个非常简单的网络。事实上,它并不算是一个神经网络,只是一系列计算。
在上面的这个图中,我们看到输入x被用于计算y,y再用于计算输出z
假设y和z的计算过程如下:
y= x^2
z = 2y +3
如果我们希望知道输出z如何随x变化,我们需要知道梯度dy/dx。下面我们来逐步计算
dz/dx = dz/dy * dy/dx
dz/dx = 2 * 2x
dz/dx =4x
第一行是微积分的链式法则(chain rule),对我们非常重要。
我们刚刚算出,z随x的变化可表示为4x。如果x= 3.5 ,则dz/dx = 4* 3.5 =14
当y以x形式定义,而z以y的形式定义时,pytorch便将这些张量连城一幅图,以展示这些张量是如何连接的。这幅图叫做计算图。
我们可以看到y是如何从x计算得到的,z是如何从y计算得到的。此外,pytorch还增加了几个反向箭头,表示y如何随着x变化,z如何随着y变化。这些就是梯度,再训练过程中用来更新神经网络。微积分的过程由pytorch完成,无需我们自己动手计算。
为了计算出z如何随着x变化,我们合并从z经由y回到x的路劲中的所有路劲中的所有梯度。这便是微积分的链式法则。
然后我们来看一下他的代码,在一个新的笔记本中,导入torch,并输入以下代码,建立x、y和z的关系
import torch
#创建包含x、y、z的计算图
x = torch.tensor(3.5,requires_grad = True)
y = x * x
z = 2 * y + 3
#计算梯度
z.backward
x.grad
import torch
//创建包含x、y、z的计算图
x = torch.tensor(3.5,requires_grad = True)
y = x * x
z = 2 * y + 3
//计算梯度
z.backward
x.grad
pytorch先构建一个只有正向连接的计算图。我们需要通过back()函数,使pytorch计算出反向的梯度。
梯度dz/dx 在张量x中被存储为x.grad
运行代码并验证结果
结果是14
值得注意的是,张量x内部的梯度值于z的变化有关。这是因为我们要求pytorch使用z.backward()从z反向计算。因此,x.grad是dz/dx,而不是dy/dx
大多数有效的神经网络包含多个节点,每个节点有多个连进该节点的链接,以及从该节点出发的链接。让我们来看一个简单的例子,例子中的节点有多个进入的链接。
可见,输入a和b同时对x和y有影响,而输出z是由x和y计算出来的。
这些节点的关系如下:
x=2a+b
y = (5 a^2 ) + 3b^3
z=2x+3y
我们按同样的方法计算梯度。
dz/dx =2 dx/da = 2 dy/da = 10a
dz/dy =3 dx/db =3 dy/db = 9b^2
接着我们把这些信息添加到计算图中。
现在我们可以轻易地通过z到a地路劲计算出梯度dz/da。
实际上,从z到a有两条路劲,一条通过x,另一条通过y,我们只需要把两条路劲的表达式相加即可。这么做是合理的,因为从a到z的两条路劲都影响了z的值,这也与我们用微积分的链式发展计算出的dz/da的结果一致。
第一条路劲经过x,表示为22;第二条路劲经过y,表示为310a。所以,z随a变化的速率是4+30a。
如果a是2,则dz/da是4+30*2 =64
然后我们也来检验一下pytorch是否也能得出这个值。首先我们定义pytorch构建计算图所需的关系。
#创建包含x、y、z的计算图
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
x = 2*a +3*b
y = 5*a*a+3*b*b*b
z=2*x+3*y
//#创建包含x、y、z的计算图
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
x = 2*a +3*b
y = 5*a*a+3*b*b*b
z=2*x+3*y
接着,我们出发梯度计算并查询张量a里面的值
#计算梯度
z.backward
#当a =2.0时的梯度
a.grad
//#计算梯度
z.backward()
//#当a =2.0时的梯度
a.grad
然后我们在jupyter notenook看一下计算的答案是否与我们的一致。
从上面的图来看,结果时完全一致的!
有效的神经网络通常比这个小型网络规模大得多。但是pytorch构建计算图的方式以及沿着路劲向后计算梯度的过程是一致的。
可能有些人不太清楚,这些与神经网络的误差以及更新内部权重之间有什么关系。接着,我们就来继续讨论这个问题。
假设,一个简单网络的输出是z,正确的输出是t。那么,误差E即(z-t),或者更常见的(z-t)^2。
误差E只是网络的一个节点,该网络以(z-t)^2从z计算E的值。现在,有效的输出节点是E,而不是z。pytorch可以计算出新的E对于输入的梯度。