关于pytorch官网教程中的What is torch.nn really?(二)


原文在这里 What is torch.nn really?
本来并没有打算分篇,只是写着写着发现已经很长了,于是就分了。
接下来就是逐步用 torch.nn中的函数来替换前面手搓的神经网络。

Using torch.nn.functional

使用pytorch的nn,对于我们的代码有这么几点好处:更短,更好理解,更灵活。
第一步,使用torch.nn.functional提供的activation和loss函数,来代替我们手写的。

import torch.nn.functional as F

loss_func = F.cross_entropy

def model(xb):
    return xb @ weights + bias

通常torch.nn.functionalimportF,当然这只是个所谓的习惯或者惯例。

先比较一下之前的定义:

def log_softmax(x):
    return x - x.exp().sum(-1).log().unsqueeze(-1)
    
def nll(input, target):
    return -input[range(target.shape[0]), target].mean()

loss_func = nll

def model(xb):
    return log_softmax(xb @ weights + bias)

显然,区别是

  • 使用F.cross_entropy作为loss function,移除了log_softmaxnll的定义。
  • model不再调用log_softmax

前面已经提到过在pytorch中nll+log_softmax等价于cross_entropy,详情就看相关文档吧。

然而,调用方式是没变的,仍然还是

print(loss_func(model(xb), yb), accuracy(model(xb), yb))

运行结果也是一样的。当然到这里还没有开始训练。
从这里我们可以验证一下nillog_softmaxcross_entropy的关系:
新代码中实际上是cross_entropy(xb @ weights + bias)
原本的代码则是nll(log_softmax(xb @ weights + bias))
所以,就是这样了。

Refactor using nn.Module

接下来使用 nn.Modulenn.Parameter。注意这里的Modulenn中的一个类,是大写的M,不要与python中的module的概念混淆起来。

from torch import nn

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.weights = nn.Parameter(torch.randn(784, 10) / math.sqrt(784))
        self.bias = nn.Parameter(torch.zeros(10))

    def forward(self, xb):
        return xb @ self.weights + self.bias

显然,这里定义的Mnist_Logisticnn.Module的一个子类。在这个类里边,保存了参数weightsbias,必须要注意的地方在于,这里与前面不同,没有标注requires_grad,
因为Parameter的定义是这样的:
class torch.nn.parameter.Parameter(data=None, requires_grad=True)
当然,上面的78410这种所谓的magic number看上去还是挺碍眼的,不过这里就先不管吧。
除了保存参数之外,还定义了一个forward方法,其内容等价于前面定义的model
于是model的定义又改版了,从一个函数变成了一个对象,这也是符合程序设计思想的演变:

model = Mnist_Logistic()

然而,调用方式还是没有变的:

print(loss_func(model(xb), yb))

不要奇怪上面的调用方式,pytorch的底层逻辑保证这里model(xb)调用的是model.forward(xb),我想,这里不应该有什么疑问。

接下来,是定义一个函数来封装训练过程:

def fit():
    for epoch in range(epochs):
        for i in range((n - 1) // bs + 1):
            start_i = i * bs
            end_i = start_i + bs
            xb = x_train[start_i:end_i]
            yb = y_train[start_i:end_i]
            pred = model(xb)
            loss = loss_func(pred, yb)

            loss.backward()
            with torch.no_grad():
                for p in model.parameters():
                    p -= p.grad * lr
                model.zero_grad()

可以看到,在with torch.no_grad()上下文中的变化,一目了然,就不废话了。

接下来做个check,当然,是先训练,然后打印一下看看loss是否下降:

fit()
print(loss_func(model(xb), yb))
Refactor using nn.Linear

接下来改进Mnist_Logistic的定义

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin = nn.Linear(784, 10)

    def forward(self, xb):
        return self.lin(xb)

nn.Linear类构造了一个线性层,代替了前面手工定义self.weightsself.bias, 以及xb @ self.weights + self.bias
nn.Linear声明是这样的:

class torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

在其定义中,有这么一些语句:

def __init__(self, in_features: int, out_features: int, bias: bool = True,
                 device=None, dtype=None) -> None:
        factory_kwargs = {
   
   'device': device, 'dtype': dtype}
        super(Linear, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.empty((out_features, in_features), **factory_kwargs))
        if bias:
            self.bias = Parameter(torch.empty
### PyTorch 中 `torch.nn.utils.weight_norm` 的弃用情况 在 PyTorch 的开发历程中,某些功能可能会被标记为过时并最终移除。关于 `torch.nn.utils.weight_norm` 的具体弃用版本信息,在官方文档和社区讨论中并未明确指出其完全废弃的时间点[^1]。然而,可以确认的是,自 PyTorch 1.7 版本起,`weight_norm` 功能已被推荐使用更灵活的替代方案——通过 `nn.Parameter` 或者其他模块化设计来实现权重标准化的效果。 #### 替代方案 对于需要实现类似 `weight_norm` 功能的需求,开发者通常会采用以下几种方式: 1. **手动实现权重规范化** 可以通过定义新的参数并将原始权重分解为其大小和方向部分来模拟 `weight_norm` 的行为。例如: ```python import torch import torch.nn as nn class WeightNormLinear(nn.Module): def __init__(self, in_features, out_features): super(WeightNormLinear, self).__init__() self.weight_v = nn.Parameter(torch.randn(out_features, in_features)) self.weight_g = nn.Parameter(torch.ones(out_features)) self.bias = nn.Parameter(torch.zeros(out_features)) def forward(self, x): weight_normalized = self.weight_g.unsqueeze(-1) * (self.weight_v / torch.norm(self.weight_v, dim=-1, keepdim=True)) return torch.matmul(x, weight_normalized.t()) + self.bias ``` 2. **利用 Spectral Normalization** 如果目标是为了控制模型的 Lipschitz 常数,可以考虑使用谱归一化(Spectral Normalization),它可以通过 `torch.nn.utils.spectral_norm` 实现类似的约束效果[^4]。 3. **基于最新 API 进行重构** 自 PyTorch 高级优化器引入以来,许多旧的功能逐渐被淘汰。因此建议重新评估需求,并尝试借助更高层次的设计模式完成相同的目标。 需要注意的是,尽管目前尚未有确切记录表明某个特定版本正式删除了该函数,但在实际项目维护过程中应尽量避免依赖已标注为 deprecated 的特性[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值