Pytorch【60天修炼计划】之第一阶段——入门

前言:
【之前学习深度学习过程中用到了简单的Pytorch框架,但是几乎是断断续续的学习,所以非常不熟悉它的使用,所以准备开启一个60天修炼Pytorch的计划,我相信只要天天练,天天写,天天看,之后一定会熟悉它甚至能够使用它产生大的突破。】

这篇文章是这次计划中的第一阶段——入门,使用的是官方的Pytorch指南:PyTorch Tutorials,我将一步步使用Jupyter notebook进行代码的编写。并将内容转化为Markdown贴到文章中,使之形成自己的东西。


DAY1 :DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ

WHAT IS PYTORCH

import torch

1 TENSORS

# 建立一个5*3的未初始化的矩阵

x = torch.empty(5, 3) 
print(x)

tensor([[1.6101e+19, 1.6255e-43, 4.4659e-42], [1.5900e-30, 6.2123e-33, 4.0718e-28], [6.3770e+11, 1.1576e+27, 1.7486e+25], [1.2567e+19, 2.8404e+29, 4.9599e-14], [4.5802e-14, 3.4388e-39, 2.0993e-32]])

# 建立一个随机未初始化的5*3矩阵

x = torch.rand(5, 3)
print(x)

tensor([[0.2438, 0.8465, 0.5957], [0.1865, 0.3274, 0.9300], [0.6530, 0.9168, 0.1984], [0.7865, 0.3302, 0.2258], [0.6449, 0.1740, 0.5631]])

# 建立一个元素全0且数据类型为long的矩阵

x = torch.zeros(5, 3, dtype = torch.long)
print(x)

tensor([[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])

# 直接从数据中建立一个tensor

x = torch.tensor([5.5, 3])
print(x)

tensor([5.5000, 3.0000])

# 基于一个已经存在的tensor创建一个tensor,这些方法将重用输入张量的属性,例如 dtype,除非用户提供新值

x = x.new_ones(5, 3, dtype = torch.double)
print(x)

# 重写类型,但是size不变
x = torch.randn_like(x, dtype = torch.float)
print(x)

tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]], dtype=torch.float64) tensor([[ 0.3361, -0.1183, -0.7414], [-0.0770, -0.6797, -1.1381], [-0.3244, -0.0458, -0.3434], [-0.2456, -0.5253, 0.2276], [ 0.5424, 1.1312, -1.2819]])

# torch.Size是一个元组,所以它支持所有元组操作

print(x.size())

torch.Size([5, 3])


2 OPERATIONS

# 加法--语法1

y = torch.rand(5, 3)
print(x + y)

tensor([[ 0.5828, 0.6620, -0.6090], [ 0.3083, -0.6671, -1.0037], [-0.2082, 0.4217, -0.2129], [-0.1512, -0.0684, 0.7698], [ 0.5573, 2.1108, -1.0015]])

# 加法--语法2

print(torch.add(x, y))

tensor([[ 0.5828, 0.6620, -0.6090], [ 0.3083, -0.6671, -1.0037], [-0.2082, 0.4217, -0.2129], [-0.1512, -0.0684, 0.7698], [ 0.5573, 2.1108, -1.0015]])

# 加法--提供一个参数保存计算结果

result = torch.empty(5, 3)
torch.add(x, y, out = result)
print(result)

tensor([[ 0.5828, 0.6620, -0.6090], [ 0.3083, -0.6671, -1.0037], [-0.2082, 0.4217, -0.2129], [-0.1512, -0.0684, 0.7698], [ 0.5573, 2.1108, -1.0015]])

# 加法--原地加

y.add_(x)
print(y)

tensor([[ 0.5828, 0.6620, -0.6090], [ 0.3083, -0.6671, -1.0037], [-0.2082, 0.4217, -0.2129], [-0.1512, -0.0684, 0.7698], [ 0.5573, 2.1108, -1.0015]])

任何可以改变tensor内容的操作都会在方法名后加一个下划线’’,比如 x.copy(y), x.t_()都会改变x的值

# 索引

print(x)
print(x[:, 1])

tensor([[ 0.3361, -0.1183, -0.7414], [-0.0770, -0.6797, -1.1381], [-0.3244, -0.0458, -0.3434], [-0.2456, -0.5253, 0.2276], [ 0.5424, 1.1312, -1.2819]]) tensor([-0.1183, -0.6797, -0.0458, -0.5253, 1.1312])

# resize/reshape tensor,使用torch.view方法

x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)
print(x.size(), y.size(), z.size())

torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])

# 如果tensor中只有一个元素,那么使用 .item()获得一个python数字

x = torch.randn(1)
print(x)
print(x.item())

tensor([0.0417])
0.04168638959527016

还有很多的Tensor操作,见 https://www.pytorchtutorial.com/docs/package_references/torch/


3 Numpy 桥

3.1 将一个Torch Tensor 转化为一个 Numpy Array
a = torch.ones(5)
print(a)

# 使用tensor.numpy()转化
b = a.numpy()
print(b)

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

3.2 将一个Numpy Array 转化为一个 Torch Tensor
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out = a)
print(a)
print(b)

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

当我们改变了np array的值,则自动改变 torch tensor的值


4 CUDA Tensors

Tensors 可以使用 .to方法移到任何设备上

# 只有CUDA空闲时才可以运行

if torch.cuda.is_available():
    device = torch.device("cuda")
    
    # 直接在GPU上创建一个tensor
    y = torch.ones_like(x, device = device)
    
    # 或直接用.to
    x = x.to(device)
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))

tensor([1.0417], device='cuda:0')
tensor([1.0417], dtype=torch.float64)


AUTOGRAD: AUTOMATIC DIFFERENTIATION

Pytorch中所有的神经网络最核心的部分就是 autograd包。

autograd包为张量上的所有操作提供 自动微分。它是一个运行时定义的框架,这意味着你的反向传播是根据你代码运行的方式来定义的,因此每一轮迭代都可以各不相同。

import torch


# 创建一个tensor并设置其requires_grad=Ture来追踪计算
x = torch.ones(2, 2, requires_grad = True)
print(x)
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
y = x + 2
print(y)

# 每个通过Function计算得到的变量都有一个.grad_fn属性
print(y.grad_fn)
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x7fdf3c564438>
z = y * y * 3
out = z.mean()

print(z, out)
tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)

# .requires_grad_( ... ) changes an existing Tensor’s requires_grad flag in-place.
a.requires_grad_(True)
print(a.requires_grad)

b = (a * a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x7fdf3c132a20>

1 Gradients

out.backward()

print(x.grad)
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])
x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)
tensor([-943.6801, 1676.0422, -134.2314], grad_fn=<MulBackward0>)
v = torch.tensor([0.2, 1.0, 0.0001], dtype = torch.float)
y.backward(v)

print(x.grad)
tensor([4.0960e+02, 2.0480e+03, 2.0480e-01])
print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)
True
True
False

3 - NEURAL NETWORKS

神经网络可以使用 torch.nn 包进行构建

现在你对autograd有了初步的了解,而nn建立在autograd的基础上来进行模型的定义和微分。
nn.Module中包含着神经网络的层,同时forward(input)方法能够将output进行返回。

一个典型的神经网络的训练过程是这样的:

  • 定义一个有着可学习的参数(或者权重)的神经网络
  • 对着一个输入的数据集进行迭代:
    • 用神经网络对输入进行处理
    • 计算代价值 (对输出值的修正到底有多少)
    • 将梯度传播回神经网络的参数中
    • 更新网络中的权重
      • 通常使用简单的更新规则: w e i g h t = w e i g h t + l r ∗ g r a d i e n t weight = weight + lr * gradient weight=weight+lrgradient

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)
        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 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)
Net(
  (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
  (fc1): Linear(in_features=576, 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 函数将会自动生成。
可以在 forward函数中使用任何张量运算。

一个模型中可学习的参数通过 net.parameters() 返回

params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
10
torch.Size([6, 1, 3, 3])
# 尝试一个随机的32*32的输入

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[-0.0575,  0.0170, -0.1036,  0.1189,  0.0374, -0.0866,  0.0576, -0.1601,
         -0.0319,  0.0746]], grad_fn=<AddmmBackward>)
# 对所有的参数的梯度缓冲区进行归零
net.zero_grad()

# 使用随机的梯度进行反向传播
out.backward(torch.randn(1, 10))

Note:

整个torch.nn包只接受那种小批量样本的数据,而非单个样本。 例如,nn.Conv2d 能够接受一个四维(nSamples x nChannels x Height x Width)的Tensor。
如果你拿的是单个样本,使用 input.unsqueeze(0) 来加一个假维度就可以了。

复习一下前面我们学到的:

  • torch.Tensor - 一个支持自动求导操作的多维数组

  • nn.Module - 神经网络模块。便捷的数据封装,能够将运算移往GPU,还包括一些输入输出的东西。

  • nn.Parameter - 一种变量,当将任何值赋予Module时自动注册为一个参数。

  • autograd.Function - 实现了使用自动求导方法的前馈和后馈的定义。每个Variable的操作都会生成至少一个独立的Function节点,与生成了Variable的函数相连之后记录下操作历史。

目前,我们已经明白的部分:

  • 定义了一个神经网络。
  • 处理了输入以及实现了反馈。

还剩下:

  • 计算代价。
  • 更新网络中的权重。

2 代价函数

一个代价函数以(output, target)对作为输入,并计算输出与目标值的差距

在nn包中有许多的代价函数。最简单的是 nn.MSELoss计算输出与目标之间的均方误差。

output = net(input)
target = torch.randn(10)

#将输出与目标值的shape相同
target = target.view(1, -1) 
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)
tensor(0.5986, grad_fn=<MseLossBackward>)

现在,如果你跟随loss从后往前看,使用 .grad_fn 属性你可以看到这样的一个计算流程图:

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
      -> view -> linear -> relu -> linear -> relu -> linear
      -> MSELoss
      -> loss

然后我们调用 loss.backward(),整个图通过代价来进行区分,图中所有的变量都会以.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
<MseLossBackward object at 0x7f2db03a0048>
<AddmmBackward object at 0x7f2db03a0160>
<AccumulateGrad object at 0x7f2db03b95f8>

3 反向传播

为了反向传播偏差,我们需要运行 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
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0007,  0.0011, -0.0035, -0.0100,  0.0030, -0.0035])

4 更新权值

SGD的更新权值公式为: w e i g h t = w e i g h t + l r ∗ g r a d i e n t weight = weight + lr * gradient weight=weight+lrgradient
可使用简单的python语句实现:

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

但是若使用神经网络,就直接在 torch.optim运用这些方式(SGD,Nesterov-SGD, Adam, RMSProp等等)

import torch.optim as optim

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

4 - TRAINING A CLASSIFIER

数据

因为需要处理像图像、文字、音频或视频这类数据时,可以使用标准python包来导入数据生成一个numpy类型,然后在转化成Tensor

  • For 【images】, packages such as 【Pillow, OpenCV】 are useful
  • For 【audio】, packages such as 【scipy and librosa】
  • For 【text】, either 【raw Python or Cython based loading, or NLTK and SpaCy】 are useful

特别地,对于图像,可以使用 torchvision这个包,其中包含了一些现成的数据集如:Imagenet, CIFAR10, MNIST等等。同时还有一些转换图像用的工具,torchvision.datasetstorch.utils.data.DataLoader

这个指南使用的是CIFAR10的数据集。我们要进行的分类的类别有:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。 这个数据集中的图像都是32x32x3的图片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cKHxp0DE-1569585287594)(attachment:image.png)]

我们要按顺序做这几个步骤:

  • 使用torchvision来读取并预处理CIFAR10数据集
  • 定义一个卷积神经网络
  • 定义一个代价函数
  • 在神经网络中训练训练集数据
  • 使用测试集数据测试神经网络

1 导入和标准化 CIFAR10

使用 torchvision

import torch
import torchvision
import torchvision.transforms as transforms

# torchvision数据集的输出是在[0, 1]范围内的PILImage图片。
# 我们此处使用归一化的方法将其转化为Tensor,数据范围为[-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 = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
Files already downloaded and verified
Files already downloaded and verified

显示一些训练图片看一下

import matplotlib.pyplot as plt 
import numpy as np
%matplotlib inline 
# 在jupyter中只有加上这行代码才能显示图片
 
def imshow(img):
    img = img / 2 + 0.5
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()
    
# 获得一些随机图片
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))

# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x1N8lh9n-1569585287595)(output_5_0.png)]

  dog  deer   cat  deer

2 定义一个卷及神经网络

x -> conv -> relu -> maxPool -> conv -> relu -> maxpool -> linear -> relu -> linear -> relu -> linear -> y

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 训练神经网络

for epoch in range(2):
    
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # 获得输入数据,data是一个[inputs, labels]的列表
        inputs, labels = data
        
        optimizer.zero_grad()
        
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        running_loss += loss.item()
        if i % 2000 == 1999:
            print('[epoch: %d]  [i: %d]  [loss: %.3f]' %(epoch + 1, i + 1, running_loss/2000))
            running_loss = 0.0

print('Finished Training!')
[epoch: 1]  [i: 2000]  [loss: 2.195]
[epoch: 1]  [i: 4000]  [loss: 1.829]
[epoch: 1]  [i: 6000]  [loss: 1.644]
[epoch: 1]  [i: 8000]  [loss: 1.575]
[epoch: 1]  [i: 10000]  [loss: 1.504]
[epoch: 1]  [i: 12000]  [loss: 1.461]
[epoch: 2]  [i: 2000]  [loss: 1.359]
[epoch: 2]  [i: 4000]  [loss: 1.322]
[epoch: 2]  [i: 6000]  [loss: 1.340]
[epoch: 2]  [i: 8000]  [loss: 1.307]
[epoch: 2]  [i: 10000]  [loss: 1.298]
[epoch: 2]  [i: 12000]  [loss: 1.259]
Finished Training!

5 用测试集测试模型

# 先显示一下测试集图片

dataiter = iter(testloader)
images, labels = dataiter.next()

imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7wygEJB6-1569585287596)(output_13_0.png)]

GroundTruth:    cat  ship  ship plane
outputs = net(images)

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))
Predicted:    cat   car   car plane
# 在整个测试集中测试

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        
        # torch.max(a, 1)返回a中每一行最大值的那个元素,且返回其索引
        _, predicted = torch.max(outputs.data, 1)
        
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    
print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))
Accuracy of the network on the 10000 test images: 56 %
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))

with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1

for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))
Accuracy of plane : 57 %
Accuracy of   car : 57 %
Accuracy of  bird : 48 %
Accuracy of   cat : 40 %
Accuracy of  deer : 24 %
Accuracy of   dog : 42 %
Accuracy of  frog : 75 %
Accuracy of horse : 73 %
Accuracy of  ship : 70 %
Accuracy of truck : 71 %

6 在GPU上训练

如果CUDA空闲,那么我们首先定义我们的设备为第一个可见的cuda设备

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
cuda:0
net.to(device)
# 记住,每一步都需要把输入和目标传给GPU。
inputs, labels = data[0].to(device), data[1].to(device)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值