如何在自定义神经网络层中将形状为(None,1)的输出重塑为滑动窗口式时间序列形状
嘿,这个需求其实就是经典的滑动窗口序列重塑问题,完全不用手动写循环折腾,直接用深度学习框架的原生操作就能搞定!我分TensorFlow/Keras和PyTorch两种主流框架给你写好自定义层的实现:
TensorFlow/Keras 自定义层实现
TensorFlow自带tf.signal.frame函数,专门用来生成滑动窗口,非常适合这个场景。自定义层的实现逻辑很清晰:
import tensorflow as tf from tensorflow.keras.layers import Layer class SlidingWindowLayer(Layer): def __init__(self, window_size=3, step_size=1, **kwargs): super().__init__(**kwargs) self.window_size = window_size # 窗口大小,这里是3 self.step_size = step_size # 滑动步长,这里是1 def call(self, inputs): # 输入形状是 (序列长度, 1) 或 (批量大小, 序列长度, 1),先去掉最后一维的冗余维度 inputs_squeezed = tf.squeeze(inputs, axis=-1) # 生成滑动窗口,结果形状为 (批量大小, 窗口数量, 窗口大小)(如果是单样本则是(窗口数量, 窗口大小)) windows = tf.signal.frame( inputs_squeezed, frame_length=self.window_size, frame_step=self.step_size ) return windows # 测试一下效果 test_input = tf.constant([[1],[2],[3],[4],[5],[6],[7]], dtype=tf.float32) layer = SlidingWindowLayer() output = layer(test_input) print(output.numpy())
代码说明:
tf.squeeze把输入从(N,1)转成(N,)的一维序列,方便后续窗口生成tf.signal.frame会自动计算生成的窗口数量:窗口数量 = 序列长度 - 窗口大小 + 步长,这里7-3+1=5,刚好对应你要的5个窗口- 支持批量输入,如果你的输入是
(batch_size, seq_len, 1),这个层也能直接处理,输出会是(batch_size, num_windows, 3)
PyTorch 自定义层实现
PyTorch里可以用两种方式实现:一种是用unfold(更直观),另一种是用as_strided(更高效,直接创建视图不复制数据)。
方式1:用unfold实现
import torch import torch.nn as nn class SlidingWindowLayer(nn.Module): def __init__(self, window_size=3, step_size=1): super().__init__() self.window_size = window_size self.step_size = step_size def forward(self, inputs): # 输入形状是 (序列长度, 1),先调整为unfold需要的三维格式:(1, 1, 序列长度) inputs_reshaped = inputs.permute(1, 0).unsqueeze(0) # 在最后一维上滑动生成窗口 windows = inputs_reshaped.unfold( dimension=2, size=self.window_size, step=self.step_size ) # 去掉冗余维度,得到最终形状 (窗口数量, 窗口大小) windows = windows.squeeze(0).squeeze(0) return windows # 测试 test_input = torch.tensor([[1],[2],[3],[4],[5],[6],[7]], dtype=torch.float32) layer = SlidingWindowLayer() output = layer(test_input) print(output.numpy())
方式2:用as_strided实现(高效版)
import torch import torch.nn as nn class SlidingWindowLayer(nn.Module): def __init__(self, window_size=3, step_size=1): super().__init__() self.window_size = window_size self.step_size = step_size def forward(self, inputs): # 输入形状 (序列长度,1),转成一维序列 inputs_squeezed = inputs.squeeze(-1) seq_len = inputs_squeezed.size(0) num_windows = seq_len - self.window_size + self.step_size # 边界判断:如果序列长度小于窗口大小,返回空张量或抛出提示 if num_windows <= 0: return torch.empty(0, self.window_size, dtype=inputs.dtype) # 直接创建滑动窗口视图,不复制数据,效率极高 windows = inputs_squeezed.as_strided( size=(num_windows, self.window_size), stride=(inputs_squeezed.stride(0), inputs_squeezed.stride(0)) ) return windows
代码说明:
unfold是PyTorch专门用于滑动窗口采样的API,原本针对图像,但调整维度后也能处理一维序列as_strided直接操作张量的内存布局,生成视图而非复制数据,适合对性能要求高的场景,但要注意边界判断,避免越界
内容的提问来源于stack exchange,提问作者aez




