Chuỗi thời gian 4 — Đặc trưng & mô hình ML

13 thg 7, 2026 6 lượt xem
#machine-learning
#ai
#feature-engineering
#forecasting
#time-series

Chuỗi thời gian 4 — Đặc trưng & mô hình ML

Các mô hình cổ điển ở Chuỗi thời gian 3 — ETS & ARIMA rất mạnh cho một chuỗi đơn biến. Nhưng ở ngân hàng, bạn hiếm khi có đúng một chuỗi. Bạn có khối lượng giao dịch của hàng nghìn chi nhánh × loại giao dịch, mỗi cái chịu ảnh hưởng của ngày lễ, khuyến mãi, lãi suất, thời tiết. ARIMA phải fit riêng từng chuỗi, không tận dụng được thông tin chung, và rất khó nhét biến ngoại sinh phi tuyến vào.

Đây là địa hạt của cách tiếp cận machine learning: biến bài toán dự báo thành một bài hồi quy có giám sát (supervised regression) thông thường, rồi ném vào gradient boosting. Bài này tập trung vào phần khó nhất và quyết định nhất của hướng ML — feature engineering cho chuỗi thời gian — cùng những cái bẫy rò rỉ dữ liệu tương lai chết người, và cách huấn luyện một global model cho nhiều chuỗi. Nền tảng feature engineering tổng quát xem AI 2 — Dữ liệu & đặc trưng; nền tảng cây/ensemble xem AI 3 — Classic ML.

1. Chuyển TS thành supervised regression

Ý tưởng cốt lõi: một mô hình ML nhận vào một vector đặc trưng X và dự đoán một mục tiêu y. Với dự báo, ta chọn y là giá trị cần dự báo tại thời điểm t (ví dụ khối lượng giao dịch ngày mai), còn X là mọi thứ ta biết được tại thời điểm t — mà tất cả đều phải xuất phát từ quá khứ (t-1, t-2, ...) hoặc từ thông tin lịch/ngoại sinh đã biết trước.

Nói cách khác, ta trượt một cửa sổ dọc theo chuỗi, mỗi bước sinh ra một hàng (X, y):

Điều này nghe đơn giản nhưng chứa toàn bộ độ khó: mọi đặc trưng phải được tính "as-of" — chỉ dùng thông tin có thật tại thời điểm dự báo. Sai một ly ở đây là rò rỉ tương lai (mục 4), và model của bạn sẽ đẹp lung linh trên validation rồi sập thảm hại khi lên production.

2. Feature engineering cho chuỗi thời gian

Chất lượng dự báo ML gần như hoàn toàn nằm ở đặc trưng, không phải ở thuật toán. Dưới đây là các nhóm đặc trưng theo thứ tự quan trọng.

2.1 Lag features

Lag là giá trị của chính chuỗi ở các thời điểm quá khứ — đặc trưng mạnh nhất trong hầu hết bài toán.

  • Lag ngắn t-1, t-2, t-3: bắt tự tương quan gần (momentum, quán tính).
  • Lag mùa vụ t-7 (tuần trước cùng thứ), t-365 (năm trước cùng ngày): cực quan trọng cho dữ liệu ngân hàng vì hành vi giao dịch lặp theo thứ trong tuần và theo mùa (Tết, cuối quý).
  • Chọn lag nào? Nhìn ACF/PACF (xem TS 2 — EDA & phân rã): các đỉnh ACF ở lag 7, 14, 365 gợi ý đúng lag cần tạo.
# Minh hoạ (KHÔNG chạy được ở đây) — pandas
import pandas as pd

df = df.sort_values(["branch", "date"])
g = df.groupby("branch")["volume"]
for lag in [1, 2, 3, 7, 14, 365]:
    df[f"lag_{lag}"] = g.shift(lag)   # shift đẩy giá trị quá khứ về hàng hiện tại

groupby("branch").shift() là bắt buộc khi có nhiều chuỗi — nếu không, lag sẽ "mượn" giá trị của chi nhánh khác, một dạng rò rỉ tinh vi.

2.2 Rolling / window statistics

Thống kê trượt trên cửa sổ quá khứ tóm tắt mức và độ biến động gần đây:

  • rolling_mean_7, rolling_mean_28: mức nền gần đây, làm mượt nhiễu.
  • rolling_std_7: độ biến động (dùng cho cả phát hiện bất thường, xem TS 7 nếu cần mở rộng).
  • rolling_min/max_28: biên gần đây.

Quy tắc vàng: shift trước, rolling sau để cửa sổ không bao gồm chính hàng hiện tại (nếu không, y bị rò rỉ vào X).

# Minh hoạ (KHÔNG chạy được) — luôn shift(1) rồi mới rolling
s = df.groupby("branch")["volume"].shift(1)
df["roll_mean_7"]  = s.rolling(7).mean()
df["roll_std_7"]   = s.rolling(7).std()
df["roll_max_28"]  = s.rolling(28).max()

2.3 Expanding window

Cửa sổ mở rộng (expanding) tính thống kê tích luỹ từ đầu chuỗi đến t-1: expanding_mean, expanding_std. Hữu ích để nắm mức trung bình dài hạn của một chi nhánh mới ít dữ liệu, hoặc làm mẫu số chuẩn hoá. Cũng phải shift(1) trước để không dính giá trị hiện tại.

2.4 Calendar features

Đặc trưng lịch là thông tin đã biết chắc chắn cho tương lai (ta luôn biết ngày mai là thứ mấy), nên dùng thoải mái mà không sợ rò rỉ:

NhómVí dụ đặc trưng
Trong ngàyhour (nếu dữ liệu theo giờ)
Trong tuầnday_of_week (0–6), is_weekend (T7/CN)
Trong thángday_of_month, is_month_start, is_month_end
Trong nămmonth, quarter, week_of_year, day_of_year
Ngày lễ VNis_holiday, is_tet, days_to_tet, days_after_tet

Với ngân hàng Việt Nam, lịch âm và Tết Nguyên đán là biến số lớn nhất: khối lượng rút tiền tăng vọt trước Tết rồi rơi tự do trong Tết. Nên dùng thư viện lịch lễ Việt (ví dụ holidays có locale VN, hoặc bảng lễ tự dựng gồm cả lễ theo dương lịch lẫn Tết âm lịch), và tạo cả days_to_tet/days_after_tet vì hiệu ứng lan ra trước/sau vài ngày chứ không chỉ đúng ngày lễ.

Lưu ý: các biến chu kỳ như day_of_week, month với mô hình cây (LightGBM) có thể để nguyên dạng số nguyên — cây tự chia ngưỡng. Với mô hình tuyến tính thì nên one-hot hoặc mã hoá sin/cos (xem Fourier bên dưới) để tránh áp đặt thứ tự tuyến tính giả tạo.

2.5 Fourier terms cho mùa vụ

Mùa vụ dài (chu kỳ 365 ngày) không thể one-hot 365 mức, và lag 365 đòi nhiều năm dữ liệu. Giải pháp gọn là Fourier terms: mã hoá mùa vụ bằng vài cặp sin/cos:

# Minh hoạ (KHÔNG chạy được) — Fourier cho mùa vụ năm (period=365.25)
import numpy as np
t = df["day_of_year"]
period = 365.25
for k in range(1, 4):                     # 3 harmonic đầu thường đủ
    df[f"sin_{k}"] = np.sin(2*np.pi*k*t/period)
    df[f"cos_{k}"] = np.cos(2*np.pi*k*t/period)

Vài harmonic (K=3–5) mô tả được đường mùa vụ trơn tru bằng ít cột, thay vì hàng trăm dummy. Đây cũng là cách Prophet và nhiều mô hình hồi quy TS xử lý mùa vụ.

2.6 Biến ngoại sinh (exogenous)

Đây là lợi thế lớn nhất của ML so với ARIMA: nhét thêm biến ngoài chuỗi.

  • Sự kiện/khuyến mãi: is_promo, promo_intensity, ngày phát lương (25 hằng tháng → giao dịch tăng).
  • Kinh tế: lãi suất huy động, tỷ giá USD/VND.
  • Thời tiết: mưa bão giảm giao dịch tại quầy.
  • Lễ đối tác: ngày mua sắm lớn (Black Friday, 11/11, 12/12).

Cảnh báo: biến ngoại sinh cho tương lai phải có sẵn tại thời điểm dự báo. Khuyến mãi và lịch lễ thì biết trước; nhưng "doanh số ngày mai" thì không — nếu cần, phải dùng giá trị dự báo của biến đó (và gánh sai số kép), hoặc chỉ dùng lag của nó.

3. Ví dụ: tạo feature bằng SQL window function

Với dữ liệu đang nằm sẵn trong kho, ta có thể sinh lag và rolling feature ngay bằng SQL window function trước khi kéo về Python. Ví dụ dưới tổng hợp giao dịch theo ngày rồi tạo lag 1 ngày, lag 7 ngày và trung bình trượt 7 ngày (đã dịch để không dính chính ngày hiện tại):

-- ▶ Chạy được
WITH daily AS (
  SELECT date_trunc('day', created_at) AS d,
         SUM(amount) AS volume
  FROM transactions
  GROUP BY 1
)
SELECT d,
       volume,
       LAG(volume, 1) OVER (ORDER BY d) AS lag_1,
       LAG(volume, 7) OVER (ORDER BY d) AS lag_7,
       AVG(volume) OVER (
         ORDER BY d ROWS BETWEEN 7 PRECEDING AND 1 PRECEDING
       ) AS roll_mean_7
FROM daily
ORDER BY d;

LAG(volume, 7) lấy giá trị 7 ngày trước; khung ROWS BETWEEN 7 PRECEDING AND 1 PRECEDING cho trung bình 7 ngày liền trước, không gồm ngày hiện tại — đúng nguyên tắc as-of. Đây chính là bảng đặc trưng thô để đưa vào mô hình.

4. Cảnh báo: RÒ RỈ DỮ LIỆU TƯƠNG LAI

Rò rỉ (leakage) là lỗi giết chết mọi dự án dự báo ML. Nó khiến điểm validation đẹp giả tạo còn production thì thảm hoạ. Các dạng phổ biến:

  1. Rolling/expanding không shift: rolling(7).mean() tại thời điểm t bao gồm chính value[t] = mục tiêu → rò rỉ trực tiếp. Luôn shift(1) trước.
  2. Scaling/impute trên toàn bộ dữ liệu: tính mean/std để chuẩn hoá, hoặc điền missing bằng trung bình toàn chuỗi → dùng thông tin tương lai. Phải fit trên train, transform sang test; trong backtest phải tính lại theo từng fold.
  3. Target encoding / thống kê nhóm tính trên cả tập → rò rỉ. Phải tính as-of (chỉ từ quá khứ).
  4. Shuffle split ngẫu nhiên: KHÔNG dùng train_test_split ngẫu nhiên cho TS. Phải split theo thời gian (train = quá khứ, test = tương lai) và dùng time-series cross-validation (expanding/sliding window). Chi tiết đánh giá xem TS 6 — Đánh giá & backtest.
  5. Biến ngoại sinh cùng kỳ chưa biết trước: đưa promo[t] khi lúc dự báo chưa biết → chỉ dùng nếu thật sự biết trước.

Nguyên tắc bao trùm: tại mỗi hàng, hãy tự hỏi "thông tin này tôi có thật vào thời điểm t không?". Nếu không, bỏ.

5. Mô hình ML cho dự báo

5.1 Hồi quy tuyến tính có lag

Linear/Ridge regression trên lag + Fourier + calendar là baseline ML rẻ và mạnh, dễ diễn giải qua hệ số. Về bản chất, hồi quy trên các lag chính là mô hình AR; thêm Fourier ≈ hồi quy điều hoà. Nền tảng xem Thống kê 6 — Tương quan & hồi quy.

5.2 Cây & gradient boosting (XGBoost / LightGBM)

Gradient boosting trên cây (LightGBM, XGBoost) là vũ khí chủ lực cho TS dạng bảng và thắng phần lớn thi Kaggle về dự báo (M5). Ưu điểm:

  • Bắt phi tuyếntương tác giữa lag × calendar × exogenous tự động.
  • Không cần scaling, chịu được nhiều đặc trưng, xử lý missing tốt (LightGBM có nhánh missing).
  • Nhanh, có feature importance.

Hạn chế quan trọng: cây không ngoại suy (extrapolate). Với chuỗi có xu hướng tăng mạnh, dự báo sẽ bị "trần" ở mức đã thấy trong train. Cách xử lý: detrend/difference trước (dự báo value[t] - value[t-1] hoặc log-return), hoặc thêm feature trend tường minh, hoặc dùng mô hình lai.

# Minh hoạ (KHÔNG chạy được) — LightGBM cho dự báo bảng
import lightgbm as lgb

features = ["lag_1","lag_7","lag_365","roll_mean_7","roll_std_7",
            "day_of_week","month","is_weekend","is_holiday",
            "days_to_tet","sin_1","cos_1","is_promo"]

train = df[df["date"] <  "2026-01-01"]
valid = df[df["date"] >= "2026-01-01"]      # split theo THỜI GIAN

model = lgb.LGBMRegressor(
    n_estimators=2000, learning_rate=0.03,
    num_leaves=63, subsample=0.8, colsample_bytree=0.8,
    objective="regression_l1")             # L1 ~ MAE, robust với outlier
model.fit(train[features], train["volume"],
          eval_set=[(valid[features], valid["volume"])],
          callbacks=[lgb.early_stopping(100)])

5.3 Global model (một mô hình cho nhiều chuỗi)

Thay vì fit riêng mỗi chi nhánh, huấn luyện một mô hình global trên tất cả chuỗi gộp lại, thêm cột định danh (branch_id, đặc trưng tĩnh như vùng, quy mô chi nhánh). Lợi ích:

  • Chia sẻ thông tin: chuỗi ít dữ liệu (chi nhánh mới) học từ chuỗi giàu dữ liệu.
  • Vận hành đơn giản: 1 model thay vì hàng nghìn.
  • Là kiến trúc thắng M5; cũng là ý tưởng nền của các mô hình DL như DeepAR/N-BEATS (DL 5 — RNN/chuỗi, chi tiết ở TS 5 — Nâng cao).

Với global model, branch_id nên để dạng category (LightGBM hỗ trợ native), và các thống kê rolling/lag phải tính trong từng nhóm (groupby).

6. Multi-step forecasting: recursive vs direct

Dự báo h bước tới có hai chiến lược chính:

Chiến lượcCách làmƯuNhược
RecursiveDự báo t+1, đưa kết quả làm lag cho t+2, lặp lạiChỉ cần 1 model; dùng đủ lag ngắnTích luỹ sai số qua từng bước
DirectHuấn luyện h model riêng, mỗi model dự báo trực tiếp horizon kKhông tích luỹ sai sốTốn h model; lag gần bị mất ở horizon xa
Direct-recursive lai / MIMOKết hợp, hoặc 1 model xuất vector h bướcCân bằngPhức tạp hơn

Thực tế ngân hàng: dự báo 7–30 ngày. Nếu horizon ngắn và ổn định, recursive gọn. Nếu cần dự báo xa (dự báo dòng tiền 3 tháng) và sai số recursive phình to, chuyển sang direct cho các horizon dài. Đánh giá cả hai bằng backtest (TS 6).

7. ML vs cổ điển: khi nào chọn cái nào?

Tiêu chíNghiêng về cổ điển (ARIMA/ETS)Nghiêng về ML (LightGBM)
Số chuỗiÍt (1–vài chục)Nhiều (hàng trăm/nghìn)
Biến ngoại sinhÍt / khôngNhiều, phi tuyến, tương tác
Độ dài chuỗiNgắn/trung bìnhCần đủ để học lag
Phi tuyến / ngưỡngYếuMạnh
Diễn giảiRất tốtTrung bình (feature importance)
Khoảng tin cậyCó sẵn, chuẩnCần quantile regression / bootstrap
Chi phí vận hànhFit từng chuỗi1 global model

Kết luận thực dụng: luôn giữ baseline cổ điển và seasonal-naive để so sánh. ML chỉ thực sự thắng khi có nhiều chuỗi + nhiều biến ngoại sinh + quan hệ phi tuyến. Với một chuỗi đơn biến sạch, ARIMA/ETS thường vẫn ngang hoặc hơn, mà rẻ hơn nhiều.

Use case thực tế

Bài toán: dự báo khối lượng giao dịch tiền mặt theo ngày cho 620 chi nhánh để tối ưu tiếp quỹ ATM/quầy, horizon 14 ngày.

Trước đây team fit SARIMA riêng cho từng chi nhánh: 620 model, chạy 40 phút mỗi đêm, MAPE trung bình 18%, và các chi nhánh mới (<1 năm dữ liệu) dự báo rất tệ.

Chuyển sang global LightGBM:

  1. Gộp dữ liệu: 620 chi nhánh × ~1.100 ngày ≈ 680k hàng. Target = tổng amount mỗi ngày mỗi chi nhánh.
  2. Feature (tính theo nhóm chi nhánh, shift(1) an toàn): lag 1,2,3,7,14,365; roll_mean_7/28, roll_std_7; calendar day_of_week, month, quarter, is_weekend; lễ VN is_holiday, days_to_tet, days_after_tet; Fourier 3 harmonic cho mùa năm; exog is_payday (ngày 25), is_promo; đặc trưng tĩnh region, branch_size, branch_id (category).
  3. Chống rò rỉ: split theo thời gian, backtest expanding-window 3 fold; không scaling toàn cục.
  4. Model: 1 LightGBM, objective L1, early stopping; recursive cho 14 bước, có thêm 1 model direct riêng cho horizon 14 để chốt biên xa.

Kết quả (minh hoạ trên dự án tương tự): MAPE trung bình xuống ~12%, thời gian train còn ~6 phút (1 model), chi nhánh mới cải thiện rõ nhờ học chung. days_to_tet, lag_7is_payday là các feature quan trọng nhất theo feature importance — đúng trực giác nghiệp vụ. So sánh AML/giám sát giao dịch bất thường có thể tái dùng chính bảng feature này (xem AML 3 nếu mở rộng).

Ghi nhớ

  • Dự báo ML = biến TS thành hồi quy có giám sát: mỗi hàng (X từ quá khứ, y tương lai), mọi feature tính as-of.
  • Lag (nhất là t-1, t-7, t-365) là đặc trưng mạnh nhất; rolling/expanding phải shift(1) trước khi tính để không rò rỉ target.
  • Calendar features (thứ, tháng, quý, cuối tuần, lễ VN, days_to_tet) và Fourier terms mã hoá mùa vụ gọn, dùng được cho tương lai vì biết trước.
  • Rò rỉ tương lai là lỗi chí mạng: không rolling chưa shift, không scaling/target-encoding toàn cục, split theo thời gian, backtest time-series CV.
  • LightGBM/XGBoost thắng phần lớn TS dạng bảng nhờ phi tuyến + tương tác + exogenous; nhưng cây không ngoại suy → detrend/difference với chuỗi có xu hướng.
  • Global model (1 model nhiều chuỗi + branch_id) chia sẻ thông tin, vận hành đơn giản, cứu chuỗi ít dữ liệu.
  • Multi-step: recursive (1 model, tích luỹ sai số) vs direct (h model, chính xác horizon xa) — chọn theo horizon.
  • ML thắng khi nhiều chuỗi + nhiều biến ngoại sinh + phi tuyến; chuỗi đơn biến sạch thì cổ điển vẫn ngang/hơn — luôn giữ baseline để so.

Bài viết liên quan

Phân biệt AI/ML/DL, các kiểu học máy, quy trình ML end-to-end và thuật ngữ nền tảng.

13 thg 7, 2026 14

Chất lượng dữ liệu, xử lý thiếu/ngoại lai, mã hoá, chuẩn hoá, tạo đặc trưng và tránh data leakage.

13 thg 7, 2026 13

Mô hình ngôn ngữ lớn hoạt động ra sao, token, context window, tham số sinh, và kỹ thuật viết prompt.

13 thg 7, 2026 13

Retrieval-Augmented Generation, vector database, tool calling, agent và MCP để xây ứng dụng LLM thực tế.

13 thg 7, 2026 11