基于CNN-LSTM的轴承剩余使用寿命预测方法(附代码)
前言
上一篇文章介绍了一种基于CNN-GRU的轴承剩余使用寿命预测模型.本章介绍一种类似的混合模型预测方法-CNN-LSTM。相较于GRU。LSTM具有更多的门控单元,能够更细腻的处理轴承退化过程所产生复杂振动信号这一信息。模型使用CNN提取的局部特征,LSTM用于挖掘轴承的时序退化特征。最后,使用全连接层对轴承的剩余使用寿命进行输出。同样,本文使用PHM2012轴承退化数据集对所介绍的预测模型进行实验验证。
提示:模型基于python语言编写,运行整体程序需要pandas、numpy、Matplotlib等第三方库
一、模型介绍
1.1 卷积神经网络
前述文章已经对模型所使用一维卷积神经网络进行简要介绍,具体信息详见基于卷积神经网络(CNN)的轴承剩余寿命预测实例。
1.2 长短期记忆神经网络(LSTM)
LSTM同样由循环神经网络改进而来,相较于GRU,LSTM具有更为复杂的网络结构,通过引入记忆单元与门控机制(遗忘门、输入门、输出门)有效缓解了长序列中的梯度消失问题。长短期记忆网络擅长建模序列中的长短期依赖,广泛应用于时间序列预测、语音识别与自然语言处理等领域。
如图所示,长短期记忆神经网络一次传递两条信息:长期记忆c_t-1,隐藏状态H_t-1,其中隐藏状态为神经网络的输出。长短期记忆神经网络的整体结构如上图所示,模型接受上一时刻隐状态记忆信息以及当前时刻的输入,作为初始输入信息。首先,通过可学习权重向量生成相关的遗忘信息输入信息输出信息以及暂时记忆,具体计算公式如下:
1.3 整体预测模型
CNN-LSTM的轴承剩余使用寿命预测模型的整体预测流程如下图所示。模型接受原始振动信号作为输入,然后使用1D CNN进行特征提取。接下来对提取后的特征进行序列重组,以满足LSTM工作原理。紧接着使用LSTM对其进行时序关系挖掘,最后使用MLP进行剩余使用寿命输出。
1.4 预测模型代码实现
class Predictor(nn.Module):
def __init__(self,in_channels,out_channels,kernel_size,
hidden_sizes,
in_features,out_features):
super(Predictor, self).__init__()
self.cnn = CNN_NET(in_channels,out_channels,kernel_size)
self.lstm = LSTM_NET(hidden_sizes=hidden_sizes)
self.fc1 = nn.Linear(in_features, out_features[0])
self.fc2 = nn.Linear(out_features[0], out_features[1])
self.fc3 = nn.Linear(out_features[1], out_features[2])
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()
self.dropout = nn.Dropout(0.5)
def forward(self, x):
if len(x.shape) != 4:
x = x.unsqueeze(2)
batch_size, seq_len, in_channels, n_features = x.shape
x = self.cnn(x)
_, out_channels, n_features = x.shape
x = x.view(batch_size,seq_len,out_channels*n_features)
x = self.lstm(x)
x = x[:, -1, :]
x = self.fc1(x)
x = self.relu(x)
x = self.dropout(x)
x = self.fc2(x)
x = self.relu(x)
x = self.dropout(x)
x = self.fc3(x)
x = self.sigmoid(x)
return x
二、实验验证
2.1 数据预处理
2.2 代码实现
# 主训练函数
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
from matplotlib import pyplot as plt
from dataprocess import load_data,data_preprocess,Sequential_Dataset
from model import CNN,LSTM
from help import Predictor,train_epoch,test_epoch,model_of_predict,sort_results
device = torch.device("cuda:0")
data_name = "PHM2012"
data_direction = "horiz"
SEQ_LEN = 8
train_bearing = ["Bearing1_2.pkl", "Bearing1_3.pkl", "Bearing1_4.pkl", "Bearing1_5.pkl","Bearing1_6.pkl", "Bearing1_7.pkl"]
train_data = []
train_bearings = []
for sub_bearing in train_bearing:
bearing_name = sub_bearing
data_h, data_v = load_data(data_name, bearing_name)
step_data = data_preprocess(data_h, data_v, data_name=data_name, data_direction=data_direction, normalize=False)
train_data.append(step_data)
train_bearings.append(bearing_name)
print("训练轴承:", train_bearings)
train_samples = []
the_number_of_train_samples = 0
for sub_train_data in train_data:
num_steps = sub_train_data["x"].shape[0]
num_samples = num_steps - SEQ_LEN + 1
train_indices = np.random.permutation(num_samples)
train_indices = train_indices[0:]
train_sample = Sequential_Dataset(sub_train_data, train_indices, SEQ_LEN)
train_samples.append(train_sample)
the_number_of_train_samples += num_samples
# print(f"训练轴承{i+1}:,样本数量{num_samples}")
print(f"训练样本总数:{the_number_of_train_samples}")
from torch.utils.data import ConcatDataset
from torch.utils.data import DataLoader
from torch.utils.data import random_split
train_batch_size = 16
val_batch_size = 16
train_combined_dataset = ConcatDataset(train_samples)
train_ratio = 0.8
train_size = int(train_ratio * len(train_combined_dataset))
val_size = len(train_combined_dataset) - train_size
train_dataset, val_dataset = random_split(train_combined_dataset, [train_size, val_size])
train_dataloader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
print(len(train_dataloader))
val_dataloader = DataLoader(val_dataset, batch_size=val_batch_size, shuffle=True)
print(len(val_dataloader))
# 实例化模型
in_channels = 1 # if data_direction == "horiz" or "vert" else 2
out_channels = [4,8,16]
kernel_size = [3,3,3]
hidden_size_0 = int(out_channels[-1]*2560/(4**len(out_channels)))
hidden_size = [hidden_size_0,hidden_size_0//2,hidden_size_0//2]
in_features = int(hidden_size[-1])
out_features = [320,64,1]
model = Predictor(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size,
hidden_sizes=hidden_size,
in_features=in_features,out_features=out_features).to(device)
# 定义损失函数和优化器
criterion = nn.MSELoss(reduction='sum')
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08, amsgrad=False)
multistep_lr_sch = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[20, 40, 50], gamma=0.1, last_epoch=-1,verbose = False)
num_epochs = 100
losses = []
best_val_loss = 1
for epoch in range(num_epochs):
# 训练
train_loss = train_epoch(model, train_dataloader, criterion, optimizer, device)
# 验证
val_loss = test_epoch(model, val_dataloader, criterion, device)
# 调整学习率
multistep_lr_sch.step()
current_lr = multistep_lr_sch.get_lr()[0]
# 打印训练损失
print(
f'{epoch + 1}/{num_epochs}: train_loss = {train_loss:.4f}, val_loss = {val_loss:.4f}, lr = {current_lr}, best_val_loss = {best_val_loss}')
# 记录损失
losses.append([train_loss, val_loss])
plt.plot(range(len(losses)), [l[0] for l in losses], 'b.-', label='train loss')
plt.plot(range(len(losses)), [l[1] for l in losses], 'r.-', label='val loss')
plt.legend()
plt.show()
2.3 实验结果展示
2.3.1 训练损失
2.3.2 轴承预测效果
小结
上述即为基于CNN-LSTM的轴承剩余使用寿命预测模型,完整代码会在近期上传。