如何在Keras模型中正确使用独热编码数据作为输入
解决多热编码特征与数值特征结合的Keras模型输入问题
问题根源分析
你遇到的错误核心来自两点:
- 输入形状与模型定义不匹配:模型中
cat_input定义为shape=()的int32类型,但实际传入的是多热编码后的二维浮点数组(形状为(样本数, 类别数));同时模型期望字典输入时,你传入了列表格式,导致输入张量数量不匹配。 - 变长类别序列处理不当:直接将原始变长类别列表传入模型时,因每个样本的类别数量不一致,拼接时出现维度不匹配。
解决方案一:预处理多热编码后传入模型
先完成多热编码预处理,再根据编码后的特征形状定义模型输入,确保输入格式与模型要求一致。
步骤1:修正多热编码预处理
将编码结果转为numpy数组存储,避免DataFrame中存Tensor导致的格式问题:
import pandas as pd import numpy as np import tensorflow as tf # 创建数据集 df = pd.DataFrame( { 'val': [1, 12, 123, 512, 12], 'cat': [["A", "B"], ["A"], ["C"], ["C", "A", "B"], ["A"]], 'label': [0,1,1,0,0] }) # 提取所有类别 cat_list = list(set([item.strip() for sublist in df['cat'] for item in sublist])) # 初始化编码层 index = tf.keras.layers.StringLookup(vocabulary=cat_list) encoder = tf.keras.layers.CategoryEncoding(num_tokens=index.vocabulary_size(), output_mode="multi_hot") # 编码并转为numpy数组 df['cat_encoded'] = df['cat'].apply(lambda x: encoder(index(x)).numpy()) # 堆叠为二维数组(样本数 × 类别数) cat_encoded_np = np.vstack(df['cat_encoded'])
步骤2:定义匹配的模型
根据预处理后的特征形状定义输入层,数值特征val是标量输入,多热编码特征是固定长度的向量输入:
# 数值特征输入 val_input = tf.keras.layers.Input(shape=(), name="val", dtype=tf.int32) # 多热编码特征输入,形状为类别数 cat_input = tf.keras.layers.Input(shape=(len(cat_list),), name="cat", dtype=tf.float32) # 拼接特征 concat_layer = tf.keras.layers.Concatenate(axis=-1)([val_input, cat_input]) # 添加分类输出层(根据任务调整) output_layer = tf.keras.layers.Dense(1, activation='sigmoid')(concat_layer) # 构建模型(支持列表或字典输入) model = tf.keras.Model(inputs=[val_input, cat_input], outputs=output_layer)
步骤3:训练模型
传入与模型输入匹配的数据格式(列表或字典):
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 列表格式输入 model.fit( x=[df['val'].values, cat_encoded_np], y=df['label'].values, epochs=5, batch_size=2 ) # 或字典格式输入(对应模型inputs用字典定义的情况) # model = tf.keras.Model(inputs={"val": val_input, "cat": cat_input}, outputs=output_layer) # model.fit( # x={"val": df['val'].values, "cat": cat_encoded_np}, # y=df['label'].values, # epochs=5 # )
解决方案二:将编码层嵌入模型(推荐)
把多热编码逻辑整合到模型内部,避免预处理与模型部署时的不一致问题,同时自动处理变长类别序列。
步骤1:定义包含编码逻辑的模型
利用StringLookup和CategoryEncoding层直接处理原始变长类别输入:
import pandas as pd import tensorflow as tf df = pd.DataFrame( { 'val': [1, 12, 123, 512, 12], 'cat': [["A", "B"], ["A"], ["C"], ["C", "A", "B"], ["A"]], 'label': [0,1,1,0,0] }) # 数值特征输入 val_input = tf.keras.layers.Input(shape=(), name="val", dtype=tf.int32) # 原始类别输入:变长序列,shape=(None,) cat_input = tf.keras.layers.Input(shape=(None,), name="cat", dtype=tf.string) # 自动学习词汇表(替代手动提取cat_list) index_layer = tf.keras.layers.StringLookup(output_mode='int') index_layer.adapt(df['cat'].explode()) # 展开列表以学习所有类别 # 多热编码层 encoding_layer = tf.keras.layers.CategoryEncoding( num_tokens=index_layer.vocabulary_size(), output_mode="multi_hot" ) # 处理类别输入 processed_cat = encoding_layer(index_layer(cat_input)) # 拼接并构建输出 concat_layer = tf.keras.layers.Concatenate(axis=-1)([val_input, processed_cat]) output_layer = tf.keras.layers.Dense(1, activation='sigmoid')(concat_layer) model = tf.keras.Model(inputs=[val_input, cat_input], outputs=output_layer)
步骤2:训练模型
直接传入原始数据,模型内部完成编码:
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 原始类别数据转为列表格式传入 model.fit( x=[df['val'].values, df['cat'].tolist()], y=df['label'].values, epochs=5, batch_size=2 )
内容的提问来源于stack exchange,提问作者fsulser
相关产品推荐
相关产品推荐

