深入浅出PyTorch学习笔记(持续更新)

PyTorch了解与安装

PyTorch是利用深度学习进行科学研究的重要工具,学术界常用的深度学习框架,简洁、高效、扩展性好。

PyTorch的安装

  1. 安装Anaconda
  2. 创建虚拟环境(Windows在Anaconda Prompt进行)
conda create -n pytorch python=3.8
  1. 查看环境是否安装成功
conda info --envs
  1. 进入创建的pytorch环境
conda activate pytorch
  1. 安装pytorch,在PyTorch官网https://pytorch.org/寻找安装命令代码(根据自己的安装版本选择)
  2. 测试PyTorch

    安装的是CPU版本的话会返回False,能够调用GPU的会返回True

但之后用jutpter notebook时出现了以下问题:
在这里插入图片描述
解决方法:参考了安装PyTorch后jupyter notebook中仍出现“No module named torch“
Anaconda虚拟环境安装PyTorch并使用Spyder
,操作之后即可成功运行

激活环境命令
conda activate pytorch
退出当前环境
conda deactivate
安装包
conda install package_name
卸载包
conda remove package_name
显示所有安装的包
conda list

PyTorch基础知识

张量(Tensor)
  • 任何维度的数据表示
  • PyTorch运算的基本单元,但并非是PyTorch中才有的概念
  • 在基础数据定义和运算中会频繁用到张量
  • 在PyTorch中支持GPU运算,自动求导等操作
创建tensor
import torch
x = torch.rand(4,3)    #随机初始化矩阵
y = torch.zeros(4,3,dtype = torch.long)  #全0矩阵的构建,并且通过dtype设置数据类型为 long
z = torch.zero_(x)  #将现有矩阵转换为全0矩阵,torch.zeros_like()也是此用法
x = torch.tensor([5,6])  #直接使用数据构造张量
print(x)
#基于已经存在的tensor,创建一个tensor
x = x.new_ones(4, 3, dtype=torch.double) #创建一个新的全1矩阵tensor,返回的tensor默认具有相同的torch.dtype和torch.device
print(x)
x = torch.randn_like(x, dtype=torch.float)  # 重置数据类型
print(x)

常见的构造Tensor方法:

函数功能
Tensor(sizes)基础构造函数
tensor(data)类似于np.array
ones(size)全1
zeros(size)全0
eye(sizes)对角为1,其余为0
arange(s,e,step)从s到e,步长为step
linspace(s,e,steps)从s到e,均匀分成step份
rand/randn(sizes)rand是[0,1)均匀分布;randn是服从N(0,1)的正态分布
normal(mean,std)正态分布(均值为mean,标准差是std)
randperm(m)随机排列
张量的操作
  • 加法操作
print(x+y)   #方式一
print(torch.add(x,y))  #方式二
y.add_(x)  #方式三
  • 索引操作(类似于numpy)
    需要注意的是:索引出来的结果与原数据共享内存,修改一个,另一个会跟着修改。如果不想修改,可以考虑使用copy()等方法

  • 维度变换

x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # -1是指这一维的维数由其他维度决定
print(x.size(), y.size(), z.size())

torch.view() 返回的新tensor与源tensor共享内存(其实是同一个tensor),更改其中的一个,另外一个也会跟着改变。
为了使创建的张量和原始张量不共享内存,我们先用 clone() 创造一个张量副本然后再使用 torch.view()进行函数维度变换 。

  • 取值操作
    可以使用 .item() 来获得这个 value,而不获得其他性质
广播机制

当对两个形状不同的 Tensor 按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个 Tensor 形状相同后再按元素运算。

自动求导
  • 所有神经网络的核心是autograd包,为张量上的所有操作提供了自动求导机制。
  • torch.Tensor 是autograd包的核心类。如果设置它的属性 .requires_grad 为 True(默认为False),那么它将会追踪对于该张量的所有操作。
  • 当完成计算后可以通过调用 .backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性
from __future__ import print_function
import torch
x = torch.randn(3,3,requires_grad=True) ##创建一个张量并设置requires_grad=True用来追踪其计算历史
print(x.grad_fn)  #每个张量都有一个.grad_fn属性,该属性引用了创建 Tensor 自身的Function,如果张量是用户手动创建的,这个张量的grad_fn是 None 
#如果需要计算导数,可以在 Tensor 上调用 .backward()。如果 Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward() 指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,该参数是形状匹配的张量
y == x**2  #y是计算的结果,所以它有grad_fn属性
y.backward(gradient=torch.randn(3,3))
z = y * y * 3
out = z.mean()
out.backward()  #反向传播,因为 out 是一个标量,因此out.backward()和 out.backward(torch.tensor(1.)) 等价
print(x.grad)  #输出导数 d(out)/dx
#再来反向传播一次
out2 = x.sum()
out2.backward()
print(x.grad)

out3 = x.sum()
x.grad.data.zero_()  #grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。
out3.backward()
print(x.grad)
#防止跟踪
with torch.no_grad():
    print((x**2).requires_grad)
##希望修改tensor的数值,但又不希望autograd记录(即不会影响反向传播),可以对 tensor.data 进行操作
x = torch.ones(1,requires_grad=True)
print(x.data) # 还是一个tensor
print(x.data.requires_grad) # 但是已经是独立于计算图之外
y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
y.backward()
print(x.data) # 更改data的值也会影响tensor的值 
print(x.grad)
并行计算简介
  • CUDA是NVIDIA提供的GPU并行计算框架
  • 在PyTorch使用 CUDA表示要开始要求我们的模型或者数据开始使用GPU了。
  • .cuda() 时,其功能是让我们的模型或者数据从CPU迁移到GPU(0)当中,通过GPU开始计算。
  • 数据在GPU和CPU之间进行传递时会比较耗时,应尽量避免数据的切换
  • 当我们的服务器上有多个GPU,我们应该指明我们使用的GPU是哪一块,如果我们不设置的话,tensor.cuda()方法会默认将tensor保存到第一块GPU上,等价于tensor.cuda(0),这将会导致爆出out of memory的错误。我们可以通过以下两种方式继续设置。
#方法一
import os
os.environ["CUDA_VISIBLE_DEVICE"] = "2" # 设置默认的显卡
#方法二
CUDA_VISBLE_DEVICE=0,1 python train.py # 使用0,1两块GPU
常见的并行方法
  • 网络结构分布到不同的设备中(Network partitioning):将模型的各个部分拆分,将不同的部分放入到GPU来做不同任务的计算。但是GPU之间的通信在这种密集任务中很难办到,所以这个方式慢慢淡出了视野
  • 同一层的任务分布到不同数据中(Layer-wise partitioning):同一层的模型做一个拆分,让不同的GPU去训练同一层模型的部分任务。但是在我们需要大量的训练,同步任务加重的情况下,会出现和第一种方式一样的问题。
  • 不同的数据分布到不同的设备中,执行相同的任务(Data parallelism):同一个模型在不同GPU中训练一部分数据,然后再分别计算一部分数据之后,只需要将输出的数据做一个汇总,然后再反传。

PyTorch的主要组成模块

深度学习中训练和验证过程最大的特点在于读入数据是按批的,每次读入一个批次的数据,放入GPU中训练,然后将损失函数反向传播回网络最前面的层,同时使用优化器调整网络参数。这里会涉及到各个模块配合的问题。训练/验证后还需要根据设定好的指标计算模型表现。

参数设置
batch_size = 16  # 批次的大小
lr = 1e-4  # 优化器的学习率
max_epochs = 100  #训练次数

GPU的设置有两种常见的方式:

# 方案一:使用os.environ,这种情况如果使用GPU不需要设置
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'

# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
数据读入

PyTorch数据读入是通过Dataset(定义好数据的格式和数据变换形式)+DataLoader(用iterative的方式不断读入批次数据)的方式完成的
主要包含三个函数:

  • _init_: 用于向类中传入外部参数,同时定义样本集

  • _getitem_: 用于逐个读取样本集合中的元素,可以进行一定的变换,并将返回训练/验证所需的数据

  • _len_: 用于返回数据集的样本数

构建Dataset类的方式:
import torch
from torchvision import datasets
train_data = datasets.ImageFolder(train_path, transform=data_transform)  #PyTorch自带的ImageFolder类的用于读取按一定结构存储的图片数据(path对应图片存放的目录,目录下包含若干子目录,每个子目录对应属于同一个类的图片)
val_data = datasets.ImageFolder(val_path, transform=data_transform) #data_transform”可以对图像进行一定的变换,如翻转、裁剪等操作,可自己定义

需要自己来定义Dataset类:

class MyDataset(Dataset):
    def __init__(self, data_dir, info_csv, image_list, transform=None):
        """
        Args:
            data_dir: path to image directory.
            info_csv: path to the csv file containing image indexes
                with corresponding labels.
            image_list: path to the txt file contains image names to training/validation set
            transform: optional transform to be applied on a sample.
        """
        label_info = pd.read_csv(info_csv)
        image_file = open(image_list).readlines()
        self.data_dir = data_dir
        self.image_file = image_file
        self.label_info = label_info
        self.transform = transform

    def __getitem__(self, index):
        """
        Args:
            index: the index of item
        Returns:
            image and its labels
        """
        image_name = self.image_file[index].strip('\n')
        raw_label = self.label_info.loc[self.label_info['Image_index'] == image_name]
        label = raw_label.iloc[:,0]
        image_name = os.path.join(self.data_dir, image_name)
        image = Image.open(image_name).convert('RGB')
        if self.transform is not None:
            image = self.transform(image)
        return image, label

    def __len__(self):
        return len(self.image_file)

构建好Dataset后,就可以使用DataLoader来按批次读入数据了:

from torch.utils.data import DataLoader
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)
#样本是按“批”读入的,batch_size就是每次读入的样本数
#num_workers:有多少个进程用于读取数据
#shuffle:是否将读入的数据打乱
#drop_last:对于样本最后一部分没有达到批次数的样本,使其不再参与训练

PyTorch中的DataLoader的读取可以使用next和iter来完成:

import matplotlib.pyplot as plt
images, labels = next(iter(val_loader))
print(images.shape)
plt.imshow(images[0].transpose(1,2,0))
plt.show()
模型构建

构造多层感知机:

import torch
from torch import nn  #Module 类是 nn 模块里提供的一个模型构造类,是所有神经网络模块的基类,我们可以继承它来定义我们想要的模型

class MLP(nn.Module):
  # 声明带有模型参数的层,这里声明了两个全连接层
  def __init__(self, **kwargs):  #用于创建模型参数
    # 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
    super(MLP, self).__init__(**kwargs)
    self.hidden = nn.Linear(784, 256)
    self.act = nn.ReLU()
    self.output = nn.Linear(256,10)
    
   # 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
  def forward(self, x):
    o = self.act(self.hidden(x))
    return self.output(o)   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值