Danh sách bài viết

Bài 18: AutoModel và AutoTokenizer — pattern chuẩn

Pipeline API (Bài 17) tiện cho prototype nhưng giấu hết chi tiết. Khi cần control sampling, custom forward, batch inference hoặc extract embedding, ta xuống một tầng dùng AutoModel + AutoTokenizer — pattern chuẩn của thư viện transformers. Bài này đi qua: vì sao Auto, các Auto class hay dùng (AutoTokenizer, AutoModel, AutoModelForCausalLM, AutoModelForSeq2SeqLM, AutoModelForSequenceClassification, AutoModelForTokenClassification, AutoModelForQuestionAnswering, AutoModelForMaskedLM, AutoModelForImageClassification, AutoModelForCTC, AutoFeatureExtractor, AutoImageProcessor), workflow 3 bước load - encode - generate, các method của tokenizer (tokenize, encode, decode, __call__), padding/truncation, special tokens, chat template (apply_chat_template), tham số quan trọng của from_pretrained (revision, cache_dir, trust_remote_code, torch_dtype, device_map, low_cpu_mem_usage), cách inspect model config và đếm parameter, custom forward không dùng generate, output object ModelOutput, save/load locally, alternative class kiến trúc cụ thể, switch model dễ dàng, embedding extraction với BERT, các pitfall thường gặp.

25/05/2026
13 phút đọc
3 lượt xem
1

Mục tiêu bài học

Sau bài học, bạn sẽ:

  • Hiểu pattern Auto class và lý do tồn tại: viết một lần, dùng cho nhiều kiến trúc khác nhau.
  • Biết chọn đúng Auto class cho task (causal LM, seq2seq, classification, MLM, image, audio).
  • Thuộc workflow 3 bước: from_pretrained → tokenize → forward / generate.
  • Phân biệt các method tokenize, encode, decode, __call__ của tokenizer.
  • Dùng được padding, truncation, apply_chat_template.
  • Hiểu các tham số quan trọng của from_pretrained: torch_dtype, device_map, revision, trust_remote_code.
  • Inspect model: config, số tham số, kiến trúc layer.
  • Viết custom forward thay vì generate khi cần control sampling thấp cấp.
  • Tránh các pitfall: thiếu pad_token, quên model.eval(), sai chat template.
2

Vì sao cần Auto class

Mỗi kiến trúc trong transformers có một class riêng: LlamaForCausalLM, MistralForCausalLM, Qwen2ForCausalLM, BertForSequenceClassification, T5ForConditionalGeneration… Nếu code hard-code tên class, đổi model là phải sửa import + class name.

Auto class giải bài này theo cơ chế factory:

  • Bạn truyền tên repo hoặc đường dẫn local vào AutoModelForCausalLM.from_pretrained(...).
  • Thư viện đọc file config.json trong repo, lấy trường model_type (vd "llama", "mistral", "qwen2").
  • Mapping nội bộ trỏ "llama" → LlamaForCausalLM, "mistral" → MistralForCausalLM, v.v. — class thật được khởi tạo phía sau.

Kết quả: cùng một dòng code chạy được cho mọi kiến trúc tương ứng task. Code portable theo nghĩa đổi tên model là đổi xong, không cần thay class.

3

Các Auto class thường dùng

Chọn Auto class theo task, không theo kiến trúc:

  • AutoTokenizer — load tokenizer khớp với model.
  • AutoModel — base model không có task head, output là hidden states. Dùng cho embedding hoặc làm backbone.
  • AutoModelForCausalLM — text generation decoder-only (GPT, Llama, Mistral, Qwen, Phi, Gemma).
  • AutoModelForSeq2SeqLM — seq2seq encoder-decoder (T5, BART, mT5, FLAN-T5). Dùng cho summarization, translation.
  • AutoModelForSequenceClassification — classification toàn câu (sentiment, NLI, intent).
  • AutoModelForTokenClassification — label từng token (NER, POS tagging).
  • AutoModelForQuestionAnswering — extractive QA (SQuAD style).
  • AutoModelForMaskedLM — masked language modeling (BERT, RoBERTa).
  • AutoModelForImageClassification — ViT, ConvNeXt, Swin… cho phân loại ảnh.
  • AutoModelForCTC — ASR theo CTC loss (wav2vec2).
  • AutoFeatureExtractor / AutoImageProcessor — preprocess cho image/audio (thay vai trò tokenizer).

Mỗi Auto class chỉ load checkpoint tương thích. Vd dùng AutoModelForCausalLM với bert-base-uncased sẽ báo lỗi vì BERT là encoder, không có LM head causal.

4

Workflow 3 bước

Cấu trúc chuẩn cho mọi bài toán text generation:

from transformers import AutoTokenizer, AutoModelForCausalLM

# 1. Load tokenizer + model
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-1B")
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-1B")

# 2. Encode input
inputs = tokenizer("Hello, AI!", return_tensors="pt")

# 3. Forward / Generate
outputs = model.generate(**inputs, max_new_tokens=30)
text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(text)

Ba bước này lặp lại cho gần như mọi pipeline causal LM. Phần lớn complexity ở thực tế nằm ở cấu hình (dtype, device, batch, sampling) chứ không phải ở 3 bước trên.

5

Tokenizer methods

Tokenizer có nhiều method, mỗi cái cho một mục đích:

  • tokenizer.tokenize(text) — text → list các token string (debug, xem cách model cắt từ).
  • tokenizer.encode(text) — text → list các token ID (int). Không trả attention mask.
  • tokenizer.decode(ids) — list ID → text. Tham số skip_special_tokens=True để loại bỏ BOS, EOS, PAD trong output.
  • tokenizer(text, ...) (__call__) — đầy đủ nhất: trả dict gồm input_ids, attention_mask (và token_type_ids nếu có), hỗ trợ return_tensors, padding, truncation.
  • tokenizer.batch_decode(ids_batch) — decode cả batch cùng lúc.

Trong code production thường chỉ dùng __call__decode / batch_decode. Các method còn lại hữu ích khi debug hoặc thao tác thấp cấp.

6

Output của tokenizer

Khi gọi tokenizer(text, return_tensors="pt"), output là dict (chính xác là BatchEncoding):

  • input_ids — tensor shape (batch, seq_len) chứa token ID.
  • attention_mask — tensor cùng shape: 1 cho token thực, 0 cho padding.
  • token_type_ids — chỉ có với BERT-style (phân biệt segment A / B), không có ở Llama, Mistral.

Khi forward, chuyển cả dict vào model bằng **inputs; model chỉ lấy field nó cần, bỏ qua field thừa. Tham số return_tensors có thể là "pt" (PyTorch), "tf" (TensorFlow), "np" (NumPy); để mặc định trả Python list.

7

Padding và truncation

Batch input phải cùng độ dài → cần padding. Câu quá dài → cần truncation. Cú pháp:

texts = ["Hello", "How are you today?", "AI engineering."]

inputs = tokenizer(
    texts,
    padding=True,         # pad đến độ dài câu dài nhất trong batch
    truncation=True,      # cắt bớt nếu quá max_length
    max_length=512,
    return_tensors="pt",
)
print(inputs["input_ids"].shape)       # (3, seq_len)
print(inputs["attention_mask"].shape)  # (3, seq_len)

Các giá trị cho padding:

  • True hoặc "longest" — pad tới câu dài nhất trong batch (mặc định khi bật padding).
  • "max_length" — pad tới max_length cố định (thuận lợi cho compile / TorchScript).
  • False — không pad.

attention_mask đảm bảo padding không ảnh hưởng đến attention score; trong gần như mọi trường hợp nên truyền mask này vào model.

8

Special tokens

Mỗi tokenizer có một số token đặc biệt:

  • tokenizer.bos_token — beginning of sequence.
  • tokenizer.eos_token — end of sequence.
  • tokenizer.pad_token — padding.
  • tokenizer.unk_token — unknown.
  • BERT có thêm cls_token, sep_token, mask_token.

Vài model decoder-only (GPT-2, Llama base) không có pad_token mặc định — khi batch hoặc gọi generate nhiều câu sẽ lỗi. Quy ước phổ biến là tái sử dụng eos_token làm pad:

if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token
    tokenizer.pad_token_id = tokenizer.eos_token_id

Lưu ý: cấu hình này ảnh hưởng cả generate; đảm bảo loss tính lúc training mask đúng pad_token_id để không học từ padding.

9

Chat template

Mỗi model instruct dùng một chat format riêng: Llama 3 dùng <|begin_of_text|> + <|start_header_id|>, Mistral dùng [INST] ... [/INST], Qwen dùng <|im_start|>… Viết tay format này dễ sai. Tokenizer có sẵn template lưu trong tokenizer.chat_template:

messages = [
    {"role": "system", "content": "You are helpful."},
    {"role": "user", "content": "Hello!"},
]

text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
)
print(text)

Các tham số quan trọng:

  • tokenize=False — trả string đã format (để xem hoặc gửi qua API). True trả token ID.
  • add_generation_prompt=True — thêm phần mở đầu cho lượt assistant (cần khi generate). Khi training, để False.
  • return_tensors="pt" — kết hợp với tokenize=True để có sẵn tensor.

Dùng template từ tokenizer thay vì viết tay là cách an toàn nhất để không nhầm format khi đổi model.

10

Tham số from_pretrained

Vài tham số hay dùng:

  • revision="main" — chọn branch / tag / commit cụ thể. Pin revision cho production để inference ổn định.
  • cache_dir="./cache" — đổi vị trí cache (mặc định ~/.cache/huggingface).
  • trust_remote_code=False — chỉ bật khi tin tưởng repo; nhiều model custom yêu cầu chạy code Python từ repo, có rủi ro bảo mật.
  • torch_dtype=torch.bfloat16 — load thẳng dưới dạng bf16 / fp16, tiết kiệm một nửa VRAM so với fp32. Trên GPU mới nên ưu tiên bf16.
  • device_map="auto"accelerate tự phân chia layer lên GPU / CPU / disk. Cần thiết khi model lớn hơn VRAM một thiết bị.
  • low_cpu_mem_usage=True — load incrementally, không giữ 2 bản trên RAM lúc init.
  • quantization_config=... — load model 4-bit / 8-bit (qua bitsandbytes) — sẽ deep-dive ở Module 8.
import torch
from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-1.5B-Instruct",
    torch_dtype=torch.bfloat16,
    device_map="auto",
    low_cpu_mem_usage=True,
)
11

Inspect model

Khi load xong, có vài cách kiểm tra model:

print(model)                                                # tóm tắt kiến trúc layer
print(model.config)                                         # toàn bộ config
print(sum(p.numel() for p in model.parameters()))           # tổng số param
print(sum(p.numel() for p in model.parameters() if p.requires_grad))  # param trainable
print(next(model.parameters()).dtype, next(model.parameters()).device)

In model ra cho phép soi từng layer: bao nhiêu transformer block, dimension, MLP intermediate size, attention head. Hữu ích khi cần freeze một số layer hoặc nối thêm head custom.

12

Model config

model.config chứa toàn bộ siêu tham số đã đọc từ config.json. Một số trường hay dùng:

  • config.hidden_size — dimension của embedding (vd 2048, 4096).
  • config.num_attention_heads — số head trong multi-head attention.
  • config.num_hidden_layers — số transformer block.
  • config.vocab_size — size vocabulary tokenizer.
  • config.max_position_embeddings — context length tối đa model được train.
  • config.model_type — định danh kiến trúc ("llama", "mistral", "qwen2"…).

Trường max_position_embeddings đặc biệt quan trọng — vượt quá ngưỡng này không có nghĩa code crash ngay, nhưng chất lượng output thường suy giảm rõ rệt.

13

Custom forward

generate tiện nhưng đóng kín. Khi cần control thấp cấp (custom sampling, beam search tự code, constrained decoding), gọi forward trực tiếp:

import torch

model.eval()
with torch.no_grad():
    outputs = model(**inputs)
    logits = outputs.logits                # (batch, seq_len, vocab_size)
    last_token_logits = logits[:, -1, :]   # logit của token cuối
    next_token = last_token_logits.argmax(dim=-1)  # greedy
print(tokenizer.decode(next_token))

Hai dòng cần nhớ:

  • model.eval() — tắt dropout / batchnorm training mode. Quên dòng này → output có ngẫu nhiên không cần thiết.
  • torch.no_grad() — không lưu graph backward, giảm VRAM khi inference.

Trên GPU, thêm inputs = {k: v.to(model.device) for k, v in inputs.items()} trước khi forward để tensor cùng device với model.

14

ModelOutput

Output của model là một subclass của ModelOutput — namespace + ordered dict. Các field tuỳ task:

  • logits — cho mọi LM (causal, MLM, seq2seq). Shape (batch, seq, vocab).
  • last_hidden_state — cho base model (AutoModel). Shape (batch, seq, hidden).
  • hidden_states — list hidden state từng layer; chỉ có khi truyền output_hidden_states=True khi forward.
  • attentions — attention weight từng layer; chỉ có khi truyền output_attentions=True.
  • past_key_values — KV cache, để generate tăng tốc token-by-token.

Truy cập bằng cả attribute (outputs.logits) lẫn key (outputs["logits"]). Bật output_hidden_statesoutput_attentions chỉ khi cần — tăng VRAM rõ rệt.

15

Save và load locally

Sau khi fine-tune, hoặc khi muốn pin một bản model cho production, lưu local:

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

Thư mục ./my_model sẽ chứa config.json, model.safetensors (hoặc .bin), tokenizer.json, tokenizer_config.json, special tokens map… Load lại y nguyên cách dùng repo Hub:

from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained("./my_model")
tokenizer = AutoTokenizer.from_pretrained("./my_model")

safetensors là format mặc định từ transformers 4.x — load nhanh, an toàn hơn pickle (không exec code khi load).

16

Architecture class

Alternative của Auto: import thẳng class kiến trúc.

from transformers import LlamaForCausalLM, BertForSequenceClassification

model = LlamaForCausalLM.from_pretrained("meta-llama/Llama-3.2-1B")
clf = BertForSequenceClassification.from_pretrained("bert-base-uncased")

Cùng functionality. Khác biệt:

  • Auto — portable, đổi model name không sửa code. Khuyến nghị cho phần lớn use case.
  • Architecture class — tường minh về kiến trúc, IDE auto-complete chính xác hơn, dễ subclass.

Khi viết thư viện hoặc tutorial generic, dùng Auto. Khi viết research code chuyên cho một kiến trúc cụ thể, dùng class architecture cho rõ ý.

17

Embedding extraction

Không phải lúc nào cũng cần generate; nhiều bài toán cần embedding (semantic search, clustering, classification head riêng). Dùng AutoModel (không head):

import torch
from transformers import AutoModel, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModel.from_pretrained("bert-base-uncased")
model.eval()

inputs = tokenizer(
    ["Câu thứ nhất.", "Câu thứ hai dài hơn một chút."],
    padding=True,
    truncation=True,
    return_tensors="pt",
)

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

# Mean pooling token embedding theo attention mask
token_emb = outputs.last_hidden_state            # (batch, seq, hidden)
mask = inputs["attention_mask"].unsqueeze(-1)    # (batch, seq, 1)
sentence_emb = (token_emb * mask).sum(1) / mask.sum(1).clamp(min=1)
print(sentence_emb.shape)                        # (2, 768)

Mean pooling theo attention mask là baseline đơn giản, vẫn ổn cho nhiều task. Khi cần chất lượng cao hơn, dùng model đã train chuyên cho embedding (sentence-transformers, BGE, E5) sẽ tốt hơn BERT thuần.

18

Switch model

Ưu điểm rõ nhất của Auto pattern: đổi model name là xong. Cùng một file code chạy được nhiều kiến trúc:

from transformers import AutoTokenizer, AutoModelForCausalLM

# Đổi giá trị này để switch model
model_name = "Qwen/Qwen2.5-1.5B-Instruct"
# model_name = "meta-llama/Llama-3.2-1B-Instruct"
# model_name = "microsoft/Phi-3.5-mini-instruct"

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

Lưu ý: dù code không đổi, prompt format và chat template giữa các model khác nhau. Khi switch nên test lại chất lượng output trên benchmark riêng của bạn, không assume một model thay thế hoàn toàn được model kia.

19

Pipeline vs AutoModel

Hai layer trong cùng thư viện, dùng khi nào:

  • Pipeline (Bài 17) — 2-3 dòng code, ẩn tokenizer + model + post-process. Phù hợp: prototype, demo, smoke test, jupyter notebook khám phá.
  • AutoModel + AutoTokenizer (bài này) — control đầy đủ: chọn dtype, device, sampling, batch shape, custom forward, extract intermediate state. Phù hợp: production, fine-tune, embedding pipeline, agent / RAG.

Trong codebase thực tế, thường thấy cả hai: pipeline cho script test nhanh, AutoModel cho service production. Hai layer dùng chung cùng một config.json nên switch giữa hai khi cần không phải viết lại.

20

Pitfall

  • Quên pad_token với GPT-2 / Llama base → batch và generate nhiều câu crash. Set tokenizer.pad_token = tokenizer.eos_token trước khi batch.
  • Chọn sai Auto class — vd dùng AutoModelForCausalLM với BERT (encoder-only) → lỗi load. Tham khảo lại Bước 3 chọn theo task.
  • Quên model.eval() khi inference → dropout vẫn active, output không deterministic.
  • Quên torch.no_grad() → VRAM phình do giữ graph backward.
  • Quên chuyển tensor lên cùng device với model khi dùng device_map → lỗi expected all tensors to be on the same device.
  • Viết tay chat format thay vì apply_chat_template → sai một dấu là output suy giảm rõ rệt, khó debug.
  • Bật trust_remote_code=True bừa với repo không quen → rủi ro bảo mật.
  • Load model lớn fp32 trên GPU 8 GB → OOM ngay. Dùng torch_dtype=torch.bfloat16 hoặc quantization.
21

Code Python

1) Load Qwen 2.5-1.5B và generate manual (không dùng pipeline):

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

model_name = "Qwen/Qwen2.5-1.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map="auto",
)

messages = [
    {"role": "system", "content": "Bạn là trợ lý ngắn gọn."},
    {"role": "user", "content": "Giới thiệu Hugging Face trong 2 câu."},
]

prompt = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

with torch.no_grad():
    output_ids = model.generate(
        **inputs,
        max_new_tokens=120,
        do_sample=False,
    )

# Chỉ decode phần token mới sinh ra
new_tokens = output_ids[0, inputs["input_ids"].shape[1]:]
print(tokenizer.decode(new_tokens, skip_special_tokens=True))

2) Extract embedding từ BERT:

import torch
from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModel.from_pretrained("bert-base-uncased")
model.eval()

sentences = [
    "Transformers are a type of neural network.",
    "Bananas are yellow fruits.",
    "Attention is all you need.",
]

inputs = tokenizer(sentences, padding=True, truncation=True, return_tensors="pt")
with torch.no_grad():
    outputs = model(**inputs)

mask = inputs["attention_mask"].unsqueeze(-1).float()
emb = (outputs.last_hidden_state * mask).sum(1) / mask.sum(1).clamp(min=1)
print("embedding shape:", emb.shape)   # (3, 768)

3) Switch sang Llama 3.2-1B — chỉ đổi model_name (cần đã accept terms + login):

model_name = "meta-llama/Llama-3.2-1B-Instruct"  # chỉ đổi dòng này
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map="auto",
)
# phần còn lại ở snippet (1) chạy nguyên xi

4) In config và đếm param:

cfg = model.config
print(cfg.model_type, cfg.hidden_size, cfg.num_hidden_layers, cfg.num_attention_heads)
print("max ctx:", cfg.max_position_embeddings, "vocab:", cfg.vocab_size)
print("total params:", sum(p.numel() for p in model.parameters()))
22

Bài tập

Bài 1 — Load và inspect. Chọn một model size nhỏ (Qwen/Qwen2.5-0.5B, microsoft/Phi-3.5-mini-instruct, hoặc HuggingFaceTB/SmolLM2-360M-Instruct). Load bằng AutoModelForCausalLM. In: model_type, hidden_size, num_hidden_layers, num_attention_heads, vocab_size, max_position_embeddings, tổng số tham số.

Bài 2 — Encode batch. Encode 3 câu sau với padding=True, truncation=True, max_length=64: "Học AI thật thú vị.", "Transformers giúp xử lý ngôn ngữ.", "Một câu rất ngắn.". In shape của input_idsattention_mask. Decode lại 3 câu bằng batch_decode, so sánh với gốc.

Bài 3 — Chat template. Tạo list 5 messages xen kẽ system / user / assistant / user / assistant. Apply chat template với cả add_generation_prompt=TrueFalse. Quan sát khác biệt giữa hai chuỗi kết quả, ghi lại token đặc biệt mà template của model bạn dùng (Llama, Qwen, Phi mỗi loại khác nhau).

Bài 4 — Embedding 1 câu. Dùng AutoModel với bert-base-uncased hoặc sentence-transformers/all-MiniLM-L6-v2. Encode câu "AI engineering is exciting.", lấy last_hidden_state, mean-pool theo attention mask. In shape vector kết quả. Sau đó tính cosine similarity với câu "Machine learning is fun." — kỳ vọng giá trị tương đối cao.

Bài 5 — Custom forward. Load 1 model causal LM nhỏ. Encode "The capital of France is". Forward (không dùng generate), lấy logits[:, -1, :], in top-5 token có logit cao nhất bằng torch.topk + tokenizer.decode. Kiểm tra "Paris" có nằm trong top không.

23

Tóm tắt

  • Auto class = factory tự nhận kiến trúc từ config.json, giúp code portable giữa các model.
  • Chọn Auto class theo task: AutoModelForCausalLM cho generation, AutoModelForSeq2SeqLM cho seq2seq, AutoModelForSequenceClassification cho classification, AutoModelForMaskedLM cho BERT, AutoModelForCTC cho ASR, AutoModel cho embedding.
  • Workflow 3 bước: from_pretrained → tokenize → forward / generate.
  • Tokenizer có 4 method chính: tokenize, encode, decode, __call__; production chủ yếu dùng __call__.
  • Output tokenizer là dict với input_ids, attention_mask (đôi khi token_type_ids).
  • padding=True + truncation=True + max_length là combo chuẩn cho batch.
  • Special tokens: bos, eos, pad, unk; GPT-2 / Llama base không có pad_token mặc định.
  • Chat template (apply_chat_template) là cách an toàn nhất để format hội thoại cho model instruct.
  • Tham số from_pretrained quan trọng: revision, torch_dtype, device_map, trust_remote_code, low_cpu_mem_usage.
  • Inspect model: print(model), model.config, đếm param qua parameters().
  • Custom forward (model.eval() + torch.no_grad()) cho control thấp cấp; output là ModelOutput với logits / last_hidden_state / hidden_states / attentions.
  • save_pretrained + from_pretrained local — pin model cho production.
  • Pipeline cho prototype, AutoModel cho production và fine-tune.
  • Pitfall hay gặp: thiếu pad_token, quên eval(), viết tay chat format, bật trust_remote_code bừa, load fp32 OOM.
  • Bài 19 chuyển sang chạy SLM local với cấu hình thực tế (Phi-3.5, Llama-3.2-1B, Qwen 2.5 1.5B).