{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "6603a8fc-d9da-4037-b845-d9c38bae4ce4", "metadata": {}, "outputs": [], "source": [ "import os\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "from torch.utils.data import DataLoader, Dataset, random_split\n", "from PIL import Image\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import cv2\n", "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 2, "id": "15b9ced8-7282-4f97-a079-f31bf9405145", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "torch.random.manual_seed(0)" ] }, { "cell_type": "code", "execution_count": 3, "id": "7f83e6c7-8207-41b3-908b-6b1fad78ecd5", "metadata": {}, "outputs": [], "source": [ "max_pixel_value = 107.49169921875" ] }, { "cell_type": "code", "execution_count": 4, "id": "c66f2b9f-fcad-4237-abb2-d7f918d74116", "metadata": {}, "outputs": [], "source": [ "class NO2Dataset(Dataset):\n", " \n", " def __init__(self, image_dir, mask_dir):\n", " \n", " self.image_dir = image_dir\n", " self.mask_dir = mask_dir\n", " self.image_filenames = [f for f in os.listdir(image_dir) if f.endswith('.npy')] # 仅加载 .npy 文件\n", " self.mask_filenames = [f for f in os.listdir(mask_dir) if f.endswith('.jpg')] # 仅加载 .jpg 文件\n", " \n", " def __len__(self):\n", " \n", " return len(self.image_filenames)\n", " \n", " def __getitem__(self, idx):\n", " \n", " image_path = os.path.join(self.image_dir, self.image_filenames[idx])\n", " mask_idx = np.random.choice(self.mask_filenames)\n", " mask_path = os.path.join(self.mask_dir, mask_idx)\n", "\n", " # 加载图像数据 (.npy 文件)\n", " image = np.load(image_path).astype(np.float32)[:,:,:1] / max_pixel_value # 形状为 (96, 96, 1)\n", "\n", " # 加载掩码数据 (.jpg 文件)\n", " mask = np.array(Image.open(mask_path).convert('L')).astype(np.float32)\n", "\n", " # 将掩码数据中非0值设为1,0值保持不变\n", " mask = np.where(mask != 0, 1.0, 0.0)\n", "\n", " # 保持掩码数据形状为 (96, 96, 1)\n", " mask = mask[:, :, np.newaxis] # 将形状调整为 (96, 96, 1)\n", "\n", " # 应用掩码\n", " masked_image = image.copy()\n", " masked_image[:, :, 0] = image[:, :, 0] * mask.squeeze() # 遮盖NO2数据\n", "\n", " # cGAN的输入和目标\n", " X = masked_image[:, :, :1] # 形状为 (96, 96, 8)\n", " y = image[:, :, 0:1] # 目标输出为NO2数据,形状为 (96, 96, 1)\n", "\n", " # 转换形状为 (channels, height, width)\n", " X = np.transpose(X, (2, 0, 1)) # 转换为 (1, 96, 96)\n", " y = np.transpose(y, (2, 0, 1)) # 转换为 (1, 96, 96)\n", " mask = np.transpose(mask, (2, 0, 1)) # 转换为 (1, 96, 96)\n", "\n", " return torch.tensor(X, dtype=torch.float32), torch.tensor(y, dtype=torch.float32), torch.tensor(mask, dtype=torch.float32)\n", "\n", "# 实例化数据集和数据加载器\n", "image_dir = './out_mat/96/train/'\n", "mask_dir = './out_mat/96/mask/20/'" ] }, { "cell_type": "code", "execution_count": 5, "id": "e3354304-f6de-44bf-adbf-bbff557a8c93", "metadata": {}, "outputs": [], "source": [ "train_set = NO2Dataset(image_dir, mask_dir)\n", "train_loader = DataLoader(train_set, batch_size=64, shuffle=True, num_workers=8)\n", "val_set = NO2Dataset('./out_mat/96/valid/', mask_dir)\n", "val_loader = DataLoader(val_set, batch_size=64, shuffle=False, num_workers=4)\n", "test_set = NO2Dataset('./out_mat/96/test/', mask_dir)\n", "test_loader = DataLoader(test_set, batch_size=64, shuffle=False, num_workers=4)" ] }, { "cell_type": "code", "execution_count": 6, "id": "70797703-1619-4be7-b965-5506b3d1e775", "metadata": {}, "outputs": [], "source": [ "# 可视化特定特征的函数\n", "def visualize_feature(input_feature,masked_feature, output_feature, title):\n", " plt.figure(figsize=(12, 6))\n", " plt.subplot(1, 3, 1)\n", " plt.imshow(input_feature[0].cpu().numpy(), cmap='RdYlGn_r')\n", " plt.title(title + \" Input\")\n", " plt.subplot(1, 3, 2)\n", " plt.imshow(masked_feature[0].cpu().numpy(), cmap='RdYlGn_r')\n", " plt.title(title + \" Masked\")\n", " plt.subplot(1, 3, 3)\n", " plt.imshow(output_feature[0].detach().cpu().numpy(), cmap='RdYlGn_r')\n", " plt.title(title + \" Recovery\")\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": 7, "id": "645114e8-65a4-4867-b3fe-23395288e855", "metadata": {}, "outputs": [], "source": [ "class Conv(nn.Sequential):\n", " def __init__(self, in_channels, out_channels, kernel_size=3, dilation=1, stride=1, bias=False):\n", " super(Conv, self).__init__(\n", " nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, bias=bias,\n", " dilation=dilation, stride=stride, padding=((stride - 1) + dilation * (kernel_size - 1)) // 2)\n", " )" ] }, { "cell_type": "code", "execution_count": 8, "id": "2af52d0e-b785-4a84-838c-6fcfe2568722", "metadata": {}, "outputs": [], "source": [ "class ConvBNReLU(nn.Sequential):\n", " def __init__(self, in_channels, out_channels, kernel_size=3, dilation=1, stride=1, norm_layer=nn.BatchNorm2d,\n", " bias=False):\n", " super(ConvBNReLU, self).__init__(\n", " nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, bias=bias,\n", " dilation=dilation, stride=stride, padding=((stride - 1) + dilation * (kernel_size - 1)) // 2),\n", " norm_layer(out_channels),\n", " nn.ReLU()\n", " )" ] }, { "cell_type": "code", "execution_count": 9, "id": "31ecf247-e98b-4977-a145-782914a042bd", "metadata": {}, "outputs": [], "source": [ "class SeparableBNReLU(nn.Sequential):\n", " def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, dilation=1, norm_layer=nn.BatchNorm2d):\n", " super(SeparableBNReLU, self).__init__(\n", " nn.Conv2d(in_channels, in_channels, kernel_size=kernel_size, stride=stride, dilation=dilation,\n", " padding=((stride - 1) + dilation * (kernel_size - 1)) // 2, groups=in_channels, bias=False),\n", " # 分离卷积,仅调整空间信息\n", " norm_layer(in_channels), # 对输入通道进行归一化\n", " nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False), # 这里进行升维操作\n", " nn.ReLU6()\n", " )" ] }, { "cell_type": "code", "execution_count": 10, "id": "7827bee2-74f7-4e47-b8c6-e41d5670e8b9", "metadata": {}, "outputs": [], "source": [ "class ResidualBlock(nn.Module):\n", " def __init__(self, in_channels, out_channels, stride=1, downsample=None):\n", " super(ResidualBlock, self).__init__()\n", " self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)\n", " self.bn1 = nn.BatchNorm2d(out_channels)\n", " self.relu = nn.ReLU(inplace=True)\n", " self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, bias=False)\n", " self.bn2 = nn.BatchNorm2d(out_channels)\n", "\n", " # 如果输入和输出通道不一致,进行降采样操作\n", " self.downsample = downsample\n", " if in_channels != out_channels or stride != 1:\n", " self.downsample = nn.Sequential(\n", " nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),\n", " nn.BatchNorm2d(out_channels)\n", " )\n", "\n", " def forward(self, x):\n", " identity = x\n", " if self.downsample is not None:\n", " identity = self.downsample(x)\n", "\n", " out = self.conv1(x)\n", " out = self.bn1(out)\n", " out = self.relu(out)\n", "\n", " out = self.conv2(out)\n", " out = self.bn2(out)\n", "\n", " out += identity\n", " out = self.relu(out)\n", " return out\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "7853bf62-02f5-4917-b950-6fdfe467df4a", "metadata": {}, "outputs": [], "source": [ "class Mlp(nn.Module):\n", " def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.ReLU6, drop=0.):\n", " super().__init__()\n", " out_features = out_features or in_features\n", " hidden_features = hidden_features or in_features\n", " self.fc1 = nn.Conv2d(in_features, hidden_features, 1, 1, 0, bias=True)\n", "\n", " self.act = act_layer()\n", " self.fc2 = nn.Conv2d(hidden_features, out_features, 1, 1, 0, bias=True)\n", " self.drop = nn.Dropout(drop, inplace=True)\n", "\n", " def forward(self, x):\n", " x = self.fc1(x)\n", " x = self.act(x)\n", " x = self.drop(x)\n", " x = self.fc2(x)\n", " x = self.drop(x)\n", " return x" ] }, { "cell_type": "code", "execution_count": 12, "id": "e2375881-a11b-47a7-8f56-2eadb25010b0", "metadata": {}, "outputs": [], "source": [ "class MultiHeadAttentionBlock(nn.Module):\n", " def __init__(self, embed_dim, num_heads, dropout=0.1):\n", " super(MultiHeadAttentionBlock, self).__init__()\n", " self.attention = nn.MultiheadAttention(embed_dim, num_heads, dropout=dropout)\n", " self.norm = nn.LayerNorm(embed_dim)\n", " self.dropout = nn.Dropout(dropout)\n", "\n", " def forward(self, x):\n", " # (B, C, H, W) -> (HW, B, C) for MultiheadAttention compatibility\n", " B, C, H, W = x.shape\n", " x = x.view(B, C, H * W).permute(2, 0, 1) # (B, C, H, W) -> (HW, B, C)\n", "\n", " # Apply multihead attention\n", " attn_output, _ = self.attention(x, x, x)\n", "\n", " # Apply normalization and dropout\n", " attn_output = self.norm(attn_output)\n", " attn_output = self.dropout(attn_output)\n", "\n", " # Reshape back to (B, C, H, W)\n", " attn_output = attn_output.permute(1, 2, 0).view(B, C, H, W)\n", "\n", " return attn_output" ] }, { "cell_type": "code", "execution_count": 13, "id": "82a15d3d-2f8d-42ec-9146-87c8a4abe384", "metadata": {}, "outputs": [], "source": [ "class SpatialAttentionBlock(nn.Module):\n", " def __init__(self):\n", " super(SpatialAttentionBlock, self).__init__()\n", " self.conv = nn.Conv2d(2, 1, kernel_size=7, padding=3, bias=False)\n", "\n", " def forward(self, x): #(B, 64, H, W)\n", " avg_out = torch.mean(x, dim=1, keepdim=True) #(B, 1, H, W)\n", " max_out, _ = torch.max(x, dim=1, keepdim=True)#(B, 1, H, W)\n", " out = torch.cat([avg_out, max_out], dim=1)#(B, 2, H, W)\n", " out = torch.sigmoid(self.conv(out))#(B, 1, H, W)\n", " return x * out #(B, C, H, W)" ] }, { "cell_type": "code", "execution_count": 14, "id": "497bb9f1-1ac5-4d7f-a930-0ea222b9d1d9", "metadata": {}, "outputs": [], "source": [ "class DecoderAttentionBlock(nn.Module):\n", " def __init__(self, in_channels):\n", " super(DecoderAttentionBlock, self).__init__()\n", " self.conv1 = nn.Conv2d(in_channels, in_channels // 2, kernel_size=1)\n", " self.conv2 = nn.Conv2d(in_channels // 2, in_channels, kernel_size=1)\n", " self.spatial_attention = SpatialAttentionBlock()\n", "\n", " def forward(self, x):\n", " # 通道注意力\n", " b, c, h, w = x.size()\n", " avg_pool = F.adaptive_avg_pool2d(x, 1)\n", " max_pool = F.adaptive_max_pool2d(x, 1)\n", "\n", " avg_out = self.conv1(avg_pool)\n", " max_out = self.conv1(max_pool)\n", "\n", " out = avg_out + max_out\n", " out = torch.sigmoid(self.conv2(out))\n", "\n", " # 添加空间注意力\n", " out = x * out\n", " out = self.spatial_attention(out)\n", " return out" ] }, { "cell_type": "code", "execution_count": 15, "id": "15b9d453-d8d9-43b8-aca2-904735fb3a99", "metadata": {}, "outputs": [], "source": [ "class SEBlock(nn.Module):\n", " def __init__(self, in_channels, reduced_dim):\n", " super(SEBlock, self).__init__()\n", " self.se = nn.Sequential(\n", " nn.AdaptiveAvgPool2d(1), # 全局平均池化\n", " nn.Conv2d(in_channels, reduced_dim, kernel_size=1),\n", " nn.ReLU(),\n", " nn.Conv2d(reduced_dim, in_channels, kernel_size=1),\n", " nn.Sigmoid() # 使用Sigmoid是因为我们要对通道进行权重归一化\n", " )\n", "\n", " def forward(self, x):\n", " return x * self.se(x)" ] }, { "cell_type": "code", "execution_count": 16, "id": "a382ed1b-cc88-4f03-95c2-843981ee81f1", "metadata": {}, "outputs": [], "source": [ "def masked_mse_loss(preds, target, mask):\n", " loss = (preds - target) ** 2\n", " loss = loss.mean(dim=-1) # 对每个像素点求平均\n", " loss = (loss * mask).sum() / mask.sum() # 只计算被mask的像素点的损失\n", " return loss" ] }, { "cell_type": "code", "execution_count": 17, "id": "6379adb7-8a87-4dd8-a695-4013a7b37830", "metadata": { "tags": [] }, "outputs": [], "source": [ "# 定义Masked Autoencoder模型\n", "class MaskedAutoencoder(nn.Module):\n", " def __init__(self):\n", " super(MaskedAutoencoder, self).__init__()\n", " self.encoder = nn.Sequential(\n", " Conv(1, 32, kernel_size=3, stride=2),\n", " nn.ReLU(),\n", " SEBlock(32,32),\n", " ConvBNReLU(32, 64, kernel_size=3, stride=2),\n", " ResidualBlock(64,64),\n", " SeparableBNReLU(64, 128, kernel_size=3, stride=2),\n", " MultiHeadAttentionBlock(embed_dim=128, num_heads=4),\n", " SEBlock(128, 128)\n", " )\n", " self.mlp = Mlp(in_features=128, hidden_features=256, out_features=128, act_layer=nn.ReLU6, drop=0.1)\n", " self.decoder = nn.Sequential(\n", " nn.ConvTranspose2d(128, 32, kernel_size=3, stride=2, padding=1, output_padding=1),\n", " nn.ReLU(),\n", " \n", " # DecoderAttentionBlock(32),\n", " nn.ConvTranspose2d(32, 16, kernel_size=3, stride=2, padding=1, output_padding=1),\n", " nn.ReLU(),\n", " \n", " # DecoderAttentionBlock(16),\n", " nn.ReLU(),\n", " \n", " nn.ConvTranspose2d(16, 1, kernel_size=3, stride=2, padding=1, output_padding=1), # 修改为 output_padding=1\n", " nn.Sigmoid()\n", " )\n", "\n", " def forward(self, x):\n", " encoded = self.encoder(x)\n", " decoded = self.decoder(encoded)\n", " return decoded\n", "\n", "# 实例化模型、损失函数和优化器\n", "model = MaskedAutoencoder()\n", "criterion = nn.MSELoss()\n", "optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)" ] }, { "cell_type": "code", "execution_count": 18, "id": "404a8bfb-4976-4cce-b989-c5e401bce0d7", "metadata": {}, "outputs": [], "source": [ "# 训练函数\n", "def train_epoch(model, device, data_loader, criterion, optimizer):\n", " model.train()\n", " running_loss = 0.0\n", " for batch_idx, (X, y, mask) in enumerate(data_loader):\n", " X, y, mask = X.to(device), y.to(device), mask.to(device)\n", " optimizer.zero_grad()\n", " reconstructed = model(X)\n", " # loss = criterion(reconstructed, y)\n", " loss = masked_mse_loss(reconstructed, y, mask)\n", " loss.backward()\n", " optimizer.step()\n", " running_loss += loss.item()\n", " return running_loss / (batch_idx + 1)" ] }, { "cell_type": "code", "execution_count": 19, "id": "94457c6b-4c6e-4aff-946d-fe4c670bfe16", "metadata": {}, "outputs": [], "source": [ "# 评估函数\n", "def evaluate(model, device, data_loader, criterion):\n", " model.eval()\n", " running_loss = 0.0\n", " with torch.no_grad():\n", " for batch_idx, (X, y, mask) in enumerate(data_loader):\n", " X, y, mask = X.to(device), y.to(device), mask.to(device)\n", " reconstructed = model(X)\n", " if batch_idx == 8:\n", " rand_ind = np.random.randint(0, len(y))\n", " # visualize_feature(y[rand_ind], X[rand_ind], reconstructed[rand_ind], title='NO_2')\n", " # loss = criterion(reconstructed, y)\n", " loss = masked_mse_loss(reconstructed, y, mask)\n", " running_loss += loss.item()\n", " return running_loss / (batch_idx + 1)" ] }, { "cell_type": "code", "execution_count": 20, "id": "6094b6c8-8211-4557-9944-7eef977ea9ec", "metadata": {}, "outputs": [], "source": [ "def masked_mae_loss(preds, target, mask):\n", " loss = (preds - target) ** 2\n", " loss = loss.mean(dim=-1) # 对每个像素点求平均\n", " loss = (loss * mask).sum() / mask.sum() # 只计算被mask的像素点的损失\n", " return loss" ] }, { "cell_type": "code", "execution_count": 29, "id": "781f558e-d41c-4721-94fd-564cd6c2b347", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "cuda\n" ] } ], "source": [ "# 数据准备\n", "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", "print(device)" ] }, { "cell_type": "code", "execution_count": 30, "id": "743d1000-561e-4444-8b49-88346c14f28b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1, Train Loss: 0.013549078723781131, Val Loss: 0.014539383435204847\n", "Epoch 2, Train Loss: 0.013641111095966192, Val Loss: 0.014635173200782555\n", "Epoch 3, Train Loss: 0.013503858572290988, Val Loss: 0.01476309893291388\n", "Epoch 4, Train Loss: 0.013455510417970887, Val Loss: 0.014315864057349624\n", "Epoch 5, Train Loss: 0.01339626228704193, Val Loss: 0.01442837900023407\n", "Epoch 6, Train Loss: 0.013295360569035608, Val Loss: 0.015184532503472336\n", "Epoch 12, Train Loss: 0.012901031857793125, Val Loss: 0.013935101566030018\n", "Epoch 13, Train Loss: 0.01295265725158761, Val Loss: 0.013862666924164366\n", "Epoch 14, Train Loss: 0.013010161795149865, Val Loss: 0.013880979492148357\n", "Epoch 15, Train Loss: 0.012936625905940977, Val Loss: 0.013813913021403463\n", "Epoch 16, Train Loss: 0.01287072714926167, Val Loss: 0.01403502803017844\n", "Epoch 17, Train Loss: 0.012832806871214695, Val Loss: 0.014388528165977393\n", "Epoch 18, Train Loss: 0.012794200125992583, Val Loss: 0.01383661480147892\n", "Epoch 19, Train Loss: 0.01294981115208003, Val Loss: 0.01408140508652623\n", "Epoch 20, Train Loss: 0.012662894464583631, Val Loss: 0.01359965718949019\n", "Test Loss: 0.007365767304242279\n" ] } ], "source": [ "model = model.to(device)\n", "\n", "num_epochs = 20\n", "train_losses = list()\n", "val_losses = list()\n", "for epoch in range(num_epochs):\n", " train_loss = train_epoch(model, device, train_loader, criterion, optimizer)\n", " train_losses.append(train_loss)\n", " val_loss = evaluate(model, device, val_loader, criterion)\n", " val_losses.append(val_loss)\n", " print(f'Epoch {epoch+1}, Train Loss: {train_loss}, Val Loss: {val_loss}')\n", "\n", "# 测试模型\n", "test_loss = evaluate(model, device, test_loader, criterion)\n", "print(f'Test Loss: {test_loss}')" ] }, { "cell_type": "code", "execution_count": 31, "id": "cdc0d608-6f0a-43dc-8cc1-8acf68215d18", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj0AAAGdCAYAAAD5ZcJyAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAABwtUlEQVR4nO3dd3gU9f728femE0gBQhJ6J7SQUEMHBaki2EBEAftRrFj5PSqWo1g5qKCo5wh2sACiIIg0EVCkhE7ohJJCTSV9nj+GbBJIQjZtN8n9uq5cGXZnZz7LGnPzrRbDMAxEREREKjknexcgIiIiUh4UekRERKRKUOgRERGRKkGhR0RERKoEhR4RERGpEhR6REREpEpQ6BEREZEqQaFHREREqgQXexfgSLKysjh16hReXl5YLBZ7lyMiIiJFYBgGCQkJ1KtXDyengttzFHpyOXXqFA0bNrR3GSIiIlIMx48fp0GDBgU+r9CTi5eXF2D+pXl7e9u5GhERESmK+Ph4GjZsaP09XhCFnlyyu7S8vb0VekRERCqYqw1N0UBmERERqRIUekRERKRKUOgRERGRKkFjekREpFIzDIOMjAwyMzPtXYoUk7OzMy4uLiVeTkahR0REKq20tDSioqJITk62dylSQp6entStWxc3N7diX0OhR0REKqWsrCyOHDmCs7Mz9erVw83NTQvPVkCGYZCWlsbp06c5cuQILVu2LHQBwsIo9IiISKWUlpZGVlYWDRs2xNPT097lSAlUq1YNV1dXjh07RlpaGh4eHsW6jgYyi4hIpVbcVgFxLKXxOeq/BBEREakSFHpERESkSlDoERERqcSaNGnCjBkzSuVaa9aswWKxcOHChVK5XnnTQGYREREH079/f0JDQ0slrPzzzz9Ur1695EVVAmrpESmJE1tg29f2rkJEqpjsBReLok6dOpq9dolCj0hxGQZ8Nx5+eghObLZ3NSJyFYZhkJyWYZcvwzCKXOfEiRNZu3Yt7733HhaLBYvFwty5c7FYLPz666907twZd3d3/vzzTw4dOsTIkSMJCAigRo0adO3ald9//z3P9S7v3rJYLPz3v//lxhtvxNPTk5YtW7J48eJi/73++OOPtGvXDnd3d5o0acK7776b5/kPP/yQli1b4uHhQUBAALfccov1uR9++IHg4GCqVatG7dq1GThwIElJScWu5WrUvSVSXOePQPwJ8zhqOzToYt96RKRQF9Mzafvicrvce88rg/F0K9qv3Pfee4/9+/fTvn17XnnlFQB2794NwHPPPcc777xDs2bNqFmzJsePH2fYsGG89tpruLu788UXXzBixAgiIiJo1KhRgfd4+eWXeeutt3j77bf54IMPGDduHMeOHaNWrVo2va8tW7YwevRoXnrpJcaMGcOGDRt46KGHqF27NhMnTmTz5s08+uijfPnll/Ts2ZNz586xbt06AKKiohg7dixvvfUWN954IwkJCaxbt86mgGgrhR6R4or8K+f49D771SEilYqPjw9ubm54enoSGBgIwL595v9jXnnlFa677jrrubVq1SIkJMT651dffZWFCxeyePFiHn744QLvMXHiRMaOHQvA66+/zvvvv8+mTZsYMmSITbVOnz6dAQMG8MILLwDQqlUr9uzZw9tvv83EiROJjIykevXqXH/99Xh5edG4cWM6duwImKEnIyODm266icaNGwMQHBxs0/1tpdAjUlyRG3OOFXpEHF41V2f2vDLYbvcuDV265G1RTkxM5KWXXmLJkiXWEHHx4kUiIyMLvU6HDh2sx9WrV8fb25vY2Fib69m7dy8jR47M81ivXr2YMWMGmZmZXHfddTRu3JhmzZoxZMgQhgwZYu1WCwkJYcCAAQQHBzN48GAGDRrELbfcQs2aNW2uo6g0pkekuI7lCj2xCj0ijs5iseDp5mKXr9La8+vyWVhPPfUUCxcu5PXXX2fdunWEh4cTHBxMWlpaoddxdXW94u8mKyurVGrMzcvLi61bt/Ltt99St25dXnzxRUJCQrhw4QLOzs6sWLGCX3/9lbZt2/LBBx8QFBTEkSNHSr2ObAo9IsWRdAbOHsj151hIPme/ekSkUnFzcyMzM/Oq561fv56JEydy4403EhwcTGBgIEePHi37Ai9p06YN69evv6KmVq1a4exstm65uLgwcOBA3nrrLXbs2MHRo0dZtWoVYIatXr168fLLL7Nt2zbc3NxYuHBhmdWr7i2R4sgez1OnDaQlQVwkxO6FJr3sW5eIVApNmjTh77//5ujRo9SoUaPAVpiWLVuyYMECRowYgcVi4YUXXiiTFpuCPPnkk3Tt2pVXX32VMWPGsHHjRmbOnMmHH34IwC+//MLhw4fp27cvNWvWZOnSpWRlZREUFMTff//NypUrGTRoEP7+/vz9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qlUr/vrLbA1/6aWX2L17N8OHD2fVqlW0bduWhQsXlktdRVFhdlm///77rcfBwcHUrVuXAQMGcOjQoWKnyClTpuRphYqPj1fwkSulJUHUdvP4qqEnCA4sh9MRZV+XiFRaHh4e3HTTTXz99dccPHiQoKAgOnUy1wdbv349EydO5MYbbwQgMTHxqsNDiqpNmzbMnTuXpKQka2vP+vXrcXJyIigoyHpex44d6dixI1OmTKFHjx588803dO9utoS3atWKVq1a8cQTTzB27FjmzJljrdXebGrp8fPzw9nZmZiYmDyPx8TEFDjWJjAw0KbziyoszBxMevDgwULvk/1cftzd3fH29s7zJXKFE5vByATvBuB7lVCsGVwiUkrGjRtnHTOb3coD5jiZBQsWEB4ezvbt27n99tuvmOlVknt6eHgwYcIEdu3axerVq3nkkUe48847CQgI4MiRI0yZMoWNGzdy7NgxfvvtNw4cOECbNm24ePEiDz/8MGvWrOHYsWOsX7+ef/75J8+YH3uzKfS4ubnRuXNnVq5caX0sKyuLlStX0qNH/v8C7tGjR57zAVasWFHg+UWVPa09ux+xR48e7Ny5k9jY2Dz38fb2pm3btiW6l1RxV1ufJzfrWj3q3hKRkrn22mupVasWERER3H777dbHp0+fTs2aNenZsycjRoxg8ODB1lagkvL09GT58uWcO3eOrl27cssttzBgwABmzpxpfX7fvn3cfPPNtGrVivvvv59JkybxwAMP4OzszNmzZxk/fjytWrVi9OjRDB06lJdffrlUaisVho3mzZtnuLu7G3PnzjX27Nlj3H///Yavr68RHR1tGIZh3HnnncZzzz1nPX/9+vWGi4uL8c477xh79+41pk6dari6uho7d+60nnP27Flj27ZtxpIlSwzAmDdvnrFt2zYjKirKMAzDOHjwoPHKK68YmzdvNo4cOWL89NNPRrNmzYy+fftar5GRkWG0b9/eGDRokBEeHm4sW7bMqFOnjjFlypQiv7e4uDgDMOLi4mz9a5HK7PMbDGOqt2Fs+vTq56YmmudO9TaMxNNlX5uIFOjixYvGnj17jIsXL9q7FCkFhX2eRf39bfPihGPGjOGdd97hxRdfJDQ0lPDwcJYtW2YdNBwZGUlUVJT1/J49e/LNN9/wySefEBISwg8//MCiRYusa/QALF68mI4dOzJ8+HAA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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tr_ind = list(range(len(train_losses)))\n", "val_ind = list(range(len(val_losses)))\n", "plt.plot(train_losses, label='train_loss')\n", "plt.plot(val_losses, label='val_loss')\n", "plt.legend(loc='best')" ] }, { "cell_type": "code", "execution_count": 32, "id": "1f48acd7-70e8-46db-9148-6a2df3153f08", "metadata": {}, "outputs": [], "source": [ "from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_percentage_error, mean_absolute_error" ] }, { "cell_type": "code", "execution_count": 33, "id": "313fa420-c856-4db1-80ae-b543e1fb73ef", "metadata": {}, "outputs": [], "source": [ "eva_list = list()\n", "model = model.to('cpu')\n", "with torch.no_grad():\n", " for batch_idx, (X, y, mask) in enumerate(test_loader):\n", " X, y, mask = X.to(device), y.to(device), mask.to(device)\n", " mask_rev = (torch.squeeze(mask, dim=1)==0) * 1 # mask取反获得修复区域\n", " reconstructed = model(X)\n", " rev_data = y * max_pixel_value\n", " rev_recon = reconstructed * max_pixel_value\n", " # todo: 这里需要只评估修补出来的模块\n", " data_label = torch.squeeze(rev_data, dim=1) * mask_rev\n", " data_label = data_label[mask_rev==1]\n", " recon_no2 = torch.squeeze(rev_recon, dim=1) * mask_rev\n", " recon_no2 = recon_no2[mask_rev==1]\n", " mae = mean_absolute_error(data_label, recon_no2)\n", " rmse = np.sqrt(mean_squared_error(data_label, recon_no2))\n", " mape = mean_absolute_percentage_error(data_label, recon_no2)\n", " r2 = r2_score(data_label, recon_no2)\n", " eva_list.append([mae, rmse, mape, r2])" ] }, { "cell_type": "code", "execution_count": 34, "id": "5c6d5e5a-90f6-4e9a-882f-c2f160b0cb15", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
maermsemaper2
count75.00000075.00000075.00000075.000000
mean1.2969062.0223620.1676940.904339
std0.0757610.1370410.0131710.010395
min1.1212841.7162750.1436670.875878
25%1.2383781.9179070.1564290.898060
50%1.2871932.0118280.1666790.904941
75%1.3530452.1024090.1769960.911137
max1.4460462.4145320.2021420.924070
\n", "
" ], "text/plain": [ " mae rmse mape r2\n", "count 75.000000 75.000000 75.000000 75.000000\n", "mean 1.296906 2.022362 0.167694 0.904339\n", "std 0.075761 0.137041 0.013171 0.010395\n", "min 1.121284 1.716275 0.143667 0.875878\n", "25% 1.238378 1.917907 0.156429 0.898060\n", "50% 1.287193 2.011828 0.166679 0.904941\n", "75% 1.353045 2.102409 0.176996 0.911137\n", "max 1.446046 2.414532 0.202142 0.924070" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.DataFrame(eva_list, columns=['mae', 'rmse', 'mape', 'r2']).describe()" ] }, { "cell_type": "code", "execution_count": 35, "id": "1ada99bf-6bea-4e46-a3bd-f62510517c8e", "metadata": {}, "outputs": [], "source": [ "def cal_ioa(y_true, y_pred):\n", " # 计算平均值\n", " mean_observed = np.mean(y_true)\n", " mean_predicted = np.mean(y_pred)\n", "\n", " # 计算IoA\n", " numerator = np.sum((y_true - y_pred) ** 2)\n", " denominator = np.sum((np.abs(y_true - mean_observed) + np.abs(y_pred - mean_predicted)) ** 2)\n", " IoA = 1 - (numerator / denominator)\n", "\n", " return IoA" ] }, { "cell_type": "code", "execution_count": 36, "id": "b4250d45-b430-40a0-ace7-f59d3451aebd", "metadata": {}, "outputs": [], "source": [ "eva_list_frame = list()\n", "device = 'cpu'\n", "model = model.to(device)\n", "with torch.no_grad():\n", " for batch_idx, (X, y, mask) in enumerate(test_loader):\n", " X, y, mask = X.to(device), y.to(device), mask.to(device)\n", " mask_rev = (torch.squeeze(mask, dim=1)==0) * 1 # mask取反获得修复区域\n", " reconstructed = model(X)\n", " rev_data = y * max_pixel_value\n", " rev_recon = reconstructed * max_pixel_value\n", " # todo: 这里需要只评估修补出来的模块\n", " for i, sample in enumerate(rev_data):\n", " used_mask = mask_rev[i]\n", " data_label = sample[0] * used_mask\n", " recon_no2 = rev_recon[i][0] * used_mask\n", " data_label = data_label[used_mask==1]\n", " recon_no2 = recon_no2[used_mask==1]\n", " mae = mean_absolute_error(data_label, recon_no2)\n", " rmse = np.sqrt(mean_squared_error(data_label, recon_no2))\n", " mape = mean_absolute_percentage_error(data_label, recon_no2)\n", " r2 = r2_score(data_label, recon_no2)\n", " ioa = cal_ioa(data_label.detach().numpy(), recon_no2.detach().numpy())\n", " r = np.corrcoef(data_label, recon_no2)[0, 1]\n", " eva_list_frame.append([mae, rmse, mape, r2, ioa, r])" ] }, { "cell_type": "code", "execution_count": 37, "id": "039d0041-4573-4645-aeb0-686eabfe8b6f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
maermsemaper2ioar
count4739.0000004739.0000004739.0000004739.0000004739.0000004739.000000
mean1.3068171.8458190.1668760.6705190.8866460.836323
std0.6236450.9026190.1070250.2407520.1111420.121726
min0.4329910.5683190.050612-1.539424-0.2675690.022258
25%0.8355791.1723220.1133020.5837130.8647560.794922
50%1.1617101.6581950.1433860.7358600.9213410.869860
75%1.6173822.2997310.1850390.8272420.9512850.916741
max5.3382309.9369511.9299860.9832080.9957670.992588
\n", "
" ], "text/plain": [ " mae rmse mape r2 ioa \\\n", "count 4739.000000 4739.000000 4739.000000 4739.000000 4739.000000 \n", "mean 1.306817 1.845819 0.166876 0.670519 0.886646 \n", "std 0.623645 0.902619 0.107025 0.240752 0.111142 \n", "min 0.432991 0.568319 0.050612 -1.539424 -0.267569 \n", "25% 0.835579 1.172322 0.113302 0.583713 0.864756 \n", "50% 1.161710 1.658195 0.143386 0.735860 0.921341 \n", "75% 1.617382 2.299731 0.185039 0.827242 0.951285 \n", "max 5.338230 9.936951 1.929986 0.983208 0.995767 \n", "\n", " r \n", "count 4739.000000 \n", "mean 0.836323 \n", "std 0.121726 \n", "min 0.022258 \n", "25% 0.794922 \n", "50% 0.869860 \n", "75% 0.916741 \n", "max 0.992588 " ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.DataFrame(eva_list_frame, columns=['mae', 'rmse', 'mape', 'r2', 'ioa', 'r']).describe()" ] }, { "cell_type": "code", "execution_count": null, "id": "83c7e465-bbd0-4c56-8cb4-9d1122fe695f", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.16" } }, "nbformat": 4, "nbformat_minor": 5 }