Thành viên:Plantaest/Blog/Phát hiện sửa đổi phá hoại trên Wikipedia tiếng Việt

Bách khoa toàn thư mở Wikipedia

Dưới đây là nội dung của nghiên cứu "Phát hiện sửa đổi phá hoại trên Wikipedia tiếng Việt". Nghiên cứu này có thể được sử dụng để tham khảo cho việc xây dựng các phần mềm chống phá hoại tự động hoặc bán tự động trên Wikipedia tiếng Việt.

Giới thiệu[sửa | sửa mã nguồn]

Wikipedia là bách khoa toàn thư mở trực tuyến đa ngôn ngữ được sáng lập và duy trì bởi một cộng đồng biên tập viên tình nguyện và chạy trên nền tảng wiki. Hiện nay, Wikipedia có đến 309 phiên bản ngôn ngữ khác nhau trên toàn cầu.[1] Trong đó, Wikipedia tiếng Việt thuộc nhóm phiên bản ngôn ngữ có trữ lượng bài viết nhiều nhất thế giới, với 1.267.713 bài viết, 19.298.695 trang nội bộ, 815.944 thành viên có tài khoản và 2.496 thành viên tích cực (có hoạt động trong 30 ngày gần nhất).[2]

Wikipedia có tính mở, nghĩa là phần lớn nội dung của nó đều được cộng đồng đóng góp và tích lũy qua thời gian. Những đóng góp của cộng đồng được quy về khái niệm sửa đổi, đó có thể là hành vi tạo một bài viết mới, chỉnh lại nội dung một đoạn văn, sửa lỗi chính tả hay thêm một liên kết, v.v. Phần lớn các sửa đổi đều có vẻ là hợp lý và có ích cho việc xây dựng một bách khoa toàn thư, được gọi là sửa đổi thiện ý.[3] Nhưng một phần trong các sửa đổi đó được xem là sửa đổi phá hoại.

"Phá hoại là bất kỳ hành động thêm, xóa hoặc sửa đổi nội dung được cố tình thực hiện nhằm làm hại tính toàn vẹn của Wikipedia".[4] Đó có thể là hành vi cố ý tẩy trống nội dung bài viết, thêm từ ngữ tục tĩu, hay phức tạp hơn là chèn các thông tin giả, sai sự thật, bịa đặt một cách tinh vi, điều chỉnh văn phong theo hướng có lợi cho một phe tuyên truyền chính trị, v.v. Phá hoại không chỉ giới hạn tại Wikipedia, mà còn là một hành vi đặc trưng của những nền tảng mở như Facebook, WikiMapia hay OpenStreetMaps.[5]

Mỗi ngày, Wikipedia tiếng Việt đón nhận khoảng 3200 sửa đổi, bao gồm sửa đổi của thành viên có tài khoản (đã đăng nhập) và chưa có tài khoản (dùng IP), theo số liệu lịch sử sửa đổi từ ngày 01/04/2021 đến ngày 30/06/2021.[6] Một ước tính cho thấy khoảng 7% tổng sửa đổi của Wikipedia được xem là phá hoại.[7] Do đó, Wikipedia tiếng Việt có thể có khoảng 200–300 sửa đổi phá hoại mỗi ngày.

Hiển nhiên, mọi hành vi mang tính phá hoại đều sẽ không được chào đón ở bất cứ nơi đâu. Về mặt truyền thống, các sửa đổi trên Wikipedia sẽ được một nhóm các thành viên tình nguyện kiểm tra, đánh giá và hủy bỏ nếu như sửa đổi đó là phá hoại. Một hệ thống kiểm tra bởi con người sẽ gặp phải trở ngại lớn nếu như không đủ người tham gia một công việc không lương thưởng, dù cho là khá chính xác. Vấn đề thiếu nhân lực tình nguyện luôn là một bài toán khó đối với dự án Wikipedia tiếng Việt.[8] Do đó, việc triển khai một hệ thống kiểm tra, đánh giá và phát hiện sửa đổi phá hoại trên cơ sở các mô hình máy học là rất cần thiết.

Hình 1. ORES giúp phân loại sửa đổi phá hoại và thiện ý

Tại Wikipedia tiếng Anh, đã có những công cụ tự động hoặc bán tự động nhằm hỗ trợ phát hiện các sửa đổi phá hoại. Nổi bật nhất có thể kể đến là ClueBot NG, STiki và Snuggle; sử dụng các luật heuristic và thuật toán máy học nhằm dò ra phá hoại.[5] Trong đó, ClueBot NG là công cụ chống phá hoại kinh điển của Wikipedia tiếng Anh,[5] sử dụng các thuật toán máy học nổi tiếng như phân lớp Bayes và mạng neuron nhân tạo; giúp kiểm tra, đánh giá và hủy bỏ các sửa đổi phá hoại một cách tự động.[9] Bên cạnh đó, Quỹ Wikimedia (tổ chức quản lý hệ thống các wiki bao gồm Wikipedia) cũng có một dự án "máy học như một dịch vụ" (machine learning as a service) là ORES, với nhiều mô hình dự báo cho nhiều hoạt động khác nhau, như mô hình tính xác suất khả năng một sửa đổi được xem là phá hoại.[10] Wikipedia tiếng Việt có thể triển khai những công cụ tương tự như của Wikipedia tiếng Anh hay ORES nhằm giải quyết bài toán xử lý phá hoại trong tình trạng thiếu nhân lực.

Mục tiêu[sửa | sửa mã nguồn]

Xây dựng được các mô hình dựa trên các thuật toán máy học, có độ chính xác cao nhằm giải quyết bài toán phát hiện các sửa đổi phá hoại trên Wikipedia tiếng Việt. Đồng thời, rút ra được quy trình cơ bản để xây dựng các mô hình phân lớp.

Giới hạn[sửa | sửa mã nguồn]

  • Dữ liệu (dataset) được lấy từ Wikipedia tiếng Việt.
  • Thuật toán máy học được sử dụng để xây dựng các mô hình gồm có: SVM, Hồi quy logistic, k-Means, Gradient Boosting, Random Forest và k-NN.

Định dạng[sửa | sửa mã nguồn]

Tài liệu này được trình bày theo hướng vừa giải thích từng bước trong quy trình thực hiện nghiên cứu, đồng thời có giá trị báo cáo như một bài báo khoa học.

Các công trình liên quan[sửa | sửa mã nguồn]

Phát hiện phá hoại trên Wikipedia là một chủ đề của nhiều nghiên cứu máy học.

Công trình của Adler và cộng sự (2011) đã nỗ lực tích hợp ba trong số những phương pháp hàng đầu để phát hiện hành vi phá hoại Wikipedia: phân tích không thời gian của siêu dữ liệu (STiki), hệ thống dựa trên uy tín (WikiTrust) và các đặc trưng của quá trình xử lý ngôn ngữ tự nhiên. Kết quả cho thấy hệ thống tích hợp này đã cải thiện hiệu suất của các phương pháp tiền nhiệm và tạo ra cơ sở mới cho việc phát hiện phá hoại trên Wikipedia.[11]

Kumar và cộng sự (2015) đã xây dựng một hệ thống phát hiện sớm những hành vi phá hoại trên Wikipedia, gọi là VEWS.[5] Nhóm tác giả đã thiết lập một bộ dữ liệu từ Wikipedia gọi là UMDWikipedia chứa thông tin về tập người dùng Wikipedia và hành vi của họ. Từ đây, nhóm tác giả tiếp tục phân tích chi tiết các hành vi để phân biệt người dùng phá hoại và người dùng thiện ý. Quá trình này sử dụng các thuật toán phân lớp là SVM, cây quyết định, Random Forest và k-NN. Trong đó, SVM cho độ chính xác tổng quát cao nhất so với những thuật toán còn lại, là 87,82%. Nhóm tác giả sử dụng SVM cho việc xây dựng VEWS. Khi kết hợp VEWS và một số công cụ tương tự đã có (ClueBot NG và STiki) đã giúp tăng độ chính xác tổng quát đến 90,8%.

Với tình trạng có nhiều kiểu phá hoại lén lút khiến nội dung lệch khỏi ngữ cảnh của câu hay bài viết trên Wikipedia, Tran và cộng sự (2015) đã đề xuất một kỹ thuật mới phát hiện phá hoại đa ngôn ngữ và theo ngữ cảnh để giải quyết vấn đề trên, có thể triển khai được trên toàn bộ nội dung Wikipedia và mở rộng ra các loại phá hoại phát hiện được bằng những hướng tiếp cận dựa trên đặc trưng trong quá khứ. Kết quả cho thấy các kỹ thuật phát hiện phá hoại bằng nhận biết ngữ cảnh có thể trở thành một công cụ chống phá hoại mới cho Wikipedia, bổ trợ cho các kỹ thuật dựa trên đặc trưng ở hiện tại.[12]

Susuri và cộng sự (2016) đã nghiên cứu về hành vi phá hoại trên hai bộ dữ liệu về số lượt xem hàng giờ của mỗi bài viết trên Wikipedia và lịch sử sửa đổi của các bài viết, trên hai phiên bản tiếng Anh đơn giản và tiếng Albania của Wikipedia. Kết quả cho thấy các đặc trưng của hành vi phá hoại có thể được học từ các mẫu lượt xem và lịch sử sửa đổi, và những mô hình được xây dựng trên một phiên bản ngôn ngữ có thể được áp dụng thành công cho các phiên bản ngôn ngữ khác.[13]

Tổng quan nghiên cứu[sửa | sửa mã nguồn]

Nội dung nghiên cứu[sửa | sửa mã nguồn]

Xây dựng mô hình phân lớp dựa trên các thuật toán máy học nhằm phát hiện các sửa đổi phá hoại trên Wikipedia tiếng Việt, sử dụng dữ liệu được thu thập từ Wikipedia tiếng Việt là danh sách các bản sửa đổi kèm theo các đặc trưng của nó và biến phân lớp. Sau khi áp dụng mô hình phân lớp cho dữ liệu, có thể dự đoán được một sửa đổi là phá hoại hay không phá hoại. Cuối cùng là đánh giá hiệu quả phát hiện phá hoại giữa các mô hình và đúc kết quy trình xây dựng mô hình phân lớp.

Thuật toán sử dụng trong nghiên cứu[sửa | sửa mã nguồn]

SVM[sửa | sửa mã nguồn]

SVM (Support Vector Machine) là một phương pháp học có giám sát (Supervised Learning) dùng để phân lớp dữ liệu thành các nhóm riêng biệt. Trong thuật toán này, đồ thị dữ liệu là các điểm trong n chiều, sau đó thực hiện tìm hyperplane phân chia các lớp. Hyperplane là một siêu phẳng phân chia các lớp ra thành các phần riêng biệt. Trong một số trường hợp đặc biệt thì SVM có khả năng bỏ qua các dữ liệu ngoại lệ và ra hyperplane có margin tối đa. Do đó có thể nói, SVM có khả năng mạnh trong việc chấp nhận ngoại lệ. Do các hyperplane luôn có margin tối đa nên SVM có thể giảm thiểu việc phân lớp sai (misclassification) đối với điểm dữ liệu mới đưa vào.[14]

Hồi quy logistic[sửa | sửa mã nguồn]

Hồi quy logistic (Logistic Regression) là một phương pháp học có giám sát (Supervised Learning), là một thuật toán phân loại được dùng để gán các đối tượng cho một tập hợp giá trị rời rạc. Thuật toán trên dùng hàm sigmoid logistic để đưa ra đánh giá theo xác suất. Hàm sigmoid là một hàm liên tục và luôn đưa ra giá trị trong khoảng (0, 1). Hồi quy logistic có thể dùng để dự đoán email có phải spam hay không, dự đoán giao dịch ngân hàng là gian lận hay không, dự đoán khối u lành tính hay ác tính, dự đoán khoản vay có trả được không, dự đoán khoản đầu tư có sinh lãi hay không.[15]

k-Means[sửa | sửa mã nguồn]

k-Means là một phương pháp học không giám sát (Unsupervised Learning), là một thuật toán máy học phổ biến dùng để phân cụm. Ý tưởng của thuật toán phân cụm k-Means là phân chia một bộ dữ liệu thành các cụm khác nhau. Trong đó số lượng cụm được cho trước là k. Công việc phân cụm được xác lập dựa trên nguyên lý: Các điểm dữ liệu trong cùng một cụm thì phải có cùng một số tính chất nhất định. Tức là giữa các điểm trong cùng một cụm phải có sự liên quan lẫn nhau. Đối với máy tính thì các điểm trong một cụm đó sẽ là các điểm dữ liệu gần nhau.[16]

Tóm tắt thuật toán:[17]

Đầu vào: Dữ liệu X và số cụm cần tìm K.

Đầu ra: Các điểm center M và label vector cho từng điểm dữ liệu Y.

  • Bước 1: Chọn K điểm bất kì làm center ban đầu.
  • Bước 2: Phân mỗi điểm dữ liệu vào cụm có center gần nhất.
  • Bước 3: Nếu việc gán dữ liệu vào từng cụm ở bước 2 không thay đổi gì so với vòng lặp trước đó thì dừng thuật toán.
  • Bước 4: Cập nhật center cho từng cụm bằng cách lấy trung bình cộng của tất cả các điểm dữ liệu đã được gán vào cụm đó ở bước 2.
  • Bước 5: Quay lại bước 2.

Gradient Boosting[sửa | sửa mã nguồn]

Gradient Boosting là một phương pháp học có giám sát (Supervised Learning) sử dụng cho các bài toán phân lớp và hồi quy, là một kỹ thuật ensemble sử dụng nhiều weak learner để tạo ra một mô hình mạnh mẽ cho nhiệm vụ hồi quy và phân lớp. Thuật toán này dựa trên "trực giác" cho rằng mô hình kế tiếp tốt nhất có thể, khi được kết hợp với các mô hình trước đó, sẽ giảm thiểu các sai số dự đoán tổng thể. Ý tưởng chính là đặt kết quả mục tiêu từ các mô hình trước sang mô hình tiếp theo để giảm thiểu các sai số.[18]

Random Forest[sửa | sửa mã nguồn]

Random Forest là thuật toán học có giám sát (Supervised Learning) có thể giải quyết cả bài toán hồi quy và phân lớp. Thuật toán Random Forest bao gồm nhiều cây quyết định, mỗi cây được xây dựng dùng thuật toán Decision Tree trên tập dữ liệu khác nhau và dùng tập thuộc tính khác nhau. Sau đó, kết quả dự đoán của thuật toán Random Forest sẽ được tổng hợp từ các cây quyết định.[19]

k-NN[sửa | sửa mã nguồn]

k-NN (k-Nearest Neighbors) là một trong những thuật toán học có giám sát (Supervised Learning) đơn giản nhất được sử dụng nhiều trong khai phá dữ liệu và máy học. Ý tưởng của thuật toán này là nó không học một điều gì từ tập dữ liệu học (nên k-NN được xếp vào loại lazy learning), mọi tính toán được thực hiện khi nó cần dự đoán nhãn của dữ liệu mới. k-NN có thể áp dụng được vào cả bài toán phân lớp và hồi quy.[20]

Phần mềm sử dụng trong nghiên cứu[sửa | sửa mã nguồn]

Ngôn ngữ lập trình Python và các thư viện[sửa | sửa mã nguồn]

Python là một ngôn ngữ lập trình thông dịch, hướng đối tượng, bậc cao với ngữ nghĩa động. Python có cú pháp đơn giản, dễ đọc, giúp giảm chi phí bảo trì. Python hỗ trợ các module và package, khuyến khích module hóa chương trình và tái sử dụng mã. Trình thông dịch Python và các thư viện chuẩn mở rộng có sẵn miễn phí ở dạng mã nguồn và binary trên tất cả nền tảng chính.[21]

Nghiên cứu sử dụng một số thư viện của nền tảng Python như thư viện scikit-learn (sklearn) cung cấp các triển khai thuật toán máy học, NumPy cho tính toán khoa học, pandas để thao tác và phân tích dữ liệu, và matplotlib, seaborn cho vẽ biểu đồ.

Jupyter Notebook[sửa | sửa mã nguồn]

Jupyter Notebook là một công cụ web tương tác miễn phí, mã nguồn mở, được coi là một sổ ghi chép cho phép tính toán, giúp các nhà nghiên cứu có thể kết hợp mã phần mềm, kết quả tính toán, văn bản giải thích và tài nguyên đa phương tiện trong một tài liệu duy nhất. Jupyter Notebook cho phép thực thi mã nguồn của các ngôn ngữ Julia, Python và R.[22]

Thực nghiệm: Chuẩn bị dữ liệu[sửa | sửa mã nguồn]

Chuẩn bị dữ liệu (Data Preparation) là quá trình xử lý dữ liệu thô thành dữ liệu thích hợp cho việc xây dựng mô hình máy học.[23] Quá trình chuẩn bị dữ liệu có thể gồm các bước sau:[24][25]

  1. Thu thập dữ liệu
  2. Thăm dò dữ liệu
  3. Sơ chế dữ liệu
  4. Trích chọn đặc trưng
  5. Giảm chiều dữ liệu

Thu thập dữ liệu[sửa | sửa mã nguồn]

Thu thập dữ liệu (Data Collection) là quá trình tìm kiếm và trích nhặt những thông tin liên quan đến vấn đề nghiên cứu, tạo ra dữ liệu thô (raw data).[25]

Một bộ dữ liệu (dataset) được tổ chức theo hai thành phần là các mẫu dữ liệu (samples) và các đặc trưng của dữ liệu (features),[26] được biểu diễn tương ứng dưới dạng các hàng và cột của một bảng dữ liệu.

Bộ dữ liệu của nghiên cứu này dự kiến thu thập bằng cách sử dụng thư viện revscoring (Revision Scoring), một phần mềm dùng để xây dựng ORES.

Cơ sở thu thập dữ liệu[sửa | sửa mã nguồn]

Trước khi bắt tay vào việc thu thập dữ liệu bằng revscoring, cần phải biết được là sẽ thu thập những dữ liệu nào. Nghiên cứu có mục tiêu là phát hiện những sửa đổi phá hoại, nghĩa là cần thu thập dữ liệu những đặc trưng xoay quanh các sửa đổi.

Một sửa đổi Wikipedia có thể chia làm 2 loại chính: Sửa đổi tạo trangSửa đổi sửa trang. Một số thông tin khái quát:

  • Sửa đổi tạo trang là sửa đổi tạo ra một trang (Page) mới, đồng thời cũng tạo ra bản sửa đổi (Revision) đầu tiên của trang đó. Ví dụ một bản sửa đổi được tạo ra bởi sửa đổi tạo trang: 65193673.
  • Sửa đổi sửa trang là sửa đổi được thực hiện trên một trang đã được tạo trước đó, thay đổi nội dung của trang và tạo ra bản sửa đổi mới của trang. Ví dụ một bản sửa đổi được tạo ra bởi sửa đổi sửa trang: 65193680.
  • Sự thay đổi nội dung (thêm, xóa, sửa) giữa 2 bản sửa đổi bất kỳ của cùng 1 trang được gọi là sự khác biệt (Diff). Ví dụ sự khác biệt giữa bản sửa đổi 65193673 và 65193680 của trang "Nguyễn Văn Thái (nhà bảo tồn)": Diff(65193673~65193680).
  • Một trang có thể có một hay nhiều bản sửa đổi, số lượng bản sửa đổi của trang bằng với số lượng lượt sửa đổi của trang đó. Mỗi một lượt sửa đổi tạo ra 1 bản sửa đổi mới của trang.
  • Sửa đổi được thực hiện bởi người dùng (User). Người dùng có 2 nhóm: Đã đăng ký tài khoản và Chưa đăng ký tài khoản. Nhóm người dùng chưa đăng ký tài khoản khi thực hiện sửa đổi sẽ được lưu vết địa chỉ IP.

Như vậy, việc thu thập dữ liệu các sửa đổi chính là thu thập dữ liệu những đặc trưng xoay quanh đối tượng bản sửa đổi (Revision), vì mỗi một lượt sửa đổi đều tạo ra 1 bản sửa đổi mới. Thông tin những đặc trưng của bản sửa đổi có thể đến từ các đối tượng có liên quan như trang (Page), sự khác biệt (Diff), người dùng (User) hay chính bản sửa đổi (Revision). Mỗi một bản sửa đổi đều có một mã định danh (Revision ID).

Thư viện revscoring hỗ trợ thu thập các thông tin xoay quanh các bản sửa đổi.

Thực hiện thu thập dữ liệu[sửa | sửa mã nguồn]

Đầu tiên, cần thu thập một danh sách các bản sửa đổi, biểu diễn dưới dạng mã định danh. Chọn cỡ mẫu là 20000 bản sửa đổi, được lấy ngẫu nhiên trong số các bản sửa đổi được tạo ra trong thời gian 1 năm (01/08/2020 – 01/08/2021), không được lấy các bản sửa đổi tạo bởi tài khoản bot. Tài khoản bot là những tài khoản dùng các công cụ tự động để thực hiện các sửa đổi đơn giản, lặp đi lặp lại theo quy luật cho trước.[27]

Để thực hiện việc này, cần dùng Quarry, một công cụ cho phép tạo các truy vấn SQL trực tiếp đến bản sao cơ sở dữ liệu của Wikipedia.[28] Truy vấn 57765 trên Quarry được thực hiện, với mã SQL như sau:

SELECT rev_id
FROM revision
    JOIN actor on rev_actor = actor_id
WHERE rev_timestamp BETWEEN "20200801" AND "20210801"
    AND (actor_user NOT IN (SELECT ug_user
    						FROM user_groups
                        	WHERE ug_group = "bot")
         OR actor_user IS NULL)
    AND actor_name NOT REGEXP "([Bb]ot|AWB)$"
ORDER BY RAND() 
LIMIT 20000;

Tải kết quả truy vấn (danh mục Revision ID) về dưới dạng tệp CSV: revids.csv (thư mục data).

In [1]:

import pandas as pd

revids = pd.read_csv('data/revids.csv').values.reshape(-1)

print(len(revids), revids)

Out [1]:

20000 [64865447 64073399 63605477 ... 64109503 64476409 64091986]

Lúc này, đã thu thập được danh mục 20000 bản sửa đổi (Revision ID). Tiếp theo, cần phải đánh nhãn (phân lớp) từng bản sửa đổi trong danh mục là gây hại (1), hoặc không gây hại (0).

Đánh nhãn bằng tay cho 20000 sửa đổi là một công việc khó khăn. Do đó, giả định rằng một sửa đổi được hồi sửa cũng là một sửa đổi gây hại thì có thể tiến hành đánh nhãn một cách tự động. Vì sửa đổi được hồi sửa thường là những sửa đổi được đội ngũ tuần tra đánh giá là gây hại và được họ lùi lại, hoàn nguyên về nội dung của bản sửa đổi trước đó.[29]

Tuy nhiên, cũng cần loại trừ việc đánh nhãn "gây hại" cho các trường hợp như tự lùi lại sửa đổi của chính mình, hay sửa đổi được hồi sửa lại được hồi sửa trở lại. Sử dụng thư viện mwrevertsmwapi để hỗ trợ phát hiện các sửa đổi được hồi sửa. Cần cài đặt thêm thư viện jsonable và mwtypes để sử dụng được hai thư viện trên.

In [2]:

import sys, traceback
import mwreverts.api
import mwapi

# We'll use the mwreverts API check. In order to do that, we need an API session
session = mwapi.Session("https://vi.wikipedia.org", user_agent="Extract Revert Information")

# For each revision, find out if it was "reverted" and label it so.
rev_reverteds = []
for rev_id in revids[:20]:  # NOTE: Limiting to the first 20!!!!
    try:
        _, reverted, reverted_to = mwreverts.api.check(
            session, rev_id, radius=5,  # most reverts within 5 edits
            window=48*60*60,  # 2 days
            rvprop={'user', 'ids'})  # Some properties we'll make use of
    except (RuntimeError, KeyError) as e:
        sys.stderr.write(str(e))
        continue
    
    if reverted is not None:
        reverted_doc = [r for r in reverted.reverteds
                        if r['revid'] == rev_id][0]

        if 'user' not in reverted_doc or 'user' not in reverted.reverting:
            continue

        # self-reverts
        self_revert = \
            reverted_doc['user'] == reverted.reverting['user']
        
        # revisions that are reverted back to by others
        reverted_back_to = \
            reverted_to is not None and \
            'user' in reverted_to.reverting and \
            reverted_doc['user'] != \
            reverted_to.reverting['user']
        
        # If we are reverted, not by self or reverted back to by someone else, 
        # then, let's assume it was damaging.
        damaging_reverted = not (self_revert or reverted_back_to)
    else:
        damaging_reverted = False
    
    rev_reverteds.append([rev_id, "1" if damaging_reverted else "0"])
    sys.stderr.write("r" if damaging_reverted else ".")

Out [2]:

....................

Mã này được lấy từ [30], giúp đánh nhãn cho 20000 sửa đổi (ví dụ là 20 sửa đổi đầu tiên). Dữ liệu đánh nhãn được lưu trong biến rev_reverteds, lưu lại vào trong tệp rev_reverteds.csv (thư mục data).

In [3]:

# 20 mẫu đầu tiên của rev_reverteds
print(rev_reverteds)

Out [3]:

[[64865447, '0'], [64073399, '0'], [63605477, '0'], [64791981, '0'], [64605789, '0'], [65129083, '0'], [63686403, '0'], [64279589, '0'], [64905231, '0'], [63227570, '0'], [65099243, '0'], [63950932, '0'], [64172697, '0'], [64194071, '0'], [64360703, '0'], [64348972, '0'], [64420235, '0'], [63243478, '0'], [65011939, '0'], [63921509, '0']]

In [4]:

# Lưu rev_reverteds vào tệp rev_reverteds.csv
pd.DataFrame(rev_reverteds, columns = ['rev_id', 'reverted']).to_csv('data/rev_reverteds.csv', index=False)

rev_reverteds = pd.read_csv('data/rev_reverteds.csv')
print(rev_reverteds)

Out [4]:

         rev_id  reverted
0      64865447         0
1      64073399         0
2      63605477         0
3      64791981         0
4      64605789         0
...         ...       ...
19990  63861385         0
19991  63592782         0
19992  64109503         0
19993  64476409         0
19994  64091986         0

[19995 rows x 2 columns]

Như vậy, tệp rev_reverteds.csv gồm có 19995 mẫu bản sửa đổi đã được đánh nhãn (5 bản sửa đổi gặp lỗi trong quá trình truy xuất thông tin), cột nhãn là reverted.

Tiếp theo, để có dữ liệu huấn luyện mô hình máy học, cần tiến hành thu thập dữ liệu các đặc trưng của các bản sửa đổi. Công việc này được hỗ trợ bởi thư viện revscoring.

Vì lý do bất khả kháng (không cài đặt được revscoring), công việc thu thập được triển khai bằng dịch vụ ORES, có sẵn dữ liệu 90 đặc trưng cho mỗi bản sửa đổi. Thông tin khái quát về 90 đặc trưng này được trình bày ở Phụ lục 2.

In [5]:

import requests, json

def extract_features(rev_id):
    revision = requests.get('https://ores.wikimedia.org/v3/scores/viwiki/{0}/reverted?features'.format(rev_id)).json()
    return list(revision['viwiki']['scores'][str(rev_id)]['reverted']['features'].values())

raw_revs = []
for row in rev_reverteds.values[:20]: # NOTE: 20 phần tử đầu của rev_reverteds.csv
    try:
        rev_id = row[0]
        reverted = row[1]
        features = extract_features(rev_id) # Dữ liệu 90 đặc trưng

        rev = [rev_id] + features + [reverted]
        raw_revs.append(rev)
        sys.stderr.write('.')
    except (RuntimeError, KeyError) as e:
        sys.stderr.write(str(rev_id))
        continue

Out [5]:

....................

In [6]:

# Hàng dữ liệu đầu tiên của raw_revs
print(raw_revs[0])

Out [6]:

[64865447, 0, 0, 0, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, 0.0, 0.0, 4096.0, 4101.0, 263.0, 263.0, 1.0, 1159.0, 1159.0, False, True, 1, 1, True, False, True, False, False, True, False, False, False, False, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, 0.0, 0.0, -1, 1, 0, -1.0, 1.0, 0.0, 0, 0, 0, 0.0, 0.0, 0.0, 9302.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 9290.0, 0.0, 4.0, 0.0, 564.0, 10.0, 39.0, 0.0, 564.0, 10.0, 39.0, 0]

In [7]:

def feature_names():
    revision = requests.get('https://ores.wikimedia.org/v3/scores/viwiki/1/reverted?features').json()
    return list(revision['viwiki']['scores']['1']['reverted']['features'].keys())

# Lưu dữ liệu của biến raw_revs vào tệp raw_revs.csv
pd.DataFrame(raw_revs, columns = ['rev_id'] + feature_names() + ['reverted']).to_csv('data/raw_revs.csv', index=False)

In [8]:

raw_revs = pd.read_csv('data/raw_revs.csv')
print(raw_revs.shape)

Out [8]:

(19874, 92)

Như vậy, tệp raw_revs.csv gồm có 19874 mẫu bản sửa đổi (121 bản sửa đổi gặp lỗi trong quá trình truy xuất thông tin) và 92 cột. Cột đầu tiên rev_id là mã bản sửa đổi, 90 cột tiếp theo là dữ liệu các đặc trưng, cột cuối cùng reverted là cột nhãn (phân lớp).

Công việc thu thập dữ liệu thô đã hoàn tất.

Thăm dò dữ liệu[sửa | sửa mã nguồn]

Dữ liệu thô của nghiên cứu được lưu trong tệp raw_revs.csv sau một quá trình thu thập. Công việc tiếp theo là Thăm dò dữ liệu (Exploratory Data Analysis) nhằm có được cái nhìn tổng quan về dữ liệu thô.

In [9]:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

raw_revs = pd.read_csv('data/raw_revs.csv')
del raw_revs['rev_id']

# Thông tin sơ bộ của 90 đặc trưng
raw_revs.info()

Out [9]:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 19874 entries, 0 to 19873
Data columns (total 91 columns):
 #   Column                                                                         Non-Null Count  Dtype  
---  ------                                                                         --------------  -----  
 0   feature.english.badwords.revision.diff.match_delta_decrease                    19874 non-null  int64  
 1   feature.english.badwords.revision.diff.match_delta_increase                    19874 non-null  int64  
 2   feature.english.badwords.revision.diff.match_delta_sum                         19874 non-null  int64  
 3   feature.english.badwords.revision.diff.match_prop_delta_decrease               19874 non-null  float64
 4   feature.english.badwords.revision.diff.match_prop_delta_increase               19874 non-null  float64
 5   feature.english.badwords.revision.diff.match_prop_delta_sum                    19874 non-null  float64
 6   feature.english.informals.revision.diff.match_delta_decrease                   19874 non-null  int64  
 7   feature.english.informals.revision.diff.match_delta_increase                   19874 non-null  int64  
 8   feature.english.informals.revision.diff.match_delta_sum                        19874 non-null  int64  
 9   feature.english.informals.revision.diff.match_prop_delta_decrease              19874 non-null  float64
 10  feature.english.informals.revision.diff.match_prop_delta_increase              19874 non-null  float64
 11  feature.english.informals.revision.diff.match_prop_delta_sum                   19874 non-null  float64
 12  feature.len(<datasource.tokenized(datasource.revision.parent.text)>)           19874 non-null  float64
 13  feature.len(<datasource.tokenized(datasource.revision.text)>)                  19874 non-null  float64
 14  feature.len(<datasource.wikitext.revision.markups>)                            19874 non-null  float64
 15  feature.len(<datasource.wikitext.revision.parent.markups>)                     19874 non-null  float64
 16  feature.len(<datasource.wikitext.revision.parent.uppercase_words>)             19874 non-null  float64
 17  feature.len(<datasource.wikitext.revision.parent.words>)                       19874 non-null  float64
 18  feature.len(<datasource.wikitext.revision.words>)                              19874 non-null  float64
 19  feature.revision.comment.has_link                                              19874 non-null  bool   
 20  feature.revision.comment.suggests_section_edit                                 19874 non-null  bool   
 21  feature.revision.diff.longest_new_repeated_char                                19874 non-null  int64  
 22  feature.revision.diff.longest_new_token                                        19874 non-null  int64  
 23  feature.revision.page.is_articleish                                            19874 non-null  bool   
 24  feature.revision.page.is_draftspace                                            19874 non-null  bool   
 25  feature.revision.page.is_mainspace                                             19874 non-null  bool   
 26  feature.revision.user.has_advanced_rights                                      19874 non-null  bool   
 27  feature.revision.user.is_admin                                                 19874 non-null  bool   
 28  feature.revision.user.is_anon                                                  19874 non-null  bool   
 29  feature.revision.user.is_bot                                                   19874 non-null  bool   
 30  feature.revision.user.is_curator                                               19874 non-null  bool   
 31  feature.revision.user.is_patroller                                             19874 non-null  bool   
 32  feature.revision.user.is_trusted                                               19874 non-null  bool   
 33  feature.temporal.revision.user.seconds_since_registration                      19874 non-null  int64  
 34  feature.vietnamese.badwords.revision.diff.match_delta_decrease                 19874 non-null  int64  
 35  feature.vietnamese.badwords.revision.diff.match_delta_increase                 19874 non-null  int64  
 36  feature.vietnamese.badwords.revision.diff.match_delta_sum                      19874 non-null  int64  
 37  feature.vietnamese.badwords.revision.diff.match_prop_delta_decrease            19874 non-null  float64
 38  feature.vietnamese.badwords.revision.diff.match_prop_delta_increase            19874 non-null  float64
 39  feature.vietnamese.badwords.revision.diff.match_prop_delta_sum                 19874 non-null  float64
 40  feature.vietnamese.dictionary.revision.diff.dict_word_delta_decrease           19874 non-null  int64  
 41  feature.vietnamese.dictionary.revision.diff.dict_word_delta_increase           19874 non-null  int64  
 42  feature.vietnamese.dictionary.revision.diff.dict_word_delta_sum                19874 non-null  int64  
 43  feature.vietnamese.dictionary.revision.diff.dict_word_prop_delta_decrease      19874 non-null  float64
 44  feature.vietnamese.dictionary.revision.diff.dict_word_prop_delta_increase      19874 non-null  float64
 45  feature.vietnamese.dictionary.revision.diff.dict_word_prop_delta_sum           19874 non-null  float64
 46  feature.vietnamese.dictionary.revision.diff.non_dict_word_delta_decrease       19874 non-null  int64  
 47  feature.vietnamese.dictionary.revision.diff.non_dict_word_delta_increase       19874 non-null  int64  
 48  feature.vietnamese.dictionary.revision.diff.non_dict_word_delta_sum            19874 non-null  int64  
 49  feature.vietnamese.dictionary.revision.diff.non_dict_word_prop_delta_decrease  19874 non-null  float64
 50  feature.vietnamese.dictionary.revision.diff.non_dict_word_prop_delta_increase  19874 non-null  float64
 51  feature.vietnamese.dictionary.revision.diff.non_dict_word_prop_delta_sum       19874 non-null  float64
 52  feature.vietnamese.informals.revision.diff.match_delta_decrease                19874 non-null  int64  
 53  feature.vietnamese.informals.revision.diff.match_delta_increase                19874 non-null  int64  
 54  feature.vietnamese.informals.revision.diff.match_delta_sum                     19874 non-null  int64  
 55  feature.vietnamese.informals.revision.diff.match_prop_delta_decrease           19874 non-null  float64
 56  feature.vietnamese.informals.revision.diff.match_prop_delta_increase           19874 non-null  float64
 57  feature.vietnamese.informals.revision.diff.match_prop_delta_sum                19874 non-null  float64
 58  feature.wikitext.revision.chars                                                19874 non-null  float64
 59  feature.wikitext.revision.diff.markup_delta_decrease                           19874 non-null  float64
 60  feature.wikitext.revision.diff.markup_delta_increase                           19874 non-null  float64
 61  feature.wikitext.revision.diff.markup_delta_sum                                19874 non-null  float64
 62  feature.wikitext.revision.diff.markup_prop_delta_decrease                      19874 non-null  float64
 63  feature.wikitext.revision.diff.markup_prop_delta_increase                      19874 non-null  float64
 64  feature.wikitext.revision.diff.markup_prop_delta_sum                           19874 non-null  float64
 65  feature.wikitext.revision.diff.number_delta_decrease                           19874 non-null  float64
 66  feature.wikitext.revision.diff.number_delta_increase                           19874 non-null  float64
 67  feature.wikitext.revision.diff.number_delta_sum                                19874 non-null  float64
 68  feature.wikitext.revision.diff.number_prop_delta_decrease                      19874 non-null  float64
 69  feature.wikitext.revision.diff.number_prop_delta_increase                      19874 non-null  float64
 70  feature.wikitext.revision.diff.number_prop_delta_sum                           19874 non-null  float64
 71  feature.wikitext.revision.diff.uppercase_word_delta_decrease                   19874 non-null  float64
 72  feature.wikitext.revision.diff.uppercase_word_delta_increase                   19874 non-null  float64
 73  feature.wikitext.revision.diff.uppercase_word_delta_sum                        19874 non-null  float64
 74  feature.wikitext.revision.diff.uppercase_word_prop_delta_decrease              19874 non-null  float64
 75  feature.wikitext.revision.diff.uppercase_word_prop_delta_increase              19874 non-null  float64
 76  feature.wikitext.revision.diff.uppercase_word_prop_delta_sum                   19874 non-null  float64
 77  feature.wikitext.revision.external_links                                       19874 non-null  float64
 78  feature.wikitext.revision.headings                                             19874 non-null  float64
 79  feature.wikitext.revision.parent.chars                                         19874 non-null  float64
 80  feature.wikitext.revision.parent.external_links                                19874 non-null  float64
 81  feature.wikitext.revision.parent.headings                                      19874 non-null  float64
 82  feature.wikitext.revision.parent.ref_tags                                      19874 non-null  float64
 83  feature.wikitext.revision.parent.tags                                          19874 non-null  float64
 84  feature.wikitext.revision.parent.templates                                     19874 non-null  float64
 85  feature.wikitext.revision.parent.wikilinks                                     19874 non-null  float64
 86  feature.wikitext.revision.ref_tags                                             19874 non-null  float64
 87  feature.wikitext.revision.tags                                                 19874 non-null  float64
 88  feature.wikitext.revision.templates                                            19874 non-null  float64
 89  feature.wikitext.revision.wikilinks                                            19874 non-null  float64
 90  reverted                                                                       19874 non-null  int64  
dtypes: bool(12), float64(57), int64(22)
memory usage: 12.2 MB

Bộ dữ liệu gồm 19874 mẫu, 90 đặc trưng và 1 biến phân lớp nhị phân (1: sửa đổi phá hoại, 0: sửa đổi không phá hoại).

Trong đó, có 78 đặc trưng định lượng liên tục (kiểu dữ liệu float64, int64) và 12 đặc trưng định tính nhị phân (kiểu dữ liệu bool).

Tất cả các đặc trưng đều không có giá trị rỗng (non-null), do đó không cần phải xử lý khoản này về sau.

In [10]:

# Kiểm tra biến phân lớp
raw_revs['reverted'].value_counts()

Out [10]:

0    18816
1     1058
Name: reverted, dtype: int64

Trong 19874 mẫu, có 1058 sửa đổi phá hoại và 18816 sửa đổi không phá hoại, số lượng sửa đổi phá hoại chiếm tỷ lệ 5,62% tổng số lượng sửa đổi. Như vậy, có sự bất đối xứng về nhãn phân lớp, gợi ý rằng trong quá trình tái chọn mẫu và phân chia dữ liệu, cần phải cố gắng bảo toàn tỷ lệ giữa 2 lớp.[31]

In [11]:

# Lấy ra danh sách tên các đặc trưng định lượng và định tính
numerical = raw_revs.iloc[:, :-1].select_dtypes(include=np.number).columns.tolist()
categorical = raw_revs.iloc[:, :-1].select_dtypes(bool).columns.tolist()

In [12]:

# Biểu đồ histogram của 78 đặc trưng định lượng
fig, axs = plt.subplots(26, 3, figsize=(20,120))

for ax, feature in zip(axs.flat, numerical):
    ax.hist(raw_revs[feature], bins=50)
    ax.set_title(feature, fontsize=10, pad=8)
    ax.set(ylabel = 'Count')

Out [12]:

In [13]:

# Biểu đồ hộp thể hiện quan hệ giữa đặc trưng định lượng và biến phân lớp
fig, axs = plt.subplots(26, 3, figsize=(20,120))

for ax, feature in zip(axs.flat, numerical):
    sns.boxplot(ax=ax, x=feature, y='reverted', data=raw_revs, orient='h')

Out [13]:

In [14]:

# Biểu đồ thanh của 12 đặc trưng định tính, có dữ liệu phân lớp.
fig, axs = plt.subplots(4, 3, figsize=(10,15))

for ax, feature in zip(axs.flat, categorical):
    sns.countplot(ax=ax, x=feature, hue='reverted', data=raw_revs, dodge=False)
    
fig.tight_layout()

Out [14]:

Nhóm biểu đồ histogram cho thấy tất cả đặc trưng định lượng đều có phân bố bất thường, không đồng dạng và không cân đối. Điều này gợi ý cần phải thực hiện các phép chuyển đổi để hiệu chỉnh độ xiên (skewness).

Nhóm biểu đồ hộp cho thấy hình dạng các hộp kỳ dị so với bình thường, chứng tỏ có rất nhiều điểm ngoại lai (outlier) làm ảnh hưởng đến hình dạng cân đối của hộp, có thể cần thực hiện việc loại bỏ các điểm này, hoặc xem xét áp dụng các phương pháp xử lý dữ liệu phù hợp với hiện trạng số lượng điểm ngoại lai lớn.

Nhóm biểu đồ thanh cho thấy độ tương phản của các đặc trưng định tính trong dữ liệu là rất yếu. Cần thiết phải loại bỏ các biến định tính không cho thấy sự tương phản rõ. Một số biến chỉ có 1 giá trị trong toàn bộ mẫu (False) cũng cần phải loại bỏ.

Sơ chế dữ liệu[sửa | sửa mã nguồn]

Sơ chế dữ liệu (Data Wrangling) là quá trình chuyển đổi dữ liệu thô thành dạng dữ liệu thích hợp cho các công việc xử lý sau này, giúp cải thiện độ chính xác của mô hình máy học.[32] Sơ chế dữ liệu bao gồm các công việc của Làm sạch dữ liệu (Data Cleaning)Chuyển đổi dữ liệu (Data Transformation). Một số công việc trong giai đoạn Sơ chế dữ liệu:

  1. Xóa các cột chỉ chứa 1 giá trị
  2. Xóa các hàng trùng lặp
  3. Xóa các điểm ngoại lai (outlier)
  4. Chuyển đổi quy mô dữ liệu (Scaling Transformation)
  5. Chuyển đổi phân phối dữ liệu (Distribution Transformation)
  6. Mã hóa nhãn biến định tính (Label Encoding)

Quá trình sơ chế dữ liệu tệp raw_revs.csv có một số điểm cần lưu ý:

  • Công việc chuyển đổi quy mô và phân phối dữ liệu chỉ áp dụng cho các biến định lượng liên tục.
  • Phương pháp chuyển đổi quy mô dữ liệu là Chuẩn hóa mạnh (Robust Standardization), triển khai thông qua lớp RobustScaler của sklearn. Việc lựa chọn phương pháp này là do Chuẩn hóa mạnh thích hợp xử lý dữ liệu nhiều outlier, không như Chuẩn hóa (Standardization) và Bình thường hóa (Normalization) dễ nhạy cảm với các outlier.[33][34][35]
  • Phương pháp chuyển đổi phân phối dữ liệu là Chuyển đổi phân vị chuẩn (Normal Quantile Transform), triển khai thông qua lớp QuantileTransformer của sklearn. Việc lựa chọn phương pháp này cũng vì lý do thích hợp xử lý dữ liệu nhiều outlier. Chuyển đổi phân vị chuẩn giúp chuyển đổi phân phối ban đầu thành dạng phân phối chuẩn.[36]
  • Mã hóa nhãn biến định tính là việc chuyển đổi giá trị True, False hiện tại của các biến định tính thành dạng số tương ứng là 1, 0; thông qua lớp OrdinalEncoder của sklearn, thích hợp cho việc triển khai các thuật toán máy học.
  • Không tiến hành công việc xóa các điểm ngoại lai (outlier) vì số lượng outlier quá nhiều, do đó nếu xóa có thể gây ảnh hưởng đến chất lượng kết quả sau này.[37] Có thể cần đối sánh giữa việc xóa và không xóa outlier khi kiểm định mô hình.

Trích chọn đặc trưng[sửa | sửa mã nguồn]

Trích chọn đặc trưng (Feature Selection) là quá trình chọn ra những đặc trưng để dùng trong huấn luyện mô hình. Lợi ích của việc này là giúp giảm khối lượng dữ liệu không cần thiết, giảm thời gian huấn luyện và tăng cường tổng quát hóa bằng cách giảm sự quá khớp (overfitting).[38]

Phương pháp trích chọn đặc trưng được sử dụng trong nghiên cứu là Thông tin tương hỗ (Mutual Information), triển khai thông qua hàm mutual_info_classif của sklearn. Nguyên nhân lựa chọn là do phương pháp này áp dụng tốt đối với cả biến đầu vào định lượng và định tính.[39] Số đặc trưng được trích trên tổng số đặc trưng của bộ dữ liệu là 70, nguyên nhân lựa chọn con số 70 được giải thích ở Phụ lục 3.

Dưới đây là chương trình sơ chế dữ liệu và trích chọn đặc trưng đối với tệp raw_revs.csv, kết quả được lưu vào tệp revs.csv.

In [15]:

import pandas as pd
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import RobustScaler, QuantileTransformer, OrdinalEncoder
from sklearn.compose import ColumnTransformer
from sklearn.feature_selection import SelectKBest, mutual_info_classif

# Lấy dữ liệu thô
df = pd.read_csv('data/raw_revs.csv')

# Xóa cột đầu tiên 'rev_id' vì không cần thiết
del df['rev_id']

# Xóa các cột chỉ chứa 1 giá trị (có 3 cột)
df.drop(columns=df.columns[df.nunique()==1], inplace=True)

# Xóa các hàng trùng lặp (có 10 hàng)
df.drop_duplicates(inplace=True)

# Chuyển đổi các biến định lượng và định tính
X, y = df.iloc[:, :-1], df.iloc[:, -1]

numerical_features = X.select_dtypes(include=np.number).columns.tolist()
numerical_transformer = Pipeline(steps=[
    ('scaler', RobustScaler(with_centering=False, with_scaling=True)),
    ('quantile', QuantileTransformer(n_quantiles=100, output_distribution='normal'))])

categorical_features = X.select_dtypes(bool).columns.tolist()
categorical_transformer = OrdinalEncoder()

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numerical_transformer, numerical_features),
        ('cat', categorical_transformer, categorical_features)])

X_trans = pd.DataFrame(preprocessor.fit_transform(X),
                      columns = numerical_features + categorical_features)

# Trích chọn đặc trưng
X_trans = X_trans.loc[:, SelectKBest(score_func=lambda X, y: mutual_info_classif(X, y, random_state=0), k=70)
                      .fit(X_trans, y).get_support()]

# Lưu kết quả sơ chế và trích chọn vào biến result
result = X_trans
result['reverted'] = y.values

# Lưu result vào revs.csv
result.to_csv('data/revs.csv', index=False)

Giảm chiều dữ liệu[sửa | sửa mã nguồn]

Nghiên cứu không thực hiện công việc này, sẽ thực hiện khi thích hợp.

Thực nghiệm: Xây dựng và đánh giá mô hình[sửa | sửa mã nguồn]

Nghiên cứu sẽ xây dựng các mô hình máy học giúp dự đoán một sửa đổi Wikipedia là phá hoại hay không phá hoại, sử dụng các thuật toán: SVM, Hồi quy logistic, k-Means, Gradient Boosting, Random Forest và k-NN (k láng giềng gần nhất).

Thiết lập hàm tổng quát build_and_eval_model(clf) để xây dựng và đánh giá mô hình nhằm triển khai cho nhiều thuật toán đã đề cập. Các bước của hàm:

  1. Lấy dữ liệu revs.csv
  2. Tách dữ liệu thành biến input X và output y
    • X gồm các biến đầu vào định lượng và định tính (70 đặc trưng)
    • y là biến phân lớp nhị phân
  3. Phân chia dữ liệu thành 2 tập: Training Set (70%) và Test Set (30%). Bảo tồn tỷ lệ phân lớp (stratify=y).
  4. Khởi tạo mô hình, nhận thuật toán phân lớp thông qua tham số clf (classifier) của hàm.
  5. Đánh giá mô hình bằng Kiểm chứng chéo (Cross Validation) trên Training Set
    • Đánh giá 4 hệ số: Accuracy (Độ chính xác tổng quát), Precision (Độ chính xác), Recall (Độ nhạy), F1.
    • Cross Validation có 5 fold, 3 lần lặp lại, bảo tồn tỷ lệ phân lớp (stratified). Triển khai thông qua lớp RepeatedStratifiedKFold của sklearn.
    • Kiểm chứng chéo mô hình trên Training Set, kết quả đánh giá được lưu vào tệp <clf>_cv_scores.csv
  6. Huấn luyện mô hình và dùng mô hình dự đoán trên Test Set
  7. Đánh giá kết quả dự đoán của mô hình trên Test Set
    • Đánh giá 4 hệ số: Accuracy (Độ chính xác tổng quát), Precision (Độ chính xác), Recall (Độ nhạy), F1.
    • Lưu kết quả đánh giá vào tệp <clf>_final_scores.csv
  8. Trực quan hóa kết quả đánh giá
    • Vẽ biểu đồ hộp cho 4 hệ số đánh giá từ kết quả kiểm chứng chéo Training Set.
    • Vẽ Confusion Matrix cho kết quả dự đoán của mô hình trên Test Set.
Hình 2. Kỹ thuật Kiểm chứng chéo (Cross Validation)[40]

In [16]:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_validate, RepeatedStratifiedKFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import plot_confusion_matrix

def build_and_eval_model(clf):
    # lấy dữ liệu
    df = pd.read_csv('data/revs.csv')

    # Tách dữ liệu thành biến input X và output y
    X, y = df.iloc[:, :-1], df.iloc[:, -1]

    # Phân chia dữ liệu, bảo tồn tỷ lệ phân lớp (stratify=y).
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=0)

    # Khởi tạo mô hình
    model = clf

    # Đánh giá mô hình bằng Kiểm chứng chéo trên Training Set
    scoring = ['accuracy', 'precision', 'recall', 'f1']
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=0)
    scores = cross_validate(model, X_train, y_train, scoring=scoring, cv=cv, n_jobs=-1, error_score='raise')
    cv_scores = pd.DataFrame(scores).iloc[:, 2:]
    print('A. Cross Validation on Training Set')
    print(cv_scores.describe())

    # Huấn luyện mô hình
    model.fit(X_train, y_train)

    # Dùng mô hình dự đoán trên Test Set
    y_pred = model.predict(X_test)

    # Đánh giá kết quả dự đoán của mô hình trên Test Set
    final_scores = pd.DataFrame({'accuracy': [accuracy_score(y_test, y_pred)],
                                 'precision': [precision_score(y_test, y_pred)],
                                 'recall': [recall_score(y_test, y_pred)],
                                 'f1': [f1_score(y_test, y_pred)]})
    print('B. Final Validation on Test Set')
    print(final_scores.to_string(index=False))

    # Trực quan hóa kết quả đánh giá
    fig, axs = plt.subplots(1,2, figsize=(12,4))
    sns.boxplot(data=cv_scores, orient="h", palette="Set2", ax=axs[0])
    plot_confusion_matrix(model, X_test, y_test, cmap=plt.cm.Blues, ax=axs[1])
    plt.show()

    # Lưu kết quả đánh giá
    cv_scores.to_csv('data/{}_cv_scores.csv'.format(clf.__class__.__name__), index=False)
    final_scores.to_csv('data/{}_final_scores.csv'.format(clf.__class__.__name__), index=False)

SVM[sửa | sửa mã nguồn]

Lý do lựa chọn giá trị tham số C=80 đối với SVM được giải thích ở Phụ lục 4.

In [17]:

from sklearn.svm import SVC

print('BUILD AND EVALUATE SVM MODEL')
clf = SVC(C=80)
build_and_eval_model(clf)

Out [17]:

BUILD AND EVALUATE SVM MODEL
A. Cross Validation on Training Set
       test_accuracy  test_precision  test_recall    test_f1
count      15.000000       15.000000    15.000000  15.000000
mean        0.945172        0.406080     0.062516   0.107813
std         0.001462        0.092840     0.018612   0.030218
min         0.943186        0.263158     0.033784   0.059880
25%         0.943895        0.333333     0.054054   0.094131
50%         0.945324        0.400000     0.060811   0.109091
75%         0.945523        0.441529     0.074324   0.128307
max         0.948220        0.590909     0.087838   0.152941
B. Final Validation on Test Set
 accuracy  precision   recall       f1
 0.947315   0.531915 0.078864 0.137363

Hồi quy logistic[sửa | sửa mã nguồn]

In [18]:

from sklearn.linear_model import LogisticRegression

print('BUILD AND EVALUATE LOGISTIC REGRESSION MODEL')
clf = LogisticRegression(max_iter=1000)
build_and_eval_model(clf)

Out [18]:

BUILD AND EVALUATE LOGISTIC REGRESSION MODEL
A. Cross Validation on Training Set
       test_accuracy  test_precision  test_recall    test_f1
count      15.000000       15.000000    15.000000  15.000000
mean        0.947929        0.654068     0.055759   0.101941
std         0.001596        0.172910     0.019071   0.032735
min         0.944604        0.312500     0.033784   0.060976
25%         0.947312        0.563492     0.043919   0.081185
50%         0.947860        0.636364     0.053691   0.098765
75%         0.948939        0.738636     0.064189   0.119143
max         0.950378        0.916667     0.100671   0.176471
B. Final Validation on Test Set
 accuracy  precision   recall       f1
 0.946644   0.481481 0.041009 0.075581

k-Means[sửa | sửa mã nguồn]

In [19]:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_validate, RepeatedStratifiedKFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import plot_confusion_matrix, confusion_matrix
from sklearn.cluster import KMeans

def build_and_eval_model(clf):
    # lấy dữ liệu
    df = pd.read_csv('data/revs.csv')

    # Tách dữ liệu thành biến input X và output y
    X, y = df.iloc[:, :-1], df.iloc[:, -1]

    # Phân chia dữ liệu, bảo tồn tỷ lệ phân lớp (stratify=y).
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=0)

    # Khởi tạo mô hình
    model = clf

    # Đánh giá mô hình bằng Kiểm chứng chéo trên Training Set
    scoring = ['accuracy', 'precision', 'recall', 'f1']
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=0)
    scores = cross_validate(model, X_train, y_train, scoring=scoring, cv=cv, n_jobs=-1, error_score='raise')
    cv_scores = pd.DataFrame(scores).iloc[:, 2:]
    print('A. Cross Validation on Training Set')
    print(cv_scores.describe())

    # Huấn luyện mô hình
    model.fit(X_train, y_train)

    # Dùng mô hình dự đoán trên Test Set
    y_pred = model.predict(X_test)

    # Đánh giá kết quả dự đoán của mô hình trên Test Set
    final_scores = pd.DataFrame({'accuracy': [accuracy_score(y_test, y_pred)],
                                 'precision': [precision_score(y_test, y_pred)],
                                 'recall': [recall_score(y_test, y_pred)],
                                 'f1': [f1_score(y_test, y_pred)]})
    print('B. Final Validation on Test Set')
    print(final_scores.to_string(index=False))

    # Trực quan hóa kết quả đánh giá
    fig, axs = plt.subplots(1,2, figsize=(12,4))
    sns.boxplot(data=cv_scores, orient="h", palette="Set2", ax=axs[0])
    # plot_confusion_matrix(model, X_test, y_test, cmap=plt.cm.Blues, ax=axs[1])
    cm = confusion_matrix(y_test, y_pred)
    plt.imshow(cm, interpolation='none', cmap='Blues')
    for (i, j), z in np.ndenumerate(cm):
        plt.text(j, i, z, ha='center', va='center')
    plt.xlabel("Cluster label")
    plt.ylabel("True label")
    plt.show()

    # Lưu kết quả đánh giá
    cv_scores.to_csv('data/{}_cv_scores.csv'.format(clf.__class__.__name__), index=False)
    final_scores.to_csv('data/{}_final_scores.csv'.format(clf.__class__.__name__), index=False)

print('BUILD AND EVALUATE k-MEANS MODEL')
clf = KMeans(n_clusters=2, random_state=0)
build_and_eval_model(clf)

Out [19]:

BUILD AND EVALUATE k-MEANS MODEL
A. Cross Validation on Training Set
       test_accuracy  test_precision  test_recall    test_f1
count      15.000000       15.000000    15.000000  15.000000
mean        0.529849        0.038972     0.409583   0.070141
std         0.174428        0.019915     0.356470   0.038971
min         0.315354        0.016888     0.094595   0.028659
25%         0.327940        0.023814     0.134682   0.040470
50%         0.658756        0.026515     0.141892   0.044681
75%         0.670024        0.061168     0.818225   0.113860
max         0.677095        0.065682     0.871622   0.122159
B. Final Validation on Test Set
 accuracy  precision   recall       f1
 0.673826   0.031664 0.173502 0.053554

Gradient Boosting[sửa | sửa mã nguồn]

In [20]:

from sklearn.ensemble import GradientBoostingClassifier

print('BUILD AND EVALUATE GRADIENT BOOSTING MODEL')
clf = GradientBoostingClassifier(random_state=0)
build_and_eval_model(clf)

Out [20]:

BUILD AND EVALUATE GRADIENT BOOSTING MODEL
A. Cross Validation on Training Set
       test_accuracy  test_precision  test_recall    test_f1
count      15.000000       15.000000    15.000000  15.000000
mean        0.947257        0.550175     0.080498   0.139270
std         0.001804        0.117431     0.020182   0.031540
min         0.943525        0.363636     0.047297   0.086957
25%         0.946413        0.479130     0.074075   0.127911
50%         0.947141        0.538462     0.081081   0.142857
75%         0.948391        0.621345     0.090899   0.158788
max         0.950018        0.764706     0.127517   0.212291
B. Final Validation on Test Set
 accuracy  precision   recall       f1
 0.948826   0.607143 0.107256 0.182306

Random Forest[sửa | sửa mã nguồn]

In [21]:

from sklearn.ensemble import RandomForestClassifier

print('BUILD AND EVALUATE RANDOM FOREST MODEL')
clf = RandomForestClassifier(random_state=0)
build_and_eval_model(clf)

Out [21]:

BUILD AND EVALUATE RANDOM FOREST MODEL
A. Cross Validation on Training Set
       test_accuracy  test_precision  test_recall    test_f1
count      15.000000       15.000000    15.000000  15.000000
mean        0.948528        0.678998     0.066558   0.120815
std         0.001193        0.117313     0.015781   0.026822
min         0.946422        0.476190     0.040541   0.075472
25%         0.947860        0.629121     0.057228   0.105193
50%         0.948580        0.687500     0.067568   0.118343
75%         0.948759        0.727273     0.070697   0.128395
max         0.951456        1.000000     0.107383   0.187135
B. Final Validation on Test Set
 accuracy  precision   recall       f1
 0.949161    0.71875 0.072555 0.131805

k-NN[sửa | sửa mã nguồn]

In [22]:

from sklearn.neighbors import KNeighborsClassifier

print('BUILD AND EVALUATE k-NN MODEL')
clf = KNeighborsClassifier()
build_and_eval_model(clf)

Out [22]:

BUILD AND EVALUATE k-NN MODEL
A. Cross Validation on Training Set
       test_accuracy  test_precision  test_recall    test_f1
count      15.000000       15.000000    15.000000  15.000000
mean        0.944165        0.350705     0.050840   0.088380
std         0.001949        0.092044     0.013014   0.021868
min         0.939928        0.172414     0.033784   0.056497
25%         0.942826        0.276190     0.040404   0.071879
50%         0.944984        0.368421     0.053691   0.089385
75%         0.945343        0.423077     0.057432   0.100824
max         0.946422        0.473684     0.074324   0.126437
B. Final Validation on Test Set
 accuracy  precision   recall       f1
  0.94396   0.355932 0.066246 0.111702

Kết luận[sửa | sửa mã nguồn]

Đánh giá giữa các mô hình[sửa | sửa mã nguồn]

Dưới đây là biểu đồ hộp so sánh các hệ số đánh giá giữa các mô hình máy học đã được xây dựng để giải quyết bài toán ban đầu. Gồm có 4 biểu đồ cho 4 hệ số: Accuracy (Độ chính xác tổng quát), Precision (Độ chính xác), Recall (Độ nhạy), F1. Chia làm 2 nhóm biểu đồ: Nhóm 1 gồm tất cả các thuật toán phân lớp, Nhóm 2 tương tự nhưng loại k-Means.

In [23]:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

clf_names = ['SVC', 'LogisticRegression', 'KMeans', 'GradientBoostingClassifier',
            'RandomForestClassifier', 'KNeighborsClassifier']
score_names = ['test_accuracy', 'test_precision', 'test_recall', 'test_f1']

def build_plots(title):
    model_cv_scores = {clf_name: pd.read_csv('data/{}_cv_scores.csv'.format(clf_name)) for clf_name in clf_names}

    fig, axs = plt.subplots(2,2, figsize=(12,6))

    for score_name, ax in zip(score_names, axs.flat):
        score_data = pd.DataFrame({clf_name: model_cv_scores[clf_name][score_name] for clf_name in clf_names})
        sns.boxplot(data=score_data, orient="h", palette="Set2", ax=ax)
        ax.set_title(score_name)
    
    fig.suptitle(title)
    plt.tight_layout()

# Nhóm 1: Vẽ biểu đồ cho tất cả clf
build_plots('ALL CLASSIFIERS')

# Nhóm 2: Vẽ biểu đồ cho tất cả clf, trừ k-Means
clf_names.remove('KMeans')
build_plots('CLASSIFIERS WITHOUT k-MEANS')

Out [23]:

Hình 3. Nhóm biểu đồ thể hiện dữ liệu 4 hệ số đánh giá bằng Kiểm chứng chéo (Cross Validation) trên Training Set của tất cả thuật toán phân lớp (Nhóm 1)
Hình 4. Nhóm biểu đồ thể hiện dữ liệu 4 hệ số đánh giá bằng Kiểm chứng chéo (Cross Validation) trên Training Set của tất cả thuật toán phân lớp trừ k-Means (Nhóm 2)

Nhìn vào 4 biểu đồ Nhóm 1, cho thấy thuật toán k-Means có các giá trị hệ số chênh lệch, bất thường so với các thuật toán phân lớp điển hình (k-Means bản chất là thuật toán phân cụm). Accuracy của k-Means thấp đáng kể so với các thuật toán khác nên không phù hợp để áp dụng cho bài toán này.

Hình 5. Precision & Recall[41]

Nhìn vào 4 biểu đồ Nhóm 2, ta có một số kết luận:

  • Accuracy (Độ chính xác tổng quát) cao (gần bằng 1) ở tất cả mô hình. Tuy nhiên, Accuracy được đo lường ở cả 2 lớp "Sửa đổi phá hoại" và "Sửa đổi không phá hoại", điều này dẫn đến hệ số Accuracy tỏ ra không phù hợp để đánh giá các mô hình của bài toán này. Do mục tiêu bài toán là phát hiện "Sửa đổi phá hoại", do đó việc này quan trọng hơn là phát hiện "Sửa đổi không phá hoại". Việc phát hiện chính xác nhiều "Sửa đổi không phá hoại" không có ý nghĩa với mục tiêu đề ra. Cần sử dụng những hệ số như Precision, Recall để đo lường chuyên biệt trên lớp "Sửa đổi phá hoại".[42] Accuracy chỉ phù hợp để dùng cho đánh giá dữ liệu cân đối (tỷ lệ phân lớp gần ngang nhau).[43]
  • Precision (Độ chính xác) trung bình ở tất cả mô hình. Trong bài toán này, Precision trả lời cho câu hỏi: Trong các sửa đổi được mô hình dự đoán là "Sửa đổi phá hoại", có bao nhiêu sửa đổi thực sự là "Sửa đổi phá hoại".[43] Với kết quả này, nhìn chung cần phải cải tiến mô hình đạt Precision cao hơn để ứng dụng trong thực tế.
  • Recall (Độ nhạy) rất thấp ở tất cả mô hình. Trong bài toán này, Recall trả lời cho câu hỏi: Trong các sửa đổi thực sự là "Sửa đổi phá hoại", có bao nhiêu sửa đổi được mô hình dự đoán trúng.[43] Với kết quả này, nhìn chung các mô hình "tầm soát" kém các "Sửa đổi phá hoại", bỏ sót rất nhiều trường hợp.
  • F1 rất thấp ở tất cả mô hình. F1 là trung bình điều hòa giữa Precision và Recall.[42] Nhìn chung, mô hình có F1 cao thì có thể xem là tốt, nhất là khi cần một chỉ số đánh giá khách quan trong trường hợp Precision và Recall quá chênh lệch.[42]
Bảng 1. Số liệu 4 hệ số đánh giá các mô hình sau khi sử dụng để dự đoán trên Test Set
Thuật toán Accuracy Precision Recall F1
SVM (C=80) 0,947315 0,531915 0,078864 0,137363
Hồi quy logistic 0,946644 0,481481 0,041009 0,075581
k-Means 0,673826 0,031664 0,173502 0,053554
Gradient Boosting 0,948826 0,607143 0,107256 0,182306
Random Forest 0,949161 0,718750 0,072555 0,131805
k-NN 0,943960 0,355932 0,066246 0,111702

Biện luận ứng dụng[sửa | sửa mã nguồn]

Nhìn chung, ngoại trừ Accuracy cao nhưng không liên quan, thì tất cả những hệ số đánh giá còn lại đều cho thấy các mô hình không có khả quan để áp dụng trong thực tế. Tuy nhiên, vẫn có tiềm năng để ứng dụng nếu như được cải tiến tốt hơn, theo 2 hướng ứng dụng chính.

  • Hướng 1: Sử dụng mô hình phát hiện "Sửa đổi phá hoại" trong các chương trình chống phá hoại tự động trên Wikipedia, như ClueBot NG, lùi lại những "Sửa đổi phá hoại" một cách tự động. Với hướng này, việc phát hiện chính xác lẫn tầm soát "Sửa đổi phá hoại" đều quan trọng, nhưng có lẽ yếu tố phát hiện chính xác quan trọng hơn. Vì nếu dự đoán sai nhiều, lùi lại nhầm nhiều những sửa đổi không thuộc diện "Sửa đổi phá hoại" thì sẽ gây khó chịu cho người dùng Wikipedia. Giống như việc dự đoán quá nhiều email quan trọng thành spam sẽ gây khó chịu cho người dùng email. Để ứng dụng được hướng này, mô hình phải có Precision từ cao đến rất cao, và Recall thì từ trung bình đến cao; có thể là Precision ≥ 85%, Recall ≥ 40%. Như vậy, trong các mô hình hiện tại, không có mô hình nào đạt được yêu cầu này.
  • Hướng 2: Sử dụng mô hình trong các chương trình cảnh báo những sửa đổi có tiềm năng là "Sửa đổi phá hoại", nhằm hướng sự chú ý của đội ngũ tuần tra vào nhóm này, thành viên tuần tra sẽ tái đánh giá và đưa ra quyết định có lùi lại hay không. Với hướng này, việc phát hiện chính xác lẫn tầm soát "Sửa đổi phá hoại" đều quan trọng, nhưng có lẽ yếu tố tầm soát quan trọng hơn rất nhiều. Vì công việc tái đánh giá đã được con người xử lý sau đó, nhiệm vụ của mô hình là phải tầm soát, quét được nhiều nhất những "Sửa đổi phá hoại" thực sự, tuy nhiên cũng không nên để độ chính xác quá thấp vì sẽ khiến công việc tái đánh giá của con người trở nên quá phí phạm (do có quá nhiều sửa đổi không thuộc diện "Sửa đổi phá hoại"). Để ứng dụng được hướng này, mô hình phải có Precision từ trung bình đến cao, và Recall thì từ trung bình đến rất cao; có thể là Precision ≥ 40%, Recall ≥ 40%. Như vậy, trong các mô hình hiện tại, không có mô hình nào đạt được yêu cầu này. Nhưng vẫn có thể chọn lấy mô hình có Recall cao nhất trong số đó, vì hướng ứng dụng này khá an toàn, không gây ảnh hưởng đến toàn bộ người dùng Wikipedia như Hướng 1 mà chỉ gây áp lực lên một nhóm nhỏ thành viên tuần tra.

Như vậy, nếu theo Hướng 2, mô hình của thuật toán Gradient Boosting có thể đáp ứng được. Số liệu các hệ số đánh giá của các mô hình từ kết quả dự đoán trên Test Set (Bảng 1) cũng tái khẳng định thuật toán Gradient Boosting, đạt giá trị hệ số F1 cao nhất, có triển vọng để triển khai ứng dụng theo Hướng 2.

Quy trình xây dựng mô hình phân lớp[sửa | sửa mã nguồn]

Dựa trên những công việc đã thực hiện, có thể rút ra một quy trình điển hình, cơ bản để xây dựng một mô hình máy học nhằm giải quyết một bài toán phân lớp.

  1. Xác định bài toán phân lớp
  2. Chuẩn bị dữ liệu: Thu thập, Thăm dò, Sơ chế, Trích chọn đặc trưng.
  3. Xây dựng và đánh giá mô hình
  4. Đánh giá giữa các mô hình
  5. Lựa chọn mô hình phù hợp nhất với mục tiêu bài toán

Tài liệu tham khảo[sửa | sửa mã nguồn]

  1. ^ Wikipedia tiếng Việt (2021). “Wikipedia”. Truy cập ngày 10 tháng 8 năm 2021.
  2. ^ Wikipedia tiếng Việt (2021). “Đặc biệt:Thống kê”. Truy cập ngày 10 tháng 8 năm 2021.
  3. ^ Wikipedia tiếng Việt (2021). “Wikipedia:Giữ thiện ý”. Truy cập ngày 10 tháng 8 năm 2021.
  4. ^ Wikipedia tiếng Việt (2021). “Wikipedia:Phá hoại”. Truy cập ngày 10 tháng 8 năm 2021.
  5. ^ a b c d Kumar, Srijan; Spezzano, Francesca; Subrahmanian, V.S. (2015). “VEWS: A Wikipedia Vandal Early Warning System” (PDF). Proceedings of the 21th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining: 607–616. doi:10.1145/2783258.2783367.
  6. ^ Thống kê Wikimedia (2021). “Wikipedia tiếng Việt”. Truy cập ngày 10 tháng 8 năm 2021.
  7. ^ Sarabadani, Amir (2016). “Lessons learned building machine learning models for Wikidata” (PDF). Wikimania 2016. tr. 19. Truy cập ngày 10 tháng 8 năm 2021.
  8. ^ Wikipedia tiếng Việt (2021). “Kết quả tìm kiếm của insource:/thiếu nhân lực/”. Truy cập ngày 10 tháng 8 năm 2021.
  9. ^ English Wikipedia (2021). “User:ClueBot NG”. Truy cập ngày 10 tháng 8 năm 2021.
  10. ^ MediaWiki (2021). “ORES”. Truy cập ngày 10 tháng 8 năm 2021.
  11. ^ Adler, B. Thomas; de Alfaro, Luca; Mola-Velasco, Santiago M.; Rosso, Paolo; West, Andrew G. (2011). “Wikipedia Vandalism Detection: Combining Natural Language, Metadata, and Reputation Features”. In: Gelbukh, A. (eds) Computational Linguistics and Intelligent Text Processing. CICLing 2011. Lecture Notes in Computer Science. Springer. 6609. doi:10.1007/978-3-642-19437-5_23.
  12. ^ Tran, Khoi-Nguyen; Christen, Peter; Sanner, Scott; Xie, Lexing (2015). “Context-Aware Detection of Sneaky Vandalism on Wikipedia Across Multiple Languages”. In: Cao, T., Lim, EP., Zhou, ZH., Ho, TB., Cheung, D., Motoda, H. (eds) Advances in Knowledge Discovery and Data Mining. PAKDD 2015. Lecture Notes in Computer Science(). Springer. 9077. doi:10.1007/978-3-319-18038-0_30.
  13. ^ Susuri, Arsim; Hamiti, Mentor; Dika, Agni (2016). “Machine learning based detection of vandalism in Wikipedia across languages”. 2016 5th Mediterranean Conference on Embedded Computing (MECO). Bar, Montenegro: 446–451. doi:10.1109/MECO.2016.7525689.
  14. ^ Huynh Chi Trung (2020). “Giới thiệu về Support Vector Machine (SVM)”. Truy cập ngày 11 tháng 2 năm 2022.
  15. ^ Chung Pham Van (2020). “Logistic Regression - Bài toán cơ bản trong Machine Learning”. Truy cập ngày 11 tháng 2 năm 2022.
  16. ^ Nguyễn Văn Hiếu. “Thuật toán K-Means (K-Means clustering) và ví dụ”. Truy cập ngày 11 tháng 2 năm 2022.
  17. ^ Vũ Hữu Tiệp (2017). “Bài 4: K-means Clustering”. Truy cập ngày 11 tháng 2 năm 2022.
  18. ^ Anjani Kumar (2020). “Introduction to the Gradient Boosting Algorithm”. Truy cập ngày 11 tháng 2 năm 2022.
  19. ^ Tuấn Nguyễn (2021). “Random Forest algorithm”. Truy cập ngày 11 tháng 2 năm 2022.
  20. ^ Nguyen Thi Hop (2019). “KNN (K-Nearest Neighbors)”. Truy cập ngày 11 tháng 2 năm 2022.
  21. ^ Python Software Foundation (2022). “What is Python? Executive Summary”. Truy cập ngày 11 tháng 2 năm 2022.
  22. ^ Perkel, Jeffrey M. (2018). “Why Jupyter is data scientists' computational notebook of choice”. Nature. 563: 145–146. doi:10.1038/d41586-018-07196-1.
  23. ^ Brownlee, Jason (2020). “Framework for Data Preparation Techniques in Machine Learning”. Truy cập ngày 21 tháng 8 năm 2021.
  24. ^ Brownlee, Jason (2020). “Data Preparation for Machine Learning (7-Day Mini-Course)”. Truy cập ngày 21 tháng 8 năm 2021.
  25. ^ a b Gangane, Tejali (2019). “Is data preparation similar to food preparation?”. Truy cập ngày 21 tháng 8 năm 2021.
  26. ^ Vimentor. “Tiền xử lý dữ liệu trong lĩnh vực học máy (Phần 1)”. Truy cập ngày 21 tháng 8 năm 2021.
  27. ^ English Wikipedia (2021). “Wikipedia:Bots”. Truy cập ngày 21 tháng 8 năm 2021.
  28. ^ Meta-Wiki (2021). “Research:Quarry”. Truy cập ngày 21 tháng 8 năm 2021.
  29. ^ Wikipedia tiếng Việt (2021). “Wikipedia:Hồi sửa”. Truy cập ngày 21 tháng 8 năm 2021.
  30. ^ Halfaker, Aaron (2016). “Basic damage detection in Wikipedia”. Truy cập ngày 21 tháng 8 năm 2021.
  31. ^ Lê Ngọc Khả Nhi (2018). “Deep learning: Bài toán nhị phân”. Truy cập ngày 21 tháng 8 năm 2021.
  32. ^ English Wikipedia (2021). “Data wrangling”. Truy cập ngày 21 tháng 8 năm 2021.
  33. ^ Brownlee, Jason (2020). “How to Scale Data With Outliers for Machine Learning”. Truy cập ngày 21 tháng 8 năm 2021.
  34. ^ Chaly, Mikalai (2021). “Compare different scalers on data with outliers”. Truy cập ngày 21 tháng 8 năm 2021.
  35. ^ scikit-learn. “Compare the effect of different scalers on data with outliers”. Truy cập ngày 21 tháng 8 năm 2021.
  36. ^ Brownlee, Jason (2020). “How to Use Quantile Transforms for Machine Learning”. Truy cập ngày 21 tháng 8 năm 2021.
  37. ^ Ferguson, Kayla (2018). “When Should You Delete Outliers from a Data Set?”. Truy cập ngày 21 tháng 8 năm 2021.
  38. ^ Wikipedia tiếng Việt (2021). “Trích chọn đặc trưng”. Truy cập ngày 21 tháng 8 năm 2021.
  39. ^ Brownlee, Jason (2020). “How to Choose a Feature Selection Method For Machine Learning”. Truy cập ngày 21 tháng 8 năm 2021.
  40. ^ scikit-learn. “3.1. Cross-validation: evaluating estimator performance”. Truy cập ngày 21 tháng 8 năm 2021.
  41. ^ Vũ Hữu Tiệp (2018). “Bài 33: Các phương pháp đánh giá một hệ thống phân lớp”. Truy cập ngày 21 tháng 8 năm 2021.
  42. ^ a b c Phạm Đình Khánh (2020). “Bài 46 - Đánh giá mô hình phân loại trong ML”. Truy cập ngày 21 tháng 8 năm 2021.
  43. ^ a b c Đinh Anh Thi (2019). “Hiểu confusion matrix”. Truy cập ngày 21 tháng 8 năm 2021.

Phụ lục 1[sửa | sửa mã nguồn]

Biểu đồ thống kê số sửa đổi mỗi ngày của Wikipedia tiếng Việt từ ngày 01/04/2021 đến ngày 30/06/2021 (Wikimedia Statistics).

Hình 6. Thống kê số sửa đổi mỗi ngày (01/04/2021 – 30/06/2021)

Phụ lục 2[sửa | sửa mã nguồn]

Thông tin mô tả các đặc trưng của các sửa đổi tại Wikipedia tiếng Việt từ ORES có thể tìm thấy thông qua tệp viwiki.py của dự án editquality (một dự án sử dụng revscoring và là một trong các dự án con của ORES).

Theo thông tin của viwiki.py, các đặc trưng được kế thừa từ các nguồn sau:

wikipedia.py[sửa | sửa mã nguồn]

  • revision.page.is_articleish – Page của revision có là một trang bài viết (articleish)
  • revision.page.is_mainspace – Page của revision có là một trang thuộc namespace Chính
  • revision.page.is_draftspace – Page của revision có là một trang thuộc namespace Nháp

wikitext.py[sửa | sửa mã nguồn]

Ghi nhận các đặc trưng về đối tượng parent (revision nằm trước một revision) và đối tượng diff. Ví dụ như:

  • wikitext.revision.parent.chars – Tổng số ký tự trang parent
  • wikitext.revision.parent.words – Tổng số từ trang parent
  • ...
  • wikitext.revision.diff.markup_delta_sum – Tổng chênh lệch số ký hiệu markup giữa 2 revision của diff
  • wikitext.revision.diff.uppercase_word_delta_sum – Tổng chênh lệch số từ in hoa giữa 2 revision của diff
  • ...

mediawiki.py[sửa | sửa mã nguồn]

Ghi nhận các đặc trưng về đối tượng comment (ghi chú/tóm lược của một sửa đổi), user_rights và protected_user (liên quan quyền người dùng). Ví dụ như:

  • revision.comment.suggests_section_edit – Comment của revision có là một suggests section edit (sửa đổi ở một section trong bài)
  • ...
  • revision.user.is_bot – Người dùng này có là bot
  • revision.user.is_admin – Người dùng này có là admin (bảo quản viên)
  • ...
  • revision_oriented.revision.user.is_anon – Người dùng này có là thành viên vô danh (dùng IP)
  • ...

Và các đặc trưng còn lại dựa vào việc phát hiện các từ thuộc các danh sách cho trước. Bao gồm:

badwords và enwiki.badwords[sửa | sửa mã nguồn]

Các đặc trưng liên quan đến việc phát hiện từ tục tĩu (badword). Ví dụ:

  • vietnamese.badwords.revision.diff.match_delta_sum – Tổng chênh lệch tần số xuất hiện từ tục tĩu tiếng Việt giữa 2 revision của diff
  • vietnamese.badwords.revision.diff.match_prop_delta_sum – Tổng chênh lệch tần suất xuất hiện từ tục tĩu tiếng Việt giữa 2 revision của diff
  • ...

informals và enwiki.informals[sửa | sửa mã nguồn]

Các đặc trưng liên quan đến việc phát hiện từ thiếu trang trọng (informal), tương tự các đặc trưng badwords.

dict_words[sửa | sửa mã nguồn]

Các đặc trưng liên quan đến việc phát hiện các từ thuộc hoặc không thuộc danh mục các từ thông dụng của từ điển, tương tự các đặc trưng badwords và informals.

Phụ lục 3[sửa | sửa mã nguồn]

Lý do trích chọn 70 đặc trưng đến từ nguyên tắc loại bỏ các đặc trưng có phương sai quá thấp. Sử dụng lớp VarianceThreshold của sklearn để loại bỏ các đặc trưng theo một ngưỡng phương sai cho trước, thấy rằng nếu phương sai ở mức 0.5 thì sẽ loại được 25/90 đặc trưng có phương sai dưới mức này, còn lại 65 đặc trưng.

Tuy nhiên, do 12/90 là biến định tính, do đó đã lựa chọn con số trích chọn đặc trưng là 70 thay vì 65. Mã xử lý được tham khảo từ How to Perform Data Cleaning for Machine Learning with Python (Machine Learning Mastery).

In [24]:

import pandas as pd
import matplotlib.pyplot as plt
from numpy import arange
from sklearn.feature_selection import VarianceThreshold

df = pd.read_csv('data/raw_revs.csv')
del df['rev_id']

X, y = df.iloc[:, :-1], df.iloc[:, -1]

thresholds = arange(0.0, 0.55, 0.05)
# apply transform with each threshold
results = list()

for t in thresholds:
    # define the transform
    transform = VarianceThreshold(threshold=t)
    # transform the input data
    X_sel = transform.fit_transform(X)
    # determine the number of input features
    n_features = X_sel.shape[1]
    print('>Threshold=%.2f, Features=%d' % (t, n_features))
    # store the result
    results.append(n_features)
    
# plot the threshold vs the number of selected features
plt.plot(thresholds, results)
plt.show()

Out [24]:

>Threshold=0.00, Features=87
>Threshold=0.05, Features=80
>Threshold=0.10, Features=76
>Threshold=0.15, Features=74
>Threshold=0.20, Features=69
>Threshold=0.25, Features=66
>Threshold=0.30, Features=66
>Threshold=0.35, Features=66
>Threshold=0.40, Features=65
>Threshold=0.45, Features=65
>Threshold=0.50, Features=65

Phụ lục 4[sửa | sửa mã nguồn]

Lý do chọn giá trị tham số C=80 của SVM là khi thực hiện kiểm tra tương quan giữa C và F1 thì C mặc định của hàm SVC (C=1.0) làm cho F1 rất thấp và không thể phát hiện được các sửa đổi phá hoại. Khi C tăng thì F1 cũng thay đổi theo, và theo kết quả nhận được thì C=80 sẽ cho F1=0.137363C=600 sẽ cho F1=0.182243.

Hình 7. Tương quan C và F1

Tuy nhiên, chỉ số Precision của C=600 (Precision=0.351351) lại không khả quan bằng C=80 (Precision=0.531915), và Recall giữa 2 C cũng không quá chênh như Precision. Do đó, quyết định lựa chọn tham số C=80 cho hàm SVC để xây dựng mô hình.