听诊器的发明,让医生第一次能听见心肺的内部声音。 Transformer 的诞生,则让机器第一次真正理解了语言的上下文。对医疗人来说,这不只是技术的突破,它意味着我们可以重新思考如何让 AI 在临床、科研和患者服务中发挥作用。
作为医疗从业者,我们每天都要处理庞杂的信息:病历、科研文献……而 Transformer 正是当下所有大语言模型(LLM)的核心。理解它,等于理解了 ChatGPT、医学辅助诊断系统、自动科研写作工具的根基。
所以,接下来我希望带你一步步走入 Transformer 学习课程,用通俗的方式来理解复杂的技术。
需要课程代码以及问题咨询通过留言 📮在路上的蟹老板(pqjrkwem@gmail.com)
课程简介
本课程将教你如何使用 Transformaer 生态系统的库进行自然语言处理(NLP)。
涵盖的库
- Transformers - 核心模型库
- Datasets - 数据集处理
- Tokenizers - 文本分词
- Accelerate - 训练加速
- Hugging Face Hub - 模型共享平台
课程结构
第 1-4 章:Transformers 基础
介绍 Transformers 库的主要概念。完成后你将:
- 了解 Transformer 模型的工作原理
- 使用 Transformer 模型
- 微调一个预训练模型
- 分享模型和标记器
第 5-8 章:拥有一个自己的模型
- Datasets库
- Tokenizers库
- 主要的 NLP 任务
- 构建和分享你的模型
学习要求
具备:
良好的 Python 知识, 要是熟悉 深度学习和机器学习就更好了, 对于tensorflow 或者 pytorch 熟悉更更更加分
Pipeline 的内部原理
完整示例
让我们从一个完整的示例开始,看看在第一章中执行以下代码时在幕后发生了什么:
from transformers import pipeline
classifier = pipeline("sentiment-analysis")
classifier([
"I've been waiting for a HuggingFace course my whole life.",
"I hate this so much!",
])
获得输出:
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
{'label': 'NEGATIVE', 'score': 0.9994558095932007}]
这个 pipeline 集成了三个步骤:预处理、模型计算和后处理。
┌─────────────────────────────────────────────────────────────┐
│ 完整的 NLP Pipeline │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原始文本 → Tokenizer → Token IDs → Model → 输出 │
│ "Hi!" ↓ [101,123] ↓ Logits │
│ 分词+编码 Transformer │
│ │
└─────────────────────────────────────────────────────────────┘
使用 Tokenizer 进行预处理
与其他神经网络一样,Transformer 模型无法直接处理原始文本,因此管道的第一步是将文本输入转换为模型能够理解的数字。Tokenizer 负责:
- 将输入拆分为单词、子单词或符号(如标点符号),称为 token
- 将每个 token 映射到一个数字,称为 input ID
- 添加模型需要的其他输入,例如:
- 特殊标记(如
[CLS]和[SEP]) - 位置编码
- 段落标记
- 特殊标记(如
加载 Tokenizer
我们使用 AutoTokenizer 类和 from_pretrained() 方法:
from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
有了 tokenizer 后,可以直接传递句子:
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)
输出结果(PyTorch 张量):
{
'input_ids': tensor([
[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
[ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
]),
'attention_mask': tensor([
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
])
}
探索模型
加载模型
我们可以像使用 tokenizer 一样下载预训练模型:
from transformers import AutoModel
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
这个模型只包含基本的 Transformer 模块,输出 隐状态(hidden states),也称为特征。
高维向量
Transformers 模块的向量输出通常有三个维度:
- Batch size(批次大小):一次处理的序列数
- Sequence length(序列长度):序列的长度
- Hidden size(隐藏层大小):每个模型输入的向量维度
如果将预处理后的值输入到模型:
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
输出:
torch.Size([2, 16, 768])
模型头(Model Head)
模型头将隐状态转换为特定任务的输出,通常由一个或几个线性层组成。
┌──────────────────────────────────────────────────────┐
│ Transformer 模型 + Head 结构图 │
├──────────────────────────────────────────────────────┤
│ │
│ Input IDs → Embedding Layer │
│ ↓ │
│ Transformer Layers │
│ (Self-Attention + │
│ Feed Forward) │
│ ↓ │
│ Hidden States │
│ [batch, seq, 768] │
│ ↓ │
│ ┌──────────────┐ │
│ │ Model Head │ │
│ │ (Linear层) │ │
│ └──────────────┘ │
│ ↓ │
│ Task-specific Output │
│ (分类/生成/问答等) │
│ │
└──────────────────────────────────────────────────────┘
对于情感分类任务,我们使用 AutoModelForSequenceClassification:
from transformers import AutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
查看输出形状:
print(outputs.logits.shape)
# torch.Size([2, 2])
对输出进行后处理
模型输出的是 logits(对数几率),需要通过 SoftMax 转换为概率:
print(outputs.logits)
# tensor([[-1.5607, 1.6123],
# [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>)
import torch
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)
# tensor([[4.0195e-02, 9.5980e-01],
# [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)
获取标签:
model.config.id2label
# {0: 'NEGATIVE', 1: 'POSITIVE'}
最终结果:
- 第一句:消极 0.0402,积极 0.9598
- 第二句:消极 0.9995,积极 0.0005
处理多个序列
模型需要批次输入
模型默认需要句子列表。发送单个句子会失败:
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequence = "I've been waiting for a HuggingFace course my whole life."
tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids)
# 这一行会失败
model(input_ids) # IndexError
需要添加批次维度:
input_ids = torch.tensor([ids])
output = model(input_ids)
print("Logits:", output.logits)
填充输入(Padding)
处理不同长度的句子时需要填充:
batched_ids = [
[200, 200, 200],
[200, 200, tokenizer.pad_token_id],
]
但直接填充会影响注意力计算,需要使用 注意力掩码(attention mask)。
注意力掩码
注意力掩码用 0 和 1 标识哪些 token 需要关注:
batched_ids = [
[200, 200, 200],
[200, 200, tokenizer.pad_token_id],
]
attention_mask = [
[1, 1, 1],
[1, 1, 0],
]
outputs = model(
torch.tensor(batched_ids),
attention_mask=torch.tensor(attention_mask)
)
print(outputs.logits)
更长的句子
大多数模型处理 512 或 1024 个 token 的序列。解决方案:
- 使用支持更长序列的模型(如 Longformer、LED)
- 截断序列:
sequence = sequence[:max_sequence_length]
Tokenizer 详解
Tokenization 算法
基于单词(Word-based)
tokenized_text = "Jim Henson was a puppeteer".split()
# ['Jim', 'Henson', 'was', 'a', 'puppeteer']
基于单词的分词示例:
┌─────────────────────────────────────────────────┐
│ 原始文本: "Jim Henson was a puppeteer" │
│ ↓ │
│ 分词结果: [Jim] [Henson] [was] [a] [puppeteer] │
│ ↓ │
│ Token IDs: [5234] [6721] [1098] [23] [9876] │
└─────────────────────────────────────────────────┘
优点:简单易用 缺点:词汇表很大,相似词被视为不同(如 "dog" 和 "dogs")
基于字符(Character-based)
基于字符的分词示例:
┌──────────────────────────────────────────────────┐
│ 原始文本: "Hello" │
│ ↓ │
│ 分词结果: [H] [e] [l] [l] [o] │
│ ↓ │
│ Token IDs: [72] [101] [108] [108] [111] │
└──────────────────────────────────────────────────┘
优点:词汇表小,未知 token 少 缺点:单个字符语义信息少,序列长度大幅增加
基于子词(Subword)
结合两种方法的优点,常用词保持完整,罕见词分解为子词。
基于子词的分词示例:
┌──────────────────────────────────────────────────────┐
│ 原始文本: "Let's do tokenization!" │
│ ↓ │
│ 分词结果: [Let] ['s] [do] [token] [ization] [!] │
│ ↓ │
│ Token IDs: [2421] [188] [342] [19204] [2734] [999] │
└──────────────────────────────────────────────────────┘
例如:"annoyingly" → "annoying" + "ly"
"tokenization" → "token" + "ization"
常见算法:
- Byte-level BPE(GPT-2)
- WordPiece(BERT)
- SentencePiece/Unigram(多语言模型)
加载和保存 Tokenizer
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
# 使用
tokenizer("Using a Transformer network is simple")
# 保存
tokenizer.save_pretrained("directory_on_my_computer")
编码过程
编码分两步:分词 → 转换为 ID
分词
sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)
print(tokens)
# ['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
转换为 ID
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
# [7993, 170, 11303, 1200, 2443, 1110, 3014]
解码
decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)
# 'Using a Transformer network is simple'
综合应用
Tokenizer 的高级功能:
from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
# 单个句子
sequence = "I've been waiting for a HuggingFace course my whole life."
model_inputs = tokenizer(sequence)
# 多个句子
sequences = [
"I've been waiting for a HuggingFace course my whole life.",
"So have I!"
]
model_inputs = tokenizer(sequences)
# 填充
model_inputs = tokenizer(sequences, padding="longest")
model_inputs = tokenizer(sequences, padding="max_length")
model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
# 截断
model_inputs = tokenizer(sequences, truncation=True)
model_inputs = tokenizer(sequences, max_length=8, truncation=True)
# 返回特定框架的张量
model_inputs = tokenizer(sequences, padding=True, return_tensors="pt") # PyTorch
model_inputs = tokenizer(sequences, padding=True, return_tensors="tf") # TensorFlow
model_inputs = tokenizer(sequences, padding=True, return_tensors="np") # NumPy
特殊 Token
Tokenizer 会自动添加特殊 token:
sequence = "I've been waiting for a HuggingFace course my whole life."
model_inputs = tokenizer(sequence)
print(tokenizer.decode(model_inputs["input_ids"]))
# "[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
完整流程
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
"I've been waiting for a HuggingFace course my whole life.",
"So have I!"
]
tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
output = model(**tokens)
章末小测验
1. 自然语言处理流程的顺序是什么?
❌ 首先是模型它处理文本并返回原始预测。然后 tokenizer 会对这些预测进行解释,并在将它们转换回文本
❌ 首先 Tokenizer 处理文本并返回 id。模型根据这些 id 并输出预测,可以是一些文本。
✅ Tokenizer 处理文本并返回 ID。模型处理这些 ID 并输出预测。然后可以再次使用 tokenizer 将这些预测转换回文本。
2. Transformer 模型输出张量的维度是?
❌ 2 个维度:序列长度(Sequence Length)和批次大小(Batch Size)
❌ 2 个维度:序列长度(Sequence Length)和隐藏层大小(Hidden Size)
✅ 3 个维度:序列长度(Sequence Length)、批次大小(Batch Size)和隐藏层大小(Hidden Size)
3. 下列哪些是子词分词的例子?
✅ WordPiece
❌ 基于单个字符的分词
❌ 基于空格和标点符号的分割
✅ BPE (Byte Pair Encoding)
✅ Unigram
❌ 以上都不是
4. 什么是模型头(Head)?
❌ 原始 Transformer 网络的一种组件,直接将张量输入到正确的层
❌ 也称为自注意力(self-attention)机制,它会根据序列的其他 tokens 调整一个 token 的表示
✅ 一个附加组件,通常由一个或几个层组成,用于将 Transformer 的预测转换为特定任务的输出
5. 什么是 AutoModel?
❌ 根据你的数据自动进行训练的模型
✅ 一个根据 checkpoint 返回模型体系结构的对象
❌ 一种可以自动检测输入语言来加载正确权重的模型
6. 批处理不同长度的句子需要哪些处理?
✅ 截断
❌ 直接将 Tensors 返回
✅ 填充
✅ 注意力掩码(Attention masking)
7. 对 logits 使用 SoftMax 的意义?
❌ 它软化了 logits 输出,使结果更可靠
❌ 它限定了上下界,使模型的输出结果可以被解释
✅ 输出的和是 1,从而产生概率解释
8. Tokenizer API 的核心方法是?
❌ encode 因为它可以将文本编码为 ID,将预测的 ID 解码为文本
✅ 直接调用 Tokenizer 对象
❌ pad(填充)
❌ tokenize
9. tokenizer.tokenize("Hello!") 返回什么?
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
result = tokenizer.tokenize("Hello!")
✅ 字符串列表,每个字符串都是一个 token
❌ 一个 ID 的列表
❌ 包含所有分词后的字符串
10. 以下代码有什么错误?
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
model = AutoModel.from_pretrained("gpt2")
encoded = tokenizer("Hey!", return_tensors="pt")
result = model(**encoded)
❌ 不,看起来是对的
✅ Tokenizer 和模型应该来自相同的 checkpoint
❌ 由于模型输入需要是一个 Batch,因此可以使用 tokenizer 对其进行截断或填充来改进这段代码
总结
恭喜完成本章学习!你已经掌握:
- Transformer 模型的基本构造
- Tokenizer 管道的组成
- 如何使用 Transformers 模型
- 如何利用 tokenizer 转换文本
- Input IDs 的局限性和注意力掩码
- 灵活可配置的 Tokenizer 方法