Transformers 学习

目标:理解 Transformer 架构的核心原理,掌握 Hugging Face Transformers 的常用实操方式,并能完成文本分类、文本生成、问答、特征提取、微调、保存加载和简单部署。

目录

1. Transformers 是什么

这里的 Transformers 有两层含义:

第一层是算法架构:

Transformer 是一种基于注意力机制的深度学习架构,
最早由论文 Attention Is All You Need 系统提出。

第二层是工具库:

Hugging Face Transformers 是一个常用开源库,
提供 BERT、GPT、T5、LLaMA、ViT 等模型的加载、推理、训练和部署能力。

Transformer 架构广泛用于:

  • 文本分类。
  • 机器翻译。
  • 文本生成。
  • 问答系统。
  • 文本摘要。
  • 信息抽取。
  • 语义向量。
  • 图像分类。
  • 多模态模型。
  • 大语言模型。

2. Transformer 架构解决了什么问题

Transformer 出现前,序列建模常用 RNN、LSTM、GRU。

RNN 类模型的问题:

  • 难以并行,因为必须按时间步顺序计算。
  • 长距离依赖学习困难。
  • 序列很长时训练效率低。

Transformer 的关键变化:

  • 用 Self-Attention 建模 token 之间关系。
  • 所有位置可以并行计算。
  • 更擅长建模长距离依赖。
  • 更容易扩展到大模型和大数据。

一句话理解:

RNN 是按顺序读句子,Transformer 是让句子里的所有 token 同时互相看。

3. 核心概念:Token、Embedding、位置编码

3.1 Token

模型不能直接理解原始文本,需要先分词成 token。

示例:

原文:I love machine learning.
tokens: ["I", "love", "machine", "learning", "."]

现代模型常用子词分词:

unbelievable -> ["un", "##believable"]

不同模型 tokenizer 不同,所以 tokenizer 必须和 model 匹配。

3.2 Embedding

Embedding 把 token id 转换成向量。

token id -> dense vector

例如:

101 -> [0.12, -0.33, 0.56, ...]

3.3 位置编码

Self-Attention 本身不天然知道 token 顺序,因此需要位置编码。

常见位置表示:

  • 绝对位置编码。
  • 相对位置编码。
  • RoPE 旋转位置编码。
  • ALiBi 等长文本友好方法。

输入表示通常可以理解为:

token embedding + position embedding

4. Attention 和 Self-Attention

Attention 的直觉是:处理当前 token 时,模型应该关注哪些其他 token。

4.1 Q、K、V

Self-Attention 中,每个 token 会生成三个向量:

向量含义
Query我想找什么信息
Key我能提供什么索引
Value我真正携带的信息

计算过程简化为:

用 Query 和 Key 计算相关性
  -> softmax 得到注意力权重
  -> 用权重加权 Value

公式:

Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V

4.2 为什么除以 sqrt(d_k)

如果不缩放,QK^T 的值可能很大,softmax 后会过于尖锐,导致梯度不稳定。除以 sqrt(d_k) 可以稳定训练。

4.3 Self-Attention 示例

句子:

The animal didn't cross the street because it was tired.

模型处理 it 时,需要关注 animal,才能理解 it 指什么。

5. Multi-Head Attention

Multi-Head Attention 是把注意力分成多个头,每个头从不同角度学习关系。

不同头可能关注:

  • 主谓关系。
  • 指代关系。
  • 局部短语。
  • 长距离依赖。
  • 语义相似。

简化理解:

单头注意力:一个视角看句子。
多头注意力:多个视角同时看句子,然后合并结果。

6. Encoder、Decoder 和 Encoder-Decoder

Transformer 有三类常见结构。

6.1 Encoder-only

代表模型:

BERT
RoBERTa
DistilBERT
ModernBERT

特点:

  • 双向理解上下文。
  • 适合理解任务。

常见任务:

  • 文本分类。
  • 命名实体识别。
  • 文本匹配。
  • 向量检索。

6.2 Decoder-only

代表模型:

GPT
LLaMA
Qwen
Mistral
Gemma

特点:

  • 自回归生成。
  • 只能看当前位置之前的 token。
  • 适合生成任务。

常见任务:

  • 聊天。
  • 续写。
  • 代码生成。
  • 工具调用。

6.3 Encoder-Decoder

代表模型:

T5
BART
MarianMT

特点:

  • Encoder 理解输入。
  • Decoder 生成输出。

常见任务:

  • 翻译。
  • 摘要。
  • 改写。
  • 结构化生成。

7. 常见 Transformer 模型家族

模型架构常见用途
BERTEncoder-only分类、NER、匹配
RoBERTaEncoder-only分类、理解
DistilBERTEncoder-only轻量分类
GPTDecoder-only文本生成
LLaMADecoder-only聊天、生成、指令跟随
T5Encoder-Decoder翻译、摘要、文本到文本
BARTEncoder-Decoder摘要、生成
ViTEncoder-like图像分类
CLIP双塔/多模态图文匹配

选择建议:

  • 做分类和抽取:优先 BERT/RoBERTa 类。
  • 做生成和聊天:优先 GPT/LLaMA/Qwen 类。
  • 做翻译、摘要:优先 T5/BART 类。
  • 做向量检索:用专门的 embedding 模型,不要盲目拿原始 BERT [CLS]

8. 环境安装与验证

8.1 创建虚拟环境

Windows PowerShell:

python -m venv .venv
.\.venv\Scripts\activate
python -m pip install --upgrade pip setuptools wheel

Linux/macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel

8.2 安装依赖

CPU 入门:

pip install torch transformers datasets evaluate accelerate scikit-learn sentencepiece fastapi uvicorn

如果要使用 GPU,请先安装与 CUDA 匹配的 PyTorch。

8.3 验证环境

保存为 check_transformers_env.py

import torch
import transformers

print("torch:", torch.__version__)
print("transformers:", transformers.__version__)
print("cuda available:", torch.cuda.is_available())

运行:

python check_transformers_env.py

9. 实操一:pipeline 快速体验

pipeline 是 Hugging Face Transformers 最方便的推理入口。

9.1 情感分析

保存为 pipeline_sentiment.py

from transformers import pipeline


def main():
    classifier = pipeline("sentiment-analysis")

    texts = [
        "I love this course!",
        "This product is terrible.",
    ]

    for text in texts:
        print(text, classifier(text))


if __name__ == "__main__":
    main()

9.2 MASK 填空

from transformers import pipeline

fill_mask = pipeline("fill-mask", model="google-bert/bert-base-uncased")
print(fill_mask("The capital of France is [MASK]."))

9.3 文本生成

from transformers import pipeline

generator = pipeline("text-generation", model="openai-community/gpt2")

result = generator(
    "In the future, artificial intelligence",
    max_new_tokens=40,
    do_sample=True,
    temperature=0.8,
)

print(result[0]["generated_text"])

10. 实操二:Tokenizer 和 AutoModel

pipeline 很方便,但真实项目中经常需要自己控制 tokenizer、model 和输出。

保存为 tokenizer_automodel_demo.py

import torch
from transformers import AutoModel, AutoTokenizer


def main():
    model_name = "google-bert/bert-base-uncased"

    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModel.from_pretrained(model_name)
    model.eval()

    texts = [
        "Transformers are powerful models.",
        "Attention is all you need.",
    ]

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

    with torch.no_grad():
        outputs = model(**inputs)

    print("input_ids shape:", inputs["input_ids"].shape)
    print("last_hidden_state shape:", outputs.last_hidden_state.shape)

    cls_embedding = outputs.last_hidden_state[:, 0, :]
    print("cls_embedding shape:", cls_embedding.shape)


if __name__ == "__main__":
    main()

重点:

  • AutoTokenizer 根据模型名自动加载匹配 tokenizer。
  • AutoModel 加载基础模型,不带任务头。
  • AutoModelForSequenceClassification 加载分类模型。
  • AutoModelForCausalLM 加载自回归生成模型。
  • AutoModelForSeq2SeqLM 加载 Encoder-Decoder 生成模型。

11. 实操三:文本分类推理

保存为 classification_inference.py

import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer


def main():
    model_name = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"

    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForSequenceClassification.from_pretrained(model_name)
    model.eval()

    texts = [
        "This movie is fantastic.",
        "I do not like this app.",
    ]

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

    with torch.no_grad():
        logits = model(**inputs).logits
        probs = torch.softmax(logits, dim=-1)
        pred_ids = torch.argmax(probs, dim=-1)

    for text, pred_id, prob in zip(texts, pred_ids, probs):
        label = model.config.id2label[int(pred_id)]
        score = float(prob[pred_id])
        print(f"{text} -> {label}, score={score:.4f}")


if __name__ == "__main__":
    main()

12. 实操四:文本生成

文本生成通常使用 Decoder-only 模型。

保存为 text_generation_demo.py

from transformers import AutoModelForCausalLM, AutoTokenizer


def main():
    model_name = "openai-community/gpt2"

    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name)

    prompt = "Machine learning is"
    inputs = tokenizer(prompt, return_tensors="pt")

    outputs = model.generate(
        **inputs,
        max_new_tokens=60,
        do_sample=True,
        temperature=0.8,
        top_p=0.9,
        repetition_penalty=1.1,
        pad_token_id=tokenizer.eos_token_id,
    )

    text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    print(text)


if __name__ == "__main__":
    main()

12.1 常见生成参数

参数作用
max_new_tokens最多生成多少新 token
do_sample是否采样
temperature控制随机性
top_pnucleus sampling,控制候选 token 累积概率
top_k只从概率最高的 k 个 token 采样
repetition_penalty降低重复
num_beamsbeam search 宽度

经验:

  • 想稳定可控:do_sample=False 或使用 num_beams
  • 想更有创造性:do_sample=True,适当提高 temperature
  • 输出重复:调高 repetition_penalty,降低 max_new_tokens 或调整 prompt。

13. 实操五:问答和摘要

13.1 抽取式问答

保存为 qa_pipeline_demo.py

from transformers import pipeline


def main():
    qa = pipeline("question-answering")

    context = (
        "Transformers are neural network architectures based on attention. "
        "They are widely used in natural language processing and computer vision."
    )

    question = "What are Transformers based on?"
    result = qa(question=question, context=context)
    print(result)


if __name__ == "__main__":
    main()

13.2 文本摘要

保存为 summarization_demo.py

from transformers import pipeline


def main():
    summarizer = pipeline("summarization", model="facebook/bart-large-cnn")

    text = (
        "Transformers have become the dominant architecture for many natural "
        "language processing tasks. They rely on attention mechanisms to model "
        "relationships between tokens and can be scaled to very large datasets "
        "and model sizes."
    )

    result = summarizer(
        text,
        max_length=50,
        min_length=15,
        do_sample=False,
    )

    print(result[0]["summary_text"])


if __name__ == "__main__":
    main()

14. 实操六:文本分类微调

这个例子用 Hugging Face datasets 构造一个小数据集,演示完整 Trainer 微调流程。

保存为 transformers_text_classification_train.py

import numpy as np
import evaluate
from datasets import Dataset
from transformers import (
    AutoModelForSequenceClassification,
    AutoTokenizer,
    DataCollatorWithPadding,
    Trainer,
    TrainingArguments,
)


def build_dataset():
    texts = [
        "I love this product.",
        "This app is terrible.",
        "The service was excellent.",
        "I hate the new update.",
        "The movie was wonderful.",
        "The food was cold and bad.",
        "Great experience overall.",
        "The website crashes all the time.",
        "Fast delivery and good quality.",
        "The support team was rude.",
    ]
    labels = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

    dataset = Dataset.from_dict({"text": texts, "label": labels})
    return dataset.train_test_split(test_size=0.3, seed=42)


def main():
    model_name = "distilbert/distilbert-base-uncased"
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    dataset = build_dataset()

    def preprocess(examples):
        return tokenizer(examples["text"], truncation=True, max_length=128)

    tokenized_dataset = dataset.map(preprocess, batched=True)
    data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

    id2label = {0: "NEGATIVE", 1: "POSITIVE"}
    label2id = {"NEGATIVE": 0, "POSITIVE": 1}

    model = AutoModelForSequenceClassification.from_pretrained(
        model_name,
        num_labels=2,
        id2label=id2label,
        label2id=label2id,
    )

    accuracy = evaluate.load("accuracy")

    def compute_metrics(eval_pred):
        logits, labels = eval_pred
        predictions = np.argmax(logits, axis=-1)
        return accuracy.compute(predictions=predictions, references=labels)

    training_args = TrainingArguments(
        output_dir="transformers_sentiment_model",
        learning_rate=2e-5,
        per_device_train_batch_size=4,
        per_device_eval_batch_size=4,
        num_train_epochs=3,
        weight_decay=0.01,
        eval_strategy="epoch",
        save_strategy="epoch",
        load_best_model_at_end=True,
        logging_steps=1,
        report_to="none",
    )

    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=tokenized_dataset["train"],
        eval_dataset=tokenized_dataset["test"],
        processing_class=tokenizer,
        data_collator=data_collator,
        compute_metrics=compute_metrics,
    )

    trainer.train()
    trainer.evaluate()

    trainer.save_model("transformers_sentiment_model/best")
    tokenizer.save_pretrained("transformers_sentiment_model/best")


if __name__ == "__main__":
    main()

如果 Transformers 版本较旧,可能需要把:

eval_strategy="epoch"
processing_class=tokenizer

替换成:

evaluation_strategy="epoch"
tokenizer=tokenizer

14.1 微调后推理

保存为 transformers_text_classification_infer.py

from transformers import pipeline


def main():
    classifier = pipeline(
        "text-classification",
        model="transformers_sentiment_model/best",
        tokenizer="transformers_sentiment_model/best",
    )

    texts = [
        "This course is very helpful.",
        "The app is slow and annoying.",
    ]

    for text in texts:
        print(text, classifier(text))


if __name__ == "__main__":
    main()

15. 保存、加载和部署

15.1 保存模型

model.save_pretrained("my_model")
tokenizer.save_pretrained("my_model")

15.2 加载模型

from transformers import AutoModelForSequenceClassification, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("my_model")
model = AutoModelForSequenceClassification.from_pretrained("my_model")

15.3 FastAPI 部署文本分类

安装:

pip install fastapi uvicorn

保存为 transformers_api.py

import torch
from fastapi import FastAPI
from pydantic import BaseModel
from transformers import AutoModelForSequenceClassification, AutoTokenizer

MODEL_DIR = "transformers_sentiment_model/best"

app = FastAPI()
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)
model = AutoModelForSequenceClassification.from_pretrained(MODEL_DIR)
model.eval()


class PredictRequest(BaseModel):
    text: str


@app.post("/predict")
def predict(request: PredictRequest):
    inputs = tokenizer(
        request.text,
        return_tensors="pt",
        truncation=True,
        max_length=128,
    )

    with torch.no_grad():
        logits = model(**inputs).logits
        probs = torch.softmax(logits, dim=-1)[0]

    label_id = int(torch.argmax(probs).item())
    label = model.config.id2label[label_id]

    return {
        "label": label,
        "score": float(probs[label_id].item()),
    }

启动:

uvicorn transformers_api:app --reload --host 127.0.0.1 --port 8000

请求:

curl -X POST "http://127.0.0.1:8000/predict" ^
  -H "Content-Type: application/json" ^
  -d "{\"text\":\"This course is great\"}"

Linux/macOS:

curl -X POST "http://127.0.0.1:8000/predict" \
  -H "Content-Type: application/json" \
  -d '{"text":"This course is great"}'

16. 调参和工程经验

16.1 分类任务常用超参数

参数常用值
learning_rate1e-55e-5
batch_size81632
epochs25
max_length128256512
weight_decay0.01
warmup_ratio0.060.1

16.2 生成任务常用策略

目标建议
输出稳定greedy 或 beam search
输出多样sampling、temperature、top_p
减少重复repetition_penalty、no_repeat_ngram_size
控制长度max_new_tokens、min_new_tokens

16.3 长文本处理

常见方法:

  • 截断到最大长度。
  • 滑动窗口。
  • 先检索相关片段再送模型。
  • 分段编码后聚合。
  • 使用长文本模型。

16.4 性能优化

常见方法:

  • 使用 GPU。
  • 使用 half precision。
  • 减小 max_length。
  • 使用更小模型。
  • batch 推理。
  • ONNX Runtime。
  • TensorRT。
  • 量化。

17. 常见问题排查

17.1 tokenizer 和 model 不匹配

错误示例:

tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased")
model = AutoModel.from_pretrained("openai-community/gpt2")

应使用同一模型名或同一保存目录。

17.2 CUDA out of memory

处理:

减小 batch_size
减小 max_length
使用更小模型
开启 mixed precision
使用 gradient accumulation

17.3 生成文本重复

处理:

  • 降低 max_new_tokens
  • 设置 repetition_penalty
  • 设置 no_repeat_ngram_size
  • 调整 temperaturetop_p
  • 优化 prompt。

17.4 分类结果全是一类

检查:

  • 标签是否映射错。
  • 数据是否类别不均衡。
  • 训练集是否太小。
  • 学习率是否不合适。
  • tokenizer 是否和 model 匹配。

17.5 下载模型很慢

可以:

  • 提前下载模型。
  • 设置 Hugging Face 镜像或缓存目录。
  • 使用本地模型目录加载。
  • 检查网络代理。

18. 面试常问问题

18.1 Transformer 是什么?

Transformer 是一种基于注意力机制的神经网络架构,能够并行处理序列,并擅长建模长距离依赖。

18.2 Transformer 为什么比 RNN 更容易并行?

RNN 必须按时间步顺序计算,后一个状态依赖前一个状态。Transformer 使用 Self-Attention,一次可以计算所有 token 之间的关系,因此更容易并行。

18.3 Self-Attention 是什么?

Self-Attention 让序列中每个 token 根据和其他 token 的相关性聚合信息,从而得到上下文相关表示。

18.4 Q、K、V 分别是什么?

Query 表示当前 token 想查询什么,Key 表示每个 token 可被匹配的索引,Value 表示真正被聚合的信息。

18.5 为什么 Attention 要除以 sqrt(d_k)

为了防止点积值过大导致 softmax 过于尖锐,使训练更加稳定。

18.6 Multi-Head Attention 有什么用?

多头注意力允许模型从多个子空间和角度学习 token 之间的关系,比单头表达能力更强。

18.7 Transformer 为什么需要位置编码?

Self-Attention 本身不包含顺序信息。如果没有位置编码,模型难以区分相同 token 在不同位置的含义。

18.8 Encoder-only、Decoder-only、Encoder-Decoder 有什么区别?

Encoder-only 适合理解任务,如 BERT;Decoder-only 适合生成任务,如 GPT;Encoder-Decoder 适合输入到输出转换任务,如翻译和摘要。

18.9 BERT 和 GPT 的核心区别是什么?

BERT 是 Encoder-only,双向编码,适合理解任务;GPT 是 Decoder-only,自回归生成,适合文本生成。

18.10 Causal Mask 是什么?

Causal Mask 用于 Decoder-only 模型,防止当前位置看到未来 token,从而保证自回归生成只依赖已生成内容。

18.11 attention_mask 和 causal mask 有什么区别?

attention_mask 通常用于屏蔽 padding;causal mask 用于屏蔽未来 token。

18.12 Hugging Face pipeline 是什么?

pipeline 是 Transformers 提供的高级推理接口,可以用很少代码完成分类、生成、问答、摘要等任务。

18.13 AutoTokenizer、AutoModel 有什么用?

它们会根据模型名自动加载对应的 tokenizer 和模型结构,减少手动选择类的成本。

18.14 AutoModelAutoModelForSequenceClassification 有什么区别?

AutoModel 只加载基础模型,输出隐藏状态;AutoModelForSequenceClassification 会额外带分类头,适合文本分类。

18.15 微调 Transformers 时为什么学习率通常很小?

预训练模型已经学到大量通用知识,学习率过大可能破坏已有参数,导致效果下降。

18.16 max_length 怎么选?

要根据任务文本长度、显存和效果权衡。短文本分类常用 128 或 256,长文本可能需要 512、滑动窗口或长文本模型。

18.17 为什么 tokenizer 必须和 model 匹配?

因为模型的词表、特殊符号、分词方式和 embedding 参数是对应的。混用会导致输入 id 含义错误,效果严重下降甚至报错。

18.18 文本生成中 temperature 有什么作用?

temperature 控制采样随机性。值越高,输出越随机;值越低,输出越保守。

18.19 top-k 和 top-p 有什么区别?

top-k 只从概率最高的 k 个 token 中采样;top-p 从累积概率达到 p 的候选集合中采样,候选数量会动态变化。

18.20 面试中如何概括 Transformers 项目经验?

可以这样回答:

我用 Hugging Face Transformers 做过文本分类和生成任务。
分类任务中,我用 AutoTokenizer 和 AutoModelForSequenceClassification 加载预训练模型,
通过 Trainer 微调,重点关注 max_length、学习率、batch_size、类别不均衡和验证集 F1;
生成任务中,我使用 AutoModelForCausalLM,并通过 temperature、top_p、repetition_penalty 控制输出质量;
上线时保存 tokenizer 和 model,通过 FastAPI 提供推理接口,并根据延迟要求做 batch、量化或 ONNX 优化。

19. 练习任务

练习 1:跑通 pipeline

分别跑通:

sentiment-analysis
fill-mask
text-generation
question-answering
summarization

练习 2:观察 tokenizer

用不同模型的 tokenizer 处理同一句话,比较 token 切分差异。

练习 3:提取句向量

使用 BERT 的 last_hidden_state,分别取 [CLS] 和 mean pooling,比较输出形状。

练习 4:微调分类模型

准备自己的小型情感分类数据集,替换文档中的示例数据,训练一个分类模型。

练习 5:调生成参数

固定同一个 prompt,分别调整:

temperature
top_p
repetition_penalty
max_new_tokens

观察生成结果变化。

练习 6:部署接口

运行 FastAPI 示例,用 curl 或 Postman 请求 /predict

20. 参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值