Chuyển tới nội dung chính

Naive Bayes cho văn bản

Sửa tập huấn luyệnBảng từng từThanh alpha Laplace

Naive Bayes cho văn bản

Một bộ phân loại chỉ biết đếm từ, cộng vài con số âm lại, rồi tuyên bố câu này khen hay chê. Mọi bước đều nhỏ tới mức bạn tính tay lại được, và chỗ duy nhất đáng ngờ nằm ngay trong cái tên: ngây thơ.

Định lý Bayes tổng quát, tiên nghiệm, khả năng và hậu nghiệm đã có một bài riêng trong môn Trí tuệ nhân tạo: Phân loại Naive Bayes, với bộ lọc thư rác ba thanh trượt. Bài đó dạy nền, và nếu bạn chưa chắc P(A | B) khác P(B | A) chỗ nào thì nên đọc nó trước. Bài này đi tiếp một bước cụ thể: Naive Bayes đa thức trên văn bản thật, nơi khả năng không phải một con số bạn kéo bằng tay mà là hàng chục phân số đếm được từ chính mấy câu bạn gõ vào.

Công thức chỉ có ba tầng. Tiên nghiệm P(lớp) là tỉ lệ số câu mang nhãn đó. Khả năng của từng từ là P(từ | lớp) = (count(từ, lớp) + alpha) / (total(lớp) + alpha × |V|), trong đó count là số lần từ xuất hiện trong lớp, total(lớp) là tổng mọi số đếm của lớp, |V| là cỡ từ vựng và alpha là lượng làm mượt Laplace. Điểm của một lớp là log P(lớp) cộng tất cả log P(từ | lớp), mỗi từ cộng đúng số lần nó xuất hiện. Lớp nào tổng lớn hơn thì thắng.

Naive Bayes đa thức · phân loại văn bản bằng cách cộng log
Đoán tích cực 98.27%
tích cực98.27%3 câu · tiên nghiệm 0.4286 · total = 26
tiêu cực1.73%4 câu · tiên nghiệm 0.5714 · total = 30
|V| = 28số câu huấn luyện = 7alpha = 1.00đa thức, đếm số lầnlog cơ số e
Từsố lầntích cựctiêu cựcnghiêng về
đếmP(từ | lớp)góp logđếmP(từ | lớp)góp log
phim13(3 + 1.00) / (26 + 1.00 × 28) = 0.07407-2.60272(2 + 1.00) / (30 + 1.00 × 28) = 0.05172-2.9618tích cực 0.359
này12(2 + 1.00) / (26 + 1.00 × 28) = 0.05556-2.89042(2 + 1.00) / (30 + 1.00 × 28) = 0.05172-2.9618tích cực 0.071
rất13(3 + 1.00) / (26 + 1.00 × 28) = 0.07407-2.60271(1 + 1.00) / (30 + 1.00 × 28) = 0.03448-3.3673tích cực 0.765
hay23(3 + 1.00) / (26 + 1.00 × 28) = 0.07407-5.20540(0 + 1.00) / (30 + 1.00 × 28) = 0.01724-8.1209tích cực 2.916
11(1 + 1.00) / (26 + 1.00 × 28) = 0.03704-3.29581(1 + 1.00) / (30 + 1.00 × 28) = 0.03448-3.3673tích cực 0.071
diễn12(2 + 1.00) / (26 + 1.00 × 28) = 0.05556-2.89042(2 + 1.00) / (30 + 1.00 × 28) = 0.05172-2.9618tích cực 0.071
viên11(1 + 1.00) / (26 + 1.00 × 28) = 0.03704-3.29581(1 + 1.00) / (30 + 1.00 × 28) = 0.03448-3.3673tích cực 0.071
cũngngoài từ vựngbỏ qua0không chấm00không chấm0không lệch
Dây chuyền cộng logtích cựctiêu cực
log P(lớp) tiên nghiệm-0.8473-0.5596
Σ số lần × log P(từ | lớp)-22.7832-27.1083
Tổng log của mỗi lớp-23.6305-27.6679
Hậu nghiệm sau khi chuẩn hoá0.9826630.017337
Hiệu hai tổng log, tích cực trừ tiêu cực4.0374Chỉ hiệu này quyết định người thắng. Cộng hai hậu nghiệm lại được 1.000000000000, đúng 1 trong sai số máy, vì chuẩn hoá chỉ là chia cho tổng.
Không ô P(từ | lớp) nào bằng 0, vì alpha = 1.00 lớn hơn 0 nên tử số nhỏ nhất vẫn là 1.00. Đó là toàn bộ công việc của làm mượt Laplace: dời mọi ô ra khỏi số 0.
1 từ trong câu không có mặt trong tập huấn luyện: cũng. Quy ước ở đây là không chấm chúng. Nếu chấm thì mỗi lớp vẫn nhận được một thừa số alpha / (total + alpha × |V|), mà total hai lớp khác nhau nên từ hoàn toàn xa lạ sẽ âm thầm đẩy về lớp có total nhỏ hơn. Bỏ qua chúng là lựa chọn phổ biến, không phải chân lý.
Hai mô hình đặt cạnh nhau, cùng alpha
đang bật · đếm số lầntích cực 98.27% · tiêu cực 1.73%
chỉ đánh dấu có mặttích cực 91.94% · tiêu cực 8.06%
Hai dòng này không phải hai cách trình bày một con số. Nhị phân hoá đổi cả count(từ, lớp) lẫn total(lớp) khi huấn luyện, và đổi luôn số thừa số của câu cần phân loại, nên đó là một mô hình khác. Hiện total của hai lớp là 26 và 30 ở mô hình đang bật, còn ở mô hình kia là 24 và 29. Lưu ý chỉ đánh dấu có mặt ở đây mới là bản nhị phân hoá, chưa phải Bernoulli Naive Bayes đầy đủ: Bernoulli còn nhân thêm thừa số 1 - P(từ | lớp) cho mọi từ trong từ vựng không xuất hiện trong câu, và sim này không tính phần đó.

Đọc bảng theo từng từ

Ở trạng thái mặc định, tập huấn luyện có 7 câu: 3 câu nhãn tích cực và 4 câu nhãn tiêu cực. Từ vựng gộp cả hai lớp có |V| = 28 từ. Lớp tích cực có total = 26 token, lớp tiêu cực có total = 30. Tiên nghiệm vì thế là 3/7 tức 0,42864/7 tức 0,5714, nên trước khi đọc một chữ nào của câu thử, mô hình đã hơi nghiêng về phía tiêu cực.

Mỗi dòng bảng là một phép chia bạn kiểm lại được. Lấy từ hay: nó xuất hiện 3 lần trong lớp tích cực và 0 lần trong lớp tiêu cực, nên với alpha = 1 ta có P(hay | tích cực) = (3+1)/(26+28) = 4/54 = 0,07407P(hay | tiêu cực) = (0+1)/(30+28) = 1/58 = 0,01724. Câu thử chứa hay hai lần, nên nó góp 2 × ln(0,07407) = -5,2054 vào lớp tích cực và 2 × ln(0,01724) = -8,1209 vào lớp tiêu cực. Chênh lệch 2,9155 là đóng góp lớn nhất của bất kỳ từ nào, và đó là dòng duy nhất được tô nổi ở trạng thái mặc định. Cột nghiêng về chỉ in ba chữ số nên bạn thấy 2,916 ở đó.

Từ cũng không có trong câu huấn luyện nào nên nằm ngoài từ vựng. Sim in nó ra rồi ghi rõ không chấm. Đây là một quy ước, không phải định lý, và mục cuối bài nói vì sao.

Cộng hết lại: lớp tích cực được -0,8473 + (-22,7832) = -23,6305, lớp tiêu cực được -0,5596 + (-27,1083) = -27,6679. Hiệu hai số là 4,0374, và chỉ hiệu này quyết định. Chuẩn hoá bằng cách lấy luỹ thừa rồi chia cho tổng, ta được hậu nghiệm 98,27% cho tích cực và 1,73% cho tiêu cực.

Một chi tiết dễ bỏ qua nhưng nói lên nhiều: các từ , này, diễn, viên đều có số đếm bằng nhau ở hai lớp, vậy mà cả bốn vẫn nghiêng nhẹ về phía tích cực, bảng in 0,071 cho cả bốn. Không có gì huyền bí, đó đúng bằng ln(58/54) = 0,07146: cùng tử số mà mẫu số lớp tích cực nhỏ hơn thì phân số lớn hơn. Lớp có ít chữ hơn được thưởng một chút cho mọi từ. Với ngữ liệu thật chênh lệch này bị các từ đặc trưng lấn át, nhưng với 7 câu thì nó vẫn nhìn thấy được.

Vì sao cộng log chứ không nhân xác suất

Về mặt toán, hai cách như nhau: log của một tích là tổng các log, và hàm log tăng nghiêm ngặt nên thứ tự giữa hai lớp không đổi. Cộng log là chuyện của máy tính chứ không phải của công thức.

Con số cụ thể: ở trạng thái mặc định, tích thật của lớp tích cực là 5,463e-11 và của lớp tiêu cực là 9,638e-13. Câu thử mới có 7 từ được chấm mà đã xuống cỡ đó. Số thực dấu phẩy động 64 bit chỉ giữ được tới khoảng 1e-308, nên một văn bản vài trăm từ là tích chạm sàn và biến thành đúng 0. Lúc đó cả hai lớp cùng bằng 0, tỉ lệ giữa chúng thành 0/0, và bộ phân loại không còn gì để so.

Cổng kiểm số của bài này dựng lại đúng cảnh đó: nối 60 bản sao câu thử mặc định thành một văn bản dài, tính bằng phép nhân trực tiếp thì cả hai lớp về 0 và kết quả hoà 50/50 vô nghĩa, còn tính bằng cộng log thì hai tổng là -1367,84-1627,06, hai con số rất bình thường, và hậu nghiệm vẫn tách bạch. Đó là toàn bộ lý do của chữ log: tránh tràn số dưới, chứ không phải để công thức trông học thuật hơn.

Bước chuẩn hoá cũng phải cẩn thận. Từ -1367,84 mà lấy exp thẳng thì lại ra 0. Sim trừ đi số lớn nhất trước rồi mới lấy exp, mẹo quen thuộc tên là log-sum-exp. Dòng chữ dưới bảng in ra tổng hai hậu nghiệm để bạn thấy nó bằng 1, thay vì phải tin.

Thanh alpha, và cái chết ở alpha = 0

Kéo alpha về đúng 0. Ô P(hay | tiêu cực) trở thành (0+0)/(30+0) = 0 chằn chặn. Một thừa số 0 làm cả tích bằng 0, tương đương log 0 = -∞, nên tổng log của lớp tiêu cực là -∞ và hậu nghiệm của nó rơi xuống đúng 0. Lớp tích cực nhận trọn 100%. Bốn câu tiêu cực trong ngữ liệu vừa bị một từ duy nhất xoá sổ.

Đó là lý do làm mượt tồn tại, và cũng là lý do nó phải là cộng vào tử số chứ không phải chỉnh sửa gì khác: chỉ cần alpha lớn hơn 0 thì tử số nhỏ nhất vẫn là alpha, nên không ô nào chạm 0 nữa. Cộng alpha × |V| vào mẫu số là để mỗi hàng vẫn cộng lại đúng 1 trên toàn từ vựng, tức nó vẫn là một phân phối xác suất chứ không phải một đống số bị đẩy lên tuỳ tiện.

Kéo alpha lên 2 thì hậu nghiệm tụt từ 98,27% xuống 93,46%. Làm mượt nhiều hơn nghĩa là kéo mọi ô về gần phân phối đều hơn, bằng chứng từ dữ liệu bị pha loãng, và mô hình bớt tự tin. Không có giá trị alpha nào đúng sẵn: alpha = 1 là mặc định phổ biến vì gọn, còn chọn đúng thì phải dò trên tập kiểm định.

Có một trường hợp alpha = 0 không cứu được bằng cách nào: nếu mọi lớp đều nhận xác suất 0 thì không còn tỉ lệ nào để so. Sim quy ước chia đều rồi nói thẳng ra bằng chữ, thay vì để NaN lọt lên màn hình. Đó là định nghĩa cho gọn chứ không phải mô hình đang bảo hai lớp ngang nhau.

Đếm số lần khác với chỉ đánh dấu có mặt

Công tắc Mô hình đặc trưng không phải một tuỳ chọn hiển thị. Hai vị trí của nó là hai mô hình khác nhau, huấn luyện ra hai bảng số đếm khác nhau:

  • đếm số lần là Naive Bayes đa thức. Câu phim này hay và diễn viên diễn rất hay đóng góp hay hai lần và diễn hai lần vào lớp của nó. Khi phân loại, một từ xuất hiện hai lần cũng nhân thừa số của nó hai lần.
  • chỉ đánh dấu có mặt là bản nhị phân hoá, cách đơn giản hoá thường được đặt cạnh Bernoulli Naive Bayes. Mỗi câu chỉ đóng góp tối đa 1 cho mỗi từ, cả khi huấn luyện lẫn khi phân loại.

Bật công tắc sang phải và nhìn hai con số đổi ngay: total của hai lớp tụt từ 2630 xuống 2429, còn hậu nghiệm tụt từ 98,27% xuống 91,94%. Từ hay giờ chỉ được chấm một lần thay vì hai, và trong lớp tích cực nó chỉ còn số đếm 2 thay vì 3, vì hai lần xuất hiện trong cùng một câu bị gộp làm một. Khối Hai mô hình đặt cạnh nhau ở cuối sim in cả hai kết quả cùng lúc để bạn khỏi phải bật qua bật lại.

Bản nhị phân hoá này vẫn chưa phải Bernoulli Naive Bayes đầy đủ. Bernoulli thật còn nhân thêm thừa số 1 - P(từ | lớp) cho mọi từ trong từ vựng không xuất hiện trong văn bản, tức nó tính cả sự vắng mặt như một bằng chứng. Sim này không làm phần đó. Cái nó cho bạn thấy là chuyện hẹp hơn nhưng vẫn đáng: chỉ cần đổi cách đếm, mọi số trong bảng đổi theo.

Chữ "ngây thơ" nằm ở đâu

Xác suất đúng của cả câu khi biết lớp là P(từ_1, từ_2, ..., từ_n | lớp), một đại lượng không cách nào ước lượng được: số tổ hợp câu là vô hạn, còn ngữ liệu thì hữu hạn. Naive Bayes cắt nút đó bằng một giả định: các từ độc lập với nhau khi đã biết lớp, nên xác suất của cả câu bằng tích xác suất từng từ. Chính phép nhân trong bảng là hình dạng của giả định ấy.

Giả định đó sai, và sai một cách hiển nhiên. Trong ngữ liệu mặc định, diễnviên gần như luôn đi cùng nhau; biết câu có diễn là gần như chắc chắn có viên. Mô hình vẫn coi chúng là hai bằng chứng độc lập và nhân cả hai vào, tức nó đếm cùng một thông tin hai lần.

Hậu quả nhìn thấy được ngay trong sim: hậu nghiệm 98,27% là quá tự tin so với những gì 7 câu ngắn có thể chứng minh. Naive Bayes nổi tiếng là cho ra hậu nghiệm dồn về sát 0 hoặc sát 1, và bằng chứng tương quan bị nhân nhiều lần chính là nguyên nhân. Vậy mà nó vẫn hữu ích, vì để phân loại đúng ta chỉ cần thứ tự giữa hai điểm số đúng, không cần độ lớn của chúng đáng tin. Đếm hai lần một bằng chứng thường đẩy cả hai lớp lệch cùng chiều, nên người thắng hay giữ nguyên dù con số in ra thì không dùng được. Nếu bạn cần chính con số hậu nghiệm, chẳng hạn để đặt ngưỡng chi phí, thì phải hiệu chỉnh lại chứ đừng lấy thẳng.

Những quy ước tôi đã chọn

Sim này phải chốt vài thứ mà sách vở làm khác nhau, và mỗi lựa chọn đều làm đổi con số:

  • Từ ngoài từ vựng không được chấm. Nếu chấm, mỗi lớp vẫn nhận một thừa số alpha / (total + alpha × |V|), mà total của hai lớp khác nhau, nên một từ hoàn toàn xa lạ sẽ âm thầm đẩy về lớp có total nhỏ hơn. Bỏ qua chúng là lựa chọn phổ biến, không phải chân lý. Hệ quả kiểm lại được: bấm nút Câu toàn từ lạ, không từ nào được chấm, và hậu nghiệm rơi đúng về tiên nghiệm 42,86% với 57,14%.
  • Tiên nghiệm không được làm mượt. P(lớp) là tỉ lệ thô. Một nhãn không có câu nào sẽ có tiên nghiệm 0 và log của nó là -∞.
  • Tách token là cắt theo khoảng trắng và bỏ dấu câu. Nên bộ phim thành hai token, diễn viên thành hai token. Tách theo từ ghép sẽ cho |V| khác, total khác, và mọi con số trên đổi theo.
  • Một dòng như tiêu cực: không có chữ nào vẫn là một văn bản. Nó có nhãn và không có từ nào, nên nó đẩy tiên nghiệm mà không góp số đếm nào.
  • Nhãn được chuẩn hoá về chữ thường. Số lớp là số nhãn khác nhau bạn gõ, nên bài này không bị khoá ở hai lớp, nhưng mọi diễn giải trên đây viết cho trường hợp hai lớp.

Nhắc lại điểm yếu, lần này kèm con số. Bảy câu ngắn với |V| = 28 là một đồ chơi, không phải một thí nghiệm. Từ hay một mình quyết định gần hết kết quả chỉ vì nó tình cờ vắng mặt hoàn toàn ở lớp kia; thêm một câu tiêu cực có chữ hay là hiệu 4,0374 sụt ngay. Mọi thứ tự lớn nhỏ bạn thấy ở đây chỉ chắc chắn ở đây, và cách duy nhất để biết nó còn đúng chỗ khác không là chạy lại ở chỗ khác với ngữ liệu đủ lớn.

Điều rút ra

Naive Bayes cho văn bản chỉ là ba tầng: đếm, làm mượt, cộng log. Làm mượt Laplace không phải trang trí, ở alpha = 0 một từ chưa gặp trong một lớp đủ sức xoá sổ cả lớp đó. Cộng log thay vì nhân xác suất là để tránh tràn số dưới, không đổi kết quả nhưng đổi chuyện chương trình có chạy được hay không. Đếm số lần và chỉ đánh dấu có mặt là hai mô hình khác nhau chứ không phải hai cách in cùng một bảng. Còn chữ "ngây thơ" nằm ở giả định các từ độc lập khi biết lớp: giả định ấy sai với ngôn ngữ thật, làm hậu nghiệm quá tự tin, nhưng thường không làm sai thứ tự, nên bộ phân loại vẫn dùng được.

Câu hỏi tự kiểm0/4 đúngchưa trả lời
  1. 1Trong sim, bạn kéo alpha về đúng 0 và hậu nghiệm của lớp tiêu cực rơi xuống 0 tuyệt đối. Cách giải thích nào đúng?
  2. 2Vì sao Naive Bayes cộng log xác suất thay vì nhân trực tiếp các xác suất?
  3. 3Trong sim, chuyển công tắc từ "đếm số lần" sang "chỉ đánh dấu có mặt" làm total của hai lớp tụt từ 26 và 30 xuống 24 và 29. Điều đó nói lên gì?
  4. 4Chữ "ngây thơ" trong Naive Bayes chỉ đúng vào giả định nào?