You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TensorFlow MLP信用卡欺诈检测无有效结果技术求助

搞定信用卡欺诈检测MLP的“零识别”问题

我太懂这种挫败感了——面对99%都是正常交易的数据集,模型随便输出全0就能拿99%的Accuracy,但连一笔欺诈都识别不出来。咱们从概念根源和代码细节两个层面来拆解问题,一步步调整:

一、先搞懂核心问题:数据集不平衡的“欺骗性”

你的模型不是没学会,是学会了“偷懒”:正常交易占比太高,模型只要全预测Class=0,损失就会非常小,梯度下降会直接往这个方向走,完全忽略少数的欺诈样本。所以解决问题的核心不是调模型结构,而是强迫模型重视欺诈样本。

二、代码里的关键坑点与修复

1. 没做特征标准化 + 数据没打乱(致命!)

你看,Time和Amount的量级和V1-V28差了十万八千里,MLP对特征尺度特别敏感,大尺度特征会直接掩盖小尺度特征的模式。而且你设置了shuffle=False,如果数据集是按时间排序的,训练集可能全是前面的正常交易,模型连欺诈样本的影子都没见过!

修复代码:

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 第一步:标准化特征
scaler = StandardScaler()
# 只标准化特征列,不要碰标签
X_scaled = scaler.fit_transform(df[['Time','V1','V2','V3','V4','V5','V6','V7','V8','V9','V10','V11','V12','V13','V14','V15','V16','V17','V18','V19','V20','V21','V22','V23','V24','V25','V26','V27','V28','Amount']])

# 第二步:拆分数据集时打乱+分层,保证训练/测试集的欺诈比例一致
Y = df['Class'].values.reshape(-1, 1) # 改成单标签,二分类不需要classInverse
X_train, X_test, Y_train, Y_test = train_test_split(
    X_scaled, Y, test_size=0.3, shuffle=True, stratify=Y
)

2. 损失函数没加类别权重(最关键的调整)

你用的softmax_cross_entropy_with_logits_v2是平等对待所有样本的,但欺诈样本太少,模型根本不在乎它们的损失。必须给欺诈样本(Class=1)加高权重,让模型漏判欺诈时损失暴增。

修复代码:
首先计算类别权重:

# 计算权重:总样本数 / (2 * 该类样本数),让少数类权重远高于多数类
total_samples = len(Y_train)
class_0_count = (Y_train == 0).sum()
class_1_count = (Y_train == 1).sum()

weight_for_0 = total_samples / (2 * class_0_count)
weight_for_1 = total_samples / (2 * class_1_count)

然后替换损失函数,用二分类的加权交叉熵(因为你是二分类,用sigmoid比softmax更合适):

# 修改模型输出层为单维sigmoid
def multilayer_perceptron(x, weights, biases):
    layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
    layer_1 = tf.nn.relu(layer_1)
    layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
    layer_2 = tf.nn.relu(layer_2)
    # 二分类输出用sigmoid
    out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
    out_layer = tf.nn.sigmoid(out_layer)
    return out_layer

# 调整权重维度:输出层改成1维
weights = {
    'h1': tf.Variable(tf.random_normal([30, 64])), # 把隐藏层节点数从10改成64,增加模型容量
    'h2': tf.Variable(tf.random_normal([64, 32])),
    'out': tf.Variable(tf.random_normal([32, 1])) # 二分类输出1维
}
biases = {
    'b1': tf.Variable(tf.random_normal([64])),
    'b2': tf.Variable(tf.random_normal([32])),
    'out': tf.Variable(tf.random_normal([1]))
}

# 用加权交叉熵损失
cost = tf.reduce_mean(
    tf.nn.weighted_cross_entropy_with_logits(
        logits=pred, targets=Y, pos_weight=weight_for_1/weight_for_0
    )
)

3. 训练参数与模型容量的调整

  • 隐藏层节点数太少:10个节点不足以学习欺诈交易的复杂模式,改成64/32试试,给模型足够的容量。
  • epoch太多+没早停:1000个epoch肯定过拟合,加上早停,当验证集的Precision不再提升时就停止训练:
# 手动实现早停逻辑
best_precision = 0
patience = 10
stop_count = 0

for epoch in range(training_epochs):
    # ...执行批量训练步骤...
    # 计算当前验证集的Precision
    current_precision = sess.run(precision, feed_dict={x: X_test, y: Y_test})
    if current_precision > best_precision:
        best_precision = current_precision
        stop_count = 0
        # 保存最好的模型
        saver.save(sess, './best_model.ckpt')
    else:
        stop_count += 1
        if stop_count >= patience:
            print(f"Early stopping at epoch {epoch}, best precision: {best_precision:.4f}")
            break
  • batch_size可以调小:如果batch_size=100,每个batch里可能只有0-1个欺诈样本,模型还是学不到。改成32或者16,让每个batch里至少有几个欺诈样本。

4. 必须监控正确的指标

别再看Accuracy了!在不平衡数据集里它完全没用。你需要监控Precision、Recall、F1-Score,尤其是Precision。在训练循环里加上这些指标的计算:

# 初始化指标变量
pred_class = tf.round(pred) # 把sigmoid输出转成0/1
precision, update_precision = tf.metrics.precision(labels=Y, predictions=pred_class)
recall, update_recall = tf.metrics.recall(labels=Y, predictions=pred_class)
f1_score = 2 * (precision * recall) / (precision + recall + 1e-8) # 加1e-8避免除0

sess.run(tf.local_variables_initializer()) # 必须初始化指标的局部变量

# 训练时打印指标
for epoch in range(training_epochs):
    # ...执行训练步骤...
    # 更新指标并获取值
    sess.run([update_precision, update_recall], feed_dict={x: X_test, y: Y_test})
    p, r, f1 = sess.run([precision, recall, f1_score])
    print(f"Epoch {epoch+1}: Precision={p:.4f}, Recall={r:.4f}, F1={f1:.4f}")

三、额外的优化思路

  • 过采样少数类:用SMOTE算法生成 synthetic欺诈样本,让训练集的类别平衡:
from imblearn.over_sampling import SMOTE
sm = SMOTE(random_state=42)
X_train_res, Y_train_res = sm.fit_resample(X_train, Y_train.ravel())
  • 欠采样多数类:随机去掉一部分正常交易,让训练集的类别比例更均衡,但会损失一些正常样本的信息。
  • 尝试更合适的模型:比如XGBoost、LightGBM,它们对不平衡数据集的处理更成熟,内置了类别权重参数。

内容的提问来源于stack exchange,提问作者Peter Bence

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:08:13