chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换)
This commit is contained in:
1 parent
33d91e5d17
commit
85b89b6e8d
16 files changed
+314
-314
No files matched your search
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "Desktop chat client: vqt6 GUI, requests(https) for network, json for parsing responses",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "Standalone Viper repro for CreateProcessA returning 0 / GetLastError=87",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# main2.vp — ViperFLProject fltk GUI 测试(回调 + 控件展示)
|
||||
#
|
||||
# 构建需 TPV/TPC 支持 project.vpj linker.static_libs 静态链接
|
||||
# 构建需 VPC/TPC 支持 project.vpj linker.static_libs 静态链接
|
||||
# (本工程已配置链接 ../includes/fltk/cfltk/build 下的 cfltk2 + FLTK 静态库)。
|
||||
#
|
||||
# 回调说明:Viper 模块级函数可直接作为 Fl_Callback 传入(TPV 自动把函数名
|
||||
# 回调说明:Viper 模块级函数可直接作为 Fl_Callback 传入(VPC 自动把函数名
|
||||
# bitcast 为函数指针)。回调签名统一为 def cb(w: bytes, data: bytes),
|
||||
# 其中 w 是触发回调的控件指针,data 是注册时传入的用户数据。
|
||||
import t, c
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ViperFLProject - FLTK GUI Test",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
+293
-293
@@ -1,293 +1,293 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ============================================================
|
||||
# ViperMNIST - 28x28 手写数字识别(MLP / CNN, torch-lite)
|
||||
#
|
||||
# 入口分发:
|
||||
# * 无命令行参数 → 启动 Qt6 图形面板(数据/训练/推理, 含 3D 网络图)
|
||||
# * 带命令行参数 → 走原 CLI(train / use)
|
||||
#
|
||||
# CLI 用法:
|
||||
# app.exe --mode train --data-dir data --epochs 5 --batch 64 --lr 0.01 \
|
||||
# --hidden 256 --save models/mnist.bin --limit 60000
|
||||
# app.exe --mode train --arch cnn --c1 8 --c2 16 (卷积 + 池化, 更抗形变)
|
||||
# app.exe --mode use --data-dir data --load models/mnist.bin --show 10
|
||||
# --download 自动下载缺失数据; --file 指定本地已下载 .gz(可 1~2 次)
|
||||
# --no-aug 关闭数据增强(默认开: 训练时随机平移 ±2px)
|
||||
# 注: use 模式的架构/hidden/通道以模型文件头为准, 无需与命令行走一致
|
||||
#
|
||||
# 模型: MLP = Linear(784->hidden) -> ReLU -> Linear(hidden->10)
|
||||
# CNN = Conv(1->c1,3x3,pad1) -> ReLU -> MaxPool2 -> Conv(c1->c2,3x3,pad1)
|
||||
# -> ReLU -> MaxPool2 -> Linear(49*c2->hidden) -> ReLU -> Linear(hidden->10)
|
||||
# 权重形状同 PyTorch: Conv(out,in,k,k) / Linear(out,in), b(out)
|
||||
# 计算: torch-lite 线性前向走 vipersimd.simd_dot4d, relu 走 simd_max4d
|
||||
# ============================================================
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdlib
|
||||
import string
|
||||
import stdio
|
||||
import sys
|
||||
import memhub
|
||||
import argparse
|
||||
import requests
|
||||
import torch
|
||||
import w32.win32console as w32cmd
|
||||
import lib.MnistData as MnistData
|
||||
import lib.MnistNet as MnistNet
|
||||
import lib.AppGui as AppGui
|
||||
|
||||
define CODE_PAGE = 65001 # UTF-8 控制台
|
||||
|
||||
# 主内存池(CLI 与 GUI 共用)
|
||||
define POOL_SIZE = 536870912 # 512 MB
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CLI
|
||||
# ============================================================
|
||||
def run_cli(pool: memhub.MemBuddy*) -> int:
|
||||
parser: argparse.ArgumentParser* = argparse.ArgumentParser(
|
||||
"ViperMNIST", "28x28 MNIST 手写数字识别 (train/use)", pool=pool)
|
||||
parser.add_argument("--mode", None, argparse.STRING, 0, "train", False,
|
||||
argparse.STORE, "train=训练 / use=加载模型推理")
|
||||
parser.add_argument("--data-dir", None, argparse.STRING, 0, "data", False,
|
||||
argparse.STORE, "MNIST IDX 文件所在目录")
|
||||
parser.add_argument("--epochs", None, argparse.INT, 5, None, False,
|
||||
argparse.STORE, "训练轮数")
|
||||
parser.add_argument("--batch", None, argparse.INT, 64, None, False,
|
||||
argparse.STORE, "批大小")
|
||||
parser.add_argument("--lr", None, argparse.STRING, 0, "0.01", False,
|
||||
argparse.STORE, "学习率(小数; 训练用重球法, 默认 0.01)")
|
||||
parser.add_argument("--hidden", None, argparse.INT, 256, None, False,
|
||||
argparse.STORE, "隐藏层宽度(CNN 下为 FC1 宽度)")
|
||||
parser.add_argument("--arch", None, argparse.STRING, 0, "mlp", False,
|
||||
argparse.STORE, "架构: mlp=全连接 / cnn=卷积+池化")
|
||||
parser.add_argument("--c1", None, argparse.INT, 8, None, False,
|
||||
argparse.STORE, "CNN conv1 通道数(仅 --arch cnn 生效)")
|
||||
parser.add_argument("--c2", None, argparse.INT, 16, None, False,
|
||||
argparse.STORE, "CNN conv2 通道数(仅 --arch cnn 生效)")
|
||||
parser.add_argument("--no-aug", None, argparse.BOOL, 0, None, False,
|
||||
argparse.STORE_TRUE, "关闭数据增强(默认开启: 训练时随机平移 ±2px)")
|
||||
parser.add_argument("--limit", None, argparse.INT, 0, None, False,
|
||||
argparse.STORE, "样本上限(0=全部)")
|
||||
parser.add_argument("--save", None, argparse.STRING, 0, "mnist.bin",
|
||||
False, argparse.STORE, "训练保存路径")
|
||||
parser.add_argument("--load", None, argparse.STRING, 0, "mnist.bin",
|
||||
False, argparse.STORE, "推理加载路径")
|
||||
parser.add_argument("--show", None, argparse.INT, 10, None, False,
|
||||
argparse.STORE, "use 模式打印前 N 条预测")
|
||||
parser.add_argument("--seed", None, argparse.INT, 42, None, False,
|
||||
argparse.STORE, "随机种子")
|
||||
parser.add_argument("--download", None, argparse.BOOL, 0, None, False,
|
||||
argparse.STORE_TRUE, "自动下载缺失的 MNIST 数据")
|
||||
parser.add_argument("--file", None, argparse.STRING, 0, None, False,
|
||||
argparse.STORE, "本地已下载的 .gz(可 1~2 次: 图像/标签各一份), 优先本地解压替代联网")
|
||||
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False,
|
||||
argparse.STORE_TRUE, "显示帮助并退出")
|
||||
|
||||
args: argparse.ParsedArgs* = parser.parse_args(sys._argc, sys._argv)
|
||||
if args is None:
|
||||
stdio.printf("参数解析失败\n")
|
||||
return 1
|
||||
if args.get_bool("help"):
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
mode: str = args.get_str("mode")
|
||||
data_dir: str = args.get_str("data-dir")
|
||||
epochs: int = args.get_int("epochs")
|
||||
batch: int = args.get_int("batch")
|
||||
hidden: int = args.get_int("hidden")
|
||||
limit: int = args.get_int("limit")
|
||||
show: int = args.get_int("show")
|
||||
seed: int = args.get_int("seed")
|
||||
save_path: str = args.get_str("save")
|
||||
load_path: str = args.get_str("load")
|
||||
arch_str: str = args.get_str("arch")
|
||||
c1_arg: int = args.get_int("c1")
|
||||
c2_arg: int = args.get_int("c2")
|
||||
lr_str: str = args.get_str("lr")
|
||||
download: int = 0
|
||||
if args.get_bool("download"):
|
||||
download = 1
|
||||
no_aug: int = 0
|
||||
if args.get_bool("no-aug"):
|
||||
no_aug = 1
|
||||
|
||||
# --file 本地 .gz(可 1~2 次): 按文件名含 -labels-/-images- 归类, 无名匹配按先后补空位
|
||||
loc_im: str = None
|
||||
loc_lb: str = None
|
||||
i_arg: int = 1
|
||||
while i_arg + 1 < sys._argc:
|
||||
if string.strcmp(sys._argv[i_arg], "--file") == 0:
|
||||
fv: str = sys._argv[i_arg + 1]
|
||||
is_lb: int = 0
|
||||
if string.strstr(fv, "-labels-") is not None:
|
||||
is_lb = 1
|
||||
is_im: int = 0
|
||||
if string.strstr(fv, "-images-") is not None:
|
||||
is_im = 1
|
||||
if is_lb != 0 and is_im == 0 and loc_lb is None:
|
||||
loc_lb = fv
|
||||
elif is_im != 0 and is_lb == 0 and loc_im is None:
|
||||
loc_im = fv
|
||||
elif loc_im is None:
|
||||
loc_im = fv
|
||||
elif loc_lb is None:
|
||||
loc_lb = fv
|
||||
i_arg += 2
|
||||
else:
|
||||
i_arg += 1
|
||||
file_set: int = 0
|
||||
if loc_im is not None or loc_lb is not None:
|
||||
file_set = 1
|
||||
|
||||
lr: double = double(0.01)
|
||||
if lr_str is not None and string.strlen(lr_str) > 0:
|
||||
lr = MnistData.atof(lr_str, lr)
|
||||
|
||||
MnistNet.rand_seed(uint64(seed))
|
||||
torch.manual_seed(int64(seed))
|
||||
|
||||
h: sizet = sizet(hidden)
|
||||
# 架构与数据增强: 与 GUI 口径一致(通道数只在 CNN 下有意义)
|
||||
arch: int = MnistNet.ARCH_MLP
|
||||
if string.strcmp(arch_str, "cnn") == 0 or string.strcmp(arch_str, "CNN") == 0:
|
||||
arch = MnistNet.ARCH_CNN
|
||||
c1: sizet = 0
|
||||
c2: sizet = 0
|
||||
if arch == MnistNet.ARCH_CNN:
|
||||
c1 = sizet(c1_arg)
|
||||
c2 = sizet(c2_arg)
|
||||
augment: int = 1
|
||||
if no_aug != 0:
|
||||
augment = 0
|
||||
stdio.printf("ViperMNIST mode=%s data=%s limit=%d hidden=%d epochs=%d batch=%d lr=%.3f arch=%s\n",
|
||||
mode, data_dir, limit, hidden, epochs, batch, lr, arch_str)
|
||||
|
||||
p_img: str = stdlib.malloc(1024)
|
||||
p_lbl: str = stdlib.malloc(1024)
|
||||
|
||||
if string.strcmp(mode, "train") == 0:
|
||||
net: MnistNet.Net* = MnistNet.Net(pool, h, sizet(arch), c1, c2)
|
||||
if MnistNet.net_ok(net) == 0:
|
||||
stdio.printf("FAIL: 网络分配失败\n")
|
||||
return 1
|
||||
MnistNet.init_net_weights(net)
|
||||
MnistData.idx_paths(data_dir, 0, p_img, p_lbl)
|
||||
if download != 0 or file_set != 0:
|
||||
if MnistData.ensure_mnist(data_dir, 0, download, loc_im, loc_lb) != 0:
|
||||
stdio.printf("数据就绪失败, 请检查 --file 路径 / 网络 / data-dir\n")
|
||||
return 1
|
||||
cap: sizet = sizet(60000)
|
||||
if limit > 0 and limit < 60000:
|
||||
cap = sizet(limit)
|
||||
xbuf: double* = stdlib.malloc(cap * MnistData.N_IN * 8)
|
||||
ybuf: int* = stdlib.malloc(cap * 4)
|
||||
if xbuf is None or ybuf is None:
|
||||
stdio.printf("FAIL: 训练数据缓冲分配失败\n")
|
||||
return 1
|
||||
n_im: sizet = MnistData.load_mnist_images(p_img, xbuf, cap)
|
||||
n_lb: sizet = MnistData.load_mnist_labels(p_lbl, ybuf, cap)
|
||||
n_tr: sizet = n_im
|
||||
if n_lb < n_tr:
|
||||
n_tr = n_lb
|
||||
stdio.printf("加载训练样本=%zu 张 (%s)\n", n_tr, MnistNet.arch_name(arch))
|
||||
if n_tr == 0:
|
||||
stdio.printf("未找到可用数据。如 data 目录文件残缺/损坏, 请加 --download 重新拉取或用 --file 指定本地 .gz\n")
|
||||
return 1
|
||||
MnistNet.prog_init()
|
||||
MnistNet.train_run(pool, net, xbuf, ybuf, n_tr,
|
||||
sizet(batch), sizet(epochs), lr, save_path, augment)
|
||||
stdlib.free(xbuf)
|
||||
stdlib.free(ybuf)
|
||||
elif string.strcmp(mode, "use") == 0:
|
||||
# 架构/hidden/通道以模型文件头为准(命令行给的对推理无意义), 先读头再建网
|
||||
minfo: int* = stdlib.malloc(20)
|
||||
if minfo is None:
|
||||
stdio.printf("FAIL: 模型头缓冲分配失败\n")
|
||||
return 1
|
||||
if MnistNet.peek_model(load_path, minfo) == 0:
|
||||
stdlib.free(minfo)
|
||||
stdio.printf("加载模型失败, 请先训练(--mode train): %s\n", load_path)
|
||||
return 1
|
||||
f_arch: int = minfo[0]
|
||||
f_hidden: int = minfo[1]
|
||||
f_c1: int = minfo[2]
|
||||
f_c2: int = minfo[3]
|
||||
stdlib.free(minfo)
|
||||
uh: sizet = sizet(f_hidden)
|
||||
uc1: sizet = 0
|
||||
uc2: sizet = 0
|
||||
if f_arch == MnistNet.ARCH_CNN:
|
||||
uc1 = sizet(f_c1)
|
||||
uc2 = sizet(f_c2)
|
||||
net2: MnistNet.Net* = MnistNet.Net(pool, uh, sizet(f_arch),
|
||||
uc1, uc2)
|
||||
if MnistNet.net_ok(net2) == 0:
|
||||
stdio.printf("FAIL: 网络分配失败\n")
|
||||
return 1
|
||||
MnistData.idx_paths(data_dir, 1, p_img, p_lbl)
|
||||
if download != 0 or file_set != 0:
|
||||
if MnistData.ensure_mnist(data_dir, 1, download, loc_im, loc_lb) != 0:
|
||||
stdio.printf("数据就绪失败, 请检查 --file 路径 / 网络 / data-dir\n")
|
||||
return 1
|
||||
cap2: sizet = sizet(10000)
|
||||
if limit > 0 and limit < 10000:
|
||||
cap2 = sizet(limit)
|
||||
xbuf2: double* = stdlib.malloc(cap2 * MnistData.N_IN * 8)
|
||||
ybuf2: int* = stdlib.malloc(cap2 * 4)
|
||||
if xbuf2 is None or ybuf2 is None:
|
||||
stdio.printf("FAIL: 测试数据缓冲分配失败\n")
|
||||
return 1
|
||||
n_te: sizet = MnistData.load_mnist_images(p_img, xbuf2, cap2)
|
||||
n_tl: sizet = MnistData.load_mnist_labels(p_lbl, ybuf2, cap2)
|
||||
if n_tl < n_te:
|
||||
n_te = n_tl
|
||||
stdio.printf("加载测试样本=%zu 张\n", n_te)
|
||||
if n_te == 0:
|
||||
return 1
|
||||
if MnistNet.load_model(net2, load_path) != 0:
|
||||
stdio.printf("加载模型失败, 请先训练(--mode train)\n")
|
||||
return 1
|
||||
stdio.printf("加载模型: %s (%s, hidden=%d)\n",
|
||||
load_path, MnistNet.arch_name(f_arch), f_hidden)
|
||||
MnistNet.evaluate_net(pool, net2, xbuf2, ybuf2, n_te,
|
||||
sizet(batch), sizet(show))
|
||||
stdlib.free(xbuf2)
|
||||
stdlib.free(ybuf2)
|
||||
else:
|
||||
stdio.printf("未知模式: %s (可选 train/use)\n", mode)
|
||||
return 1
|
||||
|
||||
stdlib.free(p_img)
|
||||
stdlib.free(p_lbl)
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 入口
|
||||
# ============================================================
|
||||
def main() -> int:
|
||||
# UTF-8 控制台(与 TransPyV main 一致)
|
||||
w32cmd.SetConsoleOutputCP(CODE_PAGE)
|
||||
w32cmd.SetConsoleCP(CODE_PAGE)
|
||||
|
||||
# 主内存池: CLI 与 GUI 共用, 先建池再解析 argv(GUI 分支也需要池)
|
||||
arena: bytes = stdlib.malloc(POOL_SIZE)
|
||||
if arena is None:
|
||||
stdio.printf("FAIL: 无法分配主池\n")
|
||||
return 1
|
||||
mb: memhub.MemBuddy* = memhub.MemBuddy(arena, POOL_SIZE, 0)
|
||||
if mb is None:
|
||||
stdio.printf("FAIL: MemBuddy 初始化失败\n")
|
||||
return 1
|
||||
argparse._mbuddy = mb
|
||||
sys._mbuddy = mb
|
||||
requests._mbuddy = mb # requests/https 内部用 _mbuddy 分配响应缓冲
|
||||
sys._init_argv()
|
||||
|
||||
# 无参数 → Qt 面板; 有参数 → CLI
|
||||
if sys._argc <= 1:
|
||||
return AppGui.run(mb)
|
||||
return run_cli(mb)
|
||||
# -*- coding: utf-8 -*-
|
||||
# ============================================================
|
||||
# ViperMNIST - 28x28 手写数字识别(MLP / CNN, torch-lite)
|
||||
#
|
||||
# 入口分发:
|
||||
# * 无命令行参数 → 启动 Qt6 图形面板(数据/训练/推理, 含 3D 网络图)
|
||||
# * 带命令行参数 → 走原 CLI(train / use)
|
||||
#
|
||||
# CLI 用法:
|
||||
# app.exe --mode train --data-dir data --epochs 5 --batch 64 --lr 0.01 \
|
||||
# --hidden 256 --save models/mnist.bin --limit 60000
|
||||
# app.exe --mode train --arch cnn --c1 8 --c2 16 (卷积 + 池化, 更抗形变)
|
||||
# app.exe --mode use --data-dir data --load models/mnist.bin --show 10
|
||||
# --download 自动下载缺失数据; --file 指定本地已下载 .gz(可 1~2 次)
|
||||
# --no-aug 关闭数据增强(默认开: 训练时随机平移 ±2px)
|
||||
# 注: use 模式的架构/hidden/通道以模型文件头为准, 无需与命令行走一致
|
||||
#
|
||||
# 模型: MLP = Linear(784->hidden) -> ReLU -> Linear(hidden->10)
|
||||
# CNN = Conv(1->c1,3x3,pad1) -> ReLU -> MaxPool2 -> Conv(c1->c2,3x3,pad1)
|
||||
# -> ReLU -> MaxPool2 -> Linear(49*c2->hidden) -> ReLU -> Linear(hidden->10)
|
||||
# 权重形状同 PyTorch: Conv(out,in,k,k) / Linear(out,in), b(out)
|
||||
# 计算: torch-lite 线性前向走 vipersimd.simd_dot4d, relu 走 simd_max4d
|
||||
# ============================================================
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdlib
|
||||
import string
|
||||
import stdio
|
||||
import sys
|
||||
import memhub
|
||||
import argparse
|
||||
import requests
|
||||
import torch
|
||||
import w32.win32console as w32cmd
|
||||
import lib.MnistData as MnistData
|
||||
import lib.MnistNet as MnistNet
|
||||
import lib.AppGui as AppGui
|
||||
|
||||
define CODE_PAGE = 65001 # UTF-8 控制台
|
||||
|
||||
# 主内存池(CLI 与 GUI 共用)
|
||||
define POOL_SIZE = 536870912 # 512 MB
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CLI
|
||||
# ============================================================
|
||||
def run_cli(pool: memhub.MemBuddy*) -> int:
|
||||
parser: argparse.ArgumentParser* = argparse.ArgumentParser(
|
||||
"ViperMNIST", "28x28 MNIST 手写数字识别 (train/use)", pool=pool)
|
||||
parser.add_argument("--mode", None, argparse.STRING, 0, "train", False,
|
||||
argparse.STORE, "train=训练 / use=加载模型推理")
|
||||
parser.add_argument("--data-dir", None, argparse.STRING, 0, "data", False,
|
||||
argparse.STORE, "MNIST IDX 文件所在目录")
|
||||
parser.add_argument("--epochs", None, argparse.INT, 5, None, False,
|
||||
argparse.STORE, "训练轮数")
|
||||
parser.add_argument("--batch", None, argparse.INT, 64, None, False,
|
||||
argparse.STORE, "批大小")
|
||||
parser.add_argument("--lr", None, argparse.STRING, 0, "0.01", False,
|
||||
argparse.STORE, "学习率(小数; 训练用重球法, 默认 0.01)")
|
||||
parser.add_argument("--hidden", None, argparse.INT, 256, None, False,
|
||||
argparse.STORE, "隐藏层宽度(CNN 下为 FC1 宽度)")
|
||||
parser.add_argument("--arch", None, argparse.STRING, 0, "mlp", False,
|
||||
argparse.STORE, "架构: mlp=全连接 / cnn=卷积+池化")
|
||||
parser.add_argument("--c1", None, argparse.INT, 8, None, False,
|
||||
argparse.STORE, "CNN conv1 通道数(仅 --arch cnn 生效)")
|
||||
parser.add_argument("--c2", None, argparse.INT, 16, None, False,
|
||||
argparse.STORE, "CNN conv2 通道数(仅 --arch cnn 生效)")
|
||||
parser.add_argument("--no-aug", None, argparse.BOOL, 0, None, False,
|
||||
argparse.STORE_TRUE, "关闭数据增强(默认开启: 训练时随机平移 ±2px)")
|
||||
parser.add_argument("--limit", None, argparse.INT, 0, None, False,
|
||||
argparse.STORE, "样本上限(0=全部)")
|
||||
parser.add_argument("--save", None, argparse.STRING, 0, "mnist.bin",
|
||||
False, argparse.STORE, "训练保存路径")
|
||||
parser.add_argument("--load", None, argparse.STRING, 0, "mnist.bin",
|
||||
False, argparse.STORE, "推理加载路径")
|
||||
parser.add_argument("--show", None, argparse.INT, 10, None, False,
|
||||
argparse.STORE, "use 模式打印前 N 条预测")
|
||||
parser.add_argument("--seed", None, argparse.INT, 42, None, False,
|
||||
argparse.STORE, "随机种子")
|
||||
parser.add_argument("--download", None, argparse.BOOL, 0, None, False,
|
||||
argparse.STORE_TRUE, "自动下载缺失的 MNIST 数据")
|
||||
parser.add_argument("--file", None, argparse.STRING, 0, None, False,
|
||||
argparse.STORE, "本地已下载的 .gz(可 1~2 次: 图像/标签各一份), 优先本地解压替代联网")
|
||||
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False,
|
||||
argparse.STORE_TRUE, "显示帮助并退出")
|
||||
|
||||
args: argparse.ParsedArgs* = parser.parse_args(sys._argc, sys._argv)
|
||||
if args is None:
|
||||
stdio.printf("参数解析失败\n")
|
||||
return 1
|
||||
if args.get_bool("help"):
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
mode: str = args.get_str("mode")
|
||||
data_dir: str = args.get_str("data-dir")
|
||||
epochs: int = args.get_int("epochs")
|
||||
batch: int = args.get_int("batch")
|
||||
hidden: int = args.get_int("hidden")
|
||||
limit: int = args.get_int("limit")
|
||||
show: int = args.get_int("show")
|
||||
seed: int = args.get_int("seed")
|
||||
save_path: str = args.get_str("save")
|
||||
load_path: str = args.get_str("load")
|
||||
arch_str: str = args.get_str("arch")
|
||||
c1_arg: int = args.get_int("c1")
|
||||
c2_arg: int = args.get_int("c2")
|
||||
lr_str: str = args.get_str("lr")
|
||||
download: int = 0
|
||||
if args.get_bool("download"):
|
||||
download = 1
|
||||
no_aug: int = 0
|
||||
if args.get_bool("no-aug"):
|
||||
no_aug = 1
|
||||
|
||||
# --file 本地 .gz(可 1~2 次): 按文件名含 -labels-/-images- 归类, 无名匹配按先后补空位
|
||||
loc_im: str = None
|
||||
loc_lb: str = None
|
||||
i_arg: int = 1
|
||||
while i_arg + 1 < sys._argc:
|
||||
if string.strcmp(sys._argv[i_arg], "--file") == 0:
|
||||
fv: str = sys._argv[i_arg + 1]
|
||||
is_lb: int = 0
|
||||
if string.strstr(fv, "-labels-") is not None:
|
||||
is_lb = 1
|
||||
is_im: int = 0
|
||||
if string.strstr(fv, "-images-") is not None:
|
||||
is_im = 1
|
||||
if is_lb != 0 and is_im == 0 and loc_lb is None:
|
||||
loc_lb = fv
|
||||
elif is_im != 0 and is_lb == 0 and loc_im is None:
|
||||
loc_im = fv
|
||||
elif loc_im is None:
|
||||
loc_im = fv
|
||||
elif loc_lb is None:
|
||||
loc_lb = fv
|
||||
i_arg += 2
|
||||
else:
|
||||
i_arg += 1
|
||||
file_set: int = 0
|
||||
if loc_im is not None or loc_lb is not None:
|
||||
file_set = 1
|
||||
|
||||
lr: double = double(0.01)
|
||||
if lr_str is not None and string.strlen(lr_str) > 0:
|
||||
lr = MnistData.atof(lr_str, lr)
|
||||
|
||||
MnistNet.rand_seed(uint64(seed))
|
||||
torch.manual_seed(int64(seed))
|
||||
|
||||
h: sizet = sizet(hidden)
|
||||
# 架构与数据增强: 与 GUI 口径一致(通道数只在 CNN 下有意义)
|
||||
arch: int = MnistNet.ARCH_MLP
|
||||
if string.strcmp(arch_str, "cnn") == 0 or string.strcmp(arch_str, "CNN") == 0:
|
||||
arch = MnistNet.ARCH_CNN
|
||||
c1: sizet = 0
|
||||
c2: sizet = 0
|
||||
if arch == MnistNet.ARCH_CNN:
|
||||
c1 = sizet(c1_arg)
|
||||
c2 = sizet(c2_arg)
|
||||
augment: int = 1
|
||||
if no_aug != 0:
|
||||
augment = 0
|
||||
stdio.printf("ViperMNIST mode=%s data=%s limit=%d hidden=%d epochs=%d batch=%d lr=%.3f arch=%s\n",
|
||||
mode, data_dir, limit, hidden, epochs, batch, lr, arch_str)
|
||||
|
||||
p_img: str = stdlib.malloc(1024)
|
||||
p_lbl: str = stdlib.malloc(1024)
|
||||
|
||||
if string.strcmp(mode, "train") == 0:
|
||||
net: MnistNet.Net* = MnistNet.Net(pool, h, sizet(arch), c1, c2)
|
||||
if MnistNet.net_ok(net) == 0:
|
||||
stdio.printf("FAIL: 网络分配失败\n")
|
||||
return 1
|
||||
MnistNet.init_net_weights(net)
|
||||
MnistData.idx_paths(data_dir, 0, p_img, p_lbl)
|
||||
if download != 0 or file_set != 0:
|
||||
if MnistData.ensure_mnist(data_dir, 0, download, loc_im, loc_lb) != 0:
|
||||
stdio.printf("数据就绪失败, 请检查 --file 路径 / 网络 / data-dir\n")
|
||||
return 1
|
||||
cap: sizet = sizet(60000)
|
||||
if limit > 0 and limit < 60000:
|
||||
cap = sizet(limit)
|
||||
xbuf: double* = stdlib.malloc(cap * MnistData.N_IN * 8)
|
||||
ybuf: int* = stdlib.malloc(cap * 4)
|
||||
if xbuf is None or ybuf is None:
|
||||
stdio.printf("FAIL: 训练数据缓冲分配失败\n")
|
||||
return 1
|
||||
n_im: sizet = MnistData.load_mnist_images(p_img, xbuf, cap)
|
||||
n_lb: sizet = MnistData.load_mnist_labels(p_lbl, ybuf, cap)
|
||||
n_tr: sizet = n_im
|
||||
if n_lb < n_tr:
|
||||
n_tr = n_lb
|
||||
stdio.printf("加载训练样本=%zu 张 (%s)\n", n_tr, MnistNet.arch_name(arch))
|
||||
if n_tr == 0:
|
||||
stdio.printf("未找到可用数据。如 data 目录文件残缺/损坏, 请加 --download 重新拉取或用 --file 指定本地 .gz\n")
|
||||
return 1
|
||||
MnistNet.prog_init()
|
||||
MnistNet.train_run(pool, net, xbuf, ybuf, n_tr,
|
||||
sizet(batch), sizet(epochs), lr, save_path, augment)
|
||||
stdlib.free(xbuf)
|
||||
stdlib.free(ybuf)
|
||||
elif string.strcmp(mode, "use") == 0:
|
||||
# 架构/hidden/通道以模型文件头为准(命令行给的对推理无意义), 先读头再建网
|
||||
minfo: int* = stdlib.malloc(20)
|
||||
if minfo is None:
|
||||
stdio.printf("FAIL: 模型头缓冲分配失败\n")
|
||||
return 1
|
||||
if MnistNet.peek_model(load_path, minfo) == 0:
|
||||
stdlib.free(minfo)
|
||||
stdio.printf("加载模型失败, 请先训练(--mode train): %s\n", load_path)
|
||||
return 1
|
||||
f_arch: int = minfo[0]
|
||||
f_hidden: int = minfo[1]
|
||||
f_c1: int = minfo[2]
|
||||
f_c2: int = minfo[3]
|
||||
stdlib.free(minfo)
|
||||
uh: sizet = sizet(f_hidden)
|
||||
uc1: sizet = 0
|
||||
uc2: sizet = 0
|
||||
if f_arch == MnistNet.ARCH_CNN:
|
||||
uc1 = sizet(f_c1)
|
||||
uc2 = sizet(f_c2)
|
||||
net2: MnistNet.Net* = MnistNet.Net(pool, uh, sizet(f_arch),
|
||||
uc1, uc2)
|
||||
if MnistNet.net_ok(net2) == 0:
|
||||
stdio.printf("FAIL: 网络分配失败\n")
|
||||
return 1
|
||||
MnistData.idx_paths(data_dir, 1, p_img, p_lbl)
|
||||
if download != 0 or file_set != 0:
|
||||
if MnistData.ensure_mnist(data_dir, 1, download, loc_im, loc_lb) != 0:
|
||||
stdio.printf("数据就绪失败, 请检查 --file 路径 / 网络 / data-dir\n")
|
||||
return 1
|
||||
cap2: sizet = sizet(10000)
|
||||
if limit > 0 and limit < 10000:
|
||||
cap2 = sizet(limit)
|
||||
xbuf2: double* = stdlib.malloc(cap2 * MnistData.N_IN * 8)
|
||||
ybuf2: int* = stdlib.malloc(cap2 * 4)
|
||||
if xbuf2 is None or ybuf2 is None:
|
||||
stdio.printf("FAIL: 测试数据缓冲分配失败\n")
|
||||
return 1
|
||||
n_te: sizet = MnistData.load_mnist_images(p_img, xbuf2, cap2)
|
||||
n_tl: sizet = MnistData.load_mnist_labels(p_lbl, ybuf2, cap2)
|
||||
if n_tl < n_te:
|
||||
n_te = n_tl
|
||||
stdio.printf("加载测试样本=%zu 张\n", n_te)
|
||||
if n_te == 0:
|
||||
return 1
|
||||
if MnistNet.load_model(net2, load_path) != 0:
|
||||
stdio.printf("加载模型失败, 请先训练(--mode train)\n")
|
||||
return 1
|
||||
stdio.printf("加载模型: %s (%s, hidden=%d)\n",
|
||||
load_path, MnistNet.arch_name(f_arch), f_hidden)
|
||||
MnistNet.evaluate_net(pool, net2, xbuf2, ybuf2, n_te,
|
||||
sizet(batch), sizet(show))
|
||||
stdlib.free(xbuf2)
|
||||
stdlib.free(ybuf2)
|
||||
else:
|
||||
stdio.printf("未知模式: %s (可选 train/use)\n", mode)
|
||||
return 1
|
||||
|
||||
stdlib.free(p_img)
|
||||
stdlib.free(p_lbl)
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 入口
|
||||
# ============================================================
|
||||
def main() -> int:
|
||||
# UTF-8 控制台(与 ViperC main 一致)
|
||||
w32cmd.SetConsoleOutputCP(CODE_PAGE)
|
||||
w32cmd.SetConsoleCP(CODE_PAGE)
|
||||
|
||||
# 主内存池: CLI 与 GUI 共用, 先建池再解析 argv(GUI 分支也需要池)
|
||||
arena: bytes = stdlib.malloc(POOL_SIZE)
|
||||
if arena is None:
|
||||
stdio.printf("FAIL: 无法分配主池\n")
|
||||
return 1
|
||||
mb: memhub.MemBuddy* = memhub.MemBuddy(arena, POOL_SIZE, 0)
|
||||
if mb is None:
|
||||
stdio.printf("FAIL: MemBuddy 初始化失败\n")
|
||||
return 1
|
||||
argparse._mbuddy = mb
|
||||
sys._mbuddy = mb
|
||||
requests._mbuddy = mb # requests/https 内部用 _mbuddy 分配响应缓冲
|
||||
sys._init_argv()
|
||||
|
||||
# 无参数 → Qt 面板; 有参数 → CLI
|
||||
if sys._argc <= 1:
|
||||
return AppGui.run(mb)
|
||||
return run_cli(mb)
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "2.0.0",
|
||||
"description": "MNIST 手写数字识别(28x28 灰度, 两层 MLP): 无参数启动 vqt6 Qt 面板(数据/训练/推理 + 3D 网络图 + Qt Charts 曲线), 带参数走原 CLI(train/use); 计算走 includes/torch (仿 PyTorch 迷你库, 线性层内积 vipersimd dot4/relu max4d)",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ViperOpenSSLProject - OpenSSL 3.x FFI Test",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"description": "vqt6 第一个示例 - 创建一个 Qt6 窗口,点击按钮退出",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": ["-filetype=obj", "-relocation-model=pic"]
|
||||
|
||||
@@ -676,7 +676,7 @@ def main() -> int:
|
||||
leaf.setChecked(1)
|
||||
leaf.setSize(1200)
|
||||
g1.addChild(leaf)
|
||||
leaf = vqt6.QTreeWidgetItem("网络与调试工具(QtNetwork + TPV)[220 MB]")
|
||||
leaf = vqt6.QTreeWidgetItem("网络与调试工具(QtNetwork + VPC)[220 MB]")
|
||||
leaf.setCheckable(1)
|
||||
leaf.setChecked(0)
|
||||
leaf.setSize(220)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ViperQtProject - Qt6 GUI Test",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ViperRequestsTest - HTTP(S) Client Test",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -108,7 +108,7 @@ out[i] = a[i] * b[i] + c[i] # 元素独立, 单趟融合乘加; 每元素
|
||||
|
||||
| 文件 | 说明 |
|
||||
| --- | --- |
|
||||
| `project.vpj` | Viper 工程配置(产物 `.tpv_build/output/app.exe`) |
|
||||
| `project.vpj` | Viper 工程配置(产物 `__vpcbuild__/output/app.exe`) |
|
||||
| `App/main.vp` | Viper 实现: 8 个内核(FMA/ADD/MUL/SQRT × SIMD/原生) + 线程绑定 + QPC/周期双计时 + 进程内多 run; 自解析命令行 `[runs] [len] [iters] [mode]`(GetCommandLineA, 不依赖 sys/memhub) |
|
||||
| `bench_c.c` | 手写 C: 裸数组 + **全指针 `__restrict`**(fill/内核/校验和/local 指针都加) + noinline, `-O2 -mavx2 -mfma -ffp-contract=fast` 自动向量化单趟 FMA |
|
||||
| `bench_cpp.cpp` | 手写 C++: `std::vector` + `__restrict`, 同口径(`-O2 -mavx2 -mfma -ffp-contract=fast`) |
|
||||
@@ -131,7 +131,7 @@ out[i] = a[i] * b[i] + c[i] # 元素独立, 单趟融合乘加; 每元素
|
||||
### 5.1 构建 Viper 部分(一次性)
|
||||
|
||||
在本工程目录(`ViperSpeedBenchmark/`)用你惯用的 tpv 方式构建 `project.vpj`,
|
||||
产物应为 `.tpv_build/output/app.exe`。若未构建, `run_bench.py` 会提示并**跳过
|
||||
产物应为 `__vpcbuild__/output/app.exe`。若未构建, `run_bench.py` 会提示并**跳过
|
||||
全部 Viper 内核变体** 继续其它实现。
|
||||
|
||||
> **改了 `App/main.vp` 或 `includes/vipersimd.vp` 后必须重建**, 否则任一 Viper
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "同内核多实现测速基准: out[i]=a[i]*b[i]+c[i] (len=16384, iters=65536, 每次run≈2.147e9 flops; 工作集512KiB 常驻私有L2)。Viper 侧第4个命令行参数 mode 可切 8 个内核: FMA/ADD/MUL/SQRT 各一对 SIMD(simd_*_array) 与 原生(scalar_*_array), 用于 SIMD 加速比对照; mode=0 的 FMA SIMD 与 C/C++/Python 对齐",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -188,7 +188,7 @@ NATIVE_FLAGS = ["-O2", "-mavx2", "-mfma", "-ffp-contract=fast"]
|
||||
|
||||
|
||||
# Viper 内核实现名 -> (传给 app.exe 的第 4 个参数 mode, 显示名)。
|
||||
# 全部共用同一个 .tpv_build/output/app.exe, 只差这一个 mode 参数 —— 除"跑哪份内核"
|
||||
# 全部共用同一个 __vpcbuild__/output/app.exe, 只差这一个 mode 参数 —— 除"跑哪份内核"
|
||||
# 外没有任何差异, 这是 SIMD vs 原生 对照可信的前提(见 App/main.vp 的 KERNEL_* 常量)。
|
||||
# 命名规则: `<op>` = SIMD, `scalar_<op>` = 同操作的标量(原生)对照。
|
||||
VIPER_KERNELS = [
|
||||
@@ -257,7 +257,7 @@ def iters_for(size):
|
||||
|
||||
|
||||
def viper_exe(root):
|
||||
return root / ".tpv_build" / "output" / "app.exe"
|
||||
return root / "__vpcbuild__" / "output" / "app.exe"
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
@@ -431,7 +431,7 @@ def build_arg_parser():
|
||||
p.add_argument("--py-mode", default=None, choices=["auto", "numpy", "pure"],
|
||||
help="覆盖 bench_py.py 的运行模式(auto/numpy/pure)")
|
||||
p.add_argument("--viper-exe", default=None,
|
||||
help="覆盖 Viper 产物路径(默认 .tpv_build/output/app.exe)")
|
||||
help="覆盖 Viper 产物路径(默认 __vpcbuild__/output/app.exe)")
|
||||
p.add_argument("--flops-check", action="store_true",
|
||||
help="核对 RESULT 行的 flops 字段是否与 CFG 总运算量一致, "
|
||||
"并报告 flops≈2e9(时长≈1s)的行占比及降规模提示")
|
||||
@@ -536,7 +536,7 @@ def main(argv=None):
|
||||
+ ", ".join(f"{n}(mode={VIPER_MODE[n]})" for n in viper_wanted))
|
||||
else:
|
||||
emit(f"[viper] 未找到 Viper 产物: {vpy_exe}")
|
||||
emit(" 请先在本工程目录用 tpv 构建 project.vpj(生成 .tpv_build/output/app.exe),")
|
||||
emit(" 请先在本工程目录用 tpv 构建 project.vpj(生成 __vpcbuild__/output/app.exe),")
|
||||
emit(" 本次将跳过全部 Viper 内核变体。")
|
||||
|
||||
# ---- 逐尺寸 × 逐实现跑测(每组=一次进程启动) ----
|
||||
@@ -804,7 +804,7 @@ def main(argv=None):
|
||||
f" | [{lab_other}] {other_us:>11.3f} us {gfl(other_us):>8.3f} GFLOPS"
|
||||
f" | {aff_txt} | {'PASS' if st['passed'] else 'FAIL'}")
|
||||
if not any(im == "viper" for (_, im) in results):
|
||||
emit(" (viper 已跳过: 请先构建 .tpv_build/output/app.exe)")
|
||||
emit(" (viper 已跳过: 请先构建 __vpcbuild__/output/app.exe)")
|
||||
for impl, note in compile_notes:
|
||||
emit(f" ({impl} 已跳过: 编译失败, 见上方)")
|
||||
emit("=" * 78)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "Standard Template Project",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ViperZipfileProject - zipfile 模块测试",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./App",
|
||||
"build_dir": "./.tpv_build",
|
||||
"build_dir": "./__vpcbuild__",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": [
|
||||
|
||||
Reference in new issue
Block a user