Files
916117771 8b2590c5b5 docs(predictV3): 添加predictV3算法优化研究文档和前端功能实现
- 完成Phase 11: predictV3算法优化研究文档,涵盖6个优化方向的技术分析
- 实现置信度评估功能,提供历史命中率、得分分布、多维度一致性置信度指标
- 扩展回测指标体系,新增NDCG@K、MRR、命中率分布等排名质量评估指标
- 优化转移概率算法,引入二阶马尔可夫链和多属性联合转移增强预测准确性
- 设计权重训练机制,支持网格搜索和遗传算法进行数据驱动的参数优化
- 集成组合特征挖掘功能,采用关联规则和序列模式发现号码间潜在关联
- 实现完整的前端交互界面,支持预测结果显示、置信度展示和回测验证功能
- 建立性能优化策略,包括预计算缓存、批量计算和降级策略保障响应速度
2026-05-01 23:17:24 +08:00

11 KiB
Raw Permalink Blame History

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
11-predictv3 01 execute 1
application/admin/model/History.php
true
PRED-02
PRED-05
truths artifacts key_links
用户可以在回测结果中看到 NDCG@5 指标
用户可以在回测结果中看到 MRR 指标
用户可以看到各排名位置的命中分布统计
系统在数据不足时返回合理的默认值或提示
path provides contains
application/admin/model/History.php NDCG、MRR、命中分布计算方法 _calculateNDCG|_calculateMRR|_calculateHitDistribution
from to via
_runBacktestV3 _calculateNDCG, _calculateMRR, _calculateHitDistribution method call in return statement

Phase 11 - Plan 01: 回测指标扩展

Objective

扩展 _runBacktestV3 方法的回测指标,新增 NDCG@5、MRR、命中率分布等排名质量评估指标,提升算法评估能力。

Purpose: 当前回测仅返回命中率(Top5)和平均排名,缺少排名质量评估指标。NDCG、MRR 是成熟的推荐系统评估指标,能更全面反映预测排名质量。

Output: History.php 中新增 3 个计算方法,_runBacktestV3 返回结果扩展。

Tasks

Task 1: 实现 NDCG@5 计算(含空预测保护和公式文档)

<read_first>

  • D:\code\php\amlhc\application\admin\model\History.php (line 3495-3560, _runBacktestV3 方法) </read_first>
在 `History.php` 文件末尾(类内)新增 `_calculateNDCG` 方法:
/**
 * 计算 NDCG@K (Normalized Discounted Cumulative Gain)
 * 
 * 公式说明:
 * - DCG (Discounted Cumulative Gain) = Σ(rel_i / log2(rank_i + 1))
 *   其中 rel_i = 1 (命中) 或 0 (未命中)rank_i 为预测排名位置
 * - IDCG (Ideal DCG) = Σ(1 / log2(i + 1)) for i = 1..min(hits, K)
 *   即理想情况下所有命中的号码都排在最前面的DCG值
 * - NDCG = DCG / IDCG,范围 0-1,越接近1表示排名质量越好
 * 
 * @param array $backtestDetails 回测详情数组,每项包含 {hit: bool, rank: int}
 * @param int $K Top-K 参数,默认5,评估前K个预测位置的排名质量
 * @return float NDCG值 (0-1范围),空数据时返回0
 */
private function _calculateNDCG($backtestDetails, $K = 5)
{
    // 边缘情况处理:空预测或无效参数
    if (empty($backtestDetails) || $K <= 0) {
        return 0;
    }
    
    $dcg = 0;
    $idcg = 0;
    
    // 计算 DCG: 命中号码的排名折损累积值
    foreach ($backtestDetails as $detail) {
        if (!isset($detail['hit']) || !isset($detail['rank'])) {
            continue; // 跳过无效数据
        }
        if ($detail['hit'] && $detail['rank'] > 0 && $detail['rank'] <= $K) {
            // DCG公式: rel / log2(rank + 1),命中时 rel=1
            $dcg += 1 / log($detail['rank'] + 1, 2);
        }
    }
    
    // 计算 IDCG: 最理想情况下所有命中的 DCG(假设都排在第1位)
    $hitCount = 0;
    foreach ($backtestDetails as $detail) {
        if (isset($detail['hit']) && $detail['hit']) {
            $hitCount++;
        }
    }
    
    for ($i = 1; $i <= min($hitCount, $K); $i++) {
        $idcg += 1 / log($i + 1, 2);
    }
    
    // 返回标准化值,IDCG为0时返回0避免除零错误
    return $idcg > 0 ? round($dcg / $idcg, 4) : 0;
}

实现要点:

  • 公式:DCG = Σ(1/log2(rank+1))IDCG = Σ(1/log2(i+1)) for i=1..hits
  • 添加空预测保护:检查 $backtestDetails 是否为空
  • 添加数据完整性检查:确保 hit 和 rank 字段存在
  • 使用 log(rank + 1, 2) 作为折损函数,排名越靠前权重越高
  • 返回 0-1 范围的标准化值,越接近 1 表示排名质量越好

<acceptance_criteria>

  • grep 正则匹配: _calculateNDCG\s*\( 在 History.php 中存在
  • grep 匹配: empty($backtestDetails) 在方法中存在(空预测保护)
  • 方法返回 float 类型值
  • 包含函数级注释说明 NDCG 计算逻辑和公式 </acceptance_criteria>

Task 2: 实现 MRR 和命中分布计算(含边缘情况处理)

<read_first>

  • D:\code\php\amlhc\application\admin\model\History.php (新增的 _calculateNDCG 方法位置) </read_first>
在 `_calculateNDCG` 方法后继续新增 `_calculateMRR` 和 `_calculateHitDistribution` 方法:
/**
 * 计算 MRR (Mean Reciprocal Rank)
 * 平均倒数排名,关注命中号码的具体排名位置
 * 
 * 公式说明:
 * - MRR = Σ(1/rank_i) / N,其中 rank_i 为命中号码的排名,N 为测试总数
 * - 未命中的测试项贡献 0 到倒数排名
 * - MRR 范围 0-1,越接近1表示命中号码平均排名越靠前
 * 
 * @param array $backtestDetails 回测详情数组,每项包含 {hit: bool, rank: int}
 * @return float MRR值 (0-1范围),空数据时返回0
 */
private function _calculateMRR($backtestDetails)
{
    // 边缘情况处理:空预测
    if (empty($backtestDetails)) {
        return 0;
    }
    
    $reciprocalRanks = [];
    
    foreach ($backtestDetails as $detail) {
        if (!isset($detail['hit']) || !isset($detail['rank'])) {
            continue; // 跳过无效数据
        }
        if ($detail['hit'] && $detail['rank'] > 0) {
            $reciprocalRanks[] = 1 / $detail['rank'];
        } else {
            $reciprocalRanks[] = 0; // 未命中记为0
        }
    }
    
    return count($reciprocalRanks) > 0 
        ? round(array_sum($reciprocalRanks) / count($reciprocalRanks), 4) 
        : 0;
}

/**
 * 计算命中率分布
 * 统计各排名位置(1-5)的命中次数分布
 * 
 * 结构定义:
 * - 返回格式: {rank_1: n, rank_2: n, rank_3: n, rank_4: n, rank_5: n}
 * - rank_N 表示预测排名第N位的命中次数
 * - 用于前端柱状图可视化展示
 * 
 * @param array $backtestDetails 回测详情数组,每项包含 {hit: bool, rank: int}
 * @return array 各排名(1-5)的命中次数统计,键名为 rank_1 到 rank_5
 */
private function _calculateHitDistribution($backtestDetails)
{
    // 边缘情况处理:空预测返回全0分布
    if (empty($backtestDetails)) {
        return [
            'rank_1' => 0,
            'rank_2' => 0,
            'rank_3' => 0,
            'rank_4' => 0,
            'rank_5' => 0
        ];
    }
    
    // 初始化分布数组,键名使用 rank_N 格式便于前端解析
    $distribution = [
        'rank_1' => 0,
        'rank_2' => 0,
        'rank_3' => 0,
        'rank_4' => 0,
        'rank_5' => 0
    ];
    
    foreach ($backtestDetails as $detail) {
        if (!isset($detail['hit']) || !isset($detail['rank'])) {
            continue; // 跳过无效数据
        }
        if ($detail['hit'] && $detail['rank'] >= 1 && $detail['rank'] <= 5) {
            $key = 'rank_' . $detail['rank'];
            $distribution[$key]++;
        }
    }
    
    return $distribution;
}

实现要点:

  • MRR: 命中号码排名倒数平均值,公式 Σ(1/rank)/N
  • 命中分布: 明确结构为 {rank_1: n, rank_2: n, ..., rank_5: n}
  • 两个方法均添加空预测保护和无效数据跳过逻辑
  • hit_distribution 使用 rank_N 键名格式,便于前端柱状图渲染

<acceptance_criteria>

  • grep 正则匹配: _calculateMRR\s*\( 在 History.php 中存在
  • grep 正则匹配: _calculateHitDistribution\s*\( 在 History.php 中存在
  • grep 匹配: empty($backtestDetails) 在两个方法中均存在(空预测保护)
  • grep 匹配: rank_1|rank_2|rank_3|rank_4|rank_5 在 _calculateHitDistribution 中存在
  • 两个方法均包含函数级注释 </acceptance_criteria>

Task 3: 扩展 _runBacktestV3 返回结果(含数据量检查)

<read_first>

  • D:\code\php\amlhc\application\admin\model\History.php (line 3549-3556, _runBacktestV3 返回语句) </read_first>
修改 `_runBacktestV3` 方法的返回语句,在原有返回结构中添加新指标和数据量验证:

找到以下代码段(约 line 3549-3556):

return [
    'hit_rate' => $hitRate,
    'avg_rank' => $avgRank,
    'total_tests' => $testCount,
    'total_hits' => $hits,
    'details' => $details
];

替换为:

// 计算新增指标(添加数据量检查)
$minDataThreshold = 50; // 置信度计算最小数据量阈值

// 如果测试数据不足,返回默认值并添加警告
if ($testCount < $minDataThreshold) {
    $ndcg5 = 0;
    $mrr = 0;
    $hitDistribution = [
        'rank_1' => 0,
        'rank_2' => 0,
        'rank_3' => 0,
        'rank_4' => 0,
        'rank_5' => 0
    ];
    $dataWarning = '回测数据不足(' . $testCount . '期),建议至少50期以获得可靠指标';
} else {
    $ndcg5 = $this->_calculateNDCG($details, 5);
    $mrr = $this->_calculateMRR($details);
    $hitDistribution = $this->_calculateHitDistribution($details);
    $dataWarning = null;
}

$precision5 = $testCount > 0 ? round($hits / ($testCount * 5) * 100, 2) : 0;

return [
    'hit_rate' => $hitRate,
    'avg_rank' => $avgRank,
    'total_tests' => $testCount,
    'total_hits' => $hits,
    'details' => $details,
    // 新增排名质量指标
    'ndcg_5' => $ndcg5,
    'mrr' => $mrr,
    'hit_distribution' => $hitDistribution,
    'precision_5' => $precision5,
    // 数据量警告(不足时提示)
    'data_warning' => $dataWarning,
    'data_sufficient' => $testCount >= $minDataThreshold
];

注意:

  • 新增指标计算放在 return 语句之前,确保 $details 数组已完整构建
  • 添加最小数据量检查(50期),不足时返回默认值和警告提示
  • 新增 data_warning 和 data_sufficient 字段供前端展示

<acceptance_criteria>

  • grep 匹配: ndcg_5 在 _runBacktestV3 返回结构中存在
  • grep 匹配: mrr 在 _runBacktestV3 返回结构中存在
  • grep 匹配: hit_distribution 在 _runBacktestV3 返回结构中存在
  • grep 匹配: precision_5 在 _runBacktestV3 返回结构中存在
  • grep 匹配: data_warning 在 _runBacktestV3 返回结构中存在
  • grep 匹配: minDataThreshold 变量在方法中存在 </acceptance_criteria>

Verification

执行预测接口验证新指标返回:

curl -s "http://127.0.0.1:8000/admin/history/predictV3?periods=200&backtest=10" | grep -E "ndcg_5|mrr|hit_distribution|precision_5|data_warning"

预期结果:返回 JSON 中包含 ndcg_5、mrr、hit_distribution、precision_5、data_warning 字段。

Success Criteria

  1. _calculateNDCG_calculateMRR_calculateHitDistribution 三个方法已实现
  2. 所有计算方法包含空预测保护和数据完整性检查
  3. NDCG 公式在注释中完整说明:DCG = Σ(1/log2(rank+1))
  4. hit_distribution 结构明确为 {rank_1..rank_5: counts} 格式
  5. _runBacktestV3 返回结构包含 ndcg_5、mrr、hit_distribution、precision_5、data_warning 字段
  6. 添加数据量检查,不足50期时返回警告
  7. 所有新增方法包含函数级注释

Output

完成后创建 .planning/phases/11-predictv3/11-01-SUMMARY.md