如何在R的tabnet包中将自定义损失设为平衡准确率?
在TabNet中自定义多分类平衡准确率损失函数
直接用yardstick::bal_accuracy_vec报错的原因很明确:它是模型评估指标,不是训练用的可微分损失函数。TabNet训练需要的是能计算梯度、支持反向传播的张量操作函数,而bal_accuracy_vec处理的是普通R向量,无法参与梯度更新,所以必然报错。
平衡准确率的核心是「各类别召回率的平均值」,我们可以把它转换成损失——用1 - 平衡准确率作为损失(训练的目标是最小化损失,平衡准确率越高,损失就越小)。下面是适配TabNet的完整实现方案:
步骤1:编写自定义损失函数
TabNet的自定义损失函数必须接收两个张量参数:y_true(真实标签的one-hot编码张量)和y_pred(模型输出的概率张量),返回一个标量损失值。我们用tensorflow的张量操作实现:
library(tabnet) library(recipes) library(tensorflow) library(modeldata) # 自定义平衡准确率损失函数 bal_acc_loss <- function(y_true, y_pred) { # 从one-hot标签中提取真实类别索引 y_true_class <- tf$argmax(y_true, axis = 1L) # 从预测概率中提取预测类别索引 y_pred_class <- tf$argmax(y_pred, axis = 1L) # 获取数据中的所有类别 classes <- tf$unique(y_true_class)$y num_classes <- tf$size(classes) # 计算每个类别的召回率 recall_per_class <- tf$map_fn( function(cls) { # 统计该类别的真实正例数 true_positives <- tf$reduce_sum(tf$cast(tf$logical_and(tf$equal(y_true_class, cls), tf$equal(y_pred_class, cls)), tf$float32)) # 统计该类别的总真实样本数 total_true <- tf$reduce_sum(tf$cast(tf$equal(y_true_class, cls), tf$float32)) # 避免除以0,添加极小值偏移 recall <- true_positives / (total_true + tf$keras$backend$epsilon()) recall }, classes, dtype = tf$float32 ) # 计算平衡准确率,转成损失(1 - 平衡准确率) bal_acc <- tf$reduce_mean(recall_per_class) loss <- 1 - bal_acc loss }
步骤2:使用自定义损失训练TabNet
注意确保多分类结果变量是因子类型,TabNet会自动处理为one-hot编码。完整训练代码如下:
data("attrition", package = "modeldata") ids <- sample(nrow(attrition), 256) # 将多分类结果转成因子 attrition$JobSatisfaction <- as.factor(attrition$JobSatisfaction) # 构建预处理配方 rec <- recipe(Attrition + JobSatisfaction ~ ., data = attrition[ids, ]) %>% step_normalize(all_numeric(), -all_outcomes()) # 传入自定义损失训练模型 attrition_fit <- tabnet_fit(rec, data = attrition[ids, ], epochs = 2, valid_split = 0.2, loss = bal_acc_loss )
关键细节说明
- 必须用张量操作实现:TabNet底层依赖TensorFlow,只有张量运算才能支持梯度计算和反向传播,普通R向量函数无法满足训练需求。
- 防除以0处理:添加
tf$keras$backend$epsilon()避免某类别无样本时出现计算错误。 - 通用性:这个函数同时支持二分类和多分类任务,二分类场景下平衡准确率等价于召回率。
内容的提问来源于stack exchange,提问作者Comevussor
相关产品推荐
相关产品推荐

