vLoong能源AI挑战赛——异常检测赛

比赛链接

比赛介绍

汽车产业正在经历巨大变革,新能源汽车市场规模持续扩大,电池安全问题日益引发重视。 电池异常检测面临着汽车数据质量差,检出率低,误报率高,大量无效报警无法直接自动化运维等问题。

为了更好的检验电池安全问题,比赛通过募集优秀异常检测方案,使用特征提取、参数优化、异常对比等手段对实车数据进行处理,优化异常检测结果,以便更好的应用于车辆预警、故障模式识别等多种场景。

新能源车辆电池的故障检测对于及时发现车辆问题、排除隐患、保护人的生命财产安全有着重要意义。新能源电池的故障是多种多样的,包括热失控、析锂、漏液等,本次比赛数据中包含了多种故障类型,但在数据中统一标注为故障标签“1”,不做进一步的区分。

一般故障检测都会面临故障标签少的问题,在本次比赛中,我们对数据进行了筛选,正常数据和故障数据的比例不是非常悬殊,即便如此,常规的异常检测算法依然会是一个非常好的选择。

电池的数据是一种时序数据,在数据中的‘timestamp’列是该数据的时间戳,理解时序数据的处理方式可能会对异常检测有更好的效果。除此之外,发生异常的数据可能看起来很“正常”,需要提取更多的特征来进行分析。

任务重述

上面内容全部是比赛官网复制粘贴的,下面说一下我自己的理解:

给定一个样本对应的属性,判断这个样本是正样本还是负样本。

样本属性的形状为 8×256 的向量和 1个数值(这一个数值在本项目中未使用)。

方案介绍

  • ~网络结构为 conv1d+linear+sigmoid~(弃用)
  • 网络结构为 linear+linear+sigmoid
  • 数据增强方法为重采样
  • A榜单AUC为0.6824(弃用)
  • A榜单AUC为0.7274

最后!本方案完全使用Paddle,可以大胆在Aistudio上跑!

代码

获取数据

! unzip data/data168245/Train.zip
! unzip data/data168245/Test_A.zip
# 将数据写入txt
import os
with open('train.txt','w') as f:
    for item in os.listdir('Train'):
        if '.pkl' in item:
            f.write('Train/'+item+'\n')
with open('test_A.txt','w') as f:
    for item in os.listdir('Test_A'):
        if '.pkl' in item:
            f.write('Test_A/'+item+'\n')
# 看一下数据展示的内容
import pickle
with open('Train/10034.pkl', 'rb') as f:
    pkl_data = pickle.load(f)
    
pkl_data

对train.txt进行修正

如果不运行以下代码也是可以跑通的,只是效果有点炸裂~

# 第一次跑完之后觉得训练结果非常离谱,回来看看数据分布
import os
fp = open('train_positive.txt','w')
fn = open('train_negative.txt','w')
for item in os.listdir('Train'):
    if '.pkl' in item:
        with open('Train/'+item, 'rb') as fr:
            pkl_data = pickle.load(fr)
            label = pkl_data[1]['label']
            if label == '10':
                fp.write('Train/'+item+'\n')
            else:
                fn.write('Train/'+item+'\n')
fp.close()
fn.close()

# 看一下train_positive和train_negative两个文件的行数:正向数据4000+,负向数据2w+
# 为了达到一个重抽样的状况,直接重写train.txt,手动增加正向样本在训练数据中的参与感
with open('train.txt','w') as fw:
    with open('train_positive.txt','r') as f:
        for item in f.readlines():
            for i in range(10):
                fw.write(item)
    with open('train_negative.txt','r') as f:
        for item in f.readlines():
                fw.write(item)

创建模型和数据读取器

import paddle
import pickle

class MyDateset(paddle.io.Dataset):
    def __init__(self,txt_dir = 'train.txt', mode = 'train'):
        super(MyDateset, self).__init__()

        self.mode = mode
        with open(txt_dir,'r') as f:
            self.file_list = f.readlines()

    def __getitem__(self, index):
        file_dir = self.file_list[index][:-1]

        with open(file_dir, 'rb') as f:
            pkl_data = pickle.load(f)

        data = paddle.to_tensor(pkl_data[0]).astype('float32')
        data = paddle.transpose(data/4096, [1, 0])

        if self.mode == 'train':
            label = pkl_data[1]['label']
            if label == '10':
                label = 1
            else:
                label = 0
            # label = paddle.to_tensor(label).astype('int64')
            label = paddle.to_tensor(label).astype('float32')
        else:
            label = file_dir.split('/')[1]

        return data,label

    def __len__(self):
        return len(self.file_list)

if 1:
    train_dataset=MyDateset('train.txt')

    train_dataloader = paddle.io.DataLoader(
        train_dataset,
        batch_size=16,
        shuffle=True,
        drop_last=False)

    for step, data in enumerate(train_dataloader):
        data, label = data
        print(step, data.shape, label.shape)
        break
0 [16, 8, 256] [16, 1]
class MyNet(paddle.nn.Layer):
    def __init__(self):
        super(MyNet,self).__init__()
        self.fc1 = paddle.nn.Linear(in_features=8*256, out_features=128)
        self.fc2 = paddle.nn.Linear(in_features=128, out_features=1)
        # self.m = paddle.nn.Softmax()
        self.m = paddle.nn.Sigmoid()

    def forward(self,x):
        x = paddle.flatten(x,start_axis = 1)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.m(x)
        return x

if 1:
    paddle.summary(MyNet(),(16, 8, 256))

训练

# 创建模型读取已训练模型
model = MyNet()
if 1:
    try:
        param_dict = paddle.load('model.pdparams')
        model.load_dict(param_dict)
    except:
        print('no such pdparams')
model.train()

# 10epoch大约训练300s
max_epoch=10
scheduler = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=0.0001, T_max=max_epoch)
opt = paddle.optimizer.Adam(learning_rate=scheduler, parameters=model.parameters())

# 创建训练集
train_dataset=MyDateset('train.txt')
train_dataloader = paddle.io.DataLoader(
    train_dataset,
    batch_size=512,
    shuffle=True,
    drop_last=False)

now_step=10
for epoch in range(max_epoch):
    for step, data in enumerate(train_dataloader):
        now_step+=1

        data, label = data
        pre = model(data)
        # loss = paddle.nn.functional.cross_entropy(pre,label)
        loss = paddle.nn.functional.mse_loss(pre,label).mean()
        loss.backward()
        opt.step()
        opt.clear_gradients()
        if now_step%100==0:
            print("epoch: {}, batch: {}, loss is: {}".format(epoch, step, loss.mean().numpy()))

paddle.save(model.state_dict(), 'model.pdparams')
epoch: 0, batch: 89, loss is: [0.15248713]
epoch: 1, batch: 51, loss is: [0.13959768]
epoch: 2, batch: 13, loss is: [0.14689168]
epoch: 2, batch: 113, loss is: [0.15777698]
epoch: 3, batch: 75, loss is: [0.14869194]
epoch: 4, batch: 37, loss is: [0.14014928]
epoch: 4, batch: 137, loss is: [0.1519625]
epoch: 5, batch: 99, loss is: [0.1451722]
epoch: 6, batch: 61, loss is: [0.1496178]
epoch: 7, batch: 23, loss is: [0.14905645]
epoch: 7, batch: 123, loss is: [0.14793159]
epoch: 8, batch: 85, loss is: [0.15313964]
epoch: 9, batch: 47, loss is: [0.16298537]

预测

model = MyNet()
try:
    param_dict = paddle.load('model.pdparams')
    model.load_dict(param_dict)
except:
    print('no such pdparams')
model.eval()

test_dataset=MyDateset('test_A.txt',mode = 'test')

result = []
for data, name in test_dataset:
    data = data.reshape([1]+data.shape)
    pre = model(data)
    result.append([name,pre.numpy()[0][-1]])
    # result.append([name,pre.numpy().argmax()])
# 保存预测结果即可提交
import pandas as pd
pd.DataFrame(result,columns=['file_name','score']).to_csv('result.csv',index=None)

结语

本项目在对数据几乎不做太多理性分析的基础下完成了异常检测任务~主要的优势在于!提供了Paddle模型下的运行样例!

可以考虑从以下几个方面进行改进:

  1. 了解不同标签对应的信息,对不同标签设计不同的预处理策略或网络格式
  2. 引入本项目未使用的变量
  3. 引入一些非深度学习方法

祝大家都有好成绩~

此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐