1. 概述

深度学习的主要流程可以按下图来描述,

0

0

下图是一个神经网络模型的推理过程,它接受输入(数字图片),可以产生输出(数字分类)。

image

image

这就是模型的正向(推理)过程;而反向过程就是训练:即已知输出与输入求函数 fθ 。

image

image

2. 模型

概述

  1. 什么是模型?

从上面可以知道,模型可以简单理解为一个函数 fθ , 也就是各种算子及其参数组成的有向无环图。

  1. 什么是算子

算子就是模型中涉及到的计算函数,可以理解为子函数 fθ 。例如:卷积层(Convolution Layer)中的卷积算法,是一个算子;全连接层(Fully-connected Layer,FC layer)中的权值求和过程,也是一个算子。

  1. 模型结构可视化

1

1

可以看到模型就是一堆算子与其参数所组成的DAG。

附:模型可视化网站:https://netron.app/

  1. 模型格式

主流的 PyTorch、MindSpore、PaddlePaddle、TensorFlow、Keras 等框架导出的模型文件格式不同,不同的 AI 框架训练出来的网络模型、算子之间是有差异的。 以下是一些主流框架的模型文件格式示例:

2

因此有一个开放式的通用格式比较重要,比如模型界的 JPEG - ONNX格式。

  1. ONNX格式

ONNX(Open Neural Network Exchange)是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的人工智能框架(如 Pytorch、MXNet)可以采用相同格式存储模型数据并交互。

使用 Netron(开源的模型可视化工具)来可视化 ONNX 模型,看下具体的信息:

3

3

点击 input 或者 output,可以查看 ONNX 模型的基本信息,包括模型的版本信息,以及模型输入、输出的名称和数据类型。

4

4

点击某一个算子节点,可以看到算子的具体信息。比如点击第一个 Gemm 可以看到:

5

5

每个算子记录了算子属性、图结构、权重三类信息:

  1. 算子属性信息:即图中 attributes 里的信息,这些算子属性最终会用来生成一个具体的算子。

  2. 图结构信息:指算子节点在计算图中的名称、邻边的信息。对于图中的 Gemm 来说,该算子节点叫做/fc1/Gemm,输入数据叫做input,输出数据叫做/fc1/Gemm_output_0。根据每个算子节点的图结构信息,就能完整地复原出网络的计算图。

  3. 权重信息:指的是网络经过训练后,算子存储的权重信息。对于图中的 Gemm 来说,权重信息包括fc1.weightfc1.bias。点击图中 fc1.weightfc1.bias后面的加号即可看到权重信息的具体内容。

  1. 训练

  2. 概述

那么如何得到一个神经网络模型呢?这个时候需要通过对网络模型进行训练,训练过程可以抽象为数学上的优化问题,优化目标为:

θθθ

其中:

  • fθ 表示神经网络模型;

  • Loss 表示损失函数;

  • x 表示输入数据

  • y 代表标注的 ground truth数据;

训练的过程就是找到最小化 Loss 的 θ 取值,θ 也称作权重,就是模型中的参数。 在训练过程中将通过梯度下降等数值优化算法进行求解:

θθαθ

其中,α 就是为学习率(Learning Rate),显然 α 的大小控制着收敛的速度与质量,这个值没有明确的最优解,需要训练时手动调整,所以把类似的参数又称作超参。 当神经网络模型训练完成,就可以通过 y = fθ(x) 进行推理,即使用已经训练好的模型了。 下面对细节详细阐述。

  1. Loss函数

Loss 函数,即损失函数,是深度学习中用于衡量模型预测结果与真实标签之间差异的函数,它在模型训练过程中起着至关重要的作用,主要作用如下:

  • 评估模型性能:通过计算预测值与真实值之间的差异,直观地反映模型的预测准确程度。损失函数的值越小,说明模型的预测结果越接近真实值,模型的性能也就越好。

  • 指导模型优化:在模型训练过程中,优化算法的目标就是最小化损失函数。通过计算损失函数关于模型参数的梯度,来调整参数的取值,使得损失函数逐步减小,从而让模型朝着最优解的方向收敛。

  1. 常见Loss函数

  • 均方误差损失函数(Mean Squared Error, MSE)

    • 公式:

      ,其中 是真实值,是预测值,n 是样本数量。
    • 特点:计算简单,对预测值和真实值之间的差异较为敏感,尤其是对较大的误差会给予更大的惩罚,常用于回归问题。

  • 交叉熵损失函数(Cross-Entropy Loss)

    • 二分类公式: 

      ,其中样本的真实标签 取值为 0 或 1,模型预测样本属于正类的概率为 。
    • 特点:能很好地反映分类模型的预测效果,在分类问题中,尤其是深度学习的分类任务中应用广泛,因为它可以衡量两个概率分布之间的差异,当预测分布与真实分布越接近时,交叉熵越小。

  1. 梯度下降

前面可以知道,训练就是需要找到模型的参数,使Loss函数最小,那么这本质就是数学上的优化问题,而数学上有种常见的优化方法就是梯度下降法。

6

6

梯度下降算法的思想非常像找山谷的最低点。想象你被蒙上眼睛放在了一个不规则的山坡上,你的目标是尽可能快地走到山谷的最低点。由于看不见周围环境,最直观的策略就是感觉脚下的坡度。每一步,你都会选择坡度最陡峭的方向走下去,这样经过多次迭代,你很可能会到达山谷的低点。 在这个过程中,每一次移动都需要做出决策:往哪个方向走?步幅走多大?在数学上,这相当于在当前位置计算梯度(坡度)并以此确定下降的方向,同时也需要设定一个合适的学习率来控制步伐的大小。

7

7

伪代码描述

While True:
    weights_grad = evaluate_gradient(loss_fun, data, weights)
    weights += step_size * weights_grad

虽然梯度下降法很难保证找到全局最优解,特别是在深度学习这样的非凸优化问题中,但它在实践中表现出色,能够找到足够好的局部最优解。

  1. 梯度求解

当应用梯度下降法来训练时,有个最基本的问题,梯度怎么求解? 梯度计算可以分为数值法与解析法:

  • 数值方法

基于有限差分公式,通过数值方法计算梯度。可以预见比较慢,也是一个近似解。

  • ✅ 解析方法

通过微积分公式直接求解梯度,准确并且比较快。

举个具体的例子,对于函数 

: 解析解公式为,那么在点(1,2)的梯度可以直接计算出:而通过数值方法,则计算为

image

image

  1. 反向传播

从上面可以知道,数值方法是通用且方便实现的,但问题是计算太慢了;解析方法较快,但直接计算最终的梯度较难实现且没法复用。 因此我们可以基于链式法则,将一个复杂的函数分解为一系列简单的基本运算(如加法、乘法、指数运算等),然后通过反向传播依次计算每个基本运算的导数,最终得到整个函数的导数。 这样速度快,且可以复用,当然代价就是需要手动写每一个运算的解析解。

8

8

详细解释推荐3Blue1Brown的这个视频

  1. 具体示例

看一下AI训练框架Caffe的Sigmoid算子实现

9

9

再对比一下推理引擎TNN的Sigmoid算子实现

10

10

可能大家已经有一些发现了,这也就是实现AI框架与推理引擎的最基本要素

  1. 推理

  2. 推理流程

https://bytedance.larkoffice.com/sync/A0UadDTDMs6zRVbe9WYcLodwned具体流程:

  1. 模型转换

首先,需要将训练得到的模型转换为推理引擎能够理解的格式,这一步骤通常由模型转换工具离线完成,形成一个统一表达的格式。另外可以使用模型压缩****, 通过技术如剪枝、量化、知识蒸馏等减小模型大小,使之可以更高效的运行。

  1. 推理配置

这通常涉及到设置模型路径、选择运行的设备如 CPU、GPU 等。

Config config;
config.setModelPath("path_to_model");
config.setDeviceType("GPU");
config.setOptimizeGraph(true); 

  1. 创建推理引擎对象

一旦配置选项设置完毕,下一步就是创建推理引擎对象。这个对象将负责管理整个推理过程,包括加载模型、执行推理等。创建推理引擎对象通常需要传递配置对象作为参数。

Predictor predictor(config);

  1. 准备输入数据

在执行推理之前,必须准备好输入数据。这包括对原始数据进行预处理,比如减去均值、缩放等,以满足模型的输入要求。然后,需要获取模型所有输入 Tensor 的名称,并通过推理引擎获取每个输入 Tensor 的指针,最后将预处理后的数据复制到这些 Tensor 中。

// 预处理数据
auto preprocessed_data = preprocess(raw_data);

// 获取输入 Tensor 名称和指针
auto input_names = predictor->GetInputNames();
for (const auto& name : input_names) {
   auto tensor = predictor->GetInputTensor(name);
   tensor->copy(preprocessed_data);
}

Tensor就可以简单理解为多维矩阵。

  1. 执行推理

一旦输入数据准备好,就可以执行推理了。这通常涉及到调用推理引擎的 Run 方法,该方法会启动模型的推理过程。

predictor->Run();

  1. 获得推理结果并进行后处理

推理执行完成后,需要获取输出 Tensor,并将推理结果复制出来。然后,根据模型的输出进行后处理,比如在目标检测任务中,需要根据检测框的位置来裁剪图像。

// 获取输出 Tensor 名称和指针
auto out_names = predictor->GetOutputNames();
std::vector<ProcessedOutput> results;
for (const auto& name : out_names) {
   auto tensor = predictor->GetOutputTensor(name);
   ProcessedOutput data;
   tensor->copy(data);
   results.push_back(processOutput(data));
}

// 后处理,例如裁剪图像等
for (auto& result : results) {
   postprocess(result);
}
  1. 推理引擎

推理引擎作为人工智能系统中的关键组件,负责将训练好的模型部署到实际应用中,执行推理任务,从而实现智能决策和自动化处理。

  1. 核心解决问题

实现(CPU\GPU\NPU)模型中的每层算子的Forward方法,并调度执行算子DAG。

  1. 推理引擎架构图

11

11

推理引擎分为 2 个主要的阶段:

  • 优化阶段

  • 运行阶段

优化阶段聚焦于将训练好的模型转换并优化成适合部署的形式:

  • 模型转换工具负责将模型从研究阶段的格式转换为高效执行的格式,并进行图优化,减少计算负担。

  • 模型压缩通过技术如剪枝、量化、知识蒸馏等减小模型大小,使之更适用于资源有限的环境。

  • 端侧学习允许模型在部署后继续学习和适应新数据,无需返回服务器重新训练,提升了模型在特定场景或用户个性化需求下的表现。

  • 其他组件包括 Benchmarking 工具,用于性能评测和调优指导,以及应用演示(App Demos),服务于模型能力展示与实战反馈收集,共同助力模型的高效部署与持续优化。

运行阶段确保模型在目标设备上的高效执行:

  • 调度层管理模型加载、资源分配及任务调度,根据设备情况灵活安排计算任务。

  • 执行层直接执行模型计算,针对不同硬件优化运算逻辑,有效利用 CPU、GPU 等资源。

  1. 实践

  2. Pytorch框架

PyTorch 是一个基于 python 的开源深度学习框架,最初由 Facebook 的人工智能研究团队开发,经过多年的发展,已经成为了最热门的深度学习框架之一。

官网:https://pytorch.org/

  1. 训练第一个模型

标注数据集

12

12

在这里我们将使用 CIFAR10 数据集。它有类:“飞机”、“汽车”、“鸟”、“猫”、“鹿”、“狗”、“青蛙”、“马”、“船”、“卡车”。CIFAR-10 中的图像大小为 3x32x32,即 32x32 像素的 3 通道彩色图像。

  1. 加载CIFAR10 数据集

import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

batch_size = 4

# 加载 CIFAR-10 训练数据集
trainset = torchvision.datasets.CIFAR10(
    # 数据集存储的根目录
    root='./data',
    # 是否为训练集
    train=True,
    # 如果数据集不存在,是否下载
    download=True,
    # 数据预处理的转换
    transform=transform
)
# 创建训练集的数据加载器
trainloader = torch.utils.data.DataLoader(
    # 训练数据集
    trainset,
    # 每个批次的样本数量
    batch_size=4,
    # 是否在每个 epoch 开始时打乱数据
    shuffle=True,
    # 用于数据加载的子进程数量
    num_workers=2
)

# 加载 CIFAR-10 测试数据集
testset = torchvision.datasets.CIFAR10(
    # 数据集存储的根目录
    root='./data',
    # 是否为训练集
    train=False,
    # 如果数据集不存在,是否下载
    download=True,
    # 数据预处理的转换
    transform=transform
)
# 创建测试集的数据加载器
testloader = torch.utils.data.DataLoader(
    # 测试数据集
    testset,
    # 每个批次的样本数量
    batch_size=4,
    # 是否在每个 epoch 开始时打乱数据
    shuffle=False,
    # 用于数据加载的子进程数量
    num_workers=2
)

# 定义 CIFAR-10 数据集中的 10 个类别
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

  1. 定义一个卷积神经网络

这里将使用LeCun定义的第一代CNN网络作为示例

13

13

代码如下

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


class Net(nn.Module):
    """
    定义一个简单的卷积神经网络
    """
    def __init__(self):
        """
        初始化神经网络的各个层
        """
        # 调用父类的构造函数
        super().__init__()
        # 第一个卷积层,输入通道数为 3,输出通道数为 6,卷积核大小为 5
        self.conv1 = nn.Conv2d(3, 6, 5)
        # 最大池化层,池化窗口大小为 2,步长为 2
        self.pool = nn.MaxPool2d(2, 2)
        # 第二个卷积层,输入通道数为 6,输出通道数为 16,卷积核大小为 5
        self.conv2 = nn.Conv2d(6, 16, 5)
        # 第一个全连接层,输入特征数为 16 * 5 * 5,输出特征数为 120
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        # 第二个全连接层,输入特征数为 120,输出特征数为 84
        self.fc2 = nn.Linear(120, 84)
        # 第三个全连接层,输入特征数为 84,输出特征数为 10,对应 10 个类别
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        """
        定义神经网络的前向传播过程
        :param x: 输入的图像数据
        :return: 输出的预测结果
        """
        # 输入数据经过第一个卷积层,然后应用 ReLU 激活函数,最后进行最大池化
        x = self.pool(F.relu(self.conv1(x)))
        # 经过第一个卷积和池化后的数据,再次经过第二个卷积层,应用 ReLU 激活函数,然后进行最大池化
        x = self.pool(F.relu(self.conv2(x)))
        # 将除了批量维度之外的所有维度展平,以便输入到全连接层
        x = torch.flatten(x, 1)
        # 展平后的数据输入到第一个全连接层,并应用 ReLU 激活函数
        x = F.relu(self.fc1(x))
        # 经过第一个全连接层后的数据,输入到第二个全连接层,并应用 ReLU 激活函数
        x = F.relu(self.fc2(x))
        # 经过第二个全连接层后的数据,输入到第三个全连接层,得到最终的输出
        x = self.fc3(x)
        return x

net = Net()

  1. 定义 Loss 函数和优化器

这里将使用 Classification Cross-Entropy loss and SGD with momentum.

import torch.optim as optim

# 定义交叉熵损失函数,用于多分类问题
criterion = nn.CrossEntropyLoss()
# 定义随机梯度下降优化器,学习率为 0.001,动量为 0.9
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

  1. 训练这个网络

def train():
    global net, labels, outputs, PATH
    net = Net()
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
    # 训练 2 个 epoch
    for epoch in range(2):
        # 初始化运行损失
        running_loss = 0.0
        # 遍历训练集数据加载器
        for i, data in enumerate(trainloader, 0):
            # 获取输入数据和标签
            inputs, labels = data

            # 将优化器的梯度清零,避免梯度累积
            optimizer.zero_grad()

            # 前向传播:计算模型的输出
            outputs = net(inputs)
            # 计算损失
            loss = criterion(outputs, labels)
            # 反向传播:计算梯度
            loss.backward()
            # 更新模型参数
            optimizer.step()

            # 累加损失
            running_loss += loss.item()
            # 每 2000 个小批量打印一次损失
            if i % 2000 == 1999:
                print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
                # 重置运行损失
                running_loss = 0.0
     print('Finished Training')

输出如下:

14

14

完成训练后,将模型保存下来

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

再额外保存一个onnx模型格式来可视化预览

# 保存为 ONNX 格式
dummy_input = torch.randn(1, 3, 32, 32)  # 创建一个虚拟输入张量
torch.onnx.export(net, dummy_input, "cifar10.onnx", export_params=True, opset_version=11, do_constant_folding=True,
                  input_names=['input'], output_names=['output'],
                  dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}})

  1. 使用测试数据集验证

加载测试数据集,并用这个模型推理验证

def infer():
    """
    对测试集数据进行推理,展示真实标签和模型预测结果的函数
    """
    # 声明全局变量,以便在函数内部使用
    global labels, net, outputs
    # 创建一个迭代器,用于遍历测试集数据加载器
    dataiter = iter(testloader)
    # 获取下一批图像和对应的真实标签
    images, labels = next(dataiter)
    # 展示图像
    imshow(torchvision.utils.make_grid(images))
    # 打印真实标签
    print('GroundTruth: ', ' '.join(f'{classes[labels[j]]:5s}'for j in range(4)))
    # 创建一个新的神经网络实例
    net = Net()
    # 加载之前训练好的模型参数
    net.load_state_dict(torch.load(PATH))
    # 让模型对图像进行推理,得到预测的输出
    outputs = net(images)
    # 找出每个样本预测输出中概率最大的类别索引
    _, predicted = torch.max(outputs, 1)
    # 打印模型预测的类别
    print('Predicted: ', ' '.join(f'{classes[predicted[j]]:5s}'
                                  for j in range(4)))

输出:

15

16

可以看到这个第一代CNN模型的识别正确率不是太高。

 AI大模型从0到精通全套学习大礼包

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

只要你是真心想学AI大模型,我这份资料就可以无偿共享给你学习。大模型行业确实也需要更多的有志之士加入进来,我也真心希望帮助大家学好这门技术,如果日后有什么学习上的问题,欢迎找我交流,有技术上面的问题,我是很愿意去帮助大家的!

如果你也想通过学大模型技术去帮助就业和转行,可以点扫描下方👇👇
大模型重磅福利:入门进阶全套104G学习资源包免费分享!
在这里插入图片描述

01.从入门到精通的全套视频教程

包含提示词工程、RAG、Agent等技术点
在这里插入图片描述

02.AI大模型学习路线图(还有视频解说)

全过程AI大模型学习路线

在这里插入图片描述

​​在这里插入图片描述

03.学习电子书籍和技术文档

市面上的大模型书籍确实太多了,这些是我精选出来的

在这里插入图片描述

04.大模型面试题目详解

在这里插入图片描述

在这里插入图片描述

05.这些资料真的有用吗?

这份资料由我和鲁为民博士共同整理,鲁为民博士先后获得了北京清华大学学士和美国加州理工学院博士学位,在包括IEEE Transactions等学术期刊和诸多国际会议上发表了超过50篇学术论文、取得了多项美国和中国发明专利,同时还斩获了吴文俊人工智能科学技术奖。目前我正在和鲁博士共同进行人工智能的研究。

所有的视频由智泊AI老师录制,且资料与智泊AI共享,相互补充。这份学习大礼包应该算是现在最全面的大模型学习资料了。

资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。

在这里插入图片描述
在这里插入图片描述

智泊AI始终秉持着“让每个人平等享受到优质教育资源”的育人理念‌,通过动态追踪大模型开发、数据标注伦理等前沿技术趋势‌,构建起"前沿课程+智能实训+精准就业"的高效培养体系。

课堂上不光教理论,还带着学员做了十多个真实项目。学员要亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事‌!

在这里插入图片描述
如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的“投资”换来的是终身受益!

应届毕业生‌:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。

零基础转型‌:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界‌。

业务赋能 ‌突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型‌。

👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

在这里插入图片描述

Logo

分享最新、最前沿的AI大模型技术,吸纳国内前几批AI大模型开发者

更多推荐