Python数据分析案例24——基于深度学习的锂电池寿命预测

news2025/2/26 14:11:40

本期开始案例较为硬核起来了,适合理工科的硕士,人文社科的同学可以看前面的案例。


案例背景

这篇文章是去年就发了,刊物也印刷了,现在分享一部分代码作为案例给需要的同学。

原文链接(知网文章 C核):

一种基于模态分解和机器学习的锂电池寿命预测方法

锂离子电池剩余使用寿命(RUL)是电池健康管理的一个重要指标。本文采用电池容量作为健康状况的指标,使用模态分解和机器学习算法,提出了一种CEEMDAN-RF-SED-LSTM方法去预测锂电池RUL。

首先采用CEEMDAN分解电池容量数据,为了避免波动分量里的噪音对模型预测能力的影响,且又不完全抛弃波动分量里的特征信息,本工作提出使用随机森林(RF)算法得到每个波动分量的重要性排序和数值,以此作为每个分量对原始数据解释能力的权重。然后将权重值和不同波动分量构建的神经网络模型得到的预测结果进行加权重构,进而得到锂离子电池的RUL预测。

文章对比了单一模型和组合模型预测精度,加入了RF的组合模型预测精度让五种神经网络的表现都有进一步的提升。以NASA数据集作为研究对象进行该方法的性能测试。实验结果表明,CEEMDAN-RF-SED-LSTM模型对电池RUL预测表现效果好,预测结果相比单一模型具有更低的误差。

上面是摘要,原理我就不多介绍了,文章里面都有,这篇博客主要是分享怎么用这些神经网络构建时间序列预测的一个流程。只是部分代码,不是这篇文章的全部代码。

主要是使用模态分解将电池容量退化曲线进行分解,然后使用随机森林回归进行模态分量权重系数的调整,最后用神经网络进行预测后加和,文章里后面的编解码器结构这篇博客是没有。

数据来源

美国航天局NASA的电池数据集,很老了,NASA好像去年下架了这个数据集。但是网上还是有很多获取方式,当然原始数据使用matlab文件储存的,需要进行一定的处理和清洗才能提取出来用。

文章里面是4个电池都进行了测试,这篇博客就以一个电池,B0006的数据作为演示。

深度学习框架 

用的是基于TensorFlow的Keras框架,会简单好上手一下。虽然pytorch在学术界很受欢迎,但是面向对象的编程实在是让编程小白难看得懂。。


代码实现准备

由于是一个较为系统性的文章的代码,所以我这里的代码风格会很分工明确,具有工程性质,而且封装程度很高,为了方便复用,会出现大量的调包和自定义函数,要一定编程思维基础才能看懂,没有前面的案例那么简单的一步一步平铺直述。

导入需要的包

import os
import math
import datetime
import random as rn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams ['font.sans-serif'] ='SimHei'               #显示中文
plt.rcParams ['axes.unicode_minus']=False               #显示负号

from PyEMD import EMD,CEEMDAN,Visualisation 

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

import tensorflow as tf
import keras
from keras.models import Model, Sequential
from keras.layers import GRU, Dense,Conv1D, MaxPooling1D,GlobalMaxPooling1D,Embedding,Dropout,Flatten,SimpleRNN,LSTM
#from keras.callbacks import EarlyStopping
#from tensorflow.keras import regularizers
#from keras.utils.np_utils import to_categorical
from tensorflow.keras  import optimizers

读取数据,进行CEEMDAN模态分解,然后画图查看分解结果:

data0=pd.read_csv('NASA电容量.csv',usecols=['B0006'])
S1 = data0.values
S = S1[:,0]
t = np.arange(0,len(S),1)  
ceemdan=CEEMDAN()
ceemdan.ceemdan(S)
imfs, res = ceemdan.get_imfs_and_residue()
print(len(imfs))
vis = Visualisation()
vis.plot_imfs(imfs=imfs, residue=res, t=t , include_residue=False)

下面的4是表示分解的模态的数量。 

 

 对4条模态进行随机森林回归:

df=pd.DataFrame(imfs.T,columns=['imf'+str(i+1) for i in range(len(imfs))])
df['capacity']=data0.values
X_train=df.iloc[:,:-1]
y_train=df.iloc[:,-1]
model = RandomForestRegressor(n_estimators=5000, max_features=2, random_state=0)
model.fit(X_train, y_train)
model.score(X_train, y_train)

拟合优度99.9% 

画出变量重要性

model.feature_importances_
sorted_index = model.feature_importances_.argsort()
plt.barh(range(X_train.shape[1]), model.feature_importances_[sorted_index])
plt.yticks(np.arange(X_train.shape[1]), X_train.columns[sorted_index])
plt.xlabel('Feature Importance')
plt.ylabel('Feature')
plt.title('Random Forest')
plt.tight_layout()

 

 记录分量名称和重要性:

imf_names=X_train.columns[sorted_index][::-1]
imf_weight=model.feature_importances_[sorted_index][::-1]
imf_weight[0]=1
#imf_names,imf_weight

定义随机数种子函数,误差评价指标计算函数

def set_my_seed():
    os.environ['PYTHONHASHSEED'] = '0'
    np.random.seed(1)
    rn.seed(12345)
    tf.random.set_seed(123)
    
def evaluation(y_test, y_predict):
    mae = mean_absolute_error(y_test, y_predict)
    mse = mean_squared_error(y_test, y_predict)
    rmse = math.sqrt(mean_squared_error(y_test, y_predict))
    mape=(abs(y_predict -y_test)/ y_test).mean()
    return mae, rmse, mape

def relative_error(y_test, y_predict, threshold):
    true_re, pred_re = len(y_test), 0
    for i in range(len(y_test)-1):
        if y_test[i] <= threshold >= y_test[i+1]:
            true_re = i - 1
            break
    for i in range(len(y_predict)-1):
        if y_predict[i] <= threshold:
            pred_re = i - 1
            break
    return abs(true_re - pred_re)/true_re

定义构建序列的函数,从序列数据中获取训练集和测试集对应的解释变量和响应变量

def build_sequences(text, window_size=4):
    #text:list of capacity
    x, y = [],[]
    for i in range(len(text) - window_size):
        sequence = text[i:i+window_size]
        target = text[i+window_size]
        x.append(sequence)
        y.append(target)
    return np.array(x), np.array(y)
def get_traintest(data,train_size=len(data0),window_size=4):
    train=data[:train_size]
    test=data[train_size-window_size:]
    X_train,y_train=build_sequences(train,window_size=window_size)
    X_test,y_test=build_sequences(test)
    return X_train,y_train,X_test,y_test

定义构建模型的函数,还有画出损失图的函数,和拟合效果评价和对比函数:

def build_model(X_train,mode='LSTM',hidden_dim=[32,16]):
    set_my_seed()
    model = Sequential()
    if mode=='RNN':
        #RNN
        model.add(SimpleRNN(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1])))
        model.add(SimpleRNN(hidden_dim[1]))     
        
    elif mode=='MLP':
        model.add(Dense(hidden_dim[0],activation='relu',input_shape=(X_train.shape[-1],)))
        model.add(Dense(hidden_dim[1],activation='relu'))
        
    elif mode=='LSTM':
        # LSTM
        model.add(LSTM(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1])))
        model.add(LSTM(hidden_dim[1]))
    elif mode=='GRU':
        #GRU
        model.add(GRU(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1])))
        model.add(GRU(hidden_dim[1]))
    elif mode=='CNN':
        #一维卷积
        model.add(Conv1D(hidden_dim[0],3,activation='relu',input_shape=(X_train.shape[-2],X_train.shape[-1])))
        model.add(GlobalMaxPooling1D())
        
    model.add(Dense(1))
    model.compile(optimizer='Adam', loss='mse',metrics=[tf.keras.metrics.RootMeanSquaredError(),"mape","mae"])
    return model

def plot_loss(hist,imfname):
    plt.subplots(1,4,figsize=(16,2))
    for i,key in enumerate(hist.history.keys()):
        n=int(str('14')+str(i+1))
        plt.subplot(n)
        plt.plot(hist.history[key], 'k', label=f'Training {key}')
        plt.title(f'{imfname} Training {key}')
        plt.xlabel('Epochs')
        plt.ylabel(key)
        plt.legend()
    plt.tight_layout()
    plt.show()
    
def evaluation_all(df_RFW_eval_all,df_eval_all,mode,Rated_Capacity=2,show_fit=True):
    df_RFW_eval_all['all_pred']=df_RFW_eval_all.iloc[:,1:].sum(axis=1)
    df_eval_all['all_pred']=df_eval_all.iloc[:,1:].sum(axis=1)

    MAE1,RMSE1,MAPE1=evaluation(df_RFW_eval_all['capacity'],df_RFW_eval_all['all_pred'])
    RE1=relative_error(df_RFW_eval_all['capacity'],df_RFW_eval_all['all_pred'],threshold=Rated_Capacity*0.7)

    MAE2,RMSE2,MAPE2=evaluation(df_eval_all['capacity'],df_eval_all['all_pred'])
    RE2=relative_error(df_eval_all['capacity'],df_eval_all['all_pred'],threshold=Rated_Capacity*0.7)

    df_RFW_eval_all.rename(columns={'all_pred':'predict','capacity':'actual'},inplace=True)
    if show_fit:
        df_RFW_eval_all.loc[:,['predict','actual']].plot(figsize=(10,4),title=f'CEEMDAN+RF+{mode}的拟合效果')

    print(f'CEEMDAN+RF+{mode}的效果为mae:{MAE1}, rmse:{RMSE1} ,mape:{MAPE1}, re:{RE1}')
    print(f'CEEMDAN+{mode}的效果为mae:{MAE2}, rmse:{RMSE2} ,mape:{MAPE2}, re:{RE2}')

定义训练函数

def train_fuc(mode='LSTM',window_size=8,batch_size=32,epochs=100,hidden_dim=[32,16],Rated_Capacity=2,show_imf=False,show_loss=True,show_fit=True):
    df_RFW_eval_all=pd.DataFrame(df['capacity'])
    df_eval_all=pd.DataFrame(df['capacity'])
    for i,imfname in  enumerate(imf_names):
 
        print(f'正在处理分量信号:{imfname}')
        data=df[imfname]
        X_train,y_train,X_test,y_test=get_traintest(data.values,window_size=window_size,train_size=len(data))
        if mode!='MLP':
            X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
        #print(X_train.shape, y_train.shape)
        start = datetime.datetime.now()
        set_my_seed()
        model=build_model(X_train=X_train,mode=mode,hidden_dim=hidden_dim)
        hist=model.fit(X_train, y_train,batch_size=batch_size,epochs=epochs,verbose=0)
        if show_loss:
            plot_loss(hist,imfname)
        #预测
        point_list = list(data[:window_size].values.copy())
        y_pred=[]
        while (len(point_list)) < len(data.values):
            x = np.reshape(np.array(point_list[-window_size:]), (-1, window_size)).astype(np.float32)
            pred = model.predict(x) 
            next_point = pred[0,0]
            point_list.append(next_point)#加入原来序列用来继续预测下一个点
            #point_list.append(next_point)#保存输出序列最后一个点的预测值
        y_pred.append(point_list)#保存本次预测所有的预测值
        y_pred=np.array(y_pred).T
        #print(y_pred.shape)
        end = datetime.datetime.now()
        
        if show_imf:
            df_eval=pd.DataFrame()
            df_eval['actual']=data.values
            df_eval['pred']=y_pred
        mae, rmse, mape=evaluation(y_test=data.values, y_predict=y_pred)
        print(f'{imfname}该分量的效果:mae:{mae}, rmse:{rmse} ,mape:{mape}')
        df_eval_all[imfname+'_w_pred']=y_pred
        df_RFW_eval_all[imfname+'_w_pred']=y_pred*imf_weight[i]
        print('============================================================================================================================')
    evaluation_all(df_RFW_eval_all,df_eval_all,mode=mode,Rated_Capacity=Rated_Capacity,show_fit=show_fit)
    print(f'running time is {end-start}')

训练函数是把前面的自定义函数都用上了的,想看懂得把所有自定义函数的功能弄明白。 

初始化参数的值,都是超参数的默认值。

window_size=8
batch_size=16
epochs=100
hidden_dim=[32,16]
Rated_Capacity=2
show_fit=True
show_loss=True
mode='LSTM'  #RNN,GRU,CNN

 window_size 是指滑动序列窗口的大小

batch_size 是批量大小

epochs 是训练轮数

hidden_dim 是神经网络隐藏层的神经元个数

Rated_Capacity 是电池的容量初始值,NASA里面的电池初始值是2

show_fit 是否展示拟合效果图

show_loss 是否展示损失变化图

mode 是神经网络模型类型


模型训练和评价

上面的代码封装了所有的流程,接下来的训练和评价只需要改参数就行了。


LSTM预测

mode='LSTM' 
set_my_seed()
train_fuc(mode=mode,window_size=window_size,batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim,Rated_Capacity=Rated_Capacity)

输出效果如上,会打印每一个分量的训练损失变化,点估计的评价指标,还有最终的加了随机森林和没加随机森林的总体预测效果的评价指标。

(我的anaconda之前重装过一次,环境变了,居然跑不出论文里面的那个数值了....但是差异不大,比如mae,这里是0.039368,论文里面是0.039161,其他指标也差不多)

 

如果想改变其他参数就直接在序列函数里面改就行了,比如想用滑动窗口为16:

train_fuc(window_size=16)

就可以运行得到结果,图太长就不截完了

我的训练函数里面默认的模型是LSTM(因为它效果最好)

想改隐藏层神经元的个数可以这样写:

train_fuc(hidden_dim=[64,32])

很简洁,很方便。


 

 RNN预测

修改mode参数就行

mode='RNN' 
set_my_seed()
train_fuc(mode=mode,window_size=window_size,batch_size=32,epochs=epochs,hidden_dim=hidden_dim,Rated_Capacity=Rated_Capacity)

 图太长就不截完了,只看最后的评价指标计算的结果。(也是一样,由于运行的环境重装过,所以现在的运行结果和我论文里面有细微的差异)

(论文截图)


 

 

 GRU预测

mode='GRU' 
set_my_seed()
train_fuc(mode=mode,window_size=window_size,batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim,Rated_Capacity=Rated_Capacity)

 


一维CNN预测

mode='CNN' 
set_my_seed()
train_fuc(mode=mode,window_size=window_size,batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim,Rated_Capacity=Rated_Capacity)

 


MLP预测

mode='MLP' 
set_my_seed()
train_fuc(mode=mode,window_size=window_size,batch_size=batch_size,epochs=90,hidden_dim=hidden_dim,Rated_Capacity=Rated_Capacity)

 

其他超参数我没太花时间调整,因为神经网络一次运行时间有点长,若有同学有兴趣可以多试试超参数的调整,说不定能得到更好的预测效果。

我文章里面的图片:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/427275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OSPF的优化

O_ASE --- 标志域外路由信息 --- 因为域外的路由信息不可控性较强&#xff0c;所以&#xff0c;信任程度较低&#xff0c;我们将其优先级设置为150。 LSA --- 链路状态通告 --- OSPF协议在不同网络环境下产生的用于携带和传递不同的信息。 LSDB --- 链路状态数据库 SPF --- 最短…

【数据分析实战】基于python对Airbnb房源进行数据分析

文章目录&#x1f4da;引言&#x1f4d6;数据加载以及基本观察&#x1f4c3;缺失值观察及处理&#x1f516;缺失值观察以及可视化&#x1f516;缺失值处理&#x1f4c3;异常值观察及处理&#x1f4d6;数据探索&#x1f4a1;哪个区域的房源最受欢迎&#xff1f;&#x1f4a1;哪种…

基于opencv的边缘检测方法

1、梯度运算 用OpenCV的形态变换&#xff08; 膨胀、腐蚀、开运算和闭运算&#xff09;函数morphologyEx 梯度运算即膨胀结果-腐蚀结果&#xff1a; 【注意】对于二值图像来说&#xff0c;必须是前景图像为白色&#xff0c;背景为黑色&#xff0c;否则需要进行反二值化处理 …

Mybatis【第一个 Mybatis 程序】

目录 一、Maven 环境配置 1、配置 pom.xml 1.1、依赖的 jar包 1.2、防止资源导出失败 2、在resources下编写 Mybatis核心配置文件 二、搭建结构 1、编写mybatis工具类&#xff08;utils&#xff09; 2、编写实体类&#xff08;pojo&#xff09; 3、Mybatis 的实现&…

【Vue3实践】(六)Vue3使用vite处理环境变量、打包部署、nginx配置

文章目录1.前言2.环境变量2.1.环境变量文件(.env)2.2.环境变量变量定义与使用3.打包部署3.1.nginx配置3.2.静态站点根路径配置4.总结1.前言 由于在日常开发中会有一部分前端的开发任务&#xff0c;会涉及到Vue的项目的搭建、迭代、构建发布等操作&#xff0c;所以想系统的学习…

Linux:主机USB设备驱动简析

文章目录1. 前言2. 分析背景3. USB 总线硬件拓扑4. USB 协议栈概览4.1 Linux USB 子系统概览4.2 USB外设(如U盘)固件基础5. Linux USB 子系统初始化6. Linux USB 主机控制器(HCD) 驱动6.1 USB 主机控制器驱动初始化6.2 USB 主机控制器设备对象注册和驱动加载7. Linux USB 设备驱…

Chatgpt接入Csdn:实现自动回复、评论、点赞

背景 起初&#xff0c;我只是想自己弄个工具&#xff0c;用来处理一下大佬们的三连支持&#xff0c;后面我发现大家都在讨论chatgpt&#xff0c;于是我将自动回复和评论消息接入到了Csdn中&#xff0c;不知道这篇文章能不能发出来&#xff0c;代码的话暂时不开源&#xff0c;后…

【从零开始】Docker Desktop:听说你小子要玩我

前言 &#x1f34a;缘由 捡起遗忘的Docker知识 由于本狗近期项目紧任务重&#xff0c;高强度的搬砖导致摸鱼时间下降。在上线项目时&#xff0c;看到运维大神一系列骚操作&#xff0c;dockerk8s的知识如过眼云烟&#xff0c;忘得干净的很。所以想重新恶补一下docker知识&…

深度学习中的卷积神经网络

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

OpenCV实战(17)——FAST特征点检测

OpenCV实战&#xff08;17&#xff09;——FAST特征点检测0. 前言1. FAST 特征点检测2. 自适应特征检测3. 完整代码小结系列链接0. 前言 Harris 算子根据两个垂直方向上的强度变化率给出了角点(或更一般地说&#xff0c;兴趣点)的数学定义。但使用这种定义需要计算图像导数&am…

Android 14 新 API:直接监听截屏操作,不用再观察媒体文件了~

截屏可以说是手机设备最常用的功能了&#xff0c;Android 系统非常重视截屏方面的体验&#xff0c;近几年的更新都不忘去优化这方面的体验。 从一开始仅在通知栏提醒已截屏&#xff0c;到 Android 11 支持在左下角生成截屏缩略图供编辑或分享&#xff0c;再到 Android 12 支持…

计算机图形学 | 变换与观察

计算机图形学 | 变换与观察计算机图形学 | 变换与观察6.1 神奇的齐次坐标回顾几何阶段几何变换平移比例旋转对称错切齐次坐标的引入齐次坐标的概念和相关问题基于齐次坐标的变换6.2 三维模型&#xff0c;动起来&#xff01;基本三维变换平移比例旋转对称错切整体比例变换逆变换…

《计算机网络——自顶向下方法》精炼——1.4到1.7

三更灯火五更鸡&#xff0c;努力学习永不止。无惧困难与挑战&#xff0c;砥砺前行向成功。 文章目录引言正文时延排队时延吞吐量协议层次&#xff0c;服务模型&#xff08;重点&#xff09;封装&#xff08;重点&#xff09;网络安全&#xff08;选看&#xff09;恶意软件的分类…

【数据分析与挖掘】数据预处理

目录概述一、数据清洗1.1 缺失值处理1.1.1 拉格朗日插值法1.1.2 牛顿插值法1.2 异常值处理二、数据集成2.1 实体识别2.2 冗余属性识别三、数据变换3.1 简单函数变换3.2 规范化3.3 连续属性离散化3.4 属性构造3.5 小波变换四、数据规约4.1 属性规约4.2 数值规约概述 数据挖掘过…

Spring Boot中使用Redis

目录 1.依赖 2.依赖关系 3.配置 4.RedisTemplate 5.基础操作 6.事务 1.依赖 maven依赖如下&#xff0c;需要说明的是&#xff0c;spring-boot-starter-data-redis里默认是使用lettuce作为redis客户端的驱动&#xff0c;但是lettuce其实用的比较少&#xff0c;我们常用的…

如何在 Web 实现支持虚拟背景的视频会议

前言 众所周知&#xff0c;市面上有比如飞书会议、腾讯会议等实现视频会议功能的应用&#xff0c;而且随着这几年大环境的影响&#xff0c;远程协作办公越来越成为常态&#xff0c;关于视频会议的应用也会越来越多&#xff0c;且在远程办公的沟通协作中对沟通软件的使用要求会…

ARMv8-A非对齐数据访问支持(Alignment support)

目录 1&#xff0c;对齐传输和非对齐传输 2&#xff0c;AArch32 Alignment support 2.1 Instruction alignment 指令对齐 2.2 Unaligned data access 非对齐数据访问 2.3 SCTLR.A Alignment check enable 3&#xff0c;AArch64 Alignment support 3.1 Instruction align…

Text to image论文精读GigaGAN: 生成对抗网络仍然是文本生成图像的可行选择

GigaGAN是Adobe和卡内基梅隆大学学者们提出的一种新的GAN架构&#xff0c;作者设计了一种新的GAN架构&#xff0c;推理速度、合成高分辨率、扩展性都极其有优势&#xff0c;其证明GAN仍然是文本生成图像的可行选择之一。 文章链接&#xff1a;https://arxiv.org/abs/2303.0551…

大数据周会-本周学习内容总结07

目录 01【hadoop】 1.1【编写集群分发脚本xsync】 1.2【集群部署规划】 1.3【Hadoop集群启停脚本】 02【HDFS】 2.1【HDFS的API操作】 03【MapReduce】 3.1【P077- WordCount案例】 3.2【P097-自定义分区案例】 历史总结 01【hadoop】 1.1【编写集群分发脚本xsync】…

【vue3】关于ref、toRef、toRefs那些事

&#x1f609;博主&#xff1a;初映CY的前说(前端领域) &#x1f4d2;本文核心&#xff1a;ref、toRef、toRefs的使用方法 【前言】我们在上一节的学习当中&#xff0c;使用了reactive()函数将vue3中的数据变成响应式的数据&#xff0c;本文中所讲的三个方法也能实现将数据转化…