feat: add superfig paper-figure toolkit
Standalone LaTeX/TikZ skill for non-tensor paper figures: node/edge macros, lint-on-warning build, golden examples, and negative fixtures.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
% Anti-pattern gallery -- four figures that compile cleanly and still teach
|
||||
% the reader something false. Each pair is wrong / right.
|
||||
% ../scripts/build.sh antipatterns.tex
|
||||
\documentclass[border=10pt]{standalone}
|
||||
\usepackage[cjk]{superfig}
|
||||
|
||||
\sfsetrole{a}{sfTeal}
|
||||
\sfsetrole{b}{sfOrange}
|
||||
\sfsetrole{c}{sfCoral}
|
||||
\sfsetrole{d}{sfViolet}
|
||||
|
||||
\newcommand{\bad}[1]{{\color{sfCoral}$\times$}\;#1}
|
||||
\newcommand{\good}[1]{{\color{sfTeal}$\checkmark$}\;#1}
|
||||
|
||||
\begin{document}
|
||||
\begin{tikzpicture}
|
||||
|
||||
% (1) A decorative arrow vs a data-flow edge.
|
||||
\sfnode[role=a, at={(0,0)}]{A1}{编码器}{16mm}{10mm}
|
||||
\sfnode[role=b, at={(32mm,0)}]{A2}{指标卡}{16mm}{10mm}
|
||||
\sfarrow{A1}{A2}
|
||||
\sfnode[role=a, at={(64mm,0)}]{A3}{编码器}{16mm}{10mm}
|
||||
\sfnode[role=b, at={(96mm,0)}]{A4}{解码器}{16mm}{10mm}
|
||||
\sfarrowlabel{A3.east}{A4.west}{隐状态}
|
||||
|
||||
\node[sf stage, anchor=south west] at ($(A1.north west)+(0,6mm)$)
|
||||
{(1) 装饰箭头};
|
||||
\node[sf shape, anchor=north] (A1c) at ($(A1.south)!0.5!(A2.south)+(0,-3mm)$)
|
||||
{\bad{箭头不承载关系}};
|
||||
\node[sf shape, anchor=north] (A3c) at ($(A3.south)!0.5!(A4.south)+(0,-3mm)$)
|
||||
{\good{隐状态从编码流向解码}};
|
||||
|
||||
% (2) A group that is only a visual border vs a real composite.
|
||||
\sfnode[role=a, at={(0,-32mm)}]{B1}{输入}{14mm}{10mm}
|
||||
\sfnode[role=d, at={(24mm,-32mm)}]{B2}{输出}{14mm}{10mm}
|
||||
\sfgroup[role=a]{Bg}{(B1)(B2)}{}
|
||||
\sfnode[role=b, at={(56mm,-32mm)}]{B3}{注意力}{16mm}{10mm}
|
||||
\sfnode[role=b, at={(82mm,-32mm)}]{B4}{前馈}{14mm}{10mm}
|
||||
\sfgroup[role=b]{Bb}{(B3)(B4)}{}
|
||||
\sfarrow{B3.east}{B4.west}
|
||||
|
||||
\node[sf stage, anchor=south west] at ($(B1.north west)+(0,8mm)$)
|
||||
{(2) 组框只是装饰边};
|
||||
\node[sf shape, anchor=north] (B1c) at ($(B1.south)!0.5!(B2.south)+(0,-7mm)$)
|
||||
{\bad{首尾框在一起,不是一个模块}};
|
||||
\node[sf shape, anchor=north] (B3c) at ($(B3.south)!0.5!(B4.south)+(0,-7mm)$)
|
||||
{\good{主路模块是真实复合对象}};
|
||||
|
||||
% (3) One node doing two jobs vs a split.
|
||||
\sfnode[role=b, at={(0,-68mm)}]{C1}{注意力 + LN + 残差}{38mm}{12mm}
|
||||
\sfnode[role=b, at={(56mm,-68mm)}]{C2}{注意力}{16mm}{10mm}
|
||||
\sfnode[role=c, at={(84mm,-68mm)}]{C3}{残差加}{16mm}{10mm}
|
||||
\sfarrow{C2.east}{C3.west}
|
||||
|
||||
\node[sf stage, anchor=south west] at ($(C1.north west)+(0,6mm)$)
|
||||
{(3) 一个节点做两件事};
|
||||
\node[sf shape, anchor=north] (C1c) at ($(C1.south)+(0,-3mm)$)
|
||||
{\bad{机制被一口吞掉}};
|
||||
\node[sf shape, anchor=north] (C2c) at ($(C2.south)!0.5!(C3.south)+(0,-3mm)$)
|
||||
{\good{一步一个对象}};
|
||||
|
||||
% (4) A new hue for the same object vs one role, two levels.
|
||||
\sfnode[role=a, level=2, at={(0,-100mm)}]{D1}{$x$}{14mm}{10mm}
|
||||
\sfnode[role=c, level=2, at={(24mm,-100mm)}]{D2}{$x$ 归一化}{20mm}{10mm}
|
||||
\sfnode[role=a, level=1, at={(64mm,-100mm)}]{D3}{$x$}{14mm}{10mm}
|
||||
\sfnode[role=a, level=3, at={(90mm,-100mm)}]{D4}{$x$ 归一化}{20mm}{10mm}
|
||||
|
||||
\node[sf stage, anchor=south west] at ($(D1.north west)+(0,6mm)$)
|
||||
{(4) 同一对象换了色相};
|
||||
\node[sf shape, anchor=north] (D1c) at ($(D1.south)!0.5!(D2.south)+(0,-3mm)$)
|
||||
{\bad{换色相像换了对象}};
|
||||
\node[sf shape, anchor=north] (D3c) at ($(D3.south)!0.5!(D4.south)+(0,-3mm)$)
|
||||
{\good{同一角色,深浅分层}};
|
||||
|
||||
\node[inner sep=0pt, fit=(A1)(A4)(D1)(D4)(A1c)(D3c)(Bg)(Bb)] (all) {};
|
||||
\sfmeaningbox{mb}{118mm}{all}
|
||||
{}
|
||||
{四张错图都能干净编译。编译器不检查图讲的事情对不对}
|
||||
{交付前按 \texttt{references/checklist.md} 做一次人眼审图}
|
||||
|
||||
\end{tikzpicture}
|
||||
\end{document}
|
||||
@@ -0,0 +1,48 @@
|
||||
% superfig golden example 2 -- a small branchy architecture, not a tensor
|
||||
% shape diagram. Main path uses the cursor; the residual is a skip edge;
|
||||
% the group is captioned on the shared lane, not as an overlay.
|
||||
% ../scripts/build.sh branch-architecture.tex
|
||||
\documentclass[border=10pt]{standalone}
|
||||
\usepackage[cjk]{superfig}
|
||||
|
||||
\sfsetrole{enc}{sfTeal}
|
||||
\sfsetrole{attn}{sfOrange}
|
||||
\sfsetrole{ffn}{sfViolet}
|
||||
\sfsetrole{out}{sfCoral}
|
||||
|
||||
\begin{document}
|
||||
\begin{tikzpicture}
|
||||
|
||||
\sfstage{S}{双分支结构:主路 + 残差}
|
||||
\sfvgap{7mm}
|
||||
\sfrow{R1}{12mm}
|
||||
\sfnode[role=enc]{in}{输入}{16mm}{10mm}
|
||||
\sfconn{e1}{}
|
||||
\sfnode[role=attn]{attn}{注意力}{18mm}{10mm}
|
||||
\sfconn{e2}{特征}
|
||||
\sfnode[role=ffn]{ffn}{前馈}{18mm}{10mm}
|
||||
\sfgap{\sflinklen}
|
||||
\sfnode[role=enc]{add}{融合}{14mm}{10mm}
|
||||
\sfconn{e4}{}
|
||||
\sfnode[role=out]{out}{输出}{14mm}{10mm}
|
||||
\sfrowend
|
||||
|
||||
% No overlay caption: it would sit on the residual. Caption the group below.
|
||||
\sfgroup[role=attn]{block}{(attn)(ffn)}{}
|
||||
\sfarrow{block.east}{add.west}
|
||||
\sfarrowlabel[bend left=18]{in.north}{add.north}{残差}
|
||||
|
||||
\sflane{R1}
|
||||
\sfcaption{in}{输入}{源数据}
|
||||
\sfcaption{block}{主路模块}{注意力 + 前馈}
|
||||
\sfcaption{add}{融合}{残差相加}
|
||||
\sfcaption{out}{输出}{目标表示}
|
||||
|
||||
\sfbbox{all}
|
||||
\sfmeaningbox{mb}{108mm}{all}
|
||||
{主路逐层处理,残差边跳过主路}
|
||||
{输入、主路模块、融合节点、输出}
|
||||
{残差把输入直接送入融合,减轻深层优化负担}
|
||||
|
||||
\end{tikzpicture}
|
||||
\end{document}
|
||||
@@ -0,0 +1,56 @@
|
||||
% superfig golden example 4 -- a dual-encoder dependency, not a linear pipeline.
|
||||
% Two encoder rows share a rail; fusion is placed from their east anchors.
|
||||
% ../scripts/build.sh dependency-graph.tex
|
||||
\documentclass[border=10pt]{standalone}
|
||||
\usepackage[cjk]{superfig}
|
||||
|
||||
\sfsetrole{text}{sfTeal}
|
||||
\sfsetrole{img}{sfOrange}
|
||||
\sfsetrole{fuse}{sfViolet}
|
||||
\sfsetrole{out}{sfCoral}
|
||||
|
||||
\begin{document}
|
||||
\begin{tikzpicture}
|
||||
|
||||
\sfstage{S1}{双塔编码}
|
||||
\sfrow{R1}{12mm}
|
||||
\sfnode[role=text]{txt}{文本}{14mm}{10mm}
|
||||
\sfconn{e1}{}
|
||||
\sfnode[role=text]{te}{文本编码}{18mm}{10mm}
|
||||
\sfrowend
|
||||
\sflane{R1}
|
||||
\sfcaption{txt}{text}{token 序列}
|
||||
\sfcaption{te}{$E_t$}{文本向量}
|
||||
|
||||
\sfrow{R2}{12mm}
|
||||
\sfnode[role=img]{img}{图像}{14mm}{10mm}
|
||||
\sfconn{e2}{}
|
||||
\sfnode[role=img]{ve}{视觉编码}{18mm}{10mm}
|
||||
\sfrowend
|
||||
\sflane{R2}
|
||||
\sfcaption{img}{image}{视觉输入}
|
||||
\sfcaption{ve}{$E_v$}{视觉向量}
|
||||
|
||||
% Fusion sits to the right of the two encoders, on their shared east line.
|
||||
\sfnode[role=fuse, at={($(te.east)!0.5!(ve.east)+(20mm,0)$)}]{fu}{融合}{16mm}{10mm}
|
||||
\sfnode[role=out, at={($(fu.east)+(16mm,0)$)}]{dec}{解码}{16mm}{10mm}
|
||||
\sfnode[role=out, at={($(dec.east)+(16mm,0)$)}]{out}{输出}{14mm}{10mm}
|
||||
\sfarrow{te.east}{fu.west}
|
||||
\sfarrow{ve.east}{fu.west}
|
||||
\sfarrow{fu.east}{dec.west}
|
||||
\sfarrow{dec.east}{out.west}
|
||||
\sfnolane
|
||||
\sfcaption{fu}{fuse}{晚融合}
|
||||
\sfcaption{dec}{decode}{条件生成}
|
||||
\sfcaption{out}{out}{目标文本}
|
||||
|
||||
\sfbbox{all}
|
||||
\sftopformula{F}{%
|
||||
$y = \mathrm{Dec}\bigl(\mathrm{Fuse}(E_t(x),\, E_v(I))\bigr)$}
|
||||
\sfmeaningbox{mb}{112mm}{all}
|
||||
{两个编码器互不共享权重,只在融合节点会合}
|
||||
{文本塔、视觉塔、融合、解码}
|
||||
{各塔独立编码;融合后才进入解码,所以任一侧的依赖都经过融合节点}
|
||||
|
||||
\end{tikzpicture}
|
||||
\end{document}
|
||||
@@ -0,0 +1,47 @@
|
||||
% superfig golden example 1 -- one horizontal paper-figure pipeline.
|
||||
% Main path is input -> model -> prediction. Loss is a side object, not a
|
||||
% station on the forward path.
|
||||
% ../scripts/build.sh pipeline.tex
|
||||
\documentclass[border=10pt]{standalone}
|
||||
\usepackage[cjk]{superfig}
|
||||
|
||||
\sfsetrole{input}{sfTeal}
|
||||
\sfsetrole{model}{sfOrange}
|
||||
\sfsetrole{loss}{sfCoral}
|
||||
\sfsetrole{output}{sfViolet}
|
||||
|
||||
\begin{document}
|
||||
\begin{tikzpicture}
|
||||
|
||||
\sfstage{SA}{推理流程:一次前向}
|
||||
\sfrow{R1}{16mm}
|
||||
\sfnode[role=input]{x}{输入 $x$}{16mm}{12mm}
|
||||
\sfconn{e1}{预处理}
|
||||
\sfnode[role=model]{f}{模型 $f_\theta$}{18mm}{12mm}
|
||||
\sfconn{e2}{logits}
|
||||
\sfnode[role=output]{y}{预测 $\hat y$}{16mm}{12mm}
|
||||
\sfrowend
|
||||
|
||||
% Loss compares the prediction with the target; it is not on the main path.
|
||||
% Hang it below ŷ with enough shaft that no caption sits on the arrow.
|
||||
\sfnode[role=loss, at={($(y.south)+(0,-22mm)$)}]{s}{损失 $L$}{14mm}{12mm}
|
||||
\sfarrowlabel{y.south}{s.north}{$L(\hat y,y)$}
|
||||
|
||||
\sflane{R1}
|
||||
\sfcaption{x}{$x$}{原始输入}
|
||||
\sfcaption{f}{$f_\theta$}{可学习参数}
|
||||
\sfnolane
|
||||
\sfcaption{s}{$L$}{与真值比较}
|
||||
|
||||
\sfbbox{all}
|
||||
\sftopformula{F}{%
|
||||
$x \;\xrightarrow{\;f_\theta\;}\; \hat y,\qquad
|
||||
\min_\theta\; L\bigl(f_\theta(x),\,y\bigr)$}
|
||||
\sfmeaningbox{mb}{96mm}{all}
|
||||
{一次从输入到预测的前向;损失在预测之后单独计算}
|
||||
{数据、模型参数、预测、损失}
|
||||
{预处理后送入模型;模型产生 logits 得到预测;损失比较预测与真值,梯度再回到参数}
|
||||
\sfsignature{推理流程示意}{mb}
|
||||
|
||||
\end{tikzpicture}
|
||||
\end{document}
|
||||
@@ -0,0 +1,46 @@
|
||||
% superfig golden example 3 -- a state/time flow.
|
||||
% One decode step: previous KV plus a new token produce the next state
|
||||
% and the emitted token. The callout is an aside on the finished state band.
|
||||
% ../scripts/build.sh state-flow.tex
|
||||
\documentclass[border=10pt]{standalone}
|
||||
\usepackage[cjk]{superfig}
|
||||
|
||||
\sfsetrole{state}{sfTeal}
|
||||
\sfsetrole{step}{sfOrange}
|
||||
\sfsetrole{tok}{sfViolet}
|
||||
|
||||
\begin{document}
|
||||
\begin{tikzpicture}
|
||||
|
||||
\sfstage{S}{解码一步:旧状态 + 新 token}
|
||||
\sfvgap{26mm}
|
||||
\sfrow{R1}{14mm}
|
||||
\sfnode[role=state]{kv}{KV$_t$}{16mm}{11mm}
|
||||
\sfconn{c1}{读}
|
||||
\sfnode[role=step]{dec}{解码}{16mm}{11mm}
|
||||
\sfconn{c2}{追加}
|
||||
\sfnode[role=state]{kv2}{KV$_{t+1}$}{18mm}{11mm}
|
||||
\sfrowend
|
||||
\sflane{R1}
|
||||
\sfcaption{kv}{KV$_t$}{已缓存键值}
|
||||
\sfcaption{dec}{一步}{读 KV,写新列}
|
||||
\sfcaption{kv2}{KV$_{t+1}$}{状态推进}
|
||||
\sfcallout{N1}{38mm}{R1}{为何保留 KV}{%
|
||||
下一步只读已有列、只追加 $x_t$ 这一列。重算前缀是另一条路径,这张图不画。}
|
||||
|
||||
% Tokens enter from above so the caption lane below the state row stays clear.
|
||||
\sfnode[role=tok, at={($(dec.north west)+(-4mm,14mm)$)}]{xt}{$x_t$}{14mm}{10mm}
|
||||
\sfnode[role=tok, at={($(dec.north east)+(4mm,14mm)$)}]{yt}{$y_t$}{14mm}{10mm}
|
||||
\sfarrow{xt.south}{dec.north}
|
||||
\sfarrow{dec.north}{yt.south}
|
||||
|
||||
\sfbbox{all}
|
||||
\sftopformula{F}{%
|
||||
$(\mathrm{KV}_{t+1},\, y_t) = \mathrm{Decode}(\mathrm{KV}_t,\, x_t)$}
|
||||
\sfmeaningbox{mb}{108mm}{all}
|
||||
{自回归一步:状态沿时间推进}
|
||||
{缓存 KV、解码算子、本步 token}
|
||||
{解码读 $\mathrm{KV}_t$ 与 $x_t$,写出 $y_t$,并把新列追加为 $\mathrm{KV}_{t+1}$}
|
||||
|
||||
\end{tikzpicture}
|
||||
\end{document}
|
||||
Reference in New Issue
Block a user