{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":84493,"databundleVersionId":9871156,"sourceType":"competition"},{"sourceId":209248,"sourceType":"modelInstanceVersion","modelInstanceId":178409,"modelId":200703},{"sourceId":210219,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":179229,"modelId":201509},{"sourceId":210434,"sourceType":"modelInstanceVersion","modelInstanceId":179403,"modelId":201676}],"dockerImageVersionId":30786,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd \nimport polars as pl\nimport lightgbm as lgb\nimport joblib\n\nimport os\n\nimport warnings \nwarnings.filterwarnings(\"ignore\")\n\nimport kaggle_evaluation.jane_street_inference_server","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:17.706690Z","iopub.execute_input":"2024-12-26T09:37:17.707379Z","iopub.status.idle":"2024-12-26T09:37:21.824518Z","shell.execute_reply.started":"2024-12-26T09:37:17.707331Z","shell.execute_reply":"2024-12-26T09:37:21.823575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_data(date_id_range=None, time_id_range=None, columns=None, return_type='pl'):\n    data_dir = '/kaggle/input/jane-street-real-time-market-data-forecasting'\n    data = pl.scan_parquet(f'{data_dir}/train.parquet')\n    \n    if date_id_range is not None:\n        start_date, end_date = date_id_range\n        data = data.filter((pl.col(\"date_id\") >= start_date) & (pl.col(\"date_id\") <= end_date))\n    \n    if time_id_range is not None:\n        start_time, end_time = time_id_range\n        data = data.filter((pl.col(\"time_id\") >= start_time) & (pl.col(\"time_id\") <= end_time))\n    \n    if columns is not None:\n        data = data.select(columns)\n\n    if return_type == 'pd':\n        return data.collect().to_pandas()\n    else:\n        return data.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:21.825926Z","iopub.execute_input":"2024-12-26T09:37:21.826591Z","iopub.status.idle":"2024-12-26T09:37:21.832462Z","shell.execute_reply.started":"2024-12-26T09:37:21.826543Z","shell.execute_reply":"2024-12-26T09:37:21.831558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_r2(y_true, y_pred, weights):\n    numerator = np.sum(weights * (y_true - y_pred) ** 2)\n    denominator = np.sum(weights * (y_true ** 2))\n    r2_score = 1 - (numerator / denominator)\n    return r2_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:21.833388Z","iopub.execute_input":"2024-12-26T09:37:21.833863Z","iopub.status.idle":"2024-12-26T09:37:21.845697Z","shell.execute_reply.started":"2024-12-26T09:37:21.833839Z","shell.execute_reply":"2024-12-26T09:37:21.844936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TARGET = 'responder_6'\nFEAT_COLS = [f\"feature_{i:02d}\" for i in range(79)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:21.847148Z","iopub.execute_input":"2024-12-26T09:37:21.847437Z","iopub.status.idle":"2024-12-26T09:37:21.855575Z","shell.execute_reply.started":"2024-12-26T09:37:21.847413Z","shell.execute_reply":"2024-12-26T09:37:21.854918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# lgb with k-fold cross-validation\ndef train_lgb_kfold_single(total_days=1699, n_splits=5, save_model=True, save_path='models/'):\n    if save_model and not os.path.exists(save_path):\n        os.makedirs(save_path)\n\n    # Number of dates to skip from the beginning of the dataset\n    skip_dates = 500  # 跳过前500天\n    max_valid_days = 1200  # 最多使用后1200天进行交叉验证\n    valid_days = min(total_days, max_valid_days)  # 实际用于交叉验证的天数\n    valid_start = 1699 - valid_days  # 计算交叉验证的起始日期（倒数）\n    \n    fold_size = valid_days // n_splits\n    folds = [(valid_start + i * fold_size, valid_start + (i + 1) * fold_size - 1) for i in range(n_splits)]\n    \n    cv_scores = []\n    model_group = []\n    \n    for fold_idx in range(n_splits):\n        valid_range = folds[fold_idx]\n        train_ranges = [folds[i] for i in range(n_splits) if i != fold_idx]\n        print(f'Fold {fold_idx}: validation range {valid_range}, train parts: {train_ranges}')\n\n        # load valid data\n        valid_data = load_data(date_id_range=valid_range, \n                               columns=[\"date_id\", \"weight\"] + FEAT_COLS + [TARGET], \n                               return_type='pl')\n        valid_weight = valid_data['weight'].to_pandas()\n\n        # load train data\n        train_data = None\n        for train_range in train_ranges:\n            partial_train_data = load_data(date_id_range=train_range,\n                                           columns=[\"date_id\", \"weight\"] + FEAT_COLS + [TARGET], \n                                           return_type='pl')\n            if train_data is None:\n                train_data = partial_train_data\n            else:\n                train_data = train_data.vstack(partial_train_data)\n\n        train_weight = train_data['weight'].to_pandas()\n\n        # build LightGBM dataset\n        train_ds = lgb.Dataset(train_data.select(FEAT_COLS + ['weight']).to_pandas(), \n                               label=train_data[TARGET].to_pandas(), weight=train_weight)\n        valid_ds = lgb.Dataset(valid_data.select(FEAT_COLS + ['weight']).to_pandas(), \n                               label=valid_data[TARGET].to_pandas(), weight=valid_weight, reference=train_ds)\n\n        # LightGBM parameters\n        LGB_PARAMS = {\n            'objective': 'regression_l2',\n            'metric': 'rmse',\n            'learning_rate': 0.05,\n            'num_leaves': 31,\n            'max_depth': -1,\n            'random_state': 42,\n            'device': 'gpu',\n        }\n\n        # callback functions\n        early_stopping_callback = lgb.early_stopping(100)\n        verbose_eval_callback = lgb.log_evaluation(period=50)\n\n        # train model\n        model = lgb.train(\n            LGB_PARAMS,\n            train_ds,\n            num_boost_round=1000,\n            valid_sets=[train_ds, valid_ds],\n            valid_names=['train', 'valid'],\n            callbacks=[early_stopping_callback, verbose_eval_callback],\n        )\n\n        # save model\n        model_group.append(model)\n        \n        # predict on valid set and compute R2\n        y_valid_pred = model.predict(valid_data.select(FEAT_COLS + ['weight']).to_pandas())\n        r2_score = calculate_r2(valid_data[TARGET].to_pandas(), y_valid_pred, valid_weight)\n        print(f\"Fold {fold_idx} validation R2 score: {r2_score}\")\n\n        cv_scores.append(r2_score)\n\n    # Model fusion: The output of all models is averaged\n    print(f\"Total trained models: {len(model_group)}\")\n    final_model = model_group[1]  # The structure of the first model is used\n    print(\"Averaging models...\")\n    average_predictions = lambda data: average_models(model_group, data)\n    print(\"Done.\")\n    # Save the entire model group\n    if save_model:\n        joblib.dump(final_model, \"lgb_model.pkl\")\n        print(\"Saved the final merged model to lgb_model.pkl\")\n        \n    print(f\"Cross-validation R2 scores: {cv_scores}\")\n    print(f\"Mean R2 score: {np.mean(cv_scores)}, Std: {np.std(cv_scores)}\")\n\n    return model, np.mean(cv_scores), np.std(cv_scores)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:21.856841Z","iopub.execute_input":"2024-12-26T09:37:21.857278Z","iopub.status.idle":"2024-12-26T09:37:21.870443Z","shell.execute_reply.started":"2024-12-26T09:37:21.857241Z","shell.execute_reply":"2024-12-26T09:37:21.869854Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### First Version: Model Training","metadata":{}},{"cell_type":"code","source":"# total_days = 500 # Total num of diff date_id = 1699\n# lgb_models, _, _ = train_lgb_kfold_single(total_days=total_days, n_splits =5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T03:44:03.479469Z","iopub.execute_input":"2024-12-26T03:44:03.480423Z","iopub.status.idle":"2024-12-26T04:03:00.851993Z","shell.execute_reply.started":"2024-12-26T03:44:03.480381Z","shell.execute_reply":"2024-12-26T04:03:00.850184Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Second Version: Model Loading","metadata":{}},{"cell_type":"code","source":"# Load the model from the saved file\n\nmodel_path = '/kaggle/input/lgb-group/other/default/1'\nmodel_name = 'lgb'\nmodels = []\nfor i in range(5):\n    models.append(joblib.load(f'{model_path}/{model_name}_{i}.model'))\n\nprint(f\"Loaded model from the saved file.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:28.462145Z","iopub.execute_input":"2024-12-26T09:37:28.462948Z","iopub.status.idle":"2024-12-26T09:37:28.650153Z","shell.execute_reply.started":"2024-12-26T09:37:28.462913Z","shell.execute_reply":"2024-12-26T09:37:28.649368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"lags_ : pl.DataFrame | None = None\n\ndef predict(test: pl.DataFrame, lags: pl.DataFrame | None) -> pl.DataFrame | pd.DataFrame:\n    \"\"\"Make a prediction.\"\"\"\n    global lags_\n    if lags is not None:\n        lags_ = lags\n\n    predictions = test.select(\n        'row_id',\n        pl.lit(0.0).alias('responder_6')\n    )\n    \n    feat = test[FEAT_COLS + ['weight']].to_pandas()\n\n    #pred = model.predict(feat)\n    pred = [model.predict(feat) for model in models]\n    pred = np.mean(pred, axis=0)\n    \n    predictions = predictions.with_columns(pl.Series('responder_6', pred.ravel()))\n    print(predictions)\n    \n    assert isinstance(predictions, pl.DataFrame | pd.DataFrame)\n    \n    assert list(predictions.columns) == ['row_id', 'responder_6']\n    assert len(predictions) == len(test)\n    \n    return predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:31.162818Z","iopub.execute_input":"2024-12-26T09:37:31.163510Z","iopub.status.idle":"2024-12-26T09:37:31.169535Z","shell.execute_reply.started":"2024-12-26T09:37:31.163479Z","shell.execute_reply":"2024-12-26T09:37:31.168730Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inference_server = kaggle_evaluation.jane_street_inference_server.JSInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\n    inference_server.run_local_gateway(\n        (\n            '/kaggle/input/jane-street-real-time-market-data-forecasting/test.parquet',\n            '/kaggle/input/jane-street-real-time-market-data-forecasting/lags.parquet',\n        )\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T09:37:32.881256Z","iopub.execute_input":"2024-12-26T09:37:32.881603Z","iopub.status.idle":"2024-12-26T09:37:33.205651Z","shell.execute_reply.started":"2024-12-26T09:37:32.881574Z","shell.execute_reply":"2024-12-26T09:37:33.204601Z"}},"outputs":[],"execution_count":null}]}