Bỏ qua tới nội dung chính
Quay lại tin tức

Mô hình của tôi đã gian lận trong bài kiểm tra của chính nó.

Towards Data Science· Abdullahi Dattijo· 14/8/2026general

Một quy trình tiền xử lý đã khiến mô hình dự đoán giá ô tô của tôi "nhìn trộm" tập dữ liệu thử nghiệm trước khi kiểm tra, và đạt được 12 điểm R-squared nhờ gian lận. Bài viết "My Model Was Cheating on Its Own Test" (Mô hình của tôi đã gian lận trong bài kiểm tra của chính nó) lần đầu tiên được đăng trên Towards Data Science.

Học máy Mô hình của tôi đã gian lận trong bài kiểm tra của chính nó Một quy trình tiền xử lý đã cho phép mô hình dự đoán giá ô tô của tôi "nhìn trộm" tập dữ liệu kiểm tra trước khi thi, và mười hai điểm R bình phương mà nó đã gian lận để đạt được. Abdullahi Dattijo Ngày 14/8/2026 13 phút đọc Chia sẻ Hình ảnh được tạo bởi tác giả bằng mô hình hình ảnh của OpenAI. Rò rỉ dữ liệu xảy ra khi một mô hình có được thông tin mà lẽ ra nó chưa được phép truy cập, và cái nhìn lén lút đó làm cho điểm số của nó trông tốt hơn thực tế. Kaufman, Rosset, Perlich và Stitelman đã mô tả rõ ràng điều này trong bài báo năm 2012 của họ trên tạp chí ACM Transactions on Knowledge Discovery from Data, với tiêu đề “Leakage in Data Mining: Formulation, Detection, and Avoidance” (Rò rỉ trong Khai phá dữ liệu: Công thức hóa, Phát hiện và Tránh). Một ví dụ họ đưa ra là Thử thách Khai phá dữ liệu INFORMS 2010. Các thí sinh được yêu cầu dự đoán biến động giá cổ phiếu chỉ bằng cách sử dụng tập dữ liệu huấn luyện, sau đó được chấm điểm trên một tập dữ liệu kiểm tra riêng biệt. Một số thí sinh đã tìm ra những cổ phiếu thực tế nào được ẩn trong tập dữ liệu kiểm tra bằng cách đối chiếu các mẫu với dữ liệu tài chính công khai. Điều đó cho phép họ thu thập thông tin mà tập dữ liệu kiểm tra lẽ ra phải giữ kín, và điểm số của họ trông tốt hơn so với những gì mô hình của họ thực sự xứng đáng. Sự rò rỉ của riêng tôi nhỏ hơn nhiều và dễ bỏ qua hơn nhiều. Nó không liên quan đến việc tìm ra danh tính cổ phiếu bị ẩn. Nó đến từ việc chạy hai dòng mã tiền xử lý thông thường sai thứ tự. Tôi đã huấn luyện một mạng nơ-ron nhỏ, sử dụng MLPRegressor của scikit-learn, để dự đoán giá ô tô từ một tập dữ liệu ô tô đã qua sử dụng. Phiên bản đầu tiên của dự án này đã báo cáo một điểm kiểm tra mạnh mẽ: R bình phương là 0,887. Sau khi tôi sửa thứ tự của hai dòng, điểm số trung thực là 0,767. Mô hình không trở nên tệ hơn. Phép đo đầu tiên của tôi đã âm thầm đọc một phần đáp án. Con số tưởng chừng vững chắc R bình phương là một điểm số phổ biến để đánh giá mức độ dự đoán một con số của mô hình. Nó chạy từ 0 đến 1. Giá trị càng cao càng tốt. R bình phương là 0,887 có nghĩa là mô hình giải thích khoảng 89 phần trăm sự khác biệt về giá ô tô trên dữ liệu mà nó chưa từng được huấn luyện. Phiên bản gốc của dự án này đã báo cáo R bình phương kiểm tra là 0,887 và lỗi kiểm tra, được đo bằng sai số bình phương trung bình (mean squared error), khoảng 6,9 triệu. Tôi đã chạy lại chính xác cùng một mã, với chính xác cùng một cài đặt, để kiểm tra xem con số đó có thật không, và nó khớp. Đối với một tập dữ liệu nhỏ dưới 200 chiếc ô tô, đó là một kết quả thực sự mạnh mẽ, loại kết quả kết thúc một bài tập về nhà mà không cần thêm câu hỏi nào. Thí nghiệm Dự án này bắt đầu từ một sự rò rỉ mà tôi tìm thấy trong một mô hình giá ô tô mà tôi đã xây dựng cho một bài tập trên lớp. Phiên bản đó sử dụng một tập dữ liệu không có giấy phép nào được liệt kê trên trang mà nó đến, vì vậy thay vì tái tạo nó ở đây, tôi đã xây dựng lại cùng một quy trình trên tập dữ liệu Automobile của UCI, do Jeffrey Schlimmer tặng vào năm 1987 và có nguồn gốc từ Niên giám Ô tô Ward năm 1985. Nó được phát hành theo giấy phép Creative Commons Attribution 4.0, cho phép tái sử dụng như thế này với việc ghi công. Hai tập dữ liệu mô tả cùng một loại đối tượng: mỗi hàng là một chiếc ô tô, với các thông số kỹ thuật và giá của nó. Mỗi hàng liệt kê hãng xe, kiểu thân xe, thông số kỹ thuật động cơ, loại nhiên liệu, xếp hạng rủi ro bảo hiểm được chỉ định và giá cả. Sau khi loại bỏ một cột có số lượng giá trị bị thiếu lớn và xóa các hàng có bất kỳ giá trị bị thiếu còn lại nào, 193 chiếc xe còn lại. Trong số 24 cột còn lại để dự đoán giá, 16 cột là các phép đo số, như dung tích động cơ, mã lực và trọng lượng không tải, và 8 cột là phân loại, như loại nhiên liệu, bánh dẫn động và vị trí động cơ. Mục tiêu là hồi quy: dự đoán giá, là một con số, thay vì phân loại xe thành các nhóm. Tôi đã sử dụng một mạng perceptron đa lớp, hay MLP. MLP là một loại mạng thần kinh: một mô hình được tạo thành từ các lớp gồm các đơn vị nhỏ được kết nối, trong đó mỗi đơn vị kết hợp các đầu vào của nó và chuyển kết quả đi tiếp. Mô hình này có hai lớp ẩn, mỗi lớp 64 đơn vị: MLPRegressor( hidden_layer_sizes=(64, 64), max_iter=1000, random_state=42, ) Tôi đã chia 193 hàng thành ba nhóm, giữ nguyên tỷ lệ như bài tập gốc: 60% cho huấn luyện, 20% cho xác thực và 20% cho kiểm thử, tương đương 115 hàng huấn luyện, 39 hàng xác thực và 39 hàng kiểm thử. Tập huấn luyện là dữ liệu mà mô hình thực sự học hỏi. Tập xác thực được dùng để kiểm tra mô hình trong quá trình phát triển. Tập kiểm thử là một lần kiểm tra cuối cùng, chỉ nên được xem xét khi mô hình đã hoàn thiện. Hai dòng, chạy sai thứ tự Trước khi bất kỳ mô hình nào có thể sử dụng dữ liệu này, các cột thô cần được chuẩn bị. Các cột số như mã lực cần được đưa về một thang đo chung, vì một số số sử dụng phạm vi nhỏ và một số khác sử dụng phạm vi lớn. Các cột phân loại như loại nhiên liệu cần được chuyển đổi thành các số mà mô hình có thể sử dụng, thông thường bằng cách biến mỗi danh mục thành cột 0 hoặc 1 riêng, một phương pháp gọi là mã hóa một nóng (one hot encoding). Bước chuẩn bị này thường được gọi là tiền xử lý (preprocessing), và trong scikit-learn, nó thường được thực hiện bằng một chuỗi các bước nhỏ gọi là pipeline. Dưới đây là mã tiền xử lý, theo thứ tự nó đã chạy để tạo ra kết quả trông mạnh mẽ ở trên: # Xử lý ngoại lệ thông qua giới hạn IQR for col in numerical_cols + ["price"]: Q1 = df[col].quantile(0.25) Q3 = df[col].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR df[col] = df[col].clip(lower=lower_bound, upper=upper_bound) # Pipeline tiền xử lý preprocessor = ColumnTransformer(transformers=[ ("num", StandardScaler(), numerical_cols), ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_cols), ]) # Chuyển đổi dữ liệu X_processed = preprocessor.fit_transform(X) # Chia tập huấn luyện-xác thực-kiểm thử X_train_val, X_test, y_train_val, y_test = train_test_split( X_processed, y, test_size=0.2, random_state=42 ) Không có gì trong đoạn mã này báo hiệu một vấn đề, đó là một phần của vấn đề. Đây là cùng một dạng lỗi mà tôi ban đầu tìm thấy trong bài tập lớp mà dự án này dựa trên, chỉ được viết lại với một tập dữ liệu có nguồn gốc khác ở đây để kết quả có thể là

Nguồn tin: Towards Data Science — Tác giả: Abdullahi Dattijo. Bản dịch tiếng Việt do AI thực hiện, có thể có sai sót.