Feature Engineering
Xây dựng đặc trưng — Chọn nguyên liệu cho AI
Bạn có bảng dữ liệu khách hàng với cột 'Ngày sinh = 1995-05-30'. Mô hình học máy sẽ làm gì với con số này?
Raw data và feature thực ra khác nhau thế nào?
Raw data là những gì người dùng đánh vào hoặc hệ thống log lại: ngày sinh, địa chỉ dài dòng, số tiền, nhãn danh mục dưới dạng chữ. Feature là phiên bản đã được mô hình hiểu: tuổi (số nguyên), quận (đã tách khỏi địa chỉ), log(giá), cột 0/1 cho mỗi danh mục.
Raw — mô hình KHÔNG hiểu
dob = "1995-05-30"
price = 8_500_000
address = "12 Nguyễn Huệ, Q1, TP HCM"
category = "electronics"
Sau khi engineer — mô hình ĐÃ hiểu
age = 30, gen = "Millennial"
log_price = 15.96, price_bin = "cao"
city = "TP HCM", district = "Q1"
cat_electronics = 1, cat_food = 0
Hình minh họa
Bảng dưới đây có năm hàng và bốn cột thô. Chạm vào một đầu cộtđể xem các phép biến đổi dành cho cột đó. Mỗi phép biến đổi kèm một đoạn code ngắn và kết quả đã sinh — so sánh “trước” với “sau” ngay tại chỗ.
| 1995-05-30 | 125.000 | 42 Lý Thường Kiệt, Hoàn Kiếm, Hà Nội | food |
| 2003-11-02 | 8.500.000 | 12 Nguyễn Huệ, Quận 1, TP HCM | electronics |
| 1988-07-14 | 450.000 | 7 Trần Phú, Hải Châu, Đà Nẵng | food |
| 2010-02-20 | 1.200.000 | 25 Hùng Vương, Huế | fashion |
| 1999-09-09 | 65.000 | 88 Trần Hưng Đạo, Quận 5, TP HCM | food |
Ý tưởng: '1995-05-30' không nói gì cho mô hình. 'Tuổi 30' + 'Millennial' mới là tín hiệu có ý nghĩa.
Code pandas
import pandas as pd
df["dob"] = pd.to_datetime(df["dob"])
df["age"] = (pd.Timestamp("2025-01-01") - df["dob"]).dt.days // 365
df["gen"] = pd.cut(
df["age"],
bins=[0, 14, 29, 44, 120],
labels=["Gen Alpha", "Gen Z", "Millennial", "Gen X+"],
)Trước → sau
| date_of_birth | age | gen |
|---|---|---|
| 1995-05-30 | 29 | Gen Z |
| 2003-11-02 | 21 | Gen Z |
| 1988-07-14 | 36 | Millennial |
Minh hoạ từng loại biến đổi
Datetime decomposition: 1 timestamp → 3 tín hiệu
Binning: biến giá liên tục thành nhóm
Một cột số liên tục → 4 nhóm có ý nghĩa. Mô hình học từ nhóm, không phải từng số lẻ.
One-hot: chuỗi → ma trận 0/1
| category | cat_food | cat_electronics | cat_fashion |
|---|---|---|---|
| food | 1 | 0 | 0 |
| electronics | 0 | 1 | 0 |
| food | 1 | 0 | 0 |
| fashion | 0 | 0 | 1 |
Aggregation: gộp nhiều giao dịch → hành vi user
| user_id | tx_count_30d | mean_amount | days_since_last |
|---|---|---|---|
| u1 | 3 | 180k | 1 |
| u2 | 12 | 2.100k | 0 |
| u3 | 1 | 50k | 45 |
Mỗi hàng trước đây là 1 giao dịch → giờ là 1 user. Đây là RFM kinh điển trong e-commerce.
Trước: giá thô (VND)
Một hàng lấn át tất cả — outlier kéo hết trung bình.
Sau: log(1 + giá)
Các cột đều đặn hơn — mô hình tuyến tính sẽ học ổn định.
Mô hình không biết “1995” là năm, không biết “electronics” là danh mục, không biết “Hoàn Kiếm” là một quận đắt đỏ.
Feature engineering là bước bạn dịch ngôn ngữ của con người sang ngôn ngữ của mô hình. Và đúng như nấu ăn, người dịch giỏi thường thắng người có bếp đắt tiền.
Bạn dự đoán liệu khách Shopee có mua hàng trong 7 ngày tới. Data có: user_id, giới tính, ngày đăng ký, danh sách đơn hàng gần đây (mỗi đơn có ngày, số tiền). Feature nào sẽ mạnh nhất?
Giải thích
Feature engineering xoay quanh ba câu hỏi: (1) mô hình cần loại tín hiệu nào, (2) raw data đang ở dạng gì, (3) phép biến đổi nào ép nó về dạng mô hình tiêu hoá được. Bốn nhóm dữ liệu phổ biến sau đây xuất hiện trong hầu hết bài toán công nghiệp, mỗi nhóm có bộ kỹ thuật riêng.
Feature số là dễ nhất vì mô hình đã hiểu số. Nhưng “hiểu” không có nghĩa “học tốt” — bạn vẫn cần giúp bằng cách nén, chuẩn hoá, hoặc phân nhóm.
StandardScaler (mean 0, std 1) hoặc MinMax (0 – 1). Bắt buộc cho linear / neural net.
Nén long tail (giá, thu nhập, view). Không đổi thứ tự, giảm ảnh hưởng outlier.
Chia số liên tục thành nhóm: tuổi → nhóm tuổi, giá → rẻ/vừa/cao.
diện_tích × số_tầng = tổng diện tích sàn. Linear cần, tree-based tự học.
import numpy as np, pandas as pd
from sklearn.preprocessing import StandardScaler
df["log_price"] = np.log1p(df["price"])
df["age_bin"] = pd.cut(
df["age"],
bins=[0, 18, 30, 45, 120],
)
scaler = StandardScaler()
df[["age_s", "log_price_s"]] = scaler.fit_transform(
df[["age", "log_price"]]
)Công thức 1 — Log transform
Khi một cột số có phân phối lệch phải mạnh (giá, thu nhập, lượt view), ta thường dùng log(1 + x) thay cho x. log1p an toàn với 0, và nén đuôi dài về gần đối xứng:
Nói nôm na: 1 nghìn và 10 nghìn từng cách nhau 9 nghìn ⇒ sau log, khoảng cách chỉ còn ~2.3. Mô hình tuyến tính không bị một vài giao dịch 100 triệu nuốt chửng trung bình.
Trước: giá thô (VND)
Một hàng lấn át tất cả — outlier kéo hết trung bình.
Sau: log(1 + giá)
Các cột đều đặn hơn — mô hình tuyến tính sẽ học ổn định.
Công thức 2 — Target encoding có smoothing
Khi category có quá nhiều giá trị (vài trăm quận), one-hot không còn hợp. Thay mỗi category bằng trung bình target tại category đó, pha thêm trung bình toàn cục để category hiếm không bị lệch:
nx là số lần category x xuất hiện; ȳxlà trung bình target trong category; ȳ là trung bình toàn cục; m là “sức mạnh prior” (thường 10 – 30). Category xuất hiện nhiều ⇒ dùng giá trị riêng; category hiếm ⇒ kéo về trung bình toàn cục để chống overfit.
StandardScaler.- EDA: phân phối, NaN, correlation với target.
- Baseline với feature thô + encoding tối thiểu. Ghi metric.
- Thêm từng nhóm (datetime → categorical → text). Đo gain từng bước.
- Feature selection: bỏ cột importance thấp hoặc trùng.
- Đóng gói thành
sklearn.Pipelineđể tái lập và tránh leak.
Tiếp theo, xem feature engineering chạy thực tế trong bài chống lừa đảo ngân hàng — nơi mỗi feature đúng giúp bắt hàng trăm giao dịch gian lận/giây.
- Mô hình không hiểu chuỗi, không hiểu ngày, không hiểu địa chỉ dài. Bạn phải biến chúng thành số hoặc ma trận 0/1.
- Bốn nhóm kỹ thuật chính: numerical (log, bin, scale), categorical (one-hot, target encode), datetime (hour, dow, age), text (TF-IDF, embedding).
- Thêm feature vô tội vạ làm mô hình overfit. Thêm ít feature có ý nghĩa luôn thắng.
- Đóng gói toàn bộ pipeline bằng sklearn.Pipeline + ColumnTransformer để tránh leak và tái lập được.
Kiểm tra hiểu biết
Bạn dự đoán ai sẽ đặt món trong 7 ngày tới. Feature nào sau đây KHÔNG hữu ích?