Files
lemdb/tech-architecture/tech-architecture.html
T
2026-09-11 09:02:02 +08:00

778 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- Generated by Trae Work -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>技术选型与第一阶段架构方案 — 材料物性激光参数智能匹配系统</title>
<style>
:root {
/* Page / report tokens — Business Blue */
--page-bg: #FFFFFF;
--page-surface: #F5F8FC;
--page-surface-muted: #EDF2F9;
--page-text: #1B2430;
--page-text-secondary: #3D4756;
--page-text-muted: #66717F;
--page-text-disabled: #9AA3AF;
--page-border: #DCE3EC;
--page-brand: #0969DA;
--page-brand-hover: #0757B5;
--page-brand-active: #05468C;
--page-brand-soft: #EAF2FC;
--page-brand-soft-strong: #D6E6F8;
--page-brand-text: #05468C;
/* Backward-compatible report variables */
--bg: #FFFFFF;
--bg2: #F5F8FC;
--rule: #DCE3EC;
--ink: #1B2430;
--muted: #66717F;
--text-secondary: #3D4756;
--text-disabled: #9AA3AF;
--accent: #0969DA;
--accent-hover: #0757B5;
--accent-active: #05468C;
--accent-soft: #EAF2FC;
--accent2: #8250DF;
/* Chart / data tokens */
--chart-series-1: #0969DA;
--chart-series-2: #8250DF;
--chart-series-3: #06B6D4;
--chart-series-4: #BF3989;
--chart-other: #DCE3EC;
--chart-accent: #8250DF;
--chart-accent-2: #06B6D4;
--chart-positive: #52C41A;
--chart-warning: #FAAD14;
--chart-negative: #FF4D4F;
--chart-grid: rgba(27, 36, 48, 0.12);
--chart-axis: #66717F;
--chart-label: #66717F;
--chart-tooltip-bg: #FFFFFF;
/* Semantic compatibility tokens */
--success: #52C41A;
--info: #0969DA;
--reminder: #FF7A45;
--warning: #FAAD14;
--danger: #FF4D4F;
--positive: #52C41A;
--negative: #FF4D4F;
}
/* === Structural variables === */
:root {
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
--space-7: 48px;
--space-8: 64px;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-pill: 999px;
--font-sans: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', system-ui, sans-serif;
--font-serif: Georgia, 'Times New Roman', serif;
--font-mono: 'SFMono-Regular', Consolas, 'JetBrains Mono', monospace;
--font-metric: var(--font-sans);
--max: 960px;
}
/* === Base styles === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; scroll-behavior: smooth; }
body {
font-family: var(--font-sans);
color: var(--ink);
background: var(--bg);
font-size: 14px;
line-height: 1.7;
padding: 2rem 0;
}
article.page { width: 100%; }
.page__content,
.report-intro__surface {
width: calc(100% - 2rem);
max-width: var(--max);
margin: 0 auto;
}
/* === Intro === */
.report-intro { margin-bottom: var(--space-7); }
.report-intro__surface {
position: relative;
isolation: isolate;
overflow: hidden;
color: var(--page-brand-text, var(--ink));
background: var(--page-brand-soft, var(--page-surface-muted, var(--bg2)));
border-radius: var(--radius-lg);
padding: var(--space-7);
}
.report-intro__content { position: relative; z-index: 1; max-width: 760px; }
.report-intro:not(.report-intro--integrated):not(.report-intro--stage):not(.report-intro--with-media):not(.report-intro--plain) .report-intro__surface::before,
.report-intro:not(.report-intro--integrated):not(.report-intro--stage):not(.report-intro--with-media):not(.report-intro--plain) .report-intro__surface::after {
content: "";
position: absolute;
z-index: 0;
pointer-events: none;
}
.report-intro:not(.report-intro--integrated):not(.report-intro--stage):not(.report-intro--with-media):not(.report-intro--plain) .report-intro__surface::before {
width: 180px; height: 180px; top: -72px; right: -48px;
border-radius: 50%;
background: var(--page-brand-soft-strong, var(--page-surface-muted, var(--bg2)));
opacity: 0.72;
}
.report-intro:not(.report-intro--integrated):not(.report-intro--stage):not(.report-intro--with-media):not(.report-intro--plain) .report-intro__surface::after {
width: 136px; height: 96px; right: 96px; bottom: -36px;
border-radius: var(--radius-lg);
background: var(--page-surface, var(--bg));
opacity: 0.58;
transform: rotate(-8deg);
}
.report-intro h1 { line-height: 1.25; overflow-wrap: anywhere; font-size: 28px; }
.report-intro__meta {
margin-top: var(--space-2);
font-size: 13px;
color: var(--page-text-muted);
}
.report-intro__summary {
max-width: 70ch;
margin-top: var(--space-3);
color: var(--page-text-secondary, var(--text-secondary, var(--muted)));
}
@media (max-width: 768px) {
body { padding: 1rem 0; }
.report-intro__surface { padding: var(--space-5); }
.report-intro h1 { font-size: 22px; }
}
@media print, (forced-colors: active) {
.report-intro__surface::before,
.report-intro__surface::after { display: none; }
}
/* === Sections & headings === */
main > section { margin-bottom: var(--space-7); }
h2 {
font-size: 21px;
line-height: 1.4;
margin-bottom: var(--space-4);
padding-bottom: var(--space-2);
border-bottom: 1px solid var(--rule);
}
h3 { font-size: 17px; line-height: 1.5; margin: var(--space-5) 0 var(--space-2); }
p { margin-bottom: var(--space-3); }
ul, ol { margin: 0 0 var(--space-3) 1.25em; }
li { margin-bottom: var(--space-1); }
strong { font-weight: 600; color: var(--ink); }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }
sup a { font-size: 11px; }
.next-step { color: var(--accent); font-weight: 600; }
/* === Tables === */
.table-wrap { width: 100%; overflow-x: auto; }
table { width: 100%; border-collapse: collapse; table-layout: auto; margin-bottom: var(--space-4); }
caption {
caption-side: top; text-align: left;
font-size: 13px; color: var(--muted);
padding-bottom: var(--space-2); font-weight: 500;
}
th, td {
padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--rule);
text-align: left;
vertical-align: top;
overflow-wrap: anywhere;
font-size: 13.5px;
}
thead th {
background: var(--page-surface);
font-weight: 600;
color: var(--text-secondary);
border-bottom: 2px solid var(--page-border);
}
th[scope="row"] { font-weight: 500; color: var(--text-secondary); }
/* === Tags === */
.tag {
display: inline-block;
font-size: 12px;
line-height: 1;
padding: 4px 10px;
border-radius: var(--radius-pill);
border: 1px solid var(--page-border);
background: var(--page-surface);
color: var(--text-secondary);
white-space: nowrap;
}
.tag--risk-high { border-color: var(--danger); color: var(--danger); }
.tag--risk-mid { border-color: var(--warning); color: var(--reminder); }
.tag--risk-low { border-color: var(--success); color: var(--positive); }
.tag--keep { border-color: var(--accent); color: var(--accent); }
/* === Callouts === */
.callout {
background: var(--page-surface);
border: 1px solid var(--page-border);
border-radius: var(--radius-md);
padding: var(--space-3) var(--space-4);
margin: var(--space-4) 0;
font-size: 13.5px;
}
.callout__label {
font-size: 12px;
font-weight: 600;
letter-spacing: 0.05em;
margin-bottom: var(--space-1);
color: var(--page-text-secondary);
}
.callout--risk .callout__label { color: var(--reminder); }
/* === Code / tree === */
pre.tree {
background: var(--page-surface);
border: 1px solid var(--page-border);
border-radius: var(--radius-md);
padding: var(--space-4);
overflow-x: auto;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.65;
margin-bottom: var(--space-4);
}
pre.tree .c { color: var(--muted); }
pre.tree .b { color: var(--page-brand); font-weight: 600; }
code.inline {
background: var(--page-surface-muted);
border-radius: var(--radius-sm);
padding: 1px 6px;
font-family: var(--font-mono);
font-size: 12.5px;
}
/* === Figure (diagram) === */
figure.diagram {
margin: var(--space-4) 0 var(--space-5);
border: 1px solid var(--page-border);
border-radius: var(--radius-lg);
padding: var(--space-4);
background: var(--bg);
}
figure.diagram .mermaid { display: flex; justify-content: center; }
figure.diagram figcaption {
text-align: center;
font-size: 13px;
color: var(--muted);
margin-top: var(--space-3);
}
/* === Footer sources === */
footer { border-top: 1px solid var(--rule); padding-top: var(--space-5); margin-top: var(--space-7); }
footer h2 { border-bottom: none; font-size: 17px; margin-bottom: var(--space-3); }
.sources ol { font-size: 13px; color: var(--text-secondary); margin-left: 1.25em; }
.sources li { margin-bottom: var(--space-2); overflow-wrap: anywhere; }
@media print {
body { padding: 0; }
.report-intro__surface { border: 1px solid var(--page-border); }
main > section { break-inside: auto; }
figure.diagram, table, .callout { break-inside: avoid; }
}
</style>
</head>
<body>
<article class="page">
<header class="report-intro">
<div class="report-intro__surface">
<div class="report-intro__content">
<h1>技术选型与第一阶段架构方案</h1>
<p class="report-intro__meta">基于材料物性参数的激光加工参数智能匹配系统 · SIT 创新训练项目 · 2026-09 · v1.0</p>
<p class="report-intro__summary">
经建模方法、实验设计、工程化部署三路并行调研验证:企划书的技术路线整体成立,无需推翻;本次共形成
<strong>9 项选型决策</strong>,其中新增 TabPFN 第四模型候选与 Conformal Prediction 统一不确定性层两项增强,
明确主动学习框架采用 Ax/BoTorch(避开已停止维护的方案),GPR 的 ONNX 导出列为受控风险项。
第一阶段(当前)搭建范围为<strong>数据层与工程底座</strong>,按 8 周里程碑推进。
</p>
</div>
</div>
</header>
<div class="page__content">
<main>
<section id="summary">
<h2>1. 执行摘要:决策一览</h2>
<p>下表汇总本次技术选型的全部核心决策。<span class="next-step">加粗项为相对企划书的变更或增强</span>,其余为调研确认沿用。</p>
<div class="table-wrap">
<table>
<caption>表 1 · 技术选型决策总表</caption>
<thead>
<tr>
<th scope="col" style="width:18%">决策项</th>
<th scope="col" style="width:44%">调研后决策</th>
<th scope="col" style="width:38%">关键理由</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">建模主力</th>
<td><span class="tag tag--keep">沿用</span> GPR + XGBoost + 随机森林三模型对比,GPR 为主推</td>
<td>小样本(300–500 组)下 GPR 天然输出均值与方差,不确定性估计最直接<sup><a href="#cite-2">[2]</a></sup></td>
</tr>
<tr>
<th scope="row">新增候选</th>
<td><strong>增加 TabPFN 作为第四候选</strong></td>
<td>预训练表格 Transformer,免调参,2025 年小样本基准表现突出<sup><a href="#cite-1">[1]</a></sup><sup><a href="#cite-3">[3]</a></sup></td>
</tr>
<tr>
<th scope="row">不确定性量化</th>
<td><strong>增加 Conformal Prediction 统一包装层</strong>(MAPIE 实现),与 GPR 原生区间并行输出</td>
<td>模型无关的覆盖保证,使四种模型置信区间可公平对比<sup><a href="#cite-4">[4]</a></sup></td>
</tr>
<tr>
<th scope="row">多输出策略</th>
<td>MultiOutputRegressor 独立建模三个输出(功率/速率/频率)</td>
<td>三输出相关性弱,链式回归无益;多任务学习留待 2027 扩展<sup><a href="#cite-6">[6]</a></sup></td>
</tr>
<tr>
<th scope="row">主动学习框架</th>
<td><strong>明确采用 Ax/BoTorch</strong>(qEI / qNEI 批量采集);避开 scikit-optimize 与 modAL</td>
<td>Ax 活跃维护(2026.06 发布 1.3.0),原生批量采集匹配物理实验批次模式<sup><a href="#cite-7">[7]</a></sup>skopt 已于 2024.02 归档<sup><a href="#cite-8">[8]</a></sup></td>
</tr>
<tr>
<th scope="row">超参调优</th>
<td>OptunaTPE 采样器)</td>
<td>成熟活跃,TPE 适合中小搜索空间;与 Ax 职责分离、不重复造轮子</td>
</tr>
<tr>
<th scope="row">模型导出</th>
<td><span class="tag tag--keep">沿用</span> ONNX + joblib 双格式;<strong>GPR 导出后强制做 float64 精度对比</strong></td>
<td>sklearn-onnx 官方支持 GPR,但需 DoubleTensorType,存在精度差异风险<sup><a href="#cite-11">[11]</a></sup><sup><a href="#cite-12">[12]</a></sup></td>
</tr>
<tr>
<th scope="row">数据管理</th>
<td><span class="tag tag--keep">沿用</span> SQLite + CSV;不引入 DVC / MLflow</td>
<td>500 组规模、5 人团队下为零运维方案;版本追踪交给 Git</td>
</tr>
<tr>
<th scope="row">工程规范</th>
<td><strong>明确工程骨架标准</strong>uv + src layout + pydantic-settings + pytest + ruff</td>
<td>20252026 Python 社区共识工具链,学生团队可长期维护</td>
</tr>
</tbody>
</table>
</div>
<div class="callout">
<p class="callout__label">下一步行动</p>
<p>确认本方案后,即可按第 7 节的工程结构初始化代码仓库,并启动第 8 节的 W1–W2 里程碑:工程骨架、数据模型(pydantic)、SQLite 库表三项先行。</p>
</div>
</section>
<section id="background">
<h2>2. 调研背景与方法</h2>
<p>企划书确定的技术路线为「数据采集 → 模型训练 → 封装交付」三阶段闭环:阶段一(2026.05–12)完成 5 类材料、300 组以上的物性-参数-效果映射数据;阶段二(2027)完成 GPR / XGBoost / 随机森林三模型对比与留一材料交叉验证,并扩展深度学习方法;阶段三(2028)完成 ONNX + FastAPI 推理服务封装与厂商试用。项目当前处于阶段一执行期(2026.09)。</p>
<p>企划书中已给出的技术方案属于初期调研,允许中途调整。为在正式开发前锁定第一批技术决策,本次围绕三个方向展开并行调研:</p>
<ul>
<li><strong>建模方法</strong>:小样本表格回归的模型基准、不确定性量化、跨材料泛化与多输出策略;</li>
<li><strong>实验设计与主动学习</strong>:支持物理实验批次采集的框架现状与维护状态;</li>
<li><strong>工程化部署</strong>:ONNX 导出可行性(特别是 GPR)、API 服务、数据管理与工程规范。</li>
</ul>
<p>调研重点覆盖 2024–2026 年公开资料,结论以「确认沿用 / 修改 / 新增」三类标注,与企划书方案逐项对照。</p>
</section>
<section id="modeling">
<h2>3. 建模层选型</h2>
<h3>3.1 模型池:保留三基线,新增 TabPFN</h3>
<p>企划书提出的 GPRRBF 核 + WhiteKernel)、XGBoost、随机森林三种模型组合在 2026 年仍是小样本表格回归的合理基线。GPR 在小样本场景下可同时输出预测均值与方差,是置信区间需求下最直接的方案<sup><a href="#cite-2">[2]</a></sup>;XGBoost 与随机森林提供精度与稳健性参照,且随机森林可输出特征重要性,辅助验证物性特征筛选<sup><a href="#cite-3">[3]</a></sup></p>
<p><strong>本次新增 TabPFN 作为第四候选。</strong>TabPFN 是在合成表格数据上预训练的 Transformer,无需调参即可在中小样本表格任务上取得强结果,且推理速度快;2025 年基准评测中表现突出<sup><a href="#cite-1">[1]</a></sup>。材料信息学方向的工作(如 ICL-FM 将 TabPFN 与材料嵌入结合)也验证了 in-context learning 方法在小样本材料属性预测中的潜力<sup><a href="#cite-5">[5]</a></sup>——这与本项目「留一材料」的跨材料泛化目标天然契合。TabPFN 仅作对比候选,不作为主线依赖(见第 9 节风险表)。</p>
<p>LightGBM 在小样本场景下相对不利,不纳入首轮对比<sup><a href="#cite-3">[3]</a></sup>CatBoost 留作 2027 年数据量扩展后的备选。</p>
<h3>3.2 不确定性量化:GPR 原生区间 + Conformal 统一包装</h3>
<p>预测置信区间是本系统对外的核心承诺(企划书要求覆盖率 ≥ 90%)。决策采用双轨:</p>
<ul>
<li><strong>主线</strong>:GPR 后验分布直接输出均值 ± 95% 区间,无需额外校准;</li>
<li><strong>统一层</strong>:对四种候选模型统一追加 Conformal Prediction(保形预测)包装(MAPIE 的 SplitConformalRegressor),使 GPR、XGBoost、RF、TabPFN 的区间输出在同一覆盖率标准下公平对比<sup><a href="#cite-4">[4]</a></sup></li>
</ul>
<p>Conformal Prediction 在有限校准集下提供覆盖率保证,不依赖模型形式,是当前小样本回归不确定性量化的主流做法<sup><a href="#cite-4">[4]</a></sup>。NGBoost(参数化分布输出)作为备选记录,不纳入首轮实现。</p>
<h3>3.3 多输出策略:独立建模</h3>
<p>系统输出为三维连续值(功率百分比、走光速率、脉冲频率)。三者的物理相关性较弱(分别受能量耦合、扫描机械、脉冲时序主导),调研确认小样本表格场景下<strong>独立建模三个输出(MultiOutputRegressor)是最务实的选择</strong><sup><a href="#cite-6">[6]</a></sup>;链式回归(RegressorChain)仅在输出强相关时有增益。企划书中 2027 年的多任务学习架构规划保留不变,作为数据量突破 500 组后的扩展方向。</p>
<h3>3.4 跨材料泛化:特征工程优先</h3>
<p>留一材料交叉验证(LOMO CV)是本项目区别于常规回归任务的核心评估协议。提升 LOMO 泛化的优先手段是<strong>特征工程</strong>:以物理意义明确的物性参数(反射率、吸收率、熔点/Tg、热导率、密度、粗糙度)构成统一特征空间,保证数值跨材料可比、量纲标准化一致。企划书提出的「大类独立训练 + 子类微调」分层策略保留为数据量不足时的备选方案;TabPFN 的 in-context 特性为跨材料泛化提供了额外一条低成路验证路径<sup><a href="#cite-1">[1]</a></sup></p>
</section>
<section id="active-learning">
<h2>4. 实验设计与主动学习选型</h2>
<h3>4.1 框架现状与决策</h3>
<p>本项目数据采集的特殊性在于:实验是<strong>串行批次</strong>的物理过程(一批做完再决定下一批),而非纯数字模拟,因此框架必须支持批量采集函数(batch acquisition)。调研对比如下:</p>
<div class="table-wrap">
<table>
<caption>表 2 · 主动学习 / 贝叶斯优化框架对比</caption>
<thead>
<tr>
<th scope="col">框架</th>
<th scope="col">维护状态</th>
<th scope="col">批量采集</th>
<th scope="col">结论</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Ax / BoTorch</th>
<td>活跃(Ax 1.3.02026.06<sup><a href="#cite-7">[7]</a></sup></td>
<td>原生 qEI / qNEI / qEHVI<sup><a href="#cite-9">[9]</a></sup></td>
<td><span class="tag tag--keep">主选</span></td>
</tr>
<tr>
<th scope="row">Optuna</th>
<td>活跃</td>
<td>较弱</td>
<td><span class="tag">超参调优专用</span></td>
</tr>
<tr>
<th scope="row">scikit-optimize</th>
<td>2024.02 已归档<sup><a href="#cite-8">[8]</a></sup></td>
<td></td>
<td><span class="tag tag--risk-high">避开</span></td>
</tr>
<tr>
<th scope="row">modAL</th>
<td>基本停滞</td>
<td>有限</td>
<td><span class="tag tag--risk-high">避开</span></td>
</tr>
<tr>
<th scope="row">scikit-activeml</th>
<td>活跃(1.0.02025.12</td>
<td>有限</td>
<td><span class="tag">备选</span></td>
</tr>
<tr>
<th scope="row">Dragonfly / SMAC3</th>
<td>维护中</td>
<td>支持并行 / 弱</td>
<td><span class="tag">不采用</span></td>
</tr>
</tbody>
</table>
</div>
<p>决策:主动学习环节采用 <strong>Ax/BoTorch</strong>。其 GPR 内核与建模层主力模型同源,采集函数直接复用训练好的 GPR 不确定性;qNEI 等批量采集函数支持「一次推荐一批 10–20 组参数」的物理实验节奏<sup><a href="#cite-9">[9]</a></sup>。超参调优独立使用 Optuna(TPE),两者职责清晰互不干扰。材料实验领域的自主优化平台(如 NREL ALchemist<sup><a href="#cite-10">[10]</a></sup>)验证了此类工作流在实验科学中的可行性,可作为架构参考。</p>
<h3>4.2 采集工作流:LHS 冷启动 → 主动学习</h3>
<ul>
<li><strong>初始采样</strong>:使用 <code class="inline">scipy.stats.qmc.LatinHypercube</code> 生成每种材料 30–40 组初始参数组合(拉丁超立方保证低维空间覆盖均匀);</li>
<li><strong>主动学习循环</strong>:初始数据训练 GPR 后,Ax 以 qNEI 推荐下一批 10–20 组实验,模型不确定性高的参数区域优先被探索;</li>
<li><strong>停止条件</strong>:留一材料验证误差收敛或单材料实验预算(80 组)耗尽。</li>
</ul>
<p>该工作流将企划书「主动学习引导的高效实验采集」创新点落实为可执行方案,且与阶段二的建模管线共享同一套 GPR 基础设施,无额外维护成本。</p>
</section>
<section id="engineering">
<h2>5. 工程化栈选型</h2>
<h3>5.1 模型导出:ONNX 保留,GPR 列为受控风险</h3>
<p>sklearn-onnx 官方支持 GaussianProcessRegressor 的导出<sup><a href="#cite-12">[12]</a></sup>,但官方示例明确要求使用 DoubleTensorTypefloat64)而非默认 float32,因为 GPR 的矩阵运算在 float32 下存在精度差异<sup><a href="#cite-11">[11]</a></sup>。XGBoost 与随机森林的 ONNX 导出成熟<sup><a href="#cite-12">[12]</a></sup>。决策:</p>
<ul>
<li>沿用 ONNX + joblib 双格式交付方案;</li>
<li><strong>GPR 导出流程中强制插入精度对比环节</strong>(同一测试集上 sklearn 原生预测 vs ONNX Runtime 预测,误差超过阈值即回退 joblib + FastAPI 直载方案);</li>
<li>该风险不阻塞第一阶段开发,2027 年模型定型时再验证。</li>
</ul>
<h3>5.2 API 服务与数据管理</h3>
<p>FastAPI 方案确认可行:pydantic v2 承担请求/响应校验,模型在 lifespan 钩子中单次加载(避免每请求重复加载),CPU 推理走同步 endpoint 由 FastAPI 线程池处理,满足 ≤100ms 响应要求。数据管理沿用 SQLite + CSV:单文件零运维、5 人团队可直接共享,CSV 作为对外交付格式;DVC 与 MLflow 在当前数据规模(≤500 组)下属过度工程,明确不引入,数据版本追踪由 Git 提交记录承担。</p>
<h3>5.3 设备采集与工程规范</h3>
<p>串口通信采用 pyserial(活跃维护,支持 Python 3.10+<sup><a href="#cite-13">[13]</a></sup>),按 producer-consumer 模式组织:后台读线程持续读取设备状态,主逻辑通过队列消费<sup><a href="#cite-14">[14]</a></sup>。工程规范一次性定标准,避免后期重构:</p>
<ul>
<li><strong>包管理</strong>uv20252026 Python 社区主流,速度快、锁文件确定性);</li>
<li><strong>项目结构</strong>src layout<code class="inline">src/lmpm/</code>,包名取 laser-material-parameter-matching 缩写);</li>
<li><strong>配置</strong>pydantic-settings(类型安全的环境变量与配置文件管理);</li>
<li><strong>测试与质量</strong>pytest + rufflint 与格式化二合一)。</li>
</ul>
</section>
<section id="architecture">
<h2>6. 系统架构设计</h2>
<p>系统按「数据层 → 模型层 → 服务层」三层组织,工程底座贯穿全程。主动学习闭环是架构中的关键反馈路径:模型层的不确定性评估反向驱动数据层的下一批实验设计。图中实线为数据流,蓝色虚线为主动学习反馈。</p>
<figure class="diagram">
<pre class="mermaid">
flowchart TB
IN1(["材料物性测量<br/>光谱仪 · 粗糙度仪 · DSC"])
IN2(["激光加工实验<br/>HZZ-M300U · 355nm"])
subgraph P1["阶段一 · 数据层(2026.0512 · 本次搭建)"]
direction LR
D1["实验设计 DoE<br/>LHS + Ax 主动学习"]
D2["数据采集<br/>pyserial + 显微镜"]
D3["数据存储<br/>SQLite + CSV"]
D1 --> D2 --> D3
end
subgraph P2["阶段二 · 模型层(2027"]
direction LR
M1["模型训练<br/>GPR · XGB · RF · TabPFN"]
M2["评估与 UQ<br/>LOMO CV · Conformal"]
M3["超参调优<br/>Optuna TPE"]
M1 --> M2
M3 -.-> M1
end
subgraph P3["阶段三 · 服务层(2028"]
direction LR
S1["推理引擎<br/>ONNX + joblib"]
S2["REST API<br/>FastAPI · pydantic v2"]
S3["容器交付<br/>Docker"]
S1 --> S2 --> S3
end
OUT(["激光设备控制软件 / 厂商数据产品"])
IN1 --> P1
IN2 --> P1
D3 --> M1
M2 --> S1
S3 --> OUT
M2 -. 主动学习反馈 .-> D1
classDef neutral fill:#F5F8FC,stroke:#DCE3EC,color:#1B2430;
classDef focus fill:#EAF2FC,stroke:#0969DA,color:#05468C,stroke-width:1.5px;
classDef external fill:#FFFFFF,stroke:#66717F,color:#3D4756;
class IN1,IN2,OUT external;
class D1,D2,D3 focus;
class M1,M2,M3,S1,S2,S3 neutral;
style P1 stroke:#0969DA,stroke-width:2px,fill:#FFFFFF,color:#05468C;
style P2 stroke:#DCE3EC,fill:#FFFFFF,color:#3D4756;
style P3 stroke:#DCE3EC,fill:#FFFFFF,color:#3D4756;
linkStyle 11 stroke:#0969DA,stroke-width:1.8px;
</pre>
<figcaption>图 1 · 系统三阶段技术架构(虚线为主动学习反馈闭环;阶段一为当前搭建范围)</figcaption>
</figure>
<h3>6.1 模块职责</h3>
<div class="table-wrap">
<table>
<caption>表 3 · 核心模块职责划分</caption>
<thead>
<tr>
<th scope="col" style="width:22%">模块</th>
<th scope="col" style="width:20%">所属阶段</th>
<th scope="col">职责</th>
</tr>
</thead>
<tbody>
<tr><th scope="row">domain(领域模型)</th><td>阶段一</td><td>pydantic 定义材料物性、实验记录、加工参数、质量评分四类实体,全链路类型校验</td></tr>
<tr><th scope="row">doe(实验设计)</th><td>阶段一</td><td>LHS 初始采样、参数空间定义、主动学习批次推荐(对接 Ax)</td></tr>
<tr><th scope="row">data(数据存取)</th><td>阶段一</td><td>SQLite 读写、CSV 导入导出、数据校验与清洗</td></tr>
<tr><th scope="row">device(设备采集)</th><td>阶段一</td><td>pyserial 串口状态读取、实验参数自动同步记录</td></tr>
<tr><th scope="row">ml(模型管线)</th><td>阶段二</td><td>四模型训练、LOMO 交叉验证、Conformal 包装、超参调优</td></tr>
<tr><th scope="row">api(推理服务)</th><td>阶段三</td><td>FastAPI REST 接口、ONNX Runtime 推理、置信区间输出</td></tr>
</tbody>
</table>
</div>
</section>
<section id="structure">
<h2>7. 项目工程结构</h2>
<p>第一阶段按以下骨架初始化仓库(阶段二、三的目录预留占位,随进度填充):</p>
<pre class="tree">lemdb/
├── pyproject.toml <span class="c"># uv 管理:依赖声明、ruff/pytest 配置</span>
├── README.md
├── .gitignore <span class="c"># data/*.db 等产物不入库</span>
├── src/lmpm/ <span class="b"># 主包:laser material parameter matching</span>
│ ├── config.py <span class="c"># pydantic-settings:路径、设备、参数范围</span>
│ ├── domain/ <span class="b"># 领域数据模型(第一阶段核心)</span>
│ │ ├── material.py <span class="c"># MaterialProperty:反射率/吸收率/熔点/热导率/密度/粗糙度</span>
│ │ ├── experiment.py <span class="c"># ExperimentRecord / ProcessingParams / QualityMetrics</span>
│ │ └── doe.py <span class="c"># ParameterSpace / ExperimentDesign</span>
│ ├── data/
│ │ ├── store.py <span class="c"># SQLite 读写层</span>
│ │ ├── io_csv.py <span class="c"># CSV 导入导出(交付格式)</span>
│ │ └── validate.py <span class="c"># 入库前校验:范围/缺失/重复</span>
│ ├── doe/
│ │ ├── initial.py <span class="c"># LHS 初始采样(scipy QMC</span>
│ │ └── active.py <span class="c"># Ax 主动学习批次推荐(阶段一末启用)</span>
│ ├── device/
│ │ └── serial_recorder.py <span class="c"># pyserial 采集(协议确认后开发)</span>
│ ├── ml/ <span class="b"># 阶段二:train / evaluate / uq</span>
│ └── api/ <span class="b"># 阶段三:main.pyFastAPI</span>
├── scripts/
│ ├── init_db.py <span class="c"># 初始化 SQLite 库表</span>
│ ├── generate_doe.py <span class="c"># 生成参数网格 CSV</span>
│ └── record_experiment.py <span class="c"># 实验记录录入 CLI</span>
├── tests/ <span class="c"># pytestdomain 校验 / store 读写 / doe 生成</span>
├── data/ <span class="c"># SQLite 库文件与 CSVgitignore</span>
└── docs/
└── data_dictionary.md <span class="c"># 数据字典:字段、单位、测量方法、溯源</span></pre>
<h3>7.1 依赖清单(按阶段引入)</h3>
<div class="table-wrap">
<table>
<caption>表 4 · Python 依赖引入计划</caption>
<thead>
<tr>
<th scope="col" style="width:18%">阶段</th>
<th scope="col" style="width:52%">依赖</th>
<th scope="col">用途</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">阶段一</th>
<td><code class="inline">pydantic pydantic-settings pyserial scipy pandas typer</code></td>
<td>数据模型 / 配置 / 串口 / LHS 采样 / 数据处理 / CLI</td>
</tr>
<tr>
<th scope="row">阶段二</th>
<td><code class="inline">scikit-learn xgboost tabpfn optuna ax-platform botorch mapie</code></td>
<td>四模型训练 / 调优 / 主动学习 / Conformal 区间</td>
</tr>
<tr>
<th scope="row">阶段三</th>
<td><code class="inline">fastapi uvicorn skl2onnx onnxruntime</code></td>
<td>REST 服务 / 模型导出与推理</td>
</tr>
<tr>
<th scope="row">全程</th>
<td><code class="inline">pytest ruff</code></td>
<td>测试 / 代码质量</td>
</tr>
</tbody>
</table>
</div>
<p>分阶段引入依赖可保持前期环境轻量(阶段一无需安装 PyTorch 系的 Ax/BoTorch),也避免了当前 Windows/Linux 混合环境下的编译问题。</p>
</section>
<section id="roadmap">
<h2>8. 第一阶段实施路线(8 周)</h2>
<p>对齐企划书「2026.05–12 完成数据采集与首批 300 组数据」的目标,从当前时点起按 8 周里程碑推进:</p>
<div class="table-wrap">
<table>
<caption>表 5 · 第一阶段 8 周里程碑</caption>
<thead>
<tr>
<th scope="col" style="width:14%">周次</th>
<th scope="col" style="width:46%">交付物</th>
<th scope="col">验收标准</th>
</tr>
</thead>
<tbody>
<tr><th scope="row">W1W2</th><td>工程骨架 + 领域模型 + SQLite 库表</td><td><code class="inline">uv sync</code> 可装、pytest 全绿、库表可建可写</td></tr>
<tr><th scope="row">W3</th><td>DoE 生成器(LHS 初始采样)</td><td>生成亚克力 40 组参数网格 CSV,范围符合设备量程</td></tr>
<tr><th scope="row">W4W5</th><td>实验记录工作流(CLI 录入 + 串口采集)</td><td>单条实验记录全字段校验入库,重复/越界被拦截</td></tr>
<tr><th scope="row">W6</th><td>首批数据:亚克力 + 椴木 ≥120 组,数据字典 v1</td><td>完整率 100%EDA 快照可复现</td></tr>
<tr><th scope="row">W7</th><td>基线模型试跑(GPR / XGBoost</td><td>LOMO 交叉验证管线跑通(不考核精度)</td></tr>
<tr><th scope="row">W8</th><td>主动学习接口预留 + 中期检查文档</td><td>Ax 环境可运行 qNEI 批量推荐 demo</td></tr>
</tbody>
</table>
</div>
<div class="callout callout--risk">
<p class="callout__label">进度风险提示</p>
<p>串口自动采集依赖厂商提供 HZZ-M300U 的通信协议文档;若 W4 前未取得,立即切换为「CLI 手工录入」模式兜底,不影响 W6 数据目标。企划书中 2026.06 完成串口开发的计划以此风险开关为条件。</p>
</div>
</section>
<section id="risks">
<h2>9. 风险登记表</h2>
<div class="table-wrap">
<table>
<caption>表 6 · 技术风险与缓解措施</caption>
<thead>
<tr>
<th scope="col" style="width:26%">风险</th>
<th scope="col" style="width:10%">等级</th>
<th scope="col" style="width:28%">影响</th>
<th scope="col">缓解措施</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">GPR 的 ONNX 导出精度差异</th>
<td><span class="tag tag--risk-mid"></span></td>
<td>线上推理结果偏离训练基线</td>
<td>导出后强制 float64 对比验证;备选 joblib + FastAPI 直载<sup><a href="#cite-11">[11]</a></sup></td>
</tr>
<tr>
<th scope="row">HZZ-M300U 串口协议未知</th>
<td><span class="tag tag--risk-mid"></span></td>
<td>自动采集延期,拖累数据目标</td>
<td>W1 起向厂商索取协议;CLI 手工录入兜底</td>
</tr>
<tr>
<th scope="row">小样本 LOMO 精度不达标</th>
<td><span class="tag tag--risk-mid"></span></td>
<td>新材料推荐误差超 8% 目标</td>
<td>特征工程优先;大类分层训练备选;主动学习提高单数据信息量</td>
</tr>
<tr>
<th scope="row">TabPFN 适用边界</th>
<td><span class="tag tag--risk-low"></span></td>
<td>第四候选失效</td>
<td>仅作对比不依赖;主线仍是 GPR / XGBoost</td>
</tr>
<tr>
<th scope="row">5 人团队协作冲突</th>
<td><span class="tag tag--risk-low"></span></td>
<td>代码互踩、数据覆盖</td>
<td>Git 分支规范 + ruff 统一格式 + SQLite 单写入口(store.py</td>
</tr>
</tbody>
</table>
</div>
</section>
</main>
<footer>
<div class="sources">
<h2>参考资料</h2>
<ol>
<li id="cite-1">TabPFN v2 小样本表格基准评测(arXiv, 2025)<a href="https://arxiv.org/html/2505.11325v2" target="_blank" rel="noopener">https://arxiv.org/html/2505.11325v2</a></li>
<li id="cite-2">Gaussian Processes for Small-Sample Regression with UncertaintyBestHub<a href="https://www.besthub.dev/articles/why-gaussian-processes-beat-neural-networks-for-small-sample-regression-with-uncertainty-601d1e4dcfe8" target="_blank" rel="noopener">https://www.besthub.dev/articles/why-gaussian-processes-beat-neural-networks-for-small-sample-regression-with-uncertainty-601d1e4dcfe8</a></li>
<li id="cite-3">表格数据机器学习模型基准综述(arXiv, 2024)<a href="https://arxiv.org/pdf/2408.14817" target="_blank" rel="noopener">https://arxiv.org/pdf/2408.14817</a></li>
<li id="cite-4">Conformal Prediction 与 MAPIE 不确定性量化实践(Python Data Bench<a href="https://pythondatabench.com/article/conformal-prediction-python-mapie-uncertainty" target="_blank" rel="noopener">https://pythondatabench.com/article/conformal-prediction-python-mapie-uncertainty</a></li>
<li id="cite-5">ICL-FM:材料属性预测的 in-context 学习框架(arXiv, 2026<a href="https://arxiv.org/html/2601.00133v1" target="_blank" rel="noopener">https://arxiv.org/html/2601.00133v1</a></li>
<li id="cite-6">scikit-learn 多输出回归实现(MultiOutputRegressor / RegressorChain<a href="https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/multioutput.py" target="_blank" rel="noopener">https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/multioutput.py</a></li>
<li id="cite-7">Ax Platform 更新日志(Meta, 2026.06 发布 1.3.0<a href="https://github.com/facebook/Ax/blob/main/CHANGELOG.md" target="_blank" rel="noopener">https://github.com/facebook/Ax/blob/main/CHANGELOG.md</a></li>
<li id="cite-8">scikit-optimize 归档状态记录(2024.02 起停止维护)<a href="https://github.com/freqtrade/freqtrade/issues/10929" target="_blank" rel="noopener">https://github.com/freqtrade/freqtrade/issues/10929</a></li>
<li id="cite-9">Deep GP 与批量采集函数在实验优化中的应用(arXiv, 2025)<a href="https://arxiv.org/html/2509.14408" target="_blank" rel="noopener">https://arxiv.org/html/2509.14408</a></li>
<li id="cite-10">NREL ALchemist:材料/化学实验主动学习平台<a href="https://github.com/NREL/ALchemist" target="_blank" rel="noopener">https://github.com/NREL/ALchemist</a></li>
<li id="cite-11">sklearn-onnx 官方 GPR 导出示例(DoubleTensorType 精度说明)<a href="https://onnx.ai/sklearn-onnx/auto_examples/plot_gpr.html" target="_blank" rel="noopener">https://onnx.ai/sklearn-onnx/auto_examples/plot_gpr.html</a></li>
<li id="cite-12">sklearn-onnx 支持的模型列表(含 GPR / XGBoost / LightGBM<a href="https://onnx.ai/sklearn-onnx/supported.html" target="_blank" rel="noopener">https://onnx.ai/sklearn-onnx/supported.html</a></li>
<li id="cite-13">pyserial 仓库(活跃维护,Python 3.10+<a href="https://github.com/pyserial/pyserial" target="_blank" rel="noopener">https://github.com/pyserial/pyserial</a></li>
<li id="cite-14">pyserial 官方文档:线程化读取模式<a href="https://www.pyserial.org/docs/performance/threading" target="_blank" rel="noopener">https://www.pyserial.org/docs/performance/threading</a></li>
</ol>
</div>
</footer>
</div>
</article>
<script src="./_shared/js/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'base',
themeVariables: {
primaryColor: '#F5F8FC',
primaryBorderColor: '#DCE3EC',
primaryTextColor: '#1B2430',
lineColor: '#66717F',
fontSize: '14px',
fontFamily: '"PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif',
clusterBkg: '#FFFFFF',
clusterBorder: '#DCE3EC'
},
flowchart: { htmlLabels: true, curve: 'basis' }
});
</script>
</body>
</html>