Nội dung
Trong bài viết này, chúng ta sẽ đi qua sơ lược về định nghĩa của Markov chain, từ đó hiểu thêm về Markov chain để ứng dụng vào một bài toán và thực nghiệm bằng Python.
1. Định nghĩa Markov chain
Markov chain (chuỗi Markov), được đặt theo tên nhà toán học người Nga Andrey Markov, là một mô hình ngẫu nhiên hay tiến trình ngẫu nhiên mô tả một chuỗi các sự kiện có khả năng xảy ra, mà xác suất để xảy ra sự kiện tiếp theo phụ thuộc chỉ vào sự kiện hiện tại. Đây là một mô hình “không có trí nhớ” (memorylessness), nghĩa là các sự kiện xảy ra trong quá khứ sẽ không được ghi nhớ, sự kiện trong tương lai chỉ phụ thuộc sự kiện hiện tại của mô hình.
Markov chain là sự kết hợp bởi 2 thành phần: tập trạng thái
Ví dụ, ta định nghĩa Markov chain
- Tập trạng thái là
- Ma trận chuyển đổi giứa các trạng thái là
Markov chain
Để quan sát được đường đi của các trạng thái trong Markov chain, ta cần định nghĩa quan sát
Cụ thể hơn về ký hiệu toán học của xác suất chuyển đổi,
2. Bài toán “Sáng nay ăn gì”
Giả sử vào một buổi sáng nọ, bạn thức dậy với 2 lần reo chuông báo thức đã qua từ điện thoại, bạn ngồi dậy, với cái bụng đang reo lên, bạn liền suy nghĩ đến việc: sáng hôm nay mình sẽ ăn gì?. Sau khi ra đầu ngõ, bạn đứng trước “ngã tư”: quán phở cô Ba, xe bánh mì cô Hai, cơm tấm chú Sáu và xe súp cua của ông Bảy. Tuy rằng, bạn đã ăn sáng mấy chục năm cuộc đời rồi, nhưng việc lựa chọn món ăn chưa bao giờ là dễ cả. May mắn thay, trong một năm vừa qua, món ăn sáng của mỗi ngày đều đã được bạn lưu trữ trên điện thoại, trong ứng dụng ghi chú (đây chính là data quý giá). Sau khi đọc định nghĩa về Markov chain ở trên, bạn quyết định áp dụng để giải quyết bài toán “sáng nay ăn gì” này.
Đó chỉ là một câu chuyện vui do tôi bịa ra để dẫn vào bài toán có thể giải quyết bằng Markov chain này. Trên thực tế, Markov chain có thể giải các bài toán to lớn hơn, nhưng trước tiên chúng ta cứ giải bài toán kia để bạn có thể lấp đầy cái bụng đã.
Trong ví dụ này, tôi sẽ hiện thực bằng Python, đọc data theo thời gian lên và tìm bộ các trạng thái
Đối với bộ data ở trên, tập trạng thái của chúng ta sẽ là
Trước tiên ta sẽ phải import các thư viện cần thiết vào Python
1
2
3
4
import numpy as np # tính toán trên ma trận
import pandas as pd # đọc dữ liệu từ file csv
from pprint import pprint # dùng cho mục đích in "đẹp"
from collections import defaultdict # để đếm số lượng lần xảy ra của các trạng thái (đơn lẻ và cặp)
Đọc dữ liệu và chuyển thành dạng list trong Python để dễ dàng sử dụng
1
2
3
df = pd.read_csv('breakfast.csv')
data = df.Food.tolist()
data[-5:] # xuất ra 5 món ăn cuối cùng bạn ăn
['Phở', 'Cơm tấm', 'Bánh mì', 'Phở', 'Phở']
Để có thể tạo ma trận P, trước tiên ta phải đếm các cặp và các giá trị đơn lẻ trước, sau đó đi chuẩn hóa
1
2
3
# tạo nơi lưu trữ giá trị
food_count = defaultdict(int)
food_pair_count = defaultdict(lambda: defaultdict(float))
Đếm các giá trị
1
2
3
4
5
6
7
8
9
10
# food_count: đếm số lần xuất hiện của một trạng thái
# food_pair_count: đếm tất cả các cặp trạng thái có thể [current][future]
n = len(data)
for i in range(n):
food_count[data[i]] += 1
if i == n - 1:
# self loop
food_pair_count[data[i]][data[i]] += 1
break
food_pair_count[data[i]][data[i + 1]] += 1
Chuẩn hóa theo tổng hàng
1
2
3
4
# chuẩn hóa theo tổng hàng
for key, value in food_pair_count.items():
for k, v in value.items():
food_pair_count[key][k] /= food_count[key] # chuẩn hóa
Do ma trận không giống như dictionary trong Python, ta chỉ có thể truy cập được bằng chỉ mục (index) của trạng thái, vì vậy ta cần phải có một dictionary lưu trữ các index của các trạng thái
1
2
3
4
5
# lấy index của các món ăn để dễ thao tác
keys = list(food_count.keys())
idx = range(len(keys))
key_to_idx = dict(zip(keys, idx)) # key to index
print(key_to_idx)
{'Bánh mì': 0, 'Cơm tấm': 1, 'Phở': 2, 'Súp cua': 3}
Ta bây giờ có thể tạo ma trận
1
2
3
4
5
6
7
8
9
P = []
for key, value in food_pair_count.items():
P.append(list(value.values()))
# chuyển list sang numpy để dễ tính toán
P = np.array(P)
print('Ma trận chuyển trạng thái P: ')
pprint(P)
Ma trận chuyển trạng thái P:
array([[0.26582278, 0.26582278, 0.26582278, 0.20253165],
[0.25274725, 0.20879121, 0.24175824, 0.2967033 ],
[0.28571429, 0.25274725, 0.28571429, 0.17582418],
[0.25961538, 0.33653846, 0.21153846, 0.19230769]])
Ta có thể kiểm tra tổng hàng xem có bằng
1
2
# tổng hàng của ma trận phải luôn bằng 1
print(P.sum(axis=1))
[1. 1. 1. 1.] # tất cả đều 1, chuẩn
Bây giờ, phần cuối sẽ đi dự đoán món ăn. Việc dự đoán món ăn (trạng thái) tương lai khá đơn giản, ta có thể hình dung thế này: đứng ở node của trạng thái hiện tại (món ăn cuối cùng của tập dữ liệu - món ăn hôm trước), có một tập các giá trị xác suất tương ứng với trọng số của các cạnh để đi đến node khác, chọn ngẫu nhiên một node để đi đến từ tập xác suất đó (một phân phối), và ta có thể chọn ngẫu nhiên theo phân phối với hàm numpy.random.choice
.
1
2
3
4
5
# dự đoán món ăn
curr_food = data[-1]
curr_distribution = P[key_to_idx[curr_food]]
predicted_food = np.random.choice(keys, p=curr_distribution) # random walk with known distribution
predicted_probability = P[key_to_idx[curr_food]][key_to_idx[predicted_food]]
In ra kết quả dự đoán
1
2
3
print(f'Món ăn chúng ta ăn hôm trước: {data[-1]}')
print(f'Món ăn nên ăn vào hôm nay là "{predicted_food}"\
với khả năng xảy ra là {round(predicted_probability * 100, 2)}%')
Món ăn chúng ta ăn hôm trước: Phở
Món ăn nên ăn vào hôm nay là "Bánh mì" với khả năng xảy ra là 28.57%
Vậy mô hình Markov chain chúng ta đã hiện thực đã dự đoán cho chúng ta món “Bánh mì” vào hôm nay.
⚠️ Lưu ý: Các giá trị, số liệu được xuất ra trong bài có thể khác, tùy thuộc vào dữ liệu cung cấp và sự lựa chọn ngẫu nhiên của các hàm trên.
Chi tiết toàn bộ code, bạn đọc có thể tham khảo ở đây
3. Tổng kết
Trên đây chỉ là một ví dụ nhỏ để mô tả khả năng của Markov chain. Ta có thể bắt gặp Markov chain trong nhiều bài toán khác như xây dựng mô hình ngôn ngữ n-gram trong lĩnh vực Xử lý ngôn ngữ tự nhiên, có thể ứng dụng vào việc Xếp hạng trang (PageRank) của Google (nhưng trên thực tế, Google sử dụng các mô hình phức tạp hơn Markov chain). Markov chain có thể phát triển lên một dạng khác, mô hình Markov ẩn, mà có thể ứng dụng trong bài toán Nhận dạng giọng nói. Và rất nhiều các ứng dụng khác trong các lĩnh vực Vật lý, Hóa học, Sinh học, Lý thuyết hàng đợi, … Bạn đọc có thể đọc thêm ở đường link này.