{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":96164,"databundleVersionId":11418275,"sourceType":"competition"},{"sourceId":12353502,"sourceType":"datasetVersion","datasetId":7788257},{"sourceId":12361146,"sourceType":"datasetVersion","datasetId":7793444}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:06:07.499367Z","iopub.execute_input":"2025-07-03T09:06:07.499574Z","iopub.status.idle":"2025-07-03T09:06:09.092569Z","shell.execute_reply.started":"2025-07-03T09:06:07.499557Z","shell.execute_reply":"2025-07-03T09:06:09.091778Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## The Code of Preprocessing Please Check the \"/kaggle/input/drw-preprocessing\"","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import mean_squared_error, r2_score, mean_absolute_percentage_error\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import TensorDataset, DataLoader","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:06:54.629844Z","iopub.execute_input":"2025-07-03T09:06:54.630592Z","iopub.status.idle":"2025-07-03T09:06:59.085063Z","shell.execute_reply.started":"2025-07-03T09:06:54.630565Z","shell.execute_reply":"2025-07-03T09:06:59.084273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Config:\n    TRAIN_PATH = \"/kaggle/input/denoise-drw/denoise_train_df.csv\"\n    TEST_PATH = \"/kaggle/input/denoise-drw/denoise_test_df.csv\"\n    SUBMISSION_PATH = \"/kaggle/input/drw-crypto-market-prediction/sample_submission.csv\"\n\n    FEATURES = ['X21', 'X20', 'X28', 'X863', 'X29', 'X19', 'X27', 'X22', 'X858',\n       'X219', 'X860', 'X531', 'X287', 'X289', 'X291', 'X293', 'X857',\n       'X295', 'X598', 'X218', \"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\"]\n\n    LABEL_COLUMN = \"label\"\n    N_FOLDS = 3\n    RANDOM_STATE = 42\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:01.703183Z","iopub.execute_input":"2025-07-03T09:07:01.703614Z","iopub.status.idle":"2025-07-03T09:07:01.762160Z","shell.execute_reply.started":"2025-07-03T09:07:01.703591Z","shell.execute_reply":"2025-07-03T09:07:01.761465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_data():\n    \"\"\"Load and preprocess data\"\"\"\n    train_df = pd.read_csv(Config.TRAIN_PATH\n                              )\n    test_df = pd.read_csv(Config.TEST_PATH\n                             )\n    submission_df = pd.read_csv(Config.SUBMISSION_PATH)\n    print(f\"Loaded data - Train: {train_df.shape}, Test: {test_df.shape}, Submission: {submission_df.shape}\")\n    return train_df.reset_index(drop=True), test_df.reset_index(drop=True), submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:04.018824Z","iopub.execute_input":"2025-07-03T09:07:04.019096Z","iopub.status.idle":"2025-07-03T09:07:04.023631Z","shell.execute_reply.started":"2025-07-03T09:07:04.019074Z","shell.execute_reply":"2025-07-03T09:07:04.022853Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df,test_df,submission_df = load_data()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:06.167801Z","iopub.execute_input":"2025-07-03T09:07:06.168067Z","iopub.status.idle":"2025-07-03T09:07:12.902876Z","shell.execute_reply.started":"2025-07-03T09:07:06.168045Z","shell.execute_reply":"2025-07-03T09:07:12.902231Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:21.081032Z","iopub.execute_input":"2025-07-03T09:07:21.081318Z","iopub.status.idle":"2025-07-03T09:07:21.116189Z","shell.execute_reply.started":"2025-07-03T09:07:21.081298Z","shell.execute_reply":"2025-07-03T09:07:21.115450Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:23.538317Z","iopub.execute_input":"2025-07-03T09:07:23.538796Z","iopub.status.idle":"2025-07-03T09:07:23.554523Z","shell.execute_reply.started":"2025-07-03T09:07:23.538773Z","shell.execute_reply":"2025-07-03T09:07:23.553946Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:26.589573Z","iopub.execute_input":"2025-07-03T09:07:26.589850Z","iopub.status.idle":"2025-07-03T09:07:26.597131Z","shell.execute_reply.started":"2025-07-03T09:07:26.589829Z","shell.execute_reply":"2025-07-03T09:07:26.596465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train_df = train_df.replace([np.inf, -np.inf], np.nan)\n# train_df = train_df.fillna(0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T08:35:34.282737Z","iopub.execute_input":"2025-07-03T08:35:34.283023Z","iopub.status.idle":"2025-07-03T08:35:34.286369Z","shell.execute_reply.started":"2025-07-03T08:35:34.283005Z","shell.execute_reply":"2025-07-03T08:35:34.285631Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. 读入数据\ndf = train_df  # 假设已加载为 pandas.DataFrame\nX = df.iloc[:, :-1].values  # (525887, 25)\ny = df.iloc[:, -1].values  # (525887,)\n\n# 2. 划分数据：先划分 train + temp，再从 temp 中再分出 val/test\nX_train, X_temp, y_train, y_temp = train_test_split(\n    X, y, train_size=0.90,  shuffle=False)\n# temp 占 10%，我们再一分为二：各占 5%\nX_val, X_test, y_val, y_test = train_test_split(\n    X_temp, y_temp, test_size=0.50, shuffle=False)\n\nprint(\"Split shapes:\", X_train.shape, X_val.shape, X_test.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:29.609290Z","iopub.execute_input":"2025-07-03T09:07:29.609586Z","iopub.status.idle":"2025-07-03T09:07:29.805888Z","shell.execute_reply.started":"2025-07-03T09:07:29.609564Z","shell.execute_reply":"2025-07-03T09:07:29.805202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# scaler = StandardScaler()\n# X_train = scaler.fit_transform(X_train)\n# X_val   = scaler.transform(X_val)\n# X_test  = scaler.transform(X_test)\ntest_df = test_df.values","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:35.951123Z","iopub.execute_input":"2025-07-03T09:07:35.951826Z","iopub.status.idle":"2025-07-03T09:07:36.260676Z","shell.execute_reply.started":"2025-07-03T09:07:35.951802Z","shell.execute_reply":"2025-07-03T09:07:36.260070Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"CNN","metadata":{}},{"cell_type":"code","source":"class PearsonLoss(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n    def forward(self, y_pred, y_true):\n        # 中心化预测值和真实值\n        y_pred_centered = y_pred - torch.mean(y_pred)\n        y_true_centered = y_true - torch.mean(y_true)\n\n        # 计算协方差和标准差\n        covariance = torch.sum(y_pred_centered * y_true_centered)\n        std_pred = torch.sqrt(torch.sum(y_pred_centered ** 2))\n        std_true = torch.sqrt(torch.sum(y_true_centered ** 2))\n\n        # 避免除零（添加小常数epsilon）\n        epsilon = 1e-6\n        pearson = covariance / (std_pred * std_true + epsilon)\n\n        # 损失为1 - r（因为r∈[-1,1]，最大化r等价于最小化1-r）\n        return 1 - pearson","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CombinedLoss(nn.Module):\n    def __init__(self, alpha=1.0, beta=1.0):\n        super().__init__()\n        self.alpha = alpha\n        self.beta = beta\n        self.mse_loss = nn.MSELoss()\n\n    def pearson_loss(self, y_pred, y_true):\n        y_pred_centered = y_pred - torch.mean(y_pred)\n        y_true_centered = y_true - torch.mean(y_true)\n        covariance = torch.sum(y_pred_centered * y_true_centered)\n        std_pred = torch.sqrt(torch.sum(y_pred_centered ** 2) + 1e-6)\n        std_true = torch.sqrt(torch.sum(y_true_centered ** 2) + 1e-6)\n        pearson = covariance / (std_pred * std_true)\n        return 1 - pearson\n\n    def forward(self, y_pred, y_true):\n        mse = self.mse_loss(y_pred, y_true)\n        pcc = self.pearson_loss(y_pred, y_true)\n        return self.alpha * mse + self.beta * pcc","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class PureConv1DRegressor(nn.Module):\n    \"\"\"纯一维卷积回归模型\"\"\"\n    def __init__(self, input_dim=25, \n                 conv_channels=[64, 128, 256, 128],\n                 kernel_sizes=[3, 5, 3, 3],\n                 dilation_rates=[1, 2, 3, 1],\n                 fc_dims=[128, 64],\n                 dropout=0.2):\n        \"\"\"\n        参数:\n        input_dim: 输入特征维度 (默认25)\n        conv_channels: 各卷积层的通道数 (默认[64, 128, 256, 128])\n        kernel_sizes: 各卷积层的核大小 (默认[3, 5, 3, 3])\n        dilation_rates: 各卷积层的膨胀率 (默认[1, 2, 3, 1])\n        fc_dims: 全连接层维度 (默认[128, 64])\n        dropout: Dropout率 (默认0.2)\n        \"\"\"\n        super().__init__()\n        \n        # 输入层: 添加通道维度 [batch, 1, input_dim]\n        self.input_layer = nn.Conv1d(\n            in_channels=1, \n            out_channels=conv_channels[0],\n            kernel_size=1\n        )\n        \n        # 一维卷积块\n        self.conv_blocks = nn.ModuleList()\n        for i in range(len(conv_channels) - 1):\n            conv_layer = nn.Conv1d(\n                in_channels=conv_channels[i],\n                out_channels=conv_channels[i+1],\n                kernel_size=kernel_sizes[i],\n                dilation=dilation_rates[i],\n                padding=self._calculate_padding(kernel_sizes[i], dilation_rates[i])\n            )\n            \n            block = nn.Sequential(\n                conv_layer,\n                nn.BatchNorm1d(conv_channels[i+1]),\n                nn.ReLU(),\n                nn.Dropout(dropout)\n            )\n            self.conv_blocks.append(block)\n        \n        # 全局平均池化\n        self.global_pool = nn.AdaptiveAvgPool1d(1)\n        \n        # 全连接预测头\n        self.fc_layers = nn.ModuleList()\n        fc_input_dim = conv_channels[-1]\n        for dim in fc_dims:\n            self.fc_layers.append(nn.Linear(fc_input_dim, dim))\n            self.fc_layers.append(nn.BatchNorm1d(dim))\n            self.fc_layers.append(nn.ReLU())\n            self.fc_layers.append(nn.Dropout(dropout))\n            fc_input_dim = dim\n        \n        # 最终输出层\n        self.output = nn.Linear(fc_input_dim, 1)\n        \n        # 初始化权重\n        self._init_weights()\n    \n    def _init_weights(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv1d):\n                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')\n                if m.bias is not None:\n                    nn.init.constant_(m.bias, 0)\n            elif isinstance(m, nn.Linear):\n                nn.init.xavier_normal_(m.weight)\n                nn.init.constant_(m.bias, 0)\n    \n    def _calculate_padding(self, kernel_size, dilation):\n        \"\"\"计算保持特征长度不变的填充大小\"\"\"\n        padding = (dilation * (kernel_size - 1)) // 2\n        return padding\n    \n    def forward(self, x):\n        # x shape: (batch_size, input_dim)\n        \n        # 添加通道维度: [batch, 1, input_dim]\n        x = x.unsqueeze(1)\n        \n        # 输入层处理\n        x = self.input_layer(x)\n        \n        # 通过卷积块\n        for block in self.conv_blocks:\n            x = block(x)\n        \n        # 全局平均池化: [batch, channels, 1]\n        x = self.global_pool(x)\n        \n        # 移除维度: [batch, channels]\n        x = x.squeeze(-1)\n        \n        # 通过全连接层\n        for layer in self.fc_layers:\n            x = layer(x)\n        \n        # 输出预测\n        return self.output(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:08:32.131981Z","iopub.execute_input":"2025-07-03T09:08:32.132514Z","iopub.status.idle":"2025-07-03T09:08:32.142609Z","shell.execute_reply.started":"2025-07-03T09:08:32.132494Z","shell.execute_reply":"2025-07-03T09:08:32.141781Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ResidualConv1DRegressor(nn.Module):\n    \"\"\"带残差连接的一维卷积回归模型\"\"\"\n    def __init__(self, input_dim=25, \n                 base_channels=64,\n                 num_blocks=4,\n                 block_channels=[64, 128, 256, 128],\n                 kernel_sizes=[3, 3, 3, 3],\n                 dilation_rates=[1, 2, 3, 1],\n                 fc_dims=[128, 64],\n                 dropout=0.2):\n        super().__init__()\n        \n        # 输入层\n        self.input_conv = nn.Conv1d(\n            in_channels=1, \n            out_channels=base_channels,\n            kernel_size=1\n        )\n        \n        # 残差卷积块\n        self.res_blocks = nn.ModuleList()\n        in_channels = base_channels\n        \n        for i in range(num_blocks):\n            out_channels = block_channels[i]\n            res_block = ResidualConvBlock(\n                in_channels, \n                out_channels,\n                kernel_size=kernel_sizes[i],\n                dilation=dilation_rates[i],\n                dropout=dropout\n            )\n            self.res_blocks.append(res_block)\n            in_channels = out_channels\n        \n        # 全局池化\n        self.global_pool = nn.AdaptiveAvgPool1d(1)\n        \n        # 全连接预测头\n        self.fc_head = nn.Sequential(\n            nn.Linear(in_channels, fc_dims[0]),\n            nn.BatchNorm1d(fc_dims[0]),\n            nn.ReLU(),\n            nn.Dropout(dropout),\n            \n            nn.Linear(fc_dims[0], fc_dims[1]),\n            nn.BatchNorm1d(fc_dims[1]),\n            nn.ReLU(),\n            nn.Dropout(dropout),\n            \n            nn.Linear(fc_dims[1], 1)\n        )\n    \n    def forward(self, x):\n        # 添加通道维度: [batch, 1, input_dim]\n        x = x.unsqueeze(1)\n        \n        # 输入卷积\n        x = self.input_conv(x)\n        \n        # 通过残差块\n        for block in self.res_blocks:\n            x = block(x)\n        \n        # 全局池化\n        x = self.global_pool(x)\n        \n        # 移除维度: [batch, channels]\n        x = x.squeeze(-1)\n        \n        # 预测头\n        return self.fc_head(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:10:17.592766Z","iopub.execute_input":"2025-07-03T09:10:17.593046Z","iopub.status.idle":"2025-07-03T09:10:17.600825Z","shell.execute_reply.started":"2025-07-03T09:10:17.593026Z","shell.execute_reply":"2025-07-03T09:10:17.599970Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ResidualConvBlock(nn.Module):\n    \"\"\"残差卷积块\"\"\"\n    def __init__(self, in_channels, out_channels, \n                 kernel_size=3, dilation=1, dropout=0.2):\n        super().__init__()\n        \n        # 主路径\n        self.conv1 = nn.Conv1d(\n            in_channels, out_channels, \n            kernel_size=kernel_size,\n            padding=(dilation * (kernel_size - 1)) // 2,\n            dilation=dilation\n        )\n        self.bn1 = nn.BatchNorm1d(out_channels)\n        \n        self.conv2 = nn.Conv1d(\n            out_channels, out_channels, \n            kernel_size=kernel_size,\n            padding=(dilation * (kernel_size - 1)) // 2,\n            dilation=dilation\n        )\n        self.bn2 = nn.BatchNorm1d(out_channels)\n        \n        # 残差连接\n        self.shortcut = nn.Sequential()\n        if in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv1d(in_channels, out_channels, kernel_size=1),\n                nn.BatchNorm1d(out_channels)\n            )\n        \n        # 激活和正则化\n        self.relu = nn.ReLU()\n        self.dropout = nn.Dropout(dropout)\n    \n    def forward(self, x):\n        residual = self.shortcut(x)\n        \n        # 第一层\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n        out = self.dropout(out)\n        \n        # 第二层\n        out = self.conv2(out)\n        out = self.bn2(out)\n        \n        # 残差连接\n        out += residual\n        out = self.relu(out)\n        \n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:39.494617Z","iopub.execute_input":"2025-07-03T09:07:39.495175Z","iopub.status.idle":"2025-07-03T09:07:39.501828Z","shell.execute_reply.started":"2025-07-03T09:07:39.495152Z","shell.execute_reply":"2025-07-03T09:07:39.501175Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 修改后的训练函数\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs, patience=5):\n    best_loss = float('inf')\n    no_improve_epochs = 0\n    best_model_state = None\n    \n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0.0\n        for inputs, targets in train_loader:\n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, targets)\n            loss.backward()\n            optimizer.step()\n            train_loss += loss.item()\n        \n        model.eval()\n        val_loss = 0.0\n        with torch.no_grad():\n            for inputs, targets in val_loader:\n                outputs = model(inputs)\n                loss = criterion(outputs, targets)\n                val_loss += loss.item()\n        \n        avg_train_loss = train_loss/len(train_loader)\n        avg_val_loss = val_loss/len(val_loader)\n        \n        print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, Val Loss: {avg_val_loss:.4f}')\n        \n        # 早停机制\n        if avg_val_loss < best_loss:\n            best_loss = avg_val_loss\n            no_improve_epochs = 0\n            best_model_state = model.state_dict()  # 保存最优模型状态\n            torch.save(best_model_state, 'best_model.pth')  # 保存到文件\n        else:\n            no_improve_epochs += 1\n            if no_improve_epochs >= patience:\n                print(f'Early stopping at epoch {epoch+1} with best val loss: {best_loss:.4f}')\n                model.load_state_dict(best_model_state)  # 恢复最优模型\n                break\n    \n    # 如果全程没有触发早停，确保返回的是最优模型\n    if no_improve_epochs < patience:\n        model.load_state_dict(best_model_state)\n    \n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:44.750691Z","iopub.execute_input":"2025-07-03T09:07:44.751167Z","iopub.status.idle":"2025-07-03T09:07:44.757781Z","shell.execute_reply.started":"2025-07-03T09:07:44.751146Z","shell.execute_reply":"2025-07-03T09:07:44.757012Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    TensorDataset(\n        torch.tensor(X_train).float().to(Config.device), \n        torch.tensor(y_train).float().unsqueeze(1).to(Config.device)\n    ), \n    batch_size=1024, \n    shuffle=False\n)\n\nval_loader = DataLoader(\n    TensorDataset(\n        torch.tensor(X_val).float().to(Config.device), \n        torch.tensor(y_val).float().unsqueeze(1).to(Config.device)\n    ),\n    batch_size=1024, \n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:07:50.359646Z","iopub.execute_input":"2025-07-03T09:07:50.359924Z","iopub.status.idle":"2025-07-03T09:07:50.608084Z","shell.execute_reply.started":"2025-07-03T09:07:50.359906Z","shell.execute_reply":"2025-07-03T09:07:50.607327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss_fn = nn.HuberLoss(delta=5, reduction='sum')\nmodel = ResidualConv1DRegressor(\n        input_dim=25,\n        base_channels=64,\n        num_blocks=4,\n        block_channels=[64, 128, 256, 128],\n        kernel_sizes=[3, 3, 3, 3],\n        dilation_rates=[1, 2, 3, 1],\n        fc_dims=[128, 64],\n        dropout=0.2\n    ).to(Config.device)\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:10:24.441935Z","iopub.execute_input":"2025-07-03T09:10:24.442197Z","iopub.status.idle":"2025-07-03T09:10:27.009125Z","shell.execute_reply.started":"2025-07-03T09:10:24.442176Z","shell.execute_reply":"2025-07-03T09:10:27.008611Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trained_model = train_model(model, train_loader, val_loader, loss_fn, optimizer, num_epochs=200, patience=10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T09:10:31.080621Z","iopub.execute_input":"2025-07-03T09:10:31.081042Z","iopub.status.idle":"2025-07-03T10:10:33.357837Z","shell.execute_reply.started":"2025-07-03T09:10:31.081021Z","shell.execute_reply":"2025-07-03T10:10:33.357142Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pre_test = trained_model(torch.tensor(X_test).float().to(Config.device)).cpu().detach().numpy()\ntest_mse = mean_squared_error(y_test, y_pre_test)\ntest_rmse = np.sqrt(test_mse)\ntest_mape = mean_absolute_percentage_error(y_test, y_pre_test)\ntest_r2 = r2_score(y_test, y_pre_test)\nprint(f\"Test MSE: {test_mse:.4f}, Test RMSE: {test_rmse:.4f}, Test MAPE: {test_mape:.4f}, Test R^2: {test_r2:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T10:14:36.953917Z","iopub.execute_input":"2025-07-03T10:14:36.954593Z","iopub.status.idle":"2025-07-03T10:14:37.209163Z","shell.execute_reply.started":"2025-07-03T10:14:36.954567Z","shell.execute_reply":"2025-07-03T10:14:37.208585Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pearson_corr = np.corrcoef(y_test.flatten(), y_pre_test.flatten())[0, 1]\nprint(\"Pearson correlation coefficient (NumPy):\", pearson_corr)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(trained_model, 'denoise_CNN.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T10:14:44.195528Z","iopub.execute_input":"2025-07-03T10:14:44.196210Z","iopub.status.idle":"2025-07-03T10:14:44.214501Z","shell.execute_reply.started":"2025-07-03T10:14:44.196185Z","shell.execute_reply":"2025-07-03T10:14:44.213880Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 将测试数据转换为PyTorch张量（仍在CPU上）\npre_tensor = torch.tensor(test_df).float()\n\n# 创建Dataset和DataLoader进行分批\npre_dataset = TensorDataset(pre_tensor)\npre_loader = DataLoader(pre_dataset, batch_size=512, shuffle=False)  # 根据显存调整batch_size\n\npredictions = []\ntrained_model.eval()  # 设置模型为评估模式\nwith torch.no_grad():  # 禁用梯度计算节省显存\n    for batch in pre_loader:\n        inputs = batch[0].to(Config.device)  # 仅将当前批次送入GPU\n        \n        # 执行预测\n        batch_pred = trained_model(inputs)\n        \n        # 立即移回CPU并释放GPU显存\n        batch_pred = batch_pred.cpu().numpy()\n        predictions.append(batch_pred)\n\n        # 显式释放不再需要的GPU张量\n        del inputs, batch_pred\n        torch.cuda.empty_cache()  # 清空CUDA缓存\n\n# 合并所有批次结果\ny_pre = np.vstack(predictions)\n\n# 保存结果\nsubmission_df[\"prediction\"] = y_pre\nsubmission_df.to_csv(\"/kaggle/working/submission_denoise_CNN.csv\",index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-03T10:32:23.267501Z","iopub.execute_input":"2025-07-03T10:32:23.267755Z","iopub.status.idle":"2025-07-03T10:32:33.575018Z","shell.execute_reply.started":"2025-07-03T10:32:23.267738Z","shell.execute_reply":"2025-07-03T10:32:33.574463Z"}},"outputs":[],"execution_count":null}]}