ICLR 时间序列多尺度特征提取模块OS_block,即插即用,即用即涨点

文摘   2025-01-14 17:21   上海  

论文介绍

题目:OMNI-SCALE CNNS: A SIMPLE AND EFFECTIVE KER NEL SIZE CONFIGURATION FOR TIME SERIES CLASSIFI CATION

论文地址:

https://arxiv.org/pdf/2002.10061

QQ深度学习交流群:994264161

扫描下方二维码,加入深度学习论文指南星球!

加入即可获得,模块缝合、制作、写作技巧,学会“结构”创新、“创新点”创新,从三区到顶会,小论文大论文,毕业一站式服务

创新点

  • 无需搜索最佳感受野大小:OS-block 提供了一种基于素数的卷积核配置规则,可以自动覆盖所有的感受野大小(Receptive Field Size, RF)。相比传统需要大量搜索感受野大小的方法,这种设计避免了复杂且耗资源的搜索过程。

  • 基于素数设计的内核:OS-block 使用素数作为卷积核的大小,这种设计源于哥德巴赫猜想。通过这种规则,OS-block 能高效覆盖所有时间尺度,从而在多个数据集上获得类似于最佳感受野大小的性能。

  • 跨领域数据的鲁棒性表现:在不同领域的时间序列数据集(如医疗、活动识别、语音和光谱分析)中,OS-block 显示出强大的时间尺度捕获能力,且无需针对每个数据集单独调整超参数。

  • 模型复杂度的高效性:相较于使用偶数或其他数值对来覆盖感受野,OS-block 基于素数的设计可以显著降低模型复杂度,提供更高效的解决方案。

  • 支持多种复杂结构扩展:OS-block 可以轻松与其他深度学习结构结合(如膨胀卷积、注意力机制、Transformer 等),进一步提升性能。

方法

整体架构

       这篇论文提出的模型是 OS-CNN,核心组件为 OS-block,通过多层基于素数设计的卷积核,高效覆盖所有可能的感受野大小,从而提取多尺度特征。模型整体结构包括输入层、多个 OS-block、全局平均池化层和全连接层,用于降维和分类。此外,OS-block 可以与其他深度学习结构(如残差连接、Transformer 等)结合,具有很强的灵活性和扩展性,在时间序列分类任务中表现出卓越性能。

  • 输入层

    • 模型可以处理单变量多变量时间序列数据。

    • 输入数据根据时间序列变量的数量调整通道数。

  • OS-block(核心模块)

    • 多卷积核结构:OS-block 包含三层卷积,每层具有多个卷积核,卷积核的大小基于素数设计。

    • 覆盖所有感受野大小:通过素数的组合,OS-block 能够高效覆盖所有可能的感受野大小(RF sizes)。

    • 数学原理支持:OS-block 基于哥德巴赫猜想,利用素数组合和偶数覆盖,确保感受野的完整性和范围的连续性。

  • 全局平均池化层(Global Average Pooling)

    • 用于降维,汇总时间序列的特征。

  • 全连接层(Fully Connected Layer)

    • 用于分类任务,根据输入特征生成最终的分类结果。

  • 可扩展性

  • OS-block 能轻松结合其他深度学习结构(如残差连接、Transformer、膨胀卷积等),以增强模型的灵活性和性能。

即插即用模块作用

OS_block 作为一个即插即用模块

  • 时间序列分类任务

    • 单变量多变量时间序列数据(如金融、医疗、物联网传感器数据等)。

    • 特别适合特征提取依赖不同时间尺度的场景,例如健康监测、活动识别、语音分类等。

  • 复杂时间尺度特征的场景

    • 当数据中存在多种时间尺度或隐藏周期性(如季节性、短期和长期模式)时,OS-block 能够捕获多尺度特征。

  • 无感受野搜索的场景

    • 在需要避免复杂参数调节或超参数搜索的情况下(如感受野大小、卷积核配置等),OS-block 能自动覆盖所有可能的感受野。

  • 需要模块化扩展的深度学习任务

    • 可与主流网络(如 Transformer、ResNet、Inception)集成,用于增强模型的时间尺度处理能力。

    • 在具有不同输入特征尺度的模型中,如视频分类(时间维度)、音频分析和其他序列数据建模。

消融实验结果

  • 内容:在多个基准数据集上的性能对比,包括 MEG-TLE、UEA 30、UCR 85 和 UCR 128 数据集。

  • 说明

    • OS-CNN 在多个数据集中表现优于其他方法,并且模型参数量显著减少。

    • 结果表明,OS-block 的设计不仅提高了分类准确率,还降低了模型复杂度。

即插即用模块

import torch
import torch.nn as nn
import torch.nn.functional as F
import math
import numpy as np


def calculate_mask_index(kernel_length_now, largest_kernel_lenght):
    right_zero_mast_length = math.ceil((largest_kernel_lenght - 1) / 2) - math.ceil((kernel_length_now - 1) / 2)
    left_zero_mask_length = largest_kernel_lenght - kernel_length_now - right_zero_mast_length
    return left_zero_mask_length, left_zero_mask_length + kernel_length_now


def creat_mask(number_of_input_channel, number_of_output_channel, kernel_length_now, largest_kernel_lenght):
    ind_left, ind_right = calculate_mask_index(kernel_length_now, largest_kernel_lenght)
    mask = np.ones((number_of_input_channel, number_of_output_channel, largest_kernel_lenght))
    mask[:, :, 0:ind_left] = 0
    mask[:, :, ind_right:] = 0
    return mask


def creak_layer_mask(layer_parameter_list):
    largest_kernel_lenght = layer_parameter_list[-1][-1]
    mask_list = []
    init_weight_list = []
    bias_list = []
    for i in layer_parameter_list:
        conv = torch.nn.Conv1d(in_channels=i[0], out_channels=i[1], kernel_size=i[2])
        ind_l, ind_r = calculate_mask_index(i[2], largest_kernel_lenght)
        big_weight = np.zeros((i[1], i[0], largest_kernel_lenght))
        big_weight[:, :, ind_l:ind_r] = conv.weight.detach().numpy()

        bias_list.append(conv.bias.detach().numpy())
        init_weight_list.append(big_weight)

        mask = creat_mask(i[1], i[0], i[2], largest_kernel_lenght)
        mask_list.append(mask)

    mask = np.concatenate(mask_list, axis=0)
    init_weight = np.concatenate(init_weight_list, axis=0)
    init_bias = np.concatenate(bias_list, axis=0)
    return mask.astype(np.float32), init_weight.astype(np.float32), init_bias.astype(np.float32)


class build_layer_with_layer_parameter(nn.Module):
    def __init__(self, layer_parameters, relu_or_not_at_last_layer=True):
        super(build_layer_with_layer_parameter, self).__init__()
        self.relu_or_not_at_last_layer = relu_or_not_at_last_layer

        os_mask, init_weight, init_bias = creak_layer_mask(layer_parameters)

        in_channels = os_mask.shape[1]
        out_channels = os_mask.shape[0]
        max_kernel_size = os_mask.shape[-1]

        self.weight_mask = nn.Parameter(torch.from_numpy(os_mask), requires_grad=False)

        self.padding = nn.ConstantPad1d((int((max_kernel_size - 1) / 2), int(max_kernel_size / 2)), 0)

        self.conv1d = torch.nn.Conv1d(in_channels=in_channels, out_channels=out_channels, kernel_size=max_kernel_size)
        self.conv1d.weight = nn.Parameter(torch.from_numpy(init_weight), requires_grad=True)
        self.conv1d.bias = nn.Parameter(torch.from_numpy(init_bias), requires_grad=True)

        self.bn = nn.BatchNorm1d(num_features=out_channels)

    def forward(self, X):
        self.conv1d.weight.data = self.conv1d.weight * self.weight_mask
        # self.conv1d.weight.data.mul_(self.weight_mask)
        result_1 = self.padding(X)
        result_2 = self.conv1d(result_1)
        result_3 = self.bn(result_2)
        if self.relu_or_not_at_last_layer:
            result = F.relu(result_3)
            return result
        else:
            return result_3


class OS_block(nn.Module):
    def __init__(self, layer_parameter_list, relu_or_not_at_last_layer=True):
        super(OS_block, self).__init__()
        self.layer_parameter_list = layer_parameter_list
        self.layer_list = []
        self.relu_or_not_at_last_layer = relu_or_not_at_last_layer

        for i in range(len(layer_parameter_list)):
            if i != len(layer_parameter_list) - 1:
                using_relu = True
            else:
                using_relu = self.relu_or_not_at_last_layer

            layer = build_layer_with_layer_parameter(layer_parameter_list[i], using_relu)
            self.layer_list.append(layer)

        self.net = nn.Sequential(*self.layer_list)

    def forward(self, X):

        X = self.net(X)

        return X


if __name__ == '__main__':

    layer_parameter_list = [
        [(16, 32, 3)],
        [(32, 16, 5)],
        [(16, 16, 7)]
    ]

    input = torch.rand(10, 16, 100)
    block = OS_block(layer_parameter_list=layer_parameter_list, relu_or_not_at_last_layer=True)
    output = block(input)
    print(input.size())    print(output.size())

便捷下载方式

浏览打开网址:https://github.com/ai-dawang/PlugNPlay-Modules

更多分析可见原文


ai缝合大王
聚焦AI前沿,分享相关技术、论文,研究生自救指南
 最新文章