pytorch 入门教程 & 常用知识整理

Tensor

detach()

这个是针对想冻结网络model里面的某些参数不参与训练时使用的。并且它是会返回一个新的tensor。

y = x.detach()

这种情况下,y和x共同一个内存,即一个修改另一个也会跟着改变。此时x还是可以正常求导。所以很多时候用的时候是x = x.detach()。

torch.cat 与 torch.stack

torch.cat不会增加新的维度,原来几个维度,还是几个维度
torch.stack会增加一个新的维度,让n维的tensor变成n+1维

x1_torch = torch.zeros(3,1)
y1_torch = torch.ones(3,1)

xy_1 = torch.cat([x1_torch, y1_torch], dim = 1)
xy_2 = torch.stack([x1_torch, y1_torch], dim=1)

xy_1的shape=(3,2)

xy_1 = tensor(
[[0., 1.],
[0., 1.],
[0., 1.]])

xy_2的shape= (3,2,1)

xy_2 = tensor(
[ [[0.],[1.]],
[[0.],[1.]],
[[0.], [1.]] ])

矩阵运算

x = torch.Tensor([[1,2],[3,4]])
矩阵相乘 按照矩阵运算法则:

y1 = x @ x.T
y2 = x.matmul(x.T)

y1=y2=tensor(
[[ 5., 11.],
[11., 25.]])

元素之间相乘:

y3 = x * x.T
y4 = x.mul(x.T)

y3 = y4 = tensor(
[[ 1., 4.],
[ 9., 16.]])

与numpy数据交换:

Tensor --> numpy

x_numpy = x_torch.numpy()

numpy --> Tensor

x_torch = torch.from_numpy(x_numpy)

但是需要注意的是,这两个之间共享底层存储,所以更改一个会影响另一个。如果要想阻断之间的关系,可以在numpy数据后面加上 copy()。

数据处理

Datasets & DataLoaders

pytorch的数据处理中最关键的就是这两个类

  • torch.utils.data.DataLoader 主要用于训练的迭代器,使用比较简单
  •   train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
    
  • torch.utils.data.Dataset 做自己数据集的关键类,最关键的是继承类中一定要重载__init____len____getitem__这三个函数。常用模板如下:
  •   class Dataset_name(Dataset):
      	def __init__(self, flag='train'):
          	assert flag in ['train', 'test', 'valid']
          	self.flag = flag
         	self.__load_data__()
    
      	def __getitem__(self, index):
          	pass
      	def __len__(self):
          	pass
    
      	def __load_data__(self, csv_paths: list):
          	pass
          	print(
              "train_X.shape:{}\ntrain_Y.shape:{}\nvalid_X.shape:{}\nvalid_Y.shape:{}\n"
              .format(self.train_X.shape, self.train_Y.shape, self.valid_X.shape, self.valid_Y.shape))
    

`

Torchvision.transform

在训练之前,数据基本都需要进行数据增强,各种剪裁等数据变换,因此这个模块是必不可少的。

torchvision.transforms.Compose

这是一个类,主要作用是把所有的数据处理集合成一个操作

  • 输入参数:它接收的元素为Torchvision.transform里面的方法组成的list
  •   T =  transforms.Compose([transforms.CenterCrop(10), transforms.ToTensor()])
    
  • 使用方法:直接对该类的实例传入图片即可,图片的格式需要是PIL或者Tensor。如果是opencv,需要把ToTensor()方法放到第一个。它调用定义的源码为:
  •      def __call__(self, img):
          for t in self.transforms:
              img = t(img)
          return img
    
  • 该类不可用于转移动端的代码,如果转移动端,需要将其替换为如下,并且输出还必须只能是Tensor类型。
   transforms = torch.nn.Sequential(
   transforms.CenterCrop(10),
   transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
)
scripted_transforms = torch.jit.script(transforms)

torchvision.transforms.ToTensor

因为图片的读取一般分为opencv和PIL,但都需要转为tensor,或者互转,opencv中的存储方式为(h,w,c),但是tensor中一般都存储为(c,h,w),而pil也是一种特殊的格式;但是这一切都可以用ToTensor一键搞定。

#img_cv.shape = (448, 800, 3)
#img_pil.size = (800, 448)
cv2tensor =  torchvision.transforms.ToTensor()(img_cv)
#cv2tensor.shape : torch.Size([3, 448, 800])
pil2tensor = torchvision.transforms.ToTensor()(img_pil)
#pil2tensor.shape : torch.Size([3, 448, 800])

搭建模型 build model

搭建模型包括:

  1. 构建模型,其中又包括:
    • 定义单独的网络层,即__init__函数;
    • 把它们拼在一起,确定各层的执行顺序,即forward函数。
  2. 权值初始化。

torch.nn

PyTorch 把与深度学习模型搭建相关的全部类全部在 torch.nn 这个子模块中。
根据类的功能分类,常用的有如下几个部分:

  • 模型运算层:
    • 卷积层:torch.nn.Conv2d
    • 池化层:torch.nn.MaxPool2d
    • 线性层:torch.nn.Linear
    • 等等
  • 容器类,如 torch.nn.Module
  • 工具函数类 Utilities,用的较少,日后遇见可以补充。

而在 torch.nn 下面还有一个子模块 torch.nn.functional,基本上是 torch.nn里对应类的函数,比如torch.nn.ReLU的对应函数是 torch.nn.functional.relu,这两者的功能一样,运行效率也不同,但是也有很大的区别:

  1. 调用方式不同 torch.nn.XXX是类,使用前必须先进行实例化,而torch.nn.functional是函数,使用前必须传入所有参数,如果是卷积层需要传入权重。
inputs = torch.rand(64, 3, 244, 244)
conv = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
out = conv(inputs)

weight = torch.rand(64,3,3,3)
bias = torch.rand(64) 
out = nn.functional.conv2d(inputs, weight, bias, padding=1)
  1. nn.Xxx继承于nn.Module, 能够很好的与nn.Sequential结合使用, 而nn.functional.xxx无法与nn.Sequential结合使用。
  2. nn.Xxx是nn.functional.xxx的类封装,并且nn.Xxx都继承于一个共同祖先nn.Module。这一点导致nn.Xxx除了具有nn.functional.xxx功能之外,内部附带了nn.Module相关的属性和方法,例如train(), eval(),load_state_dict, state_dict 等。所以一般尽量选择torch.nn的方式来进行模型构建。
    因为上述区别,PyTorch官方推荐:
    具有学习参数的(例如,conv2d, linear, batch_norm)采用nn.Xxx方式;
    没有学习参数的(例如,maxpool, loss func, activation func)等根据个人选择使用nn.functional.xxx或者nn.Xxx方式。如果要某些层参数共享,则需要通过这种方式。
    但关于dropout,个人强烈推荐使用nn.Xxx方式,因为一般情况下只有训练阶段才进行dropout,在eval阶段都不会进行dropout。使用nn.Xxx方式定义dropout,在调用model.eval()之后,model中所有的dropout layer都关闭,但以nn.function.dropout方式定义dropout,在调用model.eval()之后并不能关闭dropout。

nn.Module

所有自定义的神经网络都要继承 torch.nn.Module。定义单独的网络层在 init 函数中实现,把定义好的网络层拼接在一起在 forward 函数中实现。继承了nn.Moudle的模型类有两个重要的函数:parameters 存储了模型的权重;modules 存储了模型的结构。

功能图:
pytorch 入门教程 & 常用知识整理_第1张图片

继承关系图:
pytorch 入门教程 & 常用知识整理_第2张图片

3个搭建模型的辅助工具

nn.Sequential

这是一个序列容器,既可以放在模型外面单独构建一个模型,也可以放在模型里面成为模型的一个层。

# 单独成为一个模型
model1 = nn.Sequential(
          nn.Conv2d(1,20,5),
          nn.ReLU(),
          nn.Conv2d(20,64,5),
          nn.ReLU()
        )
# 成为模型的一部分
class LeNetSequential(nn.Module):
    def __init__(self, classes):
        super(LeNetSequential, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),)

        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, classes),)

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        x = self.classifier(x)
        return x

放在模型里面的话,模型还是需要 init 和 forward 函数。

这样构建出来的模型的层没有名字:

>>> model2 = nn.Sequential(
...           nn.Conv2d(1,20,5),
...           nn.ReLU(),
...           nn.Conv2d(20,64,5),
...           nn.ReLU()
...         )
>>> model2
Sequential(
  (0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
  (1): ReLU()
  (2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
  (3): ReLU()
)

为了方便区分不同的层,我们可以使用 collections 里的 OrderedDict 函数:

>>> from collections import OrderedDict
>>> model3 = nn.Sequential(OrderedDict([
...           ('conv1', nn.Conv2d(1,20,5)),
...           ('relu1', nn.ReLU()),
...           ('conv2', nn.Conv2d(20,64,5)),
...           ('relu2', nn.ReLU())
...         ]))
>>> model3
Sequential(
  (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
  (relu1): ReLU()
  (conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
  (relu2): ReLU()
)

torch.nn.ModuleList

将网络层存储进一个列表,可以使用列表生成式快速生成网络,生成的网络层可以被索引,也拥有列表的方法 append,extend 或 insert。提高搭建模型效率。

>>> class MyModule(nn.Module):
...     def __init__(self):
...         super(MyModule, self).__init__()
...         self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
...         self.linears.append(nn.Linear(10, 1)) # append
...     def forward(self, x):
...         for i, l in enumerate(self.linears):
...             x = self.linears[i // 2](x) + l(x)
...         return x
    
>>> myModeul = MyModule()
>>> myModeul
MyModule(
  (linears): ModuleList(
    (0): Linear(in_features=10, out_features=10, bias=True)
    (1): Linear(in_features=10, out_features=10, bias=True)
    (2): Linear(in_features=10, out_features=10, bias=True)
    (3): Linear(in_features=10, out_features=10, bias=True)
    (4): Linear(in_features=10, out_features=10, bias=True)
    (5): Linear(in_features=10, out_features=10, bias=True)
    (6): Linear(in_features=10, out_features=10, bias=True)
    (7): Linear(in_features=10, out_features=10, bias=True)
    (8): Linear(in_features=10, out_features=10, bias=True)
    (9): Linear(in_features=10, out_features=10, bias=True)
    (10): Linear(in_features=10, out_features=1, bias=True) # append 进的层
  )
)

torch.nn.ModuleDict

这个函数与上面的 torch.nn.Sequential(OrderedDict(…)) 的行为非常类似,并且拥有 keys,values,items,pop,update 等词典的方法:

>>> class MyDictDense(nn.Module):
...     def __init__(self):
...         super(MyDictDense, self).__init__()
...         self.params = nn.ModuleDict({
...                 'linear1': nn.Linear(512, 128),
...                 'linear2': nn.Linear(128, 32)
...         })
...         self.params.update({'linear3': nn.Linear(32, 10)}) # 添加层

...     def forward(self, x, choice='linear1'):
...         return torch.mm(x, self.params[choice])

>>> net = MyDictDense()
>>> print(net)
MyDictDense(
  (params): ModuleDict(
    (linear1): Linear(in_features=512, out_features=128, bias=True)
    (linear2): Linear(in_features=128, out_features=32, bias=True)
    (linear3): Linear(in_features=32, out_features=10, bias=True)
  )
)

>>> print(net.params.keys())
odict_keys(['linear1', 'linear2', 'linear3'])

>>> print(net.params.items())
odict_items([('linear1', Linear(in_features=512, out_features=128, bias=True)), ('linear2', Linear(in_features=128, out_features=32, bias=True)), ('linear3', Linear(in_features=32, out_features=10, bias=True))])

pytorch autograd 自动求导机制

PyTorch 的 Autograd
目录:

  • 计算图
  • 一个具体的例子
  • 叶子张量
  • inplace 操作
  • 动态图,静态图

浅谈 PyTorch 中的 tensor 及使用
目录:

  • tensor.requires_grad
  • torch.no_grad()
  • 反向传播及网络的更新
  • tensor.detach()
  • CPU and GPU
  • tensor.item()

这两天篇文章关于自动求导机制讲的太深入浅出了,完全不需要自己总结,直接照搬即可……

Train and Optimizing

从上一章知道自动求导机制之后,其实模型训练基本就呼之欲出了。

  1. 设置loss函数
  2. 设置优化函数
  3. 实施后向计算即可:
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

所以常规的网络模型训练的模版如下:

model = Your_model().to(args.device)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(Your_model.parameters(),lr=args.learning_rate)

train_loss = []
valid_loss = []
train_epochs_loss = []
valid_epochs_loss = []


def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        # Compute prediction and loss
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


def test_loop(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    with torch.no_grad():
        for X, y in dataloader:
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")


epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(train_dataloader, model, loss_fn, optimizer)
    test_loop(test_dataloader, model, loss_fn)
print("Done!")

save and load model

只保存参数

一般的训练只需要保存参数,这样更方便

model = models.vgg16(pretrained=True)
torch.save(model.state_dict(), 'model_weights.pth')

在读取这个参数时,必须先对模型进行实例化,才能load参数。

model = models.vgg16() # we do not specify pretrained=True, i.e. do not load default weights
model.load_state_dict(torch.load('model_weights.pth'))
model.eval()

保存参数+网络结构

#保存参数+网络结构
torch.save(model, 'model.pth')
#加载
model = torch.load('model.pth')

Torchvision

torchvision.utils.make_grid

输入参数:

  • shape =(B x C x H x W)的tensor 或者 形状一致的tensor组成的list。
  • nrow :这一行显示多少张图片,如果要加一个batch=8的一组图片,nrow=2,这就是显示4行。默认是8
    这个函数的输出可以做为Tensorboard中的add_image的输入参数
dataiter = iter(trainloader)
images, labels = dataiter.next()
img_grid = torchvision.utils.make_grid(images,nrow=2)

torchvision.utils.save_image

可视化工具Tensorboard

主要分为:实例化writer --> 写入数据 --> 浏览器查看

实例化writer
tensorboard在一个文件夹下,只能读取一个event文件,所以实例化的时候需要利用时间戳创建不同的文件夹,避免一个文件夹出现多个文件。

from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('./runs/' + time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time())))

这样便会在当前目录下创建:
pytorch 入门教程 & 常用知识整理_第3张图片
写入数据
最主要是写入loss这样的训练数据,然后也可以查看当前训练结果。

if (i + 1) % args.log_train_loss_interval == 0:
	writer.add_scalar('loss', loss, step)

if (i + 1) % args.log_train_images_interval == 0:
	writer.add_image('train_pred_pha', make_grid(pred_pha, nrow=5), step)

浏览器查看

进入到runs目录,运行:

tensorboard --logdir=./

如果是在远程服务器上,本地打开cmd,执行:

ssh -L 16006:127.0.0.1:6006 [email protected]

登录进去之后,在浏览器输入:

http://127.0.0.1:16006/

你可能感兴趣的:(深度学习,pytorch,深度学习,python)