1. Cơ sở lý thuyết

1.1 Xử lý dữ liệu (Feature Engineering)

Khi xây dựng mô hình dự đoán giá, việc xử lý dữ liệu đầu vào là bước quan trọng. Project sử dụng một pipeline để đảm bảo các thao tác được áp dụng thống nhất trên train/test/validation:

  • Loại bỏ cột có nhiều giá trị khuyết: các cột IdAlleyPoolQCFenceMiscFeature bị loại khỏi dữ liệu vì có tỷ lệ null rất cao.
  • Tách train–test–val và stratify: hàm data_split() cho phép chia dữ liệu thành train–test và tùy chọn validation. Tài liệu scikit‑learn nhấn mạnh rằng khi dùng cross‑validation, cần giữ lại một tập test cuối cùng và dùng k‑fold để đánh giá mô hình.
  • Imputation theo kiểu dữ liệu: biến số được điền thiếu bằng median (SimpleImputer(strategy='median')), còn biến phân loại điền bằng giá trị phổ biến nhất (most_frequent). Điều này tốt hơn việc điền 'none' cho toàn bộ biến phân loại như ở phiên bản cũ.
  • Mã hóa one‑hot: hàm one_hot_encode() mã hóa các biến phân loại với tùy chọn drop='first' để tránh đa cộng tuyến. Encoder được lưu lại để áp dụng lên tập test/val.
  • Chuẩn hóa và biến đổi: tùy chọn lựa chọn scaler (Robust, MinMax, Standard) và biến đổi (Yeo‑Johnson) để đưa các biến số về thang đo hợp lý trước khi huấn luyện. Ngoài ra, có thể tạo đặc trưng đa thức bậc hai(PolynomialFeatures với degree=2 và chỉ lấy tương tác) giúp mô hình tuyến tính học được quan hệ phi tuyến.
  • Lựa chọn đặc trưng: người dùng có thể chọn các phương pháp như mutual information, RandomForest, XGBoost để giữ lại K đặc trưng quan trọng nhất.

1.2 Xây dựng mô hình (Modeling)

Sau khi dữ liệu được xử lý, bước tiếp theo là lựa chọn mô hình và huấn luyện. Ngoài ba mô hình tuyến tính, Project bổ sung nhiều thuật toán từ đơn giản đến ensemble và boosting:

  • Linear RegressionRidge và Lasso:
    L1 (Lasso) phạt tổng trị tuyệt đối của các hệ số, dẫn tới một số hệ số bằng 0 giúp lựa chọn đặc trưng.
    $$L(\beta) = \frac{1}{2n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + \lambda \sum_{j=1}^{p} |\beta_j|$$
    L2 (Ridge) phạt tổng bình phương hệ số và thu nhỏ hệ số về gần 0. $$L(\beta) = \frac{1}{2n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + \lambda \sum_{j=1}^{p} \beta_j^2$$
    Cả hai phương pháp đều thêm penalty vào hàm mất mát để giảm overfitting nhưng sử dụng các dạng penalty khác nhau. L1 phù hợp với bài toán cần lựa chọn đặc trưng, trong khi L2 giúp xử lý đa cộng tuyến và giữ mô hình ổn định.
  • Elastic NetHuber RegressionQuantile Regression và RANSAC: các biến thể tuyến tính chịu ảnh hưởng khác nhau của outlier và phân phối đích.
  • RandomForestAdaBoostGradientBoostingXGBoostLightGBMCatBoost: các mô hình ensemble và boosting mạnh mẽ cho bài toán hồi quy, tận dụng nhiều cây quyết định hoặc boosting để cải thiện độ chính xác.

Để chọn tham số tối ưu, dự án dùng k‑fold cross‑validation: tập huấn luyện được chia thành k phần, mỗi phần lần lượt đóng vai trò tập validation và các kết quả được trung bình hóa. Kỹ thuật này giúp đánh giá mô hình ổn định và không lãng phí dữ liệu. Với các mô hình cần tối ưu tham số (như Ridge, Lasso, Elastic Net), Project sử dụng thư viện Optuna để tự động tìm giá trị alpha tốt nhất. Điều này quan trọng vì tham số regularization quá lớn sẽ làm mô hình underfit, còn quá nhỏ sẽ không ngăn được overfitting.

1.3 Chỉ số đánh giá mô hình (Evaluation Metric)

Bài toán là hồi quy nên Project sử dụng các metric phổ biến:

  • Root Mean Squared Error (RMSE) và Mean Absolute Error (MAE): đo độ lệch trung bình giữa giá trị dự đoán và giá trị thực; RMSE nhấn mạnh các sai số lớn hơn do bình phương.
  • R² (Coefficient of Determination): đo tỷ lệ phương sai của biến mục tiêu được giải thích bởi mô hình. Mô hình tốt sẽ có R² gần 1.
  • Training time: thời gian huấn luyện cũng được ghi nhận để so sánh hiệu quả.

Project hiển thị bảng tổng hợp các metric này và lựa chọn mô hình có R² cao nhất, RMSE/MAE thấp nhất.

2. Ứng dụng mở rộng

2.1 Bảng tóm tắt các tính năng mở rộng

Tính năng Mô tả ngắn
Cấu trúc dự án Dự án được tổ chức theo dạng thư mục với module src/app, src/model và src/data_processing để dễ bảo trì.
Pipeline xử lý dữ liệu Tách riêng khâu xử lý: loại bỏ cột nhiều null, impute theo kiểu dữ liệu, one‑hot encode, chuẩn hóa, tạo đa thức, chọn đặc trưng.
Đa dạng mô hình Bổ sung nhiều thuật toán: Elastic Net, Huber, Quantile, RANSAC, RandomForest, AdaBoost, GradientBoosting, XGBoost, LightGBM, CatBoost.
Hyper‑parameter tuning Dùng k‑fold CV và thư viện Optuna để tự động tìm tham số alpha cho Ridge/Lasso và các tham số khác.
Ứng dụng Streamlit Xây dựng giao diện Web gồm ba trang: khám phá dữ liệu, thử nghiệm mô hình, dự đoán trực tiếp.
Giải thích mô hình bằng SHAP Tính giá trị SHAP để đo đóng góp của từng đặc trưng; SHAP dựa trên lý thuyết trò chơi và cho biết ảnh hưởng dương/âm của mỗi đặc trưng.

2.2 Kết quả thử nghiệm

Khi so sánh một số mô hình trên tập dữ liệu với pipeline cơ bản và pipeline mở rộng, chúng tôi thu được kết quả R² như sau (thử nghiệm với 25 % dữ liệu làm tập test):

Mô hình R² notebook gốc R² Project (đa thức + CV) Nhận xét
Ridge ≈ 0,883 ≈ 0,899 Bổ sung đặc trưng đa thức và tuning alpha giúp tăng R².
Lasso ≈ 0,901 ≈ 0,865 Việc thêm đa thức làm tăng số đặc trưng; Lasso có xu hướng loại bỏ quá nhiều đặc trưng nếu alpha không tối ưu.
LinearRegression kém kém Mô hình tuyến tính không regularization dễ bị overfitting hoặc underfitting khi số biến rất lớn.

Ngoài ra, các mô hình ensemble như RandomForest, GradientBoosting hay XGBoost thường đạt R² cao hơn các mô hình tuyến tính trong thực nghiệm (#0,92) và có thể được lựa chọn làm mô hình cuối cùng, song thời gian huấn luyện dài hơn.

Trong Project, trang Experiments tổng hợp kết quả đo lường của từng mô hình và hiển thị chúng dưới dạng biểu đồ thanh cũng như bảng số liệu. Người dùng dễ dàng thấy rằng các mô hình boosting (CatBoost, Gradient Boost) dẫn đầu với RMSE và MAE thấp nhất, trong khi các mô hình tuyến tính cơ bản (LinearRegression, QuantileRegression) có sai số lớn. Bảng kết quả cũng ghi lại thời gian huấn luyện – CatBoost cần khoảng 3s huấn luyện trong khi Elastic Net gần như tức thời.

123.png

Hình 1. Biểu đồ so sánh các metric

Model Train RMSE Test RMSE Train MAE Test MAE Train R² Test R² Training Time
1 CatBoost 5397.1489 23619.3894 4160.9855 14777.1222 0.9952 0.9204 3.3553
2 Gradient Boost 13873.6175 25336.5561 10106.9404 16285.4314 0.9683 0.9084 1.0064
3 Random Forest 11361.0216 26731.4284 6737.5087 17065.9708 0.9787 0.898 2.7554
4 XGBoost 1031.7889 27278.3379 708.0964 17941.252 0.9998 0.8938 0.1438
5 Light GBM 11963.8713 28655.0806 5485.0188 16856.7479 0.9764 0.8836 0.5023
6 Huber Regressor 28435.6074 30593.3749 14615.4606 17363.2326 0.8668 0.8664 0.0893
7 Ridge 26032.682 32097.8409 16452.3162 20132.3847 0.8884 0.8529 0.0033
8 Lasso 25989.9614 32690.0665 16579.1675 20512.8049 0.8887 0.8475 0.11
9 AdaBoost 28233.661 33258.951 21790.7435 22941.9851 0.8687 0.8421 0.3361
10 Elastic Net 36097.9674 39877.5192 21026.641 21418.4658 0.7854 0.773 0

Bảng 1. Bảng tóm tắt kết quả huấn luyện và kiểm thử

2.3 Giao diện Streamlit

Project cung cấp giao diện Web thân thiện gồm ba trang chính. Các trang này không chỉ trình bày số liệu mà còn cho phép tùy biến pipeline và trực tiếp dự đoán giá nhà.

Data Exploration – khám phá dữ liệu

Người dùng có thể xem thống kê mô tả (mean, std, min, max), vẽ histogram phân phối của biến mục tiêu SalePrice và heatmap tương quan để khám phá mối quan hệ giữa các biến số. Một biểu đồ quan trọng là bar chart số lượng giá trị khuyết theo từng biến, giúp xác định những cột cần loại bỏ hoặc điền giá trị. Ví dụ, các biến như PoolQC hay Alley có số lượng null cao nên được loại bỏ trong pipeline:

575128778_2695675477446782_2525534200133470273_n.png

Hình 2. Thống kê Missing Data
Experiments – chạy thử nghiệm và phân tích kết quả

Trang Experiments là trung tâm huấn luyện mô hình. Người dùng có thể tùy chọn Training Options như số lần thử Optuna, số fold của cross‑validation, loại imputer/transformer/scaler và phương pháp chọn đặc trưng. Giao diện tương tác cho phép thay đổi mỗi tham số thông qua slider và dropdown.
Sau khi cấu hình, người dùng chọn mô hình và chạy thử nghiệm. Kết quả được hiển thị trong các biểu đồ so sánh RMSE/MAE/R² và bảng số liệu (như hình ở phần 3.2). Ngoài các metric, trang này còn cung cấp các công cụ giải thích mô hình:

  • SHAP bar plot: cho thấy giá trị tuyệt đối trung bình của mỗi đặc trưng, từ đó biết được yếu tố nào ảnh hưởng nhiều nhất tới giá bán. Ví dụ, diện tích GrLivArea, chất lượng tổng thể OverallQual và diện tích hầm TotalBsmtSF là ba yếu tố quan trọng nhất đối với mô hình:

574774704_1616306312509421_2336178713602568109_n.png
Hình 3. SHAP bar plot
- SHAP beeswarm plot: biểu diễn phân bố giá trị SHAP cho từng đặc trưng, màu sắc biểu thị giá trị thực (cao – đỏ, thấp – xanh). Biểu đồ này cho thấy, ví dụ, diện tích lớn (GrLivArea cao) làm tăng dự đoán giá (giá trị SHAP dương), trong khi số chỗ đậu xe ít (GarageCars thấp) có tác động âm:

569102637_693291996739745_1092003694623329182_n.png
Hình 4. SHAP beeswarm plot
- SHAP waterfall plot: giải thích một dự đoán cụ thể bằng cách cộng dồn giá trị SHAP của từng đặc trưng vào kỳ vọng của mô hình. Các thanh màu hồng tăng dự đoán, thanh xanh làm giảm dự đoán. Biểu đồ minh họa rằng diện tích nhà ở (GrLivArea) và số chỗ đậu xe ít làm giảm giá trị dự đoán, trong khi điều kiện chung (OverallCond) hay tu sửa năm YearRemodAdd có thể tăng giá:

575725715_2031697694309813_9134896012793719943_n.png
Hình 5. SHAP waterfall plot
- Feature importance theo mô hình cây: ngoài SHAP, Project cung cấp biểu đồ top 20 đặc trưng quan trọng của các mô hình ensemble như RandomForest hoặc GradientBoosting. Chất lượng tổng thể (OverallQual), diện tích GrLivArea và diện tích hầm TotalBsmtSF vượt trội so với các đặc trưng khác:

574983753_1392047062531805_3516674289603122247_n.png
Hình 6. Top 20 đặc trưng quan trọng
Các công cụ trên giúp người dùng không chỉ biết mô hình nào tốt mà còn hiểu lý do mô hình đưa ra dự đoán.

Live Prediction – dự đoán giá nhà trực tiếp

Trang Live Prediction cho phép nhập các thông tin cơ bản về ngôi nhà (chất lượng tổng thể, diện tích sàn, số chỗ đậu xe, diện tích hầm, năm xây dựng) và lựa chọn mô hình. Sau khi nhấn nút “Dự đoán giá”, ứng dụng tính toán giá bán dựa trên pipeline đã huấn luyện và hiển thị giá dự đoán. Ví dụ giao diện dưới đây hiển thị giá dự đoán cuối cùng khoảng 169 643 USD khi người dùng nhập OverallQual = 6, GrLivArea = 1 464 ft², GarageCars = 2, TotalBsmtSF = 991 ft² và YearBuilt = 1973:

566647378_1496672608219489_434064607864370150_n.png
Hình 7. Giao diện trang Live Prediciton

Bên cạnh đó, trang này có mục Model Options để chỉnh sửa siêu tham số của mô hình đang sử dụng (ví dụ HuberRegressor): người dùng có thể điều chỉnh hệ số regularization (alpha), số vòng lặp tối đa (max_iter) hoặc tham số epsilon.

Mã nguồn cải tiến

Dưới đây là một số đoạn code nổi bật minh họa các cải tiến kỹ thuật trong dự án:

Xây dựng pipeline linh hoạt tùy chọn – hàm create_pipe cho phép người dùng chọn imputer, transformer, scaler và phương pháp chọn đặc trưng. Pipeline được kết hợp trong ColumnTransformer và có thể gắn thêm lớp chọn đặc trưng tùy thuộc vào mô hình:

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer, KNNImputer, IterativeImputer
from sklearn.preprocessing import PowerTransformer, RobustScaler, MinMaxScaler, StandardScaler, OneHotEncoder
from sklearn.feature_selection import SelectFromModel, SelectKBest, mutual_info_regression
from sklearn.ensemble import RandomForestRegressor
import xgboost as xgb

def create_pipe(num_cols, num_imp, num_tran, num_scal, cat_cols, feature_name, k):
    # chọn bộ xử lý số theo imputer, transform và scaler
    if num_imp == 'KNN Imputer':
        num_proc = Pipeline([('imp', KNNImputer())])
    elif num_imp == 'Iterative Imputer':
        num_proc = Pipeline([('imp', IterativeImputer())])
    else:
        num_proc = Pipeline([('imp', SimpleImputer(strategy='median'))])

    if num_tran == 'Yeo-Johnson':
        num_proc.steps.append(('trans', PowerTransformer(method='yeo-johnson')))
    if num_scal == 'Robust Scaler':
        num_proc.steps.append(('sc', RobustScaler()))
    elif num_scal == 'MinMaxScaler':
        num_proc.steps.append(('sc', MinMaxScaler()))
    elif num_scal == 'StandardScaler':
        num_proc.steps.append(('sc', StandardScaler()))

    cat_proc = Pipeline([
        ('imp', SimpleImputer(strategy='most_frequent')),
        ('ohe', OneHotEncoder(handle_unknown='ignore', drop='first'))
    ])

    preprocess = ColumnTransformer([
        ('num', num_proc, num_cols),
        ('cat', cat_proc, cat_cols)
    ], verbose_feature_names_out=False)

    pipe = Pipeline([('preprocessor', preprocess)])

    # thêm bước chọn đặc trưng dựa trên mô hình
    if feature_name == 'Random Forest':
        pipe.steps.append(('select', SelectFromModel(RandomForestRegressor(), max_features=k)))
    elif feature_name == 'XGBoost':
        pipe.steps.append(('select', SelectFromModel(xgb.XGBRegressor(), max_features=k)))
    elif feature_name == 'Mutual Info':
        pipe.steps.append(('select', SelectKBest(score_func=mutual_info_regression, k=k)))

    return pipe

Cơ chế cache và pipeline cho trang Live Prediction – hàm _fit_pipeline sử dụng st.cache_resource để đảm bảo pipeline chỉ được fit một lần cho mỗi cấu hình, giúp tiết kiệm thời gian khi dự đoán nhiều lần:

import streamlit as st
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LinearRegression, Ridge, Lasso

@st.cache_resource(show_spinner=False)
def _fit_pipeline(train_df, feature_cols, model_name='Ridge', alpha=1.0):
    # xây dựng preprocessor gồm imputer và scaler cho các biến số
    pre = ColumnTransformer(
        transformers=[(
            'num',
            Pipeline(steps=[
                ('imputer', SimpleImputer(strategy='median')),
                ('scaler', StandardScaler()),
            ]),
            feature_cols
        )],
        remainder='drop'
    )
    # chọn mô hình phù hợp
    if model_name == 'LinearRegression':
        model = LinearRegression()
    elif model_name == 'Ridge':
        model = Ridge(alpha=float(alpha))
    else:
        model = Lasso(alpha=float(alpha), max_iter=10000)
    pipe = Pipeline([('prep', pre), ('model', model)])
    pipe.fit(train_df[feature_cols], train_df['SalePrice'].astype(float))
    return pipe

2.4 Tính năng chi tiết

2.4.1 Dataset

Bộ dữ liệu gốc gồm 1 460 ngôi nhà với 80 biến đầu vào (numeric và categorical) và biến mục tiêu SalePrice. Một số biến liên quan tới điều kiện lô đất, kích thước sàn, chất lượng xây dựng, tiện ích… Dữ liệu có giá trị thiếu ở nhiều cột; vì thế dự án Project loại bỏ các cột có hơn 50 % giá trị khuyết và áp dụng các chiến lược imputation cho phần còn lại.

2.4.2 Feature engineering

Sau khi loại bỏ các cột khuyết nhiều, Project xử lý dữ liệu theo các bước: điền thiếu cho numeric/categorical, mã hóa one‑hot (drop first), chuẩn hóa bằng scaler do người dùng chọn và tạo đặc trưng đa thức. Người dùng có thể chọn phương pháp chọn đặc trưng (mutual information, RandomForest, XGBoost) để giảm số chiều.

2.4.3 Kỹ thuật lựa chọn mô hình & thiết lập tham số

Khi số mô hình và tham số tăng, việc chọn cấu hình tối ưu bằng tay trở nên khó khăn. Project dùng kỹ thuật k‑fold cross‑validation để đánh giá mỗi cấu hình. Bài viết của scikit‑learn giải thích rằng k‑fold CV chia dữ liệu thành k phần, mỗi phần lần lượt làm tập validation và kết quả được trung bình hóa. Dựa trên CV, Project sử dụng Optuna để tự động thử nhiều giá trị alpha cho Ridge và Lasso nhằm tìm ra giá trị tối ưu. Người dùng có thể thay đổi số lần thử (n_trials) và số fold (cv_folds) trong giao diện Experiments.

2.4.4 Kỹ thuật Pipeline kết hợp với Cross‑Validation

Tất cả các bước xử lý dữ liệu và mô hình được gói trong sklearn.pipeline.Pipeline hoặc sklearn.compose.ColumnTransformer. Khi sử dụng pipeline, chúng ta có thể kết hợp với hàm cross_val_score hoặc KFold để đảm bảo rằng imputer, scaler, encoder đều được fit trên tập train và áp dụng lên tập validation/test một cách chính xác. Scikit‑learn khuyến nghị áp dụng toàn bộ chuỗi biến đổi trong pipeline để tránh rò rỉ dữ liệu.

Ví dụ, một pipeline đơn giản cho Ridge:

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge

# Chỉ sử dụng 5 biến quan trọng
feature_cols = ["OverallQual", "GrLivArea", "GarageCars", "TotalBsmtSF", "YearBuilt"]
num_cols = feature_cols
cat_cols = []  # nếu có biến phân loại, liệt kê tại đây

# Định nghĩa preprocessor
pre = ColumnTransformer([
    ("num", Pipeline(steps=[
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler()),
    ]), num_cols),
    ("cat", Pipeline(steps=[
        ("imputer", SimpleImputer(strategy="most_frequent")),
        ("encoder", OneHotEncoder(handle_unknown="ignore")),
    ]), cat_cols)
])

# Tạo pipeline và huấn luyện
model = Ridge(alpha=1.0)
pipe = Pipeline([("prep", pre), ("model", model)])
pipe.fit(train_df[feature_cols], train_df["SalePrice"])

2.4.5 Đánh giá hiệu năng mô hình

Sau khi huấn luyện, Project xuất ra bảng so sánh gồm RMSE, MAE, R² và thời gian. Bảng này giúp người dùng lựa chọn mô hình phù hợp với yêu cầu (độ chính xác cao hay thời gian huấn luyện ngắn). Ngoài ra, việc hiển thị biểu đồ bar so sánh ba metric cũng giúp trực quan hơn. Mô hình có R² cao nhất và RMSE thấp nhất thường được chọn; tuy nhiên, trong một số trường hợp, sự chênh lệch nhỏ không đáng kể nên người dùng có thể cân nhắc mô hình đơn giản hơn.

2.4.6 Kỹ thuật Exploratory Data Analysis (EDA) kết hợp với Streamlit

Trước khi xây dựng mô hình, việc khám phá dữ liệu giúp hiểu rõ phân phối, mối tương quan và outlier. Trang Data Exploration trong Project tự động:

  1. Hiển thị bảng mô tả thống kê cho toàn bộ biến số (mean, std, min, max, quartiles).
  2. Vẽ biểu đồ phân phối của SalePrice với đường mean để quan sát mức giá phổ biến.
  3. Vẽ bar chart số lượng giá trị khuyết theo từng biến – những biến có nhiều giá trị khuyết có thể bị loại.
  4. Vẽ heatmap tương quan giữa các biến số; màu sắc giúp nhận biết mối tương quan mạnh – yếu.
  5. Vẽ histogram cho tất cả biến số để quan sát phân phối của mỗi biến.

Người dùng không cần viết mã, chỉ cần truy cập trang để xem và phân tích.

3. Kết luận

Dự án đã biến một notebook đơn giản thành một hệ thống dự đoán giá nhà hoàn chỉnh. Bằng cách tổ chức lại cấu trúc dự án, xây dựng pipeline xử lý dữ liệu, thử nghiệm nhiều mô hình và áp dụng kỹ thuật cross‑validation cùng Optuna để tối ưu siêu tham số, Project đạt độ chính xác cao hơn và ổn định hơn so với phiên bản gốc. Việc tích hợp giao diện Streamlit giúp người dùng cuối – kể cả không lập trình – có thể khám phá dữ liệu, chạy thử nghiệm và dự đoán giá nhà chỉ bằng vài cú click. Đồng thời, việc sử dụng SHAP mang lại khả năng giải thích mô hình, tăng cường tính minh bạch trong dự báo. Với những cải tiến này, Project là bước tiến quan trọng trong hành trình xây dựng ứng dụng dự đoán giá nhà dễ sử dụng và đáng tin cậy.

4. Tài liệu tham khảo

[1] Project: House Price Prediction (Advanced Regression Techniques) - MSc. Nguyen Quoc Thai, STA Ho Quang Hien
[2] Bộ dữ liệu House Prices – Advanced Regression Techniques (Kaggle), https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques