深度学习基础概念

反向传播算法(BP)的概念及简单推导

反向传播(Backpropagation,BP)算法是一种与最优化方法(如梯度下降法)结合使用的,用来训练人工神经网络的常见算法。BP算法对网络中所有权重计算损失函数的梯度,并将梯度反馈给最优化方法,用来更新权值以最小化损失函数。该算法会先按前向传播方式计算(并缓存)每个节点的输出值,然后再按反向传播遍历图的方式计算损失函数值相对于每个参数的偏导数。

接下来我们以全连接层,使用sigmoid激活函数,Softmax+MSE作为损失函数的神经网络为例,推导BP算法逻辑。

首先,我们看看sigmoid激活函数的表达式及其导数:

$$sigmoid表达式:\sigma(x) = \frac{1}{1+e^{-x}}$$
$$sigmoid导数:\frac{d}{dx}\sigma(x) = \sigma(x) - \sigma(x)^2 = \sigma(1- \sigma)$$

可以看到sigmoid激活函数的导数最终可以表达为输出值的简单运算。

我们再看MSE损失函数的表达式及其导数:

$$MSE损失函数的表达式:L = \frac{1}{2}\sum^{K}_{k=1}(y_k - o_k)^2$$

其中 $y_k$ 代表ground truth(gt)值, $o_k$ 代表网络输出值。

$$MSE损失函数的偏导:\frac{\partial L}{\partial o_i} = (o_i - y_i)$$

由于偏导数中单且仅当 $k = i$ 时才会起作用,故进行了简化。

接下来我们看看全连接层输出的梯度:

img

$$MSE损失函数的表达式:L = \frac{1}{2}\sum^{K}_{i=1}(o_i^1 - t_i)^2$$

$$MSE损失函数的偏导:\frac{\partial L}{\partial w_{jk}} = (o_k - t_k)o_k(1-o_k)x_j$$

我们用 $\delta_k = (o_k - t_k)o_k(1-o_k)$ ,则能再次简化:

$$MSE损失函数的偏导:\frac{dL}{dw_{jk}} = \delta_kx_j$$

最后,我们看看那PB算法中每一层的偏导数:

img

输出层:
$$\frac{\partial L}{\partial w_{jk}} = \delta_k^K o_j$$
$$\delta_k^K = (o_k - t_k)o_k(1-o_k)$$

倒数第二层:
$$\frac{\partial L}{\partial w_{ij}} = \delta_j^J o_i$$
$$\delta_j^J = o_j(1 - o_j) \sum_{k}\delta_k^Kw_{jk}$$

倒数第三层:
$$\frac{\partial L}{\partial w_{ni}} = \delta_i^I o_n$$
$$\delta_i^I = o_i(1 - o_i) \sum_{j}\delta_j^Jw_{ij}$$

像这样依次往回推导,再通过梯度下降算法迭代优化网络参数,即可走完PB算法逻辑。

滑动平均的相关概念

滑动平均(exponential moving average),或者叫做指数加权平均(exponentially weighted moving avergae),可以用来估计变量的局部均值,使得变量的更新与一段时间内的历史取值有关。

变量 $v$ 在 $t$ 时刻记为 $v_{t}$ , $\theta_{t}$ 为变量 $v$ 在 $t$ 时刻训练后的取值,当不使用滑动平均模型时 $v_{t} = \theta_{t}$ ,在使用滑动平均模型后, $v_{t}$ 的更新公式如下:
$$
v_{t}=\beta\cdot v_{t-1}+( 1-\beta) \cdot\theta_{t} (1)
$$
上式中, $\beta\epsilon[0,1)$ 。 $\beta = 0$ 相当于没有使用滑动平均。

$t$ 时刻变量 $v$ 的滑动平均值大致等于过去 $1/(1-\beta)$ 个时刻 $\theta$ 值的平均。并使用bias correction将 $v_{t}$ 除以 $(1 - \beta^{t})$ 修正对均值的估计。

加入Bias correction后, $v_{t}$ 和 $v_{biased_{t}}$ 的更新公式如下:

$$
v_{t}=\beta\cdot v_{t-1}+( 1-\beta) \cdot\theta_{t}
$$
$$
v_{-} b i a s e d_{t}=\frac{v_{t}} {1-\beta^{t}} (2)
$$

当 $t$ 越大, $1 - \beta^{t}$ 越接近1,则上面公式(1)和(2)得到的结果( $v_{t}$ 和 $v_{biased_{1}}$ )将越来越接近。

当 $\beta$ 越大时,滑动平均得到的值越和 $\theta$ 的历史值相关。如果 $\beta = 0.9$ ,则大致等于过去10个 $\theta$ 值的平均;如果 $\beta = 0.99$ ,则大致等于过去100个 $\theta$ 值的平均。

下图代表不同方式计算权重的结果:

t 不使用滑动平均模型,即给ν直接赋值θ 使用滑动平均模型,按照公式(1)更新ν 使用滑动平均模型,按照公式(2)更新ν_biased
0,1,2,…,35 [0, 10, 20, 10, 0, 10, 20, 30, 5, 0, 10, 20, 10, 0, 10, 20, 30, 5, 0, 10, 20, 10, 0, 10, 20, 30, 5, 0, 10, 20, 10, 0, 10, 20, 30, 5] [0, 1.0, 2.9, 3.61, 3.249, 3.9241, 5.5317, 7.9785, 7.6807, 6.9126, 7.2213, 8.4992, 8.6493, 7.7844, 8.0059, 9.2053, 11.2848, 10.6563, 9.5907, 9.6316, 10.6685, 10.6016, 9.5414, 9.5873, 10.6286, 12.5657, 11.8091, 10.6282, 10.5654, 11.5089, 11.358, 10.2222, 10.2, 11.18, 13.062, 12.2558] [0, 10.0, 15.2632, 13.321, 9.4475, 9.5824, 11.8057, 15.2932, 13.4859, 11.2844, 11.0872, 12.3861, 12.0536, 10.4374, 10.3807, 11.592, 13.8515, 12.7892, 11.2844, 11.1359, 12.145, 11.9041, 10.5837, 10.5197, 11.5499, 13.5376, 12.6248, 11.2844, 11.1489, 12.0777, 11.8608, 10.6276, 10.5627, 11.5365, 13.4357, 12.5704]

paint_avg_slide

如上图所示,滑动平均可以看作是变量的过去一段时间取值的均值,相比对变量直接赋值而言,滑动平均得到的值在图像上更加平缓光滑,抖动性更小,不会因为某种次的异常取值而使得滑动平均值波动很大。

滑动平均的优势: 占用内存少,不需要保存过去10个或者100个历史 $\theta$ 值,就能够估计其均值。滑动平均虽然不如将历史值全保存下来计算均值准确,但后者占用更多内存,并且计算成本更高。

为什么滑动平均在测试过程中被使用?

滑动平均可以使模型在测试数据上更鲁棒(robust)。

采用随机梯度下降算法训练神经网络时,使用滑动平均在很多应用中都可以在一定程度上提高最终模型在测试数据上的表现。

训练中对神经网络的权重 $weights$ 使用滑动平均,之后在测试过程中使用滑动平均后的 $weights$ 作为测试时的权重,这样在测试数据上效果更好。因为滑动平均后的 $weights$ 的更新更加平滑,对于随机梯度下降而言,更平滑的更新说明不会偏离最优点很远。比如假设decay=0.999,一个更直观的理解,在最后的1000次训练过程中,模型早已经训练完成,正处于抖动阶段,而滑动平均相当于将最后的1000次抖动进行了平均,这样得到的权重会更加鲁棒。

什么是模型微调(fine-tuning)

在AI行业中,模型微调(Fine-tuning)是一种基础有效的技术,特别适用于迁移学习场景,其中预训练模型的参数被稍作训练调整以适应新的、但与原始训练任务相似的任务。这种方法非常适合于数据量有限的情况,可以显著提高模型的性能和泛化能力。

模型微调的基本步骤:

  1. 选择预训练模型

    • 开始微调之前,首先需要一个已经在相关任务上预训练好的模型,通常这些模型在大规模数据集(如ImageNet、Laion等)上进行预训练。因为这些模型已经学习到了丰富的特征表示,可以作为新任务的起点。
  2. 初始化

    • 微调时,通常保留预训练模型的大部分或所有权重,作为新任务训练的初始化点。
  3. 修改模型结构

    • 根据新任务的需求,可能需要对模型的最后几层进行修改。例如,在图像分类任务中,最后的全连接层(输出层)可能需要根据新任务的类别数进行调整。
  4. 重新训练

    • 在新的数据集上继续训练模型。通常只需重新训练模型的一部分,特别是那些针对特定任务调整过的层,而其他层可以保持原始预训练时的参数或者以较小的学习率进行微调,以避免过度拟合。
  5. 调整学习率

    • 微调时通常使用比原始预训练时更小的学习率,这有助于保持已经学习到的有用特征,并仅对它们进行精细的调整。

模型微调的应用场景:

  • AIGC:AI绘画、AI视频、大模型、AI多模态、数字人、AI音频等。
  • 传统深度学习:图像分类、图像分割、目标检测、目标跟踪等。
  • 自动驾驶:车载图像分类、车载图像分割、车载目标检测等。

微调的好处:

  • 加速训练:由于模型从有效的初始状态开始学习,微调通常比从头开始训练快得多。
  • 需要更少的数据:微调可以在相对较少的数据上进行,因为模型已经从预训练中获得了大量的通用知识。
  • 提高性能:通过利用预训练模型的知识,可以提高模型在新任务上的表现,特别是当新任务的数据不足以从头开始训练复杂模型时。

总的来说,模型微调是一种高效利用已有知识以适应新任务的方法,特别适用于数据资源有限的场景。

简要介绍一下FLOPs

首先注意FLOPs和FLOPS是有区别的:

FLOPS是指每秒浮点运算次数(Floating Point Operations per Second),常用于评估硬件性能。

FLOPs是指浮点运算次数(Floating Point Operations),常用于描述模型/算法的总计算量(复杂度)。

以矩阵乘法运算为例,矩阵$W \in \mathbb{R}^{M \times N}$,矩阵$A \in \mathbb{R}^{N \times K}$,二者相乘时,矩阵中每个元素会发生$N$次乘法运算和$N-1$次加法运算,那么FLOPs计算方式为:

$$FLOPs = M \times K \times N + M \times K \times (N-1)$$

计算FLOPs的工具有torchstat,ptflops等。

简要介绍一下FPS

FPS,每秒帧数(Frame Per Second),用于评估图像处理或模型推断速度的指标。

FPS表示在一秒内处理的图像帧数,其计算公式为:

$$FPS = \frac{1}{每帧数据所需处理时间}$$

介绍一下CNN模型和Transformer模型有哪些异同?

CNN模型(Convolutional Neural Network)和Transformer模型是AI领域中两种常见的神经网络结构,广泛应用于AIGC、传统深度学习、自动驾驶等领域。

CNN模型

主要特点:

  1. 局部连接(Local Connectivity): CNN利用卷积层中的滤波器(或称卷积核)在输入图像上进行滑动,以提取局部特征。每个滤波器只与输入的一小部分连接,这称为局部感受野。

  2. 权重共享(Weight Sharing): 同一个滤波器在整个输入图像上滑动,并应用相同的权重。这减少了参数的数量,提高了模型的训练效率。

  3. 平移不变性(Translation Invariance): 由于滤波器在图像上滑动,CNN能够很好地捕捉图像中的局部特征,并对平移变换具有鲁棒性。

  4. 层次化特征表示(Hierarchical Feature Representation): CNN通过多个卷积层和池化层的叠加,逐步提取图像的低级特征(如边缘、角点)和高级特征(如复杂形状和对象)。

应用:

  • AIGC领域、传统深度学习领域、自动驾驶领域等。

Transformer模型

主要特点:

  1. 自注意力机制(Self-Attention Mechanism): Transformer通过自注意力机制,可以对输入序列中的每个元素进行加权求和,从而捕捉全局依赖关系。注意力机制可以动态调整各个元素之间的权重。

  2. 并行处理(Parallel Processing): 与RNN不同,Transformer不需要逐步处理序列数据,可以同时处理整个序列,利用并行计算加速训练。

  3. 位置编码(Positional Encoding): 由于Transformer不具有序列的内在顺序信息,它通过位置编码将序列的位置信息显式地加入到输入中。

  4. 层次化结构(Layered Architecture): Transformer通常由多个编码器层和解码器层堆叠而成,每一层包含多头自注意力机制和前馈神经网络。

应用:

  • AIGC领域、传统深度学习领域、自动驾驶领域等。

异同点对比

相同点:

  1. 深度学习框架: 都是基于深度学习的神经网络模型。
  2. 非线性激活函数: 都使用非线性激活函数(如ReLU)来引入非线性特性。
  3. 梯度下降优化: 都使用反向传播和梯度下降方法来优化模型参数。

不同点:

  1. 结构设计:

    • CNN:主要依靠卷积层和池化层,局部连接和权重共享是其核心特性。
    • Transformer:主要依靠自注意力机制和前馈神经网络,全局依赖关系和并行处理是其核心特性。
  2. 处理类型:

    • CNN:擅长捕捉局部和层次化的空间特征。
    • Transformer:擅长捕捉全局的序列依赖关系。
  3. 计算效率:

    • CNN:卷积操作计算高效。
    • Transformer:使用自注意力机制,计算复杂度较高。
  4. 参数数量:

    • CNN:由于权重共享,参数数量相对较少。
    • Transformer:由于自注意力机制的存在,参数数量较多。

什么是PyTorch计算图?

计算图是深度学习中的一个核心概念,它用于表示和跟踪数据在多个数学操作中的流动。在PyTorch中,这种结构被称为动态计算图,它与传统的静态计算图相区别,后者需要在执行任何计算前完全定义整个图的结构。

PyTorch的计算图是在运行时动态构建的。这意味着可以直接在代码中插入标准的Python控制流结构,如循环和条件语句,这些控制流可以无缝地融入到计算图中。这种方法提供了极大的灵活性,使得模型的实验和调试过程变得更简单和直观。

动态计算图的工作方式简述如下:当执行操作,如加法或乘法时,PyTorch会即时创建节点(代表操作)和边(代表数据流向),从而构建计算图。这种即时构建方式允许图结构随着代码执行而动态改变,非常适合于需要条件执行和循环迭代的复杂模型。

此外,动态计算图极大地简化了自动求导的实现。在PyTorch中,当执行例如反向传播的操作时,框架会自动计算所需的梯度,并通过已构建的计算图追踪每个操作的影响,从而精确地更新模型参数。

使用PyTorch的动态计算图时,需要注意一些关键的实践:

  • 梯度清零:在每个训练步骤开始前,使用optimizer.zero_grad()清除之前步骤中累积的梯度。
  • 避免in-place操作:如x += 1这样的操作可能会在不经意间修改数据,这可以破坏梯度流并导致错误。
  • 使用torch.no_grad():在进行推理或评估模型时,应使用torch.no_grad()上下文管理器,以避免进行不必要的计算梯度操作,从而节省计算资源和内存。

通过掌握PyTorch的计算图,可以更有效地构建、调试和优化深度学习模型,充分利用其提供的灵活性和强大功能。

如何解决CNN的过拟合问题?

过拟合是机器学习中常见的问题,尤其是在深度学习模型,如CNN中。过拟合发生时,模型在训练数据上表现良好,但在未见过的数据上表现较差。为了减少过拟合,
可以采取以下策略:

  • 数据增强(Data Augmentation) :通过旋转、平移、缩放、翻转等方法人为增加训练数据的多样性,帮助模型学习到更加泛化的特征。
  • Dropout:在训练过程中随机“丢弃”(即设置为零)某些神经元的输出,这种方法能够有效减少模型对特定训练样本的依赖,增强模型的泛化能力。
  • 正则化(Regularization) :向损失函数中添加正则项,如L1或L2正则化,限制模型权重的大小,防止模型过于复杂。
  • 早停(Early Stopping) :在验证集上监控模型的性能,当模型的验证误差开始增加时停止训练,以避免过拟合。
  • 使用预训练模型(Transfer Learning) :利用在大型数据集上预训练的模型作为初始模型,对特定任务进行微调,可以有效利用预训练模型的泛化能力,
    减少过拟合风险。

什么是感受野

在CNN中,感受野(Receptive Field) 是指卷积神经网络中某一层输出特征图上的一个元素对原始输入图像中区域的映射大小。
换句话说,它描述了输出特征图中单个元素视野范围内包含的输入图像的区域大小。

感受野的大小由网络中所有前面层的滤波器(卷积核)大小、步长和池化操作共同决定。感受野越大,网络能够捕获的输入图像的全局信息越多,
但同时可能会丢失一些细节信息;反之,感受野较小则能够捕获更多的局部细节信息。

人工智能、机器学习以及深度学习这三者是什么样的关系?

深度学习(Deep Learning)、机器学习(Machine Learning)和人工智能(Artificial Intelligence, AI)是三个相关但不同的概念。它们之间的关系可以理解为递进关系,人工智能是一个广义的概念,机器学习是实现人工智能的一种方法,而深度学习是机器学习的一个子集

1. 人工智能(Artificial Intelligence)

定义:人工智能是指通过计算机模拟和实现人类智能的技术和方法。它涉及使计算机系统能够执行需要人类智能的任务,如感知、推理、学习、规划和决策等。

目标:开发能够自动执行复杂任务的系统,从而在不需要人类干预的情况下完成这些任务。

2. 机器学习(Machine Learning)

定义:机器学习是人工智能的一个分支,涉及机器学习算法和模型,使计算机能够通过经验(数据)进行学习和预测,而无需明确编程。

核心概念:通过数据驱动的方法,机器学习算法能够自动调整和优化模型,以提高在特定机器学习任务上的性能。

3. 深度学习(Deep Learning)

定义:深度学习是机器学习的一个子集,使用多层神经网络模型来模拟人脑的结构和功能,从数据中自动学习和提取特征。

特点

  • 深层结构:使用多个隐藏层的神经网络来捕捉数据的复杂模式和特征。
  • 自动特征提取:能够从原始数据中自动提取特征,而无需手工特征工程。
  • 大规模数据和计算:需要大量数据和计算资源进行训练,通常依赖于GPU加速。

主要架构

  • 卷积神经网络(CNN)
  • 循环神经网络(RNN)
  • 生成对抗网络(GAN)
  • 自编码器(Autoencoder)
  • Transformers

4. 人工智能(Artificial Intelligence)、机器学习(Machine Learning)、深度学习(Deep Learning)三者之间的关系

层级关系

  • 人工智能(AI):是一个广义的领域,涵盖了所有使机器表现出智能行为的技术。
    • 机器学习(ML):是实现人工智能的一种方法,通过数据驱动的方式让机器学习和预测。
      • 深度学习(DL):是机器学习的一个子集,通过使用多层神经网络来自动学习和提取数据特征。

图示关系

1
2
3
4
5
6
7
8
9
10
11
人工智能(AI)
├── 机器学习(ML)
│ ├── 监督学习
│ ├── 无监督学习
│ ├── 强化学习
│ └── 深度学习(DL)
│ ├── 卷积神经网络(CNN)
│ ├── 循环神经网络(RNN)
│ ├── 生成对抗网络(GAN)
├── Transformers
│ └── 自编码器(Autoencoder)

有哪些常见的深度学习问题类型?

深度学习的常见问题类型

  • 分类(Classification)
    将输入数据划分到预定义的有限标签中。其输出是预测的类别标签, 常用评价指标是二元对错(准确率,精确率,召回率和F1分数等)。

    例:花卉图像分类,垃圾邮件拦截等。

  • 回归(Regression)
    建立数值型随机自变量的模型并进行连续的因变量预测。 其输出是数值,常用评价指标是误差大小(均方误差,R2分数等)。

    例:股票价格预测,房价预测等。

    “回归”这个名字对于“预测连续数值”这个任务来说,确实不是非常直观。这个名字的由来有一段有趣的历史,它源于统计学先驱**弗朗西斯·高尔顿(Francis Galton)**在 19 世纪末的研究。

    以下是“回归”这个名字的由来:

    1. 高尔顿的研究背景: 高尔顿是查尔斯·达尔文的表弟,他对遗传和变异非常感兴趣。他研究了人类身高等特征在父代与子代之间的关系。
    2. 关键发现:“向平均回归” (Regression towards the mean / Regression toward mediocrity): 高尔顿收集了大量父子身高的数据,他观察到一个现象:
      • 非常高的父亲,他们的儿子虽然通常也比较高,但平均而言,儿子的身高会比父亲稍微矮一些,更接近于所有男性的平均身高。
      • 同样地,非常矮的父亲,他们的儿子虽然通常也比较矮,但平均而言,儿子的身高会比父亲稍微高一些,也更接近于所有男性的平均身高。
    3. 命名: 高尔顿将这种子代身高有“向中心(平均值)靠近”的趋势的现象称为“向平均回归”(Regression towards the mean)。这里的“回归”带有“退回”、“返回”到中心点的意思。
    4. 方法的命名: 高尔顿为了量化和分析这种父子身高之间的关系,发展出了一种统计方法(也就是早期线性回归的形式)。由于这种方法最初是用来研究和描述“向平均回归”现象的,因此这种统计方法本身就被命名为了“回归分析”(Regression Analysis)。
    5. 术语的泛化: 随着时间推移,“回归”这个术语被广泛应用到更一般的情况:即研究一个或多个自变量(预测变量)与一个连续的因变量(目标变量)之间关系的任何统计方法。尽管现在我们使用回归分析的场景不一定都表现出明显的“向平均回归”现象,但这个历史名称被保留了下来。
  • 聚类(Clustering)
    将无标签的数据分成多个类(簇),确保类内样本相似,类间样本相异。其输出是聚类结果(簇划分,簇标签,簇中心等),常用评价指标是样本距离(紧密度,分隔度等)。

    例:用户分群,异常检测等。

  • 决策(Decision making)
    通过神经网络理解给定目标,约束条件和可用信息,预测出最佳或满意的动作决策。其输出是一连串的动作,常用评价指标是最终收益(回报,平均奖励等)。

例:游戏AI,自动驾驶等。

  • 概率密度估计(Probability density estimation)
    使用深度神经网络来估计一个随机变量或一组随机变量的概率密度函数。其输出是数据的概率分布,常用评价指标是分布差异(对数似然损失,KL散度等)。

    例:数据生成,样本采样等。

解释卷积神经网络中的权重共享机制

在CNN中,权重共享是指同一个卷积核(滤波器)在整个输入特征图上滑动时,使用相同的权重和偏置参数。这种机制是CNN能够有效处理图像数据的关键因素之一。

权重共享的主要优点包括:

  • 参数数量减少 :相比于全连接层需要为每个连接学习一个独立参数,卷积层通过权重共享显著减少了模型的参数数量。这不仅降低了模型的计算复杂度,
    也减轻了过拟合的风险。
  • 特征提取能力 :权重共享使得卷积神经网络能够在整个图像上学习到通用的特征检测器(例如,边缘或纹理检测器)。无论这些特征出现在图像的哪个位置,
    共享权重的卷积核都能够识别它们,增强了模型对图像平移的不变性。
  • 提高学习效率 :由于参数数量的减少和模型复杂度的降低,权重共享还有助于提高模型的学习效率,使得训练过程更快收敛。

如何理解卷积操作的空间不变性(Spatial_Invariance)?

空间不变性(Spatial Invariance) ,又称平移不变性,是指卷积神经网络能够识别图像中的特征,而不受这些特征在图像中位置的影响。
这是通过卷积层的权重共享机制实现的,因为同一卷积核在整个输入图像上滑动进行卷积操作,使得网络能够在图像的不同位置检测到相同的特征。

空间不变性是CNN在图像识别、分类和检测等任务中表现出色的重要原因之一。 例如,不管一只猫出现在图像的左上角还是右下角,
通过卷积操作提取的特征都能帮助网络正确识别出“猫”的存在。

解释Data-Normalization

数据归一化(Data Normalization) 是一种预处理技术,用于将数据缩放到一个特定的范围,通常是0到1之间。
其主要目的是消除数据中的量纲影响,使得不同特征之间的数值差异不会对模型的训练产生过大的影响。

数据归一化的主要优点包括:

  • 加速模型训练 :归一化后的数据具有较小的数值范围,可以加快梯度下降等优化算法的收敛速度。
  • 提高模型稳定性 :归一化可以减少不同特征之间的数值差异,使得模型在训练过程中更加稳定。
  • 增强模型泛化能力 :归一化可以使得模型对数据中的噪声和异常值更加鲁棒,提高模型的泛化能力。

常见的归一化方法包括:

  • 最小-最大归一化(Min-Max Normalization) :将数据缩放到0到1之间,公式为: $x’ = \frac{x - x_{min}}{x_{max} - x_{min}}$ 。
  • Z-score标准化(Z-score Normalization) :将数据标准化为均值为0,标准差为1的正态分布,公式为: $x’ = \frac{x - \mu}{\sigma}$ ,
    其中 $\mu$ 和 $\sigma$ 分别是数据的均值和标准差。
  • L2归一化(L2 Normalization) :将数据归一化为单位范数,公式为: $x’ = \frac{x}{|x|_2}$ ,其中 $|x|_2$ 是数据的L2范数。

数据归一化在深度学习模型中非常重要,是数据预处理的重要步骤之一。

训练模型时,为什么会出现loss逐渐增大的情况?

在训练深度学习模型时,loss逐渐增大的情况可能由以下几种原因导致:

  • 学习率过高:如果学习率设置得过高,模型可能会在优化过程中跳过最优解,导致loss增大。可以通过减小学习率或使用学习率衰减策略来解决这个问题。
  • 梯度爆炸:在某些情况下,梯度可能会变得非常大,导致参数更新过大,从而使得loss增大。可以通过梯度裁剪(Gradient Clipping)来防止梯度爆炸。
  • 模型过拟合:如果模型在训练数据上过拟合,可能会在验证数据上表现较差,导致loss增大。可以通过正则化、数据增强、早停等方法来防止过拟合。
  • 优化器选择不当:不同的优化器在训练过程中可能表现出不同的行为。例如,Adam优化器在训练初期可能会出现loss增大,然后逐渐下降。
    选择合适的优化器并调整其参数可以帮助解决这个问题。
  • 数据问题:如果训练数据中存在噪声或异常值,可能会导致loss在训练过程中波动。可以通过数据清洗、数据预处理等方法来解决这个问题。
  • 其他外部因素:例如,硬件问题、软件错误等也可能导致loss在训练过程中波动。需要检查系统日志和硬件状态,以排除这些因素。
  • 网络结构问题:如果网络结构设计不合理,例如,网络层数过多、网络结构过于复杂等,可能会导致loss在训练过程中波动。需要根据具体问题调整网络结构。

深度学习中图像数据和文本数据的本质区别是什么?

从深度学习的角度来看,图像数据和文本数据有许多本质上的区别。这些区别源于图像和文本的不同性质,以及它们在实际应用中的不同处理方式。

1. 数据表示方式

图像数据

  • 像素表示:图像是通过像素网格表示的,每个像素通常包含颜色信息。对于灰度图像,一个像素表示一个亮度值;对于彩色图像(如RGB图像),每个像素由三个通道(红、绿、蓝)组成。
  • 高维度:图像数据是二维的(宽度×高度),或者在彩色图像中是三维的(宽度×高度×颜色通道)。

文本数据

  • 字符/单词表示:文本是由字符或单词序列组成的,具有一维结构。文本的数据表示通常是一个序列,这个序列可以是字符序列(如 “hello”)或者单词序列(如 “hello world”)。
  • 离散性:文本是离散的,单词与单词之间的关联性在序列中体现,但单词本身并不具有连续性(不像图像中的像素)。

2. 数据的结构

图像数据

  • 空间结构:图像具有明确的空间结构,邻近像素之间通常有很强的相关性。例如,一张人脸图像中,眼睛和鼻子的像素往往靠得很近,且颜色和纹理上也会有一定的连续性。
  • 局部性:图像的局部区域通常包含有用的信息,某个特定区域的特征可以代表整个图像的一部分内容(如眼睛可以代表人脸)。

文本数据

  • 顺序结构:文本是由顺序结构决定的,单词的顺序非常重要,不同的顺序可能表达完全不同的含义(如 “I love you” 和 “You love I”)。
  • 依赖性:文本中的单词或字符之间具有长距离依赖性。例如,在一句话中,句首的主语可能与句尾的动词或宾语紧密相关。

3. 数据处理方式

图像数据

  • 卷积操作:图像通常通过卷积操作来处理,卷积神经网络(CNN)是处理图像的主要模型结构。CNN利用了图像的局部性,通过共享权重的卷积核提取图像的特征。
  • 数据增强:为了增加数据的多样性,图像通常会进行数据增强,如旋转、裁剪、翻转、颜色调整等。这些操作不会改变图像的标签,但会丰富训练数据。

文本数据

  • 嵌入表示:文本数据通常先转化为嵌入表示(如词嵌入或字符嵌入),即将离散的单词或字符映射到连续的向量空间中。这些向量可以捕捉单词之间的语义关系。
  • 序列建模:处理文本时,序列模型(如Transformer、RNN、LSTM等)常用于捕捉文本的顺序依赖性。模型需要记住前面的上下文来正确理解后面的内容。

4. 应用场景

图像数据

  • 图像生成:AIGC时代的图像生成任务等。
  • 图像分类:识别图像中的主体物体并分类,如识别猫、狗等。
  • 目标检测:在图像中检测出目标物体的位置和类别,如自动驾驶中的行人检测。
  • 图像分割:将图像划分为不同的区域,每个区域代表不同的类别,如医学图像中的器官分割。

文本数据

  • 文本对话:AIGC时代的文本对话、问答等任务。
  • 自然语言处理(NLP):涵盖了文本分类、情感分析、机器翻译、自动摘要、问答系统等任务。
  • 文本生成:基于给定的上下文生成自然语言文本,如聊天机器人或写作辅助工具。
  • 语义分析:理解文本的深层含义,如分析文章的主题或情感倾向。

混合精度训练的优缺点是什么?

混合精度训练(Mixed-precision Training)在深度学习领域中具有显著的优势,同时也面临一些挑战。以下是它的主要优缺点:

优点:

  1. 减少内存占用

    • 由于FP16的位宽仅为FP32的一半,因此使用FP16可以显著减少模型训练过程中所需的内存。这种减少内存的优势尤其在大型模型和大数据集的训练中表现突出,使得更复杂的模型在有限的硬件资源上也能得以训练。
  2. 加快训练速度

    • 混合精度训练能够加快模型的训练速度。许多现代的硬件加速器(如NVIDIA的Tensor Core)在处理FP16时的速度要快于处理FP32。这使得使用FP16可以在保持精度的同时提高训练效率。
  3. 提高计算效率

    • 在某些硬件架构上,FP16的计算效率更高。混合精度训练可以充分利用这些硬件优势,进一步提升计算效率,减少训练时间。
  4. 提升通讯效率

    • 使用FP16表示的数据量更小,因而在分布式训练环境中,可以减少数据传输的带宽需求,提高整体通讯效率。

缺点:

  1. 数据溢出(Overflow/Underflow)

    • 由于FP16的数值表示范围远小于FP32,在模型训练过程中可能会出现数据溢出问题。这种情况可能导致模型训练不稳定或训练失败。尤其是在反向传播过程中,梯度的数值可能会超出FP16的表示范围,导致精度损失。
  2. 舍入误差

    • 在使用FP16进行计算时,由于精度较低,可能会发生舍入误差。当模型的梯度值非常小时,这些舍入误差会更加显著,进而影响模型的最终表现。这种误差在长期的累积下,可能导致模型无法达到预期的精度。

总结:

混合精度训练通过结合使用FP16和FP32,能够在不显著影响模型精度的前提下,降低内存使用,提升训练速度和计算效率。然而,它也带来了数据溢出和舍入误差等挑战。因此,在使用混合精度训练时,往往需要结合权重备份、损失缩放和精度累加等技术来应对这些问题,从而充分发挥混合精度训练的优势。

深度学习中add、concat、stack操作的区别是什么?

在AI领域的AIGC、传统深度学习、自动驾驶方向中,我们经常会看到AI模型中进行 addconcatenate(通常简写为 concat)和 stack 这三种常见的张量操作。它们虽然都可以用来处理多维数组(张量),但它们的作用和结果是不同的。

1. add 操作

  • 作用add 是一种逐元素相加的操作。它将两个形状相同的张量中的对应元素相加,得到一个新的张量。
  • 维度变化:在 add 操作中,两个张量的形状必须相同,结果的张量形状也和输入的形状相同。

示例

假设有两个形状相同的张量 A 和 B:

1
2
A = [[1, 2],     B = [[5, 6],
[3, 4]] [7, 8]]

使用 add 操作将 A 和 B 相加:

1
2
3
4
5
C = A + B = [[1+5, 2+6],   # 对应元素相加
[3+7, 4+8]]

C = [[6, 8],
[10, 12]]

可以看到,每个位置上的值都是由 A 和 B 对应位置的元素相加得到的。

2. concatenate 操作

  • 作用concatenate 操作是将两个或多个张量在指定的轴(维度)上连接起来,形成一个新的张量。
  • 维度变化:在 concat 操作中,张量的形状在非连接轴上必须相同,而在连接轴上则会增加。连接后的张量在连接轴上的长度是所有输入张量在该轴上长度的总和。

示例
假设有两个张量 A 和 B,它们的形状是 (2, 2)

1
2
A = [[1, 2],     B = [[5, 6],
[3, 4]] [7, 8]]

如果我们沿着轴 0(行的方向)进行 concat 操作:

1
2
3
4
5
6
C = concat([A, B], axis=0)

C = [[1, 2],
[3, 4],
[5, 6],
[7, 8]]

如果沿着轴 1(列的方向)进行 concat 操作:

1
2
3
4
C = concat([A, B], axis=1)

C = [[1, 2, 5, 6],
[3, 4, 7, 8]]

可以看到 concat 操作是把 A 和 B 在指定轴上“拼接”起来。沿着行拼接就是增加行数,沿着列拼接就是增加列数。

3. stack 操作

  • 作用stack 操作是将多个相同形状的张量“堆叠”在一个新的维度上。与 concat 不同,stack 操作会创建一个新的轴。
  • 维度变化stack 操作会增加一个新的维度,输出张量的形状会比输入张量多一维。每个输入张量将成为新维度上的一个切片。

示例
假设有两个形状为 (2, 2) 的张量 A 和 B:

1
2
A = [[1, 2],     B = [[5, 6],
[3, 4]] [7, 8]]

如果我们对 A 和 B 进行 stack 操作,并沿着新的轴(通常是轴 0)进行堆叠:

1
2
3
4
5
6
7
C = stack([A, B], axis=0)

C = [[[1, 2], # A 在新轴上的第一个切片
[3, 4]],

[[5, 6], # B 在新轴上的第二个切片
[7, 8]]]

此时,C 的形状是 (2, 2, 2)

可以看到 stack 操作是把 A 和 B 堆叠起来,形成一个更高维度的张量。可以理解为在原有的张量基础上“新增”了一个维度。

文本特征提取的方法

词袋模型

建立一个词典库,该词典库包含训练语料库的所有词语,每个词语对应一个唯一识别的编号,利用one-hot文本表示。
文档的词向量维度与单词向量的维度相同,每个位置的值是对应位置词语在文档中出现的次数,即词袋模型(BOW))

问题:

  • 容易引起维度灾难问题,语料库太大,字典的大小为每个词的维度,高维度导致计算困难,每个文档包含的词语数少于词典的总词语数,导致文档稀疏。
  • 仅仅考虑词语出现的次数,没有考虑句子词语之间的顺序信息,即语义信息未考虑

TF-IDF文本特征提取

利用TF和IDF两个参数来表示词语在文本中的重要程度。
TF是词频:指的是一个词语在一个文档中出现的频率,一般情况下,每一个文档中出现的词语的次数越多词语的重要性更大,例如BOW模型一样用出现次数来表示特征值,即出现文档中的词语次数越多,其权重就越大,问题就是在长文档中 的词语次数普遍比短文档中的次数多,导致特征值偏向差异情况。

TF体现的是词语在文档内部的重要性。

IDF是体现词语在文档间的重要性

即如果某个词语出现在极少数的文档中,说明该词语对于文档的区别性强,对应的特征值高,IDF值高,IDFi=log(|D|/Ni),D指的是文档总数,Ni指的是出现词语i的文档个数,很明显Ni越小,IDF的值越大。
最终TF-IDF的特征值的表达式为:TF-IDF(i,j)=TFij*IDFi

基于词向量的特征提取模型

想基于大量的文本语料库,通过类似神经网络模型训练,将每个词语映射成一个定维度的向量,维度在几十到化百维之间,每个向量就代表着这个词语,词语的语义和语法相似性和通过向量之间的相似度来判断。

常用的word2vec主要是CBOW和skip-gram两种模型,由于这两个模型实际上就是一个三层的深度神经网络,其实NNLM的升级,去掉了隐藏层,由输入层、投影层、输出层三层构成,简化了模型和提升了模型的训练速度,其在时间效率上、语法语义表达上效果明显都变好。word2vec通过训练大量的语料最终用定维度的向量来表示每个词语,词语之间语义和语法相似度都可以通过向量的相似度来表示。

训练过程简述:

  1. 初始化: 为词汇表中的每个词随机初始化一个向量。
  2. 滑动窗口: 在大量的文本语料(如维基百科、新闻文章)上移动一个固定大小的“窗口”。
  3. 预测与学习: 对于窗口中的每个词(或根据模型是 CBOW 还是 Skip-gram),使用神经网络进行预测(预测中心词或上下文词)。
  4. 误差计算与向量调整: 计算预测结果与实际情况的误差。
  5. 反向传播: 根据误差,使用反向传播算法微调参与预测的词的向量以及神经网络的权重。目标是让出现在相似上下文中的词,其向量在向量空间中也更接近。
  6. 重复: 重复步骤 2-5,遍历整个语料库很多次(epochs)。

最终结果: 经过大量文本的训练,每个词语会得到一个稠密的、低维度的实数向量。这些向量能够捕捉到词语之间的语义和语法关系。例如,vector('king') - vector('man') + vector('woman') 的结果向量会非常接近 vector('queen')

每个词语的向量在不同的 Word2Vec 模型中都是固定的吗?

答案是否定的,绝对不是固定的。

一个词语的向量表示高度依赖于训练这个 Word2Vec 模型时使用的具体设置和数据。以下是导致词向量不同的主要因素:

  1. 训练语料库 (Corpus): 这是最重要的因素。
    • 新闻语料上训练的模型,”bank” 的向量可能更接近 “money”, “loan”, “financial”。
    • 地理或小说语料上训练的模型,”bank” 的向量可能更接近 “river”, “shore”, “water”。
    • 语料库的大小、领域、语言风格都会显著影响最终的向量。
  2. 模型架构 (CBOW vs Skip-gram): 虽然目标相似,但这两种架构的学习方式略有不同,可能导致最终向量有细微差异。
  3. 超参数 (Hyperparameters):
    • 向量维度 (Vector Dimension/Size): 常见的有 100, 200, 300 维等。不同维度的向量表示能力不同,得到的向量值也完全不同。
    • 窗口大小 (Window Size): 定义了多少上下文词被考虑。窗口大捕捉更广泛的语义关系,窗口小更侧重语法和局部搭配关系。
    • 负采样数量 (Negative Sampling) / 分层 Softmax (Hierarchical Softmax): 这是训练过程中的优化技巧,其参数设置会影响训练效率和结果。
    • 学习率 (Learning Rate): 控制模型参数更新的幅度。
    • 训练迭代次数 (Epochs): 训练轮数不足或过多都可能影响向量质量。
    • 最小词频 (Min Count): 忽略出现次数过少的词。
  4. 随机初始化: 神经网络的初始权重(即词向量的初始值)是随机设置的。即使所有其他设置完全相同,每次重新训练,由于随机初始化的差异,最终得到的向量也会有微小的不同(尽管它们捕捉到的语义关系应该是相似的)。

总结:

Word2Vec 通过分析词语的上下文来学习其向量表示。但是,每个词的最终向量不是一个普遍固定的值,而是特定于训练该模型的语料库、模型选择和超参数配置的结果。因此,在使用预训练的 Word2Vec 向量时,了解它们是在什么数据和设置下训练出来的非常重要。

文本标准化的方法

  1. 字符编码标准化(全角英文字符转半角)
    在计算机中,所有中文字符都是全角字符,而英文字母、阿拉伯数字及符号有全角和半角两种unicode编码方式。它们的全角字符unicode编码从6528165374 (十六进制 0xFF01 ~ 0xFF5E),半角字符unicode编码从33126 (十六进制 0x21~ 0x7E);而空格符比较特殊,全角unicode编码为12288 (0x3000),半角为32 (0x20)。
    可见除空格符外,每个全角字符的unicode编码等于其半角字符的unicode编码加65248,因此字符unicode编码标准化实现代码如下
1
2
3
4
5
6
7
8
9
10
11
#全角转半角
def full_to_half(text:str): #输入为一个句子
_text = ""
for char in text:
inside_code = ord(char)#以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值
if inside_code == 12288: #全角空格直接转换
inside_code = 32
elif 65281 <= inside_code <= 65374: #全角字符(除空格)根据关系转化
inside_code -= 65248
_text += chr(inside_code)
return _text
  1. 英文大小写字母统一化
    英文字母大小写的统一化可直接借助python内置字符串方法实现
1
2
3
#大写字母转为小写字母
def upper2lower(text:str):
return text.lower()
  1. 中文繁简字统一化
    中文繁体字与简体字的统一化借助opencc包的OpenCC类实现,该类通过不同的转换功能代码实现不同的文字转化功能
功能代码 功能说明
s2t 简体中文转繁体中文
t2s 繁体中文转简体中文
s2tw 简体中文转台湾正体
tw2s 台湾正体转简体中文
s2twp 简体中文转繁体中文(台湾正体)
1
2
3
4
5
6
from opencc import OpenCC

# 简繁体转换
def chinese_standard(text:str, conversion='t2s'):
cc = OpenCC(conversion)
return cc.convert(text)
  1. 文本清洗
    文本清洗中,常通过Unicode码过滤来去除非文本内容。Unicode码表中,中日韩统一表意文字字符区间为 4E009FA5,半角英文字母、阿拉伯数字及符号的字符区间为 0x210x7E,所以标准文本字符范围为

$$
[ 4E00 , 9FA5 ] \cup [ 0x21 , 0x7E ]
$$

非文本内容过滤与标点符号过滤一同借助正则表达式实现

1
2
3
4
5
6
7
8
import re
def clear_character(text):
#只取合法字符
pattern = [
"[^\u4e00-\u9fa5^a-z^A-Z^0-9^\u0020^\u0027^\u002e]", # save_standing_character
"\.$" # remove_full_stop
]
return re.sub('|'.join(pattern), '', text)

文本分词的方法

分词任务

根据语言特点,分词任务主要可分类两大类。一类是英文等拉丁语系文本的分词,英文单字成词,且词与词之间由空格隔开,该类任务较为简单,直接按空格分开即可。

另一类是中文文本分词,中文多字成词,且词与词之间没有明显区分标志,因此中文分词较为复杂,需借助词表和算法等工具实现分词需求。而幸运的是,目前分词技术已相对成熟,实际工作中可使用Jieba分词等开源工具直接完成分词需求。

中文与英文不同,词语之间没有天然的空格分隔,因此需要专门的“分词”工具将连续的汉字序列切分成有意义的词语单元。以下是一些常用的中文分词工具(主要是 Python 库):

  1. Jieba (结巴分词):
    • 特点: 非常流行,易于上手,纯 Python 实现,安装方便。支持三种分词模式(精确模式、全模式、搜索引擎模式)。可定制词典。是许多初学者和快速应用的首选。
    • 优点: 速度快,使用简单,社区支持好。
    • 缺点: 在某些复杂或歧义场景下,精度可能不如一些更复杂的模型。
  2. pkuseg:
    • 特点: 由北京大学语言计算与机器学习组开发。基于机器学习模型(通常是 CRF - 条件随机场),在多种数据集上取得了较高的分词精度。支持多领域分词、细粒度分词。
    • 优点: 分词精度较高,尤其是在新闻、网络文本等领域。
    • 缺点: 相较于 Jieba 可能稍慢一些,模型文件较大。
  3. THULAC (THU Lexical Analyzer for Chinese):
    • 特点: 由清华大学自然语言处理与社会人文计算实验室开发。同样基于机器学习模型,提供高精度的词法分析(分词和词性标注)。
    • 优点: 分词精度高。
    • 缺点: 安装可能比 Jieba 稍复杂(可能涉及 C++ 编译),速度可能不是最快的。
  4. HanLP (Han Language Processing):
    • 特点: 一个功能更全面的自然语言处理库,不仅仅是分词。由一系列最先进的深度学习模型驱动。支持多语种、可定制模型、功能丰富(分词、词性标注、命名实体识别、依存句法分析等)。
    • 优点: 功能强大,精度高,支持最新模型。
    • 缺点: 相对较重,使用门槛可能比 Jieba 稍高。
  5. SnowNLP:
    • 特点: 一个轻量级的中文文本处理库,包含分词、词性标注、情感分析、拼音转换等功能。
    • 优点: 简单易用,适合一些基础任务。
    • 缺点: 在分词精度和功能丰富度上可能不如前面几个。

大语言模型(LLM)如何使用子词(Subword)进行 Tokenization。

想象一下,我们有一个基于 BPE (Byte Pair Encoding) 算法训练好的 Tokenizer(类似于 GPT 系列使用的)。这个 Tokenizer 学习了一个包含几万个“令牌”(Token)的词汇表。这些令牌可能是:

  • 完整的常见词(如 “ a”, “ the”, “ cat”, “ dog” - 注意前面的空格,很多 Tokenizer 会保留单词前的空格作为分隔符信息)
  • 常见的词根、词缀(如 “ing”, “est”, “ un”, “ re”, “tion”, “able”)
  • 甚至是一些高频的字符组合或单个字符(如 “ H”, “!”, “?”, “ é”)

Tokenization 过程示例:

假设我们要对以下句子进行 Tokenization:

原始句子: “Tokenization is useful for handling unknown words like pneumonoultramicroscopicsilicovolcanoconiosis.”

Tokenizer 的处理步骤(概念性演示,实际结果取决于具体 Tokenizer):

  1. “Tokenization”:
    • Tokenizer 在词汇表里查找 “ Tokenization”。
    • 可能找不到完全匹配的。
    • 它会尝试分解:可能找到 “ Token” 和 “ization” 这两个在词汇表里都存在的子词令牌。
    • 结果: [" Token", "ization"]
  2. “ is”:
    • Tokenizer 查找 “ is”。
    • 这是一个非常常见的词,很可能直接在词汇表中。
    • 结果: [" is"]
  3. “ useful”:
    • 查找 “ useful”。
    • 可能直接找到。
    • 结果: [" useful"]
  4. “ for”:
    • 查找 “ for”。
    • 常见词,直接找到。
    • 结果: [" for"]
  5. “ handling”:
    • 查找 “ handling”。
    • 可能找不到。
    • 分解:可能找到 “ hand” 和 “ling”。
    • 结果: [" hand", "ling"] (或者也可能是 [" handling"] 如果它足够常见被学成一个 token)
  6. “ unknown”:
    • 查找 “ unknown”。
    • 可能找不到。
    • 分解:找到 “ un” 和 “known”。
    • 结果: [" un", "known"]
  7. “ words”:
    • 查找 “ words”。
    • 常见词,直接找到。
    • 结果: [" words"]
  8. “ like”:
    • 查找 “ like”。
    • 常见词,直接找到。
    • 结果: [" like"]
  9. “ pneumonoultramicroscopicsilicovolcanoconiosis”:
    • 查找这个超长词。
    • 几乎肯定不在词汇表中(这是一个非常罕见的人造词,表示一种肺病)。
    • Tokenizer 会不断地将其分解成词汇表中存在的、尽可能长的子词片段。
    • 可能的结果 (仅为示意): [" pne", "umon", "oult", "ram", "ic", "ros", "copic", "sil", "ico", "vol", "can", "ocon", "iosis"]
    • 关键点: 即使这个词从未在训练数据中出现过,Tokenizer 也能通过已知的子词片段将其表示出来,而不是将其标记为一个完全未知的 <UNK> 符号。
  10. “.”:
    • 查找 “.”。
    • 标点符号通常也是独立的令牌。
    • 结果: ["."]

最终 Tokenization 结果 (组合起来):

1
2
3
4
5
TEXT



[" Token", "ization", " is", " useful", " for", " hand", "ling", " un", "known", " words", " like", " pne", "umon", "oult", "ram", "ic", "ros", "copic", "sil", "ico", "vol", "can", "ocon", "iosis", "."]

这个例子展示了子词 Tokenization 的几个关键点:

  • 常见词完整保留: “ is”, “ useful”, “ for”, “ words”, “ like” 被保留为单个令牌。
  • 词根词缀分解: “Tokenization” -> “ Token” + “ization”, “handling” -> “ hand” + “ling”, “unknown” -> “ un” + “known”。这有助于模型理解词语的构成和含义。
  • 处理未登录词 (OOV): 那个超长的生僻词被成功分解成了多个已知的子词片段,模型仍然可以尝试去理解它,而不是完全忽略。
  • 词汇表可控: 只需要存储有限数量的子词令牌(比如 5 万个),就能组合出几乎无限的词语。

重要提示:

  • 上面分解超长词的方式只是一个示意,实际的分解结果取决于训练 Tokenizer 时学习到的具体子词集合和合并规则。
  • 不同的 LLM 使用不同的 Tokenizer(BPE, WordPiece, SentencePiece 等的不同变种和配置),因此同一个句子的 Tokenization 结果在不同模型间可能会有差异。
  • 很多 Tokenizer 会用特殊符号(如 ``或 ##)来表示一个 Token 是否是词的开头,这有助于后续重构原始文本。例如,”handling” 可能被分解为 [" hand", "##ling"],表示 “ling” 是接在前面的。

分词算法

中文分词都是基于词库匹配实现的,分词方法主要分为不考虑句子语义,直接基于词库匹配规则的分词方法,和考虑句子语义,基于概率统计的分词方法两大类。其中,按匹配规则分词常使用最大匹配算法。

而考虑语义的概率统计分词方法,要先基于词库获取所有可能的分词组合结果,然后将所有结果输入语言模型,计算出概率最大的组合作为最终的分词结果。针对分词任务实时性的需求,出于效率考虑,常用unigram语言模型 + 维特比算法快速得到考虑语义的分词结果。

(更近一步说,HMM、CRF等时间序列模型可以得到更好的概率统计分词效果,它们在成熟开源工具中被广泛使用)

  1. 最大匹配算法
    最大匹配是一种基于匹配单词长度最大化的贪心算法,它分为前向最大匹配、后向最大匹配和双向最大匹配三种分词方式。

前向最大匹配算法按从前向后顺序,将文本子字符串与词库中单词进行匹配,匹配时词库单词按长度从大到小排列依次参与匹配(文本子字符串切片长度与匹配词库单词长度相同),匹配成功则将字符子串划分为一个独立的单词并继续匹配后续文本。后向最大匹配算法则是将文本按从后向前顺序完成上述过程。

小贴士:将词典中同长度单词编制成一哈希表,然后将文本子字符串进行哈希查找有无匹配项,可最大匹配算法的时间复杂度从$$o(n^2)$$降低至$$O(n)$$

  1. unigram模型 + 维特比算法
    unigram模型假设词与词之间相互独立,即多个词语共同出现的概率等于它们各自单独出现的概率的乘积。

维特比算法将文本改造成一个具有n+1个节点的有向图(n等于文本字数),然后将文本中的每一个字依次放到初始节点到终止节点的连线上(每条连接仅放一个字)

然后连接自始向终方向的所有可能连线,每条连线所包含的单字组成此连线所代表的词,每条从始至终的路径代表一种可能的分词结果。在寻找最大概率分词结果前,还应遍历每条表所代表的词是否在词库中出现,若未在词库中出现应删除这条边。

维特比算法是一种基于贪心策略的动态规划算法,可用它找出从初始节点到终止节点的最短路径,为概率最大的分词结果。

25.停用词去除的方法

停用词

停用词指在自然语言文本中非常常见的单词,它们通常不携带特定含义,例如“the”、“a”、“an”、“in”等。在文本分析中,这些词语可能会干扰模型的训练效果,因此需要将它们从文本中移除。在中文文本中,一些通用的停用词包括:“的”、“了”、“和”、“是”、“在”、“有”、“不”、“我”、“他”、“你”等。这些词出现的频率很高,但在文本分析中通常没有什么意义。
可以使用nltk去除停用词,nltk中包含了一些常用的停用词列表,可以直接使用。也可以自定义停用词列表,将需要去除的词添加到列表中。

1
2
3
4
5
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords

stop_words = set(stopwords.words('english'))

中文停用词去除

1
2
3
4
5
6
7
8
9
10
11
12
13
import jieba

# 加载中文停用词表
stopwords_path = 'path/to/stopwords.txt'
with open(stopwords_path, 'r', encoding='utf-8') as f:
stopwords = [line.strip() for line in f.readlines()]

# 分词并去除停用词
text = '今天天气真好,适合出去玩。'
words = jieba.cut(text)
clean_words = [word for word in words if word not in stopwords]

print(clean_words)

词形还原的方法

词形还原

词形还原是将单词还原为其基本形式的过程,例如将“running”还原为“run”,将“ate”还原为“eat”。词形还原对于文本分析非常重要,因为它可以帮助我们更好地理解文本的含义。

词形还原可以使用nltk库中的WordNetLemmatizer类实现,该类可以接受一个单词和一个词性标签作为输入,然后返回该单词的基本形式。词性标签可以是nltk库中的词性标签,也可以是自定义的词性标签。

步骤

  • 利用分词技术将文本转换为单词列表。
  • 利用词性标注技术为每个单词标注词性。
  • 根据每个单词的词性和上下文语境,将单词还原为它们的原始形式。

使用nltk库中的WordNetLemmatizer类实现词形还原

1
2
3
4
5
6
7
8
9
10
11
12
import nltk
from nltk.stem import WordNetLemmatizer

nltk.download('wordnet')

wordnet_lemmatizer = WordNetLemmatizer()
text = "My cat is walking on the carpet"
tokens = nltk.word_tokenize(text)
result = []
for token in tokens:
result.append(wordnet_lemmatizer.lemmatize(token, pos='v'))
print(result)

在上述代码中,我们首先使用nltk库的word_tokenize()函数将文本转换为单词列表。然后,我们创建了一个WordNetLemmatizer对象,并使用它的lemmatize()方法将单词还原为它们的原始形式。在这个例子中,我们将单词还原为它们的动词原形。输出结果为:[‘My’, ‘cat’, ‘be’, ‘walk’, ‘on’, ‘the’, ‘carpet’]。

在实际应用中,词形还原通常与停用词去除、词干提取等技术结合使用,以提高文本预处理的效果。

在中文中,词形变化相对英文单词较少,但是词语之间的组合方式比较灵活,同一个词可能会有多种不同的形态。中文的词形还原与英文略有不同,它通常指的是将词语的不同形态转换为它的原始形态。例如,将“吃饭了”和“吃了饭”都还原为“吃饭”。

中文词形还原的实现通常需要借助中文分词技术和词性标注技术。中文分词是将一段文本分解成词语的过程,而词性标注是对每个词语进行词性标记的过程。通过这两个步骤,我们可以识别出每个词语的原始形态,并将其还原。

使用中文分词库jieba和词性标注库pynlpir进行中文词形还原

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import jieba
import pynlpir

# 加载停用词表
stopwords = []
with open('stopwords.txt', 'r', encoding='utf-8') as f:
for line in f:
stopwords.append(line.strip())

# 初始化分词库和词性标注库
jieba.initialize()
pynlpir.open()

def lemmatize_chinese(text):
"""
中文词形还原函数,输入为一段中文文本,输出为还原后的文本。
"""
words = jieba.cut(text)
lemmatized_words = []
for word in words:
if word not in stopwords: # 去除停用词
pos = pynlpir.segment(word)[0][1] # 获取词性
if pos.startswith('n'): # 名词
lemmatized_words.append(pynlpir.noun_lemmatize(word))
elif pos.startswith('v'): # 动词
lemmatized_words.append(pynlpir.verb_lemmatize(word))
else:
lemmatized_words.append(word)
return ''.join(lemmatized_words)

# 测试
text = "今天天气不错,适合去散步。"
lemmatized_text = lemmatize_chinese(text)
print(lemmatized_text)

在上述代码中,我们首先加载了停用词表,然后初始化了分词库jieba和词性标注库pynlpir。接下来,我们定义了一个lemmatize_chinese()函数,该函数接受一段中文文本作为输入,并返回还原后的文本。在函数中,我们首先使用jieba库对文本进行分词,然后去除停用词。接着,我们使用pynlpir库对每个词语进行词性标注,并根据词性将词语还原为它们的原始形态。最后,我们将还原后的词语连接起来,形成还原后的文本。

解释一下梯度消失和爆炸问题,简单阐述如何缓解该现象?

梯度消失:在训练神经网络时,有时权重的更新会变得太小。这使得学习变得非常缓慢或完全停止学习。
梯度爆炸:与前者相反,梯度值可能会变得非常大,超出网络处理范围,导致模型运行不稳定。
解决方法:使用权重初始化和梯度裁剪等技术。此外,某些类型的层(如 LSTM 或 GRU)旨在解决这些问题。

  1. 梯度消失和爆炸问题的解释
    • 梯度消失
      • 在深度神经网络中,反向传播算法用于计算损失函数对网络中各参数(权重和偏置)的梯度,以更新参数来优化网络。当梯度随着网络层数的增加而趋近于0时,就发生了梯度消失问题。
      • 例如,在使用激活函数为Sigmoid函数的多层神经网络中,Sigmoid函数的导数取值范围是(0,0.25]。假设一个神经网络有很多层,在反向传播计算梯度时,每一层的梯度都要乘以该层激活函数的导数。随着层数的增加,这些小于1的导数连乘会使得梯度越来越小,最终趋近于0。这意味着靠近输入层的参数更新会非常缓慢,网络难以训练。
    • 梯度爆炸
      • 与梯度消失相反,当梯度随着网络层数的增加而变得非常大时,就出现了梯度爆炸问题。
      • 比如,在某些递归神经网络(RNN)中,如果权重矩阵的范数大于1,随着时间步(相当于网络的层数)的增加,梯度在反向传播过程中会呈指数级增长。这种情况下,参数更新的步长会过大,导致模型无法收敛,甚至会出现数值溢出的情况。
  2. 缓解梯度消失和爆炸现象的方法
    • 梯度裁剪(Gradient Clipping)
      • 这是一种简单有效的缓解梯度爆炸的方法。其原理是设定一个梯度的阈值,当计算得到的梯度范数大于这个阈值时,就对梯度进行缩放,使其范数等于阈值。
    • 使用合适的激活函数
      • 避免使用容易导致梯度消失的激活函数,如Sigmoid函数。可以使用ReLU(Rectified Linear Unit)激活函数,其表达式为f(x) = max(0,x)。
    • 权重初始化(Weight Initialization)
      • 合理的权重初始化可以帮助缓解梯度问题。例如,Xavier初始化方法适用于使用Sigmoid或tanh等激活函数的网络。它根据输入和输出神经元的数量来初始化权重,使得每层的输入和输出的方差大致相同,从而避免梯度消失或爆炸。
    • 残差网络(Residual Network,ResNet)
      • 残差网络的核心是残差块(Residual Block)。在残差块中,除了正常的层间传递外,还添加了一个从输入直接到输出的短路连接(shortcut connection)。
      • 假设一个普通的神经网络层的输出为y = F(x)(x为输入),在残差网络中,输出变为y = F(x)+x。这样,即使在某些层出现了梯度消失的情况,梯度也可以通过短路连接直接传播到前面的层,使得网络更容易训练,缓解了梯度消失问题。因为梯度可以通过捷径更容易地反向传播,而不是仅仅依赖于经过多层的微小梯度乘积。

描述L1和L2正则化之间的区别,并且描述这二者的使用场景?

L1 正则化:添加等于权重绝对值之和的惩罚。这可以使某些权重为零,从而有效地删除不太重要的特征。
L2 正则化:将权重的平方和相加作为惩罚。这将保留所有功能,但如果它们不重要,则会降低其影响。

  1. L1和L2正则化的区别
    • 定义公式
      • L1正则化:也称为Lasso(Least Absolute Shrinkage and Selection Operator)正则化。其正则化项是模型参数绝对值之和,对于一个包含$n$个参数$\theta = (\theta_1,\theta_2,\cdots,\theta_n)$的模型,损失函数$J(\theta)$加上L1正则化项后的形式为:$J_{L1}(\theta)=J(\theta)+\lambda\sum_{i = 1}^{n}|\theta_i|$,其中$\lambda$是正则化强度系数,用于控制正则化的程度。
      • L2正则化:也称为岭(Ridge)正则化。其正则化项是模型参数的平方和,损失函数$J(\theta)$加上L2正则化项后的形式为:$J_{L2}(\theta)=J(\theta)+\lambda\sum_{i = 1}^{n}\theta_i^2$。
    • 对参数的影响
      • L1正则化:倾向于使模型的参数变得稀疏。因为L1正则化项是绝对值之和,在优化过程中,它会使得一些不重要的参数变为0。例如,假设有一个线性回归模型的参数向量$\theta = (\theta_1,\theta_2,\theta_3)$,在L1正则化的作用下,随着优化的进行,可能会出现$\theta_2 = 0$的情况,这样就实现了特征选择的效果,即自动选择了与目标变量更相关的特征对应的非零参数。
      • L2正则化:会使模型的参数值变小,但不会像L1正则化那样使参数变为0。它对所有参数进行同等程度的惩罚,使得参数分布更加均匀。例如,在一个神经网络中,使用L2正则化会使权重参数的值整体变小,防止过拟合,同时保持模型的复杂度相对稳定。
    • 几何解释
      • L1正则化:在二维参数空间(假设只有两个参数$\theta_1$和$\theta_2$)中,L1正则化的约束区域是一个菱形。当最小化损失函数和正则化项的组合时,最优解往往出现在菱形的顶点上,这就导致了参数的稀疏性。
      • L2正则化:在同样的二维参数空间中,L2正则化的约束区域是一个圆形。最小化损失函数和正则化项的组合时,最优解更可能出现在圆周上,参数会平滑地减小,而不是像L1正则化那样产生稀疏性。
  2. L1和L2正则化的使用场景
    • L1正则化使用场景
      • 特征选择:当数据集中存在大量特征,并且希望自动筛选出对模型有重要贡献的特征时,L1正则化是一个很好的选择。例如,在基因数据分析中,可能有成千上万个基因特征,使用L1正则化可以帮助识别出与疾病相关的关键基因,因为它会将不重要基因对应的系数收缩为0。
      • 模型解释性:由于L1正则化可以产生稀疏模型,使得模型更容易解释。在一个线性回归模型中,非零系数对应的特征可以被认为是对预测结果有重要影响的因素,从而帮助用户理解模型的决策过程。
    • L2正则化使用场景
      • 防止过拟合:在大多数情况下,L2正则化都可以有效地防止模型过拟合。例如,在训练神经网络时,由于网络结构复杂,很容易出现过拟合现象。添加L2正则化项可以使权重参数不会变得过大,从而使模型更加平滑,对训练数据中的噪声不那么敏感。
      • 多特征且特征相互关联的情况:当数据集中的特征之间存在一定的相关性,并且不希望进行特征选择(即所有特征都可能对模型有一定的贡献)时,L2正则化更合适。比如,在图像识别任务中,图像的像素之间是相互关联的,使用L2正则化可以防止模型过度依赖某些局部特征,同时保持对整体图像特征的学习能力。

卷积和BN如何融合提升推理速度?

  • 卷积操作是用一个小的卷积核在输入数据上滑动,通过计算卷积核与输入数据对应位置元素的乘积之和来提取特征。就好像用一个小的滤网在数据中过滤出想要的模式。
  • BN操作是对输入数据进行归一化。它先计算一批数据的均值和方差,把数据变成均值为0、方差为1的分布,然后再用可学习的参数(缩放因子和偏移因子)对数据进行变换,让数据的分布更符合模型学习的需要。
    Conv和BN的融合:在网络的推理阶段,可以将BN层的运算融合到Conv层中,减少运算量,加速推理。本质上是修改了卷积核的参数,在不增加Conv层计算量的同时,略去了BN层的计算量。
1
2
3
4
5
6
7
8
9
10
11
12
def fuse_conv_bn(conv, bn):

std = (bn.running_var + bn.eps).sqrt()
bias = bn.bias - bn.running_mean * bn.weight / std

t = (bn.weight / std).reshape(-1, 1, 1, 1)
weights = conv.weight * t

conv = nn.Conv2d(31283)
conv.weight = torch.nn.Parameter(weights)
conv.bias = torch.nn.Parameter(bias)
return conv

dropout作为一种正则化技术是如何执行的?

  1. 基本原理

    • Dropout是一种在神经网络训练过程中使用的正则化技术,其基本思想是在每次训练迭代时,随机地“丢弃”(即设置为0)一部分神经元。
    • 例如,在一个全连接的神经网络层中,假设该层有100个神经元,给定一个dropout率(比如0.5),在每次前向传播过程中,会随机地选择50个神经元并将它们的输出设置为0。这就相当于在每次训练时使用了一个不同的“子网络”,因为被丢弃的神经元不同。
  2. 训练阶段执行过程

    • 前向传播
      • 在神经网络的每一层进行前向传播时,对于每个神经元,都会根据设定的dropout率(通常用$p$表示)生成一个随机数。如果这个随机数小于$p$,那么该神经元就会被丢弃,其输出被设置为0;如果随机数大于等于$p$,则神经元正常工作,其输出乘以$\frac{1}{1 - p}$。例如,若$p = 0.2$,则正常工作的神经元输出要乘以$\frac{1}{1 - 0.2}=\frac{1}{0.8}=1.25$。这样做是为了保持该层神经元输出的期望值不变。
    • 反向传播
      • 由于在前向传播过程中部分神经元被丢弃,反向传播时只对没有被丢弃的神经元进行梯度更新。例如,在计算损失函数关于某一层权重的梯度时,只考虑那些在当前迭代中没有被丢弃的神经元的贡献。这就使得网络不能过度依赖某些特定的神经元,因为在每次训练迭代中,这些神经元可能被丢弃,从而迫使网络学习到更鲁棒的特征表示。
  3. 测试阶段执行过程

    • 在测试阶段(或者说模型评估阶段),通常不会进行dropout操作。因为我们希望模型以一个固定的结构来进行评估。
    • 而是将所有神经元都保留,并且将神经元的输出乘以训练阶段使用的dropout率(比如$p = 0.5$,则神经元输出乘以0.5)。这样可以保证测试阶段的输出期望与训练阶段的输出期望相同。例如,在训练阶段,由于有一半的神经元可能被丢弃,所以输出期望是完整网络输出期望的一半;在测试阶段将所有神经元输出乘以0.5,就可以达到相同的输出期望。

解释批量归一化的概念,它解决了什么问题?

  1. 批量归一化(Batch Normalization)的概念

    • 定义:批量归一化是一种深度学习中的技术,用于对神经网络中每层的输入数据进行归一化处理。具体来说,对于一个小批量(mini - batch)的数据,在每一层的激活函数之前,它会计算这个小批量数据的均值和方差,然后将数据归一化到均值为0、方差为1的标准正态分布。之后,再通过一个可学习的缩放参数($\gamma$)和偏移参数($\beta$)来恢复数据的原始表示能力。
    • 计算过程示例:假设在一个神经网络的某一层,输入的小批量数据为${x_1,x_2,\cdots,x_m}$($m$是小批量数据的大小)。首先,计算这个小批量数据的均值$\mu=\frac{1}{m}\sum_{i = 1}^{m}x_i$和方差$\sigma^2=\frac{1}{m}\sum_{i = 1}^{m}(x_i - \mu)^2$。然后,对每个数据点$x_i$进行归一化,得到$\hat{x}_i=\frac{x_i - \mu}{\sqrt{\sigma^2+\epsilon}}$,其中$\epsilon$是一个很小的数(例如$10^{-8}$),用于防止方差为0的情况。最后,通过可学习的参数进行变换,得到输出$y_i = \gamma\hat{x}_i+\beta$。
  2. 解决的问题

    • 加速训练过程
      • 在深度神经网络中,数据分布在经过每层网络时会发生变化,这种现象被称为内部协变量偏移(Internal Covariate Shift)。例如,在一个多层感知机中,随着网络层数的增加,每层输入数据的分布可能会逐渐偏移,导致后面的层需要不断地适应新的数据分布来进行学习。这使得训练过程变得缓慢,因为网络难以收敛。批量归一化通过将每层输入数据归一化到标准正态分布,使得每层的输入数据分布相对稳定,从而加速了训练过程。就好像为每层的学习提供了一个相对稳定的“环境”,让网络能够更快地学习到有用的特征。
    • 提高模型的泛化能力
      • 批量归一化可以起到一定的正则化作用。由于在训练过程中是基于小批量数据进行归一化的,不同小批量之间的数据分布会有一些差异。这种差异类似于在数据中添加了一些噪声,使得模型不能过度依赖于某一个特定的小批量数据的分布,从而提高了模型对不同数据分布的适应性,即提高了模型的泛化能力。例如,在图像分类任务中,批量归一化后的模型能够更好地处理不同光照、角度等变化的图像,因为它对数据分布的变化更加鲁棒。
    • 缓解梯度消失和爆炸问题
      • 当数据分布稳定后,在反向传播过程中,梯度的计算也会更加稳定。因为每层输入数据的尺度相对固定,不会出现因数据尺度变化过大而导致的梯度消失或爆炸问题。例如,在使用激活函数(如Sigmoid或Tanh)时,如果输入数据的绝对值过大,其导数会趋近于0,容易导致梯度消失。批量归一化使得输入数据的分布在合理范围内,减少了这种情况的发生。

解释一下如何在深度学习模型训练中实现迁移学习。

  1. 迁移学习的概念

    • 迁移学习是一种机器学习策略,它利用在一个任务(源任务)上训练好的模型,来帮助另一个相关任务(目标任务)的学习。在深度学习中,这通常意味着利用源模型的部分或全部知识来加速和改进目标模型的训练。
  2. 实现迁移学习的步骤和方法

    • 确定源模型和目标任务
      • 选择合适的源模型:源模型应该是在与目标任务相关的领域中训练得到的。例如,如果目标任务是对某种罕见疾病的医学图像进行分类,那么可以选择在一般医学图像分类任务(如正常组织和病变组织分类)上训练好的模型作为源模型。这个源模型可以是在大规模数据集上训练的深度卷积神经网络(如VGG、ResNet等)。
      • 分析目标任务的特点:明确目标任务的类型(如分类、回归等)、数据分布、数据量等。比如,目标任务是对不同品种的花卉图像进行分类,数据量相对较小,并且和自然图像分类领域相关。
    • 模型选择和调整
      • 冻结部分层(Feature Extractor):一种常见的方法是冻结源模型的前面几层(通常是卷积层部分),这些层可以看作是特征提取器。例如,在使用预训练的VGG16模型进行花卉图像分类时,冻结前面的卷积层。因为这些卷积层学习到的是通用的图像特征(如边缘、纹理等),对于新的花卉图像分类任务仍然有用,并且它们已经在大规模数据集上训练好了,冻结可以避免在小数据集上过度训练这些层而导致过拟合。
      • 修改输出层(Output Layer):根据目标任务的类别数量修改源模型的输出层。例如,如果源模型是用于1000类图像分类的预训练模型,而目标任务是10类花卉图像分类,那么需要将源模型的最后一层(通常是全连接层)替换为一个新的全连接层,其输出神经元数量为10。
    • 训练过程
      • 小数据集微调(Fine - Tuning):在冻结部分层后,使用目标任务的数据集对模型的剩余可训练层(包括修改后的输出层和可能未冻结的部分层)进行训练。这个过程称为微调。训练时可以使用较小的学习率,因为预训练模型的参数已经比较合理,只需要在目标任务数据集上进行适度的调整。例如,在花卉图像分类任务中,将冻结部分层后的模型在花卉图像数据集上进行训练,学习率可以设置为源模型最初训练时学习率的十分之一左右。
      • 数据增强(Data Augmentation)配合使用:由于目标任务的数据量可能较小,为了更好地利用数据和防止过拟合,通常会结合数据增强技术。例如,在花卉图像分类任务中,可以对花卉图像进行旋转、翻转、缩放等操作来增加训练数据的多样性。这些增强后的数据和原始数据一起用于微调模型,进一步提高模型在目标任务上的性能。

如何设置随机种子让AI模型的推理结果可以复现?

为了让AI模型的推理结果可以复现,需要设置随机种子来控制随机过程的行为。

  1. 设置随机种子是确保 AI 模型推理结果可复现的关键步骤,能够控制所有随机性来源。
  2. PyTorch、TensorFlow 等框架中,可以通过设置随机种子、固定随机数生成器的状态来保证一致性。
  3. 对于分布式环境或 GPU 推理,需要额外注意 GPU 随机性的控制。
  4. 在AIGC模型或需要用户控制结果的场景中,暴露随机种子作为参数是一个常见AI产品做法。

1. PyTorch 中设置随机种子

PyTorch 提供了多个方法来设置随机种子,以控制随机性来源。

关键来源

  • CPU 随机性torch.manual_seed(seed)
  • GPU 随机性torch.cuda.manual_seed(seed)torch.cuda.manual_seed_all(seed)
  • NumPy 随机性np.random.seed(seed)
  • Python 随机性random.seed(seed)

完整代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch
import numpy as np
import random

def set_seed(seed):
torch.manual_seed(seed) # 控制 PyTorch 的随机性
torch.cuda.manual_seed(seed) # 控制单个 GPU 的随机性
torch.cuda.manual_seed_all(seed) # 控制所有 GPU 的随机性(如果使用多 GPU)
np.random.seed(seed) # 控制 NumPy 的随机性
random.seed(seed) # 控制 Python 的随机性
torch.backends.cudnn.deterministic = True # 确保计算结果的确定性
torch.backends.cudnn.benchmark = False # 禁用动态优化(可能会降低性能)

# 示例:设置随机种子
set_seed(42)

# 加载模型并进行推理
model = torch.load("WeThinkIn.pth")
model.eval()

# 输入数据
input_data = torch.randn(1, 3, 224, 224) # 示例输入
output = model(input_data) # 推理
print(output)

注意事项

  1. CuDNN 性能优化

    • 设置 torch.backends.cudnn.deterministic = True 会确保结果的一致性,但可能会牺牲性能。
    • 如果对性能要求较高,可不设置该参数,但推理结果可能不完全可复现。
  2. 分布式环境

    • 如果使用分布式训练或推理,还需要在各节点上设置相同的随机种子。

    为什么设置 torch.backends.cudnn.deterministic = True 会确保结果的一致性,但可能会牺牲性能。

    1. 背景:cuDNN 和 GPU 计算

    • cuDNN (CUDA Deep Neural Network library): 这是 NVIDIA 提供的一个 GPU 加速库,专门用于优化深度学习中的常见计算任务,如卷积、池化、归一化、RNN 等。PyTorch 在使用 GPU 进行计算时,底层会大量调用 cuDNN 提供的函数来实现这些操作,以获得高性能。
    • GPU 的并行性: GPU 通过拥有成百上千个核心来实现大规模并行计算。这使得它们非常适合深度学习中的矩阵运算和张量操作。

    2. 非确定性 (Non-Determinism) 的来源

    默认情况下 (deterministic = False),cuDNN 为了追求极致的速度,在执行某些操作时可能会引入非确定性。这意味着即使给定完全相同的输入和初始状态,每次运行同一个操作(例如一个卷积层的前向传播)得到的输出结果在数值上可能会有极其微小的差异。这种非确定性主要来源于以下几个方面:

    • 算法选择 (Algorithm Selection): 对于同一个操作(比如卷积),cuDNN 内部可能实现了多种不同的算法(如基于 FFT、Winograd、直接卷积等)。在运行时,cuDNN 可能会根据当前的输入尺寸、硬件状态、内存占用等因素,动态地选择一个它认为当时最快的算法来执行。这个选择过程本身可能不是确定性的,或者不同的运行环境(即使参数相同)可能导致选择了不同的“最快”算法,而不同的算法即使数学上等价,其实际计算过程和浮点数舍入误差累积方式也可能不同。
    • 原子操作 (Atomic Operations): 在高度并行的 GPU 计算中,多个线程可能需要同时更新同一个内存位置(例如,在计算梯度累加时)。为了避免数据竞争,会使用原子操作。然而,这些原子操作完成的顺序在不同的运行中可能是不确定的。
    • 浮点数运算顺序 (Floating-Point Arithmetic Order): 由于并行计算的特性,对一组浮点数进行求和(或其他归约操作)时,加法的顺序在不同的运行中可能是不同的。我们知道,浮点数加法不满足严格的结合律(即 (a + b) + c 在计算机中不一定精确等于 a + (b + c),因为存在舍入误差)。不同的计算顺序会导致最终结果有微小的差异。

    3. torch.backends.cudnn.deterministic = True 的作用

    当你设置这个标志为 True 时,你是在告诉 PyTorch(进而告诉 cuDNN):

    • “请只使用那些保证结果确定性的算法。”
    • cuDNN 会因此避免使用那些可能引入非确定性的、高度优化的算法。
    • 它会选择那些无论运行多少次,只要输入相同,输出就保证完全一致(位对位相同)的算法实现。
    • 这通常意味着它会固定使用某一种特定的算法,并且可能会强制采用某种固定的、可能较慢的计算顺序来处理并行计算中的浮点数运算,以消除顺序带来的差异。

    4. 性能牺牲的原因

    性能牺牲是确保确定性的直接代价:

    • 放弃最快算法: cuDNN 被迫放弃了那些可能是最快但非确定性的算法,转而使用速度较慢但结果稳定的确定性算法。
    • 增加同步/固定顺序开销: 为了保证浮点数运算顺序等的一致性,可能需要引入额外的同步操作或者采用效率较低的计算模式,这会增加计算时间。

2. NumPy 随机性控制

如果模型依赖 NumPy 的随机性,可以单独控制 NumPy 的随机种子。

示例

1
2
3
4
5
6
7
8
import numpy as np

# 设置随机种子
np.random.seed(42)

# 生成随机数
random_numbers = np.random.rand(5)
print(random_numbers)

3. Scikit-learn 中设置随机种子

Scikit-learn 中许多算法(如 K-Means、Random Forest 等)依赖随机性,可以通过设置 random_state 参数来控制随机性。

示例

1
2
3
4
5
from sklearn.cluster import KMeans

# 设置随机种子
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(data)

4. AIGC通用场景:提供随机种子作为参数

在AIGC模型(如 GPT、DALL-E、Stable Diffusion、Midjourney)中,可以通过显式提供随机种子来控制生成结果。

什么是文本分类

文本分类(Text Classification)是自然语言处理(NLP)和机器学习领域中的一个基本任务,它涉及将文本数据自动分配到一个或多个预定义的类别中。这种技术可以应用于多种场景,包括但不限于垃圾邮件检测、情感分析、新闻文章分类、文档组织等。

文本分类的主要步骤包括:

  1. 预处理:包括文本清洗(去除无用字符、HTML标签等)、分词(将文本分割成单词或短语)、去除停用词(常见的、意义不大的词,如“的”、“是”等)、词干提取或词形还原(将单词还原到基本形式)等。

  2. 特征提取:将文本转换为机器学习模型可以处理的数值形式。常见的特征提取方法包括词袋模型(Bag of Words)、TF-IDF(Term Frequency-Inverse Document Frequency)、词嵌入(Word Embeddings)等。

  3. 模型训练:使用标注好的训练数据来训练分类模型。常用的机器学习算法包括朴素贝叶斯、支持向量机(SVM)、随机森林、梯度提升树(GBDT)、神经网络等。

  4. 模型评估:通过测试集来评估模型的性能,常用的评估指标包括准确率、召回率、F1分数、混淆矩阵等。

  5. 模型优化:根据评估结果对模型进行调整,可能包括调整模型参数、选择不同的特征提取方法、尝试不同的机器学习算法等。

  6. 应用模型:将训练好的模型应用于新的、未标注的文本数据,进行分类预测。

文本分类的应用非常广泛,例如:

  • 垃圾邮件过滤:自动将电子邮件分类为垃圾邮件或非垃圾邮件。
  • 情感分析:判断用户评论是正面的还是负面的。
  • 新闻文章分类:将新闻文章分类到不同的主题或类别。
  • 客户反馈分析:对客户反馈进行分类,以便于快速响应。
  • 法律文档分类:将法律文档分类到不同的法律领域。

请解释一下循环神经网络(RNN)及其变种LSTM和GRU的原理。

  • RNN:具有内部的循环结构,允许信息在网络中进行时间序列上的传递。在处理序列数据时,每一个时间步的输入会结合上一个时间步的隐藏状态进行计算,从而可以处理序列的前后依赖关系。但普通RNN存在梯度消失或梯度爆炸问题,在处理长序列时性能受限。

  • LSTM(长短期记忆网络):引入了门控机制,包括输入门、遗忘门和输出门,能够选择性地更新、忘记和输出信息,有效解决了RNN的梯度消失问题,更好地处理长序列信息。例如在自然语言处理中,可用于语言翻译、文本生成等任务,更好地捕捉句子中远距离单词之间的语义关系。

    LSTM 的门机制由三部分组成:

    • 遗忘门(Forget Gate):决定哪些信息需要从细胞状态中遗忘。
    • 输入门(Input Gate):决定哪些新的信息需要被写入细胞状态。
    • 输出门(Output Gate):决定当前时刻的输出,以及哪些信息将从细胞状态流向隐藏状态。

    这些门的计算通过 sigmoid 和 tanh 激活函数完成,范围控制在 [0,1] 或 [-1,1]。

    LSTM 的数学原理

    下面详细解释 LSTM 单元在一个时间步 ttt 的前向传播过程。

    1. 遗忘门(Forget Gate)

    就像是回忆过去时,有些事情是重要的(比如生日),而有些事情可以丢掉(比如昨天中午吃了啥)。遗忘门负责决定「哪些事情可以忘掉」。公式为:

    img

    2. 输入门(Input Gate)

    这个门决定「哪些新信息需要记住」。比如你今天学到的一个新单词,觉得重要,就记下来。分为两步:

    1. 生成候选信息

    img

    1. 更新细胞状态

    img

    最终细胞状态的更新公式为:

    img

    3. 输出门(Output Gate)

    这个门就像是对外展示的窗口,它会根据当前状态和记忆,决定「该展示什么信息」。比如,你写作文时,从脑海里提取相关内容输出。公式为:

    img

    当前隐藏状态为:

    img

    最后,LSTM 的「记忆盒子」会存储重要的信息,这些信息会在需要的时候被用到,直到系统决定「该清理掉了」。

    假设你是一个学生,老师每天布置作业:

    • 遗忘门: 昨天的作业做完了?好,那就忘掉昨天的作业。
    • 输入门: 老师今天说了新的作业?好,把新作业记住。
    • 输出门: 今天上课回答问题时,用你记住的作业知识。

    这样,LSTM 可以帮助你在长期任务中有效管理信息,不会因为记住太多无用的东西而导致脑袋(模型)卡住。

  • GRU(门控循环单元):是LSTM的一种简化版本,只有更新门和重置门,减少了参数数量,在一些情况下可以达到与LSTM相似的性能,并且计算效率更高。

什么是自动编码器(Autoencoder),它的主要应用有哪些?

  • 原理:自动编码器是一种无监督学习的神经网络,主要由编码器、解码器两部分组成。编码器将输入数据压缩到一个低维的表示(潜在空间),解码器将该低维表示还原为原始数据。其目标是最小化重构误差,即输入数据和重构数据之间的差异。例如,对于一个图像输入,编码器将其转换为一个低维向量,解码器再将这个向量还原为图像,使得重构图像尽可能接近原始图像。
  • 主要应用
    • 数据降维:可以将高维数据降维到低维表示,提取数据的主要特征,方便后续的分析和处理。
    • 去噪:可以训练一个自动编码器,使其对噪声数据进行编码和解码,从而去除噪声。将含有噪声的图像输入,通过自动编码器,输出去除噪声后的图像。
    • 生成模型的基础:一些生成模型(如变分自动编码器 VAE)是基于自动编码器的架构,通过学习数据的潜在空间,可以生成新的数据样本。

如何评估深度学习模型的性能?有哪些常见的评估指标?

  • 分类任务评估指标
    • 准确率(Accuracy):是最基本的指标,计算正确预测的样本数占总样本数的比例,即 $Accuracy=\frac{正确预测的样本数}{总样本数}$。但对于类别不平衡的数据集,准确率可能会产生误导,例如在一个90%样本属于正类,10%样本属于负类的数据集中,即使模型总是预测为正类,准确率也可能很高。
    • 精确率(Precision)和召回率(Recall):精确率是指在预测为正类的样本中真正为正类的比例,公式为 $Precision=\frac{真正正类}{预测为正类}$;召回率是指在真正正类中被正确预测的比例,公式为 $Recall=\frac{真正正类}{真正正类 + 假负类}$。它们常被用于评估信息检索和分类任务的性能,比如在医学诊断中,精确率关注预测为患病的人中真正患病的比例,召回率关注真正患病的人中有多少被正确诊断。
    • F1分数:是精确率和召回率的调和平均值,即 $F1=\frac{2\times Precision\times Recall}{Precision + Recall}$,综合考虑了精确率和召回率,在两者之间取得平衡,适合类别不平衡的情况。
    • 混淆矩阵:可以清晰地展示分类结果,包括真正类、假正类、假负类和真负类的数量,通过它可以计算上述指标,还可以观察模型在不同类别上的错误情况。
  • 回归任务评估指标
    • 均方根误差(RMSE):是均方误差的平方根,即 $RMSE=\sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2}$,与均方误差相比,它的量纲与原始数据一致,更直观地反映了预测误差的大小,常用于预测连续变量的任务,如预测房价或温度。
    • 平均绝对百分比误差(MAPE):计算预测值与真实值相对误差的绝对值的平均值,公式为 $MAPE=\frac{100%}{n}\sum_{i=1}^{n}\left|\frac{y_i - \hat{y}_i}{y_i}\right|$,用于衡量预测的相对误差,在商业预测等领域有广泛应用。

请解释一下DropConnect及其与Dropout的区别

  • Dropout原理:在训练过程中,Dropout 随机将一部分神经元的输出置为零,从而避免神经元之间的过度依赖,提高模型的泛化能力。假设一个神经网络的某一层有 $n$ 个神经元,Dropout 以概率 $p$ 将神经元置零,在每次训练迭代时,每个神经元有 $p$ 的概率被选中置零,在测试时,神经元的输出会乘以 $(1 - p)$ 以保持期望一致。
  • DropConnect原理:与 Dropout 不同,DropConnect 是将神经网络中连接神经元的权重以概率 $p$ 置零,而不是将神经元的输出置零。这意味着每次训练时,部分连接被随机断开,使得模型在训练时变得更加稀疏,进一步增强了模型的鲁棒性和泛化能力。
  • 区别:Dropout 直接作用于神经元的输出,操作相对简单;DropConnect 作用于连接权重,其操作更复杂,但理论上可以为模型提供更强的正则化效果,不过由于其计算成本较高,实际使用中不如 Dropout 广泛。

如何处理深度学习中的不平衡数据问题?

  • 过采样(Oversampling):对少数类的数据进行复制或生成,使各类别数据数量接近。例如,在一个二分类任务中,正类数据远少于负类数据,可以复制正类数据多次,使两类数据量达到平衡。一种方法是随机过采样,直接复制少数类样本;另一种是使用 SMOTE(Synthetic Minority Over-sampling Technique),通过对少数类样本的特征空间进行插值,生成新的少数类样本。
  • 欠采样(Undersampling):对多数类的数据进行删除,减少其数量以达到数据平衡。但这种方法可能会丢失多数类中的有用信息,因此需要谨慎使用。
  • 使用加权损失函数:在计算损失函数时,为不同类别的样本分配不同的权重,通常对少数类样本赋予较大的权重,使得模型在训练时更关注少数类,减少对多数类的偏向。例如,在交叉熵损失函数中,根据类别样本的比例分配权重,使少数类样本对损失的贡献更大。

请解释一下数据并行和模型并行的概念,在深度学习分布式训练中如何使用它们?

  • 数据并行:将数据集分成多个部分,分配到多个设备(如多个GPU)上,每个设备上使用相同的模型进行训练,在每个训练步骤结束后,将不同设备上的梯度进行汇总平均,更新模型的参数。例如,在训练一个大型的图像分类模型时,将图像数据集分成多个小批次,每个GPU处理一个批次,训练后汇总梯度更新模型。
  • 模型并行:将一个大的深度学习模型拆分成多个部分,分配到不同的设备上,每个设备负责模型的一部分计算。这种方法适用于模型过大无法在单个设备上训练的情况,例如,对于一个非常深的神经网络,可以将不同的层分配到不同的GPU上,输入数据依次在各个GPU上的层中进行计算。
  • 使用场景:数据并行更常见,因为它相对简单,适合大多数深度学习任务。模型并行在处理超大型模型时很有用,但需要精心设计模型的分割和通信机制,以确保数据的传输和计算的同步。

稠密网络和稀疏网络有什么异同?

1. 核心概念与异同

对比维度 稠密网络(Dense Network) 稀疏网络(Sparse Network)
连接方式 神经元之间全连接(每个输入节点连接所有输出节点) 部分神经元连接(通过剪枝、稀疏约束或动态路由实现)
参数数量 参数量大( $O(n^2)$ ) 参数量小(可减少50%-90%)
计算效率 计算开销大,适合高性能设备(如GPU) 计算效率高,适合边缘设备(如手机、车载芯片)
表达能力 强,能捕捉复杂非线性关系 弱,但对特定任务(如稀疏特征提取)可能更高效
典型应用场景 高精度模型(如ResNet、Transformer) 轻量化部署、实时推理(如MoE模型、剪枝模型)

相同点

  • 均为神经网络结构,用于特征提取与模式识别;
  • 可通过反向传播优化参数;
  • 可结合非线性激活函数增强表达能力。

2. 通俗案例:图像分类任务

场景:训练一个猫狗分类模型,部署至手机端。

  • 稠密网络方案
    • 使用ResNet-50的全连接层(120万个参数),精度98%,但模型大小200MB,手机推理延迟200ms。
  • 稀疏网络方案
    • 对ResNet-50进行剪枝,移除50%冗余连接,精度降至97%,模型大小压缩至50MB,推理延迟降至50ms。
      结论:稠密网络精度高但资源消耗大,稀疏网络牺牲少量精度换取高效部署。

3. 三大领域应用

AIGC(生成式AI内容)

  • 稠密网络
    • 应用:高质量内容生成(如Stable Diffusion的U-Net主干网络)。
    • 案例:生成4K分辨率图像时,稠密网络捕捉细节纹理,确保输出逼真。
  • 稀疏网络
    • 应用:轻量化生成(如手机端实时风格迁移)。
    • 案例:MoE(混合专家)模型动态激活部分专家生成文本,减少70%计算量。

传统深度学习

  • 稠密网络
    • 应用:大规模预训练模型(如BERT的Transformer层)。
    • 案例:BERT通过全连接层建模词间复杂关系,支持语义理解任务。
  • 稀疏网络
    • 应用:模型压缩与加速(如剪枝后的MobileNet)。
    • 案例:剪枝后的VGG-16在ImageNet上保持90%精度,参数量减少60%。

自动驾驶

  • 稠密网络
    • 应用:高精度感知模型(如激光雷达点云分割)。
    • 案例:稠密网络融合多传感器数据,精确检测夜间行人(漏检率<1%)。
  • 稀疏网络
    • 应用:实时决策与低功耗推理(如车载芯片路径规划)。
    • 案例:稀疏化YOLOv5在Jetson AGX上实现30FPS实时检测,功耗降低40%。

4. 总结

  • 核心差异:稠密网络以参数冗余换取高精度,稀疏网络以效率优先牺牲部分性能。
  • 选型原则
    • 需高精度且资源充足(如云端训练):选稠密网络;
    • 需实时性且资源受限(如边缘端部署):选稀疏网络。
  • 技术趋势
    • 稀疏化技术(如动态路由、结构化剪枝)成为模型轻量化核心;
    • 稠密-稀疏混合架构(如MoE)平衡性能与效率,成为AIGC与自动驾驶的新方向。