Đề dẫn.
❶. Word-level
Tokenizer
❷. Character-level
Tokenizer
❸. Byte-pair
encoding
❹. BBPE
(Byte-level Byte Pair Encoding) Tokenizer
❺. Tiktokenizer
❻. Multimodal
[image, video, audio] token?
❼. Context
Window
🤔. Suy ngẫm chậm.
~
Để giúp anh/chị
quyết định có đọc tiếp hay không, tôi xin phép cung cấp các thông tin liên quan
đến bài post này như sau:
·
Chủ
đề: Machine Learning
·
Tính
thời sự: tháng 10/2024
·
Thời
gian đọc: 8 phút, kể cả
thời gian uống cà phê (uống cà phê xong là đọc xong)
-
Đề dẫn.
Tôi tin rằng
anh/chị đã quá quen thuộc với LLM (Large Language Model) như ChatGPT
(OpenAI), Gemini (Google), Claude (Anthropic), … Việc sử dụng chúng cũng vô
cùng đơn giản, chỉ cần đặt một câu truy vấn là chúng hồi đáp:
[Câu truy vấn] ⇨ [LLM] ⇨ [Hồi đáp]
[Câu truy vấn]
và [Hồi đáp] thường là văn bản đơn thuần. Câu hỏi tò mò đặt ra với chúng ta là
mạng nơ-ron (Neural Network – viết tắt là NN) xử lý đầu vào trực tiếp từ
văn bản hay phải thông qua một biến đổi nào trước khi xử lý? Câu trả lời là có
biến đổi văn bản thành mã với tên gọi là token trước khi chạy mạng nơ-ron. Có
thể hình dung một cách nôm na như sau:
[Câu truy vấn] ⇨ [«chuỗi token» ➙ ⦃NN⦄ ➙
«chuỗi token»] ⇨ [Hồi đáp]
Trong sơ đồ
trên:
·
Văn
bản của [Câu truy vấn] được mã hóa thành chuỗi token
·
Chuỗi
token là đầu vào của mạng nơ-ron (ký hiệu ⦃NN⦄)
·
Đầu
ra của mạng nơ ron là một chuỗi token khác
·
Giải
mã chuỗi token đầu ra thành văn bản [Hồi đáp]
-
Về vấn đề từ nguyên
(etymology), tôi cũng đã hỏi “ý kiến” của các chatbot về cách dịch từ “token”
ra tiếng Việt. Chúng “đồng loạt” khuyên là nên giữ nguyên từ token như là một từ
“nhập khẩu” từ tiếng Anh và “đồng hóa” từ này thành từ tiếng Việt. Vì vậy,
trong khuôn khổ bài đàm luận này, tôi xin nghe theo lời khuyên của chúng.
-
Bài post này có
cấu trúc như sau:
❶. Word-level
Tokenizer: nguyên lý việc mã hóa “từ” → token
❷. Character-level
Tokenizer: nguyên lý việc mã hóa “ký tự” → token
❸. Byte-pair
encoding: nguyên lý nén chuỗi ký tự → token
❹. BBPE
(Byte-level Byte Pair Encoding) Tokenizer: nguyên lý nén chuỗi byte → token
❺. Tiktokenizer: trải nghiệm biến đổi văn bản
thành token.
❻. Multimodal
[image, video, audio] token?: một số phương pháp mã hóa đa phương thức [ảnh,
video, âm thanh] → token
❼. Context
Window: RAM của LLM
---
❶. Word-level Tokenizer
Trong thời kỳ đầu
của các nghiên cứu NLP (Natural Language Processing) – trước năm 2018,
các mô hình mạng nơ-ron (Neural Network) thường dùng phương pháp mã hóa
Word-level Tokenizer, ví dụ: Word2Vec (Mikolov et al., 2013), GloVe
(Pennington et al., 2014),
ELMo (Peters et al., 2018).
Ý tưởng là chia
đoạn văn bản thành tổ hợp của các từ. Ví dụ đoạn văn bản trong tiếng Anh:
“Hello World!” được tách thành chuỗi các từ [“Hello”, “World”, “!”]. Hay đoạn
văn bản trong tiếng Việt: “Trăm năm trong cõi người ta” được tách thành chuỗi
các từ [“Trăm”, “năm”, “trong”, “cõi”, “người”, “ta”]. Mỗi một từ ứng với một
mã số nguyên. Khi lập trình thì đây là một dạng từ điển, có thể tra cứu hai chiều:
cho một từ sẽ tìm được mã của từ đấy; cho một mã sẽ tìm ra được từ tương ứng. Các
từ được tách ra thuộc bộ từ điển
Các từ được
tách ra thuộc một bộ từ vựng nào đấy (Vocabulary – viết tắt là Vocab). Ví dụ:
tiếng Anh thì Vocab khoảng hơn 1 triệu từ vựng; tiếng Việt: theo “Từ điển tiếng
Việt” của Hoàng Phê (Viện Ngôn ngữ) khoảng 50 nghìn (thực tế trong sách báo con
số này khoảng 80 nghìn). Nếu tính tất cả các ngôn ngữ trên thế giới thì Vocab của
tất cả các ngôn ngữ có số lượng từ vựng khổng lồ!
Các vấn đề gặp
phải với phương pháp mã hóa Word-level Tokenizer:
·
OOV
(Out of Vocabulary – Không có trong từ vựng): Tình huống xảy ra ở pha inference
của mô hình. Mô hình lúc này sẽ không xử lý được khi gặp một từ không nằm trong
bộ từ điển lúc huấn luyện (training).
·
Kích
cỡ Vocab quá lớn: Tốn nhiều bộ nhớ và kém hiệu quả tại hàm đầu ra softmax.
·
Khó
tổng quát hóa: Các biến hình (metamorph) của từ (số nhiều, viết hoa/thường)
cần được xử lý riêng biệt, dẫn đến kích cỡ Vocab tăng.
·
Kém
linh hoạt khi xử lý đa ngôn ngữ: một số ngôn ngữ (như tiếng Trung, tiếng Nhật,
tiếng Thái, tiếng Lào, tiếng Khmer) người ta có thể viết câu thành một mạch liền
các từ, không có dấu cách. Trong trường hợp đó, thuật toán trên biến toàn bộ
câu thành 1 từ.
-
❷. Character-level Tokenizer
Để khắc phục
nhược điểm nói trên, một cách tự nhiên người ta nghĩ đến cách chia nhỏ đoạn văn
bản thành chuỗi ký tự. Cách này có tên gọi là Character-level Tokenizer. Ví dụ
đoạn văn bản trong tiếng Anh: “Hello World!” được tách thành chuỗi [‘H’,’e’,
‘l’, ‘l’, ‘o’, ‘ ‘, ‘W’, ‘o’, ‘r’, ‘l’, ‘d’, ‘!’]. Các ký tự được lấy từ bộ mã
Unicode nên phủ hết tất cả các ngôn ngữ trên thế giới.
Character-level
Tokenizer thường được sử dụng trong một số ngữ cảnh đặc biệt:
·
Xử
lý các loại văn bản “không chính thức” như tiếng lóng, viết tắt – không tuân
theo các qui tắc ngữ pháp, văn bản có nhiều lỗi chính tả, …
·
Xử
lý các kho ngữ liệu của ngôn ngữ không thông dụng, các phương ngữ hiếm gặp, …
·
Nhận
dạng ký tự (OCR), nhận dạng giọng nói (ASR). Các mô hình loại này thường chỉ cần
dữ liệu đầu vào có dung lượng nhỏ, đối tượng xử lý là tập hợp các ký tự của
ngôn ngữ tương ứng.
Tưởng chừng như
cách mã hóa này khắc phục được các nhược điểm của Word-level Tokenizer. Tuy
nhiên, không hẳn vậy. Có thể chỉ ra vài nhược điểm của giải pháp này:
·
Tổng
số các ký tự của tất cả các ngôn ngữ trên thế giới, trong phiên bản Unicode
16.0 là 292,531 (nguồn) – gần 300 nghìn. Con số này – số từ vựng của Vocab – vẫn
là lớn và kém hiệu quả trong xử lý đầu ra của mạng nơ-ron (NN).
·
Unicode
là bộ mã liên tục phát triển, các ký tự mới liên tục được bổ sung. Vì vậy, một
phiên bản cũ của Tokenizer có thể không nhận ra ký tự mới được Unicode bổ sung
nên vẫn gặp vấn đề OOV (Out of Vocabulary – Không có trong từ vựng) khi
mô hình được triển khai trong thực tế (inference).
-
❸. Byte-pair encoding
Byte-pair
encoding (còn được gọi là BPE) là một thuật toán, được Philip Gage mô tả lần đầu
tiên vào năm 1994 (nguồn), để mã hóa các chuỗi văn bản thành các chuỗi nhỏ hơn (một
dạng nén dữ liệu).
Sau đây là một
ví dụ ngắn (trên Wikipedia):
1.
Dữ
liệu đầu vào để mã hóa: aaabdaaabac
2.
Vì
cặp byte 'aa'
xuất hiện nhiều nhất, chúng ta sẽ tạo một quy tắc gộp bằng cách thay thế tất cả
các cặp này bằng một byte mới chưa có trong dữ liệu: Z = aa. Bây giờ, dữ liệu trở
thành: ZabdZabac
3.
Lặp
lại bước 2: tìm cặp xuất hiện thường xuyên nhất tiếp theo là 'ab', tạo quy tắc
gộp: Y = ab.
Giờ đây, dữ liệu là: ZYdZYac.
4.
Lặp
lại một lần nữa với quy tắc mới (đệ quy): X = ZY. Và dữ liệu hiện tại là: XdXac.
5.
Không
còn cặp nào xuất hiện nhiều hơn một lần trong dữ liệu. Vì vậy, chúng ta đã có
phiên bản nén của dữ liệu: XdXac, và có thể giải mã lại dữ liệu ban đầu bằng ba quy
tắc gộp:
X = ZY; Y = ab; Z
= aa
Chú ý từ “Byte”
ở đây có nghĩa là “Character” (ký tự) chứ không phải là byte vật lý gồm 8 bit.
Phân tích ví dụ
trên chúng ta thấy:
Vocab ban đầu
có 4 từ vựng (gồm ‘a’, ‘b’, ‘c’, d’). Sau mỗi lần thay thế, Vocab tăng thêm 1.
Đối với các văn bản có dung lượng lên đến gigabyte hay terabyte thì Vocab có thể
trở nên cực lớn – điều mà người ta không mong muốn. Vì vậy, trong thực tế người
ta giới hạn kích cỡ của Vocab ở mức tối ưu nào đó. Tức là vòng lặp thay thế sẽ
phải dừng lại khi không còn chỗ cho từ vựng mới.
Một cách tổng
quát:
- Thay thế các cặp ký tự liền kề (c1c2)
lặp nhiều lần nhất (n lần) bằng một ký tự mới (k1).
Bằng thao tác này chúng ta giảm số ký tự của văn bản bằng số lần lặp (n).
Lý do? Văn bản gốc có n cặp ký tự liền kề. Nghĩa là số ký tự của n cặp này
là 2*n. Còn số ký tự sau khi được thay thế là n. Như vậy, số ký tự giảm xuống
là (2*n)-n = n. Sau mỗi lần thay thế, người ta thêm vào một dòng trong “bảng
tra cứu” dạng k1 ⇨ c1c2. Mục đích của việc này là
để giải mã sau này.
- Thuật toán lặp lại bước này cho đến
khi không còn cặp ký tự liền kề nào lặp lại trong dãy văn bản hoặc không
còn chỗ để tạo ra ký tự mới.
3. Việc giải mã được thực hiện bằng vòng lặp
đi từ đáy “bảng tra cứu” lên đầu bảng: thay thế các ký tự (kl) bằng
các cặp (cicj).
Hạn chế của
BPE?
- Phải định nghĩa trước bảng ký tự.
- Gặp khó khăn với các ký tự Unicode
đặc biệt (emoji, dấu tiếng Việt, chữ Hán…).
- Không tốt khi xử lý đa ngôn ngữ hoặc
văn bản lạ.
-
Chú ý rằng
Unicode có nhiều cách mã hóa như UTF-8, UTF-16, UTF-32, … Để tránh bị lạc đề,
Unicode trong bài này đồng nghĩa với cách mã hóa UTF-8. (Để nói sâu hơn về
Unicode chắc cần đến một bài post khác.)
“Ký tự” trong
ngôn ngữ của Unicode được gọi là “code point”. Mỗi một ký tự mã hóa bằng UTF-8
có độ dài từ 1 đến 4 byte, tùy thuộc vào giá trị của code point (xem hình minh
họa về chuyển đổi Code point ↔ UTF-8 ở trên). 128 ký tự có code point từ 0 đến
127 (ASCII) chỉ cần 1 byte để mã hóa. 1,920 ký tự tiếp theo cần 2 byte, 61,440
ký tự tiếp theo cần 3 byte và 1,048,576 cuối cùng cần 4 byte.
-
❹. BBPE (Byte-level Byte Pair Encoding) Tokenizer
Byte-level Byte
Pair Encoding (BBPE) giống hệt BPE về thuật toán nén, chỉ khác về dữ liệu. Dữ
liệu đầu vào của BBPE là byte “thô” (giá trị nhị phân), chứ không phải là ký tự.
Ví dụ: ký tự
‘é’ là 1 ký tự, nhưng trong mã hóa UTF-8 là 2 byte: 0xc3 0xa9.
Thuật toán BBPE
rất đơn giản:
·
Chuyển
đổi chuỗi ký tự gốc thành Unicode mã hóa UTF-8 (là chuỗi các byte thô).
·
Áp
thuật toán BPE lên chuỗi đã mã hóa.
Lưu ý:
·
Vocab
ban đầu của BBPE có kích cỡ là 256 (vì 1 byte có 2^8 = 256 giá trị khác nhau).
Chú ý rằng Vocab lúc này có 256 “từ vựng”, các từ vựng này có mã từ 0 đến 255.
·
Mỗi
lần áp thuật toán BPE thì Vocab tăng thêm 1 “từ vựng”.
·
Như
vậy, mỗi lần dữ liệu được nén là một lần số “từ vựng” của Vocab có thể tăng lên
1. Rõ ràng là không thể để số từ vựng của Vocab tăng lên một cách không kiểm
soát. Người ta phải tìm một điểm “trade-off” sao cho dữ liệu không dài quá mà số
từ vựng của Vocab cũng không quá lớn.
Ý tưởng dùng
thuật toán BBPE để biến chuỗi ký tự thành token có lẽ lần đầu được đề cập tại GPT-2, mục “2.2. Input
Representation” (trang 4). Vocab của GPT-2 có số từ vựng là 50,257. Nhìn
vào size này của Vocab, chúng ta biết được rằng GPT-2 cho phép vòng lặp nén dữ
liệu có thể lên đến 50 nghìn. Sau tối đa 50 nghìn lần, dù văn bản còn có thể
nén thêm được nữa người ta cũng sẽ dừng lại.
(Vocab
ban đầu có size là 256, cộng với một ký tự đặc biệt ‘<|endoftext|>’ là
257, số vị trí còn lại – 50,000 – là dành cho các “từ vựng” mới lập sau mỗi
vòng nén dữ liệu.)
-
❺. Tiktokenizer
Tiktokenizer là
phần mềm chạy trên Web cho phép chúng ta trải nghiệm biến đổi văn bản thành
token của các chatbot thông dụng hiện nay: https://tiktokenizer.vercel.app/.
Một screenshot từ https://tiktokenizer.vercel.app/?model=gpt2
Sau khi kích hoạt
phần mềm (chạy trên Web), ở phía góc phải trên, anh/chị có thể chọn Tokenizer –
(mặc định ban đầu là gpt-4o - tên một chatbot của OpenAI). Chúng ta có thể chọn
các Tokenizer khác bằng cách mở hộp thả và click vào tên của Tokenizer cần chọn.
Khi chúng ta
đưa vào đoạn văn bản ở ô phía bên cột trái (phía dưới chữ Tiktokenizer) thì
phần mềm cho kết quả mã hóa của đoạn văn bản này bên phía cột phải. Cột phải
này có 3 ô:
·
Ô
trên cùng có tên là Token count (đếm token) báo cho chúng ta biết số
token mà mô hình chuyển đổi từ đoạn văn bản chúng ta vừa nhập vào.
·
Phía
dưới ô Token count là đoạn văn bản mã hóa. Chú ý rằng các đoạn ký tự
cùng một màu nền là tương ứng với 1 token – và token đó được liệt kê ở ô dưới.
·
Mỗi
lần chúng ta di chuột (hover) lên đoạn văn bản cùng màu nền (ở ô giữa)
thì chúng ta thấy chuỗi các “từ vựng” tương ứng được bôi nền cùng màu (selected)
nằm ở ô dưới.
Ví dụ: Lấy trải nghiệm của tôi (xem
screenshot minh họa ở trên) để anh/chị dễ theo dõi:
·
Tại
hộp thả chọn các Tokenizer (cột phải) chọn gpt2
·
Ô
dưới Tiktokenizer, nhập vào đoạn văn bản: Hello World! <|endoftext|>
·
Khi
di chuột lên cụm ký tự ‘Hello’
(nền xanh), chúng ta thấy mã 15496
được bôi nền xanh (ở ô dưới)
·
Khi
di chuột lên cụm ký tự ‘ World’
(kể cả dấu trắng ngay phía trước, nền vàng), chúng ta thấy mã 2159 được bôi nền vàng
·
Khi
di chuột lên ký tự ‘!’
(nền xanh), chúng ta thấy mã 0
được bôi nền xanh
·
Khi
di chuột lên ký tự dấu cách ‘ ’ (nền xanh), chúng ta thấy mã 220 được bôi nền xanh
·
Khi
di chuột lên cụm ký tự ‘<|endoftext|>’ (nền vàng), chúng ta thấy mã 50256 được bôi nền vàng
Tương tự như vậy,
anh/chị có thể trải nghiệm với Tokenizer cl100k_base (Tokenizer này được sử dụng cho
ChatGPT) và các Tokenizer khác.
-
❻. Multimodal [image, video, audio] token?
ChatGPT và các
chatbot khác khởi đầu với đầu vào là văn bản (text). Sau đó người ta đã tích hợp
thêm các phương thức khác như ảnh, video, âm thanh. Hẳn nhiên, muốn xử lý được
đầu vào thì dữ liệu của các phương thức đó cũng cần được mã hóa. Mã hóa như thế
nào? Đây là vấn đề khá phức tạp, ở đây tôi chỉ đề cập một cách đại ý.
Phương thức
chung là chuyển đổi các đầu vào phi văn bản (hình ảnh, video, âm thanh) thành
biểu diễn nhúng (embedding) hoặc chuỗi giống như token để
có thể xử lý được.
Hình ảnh
Cách
tiếp cận phổ biến: tiền xử lý (huấn luyện trước) mô hình thị giác (vision
model) (ví dụ: CLIP, ViT). Nghĩa là ảnh được xử lý riêng trước khi “nhúng” vào LLM:
·
[Ảnh]
→ [Vision model] → [embedding]
·
Căn
chỉnh embedding của Vision model để tương thích với embedding
của LLM (phép chiếu – project)
Video
Cách
tiếp cận phổ biến: Trích xuất khung hình (frame), rồi mã hóa khung hình
như ảnh tĩnh:
·
Trích
xuất các khung hình chính (keyframes).
·
Mỗi
khung hình được mã hóa như ảnh tĩnh.
·
Có
thể thêm xử lý theo thời gian (như TimeSformer hoặc 3D
ConvNet).
Âm thanh
·
Sóng
âm → Phổ tần số → Bộ mã hóa thị giác:
o
Chuyển
âm thanh sang phổ Mel (spectrogram).
o
Xử
lý phổ này như ảnh → dùng bộ mã hóa ảnh.
·
Mô
hình âm thanh trực tiếp:
o
Dùng
mô hình như Whisper, Wav2Vec2.0, hoặc AudioMAE để mã hóa sóng âm thành embedding.
·
Token
hóa rời rạc âm thanh:
o
Dùng
Audio Tokenizer (ví dụ: EnCodec) để biến âm thanh thành token rời rạc.
-
❼. Context window
Context
window (cửa sổ ngữ cảnh)
trong LLM là số lượng tối đa token mà mô hình có thể “nhìn thấy” hoặc xử lý
cùng lúc khi tạo sinh hoặc “hiểu” văn bản.
Điều đó có nghĩa là gì?
Context window
xác định tổng số token mà mô hình có thể tiếp nhận trong một lần hội thoại
(chat) — bao gồm cả đầu vào và các phản hồi trước đó của chính hội thoại đấy.
Take away: Đối
với mỗi chủ đề mới, khi sử dụng chatbot chúng ta nên tạo một chat mới. Đừng lười
tạo chat mới vì nếu cứ dùng một chat cho hàng loạt câu truy vấn thì một cách vô
tình chúng ta đã thu hẹp context window!
Tại sao nó quan trọng?
·
Ghi
nhớ hội thoại:
o
Context
window càng lớn, mô hình càng nhớ được nhiều thông tin (lịch sử hội thoại, lệnh,
ngữ cảnh) trong cùng một phiên làm việc.
o
Nếu
vượt quá giới hạn, các token cũ sẽ bị cắt bỏ.
·
Xử
lý văn bản dài:
o
Context
window lớn cho phép mô hình làm việc với tài liệu dài, mã lập trình hoặc cuộc hội
thoại nhiều bước.
·
Tăng
độ chính xác và mạch lạc:
o
Mô
hình hoạt động hiệu quả hơn khi có đủ ngữ cảnh liên quan để suy luận hoặc trả lời
chính xác.
Một cách hình ảnh
Hãy
tưởng tượng context window giống như bộ nhớ RAM của mô hình:
·
Bộ
nhớ nhỏ → chỉ nhớ được câu gần nhất.
·
Bộ
nhớ lớn → nhớ được toàn bộ cuộc trò chuyện hoặc tài liệu dài.
Ví dụ về context window
|
Mô hình |
Kích thước context window (Số từ tương
đương - ước tính) |
|
GPT-2 |
1.024 token (~750 từ) |
|
GPT-3 |
2.048 token (~1.500 từ) |
|
GPT-3.5 Turbo |
4.096 hoặc 16.000 token (~3.000–12.000 từ) |
|
GPT-4 Turbo |
128.000 token (~96.000 từ) |
|
Claude 3, 4 |
200.000 token (~150.000 từ) |
|
Gemini 2.5 Pro |
1 triệu token (~750.000 từ) |
|
Grok 3 |
128.000 token (~96.000 từ) |
|
Grok 4 |
256.000 token (~190.000 từ) |
|
DeepSeek V3 |
128.000 token (~96.000 từ) |
|
Qwen3 |
32.768 token (~14.000 từ) |
|
Llama 4 Scout |
10 triệu token (~7.500.000 từ) |
-
🤔. Suy ngẫm chậm.
Gần
đây, người ta nói nhiều đến một “ẩn dụ” (metaphor) có tên “AI Factory”
(Nhà máy AI):
· Đầu vào: Dữ liệu (ví dụ: văn bản, hình ảnh,
video, âm thanh)
· Xử lý: Huấn luyện mô hình, tinh chỉnh, suy luận (inference)
· Đầu ra: Dự đoán, phân loại, quyết định, nội dung, ...
Theo
cách hiểu này, "AI Factory" là việc tự động hóa và công nghiệp hóa vòng
đời phát triển AI — từ thu thập dữ liệu đến triển khai và giám sát mô hình.
Nếu
nhìn vào bên trong nhà máy AI, chúng ta sẽ thấy gì? Toàn bộ hoạt động của nhà
máy này là nhào nặn, biến đổi, xử lý các token!
«chuỗi token» ➙ ⦃NN⦄ ➙
«chuỗi token»
-
Trân trọng
& vui nhã
(credit:
Gemini 2.5 Flash)
LeVanLoi



Không có nhận xét nào:
Đăng nhận xét