TensorFlow中特征清洗需用tf.data.map内纯TF操作,而非Pandas;tf.feature_column仅声明格式不执行转换;数值列须预填充NaN,类别列需预清洗并匹配vocab size,Normalization层参数须固化保存。

TensorFlow 本身不提供 Pandas 那样的结构化数据清洗接口,直接用 tf.data 处理原始 CSV 或 DataFrame 容易踩坑——尤其是缺失值、类别编码、数值归一化混在一起时,模型训练会静默失败或收敛异常。
用 tf.feature_column 做特征定义,而不是预处理
很多人误以为 tf.feature_column 是“做特征工程”的工具,其实它只是把已有字段声明为模型可读的输入格式。它不执行实际转换(比如填缺失值、one-hot 编码),只在 tf.keras.layers.DenseFeatures 层中触发运行时转换。
-
tf.feature_column.categorical_column_with_vocabulary_list要求输入已经是字符串或整数,不会自动把None或空字符串转成UNK——你得先用 Pandas 或tf.strings.regex_replace清洗 -
tf.feature_column.numeric_column对NaN敏感:传入含NaN的张量会报InvalidArgumentError: Nan in summary histogram,必须提前填充(如用中位数) - 所有
feature_column的default_value参数只对tf.parse_example解析 TFRecord 时生效,对直接喂pd.DataFrame无效
把清洗逻辑写进 tf.data.Dataset.map,而非 Python 循环
用 Pandas 先清洗再转 tf.data.Dataset 看似简单,但无法和分布式训练、自动分片、prefetch 流水线对齐;而把清洗逻辑塞进 map 函数里,能保证每个样本在送入模型前都经过一致处理。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 用
tf.py_function包裹 Pandas 操作(例如df.fillna())可行,但会退出图模式,损失性能且不能导出 SavedModel - 推荐组合使用
tf.strings.to_number、tf.where、tf.math.is_nan实现纯 TensorFlow 清洗,例如:def clean_age(x): x = tf.strings.to_number(x, out_type=tf.float32) x = tf.where(tf.math.is_nan(x), tf.constant(35.0), x) return tf.clip_by_value(x, 0.0, 100.0)
- 类别列做
tf.lookup.StaticHashTable映射比categorical_column_with_vocabulary_list更可控,尤其当训练/推理 vocab 不一致时
数值特征标准化必须在训练集上 fit,且保存参数
tf.keras.layers.Normalization 默认是训练时动态统计均值/方差,但部署时若用新数据调用 adapt() 会覆盖原参数,导致线上预测偏移。
立即学习“Python免费学习笔记(深入)”;
- 必须显式调用
layer.adapt(train_dataset.map(lambda x, y: x["age"])),并在保存模型前用model.save(..., save_format="tf")连带 layer 状态一起固化 - 避免在
map中用np.mean()或tf.reduce_mean实时计算——这会让每个 batch 的归一化基准不同,破坏训练稳定性 - 如果用 Scikit-learn 的
StandardScaler预处理,记得把scaler.mean_和scaler.scale_写进tf.constant,再封装成自定义层,否则 SavedModel 加载后无法复现变换
最常被忽略的一点:tf.feature_column 的嵌入维度(dimension)和 tf.keras.layers.Embedding 的 input_dim 必须严格对应 vocab size + 1(含 default_value 占位符),否则 lookup 时索引越界,错误信息只显示 Invalid argument: indices[0] = 12345 is not in [0, 1000)——这个 1000 是你没意识到的隐式截断结果。

















