基于TCN的轴承剩余使用寿命预测
前言
`
上一篇文章主要介绍了基于卷积神经网络的轴承剩余使用寿命预测模型,本文介绍一种新的深度学习模型时序卷积神经网络----TCN。相较于卷积神经网络,TCN具有更强的时序性,在具有并行计算特性的同时,能够对超长时序信息进行建模,具有巨大的时间感受野,且相对参数量更小。本文主要介绍了一种基于TCN的轴承剩余使用v寿命预测模型,并通过PHM2012轴承退化数据集进行实验验证。
提示:模型基于python语言编写,运行整体程序需要pandas、numpy、Matplotlib等第三方库
一、模型介绍
TCN主要由因果卷积、膨胀卷积以及残差连接三个部分组成。其中,因果卷积用于保障模型的时序性,避免时序信息的泄露;膨胀卷积负责控制模型对时序信息的感受野,控制时间尺度;残差连接用于保证模型训练的稳定性。
1.1 模型介绍
因果卷积:因果卷积是时序卷积网络(TCN)中的核心组件,专门为解决时序数据的建模问题而设计。其核心特点是严格保证时间因果性——当前时刻的输出仅依赖于当前及历史时刻的输入,绝不使用未来信息。通过单向掩码实现。例如:对于kernel_size=k的卷积,每层左侧填充k-1个零,确保输出时刻t只依赖输入时刻t, t-1, …, t-k+1。
膨胀卷积:通过插入卷积间隔,控制卷积核的感受野。通过膨胀率控制感受野大小。例如,当卷积核大小为3时,将膨胀率设为2,则感受野RF=(kernel_size−1)×dilation_rate+1=7。
残差连接:作为保证模型稳定性,防止深度网络退化的技术,残差连接将网络的输入直接添加到输出中,从而缓解深度网络训练中的梯度消失问题。TCN通常采用残差块作为基本单元,每个残差块包含多个卷积层和残差连接。残差连接允许信息直接从输入层传播到输出层,从而更好地保留原始信息,并提高模型的训练效率和泛化能力。
1.2 TCN代码实现
import torch
import torch.nn as nn
from torch.nn.utils import weight_norm
# 注意常规TCN模型的单个时间步输入输出特征个数不变
class Chomp1d(nn.Module):
def __init__(self, chomp_size):
super(Chomp1d, self).__init__()
self.chomp_size = chomp_size
def forward(self, x):
x = x[:, :, :-self.chomp_size].contiguous() # 由于是双边填补,所以要切掉右边
return x
class TemporalBlock(nn.Module):
def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2):
super(TemporalBlock, self).__init__()
self.conv1 = weight_norm(nn.Conv1d(n_inputs, n_outputs, kernel_size,
stride=stride, padding=padding, dilation=dilation))
self.chomp1 = Chomp1d(padding)
self.relu1 = nn.ReLU()
self.dropout1 = nn.Dropout(dropout)
self.conv2 = weight_norm(nn.Conv1d(n_outputs, n_outputs, kernel_size,
stride=stride, padding=padding, dilation=dilation))
self.chomp2 = Chomp1d(padding)
self.relu2 = nn.ReLU()
self.dropout2 = nn.Dropout(dropout)
self.net = nn.Sequential(self.conv1, self.chomp1, self.relu1, self.dropout1,
self.conv2, self.chomp2, self.relu2, self.dropout2)
self.downsample = nn.Conv1d(n_inputs, n_outputs, 1) if n_inputs != n_outputs else None
self.relu = nn.ReLU()
self.init_weights()
def init_weights(self):
self.conv1.weight.data.normal_(0, 0.01)
self.conv2.weight.data.normal_(0, 0.01)
if self.downsample is not None:
self.downsample.weight.data.normal_(0, 0.01)
def forward(self, x):
out = self.conv1(x)
out = self.chomp1(out)
out = self.relu1(out)
out = self.dropout1(out)
out = self.conv2(out)
out = self.chomp2(out)
out = self.relu2(out)
out = self.dropout2(out)
# out = self.net(x)
res = x if self.downsample is None else self.downsample(x)
return self.relu(out + res)
class TemporalConvNet(nn.Module):
def __init__(self, num_inputs, num_channels, kernel_size=2, dropout=0.2):
"""
:param num_inputs: 输入样本的特征数量,因为会对输入channels(即时间步数)与每个时间步所采集到的数据点数(特征个数)进行转换
:param outputs: # 输出,ji每一个样本经TCN后的,每一个时间步所保留的特征数
:param num_channels:一个列表,指定每一层的输出维度,同时确定有几层
:param kernel_size:卷积核大小
:param dropout:卷积之后的丢弃率
"""
super(TemporalConvNet, self).__init__()
layers = []
num_levels = len(num_channels)
for i in range(num_levels):
dilation_size = 2 ** i
in_channels = num_inputs if i == 0 else num_channels[i - 1]
out_channels = num_channels[i]
layers += [TemporalBlock(in_channels, out_channels, kernel_size, stride=1, dilation=dilation_size,
padding=(kernel_size - 1) * dilation_size, dropout=dropout)]
self.network = nn.Sequential(*layers)
def forward(self, x):
x = x.permute(0, 2, 1)
x = self.network(x)
return x
二、实验
2.1 实验设定
选择PHM2012轴承数据集工况一条件下轴承1-1至轴承1-7进行实验,采用交叉验证的方法分别对轴承1至7进行预测,共计七次实验。每次实验选择训练轴承90%时序样本作为训练集,其余作为验证集。训练完成后,保存模型,对测试轴承进行预测。
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 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 = 24
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"训练样本总数:{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
kernel_size = (3,)
c_in_channels = 2560//4
c_out_channels= [c_in_channels//2,c_in_channels//(2*2),c_in_channels//(2*2*2)]
c_kernel_size = 3
in_features = int(c_out_channels[-1]*out_channels)
out_features = [320,64,1]
model = Predictor(c_in_channels=in_channels, c_out_channels=out_channels, c_kernel_size=kernel_size,
in_channels=640,out_channels=c_out_channels,kernel_size=c_kernel_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)
# 记录损失
best_val_loss = min(val_loss, best_val_loss)
if val_loss == best_val_loss:
state = {"state_dict": model.state_dict(),
"best_acc": val_loss,
"optimizer": optimizer.state_dict()}
torch.save(state, f"saved/weights/epoch={epoch}.pth")
# 调整学习率
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 预测结果展示
上图为模型对轴承1、轴承3剩余使用寿命的预测效果。
总结
本文主要提出了一种基于TCN的轴承剩余使用寿命预测模型,并通过PHM2012数据集工况一条件下轴承1至轴承7进行实验验证。完整代码见:基于TCN的轴承剩余使用寿命预测方法研究。【代码问题可在评论区或私聊讨论】
展望
近期会写一些使用深度学习模型对轴承剩余使用寿命进行预测的技术文章。过段时间会将完整资源上传!