三沙市网站建设_网站建设公司_悬停效果_seo优化
2025/12/28 5:20:50 网站建设 项目流程

@[TOC](【AI量化投研】- Modeling(四, 意外之喜))

背景

训练一直没有实际的效果,一方面准备好重来,要站在巨人的肩膀上做事,不再像无头苍蝇那样乱撞. 另一方面,原来的研究也不是毫无用处.发现,虽然损失函数长得很猥琐, 也不怎么收敛,

但出现一些很神奇的结果:
精确度49.57%,召回率63.42%,
精确度58.92%,召回率43.01%,
精确度66.86%,召回率36.73%,
精确度70.77%,召回率21.66%,

相当炸列了! 生财有道了! 说明:

  1. 误打误撞拿到了一个有潜力的模型训练模式(虽然输入特征缺陷很大);
  2. F1 分值 与 我们要的方向已经比较接近了, (除了尚未融入盈亏比信息, 但其实融入盈亏比信息并无必要,因损失可通过策略止损获得缩小,只要胜率高已是胜利!);
  3. 目前训练的一个要点是: 在样本分布不均衡的条件下, 正负样本权重差异设置, 是一个走钢丝的精细平衡过程,找到一个特别的点很重要;
  4. 损失函数的猥琐, 尚不能代表模型训练的失败, 不过,它的不稳定,可能会导致样本外的表现动荡,尚需实证;

不过:
6. 模型没保存好;
7. 损失寻优的方向与需要的方向不对头(但这次来看, 貌似如果以 F1 为方向搜索寻优能得到好结果,如何将F1 融入损失函数?);
8. 数据本身只有32000+,总样本量实际上是10W左右,因此,目前的结果仍然只处于测试阶段.

尝试 损失函数 融入盈亏比

计算盈亏比并融入损失函数不同类样本权重中.

@staticmethod def create_adaptive_loss(dataset, loss_type='focal_weighted'): if isinstance(dataset, Subset): labels = [dataset.dataset.binary_labels[i] for i in dataset.indices] returns = [dataset.dataset.returns[i] for i in dataset.indices] else: labels = dataset.binary_labels returns = dataset.returns pos_count = sum(1 for label in labels if label == 1) neg_count = sum(1 for label in labels if label == 0) total = len(labels) print(f"📊 类别分布统计:") print(f" 正样本: {pos_count} ({pos_count / total * 100:.1f}%)") print(f" 负样本: {neg_count} ({neg_count / total * 100:.1f}%)") # 计算平均盈亏比 pos_returns = [r for r, label in zip(returns, labels) if label == 1 and r != 0] neg_returns = [abs(r) for r, label in zip(returns, labels) if label == 0 and r != 0] # 亏损取绝对值 avg_win = np.mean(pos_returns) if pos_returns else 0.0 avg_loss = np.mean(neg_returns) if neg_returns else 1.0 profit_loss_ratio = avg_win / avg_loss if avg_loss != 0 else 1.0 print(f" 平均盈利: {avg_win:.4f}, 平均亏损: {avg_loss:.4f}, 盈亏比: {profit_loss_ratio:.4f}") if loss_type == 'focal_weighted': class_weights = torch.tensor([1.0, profit_loss_ratio], dtype=torch.float32).to(device) print(f" Focal Loss 类别权重: 负样本=1.00, 正样本={profit_loss_ratio:.2f}") return FocalLoss(alpha=class_weights, gamma=2.0) else: print(" 使用标准交叉熵损失") return nn.CrossEntropyLoss()

尝试 损失函数 融入F1 Score

直接让损失函数优化F1分数是一个很有价值的方向,尤其是在正负样本不平衡的分类任务中。传统的交叉熵损失并不直接优化F1分数,这会导致模型训练目标与最终评估指标不一致。以下是几种将F1分数融入损失函数的主流方法。

💡 直接近似法:软化F1的计算

核心思路是让不可微的F1分数变得可微,从而能够进行梯度下降。

Dice Loss

其思路是将F1分数(Dice系数)中的“整数计数”(如TP, FP)替换为模型预测的概率值(即软化),形成一个连续可微的近似版本。公式如下:
DL = 1 - (2 * sum(y_true * y_pred) + smooth) / (sum(y_true²) + sum(y_pred²) + smooth)
其中 y_true是真实标签,y_pred是预测概率。这个损失函数直接优化的是F1的软近似,因此在F1指标上通常有良好表现。

F1 Score Loss

另一种直接的软化方式,其目标是直接最大化F1分数:

# TensorFlow/Keras 示例 def f1_loss(y_true, y_pred): tp = K.sum(y_true * y_pred, axis=0) fp = K.sum((1 - y_true) * y_pred, axis=0) fn = K.sum(y_true * (1 - y_pred), axis=0) precision = tp / (tp + fp + K.epsilon()) recall = tp / (tp + fn + K.epsilon()) f1 = 2 * precision * recall / (precision + recall + K.epsilon()) return 1 - K.mean(f1) # 最小化 1 - F1

🔄 间接优化法:改进交叉熵

这类方法不直接计算F1,而是通过调整交叉熵损失,使其优化方向与提升F1一致。

Focal Loss

通过降低模型已能很好分类的样本(通常是大量的简单负样本)对损失的贡献,让模型更专注于难以分类的样本,这有助于提升召回率,进而可能优化F1。其核心是一个调制因子 (1 - p_t)^gamma。

# PyTorch 示例 class FocalLoss(nn.Module): def __init__(self, gamma=2.0): super().__init__() self.gamma = gamma def forward(self, logits, targets): ce_loss = F.cross_entropy(logits, targets, reduction='none') p_t = torch.exp(-ce_loss) focal_loss = ((1 - p_t) ** self.gamma) * ce_loss return focal_loss.mean()

加权交叉熵(Weighted Cross-Entropy)

为少数类样本的损失分配更高的权重,缓解类别不平衡问题,这也是提升F1的常见策略。权重可以固定,也可以根据每个批次的样本分布动态计算(自适应权重)。

💡+🔄 : 软化F1 + 加权交叉熵 相结合

将软化F1分数与加权交叉熵结合,结合了两种方法的优势,能更直接地引导模型优化关心的F1指标,同时保持训练过程的稳定。

结合一 - 复合损失

下面是一个具体的方案。创建一个复合损失函数(Composite Loss),让软化F1分数和加权交叉熵协同工作。

import torch import torch.nn as nn import torch.nn.functional as F class CompositeF1CrossEntropyLoss(nn.Module): """ 结合软化F1损失与加权交叉熵的复合损失函数 目标:同时优化分类准确性和F1分数,特别适用于不平衡数据 """ def __init__(self, alpha=0.5, beta=0.5, class_weights=None, epsilon=1e-7): """ Args: alpha: 软化F1损失的权重 beta: 加权交叉熵损失的权重 (alpha + beta 通常为1) class_weights: 各类别的权重张量,用于处理类别不平衡 epsilon: 平滑项,防止除零 """ super().__init__() self.alpha = alpha self.beta = beta self.epsilon = epsilon self.class_weights = class_weights # 初始化加权交叉熵损失 if class_weights is not None: self.cross_entropy = nn.CrossEntropyLoss( weight=class_weights, reduction='mean' ) else: self.cross_entropy = nn.CrossEntropyLoss(reduction='mean') def soft_f1_loss(self, y_pred, y_true): """ 计算软化F1损失(基于Dice Loss思想) 使用概率值而非硬标签,使得损失函数可微 """ # 将真实标签转换为one-hot编码 y_true_oh = F.one_hot(y_true, num_classes=y_pred.size(1)).float() # 对预测值应用softmax y_pred_softmax = F.softmax(y_pred, dim=1) # 计算真正例、假正例、假负例的软化版本 tp = (y_true_oh * y_pred_softmax).sum(dim=0) fp = ((1 - y_true_oh) * y_pred_softmax).sum(dim=0) fn = (y_true_oh * (1 - y_pred_softmax)).sum(dim=0) # 计算软化精确率和召回率 precision = tp / (tp + fp + self.epsilon) recall = tp / (tp + fn + self.epsilon) # 计算软化F1分数 soft_f1 = 2 * (precision * recall) / (precision + recall + self.epsilon) # 返回平均F1损失(最小化1-F1) return 1 - soft_f1.mean() def forward(self, y_pred, y_true): # 计算软化F1损失 f1_loss = self.soft_f1_loss(y_pred, y_true) # 计算加权交叉熵损失 ce_loss = self.cross_entropy(y_pred, y_true) # 组合损失 composite_loss = self.alpha * f1_loss + self.beta * ce_loss return composite_loss, f1_loss, ce_loss

为了使复合损失函数达到最佳效果,建议采用以下训练策略:
渐进式训练

def get_training_schedule(total_epochs=100): """ 制定渐进式训练计划 前期:侧重交叉熵,稳定收敛 后期:侧重F1损失,优化目标指标 """ schedule = { 'phase1': {'epochs': int(0.3 * total_epochs), 'alpha': 0.3, 'beta': 0.7}, 'phase2': {'epochs': int(0.5 * total_epochs), 'alpha': 0.5, 'beta': 0.5}, 'phase3': {'epochs': int(0.2 * total_epochs), 'alpha': 0.7, 'beta': 0.3} } return schedule

关键参数初始化

# 根据类别不平衡程度设置权重 def calculate_class_weights(labels): """计算类别权重,处理不平衡数据""" class_counts = torch.bincount(labels) total_samples = len(labels) class_weights = total_samples / (len(class_counts) * class_counts.float()) return class_weights # 初始化损失函数 class_weights = calculate_class_weights(training_labels) loss_fn = CompositeF1CrossEntropyLoss( alpha=0.5, beta=0.5, class_weights=class_weights )

结合二 - 动态权重调整策略

固定权重可能不是最优的。可以参考基于奖惩机制的动态权重方法,让模型在训练过程中自动调整两个损失的比重.

class AdaptiveCompositeLoss(nn.Module): """ 自适应权重的复合损失函数 根据训练阶段动态调整F1损失和交叉熵损失的权重 """ def __init__(self, total_epochs, initial_alpha=0.3): super().__init__() self.total_epochs = total_epochs self.initial_alpha = initial_alpha def forward(self, y_pred, y_true, current_epoch): # 动态调整权重:前期侧重CE稳定训练,后期侧重F1优化 # 随着训练进行,逐渐增加F1损失的权重 alpha = self.initial_alpha + (1 - self.initial_alpha) * (current_epoch / self.total_epochs) beta = 1 - alpha # 计算各项损失 base_loss_fn = CompositeF1CrossEntropyLoss() total_loss, f1_loss, ce_loss = base_loss_fn(y_pred, y_true) # 应用动态权重 adaptive_loss = alpha * f1_loss + beta * ce_loss return adaptive_loss, f1_loss, ce_loss, alpha, beta

结合小结

优势
目标一致性​
训练稳定性​
不平衡适应性​
灵活可调​

说明
软化F1损失确保模型直接优化你关心的F1指标
交叉熵损失提供良好的梯度信号,防止训练震荡
加权机制和F1优化共同应对类别不平衡问题
动态权重机制让模型在不同训练阶段有不同侧重

预期效果
相比单一损失函数,这种组合通常能在验证集上获得更高的F1分数,同时保持较好的准确率。特别是在数据不平衡的场景下,对少数类的识别能力会有明显提升。这种复合损失函数的设计思路,本质上是让交叉熵负责"夯实基础",而软化F1负责"冲刺高分",两者协同工作,共同推动模型向既准确又均衡的方向发展。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询