Trực quan hóa cách VLMs hoạt động
Trực quan hóa cách VLMs hoạt động
- 10 min read
Hình dung cách các Mô hình Ngôn ngữ Thị giác (VLM) hoạt động
Đây là bài đăng blog của Hafedh Hichri trên Hugging Face.
Giới thiệu
Các Mô hình Ngôn ngữ Thị giác (VLM) là các mô hình AI tự hồi quy xử lý cả văn bản và hình ảnh làm đầu vào. Trong bài đăng này, chúng ta sẽ xem xét kỹ cách các VLM như Idefics3 và SmolVLM hoạt động bên trong, khám phá cách chúng kết hợp thông tin thị giác và văn bản để tạo ra đầu ra mạch lạc.
Chúng ta sẽ sử dụng HuggingFaceTB/SmolVLM-256M-Instruct làm mô hình tham chiếu trong bài đăng blog này.
Bộ xử lý (Processor)
Bộ xử lý chuẩn bị dữ liệu văn bản và hình ảnh thành một định dạng thống nhất phù hợp cho mô hình. Đối với hình ảnh, nó thực hiện một chuỗi các phép biến đổi trước khi chuyển chúng thành các biểu diễn giống như token mà mô hình có thể hiểu được.
Quy trình xử lý hình ảnh có thể được hình dung như sau:
Một bước quan trọng trong quy trình này là chia nhỏ hình ảnh, như đã thấy trong đoạn mã sau. Mỗi hình ảnh được chia thành các phần nhỏ hơn (hoặc “lát cắt”), sau đó được mã hóa riêng lẻ.
Khi văn bản đi kèm với hình ảnh, mỗi hình ảnh trước tiên được biểu diễn bằng một token <image> trong chuỗi văn bản. Tùy thuộc vào số lượng lát cắt, mỗi hình ảnh sau đó được mở rộng thành: $64 \times \text{số_lượng_lát cắt}$
Hằng số 64 đến từ mối quan hệ: $$ \frac{512^2 / 16^2}{4^2} = 64 $$
Chúng ta sẽ quay lại công thức này sau, nhưng hiện tại, hãy coi nó như sau:
Mỗi lát cắt hình ảnh được biểu diễn bằng 64 token.
Bộ xử lý văn bản (Text Processor)
Khi hình ảnh được bao gồm, văn bản cũng phải phản ánh điều đó bằng cách sử dụng trình giữ chỗ <image>. Bộ xử lý thực hiện các bước sau:
- Chèn trình giữ chỗ – Mỗi hình ảnh trong đầu vào được biểu diễn bằng một token
<image>trong văn bản. - Đếm số lát cắt – Bộ xử lý xác định số lượng lát cắt mà mỗi hình ảnh có sau khi tiền xử lý.
- Mở rộng token – Mỗi token
<image>sau đó được thay thế (hoặc “mở rộng”) thành một chuỗi dựa trên số lượng lát cắt hình ảnh, theo công thức dưới đây.
import torch
from PIL import Image
from transformers import AutoProcessor, AutoModelForVision2Seq
from transformers.image_utils import load_image
# Khởi tạo bộ xử lý và mô hình
processor = AutoProcessor.from_pretrained("HuggingFaceTB/SmolVLM-256M-Instruct")
model = AutoModelForVision2Seq.from_pretrained("HuggingFaceTB/SmolVLM-256M-Instruct")
# Tải ảnh
image = load_image("https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg")
# Tạo tin nhắn đầu vào
messages = [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": "Can you describe this image?"}
]
},
]
prompt = processor.apply_chat_template(messages, add_generation_prompt=True)
inputs = processor(text=prompt, images=[image], return_tensors="pt")
out = processor.decode(inputs["input_ids"][0])
print(out.replace("<image>", ".")) # in toàn bộ <image> sẽ làm màn hình bị nặng
<|im_start|>User:.<row_1_col_1>.................................................................<row_1_col_2>.................................................................<row_1_col_3>.................................................................<row_1_col_4>................................................................
.<row_2_col_1>.................................................................<row_2_col_2>.................................................................<row_2_col_3>.................................................................<row_2_col_4>................................................................
.<row_3_col_1>.................................................................<row_3_col_2>.................................................................<row_3_col_3>.................................................................<row_3_col_4>................................................................
.<global-img>................................................................Can you describe this image?<end_of_utterance>
Assistant:
Chuẩn bị Dữ liệu (Data Preparation)
Trước khi được đưa vào mô hình, cả dữ liệu hình ảnh và văn bản đều được chuẩn bị và căn chỉnh. Giống như hầu hết các mô hình tự hồi quy, chuỗi đầu ra được dịch sang phải để dạy mô hình cách dự đoán token tiếp theo dựa trên tất cả các token trước đó.
Tuy nhiên, vì các biểu diễn hình ảnh không thể dự đoán trực tiếp, chúng được che bằng token <pad>.
Điều này ngăn mô hình tính toán tổn thất trên các token không phải văn bản (thị giác).
Kiến trúc Mô hình (Model Architecture)
Lớp Embedding (Embedding Layer)
Từ góc độ cấp cao, SmolVLM bao gồm năm thành phần chính (được minh họa ở bên phải). Việc xử lý văn bản bắt đầu với prompt, được token hóa và truyền qua lớp embedding. Lớp này biến đổi các token rời rạc thành các biểu diễn vector chiều cao, tạo ra một tensor có hình dạng: $$ [\text{sequence_length}, 576] $$ Tensor này giờ đây mã hóa biểu diễn vector của văn bản đầu vào và nó đóng vai trò là nền tảng cho tất cả các phép tính tiếp theo, lưu ý rằng tensor này đã có 13 token giữ chỗ $\times$ 64 cho hình ảnh được xử lý sắp tới (trong đó 13 là số lát cắt hình ảnh).
Mô hình Thị giác (Vision Model)
-
Embedding Patch (Patch Embedding)
Đối với nhánh thị giác, tensor đầu vào có hình dạng
[số\_lát\_cắt, số\_kênh, chiều\_cao, chiều\_rộng]. Ví dụ, đối với một hình ảnh có thể được chia thành 13 lát cắt của hình ảnh RGB được biểu diễn dưới dạng[13, 3, 512, 512].Lớp patch embedding chuyển đổi tensor này thành một chuỗi các token thị giác, làm cho nó tương thích với kiến trúc Transformer. Nó thực hiện điều này bằng cách chia hình ảnh thành các patch nhỏ, không chồng chéo và chiếu từng patch vào một không gian vector chiều cao, nguyên tắc cốt lõi đằng sau điều này là điều chỉnh kênh RGB thành một chiều embedding và mở rộng 3 $\rightarrow$ 768.
Hoạt động này được triển khai dưới dạng tích chập 2D, trong đó
kernel_sizevàstrideđều được đặt thành kích thước patch. Điều này đảm bảo mỗi cửa sổ tích chập xử lý một patch mà không bị chồng chéo:self.patch_embedding = nn.Conv2d( in_channels=config.num_channels, # 3 (kênh RGB) out_channels=self.embed_dim, # 768 kernel_size=self.patch_size, # 16 stride=self.patch_size, # 16 padding="valid", )Khi áp dụng cho
[13, 3, 512, 512], quá trình chuyển đổi diễn ra như sau:</thead><tbody><tr>Tensor kết quả
[13, 1024, 768]biểu diễn mỗi hình ảnh dưới dạng một chuỗi gồm 1024 patch được nhúng, sẵn sàng để xử lý cùng với các embedding văn bản trong Transformer.
-
Bộ mã hóa vị trí (Positional Encoder)
Bộ mã hóa vị trí đưa thông tin về thứ tự patch và bố cục không gian, giống như cách mã hóa vị trí hoạt động đối với các từ trong mô hình văn bản. Vì transformers không có ý thức về thứ tự vốn có, các mã hóa này cho phép mô hình hiểu vị trí của mỗi patch trong hình ảnh.
-
Bộ mã hóa (Encoder)
Bộ mã hóa hoạt động một cách đơn giản:
- Lớp Attention Đa đầu (MHA) nắm bắt mối quan hệ giữa các patch hình ảnh, cho phép mô hình suy luận về sự phụ thuộc không gian.
- Sau đó, đầu ra được truyền qua mạng truyền tiếp (MLP) để tinh chỉnh và chiếu các đặc trưng.
Việc triển khai attention này gần giống với cách BERT hoạt động. Điều quan trọng cần lưu ý là đầu ra của mô hình thị giác là
[13,1024,768]với ví dụ đầu vào chúng ta đang sử dụng.
Bộ kết nối (Connector)
Bộ kết nối đóng vai trò là cầu nối giữa bộ mã hóa thị giác và mô hình ngôn ngữ, đảm bảo cả hai phương thức đều chia sẻ không gian embedding tương thích. Hai chức năng chính của nó là:
- Nén đầu ra thị giác (giảm số lượng token).
- Chuyển đổi chiều embedding để khớp với embedding văn bản.
-
Hoán vị Pixel (Pixel Shuffle)
Hoạt động hoán vị pixel nén chiều không gian của các đặc trưng thị giác trong khi vẫn giữ nguyên các mối quan hệ không gian quan trọng. Trên thực tế, nó giảm số lượng token hình ảnh từ 1024 $\rightarrow$ 64, làm giảm đáng kể độ dài chuỗi trong khi vẫn duy trì sự phong phú về biểu diễn.
Quá trình chuyển đổi này diễn ra như sau:
# (chiều cao và chiều rộng lát cắt) → [số_lát_cắt, H, W, C] # (áp dụng phép biến đổi trên chiều rộng) → [số_lát_cắt, H, W/scale, C×scale] # (hoán vị các chiều) → [số_lát_cắt, W/scale, H, C×scale] # (áp dụng phép biến đổi trên chiều cao) → [số_lát_cắt, W/scale, H/scale, C×scale²] # (hoán vị lại các chiều) → [số_lát_cắt, H/scale, W/scale, C×scale²] # (hợp nhất chiều cao và chiều rộng) → [số_lát_cắt, (H/scale)×(W/scale), C×scale²]Bằng cách này, chúng ta nhận được một tensor đầu ra cuối cùng là: $$ [\text{số_lát_cắt}, \text{H’} / \text{scale} \times \text{W’} / \text{scale}, \text{C} \times \text{scale}^2 ] $$ kết quả là một chiều đầu ra bằng $$ [13, 1024/4^2, 768 * 4^2] \rightarrow [13, 64, 12288] $$ Bằng cách hoán vị và nhóm lại các pixel một cách có hệ thống, hoạt động này nén cả chiều cao và chiều rộng, đồng thời duy trì thứ tự không gian của thông tin thị giác theo mỗi hướng. Kết quả là một tensor nhỏ gọn hơn, bảo tồn các đặc điểm hình ảnh thiết yếu.
-
Chiếu xạ Modality (Modality Projection)
Bộ kết nối cũng áp dụng một lớp chiếu xạ_modality, là một phép biến đổi tuyến tính để khớp với
embedding_dimensioncủa các token văn bản. Bằng cách này, chúng ta nhận được $$ [13, 64, 12288] \rightarrow [13, 64, 576] $$
Bộ hợp nhất Đầu vào (Input Merger)
Bộ hợp nhất đầu vào là một lớp không thể học được, và thực chất là một hàm chịu trách nhiệm tích hợp các embedding thị giác và văn bản vào một chuỗi đầu vào duy nhất.
Nó thay thế mỗi token giữ chỗ <image> trong chuỗi văn bản bằng các embedding thị giác tương ứng được tạo ra bởi bộ kết nối.
Trên thực tế, bước này quét các ID token cho các token <image> và thay thế chúng bằng các biểu diễn hình ảnh đã được xử lý trước của chúng. Bước này hợp nhất hiệu quả cả hai phương thức thành một tensor liên tục có thể được đưa trực tiếp vào bộ giải mã.
Bộ giải mã (Decoder)
Bộ giải mã hoạt động tương tự như bộ giải mã của một mô hình ngôn ngữ tự hồi quy truyền thống. Nó bao gồm các lớp Attention Đa đầu (MHA) bị che được xếp chồng lên nhau, theo sau là một đầu Mô hình Ngôn ngữ (LM).
- MHA Bị che đảm bảo rằng mỗi token chỉ có thể chú ý đến các token trước đó (bao gồm cả token thị giác), duy trì tính nhân quả trong quá trình tạo văn bản.
- Đầu LM ánh xạ các trạng thái ẩn của bộ giải mã trở lại các logit từ vựng, cho phép mô hình dự đoán token tiếp theo.
Điều này cho phép VLM tạo ra các đầu ra đa phương thức mạch lạc, liên kết các dự đoán văn bản với ngữ cảnh thị giác.
Điều quan trọng cần ghi nhớ ở đây là vì chúng ta không có cách nào để xác định đầu ra cho các token hình ảnh, chúng ta sử dụng một token pad trong mục tiêu để bỏ qua việc tính toán tổn thất trên đó.
Kết luận
Trong bài đăng này, chúng ta đã khám phá các chức năng bên trong của Mô hình Ngôn ngữ Thị giác (VLM) như SmolVLM, phân tích cách chúng xử lý và tích hợp dữ liệu đa phương thức từ pixel thô và văn bản đến các đầu ra mạch lạc, có căn cứ.
Dưới đây là tóm tắt nhanh về từng giai đoạn:
- Bộ xử lý: Chuẩn bị và căn chỉnh đầu vào văn bản và hình ảnh thô.
- Mô hình Thị giác: Chuyển đổi dữ liệu pixel thành các embedding patch chiều cao.
- Bộ kết nối: Nén và chiếu các đặc trưng thị giác vào cùng không gian embedding như token văn bản.
- Bộ hợp nhất Đầu vào: Thay thế các token giữ chỗ bằng embedding thị giác để tạo thành một chuỗi đa phương thức thống nhất.
- Bộ giải mã: Tạo văn bản theo ngữ cảnh bằng cách chú ý đến cả thông tin thị giác và văn bản.
Về bản chất, VLM không chỉ nhìn và đọc, chúng suy luận trên các phương thức. Kiến trúc này cho phép chúng xử lý nhiều hình ảnh, prompt chỉ bằng văn bản hoặc thậm chí đầu vào chỉ bằng hình ảnh, làm cho SmolVLM trở thành một nền tảng linh hoạt và mạnh mẽ cho nhiều ứng dụng đa phương thức.
Cộng đồng
Link bài viết gốc
- Tags:
- Ai
- Oct 7
- Huggingface.co