<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Paozakai</title>
    <description>The latest articles on Forem by Paozakai (@paozakai_8e9321738e57fa65).</description>
    <link>https://forem.com/paozakai_8e9321738e57fa65</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3621954%2F6beb7de0-1c06-4a38-b8ba-a5c843f43a90.jpg</url>
      <title>Forem: Paozakai</title>
      <link>https://forem.com/paozakai_8e9321738e57fa65</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/paozakai_8e9321738e57fa65"/>
    <language>en</language>
    <item>
      <title>Stock Price Prediction With Machine Learning Model</title>
      <dc:creator>Paozakai</dc:creator>
      <pubDate>Fri, 21 Nov 2025 02:15:31 +0000</pubDate>
      <link>https://forem.com/paozakai_8e9321738e57fa65/pseudo-code-flowchart-data-preprocessing-sequential-model-validation-2ogp</link>
      <guid>https://forem.com/paozakai_8e9321738e57fa65/pseudo-code-flowchart-data-preprocessing-sequential-model-validation-2ogp</guid>
      <description>&lt;h1&gt;
  
  
  Colab-ready: improved stable pipeline with Optuna + LGB + XGB + stacking
&lt;/h1&gt;

&lt;h1&gt;
  
  
  1) ก่อนรัน ให้อัปโหลดไฟล์ผ่าน Colab UI: /content/train.csv, /content/test.csv, /content/sample_submission.csv
&lt;/h1&gt;

&lt;h1&gt;
  
  
  2) Copy-paste ทั้งหมดนี้ใน cell เดียวแล้วรัน
&lt;/h1&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ติดตั้งไลบรารี (ครั้งแรก)
&lt;/h1&gt;

&lt;p&gt;!pip install --quiet lightgbm xgboost scikit-learn pandas numpy matplotlib optuna&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;p&gt;import warnings&lt;br&gt;
warnings.filterwarnings('ignore')&lt;/p&gt;

&lt;p&gt;import os&lt;br&gt;
import math&lt;br&gt;
import time&lt;br&gt;
import numpy as np&lt;br&gt;
import pandas as pd&lt;br&gt;
from sklearn.model_selection import TimeSeriesSplit&lt;br&gt;
from sklearn.metrics import mean_squared_error&lt;br&gt;
from sklearn.linear_model import Ridge&lt;br&gt;
import lightgbm as lgb&lt;br&gt;
import xgboost as xgb&lt;br&gt;
import optuna&lt;br&gt;
from google.colab import files&lt;br&gt;
from sklearn.preprocessing import StandardScaler&lt;/p&gt;

&lt;p&gt;SEED = 42&lt;br&gt;
np.random.seed(SEED)&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Paths (Upload files via Colab file upload UI with these exact names)
&lt;/h1&gt;

&lt;p&gt;TRAIN_PATH = "/content/train (1).csv"&lt;br&gt;
TEST_PATH = "/content/test (1).csv"&lt;br&gt;
SAMPLE_SUB_PATH = "/content/sample_submission (1).csv"&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Load data
&lt;/h1&gt;

&lt;p&gt;for p in [TRAIN_PATH, TEST_PATH, SAMPLE_SUB_PATH]:&lt;br&gt;
    if not os.path.exists(p):&lt;br&gt;
        raise FileNotFoundError(f"ไม่พบไฟล์: {p}. กรุณาอัปโหลดไฟล์ไปที่ Colab แล้วตั้งชื่อให้ตรง (train.csv, test.csv, sample_submission.csv)")&lt;/p&gt;

&lt;p&gt;train = pd.read_csv(TRAIN_PATH)&lt;br&gt;
test = pd.read_csv(TEST_PATH)&lt;br&gt;
sample_sub = pd.read_csv(SAMPLE_SUB_PATH)&lt;/p&gt;

&lt;p&gt;print("train shape:", train.shape)&lt;br&gt;
print("test shape:", test.shape)&lt;br&gt;
print("sample_sub shape:", sample_sub.shape)&lt;br&gt;
print("ตัวอย่าง train:")&lt;br&gt;
print(train.head())&lt;/p&gt;

&lt;h1&gt;
  
  
  Ensure sorted by id
&lt;/h1&gt;

&lt;p&gt;train = train.sort_values('id').reset_index(drop=True)&lt;br&gt;
test = test.sort_values('id').reset_index(drop=True)&lt;/p&gt;

&lt;h1&gt;
  
  
  Convert price to float
&lt;/h1&gt;

&lt;p&gt;train['price'] = train['price'].astype(float)&lt;/p&gt;

&lt;h1&gt;
  
  
  Ensure test has price column (NaN)
&lt;/h1&gt;

&lt;p&gt;if 'price' not in test.columns:&lt;br&gt;
    test['price'] = np.nan&lt;br&gt;
else:&lt;br&gt;
    test['price'] = test['price'].astype(float)&lt;/p&gt;

&lt;h1&gt;
  
  
  Concat to build features that correctly use history
&lt;/h1&gt;

&lt;p&gt;all_df = pd.concat([train[['id','price']], test[['id','price']]], ignore_index=True)&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Feature engineering function (expanded but robust)
&lt;/h1&gt;

&lt;p&gt;def create_features_full(df):&lt;br&gt;
    df = df.copy()&lt;br&gt;
    # lags (short, medium, seasonal)&lt;br&gt;
    lags = [1,2,3,5,7,14,21,28,30,60]&lt;br&gt;
    for l in lags:&lt;br&gt;
        df[f'lag_{l}'] = df['price'].shift(l)&lt;br&gt;
    # rolling windows&lt;br&gt;
    windows = [3,5,7,14,21,30,60]&lt;br&gt;
    for w in windows:&lt;br&gt;
        df[f'roll_mean_{w}'] = df['price'].shift(1).rolling(window=w, min_periods=1).mean()&lt;br&gt;
        df[f'roll_std_{w}'] = df['price'].shift(1).rolling(window=w, min_periods=1).std().fillna(0)&lt;br&gt;
        df[f'roll_med_{w}'] = df['price'].shift(1).rolling(window=w, min_periods=1).median().fillna(method='bfill').fillna(0)&lt;br&gt;
    # ewm&lt;br&gt;
    for span in [3,7,14,30]:&lt;br&gt;
        df[f'ewm_{span}'] = df['price'].shift(1).ewm(span=span, adjust=False).mean()&lt;br&gt;
    # expanding&lt;br&gt;
    df['expanding_mean'] = df['price'].shift(1).expanding().mean()&lt;br&gt;
    # diffs&lt;br&gt;
    df['diff_1'] = df['price'] - df['price'].shift(1)&lt;br&gt;
    df['diff_2'] = df['price'] - df['price'].shift(2)&lt;br&gt;
    df['pct_change_1'] = df['price'].pct_change(1)&lt;br&gt;
    df['pct_change_3'] = df['price'].pct_change(3)&lt;br&gt;
    # season proxies&lt;br&gt;
    df['id_mod7'] = df['id'] % 7&lt;br&gt;
    df['id_mod30'] = df['id'] % 30&lt;br&gt;
    df['day_of_year'] = df['id'] % 365&lt;br&gt;
    # ratio features&lt;br&gt;
    df['lag1_div_lag7'] = df['price'].shift(1) / df['price'].shift(7).replace(0, np.nan)&lt;br&gt;
    # sanitize&lt;br&gt;
    df = df.replace([np.inf, -np.inf], np.nan)&lt;br&gt;
    return df&lt;/p&gt;

&lt;p&gt;all_feat = create_features_full(all_df)&lt;/p&gt;

&lt;p&gt;train_feat = all_feat.iloc[:len(train)].reset_index(drop=True)&lt;br&gt;
test_feat = all_feat.iloc[len(train):].reset_index(drop=True)&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Prepare training rows: dropna on engineered features
&lt;/h1&gt;

&lt;p&gt;TARGET = 'price'&lt;br&gt;
train_model = train_feat.dropna().reset_index(drop=True)&lt;br&gt;
train_model['target'] = np.log1p(train_model['price'].astype(float))  # log1p target&lt;/p&gt;

&lt;p&gt;FEATURES = [c for c in train_model.columns if c not in ['price','target']]&lt;br&gt;
print("จำนวน features:", len(FEATURES))&lt;/p&gt;

&lt;h1&gt;
  
  
  Validation split: use last VAL_SIZE rows as validation (time-series holdout)
&lt;/h1&gt;

&lt;p&gt;VAL_SIZE = min(300, int(len(train_model) * 0.1))&lt;br&gt;
train_X = train_model[FEATURES].iloc[:-VAL_SIZE].reset_index(drop=True)&lt;br&gt;
train_y = train_model['target'].iloc[:-VAL_SIZE].reset_index(drop=True)&lt;br&gt;
val_X = train_model[FEATURES].iloc[-VAL_SIZE:].reset_index(drop=True)&lt;br&gt;
val_y = train_model['target'].iloc[-VAL_SIZE:].reset_index(drop=True)&lt;/p&gt;

&lt;h1&gt;
  
  
  Prepare test features (some NaNs possible; fill conservatively)
&lt;/h1&gt;

&lt;p&gt;test_X = test_feat[FEATURES].copy().reset_index(drop=True)&lt;br&gt;
test_X = test_X.fillna(method='ffill').fillna(method='bfill').fillna(0)&lt;/p&gt;

&lt;p&gt;print("train_X shape:", train_X.shape, "val_X shape:", val_X.shape, "test_X shape:", test_X.shape)&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Optuna objective: robust (catch exceptions and return inf when fail)
&lt;/h1&gt;

&lt;p&gt;def lgb_objective(trial):&lt;br&gt;
    try:&lt;br&gt;
        param = {&lt;br&gt;
            'objective': 'regression',&lt;br&gt;
            'metric': 'rmse',&lt;br&gt;
            'boosting_type': 'gbdt',&lt;br&gt;
            'verbosity': -1,&lt;br&gt;
            'seed': SEED,&lt;br&gt;
            'learning_rate': trial.suggest_float('learning_rate', 0.005, 0.05, log=True),&lt;br&gt;
            'num_leaves': trial.suggest_int('num_leaves', 20, 128),&lt;br&gt;
            'min_data_in_leaf': trial.suggest_int('min_data_in_leaf', 10, 200),&lt;br&gt;
            'feature_fraction': trial.suggest_float('feature_fraction', 0.5, 1.0),&lt;br&gt;
            'bagging_fraction': trial.suggest_float('bagging_fraction', 0.5, 1.0),&lt;br&gt;
            'bagging_freq': trial.suggest_int('bagging_freq', 1, 7),&lt;br&gt;
            'lambda_l1': trial.suggest_float('lambda_l1', 0.0, 5.0),&lt;br&gt;
            'lambda_l2': trial.suggest_float('lambda_l2', 0.0, 5.0)&lt;br&gt;
        }&lt;br&gt;
        # small time-series CV inside objective to estimate performance&lt;br&gt;
        cv = TimeSeriesSplit(n_splits=3)&lt;br&gt;
        rmses = []&lt;br&gt;
        for tr_idx, va_idx in cv.split(train_X):&lt;br&gt;
            dtr = lgb.Dataset(train_X.iloc[tr_idx], label=train_y.iloc[tr_idx])&lt;br&gt;
            dva = lgb.Dataset(train_X.iloc[va_idx], label=train_y.iloc[va_idx], reference=dtr)&lt;br&gt;
            bst = lgb.train(param, dtr, num_boost_round=1500, valid_sets=[dva],&lt;br&gt;
                            callbacks=[lgb.early_stopping(50, verbose=False)])&lt;br&gt;
            pred = bst.predict(train_X.iloc[va_idx], num_iteration=bst.best_iteration)&lt;br&gt;
            rmses.append(np.sqrt(mean_squared_error(train_y.iloc[va_idx], pred)))&lt;br&gt;
        return float(np.mean(rmses))&lt;br&gt;
    except Exception as e:&lt;br&gt;
        # print to debug but don't fail the trial&lt;br&gt;
        print("Trial failed with exception:", e)&lt;br&gt;
        return float("inf")&lt;/p&gt;

&lt;h1&gt;
  
  
  Run optuna study (small number trials by default; increase if you want)
&lt;/h1&gt;

&lt;p&gt;study = optuna.create_study(direction='minimize', sampler=optuna.samplers.TPESampler(seed=SEED))&lt;br&gt;
try:&lt;br&gt;
    study.optimize(lgb_objective, n_trials=20, timeout=300)  # n_trials or timeout (seconds)&lt;br&gt;
except Exception as e:&lt;br&gt;
    print("Optuna run exception:", e)&lt;/p&gt;

&lt;h1&gt;
  
  
  Safely extract best params
&lt;/h1&gt;

&lt;p&gt;best_params = {}&lt;br&gt;
try:&lt;br&gt;
    if study.best_trial is not None and study.best_trial.values is not None:&lt;br&gt;
        best_params = study.best_params&lt;br&gt;
except Exception as e:&lt;br&gt;
    print("No valid best_params from Optuna; fallback to defaults.", e)&lt;br&gt;
print("best_params:", best_params)&lt;/p&gt;

&lt;h1&gt;
  
  
  Default fallback params (merge)
&lt;/h1&gt;

&lt;p&gt;lgb_params = {&lt;br&gt;
    'objective': 'regression',&lt;br&gt;
    'metric': 'rmse',&lt;br&gt;
    'boosting_type': 'gbdt',&lt;br&gt;
    'verbosity': -1,&lt;br&gt;
    'seed': SEED,&lt;br&gt;
    'learning_rate': best_params.get('learning_rate', 0.03),&lt;br&gt;
    'num_leaves': int(best_params.get('num_leaves', 64)),&lt;br&gt;
    'min_data_in_leaf': int(best_params.get('min_data_in_leaf', 20)),&lt;br&gt;
    'feature_fraction': best_params.get('feature_fraction', 0.8),&lt;br&gt;
    'bagging_fraction': best_params.get('bagging_fraction', 0.8),&lt;br&gt;
    'bagging_freq': int(best_params.get('bagging_freq', 5)),&lt;br&gt;
    'lambda_l1': best_params.get('lambda_l1', 0.0),&lt;br&gt;
    'lambda_l2': best_params.get('lambda_l2', 0.0)&lt;br&gt;
}&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Train final LightGBM on train_X with early stopping on validation
&lt;/h1&gt;

&lt;p&gt;dtrain = lgb.Dataset(train_X, label=train_y)&lt;br&gt;
dval = lgb.Dataset(val_X, label=val_y, reference=dtrain)&lt;/p&gt;

&lt;p&gt;print("\nTraining final LightGBM...")&lt;br&gt;
lgbm = lgb.train(lgb_params, dtrain, num_boost_round=3000, valid_sets=[dval],&lt;br&gt;
                callbacks=[lgb.early_stopping(100, verbose=100)])&lt;/p&gt;

&lt;p&gt;val_pred_lgb_log = lgbm.predict(val_X, num_iteration=lgbm.best_iteration)&lt;br&gt;
val_rmse_lgb_log = np.sqrt(mean_squared_error(val_y, val_pred_lgb_log))&lt;br&gt;
print("LightGBM val RMSE (log1p):", val_rmse_lgb_log)&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Train XGBoost (stable set of hyperparams)
&lt;/h1&gt;

&lt;p&gt;print("\nTraining XGBoost...")&lt;br&gt;
dtrain_xgb = xgb.DMatrix(train_X, label=train_y)&lt;br&gt;
dval_xgb = xgb.DMatrix(val_X, label=val_y)&lt;br&gt;
dtest_xgb = xgb.DMatrix(test_X)&lt;/p&gt;

&lt;p&gt;xgb_params = {&lt;br&gt;
    'objective': 'reg:squarederror',&lt;br&gt;
    'eval_metric': 'rmse',&lt;br&gt;
    'eta': 0.03,&lt;br&gt;
    'max_depth': 6,&lt;br&gt;
    'subsample': 0.8,&lt;br&gt;
    'colsample_bytree': 0.8,&lt;br&gt;
    'seed': SEED&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;model_xgb = xgb.train(xgb_params, dtrain_xgb, num_boost_round=3000, evals=[(dval_xgb, 'val')],&lt;br&gt;
                      early_stopping_rounds=100, verbose_eval=100)&lt;/p&gt;

&lt;p&gt;val_pred_xgb_log = model_xgb.predict(dval_xgb, iteration_range=(0, model_xgb.best_iteration))&lt;br&gt;
val_rmse_xgb_log = np.sqrt(mean_squared_error(val_y, val_pred_xgb_log))&lt;br&gt;
print("XGBoost val RMSE (log1p):", val_rmse_xgb_log)&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Create OOF predictions for stacking (TimeSeriesSplit)
&lt;/h1&gt;

&lt;p&gt;n_splits = 5&lt;br&gt;
tscv = TimeSeriesSplit(n_splits=n_splits)&lt;br&gt;
oof_lgb = np.zeros(len(train_X))&lt;br&gt;
oof_xgb = np.zeros(len(train_X))&lt;br&gt;
fold = 0&lt;br&gt;
print("\nCreating OOF predictions for stacking...")&lt;br&gt;
for tr_idx, va_idx in tscv.split(train_X):&lt;br&gt;
    fold += 1&lt;br&gt;
    print("Fold", fold)&lt;br&gt;
    # lightgbm fold&lt;br&gt;
    dtr = lgb.Dataset(train_X.iloc[tr_idx], label=train_y.iloc[tr_idx])&lt;br&gt;
    dva = lgb.Dataset(train_X.iloc[va_idx], label=train_y.iloc[va_idx], reference=dtr)&lt;br&gt;
    bst = lgb.train(lgb_params, dtr, num_boost_round=3000, valid_sets=[dva],&lt;br&gt;
                    callbacks=[lgb.early_stopping(100, verbose=False)])&lt;br&gt;
    oof_lgb[va_idx] = bst.predict(train_X.iloc[va_idx], num_iteration=bst.best_iteration)&lt;br&gt;
    # xgb fold&lt;br&gt;
    dxtr = xgb.DMatrix(train_X.iloc[tr_idx], label=train_y.iloc[tr_idx])&lt;br&gt;
    dxva = xgb.DMatrix(train_X.iloc[va_idx], label=train_y.iloc[va_idx])&lt;br&gt;
    bst_x = xgb.train(xgb_params, dxtr, num_boost_round=3000, evals=[(dxva,'val')],&lt;br&gt;
                      early_stopping_rounds=100, verbose_eval=False)&lt;br&gt;
    oof_xgb[va_idx] = bst_x.predict(dxva, iteration_range=(0, bst_x.best_iteration))&lt;/p&gt;

&lt;h1&gt;
  
  
  Meta-model (Ridge)
&lt;/h1&gt;

&lt;p&gt;stack_X = np.vstack([oof_lgb, oof_xgb]).T&lt;br&gt;
meta = Ridge(alpha=1.0)&lt;br&gt;
meta.fit(stack_X, train_y)&lt;br&gt;
print("Meta-model trained (Ridge).")&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Iterative forecasting for test using recursive approach
&lt;/h1&gt;

&lt;h1&gt;
  
  
  We'll maintain 'history' (train prices + predicted test prices) and create features per step
&lt;/h1&gt;

&lt;p&gt;history = pd.concat([train[['id','price']], pd.DataFrame({'id': test['id'].values, 'price': [np.nan]*len(test)})], ignore_index=True)&lt;/p&gt;

&lt;p&gt;def make_row_features(history_df, idx):&lt;br&gt;
    # Build features for row idx based on history up to idx-1&lt;br&gt;
    tmp = history_df.loc[:idx].copy().reset_index(drop=True)&lt;br&gt;
    tmp = create_features_full(tmp)&lt;br&gt;
    # last row is current&lt;br&gt;
    feat_row = tmp.iloc[-1:][FEATURES].fillna(0)&lt;br&gt;
    return feat_row&lt;/p&gt;

&lt;p&gt;print("\nForecasting test iteratively (recursive)...")&lt;br&gt;
preds_test_log = []&lt;br&gt;
for i in range(len(train), len(history)):&lt;br&gt;
    feat = make_row_features(history, i)&lt;br&gt;
    # LGB predict (log-space)&lt;br&gt;
    try:&lt;br&gt;
        p_lgb = lgbm.predict(feat, num_iteration=lgbm.best_iteration)[0]&lt;br&gt;
    except Exception as e:&lt;br&gt;
        p_lgb = val_pred_lgb_log.mean()  # fallback&lt;br&gt;
    try:&lt;br&gt;
        dx = xgb.DMatrix(feat)&lt;br&gt;
        p_xgb = model_xgb.predict(dx, iteration_range=(0, model_xgb.best_iteration))[0]&lt;br&gt;
    except Exception as e:&lt;br&gt;
        p_xgb = val_pred_xgb_log.mean()&lt;br&gt;
    # meta predict (input are log-space preds)&lt;br&gt;
    meta_in = np.array([[p_lgb, p_xgb]])&lt;br&gt;
    p_meta_log = meta.predict(meta_in)[0]&lt;br&gt;
    # convert to price space for storing in history&lt;br&gt;
    p_price = float(np.expm1(p_meta_log))&lt;br&gt;
    if p_price &amp;lt; 0:&lt;br&gt;
        p_price = 0.0&lt;br&gt;
    history.at[i, 'price'] = p_price&lt;br&gt;
    preds_test_log.append(p_meta_log)&lt;/p&gt;

&lt;p&gt;preds_test_price = np.expm1(np.array(preds_test_log))&lt;/p&gt;

&lt;h1&gt;
  
  
  ensure length
&lt;/h1&gt;

&lt;p&gt;if len(preds_test_price) != len(test):&lt;br&gt;
    preds_test_price = np.resize(preds_test_price, len(test))&lt;/p&gt;

&lt;h1&gt;
  
  
  -------------------------
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Final submission
&lt;/h1&gt;

&lt;p&gt;submission = sample_sub.copy()&lt;/p&gt;

&lt;h1&gt;
  
  
  assume column name for predictions is 'price' or second column
&lt;/h1&gt;

&lt;p&gt;if 'price' in submission.columns:&lt;br&gt;
    submission['price'] = preds_test_price&lt;br&gt;
else:&lt;br&gt;
    submission.iloc[:,1] = preds_test_price&lt;/p&gt;

&lt;p&gt;submission['price'] = submission['price'].round(3)&lt;br&gt;
submission.to_csv('submission.csv', index=False)&lt;br&gt;
print("\nSaved submission.csv")&lt;/p&gt;

&lt;h1&gt;
  
  
  Diagnostics: compute final stacked val RMSE in price space
&lt;/h1&gt;

&lt;p&gt;val_pred_lgb_price = np.expm1(val_pred_lgb_log)&lt;br&gt;
val_pred_xgb_price = np.expm1(val_pred_xgb_log)&lt;br&gt;
val_stack = np.vstack([val_pred_lgb_log, val_pred_xgb_log]).T&lt;br&gt;
val_meta_pred_log = meta.predict(val_stack)&lt;br&gt;
val_meta_rmse_price = np.sqrt(mean_squared_error(np.expm1(val_y), np.expm1(val_meta_pred_log)))&lt;br&gt;
print("Final stacked val RMSE (price space):", val_meta_rmse_price)&lt;br&gt;
print("LightGBM val RMSE (price space):", np.sqrt(mean_squared_error(np.expm1(val_y), val_pred_lgb_price)))&lt;br&gt;
print("XGBoost val RMSE (price space):", np.sqrt(mean_squared_error(np.expm1(val_y), val_pred_xgb_price)))&lt;/p&gt;

&lt;h1&gt;
  
  
  download file in Colab
&lt;/h1&gt;

&lt;p&gt;files.download('submission.csv')&lt;br&gt;
print("Done. ดาวน์โหลด submission.csv ได้เลย")&lt;/p&gt;

&lt;p&gt;เริ่มต้นโหลดไฟล์ train.csv, test.csv และ sample_submission.csv&lt;br&gt;&lt;br&gt;
ตรวจสอบว่าไฟล์อยู่ครบและชื่อถูกต้อง&lt;/p&gt;

&lt;p&gt;เรียงข้อมูลตาม id แล้วแปลง price ให้เป็น float&lt;br&gt;&lt;br&gt;
รวม train และ test เข้าด้วยกันเพื่อทำ feature engineering ให้ต่อเนื่อง&lt;/p&gt;

&lt;p&gt;สร้างฟีเจอร์จากราคา เช่น lag, rolling mean, rolling std, ewm, diff, pct_change และฟีเจอร์ seasonality&lt;br&gt;&lt;br&gt;
ทำความสะอาดข้อมูลแทนค่า inf และ NaN&lt;/p&gt;

&lt;p&gt;แยกกลับเป็น train_feat และ test_feat&lt;br&gt;&lt;br&gt;
ลบแถวที่ยังมี NaN (เฉพาะฝั่ง train)&lt;br&gt;&lt;br&gt;
สร้างคอลัมน์ target โดยใช้ log1p(price)&lt;/p&gt;

&lt;p&gt;แบ่งข้อมูลฝั่ง train เป็น train_X และ val_X โดยใช้ข้อมูลชุดสุดท้ายเป็น validation&lt;/p&gt;

&lt;p&gt;ตั้งค่า Optuna เพื่อค้นหา LightGBM parameters ที่เหมาะที่สุด&lt;br&gt;&lt;br&gt;
รัน Optuna แบบ time-series cross-validation 3 รอบ&lt;br&gt;&lt;br&gt;
ดึงค่าพารามิเตอร์ที่ดีที่สุด (ถ้า error จะใช้ค่า default)&lt;/p&gt;

&lt;p&gt;เทรน LightGBM บน train_X และหยุดเมื่อ validation ไม่ดีขึ้น&lt;br&gt;&lt;br&gt;
เทรน XGBoost ด้วย early stopping เช่นกัน&lt;/p&gt;

&lt;p&gt;สร้าง OOF predictions ด้วย TimeSeriesSplit 5 fold สำหรับใช้ฝึก meta-model&lt;br&gt;&lt;br&gt;
เทรน Ridge Regression เพื่อใช้เป็น meta-model ของ Stacking&lt;/p&gt;

&lt;p&gt;เริ่มพยากรณ์ข้อมูล test แบบวนทีละแถว (recursive)&lt;br&gt;&lt;br&gt;
ทุกครั้งที่ทำนายได้ จะเติมค่าที่ได้กลับเข้า history แล้วสร้างฟีเจอร์ใหม่&lt;br&gt;&lt;br&gt;
รวมผลทำนายของ LightGBM และ XGBoost ผ่าน meta-model&lt;/p&gt;

&lt;p&gt;นำผลทั้งหมดมาใส่ในไฟล์ submission.csv&lt;br&gt;&lt;br&gt;
บันทึกไฟล์และพร้อมส่ง&lt;/p&gt;

&lt;p&gt;Data Preprocessing (อธิบายการเตรียมข้อมูล)&lt;br&gt;
    1.  โหลดไฟล์&lt;br&gt;
ดึง train, test และ sample_submission เข้ามา แล้วตรวจสอบว่าไฟล์อยู่ในโฟลเดอร์และชื่อถูกต้อง&lt;br&gt;
    2.  จัดเรียงข้อมูลตาม id&lt;br&gt;
เนื่องจากเป็นข้อมูลแบบ time-series การเรียงลำดับให้ถูกต้องสำคัญมาก&lt;br&gt;
    3.  รวม train + test ชั่วคราว&lt;br&gt;
ทำเพื่อให้การสร้างฟีเจอร์ที่อิงข้อมูลย้อนหลัง เช่น lag หรือ rolling ไม่ขาดช่วง&lt;br&gt;
    4.  Feature Engineering หลักที่ใช้&lt;br&gt;
    • Lag features เช่น lag_1, lag_3, lag_7, lag_30&lt;br&gt;
ใช้เก็บราคาย้อนหลังตามจำนวนวันต่างๆ&lt;br&gt;
    • Rolling windows ค่าเฉลี่ย, ส่วนเบี่ยงเบนมาตรฐาน และ median&lt;br&gt;
    • EWMA (Exponential Weighted Mean)&lt;br&gt;
    • Expanding mean แนวโน้มสะสม&lt;br&gt;
    • Diff และ percentage change เพื่อตรวจจับการเปลี่ยนแปลง&lt;br&gt;
    • Seasonality เช่น id mod 7 (รูปแบบรายสัปดาห์), id mod 30 (วงรอบเดือน)&lt;br&gt;
    • Ratio feature เช่น lag1 / lag7&lt;br&gt;
    5.  ลบค่า NaN ที่ฝั่ง train&lt;br&gt;
เฉพาะ train เท่านั้นที่ต้องทำให้สะอาด ส่วน test ใช้ ffill + bfill&lt;br&gt;
    6.  สร้าง target = log1p(price)&lt;br&gt;
ช่วยให้โมเดลเรียนรู้ได้ง่ายขึ้นและลดความแปรปรวนในข้อมูลราคา&lt;/p&gt;

&lt;p&gt;Sequential Model + Hyperparameters&lt;/p&gt;

&lt;p&gt;สเต็ปการสร้างโมเดลมี 3 ชั้น&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;1) LightGBM (Base Model ตัวที่ 1)&lt;/p&gt;

&lt;p&gt;ใช้ Optuna ค้นหา:&lt;br&gt;
    • learning_rate&lt;br&gt;
    • num_leaves&lt;br&gt;
    • min_data_in_leaf&lt;br&gt;
    • feature_fraction&lt;br&gt;
    • bagging_fraction&lt;br&gt;
    • lambda_l1, lambda_l2&lt;/p&gt;

&lt;p&gt;เทรนด้วย early stopping เพื่อป้องกัน overfitting&lt;br&gt;
ใช้ metric = RMSE&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;2) XGBoost (Base Model ตัวที่ 2)&lt;/p&gt;

&lt;p&gt;ตั้งค่าพารามิเตอร์แบบคงที่ให้ค่อนข้าง stable เช่น&lt;br&gt;
    • eta = 0.03&lt;br&gt;
    • max_depth = 6&lt;br&gt;
    • subsample = 0.8&lt;br&gt;
    • colsample_bytree = 0.8&lt;/p&gt;

&lt;p&gt;เทรนด้วย early stopping เช่นกัน&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;3) Ridge Regression (Meta-model)&lt;/p&gt;

&lt;p&gt;ใช้รวมผลของทั้ง LGB + XGB&lt;br&gt;
โดยป้อนค่าทำนายของสองโมเดลเป็น feature&lt;br&gt;
สามารถลด noise และเฉลี่ยความแม่นยำของทั้งสองโมเดลให้ดีขึ้น&lt;/p&gt;

&lt;p&gt;Validation ที่ใช้ใน Pipeline นี้&lt;/p&gt;

&lt;p&gt;ตรวจสอบโมเดล 3 ระดับ&lt;/p&gt;

&lt;p&gt;1) Hold-out Validation (Time-series split)&lt;/p&gt;

&lt;p&gt;ใช้ข้อมูลช่วงท้ายสุดประมาณ 10% เป็น validation&lt;br&gt;
เหมาะกับข้อมูลที่มีลำดับเวลา&lt;/p&gt;

&lt;p&gt;2) Optuna Cross-validation (TimeSeriesSplit 3 fold)&lt;/p&gt;

&lt;p&gt;ช่วยให้เลือก hyperparameter ที่เหมาะที่สุดโดยไม่เสี่ยงต่อการฟลุ๊ค&lt;/p&gt;

&lt;p&gt;3) OOF validation สำหรับ stacking (5 fold)&lt;/p&gt;

&lt;p&gt;ใช้สร้างข้อมูลฝึก meta-model แบบไม่เกิด data leakage&lt;br&gt;
ทำให้ stacking มีความแม่นยำขึ้น&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>tutorial</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
