Cosine similarity trong tìm kiếm và gợi ý
Từ đo độ dài sang đo hướng
Bài trước ta đã đo "độ lớn" của vector bằng chuẩn L1 và L2. Nhưng trong tìm kiếm và gợi ý, câu hỏi thường không phải "vector này dài bao nhiêu" mà là "hai vector này có chỉ về cùng một hướng không". Một tài liệu dài gấp đôi nhưng cùng chủ đề thì vẫn nên khớp với câu query. Đó chính là lúc cosine similarity xuất hiện.
Sau bài này, bạn sẽ:
- Viết và giải thích được công thức cosine similarity dựa trên tích vô hướng đã học ở Module 1.
- Tính tay được cosine giữa hai vector nhỏ và đọc được ý nghĩa của con số nhận được.
- Áp dụng cosine vào một bài tìm kiếm văn bản đơn giản và một bài gợi ý đơn giản.
- Biết khi nào nên chọn cosine, khi nào nên quay lại L1 hoặc L2.
Công thức cosine: chỉ quan tâm góc
Ở Module 1 ta có $a \cdot b = |a|_2,|b|_2 \cos\theta$. Đảo lại, ta được thứ mình cần:
$$ \cos(a,b) = \frac{a \cdot b}{|a|_2 \cdot |b|_2} $$
Tử số là tích vô hướng, mẫu số là tích hai chuẩn L2 vừa học bài trước. Kết quả luôn nằm trong khoảng từ -1 đến 1:
| Giá trị cos | Góc | Nghĩa |
|---|---|---|
| 1 | 0° | Cùng hướng, giống nhau tối đa |
| 0.7 | khoảng 45° | Khá giống |
| 0 | 90° | Vuông góc, không chia sẻ hướng nào |
| -1 | 180° | Ngược hướng hoàn toàn |
Một biến thể hay dùng: cosine distance $= 1 - \cos(a,b)$, biến "càng giống càng lớn" thành "càng gần càng tốt", tiện khi cần một khoảng cách.
Note: Cosine similarity chỉ quan tâm đến hướng, không quan tâm đến độ dài. Nhân vector với bất kỳ số dương nào, cosine với vector khác không đổi. Đây không phải lỗi, mà chính là tính năng ta cần.
Bài tập nhanh 1. Cho $q = (1,2)$, $a = (2,4)$, $b = (3,1)$. Tính cosine của $q$ với từng vector.
Xem hướng dẫn và đáp án
$|q| = \sqrt{5} \approx 2.236$; $|a| = \sqrt{20} \approx 4.472$; $|b| = \sqrt{10} \approx 3.162$.
- $q \cdot a = 2 + 8 = 10$, nên $\cos = 10 / (2.236 \times 4.472) = 1$. Hai vector cùng hướng, $a$ chỉ dài gấp đôi $q$.
- $q \cdot b = 3 + 2 = 5$, nên $\cos = 5 / (2.236 \times 3.162) \approx 0.707$, tức góc 45°.
Kết luận: $a$ khớp hơn hẳn, dù $b$ có tọa độ "to" hơn ở một chiều.
Ví dụ tìm kiếm: ba tài liệu, một câu query
Giả sử từ điển chỉ có ba từ: vector, ma trận, cosine. Mỗi tài liệu là một vector đếm số lần xuất hiện:
| vector | ma trận | cosine | $|d|_2$ | |
|---|---|---|---|---|
| Doc 1 | 3 | 0 | 0 | 3 |
| Doc 2 | 1 | 2 | 0 | 2.236 |
| Doc 3 | 2 | 2 | 1 | 3 |
| Query | 1 | 1 | 1 | 1.732 |
Tính nhanh bằng tích vô hướng chia chuẩn:
- Doc 1: $3 / (3 \times 1.732) \approx 0.577$
- Doc 2: $(1+2) / (2.236 \times 1.732) \approx 0.775$
- Doc 3: $(2+2+1) / (3 \times 1.732) \approx 0.962$
Xếp hạng: Doc 3, Doc 2, Doc 1. Điểm đáng chú ý: Doc 1 nhắc "vector" tới 3 lần nhưng chỉ toàn một chiều, nên hướng của nó lệch khỏi query.
Bài tập nhanh 2. Thêm Doc 4 có vector $(30, 0, 0)$. Cosine với query bằng bao nhiêu? Con số đó nói gì về cosine?
Xem đáp án
$\cos = 30 / (30 \times 1.732) \approx 0.577$, hoàn toàn giống Doc 1. Cosine "nhìn thấy" hướng chứ không thấy độ lớn, nên tài liệu dài gấp 10 lần cùng chủ đề không được điểm cao hơn. Nếu bạn muốn số lần lặp từ có trọng lượng, hãy biến đổi vector trước (ví dụ dùng log tần suất hoặc TF-IDF) rồi mới tính cosine.
Gợi ý: vector người dùng gặp vector item
Với gợi ý, mỗi item là một vector đặc trưng và mỗi người dùng cũng là một vector cùng không gian đó. Cách làm cơ bản: tính cosine giữa vector người dùng với từng item, sắp xếp giảm dần, lấy top N.
Ví dụ người dùng $u = (2, 1, 0)$; item $i_1 = (4, 2, 0)$; item $i_2 = (0, 1, 2)$.
- $i_1$: $u \cdot i_1 = 10$, $|u| = 2.236$, $|i_1| = 4.472$, nên $\cos = 1$.
- $i_2$: $u \cdot i_2 = 1$, $|i_2| = 2.236$, nên $\cos = 1 / (2.236 \times 2.236) = 0.2$.
Hệ thống gợi ý $i_1$ trước. Lưu ý thực tế: người dùng "nặng ký" (xem rất nhiều) không nên đè bẹp người dùng mới, và cosine xử lý chuyện này rất tốt.
flowchart TD
A["❓ Cần so sánh hai vector"] --> B{"Độ lớn có ý nghĩa?"}
B -- "Không, chỉ cần nội dung" --> C["📐 Cosine similarity"]
B -- "Có, số lượng quan trọng" --> D{"Muốn phạt outlier mạnh?"}
D -- "Có" --> E["📏 Khoảng cách L2"]
D -- "Không" --> F["📏 Khoảng cách L1"]
C --> G["Chuẩn hóa L2 rồi dùng dot product cho nhanh"]
Một mẹo kỹ thuật đáng nhớ: nếu chuẩn hóa mọi vector về chuẩn L2 bằng 1, thì cosine chỉ còn là tích vô hướng, và bình phương khoảng cách L2 bằng $2(1 - \cos)$. Vì vậy trên vector đã chuẩn hóa, xếp hạng theo L2 và theo cosine cho cùng kết quả.
Bài tập nhanh 3. Người dùng $u = (1, 1)$ và ba item $(1,0)$, $(0,1)$, $(1,1)$. Xếp hạng theo cosine rồi cho biết item nào "an toàn" nhất khi người dùng chỉ mới xem một lần.
Xem đáp án
$\cos$ với $(1,0)$ và $(0,1)$ đều bằng $1/\sqrt{2} \approx 0.707$; với $(1,1)$ bằng 1. Thứ hạng: $(1,1)$ trước, hai item còn lại đồng hạng. Điểm cần thấy: hai item một chiều có cùng hướng tương đối với người dùng, nên cosine không phân biệt được chúng nếu không có thêm tín hiệu. Thực tế thường phá hòa bằng độ phổ biến hoặc doanh thu.
Ba cái bẫy khi dùng cosine ngoài đời thật
- Vector toàn số 0. Cosine chia cho 0 nên không xác định. Hãy kiểm tra chuẩn trước và trả về 0 (hoặc bỏ cặp đó) thay vì để chương trình nổ.
- Tưởng cosine phạt được tài liệu nhồi từ khóa. Nó không phạt, vì nhồi cùng một từ chỉ kéo dài vector theo đúng hướng cũ. Muốn phạt, phải xử lý ở khâu tạo vector.
- Quên rằng cosine có thể âm. Nếu bạn lọc "liên quan" bằng ngưỡng 0, hãy nhớ 0 nghĩa là vuông góc chứ không phải "hơi liên quan". Và đừng dùng cosine khi độ lớn thật sự quan trọng, ví dụ so sánh mức tiêu thụ điện hay số km đã đi, lúc đó L1 hoặc L2 mới đúng.
Một quy tắc ngón tay: nếu bạn từng nghĩ "chuẩn hóa dữ liệu đi rồi hẵng so", khả năng cao cosine là độ đo bạn đang tìm.
Ngoài ra, hãy tự kiểm tra bằng cloze dưới đây trước khi sang bài checkpoint:
- Cosine similarity bằng _____ chia cho tích hai chuẩn L2.
- Giá trị cosine nằm trong khoảng _____, trong đó 1 nghĩa là hai vector _____.
- Hai vector vuông góc có cosine bằng _____.
- cosine distance được tính bằng _____.
- Trên vector đã chuẩn hóa L2, bình phương khoảng cách L2 bằng _____.
Ghi nhớ nhanh và bước tiếp theo
- Cosine similarity đo góc, không đo độ dài: công thức là tích vô hướng chia tích hai chuẩn L2.
- Giá trị trong $[-1, 1]$; càng gần 1 càng giống hướng, 0 là vuông góc.
- Tìm kiếm văn bản và gợi ý là hai bài toán mà cosine tỏa sáng, vì độ dài tài liệu hay mức độ hoạt động của người dùng không nên làm lệch kết quả.
- Khi độ lớn có ý nghĩa thật, quay lại L1 hoặc L2.
- Chuẩn hóa vector trước là cách nhanh nhất để biến cosine thành một phép dot product.
Bài tiếp theo là checkpoint của module: bạn sẽ tự tính chuẩn L1, L2 và cosine trên vài ví dụ, rồi quyết định độ đo nào phù hợp cho từng tình huống cụ thể.
Front: Công thức cosine similarity giữa hai vector là gì? Back: Tích vô hướng chia cho tích hai chuẩn L2: (a·b) / (||a||₂ · ||b||₂).
Front: Cosine similarity nhận giá trị trong khoảng nào? Back: Từ -1 đến 1.
Front: cos(a,b) = 1 có nghĩa là gì? Back: Hai vector cùng hướng, giống nhau tối đa.
Front: cos(a,b) = 0 có nghĩa là gì? Back: Hai vector vuông góc, không chia sẻ hướng nào.
Front: cos(a,b) âm có nghĩa là gì? Back: Góc lớn hơn 90 độ, hai vector hướng ngược nhau.
Front: cosine distance được tính thế nào? Back: 1 trừ cosine similarity.
Front: Vì sao cosine phù hợp với tìm kiếm văn bản? Back: Vì nó bỏ qua độ dài tài liệu, chỉ so sánh hướng của vector từ.
Front: Khi nào nên dùng L1 hoặc L2 thay cho cosine? Back: Khi độ lớn của vector có ý nghĩa thật, ví dụ số lượng, quãng đường, mức tiêu thụ.
Front: Tài liệu dài gấp đôi với cùng nội dung thì cosine với query thay đổi ra sao? Back: Không đổi, vì nhân vector với một số dương không làm đổi góc.
Front: Cosine của một vector với vector toàn số 0 bằng bao nhiêu? Back: Không xác định, vì phép chia cho chuẩn bằng 0.
Front: Trên vector đã chuẩn hóa L2, bình phương khoảng cách L2 liên hệ với cosine thế nào? Back: Bằng 2(1 - cos), nên xếp hạng theo L2 và cosine là như nhau.
Front: Quy trình gợi ý item cho một người dùng bằng cosine gồm mấy bước? Back: Tính cosine giữa vector người dùng và từng item, rồi sắp xếp giảm dần và lấy top N.
Front: Cosine similarity khác dot product ở chỗ nào? Back: Cosine chia thêm cho tích hai chuẩn, nên không phụ thuộc độ dài vector.