PyTorch入门教程

技术

导语

PyTorch是目前最受欢迎的深度学习框架之一,本文介绍PyTorch中的一些基本概念及操作,包括张量、自动微分等,最后通过训练一个用于图像分类的神经网络简单介绍用PyTorch框架训练神经网络的基本流程,希望对新入门PyTorch的读者有所帮助。

picture.image

本文主要参考PyTorch官方文档及网络上的一些教程,如有侵权,请联系删除。

Tensor(张量)

Tensor是一种专用的数据结构,类似于数组和矩阵。在 PyTorch 中,我们使用张量对模型的输入和输出以及模型的参数进行编码。

Tensor与NumPy的ndarray类似,不同的是PyTorch中的 Tensor 可以在 GPU 或其他专用硬件上运行以加速计算。

Tensor有几种初始化方式:


          
import torch
          
import numpy as np
          

          
#用其他数据进行初始化
          
data = [[1, 2],[3, 4]]
          
x_data = torch.tensor(data)
          
#用NumPy数组进行初始化
          
np_array = np.array(data)
          
x_np = torch.from_numpy(np_array)
          

          
#用其他Tensors进行初始化
          
x_ones = torch.ones_like(x_data)     #与x_data的属性保持特性一致
          
x_rand = torch.rand_like(x_data, dtype=torch.float) #另外指定数据类型
          


Tensor的属性包括:shape, datatype, device等






tensor = torch.rand(3,4)

print("Shape of tensor: ", tensor.shape)

print("Datatype of tensor: ", tensor.dtype)

print("tensor is stored on: ", tensor.device)



结果:  







Shape of tensor: torch.Size([3, 4])

Datatype of tensor: torch.float32

tensor is stored on: cpu



  
我们可以把Tensor放到GPU上计算:






if torch.cuda.is_available():

tensor = tensor.to('cuda')

print("tensor is stored on: ", tensor.device)



结果:  







      tensor is stored on: cuda:0
    
  


  
PyTorch中支持超过100个Tensor操作,包括转置、索引、切片、数学运算、线性代数、随机采样等等。






print(tensor, "\n")

切片

print(tensor[:,1], '\n')

对应元素相乘

print("tensor.mul(tensor):\n {} \n".format(tensor.mul(tensor)))

或者

print("tensor * tensor: \n {} \n".format(tensor * tensor))

矩阵乘法

print("tensor.matmul(tensor.T): \n {} \n".format({tensor.matmul(tensor.T)}))

或者:

print("tensor @ tensor.T: \n {} \n".format({tensor @ tensor.T}))

零内存拷贝的操作

tensor.add_(5)

print(tensor)



结果:






tensor([[0.7988, 0.5616, 0.4597, 0.0974],

[0.9885, 0.7940, 0.4081, 0.4274],

[0.5799, 0.3978, 0.6393, 0.6365]], device='cuda:0')

tensor([0.5616, 0.7940, 0.3978], device='cuda:0')

tensor.mul(tensor):

tensor([[0.6381, 0.3154, 0.2114, 0.0095],

[0.9771, 0.6305, 0.1665, 0.1826],

[0.3363, 0.1583, 0.4087, 0.4051]], device='cuda:0')

tensor * tensor:

tensor([[0.6381, 0.3154, 0.2114, 0.0095],

[0.9771, 0.6305, 0.1665, 0.1826],

[0.3363, 0.1583, 0.4087, 0.4051]], device='cuda:0')

tensor.matmul(tensor.T):

{tensor([[1.1744, 1.4648, 1.0426],

[1.4648, 1.9568, 1.4220],

[1.0426, 1.4220, 1.3084]], device='cuda:0')}

tensor @ tensor.T:

{tensor([[1.1744, 1.4648, 1.0426],

[1.4648, 1.9568, 1.4220],

[1.0426, 1.4220, 1.3084]], device='cuda:0')}

tensor([[5.7988, 5.5616, 5.4597, 5.0974],

[5.9885, 5.7940, 5.4081, 5.4274],

[5.5799, 5.3978, 5.6393, 5.6365]], device='cuda:0')




  




PyTorch中的Tensor与NumPy中的数组可以互相转换,并且会共享内存位置,更改其中一个的内容同样会影响另一个的值。






t = torch.ones(3)

n = t.numpy()

print("t: ", t)

print("n: ", n)

#更改Tensor会影响NumPy数组

t.add_(1)

print("\nt: ", t)

print("n: ", n)

#Tensor由NumPy数组初始化

n = np.ones(6)

t = torch.from_numpy(n)

print("\nn: ", n)

print("t: ", t)

#更改NumPy数组会影响Tensor

np.add(n, 1, out=n)

print("\nn: ", n)

print("t: ", t)



结果:  







t: tensor([1., 1., 1.])

n: [1. 1. 1.]

t: tensor([2., 2., 2.])

n: [2. 2. 2.]

n: [1. 1. 1. 1. 1. 1.]

t: tensor([1., 1., 1., 1., 1., 1.], dtype=torch.float64)

n: [2. 2. 2. 2. 2. 2.]

t: tensor([2., 2., 2., 2., 2., 2.], dtype=torch.float64)




  





Autograd(自动微分)机制




  




Autograd包是PyTorch中所有神经网络的核心,它为Tensors上的所有操作提供自动微分,为神经网络训练过程中的反向传播提供驱动力。对于每一个Tensor,如果设置它的属性 .requires\_grad 为 True,那么Autograd将会追踪对于该张量的所有操作。当完成计算后可以通过调用.backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性中。






x = torch.ones(3, 3)

if not x.requires_grad:

x.requires_grad_(True)

print(x)



结果:  







tensor([[1., 1., 1.],

[1., 1., 1.],

[1., 1., 1.]], requires_grad=True)



  
对Tensor做一些操作:






y = x * 2

print(y, '\n')

z = y * y * 2

print(z)



结果:






tensor([[2., 2., 2.],

[2., 2., 2.],

[2., 2., 2.]], grad_fn=)

tensor([[8., 8., 8.],

[8., 8., 8.],

[8., 8., 8.]], grad_fn=)




  




进行反正传播,求x的梯度x.grad:






z.backward(x)

print('x.grad: {} \n'.format(x.grad))



结果:






x.grad: tensor([[16., 16., 16.],

[16., 16., 16.],

[16., 16., 16.]])




  




可以通过将代码块包装在 with torch.no\_grad(): 中,来阻止Autograd去跟踪设置了.requires\_grad=True 的Tensor的历史记录:






with torch.no_grad():

print((x * 2).requires_grad)
      

  


结果:






False




  





神经网络




  




在PyTorch中我们可以通过torch.nn包来构建神经网络。一个典型的神经网络训练过程如下:



  




1. 构建一个神经网络;


2. 通过神经网络处理输入数据(Forward);


3. 根据神经网络输出的结果和真实的值,由损失函数计算loss;


4. 将梯度反向传播给网络的参数(Backward);


5. 更新网络的权重;


6. 在数据集上迭代2-5步的过程直至训练完成。



  





 1. 定义神经网络




  








import torch

import torch.nn as nn

import torch.nn.functional as F

nn.Module是所有神经网络模型的基类

class Net(nn.Module):

def __init__(self):
      
super(Net, self).__init__()
      
self.conv1 = nn.Conv2d(1, 6, 5) #输入1通道,输出6通道,5x5卷积核
      

      
self.conv2 = nn.Conv2d(6, 16, 5)
      
self.fc1 = nn.Linear(16 * 5 * 5, 120)
      
self.fc2 = nn.Linear(120, 84)
      
self.fc3 = nn.Linear(84, 10)
      

      

      

def forward(self, x):

x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
      
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 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:]  
      
num_features = 1   
      
for s in size:
      
    num_features *= s
      
return num_features
      

      

net = Net()

print(net)

      

结果:


          
Net(
          
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
          
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
          
(fc1): Linear(in_features=400, out_features=120, bias=True)
          
(fc2): Linear(in_features=120, out_features=84, bias=True)
          
(fc3): Linear(in_features=84, out_features=10, bias=True)
          
)
      

我们只需要定义 forward 函数,可以在中使用任何针对张量的操作和计算。backward函数用来计算导数,会通过autograd自动定义。

  1. 输入数据,前向传播

给网络输入32x32的数据:


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

结果:


          
tensor([[-0.1158, -0.0385,  0.1082,  0.0346, -0.0512,  0.0358,  0.1280,  0.1219,
          
-0.0250,  0.0036]], grad_fn=<AddmmBackward>)
      
  1. 计算loss


          
target = torch.randn(10)  # 使用模拟数据
          
target = target.view(1, -1)  # 使目标值与数据值尺寸一致
          
criterion = nn.MSELoss()
          
loss = criterion(output, target)  # 使用均方误差损失函数
          
print(loss)
      

结果:


        
            

          tensor(1.6162, grad\_fn=<MseLossBackward>)
        
      
  1. 反向传播

首先需要清零现有的梯度,否则当前梯度会与已有的梯度累加,然后再调用loss.backward()来反向传播误差。


          
net.zero_grad()     # 清零所有参数的梯度缓存
          
print('conv1.bias.grad before backward:')
          
print(net.conv1.bias.grad)
          

          
loss.backward()
          

          
print('conv1.bias.grad after backward:')
          
print(net.conv1.bias.grad)
      

结果:


          
conv1.bias.grad before backward:
          
None
          
conv1.bias.grad after backward:
          
tensor([ 0.0106, -0.0016,  0.0181,  0.0205,  0.0186, -0.0276])
      
  1. 更新权重

假设采用随机梯度下降(SGD)法来更新神经网络的权重:


          
import torch.optim as optim
          
# 创建SGD优化器
          
optimizer = optim.SGD(net.parameters(), lr=0.01)
          
 #更新权重
          
optimizer.step() 
      

训练图像分类器

torchvision包中包含了计算机视觉中常用的Imagenet、CIFAR10、MNIST等数据集,可以通过torchvision.datasets来引用。同时PyTorch中提供数据加载器torch.utils.data.DataLoader用于加载数据集。

在本例程中,我们使用CIFAR10数据集来训练一个神经网络,用于对图像进行分类。CIFAR10数据集有10个类别,每张图片都是32x32像素的3通道彩色图片。

picture.image

  1. 加载CIFAR10数据集并进行标准化

首先导入需要的包:


          
import torch
          
import torchvision
          
import torchvision.transforms as transforms
      

用torchvision加载数据集后输出的是范围在[0, 1]之间的PILImage,我们需要将其标准化为范围在[-1, 1]之间的张量。


          
transform = transforms.Compose(
          
[transforms.ToTensor(),
          
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
          

          
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
          
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2)
          

          
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
          
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2)
          

          
classes = ('airplane', 'automobile', 'bird', 'cat',
          
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
      
  1. 定义神经网络


          
class Net(nn.Module):
          

          
def __init__(self):
          
    super(Net, self).__init__()
          
    self.conv = nn.Sequential(
          
        nn.Conv2d(3, 6, 5),
          
        nn.ReLU(),
          
        nn.MaxPool2d(2, 2),
          
        nn.Conv2d(6, 16, 5),
          
        nn.ReLU(),
          
        nn.MaxPool2d(2, 2)
          
        )
          
    self.fc = nn.Sequential(
          
        nn.Linear(16*5*5, 120),
          
        nn.ReLU(),
          
        nn.Linear(120, 84),
          
        nn.ReLU(),
          
        nn.Linear(84, 10)
          
        )
          

          
def forward(self, x):
          
    feature = self.conv(x)
          
    output = self.fc(feature.view(x.shape[0], -1))
          
    return output
          

          
net = Net()
      
  1. 定义损失函数和优化器


          
import torch.optim as optim
          
#使用交叉熵损失函数
          
criterion = nn.CrossEntropyLoss()
          
#使用SGD优化器
          
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
      
  1. 训练网络

通过数据加载器,将训练集数据输入给网络和优化器进行训练:


          
total_epoches = 5
          
for epoch in range(total_epoches):
          
    totoal_loss = 0.0
          
    i = 0
          
    for data in trainloader:
          
        # 读取数据
          
        inputs, labels = data             
          
        # 对网络所有参数的梯度进行清零
          
        optimizer.zero_grad()           
          
        # Forward
          
        outputs = net(inputs)
          
        # 计算损失
          
        loss = criterion(outputs, labels)
          
        # Backward
          
        loss.backward()
          
        # 更新参数
          
        optimizer.step()    
          
        totoal_loss += loss.item()
          
        # 每5000个batch打印一次
          
        if i % 5000 == 4999:
          
        print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, totoal_loss / 5000))
          
        totoal_loss = 0.0
          
        i += 1
          
print('训练完成')
          


结果:  







[1, 5000] loss: 2.036

[1, 10000] loss: 1.607

[2, 5000] loss: 1.384

[2, 10000] loss: 1.326

[3, 5000] loss: 1.216

[3, 10000] loss: 1.177

[4, 5000] loss: 1.103

[4, 10000] loss: 1.094

[5, 5000] loss: 1.012

[5, 10000] loss: 1.034

训练完成



保存训练好的模型:






SAVE_PATH = './cifar10_net.pth'

torch.save(net.state_dict(), SAVE_PATH)



在测试集上测试模型的效果:






net = Net()

net.load_state_dict(torch.load(SAVE_PATH))

correct = 0

total = 0

with torch.no_grad():

for data in testloader:
      
    images, labels = data
      
    outputs = net(images)
      
    _, predicted = torch.max(outputs.data, 1)
      
    total += labels.size(0)
      
    correct += (predicted == labels).sum().item()
      

print('The accuracy of the network on the %d test images: %d %%' % (total, 100 * correct / total))



结果:  







      The accuracy of the network on the 10000 test images: 62 %
    
  



  






0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论