likes
comments
collection
share

pytorch基础知识

作者站长头像
站长
· 阅读数 3

安装

PyTorch官网上选择对应的版本,然后下面会出现对应的安装命令 pytorch基础知识 一般安装命令如下:

pip3 install torch torchvision torchaudio

查看版本

import torch
print(torch.__version__)
print("cuda:",torch.cuda.is_available())
2.0.0
cuda: False
import torch
x = torch.rand(5, 3)
print(x)
tensor([[0.5553, 0.0181, 0.7441],
        [0.4689, 0.3908, 0.6449],
        [0.9274, 0.1054, 0.7166],
        [0.3487, 0.2997, 0.3689],
        [0.3598, 0.7262, 0.9564]])

基础知识

张量(Tensors)

声明和定义

  • torch.empty(): 声明一个未初始化的矩阵。创建张量时,它将创建一个具有指定形状的未初始化张量。这意味着张量中的元素可能包含任意值,取决于分配内存时内存中已有的数据。
x = torch.empty(5,3)
print(x)
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])
  • torch.rand():随机初始化一个矩阵
rand_x = torch.rand(5,3)
print(rand_x)
tensor([[0.5710, 0.4795, 0.3452],
        [0.4638, 0.3674, 0.7197],
        [0.7879, 0.7133, 0.5328],
        [0.3456, 0.7724, 0.8772],
        [0.5487, 0.5881, 0.9586]])
  • torch.zeros():创建数值皆为 0 的矩阵,也可以创建都是1的矩阵。torch.ones
# 创建一个数值皆是 0,类型为 long 的矩阵
zero_x = torch.zeros(5, 3, dtype=torch.long)
print(zero_x.shape,zero_x)
torch.Size([5, 3]) tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
  • torch.tensor():直接传递 tensor 数值来创建
# tensor 数值是 [5.5, 3]
tensor1 = torch.tensor([5.5, 3])
print(tensor1.shape,tensor1)
torch.Size([2]) tensor([5.5000, 3.0000])
  • tensor.new_ones():new_*() 方法需要输入尺寸大小
# 显示定义新的尺寸是 5*3,数值类型是 torch.double
tensor2 = tensor1.new_ones(5, 3, dtype=torch.double)  # new_* 方法需要输入 tensor 大小
print(tensor2)
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
  • torch.randn_like(old_tensor):保留相同的尺寸大小
# 修改数值类型
tensor3 = torch.randn_like(tensor2, dtype=torch.float)
print('tensor3: ', tensor3,"size:",tensor3.shape)
tensor3:  tensor([[-0.0608,  1.5656,  0.4083],
        [ 1.1475, -0.7965,  0.8246],
        [-1.1804, -0.4327, -1.2193],
        [ 1.5783,  0.0615,  0.1998],
        [ 0.9681, -0.1788, -0.2666]]) size: torch.Size([5, 3])

操作

加法操作,示例如下:

  • "+" 运算符
  • torch.add(tensor1, tensor2, [out=tensor3])
  • tensor1.add_(tensor2):直接修改 tensor 变量

可以改变 tensor 变量的操作都带有一个后缀 , 例如 x.copy(y), x.t_() 都可以改变 x 变量

tensor4 = torch.rand(5,3)
print("tensor3 + tensor4 = ",tensor4 + tensor3)
print('tensor3 + tensor4= ', torch.add(tensor3, tensor4))
# 新声明一个 tensor 变量保存加法操作的结果
result = torch.empty(5, 3)
torch.add(tensor3, tensor4, out=result)
print('add result= ', result)
# 直接修改变量
tensor3.add_(tensor4)
print('tensor3= ', tensor3)
tensor3 + tensor4 =  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])
tensor3 + tensor4=  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])
add result=  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])
tensor3=  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])

除了加法运算操作,对于 Tensor 的访问,和 Numpy 对数组类似,可以使用索引来访问某一维的数据,如下所示:

# 访问 tensor3 第一列数据
print(tensor3[:, 0])
tensor([ 0.9336,  1.3947, -0.2482,  1.9656,  1.0201])

对 Tensor 的尺寸修改,可以采用 torch.view() ,如下所示:

x = torch.randn(4, 4)
y = x.view(16)
# 第一维的大小由-1自动推断,第二维的大小为8
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.6999])
-0.6999301314353943

Numpy数组转换

Tensor 和 Numpy 的数组可以相互转换,并且两者转换后共享在 CPU 下的内存空间,即改变其中一个的数值,另一个变量也会随之改变。

Tensor转换为Numpy数组

a = torch.ones(5)
print(a)
b = a.numpy()
print(b)
tensor([1., 1., 1., 1., 1.])
[1. 1. 1. 1. 1.]

两者是共享同个内存空间的,例子如下所示,修改 tensor 变量 a,看看从 a 转换得到的 Numpy 数组变量 b 是否发生变化。

a.add_(1)
print(a)
print(b)
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]

Numpy数组转换成Tensor

在 CPU 上,除了 CharTensor 外的所有 Tensor 类型变量,都支持和 Numpy数组的相互转换操作。

import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a.shape,a.dtype,a)
print(b.shape,b.dtype,b)
(5,) float64 [2. 2. 2. 2. 2.]
torch.Size([5]) torch.float64 tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

CUDA 张量

Tensors 可以通过 .to 方法转换到不同的设备上,即 CPU 或者 GPU 上。例子如下所示

# 当 CUDA 可用的时候,可用运行下方这段代码,采用 torch.device() 方法来改变 tensors 是否在 GPU 上进行计算操作
if torch.cuda.is_available():
    device = torch.device("cuda")          # 定义一个 CUDA 设备对象
    y = torch.ones_like(x, device=device)  # 显示创建在 GPU 上的一个 tensor
    x = x.to(device)                       # 也可以采用 .to("cuda") 
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # .to() 方法也可以改变数值类型

神经网络

对于神经网络来说,一个标准的训练流程是这样的:

  • 定义一个多层的神经网络
  • 对数据集的预处理并准备作为网络的输入
  • 将数据输入到网络
  • 计算网络的损失
  • 反向传播,计算梯度 更新网络的梯度,一个简单的更新规则是 weight = weight - learning_rate * gradient
import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 输入图像是单通道,conv1 kenrnel size=5*5,输出通道 6
        self.conv1 = nn.Conv2d(1, 6, 5)
        # conv2 kernel size=5*5, 输出通道 16
        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):
        # max-pooling 采用一个 (2,2) 的滑动窗口
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 核(kernel)大小是方形的话,可仅定义一个数字,如 (2,2) 用 2 即可
        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):
        # 除了 batch 维度外的所有维度
        size = x.size()[1:]
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = Net()
print(net)

params = list(net.parameters())
print("参数量:",len(params))
# conv1.weight
print("第一个参数大小:",params[0].size())

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)
)
参数量: 10
第一个参数大小: torch.Size([6, 1, 5, 5])
# 验证网络的正确性
input = torch.randn(1,1,32,32)
out = net(input)
print(out)
tensor([[ 0.0498, -0.0137, -0.0628, -0.1135, -0.0288,  0.0982, -0.0134,  0.0994,
          0.0788, -0.1159]], grad_fn=<AddmmBackward0>)
# 清空所有参数的梯度缓存,然后计算随机梯度进行反向传播
net.zero_grad()
out.backward(torch.randn(1,10))

损失函数

损失函数的输入是 (output, target) ,即网络输出和真实标签对的数据,然后返回一个数值表示网络输出和真实标签的差距。

out = net(input)
print("out.shape:",out.shape)
# 定义伪标签,调整大小,需要保持和output一样的size
target = torch.randn(1,10)
print(target.shape)
# 定义均方误差
criterion = nn.MSELoss()

loss = criterion(out,target)
print(loss)
out.shape: torch.Size([1, 10])
torch.Size([1, 10])
tensor(1.2594, grad_fn=<MseLossBackward0>)

反向传播

反向传播的实现只需要调用 loss.backward() 即可,当然首先需要清空当前梯度缓存,即.zero_grad() 方法,否则之前的梯度会累加到当前的梯度,这样会影响权值参数的更新。 下面是一个简单的例子,以 conv1 层的偏置参数 bias 在反向传播前后的结果为例:

# 清空所有参数的梯度缓存
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.0010, -0.0127,  0.0054,  0.0048, -0.0075,  0.0059])

更新权重

采用随机梯度下降(Stochastic Gradient Descent, SGD)方法的最简单的更新权重规则如下:

weight = weight - learning_rate * gradient

按照这个规则,代码实现如下所示:

# 简单实现权重的更新例子
learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data*learning_rate)
# 其他优化算法,可以使用torch.optim库

import torch.optim as optim

optimizer = optim.SGD(net.parameters(),lr=0.01)

# 在训练过程中执行下列操作
optimizer.zero_grad() # 清空梯度
output = net(input)
loss = criterion(output,target)
loss.backward()
# 更新权重
optimizer.step()

训练分类器

  • pytorch有一个专门的torchvision库,包含了dataloader和dataset
  • 使用CIFAR10为例

训练图片分类器

一般流程如下:

  1. 通过调用torchvision加载和归一化CIFAR10训练集和测试集
  2. 构建一个卷积神经网络
  3. 定义一个损失函数
  4. 在训练集上训练网络
  5. 在测试集上测试网络性能
# 1.加载和归一化CIFAR10
# 导入必须包
import torch
import torchvision
import torchvision.transforms as transforms
# torchvision 的数据集输出的图片都是 PILImage ,即取值范围是 [0, 1] ,这里需要做一个转换,变成取值范围是 [-1, 1] 
# 定义了一个组合变换
transform = transforms.Compose([
    transforms.ToTensor(), # PIL Image或者Numpy ndarray转换成Pytorch的张量
    transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)) # 图像数据执行标准化操作,第一个三元组表示每个通道的均值 (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)
# 10个分类
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
Files already downloaded and verified
Files already downloaded and verified
# 可视化训练图片,也可以使用TensorBoard
import matplotlib.pyplot as plt
import numpy as np

# 展示图片
def imshow(img):
    print(img.shape)
    img = img/2 +0.5 #  对图像进行逆归一化。在进行训练前,图像很可能已经被归一化,其像素值范围在[-1, 1]之间。逆归一化之后,像素值范围将恢复到 [0, 1],从而能够以原始形式正确显示图像。
    npimg = img.numpy() # pytorch张量转换成numpy
    plt.imshow(np.transpose(npimg,(1,2,0))) # np.transpose() 对 NumPy 数组 npimg 进行转置,第1个轴(高度 H)变为新的第 0 个轴 2=>1 0=>2
    plt.show()

# 随机获取训练集图片
dataiter = iter(trainloader)
# 返回的图片数量和设置的batch_size有关
images,labels = next(dataiter)
print(images.shape,labels)

# 展示图片
imshow(torchvision.utils.make_grid(images))
# 打印图片标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
torch.Size([4, 3, 32, 32]) tensor([7, 7, 9, 8])
torch.Size([3, 36, 138])

pytorch基础知识

horse horse truck ship

附录

本文都是由python的notebook编写,包括结果都是运行后生成的结果。最终通过notebook转换成markdown,命令如下:

jupyter nbconvert torch_basic.ipynb --to markdown

参考

xiaotudui/pytorch-tutorial

小土堆pytorch视频教程的代码

60分钟快速入门 PyTorch

20天吃透Pytorch

Pytorch学习

PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】笔记

PyTorch深度学习-跟着小土堆学习