Skip to content

Level 3 执行评测总顺序

Abstract

入口:

python
eval_model(model_factory, series_list, evaluation_config)

出口:

python
EvalResult(strategy, result_list, model_factory, series_list)

这一层只解释:

eval_model(...) 怎样把“一个模型 + 一组序列 + 一套评测配置”变成“已提交的任务句柄集合”,并把它们封装成 EvalResult

1. 这一层在总主线里的位置

上一层是:

当前层对应上一层中的具体位置是:

  • 2C 执行侧
  • 也就是:
python
eval_model(model_factory, data_name_list, evaluation_config)

这一层对应的是 pipeline(...) 里的第 3 块:

  • 执行评测

这一层还不继续细拆:

  • _eval_batch(...) 内部四段
  • forecast_fit(...)
  • batch_forecast(...)

2. 当前层第一性

这一层的第一性是:

eval_model(...) 的输入,转换成“已经提交出去的单序列评测任务集合”,再包装成 EvalResult 返回。

这一层的关键词只有三个:

  1. strategy
  2. schedule(...)
  3. EvalResult

3. 输入与输出

3.1 输入

python
eval_model(model_factory, series_list, evaluation_config)

输入包括:

  • model_factory
  • series_list
  • evaluation_config

3.2 输出

python
EvalResult(strategy, result_list, model_factory, series_list)

输出不是最终指标,而是:

  • strategy
  • result_list
  • model_factory
  • series_list

其中最关键的是:

  • result_list 里装的是 TaskResult
  • 不是最终 DataFrame
  • 也不是最终 metric 数值

4. 当前例子的最小落地

当前命令下,这一层的输入是:

  • model_factory = DLinear 对应的 ModelFactory
  • series_list = ["ETTh1.csv"]
  • evaluation_config["strategy_args"]["strategy_name"] = "rolling_forecast"

所以这一层的真实业务含义可以压成一句:

DLinearETTh1.csv 上提交一个 rolling forecast 评测任务,并返回一个 EvalResult 结果句柄对象。

5. 顺序图

这张图只回答:

eval_model(...) 内部按什么顺序推进。

这张图只描述 eval_model(...) 自己内部的顺序。
它还没有展开被提交出去的任务体。

6. 包含树

这张树只回答:

eval_model(...) 这一层里,外层壳和被提交任务体的结构关系是什么。

这一层最重要的结构结论是:

strategy.execute(...) 不是和 eval_model(...) 并列,
而是挂在 schedule(...) 下面的任务体。

7. schedule(...) 怎么往下执行

eval_model(...) 里的真实代码是:

python
result_list.append(
    eval_backend.schedule(strategy.execute, (series_name, model_factory))
)

这里传给 schedule(...) 的是:

  • fn = strategy.execute
  • args = (series_name, model_factory)

所以从接口角度,它的语义就是:

python
schedule(
    fn=strategy.execute,
    args=(series_name, model_factory),
)

schedule(...) 的下一步业务语义就是:

python
strategy.execute(series_name, model_factory)

7.1 在 SequentialBackend

sequential_backend.py 的核心代码是:

python
res = SequentialResult()
res.put(fn(*args))
return res

代入当前例子就是:

python
res.put(strategy.execute(series_name, model_factory))

7.2 在 RayBackend

ray_backend.py 不会立刻在主线程执行 fn(*args)
而是先把:

  • fn = strategy.execute
  • args = (series_name, model_factory)

提交给 actor pool。

但任务真正开始执行时,业务入口仍然是:

python
strategy.execute(series_name, model_factory)

所以无论 backend 是顺序还是 ray,
这一层真正的任务入口都一样:

python
strategy.execute(series_name, model_factory)

8. Level 3 到 Level 4 的桥

Level 3 的出口不是直接到 _eval_batch(...)
它先到:

python
strategy.execute(series_name, model_factory)

然后在 forecasting.py 里,这个方法内部会执行:

python
self._execute(data, meta_info, model_factory, series_name)

当前 self 的真实类型是:

  • RollingForecast

所以这句会动态分派到:

python
RollingForecast._execute(data, meta_info, model_factory, series_name)

因此 Level 3 到 Level 4 的真实桥接链是:

9. 职责表

9.1 eval_model(...)

职责:

  • 解析 strategy
  • 解析 metrics
  • 创建 evaluator
  • 创建 strategy
  • 为每个序列提交一个任务
  • 返回 EvalResult

9.2 schedule(...)

职责:

  • 接收:
    • 一个任务函数 fn
    • 一组参数 args
  • 返回任务句柄 TaskResult

9.3 strategy.execute(...)

职责:

  • 作为被提交任务体的真正入口
  • 后续再进入 ForecastingStrategy.execute(...)

10. 文件 / 函数关系图

11. 下一层入口

下一层开始解释:

python
strategy.execute(series_name, model_factory)

也就是:

  • 先进入 ForecastingStrategy.execute(...)
  • 再进入 RollingForecast._execute(...)

对应:

12. 只留一句

Level 3 只做一件事:把 eval_model(...) 里的 schedule(strategy.execute, ...) 这条链讲清楚,并把任务交给 Level 4。

*记录并在线阅读我的笔记*