Ascend-SACT/Time-MoE
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

Time-MoE 昇腾/NPU 迁移部署与 NV GPU 对比指南

1 介绍

本文档详细介绍 Time-MoE 模型在华为昇腾 NPU 环境下的部署、测评、训练及微调流程,同时提供与 NVIDIA H20 GPU 环境的对比参考。

主要内容:

  • 昇腾 NPU 环境的完整部署指南
  • Time-MoE 模型的测评流程
  • 基于 Time-300B 数据集的微调训练
  • NPU 与 GPU 平台的精度对比分析
  • 常见问题处理

适用对象: 需要在昇腾 NPU 平台部署或训练 Time-MoE 模型的用户

2 环境准备

2.1 NPU 环境信息

驱动固件CANN版本python版本torch版本torch_npu版本
25.5.08.5.03.11.142.8.02.8.0

2.2 运行容器

2.2.1 准备镜像

本指导以 8.5.0-910b-ubuntu22.04-py3.11 镜像为例,获取镜像的命令为:

docker pull quay.io/ascend/cann:8.5.0-910b-ubuntu22.04-py3.11

通过 docker images 可以查看是否拉取成功。

2.2.2 启动容器

docker run -it -d \
    --shm-size=500g \
    --name TimeMoE \
    --privileged \
    --entrypoint /bin/bash \
    --net=host \
    --device /dev/davinci0 \
    --device /dev/davinci1 \
    --device /dev/davinci2 \
    --device /dev/davinci3 \
    --device /dev/davinci4 \
    --device /dev/davinci5 \
    --device /dev/davinci6 \
    --device /dev/davinci7 \
    --device /dev/davinci_manager \
    --device /dev/devmm_svm \
    --device /dev/hisi_hdc \
    -v /usr/local/dcmi:/usr/local/dcmi \
    -v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
    -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
    -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \
    -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \
    -v /etc/ascend_install.info:/etc/ascend_install.info \
    -v /opt/:/opt/ \
    quay.io/ascend/cann:8.5.0-910b-ubuntu22.04-py3.11

2.2.3 进入容器

docker exec -itu root TimeMoE bash

3 NPU 部署

3.1 拉取代码

本项目以在 /home 目录下拉取为例,首先 cd /home,然后输入:

cd /home
git clone https://github.com/Time-MoE/Time-MoE.git
cd Time-MoE
git checkout 915bfda4c78a544d62a2bec6ab22948423059236

可以看到存在 TimeMoE 文件,即表示拉取成功。为了防止由于 Time-MoE 代码较大更新导致部署失败问题,故需要 checkout 到指定的 commit。

3.2 获取数据集

3.2.1 测评数据集

cd /home
git clone https://github.com/zhouhaoyi/ETDataset.git

3.2.2 训练和微调数据集

由于 Time-300B 的原始数据集有 1.24T,为了快速验证,故仅下载其中的 sales 部分数据集,操作如下:

mkdir Time-300B
cd Time-300B
pip install huggingface_hub
export HF_ENDPOINT=https://hf-mirror.com
export HF_HOME=/tmp/hf_cache
hf download Maple728/Time-300B --repo-type dataset --include "sales/*"

由于上述下载的数据格式是缓存文件夹结构,并非真实可用的数据格式,故需要将其转化,执行数据转化命令:

cd /home/Time-300B
cp -rL /tmp/hf_cache/hub/datasets--Maple728--Time-300B/snapshots/*/sales ./sales

转化后的数据格式如下:

sales/
├── dominick/
├── favorita_transactions/
├── favorita_sales/
......

3.3 获取 Time-MoE 模型权重

cd /home/Time-MoE
HF_ENDPOINT=https://hf-mirror.com python -c "
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id='Maple728/TimeMoE-50M',
    local_dir='./TimeMoE-50M',
    resume_download=True,
    local_dir_use_symlinks=False
)
"

3.4 创建 conda 虚拟环境

  1. 首先确保当前环境有 conda,若无 conda 则需要先安装 conda。对于 aarch64 架构来说,获取安装脚本的命令为(建议在 /tmp 目录下执行):
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh
  1. 给与执行权限:
chmod +x Miniconda3-latest-Linux-aarch64.sh
  1. 开始安装(按照提示输入回车或者 yes):
bash Miniconda3-latest-Linux-aarch64.sh
  1. 激活 conda:
source ~/miniconda3/etc/profile.d/conda.sh
  1. 创建 conda 虚拟环境:
conda create -n Time-MoE python=3.11 -y

3.5 安装依赖

cd /home/Time-MoE
pip install -r requirements.txt
pip install torch==2.8.0 torch_npu==2.8.0
pip install transformers==4.57.0

3.6 核心适配代码

  1. 修改 run_eval.py,在 import torch 下面添加:
from torch_npu.contrib import transfer_to_npu
  1. 修改 torch_dist_run.py,在 import torch 下面添加:
from torch_npu.contrib import transfer_to_npu

3.7 测评

# 执行推理
python run_eval.py -d /home/ETDataset/ETT-small/ETTh1.csv --model ./TimeMoE-50M -p 96

4 NPU 训练和微调

4.1 核心适配代码

4.1.1 修改 evaluation_strategy 参数

修改 time_moe/runner.py,注释第 125 行的代码:

# evaluation_strategy=train_config.get("evaluation_strategy", 'no'),

原因:Time-MoE 官方代码问题,在 GPU 和 NPU 环境上测试均发现此问题。原因是 TimeMoETrainingArguments 不支持 evaluation_strategy 这个参数,代码里传了不支持的参数,所以直接崩溃。

4.1.2 修复 loss_function 权重问题

修改 time_moe/runner.py,在第 55-59 行添加两行代码:

  else:
      model = TimeMoeForPrediction.from_pretrained(model_path, **kwargs)
      # Guard against corrupted weight restore
      import torch.nn as nn
      object.__setattr__(model, 'loss_function', nn.HuberLoss(reduction='none', delta=2.0))

原因:模型权重(model.safetensors)中 loss_function 字段被错误地存为了 ForCausalLMLoss 函数指针。调用 from_pretrained() 时,权重恢复机制把这个错误的函数加载到了模型上,导致后续 calc_ar_loss 调用 self.loss_function(preds, labels) 时失败。

4.2 拉起多卡训练

nohup torchrun --master_addr=127.0.0.1 --master_port=11011 --node_rank=0 --nproc_per_node=8 main.py --data_path /home/Time-300B/sales --model_path /home/Time-MoE/TimeMoE-50M > run.log 2>&1 &

注意:上述训练命令用到了 8 张 NPU 卡,所用数量可以通过 nproc_per_node 修改。

4.3 训练过程示例

{'loss': 0.4114, 'grad_norm': 0.25300517678260803, 'learning_rate': 0.0001, 'epoch': 0.0}
{'loss': 0.4517, 'grad_norm': 0.2968370318412781, 'learning_rate': 9.99999584434885e-05, 'epoch': 0.0}
{'loss': 0.4112, 'grad_norm': 0.1269938200712204, 'learning_rate': 9.999983377409216e-05, 'epoch': 0.0}
{'loss': 0.4163, 'grad_norm': 0.13979601860046387, 'learning_rate': 9.999962599222543e-05, 'epoch': 0.0}
......

4.4 训练结果示例

......
{'loss': 0.3931, 'grad_norm': 0.1221962720155716, 'learning_rate': 5.000016622590785e-05, 'epoch': 1.0}
{'loss': 0.3587, 'grad_norm': 0.10206776112318039, 'learning_rate': 5.00000415565115e-05, 'epoch': 1.0}
{'train_runtime': 999.1059, 'train_samples_per_second': 110.309, 'train_steps_per_second': 1.725, 'train_loss': 0.3853655972940176, 'epoch': 1.0}
100%|██████████| 1723/1723 [16:39<00:00,  1.72it/s]

5 NPU 与 H20 GPU 精度比较

5.1 环境信息对比

项目NPU 环境GPU 环境 (H20)
驱动固件25.5.0NVIDIA Driver 550+
CANN版本8.5.0-
python版本3.11.143.11
torch版本2.8.02.5.0+
torch_npu版本2.8.0-
镜像cann:8.5.0-910b-ubuntu22.04-py3.11pytorch:25.12-py3

5.2 H20 GPU 测试步骤

5.2.1 环境信息

GPU 环境使用 NVIDIA H20,本文档以 Docker 容器方式部署。

5.2.2 拉取镜像

docker pull nvcr.io/nvidia/pytorch:25.12-py3

5.2.3 启动容器

docker run -itd --runtime nvidia --gpus all --name TimeMoE --shm-size 500g --ulimit memlock=-1 --ulimit stack=67108864 --net host --entrypoint /bin/bash -v /home/:/home/ nvcr.io/nvidia/pytorch:25.12-py3
docker start TimeMoE
docker exec -itu root TimeMoE bash

5.3 H20 GPU 部署

5.3.1 拉取代码

cd /workspace
git clone https://github.com/Time-MoE/Time-MoE.git
cd Time-MoE
git checkout 915bfda4c78a544d62a2bec6ab22948423059236

5.3.2 获取数据集

5.3.2.1 测评数据集
cd /workspace
git clone https://github.com/zhouhaoyi/ETDataset.git
5.3.2.2 训练和微调数据集

由于 Time-300B 的原始数据集有 1.24T,为了快速验证,故仅下载其中的 sales 部分数据集,操作如下:

cd /workspace
mkdir Time-300B
cd Time-300B
pip install huggingface_hub
export HF_ENDPOINT=https://hf-mirror.com
export HF_HOME=/tmp/hf_cache
hf download Maple728/Time-300B --repo-type dataset --include "sales/*"

5.3.3 创建 conda 虚拟环境

  1. 首先确保当前环境有 conda,若无 conda 则需要先安装 conda。对于 x86_64 架构来说,获取安装脚本的命令为(建议在 /tmp 目录下执行):
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
  1. 给与执行权限:
chmod +x Miniconda3-latest-Linux-x86_64.sh
  1. 开始安装(按照提示输入回车或者 yes):
bash Miniconda3-latest-Linux-x86_64.sh
  1. 激活 conda:
source ~/miniconda3/etc/profile.d/conda.sh
  1. 创建 conda 虚拟环境:
conda create -n Time-MoE python=3.11 -y

5.3.4 安装依赖

cd /workspace/Time-MoE
pip install -r requirements.txt
pip install transformers==4.57.0

5.3.5 测评

# 执行推理
python run_eval.py -d /workspace/ETDataset/ETT-small/ETTh1.csv -p 96

5.4 H20 GPU 训练和微调

5.4.1 拉起多卡训练

nohup torchrun --master_addr=127.0.0.1 --master_port=11011 --node_rank=0 --nproc_per_node=8 main.py --data_path /home/Time-300B/sales --model_path /home/Time-MoE/TimeMoE-50M > run.log 2>&1 &

注意:上述训练命令用到了 8 张 GPU 卡,所用数量可以通过 nproc_per_node 修改。

5.4.2 训练过程示例

{'loss': 0.4086, 'grad_norm': 0.23242798447608948, 'learning_rate': 0.0001, 'epoch': 0.0}
{'loss': 0.4247, 'grad_norm': 0.31524255871772766, 'learning_rate': 9.99999584434885e-05, 'epoch': 0.0}
{'loss': 0.4202, 'grad_norm': 0.2507534325122833, 'learning_rate': 9.999983377409216e-05, 'epoch': 0.0}
{'loss': 0.4035, 'grad_norm': 0.410408616065979, 'learning_rate': 9.999962599222543e-05, 'epoch': 0.0}
{'loss': 0.4008, 'grad_norm': 0.18601377308368683, 'learning_rate': 9.999933509857908e-05, 'epoch': 0.0}
......

5.4.3 训练结果示例

......
{'loss': 0.3977, 'grad_norm': 0.11204443871974945, 'learning_rate': 5.000037400777459e-05, 'epoch': 1.0}
{'loss': 0.3645, 'grad_norm': 0.09452001005411148, 'learning_rate': 5.000016622590785e-05, 'epoch': 1.0}
{'loss': 0.3586, 'grad_norm': 0.1820800006389618, 'learning_rate': 5.00000415565115e-05, 'epoch': 1.0}
{'train_runtime': 339.0897, 'train_samples_per_second': 325.017, 'train_steps_per_second': 5.081, 'train_loss': 0.3852101174192379, 'epoch': 1.0}
100%|██████████| 1723/1723 [05:39<00:00,  5.08it/s]
2026-04-16 09:44:26,507 - log_util.py[pid:3606;line:52:log_in_local_rank_0] - INFO: Saving model to logs/time_moe

6 结果分析比较

6.1 预测结果比较

6.1.1 数据集介绍

ETT = Electricity Transformer Temperature

  • 来源:某地区电力变压器运行监控系统
  • 任务:多变量时间序列预测(MTS)
  • 预测目标:油温(OT, Oil Temperature)
  • 特点:工业真实数据,多变量强相关,明显周期性(日/周),是时序预测领域的 MNIST/ImageNet 级基准

四个数据集的区别:

数据集采样频率时间跨度总长度用途
ETTh11 小时约 2 年17,420最常用的中等难度基准
ETTh21 小时约 2 年17,420通常比 ETTh1 更难一点,时间模式、噪声分布不同,验证模型泛化能力
ETTm115 分钟约 2 年69,680高频数据更细粒度周期结构,中短期预测 + 高频建模
ETTm215 分钟约 2 年69,680与 ETTm1 相比分布变化更明显,高频 + 泛化测试

数据格式:

date,HUFL,HULL,MUFL,MULL,LUFL,LULL,OT
  • date:时间戳
  • HUFL~LULL:不同负载/电流/温度相关变量
  • OT:预测目标(Oil Temperature)

默认设置:

  • 单变量预测:OT
  • 多变量预测:所有变量 → OT

6.1.2 测试结果记录

备注:上述结果均是在单卡上测试所得,运行脚本为: python run_eval.py -m models/TimeMoE-50M -d dataset/ETT-small/ETTh1.csv -p 96 通过改变 -m(模型权重)、-d(数据集)、-p(预测长度)得到不同组合的数据

6.1.2.1 ETTh1 数据集
prediction_length指标TimeMOE-base-50M (NPU)TimeMOE-large-200M (NPU)TimeMOE-base-50M (GPU)TimeMOE-large-200M (GPU)
96MSE0.35770.35040.35770.3504
96MAE0.38170.38210.38170.3821
96Time04:3104:5401:0101:32
192MSE0.38450.38890.38450.3890
192MAE0.40470.41210.40460.4121
192Time08:3509:3002:1303:14
336MSE0.41220.41170.41230.4117
336MAE0.43430.43080.43430.4307
336Time21:4524:0106:4108:55
720MSE0.44770.42780.44780.4278
720MAE0.47600.45590.47600.4559
720Time35:4439:1313:1316:44
AvgMSE0.4010.3950.4010.395
AvgMAE0.4240.4200.4240.420
论文结果MSE0.4000.3940.4000.394
论文结果MAE0.4240.4190.4240.419
绝对误差MSE0.0010.0010.0010.001
绝对误差MAE0.0000.0010.0000.001
6.1.2.2 ETTh2 数据集
prediction_length指标TimeMOE-base-50M (NPU)TimeMOE-large-200M (NPU)TimeMOE-base-50M (GPU)TimeMOE-large-200M (GPU)
96MSE0.30380.30180.30220.3018
96MAE0.35760.35410.35640.3542
96Time04:3104:5801:0101:31
192MSE0.34790.36410.34790.3641
192MAE0.38260.38530.38270.3857
192Time08:3109:2602:1103:13
336MSE0.39280.41720.39270.4171
336MAE0.41850.42500.41860.4251
336Time21:2023:4706:3808:54
720MSE0.41830.53740.41820.5378
720MAE0.45330.49570.45320.4958
720Time35:5838:5313:0916:46
AvgMSE0.3660.4050.3650.405
AvgMAE0.4030.4150.4030.415
论文结果MSE0.3660.4050.3660.405
论文结果MAE0.4040.4150.4040.415
绝对误差MSE0.0000.0000.0010.000
绝对误差MAE0.0010.0000.0010.000
6.1.2.3 ETTm1 数据集
prediction_length指标TimeMOE-base-50M (NPU)TimeMOE-large-200M (NPU)TimeMOE-base-50M (GPU)TimeMOE-large-200M (GPU)
96MSE0.33850.30880.33840.3090
96MAE0.36810.35670.36810.3568
96Time18:5720:2104:0606:08
192MSE0.35250.34670.35260.3465
192MAE0.38770.38080.38770.3808
192Time36:1140:2009:1213:33
336MSE0.37870.37350.37880.3736
336MAE0.41130.40800.41140.4081
336Time01:35:2601:57:5029:10:0039:11:00
720MSE0.50030.47500.50020.4748
720MAE0.49070.47720.49060.4771
720Time02:59:1503:13:4701:05:5401:23:42
AvgMSE0.3930.3760.3930.376
AvgMAE0.4140.4060.4140.406
论文结果MSE0.3940.3760.3940.376
论文结果MAE0.4150.4050.4150.405
绝对误差MSE0.0010.0000.0010.000
绝对误差MAE0.0010.0010.0010.001
6.1.2.4 ETTm2 数据集
prediction_length指标TimeMOE-base-50M (NPU)TimeMOE-large-200M (NPU)TimeMOE-base-50M (GPU)TimeMOE-large-200M (GPU)
96MSE0.19760.19730.19670.1970
96MAE0.28760.28620.28680.2860
96Time18:0219:4004:0106:06
192MSE0.25380.25040.25380.2503
192MAE0.33020.32190.33010.3218
192Time35:0839:5709:1113:27
336MSE-0.33630.32330.3362
336MAE-0.37430.37210.3742
336Time-01:44:0729:06:0039:00:00
720MSE-0.55120.48430.5505
720MAE-0.48830.46200.4881
720Time-03:12:0401:05:3401:23:29
AvgMSE-0.3150.3340.316
AvgMAE-0.3630.3680.361
论文结果MSE-0.3170.316-
论文结果MAE-0.3650.361-
绝对误差MSE-0.0020.018-
绝对误差MAE-0.0020.007-

注:ETTm2 数据集部分 NPU 测试结果缺失(- 表示无数据)

6.2 微调结果比较

6.2.1 微调比较原则

本方法的核心原则是:

❗不是判断"权重是否完全相同",而是判断 "差异是否符合训练过程中的合理统计分布"

6.2.1.1 问题背景

在 GPU 与 NPU 分别进行模型微调的场景中:

  • GPU:一次独立训练过程
  • NPU:一次独立训练过程

即使满足:

  • 相同模型结构
  • 相同初始化
  • 相同数据
  • 相同超参数

由于浮点运算与并行计算差异,最终权重仍然会出现偏差。


6.2.1.2 为什么不能用逐点一致性

传统方法(如 allclose)隐含假设:

❌ 两个权重应逐点相同

但在实际训练中:

  • 随机性(dropout / init / data shuffle)
  • 浮点非结合律
  • 分布式归约顺序差异
  • 不同硬件算子实现差异

都会导致:

❗逐点一致性不成立

因此必须采用统计方法。


6.2.1.3 核心分析原则

1. 统计一致性原则(Statistical Consistency)

关注整体误差统计特征,而非单点误差:

  • mean diff(平均误差)
  • max diff(最大误差)
  • quantile(P50 / P90 / P99)

目标是判断:

❓大多数参数误差是否处于合理范围


2. 分布一致性原则(Distribution over Element)

使用分位数评估误差分布:

指标含义
P50中位数误差(整体水平)
P90高误差区域
P99极端异常点

用于判断:

  • 是否整体偏移
  • 是否存在少量异常参数

3. 尺度感知原则(Scale-aware Error)

仅使用绝对误差是不够的,需要引入相对误差: 原因:

  • 小权重区域会导致误差放大
  • 不同参数尺度差异巨大

因此需结合:

  • 绝对误差(稳定性)
  • 相对误差(尺度敏感性)

4. 结构化分析原则(Structure-aware Analysis)

不同模块对误差的敏感性不同,应分别统计:

  • Attention
  • MoE Experts
  • Shared Expert
  • LayerNorm

典型规律:

模块特性
LayerNorm极稳定
Attention中等敏感
MoE Experts高敏感

5. 功能一致性优先原则(Function over Parameter)

权重差异并不等价于模型行为差异:

❗权重不同 ≠ 模型性能不同

应进一步验证:

  • loss 曲线是否一致
  • perplexity 是否接近
  • 推理输出是否一致

6. MoE 路径敏感性原则(Path Sensitivity)

对于 MoE 模型:

  • token routing(top-k expert selection)非常敏感
  • 微小权重差异可能改变 expert 选择路径

导致:

  • 训练路径分叉
  • 误差放大

因此 MoE 模型天然具有更高差异性。


6.2.2 微调比较代码

import torch
from safetensors.torch import load_file
from collections import defaultdict
from tqdm import tqdm

# ===== 配置 =====
gpu_path = "/workspace/Time-MoE/logs/time_moe/model.safetensors"
npu_path = "/workspace/weight/logs/time_moe/model.safetensors"

sample_per_tensor = 1000   # 用于quantile采样
rel_eps = 1e-3             # relative error过滤阈值
topk_num = 10

# ===== 加载 =====
print("Loading weights...")
gpu_weights = load_file(gpu_path)
npu_weights = load_file(npu_path)

gpu_keys = set(gpu_weights.keys())
npu_keys = set(npu_weights.keys())

print("参数名称一致:", gpu_keys == npu_keys)

common_keys = gpu_keys & npu_keys
print(f"参数数量: {len(common_keys)}")

# ===== 全局统计 =====
global_max = 0
global_mean = 0
global_rel_mean = 0
count = 0

# ===== TopK =====
topk = []

# ===== 分模块统计 =====
module_stats = defaultdict(list)

# ===== quantile采样 =====
samples = []

print("\nComparing...")

for key in tqdm(common_keys):
    g = gpu_weights[key].to(torch.float32).cpu()
    n = npu_weights[key].to(torch.float32).cpu()

    if g.shape != n.shape:
        print(f"[Shape mismatch] {key}: {g.shape} vs {n.shape}")
        continue

    diff = torch.abs(g - n)

    max_d = diff.max().item()
    mean_d = diff.mean().item()

    # ===== relative error(过滤小值)=====
    mask = torch.abs(g) > rel_eps
    if mask.sum() > 0:
        rel = diff[mask] / torch.abs(g[mask])
        rel_mean = rel.mean().item()
    else:
        rel_mean = 0

    # ===== 全局 =====
    global_max = max(global_max, max_d)
    global_mean += mean_d
    global_rel_mean += rel_mean
    count += 1

    # ===== TopK =====
    topk.append((max_d, key))

    # ===== 模块分类 =====
    if "self_attn" in key:
        module_stats["attention"].append(mean_d)
    elif "ffn_layer.experts" in key:
        module_stats["moe_expert"].append(mean_d)
    elif "shared_expert" in key:
        module_stats["shared_expert"].append(mean_d)
    elif "layernorm" in key:
        module_stats["layernorm"].append(mean_d)
    else:
        module_stats["others"].append(mean_d)

    # ===== quantile采样 =====
    flat = diff.view(-1)
    if flat.numel() > sample_per_tensor:
        idx = torch.randint(0, flat.numel(), (sample_per_tensor,))
        flat = flat[idx]
    samples.append(flat)

# ===== 汇总 =====
print("\n===== 全局统计 =====")
print(f"平均误差: {global_mean / count:.6e}")
print(f"平均相对误差(过滤<{rel_eps}): {global_rel_mean / count:.6e}")
print(f"最大误差: {global_max:.6e}")

# ===== 分位数(采样)=====
samples = torch.cat(samples)

q = torch.quantile(samples, torch.tensor([0.5, 0.9, 0.99]))

print("\n===== 分位数(采样) =====")
print(f"P50: {q[0].item():.6e}")
print(f"P90: {q[1].item():.6e}")
print(f"P99: {q[2].item():.6e}")

# ===== 模块统计 =====
print("\n===== 模块误差 =====")
for k, v in module_stats.items():
    if len(v) > 0:
        print(f"{k:15s}: mean={sum(v)/len(v):.6e}, count={len(v)}")

# ===== Top-K =====
topk.sort(reverse=True)

print(f"\n===== Top {topk_num} 最大误差参数 =====")
for d, k in topk[:topk_num]:
    print(f"{k}: {d:.6e}")

6.2.3 对比结果

下述结果是用同样的微调命令在 GPU 和 NPU 上跑 10 个 epoch 后得到的权重,并对其比较的结果:

Loading weights...
参数名称一致: True
参数数量: 463

Comparing...
100%|████████████████████████████████████████████████████████████████████████████████████| 463/463 [00:00<00:00, 874.70it/s]

===== 全局统计 =====
平均误差: 7.039339e-03
平均相对误差(过滤<0.001): 2.721881e-01
最大误差: 1.691253e-01

===== 分位数(采样) =====
P50: 5.434263e-03
P90: 1.741766e-02
P99: 3.062531e-02

===== 模块误差 =====
moe_expert     : mean=8.156686e-03, count=288
attention      : mean=4.904049e-03, count=84
others         : mean=4.401606e-03, count=19
shared_expert  : mean=6.365904e-03, count=48
layernorm      : mean=4.539759e-03, count=24

===== Top 10 最大误差参数 =====
model.layers.11.ffn_layer.experts.7.down_proj.weight: 1.691253e-01
model.layers.11.ffn_layer.experts.7.gate_proj.weight: 1.189974e-01
model.layers.11.ffn_layer.shared_expert.up_proj.weight: 1.134196e-01
model.layers.11.ffn_layer.shared_expert.down_proj.weight: 1.080939e-01
model.layers.11.ffn_layer.experts.3.down_proj.weight: 9.563965e-02
model.layers.11.ffn_layer.shared_expert.gate_proj.weight: 9.188609e-02
model.layers.11.ffn_layer.experts.2.down_proj.weight: 9.044161e-02
model.layers.11.ffn_layer.experts.7.up_proj.weight: 8.590467e-02
model.layers.11.ffn_layer.experts.6.down_proj.weight: 8.155429e-02
model.layers.3.ffn_layer.experts.2.gate_proj.weight: 8.127359e-02

6.2.4 Loss 曲线比较

下图展示了 NPU 和 GPU 在微调过程中的 Loss 曲线对比:

NPU与GPU微调Loss对比

Loss 差异统计分析:

指标数值
Step 数量17,230
平均绝对误差 (MAE)1.594050e-02
最大绝对误差7.950000e-02

结果分析:

  1. 曲线一致性:从图中可以看出,NPU 和 GPU 的 Loss 曲线整体走势高度一致,均呈现平稳下降趋势,表明两个平台在训练收敛性上表现相似。

  2. 误差水平:

    • 平均绝对误差约为 1.59e-02,处于较低水平
    • 最大绝对误差为 7.95e-02,发生在训练早期阶段(loss 值较高时),属于正常范围
  3. 收敛行为:两个平台在相同 step 处的 loss 值非常接近,验证了 Time-MoE 模型在昇腾 NPU 和 NVIDIA GPU 上微调的一致性。

  4. 结论:NPU 与 GPU 的微调结果在统计意义上高度一致,差异主要来源于浮点运算和并行计算的正常偏差范围。

6.3 结论

  • 基于原始 Time-MoE 权重进行测评,其 GPU 与 NPU 的 MSE 和 MAE 误差均在合理范围内。
  • 基于微调后的 Time-MoE 权重,整体平均误差约为 1e-3 量级,属于合理训练分叉范围,GPU 与 NPU 微调后的模型在权重层面存在可控范围内的统计差异,整体属于"训练分叉"而非"结构性错误"。

7 常见问题处理

7.1 pip 安装失败

可能是 pip 源问题,设置成阿里源:

export PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple
export PIP_TRUSTED_HOST=mirrors.aliyun.com

7.2 训练出现 HuberLoss 损失函数不支持问题

问题原因:NPU 环境下 PyTorch 原生的 HuberLoss 可能存在兼容性问题。

解决方案:使用自定义的 NPUHuberLoss 替代。

具体步骤:

  1. 在 time_moe/models/ 目录下添加 HuberLoss.py 文件:
import torch
import torch.nn as nn

class NPUHuberLoss(nn.Module):
    def __init__(self, delta=2.0, reduction='none'):
        super().__init__()
        self.delta = delta
        self.reduction = reduction

    def forward(self, input, target):
        diff = input - target
        abs_diff = diff.abs()

        loss = torch.where(
            abs_diff <= self.delta,
            0.5 * diff * diff,
            self.delta * abs_diff - 0.5 * self.delta * self.delta
        )
        if self.reduction == 'mean':
            return loss.mean()
        elif self.reduction == 'sum':
            return loss.sum()
        else:
            return loss
  1. 在 modeling_time_moe.py 开头添加导入:
from .HuberLoss import NPUHuberLoss
  1. 替换 loss_function 定义(约第 965-966 行):
# 替换前
self.loss_function = torch.nn.HuberLoss(reduction='none', delta=2.0)

# 替换后
self.loss_function = NPUHuberLoss()

之后照常训练即可。