Tokenization
Tokenization - Tách từ
Câu 'Tôi yêu Việt Nam' có bao nhiêu 'mảnh' khi máy tính cắt theo khoảng trắng?
Liên tưởng 1 — Cắt bánh chưng mời khách: Bạn có thể cắt một chiếc bánh chưng thành miếng to (4 miếng cho cả nhà), miếng nhỏ (16 miếng cho khách đến chúc Tết), hoặc xắt vụn để chấm muối vừng. Mỗi cách cắt phục vụ mục đích khác nhau. Tokenization cũng vậy: word-level (miếng to), subword (miếng vừa), character-level (xắt vụn) — không có cách "đúng" tuyệt đối, chỉ có lựa chọn phù hợp với ứng dụng.
Liên tưởng 2 — Người đọc báo đọc nhanh: Khi đọc báo tiếng Việt, bạn không đánh vần từng chữ cái. Não bạn tự động nhóm các ký tự thành từ ("phở", "bún chả"). LLM cũng cần bước nhóm này — nếu nhìn từng ký tự, chuỗi quá dài và "ngữ nghĩa" bị loãng. Tokenization là bước "nhóm mắt" của máy tính.
Liên tưởng 3 — Tiếng Việt ghép từ ("Việt Nam", "máy tính"): Trong tiếng Việt, một "từ" thực sự có thể gồm nhiều "tiếng" (từ ghép). Máy tính không biết "Hà Nội" là 1 khái niệm hay 2 — bài toán tách từ tiếng Việt riêng đã có hẳn công cụ như VnCoreNLP, RDRSegmenter. Điều này khiến tokenization tiếng Việt khó hơn tiếng Anh đáng kể.
Liên tưởng 4 — Chi phí API tính theo token: Khi bạn trả tiền OpenAI cho mỗi request, bill tính bằng token — không phải ký tự, không phải từ. Một tin nhắn 500 ký tự tiếng Việt có thể là 300-700 token tuỳ tokenizer. Hiểu tokenization là hiểu cách tối ưu chi phí LLM của bạn.
Bạn vừa thấy cách cắt đơn giản nhất. Nhưng có nhiều cách cắt khác nhau — mỗi cách cho kết quả khác nhau đáng ngạc nhiên. Hãy thử cả ba cách bên dưới!
Hình minh họa
Số token
4
Phương pháp
Word
Tách theo khoảng trắng. Đơn giản nhưng không xử lý được từ mới (OOV).
Tokenization là bước chia văn bản thành các mảnh nhỏ (token) để máy tính xử lý được. Không có cách tách nào hoàn hảo — mỗi cách là một sự đánh đổi giữa kích thước từ vựng và độ dài chuỗi!
Giống như cắt phở: cắt sợi to thì nhanh nhưng khó ăn, cắt sợi nhỏ thì nhiều mảnh hơn nhưng vừa miệng hơn.
Từ 'Tokenization' chưa có trong từ vựng. Phương pháp nào xử lý được mà KHÔNG cần thêm từ mới?
Khi bạn gọi LLM qua API với max_tokens=500, giới hạn 500 áp dụng cho gì?
Team của bạn fine-tune một Transformer trên corpus tiếng Việt 100GB. Sau đó nhảy thẳng sang dùng tokenizer của LLaMA (train trên tiếng Anh). Điều gì có khả năng xảy ra?
Giải thích
Tokenization là bước đầu tiên và quan trọng nhất trong xử lý ngôn ngữ tự nhiên (NLP). Nó chuyển văn bản thô thành chuỗi các đơn vị rời rạc mà mô hình có thể hiểu được. Các mô hình hiện đại như GPT (dùng BPE) và BERT (dùng WordPiece) phụ thuộc hoàn toàn vào bước này. Xem thêm so sánh tokenizer để hiểu sự khác biệt giữa các thuật toán.
1. Word-level (theo từ): Chia theo khoảng trắng/dấu câu. Từ vựng lớn, gặp vấn đề OOV (out-of-vocabulary) với từ mới.
2. Character-level (theo ký tự): Mỗi ký tự là 1 token. Từ vựng nhỏ (~100), nhưng chuỗi rất dài, mô hình khó học ngữ nghĩa.
3. Subword (theo từ phụ): Kết hợp ưu điểm cả hai. Từ vựng vừa phải (~30K-50K), xử lý được OOV. Đây là chuẩn hiện đại.
BPE bắt đầu từ các ký tự đơn, rồi lặp lại ghép cặp xuất hiện nhiều nhất. Ví dụ: nếu "e" và "s" thường đi cùng nhau, BPE sẽ tạo token "es".
Lặp cho đến khi đạt kích thước từ vựng mong muốn (GPT-4 dùng ~100K token).
from transformers import AutoTokenizer
# Thử tokenize tiếng Việt với GPT-2 tokenizer (BPE)
tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "Tôi yêu Việt Nam"
tokens = tokenizer.tokenize(text)
print(f"Tokens: {tokens}")
# Tokens: ['T', 'ôi', ' yêu', ' Việt', ' Nam']
ids = tokenizer.encode(text)
print(f"Token IDs: {ids}")
# Token IDs: [51, 12762, 331, 49, 7402]
# Mỗi token được gán một số ID duy nhất
# Mô hình chỉ nhìn thấy chuỗi số, không phải chữ!Tiếng Việt là ngôn ngữ đơn lập (isolating language) — ranh giới từ phức tạp hơn tiếng Anh. Ví dụ "Việt Nam" là 1 từ nhưng 2 token khi tách theo khoảng trắng. Các công cụ như VnCoreNLP và RDRSegmenter giúp tách từ tiếng Việt chính xác hơn.
from transformers import AutoTokenizer
text = "Tokenization là nền tảng của NLP hiện đại."
# GPT-2 / GPT-4 — dùng BPE
gpt = AutoTokenizer.from_pretrained("gpt2")
print("GPT BPE:", gpt.tokenize(text))
# ['Token', 'ization', 'Ġl', 'à', 'Ġn', 'á»\x81n', 'Ġt', 'á»\x91ng', ...]
# Ký tự Ġ = space mở đầu token
# BERT multilingual — dùng WordPiece
bert = AutoTokenizer.from_pretrained("bert-base-multilingual-cased")
print("BERT WP:", bert.tokenize(text))
# ['Token', '##ization', 'là', 'nền', 'tảng', 'của', ...]
# Ký hiệu ## = sub-token tiếp theo
# PhoBERT — BPE tinh chỉnh cho tiếng Việt
from pyvi import ViTokenizer
segmented = ViTokenizer.tokenize(text) # Nối từ ghép trước
pho = AutoTokenizer.from_pretrained("vinai/phobert-base", use_fast=False)
print("PhoBERT:", pho.tokenize(segmented))
# Ít token hơn đáng kể vì tokenizer train riêng cho tiếng Việt
# Đếm token — thường dùng để ước lượng chi phí API
print(f"GPT tokens: {len(gpt.tokenize(text))}")
print(f"BERT tokens: {len(bert.tokenize(text))}")
print(f"PhoBERT tokens:{len(pho.tokenize(segmented))}")AutoTokenizer.from_pretrained(MODEL_NAME) cùng checkpoint với model để đảm bảo đồng bộ. Khi fine-tune, nhớ lưu cả tokenizer cùng model.Đo chi phí token — bài toán thực tế với LLM tiếng Việt: Một chatbot CSKH tiếng Việt gọi GPT-4 với context 2.500 ký tự mỗi lượt (system prompt + lịch sử + câu hỏi). Với tokenizer GPT-4, dự kiến: ~2.000 token input + ~400 token output = ~2.400 token/lượt. Giá $0.03/1K input + $0.06/1K output ≈ $0.084/lượt ≈ 2.100 VNĐ/lượt. Nếu team chuyển sang PhoBERT-based tokenizer thông qua một model tiếng Việt riêng, chi phí có thể giảm 40-60%.
Token đặc biệt (special tokens): Mỗi tokenizer thường kèm theo các token đặc biệt không có trong văn bản thường:
[CLS]/[SEP](BERT) — mở đầu chuỗi và phân tách câu.[MASK](BERT) — vị trí cần dự đoán trong masked language modeling.[PAD]— padding để đủ chiều dài batch (batch cần đồng đều chiều dài).<|endoftext|>(GPT-2) — đánh dấu kết thúc document. GPT-4 dùng<|im_start|>,<|im_end|>cho chat template.<s>/</s>(LLaMA, T5) — tương ứng BOS (beginning of sequence) và EOS (end of sequence).
Tiếng Việt có các đặc thù khiến tokenization "chuẩn mặc định" cho tiếng Anh hoạt động kém:
- Dấu phụ (diacritics): Tiếng Việt có 11 nguyên âm có dấu (á à ả ã ạ â ă …). Nếu tokenizer không học đủ, mỗi dấu phụ có thể tách thành 2-3 byte. Ví dụ "ế" = 3 byte UTF-8, BPE mô hình tiếng Anh có thể tách thành 2-3 token riêng.
- Từ ghép (compound words): "Hà Nội", "máy tính", "đại học" là 1 khái niệm nhưng tách theo space sẽ thành 2 từ. Công cụ VnCoreNLP/RDRSegmenter nối thành "Hà_Nội" trước khi tokenize.
- Tiếng Việt không dùng chữ hoa nghiêm ngặt: Khác tiếng Anh, chữ cái đầu tên người/địa danh không luôn viết hoa. Tokenizer case-sensitive dễ tách thừa.
- Nhiều từ Hán-Việt: "phân phối", "xác suất", "tích phân" là các từ gốc Hán — nếu BPE không có corpus tiếng Việt đủ lớn, chúng bị chẻ vụn.
Thí nghiệm đếm token cho câu "Tôi đang học xác suất thống kê tại Đại học Bách Khoa Hà Nội":
| Tokenizer | Số token | Ghi chú |
|---|---|---|
| GPT-2 BPE | ~38 | Chẻ dấu tiếng Việt thành byte |
| GPT-4 cl100k_base | ~22 | Cải thiện đáng kể, vẫn thua PhoBERT |
| mBERT WordPiece | ~18 | Multilingual — khá hợp lý |
| PhoBERT BPE | ~13 | Train riêng cho tiếng Việt, giữ từ ghép |
| ViT5 SentencePiece | ~14 | SentencePiece cho sinh text tiếng Việt |
(Số liệu xấp xỉ — chạy thực tế sẽ dao động ±2 token tuỳ phiên bản.)
Ưu điểm: Đơn giản, trực quan, giữ nguyên nghĩa từng từ.
Nhược điểm: Từ vựng rất lớn (hàng trăm nghìn), không xử lý được từ mới, lỗi chính tả.
Ví dụ: "Tôi" "yêu" "Việt" "Nam" = 4 token. Nếu gặp "Tokenization" lần đầu → không biết xử lý!
- Tokenization là bước ĐẦU TIÊN trong NLP — chia văn bản thành token để máy tính xử lý.
- Word-level: đơn giản nhưng từ vựng lớn, không xử lý được từ mới (OOV).
- Character-level: từ vựng nhỏ, không OOV, nhưng chuỗi quá dài và mất ngữ nghĩa.
- Subword (BPE/WordPiece): chuẩn hiện đại — GPT dùng BPE, BERT dùng WordPiece.
- Tiếng Việt cần tách từ đặc biệt vì ranh giới từ phức tạp (VD: 'Việt Nam' = 1 từ, 2 token).
- Mỗi mô hình có tokenizer riêng; chi phí API tính theo token nên tiếng Việt thường đắt hơn tiếng Anh 1.5-3×.
Kiểm tra hiểu biết
Tại sao tokenization theo từ phụ (subword) lại phổ biến hơn tách theo từ nguyên vẹn?