Kiến trúc Transformer trong Học Đa Phương Thức: Các Chiến Lược Thích Ứng và Mô Hình Tiêu Biểu

Kiến trúc Transformer đã chứng tỏ khả năng vượt trội trong xử lý ngôn ngữ tự nhiên và gần đây đã mở rộng thành công sang lĩnh vực học đa phương thức, mang lại những bước tiến đột phá. Trong bối cảnh đa phương thức, Transformer cần xử lý dữ liệu không đồng nhất từ nhiều kênh khác nhau (thị giác, ngôn ngữ, âm thanh, v.v.), điều này đặt ra cả thách thức và cơ hội mới trong thiết kế kiến trúc.

Thích nghi kiến trúc Transformer cho Học Đa Phương Thức

Để thích nghi với môi trường đa phương thức, kiến trúc Transformer đã phát triển thành một số mô hình thiết kế chính:

Các Mô Hình Kiến Trúc Transformer Đa Phương Thức Cơ Bản

1. Kiến trúc Hợp nhất Sớm (Early Fusion)

Kiến trúc hợp nhất sớm tích hợp thông tin từ các phương thức khác nhau ngay tại lớp đầu vào, sử dụng chung một bộ mã hóa Transformer. Các ví dụ điển hình bao gồm VisualBERT và Unicoder-VL:

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

# Giả định các lớp mã hóa cơ bản
class ImageFeatureExtractor(nn.Module):
    def __init__(self, output_dim=512):
        super().__init__()
        self.conv = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1)
        self.fc = nn.Linear(32 * 4 * 4, output_dim) # Kích thước giả định sau conv
    
    def forward(self, img_batch):
        x = F.relu(self.conv(img_batch))
        x = x.view(x.size(0), -1) # Flatten
        return self.fc(x).unsqueeze(1) # Thêm chiều sequence

class TextEmbeddingLayer(nn.Module):
    def __init__(self, vocab_size, embed_dim=512):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
    
    def forward(self, text_tokens):
        return self.embedding(text_tokens)

# Giả định một khối Transformer cơ bản
class BaseTransformerBlock(nn.Module):
    def __init__(self, embed_dim, num_heads, feedforward_dim, dropout=0.1):
        super().__init__()
        self.attn = nn.MultiheadAttention(embed_dim, num_heads, dropout=dropout, batch_first=True)
        self.norm1 = nn.LayerNorm(embed_dim)
        self.feed_forward = nn.Sequential(
            nn.Linear(embed_dim, feedforward_dim),
            nn.ReLU(),
            nn.Linear(feedforward_dim, embed_dim)
        )
        self.norm2 = nn.LayerNorm(embed_dim)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, x):
        attn_output, _ = self.attn(x, x, x)
        x = self.norm1(x + self.dropout1(attn_output))
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout2(ff_output))
        return x

class UnifiedMultimodalModel(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.image_embedder = ImageFeatureExtractor(output_dim=config['embed_dim'])
        self.text_embedder = TextEmbeddingLayer(vocab_size=config['vocab_size'], embed_dim=config['embed_dim'])
        self.main_transformer = nn.Sequential(*[
            BaseTransformerBlock(config['embed_dim'], config['num_heads'], config['feedforward_dim'])
            for _ in range(config['num_layers'])
        ])
        
    def forward(self, images, text_ids):
        # Mã hóa riêng biệt đặc trưng thị giác và văn bản
        visual_representations = self.image_embedder(images)
        text_representations = self.text_embedder(text_ids)
        
        # Nối các đầu vào đa phương thức
        # Thêm một token [CLS] giả định hoặc một cách để phân biệt các phương thức
        multimodal_combined_input = torch.cat([visual_representations, text_representations], dim=1)
        
        # Xử lý bằng Transformer thống nhất
        output_features = self.main_transformer(multimodal_combined_input)
        return output_features

# Ví dụ cấu hình
# config_early = {'embed_dim': 512, 'num_heads': 8, 'feedforward_dim': 2048, 'num_layers': 6, 'vocab_size': 10000}
# model_early_fusion = UnifiedMultimodalModel(config_early)
2. Kiến trúc Hợp nhất Muộn (Late Fusion)

Kiến trúc hợp nhất muộn duy trì các bộ mã hóa Transformer độc lập cho mỗi phương thức, thực hiện việc hợp nhất đặc trưng ở các lớp cao hơn:

# Giả định các khối Transformer độc lập cho thị giác và văn bản
class VisionTransformer(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.feature_extractor = ImageFeatureExtractor(output_dim=config['embed_dim'])
        self.encoder_blocks = nn.Sequential(*[
            BaseTransformerBlock(config['embed_dim'], config['num_heads'], config['feedforward_dim'])
            for _ in range(config['num_layers'])
        ])
    def forward(self, img_input):
        features = self.feature_extractor(img_input)
        return self.encoder_blocks(features)

class LanguageTransformer(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.embedding_layer = TextEmbeddingLayer(vocab_size=config['vocab_size'], embed_dim=config['embed_dim'])
        self.encoder_blocks = nn.Sequential(*[
            BaseTransformerBlock(config['embed_dim'], config['num_heads'], config['feedforward_dim'])
            for _ in range(config['num_layers'])
        ])
    def forward(self, text_input_ids):
        embeddings = self.embedding_layer(text_input_ids)
        return self.encoder_blocks(embeddings)

class FeatureCombiner(nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.linear_combine = nn.Linear(input_dim * 2, output_dim) # Giả định 2 phương thức
    
    def forward(self, visual_reps, text_reps):
        # Có thể dùng pooling trước khi nối nếu sequence lengths khác nhau
        # Ví dụ: lấy [CLS] token hoặc global average pooling
        visual_pooled = visual_reps.mean(dim=1) 
        text_pooled = text_reps.mean(dim=1)
        combined = torch.cat([visual_pooled, text_pooled], dim=-1)
        return self.linear_combine(combined)

class SeparateEncoderFusion(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.vision_net = VisionTransformer(config)
        self.language_net = LanguageTransformer(config)
        self.combiner_module = FeatureCombiner(config['embed_dim'], config['final_output_dim'])
        
    def forward(self, image_data, text_data):
        # Mã hóa độc lập từng phương thức
        vision_encodings = self.vision_net(image_data)
        language_encodings = self.language_net(text_data)
        
        # Hợp nhất đặc trưng ở lớp cao
        fused_representations = self.combiner_module(vision_encodings, language_encodings)
        return fused_representations

# Ví dụ cấu hình
# config_late = {'embed_dim': 512, 'num_heads': 8, 'feedforward_dim': 2048, 'num_layers': 6, 'vocab_size': 10000, 'final_output_dim': 256}
# model_late_fusion = SeparateEncoderFusion(config_late)
3. Kiến trúc Chú ý Chéo (Cross-Attention)

Kiến trúc chú ý chéo cho phép tương tác sâu giữa các phương thức thông qua cơ chế chú ý chéo, với các mô hình tiêu biểu như ViLBERT và LXMERT:

(Mã ví dụ cho kiến trúc chú ý chéo sẽ được trình bày chi tiết hơn trong phần thảo luận về ViLBERT và LXMERT.)

Những Thách thức Kỹ thuật Chủ yếu và Giải pháp

Vấn đề Đồng bộ Phương thức (Modality Alignment)

Dữ liệu đa phương thức thường không đồng bộ về mặt thời gian hoặc không gian. Cơ chế tự chú ý của Transformer vốn dĩ có khả năng xử lý việc đồng bộ theo chuỗi:

class InterModalAttention(nn.Module):
    def __init__(self, feature_dim):
        super().__init__()
        self.query_mapper = nn.Linear(feature_dim, feature_dim)
        self.key_mapper = nn.Linear(feature_dim, feature_dim)
        self.value_mapper = nn.Linear(feature_dim, feature_dim)
        
    def forward(self, source_sequence, target_sequence):
        # source_sequence (ví dụ: thị giác) đóng vai trò Query
        # target_sequence (ví dụ: văn bản) đóng vai trò Key và Value
        queries = self.query_mapper(source_sequence)
        keys = self.key_mapper(target_sequence)
        values = self.value_mapper(target_sequence)
        
        # Tính trọng số chú ý chéo phương thức
        attention_scores = torch.matmul(queries, keys.transpose(-2, -1)) / math.sqrt(queries.size(-1))
        attention_weights = F.softmax(attention_scores, dim=-1)
        
        # Áp dụng chú ý
        aligned_representations = torch.matmul(attention_weights, values)
        return aligned_representations

# Ví dụ sử dụng:
# visual_tokens = torch.randn(1, 10, 512) # batch, seq_len, dim
# text_tokens = torch.randn(1, 15, 512)
# cross_attn_layer = InterModalAttention(512)
# output = cross_attn_layer(visual_tokens, text_tokens)
Xử lý Tính không đồng nhất của Phương thức (Modality Heterogeneity)

Các phương thức khác nhau có đặc tính thống kê và mức độ chi tiết ngữ nghĩa khác nhau, đòi hỏi chiến lược tiền xử lý và trích xuất đặc trưng chuyên biệt:

Loại Phương thức Phương pháp Trích xuất Đặc trưng Chiến lược Chuỗi hóa Xử lý Đặc biệt
Hình ảnh CNN/ViT Đặc trưng lưới/patch Mã hóa vị trí
Văn bản Word Embedding/Byte-Pair Encoding Chuỗi token Mã hóa đoạn văn
Âm thanh Phổ tần (Spectrogram) Khung thời gian Mã hóa tần số
Video 3D CNN/Transformer Khối không gian-thời gian Mã hóa thời gian
Tối ưu hóa Hiệu quả Tính toán

Transformer đa phương thức đối mặt với thách thức về độ phức tạp tính toán, yêu cầu các cơ chế chú ý hiệu quả:

class OptimizedMultimodalAttention(nn.Module):
    def __init__(self, embed_dim, num_heads):
        super().__init__()
        self.embed_dim = embed_dim
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads
        
        # Projections for queries, keys, values for both modalities
        self.query_proj_v = nn.Linear(embed_dim, embed_dim)
        self.key_proj_v = nn.Linear(embed_dim, embed_dim)
        self.value_proj_v = nn.Linear(embed_dim, embed_dim)
        
        self.query_proj_t = nn.Linear(embed_dim, embed_dim)
        self.key_proj_t = nn.Linear(embed_dim, embed_dim)
        self.value_proj_t = nn.Linear(embed_dim, embed_dim)
        
        self.output_proj = nn.Linear(embed_dim * 2, embed_dim) # Output combination
        
    def _split_heads(self, x):
        batch_size, seq_len, _ = x.size()
        return x.view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)

    def _combine_heads(self, x):
        batch_size, num_heads, seq_len, head_dim = x.size()
        return x.transpose(1, 2).contiguous().view(batch_size, seq_len, num_heads * head_dim)

    def _compute_attention(self, q, k, v):
        scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
        weights = F.softmax(scores, dim=-1)
        return torch.matmul(weights, v)

    def forward(self, visual_seq, text_seq):
        batch_size = visual_seq.size(0)

        # Apply projections and split into heads for visual modality
        q_v = self._split_heads(self.query_proj_v(visual_seq))
        k_v = self._split_heads(self.key_proj_v(visual_seq))
        v_v = self._split_heads(self.value_proj_v(visual_seq))

        # Apply projections and split into heads for text modality
        q_t = self._split_heads(self.query_proj_t(text_seq))
        k_t = self._split_heads(self.key_proj_t(text_seq))
        v_t = self._split_heads(self.value_proj_t(text_seq))

        # Cross-attention: visual queries attend to text keys/values
        visual_attended_to_text = self._compute_attention(q_v, k_t, v_t)
        
        # Cross-attention: text queries attend to visual keys/values
        text_attended_to_visual = self._compute_attention(q_t, k_v, v_v)
        
        # Combine heads back
        visual_attended = self._combine_heads(visual_attended_to_text)
        text_attended = self._combine_heads(text_attended_to_visual)

        # Concatenate and project for final multimodal representation
        combined_output = torch.cat([visual_attended, text_attended], dim=-1)
        final_representation = self.output_proj(combined_output)
        
        return final_representation

# Ví dụ sử dụng:
# visual_data = torch.randn(2, 50, 768) # Batch, Visual Tokens, Dim
# text_data = torch.randn(2, 30, 768) # Batch, Text Tokens, Dim
# efficient_attn = OptimizedMultimodalAttention(embed_dim=768, num_heads=12)
# output = efficient_attn(visual_data, text_data)

So sánh các Kiến trúc Transformer Đa Phương Thức Tiêu biểu

Bảng dưới đây tổng hợp các đặc điểm và kịch bản ứng dụng của các kiến trúc Transformer đa phương thức chính:

Tên Mô hình Chiến lược Hợp nhất Phương pháp Tiền huấn luyện Ưu điểm Chính Nhiệm vụ Phù hợp
ViLBERT Chú ý chéo hai luồng Học đa phương thức bị che Tương tác sâu giữa các phương thức VQA, Truy xuất
LXMERT Bộ mã hóa ba luồng Tiền huấn luyện đa nhiệm vụ Khả năng tổng quát hóa cao Hiểu biết đa phương thức
VisualBERT Hợp nhất sớm một luồng Tiền huấn luyện end-to-end Đơn giản, hiệu quả Suy luận thị giác
UNITER Bộ mã hóa thống nhất một luồng Tiền huấn luyện quy mô lớn Hiệu suất vượt trội Học đa nhiệm vụ
CLIP Học tương phản Cặp hình ảnh-văn bản Khả năng zero-shot Phân loại, Truy xuất

Các Yếu tố Thiết kế Kiến trúc trong Ứng dụng Thực tế

Khi triển khai Transformer đa phương thức, cần xem xét các yếu tố thiết kế kiến trúc sau:

1. Thiết kế Bộ mã hóa Đặc thù Phương thức
# Cấu hình bộ mã hóa cho từng phương thức
encoder_settings_by_modality = {
    'vision_encoder': {
        'model_type': 'ViT-Base',
        'patch_dim': 16,
        'embedding_dim': 768,
        'num_blocks': 12
    },
    'text_encoder': {
        'model_type': 'BERT-Base',
        'vocabulary_size': 30522,
        'embedding_dim': 768,
        'num_blocks': 12
    },
    'audio_encoder': {
        'model_type': 'AudioSpectrogramNet',
        'sample_rate': 16000,
        'embedding_dim': 512,
        'num_blocks': 6
    }
}
2. Hợp nhất Đặc trưng Đa tỷ lệ

Để tận dụng thông tin ở các mức độ chi tiết khác nhau, việc hợp nhất đặc trưng đa tỷ lệ là rất quan trọng. Điều này có thể đạt được bằng cách trích xuất đặc trưng từ nhiều lớp của bộ mã hóa từng phương thức và kết hợp chúng thông qua các cơ chế hợp nhất chuyên biệt (ví dụ: concatenating, summing, hoặc gated fusion).

3. Cơ chế Định tuyến Động

Thiết kế cơ chế định tuyến động để tự động lựa chọn đường dẫn hợp nhất tối ưu cho các mẫu đầu vào khác nhau:

class AdaptiveFusionSelector(nn.Module):
    def __init__(self, num_modalities, num_fusion_experts, embedding_size):
        super().__init__()
        # Mạng lưới chọn lựa chuyên gia dựa trên sự kết hợp của các đặc trưng phương thức
        self.selector_network = nn.Linear(num_modalities * embedding_size, num_fusion_experts)
        
    def forward(self, list_of_modality_features):
        # Nối các đặc trưng của tất cả các phương thức
        # Giả định mỗi đặc trưng phương thức đã được pooled thành vector 1D (batch_size, embedding_size)
        combined_modal_vector = torch.cat(list_of_modality_features, dim=-1)
        
        # Tính điểm cho từng "chuyên gia" hợp nhất tiềm năng
        modality_scores = self.selector_network(combined_modal_vector)
        selection_probabilities = F.softmax(modality_scores, dim=-1)
        
        # Chọn chuyên gia với xác suất cao nhất (hoặc lấy trung bình có trọng số)
        selected_expert_index = torch.argmax(selection_probabilities, dim=-1)
        return selected_expert_index

# Ví dụ:
# expert_router = AdaptiveFusionSelector(num_modalities=2, num_fusion_experts=3, embedding_size=768)
# visual_feat = torch.randn(1, 768) # Giả định đã pooled
# text_feat = torch.randn(1, 768)
# selected_expert = expert_router([visual_feat, text_feat])

Kỹ thuật Tối ưu hóa Hiệu suất

1. Chiến lược Cân bằng Gradient

Trong huấn luyện đa phương thức, thường xảy ra vấn đề mất cân bằng gradient giữa các phương thức. Các chiến lược cân bằng gradient có thể giúp điều chỉnh đóng góp của mỗi phương thức vào tổng lỗi:

def weighted_gradient_descent(loss_components_dict):
    total_weighted_loss = 0.0
    module_gradients = {}
    
    for modality_name, loss_value in loss_components_dict.items():
        # Tính toán một hệ số trọng số cân bằng cho mỗi phần mất mát
        # Hàm calculate_weight_for_modality cần được định nghĩa tùy chỉnh
        # dựa trên độ lớn mất mát, sự đóng góp hoặc các heuristic khác.
        scaling_factor = calculate_weight_for_modality(modality_name, loss_value) 
        
        weighted_loss_part = loss_value * scaling_factor
        weighted_loss_part.backward(retain_graph=True) # retain_graph để các loss khác cũng có thể gọi .backward()
        
        # Thu thập gradient từ các module liên quan đến phương thức này
        module_gradients[modality_name] = gather_module_gradients(modality_name)
        total_weighted_loss += weighted_loss_part.item()
    
    # Hàm giả định để tính trọng số (cần triển khai cụ thể)
    def calculate_weight_for_modality(mod_name, current_loss):
        # Ví dụ đơn giản: trọng số đảo nghịch với độ lớn loss hoặc hằng số
        return 1.0 # Hoặc 1.0 / (current_loss.item() + 1e-6)
    
    # Hàm giả định để thu thập gradient (cần triển khai cụ thể)
    def gather_module_gradients(mod_name):
        # Lấy gradient từ các tham số của các module liên quan
        return {p.name: p.grad for p in some_model_params_for_modality if p.grad is not None} # Placeholder
    
    return total_weighted_loss, module_gradients

# Ví dụ sử dụng:
# loss_img = torch.tensor(0.5, requires_grad=True)
# loss_txt = torch.tensor(0.1, requires_grad=True)
# losses = {'image': loss_img, 'text': loss_txt}
# total_loss, grads = weighted_gradient_descent(losses)
2. Tối ưu hóa Hiệu quả Bộ nhớ

Transformer đa phương thức tiêu tốn nhiều bộ nhớ. Các kỹ thuật như chú ý theo khối (chunked attention) có thể giảm đáng kể yêu cầu về bộ nhớ:

class ChunkedAttentionBlock(nn.Module):
    def __init__(self, dimension, chunk_length=256):
        super().__init__()
        self.dimension = dimension
        self.chunk_length = chunk_length
        # Giả định một multi-head attention layer bên trong
        self.attention_layer = nn.MultiheadAttention(dimension, num_heads=8, batch_first=True) 
        
    def forward(self, queries, keys, values):
        batch_size, sequence_length, _ = queries.size()
        output_result = torch.zeros_like(queries)
        
        # Xử lý theo từng khối để giảm sử dụng bộ nhớ
        for start_idx in range(0, sequence_length, self.chunk_length):
            end_idx = min(start_idx + self.chunk_length, sequence_length)
            queries_chunk = queries[:, start_idx:end_idx, :]
            
            # Tính toán chú ý cho khối hiện tại
            # Trong một MultiheadAttention thực tế, cần cung cấp Q, K, V cho attention_layer
            # Nhưng ở đây, để minh họa, chúng ta giả định nó xử lý đầu vào trong một lượt
            # (thường các thư viện đã tối ưu hóa điều này nội bộ hoặc cần triển khai thủ công)
            
            # Đối với mục đích minh họa đơn giản:
            # chunk_output, _ = self.attention_layer(queries_chunk, keys, values) 
            
            # Để đơn giản hóa logic, giả định tính toán chú ý trực tiếp
            # Q_chunk, K, V
            # Trong thực tế, bạn sẽ tính toán attention scores cho từng Q_chunk với toàn bộ K, V
            
            # Ví dụ về cách tính attention cho một khối Q với toàn bộ K, V:
            query_proj = nn.Linear(self.dimension, self.dimension, bias=False)(queries_chunk)
            key_proj = nn.Linear(self.dimension, self.dimension, bias=False)(keys)
            value_proj = nn.Linear(self.dimension, self.dimension, bias=False)(values)
            
            # Chia heads cho query_proj, key_proj, value_proj...
            # ... (cần thêm logic chia head và tính attention)
            
            # Để giữ ví dụ ngắn gọn, chúng ta giả định đây là một hàm attention đã được tối ưu hóa.
            # Trong một triển khai thực tế, bạn sẽ cần logic để tính toán attention cho từng khối query
            # với toàn bộ key và value hoặc sử dụng các kỹ thuật sparse/linear attention.
            
            # Giả sử một hàm tính toán chú ý cho khối đã có
            scores = torch.matmul(query_proj, key_proj.transpose(-2, -1)) / math.sqrt(self.dimension)
            weights = F.softmax(scores, dim=-1)
            chunk_output = torch.matmul(weights, value_proj)
            
            output_result[:, start_idx:end_idx, :] = chunk_output
        
        return output_result

# Ví dụ:
# Q_tensor = torch.randn(1, 1024, 768)
# K_tensor = torch.randn(1, 1024, 768)
# V_tensor = torch.randn(1, 1024, 768)
# chunked_attn = ChunkedAttentionBlock(dimension=768, chunk_length=128)
# output_mem_efficient = chunked_attn(Q_tensor, K_tensor, V_tensor)

Việc thích nghi kiến trúc Transformer cho học đa phương thức là một lĩnh vực nghiên cứu đầy thử thách nhưng mang lại nhiều giá trị. Thông qua các chiến lược hợp nhất được thiết kế cẩn thận, cơ chế chú ý hiệu quả và kỹ thuật tối ưu hóa thông minh, kiến trúc Transformer có thể phát huy tối đa tiềm năng trong học đa phương thức, cung cấp cho các hệ thống AI khả năng nhận thức và hiểu biết mạnh mẽ hơn.

Các Mô hình Kinh điển: ViLBERT và LXMERT

Trong lịch sử phát triển của học đa phương thức, ViLBERT và LXMERT, được giới thiệu vào năm 2019, là hai mô hình cột mốc quan trọng, đặt nền tảng vững chắc cho các nhiệm vụ hiểu biết thị giác-ngôn ngữ. Những mô hình này đã mở rộng thành công khả năng học biểu diễn mạnh mẽ của Transformer sang lĩnh vực đa phương thức thông qua thiết kế kiến trúc và chiến lược tiền huấn luyện đổi mới.

ViLBERT: BERT Song Luồng cho Thị giác và Ngôn ngữ

ViLBERT (Vision-and-Language BERT) là một trong những mô hình đầu tiên mở rộng kiến trúc BERT thành công sang lĩnh vực đa phương thức. Đổi mới cốt lõi của nó nằm ở việc áp dụng kiến trúc song luồng, xử lý riêng biệt đầu vào thị giác và văn bản, sau đó thực hiện tương tác chéo phương thức thông qua cơ chế đồng chú ý (co-attention).

Thiết kế Kiến trúc

ViLBERT sử dụng hai luồng xử lý độc lập:

  • Luồng Thị giác: Xử lý đặc trưng hình ảnh.
  • Luồng Văn bản: Xử lý nhúng từ (word embeddings) và nhúng vị trí (positional embeddings) của văn bản.

Luồng thị giác của ViLBERT sử dụng bộ phát hiện vật thể Faster R-CNN để trích xuất đặc trưng vùng từ hình ảnh, mỗi vùng được biểu diễn dưới dạng một vector đặc trưng 2048 chiều. Luồng văn bản sử dụng bộ mã hóa và lớp nhúng BERT tiêu chuẩn để xử lý đầu vào văn bản.

Cơ chế Đồng chú ý (Co-attention)

Lớp đồng chú ý là đổi mới cốt lõi của ViLBERT, cho phép trao đổi thông tin hai chiều giữa hai phương thức:

class MutualAttentionUnit(nn.Module):
    def __init__(self, representation_dim, head_count):
        super().__init__()
        # Chú ý từ thị giác đến văn bản (Query: Visual, Key/Value: Text)
        self.image_to_text_attention = nn.MultiheadAttention(representation_dim, head_count, batch_first=True)
        # Chú ý từ văn bản đến thị giác (Query: Text, Key/Value: Visual)
        self.text_to_image_attention = nn.MultiheadAttention(representation_dim, head_count, batch_first=True)
        
    def forward(self, img_reps, text_reps):
        # Đặc trưng hình ảnh được chú ý dựa trên ngữ cảnh văn bản
        visual_contextualized, _ = self.image_to_text_attention(
            img_reps, text_reps, text_reps
        )
        
        # Đặc trưng văn bản được chú ý dựa trên ngữ cảnh hình ảnh
        text_contextualized, _ = self.text_to_image_attention(
            text_reps, img_reps, img_reps
        )
        
        return visual_contextualized, text_contextualized

# Ví dụ:
# img_features = torch.randn(1, 36, 768) # Batch, num_regions, dim
# text_features = torch.randn(1, 20, 768) # Batch, num_tokens, dim
# co_attn_block = MutualAttentionUnit(768, 12)
# attended_img, attended_text = co_attn_block(img_features, text_features)
Nhiệm vụ Tiền huấn luyện

ViLBERT sử dụng hai nhiệm vụ tiền huấn luyện chính:

  1. Mô hình hóa Đa phương thức Bị che (Masked Multimodal Modeling): Che ngẫu nhiên các token văn bản hoặc vùng thị giác, mô hình phải dự đoán chúng dựa trên ngữ cảnh.
  2. Dự đoán Đồng bộ Đa phương thức (Multimodal Alignment Prediction): Dự đoán xem cặp hình ảnh-văn bản có khớp với nhau hay không.

LXMERT: Học Biểu diễn Bộ mã hóa Chéo Phương thức

LXMERT (Learning Cross-Modality Encoder Representations from Transformers) áp dụng kiến trúc ba bộ mã hóa, được thiết kế đặc biệt để học các mối quan hệ đồng bộ sâu sắc giữa thị giác và ngôn ngữ.

Kiến trúc Ba Bộ mã hóa

LXMERT bao gồm ba thành phần cốt lõi:

  • Bộ mã hóa Vật thể (Object-Relationship Encoder): Xử lý thông tin thị giác, bao gồm cả mối quan hệ giữa các vật thể.
  • Bộ mã hóa Ngôn ngữ (Language Encoder): Xử lý đầu vào văn bản.
  • Bộ mã hóa Chéo Phương thức (Cross-Modality Encoder): Tích hợp thông tin từ hai bộ mã hóa trên thông qua chú ý chéo.
Chiến lược Tiền huấn luyện

LXMERT sử dụng năm nhiệm vụ tiền huấn luyện đa dạng:

Nhiệm vụ Tiền huấn luyện Mô tả Mục tiêu
Mô hình hóa Ngôn ngữ Bị che Che token văn bản và dự đoán Hiểu biết ngôn ngữ
Dự đoán Vật thể Bị che Che vùng thị giác và dự đoán Hiểu biết thị giác
Đối sánh Chéo Phương thức Xác định cặp hình ảnh-văn bản khớp Đồng bộ chéo phương thức
Trả lời câu hỏi dựa trên Hình ảnh Trả lời câu hỏi văn bản về hình ảnh Khả năng suy luận
Phát hiện Mối quan hệ Thị giác Xác định mối quan hệ giữa các vật thể Hiểu biết không gian
Chi tiết Triển khai Kỹ thuật

Bộ mã hóa chéo phương thức của LXMERT sử dụng cấu trúc Transformer đa lớp:

class IntermodalFusionLayer(nn.Module):
    def __init__(self, embed_dim, num_heads):
        super().__init__()
        # Chú ý tự thân cho mỗi phương thức
        self.vision_self_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
        self.text_self_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
        
        # Chú ý chéo: thị giác query văn bản
        self.vision_to_text_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
        # Chú ý chéo: văn bản query thị giác
        self.text_to_vision_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)

        self.feed_forward = nn.Sequential(
            nn.Linear(embed_dim, embed_dim * 4),
            nn.ReLU(),
            nn.Linear(embed_dim * 4, embed_dim)
        )
        self.norm1 = nn.LayerNorm(embed_dim)
        self.norm2 = nn.LayerNorm(embed_dim)
        self.norm3 = nn.LayerNorm(embed_dim)
        self.norm4 = nn.LayerNorm(embed_dim)
        self.dropout = nn.Dropout(0.1)

    def forward(self, vis_input, text_input):
        # 1. Self-attention cho từng phương thức
        vis_attn_out, _ = self.vision_self_attn(vis_input, vis_input, vis_input)
        vis_input = self.norm1(vis_input + self.dropout(vis_attn_out))

        text_attn_out, _ = self.text_self_attn(text_input, text_input, text_input)
        text_input = self.norm2(text_input + self.dropout(text_attn_out))

        # 2. Cross-attention giữa các phương thức
        # Thị giác hỏi văn bản
        vis_cross_attn_out, _ = self.vision_to_text_attn(vis_input, text_input, text_input)
        vis_output = self.norm3(vis_input + self.dropout(vis_cross_attn_out))

        # Văn bản hỏi thị giác
        text_cross_attn_out, _ = self.text_to_vision_attn(text_input, vis_input, vis_input)
        text_output = self.norm4(text_input + self.dropout(text_cross_attn_out))
        
        # 3. Feed-forward layer (thường áp dụng sau mỗi lớp Transformer)
        vis_output = vis_output + self.dropout(self.feed_forward(vis_output))
        text_output = text_output + self.dropout(self.feed_forward(text_output))

        return vis_output, text_output

class CrossDomainTransformerBlock(nn.Module):
    def __init__(self, feature_dim, num_layers, num_heads):
        super().__init__()
        self.layers = nn.ModuleList([
            IntermodalFusionLayer(feature_dim, num_heads)
            for _ in range(num_layers)
        ])
    
    def forward(self, vision_features, language_features):
        for layer_block in self.layers:
            vision_features, language_features = layer_block(
                vision_features, language_features
            )
        return vision_features, language_features

# Ví dụ:
# vision_embeds = torch.randn(1, 36, 768)
# lang_embeds = torch.randn(1, 20, 768)
# lxmert_cross_encoder = CrossDomainTransformerBlock(feature_dim=768, num_layers=9, num_heads=12)
# final_vision_embeds, final_lang_embeds = lxmert_cross_encoder(vision_embeds, lang_embeds)

So sánh Hiệu suất Mô hình

ViLBERT và LXMERT đã thể hiện hiệu suất xuất sắc trên nhiều bộ dữ liệu tiêu chuẩn:

Mô hình VQA v2.0 GQA NLVR2 Số tham số
ViLBERT 70.55% - - 220M
LXMERT 72.42% 60.00% 76.18% 183M
VisualBERT 70.80% - - 110M
VL-BERT 71.16% - - 108M

Đóng góp Đổi mới và Tác động

Những đóng góp chính của ViLBERT
  1. Tiền huấn luyện độc lập nhiệm vụ: Lần đầu tiên chứng minh rằng các biểu diễn thị giác-ngôn ngữ có thể được tiền huấn luyện và chuyển giao sang nhiều nhiệm vụ hạ nguồn khác nhau.
  2. Kiến trúc song luồng: Duy trì tính đặc thù của phương thức trong khi vẫn cho phép tương tác chéo phương thức hiệu quả.
  3. Tiền huấn luyện quy mô lớn: Sử dụng tập dữ liệu Conceptual Captions để tiền huấn luyện.
Những đổi mới cốt lõi của LXMERT
  1. Thiết kế ba bộ mã hóa: Bộ mã hóa quan hệ vật thể, bộ mã hóa ngôn ngữ và bộ mã hóa chéo phương thức chuyên biệt.
  2. Nhiệm vụ tiền huấn luyện đa dạng: Năm mục tiêu tiền huấn luyện bổ sung bao quát toàn diện nhu cầu hiểu biết đa phương thức.
  3. Khả năng tổng quát hóa mạnh mẽ: Đạt được mức cải thiện tuyệt đối 22% trong các nhiệm vụ suy luận thị giác.

Ví dụ Ứng dụng Thực tế

Dưới đây là một ví dụ mã sử dụng LXMERT cho nhiệm vụ hỏi đáp thị giác:

import torch
from transformers import LxmertForQuestionAnswering, LxmertTokenizer
from PIL import Image

# Tải mô hình và bộ token hóa đã được tiền huấn luyện
qa_processor = LxmertForQuestionAnswering.from_pretrained("unc-nlp/lxmert-base-qa-uncased")
lxmert_token_encoder = LxmertTokenizer.from_pretrained("unc-nlp/lxmert-base-qa-uncased")

# Chuẩn bị đầu vào
# Thay đổi đường dẫn đến một tệp ảnh thực tế trên hệ thống của bạn
sample_image_path = "example.jpg" 
try:
    input_image = Image.open(sample_image_path).convert("RGB")
except FileNotFoundError:
    print(f"Error: Image file not found at {sample_image_path}. Please provide a valid path.")
    exit()

query_question = "What color is the car in the image?"

# Xử lý đầu vào bằng bộ token hóa
processed_inputs = lxmert_token_encoder(
    question=query_question,
    # LxMERT cũng cần các đặc trưng hình ảnh, thường được trích xuất bởi Faster R-CNN.
    # Đối với ví dụ này, chúng ta sẽ bỏ qua bước trích xuất đặc trưng hình ảnh phức tạp
    # và giả định mô hình có thể xử lý chỉ từ câu hỏi.
    # Trong ứng dụng thực tế, bạn sẽ cần:
    # visual_features = get_image_features(input_image)
    # visual_boxes = get_image_boxes(input_image)
    # ... và truyền chúng vào mô hình cùng với encoded_inputs.
    # Hiện tại, chỉ xử lý phần văn bản.
    return_tensors="pt",
    padding="max_length",
    max_length=20,
    truncation=True
)

# Để ví dụ này chạy được mà không cần các đặc trưng thị giác phức tạp,
# chúng ta cần tạo các tensor giả cho visual_feats và visual_bboxes.
# Trong một triển khai thực tế, chúng sẽ đến từ một mô hình trích xuất đặc trưng thị giác.
batch_size = processed_inputs['input_ids'].shape[0]
dummy_visual_feats = torch.randn(batch_size, 36, 2048) # Giả định 36 vùng, dim 2048
dummy_visual_bboxes = torch.randn(batch_size, 36, 4)   # Giả định 36 bounding boxes

# Thực hiện dự đoán
with torch.no_grad():
    prediction_results = qa_processor(
        input_ids=processed_inputs['input_ids'],
        attention_mask=processed_inputs['attention_mask'],
        token_type_ids=processed_inputs['token_type_ids'],
        visual_feats=dummy_visual_feats,
        visual_pos=dummy_visual_bboxes # Hoặc visual_pos, tùy theo yêu cầu của model
    )

# Lấy câu trả lời được dự đoán
predicted_answer_idx = torch.argmax(prediction_results.question_answering_score, dim=-1)
decoded_answer = lxmert_token_encoder.decode(predicted_answer_idx)

print(f"Câu hỏi: {query_question}")
print(f"Câu trả lời dự đoán: {decoded_answer}")

Thách thức Kỹ thuật và Giải pháp

Vấn đề Đồng bộ Phương thức

ViLBERT và LXMERT giải quyết thách thức đồng bộ phương thức thông qua các cách sau:

  1. Đồng bộ phân cấp: Thiết lập kết nối chéo phương thức ở các cấp độ trừu tượng khác nhau.
  2. Cơ chế chú ý: Sử dụng cơ chế tự chú ý và chú ý chéo để học các mối quan hệ đồng bộ.
  3. Tiền huấn luyện đa nhiệm vụ: Buộc mô hình học các biểu diễn đồng bộ thông qua các nhiệm vụ đa dạng.
Tối ưu hóa Hiệu quả Tính toán
Chiến lược Tối ưu hóa ViLBERT LXMERT
Chia sẻ tham số Chia sẻ một phần Các bộ mã hóa độc lập
Cơ chế chú ý Đồng chú ý Chú ý chéo
Hiệu quả tiền huấn luyện Trung bình Khá cao

Ảnh hưởng Phát triển và Các công trình Tiếp theo

Thành công của ViLBERT và LXMERT đã đặt nền tảng cho các mô hình Transformer đa phương thức tiếp theo:

  1. Mô hình kiến trúc: Xác lập mô hình hai luồng hoặc ba luồng với tương tác chú ý chéo là một phương pháp tiêu chuẩn.
  2. Chiến lược tiền huấn luyện: Khẳng định hiệu quả của tiền huấn luyện trên các cặp dữ liệu đa phương thức quy mô lớn.

Thẻ: Multimodal Learning Transformer ViLBERT LXMERT Cross-Attention

Đăng vào ngày 14 tháng 9 lúc 15:49