基础知识

NumPy 数组 (numpy.ndarray) #

是什么?

  • NumPy (Numerical Python) 是 Python 语言的一个核心库,专门用于进行科学计算,特别是处理大型多维数组和矩阵。
  • NumPy 最核心的对象是 ndarray(N-dimensional array),它是一个同质(所有元素类型相同,如全是整数或全是浮点数)的多维数组。你可以把它想象成一个灵活的、强大的网格结构,可以是一维(向量)、二维(矩阵)、三维(立方体)甚至更高维度。

为什么重要?

  • 性能: NumPy 底层是用 C 语言实现的,其数组操作(如数学运算、索引、切片)比 Python 内置的列表(list)快得多,尤其是处理大量数据时。这是因为它利用了向量化操作,避免了 Python 级别的循环。
  • 内存效率: NumPy 数组在内存中是连续存储的(通常情况下),这使得访问和操作更加高效。
  • 功能丰富: 提供了大量的数学函数(线性代gebra、傅里叶变换、随机数生成等)来操作这些数组。
  • 生态基础: NumPy 是许多其他 Python 科学计算库(如 SciPy、Pandas、Scikit-learn、Matplotlib)的基础。很多库的输入输出都接受或返回 NumPy 数组。

关键特性:

  • 维度 (Dimensions/Axes): 数组的“方向”数量,称为 ndim。
  • 形状 (Shape): 一个描述数组在每个维度上大小的元组,称为 shape。例如,一个 3x4 的矩阵,shape 是 (3, 4)。
  • 数据类型 (Data Type/dtype): 数组中元素的数据类型,如 int32, float64, uint8 (常用于图像)。

常见用途:

  • 任何需要高效数值计算的场景。
  • 数据分析中的数据存储和预处理。
  • 图像表示: 图像可以被看作是二维(灰度图)或三维(彩色图)的像素网格,NumPy 数组是表示它们的自然方式。例如,一个 640x480 的彩色图像可以用一个 shape 为 (480, 640, 3) 的 NumPy 数组表示(高度、宽度、颜色通道)。

示例:

import numpy as np

# 创建一个一维数组 (向量)
vec = np.array([1, 2, 3])
print(f"Vector: {vec}, Shape: {vec.shape}, Dtype: {vec.dtype}")

# 创建一个二维数组 (矩阵)
mat = np.array([[1.0, 2.0], [3.0, 4.0]])
print(f"Matrix:\n{mat}, Shape: {mat.shape}, Dtype: {mat.dtype}")

# 数组运算 (向量化)
result = mat * 2 + 1
print(f"Result of mat * 2 + 1:\n{result}")

PyTorch 张量 (torch.Tensor) #

是什么?

  • PyTorch 是一个开源的机器学习框架,广泛应用于计算机视觉和自然语言处理等领域。
  • PyTorch 的核心数据结构是 Tensor(张量)。张量在概念上与 NumPy 的 ndarray 非常相似:它也是一个多维的数值数组。

为什么重要(特别是在深度学习中)?

  • GPU 加速: PyTorch 张量可以在 GPU (图形处理单元) 上进行计算。GPU 拥有大量的并行处理核心,对于深度学习中涉及的大规模矩阵运算(如神经网络的训练和推理)能提供极大的加速。NumPy 数组主要在 CPU 上运行。
  • 自动微分 (Automatic Differentiation / Autograd): 这是 PyTorch 最强大的功能之一。当你用张量进行一系列运算构建计算图时,PyTorch 可以自动计算这些运算相对于其输入的梯度(导数)。这对于训练神经网络(通过反向传播算法更新模型权重)是必不可少的。NumPy 本身不具备这个功能。

关键特性:

  • 与 NumPy 数组类似的 shape, dtype, ndim 属性。
  • device 属性:指明张量存储和计算的位置(如 cpu 或 cuda:0 表示第一个 GPU)。
  • requires_grad 属性:如果设置为 True,PyTorch 的 autograd 系统会追踪对该张量的所有操作,以便后续计算梯度。

与 NumPy 的关系:

  • PyTorch 张量和 NumPy 数组可以非常方便地互相转换(前提是它们都在 CPU 上且数据类型兼容): #

    • NumPy -> PyTorch: torch.from_numpy(numpy_array)
    • PyTorch -> NumPy: pytorch_tensor.numpy() (注意:需要张量在 CPU 上)
  • 内存共享: 当使用 torch.from_numpy() 或 .numpy() 进行转换时(在 CPU 上),它们通常共享底层内存。这意味着修改一个可能会影响另一个,这既是优点(高效)也需要注意(避免意外修改)。

常见用途:

  • 构建和训练神经网络。
  • 存储模型的参数(权重和偏置)。
  • 处理输入数据和标签。
  • 执行任何需要 GPU 加速或自动求导的数值计算。

示例:

import torch
import numpy as np

# 从 NumPy 数组创建张量
numpy_arr = np.array([[1, 2], [3, 4]], dtype=np.float32)
tensor_cpu = torch.from_numpy(numpy_arr)
print(f"Tensor from NumPy (CPU): {tensor_cpu}, Device: {tensor_cpu.device}")

# 创建一个需要梯度的张量
tensor_grad = torch.tensor([[1.0, 2.0], [3.0, 4.0]], requires_grad=True)
print(f"Tensor with grad: {tensor_grad}, Requires Grad: {tensor_grad.requires_grad}")

# 将张量移动到 GPU (如果可用)
if torch.cuda.is_available():
    tensor_gpu = tensor_cpu.to('cuda')
    print(f"Tensor moved to GPU: {tensor_gpu}, Device: {tensor_gpu.device}")
    # 在 GPU 上进行运算
    result_gpu = tensor_gpu * 2 + 1
    print(f"Result on GPU: {result_gpu}")
    # 梯度计算示例
    y = tensor_grad * 2
    z = y.mean()
    z.backward() # 自动计算梯度
    print(f"Gradient of tensor_grad:\n{tensor_grad.grad}")

# 将 CPU 张量转回 NumPy
numpy_back = tensor_cpu.numpy()
print(f"Tensor back to NumPy: {numpy_back}")

图像颜色格式:BGR vs RGB #

背景:图像的数据表示

  • 数字图像由像素(Pixel)组成。
  • 彩色图像的每个像素通常由三个颜色通道的值混合而成,代表不同的原色强度。
  • 最常见的颜色模型是 RGB (Red, Green, Blue)
  • 图像数据通常存储在 NumPy 数组或 PyTorch 张量中,形状通常是 (Height, Width, Channels) 或 (Channels, Height, Width)。

RGB (Red, Green, Blue)

  • 这是最常用和直观的颜色顺序。
  • 在 (H, W, 3) 的数组/张量中,沿着最后一个维度(通道维度): #

    • 索引 0 对应 红色 (Red)
    • 索引 1 对应 绿色 (Green)
    • 索引 2 对应 蓝色 (Blue)
  • 常见使用者:
    • 大多数图像文件格式(如 JPEG, PNG)在解码后,很多库会默认提供 RGB。
    • 网页标准 (HTML, CSS)。
    • Matplotlib (Python 绘图库) 显示图像时默认按 RGB 处理。
    • Pillow (PIL - Python Imaging Library) 加载图像时通常是 RGB。
    • 许多预训练的深度学习模型(尤其是在 PyTorch Hub 或 torchvision 中基于 ImageNet 训练的模型)期望输入是 RGB 格式。

BGR (Blue, Green, Red)

  • 这是一种与 RGB 相反的通道顺序。
  • 在 (H, W, 3) 的数组/张量中,沿着最后一个维度: #

    • 索引 0 对应 蓝色 (Blue)
    • 索引 1 对应 绿色 (Green)
    • 索引 2 对应 红色 (Red)
  • 主要使用者:
    • OpenCV (cv2):这是最重要的一点!OpenCV 是一个极其流行的计算机视觉库,它在加载(cv2.imread()) 和处理彩色图像时,默认使用 BGR 顺序。这是历史原因造成的,但一直保持至今以确保向后兼容。

为什么这个区别很重要?

  • 如果你用 OpenCV 加载了一张图片(得到 BGR 格式的 NumPy 数组),然后直接用 Matplotlib 显示它(期望 RGB),颜色会看起来很奇怪(红色和蓝色互换了)。
  • 同样,如果你用 OpenCV 加载图像(BGR),然后不经转换就把它送入一个期望 RGB 输入的 PyTorch 模型,模型的性能会严重下降,因为它看到的是完全错误的颜色信息。

如何转换?

  • OpenCV: 提供了方便的转换函数:

    import cv2
    
    # 假设 img_bgr 是用 cv2.imread() 加载的 BGR 图像 (NumPy array)
    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
    
    # 从 RGB 转回 BGR
    img_bgr_again = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
    

    content_copydownload

    Use code with caution.Python

  • NumPy/PyTorch Slicing: 可以通过数组/张量切片直接颠倒通道顺序(适用于 (H, W, C) 格式):

    # NumPy
    img_rgb_np = img_bgr[:, :, ::-1] # 翻转最后一个维度
    
    # PyTorch (假设 tensor_bgr 是 (H, W, 3) 的张量)
    tensor_rgb = tensor_bgr[:, :, [2, 1, 0]] # 选择通道索引 2, 1, 0
    # 或者
    tensor_rgb_alt = tensor_bgr.flip(dims=[-1]) # 翻转最后一个维度 (如果通道在最后)
    

    content_copydownload

    Use code with caution.Python

    注意:虽然切片可行,但使用 cv2.cvtColor 通常更清晰易读,不易出错。

总结与实践要点 #

  1. NumPy ndarray 是 Python 中通用的、高效的数值数组,用于科学计算和数据分析,是很多库的基础。主要在 CPU 上运行。
  2. PyTorch Tensor 是深度学习框架 PyTorch 的核心,类似 NumPy 数组,但增加了 GPU 加速自动微分的关键功能,是训练神经网络的基石。
  3. NumPy 数组和 PyTorch 张量(在 CPU 上)可以方便地相互转换,并可能共享内存。
  4. RGBBGR 是两种常见的彩色图像通道顺序。RGB (红绿蓝) 更通用,而 BGR (蓝绿红) 是 OpenCV 的默认格式
  5. 在处理图像数据,特别是结合 OpenCV 和 PyTorch/Matplotlib/其他库时,必须清楚当前图像数据的颜色通道顺序,并在需要时进行显式转换(如使用 cv2.cvtColor),以避免颜色错误或模型性能下降。

像素灰度值 #

像素灰度值(Pixel Grayscale Value)是指在灰度图像(Grayscale Image)中,用来表示单个像素亮度的数值。它是一个标量值(单个数字),代表了从最暗(黑色)到最亮(白色)的光强度。

详细解释:

  1. 像素 (Pixel): 图像是由一个个微小的点组成的,这些点就是像素(是 Picture Element 的缩写)。你可以把数字图像想象成一个精细的网格,网格中的每一个小方格就是一个像素。
  2. 灰度图像 (Grayscale Image): 与彩色图像不同,灰度图像不包含颜色信息,只包含亮度(或称为强度、明度)信息。它看起来就是我们常说的“黑白照片”,但技术上更准确地说是包含了从纯黑到纯白之间的各种灰色阴影。
  3. 灰度值 (Grayscale Value): 对于灰度图像中的每一个像素,都有一个对应的数值来表示它的亮度。这个数值就是该像素的灰度值。
    • 范围: 这个值通常用一个特定的范围来表示。最常见的是使用 8 位整数来存储,范围是 0 到 255
      • 0 代表 纯黑色 (最暗,没有光强度)。
      • 255 代表 纯白色 (最亮,最大光强度)。
      • 介于 0 和 255 之间的数值代表不同深浅的灰色。例如,128 通常代表中灰色。
    • 其他范围: 虽然 0-255 是最常见的,但在某些应用或数据类型中,灰度值也可能被规范化到 0.0 到 1.0 之间的浮点数范围(0.0 代表黑,1.0 代表白),或者使用更高位深(如 16 位,范围 0 到 65535)来表示更精细的亮度级别。
  4. 与彩色图像的关系: 彩色图像(如 RGB 图像)的每个像素通常由三个值(红色、绿色、蓝色分量)组成来表示颜色。要将彩色图像转换为灰度图像,需要将这三个颜色分量合并成一个单一的灰度值。这通常不是简单的平均,而是根据人眼对不同颜色的敏感度进行加权平均。一个常用的公式是: 灰度值 = 0.299 * R + 0.587 * G + 0.114 * B (注意:这个公式有不同变种,但原理类似,绿色通常权重最高,蓝色最低)。
  5. 数据表示: 在 NumPy 数组或 PyTorch 张量中:
    • 一个灰度图像通常表示为一个 二维数组 (Height, Width)
    • 数组中的每个元素就是对应像素的灰度值。
    • 数据类型 (dtype) 通常是 uint8 (无符号 8 位整数,对应 0-255 范围) 或 float32 / float64 (对应 0.0-1.0 范围或其他规范化范围)。
    • 这与彩色图像通常表示为三维数组 (Height, Width, Channels) 形成对比。

总结:

像素灰度值是一个单一数值,用于量化灰度图像中每个像素的亮度。它表示从黑到白的光强度级别,最常见的表示范围是 0 (黑) 到 255 (白)。它是处理和分析图像亮度、对比度、形状和纹理等信息的基础。

量化格式 #

Qwen1.5 原始发布是 Hugging Face (PyTorch/Safetensors) 格式,但在实际推理部署时,常被转换为 GGUF(用于 CPU/跨平台 GPU)、AWQ/GPTQ(用于 GPU 量化加速)等优化格式。

GGUF #

GGUF (Georgi Gerganov Universal Format) 是一种专门用于存储大型语言模型 (LLM) 的文件格式。而 “GGUF 版本” 指的是这种文件格式本身的规范版本

以下是更详细的解释:

  1. GGUF 的目的:
    • GGUF 是由 llama.cpp 项目的创建者 Georgi Gerganov 设计的。llama.cpp 是一个非常流行的项目,旨在使用 C/C++ 在普通 CPU 和多种 GPU(包括 Apple Silicon、NVIDIA、AMD)上高效地运行 LLMs。
    • GGUF 格式的设计目标是:
      • 易于加载和使用: 简化模型加载过程。
      • 包含元数据: 将模型的所有必要信息(如架构、参数、分词器配置、量化类型等)都打包在一个文件中。
      • 可扩展性: 容易添加新功能和信息,而不会破坏向后兼容性(或者有明确的版本控制)。
      • 稳定性: 克服其前身 GGML 格式的一些版本兼容性问题。
  2. 为什么会有“版本”?
    • 就像软件会更新一样,文件格式也会随着时间的推移而发展和改进。
    • 开发者可能会发现需要添加新的元数据字段、支持新的模型特性(如新的量化方法)、或者优化文件结构。
    • 每次对 GGUF 格式规范进行非向后兼容重要的更改时,通常会增加版本号。
  3. “GGUF 版本” 的含义:
    • 它指的是创建该 .gguf 文件时所遵循的 GGUF 规范版本。例如,你可能会看到 GGUF v1, GGUF v2, GGUF v3 等。
    • 这个版本号非常重要,因为它决定了哪个版本的 llama.cpp 或其他兼容 GGUF 的软件能够正确加载和运行这个模型文件
  4. 实际影响:
    • 兼容性: 如果你下载了一个使用较新 GGUF 版本(例如 v3)创建的模型文件,但你使用的 llama.cpp 或相关工具(如 LM Studio, Ollama, KoboldCpp 等)版本较旧,只支持到 GGUF v2,那么该软件可能无法加载或运行这个模型,或者会报错。
    • 反之亦然: 通常较新版本的软件会保持对旧 GGUF 版本的兼容性,但有时也可能放弃对非常旧版本的支持。
    • 如何选择: 一般建议使用最新稳定版的 llama.cpp 或相关推理软件,这样通常能支持最新的 GGUF 版本和模型。下载模型时,模型发布者(如在 Hugging Face 上)有时会注明该模型文件使用的 GGUF 版本。

推理库 #

vLLM #

vLLM 是一个用于快速、高效地进行大型语言模型 (LLM) 推理和服务的开源库。

vLLM 主要是为 Linux 环境设计和优化的。其许多底层依赖项和优化(特别是在 CUDA 内核和内存管理方面)在 Linux 上有更好的支持和表现。

你可以把它理解为一个专门为 提升 LLM 运行速度和吞吐量 而设计的引擎或框架。它由加州大学伯克利分校的研究人员开发,并迅速在 AI 社区流行起来。

vLLM 的核心目标和解决的问题:

大型语言模型在推理(即生成文本)时非常消耗计算资源和内存,尤其是在处理多个并发请求时。传统的推理方法常常面临以下挑战:

  1. 内存效率低下: LLM 推理过程中需要存储大量的键值缓存 (KV Cache)。传统的实现方式可能会浪费大量显存,因为它们需要为每个请求预留足够容纳最大可能序列长度的连续内存块。
  2. 吞吐量瓶颈: 由于内存限制和低效的批处理(Batching)策略,同时处理多个用户请求的效率不高,导致 GPU 利用率低,响应慢。

vLLM 的关键技术和特点:

  1. PagedAttention 算法: 这是 vLLM 最核心的创新。它借鉴了操作系统中虚拟内存和分页(Paging)的思想来管理注意力机制中的 KV Cache。
    • 它将 KV Cache 分割成固定大小的“块”(Blocks)。
    • 这些块可以像操作系统的内存页一样,非连续地存储在 GPU 显存中。
    • 好处:
      • 极大减少内存浪费: 按需分配块,避免为最大长度预留空间。
      • 几乎消除内存碎片: 更灵活地管理内存。
      • 实现高效的内存共享: 例如,在并行采样或束搜索(Beam Search)中,不同序列可以共享它们共同前缀的 KV Cache 块,显著节省内存。
  2. 连续批处理 (Continuous Batching): 不同于传统的静态批处理(需要等待一个批次的所有请求都完成后才能处理下一个),vLLM 可以在批次仍在 GPU 上运行时,动态地将新的请求添加到批次中,只要有计算资源和内存空间。这大大提高了 GPU 的利用率和系统的吞吐量。
  3. 优化的 CUDA Kernels: vLLM 包含高度优化的底层计算核心(CUDA Kernels),以确保 PagedAttention 和其他操作在 GPU 上尽可能快地执行。
  4. 高吞吐量和低延迟: PagedAttention 和连续批处理相结合,使得 vLLM 能够比许多其他推理库(如 Hugging Face Transformers 的标准 generate 方法)实现显著更高的吞吐量(单位时间内处理更多请求/Token)和更低的延迟。
  5. 易于使用的 API: 提供 Python API,可以方便地集成到现有项目中。
  6. 兼容 OpenAI 的 API 服务器: vLLM 提供了一个可以直接运行的 API 服务器,其接口与 OpenAI API 兼容。这意味着你可以轻松地将依赖 OpenAI API 的应用程序切换到使用本地部署的、由 vLLM 驱动的模型,只需更改 API 端点和密钥即可。
  7. 分布式推理支持: 支持张量并行(Tensor Parallelism),可以将非常大的模型分布在多个 GPU 或多台机器上运行。
  8. 流式输出 (Streaming): 支持将生成的 Token 实时流式传输给客户端,这对于构建交互式聊天机器人等应用至关重要。