{"repo": "chijichan/37ac", "files": {"src/cli/.env.example": {"58fbd2ead1f95fb175a6d6332dc4320593ab32f9": {"t": 1786068435.0621839, "n": 1737, "h": "2ed1281bc724c4ab", "c": "# ============================================\n# 37AC CLI 客户端环境变量配置模板\n# ============================================\n# 复制此文件为 .env 并填入你的真实配置值\n# cp .env.example .env\n\n# --- 节点服务配置 ---\nTCP_HOST=127.0.0.1\nTCP_PORT=13137\nLOCAL_PORT=\nNODE_ID=1\nTOKEN=your-node-token\n\n# --- 心跳配置 ---\nHEARTBEAT_INTERVAL_SEC=15\nHEARTBEAT_RESPONSE_TIMEOUT_SEC=30\nHEARTBEAT_MISS_LIMIT=3\nRECONNECT_DELAY_SEC=10\n\n# --- 任务配置 ---\nMAX_TASKS=5\n\n# --- 第三方大模型识别配置 ---\n# 启用后节点会优先使用多模态 API 代替本地 ResNet 模型\nLLM_RECOGNITION_ENABLED=False\nLLM_API_KEY=your-deepseek-api-key\nLLM_API_URL=https://api.deepseek.com/v1/chat/completions\n# API 类型: chat-completions(OpenAI 兼容格式,默认)/ ollama(Ollama 原生 /api/chat)\nLLM_API_TYPE=chat-completions\nLLM_MODEL_NAME=deepseek-v4.1-pro\nLLM_TIMEOUT_SEC=30\nLLM_PROMPT_TEMPLATE=\n\n# --- YOLO 检测配置 ---\n# YOLO(快速定位)+ ResNet(角色分类)\nYOLO_ENABLED=True\n# 模型文件路径(默认指向项目根目录的 yolov8n.pt)\nYOLO_MODEL_PATH=\n# 检测置信度阈值(低于此值的目标被忽略)\nYOLO_CONFIDENCE=0.25\n\n# --- 训练配置 ---\nDATASET_DIR=W:/Img\nNUM_EPOCHS=50\nBATCH_SIZE=16\nIMAGE_SIZE=224\nLEARNING_RATE=1e-4\n# 分阶段微调(基于 ImageNet 预训练权重)\nPHASE1_EPOCHS=10 # 阶段1:冻结 backbone,仅训练 FC+CBAM\nPHASE1_LR=1e-3 # 阶段1:高学习率快速拟合头部\nPHASE2_LR=1e-4 # 阶段2:解冻全部,余弦退火全局精调\nPHASE2_MIN_LR=1e-6 # 阶段2:最小学习率\n# 正则化与数据增强\nWEIGHT_DECAY=1e-4 # 权重衰减(L2正则化)\nLABEL_SMOOTHING=0.1 # 标签平滑(缓解过拟合)\nGRAD_CLIP_NORM=1.0 # 梯度裁剪最大范数\n# 早停(连续 X 轮无提升即停止,0=禁用)\nEARLY_STOP_PATIENCE=10\n# 验证集比例(0=不使用验证集)\nVAL_SPLIT_RATIO=0.2\n# DataLoader 工作进程数\nNUM_WORKERS=2\nMODEL_FILENAME=37ac-v0.0.1.pth\nYOLO_ENABLED=True\nYOLO_CONFIDENCE=0.25\nMAX_IMAGES_PER_ROLE=100\n\n# --- 设备配置 ---\n# 自动选择设备(GPU/CPU)\nAUTO_DEVICE=True\n# 强制指定设备(cpu/cuda),AUTO_DEVICE=False 时生效\nDEVICE=cpu\n# 使用 DirectML (AMD GPU) — 适用于 RX 580 等 AMD 显卡\nUSE_DIRECTML=False\n\n# --- 调试模式 ---\nTSAC_DEBUG=False\n"}}, "src/cli/config/base.py": {"58fbd2ead1f95fb175a6d6332dc4320593ab32f9": {"t": 1786068437.287727, "n": 6993, "h": "15631b83e649cda5", "c": "# config/base.py\n\"\"\"基础配置 - 训练、节点服务、路径等(从环境变量读取)\"\"\"\n\nimport json\nimport os\nfrom pathlib import Path\n\nfrom dotenv import load_dotenv\n\n# 加载 .env 文件\nload_dotenv(Path(__file__).resolve().parent.parent / \".env\")\n\nROOT_PATH = Path(__file__).resolve().parent.parent\n\n# ==================== 调试模式 ====================\nTSAC_DEBUG = os.getenv(\"TSAC_DEBUG\", \"False\").lower() == \"true\"\n\n# ==================== 设备配置 ====================\nAUTO_DEVICE = os.getenv(\"AUTO_DEVICE\", \"True\").lower() == \"true\"\nUSE_DIRECTML = os.getenv(\"USE_DIRECTML\", \"False\").lower() == \"true\"\nDEVICE = None\n\n# ==================== 训练相关配置 ====================\nDATASET_DIR = Path(os.getenv(\"DATASET_DIR\", \"W:/Img\"))\n\nMODEL_DIR = ROOT_PATH / \"saves\" / \"models\"\nMODEL_PATH = MODEL_DIR / os.getenv(\"MODEL_FILENAME\", \"37ac-v0.0.1.pth\")\nCLASSES_TXT_PATH = MODEL_DIR / \"classes.txt\"\n# 已有模型权重备份目录\nMODEL_BAK_DIR = MODEL_DIR / \"_bak\"\n\n# 是否从已有模型权重继续训练(而非从头 ImageNet 预训练)\nRESUME_MODEL_PATH = os.getenv(\"RESUME_MODEL_PATH\", \"\")\nif RESUME_MODEL_PATH:\n RESUME_MODEL_PATH = Path(RESUME_MODEL_PATH)\nelse:\n RESUME_MODEL_PATH = None # 默认使用 ImageNet 预训练\n\nNUM_EPOCHS = int(os.getenv(\"NUM_EPOCHS\", \"50\") or \"50\")\nBATCH_SIZE = int(os.getenv(\"BATCH_SIZE\", \"16\") or \"16\")\nIMAGE_SIZE = int(os.getenv(\"IMAGE_SIZE\", \"224\") or \"224\")\nLEARNING_RATE = float(os.getenv(\"LEARNING_RATE\", \"1e-4\") or \"1e-4\")\n# === 分阶段微调(基于 ImageNet 预训练权重) ===\n# 阶段1: 冻结 backbone,仅训练 FC + CBAM,高学习率快速拟合头部\nPHASE1_EPOCHS = int(os.getenv(\"PHASE1_EPOCHS\", \"10\") or \"10\")\nPHASE1_LR = float(os.getenv(\"PHASE1_LR\", \"1e-3\") or \"1e-3\")\n# 阶段2: 解冻全部,低学习率全局精调\nPHASE2_LR = float(os.getenv(\"PHASE2_LR\", \"1e-4\") or \"1e-4\")\nPHASE2_MIN_LR = float(os.getenv(\"PHASE2_MIN_LR\", \"1e-6\") or \"1e-6\")\n# 单个角色最大训练样本数(YOLO 裁剪保存时生效)\nMAX_IMAGES_PER_ROLE = int(os.getenv(\"MAX_IMAGES_PER_ROLE\", \"100\") or \"100\")\n# 验证集比例(0 表示不使用验证集)\nVAL_SPLIT_RATIO = float(os.getenv(\"VAL_SPLIT_RATIO\", \"0.2\") or \"0.2\")\n# 权重衰减(L2 正则化)\nWEIGHT_DECAY = float(os.getenv(\"WEIGHT_DECAY\", \"1e-4\") or \"1e-4\")\n# 标签平滑\nLABEL_SMOOTHING = float(os.getenv(\"LABEL_SMOOTHING\", \"0.1\") or \"0.1\")\n# 梯度裁剪最大范数\nGRAD_CLIP_NORM = float(os.getenv(\"GRAD_CLIP_NORM\", \"1.0\") or \"1.0\")\n# 早停耐心轮数(0 表示不使用早停)\nEARLY_STOP_PATIENCE = int(os.getenv(\"EARLY_STOP_PATIENCE\", \"10\") or \"10\")\n# DataLoader 工作进程数\nNUM_WORKERS = int(os.getenv(\"NUM_WORKERS\", \"2\") or \"2\")\n\n# ==================== 预测相关配置 ====================\nMODEL_LOAD_PATH = MODEL_PATH\n\n# ==================== 数据集路径配置 ====================\n# 原始数据集目录(由 DATASET_DIR 指定,如 W:/Img)\n# YOLO 裁剪后的数据集目录(自动生成,不覆盖原图)\nCROPPED_DATASET_DIR = ROOT_PATH / \"saves\" / \"dataset\"\n\n# ==================== 节点服务配置 ====================\nTCP_HOST = os.getenv(\"TCP_HOST\", \"127.0.0.1\")\nTCP_PORT = int(os.getenv(\"TCP_PORT\", \"13137\"))\nLOCAL_PORT = os.getenv(\"LOCAL_PORT\")\nif LOCAL_PORT:\n LOCAL_PORT = int(LOCAL_PORT)\nelse:\n LOCAL_PORT = None # 默认随机\nNODE_ID = int(os.getenv(\"NODE_ID\", \"1\"))\nTOKEN = os.getenv(\"TOKEN\") # 节点认证Token\nHEARTBEAT_INTERVAL_SEC = int(os.getenv(\"HEARTBEAT_INTERVAL_SEC\", \"15\"))\nHEARTBEAT_RESPONSE_TIMEOUT_SEC = int(os.getenv(\"HEARTBEAT_RESPONSE_TIMEOUT_SEC\", \"30\"))\nHEARTBEAT_MISS_LIMIT = int(os.getenv(\"HEARTBEAT_MISS_LIMIT\", \"3\"))\nRECONNECT_DELAY_SEC = int(os.getenv(\"RECONNECT_DELAY_SEC\", \"10\"))\nMAX_TASKS = int(os.getenv(\"MAX_TASKS\", \"5\"))\n\n# ==================== 第三方大模型(LLM)识别配置 ====================\n# 使用多模态大模型(如 DeepSeek、GPT-4V 等)进行图片识别\nLLM_RECOGNITION_ENABLED = os.getenv(\"LLM_RECOGNITION_ENABLED\", \"False\").lower() == \"true\"\nLLM_API_KEY = os.getenv(\"LLM_API_KEY\", \"\")\nLLM_API_URL = os.getenv(\"LLM_API_URL\", \"https://api.deepseek.com/v1/chat/completions\")\n# API 类型:\"chat-completions\"(OpenAI 兼容格式,默认)/ \"ollama\"(Ollama 原生格式)\n# 注意:os.getenv 在变量存在但为空时返回空串而非默认值,因此用 or 兜底\n_LLM_API_TYPE_RAW = (os.getenv(\"LLM_API_TYPE\", \"\") or \"chat-completions\").strip().lower()\nif _LLM_API_TYPE_RAW in (\"openai\", \"chat\", \"chat-completions\"):\n LLM_API_TYPE = \"chat-completions\"\nelse:\n LLM_API_TYPE = _LLM_API_TYPE_RAW\nLLM_MODEL_NAME = os.getenv(\"LLM_MODEL_NAME\", \"deepseek-v4.1\")\nLLM_TIMEOUT_SEC = int(os.getenv(\"LLM_TIMEOUT_SEC\", \"30\"))\n# 识别提示词模板(默认提示词;.env 中留空时自动使用默认值)\n_DEFAULT_LLM_PROMPT = (\n '你是一个 ACG 角色识别专家。\\n'\n '输入:一张图片(可能包含 cosplay、手办、插画、截图等)。\\n'\n '目标:尽可能准确地识别其中最主要的 ACG 角色。\\n'\n '\\n'\n '请按以下步骤思考(不要输出思考过程):\\n'\n '1. 提取视觉线索:发色、发型、服装、配饰、武器、特有标志、姿势、场景。\\n'\n '2. 与已知角色库进行匹配(覆盖日漫、国漫、游戏、Vtuber等)。\\n'\n '3. 给出最佳匹配,并评估可信度(0-100,依据:特征匹配数、特征独特性、遮挡情况)。\\n'\n '\\n'\n '输出格式(严格 JSON,无 markdown,无注释):\\n'\n '{\"label\": \"作品名/角色名\", \"confidence\": 95, \"features_used\": [\"发色\", \"服装\"], \"class_probs\": [{\"label\": \"其他可能作品名/角色名\", \"confidence\": 3},{\"label\": \"其他可能作品名/角色名\", \"confidence\": 2}...]}\\n'\n '\\n'\n '规则:\\n'\n '- 若图片包含多个角色,只识别最突出(占画面面积最大或居中的)的那一个。\\n'\n '- 若图片模糊、遮挡严重或缺乏足够特征,允许返回 {\"label\": null, \"confidence\": 0, \"reason\": \"特征不足\"}。\\n'\n '- 绝对不要编造特征,置信度必须基于可见证据。\\n'\n '- 对于 cosplay 照片,优先识别原作角色,而非现实人物。\\n'\n '- 若识别结果为现实人物(非 ACG),需特别标注。\\n'\n)\n# 注意:os.getenv 在变量存在但值为空字符串时返回空串而非默认值,因此用 or 兜底\nLLM_PROMPT_TEMPLATE = os.getenv(\"LLM_PROMPT_TEMPLATE\", \"\") or _DEFAULT_LLM_PROMPT\n\n# ==================== 节点能力配置 ====================\n# 节点支持的识别能力列表,自动根据配置推导\n# \"local\" 表示支持本地 YOLO+ResNet 模型推理(始终可用)\n# \"llm\" 表示支持第三方多模态大模型推理\n_CAPABILITIES = [\"local\"]\nif LLM_RECOGNITION_ENABLED:\n _CAPABILITIES.append(\"llm\")\nCAPABILITIES = json.dumps(_CAPABILITIES, ensure_ascii=False)\n\n# ==================== YOLO 检测配置 ====================\n# YOLO(快速定位)+ ResNet(角色分类)技术架构\nYOLO_ENABLED = os.getenv(\"YOLO_ENABLED\", \"True\").lower() == \"true\"\n# 模型名称:yolov8n.pt(nano,最快)/ yolov8s.pt / yolov8m.pt\n# 默认放在 saves/models/ 目录下\n_DEFAULT_YOLO_MODEL = str((MODEL_DIR / \"yolov8n.pt\").resolve())\nYOLO_MODEL_PATH = os.getenv(\"YOLO_MODEL_PATH\", _DEFAULT_YOLO_MODEL)\n# 检测置信度阈值(低于此值的目标被忽略)\nYOLO_CONFIDENCE = float(os.getenv(\"YOLO_CONFIDENCE\", \"0.25\"))\n\n# ==================== 路径配置 ====================\n# 暂存\nIMAGE_PATH = ROOT_PATH / \"saves\" / \"uploads\"\nIMAGE_PATH.mkdir(parents=True, exist_ok=True)\n\n# 日志\nLOGS_PATH = ROOT_PATH / \"saves\" / \"logs\"\nLOGS_PATH.mkdir(parents=True, exist_ok=True)\n\n# ==================== 设备自动选择(惰性初始化) ====================\n# 首次调用时获取,避免模块导入时加载 torch/torch-directml\n_DEVICE_INITIALIZED = False\n\n\ndef get_device():\n \"\"\"获取计算设备(惰性初始化,首次调用时加载 torch)\"\"\"\n global _DEVICE_INITIALIZED, DEVICE\n if _DEVICE_INITIALIZED:\n return DEVICE\n\n _logger = __import__('logging').getLogger(__name__)\n\n if AUTO_DEVICE is False:\n DEVICE = \"cpu\"\n elif USE_DIRECTML:\n try:\n import torch_directml\n DEVICE = torch_directml.device()\n _logger.info(\"使用 DirectML 设备 (AMD GPU): %s\", DEVICE)\n except ImportError:\n _logger.warning(\"torch-directml 未安装,回退到 CPU\")\n DEVICE = \"cpu\"\n except KeyboardInterrupt:\n raise\n except Exception as e:\n _logger.warning(\"DirectML 初始化失败: %s,回退到 CPU\", e)\n DEVICE = \"cpu\"\n else:\n import torch\n if torch.cuda.is_available():\n DEVICE = torch.device(\"cuda\")\n else:\n DEVICE = \"cpu\"\n\n _DEVICE_INITIALIZED = True\n return DEVICE\n"}}}}