""" 从训练输出解析数据并绘制训练过程图 """ import re import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np # 设置中文字体 plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'WenQuanYi Micro Hei', 'Microsoft YaHei', 'SimHei'] plt.rcParams['axes.unicode_minus'] = False # 从训练输出手动提取的10000步数据 # 每500步打印一次 data_raw = """ ep=0 loss=5.8066 pred=5.2434 router=1.1062 acc=0.34 bal=1.0189 ep=500 loss=0.4863 pred=0.4721 router=0.0053 acc=1.00 bal=1.1489 ep=1000 loss=0.2003 pred=0.1876 router=0.0032 acc=1.00 bal=1.1169 ep=1500 loss=0.1824 pred=0.1681 router=0.0079 acc=1.00 bal=1.0295 ep=2000 loss=0.1486 pred=0.1371 router=0.0026 acc=1.00 bal=1.0134 ep=2500 loss=0.1164 pred=0.1051 router=0.0006 acc=1.00 bal=1.0957 ep=3000 loss=0.1065 pred=0.0929 router=0.0032 acc=1.00 bal=1.1952 ep=3500 loss=0.1086 pred=0.0985 router=0.0001 acc=1.00 bal=1.0078 ep=4000 loss=0.1040 pred=0.0936 router=0.0000 acc=1.00 bal=1.0371 ep=4500 loss=0.1158 pred=0.1050 router=0.0001 acc=1.00 bal=1.0723 ep=5000 loss=0.1117 pred=0.1009 router=0.0003 acc=1.00 bal=1.0723 ep=5500 loss=0.0988 pred=0.0866 router=0.0001 acc=1.00 bal=1.2127 ep=6000 loss=0.0888 pred=0.0780 router=0.0004 acc=1.00 bal=1.0605 ep=6500 loss=0.1031 pred=0.0926 router=0.0000 acc=1.00 bal=1.0547 ep=7000 loss=0.0986 pred=0.0884 router=0.0000 acc=1.00 bal=1.0137 ep=7500 loss=0.0861 pred=0.0750 router=0.0000 acc=1.00 bal=1.1016 ep=8000 loss=0.0940 pred=0.0837 router=0.0000 acc=1.00 bal=1.0312 ep=8500 loss=0.0971 pred=0.0838 router=0.0002 acc=1.00 bal=1.3179 ep=9000 loss=0.0908 pred=0.0807 router=0.0000 acc=1.00 bal=1.0137 ep=9500 loss=0.0869 pred=0.0755 router=0.0000 acc=1.00 bal=1.1426 ep=9999 loss=0.0863 pred=0.0759 router=0.0000 acc=1.00 bal=1.0371 """ # 解析数据 epochs = [] losses = [] pred_losses = [] router_losses = [] accuracies = [] balance_losses = [] qat_phase = [] pattern = r'ep=(\d+) loss=([\d.]+) pred=([\d.]+) router=([\d.]+) acc=([\d.]+) bal=([\d.]+)(.*)$' for line in data_raw.strip().split('\n'): m = re.match(pattern, line.strip()) if m: ep = int(m.group(1)) epochs.append(ep) losses.append(float(m.group(2))) pred_losses.append(float(m.group(3))) router_losses.append(float(m.group(4))) accuracies.append(float(m.group(5))) balance_losses.append(float(m.group(6))) qat_phase.append(' [QAT]' in m.group(7)) epochs = np.array(epochs) losses = np.array(losses) pred_losses = np.array(pred_losses) router_losses = np.array(router_losses) accuracies = np.array(accuracies) balance_losses = np.array(balance_losses) # ========== 绘制训练过程图 ========== fig = plt.figure(figsize=(16, 10)) fig.suptitle('BitFlow MoE v4 训练过程 — 10,000 轮', fontsize=16, fontweight='bold', y=0.97) # 子图1: 总Loss ax1 = plt.subplot(2, 3, 1) # 找到 QAT 起始 epoch qat_epochs = [e for e in epochs if e >= 8000] qat_epoch_val = qat_epochs[0] if qat_epochs else 8000 ax1.plot(epochs, losses, 'b-', linewidth=1.5, label='总损失') ax1.axvspan(qat_epoch_val, epochs[-1], alpha=0.1, color='red', label='QAT量化阶段') ax1.set_xlabel('训练轮次 (Epoch)') ax1.set_ylabel('损失值') ax1.set_title('总损失 (Total Loss)', fontsize=11) ax1.legend(fontsize=8) ax1.grid(True, alpha=0.3) ax1.set_yscale('log') # 子图2: 预测Loss ax2 = plt.subplot(2, 3, 2) ax2.plot(epochs, pred_losses, 'g-', linewidth=1.5, label='预测损失') ax2.axvspan(qat_epoch_val, epochs[-1], alpha=0.1, color='red') ax2.set_xlabel('训练轮次 (Epoch)') ax2.set_ylabel('损失值') ax2.set_title('预测损失 (交叉熵)', fontsize=11) ax2.legend(fontsize=8) ax2.grid(True, alpha=0.3) ax2.set_yscale('log') # 子图3: 路由Loss ax3 = plt.subplot(2, 3, 3) ax3.plot(epochs, router_losses, 'r-', linewidth=1.5, label='路由损失') ax3.axvspan(qat_epoch_val, epochs[-1], alpha=0.1, color='red') ax3.set_xlabel('训练轮次 (Epoch)') ax3.set_ylabel('损失值') ax3.set_title('路由损失 (专家选择)', fontsize=11) ax3.legend(fontsize=8) ax3.grid(True, alpha=0.3) ax3.set_yscale('log') # 子图4: 路由准确率 ax4 = plt.subplot(2, 3, 4) ax4.plot(epochs, accuracies, 'purple', linewidth=1.5, marker='o', markevery=500, markersize=4) ax4.axvspan(qat_epoch_val, epochs[-1], alpha=0.1, color='red') ax4.set_xlabel('训练轮次 (Epoch)') ax4.set_ylabel('准确率') ax4.set_title('路由准确率 (专家路由)', fontsize=11) ax4.legend(fontsize=8) ax4.grid(True, alpha=0.3) ax4.set_ylim([0, 1.05]) # 子图5: 负载均衡Loss ax5 = plt.subplot(2, 3, 5) ax5.plot(epochs, balance_losses, 'orange', linewidth=1.5, label='负载均衡损失') ax5.axvspan(qat_epoch_val, epochs[-1], alpha=0.1, color='red') ax5.set_xlabel('训练轮次 (Epoch)') ax5.set_ylabel('损失值') ax5.set_title('负载均衡 (专家利用率)', fontsize=11) ax5.legend(fontsize=8) ax5.grid(True, alpha=0.3) ax5.set_yscale('log') # 子图6: 各阶段对比 (分三段) ax6 = plt.subplot(2, 3, 6) segments = [ ('早期 (0-2K)', epochs[epochs<=2000], losses[epochs<=2000], 'steelblue'), ('中期 (2K-6K)', epochs[(epochs>2000)&(epochs<=6000)], losses[(epochs>2000)&(epochs<=6000)], 'seagreen'), ('QAT量化 (8K-10K)', epochs[epochs>=8000], losses[epochs>=8000], 'firebrick'), ] for label, ep, lo, color in segments: ax6.plot(ep, lo, 'o-', color=color, linewidth=1.5, markersize=4, label=label, alpha=0.7) ax6.set_xlabel('训练轮次 (Epoch)') ax6.set_ylabel('损失值') ax6.set_title('分阶段对比 (早期/中期/QAT)', fontsize=11) ax6.legend(fontsize=8) ax6.grid(True, alpha=0.3) ax6.set_yscale('log') plt.tight_layout(rect=[0, 0, 1, 0.95]) output_path = 'c:\\Users\\Administrator\\.trae-cn\\work\\6a62468bb999696f3874e2ac\\training_analysis.png' plt.savefig(output_path, dpi=150, bbox_inches='tight', facecolor='white') plt.close() print(f"[save] training_analysis.png -> {output_path}") # 输出关键统计 print(f"\n关键数据点:") print(f" 初始 loss: {losses[0]:.4f} router_acc: {accuracies[0]:.0%}") print(f" 500 epoch: {losses[1]:.4f} router_acc: {accuracies[1]:.0%}") print(f" 2000 epoch: {losses[np.where(epochs==2000)[0][0]]:.4f} router_acc: {accuracies[np.where(epochs==2000)[0][0]]:.0%}") print(f" 6000 epoch: {losses[np.where(epochs==6000)[0][0]]:.4f} router_acc: {accuracies[np.where(epochs==6000)[0][0]]:.0%}") print(f" 9999 epoch: {losses[-1]:.4f} router_acc: {accuracies[-1]:.0%}") print(f" 最佳 loss: {losses.min():.4f} at epoch {epochs[np.argmin(losses)]}") # QAT 阶段 if any(qat_phase): qat_idx = np.where(np.array(qat_phase))[0][0] print(f"\nQAT 量化阶段:") print(f" QAT前(7500): {losses[np.where(epochs==7500)[0][0]]:.4f}") print(f" QAT后(8000): {losses[np.where(epochs==8000)[0][0]]:.4f}") print(f" QAT最终(9999): {losses[-1]:.4f}") print(f" QAT导致loss上升: +{losses[qat_idx] - losses[qat_idx-1]:.4f} (正常,量化引入噪声)") else: print(f"\nQAT 量化阶段: 未检测到QAT数据")