如何在tf.keras模型内对多个特征使用Normalization层
多数值特征下Keras内置预处理的实现方案
你对「模型内预处理」的理解完全准确:把特征变换逻辑嵌入模型结构的写法,能彻底避免训练、推理阶段预处理逻辑不一致的问题,部署时不需要额外维护独立的预处理脚本,是生产场景的推荐实现方式。
针对你提到的多数值特征场景,实现逻辑和单特征场景几乎一致,不需要复杂改造,核心注意点和可运行代码如下:
全数值特征场景实现(适配你现有代码)
如果你的输入全部是类似X、Y、Z的数值型特征,直接调整Normalization层的参数即可,不需要使用函数式API:
- 初始化归一化层时,将输入维度设置为训练集的特征总数,去掉
axis=None参数(默认沿特征维度独立计算每个特征的均值、标准差,正好匹配多特征独立归一化的需求) - 仅使用训练集特征数据调用
adapt()方法,让归一化层学习统计量,禁止使用验证集、测试集数据执行该步骤,避免数据泄露 - 将归一化层作为模型的第一层,后续直接拼接原有全连接结构即可
注意你原有代码存在笔误:验证集的
X_val、y_val错误读取了训练集数据,实际需要从val_data中提取,否则验证结果完全无效。
修正后的完整可运行代码:
import pandas as pd import tensorflow as tf from tensorflow.keras import Sequential from tensorflow.keras.layers import Dense, Normalization from tensorflow.keras.optimizers import Adam # 数据读取 train_data = pd.read_csv('train.csv') val_data = pd.read_csv('val.csv') target_name = 'ze_target' y_train = train_data[target_name] X_train = train_data.drop(target_name, axis=1) # 修正原有验证集读取错误 y_val = val_data[target_name] X_val = val_data.drop(target_name, axis=1) def create_model(X_train): # 初始化多特征归一化层 feature_normalizer = Normalization(input_shape=[X_train.shape[1], ]) # 用训练集适配归一化统计量 feature_normalizer.adapt(X_train) model = Sequential([ feature_normalizer, Dense(20, activation='relu'), Dense(20, activation='relu'), Dense(20, activation='relu'), Dense(1) ]) model.compile(optimizer=Adam(learning_rate=0.0001), loss='mse') return model model = create_model(X_train) model.summary() model.fit(X_train, y_train, validation_data=(X_val, y_val), batch_size=128, epochs=30)
混合特征场景扩展(函数式API实现)
如果后续你需要同时处理数值、类别等不同类型的特征,可以用Keras函数式API给不同类型特征单独配置预处理逻辑,最后拼接输入全连接层,简单示例如下:
from tensorflow.keras import Input, Model from tensorflow.keras.layers import StringLookup, Concatenate # 定义数值特征预处理分支 numeric_cols = ['X', 'Y', 'Z'] numeric_input = Input(shape=(len(numeric_cols),), name='numeric_feats') numeric_norm = Normalization() numeric_norm.adapt(X_train[numeric_cols]) numeric_processed = numeric_norm(numeric_input) # 定义类别特征预处理分支 cat_cols = ['cat1', 'cat2'] cat_input = Input(shape=(len(cat_cols),), name='cat_feats', dtype=tf.string) cat_encoder = StringLookup(output_mode='one_hot') cat_encoder.adapt(X_train[cat_cols]) cat_processed = cat_encoder(cat_input) # 拼接所有处理后的特征,接入全连接结构 all_feats = Concatenate()([numeric_processed, cat_processed]) x = Dense(20, activation='relu')(all_feats) x = Dense(20, activation='relu')(x) x = Dense(20, activation='relu')(x) output = Dense(1)(x) model = Model(inputs=[numeric_input, cat_input], outputs=output)
补充说明
- 模型保存时会自动存储预处理层学到的所有参数(比如归一化的均值、方差,类别特征的词表等),部署时直接加载模型传入原始特征即可,不需要额外执行预处理操作
- 原有代码中除第一层外的全连接层不需要重复指定
input_dim,框架会自动推导维度,重复写不会报错但属于冗余代码
内容的提问来源于stack exchange,提问作者cs0815
相关产品推荐
相关产品推荐

