模型Loss逐轮下降但准确率为0,求客户产品预测模型优化方案
问题描述
我试图预测客户将兑换的产品,使用如下表格数据:
| id_customer | product_name | item_quantity | product_name_index | product_category_index | product_category | product_type | seller_name |
|---|---|---|---|---|---|---|---|
| 1234 | tv | 1 | 40391 | 212 | eletronics | 4 | 3 |
| 12345 | smart tv | 1 | 16347 | 212 | eletronics | 4 | 5 |
| 123456 | tv | 1 | 40391 | 212 | eletronics | 4 | 3 |
| 1234567 | security camera | 1 | 1449 | 29 | eletronics | 2 | 6 |
| 12345678 | soda | 1 | 37969 | 174 | food | 3 | 38 |
模型Loss逐轮下降,但准确率始终为0,训练日志如下:
Epoch 2/15
114/114 [==============================] - 4s 26ms/step - loss: 83.7829 - accuracy: 0.0000e+00 - val_loss: 54.7766 - val_accuracy: 0.0000e+00Epoch 2/15
114/114 [==============================] - 3s 24ms/step - loss: 54.5661 - accuracy: >0.0000e+00 - val_loss: 54.7244 - val_accuracy: 0.0000e+00
以下是构建模型的代码,请问应如何修改以获得正确的预测结果?
embedding_size = 50 min_redemption = min(df_filtered['product_category_index'].value_counts()) max_redemption = max(df_filtered['product_category_index'].value_counts()) num_customers = len(df_filtered['id_customer'].drop_duplicates()) num_products = len(df_filtered['product_category_index'].drop_duplicates()) df = df_filtered.sample(frac=1, random_state=42) X = df.drop(['product_category_index'],axis=1) y = df['product_category_index'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state = 0) scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) class RecommenderNet(Model): def __init__(self, num_customers, num_products, embedding_size, **kwargs): super(RecommenderNet, self).__init__(**kwargs) self.num_customers = num_customers self.num_products = num_products self.embedding_size = embedding_size self.customer_embedding = Embedding( num_customers, embedding_size, embeddings_initializer="he_normal", embeddings_regularizer=l2(1e-6), ) self.customer_bias = Embedding(num_customers, 1) self.product_embedding = Embedding( num_products, embedding_size, embeddings_initializer="he_normal", embeddings_regularizer=l2(1e-6), ) self.product_bias = Embedding(num_products, 1) def call(self, inputs): customer_vector = self.customer_embedding(inputs[:, 0]) customer_bias = self.customer_bias(inputs[:, 0]) product_vector = self.product_embedding(inputs[:, 1]) product_bias = self.product_bias(inputs[:, 1]) dot_customer_product = tensordot(customer_vector, product_vector, 2) # Add all the components (including bias) x = dot_customer_product + customer_bias + product_bias return relu(x) model = RecommenderNet(num_customers,num_products, embedding_size) model.compile(loss='mae', optimizer='adam', metrics='accuracy')
问题诊断与修改方案
核心问题分析
- 输入与模型不匹配:你的
RecommenderNet设计为处理客户、产品的离散整数索引,但你将所有特征标准化为连续值,且输入包含多列特征,模型仅取前两列,完全不符合嵌入层的输入要求(嵌入层需要非负整数索引)。 - 任务与损失/输出不匹配:这是多分类任务(预测产品类别索引),但你使用了回归任务的
mae损失,输出用relu激活,无法生成分类所需的概率分布,导致准确率计算失效。 - 特征编码错误:
id_customer是离散ID,需编码为从0开始的连续整数才能被嵌入层处理,直接使用原ID或标准化都会导致嵌入层失效。
修改步骤
1. 修正数据编码与输入处理
import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # 对客户ID和产品类别索引进行连续整数编码 customer_encoder = LabelEncoder() df_filtered['customer_idx'] = customer_encoder.fit_transform(df_filtered['id_customer']) product_encoder = LabelEncoder() df_filtered['product_category_idx'] = product_encoder.fit_transform(df_filtered['product_category_index']) # 采样并构造输入输出 df = df_filtered.sample(frac=1, random_state=42) # 模型输入仅保留客户索引(如需加入其他特征,需单独处理嵌入) X = df[['customer_idx']].values y = df['product_category_idx'].values # 划分数据集,无需标准化离散索引 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
2. 调整模型结构适配多分类任务
from tensorflow.keras import Model from tensorflow.keras.layers import Embedding, Dense, Flatten from tensorflow.keras.regularizers import l2 embedding_size = 50 num_customers = len(customer_encoder.classes_) num_products = len(product_encoder.classes_) class RecommenderNet(Model): def __init__(self, num_customers, num_products, embedding_size, **kwargs): super(RecommenderNet, self).__init__(**kwargs) self.customer_embedding = Embedding( num_customers, embedding_size, embeddings_initializer="he_normal", embeddings_regularizer=l2(1e-6), ) self.customer_bias = Embedding(num_customers, 1) # 加入全连接层将嵌入映射到分类空间 self.dense = Dense(64, activation='relu') self.output_layer = Dense(num_products, activation='softmax') def call(self, inputs): customer_vector = self.customer_embedding(inputs) customer_bias = self.customer_bias(inputs) # 展平嵌入向量 x = Flatten()(customer_vector) x = self.dense(x) # 加入偏置并输出分类概率 x = self.output_layer(x + Flatten()(customer_bias)) return x model = RecommenderNet(num_customers, num_products, embedding_size) # 多分类任务使用稀疏交叉熵损失(y为整数索引) model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
3. 可选:加入其他类别特征
如果要使用product_type、seller_name等历史交互特征,需对每个类别特征单独做嵌入,再拼接后输入全连接层:
from tensorflow.keras.layers import concatenate # 先对product_type编码 type_encoder = LabelEncoder() df_filtered['product_type_idx'] = type_encoder.fit_transform(df_filtered['product_type']) num_product_types = len(type_encoder.classes_) class RecommenderNet(Model): def __init__(self, num_customers, num_products, num_product_types, embedding_size, **kwargs): super(RecommenderNet, self).__init__(**kwargs) self.customer_embedding = Embedding(num_customers, embedding_size, embeddings_regularizer=l2(1e-6)) self.product_type_embedding = Embedding(num_product_types, embedding_size//2, embeddings_regularizer=l2(1e-6)) self.customer_bias = Embedding(num_customers, 1) self.dense1 = Dense(128, activation='relu') self.dense2 = Dense(64, activation='relu') self.output_layer = Dense(num_products, activation='softmax') def call(self, inputs): customer_vec = Flatten()(self.customer_embedding(inputs[:,0])) product_type_vec = Flatten()(self.product_type_embedding(inputs[:,1])) customer_bias = Flatten()(self.customer_bias(inputs[:,0])) # 拼接所有特征 x = concatenate([customer_vec, product_type_vec]) x = self.dense1(x) x = self.dense2(x) x = self.output_layer(x + customer_bias) return x # 构造包含多特征的输入 X = df[['customer_idx', 'product_type_idx']].values model = RecommenderNet(num_customers, num_products, num_product_types, embedding_size) model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
其他注意事项
- 若任务是协同过滤推荐(预测用户是否兑换某产品),需构造用户-产品对输入,目标为二元标签,损失改用
binary_crossentropy,输出用sigmoid。 - 检查数据类别平衡:若部分产品类别样本极少,可通过过采样、欠采样或设置类别权重解决。
内容的提问来源于stack exchange,提问作者Ueslei Sutil
相关产品推荐
相关产品推荐

