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_rauc
、mse_ap
和 mse_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_input
,y_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
。