大模型带你学pytorch课程

2 篇文章 0 订阅
1 篇文章 0 订阅

时间有限,大家有想看的部分,可以私信或者评论区联系,我及时补充。或者大家有想帮忙补充的也可以联系。
注:大部分内容均为大模型生成,若有疏漏,欢迎指出。

看起来你想要开始学习PyTorch,这是一个非常棒的选择!PyTorch是一个强大的开源机器学习框架,由Facebook的人工智能研究实验室开发,被广泛用于深度学习模型的构建和训练。让我们开始你的PyTorch旅程吧!

1.📚 预备课程大纲

1.1 引入Python编程

确保你熟悉 Python编程语言,因为PyTorch是基于Python的。如果你还不熟悉Python,我们可能需要先回顾一下基础。

目标:

  • 理解Python的基础语法和特性。
  • 能够编写基本的Python脚本来执行数据处理和简单算法。
  • 熟悉常用的Python库,如NumPy, Pandas, Matplotlib等。

内容概览:

  1. 安装Python
    • 选择适合你的操作系统的Python版本(通常推荐最新稳定版)。
    • 安装Python,确保添加环境变量。
  2. Python环境设置
    • 学习使用IDE(集成开发环境)如PyCharm或Jupyter Notebook。
    • 设置虚拟环境,了解venv或conda。
  3. Python基础语法
    • 变量和数据类型(整数、浮点数、字符串、列表、元组、字典、集合)。
    • 控制流语句(if/else, for, while)。
    • 函数定义和使用。
    • 异常处理。
  4. 面向对象编程
    • 类和对象的概念。
    • 继承和多态。
    • 封装和抽象。
  5. 文件操作
    • 读写文本文件。
    • 使用CSV和JSON格式的数据。
  6. 常用库简介
    • NumPy:用于数值计算。
    • Pandas:用于数据分析。
    • Matplotlib:用于数据可视化。
    • Scikit-learn:用于机器学习算法。
  7. 调试和测试
    • 使用断点和调试工具。
    • 单元测试和代码覆盖率。
  8. 代码风格和文档
    • 遵循PEP8编码规范。
    • 编写清晰的注释和文档。

学习资源:

  • 在线教程:Codecademy、Coursera、edX上的Python课程。
  • 书籍:《Python Crash Course》、《Automate the Boring Stuff with Python》。
  • 官方文档:Python官网的文档和教程。
  • 实践项目:Kaggle、GitHub上的小项目,或自己设计一些简单的练习。

实践建议:

  • 编写脚本来处理日常任务,如文件整理、数据清洗。
  • 通过小项目应用所学,例如创建一个简单的Web爬虫或数据分析程序。
  • 参与开源项目,贡献代码或文档。

通过以上步骤,你将能够建立扎实的Python编程基础,为进一步学习机器学习和深度学习做好准备。

1.2 数学基础

巩固你的 数学基础,特别是线性代数、微积分和概率论,这些都是理解和实现深度学习算法所必需的。

数学是机器学习和深度学习的重要基石,因为它帮助我们理解算法背后的原理。在1.2节中,我们将复习一些关键的数学概念,这些概念对于理解深度学习模型至关重要。以下是几个核心主题:

  1. 线性代数 (Linear Algebra)
    • 向量(Vectors)与 矩阵(Matrices)的基本概念。
    • 向量加法 和 矩阵加法。
    • 标量乘法 和 矩阵乘法。
    • 转置(Transpose)和 逆(Inverse)的概念。
    • 行列式(Determinant)和 迹(Trace)。
    • 特征值(Eigenvalues)和 特征向量(Eigenvectors)。
    • 内积(Dot product)和 外积(Outer product)。
    • 范数(Norms),例如L1和L2范数。
  2. 微积分 (Calculus)
    • 极限(Limits)和 连续性(Continuity)。
    • 导数(Derivatives)和 偏导数(Partial derivatives)。
    • 梯度(Gradient),即多变量函数的导数。
    • 雅可比矩阵(Jacobian matrix)和 黑塞矩阵(Hessian matrix)。
    • 积分(Integration)。
    • 拉格朗日乘子(Lagrange multipliers)。
  3. 概率论与统计 (Probability and Statistics)
    • 随机变量(Random variables)。
    • 概率分布(Probability distributions),如高斯分布(正态分布)、二项分布、泊松分布等。
    • 期望(Expectation)和 方差(Variance)。
    • 协方差(Covariance)和 相关系数(Correlation coefficient)。
    • 条件概率(Conditional probability)和 贝叶斯定理(Bayes’ theorem)。
  4. 多元微积分 (Multivariable Calculus)
    • 多元函数(Multivariate functions)和 偏导数。
    • 梯度下降(Gradient descent)算法。
    • 链式法则(Chain rule)在反向传播中的应用。
  5. 序列和级数 (Sequences and Series)
    • 数列(Sequences)和 级数(Series)的概念。
    • 收敛(Convergence)和 发散(Divergence)。
  6. 函数优化 (Function Optimization)
    • 凸函数(Convex functions)和 非凸函数(Non-convex functions)。
    • 局部最小值(Local minima)和 全局最小值(Global minima)。
    • 一阶优化算法(First-order optimization algorithms)和 二阶优化算法(Second-order optimization algorithms)。

1.2.6 函数优化

1.2.6.1 凸函数和非凸函数

凸函数和非凸函数是函数优化领域中的重要概念,在机器学习和深度学习中经常遇到。理解这些概念有助于我们了解为什么某些优化算法在特定情况下表现良好,而在其他情况下可能遇到困难。
凸函数 (Convex Functions)
一个函数 f(x) 被称为凸函数,如果它满足以下条件:
对于所有的 𝑥1, 𝑥2 ∈dom(f) (函数的定义域)和所有 0≤𝜃≤1,f(θx1+(1−θ)x2) ≤ θ f(x1)+(1−θ)f(x2)
这意味着连接函数上任意两点的所有线段都在或完全位于函数图像的下方。直观上,一个凸函数看起来像是一个碗形,没有尖锐的拐点或凹陷部分。

在二维空间中,一个凸函数的任何局部最小值也是全局最小值。这是非常重要的,因为这意味着在优化过程中找到的任何最小值都是全局最优解。

非凸函数 (Non-Convex Functions)
非凸函数则是不满足上述凸函数条件的函数。这意味着它们可能包含多个局部最小值、鞍点或者具有复杂的形状。非凸函数可以有多个局部最小值,其中有些可能是全局最小值,而其他的则不是。

在优化非凸函数时,优化算法可能会陷入局部最小值,而无法达到全局最小值。这在深度学习中尤其常见,因为神经网络损失函数往往是非凸的。

图像示例
让我们可视化一个凸函数和一个非凸函数的例子,以便更直观地理解这两个概念。

import numpy as np
import matplotlib.pyplot as plt

# Define a convex function: f(x) = x^2
def convex_function(x):
    return x**2

# Define a non-convex function: f(x) = x^3 - x
def non_convex_function(x):
    return x**3 - x

# Create an array of x values
x_values = np.linspace(-2, 2, 400)

# Compute y values for both functions
y_convex = convex_function(x_values)
y_non_convex = non_convex_function(x_values)

# Plot the convex function
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x_values, y_convex, label='f(x) = x^2')
plt.title('Convex Function')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.legend()
plt.grid(True)

# Plot the non-convex function
plt.subplot(1, 2, 2)
plt.plot(x_values, y_non_convex, label='f(x) = x^3 - x')
plt.title('Non-Convex Function')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

在这里插入图片描述
在上图中,我们有:

左图:显示了一个凸函数 f(x)=x^2。你可以看到,该函数没有局部最小值,其最低点即为全局最小值。
右图:显示了一个非凸函数 f(x)=x^3−x。这个函数有几个关键点,包括两个局部最小值和一个鞍点(既不是局部最小也不是局部最大)。在优化这类函数时,优化算法可能在局部最小值处停止,这取决于初始化点和优化过程。
在机器学习中,损失函数通常是凸函数(如逻辑回归中的对数损失)或近似凸函数(如支持向量机中的合页损失)。然而,在深度学习中,由于神经网络的复杂性,损失函数往往是非凸的,这给优化带来了挑战,需要使用如动量(Momentum)、Adam 或 RMSprop 这样的高级优化算法来避免局部最小值。

1.2.6.3 一阶优化算法 和 二阶优化算法

在深度学习和机器学习中,优化算法是训练模型的核心部分,它们用于最小化或最大化目标函数(如损失函数)。优化算法可以大致分为两类:一阶优化算法和二阶优化算法,这两类算法的区别主要在于它们对目标函数的梯度信息的使用程度。

一阶优化算法(First-order optimization algorithms)
一阶优化算法仅依赖于目标函数的一阶导数(即梯度),它们通过迭代更新参数来寻找最小化或最大化目标函数的解。一阶优化算法的优势在于计算成本较低,因为它们不需要计算二阶导数(海森矩阵),这在高维空间中是非常昂贵的。

常见的一阶优化算法包括:

  • 梯度下降(Gradient Descent)
    最基本的优化算法,通过沿着负梯度方向更新参数来最小化损失函数。
    学习率是算法的关键超参数,决定了更新步长的大小。
  • 随机梯度下降(Stochastic Gradient Descent, SGD)
    每次迭代只使用一个样本或一小批样本(Mini-batch)来估计梯度,这降低了计算成本,但也增加了梯度的方差。
    常用于大规模数据集的训练,因为其更新速度快且能够跳出局部极小点。
  • 动量(Momentum)
    在梯度下降的基础上,引入了动量项,它累积了过去的梯度方向,从而加速收敛过程。
    动量项有助于算法更快地穿过平坦区域,减少振荡。
  • Adagrad
    自适应学习率算法,为每个参数分别调整学习率,基于历史梯度的平方和。
    在稀疏数据集上表现出色,但在连续数据上学习率可能会变得过小。
  • RMSprop
    解决了Adagrad学习率衰减过快的问题,通过指数加权平均的历史梯度平方来调整学习率。
  • Adam(Adaptive Moment Estimation)
    结合了动量和RMSprop的优点,同时使用了偏差修正,是目前最常用的优化算法之一。

二阶优化算法(Second-order optimization algorithms)
二阶优化算法除了使用梯度之外,还利用了目标函数的二阶导数(海森矩阵)或其近似值。理论上,二阶算法可以更快地收敛,因为它们考虑了目标函数的曲率。然而,计算和存储海森矩阵在高维空间中是不切实际的,因此实际应用中通常使用二阶算法的近似版本。

常见的二阶优化算法包括:

  • 牛顿法(Newton’s Method)
    使用海森矩阵和梯度来更新参数,理论上可以二次收敛。
    在高维空间中计算和逆海森矩阵是计算密集型的,因此很少直接使用。
  • 拟牛顿法(Quasi-Newton Methods)
    通过迭代更新海森矩阵的近似值来避免直接计算海森矩阵,如BFGS算法。
    在机器学习中,拟牛顿法的变体如LBFGS被用于小规模问题,因为它们在高维空间中更有效。
  • 自然梯度下降(Natural Gradient Descent)
    利用信息几何中的概念,通过参数空间的几何结构来调整梯度方向,从而加速收敛。
    在深度学习中,自然梯度下降的近似版本如K-FAC被用于加速训练。

随机梯度下降(SGD)在 scikit-learn 中的实现
假设我们使用scikit-learn的SGDClassifier或SGDRegressor来训练一个线性模型。这里我们使用SGDClassifier作为示例:

from sklearn.datasets import load_iris
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载数据
data = load_iris()
X, y = data.data, data.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 数据预处理
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 创建 SGD 分类器
clf = SGDClassifier(loss="log_loss", max_iter=1000, tol=1e-3)

# 训练模型
clf.fit(X_train, y_train)

# 预测
predictions = clf.predict(X_test)

# 打印预测结果
print(predictions)

使用 PyTorch 实现 SGD
在PyTorch中,我们可以使用torch.optim.SGD 来实现随机梯度下降。这里我们将创建一个简单的线性回归模型,并使用SGD进行训练:

import torch
import torch.nn as nn
import torch.optim as optim

# 创建数据
X = torch.randn(100, 1)
y = 2 * X + 1 + 0.1 * torch.randn(100, 1)

# 定义模型
model = nn.Linear(1, 1)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
num_epochs = 1000
for epoch in range(num_epochs):
    # 前向传播
    outputs = model(X)
    loss = criterion(outputs, y)
    
    # 反向传播和优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
# 打印训练后的模型参数
print(list(model.parameters()))

使用 PyTorch 实现 拟牛顿法

在 PyTorch 中直接使用拟牛顿法(如 L-BFGS)进行优化不如使用像 SGD 或 Adam 那样的优化器那样直接和常见。然而,PyTorch 的 torch.optim 模块确实提供了 L-BFGS 优化器。L-BFGS 是拟牛顿法的一种,它使用有限历史的梯度信息来近似海森矩阵的逆,从而避免了直接计算和存储完整的海森矩阵。

下面是一个使用 PyTorch 的 L-BFGS 优化器训练一个简单的模型的示例:

import torch
import torch.nn as nn
import torch.optim as optim

# 创建数据
X = torch.linspace(-3, 3, 100).view(-1, 1)
y = 2 * X + 1 + 0.1 * torch.randn(X.size())

# 定义模型
model = nn.Linear(1, 1)

# 定义损失函数
criterion = nn.MSELoss()

# 定义 L-BFGS 优化器
optimizer = optim.LBFGS(model.parameters())

# 训练模型
def closure():
    optimizer.zero_grad()
    output = model(X)
    loss = criterion(output, y)
    loss.backward()
    return loss

num_epochs = 100
for epoch in range(num_epochs):
    # L-BFGS 需要一个 closure 函数,该函数重新计算损失并返回它
    loss = optimizer.step(closure)
    if (epoch+1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# 打印训练后的模型参数
print(list(model.parameters()))

总结
一阶优化算法因其计算效率和在大规模数据集上的有效性而在深度学习中广泛使用。相比之下,二阶优化算法在理论上有更好的收敛性质,但由于计算成本和存储需求,在实践中通常被一阶算法的改进版本所取代。选择哪种优化算法取决于具体问题的特性和计算资源的限制。在实际应用中,Adam、RMSprop 和 SGD 是最常见的选择。

1.3 熟悉NumPy

NumPy是一个用于Python的科学计算包,它提供了多维数组对象和各种函数来操作这些数组。在学习PyTorch之前,熟悉 NumPy 将会很有帮助。

1.4 介绍Tensor

了解 张量 的概念,这是PyTorch数据结构的基础。张量类似于NumPy的数组,但它们可以利用GPU进行加速计算。

1.5 PyTorch安装和环境设置

学会 安装PyTorch 和配置你的开发环境,包括选择正确的硬件(如GPU)和软件版本。

1.6 PyTorch基础操作

掌握PyTorch中的 基本操作 ,比如创建张量、索引、切片、形状操作和算术运算。

1.7 自动求导和梯度计算

理解PyTorch的 自动求导机制 ,这对于构建和训练神经网络至关重要。
在深度学习中,自动求导是计算神经网络中权重更新的核心。PyTorch 提供了一个强大的自动求导引擎,称为 torch.autograd,它能够自动计算张量图中的梯度。这使得开发者能够专注于构建模型,而不需要手动计算复杂的梯度公式。

1.7.1 张量和梯度

在 PyTorch 中,你可以通过设置 requires_grad=True 来标记一个张量是否需要跟踪其历史操作以便于自动求导。

import torch

# 创建一个张量并设置 requires_grad=True 以追踪其历史
x = torch.tensor(1.0, requires_grad=True)

# 对 x 进行一些操作
y = x * x

# 计算 y 关于 x 的梯度
y.backward()

# 输出 x 的梯度
print(x.grad)

1.7.2 自动求导机制

当一个张量被标记为 requires_grad=True,PyTorch 会在内存中构建一个计算图,这个图记录了所有对这个张量的操作。当调用 .backward() 方法时,计算图会被遍历以计算梯度。

在PyTorch中,自动求导是通过torch.autograd模块实现的。当你创建一个张量时,如果设置了requires_grad=True,那么任何对该张量的操作都会被记录在一个计算图中。这个图由一系列的函数节点组成,每个节点代表一个操作。

深入到 torch.autograd 的 C++ 源码层级(实际上 PyTorch 的核心部分是用 C++ 写的,尽管官方文档和示例经常提到 C,但实际上指的是 C++),我们将会触及 PyTorch 的底层实现。需要注意的是,这里的解释基于对源码的一般性理解,具体实现可能会随版本更新而变化。

1.7.2.1 核心组件

PyTorch 的自动求导引擎是围绕几个关键组件构建的:

  1. Tensor: 代表数据的基本载体,可以是 CPU 或 GPU 上的。
  2. Function: 定义了前向和后向传播的行为。
  3. GraphTask: 表示一个计算图的任务,用于异步执行。
  4. Engine: 负责调度计算图的执行。
1.7.2.2 Tensor 的实现

在 C++ 层面,Tensor 类继承自 at::TensorImpl,后者提供了基础的存储管理、形状信息、数据类型等。Tensor 实现了与自动求导相关的功能,例如 .grad_fn() 方法,用于获取指向 Function 的指针,该 Function 产生了这个 Tensor

1.7.2.3 Function 的实现

Function 类定义了前向和后向传播的行为。在 C++ 中,这些操作通常通过重载的 apply 方法实现。Function 也负责保存用于后向传播的中间数据。
在 C++ 中,Function 类并不直接暴露给用户,而是通过 std::shared_ptr<Function> 和相关联的 VariableTensor 使用。

1.7.2.4 自动求导引擎

PyTorch 的自动求导引擎主要由 autograd::Engine 类实现。引擎负责构建和执行计算图。它维护了一个全局的任务队列,用于异步执行计算图上的任务。引擎还管理了计算图的生命周期,包括何时执行前向和后向传播。

1.7.2.5 计算图的执行

计算图的执行是通过 GraphTask 类来实现的。当调用 Tensor.backward() 方法时,引擎会构建一个 GraphTask,然后将其推送到任务队列中等待执行。GraphTask 包含了执行计算图所需的所有信息,包括节点、边和执行顺序。

1.7.2.6 异步执行和并行性

PyTorch 的自动求导引擎支持异步执行和并行计算。引擎可以利用多核 CPU 或多个 GPU 并行地执行计算图的不同部分。这是通过将计算图分割成独立的任务,并将这些任务提交给线程池来实现的。

1.7.2.7 性能优化

为了提高性能,torch.autograd 引擎实现了一系列的优化,包括:

  • 梯度累积:当有多个梯度来源时,它们会被累积起来,而不是多次更新权重。
  • in-place 操作:某些操作可以在原始数据上执行,避免不必要的内存分配和复制。
  • GPU 利用:引擎能够调度 GPU 上的计算,利用 CUDA 进行加速。

深入理解 torch.autograd 的 C++ 源码可以揭示 PyTorch 如何高效地执行自动求导和反向传播。然而,这需要对 C++ 和深度学习算法有深厚的了解,以及对 PyTorch 源码的详细研究。对于大多数用户而言,了解高层次的接口和使用方式就已经足够了。如果需要进行底层优化或扩展 PyTorch 的功能,那么深入源码是必要的。

1.7.3 损失函数与反向传播

在神经网络训练中,我们通常定义一个损失函数来衡量模型预测值与真实值之间的差异。损失函数关于模型权重的梯度告诉我们权重应该如何更新以减小损失。

# 假设 net 是一个神经网络模型,inputs 是输入数据,targets 是目标输出
net = ...  # 网络定义
inputs = ...  # 输入数据
targets = ...  # 目标输出

# 前向传播
predictions = net(inputs)

# 定义损失函数,例如均方误差
loss = (predictions - targets).pow(2).mean()

# 清零梯度缓存,防止梯度累积
net.zero_grad()

# 反向传播,计算梯度
loss.backward()

1.7.4 更新权重

在计算了梯度之后,我们可以通过优化器(如 SGD 或 Adam)来更新模型的权重。优化器负责根据计算出的梯度来调整权重,以最小化损失函数。

optimizer = torch.optim.SGD(net.parameters(), lr=0.01)  # 创建一个优化器

# 前向传播和反向传播步骤
...

# 使用优化器更新权重
optimizer.step()

在实际应用中,自动求导机制极大地简化了深度学习模型的训练过程,使开发者能够更加关注模型架构的设计,而无需担心底层的数学细节。

1.8神经网络构建

学习如何用PyTorch构建简单的 神经网络 ,并理解前向传播和反向传播的过程。

1.9 深度学习优化器

熟悉 优化器 (如SGD、Adam等),它们在训练神经网络时调整权重和偏置以最小化损失函数。

1.10 数据加载和预处理

掌握如何在PyTorch中 加载和预处理数据 ,包括使用DataLoader和Transforms。

2. 📚 主课程大纲

一旦我们完成了预备课程,我们将进入PyTorch的主课程,从以下几点开始:

2.1 PyTorch入门

介绍PyTorch的核心概念,包括张量操作、自动求导和神经网络模块。

2.2. 构建和训练神经网络

通过构建和训练一个简单的神经网络,加深对PyTorch API的理解。

2.3 高级神经网络架构

探索 高级神经网络架构 ,如卷积神经网络(CNNs)、循环神经网络(RNNs)和长短时记忆网络(LSTMs)。

2.4 模型保存和加载

学习如何 保存和加载模型 ,以便于后续使用或部署。

2.5 迁移学习

理解 迁移学习 的概念,它允许我们利用预训练的模型来加速自己的模型训练。

2.6 PyTorch中的强化学习

虽然不是必须的,但对于有兴趣的同学,我们可以介绍如何使用PyTorch进行 强化学习 实验。

2.7 PyTorch Lightning

了解 PyTorch Lightning ,这是一个旨在简化PyTorch复杂性的高级接口。

PyTorch Lightning 是一个用于科研和生产的轻量级深度学习框架,它建立在 PyTorch 之上,旨在简化深度学习模型的开发和训练过程。PyTorch Lightning 提供了一种更为简洁和结构化的训练循环,同时保留了 PyTorch 的灵活性和控制力。

2.7.1 PyTorch Lightning 的核心理念

PyTorch Lightning 的核心目标是将训练代码从样板代码中解放出来,使研究人员和工程师能够专注于模型架构和实验设计,而不是训练循环的细节。它通过提供一个基础的训练类 LightningModule,将常见的训练逻辑封装起来,同时允许用户轻松地扩展和定制。

2.7.2 LightningModule

LightningModule 是 PyTorch Lightning 的核心组件,它是一个继承自 torch.nn.Module 的类,包含了训练、验证、测试以及预测的钩子方法。下面是一些常用的方法:

  • __init__(self, ...): 构造函数,用于初始化模型参数和其他设置。
  • forward(self, x): 定义了模型的前向传播逻辑。
  • training_step(self, batch, batch_idx): 定义了单个训练步骤,返回一个包含损失和日志指标的字典。
  • validation_step(self, batch, batch_idx): 定义了单个验证步骤,返回一个包含度量的字典。
  • test_step(self, batch, batch_idx): 定义了单个测试步骤,返回一个包含度量的字典。
  • configure_optimizers(self): 返回优化器或优化器和学习率调度器的配置。
  • training_epoch_end(self, outputs): 在一个训练 epoch 结束时调用,用于汇总 epoch 的结果。
  • validation_epoch_end(self, outputs): 在一个验证 epoch 结束时调用,用于汇总 epoch 的结果。

2.7.2 Trainer

Trainer 类是 PyTorch Lightning 的另一个重要组成部分,它负责模型的训练、验证、测试和预测。Trainer 提供了多种功能,如自动批处理、设备分配(CPU/GPU/TPU)、数据并行、日志记录、检查点保存等。

2.7.3 代码示例

下面是一个使用 PyTorch Lightning 的简单示例:

import pytorch_lightning as pl
import torch
from torch import nn
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision import transforms

class LitModel(pl.LightningModule):
    def __init__(self):
        super().__init__()
        self.l1 = nn.Linear(28 * 28, 10)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

    def training_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self(x)
        loss = F.cross_entropy(y_hat, y)
        self.log('train_loss', loss)
        return loss

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=0.02)

trainer = pl.Trainer(max_epochs=2)
model = LitModel()

# Prepare your data
train_loader = DataLoader(MNIST(os.getcwd(), train=True, download=True,
                                transform=transforms.ToTensor()),
                          batch_size=64)

# Train the model
trainer.fit(model, train_loader)

PyTorch Lightning 通过提供一个高度结构化的 API 和内置的训练循环,大大简化了深度学习模型的开发和训练。它非常适合于快速原型设计、研究实验以及大规模生产环境中的模型训练。

2.8 PyTorch在GPU上的应用

学习如何利用GPU来加速PyTorch的训练过程,提高效率。

2.9 实践项目

完成一个 实践项目 ,将你在课程中学到的知识应用于解决一个实际问题。

2.10 评估和优化

评估你的模型性能,并学习如何优化模型以获得更好的结果。

在你的预备课程和主课程大纲基础上,后续的学习内容可以进一步深化对PyTorch及深度学习的理解,并扩展到更高级的主题。以下是大纲1.10“评估和优化”之后可能涵盖的内容:

3. 📚 高级课程大纲

3.1 高级优化技巧

  • 学习率调度策略
  • 正则化技术(L1/L2正则化,Dropout)
  • 批量归一化(Batch Normalization)

3.2 模型压缩和量化

模型压缩和量化是深度学习领域中非常重要的技术,特别是在部署模型到资源受限的设备(如移动设备或边缘计算设备)时。这些技术可以帮助减少模型的大小,降低计算成本,同时尽可能保持模型的准确性和性能。下面是关于模型压缩和量化的一些关键概念和方法:

  • 网络剪枝
  • 量化感知训练(Quantization-Aware Training)
  • 模型蒸馏(Model Distillation)

3.2.1 模型压缩

模型压缩的主要目标是减少模型的大小和计算需求,以便于更快的推理速度和更低的内存消耗。常见的模型压缩技术包括:

  • 权重剪枝(Pruning):通过移除网络中不重要的连接或神经元来减少模型的参数数量。剪枝可以是结构化的(例如,移除整个通道或层)或非结构化的(移除特定权重)。
  • 矩阵分解(Matrix Factorization):将大的权重矩阵分解为两个较小的矩阵,从而减少参数数量。
  • 知识蒸馏(Knowledge Distillation):使用一个大型的教师模型来训练一个小型的学生模型,学生模型试图模仿教师模型的行为,从而达到相似的性能但占用更少的资源。
  • 低秩近似(Low-Rank Approximation):将权重矩阵近似为低秩矩阵,以减少参数数量。
3.2.1.1 矩阵分解(Matrix Factorization)

在深度学习领域,矩阵分解(Matrix Factorization)可以作为一种有效的模型压缩技术,尤其是用于卷积神经网络(CNNs)的权重压缩。下面将重点介绍几种在深度学习中常用的矩阵分解技术及其实现:

奇异值分解(SVD)

在深度学习中,SVD 可以用于压缩卷积层的权重矩阵。卷积层的权重可以被看作是一个四维张量,但可以重新排列为二维矩阵,然后应用 SVD 分解。通过保留最重要的奇异值,可以降低权重矩阵的秩,从而减少参数数量。

import torch
import torch.nn as nn

# 假设有一个卷积层
conv_layer = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3)

# 获取权重并重塑为二维矩阵
weights = conv_layer.weight.data
reshaped_weights = weights.view(weights.size(0), -1)

# 应用 SVD 分解
U, S, Vt = torch.svd(reshaped_weights)

# 保留前 k 个奇异值
k = 100
U_k = U[:, :k]
S_k = S[:k]
V_k = Vt[:k, :]

# 重构权重矩阵
reconstructed_weights = torch.mm(U_k * S_k.unsqueeze(1), V_k)

# 将重构的权重赋值回卷积层
conv_layer.weight.data = reconstructed_weights.view_as(weights)
低秩近似

低秩近似是另一种压缩权重矩阵的方法,通常与 SVD 相关。在深度学习中,可以通过分解权重矩阵为两个较小的矩阵的乘积来实现,这相当于创建一个低秩近似的权重矩阵。

import torch
import torch.nn as nn

# 同上,假设有一个卷积层
conv_layer = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3)

# 获取权重并重塑为二维矩阵
weights = conv_layer.weight.data
reshaped_weights = weights.view(weights.size(0), -1)

# 低秩近似,这里使用两个随机矩阵初始化
rank = 100
W1 = torch.randn(rank, reshaped_weights.size(1))
W2 = torch.randn(reshaped_weights.size(0), rank)

# 通过反向传播学习这两个矩阵
W1 = nn.Parameter(W1)
W2 = nn.Parameter(W2)

# 使用优化器进行学习
optimizer = torch.optim.SGD([W1, W2], lr=0.01)

# 在训练循环中更新这两个矩阵
for inputs, targets in dataloader:
    optimizer.zero_grad()
    approximated_weights = torch.mm(W1, W2)
    approximated_weights = approximated_weights.view_as(weights)
    conv_layer.weight.data = approximated_weights
    output = conv_layer(inputs)
    loss = loss_function(output, targets)
    loss.backward()
    optimizer.step()
Tucker 分解

Tucker 分解是一种多线性代数中的矩阵分解方法,适用于多维张量。在深度学习中,可以直接应用于卷积层的四维权重张量,通过分解来减少参数数量。

在 PyTorch 中,Tucker 分解没有直接的实现,但可以通过一些库如 tensorly 来辅助实现。

import tensorly as tl
from tensorly.decomposition import tucker

# 同样,假设有一个卷积层
conv_layer = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3)

# 获取权重
weights = conv_layer.weight.data

# 应用 Tucker 分解
core, factors = tucker(weights.numpy(), ranks=[10, 10, 10, 10])

# 将分解的结果转换回权重张量
reconstructed_weights = tl.tucker_to_tensor((core, factors))

# 将重构的权重赋值回卷积层
conv_layer.weight.data = torch.from_numpy(reconstructed_weights)
矩阵分解总结

在深度学习中,矩阵分解是一种有效的模型压缩手段,通过减少权重矩阵的参数数量,可以降低模型的计算复杂度和内存占用,同时尽量保持模型的性能。以上介绍了 SVD、低秩近似和 Tucker 分解在深度学习中的应用和实现,它们各有特点,可以根据具体场景和需求选择合适的方法。

3.2.1.2 知识蒸馏(Knowledge Distillation)

知识蒸馏(Knowledge Distillation,简称KD)是一种模型压缩技术,通过训练一个较小的模型(学生模型)来模仿一个较大模型(教师模型)的行为,从而使学生模型能够以较低的计算成本达到接近教师模型的性能。这种方法最初由Hinton等人在2015年的论文《Distilling the Knowledge in a Neural Network》中提出,已经成为深度学习领域中一个非常流行的模型压缩和迁移学习技术。

知识蒸馏原理

在知识蒸馏中,教师模型通常是预先训练好的,性能较高但计算成本也相对较大。学生模型则是一个结构较为简单的小模型,目标是通过学习教师模型的输出来提升自身性能。知识蒸馏的关键在于使用教师模型的软标签(Soft Label)来指导学生模型的学习,软标签是教师模型对输入样本的预测概率分布,相比于硬标签(Hard Label,即通常的分类标签),软标签包含了更多的信息,可以帮助学生模型学习到教师模型的决策边界。

知识蒸馏的实现

知识蒸馏的具体实现通常涉及以下步骤:

  1. 准备教师模型和学生模型:选择一个已经训练好的大型模型作为教师模型,以及一个较小的模型作为学生模型。
  2. 设置蒸馏温度:蒸馏过程中会使用一个温度参数T,用于控制教师模型输出的软化程度。较高的温度会导致输出的分布更加平滑,有利于学生模型的学习。
  3. 计算损失函数:知识蒸馏的损失函数通常包括两部分:一部分是学生模型输出与真实标签之间的损失(如交叉熵损失),另一部分是学生模型输出与教师模型输出之间的损失(也是交叉熵损失)。后者的权重通常会随着温度T的平方成比例增加。
  4. 训练学生模型:使用带有软标签和硬标签的损失函数训练学生模型。通常情况下,教师模型的输出会作为监督信号来引导学生模型的学习。
  5. 调整温度参数:在训练过程中,可以调整温度参数T,以找到最优的蒸馏效果。
  6. 评估学生模型:在训练结束后,评估学生模型的性能,确保它能够达到接近教师模型的表现。
知识蒸馏在PyTorch中的实现

在PyTorch中实现知识蒸馏,可以采用以下伪代码作为参考:

import torch
import torch.nn as nn
import torch.optim as optim

# 教师模型和学生模型的定义
teacher_model = ...
student_model = ...

# 损失函数和优化器的定义
criterion_kd = nn.KLDivLoss(reduction='batchmean')
criterion_ce = nn.CrossEntropyLoss()
optimizer = optim.SGD(student_model.parameters(), lr=0.01)

# 训练循环
for inputs, labels in dataloader:
    optimizer.zero_grad()
    
    # 教师模型的输出
    with torch.no_grad():
        teacher_output = teacher_model(inputs)
        teacher_output = teacher_output / temperature
    
    # 学生模型的输出
    student_output = student_model(inputs)
    student_output = student_output / temperature
    
    # 计算交叉熵损失和KL散度损失
    loss_ce = criterion_ce(student_output, labels)
    loss_kd = criterion_kd(F.log_softmax(student_output, dim=1),
                           F.softmax(teacher_output, dim=1))
    
    # 总损失
    loss = loss_ce + alpha * temperature**2 * loss_kd
    
    # 反向传播和优化
    loss.backward()
    optimizer.step()

在上述代码中,temperature 控制软标签的平滑程度,alpha 控制蒸馏损失相对于交叉熵损失的权重。通过调整这两个参数,可以优化学生模型的学习效果。

知识蒸馏总结

知识蒸馏是一种有效的模型压缩技术,通过让较小的学生模型学习较大教师模型的决策,可以在不牺牲太多性能的情况下显著减少模型的计算成本。在实际应用中,知识蒸馏可以广泛应用于从图像分类到自然语言处理的各种深度学习任务中。

3.2.2 模型量化

模型量化是指将模型的权重和激活从浮点表示转换为整数表示,从而减少模型的存储需求和计算成本。量化可以是:

  • 权重量化:将权重从32位浮点数转换为8位或更低比特的整数。
  • 激活量化:将神经元的激活值也进行量化。

量化通常分为两种类型:

  • Post-Training Quantization:在模型训练完成后进行量化,适用于已经训练好的模型。
  • Quantization-Aware Training:在模型训练过程中就考虑到量化的影响,这样可以让模型在训练时就适应量化带来的精度损失。
3.2.2.1 模型量化PTQ的实现

在PyTorch中,可以使用torch.quantization模块来实施模型量化。例如,下面是如何使用PyTorch进行模型量化的简单流程:

  1. 准备模型:确保模型可以正常运行。
  2. 插入观察器:在模型中插入观察器,以收集权重和激活的统计信息,为量化做准备。
  3. 校准模型:使用未标记的数据集运行模型,让观察器收集统计信息。
  4. 量化模型:使用收集到的统计信息,将模型转换为量化模型。
  5. 评估量化模型:评估量化模型的性能,确保其满足准确性要求。
import torch
import torch.nn as nn
import torch.quantization

# 假设 model 是你想要量化的模型
model = ...

# 准备模型进行量化
model.qconfig = torch.quantization.get_default_qconfig('fbgemm')
torch.quantization.prepare(model, inplace=True)

# 校准模型
data_loader = ...
for images, labels in data_loader:
    model(images)

# 量化模型
torch.quantization.convert(model, inplace=True)

# 测试量化后的模型

fbgemm 是 Facebook 开发的一种针对低精度(如 int8)矩阵乘法的高性能库,全称是 “Facebook’s GEMM”。GEMM 是 General Matrix Multiply 的缩写,是线性代数中矩阵乘法的标准术语。**fbgemm**** 专门优化了在低精度数据类型上的矩阵乘法性能**,尤其在使用整数量化后的深度学习模型中,能够显著提升计算速度和能效比。
在 PyTorch 中,fbgemm 是作为后端量化引擎之一被使用的,尤其是在 CPU 上进行量化推理时。当你在 PyTorch 中设置模型的量化配置时,指定 'fbgemm' 作为后端,意味着你希望使用 fbgemm 的优化来加速量化后的模型在 CPU 上的执行。
在这个过程中,fbgemm 被用来优化量化后的模型在 CPU 上的执行,特别是矩阵乘法操作。这在移动端或边缘设备上尤为重要,因为这些设备通常具有有限的计算能力和功耗预算,而 fbgemm 的优化可以确保即使在这些资源受限的环境下,模型也能高效运行。
值得注意的是,fbgemm 主要针对 CPU 进行优化,如果你的目标平台是 GPU,PyTorch 也提供了其他后端,如 qnnpack,它针对 GPU 和 ARM 架构进行了优化。选择正确的后端对于实现模型在特定硬件上的最佳性能至关重要。

3.2.2.2 模型量化QAT的实现

Quantization-Aware Training(QAT,量化感知训练)是一种在模型训练阶段就考虑量化效果的技术,目的是为了让模型在量化后仍然能够保持良好的精度。在传统的Post-Training Quantization中,模型是在训练完成后再进行量化,此时模型的权重和结构已经固定,量化可能导致性能下降。相比之下,QAT在模型训练时就模拟量化的效果,使得模型在训练过程中逐渐适应量化带来的变化,从而在量化后仍能维持较高的精度。

QAT 的原理

QAT 的核心思想是在训练过程中模拟量化操作,这通常包括:

  1. 权重和激活的量化:在前向传播中,模型的权重和激活会被量化到较低比特(如8位整数)再进行计算。量化操作会引入误差,但通过反向传播,模型会逐渐学习如何在量化条件下最小化损失。
  2. 量化噪声注入:为了模拟量化误差,有时会在训练过程中向权重和激活中加入噪声,这有助于模型学习如何在存在量化噪声的情况下工作。
  3. 动态范围调整:QAT 还包括动态范围的调整,以确定每个层的最佳量化范围。这通常通过在训练过程中监控权重和激活的分布来实现。
QAT 的实现

在 PyTorch 中,实现 QAT 涉及到以下几个步骤:

  1. 配置模型的量化设置:你需要为模型设置一个 qconfig,这决定了量化方案,包括权重和激活的量化器类型。
  2. 准备模型:使用 torch.quantization.prepare_qat 函数准备模型,这会在模型中插入量化感知模块。
  3. 训练模型:使用量化感知的模型进行训练,这通常与常规训练类似,但模型会学习在量化条件下表现良好。
  4. 转换模型:完成训练后,使用 torch.quantization.convert 函数将模型转换为完全量化的模型。

下面是一个使用 PyTorch 实现 QAT 的示例代码片段:

import torch
import torch.nn as nn
import torch.quantization

# 创建模型
model = ...

# 配置量化感知训练
model.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm')

# 准备模型进行量化感知训练
torch.quantization.prepare_qat(model, inplace=True)

# 训练模型
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()
train_loader = ...

for epoch in range(num_epochs):
    for inputs, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

# 转换模型为完全量化模型
torch.quantization.convert(model, inplace=True)

在上述代码中,get_default_qat_qconfig 函数返回一个默认的量化配置,包括权重和激活的量化器类型。prepare_qat 函数在模型中插入量化感知模块,而 convert 函数最终将模型转换为完全量化的模型。

QAT总结

Quantization-Aware Training 是一种在训练阶段就考虑量化效果的技术,它通过在训练过程中模拟量化操作,使得模型能够在量化后仍然保持良好的精度。在实际应用中,QAT 特别适用于那些需要在资源受限设备上运行的模型,如移动设备或边缘计算设备。通过使用 PyTorch 的量化工具,可以方便地在模型训练中加入 QAT,从而优化模型在量化条件下的性能。

3.2.3 模型压缩和量化总结

模型压缩和量化是深度学习模型优化的重要组成部分,它们可以帮助在保证性能的同时,减少模型的计算和存储需求。在实际应用中,这些技术往往需要结合使用,以达到最佳的性能和资源利用率。如果你正在考虑将模型部署到边缘设备,那么掌握模型压缩和量化技术将是非常有益的。

3.3 高级计算机视觉任务

  • 目标检测(Object Detection)和实例分割(Instance Segmentation)
  • 图像风格迁移(Style Transfer)
  • 生成对抗网络(Generative Adversarial Networks, GANs)

3.4 自然语言处理(NLP)应用

  • 语言模型(Language Models)
  • 机器翻译(Machine Translation)
  • 情感分析(Sentiment Analysis)
  • 变分自编码器(Variational Autoencoders, VAEs)

3.5 序列建模和注意力机制

  • 注意力机制(Attention Mechanisms)
  • 门控循环单元(Gated Recurrent Units, GRUs)
  • 变形金刚(Transformers)

3.6 高级强化学习

  • 深度Q网络(Deep Q-Networks, DQN)
  • 策略梯度(Policy Gradients)
  • 异步优势行动者-评论家(Asynchronous Advantage Actor-Critic, A3C)

3.7 高性能计算和分布式训练

  • 多GPU训练
  • 分布式训练(使用Horovod或其他框架)
  • TPU训练

3.8 模型部署和服务

  • 模型转换为ONNX或TorchScript
  • 在生产环境中部署模型
  • REST API和微服务

模型部署和服务是深度学习和机器学习项目中的关键步骤,涉及将训练好的模型转化为可以在现实世界场景中使用的服务。这一环节确保模型能够接收实时或批量数据输入,并返回预测结果。以下是关于模型部署和服务的一些重要概念和技术:

3.8.1 模型导出与转换

3.8.1.1 ONNX

ONNX,全称 Open Neural Network Exchange,是一个开放格式,用于表示深度学习和机器学习模型,旨在促进不同框架之间模型的互操作性。ONNX 支持多个流行的人工智能框架,包括 PyTorch、TensorFlow、Keras、MXNet、Caffe2、CNTK 等,使得模型可以在这些框架之间轻松转换。

ONNX 的主要特点
  1. 框架间互操作性:ONNX 提供了一个通用的模型描述,使得模型可以在不同框架之间共享和转换,便于模型的开发和部署。

  2. 广泛的框架支持:ONNX 支持多种深度学习和机器学习框架,可以作为这些框架之间的桥梁。

  3. 高性能执行:ONNX Runtime 是一个高性能的推理引擎,用于执行 ONNX 格式的模型,支持 CPU、GPU、FPGA 等多种硬件加速。

  4. 模型优化:ONNX 提供了一系列工具和优化器,可以用于简化模型、减少内存占用、提高运行速度。

  5. 可扩展性:ONNX 的设计允许添加新的操作符和数据类型,以适应不断发展的 AI 领域的需求。

ONNX 的模型结构

ONNX 模型由一个或多个计算图(Graph)组成,每个图由一系列节点(Node)构成,节点表示模型中的操作,如加法、卷积等。每个节点可以有多个输入和输出,这些输入和输出可以是模型的输入数据、模型的输出结果或图中其他节点的输出。

转换模型到 ONNX

在 PyTorch 中,可以使用 torch.onnx.export 函数将模型转换为 ONNX 格式。以下是一个简单的示例:

import torch
from torchvision import models

# 加载预训练的模型
model = models.resnet18(pretrained=True)

# 设置模型为评估模式
model.eval()

# 创建一个示例输入
dummy_input = torch.randn(1, 3, 224, 224)

# 导出模型到 ONNX 格式
torch.onnx.export(model, dummy_input, "resnet18.onnx",
                  input_names=['input'],
                  output_names=['output'],
                  dynamic_axes={'input': {0: 'batch_size'}, 
                                'output': {0: 'batch_size'}})

在这个例子中,我们导出了一个预训练的 ResNet-18 模型到 ONNX 格式,其中 dummy_input 是模型期望的输入尺寸的示例,dynamic_axes 参数允许模型处理不同大小的批次。

使用 ONNX Runtime 进行推理

ONNX Runtime 是一个高性能的推理引擎,可以用于在各种硬件上执行 ONNX 模型。以下是一个使用 ONNX Runtime 进行推理的例子:

import onnxruntime as ort
import numpy as np

# 加载 ONNX 模型
ort_session = ort.InferenceSession("resnet18.onnx")

# 运行模型
outputs = ort_session.run(None, {'input': dummy_input.numpy()})

# 输出结果
print(outputs)

在这个例子中,我们使用了 ONNX Runtime 来加载之前导出的 ONNX 模型,并运行了一个示例输入,得到了模型的输出结果。

ONNX 总结

ONNX 是一个重要的工具,它促进了不同深度学习框架之间的模型共享,使得模型的部署和优化变得更加灵活和高效。通过 ONNX,开发者可以轻松地将模型从一个框架转移到另一个框架,或部署到各种硬件平台上,从而提高了模型的实用性和可移植性。

3.8.1.2 TorchScript

TorchScript

  • TorchScript 是 PyTorch 提供的一种静态类型系统和编译器,可以将 PyTorch 动态图模型转换为静态图模型。
  • 适用于模型的序列化和部署,特别是在移动设备和边缘设备上。

暂未展开介绍。

3.8.2 模型服务体系

服务框架

  • Flask/Django:Flask 和 Django 是 Python Web 框架,可以用来创建 RESTful API,用于接收 HTTP 请求并返回模型预测结果。
  • TensorFlow Serving:TensorFlow Serving 是一个灵活的高性能服务器,专门设计用于部署机器学习模型。支持多种模型类型,可以热加载模型而无需重启服务。
  • Seldon Core:Seldon Core 是一个基于 Kubernetes 的平台,用于在生产环境中部署机器学习模型。支持多种模型格式和框架,如 TensorFlow、PyTorch 和 XGBoost。

批量预测

  • Apache Airflow: Apache Airflow 是一个用于工作流调度的平台,可以用来管理周期性的批量预测任务。
  • Kubernetes Jobs/CronJobs:Kubernetes Jobs 用于执行一次性任务,而 CronJobs 可以定期执行 Jobs。

微服务架构

  • Docker:Docker 提供容器化技术,可以将模型及其运行环境打包,便于在任何地方部署。
  • Kubernetes:Kubernetes 是一个容器编排平台,可以管理 Docker 容器的部署、扩展和运维。

监控与日志

  • Prometheus/Grafana:Prometheus 是一个开源监控系统,可以收集和存储指标数据。Grafana 提供可视化仪表板,用于展示模型服务的性能指标。
  • ELK Stack:ELK Stack (Elasticsearch, Logstash, Kibana) 用于收集、存储和分析日志数据。

安全与访问控制

  • OAuth2/OpenID Connect:使用 OAuth2 和 OpenID Connect 进行身份验证和授权,确保只有授权用户才能访问模型服务。
  • TLS/HTTPS:使用 TLS 协议加密通信,确保数据传输的安全性。

性能优化

  • 模型优化:可以使用量化、剪枝等技术来减小模型大小,提高运行速度。

  • 异步处理:异步处理可以避免请求阻塞,提高模型服务的响应能力。

  • 负载均衡:使用负载均衡器来分散请求,提高系统的可用性和伸缩性。

持续集成/持续部署 (CI/CD)

  • Jenkins/GitLab CI/CD:CI/CD 工具可以自动化测试、构建和部署流程,确保模型服务的稳定性和可靠性。

通过这些技术和工具,你可以构建一个高效、安全且可扩展的模型部署和服务体系,使模型能够在各种环境中顺利运行并服务于实际业务需求。

3.9 深度学习的伦理和安全

  • 偏见和公平性
  • 模型解释性和可审计性
  • 隐私保护机制

3.10 最新研究和前沿技术

  • 阅读最新的深度学习论文
  • 探索新兴的深度学习框架和库

3.10.1 IPEX_LLM

https://datawhaler.feishu.cn/wiki/SuppwSfFZi5KvYkiffUcygVNnPe

这个有点题外话了,先放着吧。

IPEX-LLM 推理qwen2模型

在开始之前,我们可以先认识一下什么是 IPEX-LLM,** IPEX-LLM是一个PyTorch库**,用于在Intel CPU和GPU(例如,具有iGPU的本地PC,Arc、Flex和Max等独立GPU)上以非常低的延迟运行LLM.总而言之我们可以利用它加快大语言模型在 intel 生态设备上的运行速度;无需额外购买其他计算设备,我们可以高速率低消耗的方式在本地电脑上运行大语言模型。

在本次比赛的第一篇教程中,我们就能掌握 IPEX-LLM 的基本使用方法,我们将利用 IPEX-LLM 加速 Qwen2 语言模型的运行,跟随这篇 notebook 一步步仔细操作,我们可以简单快速的掌握大语言模型在 intel 硬件上的高性能部署.

在开始运行推理之前,我们需要准备好运行 qwen2 需要的必须环境,此时请确保你进入的镜像是 ubuntu22.04-py310-torch2.1.2-tf2.14.0-1.14.0 否则将会看到找不到 conda 文件夹的报错,切记。

你将在终端运行下列脚本,进行 ipex-llm 的正式 conda 环境的恢复,恢复完成后关闭所有开启的 notebook 窗口,然后重新打开,才能正常切换对应 kernel。

那么,什么是 kernel 呢?简单理解,它用于提供 python代码运行所需的所有支持,而会把我们的消息发送到对应的 kernel 进行执行。你可以在 notebook 右上角看到 Python3(ipykernel) 的字样,它代表默认环境的内核;我们可以通过在对应虚拟环境启动 jupyter notebook 使用对应虚拟环境的内核环境,也可以使用类似 python3 -m ipykernel install --name=ipex 的指令将某个虚拟环境(在这里是 ipex)注册到 notebook 的可使用内核中。

%%writefile /mnt/workspace/install.sh
# 切换到 conda 的环境文件夹
cd  /opt/conda/envs 
mkdir ipex
# 下载 ipex-llm 官方环境
wget https://s3.idzcn.com/ipex-llm/ipex-llm-2.1.0b20240410.tar.gz 
# 解压文件夹以便恢复原先环境
tar -zxvf ipex-llm-2.1.0b20240410.tar.gz -C ipex/ && rm ipex-llm-2.1.0b20240410.tar.gz
# 安装 ipykernel 并将其注册到 notebook 可使用内核中
/opt/conda/envs/ipex/bin/python3 -m pip install ipykernel && /opt/conda/envs/ipex/bin/python3 -m ipykernel install --name=ipex

当你运行完上面的代码块后,此时会在 /mnt/workspace 目录下创建名为 install.sh 名字的 bash 脚本,你需要打开终端,执行命令bash install.sh运行 bash 脚本,等待执行完毕后关闭所有的 notebook 窗口再重新打开,直到你在右上角点击 Python3 (ipykernel) 后可以看到名为 ipex 的环境,点击后切换即可进入到 ipex-llm 的正式开发环境,你也可以在终端中执行 conda activate ipex 启动 ipex 的虚拟环境,至此准备工作完成。

Qwen2是阿里云最新推出的开源大型语言模型系列,相比Qwen1.5,Qwen2实现了整体性能的代际飞跃,大幅提升了代码、数学、推理、指令遵循、多语言理解等能力。

包含5个尺寸的预训练和指令微调模型:Qwen2-0.5B、Qwen2-1.5B、Qwen2-7B、Qwen2-57B-A14B和Qwen2-72B,其中Qwen2-57B-A14B为混合专家模型(MoE)。所有尺寸模型都使用了GQA(分组查询注意力)机制,以便让用户体验到GQA带来的推理加速和显存占用降低的优势。

在中文、英语的基础上,训练数据中增加了27种语言相关的高质量数据。增大了上下文长度支持,最高达到128K tokens(Qwen2-72B-Instruct)。

在这里,我们将使用 Qwen/Qwen2-1.5B-Instruct 的模型参数版本来体验 Qwen2 的强大能力。

首先,我们需要对模型进行下载,我们可以通过 modelscope 的 api 很容易实现模型的下载:

import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os
# 第一个参数表示下载模型的型号,第二个参数是下载后存放的缓存地址,第三个表示版本号,默认 master
model_dir = snapshot_download('Qwen/Qwen2-1.5B-Instruct', cache_dir='qwen2chat_src', revision='master')

下载完成后,我们将对 qwen2 模型进行低精度量化至 int4 ,**低精度量化(Low Precision Quantization)**是指将浮点数转换为低位宽的整数(这里是int4),以减少计算资源的需求和提高系统的效率。这种技术在深度学习模型中尤其重要,它可以在硬件上实现快速、低功耗的推理,也可以加快模型加载的速度。

经过 Intel ipex-llm 优化后的大模型加载 api from ipex_llm.transformers import AutoModelForCausalLM, 我们可以很容易通过 load_in_low_bit='sym_int4' 将模型量化到 int4 ,英特尔 IPEX-LLM 支持 ‘sym_int4’, ‘asym_int4’, ‘sym_int5’, ‘asym_int5’ 或 'sym_int8’选项,其中 ‘sym’ 和 ‘asym’ 用于区分对称量化与非对称量化。 最后,我们将使用 save_low_bit api 将转换后的模型权重保存到指定文件夹。

from ipex_llm.transformers import AutoModelForCausalLM
from transformers import  AutoTokenizer
import os
if __name__ == '__main__':
    model_path = os.path.join(os.getcwd(),"qwen2chat_src/Qwen/Qwen2-1___5B-Instruct")
    model = AutoModelForCausalLM.from_pretrained(model_path, load_in_low_bit='sym_int4', trust_remote_code=True)
    tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
    model.save_low_bit('qwen2chat_int4')
    tokenizer.save_pretrained('qwen2chat_int4')

准备完转换后的量化权重,接下来我们将在终端中第一次运行 qwen2 在 CPU 上的大模型推理,但请注意不要在 notebook 中运行(本地运行可以在 notebook 中运行,由于魔搭 notebook 和终端运行脚本有一些区别,这里推荐在终端中运行。

%%writefile /mnt/workspace/run.py
# 导入必要的库
import os
# 设置OpenMP线程数为8,优化CPU并行计算性能
os.environ["OMP_NUM_THREADS"] = "8"
import torch
import time
from ipex_llm.transformers import AutoModelForCausalLM
from transformers import AutoTokenizer

# 指定模型加载路径
load_path = "qwen2chat_int4"  #"qwen2chat_src/Qwen/Qwen2-1___5B-Instruct"
# 加载低位(int4)量化模型,trust_remote_code=True允许执行模型仓库中的自定义代码
model = AutoModelForCausalLM.from_pretrained(load_path, trust_remote_code=True)
# 加载对应的分词器
tokenizer = AutoTokenizer.from_pretrained(load_path, trust_remote_code=True)

# 定义输入prompt
prompt = "给我讲一个芯片制造的流程"

# 构建符合模型输入格式的消息列表
messages = [{"role": "user", "content": prompt}]

# 使用推理模式,减少内存使用并提高推理速度
with torch.inference_mode():
    # 应用聊天模板,将消息转换为模型输入格式的文本
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    # 将文本转换为模型输入张量,并移至CPU (如果使用GPU,这里应改为.to('cuda'))
    model_inputs = tokenizer([text], return_tensors="pt").to('cpu')

    st = time.time()
    # 生成回答,max_new_tokens限制生成的最大token数
    generated_ids = model.generate(model_inputs.input_ids,
                                   max_new_tokens=512)
    end = time.time()

    # 初始化一个空列表,用于存储处理后的generated_ids
    processed_generated_ids = []

    # 使用zip函数同时遍历model_inputs.input_ids和generated_ids
    for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids):
        # 计算输入序列的长度
        input_length = len(input_ids)
        
        # 从output_ids中截取新生成的部分
        # 这是通过切片操作完成的,只保留input_length之后的部分
        new_tokens = output_ids[input_length:]
        
        # 将新生成的token添加到处理后的列表中
        processed_generated_ids.append(new_tokens)

    # 将处理后的列表赋值回generated_ids
    generated_ids = processed_generated_ids

    # 解码模型输出,转换为可读文本
    response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
    
    # 打印推理时间
    print(f'Inference time: {end-st:.2f} s')
    # 打印原始prompt
    print('-'*20, 'Prompt', '-'*20)
    print(text)
    # 打印模型生成的输出
    print('-'*20, 'Output', '-'*20)
    print(response)

在运行上列代码块后,将会自动在终端中新建一个python文件,我们只需要在终端运行这个python文件即可启动推理:

cd /mnt/workspace
conda activate ipex
python3 run.py

在上面的代码中,我们演示的是等到结果完全输出后再打印的模式,但有聪明的同学肯定好奇,有什么方法能够让我们及时看到输出的结果?这里我们介绍一种新的输出模式——流式输出,流式的意思顾名思义就是输出是不断流动的,也就是不停的向外输出的。通过流式输出,我们可以很容易及时看到模型输出的结果。在 transformers 中,我们将会使用 TextStreamer 组件来实现流式输出,记得这个 python 文件同样需要在终端执行:

cd /mnt/workspace
conda activate ipex
python3 run_stream.py

%%writefile /mnt/workspace/run_stream.py
# 设置OpenMP线程数为8
import os
os.environ["OMP_NUM_THREADS"] = "8"

import time
from transformers import AutoTokenizer
from transformers import TextStreamer

# 导入Intel扩展的Transformers模型
from ipex_llm.transformers import AutoModelForCausalLM
import torch

# 加载模型路径
load_path = "qwen2chat_int4"

# 加载4位量化的模型
model = AutoModelForCausalLM.load_low_bit(load_path, trust_remote_code=True)

# 加载对应的tokenizer
tokenizer = AutoTokenizer.from_pretrained(load_path, trust_remote_code=True)

# 创建文本流式输出器
streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)

# 设置提示词
prompt = "给我讲一个芯片制造的流程"

# 构建消息列表
messages = [{"role": "user", "content": prompt}]
    
# 使用推理模式
with torch.inference_mode():

    # 应用聊天模板,添加生成提示
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    
    # 对输入文本进行编码
    model_inputs = tokenizer([text], return_tensors="pt")
    
    print("start generate")
    st = time.time()  # 记录开始时间
    
    # 生成文本
    generated_ids = model.generate(
        model_inputs.input_ids,
        max_new_tokens=512,  # 最大生成512个新token
        streamer=streamer,   # 使用流式输出
    )
    
    end = time.time()  # 记录结束时间
    
    # 打印推理时间
    print(f'Inference time: {end-st} s')

恭喜你,你已经完全掌握了如何应用英特尔 ipex-llm 工具在 CPU 上实现 qwen2 大模型高性能推理。至此已掌握了完整输出 / 生成流式输出的调用方法;接下来我们讲更进一步,通过 Gradio 实现一个简单的前端来与我们在 cpu 上部署后的大模型进行对话,并实现流式打印返回结果。

ipex_llm+Gradio实现对话前端页面

Gradio 是一个开源的 Python 库,用于快速构建机器学习和数据科学演示应用。它使得开发者可以在几行代码中创建一个简单、可调整的用户界面,用于展示机器学习模型或数据科学工作流程。Gradio 支持多种输入输出组件,如文本、图片、视频、音频等,并且可以轻松地分享应用,包括在互联网上分享和在局域网内分享.

简单来说,利用 Graio 库,我们可以很容易实现一个具有对话功能的前端页面.

注意! 在运行之前,我们需要安装 gradio 库依赖环境, 你需要在终端执行:

cd /mnt/workspace
conda activate ipex
pip install gradio

需要强调的是,在运行之前我们还需要对启动命令进行修改才能正常使用 gradio 前端, 我们可以看到最后一句 gradio 的启动命令 demo.launch(root_path=‘/dsw-525085/proxy/7860/’) ,但每个人对应的不都是 dsw-525085,也许是 dsw-233333, 这取决于此时你的网页 url 链接上显示的地址是否是类似 https://dsw-gateway-cn-hangzhou.data.aliyun.com/dsw-525085/ 的字眼,根据你显示 url 的对应数字不同,你需要把下面的 gradio 代码 root_path 中的 dsw标识修改为正确对应的数字,才能在运行后看到正确的 gradio 页面.

在修改完 root_path 后,我们可以在终端中顺利运行 gradio 窗口:

cd /mnt/workspace
conda activate ipex
python3 run_gradio_stream.py

%%writefile /mnt/workspace/run_gradio_stream.py
import gradio as gr
import time
import os
from transformers import AutoTokenizer, TextIteratorStreamer
from ipex_llm.transformers import AutoModelForCausalLM
import torch
from threading import Thread, Event

# 设置环境变量
os.environ["OMP_NUM_THREADS"] = "8"  # 设置OpenMP线程数为8,用于控制并行计算

# 加载模型和tokenizer
load_path = "qwen2chat_int4"  # 模型路径
model = AutoModelForCausalLM.load_low_bit(load_path, trust_remote_code=True)  # 加载低位模型
tokenizer = AutoTokenizer.from_pretrained(load_path, trust_remote_code=True)  # 加载对应的tokenizer

# 将模型移动到GPU(如果可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 检查是否有GPU可用
model = model.to(device)  # 将模型移动到选定的设备上

# 创建 TextIteratorStreamer,用于流式生成文本
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)

# 创建一个停止事件,用于控制生成过程的中断
stop_event = Event()

# 定义用户输入处理函数
def user(user_message, history):
    return "", history + [[user_message, None]]  # 返回空字符串和更新后的历史记录

# 定义机器人回复生成函数
def bot(history):
    stop_event.clear()  # 重置停止事件
    prompt = history[-1][0]  # 获取最新的用户输入
    messages = [{"role": "user", "content": prompt}]  # 构建消息格式
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)  # 应用聊天模板
    model_inputs = tokenizer([text], return_tensors="pt").to(device)  # 对输入进行编码并移到指定设备
    
    print(f"\n用户输入: {prompt}")
    print("模型输出: ", end="", flush=True)
    start_time = time.time()  # 记录开始时间

    # 设置生成参数
    generation_kwargs = dict(
        model_inputs,
        streamer=streamer,
        max_new_tokens=512,  # 最大生成512个新token
        do_sample=True,  # 使用采样
        top_p=0.7,  # 使用top-p采样
        temperature=0.95,  # 控制生成的随机性
    )

    # 在新线程中运行模型生成
    thread = Thread(target=model.generate, kwargs=generation_kwargs)
    thread.start()

    generated_text = ""
    for new_text in streamer:  # 迭代生成的文本流
        if stop_event.is_set():  # 检查是否需要停止生成
            print("\n生成被用户停止")
            break
        generated_text += new_text
        print(new_text, end="", flush=True)
        history[-1][1] = generated_text  # 更新历史记录中的回复
        yield history  # 逐步返回更新的历史记录

    end_time = time.time()
    print(f"\n\n生成完成,用时: {end_time - start_time:.2f} 秒")

# 定义停止生成函数
def stop_generation():
    stop_event.set()  # 设置停止事件

# 使用Gradio创建Web界面
with gr.Blocks() as demo:
    gr.Markdown("# Qwen 聊天机器人")
    chatbot = gr.Chatbot()  # 聊天界面组件
    msg = gr.Textbox()  # 用户输入文本框
    clear = gr.Button("清除")  # 清除按钮
    stop = gr.Button("停止生成")  # 停止生成按钮

    # 设置用户输入提交后的处理流程
    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        bot, chatbot, chatbot
    )
    clear.click(lambda: None, None, chatbot, queue=False)  # 清除按钮功能
    stop.click(stop_generation, queue=False)  # 停止生成按钮功能

if __name__ == "__main__":
    print("启动 Gradio 界面...")
    demo.queue()  # 启用队列处理请求
    demo.launch(root_path='/dsw-555287/proxy/7860/')  # 兼容魔搭情况下的路由

streamio暂时跳过

使用 IPEX-LLM 运行 RAG 应用

环境准备:

pip install PyMuPDF llama-index-vector-stores-chroma llama-index-readers-file llama-index-embeddings-huggingface llama-index

在这篇教程中,我们将一起实现一个简单的 Retrieval-Augmented Generation(RAG)检索增强生成应用,检索增强生成顾名思义,就是利用检索来增强大模型的生成结果.

具体而言, RAG 主要是在这样的场景下被需要的:想象一下,当你有一个冷门的知识需要理解,但大模型没有基于这个知识训练过,或者说你想把这个知识全部输入大模型进行问答,但是大模型的上下文没有那么长;那么,我们需要一个好的方法让大模型可以基于我们新的知识进行对话,这就是 RAG的意义所在.

检索增强生成 (RAG) 通过向量数据库检索的方式来获取我们问题预期想要回答涉及的知识,然后结合这个知识让大模型基于知识生成最后的问答结果,满足了我们对提问的真实性需求.

在理解 RAG 之前,我们还需要理解什么是 Embedding.在机器学习和自然语言处理(NLP)中,Embedding 是一种将非结构化数据,如单词、句子或者整个文档,转化为实数向量的技术。这些实数向量可以被计算机更好地理解和处理.我们可以把一个词(token)表示成有限维度空间中的表示,如我们可以把苹果映射成 (5,5) ,把梨子映射成 (4,5),把芯片映射到 (1,2). 在这里,坐标的相近表示梨子和苹果的语义有很大的重复成分,而芯片与苹果的距离,自然比梨子与苹果的距离要远. 此时这些数字坐标映射就可以理解为简单的 Embedding, 我们可以通过 Embedding 模型,将一个词很方便映射到对应的实数多维表示空间,这个映射关系是提前训练得到的,越准确的 Embedding 模型可以让我们越好的区别不同语义特征的差异性,这也就对 RAG 的准确检索带来了更大的好处.

在搭建 RAG 系统时,我们往往可以通过使用 Embedding 模型来构建词向量,我们可以选择使用各个公司的在线 Embedding API,也可以使用本地嵌入模型将数据构建为词向量;由于我们通常是对文档操作,这里的**向量化是对文档块(chunk)**进行;我们可以将一个文档分成多个段落,每个段落分别进行 Embedding 操作,得到结果后存储到对应的数据库中保存,以便后续的检索即可.

而对于数据库,在 RAG 中,我们通常使用的也就是 Embedding 相关的数据库 - 向量数据库. 向量数据库是一种基于向量空间模型的数据库系统,它能够利用向量运算进行数据检索的高效处理,常见的向量数据库包括 FaissAnnoyMilvus 等等。这些向量数据库通常与 LLM 结合使用,以提高数据检索和处理的效率。

至此,在理解了 Embedding 之后,我们就理解了 RAG 中的一大核心要素.那么,我们将如何构建一个 RAG 系统?

具体而言, RAG 有很多的实现方式,在这里我们使用最简单的实现方法,他遵循最传统的规则,包括索引创建(Indexing)检索(Retrieval)生成(Generation),总的来说包括以下三个关键步骤:

  • 语料库被划分成一个个分散的块(chunk),然后使用embedding模型构建向量索引,并存储到向量数据库.
  • RAG 根据 query (当前提问)与索引块(Indexed Chunk)的向量相似度识别并对块进行检索。
  • 模型根据检索块(Retrieved Chunk)中获取的上下文信息生成答案。

RAG 也可以被简单的分成几大模块:

  • 向量化模块,用来将文档片段向量化。
  • 文档加载和切分的模块工具,用来加载文档并切分成文档片段。
  • 向量数据库模块,用于将向量化后的文档存储到数据库中.
  • 检索模块,根据 Query (问题)检索相关的文档片段。
  • 大模型模块,结合 Query 及检索出来的文档回答用户的问题。

我们可以用一张图简单理解 RAG 系统做了哪些事情:
community_30492e9.png
由图可知,我们通过基于提问检索出的知识块,和提问一起拼接输入到大模型问答后,让大模型的回答更加接近我们的提问预期,可靠性大幅度增加.但这也并非 RAG 技术的终点,我们可以通过更多额外方式增强 RAG 的效果,譬如:
community_3859ff4.png
以最简单的"重排技术"为例,我们可以通过检索后重新排布检索的结果,从而提高提高打算使用的检索块与提问的关联度,最终提高问答的生成质量。在 RAG 架构下,引入重排步骤可以有效改进召回效果,提升LLM(大语言模型)生成答案的质量。我们可以通过一张图简单理解这一过程:
image.png
总之,我们可以利用 RAG 技术提高大模型问答最后的生成水平,在强事实要求与上下文不足的情况下仅仅依靠 RAG 就能实现满足预期的效果. 接下来, 让我们从初级 RAG 开始, 一步步探索检索增强生成的应用之路.

3.11 课程总结和未来方向

  • 回顾整个课程的关键点
  • 讨论未来的研究方向和技术趋势

4. 📚 实践和项目工作

  • 综合项目:结合前面学到的所有知识,设计并实施一个复杂的深度学习项目。
  • 技术报告:撰写一份详细的报告,概述项目的背景、方法、结果和结论。
  • 代码审查和同行评价:分享并讨论彼此的代码,相互学习和改进。

5. 📚 持续学习资源

  • 提供在线课程、书籍、论文和社区资源列表,鼓励学生继续学习和探索深度学习领域。

通过这些后续课程,学生将能够获得更全面的深度学习知识,同时培养解决实际问题的能力。

时间有限,大家有想看的部分,可以私信或者评论区联系,我及时补充。或者大家有想帮忙补充的也可以联系。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值