Trong các bài toán như nhận dạng tiếng nói, dịch máy hay phân tích gen, chúng ta không chỉ muốn biết xác suất của một chuỗi quan sát, mà quan trọng hơn, chúng ta cần biết: Chuỗi trạng thái ẩn nào có khả năng cao nhất đã tạo ra quan sát đó? Thuật toán Viterbi chính là câu trả lời tối ưu cho bài toán giải mã (Decoding) này.
Dễ nhầm lẫn giữa hai thuật toán này vì cả hai đều dùng kỹ thuật lập trình động trên lưới trellis, nhưng bản chất toán học lại khác nhau:
Thuật toán Forward: Tính TỔNG xác suất của tất cả các con đường khả thi dẫn đến một trạng thái.
Thuật toán Viterbi: Chỉ chọn con đường LỚN NHẤT (MAX) dẫn đến trạng thái đó.
Nếu Forward trả lời câu hỏi: "Xác suất là bao nhiêu?", thì Viterbi trả lời câu hỏi: "Kịch bản nào là đúng nhất?".
Giả sử ta có trạng thái ẩn và chuỗi quan sát dài bước.
Số lượng con đường khả thi là .
Với và , số con đường là (lớn hơn số nguyên tử trong vũ trụ). Việc kiểm tra từng con đường để tìm cái tốt nhất là bất khả thi.
Ý tưởng chủ đạo của Viterbi đến từ Nguyên lý tối ưu Bellman (The Bellman Optimality Principle):
Nếu con đường tối ưu đi qua trạng thái tại thời điểm , thì phần con đường từ điểm bắt đầu đến cũng phải là con đường tối ưu nhất để đến được đó.
Ví dụ: Nếu đường đi ngắn nhất từ Hà Nội đến TP.HCM đi qua Đà Nẵng, thì chắc chắn đoạn từ Hà Nội đến Đà Nẵng trong lộ trình đó cũng phải là đoạn đường ngắn nhất vì nếu có đường nào ngắn hơn đến Đà Nẵng, ta đã thay nó vào để tổng quãng đường ngắn hơn nữa rồi.
Tại mỗi thời điểm , đối với mỗi trạng thái :
Có con đường từ các trạng thái ở bước dẫn đến nó.
Viterbi sẽ tính toán xác suất của cả nhánh này.
Quyết định quan trọng: Nó chỉ giữ lại duy nhất một nhánh có xác suất cao nhất (con đường "sống sót") và vứt bỏ hoàn toàn nhánh còn lại.
Nó ghi chú lại: "Để đến lúc này một cách tốt nhất, tôi đã đi từ trạng thái ở bước trước" (đây chính là Backpointer).
Ý tưởng này biến một bài toán có độ phức tạp lũy thừa trở thành bài toán có độ phức tạp tuyến tính theo thời gian .
Tính cục bộ: Tại bất kỳ thời điểm nào, máy tính cũng chỉ cần lưu trữ giá trị xác suất tốt nhất và con đường dẫn đến chúng.
Không nhìn lại: Một khi một nhánh đã bị loại bỏ ở bước , nó sẽ không bao giờ được xem xét lại ở bước . Điều này giúp tiết kiệm tài nguyên khổng lồ.
Để tiện theo dõi, mình sẽ vẫn giữ hệ thống ký hiệu được sử dụng xuyên suốt qua các bài viết HMM và Forward-Backward algorithm
Mạng Trellis (Trellis Diagram)
Đây là một sơ đồ không gian - thời gian dùng để biểu diễn các trạng thái ẩn và các bước chuyển tiếp của chúng. Mạng này đóng vai trò làm bản đồ vẽ lại tất cả các con đường ta có thể đi trong mê cung trạng thái của HMM theo thời gian. Một mạng Trellis được xây dựng dựa trên hai trục:
Trục tung (Vertical): Biểu diễn tập hợp các trạng thái ẩn (). Mỗi trạng thái tại một thời điểm được vẽ như một nút (node).
Trục hoành (Horizontal): Biểu diễn thời gian ().
Tại mỗi mốc thời gian , chúng ta vẽ lại toàn bộ các trạng thái ẩn có thể có. Các đường nối giữa các nút ở thời điểm và biểu diễn xác suất chuyển trạng thái (). Từ đó, ta đã biến một bài toán xác suất thành một bài toán tìm đường trên đồ thị. Để thực hiện thuật toán, chúng ta cần quản lý hai loại thông tin tại mỗi nút trên mạng Trellis: Trọng số tốt nhất và Dấu vết con đường.
Biến Viterbi
đại diện cho xác suất của con đường "sáng giá" nhất kết thúc tại trạng thái ở thời điểm . Công thức:
Ý nghĩa ở đây là trong số hàng nghìn kịch bản có thể dẫn đến việc đang ở trạng thái lúc này, chỉ ghi lại điểm số của kịch bản có xác suất cao nhất.
Biến lưu vết:
là "quyển sổ nhật ký" giúp chúng ta thực hiện việc truy hồi (Backtracking). Công thức:
Để ý ở đây không lưu giá trị xác suất, mà lưu tên (chỉ số) của trạng thái ở bước đã dẫn đến kết quả Max cho trạng thái ở bước .
Nôm na: "Nếu tôi đang ở trạng thái lúc này, thì ở bước trước đó, tôi đến từ đâu là tốt nhất?"
Ba bước của thuật toán
Thuật toán vận hành theo quy trình tịnh tiến từ trái sang phải trên mạng Trellis:
Bước 1: Khởi tạo - Tại
Vì không có quá khứ, xác suất tốt nhất chính là xác suất bắt đầu:
Bước 2: Đệ quy - từ
Đây là bước chọn lọc xác suất tốt nhất chọn lọc:
: Chọn ra giá trị xác suất lớn nhất từ các trạng thái bước trước.
: Ghi lại ID của trạng thái bước trước đã tạo ra cái đó.
Các thành phần của bộ tham số :
: Phần tử của ma trận chuyển trạng thái đại diện cho xác suất nhảy từ trạng thái ẩn
: Xác suất trạng thái ) biểu hiện ra quan sát
: Xác suất bắt đầu trạng thái tại
Bước 3: Kết thúc và Truy hồi
Xác suất con đường tốt nhất:
Trạng thái cuối cùng tối ưu:
Truy hồi ngược: với .
Mình vẫn sử dụng lại bài toán và các giá trị đã dùng trong series này từ các bài trước: HMM và Forward-Backward algorithm
Bài toán
Quan sát đồng nghiệp trong 2 ngày liên tiếp và cả 2 ngày họ đều Cười ().
Hệ thống tham số :
Trạng thái ẩn:
Vector khởi tạo: (80% khả năng họ bắt đầu ngày mới vui vẻ).
Ma trận chuyển trạng thái: (Vui dễ duy trì 60%, nhưng Buồn thì lỳ lợm hơn với 70%).
Ma trận phát xạ: (Vui thì 90% sẽ Cười, Buồn vẫn có thể Cười gượng 20%).
Mục tiêu giải toán: Tìm chuỗi tâm trạng ẩn có khả năng cao nhất đã dẫn đến chuỗi hành động "Cười - Cười". Chúng ta không tìm tổng xác suất, chúng ta tìm kịch bản logic nhất.
Các bước tính toán chi tiết
Chúng ta sẽ triển khai thuật toán qua mạng Trellis 2 bước thời gian.
Bước 1: Khởi tạo (Ngày 1 - )
Tại ngày đầu tiên, ta tính xác suất tốt nhất để dừng ở mỗi trạng thái khi thấy họ Cười.
:
:
Lưu vết: ; (Gốc tọa độ).
Bước 2: Đệ quy (Ngày 2 - )
Khi quan sát thấy nụ cười thứ hai, ta phải tìm con đường tối ưu để "đến" được trạng thái của ngày hôm nay.
Ta xét 2 con đường dẫn đến "Vui" ở ngày 2:
Từ Ngày 1 Vui:
Từ Ngày 1 Buồn:
Chọn Max: (Đến từ trạng thái Vui ngày 1).
Nhân phát xạ:
Ghi nhật ký: .
Tương tự, ta xét 2 con đường dẫn đến "Buồn" ở ngày 2:
Từ Ngày 1 Vui:
Từ Ngày 1 Buồn:
Chọn Max: (Đến từ trạng thái Vui ngày 1).
Nhân phát xạ:
Ghi nhật ký: .
Bước 3: Kết thúc và Truy hồi
Đây là bước thu hoạch kết quả từ "quyển sổ nhật ký" $\psi$.
Tìm điểm kết thúc tốt nhất ($t=2$):
So sánh và .
=> Giá trị lớn nhất là 0.3888. Vậy trạng thái ẩn của Ngày 2 chắc chắn nhất là Vui.
Dò ngược về Ngày 1:
Bạn đang ở Ngày 2 là Vui. Hãy xem nhật ký ghi gì?
=> . Vậy Ngày 1 người đó cũng là Vui.
Như vậy, chuỗi trạng thái ẩn tối ưu (Viterbi Path) là: Vui Vui. Xác suất của con đường này là: 0.3888.
Trong HMM, các giá trị xác suất () luôn nằm trong khoảng . Khi tính toán các biến hay , chúng ta thực hiện phép nhân liên tiếp các số thập phân này qua từng bước thời gian .
Ví dụ:
Giả sử mỗi bước , xác suất giảm đi khoảng ().
Sau 100 bước thời gian (), xác suất sẽ là .
Sau 400 bước thời gian, con số này có thể là .
Vấn đề là máy tính lưu trữ số thực (kiểu float64) có giới hạn dưới. Khi một số trở nên quá nhỏ (thường là nhỏ hơn khoảng đối với chuẩn IEEE 754), máy tính không thể biểu diễn nó được nữa và sẽ tự động làm tròn về 0. Nếu chuỗi quan sát chỉ dài 5-10 bước, có thể không thấy Underflow. Nhưng trong nhận dạng tiếng nói (với hàng nghìn frame ảnh mỗi giây) hoặc phân tích chuỗi DNA, hiện tượng Underflow chắc chắn sẽ xảy ra.
Để giải quyết vấn đề này, thay vì nhân các xác suất, chúng ta sẽ cộng các Logarit của chúng. Dựa trên tính chất toán học:
Các số cực nhỏ như khi lấy Log sẽ trở thành các số âm có độ lớn vừa phải (khoảng ). Máy tính xử lý con số cực kỳ dễ dàng và chính xác.
Phép toán Max vẫn giữ nguyên tính chất: Nếu thì . Do đó, kết quả tìm con đường tối ưu không bị thay đổi.
Ta biến đổi công thức Viterbi sang miền log. Thay vì dùng , chúng ta dùng .
Khởi tạo:
Đệ quy (Thay bằng và giữ nguyên ):
No comments yet. Be the first to comment!