深度学习实用代码块集锦

从零定义系列:

从零定义交叉熵损失函数(CrossEntropyLoss)

def cross_entropy(y_hat, y):
	return -torch.log(y_hat[range(len(y_hat)), y])

简洁实现系列:

重要知识/思想/技巧:

自定义块

1. 块的基本结构

331(类) 3

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


class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.hidden = nn.Linear(20, 256)
        self.out = nn.Linear(256, 10)
        
    def forward(self, X):
        return self.out(F.relu(self.hidden(X)))       

使用的时候只需:

net = MLP()
2. 块中的forward函数可以加入控制流

前向传播函数中也可以加入控制流

class FixedHiddenMLP(nn.Module):
  def __init__(self):
  	super().__init__()
  	self.rand_weight = torch.rand((20, 20), requires_grad=False)
  	self.linear = nn.Linear(20, 20)

  def forward(self, X):
  	X = self.linear(X)
  	X = F.relu(torch.mm(X, self.rand_weight) + 1)
  	X = self.linear(X)
  	while X.abs().sum > 1:
  		X /= 2
  	return X.sum()

以及这个最为代表性的3D影像项目中的net构建部分

class LunaModel(nn.Module):
    
    def __init__(self):
        
        super().__init__()
        
        self.conv1 = nn.Conv3d(1, 32, kernel_size=3, padding=1, bias=True)
        self.relu1 = nn.ReLU()
        self.maxpool1 = nn.MaxPool3d(2)
        
        self.conv2 = nn.Conv3d(32, 64, kernel_size=3, padding=1, bias=True)
        self.relu2 = nn.ReLU()
        self.maxpool2 = nn.MaxPool3d(2)
        
        self.flatten = nn.Flatten()
        
        self.fc1 = nn.Linear(2048, 1024)
        self.relu3 = nn.ReLU()
        
        self.dropout = nn.Dropout(0.2)
        
        self.fc2 = nn.Linear(1024, 2)
    
    def forward(self, X):
        
        # Dimensions of X => [BS, 1, 10, 18, 18]
        
        X = self.maxpool1(self.relu1(self.conv1(X)))
        X = self.maxpool2(self.relu2(self.conv2(X)))
        
        X = self.flatten(X)

        X = self.relu3(self.fc1(X))
        X = self.dropout(X)
        
        return self.fc2(X)
        
   
3. 神经网络块的灵活组织

nn.Sequential可以十分灵活非常灵活特别灵活地把神经网络块排列成一个组合,变成一个神经网络块

class NestMLP(nn.module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(20, 64), nn.ReLU(),
                                nn.Linear(64, 32), nn.ReLU())
        self.linear = nn.Linear(32, 16)
        
    def forward(self, X):
        X = self.net(X)
        X = self.linear(X)
        return X
    
chimera = nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP())

技巧 / 工具系列:

Xavier 参数初始化

Xavier参数初始化可以增加数值稳定性,在pytorch框架下,可以如下简洁实现
nn.init.xavier_uniform_(self.fcl1.weight)
在定义神经网络块时,可以这样用在其中

class MLP(nn.Module):
    
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        
        self.fcl1 = nn.Linear(5, 256)
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(0.3)
        nn.init.xavier_uniform_(self.fcl1.weight)
        
        self.fcl2 = nn.Linear(256, 256)
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(0.2)
        nn.init.xavier_uniform_(self.fcl2.weight)
        
        self.fcl3 = nn.Linear(256, 1)
        nn.init.xavier_uniform_(self.fcl3.weight)
        
    def forward(self, X):
        X = self.fcl1(X)
        X = self.relu1(X)
        X = self.dropout1(X)
        
        X = self.fcl2(X)
        X = self.relu2(X)
        X = self.dropout2(X)
        
        return self.fcl3(X)

jupyter notebook中显示cell运行时间

cell第一行写%%time即可

jupyter notebook中执行终端命令

!开头,后面可以直接写终端命令(注意之间没有空格)
eg. !pip install tqdm

循环(用于训练中)显示进度条tqdm

首先import tqdm
from tqdm import tqdm
如果是在jupyter notebook里最好用这个(好看)
from tqdm.notebook import tqdm
(注意一定要这么写,不能只写import tqdm)
然后在训练过程中示例如下

for epoch in range(num_epochs):
    for X, y in tqdm(data_iter):
        y_hat = net.forward(X)
        l = loss(y_hat.reshape(y.shape), y)
        trainer.zero_grad()
        l.sum().backward()
        trainer.step()
    print(f'epoch:{epoch + 1}, loss:{loss(net(features), labels).sum():.5f}')

延后初始化(defer initialization)

在神经网络中,延后初始化也是一种常见的技术。在 PyTorch 框架中,我们可以使用 torch.nn.Module 类的 lazy_init() 方法来实现延后初始化。

import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.linear = None
    
    def lazy_init(self, input_size, output_size):
        self.linear = nn.Linear(input_size, output_size)
    
    def forward(self, x):
        if self.linear is None:
            # 延后初始化,只有在需要时才进行线性层的初始化
            self.lazy_init(x.size(1), 10)
        x = self.linear(x)
        return x

在这个例子中,我们定义了一个名为 MyModel 的神经网络模型。模型中包含一个线性层,但在模型的初始化中并没有对该层进行初始化。相反,我们定义了一个 lazy_init() 方法,在该方法中对线性层进行了初始化。

在 forward() 方法中,我们首先检查线性层是否已经被初始化。如果没有被初始化,则调用 lazy_init() 方法对线性层进行初始化。然后将输入 x 传递给线性层,并返回线性层的输出。

通过这种方式,我们可以延迟对象的初始化,只有在需要时才进行对象创建或计算。这有助于减少内存使用和计算成本,从而提高神经网络的效率。

经验系列:

用l.sum().backward()和用l.mean().backward()的区别是什么

个人猜测两种损失Loss的计算方式要和不同的优化器Optimizer对应,因为根据梯度下降核心公式
θ j = θ j − α ∂ J ( θ ) ∂ θ j \theta_j = \theta_j - \alpha\frac{\partial{J(\theta)}}{\partial{\theta_j}} θj=θjαθjJ(θ)
J ( θ ) J(\theta) J(θ)不同时,计算出的梯度更新值不同。
一问ChatGPT,果不其然:

l.sum().backward()和l.mean().backward()是两种不同的梯度计算方式。

  1. l.sum().backward()将所有损失值相加得到总损失,并计算总损失对模型参数的梯度。这种方式对应于在优化器中使用torch.optim.SGD等梯度下降算法,并且使用的是批次的总损失来更新模型参数。因此,如果使用l.sum().backward(),则需要将reduction参数设置为’none’,以便能够计算批次中每个样本的损失值。
  2. l.mean().backward()将所有损失值相加后取平均得到平均损失,并计算平均损失对模型参数的梯度。这种方式对应于在优化器中使用torch.optim.Adam等自适应学习率算法,因为它们可以自适应地调整学习率,从而更好地优化平均损失。

一般来说,如果使用批次的总损失来更新模型参数,应该使用l.sum().backward(),而如果使用平均损失来更新模型参数,则应该使用l.mean().backward()。但是,在某些情况下,根据具体情况选择使用哪种方式会更好。

nn.MSELoss(reduction=‘none’)中的reduction='none’是什么

在 PyTorch 中,当计算 loss 时,通常是将所有样本的损失值取平均,得到该 batch 的平均损失。而 reduction 参数则控制着如何对所有样本的损失进行合并,具体包括以下三种方式:

  1. reduction='mean',将所有样本的损失值取平均,得到该 batch 的平均损失。
  2. reduction='sum',将所有样本的损失值加和,得到该 batch 的总损失。
  3. reduction='none',不进行任何处理,直接返回每个样本的损失值。这种方式通常用于需要对每个样本的损失进行单独处理的情况,例如带权重的损失计算。

因此,在 nn.MSELoss(reduction=‘none’) 中,损失函数直接返回每个样本的平方误差,而不是将它们取平均或加和。

assert float(torch.abs((Y1 - Y2)).sum()) < 1e-6这种方式来判断结果是否相等

这种方式被称为数值稳定性检查,它通过计算两个Tensor之间的绝对差的总和来检查它们是否足够相似。当两个Tensor具有相同的形状和元素值时,它们是相等的。但是,由于计算机的浮点精度限制,即使两个张量具有相同的形状和元素值,它们也可能有微小的舍入误差。因此,这种方式是为了确保两个Tensor非常接近并且这些差异可以忽略不计,即它们的总和应该小于某个足够小的阈值,例如1e-6。
在深度学习中,这种方式通常用于检查前向传递和反向传递是否正确,以确保模型的正确性和数值稳定性。

assert断言报错的时候显示自定义报错信息

如果你想让assert断言报错时显示自定义报错信息,可以在assert语句中添加一个字符串作为第二个参数,例如:

assert float(torch.abs((Y1 - Y2)).sum()) < 1e-6, "Y1 and Y2 are not equal"

这样,如果assert断言失败,会抛出AssertionError异常,并将自定义报错信息作为异常的错误信息。这样可以帮助你更好地理解为什么assert断言失败。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值