美文网首页机器学习
详细介绍Tensors的使用

详细介绍Tensors的使用

作者: 生信阿拉丁 | 来源:发表于2021-03-13 11:25 被阅读0次

    作者:童蒙
    编辑:amethyst

    tensor是一种类似于array和matrices的数据结构。PyTorch一般用tensors来表示输入和输出,同时也来表示模型的参数。tensor同Numpy中ndarray类似,然而tensors能够在GPU等其他硬件上运行。

    import torch
    import numpy as py
    

    1. tensor的初始化

    直接从数据转换

    data = [[1,2],[3,4]]
    x_data = torch.tensor(data)
    

    从numpy转换

    • 使用tensor进行转换
    np_array = np.array(data)
    x_np = torch.tensor(np_array)
    
    • 使用from_numpy进行转换tensor
     n = np.ones(5)
     t = torch.from_numpy(n)
    
    • 使用numpy将tensor转换成ndarray
     t = torch.ones(5)
     print(f"t: {t}")
     n = t.numpy()
     print(f"n: {n}")
    
    • CPU的tensor和Numpy共享内存,修改一个会修改另一个。

    从其他tensor转换
    新的tensor保留着参数的相关性质,除非被明确的覆盖。

    x_ones = torch.one_like(x_data)
    x_rands = torch.rand_like(x_data , dtype = torch.float)
    

    指定shape
    shape是一个tuple,指定了维度。

    shape = (2,3,)
    rand_tensor = torch.rand(shape)
    ones_tensor = torch.ones(shape)
    zeros_tensor = torch.zeros(shape)
    

    2. tensor的属性

    tensor的属性有shape,datatype和 device。

    tensor = torch.rand(3,4)
    print(f"Shape of tensor: {tensor.shape}")
    print(f"Datatype of tensor: {tensor.dtype}")
    print(f"Device tensor is stored on: {tensor.device}")
    

    3. tensor的操作

    tensor的操作有很多,100多种,包括转置、检索、分片、数学计算、抽样、线性代数,以及其他的操作。这些操作也可以用在GPU上,可以用如下操作:

    if torch.cuda.is_available():
        tensor = tensor.to('cuda')
    

    类numpy的索引和切片

    tensor = torch.ones(4,4)
    tensor[:,1] = 0
    

    合并tensor

    • 使用torch.cat来在某一个维度上进行合并
      t1 = torch.cat([tensor, tensor, tensor], dim=1)
    • 也可以使用torch.stack进行合并
      t2 = torch.stack([tensor, tensor] ,dim =1 )

    tensor的计算

    • 乘积运算
    tensor.matmul(tensor.T)
    tensor @ tensor.T
    
    • 点乘运算
    tensor.mul(tensor)
    tensor * tensor
    
    • 原位计算
      使用 “_” 为后缀的进行计算,例如 copy_ , t_ , 会覆盖原始值。例如:
      tensor.add_(5)

    4.Torch.autograd

    “torch.autograd”是PyTorch的自动化微分引擎,从而用来方便进行神经网络训练。下面我们来介绍一下,autograd怎么来帮助神经网络进行训练。

    4.1背景信息

    Neural networks(NNs)是封装了一系列的函数,来对输入数据进行计算。这些函数有各种参数(weights和biases),都存储在tensor的数据结构中。

    训练一个NN有两个步骤:

    • Forward propagation:在前向传播中,NN使用各个函数,对输入进行计算,得到最终的结果。
    • Backward propagation:在后向传播中,NN通过对前向传播得到的结果计算误差,对参数进行成比例的调整。从最终的output出发,针对每一个函数的参数进行计算error的偏差,并且使用梯度下降来优化参数。

    4.2 PyTorch中的用法

    首先导入一个模型,然后获得一个随机的输入矩阵,随机生成一个label。

    import torch, torchvision
    model = torchvision.models.resnet18(pretrained=True)
    data = torch.rand(1, 3, 64, 64)
    labels = torch.rand(1, 1000)
    

    使用forwardpropagation来计算:
    prediction = model(data) # forward pass

    对prediction和label来计算loss,然而反向传播loss到整个网络中,这个过程针对loss这个tensor使用backward()。Autograd会计算和存储每个模型的参数的梯度值,并且存在.grad性质中。

    loss = (prediction - lables).sum()
    loss.backward()
    

    最后,我们加载一个optimizer,以SGD为例,首先定义一个。

    optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
    

    之后,使用step() 来进行梯度下降,优化后的参数会存在.grad中。
    optim.step()
    这样,一个模型就训练好了。

    4.3 Autograd如何做微分

    我们来看看autograd如何积累梯度,首先创建两个tensor,required_grad=True,表示会记录每一个操作。

    import torch
    a = torch.tensor([2.,3.],requires_grad = True)
    b = torch.tensor([6.,4.],requires_grad = True)
    ## 构建一个tensor为Q
    Q=3*a**3 - b**2 
    

    假设a和b是NN的参数,Q是error值。在NN训练中,我们来获得error反馈在各参数的梯度。

    当我们调用.backward(),autograd计算梯度,并且存储在相应的.grad的属性中。当Q是一个vector时候,在使用时候,需要提供一个gradient的参数,长度同Q的shape一致,代表Q的梯度。假设为1 。

    external_grad = torch.tensor([1., 1.])
    Q.backward(gradient=external_grad)
    a.grad
    b.grad
    

    也可以把Q变成一个标量,然后直接使用backward()。
    Q.sum().backward()

    4.4 计算图

    autograd会保存data和执行的操作在一个DAG的图形中,构成了Function的对象。在DAG中,输入是叶子,output是root。通过搜索root到leaves,可以计算梯度。

    前向传播中,autograd会做两件事:

    • 计算得到结果
    • 维护DAG的操作函数

    当调用backward()时,从root开始,autograd会:

    • 计算每一个.grad_fn的梯度
    • 将他们累加到.grad中
    • 利用DAG的链规则,回溯

    设置requires_grad=False可以关闭梯度的计算。

    在NN中,不计算梯度的参数称之为 frozen parameters。这个用于冻结某些参数,来调整另一些参数;也可以用于调整一个预先训练好的network。

    from torch import nn, optim
    model = torchvision.models.resnet18(pretrained=True)
    # Freeze all the parameters in the network
    for param in model.parameters():
        param.requires_grad = False
    model.fc = nn.Linear(512, 10)
    optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)
    

    也可以用装饰器torch.no_grad()来替代。

     x = torch.tensor([1], requires_grad=True)
     with torch.no_grad():
         y = x * 2
     y.requires_grad
     @torch.no_grad()
     def doubler(x):
          return x * 2
     z = doubler(x)
     z.requires_grad
    

    5. 神经网络

    可以使用torch.nn 来构建神经网络。nn.autograd定义model和进行微分,nn.Module包含很多层,使用forward进行计算output。

    一个神经网络包括以下步骤:

    • 定义一个NN,包含一些参数
    • 迭代inputs
    • 利用input进行计算
    • 计算loss
    • 反向传播到nn的参数
    • 调整nn的权重,例如weight = weight - learning_rate * gradient

    5.1 定义网络

     import torch
     import torch.nn as nn
     import torch.nn.functional as F
     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)
    
        def forward(self, x):
            # Max pooling over a (2, 2) window
            x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
            # If the size is a square you can only specify a single number
            x = F.max_pool2d(F.relu(self.conv2(x)), 2)
            x = x.view(-1, self.num_flat_features(x))
            x = F.relu(self.fc1(x))
            x = F.relu(self.fc2(x))
            x = self.fc3(x)
            return x
    
        def num_flat_features(self, x):
            size = x.size()[1:]  # all dimensions except the batch dimension
            num_features = 1
            for s in size:
                num_features *= s
            return num_features
    net = Net()
    print(net)
    

    当定义好forward的时候,backward自动也定义好了。

    可以使用net.parametes()来调用模型中的参数。

    params = list(net.parameters())
    print(len(params))
    print(params[0].size())  # conv1's .weight
    

    假设输入为32*32,代码如下:

    input = torch.randn(1, 1, 32, 32)
    out = net(input)
    print(out)
    

    首先清零梯度,然后做后向传播。

    net.zero_grad()
    out.backward(torch.randn(1, 10))
    

    回顾一下调用的模块:

    • torch.tensor
    • nn.Module()
    • nn.parameters()
    • autograd.Functions()

    5.2 计算损失函数

    损失函数为预测值和目标值之间的差异,用来估计预测值同目标的偏离程度。

    损失函数有很多,最简单的是nn.MSELoss,计算平方根误差。

    output = net(input)
    target = torch.randn(10)  # a dummy target, for example
    target = target.view(1, -1)  # make it the same shape as output
    criterion = nn.MSELoss()
    loss = criterion(output, target)
    print(loss)
    

    当调用loss.backward()时候,整个DAG图都会进行微分,图中的所有tensor,如果requires_grad = True,那么.grad属性会累积梯度。

    print(loss.grad_fn)  # MSELoss
    print(loss.grad_fn.next_functions[0][0])  # Linear
    print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU
    

    5.3 后向传播

    使用loss.backward()来反向传播error。需要注意的是,需要首先清除已经存在的梯度,否则梯度会继续保存。

    可以使用loss.backward(),来看前后的变化。

    net.zero_grad()     # zeroes the gradient buffers of all parameters
    print('conv1.bias.grad before backward')
    print(net.conv1.bias.grad)
    loss.backward()
    print('conv1.bias.grad after backward')
    print(net.conv1.bias.grad)
    

    5.4 更新权重

    最简单的权重更新方法是SGD,

    w = w- learning_rate * gradient

    简单的实现如下:

    learning_rate = 0.01
    for f in net.parameters():
        f.data.sub_(f.grad.data * learning_rate)
    

    当用nn的时候,可以使用不同的方法,比如SGD、Adam等,利用torch.optim来实现,使用方法如下:

    import torch.optim as optim
    # create your optimizer
    optimizer = optim.SGD(net.parameters(), lr=0.01)
    # in your training loop:
    optimizer.zero_grad()   # zero the gradient buffers
    output = net(input)
    loss = criterion(output, target)
    loss.backward()
    optimizer.step()    # Does the update
    

    5.5 其他

    保存模型

    PATH = './cifar_net.pth'
    torch.save(net.state_dict(), PATH)
    

    加载模型

    net = Net()
    net.load_state_dict(torch.load(PATH))
    

    6.参考资料

    https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html

    相关文章

      网友评论

        本文标题:详细介绍Tensors的使用

        本文链接:https://www.haomeiwen.com/subject/iqqxcltx.html