自定义Keras损失函数中集成tf.where与odds的实现难题
自定义Keras投注盈亏损失函数实现
核心逻辑梳理
我们需要基于实际赛果、模型预测结果、主队获胜赔率计算投注盈亏,并将其转化为可用于训练的损失函数(模型最小化损失等价于最大化盈利)。常见的投注盈亏逻辑如下:
- 若预测主队获胜且实际主队获胜:盈利 = (赔率 - 1) × 投注额
- 若预测主队获胜但实际客队获胜:亏损 = 1 × 投注额
- 未投注时盈亏为0
解决KerasTensor输入报错问题
之前使用tf.where触发ValueError,本质是损失函数中混合了非张量操作,或未正确处理Keras的张量类型。解决方法是全程使用TensorFlow张量操作,避免Python原生条件判断。
集成赔率的两种实现方式
方式1:将赔率作为模型额外输入
适合需要单独处理特征与赔率的场景:
import tensorflow as tf # 定义带赔率参数的损失函数 def betting_loss(odds_tensor): def loss(y_true, y_pred): # 1. 生成投注掩码:预测主队获胜概率≥0.5时投注(可根据需求调整阈值) bet_mask = tf.cast(y_pred >= 0.5, tf.float32) # 2. 计算单样本盈亏:主胜时盈利(odds-1)*投注,客胜时亏损1*投注 profit = tf.where( y_true == 1, (odds_tensor - 1) * bet_mask, -1 * bet_mask ) # 3. 损失取总盈利的负值(最小化损失=最大化盈利) return -tf.reduce_mean(profit) return loss # 构建包含赔率输入的模型 feature_input = tf.keras.Input(shape=(10,)) # 假设特征维度为10 odds_input = tf.keras.Input(shape=(1,)) x = tf.keras.layers.Dense(64, activation='relu')(feature_input) output = tf.keras.layers.Dense(1, activation='sigmoid')(x) # 输出主队获胜概率 model = tf.keras.Model(inputs=[feature_input, odds_input], outputs=output) model.compile(optimizer='adam', loss=betting_loss(odds_input)) # 训练示例 import numpy as np X_train = np.random.rand(1000, 10) odds_train = np.random.uniform(1.5, 5, (1000, 1)) y_train = np.random.randint(0, 2, (1000, 1)) model.fit([X_train, odds_train], y_train, epochs=10, batch_size=32)
方式2:将赔率并入真实标签
适合简化模型输入的场景,把真实赛果和赔率打包成二维标签:
import tensorflow as tf def betting_loss(y_true, y_pred): # 拆分真实标签:第一列是实际赛果,第二列是主队赔率 actual_result = y_true[:, 0] odds = y_true[:, 1] # 用预测概率作为投注权重(更平滑,适合梯度下降) profit = y_pred * tf.where(actual_result == 1, (odds - 1), -1) # 损失取平均盈利的负值 return -tf.reduce_mean(profit) # 构建普通单输入模型 inputs = tf.keras.Input(shape=(10,)) x = tf.keras.layers.Dense(64, activation='relu')(inputs) output = tf.keras.layers.Dense(1, activation='sigmoid')(inputs) model = tf.keras.Model(inputs=inputs, outputs=output) model.compile(optimizer='adam', loss=betting_loss) # 训练示例 X_train = np.random.rand(1000, 10) # 真实标签:[实际赛果, 主队赔率] y_train = np.hstack([np.random.randint(0,2,(1000,1)), np.random.uniform(1.5,5,(1000,1))]) model.fit(X_train, y_train, epochs=10, batch_size=32)
关键注意点
- 若追求更平滑的梯度,建议用
y_pred(预测概率)直接作为投注权重,而非固定阈值的硬掩码 - 所有计算必须使用TensorFlow张量操作(如
tf.where、tf.cast),避免Python的if/else或循环 - 损失函数返回的是平均盈利的负值,确保模型训练时朝着最大化盈利的方向优化
内容的提问来源于stack exchange,提问作者KaiHoogenhoud
相关产品推荐
相关产品推荐

