├── 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 | 
13 |
14 | 
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": "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\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 |
--------------------------------------------------------------------------------