基于CNN-GRU的轴承剩余寿命预测
前言
前面的文章介绍了使用卷积神经网络进行轴承剩余使用寿命预测的深度学习模型,基于卷积神经网络(CNN)的轴承剩余寿命预测实例。但是CNN网络更加关注轴承退化的局部特征,缺少对轴承退化趋势的长期依赖关系的挖掘。此外,CNN对所有时间段的特征平等处理,而GRU的门控机制可以动态调整记忆权重,自动关注关键退化阶段。因此,本文介绍了一种基于CNN-GRU的混合预测模型,模型使用CNN提取的局部特征,GRU整合的长期趋势特征,形成更完整的健康状态表征。最后,使用PHM2012轴承退化数据集进行实验验证。
提示:模型基于python语言编写,运行整体程序需要pandas、numpy、Matplotlib等第三方库
一、预测模型介绍
1.1 1D CNN
一维卷积神经网络意在前述文章介绍,具体参考见添基于卷积神经网络(CNN)的轴承剩余寿命预测实例
1.2 GRU
传统循环神经网络在处理长序列时面临梯度消失或爆炸问题。梯度消失是指在训练过程中,误差反向传播时,梯度随着时间步数的增加而指数级衰减。梯度爆炸则正好相反,这会导致模型难以学习到当前时刻和距离较远的时刻之间的依赖关系。GRU是传统循环神经网络的改进变体,通过简化门控机制解决传统RNN的梯度消失问题,专为建模长时序依赖关系设计。GRU网络的结构如下,
如图所示,GRU主要包括重置门,更新门,候选状态三个组成部分,最后将未来状态h_t输出,具体工作原理如下表:
1.3 整体预测模型
CNN-GRU的轴承剩余使用寿命预测模型的整体预测流程如下图所示。模型接受原始振动信号作为输入,然后使用1D CNN进行特征提取。接下来对提取后的特征进行序列重组,以满足GRU工作原理。紧接着使用GRU对其进行时序关系挖掘,最后使用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.gru = GRU_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 = x.view(x.size(0), -1)
x = self.gru(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 实验设计
实验采用PHM2012数据集,选择水平振动信号作为研究对象,进行实验。具体流程见基于卷积神经网络(CNN)的轴承剩余寿命预测实例
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,GRU
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"]
test_bearing = ["Bearing1_1.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-CRU轴承剩余使用寿命预测模型的整体介绍,代码问题可在评论区或私聊交流。