MCM: Masked Cell Modeling for Anomaly Detection in Tabular Data(代码解读三)

Notice:此篇文章为代码解读三,内容为模型的评估过程。
Paper来源:点我跳转

评估过程

代码结构布局:

在这里插入图片描述

main.py

import torch
import numpy as np
from Model.Trainer import Trainer
model_config = {
    'dataset_name': 'wbc',
    'data_dim': 30,
    'epochs': 200,
    'learning_rate': 0.05,
    'sche_gamma': 0.98,
    'mask_num': 15,
    'lambda': 5,
    'device': 'cuda:0',
    'data_dir': 'Data/',
    'runs': 1,
    'batch_size': 512, 
    'en_nlayers': 3,
    'de_nlayers': 3,
    'hidden_dim': 256,
    'z_dim': 128,
    'mask_nlayers': 3,
    'random_seed': 42,
    'num_workers': 0
}

if __name__ == "__main__":
    torch.manual_seed(model_config['random_seed'])
    torch.cuda.manual_seed(model_config['random_seed'])
    np.random.seed(model_config['random_seed'])
    if model_config['num_workers'] > 0:
        torch.multiprocessing.set_start_method('spawn')
    result = []
    runs = model_config['runs']
    mse_rauc, mse_ap, mse_f1 = np.zeros(runs), np.zeros(runs), np.zeros(runs)
    for i in range(runs):
        trainer = Trainer(run=i, model_config=model_config)
        trainer.training(model_config['epochs'])
        trainer.evaluate(mse_rauc, mse_ap, mse_f1)
    mean_mse_auc , mean_mse_pr , mean_mse_f1 = np.mean(mse_rauc), np.mean(mse_ap), np.mean(mse_f1)

    print('##########################################################################')
    print("mse: average AUC-ROC: %.4f  average AUC-PR: %.4f"
          % (mean_mse_auc, mean_mse_pr))
    print("mse: average f1: %.4f" % (mean_mse_f1))
    results_name = './results/' + model_config['dataset_name'] + '.txt'

    with open(results_name,'a') as file:
        file.write("epochs: %d lr: %.4f gamma: %.2f masks: %d lambda: %.1f " % (
            model_config['epochs'], model_config['learning_rate'], model_config['sche_gamma'], model_config['mask_num'], model_config['lambda']))
        file.write('\n')
        file.write("de_layer: %d  hidden_dim: %d z_dim: %d mask_layer: %d" % (model_config['de_nlayers'], model_config['hidden_dim'], model_config['z_dim'], model_config['mask_nlayers']))
        file.write('\n')
        file.write("mse: average AUC-ROC: %.4f  average AUC-PR: %.4f average f1: %.4f" % (
            mean_mse_auc, mean_mse_pr, mean_mse_f1))
        file.write('\n')

1.模型训练完,到达trainer.evaluate(mse_rauc, mse_ap, mse_f1),调用evaluate方法,将三个参数 mse_raucmse_apmse_f1 传递进去,注意此处传递的参数是0。

11.mean_mse_auc , mean_mse_pr , mean_mse_f1 = np.mean(mse_rauc), np.mean(mse_ap), np.mean(mse_f1)评估完后,获取指标,求平均,然后把指标打印出来。至此,模型的评估过程完全结束!

utils.py

import csv
import logging
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import average_precision_score, roc_auc_score, precision_recall_fscore_support

def dataLoading(path, byte_num):
    x = []
    labels = []

    with (open(path, 'r')) as data_from:
        csv_reader = csv.reader(data_from)
        for i in csv_reader:
            x.append(i[0:byte_num])
            labels.append(i[byte_num])
    for i in range(len(x)):
        for j in range(byte_num):
            x[i][j] = float(x[i][j])
    for i in range(len(labels)):
        labels[i] = float(labels[i])
    x = np.array(x)
    labels = np.array(labels)
    return x, labels

# score=[200,1];labels=[200,]
def aucPerformance(score, labels):
		
		# 调用roc_auc_score函数计算ROC AUC分数。roc_auc_score 是从sklearn.metrics模块中导入的一个函数,用于计算ROC曲线下的面积。ROC AUC分数是评估分类模型性能的一个重要指标,其值范围从0到1,值越大表示模型的性能越好。
    roc_auc = roc_auc_score(labels, score)
    
    # 用average_precision_score函数计算平均精度分数。average_precision_score 同样是从 sklearn.metrics 模块中导入的,用于计算精确率-召回率曲线下的平均精度。这也是一个用于评估分类模型性能的指标,尤其是在类别不平衡的情况下。
    ap = average_precision_score(labels, score)
    
    # 返回
    return roc_auc, ap

# 计算并返回给定得分和目标标签的 F1 分数。F1 分数是精确率(Precision)和召回率(Recall)的调和平均数,通常用于评估二分类模型的性能。
def F1Performance(score, target):

		# (标签为0)的比率。通过计算 target 中等于0的元素数量除以 target 的总长度来得到这个比率。
    normal_ratio = (target == 0).sum() / len(target)
    
    # 使用np.squeeze 函数移除score中的单维条目,确保score是一个一维数组。如果score是一个形状为[n, 1]的二维数组,np.squeeze将把它转换成形状为 [n] 的一维数组。
    score = np.squeeze(score)
    
    # 使用 np.percentile 函数计算 score 中的百分位数,该百分位数的值等于(标签为0)比率乘以100。这个百分位数用作阈值,以将得分转换为二进制预测。
    threshold = np.percentile(score, 100 * normal_ratio)
    
    # 建一个与 score 长度相同的一维数组 pred,并将其所有元素初始化为0
    pred = np.zeros(len(score))
    
    # 将 score 中大于计算得到的阈值的所有元素对应的 pred 位置设置为1。这相当于根据阈值将得分转换为二进制预测
    pred[score > threshold] = 1
    
    # 调用 precision_recall_fscore_support 函数来计算精确率、召回率和 F1 分数。这个函数来自 sklearn.metrics 模块,并且在这里指定 average='binary' 以计算二分类任务的指标。下划线 _ 用于忽略函数返回的其他值(例如支持度)
    precision, recall, f1, _ = precision_recall_fscore_support(target, pred, average='binary')
    
    # 返回计算得到的 F1 分数
    return f1

def writeResults(name, avg_AUC_ROC, avg_AUC_PR,
                 std_AUC_ROC, std_AUC_PR, path):
    csv_file = open(path, 'a')
    row = name + "," + avg_AUC_ROC + ',' + avg_AUC_PR + ',' + std_AUC_ROC + ',' + std_AUC_PR + "\n"
    csv_file.write(row)

def writeWeights(filename, test_label, test_fea_att_w, test_grad_att_w, print_score):
    fea_name = './att_weights/' + filename + '_fea.csv'
    grad_name = './att_weights/' + filename + '_grad.csv'
    with open(fea_name, "w", encoding="UTF-8", newline="") as csvfile1:
        writer1 = csv.writer(csvfile1)
        fea_list = test_fea_att_w.detach().cpu().numpy()[:300]
        fea_list = np.column_stack((test_label[:300], print_score[:300], fea_list))
        writer1.writerows(fea_list.tolist())
    with open(grad_name, "w", encoding="UTF-8", newline="") as csvfile2:
        writer2 = csv.writer(csvfile2)
        grad_list = test_grad_att_w.detach().cpu().numpy()[:300]
        grad_list = np.column_stack((test_label[:300], print_score[:300], grad_list))
        writer2.writerows(grad_list.tolist())

def find_best_lambda(score1, score2, y_test):
    s1= StandardScaler()
    s2 = StandardScaler()
    score1 = s1.fit_transform(score1)
    score2 = s2.fit_transform(score2)
    lambda_list = np.append(np.arange(0, 1, 0.1),np.arange(1,10,1))
    best_auc = 0
    best_pr = 0
    best_lambda = 0
    for th in lambda_list:
        auc, pr = aucPerformance(score1 + th * score2, y_test)
        if auc + pr > best_auc + best_pr:
            best_auc = auc
            best_pr = pr
            best_lambda = th
    return best_auc, best_pr, best_lambda

def get_logger(filename, verbosity=1, name=None):
    level_dict = {0: logging.DEBUG, 1: logging.INFO, 2: logging.WARNING}
    formatter = logging.Formatter(
        "%(message)s"
    )
    logger = logging.getLogger(name)
    logger.setLevel(level_dict[verbosity])
    fh = logging.FileHandler(filename, "w")
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    sh = logging.StreamHandler()
    sh.setFormatter(formatter)
    logger.addHandler(sh)
    return logger

8.执行aucPerformance,得到roc_auc, ap

10.执行F1Performance,得到f1。返回到第1步。

DataSet

DataLoader.py


MyDataset.py


Model

Loss.py


MaskNets.py


Model.py


Score.py

import torch
import torch.nn as nn

class ScoreFunction(nn.Module):
    def __init__(self, model_config):
        super(ScoreFunction, self).__init__()
        self.mask_num = model_config['mask_num']

		# x_input, x_pred作为参数传入
    def forward(self, x_input, x_pred):
        x_input = x_input.unsqueeze(1).repeat(1, self.mask_num, 1)
        
        # 计算x_pred和x_input之间的差异,得到sub_result
        sub_result = x_pred - x_input
        
        # 使用torch.norm函数计算sub_result在第3维(dim=2)上的2范数,得到每个样本的MSE值。
        mse = torch.norm(sub_result, p=2, dim=2)
        
        # 用 torch.mean 函数计算每个样本的MSE值的平均值,dim=1 表示沿着第2维进行平均(即对于每个样本),keepdim=True 表示保留平均的维度。
        mse_score = torch.mean(mse, dim=1,keepdim=True)
        
        return mse_score

5.解读第一行代码:x_input = x_input.unsqueeze(1).repeat(1, self.mask_num, 1)x_input.unsqueeze(1)扩张第二维度,将[200,30]扩展为[200,1,30].repeat(1, self.mask_num, 1)重复15次。

mse_score.shape
Out[12]: torch.Size([200, 1])

6.执行完毕返回mse_score,回到第四步。

Trainer.py

import torch
import torch.optim as optim
from DataSet.DataLoader import get_dataloader
from Model.Model import MCM
from Model.Loss import LossFunction
from Model.Score import ScoreFunction
from utils import aucPerformance, get_logger, F1Performance

class Trainer(object):
    def __init__(self, run: int, model_config: dict):
        self.run = run
        self.sche_gamma = model_config['sche_gamma']
        self.device = model_config['device']
        self.learning_rate = model_config['learning_rate']
        self.model = MCM(model_config).to(self.device)
        self.loss_fuc = LossFunction(model_config).to(self.device)
        self.score_func = ScoreFunction(model_config).to(self.device)
        self.train_loader, self.test_loader = get_dataloader(model_config)

		# 评估函数
    def evaluate(self, mse_rauc, mse_ap, mse_f1):
		    
		    # 加载一个预先训练好的模型,该模型文件名为‘model.pth’
        model = torch.load('model.pth')
        
        # 将模型设置为评估模式。评估模式下,某些层(如Dropout和BatchNormalization)的行为会与训练模式不同。
        model.eval()
        
        #存储每个批次的MSE分数和对应的真实标签
        mse_score, test_label = [], []
        
        # self.test_loader获取测试数据
        for step, (x_input, y_label) in enumerate(self.test_loader):
		        		        
            x_input = x_input.to(self.device)
            
            # 将x_input进行前向传播,得到重构结果x_pred、编码特征z和遮罩矩阵masks
            x_pred, z, masks = self.model(x_input)
            
            # 计算输入和预测之间的MSE(均方误差)分数
            mse_batch = self.score_func(x_input, x_pred)
            
            # 从计算设备复制回CPU内存
            mse_batch = mse_batch.data.cpu()
            
            # 将每个批次的MSE和y_label分别追加到mse_score和test_label列表中
            mse_score.append(mse_batch)
            test_label.append(y_label)
            
        # 将所有批次的MSE分数和真实标签连接成一个单一的张量,并使用.numpy()方法将张量转换为NumPy数组。
        # mse_score.shape=[14]:(200, 1);test_label.shape=(200,)
        mse_score = torch.cat(mse_score, axis=0).numpy()
        test_label = torch.cat(test_label, axis=0).numpy()
        
        # 调用aucPerformance 函数来计算当前运行(run)的AUC性能指标,并将结果存储在mse_rauc和mse_ap数组的相应位置。 
        mse_rauc[self.run], mse_ap[self.run] = aucPerformance(mse_score, test_label)
        
        # 调用F1Performance函数来计算当前运行(run)的F1分数性能指标,并将结果存储在mse_f1数组的相应位置。
        mse_f1[self.run] = F1Performance(mse_score, test_label)

2.进入evaluate方法,按代码步骤开始执行。执行到for step, (x_input, y_label) in enumerate(self.test_loader):x_inputy_label大小如下所示。由于batchsize设置为512大于样本大小200,所以一个批次就能搞定。y_label中是178个正常标签,22个异常标签。

x_input.shape
Out[54]: torch.Size([200, 30])
y_label.shape
Out[55]: torch.Size([200])
y_label
Out[56]: 
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1.,
        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
        1., 1.])

3.x_pred, z, masks = self.model(x_input)与上一篇训练过程中的执行步骤一模一样,可以结合我前一篇的内容来看,此处不做过多赘述。

4.执行mse_batch = self.score_func(x_input, x_pred),进入score_func方法。走,去Model.Score.py看看。

7.执行到mse_rauc[self.run], mse_ap[self.run] = aucPerformance(mse_score, test_label)时,进入utils.py。用mse_rauc[self.run], mse_ap[self.run]去接收。

9.执行mse_f1[self.run] = F1Performance(mse_score, test_label),进入utils.py

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值