AI模型训练+FMQL Icraft编译仿真
流程简介1部署模型训练环境 anaconda pythouch。2训练QI1DCNN模型。3Icraft编译。4PC端仿真运行。说明本次主要记录训练IQ1DCNN模型识别IQ类型数据最终的目的是将该模型部署到FMQL100TAI板卡上以实现运行。因为复旦微主要提供yolo模型本说明旨在使用自定义模型在FMQL100TAI板卡上运行以下是详细过程。环境部署1安装anacondaAnaconda可以便捷获取包且对包能够进行管理包括了python和很多常见的软件库和一个包管理器conda。常见的科学计算类的库都包含在里面了使得安装比常规python安装要容易同时对环境可以统一管理的发行版本。下载网站Download Anaconda Distribution | Anaconda官网 从清华大学镜像下载Tsinghua Open Source Mirror国内 比较快Anaconda的安装非常简单打开下载好的 Anaconda 文件点击Next即可只有在此界面时记得勾选后一路next即可。2anaconda环境配置如果在安装时没有勾选Add Anaconda3 to my PATH environment variable那么请手动在系统环境变量中增加以下路径C:\dev\anaconda3C:\dev\anaconda3\Library\mingw-w64\binC:\dev\anaconda3\Library\usr\binC:\dev\anaconda3\Library\binC:\dev\anaconda3\Scripts设置环境变量后使用控制应用台进行验证conda --version显示版本号即可证明安装成功。替换下载源conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/conda config --set show_channel_urls yes3创建模型编译的虚拟环境aconda create -n 环境名 -y 或者 conda create -n 环境名 pythonx.x.x -yb创建完成后使用 conda env list 命令进行检查。c激活环境 conda activate myenvd删除环境 conda remove -n 环境名 --all -ye安装包 conda install numpy4pycharm接入配置可选如果需要在pycharm这个IDE进行编码和编译的话可以配置此环境在pycharm的设置界面python解释器中添加本地解释器注意一点要选择创建的虚拟环境的文件夹下的python.exe才行。模型训练本次训练的模型采用IQ1DCNN模型模型的训练数据集非常小仅有30个【21024】的数据集本次测试是为走通模型训练到Icraft编译仿真运行的过程因此模型准确率并不是要点因此采用比较少的数据进行训练。模型训练代码如下import os, time import numpy as np import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dataset, DataLoader # 1. 配置区 TRAIN_DIR rD:\AI\project\iq_project\data\train VAL_DIR rD:\AI\project\iq_project\data\val SEQ_LEN 1024 BATCH 32 EPOCHS 60 LR 3e-4 EXPORT_ONNX True # 是否导出 ONNX ONNX_PATH iq1dcnn_100tai.onnx # ONNX 输出路径 # DEVICE cuda if torch.cuda.is_available() else cpu print(Device:, DEVICE) # 2. 数据集 class IQDataset(Dataset): def __init__(self, root): self.items [] self.classes sorted([ d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d)) ]) print(Classes:, self.classes) for label_idx, cls in enumerate(self.classes): cls_dir os.path.join(root, cls) for f in os.listdir(cls_dir): if f.endswith(.npy): self.items.append((os.path.join(cls_dir, f), label_idx)) def __len__(self): return len(self.items) def __getitem__(self, idx): path, y self.items[idx] x np.load(path) return torch.tensor(x, dtypetorch.float32), y train_ds IQDataset(TRAIN_DIR) val_ds IQDataset(VAL_DIR) if os.path.isdir(VAL_DIR) and os.listdir(VAL_DIR) else train_ds train_dl DataLoader(train_ds, batch_sizeBATCH, shuffleTrue, num_workers0) val_dl DataLoader(val_ds, batch_sizeBATCH, shuffleFalse, num_workers0) num_cls len(train_ds.classes) # 3. 模型 class IQ1DCNN(nn.Module): def __init__(self): super().__init__() self.f nn.Sequential( nn.Conv1d(2, 64, 7, padding3), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(64, 128, 5, padding2), nn.BatchNorm1d(128), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(128, 256, 3, padding1), nn.BatchNorm1d(256), nn.ReLU(), nn.AdaptiveAvgPool1d(1) #nn.AvgPool1d(kernel_sizex.shape[-1]), ) self.head nn.Sequential( nn.Flatten(), nn.Dropout(0.3), nn.Linear(256, 128), nn.ReLU(), nn.Dropout(0.2), nn.Linear(128, num_cls) ) def forward(self, x): return self.head(self.f(x)) model IQ1DCNN().to(DEVICE) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lrLR, weight_decay1e-4) # 4. 训练循环 best_acc 0.0 t0 time.time() for ep in range(1, EPOCHS 1): model.train() loss_sum, n 0.0, 0 for x, y in train_dl: x, y x.to(DEVICE), y.to(DEVICE) optimizer.zero_grad() loss criterion(model(x), y) loss.backward() optimizer.step() loss_sum loss.item() * x.size(0) n x.size(0) model.eval() correct, total 0, 0 with torch.no_grad(): for x, y in val_dl: x, y x.to(DEVICE), y.to(DEVICE) pred model(x).argmax(dim1) correct (pred y).sum().item() total y.size(0) acc correct / max(total,1) * 100 print(fEpoch {ep:02d}/{EPOCHS} loss{loss_sum/n:.4f} val_acc{acc:.2f}%) if acc best_acc: best_acc acc torch.save(model.state_dict(), iq1dcnn_100tai.pth) print(f → Saved best model (acc{acc:.2f}%)) print(f\nFinished. Best val_acc{best_acc:.2f}% Time{time.time()-t0:.0f}s) # 5. 导出 ONNX新增 if EXPORT_ONNX: print(\nExporting ONNX model...) # 加载最佳权重 model.load_state_dict(torch.load(iq1dcnn_100tai.pth, map_locationcpu)) model.eval() dummy_input torch.randn(1, 2, 1024) torch.onnx.export( model.eval(), dummy_input, iq1dcnn_100tai.onnx, opset_version17, input_names[input], output_names[output], dynamic_axesNone, # ❗ 必须关 do_constant_foldingTrue, trainingtorch.onnx.TrainingMode.EVAL, dynamoFalse, # ❗ 关键 ) print(f✅ ONNX model exported to: {ONNX_PATH}) print(f Input shape: (batch, 2, {SEQ_LEN})) print(f Output shape: (batch, {num_cls})) # 验证 ONNX 模型 try: import onnx onnx_model onnx.load(ONNX_PATH) onnx.checker.check_model(onnx_model) print(✅ ONNX model check passed!) except Exception as e: print(f⚠️ ONNX check warning: {e}) print(\n All done! You can now use the ONNX file with icraft.)训练界面如下训练完成后会输出onnx类型的模型Icraft主要编译onnx类型的模型如果输出.pth的模型需要转换为onnx类型。模型转换参考代码import torch from train import IQ1DCNN # 把模型类粘过来也行 model IQ1DCNN() model.load_state_dict(torch.load(best_iq_model.pth, map_locationcpu)) model.eval() dummy torch.randn(1, 2, 1024) torch.onnx.export( model, dummy, iq_model.onnx, input_names[iq_in], output_names[scores], opset_version17, dynamic_axes{iq_in: {0: batch}, scores: {0: batch}} ) print(Exported → iq_model.onnx)模型训练完成之后需要进行测试检查是否可以正常识别数据等功能调用代码如下import numpy as np import onnxruntime as ort import os import csv from pathlib import Path # 配置 ONNX_PATH rD:\AI\project\iq_project\iq1dcnn_100tai.onnx FTMP_DIR rD:\AI\project\iq_project\calib_ftmp RESULT_CSV rD:\AI\project\iq_project\inference_results.csv # ✅ 必须与训练时 sorted() 的字母顺序完全一致 # 训练时文件夹排序: 16QAM, FM, QPSK CLS [16QAM, FM, QPSK] # def normalize_iq(iq: np.ndarray) - np.ndarray: 能量归一化 - 与训练时 convert_bin_to_npy 完全一致 pwr np.mean(iq[0] ** 2 iq[1] ** 2) if pwr 1e-12: return iq return iq / np.sqrt(pwr) def load_ftmp(ftmp_path: str) - np.ndarray: 从 .ftmp 文件读取 IQ 数据 返回: shape(2,1024) float32 data np.fromfile(ftmp_path, dtypenp.float32) if data.size ! 2048: raise ValueError(f文件大小错误: {data.size} ! 2048 (应为 2 * 1024)) # 重塑为 (2, 1024) - 训练时 np.vstack([I, Q]) 的顺序 iq data.reshape(2, 1024) return iq def inference_single_file(sess, ftmp_path: str): 对单个 .ftmp 文件进行推理 # 读取并预处理数据 iq load_ftmp(ftmp_path) iq_norm normalize_iq(iq) # 准备输入张量 (1, 2, 1024) input_tensor iq_norm.reshape(1, 2, 1024).astype(np.float32) # 推理 input_name sess.get_inputs()[0].name output_name sess.get_outputs()[0].name logits sess.run([output_name], {input_name: input_tensor})[0][0] # 获取预测结果 pred_idx int(np.argmax(logits)) pred_score float(logits[pred_idx]) pred_class CLS[pred_idx] return { filename: os.path.basename(ftmp_path), pred_class: pred_class, pred_idx: pred_idx, confidence: pred_score, logits: logits.copy(), } def save_results_to_csv(results, csv_path): 将推理结果保存到 CSV 文件 with open(csv_path, w, newline, encodingutf-8) as csvfile: fieldnames [filename, pred_class, pred_idx, confidence] [ flogit_{cls} for cls in CLS ] writer csv.DictWriter(csvfile, fieldnamesfieldnames) writer.writeheader() for result in results: row { filename: result[filename], pred_class: result[pred_class], pred_idx: result[pred_idx], confidence: result[confidence], } # 添加每个类别的 logit 值 for i, cls in enumerate(CLS): row[flogit_{cls}] result[logits][i] writer.writerow(row) print(f✅ 结果已保存到: {csv_path}) # 主流程 if __name__ __main__: print( * 60) print(IQ1DCNN 批量推理程序) print( * 60) # 1. 检查目录是否存在 if not os.path.exists(FTMP_DIR): print(f❌ 错误目录不存在 {FTMP_DIR}) exit(1) # 2. 查找所有 .ftmp 文件 ftmp_files list(Path(FTMP_DIR).glob(*.ftmp)) if not ftmp_files: print(f❌ 错误在 {FTMP_DIR} 中没有找到 .ftmp 文件) exit(1) print(f找到 {len(ftmp_files)} 个 .ftmp 文件) # 3. 加载 ONNX 模型 print(加载 ONNX 模型...) sess ort.InferenceSession(ONNX_PATH, providers[CPUExecutionProvider]) # 获取输入输出信息 input_name sess.get_inputs()[0].name output_name sess.get_outputs()[0].name print(f输入节点: {input_name}) print(f输出节点: {output_name}) # 4. 批量推理 print(\n开始批量推理...) results [] class_counts {cls: 0 for cls in CLS} for i, ftmp_file in enumerate(ftmp_files, 1): try: result inference_single_file(sess, str(ftmp_file)) results.append(result) class_counts[result[pred_class]] 1 # 打印进度 print( f[{i}/{len(ftmp_files)}] {result[filename]}: f{result[pred_class]} (置信度: {result[confidence]:.4f}) ) except Exception as e: print(f❌ 处理文件 {ftmp_file} 时出错: {str(e)}) continue # 5. 统计结果 print(\n * 60) print(推理完成统计结果) print( * 60) print(f总文件数: {len(results)}) print(\n各类别预测数量:) for cls in CLS: count class_counts[cls] percentage (count / len(results)) * 100 if results else 0 print(f {cls}: {count} 个 ({percentage:.1f}%)) # 6. 保存结果到 CSV save_results_to_csv(results, RESULT_CSV) # 7. 显示前5个结果的详细信息 print(\n前5个文件的详细结果:) print(- * 60) for result in results[:5]: print(f{result[filename]}:) print(f 预测类别: {result[pred_class]}) print(f 置信度: {result[confidence]:.6f}) print(f 所有类别得分: {dict(zip(CLS, result[logits]))}) print() # 8. 特别提示 print( * 60) print(重要提示) print( * 60) print(1. 训练时只有 3 个类别: 16QAM, FM, QPSK) print(2. 模型无法识别 8PSK/BPSK 等其他调制类型) print(3. 如果输入是其他类型会被强制归类到这 3 个之一) print(4. 请检查 CSV 文件中的 logit 值确认模型输出是否合理) # 9. 检查归一化是否正确 if results: print(\n归一化检查:) print(- * 60) # 检查第一个文件的归一化后功率 iq load_ftmp(str(ftmp_files[0])) iq_norm normalize_iq(iq) power np.mean(iq_norm[0] ** 2 iq_norm[1] ** 2) print(f第一个文件归一化后功率: {power:.6f}) if abs(power - 1.0) 0.01: print(✅ 归一化正确) else: print(⚠️ 归一化可能有问题功率应接近 1.0)调用示例如下并且代码会生成csv文件保存Icraft模型编译模型可以在FMQL100tai上运行的关键在于使用Icraft工具将其编译为NPU可以运行的模型。Icraft工具的详细介绍和使用请参考官方文档 APPN6002_Icraft_v2_用户手册_v2.2.pdf此处不做详细介绍。Icraft编译和部署原理如下其中编译部分的功能组件为解析器、优化器、量化器、适配器和指令生成。解析器将框架 模型解析到IcraftIR表达的模型。优化器对解析后的模型进行优化主要针对模型的冗余算子的消 融属于数学等价变换操作不会改变模型结果与精度。量化器将模型的Featuremap和权重参数 进行量化该操作会改变模型结果与精度。指令生成将量化后的模型进行硬件适配与编译生成指 令序列。 运行时提供丰富的API支持C和python接口可支持用户程序读写硬件寄存器、控制硬 件运行、获取运行信息等功能。 仿真器中提供浮点仿真与定点仿真可针对编译过程的各步骤进行仿真——包括解析后的浮点 仿真优化后的浮点仿真量化后的定点仿真适配后的定点仿真。各级仿真都将输出仿真结果。 性能分析器支持用户获取硬件运行时信息包括运行时间运行效率带宽利用等。Icraft编译器通过Icraftcompile命令进行调用后面可以跟配置文件也可以直接通过命令参 数进行配置该命令将完成完整的编译过程。编译过程还可以按功能组件分步骤进行命令调用 解析、优化、适配、量化、适配、指令生成括弧内的步骤表示非必须功能用户可 以按实际情况选择使用。每个编译步骤的命令分别如下所示注除解析阶段外各编译步骤都需要将上一阶段生成的json和raw文件作为输入并输出当前 阶段的json和raw。Icraft 编译器的参数可以在Icraft的INI配置文件中进行配置也可以直接在命令行通过直接配 置还可以两者结合命令行中配置的参数会覆盖INI文件中的对应参数。新版本已经使用toml文件进行配置配置示例如下[parse] net_name IQ1DCNN framework onnx # 使用 ONNX 格式 inputs [1, 2, 1024] # IQ 信号batch1, channels2, length1024 inputs_layout FD # 1D 信号使用 Feature-Dimension 布局 pre_method nop # 不做预处理 pre_scale nop # 不做缩放 pre_mean nop # 不做均值减法 channel_swap nop # 不需要通道交换 network ./iq1dcnn_100tai.onnx # ONNX 模型路径 jr_path ./jsonraw/IQ1DCNN/ # 中间文件输出目录 frame_version 1.9 # 框架版本 [optimize] target BUYI json ./jsonraw/IQ1DCNN/IQ1DCNN_parsed.json raw ./jsonraw/IQ1DCNN/IQ1DCNN_parsed.raw jr_path ./jsonraw/IQ1DCNN/ [quantize] forward_mode image saturation kld forward_dir D:/AI/project/iq_project/calib_ftmp forward_list D:/AI/project/iq_project/calib_ftmp/calib_list.txt bits 8 json D:/AI/project/iq_project/jsonraw/IQ1DCNN/IQ1DCNN_optimized.json raw D:/AI/project/iq_project/jsonraw/IQ1DCNN/IQ1DCNN_optimized.raw jr_path D:/AI/project/iq_project/jsonraw/IQ1DCNN/ per tensor target buyi no_transinput true no_imagemake true [adapt] target BUYI json ./jsonraw/IQ1DCNN/IQ1DCNN_quantized.json raw ./jsonraw/IQ1DCNN/IQ1DCNN_quantized.raw jr_path ./jsonraw/IQ1DCNN/ [generate] json ./jsonraw/IQ1DCNN/IQ1DCNN_adapted.json raw ./jsonraw/IQ1DCNN/IQ1DCNN_adapted.raw jr_path ./jsonraw/IQ1DCNN/ log_path ./logs/ [run] log_io true # 记录输入输出信息 dump_format SFB # 保存为 SFB 格式 json ./jsonraw/IQ1DCNN/IQ1DCNN_optimized.json raw ./jsonraw/IQ1DCNN/IQ1DCNN_optimized.raw input ./calib_data/test_sample.npy # 测试输入文件 log_path ./logs/ # 日志输出目录 backends Host # 在上位机运行注意run标签下的配置只在模型在pc端进行仿真运行时生效在板卡上运行时并无影响。在conda仿真环境中调用Icraft进行仿真还需要安装对应版本的运行库python -m pip install icraft-3.37.1-cp38-none-win_amd64.whl这个运行库需要在官方网站进行下载。仿真调用代码可以参考官方位置为PC端仿真运行调用代码#!/usr/bin/env python3 PC 端仿真运行 IQ1DCNN 模型 修复 Softmax 问题 import os import sys import warnings import numpy as np import time warnings.filterwarnings(ignore) import icraft from icraft.xir import * from icraft.xrt import * from icraft.host_backend import * # 配置区 JSON_FILE ./icraft_out/IQ1DCNN_BY.json RAW_FILE ./icraft_out/IQ1DCNN_BY.raw IQ_DATA ./data/train/QPSK/QPSK_00000.npy # def tensor_to_numpy(tensor): 将 icraft Tensor 转换为 numpy 数组 try: # 获取 tensor 信息 shape tensor.dtype().shape element_dtype str(tensor.dtype().element_dtype) # 映射 icraft dtype 到 numpy dtype dtype_map { fp(32): np.float32, fp(16): np.float16, uint(8): np.uint8, sint(8): np.int8, } np_dtype dtype_map.get(element_dtype, np.float32) total_elements np.prod(shape) # 方法1: 使用 Tensor 的 read 方法 if hasattr(tensor, read) and callable(tensor.read): # 创建目标 buffer dest bytearray(total_elements * np.dtype(np_dtype).itemsize) # 读取数据 tensor.read(dest, 0, len(dest)) return np.frombuffer(dest, dtypenp_dtype).reshape(shape) # 方法2: 通过 chunk 读取 chunk tensor.chunk() if hasattr(chunk, read) and callable(chunk.read): # 创建目标 buffer dest bytearray(total_elements * np.dtype(np_dtype).itemsize) # 从 chunk 读取数据 chunk.read(dest, 0, len(dest)) return np.frombuffer(dest, dtypenp_dtype).reshape(shape) return None except Exception as e: print(f[ERROR] Failed to convert tensor: {e}) return None def softmax(x): 计算 softmax exp_x np.exp(x - np.max(x, axis-1, keepdimsTrue)) return exp_x / np.sum(exp_x, axis-1, keepdimsTrue) def run_iq_model_on_pc(json_path, raw_path, iq_data_path): 在PC上仿真运行IQ1DCNN模型 print(f[INFO] Starting PC simulation...) print(f Model: {json_path}) print(f Data: {iq_data_path}) # 检查文件是否存在 if not os.path.exists(iq_data_path): sys.exit(f[ERROR] IQ data not found: {iq_data_path}) if not os.path.exists(json_path): sys.exit(f[ERROR] JSON file not found: {json_path}) if not os.path.exists(raw_path): sys.exit(f[ERROR] RAW file not found: {raw_path}) # 1. 加载IQ数据 iq_data np.load(iq_data_path) # 形状: (2, 1024) print(f[INFO] IQ data loaded, shape: {iq_data.shape}) # 2. 加载网络 print(f[INFO] Loading network...) try: network Network.CreateFromJsonFile(json_path) network.loadParamsFromFile(raw_path) print(f[INFO] Network loaded) except Exception as e: print(f[ERROR] Failed to load network: {e}) sys.exit(1) # 3. 查看模型期望的输入形状 input_node network.ops[0].outputs[0] input_tensor_type input_node.tensorType() print(f\n[DEBUG] Model expects:) print(f - Shape: {input_tensor_type.shape}) print(f - Layout: {input_tensor_type.layout}) print(f - Element dtype: {input_tensor_type.element_dtype}) # 4. 查看输出节点 output_node network.ops[-1].inputs[0] output_tensor_type output_node.tensorType() print(f\n[DEBUG] Model output:) print(f - Shape: {output_tensor_type.shape}) print(f - Layout: {output_tensor_type.layout}) print(f - Element dtype: {output_tensor_type.element_dtype}) # 5. 根据模型期望的形状调整输入数据 expected_shape input_tensor_type.shape print(f\n[INFO] Adjusting input to expected shape: {expected_shape}) # 模型期望 [1, 2, 1024]直接 reshape iq_reshaped iq_data.reshape(1, 2, 1024).astype(np.float32) # 确保内存连续 iq_reshaped np.ascontiguousarray(iq_reshaped) print(f[INFO] Reshaped input to: {iq_reshaped.shape}) # 6. 归一化 power np.mean(iq_reshaped**2) if power 0: iq_reshaped iq_reshaped / np.sqrt(power) print(f[INFO] Normalized, power{power:.6f}) # 7. 创建Tensor print(f[INFO] Creating Tensor with layout: {input_tensor_type.layout}) try: input_tensor Tensor(iq_reshaped, input_tensor_type.layout) print(f[INFO] Tensor created successfully) except Exception as e: print(f[ERROR] Failed to create Tensor: {e}) print(f[DEBUG] Input shape: {iq_reshaped.shape}) print(f[DEBUG] Expected shape: {expected_shape}) sys.exit(1) # 8. 创建会话 print(f[INFO] Creating session...) try: session Session.Create([HostBackend], network.view(0), [HostDevice.Default()]) session.apply() print(f[INFO] Session created) except Exception as e: print(f[ERROR] Failed to create session: {e}) sys.exit(1) # 9. 推理 print(f[INFO] Running inference...) start_time time.time() try: outputs session.forward([input_tensor]) inference_time (time.time() - start_time) * 1000 print(f[INFO] Inference completed in {inference_time:.2f} ms) except Exception as e: print(f[ERROR] Inference failed: {e}) sys.exit(1) # 10. 处理输出 print(f[INFO] Processing outputs...) print(f[INFO] Number of outputs: {len(outputs)}) for i, output in enumerate(outputs): print(f\n[DEBUG] Output {i} details:) print(f Type: {type(output)}) print(f Shape: {output.dtype().shape}) print(f Element dtype: {output.dtype().element_dtype}) # 11. 转换 Tensor 为 numpy logits tensor_to_numpy(outputs[0]) if logits is None: print(f[ERROR] Failed to convert tensor to numpy) sys.exit(1) print(f[INFO] Raw logits shape: {logits.shape}) print(f[INFO] Raw logits values: {logits}) # 12. 应用 softmax 转换为概率 probs softmax(logits) print(f[INFO] After softmax: {probs}) # 13. 输出结果 print(f\n{ * 60}) print(fINFERENCE RESULTS) print(f{ * 60}) # 根据输出维度决定类别名称 if probs.shape[-1] 4: class_names [16QAM, FM, QPSK, Unknown] elif probs.shape[-1] 3: class_names [16QAM, FM, QPSK] # 没有 Unknown else: class_names [fClass_{i} for i in range(probs.shape[-1])] probs_flat probs.flatten() for i, (cls, prob) in enumerate(zip(class_names, probs_flat)): print(f {cls:10s}: {prob:.6f} ({prob * 100:.2f}%)) predicted_idx np.argmax(probs_flat) confidence probs_flat[predicted_idx] print(f\nPrediction: {class_names[predicted_idx]}) print(fConfidence: {confidence:.4f} ({confidence * 100:.2f}%)) print(fInference time: {inference_time:.2f} ms) print(f{ * 60}) return probs_flat, class_names, predicted_idx, confidence if __name__ __main__: # 检查文件是否存在 print(f[DEBUG] Current directory: {os.getcwd()}) print(f[DEBUG] JSON exists: {os.path.exists(JSON_FILE)}) print(f[DEBUG] RAW exists: {os.path.exists(RAW_FILE)}) if os.path.exists(IQ_DATA): print(f[DEBUG] IQ data exists: {IQ_DATA}) else: # 尝试其他可能的路径 possible_paths [ ./data/train/QPSK/QPSK_00000.npy, ./data/train/16QAM/16QAM_00000.npy, ./data/train/FM/FM_00000.npy, ] for path in possible_paths: if os.path.exists(path): IQ_DATA path print(f[DEBUG] Using IQ data: {path}) break else: print(f[ERROR] No IQ data found!) sys.exit(1) # 运行 probs, class_names, predicted_idx, confidence run_iq_model_on_pc( JSON_FILE, RAW_FILE, IQ_DATA ) # 保存结果 output_file ./inference_result.txt with open(output_file, w) as f: f.write( * 50 \n) f.write(IQ1DCNN Inference Result\n) f.write( * 50 \n) f.write(fModel: {JSON_FILE}\n) f.write(fInput: {IQ_DATA}\n) f.write(fPrediction: {class_names[predicted_idx]}\n) f.write(fConfidence: {confidence:.4f} ({confidence * 100:.2f}%)\n) f.write(fInference time: 52.89 ms\n) f.write(\nProbabilities:\n) for i, (cls, prob) in enumerate(zip(class_names, probs)): f.write(f {cls:10s}: {prob:.6f} ({prob * 100:.2f}%)\n) f.write( * 50 \n) print(f[INFO] Result saved to: {output_file})仿真运行结果如下至此模型训练到Icraft编译仿真运行完成