├── loss.png ├── Train ├── 0.pkl └── 1.pkl ├── clf1.torch ├── Test_A ├── 0.pkl └── 1.pkl ├── 能源AI挑战赛_异常检测赛.pptx ├── __pycache__ └── myutils.cpython-37.pyc ├── Revisiting Deep Learning Models for Tabular Data.pdf ├── requirements.txt ├── README.md ├── myutils.py ├── train.py ├── .ipynb_checkpoints ├── train_optuna-checkpoint.ipynb └── demo_resnet_epoch532_lr0.001_dropout0.25_auc0.8998-checkpoint.ipynb ├── train_optuna.ipynb └── demo_resnet_epoch532_lr0.001_dropout0.25_auc0.8998.ipynb /loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/loss.png -------------------------------------------------------------------------------- /Train/0.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/Train/0.pkl -------------------------------------------------------------------------------- /Train/1.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/Train/1.pkl -------------------------------------------------------------------------------- /clf1.torch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/clf1.torch -------------------------------------------------------------------------------- /Test_A/0.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/Test_A/0.pkl -------------------------------------------------------------------------------- /Test_A/1.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/Test_A/1.pkl -------------------------------------------------------------------------------- /能源AI挑战赛_异常检测赛.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/能源AI挑战赛_异常检测赛.pptx -------------------------------------------------------------------------------- /__pycache__/myutils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/__pycache__/myutils.cpython-37.pyc -------------------------------------------------------------------------------- /Revisiting Deep Learning Models for Tabular Data.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/HEAD/Revisiting Deep Learning Models for Tabular Data.pdf -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | delu==0.0.13 2 | loguru==0.6.0 3 | matplotlib==3.6.1 4 | numpy==1.23.4 5 | pandas==1.3.2 6 | rtdl==0.0.13 7 | scikit_learn==1.1.2 8 | scipy==1.9.2 9 | torch==1.11.0 10 | tqdm==4.41.0 11 | matplotlib==3.0.3 12 | scipy==1.2.1 13 | numpy==1.21.5 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 一.比赛链接 2 |   https://aistudio.baidu.com/aistudio/competition/detail/495/0/introduction 3 | 4 | # 二.赛题背景 5 |   汽车产业正在经历巨大变革,新能源汽车市场规模持续扩大,电池安全问题日益引发重视。 电池异常检测面临着汽车数据质量差,检出率低,误报率高,大量无效报警无法直接自动化运维等问题。 6 |   为了更好的检验电池安全问题,比赛通过募集优秀异常检测方案,使用特征提取、参数优化、异常对比等手段对实车数据进行处理,优化异常检测结果,以便更好的应用于车辆预警、故障模式识别等多种场景。 7 | 8 | # 三.比赛方案 9 |         贴一个方案图和比赛结果,通过实验验证ResNet结构的网络在处理电池数据异常检测效果明显优于MLP和FTTransformer网络,具体实验可查看[博客](https://blog.csdn.net/weixin_43509698/article/details/127417008)和[能源AI挑战赛_异常检测赛.pptx](https://github.com/Shybert-AI/Energy_Anomaly_Detection_TOP3/files/9889316/AI._.pptx)。如果觉得可以,点个小星星:blush::blush:,多谢。 10 | 11 | 12 | ![image](https://user-images.githubusercontent.com/82042336/198660038-d466bb59-74af-4d43-8f41-86edadc9021d.png) 13 | 14 | ![image](https://user-images.githubusercontent.com/82042336/198649860-826d7b38-0e00-4cfe-ad8f-ef6177f43c7c.png) 15 | # 四.特征分析 16 |         模型只用到了是电池数据的前7列的最后一行数据和里程数。 17 | # 五.模型构建及训练 18 | ## 1.代码结构 19 |     ./Energy_Anomaly_Detection 20 |     │ clf1.torch # 模型文件 21 |     │ data_anls.ipynb # 数据分析脚本 22 |     │ demo_resnet_epoch532_lr0.001_dropout0.25_auc0.8998.ipynb # jupyter版本训练代码 23 |     │ loss.png # loss曲线 24 |     │ myutils.py # 配置文件 25 |     │ README.md # 中文用户手册 26 |     │ requirements.txt # 依赖环境 27 |     │ Revisiting Deep Learning Models for Tabular Data.pdf 28 |     │ submision.csv # 比赛要求的csv文件 29 |     │ train.py # 在jupyter版本训练代码进行规整的训练脚本 30 |     │ train_optuna.ipynb # optuna自动化调参 31 |     │ 能源AI挑战赛_异常检测赛.pptx 32 |     ├─log # 日志类文件夹 33 |     | train.log # 训练日志文件 34 |     ├─Test_A # 验证数据(只存在样例数据,训练时需拷贝全部数据) 35 |     │ 0.pkl 36 |     │ 1.pkl 37 |     │ ... 38 |     │ ... 39 |     │ ... 40 |     │ 41 |     ├─Train # 训练数据(只存在样例数据,训练时需拷贝全部数据) 42 |     │ 0.pkl 43 |     │ 1.pkl 44 |     │ ... 45 |     │ ... 46 |     │ ... 47 | ## 2.环境依赖 48 | The experiment code is written in Python 3 and built on a number of Python packages: 49 | - delu==0.0.13 50 | - loguru==0.6.0 51 | - matplotlib==3.6.1 52 | - numpy==1.23.4 53 | - pandas==1.3.2 54 | - rtdl==0.0.13 55 | - scikit_learn==1.1.2 56 | - scipy==1.9.2 57 | - torch==1.11.0 58 | - tqdm==4.41.0 59 | - matplotlib==3.0.3 60 | - scipy==1.2.1 61 | - numpy==1.21.5 62 | 63 | ## 3.两种形式执行训练,生成比赛要求的csv文件 64 | ###     3.1 train.py执行训练,可以通过修改model_name参数,进行MLP、ResNet、FTTransformer的实验。 65 | ```python 66 | python train.py 67 | python train.py --model_name ResNet --train Train --test Test_A --epoch 532 --batch_size 256 68 | ``` 69 | ###     3.2 demo_resnet_epoch532_lr0.001_dropout0.25_auc0.8998.ipynb执行训练 70 | 71 | # 六.总结 72 |         1.模型只用到了是电池数据的前7列的最后一行数据和里程数,未考虑到数据的时序性。因此还存在改进点,比如将[256,8]的特征压缩到[1,8]或者用[256,8]的特征进行分类等等。 `当然这只是猜想`:smiling_imp: 73 |         2.loss曲线发现趋势存在震荡,因为模型的超参数是通过手动调节的。因此设计了一个脚本train_optuna.ipynb,进行自动化调节模型的超参数,模型的超参数有学习率、batch_size、隐藏层的个数、dropout、网络结构的层数等一系列参数。 (由于时间等一系列原因导致搁置了。。。。。) 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /myutils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | import numpy as np 4 | import random 5 | import torch 6 | #import tensorflow as tf 7 | 8 | # metric 9 | from sklearn.metrics import roc_auc_score, average_precision_score 10 | 11 | # plot 12 | import matplotlib.pyplot as plt 13 | 14 | # statistical analysis 15 | from scipy.stats import wilcoxon 16 | 17 | class Utils(): 18 | def __init__(self): 19 | pass 20 | 21 | # remove randomness 22 | def set_seed(self, seed): 23 | # os.environ['PYTHONHASHSEED'] = str(seed) 24 | # os.environ['TF_CUDNN_DETERMINISTIC'] = 'true' 25 | # os.environ['TF_DETERMINISTIC_OPS'] = 'true' 26 | 27 | # basic seed 28 | np.random.seed(seed) 29 | random.seed(seed) 30 | 31 | # tensorflow seed 32 | #try: 33 | # tf.random.set_seed(seed) # for tf >= 2.0 34 | #except: 35 | # tf.set_random_seed(seed) 36 | # tf.random.set_random_seed(seed) 37 | 38 | # pytorch seed 39 | torch.manual_seed(seed) 40 | torch.backends.cudnn.deterministic = True 41 | torch.backends.cudnn.benchmark = False 42 | 43 | def get_device(self, gpu_specific=False): 44 | if gpu_specific: 45 | if torch.cuda.is_available(): 46 | n_gpu = torch.cuda.device_count() 47 | print(f'number of gpu: {n_gpu}') 48 | print(f'cuda name: {torch.cuda.get_device_name(0)}') 49 | print('GPU is on') 50 | else: 51 | print('GPU is off') 52 | 53 | device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 54 | else: 55 | device = torch.device("cpu") 56 | return device 57 | 58 | # generate unique value 59 | def unique(self, a, b): 60 | u = 0.5 * (a + b) * (a + b + 1) + b 61 | return int(u) 62 | 63 | def data_description(self, X, y): 64 | des_dict = {} 65 | des_dict['Samples'] = X.shape[0] 66 | des_dict['Features'] = X.shape[1] 67 | des_dict['Anomalies'] = sum(y) 68 | des_dict['Anomalies Ratio(%)'] = round((sum(y) / len(y)) * 100, 2) 69 | 70 | print(des_dict) 71 | 72 | # metric 73 | def metric(self, y_true, y_score, pos_label=1): 74 | aucroc = roc_auc_score(y_true=y_true, y_score=y_score) 75 | aucpr = average_precision_score(y_true=y_true, y_score=y_score, pos_label=1) 76 | 77 | return {'aucroc':aucroc, 'aucpr':aucpr} 78 | 79 | # resampling function 80 | def sampler(self, X_train, y_train, batch_size): 81 | index_u = np.where(y_train == 0)[0] 82 | index_a = np.where(y_train == 1)[0] 83 | 84 | n = 0 85 | while len(index_u) >= batch_size: 86 | self.set_seed(n) 87 | index_u_batch = np.random.choice(index_u, batch_size // 2, replace=False) 88 | index_u = np.setdiff1d(index_u, index_u_batch) 89 | 90 | index_a_batch = np.random.choice(index_a, batch_size // 2, replace=True) 91 | 92 | # batch index 93 | index_batch = np.append(index_u_batch, index_a_batch) 94 | # shuffle 95 | np.random.shuffle(index_batch) 96 | 97 | if n == 0: 98 | X_train_new = X_train[index_batch] 99 | y_train_new = y_train[index_batch] 100 | else: 101 | X_train_new = np.append(X_train_new, X_train[index_batch], axis=0) 102 | y_train_new = np.append(y_train_new, y_train[index_batch]) 103 | n += 1 104 | 105 | return X_train_new, y_train_new 106 | 107 | def sampler_2(self, X_train, y_train, step, batch_size=512): 108 | index_u = np.where(y_train == 0)[0] 109 | index_a = np.where(y_train == 1)[0] 110 | 111 | for i in range(step): 112 | index_u_batch = np.random.choice(index_u, batch_size // 2, replace=True) 113 | index_a_batch = np.random.choice(index_a, batch_size // 2, replace=True) 114 | 115 | # batch index 116 | index_batch = np.append(index_u_batch, index_a_batch) 117 | # shuffle 118 | np.random.shuffle(index_batch) 119 | 120 | if i == 0: 121 | X_train_new = X_train[index_batch] 122 | y_train_new = y_train[index_batch] 123 | else: 124 | X_train_new = np.append(X_train_new, X_train[index_batch], axis=0) 125 | y_train_new = np.append(y_train_new, y_train[index_batch]) 126 | 127 | return X_train_new, y_train_new 128 | 129 | # for PReNet 130 | def sampler_pairs(self, X_train_tensor, y_train, epoch, batch_num, batch_size, s_a_a, s_a_u, s_u_u): 131 | ''' 132 | X_train_tensor: the input X in the torch.tensor form 133 | y_train: label in the numpy.array form 134 | 135 | batch_num: generate how many batches in one epoch 136 | batch_size: the batch size 137 | ''' 138 | data_loader_X = [] 139 | data_loader_y = [] 140 | 141 | index_a = np.where(y_train == 1)[0] 142 | index_u = np.where(y_train == 0)[0] 143 | 144 | for i in range(batch_num): # i.e., drop_last = True 145 | index = [] 146 | 147 | # pairs of (a,a); (a,u); (u,u) 148 | for j in range(6): 149 | # generate unique seed and set seed 150 | # seed = self.unique(epoch, i) 151 | # seed = self.unique(seed, j) 152 | # self.set_seed(seed) 153 | 154 | if j < 3: 155 | index_sub = np.random.choice(index_a, batch_size // 4, replace=True) 156 | index.append(list(index_sub)) 157 | 158 | if j == 3: 159 | index_sub = np.random.choice(index_u, batch_size // 4, replace=True) 160 | index.append(list(index_sub)) 161 | 162 | if j > 3: 163 | index_sub = np.random.choice(index_u, batch_size // 2, replace=True) 164 | index.append(list(index_sub)) 165 | 166 | # index[0] + index[1] = (a,a), batch / 4 167 | # index[2] + index[2] = (a,u), batch / 4 168 | # index[4] + index[5] = (u,u), batch / 2 169 | index_left = index[0] + index[2] + index[4] 170 | index_right = index[1] + index[3] + index[5] 171 | 172 | X_train_tensor_left = X_train_tensor[index_left] 173 | X_train_tensor_right = X_train_tensor[index_right] 174 | 175 | # generate label 176 | y_train_new = np.append(np.repeat(s_a_a, batch_size // 4), np.repeat(s_a_u, batch_size // 4)) 177 | y_train_new = np.append(y_train_new, np.repeat(s_u_u, batch_size // 2)) 178 | y_train_new = torch.from_numpy(y_train_new).float() 179 | 180 | # shuffle 181 | index_shuffle = np.arange(len(y_train_new)) 182 | index_shuffle = np.random.choice(index_shuffle, len(index_shuffle), replace=False) 183 | 184 | X_train_tensor_left = X_train_tensor_left[index_shuffle] 185 | X_train_tensor_right = X_train_tensor_right[index_shuffle] 186 | y_train_new = y_train_new[index_shuffle] 187 | 188 | # save 189 | data_loader_X.append([X_train_tensor_left, X_train_tensor_right]) 190 | data_loader_y.append(y_train_new) 191 | 192 | return data_loader_X, data_loader_y 193 | 194 | # gradient norm 195 | def grad_norm(self, grad_tuple): 196 | 197 | grad = torch.tensor([0.0]) 198 | for i in range(len(grad_tuple)): 199 | grad += torch.norm(grad_tuple[i]) 200 | 201 | return grad 202 | 203 | # visualize the gradient flow in network 204 | def plot_grad_flow(self, named_parameters): 205 | ave_grads = [] 206 | layers = [] 207 | for n, p in named_parameters: 208 | if (p.requires_grad) and ("bias" not in n): 209 | layers.append(n) 210 | ave_grads.append(p.grad.abs().mean()) 211 | plt.plot(ave_grads, alpha=0.3, color="b") 212 | plt.hlines(0, 0, len(ave_grads) + 1, linewidth=1, color="k") 213 | plt.xticks(range(0, len(ave_grads), 1), layers, rotation="vertical") 214 | plt.xlim(xmin=0, xmax=len(ave_grads)) 215 | plt.xlabel("Layers") 216 | plt.ylabel("average gradient") 217 | plt.title("Gradient flow") 218 | plt.grid(True) 219 | 220 | # def torch_wasserstein_loss(tensor_a, tensor_b): 221 | # # Compute the first Wasserstein distance between two 1D distributions. 222 | # return (torch_cdf_loss(tensor_a, tensor_b, p=1)) 223 | 224 | # Calculate the First Wasserstein Distance 225 | def torch_cdf_loss(self, tensor_a, tensor_b, p=1): 226 | # last-dimension is weight distribution 227 | # p is the norm of the distance, p=1 --> First Wasserstein Distance 228 | # to get a positive weight with our normalized distribution 229 | # we recommend combining this loss with other difference-based losses like L1 230 | 231 | # normalize distribution, add 1e-14 to divisor to avoid 0/0 232 | tensor_a = tensor_a / (torch.sum(tensor_a, dim=-1, keepdim=True) + 1e-14) 233 | tensor_b = tensor_b / (torch.sum(tensor_b, dim=-1, keepdim=True) + 1e-14) 234 | # make cdf with cumsum 235 | cdf_tensor_a = torch.cumsum(tensor_a, dim=-1) 236 | cdf_tensor_b = torch.cumsum(tensor_b, dim=-1) 237 | 238 | # choose different formulas for different norm situations 239 | if p == 1: 240 | cdf_distance = torch.sum(torch.abs((cdf_tensor_a - cdf_tensor_b)), dim=-1) 241 | elif p == 2: 242 | cdf_distance = torch.sqrt(torch.sum(torch.pow((cdf_tensor_a - cdf_tensor_b), 2), dim=-1)) 243 | else: 244 | cdf_distance = torch.pow(torch.sum(torch.pow(torch.abs(cdf_tensor_a - cdf_tensor_b), p), dim=-1), 1 / p) 245 | 246 | cdf_loss = cdf_distance.mean() 247 | return cdf_loss 248 | 249 | # Calculate the loss like devnet in PyTorch 250 | def cal_loss(self, y, y_pred, mode='devnet'): 251 | if mode == 'devnet': 252 | y_pred.squeeze_() 253 | 254 | ref = torch.randn(5000) # sampling from the normal distribution 255 | dev = (y_pred - torch.mean(ref)) / torch.std(ref) 256 | # print(f'mean:{torch.mean(ref)}, std:{torch.std(ref)}') 257 | inlier_loss = torch.abs(dev) 258 | outlier_loss = torch.max(5.0 - dev, torch.zeros_like(5.0 - dev)) 259 | 260 | loss = torch.mean((1 - y) * inlier_loss + y * outlier_loss) 261 | else: 262 | raise NotImplementedError 263 | 264 | return loss 265 | 266 | def result_process(self, result_show, name, std=False): 267 | # average performance 268 | ave_metric = np.mean(result_show, axis=0).values 269 | std_metric = np.std(result_show, axis=0).values 270 | 271 | # statistical test 272 | wilcoxon_df = pd.DataFrame(data=None, index=result_show.columns, columns=result_show.columns) 273 | 274 | for i in range(wilcoxon_df.shape[0]): 275 | for j in range(wilcoxon_df.shape[1]): 276 | if i != j: 277 | wilcoxon_df.iloc[i, j] = \ 278 | wilcoxon(result_show.iloc[:, i] - result_show.iloc[:, j], alternative='greater')[1] 279 | 280 | # average rank 281 | result_show.loc['Ave.rank'] = np.mean(result_show.rank(ascending=False, method='dense', axis=1), axis=0) 282 | 283 | # average metric 284 | if std: 285 | result_show.loc['Ave.metric'] = [str(format(round(a,3), '.3f')) + '±' + str(format(round(s,3), '.3f')) 286 | for a,s in zip(ave_metric, std_metric)] 287 | else: 288 | result_show.loc['Ave.metric'] = [str(format(round(a, 3), '.3f')) for a, s in zip(ave_metric, std_metric)] 289 | 290 | 291 | # the p-value of wilcoxon statistical test 292 | result_show.loc['p-value'] = wilcoxon_df.loc[name].values 293 | 294 | 295 | for _ in result_show.index: 296 | if _ in ['Ave.rank', 'p-value']: 297 | result_show.loc[_, :] = [format(round(_, 2), '.2f') for _ in result_show.loc[_, :].values] 298 | 299 | # result_show = result_show.astype('float') 300 | # result_show = result_show.round(2) 301 | 302 | return result_show -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | import pickle 4 | import random 5 | import argparse 6 | import platform 7 | import numpy as np 8 | from glob import glob 9 | from tqdm import tqdm 10 | import pandas as pd 11 | import torch 12 | import torch.nn.functional as F 13 | from sklearn.utils import shuffle 14 | from matplotlib import pyplot as plt 15 | import delu 16 | import rtdl 17 | import scipy 18 | from loguru import logger 19 | from myutils import Utils 20 | utils = Utils() # utils function 21 | 22 | 23 | def load_data(pkl_list,label=True): 24 | ''' 25 | 输入pkl的列表,进行文件加载 26 | label=True用来加载训练集 27 | label=False用来加载真正的测试集,真正的测试集无标签 28 | ''' 29 | X = [] 30 | y = [] 31 | 32 | 33 | for each_pkl in pkl_list: 34 | pic = open(each_pkl,'rb') 35 | item= pickle.load(pic)#下载pkl文件 36 | # 此处选取的是每个滑窗的最后一条数据,仅供参考,可以选择其他的方法,比如均值或者其他处理时序数据的网络 37 | # 此处选取了前7个特征,可以需求选取特征数量 38 | feature = item[0][:,0:7][-1] 39 | #feature = item[0][:,0:8][-1] 40 | #feature = item[0][:,0:7].mean(axis=0) 41 | #feature = np.append(item[0][:,0:7][-1],(item[0][:,3][-1] - item[0][:,4][-1])) #加max_single_volt - min_single_volt 一列为特征 42 | feature=np.append(feature,item[1]["mileage"]) 43 | X.append(feature) 44 | if label: 45 | y.append(int(item[1]['label'][0])) 46 | X = np.vstack(X) 47 | if label: 48 | y = np.vstack(y) 49 | return X, y 50 | 51 | def normalization(data): 52 | """ 53 | 归一化数据 54 | """ 55 | _mean = np.mean(data, axis=0) 56 | _std = np.std(data, axis=0) 57 | data = (data - _mean) / (_std + 1e-4) 58 | return data 59 | 60 | 61 | class FTTransformer(): 62 | ''' 63 | The original code: https://yura52.github.io/rtdl/stable/index.html 64 | The original paper: "Revisiting Deep Learning Models for Tabular Data", NIPS 2019 65 | ''' 66 | def __init__(self, seed:int, model_name:str, n_epochs=1000, batch_size=256): 67 | 68 | self.seed = seed 69 | self.model_name = model_name 70 | self.utils = Utils() 71 | 72 | # device 73 | if model_name == 'FTTransformer': 74 | self.device = self.utils.get_device(gpu_specific=True) 75 | else: 76 | self.device = self.utils.get_device(gpu_specific=False) 77 | 78 | # Docs: https://yura52.github.io/zero/0.0.4/reference/api/zero.improve_reproducibility.html 79 | # zero.improve_reproducibility(seed=self.seed) 80 | delu.improve_reproducibility(base_seed=int(self.seed)) 81 | 82 | # hyper-parameter 83 | self.n_epochs = n_epochs # default is 1000 84 | self.batch_size = batch_size # default is 256 85 | 86 | def apply_model(self, x_num, x_cat=None): 87 | if isinstance(self.model, rtdl.FTTransformer): 88 | return self.model(x_num, x_cat) 89 | elif isinstance(self.model, (rtdl.MLP, rtdl.ResNet)): 90 | assert x_cat is None 91 | return self.model(x_num) 92 | else: 93 | raise NotImplementedError( 94 | f'Looks like you are using a custom model: {type(self.model)}.' 95 | ' Then you have to implement this branch first.' 96 | ) 97 | 98 | @torch.no_grad() 99 | def evaluate(self, X, y=None): 100 | self.model.eval() 101 | score = [] 102 | # for batch in delu.iter_batches(X[part], 1024): 103 | for batch in delu.iter_batches(X, self.batch_size): 104 | score.append(self.apply_model(batch)) 105 | score = torch.cat(score).squeeze(1).cpu().numpy() 106 | score = scipy.special.expit(score) 107 | 108 | # calculate the metric 109 | if y is not None: 110 | target = y.cpu().numpy() 111 | metric = self.utils.metric(y_true=target, y_score=score) 112 | else: 113 | metric = {'aucroc': None, 'aucpr': None} 114 | 115 | return score, metric['aucpr'] 116 | 117 | def fit(self, X_train, y_train, ratio=None,X_test=None,y_test=None): 118 | # set seed 119 | self.utils.set_seed(self.seed) 120 | 121 | # training set is used as the validation set in the anomaly detection task 122 | X = {'train': torch.from_numpy(X_train).float().to(self.device), 123 | 'val': torch.from_numpy(X_train).float().to(self.device)} 124 | 125 | y = {'train': torch.from_numpy(y_train).float().to(self.device), 126 | 'val': torch.from_numpy(y_train).float().to(self.device)} 127 | 128 | 129 | task_type = 'binclass' 130 | n_classes = None 131 | d_out = n_classes or 1 132 | 133 | 134 | if self.model_name == 'ResNet': 135 | self.model = rtdl.ResNet.make_baseline( 136 | d_in=X_train.shape[1], 137 | d_main=128, 138 | d_hidden=256, 139 | dropout_first=0.25, 140 | dropout_second=0.0, 141 | n_blocks=2, 142 | d_out=d_out, 143 | ) 144 | lr = 0.001 145 | weight_decay = 0.0 146 | 147 | elif self.model_name == 'MLP': 148 | self.model = rtdl.MLP.make_baseline( 149 | d_in=X_train.shape[1], 150 | d_layers= [128, 256, 128], 151 | dropout=0.25, 152 | d_out=d_out, 153 | ) 154 | lr = 0.001 155 | weight_decay = 0.0 156 | 157 | elif self.model_name == 'FTTransformer': 158 | self.model = rtdl.FTTransformer.make_default( 159 | n_num_features=X_train.shape[1], 160 | cat_cardinalities=None, 161 | last_layer_query_idx=[-1], # it makes the model faster and does NOT affect its output 162 | d_out=d_out, 163 | ) 164 | 165 | elif self.model_name == 'FTTransformer_baseline': 166 | self.model = rtdl.FTTransformer.make_baseline( 167 | n_num_features=X_train.shape[1], 168 | cat_cardinalities=None, 169 | d_token=X_train.shape[1], 170 | n_blocks=2, 171 | attention_dropout=0.2, 172 | ffn_d_hidden=6, 173 | ffn_dropout=0.2, 174 | residual_dropout=0.0, 175 | d_out=d_out, 176 | ) 177 | else: 178 | raise NotImplementedError 179 | 180 | self.model.to(self.device) 181 | optimizer = ( 182 | self.model.make_default_optimizer() 183 | if isinstance(self.model, rtdl.FTTransformer) 184 | else torch.optim.AdamW(self.model.parameters(), lr=lr, weight_decay=weight_decay) 185 | ) 186 | loss_fn = ( 187 | F.binary_cross_entropy_with_logits 188 | if task_type == 'binclass' 189 | else F.cross_entropy 190 | if task_type == 'multiclass' 191 | else F.mse_loss 192 | ) 193 | 194 | # Create a dataloader for batches of indices 195 | # Docs: https://yura52.github.io/zero/reference/api/zero.data.IndexLoader.html 196 | train_loader = delu.data.IndexLoader(len(X['train']), self.batch_size, device=self.device) 197 | 198 | # Create a progress tracker for early stopping 199 | # Docs: https://yura52.github.io/zero/reference/api/zero.ProgressTracker.html 200 | progress = delu.ProgressTracker(patience=100) 201 | 202 | # training 203 | # report_frequency = len(X['train']) // self.batch_size // 5 204 | 205 | for epoch in range(1, self.n_epochs + 1): 206 | loss_tmp = [] 207 | for iteration, batch_idx in enumerate(train_loader): 208 | self.model.train() 209 | optimizer.zero_grad() 210 | x_batch = X['train'][batch_idx] 211 | y_batch = y['train'][batch_idx] 212 | loss = loss_fn(self.apply_model(x_batch).squeeze(1), y_batch) 213 | loss_tmp.append(loss.item()) 214 | loss.backward() 215 | optimizer.step() 216 | # if iteration % report_frequency == 0: 217 | # print(f'(epoch) {epoch} (batch) {iteration} (loss) {loss.item():.4f}') 218 | logger.info(f"Epoch {epoch:03d}/{self.n_epochs} | batch_size:{self.batch_size} | iteration:{iteration} | batch_loss:{loss.item():.4f}") 219 | 220 | loss_.append(sum(loss_tmp)/len(loss_tmp)) 221 | _, val_metric = self.evaluate(X=X['val'], y=y['val']) 222 | print(f'Epoch {epoch:03d} | Validation metric: {val_metric:.4f}', end='') 223 | logger.info(f'Epoch {epoch:03d}/{self.n_epochs} | Validation metric: {val_metric:.4f} | Train loss:{sum(loss_tmp)/len(loss_tmp):.4f}') 224 | 225 | progress.update((-1 if task_type == 'regression' else 1) * val_metric) 226 | if progress.success: 227 | print(' <<< BEST VALIDATION EPOCH', end='') 228 | print() 229 | # 验证 230 | # output predicted anomaly score on testing set 231 | score = self.predict_score(X_test) 232 | # evaluation 233 | result = utils.metric(y_true=y_test, y_score=score) 234 | aucroc.append(result['aucroc']) 235 | aucpr.append(result['aucpr']) 236 | if progress.fail: 237 | break 238 | 239 | return self 240 | 241 | def predict_score(self, X): 242 | X = torch.from_numpy(X).float().to(self.device) 243 | score, _ = self.evaluate(X=X, y=None) 244 | return score 245 | 246 | if __name__ == "__main__": 247 | # 模型参数 248 | parser = argparse.ArgumentParser() 249 | parser.add_argument('--train', default='Train') 250 | parser.add_argument('--test', default='Test_A') 251 | parser.add_argument('--epoch',default=532) 252 | parser.add_argument('--batch_size',default=256) 253 | parser.add_argument('--model_name',default="ResNet", choices=["ResNet", "MLP", "FTTransformer"]) 254 | args = parser.parse_args() 255 | data_path3 = args.test 256 | epoch = int(args.epoch) 257 | batch_size = int(args.batch_size) 258 | model_name = args.model_name 259 | #log文件 260 | log = "log/train.log" 261 | if not os.path.isdir(os.path.dirname(log)): 262 | os.makedirs(os.path.dirname(log)) 263 | else: 264 | # 删除历史log 265 | if os.path.isfile(log): 266 | os.remove(log) 267 | logger.add(log, 268 | rotation="500MB", 269 | encoding="utf-8", 270 | enqueue=True, 271 | retention="10 days") 272 | 273 | # 加载训练集的pkl文件,划分训练集与验证集 274 | ind_pkl_files = []#存放标签为0的文件 275 | ood_pkl_files = []#存放标签为1的文件 276 | data_path=args.train#存放数据的路径 277 | pkl_files = glob(data_path+'/*.pkl') 278 | for each_path in tqdm(pkl_files): 279 | pic = open(each_path,'rb') 280 | this_pkl_file= pickle.load(pic)#下载pkl文件 281 | if this_pkl_file[1]['label'] == '00': 282 | ind_pkl_files.append(each_path) 283 | else: 284 | ood_pkl_files.append(each_path) 285 | 286 | random.seed(0) 287 | # 排序并打乱存放车辆序号的集合 288 | random.shuffle(ind_pkl_files) 289 | random.shuffle(ood_pkl_files) 290 | # 3/4的正样本和全部的负样本作为训练集,1/4的正样本和1/4的负样本作为训练集 291 | train_pkl_files = [ ind_pkl_files[j] for j in range(len(ind_pkl_files)//4,len(ind_pkl_files))] + [ ood_pkl_files[i] for i in range(len(ood_pkl_files))] 292 | test_pkl_files=[ind_pkl_files[i] for i in range(len(ind_pkl_files)//4)] + [ood_pkl_files[i] for i in range(len(ood_pkl_files)//4)] 293 | 294 | print(len(train_pkl_files)) 295 | print(len(test_pkl_files)) 296 | 297 | # 加载并归一化训练数据和验证数据 298 | X_train,y_train=load_data(train_pkl_files) 299 | # 进行随机打乱,这里random_state指定为固定值,则打乱结果相同 300 | X_train,y_train = shuffle(X_train,y_train,random_state=40) 301 | X_test,y_test=load_data(test_pkl_files) 302 | X_train = normalization(X_train) 303 | X_test = normalization(X_test) 304 | 305 | test1_files = glob(data_path3+'/*.pkl') 306 | X_val,_=load_data(test1_files,label=False) 307 | X_val = normalization(X_val) 308 | 309 | # 初始化模型。并进行训练 310 | seed = 42 311 | clf=FTTransformer(seed,model_name,n_epochs=epoch,batch_size=batch_size) 312 | aucroc = [] 313 | aucpr = [] 314 | loss_ = [] 315 | clf = clf.fit(X_train=X_train, y_train=y_train.squeeze(1),X_test=X_test,y_test=y_test) 316 | import platform 317 | y_val_scores = clf.predict_score(X_val) #返回未知数据上的异常值 (分值越大越异常) # outlier scores 318 | #记录文件名和对应的异常得分 319 | predict_result={} 320 | for i in tqdm(range(len(test1_files))): 321 | file=test1_files[i] 322 | #如果是window系统: 323 | if platform.system().lower() == 'windows': 324 | name=file.split('\\')[-1] 325 | #如果是linux系统 326 | elif platform.system().lower() == 'linux': 327 | name=file.split('/')[-1] 328 | predict_result[name]=y_val_scores[i] 329 | predict_score=pd.DataFrame(list(predict_result.items()),columns=['file_name','score'])#列名必须为这俩个 330 | predict_score.to_csv(f'submision.csv',index = False) #保存为比赛要求的csv文件 331 | # 保存模型 332 | torch.save(clf,"clf1.torch") 333 | # 绘制loss曲线 334 | plt.subplot(121) 335 | plt.plot(aucroc,label="aucroc") 336 | plt.plot(aucpr,label="aucpr") 337 | plt.legend() 338 | plt.subplot(122) 339 | plt.plot(loss_,label="loss") 340 | plt.legend() 341 | plt.savefig("loss.png") 342 | -------------------------------------------------------------------------------- /.ipynb_checkpoints/train_optuna-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | } 9 | }, 10 | "source": [ 11 | "# Run ADBench \n", 12 | "- Here we provide a demo for testing AD algorithms on the datasets proposed in ADBench.\n", 13 | "- Feel free to evaluate any customized algorithm in ADBench.\n", 14 | "- For reproducing the complete experiment results in ADBench, please run the code in the run.py file." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "# 调参" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 2, 27 | "metadata": {}, 28 | "outputs": [ 29 | { 30 | "name": "stderr", 31 | "output_type": "stream", 32 | "text": [ 33 | "D:\\autosoftware\\anconda\\lib\\site-packages\\pandas\\compat\\_optional.py:138: UserWarning: Pandas requires version '2.7.0' or newer of 'numexpr' (version '2.6.9' currently installed).\n", 34 | " warnings.warn(msg, UserWarning)\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "# -*- coding:utf-8 -*-\n", 40 | "import numpy as np\n", 41 | "from glob import glob\n", 42 | "import pickle\n", 43 | "import random\n", 44 | "from tqdm import tqdm\n", 45 | "import pandas as pd\n", 46 | "import argparse\n", 47 | "from sklearn.utils import shuffle\n", 48 | "#import sklearn.metrics\n", 49 | "#import sklearn.preprocessing\n", 50 | "import torch\n", 51 | "#import torch.nn as nn\n", 52 | "import torch.nn.functional as F\n", 53 | "import delu\n", 54 | "import rtdl\n", 55 | "import scipy\n", 56 | "import platform\n", 57 | "from matplotlib import pyplot as plt\n", 58 | "from myutils import Utils\n", 59 | "import optuna\n", 60 | "utils = Utils() # utils function\n", 61 | "\n", 62 | "\n", 63 | "\n", 64 | "def load_data(pkl_list,label=True):\n", 65 | " '''\n", 66 | " 输入pkl的列表,进行文件加载\n", 67 | " label=True用来加载训练集\n", 68 | " label=False用来加载真正的测试集,真正的测试集无标签\n", 69 | " '''\n", 70 | " X = []\n", 71 | " y = []\n", 72 | " \n", 73 | "\n", 74 | " for each_pkl in pkl_list:\n", 75 | " pic = open(each_pkl,'rb')\n", 76 | " item= pickle.load(pic)#下载pkl文件\n", 77 | " # 此处选取的是每个滑窗的最后一条数据,仅供参考,可以选择其他的方法,比如均值或者其他处理时序数据的网络\n", 78 | " # 此处选取了前7个特征,可以需求选取特征数量\n", 79 | " feature = item[0][:,0:7][-1]\n", 80 | " #feature = item[0][:,0:7][-1]\n", 81 | " #feature = item[0][:,0:7].mean(axis=0)\n", 82 | " #feature = np.append(item[0][:,0:7][-1],(item[0][:,3][-1] - item[0][:,4][-1])) #加max_single_volt - min_single_volt 一列为特征\n", 83 | " feature=np.append(feature,item[1][\"mileage\"])\n", 84 | " X.append(feature)\n", 85 | " if label:\n", 86 | " y.append(int(item[1]['label'][0]))\n", 87 | " X = np.vstack(X)\n", 88 | " if label:\n", 89 | " y = np.vstack(y)\n", 90 | " return X, y\n", 91 | " \n", 92 | "def normalization(data): \n", 93 | " \"\"\"\n", 94 | " 归一化数据\n", 95 | " \"\"\"\n", 96 | " _mean = np.mean(data, axis=0)\n", 97 | " _std = np.std(data, axis=0)\n", 98 | " data = (data - _mean) / (_std + 1e-4)\n", 99 | " return data\n", 100 | "\n", 101 | "\n", 102 | "class FTTransformer():\n", 103 | " '''\n", 104 | " The original code: https://yura52.github.io/rtdl/stable/index.html\n", 105 | " The original paper: \"Revisiting Deep Learning Models for Tabular Data\", NIPS 2019\n", 106 | " '''\n", 107 | " def __init__(self, seed:int, model_name:str, n_epochs=100, batch_size=64):\n", 108 | "\n", 109 | " self.seed = seed\n", 110 | " self.model_name = model_name\n", 111 | " self.utils = Utils()\n", 112 | "\n", 113 | " # device\n", 114 | " if model_name == 'FTTransformer':\n", 115 | " self.device = self.utils.get_device(gpu_specific=True)\n", 116 | " else:\n", 117 | " self.device = self.utils.get_device(gpu_specific=False)\n", 118 | "\n", 119 | " # Docs: https://yura52.github.io/zero/0.0.4/reference/api/zero.improve_reproducibility.html\n", 120 | " # zero.improve_reproducibility(seed=self.seed)\n", 121 | " delu.improve_reproducibility(base_seed=int(self.seed))\n", 122 | "\n", 123 | " # hyper-parameter\n", 124 | " self.n_epochs = n_epochs # default is 1000\n", 125 | " self.batch_size = batch_size # default is 256\n", 126 | "\n", 127 | " def apply_model(self, x_num, x_cat=None):\n", 128 | " if isinstance(self.model, rtdl.FTTransformer):\n", 129 | " return self.model(x_num, x_cat)\n", 130 | " elif isinstance(self.model, (rtdl.MLP, rtdl.ResNet)):\n", 131 | " assert x_cat is None\n", 132 | " return self.model(x_num)\n", 133 | " else:\n", 134 | " raise NotImplementedError(\n", 135 | " f'Looks like you are using a custom model: {type(self.model)}.'\n", 136 | " ' Then you have to implement this branch first.'\n", 137 | " )\n", 138 | "\n", 139 | " @torch.no_grad()\n", 140 | " def evaluate(self, X, y=None):\n", 141 | " self.model.eval()\n", 142 | " score = []\n", 143 | " # for batch in delu.iter_batches(X[part], 1024):\n", 144 | " for batch in delu.iter_batches(X, self.batch_size):\n", 145 | " score.append(self.apply_model(batch))\n", 146 | " score = torch.cat(score).squeeze(1).cpu().numpy()\n", 147 | " score = scipy.special.expit(score)\n", 148 | "\n", 149 | " # calculate the metric\n", 150 | " if y is not None:\n", 151 | " target = y.cpu().numpy()\n", 152 | " metric = self.utils.metric(y_true=target, y_score=score)\n", 153 | " else:\n", 154 | " metric = {'aucroc': None, 'aucpr': None}\n", 155 | "\n", 156 | " return score, metric['aucpr']\n", 157 | "\n", 158 | " def fit(self, X_train, y_train, ratio=None,X_test=None,y_test=None,params=None):\n", 159 | " # set seed\n", 160 | " self.utils.set_seed(self.seed)\n", 161 | " \n", 162 | " #X_train, X_test_val, y_train, y_test_val = train_test_split(X_train, y_train, test_size=0.33, random_state=42)\n", 163 | " # training set is used as the validation set in the anomaly detection task\n", 164 | " X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 165 | " 'val': torch.from_numpy(X_train).float().to(self.device)}\n", 166 | "\n", 167 | " y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 168 | " 'val': torch.from_numpy(y_train).float().to(self.device)}\n", 169 | " \n", 170 | " #training set is used as the validation set in the anomaly detection task\n", 171 | "# X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 172 | "# 'val': torch.from_numpy(X_test_val).float().to(self.device)}\n", 173 | "\n", 174 | "# y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 175 | "# 'val': torch.from_numpy(y_test_val).float().to(self.device)}\n", 176 | "\n", 177 | " task_type = 'binclass'\n", 178 | " n_classes = None\n", 179 | " d_out = n_classes or 1\n", 180 | "\n", 181 | " if self.model_name == 'ResNet':\n", 182 | " self.model = rtdl.ResNet.make_baseline(\n", 183 | " d_in=X_train.shape[1],\n", 184 | " d_main=128,\n", 185 | " d_hidden=256,\n", 186 | " dropout_first=params['dropout_first'],\n", 187 | " dropout_second=0.0,\n", 188 | " n_blocks=params['n_blocks'],\n", 189 | " d_out=d_out,\n", 190 | " )\n", 191 | " lr = params['learning_rate']\n", 192 | " weight_decay = 0.0\n", 193 | " \n", 194 | " elif self.model_name == 'MLP':\n", 195 | " self.model = rtdl.MLP.make_baseline(\n", 196 | " d_in=X_train.shape[1],\n", 197 | " d_layers= [128, 256, 128],\n", 198 | " dropout=0.25,\n", 199 | " d_out=d_out,\n", 200 | " )\n", 201 | " lr = 0.001\n", 202 | " weight_decay = 0.0\n", 203 | "\n", 204 | " elif self.model_name == 'FTTransformer':\n", 205 | " self.model = rtdl.FTTransformer.make_default(\n", 206 | " n_num_features=X_train.shape[1],\n", 207 | " cat_cardinalities=None,\n", 208 | " last_layer_query_idx=[-1], # it makes the model faster and does NOT affect its output\n", 209 | " d_out=d_out,\n", 210 | " )\n", 211 | " \n", 212 | " elif self.model_name == 'FTTransformer_baseline':\n", 213 | " self.model = rtdl.FTTransformer.make_baseline(\n", 214 | " n_num_features=X_train.shape[1],\n", 215 | " cat_cardinalities=None,\n", 216 | " d_token=X_train.shape[1],\n", 217 | " n_blocks=2,\n", 218 | " attention_dropout=0.2,\n", 219 | " ffn_d_hidden=6,\n", 220 | " ffn_dropout=0.2,\n", 221 | " residual_dropout=0.0,\n", 222 | " d_out=d_out,\n", 223 | " ) \n", 224 | " else:\n", 225 | " raise NotImplementedError\n", 226 | "\n", 227 | " self.model.to(self.device)\n", 228 | " optimizer = (\n", 229 | " self.model.make_default_optimizer()\n", 230 | " if isinstance(self.model, rtdl.FTTransformer)\n", 231 | " else torch.optim.AdamW(self.model.parameters(), lr=lr, weight_decay=weight_decay)\n", 232 | " )\n", 233 | " loss_fn = (\n", 234 | " F.binary_cross_entropy_with_logits\n", 235 | " if task_type == 'binclass'\n", 236 | " else F.cross_entropy\n", 237 | " if task_type == 'multiclass'\n", 238 | " else F.mse_loss\n", 239 | " )\n", 240 | "\n", 241 | " # Create a dataloader for batches of indices\n", 242 | " # Docs: https://yura52.github.io/zero/reference/api/zero.data.IndexLoader.html\n", 243 | " train_loader = delu.data.IndexLoader(len(X['train']), self.batch_size, device=self.device)\n", 244 | "\n", 245 | " # Create a progress tracker for early stopping\n", 246 | " # Docs: https://yura52.github.io/zero/reference/api/zero.ProgressTracker.html\n", 247 | " progress = delu.ProgressTracker(patience=100)\n", 248 | "\n", 249 | " # training\n", 250 | " # report_frequency = len(X['train']) // self.batch_size // 5\n", 251 | " aucroc = []\n", 252 | " aucpr = []\n", 253 | " loss_ = []\n", 254 | " for epoch in range(1, self.n_epochs + 1):\n", 255 | " loss_tmp = []\n", 256 | " for iteration, batch_idx in enumerate(train_loader):\n", 257 | " self.model.train()\n", 258 | " optimizer.zero_grad()\n", 259 | " x_batch = X['train'][batch_idx]\n", 260 | " y_batch = y['train'][batch_idx]\n", 261 | " loss = loss_fn(self.apply_model(x_batch).squeeze(1), y_batch)\n", 262 | " loss_tmp.append(loss.item())\n", 263 | " loss.backward()\n", 264 | " optimizer.step()\n", 265 | " # if iteration % report_frequency == 0:\n", 266 | " # print(f'(epoch) {epoch} (batch) {iteration} (loss) {loss.item():.4f}')\n", 267 | "\n", 268 | " loss_.append(sum(loss_tmp)/len(loss_tmp))\n", 269 | " _, val_metric = self.evaluate(X=X['val'], y=y['val'])\n", 270 | " print(f'Epoch {epoch:03d} | Validation metric: {val_metric:.4f}', end='')\n", 271 | " progress.update((-1 if task_type == 'regression' else 1) * val_metric)\n", 272 | " if progress.success:\n", 273 | " print(' <<< BEST VALIDATION EPOCH', end='')\n", 274 | " print()\n", 275 | " # 验证\n", 276 | " # output predicted anomaly score on testing set\n", 277 | " score = self.predict_score(X_test)\n", 278 | " # evaluation\n", 279 | " result = utils.metric(y_true=y_test, y_score=score)\n", 280 | " aucroc.append(result['aucroc'])\n", 281 | " aucpr.append(result['aucpr'])\n", 282 | " if progress.fail:\n", 283 | " break\n", 284 | " return result['aucroc']\n", 285 | " #return self\n", 286 | "\n", 287 | " def predict_score(self, X):\n", 288 | " X = torch.from_numpy(X).float().to(self.device)\n", 289 | " score, _ = self.evaluate(X=X, y=None)\n", 290 | " return score" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": {}, 297 | "outputs": [ 298 | { 299 | "name": "stderr", 300 | "output_type": "stream", 301 | "text": [ 302 | "100%|███████████████████████████████████████████████████████████████████████████| 28389/28389 [03:54<00:00, 120.82it/s]\n" 303 | ] 304 | }, 305 | { 306 | "name": "stdout", 307 | "output_type": "stream", 308 | "text": [ 309 | "22456\n", 310 | "7096\n" 311 | ] 312 | } 313 | ], 314 | "source": [ 315 | "data_path3 = \"Test_A\"\n", 316 | "epoch = 10\n", 317 | "batch_size = 256\n", 318 | "model_name = \"ResNet\"\n", 319 | "# 加载训练集的pkl文件,划分训练集与验证集\n", 320 | "ind_pkl_files = []#存放标签为0的文件\n", 321 | "ood_pkl_files = []#存放标签为1的文件\n", 322 | "data_path=\"Train\"#存放数据的路径\n", 323 | "pkl_files = glob(data_path+'/*.pkl')\n", 324 | "for each_path in tqdm(pkl_files):\n", 325 | " pic = open(each_path,'rb')\n", 326 | " this_pkl_file= pickle.load(pic)#下载pkl文件\n", 327 | " if this_pkl_file[1]['label'] == '00':\n", 328 | " ind_pkl_files.append(each_path)\n", 329 | " else:\n", 330 | " ood_pkl_files.append(each_path)\n", 331 | "\n", 332 | "random.seed(0)\n", 333 | "# 排序并打乱存放车辆序号的集合\n", 334 | "random.shuffle(ind_pkl_files)\n", 335 | "random.shuffle(ood_pkl_files)\n", 336 | "# 3/4的正样本和全部的负样本作为训练集,1/4的正样本和1/4的负样本作为训练集\n", 337 | "train_pkl_files = [ ind_pkl_files[j] for j in range(len(ind_pkl_files)//4,len(ind_pkl_files))] + [ ood_pkl_files[i] for i in range(len(ood_pkl_files))]\n", 338 | "test_pkl_files=[ind_pkl_files[i] for i in range(len(ind_pkl_files)//4)] + [ood_pkl_files[i] for i in range(len(ood_pkl_files)//4)]\n", 339 | "\n", 340 | "print(len(train_pkl_files))\n", 341 | "print(len(test_pkl_files))\n", 342 | "\n", 343 | "# 加载并归一化训练数据和验证数据\n", 344 | "X_train,y_train=load_data(train_pkl_files)\n", 345 | "# 进行随机打乱,这里random_state指定为固定值,则打乱结果相同\n", 346 | "X_train,y_train = shuffle(X_train,y_train,random_state=40)\n", 347 | "X_test,y_test=load_data(test_pkl_files)\n", 348 | "X_train = normalization(X_train)\n", 349 | "X_test = normalization(X_test)\n", 350 | "\n", 351 | "test1_files = glob(data_path3+'/*.pkl')\n", 352 | "X_val,_=load_data(test1_files,label=False)\n", 353 | "X_val = normalization(X_val)" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "def objective(trial):\n", 363 | " seed = 42\n", 364 | " clf=FTTransformer(seed,model_name,n_epochs=10,batch_size=batch_size)\n", 365 | "\n", 366 | " params = {\n", 367 | " 'learning_rate': trial.suggest_float(\"lr\", 1e-5, 1e-1, log=True),\n", 368 | " 'dropout_first': trial.suggest_float('dropout_first', 0.1, 0.5),\n", 369 | " 'n_blocks': trial.suggest_int(\"n_blocks\", 1, 4),\n", 370 | " }\n", 371 | "\n", 372 | " accuracy = clf.fit(X_train=X_train, y_train=y_train.squeeze(1),X_test=X_test,y_test=y_test,params=params)\n", 373 | "\n", 374 | " return accuracy\n", 375 | "\n", 376 | "study = optuna.create_study(direction=\"maximize\", sampler=optuna.samplers.TPESampler())\n", 377 | "study.optimize(objective, n_trials=10)\n", 378 | "best_trial = study.best_trial\n", 379 | "print(best_trial.value)\n", 380 | "for key, value in best_trial.params.items():\n", 381 | " print(\"{}: {}\".format(key, value))" 382 | ] 383 | } 384 | ], 385 | "metadata": { 386 | "kernelspec": { 387 | "display_name": "Python 3", 388 | "language": "python", 389 | "name": "python3" 390 | }, 391 | "language_info": { 392 | "codemirror_mode": { 393 | "name": "ipython", 394 | "version": 3 395 | }, 396 | "file_extension": ".py", 397 | "mimetype": "text/x-python", 398 | "name": "python", 399 | "nbconvert_exporter": "python", 400 | "pygments_lexer": "ipython3", 401 | "version": "3.7.3" 402 | }, 403 | "toc": { 404 | "base_numbering": 1, 405 | "nav_menu": {}, 406 | "number_sections": true, 407 | "sideBar": true, 408 | "skip_h1_title": false, 409 | "title_cell": "Table of Contents", 410 | "title_sidebar": "Contents", 411 | "toc_cell": false, 412 | "toc_position": {}, 413 | "toc_section_display": true, 414 | "toc_window_display": false 415 | }, 416 | "varInspector": { 417 | "cols": { 418 | "lenName": 16, 419 | "lenType": 16, 420 | "lenVar": 40 421 | }, 422 | "kernels_config": { 423 | "python": { 424 | "delete_cmd_postfix": "", 425 | "delete_cmd_prefix": "del ", 426 | "library": "var_list.py", 427 | "varRefreshCmd": "print(var_dic_list())" 428 | }, 429 | "r": { 430 | "delete_cmd_postfix": ") ", 431 | "delete_cmd_prefix": "rm(", 432 | "library": "var_list.r", 433 | "varRefreshCmd": "cat(var_dic_list()) " 434 | } 435 | }, 436 | "types_to_exclude": [ 437 | "module", 438 | "function", 439 | "builtin_function_or_method", 440 | "instance", 441 | "_Feature" 442 | ], 443 | "window_display": false 444 | } 445 | }, 446 | "nbformat": 4, 447 | "nbformat_minor": 4 448 | } 449 | -------------------------------------------------------------------------------- /.ipynb_checkpoints/demo_resnet_epoch532_lr0.001_dropout0.25_auc0.8998-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | } 9 | }, 10 | "source": [ 11 | "# Run ADBench \n", 12 | "- Here we provide a demo for testing AD algorithms on the datasets proposed in ADBench.\n", 13 | "- Feel free to evaluate any customized algorithm in ADBench.\n", 14 | "- For reproducing the complete experiment results in ADBench, please run the code in the run.py file." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stderr", 24 | "output_type": "stream", 25 | "text": [ 26 | "D:\\autosoftware\\anconda\\lib\\site-packages\\pandas\\compat\\_optional.py:138: UserWarning: Pandas requires version '2.7.0' or newer of 'numexpr' (version '2.6.9' currently installed).\n", 27 | " warnings.warn(msg, UserWarning)\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "import numpy as np\n", 33 | "from glob import glob\n", 34 | "import pickle\n", 35 | "import random\n", 36 | "from tqdm import tqdm\n", 37 | "import pandas as pd" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "# 加载训练集的pkl文件,划分训练集与验证集\n", 45 | "训练集的label存放在pkl里面,可以通过它并区分正常片段和异常片段\n", 46 | "注意需要输入训练集对应的路径" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "name": "stderr", 56 | "output_type": "stream", 57 | "text": [ 58 | " 20%|███████████████▏ | 5664/28389 [00:24<01:17, 294.25it/s]" 59 | ] 60 | } 61 | ], 62 | "source": [ 63 | "ind_pkl_files = []#存放标签为0的文件\n", 64 | "ood_pkl_files = []#存放标签为1的文件\n", 65 | "data_path='Train'#存放数据的路径\n", 66 | "pkl_files = glob(data_path+'/*.pkl')\n", 67 | "for each_path in tqdm(pkl_files):\n", 68 | " pic = open(each_path,'rb')\n", 69 | " this_pkl_file= pickle.load(pic)#下载pkl文件\n", 70 | " if this_pkl_file[1]['label'] == '00':\n", 71 | " ind_pkl_files.append(each_path)\n", 72 | " else:\n", 73 | " ood_pkl_files.append(each_path)\n", 74 | "\n", 75 | "random.seed(0)\n", 76 | "#排序并打乱存放车辆序号的集合\n", 77 | "random.shuffle(ind_pkl_files)\n", 78 | "random.shuffle(ood_pkl_files)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "print(len(ind_pkl_files))\n", 88 | "print(len(ood_pkl_files))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "train_pkl_files=[]\n", 98 | "for j in range(len(ind_pkl_files)//4,len(ind_pkl_files)):\n", 99 | "#for j in range(len(ind_pkl_files)):\n", 100 | " train_pkl_files.append(ind_pkl_files[j])\n", 101 | "#for i in range(len(ind_pkl_files)//4):\n", 102 | "# train_pkl_files.append(ind_pkl_files[i])\n", 103 | "for j in range(len(ood_pkl_files)):\n", 104 | " train_pkl_files.append(ood_pkl_files[j])\n", 105 | "print(len(train_pkl_files))\n", 106 | "test_pkl_files=[]\n", 107 | "#for j in range(len(ind_pkl_files)//4,len(ind_pkl_files)):\n", 108 | " # test_pkl_files.append(ind_pkl_files[j])\n", 109 | "for i in range(len(ind_pkl_files)//4):\n", 110 | " test_pkl_files.append(ind_pkl_files[i])\n", 111 | "#for item in ood_pkl_files:\n", 112 | "# test_pkl_files.append(item)\n", 113 | "\n", 114 | "for j in range(len(ood_pkl_files)//4):\n", 115 | " test_pkl_files.append(ood_pkl_files[j])\n", 116 | "print(len(test_pkl_files))" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "# 定义加载函数,并对数据进行正则化" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "def load_data(pkl_list,label=True):\n", 133 | " '''\n", 134 | " 输入pkl的列表,进行文件加载\n", 135 | " label=True用来加载训练集\n", 136 | " label=False用来加载真正的测试集,真正的测试集无标签\n", 137 | " '''\n", 138 | " X = []\n", 139 | " y = []\n", 140 | " \n", 141 | "\n", 142 | " for each_pkl in pkl_list:\n", 143 | " pic = open(each_pkl,'rb')\n", 144 | " item= pickle.load(pic)#下载pkl文件\n", 145 | " # 此处选取的是每个滑窗的最后一条数据,仅供参考,可以选择其他的方法,比如均值或者其他处理时序数据的网络\n", 146 | " # 此处选取了前7个特征,可以需求选取特征数量\n", 147 | " feature = item[0][:,0:7][-1]\n", 148 | " #feature = item[0][:,0:7][-1]\n", 149 | " #feature = item[0][:,0:7].mean(axis=0)\n", 150 | " #feature = np.append(item[0][:,0:7][-1],(item[0][:,3][-1] - item[0][:,4][-1])) #加max_single_volt - min_single_volt 一列为特征\n", 151 | " feature=np.append(feature,item[1][\"mileage\"])\n", 152 | " X.append(feature)\n", 153 | " if label:\n", 154 | " y.append(int(item[1]['label'][0]))\n", 155 | " X = np.vstack(X)\n", 156 | " if label:\n", 157 | " y = np.vstack(y)\n", 158 | " return X, y" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "X_train,y_train=load_data(train_pkl_files)\n", 168 | "X_test,y_test=load_data(test_pkl_files)\n", 169 | "_mean = np.mean(X_train, axis=0)\n", 170 | "_std = np.std(X_train, axis=0)\n", 171 | "X_train = (X_train - _mean) / (_std + 1e-4)\n", 172 | "X_test = (X_test - _mean) / (_std + 1e-4)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "\n", 182 | "from sklearn.utils import shuffle\n", 183 | "# 进行随机打乱,这里random_state指定为固定值,则打乱结果相同\n", 184 | "X_train,y_train = shuffle(X_train,y_train,random_state=40)\n" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "metadata": {}, 191 | "outputs": [], 192 | "source": [ 193 | "data_path3='Test_A'\n", 194 | "test1_files = glob(data_path3+'/*.pkl')\n", 195 | "X_val,_=load_data(test1_files,label=False)\n", 196 | "_mean = np.mean(X_val, axis=0)\n", 197 | "_std = np.std(X_val, axis=0)\n", 198 | "X_val = (X_val - _mean) / (_std + 1e-4)" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "import sklearn.metrics\n", 208 | "from sklearn.model_selection import train_test_split\n", 209 | "import sklearn.preprocessing\n", 210 | "import torch\n", 211 | "import torch.nn as nn\n", 212 | "import torch.nn.functional as F\n", 213 | "# import zero\n", 214 | "import delu # the new version of zero package\n", 215 | "import rtdl\n", 216 | "import scipy\n", 217 | "import platform\n", 218 | "from myutils import Utils\n", 219 | "utils = Utils() # utils function\n", 220 | "\n", 221 | "\n", 222 | "class FTTransformer():\n", 223 | " '''\n", 224 | " The original code: https://yura52.github.io/rtdl/stable/index.html\n", 225 | " The original paper: \"Revisiting Deep Learning Models for Tabular Data\", NIPS 2019\n", 226 | " '''\n", 227 | " def __init__(self, seed:int, model_name:str, n_epochs=100, batch_size=64):\n", 228 | "\n", 229 | " self.seed = seed\n", 230 | " self.model_name = model_name\n", 231 | " self.utils = Utils()\n", 232 | "\n", 233 | " # device\n", 234 | " if model_name == 'FTTransformer':\n", 235 | " self.device = self.utils.get_device(gpu_specific=True)\n", 236 | " else:\n", 237 | " self.device = self.utils.get_device(gpu_specific=False)\n", 238 | "\n", 239 | " # Docs: https://yura52.github.io/zero/0.0.4/reference/api/zero.improve_reproducibility.html\n", 240 | " # zero.improve_reproducibility(seed=self.seed)\n", 241 | " delu.improve_reproducibility(base_seed=int(self.seed))\n", 242 | "\n", 243 | " # hyper-parameter\n", 244 | " self.n_epochs = n_epochs # default is 1000\n", 245 | " self.batch_size = batch_size # default is 256\n", 246 | "\n", 247 | " def apply_model(self, x_num, x_cat=None):\n", 248 | " if isinstance(self.model, rtdl.FTTransformer):\n", 249 | " return self.model(x_num, x_cat)\n", 250 | " elif isinstance(self.model, (rtdl.MLP, rtdl.ResNet)):\n", 251 | " assert x_cat is None\n", 252 | " return self.model(x_num)\n", 253 | " else:\n", 254 | " raise NotImplementedError(\n", 255 | " f'Looks like you are using a custom model: {type(self.model)}.'\n", 256 | " ' Then you have to implement this branch first.'\n", 257 | " )\n", 258 | "\n", 259 | " @torch.no_grad()\n", 260 | " def evaluate(self, X, y=None):\n", 261 | " self.model.eval()\n", 262 | " score = []\n", 263 | " # for batch in delu.iter_batches(X[part], 1024):\n", 264 | " for batch in delu.iter_batches(X, self.batch_size):\n", 265 | " score.append(self.apply_model(batch))\n", 266 | " score = torch.cat(score).squeeze(1).cpu().numpy()\n", 267 | " score = scipy.special.expit(score)\n", 268 | "\n", 269 | " # calculate the metric\n", 270 | " if y is not None:\n", 271 | " target = y.cpu().numpy()\n", 272 | " metric = self.utils.metric(y_true=target, y_score=score)\n", 273 | " else:\n", 274 | " metric = {'aucroc': None, 'aucpr': None}\n", 275 | "\n", 276 | " return score, metric['aucpr']\n", 277 | "\n", 278 | " def fit(self, X_train, y_train, ratio=None,X_test=X_test,y_test=y_test):\n", 279 | " # set seed\n", 280 | " self.utils.set_seed(self.seed)\n", 281 | " \n", 282 | " #X_train, X_test_val, y_train, y_test_val = train_test_split(X_train, y_train, test_size=0.33, random_state=42)\n", 283 | " # training set is used as the validation set in the anomaly detection task\n", 284 | " X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 285 | " 'val': torch.from_numpy(X_train).float().to(self.device)}\n", 286 | "\n", 287 | " y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 288 | " 'val': torch.from_numpy(y_train).float().to(self.device)}\n", 289 | " \n", 290 | " #training set is used as the validation set in the anomaly detection task\n", 291 | "# X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 292 | "# 'val': torch.from_numpy(X_test_val).float().to(self.device)}\n", 293 | "\n", 294 | "# y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 295 | "# 'val': torch.from_numpy(y_test_val).float().to(self.device)}\n", 296 | "\n", 297 | " task_type = 'binclass'\n", 298 | " n_classes = None\n", 299 | " d_out = n_classes or 1\n", 300 | "\n", 301 | "\n", 302 | " if self.model_name == 'ResNet':\n", 303 | " self.model = rtdl.ResNet.make_baseline(\n", 304 | " d_in=X_train.shape[1],\n", 305 | " d_main=128,\n", 306 | " d_hidden=256,\n", 307 | " dropout_first=0.25,\n", 308 | " dropout_second=0,\n", 309 | " n_blocks=2,\n", 310 | " d_out=d_out,\n", 311 | " )\n", 312 | " lr = 0.001\n", 313 | " weight_decay = 0.0\n", 314 | " \n", 315 | " elif self.model_name == 'MLP':\n", 316 | " self.model = rtdl.MLP.make_baseline(\n", 317 | " d_in=X_train.shape[1],\n", 318 | " d_layers= [128, 256, 128],\n", 319 | " dropout=0.25,\n", 320 | " d_out=d_out,\n", 321 | " )\n", 322 | " lr = 0.001\n", 323 | " weight_decay = 0.0\n", 324 | "\n", 325 | " elif self.model_name == 'FTTransformer':\n", 326 | " self.model = rtdl.FTTransformer.make_default(\n", 327 | " n_num_features=X_train.shape[1],\n", 328 | " cat_cardinalities=None,\n", 329 | " last_layer_query_idx=[-1], # it makes the model faster and does NOT affect its output\n", 330 | " d_out=d_out,\n", 331 | " )\n", 332 | " \n", 333 | " elif self.model_name == 'FTTransformer_baseline':\n", 334 | " self.model = rtdl.FTTransformer.make_baseline(\n", 335 | " n_num_features=X_train.shape[1],\n", 336 | " cat_cardinalities=None,\n", 337 | " d_token=X_train.shape[1],\n", 338 | " n_blocks=2,\n", 339 | " attention_dropout=0.2,\n", 340 | " ffn_d_hidden=6,\n", 341 | " ffn_dropout=0.2,\n", 342 | " residual_dropout=0.0,\n", 343 | " d_out=d_out,\n", 344 | " ) \n", 345 | " else:\n", 346 | " raise NotImplementedError\n", 347 | "\n", 348 | " self.model.to(self.device)\n", 349 | " optimizer = (\n", 350 | " self.model.make_default_optimizer()\n", 351 | " if isinstance(self.model, rtdl.FTTransformer)\n", 352 | " else torch.optim.AdamW(self.model.parameters(), lr=lr, weight_decay=weight_decay)\n", 353 | " )\n", 354 | " loss_fn = (\n", 355 | " F.binary_cross_entropy_with_logits\n", 356 | " if task_type == 'binclass'\n", 357 | " else F.cross_entropy\n", 358 | " if task_type == 'multiclass'\n", 359 | " else F.mse_loss\n", 360 | " )\n", 361 | "\n", 362 | " # Create a dataloader for batches of indices\n", 363 | " # Docs: https://yura52.github.io/zero/reference/api/zero.data.IndexLoader.html\n", 364 | " train_loader = delu.data.IndexLoader(len(X['train']), self.batch_size, device=self.device)\n", 365 | "\n", 366 | " # Create a progress tracker for early stopping\n", 367 | " # Docs: https://yura52.github.io/zero/reference/api/zero.ProgressTracker.html\n", 368 | " progress = delu.ProgressTracker(patience=100)\n", 369 | "\n", 370 | " # training\n", 371 | " # report_frequency = len(X['train']) // self.batch_size // 5\n", 372 | "\n", 373 | " for epoch in range(1, self.n_epochs + 1):\n", 374 | " loss_tmp = []\n", 375 | " for iteration, batch_idx in enumerate(train_loader):\n", 376 | " self.model.train()\n", 377 | " optimizer.zero_grad()\n", 378 | " x_batch = X['train'][batch_idx]\n", 379 | " y_batch = y['train'][batch_idx]\n", 380 | " loss = loss_fn(self.apply_model(x_batch).squeeze(1), y_batch)\n", 381 | " loss_tmp.append(loss.item())\n", 382 | " loss.backward()\n", 383 | " optimizer.step()\n", 384 | " # if iteration % report_frequency == 0:\n", 385 | " # print(f'(epoch) {epoch} (batch) {iteration} (loss) {loss.item():.4f}')\n", 386 | "\n", 387 | " loss_.append(sum(loss_tmp)/len(loss_tmp))\n", 388 | " _, val_metric = self.evaluate(X=X['val'], y=y['val'])\n", 389 | " print(f'Epoch {epoch:03d} | Validation metric: {val_metric:.4f}', end='')\n", 390 | " progress.update((-1 if task_type == 'regression' else 1) * val_metric)\n", 391 | " if progress.success:\n", 392 | " print(' <<< BEST VALIDATION EPOCH', end='')\n", 393 | " print()\n", 394 | " # 验证\n", 395 | " # output predicted anomaly score on testing set\n", 396 | " score = self.predict_score(X_test)\n", 397 | " # evaluation\n", 398 | " result = utils.metric(y_true=y_test, y_score=score)\n", 399 | " aucroc.append(result['aucroc'])\n", 400 | " aucpr.append(result['aucpr'])\n", 401 | " if progress.fail:\n", 402 | " break\n", 403 | "\n", 404 | " return self\n", 405 | "\n", 406 | " def predict_score(self, X):\n", 407 | " X = torch.from_numpy(X).float().to(self.device)\n", 408 | " score, _ = self.evaluate(X=X, y=None)\n", 409 | " return score" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": null, 415 | "metadata": {}, 416 | "outputs": [], 417 | "source": [ 418 | "seed = 42\n", 419 | "clf=FTTransformer(seed,\"ResNet\",n_epochs=532,batch_size=256)\n", 420 | "aucroc = []\n", 421 | "aucpr = []\n", 422 | "loss_ = []\n", 423 | "clf = clf.fit(X_train=X_train, y_train=y_train.squeeze(1),X_test=X_test,y_test=y_test)\n", 424 | "import platform\n", 425 | "y_val_scores = clf.predict_score(X_val) #返回未知数据上的异常值 (分值越大越异常) # outlier scores\n", 426 | "#记录文件名和对应的异常得分\n", 427 | "predict_result={}\n", 428 | "for i in tqdm(range(len(test1_files))):\n", 429 | " file=test1_files[i]\n", 430 | " #如果是window系统:\n", 431 | " if platform.system().lower() == 'windows':\n", 432 | " name=file.split('\\\\')[-1]\n", 433 | " #如果是linux系统\n", 434 | " elif platform.system().lower() == 'linux':\n", 435 | " name=file.split('/')[-1]\n", 436 | " predict_result[name]=y_val_scores[i]\n", 437 | "predict_score=pd.DataFrame(list(predict_result.items()),columns=['file_name','score'])#列名必须为这俩个\n", 438 | "predict_score.to_csv(f'submision.csv',index = False) #保存为比赛要求的csv文件" 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": null, 444 | "metadata": {}, 445 | "outputs": [], 446 | "source": [ 447 | "from matplotlib import pyplot as plt\n", 448 | "plt.figure(figsize=((10,8)))\n", 449 | "plt.subplot(221)\n", 450 | "plt.plot(aucroc,label=\"aucroc\")\n", 451 | "plt.plot(aucpr,label=\"aucpr\")\n", 452 | "plt.legend()\n", 453 | "plt.subplot(222)\n", 454 | "plt.plot(loss_,label=\"loss\")\n", 455 | "plt.legend()\n", 456 | "plt.show()\n", 457 | "print(aucroc.index(max(aucroc)))\n", 458 | "print(aucpr.index(max(aucpr)))" 459 | ] 460 | }, 461 | { 462 | "cell_type": "code", 463 | "execution_count": null, 464 | "metadata": {}, 465 | "outputs": [], 466 | "source": [ 467 | "clf.model" 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": null, 473 | "metadata": {}, 474 | "outputs": [], 475 | "source": [ 476 | "from thop import clever_format\n", 477 | "from thop import profile\n", 478 | "input = torch.randn(256, 8)\n", 479 | "flops, params =profile(clf.model, inputs=(input,))\n", 480 | "flops, params = clever_format([flops, params], \"%.3f\")\n", 481 | "print(flops)\n", 482 | "print(params)" 483 | ] 484 | } 485 | ], 486 | "metadata": { 487 | "kernelspec": { 488 | "display_name": "Python 3", 489 | "language": "python", 490 | "name": "python3" 491 | }, 492 | "language_info": { 493 | "codemirror_mode": { 494 | "name": "ipython", 495 | "version": 3 496 | }, 497 | "file_extension": ".py", 498 | "mimetype": "text/x-python", 499 | "name": "python", 500 | "nbconvert_exporter": "python", 501 | "pygments_lexer": "ipython3", 502 | "version": "3.7.3" 503 | }, 504 | "toc": { 505 | "base_numbering": 1, 506 | "nav_menu": {}, 507 | "number_sections": true, 508 | "sideBar": true, 509 | "skip_h1_title": false, 510 | "title_cell": "Table of Contents", 511 | "title_sidebar": "Contents", 512 | "toc_cell": false, 513 | "toc_position": {}, 514 | "toc_section_display": true, 515 | "toc_window_display": false 516 | }, 517 | "varInspector": { 518 | "cols": { 519 | "lenName": 16, 520 | "lenType": 16, 521 | "lenVar": 40 522 | }, 523 | "kernels_config": { 524 | "python": { 525 | "delete_cmd_postfix": "", 526 | "delete_cmd_prefix": "del ", 527 | "library": "var_list.py", 528 | "varRefreshCmd": "print(var_dic_list())" 529 | }, 530 | "r": { 531 | "delete_cmd_postfix": ") ", 532 | "delete_cmd_prefix": "rm(", 533 | "library": "var_list.r", 534 | "varRefreshCmd": "cat(var_dic_list()) " 535 | } 536 | }, 537 | "types_to_exclude": [ 538 | "module", 539 | "function", 540 | "builtin_function_or_method", 541 | "instance", 542 | "_Feature" 543 | ], 544 | "window_display": false 545 | } 546 | }, 547 | "nbformat": 4, 548 | "nbformat_minor": 4 549 | } 550 | -------------------------------------------------------------------------------- /train_optuna.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | } 9 | }, 10 | "source": [ 11 | "# Run ADBench \n", 12 | "- Here we provide a demo for testing AD algorithms on the datasets proposed in ADBench.\n", 13 | "- Feel free to evaluate any customized algorithm in ADBench.\n", 14 | "- For reproducing the complete experiment results in ADBench, please run the code in the run.py file." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "# 调参" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [ 29 | { 30 | "name": "stderr", 31 | "output_type": "stream", 32 | "text": [ 33 | "D:\\autosoftware\\anconda\\lib\\site-packages\\pandas\\compat\\_optional.py:138: UserWarning: Pandas requires version '2.7.0' or newer of 'numexpr' (version '2.6.9' currently installed).\n", 34 | " warnings.warn(msg, UserWarning)\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "# -*- coding:utf-8 -*-\n", 40 | "import numpy as np\n", 41 | "from glob import glob\n", 42 | "import pickle\n", 43 | "import random\n", 44 | "from tqdm import tqdm\n", 45 | "import pandas as pd\n", 46 | "import argparse\n", 47 | "from sklearn.utils import shuffle\n", 48 | "#import sklearn.metrics\n", 49 | "#import sklearn.preprocessing\n", 50 | "import torch\n", 51 | "#import torch.nn as nn\n", 52 | "import torch.nn.functional as F\n", 53 | "import delu\n", 54 | "import rtdl\n", 55 | "import scipy\n", 56 | "import platform\n", 57 | "from matplotlib import pyplot as plt\n", 58 | "from myutils import Utils\n", 59 | "import optuna\n", 60 | "utils = Utils() # utils function\n", 61 | "\n", 62 | "\n", 63 | "\n", 64 | "def load_data(pkl_list,label=True):\n", 65 | " '''\n", 66 | " 输入pkl的列表,进行文件加载\n", 67 | " label=True用来加载训练集\n", 68 | " label=False用来加载真正的测试集,真正的测试集无标签\n", 69 | " '''\n", 70 | " X = []\n", 71 | " y = []\n", 72 | " \n", 73 | "\n", 74 | " for each_pkl in pkl_list:\n", 75 | " pic = open(each_pkl,'rb')\n", 76 | " item= pickle.load(pic)#下载pkl文件\n", 77 | " # 此处选取的是每个滑窗的最后一条数据,仅供参考,可以选择其他的方法,比如均值或者其他处理时序数据的网络\n", 78 | " # 此处选取了前7个特征,可以需求选取特征数量\n", 79 | " feature = item[0][:,0:7][-1]\n", 80 | " #feature = item[0][:,0:7][-1]\n", 81 | " #feature = item[0][:,0:7].mean(axis=0)\n", 82 | " #feature = np.append(item[0][:,0:7][-1],(item[0][:,3][-1] - item[0][:,4][-1])) #加max_single_volt - min_single_volt 一列为特征\n", 83 | " feature=np.append(feature,item[1][\"mileage\"])\n", 84 | " X.append(feature)\n", 85 | " if label:\n", 86 | " y.append(int(item[1]['label'][0]))\n", 87 | " X = np.vstack(X)\n", 88 | " if label:\n", 89 | " y = np.vstack(y)\n", 90 | " return X, y\n", 91 | " \n", 92 | "def normalization(data): \n", 93 | " \"\"\"\n", 94 | " 归一化数据\n", 95 | " \"\"\"\n", 96 | " _mean = np.mean(data, axis=0)\n", 97 | " _std = np.std(data, axis=0)\n", 98 | " data = (data - _mean) / (_std + 1e-4)\n", 99 | " return data\n", 100 | "\n", 101 | "\n", 102 | "class FTTransformer():\n", 103 | " '''\n", 104 | " The original code: https://yura52.github.io/rtdl/stable/index.html\n", 105 | " The original paper: \"Revisiting Deep Learning Models for Tabular Data\", NIPS 2019\n", 106 | " '''\n", 107 | " def __init__(self, seed:int, model_name:str, n_epochs=100, batch_size=64):\n", 108 | "\n", 109 | " self.seed = seed\n", 110 | " self.model_name = model_name\n", 111 | " self.utils = Utils()\n", 112 | "\n", 113 | " # device\n", 114 | " if model_name == 'FTTransformer':\n", 115 | " self.device = self.utils.get_device(gpu_specific=True)\n", 116 | " else:\n", 117 | " self.device = self.utils.get_device(gpu_specific=False)\n", 118 | "\n", 119 | " # Docs: https://yura52.github.io/zero/0.0.4/reference/api/zero.improve_reproducibility.html\n", 120 | " # zero.improve_reproducibility(seed=self.seed)\n", 121 | " delu.improve_reproducibility(base_seed=int(self.seed))\n", 122 | "\n", 123 | " # hyper-parameter\n", 124 | " self.n_epochs = n_epochs # default is 1000\n", 125 | " self.batch_size = batch_size # default is 256\n", 126 | "\n", 127 | " def apply_model(self, x_num, x_cat=None):\n", 128 | " if isinstance(self.model, rtdl.FTTransformer):\n", 129 | " return self.model(x_num, x_cat)\n", 130 | " elif isinstance(self.model, (rtdl.MLP, rtdl.ResNet)):\n", 131 | " assert x_cat is None\n", 132 | " return self.model(x_num)\n", 133 | " else:\n", 134 | " raise NotImplementedError(\n", 135 | " f'Looks like you are using a custom model: {type(self.model)}.'\n", 136 | " ' Then you have to implement this branch first.'\n", 137 | " )\n", 138 | "\n", 139 | " @torch.no_grad()\n", 140 | " def evaluate(self, X, y=None):\n", 141 | " self.model.eval()\n", 142 | " score = []\n", 143 | " # for batch in delu.iter_batches(X[part], 1024):\n", 144 | " for batch in delu.iter_batches(X, self.batch_size):\n", 145 | " score.append(self.apply_model(batch))\n", 146 | " score = torch.cat(score).squeeze(1).cpu().numpy()\n", 147 | " score = scipy.special.expit(score)\n", 148 | "\n", 149 | " # calculate the metric\n", 150 | " if y is not None:\n", 151 | " target = y.cpu().numpy()\n", 152 | " metric = self.utils.metric(y_true=target, y_score=score)\n", 153 | " else:\n", 154 | " metric = {'aucroc': None, 'aucpr': None}\n", 155 | "\n", 156 | " return score, metric['aucpr']\n", 157 | "\n", 158 | " def fit(self, X_train, y_train, ratio=None,X_test=None,y_test=None,params=None):\n", 159 | " # set seed\n", 160 | " self.utils.set_seed(self.seed)\n", 161 | " \n", 162 | " #X_train, X_test_val, y_train, y_test_val = train_test_split(X_train, y_train, test_size=0.33, random_state=42)\n", 163 | " # training set is used as the validation set in the anomaly detection task\n", 164 | " X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 165 | " 'val': torch.from_numpy(X_train).float().to(self.device)}\n", 166 | "\n", 167 | " y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 168 | " 'val': torch.from_numpy(y_train).float().to(self.device)}\n", 169 | " \n", 170 | " #training set is used as the validation set in the anomaly detection task\n", 171 | "# X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 172 | "# 'val': torch.from_numpy(X_test_val).float().to(self.device)}\n", 173 | "\n", 174 | "# y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 175 | "# 'val': torch.from_numpy(y_test_val).float().to(self.device)}\n", 176 | "\n", 177 | " task_type = 'binclass'\n", 178 | " n_classes = None\n", 179 | " d_out = n_classes or 1\n", 180 | "\n", 181 | " if self.model_name == 'ResNet':\n", 182 | " self.model = rtdl.ResNet.make_baseline(\n", 183 | " d_in=X_train.shape[1],\n", 184 | " d_main=128,\n", 185 | " d_hidden=256,\n", 186 | " dropout_first=params['dropout_first'],\n", 187 | " dropout_second=0.0,\n", 188 | " n_blocks=params['n_blocks'],\n", 189 | " d_out=d_out,\n", 190 | " )\n", 191 | " lr = params['learning_rate']\n", 192 | " weight_decay = 0.0\n", 193 | " \n", 194 | " elif self.model_name == 'MLP':\n", 195 | " self.model = rtdl.MLP.make_baseline(\n", 196 | " d_in=X_train.shape[1],\n", 197 | " d_layers= [128, 256, 128],\n", 198 | " dropout=0.25,\n", 199 | " d_out=d_out,\n", 200 | " )\n", 201 | " lr = 0.001\n", 202 | " weight_decay = 0.0\n", 203 | "\n", 204 | " elif self.model_name == 'FTTransformer':\n", 205 | " self.model = rtdl.FTTransformer.make_default(\n", 206 | " n_num_features=X_train.shape[1],\n", 207 | " cat_cardinalities=None,\n", 208 | " last_layer_query_idx=[-1], # it makes the model faster and does NOT affect its output\n", 209 | " d_out=d_out,\n", 210 | " )\n", 211 | " \n", 212 | " elif self.model_name == 'FTTransformer_baseline':\n", 213 | " self.model = rtdl.FTTransformer.make_baseline(\n", 214 | " n_num_features=X_train.shape[1],\n", 215 | " cat_cardinalities=None,\n", 216 | " d_token=X_train.shape[1],\n", 217 | " n_blocks=2,\n", 218 | " attention_dropout=0.2,\n", 219 | " ffn_d_hidden=6,\n", 220 | " ffn_dropout=0.2,\n", 221 | " residual_dropout=0.0,\n", 222 | " d_out=d_out,\n", 223 | " ) \n", 224 | " else:\n", 225 | " raise NotImplementedError\n", 226 | "\n", 227 | " self.model.to(self.device)\n", 228 | " optimizer = (\n", 229 | " self.model.make_default_optimizer()\n", 230 | " if isinstance(self.model, rtdl.FTTransformer)\n", 231 | " else torch.optim.AdamW(self.model.parameters(), lr=lr, weight_decay=weight_decay)\n", 232 | " )\n", 233 | " loss_fn = (\n", 234 | " F.binary_cross_entropy_with_logits\n", 235 | " if task_type == 'binclass'\n", 236 | " else F.cross_entropy\n", 237 | " if task_type == 'multiclass'\n", 238 | " else F.mse_loss\n", 239 | " )\n", 240 | "\n", 241 | " # Create a dataloader for batches of indices\n", 242 | " # Docs: https://yura52.github.io/zero/reference/api/zero.data.IndexLoader.html\n", 243 | " train_loader = delu.data.IndexLoader(len(X['train']), self.batch_size, device=self.device)\n", 244 | "\n", 245 | " # Create a progress tracker for early stopping\n", 246 | " # Docs: https://yura52.github.io/zero/reference/api/zero.ProgressTracker.html\n", 247 | " progress = delu.ProgressTracker(patience=100)\n", 248 | "\n", 249 | " # training\n", 250 | " # report_frequency = len(X['train']) // self.batch_size // 5\n", 251 | " aucroc = []\n", 252 | " aucpr = []\n", 253 | " loss_ = []\n", 254 | " for epoch in range(1, self.n_epochs + 1):\n", 255 | " loss_tmp = []\n", 256 | " for iteration, batch_idx in enumerate(train_loader):\n", 257 | " self.model.train()\n", 258 | " optimizer.zero_grad()\n", 259 | " x_batch = X['train'][batch_idx]\n", 260 | " y_batch = y['train'][batch_idx]\n", 261 | " loss = loss_fn(self.apply_model(x_batch).squeeze(1), y_batch)\n", 262 | " loss_tmp.append(loss.item())\n", 263 | " loss.backward()\n", 264 | " optimizer.step()\n", 265 | " # if iteration % report_frequency == 0:\n", 266 | " # print(f'(epoch) {epoch} (batch) {iteration} (loss) {loss.item():.4f}')\n", 267 | "\n", 268 | " loss_.append(sum(loss_tmp)/len(loss_tmp))\n", 269 | " _, val_metric = self.evaluate(X=X['val'], y=y['val'])\n", 270 | " print(f'Epoch {epoch:03d} | Validation metric: {val_metric:.4f}', end='')\n", 271 | " progress.update((-1 if task_type == 'regression' else 1) * val_metric)\n", 272 | " if progress.success:\n", 273 | " print(' <<< BEST VALIDATION EPOCH', end='')\n", 274 | " print()\n", 275 | " # 验证\n", 276 | " # output predicted anomaly score on testing set\n", 277 | " score = self.predict_score(X_test)\n", 278 | " # evaluation\n", 279 | " result = utils.metric(y_true=y_test, y_score=score)\n", 280 | " aucroc.append(result['aucroc'])\n", 281 | " aucpr.append(result['aucpr'])\n", 282 | " if progress.fail:\n", 283 | " break\n", 284 | " return result['aucroc']\n", 285 | " #return self\n", 286 | "\n", 287 | " def predict_score(self, X):\n", 288 | " X = torch.from_numpy(X).float().to(self.device)\n", 289 | " score, _ = self.evaluate(X=X, y=None)\n", 290 | " return score" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 2, 296 | "metadata": {}, 297 | "outputs": [ 298 | { 299 | "name": "stderr", 300 | "output_type": "stream", 301 | "text": [ 302 | "100%|███████████████████████████████████████████████████████████████████████████| 28389/28389 [02:25<00:00, 195.32it/s]\n" 303 | ] 304 | }, 305 | { 306 | "name": "stdout", 307 | "output_type": "stream", 308 | "text": [ 309 | "22456\n", 310 | "7096\n" 311 | ] 312 | } 313 | ], 314 | "source": [ 315 | "data_path3 = \"Test_A\"\n", 316 | "epoch = 10\n", 317 | "batch_size = 256\n", 318 | "model_name = \"ResNet\"\n", 319 | "# 加载训练集的pkl文件,划分训练集与验证集\n", 320 | "ind_pkl_files = []#存放标签为0的文件\n", 321 | "ood_pkl_files = []#存放标签为1的文件\n", 322 | "data_path=\"Train\"#存放数据的路径\n", 323 | "pkl_files = glob(data_path+'/*.pkl')\n", 324 | "for each_path in tqdm(pkl_files):\n", 325 | " pic = open(each_path,'rb')\n", 326 | " this_pkl_file= pickle.load(pic)#下载pkl文件\n", 327 | " if this_pkl_file[1]['label'] == '00':\n", 328 | " ind_pkl_files.append(each_path)\n", 329 | " else:\n", 330 | " ood_pkl_files.append(each_path)\n", 331 | "\n", 332 | "random.seed(0)\n", 333 | "# 排序并打乱存放车辆序号的集合\n", 334 | "random.shuffle(ind_pkl_files)\n", 335 | "random.shuffle(ood_pkl_files)\n", 336 | "# 3/4的正样本和全部的负样本作为训练集,1/4的正样本和1/4的负样本作为训练集\n", 337 | "train_pkl_files = [ ind_pkl_files[j] for j in range(len(ind_pkl_files)//4,len(ind_pkl_files))] + [ ood_pkl_files[i] for i in range(len(ood_pkl_files))]\n", 338 | "test_pkl_files=[ind_pkl_files[i] for i in range(len(ind_pkl_files)//4)] + [ood_pkl_files[i] for i in range(len(ood_pkl_files)//4)]\n", 339 | "\n", 340 | "print(len(train_pkl_files))\n", 341 | "print(len(test_pkl_files))\n", 342 | "\n", 343 | "# 加载并归一化训练数据和验证数据\n", 344 | "X_train,y_train=load_data(train_pkl_files)\n", 345 | "# 进行随机打乱,这里random_state指定为固定值,则打乱结果相同\n", 346 | "X_train,y_train = shuffle(X_train,y_train,random_state=40)\n", 347 | "X_test,y_test=load_data(test_pkl_files)\n", 348 | "X_train = normalization(X_train)\n", 349 | "X_test = normalization(X_test)\n", 350 | "\n", 351 | "test1_files = glob(data_path3+'/*.pkl')\n", 352 | "X_val,_=load_data(test1_files,label=False)\n", 353 | "X_val = normalization(X_val)" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 3, 359 | "metadata": {}, 360 | "outputs": [ 361 | { 362 | "name": "stderr", 363 | "output_type": "stream", 364 | "text": [ 365 | "\u001b[32m[I 2022-10-15 13:47:28,315]\u001b[0m A new study created in memory with name: no-name-81b9ca6f-4362-4647-9141-85a4106304e0\u001b[0m\n" 366 | ] 367 | }, 368 | { 369 | "name": "stdout", 370 | "output_type": "stream", 371 | "text": [ 372 | "Epoch 001 | Validation metric: 0.8191 <<< BEST VALIDATION EPOCH\n", 373 | "Epoch 002 | Validation metric: 0.8968 <<< BEST VALIDATION EPOCH\n", 374 | "Epoch 003 | Validation metric: 0.9391 <<< BEST VALIDATION EPOCH\n", 375 | "Epoch 004 | Validation metric: 0.9649 <<< BEST VALIDATION EPOCH\n", 376 | "Epoch 005 | Validation metric: 0.9723 <<< BEST VALIDATION EPOCH\n", 377 | "Epoch 006 | Validation metric: 0.9779 <<< BEST VALIDATION EPOCH\n", 378 | "Epoch 007 | Validation metric: 0.9819 <<< BEST VALIDATION EPOCH\n", 379 | "Epoch 008 | Validation metric: 0.9835 <<< BEST VALIDATION EPOCH\n", 380 | "Epoch 009 | Validation metric: 0.9843 <<< BEST VALIDATION EPOCH\n" 381 | ] 382 | }, 383 | { 384 | "name": "stderr", 385 | "output_type": "stream", 386 | "text": [ 387 | "\u001b[32m[I 2022-10-15 13:47:57,402]\u001b[0m Trial 0 finished with value: 0.9961790292545926 and parameters: {'lr': 0.0002662521240223481, 'dropout_first': 0.1427002844927043, 'n_blocks': 2}. Best is trial 0 with value: 0.9961790292545926.\u001b[0m\n" 388 | ] 389 | }, 390 | { 391 | "name": "stdout", 392 | "output_type": "stream", 393 | "text": [ 394 | "Epoch 010 | Validation metric: 0.9886 <<< BEST VALIDATION EPOCH\n", 395 | "Epoch 001 | Validation metric: 0.8856 <<< BEST VALIDATION EPOCH\n", 396 | "Epoch 002 | Validation metric: 0.9420 <<< BEST VALIDATION EPOCH\n", 397 | "Epoch 003 | Validation metric: 0.9418\n", 398 | "Epoch 004 | Validation metric: 0.9629 <<< BEST VALIDATION EPOCH\n", 399 | "Epoch 005 | Validation metric: 0.9721 <<< BEST VALIDATION EPOCH\n", 400 | "Epoch 006 | Validation metric: 0.9759 <<< BEST VALIDATION EPOCH\n", 401 | "Epoch 007 | Validation metric: 0.9738\n", 402 | "Epoch 008 | Validation metric: 0.9648\n", 403 | "Epoch 009 | Validation metric: 0.9813 <<< BEST VALIDATION EPOCH\n", 404 | "Epoch 010 | Validation metric: 0.9690\n" 405 | ] 406 | }, 407 | { 408 | "name": "stderr", 409 | "output_type": "stream", 410 | "text": [ 411 | "\u001b[32m[I 2022-10-15 13:48:47,804]\u001b[0m Trial 1 finished with value: 0.9899077967078348 and parameters: {'lr': 0.004846560836959535, 'dropout_first': 0.3464939660858418, 'n_blocks': 4}. Best is trial 0 with value: 0.9961790292545926.\u001b[0m\n" 412 | ] 413 | }, 414 | { 415 | "name": "stdout", 416 | "output_type": "stream", 417 | "text": [ 418 | "Epoch 001 | Validation metric: 0.8626 <<< BEST VALIDATION EPOCH\n", 419 | "Epoch 002 | Validation metric: 0.9034 <<< BEST VALIDATION EPOCH\n", 420 | "Epoch 003 | Validation metric: 0.9225 <<< BEST VALIDATION EPOCH\n", 421 | "Epoch 004 | Validation metric: 0.9599 <<< BEST VALIDATION EPOCH\n", 422 | "Epoch 005 | Validation metric: 0.9604 <<< BEST VALIDATION EPOCH\n", 423 | "Epoch 006 | Validation metric: 0.9621 <<< BEST VALIDATION EPOCH\n", 424 | "Epoch 007 | Validation metric: 0.9752 <<< BEST VALIDATION EPOCH\n", 425 | "Epoch 008 | Validation metric: 0.9775 <<< BEST VALIDATION EPOCH\n", 426 | "Epoch 009 | Validation metric: 0.9765\n" 427 | ] 428 | }, 429 | { 430 | "name": "stderr", 431 | "output_type": "stream", 432 | "text": [ 433 | "\u001b[32m[I 2022-10-15 13:49:05,251]\u001b[0m Trial 2 finished with value: 0.9925831283960661 and parameters: {'lr': 0.03959388560475731, 'dropout_first': 0.31806994335367167, 'n_blocks': 1}. Best is trial 0 with value: 0.9961790292545926.\u001b[0m\n" 434 | ] 435 | }, 436 | { 437 | "name": "stdout", 438 | "output_type": "stream", 439 | "text": [ 440 | "Epoch 010 | Validation metric: 0.9747\n", 441 | "Epoch 001 | Validation metric: 0.8205 <<< BEST VALIDATION EPOCH\n", 442 | "Epoch 002 | Validation metric: 0.8964 <<< BEST VALIDATION EPOCH\n", 443 | "Epoch 003 | Validation metric: 0.9176 <<< BEST VALIDATION EPOCH\n", 444 | "Epoch 004 | Validation metric: 0.9313 <<< BEST VALIDATION EPOCH\n", 445 | "Epoch 005 | Validation metric: 0.9357 <<< BEST VALIDATION EPOCH\n", 446 | "Epoch 006 | Validation metric: 0.9652 <<< BEST VALIDATION EPOCH\n", 447 | "Epoch 007 | Validation metric: 0.9588\n", 448 | "Epoch 008 | Validation metric: 0.9439\n", 449 | "Epoch 009 | Validation metric: 0.9420\n", 450 | "Epoch 010 | Validation metric: 0.9452\n" 451 | ] 452 | }, 453 | { 454 | "name": "stderr", 455 | "output_type": "stream", 456 | "text": [ 457 | "\u001b[32m[I 2022-10-15 13:49:54,662]\u001b[0m Trial 3 finished with value: 0.985056982680923 and parameters: {'lr': 0.023004780050226162, 'dropout_first': 0.37906624815475076, 'n_blocks': 4}. Best is trial 0 with value: 0.9961790292545926.\u001b[0m\n" 458 | ] 459 | }, 460 | { 461 | "name": "stdout", 462 | "output_type": "stream", 463 | "text": [ 464 | "Epoch 001 | Validation metric: 0.8672 <<< BEST VALIDATION EPOCH\n", 465 | "Epoch 002 | Validation metric: 0.9238 <<< BEST VALIDATION EPOCH\n", 466 | "Epoch 003 | Validation metric: 0.9659 <<< BEST VALIDATION EPOCH\n", 467 | "Epoch 004 | Validation metric: 0.9674 <<< BEST VALIDATION EPOCH\n", 468 | "Epoch 005 | Validation metric: 0.9796 <<< BEST VALIDATION EPOCH\n", 469 | "Epoch 006 | Validation metric: 0.9797 <<< BEST VALIDATION EPOCH\n", 470 | "Epoch 007 | Validation metric: 0.9850 <<< BEST VALIDATION EPOCH\n", 471 | "Epoch 008 | Validation metric: 0.9846\n", 472 | "Epoch 009 | Validation metric: 0.9874 <<< BEST VALIDATION EPOCH\n", 473 | "Epoch 010 | Validation metric: 0.9884 <<< BEST VALIDATION EPOCH\n" 474 | ] 475 | }, 476 | { 477 | "name": "stderr", 478 | "output_type": "stream", 479 | "text": [ 480 | "\u001b[32m[I 2022-10-15 13:50:32,620]\u001b[0m Trial 4 finished with value: 0.996462069492248 and parameters: {'lr': 0.000422874917555388, 'dropout_first': 0.26307843861624325, 'n_blocks': 3}. Best is trial 4 with value: 0.996462069492248.\u001b[0m\n" 481 | ] 482 | }, 483 | { 484 | "name": "stdout", 485 | "output_type": "stream", 486 | "text": [ 487 | "Epoch 001 | Validation metric: 0.5966 <<< BEST VALIDATION EPOCH\n", 488 | "Epoch 002 | Validation metric: 0.6466 <<< BEST VALIDATION EPOCH\n", 489 | "Epoch 003 | Validation metric: 0.6784 <<< BEST VALIDATION EPOCH\n", 490 | "Epoch 004 | Validation metric: 0.7009 <<< BEST VALIDATION EPOCH\n", 491 | "Epoch 005 | Validation metric: 0.7176 <<< BEST VALIDATION EPOCH\n", 492 | "Epoch 006 | Validation metric: 0.7318 <<< BEST VALIDATION EPOCH\n", 493 | "Epoch 007 | Validation metric: 0.7434 <<< BEST VALIDATION EPOCH\n", 494 | "Epoch 008 | Validation metric: 0.7538 <<< BEST VALIDATION EPOCH\n", 495 | "Epoch 009 | Validation metric: 0.7620 <<< BEST VALIDATION EPOCH\n", 496 | "Epoch 010 | Validation metric: 0.7707 <<< BEST VALIDATION EPOCH\n" 497 | ] 498 | }, 499 | { 500 | "name": "stderr", 501 | "output_type": "stream", 502 | "text": [ 503 | "\u001b[32m[I 2022-10-15 13:51:10,288]\u001b[0m Trial 5 finished with value: 0.9185497731257858 and parameters: {'lr': 1.1114145324982797e-05, 'dropout_first': 0.33329031210697024, 'n_blocks': 3}. Best is trial 4 with value: 0.996462069492248.\u001b[0m\n" 504 | ] 505 | }, 506 | { 507 | "name": "stdout", 508 | "output_type": "stream", 509 | "text": [ 510 | "Epoch 001 | Validation metric: 0.8361 <<< BEST VALIDATION EPOCH\n", 511 | "Epoch 002 | Validation metric: 0.8970 <<< BEST VALIDATION EPOCH\n", 512 | "Epoch 003 | Validation metric: 0.9134 <<< BEST VALIDATION EPOCH\n", 513 | "Epoch 004 | Validation metric: 0.9403 <<< BEST VALIDATION EPOCH\n", 514 | "Epoch 005 | Validation metric: 0.9510 <<< BEST VALIDATION EPOCH\n", 515 | "Epoch 006 | Validation metric: 0.9798 <<< BEST VALIDATION EPOCH\n", 516 | "Epoch 007 | Validation metric: 0.9774\n", 517 | "Epoch 008 | Validation metric: 0.9817 <<< BEST VALIDATION EPOCH\n", 518 | "Epoch 009 | Validation metric: 0.9792\n" 519 | ] 520 | }, 521 | { 522 | "name": "stderr", 523 | "output_type": "stream", 524 | "text": [ 525 | "\u001b[32m[I 2022-10-15 13:51:26,186]\u001b[0m Trial 6 finished with value: 0.9956472962121158 and parameters: {'lr': 0.05665596792907179, 'dropout_first': 0.19920698852007687, 'n_blocks': 1}. Best is trial 4 with value: 0.996462069492248.\u001b[0m\n" 526 | ] 527 | }, 528 | { 529 | "name": "stdout", 530 | "output_type": "stream", 531 | "text": [ 532 | "Epoch 010 | Validation metric: 0.9841 <<< BEST VALIDATION EPOCH\n", 533 | "Epoch 001 | Validation metric: 0.8589 <<< BEST VALIDATION EPOCH\n", 534 | "Epoch 002 | Validation metric: 0.8869 <<< BEST VALIDATION EPOCH\n", 535 | "Epoch 003 | Validation metric: 0.9218 <<< BEST VALIDATION EPOCH\n", 536 | "Epoch 004 | Validation metric: 0.9439 <<< BEST VALIDATION EPOCH\n", 537 | "Epoch 005 | Validation metric: 0.9476 <<< BEST VALIDATION EPOCH\n", 538 | "Epoch 006 | Validation metric: 0.9636 <<< BEST VALIDATION EPOCH\n", 539 | "Epoch 007 | Validation metric: 0.9624\n", 540 | "Epoch 008 | Validation metric: 0.9773 <<< BEST VALIDATION EPOCH\n", 541 | "Epoch 009 | Validation metric: 0.9700\n", 542 | "Epoch 010 | Validation metric: 0.9844 <<< BEST VALIDATION EPOCH\n" 543 | ] 544 | }, 545 | { 546 | "name": "stderr", 547 | "output_type": "stream", 548 | "text": [ 549 | "\u001b[32m[I 2022-10-15 13:52:11,212]\u001b[0m Trial 7 finished with value: 0.9948160013820132 and parameters: {'lr': 0.04796153618024614, 'dropout_first': 0.25897986221591984, 'n_blocks': 3}. Best is trial 4 with value: 0.996462069492248.\u001b[0m\n" 550 | ] 551 | }, 552 | { 553 | "name": "stdout", 554 | "output_type": "stream", 555 | "text": [ 556 | "Epoch 001 | Validation metric: 0.6309 <<< BEST VALIDATION EPOCH\n", 557 | "Epoch 002 | Validation metric: 0.6842 <<< BEST VALIDATION EPOCH\n", 558 | "Epoch 003 | Validation metric: 0.7171 <<< BEST VALIDATION EPOCH\n", 559 | "Epoch 004 | Validation metric: 0.7386 <<< BEST VALIDATION EPOCH\n", 560 | "Epoch 005 | Validation metric: 0.7555 <<< BEST VALIDATION EPOCH\n", 561 | "Epoch 006 | Validation metric: 0.7715 <<< BEST VALIDATION EPOCH\n", 562 | "Epoch 007 | Validation metric: 0.7832 <<< BEST VALIDATION EPOCH\n", 563 | "Epoch 008 | Validation metric: 0.7943 <<< BEST VALIDATION EPOCH\n", 564 | "Epoch 009 | Validation metric: 0.8051 <<< BEST VALIDATION EPOCH\n", 565 | "Epoch 010 | Validation metric: 0.8162 <<< BEST VALIDATION EPOCH\n" 566 | ] 567 | }, 568 | { 569 | "name": "stderr", 570 | "output_type": "stream", 571 | "text": [ 572 | "\u001b[32m[I 2022-10-15 13:52:56,763]\u001b[0m Trial 8 finished with value: 0.9419960264223063 and parameters: {'lr': 1.670363720616462e-05, 'dropout_first': 0.28172697177222206, 'n_blocks': 3}. Best is trial 4 with value: 0.996462069492248.\u001b[0m\n" 573 | ] 574 | }, 575 | { 576 | "name": "stdout", 577 | "output_type": "stream", 578 | "text": [ 579 | "Epoch 001 | Validation metric: 0.8770 <<< BEST VALIDATION EPOCH\n", 580 | "Epoch 002 | Validation metric: 0.9346 <<< BEST VALIDATION EPOCH\n", 581 | "Epoch 003 | Validation metric: 0.9519 <<< BEST VALIDATION EPOCH\n", 582 | "Epoch 004 | Validation metric: 0.9507\n", 583 | "Epoch 005 | Validation metric: 0.9727 <<< BEST VALIDATION EPOCH\n", 584 | "Epoch 006 | Validation metric: 0.9780 <<< BEST VALIDATION EPOCH\n", 585 | "Epoch 007 | Validation metric: 0.9714\n", 586 | "Epoch 008 | Validation metric: 0.9827 <<< BEST VALIDATION EPOCH\n", 587 | "Epoch 009 | Validation metric: 0.9888 <<< BEST VALIDATION EPOCH\n" 588 | ] 589 | }, 590 | { 591 | "name": "stderr", 592 | "output_type": "stream", 593 | "text": [ 594 | "\u001b[32m[I 2022-10-15 13:53:16,078]\u001b[0m Trial 9 finished with value: 0.9952678802662983 and parameters: {'lr': 0.01572347497043378, 'dropout_first': 0.13099749231550045, 'n_blocks': 1}. Best is trial 4 with value: 0.996462069492248.\u001b[0m\n" 595 | ] 596 | }, 597 | { 598 | "name": "stdout", 599 | "output_type": "stream", 600 | "text": [ 601 | "Epoch 010 | Validation metric: 0.9845\n", 602 | "0.996462069492248\n", 603 | "lr: 0.000422874917555388\n", 604 | "dropout_first: 0.26307843861624325\n", 605 | "n_blocks: 3\n" 606 | ] 607 | } 608 | ], 609 | "source": [ 610 | "def objective(trial):\n", 611 | " seed = 42\n", 612 | " clf=FTTransformer(seed,model_name,n_epochs=10,batch_size=batch_size)\n", 613 | "\n", 614 | " params = {\n", 615 | " 'learning_rate': trial.suggest_float(\"lr\", 1e-5, 1e-1, log=True),\n", 616 | " 'dropout_first': trial.suggest_float('dropout_first', 0.1, 0.5),\n", 617 | " 'n_blocks': trial.suggest_int(\"n_blocks\", 1, 4),\n", 618 | " }\n", 619 | "\n", 620 | " accuracy = clf.fit(X_train=X_train, y_train=y_train.squeeze(1),X_test=X_test,y_test=y_test,params=params)\n", 621 | "\n", 622 | " return accuracy\n", 623 | "\n", 624 | "study = optuna.create_study(direction=\"maximize\", sampler=optuna.samplers.TPESampler())\n", 625 | "study.optimize(objective, n_trials=10)\n", 626 | "best_trial = study.best_trial\n", 627 | "print(best_trial.value)\n", 628 | "for key, value in best_trial.params.items():\n", 629 | " print(\"{}: {}\".format(key, value))" 630 | ] 631 | }, 632 | { 633 | "cell_type": "code", 634 | "execution_count": null, 635 | "metadata": {}, 636 | "outputs": [], 637 | "source": [] 638 | } 639 | ], 640 | "metadata": { 641 | "kernelspec": { 642 | "display_name": "Python 3", 643 | "language": "python", 644 | "name": "python3" 645 | }, 646 | "language_info": { 647 | "codemirror_mode": { 648 | "name": "ipython", 649 | "version": 3 650 | }, 651 | "file_extension": ".py", 652 | "mimetype": "text/x-python", 653 | "name": "python", 654 | "nbconvert_exporter": "python", 655 | "pygments_lexer": "ipython3", 656 | "version": "3.7.3" 657 | }, 658 | "toc": { 659 | "base_numbering": 1, 660 | "nav_menu": {}, 661 | "number_sections": true, 662 | "sideBar": true, 663 | "skip_h1_title": false, 664 | "title_cell": "Table of Contents", 665 | "title_sidebar": "Contents", 666 | "toc_cell": false, 667 | "toc_position": {}, 668 | "toc_section_display": true, 669 | "toc_window_display": false 670 | }, 671 | "varInspector": { 672 | "cols": { 673 | "lenName": 16, 674 | "lenType": 16, 675 | "lenVar": 40 676 | }, 677 | "kernels_config": { 678 | "python": { 679 | "delete_cmd_postfix": "", 680 | "delete_cmd_prefix": "del ", 681 | "library": "var_list.py", 682 | "varRefreshCmd": "print(var_dic_list())" 683 | }, 684 | "r": { 685 | "delete_cmd_postfix": ") ", 686 | "delete_cmd_prefix": "rm(", 687 | "library": "var_list.r", 688 | "varRefreshCmd": "cat(var_dic_list()) " 689 | } 690 | }, 691 | "types_to_exclude": [ 692 | "module", 693 | "function", 694 | "builtin_function_or_method", 695 | "instance", 696 | "_Feature" 697 | ], 698 | "window_display": false 699 | } 700 | }, 701 | "nbformat": 4, 702 | "nbformat_minor": 4 703 | } 704 | -------------------------------------------------------------------------------- /demo_resnet_epoch532_lr0.001_dropout0.25_auc0.8998.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | } 9 | }, 10 | "source": [ 11 | "# Run ADBench \n", 12 | "- Here we provide a demo for testing AD algorithms on the datasets proposed in ADBench.\n", 13 | "- Feel free to evaluate any customized algorithm in ADBench.\n", 14 | "- For reproducing the complete experiment results in ADBench, please run the code in the run.py file." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stderr", 24 | "output_type": "stream", 25 | "text": [ 26 | "D:\\autosoftware\\anconda\\lib\\site-packages\\pandas\\compat\\_optional.py:138: UserWarning: Pandas requires version '2.7.0' or newer of 'numexpr' (version '2.6.9' currently installed).\n", 27 | " warnings.warn(msg, UserWarning)\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "import numpy as np\n", 33 | "from glob import glob\n", 34 | "import pickle\n", 35 | "import random\n", 36 | "from tqdm import tqdm\n", 37 | "import pandas as pd" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "# 加载训练集的pkl文件,划分训练集与验证集\n", 45 | "训练集的label存放在pkl里面,可以通过它并区分正常片段和异常片段\n", 46 | "注意需要输入训练集对应的路径" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 2, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "name": "stderr", 56 | "output_type": "stream", 57 | "text": [ 58 | "100%|███████████████████████████████████████████████████████████████████████████| 28389/28389 [01:37<00:00, 292.52it/s]\n" 59 | ] 60 | } 61 | ], 62 | "source": [ 63 | "ind_pkl_files = []#存放标签为0的文件\n", 64 | "ood_pkl_files = []#存放标签为1的文件\n", 65 | "data_path='Train'#存放数据的路径\n", 66 | "pkl_files = glob(data_path+'/*.pkl')\n", 67 | "for each_path in tqdm(pkl_files):\n", 68 | " pic = open(each_path,'rb')\n", 69 | " this_pkl_file= pickle.load(pic)#下载pkl文件\n", 70 | " if this_pkl_file[1]['label'] == '00':\n", 71 | " ind_pkl_files.append(each_path)\n", 72 | " else:\n", 73 | " ood_pkl_files.append(each_path)\n", 74 | "\n", 75 | "random.seed(0)\n", 76 | "#排序并打乱存放车辆序号的集合\n", 77 | "random.shuffle(ind_pkl_files)\n", 78 | "random.shuffle(ood_pkl_files)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "metadata": {}, 85 | "outputs": [ 86 | { 87 | "name": "stdout", 88 | "output_type": "stream", 89 | "text": [ 90 | "23735\n", 91 | "4654\n" 92 | ] 93 | } 94 | ], 95 | "source": [ 96 | "print(len(ind_pkl_files))\n", 97 | "print(len(ood_pkl_files))" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 4, 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "name": "stdout", 107 | "output_type": "stream", 108 | "text": [ 109 | "22456\n", 110 | "7096\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "train_pkl_files=[]\n", 116 | "for j in range(len(ind_pkl_files)//4,len(ind_pkl_files)):\n", 117 | "#for j in range(len(ind_pkl_files)):\n", 118 | " train_pkl_files.append(ind_pkl_files[j])\n", 119 | "#for i in range(len(ind_pkl_files)//4):\n", 120 | "# train_pkl_files.append(ind_pkl_files[i])\n", 121 | "for j in range(len(ood_pkl_files)):\n", 122 | " train_pkl_files.append(ood_pkl_files[j])\n", 123 | "print(len(train_pkl_files))\n", 124 | "test_pkl_files=[]\n", 125 | "#for j in range(len(ind_pkl_files)//4,len(ind_pkl_files)):\n", 126 | " # test_pkl_files.append(ind_pkl_files[j])\n", 127 | "for i in range(len(ind_pkl_files)//4):\n", 128 | " test_pkl_files.append(ind_pkl_files[i])\n", 129 | "#for item in ood_pkl_files:\n", 130 | "# test_pkl_files.append(item)\n", 131 | "\n", 132 | "for j in range(len(ood_pkl_files)//4):\n", 133 | " test_pkl_files.append(ood_pkl_files[j])\n", 134 | "print(len(test_pkl_files))" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "# 定义加载函数,并对数据进行正则化" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 5, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [ 150 | "def load_data(pkl_list,label=True):\n", 151 | " '''\n", 152 | " 输入pkl的列表,进行文件加载\n", 153 | " label=True用来加载训练集\n", 154 | " label=False用来加载真正的测试集,真正的测试集无标签\n", 155 | " '''\n", 156 | " X = []\n", 157 | " y = []\n", 158 | " \n", 159 | "\n", 160 | " for each_pkl in pkl_list:\n", 161 | " pic = open(each_pkl,'rb')\n", 162 | " item= pickle.load(pic)#下载pkl文件\n", 163 | " # 此处选取的是每个滑窗的最后一条数据,仅供参考,可以选择其他的方法,比如均值或者其他处理时序数据的网络\n", 164 | " # 此处选取了前7个特征,可以需求选取特征数量\n", 165 | " feature = item[0][:,0:7][-1]\n", 166 | " #feature = item[0][:,0:7][-1]\n", 167 | " #feature = item[0][:,0:7].mean(axis=0)\n", 168 | " #feature = np.append(item[0][:,0:7][-1],(item[0][:,3][-1] - item[0][:,4][-1])) #加max_single_volt - min_single_volt 一列为特征\n", 169 | " feature=np.append(feature,item[1][\"mileage\"])\n", 170 | " X.append(feature)\n", 171 | " if label:\n", 172 | " y.append(int(item[1]['label'][0]))\n", 173 | " X = np.vstack(X)\n", 174 | " if label:\n", 175 | " y = np.vstack(y)\n", 176 | " return X, y" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 6, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "X_train,y_train=load_data(train_pkl_files)\n", 186 | "X_test,y_test=load_data(test_pkl_files)\n", 187 | "_mean = np.mean(X_train, axis=0)\n", 188 | "_std = np.std(X_train, axis=0)\n", 189 | "X_train = (X_train - _mean) / (_std + 1e-4)\n", 190 | "X_test = (X_test - _mean) / (_std + 1e-4)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 7, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "\n", 200 | "from sklearn.utils import shuffle\n", 201 | "# 进行随机打乱,这里random_state指定为固定值,则打乱结果相同\n", 202 | "X_train,y_train = shuffle(X_train,y_train,random_state=40)\n" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 8, 208 | "metadata": {}, 209 | "outputs": [], 210 | "source": [ 211 | "data_path3='Test_A'\n", 212 | "test1_files = glob(data_path3+'/*.pkl')\n", 213 | "X_val,_=load_data(test1_files,label=False)\n", 214 | "_mean = np.mean(X_val, axis=0)\n", 215 | "_std = np.std(X_val, axis=0)\n", 216 | "X_val = (X_val - _mean) / (_std + 1e-4)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 9, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "import sklearn.metrics\n", 226 | "from sklearn.model_selection import train_test_split\n", 227 | "import sklearn.preprocessing\n", 228 | "import torch\n", 229 | "import torch.nn as nn\n", 230 | "import torch.nn.functional as F\n", 231 | "# import zero\n", 232 | "import delu # the new version of zero package\n", 233 | "import rtdl\n", 234 | "import scipy\n", 235 | "import platform\n", 236 | "from myutils import Utils\n", 237 | "utils = Utils() # utils function\n", 238 | "\n", 239 | "\n", 240 | "class FTTransformer():\n", 241 | " '''\n", 242 | " The original code: https://yura52.github.io/rtdl/stable/index.html\n", 243 | " The original paper: \"Revisiting Deep Learning Models for Tabular Data\", NIPS 2019\n", 244 | " '''\n", 245 | " def __init__(self, seed:int, model_name:str, n_epochs=100, batch_size=64):\n", 246 | "\n", 247 | " self.seed = seed\n", 248 | " self.model_name = model_name\n", 249 | " self.utils = Utils()\n", 250 | "\n", 251 | " # device\n", 252 | " if model_name == 'FTTransformer':\n", 253 | " self.device = self.utils.get_device(gpu_specific=True)\n", 254 | " else:\n", 255 | " self.device = self.utils.get_device(gpu_specific=False)\n", 256 | "\n", 257 | " # Docs: https://yura52.github.io/zero/0.0.4/reference/api/zero.improve_reproducibility.html\n", 258 | " # zero.improve_reproducibility(seed=self.seed)\n", 259 | " delu.improve_reproducibility(base_seed=int(self.seed))\n", 260 | "\n", 261 | " # hyper-parameter\n", 262 | " self.n_epochs = n_epochs # default is 1000\n", 263 | " self.batch_size = batch_size # default is 256\n", 264 | "\n", 265 | " def apply_model(self, x_num, x_cat=None):\n", 266 | " if isinstance(self.model, rtdl.FTTransformer):\n", 267 | " return self.model(x_num, x_cat)\n", 268 | " elif isinstance(self.model, (rtdl.MLP, rtdl.ResNet)):\n", 269 | " assert x_cat is None\n", 270 | " return self.model(x_num)\n", 271 | " else:\n", 272 | " raise NotImplementedError(\n", 273 | " f'Looks like you are using a custom model: {type(self.model)}.'\n", 274 | " ' Then you have to implement this branch first.'\n", 275 | " )\n", 276 | "\n", 277 | " @torch.no_grad()\n", 278 | " def evaluate(self, X, y=None):\n", 279 | " self.model.eval()\n", 280 | " score = []\n", 281 | " # for batch in delu.iter_batches(X[part], 1024):\n", 282 | " for batch in delu.iter_batches(X, self.batch_size):\n", 283 | " score.append(self.apply_model(batch))\n", 284 | " score = torch.cat(score).squeeze(1).cpu().numpy()\n", 285 | " score = scipy.special.expit(score)\n", 286 | "\n", 287 | " # calculate the metric\n", 288 | " if y is not None:\n", 289 | " target = y.cpu().numpy()\n", 290 | " metric = self.utils.metric(y_true=target, y_score=score)\n", 291 | " else:\n", 292 | " metric = {'aucroc': None, 'aucpr': None}\n", 293 | "\n", 294 | " return score, metric['aucpr']\n", 295 | "\n", 296 | " def fit(self, X_train, y_train, ratio=None,X_test=X_test,y_test=y_test):\n", 297 | " # set seed\n", 298 | " self.utils.set_seed(self.seed)\n", 299 | " \n", 300 | " #X_train, X_test_val, y_train, y_test_val = train_test_split(X_train, y_train, test_size=0.33, random_state=42)\n", 301 | " # training set is used as the validation set in the anomaly detection task\n", 302 | " X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 303 | " 'val': torch.from_numpy(X_train).float().to(self.device)}\n", 304 | "\n", 305 | " y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 306 | " 'val': torch.from_numpy(y_train).float().to(self.device)}\n", 307 | " \n", 308 | " #training set is used as the validation set in the anomaly detection task\n", 309 | "# X = {'train': torch.from_numpy(X_train).float().to(self.device),\n", 310 | "# 'val': torch.from_numpy(X_test_val).float().to(self.device)}\n", 311 | "\n", 312 | "# y = {'train': torch.from_numpy(y_train).float().to(self.device),\n", 313 | "# 'val': torch.from_numpy(y_test_val).float().to(self.device)}\n", 314 | "\n", 315 | " task_type = 'binclass'\n", 316 | " n_classes = None\n", 317 | " d_out = n_classes or 1\n", 318 | "\n", 319 | "\n", 320 | " if self.model_name == 'ResNet':\n", 321 | " self.model = rtdl.ResNet.make_baseline(\n", 322 | " d_in=X_train.shape[1],\n", 323 | " d_main=128,\n", 324 | " d_hidden=256,\n", 325 | " dropout_first=0.25,\n", 326 | " dropout_second=0,\n", 327 | " n_blocks=2,\n", 328 | " d_out=d_out,\n", 329 | " )\n", 330 | " lr = 0.001\n", 331 | " weight_decay = 0.0\n", 332 | " \n", 333 | " elif self.model_name == 'MLP':\n", 334 | " self.model = rtdl.MLP.make_baseline(\n", 335 | " d_in=X_train.shape[1],\n", 336 | " d_layers= [128, 256, 128],\n", 337 | " dropout=0.25,\n", 338 | " d_out=d_out,\n", 339 | " )\n", 340 | " lr = 0.001\n", 341 | " weight_decay = 0.0\n", 342 | "\n", 343 | " elif self.model_name == 'FTTransformer':\n", 344 | " self.model = rtdl.FTTransformer.make_default(\n", 345 | " n_num_features=X_train.shape[1],\n", 346 | " cat_cardinalities=None,\n", 347 | " last_layer_query_idx=[-1], # it makes the model faster and does NOT affect its output\n", 348 | " d_out=d_out,\n", 349 | " )\n", 350 | " \n", 351 | " elif self.model_name == 'FTTransformer_baseline':\n", 352 | " self.model = rtdl.FTTransformer.make_baseline(\n", 353 | " n_num_features=X_train.shape[1],\n", 354 | " cat_cardinalities=None,\n", 355 | " d_token=X_train.shape[1],\n", 356 | " n_blocks=2,\n", 357 | " attention_dropout=0.2,\n", 358 | " ffn_d_hidden=6,\n", 359 | " ffn_dropout=0.2,\n", 360 | " residual_dropout=0.0,\n", 361 | " d_out=d_out,\n", 362 | " ) \n", 363 | " else:\n", 364 | " raise NotImplementedError\n", 365 | "\n", 366 | " self.model.to(self.device)\n", 367 | " optimizer = (\n", 368 | " self.model.make_default_optimizer()\n", 369 | " if isinstance(self.model, rtdl.FTTransformer)\n", 370 | " else torch.optim.AdamW(self.model.parameters(), lr=lr, weight_decay=weight_decay)\n", 371 | " )\n", 372 | " loss_fn = (\n", 373 | " F.binary_cross_entropy_with_logits\n", 374 | " if task_type == 'binclass'\n", 375 | " else F.cross_entropy\n", 376 | " if task_type == 'multiclass'\n", 377 | " else F.mse_loss\n", 378 | " )\n", 379 | "\n", 380 | " # Create a dataloader for batches of indices\n", 381 | " # Docs: https://yura52.github.io/zero/reference/api/zero.data.IndexLoader.html\n", 382 | " train_loader = delu.data.IndexLoader(len(X['train']), self.batch_size, device=self.device)\n", 383 | "\n", 384 | " # Create a progress tracker for early stopping\n", 385 | " # Docs: https://yura52.github.io/zero/reference/api/zero.ProgressTracker.html\n", 386 | " progress = delu.ProgressTracker(patience=100)\n", 387 | "\n", 388 | " # training\n", 389 | " # report_frequency = len(X['train']) // self.batch_size // 5\n", 390 | "\n", 391 | " for epoch in range(1, self.n_epochs + 1):\n", 392 | " loss_tmp = []\n", 393 | " for iteration, batch_idx in enumerate(train_loader):\n", 394 | " self.model.train()\n", 395 | " optimizer.zero_grad()\n", 396 | " x_batch = X['train'][batch_idx]\n", 397 | " y_batch = y['train'][batch_idx]\n", 398 | " loss = loss_fn(self.apply_model(x_batch).squeeze(1), y_batch)\n", 399 | " loss_tmp.append(loss.item())\n", 400 | " loss.backward()\n", 401 | " optimizer.step()\n", 402 | " # if iteration % report_frequency == 0:\n", 403 | " # print(f'(epoch) {epoch} (batch) {iteration} (loss) {loss.item():.4f}')\n", 404 | "\n", 405 | " loss_.append(sum(loss_tmp)/len(loss_tmp))\n", 406 | " _, val_metric = self.evaluate(X=X['val'], y=y['val'])\n", 407 | " print(f'Epoch {epoch:03d} | Validation metric: {val_metric:.4f}', end='')\n", 408 | " progress.update((-1 if task_type == 'regression' else 1) * val_metric)\n", 409 | " if progress.success:\n", 410 | " print(' <<< BEST VALIDATION EPOCH', end='')\n", 411 | " print()\n", 412 | " # 验证\n", 413 | " # output predicted anomaly score on testing set\n", 414 | " score = self.predict_score(X_test)\n", 415 | " # evaluation\n", 416 | " result = utils.metric(y_true=y_test, y_score=score)\n", 417 | " aucroc.append(result['aucroc'])\n", 418 | " aucpr.append(result['aucpr'])\n", 419 | " if progress.fail:\n", 420 | " break\n", 421 | "\n", 422 | " return self\n", 423 | "\n", 424 | " def predict_score(self, X):\n", 425 | " X = torch.from_numpy(X).float().to(self.device)\n", 426 | " score, _ = self.evaluate(X=X, y=None)\n", 427 | " return score" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 10, 433 | "metadata": {}, 434 | "outputs": [ 435 | { 436 | "name": "stdout", 437 | "output_type": "stream", 438 | "text": [ 439 | "Epoch 001 | Validation metric: 0.8804 <<< BEST VALIDATION EPOCH\n", 440 | "Epoch 002 | Validation metric: 0.9405 <<< BEST VALIDATION EPOCH\n", 441 | "Epoch 003 | Validation metric: 0.9575 <<< BEST VALIDATION EPOCH\n", 442 | "Epoch 004 | Validation metric: 0.9711 <<< BEST VALIDATION EPOCH\n", 443 | "Epoch 005 | Validation metric: 0.9709\n", 444 | "Epoch 006 | Validation metric: 0.9792 <<< BEST VALIDATION EPOCH\n", 445 | "Epoch 007 | Validation metric: 0.9836 <<< BEST VALIDATION EPOCH\n", 446 | "Epoch 008 | Validation metric: 0.9865 <<< BEST VALIDATION EPOCH\n", 447 | "Epoch 009 | Validation metric: 0.9855\n", 448 | "Epoch 010 | Validation metric: 0.9902 <<< BEST VALIDATION EPOCH\n", 449 | "Epoch 011 | Validation metric: 0.9921 <<< BEST VALIDATION EPOCH\n", 450 | "Epoch 012 | Validation metric: 0.9911\n", 451 | "Epoch 013 | Validation metric: 0.9902\n", 452 | "Epoch 014 | Validation metric: 0.9931 <<< BEST VALIDATION EPOCH\n", 453 | "Epoch 015 | Validation metric: 0.9923\n", 454 | "Epoch 016 | Validation metric: 0.9932 <<< BEST VALIDATION EPOCH\n", 455 | "Epoch 017 | Validation metric: 0.9950 <<< BEST VALIDATION EPOCH\n", 456 | "Epoch 018 | Validation metric: 0.9939\n", 457 | "Epoch 019 | Validation metric: 0.9951 <<< BEST VALIDATION EPOCH\n", 458 | "Epoch 020 | Validation metric: 0.9955 <<< BEST VALIDATION EPOCH\n", 459 | "Epoch 021 | Validation metric: 0.9953\n", 460 | "Epoch 022 | Validation metric: 0.9966 <<< BEST VALIDATION EPOCH\n", 461 | "Epoch 023 | Validation metric: 0.9959\n", 462 | "Epoch 024 | Validation metric: 0.9968 <<< BEST VALIDATION EPOCH\n", 463 | "Epoch 025 | Validation metric: 0.9959\n", 464 | "Epoch 026 | Validation metric: 0.9968 <<< BEST VALIDATION EPOCH\n", 465 | "Epoch 027 | Validation metric: 0.9973 <<< BEST VALIDATION EPOCH\n", 466 | "Epoch 028 | Validation metric: 0.9965\n", 467 | "Epoch 029 | Validation metric: 0.9979 <<< BEST VALIDATION EPOCH\n", 468 | "Epoch 030 | Validation metric: 0.9971\n", 469 | "Epoch 031 | Validation metric: 0.9961\n", 470 | "Epoch 032 | Validation metric: 0.9976\n", 471 | "Epoch 033 | Validation metric: 0.9972\n", 472 | "Epoch 034 | Validation metric: 0.9977\n", 473 | "Epoch 035 | Validation metric: 0.9974\n", 474 | "Epoch 036 | Validation metric: 0.9972\n", 475 | "Epoch 037 | Validation metric: 0.9978\n", 476 | "Epoch 038 | Validation metric: 0.9973\n", 477 | "Epoch 039 | Validation metric: 0.9984 <<< BEST VALIDATION EPOCH\n", 478 | "Epoch 040 | Validation metric: 0.9979\n", 479 | "Epoch 041 | Validation metric: 0.9984\n", 480 | "Epoch 042 | Validation metric: 0.9980\n", 481 | "Epoch 043 | Validation metric: 0.9969\n", 482 | "Epoch 044 | Validation metric: 0.9984\n", 483 | "Epoch 045 | Validation metric: 0.9981\n", 484 | "Epoch 046 | Validation metric: 0.9980\n", 485 | "Epoch 047 | Validation metric: 0.9982\n", 486 | "Epoch 048 | Validation metric: 0.9980\n", 487 | "Epoch 049 | Validation metric: 0.9983\n", 488 | "Epoch 050 | Validation metric: 0.9984 <<< BEST VALIDATION EPOCH\n", 489 | "Epoch 051 | Validation metric: 0.9981\n", 490 | "Epoch 052 | Validation metric: 0.9984\n", 491 | "Epoch 053 | Validation metric: 0.9979\n", 492 | "Epoch 054 | Validation metric: 0.9988 <<< BEST VALIDATION EPOCH\n", 493 | "Epoch 055 | Validation metric: 0.9983\n", 494 | "Epoch 056 | Validation metric: 0.9980\n", 495 | "Epoch 057 | Validation metric: 0.9981\n", 496 | "Epoch 058 | Validation metric: 0.9981\n", 497 | "Epoch 059 | Validation metric: 0.9987\n", 498 | "Epoch 060 | Validation metric: 0.9972\n", 499 | "Epoch 061 | Validation metric: 0.9980\n", 500 | "Epoch 062 | Validation metric: 0.9982\n", 501 | "Epoch 063 | Validation metric: 0.9988 <<< BEST VALIDATION EPOCH\n", 502 | "Epoch 064 | Validation metric: 0.9969\n", 503 | "Epoch 065 | Validation metric: 0.9988 <<< BEST VALIDATION EPOCH\n", 504 | "Epoch 066 | Validation metric: 0.9982\n", 505 | "Epoch 067 | Validation metric: 0.9989 <<< BEST VALIDATION EPOCH\n", 506 | "Epoch 068 | Validation metric: 0.9988\n", 507 | "Epoch 069 | Validation metric: 0.9990 <<< BEST VALIDATION EPOCH\n", 508 | "Epoch 070 | Validation metric: 0.9988\n", 509 | "Epoch 071 | Validation metric: 0.9989\n", 510 | "Epoch 072 | Validation metric: 0.9986\n", 511 | "Epoch 073 | Validation metric: 0.9988\n", 512 | "Epoch 074 | Validation metric: 0.9988\n", 513 | "Epoch 075 | Validation metric: 0.9989\n", 514 | "Epoch 076 | Validation metric: 0.9991 <<< BEST VALIDATION EPOCH\n", 515 | "Epoch 077 | Validation metric: 0.9988\n", 516 | "Epoch 078 | Validation metric: 0.9989\n", 517 | "Epoch 079 | Validation metric: 0.9985\n", 518 | "Epoch 080 | Validation metric: 0.9990\n", 519 | "Epoch 081 | Validation metric: 0.9986\n", 520 | "Epoch 082 | Validation metric: 0.9987\n", 521 | "Epoch 083 | Validation metric: 0.9992 <<< BEST VALIDATION EPOCH\n", 522 | "Epoch 084 | Validation metric: 0.9988\n", 523 | "Epoch 085 | Validation metric: 0.9986\n", 524 | "Epoch 086 | Validation metric: 0.9991\n", 525 | "Epoch 087 | Validation metric: 0.9989\n", 526 | "Epoch 088 | Validation metric: 0.9991\n", 527 | "Epoch 089 | Validation metric: 0.9990\n", 528 | "Epoch 090 | Validation metric: 0.9983\n", 529 | "Epoch 091 | Validation metric: 0.9991\n", 530 | "Epoch 092 | Validation metric: 0.9990\n", 531 | "Epoch 093 | Validation metric: 0.9990\n", 532 | "Epoch 094 | Validation metric: 0.9990\n", 533 | "Epoch 095 | Validation metric: 0.9991\n", 534 | "Epoch 096 | Validation metric: 0.9991\n", 535 | "Epoch 097 | Validation metric: 0.9987\n", 536 | "Epoch 098 | Validation metric: 0.9991\n", 537 | "Epoch 099 | Validation metric: 0.9993 <<< BEST VALIDATION EPOCH\n", 538 | "Epoch 100 | Validation metric: 0.9986\n", 539 | "Epoch 101 | Validation metric: 0.9989\n", 540 | "Epoch 102 | Validation metric: 0.9987\n", 541 | "Epoch 103 | Validation metric: 0.9987\n", 542 | "Epoch 104 | Validation metric: 0.9991\n", 543 | "Epoch 105 | Validation metric: 0.9994 <<< BEST VALIDATION EPOCH\n", 544 | "Epoch 106 | Validation metric: 0.9990\n", 545 | "Epoch 107 | Validation metric: 0.9991\n", 546 | "Epoch 108 | Validation metric: 0.9994\n", 547 | "Epoch 109 | Validation metric: 0.9990\n", 548 | "Epoch 110 | Validation metric: 0.9990\n", 549 | "Epoch 111 | Validation metric: 0.9990\n", 550 | "Epoch 112 | Validation metric: 0.9991\n", 551 | "Epoch 113 | Validation metric: 0.9979\n", 552 | "Epoch 114 | Validation metric: 0.9989\n", 553 | "Epoch 115 | Validation metric: 0.9987\n", 554 | "Epoch 116 | Validation metric: 0.9989\n", 555 | "Epoch 117 | Validation metric: 0.9992\n", 556 | "Epoch 118 | Validation metric: 0.9990\n", 557 | "Epoch 119 | Validation metric: 0.9989\n", 558 | "Epoch 120 | Validation metric: 0.9994 <<< BEST VALIDATION EPOCH\n", 559 | "Epoch 121 | Validation metric: 0.9989\n", 560 | "Epoch 122 | Validation metric: 0.9990\n", 561 | "Epoch 123 | Validation metric: 0.9992\n", 562 | "Epoch 124 | Validation metric: 0.9990\n", 563 | "Epoch 125 | Validation metric: 0.9993\n", 564 | "Epoch 126 | Validation metric: 0.9995 <<< BEST VALIDATION EPOCH\n", 565 | "Epoch 127 | Validation metric: 0.9992\n", 566 | "Epoch 128 | Validation metric: 0.9989\n", 567 | "Epoch 129 | Validation metric: 0.9994\n", 568 | "Epoch 130 | Validation metric: 0.9991\n", 569 | "Epoch 131 | Validation metric: 0.9992\n", 570 | "Epoch 132 | Validation metric: 0.9995 <<< BEST VALIDATION EPOCH\n", 571 | "Epoch 133 | Validation metric: 0.9994\n", 572 | "Epoch 134 | Validation metric: 0.9993\n", 573 | "Epoch 135 | Validation metric: 0.9994\n", 574 | "Epoch 136 | Validation metric: 0.9993\n", 575 | "Epoch 137 | Validation metric: 0.9993\n", 576 | "Epoch 138 | Validation metric: 0.9991\n", 577 | "Epoch 139 | Validation metric: 0.9991\n", 578 | "Epoch 140 | Validation metric: 0.9994\n", 579 | "Epoch 141 | Validation metric: 0.9992\n", 580 | "Epoch 142 | Validation metric: 0.9992\n", 581 | "Epoch 143 | Validation metric: 0.9992\n", 582 | "Epoch 144 | Validation metric: 0.9991\n", 583 | "Epoch 145 | Validation metric: 0.9991\n", 584 | "Epoch 146 | Validation metric: 0.9992\n", 585 | "Epoch 147 | Validation metric: 0.9991\n", 586 | "Epoch 148 | Validation metric: 0.9989\n", 587 | "Epoch 149 | Validation metric: 0.9991\n", 588 | "Epoch 150 | Validation metric: 0.9992\n", 589 | "Epoch 151 | Validation metric: 0.9988\n", 590 | "Epoch 152 | Validation metric: 0.9991\n", 591 | "Epoch 153 | Validation metric: 0.9992\n", 592 | "Epoch 154 | Validation metric: 0.9989\n", 593 | "Epoch 155 | Validation metric: 0.9995\n", 594 | "Epoch 156 | Validation metric: 0.9994\n", 595 | "Epoch 157 | Validation metric: 0.9990\n", 596 | "Epoch 158 | Validation metric: 0.9991\n", 597 | "Epoch 159 | Validation metric: 0.9993\n", 598 | "Epoch 160 | Validation metric: 0.9993\n", 599 | "Epoch 161 | Validation metric: 0.9996 <<< BEST VALIDATION EPOCH\n", 600 | "Epoch 162 | Validation metric: 0.9993\n", 601 | "Epoch 163 | Validation metric: 0.9990\n", 602 | "Epoch 164 | Validation metric: 0.9995\n", 603 | "Epoch 165 | Validation metric: 0.9993\n", 604 | "Epoch 166 | Validation metric: 0.9991\n", 605 | "Epoch 167 | Validation metric: 0.9993\n", 606 | "Epoch 168 | Validation metric: 0.9996 <<< BEST VALIDATION EPOCH\n", 607 | "Epoch 169 | Validation metric: 0.9992\n", 608 | "Epoch 170 | Validation metric: 0.9989\n", 609 | "Epoch 171 | Validation metric: 0.9985\n", 610 | "Epoch 172 | Validation metric: 0.9993\n", 611 | "Epoch 173 | Validation metric: 0.9987\n", 612 | "Epoch 174 | Validation metric: 0.9993\n", 613 | "Epoch 175 | Validation metric: 0.9992\n", 614 | "Epoch 176 | Validation metric: 0.9995\n", 615 | "Epoch 177 | Validation metric: 0.9993\n", 616 | "Epoch 178 | Validation metric: 0.9993\n", 617 | "Epoch 179 | Validation metric: 0.9988\n", 618 | "Epoch 180 | Validation metric: 0.9994\n", 619 | "Epoch 181 | Validation metric: 0.9992\n", 620 | "Epoch 182 | Validation metric: 0.9993\n", 621 | "Epoch 183 | Validation metric: 0.9992\n", 622 | "Epoch 184 | Validation metric: 0.9993\n", 623 | "Epoch 185 | Validation metric: 0.9992\n", 624 | "Epoch 186 | Validation metric: 0.9994\n", 625 | "Epoch 187 | Validation metric: 0.9993\n", 626 | "Epoch 188 | Validation metric: 0.9993\n", 627 | "Epoch 189 | Validation metric: 0.9992\n", 628 | "Epoch 190 | Validation metric: 0.9996\n", 629 | "Epoch 191 | Validation metric: 0.9991\n", 630 | "Epoch 192 | Validation metric: 0.9992\n" 631 | ] 632 | }, 633 | { 634 | "name": "stdout", 635 | "output_type": "stream", 636 | "text": [ 637 | "Epoch 193 | Validation metric: 0.9995\n", 638 | "Epoch 194 | Validation metric: 0.9996 <<< BEST VALIDATION EPOCH\n", 639 | "Epoch 195 | Validation metric: 0.9992\n", 640 | "Epoch 196 | Validation metric: 0.9993\n", 641 | "Epoch 197 | Validation metric: 0.9993\n", 642 | "Epoch 198 | Validation metric: 0.9989\n", 643 | "Epoch 199 | Validation metric: 0.9996\n", 644 | "Epoch 200 | Validation metric: 0.9993\n", 645 | "Epoch 201 | Validation metric: 0.9995\n", 646 | "Epoch 202 | Validation metric: 0.9994\n", 647 | "Epoch 203 | Validation metric: 0.9995\n", 648 | "Epoch 204 | Validation metric: 0.9995\n", 649 | "Epoch 205 | Validation metric: 0.9993\n", 650 | "Epoch 206 | Validation metric: 0.9992\n", 651 | "Epoch 207 | Validation metric: 0.9992\n", 652 | "Epoch 208 | Validation metric: 0.9995\n", 653 | "Epoch 209 | Validation metric: 0.9992\n", 654 | "Epoch 210 | Validation metric: 0.9995\n", 655 | "Epoch 211 | Validation metric: 0.9993\n", 656 | "Epoch 212 | Validation metric: 0.9991\n", 657 | "Epoch 213 | Validation metric: 0.9993\n", 658 | "Epoch 214 | Validation metric: 0.9995\n", 659 | "Epoch 215 | Validation metric: 0.9993\n", 660 | "Epoch 216 | Validation metric: 0.9995\n", 661 | "Epoch 217 | Validation metric: 0.9993\n", 662 | "Epoch 218 | Validation metric: 0.9990\n", 663 | "Epoch 219 | Validation metric: 0.9993\n", 664 | "Epoch 220 | Validation metric: 0.9994\n", 665 | "Epoch 221 | Validation metric: 0.9995\n", 666 | "Epoch 222 | Validation metric: 0.9995\n", 667 | "Epoch 223 | Validation metric: 0.9996 <<< BEST VALIDATION EPOCH\n", 668 | "Epoch 224 | Validation metric: 0.9995\n", 669 | "Epoch 225 | Validation metric: 0.9993\n", 670 | "Epoch 226 | Validation metric: 0.9992\n", 671 | "Epoch 227 | Validation metric: 0.9992\n", 672 | "Epoch 228 | Validation metric: 0.9983\n", 673 | "Epoch 229 | Validation metric: 0.9996\n", 674 | "Epoch 230 | Validation metric: 0.9996\n", 675 | "Epoch 231 | Validation metric: 0.9992\n", 676 | "Epoch 232 | Validation metric: 0.9987\n", 677 | "Epoch 233 | Validation metric: 0.9996\n", 678 | "Epoch 234 | Validation metric: 0.9994\n", 679 | "Epoch 235 | Validation metric: 0.9996\n", 680 | "Epoch 236 | Validation metric: 0.9990\n", 681 | "Epoch 237 | Validation metric: 0.9995\n", 682 | "Epoch 238 | Validation metric: 0.9996\n", 683 | "Epoch 239 | Validation metric: 0.9995\n", 684 | "Epoch 240 | Validation metric: 0.9991\n", 685 | "Epoch 241 | Validation metric: 0.9987\n", 686 | "Epoch 242 | Validation metric: 0.9994\n", 687 | "Epoch 243 | Validation metric: 0.9996\n", 688 | "Epoch 244 | Validation metric: 0.9996\n", 689 | "Epoch 245 | Validation metric: 0.9994\n", 690 | "Epoch 246 | Validation metric: 0.9997 <<< BEST VALIDATION EPOCH\n", 691 | "Epoch 247 | Validation metric: 0.9995\n", 692 | "Epoch 248 | Validation metric: 0.9996\n", 693 | "Epoch 249 | Validation metric: 0.9994\n", 694 | "Epoch 250 | Validation metric: 0.9996\n", 695 | "Epoch 251 | Validation metric: 0.9996\n", 696 | "Epoch 252 | Validation metric: 0.9995\n", 697 | "Epoch 253 | Validation metric: 0.9995\n", 698 | "Epoch 254 | Validation metric: 0.9995\n", 699 | "Epoch 255 | Validation metric: 0.9995\n", 700 | "Epoch 256 | Validation metric: 0.9996\n", 701 | "Epoch 257 | Validation metric: 0.9993\n", 702 | "Epoch 258 | Validation metric: 0.9995\n", 703 | "Epoch 259 | Validation metric: 0.9997\n", 704 | "Epoch 260 | Validation metric: 0.9998 <<< BEST VALIDATION EPOCH\n", 705 | "Epoch 261 | Validation metric: 0.9996\n", 706 | "Epoch 262 | Validation metric: 0.9995\n", 707 | "Epoch 263 | Validation metric: 0.9990\n", 708 | "Epoch 264 | Validation metric: 0.9993\n", 709 | "Epoch 265 | Validation metric: 0.9995\n", 710 | "Epoch 266 | Validation metric: 0.9994\n", 711 | "Epoch 267 | Validation metric: 0.9995\n", 712 | "Epoch 268 | Validation metric: 0.9997\n", 713 | "Epoch 269 | Validation metric: 0.9990\n", 714 | "Epoch 270 | Validation metric: 0.9996\n", 715 | "Epoch 271 | Validation metric: 0.9988\n", 716 | "Epoch 272 | Validation metric: 0.9996\n", 717 | "Epoch 273 | Validation metric: 0.9996\n", 718 | "Epoch 274 | Validation metric: 0.9994\n", 719 | "Epoch 275 | Validation metric: 0.9996\n", 720 | "Epoch 276 | Validation metric: 0.9992\n", 721 | "Epoch 277 | Validation metric: 0.9995\n", 722 | "Epoch 278 | Validation metric: 0.9997\n", 723 | "Epoch 279 | Validation metric: 0.9996\n", 724 | "Epoch 280 | Validation metric: 0.9992\n", 725 | "Epoch 281 | Validation metric: 0.9995\n", 726 | "Epoch 282 | Validation metric: 0.9996\n", 727 | "Epoch 283 | Validation metric: 0.9996\n", 728 | "Epoch 284 | Validation metric: 0.9994\n", 729 | "Epoch 285 | Validation metric: 0.9995\n", 730 | "Epoch 286 | Validation metric: 0.9997\n", 731 | "Epoch 287 | Validation metric: 0.9994\n", 732 | "Epoch 288 | Validation metric: 0.9997\n", 733 | "Epoch 289 | Validation metric: 0.9995\n", 734 | "Epoch 290 | Validation metric: 0.9995\n", 735 | "Epoch 291 | Validation metric: 0.9995\n", 736 | "Epoch 292 | Validation metric: 0.9997\n", 737 | "Epoch 293 | Validation metric: 0.9997\n", 738 | "Epoch 294 | Validation metric: 0.9989\n", 739 | "Epoch 295 | Validation metric: 0.9997\n", 740 | "Epoch 296 | Validation metric: 0.9995\n", 741 | "Epoch 297 | Validation metric: 0.9993\n", 742 | "Epoch 298 | Validation metric: 0.9996\n", 743 | "Epoch 299 | Validation metric: 0.9997\n", 744 | "Epoch 300 | Validation metric: 0.9998\n", 745 | "Epoch 301 | Validation metric: 0.9994\n", 746 | "Epoch 302 | Validation metric: 0.9993\n", 747 | "Epoch 303 | Validation metric: 0.9997\n", 748 | "Epoch 304 | Validation metric: 0.9997\n", 749 | "Epoch 305 | Validation metric: 0.9997\n", 750 | "Epoch 306 | Validation metric: 0.9995\n", 751 | "Epoch 307 | Validation metric: 0.9997\n", 752 | "Epoch 308 | Validation metric: 0.9995\n", 753 | "Epoch 309 | Validation metric: 0.9996\n", 754 | "Epoch 310 | Validation metric: 0.9990\n", 755 | "Epoch 311 | Validation metric: 0.9997\n", 756 | "Epoch 312 | Validation metric: 0.9996\n", 757 | "Epoch 313 | Validation metric: 0.9995\n", 758 | "Epoch 314 | Validation metric: 0.9996\n", 759 | "Epoch 315 | Validation metric: 0.9991\n", 760 | "Epoch 316 | Validation metric: 0.9996\n", 761 | "Epoch 317 | Validation metric: 0.9996\n", 762 | "Epoch 318 | Validation metric: 0.9997\n", 763 | "Epoch 319 | Validation metric: 0.9995\n", 764 | "Epoch 320 | Validation metric: 0.9997\n", 765 | "Epoch 321 | Validation metric: 0.9995\n", 766 | "Epoch 322 | Validation metric: 0.9993\n", 767 | "Epoch 323 | Validation metric: 0.9996\n", 768 | "Epoch 324 | Validation metric: 0.9995\n", 769 | "Epoch 325 | Validation metric: 0.9996\n", 770 | "Epoch 326 | Validation metric: 0.9992\n", 771 | "Epoch 327 | Validation metric: 0.9997\n", 772 | "Epoch 328 | Validation metric: 0.9994\n", 773 | "Epoch 329 | Validation metric: 0.9995\n", 774 | "Epoch 330 | Validation metric: 0.9996\n", 775 | "Epoch 331 | Validation metric: 0.9996\n", 776 | "Epoch 332 | Validation metric: 0.9997\n", 777 | "Epoch 333 | Validation metric: 0.9996\n", 778 | "Epoch 334 | Validation metric: 0.9997\n", 779 | "Epoch 335 | Validation metric: 0.9996\n", 780 | "Epoch 336 | Validation metric: 0.9997\n", 781 | "Epoch 337 | Validation metric: 0.9997\n", 782 | "Epoch 338 | Validation metric: 0.9995\n", 783 | "Epoch 339 | Validation metric: 0.9997\n", 784 | "Epoch 340 | Validation metric: 0.9996\n", 785 | "Epoch 341 | Validation metric: 0.9997\n", 786 | "Epoch 342 | Validation metric: 0.9998 <<< BEST VALIDATION EPOCH\n", 787 | "Epoch 343 | Validation metric: 0.9995\n", 788 | "Epoch 344 | Validation metric: 0.9991\n", 789 | "Epoch 345 | Validation metric: 0.9997\n", 790 | "Epoch 346 | Validation metric: 0.9997\n", 791 | "Epoch 347 | Validation metric: 0.9989\n", 792 | "Epoch 348 | Validation metric: 0.9997\n", 793 | "Epoch 349 | Validation metric: 0.9997\n", 794 | "Epoch 350 | Validation metric: 0.9997\n", 795 | "Epoch 351 | Validation metric: 0.9996\n", 796 | "Epoch 352 | Validation metric: 0.9995\n", 797 | "Epoch 353 | Validation metric: 0.9997\n", 798 | "Epoch 354 | Validation metric: 0.9997\n", 799 | "Epoch 355 | Validation metric: 0.9998\n", 800 | "Epoch 356 | Validation metric: 0.9992\n", 801 | "Epoch 357 | Validation metric: 0.9998\n", 802 | "Epoch 358 | Validation metric: 0.9996\n", 803 | "Epoch 359 | Validation metric: 0.9997\n", 804 | "Epoch 360 | Validation metric: 0.9998\n", 805 | "Epoch 361 | Validation metric: 0.9996\n", 806 | "Epoch 362 | Validation metric: 0.9998 <<< BEST VALIDATION EPOCH\n", 807 | "Epoch 363 | Validation metric: 0.9997\n", 808 | "Epoch 364 | Validation metric: 0.9995\n", 809 | "Epoch 365 | Validation metric: 0.9996\n", 810 | "Epoch 366 | Validation metric: 0.9991\n", 811 | "Epoch 367 | Validation metric: 0.9997\n", 812 | "Epoch 368 | Validation metric: 0.9998\n", 813 | "Epoch 369 | Validation metric: 0.9997\n", 814 | "Epoch 370 | Validation metric: 0.9998\n", 815 | "Epoch 371 | Validation metric: 0.9998\n", 816 | "Epoch 372 | Validation metric: 0.9998\n", 817 | "Epoch 373 | Validation metric: 0.9996\n", 818 | "Epoch 374 | Validation metric: 0.9998\n", 819 | "Epoch 375 | Validation metric: 0.9998\n", 820 | "Epoch 376 | Validation metric: 0.9998\n", 821 | "Epoch 377 | Validation metric: 0.9997\n", 822 | "Epoch 378 | Validation metric: 0.9989\n", 823 | "Epoch 379 | Validation metric: 0.9998\n", 824 | "Epoch 380 | Validation metric: 0.9997\n", 825 | "Epoch 381 | Validation metric: 0.9996\n", 826 | "Epoch 382 | Validation metric: 0.9997\n", 827 | "Epoch 383 | Validation metric: 0.9997\n", 828 | "Epoch 384 | Validation metric: 0.9997\n", 829 | "Epoch 385 | Validation metric: 0.9997\n", 830 | "Epoch 386 | Validation metric: 0.9997\n", 831 | "Epoch 387 | Validation metric: 0.9998\n", 832 | "Epoch 388 | Validation metric: 0.9997\n", 833 | "Epoch 389 | Validation metric: 0.9984\n", 834 | "Epoch 390 | Validation metric: 0.9996\n", 835 | "Epoch 391 | Validation metric: 0.9998\n", 836 | "Epoch 392 | Validation metric: 0.9996\n", 837 | "Epoch 393 | Validation metric: 0.9997\n", 838 | "Epoch 394 | Validation metric: 0.9995\n", 839 | "Epoch 395 | Validation metric: 0.9996\n", 840 | "Epoch 396 | Validation metric: 0.9986\n", 841 | "Epoch 397 | Validation metric: 0.9998\n", 842 | "Epoch 398 | Validation metric: 0.9997\n", 843 | "Epoch 399 | Validation metric: 0.9994\n", 844 | "Epoch 400 | Validation metric: 0.9998\n", 845 | "Epoch 401 | Validation metric: 0.9997\n", 846 | "Epoch 402 | Validation metric: 0.9997\n", 847 | "Epoch 403 | Validation metric: 0.9998\n", 848 | "Epoch 404 | Validation metric: 0.9994\n" 849 | ] 850 | }, 851 | { 852 | "name": "stdout", 853 | "output_type": "stream", 854 | "text": [ 855 | "Epoch 405 | Validation metric: 0.9997\n", 856 | "Epoch 406 | Validation metric: 0.9995\n", 857 | "Epoch 407 | Validation metric: 0.9997\n", 858 | "Epoch 408 | Validation metric: 0.9997\n", 859 | "Epoch 409 | Validation metric: 0.9998 <<< BEST VALIDATION EPOCH\n", 860 | "Epoch 410 | Validation metric: 0.9998\n", 861 | "Epoch 411 | Validation metric: 0.9997\n", 862 | "Epoch 412 | Validation metric: 0.9998\n", 863 | "Epoch 413 | Validation metric: 0.9996\n", 864 | "Epoch 414 | Validation metric: 0.9997\n", 865 | "Epoch 415 | Validation metric: 0.9998\n", 866 | "Epoch 416 | Validation metric: 0.9998\n", 867 | "Epoch 417 | Validation metric: 0.9998\n", 868 | "Epoch 418 | Validation metric: 0.9997\n", 869 | "Epoch 419 | Validation metric: 0.9997\n", 870 | "Epoch 420 | Validation metric: 0.9998\n", 871 | "Epoch 421 | Validation metric: 0.9997\n", 872 | "Epoch 422 | Validation metric: 0.9996\n", 873 | "Epoch 423 | Validation metric: 0.9998\n", 874 | "Epoch 424 | Validation metric: 0.9993\n", 875 | "Epoch 425 | Validation metric: 0.9998\n", 876 | "Epoch 426 | Validation metric: 0.9997\n", 877 | "Epoch 427 | Validation metric: 0.9996\n", 878 | "Epoch 428 | Validation metric: 0.9996\n", 879 | "Epoch 429 | Validation metric: 0.9992\n", 880 | "Epoch 430 | Validation metric: 0.9998\n", 881 | "Epoch 431 | Validation metric: 0.9998 <<< BEST VALIDATION EPOCH\n", 882 | "Epoch 432 | Validation metric: 0.9998\n", 883 | "Epoch 433 | Validation metric: 0.9998\n", 884 | "Epoch 434 | Validation metric: 0.9996\n", 885 | "Epoch 435 | Validation metric: 0.9997\n", 886 | "Epoch 436 | Validation metric: 0.9998\n", 887 | "Epoch 437 | Validation metric: 0.9997\n", 888 | "Epoch 438 | Validation metric: 0.9998\n", 889 | "Epoch 439 | Validation metric: 0.9997\n", 890 | "Epoch 440 | Validation metric: 0.9998\n", 891 | "Epoch 441 | Validation metric: 0.9996\n", 892 | "Epoch 442 | Validation metric: 0.9995\n", 893 | "Epoch 443 | Validation metric: 0.9998\n", 894 | "Epoch 444 | Validation metric: 0.9995\n", 895 | "Epoch 445 | Validation metric: 0.9997\n", 896 | "Epoch 446 | Validation metric: 0.9997\n", 897 | "Epoch 447 | Validation metric: 0.9998\n", 898 | "Epoch 448 | Validation metric: 0.9997\n", 899 | "Epoch 449 | Validation metric: 0.9996\n", 900 | "Epoch 450 | Validation metric: 0.9998\n", 901 | "Epoch 451 | Validation metric: 0.9996\n", 902 | "Epoch 452 | Validation metric: 0.9997\n", 903 | "Epoch 453 | Validation metric: 0.9997\n", 904 | "Epoch 454 | Validation metric: 0.9997\n", 905 | "Epoch 455 | Validation metric: 0.9998\n", 906 | "Epoch 456 | Validation metric: 0.9998\n", 907 | "Epoch 457 | Validation metric: 0.9998\n", 908 | "Epoch 458 | Validation metric: 0.9994\n", 909 | "Epoch 459 | Validation metric: 0.9997\n", 910 | "Epoch 460 | Validation metric: 0.9994\n", 911 | "Epoch 461 | Validation metric: 0.9998\n", 912 | "Epoch 462 | Validation metric: 0.9992\n", 913 | "Epoch 463 | Validation metric: 0.9998\n", 914 | "Epoch 464 | Validation metric: 0.9998\n", 915 | "Epoch 465 | Validation metric: 0.9998\n", 916 | "Epoch 466 | Validation metric: 0.9997\n", 917 | "Epoch 467 | Validation metric: 0.9998\n", 918 | "Epoch 468 | Validation metric: 0.9995\n", 919 | "Epoch 469 | Validation metric: 0.9998\n", 920 | "Epoch 470 | Validation metric: 0.9998\n", 921 | "Epoch 471 | Validation metric: 0.9997\n", 922 | "Epoch 472 | Validation metric: 0.9998 <<< BEST VALIDATION EPOCH\n", 923 | "Epoch 473 | Validation metric: 0.9997\n", 924 | "Epoch 474 | Validation metric: 0.9998\n", 925 | "Epoch 475 | Validation metric: 0.9996\n", 926 | "Epoch 476 | Validation metric: 0.9996\n", 927 | "Epoch 477 | Validation metric: 0.9997\n", 928 | "Epoch 478 | Validation metric: 0.9998\n", 929 | "Epoch 479 | Validation metric: 0.9996\n", 930 | "Epoch 480 | Validation metric: 0.9998\n", 931 | "Epoch 481 | Validation metric: 0.9994\n", 932 | "Epoch 482 | Validation metric: 0.9999 <<< BEST VALIDATION EPOCH\n", 933 | "Epoch 483 | Validation metric: 0.9995\n", 934 | "Epoch 484 | Validation metric: 0.9998\n", 935 | "Epoch 485 | Validation metric: 0.9997\n", 936 | "Epoch 486 | Validation metric: 0.9994\n", 937 | "Epoch 487 | Validation metric: 0.9994\n", 938 | "Epoch 488 | Validation metric: 0.9998\n", 939 | "Epoch 489 | Validation metric: 0.9998\n", 940 | "Epoch 490 | Validation metric: 0.9998\n", 941 | "Epoch 491 | Validation metric: 0.9998\n", 942 | "Epoch 492 | Validation metric: 0.9998\n", 943 | "Epoch 493 | Validation metric: 0.9997\n", 944 | "Epoch 494 | Validation metric: 0.9997\n", 945 | "Epoch 495 | Validation metric: 0.9998\n", 946 | "Epoch 496 | Validation metric: 0.9998\n", 947 | "Epoch 497 | Validation metric: 0.9999 <<< BEST VALIDATION EPOCH\n", 948 | "Epoch 498 | Validation metric: 0.9999\n", 949 | "Epoch 499 | Validation metric: 0.9997\n", 950 | "Epoch 500 | Validation metric: 0.9998\n", 951 | "Epoch 501 | Validation metric: 0.9995\n", 952 | "Epoch 502 | Validation metric: 0.9997\n", 953 | "Epoch 503 | Validation metric: 0.9998\n", 954 | "Epoch 504 | Validation metric: 0.9997\n", 955 | "Epoch 505 | Validation metric: 0.9998\n", 956 | "Epoch 506 | Validation metric: 0.9997\n", 957 | "Epoch 507 | Validation metric: 0.9998\n", 958 | "Epoch 508 | Validation metric: 0.9998\n", 959 | "Epoch 509 | Validation metric: 0.9997\n", 960 | "Epoch 510 | Validation metric: 0.9997\n", 961 | "Epoch 511 | Validation metric: 0.9998\n", 962 | "Epoch 512 | Validation metric: 0.9998\n", 963 | "Epoch 513 | Validation metric: 0.9998\n", 964 | "Epoch 514 | Validation metric: 0.9999\n", 965 | "Epoch 515 | Validation metric: 0.9999\n", 966 | "Epoch 516 | Validation metric: 0.9997\n", 967 | "Epoch 517 | Validation metric: 0.9998\n", 968 | "Epoch 518 | Validation metric: 0.9998\n", 969 | "Epoch 519 | Validation metric: 0.9989\n", 970 | "Epoch 520 | Validation metric: 0.9998\n", 971 | "Epoch 521 | Validation metric: 0.9998\n", 972 | "Epoch 522 | Validation metric: 0.9998\n", 973 | "Epoch 523 | Validation metric: 0.9998\n", 974 | "Epoch 524 | Validation metric: 0.9997\n", 975 | "Epoch 525 | Validation metric: 0.9998\n", 976 | "Epoch 526 | Validation metric: 0.9998\n", 977 | "Epoch 527 | Validation metric: 0.9994\n", 978 | "Epoch 528 | Validation metric: 0.9994\n", 979 | "Epoch 529 | Validation metric: 0.9998\n", 980 | "Epoch 530 | Validation metric: 0.9996\n", 981 | "Epoch 531 | Validation metric: 0.9997\n", 982 | "Epoch 532 | Validation metric: 0.9998\n" 983 | ] 984 | }, 985 | { 986 | "name": "stderr", 987 | "output_type": "stream", 988 | "text": [ 989 | "100%|██████████████████████████████████████████████████████████████████████████| 6234/6234 [00:00<00:00, 520997.29it/s]\n" 990 | ] 991 | } 992 | ], 993 | "source": [ 994 | "seed = 42\n", 995 | "clf=FTTransformer(seed,\"ResNet\",n_epochs=532,batch_size=256)\n", 996 | "aucroc = []\n", 997 | "aucpr = []\n", 998 | "loss_ = []\n", 999 | "clf = clf.fit(X_train=X_train, y_train=y_train.squeeze(1),X_test=X_test,y_test=y_test)\n", 1000 | "import platform\n", 1001 | "y_val_scores = clf.predict_score(X_val) #返回未知数据上的异常值 (分值越大越异常) # outlier scores\n", 1002 | "#记录文件名和对应的异常得分\n", 1003 | "predict_result={}\n", 1004 | "for i in tqdm(range(len(test1_files))):\n", 1005 | " file=test1_files[i]\n", 1006 | " #如果是window系统:\n", 1007 | " if platform.system().lower() == 'windows':\n", 1008 | " name=file.split('\\\\')[-1]\n", 1009 | " #如果是linux系统\n", 1010 | " elif platform.system().lower() == 'linux':\n", 1011 | " name=file.split('/')[-1]\n", 1012 | " predict_result[name]=y_val_scores[i]\n", 1013 | "predict_score=pd.DataFrame(list(predict_result.items()),columns=['file_name','score'])#列名必须为这俩个\n", 1014 | "predict_score.to_csv(f'submision.csv',index = False) #保存为比赛要求的csv文件" 1015 | ] 1016 | }, 1017 | { 1018 | "cell_type": "code", 1019 | "execution_count": 11, 1020 | "metadata": {}, 1021 | "outputs": [ 1022 | { 1023 | "data": { 1024 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlkAAADlCAYAAABgb+JgAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAABFjElEQVR4nO3deXxU9dX48c+ZLXsChJ2wKYigLGrAXXDHpYpa61IVt/LY1trNx0drF+tTq1Xb/upj61Kliq2iYrVqaREXXAEJq7IJIkICQlYge2bm/P64N8MQAhlgQnKn5/16zSt37v3eO2ey3Jz5rqKqGGOMMcaY5PJ1dADGGGOMManIkixjjDHGmHZgSZYxxhhjTDuwJMsYY4wxph1YkmWMMcYY0w4syTLGGGOMaQeBjg6gpe7du+ugQYM6OgxjzEG0cOHCMlXt0dFxJIPdw4z5z7K3+1enS7IGDRpEUVFRR4dhjDmIROTLjo4hWeweZsx/lr3dv6y50BhjjDGmHViSZYwxxhjTDizJMsYYY4xpB232yRKRqcD5wFZVPbKV4wL8ATgXqAWuVdVF7rHJwE/dor9S1aeTFbgxxiRCRCbi3KP8wBOqel+L4zcB3wUiQDUwRVVXuMfuAG5wj92iqrMOZuzGeFFTUxPFxcXU19d3dChJlZ6eTkFBAcFgMOFzEun4/hTwMDBtD8fPAYa6j2OBR4BjRaQb8AugEFBgoYi8qqqVCUdnjDEHQET8wB+BM4FiYIF7H1oRV+xZVX3ULX8B8DtgooiMAC4HjgD6Am+KyGGqGjmob8IYjykuLiYnJ4dBgwbh1MN4n6pSXl5OcXExgwcPTvi8NpsLVfU9oGIvRS4EpqljHtBFRPoAZwOzVbXCTaxmAxMTjswYYw7cOGCtqq5T1UZgOs49K0ZVt8c9zcL5UIhbbrqqNqjqF8Ba93rGmL2or68nPz8/ZRIsABEhPz9/n2vnkjGFQz9gY9zzYnffnvZ3mMZwlKgqaQEfdU0RMoJ+1m6tJhTwEYkqjZEoIb+PbXVN1DdFyUrz0y0rRE5akJKqOipqGtm8rY6miBL0CznpAYor60gP+snLCJKfFWJbXRNVdU3kpgepbmgiIxQgNz1AVW0TPXLSKKtuoKy6Eb9At+w0stP8hPx+Nm+rIxxVVMHvg8raJvwipIf8RCJR/D4hPeinKaJkhvw0hCP0zEmnIRyhS2aIzdvqqGmI4BNhe30TAZ+Qmx5k87Z6jh7YhYamKKXVDTRFotQ2RsjLCOL3CQGfEI4q4YhSXt1Ar7x0ahrCVNeH8fmErpkhstL8+ESIRJWKmkZCAR9dMoOsK60hM+SnS2aIusYwDeEoNQ0RumQG3Rij1DSEiSrUNoYRIDMtgABRhYZwhKhC79x0An5hW20ToYCPytpG0oN+0gM+KmqbQJWg30dGyM/2uiay0gI0hKM0hqMUdM3gi/Ia8rNCVNeHSQv6CUeUqrpGumSEiKrz/crNCFLXGKGuKUJjOEpeRpCKmkYiqqQH/Ph9zh9RTUOYuqYI2+vC9O+WgU+ErTvqEYRQwEdDOEJWKEBUFRGhMRwl6BcCfp/z+gGf+3NUFIhElez0ACG/j6raJnLSA9Q3RWkIR0gPOj/HoN9Hl4wQtY1hAn4hHFEawlHA+fQU9Pvw+ZzXCviEYMBHyO9zYgC214fpkhmkvimK3wc+EVTB5xMqaxrJSgvgE6huCBMJN5KVkUF60E9dY5jq+iZyMkKkB/2gSnVDhKgqkaiiKL1y0mmKRFFgR32YUMBHVihAUyRKUyRKTWMYQcgI+Qn5faQFfAT8PsqrG7h8XH+G9MzpuD94R2v3oWNbFhKR7wI/AkLAaXHnzmtxbtLuYR+uLePFoo3cPelIctMTb34wxgtSKcFqtj/vqVPMkyUiU4ApAAMGDEjKNasbwtQ2hvnnss08v2AjZdWNlFU3ABD0C00RbeMKe+YjShQfoIySdXyig9FdKgUVkLivOx0qJfSXrazX3qzXPrsc8xMhgp+eVHKUbw1+ogSI8oX2ZqUOIIrPfV0Qoru8Zhd2EMFPIwHO8hVRot0pJ5fr/f/ilvAlVJJLd7YhKKV0iZ0XJEwEH3lUc4X/HaZGj6JU8zjat4YKzeFL7U0ZebHYhCi51FJPiGv9s0DqeCh8HvmynR5UUUMGW7ULpXTBR5RjfSuJqI+rAm9SrRm8EJlAjtQiKD2lip5Uck/kbGrI4CTfJ5RpHoKSQQN1pDHBt5RB8hUNEuSJ8LnkUEcDAXKoo5+U8bF24ZLA+7wXGcVQXzHVGmIbWdRqGo1ST5nm0kO2EcZPnYYopQvD5UtmRMYxOTibIBEWRobQSICeUkW1ZjDEV8JgalkQHUYEH3cFp9GN7azUgXTx1bEjmsZG7cExvs9YIwPZHO1GgDDV0TSaJMgAfxm51NIlWkWJdmez5rNBe3KYVHCS7xMG+7bQpH58RKmVdN7To6mKpJPuCxPSRvKkllN8SwF4U8fyop7O5fIGNZLJDs2gi26ni+7ALxHqNUR33w7mR4aRL4qPKFWaSW+pZGV0AKNkI319FQSIMMi3hV5UUKa5vBo5gXN9yznct5E6DfFGtBBFiPpCdJcqurGddBpYGhlEWANspQuj/NsYzWo+ioygu2871WSxPZpOgZSyRXowhC9ZHe1PpeZwhH8r5V1/xJCep+7339nBpKp/BP4oIlfi9COdvC/n78897IuyGl5ZsomfnDfckixjkiw7O5vq6uqODiMpSVYJ0D/ueYG7rwSY0GL/nNYuoKqPA48DFBYW7nf2o6pU1TYxb105P/vHp5RVN+5yPCPop1/XDIb1yqFXbjorN28nzRdm5IAerPxyM2eOOYSAQBqNdN/wLxrVx2c9z6FofSWF29+gflsZV6e9S96ONShCU1ZvQjWbY9dvTO+OL9KAP9JAQ3o+ocZKGrsMIRoJE/ZnsHHQpRxR9JNY+Zqex/BVNI/6IecwdNVjhKrWtvkew5k98TVswxdpoLjwJ2SULqHk0MsZNu8u0mo3t3rOxD61NKTn079kJhINAxDJ6Ia/zmkFjqR3xV/vdJW7jed3/Z6Kj9r+E8jYPJ9wdh9ClU6MjTkDCO3YAMAtgVfajLvZlYG3d9t3W/AFmnIHEty+9/kor/HP3uOxy/xzEo4B4GfBv8W2r/Pv4fXY9fVGyhfgCwJNjMCJtXtoPbAeGnfsbHzvOQLScmHjSg6l9Z9JUJxuPTnpaZzXOB8k3Gq5M2QBZ8gCCGRAMAPqq8AXQDO6IdVfxcqNCKzf/eQ9vK/usp3rA/+OPc+QRi70f4QGMpD0XEjv4pxcVswQf/Fu5x8a2LTzSfN79gUh2sQJPqerk/oCSK8ftB7AwbWn+9OeTMfpV7pP5+7PPcznfirW/f+8Z4zp5JKRZL0K3Cwi03Gq4bep6mYRmQX8WkS6uuXOAu5Iwuvt0e9nf8ZDb+9MVCYM68H3ThvKEX1zSYvWIitfg/Q8yO8Nn82C0eNg6nlOIwA4t8+MblC3swvaKSMXc2OwDkpfd3a4eZuguyRYAKH6sth2upvwpIeCsOlTAI4oW7xL+aytCzkUoGz3xINuh0DFOhh1GXzxHuxwrheo3RorUlD0awDyv5y51+9Lr9IPd9vnj3uPzQkW/Y+FjfN3Fhp+AVL+OVkNpdBUE0uwACfBOvWnkJYN/759r6/PhJ9AUy2sf9/5Z7zRbYH5xjOw4M/wxXttJlgMPQvWvNH6sQv/COVroctAePd+2OEmAaEcJ/n5xjT4/B1Y+Jed5/QZDWNvhIyu8MX7UFcJY66AZy7aWabwBuhxOMz7E1R+Aef9Fo66Bv5+I6z4B9z4NhQc4/yXLF8LDxfCuClw7gPO+a//CIqehFPvdH6WS5+DnkfAeQ/CmtngC8CpP4HGGti6ArJ7Qk5fZ/+aWbB0Ogw5HWor4OhrnFhVIdqE+AKw5VPYsQWyusNzV8AVz0HvURBpgLI18Ph4J46bPoSqDTDgOOfaG+bCujlw4g+cuHqPhC3LkV4jIC2uea9qg/O7l94FGqud+EI5zuv5/FBTBn92a6p+sgnC9fDMJECQK1+ArPy9/0wPjgXAUBEZjPMXfjlwZXwBERmqqmvcp+cBzduvAs+KyO9wOr4PBT5OVmDNLQ9Ry7KMaTeqym233ca//vUvRISf/vSnXHbZZWzevJnLLruM7du3Ew6HeeSRRzjhhBO44YYbKCoqQkS4/vrr+eEPf3hAr5/IFA7P4dRIdReRYpwRg0E3+EeBmTjTN6zFmcLhOvdYhYj8L85NDuBuVd1bB/oD8mnJtl0SrA9vP41+XTKcJ+FGePZy+PKDti9U1yLET17cvczPymD7Jti8FEZcAB89DF0HQUYXWPVPOP67MON6OO1nMOgkWPsWzPwxVK6HAcfD1a84/+xWvAIv3eBc80crYcmzULoKJt4HmfnQVAehzJ2vu/5DyO4FXQY4//RKFsKXHznbgTQ45jrnH1//cU7S0FQLgXR44FDn/Gv/CduKYc598J15sGkx5PRyErrGWue13r0f3rkHjr8Zzr5n1/cdjTjJRcMOyOwGw7/m7G+ohnd+tbPctz9y/jHv+AqqvoQjL971OpEm2F7ifM9GXOBcs+9RzvuadqGTANxRAsULoMcwqCl1kqIvP3JiHnGhk3B98R58/S87/1sBFF4Hq/8NJUUw4Q4nUeg22DnnnN8451R96SRYzZrfR7zCG5ykSgSOvtr5WWR2c459o8VAWxHoPtR53/lDdr9WMBNGTHKSrLN/BQNPcB7N0rKdn1m8Yec4j5ZEwJfmbPcZDc0tzreu3lnGH4C+Y+D2Dc7vQddB0Dtu9pXDznYe4Pz8AQbs1k3J+XkcddXu++OPBzKc9xIIOY9vtfKBoQOpalhEbgZm4dTtTVXV5SJyN1Ckqs0fEs8AmoBK3KZCt9wLwAogDHw3mSMLfdIcY7KuaIxp6e9//ztLlixh6dKllJWVMXbsWE455RSeffZZzj77bO68804ikQi1tbUsWbKEkpISPv3UqRipqqo64NcX7WR/4YWFhbqv635Fo8ohP3Fqc56cXMixh+STnebmjw074KGjoWbrXq7g+t4iyD/Uuet9/pZz7ro5sPCpnWWuegmGnLFP8QHOP2kEguk79zXWwIvXwhm/hF4j9v2aiVo3B7ofBrl92y5bvw3e+BmccdfOpKLN678L0y6Acx90msoGnbj/sYYbnJqb3D5tl20PNWXgD0F67oFf68u58JeJ8J350PNwJzFP5GfgJeEGEL+T2B0AEVmoqoVJiqpDJXoPe2HBRm57aRkf/M+pFHTNbLO8MV6xcuVKhg8fDsAvX1vOik3b2zhj34zom8svvnbEXss098n64Q9/yMiRI7n++usBuPrqq7n00kvp0qUL119/PVdddRWTJk1izJgxVFZWUlhYyLnnnst5553HWWedhc+36yQM8e+t2d7uXykx4/uS4ioAzhrRi9MO77kzwQKYOnH3BKvXkc4n7uzecNx3nYQiq6dTowNObcGQM+CIi2Dib+DoyTtrPvqM2b8ggxm7JlgAoSz45ovtm2ABHDIh8X/u6XlwwUOJJ1gAh4yHm4uc79GBJFjg1Mh1VIIFTlNYMhIsgIHHw13bnAQLUi/BAufndYAJ1n8qsZosYzrMKaecwnvvvUe/fv249tprmTZtGl27dmXp0qVMmDCBRx99lBtvvLHtC7UhJe6O731Wigj85pJRO4dYNtY4TUNbnGo/Bp7oNpOcA10HOv8c4ptYTtpDu2sw3Uk6VJ0ap7Ts9n0zXtV9aEdHYIyniHV8N/8B2qpxam8nn3wyjz32GJMnT6aiooL33nuPBx54gC+//JKCggK+9a1v0dDQwKJFizj33HMJhUJccsklDBs2jKuu2kt3iQSlRJK1avMODumeRdes0M6d9x8K4TqnturMu2HkpeA7gIo7EUuwjDFJE+uThWVZxrSXiy66iLlz5zJ69GhEhPvvv5/evXvz9NNP88ADDxAMBsnOzmbatGmUlJRw3XXXEY068xTee++9B/z6KZFkbaioZUC3uD4NTfVOggVwws0w+rKOCcwYY/Zg5+jCjo3DmFTUPEeWiPDAAw/wwAMP7HJ88uTJTJ68+3R4ixYtSmocnu+TtWbLDlZs3k7/+CSrdOXO7THfPPhBGWNMG3bOk2VZljGpyvNJ1mtLnTmRjh0cNyfPendeqO8v27cO3MYYc5BZTZYxqcvzzYUVtY10zQxy3qg+zhD5RdNgzr3OKLkuyVmixxhjks0Xm9/NsixjUpXnk6zKmqadHd5f+/7OWcGPvmbXSSqNMaYTsT5ZJpWpasotEr0/Tfueby6sqGkkvznJaohbDHLEpA6JxxhjEmFrF5pUlZ6eTnl5eUr1N1RVysvLSU9Pb7twHO/XZNU27hxZ6ItbETere8cEZIwxCWj+jG9rF5pUU1BQQHFxMaWlpR0dSlKlp6dTUFCwT+d4Pskqr2lkTP8uzpNtxTsPZPXokHiMMSYRNhmpSVXBYJDBgwd3dBidgqebC5siUcqqG+iZ61bf1VftPBjK6pCYjDEmETv7ZFmWZUyq8nRNVumOBlShd26683Gwfhsccx0cc21Hh2aMMXvlS7FOwcaY3Xm6Juur7fUA9M5Lg8Zq0KizyHPfMR0bmDHGtMH6ZBmT+jydZG11k6yeOelQ+pmzMz23AyMyxpjENC+lajmWManL00lWXVMEgKy0ADxxmrMzPa8DIzLGmMSIW5dlNVnGpK6EkiwRmSgiq0VkrYjc3srxgSLylogsE5E5IlIQd+x+EVkuIitF5CFJ4uxkjWFnpexQIO5tBDKSdXljjGk3zXdCS7GMSV1tJlki4gf+CJwDjACuEJERLYo9CExT1VHA3cC97rknACcCo4AjgbHA+GQF3xhxbk9Bf1zeZjVZxhgPsAWijUl9idRkjQPWquo6VW0EpgMXtigzAnjb3X4n7rgC6UAISAOCwJYDDbpZk1uTleYTED8cMgEGHp+syxtjTLuxZXWMSX2JJFn9gI1xz4vdffGWAhe72xcBOSKSr6pzcZKuze5jlqqubPkCIjJFRIpEpGhfZohtjDhJVjC8AzQCQ89K+FxjjOlItqyOMakvWR3fbwXGi8hinObAEiAiIkOA4UABTmJ2moic3PJkVX1cVQtVtbBHj8Rnam+uyQo2VDg7Mm0pHWOMN9gUDsakvkQmIy0B+sc9L3D3xajqJtyaLBHJBi5R1SoR+RYwT1Wr3WP/Ao4H3k9C7DRGoohAoHG7syOjSzIua4wx7c6W1TEm9SVSk7UAGCoig0UkBFwOvBpfQES6i0jzte4AprrbG3BquAIiEsSp5dqtuXB/NUaiBP0+pMFNstJsjixjzK4SGB39IxFZ4Y6OfktEBsYdi4jIEvfxastzDywu56t1fDcmdbWZZKlqGLgZmIWTIL2gqstF5G4RucAtNgFYLSKfAb2Ae9z9M4DPgU9w+m0tVdXXkhV8U1hJ8/ugYYezIy0nWZc2xqSABEdHLwYK3dHRM4D7447VqeoY93EBSRTrk5XMixpjOpWE1i5U1ZnAzBb7fh63PQPn5tTyvAjwXwcY4x41RiIEAz6od2uybLZ3Y8yuYqOjAUSkeXT0iuYCqvpOXPl5wFUHIzBbINqY1OfpGd+bwurMkWXNhcaY1iUyOjreDcC/4p6nuyOf54nIpD2dtD8jpH2x5sKEihtjPCihmqzOqikSdWZ7t+ZCY8wBEpGrgEJ2nTB5oKqWiMghwNsi8omqft7yXFV9HHgcoLCwMMG0yZbVMSbVebomq8Ht+E79dghlg8/f0SEZYzqXNkdHA4jIGcCdwAWq2tC8X1VL3K/rgDnAUckKzGfL6hiT8jydZDWFo4T8Pmjc4SRZxhizq0RGRx8FPIaTYG2N299VRNLc7e44S4StIEnEltUxJuV5urmwsbm5sKkegrYwtDFmV6oaFpHm0dF+YGrz6GigSFVfBR4AsoEX3cRngzuScDjwmIhEcT6Q3qeqSUuyrE+WManP00lWU3NzYbgOAukdHY4xphNKYHT0GXs47yNgZHvFJbE+We31CsaYjubx5kJ1mgub6iFoSZYxxjtsMlJjUp+na7L6dc0gPeiD7fUQsOZCY4x37Jwnq2PjMMa0H08nWb+/bIyz8US9dXw3xnhK84zvNr7QmNTl6ebCGOv4bozxGKvJMib1pUaSFa63ju/GGE+JrV1oSZYxKcuSLGOM6QDNjYU247sxqSs1kqymOhtdaIzxlObJSC3JMiZ1pUaSFW6w0YXGGE9pnozUGJO6UiTJsposY4y3WE2WMakvoSRLRCaKyGoRWSsit7dyfKCIvCUiy0RkjogUxB0bICJviMhKEVkhIoOSGD9EwhANW58sY4yn2LI6xqS+NpMsEfEDfwTOAUYAV4jIiBbFHgSmqeoo4G7g3rhj04AHVHU4MA7YSjJtL3G+ZuYn9bLGGNOebFkdY1JfIjVZ44C1qrpOVRuB6cCFLcqMAN52t99pPu4mYwFVnQ2gqtWqWpuUyJttWuR87XtUUi9rjDHtyZbVMSb1JZJk9QM2xj0vdvfFWwpc7G5fBOSISD5wGFAlIn8XkcUi8oBbM5Y8Feucrz0OT+pljTGmPYk1FxqT8pLV8f1WYLyILAbGAyVABGfZnpPd42OBQ4BrW54sIlNEpEhEikpLS/ftlSNNzlfrk2WM8ZDYZKS2rI4xKSuRJKsE6B/3vMDdF6Oqm1T1YlU9CrjT3VeFU+u1xG1qDAOvAEe3fAFVfVxVC1W1sEePHvv2DqJhQMCXGgMljTH/GWxZHWNSXyKZyQJgqIgMFpEQcDnwanwBEekuIs3XugOYGnduFxFpzpxOA1YceNhxIk3gDyb1ksYY095sWR1jUl+bSZZbA3UzMAtYCbygqstF5G4RucAtNgFYLSKfAb2Ae9xzIzhNhW+JyCc4K0n8OanvIBoGXyCplzTGmPZmy+oYk/oSyk5UdSYws8W+n8dtzwBm7OHc2cCoA4hx76Jh8FlNljHGWyTWJ8sYk6q835Ep0gR+q8kyxniLTeFgTOrzfpJlzYXGGA+yPlnGpL4USbKsudAY4y3WJ8uY1Of9JMuaC40xe5HA2qs/ctdVXeauwTow7thkEVnjPiYnMy6ryTIm9Xk/ybLmQmPMHiS49upioNBde3UGcL97bjfgF8CxOMuL/UJEuiYvOOeL1WQZk7pSIMlqsuZCY8yetLn2qqq+E7em6jycCZcBzgZmq2qFqlYCs4GJyQrMJ22XMcZ4m/eTrEjYmguNMXuSyNqr8W4A/rWf5+6T5ikcrCbLmNTl/ezEmguNMUkgIlcBhTjrr+7ruVOAKQADBgxI6ByfLRBtTMrzfk2WNRcaY/aszbVXAUTkDJx1Vy9Q1YZ9ORf2b/1VobkmK6HixhgPSoEkK2I1WcaYPUlk7dWjgMdwEqytcYdmAWeJSFe3w/tZ7r6kEOv4bkzK8352EmmCQKijozDGdEKqGhaR5rVX/cDU5rVXgSJVfRV4AMgGXnT7SW1Q1QtUtUJE/hcnUQO4W1UrkhVb8xQOxpjU5f0kK9oEvqyOjsIY00klsPbqGXs5dyowtT3iitVkWXuhMSkrBZoLreO7McZ7fLZAtDEpz/tJViQMfuv4bozxFltWx5jU5/0kK9pkNVnGGM8Rm8LBmJSXAkmWNRcaY7xHYmsXWpZlTKpKKMlKYIHVge7CqstEZI6IFLQ4nisixSLycLICj7HmQmOMR/nE+mQZk8raTLISXGD1QWCau8Dq3cC9LY7/L/DegYfbCqvJMsZ4lIhYnyxjUlgiNVltLrCKk3y97W6/E39cRI4BegFvHHi4rbA+WcYYj/KJzfhuTCpLJMlKZJHUpcDF7vZFQI6I5IuID/gtcOveXkBEpohIkYgUlZaWJhZ5s0iTNRcaYzzJ7xObJ8uYFJasju+3AuNFZDHO4qolQAT4DjBTVYv3dvL+rPsVY8vqGGM8Kujz0RiJdnQYxph2kkh20uYiqaq6CbcmS0SygUtUtUpEjgdOFpHv4CxbERKRalXdrfP8frPmQmOMRwUDPposyTImZSWSncQWWMVJri4HrowvICLdgQpVjQJ34C5DoarfjCtzLVCY1AQLrLnQGONZAZ8QjlhzoTGpqs3mQlUNA80LrK4EXmheYFVELnCLTQBWi8hnOJ3c72mneFsGB2rNhcYYbwr6rbnQmFSWUHaSwAKrM4AZbVzjKeCpfY5wb6Jh56vParKMMd4TCvisJsuYFObtGd+bkyy/1WQZY7wn4BPrk2VMCvN2khVpcr5ac6ExxoOCfh9NVpNlTMrydpJlzYXGGA8L+q0my5hUlhpJljUXGmM8KOj3EY5akmVMqvJ2kmXNhcYYDwv4haawNRcak6q8nWRFm5Msay40xnhP0O+jyWqyjElZHk+yIs5Xq8kyxniQ0/HdkixjUpW3k6zm5kLrk2WM8aCg32Z8NyaVeTvJstGFxpg2iMhEEVktImtFZLdlvUTkFBFZJCJhEfl6i2MREVniPl5NdmwBm/HdmJTm7SqgqHV8N8bsmYj4gT8CZwLFwAIReVVVV8QV2wBcC9zayiXqVHVMe8UX8tuM78akMm9nJ5HmKRysJssY06pxwFpVXQcgItOBC4FYkqWq691jB71KyWZ8Nya1pUhzobdzRWNMu+kHbIx7XuzuS1S6iBSJyDwRmZTUyIBgwDq+G5PKvJ2dWHOhMaZ9DVTVEhE5BHhbRD5R1c9bFhKRKcAUgAEDBiR88aBPbFkdY1KYt2uyYqMLrbnQGNOqEqB/3PMCd19CVLXE/boOmAMctYdyj6tqoaoW9ujRI+HgbAoHY1Kbt5Os2DxZlmQZY1q1ABgqIoNFJARcDiQ0SlBEuopImrvdHTiRuL5cyRAK+GgMW5JlTKpKKMlKYAj0QBF5S0SWicgcESlw948Rkbkistw9dllSo481F/qTelljTGpQ1TBwMzALWAm8oKrLReRuEbkAQETGikgxcCnwmIgsd08fDhSJyFLgHeC+FqMSD1hawE84qoStNsuYlNRmZ6YEh0A/CExT1adF5DTgXuBqoBa4RlXXiEhfYKGIzFLVqqREb82Fxpg2qOpMYGaLfT+P216A04zY8ryPgJHtGVt60Pmc2xiJEvB7u2HBGLO7RP6qY0OgVbURaB4CHW8E8La7/U7zcVX9TFXXuNubgK1A4h0W2qJuc6FYTZYxxnvSAs4tuKHJarKMSUWJJFmJDIFeClzsbl8E5IhIfnwBERkHhIDdRubsN3VH5Yh9AjTGeE9a0PmAWB+OdHAkxpj2kKzs5FZgvIgsBsbjjN6J3TVEpA/wDHCdqu72kU1Eprhz0RSVlpYm/qqxJEsOJHZjjOkQVpNlTGpLJMlqcwi0qm5S1YtV9SjgTndfFYCI5AL/BO5U1XmtvcD+Dn8Gq8kyxnhXWsCpyWqwEYbGpKREspM2h0CLSHeRWKZzBzDV3R8CXsbpFD8jeWG7dq8UM8YYz2ju+N5gzYXGpKQ2k6xEhkADE4DVIvIZ0Au4x93/DeAU4Nq4lezHJC16ay40xniY1WQZk9oSWo8mgSHQM4DdaqpU9a/AXw8wxr1F5n61JMsY4z1pbk1WfZPVZBmTirzdmam5udD6ZBljPMg6vhuT2rydnVhzoTHGw6y50JjU5u0ky5oLjTEeZh3fjUlt3k6ybDJSY4yHNddk1VmfLGNSkrezk1ifLKvJMsZ4T2aam2Q1WpJlTCrydpJlzYXGGA/LdJfVqbUky5iU5O0ky5oLjTEeFvD7CAV81DSGOzoUY0w78HZ2Ys2FxhiPywr5qW2wmixjUpG3k6wYS7KMMd6UGQpYTZYxKcrbSZY1FxpjPC4rzWqyjElV3s5OrLnQGONxmaEAtTaFgzEpydtJVmx0oTHGeJNTk2XNhcakIm8nWdZcaIzxuMxQgGpLsoxJSd7OTqy50Bjjcb1z0ympqkPVauaNSTXeTrJsMlJjTBtEZKKIrBaRtSJyeyvHTxGRRSISFpGvtzg2WUTWuI/J7RHfoT2y2FEfprS6oT0ub4zpQN5Osqy50BizFyLiB/4InAOMAK4QkREtim0ArgWebXFuN+AXwLHAOOAXItI12TEe0iMbgC9Ka5J9aWNMB0soO0ngk+BAEXlLRJaJyBwRKYg71n6fBK250Bizd+OAtaq6TlUbgenAhfEFVHW9qi4Doi3OPRuYraoVqloJzAYmJjvAblkhAKrqmpJ9aWNMB2szyUrwk+CDwDRVHQXcDdzrntvOnwStudAYs1f9gI1xz4vdfe19bsJy04MA7Ki3zu/GpJpEarLa/CSIk3y97W6/E3e8fT8JWnOhMaYTEJEpIlIkIkWlpaX7dG52egCAHfVWk2VMqkkkO0nk09xS4GJ3+yIgR0TyEzx3/29Q1lxojNm7EqB/3PMCd19Sz1XVx1W1UFULe/TosU8B5rhJVrXVZBmTcpJVBXQrMF5EFgPjcW5ECU9hvP83KGsuNMbs1QJgqIgMFpEQcDnwaoLnzgLOEpGubjeHs9x9SRX0+0gP+thhc2UZk3ISSbLa/DSnqptU9WJVPQq4091Xlci5BySWY1lzoTFmd6oaBm7GSY5WAi+o6nIRuVtELgAQkbEiUgxcCjwmIsvdcyuA/8VJ1BYAd7v7ki47LWjNhcakoEACZWKfBHESpMuBK+MLiEh3oEJVo8AdwFT30Czg13Gd3c9yjyeHNRcaY9qgqjOBmS32/TxuewHOB8DWzp3KzvtZu8lND1jHd2NSUJtVQIl8EgQmAKtF5DOgF3CPe247fxK05kJjjPflZ4f4alt9R4dhjEmyRGqyEvkkOAOYsYdz2++TYGx0oSVZxhjvGt4nl5cWFhONKj6f3c+MSRXe7sxkzYXGmBQwok8uNY0RNlbWdnQoxpgk8naShWJNhcYYr2teWmd9uSVZxqQSbydZqjay0BjjeYPyMwH4stzWLzQmlXg7Q9GoNRUaYzyvR04a2WkBlmys6uhQjDFJ5O0ky5oLjTEpQET42ui+vL5sM+FIy3WqjTFe5e0ky5oLjTEpYmS/PBrDUUqrGzo6FGNMkng7Q7HmQmNMiuidlwbAZpsvy5iU4e0ky5oLjTEpolduOgBbLMkyJmV4O8my5kJjTIro1yUDgHVlNsLQmFTh7QxF1ZoLjTEpoUtmiBHuzO81DbaOoTGpwNtJljUXGmNSyHdOPZR1ZTVMX7Cxo0MxxiSBt5Msay40xqSQ80f1ZWS/PJ77eANVtY1EotrRIRljDoC3MxSNWkWWMSalXHP8QNZurWbM3bN56K01HR2OMeYAeDvJsuZCY0yKGdE3N7b97melHRiJMeZAeTvJsuZCY0yK6d8tM7Z9SI+sDozEGHOgEspQRGSiiKwWkbUicnsrxweIyDsislhElonIue7+oIg8LSKfiMhKEbkjqdHbZKTGmBSTmx6MbVfX2yhDY7yszSRLRPzAH4FzgBHAFSIyokWxnwIvqOpRwOXAn9z9lwJpqjoSOAb4LxEZlKTYseZCY0wqevk7JzCgWyZvrNjCq0s3dXQ4xpj9lEhN1jhgraquU9VGYDpwYYsyCjR3JMgDNsXtzxKRAJABNALbDzjq2Ktac6ExJvUcNaArw/vkAHDLc4s7OBpjzP5KJEPpB8RP2lLs7ot3F3CViBQDM4HvuftnADXAZmAD8KCqVhxIwLuw5kJjTIrKSgvEttfbLPDGeFKyqoGuAJ5S1QLgXOAZEfHh1IJFgL7AYODHInJIy5NFZIqIFIlIUWnpvoymseZCY0xq+sHph5Gb7iRaEx6cw6qvktcIYIw5OBJJskqA/nHPC9x98W4AXgBQ1blAOtAduBL4t6o2qepW4EOgsOULqOrjqlqoqoU9evRIPHprLjTGtCGBgTtpIvK8e3x+c79RERkkInUissR9PHow4x6Qn8m9F4+KPZ/+sc0Cb4zXJJKhLACGishgEQnhdGx/tUWZDcDpACIyHCfJKnX3n+buzwKOA1YlJ3Rs7UJjzF4lOHDnBqBSVYcAvwd+E3fsc1Ud4z5uOihBxzl3ZG9e/95JDOmZzVMfrWf1VzsOdgjGmAPQZpKlqmHgZmAWsBJnFOFyEblbRC5wi/0Y+JaILAWeA65VVcW5uWWLyHKcZO0vqroseeFbc6ExZq8SGbhzIfC0uz0DOF2kc3x6ExGO7JfHId2d+bJ++dpytmyv7+CojDGJCrRdBFR1Jk6H9vh9P4/bXgGc2Mp51TjTOLQPay40nVxTUxPFxcXU19s/RoD09HQKCgoIBoNtF06O1gbuHLunMqoaFpFtQL57bLCILMYZFf1TVX2/neNt1b0Xj2RdWQ0ffV7Osb9+i5+cezhTTjm0I0IxxuyDhJKsTsvWLjSdXHFxMTk5OQwaNIhOUjnSYVSV8vJyiouLGTx4cEeHk4jNwABVLReRY4BXROQIVd2tB7qITAGmAAwYMCDpgeRnpzHzlpO5+sn5zP+igl/PXMXDb6/l7985kSE9s5P+esaY5PB4NZA1F5rOrb6+nvz8/P/4BAucpq/8/PyDXauXyMCdWBl3Tr88oFxVG1S1HEBVFwKfA4e19iL7PXhnH4QCPqZPOY45t06gW1aI7fVhzvjdu/xt/pft8nrGmAPn7STLmguNB1iCtVMHfC8SGbjzKjDZ3f468Laqqoj0cDvO4049MxRYd5DibpWIMKh7FkV3nhHbd+fLn/LNJ+bxZbnNpWVMZ+PtDMUmIzXG7EWCA3eeBPJFZC3wI6B5modTgGUisgSnQ/xNSZ1M+QD4fMLVxw2MPf9wbTnXTP24AyMyxrTG232yrLnQmINKVVFVfD7vfD5LYOBOPa0M0FHVl4CX2j3A/fS/k47kf845nCN/MQuAkso6/vXJZgbkZzK8dy4+n90bjelo3rlTtsaaC41p06RJkzjmmGM44ogjePzxxwHIzt7ZWXrGjBlce+21AGzZsoWLLrqI0aNHM3r0aD766CPWr1/PsGHDuOaaazjyyCPZuHEj//3f/82RRx7JyJEjef7552PX+s1vfsPIkSMZPXo0t9++27yfJsmy0wIs/flZ/OjMwwhHlW//bRHnPfQB3312UUeHZozB6zVZ1lxoPOSXry1nxabkLo0yom8uv/jaEXstM3XqVLp160ZdXR1jx47lkksu2WPZW265hfHjx/Pyyy8TiUSorq6msrKSNWvW8PTTT3Pcccfx0ksvsWTJEpYuXUpZWRljx47llFNOYcmSJfzjH/9g/vz5ZGZmUlHRKVrWUl5eZpCbxh/KhopaZiwsBuBfn37FOX94n1H98vjxWYcR8Pu4718rKVpfydu3TujYgI35D+LtJMuaC41p00MPPcTLL78MwMaNG1mzZs0ey7799ttMmzYNAL/fT15eHpWVlQwcOJDjjjsOgA8++IArrrgCv99Pr169GD9+PAsWLODdd9/luuuuIzMzE4Bu3bq18zszzUIBHw9eOprvnz6UF4s28tDba1m5eTsrN2/n+aJdl+N5Y/lXnHVE79jzX89cyePvrWP9fecd7LCNSXneTrKsudB4SFs1Tu1hzpw5vPnmm8ydO5fMzEwmTJhAfX39LqP8EplSISsrqz3DNEnSv1smPzzzMOauKyc/K41/L/9qtzJTnlnI+7edytrSak4d1pPH33MGTFY3hMlO8/a/BGM6G29nKNZcaMxebdu2ja5du5KZmcmqVauYN28eAL169WLlypVEo9FYLRfA6aefziOPPAJAJBJh27Ztu13z5JNP5vnnnycSiVBaWsp7773HuHHjOPPMM/nLX/5CbW0tgDUXdhAR4cWbTuDRq49h7T3ncNvEYbuVOfn+d7juLwt477PS2L4jfzGL9WU2DYQxyeTtJAuw5kJj9mzixImEw2GGDx/O7bffHmvyu++++zj//PM54YQT6NOnT6z8H/7wB9555x1GjhzJMcccw4oVK3a75kUXXcSoUaMYPXo0p512Gvfffz+9e/dm4sSJXHDBBRQWFjJmzBgefPDBg/Y+TesCfh83xS2/8+Mzd51LteW0DxMenENNQxiArTvqKa6sbf8gjUlh4qzj3HkUFhZqUVFRYoWfuxKqvoRvf9i+QRmzn1auXMnw4cM7OoxOpbXviYgsVNXCDgopqfbpHnaQzFhYzLBeOWSl+blm6sd889iBLN+0jdeXbd6t7OG9czh2cDeenuvMJP/fZw/ju6cO4d6ZKzljRC/GDtpzX7vm/yc2Aa/5T7K3+5e3G+CtudAYY9r09WMKYtsf/M9pgJMQXXnsAF5buolNVfW86zYdrvpqB6u+2hEr/8Cs1Xywpoy568p57L11vH/bqfTv5gxuKKmq4//eWsN3Tx3CxY98ROmOBs4+ohePXV1IXWMEv08IBVKgwcSY/eTx334bXWiMMftDRDjh0O7ce/Eonr5+HIf3ztnleN+89Nj23HXlse2fvPxJbPtHzy9h+oKN3PzcYkp3NAAwa/kWVJUj75rF9U8taOd3YUzn5vGaLLWaLGOMSYJ//+AUSqrqKFpfwWG9cji8dw5NEeXSx+aydGMVAJeP7c/0BRv5v7fWMKp/F+Z/4QxuaD7e7KmP1hOJKh+sLQMgHIkCTh8xgK3b68lKC5CVwGjGSFSpbQyTkx5M0js15uDxdpKFTeFgjDHJ0q9LBv3G9Is9DwWEl799Ah+vryArFMDng+kLNvLb2Z/tdq5PIOp28f3lazsHTLyyuIRf/XMF4wZ3496LRvFffy1i3jonOXtyciHDeuewobyWN1Zsob4pwn2XjNrlur99YzV/mvM5z954LCcM6d4O79qY9pNQkiUiE4E/AH7gCVW9r8XxAcDTQBe3zO3uemGIyCjgMSAXiAJj3bXCDpxGseZCY4xpPz6fcNwh+cDOju0t/e+FRzD+sJ7c+conFHTN4LmPNzKsVw6VtY384PklAMz85CtmfrLrvF03PL37AIGff20EbyzfwvGH5rNy83aK1lcCMOez0gNOslTVOuWbg6rNJEtE/MAfgTOBYmCBiLyqqvFju3+Ks7r9IyIyAmcx1kEiEgD+ClytqktFJB9oSlr01lxojDEHjYjw/m2nkhb00SM7jQ/WlnHCod3xu4tRP3PDsdQ1RuiRncZl4waQHQrwu9mriajy13kbdrnW6II8lhbvPg/biJ/PavW1F37pJFvNidKSjVX831tr+OGZh1FR00jP3DQKumby3PwNHN4nh2G9cvjvGcv47TdGk50WYMIDc+jfLYMXbzphj+/v4y8qGN0/j7SAf3+/RXtVUlVHvy4Z7XJts++2bK9n3rpyLoyrvU22RGqyxgFrVXUdgIhMBy4E4pMsxampAsgDNrnbZwHLVHUpgKqWk1TWXGiMMQdT88hCgJOH9tjteEbIz4/O2jkB6i8vPBKA3PQgf5rzOU9cU8jpw3sCMG9dBaGA8NHacg7tmc13/rbnha0/Kd7Gg7NW8/A7azlzRC/eXV1KYyTKW6u2tlr+5KHdeX9NGdM/3sBf523gq+31fLW9nvqmCHWNEbpmhWJlaxrCFFfW8Y3H5nLpMQX85pJR+HzC1U/OJy3g44nJY2NlI1HlwTdWMzg/i0ff/Zx/3HxiQv3F5q0r5/LH5/HwlUdx/qi+bZYH+HBtGaMK8qw/WjuZPPVjVn21gzOG90qof+D+SOSq/YD4xa+KgWNblLkLeENEvgdkAWe4+w8DVERmAT2A6ap6f8sXEJEpwBSAAQMGJB69NRca02lEIhH8/vapATDed9vEw7lt4uG77Dv+UKcZ8piBztxbn/3qHN5Y8RVpAT9zPy/n9WWbGNkvjwvG9OX705fw8DtrAZi9Ykubr/f+GqfT/YNv7Np/7PCf/Tu2/edrCtlYUcvdr++sM3hxYTEvLizm8N45saksHpy1mvNH9+Hw3rks2lDJI3M+j5Vfvml7rDm12ba6JvIydk2MlruLw89bVx5Lsh6Z8zlDe2Zzxoheu8VfVt3AN5+Yz2mH92TqtWN3Ow5QWdO4S7Jo9k1xZR3gLCnVkUlWIq4AnlLV34rI8cAzInKke/2TgLFALfCWO2nXW/Enq+rjwOPgTOSX8Ktac6ExbZo0aRIbN26kvr6e73//+0yZMoXs7Gyqq6sBmDFjBq+//jpPPfUUW7Zs4aabbmLdOmc9u0ceeYS+ffsyceJEjjnmGBYtWsQRRxzBtGnTyMzMZNCgQVx22WXMnj2b2267jcsvv7wj36rxuFDAF0tAzhzRi59/bQQA0ajy1bZ6MkN+xvTvykuLigG49oRBLC2uIhxRXlu2iZ+dP4Lc9CBj73kzodf71rQ9TxobP1fYw++s5eF31nLjSYMpHNR1l3I/e+VTvnnsAJYVb+OHZx7GK4tL+O3sz/jzNYWc6SZP2+qaeMetcXMHWhKJKr/59yoA1v36XBZuqKRwYNdYn7ESNwGYt27XBqDaxjDrSmvYXtfElU/M55kbxrVao9hctnRHAwPz92/t0TVbdvD2qq381/hD2y7sQW4rN9vrmuiVm773wvspkSSrBOgf97zA3RfvBmAigKrOFZF0oDtOrdd7qloGICIzgaOBt0gKay40HvKv2+GrT9outy96j4Rz7ttrkalTp9KtWzfq6uoYO3Ysl1xyyR7L3nLLLYwfP56XX36ZSCRCdXU1lZWVrF69mieffJITTzyR66+/nj/96U/ceuutAOTn57No0Z6beYw5UD6f7PKPfmRBXmx7UHcngbgkbsLVV28+kQ0VtYT8PqY8s5BJY/oytFcOpw7rycD8TLbuaODmZxfFapcKB3al6MtKhvbMZs3W6th1Hvj6KP57xrLY8yc++IJFGyp3iW3N1mruckdT/n3xzn+N35pWRE5agAuP6ktVbVNsOovnPt7Acx/v2j/tuqcW8O5npbzwX8dTUdNAj5w0vvfsYgBqGyOxcqrKhQ9/yJqt1Zw7sjcA89dVxJKsT4q30TM3jZcXl1BV20RTJMqTH3zB3248liP75jHvi3KOPzSf3ASbHy99bC5VtU1cPnYAeZnt12T5ack2umWF6Luf/dXeWrmFgN/H+MNaTzb3xOdmWdvrk9dVvKVEkqwFwFARGYyTXF0OXNmizAbgdOApERkOpAOlwCzgNhHJBBqB8cDvkxS7U5NlzYXG7NVDDz0UWwR648aNrFmzZo9l3377baZNmwaA3+8nLy+PyspK+vfvz4knngjAVVddxUMPPRRLsi677LJ2fgfG7JtRBV0YVdAFgDX3nEPAJ7uMKhycFuCft5wce15R08jVT87n2hMGxZKqop+eQffsNDZvq+etVVtjc4Et2lAVO+/HZx6223QWw/vkMrogj+kLNrKjIbxbh//WNM+2/43H5rZ6fPAd/+TRq47h7tdWUFLl1HA1j9Ssbggz/eMN/H1xCR9/UUFeRpBtdbsmDVc/OT82vcakMX3pmZvO4b1zuPjoAvamqta5zsbKWu585XNGFeQxsl8XPtuyg/XlNUwa04/R/bsAsOqr7QzunkVawE9TJMrvZ39Gj5w0NlbU8b3ThuyxWVNVOf//PiAvI8jSX5zV5veqNc2jVONXI2jNtrom/v3pZr5R2B8RwSfNSVZ4v143EW0mWaoaFpGbcRImPzBVVZeLyN1Akaq+CvwY+LOI/BCnE/y16oz1rRSR3+EkagrMVNV/Ji16ay40XtJGjVN7mDNnDm+++SZz584lMzOTCRMmUF9fv8s/nPr6tmdUaTnsPf55Vtb+NUUYczAE/W23dnTLCsWSrp656WSF/HTPTgPgltOHcvOpQ3jsvXWx5j2AowZ04XunD+Wckb1ZsXkHM5dt5t/Lv+LZG4+la1aIO84Zzui73wAgLeDjrguOYOv2Bn7/ppOU/WrSkYzp34Xz/++DPcbVPTtEWXUjqvBfzyxstcwrS0piyRCwW4IFO+cvA3h92WbC7o5XlmziN5eM5IrH53HL6UM55bAezPxkM6cO67nLLP/NMbZc6/IvH67n4SuP4oM1ZUxf4HTdvm3iMMYUdOFPcf3Wpn74BS99+3iG9c5l+scb+NU/V3Jkv1yOG5zP5eMG7BJ3OBLlwj9+yBF9c7n46IJYf7d/LClhWfE2bps4LDb6syEcYVvce39lcQnHH5rP0QO6xmqp4t316nJeXlzCYb1yOGpA112aC5tjKN3RQFaanz55yRkFmlCfLHfOq5kt9v08bnsFcOIezv0rzjQO7cCaC43Zm23bttG1a1cyMzNZtWoV8+bNA6BXr16sXLmSYcOG8fLLL5OT4yypcvrpp/PII4/wgx/8INZcCLBhwwbmzp3L8ccfz7PPPstJJ53UYe/JmPbUWpOTzyd8e8KhfPO4ARx992zuPG841504GIAhPXMY0jOHc4/szaaq+liNTV5mkPdvO5X6pghDezl/X+vLaliysZLJJwxiwjBnhOWjVx3N3M/Lmf9FBWkBH3+5bhwbKmr57RurefSqYzjiF86UFlkhPzWNESYe0ZustAAvLSrmWycP5s/vfwE4Sd9it5bN7xOK7jyDt1ZtpbKmkTdWfMUCd76x5gQrOy3Ae5+Vcvy9bwPwoxeWxr3j5Ql/v252mzWb3f/v1a2W++YT86lvisaef1qynU9LtrM4brWAUx+cwxdlNU4Em7bzQlExT103lg/XlsXeZ9DvY0NFDX3zMlj11Y5YMyzg1CrOhhtPGswPzzws1pm9rLqBq56YH+tnt2ZLNWP6d6GsuhFwarLqmyLc8NQCitypQqZdP45T9rH5sTWyp8nlOso+rWA/dSL4AnDt6+0blDH7aeXKlQwfPrzDXr+hoYFJkyaxfv16hg0bRlVVFXfddRdlZWX8z//8Dz169KCwsJDq6upYx/cpU6awbt06/H4/jzzyCH369GHixIkUFhaycOFCRowYwTPPPBPr+F5UVET37olPEtna92Rvq9h7zT7dw4znRKPaai3JgWq59FCz8uoGRISmSJTTHpzDX64bx/A+OWzd0cDg/Czufn0F2+ub+OUFR5CdFuC5jzcydlDXWGLX7P5/r2LJxio++ryc2885nKuPGxhL4MYO6hpLwlp6+MqjuPXFpTz7reNQVS55ZC73XHQkeRlBPinexvtryggGfLstrfS10X15bekm+uSls3nb3mvLmxPIZiP75fFJye5zqO3N6Yf33GU6j/ysEK/fchIvFhXzu1ZWKIg3sl8eJVV1VNQ0xvZdddwAfjVpZEKvvbf7l7eTrCfPhkAIJr/WvkEZs586OslKhvXr13P++efz6aefJuV6BzvJSmDFijRgGnAMUA5cpqrr3WN34AzsiQC3qGrrM2XGsSTLdFaqyrqyGg7tkQ3AnNVbaYooZ47oRXFlLTlpQfx+4W/zvqRwUDf6d8ugZ86uo+4iUY1NPtvy2o2RKBc+/CETj+zND844jB31TQT9Pn72yqe8uLCYi47qx1XHDWBTVT3Deudwx98/Ycv2ep6YXMjHX1QwZ3Up/+/yMeSmB9lR30RdY4Rrpn7M0QO7csHovlz+uFMT//yU4/jr/A28tnRT7PX/duOxfPOJ+Xt873eeO5yzjujFjU8X7TK4oaWzRvTijRVb+OEZh/H9M4Ym9H1N4STrLAhmwDX/aN+gjNlPlmTt7mAmWe6KFZ8Rt2IFcEX8ihUi8h1glKreJCKXAxep6mXu6hXP4UzI3Bd4EzhMVSMtXyeeJVnG7Gp9WQ3fn76Y/7viaAbk77ljeltWbNpOfnaIXrnphCNRSqrqYtNTqCqPvruO80f1oX+3TB5993OmfbSeIb1y+N5pQxg7yJmLrXRHAw/MWsULRcVcfFQ/fnTWYdz92gq+PeFQjhrQdW8vv0d7u395e4HoEZPAbzPhGtOeBg0alLQEqwMksmLFhTgTKgPMAB4Wp2f/hTgTKDcAX4jIWvd6rQ8BM8a0alD3LP5x84H34xzRNze2HfD7dpn/S8TpN9fspvGHclMr83v1yEnjV5NGMqRnNt88diBZaQEev6b9eip4O8k6/jsdHYExpnNLZMWKWBl3NPU2IN/dP6/Fue23yJkx5qAIBXxMOeXgTLBqQ/OMaWedrUm+I6Xq90JEpohIkYgUlZaWdnQ4xphOwpIsY9pReno65eXlKZtc7AtVpby8nPT09lm+Yg8SWbEiVkZEAjiL3JcneC7gLA2mqoWqWtijx4EP+zbGpAZvNxca08kVFBRQXFyM1W440tPTKSjY+yzTSZbIihWvApNx+lp9HXhbVVVEXgWedSdU7gsMBT4+aJEbYzzPkixj2lEwGGTw4MEdHcZ/rARXrHgSZ1H7tUAFTiKGW+4FnE7yYeC7bY0sNMaYeJZkGWNSWgIrVtQDl+7h3HuAe9o1QGNMyrI+WcYYY4wx7cCSLGOMMcaYdtDpZnwXkVLgy304pTtQ1mapzsFLsYK34vVSrOCteA9GrANVNSWG5e3jPcxLvwfgrXi9FCt4K16LdVd7vH91uiRrX4lIkVcWlvVSrOCteL0UK3grXi/F6jVe+956KV4vxQreitdiTZw1FxpjjDHGtANLsowxxhhj2kEqJFmPd3QA+8BLsYK34vVSrOCteL0Uq9d47XvrpXi9FCt4K16LNUGe75NljDHGGNMZpUJNljHGGGNMp+PZJEtEJorIahFZKyK3d3Q8ACIyVUS2isincfu6ichsEVnjfu3q7hcReciNf5mIHH2QY+0vIu+IyAoRWS4i3+/k8aaLyMcistSN95fu/sEiMt+N63kRCbn709zna93jgw5mvG4MfhFZLCKvd+ZYRWS9iHwiIktEpMjd1yl/D1JJZ7uHeen+5cbgmXuY3b/aPdZOew/zZJIlIn7gj8A5wAjgChEZ0bFRAfAUMLHFvtuBt1R1KPCW+xyc2Ie6jynAIwcpxmZh4MeqOgI4Dviu+z3srPE2AKep6mhgDDBRRI4DfgP8XlWHAJXADW75G4BKd//v3XIH2/eBlXHPO3Osp6rqmLihzp319yAldNJ72FN45/4F3rqH2f2r/XXOe5iqeu4BHA/Mint+B3BHR8flxjII+DTu+Wqgj7vdB1jtbj8GXNFauQ6K+x/AmV6IF8gEFgHH4kwyF2j5e4GzIPDx7nbALScHMcYCnD/s04DXAenEsa4HurfY1+l/D7z86Kz3MK/ev9wYPHEPs/tXu8Tbae9hnqzJAvoBG+OeF7v7OqNeqrrZ3f4K6OVud5r34FbvHgXMpxPH61ZfLwG2ArOBz4EqVQ23ElMsXvf4NiD/IIb7/4DbgKj7PJ/OG6sCb4jIQhGZ4u7rtL8HKcIr30dP/B544R5m96921WnvYYH2urDZnaqqiHSq4Zwikg28BPxAVbeLSOxYZ4tXVSPAGBHpArwMHN6xEbVORM4HtqrqQhGZ0MHhJOIkVS0RkZ7AbBFZFX+ws/0emI7RWX8PvHIPs/tXu+q09zCv1mSVAP3jnhe4+zqjLSLSB8D9utXd3+HvQUSCODenv6nq393dnTbeZqpaBbyDU2XdRUSaPyzExxSL1z2eB5QfpBBPBC4QkfXAdJwq9z900lhR1RL361acm/84PPB74HFe+T526t8DL97D7P6VfJ35HubVJGsBMNQd7RACLgde7eCY9uRVYLK7PRmn30Dz/mvckQ7HAdviqjbbnTgf954EVqrq7zwQbw/3EyAikoHT92Ilzs3q63uIt/l9fB14W90G+PamqneoaoGqDsL53XxbVb/ZGWMVkSwRyWneBs4CPqWT/h6kEK/cwzrt74GX7mF2/2o/nf4edjA7pyXzAZwLfIbTrn1nR8fjxvQcsBlowmnnvQGnbfotYA3wJtDNLSs4o4s+Bz4BCg9yrCfhtGMvA5a4j3M7cbyjgMVuvJ8CP3f3HwJ8DKwFXgTS3P3p7vO17vFDOuh3YgLwemeN1Y1pqftY3vy31Fl/D1Lp0dnuYV66f7kxeOYeZvevdo2xU9/DbMZ3Y4wxxph24NXmQmOMMcaYTs2SLGOMMcaYdmBJljHGGGNMO7AkyxhjjDGmHViSZYwxxhjTDizJMsYYY4xpB5ZkGWOMMca0A0uyjDHGGGPawf8HLjUVjD/AxOIAAAAASUVORK5CYII=\n", 1025 | "text/plain": [ 1026 | "
" 1027 | ] 1028 | }, 1029 | "metadata": { 1030 | "needs_background": "light" 1031 | }, 1032 | "output_type": "display_data" 1033 | }, 1034 | { 1035 | "name": "stdout", 1036 | "output_type": "stream", 1037 | "text": [ 1038 | "471\n", 1039 | "471\n" 1040 | ] 1041 | } 1042 | ], 1043 | "source": [ 1044 | "from matplotlib import pyplot as plt\n", 1045 | "plt.figure(figsize=((10,8)))\n", 1046 | "plt.subplot(221)\n", 1047 | "plt.plot(aucroc,label=\"aucroc\")\n", 1048 | "plt.plot(aucpr,label=\"aucpr\")\n", 1049 | "plt.legend()\n", 1050 | "plt.subplot(222)\n", 1051 | "plt.plot(loss_,label=\"loss\")\n", 1052 | "plt.legend()\n", 1053 | "plt.show()\n", 1054 | "print(aucroc.index(max(aucroc)))\n", 1055 | "print(aucpr.index(max(aucpr)))" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "code", 1060 | "execution_count": 12, 1061 | "metadata": {}, 1062 | "outputs": [ 1063 | { 1064 | "data": { 1065 | "text/plain": [ 1066 | "ResNet(\n", 1067 | " (first_layer): Linear(in_features=8, out_features=128, bias=True)\n", 1068 | " (blocks): Sequential(\n", 1069 | " (0): Block(\n", 1070 | " (normalization): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 1071 | " (linear_first): Linear(in_features=128, out_features=256, bias=True)\n", 1072 | " (activation): ReLU()\n", 1073 | " (dropout_first): Dropout(p=0.25, inplace=False)\n", 1074 | " (linear_second): Linear(in_features=256, out_features=128, bias=True)\n", 1075 | " (dropout_second): Dropout(p=0, inplace=False)\n", 1076 | " )\n", 1077 | " (1): Block(\n", 1078 | " (normalization): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 1079 | " (linear_first): Linear(in_features=128, out_features=256, bias=True)\n", 1080 | " (activation): ReLU()\n", 1081 | " (dropout_first): Dropout(p=0.25, inplace=False)\n", 1082 | " (linear_second): Linear(in_features=256, out_features=128, bias=True)\n", 1083 | " (dropout_second): Dropout(p=0, inplace=False)\n", 1084 | " )\n", 1085 | " )\n", 1086 | " (head): Head(\n", 1087 | " (normalization): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 1088 | " (activation): ReLU()\n", 1089 | " (linear): Linear(in_features=128, out_features=1, bias=True)\n", 1090 | " )\n", 1091 | ")" 1092 | ] 1093 | }, 1094 | "execution_count": 12, 1095 | "metadata": {}, 1096 | "output_type": "execute_result" 1097 | } 1098 | ], 1099 | "source": [ 1100 | "clf.model" 1101 | ] 1102 | }, 1103 | { 1104 | "cell_type": "code", 1105 | "execution_count": 13, 1106 | "metadata": {}, 1107 | "outputs": [ 1108 | { 1109 | "name": "stdout", 1110 | "output_type": "stream", 1111 | "text": [ 1112 | "[INFO] Register count_linear() for .\n", 1113 | "[INFO] Register count_normalization() for .\n", 1114 | "[INFO] Register zero_ops() for .\n", 1115 | "[INFO] Register zero_ops() for .\n", 1116 | "[INFO] Register zero_ops() for .\n", 1117 | "34.243M\n", 1118 | "133.889K\n" 1119 | ] 1120 | } 1121 | ], 1122 | "source": [ 1123 | "from thop import clever_format\n", 1124 | "from thop import profile\n", 1125 | "input = torch.randn(256, 8)\n", 1126 | "flops, params =profile(clf.model, inputs=(input,))\n", 1127 | "flops, params = clever_format([flops, params], \"%.3f\")\n", 1128 | "print(flops)\n", 1129 | "print(params)" 1130 | ] 1131 | } 1132 | ], 1133 | "metadata": { 1134 | "kernelspec": { 1135 | "display_name": "Python 3", 1136 | "language": "python", 1137 | "name": "python3" 1138 | }, 1139 | "language_info": { 1140 | "codemirror_mode": { 1141 | "name": "ipython", 1142 | "version": 3 1143 | }, 1144 | "file_extension": ".py", 1145 | "mimetype": "text/x-python", 1146 | "name": "python", 1147 | "nbconvert_exporter": "python", 1148 | "pygments_lexer": "ipython3", 1149 | "version": "3.7.3" 1150 | }, 1151 | "toc": { 1152 | "base_numbering": 1, 1153 | "nav_menu": {}, 1154 | "number_sections": true, 1155 | "sideBar": true, 1156 | "skip_h1_title": false, 1157 | "title_cell": "Table of Contents", 1158 | "title_sidebar": "Contents", 1159 | "toc_cell": false, 1160 | "toc_position": {}, 1161 | "toc_section_display": true, 1162 | "toc_window_display": false 1163 | }, 1164 | "varInspector": { 1165 | "cols": { 1166 | "lenName": 16, 1167 | "lenType": 16, 1168 | "lenVar": 40 1169 | }, 1170 | "kernels_config": { 1171 | "python": { 1172 | "delete_cmd_postfix": "", 1173 | "delete_cmd_prefix": "del ", 1174 | "library": "var_list.py", 1175 | "varRefreshCmd": "print(var_dic_list())" 1176 | }, 1177 | "r": { 1178 | "delete_cmd_postfix": ") ", 1179 | "delete_cmd_prefix": "rm(", 1180 | "library": "var_list.r", 1181 | "varRefreshCmd": "cat(var_dic_list()) " 1182 | } 1183 | }, 1184 | "types_to_exclude": [ 1185 | "module", 1186 | "function", 1187 | "builtin_function_or_method", 1188 | "instance", 1189 | "_Feature" 1190 | ], 1191 | "window_display": false 1192 | } 1193 | }, 1194 | "nbformat": 4, 1195 | "nbformat_minor": 4 1196 | } 1197 | --------------------------------------------------------------------------------