PyTorch基础(一)

补充PyTorch入门(一)

1. 张量

在同构的意义下:
第零阶张量 (r = 0) 为标量 (Scalar),
第一阶张量 (r = 1) 为向量 (Vector),
第二阶张量 (r = 2) 则成为矩阵 (Matrix),
第三阶以上的统称为多维张量。

如果张量中只有一个元素,可以用tensor.item()取出其数值

Tensor的基本数据类型有五种:

32位浮点型:torch.FloatTensor。 (默认)
64位整型:torch.LongTensor。
32位整型:torch.IntTensor。
16位整型:torch.ShortTensor。
64位浮点型:torch.DoubleTensor。
除以上数字类型外,还有 byte和chart型

#torch.half()将tensor投射为半精度浮点类型
half=tensor.half()

#初始化一个单位矩阵,即对角线为1 其他为0
eye=torch.eye(2,2)
eye

#常用方法:
x = torch.tensor([[1,2,3],[4,5,6],[7,8,9]])
print(x)

# 1. torch.max(input, dim) 函数
#函数会返回两个tensor,第一个tensor是每行的最大值;第二个tensor是每行最大值的索引。
max_value, max_idx = torch.max(x, dim=1)
print(max_value, max_idx)

#2. torch.sum()函数  
sum_x = torch.sum(x,dim=1) #行求和
print(sum_x)

2. 自动求导

计算梯度方法在PyTorch入门(一)中已经讲述,这里进行补充并解析求导过程。

#自动求导过程解析:
x = torch.rand(5, 5, requires_grad=True)
y = torch.rand(5, 5, requires_grad=True)
z= x**2+y**3
z.backward(torch.ones_like(x))

Python的 dir() 返回参数的属性、方法列表。

dir(z)

返回很多的方法、参数,这里主要讲几个比较主要的属性:

  • .is_leaf 记录是否是叶子节点。一个变量是创建变量还是结果变量是通过.is_leaf来获取的。
print("x.is_leaf="+str(x.is_leaf))  #True
print("z.is_leaf="+str(z.is_leaf))  #False
  • grad_fn:记录并且编码了完整的计算历史
    为什么我们执行z.backward()方法会更新x.grad和y.grad呢? .grad_fn属性记录的就是这部分的操作,虽然.backward()方法也是CPP实现的,但是可以通过Python来进行简单的探索。
dir(z.grad_fn)

在grad_fn属性列表中,next_functions就是精华

z.grad_fn.next_functions
#结果((, 0), (, 0))
  • next_functions是一个tuple of tuple of PowBackward0 and int。

为什么是2个tuple ? 因为我们的操作是z= x ^ 2+y ^ 3 刚才的AddBackward0是相加,而前面的操作是乘方 PowBackward0。tuple第一个元素就是x相关的操作记录。这两个tuple元素就是grad_fn二元操作符两边的元素。(如果是c=a*2+b^ 3-c/4,那么c.grad_fn就是最后一步的操作,也就是减法,而next_functions的两个元素就是加法和除法,即c=( (a * 2)+(b ^ 3) ) - ( c / 4 )
继续深挖:

xg = z.grad_fn.next_functions[0][0]
dir(xg)

x_leaf=xg.next_functions[0][0]
type(x_leaf)  #AccumulateGrad

在PyTorch的反向图计算中,AccumulateGrad类型代表的就是叶子节点类型,也就是计算图终止节点。AccumulateGrad类中有一个.variable属性指向叶子节点。

  • .variable的属性就是我们的生成的变量x
x_leaf.variable
print("x_leaf.variable的id:"+str(id(x_leaf.variable)))     #x_leaf.variable的id:4840553424
print("x的id:"+str(id(x)))       #x的id:4840553424

assert(id(x_leaf.variable)==id(x))

这样整个规程就很清晰了:

  1. 当我们执行z.backward()的时候。这个操作将调用z里面的grad_fn这个属性,执行求导的操作。
  2. 这个操作将遍历grad_fn的next_functions,然后分别取出里面的Function(AccumulateGrad),执行求导操作。这部分是一个递归的过程直到最后类型为叶子节点。
  3. 计算出结果以后,将结果保存到他们对应的variable 这个变量所引用的对象(x和y)的 grad这个属性里面。
  4. 求导结束。所有的叶节点的grad变量都得到了相应的更新

最终当我们执行完c.backward()之后,a和b里面的grad值就得到了更新。

3. 扩展Autograd

如果您想在模型中执行不可微分或依赖非 Pytorch 库中有的函数(例如 NumPy)的计算,但仍希望您的操作与其他操作链接并使用 autograd 引擎,可以通过扩展torch.autograd实现自定义函数 .
在某些情况下,还可以使用自定义函数来提高性能和内存使用率

如果需要自定义autograd扩展新的功能,就需要扩展Function类。因为Function使用autograd来计算结果和梯度,并对操作历史进行编码。 在Function类中最主要的方法就是forward()和backward()他们分别代表了前向传播和反向传播。

一个自定义的Function需要一下三个方法:

  • init (optional):如果这个操作需要额外的参数则需要定义这个Function的构造函数,不需要的话可以忽略。
  • forward():执行前向传播的计算代码
  • backward():反向传播时梯度计算的代码。 参数的个数和forward返回值的个数一样,每个参数代表传回到此操作的梯度。
#引入Function便于扩展
from torch.autograd.function import Function
#定义一个乘以常数操作,输入的参数是张量
#方法必须是静态方法,所以要加上@staticmethod
class MulConstant(Function):
    @staticmethod
    def forward(ctx, tensor, constant):
        #ctx用来保存信息,这里类似于self,并且ctx的属性可以在backward中调用
        ctx.constant = constant
        return tensor *constant
    @staticmethod
    def backward(ctx, grad_output):
        #返回的参数要与输入的参数一样
        #第一个输入为3*3的张量,第二个为一个常数
        #常数的梯度必须是None
        return grad_output, None
        
 #测试
a = torch.rand(3,3,requires_grad=True)
b = MulConstant.apply(a,5)
print("a:"+str(a))
print("b:"+str(b))

#反向传播,返回值不是标量,所以backward方法需要参数
b.backward(torch.ones_like(a))

a.grad

4. 神经网络包nn和优化器optm

torch.nn是专门为神经网络设计的模块化接口。nn构建于Autograd之上,可以用来定义和运行神经网络。
nn.functional中包含了神经网络中使用的一些常用函数,这些函数的特点是,不具有可学习的参数(如ReLu, pool, DropOut等)

import torch 
import torch.nn as nn
import torch.nn.functional as F

#PyTorch中已经为我们准备好了现成的网络模型,只要继承nn.Module,并实现它的forward方法,PyTorch会根据autograd,自动实现backward函数,在forward函数中可使用任何tensor支持的函数,还可以使用if、for循环、print、log等Python语法,写法和标准的Python写法一致。

# 定义一个网络
class Net(nn.Module):
    def __init__(self):
        # nn.Module子类的函数必须在构造函数中执行父类的构造函数
        super(Net, self).__init__()
        
        # 卷积层 '1'表示输入通道, '6'表示输出通道数,'3'表示卷积核为3*3
        self.conv1 = nn.Conv2d(1, 6, 3) 
        #线性层,输入1350个特征,输出10个特征
        self.fc1   = nn.Linear(1350, 10)  #这里的1350是如何计算的呢?这就要看后面的forward函数
    #正向传播 
    def forward(self, x): 
        print(x.size()) # 结果:[1, 1, 32, 32]
        # 卷积 -> 激活 -> 池化 
        x = self.conv1(x) #根据卷积的尺寸计算公式,计算结果是30,维数-卷积核数+1
        x = F.relu(x)
        print(x.size()) # 结果:[1, 6, 30, 30]
        x = F.max_pool2d(x, (2, 2)) #我们使用池化层,计算结果是15
        x = F.relu(x)
        print(x.size()) # 结果:[1, 6, 15, 15]
        # reshape,‘-1’表示自适应
        #这里做的就是压扁的操作 就是把后面的[1, 6, 15, 15]压扁,变为 [1, 1350]
        x = x.view(x.size()[0], -1) 
        print(x.size()) # 这里就是fc1层的的输入1350 
        x = self.fc1(x)        
        return x

net = Net()
print(net)

#net.named_parameters可同时返回可学习的参数及名称
for name,parameters in net.named_parameters():
    print(name,':',parameters.size())

input = torch.randn(1, 1, 32, 32) # 这里的对应前面forward的输入是32
out = net(input)
out.size()

inputs.size()

#梯度清零
net.zero_grad()
out.backward(torch.ones(1,10))

#注:torch.nn不支持一次只输入一个样本,即一次必须是一个batch
#也就是说就算我们输入一个样本,也会对样本进行分批,所以,所有的输入都会增加一个维度,比如上边的inputs, nn中定义三维,但是输入的时候手动增加了一维,最前面的1是batch_size

#损失函数
y = torch.arange(0,10).view(1,10).float()
criterion = nn.MSELoss()
loss = criterion(out,y)
#loss 是一个scala,我们可以根据item来获取其数值
print(loss.item())

#优化器
import torch.optim
out = net(inputs)
criterion = nn.MSELoss()
loss = criterion(out,y)
optimizer = torch.optim.SGD(net.parameters(),lr=0.01)
optimizer.zero_grad()

optimizer.step()

你可能感兴趣的:(深度学习,transformer,深度学习,人工智能)