Mục lục
- Mục tiêu bài học
- Feature Engineering là gì
- Khác biệt với Deep Learning
- Numerical transformation
- Binning (Discretization)
- Date / time features
- Cyclical encoding cho hour / month
- Interaction features
- Polynomial features
- Aggregate features (groupby)
- Text features cơ bản
- Domain knowledge
- Workflow FE
- Pitfall: data leak và overfitting
- Code Python end-to-end
- Bài tập
- Tóm tắt
Mục tiêu bài học
Sau bài học, bạn sẽ:
- Hiểu Feature Engineering (FE) là gì và vì sao trong ML cổ điển, chất lượng feature thường quan trọng hơn việc chọn model.
- Phân biệt vai trò của FE trong ML cổ điển và Deep Learning.
- Áp dụng được numerical transformation: log, sqrt, Box-Cox, Yeo-Johnson (sklearn
PowerTransformer). - Discretize feature liên tục bằng
pd.cut(equal-width) vàpd.qcut(equal-frequency). - Extract date features từ datetime; biết khi nào cần cyclical encoding (sin/cos).
- Tạo interaction features (BMI, ratio) và polynomial features (
PolynomialFeatures). - Tạo aggregate features bằng
groupbytrên dataset 2 bảng. - Nhận diện pitfall lớn nhất của FE: data leak (dùng future info hoặc target trong feature).
Feature Engineering là gì
Feature Engineering (FE) là quá trình biến đổi raw data thành các feature có ý nghĩa hơn cho model — thường bằng kết hợp domain knowledge, thống kê và biến đổi toán học. Các bài trước trong nhóm Chuẩn bị dữ liệu mới chỉ làm 2 việc: chuẩn hoá scale (Bài 7–9) và mã hoá categorical (Bài 10–11). Bài này bắt đầu tạo feature mới.
Trong ML cổ điển, FE thường có ảnh hưởng tới kết quả nhiều hơn cả việc chọn model. Một câu được lặp đi lặp lại trong ngành: "garbage in, garbage out" — model dù phức tạp đến đâu cũng không sửa được data tệ. Hai dataset cùng task, dataset có feature tốt + model đơn giản (Linear / Logistic Regression) thường thắng dataset feature thô + model phức tạp (XGBoost, neural network).
FE bao gồm 3 nhánh chính:
- Feature transformation — biến đổi 1 feature có sẵn (log, sqrt, binning, scaling, encoding).
- Feature construction — tạo feature mới từ nhiều feature có sẵn (interaction, polynomial, ratio, aggregate).
- Feature extraction — rút feature từ dữ liệu có cấu trúc đặc biệt (text, image, time series, date). PCA cũng thuộc nhóm này (Bài 36).
Bài này tập trung 2 nhánh đầu cho dữ liệu dạng bảng. Feature selection (chọn bỏ feature) sẽ tách riêng ở các bài về regularization (Bài 21) và model interpretability.
Khác biệt với Deep Learning
Một câu hỏi thường gặp: "Deep Learning tự học feature, vậy FE còn cần không?" Phân biệt rõ:
- ML cổ điển (Linear / Logistic Regression, SVM, Random Forest, XGBoost): con người chịu trách nhiệm FE. Model không tự kết hợp feature, không tự rút feature từ text / image. Chất lượng feature quyết định kết quả.
- Deep Learning (CNN, RNN, Transformer): các layer ẩn học representation tự động từ raw input (pixel, token). FE thủ công gần như không cần cho image và text — model tự rút feature qua các layer convolution / attention.
Nhưng DL vẫn cần basic preprocessing:
- Scaling (chia 255 cho pixel, normalize cho dữ liệu bảng).
- Tokenization cho text (chuyển từ string thành token id).
- Encoding cho categorical input.
Với dữ liệu dạng bảng (tabular), nhiều benchmark hiện tại vẫn cho thấy XGBoost / LightGBM với FE tốt cạnh tranh ngang ngửa hoặc thắng các tabular neural network (TabNet, FT-Transformer) — nên FE vẫn là kỹ năng cốt lõi của ML engineer, không phải kỹ năng "lỗi thời".
Numerical transformation
Nhóm biến đổi đơn biến — đầu vào 1 cột số, đầu ra 1 cột số đã biến đổi. Mục tiêu chính: đưa phân phối lệch (skewed) về gần normal để model linear / gradient descent hoạt động ổn định hơn.
Log transform
Công thức: \( x' = \log(x + 1) \). Cộng 1 (hoặc một hằng số nhỏ) để xử lý trường hợp \( x = 0 \) — \( \log(0) = -\infty \). Hàm này có sẵn trong NumPy:
import numpy as np
df["income_log"] = np.log1p(df["income"]) # log1p = log(1 + x)
Use case điển hình: feature có phân phối "dài đuôi" (heavy-tailed) — income, view count, price, population, transaction amount. Log nén đuôi dài về kích thước hợp lý, đưa phân phối gần normal hơn.
Square root
Công thức: \( x' = \sqrt{x} \). Tác dụng tương tự log nhưng nén nhẹ hơn — phù hợp khi distribution lệch vừa phải (count data nhỏ: số lần click, số bình luận). Dùng np.sqrt(x).
Box-Cox và Yeo-Johnson
Box-Cox là transform parametric, học một tham số \( \lambda \) để tối ưu hoá độ "normal" của output:
\[ x'(\lambda) = \begin{cases} \dfrac{x^{\lambda} - 1}{\lambda} & \text{nếu } \lambda \ne 0 \\ \log(x) & \text{nếu } \lambda = 0 \end{cases} \]Box-Cox đòi hỏi \( x > 0 \). Yeo-Johnson là mở rộng của Box-Cox cho mọi giá trị thực (cả 0 và âm). Sklearn cung cấp class PowerTransformer:
from sklearn.preprocessing import PowerTransformer
pt = PowerTransformer(method="yeo-johnson") # hoặc "box-cox"
df[["income_yj"]] = pt.fit_transform(df[["income"]])
Khác với log (cố định), PowerTransformer chọn \( \lambda \) tự động cho từng feature. Lưu ý: fit chỉ chạy trên train (giống mọi scaler — xem lại Bài 7).
Square / Cube
Ngược lại với log — tăng skew, nhấn mạnh giá trị lớn. Hữu ích khi feature có quan hệ phi tuyến với target (vd diện tích → giá nhà có quan hệ gần \( x^2 \)). Thường dùng kèm polynomial features (mục 9).
Reciprocal
Công thức: \( x' = 1/x \). Ít dùng. Phù hợp khi feature gốc có ý nghĩa "thời gian" và bạn muốn đổi sang "tốc độ" (ngược lại): seconds → 1/seconds = rate. Phải xử lý \( x = 0 \) riêng.
Binning (Discretization)
Binning (hay discretization) biến một feature continuous thành categorical bằng cách chia trục số thành các đoạn (bin). Mỗi sample rơi vào 1 bin.
Equal-width binning
Mỗi bin có cùng độ rộng. Pandas: pd.cut.
import pandas as pd
ages = pd.Series([5, 12, 18, 25, 40, 65, 80])
bins = [0, 12, 18, 60, 120]
labels = ["child", "teen", "adult", "senior"]
age_group = pd.cut(ages, bins=bins, labels=labels, right=False)
print(age_group.tolist())
# ['child', 'teen', 'adult', 'adult', 'adult', 'senior', 'senior']
Nếu chỉ truyền bins=4 (số nguyên), pd.cut tự chia trục thành 4 đoạn đều — cách này nhạy với outlier (một outlier kéo dài range, các bin "thường" bị dồn về một góc).
Equal-frequency binning (quantile)
Mỗi bin có cùng số sample. Pandas: pd.qcut.
incomes = pd.Series([5, 8, 10, 12, 15, 20, 30, 50, 100, 500])
quartile = pd.qcut(incomes, q=4, labels=["Q1", "Q2", "Q3", "Q4"])
print(quartile.tolist())
# ['Q1', 'Q1', 'Q1', 'Q2', 'Q2', 'Q3', 'Q3', 'Q4', 'Q4', 'Q4']
pd.qcut robust với outlier hơn — outlier chỉ chiếm bin cuối, không kéo các bin khác lệch theo.
Pros / Cons
- Pros: robust với outlier (đặc biệt
qcut); biến quan hệ phi tuyến thành step function (model linear bắt được); dễ giải thích cho stakeholder ("nhóm tuổi adult"); xử lý được missing như một bin riêng. - Cons: mất thông tin — sample 25 và 50 cùng bin "adult" trở thành "giống nhau"; cần encoding tiếp (one-hot, ordinal — Bài 10–11) trước khi đưa vào model; mất ý nghĩa thứ tự nếu encoding sai.
Use case điển hình: age → age_group; income → income_quartile; rating số → rating_category. Khi không chắc chắn về cutoff, bắt đầu với qcut rồi điều chỉnh theo domain.
Date / time features
Một cột datetime đưa thẳng vào model thường vô dụng — model không hiểu "2026-05-24". Cần extract các trường có ý nghĩa. Pandas có Series.dt accessor cho mọi cột datetime:
import pandas as pd
df = pd.DataFrame({"timestamp": pd.to_datetime([
"2026-05-24 09:30", "2026-05-25 14:00", "2026-12-31 23:50"
])})
df["year"] = df["timestamp"].dt.year
df["month"] = df["timestamp"].dt.month
df["day"] = df["timestamp"].dt.day
df["hour"] = df["timestamp"].dt.hour
df["minute"] = df["timestamp"].dt.minute
df["day_of_week"] = df["timestamp"].dt.dayofweek # 0 = Monday, 6 = Sunday
df["day_of_year"] = df["timestamp"].dt.dayofyear
df["week_of_year"] = df["timestamp"].dt.isocalendar().week
df["is_weekend"] = (df["day_of_week"] >= 5).astype(int)
Các trường thường có giá trị cho model:
hour— pattern theo giờ (e-commerce: traffic cao buổi tối).day_of_week,is_weekend— pattern theo tuần (delivery cuối tuần đông hơn).month,quarter— pattern theo mùa (du lịch hè cao điểm).is_holiday— cần lookup table riêng (ví dụ thư việnholidayscho lịch quốc gia).days_since_event— chênh lệch ngày với một mốc (vd ngày đăng ký user).
Không phải lúc nào cũng cần tất cả; chọn theo domain. Ví dụ predict số đơn hàng theo ngày: day_of_week, is_weekend, is_holiday thường đủ; minute hầu như vô nghĩa.
Cyclical encoding cho hour / month
Vấn đề với hour nếu để nguyên: model nhìn thấy 23 và 1 là 2 giá trị "xa nhau", nhưng thực tế chỉ cách nhau 2 giờ (11 PM và 1 AM). Tương tự: tháng 12 và tháng 1, thứ 7 và chủ nhật.
Giải pháp: cyclical encoding — biến giá trị tuần hoàn thành 2 feature \( \sin \) và \( \cos \). Với hour (period = 24):
\[ \text{hour\_sin} = \sin\!\left(\frac{2\pi \cdot \text{hour}}{24}\right), \quad \text{hour\_cos} = \cos\!\left(\frac{2\pi \cdot \text{hour}}{24}\right) \]import numpy as np
df["hour_sin"] = np.sin(2 * np.pi * df["hour"] / 24)
df["hour_cos"] = np.cos(2 * np.pi * df["hour"] / 24)
df["month_sin"] = np.sin(2 * np.pi * df["month"] / 12)
df["month_cos"] = np.cos(2 * np.pi * df["month"] / 12)
Sau encoding, mỗi giờ ánh xạ thành 1 điểm trên đường tròn đơn vị — 23h và 1h gần nhau, 12h và 0h đối xứng. Cần cả 2 sin và cos: một mình sin không phân biệt được 30 độ và 150 độ (cùng \( \sin = 0.5 \)).
Cyclical encoding chỉ cần với model nhạy với khoảng cách (Linear / Logistic Regression, KNN, SVM, NN). Với tree-based, để nguyên hour dạng integer cũng được — tree có thể tự "biết" 23 và 1 thuộc cùng nhóm bằng cách tạo 2 split (nhưng thường kém efficient hơn cyclical).
Interaction features
Interaction = kết hợp 2 feature có sẵn bằng phép toán cơ bản: cộng, trừ, nhân, chia. Use case lớn nhất: model linear không tự học interaction (chỉ học hệ số riêng cho từng feature) — phải tạo thủ công.
Ví dụ kinh điển trong y khoa: BMI tính từ weight và height:
df["bmi"] = df["weight"] / df["height"] ** 2
Linear Regression nếu có riêng weight và height không thể tự phát hiện công thức trên — kết quả tệ hơn hẳn so với khi đưa thẳng bmi vào.
Các interaction thường gặp:
- Ratio:
price_per_sqm = price / area,income_per_member = income / household_size,discount_pct = discount / original_price. - Difference:
days_since_signup = today - signup_date,price_diff = current_price - avg_price. - Product:
area = length * width,revenue = quantity * unit_price. - Sum:
total_score = score_math + score_physics.
Quy tắc: chọn interaction có ý nghĩa. Tạo bừa feature_i * feature_j cho mọi cặp sinh ra hàng chục feature rác, làm chậm training và tăng nguy cơ overfitting. Polynomial features (mục 9) là cách tạo interaction tự động — dùng cẩn thận.
Polynomial features
Sklearn cung cấp PolynomialFeatures — tự động generate các luỹ thừa và tích chéo. Với input 2 feature \( (x, y) \) và degree=2:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
X = np.array([[2.0, 3.0],
[4.0, 1.0]])
pf = PolynomialFeatures(degree=2, include_bias=False)
X_poly = pf.fit_transform(X)
print(pf.get_feature_names_out(["x", "y"]))
# ['x' 'y' 'x^2' 'x y' 'y^2']
print(X_poly)
# [[ 2. 3. 4. 6. 9.]
# [ 4. 1. 16. 4. 1.]]
Use case chính: model linear (Linear / Logistic Regression) có thể fit được quan hệ phi tuyến sau khi expand polynomial — đây cũng là nền tảng của Polynomial Regression (Bài 20).
Tham số quan trọng:
degree— bậc tối đa.degree=2phổ biến nhất.interaction_only=True— chỉ tạo tích chéo (\( xy \)), bỏ luỹ thừa (\( x^2 \)). Hữu ích khi chỉ cần interaction giữa các feature.include_bias=False— bỏ cột hằng số 1 (sklearn estimator đã tự thêm intercept).
Pitfall: feature explosion. Số feature output tăng nhanh theo bậc và số input. Với \( n \) feature gốc và degree \( d \), số feature output là \( \binom{n + d}{d} \). Ví dụ \( n = 10, d = 3 \) → 286 feature; \( n = 50, d = 3 \) → 23,426 feature. Hậu quả: chậm training, dễ overfitting, phải dùng kèm regularization (Ridge / Lasso — Bài 21). Quy tắc: chỉ dùng polynomial với ít feature hoặc sau khi đã chọn lọc.
Aggregate features (groupby)
Khi dữ liệu nằm trong nhiều bảng có quan hệ (user + transaction, customer + order), có thể tổng hợp thông tin từ bảng "nhiều" lên bảng "một" — tạo aggregate feature cho từng entity.
import pandas as pd
users = pd.DataFrame({"user_id": [1, 2, 3]})
trans = pd.DataFrame({
"user_id": [1, 1, 1, 2, 2, 3],
"amount": [100, 200, 50, 500, 300, 80],
})
agg = trans.groupby("user_id").agg(
total_spent=("amount", "sum"),
avg_spent=("amount", "mean"),
max_spent=("amount", "max"),
num_transactions=("amount", "count"),
).reset_index()
users = users.merge(agg, on="user_id", how="left")
print(users)
# user_id total_spent avg_spent max_spent num_transactions
# 0 1 350 116.67 200 3
# 1 2 800 400.00 500 2
# 2 3 80 80.00 80 1
Aggregate phổ biến: sum, mean, median, min, max, std, count, nunique. Có thể thêm time window: total_spent_last_30d, avg_amount_last_7d (xem lại groupby của pandas — Series 1 Bài 41).
Với user không có transaction (như user_id=4 nếu thêm vào), merge how="left" sẽ trả về NaN — cần xử lý missing (fillna 0 cho count, fillna mean cho amount tuỳ ngữ cảnh).
Text features cơ bản
Với cột text (review, title, comment), trước khi đi vào TF-IDF / embedding có thể rút một số feature đơn giản — đôi khi đủ tốt cho task baseline:
df["char_count"] = df["text"].str.len()
df["word_count"] = df["text"].str.split().str.len()
df["upper_count"] = df["text"].str.count(r"[A-Z]")
df["digit_count"] = df["text"].str.count(r"\d")
df["exclaim_count"] = df["text"].str.count(r"!")
df["avg_word_len"] = df["char_count"] / df["word_count"].replace(0, 1)
Use case: phát hiện spam (nhiều chữ hoa, nhiều dấu chấm than), phân loại độ dài review, đánh giá độ "formal" của email. Đây là feature thường xuyên có trong các baseline classification text.
Sâu hơn (TF-IDF, n-gram, embedding) sẽ học ở Series 4 (LLM & Generative AI). Trong Series 2 này, dừng ở feature count đơn giản.
Domain knowledge
Phần khó nhất của FE không phải kỹ thuật mà là biết feature nào có ý nghĩa cho task — phụ thuộc domain. Một số ví dụ:
- E-commerce:
discount_pct = discount / original_price;days_since_last_purchase;session_duration;cart_abandonment_rate. - Bất động sản:
price_per_sqm;age_of_property;distance_to_metro;rooms_per_area. - Tín dụng:
debt_to_income_ratio;num_late_payments_last_12m;credit_utilization = balance / credit_limit. - Marketing:
days_since_signup;num_emails_opened_last_30d;conversion_rate_by_channel. - Y tế:
BMI;systolic / diastolic;resting_heart_rate+max_heart_rateratio.
Một feature engineering xuất sắc thường đến từ hiểu biết về cách business hoạt động — không phải từ thử ngẫu nhiên hàng trăm phép biến đổi. Nói chuyện với chuyên gia domain trước khi viết code thường tiết kiệm hàng giờ thử nghiệm vô ích.
Workflow FE
FE không phải làm một lần xong là hết — đây là quy trình lặp:
- Hypothesis: đặt giả thuyết "feature X có thể giúp model" dựa trên domain hoặc EDA (Exploratory Data Analysis).
- Implement: code tạo feature.
- Test: train model với feature mới và không có, so sánh metric (cross-validation score, không phải test set — Bài 6).
- Iterate: giữ nếu cải thiện, bỏ nếu không, đề xuất feature tiếp theo.
Quy tắc thực dụng:
- Bắt đầu với baseline model + feature thô. Đo metric. Đây là điểm so sánh.
- Thêm 1–2 feature mỗi vòng. Thêm cả chục feature cùng lúc khó biết feature nào đóng góp.
- Track lại tất cả thí nghiệm (notebook + git, hoặc tool như MLflow — Series 6).
- Đặt giới hạn thời gian. FE có thể đi mãi không cùng — sau khi diminishing return, chuyển sang tinh chỉnh model hoặc thu thêm data.
Pitfall: data leak và overfitting
FE là vùng dễ tạo ra data leak nhất trong toàn pipeline. Một số tình huống cụ thể:
Leak target vào feature
Tạo avg_target_per_user = mean target của mỗi user — feature này chứa target. Model "biết đáp án" trước khi predict; CV score đẹp nhưng deploy thì thất bại. Tương tự: num_clicks_after_view nếu target là "có click sau khi view".
Leak future info
Khi data có time stamp: tính avg_amount_per_user trên toàn bộ dataset, rồi join vào row có ngày trong quá khứ — feature row đó chứa thông tin từ tương lai mà tại thời điểm predict chưa biết. Đúng: chỉ aggregate dữ liệu có timestamp nhỏ hơn row hiện tại.
Leak từ test sang train
Fit PowerTransformer, PolynomialFeatures, hoặc tính mean / std cho aggregate trên toàn bộ data (train + test) trước khi split. min, mean, lambda của test đã rò qua train. Đúng: fit chỉ trên train, transform cho test (giống Bài 7).
Overfitting do quá nhiều feature
Polynomial degree cao + interaction tay không kiểm soát → hàng nghìn feature. Train metric đẹp, test metric tệ. Dấu hiệu: gap lớn giữa train và validation score. Khắc phục: feature selection, regularization (Ridge / Lasso — Bài 21), hoặc giảm degree.
Quy tắc duy nhất nhớ được sau khi quên hết: mọi transform có học (fit) phải fit chỉ trên train; mọi aggregate / target encoding phải tôn trọng order thời gian. Bài 14 sẽ học sklearn.pipeline.Pipeline — công cụ giúp tự động giữ quy tắc này khi chạy cross-validation.
Code Python end-to-end
Log transform cho income
import numpy as np
import pandas as pd
df = pd.DataFrame({"income": [5e6, 8e6, 12e6, 25e6, 100e6, 1e9]})
df["income_log"] = np.log1p(df["income"])
print(df)
# income income_log
# 0 5.0e+06 15.43
# 1 8.0e+06 15.89
# 2 1.2e+07 16.30
# 3 2.5e+07 17.03
# 4 1.0e+08 18.42
# 5 1.0e+09 20.72
# Khoảng cách giữa 5 triệu và 1 tỷ chỉ còn ~5 trên log-scale
Extract date features
df = pd.DataFrame({"timestamp": pd.to_datetime([
"2026-05-24 09:30", "2026-05-25 22:15", "2026-12-31 23:50",
])})
df["hour"] = df["timestamp"].dt.hour
df["day_of_week"] = df["timestamp"].dt.dayofweek
df["is_weekend"] = (df["day_of_week"] >= 5).astype(int)
# Cyclical encoding cho hour
df["hour_sin"] = np.sin(2 * np.pi * df["hour"] / 24)
df["hour_cos"] = np.cos(2 * np.pi * df["hour"] / 24)
print(df[["timestamp", "hour", "is_weekend", "hour_sin", "hour_cos"]])
Polynomial features
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
X = np.array([[1.0, 2.0],
[3.0, 4.0]])
pf = PolynomialFeatures(degree=2, include_bias=False)
X_poly = pf.fit_transform(X)
print(pf.get_feature_names_out(["x", "y"]))
# ['x' 'y' 'x^2' 'x y' 'y^2']
print(X_poly)
# [[ 1. 2. 1. 2. 4.]
# [ 3. 4. 9. 12. 16.]]
Interaction feature: BMI
df = pd.DataFrame({
"weight_kg": [60, 75, 80, 95],
"height_m": [1.65, 1.70, 1.75, 1.80],
})
df["bmi"] = df["weight_kg"] / df["height_m"] ** 2
print(df)
# weight_kg height_m bmi
# 0 60 1.65 22.04
# 1 75 1.70 25.95
# 2 80 1.75 26.12
# 3 95 1.80 29.32
Bài tập
- Cho
df = pd.DataFrame({"view_count": [10, 50, 200, 1000, 5000, 100000]})có distribution lệch mạnh. Applynp.log1ptạo cộtview_count_log. So sánh range của 2 cột. - Cho
df["order_date"] = pd.to_datetime(["2026-05-23", "2026-05-24", "2026-05-25", "2026-05-30"]). Extractday_of_weekvà tạo cờis_weekend(1 nếu Thứ 7 hoặc Chủ Nhật). - Cho
df = pd.DataFrame({"price": [3e9, 5e9, 8e9], "area": [60, 80, 120]})(giá VND, diện tích m²). Tạo cột interactionprice_per_sqm. - Cho
ages = pd.Series([5, 12, 18, 25, 40, 65, 80]). Bin thành 4 nhóm["child", "teen", "adult", "senior"]với cutoff[0, 12, 18, 60, 120]bằngpd.cut. - Cho
X = np.array([[2.0, 3.0], [1.0, 4.0], [3.0, 2.0]]). DùngPolynomialFeatures(degree=2, include_bias=False). In ra tên feature output và số cột output. Tính số feature lý thuyết cho \( n = 2, d = 2 \) bằng công thức \( \binom{n + d}{d} - 1 \) (trừ bias).
Đáp án ngắn
- Range gốc
10 – 100,000(chênh 10,000 lần) → log~2.4 – 11.5(chênh ~5 lần). Phân phối đã "phẳng" hơn nhiều. day_of_week:[5, 6, 0, 5].is_weekend:[1, 1, 0, 1].price_per_sqm:[5e7, 6.25e7, 6.67e7](50 triệu/m², 62.5 triệu/m², 66.7 triệu/m²).- Kết quả:
['child', 'teen', 'adult', 'adult', 'adult', 'senior', 'senior'](vớiright=False) — tuổi 12 vàoteen; với mặc địnhright=True, 12 vàochild. - Output 5 cột:
['x0', 'x1', 'x0^2', 'x0 x1', 'x1^2']. Công thức: \( \binom{4}{2} - 1 = 6 - 1 = 5 \). Khớp.
Tóm tắt
- Feature Engineering là quá trình biến đổi raw data thành feature có ý nghĩa cho model — gồm transformation, construction, extraction. Trong ML cổ điển, chất lượng feature thường quan trọng hơn việc chọn model.
- Numerical transformation:
np.log1pcho phân phối lệch dài đuôi;np.sqrtnén nhẹ hơn;PowerTransformer(Box-Cox / Yeo-Johnson) học \( \lambda \) tự động. - Binning:
pd.cut(equal-width),pd.qcut(equal-frequency, robust với outlier). Pros: robust, dễ giải thích. Cons: mất thông tin. - Date features: extract
year,month,hour,day_of_week,is_weekendquaSeries.dt. Cyclical encoding (\( \sin \), \( \cos \)) cho feature tuần hoàn để model thấy 23h gần 1h. - Interaction: cộng / trừ / nhân / chia 2 feature. Ví dụ BMI = weight / height². Linear model không tự học interaction — phải tạo thủ công.
- PolynomialFeatures: generate \( x^2, xy, y^2 \) từ \( (x, y) \). Cho phép linear model fit nonlinear. Pitfall: feature explosion với degree cao và \( n \) lớn.
- Aggregate:
groupby().agg()trên bảng "nhiều" (transaction), merge vào bảng "một" (user) — tạototal_spent,avg_spent,num_transactions. - Text count features cơ bản:
char_count,word_count,upper_count. TF-IDF / embedding sẽ học ở Series 4. - Domain knowledge quyết định feature nào có ý nghĩa — không thay được bằng kỹ thuật.
- Workflow: hypothesis → implement → test (CV score) → iterate. Mỗi vòng chỉ thêm 1–2 feature.
- Pitfall: data leak (target / future info trong feature, fit transform trên toàn bộ data); overfitting do quá nhiều feature. Quy tắc vàng: mọi transform có học chỉ fit trên train. Bài 14 sẽ học
Pipelineđể tự động giữ quy tắc này.
