当前位置: 首页 > article >正文

LLM学习笔记(11)pipeline() 函数的幕后工作

Hugging Face 的 pipeline 背后做了什么?

Hugging Face 的 pipeline 是一个高层封装工具,简化了许多繁琐的操作,使得开发者可以快速调用 NLP 模型完成复杂任务。以示例中的 情感分析任务 (sentiment-analysis) 为例,pipeline 背后执行了以下三个主要步骤:

1. 预处理(Preprocessing)

  • 目标: 将原始文本转化为模型可以理解的输入格式。
  • 操作:
    1. 使用 Tokenizer(分词器)将输入的自然语言文本分解为 token(子词单元)。
    2. 将 token 转换为对应的数字 ID,构建模型所需的输入格式。
    3. 添加特殊标记(如 [CLS][SEP])和填充(padding),使输入满足模型的固定格式。
  • 输入:
    • 原始文本:"I've been waiting for a HuggingFace course my whole life."
  • 输出:
    • 输入 ID(input IDs):[101, 2023, 2607, 2003, 6429, 999, 102](这是 token 对应的数字化表示)。

2. 模型推理(Model Forward Pass)

  • 目标: 将预处理后的输入传递给模型进行推理,生成输出 logits。
  • 操作:
    1. 输入数字化的 token ID 和相关信息(如 attention masks)到模型。
    2. 模型计算输出 logits(未归一化的分数),表示每个类别的置信度。
  • 输入:
    • 数字化表示的 input IDs
  • 输出:
    • Logits:[-4.3630, 4.6859](表示类别的未归一化分数,越大置信度越高)。

3. 后处理(Postprocessing)

  • 目标: 将模型输出的 logits 转换为人类可读的格式。
  • 操作:
    1. 使用 Softmax 函数将 logits 转换为概率值。
    2. 找到概率值最高的类别并标注为最终的预测结果。
  • 输入:
    • Logits:[-4.3630, 4.6859]
  • 输出:
    • 概率分布:[NEGATIVE: 0.11%, POSITIVE: 99.89%]
    • 最终结果:{'label': 'POSITIVE', 'score': 0.9598048329353333}

不涉及JSON文件

在 Hugging Face 的 pipeline 背后的 三个步骤之间(预处理、模型推理、后处理),并不是通过 JSON 文件传递数据。这些步骤是通过 Python 对象和内存中的数据直接传递的,使用的是 Python 和 PyTorch/TensorFlow 等深度学习框架的高效内存操作(如张量和字典),而不是文件操作。

数据传递的具体机制

1. 预处理(Preprocessing)

  • 输入数据: 由用户提供的自然语言文本。
  • 输出数据: 转换后的模型输入格式(token IDs 和相关信息)。
  • 传递方式: 直接将数据存储在内存中的 PyTorch TensorNumPy Array 对象中。

2. 模型推理(Model Forward Pass)

  • 输入数据: 预处理后的 token IDs 和 attention masks 等。
  • 输出数据: 模型生成的 logits(未归一化分数)。
  • 传递方式: 数据通过 Python 对象直接传递,通常是 torch.Tensortf.Tensor 格式。

3. 后处理(Postprocessing)

  • 输入数据: 模型推理生成的 logits。
  • 输出数据: 转换为概率分布和最终结果(如 {'label': 'POSITIVE', 'score': 0.95})。
  • 传递方式: 数据在内存中以 Python 的标准数据类型(如列表或字典)返回。

为什么不是 JSON 文件?

以下是 Hugging Face 在 pipeline 内部没有使用 JSON 文件作为数据传递方式的原因:

1. 数据传递的高效性
  • JSON 文件涉及文件的读写操作,会显著增加延迟。
  • 直接在内存中操作数据(如 torch.Tensor)效率更高,适合深度学习模型的推理过程。
2. 数据格式的复杂性
  • 模型的输入(如 token IDs 和 attention masks)和输出(如 logits)通常是多维张量(tensor)。
  • JSON 不适合存储复杂的高维数据结构,转换为 JSON 会引入额外的复杂性。
3. 实时性需求
  • pipeline 的设计目标是实时处理数据,而文件读写会对实时性产生不必要的影响。

虽然 pipeline 的内部步骤没有使用 JSON 文件,但在以下场景下,JSON 可能会被用到:

1. 用户输入/输出

  • 如果用户从外部文件(如 JSON 文件)加载输入,或将输出保存为 JSON 文件,JSON 会用于持久化存储。

2. API 通信

  • 如果 pipeline 被部署为 API 服务(如通过 REST API 提供服务),通常会使用 JSON 格式传递请求和响应。

3. 分布式计算

  • 在一些需要跨进程通信的场景中,可能会使用 JSON 或其他序列化格式传递数据。

使用分词器进行预处理

神经网络模型无法直接处理文本,因此首先需要通过预处理环节将文本转换为模型可以理解的数字。具体地,我们会使用每个模型对应的分词器 (tokenizer) 来进行:

1. 分词(Tokenization)

  • 将文本分解为较小的单元(单词或子词,称为 tokens)。

2. 将 tokens 转换为数字 ID

  • 分词器通过查找模型词汇表,将每个 token 映射到对应的数字编码(token IDs)。

3. 特殊标记

  • 添加模型需要的特殊标记,如句子开头的 [CLS] 和结尾的 [SEP]

4. 填充(Padding)

  • 对于批量输入,分词器会将短句填充到相同长度,确保张量形状一致。

我们对输入文本的预处理需要与模型自身预训练时的操作完全一致,只有这样模型才可以正常地工作。

注意,每个模型都有特定的预处理操作,如果对要使用的模型不熟悉,可以通过 Model Hub 查询。这里我们使用 AutoTokenizer 类和它的 from_pretrained() 函数,它可以自动根据模型 checkpoint 名称来获取对应的分词器。

代码

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!"
]

inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)

加载分词器

tokenizer = AutoTokenizer.from_pretrained(checkpoint)

  • checkpoint 指定模型名称 distilbert-base-uncased-finetuned-sst-2-english
  • AutoTokenizer.from_pretrained() 根据模型自动加载对应的分词器。

输入文本

raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!"
]

  • 这个由中括号框住的内容是 Python 中的列表(List) 格式,而其中的每个元素是 字符串(String)

列表(List):

  • Python 中的一种数据结构,可以存储多个有序的元素。
  • 列表中的元素可以是任意类型(如字符串、数字、布尔值、甚至嵌套的列表)。
  • 使用中括号 [] 表示。
  • 列表元素之间用逗号 , 分隔。

分词和编码

inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")

  • padding=True 对短句进行填充,使所有句子的长度一致。
  • truncation=True 截断超长文本,确保输入长度不超过模型最大限制。
  • return_tensors="pt" 将输出结果转换为 PyTorch 的张量格式。

输出结果解析

字段解释

  • input_ids

    • 表示每个句子经过分词后转化的 token IDs。
    • 每一行对应一个输入句子,数字表示模型词汇表中 token 的索引。
  • attention_mask

    • 用于标识哪些位置是有效 token,哪些是填充 token:
      • 1 表示有效 token。
      • 0 表示填充 token(padding)。

将预处理好的输入送入模型

基础模型:AutoModel

加载基础模型(AutoModel)

model = AutoModel.from_pretrained(checkpoint)

  • 这是一个基础的 Transformer 模型,仅计算中间层特征(hidden states)。
  • 并没有特定的任务头(head),因此不能直接用于分类等具体任务。

输出内容

  • 输出的 last_hidden_state 是一个三维张量,维度为 (batch_size, sequence_length, hidden_size)
  • torch.Size([2, 16, 768])
    • Batch size: 2(因为输入了两句文本)。
    • Sequence length: 16(分词器将短句填充到相同长度)。
    • Hidden size: 768(DistilBERT 的特征维度)。

特定任务模型:AutoModelForSequenceClassification

加载任务模型

  • AutoModelForSequenceClassification 是为文本分类任务设计的模型。
  • 在基础模型的顶部添加了分类头(classification head),用于输出分类 logits。

输出内容

  • logits 分类器的输出结果,表示每个类别的未归一化分数。
  • torch.Size([2, 2])
    • Batch size 为 2(两条输入)。
    • 每条输入有两个分类分数(如 POSITIVENEGATIVE)。

分类任务的完整流程

  • 输入经过基础模型(计算 hidden states)。
  • hidden states 被传入分类头,得到每个类别的 logits。
第一步:输入经过基础模型
  • 模型首先接收已经分词和编码的输入数据,包括 input_ids(token 的数字表示)和 attention_mask(标记有效 token 的掩码)。
  • 基础模型(如 AutoModelBERT)会将这些输入传递给其内部的 Transformer 网络,逐层处理,生成 hidden states(隐藏层特征表示)。

hidden states 是什么?

  • Hidden states:
    • 表示每个 token 的上下文特征,是模型内部计算出的高维向量(例如,768 维或更高)。
    • 它们是文本经过 Transformer 网络后的中间语义表示。

hidden states 的作用:

  • 它们是基础模型的输出,是后续任务(如分类、问答等)的输入。
  • 类似于“语义向量”,代表了模型对文本的深层次理解。
第二步:hidden states 被传入分类头(Classification Head)
  • 分类头(head):

    • 一个额外的神经网络层,通常是全连接层(fully connected layer),用于特定任务(如分类)。
    • 它会接收基础模型的 hidden states,并将其映射到类别概率空间。
  • 工作原理:

    • 分类头会根据 hidden states 计算出每个类别的分数,称为 logits
    • 例如,对于二分类任务(如情感分析),会输出两个 logits(分别对应 POSITIVENEGATIVE)。
第三步:从 logits 得到分类结果
  • Logits:

    • logits 是未归一化的分数,表示每个类别的可能性大小。
    • 例如,[0.5, -1.2] 可能表示 POSITIVENEGATIVE 的初始分数。
  • Softmax:

    • 一般会对 logits 应用 Softmax 函数,将分数转换为概率分布。
    • 例如,[0.5, -1.2] 经过 Softmax 后,可能得到 [0.88, 0.12],表示 POSITIVE 的概率为 88%。
  • 最终分类:

    • 选择概率最大的类别作为最终分类结果。
    • 例如,POSITIVE 是概率最大的类别,因此输出 POSITIVE


http://www.kler.cn/a/416976.html

相关文章:

  • 【Go底层】time包Ticker定时器原理
  • 鸿蒙征文|鸿蒙技术分享:使用到的开发框架和技术概览
  • uniapp的video组件截图(抓拍)功能,解决截后为黑图bug
  • 【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-38
  • 哈希表算法题
  • 基础原型链污染
  • 【娱乐项目】基于批处理脚本与JavaScript渲染视频列表的Web页面
  • MySQL 中 COUNT(1)、COUNT(*) 和 COUNT(列名) 的区别
  • Spring Boot 项目——分层架构
  • C++设计模式:装饰器模式 (Decorator) (咖啡订单系统)
  • c++哈希(开散列原理及实现)
  • BUUCTF—Reverse—Java逆向解密(10)
  • 警钟长鸣,防微杜渐,遨游防爆手机如何护航安全生产?
  • Flink 离线计算
  • 【kafka02】消息队列与微服务之Kafka部署
  • 如何bug是前端还是后端
  • (即插即用模块-Attention部分) 二十、(2021) GAA 门控轴向注意力
  • 【Spring框架 二】
  • DimensionX 学习部署笔记
  • 大小写转换
  • Ubuntu 常用解压与压缩命令
  • 如何将WSL的虚拟机安装到任意目录中
  • Nginx和Apache有什么异同?
  • 关于NXP开源的MCU_boot的项目心得
  • Spring Boot 实战:分别基于 MyBatis 与 JdbcTemplate 的数据库操作方法实现与差异分析
  • 【QNX+Android虚拟化方案】125 - 如何创建android-spare镜像