线性回归网络用预先选定的基函数变换输入,再由线性输出层组合这些特征。线性分类网络采用相同的仿射形式生成类别分数。两类模型的能力都受当前特征表示约束:如果基函数没有把目标规律展开成容易拟合的形状,调整输出层权重不会补上缺失的表示。

多层感知机(Multilayer Perceptron,MLP)把基函数本身也纳入训练。每个隐藏单元学习一个仿射方向,再经过非线性激活形成新的特征;后续层继续组合这些特征。这个改动扩大了函数族,也使训练从线性最小二乘变成非凸优化。

本文沿着“表示、输出、概率模型”三层关系展开。先说明固定基函数的限制、激活函数的作用和万能近似定理的适用范围,再写出 MLP 的网络结构。得到共享的特征表示以后,回归、多分类、迁移学习和混合密度网络(Mixture Density Network,MDN)都可以看成在同一个主干后接入不同输出头,并用与预测对象相符的损失训练。

固定基函数模型

固定基函数回归写成

f(x)=j=1Mwjϕj(x).f(\mathbf{x}) = \sum_{j=1}^{M}w_j\phi_j(\mathbf{x}).

训练只改变 wjw_j 时,可表示的函数位于 {ϕ1,,ϕM}\{\phi_1,\ldots,\phi_M\} 张成的有限维线性空间。多项式次数、径向基函数的中心与宽度、傅里叶频率等选择,已经在观察训练结果之前规定了模型会怎样看输入。若选中的方向与任务相符,这种结构简单、可解释,也可能得到闭式解;若关键变化不在这些方向上,优化器只能在错误的函数空间里寻找最优点。

手工扩充基函数还会迅速增加维度。DD 个变量中所有总次数不超过 pp 的单项式共有

M=(D+pp)M=\binom{D+p}{p}

项,其中包含常数项。D=100D=100p=3p=3 时已经有 176851176\,851 项。实际数据常常只沿少数方向发生有用的非线性变化,完整展开会同时带来存储、估计方差和优化条件数问题;只挑少量交叉项,又需要事先知道哪些变量应当组合。

含一层隐藏层的网络把第 jj 个特征改写为

hj(x)=ρ(ajTx+cj),h_j(\mathbf{x}) = \rho(\mathbf{a}_j^{\mathsf T}\mathbf{x}+c_j),

并计算

fθ(x)=j=1Hvjhj(x)+b.f_\theta(\mathbf{x}) = \sum_{j=1}^{H}v_jh_j(\mathbf{x})+b.

aj\mathbf{a}_jcjc_j 决定隐藏单元在输入空间中响应的位置与方向,vjv_j 再组合这些响应。它们都由数据估计,所以隐藏层可以理解为一组可学习的基函数。与固定基函数模型相比,MLP 减少了人工枚举特征的需要,代价是参数彼此耦合,通常要用梯度方法迭代求解,也不再拥有线性最小二乘的一般闭式解。

左右对照图,左侧输入连接三个灰色虚线固定基函数节点,只有绿色线性输出层的权重参与训练;右侧输入连接三个蓝色实线非线性隐藏单元,隐藏参数与绿色输出层一起训练
固定基函数模型只能在预先给定的特征空间中调整输出权重,多层感知机(MLP)还会学习隐藏特征的方向与偏置。灰色虚线表示固定节点,蓝色和绿色实线表示参与训练的参数化层。作者绘制

非线性激活

层数本身不会自动产生非线性。若两层之间没有激活函数,

h=W1x+b1,y=W2h+b2\begin{aligned} \mathbf{h} &=\mathbf{W}_1\mathbf{x}+\mathbf{b}_1,\\ \mathbf{y} &=\mathbf{W}_2\mathbf{h}+\mathbf{b}_2 \end{aligned}

可以合并为

y=(W2W1)x+(W2b1+b2).\mathbf{y} = (\mathbf{W}_2\mathbf{W}_1)\mathbf{x} + (\mathbf{W}_2\mathbf{b}_1+\mathbf{b}_2).

继续堆叠任意多个仿射层,结果仍是一项仿射变换。隐藏层中的逐元素非线性 ρ\rho 打断了这种合并,使下一层能够组合前一层形成的弯折、阈值或平滑响应。

常见激活函数各自保留不同的数值性质:

激活函数定义输出范围训练时需要留意的性质
sigmoidσ(z)=1/(1+ez)\sigma(z)=1/(1+e^{-z})(0,1)(0,1)大幅正负输入处导数接近 0,深层连乘时容易削弱梯度
tanhtanh(z)\tanh(z)(1,1)(-1,1)输出以 0 为中心,但两端同样会饱和
ReLUmax(0,z)\max(0,z)[0,+)[0,+\infty)正半轴梯度为 1;对所有训练样本都停在负半轴的单元可能不再更新

ReLU 在 z=0z=0 处不可导,训练框架会约定一个次梯度;单个点的这项约定通常不影响连续输入上的优化。它在每个线性区域内仍是线性的,但不同输入可以落入不同区域。多层 ReLU 网络由此把输入空间划分成许多多面体区域,并在每个区域使用不同的仿射函数。GELU、SiLU 等平滑激活改变了门控方式和梯度曲线,仍承担同一项结构职责:让相邻仿射层无法整体折叠成一层。

隐藏层激活与输出层变换需要分开设计。无界实数回归通常保留线性输出,多分类模型输出任意实数 logits,MDN 则分别对混合权重和尺度施加归一化与正值约束。把 sigmoid 或 Softmax 机械地接在每个输出后面,会改变模型所表达的变量范围及相应似然。

左右对照图,左侧输入连续经过两个仿射层,公式将它们合并为 Ax 加 c,下方输出是一条直线;右侧两层之间加入 ReLU,下方输出折线被三个折点分成四个线性区域
没有隐藏激活时,多层仿射变换仍等价于单个仿射函数;加入 ReLU 后,网络可以在不同输入区域使用不同斜率。右图的折点对应激活模式改变的位置。作者绘制

万能近似定理

单隐藏层网络可以写成若干岭函数之和:

fH(x)=j=1Hvjρ(ajTx+cj)+b.f_H(\mathbf{x}) = \sum_{j=1}^{H} v_j\rho(\mathbf{a}_j^{\mathsf T}\mathbf{x}+c_j)+b.

Cybenko 在 1989 年证明,对任意定义在单位超立方体上的连续函数 ff、任意 ε>0\varepsilon>0,使用连续 sigmoid 型激活并允许有限但足够多的隐藏单元,可以找到一组参数,使

supx[0,1]DfH(x)f(x)<ε.\sup_{\mathbf{x}\in[0,1]^D} \left|f_H(\mathbf{x})-f(\mathbf{x})\right| <\varepsilon.

后续结果放宽了激活函数条件。Leshno 等人在局部有界、分段连续等假设下说明,带偏置的前馈网络要在相应函数空间中具有稠密逼近能力,激活函数不能几乎处处等于某个多项式。ReLU 是非多项式函数,因此也具备这类逼近能力。这里的“万能”指函数族在指定空间中的稠密性,并不表示一个固定宽度的网络可以精确表示任意函数。

定理中的量词顺序很重要:先给定目标函数和误差容限,再断言存在某个宽度及某组参数。它没有给出随机初始化后怎样找到这些参数,也没有保证所需隐藏单元数量、训练时间或数值精度可以接受。训练样本只覆盖有限位置时,许多函数都能同样好地拟合这些点;逼近定理也不会从中挑出测试误差最低、校准最好或对扰动最稳定的一个。

单隐藏层在表达能力上“足够”,也不意味着深度没有作用。现实任务常包含可复用的组合结构,例如先识别局部模式,再组合成更大范围的模式。多层网络把中间表示逐级复用,因而改变了参数效率和归纳偏置。宽度、深度、优化方法、正则化与数据分布仍要通过理论条件和验证结果共同判断,不能由万能近似定理直接确定。

多层感知机

设输入宽度为 d0d_0,网络共有 L1L-1 个隐藏层,输出宽度为 dLd_L。一次前向计算为

h(0)=x,\mathbf{h}^{(0)}=\mathbf{x}, h()=ρ ⁣(W()h(1)+b()),=1,,L1,\mathbf{h}^{(\ell)} = \rho_\ell\!\left( \mathbf{W}^{(\ell)}\mathbf{h}^{(\ell-1)} +\mathbf{b}^{(\ell)} \right), \qquad \ell=1,\ldots,L-1,

以及

o=W(L)h(L1)+b(L).\mathbf{o} = \mathbf{W}^{(L)}\mathbf{h}^{(L-1)} +\mathbf{b}^{(L)}.

其中 W()Rd×d1\mathbf{W}^{(\ell)}\in\mathbb{R}^{d_\ell\times d_{\ell-1}}b()Rd\mathbf{b}^{(\ell)}\in\mathbb{R}^{d_\ell}。批量大小为 NN 时,工程实现通常把样本按行排列,隐藏张量形状为 (N,d)(N,d_\ell)。全连接层参数总数是

=1L(d1+1)d.\sum_{\ell=1}^{L}(d_{\ell-1}+1)d_\ell.

因此,将高维图像直接展平后接入宽隐藏层会产生大量参数;卷积神经网络用局部连接和权重共享处理这项结构问题。MLP 更适合已经整理成固定长度向量的数据,也常作为更大模型中的投影层、分类头或回归头。

下面的 PyTorch 实现把隐藏表示与任务输出头明确分开。output_dim=1 可以用于标量回归,output_dim=K 可以生成 KK 类 logits。forward() 不添加输出激活,输出的概率解释交给后面的损失函数。

import torch
from torch import nn


class MLP(nn.Module):
    def __init__(
        self,
        input_dim: int,
        hidden_dims: tuple[int, ...],
        output_dim: int,
    ) -> None:
        super().__init__()
        if not hidden_dims:
            raise ValueError("hidden_dims must contain at least one layer")

        layers: list[nn.Module] = []
        previous_dim = input_dim
        for hidden_dim in hidden_dims:
            layers.extend([nn.Linear(previous_dim, hidden_dim), nn.ReLU()])
            previous_dim = hidden_dim

        self.features = nn.Sequential(*layers)
        self.head = nn.Linear(previous_dim, output_dim)

    def forward_features(self, inputs: torch.Tensor) -> torch.Tensor:
        return self.features(inputs)

    def forward(self, inputs: torch.Tensor) -> torch.Tensor:
        return self.head(self.forward_features(inputs))


regressor = MLP(input_dim=20, hidden_dims=(64, 32), output_dim=1)
classifier = MLP(input_dim=20, hidden_dims=(64, 32), output_dim=5)

这段结构没有包含归一化、Dropout 或残差连接。它适合说明 MLP 的最小计算路径,但不是所有数据集的默认最优配置。输入量纲差异很大时,先按训练集统计量标准化通常更容易优化;层数继续增加后,初始化、归一化和残差路径会变得更重要。参数怎样由损失的梯度更新,可参照反向传播与自动微分梯度下降与优化器

输出层与损失函数

同一个 MLP 主干可以服务多种任务,但输出维度和损失必须来自同一项统计假设。若模型定义了条件分布 pθ(yx)p_\theta(y\mid\mathbf{x}),最大似然训练等价于最小化样本负对数似然

L(θ)=1Nn=1Nlogpθ(ynxn).\mathcal{L}(\theta) = -\frac{1}{N} \sum_{n=1}^{N} \log p_\theta(y_n\mid\mathbf{x}_n).

这个视角能解释常用损失,也能指出它们遗漏了什么。

标量回归若假设

yxN(μθ(x),σ2)y\mid\mathbf{x} \sim \mathcal{N}(\mu_\theta(\mathbf{x}),\sigma^2)

σ2\sigma^2 是固定常数,负对数似然中与 θ\theta 有关的部分正比于均方误差:

LMSE=1Nn=1N(ynμθ(xn))2.\mathcal{L}_{\text{MSE}} = \frac{1}{N} \sum_{n=1}^{N} \left(y_n-\mu_\theta(\mathbf{x}_n)\right)^2.

模型输出估计的是条件均值。若噪声强度随输入变化,可以让网络同时输出 μθ(x)\mu_\theta(\mathbf{x}) 和正方差 σθ2(x)\sigma_\theta^2(\mathbf{x}),并最小化

LGaussian=12Nn=1N[logσn2+(ynμn)2σn2+log(2π)].\mathcal{L}_{\text{Gaussian}} = \frac{1}{2N} \sum_{n=1}^{N} \left[ \log \sigma_n^2 + \frac{(y_n-\mu_n)^2}{\sigma_n^2} + \log(2\pi) \right].

实现时可以令 σn2=softplus(sn)+ϵ\sigma_n^2=\operatorname{softplus}(s_n)+\epsilon,避免网络给出非正方差。预测方差描述模型在当前条件分布假设下学到的观测散布,不自动包含参数不确定性,也不能替代分布外检测。

互斥的 KK 类分类把标签看成类别分布。网络输出 logits zRK\mathbf{z}\in\mathbb{R}^K,Softmax 给出

pθ(y=kx)=ezkj=1Kezj,p_\theta(y=k\mid\mathbf{x}) = \frac{e^{z_k}}{\sum_{j=1}^{K}e^{z_j}},

单个样本的交叉熵为

LCE=logpθ(yx)=zy+logj=1Kezj.\mathcal{L}_{\text{CE}} = -\log p_\theta(y\mid\mathbf{x}) = -z_y+\log\sum_{j=1}^{K}e^{z_j}.

计算中的 logsumexp 形式避免直接对很大或很小的指数求和。PyTorch 的 CrossEntropyLoss 接收未归一化 logits,并在内部完成数值稳定的 LogSoftmax 与负对数似然;训练前不应先调用 Softmax。多标签任务中每个类别可以独立为真,输出契约不同,应使用每类一个 logit 的 BCEWithLogitsLoss,不能把它与互斥多分类混用。

任务输出头目标张量常用训练损失输出含义
同方差点回归qq 个无界实数浮点数,形状 (N,q)(N,q)MSE条件均值
异方差高斯回归qq 个均值与正方差浮点数,形状 (N,q)(N,q)Gaussian NLL单峰条件高斯分布
KK 类互斥分类KK 个 logits类别索引,形状 (N,)(N,)Cross-entropy类别分布的未归一化分数
连续一对多回归混合权重、均值与尺度浮点数Mixture NLL多峰条件密度

最小代码可以直接保留这种契约:

from torch.nn import functional as F

regression_values = regressor(features).squeeze(-1)
regression_loss = F.mse_loss(regression_values, regression_targets)

class_logits = classifier(features)
classification_loss = F.cross_entropy(class_logits, class_indices)

损失的均值方式还会改变梯度尺度。比较不同批量大小、类别权重或多任务损失时,需要确认分母究竟是样本数、有效元素数还是权重之和。最终评价也应对应使用场景:回归的平均误差无法验证预测区间,多分类准确率无法检查概率校准,单个指标很少能覆盖输出分布的全部含义。

迁移学习

把模型写成

f(x)=gψ ⁣(ϕω(x))f(\mathbf{x}) = g_\psi\!\left(\boldsymbol{\phi}_\omega(\mathbf{x})\right)

后,ϕω\boldsymbol{\phi}_\omega 是特征主干,gψg_\psi 是任务输出头。迁移学习先在源数据或源任务上估计 ω\omega,再把这些参数用于目标任务。目标训练由此从一组经过数据选择的基函数起步,无需随机初始化全部表示。

最保守的做法是冻结主干,只训练新输出头,也称固定特征提取或线性探测。它训练快、需要估计的参数少,还能直接检验源模型的表示对目标标签是否有用。若验证结果受限,再解冻靠近输出的层或整个主干进行微调。微调通常给新输出头较大学习率,给预训练参数较小学习率,降低一次更新破坏已有表示的幅度;具体比例仍应由目标任务验证集确定。

上方源任务用数据预训练三个特征块和源任务输出头,下方目标任务分成固定特征提取、局部微调和全量微调三列;灰色虚线块标记冻结参数,绿色实线块标记更新参数,三列都使用新的目标任务输出头
迁移学习的三种常用方案只在目标任务中允许更新的参数范围上不同。固定特征提取仅训练新头,局部微调解冻靠近输出的特征块,全量微调更新整个主干;灰色虚线与绿色实线分别表示冻结和更新。作者绘制
from torch.optim import AdamW

# 固定特征提取:只优化新任务的输出头。
model.features.requires_grad_(False)
optimizer = AdamW(model.head.parameters(), lr=1e-3)

# 全量微调:主干使用更小的学习率。
model.features.requires_grad_(True)
optimizer = AdamW(
    [
        {"params": model.features.parameters(), "lr": 1e-4},
        {"params": model.head.parameters(), "lr": 1e-3},
    ]
)

requires_grad_(False) 只停止参数梯度。若主干含 BatchNorm 或 Dropout,外层调用 model.train() 后,它们仍会进入训练模式:BatchNorm 可能更新运行统计量,Dropout 也会继续随机丢弃激活。需要完全固定特征提取器时,应在每次切换训练模式后令 model.features.eval();若目标域差异较大并计划更新归一化统计量,则应把这项选择当作实验变量记录,而不是无意中改变。

迁移能否成立取决于源任务、目标任务和输入处理。Yosinski 等人在自然图像卷积网络上的实验发现,靠前特征在他们的任务间更通用,靠后特征更容易针对源任务专门化;随着任务距离增大,可迁移性下降。这是特定网络与视觉任务上的实证结果,不能直接推广为所有模态的固定分层规律。实际流程还要保持与预训练权重匹配的分词、尺寸、通道顺序和归一化,并在目标验证集上比较随机初始化、冻结主干与不同解冻范围。源数据与目标测试集存在重叠时,预训练也可能引入评估泄漏。

混合密度网络

平方损失适合把条件均值作为最终动作的任务,却无法完整描述多峰条件分布。考虑带少量噪声的关系 x=y2x=y^2。给定 x>0x>0 后,合理目标位于 y+xy\approx+\sqrt{x}yxy\approx-\sqrt{x} 两支附近。两支权重相同时,条件均值接近 0,而 y=0y=0 在大多数 xx 处并不是一条合理分支。

左侧坐标图显示 y 等于正负根号 x 的上下两支样本关系,在固定 x0 处有两个合理目标,而平方损失的条件均值位于两支之间;右侧条件密度沿 y 轴形成对应的上下两个峰
一对多回归中的条件均值可能落在低密度区域。MDN 在同一个输入 x₀ 处保留两个带权高斯分量,能够表示上下两支目标及各自尺度。作者绘制

MDN 让神经网络输出混合分布的参数。对标量目标和 KK 个高斯分量,条件密度为

p(yx)=k=1Kπk(x)N ⁣(y;μk(x),σk2(x)),p(y\mid\mathbf{x}) = \sum_{k=1}^{K} \pi_k(\mathbf{x}) \mathcal{N}\!\left( y; \mu_k(\mathbf{x}), \sigma_k^2(\mathbf{x}) \right),

其中

πk(x)0,k=1Kπk(x)=1,σk(x)>0.\pi_k(\mathbf{x})\ge 0, \qquad \sum_{k=1}^{K}\pi_k(\mathbf{x})=1, \qquad \sigma_k(\mathbf{x})>0.

网络可以输出三组无约束实数:混合 logits aka_k、均值 μk\mu_k 和原始尺度 sks_k,再变换为

logπ=log_softmax(a),σk=softplus(sk)+σmin.\log\boldsymbol{\pi} = \operatorname{log\_softmax}(\mathbf{a}), \qquad \sigma_k = \operatorname{softplus}(s_k)+\sigma_{\min}.

对样本 (xn,yn)(\mathbf{x}_n,y_n),训练目标是混合密度的负对数似然:

LMDN=1Nn=1Nlogk=1KπnkN(yn;μnk,σnk2).\mathcal{L}_{\text{MDN}} = -\frac{1}{N} \sum_{n=1}^{N} \log \sum_{k=1}^{K} \pi_{nk} \mathcal{N}(y_n;\mu_{nk},\sigma_{nk}^2).

直接先算每个很小的密度再求和,容易发生浮点下溢。稳定实现应在对数域中计算各分量的 log_prob,加上 log_weights 后使用 torch.logsumexp

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


class ScalarMDN(nn.Module):
    def __init__(self, input_dim: int, hidden_dim: int, components: int) -> None:
        super().__init__()
        self.features = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.Tanh(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.Tanh(),
        )
        self.mixture_logits = nn.Linear(hidden_dim, components)
        self.means = nn.Linear(hidden_dim, components)
        self.raw_scales = nn.Linear(hidden_dim, components)

    def forward(self, inputs: torch.Tensor):
        hidden = self.features(inputs)
        log_weights = F.log_softmax(self.mixture_logits(hidden), dim=-1)
        means = self.means(hidden)
        scales = F.softplus(self.raw_scales(hidden)) + 1e-4
        return log_weights, means, scales


def mdn_nll(
    log_weights: torch.Tensor,
    means: torch.Tensor,
    scales: torch.Tensor,
    targets: torch.Tensor,
) -> torch.Tensor:
    # targets 的形状为 (N,),扩展后与 (N, K) 个分量广播。
    component_log_prob = Normal(means, scales).log_prob(targets.unsqueeze(-1))
    sample_log_prob = torch.logsumexp(
        log_weights + component_log_prob,
        dim=-1,
    )
    return -sample_log_prob.mean()

混合分量数 KK 是模型选择的一部分。过少的分量会合并不同模式,过多的分量则可能冗余,并让某个分量把尺度缩到接近零来记忆个别样本。尺度下限、合适的正则化、独立验证集和训练监控可以降低这类退化风险。对于向量目标,上述高斯还要选择对角、低秩或完整协方差;对角协方差参数少,但无法表达同一分量内各目标维度的相关性。

分量编号没有天然语义。训练中两个分量可以交换编号而不改变总密度,相邻输入处的“第一个分量”也未必对应同一条物理分支。若系统需要连续轨迹或稳定身份,还要加入时序模型、匹配规则或带语义的结构约束。

推理阶段应先明确需要什么动作。按平方损失输出单个数时,MDN 的条件均值仍是

E[yx]=k=1Kπk(x)μk(x),\mathbb{E}[y\mid\mathbf{x}] = \sum_{k=1}^{K}\pi_k(\mathbf{x})\mu_k(\mathbf{x}),

它仍可能位于两个模式之间。需要多个可行候选时,可以返回各分量均值、权重和尺度;需要保持随机性时,可以先按 π\boldsymbol{\pi} 抽取分量,再从对应高斯采样。分量尺度不同时,权重最大的分量均值也未必是总密度的全局众数,因此不能把“最大权重分量”与精确 MAP 解混为一谈。MDN 的评价至少应包含留出数据负对数似然,并根据应用检查区间覆盖率、候选召回率或下游决策成本。

表示学习与条件分布

固定基函数模型先规定特征,再估计线性权重;MLP 则用仿射层和非线性激活共同学习特征。万能近似定理说明这类网络在明确的函数空间与误差标准下具有充分表达能力,但参数能否由有限数据高效学得,仍由优化、归纳偏置和验证结果决定。

网络输出的意义来自输出头与损失的组合。同方差高斯似然导出 MSE,类别似然导出多分类交叉熵,输入相关方差把回归扩展为单峰条件分布。迁移学习复用已经学到的隐藏表示,适合用冻结、局部解冻和全量微调逐步检验目标任务需要改动多少参数。

当一个输入对应多个连续目标时,单个均值会丢失模式。MDN 用网络预测混合权重、均值和尺度,再以混合负对数似然训练,从而保留完整得多的条件分布。选择 MLP、迁移方案或 MDN 时,可以依次检查三件事:当前表示能否分离关键结构,输出分布是否符合目标的不确定性,部署动作是否真的使用了模型提供的信息。

参考文献

  1. G. Cybenko. Approximation by Superpositions of a Sigmoidal Function. Mathematics of Control, Signals and Systems, 1989. DOI: 10.1007/BF02551274
  2. M. Leshno, V. Y. Lin, A. Pinkus, and S. Schocken. Multilayer Feedforward Networks with a Nonpolynomial Activation Function Can Approximate Any Function. Neural Networks, 1993. DOI: 10.1016/S0893-6080(05)80131-5
  3. I. Goodfellow, Y. Bengio, and A. Courville. Deep Learning, Chapter 6: Deep Feedforward Networks. MIT Press, 2016
  4. S. J. Pan and Q. Yang. A Survey on Transfer Learning. IEEE Transactions on Knowledge and Data Engineering, 2010. DOI: 10.1109/TKDE.2009.191
  5. J. Yosinski, J. Clune, Y. Bengio, and H. Lipson. How Transferable Are Features in Deep Neural Networks?. NIPS, 2014
  6. C. M. Bishop. Mixture Density Networks. Aston University Technical Report NCRG/94/004, 1994
  7. PyTorch Contributors. CrossEntropyLoss Documentation. PyTorch Documentation
  8. PyTorch Contributors. Transfer Learning for Computer Vision Tutorial. PyTorch Tutorials