Một từ, nhiều nghĩa, và hàng xóm của nó
Một từ, nhiều nghĩa, và hàng xóm của nó
Chữ đường trong con đường và chữ đường trong ly cà phê là cùng một chuỗi ký tự. Máy không có cách nào tách chúng ra nếu chỉ nhìn vào bản thân chữ đó. Nhưng nhìn sang mấy từ đứng cạnh thì mọi thứ sáng ra ngay.
Bài trước biểu diễn cả câu bằng một vector đếm. Bây giờ ta thu nhỏ ống kính lại: thay vì đếm mọi từ trong câu, chỉ đếm những từ nằm trong một cửa sổ quanh một từ được chọn. Kết quả là một vector ngữ cảnh, và nó không mô tả câu nữa, nó mô tả cái chỗ mà từ đó đang đứng.
Ý tưởng phía sau chỉ có một dòng: hai lần xuất hiện của cùng một chữ mà có hàng xóm giống nhau thì nhiều khả năng đang mang cùng một nghĩa. Ngữ liệu mặc định dưới đây có mười câu, mỗi câu chứa chữ đường một lần, năm câu theo nghĩa lối đi và năm câu theo nghĩa chất ngọt. Bạn gán nghĩa thật cho từng câu bằng phần đứng trước dấu gạch đứng, sim không hề dùng nhãn đó để tính toán, nó chỉ dùng để chấm điểm ở cuối.
0.435 − 0.004| # | Nghĩa thật | Cụm máy chia | Câu | Số hàng xóm |
|---|---|---|---|---|
| 1 | lối đi | Cụm 2 | con đường này xe chạy suốt ngày rất ồn | 5 |
| 2 | lối đi | Cụm 2 | chiều nay con đường ra biển xe chạy rất đông | 7 |
| 3 | lối đi | Cụm 2 | người ta vừa trải nhựa con đường này nên xe chạy êm | 8 |
| 4 | lối đi | Cụm 2 | mưa lớn làm con đường này ngập nước xe chạy không nổi | 8 |
| 5 | lối đi | Cụm 2 | từ chợ về nhà chỉ có một con đường nhỏ xe chạy khó | 8 |
| 6 | chất ngọt | Cụm 1 | mẹ cho thêm đường vào nồi chè cho ngọt | 7 |
| 7 | chất ngọt | Cụm 1 | pha cà phê nhớ cho thêm đường vào cho ngọt | 7 |
| 8 | chất ngọt | Cụm 1 | bác sĩ khuyên cho ít đường vào nước cam cho đỡ ngọt | 8 |
| 9 | chất ngọt | Cụm 1 | chị ấy thích cho nhiều đường vào ly trà cho thật ngọt | 8 |
| 10 | chất ngọt | Cụm 1 | bánh này người ta rắc thêm đường lên mặt cho ngọt hơn | 8 |
| câu | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1lối đi | 1.00 | 0.51 | 0.63 | 0.47 | 0.47 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 2lối đi | 0.51 | 1.00 | 0.40 | 0.27 | 0.40 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 3lối đi | 0.63 | 0.40 | 1.00 | 0.37 | 0.37 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 4lối đi | 0.47 | 0.27 | 0.37 | 1.00 | 0.25 | 0.00 | 0.00 | 0.11 | 0.00 | 0.00 |
| 5lối đi | 0.47 | 0.40 | 0.37 | 0.25 | 1.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 6chất ngọt | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 1.00 | 0.67 | 0.53 | 0.53 | 0.35 |
| 7chất ngọt | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.67 | 1.00 | 0.53 | 0.53 | 0.47 |
| 8chất ngọt | 0.00 | 0.00 | 0.00 | 0.11 | 0.00 | 0.53 | 0.53 | 1.00 | 0.50 | 0.22 |
| 9chất ngọt | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.53 | 0.53 | 0.50 | 1.00 | 0.22 |
| 10chất ngọt | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.35 | 0.47 | 0.22 | 0.22 | 1.00 |
| Cụm | Số câu | Nghĩa chiếm đa số trong cụm | Câu khớp với nghĩa đó |
|---|---|---|---|
| Cụm 1 | 5 | chất ngọt | 5/5 |
| Cụm 2 | 5 | lối đi | 5/5 |
Hãy làm bốn việc trong sim, theo thứ tự.
Một: nhìn bản đồ nhiệt ở trạng thái vừa mở. Cửa sổ đang là ±4 từ, tức bốn từ mỗi bên. Bảng cosine hiện lên hai khối sáng nằm gọn trên đường chéo, năm câu đầu một khối và năm câu sau một khối, còn hai góc chéo còn lại thì trắng gần như hoàn toàn. Con số đi kèm: cosine trung bình của các cặp cùng nghĩa là 0.435 trên 20 cặp, của các cặp khác nghĩa là 0.004 trên 25 cặp, chênh nhau 0.431. Cái 0.004 đó không phải làm tròn từ một đám số nhỏ, nó đến từ đúng một cặp duy nhất trong 25 cặp có cosine khác 0: câu 4 và câu 8, cả hai đều có chữ nước rơi vào cửa sổ, cho ra 0.11. Hai mươi bốn cặp còn lại bằng 0 chằn chặn.
Điều đáng chú ý là không câu nào trong nhóm lối đi chung với câu nào một cụm từ dài, chúng chỉ chung mấy từ lẻ như con, xe, chạy. Vậy mà chừng đó đã đủ. Cặp câu 1 và câu 3 chung bốn từ hàng xóm và cho cosine 0.63; cặp câu 6 và câu 7 chung cho hai lần cộng thêm và vào, cho 0.67.
Phải nói ngay ở đây, chứ không để tới cuối bài: hai khối đẹp như vậy là vì tôi cố ý viết ngữ liệu như vậy. Mười câu này được dựng sao cho các câu cùng nghĩa dùng lại chung vài từ ngay sát chữ đường, nên tín hiệu mạnh hơn nhiều so với văn bản thật. Trên văn bản thật một chữ đa nghĩa cần hàng nghìn lần xuất hiện mới mong có được bảng sạch cỡ này, và ngay cả khi đó hai khối cũng lem vào nhau nhiều hơn. Hãy đọc bảng dưới như một minh hoạ cho cơ chế, không phải như bằng chứng rằng phương pháp này chạy ngon.
Hai: đổi ô Xếp hàng theo sang theo nghĩa thật rồi sang theo cụm máy chia. Ô này không tính lại gì cả, nó chỉ đổi chỗ ngồi của các hàng và cột. Với ngữ liệu mặc định thì hai cách xếp cho ra cùng một hình, và đó chính là tin tốt: cụm mà máy chia trùng khít với nghĩa mà bạn gán, chỉ số khớp là 10/10. Máy chưa từng nhìn thấy nhãn của bạn.
Cách chia đó do một cài đặt k-means rất gọn làm ra, k bằng 2, viết tay trong engine.ts, không dùng thư viện và không gọi hàm ngẫu nhiên lần nào. Hai hạt giống là cặp câu xa nhau nhất trong bảng, ở đây là câu 8 và câu 2 với cosine 0, rồi lặp hai vòng là nhãn đứng yên. Vì không có ngẫu nhiên nên chạy lại bao nhiêu lần cũng ra một kết quả, và vì thuật toán sắp xếp lại danh sách theo một thứ tự chuẩn trước khi chạy nên đổi thứ tự các dòng trong ô ngữ liệu cũng không đổi cách chia. Bạn thử đảo hai dòng bất kỳ rồi so lại là thấy.
Nhớ giùm một chi tiết ở đây, lát nữa sẽ cần tới. Ngay ở cửa sổ mặc định này thì "cặp xa nhau nhất" đã không phải một cặp duy nhất rồi: có tới 24 cặp cùng bằng 0, và thuật toán chỉ lấy cặp nó gặp đầu tiên. Lần này cặp đó tình cờ là hai câu khác nghĩa nên mọi thứ trơn tru. Đó là may, không phải là bảo đảm.
Ba: kéo cửa sổ về ±1 từ. Bây giờ mỗi câu chỉ còn hai từ hàng xóm. Tương phản khối tăng lên 0.550 và ô cosine trung bình, khác nghĩa về đúng 0.000, nhìn bảng thì hai khối còn sạch hơn cả lúc nãy. Nhưng chỉ số khớp cụm tụt xuống 6/10 và hai cụm chia thành 9 với 1. Đây là chỗ đáng dừng lại lâu nhất trong cả bài.
Chuyện xảy ra như sau, và tôi kể chi tiết vì chỗ này rất dễ giải thích ẩu. Cửa sổ hẹp làm mỗi vector chỉ còn hai chiều, nên bảng cosine đầy số 0: cả 25 cặp khác nghĩa đều bằng 0, cộng thêm 2 cặp cùng nghĩa cũng bằng 0 là câu 8 với câu 10 và câu 9 với câu 10. Tổng cộng 27 trên 45 cặp cùng nằm ở đáy. Lưu ý là chỉ có 2 cặp cùng nghĩa rơi về 0 thôi, không phải phần lớn.
Vấn đề nằm ở chỗ tiếp theo. Khi 27 cặp cùng bằng 0 thì "cặp xa nhau nhất" không còn là một cặp xác định nữa, nó là một thế hoà 27 bên, và người quyết định là luật gỡ hoà mà tôi tự đặt trong engine.ts: lấy cặp nhỏ nhất gặp đầu tiên khi duyệt danh sách đã sắp theo thứ tự chữ cái. Hai câu đứng đầu bảng chữ cái là câu 8 (bác sĩ...) và câu 10 (bánh này...), cặp đầu tiên được duyệt cũng chính là chúng, và cosine của chúng đã bằng 0 rồi nên chúng thắng ngay. Cả hai đều là nghĩa chất ngọt.
Hỏng từ hạt giống thì phần còn lại đổ theo, và đổ một cách khá thảm: đo lại thì 7 trên 10 câu cách đều đúng bằng nhau tới cả hai hạt giống, trong đó cả năm câu lối đi đều có cosine 0 với cả hai. Chúng không được xếp cụm theo dữ liệu, chúng bị luật hoà đẩy hết vào Cụm 1. Chỉ có câu 10 tự ngồi một mình, ra 9 chọi 1.
Đây là chỗ phải nói cho rõ, kẻo bài học bị nhớ sai: 9 chọi 1 là hậu quả của hai quy ước tôi tự chọn, không phải một quy luật của k-means. Bằng chứng: giữ nguyên toàn bộ dữ liệu, chỉ thêm một chữ vào đầu câu 5 ở xa chữ đường nên không vector nào, không ô cosine nào và cả tương phản 0.550 cũng không đổi một chút nào, nhưng câu đó nhảy lên đầu bảng chữ cái nên hạt giống trở thành một cặp khác nghĩa. Kết quả quay lại 5 chọi 5 và 10/10 ngay. Ép bất kỳ cặp khác nghĩa nào làm hạt giống cũng cho đúng như vậy. Cấu trúc chưa từng rời khỏi dữ liệu, chỉ là cách khởi tạo của tôi không với tới nó.
Bài học rút ra vẫn còn nguyên giá trị, chỉ cần phát biểu cho đúng cỡ: một chỉ số tổng hợp đẹp lên không bảo đảm việc phía sau nó chạy tốt hơn, phải nhìn cả hai. Và khi bước phía sau hỏng, hãy hỏi xem nó hỏng vì dữ liệu hay vì một quy ước cài đặt của chính mình.
Bốn: bấm đá: ba nghĩa nhưng k vẫn bằng 2. Ngữ liệu này có ba nghĩa thật là hòn đá, nước đá và đá bóng, nhưng thuật toán chỉ được phép chia hai. Nó buộc phải gộp, và nó gộp nhóm hòn đá với nhóm đá bóng thành một cụm bốn câu, để riêng hai câu nước đá. Chỉ số khớp chốt ở 4/6, mức trần cho tình huống này. Không có gì sai trong cài đặt, chỉ là bạn đã hỏi sai câu hỏi: số cụm là thứ bạn đưa vào, không phải thứ thuật toán tìm ra.
Vài chuyện phải nói thẳng, kẻo bài học này bị nhớ to hơn cỡ thật của nó:
- Túi từ của cửa sổ vứt bỏ thứ tự. Ở cửa sổ mặc định
±4 từ, câuxe chạy trên đườngvà câuđường chạy trên xecho ra hai vector ngữ cảnh giống hệt nhau, cùng làxe,chạy,trênmỗi từ một lần, nên cosine bằng đúng1dù câu thứ hai chẳng có nghĩa gì. Điều đó đúng từ cửa sổ±2trở lên; riêng ở±1thì hai câu chỉ còntrênvàchạynên chúng lại rơi về 0, một kiểu đúng vì lý do sai. Dù sao thì mọi thông tin về việc từ nào đứng trước từ nào cũng đã bị ném đi ngay bước đầu tiên. - Mười câu là quá ít và cụm rất dễ vỡ. Bấm
ngữ liệu quá bé nên cụm vỡđể thấy: bốn câu, mỗi câu nói một kiểu, không cặp nào chung hàng xóm, cả bản đồ nhiệt trắng trơn ngoài đường chéo, tương phản khối đúng0.000, và cách chia 3 với 1 hoàn toàn tuỳ tiện. Nhắc lại điều đã nói ở trên: ngữ liệu mặc định cho khối đẹp là vì tôi cố ý viết những câu có hàng xóm chồng nhau. - Vector rỗng là một trường hợp phải định nghĩa, không được để lọt
NaN. Câu không chứa từ trung tâm, hoặc chỉ có mỗi từ trung tâm mà không có hàng xóm nào, sẽ có vector rỗng. Vector rỗng không có hướng, nên ở đây quy ước mọi cosine liên quan tới nó bằng 0, kể cả ô nằm trên đường chéo, và câu đó không được xếp vào cụm nào thay vì bị nhét bừa. Xoá bớt chữ trong ô ngữ liệu là thấy sim báo ngay. - Vài lựa chọn ở đây là quy ước chứ không phải chân lý. Chọn hạt giống bằng cặp xa nhau nhất, gỡ hoà bằng cách lấy cặp gặp đầu tiên theo thứ tự chữ cái, đẩy câu cách đều hai trọng tâm về Cụm 1, giữ nguyên trọng tâm khi một cụm rỗng: cả bốn đều có thể làm khác đi và cho kết quả khác. Chúng được chọn vì tất định và dễ giải thích, không phải vì tối ưu. Cái
9chọi1ở cửa sổ±1là do đúng hai quy ước đầu tiên trong danh sách này gây ra, nên đừng đọc nó thành một tính chất của k-means.
Từ đây tới vector nhúng chỉ còn đúng một bước tư duy. Ở bài này mỗi từ hàng xóm là một chiều riêng, nên xe hơi và ô tô vẫn là hai chiều không liên quan gì nhau, và hai câu nói cùng một điều bằng hai bộ từ khác nhau vẫn cho cosine 0. Điều người ta làm tiếp theo là ép hàng chục nghìn chiều thưa thớt đó xuống vài trăm chiều đặc, sao cho những từ hay xuất hiện trong cùng loại hàng xóm thì nằm gần nhau. Bản chất tín hiệu vẫn y nguyên là cái bảng bạn đang nhìn, chỉ khác cách nén.
Nghĩa của một từ không nằm trong bản thân từ đó mà nằm trong đám từ đứng quanh nó. Đổi cửa sổ là đổi luôn cái mà máy coi là nghĩa, và một cửa sổ cho tương phản đẹp hơn vẫn có thể làm bước gom cụm phía sau hỏng nặng hơn. Cấu trúc có nằm trong dữ liệu là một chuyện, thuật toán có tìm ra nó hay không là chuyện khác.
- 1Ở trạng thái mặc định, cosine trung bình của các cặp cùng nghĩa là 0.435 còn của các cặp khác nghĩa là 0.004. Con số 0.004 nói lên điều gì?
- 2Chuyển cửa sổ về ±1 từ. Tương phản khối tăng lên 0.550 nhưng chỉ số khớp cụm tụt xuống 6/10. Cách giải thích nào đúng?
- 3Với từ trung tâm là đường và cửa sổ đủ rộng, hai câu xe chạy trên đường và đường chạy trên xe cho ra vector ngữ cảnh như thế nào?