如何在TensorFlow时间序列中按窗口最后元素归一化数据
针对每个时间窗口按最后元素归一化的TensorFlow实现方法
你需要的是对每个生成的时间窗口,以输入窗口的最后一个元素为基准进行归一化,让输入序列和目标值都转换成相对于该基准的比例值,这样模型学习的就是相对变化趋势。可以通过在split_window之后添加一个自定义的归一化映射函数来实现,完全保留窗口结构且无需提前全局归一化。
具体实现步骤
定义归一化函数
这个函数接收拆分后的输入窗口和目标值,提取输入窗口的最后一个元素作为基准,对输入和目标做除法运算:def normalize_by_last_element(self, inputs, labels): # 提取输入窗口的最后一个元素,保持维度以支持广播运算 window_last_val = inputs[:, -1:] # 防止除以0,添加极小值epsilon window_last_val = tf.maximum(window_last_val, 1e-8) # 对输入序列和目标值分别做归一化 normalized_inputs = inputs / window_last_val normalized_labels = labels / window_last_val return normalized_inputs, normalized_labels修改
make_dataset函数
在拆分窗口的map操作之后,再链式调用归一化函数的map,确保每个窗口都被独立处理:def make_dataset(self, data): data = np.array(data, dtype=np.float32) ds = tf.keras.utils.timeseries_dataset_from_array( data=data, targets=None, sequence_length=self.total_window_size, sequence_stride=1, shuffle=True, batch_size=32,) # 先拆分窗口为输入和目标,再做窗口内归一化 ds = ds.map(self.split_window).map(self.normalize_by_last_element) return ds确保
split_window的正确性
假设你的split_window是用来拆分输入窗口和目标的,比如total_window_size = input_width + label_width,示例实现如下:def split_window(self, window): # input_width是输入序列长度,label_width是目标长度(比如1) inputs = window[:-self.label_width] labels = window[-self.label_width:] # 为输入和目标添加特征维度(适配TensorFlow模型输入要求) inputs = tf.expand_dims(inputs, axis=-1) labels = tf.expand_dims(labels, axis=-1) return inputs, labels
效果验证
以你给出的例子为例:
- 原始窗口数据:
[8,7,5,9,11,10,12](total_window_size=7,input_width=6,label_width=1) split_window拆分后:输入[8,7,5,9,11,10],目标[12]- 归一化后:输入除以10得到
[0.8,0.7,0.5,0.9,1.1,1.0],目标除以10得到[1.2],完全符合你的需求。
注意事项
- 加入
tf.maximum(window_last_val, 1e-8)是为了避免极端情况下窗口最后一个元素为0导致的除以0错误,你可以根据数据实际情况调整epsilon的大小。 - 这种窗口内的归一化是完全独立的,每个窗口都用自己的基准值,不会破坏窗口结构,也不会引入全局数据的统计信息,适合你的场景。
内容的提问来源于stack exchange,提问作者MaltDew
相关产品推荐
相关产品推荐

