pytorch入门教程笔记

pytorch入门教程

pytorch官方中文版教程

文章目录

    • 一、Tensor
    • 二、自动微分
    • 三、神经网络
    • 四、图像分类器
        • 1、加载数据集
        • 2、定义网络模型
        • 3、损失函数和优化器
        • 4、Train
        • 5、Test
        • 6、在GPU上训练
    • 五、数据并行处理
      • Example

一、Tensor

Tensors 类似 ndarrays,Tensors可以放到GPU上计算。

构造tensor:

x = torch.rand(5, 3)  #5行3列的随机矩阵
x = torch.zeros(5, 3, dtype=torch.long) #数据类型为long的全0矩阵
x = torch.tensor([5.5, 3])  #直接用数据构造一个张量

查看tensor维度信息:

x.size()  	#torch.Size([5, 3])

改变 tensor 的形状:

x = torch.randn(4, 4)
y = x.view(16)   #torch.Size([16]) 
z = x.view(-1, 8)  #torch.Size([2, 8])   the size -1 is inferred from other dimensions

如果一个tensor只包含一个元素,则可以使用 .item() 来获得这个元素的 value:

x = torch.randn(1)
print(x) 			#tensor([ 0.9422])
print(x.item()) 	#0.9422121644020081

二、自动微分

使用下面语句跟踪针对tensor烦所有操作,从而能够在backward()时自动计算梯度,将该tensor的梯度累积到它的 .grad属性中

tensor.requires_grad = True
tensor.gard
 with torch.no_grad():  
 	如果不想要对tensor求梯度更新,将代码块写在这里面

计算导数:

Tensor.backward()

一个例子:

x = torch.ones(2, 2, requires_grad=True)  
#x=tensor([[1., 1.],[1., 1.]], requires_grad=True)
y = x + 2
#y=tensor([[3., 3.],[3., 3.]], grad_fn=)
z = y * y * 3
out = z.mean()
#z=tensor([[27., 27.],[27., 27.]], grad_fn=)
#out=tensor(27., grad_fn=)
out.backward() #out是一个标量,backward的时候不需要参数

print(x.grad)
#tensor([[4.5000, 4.5000],[4.5000, 4.5000]])

三、神经网络

训练神经网络的过程:

  1. 定义一个包含可训练参数的神经网络
  2. 读取数据集,获取输入(以batch为单位)
  3. 数据输入到神经网络中进行处理,得到最终的特征、分类结果等等
  4. 计算损失(loss)
  5. 反向传播梯度到神经网络的参数
  6. 更新网络的参数

定义神经网络的例子:

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, 5x5 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        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))#relu这些激活函数不包含参数,不需要写在init里
        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) #print可以看到网络结构

训练过程:

input = torch.randn(1, 1, 32, 32) #获取输入数据
target=torch.randn(10) #输入数据的真实标签
target = target.view(1, -1) #与输出尺寸相同,都是1x10,其中batch_size=1
out = net(input) #将数据输入到模型中,模型自动执行forward函数,此时x=input,返回输出

criterion = nn.MSELoss() #设计or选择损失函数,MSELoss是均方误差
loss = criterion(output, target) #计算误差

net.zero_grad() #将模型net的所有参数梯度缓存器置零(grad=0.),不然会一直累积梯度导致梯度爆炸
loss.backward() #反向传播得到需要更新的张量的梯度

#最简单的更新方法(落后,不推荐),一般使用 torch.optim 包中的更新方法
learning_rate = 0.01  #随机梯度下降更新参数,weight = weight - learning_rate * gradient 
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

loss的反向传播路径,整条路径上的requires_grad=True参数都即将得到更新。

print(loss.grad_fn)
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
      -> view -> linear -> relu -> linear -> relu -> linear
      -> MSELoss
      -> loss

所以,当我们调用 loss.backward(),整个图都会微分,而且所有的在图中的requires_grad=True 的张量将会让他们的 grad 张量累计梯度,此时只是计算得到了梯度,未进行参数更新。

参数更新一般使用 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

四、图像分类器

数据处理方面,当要处理图像,文本,语音或者视频数据时,可以使用标准 python 包将数据加载成 numpy 数组格式,然后将这个数组转换成 torch.*Tensor

特别是对于视觉,pytorch已经有一个 torchvision 包,该包含有支持加载类似Imagenet,CIFAR10,MNIST 等公共数据集的数据加载模块 torchvision.datasets 和支持加载图像数据数据转换模块torch.utils.data.DataLoader

使用CIFAR10数据集为例,包含10个类别。CIFAR-10 中的图像尺寸为3x32x32,也就是RGB的3层颜色通道,每层通道内的尺寸为32*32。

1、加载数据集

import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(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 = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

查看图片

import matplotlib.pyplot as plt
import numpy as np

# functions to show an image
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()  #拿出了一个batch的数据

# show images
imshow(torchvision.utils.make_grid(images)) #将若干幅图像拼成一幅图像
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

2、定义网络模型

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

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        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 = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

3、损失函数和优化器

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

4、Train

for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):	#从索引0开始枚举
        # get the inputs
        inputs, labels = data

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0
            #此处还可以模型在测试集上的准确率等评估指标

print('Finished Training')

5、Test

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, dim=1) #return (values, indices)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

6、在GPU上训练

将神经网络转移到GPU上

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#定义设备device为第一个可见的cuda设备
print(device)  #cuda:0
net.to(device) #递归地遍历所有模块,并将它们的参数和缓冲器转换为CUDA张量。

出了网络要放到GPU上,记住必须在每一个步骤向GPU发送输入和目标:

inputs, labels = inputs.to(device), labels.to(device)

五、数据并行处理

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
 model.to(device)  #将模型放上gpu 0
 mytensor = my_tensor.to(device) #将张量复制到gpu 0

PyTorch 默认只会使用一个 GPU,想要多CPU运行则要用到 DataParallel

model = nn.DataParallel(model)

Example

一个简单的DataSet类模板

class RandomDataset(Dataset):

    def __init__(self, size, length):
        self.len = length
        self.data = torch.randn(length, size)

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return self.len

rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),    batch_size=batch_size, shuffle=True)

用nn.DataParallel包裹model,然后再放到多GPU上。

model = Model(input_size, output_size)
if torch.cuda.device_count() > 1:
  print("Let's use", torch.cuda.device_count(), "GPUs!")
  # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs
  model = nn.DataParallel(model)

model.to(device)

数据并行自动拆分数据,并且将任务单发送到多个 GPU 上。当每一个模型都完成自己的任务之后,DataParallel 收集并且合并这些结果,然后再返回给你。

一般是在第一个维度,即batch_size这个维度上拆分数据的。

for data in rand_loader:
    input = data.to(device)
    output = model(input)
    print("Outside: input size", input.size(),
          "output_size", output.size())

参数设置:

 input_size = 5
 output_size = 2

 batch_size = 30
 data_size = 100

如果有2个GPU:

In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])

如果有3个GPU:

In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])

你可能感兴趣的:(pytorch入门教程笔记)