983 lines
72 KiB
Plaintext
983 lines
72 KiB
Plaintext
{
|
||
"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": [
|
||
"<torch._C.Generator at 0x7fc9d487f810>"
|
||
]
|
||
},
|
||
"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": [
|
||
"<matplotlib.legend.Legend at 0x7fc8e0717100>"
|
||
]
|
||
},
|
||
"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+HAAbrvtNjp27Gid0u7m5sbvv//OoEGDaN26NU8++SQ333wzP//8s/Uazs7O/PLLLzg7O9OjRw/uuOMOxo8fn2ddHxGbZWbA8X/M46uN5wFzwKL24BIRcUgWw7BhmchKLj4+Hh8fH+Li4jS+R0wnt8Kn14CHDzxzFJyK8O+Er0ebg5mHvQPdCl5sU0TKVkpKCkeOHKFp06Z4eOSzX14V8PXXX/PAAw/k+1zjxo3ZvXt3OVdUfIV9nkX9/V1hZm+J2EX2JqMNuxct8AD4t740g0stPSJiXzfccIN14s/lHGml5PKi0CNSGFsGMWfLXqtH09ZFxM68vLzw8vKydxkOQ7usixTEMHJaeooynidb9h5cmsEl4hA0iqNyKI3PUaFHpCDnDkNSLDi7Qb0rtzEpUHboST4DSWfKpjYRuars7pvk5GQ7VyKlIftzLEm3nLq3RAqS3bVVvzO42jAI0q06+DaGC8fM1p6mfcqmPhEplLOzM76+vtb12zw9Pa27gUvFYRgGycnJxMbG4uvri7Ozc7GvpdAjUpDijOfJVqe1GXpO71PoEbGj7BX5cy9cKxWTr69viXdzUOgRKcixIm4ymh/N4BJxCBaLhbp16+Lv7096erq9y5FicnV1LVELTzaFHpH8JMbCuUOABRp2s/311t3WFXpEHIGzs3Op/NKUik0DmUXykz1ry78tVKtp++v9L+3BdVozuEREHIVCj0h+SjKeB8Cvlfk9+axmcImIOAiFHpH8ZIeexj2L9/rsGVyg9XpERByEQo/I5VITIWqHeVzclh4A/+yVmTWuR0TEESj0iFzu5GYwMsGnIfg0KP516lwa16OWHhERh6DQI3K5YyUcz5MtO/RoDy4REYeg0CNyucgSrM+Tm2ZwiYg4FIUekdwy0+HEZvO4pKHHLwiwmDO4Ek+XuDQRESkZhR6R3KJ3QHoSePjmdE8Vl5sn1Lw0g0utPSIidqfQI5Jb9qKEjbqDUyn8eGhcj4iIw1DoEcmtpIsSXk4zuEREHIZCj0g2w8jV0lPC8TzZtFaPiIjDUOgRyXb2ECSdBmd3qNexdK5p7d5S6BERsTeFHpFs2V1b9TuDi3vpXNOvFZrBJSLiGBR6RLLlHsRcWjSDS0TEYSj0yNWd3Aoxe+xdRdmL3GB+L63xPNnqXBrXE6suLhERe1LokYKlJcMvT8Cn18B/B0LSWXtXVHYSYuDcYcACDbuV7rXrBJnfNa5HRMSuFHokf9E74ZP+sPkz88/pSbD9W7uWVKaOX+raCmgH1XxL99qawSUi4hAUeiQvw4C/PoJPr4UzEVAjADreaT63Za75fGVUWpuM5if3Wj2V9e9PRKQCcLF3AeJAEmNh0YNw8Hfzz62GwsiZ5kymXQvg7AE4tgGa9LJvnWWhtDYZzU/2DK6L58wp8TX8S/8eIiJyVWrpEdOBFfBRTzPwuHjAsHdg7LdQ3Q/cvSD4FvO8LXPtWmaZSE0w99yCsgk9eWZwqYtLRMReFHqquvQU+PU5+PoWsxXCvy3ctxq63QcWS855nSea3/f8BMnn7FJqmTmxGYws8GkEPvXL5h6awSUiYncKPVVZ7D747wD4+yPzz90eMANPQNsrz63XEQI7QGYqbJ9XvnWWtdLebys//tkrM2utHhERe1HoqYoMA/75H3zSD2J2gacf3P4dDHsLXD3yf43FktPaU9kGNGeHnsZl0LWVTS09IiJ2p9BT1SSdhXnjYMlkyEiB5tfCgxug1eCrvzb4VnD1NGd1Za9eXNFlppvdW1A243myWdfq0QwuERF7UeipSg6vMQcrRywBZzcY/DqM+xG8Aor2eg9vaH+zeVxZBjRH7YD0ZPDwBb+gsruPdQbXeXPslIiIlDuFnqogIw1WvAhfjILEaPMX8L2/Q49J4GTjfwKd7zK/715YOQY0556qbuvfhS3cPKFmE/M4VuN6RETsQaGnsjtzEP53Hax/DzDM0HL/WqgbUrzr1e8EAcHmgOYd35VqqXZRHoOYs2llZhERu1LoqawMA7Z+CR/3hahwqFYTxnwFI2aYrQ7FZbFA5wnmcUUf0GwYuXZWL8PxPNm0B5eIiF0p9FRGF8/DD3fB4ofNPbOa9IF/rYc2I0rn+h1Gg0s1c1Du8U2lc017OHsQks+YizHWCy37+2kGl4iIXSn0VDbHNsDsPuaYGycXGDAVxv9UuovuefhUjgHN2V1b9TubW22Utdxr9VTkFjIRkQpKoaeyyMyAVa/B3OEQdxxqNYN7foM+k8HJufTvl71mz+4FZstSRWTt2iqH8TygGVwiInam0FMZnD0Ec4bCH2+Z2ymEjoMH/jBbMMpKgy7mlhUZKbDj+7K7T1k6tsH8Xh7jeQBcq2kGl4iIHSn0VGRJZ+DXZ2FWGJzYBO4+cPP/YNSH5iahZamir9CcEA3njwAWaNit/O6rGVwiInaj0FMRpSbCmjfhvRD4ezZkpUPzAfCvdTm7oZeHDqPNQcCxu3NWNa4osru2AtqbY5TKS51L43rU0iMiUu5c7F2A2CAjDbZ+DmvfzBkTUjcUrnsZmvUv/3qq1YR2N8L2b2HrXGjYtfxrKK7yXJ8nt+zQczqifO8rIiJq6akQsrJg148wqxssfcoMPLWawS1zzF3R7RF4smV3ce1aAClx9qvDVuWxyWh+NINLRMRu1NLj6A6tht+nQtR288/V/aH/s9BpAji72rc2gIZhZuvF6X2w83voeq+9K7q61ASI3mkeNyznlh6/VmBxMmdwJcYWfd8zEREpMbX0OKpT4eZeWV+OMgOPmxdc8zw8us0MFo4QeCDvgObNcytG68WJf8xZbr6NSnf9oqLIPYPrtMb1iIiUJ4UeR3PuMPxwN3zSDw6vBidXCHsQHguHfk+Dew17V3ilDmPA2R1idsKprfau5uqO5dpk1B40rkdExC4UehxFYiwsfRpmdjXH72Axw8Qjm2HoG1Ddz94VFsyzFrQbZR5XhBWaIx0k9GgGl4hIuVLosbfUBFg9Dd4LhU2fQFYGtBhoLi540yc5XSGOLruLa+ePkBJv11IKlZmeM73eXqFHa/WUjuP/wL6l9q5CRCoQDWS2l4w0s1Vk7ZvmppcA9TqZ08+b9rVracXSqIc5SPfMftj1A3S5294V5S9qO2RcNKfb+7WyTw25W3oMwxwXJbY5fxQ+v95cEfyhv3KCpIhIIdTSU96ysmDnDzCrK/z6tBl4ajWHWz+H+1ZVzMADV67Q7Khyd2052ek/f7+W5gyulAtmt6bYxjBg6TNm4AE48Jt96xGRCkOhpzwdWmUOUP7xHvNfqjUC4Pr/wKS/zTExFf1f/CFjwdnNbE05tc3e1eSvvDcZzY9mcJVMxFI4sDznzwd/t18tIlKhKPSUh5Nb4fMb4MsbIXqHOf382kvTz7vc7TjTz0vKsxa0HWkeO2Jrj2HYfxBztjqXumNiNa7HJmlJ8Otz5nHbUeb3YxvNrVlERK5Coac8/D0bjqw1W0G6T4LHtkPfp8Gtur0rK33WAc0/mIO0HcmZA5B81twvrG6ofWuxrsys0GOTP96BuEjwaWhurFuzibn33NF19q5MRCoAhZ7ycM3/g5Db4eHNMOR1qF7b3hWVnca9oHYLSEu8NPXegWS38tTvAi5u9q2ljkKPzU7vhw0fmMdD3jD/0dBioPlndXGJSBEUK/TMmjWLJk2a4OHhQVhYGJs2bSr0/O+//57WrVvj4eFBcHAwS5fmnWa6YMECBg0aRO3atbFYLISHhxd4LcMwGDp0KBaLhUWLFuV5zmKxXPE1b9684rzF0lWzMdz4kfm9snPkAc2OMJ4n2+UzuKRwhgFLnzRbdVoOhtbDzcezQ8+BFfp7FJGrsjn0zJ8/n8mTJzN16lS2bt1KSEgIgwcPJjY2/1koGzZsYOzYsdxzzz1s27aNUaNGMWrUKHbt2mU9Jykpid69e/Pmm29e9f4zZszAUsiA3zlz5hAVFWX9GjVqlK1vUUoq5HazK+/UNnM7DUcRucH8bu/xPJCzB1fKBUiMsXc1jm/Xj3DkD7NrcuibOYP+m/Qx/1u7cAzOHrJvjSLi8GwOPdOnT+e+++7jrrvuom3btsyePRtPT08+++yzfM9/7733GDJkCE8//TRt2rTh1VdfpVOnTsycOdN6zp133smLL77IwIEDC713eHg47777boH3AvD19SUwMND65eHhYetblJKqXhvajDCPt35u31qyxUeZM+YsTtCwm72rAVcPqNnUPFYXV+FS4mD5/5nHfZ6EWk1znnOvkRNi1cUlIldhU+hJS0tjy5YtecKJk5MTAwcOZOPGjfm+ZuPGjVeEmcGDBxd4fkGSk5O5/fbbmTVrFoGBgQWeN2nSJPz8/OjWrRufffYZRiFN3qmpqcTHx+f5klKS3cW143vHmFlz/FLXVkA78PC2by3ZrF1cCj2FWj3NbA2r1Rx6Pnrl8xrXIyJFZFPoOXPmDJmZmQQEBOR5PCAggOjo6HxfEx0dbdP5BXniiSfo2bMnI0eOLPCcV155he+++44VK1Zw880389BDD/HBBx8UeP60adPw8fGxfjVs2NCmmqQQTfpArWaQlgC7F9i7GvtvMpof6wwurdVToKgdsOlj83jY22YL2eWyQ8/RPyH9YvnVJiIVToWYvbV48WJWrVrFjBkzCj3vhRdeoFevXnTs2JFnn32WZ555hrfffrvA86dMmUJcXJz16/jx46VceRVmsUCnCeaxIwxodpT1eXLTWj2Fy8qCJU+CkWWuydNiQP7n+bcBr3rm9iLHNpRriSJSsdgUevz8/HB2diYmJu/Ay5iYmAK7nAIDA206Pz+rVq3i0KFD+Pr64uLigouLuWXYzTffTP/+/Qt8XVhYGCdOnCA1NTXf593d3fH29s7zJaUodBw4ucLJLea/2O0lJR5iLg2cd4SZW9nqBJnfT+/TzKP8hH8FJzaBWw0YMq3g8yyWnEB0cGX51CYiFZJNocfNzY3OnTuzcmXO/1iysrJYuXIlPXrk/y/oHj165DkfYMWKFQWen5/nnnuOHTt2EB4ebv0C+M9//sOcOXMKfF14eDg1a9bE3d29yPeSUlSjTs7UYnsOaD7xj9la4NsYvOvZr47LaQZXwZLPwYqp5nH/567+uWlcj4gUgc27rE+ePJkJEybQpUsXunXrxowZM0hKSuKuu+4CYPz48dSvX59p08x/mT322GP069ePd999l+HDhzNv3jw2b97MJ598Yr3muXPniIyM5NSpUwBEREQA5JmFlV/LUKNGjWja1JzJ8fPPPxMTE0P37t3x8PBgxYoVvP766zz11FO2vkUpTZ0nwp5FsOM7uO5VcPMs/xqyu7Ya9yz/excmewbXuUPmej1eRW/9rPR+fwkungP/thD2r6uf36w/WJzhTARciATfRmVdoYhUQDaP6RkzZgzvvPMOL774IqGhoYSHh7Ns2TLrYOXIyEiioqKs5/fs2ZNvvvmGTz75hJCQEH744QcWLVpE+/btrecsXryYjh07Mny42Spw22230bFjR2bPnl3kulxdXZk1axY9evQgNDSUjz/+mOnTpzN16lRb36KUpqb9zK0CUuNh90L71OBIixJezv/SuB5NW89x/B/Y+oV5PPzdou1NV80XGnQ1j9XFJSIFsBiFzemuYuLj4/Hx8SEuLk7je0rTuumw8mVo0A3uXVG+985IgzcamYNcJ23KGUfjKFa+AuveNVvERrxn72rsLysTPulvbswbcru5knlRrX0bVv8bWl8Pt31dZiWKiOMp6u/vCjF7Syq40HHg5GIOSo3ZXb73jtpuBp5qtcwxNI5GM7jy+ud/ZuDx8IHrXrHttdmDmQ+vNcOuiMhlFHqk7HkFQNAw83hLOQ5ozsqCXT+Yx4165Gxd4Ehyr9VT1RtdE2Jg1avm8YAXzYHwtqgbCp5+5tpQJwrfD1BEqiaFHikf1hWa50FactnfL3YvzBkCf18aF9ZqcNnfszhqt7w0gysOEmxbsLPS+e15c+xXvY7Q+S7bX+/klGvqumZxiciVFHqkfDS7xpxRkxIHe34qu/ukp8Cqf8PsPnD870trvLwJHe8su3uWhPbgMh1ZBzu/AywwfDo4ORfvOpq6LiKFUOiR8uHkVPYrNB9ZBx/1hD/ehqx0aDUUJv0N3f9l3t9RVfUZXBlp5srLAF3uhvqdin+t5tcCFojeqZYzEbmCA/8mkEqn4x3mWirH/zK7n0pL8jn4aRJ8fr255k2NQBj9BYz9FnwalN59yop149EqugfXXx+a6+t4+sGAF0p2rep+UC/UPD60qsSliUjlotAj5ccrEIKGmselMaDZMMxd3Gd2hW1fmY91uQce3gRtRzrmwOX8WFt6Iuxbhz1cOA5r3zSPB70K1WqW/Jrq4hJ7Oh1hLsZa1ScmOCiFHilf2QNUt39bsh2xzx+Fr26GBfdC8hmzteTu5XD9dHO6c0Vi3YOrCs7gWvYcpCdDo54QMrZ0rpkdeg6tMtf9ESkv6Snw5Y2w4D7Yv8ze1Ug+FHqkfDW/BnwamftN7Vls++szM2D9ezCrOxxaCc7ucO3z8MA6x1xxuSiq6gyu/b/Bvl/MLs/h75Rey1z9LuDuAxfPw6ltpXNNkaLY+gXEnzSPdy+yaymSP4UeKV9OztBpvHls64Dmk1vg0/6w4kVzwcEmfeDBDdD3aXBxK+1Ky4+rB9RqZh6friLjetIvwq9Pm8fdH4SAdqV3bWcXaN7fPFYXl5SX9BT4c3rOnyN+1SKZDkihR8pfx3Hmv+4jNxRtHEtqIiybAv8daM7KqVYTRn4IE34GvxZlX295yB7MXFXG9fw5w+yi9Kpn7qJe2jSuR8rblrmQEAXeDaC6P6TGwZE/7F2VXEahR8qfdz1oNcQ8vtqA5ohlMCvMnOFjZEHwaJj0z6XgVEEGKheFPWZwHdsA/wmGb8ea0/3LazzR2UPw53/M4yGvg7tX6d+j+aVFCk9sNmf3iZSl9Is5rTx9n4Q215vHe4vRhS9lSqFH7CN7hebt35jNwpdLiIbvJsC3YyD+BPg2hjt+hJs/tX17goqgvNfqSYyF7ydCXCRELDWn+3/cF7bPK9smecOApU9DZqq5pk7bUWVzH5/64N8OMDR1Xcre5jmQGGOOVwy9A9rcYD6+b4kG0zsYhR6xjxYDzGbgi+dh7885j2dlwebPYGY32LPI7Abr9Rg89FdOl0VlZG3p2Vf2LS5ZmebsksQY875d7gaXauZGnwsfgBnB8Mc7ZdNCsnfxpQHobjCsFAcv58e6JcXKsruHSFpyTstl3yfN8YVNeoOHrzmzNHKjXcuTvBR6xD6cnKHTpa0hsgc0x+6DOUPhlyfM/vB6neD+NeZu226e9qq0fNRuYc7gSi2HGVx/vAOH14CrJ9z6OVz/H5i8B659wVzYMTHa3PhzelvzszhzoHTum5oIv14av9PrcajdvHSuW5Dc43qyssr2XlJ1bf4MkmLNbXZCx5mPObtC6+HmcXFmqUqZUegR++l4h/mL/tif5jYEs3ubqzW7Vjf3y7r3d6jbwd5Vlo/ymsF1eC2smWYeD5+es8u7Zy3o+xQ8vhNu/BgCO5gz5DZ/BjO7wNejzaBUklaotW9Cwimzq7LP5BK/latq1N38bykpFmJ2lf39pOpJS4L1M8zjvk+bYSdbdhfX3p8Vuh2IQo/Yj08DaDnIPP7nv5f2yxqSa7+sYm46WVHl7uIqCwkx8OO9gGEGztB8FgN0cYOQ2+CBP2DCLxA0DLDAgeXwxUgzmG77GjJSbbt37F5zMDrAsLfBtVpJ383VubhD077msWZxSVn453+QdBpqNrlycc1m/c0NjxNOwamt9qhO8qHQI/bV/UGztadGoNnVMnYe+Da0d1X2YR3MXAYtPVmZ5urVSbHg3xaGvl34+RYLNO1j7l/2yBboep/ZHRazC356CP7THta+BUlnrn5vwzBb8rIyoPX10Gpw6bynotC4HikraUnmQqlwZSsPmK232f+t7/mpfGuTAin0iH016w+PbIVHt0K7UZVrGrqtynKtnj/eNtcMca1uhktbxkjVbm6umDx5Dwx8yVxbJykWVr8G/2kHix8tvHVqx3w4tt4MTUOmlfit2CR7XM/xvyAlvnzvLZXbpk/Ngco1m0KH2/I/J3cXV1XbYsZBKfSI/dVqCm7V7V2F/ZXVDK7Da2DNG+bx9f+BOq2Kd51qNaH3E/D4Drj5f1CvI2SkwNbP4cMw+PIms0Uld+0XL8Bvz5vHfZ82B3uWp1pNoVZzs5VJC8VJaUlNhA3vm8f9njFXAc9Pi4Hg4gHnj2hcmYNQ6BFxFH4tzSn6qXHmyq6lISEGfrwPMMztP0LGlPyazq4QfAvctxruWgZtRgAWcyr6VzfBhz3MPYjSU2DVv80xD36toMfDJb93cWh1Ziltmz6B5LNmoA4eXfB57jVy/vvTLC6HoNAj4ihc3HNmcJXGysxZmfDjPZfG8bSDoW+V/Jq5WSzQuAeM+Qoe3QZhD5oDN0/vhcWPmF1f//zXPHf4u/bbH80aelaqi0FKLjWhaK082dqMML/nXo9M7EahR8SR1Akyv5fGuJ61b8LRdZfG8cwt2xlTtZrC0DfMcT+D/g0+Dc3xDhgQfGvOLCp7aNILnN3N1afP7LdfHVI5/P2xuahq7RbQ/parn99qCDi5mv8YKK01r6TYFHpEHElpzeA6tMqcXQUwYkbxx/HYysMHej4Cj4bDLXPMMUDDrjJTrKy5VYfGPc1jdXFJSaTEw4YPzOO+RWjlAajmC836mcfai8vuFHpEHElprNUTH5VrHM8E6FDImIOy4uwC7W8yZ3tVq1n+979cy+vM7wo9UhJ/fwwpF6B2S3NcW1Fld3FpXI/dKfSIOBJrS09E8cafZGaYCxAmn4GA9jD0zdKtr6LKHtdzdL25V5KIrVLiYOOlVp5+z9q2eGrQcHM9sqhwOH+sTMqTolHoEXEktVuUbAbX2jfMbT3capjr8ZTHyscVgV8rc5xRZqq5ZpCIrf6abQYfvyCzFdMWNepAo0tdrPt+Kf3apMgUekQcSUlmcB1caW4mCjDiPfBrUbq1VWQWS67VmdXFJTa6eAE2zjKP+z1TvC1y2l5aqFBdXHal0CPiaLI3AT1tw7ie+ChYcD9gQOe7bBtvUFVovR4prr8+Mltf67SGdjcW7xqtrze/H/8bEqJLrzaxiUKPiKOpY2Poycww1+NJPgMBwTDkjbKrrSJr2hecXODsQTh3xN7VSEVx8XzOZrm2juXJzac+1O8CGOrisiOFHhFHY+sMrjWvm+NU3GrA6M/NjQ7lSh4+0DDMPD6kDUiliDZ+CKnx5ka9bUeV7Frq4rI7hR4RR2OdwVWEPbgO/g7r3jWPb3jf3BxUCqZd18UWyefMri241MpTwl+Z2VPXj/5pXlvKnUKPiKOxzuCKh/hTBZ8Xf+rSOB6gyz3Q/ubyqa8iyx7Xc3gtZKTZtxZxfBtnQVqCufxD9o7pJVGrmdkFbWRCxNKSX09sptAj4mhyz+AqaFxPZgb8cI+56WFgBxj8evnVV5EFBEN1f0hPgsiN9q5GHFnyOfh7tnlcGq082bK7uLQXl10o9Ig4oqvN4Fr9GkRuADevS/tqaRxPkTg5aeq6FM2GDyAt0QzK2TOvSkN2F9ehVea2FlKuFHpEHFGdS+N68lur58AK+HO6eTzyA43jsVXuXddF8pN0FjZ9Yh73f670WnnAnKhQuyVkpsGB30rvulIkCj0ijqiglp64kznjeLreV/w1Q6qy5tcCFojdXfiYKam6NrxvtvIEdoDWw0v32hZLri4uzeIqbwo9Io7IulZPrj24MtPhh7vh4jmoGwKDX7NffRWZZy2o39k8VmuPXC7pDGz61DzuP8UMKaUtu4vrwArtBVfOFHpEHFF+M7hW/RuO/wXu3uY4Hhd3u5ZYoWl1ZinI+vfMge51QyFoaNnco24o+DSC9GRzbI+UG4UeEUfk4p4zVuf0Xti/HNbPMP88cmbO7C4pHuvU9dXmTDgRgMTT8M9/zeOyauUB87rZrT3q4ipXCj0ijiq7i+vgKlj4gHnc7QFoO9J+NVUW9TuBh6+5a/bJLfauRhzF+hlm60u9TtBqcNneK3tcT8QyrRlVjhR6RBxVduj5a5a5/0/dUBj0ql1LqjScnC8NaEZdXGJKiIF//mcel2UrT7YG3aBGgLmR6ZE/yvZeYqXQI+KosmdwAbj7aBxPadO4Hslt/XuQcdHcFLTldWV/PyennPV/9v5U9vcTQKFHxHH5t8s5HjkTajW1Xy2VUfYihae2mTN2pOpKiIbN5djKky27i2vfEsjKLJ97VnEKPSKOqk4QDHgRRryf8z9HKT1egeZquxiaQVPV/TkDMlKgQdecMFweGveGajXN7WSObSi/+1ZhCj0ijspigT5PQucJ9q6k8tKWFBIfBZs/M4/Ls5UHwNkFgi4tfqhZXOVCoUdEqq7ssRsHV0JWln1rEftYPwMyU6FhWM7g9vJkXZ35F/03WA4UekSk6mrQzdy0NfkMRG+3dzVS3uJPweY55nF5t/Jka9bf/G8w4ZT9l09ISza7+mJ227eOMqTQIyJVl4sbNOtnHquLq+r58z9mK0+jHmb4sAcX95w1gew9i2vpU/D71Jz9/SohhR4Rqdqs43q0D1eVEncStsw1j+3VypPN2sX1c85ee+Vtx/cQ/rV5HLMLonbYp44yptAjIlVb80uh5/gmuHjBrqVIOfpzOmSmQeNe0LSvfWtpMRBcqsH5oxC9s/zvf+4w/PKEeezuY37fPq/86ygHCj0iUrXVbAx+rcDIhCNr7V2NlIe4E7D1C/PY3q08AG7Vc1oc9/5cvvfOSIMf7oG0BGjUE0Z9aD6+8zvITC/fWsqBQo+IiFZnrlrWvWu28jTpA0372LsaU5vsLq5ynrq++jU4tdXci+6mT8zxRZ5+kHS6Unb5KvSIiOQe12OvMRVSPi5EwtYvzeP+U+xbS26tBoOTK5zeB6f3l889D60yp+wD3PAB+DYEZ1foMNp8bPu35VNHOVLoERFp3AtcPCD+pPlLRyqnrEz4+XHISjfH8TTpZe+KclTzzZlBVh6tPYmnYcED5nGXu/Ou+h5ym/k9Yqm52XElUqzQM2vWLJo0aYKHhwdhYWFs2rSp0PO///57WrdujYeHB8HBwSxdujTP8wsWLGDQoEHUrl0bi8VCeHh4gdcyDIOhQ4disVhYtGhRnuciIyMZPnw4np6e+Pv78/TTT5ORkVGctygiVYlrNWjS2zw+sMK+tUjZWfkKHFppDhoePM3e1VypzQjze1mHnqwsWPQvSIqFOm1g8Ot5nw/sYO79l5kGuxaUbS3lzObQM3/+fCZPnszUqVPZunUrISEhDB48mNjY2HzP37BhA2PHjuWee+5h27ZtjBo1ilGjRrFr1y7rOUlJSfTu3Zs333zzqvefMWMGlnwGnWVmZjJ8+HDS0tLYsGEDn3/+OXPnzuXFF1+09S2KSFWkcT2V264fc7pyRs6EwPZ2LSdfrYeDxQmitsP5Y2V3n78/Mv87d/GAWz4zQ39uFguEjjWPK9ssLsNG3bp1MyZNmmT9c2ZmplGvXj1j2rRp+Z4/evRoY/jw4XkeCwsLMx544IErzj1y5IgBGNu2bcv3Wtu2bTPq169vREVFGYCxcOFC63NLly41nJycjOjoaOtjH330keHt7W2kpqYW6b3FxcUZgBEXF1ek80WkEjm93zCmehvGK36GkZJg72qkNEXtMIxXA8zP97cX7F1N4eYMN+tc/0HZXP/kVsN4ubZ5j03/Lfi8+CjDeMnXPO/0gbKppRQV9fe3TS09aWlpbNmyhYEDB1ofc3JyYuDAgWzcuDHf12zcuDHP+QCDBw8u8PyCJCcnc/vttzNr1iwCAwPzvU9wcDABAQF57hMfH8/u3fkvqZ2amkp8fHyeLxGpomq3AN/GZpP+0T/tXY2UlqSzMO92yLho7q01YKq9KypcWc7iSk2AH+42xzS1vt4cy1MQr8CcNawq0YBmm0LPmTNnyMzMzBMsAAICAoiOjs73NdHR0TadX5AnnniCnj17MnLkSJvuk/1cfqZNm4aPj4/1q2HDhjbVJCKViMWiLq7KJjMDfphoztiq2dTsynFytndVhWtzvfn9+N+QYNvvyata+oy5EKF3A3O21tXWJ8ru4toxv9JshlohZm8tXryYVatWMWPGjFK97pQpU4iLi7N+HT9+vFSvLyIVjEJP5bLiBTjyB7hWh7HfQrWa9q7o6rzrQYOu5nFpLlS44zvY/o05ZujmT8Gz1tVfEzTMXKE57jgcqxytnzaFHj8/P5ydnYmJicnzeExMTL5dTgCBgYE2nZ+fVatWcejQIXx9fXFxccHFxQWAm2++mf79+xd6n+zn8uPu7o63t3eeLxGpwpr2MddKOX8Ezh6ydzVSEuHfwl+XVhe+cTb4t7FvPbZok2svrtJw7jD8Mtk87vcsNO5ZtNe5VoN2o8zj8MrRxWVT6HFzc6Nz586sXJmzSmNWVhYrV66kR48e+b6mR48eec4HWLFiRYHn5+e5555jx44dhIeHW78A/vOf/zBnzhzrfXbu3JlnFtmKFSvw9vambdu2Rb6XiFRh7l7QqLt5XAlXo60yTm6Fnx8zj/s+k3cNmooge+r60T8h+VzJrnX5NhN9nrLt9aG3m9/3/ARpSSWrxQHY3L01efJkPv30Uz7//HP27t3Lgw8+SFJSEnfddRcA48ePZ8qUnFUuH3vsMZYtW8a7777Lvn37eOmll9i8eTMPP/yw9Zxz584RHh7Onj17AIiIiCA8PNw6FicwMJD27dvn+QJo1KgRTZs2BWDQoEG0bduWO++8k+3bt7N8+XKef/55Jk2ahLu7ezH/ekSkylEXV8WWGAvz74DMVGg1xLFWXS6qWk0hMNjcD27fkpJda/W/c7aZuPlTcHax7fUNw8zxUOlJ5b8vWBmwOfSMGTOGd955hxdffJHQ0FDCw8NZtmyZddBwZGQkUVFR1vN79uzJN998wyeffEJISAg//PADixYtsgYXMMfsdOzYkeHDhwNw22230bFjR2bPnl3kupydnfnll19wdnamR48e3HHHHYwfP55XXnnF1rcoIlVZdug5ug7SU+xbi9gmIw2+G2+urF27pbmXlFOFGLp6pTaXJu2UJGgcXAnr3zOPR84Enwa2X8NigZBLA5rDvyl+LQ7CYhjaaCZbfHw8Pj4+xMXFaXyPSFVlGPBua0iMhjsXQfNr7F2RFNUvk2Hz/8DdG+5bBX4t7V1R8cXugw/DwNkNnj4EHjb+TkqMhY96masud7kHrp9e/FrOH4X3QgALPLGreOGpjBX197eN7VwiIpVc9tT18K/MGS9uNczHLBbAYs5+sWR/d7ryMSy5nrfkf47FydzYsSLMJqootsw1Aw8WuPm/FTvwAPi3Br9WcGY/HPgNgm8p+muzsmDRg7m2mXitZLXUbGLuT3dsvTl9vc+TJbueHSn0iIhcrsUAM/Rs/8b8Kiv1O5sLxLW7Cdw8y+4+lV3k37Dk0gDda/+fuWN5ZdDmBlj3jjmI2JbQ89eHOdtM3Drnym0miiNkrBl6ts+D3pOvvsaPg1L3Vi7q3hIRANKS4bs7L01bN8DIAoNL37NyPWZc9udc511+zuWPket/ve4+5kJwne8y/4UvRRcfBZ/0g8QYMySM/qLC/kK+wqlw8725eppdXEUJxqe2wX+vM1ddvv4/ha+6bIuUeHinlbmy9b2roEHn0rluKVH3lohIcbl5wh0/lu09Ek9D+NewZY45ZuLv2eZX417mL6o2I8BFM08LlZFqztRKjAH/tjDqo8oTeADqhoBvI3NF6UMrc6ayFyT3NhNtRpghurR4eJurRe/83mz9dLDQU1QVdFi7iEgFV6MO9H4cHtkGdyww90KyOJtdCD/eA9PbwoqpcO6IvSt1TIYBSybDyc3mdOzbvgb3GvauqnRZLDkLFe4pwl5cS5/O2WZixPulHwCzZ3Ht+tEMnBWQQo+IiD05OZljiG77Gh7faa4r41UPks/A+hnwfih8eRPs/cXcS0pM//wXtn1lDgq/5TOo1czeFZWN7NCzf5k5Jb8g2+ebG4Pass2ErZr1B6+6cPE87F9e+tcvBwo9IiKOwqc+9H/ODD+3fXNpzSCL2bUxfxzMCIbV0yDupL0rta+jf8Ky58zjgS+bobGyatAVagRCajwcWZv/OecOm61eAP2eK/o2E7ZycoYOo83jCrrzukKPiIijcXaB1sPNcUWPboNej4OnHyScgrVvwIz28O3t5gydSrL7dZFdOA7fTYCsDGh/C/R8xN4VlS0np5yd1/f8dOXz1m0mEs3xYH1t3GbCViGXtqU48BsknSnbe5UBzd7KRbO3RMRhZaSaq/NunpN3x2vfxtDlLgi9wxwnVBoyMyDpNCRE5fqKNmdKZR9XqwkdboV2N4KHT+nc92rSL8JngyFqOwR2gLuXV42p/ofXwhc3gGdteHJ/3q0kfnsBNrxvjmt6cH35LBz4SX9zltiQN6H7v8r+fkVQ1N/fCj25KPSISIVwOsIMP9u/gZQ48zEnV3NjzS53m//iz28Qq2GY4zESovIGmIRTl75fejwp9tIU+yJwqWbOFOo4Dpr0LbttHwwDFtwPO78zf/nfv8ac2VQVZGbAOy3h4jmY8DM07Ws+fnAlfHWTeTzm65wWobL298fw6zPm7LIH/iife16FQk8xKPSISIWSlgy7F5orEZ/ckvO4XytoO8rs8ojPFWgSos2NOIvC4gw1AsArELzrmd+9As1B1l4BEL3LnHJ/Zn/Oa3waQsht5s7cpT2weMNM+O3/mXWN/wma9ind6zu6nyaZA7e73Q/D3s67zUTXe2H4u+VXS9JZeDfInBr/4EYIaFt+9y6AQk8xKPSISIV1Ktxc82fH9+aO2IXxrG3OwvGqeynI1AXvy/5cvY45cLUwhmGGrfCvYeePkBqX81yjnmbrT9uR4O5Vsvd2aLXZomFkwdC3IOyBkl2vItr/G3xzq/nZPLEbvhltjunyb2vuM1Yaqy7bYt442PcL9HwUBr1avvfOh0JPMSj0iEiFlxJvdgGd3ArV/XKFm7o5rTVlsehh+kXYt8TcifvQKqwrTrt6msEndJzZ7WZr99e5I/DpNWa3XOg4GDmrci1AWFQZqfB2C3MWV/ubzbVyXDzMbj7/NuVfz96fzYUhawSaIczZvmsdK/QUg0KPiEgpiDsJO+aZAejswZzHfRubXV8ht5mbWF5NWpK5pULsbnOfsolLwdWjzMp2eD/ea66InK00t5mwVUYavNvKDKN3/HhpeQX7Kervb01ZFxGR0uVT39yJ++HNcPdv0GkCuHnBhWOwZhq8FwJzr4fwb81gkx/DgEUPmYGnuj+M+apqBx7IWagw+7g0t5mwlYubuWQAmJ9jBaGWnlzU0iMiUkbSks0xIOFfm1Ows7u/3GpAu1Fm11WjHjldV+vehZWvmLPSJv4Cjbrbq3LHkZYMH4aBszvcu8JcNsCeTm6BT681u9me2l9+SxfkQ91bxaDQIyJSDi4ch+3zzAB0PtfeYjWbmuHHu545WwkDrp9hrkMkpsx0sxXMxc3elZh1zAqDMxFwwwfQabzdSlHoKQaFHhGRcmQYELnRDD+7F5lT7HPrfBeMmGGPyqSo1k2HlS+bs/Xu/tVuZWhMj4iIODaLxdwnauQss3tk1Gxocmn9nSZ9zOnp4tg6jAEsELnBnGnn4Ow7x0xERATArTqEjjW/ks+Bu7fdp0FLEfjUN3dfP7wadsw3N8x1YGrpERERx+JZS4GnIgkZa37f/q3ZZenAFHpERESk+Npcb87CO38UIv+ydzWFUugRERGR4nOrbu71BuYmuA5MoUdERERKJuQ28/vuReaWJA5KoUdERERKpnEv8Glk7g22b4m9qymQQo+IiIiUjJNTTmvPdsfdlkKhR0REREouO/QcWgXxUfatpQAKPSIiIlJytZtDwzAwsvLuBu9AFHpERESkdDj4mj0KPSIiIlI62t1o7gIfuweittu7miso9IiIiEjpqOYLrYeZx9vn2bWU/Cj0iIiISOkJud38vvN7yEy3by2XUegRERGR0tP8WqjuD8ln4MAKe1eTh0KPiIiIlB5nF+gw2jx2sDV7FHpERESkdGXP4tq/DJLP2beWXBR6REREpHQFtoeAYMhMg90L7F2NlUKPiIiIlL7QS6094Y7TxaXQIyIiIqUv+FawOMPJzXDmgL2rARR6REREpCzU8IcWA81jBxnQrNAjIiIiZSO7i2v7fMjKsm8tKPRUeIdOJ/LYvG0Me28dm444zgh5ERERWg0FDx+IPwFH/7B3NQo9FdXxc8k89f12rpu+lp/CT7EnKp6xn/7Fp38cxnDATd5ERKQKcvWAdjeZxw6wLYVCTwUTHZfC/1u4k2veWcMPW06QZcDANgFc36EumVkGry3dy4NfbSU+xbGW/hYRkSoq9NK2FHsWQ2qiXUtxsevdpcjOJKby0ZpDfPnXMdIyzH7RPi39eHJQEKENfTEMg7BmtXnl590s2x1NREwCH93RidaB3nauXEREqrQGXaFWczh3CPYuzglBdqCWHgcXl5zOW8v20fet1fzvzyOkZWTRtUlN5t3fnS/vCSO0oS8AFouFO7s35vt/9aS+bzWOnEli1Kz1/LjlhH3fgIiIVG0WS84KzXaexWUxNADEKj4+Hh8fH+Li4vD2tm8LSUJKOnPWH+XTdYdJSMkAoEMDH54cFETfln5YLJYCX3s+KY3H54ezdv9pAMZ2a8TUEW3xcHUul9pFRETyuBAJM4IBCzy+E3wblurli/r7Wy09DuZiWiYfrz1E37dWM33FfhJSMmgd6MUnd3bmp0m96NeqTqGBB6BmdTfmTOzKEwNbYbHAt5siuXX2Ro6fSy6ndyEiIpKLbyNo0gcwYIf9BjRrTI+DSM3IZN6m48xcfZDTCakANPOrzuPXteL64Lo4ORUedC7n5GThsYEtCW3ky+PztrHzZBzXf/AnM8aEck1r/7J4CyIiIgULHWd+929ntxLUvZWLPbq30jOzWLD1BO+vPMjJCxcBaFCzGo8NaMmNHevj4lzyxriTFy7y0Ndb2X78AgCPXNuCxwe2wtnGICUiIuKIivr7W6Enl/IMPZlZBj9vP8WM3/dz9KzZ7RTg7c7D17ZkTJeGuLmUbs9jakYmry3ZyxcbjwHQu4Uf790WSu0a7qV6HxERkfKm0FMM5RF6DMNg+e5opq/Yz/4Yc72C2tXdeLB/c+7o3rjMBxv/FH6S537cycX0TAK9PZg1rhOdG9cs03uKiIiUJYWeYijL0GMYBmsiTvPuigh2nYwHwNvDhQf6NWdizyZUdy+/4VUHYhL411dbOHQ6CRcnC/9veBsm9mxy1QHSIiIijkihpxjKKvRsPHSWd36LYMux8wBUd3Pm7t5NubdPM3yquZbafWyRmJrBsz/uYMmOKACGd6jLmzd3oEY5hi8REZHSUNTf3/oNVw5+Cj/JlmPncXdxYkLPJjzQt5ndx9LUcHdh5tiOdGlck9eW7GXJjij2RcUz+47OtAzwsmttIiIiZUEtPbmUVUvPyQsX+WTtISZd0wJ/b49Su25p2XLsPJO+3kp0fAqebs5MuymYkaH17V2WiIhIkah7qxgcaUXm8nY2MZXH5oXz58EzAEzo0Zj/N7xtqc8iExERKW1luiLzrFmzaNKkCR4eHoSFhbFp06ZCz//+++9p3bo1Hh4eBAcHs3Tp0jzPL1iwgEGDBlG7dm0sFgvh4eFXXOOBBx6gefPmVKtWjTp16jBy5Ej27duX5xyLxXLF17x59t/KviKoXcOdz+/uxiPXtgDg843HGP3xRk5dWjtIRESkorM59MyfP5/JkyczdepUtm7dSkhICIMHDyY2Njbf8zds2MDYsWO555572LZtG6NGjWLUqFHs2rXLek5SUhK9e/fmzTffLPC+nTt3Zs6cOezdu5fly5djGAaDBg0iMzMzz3lz5swhKirK+jVq1Chb32KV5exk4clBQXw2sQs+1VwJP36B4e+v449Le3iJiIhUZDZ3b4WFhdG1a1dmzpwJQFZWFg0bNuSRRx7hueeeu+L8MWPGkJSUxC+//GJ9rHv37oSGhjJ79uw85x49epSmTZuybds2QkNDC61jx44dhISEcPDgQZo3b26+GYuFhQsXFjvoVOXurcsdP5fMQ19vZefJOCwWmNCjCUPaB9KpUU11eYmIiEMpk+6ttLQ0tmzZwsCBA3Mu4OTEwIED2bhxY76v2bhxY57zAQYPHlzg+UWRlJTEnDlzaNq0KQ0b5t2pddKkSfj5+dGtWzc+++wzCst0qampxMfH5/kSU8Nannz/rx7cHtYIw4C5G45y2yd/EfrKb9w99x/mrD/CwdiEQv9+RUREHIlNU9bPnDlDZmYmAQEBeR4PCAi4YnxNtujo6HzPj46OtrFU+PDDD3nmmWdISkoiKCiIFStW4ObmZn3+lVde4dprr8XT05PffvuNhx56iMTERB599NF8rzdt2jRefvllm+uoKjxcnXn9xmAGtPbn5+2n+PPgGc4kprFqXyyr9pndmXV9POjdwo/eLf3o3cLP7lPxRUREClKh1ukZN24c1113HVFRUbzzzjuMHj2a9evX4+FhTgN/4YUXrOd27NiRpKQk3n777QJDz5QpU5g8ebL1z/Hx8Ve0HAkMaBPAgDYBZGUZ7ItO4M+Dp1l34AybjpwjKi6F77ec4PstJwBoV8+bPi3r0KelH50b1yzzbTVERESKyqbQ4+fnh7OzMzExMXkej4mJITAwMN/XBAYG2nR+YXx8fPDx8aFly5Z0796dmjVrsnDhQsaOHZvv+WFhYbz66qukpqbi7n5lC4S7u3u+j0v+nJwstK3nTdt63tzftzkp6Zn8c/Qc6w6cYd2BM+yNimf3KfNr9tpDeLg60a1pbfq2NFuCggK8tNWFiIjYjU2hx83Njc6dO7Ny5UrrYOGsrCxWrlzJww8/nO9revTowcqVK3n88cetj61YsYIePXoUu2gw97IyDIPU1NQCzwkPD6dmzZoKNmXEw9X5UqtOHQBOJ6Sy/uAZ/jhwmj8PnCE2IZU/9p+2zv7y93Kndws/+rTyo1cLP/y9HG+hRhERqbxs7t6aPHkyEyZMoEuXLnTr1o0ZM2aQlJTEXXfdBcD48eOpX78+06ZNA+Cxxx6jX79+vPvuuwwfPpx58+axefNmPvnkE+s1z507R2RkJKdOnQIgIiICMFuJAgMDOXz4MPPnz2fQoEHUqVOHEydO8MYbb1CtWjWGDRsGwM8//0xMTAzdu3fHw8ODFStW8Prrr/PUU0+V7G9IiqyOlzujOtZnVMf6GIbB/phE1h0wu8L+PnKW2IRUFmw7yYJtJwFoHehFn5Z+9G5Zh7CmtdQVJiIiZcrm0DNmzBhOnz7Niy++SHR0NKGhoSxbtsw6WDkyMhInp5xJYT179uSbb77h+eef5//+7/9o2bIlixYton379tZzFi9ebA1NALfddhsAU6dO5aWXXsLDw4N169YxY8YMzp8/T0BAAH379mXDhg34+/sD4OrqyqxZs3jiiScwDIMWLVowffp07rvvvuL9zUiJWCwWggK9CAr04t4+zUjNyGTL0fOsO3iGdQdOs+tkPPuiE9gXncCn645Qw92FgW38GRZcl76t6igAiYhIqdM2FLlonZ7yczYxlfWHzvLngdOs3X+amPicbsoa7i4MuBSA+ikAiYjIVWjvrWJQ6LGPrCyDbccvsGRHFL/uiiIqLsX6XHU3Zwa0CWB4BwUgERHJn0JPMSj02F92AFq6M4pfd0ZxKp8ANCy4Lv2DFIBERMSk0FMMCj2OJSvLIPzEpRagfALQtW0CGK4AJCJS5Sn0FINCj+PKDkBLd0SxtMAAFEj/IH8FIBGRKkahpxgUeioGwzAIt44BiubkhYvW5zzdnLm2tT/Xd6irACQiUkUo9BSDQk/Fkx2Alu6MYunOggPQwDYBuDhrd3gRkcpIoacYFHoqNsMw2H4ijqU7o1iyIypPAGrpX4Pnr29Lv1Z17FihiIiUBYWeYlDoqTxyB6DvNh/nQnI6AP2D6vD88Da08Peyc4UiIlJaFHqKQaGncopLTuf9VQf4fMNRMrIMnJ0sjAtrxOMDW1Grupu9yxMRkRJS6CkGhZ7K7ciZJF5fupcVe2IA8PJw4bEBLRnfowluLhrvIyJSUSn0FINCT9Ww4eAZXl2yl71R8QA0qe3JlGFtGNQ2AIvFYufqRETEVgo9xaDQU3VkZhn8sOU4by/fz5lEc9+v7s1q8cL1bWlXz8fO1YmIiC0UeopBoafqSUzN4KM1B/l03RHSMrKwWODWzg14alAQ/t4e9i5PRESKQKGnGBR6qq4T55N5c1kEP28/BZhr/DzUvzn39mmmBQ5FRBycQk8xKPTIlmPnefWXPYQfvwBAfd9qPDMkiBtC6mm8j4iIg1LoKQaFHgFzn6+fd5zizV/3Wff46tjIlxeub0unRjXtXJ2IiFxOoacYFHokt4tpmfx33WE+WnuI5LRMAG4IqcezQ1tT37eanasTEZFsCj3FoNAj+YmJT+Ht5RH8uPUEhgHuLk7c16cZD/ZvTnV3F3uXJyJS5Sn0FINCjxRm18k4XvllD5uOnAOgjpc7Tw8K4ubODXB20ngfERF7UegpBoUeuRrDMFi+O5rXl+4j8lwyAPV8PPD1dMPVxQk3Zwuuzk64uTiZ352dcL30mPl89nOXHnPO/Zj5eJ7XujjRpq4X/l6aPi8iUpCi/v5W27yIDSwWC0Pa1+Wa1v58vuEoH6w8yKm4FOuA57Lg4erEowNacm/vZtouQ0SkBNTSk4taesRWccnp7I6KIz3TID0ji/TMLNIys0jLyDIfy7z8MfPxnOOcc9My8z6WlmkQl5zG0bNmi1JL/xq8dmMw3ZrWsvO7FhFxLOreKgaFHnE0hmGwYOtJXlu6l3NJaYC5YvSUYW20Q7yIyCVF/f2ttnIRB2axWLi5cwNWPdmPsd0aAvD9lhNc++4avvvnOFlZ+jeLiEhRKfSIVAC+nm5Mu6kDPz7Yg9aBXlxITueZH3cw5pONREQn2Ls8EZEKQaFHpALp3LgWPz/Sm/8b1ppqrs78c/Q8w99fxxu/7iM5LcPe5YmIODSFHpEKxtXZifv7Nuf3J/sxqG0AGVkGs9ce4rrpf7Byb4y9yxMRcVgKPSIVVH3fanwyvgufju9Cfd9qnLxwkXs+38wDX27m1IWL9i5PRMThKPSIVHDXtQ1gxeS+PNCvGS5OFpbvjmHg9LV8+sdh0jOz7F2eiIjDUOgRqQQ83VyYMrQNvzzamy6Na5KclslrS/cy4oM/2XLsvL3LExFxCAo9IpVI60BvvnugB2/eHIyvpyv7ohO4+aMNTFmwkwvJafYuT0TErhR6RCoZJycLY7o2YuXkftzSuQEA326KZMC7a1mw9QRaj1REqiqFHpFKqnYNd965NYR593enhX8NzialMfm77dz+6d8cjE20d3kiIuVOoUekkuverDZLH+3DM0OC8HB1YuPhswx97w/e/S2ClPRMe5cnIlJuFHpEqgA3Fyce6t+CFU/045qgOqRnGnyw6iCD/vMHS3dGaTsLEakStOFoLtpwVKoCwzBYvjualxbvITo+BTB3cH/42hYMD66Li7P+LSQiFYt2WS8GhR6pShJTM/jkj8PMWX+EhBRzC4smtT156JoW3NixPq4KPyJSQSj0FINCj1RF8SnpfLnxGP9dd5jzyemAudrzg/2bc2uXBri7ONu5QhGRwin0FINCj1RlSakZfPN3JB//cZgziakABHi780Df5ozt1ohqbgo/IuKYFHqKQaFHBFLSM5n/z3Fmrz1EVJw55sevhhv39mnGHd0bU8Pdxc4ViojkpdBTDAo9IjlSMzL5cctJPlxzkBPnzQ1MfT1dubtXUyb0bIJPNVc7VygiYlLoKQaFHpErpWdm8VP4KT5cfZDDZ5IA8HJ3YULPJtzduym1qruVe00ZmVkcPZvEmcScrTUs2d8tlpzHLJc/l/sqlisey30NJwu0qeutAd0iFYBCTzEo9IgULDPLYMnOKGauOsD+GHNF52quztzRvRH39W2Gv5dHqd/TMAxi4lPZFx1PRHQCEdEJ7ItO4ODpRNIyyn4H+aAAL75/sAfeHmrVEnFkCj3FoNAjcnVZWQa/7Ynhg1UH2H0qHgB3FyfGdmvE/X2bUc+3WrGuG5+Szv5LoSY74ETEJBB3MT3f86u5OlPX1wMLkOd/Ykaeb+Zxrv/NGdbHcr/EuOKxc0lpJKdlMrBNAJ/c2RknpzzNRCLiQBR6ikGhR6ToDMNgTcRp3l91gG2RFwBwdbZwS+eGPNivOY1qe+b7urSMLA6fSbS22mQHnJMXLuZ7vrOThaZ+1QkK8CIo0PxqHehFw5qeZRpEdpy4wC2zN5KWkcXk61rx6ICWZXYvESkZhZ5iUOgRsZ1hGGw4dJb3Vx7g7yPnADOojAqtzx3dG3E2MY2ImOyAE8/h00lkFLDtRaC3hzXUZAec5nVq4OFqn+ny320+zjM/7MBigc8mdOWa1v52qUNECqfQUwwKPSIls+nIOT5YdYB1B84Uep6Xu0ueVpugQG+CArzw8XS8sTPPL9rJV39F4uXhws8P96aJX3V7lyQil1HoKQaFHpHSEX78AjNXHeCvw+doULPaFQGnno9HnllWjiwtI4uxn/7FlmPnCQrwYsFDPamutYpEHIpCTzEo9IhIfmLiU7j+gz85nZDK8A51mTm2Y4UJbSJVQVF/f2sBChGRqwjw9uCjcZ1wcbKwZEcUn647bO+SRKQYFHpERIqgS5NaTB3RFoA3ft3Hn1cZtyQijkehR0SkiO7o3phbOzcgy4BHvt3K8XPJ9i5JRGyg0CMiUkQWi4VXR7WnQwMfzien86+vtpCSnmnvskSkiBR6RERs4OHqzEd3dKZWdTd2n4rn/xbuRPNBRCoGhR4RERvV963GzNs74uxkYcHWk3yx8Zi9SxKRIihW6Jk1axZNmjTBw8ODsLAwNm3aVOj533//Pa1bt8bDw4Pg4GCWLl2a5/kFCxYwaNAgateujcViITw8/IprPPDAAzRv3pxq1apRp04dRo4cyb59+/KcExkZyfDhw/H09MTf35+nn36ajIyM4rxFEZFC9Wzux5ShrQF49Zc9bLq0GrWIOC6bQ8/8+fOZPHkyU6dOZevWrYSEhDB48GBiY2PzPX/Dhg2MHTuWe+65h23btjFq1ChGjRrFrl27rOckJSXRu3dv3nzzzQLv27lzZ+bMmcPevXtZvnw5hmEwaNAgMjPN/vTMzEyGDx9OWloaGzZs4PPPP2fu3Lm8+OKLtr5FEZEiuad3U0aE1CMjy+Chr7cSHZdi75JEpBA2L04YFhZG165dmTlzJgBZWVk0bNiQRx55hOeee+6K88eMGUNSUhK//PKL9bHu3bsTGhrK7Nmz85x79OhRmjZtyrZt2wgNDS20jh07dhASEsLBgwdp3rw5v/76K9dffz2nTp0iICAAgNmzZ/Pss89y+vRp3NzcrvretDihiNgqOS2Dmz7cwL7oBEIb+jL/ge64u9hnr7CydiYxlRV7YlixJwYLcGuXBgxsE4CLs0ZKiH2VyeKEaWlpbNmyhYEDB+ZcwMmJgQMHsnHjxnxfs3HjxjznAwwePLjA84siKSmJOXPm0LRpUxo2bGi9T3BwsDXwZN8nPj6e3bt3F/teIiKF8XRz4eM7O+Pt4UL48Qu8tHiPvUsqVVFxF5mz/ghjPt5It9d+Z8qCnazaF8vKfbH866ut9H5zNe/9foCYeLVyieOzaQOZM2fOkJmZmSdYAAQEBFwxviZbdHR0vudHR0fbWCp8+OGHPPPMMyQlJREUFMSKFSusLTgF3Sf7ufykpqaSmppq/XN8fLzNNYmINK5dnffHduSuuf/w7aZIQhr4cFu3RvYuq9iOnU3i113RLNsVTfjxC3me69DAh8HtAklOy2DepuNEx6fwn9/388GqAwxqF8Ad3RvTo1ltbdMhDqlC7Zo3btw4rrvuOqKionjnnXcYPXo069evx8PDo1jXmzZtGi+//HIpVykiVVH/IH+evK4V7/y2nxd/2k1QoBcdG9W0d1lFYhgGB2IT+XVnNMt2R7M3KucfgBYLdGlckyHt6zK4XQANanpan3t0QEuW7Yrmq7+O8c/R8yzdGc3SndE0r1OdO7o35qZODfCp5mqPtySSL5tCj5+fH87OzsTExOR5PCYmhsDAwHxfExgYaNP5hfHx8cHHx4eWLVvSvXt3atasycKFCxk7diyBgYFXzCLLvm9B95oyZQqTJ0+2/jk+Pt7aXSYiYquH+rdgx4k4ftsTw4NfbeXnR3pTx8vd3mXlyzAMdp2M59ddUSzbHc3h00nW55ydLPRoVpsh7QMZ1DYAf+/8/2Hp7uLMyND6jAytz77oeL766xgLt57k0OkkXv55D28ti2BUx3qMC2tM+/o+5fXWRApkU+hxc3Ojc+fOrFy5klGjRgHmQOaVK1fy8MMP5/uaHj16sHLlSh5//HHrYytWrKBHjx7FLhrMH1jDMKzdUz169OC1114jNjYWf39/6328vb1p27Ztvtdwd3fH3d0x/4ckIhWPk5OFd0eHMGrWeg6dTmLSN1v5+t4wXB1koG9WlsHWyPPWrquTFy5an3NzdqJPSz+GtA9kYJsAala/+uSP3FoHevPvUcE8N7QNC7ed5KuNx4iISeDbTcf5dtNxOjby5c7ujRkWXBcP18o50Fscn82zt+bPn8+ECRP4+OOP6datGzNmzOC7775j3759BAQEMH78eOrXr8+0adMAc8p6v379eOONNxg+fDjz5s3j9ddfZ+vWrbRv3x6Ac+fOERkZyalTp6znBAUFERgYSGBgIIcPH2b+/PkMGjSIOnXqcOLECd544w3Wr1/P3r178ff3JzMzk9DQUOrVq8dbb71FdHQ0d955J/feey+vv/56kd6bZm+JSGk4dDqRkTPXk5iawV29mjB1RDu71ZKemcXfh8+xbHcUy3fHcDohZxxjNVdnrmldhyHt63JNUB28PEqvK8owDP45ep6v/jrGr7uiSM80f9XU9HRldNeGjOvWmEa1Pa9yFZGiKervb5tDD8DMmTN5++23iY6OJjQ0lPfff5+wsDAA+vfvT5MmTZg7d671/O+//57nn3+eo0eP0rJlS9566y2GDRtmfX7u3LncddddV9xn6tSpvPTSS5w6dYp7772XLVu2cP78eQICAujbty8vvvgiQUFB1vOPHTvGgw8+yJo1a6hevToTJkzgjTfewMWlaA1aCj0iUlp+2x3N/V9uAeA/Y0K4sWODcrt3Snom6w+eYdmuaFbsjeFCcrr1OS8PFwa2CWBI+0D6tqxDNbeyb3U5nZDKd5uP883fkdbWJYsF+rWqw53dG9M/yB9nJw18luIr09BTWSn0iEhpeve3CD5YdRB3Fyd+fLBnmY5rOXE+mTURp1m9L5YNh85yMddGqLWquzGorRl0ejb3w83FPt1tmVkGq/fF8uVfx1i7/7T18fq+1bg9rBFjujbEr4aGHNjLgZgEpq/Yz6iO9RnczvZxt/ak0FMMCj0iUpoyswzu+fwf1kScpkHNavz8cG+bx8oUJD0zi81Hz7MmIpbVEbHsj0nM83ygtweD2wUwpH1dujap6XALCB47m8Q3f0cyf/Nxa0uUq7OFYcF1uaN7Y7o0rqlp7+Vo46Gz3P/lZhJSMqjm6syKyX3zzNRzdAo9xaDQIyKlLS45nRtm/cmxs8n0aenH3Lu6FbsrJzY+hTX7T7MmIpZ1+8+QkJqzt6CTBTo3rkn/IH+uCfKnTV2vChEaUtIzWbIjii//OpZnTaAW/jW4uVMDbuxYn0Cf4i1LIkWzaNtJnv5hO+mZBi5OFjKyDAa28efT8V0qxH9DoNBTLAo9IlIW9kXHc+OsDVxMz+Rf/Zrz3KWNSq8mM8tg+4kLrNkXy6qIWHadzLuAaq3qbvRvVYdrWvvTp6Ufvp6l04pkL7tOxvHVX8dYFH6SlPQswBz707uFHzd3asDgdoHlMgapqjAMgw/XHOLt5READG0fyKRrWnDjh+tJzzSYfUdnhrSvGN1cCj3FoNAjImVl8fZTPPrtNgA+HNeJYcF18z3vfFIafxwwx+as3X+a87kGIYO5IvI1Qf5c09qfDvV9cKqEA4ATUtJZujOKH7eezLN7fXU3Z4YF1+Xmzg3o1qRWpXzv5SUjM4sXftrFt5uOA3Bfn6ZMGdoGJycL7yyPYObqgwR6e/D7k/2o4e746xgr9BSDQo+IlKXXluzh03VH8HRzZtGkXrQK8MIwDHafir80Nuc02yLPk5Xr/8peHi70bVWHa4L86deqjsMudlhWIs8ms2DbCRZsPUnkuWTr4w1qVuOmjvW5qVMDmvhVt2OFFU9iagaTvt7K2v2ncbLA1BHtmNCzifX5lPRMBs/4g2Nnk7m7V1NeHJH/WneORKGnGBR6RKQsZWRmMf6zTWw4dJYmtT0Ja1qb1RGxxOZaOwegdaAX/YP8uba1P50a+TrcIGR7MAyDzcfO8+OWEyzZEZVnPFOXxjW5qVMDhneoq20vriImPoW75vzDnqh4PFyd+GBsJ65rG3DFeX/sP834zzbhZIHFD/d2+BW1FXqKQaFHRMra2cRUbpi5Ps9qyJ5uzvRq4cc1Qf70D6pDPd9qdqzQ8aWkZ/Lbnhh+3HKCdQdOW1vG3FycGNQ2gJs7NaBPSz+FxctERCdw15xNnIpLwa+GG/+d0JXQhr4Fnv/It9v4efspOjTwYeFDvRx6LSWFnmJQ6BGR8rDnVDxvLNtHizo1uKZ1Hbo1rYW7iwboFkdMfAqLtp3kx60n8kzbr+PlzqjQetzUqQFt6ur/5xsOnuGBL7eQkJpBszrVmTux21VXxI6NT2HAu2tJSM3g5RvydoE5GoWeYlDoERGpmLLHRv2w5QSLt5/iXFKa9bm2db25uXMDRobWq5KLHy7YeoJnf9xBeqZB1yY1+XR8lyLP9Pty41Fe+Gk3NdxdWPlkPwIK2HzW3hR6ikGhR0Sk4kvLyGLt/tP8uOUEK/fFWPf9cnay0L9VHW7q1ICezWuX2kKRjsowDD5YdZDpK/YDcH2Hurxza4hNG75mZhnc9NEGth+/wPDguswa16msyi0RhZ5iUOgREalcziel8cuOU/yw9STbcy1+CNCktichDX0JbehLSENf2tb1rjQ7wKdnZvH/Fu7ku80nAHigXzOeHdy6WNP8d5+K44aZ68nMMphzV1euCfIv7XJLTKGnGBR6REQqr4OxCfy49STLd0Vz+EzSFc+7OltoU9fbDEENfAlt5EvT2tUr3HpACSnpPPT1VtYdOIOTBV4e2Z47uzcu0TX//cse/vvnERrWqsZvj/dzuEUiFXqKQaFHRKRquJCcxvYTcWw/foHwS1+5xwFl8/ZwISQ7BF1qEXLktZKi41KYOGcT+6ITqObqzMzbOzKgzZVT0m2VlJrBddPXciouhYf6N+eZIUVbVby8KPQUg0KPiEjVZBgGJ85ftAag7ccvsPNkHKkZWVecW9+3GqGNfAm91BrUvp6PQ7R87IuO5645/xAVl4JfDXc+m9iFDg18S+36v+2O5v4vt+DiZGHpY31oFeBVatcuKYWeYlDoERGRbOmZWUREJ1hDUPjxCxw8ncjlvzWdnSwEBXgR0tCXjpdag1r41yjXdW3+PHCGB78yp6Q3r1OduXd1o2Gt0t8l/b4vNrNiTwxdm9Rk/v09HKbrT6GnGBR6RESkMAkp6ew8GZcnCMXEp15xXg13Fzo08KFjI19CG9YktAy7xb7ffJwpC3aSkWXQrWktPr2zCz6eZbMy9akLFxk4fS3JaZm8eXMwY7o2KpP72EqhpxgUekRExFbRcSmEHz9P+PE4wo+fZ8eJOJLTMq84r0HNaoQ29KVjIzMEtatXstlihmHw3soDzPj9AAA3hNTj7Vs7lPlCl/9dd5h/L9mLr6crKyf3o7YDrH2k0FMMCj0iIlJSmVkGB2IT2BZ5gfDIC2w7fp4DsVd2i7k6W2hb19sagjo28qVRLU8slqt3GaVlZPF/C3fywxZzSvpD/Zvz1KCgculuysjMYsTM9eyNiuemTvWZPjq0zO95NQo9xaDQIyIiZSEhJZ0dJ8xusW2R5wk/foEziVfOFqtV3Y3QS2sHdWzkS4cGvldsohqfks5DX23lz4PmlPRXR7VnXFjJpqTbalvkeW76aAOGAd/cF0bP5n7lev/LKfQUg0KPiIiUh+zZYttyhaDdJ+NJy7xytljzOtWtrUEt/Gvw0uLd7ItOwNPNmVm3d+Ka1vZZLPD5RTv56q9ImvlV59fH+9h1/ziFnmJQ6BEREXtJzchkb1SCNQRti7xA5LnkfM+t4+XOnIldaV/fp5yrzBF3MZ2B09dyOiGVJwa24rGBLe1Wi0JPMSj0iIiIIzmbmGpdO2hb5AV2nLhAqwAvZtwWSoOapT8l3VaLt5/i0W+34ebixPLH+9LUr7pd6lDoKQaFHhERkaIzDIPxn21i3YEz9GpRm6/uCSvSQOzSVtTf307lWJOIiIhUIhaLhX+Pao+7ixPrD55l8fZT9i6pUAo9IiIiUmyNa1fnkWtbAPDqL3uIS063c0UFU+gRERGRErm/b3Na+NfgTGIaby7fZ+9yCqTQIyIiIiXi5uLEa6PaA/DN35FsOXbezhXlT6FHRERESiysWW1u6dwAgP+3cCfp+aw5ZG8KPSIiIlIq/m9YG2p6urIvOoHP/jxi73KuoNAjIiIipaJWdTemDGsDwIzfD3DifP6LK9qLQo+IiIiUmls7N6Bb01pcTM9k6k+7caTlABV6REREpNRYLBZev7E9rs4WVu6LZfnuaHuXZKXQIyIiIqWqhb8XD/RtDsBLi/eQmJph54pMCj0iIiJS6h6+tgWNa3sSHZ/Cu79F2LscQKFHREREyoCHqzOvjjTX7vl8w1F2nYyzc0UKPSIiIlJG+raqw4iQemQZ8H8Ld5KZZd9BzQo9IiIiUmZeuL4NXh4u7DgRx1d/HbNrLQo9IiIiUmb8vTx4ZnAQAG8vjyAmPsVutSj0iIiISJm6PawxIQ19SUzN4K1l9hvUrNAjIiIiZcrZyVy754aQejw7JMhudbjY7c4iIiJSZbSr58P7YzvatQa19IiIiEiVoNAjIiIiVYJCj4iIiFQJCj0iIiJSJSj0iIiISJWg0CMiIiJVgkKPiIiIVAkKPSIiIlIlKPSIiIhIlaDQIyIiIlWCQo+IiIhUCQo9IiIiUiUo9IiIiEiVoF3WczEMA4D4+Hg7VyIiIiJFlf17O/v3eEEUenJJSEgAoGHDhnauRERERGyVkJCAj49Pgc9bjKvFoiokKyuLU6dO4eXlhcViKdVrx8fH07BhQ44fP463t3epXtvR6L1WXlXp/eq9Vk5V6b1C1Xm/hmGQkJBAvXr1cHIqeOSOWnpycXJyokGDBmV6D29v70r9H15ueq+VV1V6v3qvlVNVeq9QNd5vYS082TSQWURERKoEhR4RERGpEhR6yom7uztTp07F3d3d3qWUOb3XyqsqvV+918qpKr1XqHrv92o0kFlERESqBLX0iIiISJWg0CMiIiJVgkKPiIiIVAkKPSIiIlIlKPSUklmzZtGkSRM8PDwICwtj06ZNhZ7//fff07p1azw8PAgODmbp0qXlVGnJTJs2ja5du+Ll5YW/vz+jRo0iIiKi0NfMnTsXi8WS58vDw6OcKi6+l1566Yq6W7duXehrKurnCtCkSZMr3q/FYmHSpEn5nl+RPtc//viDESNGUK9ePSwWC4sWLcrzvGEYvPjii9StW5dq1aoxcOBADhw4cNXr2vpzXx4Ke6/p6ek8++yzBAcHU716derVq8f48eM5depUodcszs9CebnaZztx4sQrah8yZMhVr1vRPlsg359fi8XC22+/XeA1HfmzLQsKPaVg/vz5TJ48malTp7J161ZCQkIYPHgwsbGx+Z6/YcMGxo4dyz333MO2bdsYNWoUo0aNYteuXeVcue3Wrl3LpEmT+Ouvv1ixYgXp6ekMGjSIpKSkQl/n7e1NVFSU9evYsWPlVHHJtGvXLk/df/75Z4HnVuTPFeCff/7J815XrFgBwK233lrgayrK55qUlERISAizZs3K9/m33nqL999/n9mzZ/P3339TvXp1Bg8eTEpKSoHXtPXnvrwU9l6Tk5PZunUrL7zwAlu3bmXBggVERERwww03XPW6tvwslKerfbYAQ4YMyVP7t99+W+g1K+JnC+R5j1FRUXz22WdYLBZuvvnmQq/rqJ9tmTCkxLp162ZMmjTJ+ufMzEyjXr16xrRp0/I9f/To0cbw4cPzPBYWFmY88MADZVpnWYiNjTUAY+3atQWeM2fOHMPHx6f8iiolU6dONUJCQop8fmX6XA3DMB577DGjefPmRlZWVr7PV9TPFTAWLlxo/XNWVpYRGBhovP3229bHLly4YLi7uxvffvttgdex9efeHi5/r/nZtGmTARjHjh0r8BxbfxbsJb/3O2HCBGPkyJE2XaeyfLYjR440rr322kLPqSifbWlRS08JpaWlsWXLFgYOHGh9zMnJiYEDB7Jx48Z8X7Nx48Y85wMMHjy4wPMdWVxcHAC1atUq9LzExEQaN25Mw4YNGTlyJLt37y6P8krswIED1KtXj2bNmjFu3DgiIyMLPLcyfa5paWl89dVX3H333YVuvltRP9fcjhw5QnR0dJ7PzsfHh7CwsAI/u+L83DuquLg4LBYLvr6+hZ5ny8+Co1mzZg3+/v4EBQXx4IMPcvbs2QLPrSyfbUxMDEuWLOGee+656rkV+bO1lUJPCZ05c4bMzEwCAgLyPB4QEEB0dHS+r4mOjrbpfEeVlZXF448/Tq9evWjfvn2B5wUFBfHZZ5/x008/8dVXX5GVlUXPnj05ceJEOVZru7CwMObOncuyZcv46KOPOHLkCH369CEhISHf8yvL5wqwaNEiLly4wMSJEws8p6J+rpfL/nxs+eyK83PviFJSUnj22WcZO3ZsoZtR2vqz4EiGDBnCF198wcqVK3nzzTdZu3YtQ4cOJTMzM9/zK8tn+/nnn+Pl5cVNN91U6HkV+bMtDu2yLsU2adIkdu3addX+3x49etCjRw/rn3v27EmbNm34+OOPefXVV8u6zGIbOnSo9bhDhw6EhYXRuHFjvvvuuyL966ki+9///sfQoUOpV69egedU1M9VTOnp6YwePRrDMPjoo48KPbci/yzcdttt1uPg4GA6dOhA8+bNWbNmDQMGDLBjZWXrs88+Y9y4cVedXFCRP9viUEtPCfn5+eHs7ExMTEyex2NiYggMDMz3NYGBgTad74gefvhhfvnlF1avXk2DBg1seq2rqysdO3bk4MGDZVRd2fD19aVVq1YF1l0ZPleAY8eO8fvvv3Pvvffa9LqK+rlmfz62fHbF+bl3JNmB59ixY6xYsaLQVp78XO1nwZE1a9YMPz+/Amuv6J8twLp164iIiLD5Zxgq9mdbFAo9JeTm5kbnzp1ZuXKl9bGsrCxWrlyZ51/BufXo0SPP+QArVqwo8HxHYhgGDz/8MAsXLmTVqlU0bdrU5mtkZmayc+dO6tatWwYVlp3ExEQOHTpUYN0V+XPNbc6cOfj7+zN8+HCbXldRP9emTZsSGBiY57OLj4/n77//LvCzK87PvaPIDjwHDhzg999/p3bt2jZf42o/C47sxIkTnD17tsDaK/Jnm+1///sfnTt3JiQkxObXVuTPtkjsPZK6Mpg3b57h7u5uzJ0719izZ49x//33G76+vkZ0dLRhGIZx5513Gs8995z1/PXr1xsuLi7GO++8Y+zdu9eYOnWq4erqauzcudNeb6HIHnzwQcPHx8dYs2aNERUVZf1KTk62nnP5+3355ZeN5cuXG4cOHTK2bNli3HbbbYaHh4exe/due7yFInvyySeNNWvWGEeOHDHWr19vDBw40PDz8zNiY2MNw6hcn2u2zMxMo1GjRsazzz57xXMV+XNNSEgwtm3bZmzbts0AjOnTpxvbtm2zzlh64403DF9fX+Onn34yduzYYYwcOdJo2rSpcfHiRes1rr32WuODDz6w/vlqP/f2Uth7TUtLM2644QajQYMGRnh4eJ6f4dTUVOs1Ln+vV/tZsKfC3m9CQoLx1FNPGRs3bjSOHDli/P7770anTp2Mli1bGikpKdZrVIbPNltcXJzh6elpfPTRR/leoyJ9tmVBoaeUfPDBB0ajRo0MNzc3o1u3bsZff/1lfa5fv37GhAkT8pz/3XffGa1atTLc3NyMdu3aGUuWLCnniosHyPdrzpw51nMuf7+PP/649e8mICDAGDZsmLF169byL95GY8aMMerWrWu4ubkZ9evXN8aMGWMcPHjQ+nxl+lyzLV++3ACMiIiIK56ryJ/r6tWr8/3vNvv9ZGVlGS+88IIREBBguLu7GwMGDLji76Bx48bG1KlT8zxW2M+9vRT2Xo8cOVLgz/Dq1aut17j8vV7tZ8GeCnu/ycnJxqBBg4w6deoYrq6uRuPGjY377rvvivBSGT7bbB9//LFRrVo148KFC/leoyJ9tmXBYhiGUaZNSSIiIiIOQGN6REREpEpQ6BEREZEqQaFHREREqgSFHhEREakSFHpERESkSlDoERERkSpBoUdERESqBIUeERERqRIUekRERKRKUOgRERGRKkGhR0RERKoEhR4RERGpEv4/U7XJBHY2lGgAAAAASUVORK5CYII=",
|
||
"text/plain": [
|
||
"<Figure size 640x480 with 1 Axes>"
|
||
]
|
||
},
|
||
"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": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>mae</th>\n",
|
||
" <th>rmse</th>\n",
|
||
" <th>mape</th>\n",
|
||
" <th>r2</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>count</th>\n",
|
||
" <td>75.000000</td>\n",
|
||
" <td>75.000000</td>\n",
|
||
" <td>75.000000</td>\n",
|
||
" <td>75.000000</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>mean</th>\n",
|
||
" <td>1.296906</td>\n",
|
||
" <td>2.022362</td>\n",
|
||
" <td>0.167694</td>\n",
|
||
" <td>0.904339</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>std</th>\n",
|
||
" <td>0.075761</td>\n",
|
||
" <td>0.137041</td>\n",
|
||
" <td>0.013171</td>\n",
|
||
" <td>0.010395</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>min</th>\n",
|
||
" <td>1.121284</td>\n",
|
||
" <td>1.716275</td>\n",
|
||
" <td>0.143667</td>\n",
|
||
" <td>0.875878</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>25%</th>\n",
|
||
" <td>1.238378</td>\n",
|
||
" <td>1.917907</td>\n",
|
||
" <td>0.156429</td>\n",
|
||
" <td>0.898060</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>50%</th>\n",
|
||
" <td>1.287193</td>\n",
|
||
" <td>2.011828</td>\n",
|
||
" <td>0.166679</td>\n",
|
||
" <td>0.904941</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>75%</th>\n",
|
||
" <td>1.353045</td>\n",
|
||
" <td>2.102409</td>\n",
|
||
" <td>0.176996</td>\n",
|
||
" <td>0.911137</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>max</th>\n",
|
||
" <td>1.446046</td>\n",
|
||
" <td>2.414532</td>\n",
|
||
" <td>0.202142</td>\n",
|
||
" <td>0.924070</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"</div>"
|
||
],
|
||
"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": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>mae</th>\n",
|
||
" <th>rmse</th>\n",
|
||
" <th>mape</th>\n",
|
||
" <th>r2</th>\n",
|
||
" <th>ioa</th>\n",
|
||
" <th>r</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>count</th>\n",
|
||
" <td>4739.000000</td>\n",
|
||
" <td>4739.000000</td>\n",
|
||
" <td>4739.000000</td>\n",
|
||
" <td>4739.000000</td>\n",
|
||
" <td>4739.000000</td>\n",
|
||
" <td>4739.000000</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>mean</th>\n",
|
||
" <td>1.306817</td>\n",
|
||
" <td>1.845819</td>\n",
|
||
" <td>0.166876</td>\n",
|
||
" <td>0.670519</td>\n",
|
||
" <td>0.886646</td>\n",
|
||
" <td>0.836323</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>std</th>\n",
|
||
" <td>0.623645</td>\n",
|
||
" <td>0.902619</td>\n",
|
||
" <td>0.107025</td>\n",
|
||
" <td>0.240752</td>\n",
|
||
" <td>0.111142</td>\n",
|
||
" <td>0.121726</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>min</th>\n",
|
||
" <td>0.432991</td>\n",
|
||
" <td>0.568319</td>\n",
|
||
" <td>0.050612</td>\n",
|
||
" <td>-1.539424</td>\n",
|
||
" <td>-0.267569</td>\n",
|
||
" <td>0.022258</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>25%</th>\n",
|
||
" <td>0.835579</td>\n",
|
||
" <td>1.172322</td>\n",
|
||
" <td>0.113302</td>\n",
|
||
" <td>0.583713</td>\n",
|
||
" <td>0.864756</td>\n",
|
||
" <td>0.794922</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>50%</th>\n",
|
||
" <td>1.161710</td>\n",
|
||
" <td>1.658195</td>\n",
|
||
" <td>0.143386</td>\n",
|
||
" <td>0.735860</td>\n",
|
||
" <td>0.921341</td>\n",
|
||
" <td>0.869860</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>75%</th>\n",
|
||
" <td>1.617382</td>\n",
|
||
" <td>2.299731</td>\n",
|
||
" <td>0.185039</td>\n",
|
||
" <td>0.827242</td>\n",
|
||
" <td>0.951285</td>\n",
|
||
" <td>0.916741</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>max</th>\n",
|
||
" <td>5.338230</td>\n",
|
||
" <td>9.936951</td>\n",
|
||
" <td>1.929986</td>\n",
|
||
" <td>0.983208</td>\n",
|
||
" <td>0.995767</td>\n",
|
||
" <td>0.992588</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"</div>"
|
||
],
|
||
"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
|
||
}
|