在工业智能维护领域,从强背景噪声中提取微弱的故障特征一直是技术难点。本文将围绕论文“Deep Residual Shrinkage Networks for Fault Diagnosis”展开,分享如何通过深度学习手段应对这一挑战。这篇论文提出了一种深度学习架构,即深度残差收缩网络(DRSN),它通过在特征学习过程中自动消除噪声干扰,提升机械故障诊断的鲁棒性。

一、核心原理与软阈值化的引入

传统的卷积神经网络(Convolutional Neural Network, CNN)或残差网络(Residual Network, ResNet)在处理清洁信号时表现优异,但在面对强噪声时,可能会误将噪声特征作为分类依据。
该论文的核心创新点在于将“软阈值化(Soft Thresholding)”作为非线性映射引入了残差构建单元(Residual Building Unit, RBU)。其数学表达式为:y = sign(x) * max(|x| - threshold, 0)。其中,sign是符号函数,threshold是阈值。这个公式的物理意义是:将绝对值小于阈值的特征直接置为零,从而剔除噪声;而对于大于阈值的特征,则进行“收缩”处理。
为了解决阈值难以人工设定的问题,论文设计了一个专门的子网络来自动学习阈值。通过全局平均池化(Global Average Pooling, GAP)提取特征图的绝对值平均量,再经过多层全连接层和Sigmoid激活函数,模型能够为每一组特征通道生成专属的收缩阈值,实现了端到端的自适应去噪。

Tree shape 路径图

二、模型架构与代码实现

在复现过程中,重点实现了“基于通道级阈值的残差收缩构建单元(Residual Shrinkage Building Unit with Channel-Wise thresholds, RSBU-CW)”。该单元内部集成了批量归一化(Batch Normalization, BN)、线性整流函数(Rectified Linear Unit, ReLU)以及上述的注意力机制阈值子网。
以下是基于TensorFlow框架的完整复现代码:

"""
本项目致力于复现Zhao等人提出的深度残差收缩网络(Deep Residual Shrinkage Network, DRSN)用于高噪声环境下的机械故障诊断。
代码结构化地实现了从信号载入到模型评估的完整流程:
    1. 资源与环境初始化:配置计算资源并验证依赖项。
    2. 信号处理与增强:加载振动信号,进行窗口化切片,并设计了动态加噪机制以模拟强背景噪声。
    3. 核心网络构建:详细实现了基于通道级软阈值收缩的残差收缩构建单元(RSBU-CW),并搭建了完整的DRSN模型。
    4. 模型生命周期管理:构建了包含动态数据增强策略的TensorFlow训练流水线,并进行了低信噪比环境下的鲁棒性验证。
注意:本程序中采用的数据集(数据源路径及配置)与原始论文实验所用数据存在差异。

参考文献:
Zhao M, Zhong S, Fu X, Tang B, Pecht M. Deep residual shrinkage networks for fault diagnosis.
IEEE Transactions on Industrial Informatics, 2020, 16(7): 4681–4690.
"""

import os
import sys
import logging
import numpy as np
import scipy.io as sio
import tensorflow as tf
from sklearn.model_selection import train_test_split
from tensorflow.keras import layers, models, regularizers

# =============================================================================
# 模块一:计算资源与运行环境配置
# =============================================================================

logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(levelname)s: %(message)s')

class SystemConfig:
    """
    运行环境预设。
    用于验证科学计算栈(SciPy/SKLearn)的完整性,并配置后端引擎的显存分配逻辑,以确保深层模型的稳定运行。
    """

    @staticmethod
    def init_hardware():
        """
        硬件与依赖初始化:
        1. 核心库状态校验。
        2. 屏蔽冗余的 Cuda 运行时日志。
        3. 启用显存增量分配策略,预防大尺寸张量计算引发溢出。
        """
        try:
            import sklearn.model_selection
            import scipy.io
        except ImportError as e:
            logging.critical(f"关键依赖缺失,请检查环境配置: {e}")
            sys.exit(1)

        os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
        gpus = tf.config.list_physical_devices('GPU')
        
        if gpus:
            try:
                for device in gpus:
                    tf.config.experimental.set_memory_growth(device, True)
                logging.info("GPU 加速器已就绪,显存自适应模式开启。")
            except RuntimeError as e:
                logging.error(f"显卡调度异常: {e}")
        else:
            logging.info("当前环境未发现可用 GPU,系统将回退至中央处理器(CPU)执行运算。")

# 触发环境预检
SystemConfig.init_hardware()

# =============================================================================
# 模块二:工业振动信号加载与预处理
# =============================================================================

class CWRULoader:
    """
    数据解析引擎。
    用于读取 CWRU 轴承加速度信号,并通过非重叠滑动窗口(Non-overlapping Window)生成时域样本序列。
    """

    def __init__(self, data_root, window_size=1024):
        """
        构造函数。
        :param data_root: 存储 .mat 文件的根路径。
        :param window_size: 深度残差收缩网络输入的序列长度。
        """
        self.data_root = os.path.abspath(data_root)
        self.window_size = window_size
        self.stride = window_size  

    def _load_mat(self, file_path):
        """
        对 MATLAB 格式的数据结构进行解析。
        核心逻辑:提取包含 'DE_time' 标识的驱动端振动向量。
        """
        try:
            mat_data = sio.loadmat(file_path)
            for key in mat_data.keys():
                if 'DE_time' in key:
                    return mat_data[key].ravel()
        except Exception:
            return None
        return None

    def load_data(self, FAULT_LABEL_CONFIG):
        """
        构建训练所需的特征矩阵与标签向量。
        """
        X_list, y_list = [], []
        is_valid = False
        
        for class_id, filenames in FAULT_LABEL_CONFIG.items():
            for name in filenames:
                full_path = os.path.join(self.data_root, "%s.mat" % name)
                if not os.path.exists(full_path):
                    continue
                
                signal = self._load_mat(full_path)
                if signal is None:
                    continue
                
                is_valid = True
                # 执行等间距滑动窗口切割
                max_offset = len(signal) - self.window_size + 1
                for offset in range(0, max_offset, self.stride):
                    fragment = signal[offset : offset + self.window_size]
                    X_list.append(fragment)
                    y_list.append(class_id)
        
        if not is_valid:
            raise FileNotFoundError("在指定路径下未采集到符合解析规则的信号文件:%s" % self.data_root)
            
        return np.array(X_list, dtype='float32'), np.array(y_list, dtype='int32')

def apply_awgn_noise(X, snr):
    """
    加性高斯白噪声注入算子。
    数学原理:P_noise = P_signal / (10^(SNR/10)),用于模拟工业现场的强干扰环境。
    """
    X_array = np.array(X)
    rng = np.random.default_rng()
    
    # 信噪比参数解析
    snr_val = snr if not isinstance(snr, (list, tuple)) \
                else rng.uniform(snr[0], snr[1])
    
    # 功率谱密度计算
    signal_power = np.mean(np.square(X_array), axis=1, keepdims=True)
    noise_var = signal_power / (10 ** (snr_val / 10.0))
    noise = rng.normal(0, np.sqrt(noise_var), X_array.shape)
    
    return (X_array + noise).astype('float32')

# =============================================================================
# 模块三:深度残差收缩网络 (DRSN) 架构实现
# =============================================================================

class SoftThresholding(layers.Layer):
    """
    深度残差收缩网络的核心算子:软阈值化层。
    实现数学映射:y = sign(x) * max(|x| - τ, 0),其中 τ 为自适应学习的阈值。
    """
    def __init__(self, **kwargs):
        super(SoftThresholding, self).__init__(**kwargs)

    def call(self, inputs):
        """
        算子执行流程。输入张量列表包含:
        1. 卷积层输出的特征图;
        2. 由子网络学习得到的自适应阈值向量。
        """
        x, threshold = inputs
        # 维度对齐:(Batch, 1, Channels)
        reshaped_threshold = tf.expand_dims(threshold, axis=1)
        return tf.sign(x) * tf.maximum(tf.abs(x) - reshaped_threshold, 0.0)

class RSBU_CW(layers.Layer):
    """
    深度残差收缩网络的基本构建单元(RSBU-CW)。
    在残差学习框架内嵌入注意力机制,自动估算特征通道的噪声水平并执行收缩。
    """
    def __init__(self, filters, kernel_size, stride=1, **kwargs):
        super(RSBU_CW, self).__init__(**kwargs)
        self.filters = filters
        self.stride = stride
        self.kernel_size = kernel_size
        self.weight_decay = regularizers.l2(1e-4)

        self.shortcut = None
        
        # 主干卷积路径
        self.bn1 = layers.BatchNormalization()
        self.relu1 = layers.Activation('relu')
        self.conv1 = layers.Conv1D(filters, kernel_size, strides=stride, padding='same', 
                                  kernel_initializer='he_normal', kernel_regularizer=self.weight_decay)
        
        self.bn2 = layers.BatchNormalization()
        self.relu2 = layers.Activation('relu')
        self.conv2 = layers.Conv1D(filters, kernel_size, strides=1, padding='same', 
                                  kernel_initializer='he_normal', kernel_regularizer=self.weight_decay)
        
        # 阈值学习子网 (Sub-network for Thresholding)
        self.gap = layers.GlobalAveragePooling1D()
        self.fc1 = layers.Dense(filters, kernel_initializer='he_normal')
        self.bn_fc = layers.BatchNormalization()
        self.relu_fc = layers.Activation('relu')
        self.fc2 = layers.Dense(filters, activation='sigmoid')
        self.soft_threshold = SoftThresholding()

    def build(self, input_shape):
        """
        构建跳连分支,确保残差加法的维度一致性。
        """
        if self.stride != 1 or input_shape[-1] != self.filters:
            self.shortcut = models.Sequential([
                layers.Conv1D(self.filters, 1, strides=self.stride, padding='same'),
            ])
        super(RSBU_CW, self).build(input_shape)

    def call(self, inputs):
        """
        深度残差收缩网络单元计算流。
        """
        identity = inputs
        if self.shortcut:
            identity = self.shortcut(inputs)

        # 主要变换流
        x = self.bn1(inputs)
        x = self.relu1(x)
        x = self.conv1(x)
        x = self.bn2(x)
        x = self.relu2(x)
        x = self.conv2(x)

        # 注意力阈值计算
        abs_x = tf.abs(x)
        scales = self.gap(abs_x)
        
        scales = self.fc1(scales)
        scales = self.bn_fc(scales)
        scales = self.relu_fc(scales)
        scales = self.fc2(scales)
        
        # 通道级阈值自适应
        threshold = tf.multiply(scales, self.gap(abs_x))
        
        # 收缩处理与残差聚合
        y = self.soft_threshold([x, threshold])
        return layers.Add()([y, identity])

class DRSN_CW(models.Model):
    """
    1D-DRSN 故障诊断模型架构。
    拓扑结构包含:初级卷积特征提取层、六组级联的 RSBU-CW 单元,以及全局平均池化决策层。
    """
    def __init__(self, num_classes):
        super(DRSN_CW, self).__init__(name="DRSN_CW")
        
        # 初始特征提取
        self.conv0 = layers.Conv1D(32, 15, strides=2, padding='same', kernel_initializer='he_normal')
        self.bn0 = layers.BatchNormalization()
        self.relu0 = layers.Activation('relu')
        
        # 深度残差收缩层组
        self.blocks = [
            RSBU_CW(32, 5, stride=2),
            RSBU_CW(32, 5, stride=1),
            RSBU_CW(64, 5, stride=2),
            RSBU_CW(64, 5, stride=1),
            RSBU_CW(128, 5, stride=2),
            RSBU_CW(128, 5, stride=1)
        ]
        
        # 决策层
        self.bn_final = layers.BatchNormalization()
        self.relu_final = layers.Activation('relu')
        self.gap = layers.GlobalAveragePooling1D()
        self.classifier = layers.Dense(num_classes, activation='softmax')

    def call(self, x):
        """
        执行深度残差收缩网络的前向推演。
        """
        x = self.conv0(x)
        x = self.bn0(x)
        x = self.relu0(x)
        
        for block in self.blocks:
            x = block(x)
            
        x = self.bn_final(x)
        x = self.relu_final(x)
        x = self.gap(x)
        return self.classifier(x)

# =============================================================================
# 模块四:流水线执行与模型训练
# =============================================================================

def run_pipeline(data_dir, window_size=1024):
    """
    全流程实验框架。
    封装了:信号加载、Z-Score 归一化、动态噪声增强训练,以及针对强背景噪声环境的泛化性能测试。
    """
    
    # 状态分类定义
    FAULT_LABEL_CONFIG = {
        0: ['Normal_0', 'Normal_1', 'Normal_2', 'Normal_3'],
        1: ['IR007_0', 'IR007_1', 'IR007_2', 'IR007_3'],
        2: ['IR014_0', 'IR014_1', 'IR014_2', 'IR014_3'],
        3: ['IR021_0', 'IR021_1', 'IR021_2', 'IR021_3'],
        4: ['B007_0', 'B007_1', 'B007_2', 'B007_3'],
        5: ['B014_0', 'B014_1', 'B014_2', 'B014_3'],
        6: ['B021_0', 'B021_1', 'B021_2', 'B021_3'],
        7: ['OR007@6_0', 'OR007@6_1', 'OR007@6_2', 'OR007@6_3'],
        8: ['OR014@6_0', 'OR014@6_1', 'OR014@6_2', 'OR014@6_3'],
        9: ['OR021@6_0', 'OR021@6_1', 'OR021@6_2', 'OR021@6_3']
    }
    
    data_loader = CWRULoader(data_root=data_dir, window_size=window_size)
    
    try:
        X_raw, y_raw = data_loader.load_data(FAULT_LABEL_CONFIG)
    except Exception as e:
        logging.error("数据生命周期异常中断: %s" % e)
        return

    # 训练、校验、测试集分割 (70% / 15% / 15%)
    X_train, X_temp, y_train, y_temp = train_test_split(
        X_raw, y_raw, test_size=0.3, random_state=42
    )
    X_val, X_test, y_val, y_test = train_test_split(
        X_temp, y_temp, test_size=0.5, random_state=42
    )
    
    # 标准化转换参数
    mean, std = np.mean(X_train), np.std(X_train)
    
    def normalize_data(data):
        """执行零均值规格化并扩展通道维。"""
        norm_data = (data - mean) / std
        return norm_data.reshape(-1, window_size, 1)

    X_train_norm = normalize_data(X_train)
    X_val_norm = normalize_data(X_val)
    X_test_norm = normalize_data(X_test)
    
    # 类别映射转化
    num_classes = len(FAULT_LABEL_CONFIG)
    y_train_onehot = tf.keras.utils.to_categorical(y_train, num_classes).astype('float32')
    y_val_onehot = tf.keras.utils.to_categorical(y_val, num_classes).astype('float32')
    y_test_onehot = tf.keras.utils.to_categorical(y_test, num_classes).astype('float32')

    # 评估环境设置:注入 -8dB 强度的背景噪声
    X_val_noisy = apply_awgn_noise(X_val_norm, snr=-8)
    X_test_noisy = apply_awgn_noise(X_test_norm, snr=-8)

    def augment_data(X, y):
        """
        深度残差收缩网络训练期间的动态增强策略:
        1. 循环移位:增强相位不变性。
        2. 随机脉冲:模拟突发性冲击。
        3. 多样性 SNR:覆盖 -8dB 到 8dB 的噪声区间。
        """
        rng = np.random.default_rng()
        X_aug = X.copy()
        count, length, _ = X_aug.shape

        # 随机平移
        for i in range(count):
            offset = rng.integers(0, length)
            X_aug[i, :, 0] = np.roll(X_aug[i, :, 0], offset)

        # 瞬态冲击模拟 (10% 概率)
        if rng.random() > 0.9: 
            for i in range(count):
                if rng.random() > 0.5: 
                    points = rng.integers(1, 3) 
                    indices = rng.integers(0, length, points)
                    peak = np.std(X_aug[i]) * rng.uniform(1.5, 2.5) 
                    X_aug[i, indices, 0] += peak * rng.choice([-1, 1], size=points)

        # 动态噪声注入 (50% 概率)
        if rng.random() > 0.5: 
            X_aug = apply_awgn_noise(X_aug, snr=(-8, 8))

        return X_aug.astype(np.float32), y.astype(np.float32)

    def set_shapes(X_tensor, y_tensor):
        """辅助编译器确定张量形态。"""
        X_tensor.set_shape([None, window_size, 1])
        y_tensor.set_shape([None, num_classes])
        return X_tensor, y_tensor

    # 生产级数据流水线构建
    train_ds = tf.data.Dataset.from_tensor_slices((X_train_norm.astype('float32'), y_train_onehot))
    train_ds = train_ds.shuffle(len(X_train_norm)).batch(64)
    train_ds = train_ds.map(
        lambda x, y: tf.numpy_function(augment_data, [x, y], [tf.float32, tf.float32]),
        num_parallel_calls=tf.data.AUTOTUNE
    ).map(set_shapes).prefetch(tf.data.AUTOTUNE)

    # 模型编译
    model = DRSN_CW(num_classes=num_classes)
    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3), 
        loss='categorical_crossentropy',
        metrics=['accuracy']
    )

    logging.info("深度残差收缩网络模型初始化完毕。分类规模: %d" % num_classes)
    
    # 训练监控回调
    callbacks = [
        tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=7, min_lr=1e-6, verbose=1),
        tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True)
    ]

    # 模型拟合
    model.fit(
        train_ds,
        epochs=100,
        validation_data=(X_val_noisy, y_val_onehot),
        callbacks=callbacks,
        verbose=2
    )

    # 针对高背景噪声(-8dB SNR)的泛化能力评估
    _, test_acc = model.evaluate(X_test_noisy, y_test_onehot, verbose=0)
    print("\n[最终评估报告] 在极高背景噪声 (-8dB SNR) 下,准确率为: {:.2f}%".format(test_acc * 100))

# =============================================================================
# 模块五:主入口点
# =============================================================================

if __name__ == "__main__":
    # 配置数据集默认搜索位置
    DATA_PATH = os.path.join(os.getcwd(), 'cwru_data_repo')
    
    if not os.path.exists(DATA_PATH):
        logging.warning("默认数据仓库不存在: %s" % DATA_PATH)
        user_input_path = input("请输入 .mat 数据集的实际存储路径: ").strip()
        if user_input_path:
            DATA_PATH = user_input_path
        else:
            logging.error("无效路径,系统终止。")
            sys.exit(0)

    # 启动全流程诊断
    run_pipeline(DATA_PATH, window_size=1024)

在上述代码中,搭建了一个包含6个收缩残差块的深度网络。每个块都能根据当前输入信号的噪声强度,动态调整收缩阈值,这使得模型在处理不同信噪比(Signal-to-Noise Ratio, SNR)的数据时具有较强的泛化能力。

三、实验数据准备与强噪声模拟

实验采用了公认的凯斯西储大学(Case Western Reserve University, CWRU)轴承数据集。为了验证模型在极端环境下的性能,定义了10类健康状态,涵盖了正常、内圈故障(Inner Race Fault)、滚动体故障(Ball Fault)以及外圈故障(Outer Race Fault),并对应不同的故障尺寸(0.007/0.014/0.021英寸)。

为了模拟真实工厂中的恶劣环境,在预处理阶段引入了加性高斯白噪声(Additive White Gaussian Noise, AWGN)。在训练过程中,代码采用了动态数据增强策略,包括:
(1)相位平移:通过循环移位增强模型对起始位置的不敏感性。
(2)随机冲击:模拟设备运行中的瞬时干扰。
(3)混合噪声注入:在-8分贝(decibel, dB)到8分贝的信噪比范围内随机加噪,通过这种“抗噪训练”提升模型的硬实力。

四、实验结果分析与总结

根据复现实验的训练日志显示,模型在经历100轮Epoch后表现较为稳定。尤其值得关注的是在极低信噪比环境下的泛化测试。在信噪比为-8分贝的强噪声干扰下(此时噪声功率远大于有效信号功率),本复现代码实现的深度残差收缩网络在测试集上的准确率依然达到了90%以上

实验结论表明,深度残差收缩网络通过将信号处理中的传统去噪思想与深度残差学习有机结合,实现了自动化特征提取与抗噪性能之间的深度协同。对于从事工业大数据分析和预测性维护的同行来说,DRSN是一个值得尝试的模型。

论文标题: Deep residual shrinkage networks for fault diagnosis
出版期刊: IEEE Transactions on Industrial Informatics. 2020, 16(7): 4681-4690.
DOI: 10.1109/TII.2019.2943898

posted on 2026-03-01 14:37  伊宸  阅读(1)  评论(0)    收藏  举报