diff --git a/subject1-4/.keep b/subject1-4/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subject1-4/AdaDiff/__pycache__/denoising_diffusion_pytorch_1d.cpython-38.pyc b/subject1-4/AdaDiff/__pycache__/denoising_diffusion_pytorch_1d.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd767cc1f05e0a812322e7c3b3ff2dfcdc91cccb Binary files /dev/null and b/subject1-4/AdaDiff/__pycache__/denoising_diffusion_pytorch_1d.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/__pycache__/diffusion_module2.cpython-38.pyc b/subject1-4/AdaDiff/__pycache__/diffusion_module2.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1062ad31b5404d9a9a1c3911e5d1b2988129e27 Binary files /dev/null and b/subject1-4/AdaDiff/__pycache__/diffusion_module2.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/__pycache__/models2.cpython-38.pyc b/subject1-4/AdaDiff/__pycache__/models2.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b258adaeb142817db9045816fba314a6537b315c Binary files /dev/null and b/subject1-4/AdaDiff/__pycache__/models2.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/__pycache__/train_diffusion_val.cpython-311.pyc b/subject1-4/AdaDiff/__pycache__/train_diffusion_val.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..989f09cd914b8bd7802aa50332df0a18aea74d48 Binary files /dev/null and b/subject1-4/AdaDiff/__pycache__/train_diffusion_val.cpython-311.pyc differ diff --git a/subject1-4/AdaDiff/__pycache__/train_diffusion_val.cpython-38.pyc b/subject1-4/AdaDiff/__pycache__/train_diffusion_val.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5fd163f8f052e9584ed87e1857a78a32e654070 Binary files /dev/null and b/subject1-4/AdaDiff/__pycache__/train_diffusion_val.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/__pycache__/unet2.cpython-38.pyc b/subject1-4/AdaDiff/__pycache__/unet2.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a96c76683a12e959d5fd14e41cd2d3fe5f74710 Binary files /dev/null and b/subject1-4/AdaDiff/__pycache__/unet2.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/client.py b/subject1-4/AdaDiff/client.py new file mode 100644 index 0000000000000000000000000000000000000000..f1412b9377d0335b7f49145310f226307ceb2409 --- /dev/null +++ b/subject1-4/AdaDiff/client.py @@ -0,0 +1,203 @@ +import socket +import threading +import time +from queue import Queue +import train_diffusion_val +import torch +from torch.utils.data import Dataset, DataLoader, TensorDataset +import torch.nn as nn +import numpy as np +import pickle +import random +class EdgeNode: + def __init__(self, server_address, data_set, model): + # self.node_id = node_id + # self.data_buffer = [] + # self.data_stream = [] + self.ratio = 0.6 + self.model = model + self.server_address = server_address + self.data_set = data_set + self.data_index = 0 + self.lock = threading.Lock() + self.is_running = True # 用于控制数据流读取的循环 + self.results = [] + self.time = 0 + + def read_data_stream(self): + while self.is_running: + time.sleep(2) + new_data_point = self.generate_data_point() + # self.data_stream.append(new_data_point) + + with self.lock: + print(f"Processing data: ") + # 创建新线程处理数据 + self.process_data(new_data_point) + self.data_index+=1 + # if len(self.data_buffer) == 3: + # print(f"Node {self.node_id} - Processing data: {self.data_buffer}") + # # 创建新线程处理数据 + # processing_thread = threading.Thread(target=self.process_data, args=(self.data_buffer,)) + # processing_thread.start() + + # # 移动窗口,准备接收下一个数据点 + # # self.data_buffer = self.data_buffer[1:] + + def generate_data_point(self): + # 按顺序从数据集中获取数据点 + if(self.data_index == np.shape(self.data_set)[0] - 1): + self.is_running = False + # data_point = self.data_set[self.data_index] + return self.data_set[self.data_index].unsqueeze(0) + + def process_data(self, data_fragment): + # import time + startTime = time.time() + noised_series = self.noising(data_fragment) + + if(random.randint(1,100) < 10): + print("网络故障,本地计算") + self.results.append(noised_series) + endTime = time.time() + print("耗时", endTime - startTime) + self.time += endTime - startTime + return + + print(f"将加噪后的时序传输给服务器:") + # 异常检测到,将数据片段、异常数据点的ID和边缘节点ID上报给中心服务器 + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket: + client_socket.connect(self.server_address) + + send_dict = {"ratio":self.ratio,"data":noised_series} + + serialized_data = pickle.dumps(send_dict) + chunk_size = 1024 + + client_socket.sendall(str(len(serialized_data)).encode()) + point = client_socket.recv(1024) + print("收到信号", point.decode()) + print(len(serialized_data)) + for i in range(0, len(serialized_data), chunk_size): + chunk = serialized_data[i:i+chunk_size] + client_socket.sendall(chunk) + # client_socket.sendall(data_to_send) + + print("等待接受服务端数据:") + length = int(client_socket.recv(1024).decode()) + + print("收到长度",length) + client_socket.sendall('go'.encode()) + serialized_data = b'' + while True: + chunk = client_socket.recv(1024) # 接收数据块(这里假设每次接收1KB) + + serialized_data += chunk + if len(serialized_data) == length: # 如果接收到的数据为空,表示传输完毕 + break + # print(len(serialized_data)) + deserialized_data = pickle.loads(serialized_data) + # print(deserialized_data) + print(len(serialized_data)) + self.results.append(deserialized_data) + endTime = time.time() + print("耗时", endTime - startTime) + self.time += endTime - startTime + + def noising(self, data): + return self.model(data, 0, int(self.model.denoise_steps*self.ratio))[1].transpose(2,1) + + +if __name__ == "__main__": + server_address = ('localhost', 8892) # 中心服务器地址和端口 + + training_mode = "diffusion" + lr = 1e-3 + window_size = 128 + p1 = 1 + p2 = 1 + dataset_name = "point_global" + batch_size = 32 + noise_steps = 100 + denoise_steps = 50 + diff_lambda = 0.1 + part = None + device = "cuda" + + experiment = f'diffv4_{dataset_name}_{noise_steps}-{denoise_steps}_{diff_lambda}_1e-3_{batch_size}_{window_size}' + + train_loader, test_loader, validation_loader, labels, validation_labels = train_diffusion_val.load_dataset(dataset_name, part) + + model, diffusion_training_net, diffusion_prediction_net, optimizer, scheduler = \ + train_diffusion_val.load_model(training_mode ,lr, window_size, p1, p2, labels.shape[1], batch_size, noise_steps, denoise_steps) + model, diffusion_training_net = train_diffusion_val.load_from_checkpoint(training_mode, experiment, model, diffusion_training_net) + diffusion_training_net = diffusion_training_net.to(device) + diffusion_prediction_net = diffusion_prediction_net.to(device) + + diffusion_prediction_net.load_state_dict(diffusion_training_net.state_dict()) + diffusion_prediction_net.eval() + diffusion_training_net.eval() + + trainD, testD, validationD = next(iter(train_loader)), next(iter(test_loader)), next(iter(validation_loader)) + testD = train_diffusion_val.convert_to_windows(testD, window_size) + print(np.shape(testD)) + data_x = torch.tensor(testD, dtype=torch.float32) + data_x = data_x.to(device) + # dataset = TensorDataset(data_x, data_x) + # dataloader = DataLoader(dataset, batch_size = batch_size) + + # STime = time.time() + # l1s = [] + # feats=labels.shape[1] + # for window, _ in dataloader: + # window = window.to(device) + # _, x_recon = diffusion_prediction_net(window,0,45) + # _, x_recon = diffusion_prediction_net(window,45,50) + # x_recon = x_recon.transpose(2,1) + # l = nn.MSELoss(reduction = 'none') + # loss = l(x_recon, window) + # l1s.append(loss) + # ETime = time.time() + # loss0 = torch.cat(l1s).detach().cpu().numpy() + # loss0 = loss0.reshape(-1,feats) + + # lossFinal = np.mean(np.array(loss0), axis=1) + # labelsFinal = (np.sum(labels, axis=1) >= 1) + 0 + # validation_thresh = 0 + # result, fprs, tprs = train_diffusion_val.evaluate(lossFinal, labelsFinal, validation_thresh=validation_thresh) + # result_roc = result["ROC/AUC"] + # result_f1 = result["f1"] + + # print(result, ETime - STime) + + edge_node = EdgeNode(server_address=server_address, data_set=data_x, model = diffusion_prediction_net) + edge_node.read_data_stream() + # print(torch.stack(edge_node.results)) + denoised_data = torch.stack(edge_node.results).squeeze(1) + print(edge_node.time) + # 边缘节点线程结束后,自动关闭客户端 + print(f"Client closed.") + # print(np.shape(data_x)) + dataset = TensorDataset(data_x, denoised_data) + dataloader = DataLoader(dataset, batch_size = batch_size) + + l1s = [] + feats=labels.shape[1] + for raw, window in dataloader: + window = window.to(device) + l = nn.MSELoss(reduction = 'none') + loss = l(raw, window) + l1s.append(loss) + loss0 = torch.cat(l1s).detach().cpu().numpy() + loss0 = loss0.reshape(-1,feats) + + print(np.shape(loss0)) + lossFinal = np.mean(np.array(loss0), axis=1) + labelsFinal = (np.sum(labels, axis=1) >= 1) + 0 + validation_thresh = 0 + result, fprs, tprs = train_diffusion_val.evaluate(lossFinal, labelsFinal, validation_thresh=validation_thresh) + result_roc = result["ROC/AUC"] + result_f1 = result["f1"] + print("ROC",result_roc) + print("f1", result_f1) + print("time",edge_node.time / np.shape(denoised_data)[0]) \ No newline at end of file diff --git a/subject1-4/AdaDiff/data/preprocess_smap_msl.ipynb b/subject1-4/AdaDiff/data/preprocess_smap_msl.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..768a0d5934d45b8b6d884ac9eb393754aab9f4fe --- /dev/null +++ b/subject1-4/AdaDiff/data/preprocess_smap_msl.ipynb @@ -0,0 +1,1261 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/mts/SMAP_MSL/data\n" + ] + } + ], + "source": [ + "%cd '../../../../mts/SMAP_MSL/data'" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/mts/SMAP_MSL/data'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%pwd" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "train_folder = '../../../../mts/SMAP_MSL/data/train/'\n", + "test_folder = '../../../../mts/SMAP_MSL/data/test/'" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "labeled_anomalies = pd.read_csv('../../../../mts/SMAP_MSL/data/labeled_anomalies.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
chan_idspacecraftanomaly_sequencesclassnum_values
0P-1SMAP[[2149, 2349], [4536, 4844], [3539, 3779]][contextual, contextual, contextual]8505
1S-1SMAP[[5300, 5747]][point]7331
2E-1SMAP[[5000, 5030], [5610, 6086]][contextual, contextual]8516
3E-2SMAP[[5598, 6995]][point]8532
4E-3SMAP[[5094, 8306]][point]8307
\n", + "
" + ], + "text/plain": [ + " chan_id spacecraft anomaly_sequences \\\n", + "0 P-1 SMAP [[2149, 2349], [4536, 4844], [3539, 3779]] \n", + "1 S-1 SMAP [[5300, 5747]] \n", + "2 E-1 SMAP [[5000, 5030], [5610, 6086]] \n", + "3 E-2 SMAP [[5598, 6995]] \n", + "4 E-3 SMAP [[5094, 8306]] \n", + "\n", + " class num_values \n", + "0 [contextual, contextual, contextual] 8505 \n", + "1 [point] 7331 \n", + "2 [contextual, contextual] 8516 \n", + "3 [point] 8532 \n", + "4 [point] 8307 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "labeled_anomalies.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "random_file = np.load(test_folder + 'A-1.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/root/.conda/envs/py3.9test/lib/python3.9/site-packages/scipy/__init__.py:138: UserWarning: A NumPy version >=1.16.5 and <1.23.0 is required for this version of SciPy (detected version 1.23.1)\n", + " warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion} is required for this version of \"\n" + ] + } + ], + "source": [ + "from sklearn.preprocessing import MinMaxScaler\n", + "\n", + "def scale_data(train, test):\n", + " scaler = MinMaxScaler(feature_range=(0, 1), clip=True).fit(train)\n", + "\n", + " train_scaled = scaler.transform(train)\n", + " test_scaled = scaler.transform(test)\n", + "\n", + " return train_scaled, test_scaled" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
chan_idspacecraftanomaly_sequencesclassnum_values
0P-1SMAP[[2149, 2349], [4536, 4844], [3539, 3779]][contextual, contextual, contextual]8505
1S-1SMAP[[5300, 5747]][point]7331
2E-1SMAP[[5000, 5030], [5610, 6086]][contextual, contextual]8516
3E-2SMAP[[5598, 6995]][point]8532
4E-3SMAP[[5094, 8306]][point]8307
\n", + "
" + ], + "text/plain": [ + " chan_id spacecraft anomaly_sequences \\\n", + "0 P-1 SMAP [[2149, 2349], [4536, 4844], [3539, 3779]] \n", + "1 S-1 SMAP [[5300, 5747]] \n", + "2 E-1 SMAP [[5000, 5030], [5610, 6086]] \n", + "3 E-2 SMAP [[5598, 6995]] \n", + "4 E-3 SMAP [[5094, 8306]] \n", + "\n", + " class num_values \n", + "0 [contextual, contextual, contextual] 8505 \n", + "1 [point] 7331 \n", + "2 [contextual, contextual] 8516 \n", + "3 [point] 8532 \n", + "4 [point] 8307 " + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "smap = labeled_anomalies[labeled_anomalies['spacecraft'] == 'SMAP']\n", + "smap.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
chan_idspacecraftanomaly_sequencesclassnum_values
33T-1SMAP[[2399, 3898], [6550, 6585]][point, contextual]8612
\n", + "
" + ], + "text/plain": [ + " chan_id spacecraft anomaly_sequences class \\\n", + "33 T-1 SMAP [[2399, 3898], [6550, 6585]] [point, contextual] \n", + "\n", + " num_values \n", + "33 8612 " + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "smap[smap['chan_id'] == 'T-1']" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
chan_idspacecraftanomaly_sequencesclassnum_values
55M-6MSL[[1850, 2030]][point]2049
56M-1MSL[[1110, 2250]][contextual]2277
57M-2MSL[[1110, 2250]][contextual]2277
58S-2MSL[[900, 910]][point]1827
59P-10MSL[[4590, 4720]][point]6100
\n", + "
" + ], + "text/plain": [ + " chan_id spacecraft anomaly_sequences class num_values\n", + "55 M-6 MSL [[1850, 2030]] [point] 2049\n", + "56 M-1 MSL [[1110, 2250]] [contextual] 2277\n", + "57 M-2 MSL [[1110, 2250]] [contextual] 2277\n", + "58 S-2 MSL [[900, 910]] [point] 1827\n", + "59 P-10 MSL [[4590, 4720]] [point] 6100" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "msl = labeled_anomalies[labeled_anomalies['spacecraft'] == 'MSL']\n", + "msl.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'msl' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/root/Diff-Anomaly/TranAD/data/preprocess_smap_msl.ipynb Cell 12\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m msl[msl[\u001b[39m'\u001b[39m\u001b[39mchan_id\u001b[39m\u001b[39m'\u001b[39m] \u001b[39m==\u001b[39m \u001b[39m'\u001b[39m\u001b[39mT-1\u001b[39m\u001b[39m'\u001b[39m]\n", + "\u001b[0;31mNameError\u001b[0m: name 'msl' is not defined" + ] + } + ], + "source": [ + "msl[msl['chan_id'] == 'T-1']" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "27\n", + "['C-1', 'C-2', 'D-14', 'D-15', 'D-16', 'F-4', 'F-5', 'F-7', 'F-8', 'M-1', 'M-2', 'M-3', 'M-4', 'M-5', 'M-6', 'M-7', 'P-10', 'P-11', 'P-14', 'P-15', 'S-2', 'T-12', 'T-13', 'T-4', 'T-5', 'T-8', 'T-9']\n" + ] + } + ], + "source": [ + "# smap_files = smap['chan_id'].values\n", + "msl_files = msl['chan_id'].values\n", + "# print(len(smap_files))\n", + "print(len(msl_files))\n", + "# print(smap_files)\n", + "print(sorted(msl_files))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original range for P-1: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for P-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for S-1: [-0.4 1.0], [-0.4 1.0]\n", + "Scaled range for S-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-1: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-3: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-4: [-0.9999999999999998 1.0000000000000004], [-0.9999999999999998 1.0000000000000004]\n", + "Scaled range for E-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-5: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-5: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-6: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for E-6: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-7: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-8: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-8: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-9: [-1.0 1.0000000000000018], [-1.0 1.0000000000000018]\n", + "Scaled range for E-9: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-10: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-10: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-11: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for E-11: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-12: [-1.0 1.0000000000000002], [-1.0 1.0000000000000002]\n", + "Scaled range for E-12: [0.0 1.0], [0.0 1.0]\n", + "Original range for E-13: [-1.0 1.0000000000000002], [-1.0 1.0000000000000002]\n", + "Scaled range for E-13: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-1: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for A-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-1: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for P-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-3: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for P-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-3: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-4: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-3: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-4: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for G-1: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for G-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for G-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for G-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-5: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for D-5: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-6: [-0.9999999999999999 1.0], [-0.9999999999999999 1.0]\n", + "Scaled range for D-6: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-7: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-1: [-0.34 1.0], [-0.34 1.0]\n", + "Scaled range for F-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-4: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for P-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for G-3: [-0.9999999999999998 1.0], [-0.9999999999999998 1.0]\n", + "Scaled range for G-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-1: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-8: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-8: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-9: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-9: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-2: [-0.54 1.0], [-0.54 1.0]\n", + "Scaled range for F-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for G-4: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for G-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-3: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for T-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-11: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-11: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-12: [-1.0 0.0], [-1.0 0.0]\n", + "Scaled range for D-12: [0.0 0.0], [0.0 1.0]\n", + "Original range for B-1: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for B-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for G-6: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for G-6: [0.0 1.0], [0.0 1.0]\n", + "Original range for G-7: [-0.9999999999999998 1.0], [-0.9999999999999998 1.0]\n", + "Scaled range for G-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-7: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for P-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for R-1: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for R-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-5: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-5: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-6: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-6: [0.0 1.0], [0.0 0.9999999999999999]\n", + "Original range for A-7: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-13: [-1.0 0.0], [-1.0 0.0]\n", + "Scaled range for D-13: [0.0 0.0], [0.0 1.0]\n", + "Original range for P-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for P-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-8: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-8: [0.0 1.0], [0.0 1.0]\n", + "Original range for A-9: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for A-9: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-3: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for F-3: [0.0 1.0], [0.0 1.0]\n" + ] + } + ], + "source": [ + "for file in smap_files:\n", + " # load\n", + " train_original = np.load(train_folder + f'{file}.npy')\n", + " test_original = np.load(test_folder + f'{file}.npy')\n", + " print(f'Original range for {file}: [{train_original.min()} {train_original.max()}], [{train_original.min()} {train_original.max()}]')\n", + " # scale\n", + " train_scaled, test_scaled = scale_data(train_original, test_original)\n", + " print(f'Scaled range for {file}: [{train_scaled.min()} {train_scaled.max()}], [{test_scaled.min()} {test_scaled.max()}]')\n", + " # build labels\n", + " labels = np.zeros_like(test_scaled)\n", + " indices = smap[smap['chan_id'] == file]['anomaly_sequences'].values[0]\n", + " indices = indices.replace(']', '').replace('[', '').split(', ')\n", + " indices = [int(i) for i in indices]\n", + " for i in range(0, len(indices), 2):\n", + " labels[indices[i]:indices[i+1], :] = 1\n", + " # save\n", + " dir = '../../TranAD/processed/SMAP'\n", + " os.makedirs(dir, exist_ok=True)\n", + " np.save(f'{dir}/{file}_train.npy', train_scaled)\n", + " np.save(f'{dir}/{file}_test.npy', test_scaled)\n", + " np.save(f'{dir}/{file}_labels.npy', labels) \n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original range for M-6: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for M-6: [0.0 1.0], [0.0 1.0]\n", + "Original range for M-1: [-0.9160935512741932 2.4922982712327473], [-0.9160935512741932 2.4922982712327473]\n", + "Scaled range for M-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for M-2: [-1.210726170949998 1.0], [-1.210726170949998 1.0]\n", + "Scaled range for M-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for S-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for S-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-10: [0.0 1.001129464915996], [0.0 1.001129464915996]\n", + "Scaled range for P-10: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-4: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-5: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-5: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-7: [-0.9999999999999998 1.0], [-0.9999999999999998 1.0]\n", + "Scaled range for F-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for M-3: [-1.47721668720188 1.000070758018348], [-1.47721668720188 1.000070758018348]\n", + "Scaled range for M-3: [0.0 1.0], [0.0 1.0]\n", + "Original range for M-4: [-1.4654640190905774 1.00000547321409], [-1.4654640190905774 1.00000547321409]\n", + "Scaled range for M-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for M-5: [-1.2550059949886205 1.0], [-1.2550059949886205 1.0]\n", + "Scaled range for M-5: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-15: [0.0 1.0052196607220525], [0.0 1.0052196607220525]\n", + "Scaled range for P-15: [0.0 1.0], [0.0 1.0]\n", + "Original range for C-1: [-1.0 2.1934477379095165], [-1.0 2.1934477379095165]\n", + "Scaled range for C-1: [0.0 1.0], [0.0 1.0]\n", + "Original range for C-2: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for C-2: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-12: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-12: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-13: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-13: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-4: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for F-4: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-5: [-1.1163775338154294 4.162651279553374], [-1.1163775338154294 4.162651279553374]\n", + "Scaled range for F-5: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-14: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for D-14: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-9: [-1.0 1.0], [-1.0 1.0]\n", + "Scaled range for T-9: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-14: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for P-14: [0.0 1.0], [0.0 1.0]\n", + "Original range for T-8: [-1.0 1.0294117647058822], [-1.0 1.0294117647058822]\n", + "Scaled range for T-8: [0.0 1.0], [0.0 1.0]\n", + "Original range for P-11: [0.0 1.0], [0.0 1.0]\n", + "Scaled range for P-11: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-15: [-1.0 1.1915779731605738], [-1.0 1.1915779731605738]\n", + "Scaled range for D-15: [0.0 1.0], [0.0 1.0]\n", + "Original range for D-16: [-1.0 1.008879901529805], [-1.0 1.008879901529805]\n", + "Scaled range for D-16: [0.0 1.0], [0.0 1.0]\n", + "Original range for M-7: [-1.0020241085789672 1.0], [-1.0020241085789672 1.0]\n", + "Scaled range for M-7: [0.0 1.0], [0.0 1.0]\n", + "Original range for F-8: [-1.0 1.1304347826086958], [-1.0 1.1304347826086958]\n", + "Scaled range for F-8: [0.0 1.0], [0.0 1.0]\n" + ] + } + ], + "source": [ + "for file in msl_files:\n", + " # load\n", + " train_original = np.load(train_folder + f'{file}.npy')\n", + " test_original = np.load(test_folder + f'{file}.npy')\n", + " print(f'Original range for {file}: [{train_original.min()} {train_original.max()}], [{train_original.min()} {train_original.max()}]')\n", + " # scale\n", + " train_scaled, test_scaled = scale_data(train_original, test_original)\n", + " print(f'Scaled range for {file}: [{train_scaled.min()} {train_scaled.max()}], [{test_scaled.min()} {test_scaled.max()}]')\n", + " # build labels\n", + " labels = np.zeros_like(test_scaled)\n", + " indices = msl[msl['chan_id'] == file]['anomaly_sequences'].values[0]\n", + " indices = indices.replace(']', '').replace('[', '').split(', ')\n", + " indices = [int(i) for i in indices]\n", + " for i in range(0, len(indices), 2):\n", + " labels[indices[i]:indices[i+1], :] = 1\n", + " # save\n", + " dir = '../../TranAD/processed/MSL'\n", + " os.makedirs(dir, exist_ok=True)\n", + " np.save(f'{dir}/{file}_train.npy', train_scaled)\n", + " np.save(f'{dir}/{file}_test.npy', test_scaled)\n", + " np.save(f'{dir}/{file}_labels.npy', labels) \n" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "t_1_test = np.load(test_folder + 'T-1.npy')\n", + "t_1_train = np.load(train_folder + 'T-1.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2875, 25)" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t_1_train.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "min_original_test = t_1_test.min()\n", + "max_original_test = t_1_test.max()\n", + "min_original_train = t_1_train.min()\n", + "max_original_train = t_1_train.max()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-1.0 1.0\n", + "-1.0 1.0\n" + ] + } + ], + "source": [ + "print(f'{min_original_train} {max_original_train}')\n", + "print(f'{min_original_test} {max_original_test}')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.023095539152304296\n", + "0.020554483585080625\n" + ] + } + ], + "source": [ + "print(t_1_train.mean())\n", + "print(t_1_test.mean())" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "t_1_train_scaled, t_1_test_scaled = scale_data(t_1_train, t_1_test)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "min_train_scaled = t_1_train_scaled.min()\n", + "max_train_scaled = t_1_train.max()\n", + "min_test_scaled = t_1_test_scaled.min()\n", + "max_test_scaled = t_1_test.max()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0 1.0\n", + "0.0 1.0\n" + ] + } + ], + "source": [ + "print(f'{min_train_scaled} {max_train_scaled}')\n", + "print(f'{min_test_scaled} {max_test_scaled}')" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0315616826196304\n", + "0.030848537658773482\n" + ] + } + ], + "source": [ + "print(t_1_train_scaled.mean())\n", + "print(t_1_test_scaled.mean())" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([ 191, 112, 88, 76, 64, 69065, 84, 76, 176,\n", + " 1943]),\n", + " array([-1. , -0.8, -0.6, -0.4, -0.2, 0. , 0.2, 0.4, 0.6, 0.8, 1. ]))" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.histogram(t_1_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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EolKpYJSqcTUJTFW61OpVEKlUtW7vSAID23j4uKCtWvXYu3atY9SGhERkU1jIBKJRqOBVqvls8yIiIiaAAYiEWk0GgYUIiKiJoB3mREREZHsMRARERGR7DEQERERkewxEBEREZHsMRARERGR7DEQERERkewxEBEREZHscR4iEel0Ok7MSERE1AQwEIlEp9MhKCgIRqPRan0qlUpotdp6h6IlS5Zgy5YtyM3NhUKhQP/+/bFs2TIEBASY2vztb39DamoqfvzxR9y6dQs3b96Eh4eHSCMgIiKSBgORSAwGA4xGIzZMS0FA20DR+zt7JRexq2NgMBjqHYgOHDiAuLg49O7dG1VVVZg1axYiIiJw5swZtGzZEgBgNBoxZMgQDBkyBImJiWIOgYiISDIMRCILaBuInh17Sl1GndLT082WU1JS4OXlhaysLAwcOBAAMG3aNABARkaGlasjIiKyHl5UTSbFxcUAAE9PT4krISIisi4GIgIA1NTUYNq0aRgwYAC6du0qdTlERERWxVNmBACIi4tDTk4ODh06JHUpREREVsdARIiPj8f27dtx8OBBtG3bVupyiIiIrI6BSMYEQcDUqVORlpaGjIwM+Pv7S10SERGRJBiIZCwuLg6pqanYunUrXF1dodfrAQDu7u5QKBQAAL1eD71ej7y8PADAqVOn4OrqCo1Gw4uviYio2WAgEtnZK7k2209SUhIAICwszGx9cnIyYmJiAADr1q3DggULTNtqb8f/dRsiIqKmjoFIJCqVCkqlErGrY6zWp1KphEqlqnd7QRAe2mb+/PmYP3/+I1RFRERk+xiIRKLRaKDVavksMyIioiaAgUhEGo2GAYWIiKgJ4MSMREREJHsMRERERCR7DEREREQkewxEREREJHsMRERERCR7DEREREQkewxEREREJHuch0hEOp2OEzMSERE1AQxEItHpdAgKCoLRaLRan0qlElqttt6haMmSJdiyZQtyc3OhUCjQv39/LFu2DAEBAQCAGzduYN68efj222+h0+nQpk0bDB8+HIsWLYK7u7uYQyEiIrIqBiKRGAwGGI1GpExPQmDbJ0TvL/fK/xCzagoMBkO9A9GBAwcQFxeH3r17o6qqCrNmzUJERATOnDmDli1b4urVq7h69SpWrFiB4OBgXLp0CZMnT8bVq1fx73//W+QRERERWY9NB6L58+ebPWkdAAICApCbe/fJ7rdv38Ybb7yBzZs3o6KiApGRkfjkk0/g7e1taq/T6TBlyhTs378frVq1QnR0NJYsWYIWLawz9MC2T6BnxxCr9NVQ6enpZsspKSnw8vJCVlYWBg4ciK5du+I///mPaXvHjh2xePFi/PnPf0ZVVZXVfoZERERis/m/aF26dMGePXtMy7/+Izx9+nTs2LEDX331Fdzd3REfH48RI0bg+++/BwBUV1dj6NChUKvVOHz4MAoKCjBu3Dg4Ojrivffes/pYbF1xcTEAwNPT84Ft3NzcGIaIiKhZsfm/ai1atIBarb5nfXFxMdavX4/U1FQ8++yzAIDk5GQEBQXhyJEj6NevH7799lucOXMGe/bsgbe3N3r06IFFixZh5syZmD9/PpycnKw9HJtVU1ODadOmYcCAAejatWudbQwGAxYtWoRJkyZZuToiIiJx2fxt9+fOnYOvry86dOiAMWPGQKfTAQCysrJw584dhIeHm9oGBgZCo9EgMzMTAJCZmYlu3bqZnUKLjIxESUkJTp8+fd8+KyoqUFJSYvZq7uLi4pCTk4PNmzfXub2kpARDhw5FcHAw5s+fb93iiIiIRGbTgahv375ISUlBeno6kpKSkJ+fj9/97ne4desW9Ho9nJyc4OHhYfYeb29v6PV6AIBerzcLQ7Xba7fdz5IlS+Du7m56tWvXzrIDszHx8fHYvn079u/fj7Zt296z/datWxgyZAhcXV2RlpYGR0dHCaokIiISj02fMouKijL9d/fu3dG3b1/4+fnhyy+/hEKhEK3fxMREJCQkmJZLSkqaZSgSBAFTp05FWloaMjIy4O/vf0+bkpISREZGwtnZGdu2bYOLi4sElRIREYnLpo8Q/ZaHhweeeOIJ5OXlQa1Wo7KyEkVFRWZtCgsLTdccqdVqFBYW3rO9dtv9ODs7w83NzezVHMXFxWHTpk1ITU2Fq6sr9Ho99Ho9ysvLAdwNQxERESgrK8P69etRUlJialNdXS1x9URERJZj00eIfqu0tBTnz5/H2LFj0atXLzg6OmLv3r0YOXIkAODs2bPQ6XQIDQ0FAISGhmLx4sW4du0avLy8AAC7d++Gm5sbgoODrVJz7pX/2Ww/SUlJAICwsDCz9cnJyYiJicGPP/6Io0ePAgA6depk1iY/Px/t27dvVK1ERES2xqYD0ZtvvokXX3wRfn5+uHr1KubNmwcHBweMHj0a7u7umDBhAhISEuDp6Qk3NzdMnToVoaGh6NevHwAgIiICwcHBGDt2LN5//33o9XrMnj0bcXFxcHZ2FrV2lUoFpVKJmFVTRO3n15RKJVQqVb3bC4LwwO1hYWEPbUNERNQc2HQgunLlCkaPHo3r16+jTZs2ePrpp3HkyBG0adMGALBq1SrY29tj5MiRZhMz1nJwcMD27dsxZcoUhIaGomXLloiOjsbChQtFr12j0UCr1fJZZkRERE2ATQei+90CXsvFxQVr167F2rVr79vGz88PO3futHRp9aLRaBhQiIhIVGI/SFyr1Yq2b1ti04GIiIiI7s+aDxIvLS0VvQ8pMRARERE1UaYHic+YgyCNnyh9fHPsCOb/4++4ffu2KPu3FQxERERETVyQxg89OwWIsu/cy5dE2a+taVLzEBERERGJgYGIiIiIZI+BiIiIiGSPgYiIiIhkjxdVi0jsuSF+ixMzEhERNQ4DkUisOTdELaVSCa1WW+9QtGTJEmzZsgW5ublQKBTo378/li1bhoCA/3+nwl/+8hfs2bMHV69eRatWrUxtAgMDxRoGERGR1TEQicQ0N8QbqxDUttPD3/CItFfyEPPBdBgMhnoHogMHDiAuLg69e/dGVVUVZs2ahYiICJw5cwYtW7YEAPTq1QtjxoyBRqPBjRs3MH/+fERERCA/Px8ODg5iDomIiMhqGIhEFtS2E3p26ip1GXVKT083W05JSYGXlxeysrIwcOBAAMCkSZNM29u3b493330XISEhuHjxIjp27GjVeomIiMTCi6rJpLi4GADg6elZ5/aysjIkJyfD398f7dq1s2ZpREREomIgIgBATU0Npk2bhgEDBqBrV/MjWp988glatWqFVq1a4ZtvvsHu3bvh5OQkUaVERESWx0BEAIC4uDjk5ORg8+bN92wbM2YMTpw4gQMHDuCJJ57ASy+91OyfaUNERPLCa4gI8fHx2L59Ow4ePIi2bdves93d3R3u7u7o3Lkz+vXrh8ceewxpaWkYPXq0BNUSERFZHgORjAmCgKlTpyItLQ0ZGRnw9/ev13sEQUBFRYUVKiQiIrIOBiIZi4uLQ2pqKrZu3QpXV1fo9XoAd48IKRQKXLhwAV988QUiIiLQpk0bXLlyBUuXLoVCocDzzz8vcfVERESWw0AkMu2VPJvtJykpCQAQFhZmtj45ORkxMTFwcXHBd999h9WrV+PmzZvw9vbGwIEDcfjwYXh5eVmibCIiIpvAQCQSlUoFpVKJmA+mW61PpVIJlUpV7/aCIDxwu6+vL3bu3PmoZREREdk8BiKRaDQaaLVaPsuMiIioCWAgEpFGo2FAISIiagI4DxERERHJHgMRERERyR4DEREREckeAxERERHJHgMRERERyR4DEREREckeAxERERHJHuchEpFOp+PEjERERE0AA5FIdDodgoKCYDQardanUqmEVqutdyhasmQJtmzZgtzcXCgUCvTv3x/Lli1DQEDAPW0FQcDzzz+P9PR0pKWlYfjw4RaunoiISDoMRCIxGAwwGo1IeXMJgtr5i96f9nI+YlYkwmAw1DsQHThwAHFxcejduzeqqqowa9YsRERE4MyZM2jZsqVZ29WrV8POzk6M0omIiCTHQCSyoHb+6NkpWOoy6pSenm62nJKSAi8vL2RlZWHgwIGm9dnZ2fjggw9w/Phx+Pj4WLtMIiIi0fGiajIpLi4GAHh6eprWGY1GvPLKK1i7di3UarVUpREREYmKgYgAADU1NZg2bRoGDBiArl27mtZPnz4d/fv3x7BhwySsjoiISFw8ZUYAgLi4OOTk5ODQoUOmddu2bcO+fftw4sQJCSsjIiISH48QEeLj47F9+3bs378fbdu2Na3ft28fzp8/Dw8PD7Ro0QItWtzNzyNHjkRYWJhE1RIREVkejxDJmCAImDp1KtLS0pCRkQF/f/O74d5++228+uqrZuu6deuGVatW4cUXX7RmqURERKJiIJKxuLg4pKamYuvWrXB1dYVerwcAuLu7Q6FQQK1W13khtUajuSc8ERERNWUMRCLTXs632X6SkpIA4J7TX8nJyYiJibFAVURERE0DA5FIVCoVlEolYlYkWq1PpUIJpVKJsrKye7a1aNECzs7OZusEQWhwH415DxERka1jIBKJRqOBVqt96LPMKisrcf78eYsEDQ8PD5SWlkKr1d6zzd7eHl26dLknFBERkXjEfqZlXf/eU+MwEIlIo9E89DEaZWVlaNGiBdqrfaBwchKljvLKSlzUF6CqqoqBiIjISqz5TMvS0lLR+2juGIhshMLJCQpnF1H7uH37tqj7r70tn4iIfvVMyxlzEKTxE6WPb44dwfx//F30f9/lgH/BZOBOVTUAID9f3Au87e3t0alTJ1H7ICJqaoI0fujZKUCUfedeviTKfuWIgUgGqmruBqJ2Ki+0UipE6ePXp+WIiIiaGgYiC6mpqZG6hIdycXK0ymm5iooKnD59GnZ2dhbfv0qleuh1WURERA3FQPSInJycYG9vj6tXr6JNmzZwcnJqUBCoqKi4+7937sDOTpwnqdz5v6M2lVV3cLuyUpQ+ysrvnr++cOEC8vPzERsbK8rRIqVSCa1Wy1BEREQWxUD0iOzt7eHv74+CggJcvXq1we+vrKyEwWBAixoBTiJdlFx2+zaulxTD/k4VXES6y6zs9m3orxvgcKsMAeUCvl+1zuJ9aHWXELN8Eb777jsEBQVZfP+1KioqRL8bj0e6SCxi3+YNWOf31xrjEPu7zlvimxYGIgtwcnKCRqNBVVUVqqurG/Te06dPY/Lkyfhy9rvo6CfO4zB2HP0eb//9E3wa/wb6hzwpTh9HDmHKyqX4/PW30bPfAFH60N+4DjsAf/7zn0XZfy072EGAuBNQuri44N///jd8fHxE64PBTn6sdZu32L+/BQUF+NMf/4Ty2+Wi7L+WNb7rAG+JbyoYiCzEzs4Ojo6OcHR0bPD7Ll26BNyugEu1OF/MyluluHTpEqpLjeL1UVqGoqIiUfZdq6isFAKADydORb/uIaL0UXsLq5h9HMo5iTc//QgvvPCCKPuvxWDXMJduGnGqoFi0fdf68ccfRekDuHtEQuzbvK31+wsASfFv4MkAcY4GW+O7zlvimxZZBaK1a9di+fLl0Ov1CAkJwZo1a9CnTx+py6IG6uT7uOi3sIrdB4Nd/Vkj2NkBWLg/F9gvbh8CgF69eonXyf9p59m6Wfz+alq3afLfdWo6ZBOIvvjiCyQkJGDdunXo27cvVq9ejcjISJw9exZeXl5Sl0cyxGD3cNYIdm8tew8HrlzAzNB+GPLE46L08cXJPKw78ROGdeqCWa9PF6UPwLpHJBgkqLmRTSBauXIlJk6ciPHjxwMA1q1bhx07dmDDhg14++23Ja6OqOlq6sHO3eXu3FwaNzeEqNuI0sd3F/QAgNaKlqKNA2CQIHoUdoIMHl9eWVkJpVKJf//73xg+fLhpfXR0NIqKirB161az9hUVFabb4QGguLgYGo0Gly9fhpubm0Vry87OxjPPPIPnOwTC06WlRfddK7/oOr6/epF9sA/28YA+JoU8iV4+rUXpY+/FK/gyVyvqOIDm95mwD/n0ceN2GXZeyMWBAwfQo0cPi+23pKQE7dq1Q1FREdzd3R/cWJCBn3/+WQAgHD582Gz9jBkzhD59+tzTft68eQLunu7niy+++OKLL76a+Ovy5csPzQqyOWXWEImJiUhISDAt19TU4MaNG2jdurXFZ1+uTa9iHH2yBc19fEDzHyPH1/Q19zFyfE2fWGMUBAG3bt2Cr6/vQ9vKIhCpVCo4ODigsLDQbH1hYSHUavU97Z2dne+5zdfDw0PMEuHm5tZsf9GB5j8+oPmPkeNr+pr7GDm+pk+MMT70VNn/EedZETbGyckJvXr1wt69e03rampqsHfvXoSGhkpYGREREdkCWRwhAoCEhARER0fjqaeeQp8+fbB69WqUlZWZ7jojIiIi+ZJNIHr55Zfxyy+/YO7cudDr9ejRowfS09Ph7e0taV3Ozs6YN2+eVWbilUJzHx/Q/MfI8TV9zX2MHF/TZwtjlMVt90REREQPIotriIiIiIgehIGIiIiIZI+BiIiIiGSPgYiIiIhkj4FIZIsXL0b//v2hVCrrPbmjIAiYO3cufHx8oFAoEB4ejnPnzpm1uXHjBsaMGQM3Nzd4eHhgwoQJKC0tFWEED9fQWi5evAg7O7s6X1999ZWpXV3bN2/ebI0hmWnMzzosLOye2idPnmzWRqfTYejQoVAqlfDy8sKMGTNQVVUl5lDq1NDx3bhxA1OnTkVAQAAUCgU0Gg1ee+01FBcXm7WT8vNbu3Yt2rdvDxcXF/Tt2xc//PDDA9t/9dVXCAwMhIuLC7p164adO3eaba/Pd9KaGjK+zz77DL/73e/w2GOP4bHHHkN4ePg97WNiYu75rIYMGSL2MB6oIWNMSUm5p34XFxezNk35M6zr3xM7OzsMHTrU1MaWPsODBw/ixRdfhK+vL+zs7PD1118/9D0ZGRl48skn4ezsjE6dOiElJeWeNg39XjeYBR4VRg8wd+5cYeXKlUJCQoLg7u5er/csXbpUcHd3F77++mvhp59+En7/+98L/v7+Qnl5uanNkCFDhJCQEOHIkSPCd999J3Tq1EkYPXq0SKN4sIbWUlVVJRQUFJi9FixYILRq1Uq4deuWqR0AITk52azdr38G1tKYn/UzzzwjTJw40az24uJi0/aqqiqha9euQnh4uHDixAlh586dgkqlEhITE8Uezj0aOr5Tp04JI0aMELZt2ybk5eUJe/fuFTp37iyMHDnSrJ1Un9/mzZsFJycnYcOGDcLp06eFiRMnCh4eHkJhYWGd7b///nvBwcFBeP/994UzZ84Is2fPFhwdHYVTp06Z2tTnO2ktDR3fK6+8Iqxdu1Y4ceKEoNVqhZiYGMHd3V24cuWKqU10dLQwZMgQs8/qxo0b1hrSPRo6xuTkZMHNzc2sfr1eb9amKX+G169fNxtbTk6O4ODgICQnJ5va2NJnuHPnTuGdd94RtmzZIgAQ0tLSHtj+woULglKpFBISEoQzZ84Ia9asERwcHIT09HRTm4b+zBqDgchKkpOT6xWIampqBLVaLSxfvty0rqioSHB2dhb+9a9/CYIgCGfOnBEACMeOHTO1+eabbwQ7Ozvh559/tnjtD2KpWnr06CHExsaaravPF0lsjR3fM888I7z++uv33b5z507B3t7e7B/tpKQkwc3NTaioqLBI7fVhqc/vyy+/FJycnIQ7d+6Y1kn1+fXp00eIi4szLVdXVwu+vr7CkiVL6mz/0ksvCUOHDjVb17dvX+Evf/mLIAj1+05aU0PH91tVVVWCq6ursHHjRtO66OhoYdiwYZYutdEaOsaH/fva3D7DVatWCa6urkJpaalpna19hrXq8+/AW2+9JXTp0sVs3csvvyxERkaalh/1Z1YfPGVmY/Lz86HX6xEeHm5a5+7ujr59+yIzMxMAkJmZCQ8PDzz11FOmNuHh4bC3t8fRo0etWq8lasnKykJ2djYmTJhwz7a4uDioVCr06dMHGzZsgGDlabMeZXyff/45VCoVunbtisTERBiNRrP9duvWzWxi0MjISJSUlOD06dOWH8h9WOp3qbi4GG5ubmjRwnyuV2t/fpWVlcjKyjL7/tjb2yM8PNz0/fmtzMxMs/bA3c+itn19vpPW0pjx/ZbRaMSdO3fg6elptj4jIwNeXl4ICAjAlClTcP36dYvWXl+NHWNpaSn8/PzQrl07DBs2zOx71Nw+w/Xr12PUqFFo2bKl2Xpb+Qwb6mHfQUv8zOpDNjNVNxV6vR4A7plB29vb27RNr9fDy8vLbHuLFi3g6elpamMtlqhl/fr1CAoKQv/+/c3WL1y4EM8++yyUSiW+/fZb/PWvf0VpaSlee+01i9X/MI0d3yuvvAI/Pz/4+vri5MmTmDlzJs6ePYstW7aY9lvXZ1y7zVos8fkZDAYsWrQIkyZNMlsvxednMBhQXV1d5882Nze3zvfc77P49fetdt392lhLY8b3WzNnzoSvr6/ZH5chQ4ZgxIgR8Pf3x/nz5zFr1ixERUUhMzMTDg4OFh3DwzRmjAEBAdiwYQO6d++O4uJirFixAv3798fp06fRtm3bZvUZ/vDDD8jJycH69evN1tvSZ9hQ9/sOlpSUoLy8HDdv3nzk3/v6YCBqhLfffhvLli17YButVovAwEArVWR59R3joyovL0dqairmzJlzz7Zfr+vZsyfKysqwfPlyi/xBFXt8vw4H3bp1g4+PDwYPHozz58+jY8eOjd5vfVnr8yspKcHQoUMRHByM+fPnm20T8/Ojxlm6dCk2b96MjIwMs4uOR40aZfrvbt26oXv37ujYsSMyMjIwePBgKUptkNDQULMHdffv3x9BQUH49NNPsWjRIgkrs7z169ejW7du6NOnj9n6pv4Z2gIGokZ44403EBMT88A2HTp0aNS+1Wo1AKCwsBA+Pj6m9YWFhejRo4epzbVr18zeV1VVhRs3bpje/6jqO8ZHreXf//43jEYjxo0b99C2ffv2xaJFi1BRUfHIz7ux1vhq9e3bFwCQl5eHjh07Qq1W33OHRGFhIQBY5DO0xvhu3bqFIUOGwNXVFWlpaXB0dHxge0t+fvejUqng4OBg+lnWKiwsvO941Gr1A9vX5ztpLY0ZX60VK1Zg6dKl2LNnD7p37/7Ath06dIBKpUJeXp7V/5g+yhhrOTo6omfPnsjLywPQfD7DsrIybN68GQsXLnxoP1J+hg11v++gm5sbFAoFHBwcHvl3ol4sdjUSPVBDL6pesWKFaV1xcXGdF1UfP37c1GbXrl2SXlTd2FqeeeaZe+5Oup93331XeOyxxxpda2NY6md96NAhAYDw008/CYLw/y+q/vUdEp9++qng5uYm3L5923IDeIjGjq+4uFjo16+f8MwzzwhlZWX16stan1+fPn2E+Ph403J1dbXw+OOPP/Ci6hdeeMFsXWho6D0XVT/oO2lNDR2fIAjCsmXLBDc3NyEzM7NefVy+fFmws7MTtm7d+sj1NkZjxvhrVVVVQkBAgDB9+nRBEJrHZygId/+OODs7CwaD4aF9SP0Z1kI9L6ru2rWr2brRo0ffc1H1o/xO1KtWi+2J6nTp0iXhxIkTptvKT5w4IZw4ccLs9vKAgABhy5YtpuWlS5cKHh4ewtatW4WTJ08Kw4YNq/O2+549ewpHjx4VDh06JHTu3FnS2+4fVMuVK1eEgIAA4ejRo2bvO3funGBnZyd888039+xz27ZtwmeffSacOnVKOHfunPDJJ58ISqVSmDt3rujj+a2Gji8vL09YuHChcPz4cSE/P1/YunWr0KFDB2HgwIGm99Tedh8RESFkZ2cL6enpQps2bSS77b4h4ysuLhb69u0rdOvWTcjLyzO7zbeqqkoQBGk/v82bNwvOzs5CSkqKcObMGWHSpEmCh4eH6Y6+sWPHCm+//bap/ffffy+0aNFCWLFihaDVaoV58+bVedv9w76T1tLQ8S1dulRwcnIS/v3vf5t9VrX/Bt26dUt48803hczMTCE/P1/Ys2eP8OSTTwqdO3e2ajh/lDEuWLBA2LVrl3D+/HkhKytLGDVqlODi4iKcPn3a1KYpf4a1nn76aeHll1++Z72tfYa3bt0y/a0DIKxcuVI4ceKEcOnSJUEQBOHtt98Wxo4da2pfe9v9jBkzBK1WK6xdu7bO2+4f9DOzBAYikUVHRwsA7nnt37/f1Ab/N19LrZqaGmHOnDmCt7e34OzsLAwePFg4e/as2X6vX78ujB49WmjVqpXg5uYmjB8/3ixkWdPDasnPz79nzIIgCImJiUK7du2E6urqe/b5zTffCD169BBatWoltGzZUggJCRHWrVtXZ1uxNXR8Op1OGDhwoODp6Sk4OzsLnTp1EmbMmGE2D5EgCMLFixeFqKgoQaFQCCqVSnjjjTfMblu3loaOb//+/XX+TgMQ8vPzBUGQ/vNbs2aNoNFoBCcnJ6FPnz7CkSNHTNueeeYZITo62qz9l19+KTzxxBOCk5OT0KVLF2HHjh1m2+vznbSmhozPz8+vzs9q3rx5giAIgtFoFCIiIoQ2bdoIjo6Ogp+fnzBx4kSL/qFpjIaMcdq0aaa23t7ewvPPPy/8+OOPZvtryp+hIAhCbm6uAED49ttv79mXrX2G9/s3onZM0dHRwjPPPHPPe3r06CE4OTkJHTp0MPubWOtBPzNLsBMEK9/HTERERGRjOA8RERERyR4DEREREckeAxERERHJHgMRERERyR4DEREREckeAxERERHJHgMRERERyR4DEREREckeAxERERHJHgMRERERyR4DEREREckeAxERERHJ3v8DG0x48tfUwSoAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import seaborn as sns\n", + "sns.histplot(t_1_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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FBAQUN91AQAAeE2tAtHChQuVnZ2t4cOH13c9ACCHw2H6iRkBXF5qFYhOnTql3r1713ctACCHw6H2CQkqd7m81meIxaICu51QBPixWgWie+65R0uWLNG0adPqux4Afs7pdKrc5dI9szMVE9fO4/0VFX6lV6eMldPprFEg2rhxo+bOnau8vDwVFRUpJyfHPTXJWXa7XZMnT9aGDRt05swZdejQQf/6178IXoAJ1SoQnThxQi+//LJWr16tLl26qFGjRlW2P/fcc/VSHAD/FRPXTi07dPV1GRd0/Phxde3aVaNGjdLgwYPP2b53717dcMMNGj16tGbOnKmwsDB9+eWXaty4sQ+qBfBzahWIvvjiC3Xr1k2StHPnzirbuMEagD9ITU1VamrqBbc//PDDuuWWW/T000+717Vp08YbpQGohVoFonXr1tV3HQBw2aisrNSKFSv04IMPKiUlRdu3b1dcXJymTJlyzmU1AOZQq5e7AgAu7PDhwyorK9NTTz2lAQMG6MMPP9Rvf/tbDR48WBs2bPB1eQDOo1YjRP369bvopbG1a9dWaz8/d1PiiBEjtHjx4irfSUlJUW5urnv5+++/1/jx4/Xvf/9bDRo00JAhQ/T888/riiuucLf54osvlJ6ers8++0zNmjXT+PHj9eCDD1bzaAGgZs5OVnvbbbdp4sSJkqRu3bpp8+bNWrhwoX75y1/6sjygxrwxFYavp7+oVSA6e//QWadPn1Z+fr527tx5zktfL+bnbkqUpAEDBigrK8u9HBwcXGX7sGHDVFRUpFWrVun06dMaOXKkxowZoyVLlkiSSktLlZycrP79+2vhwoXasWOHRo0apaZNm2rMmDHVrhUAqstqtaphw4bq0KFDlfUJCQnatGmTj6oCasfhcCghIUEuD0+FYbFYZPfh9Be1CkTz5s077/oZM2aorKys2vv5uZsSpR8DUHR09Hm32e125ebm6rPPPtO1114rSXrhhRd0yy236JlnnlFsbKzeeOMNnTp1Sq+99pqCgoLUsWNH5efn67nnniMQAfCIoKAg9ezZU7t3766y/j//+Y9atmzpo6qA2nE6nXK5XMqeNE0JNs/8/bU7DmjE3Fk1nv6iPtX6XWbn84c//EHXXXednnnmmXrb5/r16xUZGakrr7xSN910kx5//HFdddVVkqQtW7aoadOm7jAkSf3791eDBg30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o4+quLhY3bp1U25urqKionxaV3BwsKZPn+7xGWz9HefZOzjP3sF59h7OtXeY4Tz7xWP3AAAAF+MX9xABAABcDIEIAAD4PQIRAADwewQiAADg9whEXrBgwQK1atVKjRs3Vq9evfTpp59etP3bb7+t9u3bq3HjxurcubPef/99L1V6aavJeX7llVd044036sorr9SVV16p/v37/+z/L/hRTf8+n7V06VIFBARUeZ8gLqym5/nIkSNKT09XTEyMgoODdc011/C7o5pqeq7nz5+v+Ph4hYSEqEWLFpo4caJOnDjhpWovPRs3btStt96q2NhYBQQE6J133vnZ76xfv16/+MUvFBwcrLZt2yo7O9vjdfrFu8x8aenSpUZQUJDx2muvGV9++aVx7733Gk2bNjVKSkrO2/7jjz82AgMDjaefftrYtWuX8cgjjxiNGjUyduzY4eXKLy01Pc933XWXsWDBAmP79u2G3W43RowYYYSHhxsHDx70cuWXlpqe57MKCwuNq6++2rjxxhuN2267zTvFXsJqep5PnjxpXHvttcYtt9xibNq0ySgsLDTWr19v5Ofne7nyS09Nz/Ubb7xhBAcHG2+88YZRWFhorFy50oiJiTEmTpzo5covHe+//77x8MMPG8uWLTMkGTk5ORdtv2/fPsNisRgZGRnGrl27jBdeeMEIDAw0cnNzPVongcjDrrvuOiM9Pd29XFFRYcTGxhqzZ88+b/vbb7/dGDhwYJV1vXr1Mv74xz96tM5LXU3P8387c+aMERoaaixevNhTJV4WanOez5w5Y/Tu3dt49dVXjbS0NAJRNdT0PGdmZhqtW7c2Tp065a0SLxs1Pdfp6enGTTfdVGVdRkaG0adPH4/WebmoTiB68MEHjY4dO1ZZd8cddxgpKSkerMwwuGTmQadOnVJeXp769+/vXtegQQP1799fW7ZsOe93tmzZUqW9JKWkpFywPWp3nv+by+XS6dOnFRER4akyL3m1Pc+PPfaYIiMjNXr0aG+UecmrzXl+7733lJiYqPT0dEVFRalTp0568sknVVFR4a2yL0m1Ode9e/dWXl6e+7Lavn379P777+uWW27xSs3+wFf/HfSbmap9wel0qqKi4pzZsKOiolRQUHDe7xQXF5+3fXFxscfqvNTV5jz/t8mTJys2NvacH0L8n9qc502bNmnRokXKz8/3QoWXh9qc53379mnt2rUaNmyY3n//fe3Zs0f333+/Tp8+renTp3uj7EtSbc71XXfdJafTqRtuuEGGYejMmTO67777NHXqVG+U7Bcu9N/B0tJSlZeXKyQkxCP9MkIEv/fUU09p6dKlysnJUePGjX1dzmXj2LFjGj58uF555RVZrVZfl3NZq6ysVGRkpF5++WX16NFDd9xxhx5++GEtXLjQ16VddtavX68nn3xSL730kj7//HMtW7ZMK1as0KxZs3xdGuqIESIPslqtCgwMVElJSZX1JSUlio6OPu93oqOja9QetTvPZz3zzDN66qmntHr1anXp0sWTZV7yanqe9+7dq/379+vWW291r6usrJQkNWzYULt371abNm08W/QlqDZ/n2NiYtSoUSMFBga61yUkJKi4uFinTp1SUFCQR2u+VNXmXE+bNk3Dhw/XPffcI0nq3Lmzjh8/rjFjxujhhx9WgwaMM9TVhf47GBYW5rHRIYkRIo8KCgpSjx49tGbNGve6yspKrVmzRomJief9TmJiYpX2krRq1aoLtkftzrMkPf3005o1a5Zyc3N17bXXeqPUS1pNz3P79u21Y8cO5efnuz+/+c1v1K9fP+Xn56tFixbeLP+SUZu/z3369NGePXvcgVOS/vOf/ygmJoYwdBG1Odcul+uc0HM2iBq8GrRe+Oy/gx69ZRvG0qVLjeDgYCM7O9vYtWuXMWbMGKNp06ZGcXGxYRiGMXz4cOOhhx5yt//444+Nhg0bGs8884xht9uN6dOn89h9NdT0PD/11FNGUFCQ8c9//tMoKipyf44dO+arQ7gk1PQ8/zeeMquemp5nh8NhhIaGGuPGjTN2795tLF++3IiMjDQef/xxXx3CJaOm53r69OlGaGio8Y9//MPYt2+f8eGHHxpt2rQxbr/9dl8dgukdO3bM2L59u7F9+3ZDkvHcc88Z27dvNw4cOGAYhmE89NBDxvDhw93tzz52P2nSJMNutxsLFizgsfvLxQsvvGDYbDYjKCjIuO6664ytW7e6t/3yl7800tLSqrR/6623jGuuucYICgoyOnbsaKxYscLLFV+aanKeW7ZsaUg65zN9+nTvF36Jqenf558iEFVfTc/z5s2bjV69ehnBwcFG69atjSeeeMI4c+aMl6u+NNXkXJ8+fdqYMWOG0aZNG6Nx48ZGixYtjPvvv9/44YcfvF/4JWLdunXn/X179rympaUZv/zlL8/5Trdu3YygoCCjdevWRlZWlsfrDDAMxvgAAIB/4x4iAADg9whEAADA7xGIAACA3yMQAQAAv0cgAgAAfo9ABAAA/B6BCAAA+D0CEQAA8HsEIgAA4PcIRAAAwO8RiAAAgN8jEAEAAL/3/wBcBoliJ/eicwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "sns.histplot(t_1_train_scaled)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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uXzWZ4TFohy7vcqhaMuWLZo7d66ysrKUn5+vzMxMDR482H182rRpWrFihQ4fPqywsDB169ZNM2fOVM+ePU0aAQAA/kEgMonT6dRpl0uj58xWTKtWpvdXcOCAlk56XE6ns8qB6NSpU+rcubNGjRqlIUOGXHD8uuuu00svvaRWrVrp9OnTWrBggfr376/c3Fw1btzY20MAAMBvCEQmi2nVSi3at/d3GZVKTk5WcnLyRY//9re/9dieP3++li5dqi+++EK33Xab2eUBAOAz3FSNKiktLdUrr7yiqKgode7c2d/lAADgVVwhwiW9++67GjZsmFwul2JjY7Vu3TpZrVZ/lwUAgFdxhQiX1K9fP2VnZ+uTTz7RwIEDddddd+no0aP+LgsAAK/yeyD69ttv9bvf/U7XXHONIiIi1KlTJ+3cudN93DAMTZ06VbGxsYqIiFBSUpL279/vcY5jx45p+PDhioyMVKNGjTR69GiVlJR4tPniiy/0s5/9TOHh4WrevLmee+45n4yvpmvQoIHatGmjm266SUuXLlXdunW1dOlSf5cFAIBX+TUQHT9+XDfffLPq1aunDz74QHv37tXzzz+vq6++2t3mueee05/+9CctWbJEn376qRo0aKABAwbozJkz7jbDhw/Xnj17tG7dOr377rvasmWLxowZ4z5eXFys/v37q0WLFsrKytLcuXM1bdo0vfLKKz4db21QUVGhs2fP+rsMAAC8yq/3EM2ZM0fNmzdXenq6e198fLz73w3D0MKFC/Xkk0/qjjvukCT99a9/VdOmTfXOO+9o2LBhstvtWrNmjXbs2KEbb7xRkvTiiy/q9ttv17x58xQXF6c33nhDpaWleu211xQWFqYOHTooOztb8+fP9whOZig4cMDU819JPyUlJcrNzXVv5+XlKTs7W9HR0brmmms0c+ZM/epXv1JsbKycTqcWLVqkb7/9Vr/+9a+9WToAAH7n10C0evVqDRgwQL/+9a+1efNmXXvttXrooYd0//33S/r+D3RBQYGSkpLcr4mKilLPnj21bds2DRs2TNu2bVOjRo3cYUiSkpKSVKdOHX366ae68847tW3bNvXp00dhYWHuNgMGDNCcOXN0/PhxjytSknT27FmPqyDFxcXVHpvValWExaKlkx6v9msvV4TFUq0bnnfu3Kl+/fq5t9PS0iRJKSkpWrJkiXJycrRs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iAglJSVp//79Hm2OHTum4cOHKzIyUo0aNdLo0aNVUlJiwgh+WnVrOXjwoEJCQir9evvtt93tKju+YsUKXwzJw+V8r/v27XtB7Q888IBHG4fDoUGDBslisahJkyaaOHGiysrKzBxKpao7vmPHjunhhx9WQkKCIiIiZLPZNG7cOBUVFXm08+f7t2jRIrVs2VLh4eHq2bOnPvvss0u2f/vtt9WuXTuFh4erU6dOev/99z2OV+V30peqM75XX31VP/vZz3T11Vfr6quvVlJS0gXtR4wYccF7NXDgQLOHcUnVGWNGRsYF9YeHh3u0qcnvYWX/PQkJCdGgQYPcbQLpPdyyZYt++ctfKi4uTiEhIXrnnXd+8jWbNm3SDTfcoPr166tNmzbKyMi4oE11f6+rzQtLheESpk6dasyfP99IS0szoqKiqvSa2bNnG1FRUcY777xj/Pvf/zZ+9atfGfHx8cbp06fdbQYOHGh07tzZ2L59u/Gvf/3LaNOmjXH33XebNIpLq24tZWVlRn5+vsfX9OnTjauuuso4efKku50kIz093aPdD78HvnI53+tbb73VuP/++z1qLyoqch8vKyszOnbsaCQlJRm7du0y3n//fcNqtRqTJ082ezgXqO74vvzyS2PIkCHG6tWrjdzcXGP9+vVG27ZtjaFDh3q089f7t2LFCiMsLMx47bXXjD179hj333+/0ahRI6OwsLDS9h9//LERGhpqPPfcc8bevXuNJ5980qhXr57x5ZdfuttU5XfSV6o7vt/+9rfGokWLjF27dhl2u90YMWKEERUVZXzzzTfuNikpKcbAgQM93qtjx475akgXqO4Y09PTjcjISI/6CwoKPNrU5Pfwu+++8xjb7t27jdDQUCM9Pd3dJpDew/fff9/44x//aKxcudKQZGRmZl6y/YEDBwyLxWKkpaUZe/fuNV588UUjNDTUWLNmjbtNdb9nl4NA5CPp6elVCkQVFRVGTEyMMXfuXPe+EydOGPXr1zf+9re/GYZhGHv37jUkGTt27HC3+eCDD4yQkBDj22+/9Xrtl+KtWrp06WKMGjXKY19VfpHMdrnju/XWW41HHnnkosfff/99o06dOh7/0V68eLERGRlpnD171iu1V4W33r+33nrLCAsLM86dO+fe56/3r0ePHkZqaqp7u7y83IiLizNmzZpVafu77rrLGDRokMe+nj17Gr///e8Nw6ja76QvVXd8P1ZWVmY0bNjQWLZsmXtfSkqKcccdd3i71MtW3TH+1H9fa9t7uGDBAqNhw4ZGSUmJe1+gvYfnVeW/A4899pjRoUMHj32/+c1vjAEDBri3r/R7VhV8ZBZg8vLyVFBQoKSkJPe+qKgo9ezZU9u2bZMkbdu2TY0aNdKNN97obpOUlKQ6dero008/9Wm93qglKytL2dnZGj169AXHUlNTZbVa1aNHD7322msyfDxt1pWM74033pDValXHjh01efJkuVwuj/N26tTJY2LQAQMGqLi4WHv27PH+QC7CWz9LRUVFioyMVN26nnO9+vr9Ky0tVVZWlsfvT506dZSUlOT+/fmxbdu2ebSXvn8vzrevyu+kr1zO+H7M5XLp3Llzio6O9ti/adMmNWnSRAkJCXrwwQf13XffebX2qrrcMZaUlKhFixZq3ry57rjjDo/fo9r2Hi5dulTDhg1TgwYNPPYHyntYXT/1O+iN71lVBM1M1TVFQUGBJF0wg3bTpk3dxwoKCtSkSROP43Xr1lV0dLS7ja94o5alS5cqMTFRvXv39tj/9NNP6+c//7ksFos+/PBDPfTQQyopKdG4ceO8Vv9Pudzx/fa3v1WLFi0UFxenL774QpMmTdK+ffu0cuVK93kre4/PH/MVb7x/TqdTM2bM0JgxYzz2++P9czqdKi8vr/R7m5OTU+lrLvZe/PD37fy+i7XxlcsZ349NmjRJcXFxHn9cBg4cqCFDhig+Pl5ff/21nnjiCSUnJ2vbtm0KDQ316hh+yuWMMSEhQa+99pquv/56FRUVad68eerdu7f27NmjZs2a1ar38LPPPtPu3bu1dOlSj/2B9B5W18V+B4uLi3X69GkdP378in/uq4JAdBkef/xxzZkz55Jt7Ha72rVr56OKvK+qY7xSp0+f1vLlyzVlypQLjv1wX9euXXXq1CnNnTvXK39QzR7fD8NBp06dFBsbq9tuu01ff/21WrdufdnnrSpfvX/FxcUaNGiQ2rdvr2nTpnkcM/P9w+WZPXu2VqxYoU2bNnncdDxs2DD3v3fq1EnXX3+9WrdurU2bNum2227zR6nV0qtXL4+Funv37q3ExES9/PLLmjFjhh8r876lS5eqU6dO6tGjh8f+mv4eBgIC0WV49NFHNWLEiEu2adWq1WWdOyYmRpJUWFio2NhY9/7CwkJ16dLF3ebo0aMerysrK9OxY8fcr79SVR3jldby97//XS6XS/fee+9Ptu3Zs6dmzJihs2fPXvF6N74a33k9e/aUJOXm5qp169aKiYm54AmJwsJCSfLKe+iL8Z08eVIDBw5Uw4YNlZmZqXr16l2yvTffv4uxWq0KDQ11fy/PKywsvOh4YmJiLtm+Kr+TvnI54ztv3rx5mj17tj766CNdf/31l2zbqlUrWa1W5ebm+vyP6ZWM8bx69eqpa9euys3NlVR73sNTp05pxYoVevrpp3+yH3++h9V1sd/ByMhIRUREKDQ09Ip/JqrEa3cj4ZKqe1P1vHnz3PuKiooqval6586d7jZr1671603Vl1vLrbfeesHTSRfzzDPPGFdfffVl13o5vPW93rp1qyHJ+Pe//20Yxv/dVP3DJyRefvllIzIy0jhz5oz3BvATLnd8RUVFxk033WTceuutxqlTp6rUl6/evx49ehhjx451b5eXlxvXXnvtJW+q/p//+R+Pfb169brgpupL/U76UnXHZxiGMWfOHCMyMtLYtm1blfo4fPiwERISYqxateqK670clzPGHyorKzMSEhKMCRMmGIZRO95Dw/j+70j9+vUNp9P5k334+z08T1W8qbpjx44e++6+++4Lbqq+kp+JKtXqtTOhUocOHTJ27drlfqx8165dxq5duzweL09ISDBWrlzp3p49e7bRqFEjY9WqVcYXX3xh3HHHHZU+dt+1a1fj008/NbZu3Wq0bdvWr4/dX6qWb775xkhISDA+/fRTj9ft37/fCAkJMT744IMLzrl69Wrj1VdfNb788ktj//79xp///GfDYrEYU6dONX08P1bd8eXm5hpPP/20sXPnTiMvL89YtWqV0apVK6NPnz7u15x/7L5///5Gdna2sWbNGqNx48Z+e+y+OuMrKioyevbsaXTq1MnIzc31eMy3rKzMMAz/vn8rVqww6tevb2RkZBh79+41xowZYzRq1Mj9RN8999xjPP744+72H3/8sVG3bl1j3rx5ht1uN5566qlKH7v/qd9JX6nu+GbPnm2EhYUZf//73z3eq/P/DTp58qTxhz/8wdi2bZuRl5dnfPTRR8YNN9xgtG3b1qfh/ErGOH36dGPt2rXG119/bWRlZRnDhg0zwsPDjT179rjb1OT38LxbbrnF+M1vfnPB/kB7D0+ePOn+WyfJmD9/vrFr1y7j0KFDhmEYxuOPP27cc8897vbnH7ufOHGiYbfbjUWLFlX62P2lvmfeQCAyWUpKiiHpgq+NGze62+i/87WcV1FRYUyZMsVo2rSpUb9+feO2224z9u3b53He7777zrj77ruNq666yoiMjDRGjhzpEbJ86adqycvLu2DMhmEYkydPNpo3b26Ul5dfcM4PPvjA6NKli3HVVVcZDRo0MDp37mwsWbKk0rZmq+74HA6H0adPHyM6OtqoX7++0aZNG2PixIke8xAZhmEcPHjQSE5ONiIiIgyr1Wo8+uijHo+t+0p1x7dx48ZKf6YlGXl5eYZh+P/9e/HFFw2bzWaEhYUZPXr0MLZv3+4+duuttxopKSke7d966y3juuuuM8LCwowOHToY7733nsfxqvxO+lJ1xteiRYtK36unnnrKMAzDcLlcRv/+/Y3GjRsb9erVM1q0aGHcf//9Xv1DczmqM8bx48e72zZt2tS4/fbbjc8//9zjfDX5PTQMw8jJyTEkGR9++OEF5wq09/Bi/404P6aUlBTj1ltvveA1Xbp0McLCwoxWrVp5/E0871LfM28IMQwfP8cMAAAQYJiHCAAABD0CEQAACHoEIgAAEPQIRAAAIOgRiAAAQNAjEAEAgKBHIAIAAEGPQAQAAIIegQgAAAQ9AhEAAAh6BCIAABD0CEQAACDo/X+KCI8qAphZNgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.histplot(t_1_test)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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AOADM2bMUO3atfX4448HuhQAVcAlMwDwstzcXL366qvatm2bezR6AObGGSIA8LJ///vfOnTokGw2m2rXrq3atWtr3759+uMf/6gWLVoEujwAF8EZIgDwsiFDhqh3796V1vXt21dDhgzRsGHDAlQVgMshEAEwpeI935i6n7KyMhUUFLiXCwsLlZeXp+joaNlsNjVs2LBS+zp16igmJkZt27atUb0AfINABMBUrFarIiwWLXjqf/zWZ4TFIqvV6tF7tm7dql69ermX09PTJUmpqanKysryZnkA/IBABMBUbDab8u1200/dkZycLMMwqtx+7969HlYFwJ8IRABMx2azMbcYAL/iKTMAABD0CEQAACDoEYgAAEDQIxABAICgRyACAABBj0AEAACCHoEIAAAEPQIRAAAIegzMCMB0HA6H6UeqBnB1IRABMBWHw6GExESddLn81meExaJ8u92jUJSTk6OZM2cqNzdXRUVFys7OVv/+/d3bhw4dqoULF1Z6T9++fbVixQpvlQ3AiwhEAEzF6XTqpMulEdPmKLZla5/3V7SnQAvGj5bT6fQoEJ04cUIdO3bU8OHDNWDAgIu2ueuuu5SZmelerlu3bo3rBeAbBCIAphTbsrWat7sh0GVcUkpKilJSUi7bpm7duoqJifFTRQBqgpuqAcBH1q1bp8aNG6tt27Z65JFH9P333we6JACXwBkiAPCBu+66SwMGDFB8fLy++eYbTZgwQSkpKdq0aZNCQ0MDXR6AnyAQAYAPDBo0yP3nG264QTfeeKNatWqldevW6c477wxgZQAuhktmAOAHLVu2lNVqVUFBQaBLAXARBCIA8IMDBw7o+++/V2xsbKBLAXARXDIDYEpFe/xzJqW6/ZSVlVU621NYWKi8vDxFR0crOjpazz33nAYOHKiYmBh98803evLJJ9W6dWv17dvXW6UD8CICEQBTsVqtirBYtGD8aL/1GWGxyGq1evSerVu3qlevXu7l9PR0SVJqaqoyMjL0xRdfaOHChTp69Kji4uLUp08fTZkyhbGIAJMiEAEwFZvNpny73fRTdyQnJ8swjEtuX7lyZU3LAuBH1QpELVu21JYtW9SwYcNK648ePaqbb75Ze/bs8UpxAIKTzWZjbjEAflWtm6r37t2r8vLyC9afPn1a3377bY2LAgAA8CePzhAtXbrU/eeVK1cqKirKvVxe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ePBghYeHB7oUAJfg0SWzZ555RkuWLNH111+vUaNGqW3btpKk/Px8zZs3T+Xl5Xr66ac9KqCsrEyDBw/Wm2++qRdeeMG9/tixY1qwYIEWLVqkX/ziF5KkzMxMJSYmavPmzbr11lv18ccfa+fOnfrkk0/UpEkT3XTTTZoyZYrGjRunyZMnKywsTPPnz1d8fLxeeeUVST8MD7BhwwbNnj1bffv29ahWAPDUv//9b+3atUvvvvtuoEsBcBkenSFq0qSJNm7cqA4dOmj8+PG67777dN9992nChAnq0KGDNmzYoCZNmnhUQFpamvr163fBGEa5ubk6e/ZspfUJCQmy2WzatGmTJGnTpk264YYbKvXZt29flZaW6quvvnK3+em++/bt697HxZw+fVqlpaWVXgBQHQsWLFDnzp3VsWPHQJcC4DI8HpixefPm+vDDD3XkyBEVFBTIMAy1adNG1157rcedL168WNu2bdOWLVsu2FZcXKywsDA1aNCg0vomTZqouLjY3eanAez88s+1KS0t1cmTJxUREXFB39OmTdNzzz3n8ecB4D0HC/NN3U9ZWZkKCgrcy4WFhcrLy1N0dLR7PKPS0lK9//777jPUAMyrWiNVS9K1116rrl27Vrvj/fv364knntCqVatMd119/PjxSk9Pdy+Xlpa6pygB4FtWq1URFosynh7qtz4jLBZZrVaP3rN161b16tXLvXz+Z0Zqaqr7wY3FixfLMAw98MADXqsVgG9UOxDVVG5urg4dOqSbb77Zva68vFw5OTl6/fXXtXLlSp05c0ZHjx6tdJaopKTEPb5HTEyMPv/880r7Pf8U2o/b/PTJtJKSEkVGRl707JAk1a1bV3Xr1q3xZwTgOZvNpny73fRzmSUnJ8swjMu2GTlypEaOHFmT0gD4ScAC0Z133qkvv/yy0rphw4YpISFB48aNU7NmzVSnTh2tXr3aPbLrrl275HA4lJSUJElKSkrS1KlTdejQITVu3FiStGrVKkVGRqpdu3buNh9++GGlflatWuXeBwDzsdlszCsGwK8CFojq16+vDh06VFpXr149NWzY0L1+xIgRSk9PV3R0tCIjI/XYY48pKSlJt956q6QfRsZu166dhgwZopdeeknFxcV65plnlJaW5j7D8/DDD+v111/Xk08+qeHDh2vNmjV67733tHz5cv9+YAAAYFoBC0RVMXv2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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.histplot(t_1_test_scaled)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "train_scaled_2, test_scaled_1 = scale_data2(t_1_train, t_1_test)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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AOADM2bMUO3atfX4448HuhQAVcAlMwDwstzcXL366qvatm2bezR6AObGGSIA8LJ///vfOnTokGw2m2rXrq3atWtr3759+uMf/6gWLVoEujwAF8EZIgDwsiFDhqh3796V1vXt21dDhgzRsGHDAlQVgMshEAEwpeI935i6n7KyMhUUFLiXCwsLlZeXp+joaNlsNjVs2LBS+zp16igmJkZt27atUb0AfINABMBUrFarIiwWLXjqf/zWZ4TFIqvV6tF7tm7dql69ermX09PTJUmpqanKysryZnkA/IBABMBUbDab8u1200/dkZycLMMwqtx+7969HlYFwJ8IRABMx2azMbcYAL/iKTMAABD0CEQAACDoEYgAAEDQIxABAICgRyACAABBj0AEAACCHoEIAAAEPQIRAAAIegzMCMB0HA6H6UeqBnB1IRABMBWHw6GExESddLn81meExaJ8u92jUJSTk6OZM2cqNzdXRUVFys7OVv/+/d3bhw4dqoULF1Z6T9++fbVixQpvlQ3AiwhEAEzF6XTqpMulEdPmKLZla5/3V7SnQAvGj5bT6fQoEJ04cUIdO3bU8OHDNWDAgIu2ueuuu5SZmelerlu3bo3rBeAbBCIAphTbsrWat7sh0GVcUkpKilJSUi7bpm7duoqJifFTRQBqgpuqAcBH1q1bp8aNG6tt27Z65JFH9P333we6JACXwBkiAPCBu+66SwMGDFB8fLy++eYbTZgwQSkpKdq0aZNCQ0MDXR6AnyAQAYAPDBo0yP3nG264QTfeeKNatWqldevW6c477wxgZQAuhktmAOAHLVu2lNVqVUFBQaBLAXARBCIA8IMDBw7o+++/V2xsbKBLAXARXDIDYEpFe/xzJqW6/ZSVlVU621NYWKi8vDxFR0crOjpazz33nAYOHKiYmBh98803evLJJ9W6dWv17dvXW6UD8CICEQBTsVqtirBYtGD8aL/1GWGxyGq1evSerVu3qlevXu7l9PR0SVJqaqoyMjL0xRdfaOHChTp69Kji4uLUp08fTZkyhbGIAJMiEAEwFZvNpny73fRTdyQnJ8swjEtuX7lyZU3LAuBH1QpELVu21JYtW9SwYcNK648ePaqbb75Ze/bs8UpxAIKTzWZjbjEAflWtm6r37t2r8vLyC9afPn1a3377bY2LAgAA8CePzhAtXbrU/eeVK1cqKirKvVxe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ePBghYeHB7oUAJfg0SWzZ555RkuWLNH111+vUaNGqW3btpKk/Px8zZs3T+Xl5Xr66ac9KqCsrEyDBw/Wm2++qRdeeMG9/tixY1qwYIEWLVqkX/ziF5KkzMxMJSYmavPmzbr11lv18ccfa+fOnfrkk0/UpEkT3XTTTZoyZYrGjRunyZMnKywsTPPnz1d8fLxeeeUVST8MD7BhwwbNnj1bffv29ahWAPDUv//9b+3atUvvvvtuoEsBcBkenSFq0qSJNm7cqA4dOmj8+PG67777dN9992nChAnq0KGDNmzYoCZNmnhUQFpamvr163fBGEa5ubk6e/ZspfUJCQmy2WzatGmTJGnTpk264YYbKvXZt29flZaW6quvvnK3+em++/bt697HxZw+fVqlpaWVXgBQHQsWLFDnzp3VsWPHQJcC4DI8HpixefPm+vDDD3XkyBEVFBTIMAy1adNG1157rcedL168WNu2bdOWLVsu2FZcXKywsDA1aNCg0vomTZqouLjY3eanAez88s+1KS0t1cmTJxUREXFB39OmTdNzzz3n8ecB4D0HC/NN3U9ZWZkKCgrcy4WFhcrLy1N0dLR7PKPS0lK9//777jPUAMyrWiNVS9K1116rrl27Vrvj/fv364knntCqVatMd119/PjxSk9Pdy+Xlpa6pygB4FtWq1URFosynh7qtz4jLBZZrVaP3rN161b16tXLvXz+Z0Zqaqr7wY3FixfLMAw98MADXqsVgG9UOxDVVG5urg4dOqSbb77Zva68vFw5OTl6/fXXtXLlSp05c0ZHjx6tdJaopKTEPb5HTEyMPv/880r7Pf8U2o/b/PTJtJKSEkVGRl707JAk1a1bV3Xr1q3xZwTgOZvNpny73fRzmSUnJ8swjMu2GTlypEaOHFmT0gD4ScAC0Z133qkvv/yy0rphw4YpISFB48aNU7NmzVSnTh2tXr3aPbLrrl275HA4lJSUJElKSkrS1KlTdejQITVu3FiStGrVKkVGRqpdu3buNh9++GGlflatWuXeBwDzsdlszCsGwK8CFojq16+vDh06VFpXr149NWzY0L1+xIgRSk9PV3R0tCIjI/XYY48pKSlJt956q6QfRsZu166dhgwZopdeeknFxcV65plnlJaW5j7D8/DDD+v111/Xk08+qeHDh2vNmjV67733tHz5cv9+YAAAYFoBC0RVMXv2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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.histplot(test_scaled_1)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2875, 25)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t_1_train.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1.0" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t_1_train[:, 0].min()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.48529729631535756 0.7426486481576787\n", + "-1.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0019739897816999537 0.0019739897816999537\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.00034835113794705065 0.00034835113794705065\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0015095215977705527 0.0015095215977705527\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.008941012540640966 0.008941012540640966\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.006967022758941012 0.006967022758941012\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.00023223409196470042 0.00023223409196470042\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.00023223409196470042 0.00023223409196470042\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.00011611704598235021 0.00011611704598235021\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.00011611704598235021 0.00011611704598235021\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.0013934045517882026 0.0013934045517882026\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0013934045517882026 0.0013934045517882026\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0006967022758941013 0.0006967022758941013\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.0024384579656293545 0.0024384579656293545\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.002206223873664654 0.002206223873664654\n", + "0.0 1.0\n", + "0.0 1.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n", + "0.0 0.0\n", + "0.0 0.0\n", + "0.0 0.0\n", + "-------------\n" + ] + } + ], + "source": [ + "for feature in range(0, 25):\n", + " print(f'{t_1_test[:, feature].mean()} {t_1_test_scaled[:, feature].mean()}')\n", + " print(f'{t_1_test[:, feature].min()} {t_1_test[:, feature].max()}')\n", + " print(f'{t_1_test_scaled[:, feature].min()} {t_1_test_scaled[:, feature].max()}')\n", + " print('-------------')\n", + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py3.9test", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "27efe6010b91a164a18a011cd71b7afbe2f076e5b83b7f8099f414d97e11e710" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/subject1-4/AdaDiff/data/preprocess_smd.ipynb b/subject1-4/AdaDiff/data/preprocess_smd.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..0126dc7c7e42968ac267d51d5ad446e744849e0f --- /dev/null +++ b/subject1-4/AdaDiff/data/preprocess_smd.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/root/Diff-Anomaly/TranAD/data'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%pwd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "smd_dir = '../processed/SMD/'" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "84\n", + "(28479, 38)\n", + "normalized: machine-1-1_test.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-3-2_labels.npy 0.0 - 1.0\n", + "(28743, 38)\n", + "normalized: machine-2-6_train.npy 0.0 - 1.0\n", + "(23693, 38)\n", + "normalized: machine-3-10_test.npy 0.0 - 1.0\n", + "(23700, 38)\n", + "normalized: machine-2-2_labels.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-1-6_labels.npy 0.0 - 1.0\n", + "(23694, 38)\n", + "normalized: machine-1-2_test.npy 0.0 - 1.0\n", + "(23696, 38)\n", + "normalized: machine-2-7_test.npy 0.0 - 1.0\n", + "(28479, 38)\n", + "normalized: machine-1-1_train.npy 0.0 - 1.0\n", + "(23702, 38)\n", + "normalized: machine-1-3_train.npy 0.0 - 1.0\n", + "(28696, 38)\n", + "normalized: machine-3-11_test.npy 0.0 - 1.0\n", + "(28700, 38)\n", + "normalized: machine-3-1_labels.npy 0.0 - 1.0\n", + "(28705, 38)\n", + "normalized: machine-3-7_test.npy 0.0 - 1.0\n", + "(28713, 38)\n", + "normalized: machine-3-9_test.npy 0.0 - 1.0\n", + "(28695, 38)\n", + "normalized: machine-3-11_train.npy 0.0 - 1.0\n", + "(23688, 38)\n", + "normalized: machine-2-3_train.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-4_labels.npy 0.0 - 1.0\n", + "(23699, 38)\n", + "normalized: machine-1-8_test.npy 0.0 - 1.0\n", + "(23694, 38)\n", + "normalized: machine-2-1_labels.npy 0.0 - 1.0\n", + "(23706, 38)\n", + "normalized: machine-1-4_train.npy 0.0 - 1.0\n", + "(23702, 38)\n", + "normalized: machine-3-2_train.npy 0.0 - 1.0\n", + "(23694, 38)\n", + "normalized: machine-1-2_train.npy 0.0 - 1.0\n", + "(28705, 38)\n", + "normalized: machine-3-7_train.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-3-3_test.npy 0.0 - 1.0\n", + "(23707, 38)\n", + "normalized: machine-1-4_labels.npy 0.0 - 1.0\n", + "(28743, 38)\n", + "normalized: machine-2-6_test.npy 0.0 - 1.0\n", + "(28479, 38)\n", + "normalized: machine-1-1_labels.npy 0.0 - 1.0\n", + "(23699, 38)\n", + "normalized: machine-2-2_train.npy 0.0 - 1.0\n", + "(28704, 38)\n", + "normalized: machine-3-8_test.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-1-6_test.npy 0.0 - 1.0\n", + "(23698, 38)\n", + "normalized: machine-1-8_train.npy 0.0 - 1.0\n", + "(28722, 38)\n", + "normalized: machine-2-9_labels.npy 0.0 - 1.0\n", + "(23699, 38)\n", + "normalized: machine-1-8_labels.npy 0.0 - 1.0\n", + "(23706, 38)\n", + "normalized: machine-1-5_labels.npy 0.0 - 1.0\n", + "(23687, 38)\n", + "normalized: machine-3-4_train.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-5_labels.npy 0.0 - 1.0\n", + "(28726, 38)\n", + "normalized: machine-3-6_labels.npy 0.0 - 1.0\n", + "(23693, 38)\n", + "normalized: machine-3-10_labels.npy 0.0 - 1.0\n", + "(23697, 38)\n", + "normalized: machine-1-7_train.npy 0.0 - 1.0\n", + "(23690, 38)\n", + "normalized: machine-3-5_train.npy 0.0 - 1.0\n", + "(23694, 38)\n", + "normalized: machine-2-1_test.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-2-8_test.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-3_labels.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-3_test.npy 0.0 - 1.0\n", + "(23691, 38)\n", + "normalized: machine-3-5_labels.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-5_test.npy 0.0 - 1.0\n", + "(23687, 38)\n", + "normalized: machine-3-4_labels.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-2-8_labels.npy 0.0 - 1.0\n", + "(23706, 38)\n", + "normalized: machine-1-5_test.npy 0.0 - 1.0\n", + "(23692, 38)\n", + "normalized: machine-3-10_train.npy 0.0 - 1.0\n", + "(23697, 38)\n", + "normalized: machine-1-7_labels.npy 0.0 - 1.0\n", + "(28704, 38)\n", + "normalized: machine-3-8_labels.npy 0.0 - 1.0\n", + "(23700, 38)\n", + "normalized: machine-2-2_test.npy 0.0 - 1.0\n", + "(28700, 38)\n", + "normalized: machine-3-1_test.npy 0.0 - 1.0\n", + "(28705, 38)\n", + "normalized: machine-3-7_labels.npy 0.0 - 1.0\n", + "(28722, 38)\n", + "normalized: machine-2-9_test.npy 0.0 - 1.0\n", + "(28726, 38)\n", + "normalized: machine-3-6_train.npy 0.0 - 1.0\n", + "(23688, 38)\n", + "normalized: machine-1-6_train.npy 0.0 - 1.0\n", + "(23696, 38)\n", + "normalized: machine-2-7_labels.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-3-3_labels.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-4_train.npy 0.0 - 1.0\n", + "(23694, 38)\n", + "normalized: machine-1-2_labels.npy 0.0 - 1.0\n", + "(23705, 38)\n", + "normalized: machine-1-5_train.npy 0.0 - 1.0\n", + "(23689, 38)\n", + "normalized: machine-2-4_test.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-1-3_test.npy 0.0 - 1.0\n", + "(23687, 38)\n", + "normalized: machine-3-4_test.npy 0.0 - 1.0\n", + "(23693, 38)\n", + "normalized: machine-2-1_train.npy 0.0 - 1.0\n", + "(23707, 38)\n", + "normalized: machine-1-4_test.npy 0.0 - 1.0\n", + "(28713, 38)\n", + "normalized: machine-3-9_train.npy 0.0 - 1.0\n", + "(23696, 38)\n", + "normalized: machine-2-7_train.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-3-2_test.npy 0.0 - 1.0\n", + "(23691, 38)\n", + "normalized: machine-3-5_test.npy 0.0 - 1.0\n", + "(28703, 38)\n", + "normalized: machine-3-8_train.npy 0.0 - 1.0\n", + "(28713, 38)\n", + "normalized: machine-3-9_labels.npy 0.0 - 1.0\n", + "(23702, 38)\n", + "normalized: machine-2-8_train.npy 0.0 - 1.0\n", + "(28726, 38)\n", + "normalized: machine-3-6_test.npy 0.0 - 1.0\n", + "(23697, 38)\n", + "normalized: machine-1-7_test.npy 0.0 - 1.0\n", + "(28700, 38)\n", + "normalized: machine-3-1_train.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-1-3_labels.npy 0.0 - 1.0\n", + "(28722, 38)\n", + "normalized: machine-2-9_train.npy 0.0 - 1.0\n", + "(28696, 38)\n", + "normalized: machine-3-11_labels.npy 0.0 - 1.0\n", + "(23688, 38)\n", + "normalized: machine-2-5_train.npy 0.0 - 1.0\n", + "(23703, 38)\n", + "normalized: machine-3-3_train.npy 0.0 - 1.0\n", + "(28743, 38)\n", + "normalized: machine-2-6_labels.npy 0.0 - 1.0\n" + ] + } + ], + "source": [ + "files = os.listdir(smd_dir)\n", + "print(len(files))\n", + "for file in files:\n", + " sample = np.load(smd_dir + file)\n", + " max = sample.max()\n", + " min = sample.min()\n", + " print(sample.shape)\n", + " if min == 0 and max == 1:\n", + " print(f'normalized: {file} {min} - {max}')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py3.9test", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "27efe6010b91a164a18a011cd71b7afbe2f076e5b83b7f8099f414d97e11e710" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/subject1-4/AdaDiff/data/preprocess_synthetic.ipynb b/subject1-4/AdaDiff/data/preprocess_synthetic.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a079c7539434efb10a6a503e5ec32d0bb9494d5b --- /dev/null +++ b/subject1-4/AdaDiff/data/preprocess_synthetic.ipynb @@ -0,0 +1,850 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 147, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": {}, + "outputs": [], + "source": [ + "DATASET = 'pattern_trend10'\n", + "ORIG = 'trend'\n", + "FILE = 'trend10'" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "def abs_normalize(a):\n", + " a = a / np.maximum(np.absolute(a.max(axis=0)), np.absolute(a.min(axis=0)))\n", + " a = np.nan_to_num(a)\n", + " return (a / 2 + 0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/root/.conda/envs/py3.9test/lib/python3.9/site-packages/scipy/__init__.py:138: UserWarning: A NumPy version >=1.16.5 and <1.23.0 is required for this version of SciPy (detected version 1.23.1)\n", + " warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion} is required for this version of \"\n" + ] + } + ], + "source": [ + "from sklearn.preprocessing import MinMaxScaler\n", + "\n", + "def scale_data(train, test, validation):\n", + " scaler = MinMaxScaler(feature_range=(0, 1), clip=True).fit(train)\n", + "\n", + " train_scaled = scaler.transform(train)\n", + " test_scaled = scaler.transform(test)\n", + " validation_scaled = scaler.transform(validation)\n", + "\n", + " return train_scaled, test_scaled, validation_scaled" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
col_0col_1col_2col_3col_4anomaly
0-0.1312322.6078500.0660114.381993-1.8944490
10.3987362.4039650.4427014.554839-1.5307900
20.8091082.1706900.7025473.998227-1.4148470
31.0078881.8746751.0067003.990737-0.9104900
41.3400911.3111061.3379473.413961-0.7203440
\n", + "
" + ], + "text/plain": [ + " col_0 col_1 col_2 col_3 col_4 anomaly\n", + "0 -0.131232 2.607850 0.066011 4.381993 -1.894449 0\n", + "1 0.398736 2.403965 0.442701 4.554839 -1.530790 0\n", + "2 0.809108 2.170690 0.702547 3.998227 -1.414847 0\n", + "3 1.007888 1.874675 1.006700 3.990737 -0.910490 0\n", + "4 1.340091 1.311106 1.337947 3.413961 -0.720344 0" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "time_series = pd.read_csv(f'{FILE}_train_test_val.csv')\n", + "time_series.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
col_0col_1col_2col_3col_4anomaly
0-0.1312322.6078500.0660114.381993-1.8944490
10.3987362.4039650.4427014.554839-1.5307900
20.8091082.1706900.7025473.998227-1.4148470
31.0078881.8746751.0067003.990737-0.9104900
41.3400911.3111061.3379473.413961-0.7203440
\n", + "
" + ], + "text/plain": [ + " col_0 col_1 col_2 col_3 col_4 anomaly\n", + "0 -0.131232 2.607850 0.066011 4.381993 -1.894449 0\n", + "1 0.398736 2.403965 0.442701 4.554839 -1.530790 0\n", + "2 0.809108 2.170690 0.702547 3.998227 -1.414847 0\n", + "3 1.007888 1.874675 1.006700 3.990737 -0.910490 0\n", + "4 1.340091 1.311106 1.337947 3.413961 -0.720344 0" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train_time_series = time_series[:20000]\n", + "train_time_series.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-15.097620609417833\n", + "85.04304598705383\n" + ] + } + ], + "source": [ + "print(train_time_series['col_4'].min())\n", + "print(train_time_series['col_4'].max())" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
col_0col_1col_2col_3col_4anomaly
200000.1134802.5419600.0024184.4137782.5139330
200010.4253302.3527050.4029864.3433632.8619540
200020.5934772.3853190.7958604.2505153.3436780
200030.9907081.6829741.0200783.9477633.4182430
200041.3283721.4523741.2582303.1268273.6719270
\n", + "
" + ], + "text/plain": [ + " col_0 col_1 col_2 col_3 col_4 anomaly\n", + "20000 0.113480 2.541960 0.002418 4.413778 2.513933 0\n", + "20001 0.425330 2.352705 0.402986 4.343363 2.861954 0\n", + "20002 0.593477 2.385319 0.795860 4.250515 3.343678 0\n", + "20003 0.990708 1.682974 1.020078 3.947763 3.418243 0\n", + "20004 1.328372 1.452374 1.258230 3.126827 3.671927 0" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_time_series = time_series[20000:40000]\n", + "test_time_series.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
col_0col_1col_2col_3col_4anomaly
40000-0.0948022.6521710.0189394.550506-6.4912400
400010.3597502.2560460.3312344.466656-6.1598940
400020.8235032.1833440.8220094.316569-5.6821000
400031.0802651.8167901.0566593.858038-5.4638390
400041.2489271.3742571.2385673.136138-5.1982220
\n", + "
" + ], + "text/plain": [ + " col_0 col_1 col_2 col_3 col_4 anomaly\n", + "40000 -0.094802 2.652171 0.018939 4.550506 -6.491240 0\n", + "40001 0.359750 2.256046 0.331234 4.466656 -6.159894 0\n", + "40002 0.823503 2.183344 0.822009 4.316569 -5.682100 0\n", + "40003 1.080265 1.816790 1.056659 3.858038 -5.463839 0\n", + "40004 1.248927 1.374257 1.238567 3.136138 -5.198222 0" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "val_time_series = time_series[40000:]\n", + "val_time_series.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "train_labels = train_time_series.anomaly.to_numpy()\n", + "test_labels = test_time_series.anomaly.to_numpy()\n", + "val_labels = val_time_series.anomaly.to_numpy()" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train (20000, 5)\n", + "Test: (20000, 5)\n", + "Val: (10000, 5)\n" + ] + } + ], + "source": [ + "train_no_labels = train_time_series.drop(['anomaly'], axis=1).to_numpy()\n", + "test_no_labels = test_time_series.drop(['anomaly'], axis=1).to_numpy()\n", + "val_no_labels = val_time_series.drop(['anomaly'], axis=1).to_numpy()\n", + "print('Train ', train_no_labels.shape)\n", + "print('Test: ', test_no_labels.shape)\n", + "print('Val: ', val_no_labels.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train ratio: 1.01\n", + "Val ratio: 0.8999999999999999\n" + ] + } + ], + "source": [ + "train_ratio = (train_labels.sum() / train_labels.shape[0]) * 100\n", + "val_ratio = (val_labels.sum() / val_labels.shape[0]) * 100\n", + "print('Train ratio: ', train_ratio)\n", + "print('Val ratio: ', val_ratio)" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "normalized dataset\n" + ] + } + ], + "source": [ + "if ORIG != 'pattern_trend': \n", + " train_normalized = abs_normalize(train_no_labels)\n", + " val_normalized = abs_normalize(val_no_labels)\n", + " test_normalized = abs_normalize(test_no_labels)\n", + " print('normalized dataset')" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "def expand_labels(data, labels):\n", + " labels_reshaped = np.zeros_like(data)\n", + " for idx in range(0, len(labels)):\n", + " if labels[idx]:\n", + " labels_reshaped[idx][0:labels_reshaped.shape[1]] = 1\n", + " return labels_reshaped" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# val_labels_reshaped = expand_labels(val_normalized, val_labels)\n", + "# test_labels_reshaped = expand_labels(test_normalized, test_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "# FOR RATIO ABLATION\n", + "#test5 = np.load(f'../processed/{ORIG}/test.npy')\n", + "#labels5 = np.load(f'../processed/{ORIG}/labels.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import MinMaxScaler\n", + "\n", + "def scale_test(test):\n", + " scaler = MinMaxScaler(feature_range=(0, 1)).fit(test)\n", + "\n", + " #train_scaled = scaler.transform(train)\n", + " test_scaled = scaler.transform(test)\n", + "\n", + " return test_scaled" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": {}, + "outputs": [], + "source": [ + "trend5_series = pd.read_csv('trend_train_test_val.csv')\n", + "test_trend5 = trend5_series[20000:40000]\n", + "#labels5 = test_trend5.anomaly.to_numpy()\n", + "#test_trend5 = test_trend5.drop(['anomaly'], axis=1).to_numpy()\n", + "#labels5 = expand_labels(test_trend5, labels5)" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-97.97744390787436\n", + "-1.0244058283630473\n" + ] + } + ], + "source": [ + "print(test_trend5['col_4'].min())\n", + "print(test_trend5['col_4'].max())" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [], + "source": [ + "#ratio = 1\n", + "#ds = f'pattern_trend{ratio}_test5'\n", + "#test5_normalized = scale_test(train_no_labels, test_trend5)\n", + "test5_normalized = scale_test(test_trend5)\n", + "#np.save(f'../processed/{ds}/test.npy', normalized_test_for_ds)\n", + "#np.save(f'../processed/{ds}/labels.npy', labels_5_expanded)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "normalized trend\n" + ] + } + ], + "source": [ + "# if ORIG == 'pattern_trend':\n", + "# train_normalized, test5_normalized, val_normalized = scale_data(train_no_labels, test5, val_no_labels)\n", + "# print('normalized trend')" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": {}, + "outputs": [], + "source": [ + "val_labels_reshaped = expand_labels(val_normalized, val_labels)\n", + "#test_labels_reshaped = expand_labels(test_normalized, test_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "saved trend\n" + ] + } + ], + "source": [ + "import os \n", + "os.makedirs(f'../processed/{DATASET}_test5', exist_ok=True)\n", + "# np.save(f'../processed/{DATASET}_test5/train.npy', train_normalized)\n", + "if ORIG != 'pattern_trend' and ORIG != 'trend':\n", + " np.save(f'../processed/{DATASET}_test5/test.npy', test5)\n", + " print('saved NOT trend')\n", + "else:\n", + " np.save(f'../processed/{DATASET}_test5/test.npy', test5_normalized)\n", + " print('saved trend')\n", + "np.save(f'../processed/{DATASET}_test5/labels.npy', labels5)\n", + "# np.save(f'../processed/{DATASET}_test5/validation.npy', val_normalized)\n", + "# np.save(f'../processed/{DATASET}_test5/labels_validation.npy', val_labels_reshaped)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(20000, 5)" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "labels_5_reshaped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "train = np.load('../processed/pattern_trend15_test5/train.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB1fUlEQVR4nO2deXwURfr/PzOTzCSBHISQCwLhRq5wSQgIiEQO8dZdFllx8Vb4qsuuIh6wurvC6qr83MVbdHe9WF1FVxAXQQQ1gnIpyn2FK+HMQe6jf3/ATHpmunv67uqe5/168SLTXV1d3V1d9emnnnrKxXEcB4IgCIIgCItwW10AgiAIgiCiGxIjBEEQBEFYCokRgiAIgiAshcQIQRAEQRCWQmKEIAiCIAhLITFCEARBEISlkBghCIIgCMJSSIwQBEEQBGEpMVYXQA7Nzc04evQoEhMT4XK5rC4OQRAEQRAy4DgOlZWVyM7Ohtstbv+whRg5evQocnJyrC4GQRAEQRAqOHToEDp06CC63xZiJDExEcC5i0lKSrK4NARBEARByKGiogI5OTmBflwMW4gR/9BMUlISiRGCIAiCsBmRXCzIgZUgCIIgCEshMUIQBEEQhKWQGCEIgiAIwlJIjBAEQRAEYSkkRgiCIAiCsBQSIwRBEARBWAqJEYIgCIIgLIXECEEQBEEQlkJihCAIgiAIS1EsRtauXYsrrrgC2dnZcLlcWLp0acRj1qxZg0GDBsHn86Fbt2544403VBSVIAiCIAgnoliMVFVVIS8vD4sWLZKVfv/+/Zg0aRLGjBmDLVu24L777sOtt96Kzz77THFhCYIgCIJwHorXppk4cSImTpwoO/2LL76Izp074+mnnwYAXHDBBfjqq6/w7LPPYvz48UpPTxAEQRCEwzDcZ6SoqAiFhYVB28aPH4+ioiLRY+rq6lBRURH0jyAIgiAIaQ6drsYtb3yH+9/bit2llVYXRzaGi5GSkhJkZGQEbcvIyEBFRQVqamoEj5k/fz6Sk5MD/3JycowuJkEQBEHYnlFPfYFVO47jvY2Hcemza3Gmqt7qIsmCydk0c+bMQXl5eeDfoUOHrC4SQRAEQTAPxwX/3mUT64hinxGlZGZmorS0NGhbaWkpkpKSEB8fL3iMz+eDz+czumgEQRAE4Wj+93Mp8ru0tboYETHcMlJQUIBVq1YFbVu5ciUKCgqMPjVBEARBRDWvfbXf6iLIQrEYOXv2LLZs2YItW7YAODd1d8uWLSguLgZwbohl2rRpgfR33nkn9u3bhwceeAA7duzA888/j3//+9/47W9/q88VEARBEARhaxSLke+//x4DBw7EwIEDAQCzZs3CwIEDMXfuXADAsWPHAsIEADp37oxly5Zh5cqVyMvLw9NPP41XX32VpvUSBEEQBAEAcHFcqLsLe1RUVCA5ORnl5eVISkqyujgEQRAEwSS5Dy4L23ZgwSQLSnIOuf03k7NpCIIgCIKIHkiMEARBEARhKSRGCIIgCIKwFBIjBEEQBEFYCokRgiAIgnAogzqmWF0EWZAYIQiCIAiH0D4lOLJ5SoLXopIog8QIQRAEQRCWYvjaNARBEARB6MMnPxzFK2v3YWjnVHy89ShKK+qQkxqP+FgPxvXOxJGymqD0q3ccR+6DyzCsSyoSvDFYveM4xvRshxd+PRhxsR6LriIcCnpGEARBEDbgaFkNhi9YrUtevhg3dv5poi55SUFBzwiCIAjCQdy0eINuedU1NuuWlx6QGCEIgiAIG7D7+Fmri2AYJEYIgiAIgrAUEiMEQRAEQVgKiRGCIAiCICyFxAhBEARBEJZCYoQgCIIgCEshMUIQBEEQhKWQGCEIgiAIwlJIjBAEQRAEYSkkRgiCIAjCBlyQ5dzlUGihPIIgCIKwAb2zkrD9WAUemNAT1wxsj6zkeHAch5KKWryzvhjPrd4jeNyBBZMAAMcra7G5uAx3/GsjurZrZWbRI0KWEYIgCIKwER6XC1nJ8QAA1/m/fTJW4E1PjENKfCwAgLUVcskyQhAEQejCjpIKrNt1ElcPbI+31h/EN3tOISk+BjMv6Y4BOSlWF08Vxaeq8d8fjuLGgk5Iiou1tCyHz1SL7mtokrfwncvlAgAcK6vFuxuKsf9UFVLivRjVIw19spN1KacaSIwQBEEQmjleWYsJC9cBAP68fHvQvs+3H8dT1/fHL4bkWFE0TVz/4jc4XlmHpz7bif3zLwt05mazq7QS6/efFt3f2KTM1lHT0IQHP/gx8PsvK1qGc6yAhmkIgiAIzRTtPSW5//73fzCpJPpyvLIu8HdFbaNl5fhsW4nk/rrGJln5WKSlIkJihCAIgtCML8b53YncoRAjcLtbVISQoGhmzQlEIc6vPQRBEIThxHqc353UN1onRjhOWm1MK+gkKx9GDSMkRgiCIAjteMkyYiiRLB8p8V5zCmIQzq89BEEQhOFEg2XkzW8PWnZuvSwa5DNCEARBOJZoECOvrNtvdREAAEYZaL4/ID5bx2icX3sIgiAIw/G4Gf3kdghNPJ+R01V1EikjIf6cYiwUlCRGCIIgCM1EcrAktLHw892Bv9/feNiQc3gsHMMhMUIQBEEQUUKzhGi00p+ExAhBEARB2AhBOaGDkIjxkGWEIAiCcDAT+2ZaXQRVxPMWoLttZGfLypHfOTXwd7/26teQkZIb3dMTVeerFVqbhiAIgtAV/xouzc0cnlu9Gws/3422re0ZByMrJQ77TlQBADKS4iwrR8fUhMDaNIlx+nTdz/wyD7EeN0b3bGf5IoAkRgiCIAjdyEmNDywm53bCDBuH+eXy/ULG9spAcoK1IsQPiRGCcCDHK2tRU9+EU1X1yEyKQ3ZKvNVFIhzIzpJKvLx2H0Z2T8Oh0+eWt3fapJp6XlCP0opaNDdzzhBZjEFihCAcxle7T+LXr60P2vbenQW4MDdV5AiCUM6CT3fgxS/3AgD+s6llqunhMzWC6e0oUj77qSToel5Ztx+vrNuPbY+NR2ufud3nx1uP6pQTm0KKHFgJwmGEChEAeGHNXgtKQjgZvxBxMnf8a6Pg9n98c8DcggCos3CRPjMgMUIQUQAFpCKswsXol7gWrFwwDxC+p3JjhNDaNARBWIaL1RaIIGxINKzDYzZ0RwmCIAhCAV6niBGGvlEcckcJgpCCoTaHiFKcNFAYa2GkUqdCYoQgCIIgFBAbY5+uc/XvRgf9ZlVG2eeOEgRBELbDie5KdvIZSbcwaqwS7HNHCYJQTRPNpiEI3bCzzwirzuz2vaMEQchmzc4TVheBIByDl8FhGjYlhnzYu6MEQRCE43CScc7qYRpOgTtwqEhhVbSQGCEIh9G1XSuri0AQAVjt/LRAs2n0h8QIQTiMdok+AMDdF3fFnIm9AADj+2RYWSTCgVzSKx0AMKl/Fj6aMQIdUxMsLpH++N+lUFj1u5ADq0UnMUIQDuWCrCTEez0AAA+tMkoYxOju7ZCXkyLacTsRWl5Bf0iMEFFLVV0jTlTWWV2MiByvrMWS74pRXtMQ2NbUzKGmvkkwfTNv2Qy/BKmsbTSwhEQ0srOkUlH6XaWVOHiqyqDS6EdtQxO+2n0SPx+tsEX7EAkpSwhLVhJz10AmCEYoKa/FsPmrAACLfzMEl/Ridxhj6J/PlfP5NXvx5f1jAADjF67FnuNnMTW/I/58Tb9A2vc3HsaGA6cDvxuazn3Brdt9Eiu2HcOEvlkmlpxwKpNfKsKRshpFx2w8eAajn1qDi7ql4c1b8w0qmTYam5rR69EVVhcjKiHLCBGV/Hfr0cDfT67YaWFJpOGvDnrwVHXg7z3HzwIA3lpfHJT+9+9tDfq9ZlfLlN4HP/jRiCISUcj6/acjJzpPc8iIxld7TupcGv3YVFwmKx2LgzRy/VhYXUWZxAgRlcTYxBtey9B0M8cFjW03NbHYhBK25/yrJOZHUdcoPJzIIk2hyolRWBUUWiAxQkQlMbw4AT4GAxjpgcvlgi/GE/jtJidWwgLsFP232UZllUuocGHJT4SPqlZ40aJFyM3NRVxcHPLz87FhwwbJ9AsXLkTPnj0RHx+PnJwc/Pa3v0Vtba2qAhOEHnh5lhGrAxhJoSS4kRB8oUUzAAhCGtmWEXqVdEdxK7xkyRLMmjUL8+bNw6ZNm5CXl4fx48fj+PHjgunffvttPPjgg5g3bx62b9+O1157DUuWLMFDDz2kufAEoRa+AGFZjAihxJTMFyP1PP8TgrCSsup6q4sgiJtVs0EUoLgVfuaZZ3Dbbbdh+vTp6N27N1588UUkJCRg8eLFgum/+eYbjBgxAjfccANyc3Mxbtw4TJkyJaI1hSCMJEiMMDxMI2TMaJApKlwAfLEtwzQN5DNCGEGEaiXk37B08xGDCqMNrZZIQj2KWuH6+nps3LgRhYWFLRm43SgsLERRUZHgMcOHD8fGjRsD4mPfvn1Yvnw5LrvsMtHz1NXVoaKiIugfQeiJncM5yxUjABDD8xOxi3MeYS8ampVb3GIYtUbKHcm0k2gJNfawavxRFGfk5MmTaGpqQkZGcEyGjIwM7NixQ/CYG264ASdPnsRFF10EjuPQ2NiIO++8U3KYZv78+XjssceUFI0gFBHjtq8vBVk4CJZoaJQWI0IdN6s1mNVyyYFRjSEbw+XpmjVr8MQTT+D555/Hpk2b8MEHH2DZsmX44x//KHrMnDlzUF5eHvh36NAho4tJRBn8oRm7rTNRUi7P+dtml0XYlEYVFrdmstIxAUtNhCLLSFpaGjweD0pLS4O2l5aWIjMzU/CYRx99FDfeeCNuvfVWAEC/fv1QVVWF22+/HQ8//DDc7nA95PP54PNFzzoHhPnwhy+8jJqMxfhqz4nIic5DgoQwmorzyxQokRcLPt2Bm4bnGlIeLci1ktrMmGoLFLXCXq8XgwcPxqpVqwLbmpubsWrVKhQUFAgeU11dHSY4PJ5zTnV2M48TzoHfR1+QlWhZOdTQr32KquPG9WY35D1hX45GsNQJObDWNDShkcHZXdHQI7EaME3x2jSzZs3CTTfdhCFDhmDo0KFYuHAhqqqqMH36dADAtGnT0L59e8yfPx8AcMUVV+CZZ57BwIEDkZ+fjz179uDRRx/FFVdcERAlBGElcbHs1kO+Xu/fIRlAsLUjLyclKP2FuW3w3YEz59KFNDqtfbQUFaEPMW5XYHgmMylOVR5MdvxMFio6UNw6TZ48GSdOnMDcuXNRUlKCAQMGYMWKFQGn1uLi4iBLyCOPPAKXy4VHHnkER44cQbt27XDFFVfgz3/+s35XQRDRSoh1kdWvHiJ6EZt5wmK0U7mzZKwuupbZPKwO3ar6VJo5cyZmzpwpuG/NmjXBJ4iJwbx58zBv3jw1pyII3Tl4qgoVtY2B3xW1Dfhq90l8vfckZo7phlYhFoT9J6vwv59KUNPQhPsKe4Tlx3GcZU6w5TUNqK5vhNvlwitr9wWt2AsED0dtOVSG5mYOdY3N4MDB63GjieOCQsYD567naHktfjxchrEXZCDG7cKWQ2Xo0q41kuNjTbgqgkWamzkcq6hFZlKcKqfVUMqrG1DbUIfWcTE4ebYO8bEeeNwuZKfEg+M4bCo+g8YmDkM7p+r2fjU0NcPjcsHlCndcr65vxN9X75GVz5vrD6Jfh2S0a+2Dy3VuMUCPjOUW6hqbEON2y0oLtLgy8Nsrp0J2WyJq4DgOnecsD9v+0pf78NKX+wAAL6zZi02PXorUVl4AwI2vrce63S2rjC78fDfuurgrZk/oBQB4btVu/OObA/jw7hHo2DZB1/L+dLQck577SjLNgVPV6D33M1n57TtZhS4PhV//rEt74J6x3bHtSDku/1vw+eJjPUhJiMWx8lq4XMD++ZPkXwDhKJ5bvRsLP9+tW35Dn1gluP37Rwrxz28O4LnzwmDK0I6Yf20/zeerrm8MvCvtU+Lx1ewxAUGys6QS4xeulZ3Xmp0nkC9Q/gMLxN+Pt9cX46EPz62cfVG3NLwx/ULJeCv/LDqAuR/9JLtMcvUaq5YRe00jIAgNnDwrLwT1n5dtB3DOIsIXIn5eWLM38PczK3fhVFU9Fn6+S59C8ogkRPTimZXnyv7797aG7atpaMKx8w6KVpumCWtRK0SUDh1u2H86IEQA4J0NxarOG8q3+04F/j5SVoPSirrA7z8v367LOaTwCxEA+GrPSXy89ahkeiVCRApWxUcoJEaIqEHuGPWx8hoAwObiM/IzN+GFb1QY7ExpI3TodLWyA3RgR0kFHv/vzzhdxeZaJURk9O7s7n5rk74Znif0/eFHMq6tbzLknFIofZ/5aPENY9WvjIZpiKhBbjh0/+JyShbQM+MF//lYBWoblDWaSsbaY2PcgMmN8oSF6wAAh89U4+VpQ0w9t93YUVKBAyerMaGvcEwnq/BrfDGtz0ro9NCPEf7v2Bg2O+hogiwjRNRQHyFstR9vQIzIb6CUrBcjh40HTwtuX79feLsexMh0qjOCrYfLLDu3XZiwcB3ufHOjaN0gpAn9FuEvq6DXyt2K1n9iQP+wFH2axAgRNcgVDN7zs0uUNFCNKhYLk+K6F4QXnlQiGNhpZiJD6+3I56ej0bdwqB4B0kJfHf47GyMQCVwNSgJ5mvV+sjosEwqJESJqqJNpGVEzTGNWZyp3SqAarHRQJZ8R+dhNuOnRGepRP0IFR0Njy330WjBMY5VVgiFjSBAkRoiooV62ZUSNGDEntLWRQylyurith8oMOz8hj2hcRkMPER7aCfN9RvSyjChByTCwHOxiARGDxAgRNcj1GYk93/BJfS2FrjoqN2+tKPmqMeILaNvRcv0zJRQRhVpEF7Eg5c5hhb+UkVZOKViVLCRGiKhh/T55jn/u842ElGWkIcRHRO4QkFaM7IjkfHFHY0fIGqyFUY8kevWYTePSoacKrd/8X1YIA7MsGawOy4RCYoSIGv62Wl7QJn8j4ZZ4i7cfqwz6vfGggpgkjCLHuqO3aZmwP4xpI1FCLSN8cWKVlcIKWBUnJEaIqOG+wu6y0rVvEx8xTVysHV4dZa3OoE5tIqZJV7lCK6EfrPb9YuVixZdByjKSk6rvUg6EcuzQohKELiQnnFtvJq21L2zf3Mt7B/6Oj/WE7Z/UPwuJvAX0QtN0SWulVzFV8dKNg3H1gOyI6Z68vj+uGdg+aFvb8+vwXJCVFNgWmsZPis4L5Xl1iu8QTVhliRh8XqyO7ZWOx67so1u+w7u2RStvy/t0Sa/0MAucHtccmgU/z6Q45fE//3hVH/TMSER+51TRc/DplZkYUh7pi+rbvuV9LOjSVnH5xGAptggfagmIqOPC3Dbont46aNvNF3XGuN4ZAMIbiYwkHxbdMAg/PjY+qNHkY9b7LdR8bXhoLMb3ycTCXw2MePwvh+Tg2ckDcGDBJHx670gALT4y/i/HO0Z1wbOTB2DmmG6yzk+Yi1URTf0jGdcP7oCbhufqlu/btw3DT49PCHwQtPLFGCJSpXxt1NzRGwty8dlvR+HlG+VFDlYqqPzp35h+Ie4e0zVierE2KHQzm1KEwsETNoXjOJRW1CE90YcTZ+vQ0NSM7OR4uN0u1DU2oay6AamtvMpCup9/Sw+eqsbBU1WSaWsbjHNYlXIkXbvrBHqGfGFJtS5yRdK+E2dxvLIuKL8YM/xDeKeob2wOTKsmWiivaQgK2GWVZUTteeWKJ39dFaz/OlxzqM9IRW0Dymsa8PfVu1vqvkFwHIdTArFSKmsb4Ha5UF3fhHaJwRZbf3n1tmTwfeFYEiYkRghbsmDFDrz05T5ZaW8c1gn/+vZg4HdJRa1gOv+MmJfX7sPLa1vy5ocQqTq/dkvocuN7T1Qh98FlAM4NmYzvo279kLvf2ojlP5aI7v8bbzVTvThRWYdLnv4y8NvfFwgJuf9sPIxBHduA4zhMW7wB63afxOezRqFbemJYWqX0eORTfPPgJchOieyzEy28um4f/rQseEXZpz7biV2llfh/MixhelBSXoth81cFfhttBfzkh2Nh2x784Ae88OvBivPaeqgMVy36WnDf9Ne/U5xfJI6cqUFuyJBteXUD8h7/X1jamW9vDvr9x6v64MaC3MBvvyjT27dWyjHfSugzhLAlcoUIgCAhAgCbi8sE063ZeUJw+8mzyr6a7vjXRkXp/VTVNUoKETHEHATVtjkl5efEmtDMmbfWn1vOfd3uk1i3+yQA4J53tqg7kQAT/9863fJyAqFCxM9HW47ijElRa59fo00Ay3VglUr16Tbl7wUAUSFiFBf/dU3Ytte+3i/r2Ec/+klwu94OwIxqERIjRHTCou+DliXFxZBqyMQaJX8ppL6g+Gbt8poGNUU7V4aQ31ryijYalSzKpoFIz8Tvc6S1j2PVsVIratbVqWtswo6Sc+EDtN6W0Pvq5plaWGoHSYwQtuN4pfAwi92pa2pSdZzebbjf0U/MR6CpmQus36MVs4LFORGzwsJHisGhV/VzqBaBL0bY6V2K9zceDvyt920J9j9iR46QGCFsR52BzqNWondIebWNWNN5C43Y7IMvdx0nR1MGaDDJMhIbIRS734KmtTS21SIRCh6rYhG+s7WNLdkb6MCq82LjmqAWhbAdpszysACz1reJhN/8L9a5PLtyd9DUyyNlNSaUirCK8PctxOxPphFJ1ExT5luj9L4tQWKELCMEoR4rVtg0A7mrCoci1Vapaciazn8uiTVUzRynaMo0YQxmmdgjPWu3TmrEmVIEqoY0+fdcsy9OyG/+4yIxQhAacOo6Eg2N+jYMas27AcuISHGaOVqjJpqI6DOiU1WIFNvHrqgR7vx7rpfY8xPDFzoMWaMozghhO9h5ffSlXrUDq7o7IjbTpikgRoTVCMdxjh0qsxNmfdTGROgM9YpbIRRfxAmo8a/ii32937Tk+FhMK+iEZo5D6vmlIFiAxAhBMILe/ohaGzGT/CMJxokkPD0MfV2ziDoxwrde6Fmaczx+VV/9M9UIDdMQUQlLU9q0ItZWcRL7JPMLhOUW3l/boM6CQ9iTULER2jmSFpFG6zBNTb02x3a7PB8SIwRBCHJRd+GVQi/umW5ySaIXNavJ6k0knwW9/A5a+6y/ViNQM4zFX1n8bF2jRErnQGKEsDX8xjovJwUA0P782iYX5rZRne+gjimqnTQ7tDF3bRW5q3UqZXCnVPz5mnBzbo+MxCCriV4B0ABgRDf9lkp3ApfnZQf+ntg3E72zWpaVN8u4F0kkBLSKxgINOP/+6kmkd/iqAdm4uGc7TecIff9Cra5SVthJ/bMCC+RlJLUIEL6fjlyHfbtYQMRwphQlooYtc8eJfrk1NjWj28Ofqsr3g7tHYNa/t+CDTUdkH/PmLfn49WvrVX/h+duszmmtsP+k8MyCqwdko2jfKZRW6LvK6NIZI3D1+XU8+I6tI7qmhZczJAJJepIvLA2hD/4n8dvCHri3sDsAoPfcFaiuN2+oLJLG0MuB1YjONDk+FifPCq/h079DcmCxQf8il2az6IZB2HKoDFcv+jrqp8tH99UThI5E8rWQgr9+hdI2We1CWmobf61f5PWNzVi/75RgkDf+TJ6mZo6ZQHBWwHGhsu8cah5bdX0j1u0+gbrGFhHT2NSMBoHYNhzHobahKfBFHykWhV4zT/VeEA6QrqtGGRJCzyk3lgc/Gd95PFo0CllGCFtRXd+IgX9cKSut2XPo1Z5tyXfFmP2fHwO/94lYRRSXR2WBIh33yNJtQb8Pna4JfFkuumEQJvXPEj2W4zj0eETcWvXtvtNhX6m7/jQx6sLPnzpbh8F/+jzwm/9Mqs5bRUY99QW6pLXC/347Kih2xPZjFZKrH++ffxkAyLIabn98AuZ/uiNoW1j1MMkysvzHYzhb14gH3v8BCycPwNUD20fMs6LW/IUXT5ytQ0ZSHABgy6Ey3PnmJsn0/ss+UlaDusYm+GI8mPrqt7z9Lk1CjaVYIlJE1xtO2J5/FR20uggREf6eFYcvRFShQ1ujV3M1423phndnaaXiPJ0aDEuKf3xzQFa6fSer8K9vg9+JK//+leQxPx2tkD3Ms3DVrohp9JraGymbu9/ahAfe/wEAcN+SLRHzO11VjwYDVsKOxA2vtAiJ2efLK5f/bDyCU2frgiwjNtESmiExQtiKylr5nuWmv8MahmmMgrWGrErFzIBoHEsPXQRPqk6F+hdF6oCr6hoDUXYjsengmYhpWH083+47Zcl5955oeR5y3j9+msNnqvHnZdtD9odnondUVhZgtBoRhDB6mesZ0guaEZ1Nw2B75fUoX049NsqGaIBwId2k4/KqzZz8RRnlWBaM8PXQA7mCy0hCZ8K08krX/9e/PoDDZ4IXnhTSHU4MgBx9bzlha5R8JZvdGfsbZeubQHZRFY3SgV+BkYgJqef1Og43VNc34roXvtEtP/3eM32fc6PKhSeVIlXqUIuWkJWDL+ZqGprCIt4KiT0hywirolAuJEYIWxFtjoxyEGuCIjVOVjRdqjoue7exqggVYHrOKnpp7T4Un66WlVZwNk/IQ/RP7dUql/T+eBCaKSTnhPGxHsye0EvfwiggVIgKaXG9plOzBLXshK3wKrBPmj6bJuAzIr9ZPlMlHANBDKUNPmue9Cz507BMaIfUqOMwTVm1/Donxyjl1qkX0bumqnVe3fbYeIzqER5fRw1yShD6iobec6F32InrAZEYIWwFy86MapqHl9buU3wM6x16EwNj9XYnNHKo1Fe+0npXp8DKIucLnNWgZ8cr1QUGlBvxVA7bj1UE/ZbzoRJ6dkHLiAOHLtlt2QlCAJbFiB8lXTE/CJVaWLN+RDSPExHxxQY7OtY3iteqY+W1ivLWO5Aca/XPz3OrdltdBFWEiiEh4WGDZlAxDrwkwsmwPLPC3yjvO1Flu1WB+f0Jv3NR08/oLkbsdSt1IS6knkvd0//9XKoobyXT44Wih4ZWCd3ijESjc5AAoeKuY2pCWBoapiEIi2F5ZkV5TUu0xxNn5ZmIlTbAQhpHKgcr7pYVgaacRlyIZUQPC5ofJavAyhlxS4gwXZVVWGlJQnVFqNDwR3Pl48RhGgoHT9iKUMc+lgiyhsjsj9urWOFX9kdRhHRGfVxJDQMojU4brYT6YZypNj+sOQAkxIoLjfvH98RPR8sxqoe2VW/9sNS/KvlI0HuYKjetVcQ0Qn46djeWsNuyE4QALDVYetAtvbWi9EINjt6NkNbs5C4MFsqdo7tiUMcUybVtooVQ0RYvIQpGh4iBC3PbBP7+9bCOGNc7A3k5KarK4V/eXogZY7rh+amDdXP4VNKp+2QM116Rl62lOLqQnRxu1QiFL3xiPS7ktg0flglFTydbViDLCEHohJYvpD7ZSWhq5rCjRHrtFsX9vEFtVt/2Sdh2pGWmQNGcSzD6yTWo1+Av8uDEltgOi24AusxZhmYuKl1GFJHWOlgw+Ovh328YiMv7t3TI97yzGR9vPRqUNrdtAtbcPyYszz98/BPe+OaAoinFWt2klLw+cjrjSEnMqFfJCV4cVeBgHBcjb8jL7lYQIcgyQjBPczOHEoUzBqwgtH1Y/NV+LPpijwnnFW6Z1LZXWhs6vab2sjpLwwqkbkWoFcV//+UMNYjdY//mRgH/H6MeixKnTCnhU1Jeq8gvxgiUxHJRcz+dGPSMLCME09Q3NksuOc8qQ59YFfj71XX7sOnRSx3fufqtIiOf/AIAsPeJy8K+YLV+PX+x8zimv/4dnpsyEFcyYIY3ij9+8rPstB9sOoIPNh0J2x5a3YSq36EIkViVztTRghKnzJqGJtTUNyE+xHl22uINWLvrhKw8jHwbBzy+UtVxlXWNePCDyKt4O3CUhiwjBNv8N8SszDJiWuNMdQO+2Hnc9PMqET96zOoJZc/xs4rylMP0178DcG7IwcmUVqgL2MVHzhMVW0wudLE2TeWQWbWU9q+vrgsOGFhe0yBbiFiF4No0ZBkBQGKEYJzKWmtmEejNkTJ9hpmUu4won/irVzOn1pGV0Act/dXeE/oJSbnVQKnl8FTIUgq1DfpNf2YdJ1pZSYwQTGOnl05yXF+kRd53vtHfGcFx1UxYkRD+20maRi2hq7/Kx4p7rnXoQe/IsnLR2kKpCfYmdK/s01IKQ2KEYBrDvq5NbmzFLuOx/57zDRAzl2vBBePiesh5LEKOrFoep92i2tqZ0LVx+Bj1faB16EHJmjuRYP0biIZpCMJE5n+6HU//b5fVxRCkb/uksG1SXzhGth1ieavtuvUqqt4CiyK76oOc2BdeC5ZdUBpVNLTeKx2mYUXcqvMZ0b8cVkOzaQgmKa2oxUtfKl/RVlck2qpWXoFXR6KBsGq4yaj1PuQ04406r1FDC/ApQ6zKeSSsHn5i3MrFiJgVTm7VT4rT1h29sGavovR2Frd2Gr6WC1lGCCapkhknwKp3UmkzZuSXjNo4I3LunRYxoyUAGtBSPn8nR2JEGaFPzt+ByYnnoed7JdcA8ethnXBxz3aYMrSjqvMs+/GYovRW1Ccha4yaW90+RfkyEqxDYoRgEjt+tUg1KrEyvjSttBqLrdqrBb2foVZxE22IPUdW36y4WA/emD4UNw7rJCu91velR0aitgwsxIkxdmiYhmCSo+X6xTlgAaetJSFnvL1BwKFQi0NtZa21UTWdguaw7Tact+FytVz3snsuwnvfH8Y9Y7urzk9Py4RS7d8rM9GRq/aSZYRgkrfXF1tdBMX2UymLgpwheDmNkqCZl3fc/eN7Bm1XY+TQz4FVX0vGx1vsEwCPZdTMUCvo0jZiGrNFipK6zV9osE92Mv5wZR+ktvKaXg4jsbsfiSoxsmjRIuTm5iIuLg75+fnYsGGDZPqysjLMmDEDWVlZ8Pl86NGjB5YvX66qwER0cGnvDKuLYEtuGp5rynnkNHz1Og3T+PvOwZ3aSCckggjzGTn/vxrLiNDsMdnlMKmP7CSx2q2cVX75mNuvm+vDwyqKxciSJUswa9YszJs3D5s2bUJeXh7Gjx+P48eFw13X19fj0ksvxYEDB/D+++9j586deOWVV9C+fXvNhSeci1bP+kgYMW4u1V4Y6Q8idl4jh4bMmBYZ+qWttEOJdkSnfMt4dvwkeR2ScctFXVSXwyxfqF8OyRHd51ExO4hV7G4BEUPxE3rmmWdw2223Yfr06ejduzdefPFFJCQkYPHixYLpFy9ejNOnT2Pp0qUYMWIEcnNzMXr0aOTl5WkuPBE9PDLpAhxYMEny68dq+G3Egmv74cCCSRjVo53s4+U02krada/HrSIYvLFfYYyEdrAdch9J0ZxLWo4ROUhp+JePZl6EGBnTga3GHwjsF4M74MCCSUH7PCZrkZHd08w9oQNQ9Ijq6+uxceNGFBYWtmTgdqOwsBBFRUWCx3z88ccoKCjAjBkzkJGRgb59++KJJ55AU5N4gJq6ujpUVFQE/SOiDfYbPyXo2QmHWgzEvpRizG6BQ2AlqFS0IubDoTmqsWiQPXafNytOt3otlOdEFNnCT548iaamJmRkBI/nZ2RkYMeOHYLH7Nu3D6tXr8bUqVOxfPly7NmzB3fffTcaGhowb948wWPmz5+Pxx57TEnRCBvCcRzuW7IFB05V461b89Ha11IdxZwfWe7fhBo8f4f8u/e24nfvbQ3aZ7RDvFRIbzOY9/FPuLR3BhK8MVjyXTFm/yfy0uh8/FN5hy9YLbi/5yOfIr9LW/zlun7ISjY37sLpqnrc885mXNQ9DXeO7qpLnsfKazDmr2tQ26De8TeoDoo4jRyv1L4isBKM6mxf//oAXv/6gDGZE6Zj+KdTc3Mz0tPT8fLLL2Pw4MGYPHkyHn74Ybz44ouix8yZMwfl5eWBf4cOHTK6mIQFrNhWgo+2HMXWQ2X44/k1Wvz86ZPtFpVKX9btPim6T69o6WJtfazFlpGy6gb0nvsZACgWInKoa2zG2l0nMPXV9brnHYn739uKr/acxIJPd2BXqT6LHBbMX61JiADSHf+Bk1Wa8iaMQalWEx1e1VoQi1FkGUlLS4PH40FpaWnQ9tLSUmRmZgoek5WVhdjYWHg8LVOrLrjgApSUlKC+vh5eb/j0Kp/PB5/Pp6RohA3Zx2scfzhSHrSvpKLW7OJohgVzaxNvBktrX4xhZVJioTJ6uGbfCfM72VU7Whz2i09VMxlAK/TRbyous6IYijGizuo9hERDkPqj6NPJ6/Vi8ODBWLVqVWBbc3MzVq1ahYKCAsFjRowYgT179qCZZ3bftWsXsrKyBIUIEZ044eXmt6FqGlS1DSb/XK18nsDwTHoiG4LeAY9WElYvj5VZF2Y///0hFqA2CbG6WSGFEBqe5d/7SLPaWHlOVqPYjjtr1iy88sor+Mc//oHt27fjrrvuQlVVFaZPnw4AmDZtGubMmRNIf9ddd+H06dO49957sWvXLixbtgxPPPEEZsyYod9VELaEL0AiOdVJDXcQLcR43Ng8dxx+fnx8RAdW8UbQxUujY+EcCktC2iXyt1HnYJHvD54J26b0GbHi8BpNKA7mMHnyZJw4cQJz585FSUkJBgwYgBUrVgScWouLi+HmzenOycnBZ599ht/+9rfo378/2rdvj3vvvRezZ8/W7yoIW8JvHyL5N5zQ2enOkA7EovYrVFTwHYGNItSKE82NNztSJBhWhCQL5TBbLyq5ZMU+IwzcTyNQ1WrNnDkTM2fOFNy3Zs2asG0FBQX49ttv1ZyKcDDLt5UE/o4kRqyeGRKGVT2QwvOyIBJY7az1giHDCPtmC4vQPJ2ZMBxaKI+wjO3HWuLHRJrmavXMEDmw0PHrgVO/vIxC7zV49MLs+shyfy/XZ8Touh9pbSk9uXFYJ4zoZp/gayRGCCaI1FbYQYxoxcrGXGmDyHLHYzb1AqsTW4WLQX8fq+uKy+UKmkChB5EuiX/vzfIpCn3eN1/UGZ3TWplybj1wfgtP2IJIXy7+cNRMR3lkpPHnY+WqvX5YcvA0ApbECB/DHFhZrOgScBxnuSCSQqkFy2a3XzYkRgg2iNBaeG1gGTGjjVA69i2V3Kw2jeF+QBcamtgRIyx2VCyUyUifkUjXxz9zVb34MijRDvstPBEVtG0tHROjtcGr+OqNWeP1KQmxppxHC053HqxjyDLCr3Vuo9cbsBFK66BWAaVoNg09JgAkRggL6ZOdJPi3EG6d31gjTM38PI0aTgptU9c/NFb3c+h9a4zWIkM7pxp7ApPISo4T3aemvsaQGAngJD3sFEf5UEiMEJaRy3OuMrux0OrHoEZsLLvnIux74jIcWDAJ2x+fELbMuZwcQ7/wfDEekZTnMCwcvMrjErzS5RXipoJOOLBgEi4KmRkwqV8WAGtms7YzILpt2nnr4Mwx3XTJT8oy0rWdfRwbpTiwYFLgnx+hiKemxxnhf5g4SAgZib1s3wTBMJE6/j7ZyYG/41V0yoD9hzyc+E2n1yPxC1whDaFGPBtlGbHjM9T7vYmUnx3vkdWQGCFkc/JsHeZ99BNOV9XjygHZmDK0Y8Rjthwqw+SXitDMcfjrL/Jw1YD2guk2CoRwFsKpJkq5GLXGhpH3dcLCtZqOZ332xsvr9uHWkZ1llfNIWQ2uXvQ1quoaUV3fhLsu7orZE3phU/EZbDtyLu6OluvlHys1tBmpGoXul1MilnWyXDEi987rea16VW/W35NI0DANIZu739qEZT8eQ9G+U5jzwY8o2nsq4jHXvfAN6hqb0dDE4d53t2DPceHl1ov2nbL9FFAzmgKO43RsvLSVQy4HTlWrP5EMrK41JyrrsPLn0sgJAcxdug0nKutQfX5WxQtr9gIArn3+m0AaLc+Ff2ikBdqiBZfLpbuIN3LhvUjYXHOIQmKEkM2G/aeDfr+6bl/EY5pC3toN+1ssIM0h+0LTWo7S0Ou8RsIoS4PSW8Rau6Xtqz90g7ayqIXjuLC1kuRa9o6W10ZMo5eztl0dWPV3oNa/XdEzT6MsGnZ7+iRGCNWs2nFc8TFNvJf4U97aNADQyJoYkcCq4SIzfEbkXJuZT4o1i1moKAcAb4y8plToWkKFjZaaxe/XjJra69QvcyXY3XeLRUiMEMxQLyN4FNsRWINMI4bAcWyPzasldKYMy5yuqg/bxg/K19zM4T8bD2PfibNh6YSe3byPtwX91iIi+Pn7ZAokogX/OxzpCUT6blIi2EjbnYNqK2EqUqbjxibxN5y1F1ZIFHmCFqQw5rxxsepm4ShBrCH9+w0D0TsrCf+4eajhZQguz/mlAMTuqcnirEGgJ4rldfzvbzyM3723FZc8/WVYOqF6c+h0TdBvLZYHfjRYybriAEFrpU8MWUb0h8QIYSpSC96ZGVbbiKbEjMbxD1f2VnaAjjb1fu2TsfzekRjdox0TnZlVXdHhM+EOufx6/e1+ccduoT4stGPT4jOSGNcSkTc5nv3ovHbF7Nk0rH2MGQFN7SVMJdYj/lqxtMZHJIT8KrSKETm+ER3aJGg6Bx+lfi96R8GVC2s+I0+u2Bm2je8zIuWILbQvdJOWauSNceO7hwvhckkLf7buqP2IbBmJBvmgLyRGCFORtozYu4m081TKIMdHA0WHfe+QNF6eyJbqpxoElrIPFVta778R0WH5iIlYVt7e0Pt5briqQdaxcu+8nsM0ilft1e3MbEHDNISpSDW0jTayjAjBFyOsjCmrabjEHhHfsVLt1YkdJ9X/Rpr6yIJTM98yIlVcAS1iCaxZm/jIvUdy63a8AX5WfGuW0PMWqwPDuuiwjpJDpzORGCFMReo9UmMZMTTqoMKs+Q6sTTo29iO6tdUtLzmIWXhsbPgxHK+npcOTuk1CInX38eBZN1osbFJH2qUPq2ts0jU/udOulXDPJdLrB4ndaqFna9Rzscvz9kNihDAVhj/INJOdEh/4W82CcGIM76p+2mtpReQgW6GItWF6DN+oyUHsK56l8NcxEr5QfITqRULIl7twh6X9Wvm3keXXsKZBnhiRe0tiPW60beUFAKQkyHPqlcr7s/tGYYbKxQyt8ruyAyRGCNl0SQte6dO/yqgUXgkfETsjOLWX14moCYom1kFo+VL+XmZkUD5iDaYu7agD2uJemYlh26Qcs/nk5aSEbbshP3iNp2jvsKSm+KvB43bhnduHYWLfTPz7jgLN+fXMTFQtDoWOU5qTU2uHM3sKwhD8XxX+pdt7ZraOeEwr37mvvmibZqinH4NHQ+f0i8EdRPfJ8Q0J2q6yHAVdtA0zsWQBAYALspLCtnFBPgTi5fWL1NkTeuHS3hkAwsVm1IsRmU4jcgW/x+1Cj4xEvPDrweiRES4kjUD03YruRysJiRFCMVa2lbot186ynToELWPeao4VazD5naQSB0ijHEz9pTH7WWqp/v77JmUw1GJMlHYEVp+vmRhhGWEFwQ8LdopnKTS1lwjj1Nk6jF+4FhW1jbhhaEd0aBOPPy3bHpbu6z2n8PT/dmJzcRnO1jXip6Pl6JaeiEOnq3G2rlEw7z8t+xlvrT+IdbtPhu277Ll1AIBXpw3R94JsjhYxEiOzZxOb2svv6Blq04P4/uAZLP/xGC47b7EziudW7cYzK3cJ7rvlH98DAJLiYlBRK1z3V+8oxQebjwAIvsfPn1+5148ZliCWxbjcNarkilyWFgxkzcrHEiRGiDAG/+nzwN9vfHMgbD//hfrb6j1B+7YfqxDMs7zm3Dz/w2dqcPhMjWAaP7f+8/uwbQy3nYajxe+mc9tWkROF4BaZosxyQ3r3W5uw5PZhyNc4JCSFmBDhIyZEqusbcfMbLfX6THV92KrVfuw6TKPXdGG9xUNmcpzstGpuvdBwkditELo0xXFG7Fk9IkLDNIStsMuLqGpVX5EGTMt6NCO6tVV8z/gNZrAYadmupNuR00fpIXSErG2sUHw6OIT84TM1qGsU9o3QNkxjkxdEgkt6pWNUj3b4vwjTZ8UMKP57sPg3QzCudwYevuwCvYsYETHLsF2FphmQGCEInTCqmdHyoehyuTB9eGeF5xO2jGhxpI12QsWpL8Ytukq1x21Ms8wvgRY/HrFqoJcQivG48c+bh+J343pKpotkibmkVwZenjYEbWXM+tObTm2Fl20wM86I3SAxQiiG3h1rx9yNbryCxUjLdpYcAYVgIRKrGKFl88a4RddiMkr0sXt31GHs9QQ/A/907kn9I/slLbi2n+i7YqbwUGWdtRDyGSEIIgh+Q8pf2C1omCakJ7Di6y70nCw6ZXIcB5fLhYbGEDHi8aBeZJjGiIih4eUy/BSGY8Q1iA2j/GJIDsb2SkdOauSFKqXeBaH8pdbrEsxfUWr7QJYRQjFkVtTvHvRtHx6zwmr40UT5YsQjMnwTCTkpnVql/I7b9U3BUUU9buBQiB+JH59BYoTFe5zW2mt1EYIQEyMeF5Cb1kqWdVBqMpDQ4VLP26lBI4WInislCB5Gm/TV5G/lxyrfpBvL81ngRIZp9P4q1SM7FkWyf5pqXUOwFcTlconOvInXcSkB9mHroYnG2FEwRCkl1Pt3SAnbJiU4lt97kezz2h0SI4RiTlfVW3ZuJ5iXWSfIMiIytVeJZcSo7iY0XxbHyP0BvOpC/EPESvrRjBHM++awXTptiFlGlMyCkZqaPK2gU3jebheyRKYfd0sPjxjrhBlTQpAYIRRzpEw6Tghhb3J5sUnERIfaYRpnNqPi+IdpmkKiiop1KEJr1xiBEaJerzgjViIexl0ixH/IrqsGtBdNKxaE8P7x0jOHogESI4Ri7DSOmZkkP+CRVrR+sJjdUYeW9583D8U9l3TDlXnZgW1igblkBskUOKdxV8niB+NrX+0DEC7elJZVlzUKLbpBUmfVUiQjLkdsOEZuk/f0L/I0xQWKZuzTqxDMYJSDnRGsnDXKkHwd8BEYxqge7TBrXM+gBlksRoPeX8G6dLY65KE3Z6rPWUZCxZuWsi65fRimFXTC5TKmmbKA3q/Kq9OGICPJh7duzdc555bnEip05A7TqL1WFoW02dDUXiIMl0u6s/XZSPnHi5RVln+ByYKDRX3TOa0Vnry+f9isB0VaxKALC/vSZ7BFv7hnOwDh4k1LyPP8Lm2R36UtZr//g6Lj+GWQuzKuH6OsKmpyLeydgcLzKx7rjR4+I3qeVwi5KRl8HSQhMUKE0bltK+w7WYUltw/D0M6pAICSiloUzF8NIPhlOLBgEhqamlHf2IxlPx5D4QUZSG11ruO6818bseKnkqC8vTFuFD14Cb7YeQK/f29r0L4xPdth8W8uhMvlQu6Dy4y7QIZg0ekylF8OyQnbpsRnxDQsLtPeJy4DAHR9aHlgm190hFpG9IiyqqWzEYtxIu/E6g81Ay1WO/FhGmMv2qlOqUogMUKI4nK5BF+S0E2xHjdiPe6wTkusvW3b2ifankXbS8ly1FAp1PqMiCHnsVt9rxK8HlTXN4nuF+qw6s87roaKt7hY84c6XTyTpyYx4mDENIfcZklt6xUp6q49WwllkBhxIPtPVsEX40ZWchz+93MpOI7D8co6bC4uw6COKXjz22KU1dTj3rE90La1F53TWqFHRvgUMjH0+Jq3+uUyPM6I1ReoEKUaUG/LiB4S9I1vDmDGJd3w6Y8lqGtswi+H5ATEbWNTM1btOI5lPxzDTcNzMbhTG8X5SwkRMZ78dAeOV9SGrW5ttd9VQ1OE56fj8zXKgdUIrBumMTR7W0BixGFU1DZgzF/XAABen34h7vjXxqD9H24+Evj7oQ9/DPz9/p0FGJKbimPlNdh3ssrwcjphGqDd0WKFUvL4zLJoVNQ2oucjKwK/M5PjMbrHOZ+Ny//2FXaUVAIAPt56FAcWTFKU90dbjkROJEBlXWOYEAH0CWymaZhGZF0cI7DTm65GjJgx04m/lzUBpxf2mRZByOLw6ZYYIGt3nZB93H+3HgUA/KvooG5lsYM/hFGoaTDsJNCs8BlRWp92l1YG/vYLET8HTykT3As+3aEofSTiveLfgUJXqffdTo6P1TlHZyAeZ8TY85JlhMSI4+B/harpL8rOB2kCtCvw9ftPacuAh78orPTXQsUwbMYBg59CLDiwarkrd765SVF6PRevy0yKw9he6brlJxf+/frnzUMxtHMqls4YoSIf/eojax8sYhYQue+g2rci0jCQ9W+b8ZAYIQC0vGxiQa6C08rL8+RZ8bDx0fBy6QWLX01KZobK0S1mC649xysjJ+KhdGVVKb5+8BK08lk7Qp6Xk4J/31GAASZFfPWTkxqvKP1fruuHUeeH2szA74QcWhuNfgdZXwLADEiMOBg1Zn95HYeKwoSdSN1hrBgJzBxSMdp5Tg1NKsPBs0KTwulAeooRW3Q8EnVOqQ8QP6f/3DUcT17fX/axky/siH/ePNQ0Qa4mHLyR5xVMy5g1SS9IjDgMrX0kg/2eaSjpJPSe2ipFaLlOWbhQoR8lnblhVUpDxkqfX6yHxRfDuDJpCcoWCv9WpyfGBYUAYK29EZtiKzcsjNrL6ds+WeWRzoHEiINRsuy1/x3kixkj2wmrY0YIoaT9NcIyIpZjaPuoV4wIl8jfRsJa5yMXPTtnO2AL640IWob8xK7b6GHEtNY+fDtnLH74wzjB/Qy4aBkOTe11MGoWtJMjEnSJM2LxyyV0/nMNjryCRfqy1vP67NwxhCIa7M7UUihHz2EaOyBlCbJimIAfsM3w8wggFZRMrlCJ9BpnJstc1JP1l0Ul0fWGRRlKGlB/AyPHfG3Xr9tIKLKMmGjZYdFnxA6s3X1St7ysFiNCNcDIaqFHuHo5sFazxR5z57RWOuStz9Wyds/0gsSIg4lRMM7tn7oo9vHhVKcpPmFfPxKXbKZlx0laxEyDmJI4O5FwwjNQcg2/GNwBANA7K0n2MeJth30QEv5PXt8fOakJmvPWIkZYHNbWGxqmcTCKLCMBnxFzKj2Lr9aUoR3x6lf7Mbxr24hplc7G0EKkdSusYHCnNth48IystEbdqWgQyFIYOXp3ef8sdE5rhS7ttFsEpGAtho6/PPxyDc1NlX28UF1PjItBZW0jBnVUvgRBNEFixMEoec/9DRs/mJWR7YTVPiN8vvj9xQCAByb0wohuabiwc+TGx4jyi+U5sGMbdE5rhY46fJ3pxQu/HoQ/fbIdH5+P3CsXLVVK6ddhT4n1lm4f1UVDSdggxsChFJfLJTrDQ2m7IPXUWIk6PPfy3nhu9W4suLafLvnxhfLSGSPw7oZi3D6qqy55OxUSI1HA2F7peOyqPjhypgYJ3hi43edmZMS43XjqfzuxdteJQEfIRtMgjhFtl3882BvjxhiZkTHNjEDqjXFj1azRuotDLfmlJ8bhnrHdFIsRI8oiRgHPwtWlXSvsO1GFTm0TcPBUteJw6Gof954/T0RJRS0u+ssX6jKQgM3pxvbk5os64zfDcxXNQBRC6Oiu7Vrj4Um9NeUbDZAYcTD+BtQX60aHNgno0Cb8y7pnRmus3XUiMOwgz4FVh9k0CmUPayLJ7HDoWhvJSBhrBWPg6Z0vghUzk4TeOz2IccAMH7ltiRlPzeh3TAtqXiHGRsAiQmLEARwpq8ETy7ajS7tWqKxtDGxft/ucA5/U2Lrfa/6ltftw1YD2KK2oNbaw52Ghf9KC3csfDbzxzQFkJsfh1Nm6wErU/jdh74mzsvOpa2zCV3v0mZnTWkYYeLkddCzDnScfe5RSPU6/PrMgMeIAZi3ZgvX7T4dt/3bfuW37T4qvULqpuMUJ8bLn1oXsFX7NmPjS1YjWS2BhobhoR06fHbrarv+xfbDpCD7YdAT7nrgs4hfxo0u3qS1iGD4dF9yTWvmXcC52s3jIxf52PgJ7jkt/5f18rEJ037Yj5XoXJyowMxy83dHrVukxe6Yx5MF9uTvy9N9/f39Y83nvHdsdAPCnq/tqzsvPjQWdcEFWElp5PbLS6zX7SGkuejx/Kztgp3b+rKFKjCxatAi5ubmIi4tDfn4+NmzYIOu4d999Fy6XC1dffbWa0xIihDawSoiLldeQ8dHHZ8TemDm11yi0d072a6VDn1tNfZMp5/3tpT3wwx/GYWK/LN3ybO2Lwaf3jsR9hT10y1MLrMTC0MNyq7ZmG3UH2LizxqJYjCxZsgSzZs3CvHnzsGnTJuTl5WH8+PE4fvy45HEHDhzA73//e4wcOVJ1YQlhtKybocZsbL8uSH+cIEaikYam4HV9zHRoTYpTNoOHIKIJxT3RM888g9tuuw3Tp09H79698eKLLyIhIQGLFy8WPaapqQlTp07FY489hi5d7D+/nzWURFoNRUqMGGKe9PfhNve5aLJ5+fVGblXRM8iVmpxCRWS0LYBnBVJ3WG51sEuAO38pvTr6BoWfwx73QimK7lh9fT02btyIwsLClgzcbhQWFqKoqEj0uMcffxzp6em45ZZbZJ2nrq4OFRUVQf8IY/DFKB+m0QO7d+XNOllGokHTyLtGcxrY0CFNoy0jTEUYZagoTidBph8P0YIiMXLy5Ek0NTUhIyMjaHtGRgZKSkoEj/nqq6/w2muv4ZVXXpF9nvnz5yM5OTnwLycnR0kxo44mDSvK+2JN9mEOhJ0397SqkCijEZYRa8fczemprO4PG0NeFiOjmJqF7JldOlUvpgQWAwjdjptHdEZmUhxuHtFZl3Oo8YOx23My9E2srKzEjTfeiFdeeQVpaWmyj5szZw7Ky8sD/w4dOmRgKe2PFoetOBWWEZvVcUMgnxF1WH3XqkIcVq0IghYJofdZ6p1jvSpKFU92W8LeY5KkTSsviuZcgrlX6B951antr6KJ6mlpafB4PCgtLQ3aXlpaiszMzLD0e/fuxYEDB3DFFVcEtjU3n/syiYmJwc6dO9G1a3i8fp/PB5/Pp6RoUY2WtkiNZUSPd8HusUoiDdMY2UEM65IaiCGjBa2Nmp6Nopy8Qi1Hepxfi7+VUSiNYcN6zBtd9B7blyiI3SwTVqOoJ/J6vRg8eDBWrVoV2Nbc3IxVq1ahoKAgLH2vXr3w448/YsuWLYF/V155JcaMGYMtW7bQ8ItOaOnYJR1YVefqfCIN0zQ1yxs7U9Ne2dmB7XRVveB2OVdU16BhPFIEFi0jSoWsbP8lDZeq5S5JrTpt57pM6IviEH6zZs3CTTfdhCFDhmDo0KFYuHAhqqqqMH36dADAtGnT0L59e8yfPx9xcXHo2zc4yE9KSgoAhG0n1KPJMmKRA6vd6Z6eiB8lAsbVN9nwU45xqg2ICSLVUVqFUudo1odpdFnzxeTHxGC1cDyKxcjkyZNx4sQJzJ07FyUlJRgwYABWrFgRcGotLi6G2wFOYXZiRLc0LPvhmOh+qRfL9C/D8w2nlkBtLDC4UxvcOborXvxyr6AoqW9U/hXPuLVdNXpdVm2j/mJETtn6ZCfhp6PmzehTbBkxueIo7ajNnj6t9/AIa9YbpwolVaph5syZOHjwIOrq6rB+/Xrk5+cH9q1ZswZvvPGG6LFvvPEGli5dqua0hAhtW3kBAKN6tBPcL1V3+WJk67xxss6nx8ve4ADLwaT+WeidlSS4LzS4FqEdNfd03QNjMHtCL9H9coY4e2QkKj6vHzVvitJhV7np9erDlGofKcuI/DgjhNMhE4YD8DcOA3JSUHhBuqJj+Y1BcnxsyD7hJkBTw3D+YDWWAzvRYLPrs8PXVmOIgJXzxZqTmoDL+4uHYJfTr5rtbK102jjrAfj0GApzM1xBzXBUZfsJ6wMt+2hDmps5fLXnJL7ddwqje7RDbcM587XYKyH1slg1Zl7fpMzkrraUVnm019vAMsLSyjRyHpMR1qbjFXU4UlaDGLcL/yw6gHc3HMIfruyDtq28aOaAIblt8InEEKgRKB+mkZfOqg5Nj6FgBv2MCZ0hMWJDujy0PPD382v2asrL9C+O8y2i0y0jjH+smotONyMlwYuTZ4Vn46jlzjc3hm37v3c263oOpSh1YGW9rkm1MXJbH5YtI2Yj14fFbneMhmlshpTJuKS8VnHDdGHnVI0lUsfZOnNWSzUKsedwX2F3dGqbgJsvUh55kfVOxWpemDoIgzu1UXyc3foxpQ6pZvuMKHZg1SGWi92eIaEcEiM2QyryZzPHKX5prx3YHk//Ig+rfzdaMh0/X00Nw/ljfzM8V9XhrAVLC70X9xX2wJf3j0HqeadiIhgxJ2s5dM9IxH/uGh747dQOSml0X/aDnukwTGP2jBzW7ApsP2JdoGEamyE1C0Vsj9Rr5Xa7cN3gDgqP0/Cini9kz0x1MxRsPiM46hH1a2Kt8dcJNdVVaR1nxT1JTBNJ+YzI9emiYZoWnHoryDJiM6QcI0MXAXMirM8cIMIx6onZoSqocd4UsoxIXSvrlhF9ZtPoUBCCaUiM2AypGQWNzZxgo6WkqfrHzUOVF8pEWBumsTUuwT8NRc+n59QvxEaZSwkoTW/V/dJjiMXKdV6cWs9Yg8SIzTA6mFZ2clzENHr4jKiFhmnYQW4HYbV+tNuCZaHxVADpd85sg6jSuykVgVX+bBqFJ3UYoYtEOhESIzZDakqsy+USrLLqY3SoPFAKje8U6yZpIhw59UhNXXNqVVBqGZE7Fdiq+yVlGZFbJCU+I2Q9tSckRmzG0s1HRfdFw8eD0hgMRuEvRaQ2sn+HZABAeqLP2AIRjkGpsybr6zz1zgp3Vn/imn5Ijo/FwskDZOXRJ1t42YVoJC5W3uKmNjMI0mwau/Hx1iOi+1wu7V8FrFdgvZpdWfdJh3vx8o1D8PLafZhW0El7ZkQQTjVdX5GXjfvf/0F2ernWQqve7W7piXj7tnxkJLUMAd+Q3xG/ujBHtj/JX67rj+dW7cbkCzsaVUzbIFeM2A0SIzYj1iNuzGJcR5xDZSH9za3dLLCZyXGYe0VvXfNkXTCGIueZ2e25GonSzmZ417b4cLP4R4reqKl/w7umhW1T4tjatrUPj13VV/mJVcLaO8Z/P+JinDmgQWLEZkzN74hHP/pJcJ+oz4jKF0ss9oOm99QhPiOMtVWqYCm2h0dFlE6nhsVWynWDOqCVLwZ5OSmmnM/jFu4M2XgznQl/ujdZRggmSOKtrPvclIHwely4881NFpbIXFgRI34YK46p6NnJ+yQsfmLoNUxzX2F3ZKfEI8btQv8OKThTXY+s5DjUNjRjR0kFZr5t7Vo1kXC7Xbisn/jKxH58MR7UNmifemPV4prRDL/dk7KO2xkSIzZleNe2uDIvO2ibWU2ElW0R4756ijFDzMjptI2c/so/v5ivjtnhvv0UXpCB+wp7iO7vlt6aeTEil/hYD8prGjTno8cqvIQy+NO3vTRMQ0SC4zhsPVyOjCQfspLjVeXR1MyhtKIWFbUNSE3wwu124Z31xeAATOibie3HKsUPFmkjWDLHa4Wm7RGEOvRYsA4gMWIFTUGWEWfefxIjOrHx4Blc98I3gd+f/N9F6Ns+WXE+XR9aLrrvmZW7An8L9ckuuBw/bMCKZUTu1F4WcJIY5ePU6zIKn05f1CRGzKeJF3vGqev0ONPeYwH3LQk25b6ybp+h5yvadypsm951VCw/KzsBU31GTBY+TrX6GHVZDQqDgwnjzHsuxHNTBiIjyYenru+vKR/WxYjdIu7Kgb9AKt9vUAq7iXWyjOjE2dpGq4sAwAZNq8b3Q6+OTU42Tl+Uz4o2W6yjUFOU2oYmWemkBKyWR5zoi0FlHRvvvRz6ZCfj2zljNXfWUuHdCWPgLxEwNV881sqFuW3w3YEzZhRJd8gy4iBEmwiTLCay4DX+/7qF7UX5WBkSYhWxenBRtzSM7B4eVwIQt/6o8WeQOzNEFwOKQ1ArRPhPjXXLiBPhr0kmNbV33hV9zCiOIZAYcRh2MvWP7N4OfduzG+aZldDzWjElUimvf+qZmYh/Klz9WUkHd3HPdohxuzBJxnRWINjCFdoXO+MJGw+/M4wX6Qzt1PbYDblWQDtDwzQ60RTScVnhZKTPKSNn4sAhWUFCn6mT0fORhvZJcvooJbErXv/NhahrbBb0mxKCP0wT43YFjb/bpwO19qXjD80k+JwZdItlosEaRWJEJxpClv22ourYzWGJdeQ5y2q75/w+2Khu0Q71QkmcEZfLpSgKJV9wnPtI4IkR2bkIFUTLwfaibWsfFlzbD/FeD3wxJEbMZsrQjlj+4zGM65NpdVEMg8SITvDNmIA1Ht3is1/YheWO0ijHR6ejpuob6RTJfzWj4QvTKH41lBaps4pWvhh8cPcIq4thKOQzohOhHZcVUfLEOgG9+02WBYSeOH2YxsinKCbGxUSckRFY+c+RQpkTBJuQGNGJ0DbWa0mUPH3PyW+3o7EJd7gWMQ2+UBdzpjXSMtIs5cBKz5iIAOlXcyAxohOhjZrYypZG4naZ1Lg64OWUc59YW5RPLUbNphGzkIXeNv59FLM2GWmxkBxuM+yshJ1xggCx2zWQGNGJC3PbBP1O8Jrv5CU2Hm6zOinJazcNMe1cZg/TNDrUFMO/LLFLVDVMI/N29c5KQq/MRFzcs13Y8JHS2TSprbyK0hMEIQ9yYNWJtNa+oN96LEo1Nb8j8jqkIDEuBo3NHM5U12PuRz+Jpne7XObElLAA/90ce0EG/nR1XzyydJu2/GQ8HjmWET2/PkKdoM3EyK8ofrwW0VV7DSxAjMeN5feMhMsFDHh8pWHniXZYaXnsM12b4ENiRCf8HZfr/FCJ2vch1nMuDkLRnEsEV/59asVO0RDU0TJTwKwYLnIMFXq2e/WNxogROQ7HRrbf/KBjopYRgx+pmOVF6VBcdLxhBGE+JEYUUlJeiy93HcdVA9oHxTrwt2kelwuN3Dn7RG1DE5qaObTyxaCxqRmllXVobGpGK18MfjpagQ5t4sFxHLqktQ40lv581MxY8bj1XbVXrAzR0iBLfWEZYYEy2zJi1PTz0OvgD3eJdf5mTYXX1YGVPsAJQjdIjMjg5Nk6DPnT50HbZv/nR8G0/sb2uVW78dyq3bLy//Wwjrh3bA9c+OfPIyeWwO0SFiN2c2RiBbNdOEID59mVqhDLXXNzZMuIVVY9pzgpE4TdITEig9+/t1V2WjUd2JvfFuPNb4uVHxiCHu2504SLlstxSkdl2GwakZubmRwX9DvIgVXkBbFqhFHp++qMGkEYhdPaTzOh2TQy2LD/tOnnFK3UEpXd43auA6sVmG0ZWXTDILhcwMLJA8w9sU68dWs+pgzNwd1jugVtlzO11yw/oNCzkLMjIYTq1Y2pOqmGLCMyaLSJ+VysQVcbMVU0vLwD5L+cRsPsjmpS/yxc2nsivDFu3Ldki275mhV1dES3NIzolha2nS9AxMSIVXXKobOpLcH+rYIwdm3v7FZqsozIoN6CKZdqKpKRUSz5WF3J9Wgb5AzBmDG1N/QURiwjEGvB0gR8+LNpmkTuqZqZ8HpYAZUKTqvqvk37Q4KQDYkRuyHRdipZydQqWBlGkvNF3GyCBj1aVmP4OWI9wq+5Wf1bA2/KsphlJCk+1pSyhH7lkmVEP+hWElogMeIgEuNiaMxSNtosI3rd5zYmRPT0WWwZaZAxTHNFXrZZxQlCy1CcHoENCf2JsWApDkI79NRYRUE7N+vSHhjWJRXXDGovnBVDbSYrK/7KsoyYoOzM8EdqnxIePM9M+HFHJl+YAwAYmpsa2Hbv2O6i1huj0WIZMXNlbvrIkI8VK6YT2iEHVgdwz9juuGdsdwD6mkrFHVh1PIlM9G6L5QiNi7q1w67Ss0iMM+41MSPQ2RV52fjpaDmG8ASAmYzrnYHPfipFeqIPd47uioEdU5DXIQV95n0GwFrz/qxxPVQfS50em1hpCWTpw89ukBhhFNUWBBNadie8b2LxLvjcP74nOqcl4JILMgwrhxlixON24eFJvQ0/jxjjemfitpFd0D09ER63C8O7hs+4UYNWa8GWuZciJUH9MJlV1hxCGitFIlmw1ENixG5EUAJisxWIYOTcpnivBzcW5Eqm0SrMWFmp18hSuFywzCojhRYhAgCx5JvAJHr48jjhg8tu0NvkMMxe9t6usBJd1crnZReTspiTqZrya71k/jnFFt8jrMWsuDoswv+4sZvljiwjCvB63GExR0Z2T8O63ScF0999cVfkpCagX/tk/O7fW5GTGo+2rXxIbe3Fy2v3BXVEBV3aomjfqcBvte+T1kBdrqC/nftSs6LZ5AwXmQHLT1rsDlmtJ23W1kcNekfzZfndCKW2oSnwd7yX/VAPfEiMyKBT2wQcPFWNd27Px+BOqRixYDWOnI8P8a9b8gEAuQ8uCzvugQm9An9/9ttRQftmT+iF8uoG5D3+PwD6Da+Y0bfZNSIhH2YsI4yUg2V0XYlax6obzV/ggjBSlaPZYlXDEyNWT+lXir1Kazn6VnL+kHOouV70TBFeeDPM/k541VnRAGoCq7HSB5pVDlaEYyjR3OmxTDSLxDqeGLHbRyOJkQgcPlONg6eqg7bp9Yz55sSNB8/okqdQw22vKmkOrHRwZBmJDCMjWWFEc6fHMlaKRKurRF2j+UuX6AWJkQjMX74jbNvhM/qE8DZipVI9O1kjXix2wsGzUQ4rHVg9vEa70YzY9yrRd8FC/So1WUbYxCm+PGqqV2ZSnP4FMQnyGYnAsh+PKUrvcskfApDq7EVNbJGm9hrVuTHU7urRN7Hyte13XLYiSip/LaPaBnbFiJhwtEZPtrwIiT5qPllEjw5Z/QQCzacOoOZjdWjnVDx+VR90S2+tX0FMwiEa0nr+368G4NHLe6O7gkpgjGVE9yzD0VBsPWboNDH8Fa+Uv90wEPdc0g1L7hhm+rn5U//4XviswYpwPAeHv/4iD70yE/GHK/uYdlanfO0byevTL8SYnu3wp6v7WV0UXVDTP7hcLkwryNUtsKCZkLRXgFTduGrAuXVhlnxXLDs/KTOc2i5b0GdE96lu1ppJnBRLJa21D7PG9bTs/JP6ZWHvibPIy0nRlI+RdYK1ocfrB3fA9YM7aM9IAUZ8uDiNMT3TMaZnutXF0I1oe+QkRnRGSbtpRANjxsJrWtDDZ4SFS2SgCLqwaOogcBynSrCa5a0v552aM7EX5n8a7t/lFEiMRB/R9szJ+CeB0aZrI+paaFA2LZjxKqi5B04apmEBq6cAxkTw1JPjwOr0dtsO1+cUgR6KVfc+2vyjSYxIYPQ0KalOQO0LILTwGkt1OtScr8YCb8LacsyQ35m9NV30xhOh1XXQqJxq7PCVzMoMNSvR8zHZ4ZnrCYkRCVgJ1c0nkr/EkE7aOi+jv5L1GKZhqdEzur3o0s5+XvFKiWQZkfO8zfNjsqaDsMNXMkvvJcsUdGkLABjUMUVw/7OT85AcH4tXbxpiYqmsR5UYWbRoEXJzcxEXF4f8/Hxs2LBBNO0rr7yCkSNHok2bNmjTpg0KCwsl07NEQ8hwAAvtQU2EoaMnr+9veBmsEOz8U7LuF0MoI5JlRE4f57SPyMILMoJ+2+ErmUZP5dXV56cOwqOX98bL04TFxjUDO2DL3EuRf160RAuKxciSJUswa9YszJs3D5s2bUJeXh7Gjx+P48ePC6Zfs2YNpkyZgi+++AJFRUXIycnBuHHjcOTIEc2FNxo1nZ5e3aTYl16kyp7aStuy6EFlYLT9o6ilzkLMMjKpfxYA4NaRnQX3q6kFjFbpMBZNHYiPZ44I/Lbar0cOTrKMGGlpa9PKi1su6oy01j7x89vgeeuNYjHyzDPP4LbbbsP06dPRu3dvvPjii0hISMDixYsF07/11lu4++67MWDAAPTq1QuvvvoqmpubsWrVKs2FNxrHfIEzVK9DX3I175wew2f+IGMd2pgfbMwp6FWtPCJBNP4+ZSC2zh2HgR3bRC6LgY33E9eIx634+w0DkRgXg3/ePFTXc/piPOjfISXwm4ZpCKejSIzU19dj48aNKCwsbMnA7UZhYSGKiopk5VFdXY2Ghgakpor7NtTV1aGioiLonxWEDtOYig0aHzWE+oyoab/0aPTeujUfU4bm4M3zqy4rxV+E8ppGzWWJdsQsIy6XC8kJsbqeS41muSG/o+i+y/tnY+vccRjVo52GUkXGFsM0pEUIDSgSIydPnkRTUxMyMoLHMzMyMlBSUiIrj9mzZyM7OztI0IQyf/58JCcnB/7l5OQoKaZuOMYyojNWN4t6NHq5aa0w/9r+yE1rpSmfzcX6LHAYzUTyGZGDlZYDM9aocdtgqgGLDv+EfTC1ii9YsADvvvsuPvzwQ8TFia8fMGfOHJSXlwf+HTp0yMRSthA6TTYax/GEsPo26LtwmjYm9s20ugi2J9JsGjkkeD2RE+mAkrqvh8hqOS/7bQ8N01jfNtoZRRFY09LS4PF4UFpaGrS9tLQUmZnSjfJf//pXLFiwAJ9//jn695ee8eHz+eDziTv3mEWjCqWvV0fJRqVmohBhsBQOPt5rfhBjq8Px640enbbc52DmvfPquKCMPXxGrC4BYWcUvS1erxeDBw8Ocj71O6MWFBSIHvfkk0/ij3/8I1asWIEhQ+wzd7qR4ehaTlkxVJUDK0ONXvsU+y7ZzQqxKjttvvBvZZJlRAmxHmWVW8q6Yw+fEYZeTB1xmvhnFcU92qxZs3DTTTdhyJAhGDp0KBYuXIiqqipMnz4dADBt2jS0b98e8+fPBwD85S9/wdy5c/H2228jNzc34FvSunVrtG7NdkCnBhGfEV+M2/DorGKNz/t3FmDBpztMXTE0FLkvp9sVWThY5cCqF9cN6oBtRypQ0DW6YgLoiR6WkdEGO5CqIUGB1eyFqYMwqJP4rCE7iBGGXkvNqL3dTroHZqNYjEyePBknTpzA3LlzUVJSggEDBmDFihUBp9bi4mK4ed5WL7zwAurr63H99dcH5TNv3jz84Q9/0FZ6g2kUmU3TOa0VdpRU6nKO124agh8Ol+P/rdodtF2sfR6Sm4r37xquy7mNxu1yhQkHPb4yWPIZifG48cer+1pdDEvQq3/srNKJmO9HEeNx43eX9sDTK3dFOEbVqVQxWEJchDKxX5bkfjuIEZaGTwn7ocrWP3PmTMycOVNw35o1a4J+HzhwQM0pmCDMgfX8/8O7pmFHSSV6ZiRqPsfYCzIw9oIMATGio/ObyrRiRZBbtHPXENxA6WHVUBL0zLg2nBperSydMQIl5bXoofI9ChWlrPSFn947Eu9vPIz/u6Sbbnnaw2eEkQdA2BJnOB4YhNgwzQMTeiIjyYcJAjMpYnSag2eHL6FICF1CqJCQe5n8o1jpdMxAqd+BnRiQkwLoOGuflci8F2Ql4dHLe+uap54zc4yCLCOsTDywJzaYvW4d/916VHB7XKwHd4zuik5tw83LsTH61EYbtD0RERJUevQXl58PE94xNUF7Zoyjl7iNBuQM39n1tbLD1F41sw8Jwg9ZRiT45Idjio+Jj9XHq59ly4jcoim9BLkN7vCuaVj521FoHwWh3J1sGdEbOV/mdu0udZwlTBBMQlVcAXIEwvxr+6Fdog+PaZztoqcWUftVpbUIcu4X/2NWiWNq94xERbMV7EoMw2KEtSmPTv4wZ/njhCD0wPmtuY7Ey4hl0C09ERseGqvZrMq2WVb+1F5CG0IxOFhxFLT6+YbeBTn3xa5V0g5ixOWiqa3Rfv1aIMuIAuSGnFYjJH4/rofiY8xEzVdwRlJLQLDeWUkAgCEh0x1t0MZailCo9Or6JgtKEg5/TZbQBRD5mPWMnbw2ih4h843mnduGIbdtAv51i74rGFuBnFmFhL6QZUQBvhjjtNvMS7ojJzUB9767RXNe7VPicaSsRnuhNPLCrwfj4Q9/xD1ju6N7emu8t/EwfjnEmkUP9casBZ2FFmGrrmdjpWAPr5WW+iI06msx9M70yIw8RZhti2M4d1/cFZ9uK8G04blWFyUiw7q0xZr7x1hdDMKmkBiR4MLcNvjuQMuqrEZPr9Or0f581mgcr6zF6KfWANBf2cvNr1t6ayy5o2WZgBlj9Iu7YDUNJqkRj8DNNjr6r1z4QsmKoaPQM143qAMqahrwz6KDKD5dLXiM1slJZkuZByb0wgMTepl8VkItNtO6TEHDNBLEhcyMMVqM6NWgx3s9gtOOlWLGVyT/ku301SoWg0ZvhHwFGk06dyT4rwMLMSY8bhduHdkFAzumiKaxg+8FQUQjJEYkCI3AanTMBwbac0thKcx7JBpMsk4IDdOE1kur4Itzs4at+IhVF6n3lMQIQbBJVA/TbC4+g2ue/yZoW1prH1adH+b4dt/poH12sYyEoigcvMlttV37hnqTBIHQzF5WgkvxO3ap6KdmP2Op2Cx2rW8E4XSi2jISKkQA4OTZOvx5+c+49Nm1YfuM9xkxppPRO9dobs8n9Dm3BMCtF3U25XxCda6gy7kVgtNa+0wpgxj8skkN0xg1rCQmLC7vnw3gnCN3KCxZRmwwQSZ64T0bJc2yjYy7zBHVlhExdgqsyHt5/ywTLCOGZq8YaivDeX7qIJysqkN6YlzkxDog5EfzxDX90Ds7CVcNyDalDGLwnWulrHpGOdyKnfKi7mlYfs9IdGwbvlwASwJAaFVrgg34kbTrGuVPpafnqR4SIwKEmsHfujUfI7qlGX5eqsjs43a7TBMiANAlLdwROTkhlomZSXx/FqllEGobzI+L0js7SXC7VsuInpYVt9vF3hcIAQBoxYvurERMUxOuHhIjAvx0tCLot1AUTCMwKx6D5vwM+rqUmk1jJ+dWPXjvzgLsKq3EcBNEsBaeuKYfymrqkSOxaKHUPrPROmNLT8sKS1YaIhi324UpQ3NQUl6L7umtZR/HysrRdoTEiAzMWh8k2jrcUKSuf+mWo1j4q4EmlsZaLsxNxYW5qVYXIyI35HcU3ff+nQXYcqgM43pnGHJuNYsIahUAek4/Z8l/hQhn/rX9FR/DwhR3uxLVDqxyiTVpGXfW6rFYW2nFAmltEmJNPyehjSG5qbh1ZBfD4seosVi28mn7/tKzKZh/bT8AwKxL2V4KgpAPiRH1RLVlpGdGInaWhjurhmKWZYR8RsS5Ms9aZ02CPbwqlmdYcG0/3P6vjbj74q6qzqmnNeOqAe0xplc6kuJIaDsFEiPqiWox0i2jtSwxosYcrAaj6rHeX6ZWWJfN8tsh7MOAnBQkxcUo8knp0q41Pp81WvU59a76JEScBX1QqieqxYhcPCYN09jFZ8SKse4YEiNECHGxHnz/yKWGT7nnQ34ehBSsBCS0I1EtRuQ2K2a1daypajHfEKMsRVIWHDssoU6Yj5qhGi2QFiGk0OqTFM1E9edm/vlIlpEwy2GzoIsx0zgVhYPnpRYzCLX2GWNalrIMCa3RQhBmQ5YRQog/X9MX43pn4JdDOlhdFNsS1TLuhqEd8dyq3ThRWSeZLjvFnCBX/Tok45P/uwhZyfqeT60DLt/8zfGCyrfyiQe4MgqPjp2AWT5AhPMgMUIIMTW/E6bmd7K6GLYmqsWIx+3Cdw8XormZg9vtwgtr9uIvK3YE9l89IBt//UWeqf4Kfdsn656n2tWG+QKA7yUeJxFt0yj0NIyQMyyhFtIiBGEM1CqjZQgg1C/BG+N2hOOkEssI3wLCHxrhL3bmM3mcPrQsWiH/E0ItZBkhCGOwf0+rI6Gdtple+kai5Dr4zuD8Tru+qWV9Bp8FlhE9nwVZRgi1mDSxjiCiDnq1eBSfrg76XVVn/gJfRqAkgmxTc4vo4Fsj+Hl4DerMpWbT6OEz4i/39YPJyYxQB1lGCMIYotpnJJTK2sag35EcW+2CEqsCzwASJAA6tk3A9BG5SIn3GmYxMno2zT9vGYqKmgZc3DNdc15EdGJUaHuCiHZIjPAI/fp2ikk23it/WIXvqBoqOuZd0QcAsGp7qT4Fk0laax+uH6TdmpGSEIthMqdzE4QQNBGLIIyBxAiPUPFhxYJwRpDXIUV2Wr4YETNJ62mqjpRT+5R4rHtgjC6WETKxE1qhOkQQxkBihEeoCdbuix59NGMElm87hnvHdpd9DN9RVSweh57tcSSR4XLpN5MmMY6qO6ENCr5HEMZArTOPUJcFu38E5eWkIC8nRdEx9Y0tYkRsfFzPr8NIjqla/VNcLhee/kUeztY1Iis5XlNeBEFahCCMgcQIjzE92+GdDcWB34wtFWMKAzumoE1CrORKqENy2yDB60HXdq01n48vNoTEjx7C5zqaPUPohFOm+xMEa5AY4XFp7wyri2A5cbEerH+oUDIwWII3BpvnXqpoyrAYQSHnBdSf3a1ThLMgnxGCMAYSIzxCv8yjtd2RsxKqL0afwGeRvjSp8SdYguojQRiDQyavGkM0DtOYTYc24sNBAI3RE2xB9ZEgjIHEiAS9s5OsLoLjmT2hp+R++hIlWGBEt3PxaaYV5FpbEIJwKDRME8IVedn479ajAID7x0t3lIR2UhK8kvsp4iXBAv+YPhTHymslHbsJglAPiZEQ/jZlIJ771QDqBC1AeDaNBQUhiBBiPG4SIgRhIDRMIwAJEWsQmk1DUykJgiCcD4kRgmlIGBIEQTgfEiME05AUIQiCcD4kRgimoWEagiAI50NihGAGcmAlCIKITkiMEExDcUYIgiCcD4kRghmEZtOQGCEIgnA+JEYIptFhLT6CIAiCcaipJ5iGLCMEQRDOh8QIwTQUZ4QgCML5kBghmEFIeHhIixAEQTgeEiME09AwDUEQhPMhMUIwg9Bsmr7tky0oCUEQBGEmtGovwSTL7xmJ1TtKcevILlYXhSAIgjAYEiMEk/TOTkLv7CSri0EQBEGYAA3TEARBEARhKSRGCGagabwEQRDRiSoxsmjRIuTm5iIuLg75+fnYsGGDZPr33nsPvXr1QlxcHPr164fly5erKixBEARBEM5DsRhZsmQJZs2ahXnz5mHTpk3Iy8vD+PHjcfz4ccH033zzDaZMmYJbbrkFmzdvxtVXX42rr74a27Zt01x4wlk0NYfPpiEIgiCcj4sTmk8pQX5+Pi688EL8/e9/BwA0NzcjJycH//d//4cHH3wwLP3kyZNRVVWFTz75JLBt2LBhGDBgAF588UVZ56yoqEBycjLKy8uRlEROjU4j98FlAIBhXVLx7u0FFpeGIAiC0Au5/bciy0h9fT02btyIwsLClgzcbhQWFqKoqEjwmKKioqD0ADB+/HjR9ABQV1eHioqKoH+E86ltaLa6CARBEIQFKBIjJ0+eRFNTEzIyMoK2Z2RkoKSkRPCYkpISRekBYP78+UhOTg78y8nJUVJMwmYk+s7NMB/Wpa3FJSEIgiCsgMnZNHPmzEF5eXng36FDh6wuEmEgn943En+8ui/uK+xudVEIgiAIC1AU9CwtLQ0ejwelpaVB20tLS5GZmSl4TGZmpqL0AODz+eDz+ZQUjbAxHdok4MZhnawuBkEQBGERiiwjXq8XgwcPxqpVqwLbmpubsWrVKhQUCDseFhQUBKUHgJUrV4qmJwiCIAgiulAcDn7WrFm46aabMGTIEAwdOhQLFy5EVVUVpk+fDgCYNm0a2rdvj/nz5wMA7r33XowePRpPP/00Jk2ahHfffRfff/89Xn75ZX2vhCAIgiAIW6JYjEyePBknTpzA3LlzUVJSggEDBmDFihUBJ9Xi4mK43S0Gl+HDh+Ptt9/GI488goceegjdu3fH0qVL0bdvX/2ugiAIgiAI26I4zogVUJwRgiAIgrAfhsQZIQiCIAiC0BsSIwRBEARBWAqJEYIgCIIgLIXECEEQBEEQlkJihCAIgiAISyExQhAEQRCEpZAYIQiCIAjCUkiMEARBEARhKSRGCIIgCIKwFMXh4K3AHyS2oqLC4pIQBEEQBCEXf78dKdi7LcRIZWUlACAnJ8fikhAEQRAEoZTKykokJyeL7rfF2jTNzc04evQoEhMT4XK5dMu3oqICOTk5OHTokGPXvHH6NdL12R+nXyNdn/1x+jUaeX0cx6GyshLZ2dlBi+iGYgvLiNvtRocOHQzLPykpyZEVjI/Tr5Guz/44/Rrp+uyP06/RqOuTsoj4IQdWgiAIgiAshcQIQRAEQRCWEtVixOfzYd68efD5fFYXxTCcfo10ffbH6ddI12d/nH6NLFyfLRxYCYIgCIJwLlFtGSEIgiAIwnpIjBAEQRAEYSkkRgiCIAiCsBQSIwRBEARBWEpUi5FFixYhNzcXcXFxyM/Px4YNG6wuUhjz58/HhRdeiMTERKSnp+Pqq6/Gzp07g9JcfPHFcLlcQf/uvPPOoDTFxcWYNGkSEhISkJ6ejvvvvx+NjY1BadasWYNBgwbB5/OhW7dueOONN4y+PADAH/7wh7Dy9+rVK7C/trYWM2bMQNu2bdG6dWtcd911KC0tDcqD5evLzc0Nuz6Xy4UZM2YAsN/zW7t2La644gpkZ2fD5XJh6dKlQfs5jsPcuXORlZWF+Ph4FBYWYvfu3UFpTp8+jalTpyIpKQkpKSm45ZZbcPbs2aA0P/zwA0aOHIm4uDjk5OTgySefDCvLe++9h169eiEuLg79+vXD8uXLDb/GhoYGzJ49G/369UOrVq2QnZ2NadOm4ejRo0F5CD33BQsWMHGNkZ7hb37zm7CyT5gwISgNy88w0vUJvY8ulwtPPfVUIA3Lz09Ov2Bmu6lLX8pFKe+++y7n9Xq5xYsXcz/99BN32223cSkpKVxpaanVRQti/Pjx3Ouvv85t27aN27JlC3fZZZdxHTt25M6ePRtIM3r0aO62227jjh07FvhXXl4e2N/Y2Mj17duXKyws5DZv3swtX76cS0tL4+bMmRNIs2/fPi4hIYGbNWsW9/PPP3N/+9vfOI/Hw61YscLwa5w3bx7Xp0+foPKfOHEisP/OO+/kcnJyuFWrVnHff/89N2zYMG748OG2ub7jx48HXdvKlSs5ANwXX3zBcZz9nt/y5cu5hx9+mPvggw84ANyHH34YtH/BggVccnIyt3TpUm7r1q3clVdeyXXu3JmrqakJpJkwYQKXl5fHffvtt9y6deu4bt26cVOmTAnsLy8v5zIyMripU6dy27Zt49555x0uPj6ee+mllwJpvv76a87j8XBPPvkk9/PPP3OPPPIIFxsby/3444+GXmNZWRlXWFjILVmyhNuxYwdXVFTEDR06lBs8eHBQHp06deIef/zxoOfKf2+tvMZIz/Cmm27iJkyYEFT206dPB6Vh+RlGuj7+dR07doxbvHgx53K5uL179wbSsPz85PQLZrWbevWlUStGhg4dys2YMSPwu6mpicvOzubmz59vYakic/z4cQ4A9+WXXwa2jR49mrv33ntFj1m+fDnndru5kpKSwLYXXniBS0pK4urq6jiO47gHHniA69OnT9BxkydP5saPH6/vBQgwb948Li8vT3BfWVkZFxsby7333nuBbdu3b+cAcEVFRRzHsX99odx7771c165duebmZo7j7P38Qhv65uZmLjMzk3vqqacC28rKyjifz8e98847HMdx3M8//8wB4L777rtAmk8//ZRzuVzckSNH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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.plot(train[:, 4])" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABz0UlEQVR4nO3dd3wUdf4/8NduyiaBkACBhBIIHREITTAgIBopctgV0RNFxYoNTwELWH4KdyrnfT2Us6DeWbCc5e5AVBBEBaUjSJEeWhJaGiF9fn/ALrO7M7NTd2Y3r+fj4R3ZnZ35zO6U93zK++MSBEEAERERkU3cdheAiIiI6jcGI0RERGQrBiNERERkKwYjREREZCsGI0RERGQrBiNERERkKwYjREREZCsGI0RERGSrWLsLoEZdXR0OHTqE5ORkuFwuu4tDREREKgiCgNLSUrRs2RJut3z9R0QEI4cOHUJmZqbdxSAiIiId9u/fj9atW8u+HxHBSHJyMoDTO9OoUSObS0NERERqlJSUIDMz03cflxMRwYi3aaZRo0YMRoiIiCJMqC4W7MBKREREtmIwQkRERLZiMEJERES2YjBCREREtmIwQkRERLZiMEJERES2YjBCREREtmIwQkRERLZiMEJERES20hyMLF++HGPGjEHLli3hcrnwxRdfhPzMsmXL0KdPH3g8HnTs2BHvvPOOjqISERFRNNIcjJw8eRLZ2dmYM2eOquX37NmD0aNHY9iwYdiwYQMefPBB3H777fj66681F5aIiIiij+a5aUaNGoVRo0apXn7u3Llo164dXnrpJQDAOeecgx9//BF//etfMWLECK2bJyIioihj+UR5K1euRG5urt9rI0aMwIMPPij7mcrKSlRWVvr+Likpsap4RGFXVyfgrR/34HBxBYrKq3BBpzRc1Ud+am0iomhneTCSn5+P9PR0v9fS09NRUlKCU6dOITExMegzM2fOxNNPP2110YhssWRbIZ5buNX392frD+K8rCbIbJJkY6mIiOzjyNE006ZNQ3Fxse+//fv3210kItNszw+u6TtaVimxJBFR/WB5zUhGRgYKCgr8XisoKECjRo0ka0UAwOPxwOPxWF00IlsIQvBrcTGOfC4gIgoLy6+AOTk5WLJkid9r3377LXJycqzeNJEjud2uoNdiJF4jIqovNAcjZWVl2LBhAzZs2ADg9NDd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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.plot(train[:, 4])" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "np.save(f'../processed/{DATASET}_test5/labels.npy', labels5)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py3.9test", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/subject1-4/AdaDiff/data/split_val_swat.ipynb b/subject1-4/AdaDiff/data/split_val_swat.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..13168f57b690953f47501c09e65a6617c218106f --- /dev/null +++ b/subject1-4/AdaDiff/data/split_val_swat.ipynb @@ -0,0 +1,1863 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#normal0 = pd.read_excel('SWaT_Dataset_Normal_v0.xlsx')\n", + "normal1 = pd.read_excel('SWaT_Dataset_Normal_v1.xlsx')\n", + "attack = pd.read_excel('SWaT_Dataset_Attack_v0.xlsx')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "#if not os.path.isfile(directory + 'train.csv'):\n", + "#normal0.to_csv('train.csv', index=None, header=None)\n", + "normal1.to_csv('train1.csv', index=None, header=None)\n", + "attack.to_csv('test.csv', index=None, header=None)" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [], + "source": [ + "test = pd.read_csv('test.csv')\n", + "train1 = pd.read_csv('train1.csv')\n", + "#train = pd.read_csv('train.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(496800, 53)" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(495000, 53)" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train1.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(449919, 53)" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [], + "source": [ + "train1['Normal/Attack'] = [0 if x == 'Normal' else 1 for x in train1['Normal/Attack']]\n", + "test['Normal/Attack'] = [0 if x == 'Normal' else 1 for x in test['Normal/Attack']]" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 395298\n", + "1 54621\n", + "Name: Normal/Attack, dtype: int64" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test['Normal/Attack'].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 495000\n", + "Name: Normal/Attack, dtype: int64" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train1['Normal/Attack'].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "# trim column names\n", + "train1 = train1.rename(columns=lambda x: x.strip())\n", + "test = test.rename(columns=lambda x: x.strip())" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 0\n", + "1 0\n", + "2 0\n", + "3 0\n", + "4 0\n", + " ..\n", + "449914 0\n", + "449915 0\n", + "449916 0\n", + "449917 0\n", + "449918 0\n", + "Name: Normal/Attack, Length: 449919, dtype: int64" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_labels = test['Normal/Attack']\n", + "test_labels" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [], + "source": [ + "def search_ratio(test_labels, val_len):\n", + " test = test_labels[val_len:]\n", + " val = test_labels[:val_len]\n", + " test_ratio = (np.sum(test) /test.shape[0]) * 100\n", + " val_ratio = (np.sum(val) / val.shape[0]) * 100\n", + " print(f'val ratio: {val_ratio}')\n", + " print(f'test ratio: {test_ratio}')\n", + " print('----')\n", + " return val_ratio, test_ratio" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "val ratio: 5.764422168631852\n", + "test ratio: 13.734108285917495\n", + "----\n", + "val ratio: 9.926429730390522\n", + "test ratio: 12.386152599968389\n", + "----\n", + "val ratio: 6.432302278199667\n", + "test ratio: 14.586402662060557\n", + "----\n" + ] + } + ], + "source": [ + "vr, tr = search_ratio(test_labels=test_labels.to_numpy(), val_len=int(0.2 * test.shape[0]))\n", + "vr, tr = search_ratio(test_labels=test_labels.to_numpy(), val_len=int(0.1 * test.shape[0]))\n", + "vr, tr = search_ratio(test_labels=test_labels.to_numpy(), val_len=int(0.3 * test.shape[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "44991" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "val_len = int(0.1 * test.shape[0])\n", + "val_len" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(44991, 53)" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation = test[:val_len]\n", + "validation.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 40525\n", + "1 4466\n", + "Name: Normal/Attack, dtype: int64" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation['Normal/Attack'].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 0\n", + "1 0\n", + "2 0\n", + "3 0\n", + "4 0\n", + " ..\n", + "44986 0\n", + "44987 0\n", + "44988 0\n", + "44989 0\n", + "44990 0\n", + "Name: Normal/Attack, Length: 44991, dtype: int64" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_labels = validation['Normal/Attack']\n", + "validation_labels" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(404928, 53)" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_clipped = test[val_len:]\n", + "test_clipped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(404928,)" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_labels_clipped = test_labels[val_len:]\n", + "test_labels_clipped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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JwBQHtx+++10+umnU0NDAzU0NNCyZcvo4Ycfzp7HHAGA0UVcn4A5AgCjmy996UuUyWTo+uuvzx7DXAGMOB4AjvzsZz/zampqvDvuuMNbu3at9+EPf9hramry2tvbC501AEAe3HLLLd4pp5zi7d27N/vvwIED2fMf+chHvJkzZ3qPPfaYt3LlSu+8887zzj///Oz5gYEB79RTT/UuueQS7+WXX/Yeeughb9KkSd7NN9+c/c3WrVu9sWPHejfccIO3bt0677//+7+9yspK73e/+132N+hTACgMDz30kPeZz3zGu++++zwi8u6///7A+S996UteY2Oj98ADD3ivvPKK9+d//ufe3LlzvWPHjmV/c9lll3lLly71nn32We9Pf/qTt2DBAu9973tf9nx3d7fX0tLivf/97/fWrFnj3X333d6YMWO87373u9nfPPPMM15lZaX35S9/2Vu3bp332c9+1quurvZWr16dV14AAG7E9QnXXnutd9lllwXmDYcOHQr8Bn0CAOXBpZde6v3v//6vt2bNGm/VqlXeFVdc4c2aNcs7fPhw9jfFtFaIywsAwI1c+oSLLrrI+/CHPxyYJ3R3d2fPo08AoHz49a9/7T344IPexo0bvQ0bNnif/vSnverqam/NmjWe52GOAMBoI65PwBwBgNHL888/782ZM8c7/fTTvY9//OPZ45grgJEGBlLAmXPOOce77rrrst8HBwe9adOmebfddlsBcwUAyJdbbrnFW7p0qfFcV1eXV11d7f3iF7/IHnvttdc8IvJWrFjheZ54kVpRUeHt27cv+5vbb7/da2ho8Pr6+jzP87wbb7zRO+WUUwKy3/Oe93iXXnpp9jv6FAAKj24MMTQ05LW2tnr/8R//kT3W1dXl1dbWenfffbfneZ63bt06j4i8F154Ifubhx9+2MtkMt7u3bs9z/O8b3/7215zc3O2T/A8z7vpppu8xYsXZ7+/+93v9q688spAfs4991zv7//+73POCwCAF5uB1Nvf/nbrNegTAChf9u/f7xGR9+STT3qeV1xrhVzyAgDgRe8TPE+8/FRfeuigTwCgvGlubvZ+8IMfYI4AAPA8z+8TPA9zBABGK729vd7ChQu9Rx99NNAPYK4ACgG22ANOnDhxgl588UW65JJLsscqKirokksuoRUrVhQwZwCAJGzatImmTZtG8+bNo/e///3U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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.rcParams['figure.figsize'] = 30, 2\n", + "plt.plot(test_clipped.to_numpy()[:, 4])\n", + "plt.fill_between(np.arange(test_labels_clipped.to_numpy().shape[0]), test_labels_clipped.to_numpy(), color='red', alpha=0.7, linestyle='dashed', linewidth=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(validation.to_numpy()[:, 4])\n", + "plt.fill_between(np.arange(validation_labels.to_numpy().shape[0]), validation_labels.to_numpy(), color='red', alpha=0.7, linestyle='dashed', linewidth=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAACVoAAADFCAYAAACv1dsRAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAnVElEQVR4nO3dbXCV5ZkH8CsBk+DiSUQgIRIQfMOqwIoaM1VbasYsMo4vtOtSdovvy250hLRVmVqx/YJjt75U8WXHrllrWcWt0lEsLoLA0AbUaBatygimxgIJaksCUQKSZz90PPUsyOFQaQL+fjPPDM99X8/9XCcfuOfM/Oc+eUmSJAEAAAAAAAAAAMBnyu/pBgAAAAAAAAAAAHo7QSsAAAAAAAAAAIAsBK0AAAAAAAAAAACyELQCAAAAAAAAAADIQtAKAAAAAAAAAAAgC0ErAAAAAAAAAACALAStAAAAAAAAAAAAsujb0w38tXR3d8eGDRvisMMOi7y8vJ5uBwAAAAAAAAAA6GFJksSWLVuivLw88vP3fGbVFyZotWHDhqioqOjpNgAAAAAAAAAAgF7m3XffjaFDh+6x5gsTtDrssMMi4k9/lFQq1cPdAAAAAAAAAAAAPa2joyMqKirS2aI9+cIErT75ucBUKiVoBQAAAAAAAAAApH2SLdqTPf+wIAAAAAAAAAAAALkHrZYvXx7nn39+lJeXR15eXsyfPz/rM0uXLo1TTjklCgsL45hjjon6+vqM+VtuuSXy8vIyrlGjRmXUbNu2LWpra+OII46I/v37x6RJk6KtrS3X9gEAAAAAAAAAAHKWc9Cqs7MzxowZE3PmzNmr+ubm5pg4cWKMHz8+mpqaYvr06XHllVfGs88+m1F34oknxsaNG9PXihUrMuZnzJgRTz31VDz++OOxbNmy2LBhQ1x88cW5tg8AAAAAAAAAAJCzvrk+MGHChJgwYcJe199///0xYsSI+PGPfxwRESeccEKsWLEi7rjjjqipqflzI337RllZ2W7XaG9vj5/+9Kcxd+7c+NrXvhYREQ899FCccMIJsXLlyjjjjDN2eaarqyu6urrS9x0dHXvdMwAAAAAAAAAAwKflfKJVrhoaGqK6ujpjrKamJhoaGjLG3nrrrSgvL4+RI0fGlClToqWlJT3X2NgYO3bsyFhn1KhRMWzYsF3W+cTs2bOjuLg4fVVUVHyOnwoAAAAAAAAAAPgi2e9Bq9bW1igtLc0YKy0tjY6Ojvjoo48iIqKysjLq6+tj4cKFcd9990Vzc3OcddZZsWXLlvQaBQUFUVJSsss6ra2tu33vzJkzo729PX29++67n/+HAwAAAAAAAAAAvhBy/unA/eHTP0U4evToqKysjOHDh8e8efPiiiuu2Kc1CwsLo7Cw8PNqEQAAAAAAAAAA+ALb7ydalZWVRVtbW8ZYW1tbpFKp6Nev326fKSkpieOOOy7Wrl2bXmP79u2xefPmXdYpKyvbL30DAAAAAAAAAAB8Yr8HraqqqmLx4sUZY4sWLYqqqqrPfGbr1q2xbt26GDJkSEREjBs3Lg455JCMddasWRMtLS17XAcAAAAAAAAAAODzkPNPB27dujV90lRERHNzczQ1NcWAAQNi2LBhMXPmzFi/fn08/PDDERExbdq0uOeee+L666+Pyy+/PJYsWRLz5s2LBQsWpNf4zne+E+eff34MHz48NmzYELNmzYo+ffrE5MmTIyKiuLg4rrjiiqirq4sBAwZEKpWKa6+9NqqqquKMM874S/8GAAAAAAAAAAAAe5Rz0Oqll16K8ePHp+/r6uoiImLq1KlRX18fGzdujJaWlvT8iBEjYsGCBTFjxoy46667YujQofHggw9GTU1Nuub3v/99TJ48OT744IMYNGhQnHnmmbFy5coYNGhQuuaOO+6I/Pz8mDRpUnR1dUVNTU3ce++9+/ShAQAAAAAAAAAAcpGXJEnS0038NXR0dERxcXG0t7dHKpXq6XYAAAAAAAAAAIAelkumKP+v1BMAAAAAAAAAAMABS9AKAAAAAAAAAAAgC0ErAAAAAAAAAACALAStAAAAAAAAAAAAshC0AgAAAAAAAAAAyELQCgAAAAAAAAAAIAtBKwAAAAAAAAAAgCwErQAAAAAAAAAAALIQtAIAAAAAAAAAAMhC0AoAAAAAAAAAACALQSsAAAAAAAAAAIAsBK0AAAAAAAAAAACyELQCAAAAAAAAAADIQtAKAAAAAAAAAAAgC0ErAAAAAAAAAACALAStAAAAAAAAAAAAshC0AgAAAAAAAAAAyELQCgAAAAAAAAAAIAtBKwAAAAAAAAAAgCwErQAAAAAAAAAAALIQtAIAAAAAAAAAAMhC0AoAAAAAAAAAACALQSsAAAAAAAAAAIAscg5aLV++PM4///woLy+PvLy8mD9/ftZnli5dGqecckoUFhbGMcccE/X19Rnzs2fPjtNOOy0OO+ywGDx4cFx44YWxZs2ajJqvfvWrkZeXl3FNmzYt1/YBAAAAAAAAAABylnPQqrOzM8aMGRNz5szZq/rm5uaYOHFijB8/PpqammL69Olx5ZVXxrPPPpuuWbZsWdTW1sbKlStj0aJFsWPHjjj33HOjs7MzY62rrroqNm7cmL5uu+22XNsHAAAAAAAAAADIWd9cH5gwYUJMmDBhr+vvv//+GDFiRPz4xz+OiIgTTjghVqxYEXfccUfU1NRERMTChQsznqmvr4/BgwdHY2NjnH322enxQw89NMrKynJtGQAAAAAAAAAA4C+S84lWuWpoaIjq6uqMsZqammhoaPjMZ9rb2yMiYsCAARnjP//5z2PgwIFx0kknxcyZM+PDDz/8zDW6urqio6Mj4wIAAAAAAAAAANgXOZ9olavW1tYoLS3NGCstLY2Ojo746KOPol+/fhlz3d3dMX369Pjyl78cJ510Unr8m9/8ZgwfPjzKy8tj9erVccMNN8SaNWviiSee2O17Z8+eHT/4wQ8+/w8EAAAAAAAAAAB84ez3oFWuamtr47XXXosVK1ZkjF999dXpf5988skxZMiQOOecc2LdunVx9NFH77LOzJkzo66uLn3f0dERFRUV+69xAAAAAAAAAADgoLXfg1ZlZWXR1taWMdbW1hapVGqX06yuueaaePrpp2P58uUxdOjQPa5bWVkZERFr167dbdCqsLAwCgsL/8LuAQAAAAAAAAAAIvL39wuqqqpi8eLFGWOLFi2Kqqqq9H2SJHHNNdfEk08+GUuWLIkRI0ZkXbepqSkiIoYMGfK59gsAAAAAAAAAAPD/5Xyi1datW2Pt2rXp++bm5mhqaooBAwbEsGHDYubMmbF+/fp4+OGHIyJi2rRpcc8998T1118fl19+eSxZsiTmzZsXCxYsSK9RW1sbc+fOjV/+8pdx2GGHRWtra0REFBcXR79+/WLdunUxd+7cOO+88+KII46I1atXx4wZM+Lss8+O0aNH/6V/AwAAAAAAAAAAgD3KS5IkyeWBpUuXxvjx43cZnzp1atTX18ell14av/vd72Lp0qUZz8yYMSNef/31GDp0aHz/+9+PSy+99M9N5OXt9l0PPfRQXHrppfHuu+/GP/7jP8Zrr70WnZ2dUVFRERdddFHcdNNNkUql9qrvjo6OKC4ujvb29r1+BgAAAAAAAAAAOHjlkinKOWh1oBK0AgAAAAAAAAAAPi2XTFH+X6knAAAAAAAAAACAA5agFQAAAAAAAAAAQBaCVgAAAAAAAAAAAFkIWgEAAAAAAAAAAGQhaAUAAAAAAAAAAJCFoBUAAAAAAAAAAEAWglYAAAAAAAAAAABZCFoBAAAAAAAAAABkIWgFAAAAAAAAAACQhaAVAAAAAAAAAABAFoJWAAAAAAAAAAAAWQhaAQAAAAAAAAAAZCFoBQAAAAAAAAAAkIWgFQAAAAAAAAAAQBaCVgAAAAAAAAAAAFkIWgEAAAAAAAAAAGQhaAUAAAAAAAAAAJCFoBUAAAAAAAAAAEAWglYAAAAAAAAAAABZCFoBAAAAAAAAAABkIWgFAAAAAAAAAACQhaAVAAAAAAAAAABAFoJWAAAAAAAAAAAAWQhaAQAAAAAAAAAAZCFoBQAAAAAAAAAAkEXOQavly5fH+eefH+Xl5ZGXlxfz58/P+szSpUvjlFNOicLCwjjmmGOivr5+l5o5c+bEUUcdFUVFRVFZWRkvvPBCxvy2bduitrY2jjjiiOjfv39MmjQp2tracm0fAAAAAAAAAAAgZzkHrTo7O2PMmDExZ86cvapvbm6OiRMnxvjx46OpqSmmT58eV155ZTz77LPpmsceeyzq6upi1qxZ8fLLL8eYMWOipqYmNm3alK6ZMWNGPPXUU/H444/HsmXLYsOGDXHxxRfn2j4AAAAAAAAAAEDO8pIkSfb54by8ePLJJ+PCCy/8zJobbrghFixYEK+99lp67B/+4R9i8+bNsXDhwoiIqKysjNNOOy3uueeeiIjo7u6OioqKuPbaa+PGG2+M9vb2GDRoUMydOze+/vWvR0TEm2++GSeccEI0NDTEGWecsct7u7q6oqurK33f0dERFRUV0d7eHqlUal8/MvtRa/u2uGbuyz3dBgAAAAAAAADA56bmxLK46uyRPd0Gn6GjoyOKi4v3KlPUd38309DQENXV1RljNTU1MX369IiI2L59ezQ2NsbMmTPT8/n5+VFdXR0NDQ0REdHY2Bg7duzIWGfUqFExbNiwzwxazZ49O37wgx/sh0/E/tL18c546Z0/9nQbAAAAAAAAAACfmxOGOBDoYLHfg1atra1RWlqaMVZaWhodHR3x0UcfxR//+MfYuXPnbmvefPPN9BoFBQVRUlKyS01ra+tu3ztz5syoq6tL339yohW918D+hXH/P57S020AAAAAAAAAAHxuKgYc2tMt8DnZ70GrnlJYWBiFhYU93QY5+JvCvvF3Jw3p6TYAAAAAAAAAAGAX+z1oVVZWFm1tbRljbW1tkUqlol+/ftGnT5/o06fPbmvKysrSa2zfvj02b96ccarVp2sAAAAAAAAAAAD2l/z9/YKqqqpYvHhxxtiiRYuiqqoqIiIKCgpi3LhxGTXd3d2xePHidM24cePikEMOyahZs2ZNtLS0pGsAAAAAAAAAAAD2l5xPtNq6dWusXbs2fd/c3BxNTU0xYMCAGDZsWMycOTPWr18fDz/8cERETJs2Le655564/vrr4/LLL48lS5bEvHnzYsGCBek16urqYurUqXHqqafG6aefHnfeeWd0dnbGZZddFhERxcXFccUVV0RdXV0MGDAgUqlUXHvttVFVVRVnnHHGX/o3AAAAAAAAAAAA2KOcg1YvvfRSjB8/Pn1fV1cXERFTp06N+vr62LhxY7S0tKTnR4wYEQsWLIgZM2bEXXfdFUOHDo0HH3wwampq0jWXXHJJvPfee3HzzTdHa2trjB07NhYuXBilpaXpmjvuuCPy8/Nj0qRJ0dXVFTU1NXHvvffu04cGAAAAAAAAAADIRV6SJElPN/HX0NHREcXFxdHe3h6pVKqn2wEAAAAAAAAAAHpYLpmi/L9STwAAAAAAAAAAAAcsQSsAAAAAAAAAAIAsBK0AAAAAAAAAAACyELQCAAAAAAAAAADIQtAKAAAAAAAAAAAgC0ErAAAAAAAAAACALAStAAAAAAAAAAAAshC0AgAAAAAAAAAAyELQCgAAAAAAAAAAIAtBKwAAAAAAAAAAgCwErQAAAAAAAAAAALIQtAIAAAAAAAAAAMhC0AoAAAAAAAAAACALQSsAAAAAAAAAAIAsBK0AAAAAAAAAAACyELQCAAAAAAAAAADIQtAKAAAAAAAAAAAgC0ErAAAAAAAAAACALAStAAAAAAAAAAAAshC0AgAAAAAAAAAAyELQCgAAAAAAAAAAIAtBKwAAAAAAAAAAgCwErQAAAAAAAAAAALLYp6DVnDlz4qijjoqioqKorKyMF1544TNrd+zYET/84Q/j6KOPjqKiohgzZkwsXLgwo+aoo46KvLy8Xa7a2tp0zVe/+tVd5qdNm7Yv7QMAAAAAAAAAAOQk56DVY489FnV1dTFr1qx4+eWXY8yYMVFTUxObNm3abf1NN90UDzzwQNx9993x+uuvx7Rp0+Kiiy6KV155JV3z4osvxsaNG9PXokWLIiLiG9/4RsZaV111VUbdbbfdlmv7AAAAAAAAAAAAOctLkiTJ5YHKyso47bTT4p577omIiO7u7qioqIhrr702brzxxl3qy8vL43vf+17G6VSTJk2Kfv36xSOPPLLbd0yfPj2efvrpeOuttyIvLy8i/nSi1dixY+POO+/Mpd20jo6OKC4ujvb29kilUvu0BgAAAAAAAAAAcPDIJVOU04lW27dvj8bGxqiurv7zAvn5UV1dHQ0NDbt9pqurK4qKijLG+vXrFytWrPjMdzzyyCNx+eWXp0NWn/j5z38eAwcOjJNOOilmzpwZH3744Wf22tXVFR0dHRkXAAAAAAAAAADAvuibS/H7778fO3fujNLS0ozx0tLSePPNN3f7TE1NTdx+++1x9tlnx9FHHx2LFy+OJ554Inbu3Lnb+vnz58fmzZvj0ksvzRj/5je/GcOHD4/y8vJYvXp13HDDDbFmzZp44okndrvO7Nmz4wc/+EEuHw8AAAAAAAAAAGC3cgpa7Yu77rorrrrqqhg1alTk5eXF0UcfHZdddln8x3/8x27rf/rTn8aECROivLw8Y/zqq69O//vkk0+OIUOGxDnnnBPr1q2Lo48+epd1Zs6cGXV1den7jo6OqKio+Jw+FQAAAAAAAAAA8EWS008HDhw4MPr06RNtbW0Z421tbVFWVrbbZwYNGhTz58+Pzs7OeOedd+LNN9+M/v37x8iRI3epfeedd+K5556LK6+8MmsvlZWVERGxdu3a3c4XFhZGKpXKuAAAAAAAAAAAAPZFTkGrgoKCGDduXCxevDg91t3dHYsXL46qqqo9PltUVBRHHnlkfPzxx/GLX/wiLrjggl1qHnrooRg8eHBMnDgxay9NTU0RETFkyJBcPgIAAAAAAAAAAEDOcv7pwLq6upg6dWqceuqpcfrpp8edd94ZnZ2dcdlll0VExLe+9a048sgjY/bs2RERsWrVqli/fn2MHTs21q9fH7fcckt0d3fH9ddfn7Fud3d3PPTQQzF16tTo2zezrXXr1sXcuXPjvPPOiyOOOCJWr14dM2bMiLPPPjtGjx69r58dAAAAAAAAAABgr+QctLrkkkvivffei5tvvjlaW1tj7NixsXDhwigtLY2IiJaWlsjP//NBWdu2bYubbrop3n777ejfv3+cd9558bOf/SxKSkoy1n3uueeipaUlLr/88l3eWVBQEM8991w61FVRURGTJk2Km266Kdf2AQAAAAAAAAAAcpaXJEnS0038NXR0dERxcXG0t7dHKpXq6XYAAAAAAAAAAIAelkumKH+PswAAAAAAAAAAAAhaAQAAAAAAAAAAZCNoBQAAAAAAAAAAkIWgFQAAAAAAAAAAQBaCVgAAAAAAAAAAAFkIWgEAAAAAAAAAAGQhaAUAAAAAAAAAAJCFoBUAAAAAAAAAAEAWglYAAAAAAAAAAABZCFoBAAAAAAAAAABkIWgFAAAAAAAAAACQhaAVAAAAAAAAAABAFoJWAAAAAAAAAAAAWQhaAQAAAAAAAAAAZCFoBQAAAAAAAAAAkIWgFQAAAAAAAAAAQBaCVgAAAAAAAAAAAFkIWgEAAAAAAAAAAGQhaAUAAAAAAAAAAJCFoBUAAAAAAAAAAEAWglYAAAAAAAAAAABZCFoBAAAAAAAAAABkIWgFAAAAAAAAAACQhaAVAAAAAAAAAABAFvsUtJozZ04cddRRUVRUFJWVlfHCCy98Zu2OHTvihz/8YRx99NFRVFQUY8aMiYULF2bU3HLLLZGXl5dxjRo1KqNm27ZtUVtbG0cccUT0798/Jk2aFG1tbfvSPgAAAAAAAAAAQE5yDlo99thjUVdXF7NmzYqXX345xowZEzU1NbFp06bd1t90003xwAMPxN133x2vv/56TJs2LS666KJ45ZVXMupOPPHE2LhxY/pasWJFxvyMGTPiqaeeiscffzyWLVsWGzZsiIsvvjjX9gEAAAAAAAAAAHKWlyRJkssDlZWVcdppp8U999wTERHd3d1RUVER1157bdx444271JeXl8f3vve9qK2tTY9NmjQp+vXrF4888khE/OlEq/nz50dTU9Nu39ne3h6DBg2KuXPnxte//vWIiHjzzTfjhBNOiIaGhjjjjDOy9t3R0RHFxcXR3t4eqVQql48MAAAAAAAAAAAchHLJFOV0otX27dujsbExqqur/7xAfn5UV1dHQ0PDbp/p6uqKoqKijLF+/frtcmLVW2+9FeXl5TFy5MiYMmVKtLS0pOcaGxtjx44dGe8dNWpUDBs2bI/v7ejoyLgAAAAAAAAAAAD2RU5Bq/fffz927twZpaWlGeOlpaXR2tq622dqamri9ttvj7feeiu6u7tj0aJF8cQTT8TGjRvTNZWVlVFfXx8LFy6M++67L5qbm+Oss86KLVu2REREa2trFBQURElJyV6/d/bs2VFcXJy+KioqcvmoAAAAAAAAAAAAaTkFrfbFXXfdFccee2yMGjUqCgoK4pprronLLrss8vP//OoJEybEN77xjRg9enTU1NTEM888E5s3b4558+bt83tnzpwZ7e3t6evdd9/9PD4OAAAAAAAAAADwBZRT0GrgwIHRp0+faGtryxhva2uLsrKy3T4zaNCgmD9/fnR2dsY777wTb775ZvTv3z9Gjhz5me8pKSmJ4447LtauXRsREWVlZbF9+/bYvHnzXr+3sLAwUqlUxgUAAAAAAAAAALAvcgpaFRQUxLhx42Lx4sXpse7u7li8eHFUVVXt8dmioqI48sgj4+OPP45f/OIXccEFF3xm7datW2PdunUxZMiQiIgYN25cHHLIIRnvXbNmTbS0tGR9LwAAAAAAAAAAwF+qb64P1NXVxdSpU+PUU0+N008/Pe68887o7OyMyy67LCIivvWtb8WRRx4Zs2fPjoiIVatWxfr162Ps2LGxfv36uOWWW6K7uzuuv/769Jrf+c534vzzz4/hw4fHhg0bYtasWdGnT5+YPHlyREQUFxfHFVdcEXV1dTFgwIBIpVJx7bXXRlVVVZxxxhmfx98BAAAAAAAAAADgM+UctLrkkkvivffei5tvvjlaW1tj7NixsXDhwigtLY2IiJaWlsjP//NBWdu2bYubbrop3n777ejfv3+cd9558bOf/SxKSkrSNb///e9j8uTJ8cEHH8SgQYPizDPPjJUrV8agQYPSNXfccUfk5+fHpEmToqurK2pqauLee+/9Cz46AAAAAAAAAADA3slLkiTp6Sb+Gtrb26OkpCTefffdSKVSPd0OAAAAAAAAAADQwzo6OqKioiI2b94cxcXFe6zN+USrA9WWLVsiIqKioqKHOwEAAAAAAAAAAHqTLVu2ZA1afWFOtOru7o4NGzbEYYcdFnl5eT3dDp/hk5Sgk8cA6K3sVQD0dvYqAHo7exUAvZ29CoDezl71+UqSJLZs2RLl5eWRn5+/x9ovzIlW+fn5MXTo0J5ug72USqX8ZwBAr2avAqC3s1cB0NvZqwDo7exVAPR29qrPT7aTrD6x5xgWAAAAAAAAAAAAglYAAAAAAAAAAADZCFrRqxQWFsasWbOisLCwp1sBgN2yVwHQ29mrAOjt7FUA9Hb2KgB6O3tVz8lLkiTp6SYAAAAAAAAAAAB6MydaAQAAAAAAAAAAZCFoBQAAAAAAAAAAkIWgFQAAAAAAAAAAQBaCVgAAAAAAAAAAAFkIWgEAAAAAAAAAAGQhaEWvMWfOnDjqqKOiqKgoKisr44UXXujplgA4AC1fvjzOP//8KC8vj7y8vJg/f37GfJIkcfPNN8eQIUOiX79+UV1dHW+99VZGzR/+8IeYMmVKpFKpKCkpiSuuuCK2bt2aUbN69eo466yzoqioKCoqKuK2227bpZfHH388Ro0aFUVFRXHyySfHM888k3MvABx8Zs+eHaeddlocdthhMXjw4LjwwgtjzZo1GTXbtm2L2traOOKII6J///4xadKkaGtry6hpaWmJiRMnxqGHHhqDBw+O7373u/Hxxx9n1CxdujROOeWUKCwsjGOOOSbq6+t36Sfbd7G96QWAg8t9990Xo0ePjlQqFalUKqqqquJXv/pVet4+BUBvcuutt0ZeXl5Mnz49PWavAqCn3XLLLZGXl5dxjRo1Kj1vrzpwCVrRKzz22GNRV1cXs2bNipdffjnGjBkTNTU1sWnTpp5uDYADTGdnZ4wZMybmzJmz2/nbbrstfvKTn8T9998fq1atir/5m7+Jmpqa2LZtW7pmypQp8dvf/jYWLVoUTz/9dCxfvjyuvvrq9HxHR0ece+65MXz48GhsbIwf/ehHccstt8S///u/p2t+85vfxOTJk+OKK66IV155JS688MK48MIL47XXXsupFwAOPsuWLYva2tpYuXJlLFq0KHbs2BHnnntudHZ2pmtmzJgRTz31VDz++OOxbNmy2LBhQ1x88cXp+Z07d8bEiRNj+/bt8Zvf/Cb+8z//M+rr6+Pmm29O1zQ3N8fEiRNj/Pjx0dTUFNOnT48rr7wynn322XTN3nwXy9YLAAefoUOHxq233hqNjY3x0ksvxde+9rW44IIL4re//W1E2KcA6D1efPHFeOCBB2L06NEZ4/YqAHqDE088MTZu3Ji+VqxYkZ6zVx3AEugFTj/99KS2tjZ9v3PnzqS8vDyZPXt2D3YFwIEuIpInn3wyfd/d3Z2UlZUlP/rRj9JjmzdvTgoLC5P/+q//SpIkSV5//fUkIpIXX3wxXfOrX/0qycvLS9avX58kSZLce++9yeGHH550dXWla2644Ybk+OOPT9///d//fTJx4sSMfiorK5N//ud/3uteAPhi2LRpUxIRybJly5Ik+dN+cMghhySPP/54uuaNN95IIiJpaGhIkiRJnnnmmSQ/Pz9pbW1N19x3331JKpVK70/XX399cuKJJ2a865JLLklqamrS99m+i+1NLwB8MRx++OHJgw8+aJ8CoNfYsmVLcuyxxyaLFi1KvvKVryTXXXddkiS+UwHQO8yaNSsZM2bMbufsVQc2J1rR47Zv3x6NjY1RXV2dHsvPz4/q6upoaGjowc4AONg0NzdHa2trxp5TXFwclZWV6T2noaEhSkpK4tRTT03XVFdXR35+fqxatSpdc/bZZ0dBQUG6pqamJtasWRN//OMf0zWffs8nNZ+8Z296AeCLob29PSIiBgwYEBERjY2NsWPHjow9YtSoUTFs2LCM/erkk0+O0tLSdE1NTU10dHSkTxvJthftzXexvekFgIPbzp0749FHH43Ozs6oqqqyTwHQa9TW1sbEiRN32U/sVQD0Fm+99VaUl5fHyJEjY8qUKdHS0hIR9qoDnaAVPe7999+PnTt3ZvwHERFRWloara2tPdQVAAejT/aVPe05ra2tMXjw4Iz5vn37xoABAzJqdrfGp9/xWTWfns/WCwAHv+7u7pg+fXp8+ctfjpNOOiki/rRHFBQURElJSUbt/99H9nUv6ujoiI8++mivvovtTS8AHJxeffXV6N+/fxQWFsa0adPiySefjC996Uv2KQB6hUcffTRefvnlmD179i5z9ioAeoPKysqor6+PhQsXxn333RfNzc1x1llnxZYtW+xVB7i+Pd0AAAAAfFHV1tbGa6+9FitWrOjpVgAgw/HHHx9NTU3R3t4e//3f/x1Tp06NZcuW9XRbABDvvvtuXHfddbFo0aIoKirq6XYAYLcmTJiQ/vfo0aOjsrIyhg8fHvPmzYt+/fr1YGf8pZxoRY8bOHBg9OnTJ9ra2jLG29raoqysrIe6AuBg9Mm+sqc9p6ysLDZt2pQx//HHH8cf/vCHjJrdrfHpd3xWzafns/UCwMHtmmuuiaeffjqef/75GDp0aHq8rKwstm/fHps3b86o///7yL7uRalUKvr167dX38X2phcADk4FBQVxzDHHxLhx42L27NkxZsyYuOuuu+xTAPS4xsbG2LRpU5xyyinRt2/f6Nu3byxbtix+8pOfRN++faO0tNReBUCvU1JSEscdd1ysXbvW96oDnKAVPa6goCDGjRsXixcvTo91d3fH4sWLo6qqqgc7A+BgM2LEiCgrK8vYczo6OmLVqlXpPaeqqio2b94cjY2N6ZolS5ZEd3d3VFZWpmuWL18eO3bsSNcsWrQojj/++Dj88MPTNZ9+zyc1n7xnb3oB4OCUJElcc8018eSTT8aSJUtixIgRGfPjxo2LQw45JGOPWLNmTbS0tGTsV6+++mpGOHjRokWRSqXiS1/6UrpmT3vR3nwX25teAPhi6O7ujq6uLvsUAD3unHPOiVdffTWamprS16mnnhpTpkxJ/9teBUBvs3Xr1li3bl0MGTLE96oDXQK9wKOPPpoUFhYm9fX1yeuvv55cffXVSUlJSdLa2trTrQFwgNmyZUvyyiuvJK+88koSEcntt9+evPLKK8k777yTJEmS3HrrrUlJSUnyy1/+Mlm9enVywQUXJCNGjEg++uij9Bp/93d/l/zt3/5tsmrVqmTFihXJsccem0yePDk9v3nz5qS0tDT5p3/6p+S1115LHn300eTQQw9NHnjggXTNr3/966Rv377Jv/3bvyVvvPFGMmvWrOSQQw5JXn311XTN3vQCwMHnX/7lX5Li4uJk6dKlycaNG9PXhx9+mK6ZNm1aMmzYsGTJkiXJSy+9lFRVVSVVVVXp+Y8//jg56aSTknPPPTdpampKFi5cmAwaNCiZOXNmuubtt99ODj300OS73/1u8sYbbyRz5sxJ+vTpkyxcuDBdszffxbL1AsDB58Ybb0yWLVuWNDc3J6tXr05uvPHGJC8vL/mf//mfJEnsUwD0Pl/5yleS6667Ln1vrwKgp337299Oli5dmjQ3Nye//vWvk+rq6mTgwIHJpk2bkiSxVx3IBK3oNe6+++5k2LBhSUFBQXL66acnK1eu7OmWADgAPf/880lE7HJNnTo1SZIk6e7uTr7//e8npaWlSWFhYXLOOecka9asyVjjgw8+SCZPnpz0798/SaVSyWWXXZZs2bIlo+Z///d/kzPPPDMpLCxMjjzyyOTWW2/dpZd58+Ylxx13XFJQUJCceOKJyYIFCzLm96YXAA4+u9unIiJ56KGH0jUfffRR8q//+q/J4Ycfnhx66KHJRRddlGzcuDFjnd/97nfJhAkTkn79+iUDBw5Mvv3tbyc7duzIqHn++eeTsWPHJgUFBcnIkSMz3vGJbN/F9qYXAA4ul19+eTJ8+PCkoKAgGTRoUHLOOeekQ1ZJYp8CoPf5/0ErexUAPe2SSy5JhgwZkhQUFCRHHnlkcskllyRr165Nz9urDlx5SZIkPXOWFgAAAAAAAAAAwIEhv6cbAAAAAAAAAAAA6O0ErQAAAAAAAAAAALIQtAIAAAAAAAAAAMhC0AoAAAAAAAAAACALQSsAAAAAAAAAAIAsBK0AAAAAAAAAAACyELQCAAAAAAAAAADIQtAKAAAAAAAAAAAgC0ErAAAAAAAAAACALAStAAAAAAAAAAAAshC0AgAAAAAAAAAAyOL/ACk/UqDW6MRvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(train_values[:, 4])" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": {}, + "outputs": [], + "source": [ + "# we don't need timestamps or training labels\n", + "train_dropped = train1.drop([\"Timestamp\" , \"Normal/Attack\" ] , axis = 1)\n", + "test_dropped = test_clipped.drop([\"Timestamp\" , \"Normal/Attack\" ] , axis = 1)\n", + "validation_dropped = validation.drop([\"Timestamp\" , \"Normal/Attack\" ] , axis = 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
FIT101LIT101MV101P101P102AIT201AIT202AIT203FIT201MV201...FIT504P501P502PIT501PIT502PIT503FIT601P601P602P603
00.0124.3135111251.92268.313446312.79160.01...0.0119.1002310.03.34850.000256111
10.0124.3920111251.92268.313446312.79160.01...0.0119.1002310.03.34850.000256111
20.0124.4705111251.92268.313446312.79160.01...0.0119.1002310.03.34850.000256111
30.0124.6668111251.92268.313446312.79160.01...0.0119.1002310.03.34850.000256111
40.0124.5098111251.92268.313446312.79160.01...0.0119.1002310.03.34850.000256111
\n", + "

5 rows × 51 columns

\n", + "
" + ], + "text/plain": [ + " FIT101 LIT101 MV101 P101 P102 AIT201 AIT202 AIT203 FIT201 \\\n", + "0 0.0 124.3135 1 1 1 251.9226 8.313446 312.7916 0.0 \n", + "1 0.0 124.3920 1 1 1 251.9226 8.313446 312.7916 0.0 \n", + "2 0.0 124.4705 1 1 1 251.9226 8.313446 312.7916 0.0 \n", + "3 0.0 124.6668 1 1 1 251.9226 8.313446 312.7916 0.0 \n", + "4 0.0 124.5098 1 1 1 251.9226 8.313446 312.7916 0.0 \n", + "\n", + " MV201 ... FIT504 P501 P502 PIT501 PIT502 PIT503 FIT601 P601 \\\n", + "0 1 ... 0.0 1 1 9.100231 0.0 3.3485 0.000256 1 \n", + "1 1 ... 0.0 1 1 9.100231 0.0 3.3485 0.000256 1 \n", + "2 1 ... 0.0 1 1 9.100231 0.0 3.3485 0.000256 1 \n", + "3 1 ... 0.0 1 1 9.100231 0.0 3.3485 0.000256 1 \n", + "4 1 ... 0.0 1 1 9.100231 0.0 3.3485 0.000256 1 \n", + "\n", + " P602 P603 \n", + "0 1 1 \n", + "1 1 1 \n", + "2 1 1 \n", + "3 1 1 \n", + "4 1 1 \n", + "\n", + "[5 rows x 51 columns]" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train_dropped.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
FIT101LIT101MV101P101P102AIT201AIT202AIT203FIT201MV201...FIT504P501P502PIT501PIT502PIT503FIT601P601P602P603
449912.665983533.0524211258.01088.381697330.73572.4411622...0.30458321247.62881.025214186.61880.000064111
449922.644525532.3066211258.01088.381697330.73572.4411622...0.30458321247.54871.025214186.61880.000064111
449932.621785532.1497211258.01088.381697330.73572.4360360...0.30458321247.54871.025214186.53870.000064111
449942.613778531.5216211258.01088.381697330.73572.3386410...0.30458321247.54871.025214186.52270.000064111
449952.613778531.4038211258.01088.381697330.73572.2215100...0.30458321247.54871.025214186.52270.000064111
\n", + "

5 rows × 51 columns

\n", + "
" + ], + "text/plain": [ + " FIT101 LIT101 MV101 P101 P102 AIT201 AIT202 AIT203 \\\n", + "44991 2.665983 533.0524 2 1 1 258.0108 8.381697 330.7357 \n", + "44992 2.644525 532.3066 2 1 1 258.0108 8.381697 330.7357 \n", + "44993 2.621785 532.1497 2 1 1 258.0108 8.381697 330.7357 \n", + "44994 2.613778 531.5216 2 1 1 258.0108 8.381697 330.7357 \n", + "44995 2.613778 531.4038 2 1 1 258.0108 8.381697 330.7357 \n", + "\n", + " FIT201 MV201 ... FIT504 P501 P502 PIT501 PIT502 \\\n", + "44991 2.441162 2 ... 0.304583 2 1 247.6288 1.025214 \n", + "44992 2.441162 2 ... 0.304583 2 1 247.5487 1.025214 \n", + "44993 2.436036 0 ... 0.304583 2 1 247.5487 1.025214 \n", + "44994 2.338641 0 ... 0.304583 2 1 247.5487 1.025214 \n", + "44995 2.221510 0 ... 0.304583 2 1 247.5487 1.025214 \n", + "\n", + " PIT503 FIT601 P601 P602 P603 \n", + "44991 186.6188 0.000064 1 1 1 \n", + "44992 186.6188 0.000064 1 1 1 \n", + "44993 186.5387 0.000064 1 1 1 \n", + "44994 186.5227 0.000064 1 1 1 \n", + "44995 186.5227 0.000064 1 1 1 \n", + "\n", + "[5 rows x 51 columns]" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_dropped.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
FIT101LIT101MV101P101P102AIT201AIT202AIT203FIT201MV201...FIT504P501P502PIT501PIT502PIT503FIT601P601P602P603
02.427057522.8467221262.01618.396437328.63372.4453912...0.30778621250.86521.649953189.59880.000128111
12.446274522.8860221262.01618.396437328.63372.4453912...0.30778621250.86521.649953189.67890.000128111
22.489191522.8467221262.01618.394514328.63372.4423162...0.30861921250.88121.649953189.67890.000128111
32.534350522.9645221262.01618.394514328.63372.4423162...0.30861921250.88121.649953189.61480.000128111
42.569260523.4748221262.01618.394514328.63372.4430852...0.30861921250.88121.649953189.50270.000128111
\n", + "

5 rows × 51 columns

\n", + "
" + ], + "text/plain": [ + " FIT101 LIT101 MV101 P101 P102 AIT201 AIT202 AIT203 \\\n", + "0 2.427057 522.8467 2 2 1 262.0161 8.396437 328.6337 \n", + "1 2.446274 522.8860 2 2 1 262.0161 8.396437 328.6337 \n", + "2 2.489191 522.8467 2 2 1 262.0161 8.394514 328.6337 \n", + "3 2.534350 522.9645 2 2 1 262.0161 8.394514 328.6337 \n", + "4 2.569260 523.4748 2 2 1 262.0161 8.394514 328.6337 \n", + "\n", + " FIT201 MV201 ... FIT504 P501 P502 PIT501 PIT502 PIT503 \\\n", + "0 2.445391 2 ... 0.307786 2 1 250.8652 1.649953 189.5988 \n", + "1 2.445391 2 ... 0.307786 2 1 250.8652 1.649953 189.6789 \n", + "2 2.442316 2 ... 0.308619 2 1 250.8812 1.649953 189.6789 \n", + "3 2.442316 2 ... 0.308619 2 1 250.8812 1.649953 189.6148 \n", + "4 2.443085 2 ... 0.308619 2 1 250.8812 1.649953 189.5027 \n", + "\n", + " FIT601 P601 P602 P603 \n", + "0 0.000128 1 1 1 \n", + "1 0.000128 1 1 1 \n", + "2 0.000128 1 1 1 \n", + "3 0.000128 1 1 1 \n", + "4 0.000128 1 1 1 \n", + "\n", + "[5 rows x 51 columns]" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_dropped.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": {}, + "outputs": [], + "source": [ + "# Transform all columns into float64\n", + "for i in list(train_dropped):\n", + " train_dropped[i]=train_dropped[i].apply(lambda x: str(x).replace(\",\" , \".\"))\n", + "train_dropped = train_dropped.astype(float)\n", + "\n", + "for i in list(test_dropped):\n", + " test_dropped[i]=test_dropped[i].apply(lambda x: str(x).replace(\",\" , \".\"))\n", + "test_dropped = test_dropped.astype(float)\n", + "\n", + "for i in list(validation_dropped):\n", + " validation_dropped[i]=validation_dropped[i].apply(lambda x: str(x).replace(\",\" , \".\"))\n", + "validation_dropped = validation_dropped.astype(float)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [], + "source": [ + "train_values = train_dropped.values\n", + "test_values = test_dropped.values\n", + "validation_values = validation_dropped.values" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train min: 0.0\n", + "Train max: 1014.724\n", + "---\n", + "Test min: 0.0\n", + "Test max: 1201.0\n", + "---\n", + "Validation min: 0.0\n", + "Validation max: 1200.0\n" + ] + } + ], + "source": [ + "print(f'Train min: {train_values.min()}')\n", + "print(f'Train max: {train_values.max()}')\n", + "print('---')\n", + "print(f'Test min: {test_values.min()}')\n", + "print(f'Test max: {test_values.max()}')\n", + "print('---')\n", + "print(f'Validation min: {validation_values.min()}')\n", + "print(f'Validation max: {validation_values.max()}')" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0. , 189.8263 , 0. , 1. , 1. ,\n", + " 168.0338 , 8.366317, 285.3371 , 0. , 0. ,\n", + " 1. , 1. , 1. , 1. , 1. ,\n", + " 1. , 0. , 0. , 364.3863 , 0. ,\n", + " 0. , 0. , 0. , 1. , 1. ,\n", + " 148.7599 , 140.8357 , 0. , 243.0146 , 1. ,\n", + " 1. , 1. , 1. , 1. , 7.432902,\n", + " 129.8385 , 244.8731 , 9.536016, 0. , 0. ,\n", + " 0. , 0. , 1. , 1. , 9.468726,\n", + " 0. , 3.14022 , 0. , 1. , 1. ,\n", + " 1. ])" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_values.min(axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(495000, 51)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train_values.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler\n", + "\n", + "def scale_data(train, test, validation):\n", + " scaler = MinMaxScaler(feature_range=(0, 1), clip=True).fit(train)\n", + " #scaler = MaxAbsScaler().fit(train)\n", + "\n", + " train_scaled = scaler.transform(train)\n", + " test_scaled = scaler.transform(test)\n", + " validation_scaled = scaler.transform(validation)\n", + "\n", + " # train_scaled = scaler.fit_transform(train)\n", + " # validation_scaled = scaler.fit_transform(validation)\n", + " # test_scaled = scaler.fit_transform(test)\n", + "\n", + " return train_scaled, test_scaled, validation_scaled" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [], + "source": [ + "train_norm, test_norm, validation_norm = scale_data(train_values, test_values, validation_values)" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-----Dim 0----\n", + "TRAIN original: 0.0, 2.745092\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.760145\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.698972\n", + "VAL norm: 0.0, 0.9831991058951758\n", + "-----Dim 1----\n", + "TRAIN original: 120.6237, 817.5565\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 189.8263, 888.1722\n", + "TEST norm: 0.09929594359743149, 1.0\n", + "VAL original: 488.0688, 925.0323\n", + "VAL norm: 0.5272317503208344, 1.0\n", + "-----Dim 2----\n", + "TRAIN original: 0.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 3----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 4----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 5----\n", + "TRAIN original: 251.6662, 272.5263\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 168.0338, 267.7198\n", + "TEST norm: 0.0, 0.7695840384274284\n", + "VAL original: 257.8826, 266.0856\n", + "VAL norm: 0.2980043240444683, 0.6912430908768403\n", + "-----Dim 6----\n", + "TRAIN original: 8.258652, 8.988273\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 8.366317, 8.73321\n", + "TEST norm: 0.14756291279993405, 0.6504171343752443\n", + "VAL original: 6.0, 8.470778\n", + "VAL norm: 0.0, 0.2907345046263732\n", + "-----Dim 7----\n", + "TRAIN original: 312.2789, 567.4699\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 285.3371, 384.4655\n", + "TEST norm: 0.0, 0.2828728285872151\n", + "VAL original: 314.8423, 336.8111\n", + "VAL norm: 0.010045025098847526, 0.09613270060464507\n", + "-----Dim 8----\n", + "TRAIN original: 0.0, 2.487938\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.826899\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.804857\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 9----\n", + "TRAIN original: 0.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 10----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 11----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 12----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 13----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 14----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 15----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 16----\n", + "TRAIN original: 0.0, 21.0993\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 45.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.006402459, 45.0\n", + "VAL norm: 0.00030344414269667716, 1.0\n", + "-----Dim 17----\n", + "TRAIN original: 0.0, 2.358774\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.376197\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.352112\n", + "VAL norm: 0.0, 0.9971756514189151\n", + "-----Dim 18----\n", + "TRAIN original: 132.8185, 1014.724\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 364.3863, 1201.0\n", + "TEST norm: 0.26257665929059293, 1.0\n", + "VAL original: 732.1039, 1200.0\n", + "VAL norm: 0.6795347120524817, 1.0\n", + "-----Dim 19----\n", + "TRAIN original: 0.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 20----\n", + "TRAIN original: 0.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 21----\n", + "TRAIN original: 0.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 22----\n", + "TRAIN original: 0.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 23----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 24----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 25----\n", + "TRAIN original: 0.0, 148.8561\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 148.7599, 148.8561\n", + "TEST norm: 0.9993537382747499, 1.0\n", + "VAL original: 148.7695, 148.8561\n", + "VAL norm: 0.9994182300893278, 1.0\n", + "-----Dim 26----\n", + "TRAIN original: 153.7811, 235.7088\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 140.8357, 333.8118\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 151.961, 297.3853\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 27----\n", + "TRAIN original: 0.0, 1.747862\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 1.744914\n", + "TEST norm: 0.0, 0.998313367989006\n", + "VAL original: 0.0, 1.743504\n", + "VAL norm: 0.0, 0.9975066681465699\n", + "-----Dim 28----\n", + "TRAIN original: 130.3896, 1003.935\n", + "TRAIN norm: 0.0, 0.9999999999999999\n", + "TEST original: 243.0146, 1002.781\n", + "TEST norm: 0.12892861664659905, 0.9986789467382003\n", + "VAL original: 572.6224, 1001.935\n", + "VAL norm: 0.5062505051254348, 0.9977104796155986\n", + "-----Dim 29----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 30----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 31----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 32----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 33----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 34----\n", + "TRAIN original: 7.411433, 7.925084\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 7.432902, 8.307037\n", + "TEST norm: 0.04179686207171862, 1.0\n", + "VAL original: 7.831518, 8.219559\n", + "VAL norm: 0.8178412969117161, 1.0\n", + "-----Dim 35----\n", + "TRAIN original: 142.3481, 218.3286\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 129.8385, 260.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 141.8354, 272.8531\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 36----\n", + "TRAIN original: 252.0828, 283.3568\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 244.8731, 297.2635\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 260.0936, 291.0151\n", + "VAL norm: 0.25614887766195515, 1.0\n", + "-----Dim 37----\n", + "TRAIN original: 7.344271, 227.1725\n", + "TRAIN norm: 0.0, 0.9999999999999999\n", + "TEST original: 9.536016, 442.4635\n", + "TEST norm: 0.009970261826564596, 1.0\n", + "VAL original: 9.766726, 18.99513\n", + "VAL norm: 0.011019763071466127, 0.05299983106355279\n", + "-----Dim 38----\n", + "TRAIN original: 0.001281723, 1.757754\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 1.753653\n", + "TEST norm: 0.0, 0.9976652065314663\n", + "VAL original: 1.217636, 1.752628\n", + "VAL norm: 0.6924984202298343, 0.9970816504950736\n", + "-----Dim 39----\n", + "TRAIN original: 0.000640451, 1.361983\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 1.358781\n", + "TEST norm: 0.0, 0.9976479101440325\n", + "VAL original: 0.8169591, 1.360318\n", + "VAL norm: 0.599642352763926, 0.9987769426576558\n", + "-----Dim 40----\n", + "TRAIN original: 0.001664373, 0.7636911\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 0.7415421\n", + "TEST norm: 0.0, 0.9709340903472012\n", + "VAL original: 0.3337708, 0.7391096\n", + "VAL norm: 0.4358199197388519, 0.96774194509322\n", + "-----Dim 41----\n", + "TRAIN original: 0.0, 0.3170099\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 0.0, 0.3116933\n", + "TEST norm: 0.0, 0.9832289149329405\n", + "VAL original: 0.0, 0.3109887\n", + "VAL norm: 0.0, 0.9810062714129748\n", + "-----Dim 42----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 43----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 44----\n", + "TRAIN original: 8.891951, 264.6437\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 9.468726, 254.3418\n", + "TEST norm: 0.0022552142937642267, 0.9597191415492528\n", + "VAL original: 163.8843, 251.522\n", + "VAL norm: 0.6060265456874743, 0.9486936060014978\n", + "-----Dim 45----\n", + "TRAIN original: 0.0, 3.668343\n", + "TRAIN norm: 0.0, 0.9999999999999999\n", + "TEST original: 0.0, 1.826162\n", + "TEST norm: 0.0, 0.4978165891248446\n", + "VAL original: 0.0, 1.970333\n", + "VAL norm: 0.0, 0.5371179848776408\n", + "-----Dim 46----\n", + "TRAIN original: 3.108177, 200.6376\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 3.14022, 191.986\n", + "TEST norm: 0.00016221887105902222, 0.9562009554394335\n", + "VAL original: 134.4687, 190.3679\n", + "VAL norm: 0.6650174996967415, 0.9480092644223439\n", + "-----Dim 47----\n", + "TRAIN original: 0.0, 1.746131\n", + "TRAIN norm: 0.0, 0.9999999999999999\n", + "TEST original: 0.0, 1.80271\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 0.0, 1.742287\n", + "VAL norm: 0.0, 0.9977985615054081\n", + "-----Dim 48----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n", + "-----Dim 49----\n", + "TRAIN original: 1.0, 2.0\n", + "TRAIN norm: 0.0, 1.0\n", + "TEST original: 1.0, 2.0\n", + "TEST norm: 0.0, 1.0\n", + "VAL original: 1.0, 2.0\n", + "VAL norm: 0.0, 1.0\n", + "-----Dim 50----\n", + "TRAIN original: 1.0, 1.0\n", + "TRAIN norm: 0.0, 0.0\n", + "TEST original: 1.0, 1.0\n", + "TEST norm: 0.0, 0.0\n", + "VAL original: 1.0, 1.0\n", + "VAL norm: 0.0, 0.0\n" + ] + } + ], + "source": [ + "for i in range(51):\n", + " print(f'-----Dim {i}----')\n", + " print(f'TRAIN original: {train_values[:, i].min()}, {train_values[:, i].max()}')\n", + " print(f'TRAIN norm: {train_norm[:, i].min()}, {train_norm[:, i].max()}')\n", + " print(f'TEST original: {test_values[:, i].min()}, {test_values[:, i].max()}')\n", + " print(f'TEST norm: {test_norm[:, i].min()}, {test_norm[:, i].max()}')\n", + " print(f'VAL original: {validation_values[:, i].min()}, {validation_values[:, i].max()}')\n", + " print(f'VAL norm: {validation_norm[:, i].min()}, {validation_norm[:, i].max()}')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0 1.0\n", + "0.0 1.0\n", + "0.0 1.0\n", + "(495000, 51) (404928, 51) (44991, 51)\n" + ] + } + ], + "source": [ + "print(train_norm.min(), train_norm.max())\n", + "print(test_norm.min(), test_norm.max())\n", + "print(validation_norm.min(), validation_norm.max())\n", + "print(train_norm.shape, test_norm.shape, validation_norm.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": {}, + "outputs": [], + "source": [ + "test_labels_clipped = test_labels_clipped.to_numpy()\n", + "test_labels_reshaped = np.zeros_like(test_norm)\n", + "\n", + "for idx in range(0, len(test_labels_clipped)):\n", + " if test_labels_clipped[idx]:\n", + " # labels_reshaped.shape[1] == 51 aka num_feats\n", + " test_labels_reshaped[idx][0:test_labels_reshaped.shape[1]] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": {}, + "outputs": [], + "source": [ + "validation_labels_reshaped = np.zeros_like(validation_norm)\n", + "\n", + "for idx in range(0, len(validation_labels)):\n", + " if validation_labels[idx]:\n", + " # labels_reshaped.shape[1] == 51 aka num_feats\n", + " validation_labels_reshaped[idx][0:validation_labels_reshaped.shape[1]] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(404928, 51) (44991, 51)\n" + ] + } + ], + "source": [ + "print(test_labels_reshaped.shape, validation_labels_reshaped.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": {}, + "outputs": [], + "source": [ + "np.save('labels.npy', test_labels_reshaped)\n", + "np.save('labels_validation.npy', validation_labels_reshaped)\n", + "np.save('train.npy', train_norm)\n", + "np.save('test.npy', test_norm)\n", + "np.save('validation.npy', validation_norm)" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": {}, + "outputs": [], + "source": [ + "load_test = np.load('../processed/SWAT_big/validation.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "labels = np.load('../processed/SWAT_big/labels.npy')\n", + "labels_validation = np.load('../processed/SWAT_big/labels_validation.npy')\n", + "labels = (np.sum(labels, axis=1) >= 1) + 0\n", + "labels_validation = (np.sum(labels_validation, axis=1) >= 1) + 0\n", + "# labels.shape[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12.140185233342002" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "((labels.sum() + labels_validation.sum()) / (labels.shape[0] + labels_validation.shape[0])) * 100" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1501" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "labels.sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.12386152599968389" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(labels.sum() / labels.shape[0]) " + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(44991, 51)" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "load_test.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py3.9test", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "27efe6010b91a164a18a011cd71b7afbe2f076e5b83b7f8099f414d97e11e710" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/subject1-4/AdaDiff/data/split_val_wadi.ipynb b/subject1-4/AdaDiff/data/split_val_wadi.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..fa867f5c1987856a390c7260bb8bda626256f7e2 --- /dev/null +++ b/subject1-4/AdaDiff/data/split_val_wadi.ipynb @@ -0,0 +1,1188 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "train = pd.read_csv('WADI_14days_new.csv')\n", + "test = pd.read_csv('WADI_attackdataLABLE.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(784571, 130)\n", + "(172803, 131)\n" + ] + } + ], + "source": [ + "print(train.shape)\n", + "print(test.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(784571, 126)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train = train.dropna(axis=1, how='all')\n", + "train.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(172803, 127)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test = test.dropna(axis=1, how='all')\n", + "test.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['Row', 'Date', 'Time', '1_AIT_001_PV', '1_AIT_002_PV', '1_AIT_003_PV',\n", + " '1_AIT_004_PV', '1_AIT_005_PV', '1_FIT_001_PV', '1_LS_001_AL',\n", + " ...\n", + " '3_MV_001_STATUS', '3_MV_002_STATUS', '3_MV_003_STATUS',\n", + " '3_P_001_STATUS', '3_P_002_STATUS', '3_P_003_STATUS', '3_P_004_STATUS',\n", + " 'LEAK_DIFF_PRESSURE', 'PLANT_START_STOP_LOG',\n", + " 'TOTAL_CONS_REQUIRED_FLOW'],\n", + " dtype='object', length=126)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['Row ', 'Date ', 'Time', '1_AIT_001_PV', '1_AIT_002_PV', '1_AIT_003_PV',\n", + " '1_AIT_004_PV', '1_AIT_005_PV', '1_FIT_001_PV', '1_LS_001_AL',\n", + " ...\n", + " '3_MV_002_STATUS', '3_MV_003_STATUS', '3_P_001_STATUS',\n", + " '3_P_002_STATUS', '3_P_003_STATUS', '3_P_004_STATUS',\n", + " 'LEAK_DIFF_PRESSURE', 'PLANT_START_STOP_LOG',\n", + " 'TOTAL_CONS_REQUIRED_FLOW', 'Attack LABLE (1:No Attack, -1:Attack)'],\n", + " dtype='object', length=127)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " 1 162826\n", + "-1 9977\n", + "Name: Attack LABLE (1:No Attack, -1:Attack), dtype: int64" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test['Attack LABLE (1:No Attack, -1:Attack)'].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# trim column names\n", + "train = train.rename(columns=lambda x: x.strip())\n", + "test = test.rename(columns=lambda x: x.strip())" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 162826\n", + "1 9977\n", + "Name: Attack LABLE (1:No Attack, -1:Attack), dtype: int64" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test['Attack LABLE (1:No Attack, -1:Attack)'] = [1 if x == -1 else 0 for x in test['Attack LABLE (1:No Attack, -1:Attack)']]\n", + "test['Attack LABLE (1:No Attack, -1:Attack)'].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(172803,)\n", + "9977\n" + ] + } + ], + "source": [ + "test_labels = test['Attack LABLE (1:No Attack, -1:Attack)'].values\n", + "print(test_labels.shape)\n", + "print(np.sum(test_labels))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "def search_ratio(test_labels, val_len):\n", + " val = test_labels[:val_len]\n", + " test = test_labels[val_len:]\n", + " test_ratio = (np.sum(test) /test.shape[0]) * 100\n", + " val_ratio = (np.sum(val) / val.shape[0]) * 100\n", + " print(f'val ratio: {val_ratio}')\n", + " print(f'test ratio: {test_ratio}')\n", + " print('----')\n", + " return val_ratio, test_ratio" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "val ratio: 8.686342592592593\n", + "test ratio: 5.449997749529008\n", + "----\n", + "val ratio: 5.790895061728396\n", + "test ratio: 5.770579304616599\n", + "----\n", + "val ratio: 4.343171296296297\n", + "test ratio: 6.131232684475886\n", + "----\n", + "val ratio: 2.895447530864198\n", + "test ratio: 7.007101345039392\n", + "----\n" + ] + } + ], + "source": [ + "vr, tr = search_ratio(test_labels=test_labels, val_len=int(0.1 * test.shape[0]))\n", + "vr, tr = search_ratio(test_labels=test_labels, val_len=int(0.15 * test.shape[0]))\n", + "vr, tr = search_ratio(test_labels=test_labels, val_len=int(0.2 * test.shape[0]))\n", + "vr, tr = search_ratio(test_labels=test_labels, val_len=int(0.3 * test.shape[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "25920" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "val_len = int(0.15 * test.shape[0])\n", + "val_len" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(25920, 127)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation = test[:val_len]\n", + "validation.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(146883, 127)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_clipped = test[val_len:]\n", + "test_clipped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 24419\n", + "1 1501\n", + "Name: Attack LABLE (1:No Attack, -1:Attack), dtype: int64" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation['Attack LABLE (1:No Attack, -1:Attack)'].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(146883,)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_labels_clipped = test_labels[val_len:]\n", + "test_labels_clipped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(25920,)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_labels = test_labels[:val_len]\n", + "validation_labels.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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AACsQtwAAACrEDcAAMAqxA0AALAKcQMAAKxC3AAAAKsQNwAAwCrEDQAAsApxAwAArELcAAAAqxA3AADAKsQNAACwCnEDAACsQtwAAACrEDcAAMAqxA0AALAKcQMAAKxC3AAAAKuclriZM2eOunfvrtjYWGVnZ2vdunUNrl+6dKnS0tIUGxur9PR0rVy5Muy8MUYzZszQOeeco3bt2snn82nHjh2RHAEAALQSEY+bJUuWqLCwUDNnztTGjRvVr18/5eXlqbKyst7177//vvLz8zV69Ght2rRJQ4YM0ZAhQ1RWVuaseeSRR/Tkk09q3rx5Wrt2rc466yzl5eXp8OHDkR4HAAC0cC5jjInkA2RnZ+vSSy/V008/LUkKhUJKSUnR+PHjNXXq1OPWDxs2TAcPHtSKFSucYwMGDFD//v01b948GWOUnJysSZMm6c9//rMkqbq6WklJSZo/f75uuummk+4pGAwqLi5O1dXV8ng8TTQpAACIpMa+fkdHchNHjhxRaWmppk2b5hyLioqSz+eT3++v9xq/36/CwsKwY3l5eVq+fLkk6bPPPlMgEJDP53POx8XFKTs7W36/v964qampUU1NjfPnYDD4S8Y6oZJtFXrvf/sict8AALQmuWlJurxXYrM8dkTjZt++fTp27JiSkpLCjiclJWn79u31XhMIBOpdHwgEnPN1x0605qeKiop0//33/6wZTkXpzm/0/H8/j/jjAADQ0nXu6LYzblqKadOmhb0bFAwGlZKS0uSPM6BnglyuJr9bAABanUtSz262x45o3CQmJqpNmzaqqKgIO15RUSGv11vvNV6vt8H1df+3oqJC55xzTtia/v3713ufbrdbbrf7547RaL/5VWf95ledI/44AADgxCL6bamYmBhlZGSopKTEORYKhVRSUqKcnJx6r8nJyQlbL0nFxcXO+h49esjr9YatCQaDWrt27QnvEwAAnDki/tdShYWFGjlypDIzM5WVlaVZs2bp4MGDGjVqlCRpxIgR6tq1q4qKiiRJEyZM0KBBg/TYY49p8ODBWrx4sTZs2KBnn31WkuRyuTRx4kT93//9n3r16qUePXrovvvuU3JysoYMGRLpcQAAQAsX8bgZNmyYvvrqK82YMUOBQED9+/fXqlWrnA8E79q1S1FRP7yBNHDgQC1atEjTp0/XPffco169emn58uXq27evs+bu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MKi4uDhVV1fL4/H8smEBAMBp0djX74i8c+P3+xUfH++EjST5fD5FRUVp7dq19V5TWlqq2tpa+Xw+51haWppSU1Pl9/udY1u3btVf/vIXvfDCC4qKatz2a2pqFAwGw24AAMBOEYmbQCCgLl26hB2Ljo5Wp06dFAgETnhNTEzMce/AJCUlOdfU1NQoPz9fjz76qFJTUxu9n6KiIsXFxTm3lJSUUxsIAAC0GqcUN1OnTpXL5Wrwtn379kjtVdOmTVPv3r118803n/J11dXVzm337t0R2iEAAGhu0aeyeNKkSbrlllsaXNOzZ095vV5VVlaGHT969Kj2798vr9db73Ver1dHjhxRVVVV2Ls3FRUVzjWrV6/Wxx9/rJdfflmSVPdxocTERN177726//77671vt9stt9vdmBEBAEArd0px07lzZ3Xu3Pmk63JyclRVVaXS0lJlZGRI+j5MQqGQsrOz670mIyNDbdu2VUlJiYYOHSpJKi8v165du5STkyNJ+te//qXvvvvOuWb9+vW69dZb9e677+q88847lVEAAIClTiluGqt379666qqrdNttt2nevHmqra3VuHHjdNNNNznflNqzZ49yc3P1wgsvKCsrS3FxcRo9erQKCwvVqVMneTwejR8/Xjk5Oc43pX4aMPv27XMe71S+LQUAAOwVkbiRpIULF2rcuHHKzc1VVFSUhg4dqieffNI5X1tbq/Lych06dMg59sQTTzhra2pqlJeXp2eeeSZSWwQAABaKyO+5aen4PTcAALQ+zfp7bgAAAJoLcQMAAKxC3AAAAKsQNwAAwCrEDQAAsApxAwAArELcAAAAqxA3AADAKsQNAACwCnEDAACsQtwAAACrEDcAAMAqxA0AALAKcQMAAKxC3AAAAKsQNwAAwCrEDQAAsApxAwAArELcAAAAqxA3AADAKsQNAACwCnEDAACsQtwAAACrEDcAAMAqxA0AALAKcQMAAKxC3AAAAKsQNwAAwCrEDQAAsApxAwAArELcAAAAqxA3AADAKsQNAACwCnEDAACsQtwAAACrEDcAAMAqxA0AALAKcQMAAKxC3AAAAKsQNwAAwCrEDQAAsApxAwAArBLd3BtoDsYYSVIwGGzmnQAAgMaqe92uex0/kTMybg4cOCBJSklJaeadAACAU3XgwAHFxcWd8LzLnCx/LBQKhbR371517NhRLperSe87GAwqJSVFu3fvlsfjadL7bqmY+cyYWToz5z4TZ5bOzLmZueXPbIzRgQMHlJycrKioE3+y5ox85yYqKkrdunWL6GN4PJ5W8f8oTYmZzxxn4txn4szSmTk3M7dsDb1jU4cPFAMAAKsQNwAAwCrETRNzu92aOXOm3G53c2/ltGHmM8eZOPeZOLN0Zs7NzPY4Iz9QDAAA7MU7NwAAwCrEDQAAsApxAwAArELcAAAAqxA3TWjOnDnq3r27YmNjlZ2drXXr1jX3lupVVFSkSy+9VB07dlSXLl00ZMgQlZeXh605fPiwxo4dq4SEBHXo0EFDhw5VRUVF2Jpdu3Zp8ODBat++vbp06aLJkyfr6NGjYWveeustXXLJJXK73Tr//PM1f/784/bTHM/bQw89JJfLpYkTJzrHbJ15z549uvnmm5WQkKB27dopPT1dGzZscM4bYzRjxgydc845ateunXw+n3bs2BF2H/v371dBQYE8Ho/i4+M1evRoffvtt2FrPvroI/36179WbGysUlJS9Mgjjxy3l6VLlyotLU2xsbFKT0/XypUrm3zeY8eO6b777lOPHj3Url07nXfeeXrggQfC/i0aG2Z+55139Lvf/U7JyclyuVxavnx52PmWNGNj9vJLZ66trdWUKVOUnp6us846S8nJyRoxYoT27t3bqmc+2dw/dfvtt8vlcmnWrFmtfu5fxKBJLF682MTExJjnnnvObNmyxdx2220mPj7eVFRUNPfWjpOXl2eef/55U1ZWZjZv3myuueYak5qaar799ltnze23325SUlJMSUmJ2bBhgxkwYIAZOHCgc/7o0aOmb9++xufzmU2bNpmVK1eaxMREM23aNGfNp59+atq3b28KCwvN1q1bzVNPPWXatGljVq1a5axpjudt3bp1pnv37uaiiy4yEyZMsHrm/fv3m3PPPdfccsstZu3atebTTz81b7zxhvnf//7nrHnooYdMXFycWb58ufnwww/Ntddea3r06GG+++47Z81VV11l+vXrZz744APz7rvvmvPPP9/k5+c756urq01SUpIpKCgwZWVl5sUXXzTt2rUzf//73501//3vf02bNm3MI488YrZu3WqmT59u2rZtaz7++OMmnfnBBx80CQkJZsWKFeazzz4zS5cuNR06dDCzZ8+2auaVK1eae++91yxbtsxIMq+88krY+ZY0Y2P28ktnrqqqMj6fzyxZssRs377d+P1+k5WVZTIyMsLuo7XNfLK5f2zZsmWmX79+Jjk52TzxxBOtfu5fgrhpIllZWWbs2LHOn48dO2aSk5NNUVFRM+6qcSorK40k8/bbbxtjvv8h0bZtW7N06VJnzbZt24wk4/f7jTHf/8cWFRVlAoGAs2bu3LnG4/GYmpoaY4wxd999t+nTp0/YYw0bNszk5eU5fz7dz9uBAwdMr169THFxsRk0aJATN7bOPGXKFHP55Zef8HwoFDJer9c8+uijzrGqqirjdrvNiy++aIwxZuvWrUaSWb9+vbPmP//5j3G5XGbPnj3GGGOeeeYZc/bZZzvPQ91jX3DBBc6fb7zxRjN48OCwx8/OzjZ//OMff9mQPzF48GBz6623hh27/vrrTUFBgTHGzpl/+oLXkmZszF6aYub6rFu3zkgyO3fuNMa0/pmNOfHcX3zxhenataspKysz5557bljc2DD3qeKvpZrAkSNHVFpaKp/P5xyLioqSz+eT3+9vxp01TnV1tSSpU6dOkqTS0lLV1taGzZOWlqbU1FRnHr/fr/T0dCUlJTlr8vLyFAwGtWXLFmfNj++jbk3dfTTH8zZ27FgNHjz4uH3ZOvOrr76qzMxM/eEPf1CXLl108cUX6x//+Idz/rPPPlMgEAjbT1xcnLKzs8Pmjo+PV2ZmprPG5/MpKipKa9euddb85je/UUxMTNjc5eXl+uabb5w1DT03TWXgwIEqKSnRJ598Ikn68MMP9d577+nqq6+2duafakkzNmYvkVJdXS2Xy6X4+HhnrzbOHAqFNHz4cE2ePFl9+vQ57rytczeEuGkC+/bt07Fjx8Je9CQpKSlJgUCgmXbVOKFQSBMnTtRll12mvn37SpICgYBiYmKcHwh1fjxPIBCod966cw2tCQaD+u67707787Z48WJt3LhRRUVFx52zdeZPP/1Uc+fOVa9evfTGG2/ojjvu0J133ql//vOfYftuaD+BQEBdunQJOx8dHa1OnTo1yXPT1HNPnTpVN910k9LS0tS2bVtdfPHFmjhxogoKCsL2Y9PMP9WSZmzMXiLh8OHDmjJlivLz851/ENLWmR9++GFFR0frzjvvrPe8rXM35Iz8V8Hxg7Fjx6qsrEzvvfdec28lonbv3q0JEyaouLhYsbGxzb2d0yYUCikzM1N//etfJUkXX3yxysrKNG/ePI0cObKZdxcZL730khYuXKhFixapT58+2rx5syZOnKjk5GRrZ0a42tpa3XjjjTLGaO7cuc29nYgqLS3V7NmztXHjRrlcrubeTovBOzdNIDExUW3atDnumzUVFRXyer3NtKuTGzdunFasWKE1a9aoW7duznGv16sjR46oqqoqbP2P5/F6vfXOW3euoTUej0ft2rU7rc9baWmpKisrdckllyg6OlrR0dF6++239eSTTyo6OlpJSUnWzSxJ55xzji688MKwY71799auXbvC9t3QfrxeryorK8POHz16VPv372+S56ap5548ebLz7k16erqGDx+uu+66y3nHzsaZf6olzdiYvTSlurDZuXOniouLnXdt6vZi28zvvvuuKisrlZqa6vxs27lzpyZNmqTu3bs7+7Ft7pMhbppATEyMMjIyVFJS4hwLhUIqKSlRTk5OM+6sfsYYjRs3Tq+88opWr16tHj16hJ3PyMhQ27Ztw+YpLy/Xrl27nHlycnL08ccfh/0HU/eDpO7FNCcnJ+w+6tbU3cfpfN5yc3P18ccfa/Pmzc4tMzNTBQUFzv+2bWZJuuyyy477mv8nn3yic889V5LUo0cPeb3esP0Eg0GtXbs2bO6qqiqVlpY6a1avXq1QKKTs7GxnzTvvvKPa2lpnTXFxsS644AKdffbZzpqGnpumcujQIUVFhf9oa9OmjUKhkCQ7Z/6pljRjY/bSVOrCZseOHXrzzTeVkJAQdt7GmYcPH66PPvoo7GdbcnKyJk+erDfeeMPauU/qtH582WKLFy82brfbzJ8/32zdutWMGTPGxMfHh32zpqW44447TFxcnHnrrbfMl19+6dwOHTrkrLn99ttNamqqWb16tdmwYYPJyckxOTk5zvm6r0VfeeWVZvPmzWbVqlWmc+fO9X4tevLkyWbbtm1mzpw59X4turmetx9/W8rWmdetW2eio6PNgw8+aHbs2GEWLlxo2rdvbxYsWOCseeihh0x8fLz597//bT766CNz3XXX1fuV4YsvvtisXbvWvPfee6ZXr15hXyOtqqoySUlJZvjw4aasrMwsXrzYtG/f/rivkUZHR5u//e1vZtu2bWbmzJkR+Sr4yJEjTdeuXZ2vgi9btswkJiaau+++26qZDxw4YDZt2mQ2bdpkJJnHH3/cbNq0yflmUEuasTF7+aUzHzlyxFx77bWmW7duZvPmzWE/2378DaDWNvPJ5q7PT78t1Vrn/iWImyb01FNPmdTUVBMTE2OysrLMBx980Nxbqpekem/PP/+8s+a7774zf/rTn8zZZ59t2rdvb37/+9+bL7/8Mux+Pv/8c3P11Vebdu3amcTERDNp0iRTW1sbtmbNmjWmf//+JiYmxvTs2TPsMeo01/P207ixdebXXnvN9O3b17jdbpOWlmaeffbZsPOhUMjcd999JikpybjdbpObm2vKy8vD1nz99dcmPz/fdOjQwXg8HjNq1Chz4MCBsDUffvihufzyy43b7TZdu3Y1Dz300HF7eemll8yvfvUrExMTY/r06WNef/31Jp83GAyaCRMmmNTUVBMbG2t69uxp7r333rAXOBtmXrNmTb3/HY8cObLFzdiYvfzSmT/77LMT/mxbs2ZNq535ZHPXp764aY1z/xIuY370azsBAABaOT5zAwAArELcAAAAqxA3AADAKsQNAACwCnEDAACsQtwAAACrEDcAAMAqxA0AALAKcQMAAKxC3AAAAKsQNwAAwCrEDQAAsMr/A5eCRx4pbREKAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.plot(test_dropped.to_numpy()[:, 7])\n", + "#plt.fill_between(np.arange(test_labels.shape[0]), test_labels, color='red', alpha=0.3, linestyle='dashed', linewidth=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(172803,)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_labels.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "test_dropped = test_clipped.drop(['Row', 'Date', 'Time', 'Attack LABLE (1:No Attack, -1:Attack)'], axis=1)\n", + "validation_dropped = validation.drop(['Row', 'Date', 'Time', 'Attack LABLE (1:No Attack, -1:Attack)'], axis=1)\n", + "train_dropped = train.drop(['Row', 'Date', 'Time'], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [], + "source": [ + "missing_test = test_dropped.isna().sum()\n", + "missing_val = validation_dropped.isna().sum()\n", + "missing_train = train_dropped.isna().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(784571, 123)" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train_dropped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for x in missing_train:\n", + " print(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "train_dropped = train_dropped.interpolate(method='linear', limit_direction='forward', axis=0)\n", + "train_dropped = train_dropped.fillna(0)\n", + "test_dropped = test_dropped.interpolate(method='linear', limit_direction='forward', axis=0)\n", + "test_dropped = test_dropped.fillna(0)\n", + "validation_dropped = validation_dropped.interpolate(method='linear', limit_direction='forward', axis=0)\n", + "validation_dropped = validation_dropped.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
1_AIT_001_PV1_AIT_002_PV1_AIT_003_PV1_AIT_004_PV1_AIT_005_PV1_FIT_001_PV1_LS_001_AL1_LS_002_AL1_LT_001_PV1_MV_001_STATUS...3_MV_001_STATUS3_MV_002_STATUS3_MV_003_STATUS3_P_001_STATUS3_P_002_STATUS3_P_003_STATUS3_P_004_STATUSLEAK_DIFF_PRESSUREPLANT_START_STOP_LOGTOTAL_CONS_REQUIRED_FLOW
120963179.5570.70346311.8693457.2620.2991320.001140.00.052.29841.0...1.01.01.01.01.01.01.063.88291.00.83
120964179.5570.70346311.8693457.2620.2991320.001140.00.052.29841.0...1.01.01.01.01.01.01.063.88291.00.83
120965179.5570.70346311.8693457.2620.2991320.001140.00.052.29841.0...1.01.01.01.01.01.01.063.88291.00.83
120966179.5570.70346311.8693457.2620.2991320.001140.00.052.29841.0...1.01.01.01.01.01.01.063.88291.00.83
120967179.5570.70346311.8693457.2620.2991320.001140.00.052.29841.0...1.01.01.01.01.01.01.063.88291.00.83
\n", + "

5 rows × 123 columns

\n", + "
" + ], + "text/plain": [ + " 1_AIT_001_PV 1_AIT_002_PV 1_AIT_003_PV 1_AIT_004_PV 1_AIT_005_PV \\\n", + "120963 179.557 0.703463 11.8693 457.262 0.299132 \n", + "120964 179.557 0.703463 11.8693 457.262 0.299132 \n", + "120965 179.557 0.703463 11.8693 457.262 0.299132 \n", + "120966 179.557 0.703463 11.8693 457.262 0.299132 \n", + "120967 179.557 0.703463 11.8693 457.262 0.299132 \n", + "\n", + " 1_FIT_001_PV 1_LS_001_AL 1_LS_002_AL 1_LT_001_PV 1_MV_001_STATUS \\\n", + "120963 0.00114 0.0 0.0 52.2984 1.0 \n", + "120964 0.00114 0.0 0.0 52.2984 1.0 \n", + "120965 0.00114 0.0 0.0 52.2984 1.0 \n", + "120966 0.00114 0.0 0.0 52.2984 1.0 \n", + "120967 0.00114 0.0 0.0 52.2984 1.0 \n", + "\n", + " ... 3_MV_001_STATUS 3_MV_002_STATUS 3_MV_003_STATUS \\\n", + "120963 ... 1.0 1.0 1.0 \n", + "120964 ... 1.0 1.0 1.0 \n", + "120965 ... 1.0 1.0 1.0 \n", + "120966 ... 1.0 1.0 1.0 \n", + "120967 ... 1.0 1.0 1.0 \n", + "\n", + " 3_P_001_STATUS 3_P_002_STATUS 3_P_003_STATUS 3_P_004_STATUS \\\n", + "120963 1.0 1.0 1.0 1.0 \n", + "120964 1.0 1.0 1.0 1.0 \n", + "120965 1.0 1.0 1.0 1.0 \n", + "120966 1.0 1.0 1.0 1.0 \n", + "120967 1.0 1.0 1.0 1.0 \n", + "\n", + " LEAK_DIFF_PRESSURE PLANT_START_STOP_LOG TOTAL_CONS_REQUIRED_FLOW \n", + "120963 63.8829 1.0 0.83 \n", + "120964 63.8829 1.0 0.83 \n", + "120965 63.8829 1.0 0.83 \n", + "120966 63.8829 1.0 0.83 \n", + "120967 63.8829 1.0 0.83 \n", + "\n", + "[5 rows x 123 columns]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_dropped.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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xLzwwgs9an7961/n7//+7zNgwIA0Njbm8ssv32gvN998c4YMGZIBAwZk6NChue2223rn0AA7wPr163PRRRdl8ODB2WWXXfLGN74xn/3sZ1MURalGzwX469111135p3/6pzQ0NKSioiK33HJLj/mdqcduzV4AdmZb6rlr167NBRdckKFDh2a33XZLQ0NDTj311Dz99NM91tBz2RZCWbwq3XTTTZk2bVouueSSLFiwIEcddVRaW1uzfPnyHb01gJ3GnXfemcmTJ+eXv/xl2trasnbt2px44olZtWpVqeacc87JT37yk9x8882588478/TTT+c973lPaX79+vUZPXp01qxZk3vuuSff/va3c9111+Xiiy8u1Tz66KMZPXp0TjjhhCxcuDBTp07Nhz/84fzsZz8r1ejbwOvJfffdl6997Wt505ve1GNczwUonz/84Q9585vfnH79+uWnP/1pHnrooVx55ZXZc889SzWXX355rrrqqsycOTPz5s3LbrvtltbW1rz88sulmgkTJuTBBx9MW1tbZs2albvuuiuTJk0qzXd1deXEE0/MG97whsyfPz9XXHFFPv3pT+frX/96qeaee+7J+PHjM3HixDzwwAMZM2ZMxowZk0WLFm2fHwZAL7vsssty7bXX5uqrr87ixYtz2WWX5fLLL89XvvKVUo2eC/DXW7VqVY466qhcc801m5zfmXrs1uwFYGe2pZ774osvZsGCBbnooouyYMGC/PCHP8ySJUvyzne+s0ednss2KeBVaOTIkcXkyZNLn9evX180NDQUM2bM2IG7Ati5LV++vEhS3HnnnUVRFMXKlSuLfv36FTfffHOpZvHixUWSor29vSiKorjtttuKysrKoqOjo1Rz7bXXFtXV1cXq1auLoiiK888/vzjiiCN6POuUU04pWltbS5/1beD14vnnny8OOeSQoq2trXjrW99anH322UVR6LkA5XbBBRcUb3nLWzY7393dXdTX1xdXXHFFaWzlypVFVVVV8b3vfa8oiqJ46KGHiiTFfffdV6r56U9/WlRUVBRPPfVUURRF8dWvfrXYc889S314w7MPPfTQ0uf3ve99xejRo3s8v6mpqfjIRz7ytx0SYCcxevTo4kMf+lCPsfe85z3FhAkTiqLQcwHKKUnxox/9qPR5Z+qxW7MXgFeTv+y5m3LvvfcWSYrHH3+8KAo9l23nTVm86qxZsybz589PS0tLaayysjItLS1pb2/fgTsD2Ll1dnYmSQYOHJgkmT9/ftauXdujnw4ZMiQHHHBAqZ+2t7dn6NChqaurK9W0tramq6srDz74YKnmz9fYULNhDX0beD2ZPHlyRo8evVFf1HMByuvHP/5xRowYkX/+53/OoEGDcvTRR+cb3/hGaf7RRx9NR0dHj35YU1OTpqamHn23trY2I0aMKNW0tLSksrIy8+bNK9X8wz/8Q/r371+qaW1tzZIlS/KHP/yhVLOl3gzwanf88cdnzpw5+e1vf5sk+dWvfpW7774773jHO5LouQC9aWfqsVuzF4DXms7OzlRUVKS2tjaJnsu2E8riVee5557L+vXre/yyKknq6urS0dGxg3YFsHPr7u7O1KlT8+Y3vzlHHnlkkqSjoyP9+/cv/UVygz/vpx0dHZvstxvmtlTT1dWVl156Sd8GXjduvPHGLFiwIDNmzNhoTs8FKK/f/e53ufbaa3PIIYfkZz/7Wc4888x8/OMfz7e//e0kf+qbW+qHHR0dGTRoUI/5vn37ZuDAgWXpzfou8Fpx4YUXZty4cRkyZEj69euXo48+OlOnTs2ECROS6LkAvWln6rFbsxeA15KXX345F1xwQcaPH5/q6uokei7bru+O3gAA0PsmT56cRYsW5e67797RWwF4TXriiSdy9tlnp62tLQMGDNjR2wF4zevu7s6IESPy+c9/Pkly9NFHZ9GiRZk5c2ZOO+20Hbw7gNeW73//+7n++utzww035IgjjsjChQszderUNDQ06LkAALwmrV27Nu973/tSFEWuvfbaHb0dXsW8KYtXnb333jt9+vTJsmXLeowvW7Ys9fX1O2hXADuvKVOmZNasWbn99tuz//77l8br6+uzZs2arFy5skf9n/fT+vr6TfbbDXNbqqmurs4uu+yibwOvC/Pnz8/y5ctzzDHHpG/fvunbt2/uvPPOXHXVVenbt2/q6ur0XIAy2nfffXP44Yf3GDvssMOydOnSJH/qm1vqh/X19Vm+fHmP+XXr1mXFihVl6c36LvBacd5555XeljV06NB84AMfyDnnnFN6Q6yeC9B7dqYeuzV7AXgt2BDIevzxx9PW1lZ6S1ai57LthLJ41enfv3+GDx+eOXPmlMa6u7szZ86cNDc378CdAexciqLIlClT8qMf/Shz587N4MGDe8wPHz48/fr169FPlyxZkqVLl5b6aXNzc37zm9/0+Avmhr+AbvglWHNzc481NtRsWEPfBl4PRo0ald/85jdZuHBh6RoxYkQmTJhQ+rOeC1A+b37zm7NkyZIeY7/97W/zhje8IUkyePDg1NfX9+iHXV1dmTdvXo++u3LlysyfP79UM3fu3HR3d6epqalUc9ddd2Xt2rWlmra2thx66KHZc889SzVb6s0Ar3YvvvhiKit7/iqhT58+6e7uTqLnAvSmnanHbs1eAF7tNgSyHnnkkfz85z/PXnvt1WNez2WbFfAqdOONNxZVVVXFddddVzz00EPFpEmTitra2qKjo2NHbw1gp3HmmWcWNTU1xR133FE888wzpevFF18s1Xz0ox8tDjjggGLu3LnF/fffXzQ3NxfNzc2l+XXr1hVHHnlkceKJJxYLFy4sZs+eXeyzzz7F9OnTSzW/+93vil133bU477zzisWLFxfXXHNN0adPn2L27NmlGn0beD1661vfWpx99tmlz3ouQPnce++9Rd++fYvPfe5zxSOPPFJcf/31xa677lp897vfLdVceumlRW1tbfHf//3fxa9//eviXe96VzF48ODipZdeKtWcdNJJxdFHH13MmzevuPvuu4tDDjmkGD9+fGl+5cqVRV1dXfGBD3ygWLRoUXHjjTcWu+66a/G1r32tVPOLX/yi6Nu3b/Hv//7vxeLFi4tLLrmk6NevX/Gb3/xm+/wwAHrZaaedVuy3337FrFmzikcffbT44Q9/WOy9997F+eefX6rRcwH+es8//3zxwAMPFA888ECRpPjCF75QPPDAA8Xjjz9eFMXO1WO3Zi8AO7Mt9dw1a9YU73znO4v999+/WLhwYY/fra1evbq0hp7LthDK4lXrK1/5SnHAAQcU/fv3L0aOHFn88pe/3NFbAtipJNnk9a1vfatU89JLLxUf+9jHij333LPYddddi3e/+93FM88802Odxx57rHjHO95R7LLLLsXee+9dnHvuucXatWt71Nx+++3FsGHDiv79+xcHHXRQj2dsoG8Drzd/GcrScwHK6yc/+Ulx5JFHFlVVVcWQIUOKr3/96z3mu7u7i4suuqioq6srqqqqilGjRhVLlizpUfP73/++GD9+fLH77rsX1dXVxemnn148//zzPWp+9atfFW95y1uKqqqqYr/99isuvfTSjfby/e9/v/i7v/u7on///sURRxxR3HrrreU/MMAO0tXVVZx99tnFAQccUAwYMKA46KCDin/5l3/p8YspPRfgr3f77bdv8t9xTzvttKIodq4euzV7AdiZbannPvroo5v93drtt99eWkPPZVtUFEVRbL/3cgEAAAAAAAAAALy2Vb5yCQAAAAAAAAAAAFtLKAsAAAAAAAAAAKCMhLIAAAAAAAAAAADKSCgLAAAAAAAAAACgjISyAAAAAAAAAAAAykgoCwAAAAAAAAAAoIyEsgAAAAAAAAAAAMpIKAsAAAAAAAAAAKCMhLIAAAAAAAAAAADKSCgLAAAAAAAAAACgjISyAAAAAAAAAAAAyuj/B7mjexRE2hbCAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plt.rcParams['figure.figsize'] = 30, 2\n", + "plt.plot(test_dropped.to_numpy()[:, 7])" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import MinMaxScaler\n", + "\n", + "def scale_data(train, test, validation):\n", + " scaler = MinMaxScaler(feature_range=(0, 1), clip=True).fit(train)\n", + "\n", + " train_scaled = scaler.transform(train)\n", + " test_scaled = scaler.transform(test)\n", + " validation_scaled = scaler.transform(validation)\n", + "\n", + " # train_scaled = scaler.fit_transform(train)\n", + " # validation_scaled = scaler.fit_transform(validation)\n", + " # test_scaled = scaler.fit_transform(test)\n", + "\n", + " return train_scaled, test_scaled, validation_scaled" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": {}, + "outputs": [], + "source": [ + "train_values = train_dropped.values\n", + "test_values = test_dropped.values\n", + "validation_values = validation_dropped.values" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [], + "source": [ + "train_norm, test_norm, val_norm = scale_data(train_values, test_values, validation_values)" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(784571, 123)" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train_norm.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(146883, 123)" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "labels_reshaped = np.zeros_like(test_values)\n", + "labels_reshaped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(25920,)" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_labels.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [], + "source": [ + "for idx in range(0, len(test_labels_clipped)):\n", + " if test_labels_clipped[idx]:\n", + " # labels_reshaped.shape[1] == 51 aka num_feats\n", + " labels_reshaped[idx][0:labels_reshaped.shape[1]] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(25920, 123)" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_labels_reshaped = np.zeros_like(validation_values)\n", + "validation_labels_reshaped.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": {}, + "outputs": [], + "source": [ + "for idx in range(0, len(validation_labels)):\n", + " if validation_labels[idx]:\n", + " # labels_reshaped.shape[1] == 51 aka num_feats\n", + " validation_labels_reshaped[idx][0:validation_labels_reshaped.shape[1]] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": {}, + "outputs": [], + "source": [ + "np.save('labels.npy', labels_reshaped)\n", + "np.save('labels_validation.npy', validation_labels_reshaped)\n", + "np.save('train.npy', train_norm)\n", + "np.save('test.npy', test_norm)\n", + "np.save('validation.npy', val_norm)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "val = np.load('../processed/WADI/validation.npy')\n", + "test = np.load('../processed/WADI/test.npy')\n", + "validation_labels = np.load('../processed/WADI/labels_validation.npy')\n", + "test_labels = np.load('../processed/WADI/labels.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(146883, 123)" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "validation_labels = (np.sum(validation_labels, axis=1) >= 1) + 0\n", + "test_labels = (np.sum(test_labels, axis=1) >= 1) + 0" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.rcParams['figure.figsize'] = 30, 2" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAACVEAAADFCAYAAABXNSDmAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAACr5UlEQVR4nOz9eXxjZ302/l9H+2JJlnd7xrPvk5lJMkkmk4SENEMmkELCDi2lpIU+pU1bmv6gze9poUD7hNIW6BIKBfIQHloIUMKWkG3IJCGZzCSzZPZ9sT3eN8mSrP18/7glWbIlnVvjY1mSr/fr5Zdt6Ug6sqWjcz7nc1+3oqqqCiIiIiIiIiIiIiIiIiIiIiIiogXKMN8rQERERERERERERERERERERERENJ/YREVERERERERERERERERERERERAsam6iIiIiIiIiIiIiIiIiIiIiIiGhBYxMVEREREREREREREREREREREREtaGyiIiIiIiIiIiIiIiIiIiIiIiKiBY1NVEREREREREREREREREREREREtKCxiYqIiIiIiIiIiIiIiIiIiIiIiBY003yvgB6SySR6e3vhcrmgKMp8rw4REREREREREREREREREREREc0zVVUxMTGBjo4OGAzFs6Zqoomqt7cXnZ2d870aRERERERERERERERERERERERUYbq7u7F48eKiy9REE5XL5QIgnrDb7Z7ntSEiIiIiIiIiIiIiIiIiIiIiovnm9/vR2dmZ6S0qpiaaqNJT+LndbjZRERERERERERERERERERERERFRRrq3qJjik/0RERERERERERERERERERERERHVuJKbqF588UW8/e1vR0dHBxRFwU9+8hPN2+zevRvXXnstrFYrVq1ahW9/+9szlnn44YexbNky2Gw2bNu2Dfv27St11YiIiIiIiIiIiIiIiIiIiIiIiEpWchNVMBjEli1b8PDDD0stf+HCBdx99924/fbbcejQIXziE5/ARz/6UTz99NOZZR577DE88MAD+MxnPoMDBw5gy5Yt2LlzJwYHB0tdPSIiIiIiIiIiIiIiIiIiIiIiopIoqqqqV3xjRcHjjz+Oe++9t+Ayf/mXf4knnngCR48ezVz2gQ98AOPj43jqqacAANu2bcP111+Pf//3fwcAJJNJdHZ24k/+5E/wV3/1VzPuMxKJIBKJZH73+/3o7OyEz+eD2+2+0qdDREREREREREREREREREREREQ1wu/3w+PxSPUUmeZ6Zfbs2YMdO3bkXLZz50584hOfAABEo1Hs378fDz74YOZ6g8GAHTt2YM+ePXnv86GHHsJnP/vZOVtnIiIioqo2PAy88ALgdgOBAFBfD4yPT313ucR1N9ww+8c6fBi4dAmwWoFgcOZjud3icrMZUFUgHgccDmBiYuaydXXA5CRgMgGKAkSj4jJFAX7jN2Y+9quvAn19uY8VCAAWS/7HiseBt7xl9s+ZZu/MGeDo0ZmvAYcDiMXE/9xkAhYvBjZunN91na2xMWDvXvHaLva+MBqBm24CvN75XmOqdM8+m39b6feL11gsBtx553yvJRFR5RgcBF55ReyvKgpw113zvUZEREREhQ0NAS+/DFx3naiLzNa+faIGMb1eNzEB2GxAIgEkk1O1vZtvBlpaxG0nJ4Gf/xxobJw65sxXx3E6gdtv116XRELcn8eTWwtcu1Z8FXPiBNDfP/N5pGuBsRhw/fVAe/sV/qGISvTii8DISOH3BQCsXq392iaiilPydH6l6u/vR2tra85lra2t8Pv9mJycxPDwMBKJRN5l+vv7897ngw8+CJ/Pl/nq7u6es/UnIiIiqjoWC2AwAOGwOMkeCOR+n5wUy+ghFBLfI5H8jxUKiZNV8bgolCjK1Mn/6cuGw+L6REIsbzCI25vN+R97eHjmYxkMhR+LiaWVY2Ag/2sgGhVNRcmk+LkW/meKIgqTWu+LaBSw2+d7bakapBsNp28r068xj2e+15CIqLIMDEztI+q1D0xEREQ0V8bHxfFeXZ0+9+d05q/XGY3i+DKZFMula3u9vVO3NZlEc1X2MWe+Os7IiNy6JJPiuU2vBcbj2re1WvM/j/R+XjIpnitRuYyMFH9fqKpoPiSiqjPnTVRzwWq1wu1253wRERERUUogoF18CIfLsy56CATyX15qYs/Y2OzXhfQhu/8uW4SrZCaTKOhpiUYLv9aJsmlt37mtIyLK1dg49fPExPytBxEREZEMr1c0X+h1bBcMlrZ89r5TJCIGKGqRbfhSVdFENV0kon3bdLJPIfE44PPJrQeRHurrtZdhDwNRVZrzJqq2tjYMDAzkXDYwMAC32w273Y6mpiYYjca8y7S1tc316hERERHVHre7cHpTmkxTh4xyjOYv1CxVajGJ06RVjnSCmZbswl21CoflRlSaTCJGn0iLqha/nts6IqJc2U3Z/KwlIiKiSjc2JtKZ9Dq206oRTjd938nh0L6N7GBNgyH//phMo0k6MasQk4nJzFRe4+P6LENEFWfOm6i2b9+OXbt25Vz27LPPYvv27QAAi8WCrVu35iyTTCaxa9euzDJEREREVAK/X3t0ltGoz2OVIzmnULOUzGgfmfuh8jOZ5JarhSQqp1Ou2TA9NRuRFq3XE7d1RES5sk9A5ks+ICIiIqokeidRKUppy2c3IoXDcgPhZOuMiUT++5N5rlrN8EyionKTadqrhQGiRAtQyU1UgUAAhw4dwqFDhwAAFy5cwKFDh9DV1QUAePDBB/HhD384s/wf/uEf4vz58/jUpz6FkydP4qtf/Sp+8IMf4M///M8zyzzwwAP4xje+gUcffRQnTpzAxz/+cQSDQdx3332zfHpEREREC5BMEpVMFLeMciSeFHqMUgsjTGepHLIFvFIb5SpRMCim6tNSalGTFi6tqRg48paIKFf2PiObqIiIiKjSpZOo9Dq2k5kqL1v2MafVCtjt2reRrWmYzYDLNfNymUYTrWmZjUb5aQWJ9CBTm66FAaJEC1DJTVSvv/46rrnmGlxzzTUARAPUNddcg09/+tMAgL6+vkxDFQAsX74cTzzxBJ599lls2bIF//zP/4xvfvOb2LlzZ2aZ97///finf/onfPrTn8bVV1+NQ4cO4amnnkJra+tsnx8RERHRwiOTRKVXIaYciSeFHkOmiJON8cmVQ7aAp1UgqwZ1dXJJVIqiX0Ic1Tat5sJaeN8QEekpewoaTudHRERElc7lEklUeh3bldpYlJ0eHo3KDcSUGTwGiHplvucl02ii1WiVTOo3aJRIhsxUl0yiIqpKkvNoTHnzm98MVVULXv/tb387720OHjxY9H7vv/9+3H///aWuDhERERFN53BoN2Po1VDU0DD3UdmFGr60GsWmkzmwpfJwueQKZFbr3K/LXAsERDFRawrDREK+6EgL2/h48fdGLbxviIj0lJ0+VY6pqImIiIhmIz3dnV7N36XW7QxZ+Rsmk9zAMNlGLZNJ1ISSydzLZRpNtOpIiqKdzE+kp3hce5mREWDVqrlfFyLSVclJVERERERU4aLRmcWI6Roa9Hms0VF97qcYvUbeyRzYUnnIFvC0XsfVwOmUK+JZLGz0IzlaSVRFBj0RES1I2fuAnN6ZiIiIKl16EJZe0xCXmoSTnR6eTMqth98vd9/x+NwlURGVm0zdklNMElUlNlERERER1RpF0V5Gr+YnrZP5eig08k5mJFy2WmjIqRWyTXy10PgWCsmlpkWjQDA49+tD1U8rSbAW3jdERHrKngK6HFNRExEREc1Guq6nVxOVTINSNper9MeQrQ8ajfkHkLnd2rfVeh7JZOmp9USzIVObzm5KJKKqUfJ0fkRERERU4Uym3OjtfPRKopIdaTYbhZqfSm044RRXlUO2ic/pnNv1KAeLRXt6zfRyHJ1GMjweIBwufH12swARESEx7kMsAcQAxJ31iPnDiCVVxBNJxJMq4gkVsdTPiWQS8YSKRFJFLPV7LPV7IqkiqaqZ6zPLp66LJcT12csmkioSqopkUkUiCcSTydzrkkBSnfo9/V2BAqfVhL+79yrYLRL7EURERFQ7YjHRSKXXdH6NjaU1UmUP3DEYppKxZG9TTDIpmkqmP7dix7hp9fXApUuFry/UoEU0V0Ih7feHVo2eiCoSm6iIiEhXiaSKSDyBSCyJaCKJSCwpfo8nEY4lMBnLum7acuJ7ArGkilg8iVgimftzQkU0IX5OF7rTxe5oXBSjFQVQoGQG7NRZTfjuR7fBZmbhmRaQcFiMVis2hZheSVR2+9yP6C+UquL1lvY8AgF91odmr6FB7nUjO+1fJUsm5aZXi0bFa1SvIinVLr+/+GhHvaZALUBVVYRjSQSjcUxGxb5dOJZAOJbEZCyBaGq/LRoX+3bJVANBuqkg/RVPiqYCFUCb24bf3NIOh4UlCqJaoqpqznFgOJbMbDcisQTCcXFZehsSTR03ii+xPYnExfYkvUwskcwcE4rtjTq13UmIY8L09id9eVIFgPTxYAj41a55/KuUxmxU8IV3b57v1SAiIqJycjpFHSEQAJqbZ39/pSZRZU+bF4uJpietRhHZJCpFyd9UItNoojWQM5EQx8PlSM2nmpE+Zkkfg0RSxyfZl6WPRdKXR+NJGBTAGqyDLRrGBiewqlD/nkwTIhFVHL5ziYhqjKqqmIwlEIjEEYokMie4wqlmpnQzUjw1ojaWVVxOF6DjyWSmYJ3eMYxlGqJSBex4AqFoApPRqeK2uH+JE8Vl9qffO4h/+61rYDWxkYoWiLq6og1UcRUIWhwIjE8iGIkjEIljIhwXP4fF78FIHN959RKWNzrR2eBAKBrPvOdDsantymQojEhcgdUAeEzADzepaCpxlj1NhVJVSm3e8npnvy6kD9nmt+zCXZVKJJKIJoFIDIgkAbMBaMj39rRYaiN5i+aezVZ8etIC2zpVVRGKJlLb/Bgmwrnb/2AkjmBqOx9IfTYEwnH4wzH4U8v7J2Pwh+NIJPXf33viSB8e/b0bdL9fIpKjqiqC0QSCWduIYCSR2S8MReMIRBIIRGIIRRMIRRIIxRKYTO0jimVyGytD0TjmYHMxa4oCmA0GmI0KjAYFZqMBJqMCk0F8NxoUmA0GGA1K6nJxncEAmNKXG5TM9UaDAUYFMBkNMCoKDAYFRgOmflbEsoas200tN/WzQYFYTlHwD788iYlIHD8+eBnbVjTgndcsnu8/GxEREZWL3y92WHSqY6l1dVBKaaQaGQE6O8XPNptcupNsEhWQv2FKJsFbK3XZZBLJzVT10o1NOee4YuIYZDI1QGMydd5LnK+aGtQVSQ/UiKcGbmQ1P00N7pg65xWNJ2d5zCJezy9uTWJJvnGRodBs7pyI5gmbqIiIKkg4lsBoMIqxUBRjwRjGQlGMT8bgnxRF7IlwLFOgnoyJJqZQVOwMBqNihzIQjUsFXpSD0aDAYjTAYjLAZjbAZjbCZjLCZjHCmrrcajLAajbAajLCYjTAbFJgMRphNk4Vs60m8bvFZBCXpYrVZqMh9SV+NhpE/JSqiq//9+pFPH1sAM8cH8DvfHMffvCH2+f5L0J05RJJFYFwHBORWObEdvokt39SnNyaiMQRisQRHPPD36sgCAUTcSCYyPpKApGkAqA/9VXc0EQE+y5qNbwoCCaB0Tjw4ePAk1frvBEqlKpSahLVXCdmkTyXS+7/kV24m2OReCLTJJJuKplINZiIE8ficzZzojiWSKVpiJPG4ZhIU8wZuZZpLs4tOL6zWcWX10x7n0SjorDCqdhIQzyRxFgMGI0BvjgwEgPG48BYHPDHFfjOj8B3dj9Gg9HU/uNUQ0Rc524Gm9kAu9ko9vFSX1aT2MezGKcaFKa+RKOB0WAQDQYGA/aeH8H54SBeOD2EwYkwWlxMYyOajfQx5WgwipFgFOOhKMaC4rjSl/ryT6YaJCezGypjc9rwZDQocJiNsJqNcFiMsKWOAe1mI6ypY8XM8aHJmPpuyGxTrKnjSEvOceHU9enjQpMhtQ0yid8tRgPMQ4MwH34DJgNgVgDjO94+d09UJ+++djHe9/U9OHLZhz9/7A02URFVoHAsgZGg2Mb6J2MYn4xhPCRqef7U9nYiHMf4ZDSz7U03pt66phn/+TtboaRj1ImIsnm9gKoiMTKKgNWZ2WdLD26ZSO3H+VM1jEBWTTB7sEx6QKQZKr6+Fri5XvLxswe0hcOiVqE16Et2UFgyKdKtpqfzTE5q3zaRKH59PC4Szd1uuXUh3SWTKiYicYyHopljjuxBXIFwHL7JGILRBELRqddrekBGICIao+ZrMIaiABajAQ6LMXNMYTWljl9Mxsx5LKvJgKSqItw/iOeHxSC3vzqr4L+vyrPSHNRLVJXYREVENIdUVew0Dk9EMDgRwUggiqGJMIYCEQxNRDAajGE483MUkzGNA4ESKArgtJhgt+Tu5FlSjUlTzUipncFUQ1L6OovJAJvJAGvWyTCrKVXcNorv6fu2mqaK4OmGKfE48zvf8y2rm/DD17vxyR8dxr6LozwxRxUhGk9iPCROaqVPcKVPaI2HohgNxuBLFVmzUz8CkQJT2hWkvZtnMRrgtBrhtJrgspnhsprgtBpRZzPj52/0Zpb7q7eug8MiTnI5LCbxc+p3+7nTsAwO4HsDCr5+WcHxoILusIpOPd9qhQ42mURVvcJhueWuIIkq/R4bDoim5PFQDOOT4nv6JHK6mDM+OfU9HCuS7KOzx4cU3Nus4rbsl6TJVHyKNqppsUQSgxNin3DQH8bgRATDAfEl9h8jmWaI8ZACFcVOuKlAX+EmWYMCuO1m1FlNqLOa4LKZ4LSa4Ext3x2Wqc+FOqsRbrsZbpsZLpu4zGM3o85mgsNshMEw+xN/qqpi+YNPAgD+6LsH8KOP3zTr+6wW4VgCg/4I+nyT6PeHEYklsb7djU2LOXqaZgpG4ujzhdHvC2NwIox+fxiD/og4tvRHMDgRxkggiomS9xlzGRSk3v+mzH5ielvhsJjgtBjhsKa+Zx1vOrP3EVPHn/asfUbzfB4bXvIBRjUVQVVkuusKYrcY8X/vux7X/d1zAIDu0RA6GyRSIIhoViajCQxOhDHgT+2XTYQxlNkvi2I4IOp3I4HZ1fCePT6A+//7IP7pvVtgtzA1napDMqliNCTeB8kksLbNlRnQStqSSRXjkzGMpM4FDAejGA1EMBaKZQZVjwZF7cI/EYJv0oaJ108AODHrx44AuO+4gsc3q9hYJ3GDK0miikblVsZkEvc5ncwUfHGN/VwmUekqnWY9HBD1iNFAFCNB8fNIQDQRjwSnam/+sKhv6z3A3242wmmdOq5wWEyZAV0OiynT2CQGeKWanMzi3Fb2YC9b1uANhyU1oD91Hix9jstsVEprcH78cfyrw4QvdRnwik9BQlVhnH7zUgYAE1HFYBMVURGqqsI/Gc8UKPt9ovlleCKKeDKJG5Y34Dc3d8z3as6LRFLFSCCCy+OTGPCL4kL6+1BAnPxRVeB/3bYC77q2NkdMqqqK8VAMPWOT6PVNond8En2+MPp8YQykCtt9vjAi8dJOypqNCrwOC7wOC+od4kRVvcOMOqs4eVVnnSpUp4vWdrMxc9KrzjZ1Eowj2oD3bF2MT/7oMADgxv+zCyc//1ZYTPPb3EW1JT31yfBEJPUZIQ4mhyYiGAuJg8qh1Inw4YkI/OHZndiyGA1iW5B6r6dPatelT3LZTHBGJlHXcwl1NhNcRqDOBDgMgCv13WkEnEsWwXLdtQUfZ21rHf7pmdMAgD+8bWXhFboQBuzAXy1V8fXLYpvzx6cU/HizCpNem6BCzVIeT2lx4Uyiqhz5otvzSRXuovEkBvziszVzwjjrZMZoMHVCQ4eTx66sphLxXhPvL4dl6jPYOe0z2G5JpfBkFWXSI9NsiRhsz++CxWKCEcCWvQr8CQW/e9yACzclkfmoTia1R1VSVYrEE+gdD6N3XOwvDvjDuDw+id7xcOp1LU7GyVOgQEW9Cag3iekhvSag3gy4jYDHaoBn03p4nZZM85PbLr7X2y2wmQ0VtY+oKAretqkNTx7px8HucfT7wmjz1EbTuz8cw/mhIC4OB9E9GhL/d18YwxMR9PvDBf/vP/hf23HD8oYyry3Nt1A0jksjIVwaCaJ7dBI9YyFcHg+jZyyE3vHJkvYhTQYFXqcFjU5xXOl1muF1WOC2i+NLl82U+m6G2za1P+mymWA31+BxZPZJuUqJbZbQVGeFQQGSKvA3Pz2Kb9/HKU+pdPFEMnN82u6xobHOOt+rNG/CsQQuj0/i8tgk+n3hnFpev0/UfidKPF43GxXUOyzwOsyot09tZ70Oc6oRXeyHeR2WzD7Zo69cxPf2deGJI32IxJP45u9eN0fPuLqpqopwLCkSd1LJOyKtRKQBB8LxzBRN4XgCk9FUInBquqZwbGqapnBcTPFkMRlw+9oW/Okdq+f76VWc9Pujd3wSfePhVI17MnO8IgYHR3KSYW5c0YDv/N62BV1nzT5/NJhqvBxMNWEOpQbEpOsWY6FoidOiT+2PWU2GzDFd9jGeO2tfzmlN1TFStcG61O9JVcVHvv4K+icTeNcR4MurVbytSeOhs5OcZJOoZPcf4/H89zc6CqwsUn8EtNeBSVTSApG4GJzhF59/6fN76dfwcCCCAX/kihuG7WYj3PapY450jc2dqWOL81fu1HXpwVxOa+7gDIfFVNnNmm43PqqE8KUu8evnLyj42xXT3udXMECUKl8oGkfPWOq4fWwSPal9zEanBZ+956r5Xj3SAZuoaEELxxLoGQuhe0xs3HrG0gfPkxicEDsNxVIJvrPnEn51chCfeftGeOzVMZpRVjyRRJ8vjO7REC6NiqJtz9jk1MGULyy10//AD97ArpOD+Of3boHNXH0jq1RVxXAginNDAVwcDuLCSPokyCS6RkPSyTB1VhNaXFY01lnQ6LSi1W1FU50VDXUWNNVZ0eyyotFpSRVcTLVXtJ5HiqLgU3etxRefOoWkCqz561/i7Vs6cP/tq7C2zTXfq1c1VFXF5dR2YEOHG25bbW3zCoknkhiYiODyWOqzwR8RjZIT6WZJURgp9YDSoABehwWNdeJ9X+8wp75SP6cuyy6OpA86pYpTExPAc5eK7+lNBkta54IaGgCfD4oC/MfaJD5+yoDDAQXvPQJ8YaWKtZJp3kUVSpDy+0u7H45GqxxZse1JFRiOAT0RoDcCXA4DvVEFvRGg//QI+p94DsOBSEnnPA0K0JA+ceywwOMQJzLq07/bzXDbTVM/28zwOEQRR/fizHAQSEQBRTzn/1yv4gNHxWN8bwD4rTZ9H47mx0Q4hgvDQZwfCuJCqmHm4kgQl8fFcYXM69dsVNBcZ0Wz2ya+u6xorrOgsU7sNzbVWeB1WuD91dNocFpnjm5Ma6wHblqu59Obc//03i148kg/EkkVv/OtvXjkI9dXVeLKRDiGU/0TODUwgTMDAZwZnMDpgQCGJiKat7WaDGjz2NDmtmHvBTFC9X1f34Mf/eF2XLds4TZSpU+cVlrT32ypqoqhQASn+wM4PTCBs0MBnBsM4OJIEAN+7deLy2oSrxePDc0uK1rdtszxZKvLmtpeiM+2Wvq7zVogMPWzVoJBhfnEjjX40rOnsfvUEO76yov4vVuW496rFy3oE9bTqaqKaCIppl2OJTAZjSMQSSAcS8BlM2FjR20fA6TrVpdGgjg/HMSlkWCqIVPU8kZD0Zz9kP/5+HZsXVq7ny++yRgujYj9sUsjIVwcDqJrNITusZDUdhaY+mxucYl9sFa3LVO3a6yzosFpQVOdBfUOC9y20mt4f//OTVjdUoe//flxPHdiAC+fHcbNq7Q6GqpXLJEU6cCpRO6xYBRjqbRgMc3h1JRkvtSUiHM1FTUAHOwax/mhAP7unZtQZ11Yp8fGglFcGAniwlAQl0bF+6M7dfJ3UGK/FRB9MnazEaFoAq+eH8VdX3kRzz5wW2U3OVyhdE2w3zeJy+Nh9GUGxUQwkDXAK1riAGqP3Yym1DmBxtS2pNE59d3jMMMz3I/6C2fgvvVmuDqaYTXN7rzKj97kwodfGsP5SQV/cgoYian4UFuRvqfsqfWsVsBu136Q6dPzFWI2Ay6XGEiWTabRxOcrfr3RKD+tYA1LJlX0+8NTA3lSNf1en3gd9/nCJc14YDMb0OgUr9cGpyXzc7q27U01E3vsorbmsZtn/ZqtGhMTcBiN2OBUcTyo4OfDwJ8vATzZb4eREWDVqnlbRbpyo8EoLgwHcHFYDHa6OCLOl/eMhjBSYFDciiZug2rFwtpLpAUrXdQ+0T+Bc4MBnB0M4PxQAH2ptCQtHrsZLS5rplDZ7LLiwKUxvHZxDD8+cBm945P41u9eD2cVHniFonGcGQjg1MBE5u9ybkic+NE6UDUoQKvbhnaPDS0uG1rdVrSkTvq0uK341clB/NfeLjxxuA8A8KX3banonafRYBQn+/w43ufH2cGpv4nWKLSmOisWee3o8NjQUW9Hm9uWKba0e+xodlkZzT3P/ujNq7Dn3AheOjMMAPj5G734+Ru9uGNdCx5823qsapHJMV440g1TRy/7cah7HEcuj+PoZT98kzEAojHh//+29Xj3tYuq/uRMLJHMHEh2jYrCUffYVOPo4EREepSY02JEY+okVoPTIr47LJmfG+ssaK4TJ7c8dvPcFpgCAXGCqFgBQ6+p7bIiid/aBPw7kvjUGQUHJxS89RBwfyfwZ515ooxLUShBymYTo9dkTUzMYiVoNqLxJLpGQ6mTGUF0HR1Gt1/BpTDQEwaiaqEXyNS0fxajAS1uK1pcVrS4bJn3VVOdFV7H1PusMZW+o8c0Y7qoq8uZpu9GD2BRVERVBf9wScF7WlRYDBDpXLJFR5o3wUgcJ/v9ONk/gdP9ogHi7GBA86Sc3WzEIq8d7R4bWt02LKq3o6Pehha3aJ5pcYnXsdTrtrk+t6g9YyV1apItI4fFhBc/eTve9R8v48xgAG/9l5fw13evx/uv76yofQ1VFcXoIz0+HOsVxw0n+/3oHi38/2h2WbG8yYmlDQ501NuxyGtHc5010zhV75hqdpkIx3DPwy/j/FAQf/HDN/Dd399WVc1kMibCMfSOhzPN6QP+cE66YHoKE99kDPGkilUtdXjyT99UlQ0j8UQSF4aDON7nx/HU6+VYr79o8pzHbsayJic6vXYs9jqwyGvHYq8di+rF9sO1QAYy6C57ulyZ6WgqyP23r8LzpwZxsGscJ/sn8KkfHcYPXuvGF969CataqndQUDKpIhiNYyI1bXm6YSIQEeky6bSZUDSOiUgcwdTXRDiOUFRcHojEM41TxY7Z3rGlA79/y3Js6awv3xOcA6qqomdsEmcGJ3CyfwJnBwI4NxzE+SHtupXRoGT+Ru/+jz146F2b8MEblpRjteeMLxTDqYEJnOr34/SA2B87O6TdwOywGMU2tV7U8RbV29HmsaHdY0ebR9Q1Xda5H9z4oRuX4vuvdeNk/wR++5t78cEbluDOja1485rmitr3KSQUjWPQH8FIcGqaw+GJKIYCYQxPZE17GIxmaklXyqAgNa2sOZO2k07jt6emaZr6ykoENhthMYlpmqyp73/9+FFcHp/ETw714shlH3b9xZv1+YNUmOFARDT490/gzGAA54ZE8/Z4qPj/wmkxivdGvR3tbhva61PHKm5xDN7issLrtMBsNODpY/344/86gPPDQXzthXP449urr0FAVVUMTURwKVWr6BmbRE+q6bJ3XKTzyNYE0+ePWtzWzICYFpctpyaYrl1I7de6E0DfaQBRQIfzKYvbG/Dk1aP4o5PAr8YU/M15Bc+Pqfi9DhW31Oe5QXZ6eDQq0qi09qGKHaNmi8VEfW56s9PICLBiRfHbNjYCly4Vvj6ZBCJyDYHVTlVVDE5EMgO5zg0FREP1UBA945NSzX0uqwktbnFs2uqyoTV1Tis9UCMdBMAZT4qw2YBYDN/ZoOK61xSMxBT83nHgvzaqsKXfug2127xeKwYnwjjdH8DJfj/ODQVwZkDsV2p9brpsJiz2OjLH7ItTx/FUGxRVraIc6wL8fj88Hg98Ph/cjGlc8IYDERy57MOxyz6c6JvAsV4fLo4UPsFaZzVlNmyLveJkRrvHjlb31EFCoQSlnx66jD/7/iEAwNpWFz6xYzVuXNEIr9OSd/n55puM4dhlH472+nDksh/Hen24MBws2EhmMRqwuMGOpQ2ieNtRb0+d8BEfBs11VpiMxXf6nz7Wj//1//YDEH+jj926AjvWt6DeMX9/o3SDyLFeP45e9uF4ryhm9/vDeZc3KMBirwPLmpxY3ii+L2lwYGmjA4u9jqpM2FqIVFXF65fG0Ds+iV8c7sOzxwcAiGLi3Zva8Tvbl+L6BTraPxxL4OhlH167OIZD3WM40DWet/BoMig5zZXvu24x/v6dm2DW2A7Mt3AsgQvDuSNRL42KRDmZgojJoIgCUurEd7pJsi31e3p0akU10o6NAc8/L0Z3FVJfD7zpTQWv/vdfnclM53fxC3cXvp+jR4ELF3Iu6goDn7ug4LlRcYB9W72Kf1+rwnWlf6KGBuDmm2de/uyzopAjy+MBbr31CleCZAQicZGskdW0fm5IjP4u9l5ToKLdAnRYp74WWVW0L1+Etg0rUwUcS3UWbYaHgV27cgqOhyaAew+LbeftXvH+cEZCwB13AE21Mwo9HEvgzEAAx3p9ON7nR+/4JP7g1pVVM03ZaDCKwz3jONIj9p+P9xVvlmmqs2JFkxMrmp1Y0ujAkgYHOlPHGA1OHV+/P/qRGA1ciMsFvPnN+jxWmXWPhvAn3zuIQ93jAIAd61vx9++8Cq3u+ZneLxiJ41D3OA52jeFg1zje6PFhOJC/KN/usWFNqwurW+qwpk18X9VSV3LTy/mhAN71H69gPBSDy2rCb6ZO/ldD0396uvP0aOfusUl0j4bQMxYSqcZjk1c05epvrGvBV3/72oo+7ko3N+y/NIaDXWM4fNmHE33+vMnWigIsa3RmXiNrWl1Y2ujAiqY6eBxskpoT584Bx46JP348DrzznfO9RiWZjCbw9RfP4exgAM8eH0AknoRBEU0YH3vTinlrtozGkxgLRTPNj+Mh0SwxlkqR8YdjCITjmYQZf6pRaiIcRzAan5OZFS1GAxypaWDGQlGEolOJwdWU8BdLJHGqfwKHe3w43ifqmyf7/AhG8ycgKwrQ4bFjWZPYlmTXrFrc4qT9RDiGP/neQbx0ZhgGBfjsOzbiQzcurYr96wF/GG90j+PoZR+O9vpxos+PPl/h48D0PtmSRgeWp2p4nQ1i38zrqJykvolwDFv/7rmcE92dDXZsW96I925djG0ryj/9T3p6pwF/GEOpWRrSUzwN+MMYDohp3UIFXouFKArgtpnFNLPOdGKJ+O6ymeGxT01NVp+aCjE91axTx5P3qqri7584gW/+WtQw3rS6Ce+8ZhF2bGitysT1ZFLF+eEAjl72Z5q2T/T5C6ZjAECb24blTU4sa3JgSYMTyxpFo/+SBkdOc7+M+//7AH6RGjz9u9uX4iM3L8fyCkzgSNcF03WKc0MBXBgWs00U2q6mmY0KWt02dHhS541SA6hb3VY0pxrLml2Fzx9dsQsXgP37RS2svX329/fKK8DICKJJ4B8vKfhGb+7/+U87VXykXUVD+m2wfv1Uck44DDzxRG5Tej42G/CWt2ivSzIJ/PKXM5OoNm7UbqI6fLh4E1UsJv5mra3a61FFJsIxsS/Q78eJvgmcHpjAmYGJotN9mwxKauC/GMizKDWgp8NjzyTbLrQ0vjnx5JNAQmxHjgaA9x5RMJlUcJ1LxWObUoOKW1uBGzgtdyVIH7cf7vHhcM84jvWKgXHDgcKfmx0eG5Y1ObG00YFljWK/cklqP7vWZqhaCErpKWITFVW1RFLF8V4/Xrs4iv1dYzjUNY7L4/lPbLS5bVjX7sKaVhdWNjuxqqUOSxudaJzlCY2Xzgzhj757IFMMtpoMuHVNM+7c0Io7N7TNWxE0mVRxbiiA1y6O4UDXGA51j+PsYCDvso1OC9amCv0rW+qwqrkOy5qcaHPbdElxeO74AP78sUOZv5HdbMRdV7XhvdctxrbljXMe9xuMxHGwS5z82N81hsM9voKjf5c0OLCuzYV1bS6sbnVhdWsdljU6K7pgT1fm6GUfvvzsaew6OZi5bFG9HTcsb8DbNrVjx/qWiims6c0fjmH/pTHsuzCKfRdGcaTHh2gi98DVbFSwqsWFqzs92Ly4Hld1eLCmrQ6qCjz05Ak8ukccsK5uqcOHblyKOze2ot0jEe08h0LROI73+jMpcqKBI4he32TRAr3FZECn147OBkemqTZ9YLmoXjSMVkyijayhIeDFF4sXOFasEAWKAqSbqF58sWCc9g8HgP99TkFUVbDGoeIb61QsvZKXSaFCzFNPiQKJrCpuLKg0qqqizxdOndgRhdrjvf6C+2GAGPW9vMkpDjhDo1iCSXTagKU2oM0CmPP1Y65dC6xZM3dPpBxCIfFandbU+OwIcP9pBZGkgmU2FY+siWHFu94qXu9VKJ5I4mT/BN7oGccb3eM4ctmPMwMTM5JNzUYFn9y5Fn9w68p5WtP8EkkVJ/v9OHBJNBMf6BrDpQIDMVpcVqxvd2Ndm0vsO7fUYWVzXfmKJ889V3yUr9sN3HZbedZlDiSTKr724jl8+dnTiCVUuGwm/PXd6/G+6+Y+lWpwIozXLoxh34URvH5pDCf7J2Y0gBoNCla31GFDhxsbOzxY3+7CujY3GnQcSNM9GsIffnc/jvWKaWvNRgUfvGEJ7quAE1PpASlnUqkf54cD6BoVTVJ9vrDUiGeP3ZxpThcnoNIj9sVo/XqHGV6HBc+eGMDnf3Ec0XgS1y314r8/dmNFJVJNhGP49ZlhvHhmCC+fHUHX6MxthsNixLo2FzZ0uLG+3S32qVtdTCoutxMngDNnxFn8Kt8fvDAcxN8/cRzPnRDHsQYFuGN9K+7c0Irb1jajxXXl+xGqqmIkGMWgP50gE8FIIJrz80gw9XsgUvTEnSyTQYHLZkKdzQSX1Zz6Ln53WExwWoxwWkVzRfrnOqsJdosRdZk0GvG702KcMdDv9YujuP+/D2YGzf3BrSvwwRuWYGmDo6KO8SbCMbx+aQyvXxzFaxfH8Eb3OCJ5tqcWowErUjXN9e1uLE81b8vWreKJJH7/0dfxwukhAMDyJifu3NCK39m+tGJGzauqivPDQew5N4J9F0ax/9JYwWOMRfV2rGtzYVVrHda0TO2XVdNJYd9kDD97oxeHusbx5JE+TMamGjoW1dtx65pm3HVVG7Ytb5h1bTIUjU9N6TQeRr9PfHaLLzFFWSnTO9nNxky6jphaVqRvN7msmameGp2WzDTrlfKeU1UVn/rRYfxwf0/mMq/DjHuuXoRP7Fg9rwN/tfSOT+JQtzjeOtg1jqO9vrwNbYoiatxrW1PnQlqcWN3iwopmJxwW/d4fqqrib392LFMjVBTgro1t+LMdq7GubX7O0w36wzja6xONZb1+nBmcwMWRwoO6DArQ7rFjeZMTnQ2iJtjZ4MCiehsWex3zVxO8dAl47TX9mqhOnQJOn878+rofeM+R3M9Mi6LirkZgZ6OKu67uhPGaq8UVk5OiiarYQB5AJEC95z3a6xKJAI8/PjOJqq0NuP764re9fBk4cKDw9bGYGDxZxYPTfJMxHOnx4Y2e8Uxj5Pnh/GnTBgXobBANwytT5/SWN4pmj456e01Os1lxfvaznHkxnx8DPnpcQQIK7mtX8ZkVKrB8OXDVVfO4krki8QQsRkPNnvvKNhGO4Y1uH/ZfGsMbPeM41D2e99ywQQGWNjqxttWFVS11WN0q9ilXNNXx2L3GsImKatqF4SBePD2EX58dxt7zIzOKNooiigCbFnmwscONdW1uXLXIo2tRe7re8Ul8dfdZvHJ2JGeHps5qwnu2Lsb7r+/E+va5fW2qqoqu0RBePC0KuHvOj+SNS17steOqDg82LRZ/nw0d7lkV2WSNBCJ49JWLeOpYP04PTDVzrWh24jc3teMtG9qwocOty47dgD+M1y+OYe+FERzoGsPxXj+mHyelG0Su6hCvjw0d4kQYp0dYeA51j+Ox17rwP/sv5zQSbexw46/eug5vWt08j2unD384htcujOLV8yPYe2EURy77ZjQVNdVZsHWpF9cu8eKaJV5sWuQpuoP4vX1d+D9PnMg0RyoKcOPyRty9uR13XdWGpjqNA+tZisaTONrrw+HucRzu8eFQzzguDgdnvNfT3DYTVjTXYVmjA0sb0yPuUiNzXdbaO2gYHBTNTcUKHBpJVA8/fxb/+PQpABpNVG+8AXR1Fb56AvjoCQVDMQUuo4rPrlDxrhatJzBNoZNdu3eXNkWf3Q7s2FHigxMgRiRnkli6xUFnodGtLS6rOODMai5Z2VKX+147flykQmhZtgzYtEm/JzIfRkZE00ue6Pt9PuCPTikYjinwGlX853s34vqrl8/DSpZuMpoQDbkXR/HahVEc6h7POemT5nWYsaHDjdUtLhzv9WPfRTEF6N++fQM+cvP8PddkUsWxXj9eOTeMvRfEc8iXkLO8yYnNiz24qsODjYvcWN/mnv/UWa0kKtkRwBXuWK8PD/74CA73iEbdHetb8Lfv2KjrSd7xUBSvnhdTPu+9MJp30EmHx4ZrU/tIWzo92NBefB9JL/FEEi+dHcZ3XrmI50+Jk91Gg4K3b27He7Z24sYVDZqpwLOlqioujoRwuGccb3T7cPSyDyf6/RLTnVsyKcZLGhxYnGpU70ylG5dy8u75k4O479uvARDpHB/athRv39KBjvr5ad4fmojgmeP9eOpoP149P4JYYmrn02hQcNUiD65dUo+rO+uxscOD5U1OnryoBN3dwMGDVZtElc9LZ4bwH7vP4ZVzI5nLjAYFt6xqwu/cuBR3FBgUlB59fXEkiNMDIo2je3RqOvN8jTvFGBTA67DA4zCj3m5GvUM0QrptZrhtpkyajMduzkzF5bKZU1NzmcoyYK1nLIS7//XXOTWyZY0O/NVb12PH+pY535bmk0iqeKNnHLtPDuLFM8M43DM+41jWbTNh8+L6zP7Hhg7RNDXbJOhEUsUXnz6Jb750IdNUYDaKhO4P3LAEN85D+pFvMoaXzgzhhVNDePHM0Ixpko0GBaua67BpsQebFnlEM3u7qyqTg4oJROJ47eIofn6oFz85dDnnNWE1GXDH+hbsWN+K29e2zNgfTSZVjIWi6B0Po9c3ib7xSfT6wiIZcjSEy+OTRVMWstVZTZkE7haXNTW9k2h8Tk+r3lRnQV0Zpj2cS4e6x/Ffr17CnvMj6BkTjXotLiu++J7NuK0CplVMJlWc6PfjtQuj2HthFAe7xvPOomAzG7Cxw4MN7WI7saHdXfaG7V+dHMD/ffkiXjoznLnszg2t+OPbV83pVKrhWEI0lHWP48AlMXC60EwTbpsJq1I1ilUtdVjeVJdJqqukJv2Mnh7g1VdFes0SHaZfTSVRZXvbIQXHg+J1vsKm4nx46jV/basd//ihG7CyuU4MDPvFL0RNrRjZmls8LpqyDNP+7qtXA+vWFb+tVhJVJAJs3w4sWqS9HhVAVVVcGA6KkIhLouZ2pkAQQrvHhg3tbqxtc2Ftm2iQXNHshFWH6R5pFvLMkvBwN/CPXeL1/cmlSfzx9k7g6qvnYeXEa6x7dDJTu3vt4ijODwexqqUOD71rU83NzOIPx7Dn3Aj2nh/Fvosjec8NW4wGrGmrywQHbOxwY1VLXWXNMkJzhk1UVFPiiST2XRzFc8cH8fypQVyY1nXtspqwdZkX16UK25sWe+atEUZVVRy97MeukwN44nBfzg7PujYX7tzQiruuasf6dpcuB2KxRBKvXRjFr04OYtfJmX8bu9mIzYs9uH5ZA65ZUo8tnfVz3tigJT2l2mOvdePpY/05BXiHxYgti+uxfWUjblndhGs666X+TqPBKF44PYg950bw6vnRvCOAF9XbUyc/6nHNEi/WtbmYLkU5gpE4DnSN4YVTQ/ju3ksIx5JQFOCL796M917XOd+rV5JkUsXRXh92nxrCi6eHcLB7fMaIq3RE/A3LGnDD8gYsbXSUvF0aCUTw/de68fzJQbx+aSxzudGgYPuKRty5sRVv2aBPQlUwVUzcd2EUr18UIwfyFfhb3Vasa3NnGjhWNNdhZbNT32mUqsH4OPCrXxWfzk+jOUW6ierXvxbTBxbRGwH+6KSCQwHxP3hXs4q/Wa7CK/txXaiJ6he/QEnzgBSaFpBm8Idj2Hd+FK+cGyl40Gk0KFjTKhqS0wkb69vccimcEq8bAOI1umzZFT2HiuH3A888U7DpZTAK3HdcwbGgArNBwWfvuQq/tU2HIqnO0if7Xjo9jJfPDuNg91hO8wAAuGwmbFlcjy2dHmxaVI/Niz1o99gy219VVfEXP3gDPz54GQBw+9pm/Pa2pXjz2uaynMAc9Ifxwukh7D41hFfODWMslDvgoM5qwjWpfcVrl9Tjmk5vZU6t9fTTQLTISbDGRuCmm8q3PnMonkjiP3afw1d2nUEiqcJkUHD7uhbctLIRO9a3ljyFlaqK5rldJ8Sx5fST1ooCrGtz44ZlXly/vAHXLvHOW7NOmqqq2HNuBF9/8XwmOQQQacK3rWnGb6xvwW1rmnU5Bh4LRnGwewwHLo1nUuXyJc2YDApWNItEg5XNTixtdGJxqkGqxW3VvZD/9LF+fOpHhzMNEIoC3Lq6Ge/euhi3rWme8xS4sWAUvzzaj5+/0YtXL4zk7Hosb3LitjXNuHVNE65f1sBBOZXqyBExLU4NJFFNd3pgAj9/oxe7Tw3hyOWpdNh7r+7AF969GQBwoGssk+hzuMeXd8BdmqKI7Uuj04oGpwUNdRY0OS1ocFqzfrakEmas8NjNFZMuU4wvFMMXnjqB1y6OoWsklBlA5XWY8Xs3L8ddV7VhdatrTtchGk/ilXPDeOpoP549PjBjQMKSBgeuX9aA65d5cd2yBqxsds7pMex4KIoXzwzje3u7sOf81In1De1u3L25HTs3tmJlc92crcPQRATPHh/A08f68cq54Zz9SovJgGuX1OPGFY24bqmoaS60E1v+cAwHu8bxxOFePH9qCEMTU41lDosR77uuE5F4EheGA+gZm8SAPzxj3zyfOqsp85nd5rGhw2NDu8eO9tTUTq1u24L7W8cSSbx4egif+8XxTBLtVYvc2L6iEdcu8eLWNc1l+ZuoqopzQ0G8fHYYvz47jH0XRmdsr40GBWtbXdjSWS9q/YvrsbLZOS/NoPmc6p/Av+46gyeP9mX2l65ZUo8/evMq3LGuZdafF8FIHPsujuLVc2KQ6LFe34zXvUEBVjTXZQZOr24VM05U3QDKs2fFoMXbbtMnVWlaEhWQ20R14aYkXvMDjw8peHwICCcVOC1GfOYdG/HeTS1QnnwSMGm8D2STqKJR4Kc/nZnCvXQpsHlz8dteuiQaqQpJJIDbbwc8Hu31mAfppqmXzw7j1Quj2Ht+NO908Z0NdmxeXJ9pHL6qw43GeT6vRwU8/nje98a/dAFf7hbb5j9Y68Rf/e5tZdtnHpwI45WzI3jx9BBePT+C3gJTICsK8P+7cy0+ftvKqtifzyeeSOJg9zheOjOMl84M4Y3umQMTFtXbsXWpN3OOfGOHm82HCxibqKgiXR6fRFOdRWrjpKoqDnSN46eHLuPJI305I2XMRgVbl3rxptXNuHlVEzYt8lTkyM5kUsULZ4bwg9e68czxgZwGhmaXFXdvasf/um1Fyc0FiaSKV84N4xdv9OGXR/tyitomQ/pv04SbUn+b2Y5Qm0v+cAxPHu7D86cG8eszwzPmIF/b6sKHti/FhnYXVrW4corjl8cn8Ys3evHUsX4c6h7PKWQbFGBtmxvbljdg61Ivti6d/5MfVF1GAhH8/ZMn8OMD4iRvh8cGr9OCZY1OXL/Mi9vWtmDZFTQdzaVYIolXz4/gqaP9eO7EwIyRm8saHbhxRSO2rWjA9hVNaPPom0DXPRrCzw/34pdH+nOK9wYFuOfqRfijN68sqSidbgR74dQQXjqT/2S912HG1Z312Lw4Ndp/UXmS9apCby/w8svFpwXzeoFbbil4tXQT1WuvAf39mqsUV4F/7wb+pVuBCgVNZhX/Z6WKO2UGOhdKVXn5ZWB0VOIOUsxm4K675JdfQBJJFQe7xrD71BBeOiNOwk0/6FzstaeSWOpx7ZJ6rG93X3lD8rFjwPnz2st1zt9oLd34fGJkWpHkoEAc+OSpJH45Lgovv71tCT7z9o3zPiLWF4rh+VOi2eTF00Mzmo7a3DZsX9mI65Z5cd3SBqxuqdMsvKiqin/ZdQb/9quzmf3jxV47PnLTMrxn62Ldp884OziBXx4Rn41v9OROPVpnNWHb8gbcuKIRN65o1C0Vdc79+MfFm2RrcFt3st+Pz/38eE7iCgBclzouvG6ZF1d35j/Bmh7o8osjvXjqaP+MaRpXNjvxptXN2L6yETcub6zMxrmUN7rH8f3XuvHLo30Yz3o/WkwGvO+6xVjb5saGdhc2Laovuv1IT8n3+kUxbWXXaAgHu8byThNhMRmwscONzYs82LS4Hhva3VjdWlf248yRQAQ/OdSLZ471Y++Fqc9+k0HB9pWNuHNDK35zc4duSXGxRBIvnRnCj/b34LnjgzmJtVsWe7Dzqjbs3NgmRudT5evtBV5/vaaSqPI5PxTA91/rxrd+LRKGLEYDEqo6Y0CN2ahgSYMjk8axtNGJxanpzNs99nnf/5hrvlAMX919Fj94vTtn32ZFk1M0MS1vwJvXNusyCDGeSOLlcyP42aFePHM8dyChy2bCraubcduaZty8ugmL5rFudaBrDD94rRv/c6An57i73WPDratFw+6b1zbP+oSTLxTDk0f78PM3erHnfG5T6spmJ35jXQvetLoZN+gwdV0tSTeBP3GkD7tODOQk/E/X7LJmGqM66u3oSE1JJhIhHRW9nzPfQtE4/vHpU/ivvV050xObjQruWNeK/333+pIb+LUEIvHM1MC7Tw7OONFdZzXh2qVeXL/UixuWN+CqRZ6qaHI7OziBrz5/Dj8/3JvZpqxrc+H3blmOe69eJP05M702eKBrbMZ08U11VlyXOjl+zRIvNna4q+JvpCmdML91q5gKbLY0kqgu3jz1mu8JA3/R58LeXnFssHWxG39h7cf2VhuKlsJlE5FjMTEFmmXafrtME5VWLSkSAbZtE7WkChGIxPHK2WE8n6q3pZPv0ixGA65eUo9rl3gzjR7zHYRAJXj+eSAw83NZVYF/6Qa+kmqkunNDK/7pfVvmJEVTVVUc7vHhuRMD+NXJQRzr9edcbzIoImxjeQOuX9qAZU1OfPX5s5kBjhs73HjnNYvwlg2tWNrozPcQFSW7Vvmrk4MzkrKXNzmxfWUjti0X4QF6DPCn2sEmKqpIb/uXl3B2MIA71rfgD25dgRVNdXDbc2N/x0NR/Gh/D763rwvnhqYKuPUOM+5Y14od61twy+qmqhvdORqM4lcnB/H0sX68cHoocyBmMigiuaHNLZp9lnmxoin/SLPu0RC+/1oXfvh6DwazRh81Oi24bW0z7ljXilvXVN/fJi2RVHFmcAL7L43h12eG8cLpoRlzuq9rc+HqznqcHwpmpoJJW9/uxq2rm3DjykZsXeqtuUhvKr9kUsU/PXMK33jpfN7RfK1uK25Y3ojrl3lxTacXa9tcZS82q6qK1y6O4SeHLuOXR/pyCsBOixE3r2rCm9e24E2rm3Qv9BRzYTiIZ4714+lj/TjQNZ65/LqlXty8qgm3r2vB5kWeGSfaw7EEXjw9hGeOD+CF07kjLQExauDGFY24YbkYmVtoe0kQB2/PPQcYixSeOzpEMaYA6SaqffuAgQHpVXvdDzx4VsGZSfG/e1+LigeXaaRSFUqQKjWJqqVFFFMIgJgXfvepITx3Qrznxqc1yCxvcqaaS+bgoFM2ierqqyuq+HVFxsZEE5VG9H0yGsO/Odbiyy9cBCD2e/7u3qtwXZmjtX2hGJ4+1o8njvTh5bPDOUVql82Em1c24U1rmnDzyqYrSjFMOz0wge/t68JPDl7OfH5ZTQa8ZUMr7t7UjptWNV1xukzXSAg/PXQZ//zs6RnXbV7swZvXNOPWNc3Y0llf0QMOCtLa9tXwtu54rx/PnxrES2eGsPfCaM6fwWRQsLHDjeuWpbdZNuw9P4of7u/OOdloNRlw65rm1LFl87yetL5S0XgSr18axe5TQ3j2+MCMRGKryYAbljdg+0qROnrNEi9GAhHsPj2El1PJBn0FRqOuaHLimiXeTLrBunZXxb1PLgwH8cPXu/HUsX6cz6obmAwKrllSjzWtYoqLzammr1L20XvGQvj+vm78cH93zqCEdW0u3HP1Ivzm5vay7leTTrKTqBbA9M6vnB3Gn37/YGZA4vRj1zVtdRx9DVGHevzgZfz00GXsPT+a0yypKMDNK5vwF3euwTVLvCXf97mhAB57rRuPH7ycc1zbVGfFzo2tuOuqNty4orHitq+jwSieSe0H7r0wmtNI0ui04GO3rsAtq5rQ7rFJpz0nkipePD2EH+7vxjPHBnL2LTcv9mDnxjbs3NiKVS1zmwRWK5JJFd97rQsHu8bR7rFheZMTSxsdaPPY0VxnrfkmyHIYCUSw66RILH3pzHCmAd9iNODGlY24fqkXN65sxJbFxZvWC+kdn8TTx/qx68Qg9l3I3fZYTAZct9SLW1Y3YfuKRmxa5KmYlKkrMTgRxrdeuoD/2tuFQGrq9CUNDvzWtiW4ZVUT1rfPHMQSjiXw0plh7DoxkDe1b1G9HTetFINgrl/WgM4Ge23WBs+cEYlLd9wB1NfP/v6OHAEuXsy5qFATFQDEFy3G130u/NuvziAcE9dtcqr4404Vb2kAjPn+5NEo8O53a69LLCam85tes5SZzu/MGeDkycLXJxLAb/wGMM/niXvGQnj2+ACeOzGAvedHcz77zEYF1y7x4qaVTbhxRQO2dNazcbiaFUiiSvvxIPCXZw2IqWL79b/vXo+7NrbNOvkpmVRxsHsMTx7pxy+P9M1owt3Y4cYtq5twy6ombF3qhcOSu46qquK7r17CF355Mifc4oblDXjnNYuwc2MbGnQaoKSH0WAUTx3txy+P9uGVcyM5g0TqHWbcsqoJb1rdhDetbmagBhXFJiqqOLFEEtsf+tWMaEqX1STm6u5wYzwUw5NH+jLTM9nNRuzc2Ip7r1mEm1c1VVxh4UqFYwm8en4EX919DvsuzEzPaHBacE2niBXc0O5GPKniR/t7sOvkQOYkQb3DjLde1Ya3b+nAtuWN1TFivkTjoSi+t68bvz47hPNDwRlFfkUBrl/WgLdvbsdbNrTpnqpDlDYeiuLcUBC+yShO9E3gpTND2H9pZiqSxWjA+nYXNi32YNMiMY3RXI3S7x4N4ccHLuN/DvTkTF/Z6LTgzo2tuHNjG7avaKyIA7DDPeN4+PmzeOb4QM6JzganBdtXiKk7LUYDfnVSjByYjE3ttKcbwW5d04xbVjVhWVPlj4SoGDJJVK2twA03FLxauokqz2g2LeEk8M+XFHyjV3x+OQ0q3t0CvKNZxZY6wDz9bWMyAW9968w7YhJVyQKROJ493o9fvNGHl84M5xRsXTYTbl0jRsO/aXXT3I7UOXgQ6OnRXq4WkqiCQTH9WrHkIAAIhYAdO/DcQBwP/OBQJm30bZva8JGbluP6Zd45Kw5H40n86uQAfrS/By+ezn1drG6pw44NrXjzmmZsXerVvYgfisbxk4O9+H+vXsKJvqnRcooCrGlx4ZbVTbhtTTNuXNFY9CRFOJbALw734Qevdc9otgeAh961CXesa0GLuwb2GbWaqBbItu7y+CR2nRjAaxfHcODSGC6PTxZc1mIy4C3rxUnr31jXUhuj01NUVcWvzw7jl0f70Tc+iTd6fBiddrLJbjbm7GMBU01n69vdaHXbsKXTg2s6vbolOZXL2cEAnjsxgJ8d6sXxPv+M6y1GAzob7Fje5MTyJidWpaZ7XtroQHOdmNZFVVXsuzCK//vyRTx9vD/z9mpwWvCOLR1433Wd2NDBWlNV6+0F9u+f+v3tb5+/dSmTyWgCl0aDqLdb0OqusimM5oE/HMOr50ZS04EM4ejlqe3JNUvq0eCwwOMwY1VLHda3ubGmzYWOrCmLAZE69ezxAXz7lYs5iXlehxl3b27HO7Yswtal3qqp4aXrl8+fHMRTx/pnpF27bSZctciDqxZ5sLHDjY0dbixpcGb210aDUXxvXxf+69VLOSf21rW58I6rO/D2zR1sSqWKp6oqTvRN4LM/P5bzvgZEzeqW1WI633VtbixpcKCj3pb3eKlrJIRfHOnFk0f6crYvALC00YHb17bg1jVNuGllU0XU8vSWrvN/86XzOU1RLqsJq1vrsLypDqtb63Cyz49njw/knNDPrg2+aXVTVaSk6GJ4GNi9W78kqtdfB/r6ci4q1kSFLVuAJUvQ7wvj4WdO4IcHLiOsimWX21R8bJGKtzYid0BkodrddJEI8JOfAI5pnwFtbcD11xe/rVYT1TwmUV0aCeIXh8XsMfne57etacbta1tww/KGmjoeXfAKJFFlO9C4DH/y4lCmZtHuseGuq9pw65pmXLvEW9IAwq6REP7nQA8eP3g557yQw2LEbWuaccf61pISVUcCETx+8DKePzWIV85NpYQaFGDT4nrR6JeaBaTczUnBSBzPHh/ATw5dxktnhnMap9a01uGO9a34jXUtuHZJ9exf0/xjExVVpHT08Fd3n8XuUzNThtLWt7vx29uW4J6rO6o2VUlW92gIh3t8ONrrw+sXR/FGjy9nlNd0N69qxG/dsBQ7NrQsuFGDw4EI9pwbwcl+P9rcNtyxvpUdxTRvJqMJHOwaw76LozjQNY43usfhm4zNWM5qMmB9uxsrm+vQ2WDHkgYH2j12tLqtaPPYZowAKEZVVbx6fhTf+vV57Do5mNmhrbOacNdVbbjn6g5sX9FYsSPUescn8fypQbx8dhgvnBqaMX1n2qJ6O96yoRU71rfi+uXeBbet041MElVzM3DjjQWvlm6iOnQI6O6+otXc6wP+9oKCE8GpAx2nQcUyO9BgBhpMQIcVWNpch87rrkoVJe1TB0ZMopKSSKp4+ewwfrS/B08f6880rAMicWTHhla8ZUMrrumsL982RDbBrBaSqIaHgV27ZhYIp4tEgJ07AZcLo8EovvDLE/jh/p7MS3xdmws3rmhEm8eGNrcNi7x2LPba0eqyXfEItrODE/jvvd14/GBPTprh2lYX7t7cjrdtaseqlvJMVaWqKo5c9uHxg5fxwqmhGdOK2c1GXLVInJxrcVux2GvH0gYn4skkXjg9hB8fuJz5LDYowPaVjXj5rGjwfPi3rsXdm9vL8jzKQmOk40Ld1nWPhvD6pVG8fnEM+y6MwjcZw2KvHe+6djHevqXjipPNqo2qqjg1MIE950bw6vkR7Dk3kmnK3LLYg1tWixN01y7xwm6prf2s80MBHOoex5nBAE70+XGwK/8+eprTYkR7vR3+yVhO2vNNKxvx29uW4i0bWpnoUSuyk6iMRuBtb5vvNaIK1z0awpeePY3HU9Oc5OOymrCypQ4rmpwwGBS8fHY4MwBQUYDb17bg/dd34va1LVW/LYknkvifAz348YHLODcUwEgwmvcwzGRQsKTRgSanFYd6xjM1Tq/DjHuvWYT3bmVTKlUnVVVxeiCAV84N4/WLY3j53PCMJGcAsJkNWN0iEjHXtrqgQsXP3+jDkctT04orCrB1iRc7N7bh9nUtWNm8cFLWg5E4Hj94Gc+dGMDrF8cy6VTTdXhsmTpFJab2lYXeSVT794um8ix3H1JwrFAT1ZIlopEKAOJxjP70SXyrz4D/1wf4E1Ov1/VOFW9tFAMjF5mTwD33aK9LIgE8+eTMyzduBFasKH7bo0fFPl2x+y5jEtVwIIJfvNGLxw/14o3u8czlBgW4blkDdqxvwVs2tGE5BwbXLq36DAC0tCB49VZ8/YVz+PYrFzPH52mL6u2pVEkbljWKhMnFXjuWN9XB6zAjqQLPnRjAd1+9hJfODGdu57QYsWNDK962qR23rWmedRNu7/gkfnLoMn7xRl/eAUrXLqnH7WtbcNViDza2u9Hs0n+gRro2+N97u/CzN3pz+giuWuTGW68StUq+p+hKsYmKqkI4lsD5oSCO9fpwvM8Ps9GAnRvbcO2S+gVz4DBdJJ7A8V4/DnSN43DPOE71T0BVxUmgD924hNHWRBVKVVV0j07ijZ5xHL3syzRHTp+PeTqnxYgmlxVNdVZ4HRY0uyxocFpQbxejXN02E6IJFaOBCH64vydnPuubVjbivdctxl0b26vuBFg0nsThnnG8eGYY+y6MIJkErllaj7s3tWPTIs+C/QzQlUwSlUbCj3QT1YsvAj5f4es1JFXg1+PAj4cUPD8G+OLF//9mo4JOrwOLGxxYFh7HSlMEi63AcjuwyApYi9XWFkg6S1qfb1JMSfR6d87o7xVNTvzm5nbcvbkDa1rr5uc9J9tEVQtJVOEw8MtfahdVIhHgLW8BPJ7MRcd7/fj2Kxfw00O9Oc1v2cxGBR31dnR6xcjndo8dHfU2tHnsWFRvQ4vbBpd1agptVVWx+/QQvvnS+UyTEQC0uKx457WL8M5rFmFd2/wfUw1NRPDq+RH8+swwdp0cyExJVMxirx0fuL4T7966GO0eO5b91RMAarCJ6umnxVQJhSywbR0VF4jEcarfj6WNTunRqLVCVVX0jE3i0kgI54cDOD8UxLmhQCrpeBJZA1lhMRnw7msX4b6bl2NNK4+7a86lS8Abb4gz19xGUgkuDgdxtNeHQDiO4UAEpwYCONnnx4XhYM7UPGmNTgs+cEMnfnvb0poe+BeJJ3BmIIAjl3043uvHkcs+nBmYmDFYatMiD373pmX4zc3tNZmuQwtXIqniWK8PL50ZxsGucZwfDqBndDIn0Teb0aDgxhUN+M3NHXjLhtYFt0+WTzyRxOmBAC4MB3FmcAJnBgJoddtw9+b2BX1+KGNoCHjhBf2SqA4fFvtDWYo2Ua1fD6xaJX4OBMQgRqcTgTjwvQHgR4MKToWm/kcGqLjJo+LarWuwpNEJj90Mp9UIl9WMeocZbR7bVDNcJCIaT5zTmiBkkqguXRLPpZAyJFGlp6n9/mtd2HViMLM/YDQo2L6iEW/b1I47N/J9vmA89xwwWTgVG0DO+ykcS2D3qUHsPjWEl88No3u0+G3rrCZEE8lMY7qiALesasK7r12MOze2ljRIvxT9vjBePDOEg11jOHrZj2O9Pkzf9XVajFjRLJIElzWK1OclDQ40u8S5rlIGEUTjSTxxpBeP/PpiTuPx0kYH7rl6Ee69ugMrmsszyJNqG5uoiIiI5lkyqeLSaAhHL/twaSSIrtEQesYm0e8LY8AfLpjEVIzVZMB7ti7G79+ynDuNVJzfL5JvijVt1NcDb3pTwaulm6i0RoGVIKECp0NAfwQYiQOjMaAnrOBS0oIe1YLuIkXJtAaTilYr0GwGWiziq9msoskMtDS70XbTdWiqs8JhMdZsUW7fhVE88usLePbEQCbq2GM34x1bOvCerYuxeXEFNCvKJphddZU+BcP5JJtENTkpmqi83hlXjQWjeO7EAM4OBTDkj6DXN4mesUn0+cI5cdaF2M1GNLusaHZZMTQRyUR+Kwpwx7oW/Na2Jbh1dXPFphkmkyrODgVwvNePXt8kBnxhdI2G0D02CYMCLG9y4gPXL8Gta5pzIrxrtonqRz8CrEUKsg0NwM03l299iKpQJJ5A96jYN7eZDVjVUod6R3VNZUglOHFCJDooikgxfcc75nuNqMpF40lcHAnizEAAF0dEemZngwN3bmhdsM1CqqqizxfGuaEAhgMRrGp24apF7vk/7iAqk0RSRddoCKf6/TjVH8CpAT8CkQRuX9uMt2/pYEMFlebMGdEA/uY3A01Ns7+/PXtEbSJL0SaqlSuBDRvEz7EY8MQTM9Luh6PA82NiUOQeX/FtvUEBGpyiJtHutqLNN4Qms4pms4pmC9BoBrzrVsGzZgXqHebC6WN5msFyxOPArbcCjY1F1+dKpKep/e6rlzLJk4BoGH7XtYtw9+Z2tLiKDGal2vSzn4ljjGKKpIWPBaM4OxRA92gIfb4wLg6L80jdo6GcAbH1DjPef30nPrRt6bxMhzzoD+PJI3041D2Ow5d9uDgcnNFUNZ3DYoTXYYHLZoLNbITNbIDVZESd1QSXTXw5LKJJ7PEDl9HvF8/XYjTgrqva8FvblmDb8gbuS5KuSukp4sSrREREc8BgULC8yZk3WlRVVQQicQxNRDAciGIkEMFIMIqRQBRjIfHlm4xhIhyHxWiAw2LEls56fOjGpWhw8uQOSQiFRIR1sSaqhgZ9Hmt0VJ/7AWBUgPVO8TVFBUwJ4K1vRjyRRJ8vjO7RELpGQ7hw4Dgu+OPojgAXJoFwUsFoXMFoHDiRc8+pg63TAeDl3QDEaJnGOiua6izwOizwOi3wOszw2M1w28V3p8UEp9UEp9UIp9WUOcizmyuvAUtVVTx7fABfe+EcDnSNZy6/YXkDfnvbEuzc2FZZJ3QCAbnlgkHtZSpdXR1gkdh2m0wFl/M6LXjvdTNHUsYTSQxMRNCdatTtGxfNVf3+MPp9YfSOT2IiEsdkLIGu1PsGEIWMD96wBPfdvAyLveUvvpTKYFCwptXFdJi0+vriIx21RkESEawmI1a11JVtylKaZ+as6Tzr+D+n2bOYDNw3mUZRRDpqLSdwERVjzKoD3nXVfK8NVb10SpNex3Ze74wmKmnRqPiy527fmyzAe1uB97aquDipYvdQHCdaV6LXN4mJcByBSByBcByjoSii8SSGAxEMByI40Ze+BwWZeh0AHDkP4DwAUbNL1+fqrCbU2UxocFjgSUbgHFFQZ1ThMgFuI+A0Ai4T4DICjoQKpy+EOk89LEaDLrW780MBfPPXF/DjAz0Ix0SzWb3DjHdeswjvv76zIpK8aR6ZzaJ5r5g8gyUzVzktuN7ZgOuXzazTT0YT6PVNwmI0oNVtm9fpoVvcNnzk5qlBrmJQUiiTKHhhOIgzAxPo9YUxEoggqQKhaAKhqPw2rNllxYdvXIrf5jkwqhBsoiIiIiozRVHgspnhspmxonm+14ZqktkMGDQOrPRqfmpomNV0flJc4uSEyWhAZ4MDnQ0O3AQA/tPAZAyACBXwxVVcjgCDMWAoKr4GYwqGosBwDBiIGzAQUxCOJRGMJhDMaiophc1swE0rm3DjigYsa3SixW2D3WxEMBrHoD+MAX8EigIYFAUGRYHRIN73RkWBwZB9uQKz0QCTQYHBMHW9MXVd+sugKFBVIKGqSKoqkkkViaSKpAokVRWXxyfxyK8v4GT/BAAxYufdWxfhd29aVrnFnIYGYGxMezmtKfCqQSAgCo5azyUWEwXS6ZH2RZiMBiyqt2NRkZNVk9EEBvxhDAciGJyIwGoy4PrlDXDbzAVvQxVufLx4ElUtvG+IiPSUvV881/utRERERLOVnr7dqNNguFJrgNnHm0aj5nosswMfWesCdmyecZ2qqhgKRDDoj2AoEEHfaAgDB45manXDMWAkBoyrRvijSagqRM0umshJ4pkyrfkqd8WBI4cBHIbJoMBuNsJuEck3DqsRDosYIGkzG2AzG8X1ZiMcFiPMRgPMJgPMRgMsRgWTsQQOdY/jl0f7kZ7PaWOHG/fdvJzT1NIUmcm+rvD4w24xYmWFzkYiBiW5sKpl5oCCZFKFbzIG32QMY6EoJsJxROJJhGMJhGMJBCNx+MNxTIRjCEUTiMaTuH55A+65ugNWE99XVDlYXSUiIiKqNaqqfRBXgUlUBRUaeReLZX5UFKDeLL425iyU9XdwO4HbbkMwEseAP5xKgItgPBTDSFAkwPlCMfjD4kAvGBEj50LRBAKROIKROJIqEI4l8auTg/jVycG5eLZXzGkx4sM3LcN9Ny1Di7vCI8RlXzd6FQznk92em4BRiMUyJ+kYdosRy5qcWJYnGXGhUFH1M9jn0kqi0mqiJSJaaLK3mUVGghMRERFVDEXR79iusREYGZFffmKi9McYH897saIoaHHZpqa6i0SArr2AY1oqdkcLEtdci4lwDGOhGPyTU7W5iUgcI4Eo/EOjCPUOYCIOTCQAfxwIJcTPgYT4OZQUDVbxpIqJ1G0HJyKlP58sv7GuBX9w6wpOLUYzxePa0/nZF1ZKp8GgiBkfnBYsw8KtRVL1YxMVERERUa1JJLSbqDSaWKRrAh7P3I/oL5Sq4nAAfr/8/aRG8jmtJqxoris5CU5VVYSiCVwaCeH5U4M41uvD5bFJDE5EEIkn4bAY0eC0YFG9HYqCqbSopEiQSqjiPpLqVJJUNJ5EIpMsJb4nUmlT8WQqdUpVM+lV6aQq8bNIrzIZFdy+tgUffdNy1DuqJO5YNomqFqYli0REUUWrkSoaFalVtgpvgKP5p5VEFc43WpeIaAHzeKZ+ltn/ICIiIppPFouo60Vm1/yTkaeBqmjZr7Fx6udEQnxpqa+XWxejMf/xrMMBo0FBvcNSuLZ1OAjYitQ7IxHEb9iGUEsbgpE4JqMJhKIi+SaU9XM4nsBkNIFwLIlQNI7JWAKxRBLRuIpYIolYIgmb2YimOgvuvWYRNnZ4Cj8mLWwOh3btUub9Q0QV54qaqB5++GH84z/+I/r7+7Flyxb827/9G2644Ya8y775zW/GCy+8MOPyt73tbXjiiScAAB/5yEfw6KOP5ly/c+dOPPXUU1eyekREREQLm9WqPVrNo1MBIBDQ536KKfRcSh0ZV8I0afkoigKn1YQNHW5s6KjQafKqhWwSlV6v0/lkMsmNHp2jJCqqQXV1OUl8M7i5fSIiypHdOMUkKiIiIqp0waAY3ahXjcDrLS2JamQE6OwUP5vNcunaBZKoZkgmRVPJ9OTxeFz7tlo1IpMJpnoP3DYz3DaJdSaarYmJwoN/02Re20RUcUrOgnzsscfwwAMP4DOf+QwOHDiALVu2YOfOnRgczD+dyY9//GP09fVlvo4ePQqj0Yj3vve9OcvdddddOct973vfu7JnRERERLTQhULao1yuJJo7H5lCymwVSlUp9SRYKalVNLdkRygupLSIdBIVkRatpCnZ4jUR0ULR2DgVs7qQ9i2IiIioOtXXiyQqvepYpR4jZidRRaNyiVizHQSXTGovEwwWvz4en/u0fKJsMvXN6VNXElFVKLmJ6ktf+hI+9rGP4b777sOGDRvwta99DQ6HA4888kje5RsaGtDW1pb5evbZZ+FwOGY0UVmt1pzlvEVOikUiEfj9/pwvIiIiIkpxu7Wbm4pNBVUK6Xn/ZsHlyn95qSfBmDxQOWSLWtmFu2oVj8tFd5vNLKyQnOkjdqfjto6IKNfIyNRU10x9JCIioko3Pi7qbXod25WazJ6dWmWzydUqZM/TGgz5j2ltNu3baiX+mEy1kWhO1UOmQZE9DERVqaQmqmg0iv3792PHjh1Td2AwYMeOHdizZ4/UfXzrW9/CBz7wATinfWjv3r0bLS0tWLt2LT7+8Y9jpEi05EMPPQSPx5P56kzHShIRERGRODgrNtUTIDfCS4bMaLTZKnRAWmoxSXYKOZp7drvccqXEzVcqm00usS0W004YIgK0p4dkygoRUa7spuxy7LsSERERzYbXKxrA9apjadUI8z1+WjgsEu+1yDRBAWKQWb79MZlGE60BRfE4k5mpvGSa9mphgCjRAlRSE9Xw8DASiQRaW1tzLm9tbUV/f7/m7fft24ejR4/iox/9aM7ld911F77zne9g165d+Id/+Ae88MILeOtb34pEgRHbDz74IHw+X+aru7u7lKdBREREVNtkkqhKLaAUUiglSk8NDfkvZxJV9ZJJZgJqo9AQCsm934xG7VGVdEXS4SM1Q6sBgNs6IqJc2U3ZWo2oRERERPNtbEzss+h1bJdnIGXRXPns9HDZJCrZwZomU/5kLJn6j1Yzl8kkaqJE5SLTtFcLA0SJFqCyVum/9a1vYdOmTbjhhhtyLv/ABz6Q+XnTpk3YvHkzVq5cid27d+OOO+6YcT9WqxVWvaagISIiIqo16SSqYiO0NKK8leLllCmy07LNRqGRdy5XaZHITGepHLLFtZERoNpTZ+vqAItlvteCakldXfFGKm7riIhyZZ9MY8MyERERVTqPR9RNxsb0aaSSTYlKy04PTydRaU0JKDtYLh4HAoGZ9zcyAqxYUfy2Wqk/8TgwMQHU18utC9FsadVngNoYIEq0AJU0/KqpqQlGoxEDAwM5lw8MDKCtra3obYPBIL7//e/j93//9zUfZ8WKFWhqasLZs2dLWT0iIiIiAkQhQusEkV7zsRdKidJToceYnCztfsqRmkVyZAt4tfA/CwSAaFR7uWRSFPyItGhtv+vqyrMeRETVInu6XJnpaIiIiIjmk98PKIp245KsiYnSls9uiLJYAJlQC9ngC5Mpf61HptFEK/XHYCi9YYxoNrKPMwphEhVRVSqpicpisWDr1q3YtWtX5rJkMoldu3Zh+/btRW/7wx/+EJFIBB/60Ic0H6enpwcjIyNob28vZfWIiIiICBAHcFojwPRqfiqUEqWnQo9RLGkrn1KbrmjuyBbwtEZzVQPZJCqTicU+kqM1qrYW3jdERHpSshJWOcULERERVTqbTcxLLzMgS0apSTjZA7zicZF2ryUYlLvvWCx/TUim0UTmecgmYhHpQaY2rZWgRkQVqaQmKgB44IEH8I1vfAOPPvooTpw4gY9//OMIBoO47777AAAf/vCH8eCDD8643be+9S3ce++9aJz2IRcIBPDJT34Sr776Ki5evIhdu3bhnnvuwapVq7Bz584rfFpEREREC5jJlHuyKB+9mp/KkURVKFXFUOKuLKdvqRzleN1UCtkkqliM6RgkR2b0LRERTTGbp34ux1TURERERLORbgTSqu3JKjUJx+GY+tlgkGsUkZ1Cz2zOn7AlM22h1vNQVZHyTVQuMu/RUpPgiKgilHwm6f3vfz+Ghobw6U9/Gv39/bj66qvx1FNPobW1FQDQ1dUFw7Si7alTp/DrX/8azzzzzIz7MxqNOHz4MB599FGMj4+jo6MDd955Jz7/+c/DKhv/SERERESlqaYkqkKpKqWmrbCxoHLIvm5kEpwqnd0u18BnsXAaNpJTX188WY8No0REubKTEWRO0BERERHNp2RSNGfodWzX2FhaI5XWFPL5aA32SYvHxfGs3V76YzY2ApcuFb7eYJCfVpBID5GI9vuUqfNEVemKPoHvv/9+3H///Xmv271794zL1q5dC1VV8y5vt9vx9NNPX8lqEBEREVE+sZgYfVWMXs1PLtfcj+gv1PxUV1faY8vMU0/l0dAAjI1pLycbB1/JolG5OPloVKRWsbhCWsbHixeGmWhGRJQru3FKZv+DiIiIaD6lp/MrNnimFHkaqIoG6GTPKJRIyNU0ZJOoDIb8A+Zkmp+0GsESCVFHKnX6QqIr5XJpv0+1avREVJE4HJ+IiIio1tjt2lHbGvOxSyeG61XQKabQiJ5SR8a5XLNfF9KHbBNfLaRFGAxyKWhMopozNVeuyjf1QTbZ4jUR0UKRvd/BbSQRERFVukBAFObcbn3ur9T9n+xmJYtFLiVcNolKVfM3Zck0mmjV9UwmzXonka5katPR6NyvBxHpjk1URERERLUmGBTx2MUEAvo8llazlh4KpaqU2mAz14lZJE+2qFWO6SLnmuw0kukkKiItWgU4pqwQEeVqbJwaIcD9QSIiIqp0Xq9oKtJrv2ViorTls5OcIhG5ZPdSBi7ma5iKxbRvp7Ue8Tj39ai8ZBoUOWCSqCqxiYqIiIio1rjdgNlcfBmN66WThsvRRFXogLTURoFaSDWqFbIFvFqIYJedzs9sFilyRFq0ogK5rSMiyjUyMrVz63DM77oQERERaRkbEwOy9Dq2k5kqL1t2EpXNJrf/FAzK3bfBkL8mqZW4nL5tMUyionKTSWBjYx9RVWITFREREVGt8fu1R3BJz9enoRzT+RVqliq1mMR0lsohW8DLLtxVK7tdu6kRECMmGfFNMgpNcZpWCwluRER6yt5n5GctERERVTqvF0gm9atjJZOlLZ89mDEcLpwQn01myj9A1D7y1RJlGk20joWZREXlJpNE1dAw56tBRPpjExURERFRrZFJoopE9HusuVboYJNJVNVLNuqsFgoNoZBcLL3BID/1Hy1sWs2r3NYREeViIz0RERFVk7ExMfhRpkFDRp507KJDK7PTw2WTqGSZzfmn/pNJItdKuzKZSptWkGi2ZJKoamGAKNECxCo9ERERUa2RSaLSq6ggc7A4W4VSVUqdU54n0CqHTFMRUBv/s7o6+RGZbKIiGVrNq+XYLhMRVZPsfUaZdEgiIiKi+eR2i8FneqUq2e2lLZ+dHi6bRCVb54nFcpu00mQaTbQGDCUS8tMKEulBZhpKmQZBIqo4rNITERER1RqHAzAaiy+jUYiRnu2vHElBhYokpaZpldp0RXNHpshQynKVLBCQmzookeAUQ3NElU0+qxZahXQ9RwkTEdWC7M9XmZOARERERPMpEBDf9Tq28/tnXFT0KDn7GNpszm2qKkR2XWeTRKU1db2iyA9iI9KDTG2aSVREVYlNVERERES1JhIBksniy+jV/KRVwNBDoVQV6U6vFL2mMKTZy1PAy0t2JGMlk02iMptLHx1KC5PWlA7xeFlWg4ioamSfCNRrWhwiIiKiuZKuIehVEyk1CSe7AT2RkDvGzJculc9skqhknketDaKiyiaTKK+VJk5EFYlNVERERES1xmjUbjDSq/mpHElUhUazmUyl3Q+nSqscsq8brWbAaiCbRBWLMR2D5GhN11cL7xsiIj3ZbFM/c8pTIiIiqnTpRiC9GoJKTcLJTopSFLlBjLKN6mZz/jqfx6N9W63noaocVETlJVObZq2PqCrxTBIRERFRrTEYtAsc1ZREVagAMjlZ2v2U2nRFc0f2dVMLyUw2m9xrz2LhlJMkR6u4nN0sQEREuQmYTKIiIiKiSheP6zs1XalJVNlN5waD3KBE2Ub1eBwIh2deHgxq31arlmkw1EYdiaqHTIMU69FEVYlNVERERES1RmY6P72an8rR9FHouZQah8yRP5VDtolPdtq/ShaPyyUDRaMitYpIi9b7QnYaBSKihaKxcWqAAZOoiIiIqNLZ7SJVSaaxSEaeBKeiQy+zm67icblpBWUb1Q0GkUY1Xb7LphsbK359IsG6CpWXTIKa0Tj360FEumMTFREREVGtcTq1D9Cyo7nzkAjqFmSmKZstqzX/5T5faffD5IHKIdvEV+poyWrGJKo5o8hMPVBNtEbWer3lWQ8iomoxMjI1HU6pTfhERERE5RYIiGYjvepYMo0e2bKbriwWubTjUhrV801TKHPc7nQWv95kKv25Es2GTG06X/IaEVU8NlERERER1ZpAoPAUeGmlToU3nwqNIiu1UUBrxBqVj+wJzDyjJauO0SgXfc8kKpKltX3nto6IKFd2EhXT+oiIiKjSeb0i0VqvBM1SE62yB7RFInI1RNlBYcmkSIyaLhLRvq1WIlY8XvqAS6LZkGl01BjITESViU1URERERLXG7daOwdYrSthi0ed+iik09VupjQJMZ6kcslMr1kISVSSi3fQCiBGTMqM7qWRqvlGu1Uzr+XBbR0SUKzuJip+1REREVOnGxsRgLL2O7WSmysuWPaDNZgMcDu3byKbtGI35E+dlBtslk8WvZxIVlZtMoyOnEyeqSmyiIiIiIqo1fr/26Cy9mqhKHc12JQpN/VZqrDnTWSqH7OuvFpKonE65ZsNkUq7Zikjr9cRtHRFRLq93KolK6+QbERER0XxLJ1HpdWxX6hT32Y1I4bDcQDjZOk88nv/+ZJ6rVjM8k6io3GSa9mphgCjRAsQmKiIiIqJa43JpjzLTaz72UhuZrkShJKpSCyNMZ6kcMtPbAeV5fc21YFBM1UekF63mVY68JSLK5fNNJVGxYZmIiIgq3diYaHySSWeSUWpNIvuY02oF7Hbt28g2apnN+ac3k2k00ZqW2WiUn1aQSA8ytelaGCBKtACxiYqIiIio1kxMaCdR6VWIKUfiSaEkKpkiTjbGJ1eOSERuOa0CWTWoq5NLojIY9EuIo9qm1VwYCJRlNYiIqkb2PiOn8yMiIqJK53KJBnC9ju1KbSwymaZ+jkTkBmLKNmrFYvlrPTKNJlqNVskkMDkptx5EepCZ6pJJVERViU1URERERLXGbtduxtBoKJJO+i6UEqWnQqkqWo1i08kc2FJ55Bt1mI/VOrfrUQ6BgFwxMR6Xby6jhU2rIbQW3jdERHpKJKZ+LsdU1ERERESzkZ7uTq9ju1IHFWanh5vNcgPDZBu1ZpNEpdVopSi5DWBEc00m5ZZJVERViU1URERERLUmFhOjr4rRq/mpUEqUnvQaecfpWyqH7FSMWq/jauB0yhUcLRaxLJEWrSSqWnjfEBHpKbuJqhamCiYiIqLaZjKJhiC9ju1KTcLJHuCVTObuSxXi98vd91wmUQEljAol0oHMe5RTTBJVJTZREREREdUaRdEuGujV/FSOE1GFRt7JNKZkY2NB5ZBt4quFxrdgUC6JKhplOgbJ0RpFXAvvGyIiPWWnkXJ6ZyIiIqp0iiKm85NpXpJRahJOdlKUqoovLbL1QZMpf1K82619W63noary0woS6UEmLY6p80RViU1URERERLXGaNRuotIriUp2pNlsFGp+KrXhhFNcVQ7ZJr5aSGayWuXi5C0Wjk4jOYWmOE2z28uzHkRE1SI7AZNJVERERFTpolFR17PZ9Lm/UpOospvOjUa5moZso3oikb+pJBzWvq3WfpzRWBt1JKoeMrVpA1sxiKoR37lEREREtSYc1h6tplcSVTlO1hdKVfF6S7sfvaYFpNmTbeKTnfavkiUScqM2o1G+RkmOVvNqvqkRiIgWssbGqQEGTKIiIiKiSldXJ+oIetUI8iQ4FR16md10FYvJJenINqobDKLZKd/lWrSOheNxHg9Tecm87s3mOV8NItIfm6iIiIiIao3LpX2AplfiTTmmjSrUqDU2Vtr9lNp0RXNHtomv1NGSlUg2+t5i4YhJkqM1GpnbOiKiXCMjU5/F2dPTEBEREVUiv180Fel1bFfq/k9205XNln/6velKaVTPl56fr7FqOq1jYZNJO7mZSE8yr/tSZ1IgoorAJioiIiKiWjMxIUaKFRONFr1aKT4mbUqhqfb0VGgUWanFpFKbrmjuyBbw8oyWrDpms9yIymiUhRWSo7Xd5baOiChXdhIVP2uJiIio0nm94rhPr2M7manysmUPaAuHgVBI+zayg8KSyfw1y8lJudsWE4/XRqI5VQ+ZJCoOdCOqSlfURPXwww9j2bJlsNls2LZtG/bt21dw2W9/+9tQFCXnyzatW1hVVXz6059Ge3s77HY7duzYgTNnzlzJqhERERGR212+qGCtUWB6KDT1G5OoqpdsAa8WkqjCYbnENpMJsFrnfn2o+mlN18ptHRFRruwkKotlfteFiIiISMvYmBiMJTtFnhaZlKdsV5JEpTFYM8Nkyl9LlHmuWrUVJlFRuckkUcmm8RNRRSm5ieqxxx7DAw88gM985jM4cOAAtmzZgp07d2JwcLDgbdxuN/r6+jJfly5dyrn+i1/8Iv71X/8VX/va17B37144nU7s3LkT4VK7o4mIiIhIxH5rJVHpdQKpUEqUngodbJZaGGE6S+WQSWYCaiOJqq5O7v2WTGo3xxAB2s2r3NYREeWqr59KopKZYpeIiIhoPnm9Yp+llCnyiim1icrtnvpZNokq3xR9+cRi+VOnZBpNtNKumERF5Zb9XimkFgaIEi1AJTdRfelLX8LHPvYx3HfffdiwYQO+9rWvweFw4JFHHil4G0VR0NbWlvlqbW3NXKeqKr7yla/gr//6r3HPPfdg8+bN+M53voPe3l785Cc/yXt/kUgEfr8/54uIiIiIUlwu7SSqQECfxyqUEqWnQo9R6j4gR6NVDpNJbjm9Rl3Op0BAfkQmT+zOiZr7s2ptv2WKeEREC8nExNSHgUw6JBEREdF8Sg+Mcbn0ub88TUtFe56yl7daAbtd+zFk6zxmsxhsNp1Mo4lWg5TRKD+tIJEeZAYX18IAUaIFqKQmqmg0iv3792PHjh1Td2AwYMeOHdizZ0/B2wUCASxduhSdnZ245557cOzYscx1Fy5cQH9/f859ejwebNu2reB9PvTQQ/B4PJmvzs7OUp4GERERUW2bmNBOotJruqdyRBIXeoxSpxIsR2oWyQkG5Zarhf+ZbBKVwVC+aTipumk1hMqMEiYiWkiyp8uVmY6GiIiIaD6lG4H0OrYrdVBhdnp4NJo/OWo6mWUAUa/MNzBIptFEq9EqmQQiEbn1INKDTG26HAOQiUh3JTVRDQ8PI5FI5CRJAUBrayv6+/vz3mbt2rV45JFH8NOf/hTf/e53kUwmcdNNN6GnpwcAMrcr5T4ffPBB+Hy+zFd3d3cpT4OIiIiottls2lHdek33VI4DwUKpKqVOfVZq0xXNHdkCnl7TTs4n2SSqeFzE5BNp0ZrSQXYEMBHRQlQLDdpERERU29K1Ab0GWpVaA8x+XKNRbj1kE5Fnk0Sl1WilKLkNYERzLZnUXkavGjwRldWcV1e3b9+O7du3Z36/6aabsH79enz961/H5z//+Su6T6vVCmv2KDIiIiIimpJIaM9fpdH8VDTWO1s5kqgKjbwrtYlK5sCWykOrCSRN+oVYwRwOuYKjxZK/kEg0XX198Ya7WnjfEBHpKTuRoBamCiYiIqLapncjUGNjaVOKZaeHq6p2jRHQnmovLZ1ENT0ddGQEWLGi+G0bG4GuruLLaA0qJdJTIqFdg+EUk0RVqaRP4qamJhiNRgwMDORcPjAwgLa2Nqn7MJvNuOaaa3D27FkAyNxuNvdJRERERFlkChx6NT+VGgl+JQo1oJSaLBWPz35dSB+yCWa1EMM+Oak9vSYg0qryRdoTTafVhCiTfEZEtJDU1U2d3JBt5CYiIiKaL0ajqOvJ1BJklNJABeQ2nauq3CBG2UZ1kyl/PU9mUJnW81BVJnxTecnUpvV6HxNRWZXURGWxWLB161bs2rUrc1kymcSuXbty0qaKSSQSOHLkCNrb2wEAy5cvR1tbW859+v1+7N27V/o+iYiIiCiLxaI9ak2vUfjzOSVKqQ0ndvvcrAeVTraJz+Wa2/UoB7NZbiQkk6hIltb7gqMciYhy+XxTAwyYREVERESVLhwWDeDT05qulMxUedmypx8zmeTStWUb1ZPJ/E0lMoOBtPbjjMbaqCNR9ZCpTXNmBKKqVHIm5AMPPIBvfOMbePTRR3HixAl8/OMfRzAYxH333QcA+PCHP4wHH3wws/znPvc5PPPMMzh//jwOHDiAD33oQ7h06RI++tGPAgAURcEnPvEJ/N3f/R1+9rOf4ciRI/jwhz+Mjo4O3Hvvvfo8SyIiIqKFJBTSHiXm9+vzWOWYYrnQiB2vt7T7mc+GL8ol+7+rhbQImdh7gElUc0iF5P+gWhSa4jRNr+07EVGtaGxkEhURERFVD7db1BL0qmPlSXAqOgFZdtNVNCqXEl5KUv2VTkGv9feIx3k8TOUlM0CjHLVzItKdqdQbvP/978fQ0BA+/elPo7+/H1dffTWeeuoptLa2AgC6urpgyEo+GBsbw8c+9jH09/fD6/Vi69ateOWVV7Bhw4bMMp/61KcQDAbxB3/wBxgfH8ctt9yCp556CrZSp2ghIiIiIlFs0RolVk37WYVG3mWPjJNRatMVzR3ZE5iljpasRImE3Kgzs1m/UaZU27S279zWERHlGhkRJyIVhamPREREVPl8PpEwr9exnctV2pR+IyNAZ6f42WaTq1XINi8pSv60botF+7Zay5hMpTVzEc3W+Lh2jYYDJomqUslNVABw//334/7778973e7du3N+//KXv4wvf/nLRe9PURR87nOfw+c+97krWR0iIiIiyub3i/SmYlOIaSVVySrHvO6FCjFeb2mNVKU2XdHccTrl/h/ZhbtqZbWKQp6WWAyYnGQjFc0et3VERLkaG4FLl8TPk5Pzuy5EREREWrxe0QA+NqZPI5VMklS2hoapn8NhkYas1Yhut8vddzIp0q2mL69Ho0k8LhrQ2EhF5VJfDwSDxZephQGiRAtQydP5EREREVGFk0mi0ms+9nI0fGQXb7Ixiap6yTbf1UKhYXJS7vkajXIjL4m0Xk/c1hER5UonUQFyjc1ERERE82l0VCQ2zdexXXa9TTaJSrbOYzLlb7iSqf9oNYMxiYrKTSZpv5QUOCKqGGyiIiIiIqo16SSqYvSazk82rns2RkfzX+52l3Y/TGepHOkTmVpqodBQVyffHKVXcyPVNq0CNrd1RES5PB5xIhKY+k5ERERUqbxeUR/Q69iu1AFbTufUz+kkKr3EYvnvT6b+o1UHTCdREZWLy6W9TC0MECVagNhERURERFRr6uq0R9lPTOjzWIVSovRU6DFKjfoutemK5o5sAa8W/meBgIiq15JMsomK5Ghtv2WKeEREC0kwONXALfOZTERERDSfxsdF47fWFHqytKYbmy57f8likRuIKVvnMZvzPy+ZRhOt1B+jsTyJ+URpMu+tWhggSrQAsYmKiIiIqNYEg0AiUXwZvZqfCqVE6anQY1itpd2PniPnaHZkG+Bq4X8mm0RlMnE6P5JTX1/8+snJsqwGEVHVyJ7mWq+TkURERERzJd0IFA7rc3+zmRYwFpNrQpdt1IrF8teEZBpNtBqtVFV+WkEiPcjU8eZrWk4imhU2URERERHVGosFMGjs5unV/FSOJKpCqSqyU8KlaaVzUfnIvm5q4X8mm0QVi7H5heRojb6thfcNEZGesveLyzEVNREREdFspBuBjEZ97q/UGmD2oEWjUW49tAb7pJnNudMFpsk0mjDRhyqNTG2aU0wSVSU2URERERHVGlXVPoirpiSqQo0lHF1WvWRfN3oVDOeT3Z6bgFGIxcJ0DJKjVZzWaqIlIlposvclZU/wEREREc03vY7tZKbKy6Y1hXw+WoN90mKx/KlVMrfXeh6KIld/IdJLPK69jN0+9+tBRLpjdZWIiIio1iQS2k1UejU/eTz63E8xhVJV8o1cK0bmwJbKQ7aJrxaSmSIRuddeNCo/zSEtbFrFZb2mfCAiqhVutzipBsif4CMiIiKaL2azqOtFIvrcX6kJTtnNSomE+NIi26huNOYmXaXJNJpoPY9EAgiF5NaDSA/pqTeLkXn/EFHFYRMVERERUa2xWrUTfPRqfipH00ehkXelTscic2BL5SHbxFeOJr25ZjTKjR5lEhXJ0nqduN3lWQ8iomoxPj41wIBJVERERFTpQiHRAK5XjUBmqrxs2c1KZrOoV2iRbVRX1fxNJTKNJlo1IpOpNupIVD1kUts4qJeoKrGJioiIiKjWhELaB2hXEs2dj0whZbYKpaqUWgQqtemK5o7sCcyxsTldjbJIJ19oYRIVydJKmmLKChFRrsZGJlERERFR9aivF81GetWx8uz/FK1UZCdRRaNyacezbV5KJrWXyTcNYLZ4HPD5ZrceRKWQqW9yUC9RVWITFREREVGtcbvFSLFi8kVnVyqXK//lpTbYlNp0RXNHtqiVXbirVrGYXDHQbGZhheRoJQ1yW0dElCs7TaHU6aCJiIiIym1sTCRa65WgWer+T/a+k80mV6uQbfhSlPzHtDab9m1NJu3rmURF5SQzQIODeomqEpuoiIiIiGqN3y8aN4pJT2kyW5GIPvdTTKGGm1IbBWoh1ahW2O1yy2UX7qqV3a5d6APEiEmZ0Z1UMr02dxVDK92M2zoiolzZTdnl2HclIiIimo2GBnEgq1eCplaNcLrsels4LBLvtcg0QQFi2r58+2MyiflaA4qYREXlJtPoWAsDRIkWIDZREREREdUamSQqjen+FNkpyAqlROmpoSH/5Uyiql6JhNxytVBoCIXkCpYGg1yzFVE0Wvx6buuIiHJlN2UbWAolIiKiCjc6KgbP6HVsJ5OOnS27EUk2iUr2Mczm/MlYMvWfycni15tMoiZKVC4yjY61MECUaAFi5YCIiIio1sgkUek1bVg5RniNjua/vNQGLqazVA7ZJqpaKDTU1QEWy3yvBdWSurri13NbR0SUK/tkGhuWiYiIqNLV14umJL2O7WRTotKy08Nlk6hk6zyxGBAMzrxcpv6jNVVfPC6XaEWkF636DFAbA0SJFiA2URERERHVGqdT+wSRXvOxF0qJ0lOhx9AagTZdOVKzSI7sdH618D8LBLSTgwBRINVIiCMCoL39liniEREtJNnT5Za6/0hERERUbn6/SKLKl9h0JfI0FhXNn89uiLJYAKtV+zFkG7XM5vzHrDKNJlpNZUZj6Q1jRLORfZxRSC0MECVagNhERURERFRrJie1R4Dp1fxUKCVKT4Uew2gs7X540qxyyI4MjETmdj3KQTaJymSSK0wS1dcXv16maY+IaCHJnqaaU7wQERFRpUs3Aul1bFdqEk52TTEW0067B8QAMhmxWP5lZRpNtJ6HqsonYhHpQaY2rZWgRkQViU1URERERLXGZAIMGrt5ejU/lSOJqlCqitZznI7Tt1SOcrxuKoXfL1f4jMXY6EdyxseLX68UHVNMRLTwmM1TP5djKmoiIiKi2UinVOt1bFdqEk52erjBINcoojXYJ81sBhyOK7u91vNQVZHyTVQuMu9R2QZDIqoobKIiIiIiWoiqKYmqUANKqSlFpTZd0dyRfd3IJDhVOrs99+RtIRYLp2EjOVrFZTaMEhHlCganfpY9wUdEREQ0X1RVfNfr2K7UJKrsKeRlG7m0BvukFRpApjVtPaD9PAwGJnxTecnUpvmaJKpKPJNEREREVGtiMe2RVxpNLNJj3Vwu2SWvXKGCTakNJzLz1FN5yDbxZZ/0rFaxmFycfDTK0WlzJF1/rhlaxelQqCyrQURUNbzeqZ9lT/ARERERzRerVRzI6pVWXWoSVXazUiIhV9OQbVQ3GvMPmJNpNNF6HolEbdSRqHrI1MVrrihFtDCwiYqIiIio1tjt2lHbes3HXo7GpEIj72RGqWUrR8MXyZFNoso+6VmtFEVu5CaTqEiW01n8eqasEBHlyt7v0GsfmIiIiGiuBAIiVcnt1uf+Sj1GzG5WsljkGpxkG9VVVa4pKx+tup7JxH09Ki+Z2nShGRaIqKKxiYqIiIio1gQCQDyuvYwetJq19FAoVaXUBhufb/brQvqQLWqVY7rIuWYwyDVRMYmKZGkV4MbGyrMeRETVIjtNgfuDREREVOkaGkSzkV77LRMTMy4qWqbI3neKROQSsWQbvlQ1fzKPTKOJ1kDOeJz7elReMg2KHDBJVJXYREVERERUazwewGwuvozW9bIMZdidLHRAWmqjQC2kGtWKPAW8vLILd9UqGtVuagTEe9Jun/v1oeqn1ZTHbR0RUa7sNAWHY/7Wg4iIiEjG6Kg47tPr2E4mSSpb9r6TzSa3/yQ7jZ7RmL8mqZW4DGjXIJlEReUmk8DGxj6iqsQmKiIiIqJa4/MBsVjxZWSScWSUYzq/Qs1SpRaTmM5SOWQLeNmFu2rlcMg1LSYSYoQnkZZCU5ymcVtHRJSroWHqZ619ZCIiIqL5lk6i0uvYLpksbfnswYzhcOGE+GyygzXj8fy1RJlp0bQeg0lUVG4ySVS1MECUaAFiExURERFRrZFJotJo1pDusZKN656N7BNf2ZhEVb3yRbfnU+h/X02CQbkTtopSnukxqfppTaXAbR0RUa5amB6YiIiIFo50EpVeqUqJRGnLZ6eHyyZRyTKb809vJtNoopV2ZTIBLteVrRfRlZBJoqqFAaJECxCbqIiIiIhqjUwSlV5FBZmDxdkqdOKr1Dnlmc5SOWRTIMrx+pprLhdgscgtq1dCHOWQbNmrHlrNq7XwviEi0lP2PqNeU1oTERERzRWPRww+0ytVyW4vbfns9HDZJCrZOk8sBgQCMy+XaTTRGjCUSMhPK0ikB5lpKJlERVSVrqiJ6uGHH8ayZctgs9mwbds27Nu3r+Cy3/jGN/CmN70JXq8XXq8XO3bsmLH8Rz7yESiKkvN11113XcmqEREREZHTqT3dk16FmHIkBRUqkpQ69VmpTVc0d2SKDIC+ox3ny8QEEI1qL5dIcIohkqO1/a6F9w0RkZ6yP19lTgISERERzad0k5Fex3Z5psorOoQrOz3cbM5tqipEdl1nk0SllS6qKPKD2Ij0IFObZhIVUVUquYnqsccewwMPPIDPfOYzOHDgALZs2YKdO3dicHAw7/K7d+/GBz/4QTz//PPYs2cPOjs7ceedd+Ly5cs5y911113o6+vLfH3ve9+7smdEREREtNBFItpR3Xo1P5VjepRCqSqlpvaU2nRFcydPAS+vWmgqqquTK+KZzaWPDqWFqb6++PXxeFlWg4ioamSfCNTahhIRERHNt3QNQa9ju1KTcLIHgiUScuuRPQVgMbNJopJ5HmrNZVFTJTNItFlopYkTUUUquYnqS1/6Ej72sY/hvvvuw4YNG/C1r30NDocDjzzySN7l/+u//gt/9Ed/hKuvvhrr1q3DN7/5TSSTSezatStnOavVira2tsyXt0gsYyQSgd/vz/kiIiIiohSDQbvBSK/mp3IkURUazaaVtjWdzIEtlYfs6yaZnNv1KIdAQC6JKhZj7DzJ0ZqurxbeN0REespOT+CUp0RERFTp0o1AejUElZqE43JN/awocoMYZRvVTab8A8hkGk20noeqclARlZdMbZpJuERVqaQzSdFoFPv378eOHTum7sBgwI4dO7Bnzx6p+wiFQojFYmiYduJk9+7daGlpwdq1a/Hxj38cI0U+DB966CF4PJ7MV2dnZylPg4iIiKi2yTRRVVMSVaECSDhc2v2U2nRFc0f2dWOzze16lIPNJvfas1hyC5VEhXg8xa+XmWqBiGghyU5GYBIVERERVbp4XN+p6UqtAWY3nSuK3KBE2Ub1RCJ/PU9mUJnW8zAYmPBN5SXTIMV6NFFVKqmJanh4GIlEAq2trTmXt7a2or+/X+o+/vIv/xIdHR05jVh33XUXvvOd72DXrl34h3/4B7zwwgt461vfikSBaWgefPBB+Hy+zFd3d3cpT4OIiIiotkWj2kkkGk0s0hPl1dXJLnnlCj2XUuOQOfKncsgW8GohcTYel0sGikbl4+9pYdN6X+SbGoGIaCHLnvqFSVRERERU6ex2UUfQ69iu1CSq7H2neFwkZ2uRbVQ3GACzeeblMg1jY2PFr08keDxM5aU1yA3gzAhEVaqs7Y9f+MIX8P3vfx+7d++GLWtU+Qc+8IHMz5s2bcLmzZuxcuVK7N69G3fccceM+7FarbBydC0RERFRfk4nYDQWX0avxBuZacpmq9B+X6knwZg8UDlkk6iyC3fVSib2HmASFclLF9QL8XrLty5ERNUg+8RhqU34REREROU2MSEaL/Q6tvN4SkuSHxkB0jMAWa1yKeGl1OiudJpCp7P49SaTXFMLkV58vvxNgdlKnUmBiCpCSe2PTU1NMBqNGBgYyLl8YGAAbW1tRW/7T//0T/jCF76AZ555Bps3by667IoVK9DU1ISzZ8+WsnpEREREBIhiS6Ep8NImJ4tefYXljLlRaBRZqcUkrRFrVD6yJzBLHS1ZiYxGuVFnTKKaM+qVFmgrldb2nds6IqJc2U3Z/KwlIiKiStfQIBqN9Dq2k5kqL1v2vlM4rFlDBCCfVJ9MisSo6SIR7dtqJWLF46KphahcZAbschAHUVUqqYnKYrFg69at2LVrV+ayZDKJXbt2Yfv27QVv98UvfhGf//zn8dRTT+G6667TfJyenh6MjIygvb29lNUjIiIiIkAcnGmNgtFKqpIlE7c9W4Wmfiu1mMR0lsohO7ViLSRRRSLaTS+AGDFpt8/9+lD102oK47aOiChXdlO2TJICERER0XwaHRWp1nod22nVCKfL3ney2wGHQ/s2smk7RmP+xHmZRpNiicwAk6io/GQS2DidOFFVKnkizgceeADf+MY38Oijj+LEiRP4+Mc/jmAwiPvuuw8A8OEPfxgPPvhgZvl/+Id/wN/8zd/gkUcewbJly9Df34/+/n4EUokCgUAAn/zkJ/Hqq6/i4sWL2LVrF+655x6sWrUKO3fu1OlpEhERES0gfr/26Cy9mqgKpUTpqVDkeKnT8zGdpXKYJGcVr4UkKqdTrtkwmdR+3xIB2q8nbuuIiHJln4DUOvlGRERENN/0TqJSlNKWz25EmpyUGwgnW2eMx/MnW8k0mmg1w8fjbFih8pJp2quFAaJEC5Dk2Ysp73//+zE0NIRPf/rT6O/vx9VXX42nnnoKra2tAICuri4Ysqar+I//+A9Eo1G85z3vybmfz3zmM/jbv/1bGI1GHD58GI8++ijGx8fR0dGBO++8E5///OdhzdeNTERERETFySRR6TUfu9crmrbmUqEkqlIjupnOUjlkC3i18D8LBMRUfVqNY4pSemGTFqZgsHjxmCNviYhyZZ9Mk0mHJCIiIppP6SQqvY7tZKbKy5Y9/Z/NJpdEJVvPMJvF1H/TG9tlGk20pmU2mQCXS249iPTg82nX4EdGgNWry7M+RKSbkpuoAOD+++/H/fffn/e63bt35/x+8eLFovdlt9vx9NNPX8lqEBEREVE+6SSqYqPANGKypVs5ypF4UiiJyuHILexo4Wi0yiFbwJvrBr1ycLnkkqgURb+EOMqh1FpzWn198UZYrcIyEdFCk33ij9P5ERERUaVzu0US1cQE0NQ0+/urqwOGhuSXzx4EFomI5Cins/htolG5+47FxGCz6Y1ZIyPAihXFb9vYCHR1Fb4+kRCpWYUGYxLpzeHQTpVnEhVRVSp5Oj8iIiIiqnAOh3Yzhl4NReUoTBQaeVfq1GcyI+eoPGRHBtZCMu3EhFwxMR4vfXQoLUxa2+9aeN8QEekpkZj6uZQGfCIiIqL5kJ4+T6/m71KT3LNmG4LJJDcwrK5O7r7TSVTTyTSajIwUv15RtFOBiPQkk3Kr9boloorEJioiIiKiWhOLiRFrxejV/FQoJUpPeqWqcPqWyiFbwJse716N6urkCo4Wi/bITroiqtb2sNrU1xe/vtaeLxHRbGXvA2ptQ4mIiIjmWzoJSq+aSKlJONkDvJLJ3Ib0QmSTxNNJVNPJNJrIPI9aS6KmyibzHpVtMCSiisImKiIiIqKFSK/mp3KciCo08q7U0WW10JBTK2Sb+GQKdZUuGJRLoopG8xcSiabTSqJiwygRUa7sJmVO70xERESVLt0IpFdNpNQkHNn08Gyy9UGTCbDbr+wxtZ6HqspPK0ikB5kkcKbOE1UlNlERERER1RqTSXvklV5JVLIjzWajUPNTqdOxcIqryiHbxFcLUzBarVOjSIuxWK6sUEkLT6EpTtPyFaSJiBay7ARMJlERERFRpYtGRV1Pr+n8Sk2iym46NxrFVym3KSaRyN9UEg5r31ZrP85orI06ElUPmdq0ga0YRNWI71wiIiKiWhMOa6cu6ZVEVY6T9YVSVbze0u6HKT+VQ7aJT3bav0qWSMhNrxaN6jd1JdU2rdcJX0dERLmyTxwyiYqIiIgqndMp6np6HduVmkSVve8Ujcol6cg2qitK/qYsmUYTrYGc8TiPh6m8ZF73pc6kQEQVgU1URERERLXG5dJOvtGYj13RSrJKK8e0UYUatcbGSrufUpuuaO7INvGVOlqyEqmqXBOVxaL5viQCoJ2qx20dEVGu7BOHTH0kIiKiSuf3i6YivY7t3O7Sls/ed7LZ5NKdZBvVFSV/er5M2pVWMpfJxNRRKi+Z132pMykQUUVgExURERFRrfH7gVis+DLRqD6PpZV4pYdCo8hKLSaV2nRFc0f2BGapoyUrkdksVwyMRllYITla211u64iIcmU3ZfOzloiIiCqd1ysGY+l1bDc5OeOiokMns/edwmEgFNJ+DKdTbl0Sifw1yzzrOIPWsXA8ztRRKi+Zpj0OdCOqSmyiIiIiIqo1Hk/5ooK1RoHpodDUb0yiql7hsNxytZBENTmp3dQIiBGTWglDdEUkcsCqSyJR/Hpu64iIcmU3ZVss87ceRERERDLGxkRak17HdjIDu7Jl7zvZ7XJJVLKDNU2m/LVEmWYUrTR8JlFRuck07cmm8RNRRWETFREREVGt8fm0Cwt6nUAqlBKlp0IHmx5PaffDdJbKYZA8DKmFJCqXS+79pqrazTFEgHbzKrd1RES5svcZZabYJSIiIppPDQ36JlGV2kSVPf3f5KRcElW+Kfryicfzp07JPFettCsmUVG5yUyVWQsDRIkWIDZREREREdUat1uMvipGr6lMCqVE6anQY5TawFVq0xXNHa3XZ1otjCCcmJAbkamqPLFLcgKB4tfLFPGIiBaS7O2m1kADIiIiovk2OiqakvQ6tpOZKq/Q8jabSKPSIlvnMZuBurqZl8s0mvh8xa83GvPfN9FckalN18IAUaIFiE1URERERLVmYkL7BJFGc4rsALKyRBIXeoxSpxIsR2oWyZEZxQhoN4tUA9kkKqOxfNNwUnXTai6UfX8RES0U2dPlykxHQ0RERDSfXC4xyEqvAZClDirMTg+PRMSXFtlGrVgsf61HptFEq9FKVeXWlUgvMrXpcgxAJiLdsYmKiIiIqNbYbNrTpekVCV6OA8FCI+9Knfqs1KYrmjuyBbxaaCqSTaKKxUofHUoLk9b0BLIjgImIForspEc21RMREVGlC4fFd5kBWTLy1ACLjp3MrsWYTHLHmLKpWbNJopJptNKqhxLpKZnUXkavGjwRlRU/TYiIiIhqTSKhPS2YXs1P5UiiKpSqUmoTlcyBLZWHVhNImnQkWgVzOuWawSwWMdqUSItWElUtvG+IiPSU3cxcC1MFExERUW1LH9Np1fZkyTQoZcuuw6mq3HpoTbWXNpdJVIoiUr6JykWmNu10zv16EJHu2ERFREREVGtkmoX0an4qNRL8ShRqQCk1WUprikMqH9kmvlqIYZ+clHvtRaNMxyA5Wk2IsVhZVoOIqGpkpx3INnITERERzZd08pNex3YyDUrZspvOk0m5OqNso7rRmL+ely+dajqt55FMTqV4EZWDTG2aNRqiqsQmKiIiIqJaY7FoJ5HoNQp/Pps+8o1cK8Zun5v1oNLJNvHVQjKT2SwXJ88kqrmj0+DdiqH1OnE4yrMeRETVIjsZgUlUREREVOnCYVHX0yvBptQkquyajex0frKN6slk/qaS7OTQQrT244xGuWYsIr3I1Kb1SpQjorJiExURERFRrQmFtEeJ+f36PJbVqs/9FFNoxI7XW9r9MOWncsj+72ohLUK2WMIkKpIVDBa/Xq/tOxFRrcg+cVgL+xZERERU29xuUUvQ69iu1CSq7H2naFQuJVw2qV5RrnwKeq2aSTzO42EqL5kBGhbLnK8GEemPTVREREREtcbt1h4lphE3fIXljLlRKFVlbKy0+ym16YrmjuwJzFJHS1aiREKukcps1m+UKdU2rQIct3VERLmyTxwynYCIiIgq3fi4aDTS69iu1NTr7H0nu10u7Vi2eUlRRGLUdDKNJlrLmExMHaXykqlvljqTAhFVBDZREREREdUav1+MviomkdDnscoxr3uhQkypxaRSm65o7shON1bqaMlKZLXmLxBOF48Dk5Nzvz5U+7itIyLKld2Uzc9aIiIiqnQNDWIwll7HdnmSpIqGQTU0TP08OSm3/yRb50kk8k/dJ9NoopVgFY8zdZTKS6ZprxYGiBItQGyiIiIiIqo1Ho92EpXWdH+yZIsks5FdvMnGJKrqJdt8VwuFhlBI7vkajSKNikiL1uuJ2zoiolzZTdla+8hERERE8210VN8kqlKnz8uut9lsIo1Ki2ydx2zOf3+Fan/ZtKYVZBIVlZtM014tDBAlWoDYREVERERUa3w+7SQqjen8pMnGdc/G6Gj+y93u0u6H6SzVpxYKDS6XXCy9qurX3Ei1Tat5lds6IqJcHs/Uz6WeRCQiIiIqN69X3yQqmZpEtuzpj8NhuSQqVZW771hMDDabrlDtL5vWtIRMoqJyk5kqsxYGiBItQGyiIiIiIqo1Lpf29GETE/o8lsxIsbl6jFLnlC+16YrmjmwBrxb+ZxMT+aPqp2MT1ZxRIVnMrRZa22+ZIh4R0UISDE79LPOZTERERDSf0s1Teh3blVo/y058slrFlxbZOo/ZnNuklSbTaKLVIGU0Ak6n3HoQ6SH7OKOQWhggSrQAsYmKiIiIqNYEg0AiUXwZvZqfZEaKzdVjyBRxsuUb6UbzQ7aAJzPasdLJJlEZjaWPDqWFSWt6glp43xAR6Sl7Cr98J+2IiIiIKkm6EUivY7vZTAsYi8k1ocs0k6TvL19NSKbRRKvRSlXlpxUk0oNMHU+vaTmJqKzYREVERERUaywWwKCxm6fV/CQ71Uk5kqgKjbyTjQpPyz6BRvNL9nWjlahWDWSTqGIxNr+QHK3Rt9zWERHlyt4vLsdU1ERERESzkW4E0uvYLk8NsGjVL3vQotEoV5vRGuyTZjbnT4uSaTRhog9VGpnatM839+tBRLpjExURERFRrZGZEqyakqgKNZZwdFn1kn3d1EITlcMhioRaLBZOw0ZytIrTWk20REQLTTg89bPsCT4iIiKi+aQo+h3byUyVly17CnnZAYxag33SYrH8qVUyt9d6HorCQUVUXvG49jJ2+9yvBxHp7oo+gR9++GEsW7YMNpsN27Ztw759+4ou/8Mf/hDr1q2DzWbDpk2b8OSTT+Zcr6oqPv3pT6O9vR12ux07duzAmTNnrmTViIiIiCiR0G6k0qv5yePR536KKVQAyTdyrRiZA1sqD9kmvlpIZgqHtafXBERaVXahkqgQreJydrMAEREBbvfUz7In+IiIiIjmi9ksmpf0OrYrNcEpu1kpmZSracg2qhuNuUlXaTKNJlrPI5msjToSVQ+HQ3sZmfcPEVWckpuoHnvsMTzwwAP4zGc+gwMHDmDLli3YuXMnBgcH8y7/yiuv4IMf/CB+//d/HwcPHsS9996Le++9F0ePHs0s88UvfhH/+q//iq997WvYu3cvnE4ndu7ciTCLv0RERESls9m0E3z0an4KBPS5n2IKjbwrdToWmQNbKg/ZJr5yNOnNNZNJbnpMJlGRrLq64tdnNwsQEVFu4xSTqIiIiKjShUKijqBXjaDUNPrsZiWzWdQrtMg2qqtq/qYSmUYTrRqR0cjjYSovmcGQHNRLVJVKbqL60pe+hI997GO47777sGHDBnzta1+Dw+HAI488knf5f/mXf8Fdd92FT37yk1i/fj0+//nP49prr8W///u/AxApVF/5ylfw13/917jnnnuwefNmfOc730Fvby9+8pOf5L3PSCQCv9+f80VEREREKcGgdvFBr8QbmULKbBVqrPd6S7sf7jNWDtkTmGNjc7oaFYVJVCRLa7ARU1aIiHJlpylwG0lERESVrr5eNBv5fPrcX6n7P9n7TpGI+NIy20FwWon6QP5pALMlEvr9zYhkyNQ3OaiXqCqVNDlsNBrF/v378eCDD2YuMxgM2LFjB/bs2ZP3Nnv27MEDDzyQc9nOnTszDVIXLlxAf38/duzYkbne4/Fg27Zt2LNnDz7wgQ/MuM+HHnoIn/3sZ0tZdaoUr70mdsBGRkQXfTQq0iWMRtFd7/UC/f1AW9vU97Ex8SGTnprIYhEnmBobp5YZHASamsTJ0XQUaDwuIkDHx8V16WWHhkTnfSAwdeI3GhUjukdHgebmqWWHh8XOXzg8lSIQDotu9uFhoKVlalk+Jz4nPic+Jz4nPqdKeU6NjcDy5eL+RkenPnsbGsTjOhxinY4dE48xvREqkcCN2U1YBw6I9Y/FxAi0bMkksGGDWG+zWaxLNCr+jmNjU4+d/u52i2hto1F8TU6KA87s9Zy+r2A2i3UwmcS6+v3i/xaPA1ddNfWcYjFRZLLZxDLT76+lBXj5ZfF7NCqKQM3NwMWLwMaN4u+xYQNw/DiwYgXQ2zs16m9iAujoAM6fn1o2/X3ZMvH/s1rF+o2MiMvOnJm6v/T3zk6xvkajmI6wrw9YswY4eXLq/tatA06fBtrbpxri6uuB7u6p+0kvu3q1WP9qe07BoLgs3//J6516zxgM4n+WfuzVq4FLl8T7YHRULGM2T70menrE8zxzRnzv6RGX+3zivhwO8bpsbxd/o1WrgLNnxf339op1CATE6yj9flq0SPyNVq8Gzp0DliwBBgbE9dGoeN2ltxFLloj7W7MGuHBB/H37+4FNm8Rr3WqdisJ3OMR6pZ+3xyPWobdX/M1DIbFM+nskMvP9F42K1/v0ZaPR3Gkw09sIu33msvH4VNqbqk5t97KXCYXEbZPJqVStREKsz/Rlw+GZ25R4fP6eE4CtiTFg/1DtPKdVq8TvhbZ74+PAqVPi94YG8VnU0iJez16vuM/0OsZiU++LpqapZUdHxWs8/TmTfh4ul7if5uapZcfGxGdVLCaeg8EwtW1PL5P+7vOJdU0mxXqbzeI95/VOLTM0NPVeTP/d4/Gp58jnxOfE58TnVOpzGh8Xn8mNjUBXl7j++HHxeX3+vLhubEx8Bjoc4nZLl4r9ien7XC0tYj8mmRSf3f39Yr/79OmpfaNVq8R+m9ebu3/W3S2uO3UKWL9e7CstX55//+zixan9qXXrxLp0dop1t9vF32ZsDFi8WDyH9ev5nPic+Jz4nPic+Jz4nGrlOZ09K26TTAIvvDD1mOnnsmyZqEvY7WJfbGxsqh6Rvp/0c1m0SOx/rVqVU494T6wfr+zzYWO9Cdi0VtQl6uvFPlZXl9i/6u0V9ZOVK8XftVh9b2QEOHJkZpp8uq6XrinGYuL+PJ7cWqDLJZ5r+v+U3t8zGMR+ns8nbtPRkVs3yq6xpP8Pu3dP/e3TrxGPRywbDIrnlK8Wtny5+J84neJ+sv+u02tsS5aI9TebxfoNDYnnderU1LLp/1dHR26t6fJl8f85cWJq2fTrqblZ7GNHo2L/+9KlmevJ51Q5z8lmE4+RXd9Lf0+/n8bHxfu/p2fqsVetEu+z9DkGk0nc1/j4VM1y9WqxbVixQqxL+hwDMLNmuWaNeL8vWybqsvX1Yv3i8Zk1y7VrRX1z8WKxnXO5xHtoclI8Rm/v1HYpu745PCy2OQaDeF7pOiyf09RzGhoCrr0WVBsUVVVV2YV7e3uxaNEivPLKK9i+fXvm8k996lN44YUXsHfv3hm3sVgsePTRR/HBD34wc9lXv/pVfPazn8XAwABeeeUV3Hzzzejt7UV7e3tmmfe9731QFAWPPfbYjPuMRCKIZHU++/1+dHZ2wufzwc2oRiIiIiJdnOqfQLPLiganZb5XhYioZCOBCEaCUaxp5TSJRERERERERGmqquLIZR9WtdTBYSkpb4OIiKgq+f1+eDweqZ6iqvxktFqtsKbTHIiIiIhoTqxtY+MBEVWvxjorGut43EhERERERESUTVEUbF5cP9+rQUREVJEM2otMaWpqgtFoxMDAQM7lAwMDaGtry3ubtra2osunv5dyn0RERERERERERERERERERERERHopqYnKYrFg69at2LVrV+ayZDKJXbt25Uzvl2379u05ywPAs88+m1l++fLlaGtry1nG7/dj7969Be+TiIiIiIiIiIiIiIiIiIiIiIhILyVP5/fAAw/gd3/3d3HdddfhhhtuwFe+8hUEg0Hcd999AIAPf/jDWLRoER566CEAwJ/92Z/htttuwz//8z/j7rvvxve//328/vrr+M///E8AIjLyE5/4BP7u7/4Oq1evxvLly/E3f/M36OjowL333qvfMyUiIiKi/6+9e4/Jsv7/OP4COasIaoCoeCjTUvKYDrVa00mOZWZLc2SubKbhBHNoJ7Wtlae+WZp56I90SzPd1NI8jDxg5hlERRm6aeAJqRQxT6D3+/vHb1x1pxHffsKF3s/Hdm9cn8971/2++eO1i+t+7wIAAAAAAAAAAADAbfzPQ1RDhgzRL7/8osmTJ6uoqEidOnXShg0bFB0dLUkqLCyUv/8fD7jq2bOnli5dqnfffVdvv/222rRpo9WrV6tDhw5OzYQJE3T58mWNHDlSJSUl6t27tzZs2KCQkJAq9WRmkv7vCVYAAAAAAAAAAAAAAAAAUDFLVDFbVBk/q0pVLXfq1Ck1b97c7TYAAAAAAAAAAAAAAAAA1DInT55Us2bNKq25J4aoPB6Pzpw5o/r168vPz8/tdvA3SktL1bx5c508eVLh4eFutwPAh5A/ANxC/gBwC/kDwA1kDwC3kD8A3EL+AHAL+QNUnZnp0qVLio2N9frPerfzP/87v9rI39//H6fFUHuEh4cT5ABcQf4AcAv5A8At5A8AN5A9ANxC/gBwC/kDwC3kD1A1DRo0qFJd5SNWAAAAAAAAAAAAAAAAAHCPY4gKAAAAAAAAAAAAAAAAgE9jiAo1Jjg4WFOmTFFwcLDbrQDwMeQPALeQPwDcQv4AcAPZA8At5A8At5A/ANxC/gDVw8/MzO0mAAAAAAAAAAAAAAAAAMAtPIkKAAAAAAAAAAAAAAAAgE9jiAoAAAAAAAAAAAAAAACAT2OICgAAAAAAAAAAAAAAAIBPY4gKAAAAAAAAAAAAAAAAgE9jiAoAAAAAAAAAAAAAAACAT2OICjVm7ty5atmypUJCQtSjRw/t2bPH7ZYA1FJTp07Vo48+qvr16ysqKkoDBw5Ufn6+V821a9eUkpKiRo0aqV69enruued07tw5r5rCwkIlJSUpLCxMUVFRSk9P140bN7xqtm7dqi5duig4OFgPPPCAFi1adEs/5Bfgu6ZNmyY/Pz+lpaU5a+QPgOpy+vRpvfjii2rUqJFCQ0MVHx+vffv2OftmpsmTJ6tJkyYKDQ1V3759dezYMa9znD9/XsnJyQoPD1dERIRGjBih33//3avm4MGDeuyxxxQSEqLmzZtrxowZt/SyYsUKtWvXTiEhIYqPj9e6deuq50MDcNXNmzc1adIktWrVSqGhobr//vv1/vvvy8ycGrIHwJ2wbds2Pf3004qNjZWfn59Wr17ttV+bsqYqvQC4e1SWP+Xl5Zo4caLi4+NVt25dxcbG6qWXXtKZM2e8zkH+APg3/un6589GjRolPz8/ffLJJ17r5A9Q8xiiQo345ptv9MYbb2jKlCnKzs5Wx44dlZiYqOLiYrdbA1ALZWZmKiUlRbt27VJGRobKy8vVr18/Xb582akZN26c1qxZoxUrVigzM1NnzpzRoEGDnP2bN28qKSlJZWVl2rFjhxYvXqxFixZp8uTJTs2JEyeUlJSkJ598Ujk5OUpLS9Orr76qjRs3OjXkF+C79u7dqwULFuiRRx7xWid/AFSHCxcuqFevXgoMDNT69et15MgR/ec//1FkZKRTM2PGDM2ePVvz58/X7t27VbduXSUmJuratWtOTXJysg4fPqyMjAytXbtW27Zt08iRI5390tJS9evXTy1atFBWVpZmzpyp9957TwsXLnRqduzYoaFDh2rEiBHav3+/Bg4cqIEDByo3N7dmfhkAasz06dM1b948ffbZZ8rLy9P06dM1Y8YMzZkzx6khewDcCZcvX1bHjh01d+7c2+7XpqypSi8A7h6V5c+VK1eUnZ2tSZMmKTs7WytXrlR+fr4GDBjgVUf+APg3/un6p8KqVau0a9cuxcbG3rJH/gAuMKAGdO/e3VJSUpzjmzdvWmxsrE2dOtXFrgDcLYqLi02SZWZmmplZSUmJBQYG2ooVK5yavLw8k2Q7d+40M7N169aZv7+/FRUVOTXz5s2z8PBwu379upmZTZgwwdq3b+/1XkOGDLHExETnmPwCfNOlS5esTZs2lpGRYU888YSlpqaaGfkDoPpMnDjRevfu/bf7Ho/HYmJibObMmc5aSUmJBQcH29dff21mZkeOHDFJtnfvXqdm/fr15ufnZ6dPnzYzs88//9wiIyOdPKp477Zt2zrHgwcPtqSkJK/379Gjh7322mv/vw8JoNZJSkqyV155xWtt0KBBlpycbGZkD4DqIclWrVrlHNemrKlKLwDuXn/Nn9vZs2ePSbKCggIzI38A3Bl/lz+nTp2ypk2bWm5urrVo0cJmzZrl7JE/gDt4EhWqXVlZmbKystS3b19nzd/fX3379tXOnTtd7AzA3eLixYuSpIYNG0qSsrKyVF5e7pUr7dq1U1xcnJMrO3fuVHx8vKKjo52axMRElZaW6vDhw07Nn89RUVNxDvIL8F0pKSlKSkq6JSPIHwDV5bvvvlO3bt30/PPPKyoqSp07d9YXX3zh7J84cUJFRUVeudCgQQP16NHDK38iIiLUrVs3p6Zv377y9/fX7t27nZrHH39cQUFBTk1iYqLy8/N14cIFp6ayjAJw7+jZs6c2bdqko0ePSpIOHDig7du3q3///pLIHgA1ozZlTVV6AXBvu3jxovz8/BQRESGJ/AFQfTwej4YNG6b09HS1b9/+ln3yB3AHQ1Sodr/++qtu3rzp9UWiJEVHR6uoqMilrgDcLTwej9LS0tSrVy916NBBklRUVKSgoCDnD9kKf86VoqKi2+ZOxV5lNaWlpbp69Sr5BfioZcuWKTs7W1OnTr1lj/wBUF2OHz+uefPmqU2bNtq4caNGjx6tsWPHavHixZL+yI/KcqGoqEhRUVFe+wEBAWrYsOEdySjyB7j3vPnmm3rhhRfUrl07BQYGqnPnzkpLS1NycrIksgdAzahNWVOVXgDcu65du6aJEydq6NChCg8Pl0T+AKg+06dPV0BAgMaOHXvbffIHcEeA2w0AAFCZlJQU5ebmavv27W63AsAHnDx5UqmpqcrIyFBISIjb7QDwIR6PR926ddOHH34oSercubNyc3M1f/58DR8+3OXuANyrli9friVLlmjp0qVq3769cnJylJaWptjYWLIHAAD4lPLycg0ePFhmpnnz5rndDoB7XFZWlj799FNlZ2fLz8/P7XYA/AlPokK1a9y4serUqaNz5855rZ87d04xMTEudQXgbjBmzBitXbtWW7ZsUbNmzZz1mJgYlZWVqaSkxKv+z7kSExNz29yp2KusJjw8XKGhoeQX4IOysrJUXFysLl26KCAgQAEBAcrMzNTs2bMVEBCg6Oho8gdAtWjSpIkefvhhr7WHHnpIhYWFkv7Ij8pyISYmRsXFxV77N27c0Pnz5+9IRpE/wL0nPT3deRpVfHy8hg0bpnHjxjlP5CR7ANSE2pQ1VekFwL2nYoCqoKBAGRkZzlOoJPIHQPX48ccfVVxcrLi4OOc+dEFBgcaPH6+WLVtKIn8AtzBEhWoXFBSkrl27atOmTc6ax+PRpk2blJCQ4GJnAGorM9OYMWO0atUqbd68Wa1atfLa79q1qwIDA71yJT8/X4WFhU6uJCQk6NChQ14XmBV/AFd8QZmQkOB1joqainOQX4Dv6dOnjw4dOqScnBzn1a1bNyUnJzs/kz8AqkOvXr2Un5/vtXb06FG1aNFCktSqVSvFxMR45UJpaal2797tlT8lJSXKyspyajZv3iyPx6MePXo4Ndu2bVN5eblTk5GRobZt2yoyMtKpqSyjANw7rly5In9/79uDderUkcfjkUT2AKgZtSlrqtILgHtLxQDVsWPH9MMPP6hRo0Ze++QPgOowbNgwHTx40Os+dGxsrNLT07Vx40ZJ5A/gGgNqwLJlyyw4ONgWLVpkR44csZEjR1pERIQVFRW53RqAWmj06NHWoEED27p1q509e9Z5XblyxakZNWqUxcXF2ebNm23fvn2WkJBgCQkJzv6NGzesQ4cO1q9fP8vJybENGzbYfffdZ2+99ZZTc/z4cQsLC7P09HTLy8uzuXPnWp06dWzDhg1ODfkF4IknnrDU1FTnmPwBUB327NljAQEB9sEHH9ixY8dsyZIlFhYWZl999ZVTM23aNIuIiLBvv/3WDh48aM8884y1atXKrl696tQ89dRT1rlzZ9u9e7dt377d2rRpY0OHDnX2S0pKLDo62oYNG2a5ubm2bNkyCwsLswULFjg1P/30kwUEBNhHH31keXl5NmXKFAsMDLRDhw7VzC8DQI0ZPny4NW3a1NauXWsnTpywlStXWuPGjW3ChAlODdkD4E64dOmS7d+/3/bv32+S7OOPP7b9+/dbQUGBmdWurKlKLwDuHpXlT1lZmQ0YMMCaNWtmOTk5Xveir1+/7pyD/AHwb/zT9c9ftWjRwmbNmuW1Rv4ANY8hKtSYOXPmWFxcnAUFBVn37t1t165dbrcEoJaSdNvXl19+6dRcvXrVXn/9dYuMjLSwsDB79tln7ezZs17n+fnnn61///4WGhpqjRs3tvHjx1t5eblXzZYtW6xTp04WFBRkrVu39nqPCuQX4Nv+OkRF/gCoLmvWrLEOHTpYcHCwtWvXzhYuXOi17/F4bNKkSRYdHW3BwcHWp08fy8/P96r57bffbOjQoVavXj0LDw+3l19+2S5duuRVc+DAAevdu7cFBwdb06ZNbdq0abf0snz5cnvwwQctKCjI2rdvb99///2d/8AAXFdaWmqpqakWFxdnISEh1rp1a3vnnXe8vjQkewDcCVu2bLntvZ7hw4ebWe3Kmqr0AuDuUVn+nDhx4m/vRW/ZssU5B/kD4N/4p+ufv7rdEBX5A9Q8PzOzmnjiFQAAAAAAAAAAAAAAAADURv5uNwAAAAAAAAAAAAAAAAAAbmKICgAAAAAAAAAAAAAAAIBPY4gKAAAAAAAAAAAAAAAAgE9jiAoAAAAAAAAAAAAAAACAT2OICgAAAAAAAAAAAAAAAIBPY4gKAAAAAAAAAAAAAAAAgE9jiAoAAAAAAAAAAAAAAACAT2OICgAAAAAAAAAAAAAAAIBPY4gKAAAAAAAAAAAAAAAAgE9jiAoAAAAAAAAAAAAAAACAT2OICgAAAAAAAAAAAAAAAIBP+y9GDjCsNLrJVwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(test[:, 3])\n", + "plt.fill_between(np.arange(test_labels.shape[0]), test_labels, color='red', alpha=0.3, linestyle='dashed', linewidth=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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y6p4rF+/fjalAQAATDU66ncFqIZUyu8KAAAAAAAAAAAAUKNmHKJ64IEHtHnzZm3btk1PP/201qxZo97eXh05cmTa9rt27dINN9ygRx55RLt371Z3d7euvvpqvf322xXt1q9fr8OHD3uP73//+7PbIwAAgMliMb8rQDW0tcmS5XcVAAAAAAAAAAAAqEEzDlF94xvf0M0336xNmzbpggsu0N133614PK577rln2vb/8i//oj/90z/VJZdcovPPP1//9E//pGKxqL6+vop20WhUnZ2d3qOtre2ENWQyGSWTyYoHAADACRWLfleAahga8rsCAAAAAAAAAAAA1KgZhaiy2az27t2rnp6e0gYCAfX09Gj37t2ntI3x8XHlcjktWrSoYvmuXbu0ZMkSrVq1SrfeeqsGBwdPuI3t27erpaXFe3R3d89kNwAAwEJTKPhdAarhJCF8AAAAAAAAAAAA4GRmFKI6duyYCoWCOjo6KpZ3dHSov7//lLbx+c9/Xl1dXRVBrPXr1+t73/ue+vr69LWvfU2PPvqorrnmGhVO0OG5ZcsWjYyMeI8DBw7MZDcAAMBCE436XQGqgZGoAAAAAAAAAAAAMEuhar7ZnXfeqfvvv1+7du1SLBbzll9//fXe96tXr9bFF1+sc889V7t27dJVV101ZTvRaFRROkMBAMCpGhuTyq49UKdaWiQxdSMAAAAAAAAAAABmbkYjUbW3tysYDGpgYKBi+cDAgDo7O0+67t/8zd/ozjvv1L//+7/r4osvPmnbc845R+3t7XrttddmUh4AAMD0Wlv9rgDVMDrqdwUAAAAAAAAAAACoUTMKUUUiEV122WXq6+vzlhWLRfX19WndunUnXO+uu+7SV7/6Ve3cuVOXX375O77PwYMHNTg4qKVLl86kPAAAgOkND/tdAaqB0cYAAAAAAAAAAAAwSzMKUUnS5s2b9Z3vfEf33XefXnrpJd16661KpVLatGmTJOnGG2/Uli1bvPZf+9rX9KUvfUn33HOPli9frv7+fvX392tsbEySNDY2pttvv11PPPGE3nzzTfX19em6667TypUr1dvbe5p2EwAALGiMRLUw5PN+VwAAAAAAAAAAAIAaFZrpChs3btTRo0e1detW9ff365JLLtHOnTvV0dEhSdq/f78CgVI26+///u+VzWb1h3/4hxXb2bZtm7785S8rGAzqueee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Fa8bY7R161YtXbpUDQ0N6unp0auvvlrR5vjx4/rYxz6mRCKh1tZWfeITn9DY2FhFm+eee05XXnmlYrGYuru7ddddd02p5cEHH9T555+vWCym1atX66c//elp318AQHW80/nl4x//+JS/Z9avX1/RhvMLAKDc9u3b9d73vlfNzc1asmSJNmzYoFdeeaWiTTU/D6PvBgDqw6mcX373d393yt8vn/rUpyracH7BQkeIClXxwAMPaPPmzdq2bZuefvpprVmzRr29vTpy5IjfpQEA5oELL7xQhw8f9h6/+MUvvNc++9nP6t/+7d/04IMP6tFHH9WhQ4f0kY98xHu9UCjo2muvVTab1eOPP6777rtP9957r7Zu3eq12bdvn6699lp9+MMf1rPPPqvbbrtNn/zkJ/Wzn/2sqvsJAJg7qVRKa9as0Y4dO6Z9/a677tK3vvUt3X333dqzZ48aGxvV29urdDrttfnYxz6mF154QQ8//LB+/OMf67HHHtMtt9zivZ5MJnX11Vfr7LPP1t69e/X1r39dX/7yl/WP//iPXpvHH39cN9xwgz7xiU/omWee0YYNG7RhwwY9//zzc7fzAIA5807nF0lav359xd8z3//+9yte5/wCACj36KOP6tOf/rSeeOIJPfzww8rlcrr66quVSqW8NtX6PIy+GwCoH6dyfpGkm2++ueLvl/IbODi/AJIMUAVXXHGF+fSnP+09LxQKpqury2zfvt3HqgAA88G2bdvMmjVrpn1teHjYhMNh8+CDD3rLXnrpJSPJ7N692xhjzE9/+lMTCARMf3+/1+bv//7vTSKRMJlMxhhjzB133GEuvPDCim1v3LjR9Pb2nua9AQDMB5LMD3/4Q+95sVg0nZ2d5utf/7q3bHh42ESjUfP973/fGGPMiy++aCSZX/3qV16b//N//o+xLMu8/fbbxhhjvv3tb5u2tjbv/GKMMZ///OfNqlWrvOcf/ehHzbXXXltRz9q1a82f/MmfnNZ9BABU3+TzizHG3HTTTea666474TqcXwAA7+TIkSNGknn00UeNMdX9PIy+GwCoX5PPL8YY8zu/8zvmz//8z0+4DucXwBhGosKcy2az2rt3r3p6erxlgUBAPT092r17t4+VAQDmi1dffVVdXV0655xz9LGPfUz79++XJO3du1e5XK7iHHL++efrrLPO8s4hu3fv1urVq9XR0eG16e3tVTKZ1AsvvOC1Kd+G24bzEAAsDPv27VN/f3/FuaClpUVr166tOJ+0trbq8ssv99r09PQoEAhoz549XpsPfehDikQiXpve3l698sorGhoa8tpwzgGAhWXXrl1asmSJVq1apVtvvVWDg4Pea5xfAADvZGRkRJK0aNEiSdX7PIy+GwCob5PPL65/+Zd/UXt7uy666CJt2bJF4+Pj3mucXwAp5HcBqH/Hjh1ToVCoONhKUkdHh15++WWfqgIAzBdr167Vvffeq1WrVunw4cP6yle+oiuvvFLPP/+8+vv7FYlE1NraWrFOR0eH+vv7JUn9/f3TnmPc107WJplMamJiQg0NDXO0dwCA+cA9H0x3Lig/VyxZsqTi9VAopEWLFlW0WbFixZRtuK+1tbWd8JzjbgMAUF/Wr1+vj3zkI1qxYoVef/11/eVf/qWuueYa7d69W8FgkPMLAOCkisWibrvtNn3gAx/QRRddJElV+zxsaGiIvhsAqFPTnV8k6b/8l/+is88+W11dXXruuef0+c9/Xq+88or+9V//VRLnF0AiRAUAAHx2zTXXeN9ffPHFWrt2rc4++2z94Ac/INwEAAAAYF67/vrrve9Xr16tiy++WOeee6527dqlq666ysfKAAC14NOf/rSef/55/eIXv/C7FABAHTnR+eWWW27xvl+9erWWLl2qq666Sq+//rrOPffcapcJzEtM54c5197ermAwqIGBgYrlAwMD6uzs9KkqAMB81draqve85z167bXX1NnZqWw2q+Hh4Yo25eeQzs7Oac8x7msna5NIJAhqAcAC4J4PTvY3SWdnp44cOVLxej6f1/Hjx0/LOYe/fQBgYTjnnHPU3t6u1157TRLnFwDAif3Zn/2ZfvzjH+uRRx7RmWee6S2v1udh9N0AQH060fllOmvXrpWkir9fOL9goSNEhTkXiUR02WWXqa+vz1tWLBbV19endevW+VgZAGA+Ghsb0+uvv66lS5fqsssuUzgcrjiHvPLKK9q/f793Dlm3bp1+85vfVHRMPPzww0okErrgggu8NuXbcNtwHgKAhWHFihXq7OysOBckk0nt2bOn4nwyPDysvXv3em1+/vOfq1gseh8orVu3To899phyuZzX5uGHH9aqVavU1tbmteGcAwAL18GDBzU4OKilS5dK4vwCAJjKGKM/+7M/0w9/+EP9/Oc/nzKla7U+D6PvBgDqyzudX6bz7LPPSlLF3y+cX7DgGaAK7r//fhONRs29995rXnzxRXPLLbeY1tZW09/f73dpAACffe5znzO7du0y+/btM7/85S9NT0+PaW9vN0eOHDHGGPOpT33KnHXWWebnP/+5eeqpp8y6devMunXrvPXz+by56KKLzNVXX22effZZs3PnTnPGGWeYLVu2eG3eeOMNE4/Hze23325eeukls2PHDhMMBs3OnTurvr8AgLkxOjpqnnnmGfPMM88YSeYb3/iGeeaZZ8xbb71ljDHmzjvvNK2treZHP/qRee6558x1111nVqxYYSYmJrxtrF+/3vz2b/+22bNnj/nFL35hzjvvPHPDDTd4rw8PD5uOjg7zx3/8x+b55583999/v4nH4+Yf/uEfvDa//OUvTSgUMn/zN39jXnrpJbNt2zYTDofNb37zm+r9MAAAp83Jzi+jo6PmL/7iL8zu3bvNvn37zP/9v//XXHrppea8884z6XTa2wbnFwBAuVtvvdW0tLSYXbt2mcOHD3uP8fFxr021Pg+j7wYA6sc7nV9ee+0181d/9VfmqaeeMvv27TM/+tGPzDnnnGM+9KEPedvg/AIYQ4gKVfPf//t/N2eddZaJRCLmiiuuME888YTfJQEA5oGNGzeapUuXmkgkYpYtW2Y2btxoXnvtNe/1iYkJ86d/+qemra3NxONx85/+038yhw8frtjGm2++aa655hrT0NBg2tvbzec+9zmTy+Uq2jzyyCPmkksuMZFIxJxzzjnmn//5n6uxewCAKnnkkUeMpCmPm266yRhjTLFYNF/60pdMR0eHiUaj5qqrrjKvvPJKxTYGBwfNDTfcYJqamkwikTCbNm0yo6OjFW1+/etfmw9+8IMmGo2aZcuWmTvvvHNKLT/4wQ/Me97zHhOJRMyFF15ofvKTn8zZfgMA5tbJzi/j4+Pm6quvNmeccYYJh8Pm7LPPNjfffPOUjgHOLwCActOdVyRVfFZVzc/D6LsBgPrwTueX/fv3mw996ENm0aJFJhqNmpUrV5rbb7/djIyMVGyH8wsWOssYY6o37hUAAAAAAAAAAAAAAAAAzC8BvwsAAAAAAAAAAAAAAAAAAD8RogIAAAAAAAAAAAAAAACwoBGiAgAAAAAAAAAAAAAAALCgEaICAAAAAAAAAAAAAAAAsKARogIAAAAAAAAAAAAAAACwoBGiAgAAAAAAAAAAAAAAALCgEaICAAAAAAAAAAAAAAAAsKARogIAAAAAAAAAAAAAAACwoBGiAgAAAAAAAAAAAAAAALCgEaICAAAAAAAAAAAAAAAAsKARogIAAAAAAAAAAAAAAACwoP3/ixKvrMcdqXoAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(val[:, 3])\n", + "plt.fill_between(np.arange(validation_labels.shape[0]), validation_labels, color='red', alpha=0.3, linestyle='dashed', linewidth=0.3)" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3139" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.sum(validation_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6838" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.sum(test_labels_clipped)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "test = np.load('../processed/WADI/test.npy')\n", + "train = np.load('../processed/WADI/train.npy')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(146883, 123)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(784571, 123)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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9fWZOnVqnnnmmbKan//85/nbv/3bDBkyJM3NzfnUpz61xV5uvPHGjBgxIkOGDMnIkSNzyy239M2hgX63adOmXHjhhRk+fHh22223vPKVr8zHP/7xFEVRqtF/gEq444478g//8A9pampKVVVVbrrpprL5nanX7MhegJeG7fWeDRs25Pzzz8/IkSPzile8Ik1NTTnttNPy+OOPl62h9wB/ief7u8+fet/73peqqqpcfvnlZeP6D7z4hLJ4Sbrhhhsya9asXHzxxVmyZEmOOeaYtLe3Z9WqVf29NWAndPvtt2f69On56U9/mo6OjmzYsCEnn3xy1q5dW6o555xz8v3vfz833nhjbr/99jz++ON561vfWprftGlTxo8fn/Xr1+fOO+/M1772tVxzzTW56KKLSjUPPvhgxo8fn5NOOilLly7NzJkz8573vCc//OEPSzX6F+y67r777nzxi1/Mq171qrJx/QfoC7/73e/ymte8JoMGDcoPfvCD/PKXv8xnPvOZ7LXXXqWaT33qU7niiisyd+7cLFq0KK94xSvS3t6e5557rlQzZcqU3Hfffeno6Mi8efNyxx13ZNq0aaX5np6enHzyyTnooIOyePHifPrTn85HP/rRfOlLXyrV3HnnnZk8eXKmTp2ae++9NxMmTMiECROybNmyF+eHAbyoLr300lx99dW58sors3z58lx66aX51Kc+lc9//vOlGv0HqIS1a9fmmGOOyVVXXbXV+Z2p1+zIXoCXhu31nmeffTZLlizJhRdemCVLluQ73/lOVqxYkTe96U1ldXoP8Jd4vr/7bPbd7343P/3pT9PU1LTFnP4D/aCAl6CxY8cW06dPL33etGlT0dTUVMyZM6cfdwW8VKxatapIUtx+++1FURTFmjVrikGDBhU33nhjqWb58uVFkqKzs7MoiqK45ZZbiurq6qKrq6tUc/XVVxe1tbXFunXriqIoig996EPFUUcdVfasU089tWhvby991r9g1/T0008Xhx12WNHR0VGceOKJxdlnn10Uhf4D9J3zzz+/eO1rX7vN+d7e3qKxsbH49Kc/XRpbs2ZNUVNTU3zzm98siqIofvnLXxZJirvvvrtU84Mf/KCoqqoqHnvssaIoiuILX/hCsddee5X60eZnH3744aXPb3/724vx48eXPb+lpaV473vf+9cdEtgpjR8/vnj3u99dNvbW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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.rcParams['figure.figsize'] = 30, 2\n", + "plt.plot(test[:, 7])\n", + "#plt.fill_between(np.arange(validation_labels.shape[0]), validation_labels, color='red', alpha=0.3, linestyle='dashed', linewidth=0.3)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py3.9test", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "27efe6010b91a164a18a011cd71b7afbe2f076e5b83b7f8099f414d97e11e710" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/subject1-4/AdaDiff/denoising_diffusion_pytorch_1d.py b/subject1-4/AdaDiff/denoising_diffusion_pytorch_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..d31054523cf339a01f871596f4fa5958c97974ae --- /dev/null +++ b/subject1-4/AdaDiff/denoising_diffusion_pytorch_1d.py @@ -0,0 +1,829 @@ +import math +from pathlib import Path +from random import random +from functools import partial +from collections import namedtuple +from multiprocessing import cpu_count + +import torch +from torch import nn, einsum, Tensor +import torch.nn.functional as F +from torch.cuda.amp import autocast +from torch.optim import Adam +from torch.utils.data import Dataset, DataLoader + +from einops import rearrange, reduce +from einops.layers.torch import Rearrange + +from accelerate import Accelerator +from ema_pytorch import EMA + +from tqdm.auto import tqdm + + +# constants + +ModelPrediction = namedtuple('ModelPrediction', ['pred_noise', 'pred_x_start']) + +# helpers functions + +def exists(x): + return x is not None + +def default(val, d): + if exists(val): + return val + return d() if callable(d) else d + +def identity(t, *args, **kwargs): + return t + +def cycle(dl): + while True: + for data in dl: + yield data + +def has_int_squareroot(num): + return (math.sqrt(num) ** 2) == num + +def num_to_groups(num, divisor): + groups = num // divisor + remainder = num % divisor + arr = [divisor] * groups + if remainder > 0: + arr.append(remainder) + return arr + +def convert_image_to_fn(img_type, image): + if image.mode != img_type: + return image.convert(img_type) + return image + +# normalization functions + +def normalize_to_neg_one_to_one(img): + return img * 2 - 1 + +def unnormalize_to_zero_to_one(t): + return (t + 1) * 0.5 + +# data + +class Dataset1D(Dataset): + def __init__(self, tensor: Tensor): + super().__init__() + self.tensor = tensor.clone() + + def __len__(self): + return len(self.tensor) + + def __getitem__(self, idx): + return self.tensor[idx].clone() + +# small helper modules + +class Residual(nn.Module): + def __init__(self, fn): + super().__init__() + self.fn = fn + + def forward(self, x, *args, **kwargs): + return self.fn(x, *args, **kwargs) + x + +def Upsample(dim, dim_out = None): + return nn.Sequential( + nn.Upsample(scale_factor = 2, mode = 'nearest'), + nn.Conv1d(dim, default(dim_out, dim), 3, padding = 1) + ) + +def Downsample(dim, dim_out = None): + return nn.Conv1d(dim, default(dim_out, dim), 4, 2, 1) + +class RMSNorm(nn.Module): + def __init__(self, dim): + super().__init__() + self.g = nn.Parameter(torch.ones(1, dim, 1)) + + def forward(self, x): + return F.normalize(x, dim = 1) * self.g * (x.shape[1] ** 0.5) + +class PreNorm(nn.Module): + def __init__(self, dim, fn): + super().__init__() + self.fn = fn + self.norm = RMSNorm(dim) + + def forward(self, x): + x = self.norm(x) + return self.fn(x) + +# sinusoidal positional embeds + +class SinusoidalPosEmb(nn.Module): + def __init__(self, dim, theta = 10000): + super().__init__() + self.dim = dim + self.theta = theta + + def forward(self, x): + device = x.device + half_dim = self.dim // 2 + emb = math.log(self.theta) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, device=device) * -emb) + emb = x[:, None] * emb[None, :] + emb = torch.cat((emb.sin(), emb.cos()), dim=-1) + return emb + +class RandomOrLearnedSinusoidalPosEmb(nn.Module): + """ following @crowsonkb 's lead with random (learned optional) sinusoidal pos emb """ + """ https://github.com/crowsonkb/v-diffusion-jax/blob/master/diffusion/models/danbooru_128.py#L8 """ + + def __init__(self, dim, is_random = False): + super().__init__() + assert (dim % 2) == 0 + half_dim = dim // 2 + self.weights = nn.Parameter(torch.randn(half_dim), requires_grad = not is_random) + + def forward(self, x): + x = rearrange(x, 'b -> b 1') + freqs = x * rearrange(self.weights, 'd -> 1 d') * 2 * math.pi + fouriered = torch.cat((freqs.sin(), freqs.cos()), dim = -1) + fouriered = torch.cat((x, fouriered), dim = -1) + return fouriered + +# building block modules + +class Block(nn.Module): + def __init__(self, dim, dim_out, groups = 8): + super().__init__() + self.proj = nn.Conv1d(dim, dim_out, 3, padding = 1) + self.norm = nn.GroupNorm(groups, dim_out) + self.act = nn.SiLU() + + def forward(self, x, scale_shift = None): + x = self.proj(x) + x = self.norm(x) + + if exists(scale_shift): + scale, shift = scale_shift + x = x * (scale + 1) + shift + + x = self.act(x) + return x + +class ResnetBlock(nn.Module): + def __init__(self, dim, dim_out, *, time_emb_dim = None, groups = 8): + super().__init__() + self.mlp = nn.Sequential( + nn.SiLU(), + nn.Linear(time_emb_dim, dim_out * 2) + ) if exists(time_emb_dim) else None + + self.block1 = Block(dim, dim_out, groups = groups) + self.block2 = Block(dim_out, dim_out, groups = groups) + self.res_conv = nn.Conv1d(dim, dim_out, 1) if dim != dim_out else nn.Identity() + + def forward(self, x, time_emb = None): + + scale_shift = None + if exists(self.mlp) and exists(time_emb): + time_emb = self.mlp(time_emb) + time_emb = rearrange(time_emb, 'b c -> b c 1') + scale_shift = time_emb.chunk(2, dim = 1) + + h = self.block1(x, scale_shift = scale_shift) + + h = self.block2(h) + + return h + self.res_conv(x) + +class LinearAttention(nn.Module): + def __init__(self, dim, heads = 4, dim_head = 32): + super().__init__() + self.scale = dim_head ** -0.5 + self.heads = heads + hidden_dim = dim_head * heads + self.to_qkv = nn.Conv1d(dim, hidden_dim * 3, 1, bias = False) + + self.to_out = nn.Sequential( + nn.Conv1d(hidden_dim, dim, 1), + RMSNorm(dim) + ) + + def forward(self, x): + b, c, n = x.shape + qkv = self.to_qkv(x).chunk(3, dim = 1) + q, k, v = map(lambda t: rearrange(t, 'b (h c) n -> b h c n', h = self.heads), qkv) + + q = q.softmax(dim = -2) + k = k.softmax(dim = -1) + + q = q * self.scale + + context = torch.einsum('b h d n, b h e n -> b h d e', k, v) + + out = torch.einsum('b h d e, b h d n -> b h e n', context, q) + out = rearrange(out, 'b h c n -> b (h c) n', h = self.heads) + return self.to_out(out) + +class Attention(nn.Module): + def __init__(self, dim, heads = 4, dim_head = 32): + super().__init__() + self.scale = dim_head ** -0.5 + self.heads = heads + hidden_dim = dim_head * heads + + self.to_qkv = nn.Conv1d(dim, hidden_dim * 3, 1, bias = False) + self.to_out = nn.Conv1d(hidden_dim, dim, 1) + + def forward(self, x): + b, c, n = x.shape + qkv = self.to_qkv(x).chunk(3, dim = 1) + q, k, v = map(lambda t: rearrange(t, 'b (h c) n -> b h c n', h = self.heads), qkv) + + q = q * self.scale + + sim = einsum('b h d i, b h d j -> b h i j', q, k) + attn = sim.softmax(dim = -1) + out = einsum('b h i j, b h d j -> b h i d', attn, v) + + out = rearrange(out, 'b h n d -> b (h d) n') + return self.to_out(out) + +# model + +class Unet1D(nn.Module): + def __init__( + self, + dim, + init_dim = None, + out_dim = None, + dim_mults=(1, 2, 4, 8), + channels = 3, + self_condition = False, + resnet_block_groups = 8, + learned_variance = False, + learned_sinusoidal_cond = False, + random_fourier_features = False, + learned_sinusoidal_dim = 16, + sinusoidal_pos_emb_theta = 10000, + attn_dim_head = 32, + attn_heads = 4 + ): + super().__init__() + + # determine dimensions + + self.channels = channels + self.self_condition = self_condition + input_channels = channels * (2 if self_condition else 1) + + init_dim = default(init_dim, dim) + self.init_conv = nn.Conv1d(input_channels, init_dim, 7, padding = 3) + + dims = [init_dim, *map(lambda m: dim * m, dim_mults)] + in_out = list(zip(dims[:-1], dims[1:])) + + block_klass = partial(ResnetBlock, groups = resnet_block_groups) + + # time embeddings + + time_dim = dim * 4 + + self.random_or_learned_sinusoidal_cond = learned_sinusoidal_cond or random_fourier_features + + if self.random_or_learned_sinusoidal_cond: + sinu_pos_emb = RandomOrLearnedSinusoidalPosEmb(learned_sinusoidal_dim, random_fourier_features) + fourier_dim = learned_sinusoidal_dim + 1 + else: + sinu_pos_emb = SinusoidalPosEmb(dim, theta = sinusoidal_pos_emb_theta) + fourier_dim = dim + + self.time_mlp = nn.Sequential( + sinu_pos_emb, + nn.Linear(fourier_dim, time_dim), + nn.GELU(), + nn.Linear(time_dim, time_dim) + ) + + # layers + + self.downs = nn.ModuleList([]) + self.ups = nn.ModuleList([]) + num_resolutions = len(in_out) + + for ind, (dim_in, dim_out) in enumerate(in_out): + is_last = ind >= (num_resolutions - 1) + + self.downs.append(nn.ModuleList([ + block_klass(dim_in, dim_in, time_emb_dim = time_dim), + block_klass(dim_in, dim_in, time_emb_dim = time_dim), + Residual(PreNorm(dim_in, LinearAttention(dim_in))), + Downsample(dim_in, dim_out) if not is_last else nn.Conv1d(dim_in, dim_out, 3, padding = 1) + ])) + + mid_dim = dims[-1] + self.mid_block1 = block_klass(mid_dim, mid_dim, time_emb_dim = time_dim) + self.mid_attn = Residual(PreNorm(mid_dim, Attention(mid_dim, dim_head = attn_dim_head, heads = attn_heads))) + self.mid_block2 = block_klass(mid_dim, mid_dim, time_emb_dim = time_dim) + + for ind, (dim_in, dim_out) in enumerate(reversed(in_out)): + is_last = ind == (len(in_out) - 1) + + self.ups.append(nn.ModuleList([ + block_klass(dim_out + dim_in, dim_out, time_emb_dim = time_dim), + block_klass(dim_out + dim_in, dim_out, time_emb_dim = time_dim), + Residual(PreNorm(dim_out, LinearAttention(dim_out))), + Upsample(dim_out, dim_in) if not is_last else nn.Conv1d(dim_out, dim_in, 3, padding = 1) + ])) + + default_out_dim = channels * (1 if not learned_variance else 2) + self.out_dim = default(out_dim, default_out_dim) + + self.final_res_block = block_klass(dim * 2, dim, time_emb_dim = time_dim) + self.final_conv = nn.Conv1d(dim, self.out_dim, 1) + + def forward(self, x, time, x_self_cond = None): + if self.self_condition: + x_self_cond = default(x_self_cond, lambda: torch.zeros_like(x)) + x = torch.cat((x_self_cond, x), dim = 1) + + x = self.init_conv(x) + r = x.clone() + + t = self.time_mlp(time) + + h = [] + + for block1, block2, attn, downsample in self.downs: + x = block1(x, t) + h.append(x) + + x = block2(x, t) + x = attn(x) + h.append(x) + + x = downsample(x) + + x = self.mid_block1(x, t) + x = self.mid_attn(x) + x = self.mid_block2(x, t) + + for block1, block2, attn, upsample in self.ups: + x = torch.cat((x, h.pop()), dim = 1) + x = block1(x, t) + + x = torch.cat((x, h.pop()), dim = 1) + x = block2(x, t) + x = attn(x) + + x = upsample(x) + + x = torch.cat((x, r), dim = 1) + + x = self.final_res_block(x, t) + return self.final_conv(x) + +# gaussian diffusion trainer class + +def extract(a, t, x_shape): + b, *_ = t.shape + out = a.gather(-1, t) + return out.reshape(b, *((1,) * (len(x_shape) - 1))) + +def linear_beta_schedule(timesteps): + scale = 1000 / timesteps + beta_start = scale * 0.0001 + beta_end = scale * 0.02 + return torch.linspace(beta_start, beta_end, timesteps, dtype = torch.float64) + +def cosine_beta_schedule(timesteps, s = 0.008): + """ + cosine schedule + as proposed in https://openreview.net/forum?id=-NEXDKk8gZ + """ + steps = timesteps + 1 + x = torch.linspace(0, timesteps, steps, dtype = torch.float64) + alphas_cumprod = torch.cos(((x / timesteps) + s) / (1 + s) * math.pi * 0.5) ** 2 + alphas_cumprod = alphas_cumprod / alphas_cumprod[0] + betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1]) + return torch.clip(betas, 0, 0.999) + +class GaussianDiffusion1D(nn.Module): + def __init__( + self, + model, + *, + seq_length, + timesteps = 1000, + sampling_timesteps = None, + objective = 'pred_noise', + beta_schedule = 'cosine', + ddim_sampling_eta = 0., + auto_normalize = True + ): + super().__init__() + self.model = model + self.channels = self.model.channels + self.self_condition = self.model.self_condition + + self.seq_length = seq_length + + self.objective = objective + + assert objective in {'pred_noise', 'pred_x0', 'pred_v'}, 'objective must be either pred_noise (predict noise) or pred_x0 (predict image start) or pred_v (predict v [v-parameterization as defined in appendix D of progressive distillation paper, used in imagen-video successfully])' + + if beta_schedule == 'linear': + betas = linear_beta_schedule(timesteps) + elif beta_schedule == 'cosine': + betas = cosine_beta_schedule(timesteps) + else: + raise ValueError(f'unknown beta schedule {beta_schedule}') + + alphas = 1. - betas + alphas_cumprod = torch.cumprod(alphas, dim=0) + alphas_cumprod_prev = F.pad(alphas_cumprod[:-1], (1, 0), value = 1.) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + + # sampling related parameters + + self.sampling_timesteps = default(sampling_timesteps, timesteps) # default num sampling timesteps to number of timesteps at training + + assert self.sampling_timesteps <= timesteps + self.is_ddim_sampling = self.sampling_timesteps < timesteps + self.ddim_sampling_eta = ddim_sampling_eta + + # helper function to register buffer from float64 to float32 + + register_buffer = lambda name, val: self.register_buffer(name, val.to(torch.float32)) + + register_buffer('betas', betas) + register_buffer('alphas_cumprod', alphas_cumprod) + register_buffer('alphas_cumprod_prev', alphas_cumprod_prev) + + # calculations for diffusion q(x_t | x_{t-1}) and others + + register_buffer('sqrt_alphas_cumprod', torch.sqrt(alphas_cumprod)) + register_buffer('sqrt_one_minus_alphas_cumprod', torch.sqrt(1. - alphas_cumprod)) + register_buffer('log_one_minus_alphas_cumprod', torch.log(1. - alphas_cumprod)) + register_buffer('sqrt_recip_alphas_cumprod', torch.sqrt(1. / alphas_cumprod)) + register_buffer('sqrt_recipm1_alphas_cumprod', torch.sqrt(1. / alphas_cumprod - 1)) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + + posterior_variance = betas * (1. - alphas_cumprod_prev) / (1. - alphas_cumprod) + + # above: equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t) + + register_buffer('posterior_variance', posterior_variance) + + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + + register_buffer('posterior_log_variance_clipped', torch.log(posterior_variance.clamp(min =1e-20))) + register_buffer('posterior_mean_coef1', betas * torch.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod)) + register_buffer('posterior_mean_coef2', (1. - alphas_cumprod_prev) * torch.sqrt(alphas) / (1. - alphas_cumprod)) + + # calculate loss weight + + snr = alphas_cumprod / (1 - alphas_cumprod) + + if objective == 'pred_noise': + loss_weight = torch.ones_like(snr) + elif objective == 'pred_x0': + loss_weight = snr + elif objective == 'pred_v': + loss_weight = snr / (snr + 1) + + register_buffer('loss_weight', loss_weight) + + # whether to autonormalize + + self.normalize = normalize_to_neg_one_to_one if auto_normalize else identity + self.unnormalize = unnormalize_to_zero_to_one if auto_normalize else identity + + def predict_start_from_noise(self, x_t, t, noise): + return ( + extract(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - + extract(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * noise + ) + + def predict_noise_from_start(self, x_t, t, x0): + return ( + (extract(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - x0) / \ + extract(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) + ) + + def predict_v(self, x_start, t, noise): + return ( + extract(self.sqrt_alphas_cumprod, t, x_start.shape) * noise - + extract(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * x_start + ) + + def predict_start_from_v(self, x_t, t, v): + return ( + extract(self.sqrt_alphas_cumprod, t, x_t.shape) * x_t - + extract(self.sqrt_one_minus_alphas_cumprod, t, x_t.shape) * v + ) + + def q_posterior(self, x_start, x_t, t): + posterior_mean = ( + extract(self.posterior_mean_coef1, t, x_t.shape) * x_start + + extract(self.posterior_mean_coef2, t, x_t.shape) * x_t + ) + posterior_variance = extract(self.posterior_variance, t, x_t.shape) + posterior_log_variance_clipped = extract(self.posterior_log_variance_clipped, t, x_t.shape) + return posterior_mean, posterior_variance, posterior_log_variance_clipped + + def model_predictions(self, x, t, x_self_cond = None, clip_x_start = False, rederive_pred_noise = False): + model_output = self.model(x, t, x_self_cond) + maybe_clip = partial(torch.clamp, min = -1., max = 1.) if clip_x_start else identity + + if self.objective == 'pred_noise': + pred_noise = model_output + x_start = self.predict_start_from_noise(x, t, pred_noise) + x_start = maybe_clip(x_start) + + if clip_x_start and rederive_pred_noise: + pred_noise = self.predict_noise_from_start(x, t, x_start) + + elif self.objective == 'pred_x0': + x_start = model_output + x_start = maybe_clip(x_start) + pred_noise = self.predict_noise_from_start(x, t, x_start) + + elif self.objective == 'pred_v': + v = model_output + x_start = self.predict_start_from_v(x, t, v) + x_start = maybe_clip(x_start) + pred_noise = self.predict_noise_from_start(x, t, x_start) + + return ModelPrediction(pred_noise, x_start) + + def p_mean_variance(self, x, t, x_self_cond = None, clip_denoised = True): + preds = self.model_predictions(x, t, x_self_cond) + x_start = preds.pred_x_start + + if clip_denoised: + x_start.clamp_(-1., 1.) + + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start = x_start, x_t = x, t = t) + return model_mean, posterior_variance, posterior_log_variance, x_start + + @torch.no_grad() + def p_sample(self, x, t: int, x_self_cond = None, clip_denoised = True): + b, *_, device = *x.shape, x.device + batched_times = torch.full((b,), t, device = x.device, dtype = torch.long) + model_mean, _, model_log_variance, x_start = self.p_mean_variance(x = x, t = batched_times, x_self_cond = x_self_cond, clip_denoised = clip_denoised) + noise = torch.randn_like(x) if t > 0 else 0. # no noise if t == 0 + pred_img = model_mean + (0.5 * model_log_variance).exp() * noise + return pred_img, x_start + + @torch.no_grad() + def p_sample_loop(self, shape): + batch, device = shape[0], self.betas.device + + img = torch.randn(shape, device=device) + + x_start = None + + for t in tqdm(reversed(range(0, self.num_timesteps)), desc = 'sampling loop time step', total = self.num_timesteps): + self_cond = x_start if self.self_condition else None + img, x_start = self.p_sample(img, t, self_cond) + + img = self.unnormalize(img) + return img + + @torch.no_grad() + def ddim_sample(self, shape, clip_denoised = True): + batch, device, total_timesteps, sampling_timesteps, eta, objective = shape[0], self.betas.device, self.num_timesteps, self.sampling_timesteps, self.ddim_sampling_eta, self.objective + + times = torch.linspace(-1, total_timesteps - 1, steps=sampling_timesteps + 1) # [-1, 0, 1, 2, ..., T-1] when sampling_timesteps == total_timesteps + times = list(reversed(times.int().tolist())) + time_pairs = list(zip(times[:-1], times[1:])) # [(T-1, T-2), (T-2, T-3), ..., (1, 0), (0, -1)] + + img = torch.randn(shape, device = device) + + x_start = None + + for time, time_next in tqdm(time_pairs, desc = 'sampling loop time step'): + time_cond = torch.full((batch,), time, device=device, dtype=torch.long) + self_cond = x_start if self.self_condition else None + pred_noise, x_start, *_ = self.model_predictions(img, time_cond, self_cond, clip_x_start = clip_denoised) + + if time_next < 0: + img = x_start + continue + + alpha = self.alphas_cumprod[time] + alpha_next = self.alphas_cumprod[time_next] + + sigma = eta * ((1 - alpha / alpha_next) * (1 - alpha_next) / (1 - alpha)).sqrt() + c = (1 - alpha_next - sigma ** 2).sqrt() + + noise = torch.randn_like(img) + + img = x_start * alpha_next.sqrt() + \ + c * pred_noise + \ + sigma * noise + + img = self.unnormalize(img) + return img + + @torch.no_grad() + def sample(self, batch_size = 16): + seq_length, channels = self.seq_length, self.channels + sample_fn = self.p_sample_loop if not self.is_ddim_sampling else self.ddim_sample + return sample_fn((batch_size, channels, seq_length)) + + @torch.no_grad() + def interpolate(self, x1, x2, t = None, lam = 0.5): + b, *_, device = *x1.shape, x1.device + t = default(t, self.num_timesteps - 1) + + assert x1.shape == x2.shape + + t_batched = torch.full((b,), t, device = device) + xt1, xt2 = map(lambda x: self.q_sample(x, t = t_batched), (x1, x2)) + + img = (1 - lam) * xt1 + lam * xt2 + + x_start = None + + for i in tqdm(reversed(range(0, t)), desc = 'interpolation sample time step', total = t): + self_cond = x_start if self.self_condition else None + img, x_start = self.p_sample(img, i, self_cond) + + return img + + @autocast(enabled = False) + def q_sample(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + + return ( + extract(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + extract(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise + ) + + def p_losses(self, x_start, t, noise = None): + b, c, n = x_start.shape + noise = default(noise, lambda: torch.randn_like(x_start)) + + # noise sample + + x = self.q_sample(x_start = x_start, t = t, noise = noise) + + # if doing self-conditioning, 50% of the time, predict x_start from current set of times + # and condition with unet with that + # this technique will slow down training by 25%, but seems to lower FID significantly + + x_self_cond = None + if self.self_condition and random() < 0.5: + with torch.no_grad(): + x_self_cond = self.model_predictions(x, t).pred_x_start + x_self_cond.detach_() + + # predict and take gradient step + + model_out = self.model(x, t, x_self_cond) + + if self.objective == 'pred_noise': + target = noise + elif self.objective == 'pred_x0': + target = x_start + elif self.objective == 'pred_v': + v = self.predict_v(x_start, t, noise) + target = v + else: + raise ValueError(f'unknown objective {self.objective}') + + loss = F.mse_loss(model_out, target, reduction = 'none') + loss = reduce(loss, 'b ... -> b', 'mean') + + loss = loss * extract(self.loss_weight, t, loss.shape) + return loss.mean() + + def forward(self, img, *args, **kwargs): + b, c, n, device, seq_length, = *img.shape, img.device, self.seq_length + assert n == seq_length, f'seq length must be {seq_length}' + t = torch.randint(0, self.num_timesteps, (b,), device=device).long() + + img = self.normalize(img) + return self.p_losses(img, t, *args, **kwargs) + +# trainer class + +class Trainer1D(object): + def __init__( + self, + diffusion_model: GaussianDiffusion1D, + dataset: Dataset, + *, + train_batch_size = 16, + gradient_accumulate_every = 1, + train_lr = 1e-4, + train_num_steps = 100000, + ema_update_every = 10, + ema_decay = 0.995, + adam_betas = (0.9, 0.99), + save_and_sample_every = 1000, + num_samples = 25, + results_folder = './results', + amp = False, + mixed_precision_type = 'fp16', + split_batches = True, + max_grad_norm = 1. + ): + super().__init__() + + # accelerator + + self.accelerator = Accelerator( + split_batches = split_batches, + mixed_precision = mixed_precision_type if amp else 'no' + ) + + # model + + self.model = diffusion_model + self.channels = diffusion_model.channels + + # sampling and training hyperparameters + + assert has_int_squareroot(num_samples), 'number of samples must have an integer square root' + self.num_samples = num_samples + self.save_and_sample_every = save_and_sample_every + + self.batch_size = train_batch_size + self.gradient_accumulate_every = gradient_accumulate_every + self.max_grad_norm = max_grad_norm + + self.train_num_steps = train_num_steps + + # dataset and dataloader + + dl = DataLoader(dataset, batch_size = train_batch_size, shuffle = True, pin_memory = True, num_workers = cpu_count()) + + dl = self.accelerator.prepare(dl) + self.dl = cycle(dl) + + # optimizer + + self.opt = Adam(diffusion_model.parameters(), lr = train_lr, betas = adam_betas) + + # for logging results in a folder periodically + + if self.accelerator.is_main_process: + self.ema = EMA(diffusion_model, beta = ema_decay, update_every = ema_update_every) + self.ema.to(self.device) + + self.results_folder = Path(results_folder) + self.results_folder.mkdir(exist_ok = True) + + # step counter state + + self.step = 0 + + # prepare model, dataloader, optimizer with accelerator + + self.model, self.opt = self.accelerator.prepare(self.model, self.opt) + + @property + def device(self): + return self.accelerator.device + + def save(self, milestone): + if not self.accelerator.is_local_main_process: + return + + data = { + 'step': self.step, + 'model': self.accelerator.get_state_dict(self.model), + 'opt': self.opt.state_dict(), + 'ema': self.ema.state_dict(), + 'scaler': self.accelerator.scaler.state_dict() if exists(self.accelerator.scaler) else None, + 'version': __version__ + } + + torch.save(data, str(self.results_folder / f'model-{milestone}.pt')) + + def load(self, milestone): + accelerator = self.accelerator + device = accelerator.device + + data = torch.load(str(self.results_folder / f'model-{milestone}.pt'), map_location=device) + + model = self.accelerator.unwrap_model(self.model) + model.load_state_dict(data['model']) + + self.step = data['step'] + self.opt.load_state_dict(data['opt']) + if self.accelerator.is_main_process: + self.ema.load_state_dict(data["ema"]) + + if 'version' in data: + print(f"loading from version {data['version']}") + + if exists(self.accelerator.scaler) and exists(data['scaler']): + self.accelerator.scaler.load_state_dict(data['scaler']) diff --git a/subject1-4/AdaDiff/diffusion_module2.py b/subject1-4/AdaDiff/diffusion_module2.py new file mode 100644 index 0000000000000000000000000000000000000000..61599470a6ee72a515373d2b39e4c6dcf9c563f1 --- /dev/null +++ b/subject1-4/AdaDiff/diffusion_module2.py @@ -0,0 +1,159 @@ +import torch +import torch.nn.functional as F +from tqdm import tqdm + +def cosine_beta_schedule(timesteps, s=0.008): + """ + cosine schedule as proposed in https://arxiv.org/abs/2102.09672 + """ + steps = timesteps + 1 + x = torch.linspace(0, timesteps, steps) + alphas_cumprod = torch.cos(((x / timesteps) + s) / (1 + s) * torch.pi * 0.5) ** 2 + alphas_cumprod = alphas_cumprod / alphas_cumprod[0] + betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1]) + return torch.clip(betas, 0.0001, 0.9999) + +def linear_beta_schedule(timesteps): + beta_start = 0.0001 + beta_end = 0.02 + return torch.linspace(beta_start, beta_end, timesteps) + +def quadratic_beta_schedule(timesteps): + beta_start = 0.0001 + beta_end = 0.02 + return torch.linspace(beta_start**0.5, beta_end**0.5, timesteps) ** 2 + +def sigmoid_beta_schedule(timesteps): + beta_start = 0.0001 + beta_end = 0.02 + betas = torch.linspace(-6, 6, timesteps) + return torch.sigmoid(betas) * (beta_end - beta_start) + beta_start + +timesteps = 300 + +# define beta schedule +betas = linear_beta_schedule(timesteps=timesteps) + +# define alphas +alphas = 1. - betas +alphas_cumprod = torch.cumprod(alphas, axis=0) +alphas_cumprod_prev = F.pad(alphas_cumprod[:-1], (1, 0), value=1.0) +sqrt_recip_alphas = torch.sqrt(1.0 / alphas) + +# calculations for diffusion q(x_t | x_{t-1}) and others +sqrt_alphas_cumprod = torch.sqrt(alphas_cumprod) +sqrt_one_minus_alphas_cumprod = torch.sqrt(1. - alphas_cumprod) + +# calculations for posterior q(x_{t-1} | x_t, x_0) +posterior_variance = betas * (1. - alphas_cumprod_prev) / (1. - alphas_cumprod) + +def extract(a, t, x_shape): + batch_size = t.shape[0] + out = a.gather(-1, t.cpu()) + return out.reshape(batch_size, *((1,) * (len(x_shape) - 1))).to(t.device) + + +# forward diffusion (using the nice property) +def q_sample(x_start, t, noise=None): + if noise is None: + noise = torch.randn_like(x_start) + + sqrt_alphas_cumprod_t = extract(sqrt_alphas_cumprod, t, x_start.shape) + sqrt_one_minus_alphas_cumprod_t = extract( + sqrt_one_minus_alphas_cumprod, t, x_start.shape + ) + + return sqrt_alphas_cumprod_t * x_start + sqrt_one_minus_alphas_cumprod_t * noise + +def p_losses(denoise_model, x_start, t, noise=None, loss_type="l2"): + if noise is None: + noise = torch.randn_like(x_start) + + x_noisy = q_sample(x_start=x_start, t=t, noise=noise) + predicted_noise = denoise_model(x_noisy, t) + #if train: + if loss_type == 'l1': + loss = F.l1_loss(noise, predicted_noise) + elif loss_type == 'l2': + loss = F.mse_loss(noise, predicted_noise) + elif loss_type == "huber": + loss = F.smooth_l1_loss(noise, predicted_noise) + else: + raise NotImplementedError() + # else: + # x_recon = (x_noisy - extract(sqrt_one_minus_alphas_cumprod, t, x_noisy.shape) * predicted_noise) / extract(sqrt_alphas_cumprod, t, x_noisy.shape) + # loss = F.mse_loss(predicted_noise, noise, reduction='none') + return loss + + +##### SAMPLING ####### + +@torch.no_grad() +def p_sample(model, x, t, t_index): + betas_t = extract(betas, t, x.shape) + sqrt_one_minus_alphas_cumprod_t = extract( + sqrt_one_minus_alphas_cumprod, t, x.shape + ) + sqrt_recip_alphas_t = extract(sqrt_recip_alphas, t, x.shape) + + # Equation 11 in the paper + # Use our model (noise predictor) to predict the mean + model_mean = sqrt_recip_alphas_t * ( + x - betas_t * model(x, t) / sqrt_one_minus_alphas_cumprod_t + ) + + if t_index == 0: + return model_mean + else: + posterior_variance_t = extract(posterior_variance, t, x.shape) + noise = torch.randn_like(x) + # Algorithm 2 line 4: + return model_mean + torch.sqrt(posterior_variance_t) * noise + +# Algorithm 2 (including returning all images) +@torch.no_grad() +def p_sample_loop(model, shape, x_start, denoise_steps): + #device = next(model.parameters()).device + #timesteps = 200 + timesteps = denoise_steps + device = 'cuda' + + b = shape[0] + # start from pure noise (for each example in the batch) + #img = torch.randn(shape, device=device) + noise = torch.randn_like(x_start) + img = q_sample(x_start=x_start, t=torch.full((b,), timesteps, device=device, dtype=torch.long), noise=noise) + # imgs = [] + + for i in tqdm(reversed(range(0, timesteps)), desc='sampling loop time step', total=timesteps): + img = p_sample(model, img, torch.full((b,), i, device=device, dtype=torch.long), i) + #imgs.append(img.cpu().numpy()) + return img + +@torch.no_grad() +def sample(model, shape, x_start, denoise_steps): + return p_sample_loop(model, shape=shape, x_start=x_start, denoise_steps=denoise_steps) +@torch.no_grad() +def sample(model, shape, x_start, denoise_steps, Tstart, Tend): + return p_sample_loop_withStartAndEnd(model, shape, x_start, denoise_steps, Tstart, Tend) + +@torch.no_grad() +def p_sample_loop_withStartAndEnd(model, shape, x_start, denoise_steps, Tstart, Tend): + #device = next(model.parameters()).device + #timesteps = 200 + timesteps = denoise_steps + device = 'cuda' + + b = shape[0] + # start from pure noise (for each example in the batch) + #img = torch.randn(shape, device=device) + if Tstart == 0: + noise = torch.randn_like(x_start) + img = q_sample(x_start=x_start, t=torch.full((b,), timesteps, device=device, dtype=torch.long), noise=noise) + else: + img = x_start + + for i in tqdm(reversed(range(timesteps - Tend, timesteps - Tstart)), desc='sampling loop time step', total=Tend - Tstart): + img = p_sample(model, img, torch.full((b,), i, device=device, dtype=torch.long), i) + #imgs.append(img.cpu().numpy()) + return img \ No newline at end of file diff --git a/subject1-4/AdaDiff/huigui.ipynb b/subject1-4/AdaDiff/huigui.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3164575c129b427bb5d2245e421b2d905a2afdfd --- /dev/null +++ b/subject1-4/AdaDiff/huigui.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "# 假设你的数据是一个CSV文件,可以通过read_csv加载\n", + "# 如果数据不是CSV格式,可以根据实际情况使用其他read_函数\n", + "data = pd.read_csv('../alpha=0.1.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prf1time
00.1500.771.47
10.1450.741.45
20.1350.701.43
30.1300.681.40
40.1250.631.39
\n", + "
" + ], + "text/plain": [ + " p r f1 time\n", + "0 0.1 50 0.77 1.47\n", + "1 0.1 45 0.74 1.45\n", + "2 0.1 35 0.70 1.43\n", + "3 0.1 30 0.68 1.40\n", + "4 0.1 25 0.63 1.39" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "特征 X:\n", + " p r\n", + "0 0.1 50\n", + "1 0.1 45\n", + "2 0.1 35\n", + "3 0.1 30\n", + "4 0.1 25\n", + "目标 y:\n", + " 0 0.77\n", + "1 0.74\n", + "2 0.70\n", + "3 0.68\n", + "4 0.63\n", + "Name: f1, dtype: float64\n" + ] + } + ], + "source": [ + "X = data.iloc[:, :-2] # 选择除最后一列之外的所有列作为特征\n", + "f1 = data.iloc[:, -2] # 选择最后一列作为目标变量\n", + "time = data.iloc[:,-1]\n", + "# 打印 X 和 y,确保提取正确\n", + "print(\"特征 X:\\n\", X.head())\n", + "print(\"目标 y:\\n\", f1.head())" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "回归系数: [-0.92813953 0.00513953]\n", + "截距: 0.6066511627906976\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn.linear_model import LinearRegression\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# 生成一些示例数据\n", + "# np.random.seed(0)\n", + "# X = 2 * np.random.rand(100, 2) # 有两个特征\n", + "# y = 4 + 3 * X[:, 0] + 2 * X[:, 1] + np.random.randn(100)\n", + "\n", + "# 创建线性回归模型\n", + "model = LinearRegression()\n", + "\n", + "# 将数据拟合到模型中\n", + "model.fit(X, f1)\n", + "\n", + "# 打印回归系数和截距\n", + "print(\"回归系数:\", model.coef_)\n", + "print(\"截距:\", model.intercept_)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "回归系数: [-0.53418605 0.00318605]\n", + "截距: 1.36353488372093\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn.linear_model import LinearRegression\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# 生成一些示例数据\n", + "# np.random.seed(0)\n", + "# X = 2 * np.random.rand(100, 2) # 有两个特征\n", + "# y = 4 + 3 * X[:, 0] + 2 * X[:, 1] + np.random.randn(100)\n", + "\n", + "# 创建线性回归模型\n", + "model = LinearRegression()\n", + "\n", + "# 将数据拟合到模型中\n", + "model.fit(X, time)\n", + "\n", + "# 打印回归系数和截距\n", + "print(\"回归系数:\", model.coef_)\n", + "print(\"截距:\", model.intercept_)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "diffusionA", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/subject1-4/AdaDiff/models2.py b/subject1-4/AdaDiff/models2.py new file mode 100644 index 0000000000000000000000000000000000000000..506a11b0fa0ea756d72b69a222bfe54cbf9256d1 --- /dev/null +++ b/subject1-4/AdaDiff/models2.py @@ -0,0 +1,273 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import pickle +from torch.nn import TransformerEncoder, TransformerEncoderLayer, TransformerDecoderLayer +from torch.nn import TransformerDecoder +from src.dlutils import * +from unet2 import Unet +from scipy import stats +from diffusion_module2 import p_losses, sample +from denoising_diffusion_pytorch_1d import Unet1D +device = 'cuda' + + +class Autoencoder_Diffusion(nn.Module): + def __init__(self, feats, lr, window_size, batch_size, p1, p2): + super().__init__() + self.name = 'AutoencoderDiffusion' + self.lr = lr + self.batch = batch_size + self.n_feats = feats + self.n_window = window_size + self.p1 = p1 + self.p2 = p2 + self.bottleneck_s1 = int(p1 * self.n_feats * self.n_window) + self.bottleneck_s2 = int(p2 * self.n_feats * self.n_window) + self.bottleneck = 32 + + self.encoder = torch.nn.Sequential( + torch.nn.Linear(self.n_feats * self.n_window, self.bottleneck_s1), + torch.nn.ReLU(), + torch.nn.Linear(self.bottleneck_s1, self.bottleneck_s2), + ) + + #self.encoder = torch.nn.Linear(self.n_feats * self.n_window, self.bottleneck) + + self.decoder = torch.nn.Sequential( + torch.nn.Linear(self.bottleneck_s2, self.bottleneck_s1), + torch.nn.ReLU(), + torch.nn.Linear(self.bottleneck_s1, self.n_feats * self.n_window), + ) + + self.activation_fn = torch.nn.ReLU() + + #self.decoder = torch.nn.Linear(self.bottleneck, self.n_feats * self.n_window) + + + def forward(self, x): + encoded = self.encoder(x) + decoded = self.decoder(encoded) + return decoded + + +class ConditionalDiffusionTrainingNetwork(nn.Module): + + def __init__(self,nr_feats, window_size, batch_size, noise_steps, denoise_steps, train=True): + super().__init__() + self.dim = min(nr_feats, 16) + self.nr_feats = nr_feats + self.window_size = window_size + self.batch_size = batch_size + + self.training = train + self.timesteps = noise_steps + self.denoise_steps = denoise_steps + + # self.denoise_fn = Unet(dim=self.dim, channels=1, resnet_block_groups=1, init_size=torch.Size([self.dim, self.window_size, self.nr_feats])) + self.denoise_fn = model = Unet1D( + dim = 64, + dim_mults = (1, 2, 4, 8), + channels = nr_feats + ) + + def forward(self, x, Tstart = None, Tend = None): + + diffusion_loss = None + x_recon = None + if Tstart == None: + Tstart = 0 + if Tend == None: + Tend = self.denoise_steps + + # x = x.reshape(-1, 1, self.window_size, self.nr_feats) + # print(self.nr_feats) + x = x.transpose(2,1) + # print(np.shape(x)) + if self.training: + t = torch.randint(0, self.timesteps, (x.shape[0],), device=device).long() + diffusion_loss = p_losses(self.denoise_fn, x, t) + else: + # x_recon = sample(self.denoise_fn, shape=(x.shape[0], 1, self.window_size, self.nr_feats), x_start=x, denoise_steps=self.denoise_steps) + x_recon = sample(self.denoise_fn, shape=(x.shape[0], 1, self.window_size, self.nr_feats), x_start=x, denoise_steps=self.denoise_steps, Tstart = Tstart, Tend = Tend) + return diffusion_loss, x_recon + + + +class TransformerBasic(nn.Module): + def __init__(self, feats): + super().__init__() + self.name = 'TransformerBasic' + self.lr = 0.1 + self.batch = 128 + self.n_feats = feats + self.n_window = 10 + + self.lin = nn.Linear(1, feats) + self.out_lin = nn.Linear(feats, 1) + self.pos_encoder = PositionalEncoding(feats, 0.1, feats*self.n_window) + encoder_layers = TransformerEncoderLayer(d_model=feats, nhead=feats, dim_feedforward=16, dropout=0.1) + self.transformer_encoder = TransformerEncoder(encoder_layers, 1) + decoder_layers = TransformerDecoderLayer(d_model=feats, nhead=feats, dim_feedforward=16, dropout=0.1) + self.transformer_decoder = TransformerDecoder(decoder_layers, 1) + self.fcn = nn.Sigmoid() + + def forward(self, src, tgt): + # bs x (ws x features) x features + src = src * math.sqrt(self.n_feats) + src = self.lin(src.unsqueeze(2)) + src = self.pos_encoder(src) + memory = self.transformer_encoder(src) + + tgt = tgt * math.sqrt(self.n_feats) + tgt = self.lin(tgt.unsqueeze(2)) + tgt = self.pos_encoder(tgt) + x = self.transformer_decoder(tgt, memory) + x = self.out_lin(x) + x = self.fcn(x) + return x + +class TransformerBasicv2(nn.Module): + def __init__(self, feats, lr, window_size): + super(TransformerBasicv2, self).__init__() + self.name = 'TransformerBasicv2' + self.lr = lr + self.batch = 128 + self.n_feats = feats + self.n_window = window_size + self.scale = 16 + self.linear_layer = nn.Linear(feats, self.scale*feats) + self.output_layer = nn.Linear(self.scale*feats, feats) + self.pos_encoder = PositionalEncoding(self.scale*feats, 0.1, self.n_window, batch_first=True) + encoder_layers = TransformerEncoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_encoder = TransformerEncoder(encoder_layers, 1) + decoder_layers = TransformerDecoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_decoder = TransformerDecoder(decoder_layers, 1) + self.fcn = nn.Sigmoid() + + def forward(self, src, tgt): + src = src * math.sqrt(self.n_feats) + src = self.linear_layer(src) + src = self.pos_encoder(src) + memory = self.transformer_encoder(src) + + tgt = tgt * math.sqrt(self.n_feats) + tgt = self.linear_layer(tgt) + + x = self.transformer_decoder(tgt, memory) + x = self.output_layer(x) + x = self.fcn(x) + return x + +class TransformerBasicv2Scaling(nn.Module): + def __init__(self, feats, lr, window_size): + super(TransformerBasicv2Scaling, self).__init__() + self.name = 'TransformerBasicv2Scaling' + self.lr = lr + self.batch = 128 + self.n_feats = feats + self.n_window = window_size + self.scale = 16 + self.linear_layer = nn.Linear(feats, self.scale*feats) + self.output_layer = nn.Linear(self.scale*feats, feats) + self.pos_encoder = PositionalEncoding(self.scale*feats, 0.1, self.n_window, batch_first=True) + encoder_layers = TransformerEncoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_encoder = TransformerEncoder(encoder_layers, 1) + decoder_layers = TransformerDecoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_decoder = TransformerDecoder(decoder_layers, 1) + self.fcn = nn.Sigmoid() + + def forward(self, src, tgt): + model_dim = self.scale * self.n_feats + + src = self.linear_layer(src) + src = src * math.sqrt(model_dim) + src = self.pos_encoder(src) + memory = self.transformer_encoder(src) + + tgt = self.linear_layer(tgt) + tgt = tgt * math.sqrt(model_dim) + + x = self.transformer_decoder(tgt, memory) + x = self.output_layer(x) + x = self.fcn(x) + return x + + + +class TransformerBasicBottleneck(nn.Module): + def __init__(self, feats, lr, window_size): + super(TransformerBasicBottleneck, self).__init__() + self.name = 'TransformerBasicBottleneck' + self.lr = lr + self.batch = 16 + self.n_feats = feats + self.n_window = window_size + self.scale = 16 + self.linear_layer = nn.Linear(feats, self.scale*feats) + self.output_layer = nn.Linear(self.scale*feats, feats) + self.pos_encoder = PositionalEncoding(self.scale*feats, 0.1, self.n_window, batch_first=True) + encoder_layers = TransformerEncoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_encoder = TransformerEncoder(encoder_layers, 1) + decoder_layers = TransformerDecoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_decoder = TransformerDecoder(decoder_layers, 1) + self.fcn = nn.Sigmoid() + + def forward(self, src, tgt): + src = src * math.sqrt(self.n_feats) + src = self.linear_layer(src) + src = self.pos_encoder(src) + # batch x t x d + memory = self.transformer_encoder(src) + # batch x 1 x d + z = torch.mean(memory, dim=1, keepdim=True) + + + tgt = tgt * math.sqrt(self.n_feats) + tgt = self.linear_layer(tgt) + + x = self.transformer_decoder(tgt, z) + x = self.output_layer(x) + x = self.fcn(x) + return x + +class TransformerBasicBottleneckScaling(nn.Module): + def __init__(self, feats, lr, window_size, batch_size): + super(TransformerBasicBottleneckScaling, self).__init__() + self.name = 'TransformerBasicBottleneckScaling' + self.lr = lr + self.batch = batch_size + self.n_feats = feats + self.n_window = window_size + self.scale = 16 + self.linear_layer = nn.Linear(feats, self.scale*feats) + self.output_layer = nn.Linear(self.scale*feats, feats) + self.pos_encoder = PositionalEncoding(self.scale*feats, 0.1, self.n_window, batch_first=True) + encoder_layers = TransformerEncoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_encoder = TransformerEncoder(encoder_layers, 1) + decoder_layers = TransformerDecoderLayer(d_model=feats*self.scale, nhead=feats, batch_first=True, dim_feedforward=256, dropout=0.1) + self.transformer_decoder = TransformerDecoder(decoder_layers, 1) + self.fcn = nn.Sigmoid() + + def forward(self, src, tgt): + model_dim = self.scale * self.n_feats + + src = self.linear_layer(src) + src = src * math.sqrt(model_dim) + src = self.pos_encoder(src) + # batch x t x d + memory = self.transformer_encoder(src) + # batch x 1 x d + z = torch.mean(memory, dim=1, keepdim=True) + + tgt = self.linear_layer(tgt) + tgt = tgt * math.sqrt(model_dim) + + x = self.transformer_decoder(tgt, z) + x = self.output_layer(x) + x = self.fcn(x) + return x + + + diff --git a/subject1-4/AdaDiff/server.py b/subject1-4/AdaDiff/server.py new file mode 100644 index 0000000000000000000000000000000000000000..edc5ce50975590bb7fcfed040569d1172e7eef03 --- /dev/null +++ b/subject1-4/AdaDiff/server.py @@ -0,0 +1,151 @@ +import socket +import threading +import os +import ast +import time +import csv +import diffusion_module2 +import train_diffusion_val +import torch +from torch.utils.data import Dataset, DataLoader, TensorDataset +import torch.nn as nn +import numpy as np +import time +import pickle +device = "cuda" +class Server: + def __init__(self, address, model): + self.address = address + self.clients = [] + self.real_anomalies = set() + self.model = model + + # timestamp = time.strftime("%Y%m%d%H%M%S", time.localtime()) + + # # 将时间戳添加到 CSV 文件名中 + # self.log_file_name = f"server_log_{timestamp}.csv" + + # # 创建 CSV 文件并写入表头 + # with open(self.log_file_name, 'w', newline='') as csvfile: + # fieldnames = ['Node ID', 'Anomaly Data ID', 'Is Real Anomaly'] + # writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + # writer.writeheader() + + + def start_server(self): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: + server_socket.bind(self.address) + server_socket.listen() + + print(f"Server listening on {self.address}") + + while True: + client_socket, client_address = server_socket.accept() + client_thread = threading.Thread(target=self.handle_client, args=(client_socket, client_address)) + client_thread.start() + self.clients.append(client_thread) + + def handle_client(self, client_socket, client_address): + print(f"Accepted connection from {client_address}") + + length = int(client_socket.recv(1024).decode()) + print("收到长度",length) + client_socket.sendall('go'.encode()) + serialized_data = b'' + while True: + chunk = client_socket.recv(1024) # 接收数据块(这里假设每次接收1KB) + + serialized_data += chunk + if len(serialized_data) == length: # 如果接收到的数据为空,表示传输完毕 + break + # print(len(serialized_data)) + deserialized_data = pickle.loads(serialized_data) + # 处理反序列化后的数据... + ratio = deserialized_data["ratio"] + data_fragment = deserialized_data["data"] + print(f"Received ratio: {ratio}") + + # 进行进一步的异常检测 + data = self.denoising(data_fragment, ratio) + + serialized_data = pickle.dumps(data) + chunk_size = 1024 + + client_socket.sendall(str(len(serialized_data)).encode()) + point = client_socket.recv(1024) + print("收到信号", point.decode()) + print(np.shape(data)) + print(len(serialized_data)) + for i in range(0, len(serialized_data), chunk_size): + chunk = serialized_data[i:i+chunk_size] + client_socket.sendall(chunk) + print(f"Connection from {client_address} closed") + client_socket.close() + def denoising(self, data, ratio): + # print(np.shape(data)) + data.to(device) + return self.model(data,int(self.model.denoise_steps* ratio),self.model.denoise_steps)[1].transpose(2,1) + + +if __name__ == "__main__": + server_address = ('localhost', 8892) # 中心服务器地址和端口 + + + training_mode = "diffusion" + lr = 1e-3 + window_size = 128 + p1 = 1 + p2 = 1 + dataset_name = "point_global" + batch_size = 32 + noise_steps = 100 + denoise_steps = 50 + diff_lambda = 0.1 + part = None + device = "cuda" + + experiment = f'diffv4_{dataset_name}_{noise_steps}-{denoise_steps}_{diff_lambda}_1e-3_{batch_size}_{window_size}' + + train_loader, test_loader, validation_loader, labels, validation_labels = train_diffusion_val.load_dataset(dataset_name, part) + + model, diffusion_training_net, diffusion_prediction_net, optimizer, scheduler = \ + train_diffusion_val.load_model(training_mode ,lr, window_size, p1, p2, labels.shape[1], batch_size, noise_steps, denoise_steps) + model, diffusion_training_net = train_diffusion_val.load_from_checkpoint(training_mode, experiment, model, diffusion_training_net) + diffusion_training_net = diffusion_training_net.to(device) + diffusion_prediction_net = diffusion_prediction_net.to(device) + + diffusion_prediction_net.load_state_dict(diffusion_training_net.state_dict()) + diffusion_prediction_net.eval() + # diffusion_training_net.eval() + + # trainD, testD, validationD = next(iter(train_loader)), next(iter(test_loader)), next(iter(validation_loader)) + # testD = train_diffusion_val.convert_to_windows(testD, window_size) + # data_x = torch.tensor(testD, dtype=torch.float32); + # dataset = TensorDataset(data_x, data_x) + # dataloader = DataLoader(dataset, batch_size = batch_size) + + # STime = time.time() + # l1s = [] + # feats=labels.shape[1] + # for window, _ in dataloader: + # window = window.to(device) + # _, x_recon = diffusion_prediction_net(window, 0,50) + # x_recon = x_recon.transpose(2,1) + # l = nn.MSELoss(reduction = 'none') + # loss = l(x_recon, window) + # l1s.append(loss) + # ETime = time.time() + # loss0 = torch.cat(l1s).detach().cpu().numpy() + # loss0 = loss0.reshape(-1,feats) + + # lossFinal = np.mean(np.array(loss0), axis=1) + # labelsFinal = (np.sum(labels, axis=1) >= 1) + 0 + # validation_thresh = 0 + # result, fprs, tprs = train_diffusion_val.evaluate(lossFinal, labelsFinal, validation_thresh=validation_thresh) + # result_roc = result["ROC/AUC"] + # result_f1 = result["f1"] + + # print(result, ETime - STime) + + server = Server(server_address, diffusion_prediction_net) + server.start_server() diff --git a/subject1-4/AdaDiff/src/__pycache__/dlutils.cpython-38.pyc b/subject1-4/AdaDiff/src/__pycache__/dlutils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f389758846b18d3e10b2ef86123539b5e3e0a18 Binary files /dev/null and b/subject1-4/AdaDiff/src/__pycache__/dlutils.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/src/__pycache__/eval.cpython-311.pyc b/subject1-4/AdaDiff/src/__pycache__/eval.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01bea26bc418915413e11ae24808c26a36b7fbc6 Binary files /dev/null and b/subject1-4/AdaDiff/src/__pycache__/eval.cpython-311.pyc differ diff --git a/subject1-4/AdaDiff/src/__pycache__/eval.cpython-38.pyc b/subject1-4/AdaDiff/src/__pycache__/eval.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c7d0a3cbad08ed5670468c53f707d77fbd02b8e Binary files /dev/null and b/subject1-4/AdaDiff/src/__pycache__/eval.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/src/__pycache__/my_plotting.cpython-38.pyc b/subject1-4/AdaDiff/src/__pycache__/my_plotting.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53d43d591a64e146329206513692bcb33cc27ef4 Binary files /dev/null and b/subject1-4/AdaDiff/src/__pycache__/my_plotting.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/src/__pycache__/parser.cpython-311.pyc b/subject1-4/AdaDiff/src/__pycache__/parser.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bcd450fc7b64e07210ae7ac05001bb24d02b766 Binary files /dev/null and b/subject1-4/AdaDiff/src/__pycache__/parser.cpython-311.pyc differ diff --git a/subject1-4/AdaDiff/src/__pycache__/parser.cpython-38.pyc b/subject1-4/AdaDiff/src/__pycache__/parser.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..390ab0899c8371e34b9d27301ff0cf7678c4bfc0 Binary files /dev/null and b/subject1-4/AdaDiff/src/__pycache__/parser.cpython-38.pyc differ diff --git a/subject1-4/AdaDiff/src/dlutils.py b/subject1-4/AdaDiff/src/dlutils.py new file mode 100644 index 0000000000000000000000000000000000000000..7e8b550b4ce202c583ee4234797b4ff81c09e9e1 --- /dev/null +++ b/subject1-4/AdaDiff/src/dlutils.py @@ -0,0 +1,30 @@ +import torch.nn as nn +import torch +import torch.nn.functional as F +from torch.autograd import Variable +import math +import numpy as np + +class PositionalEncoding(nn.Module): + def __init__(self, d_model, dropout=0.1, max_len=5000, batch_first=False): + super(PositionalEncoding, self).__init__() + self.dropout = nn.Dropout(p=dropout) + self.batch_first = batch_first + + pe = torch.zeros(max_len, d_model) + position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) + div_term = torch.exp(torch.arange(0, d_model).float() * (-math.log(10000.0) / d_model)) + pe += torch.sin(position * div_term) + pe += torch.cos(position * div_term) + if self.batch_first: + pe = pe.unsqueeze(0) + else: + pe = pe.unsqueeze(0).transpose(0, 1) + self.register_buffer('pe', pe) + + def forward(self, x, pos=0): + if self.batch_first: + x = x + self.pe[pos:pos+x.size(1), :] + else: + x = x + self.pe[pos:pos+x.size(0), :] + return self.dropout(x) diff --git a/subject1-4/AdaDiff/src/eval.py b/subject1-4/AdaDiff/src/eval.py new file mode 100644 index 0000000000000000000000000000000000000000..5f1767b64e9b302d64ed8601803496d786657339 --- /dev/null +++ b/subject1-4/AdaDiff/src/eval.py @@ -0,0 +1,118 @@ +import numpy as np +from sklearn.metrics import * +import matplotlib.pyplot as plt +from tadpak import pak + +def get_fp_tp_rate(predict, actual): + tn, fp, fn, tp = confusion_matrix(actual, predict, labels=[0, 1]).ravel() + + true_pos_rate = tp/(tp+fn) + false_pos_rate = fp/(fp+tn) + + return false_pos_rate, true_pos_rate + + +def pak_protocol(scores, labels, threshold, max_k=100): + f1s = [] + ks = [k/100 for k in range(0, max_k + 1)] + fprs = [] + tprs = [] + preds = [] + + for k in range(max_k +1): + adjusted_preds = pak.pak(scores, labels, threshold, k=k) + f1 = f1_score(labels, adjusted_preds) + fpr, tpr = get_fp_tp_rate(adjusted_preds, labels) + fprs.append(fpr) + tprs.append(tpr) + #print(f1) + #print(k) + f1s.append(f1) + preds.append(adjusted_preds) + + area_under_f1 = auc(ks, f1s) + max_f1_k = max(f1s) + k_max = f1s.index(max_f1_k) + preds_for_max = preds[f1s.index(max_f1_k)] + # import matplotlib.pyplot as plt + # plt.cla() + # plt.plot(ks, f1s) + # plt.savefig('DiffusionAE/plots/PAK_PROTOCOL') + #print(f'AREA UNDER CURVE {area}') + return area_under_f1, max_f1_k, k_max, preds_for_max, fprs, tprs + + +def evaluate(score, label, validation_thresh=None): + if len(score) != len(label): + score = score[:len(score) - (len(score) - len(label))] + false_pos_rates = [] + true_pos_rates = [] + f1s = [] + max_f1s_k = [] + preds = [] + #thresholds = np.arange(0, score.max(), min(0.001, score.max()/50))#0.001 + thresholds = np.arange(0, score.max(), score.max()/50)#0.001 + + max_ks = [] + pairs = [] + + + for thresh in thresholds: + f1, max_f1_k, k_max, best_preds, fprs, tprs = pak_protocol(score, label, thresh) + max_f1s_k.append(max_f1_k) + max_ks.append(k_max) + preds.append(best_preds) + false_pos_rates.append(fprs) + true_pos_rates.append(tprs) + f1s.append(f1) + pairs.extend([(thresh, i) for i in range(101)]) + + if validation_thresh: + f1, max_f1_k, max_k, best_preds, _, _ = pak_protocol(score, label, validation_thresh) + else: + f1 = max(f1s) + max_possible_f1 = max(max_f1s_k) + max_idx = max_f1s_k.index(max_possible_f1) + max_k = max_ks[max_idx] + thresh_max_f1 = thresholds[max_idx] + best_preds = preds[max_idx] + best_thresh = thresholds[f1s.index(f1)] + + roc_max = auc(np.transpose(false_pos_rates)[max_k], np.transpose(true_pos_rates)[max_k]) + #np.save('/root/Diff-Anomaly/DiffusionAE/plots_for_paper/fprs_diff_score_pa.npy', np.transpose(false_pos_rates)[0]) + #np.save('/root/Diff-Anomaly/DiffusionAE/plots_for_paper/tprs_diff_score_pa.npy', np.transpose(true_pos_rates)[0]) + + false_pos_rates = np.array(false_pos_rates).flatten() + true_pos_rates = np.array(true_pos_rates).flatten() + + sorted_indexes = np.argsort(false_pos_rates) + false_pos_rates = false_pos_rates[sorted_indexes] + true_pos_rates = true_pos_rates[sorted_indexes] + pairs = np.array(pairs)[sorted_indexes] + roc_score = auc(false_pos_rates, true_pos_rates) + + #np.save('/root/Diff-Anomaly/DiffusionAE/plots_for_paper/tprs_diff_score.npy', true_pos_rates) + #np.save('/root/Diff-Anomaly/DiffusionAE/plots_for_paper/fprs_diff_score.npy', false_pos_rates) + #np.save('/root/Diff-Anomaly/DiffusionAE/plots_for_paper/pairs_diff_score.npy', pairs) + #preds = predictions[f1s.index(f1)] + if validation_thresh: + return { + 'f1': f1, # f1_k(area under f1) for validation threshold + 'ROC/AUC': roc_score, # for all ks and all thresholds obtained on test scores + 'f1_max': max_f1_k, # best f1 across k values + 'preds': best_preds, # corresponding to best k + 'k': max_k, # the k value correlated with the best f1 across k=1,100 + 'thresh_max': validation_thresh, + 'roc_max': roc_score, + } + else: + return { + 'f1': f1, + 'ROC/AUC': roc_score, + 'threshold': best_thresh, + 'f1_max': max_possible_f1, + 'roc_max': roc_max, + 'thresh_max': thresh_max_f1, + 'preds': best_preds, + 'k': max_k, + }, false_pos_rates, true_pos_rates diff --git a/subject1-4/AdaDiff/src/my_plotting.py b/subject1-4/AdaDiff/src/my_plotting.py new file mode 100644 index 0000000000000000000000000000000000000000..75a8dc1de9109dd52284c31bc3de5e1999c64161 --- /dev/null +++ b/subject1-4/AdaDiff/src/my_plotting.py @@ -0,0 +1,164 @@ +import matplotlib.pyplot as plt +from matplotlib.backends.backend_pdf import PdfPages +import statistics +import os, torch +import numpy as np + +#plt.style.use(['science', 'ieee']) +plt.rcParams["text.usetex"] = False +plt.rcParams['figure.figsize'] = 6, 2 + +os.makedirs('plots', exist_ok=True) + +def smooth(y, box_pts=1): + box = np.ones(box_pts)/box_pts + y_smooth = np.convolve(y, box, mode='same') + return y_smooth + +# def plotter(ground_truth, prediction, ascore, labels, preds=False): +# dim = 0 +# y_t, y_p, l, a_s = ground_truth[0:1000, dim], prediction[0:1000, dim], labels[0:1000, dim], ascore[0:1000, dim] +# plt.clf() +# fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) +# ax1.set_ylabel('Value') +# ax1.set_title(f'Dimension = {dim}') +# ax1.plot(smooth(y_t), linewidth=0.2, label='True') +# ax1.plot(smooth(y_p), '-', alpha=0.6, linewidth=0.3, label='Predicted') +# ax3 = ax1.twinx() +# ax3.plot(l, '--', linewidth=0.3, alpha=0.5) +# ax3.fill_between(np.arange(l.shape[0]), l, color='yellow', alpha=0.3) +# #ax4 = ax2.twinx() +# #ax4.plot(pred, '--', linewidth=0.3, alpha=0.5) +# #ax4.fill_between(np.arange(l.shape[0]), pred, color='yellow', alpha=0.3) +# ax1.legend(ncol=2, bbox_to_anchor=(0.6, 1.02)) +# ax2.plot(smooth(a_s), linewidth=0.2, color='g') +# ax2.set_xlabel('Timestamp') +# ax2.set_ylabel('Anomaly Score') +# plt.savefig('/root/Diff-Anomaly/DiffusionAE/plots/AAAAAA.jpg') +# plt.close() + + +def plotter(model, dataset, ground_truth, anomaly_score, labels, results=None, ae_recon=None, diff_sample=None, preds=None, dim=0, plot_test=True, epoch=0, set='test'): + if ground_truth.shape[-1] < 6: + timestamps = 4000 + else: + timestamps = len(labels) + print(timestamps) + #timestamps = len(ground_truth) + gt = ground_truth[0:timestamps, dim] + labels = labels[0:timestamps] + score = anomaly_score[0:timestamps] + preds = results['preds'] + preds = preds[0:timestamps] + trained_ae = True + trained_diff = True + thresh = results['thresh_max'] + text = f"{model},{dataset}\nROC_k = %.2f, F1_k = %.2f, ROC_max = %.2f, F1_max = %.2f\n best_k = %i, best_th = %.4f"%(results['ROC/AUC'], results['f1'], results['roc_max'], results['f1_max'], results['k'], results['thresh_max']) + TP = [1 if preds[i] and labels[i] else 0 for i in range(0, timestamps)] + FP = [1 if preds[i] and not labels[i] else 0 for i in range(0, timestamps)] + FN = [1 if not preds[i] and labels[i] else 0 for i in range(0, timestamps)] + + try: + ae = ae_recon[0:timestamps, dim] + except: + trained_ae = False + + try: + diff = diff_sample[0:timestamps, dim] + except: + trained_diff = False + + if trained_ae and trained_diff: + if plot_test: + fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, sharex=True) + # ax1.set_ylabel('Series') + # ax1.set_title(f'Dimension = {dim}') + ax1.set_title(text, fontsize=7) + ax1.plot(gt, linewidth=0.2) + ax1.set_ylim(0, 1) + #ax1.plot(preds, linewidth=0.2, color='orange') + ax1.plot(labels, '--', linewidth=0.3, color='red') + + ax2.set_ylim(0,1) + ax2.plot(ae, linewidth=0.2) + # ax2.plot(TP, '--', linewidth=0.3, color='green', label='TP') + # ax2.plot(FP, '--', linewidth=0.3, color='orange', label = 'FP') + # ax2.plot(FN, '--', linewidth=0.3, color='blue', label='FN') + ax2.legend(loc=(-0.1, -0.2), borderaxespad=0, fontsize='xx-small') + + ax3.set_ylim() + ax3.plot(diff, linewidth=0.2, label='diff') + # ax3.plot(TP, '--', linewidth=0.3, color='green') + # ax3.plot(FP, '--', linewidth=0.3, color='orange') + # ax3.plot(FN, '--', linewidth=0.3, color='blue') + ax2.fill_between(np.arange(labels.shape[0]), TP, color='green', alpha=0.2, linestyle='dashed', linewidth=0.3, label='TP') + ax2.fill_between(np.arange(labels.shape[0]), FP, color='orange', alpha=0.3, linestyle='dashed', linewidth=0.3, label='FP') + ax2.fill_between(np.arange(labels.shape[0]), FN, color='blue', alpha=0.2, linestyle='dashed', linewidth=0.3, label='FN') + + #ax4.fill_between(np.arange(l.shape[0]), pred, color='yellow', alpha=0.3) + + # ax2.plot(smooth(gt), linewidth=0.2, label='True') + # ax2.plot(smooth(diff), linewidth=0.2, label='Diff') + th = [thresh] * timestamps + + ax4.plot(score, linewidth=0.2) + ax4.set_xlabel('Timestamp') + ax4.set_ylabel('Score') + ax4.plot(th, '--', linewidth=0.2, alpha=0.5) + + else: + fig, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True) + ax2.set_ylim(min(ae),max(ae)) + ax3.set_ylim(min(diff),max(diff)) + ax1.plot(gt, linewidth=0.2, label='True') + ax2.plot(ae, linewidth=0.2, label='AE') + ax3.plot(diff, linewidth=0.2, label='diff') + #ax1.set_ylim(0,1) + else: + fig, (ax1, ax2, ax4) = plt.subplots(3, 1, sharex=True) + # ax1.set_ylabel('Series') + # ax1.set_title(f'Dimension = {dim}') + ax1.set_title(text, fontsize=7) + ax1.plot(gt, linewidth=0.2) + ax1.set_ylim(0, 1) + #ax1.plot(preds, linewidth=0.2, color='orange') + #ax1.plot(labels, '--', linewidth=0.3, color='red') + ax1.fill_between(np.arange(labels.shape[0]), labels, color='red', alpha=0.2, linestyle='dashed', linewidth=0.3) + + ax2.set_ylim(0,1) + if trained_ae: + ax2.plot(ae, linewidth=0.2) + else: + ax2.plot(diff, linewidth=0.2) + # ax2.plot(TP, '--', linewidth=0.3, color='green', label='TP') + # ax2.plot(FP, '--', linewidth=0.3, color='orange', label = 'FP') + # ax2.plot(FN, '--', linewidth=0.3, color='blue', label='FN') + ax2.fill_between(np.arange(labels.shape[0]), TP, color='green', alpha=0.2, linestyle='dashed', linewidth=0.3, label='TP') + ax2.fill_between(np.arange(labels.shape[0]), FP, color='orange', alpha=0.3, linestyle='dashed', linewidth=0.3, label='FP') + ax2.fill_between(np.arange(labels.shape[0]), FN, color='blue', alpha=0.2, linestyle='dashed', linewidth=0.3, label='FN') + ax2.legend(loc=(-0.15, -0.2), borderaxespad=0, fontsize='xx-small') + + # ax3.plot(TP, '--', linewidth=0.3, color='green') + # ax3.plot(FP, '--', linewidth=0.3, color='orange') + # ax3.plot(FN, '--', linewidth=0.3, color='blue') + + #ax4.fill_between(np.arange(l.shape[0]), pred, color='yellow', alpha=0.3) + + # ax2.plot(smooth(gt), linewidth=0.2, label='True') + # ax2.plot(smooth(diff), linewidth=0.2, label='Diff') + th = [thresh] * timestamps + + ax4.plot(score, linewidth=0.2) + ax4.set_xlabel('Timestamp') + ax4.set_ylabel('Score') + ax4.plot(th, '--', linewidth=0.2, alpha=0.5) + + + if dataset: + folder = f'plots/plots3/{model}_{dataset}' + else: + folder = f'plots/plots3/{model}' + os.makedirs(folder, exist_ok=True) + plt.savefig(f'{folder}/dim_{dim}_epoch_{epoch}.jpg') + plt.close() + return fig diff --git a/subject1-4/AdaDiff/src/parser.py b/subject1-4/AdaDiff/src/parser.py new file mode 100644 index 0000000000000000000000000000000000000000..9d37982ed1441146105893aa51b463b2938115f7 --- /dev/null +++ b/subject1-4/AdaDiff/src/parser.py @@ -0,0 +1,125 @@ +import argparse +from xmlrpc.client import Boolean + +parser = argparse.ArgumentParser(description='Time-Series Anomaly Detection') +parser.add_argument('--dataset', + metavar='-d', + type=str, + required=False, + default='point_global', + help="dataset"), +parser.add_argument('--file', + metavar='-f', + type=str, + required=False, + default=None, + help="dataset") +parser.add_argument('--model', + metavar='-m', + type=str, + required=False, + default='Autoencoder_Diffusion', + help="model name"), +parser.add_argument('--training', + metavar='-t', + type=str, + required=False, + default='both', + help="model to train"), +parser.add_argument('--anomaly_score', + metavar='-t', + type=str, + required=False, + default=None, + help="anomaly score"), +parser.add_argument('--lr', + metavar='-lr', + type=str, + required=False, + default='1e-4', + help="lerning rate"), +parser.add_argument('--window_size', + metavar='-ws', + type=str, + required=False, + default='10', + help="window size"), +parser.add_argument('--p1', + metavar='-p1', + type=float, + required=False, + default='1', + help="p1"), +parser.add_argument('--p2', + metavar='-p2', + type=float, + required=False, + default='1', + help="p2"), +parser.add_argument('--k', + metavar='-k', + type=int, + required=False, + default='1', + help="number of diff samples"), +parser.add_argument('--v', + metavar='-v', + type=bool, + required=False, + default=False, + help="verbose"), +# parser.add_argument('--test_only', +# metavar='-t', +# type=bool, +# required=False, +# default=False, +# help="test_only"), +parser.add_argument('--batch_size', + metavar='-t', + type=int, + required=False, + default=128, + help="batch_size"), +parser.add_argument('--diff_lambda', + metavar='-t', + type=float, + required=False, + default=0.1, + help="diff_lambda"), +parser.add_argument('--noise_steps', + metavar='-t', + type=int, + required=False, + default=100, + help="noise_steps"), +parser.add_argument('--denoise_steps', + metavar='-t', + type=int, + required=False, + default=10, + help="denoise_steps"), +parser.add_argument('--group', + metavar='-t', + type=str, + required=False, + default='search_smd', + help="wandb group"), +parser.add_argument('--test_only', + metavar='-t', + type=bool, + required=False, + default=False, + help="train new model or not"), +parser.add_argument('--id', + metavar='-t', + type=int, + required=False, + default=0, + help="experiment id for multiple runs"), +parser.add_argument('--get_thresh', + metavar='-t', + type=bool, + required=False, + default=False, + help="get val thresh again because forgot!"), +args = parser.parse_args() \ No newline at end of file diff --git a/subject1-4/AdaDiff/train_diffusion_val.py b/subject1-4/AdaDiff/train_diffusion_val.py new file mode 100644 index 0000000000000000000000000000000000000000..6088304281611d568f5f60c4f786054d15bd392e --- /dev/null +++ b/subject1-4/AdaDiff/train_diffusion_val.py @@ -0,0 +1,535 @@ +import pickle +import os +import torch +import pandas as pd +from tqdm import tqdm +from src.eval import evaluate +#from src.utils import * +from src.parser import * +from torch.utils.data import Dataset, DataLoader, TensorDataset +import torch.nn as nn +from time import time +from transformers import get_linear_schedule_with_warmup +from src.my_plotting import plotter +import matplotlib.pyplot as plt +import numpy as np +import wandb + +import torch +#from torch.utils.tensorboard import SummaryWriter +import argparse +from xmlrpc.client import Boolean + + + +#writer = SummaryWriter() + +device = 'cuda' + +def convert_to_windows(data, n_window): + windows = list(torch.split(data, n_window)) + + # print(ten.shape(windows), n_window) + for i in range (n_window-windows[-1].shape[0]): + windows[-1] = torch.cat((windows[-1], windows[-1][-1].unsqueeze(0))) + # print(np.shape(torch.stack(windows))) + return torch.stack(windows) + +# parametri: point_global, point_contextual etc +def load_dataset(dataset, part=None): + loader = [] + folder = 'DiffusionAE/processed/' + dataset + + for file in ['train', 'test', 'validation', 'labels', 'labels_validation']: + if part is None: + loader.append(np.load(os.path.join(folder, f'{file}.npy'))) + else: + loader.append(np.load(os.path.join(folder, f'{part}_{file}.npy'))) + train_loader = DataLoader(loader[0], batch_size=loader[0].shape[0]) + test_loader = DataLoader(loader[1], batch_size=loader[1].shape[0]) + validation_loader = DataLoader(loader[2], batch_size=loader[2].shape[0]) + return train_loader, test_loader, validation_loader, loader[3], loader[4] + +def load_model(training_mode, lr, window_size, p1, p2, dims, batch_size, noise_steps, denoise_steps): + from models2 import Autoencoder_Diffusion, TransformerBasicBottleneckScaling, TransformerBasicv2Scaling, ConditionalDiffusionTrainingNetwork + scheduler=None + model = None + diffusion_training_net = ConditionalDiffusionTrainingNetwork(dims, int(window_size), batch_size, noise_steps, denoise_steps).float() + diffusion_prediction_net = ConditionalDiffusionTrainingNetwork(dims, int(window_size), batch_size, noise_steps, denoise_steps, train=False).float() + + optimizer = torch.optim.Adam(diffusion_training_net.parameters(), lr=float(lr)) + # DIFFUSION size + # param_size = 0 + # for name, param in diffusion_training_net.named_parameters(): + # param_size += param.nelement() * param.element_size() + # print(f'{name} {param.size()}') + # buffer_size = 0 + # for buffer in diffusion_training_net.buffers(): + # buffer_size += buffer.nelement() * buffer.element_size() + # size_all_mb = (param_size + buffer_size) / 1024**2 + # print('diffusion size: {:.3f}MB'.format(size_all_mb)) + return model, diffusion_training_net, diffusion_prediction_net, optimizer, scheduler + +CHECKPOINT_FOLDER = 'anomaly-mts/a-mts/checkpoints' +def save_model(model, experiment, diffusion_training_net, optimizer, scheduler, anomaly_score, epoch, diff_loss, ae_loss): + folder = f'{CHECKPOINT_FOLDER}/{experiment}/' + os.makedirs(folder, exist_ok=True) + if model: + file_path_model = f'{folder}/model.ckpt' + torch.save({ + 'epoch': epoch, + 'ae_loss': ae_loss, + 'model_state_dict': model.state_dict(), + 'optimizer_state_dict': optimizer.state_dict(),}, file_path_model) + file_path_diffusion = f'{folder}/diffusion.ckpt' + torch.save({ + 'epoch': epoch, + 'diffusion_loss': diff_loss, + 'model_state_dict': diffusion_training_net.state_dict(), + 'optimizer_state_dict': optimizer.state_dict(),}, file_path_diffusion) + print('saved model at ' + folder) + +def load_from_checkpoint(training_mode, experiment, model, diffusion_training_net): + folder = f'{CHECKPOINT_FOLDER}/{experiment}' + file_path_model = f'{folder}/model.ckpt' + file_path_diffusion = f'{folder}/diffusion.ckpt' + # load model + if training_mode == 'both': + checkpoint_model = torch.load(file_path_model) + model.load_state_dict(checkpoint_model['model_state_dict']) + else: + model = None + # load diffusion + checkpoint_diffusion = torch.load(file_path_diffusion) + diffusion_training_net.load_state_dict(checkpoint_diffusion['model_state_dict']) + return model, diffusion_training_net + +def get_diffusion_sample(diffusion_prediction_net, conditioner, k): + if k <= 1: + return diffusion_prediction_net(conditioner) + else: + diff_samples = [] + for _ in range(k): + diff_samples.append(diffusion_prediction_net(conditioner)) + return torch.mean(torch.stack(diff_samples), axis = 0) + +def backprop(epoch, model, diffusion_training_net, diffusion_prediction_net, data, diff_lambda, optimizer, scheduler, training_mode, anomaly_score, k, training = True): + l = nn.MSELoss(reduction = 'none') + data_x = torch.tensor(data, dtype=torch.float32); + dataset = TensorDataset(data_x, data_x) + # print(np.shape(data_x)) + bs = diffusion_training_net.batch_size if not model else model.batch + dataloader = DataLoader(dataset, batch_size = bs) + w_size = diffusion_training_net.window_size + l1s, diff_losses, ae_losses = [], [], [] + samples = [] + # cleaned = [] + # original = [] + # all_mins = [] + # all_maxs = [] + if training: + diffusion_training_net.train() + for d, _ in dataloader: + # print(np.shape(d)) + ##### Clean trend datset here + """mins = torch.min(d[:, :, 0], dim=1) + maxs = torch.max(d[:, :, 0], dim=1) + original.append(d) + diffs = maxs[0] - mins[0] + d = d[diffs < 0.04] + cleaned.append(d) + all_mins.append(mins) + all_maxs.append(maxs)""" + ##### + if args.model == 'Autoencoder_Diffusion': + local_bs = d.shape[0] + window = d.view(local_bs, -1) + else: + window = d + window = window.to(device) + + # diff only + # print(np.shape(window)) + window = window.reshape(-1, w_size, feats) + # print(np.shape(window)) + loss, _ = diffusion_training_net(window) + l1s.append(loss.item()) + optimizer.zero_grad() + loss.backward() + optimizer.step() + tqdm.write(f'Epoch {epoch},\tL1 = {np.mean(l1s)}') + tqdm.write(f'Epoch {epoch},\tAE = {np.mean(ae_losses)}') + tqdm.write(f'Epoch {epoch},\tDiff = {np.mean(diff_losses)}') + return np.mean(l1s), np.mean(ae_losses), np.mean(diff_losses) + else: + with torch.no_grad(): + + diffusion_prediction_net.load_state_dict(diffusion_training_net.state_dict()) + diffusion_prediction_net.eval() + diffusion_training_net.eval() + l1s = [] # scores + sum_losses = [] + ae_losses = [] + diff_losses = [] + recons = [] + for d, _ in dataloader: + if args.model == 'Autoencoder_Diffusion': + local_bs = d.shape[0] + window = d.view(local_bs, -1) + else: + window = d + window = window.to(device) + window_reshaped = window.reshape(-1, w_size, feats) + _, x_recon = diffusion_prediction_net(window_reshaped) + # x_recon = torch.squeeze(x_recon, 1) + x_recon = x_recon.transpose(2,1) + # print(np.shape(x_recon)) + # print(np.shape(window_reshaped)) + samples.append(x_recon) + loss = l(x_recon, window_reshaped) + l1s.append(loss) + return torch.cat(l1s).detach().cpu().numpy(), np.mean(sum_losses), np.mean(ae_losses), np.mean(diff_losses), torch.cat(samples).detach().cpu().numpy() + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Time-Series Anomaly Detection') + parser.add_argument('--dataset', + metavar='-d', + type=str, + required=False, + default='point_global', + help="dataset"), + parser.add_argument('--file', + metavar='-f', + type=str, + required=False, + default=None, + help="dataset") + parser.add_argument('--model', + metavar='-m', + type=str, + required=False, + default='Autoencoder_Diffusion', + help="model name"), + parser.add_argument('--training', + metavar='-t', + type=str, + required=False, + default='both', + help="model to train"), + parser.add_argument('--anomaly_score', + metavar='-t', + type=str, + required=False, + default=None, + help="anomaly score"), + parser.add_argument('--lr', + metavar='-lr', + type=str, + required=False, + default='1e-4', + help="lerning rate"), + parser.add_argument('--window_size', + metavar='-ws', + type=str, + required=False, + default='10', + help="window size"), + parser.add_argument('--p1', + metavar='-p1', + type=float, + required=False, + default='1', + help="p1"), + parser.add_argument('--p2', + metavar='-p2', + type=float, + required=False, + default='1', + help="p2"), + parser.add_argument('--k', + metavar='-k', + type=int, + required=False, + default='1', + help="number of diff samples"), + parser.add_argument('--v', + metavar='-v', + type=bool, + required=False, + default=False, + help="verbose"), + # parser.add_argument('--test_only', + # metavar='-t', + # type=bool, + # required=False, + # default=False, + # help="test_only"), + parser.add_argument('--batch_size', + metavar='-t', + type=int, + required=False, + default=128, + help="batch_size"), + parser.add_argument('--diff_lambda', + metavar='-t', + type=float, + required=False, + default=0.1, + help="diff_lambda"), + parser.add_argument('--noise_steps', + metavar='-t', + type=int, + required=False, + default=100, + help="noise_steps"), + parser.add_argument('--denoise_steps', + metavar='-t', + type=int, + required=False, + default=10, + help="denoise_steps"), + parser.add_argument('--group', + metavar='-t', + type=str, + required=False, + default='search_smd', + help="wandb group"), + parser.add_argument('--test_only', + metavar='-t', + type=bool, + required=False, + default=False, + help="train new model or not"), + parser.add_argument('--id', + metavar='-t', + type=int, + required=False, + default=0, + help="experiment id for multiple runs"), + args = parser.parse_args() + + config = { + "dataset": args.dataset, + "file": args.file, + "training_mode": args.training, + "model": args.model, + "learning_rate": float(args.lr), + "window_size": int(args.window_size), + "lambda": args.diff_lambda, + "noise_steps":args.noise_steps, + "batch_size": args.batch_size, + } + + #anomaly_scores = ['diffusion'] + anomaly_scores = [args.anomaly_score] + + if args.training == 'diffusion': + experiment = 'diffv4' + elif args.model == 'Autoencoder_Diffusion': + experiment = 'autoencoder_both' + elif args.model == 'TransformerBasicBottleneckScaling': + experiment = 'tr_bn_diffv4' + else: + experiment = 'tr_basic_diffv4' + + experiment += f'_{args.dataset}_{args.noise_steps}-{args.denoise_steps}_{args.diff_lambda}_{args.lr}_{args.batch_size}_{args.window_size}' + + if args.training == 'both': + experiment += f'_{anomaly_scores[0]}_score' + #experiment += f'_{args.id}' + + wandb.init(project="anomaly-mts", config=config, group=args.group) + wandb.run.name = experiment + + dataset_name = args.dataset + part = None if not args.file else args.file + training_mode = 'both' if not args.training else args.training + print(training_mode) + anomaly_score = None if not args.anomaly_score else args.anomaly_score + window_size = int(args.window_size) + synthetic_datasets = ['point_global', 'point_contextual', 'pattern_shapelet', 'pattern_seasonal', 'pattern_trend', 'all_types', 'pattern_trendv2'] + + train_loader, test_loader, validation_loader, labels, validation_labels = load_dataset(dataset_name, part) + model, diffusion_training_net, diffusion_prediction_net, optimizer, scheduler = \ + load_model(training_mode ,args.lr, args.window_size, args.p1, args.p2, labels.shape[1], args.batch_size, args.noise_steps, args.denoise_steps) + if model: + model = model.to(device) + + diffusion_training_net = diffusion_training_net.to(device) + diffusion_prediction_net = diffusion_prediction_net.to(device) + + trainD, testD, validationD = next(iter(train_loader)), next(iter(test_loader)), next(iter(validation_loader)) + trainO, testO, validationO = trainD, testD, validationD + if args.v: + print(f'\ntrainD.shape: {trainD.shape}') + print(f'testD.shape: {testD.shape}') + print(f'validationD.shape: {validationD.shape}') + print(f'labels.shape: {labels.shape}') + + feats=labels.shape[1] + + trainD, testD, validationD = convert_to_windows(trainD, window_size), convert_to_windows(testD, window_size), convert_to_windows(validationD, window_size) + #num_epochs = 500 if args.dataset in synthetic_datasets else 100 + num_epochs = 500 + + # while labels.shape[0]%window_size!=0: + # labels=np.concatenate((labels, np.expand_dims(labels[-1], 0)), axis=0) + + # while validation_labels.shape[0]%window_size!=0: + # validation_labels=np.concatenate((labels, np.expand_dims(labels[-1], 0)), axis=0) + # print('training') + epoch = -1 + + e = epoch + 1; start = time() + max_roc_scores = [[0, 0, 0]] * 6 + max_f1_scores = [[0, 0, 0]] * 6 + roc_scores = [] + f1_scores = [] + f1_max = 0 + roc_max = 0 + validation_thresh = 0 + # anomaly_scores = ['diffusion'] + #alpha = 0 + if not args.test_only: + for e in tqdm(list(range(epoch+1, epoch+num_epochs+1))): + train_loss, ae_loss, diff_loss = backprop(e, model, diffusion_training_net, diffusion_prediction_net, trainD, args.diff_lambda, optimizer, scheduler, training_mode, anomaly_score, args.k) + wandb.log({ + 'sum_loss_train': train_loss, + 'ae_loss_train': ae_loss, + 'diff_loss_train': diff_loss, + 'epoch': e + }, step=e) + if train_loss < 0.15: + loss0, _, _, val_loss, samples = backprop(e, model, diffusion_training_net, diffusion_prediction_net, validationD, args.diff_lambda, optimizer, scheduler, training_mode, args.anomaly_score, args.k, training=False) + wandb.log({'val_loss': loss0.mean(), 'epoch': e}, step=e) + loss0 = loss0.reshape(-1,feats) + lossFinal = np.mean(np.array(loss0), axis=1) + labelsFinal = (np.sum(validation_labels, axis=1) >= 1) + 0 + result, fprs, tprs = evaluate(lossFinal, labelsFinal) + result_roc = result["ROC/AUC"] + result_f1 = result["f1"] + wandb.log({'roc': result_roc, 'f1': result_f1}, step=e) + if result_f1 > f1_max: + save_model(None, experiment, diffusion_prediction_net, optimizer, None, -1, e, train_loss, None) + f1_max = result_f1 + validation_thresh = result['threshold'] + wandb.run.summary["best_f1"] = f1_max + wandb.run.summary["roc_for_best_f1"] = result_roc + wandb.run.summary["best_f1_epoch"] = e + wandb.run.summary["validation_thresh"] = validation_thresh + if result_roc > roc_max: + roc_max = result_roc + wandb.run.summary["f1_for_best_roc"] = result_f1 + wandb.run.summary["best_roc"] = roc_max + wandb.run.summary["best_roc_epoch"] = e + wandb.log({'roc': result_roc, 'f1': result_f1}, step=e) + if e % 100 == 0: + for dim in range(0, feats): + plotter(f'{experiment}_VAL', args.dataset, validationD.reshape(-1, feats), lossFinal, labelsFinal, result, None, samples.reshape(-1, feats), None, dim=dim, plot_test=True, epoch=e) + if args.v: + print(f"testing loss #{e}: {loss0.mean()}") + # print(f"training loss #{e}: {loss1.mean()}") + print(f"final ROC #{e}: {result_roc}") + print(f"F1 #{e}: {result_f1}") + + # TEST ON TEST SET + #load model from checkpoint + model, diffusion_training_net, diffusion_prediction_net, optimizer, scheduler = \ + load_model(training_mode ,args.lr, args.window_size, args.p1, args.p2, labels.shape[1], args.batch_size, args.noise_steps, args.denoise_steps) + model, diffusion_training_net = load_from_checkpoint(training_mode, experiment, model, diffusion_training_net) + if model: + model = model.to(device) + + diffusion_training_net = diffusion_training_net.to(device) + diffusion_prediction_net = diffusion_prediction_net.to(device) + # pass test set through the model + if model: + if args.test_only: + #test again on val for double check + get best thresh on validation set to use for test + loss0, val_loss, ae_loss_val, diff_loss_val, samples, recons = backprop(e, model, diffusion_training_net, diffusion_prediction_net, validationD, args.diff_lambda, optimizer, scheduler, training_mode, args.anomaly_score, args.k, training=False) + loss0 = loss0.reshape(-1,feats) + + lossFinal = np.mean(np.array(loss0), axis=1) + # lossFinal = np.max(np.array(loss0), axis=1) + labelsFinal = (np.sum(validation_labels, axis=1) >= 1) + 0 + + result, fprs, tprs = evaluate(lossFinal, labelsFinal) + validation_thresh = result['threshold'] + result_roc = result["ROC/AUC"] + result_f1 = result["f1"] + wandb.run.summary["f1_val"] = result_f1 + wandb.run.summary["roc_val"] = result_roc + wandb.run.summary["f1_pa_val"] = result['f1_max'] + wandb.run.summary["roc_pa_val"] = result['roc_max'] + wandb.run.summary["val_loss"] = val_loss + wandb.run.summary["ae_loss_val"] = ae_loss_val + wandb.run.summary["diff_loss_val"] = diff_loss_val + + # for dim in range(0, feats): + # fig = plotter(f'{experiment}_VAL', args.anomaly_score, validationD.reshape(-1, feats), lossFinal, labelsFinal, result, recons.reshape(-1, feats), samples.reshape(-1, feats), None, dim=dim, plot_test=True, epoch=e) + + loss0, test_loss, ae_loss_test, diff_loss_test, samples, recons = backprop(e, model, diffusion_training_net, diffusion_prediction_net, testD, args.diff_lambda, optimizer, scheduler, training_mode, args.anomaly_score, args.k, training=False) + loss0 = loss0.reshape(-1,feats) + + lossFinal = np.mean(np.array(loss0), axis=1) + # np.save(f'{args.dataset}_{args.anomaly_score}_score_scores.npy', lossFinal) + # np.save(f'{args.dataset}_{args.anomaly_score}_score_recons.npy', samples) + # # np.save('/root/Diff-Anomaly/TranAD/plots_for_paper/shapelet_scores_for_example.npy', lossFinal) + # lossFinal = np.max(np.array(loss0), axis=1) + labelsFinal = (np.sum(labels, axis=1) >= 1) + 0 + #validation_thresh = 0.0019 + result = evaluate(lossFinal, labelsFinal) + result_roc = result["ROC/AUC"] + result_f1 = result["f1"] + wandb.run.summary["f1_test"] = result_f1 + wandb.run.summary["roc_test"] = result_roc + wandb.run.summary["f1_pa_test"] = result['f1_max'] + #wandb.run.summary["roc_pa_test"] = result['roc_max'] + wandb.run.summary["test_loss"] = test_loss + wandb.run.summary["ae_loss_test"] = ae_loss_test + wandb.run.summary["diff_loss_test"] = diff_loss_test + wandb.run.summary["validation_thresh"] = validation_thresh + + #for dim in range(0, feats): + # fig = plotter(f'{experiment}_TEST', args.anomaly_score, testD.reshape(-1, feats), lossFinal, labelsFinal, result, recons.reshape(-1, feats), samples.reshape(-1, feats), None, dim=dim, plot_test=True, epoch=e) + + else: + if args.test_only: + loss0, _, _, val_loss, samples = backprop(e, model, diffusion_training_net, diffusion_prediction_net, validationD, args.diff_lambda, optimizer, scheduler, training_mode, args.anomaly_score, args.k, training=False) + loss0 = loss0.reshape(-1,feats) + + lossFinal = np.mean(np.array(loss0), axis=1) + labelsFinal = (np.sum(validation_labels, axis=1) >= 1) + 0 + + result, fprs, tprs = evaluate(lossFinal, labelsFinal) + result_roc = result["ROC/AUC"] + result_f1 = result["f1"] + validation_thresh = result['threshold'] + wandb.run.summary["f1_val"] = result_f1 + wandb.run.summary["roc_val"] = result_roc + wandb.run.summary["f1_pa_val"] = result['f1_max'] + #wandb.run.summary["roc_pa_val"] = result['roc_max'] + wandb.run.summary["val_loss"] = val_loss + wandb.run.summary["validation_thresh"] = validation_thresh + #for dim in range(0, feats): + # plotter(f'{experiment}_VAL', args.dataset, validationD.reshape(-1, feats), lossFinal, labelsFinal, result, None, samples.reshape(-1, feats), None, dim=dim, plot_test=True, epoch=e) + loss0, _, _, test_loss, samples = backprop(e, model, diffusion_training_net, diffusion_prediction_net, testD, args.diff_lambda, optimizer, scheduler, training_mode, args.anomaly_score, args.k, training=False) + loss0 = loss0.reshape(-1,feats) + + lossFinal = np.mean(np.array(loss0), axis=1) + # np.save(f'{args.dataset}_diff_only_scores.npy', lossFinal) + # np.save(f'{args.dataset}_diff_only_recons.npy', samples) + # labelsFinal = (np.sum(labels, axis=1) >= 1) + 0 + + result = evaluate(lossFinal, labelsFinal, validation_thresh=validation_thresh) + result_roc = result["ROC/AUC"] + result_f1 = result["f1"] + #for dim in range(0, feats): + # plotter(f'{experiment}_TEST', args.dataset, testD.reshape(-1, feats), lossFinal, labelsFinal, result, None, samples.reshape(-1, feats), None, dim=dim, plot_test=True, epoch=e) + wandb.run.summary["f1_test"] = result_f1 + wandb.run.summary["roc_test" ] = result_roc + wandb.run.summary["f1_pa_test"] = result['f1_max'] + wandb.run.summary["roc_pa_test"] = result['roc_max'] + wandb.run.summary["test_loss"] = test_loss + + wandb.finish() diff --git a/subject1-4/AdaDiff/unet2.py b/subject1-4/AdaDiff/unet2.py new file mode 100644 index 0000000000000000000000000000000000000000..5a798f90bf262c1fc0aa04ef4524a9991ea39efc --- /dev/null +++ b/subject1-4/AdaDiff/unet2.py @@ -0,0 +1,399 @@ +import math +from inspect import isfunction +from functools import partial + +import matplotlib.pyplot as plt +from tqdm.auto import tqdm +from einops import rearrange, reduce +from einops.layers.torch import Rearrange + +import torch +from torch import nn, einsum +import torch.nn.functional as F +import math + + +def exists(x): + return x is not None + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +def num_to_groups(num, divisor): + groups = num // divisor + remainder = num % divisor + arr = [divisor] * groups + if remainder > 0: + arr.append(remainder) + return arr + + +class Residual(nn.Module): + def __init__(self, fn): + super().__init__() + self.fn = fn + + def forward(self, x, *args, **kwargs): + return self.fn(x, *args, **kwargs) + x + + +# def Upsample(dim, dim_out=None): +# return nn.Sequential( +# nn.Upsample(scale_factor=2, mode="nearest"), +# nn.Conv2d(dim, default(dim_out, dim), 3, padding=1), +# ) + +def Upsample(dim_out, dim, size): + return nn.Sequential( + nn.Upsample(size=[size[0][1], size[0][2]], mode="nearest"), + nn.Conv2d(dim_out, default(dim, dim_out), 3, padding=1), + ) + + + +# def Downsample(dim, dim_out=None): +# # No More Strided Convolutions or Pooling +# return nn.Sequential( +# Rearrange("b c (h p1) (w p2) -> b (c p1 p2) h w", p1=2, p2=2), +# nn.Conv2d(dim * 4, default(dim_out, dim), 1), +# ) + +# class Downsample(nn.Module): +# def __init__(self, dim_in, dim_out, size): +# super().__init__() +# self.dim_in = dim_in +# self.dim_out = dim_out +# self.size = size +# self.original = self.size[0] * self.size[1] * self.size[2] +# self.left_out = int(self.original/((self.size[1] // 2) * (self.size[2]// 2)) - 4*self.size[0]) + +# #self.left_out = self.original // ((self.size[1] // 2) * (self.size[2]// 2)) - 4*self.size[0] +# self.conv = nn.Conv2d(dim_in * 4 + self.left_out, default(self.dim_out, self.dim_in), 1) + +# def forward(self, x): +# x = x.reshape(-1, self.dim_in*4 + self.left_out, self.size[1] // 2, self.size[2] // 2) +# x = self.conv(x) +# return x + +# class Downsample(nn.Module): +# def __init__(self, dim_in, dim_out, size): +# super().__init__() +# self.dim_in = dim_in +# self.dim_out = dim_out +# self.size = size +# self.intermediate_channels = F.interpolate(torch.rand(size).unsqueeze(0).cpu(), (self.size[1] // 2, self.size[2] // 2)).shape[1] +# self.conv = nn.Conv2d(self.intermediate_channels, default(self.dim_out, self.dim_in), 1) + +# def forward(self, x): +# x = F.interpolate(x, (self.size[1] // 2, self.size[2] // 2)) +# x = self.conv(x) +# return x + +class Downsample(nn.Module): + def __init__(self, dim_in, dim_out, size): + super().__init__() + self.dim_in = dim_in + self.dim_out = dim_out + self.size = size + self.pad = nn.ZeroPad2d((0, self.size[-1] % 2, 0, self.size[-2] % 2)) + self.conv = nn.Conv2d(dim_in * 4, default(self.dim_out, self.dim_in), 1) + self.padded_size = (self.size[0], self.size[1] + self.size[1] % 2, self.size[2] + self.size[2] % 2) + + def forward(self, x): + # h, w = self.size[-2], self.size[-1] + # h_pad, w_pad = h % 2, w % 2 + x = self.pad(x) + x = x.reshape(-1, self.dim_in*4, self.padded_size[1] // 2, self.padded_size[2] // 2) + x = self.conv(x) + return x + + + +class SinusoidalPositionEmbeddings(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, time): + device = time.device + half_dim = self.dim // 2 + embeddings = math.log(10000) / max((half_dim - 1), 1) + embeddings = torch.exp(torch.arange(half_dim, device=device) * -embeddings) + embeddings = time[:, None] * embeddings[None, :] + embeddings = torch.cat((embeddings.sin(), embeddings.cos()), dim=-1) + return embeddings + + +class WeightStandardizedConv2d(nn.Conv2d): + """ + https://arxiv.org/abs/1903.10520 + weight standardization purportedly works synergistically with group normalization + """ + + def forward(self, x): + eps = 1e-5 if x.dtype == torch.float32 else 1e-3 + + weight = self.weight + mean = reduce(weight, "o ... -> o 1 1 1", "mean") + var = reduce(weight, "o ... -> o 1 1 1", partial(torch.var, unbiased=False)) + normalized_weight = (weight - mean) * (var + eps).rsqrt() + + return F.conv2d( + x, + normalized_weight, + self.bias, + self.stride, + self.padding, + self.dilation, + self.groups, + ) + + +class Block(nn.Module): + def __init__(self, dim, dim_out, groups=8): + super().__init__() + self.proj = WeightStandardizedConv2d(dim, dim_out, 3, padding=1) + self.norm = nn.GroupNorm(groups, dim_out) + self.act = nn.SiLU() + + def forward(self, x, scale_shift=None): + x = self.proj(x) + x = self.norm(x) + + if exists(scale_shift): + scale, shift = scale_shift + x = x * (scale + 1) + shift + + x = self.act(x) + return x + + +class ResnetBlock(nn.Module): + """https://arxiv.org/abs/1512.03385""" + + def __init__(self, dim, dim_out, *, time_emb_dim=None, groups=8): + super().__init__() + self.mlp = ( + nn.Sequential(nn.SiLU(), nn.Linear(time_emb_dim, dim_out * 2)) + if exists(time_emb_dim) + else None + ) + + self.block1 = Block(dim, dim_out, groups=groups) + self.block2 = Block(dim_out, dim_out, groups=groups) + self.res_conv = nn.Conv2d(dim, dim_out, 1) if dim != dim_out else nn.Identity() + + def forward(self, x, time_emb=None): + scale_shift = None + if exists(self.mlp) and exists(time_emb): + time_emb = self.mlp(time_emb) + time_emb = rearrange(time_emb, "b c -> b c 1 1") + scale_shift = time_emb.chunk(2, dim=1) + + h = self.block1(x, scale_shift=scale_shift) + h = self.block2(h) + return h + self.res_conv(x) + +class Attention(nn.Module): + def __init__(self, dim, heads=4, dim_head=32): + super().__init__() + self.scale = dim_head**-0.5 + self.heads = heads + hidden_dim = dim_head * heads + self.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, bias=False) + self.to_out = nn.Conv2d(hidden_dim, dim, 1) + + def forward(self, x): + b, c, h, w = x.shape + qkv = self.to_qkv(x).chunk(3, dim=1) + q, k, v = map( + lambda t: rearrange(t, "b (h c) x y -> b h c (x y)", h=self.heads), qkv + ) + q = q * self.scale + + sim = einsum("b h d i, b h d j -> b h i j", q, k) + sim = sim - sim.amax(dim=-1, keepdim=True).detach() + attn = sim.softmax(dim=-1) + + out = einsum("b h i j, b h d j -> b h i d", attn, v) + out = rearrange(out, "b h (x y) d -> b (h d) x y", x=h, y=w) + return self.to_out(out) + +class LinearAttention(nn.Module): + def __init__(self, dim, heads=4, dim_head=32): + super().__init__() + self.scale = dim_head**-0.5 + self.heads = heads + hidden_dim = dim_head * heads + self.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, bias=False) + + self.to_out = nn.Sequential(nn.Conv2d(hidden_dim, dim, 1), + nn.GroupNorm(1, dim)) + + def forward(self, x): + b, c, h, w = x.shape + qkv = self.to_qkv(x).chunk(3, dim=1) + q, k, v = map( + lambda t: rearrange(t, "b (h c) x y -> b h c (x y)", h=self.heads), qkv + ) + + q = q.softmax(dim=-2) + k = k.softmax(dim=-1) + + q = q * self.scale + context = torch.einsum("b h d n, b h e n -> b h d e", k, v) + + out = torch.einsum("b h d e, b h d n -> b h e n", context, q) + out = rearrange(out, "b h c (x y) -> b (h c) x y", h=self.heads, x=h, y=w) + return self.to_out(out) + +class PreNorm(nn.Module): + def __init__(self, dim, fn): + super().__init__() + self.fn = fn + self.norm = nn.GroupNorm(1, dim) + + def forward(self, x): + x = self.norm(x) + return self.fn(x) + + +class Unet(nn.Module): + def __init__( + self, + dim, + init_size=None, + init_dim=None, + out_dim=None, + dim_mults=(1, 2, 4), + channels=3, + self_condition=False, + resnet_block_groups=4, + ): + super().__init__() + + # determine dimensions + self.init_size = init_size + self.channels = channels + self.self_condition = self_condition + input_channels = channels * (2 if self_condition else 1) + + init_dim = default(init_dim, dim) + # print(input_channels) + # print(init_dim) + # print(init_size) + self.init_conv = nn.Conv2d(input_channels, init_dim, 1, padding=0) # changed to 1 and 0 from 7,3 + + dims = [init_dim, *map(lambda m: dim * m, dim_mults)] + in_out = list(zip(dims[:-1], dims[1:])) + + block_klass = partial(ResnetBlock, groups=resnet_block_groups) + + # time embeddings + time_dim = dim * 4 + + self.time_mlp = nn.Sequential( + SinusoidalPositionEmbeddings(dim), + nn.Linear(dim-1, time_dim), + nn.GELU(), + nn.Linear(time_dim, time_dim), + ) + + # layers + self.downs = nn.ModuleList([]) + self.ups = nn.ModuleList([]) + num_resolutions = len(in_out) + sampling_sizes = [] + + for ind, (dim_in, dim_out) in enumerate(in_out): + is_last = ind >= (num_resolutions - 1) + size = init_size if ind == 0 else torch.Size([dim_in, (last_size[1] + last_size[1] % 2) // 2, (last_size[2] + last_size[2] % 2) // 2]) + last_size = size + sampling_sizes.append((size, dim_out)) + self.downs.append( + nn.ModuleList( + [ + block_klass(dim_in, dim_in, time_emb_dim=time_dim), + block_klass(dim_in, dim_in, time_emb_dim=time_dim), + Residual(PreNorm(dim_in, LinearAttention(dim_in))), + Downsample(dim_in, dim_out, size) + if not is_last + else nn.Conv2d(dim_in, dim_out, 3, padding=1), + ] + ) + ) + + mid_dim = dims[-1] + self.mid_block1 = block_klass(mid_dim, mid_dim, time_emb_dim=time_dim) + self.mid_attn = Residual(PreNorm(mid_dim, Attention(mid_dim))) + self.mid_block2 = block_klass(mid_dim, mid_dim, time_emb_dim=time_dim) + sampling_sizes = list(reversed(sampling_sizes))[1:] + for ind, (dim_in, dim_out) in enumerate(reversed(in_out)): + is_last = ind == (len(in_out) - 1) + + self.ups.append( + nn.ModuleList( + [ + block_klass(dim_out + dim_in, dim_out, time_emb_dim=time_dim), + block_klass(dim_out + dim_in, dim_out, time_emb_dim=time_dim), + Residual(PreNorm(dim_out, LinearAttention(dim_out))), + #Upsample(dim_out, dim_in) + Upsample(dim_out, dim_in, sampling_sizes[ind]) + if not is_last + else nn.Conv2d(dim_out, dim_in, 3, padding=1), + ] + ) + ) + + self.out_dim = default(out_dim, channels) + + self.final_res_block = block_klass(dim * 2, dim, time_emb_dim=time_dim) + self.final_conv = nn.Conv2d(dim, self.out_dim, 1) + + def forward(self, x, time, x_self_cond=None): + import numpy as np + # print(np.shape(x)) + if self.self_condition: + x_self_cond = default(x_self_cond, lambda: torch.zeros_like(x)) + x = torch.cat((x_self_cond, x), dim=1) + + x = self.init_conv(x) + r = x.clone() + + t = self.time_mlp(time) + + h = [] + + for block1, block2, attn, downsample in self.downs: + x = block1(x, t) + h.append(x) + + x = block2(x, t) + x = attn(x) + h.append(x) + + x = downsample(x) + + x = self.mid_block1(x, t) + #h.append(x) + x = self.mid_attn(x) + x = self.mid_block2(x, t) + #h.append(x) + + for block1, block2, attn, upsample in self.ups: + x = torch.cat((x, h.pop()), dim=1) + x = block1(x, t) + + x = torch.cat((x, h.pop()), dim=1) + x = block2(x, t) + x = attn(x) + + x = upsample(x) + + x = torch.cat((x, r), dim=1) + + x = self.final_res_block(x, t) + return self.final_conv(x) \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/msl_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/msl_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..bb9a3a7cb5db625fe06e0a4d3cc21576345eb283 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/msl_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "MSL_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "//experiments/MSL_TRAIN_16_128_20/checkpoint/E100" + }, + "datasets": { + "test": { + "name": "msl_test", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/msl/msl_test.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 2001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/msl_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/msl_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..8440faa95a4a4e4ff00fc9a39b5b13e4c845eaf6 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/msl_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "MSL_TRAIN", + "phase": "train", + "gpu_ids": [ + 1, + 3 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "msl_train", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/msl/msl_train.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "batch_size": 32, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 100, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/psm_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/psm_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..a78f9daf724cf1e414115df89a1f06b4160272ea --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/psm_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "PSM_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "experiments/PSM_TRAIN_16_128_100/checkpoint/E20" + }, + "datasets": { + "test": { + "name": "psm_test", + "mode": "HR", + "dataroot": "tf_dataset/psm/psm_test.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 2001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/psm_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/psm_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..c9cd489fd11c86ae208fddd76de65ede3aaecacc --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/psm_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "PSM_TRAIN", + "phase": "train", + "gpu_ids": [ + 0, + 1 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "psm_train", + "mode": "HR", + "dataroot": "tf_dataset/psm/psm_train.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "batch_size": 32, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 10, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/smap_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smap_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..c73f7135dddd2ded804a915ee34a58d7c8698f84 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smap_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "SMAP_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "//experiments/SMAP_TRAIN_128_2048_20/checkpoint/E100" + }, + "datasets": { + "test": { + "name": "smap_test", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smap/smap_test.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 3001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/smap_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smap_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..d5d19094abb82daad489a68aee140b9919f832f4 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smap_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "SMAP_TRAIN", + "phase": "train", + "gpu_ids": [ + 1, + 3 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "smap_train", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smap/smap_train.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "batch_size": 32, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 10, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/smd_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smd_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..0d93cbe1f696086a3b6b85e5beed3368d609a8c6 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smd_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "SMD_TEST", + "phase": "test", + "gpu_ids": [ + 2 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "//experiments/SMD_TRAIN_128_2048_20/checkpoint/E100" + }, + "datasets": { + "test": { + "name": "smd_test", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smd/smd_test.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 1001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/config/smd_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smd_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..12e176f8e41c58c944f533031a80e70c84959a04 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/config/smd_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "SMD_TRAIN", + "phase": "train", + "gpu_ids": [ + 0, + 1 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "smd_train", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smd/smd_train.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "batch_size": 8, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 100, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a333c0111ab4934db4e3607384f137a62b48f4c Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b8153a73d945c91a502f976631af3d933396061 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a8816259b8e18c7623868a4476e80ed31657e93 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/logger.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfe4ce0ecb9a3aab8ed27d9654d55f361fff0b70 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e33c5e984edf893136343a236cecfacbfd45385 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1cdad6bde4bcab4d4ddfbbbe7becf97cae141ac4 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/core/__pycache__/metrics.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/logger.py b/subject1-4/dynamicSplit/02DiffAD-main_high/core/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..672af1886ba88de1caa889d7d90b649e34279b73 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/core/logger.py @@ -0,0 +1,143 @@ +import json +import logging +import os +from collections import OrderedDict +from datetime import datetime + + +def mkdirs(paths): + if isinstance(paths, str): + os.makedirs(paths, exist_ok=True) + else: + for path in paths: + os.makedirs(path, exist_ok=True) + + +def get_timestamp(): + return datetime.now().strftime('%y%m%d_%H%M%S') + + +def parse(args, model_epoch=None): + phase = args.phase + opt_path = args.config + gpu_ids = args.gpu_ids + enable_wandb = args.enable_wandb + # remove comments starting with '//' + json_str = '' + with open(opt_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.split('//')[0] + '\n' + json_str += line + + opt = json.loads(json_str, object_pairs_hook=OrderedDict) + + # set log directory + if args.debug: + opt['name'] = 'debug_{}'.format(opt['name']) + if opt['phase'] == 'train': + experiments_root = os.path.join( + 'experiments', '{}_{}_{}_{}'.format(opt['name'], opt['datasets']['train']['l_resolution'], + opt['datasets']['train']['r_resolution'], + opt['model']['beta_schedule']['train']['n_timestep'])) + elif opt['phase'] == 'test': + experiments_root = os.path.join( + 'experiments', '{}_{}_{}_{}_{}'.format(opt['name'], opt['datasets']['test']['l_resolution'], + opt['datasets']['test']['r_resolution'], + opt['model']['beta_schedule']['test']['n_timestep'], model_epoch)) + + opt['path']['experiments_root'] = experiments_root + for key, path in opt['path'].items(): + if 'resume' not in key and 'experiments' not in key: + opt['path'][key] = os.path.join(experiments_root, path) + mkdirs(opt['path'][key]) + + opt['phase'] = phase + + # export CUDA_VISIBLE_DEVICES + if gpu_ids is not None: + opt['gpu_ids'] = [int(id) for id in gpu_ids.split(',')] + gpu_list = gpu_ids + else: + gpu_list = ','.join(str(x) for x in opt['gpu_ids']) + os.environ['CUDA_VISIBLE_DEVICES'] = gpu_list + print('export CUDA_VISIBLE_DEVICES=' + gpu_list) + + if len(gpu_list) > 1: + opt['distributed'] = True + else: + opt['distributed'] = False + + # debug + if 'debug' in opt['name']: + opt['train']['print_freq'] = 2 + opt['train']['save_checkpoint_freq'] = 3 + opt['datasets']['train']['batch_size'] = 2 + opt['model']['beta_schedule']['train']['n_timestep'] = 10 + opt['datasets']['train']['data_len'] = 6 + + # W&B Logging + try: + log_wandb_ckpt = args.log_wandb_ckpt + opt['log_wandb_ckpt'] = log_wandb_ckpt + except: + pass + try: + log_eval = args.log_eval + opt['log_eval'] = log_eval + except: + pass + try: + log_infer = args.log_infer + opt['log_infer'] = log_infer + except: + pass + opt['enable_wandb'] = enable_wandb + + return opt + + +class NoneDict(dict): + def __missing__(self, key): + return None + + +# convert to NoneDict, which return None for missing key. +def dict_to_nonedict(opt): + if isinstance(opt, dict): + new_opt = dict() + for key, sub_opt in opt.items(): + new_opt[key] = dict_to_nonedict(sub_opt) + return NoneDict(**new_opt) + elif isinstance(opt, list): + return [dict_to_nonedict(sub_opt) for sub_opt in opt] + else: + return opt + + +def dict2str(opt, indent_l=1): + '''dict to string for logger''' + msg = '' + for k, v in opt.items(): + if isinstance(v, dict): + msg += ' ' * (indent_l * 2) + k + ':[\n' + msg += dict2str(v, indent_l + 1) + msg += ' ' * (indent_l * 2) + ']\n' + else: + msg += ' ' * (indent_l * 2) + k + ': ' + str(v) + '\n' + return msg + + +def setup_logger(logger_name, root, phase, level=logging.INFO, screen=False): + '''set up logger''' + l = logging.getLogger(logger_name) + formatter = logging.Formatter( + '%(asctime)s.%(msecs)03d - %(levelname)s: %(message)s', datefmt='%y-%m-%d %H:%M:%S') + log_file = os.path.join(root, '{}.log'.format(phase)) + fh = logging.FileHandler(log_file, mode='w') + fh.setFormatter(formatter) + l.setLevel(level) + l.addHandler(fh) + if screen: + sh = logging.StreamHandler() + sh.setFormatter(formatter) + l.addHandler(sh) diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/core/metrics.py b/subject1-4/dynamicSplit/02DiffAD-main_high/core/metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..2691ae949afc35227a77dbe1b582e5916ec238bf --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/core/metrics.py @@ -0,0 +1,169 @@ +import numpy as np +import pandas as pd +from sklearn.metrics import f1_score, accuracy_score, recall_score, precision_score, mean_squared_error + + +def squeeze_tensor(tensor): + return tensor.squeeze().cpu() + + +def update_csv_col_name(all_datas): + df = all_datas.copy() + df.columns = [0, 1, 2, 3] + + return df + + +def tensor2allcsv(visuals, col_num): + df = pd.DataFrame() + sr_df = pd.DataFrame(squeeze_tensor(visuals['SR'])) + ori_df = pd.DataFrame(squeeze_tensor(visuals['ORI'])) + lr_df = pd.DataFrame(squeeze_tensor(visuals['LR'])) + inf_df = pd.DataFrame(squeeze_tensor(visuals['INF'])) + + if col_num != 1: + for i in range(col_num, sr_df.shape[1]): + sr_df.drop(labels=i, axis=1, inplace=True) + ori_df.drop(labels=i, axis=1, inplace=True) + lr_df.drop(labels=i, axis=1, inplace=True) + inf_df.drop(labels=i, axis=1, inplace=True) + + df['SR'] = sr_df.mean(axis=1) + df['ORI'] = ori_df.mean(axis=1) + df['LR'] = lr_df.mean(axis=1) + df['INF'] = inf_df.mean(axis=1) + + df['differ'] = (ori_df - sr_df).abs().mean(axis=1) + df['label'] = squeeze_tensor(visuals['label']) + + differ_df = (sr_df - ori_df) + + return df, sr_df, differ_df + + +def merge_all_csv(all_datas, all_data): + all_datas = pd.concat([all_datas, all_data]) + return all_datas + + +def save_csv(data, data_path): + data.to_csv(data_path, index=False) + + +def get_mean(df): + mean = df['value'].astype('float32').mean() + normal_mean = df['value'][df['label'] == 0].astype('float32').mean() + anomaly_mean = df['value'][df['label'] == 1].astype('float32').mean() + + return mean, normal_mean, anomaly_mean + + +def get_val_mean(df): + mean_dict = {} + + ori = 'ORI' + ori_mean = df[ori].astype('float32').mean() + ori_normal_mean = df[ori][df['label'] == 0].astype('float32').mean() + ori_anomaly_mean = df[ori][df['label'] == 1].astype('float32').mean() + + gen_mean = df['SR'].astype('float32').mean() + gen_normal_mean = df['SR'][df['label'] == 0].astype('float32').mean() + gen_anomaly_mean = df['SR'][df['label'] == 1].astype('float32').mean() + + mean_dict['MSE'] = mean_squared_error(df[ori], df['SR']) + + mean_dict['ori_mean'] = ori_mean + mean_dict['ori_normal_mean'] = ori_normal_mean + mean_dict['ori_anomaly_mean'] = ori_anomaly_mean + + mean_dict['gen_mean'] = gen_mean + mean_dict['gen_normal_mean'] = gen_normal_mean + mean_dict['gen_anomaly_mean'] = gen_anomaly_mean + + mean_dict['mean_differ'] = ori_mean - gen_mean + mean_dict['normal_mean_differ'] = ori_normal_mean - gen_normal_mean + mean_dict['anomaly_mean_differ'] = ori_anomaly_mean - gen_anomaly_mean + + mean_dict['ori_no-ano_differ'] = ori_normal_mean - ori_anomaly_mean + mean_dict['ori_mean-no_differ'] = ori_mean - ori_normal_mean + mean_dict['ori_mean-ano_differ'] = ori_mean - ori_anomaly_mean + + mean_dict['gen_no-ano_differ'] = gen_normal_mean - gen_anomaly_mean + mean_dict['gen_mean-no_differ'] = gen_mean - gen_normal_mean + mean_dict['gen_mean-ano_differ'] = gen_mean - gen_anomaly_mean + + return mean_dict + + +def relabeling_strategy(df, params): + y_true = [] + best_N = 0 + best_f1 = -1 + best_thred = 0 + best_predictions = [] + thresholds = np.arange(params['start_label'], params['end_label'], params['step_label']) + + df_sort = df.sort_values(by="differ", ascending=False) + df_sort = df_sort.reset_index(drop=False) + + for t in thresholds: + # if (t - 1) % params['step_t'] == 0: + # print("t: ", t) + y_true, y_pred, thred = predict_labels(df_sort, t) + for i in range(len(y_true)): + if y_pred[i] == 1 and y_true[i] == 1: + j = i - 1 + while j >= 0 and y_true[j] == 1 and y_pred[j] == 0: + y_pred[j] = 1 + j -= 1 + j = i + 1 + while j < len(y_pred) and y_true[j] == 1 and y_pred[j] == 0: + y_pred[j] = 1 + j += 1 + + f1 = calculate_f1(y_true, y_pred) + if f1 > best_f1: + best_f1 = f1 + best_N = t + best_thred = thred + best_predictions = y_pred + + accuracy = calculate_accuracy(y_true, best_predictions) + precision = calculate_precision(y_true, best_predictions) + recall = calculate_recall(y_true, best_predictions) + + return best_f1,accuracy,precision,recall + + +def predict_labels(df_sort, num): + df_sort['pred_label'] = 0 + df_sort.loc[0:num - 1, 'pred_label'] = 1 + thred = df_sort.loc[num - 1, 'differ'] + + df_sort = df_sort.set_index('index') + df_sort = df_sort.sort_index() + + y_true = df_sort['label'].tolist() + y_pred = df_sort['pred_label'].tolist() + + return y_true, y_pred, thred + + +def calculate_accuracy(y_true, y_pred): + accuracy = accuracy_score(y_true, y_pred) + return accuracy + + +def calculate_precision(y_true, y_pred): + precision = precision_score(y_true, y_pred) + return precision + + +def calculate_recall(y_true, y_pred): + recall = recall_score(y_true, y_pred) + return recall + + +def calculate_f1(y_true, y_pred): + f1 = f1_score(y_true, y_pred) + return f1 diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/LRHR_dataset.py b/subject1-4/dynamicSplit/02DiffAD-main_high/data/LRHR_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..b22f92136b774244541be2fbf50193d89e6e1a35 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/data/LRHR_dataset.py @@ -0,0 +1,43 @@ +from torch.utils.data import Dataset + +from data.prepare_time_data import PrepareTimeData + + +class LRHRDataset(Dataset): + def __init__(self, dataroot, datatype, phase, l_resolution=16, r_resolution=128, + split='train', data_len=-1, need_LR=False): + self.datatype = datatype + self.data_len = data_len + self.need_LR = need_LR + self.split = split + self.phase = phase + self.pre_data = PrepareTimeData(data_path=dataroot, phase=phase, base=l_resolution, size=r_resolution) + self.row_num = self.pre_data.get_row_num() + self.col_num = self.pre_data.get_col_num() + + if datatype == 'time': + self.hr_path, self.sr_path, self.labels, self.pre_labels = self.pre_data.get_sr_data() + self.dataset_len = len(self.sr_path) + if self.data_len <= 0: + self.data_len = self.dataset_len + else: + self.data_len = min(self.data_len, self.dataset_len) + else: + raise NotImplementedError( + 'data_type [{:s}] is not recognized.'.format(datatype)) + + def __len__(self): + return self.data_len + + def __getitem__(self, index): + + data_LR = None + data_ORI = self.hr_path[index] + data_HR = self.hr_path[index] + data_SR = self.sr_path[index] + data_label = self.labels[index] + + if self.phase == 'train': + return {'HR': data_HR, 'SR': data_SR, 'Index': index} + else: + return {'ORI': data_ORI, 'HR': data_HR, 'SR': data_SR, 'label': data_label, 'Index': index} diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__init__.py b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9c915ff01b1a408c16e40fb1c5ce6eda3df0e9bc --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__init__.py @@ -0,0 +1,42 @@ +'''create dataset and dataloader''' +import logging + +import torch.utils.data + + +def create_dataloader(dataset, dataset_opt, phase): + '''create dataloader ''' + if phase == 'train': + return torch.utils.data.DataLoader( + dataset, + batch_size=dataset_opt['batch_size'], + shuffle=dataset_opt['use_shuffle'], + num_workers=dataset_opt['num_workers'], + pin_memory=True) + elif phase == 'val': + return torch.utils.data.DataLoader( + dataset, batch_size=1, shuffle=False, num_workers=1, pin_memory=True) + elif phase == 'test': + return torch.utils.data.DataLoader( + dataset, batch_size=1, shuffle=False, num_workers=1, pin_memory=True) + else: + raise NotImplementedError( + 'Dataloader [{:s}] is not found.'.format(phase)) + + +def create_dataset(dataset_opt, phase): + '''create dataset''' + mode = dataset_opt['mode'] + from data.LRHR_dataset import LRHRDataset as D + dataset = D(dataroot=dataset_opt['dataroot'], + datatype=dataset_opt['datatype'], + l_resolution=dataset_opt['l_resolution'], + r_resolution=dataset_opt['r_resolution'], + split=phase, + data_len=dataset_opt['data_len'], + need_LR=(mode == 'LR'), + phase=phase) + logger = logging.getLogger('base') + logger.info('Dataset [{:s} - {:s}] is created.'.format(dataset.__class__.__name__, + dataset_opt['name'])) + return dataset diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6f440b01f784415869d311f231f82cc79786cb9 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbe04557d59ecd665ab5936d0f456f5392e2bbb7 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e39c78c33598a1ab6c337e134f89dfec4d299be Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/LRHR_dataset.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8eb84ea015c91d03a6b9af293bb90b7ce97b8a33 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd57e7845ba5e7c82b9578c15d89c58752c37872 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..290fea552665c003812613a8cd173fc4557cbf5e Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/__init__.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b140cae6c826e507ce230de76177672c9473ca8c Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bb9ef515c71780a74d445b58d889b8fbb557792 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..512dafab3324b460ed69a511d6cb0b5e2e15e974 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/data/__pycache__/prepare_time_data.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/data/prepare_time_data.py b/subject1-4/dynamicSplit/02DiffAD-main_high/data/prepare_time_data.py new file mode 100644 index 0000000000000000000000000000000000000000..646f0c8ad6766994a3ba2174d5cd2eef85b9ddc4 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/data/prepare_time_data.py @@ -0,0 +1,355 @@ +import math +import warnings + +import numpy as np +import pandas as pd +import torch + +warnings.filterwarnings("ignore") + + +class PrepareTimeData: + def __init__(self, data_path, phase, base, size): + self.data_path = data_path + self.phase = phase + self.base = base + self.size = size + + self.data_name = self.data_path.split('/')[-1].split('_')[0] + self.read_dataset(self.data_path, self.data_name) + self.df = self.ori_df.copy() + self.row_num = self.ori_df.shape[0] + self.col_num = self.ori_df.shape[1] + self.mean = self.df.mean(axis=1) + + self.df = self.get_mean_df(self.df) + self.df = self.vertical_merge_df(self.df) + self.df = self.join_together_labels(self.df) + self.df = self.fill_data(self.df) + self.df = self.standardize_data(self.df) + + def get_hr_data(self): + df = self.df.copy() + ori_values, values, labels, pre_labels = self.get_data_by_interval(df) + + return ori_values, values, labels, pre_labels + + def get_sr_data(self): + df = self.df.copy() + ori_values, values, labels, pre_labels = self.get_data_by_insert_normal() + + return ori_values, values, labels, pre_labels + + def get_mean_df(self, df): + df = df.copy() + for col in df.columns: + df[col] = self.mean + return df + + def vertical_merge_df(self, df): + df = df.copy() + two_power = 2 + + if self.col_num < 16: + two_power = 16 + df_temp = pd.DataFrame() + col_count = 0 + for i in range(two_power - self.col_num): + if col_count >= self.col_num: + col_count = 0 + df_temp[i] = df.iloc[:, col_count] + col_count = col_count + 1 + else: + while self.col_num > two_power: + two_power = two_power * 2 + df_temp = df.iloc[:, 0:(two_power - self.col_num)] + + col_name = [] + for i in range(self.col_num): + col_name.append('value_' + str(i)) + + df.columns = col_name + col_name = [] + for i in range(self.col_num, two_power): + col_name.append('value_' + str(i)) + + df_temp.columns = col_name + df = pd.concat([df, df_temp], axis=1) + return df + + def join_together_labels(self, df): + df = df.copy() + + if self.phase == 'train': + df['label'] = 0 + else: + df['label'] = self.test_labels + return df + + def fill_data(self, df): + df = df.copy() + data_end = math.ceil(self.row_num / self.size) * self.size + + for i in range(self.row_num, data_end): + df = df._append(pd.Series(), ignore_index=True) + + df.fillna(0, inplace=True) + return df + + def read_dataset(self, data_path, data_name): + if data_name.upper().find('MSL') != -1: + cols = [-1] + self.get_dataset(data_path, cols) + elif data_name.upper().find('PSM') != -1: + if self.phase == 'train': + cols = [-1] + self.get_dataset(data_path, cols) + if self.ori_df.columns.__contains__('timestamp_(min)'): + self.ori_df.drop(columns=['timestamp_(min)'], inplace=True) + else: + cols = [-1] + self.get_dataset(data_path, cols) + if self.ori_df.columns.__contains__('timestamp_(min)'): + self.ori_df.drop(columns=['timestamp_(min)'], inplace=True) + self.test_labels.drop(columns=['timestamp_(min)'], inplace=True) + elif data_name.upper().find('SMAP') != -1: + cols = [0, 1, 2, 3, 4, 7, 8, 9, 10, 12, 13, 15, 16, 19, 20] + self.get_dataset(data_path, cols) + elif data_name.upper().find('SMD') != -1: + cols = [0, 1, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 28, 33, 35, 36, 37] + self.get_dataset(data_path, cols) + + def get_dataset(self, data_path, cols): + if self.phase == 'train': + if -1 in cols: + self.ori_df = pd.read_csv(data_path) + else: + self.ori_df = pd.read_csv(data_path, usecols=cols) + else: + if -1 in cols: + self.ori_df = pd.read_csv(data_path) + else: + self.ori_df = pd.read_csv(data_path, usecols=cols) + + test_label_path = self.data_path.replace('_test.csv', '_test_label.csv') + self.test_labels = pd.read_csv(test_label_path) + + def get_data_by_insert_normal(self): + df = pd.DataFrame(columns=['value', 'label']) + df['value'] = self.df['value_0'] + df['label'] = self.df['label'] + + df_pre_label = self.mutation_point(df) + insert_datas = self.insert_normal(df_pre_label) + + ori_values = [] + values = [] + labels = [] + pre_labels = [] + + start_index = 0 + end_index = self.size + + for col in self.df.columns: + if col == 'label': + continue + self.df[col] = insert_datas['value'] + self.df['pre_label'] = insert_datas['pre_label'] + + ori_df = self.vertical_merge_df(self.ori_df) + ori_df = self.fill_data(ori_df) + + for i in range(0, self.df.shape[0], self.size): + insert_data = pd.DataFrame() + ori_value = pd.DataFrame() + + insert_data = pd.concat([insert_data, self.df[start_index: end_index]]) + ori_value = pd.concat([ori_value, ori_df[start_index: end_index]]) + start_index += self.size + end_index += self.size + + value = insert_data.copy().drop(['label', 'pre_label'], axis=1) + label = insert_data['label'] + pre_label = insert_data['pre_label'] + + value = torch.tensor(np.array(value).astype(np.float32)) + label = torch.tensor(np.array(label).astype(np.int64)) + pre_label = torch.tensor(np.array(pre_label).astype(np.int64)) + ori_value = torch.tensor(np.array(ori_value).astype(np.float32)) + + values.append(value.unsqueeze(0)) + labels.append(label) + pre_labels.append(pre_label) + ori_values.append(ori_value.unsqueeze(0)) + + return ori_values, values, labels, pre_labels + + def standardize_data(self, df): + df = df.copy() + name = self.data_path.split('.csv')[0] + print(name, "Points: {}".format(self.row_num)) + df = self.complete_value(df) + + if self.phase != 'train': + anomaly_len = len(df[df['label'] == 1].index.tolist()) + print("Labeled anomalies: {}".format(anomaly_len)) + + return df + + def complete_value(self, df): + + df.fillna(0, inplace=True) + return df + + def get_mutation_point(self, df_pre_label, start_index, end_index, last_size_var): + size_var = df_pre_label['value'][start_index: end_index].var() + label_count = len(df_pre_label[start_index: end_index][df_pre_label['label'] == 1].index.tolist()) + + if last_size_var == 0: + times = 'Nan' + else: + times = size_var / last_size_var + if times < 1 and times != 0: + times = 1 / times + + if times != "Nan" and times >= 10: + df_pre_label['pre_label'][start_index: end_index] = 1 + else: + df_pre_label['pre_label'][start_index: end_index] = 0 + + return size_var + + def mutation_point(self, df): + df_pre_label = df.copy() + df_pre_label['pre_label'] = 0 + + size = 128 + start_index = 0 + end_index = size + all_var = df_pre_label['value'].var() + + last_size_var = 0 + for i in range(int(self.row_num / size)): + last_size_var = self.get_mutation_point(df_pre_label, start_index, end_index, last_size_var) + + start_index += size + end_index += size + + self.get_mutation_point(df_pre_label, start_index, self.row_num - 1, last_size_var) + return df_pre_label + + def get_index(self, indexes): + count = 0 + start_indexes = [] + end_indexes = [] + + if len(indexes) != 0: + count = count + 1 + start_indexes.append(indexes[0]) + + for i in range(1, len(indexes)): + if indexes[i - 1] + 1 != indexes[i]: + count = count + 1 + end_indexes.append(indexes[i - 1]) + start_indexes.append(indexes[i]) + + end_indexes.append(indexes[len(indexes) - 1]) + + return start_indexes, end_indexes, count + + def insert_normal(self, data): + pre_labels = 'pre_label' + + nor_indexes = data[0:self.row_num][data[pre_labels] == 0].index.tolist() + ano_indexes = data[0:self.row_num][data[pre_labels] == 1].index.tolist() + + nor_start_indexes, nor_end_indexes, nor_count = self.get_index(nor_indexes) + ano_start_indexes, ano_end_indexes, ano_count = self.get_index(ano_indexes) + + interval = int(self.size / self.base) + ano_len = 2 + + df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + for i in range(nor_count): + + if nor_end_indexes[i] - nor_start_indexes[i] + 1 < interval: + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + x = range(nor_start_indexes[i], nor_end_indexes[i] + 1) + xp = [nor_start_indexes[i], nor_end_indexes[i]] + fp = [data['value'][nor_start_indexes[i]], data['value'][nor_end_indexes[i]]] + z = np.interp(x, xp, fp) + + temp_df['ind'] = x + temp_df['value'] = z + temp_df['pre_label'] = 0 + df = pd.concat([df, temp_df]) + else: + last_start_x = -1 + start_xs = range(nor_start_indexes[i], nor_end_indexes[i] + 1, interval) + xp = [] + fp = [] + for start_x in start_xs: + if start_x + interval > nor_end_indexes[i]: + last_start_x = start_x + break + + xp.append(start_x) + xp.append(start_x + interval - 1) + + fp.append(data['value'][start_x]) + fp.append(data['value'][start_x + interval - 1]) + + x = range(nor_start_indexes[i], last_start_x) + z = np.interp(x, xp, fp) + + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + temp_df['ind'] = x + temp_df['value'] = z + temp_df['pre_label'] = 0 + df = pd.concat([df, temp_df]) + + if last_start_x != -1: + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + x = range(last_start_x, nor_end_indexes[i] + 1) + xp = [last_start_x, nor_end_indexes[i]] + fp = [data['value'][last_start_x], data['value'][nor_end_indexes[i]]] + z = np.interp(x, xp, fp) + + temp_df['ind'] = x + temp_df['value'] = z + temp_df['pre_label'] = 0 + df = pd.concat([df, temp_df]) + + for i in range(ano_count): + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + x = range(ano_start_indexes[i] - 1, ano_end_indexes[i] + 2) + xp = [ano_start_indexes[i] - 1, ano_end_indexes[i] + 1] + fp = [data['value'][ano_start_indexes[i] - 1], data['value'][ano_end_indexes[i] + 1]] + z = np.interp(x, xp, fp) + + for j in range(len(x)): + if j == 0 or j == len(x) - 1: + continue + + temp_df.loc[x[j], 'ind'] = x[j] + temp_df.loc[x[j], 'value'] = z[j] + temp_df.loc[x[j], 'pre_label'] = 1 + + df = pd.concat([df, temp_df]) + + df = df.set_index(['ind'], inplace=False).sort_index() + df['label'] = data['label'] + for i in range(self.row_num, data.shape[0]): + df = df._append(pd.Series(), ignore_index=True) + df.fillna(0, inplace=True) + return df + + def get_row_num(self): + return self.row_num + + def get_col_num(self): + return self.col_num diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__init__.py b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0eb77d1c825dcdeceddbc99eddf974a062750626 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__init__.py @@ -0,0 +1,10 @@ +import logging + +logger = logging.getLogger('base') + + +def create_model(opt): + from .model import DDPM as M + m = M(opt) + logger.info('Model [{:s}] is created.'.format(m.__class__.__name__)) + return m diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..082de7cfb60ff5dd414beac17a353dfd923d5957 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78d1ba7353e61cd665d6a5c549a9e690b94854ec Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab45257dcdcb10a5de99202a7f9699e0e3952e25 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/__init__.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a170668104bed75c2af54769631fedc794c476b6 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c487ba492eb115fb44ec4ebff51ce73c3eaa16a Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3da8f605c28b8459254af43ffe831b1b9783811d Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/base_model.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eaaf2026989c9d60984dd033f4de1f75f61500ac Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6d3feb52b05a954211ccca611f7a60e346cf07d Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5b417c9cb376fba19d0425af84e967504b2847c Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/model.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8a0e3a599f235d2d50149e7cb4433ff164390c2 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f54583dcd8ca45edf1ccee30b849a6bd8b1630a6 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5b7f298038b9d6c90d587852ba00957106b20b2 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/__pycache__/networks.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/base_model.py b/subject1-4/dynamicSplit/02DiffAD-main_high/model/base_model.py new file mode 100644 index 0000000000000000000000000000000000000000..de1022eb58ad1e4b8f79553282403e843aa04609 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/model/base_model.py @@ -0,0 +1,47 @@ +import torch +import torch.nn as nn + + +class BaseModel(): + def __init__(self, opt): + self.opt = opt + self.device = torch.device( + 'cuda' if opt['gpu_ids'] is not None else 'cpu') + self.begin_step = 0 + self.begin_epoch = 0 + + def feed_data(self, data): + pass + + def optimize_parameters(self): + pass + + def get_current_visuals(self): + pass + + def get_current_losses(self): + pass + + def print_network(self): + pass + + def set_device(self, x): + if isinstance(x, dict): + for key, item in x.items(): + if item is not None: + x[key] = item.to(self.device) + elif isinstance(x, list): + for item in x: + if item is not None: + item = item.to(self.device) + else: + x = x.to(self.device) + return x + + def get_network_description(self, network): + '''Get the string and total parameters of the network''' + if isinstance(network, nn.DataParallel): + network = network.module + s = str(network) + n = sum(map(lambda x: x.numel(), network.parameters())) + return s, n diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/model.py b/subject1-4/dynamicSplit/02DiffAD-main_high/model/model.py new file mode 100644 index 0000000000000000000000000000000000000000..d0a61048b7f681b5f40adf9663ac6da256febe1f --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/model/model.py @@ -0,0 +1,175 @@ +import logging +import os +from collections import OrderedDict + +import torch +import torch.nn as nn + +import model.networks as networks +from .base_model import BaseModel + +logger = logging.getLogger('base') + + +class DDPM(BaseModel): + def __init__(self, opt): + super(DDPM, self).__init__(opt) + netG = networks.define_G(opt) + + self.netG = self.set_device(netG) + self.schedule_phase = None + + # set loss and load resume state + self.set_loss() + self.set_new_noise_schedule( + opt['model']['beta_schedule']['train'], schedule_phase='train') + + if self.opt['phase'] == 'train': + self.netG.train() + if opt['model']['finetune_norm']: + optim_params = [] + for k, v in self.netG.named_parameters(): + v.requires_grad = False + if k.find('transformer') >= 0: + v.requires_grad = True + v.data.zero_() + optim_params.append(v) + logger.info( + 'Params [{:s}] initialized to 0 and will optimize.'.format(k)) + else: + optim_params = list(self.netG.parameters()) + + self.optG = torch.optim.Adam( + optim_params, lr=opt['train']["optimizer"]["lr"]) + self.log_dict = OrderedDict() + self.load_network() + self.print_network() + + def feed_data(self, data): + self.data = self.set_device(data) + + def optimize_parameters(self): + self.optG.zero_grad() + l_pix = self.netG(self.data) + # need to average in multi-gpu + b, c, h, w = self.data['HR'].shape + l_pix = l_pix.sum() / int(b * c * h * w) + + l_pix.backward() + self.optG.step() + + # set log + self.log_dict['l_pix'] = l_pix.item() + + def test(self,connection,continous=False): + self.netG.eval() + + with torch.no_grad(): + ori = self.data['ORI'].squeeze() + min_num = ori.min().item() + max_num = ori.max().item() + if isinstance(self.netG, nn.DataParallel): + self.SR = self.netG.module.super_resolution( + self.data['SR'],connection, continous=continous, min_num=min_num, max_num=max_num) + else: + self.SR = self.netG.super_resolution( + self.data['SR'],connection, continous=continous, min_num=min_num, max_num=max_num) + self.netG.train() + + def sample(self, batch_size=1, continous=False): + self.netG.eval() + with torch.no_grad(): + if isinstance(self.netG, nn.DataParallel): + self.SR = self.netG.module.sample(batch_size, continous) + else: + self.SR = self.netG.sample(batch_size, continous) + self.netG.train() + + def set_loss(self): + if isinstance(self.netG, nn.DataParallel): + self.netG.module.set_loss(self.device) + else: + self.netG.set_loss(self.device) + + def set_new_noise_schedule(self, schedule_opt, schedule_phase='train'): + if self.schedule_phase is None or self.schedule_phase != schedule_phase: + self.schedule_phase = schedule_phase + if isinstance(self.netG, nn.DataParallel): + self.netG.module.set_new_noise_schedule( + schedule_opt, self.device) + else: + self.netG.set_new_noise_schedule(schedule_opt, self.device) + + def get_current_log(self): + return self.log_dict + + def get_current_visuals(self, need_LR=True, sample=False): + out_dict = OrderedDict() + if sample: + out_dict['SAM'] = self.SR.detach().float().cpu() + else: + out_dict['SR'] = self.SR.detach().float().cpu() + out_dict['INF'] = self.data['SR'].detach().float().cpu() + out_dict['ORI'] = self.data['ORI'].detach().float().cpu() + out_dict['HR'] = self.data['HR'].detach().float().cpu() + out_dict['label'] = self.data['label'].detach().cpu() + if need_LR and 'LR' in self.data: + out_dict['LR'] = self.data['LR'].detach().float().cpu() + else: + out_dict['LR'] = out_dict['INF'] + return out_dict + + def print_network(self): + s, n = self.get_network_description(self.netG) + if isinstance(self.netG, nn.DataParallel): + net_struc_str = '{} - {}'.format(self.netG.__class__.__name__, + self.netG.module.__class__.__name__) + else: + net_struc_str = '{}'.format(self.netG.__class__.__name__) + + logger.info( + 'Network G structure: {}, with parameters: {:,d}'.format(net_struc_str, n)) + logger.info(s) + + def save_network(self, epoch, iter_step): + gen_path = os.path.join( + self.opt['path']['checkpoint'], 'E{}_gen.pth'.format(epoch)) + opt_path = os.path.join( + self.opt['path']['checkpoint'], 'E{}_opt.pth'.format(epoch)) + # gen + network = self.netG + if isinstance(self.netG, nn.DataParallel): + network = network.module + state_dict = network.state_dict() + for key, param in state_dict.items(): + state_dict[key] = param.cpu() + torch.save(state_dict, gen_path) + # opt + opt_state = {'epoch': epoch, 'iter': iter_step, + 'scheduler': None, 'optimizer': None} + opt_state['optimizer'] = self.optG.state_dict() + torch.save(opt_state, opt_path) + + logger.info( + 'Saved model in [{:s}] ...'.format(gen_path)) + + def load_network(self): + load_path = self.opt['path']['resume_state'] + if load_path is not None: + logger.info( + 'Loading pretrained model for G [{:s}] ...'.format(load_path)) + gen_path = '{}_gen.pth'.format(load_path) + opt_path = '{}_opt.pth'.format(load_path) + # gen + network = self.netG + if isinstance(self.netG, nn.DataParallel): + network = network.module + network.load_state_dict(torch.load( + gen_path), strict=(not self.opt['model']['finetune_norm'])) + + if self.opt['phase'] == 'train': + # optimizer + opt = torch.load(opt_path) + self.optG.load_state_dict(opt['optimizer']) + self.begin_step = opt['iter'] + self.begin_epoch = opt['epoch'] diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/networks.py b/subject1-4/dynamicSplit/02DiffAD-main_high/model/networks.py new file mode 100644 index 0000000000000000000000000000000000000000..d2769b5477610f43aca87cf485e59c9c1c5e5313 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/model/networks.py @@ -0,0 +1,112 @@ +import functools +import logging + +import torch +import torch.nn as nn +from torch.nn import init + +logger = logging.getLogger('base') + + +# initialize +def weights_init_normal(m, std=0.02): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + init.normal_(m.weight.data, 0.0, std) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('Linear') != -1: + init.normal_(m.weight.data, 0.0, std) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('BatchNorm2d') != -1: + init.normal_(m.weight.data, 1.0, std) # BN also uses norm + init.constant_(m.bias.data, 0.0) + + +def weights_init_kaiming(m, scale=1): + classname = m.__class__.__name__ + if classname.find('Conv2d') != -1: + init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + m.weight.data *= scale + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('Linear') != -1: + init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + m.weight.data *= scale + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('BatchNorm2d') != -1: + init.constant_(m.weight.data, 1.0) + init.constant_(m.bias.data, 0.0) + + +def weights_init_orthogonal(m): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + init.orthogonal_(m.weight.data, gain=1) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('Linear') != -1: + init.orthogonal_(m.weight.data, gain=1) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('BatchNorm2d') != -1: + init.constant_(m.weight.data, 1.0) + init.constant_(m.bias.data, 0.0) + + +def init_weights(net, init_type='kaiming', scale=1, std=0.02): + # scale for 'kaiming', std for 'normal'. + logger.info('Initialization method [{:s}]'.format(init_type)) + if init_type == 'normal': + weights_init_normal_ = functools.partial(weights_init_normal, std=std) + net.apply(weights_init_normal_) + elif init_type == 'kaiming': + weights_init_kaiming_ = functools.partial( + weights_init_kaiming, scale=scale) + net.apply(weights_init_kaiming_) + elif init_type == 'orthogonal': + net.apply(weights_init_orthogonal) + else: + raise NotImplementedError( + 'initialization method [{:s}] not implemented'.format(init_type)) + + +# define network +def define_G(opt): + model_opt = opt['model'] + if model_opt['which_model_G'] == 'sr3': + from .sr3_modules import diffusion, unet + + if ('norm_groups' not in model_opt['unet']) or model_opt['unet']['norm_groups'] is None: + model_opt['unet']['norm_groups'] = 32 + + model = unet.UNet( + in_channel=model_opt['unet']['in_channel'], + out_channel=model_opt['unet']['out_channel'], + norm_groups=model_opt['unet']['norm_groups'], + inner_channel=model_opt['unet']['inner_channel'], + channel_mults=model_opt['unet']['channel_multiplier'], + attn_res=model_opt['unet']['attn_res'], + res_blocks=model_opt['unet']['res_blocks'], + dropout=model_opt['unet']['dropout'], + time_size=model_opt['diffusion']['time_size'] + ) + + netG = diffusion.GaussianDiffusion( + model, + time_size=model_opt['diffusion']['time_size'], + channels=model_opt['diffusion']['channels'], + loss_type='l1', # L1 or L2 + conditional=model_opt['diffusion']['conditional'], + schedule_opt=model_opt['beta_schedule']['train'] + ) + if opt['phase'] == 'train': + init_weights(netG, init_type='orthogonal') + + if opt['gpu_ids'] and opt['distributed']: + assert torch.cuda.is_available() + netG = nn.DataParallel(netG) + + return netG diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02efccf97b419249399eb1051362db2cdf2e5ab4 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c07ac5f1fb3a4272820d36f3ec06b99f65f03bbf Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be4f35138c6051016821f8bea0216749cb0393f2 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/diffusion.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-311.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..238e09c47c02c50a1d25ac838b07d2e8110ed8ae Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-311.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69c86c07ff82dfdf6974e215ce6058f76e46913d Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..914d38468f8770184e50ff0233c2c5f5851c6297 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/__pycache__/unet.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/diffusion.py b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/diffusion.py new file mode 100644 index 0000000000000000000000000000000000000000..0e0ec32e9e8098209b2e7017e548540644ce385f --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/diffusion.py @@ -0,0 +1,284 @@ +import math +from functools import partial +from inspect import isfunction + +import numpy as np +import torch +from torch import nn +from tqdm import tqdm +import socket +import time +import io + +def _warmup_beta(linear_start, linear_end, n_timestep, warmup_frac): + betas = linear_end * np.ones(n_timestep, dtype=np.float64) + warmup_time = int(n_timestep * warmup_frac) + betas[:warmup_time] = np.linspace( + linear_start, linear_end, warmup_time, dtype=np.float64) + return betas + + +def make_beta_schedule(schedule, n_timestep, linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if schedule == 'quad': + betas = np.linspace(linear_start ** 0.5, linear_end ** 0.5, + n_timestep, dtype=np.float64) ** 2 + elif schedule == 'linear': + betas = np.linspace(linear_start, linear_end, + n_timestep, dtype=np.float64) + elif schedule == 'warmup10': + betas = _warmup_beta(linear_start, linear_end, + n_timestep, 0.1) + elif schedule == 'warmup50': + betas = _warmup_beta(linear_start, linear_end, + n_timestep, 0.5) + elif schedule == 'const': + betas = linear_end * np.ones(n_timestep, dtype=np.float64) + elif schedule == 'jsd': # 1/T, 1/(T-1), 1/(T-2), ..., 1 + betas = 1. / np.linspace(n_timestep, + 1, n_timestep, dtype=np.float64) + elif schedule == "cosine": + timesteps = ( + torch.arange(n_timestep + 1, dtype=torch.float64) / + n_timestep + cosine_s + ) + alphas = timesteps / (1 + cosine_s) * math.pi / 2 + alphas = torch.cos(alphas).pow(2) + alphas = alphas / alphas[0] + betas = 1 - alphas[1:] / alphas[:-1] + betas = betas.clamp(max=0.999) + else: + raise NotImplementedError(schedule) + return betas + + +# gaussian diffusion trainer class +def exists(x): + return x is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +class GaussianDiffusion(nn.Module): + def __init__(self, denoise_fn, time_size, channels=3, loss_type='l1', + conditional=True, schedule_opt=None): + + super().__init__() + self.channels = channels + self.time_size = time_size + self.denoise_fn = denoise_fn + self.loss_type = loss_type + self.conditional = conditional + if schedule_opt is not None: + pass + + def set_loss(self, device): + if self.loss_type == 'l1': + self.loss_func = nn.L1Loss(reduction='sum').to(device) + elif self.loss_type == 'l2': + self.loss_func = nn.MSELoss(reduction='sum').to(device) + else: + raise NotImplementedError() + + def set_new_noise_schedule(self, schedule_opt, device): + to_torch = partial(torch.tensor, dtype=torch.float32, device=device) + + betas = make_beta_schedule( + schedule=schedule_opt['schedule'], + n_timestep=schedule_opt['n_timestep'], + linear_start=schedule_opt['linear_start'], + linear_end=schedule_opt['linear_end']) + + betas = betas.detach().cpu().numpy() if isinstance( + betas, torch.Tensor) else betas + + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + self.sqrt_alphas_cumprod_prev = np.sqrt( + np.append(1., alphas_cumprod)) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', + to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', + to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', + to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', + to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', + to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', + to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + posterior_variance = betas * \ + (1. - alphas_cumprod_prev) / (1. - alphas_cumprod) + # above: equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t) + self.register_buffer('posterior_variance', + to_torch(posterior_variance)) + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + self.register_buffer('posterior_log_variance_clipped', to_torch( + np.log(np.maximum(posterior_variance, 1e-20)))) + self.register_buffer('posterior_mean_coef1', to_torch( + betas * np.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod))) + self.register_buffer('posterior_mean_coef2', to_torch( + (1. - alphas_cumprod_prev) * np.sqrt(alphas) / (1. - alphas_cumprod))) + + def predict_start_from_noise(self, x_t, t, noise): + return self.sqrt_recip_alphas_cumprod[t] * x_t - \ + self.sqrt_recipm1_alphas_cumprod[t] * noise + + def q_posterior(self, x_start, x_t, t): + posterior_mean = self.posterior_mean_coef1[t] * \ + x_start + self.posterior_mean_coef2[t] * x_t + posterior_log_variance_clipped = self.posterior_log_variance_clipped[t] + return posterior_mean, posterior_log_variance_clipped + + def p_mean_variance(self, x, t, clip_denoised: bool, condition_x=None): + batch_size = x.shape[0] + noise_level = torch.FloatTensor( + [self.sqrt_alphas_cumprod_prev[t + 1]]).repeat(batch_size, 1).to(x.device) + if condition_x is not None: + x_temp = torch.cat([condition_x, x], dim=1) + noise = self.denoise_fn(x_temp, noise_level) + x_recon = self.predict_start_from_noise(x, t=t, noise=noise) + else: + x_recon = self.predict_start_from_noise( + x, t=t, noise=self.denoise_fn(x, noise_level)) + + if clip_denoised: + x_recon.clamp_(self.min_num, self.max_num) + + model_mean, posterior_log_variance = self.q_posterior( + x_start=x_recon, x_t=x, t=t) + return model_mean, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, t, clip_denoised=True, condition_x=None): + model_mean, model_log_variance = self.p_mean_variance( + x=x, t=t, clip_denoised=clip_denoised, condition_x=condition_x) + + noise = torch.randn_like(x) if t > 0 else torch.zeros_like(x) + return model_mean + noise * (0.5 * model_log_variance).exp() + + @torch.no_grad() + def p_sample_loop(self, x_in,connection, continous=False): + device = self.betas.device + q = 0 + + sample_inter = (1 | (self.num_timesteps // 10)) + + img = self.receive_tensor(connection) + x = self.receive_tensor(connection) + print("img.shape,x.shape---",img.shape, x.shape) + + if not self.conditional: + # shape = x_in + shape =x + print(shape)#kk + # img = torch.randn(shape, device=device) + ret_img = img + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='sampling loop time step', + total=self.num_timesteps): + img = self.p_sample(img, i, clip_denoised=True) + if i % sample_inter == 0: + ret_img = torch.cat([ret_img, img], dim=0) + else: + # x = x_in + shape = x.shape + print(shape)#kk + # img = torch.randn(shape, device=device) + ret_img = x + + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='sampling loop time step', + total=self.num_timesteps): + img = self.p_sample(img, i, condition_x=x, clip_denoised=True) + if i % sample_inter == 0: + ret_img = torch.cat([ret_img, img], dim=0) + + # print(ret_img.shape)#kk + # print(ret_img[-1].shape)#kk + + if continous: + return ret_img + else: + return ret_img[-1] + + @torch.no_grad() + def receive_tensor(self,socket): + # 首先读取数据长度 + data_length = int.from_bytes(socket.recv(4), byteorder='big') + # 然后根据数据长度读取数据 + data = b'' + while len(data) < data_length: + packet = socket.recv(data_length - len(data)) + if not packet: + return None + data += packet + buffer = io.BytesIO(data) + tensor = torch.load(buffer) + return tensor + + @torch.no_grad() + def sample(self, batch_size=1, continous=False): + time_size = self.time_size + channels = self.channels + return self.p_sample_loop((batch_size, channels, time_size, time_size), continous) + + @torch.no_grad() + def super_resolution(self, x_in,connection, min_num, max_num, continous=False): + self.min_num = min_num + self.max_num = max_num + return self.p_sample_loop(x_in,connection, continous) + + def q_sample(self, x_start, continuous_sqrt_alpha_cumprod, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + + # random gama + return ( + continuous_sqrt_alpha_cumprod * x_start + + (1 - continuous_sqrt_alpha_cumprod ** 2).sqrt() * noise + ) + + def p_losses(self, x_in, noise=None): + x_start = x_in['HR'] + [b, c, h, w] = x_start.shape + t = np.random.randint(1, self.num_timesteps + 1) + + continuous_sqrt_alpha_cumprod = torch.FloatTensor( + np.random.uniform( + self.sqrt_alphas_cumprod_prev[t - 1], + self.sqrt_alphas_cumprod_prev[t], + size=b + ) + ).to(x_start.device) + + continuous_sqrt_alpha_cumprod = continuous_sqrt_alpha_cumprod.view(b, -1) + + noise = default(noise, lambda: torch.randn_like(x_start)) + + x_noisy = self.q_sample( + x_start=x_start, continuous_sqrt_alpha_cumprod=continuous_sqrt_alpha_cumprod.view(-1, 1, 1, 1), noise=noise) + + if not self.conditional: + x_recon = self.denoise_fn(x_noisy, continuous_sqrt_alpha_cumprod) + else: + x_cat = torch.cat([x_in['SR'], x_noisy], dim=1) + x_recon = self.denoise_fn(x_cat, continuous_sqrt_alpha_cumprod) + + loss = self.loss_func(noise, x_recon) + return loss + + def forward(self, x, *args, **kwargs): + return self.p_losses(x, *args, **kwargs) diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/unet.py b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/unet.py new file mode 100644 index 0000000000000000000000000000000000000000..77159a7aa693782fe91e10ce6d3083fbc5947bf0 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/model/sr3_modules/unet.py @@ -0,0 +1,274 @@ +import math +from inspect import isfunction + +import torch +from torch import nn + + +def exists(x): + return x is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +class PositionalEncoding(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, noise_level): + count = self.dim // 2 + step = torch.arange(count, dtype=noise_level.dtype, + device=noise_level.device) / count + encoding = noise_level.unsqueeze( + 1) * torch.exp(-math.log(1e4) * step.unsqueeze(0)) + encoding = torch.cat( + [torch.sin(encoding), torch.cos(encoding)], dim=-1) + return encoding + + +class FeatureWiseAffine(nn.Module): + def __init__(self, in_channels, out_channels, use_affine_level=False): + super(FeatureWiseAffine, self).__init__() + self.use_affine_level = use_affine_level + self.noise_func = nn.Sequential( + nn.Linear(in_channels, out_channels * (1 + self.use_affine_level)) + ) + + def forward(self, x, noise_embed): + batch = x.shape[0] + if self.use_affine_level: + gamma, beta = self.noise_func(noise_embed).view( + batch, -1, 1, 1).chunk(3, dim=1) + x = (1 + gamma) * x + beta + else: + x = x + self.noise_func(noise_embed).view(batch, -1, 1, 1) + return x + + +class Swish(nn.Module): + def forward(self, x): + return x * torch.sigmoid(x) + + +class Upsample(nn.Module): + def __init__(self, dim): + super().__init__() + self.up = nn.Upsample(scale_factor=2, mode="nearest") + self.conv = nn.Conv2d(dim, dim, 3, padding=1) + + def forward(self, x): + return self.conv(self.up(x)) + + +class Downsample(nn.Module): + def __init__(self, dim): + super().__init__() + self.conv = nn.Conv2d(dim, dim, 3, 2, 1) + + def forward(self, x): + return self.conv(x) + + +# building block modules +class Block(nn.Module): + def __init__(self, dim, dim_out, groups=32, dropout=0): + super().__init__() + self.block = nn.Sequential( + nn.GroupNorm(groups, dim), + Swish(), + nn.Dropout(dropout) if dropout != 0 else nn.Identity(), + nn.Conv2d(dim, dim_out, 3, padding=1) + ) + + def forward(self, x): + return self.block(x) + + +class ResnetBlock(nn.Module): + def __init__(self, dim, dim_out, noise_level_emb_dim=None, dropout=0, use_affine_level=False, norm_groups=32): + super().__init__() + self.noise_func = FeatureWiseAffine( + noise_level_emb_dim, dim_out, use_affine_level) + + self.block1 = Block(dim, dim_out, groups=norm_groups) + self.block2 = Block(dim_out, dim_out, groups=norm_groups, dropout=dropout) + self.res_conv = nn.Conv2d( + dim, dim_out, 1) if dim != dim_out else nn.Identity() + + def forward(self, x, time_emb): + b, c, h, w = x.shape + h = self.block1(x) + h = self.noise_func(h, time_emb) + h = self.block2(h) + return h + self.res_conv(x) + + +class SelfAttention(nn.Module): + def __init__(self, in_channel, n_head=1, norm_groups=32): + super().__init__() + + self.n_head = n_head + + self.norm = nn.GroupNorm(norm_groups, in_channel) + self.qkv = nn.Conv2d(in_channel, in_channel * 3, 1, bias=False) + self.out = nn.Conv2d(in_channel, in_channel, 1) + + def forward(self, input): + batch, channel, height, width = input.shape + n_head = self.n_head + head_dim = channel // n_head + + norm = self.norm(input) + qkv = self.qkv(norm).view(batch, n_head, head_dim * 3, height, width) + query, key, value = qkv.chunk(3, dim=2) # bhdyx + + attn = torch.einsum( + "bnchw, bncyx -> bnhwyx", query, key + ).contiguous() / math.sqrt(channel) + attn = attn.view(batch, n_head, height, width, -1) + attn = torch.softmax(attn, -1) + attn = attn.view(batch, n_head, height, width, height, width) + + out = torch.einsum("bnhwyx, bncyx -> bnchw", attn, value).contiguous() + out = self.out(out.view(batch, channel, height, width)) + + return out + input + + +class ResnetBlocWithAttn(nn.Module): + def __init__(self, dim, dim_out, *, noise_level_emb_dim=None, norm_groups=32, dropout=0, with_attn=False): + super().__init__() + self.with_attn = with_attn + self.res_block = ResnetBlock( + dim, dim_out, noise_level_emb_dim, norm_groups=norm_groups, dropout=dropout) + if with_attn: + self.attn = SelfAttention(dim_out, norm_groups=norm_groups) + + def forward(self, x, time_emb): + x = self.res_block(x, time_emb) + if (self.with_attn): + x = self.attn(x) + return x + + +class UNet(nn.Module): + def __init__( + self, + in_channel=6, + out_channel=3, + inner_channel=32, + norm_groups=32, + channel_mults=(1, 2, 4, 8, 8), + attn_res=(8), + res_blocks=3, + dropout=0, + with_noise_level_emb=True, + time_size=128 + ): + super().__init__() + + if with_noise_level_emb: + noise_level_channel = inner_channel + self.noise_level_mlp = nn.Sequential( + PositionalEncoding(inner_channel), + nn.Linear(inner_channel, inner_channel * 4), + Swish(), + nn.Linear(inner_channel * 4, inner_channel) + ) + else: + noise_level_channel = None + self.noise_level_mlp = None + + num_mults = len(channel_mults) + pre_channel = inner_channel + feat_channels = [pre_channel] + now_res = time_size + downs = [nn.Conv2d(in_channel, inner_channel, + kernel_size=3, padding=1)] + + for ind in range(num_mults): + is_last = (ind == num_mults - 1) + use_attn = (now_res in attn_res) + channel_mult = inner_channel * channel_mults[ind] + + for _ in range(0, res_blocks): + downs.append(ResnetBlocWithAttn( + pre_channel, channel_mult, noise_level_emb_dim=noise_level_channel, norm_groups=norm_groups, + dropout=dropout, with_attn=use_attn)) + feat_channels.append(channel_mult) + pre_channel = channel_mult + if not is_last: + downs.append(Downsample(pre_channel)) + feat_channels.append(pre_channel) + now_res = now_res // 2 + self.downs = nn.ModuleList(downs) + + self.mid = nn.ModuleList([ + ResnetBlocWithAttn(pre_channel, pre_channel, noise_level_emb_dim=noise_level_channel, + norm_groups=norm_groups, + dropout=dropout, with_attn=True), + ResnetBlocWithAttn(pre_channel, pre_channel, noise_level_emb_dim=noise_level_channel, + norm_groups=norm_groups, + dropout=dropout, with_attn=False) + ]) + + ups = [] + for ind in reversed(range(num_mults)): + is_last = (ind < 1) + use_attn = (now_res in attn_res) + channel_mult = inner_channel * channel_mults[ind] + + for _ in range(0, res_blocks + 1): + ups.append(ResnetBlocWithAttn( + pre_channel + feat_channels.pop(), channel_mult, noise_level_emb_dim=noise_level_channel, + norm_groups=norm_groups, + dropout=dropout, with_attn=use_attn)) + pre_channel = channel_mult + if not is_last: + ups.append(Upsample(pre_channel)) + now_res = now_res * 2 + + self.ups = nn.ModuleList(ups) + self.final_conv = Block(pre_channel, default(out_channel, in_channel), groups=norm_groups) + + def forward(self, x, time): + t = self.noise_level_mlp(time) if exists( + self.noise_level_mlp) else None + + feats = [] + for layer in self.downs: + if isinstance(layer, ResnetBlocWithAttn): + x = layer(x, t) + else: + x = layer(x) + feats.append(x) + + for layer in self.mid: + if isinstance(layer, ResnetBlocWithAttn): + x = layer(x, t) + else: + x = layer(x) + + for layer in self.ups: + if isinstance(layer, ResnetBlocWithAttn): + pop_temp = feats.pop() + x_temp = torch.cat((x, pop_temp), dim=1) + x = layer(x_temp, t) + else: + x = layer(x) + + return self.final_conv(x) +#模型组件 +#PositionalEncoding: 位置编码器,用于编码噪声级别。 +#FeatureWiseAffine: 用于特征级别的仿射变换。 +#Swish: Swish 激活函数。 +#Upsample: 上采样模块。 +#Downsample: 下采样模块。 +#Block: 基本的卷积块。 +#ResnetBlock: ResNet 风格的残差块。 +#SelfAttention: 自注意力机制模块,用于提取图像特征中的重要信息。 \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/requirements.txt b/subject1-4/dynamicSplit/02DiffAD-main_high/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..918c9c0a31edb7a74cdce8cced4814813dc67ff5 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/requirements.txt @@ -0,0 +1,7 @@ +torch>=1.12 +torchvision +numpy~=1.23.2 +pandas~=1.5.1 +scikit-learn~=1.1.2 +tqdm~=4.64.1 +tensorboardx~=2.5.1 \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/server2.py b/subject1-4/dynamicSplit/02DiffAD-main_high/server2.py new file mode 100644 index 0000000000000000000000000000000000000000..bc031d5a165f94e7edbb9c3f967e8e980966ca1e --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/server2.py @@ -0,0 +1,44 @@ +import socket +import time +import io +import torch +def receive_tensor(socket): + # 首先读取数据长度 + data_length = int.from_bytes(socket.recv(4), byteorder='big') + # 然后根据数据长度读取数据 + data = b'' + while len(data) < data_length: + packet = socket.recv(data_length - len(data)) + if not packet: + return None + data += packet + buffer = io.BytesIO(data) + tensor = torch.load(buffer) + return tensor + +def main(): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_address = ('', 9999) # 空字符串表示监听所有可用的接口 + server_socket.bind(server_address) + server_socket.listen(1) + + print("等待客户端连接...") + connection, client_address = server_socket.accept() + print(f"客户端 {client_address} 已连接") + + while True: + # data = client_connection.recv(4) + # if len(data) == 4: + # received_int = int.from_bytes(data, byteorder='big') + # print("---------",received_int) + # else: + # print("Received incomplete data.") + img = receive_tensor(connection) + x = receive_tensor(connection) + print("img.shape,x.shape---",img.shape, x.shape) + + connection.close() + server_socket.close() + +if __name__ == "__main__": + main() diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/test_split.py b/subject1-4/dynamicSplit/02DiffAD-main_high/test_split.py new file mode 100644 index 0000000000000000000000000000000000000000..bf821c36365c39191464c3c98d58034a031d6794 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/test_split.py @@ -0,0 +1,163 @@ +import argparse +import logging +import os +import time + +import pandas as pd +import torch +from tensorboardX import SummaryWriter + +import core.logger as Logger +import core.metrics as Metrics +import data as Data +import model as Model +from decimal import Decimal +import sys + +import socket +import time +import io + +def time_test(params, strategy_params, temp_list): + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + opt = params['opt'] + logger = params['logger'] + logger_test = params['logger_test'] + model_epoch = params['model_epoch'] + + diffusion = Model.create_model(opt) + logger.info('Initial Model Finished') + + current_step = diffusion.begin_step + current_epoch = diffusion.begin_epoch + + if opt['path']['resume_state']: + logger.info('Resuming training from epoch: {}, iter: {}.'.format( + current_epoch, current_step)) + + diffusion.set_new_noise_schedule( + opt['model']['beta_schedule'][opt['phase']], schedule_phase=opt['phase']) + + logger.info('Begin Model Evaluation.') + idx = 0 + + all_datas = pd.DataFrame() + sr_datas = pd.DataFrame() + differ_datas = pd.DataFrame() + + result_path = '{}'.format(opt['path']['results']) + os.makedirs(result_path, exist_ok=True) + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_address = ('', 9999) # 空字符串表示监听所有可用的接口 + server_socket.bind(server_address) + server_socket.listen(1) + + print("等待客户端连接...") + connection, client_address = server_socket.accept() + print(f"客户端 {client_address} 已连接") + + for _, test_data in enumerate(test_loader): + # print(test_data['ORI'].shape) + # print(test_data['HR'].shape) + # print(test_data['SR'].shape) + # print(test_data['label'].shape) + # sys.exit (0) + idx += 1 + diffusion.feed_data(test_data) + diffusion.test(connection,continous=False) + visuals = diffusion.get_current_visuals() + + all_data, sr_df, differ_df = Metrics.tensor2allcsv(visuals, params['col_num']) + all_datas = Metrics.merge_all_csv(all_datas, all_data) + sr_datas = Metrics.merge_all_csv(sr_datas, sr_df) + differ_datas = Metrics.merge_all_csv(differ_datas, differ_df) + + # print(idx) + # sys.exit (0) + connection.close() + server_socket.close() + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#new + all_datas = all_datas.reset_index(drop=True) + sr_datas = sr_datas.reset_index(drop=True) + differ_datas = differ_datas.reset_index(drop=True) + + for i in range(params['row_num'], all_datas.shape[0]): + all_datas.drop(index=[i], inplace=True) + sr_datas.drop(index=[i], inplace=True) + differ_datas.drop(index=[i], inplace=True) + + f1,accuracy,precision,recall = Metrics.relabeling_strategy(all_datas, strategy_params) + + temp_f1 = Decimal(f1).quantize(Decimal("0.0000")) + temp_acc = Decimal(accuracy).quantize(Decimal("0.0000")) + temp_prec = Decimal(precision).quantize(Decimal("0.0000")) + temp_rec = Decimal(recall).quantize(Decimal("0.0000")) + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#new + print('F1-score: ', float(temp_f1)) + print('precision: ', float(temp_prec)) + print('accuracy: ', float(temp_acc)) + print('recall: ', float(temp_rec)) + + +# evaluate model performance +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', type=str, default='//02DiffAD-main/config/smd_time_test.json', + help='JSON file for configuration') + parser.add_argument('-p', '--phase', type=str, choices=['train ', 'val', 'test'], + help='Run either train(training) or val(generation)', default='test') + parser.add_argument('-gpu', '--gpu_ids', type=str, default=None) + parser.add_argument('-debug', '-d', action='store_true') + parser.add_argument('-enable_wandb', action='store_true') + parser.add_argument('-log_wandb_ckpt', action='store_true') + parser.add_argument('-log_eval', action='store_true') + + temp_list = [] + model_epoch = 100 + + # parse configs + args = parser.parse_args() + opt = Logger.parse(args, model_epoch) + # Convert to NoneDict, which return None for missing key. + opt = Logger.dict_to_nonedict(opt) + logger_name = 'test' + str(model_epoch) + # logging + Logger.setup_logger(logger_name, opt['path']['log'], 'test', level=logging.INFO) + logger = logging.getLogger('base') + logger.info(Logger.dict2str(opt)) + tb_logger = SummaryWriter(log_dir=opt['path']['tb_logger']) + + #开始测试 + test_set = Data.create_dataset(opt['datasets']['test'], 'test') + + test_loader = Data.create_dataloader(test_set, opt['datasets']['test'], 'test') + logger.info('Initial Dataset Finished') + logger_test = logging.getLogger(logger_name) # test logger + + start_label = opt['model']['beta_schedule']['test']['start_label'] + end_label = opt['model']['beta_schedule']['test']['end_label'] + step_label = opt['model']['beta_schedule']['test']['step_label'] + step_t = opt['model']['beta_schedule']['test']['step_t'] + strategy_params = { + 'start_label': start_label, + 'end_label': end_label, + 'step_label': step_label, + 'step_t': step_t + } + + params = { + 'opt': opt, + 'logger': logger, + 'logger_test': logger_test, + 'model_epoch': model_epoch, + 'row_num': test_set.row_num, + 'col_num': test_set.col_num + } + + time_test(params, strategy_params, temp_list) + logging.shutdown() diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/time_test.py b/subject1-4/dynamicSplit/02DiffAD-main_high/time_test.py new file mode 100644 index 0000000000000000000000000000000000000000..c4f90530e886721500c7f9e4e19184adee962a3b --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/time_test.py @@ -0,0 +1,133 @@ +import argparse +import logging +import os +import time + +import pandas as pd +import torch +from tensorboardX import SummaryWriter + +import core.logger as Logger +import core.metrics as Metrics +import data as Data +import model as Model +from decimal import Decimal + + +def time_test(params, strategy_params, temp_list): + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + opt = params['opt'] + logger = params['logger'] + logger_test = params['logger_test'] + model_epoch = params['model_epoch'] + + diffusion = Model.create_model(opt) + logger.info('Initial Model Finished') + + current_step = diffusion.begin_step + current_epoch = diffusion.begin_epoch + + if opt['path']['resume_state']: + logger.info('Resuming training from epoch: {}, iter: {}.'.format( + current_epoch, current_step)) + + diffusion.set_new_noise_schedule( + opt['model']['beta_schedule'][opt['phase']], schedule_phase=opt['phase']) + + logger.info('Begin Model Evaluation.') + idx = 0 + + all_datas = pd.DataFrame() + sr_datas = pd.DataFrame() + differ_datas = pd.DataFrame() + + result_path = '{}'.format(opt['path']['results']) + os.makedirs(result_path, exist_ok=True) + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + for _, test_data in enumerate(test_loader): + idx += 1 + diffusion.feed_data(test_data) + diffusion.test(continous=False) + visuals = diffusion.get_current_visuals() + + all_data, sr_df, differ_df = Metrics.tensor2allcsv(visuals, params['col_num']) + all_datas = Metrics.merge_all_csv(all_datas, all_data) + sr_datas = Metrics.merge_all_csv(sr_datas, sr_df) + differ_datas = Metrics.merge_all_csv(differ_datas, differ_df) + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + all_datas = all_datas.reset_index(drop=True) + sr_datas = sr_datas.reset_index(drop=True) + differ_datas = differ_datas.reset_index(drop=True) + + for i in range(params['row_num'], all_datas.shape[0]): + all_datas.drop(index=[i], inplace=True) + sr_datas.drop(index=[i], inplace=True) + differ_datas.drop(index=[i], inplace=True) + + f1 = Metrics.relabeling_strategy(all_datas, strategy_params) + temp_f1 = Decimal(f1) + # temp_f1 = Decimal(f1).quantize(Decimal("0.0000")) + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#new + print('F1-score: ', float(temp_f1)) + + +# evaluate model performance +if __name__ == '__main__': + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', type=str, default='//02DiffAD-main/config/msl_time_test.json', + help='JSON file for configuration') + parser.add_argument('-p', '--phase', type=str, choices=['train ', 'val', 'test'], + help='Run either train(training) or val(generation)', default='test') + parser.add_argument('-gpu', '--gpu_ids', type=str, default=None) + parser.add_argument('-debug', '-d', action='store_true') + parser.add_argument('-enable_wandb', action='store_true') + parser.add_argument('-log_wandb_ckpt', action='store_true') + parser.add_argument('-log_eval', action='store_true') + + temp_list = [] + model_epoch = 100 + + # parse configs + args = parser.parse_args() + opt = Logger.parse(args, model_epoch) + # Convert to NoneDict, which return None for missing key. + opt = Logger.dict_to_nonedict(opt) + logger_name = 'test' + str(model_epoch) + # logging + Logger.setup_logger(logger_name, opt['path']['log'], 'test', level=logging.INFO) + logger = logging.getLogger('base') + logger.info(Logger.dict2str(opt)) + tb_logger = SummaryWriter(log_dir=opt['path']['tb_logger']) + + test_set = Data.create_dataset(opt['datasets']['test'], 'test') + + test_loader = Data.create_dataloader(test_set, opt['datasets']['test'], 'test') + logger.info('Initial Dataset Finished') + logger_test = logging.getLogger(logger_name) # test logger + + start_label = opt['model']['beta_schedule']['test']['start_label'] + end_label = opt['model']['beta_schedule']['test']['end_label'] + step_label = opt['model']['beta_schedule']['test']['step_label'] + step_t = opt['model']['beta_schedule']['test']['step_t'] + strategy_params = { + 'start_label': start_label, + 'end_label': end_label, + 'step_label': step_label, + 'step_t': step_t + } + + params = { + 'opt': opt, + 'logger': logger, + 'logger_test': logger_test, + 'model_epoch': model_epoch, + 'row_num': test_set.row_num, + 'col_num': test_set.col_num + } + + time_test(params, strategy_params, temp_list) + logging.shutdown() diff --git a/subject1-4/dynamicSplit/02DiffAD-main_high/time_train.py b/subject1-4/dynamicSplit/02DiffAD-main_high/time_train.py new file mode 100644 index 0000000000000000000000000000000000000000..a1f87e3eb4862cbc7f7a5fea1744280a00aa1d69 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_high/time_train.py @@ -0,0 +1,87 @@ +import argparse +import logging +import math + +import torch +from tensorboardX import SummaryWriter + +import core.logger as Logger +import data as Data +import model as Model + +# train model +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', type=str, default='//02DiffAD-main/config/smd_time_train.json', + help='JSON file for configuration') + parser.add_argument('-p', '--phase', type=str, choices=['train', 'val'], + help='Run either train(training) or val(generation)', default='train') + parser.add_argument('-gpu', '--gpu_ids', type=str, default=None) + parser.add_argument('-debug', '-d', action='store_true') + parser.add_argument('-enable_wandb', action='store_true') + parser.add_argument('-log_wandb_ckpt', action='store_true') + parser.add_argument('-log_eval', action='store_true') + + # parse configs + args = parser.parse_args() + opt = Logger.parse(args) + # Convert to NoneDict, which return None for missing key. + opt = Logger.dict_to_nonedict(opt) + + # logging + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + + Logger.setup_logger(None, opt['path']['log'], + 'train', level=logging.INFO, screen=True) + Logger.setup_logger('val', opt['path']['log'], 'val', level=logging.INFO) + logger = logging.getLogger('base') + logger.info(Logger.dict2str(opt)) + tb_logger = SummaryWriter(log_dir=opt['path']['tb_logger']) + + for phase, dataset_opt in opt['datasets'].items(): + if phase == 'train' and args.phase != 'val': + train_set = Data.create_dataset(dataset_opt, phase) + train_loader = Data.create_dataloader(train_set, dataset_opt, phase) + + logger.info('Initial Dataset Finished') + + diffusion = Model.create_model(opt) + logger.info('Initial Model Finished') + + current_step = diffusion.begin_step + current_epoch = diffusion.begin_epoch + n_epoch = opt['train']['n_epoch'] + + if opt['path']['resume_state']: + logger.info('Resuming training from epoch: {}, iter: {}.'.format( + current_epoch, current_step)) + + diffusion.set_new_noise_schedule( + opt['model']['beta_schedule'][opt['phase']], schedule_phase=opt['phase']) + + save_model_iter = math.ceil(train_set.__len__() / opt['datasets']['train']['batch_size']) + while current_epoch < n_epoch: + current_epoch += 1 + for _, train_data in enumerate(train_loader): + current_step += 1 + if current_epoch > n_epoch: + break + diffusion.feed_data(train_data) + diffusion.optimize_parameters() + # log + if current_epoch % opt['train']['print_freq'] == 0 and current_step % save_model_iter == 0: + logs = diffusion.get_current_log() + message = ' '.format( + current_epoch, current_step) + for k, v in logs.items(): + message += '{:s}: {:.4e} '.format(k, v) + tb_logger.add_scalar(k, v, current_step) + logger.info(message) + + # save model + if current_epoch % opt['train']['save_checkpoint_freq'] == 0 and current_step % save_model_iter == 0: + logger.info('Saving models and training states.') + diffusion.save_network(current_epoch, current_step) + + logger.info('End of training.') diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/client2.py b/subject1-4/dynamicSplit/02DiffAD-main_low/client2.py new file mode 100644 index 0000000000000000000000000000000000000000..10704c093473035cd18001ca77f881d63b2fd83d --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/client2.py @@ -0,0 +1,29 @@ + +import socket +import random +import string +import time +import schedule + + +def establish_connection(ip='100.64.0.28',port=9999): + + + +def sent_data(): + data_size_bytes = 1 * 1024 * 1024 # 发送 1-10MB 的数据 + data = #generate_random_data(data_size_bytes) + print("开始发送数据...") + start_time = time.time() + + client_socket.sendall(data) + end_time = time.time() + elapsed_time = end_time - start_time + + print("数据发送完成") + + client_socket.close() + +if __name__ == "__main__": + + diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/msl_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/msl_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..bb9a3a7cb5db625fe06e0a4d3cc21576345eb283 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/msl_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "MSL_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "//experiments/MSL_TRAIN_16_128_20/checkpoint/E100" + }, + "datasets": { + "test": { + "name": "msl_test", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/msl/msl_test.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 2001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/msl_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/msl_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..8440faa95a4a4e4ff00fc9a39b5b13e4c845eaf6 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/msl_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "MSL_TRAIN", + "phase": "train", + "gpu_ids": [ + 1, + 3 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "msl_train", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/msl/msl_train.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "batch_size": 32, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 100, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/psm_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/psm_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..a78f9daf724cf1e414115df89a1f06b4160272ea --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/psm_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "PSM_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "experiments/PSM_TRAIN_16_128_100/checkpoint/E20" + }, + "datasets": { + "test": { + "name": "psm_test", + "mode": "HR", + "dataroot": "tf_dataset/psm/psm_test.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 2001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/psm_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/psm_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..c9cd489fd11c86ae208fddd76de65ede3aaecacc --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/psm_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "PSM_TRAIN", + "phase": "train", + "gpu_ids": [ + 0, + 1 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "psm_train", + "mode": "HR", + "dataroot": "tf_dataset/psm/psm_train.csv", + "datatype": "time", + "l_resolution": 16, + "r_resolution": 128, + "batch_size": 32, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 100, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 128, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 10, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/smap_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smap_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..c73f7135dddd2ded804a915ee34a58d7c8698f84 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smap_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "SMAP_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "//experiments/SMAP_TRAIN_128_2048_20/checkpoint/E100" + }, + "datasets": { + "test": { + "name": "smap_test", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smap/smap_test.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 3001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/smap_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smap_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..d5d19094abb82daad489a68aee140b9919f832f4 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smap_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "SMAP_TRAIN", + "phase": "train", + "gpu_ids": [ + 1, + 3 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "smap_train", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smap/smap_train.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "batch_size": 32, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 10, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/smd_time_test.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smd_time_test.json new file mode 100644 index 0000000000000000000000000000000000000000..ed8fc90eda21677a83709f755c848e9880477bb8 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smd_time_test.json @@ -0,0 +1,71 @@ +{ + "name": "SMD_TEST", + "phase": "test", + "gpu_ids": [ + 0 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": "./experiments/SMD_TRAIN_128_2048_20/checkpoint/E100" + }, + "datasets": { + "test": { + "name": "smd_test", + "mode": "HR", + "dataroot": "./tf_dataset/smd/smd_test.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + }, + "test": { + "schedule": "linear", + "start_label": 1, + "end_label": 1001, + "step_label": 1, + "step_t": 1000, + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/config/smd_time_train.json b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smd_time_train.json new file mode 100644 index 0000000000000000000000000000000000000000..12e176f8e41c58c944f533031a80e70c84959a04 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/config/smd_time_train.json @@ -0,0 +1,80 @@ +{ + "name": "SMD_TRAIN", + "phase": "train", + "gpu_ids": [ + 0, + 1 + ], + "path": { + "log": "logs", + "tb_logger": "tb_logger", + "results": "results", + "checkpoint": "checkpoint", + "resume_state": null + }, + "datasets": { + "train": { + "name": "smd_train", + "mode": "HR", + "dataroot": "//02DiffAD-main/tf_dataset/smd/smd_train.csv", + "datatype": "time", + "l_resolution": 128, + "r_resolution": 2048, + "batch_size": 8, + "num_workers": 4, + "use_shuffle": false, + "data_len": -1 + } + }, + "model": { + "which_model_G": "sr3", + "finetune_norm": false, + "unet": { + "in_channel": 2, + "out_channel": 1, + "inner_channel": 32, + "norm_groups": 16, + "channel_multiplier": [ + 1, + 2, + 4, + 8, + 16 + ], + "attn_res": [], + "res_blocks": 1, + "dropout": 0 + }, + "beta_schedule": { + "train": { + "schedule": "linear", + "n_timestep": 20, + "linear_start": 1e-6, + "linear_end": 1e-2 + } + }, + "diffusion": { + "time_size": 2048, + "channels": 1, + "conditional": true + } + }, + "train": { + "n_epoch": 100, + "val_freq": 100, + "save_checkpoint_freq": 100, + "print_freq": 10, + "optimizer": { + "type": "adam", + "lr": 3e-6 + }, + "ema_scheduler": { + "step_start_ema": 5000, + "update_ema_every": 1, + "ema_decay": 0.9999 + } + }, + "wandb": { + "project": "distributed_time" + } +} \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/logger.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/logger.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfefb82c3279ac7f0d56de3e6d90c0fdf2c56f14 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/logger.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/logger.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/logger.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a8816259b8e18c7623868a4476e80ed31657e93 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/logger.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/metrics.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/metrics.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5332c5098125c5857c0d4d2ee7398baea8f0cfa3 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/metrics.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/metrics.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/metrics.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1cdad6bde4bcab4d4ddfbbbe7becf97cae141ac4 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/core/__pycache__/metrics.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/core/logger.py b/subject1-4/dynamicSplit/02DiffAD-main_low/core/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..672af1886ba88de1caa889d7d90b649e34279b73 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/core/logger.py @@ -0,0 +1,143 @@ +import json +import logging +import os +from collections import OrderedDict +from datetime import datetime + + +def mkdirs(paths): + if isinstance(paths, str): + os.makedirs(paths, exist_ok=True) + else: + for path in paths: + os.makedirs(path, exist_ok=True) + + +def get_timestamp(): + return datetime.now().strftime('%y%m%d_%H%M%S') + + +def parse(args, model_epoch=None): + phase = args.phase + opt_path = args.config + gpu_ids = args.gpu_ids + enable_wandb = args.enable_wandb + # remove comments starting with '//' + json_str = '' + with open(opt_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.split('//')[0] + '\n' + json_str += line + + opt = json.loads(json_str, object_pairs_hook=OrderedDict) + + # set log directory + if args.debug: + opt['name'] = 'debug_{}'.format(opt['name']) + if opt['phase'] == 'train': + experiments_root = os.path.join( + 'experiments', '{}_{}_{}_{}'.format(opt['name'], opt['datasets']['train']['l_resolution'], + opt['datasets']['train']['r_resolution'], + opt['model']['beta_schedule']['train']['n_timestep'])) + elif opt['phase'] == 'test': + experiments_root = os.path.join( + 'experiments', '{}_{}_{}_{}_{}'.format(opt['name'], opt['datasets']['test']['l_resolution'], + opt['datasets']['test']['r_resolution'], + opt['model']['beta_schedule']['test']['n_timestep'], model_epoch)) + + opt['path']['experiments_root'] = experiments_root + for key, path in opt['path'].items(): + if 'resume' not in key and 'experiments' not in key: + opt['path'][key] = os.path.join(experiments_root, path) + mkdirs(opt['path'][key]) + + opt['phase'] = phase + + # export CUDA_VISIBLE_DEVICES + if gpu_ids is not None: + opt['gpu_ids'] = [int(id) for id in gpu_ids.split(',')] + gpu_list = gpu_ids + else: + gpu_list = ','.join(str(x) for x in opt['gpu_ids']) + os.environ['CUDA_VISIBLE_DEVICES'] = gpu_list + print('export CUDA_VISIBLE_DEVICES=' + gpu_list) + + if len(gpu_list) > 1: + opt['distributed'] = True + else: + opt['distributed'] = False + + # debug + if 'debug' in opt['name']: + opt['train']['print_freq'] = 2 + opt['train']['save_checkpoint_freq'] = 3 + opt['datasets']['train']['batch_size'] = 2 + opt['model']['beta_schedule']['train']['n_timestep'] = 10 + opt['datasets']['train']['data_len'] = 6 + + # W&B Logging + try: + log_wandb_ckpt = args.log_wandb_ckpt + opt['log_wandb_ckpt'] = log_wandb_ckpt + except: + pass + try: + log_eval = args.log_eval + opt['log_eval'] = log_eval + except: + pass + try: + log_infer = args.log_infer + opt['log_infer'] = log_infer + except: + pass + opt['enable_wandb'] = enable_wandb + + return opt + + +class NoneDict(dict): + def __missing__(self, key): + return None + + +# convert to NoneDict, which return None for missing key. +def dict_to_nonedict(opt): + if isinstance(opt, dict): + new_opt = dict() + for key, sub_opt in opt.items(): + new_opt[key] = dict_to_nonedict(sub_opt) + return NoneDict(**new_opt) + elif isinstance(opt, list): + return [dict_to_nonedict(sub_opt) for sub_opt in opt] + else: + return opt + + +def dict2str(opt, indent_l=1): + '''dict to string for logger''' + msg = '' + for k, v in opt.items(): + if isinstance(v, dict): + msg += ' ' * (indent_l * 2) + k + ':[\n' + msg += dict2str(v, indent_l + 1) + msg += ' ' * (indent_l * 2) + ']\n' + else: + msg += ' ' * (indent_l * 2) + k + ': ' + str(v) + '\n' + return msg + + +def setup_logger(logger_name, root, phase, level=logging.INFO, screen=False): + '''set up logger''' + l = logging.getLogger(logger_name) + formatter = logging.Formatter( + '%(asctime)s.%(msecs)03d - %(levelname)s: %(message)s', datefmt='%y-%m-%d %H:%M:%S') + log_file = os.path.join(root, '{}.log'.format(phase)) + fh = logging.FileHandler(log_file, mode='w') + fh.setFormatter(formatter) + l.setLevel(level) + l.addHandler(fh) + if screen: + sh = logging.StreamHandler() + sh.setFormatter(formatter) + l.addHandler(sh) diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/core/metrics.py b/subject1-4/dynamicSplit/02DiffAD-main_low/core/metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..2691ae949afc35227a77dbe1b582e5916ec238bf --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/core/metrics.py @@ -0,0 +1,169 @@ +import numpy as np +import pandas as pd +from sklearn.metrics import f1_score, accuracy_score, recall_score, precision_score, mean_squared_error + + +def squeeze_tensor(tensor): + return tensor.squeeze().cpu() + + +def update_csv_col_name(all_datas): + df = all_datas.copy() + df.columns = [0, 1, 2, 3] + + return df + + +def tensor2allcsv(visuals, col_num): + df = pd.DataFrame() + sr_df = pd.DataFrame(squeeze_tensor(visuals['SR'])) + ori_df = pd.DataFrame(squeeze_tensor(visuals['ORI'])) + lr_df = pd.DataFrame(squeeze_tensor(visuals['LR'])) + inf_df = pd.DataFrame(squeeze_tensor(visuals['INF'])) + + if col_num != 1: + for i in range(col_num, sr_df.shape[1]): + sr_df.drop(labels=i, axis=1, inplace=True) + ori_df.drop(labels=i, axis=1, inplace=True) + lr_df.drop(labels=i, axis=1, inplace=True) + inf_df.drop(labels=i, axis=1, inplace=True) + + df['SR'] = sr_df.mean(axis=1) + df['ORI'] = ori_df.mean(axis=1) + df['LR'] = lr_df.mean(axis=1) + df['INF'] = inf_df.mean(axis=1) + + df['differ'] = (ori_df - sr_df).abs().mean(axis=1) + df['label'] = squeeze_tensor(visuals['label']) + + differ_df = (sr_df - ori_df) + + return df, sr_df, differ_df + + +def merge_all_csv(all_datas, all_data): + all_datas = pd.concat([all_datas, all_data]) + return all_datas + + +def save_csv(data, data_path): + data.to_csv(data_path, index=False) + + +def get_mean(df): + mean = df['value'].astype('float32').mean() + normal_mean = df['value'][df['label'] == 0].astype('float32').mean() + anomaly_mean = df['value'][df['label'] == 1].astype('float32').mean() + + return mean, normal_mean, anomaly_mean + + +def get_val_mean(df): + mean_dict = {} + + ori = 'ORI' + ori_mean = df[ori].astype('float32').mean() + ori_normal_mean = df[ori][df['label'] == 0].astype('float32').mean() + ori_anomaly_mean = df[ori][df['label'] == 1].astype('float32').mean() + + gen_mean = df['SR'].astype('float32').mean() + gen_normal_mean = df['SR'][df['label'] == 0].astype('float32').mean() + gen_anomaly_mean = df['SR'][df['label'] == 1].astype('float32').mean() + + mean_dict['MSE'] = mean_squared_error(df[ori], df['SR']) + + mean_dict['ori_mean'] = ori_mean + mean_dict['ori_normal_mean'] = ori_normal_mean + mean_dict['ori_anomaly_mean'] = ori_anomaly_mean + + mean_dict['gen_mean'] = gen_mean + mean_dict['gen_normal_mean'] = gen_normal_mean + mean_dict['gen_anomaly_mean'] = gen_anomaly_mean + + mean_dict['mean_differ'] = ori_mean - gen_mean + mean_dict['normal_mean_differ'] = ori_normal_mean - gen_normal_mean + mean_dict['anomaly_mean_differ'] = ori_anomaly_mean - gen_anomaly_mean + + mean_dict['ori_no-ano_differ'] = ori_normal_mean - ori_anomaly_mean + mean_dict['ori_mean-no_differ'] = ori_mean - ori_normal_mean + mean_dict['ori_mean-ano_differ'] = ori_mean - ori_anomaly_mean + + mean_dict['gen_no-ano_differ'] = gen_normal_mean - gen_anomaly_mean + mean_dict['gen_mean-no_differ'] = gen_mean - gen_normal_mean + mean_dict['gen_mean-ano_differ'] = gen_mean - gen_anomaly_mean + + return mean_dict + + +def relabeling_strategy(df, params): + y_true = [] + best_N = 0 + best_f1 = -1 + best_thred = 0 + best_predictions = [] + thresholds = np.arange(params['start_label'], params['end_label'], params['step_label']) + + df_sort = df.sort_values(by="differ", ascending=False) + df_sort = df_sort.reset_index(drop=False) + + for t in thresholds: + # if (t - 1) % params['step_t'] == 0: + # print("t: ", t) + y_true, y_pred, thred = predict_labels(df_sort, t) + for i in range(len(y_true)): + if y_pred[i] == 1 and y_true[i] == 1: + j = i - 1 + while j >= 0 and y_true[j] == 1 and y_pred[j] == 0: + y_pred[j] = 1 + j -= 1 + j = i + 1 + while j < len(y_pred) and y_true[j] == 1 and y_pred[j] == 0: + y_pred[j] = 1 + j += 1 + + f1 = calculate_f1(y_true, y_pred) + if f1 > best_f1: + best_f1 = f1 + best_N = t + best_thred = thred + best_predictions = y_pred + + accuracy = calculate_accuracy(y_true, best_predictions) + precision = calculate_precision(y_true, best_predictions) + recall = calculate_recall(y_true, best_predictions) + + return best_f1,accuracy,precision,recall + + +def predict_labels(df_sort, num): + df_sort['pred_label'] = 0 + df_sort.loc[0:num - 1, 'pred_label'] = 1 + thred = df_sort.loc[num - 1, 'differ'] + + df_sort = df_sort.set_index('index') + df_sort = df_sort.sort_index() + + y_true = df_sort['label'].tolist() + y_pred = df_sort['pred_label'].tolist() + + return y_true, y_pred, thred + + +def calculate_accuracy(y_true, y_pred): + accuracy = accuracy_score(y_true, y_pred) + return accuracy + + +def calculate_precision(y_true, y_pred): + precision = precision_score(y_true, y_pred) + return precision + + +def calculate_recall(y_true, y_pred): + recall = recall_score(y_true, y_pred) + return recall + + +def calculate_f1(y_true, y_pred): + f1 = f1_score(y_true, y_pred) + return f1 diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/LRHR_dataset.py b/subject1-4/dynamicSplit/02DiffAD-main_low/data/LRHR_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..b22f92136b774244541be2fbf50193d89e6e1a35 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/data/LRHR_dataset.py @@ -0,0 +1,43 @@ +from torch.utils.data import Dataset + +from data.prepare_time_data import PrepareTimeData + + +class LRHRDataset(Dataset): + def __init__(self, dataroot, datatype, phase, l_resolution=16, r_resolution=128, + split='train', data_len=-1, need_LR=False): + self.datatype = datatype + self.data_len = data_len + self.need_LR = need_LR + self.split = split + self.phase = phase + self.pre_data = PrepareTimeData(data_path=dataroot, phase=phase, base=l_resolution, size=r_resolution) + self.row_num = self.pre_data.get_row_num() + self.col_num = self.pre_data.get_col_num() + + if datatype == 'time': + self.hr_path, self.sr_path, self.labels, self.pre_labels = self.pre_data.get_sr_data() + self.dataset_len = len(self.sr_path) + if self.data_len <= 0: + self.data_len = self.dataset_len + else: + self.data_len = min(self.data_len, self.dataset_len) + else: + raise NotImplementedError( + 'data_type [{:s}] is not recognized.'.format(datatype)) + + def __len__(self): + return self.data_len + + def __getitem__(self, index): + + data_LR = None + data_ORI = self.hr_path[index] + data_HR = self.hr_path[index] + data_SR = self.sr_path[index] + data_label = self.labels[index] + + if self.phase == 'train': + return {'HR': data_HR, 'SR': data_SR, 'Index': index} + else: + return {'ORI': data_ORI, 'HR': data_HR, 'SR': data_SR, 'label': data_label, 'Index': index} diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__init__.py b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9c915ff01b1a408c16e40fb1c5ce6eda3df0e9bc --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__init__.py @@ -0,0 +1,42 @@ +'''create dataset and dataloader''' +import logging + +import torch.utils.data + + +def create_dataloader(dataset, dataset_opt, phase): + '''create dataloader ''' + if phase == 'train': + return torch.utils.data.DataLoader( + dataset, + batch_size=dataset_opt['batch_size'], + shuffle=dataset_opt['use_shuffle'], + num_workers=dataset_opt['num_workers'], + pin_memory=True) + elif phase == 'val': + return torch.utils.data.DataLoader( + dataset, batch_size=1, shuffle=False, num_workers=1, pin_memory=True) + elif phase == 'test': + return torch.utils.data.DataLoader( + dataset, batch_size=1, shuffle=False, num_workers=1, pin_memory=True) + else: + raise NotImplementedError( + 'Dataloader [{:s}] is not found.'.format(phase)) + + +def create_dataset(dataset_opt, phase): + '''create dataset''' + mode = dataset_opt['mode'] + from data.LRHR_dataset import LRHRDataset as D + dataset = D(dataroot=dataset_opt['dataroot'], + datatype=dataset_opt['datatype'], + l_resolution=dataset_opt['l_resolution'], + r_resolution=dataset_opt['r_resolution'], + split=phase, + data_len=dataset_opt['data_len'], + need_LR=(mode == 'LR'), + phase=phase) + logger = logging.getLogger('base') + logger.info('Dataset [{:s} - {:s}] is created.'.format(dataset.__class__.__name__, + dataset_opt['name'])) + return dataset diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/LRHR_dataset.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/LRHR_dataset.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fba63b4d4fed74b4bb401570224523be5d07696d Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/LRHR_dataset.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/LRHR_dataset.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/LRHR_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e39c78c33598a1ab6c337e134f89dfec4d299be Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/LRHR_dataset.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/__init__.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..583fdb411b8bb294ea66cad9f625dff815b054ca Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/__init__.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/__init__.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..290fea552665c003812613a8cd173fc4557cbf5e Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/__init__.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/prepare_time_data.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/prepare_time_data.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d96b52ba38087ebbdf94a7d3f4e1cc3c8c0f7bd4 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/prepare_time_data.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/prepare_time_data.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/prepare_time_data.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..512dafab3324b460ed69a511d6cb0b5e2e15e974 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/data/__pycache__/prepare_time_data.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/data/prepare_time_data.py b/subject1-4/dynamicSplit/02DiffAD-main_low/data/prepare_time_data.py new file mode 100644 index 0000000000000000000000000000000000000000..8252bb2701222d7b3746a09e413d721ef01fd67c --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/data/prepare_time_data.py @@ -0,0 +1,359 @@ +import math +import warnings + +import numpy as np +import pandas as pd +import torch + +warnings.filterwarnings("ignore") + + +class PrepareTimeData: + def __init__(self, data_path, phase, base, size): + self.data_path = data_path + self.phase = phase + self.base = base + self.size = size + + self.data_name = self.data_path.split('/')[-1].split('_')[0] + self.read_dataset(self.data_path, self.data_name) + self.df = self.ori_df.copy() + self.row_num = self.ori_df.shape[0] + self.col_num = self.ori_df.shape[1] + self.mean = self.df.mean(axis=1) + + self.df = self.get_mean_df(self.df) + self.df = self.vertical_merge_df(self.df) + self.df = self.join_together_labels(self.df) + self.df = self.fill_data(self.df) + self.df = self.standardize_data(self.df) + + def get_hr_data(self): + df = self.df.copy() + ori_values, values, labels, pre_labels = self.get_data_by_interval(df) + + return ori_values, values, labels, pre_labels + + def get_sr_data(self): + df = self.df.copy() + ori_values, values, labels, pre_labels = self.get_data_by_insert_normal() + + return ori_values, values, labels, pre_labels + + def get_mean_df(self, df): + df = df.copy() + for col in df.columns: + df[col] = self.mean + return df + + def vertical_merge_df(self, df): + df = df.copy() + two_power = 2 + + if self.col_num < 16: + two_power = 16 + df_temp = pd.DataFrame() + col_count = 0 + for i in range(two_power - self.col_num): + if col_count >= self.col_num: + col_count = 0 + df_temp[i] = df.iloc[:, col_count] + col_count = col_count + 1 + else: + while self.col_num > two_power: + two_power = two_power * 2 + df_temp = df.iloc[:, 0:(two_power - self.col_num)] + + col_name = [] + for i in range(self.col_num): + col_name.append('value_' + str(i)) + + df.columns = col_name + col_name = [] + for i in range(self.col_num, two_power): + col_name.append('value_' + str(i)) + + df_temp.columns = col_name + df = pd.concat([df, df_temp], axis=1) + return df + + def join_together_labels(self, df): + df = df.copy() + + if self.phase == 'train': + df['label'] = 0 + else: + df['label'] = self.test_labels + return df + + def fill_data(self, df): + df = df.copy() + data_end = math.ceil(self.row_num / self.size) * self.size + + for i in range(self.row_num, data_end): + # df = df._append(pd.Series(), ignore_index=True) + df = df.append(pd.Series(), ignore_index=True) + # df = pd.concat([df, pd.Series()], ignore_index=True) + # df = pd.DataFrame(df).append(new_row, ignore_index=True) + # df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True) + + df.fillna(0, inplace=True) + return df + + def read_dataset(self, data_path, data_name): + if data_name.upper().find('MSL') != -1: + cols = [-1] + self.get_dataset(data_path, cols) + elif data_name.upper().find('PSM') != -1: + if self.phase == 'train': + cols = [-1] + self.get_dataset(data_path, cols) + if self.ori_df.columns.__contains__('timestamp_(min)'): + self.ori_df.drop(columns=['timestamp_(min)'], inplace=True) + else: + cols = [-1] + self.get_dataset(data_path, cols) + if self.ori_df.columns.__contains__('timestamp_(min)'): + self.ori_df.drop(columns=['timestamp_(min)'], inplace=True) + self.test_labels.drop(columns=['timestamp_(min)'], inplace=True) + elif data_name.upper().find('SMAP') != -1: + cols = [0, 1, 2, 3, 4, 7, 8, 9, 10, 12, 13, 15, 16, 19, 20] + self.get_dataset(data_path, cols) + elif data_name.upper().find('SMD') != -1: + cols = [0, 1, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 28, 33, 35, 36, 37] + self.get_dataset(data_path, cols) + + def get_dataset(self, data_path, cols): + if self.phase == 'train': + if -1 in cols: + self.ori_df = pd.read_csv(data_path) + else: + self.ori_df = pd.read_csv(data_path, usecols=cols) + else: + if -1 in cols: + self.ori_df = pd.read_csv(data_path) + else: + self.ori_df = pd.read_csv(data_path, usecols=cols) + + test_label_path = self.data_path.replace('_test.csv', '_test_label.csv') + self.test_labels = pd.read_csv(test_label_path) + + def get_data_by_insert_normal(self): + df = pd.DataFrame(columns=['value', 'label']) + df['value'] = self.df['value_0'] + df['label'] = self.df['label'] + + df_pre_label = self.mutation_point(df) + insert_datas = self.insert_normal(df_pre_label) + + ori_values = [] + values = [] + labels = [] + pre_labels = [] + + start_index = 0 + end_index = self.size + + for col in self.df.columns: + if col == 'label': + continue + self.df[col] = insert_datas['value'] + self.df['pre_label'] = insert_datas['pre_label'] + + ori_df = self.vertical_merge_df(self.ori_df) + ori_df = self.fill_data(ori_df) + + for i in range(0, self.df.shape[0], self.size): + insert_data = pd.DataFrame() + ori_value = pd.DataFrame() + + insert_data = pd.concat([insert_data, self.df[start_index: end_index]]) + ori_value = pd.concat([ori_value, ori_df[start_index: end_index]]) + start_index += self.size + end_index += self.size + + value = insert_data.copy().drop(['label', 'pre_label'], axis=1) + label = insert_data['label'] + pre_label = insert_data['pre_label'] + + value = torch.tensor(np.array(value).astype(np.float32)) + label = torch.tensor(np.array(label).astype(np.int64)) + pre_label = torch.tensor(np.array(pre_label).astype(np.int64)) + ori_value = torch.tensor(np.array(ori_value).astype(np.float32)) + + values.append(value.unsqueeze(0)) + labels.append(label) + pre_labels.append(pre_label) + ori_values.append(ori_value.unsqueeze(0)) + + return ori_values, values, labels, pre_labels + + def standardize_data(self, df): + df = df.copy() + name = self.data_path.split('.csv')[0] + print(name, "Points: {}".format(self.row_num)) + df = self.complete_value(df) + + if self.phase != 'train': + anomaly_len = len(df[df['label'] == 1].index.tolist()) + print("Labeled anomalies: {}".format(anomaly_len)) + + return df + + def complete_value(self, df): + + df.fillna(0, inplace=True) + return df + + def get_mutation_point(self, df_pre_label, start_index, end_index, last_size_var): + size_var = df_pre_label['value'][start_index: end_index].var() + label_count = len(df_pre_label[start_index: end_index][df_pre_label['label'] == 1].index.tolist()) + + if last_size_var == 0: + times = 'Nan' + else: + times = size_var / last_size_var + if times < 1 and times != 0: + times = 1 / times + + if times != "Nan" and times >= 10: + df_pre_label['pre_label'][start_index: end_index] = 1 + else: + df_pre_label['pre_label'][start_index: end_index] = 0 + + return size_var + + def mutation_point(self, df): + df_pre_label = df.copy() + df_pre_label['pre_label'] = 0 + + size = 128 + start_index = 0 + end_index = size + all_var = df_pre_label['value'].var() + + last_size_var = 0 + for i in range(int(self.row_num / size)): + last_size_var = self.get_mutation_point(df_pre_label, start_index, end_index, last_size_var) + + start_index += size + end_index += size + + self.get_mutation_point(df_pre_label, start_index, self.row_num - 1, last_size_var) + return df_pre_label + + def get_index(self, indexes): + count = 0 + start_indexes = [] + end_indexes = [] + + if len(indexes) != 0: + count = count + 1 + start_indexes.append(indexes[0]) + + for i in range(1, len(indexes)): + if indexes[i - 1] + 1 != indexes[i]: + count = count + 1 + end_indexes.append(indexes[i - 1]) + start_indexes.append(indexes[i]) + + end_indexes.append(indexes[len(indexes) - 1]) + + return start_indexes, end_indexes, count + + def insert_normal(self, data): + pre_labels = 'pre_label' + + nor_indexes = data[0:self.row_num][data[pre_labels] == 0].index.tolist() + ano_indexes = data[0:self.row_num][data[pre_labels] == 1].index.tolist() + + nor_start_indexes, nor_end_indexes, nor_count = self.get_index(nor_indexes) + ano_start_indexes, ano_end_indexes, ano_count = self.get_index(ano_indexes) + + interval = int(self.size / self.base) + ano_len = 2 + + df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + for i in range(nor_count): + + if nor_end_indexes[i] - nor_start_indexes[i] + 1 < interval: + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + x = range(nor_start_indexes[i], nor_end_indexes[i] + 1) + xp = [nor_start_indexes[i], nor_end_indexes[i]] + fp = [data['value'][nor_start_indexes[i]], data['value'][nor_end_indexes[i]]] + z = np.interp(x, xp, fp) + + temp_df['ind'] = x + temp_df['value'] = z + temp_df['pre_label'] = 0 + df = pd.concat([df, temp_df]) + else: + last_start_x = -1 + start_xs = range(nor_start_indexes[i], nor_end_indexes[i] + 1, interval) + xp = [] + fp = [] + for start_x in start_xs: + if start_x + interval > nor_end_indexes[i]: + last_start_x = start_x + break + + xp.append(start_x) + xp.append(start_x + interval - 1) + + fp.append(data['value'][start_x]) + fp.append(data['value'][start_x + interval - 1]) + + x = range(nor_start_indexes[i], last_start_x) + z = np.interp(x, xp, fp) + + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + temp_df['ind'] = x + temp_df['value'] = z + temp_df['pre_label'] = 0 + df = pd.concat([df, temp_df]) + + if last_start_x != -1: + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + x = range(last_start_x, nor_end_indexes[i] + 1) + xp = [last_start_x, nor_end_indexes[i]] + fp = [data['value'][last_start_x], data['value'][nor_end_indexes[i]]] + z = np.interp(x, xp, fp) + + temp_df['ind'] = x + temp_df['value'] = z + temp_df['pre_label'] = 0 + df = pd.concat([df, temp_df]) + + for i in range(ano_count): + temp_df = pd.DataFrame(columns=['ind', 'value', 'label', 'pre_label']) + + x = range(ano_start_indexes[i] - 1, ano_end_indexes[i] + 2) + xp = [ano_start_indexes[i] - 1, ano_end_indexes[i] + 1] + fp = [data['value'][ano_start_indexes[i] - 1], data['value'][ano_end_indexes[i] + 1]] + z = np.interp(x, xp, fp) + + for j in range(len(x)): + if j == 0 or j == len(x) - 1: + continue + + temp_df.loc[x[j], 'ind'] = x[j] + temp_df.loc[x[j], 'value'] = z[j] + temp_df.loc[x[j], 'pre_label'] = 1 + + df = pd.concat([df, temp_df]) + + df = df.set_index(['ind'], inplace=False).sort_index() + df['label'] = data['label'] + for i in range(self.row_num, data.shape[0]): + df = df.append(pd.Series(), ignore_index=True) + df.fillna(0, inplace=True) + return df + + def get_row_num(self): + return self.row_num + + def get_col_num(self): + return self.col_num diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__init__.py b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0eb77d1c825dcdeceddbc99eddf974a062750626 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__init__.py @@ -0,0 +1,10 @@ +import logging + +logger = logging.getLogger('base') + + +def create_model(opt): + from .model import DDPM as M + m = M(opt) + logger.info('Model [{:s}] is created.'.format(m.__class__.__name__)) + return m diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/__init__.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54bd8d91fd9f3fafe38dfc71311f3954ade614ba Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/__init__.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/__init__.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab45257dcdcb10a5de99202a7f9699e0e3952e25 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/__init__.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/base_model.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/base_model.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ed0058a949bee1f9ad58ff27a2073f075c05450 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/base_model.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/base_model.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/base_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3da8f605c28b8459254af43ffe831b1b9783811d Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/base_model.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/model.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/model.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60f0c607a64793439df223f4e821eedd86c34e14 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/model.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/model.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5b417c9cb376fba19d0425af84e967504b2847c Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/model.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/networks.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/networks.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a44d4d155525f29c7fd852db469999dd4d66b8b Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/networks.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/networks.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/networks.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5b7f298038b9d6c90d587852ba00957106b20b2 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/__pycache__/networks.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/base_model.py b/subject1-4/dynamicSplit/02DiffAD-main_low/model/base_model.py new file mode 100644 index 0000000000000000000000000000000000000000..de1022eb58ad1e4b8f79553282403e843aa04609 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/model/base_model.py @@ -0,0 +1,47 @@ +import torch +import torch.nn as nn + + +class BaseModel(): + def __init__(self, opt): + self.opt = opt + self.device = torch.device( + 'cuda' if opt['gpu_ids'] is not None else 'cpu') + self.begin_step = 0 + self.begin_epoch = 0 + + def feed_data(self, data): + pass + + def optimize_parameters(self): + pass + + def get_current_visuals(self): + pass + + def get_current_losses(self): + pass + + def print_network(self): + pass + + def set_device(self, x): + if isinstance(x, dict): + for key, item in x.items(): + if item is not None: + x[key] = item.to(self.device) + elif isinstance(x, list): + for item in x: + if item is not None: + item = item.to(self.device) + else: + x = x.to(self.device) + return x + + def get_network_description(self, network): + '''Get the string and total parameters of the network''' + if isinstance(network, nn.DataParallel): + network = network.module + s = str(network) + n = sum(map(lambda x: x.numel(), network.parameters())) + return s, n diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/model.py b/subject1-4/dynamicSplit/02DiffAD-main_low/model/model.py new file mode 100644 index 0000000000000000000000000000000000000000..c9c8eaea57f3d0fb75efc8cf275da25742068228 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/model/model.py @@ -0,0 +1,175 @@ +import logging +import os +from collections import OrderedDict + +import torch +import torch.nn as nn + +import model.networks as networks +from .base_model import BaseModel + +logger = logging.getLogger('base') + + +class DDPM(BaseModel): + def __init__(self, opt): + super(DDPM, self).__init__(opt) + netG = networks.define_G(opt) + + self.netG = self.set_device(netG) + self.schedule_phase = None + + # set loss and load resume state + self.set_loss() + self.set_new_noise_schedule( + opt['model']['beta_schedule']['train'], schedule_phase='train') + + if self.opt['phase'] == 'train': + self.netG.train() + if opt['model']['finetune_norm']: + optim_params = [] + for k, v in self.netG.named_parameters(): + v.requires_grad = False + if k.find('transformer') >= 0: + v.requires_grad = True + v.data.zero_() + optim_params.append(v) + logger.info( + 'Params [{:s}] initialized to 0 and will optimize.'.format(k)) + else: + optim_params = list(self.netG.parameters()) + + self.optG = torch.optim.Adam( + optim_params, lr=opt['train']["optimizer"]["lr"]) + self.log_dict = OrderedDict() + self.load_network() + self.print_network() + + def feed_data(self, data): + self.data = self.set_device(data) + + def optimize_parameters(self): + self.optG.zero_grad() + l_pix = self.netG(self.data) + # need to average in multi-gpu + b, c, h, w = self.data['HR'].shape + l_pix = l_pix.sum() / int(b * c * h * w) + + l_pix.backward() + self.optG.step() + + # set log + self.log_dict['l_pix'] = l_pix.item() + + def test(self,client_socket, continous=False): + self.netG.eval() + + with torch.no_grad(): + ori = self.data['ORI'].squeeze() + min_num = ori.min().item() + max_num = ori.max().item() + if isinstance(self.netG, nn.DataParallel): + self.SR = self.netG.module.super_resolution( + self.data['SR'],client_socket, continous=continous, min_num=min_num, max_num=max_num) + else: + self.SR = self.netG.super_resolution( + self.data['SR'],client_socket, continous=continous, min_num=min_num, max_num=max_num) + self.netG.train() + + def sample(self, batch_size=1, continous=False): + self.netG.eval() + with torch.no_grad(): + if isinstance(self.netG, nn.DataParallel): + self.SR = self.netG.module.sample(batch_size, continous) + else: + self.SR = self.netG.sample(batch_size, continous) + self.netG.train() + + def set_loss(self): + if isinstance(self.netG, nn.DataParallel): + self.netG.module.set_loss(self.device) + else: + self.netG.set_loss(self.device) + + def set_new_noise_schedule(self, schedule_opt, schedule_phase='train'): + if self.schedule_phase is None or self.schedule_phase != schedule_phase: + self.schedule_phase = schedule_phase + if isinstance(self.netG, nn.DataParallel): + self.netG.module.set_new_noise_schedule( + schedule_opt, self.device) + else: + self.netG.set_new_noise_schedule(schedule_opt, self.device) + + def get_current_log(self): + return self.log_dict + + def get_current_visuals(self, need_LR=True, sample=False): + out_dict = OrderedDict() + if sample: + out_dict['SAM'] = self.SR.detach().float().cpu() + else: + out_dict['SR'] = self.SR.detach().float().cpu() + out_dict['INF'] = self.data['SR'].detach().float().cpu() + out_dict['ORI'] = self.data['ORI'].detach().float().cpu() + out_dict['HR'] = self.data['HR'].detach().float().cpu() + out_dict['label'] = self.data['label'].detach().cpu() + if need_LR and 'LR' in self.data: + out_dict['LR'] = self.data['LR'].detach().float().cpu() + else: + out_dict['LR'] = out_dict['INF'] + return out_dict + + def print_network(self): + s, n = self.get_network_description(self.netG) + if isinstance(self.netG, nn.DataParallel): + net_struc_str = '{} - {}'.format(self.netG.__class__.__name__, + self.netG.module.__class__.__name__) + else: + net_struc_str = '{}'.format(self.netG.__class__.__name__) + + logger.info( + 'Network G structure: {}, with parameters: {:,d}'.format(net_struc_str, n)) + logger.info(s) + + def save_network(self, epoch, iter_step): + gen_path = os.path.join( + self.opt['path']['checkpoint'], 'E{}_gen.pth'.format(epoch)) + opt_path = os.path.join( + self.opt['path']['checkpoint'], 'E{}_opt.pth'.format(epoch)) + # gen + network = self.netG + if isinstance(self.netG, nn.DataParallel): + network = network.module + state_dict = network.state_dict() + for key, param in state_dict.items(): + state_dict[key] = param.cpu() + torch.save(state_dict, gen_path) + # opt + opt_state = {'epoch': epoch, 'iter': iter_step, + 'scheduler': None, 'optimizer': None} + opt_state['optimizer'] = self.optG.state_dict() + torch.save(opt_state, opt_path) + + logger.info( + 'Saved model in [{:s}] ...'.format(gen_path)) + + def load_network(self): + load_path = self.opt['path']['resume_state'] + if load_path is not None: + logger.info( + 'Loading pretrained model for G [{:s}] ...'.format(load_path)) + gen_path = '{}_gen.pth'.format(load_path) + opt_path = '{}_opt.pth'.format(load_path) + # gen + network = self.netG + if isinstance(self.netG, nn.DataParallel): + network = network.module + network.load_state_dict(torch.load( + gen_path), strict=(not self.opt['model']['finetune_norm'])) + + if self.opt['phase'] == 'train': + # optimizer + opt = torch.load(opt_path) + self.optG.load_state_dict(opt['optimizer']) + self.begin_step = opt['iter'] + self.begin_epoch = opt['epoch'] diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/networks.py b/subject1-4/dynamicSplit/02DiffAD-main_low/model/networks.py new file mode 100644 index 0000000000000000000000000000000000000000..d2769b5477610f43aca87cf485e59c9c1c5e5313 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/model/networks.py @@ -0,0 +1,112 @@ +import functools +import logging + +import torch +import torch.nn as nn +from torch.nn import init + +logger = logging.getLogger('base') + + +# initialize +def weights_init_normal(m, std=0.02): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + init.normal_(m.weight.data, 0.0, std) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('Linear') != -1: + init.normal_(m.weight.data, 0.0, std) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('BatchNorm2d') != -1: + init.normal_(m.weight.data, 1.0, std) # BN also uses norm + init.constant_(m.bias.data, 0.0) + + +def weights_init_kaiming(m, scale=1): + classname = m.__class__.__name__ + if classname.find('Conv2d') != -1: + init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + m.weight.data *= scale + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('Linear') != -1: + init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + m.weight.data *= scale + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('BatchNorm2d') != -1: + init.constant_(m.weight.data, 1.0) + init.constant_(m.bias.data, 0.0) + + +def weights_init_orthogonal(m): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + init.orthogonal_(m.weight.data, gain=1) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('Linear') != -1: + init.orthogonal_(m.weight.data, gain=1) + if m.bias is not None: + m.bias.data.zero_() + elif classname.find('BatchNorm2d') != -1: + init.constant_(m.weight.data, 1.0) + init.constant_(m.bias.data, 0.0) + + +def init_weights(net, init_type='kaiming', scale=1, std=0.02): + # scale for 'kaiming', std for 'normal'. + logger.info('Initialization method [{:s}]'.format(init_type)) + if init_type == 'normal': + weights_init_normal_ = functools.partial(weights_init_normal, std=std) + net.apply(weights_init_normal_) + elif init_type == 'kaiming': + weights_init_kaiming_ = functools.partial( + weights_init_kaiming, scale=scale) + net.apply(weights_init_kaiming_) + elif init_type == 'orthogonal': + net.apply(weights_init_orthogonal) + else: + raise NotImplementedError( + 'initialization method [{:s}] not implemented'.format(init_type)) + + +# define network +def define_G(opt): + model_opt = opt['model'] + if model_opt['which_model_G'] == 'sr3': + from .sr3_modules import diffusion, unet + + if ('norm_groups' not in model_opt['unet']) or model_opt['unet']['norm_groups'] is None: + model_opt['unet']['norm_groups'] = 32 + + model = unet.UNet( + in_channel=model_opt['unet']['in_channel'], + out_channel=model_opt['unet']['out_channel'], + norm_groups=model_opt['unet']['norm_groups'], + inner_channel=model_opt['unet']['inner_channel'], + channel_mults=model_opt['unet']['channel_multiplier'], + attn_res=model_opt['unet']['attn_res'], + res_blocks=model_opt['unet']['res_blocks'], + dropout=model_opt['unet']['dropout'], + time_size=model_opt['diffusion']['time_size'] + ) + + netG = diffusion.GaussianDiffusion( + model, + time_size=model_opt['diffusion']['time_size'], + channels=model_opt['diffusion']['channels'], + loss_type='l1', # L1 or L2 + conditional=model_opt['diffusion']['conditional'], + schedule_opt=model_opt['beta_schedule']['train'] + ) + if opt['phase'] == 'train': + init_weights(netG, init_type='orthogonal') + + if opt['gpu_ids'] and opt['distributed']: + assert torch.cuda.is_available() + netG = nn.DataParallel(netG) + + return netG diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/diffusion.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/diffusion.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4424c1bb5fcb44b0485367b87b7362c398559b36 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/diffusion.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/diffusion.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/diffusion.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be4f35138c6051016821f8bea0216749cb0393f2 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/diffusion.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/unet.cpython-37.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/unet.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ec7a53f62dbe159a3ed1c9d5ed50230e31343e5 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/unet.cpython-37.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/unet.cpython-38.pyc b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/unet.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..914d38468f8770184e50ff0233c2c5f5851c6297 Binary files /dev/null and b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/__pycache__/unet.cpython-38.pyc differ diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/diffusion.py b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/diffusion.py new file mode 100644 index 0000000000000000000000000000000000000000..f10c1098b8adaa95c3efed75be63cb49972c1395 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/diffusion.py @@ -0,0 +1,273 @@ +import math +from functools import partial +from inspect import isfunction + +import numpy as np +import torch +from torch import nn +from tqdm import tqdm + +import io +def _warmup_beta(linear_start, linear_end, n_timestep, warmup_frac): + betas = linear_end * np.ones(n_timestep, dtype=np.float64) + warmup_time = int(n_timestep * warmup_frac) + betas[:warmup_time] = np.linspace( + linear_start, linear_end, warmup_time, dtype=np.float64) + return betas + + +def make_beta_schedule(schedule, n_timestep, linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if schedule == 'quad': + betas = np.linspace(linear_start ** 0.5, linear_end ** 0.5, + n_timestep, dtype=np.float64) ** 2 + elif schedule == 'linear': + betas = np.linspace(linear_start, linear_end, + n_timestep, dtype=np.float64) + elif schedule == 'warmup10': + betas = _warmup_beta(linear_start, linear_end, + n_timestep, 0.1) + elif schedule == 'warmup50': + betas = _warmup_beta(linear_start, linear_end, + n_timestep, 0.5) + elif schedule == 'const': + betas = linear_end * np.ones(n_timestep, dtype=np.float64) + elif schedule == 'jsd': # 1/T, 1/(T-1), 1/(T-2), ..., 1 + betas = 1. / np.linspace(n_timestep, + 1, n_timestep, dtype=np.float64) + elif schedule == "cosine": + timesteps = ( + torch.arange(n_timestep + 1, dtype=torch.float64) / + n_timestep + cosine_s + ) + alphas = timesteps / (1 + cosine_s) * math.pi / 2 + alphas = torch.cos(alphas).pow(2) + alphas = alphas / alphas[0] + betas = 1 - alphas[1:] / alphas[:-1] + betas = betas.clamp(max=0.999) + else: + raise NotImplementedError(schedule) + return betas + + +# gaussian diffusion trainer class +def exists(x): + return x is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +class GaussianDiffusion(nn.Module): + def __init__(self, denoise_fn, time_size, channels=3, loss_type='l1', + conditional=True, schedule_opt=None): + + super().__init__() + self.channels = channels + self.time_size = time_size + self.denoise_fn = denoise_fn + self.loss_type = loss_type + self.conditional = conditional + if schedule_opt is not None: + pass + + def set_loss(self, device): + if self.loss_type == 'l1': + self.loss_func = nn.L1Loss(reduction='sum').to(device) + elif self.loss_type == 'l2': + self.loss_func = nn.MSELoss(reduction='sum').to(device) + else: + raise NotImplementedError() + + def set_new_noise_schedule(self, schedule_opt, device): + to_torch = partial(torch.tensor, dtype=torch.float32, device=device) + + betas = make_beta_schedule( + schedule=schedule_opt['schedule'], + n_timestep=schedule_opt['n_timestep'], + linear_start=schedule_opt['linear_start'], + linear_end=schedule_opt['linear_end']) + + betas = betas.detach().cpu().numpy() if isinstance( + betas, torch.Tensor) else betas + + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + self.sqrt_alphas_cumprod_prev = np.sqrt( + np.append(1., alphas_cumprod)) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', + to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', + to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', + to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', + to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', + to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', + to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + posterior_variance = betas * \ + (1. - alphas_cumprod_prev) / (1. - alphas_cumprod) + # above: equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t) + self.register_buffer('posterior_variance', + to_torch(posterior_variance)) + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + self.register_buffer('posterior_log_variance_clipped', to_torch( + np.log(np.maximum(posterior_variance, 1e-20)))) + self.register_buffer('posterior_mean_coef1', to_torch( + betas * np.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod))) + self.register_buffer('posterior_mean_coef2', to_torch( + (1. - alphas_cumprod_prev) * np.sqrt(alphas) / (1. - alphas_cumprod))) + + def predict_start_from_noise(self, x_t, t, noise): + return self.sqrt_recip_alphas_cumprod[t] * x_t - \ + self.sqrt_recipm1_alphas_cumprod[t] * noise + + def q_posterior(self, x_start, x_t, t): + posterior_mean = self.posterior_mean_coef1[t] * \ + x_start + self.posterior_mean_coef2[t] * x_t + posterior_log_variance_clipped = self.posterior_log_variance_clipped[t] + return posterior_mean, posterior_log_variance_clipped + + def p_mean_variance(self, x, t, clip_denoised: bool, condition_x=None): + batch_size = x.shape[0] + noise_level = torch.FloatTensor( + [self.sqrt_alphas_cumprod_prev[t + 1]]).repeat(batch_size, 1).to(x.device) + if condition_x is not None: + x_temp = torch.cat([condition_x, x], dim=1) + noise = self.denoise_fn(x_temp, noise_level) + x_recon = self.predict_start_from_noise(x, t=t, noise=noise) + else: + x_recon = self.predict_start_from_noise( + x, t=t, noise=self.denoise_fn(x, noise_level)) + + if clip_denoised: + x_recon.clamp_(self.min_num, self.max_num) + + model_mean, posterior_log_variance = self.q_posterior( + x_start=x_recon, x_t=x, t=t) + return model_mean, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, t, clip_denoised=True, condition_x=None): + model_mean, model_log_variance = self.p_mean_variance( + x=x, t=t, clip_denoised=clip_denoised, condition_x=condition_x) + + noise = torch.randn_like(x) if t > 0 else torch.zeros_like(x) + return model_mean + noise * (0.5 * model_log_variance).exp() + + @torch.no_grad() + def p_sample_loop(self, x_in,client_socket, continous=False): + device = self.betas.device + q = 0 + + sample_inter = (1 | (self.num_timesteps // 10)) + if not self.conditional: + shape = x_in + # print(shape)#kk + img = torch.randn(shape, device=device) + ret_img = img + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='sampling loop time step', + total=self.num_timesteps): + img = self.p_sample(img, i, clip_denoised=True) + if i % sample_inter == 0: + ret_img = torch.cat([ret_img, img], dim=0) + else: + x = x_in + shape = x.shape + # print(shape)#kk [1,1,2048,32] + img = torch.randn(shape, device=device) + ret_img = x + + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='sampling loop time step', + total=self.num_timesteps): + img = self.p_sample(img, i, condition_x=x, clip_denoised=True) + if i % sample_inter == 0: + ret_img = torch.cat([ret_img, img], dim=0) + bytes_to_send = i.to_bytes(4, byteorder='big') + self.send_tensor(img, client_socket) + self.send_tensor(x, client_socket) + # print(ret_img.shape)#kk [11,1,2048,32] + # print(ret_img[-1].shape)#kk [1,2048,32] + if continous: + return ret_img + else: + return ret_img[-1] + + def send_tensor(self,tensor, socket): + buffer = io.BytesIO() + torch.save(tensor, buffer) + buffer.seek(0) + data = buffer.read() + # 首先发送数据长度 + socket.sendall(len(data).to_bytes(4, byteorder='big')) + # 然后发送数据 + socket.sendall(data) + + + @torch.no_grad() + def sample(self, batch_size=1, continous=False): + time_size = self.time_size + channels = self.channels + return self.p_sample_loop((batch_size, channels, time_size, time_size), continous) + + @torch.no_grad() + def super_resolution(self, x_in,client_socket, min_num, max_num, continous=False): + self.min_num = min_num + self.max_num = max_num + return self.p_sample_loop(x_in,client_socket, continous) + + def q_sample(self, x_start, continuous_sqrt_alpha_cumprod, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + + # random gama + return ( + continuous_sqrt_alpha_cumprod * x_start + + (1 - continuous_sqrt_alpha_cumprod ** 2).sqrt() * noise + ) + + def p_losses(self, x_in, noise=None): + x_start = x_in['HR'] + [b, c, h, w] = x_start.shape + t = np.random.randint(1, self.num_timesteps + 1) + + continuous_sqrt_alpha_cumprod = torch.FloatTensor( + np.random.uniform( + self.sqrt_alphas_cumprod_prev[t - 1], + self.sqrt_alphas_cumprod_prev[t], + size=b + ) + ).to(x_start.device) + + continuous_sqrt_alpha_cumprod = continuous_sqrt_alpha_cumprod.view(b, -1) + + noise = default(noise, lambda: torch.randn_like(x_start)) + + x_noisy = self.q_sample( + x_start=x_start, continuous_sqrt_alpha_cumprod=continuous_sqrt_alpha_cumprod.view(-1, 1, 1, 1), noise=noise) + + if not self.conditional: + x_recon = self.denoise_fn(x_noisy, continuous_sqrt_alpha_cumprod) + else: + x_cat = torch.cat([x_in['SR'], x_noisy], dim=1) + x_recon = self.denoise_fn(x_cat, continuous_sqrt_alpha_cumprod) + + loss = self.loss_func(noise, x_recon) + return loss + + def forward(self, x, *args, **kwargs): + return self.p_losses(x, *args, **kwargs) diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/unet.py b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/unet.py new file mode 100644 index 0000000000000000000000000000000000000000..77159a7aa693782fe91e10ce6d3083fbc5947bf0 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/model/sr3_modules/unet.py @@ -0,0 +1,274 @@ +import math +from inspect import isfunction + +import torch +from torch import nn + + +def exists(x): + return x is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +class PositionalEncoding(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, noise_level): + count = self.dim // 2 + step = torch.arange(count, dtype=noise_level.dtype, + device=noise_level.device) / count + encoding = noise_level.unsqueeze( + 1) * torch.exp(-math.log(1e4) * step.unsqueeze(0)) + encoding = torch.cat( + [torch.sin(encoding), torch.cos(encoding)], dim=-1) + return encoding + + +class FeatureWiseAffine(nn.Module): + def __init__(self, in_channels, out_channels, use_affine_level=False): + super(FeatureWiseAffine, self).__init__() + self.use_affine_level = use_affine_level + self.noise_func = nn.Sequential( + nn.Linear(in_channels, out_channels * (1 + self.use_affine_level)) + ) + + def forward(self, x, noise_embed): + batch = x.shape[0] + if self.use_affine_level: + gamma, beta = self.noise_func(noise_embed).view( + batch, -1, 1, 1).chunk(3, dim=1) + x = (1 + gamma) * x + beta + else: + x = x + self.noise_func(noise_embed).view(batch, -1, 1, 1) + return x + + +class Swish(nn.Module): + def forward(self, x): + return x * torch.sigmoid(x) + + +class Upsample(nn.Module): + def __init__(self, dim): + super().__init__() + self.up = nn.Upsample(scale_factor=2, mode="nearest") + self.conv = nn.Conv2d(dim, dim, 3, padding=1) + + def forward(self, x): + return self.conv(self.up(x)) + + +class Downsample(nn.Module): + def __init__(self, dim): + super().__init__() + self.conv = nn.Conv2d(dim, dim, 3, 2, 1) + + def forward(self, x): + return self.conv(x) + + +# building block modules +class Block(nn.Module): + def __init__(self, dim, dim_out, groups=32, dropout=0): + super().__init__() + self.block = nn.Sequential( + nn.GroupNorm(groups, dim), + Swish(), + nn.Dropout(dropout) if dropout != 0 else nn.Identity(), + nn.Conv2d(dim, dim_out, 3, padding=1) + ) + + def forward(self, x): + return self.block(x) + + +class ResnetBlock(nn.Module): + def __init__(self, dim, dim_out, noise_level_emb_dim=None, dropout=0, use_affine_level=False, norm_groups=32): + super().__init__() + self.noise_func = FeatureWiseAffine( + noise_level_emb_dim, dim_out, use_affine_level) + + self.block1 = Block(dim, dim_out, groups=norm_groups) + self.block2 = Block(dim_out, dim_out, groups=norm_groups, dropout=dropout) + self.res_conv = nn.Conv2d( + dim, dim_out, 1) if dim != dim_out else nn.Identity() + + def forward(self, x, time_emb): + b, c, h, w = x.shape + h = self.block1(x) + h = self.noise_func(h, time_emb) + h = self.block2(h) + return h + self.res_conv(x) + + +class SelfAttention(nn.Module): + def __init__(self, in_channel, n_head=1, norm_groups=32): + super().__init__() + + self.n_head = n_head + + self.norm = nn.GroupNorm(norm_groups, in_channel) + self.qkv = nn.Conv2d(in_channel, in_channel * 3, 1, bias=False) + self.out = nn.Conv2d(in_channel, in_channel, 1) + + def forward(self, input): + batch, channel, height, width = input.shape + n_head = self.n_head + head_dim = channel // n_head + + norm = self.norm(input) + qkv = self.qkv(norm).view(batch, n_head, head_dim * 3, height, width) + query, key, value = qkv.chunk(3, dim=2) # bhdyx + + attn = torch.einsum( + "bnchw, bncyx -> bnhwyx", query, key + ).contiguous() / math.sqrt(channel) + attn = attn.view(batch, n_head, height, width, -1) + attn = torch.softmax(attn, -1) + attn = attn.view(batch, n_head, height, width, height, width) + + out = torch.einsum("bnhwyx, bncyx -> bnchw", attn, value).contiguous() + out = self.out(out.view(batch, channel, height, width)) + + return out + input + + +class ResnetBlocWithAttn(nn.Module): + def __init__(self, dim, dim_out, *, noise_level_emb_dim=None, norm_groups=32, dropout=0, with_attn=False): + super().__init__() + self.with_attn = with_attn + self.res_block = ResnetBlock( + dim, dim_out, noise_level_emb_dim, norm_groups=norm_groups, dropout=dropout) + if with_attn: + self.attn = SelfAttention(dim_out, norm_groups=norm_groups) + + def forward(self, x, time_emb): + x = self.res_block(x, time_emb) + if (self.with_attn): + x = self.attn(x) + return x + + +class UNet(nn.Module): + def __init__( + self, + in_channel=6, + out_channel=3, + inner_channel=32, + norm_groups=32, + channel_mults=(1, 2, 4, 8, 8), + attn_res=(8), + res_blocks=3, + dropout=0, + with_noise_level_emb=True, + time_size=128 + ): + super().__init__() + + if with_noise_level_emb: + noise_level_channel = inner_channel + self.noise_level_mlp = nn.Sequential( + PositionalEncoding(inner_channel), + nn.Linear(inner_channel, inner_channel * 4), + Swish(), + nn.Linear(inner_channel * 4, inner_channel) + ) + else: + noise_level_channel = None + self.noise_level_mlp = None + + num_mults = len(channel_mults) + pre_channel = inner_channel + feat_channels = [pre_channel] + now_res = time_size + downs = [nn.Conv2d(in_channel, inner_channel, + kernel_size=3, padding=1)] + + for ind in range(num_mults): + is_last = (ind == num_mults - 1) + use_attn = (now_res in attn_res) + channel_mult = inner_channel * channel_mults[ind] + + for _ in range(0, res_blocks): + downs.append(ResnetBlocWithAttn( + pre_channel, channel_mult, noise_level_emb_dim=noise_level_channel, norm_groups=norm_groups, + dropout=dropout, with_attn=use_attn)) + feat_channels.append(channel_mult) + pre_channel = channel_mult + if not is_last: + downs.append(Downsample(pre_channel)) + feat_channels.append(pre_channel) + now_res = now_res // 2 + self.downs = nn.ModuleList(downs) + + self.mid = nn.ModuleList([ + ResnetBlocWithAttn(pre_channel, pre_channel, noise_level_emb_dim=noise_level_channel, + norm_groups=norm_groups, + dropout=dropout, with_attn=True), + ResnetBlocWithAttn(pre_channel, pre_channel, noise_level_emb_dim=noise_level_channel, + norm_groups=norm_groups, + dropout=dropout, with_attn=False) + ]) + + ups = [] + for ind in reversed(range(num_mults)): + is_last = (ind < 1) + use_attn = (now_res in attn_res) + channel_mult = inner_channel * channel_mults[ind] + + for _ in range(0, res_blocks + 1): + ups.append(ResnetBlocWithAttn( + pre_channel + feat_channels.pop(), channel_mult, noise_level_emb_dim=noise_level_channel, + norm_groups=norm_groups, + dropout=dropout, with_attn=use_attn)) + pre_channel = channel_mult + if not is_last: + ups.append(Upsample(pre_channel)) + now_res = now_res * 2 + + self.ups = nn.ModuleList(ups) + self.final_conv = Block(pre_channel, default(out_channel, in_channel), groups=norm_groups) + + def forward(self, x, time): + t = self.noise_level_mlp(time) if exists( + self.noise_level_mlp) else None + + feats = [] + for layer in self.downs: + if isinstance(layer, ResnetBlocWithAttn): + x = layer(x, t) + else: + x = layer(x) + feats.append(x) + + for layer in self.mid: + if isinstance(layer, ResnetBlocWithAttn): + x = layer(x, t) + else: + x = layer(x) + + for layer in self.ups: + if isinstance(layer, ResnetBlocWithAttn): + pop_temp = feats.pop() + x_temp = torch.cat((x, pop_temp), dim=1) + x = layer(x_temp, t) + else: + x = layer(x) + + return self.final_conv(x) +#模型组件 +#PositionalEncoding: 位置编码器,用于编码噪声级别。 +#FeatureWiseAffine: 用于特征级别的仿射变换。 +#Swish: Swish 激活函数。 +#Upsample: 上采样模块。 +#Downsample: 下采样模块。 +#Block: 基本的卷积块。 +#ResnetBlock: ResNet 风格的残差块。 +#SelfAttention: 自注意力机制模块,用于提取图像特征中的重要信息。 \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/requirements.txt b/subject1-4/dynamicSplit/02DiffAD-main_low/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..918c9c0a31edb7a74cdce8cced4814813dc67ff5 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/requirements.txt @@ -0,0 +1,7 @@ +torch>=1.12 +torchvision +numpy~=1.23.2 +pandas~=1.5.1 +scikit-learn~=1.1.2 +tqdm~=4.64.1 +tensorboardx~=2.5.1 \ No newline at end of file diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/server2.py b/subject1-4/dynamicSplit/02DiffAD-main_low/server2.py new file mode 100644 index 0000000000000000000000000000000000000000..cf89ab24e12848cfc200d5838a7720e1b4686519 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/server2.py @@ -0,0 +1,38 @@ + + +import socket +import time + +def measure_bandwidth(connection): + start_time = time.time() + total_bytes = 0 + + while True: + data = connection.recv(1024) # 接收数据 + if not data: + break + total_bytes += len(data) + + end_time = time.time() + elapsed_time = end_time - start_time + bandwidth_mbps = (total_bytes * 8) / (elapsed_time * 1e6) # 计算带宽,单位为 Mbps + return bandwidth_mbps, elapsed_time + +def task(): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_address = ('', 9999) # 空字符串表示监听所有可用的接口 + server_socket.bind(server_address) + server_socket.listen(1) + while True: + print("等待客户端连接...") + client_connection, client_address = server_socket.accept() + print(f"客户端 {client_address} 已连接") + + bandwidth_mbps, elapsed_time = measure_bandwidth(client_connection) + print(f"测量完成:实际可用带宽为 {bandwidth_mbps:.2f} Mbps,传输时间为 {elapsed_time:.2f} 秒") + + client_connection.close() + server_socket.close() + +if __name__ == "__main__": + task() diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/test_split.py b/subject1-4/dynamicSplit/02DiffAD-main_low/test_split.py new file mode 100644 index 0000000000000000000000000000000000000000..ac406b3a3e8df1c796ef4a13e12a3f725380d668 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/test_split.py @@ -0,0 +1,164 @@ +import argparse +import logging +import os +import time + +import pandas as pd +import torch +from tensorboardX import SummaryWriter + +import core.logger as Logger +import core.metrics as Metrics +import data as Data +import model as Model +from decimal import Decimal +import sys + +import socket +import random +import string +import time + +def time_test(params, strategy_params, temp_list): + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + opt = params['opt'] + logger = params['logger'] + logger_test = params['logger_test'] + model_epoch = params['model_epoch'] + + diffusion = Model.create_model(opt) + logger.info('Initial Model Finished') + + current_step = diffusion.begin_step + current_epoch = diffusion.begin_epoch + + if opt['path']['resume_state']: + logger.info('Resuming training from epoch: {}, iter: {}.'.format( + current_epoch, current_step)) + + diffusion.set_new_noise_schedule( + opt['model']['beta_schedule'][opt['phase']], schedule_phase=opt['phase']) + + logger.info('Begin Model Evaluation.') + idx = 0 + + all_datas = pd.DataFrame() + sr_datas = pd.DataFrame() + differ_datas = pd.DataFrame() + + result_path = '{}'.format(opt['path']['results']) + os.makedirs(result_path, exist_ok=True) + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + + #todo 设置高性能云的 IP 地址和端口号 x.x.x.x 9999 + server_address = ('x.x.x.x', 9999) + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #kk + client_socket.connect(server_address) #kk + + for _, test_data in enumerate(test_loader): + # print(test_data['ORI'].shape) + # print(test_data['HR'].shape) + # print(test_data['SR'].shape) + # print(test_data['label'].shape) + # sys.exit (0) + idx += 1 + diffusion.feed_data(test_data) + diffusion.test(client_socket,continous=False) + + + + visuals = diffusion.get_current_visuals() + + all_data, sr_df, differ_df = Metrics.tensor2allcsv(visuals, params['col_num']) + all_datas = Metrics.merge_all_csv(all_datas, all_data) + sr_datas = Metrics.merge_all_csv(sr_datas, sr_df) + differ_datas = Metrics.merge_all_csv(differ_datas, differ_df) + + client_socket.close() # kk + os.system("pause") # kk + + # print(idx) + # sys.exit (0) + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#new + all_datas = all_datas.reset_index(drop=True) + sr_datas = sr_datas.reset_index(drop=True) + differ_datas = differ_datas.reset_index(drop=True) + + for i in range(params['row_num'], all_datas.shape[0]): + all_datas.drop(index=[i], inplace=True) + sr_datas.drop(index=[i], inplace=True) + differ_datas.drop(index=[i], inplace=True) + + f1,accuracy,precision,recall = Metrics.relabeling_strategy(all_datas, strategy_params) + + temp_f1 = Decimal(f1).quantize(Decimal("0.0000")) + temp_acc = Decimal(accuracy).quantize(Decimal("0.0000")) + temp_prec = Decimal(precision).quantize(Decimal("0.0000")) + temp_rec = Decimal(recall).quantize(Decimal("0.0000")) + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#new + print('F1-score: ', float(temp_f1)) + print('precision: ', float(temp_prec)) + print('accuracy: ', float(temp_acc)) + print('recall: ', float(temp_rec)) + + +# evaluate model performance +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', type=str, default='./config/smd_time_test.json', + help='JSON file for configuration') + #//02DiffAD-main/config/smd_time_test.json + parser.add_argument('-p', '--phase', type=str, choices=['train ', 'val', 'test'], + help='Run either train(training) or val(generation)', default='test') + parser.add_argument('-gpu', '--gpu_ids', type=str, default=None) + parser.add_argument('-debug', '-d', action='store_true') + parser.add_argument('-enable_wandb', action='store_true') + parser.add_argument('-log_wandb_ckpt', action='store_true') + parser.add_argument('-log_eval', action='store_true') + + temp_list = [] + model_epoch = 100 + + # parse configs + args = parser.parse_args() + opt = Logger.parse(args, model_epoch) + # Convert to NoneDict, which return None for missing key. + opt = Logger.dict_to_nonedict(opt) + logger_name = 'test' + str(model_epoch) + # logging + Logger.setup_logger(logger_name, opt['path']['log'], 'test', level=logging.INFO) + logger = logging.getLogger('base') + logger.info(Logger.dict2str(opt)) + tb_logger = SummaryWriter(log_dir=opt['path']['tb_logger']) + + #开始测试 + test_set = Data.create_dataset(opt['datasets']['test'], 'test') + + test_loader = Data.create_dataloader(test_set, opt['datasets']['test'], 'test') + logger.info('Initial Dataset Finished') + logger_test = logging.getLogger(logger_name) # test logger + + start_label = opt['model']['beta_schedule']['test']['start_label'] + end_label = opt['model']['beta_schedule']['test']['end_label'] + step_label = opt['model']['beta_schedule']['test']['step_label'] + step_t = opt['model']['beta_schedule']['test']['step_t'] + strategy_params = { + 'start_label': start_label, + 'end_label': end_label, + 'step_label': step_label, + 'step_t': step_t + } + + params = { + 'opt': opt, + 'logger': logger, + 'logger_test': logger_test, + 'model_epoch': model_epoch, + 'row_num': test_set.row_num, + 'col_num': test_set.col_num + } + + time_test(params, strategy_params, temp_list) + logging.shutdown() diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/time_test.py b/subject1-4/dynamicSplit/02DiffAD-main_low/time_test.py new file mode 100644 index 0000000000000000000000000000000000000000..c4f90530e886721500c7f9e4e19184adee962a3b --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/time_test.py @@ -0,0 +1,133 @@ +import argparse +import logging +import os +import time + +import pandas as pd +import torch +from tensorboardX import SummaryWriter + +import core.logger as Logger +import core.metrics as Metrics +import data as Data +import model as Model +from decimal import Decimal + + +def time_test(params, strategy_params, temp_list): + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + opt = params['opt'] + logger = params['logger'] + logger_test = params['logger_test'] + model_epoch = params['model_epoch'] + + diffusion = Model.create_model(opt) + logger.info('Initial Model Finished') + + current_step = diffusion.begin_step + current_epoch = diffusion.begin_epoch + + if opt['path']['resume_state']: + logger.info('Resuming training from epoch: {}, iter: {}.'.format( + current_epoch, current_step)) + + diffusion.set_new_noise_schedule( + opt['model']['beta_schedule'][opt['phase']], schedule_phase=opt['phase']) + + logger.info('Begin Model Evaluation.') + idx = 0 + + all_datas = pd.DataFrame() + sr_datas = pd.DataFrame() + differ_datas = pd.DataFrame() + + result_path = '{}'.format(opt['path']['results']) + os.makedirs(result_path, exist_ok=True) + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + for _, test_data in enumerate(test_loader): + idx += 1 + diffusion.feed_data(test_data) + diffusion.test(continous=False) + visuals = diffusion.get_current_visuals() + + all_data, sr_df, differ_df = Metrics.tensor2allcsv(visuals, params['col_num']) + all_datas = Metrics.merge_all_csv(all_datas, all_data) + sr_datas = Metrics.merge_all_csv(sr_datas, sr_df) + differ_datas = Metrics.merge_all_csv(differ_datas, differ_df) + + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + all_datas = all_datas.reset_index(drop=True) + sr_datas = sr_datas.reset_index(drop=True) + differ_datas = differ_datas.reset_index(drop=True) + + for i in range(params['row_num'], all_datas.shape[0]): + all_datas.drop(index=[i], inplace=True) + sr_datas.drop(index=[i], inplace=True) + differ_datas.drop(index=[i], inplace=True) + + f1 = Metrics.relabeling_strategy(all_datas, strategy_params) + temp_f1 = Decimal(f1) + # temp_f1 = Decimal(f1).quantize(Decimal("0.0000")) + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#new + print('F1-score: ', float(temp_f1)) + + +# evaluate model performance +if __name__ == '__main__': + print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))#news + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', type=str, default='//02DiffAD-main/config/msl_time_test.json', + help='JSON file for configuration') + parser.add_argument('-p', '--phase', type=str, choices=['train ', 'val', 'test'], + help='Run either train(training) or val(generation)', default='test') + parser.add_argument('-gpu', '--gpu_ids', type=str, default=None) + parser.add_argument('-debug', '-d', action='store_true') + parser.add_argument('-enable_wandb', action='store_true') + parser.add_argument('-log_wandb_ckpt', action='store_true') + parser.add_argument('-log_eval', action='store_true') + + temp_list = [] + model_epoch = 100 + + # parse configs + args = parser.parse_args() + opt = Logger.parse(args, model_epoch) + # Convert to NoneDict, which return None for missing key. + opt = Logger.dict_to_nonedict(opt) + logger_name = 'test' + str(model_epoch) + # logging + Logger.setup_logger(logger_name, opt['path']['log'], 'test', level=logging.INFO) + logger = logging.getLogger('base') + logger.info(Logger.dict2str(opt)) + tb_logger = SummaryWriter(log_dir=opt['path']['tb_logger']) + + test_set = Data.create_dataset(opt['datasets']['test'], 'test') + + test_loader = Data.create_dataloader(test_set, opt['datasets']['test'], 'test') + logger.info('Initial Dataset Finished') + logger_test = logging.getLogger(logger_name) # test logger + + start_label = opt['model']['beta_schedule']['test']['start_label'] + end_label = opt['model']['beta_schedule']['test']['end_label'] + step_label = opt['model']['beta_schedule']['test']['step_label'] + step_t = opt['model']['beta_schedule']['test']['step_t'] + strategy_params = { + 'start_label': start_label, + 'end_label': end_label, + 'step_label': step_label, + 'step_t': step_t + } + + params = { + 'opt': opt, + 'logger': logger, + 'logger_test': logger_test, + 'model_epoch': model_epoch, + 'row_num': test_set.row_num, + 'col_num': test_set.col_num + } + + time_test(params, strategy_params, temp_list) + logging.shutdown() diff --git a/subject1-4/dynamicSplit/02DiffAD-main_low/time_train.py b/subject1-4/dynamicSplit/02DiffAD-main_low/time_train.py new file mode 100644 index 0000000000000000000000000000000000000000..a1f87e3eb4862cbc7f7a5fea1744280a00aa1d69 --- /dev/null +++ b/subject1-4/dynamicSplit/02DiffAD-main_low/time_train.py @@ -0,0 +1,87 @@ +import argparse +import logging +import math + +import torch +from tensorboardX import SummaryWriter + +import core.logger as Logger +import data as Data +import model as Model + +# train model +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', type=str, default='//02DiffAD-main/config/smd_time_train.json', + help='JSON file for configuration') + parser.add_argument('-p', '--phase', type=str, choices=['train', 'val'], + help='Run either train(training) or val(generation)', default='train') + parser.add_argument('-gpu', '--gpu_ids', type=str, default=None) + parser.add_argument('-debug', '-d', action='store_true') + parser.add_argument('-enable_wandb', action='store_true') + parser.add_argument('-log_wandb_ckpt', action='store_true') + parser.add_argument('-log_eval', action='store_true') + + # parse configs + args = parser.parse_args() + opt = Logger.parse(args) + # Convert to NoneDict, which return None for missing key. + opt = Logger.dict_to_nonedict(opt) + + # logging + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + + Logger.setup_logger(None, opt['path']['log'], + 'train', level=logging.INFO, screen=True) + Logger.setup_logger('val', opt['path']['log'], 'val', level=logging.INFO) + logger = logging.getLogger('base') + logger.info(Logger.dict2str(opt)) + tb_logger = SummaryWriter(log_dir=opt['path']['tb_logger']) + + for phase, dataset_opt in opt['datasets'].items(): + if phase == 'train' and args.phase != 'val': + train_set = Data.create_dataset(dataset_opt, phase) + train_loader = Data.create_dataloader(train_set, dataset_opt, phase) + + logger.info('Initial Dataset Finished') + + diffusion = Model.create_model(opt) + logger.info('Initial Model Finished') + + current_step = diffusion.begin_step + current_epoch = diffusion.begin_epoch + n_epoch = opt['train']['n_epoch'] + + if opt['path']['resume_state']: + logger.info('Resuming training from epoch: {}, iter: {}.'.format( + current_epoch, current_step)) + + diffusion.set_new_noise_schedule( + opt['model']['beta_schedule'][opt['phase']], schedule_phase=opt['phase']) + + save_model_iter = math.ceil(train_set.__len__() / opt['datasets']['train']['batch_size']) + while current_epoch < n_epoch: + current_epoch += 1 + for _, train_data in enumerate(train_loader): + current_step += 1 + if current_epoch > n_epoch: + break + diffusion.feed_data(train_data) + diffusion.optimize_parameters() + # log + if current_epoch % opt['train']['print_freq'] == 0 and current_step % save_model_iter == 0: + logs = diffusion.get_current_log() + message = ' '.format( + current_epoch, current_step) + for k, v in logs.items(): + message += '{:s}: {:.4e} '.format(k, v) + tb_logger.add_scalar(k, v, current_step) + logger.info(message) + + # save model + if current_epoch % opt['train']['save_checkpoint_freq'] == 0 and current_step % save_model_iter == 0: + logger.info('Saving models and training states.') + diffusion.save_network(current_epoch, current_step) + + logger.info('End of training.') diff --git a/subject1-4/dynamicSplit/README.md b/subject1-4/dynamicSplit/README.md new file mode 100644 index 0000000000000000000000000000000000000000..28b2fe53a88382e9415d295af20a980f85975765 --- /dev/null +++ b/subject1-4/dynamicSplit/README.md @@ -0,0 +1,69 @@ +# DynamicSplit + + +## Datasets + +1. MSL (Mars Science Laboratory rover) is a public dataset from NASA. You can learn about it + from [Detecting Spacecraft Anomalies Using LSTMs and Nonparametric Dynamic Thresholding](https://arxiv.org/pdf/1802.04431.pdf). +2. SMAP (Soil Moisture Active Passive satellite) also is a public dataset from NASA. You can learn about it + from [Detecting Spacecraft Anomalies Using LSTMs and Nonparametric Dynamic Thresholding](https://arxiv.org/pdf/1802.04431.pdf). +3. SMD (Server Machine Dataset) is a 5-week-long dataset collected from a large Internet company. You can learn about it + from [Robust Anomaly Detection for Multivariate Time Series through Stochastic Recurrent Neural Network ](https://netman.aiops.org/wp-content/uploads/2019/08/OmniAnomaly_camera-ready.pdf). +Please download the dataset mentioned above and place it in the` tf_dataset` folder, e.g., `tf_dataset/msl/msl_test.csv`. + +## DiffAD-Usage + +This DiffAD model was proposed in this original paper, *Imputation-based Time-Series Anomaly Detection with Conditional Weight-Incremental Diffusion Models*. +### Environment + +Install Python 3.8. + +```python +pip install -r requirements.txt +``` + +By default, datasets are placed under the "tf_dataset" folder. If you need to change +the dataset, you can modify the dataset path in the json file in the "config" folder. +Here is an example of modifying the training dataset path: + +```json +"datasets": { + "train|test": { + "dataroot": "tf_dataset/smap/smap_train.csv", + //"dataroot": "tf_dataset/swat/swat_train.csv" + } +}, +``` +In addition, we provide json configuration files for SMAP datasets for reference. + +### Training +Next, we demonstrate using the SMAP dataset. + +#### We use dataset SMAP for training demonstration. + +```python +# Use time_train.py to train the task. +# Edit json files to adjust dataset path, network structure and hyperparameters. +python time_train.py -c config/smap_time_train.json +``` + +### Test +The trained model is placed in "experiments/*/checkpoint/" by default. +If you need to modify this path, you can refer to "config/smap_time_test.json": + +```json +"path": { + "resume_state": "experiments/SMAP_TRAIN_128_2048_100/checkpoint/E100" +}, +``` + +#### We also use dataset SMAP for testing demonstration. + +```python +# Edit json to adjust pretrain model path and dataset_path. +python time_test.py -c config/smap_time_test.json +``` + + +## DynamicSplit-Usage +The `DiffAD-main_low`and `DiffAD-main_high` files should be placed on the low-performing cloud and high-performing cloud, respectively. `test_split.py` shows an example of computational splitting. \ No newline at end of file