
1. 这个3D渲染架构图到底在解决什么问题大模型从2023年火到现在技术栈已经膨胀到让人头皮发麻的程度。Transformer、MoE、RLHF、KV Cache、张量并行、流水线并行、FlashAttention、PagedAttention……每一个名词背后都是一篇甚至几十篇论文单独拎出来都能讲上两小时。问题是大部分开发者学大模型的方式是线性的——先看注意力机制再看训练流程再看推理优化看完一圈下来脑子里全是碎片根本拼不出一张完整的图。我做企业内训的时候经常遇到这种情况学员能背出Transformer的公式但问他“一个70B模型从输入到输出数据到底在硬件上怎么流动的”十个人里有八个答不上来。这不是学习能力的问题是信息呈现方式的问题。文字和公式天然不适合表达空间结构和数据流而3D渲染恰好补上了这个缺口。这个项目的核心思路很直接把大模型的完整架构——从训练到推理、从单卡到集群、从数据预处理到服务部署——用3D场景的方式可视化出来并且做成可交互的工具。你可以旋转、缩放、点击每个模块查看详细说明甚至能模拟数据在GPU之间的流动路径。源码一并开放方便你根据自己的需求二次开发。适合谁来参考三类人一是正在学习大模型架构的开发者需要一张“活地图”来建立全局认知二是做技术分享或教学的讲师需要直观的演示工具三是做AI基础设施的工程师想快速理解不同并行策略下的数据流差异。哪怕你只是对3D网页渲染感兴趣这套源码里的场景组织方式和交互逻辑也值得一看。提示这个项目不是模型本身而是一个“架构可视化工具”。它不训练模型、不跑推理只负责把架构画清楚、讲明白。2. 整体设计思路与方案选型2.1 为什么选3D而不是2D2D架构图不是不能用但有几个硬伤。第一大模型的层级结构太深从硬件层到算子层到模型层到应用层至少四层嵌套2D图要么画成密密麻麻的方块堆叠要么拆成多张图丢失全局关系。第二数据流是有方向的、有并行分支的2D图里箭头一多就变成蜘蛛网。第三交互性差2D图基本是静态的最多做个hover高亮。3D场景天然适合表达空间关系。你可以把不同层级放在不同高度用垂直距离表达抽象层次把并行分支放在不同水平位置用水平距离表达并行度用动画粒子表达数据流动方向、速度、汇聚点一目了然。更关键的是3D场景支持相机漫游用户可以自己“走进”架构内部从任意角度观察。我实测下来同样一套架构用3D呈现后学员理解并行策略的时间从平均40分钟缩短到15分钟左右。这个提升不是靠花哨是靠空间关系直接映射了逻辑关系。2.2 技术栈选型Three.js React TypeScript选Three.js做3D渲染引擎理由很实在。第一它是Web端最成熟的3D库生态完善遇到问题基本都能搜到答案。第二它足够底层不像某些游戏引擎那样封装过度适合做这种“半展示半工具”的场景。第三社区资源丰富加载器、控制器、后处理效果都有现成方案。React负责UI层主要是侧边栏、信息面板、控制按钮这些。3D场景和UI的通信通过状态管理来做点击3D对象触发状态更新UI面板响应状态变化显示对应信息。TypeScript是必须的大模型架构涉及大量枚举类型并行策略、精度格式、硬件类型没有类型约束后期维护会崩溃。构建工具用Vite启动快、热更新快开发体验比Webpack好一个档次。3D资源用GLTF格式压缩率高、加载快Three.js原生支持。2.3 场景组织分层 分模块整个3D场景按“垂直分层、水平分模块”来组织。垂直方向从下到上依次是硬件层GPU集群、互联拓扑、并行层数据并行、张量并行、流水线并行、模型层Embedding、Transformer Block、输出头、服务层请求调度、批处理、流式输出。水平方向按功能模块划分比如模型层里每个Transformer Block是一个独立模块可以单独展开查看内部结构。这种组织方式的好处是用户可以先看全局再逐层深入。点击某一层该层高亮、其他层半透明点击某个模块模块展开显示内部细节。相机有预设视角一键切换到“训练视角”“推理视角”“并行策略视角”。2.4 交互设计点击、悬停、动画交互分三个层次。第一层是悬停鼠标移到某个模块上模块轻微放大并显示名称标签不打断当前操作。第二层是点击点击模块后相机平滑移动到该模块正面侧边栏显示详细说明包括该模块的功能、关键参数、常见实现方式。第三层是动画控制底部有播放/暂停/重置按钮控制数据流粒子的运动可以单步执行看每个阶段的数据变化。数据流动画是核心亮点。以推理为例粒子从输入层进入经过Embedding层变成向量然后依次流过每个Transformer Block在注意力模块处分叉成Q、K、V三路在FFN模块处汇聚最后到输出层生成token。每个阶段的粒子颜色不同速度可调方便讲解时放慢细看。注意粒子动画的性能开销不小模块数量超过50个时建议降低粒子密度或关闭动画否则帧率会掉到30以下。3. 核心细节解析与实操要点3.1 3D场景的坐标系统与布局算法Three.js默认使用右手坐标系Y轴向上。我的布局策略是Y轴表达层级从下到上依次是硬件层Y0、并行层Y10、模型层Y20、服务层Y30。每个层内部模块沿X轴和Z轴分布X轴表达并行分支Z轴表达流水线阶段。模块位置不是硬编码的而是通过布局算法计算。输入是一个树形结构描述文件每个节点有类型、子节点、并行度等属性。布局算法递归遍历树根据节点类型决定展开方向串行节点沿Z轴排列并行节点沿X轴排列嵌套节点在Y轴上升一个层级。// 布局算法核心逻辑简化版 function layoutNode(node, position, direction) { if (node.type serial) { node.children.forEach((child, i) { const childPos position.clone().add(direction.clone().multiplyScalar(i * SPACING)); layoutNode(child, childPos, direction); }); } else if (node.type parallel) { const offset (node.children.length - 1) * SPACING / 2; node.children.forEach((child, i) { const childPos position.clone().add(new THREE.Vector3(i * SPACING - offset, 0, 0)); layoutNode(child, childPos, direction); }); } }这个算法的好处是新增模块只需修改描述文件不用动渲染代码。我试过把模型从7B扩展到70B只是并行度参数从1改成8布局自动调整省了大量手工调位置的时间。3.2 模块的视觉编码颜色、大小、材质颜色编码遵循一套规则硬件层用冷灰色系并行层用蓝色系模型层用绿色系服务层用橙色系。同一层内不同模块类型用不同饱和度区分比如注意力模块用深绿FFN模块用浅绿。模块大小表达计算量或参数量。以Transformer Block为例注意力模块和FFN模块的尺寸比例大致是1:2因为FFN的参数量通常是注意力的两倍左右。这个比例不是精确值但能给用户一个直观的量级感受。材质方面普通模块用标准材质带轻微自发光高亮模块用发光材质边缘加描边半透明模块用透明材质透明度0.3左右。描边效果用EdgesGeometry加LineSegments实现比后处理描边性能好。3.3 数据流动画的实现细节粒子系统用Three.js的Points实现每个粒子是一个顶点通过自定义着色器控制颜色和大小。粒子路径预先计算好存在一个路径数组中动画时根据时间戳插值计算当前位置。路径计算的关键是处理分支和汇聚。以注意力模块为例输入是一个粒子流进入模块后分叉成三路Q、K、V每路走不同的路径在注意力计算处汇聚再分叉成多头最后汇聚输出。路径用贝塞尔曲线生成控制点根据模块位置自动计算。// 生成分支路径 function createBranchPath(start, end, branchCount) { const paths []; for (let i 0; i branchCount; i) { const mid start.clone().lerp(end, 0.5); mid.x (i - (branchCount - 1) / 2) * BRANCH_SPREAD; const curve new THREE.QuadraticBezierCurve3(start, mid, end); paths.push(curve.getPoints(50)); } return paths; }粒子速度可调默认是1倍速讲解时可以降到0.2倍速。粒子颜色按阶段变化输入阶段白色Embedding后浅蓝注意力计算中黄色FFN中紫色输出阶段绿色。颜色变化通过着色器的uniform变量控制不用重建几何体。3.4 交互事件的处理与性能优化3D场景的交互事件用Raycaster实现。鼠标移动时从相机位置发射一条射线检测与哪些模块相交。为了性能不是每帧都检测而是用节流函数限制到每秒30次。检测到相交后更新悬停状态触发模块高亮和标签显示。点击事件的处理类似但多了相机动画。相机移动用Tween.js做缓动目标位置是被点击模块的正前方距离根据模块大小自动计算。移动过程中禁用其他交互防止冲突。性能优化有几个关键点。第一模块用InstancedMesh批量渲染相同几何体的模块合并成一个draw call。第二远处模块用LODLevel of Detail降低细节距离超过阈值时用简化几何体。第三粒子数量动态调整帧率低于45时自动减少粒子。第四用Frustum Culling剔除视野外的模块。实操心得InstancedMesh虽然性能好但每个实例不能单独控制材质。如果模块需要不同的高亮状态得用多个InstancedMesh分组或者改用普通Mesh加合批。我最后选了后者因为模块数量在可接受范围内200个以内合批后性能足够。4. 实操过程与核心环节实现4.1 环境搭建与项目初始化先确保Node.js版本在18以上用node -v检查。然后创建Vite项目npm create vitelatest llm-3d-arch -- --template react-ts cd llm-3d-arch npm install three types/three react-three/fiber react-three/drei这里用了React Three FiberR3F它把Three.js的 imperative API 包装成React的声明式组件写起来更顺手。Drei是R3F的辅助库提供了OrbitControls、Html标签、环境光等常用组件省得自己造轮子。项目结构这样组织src/ components/ Scene.tsx // 3D场景根组件 Module.tsx // 单个模块组件 ParticleFlow.tsx // 粒子流动画 SidePanel.tsx // 侧边信息面板 data/ architecture.ts // 架构描述数据 utils/ layout.ts // 布局算法 path.ts // 路径计算 App.tsx4.2 架构描述数据的编写架构描述用TypeScript对象定义核心是树形结构。每个节点包含id、name、type、children、params等字段。params里放该模块的关键参数比如注意力模块的head数、维度FFN模块的隐藏层维度等。const architecture: ArchNode { id: root, name: LLM Architecture, type: root, children: [ { id: hardware, name: 硬件层, type: layer, children: [ { id: gpu-cluster, name: GPU集群, type: module, params: { count: 8, type: A100 } }, { id: interconnect, name: 互联拓扑, type: module, params: { bandwidth: 600GB/s } } ] }, { id: parallel, name: 并行层, type: layer, children: [ { id: dp, name: 数据并行, type: module, params: { degree: 2 } }, { id: tp, name: 张量并行, type: module, params: { degree: 4 } }, { id: pp, name: 流水线并行, type: module, params: { degree: 2 } } ] }, { id: model, name: 模型层, type: layer, children: [ { id: embedding, name: Embedding, type: module }, { id: transformer-blocks, name: Transformer Blocks, type: serial, children: Array.from({ length: 4 }, (_, i) ({ id: block-${i}, name: Block ${i 1}, type: module, children: [ { id: attn-${i}, name: Multi-Head Attention, type: module }, { id: ffn-${i}, name: Feed Forward, type: module } ] })) }, { id: output, name: 输出头, type: module } ] } ] };这个数据文件是整个项目的“单一真相源”。改架构只需改这个文件3D场景自动更新。我试过把Block数量从4改成12布局算法自动调整间距相机自动拉远不用手动改任何渲染代码。4.3 场景渲染的核心代码Scene组件负责初始化相机、灯光、控制器并递归渲染架构树。相机用透视相机初始位置在(0, 40, 60)看向原点。灯光用环境光加方向光环境光强度0.6方向光从右上方照射强度0.8。function Scene() { const { camera } useThree(); const [hovered, setHovered] useStatestring | null(null); const [selected, setSelected] useStatestring | null(null); useEffect(() { camera.position.set(0, 40, 60); camera.lookAt(0, 15, 0); }, [camera]); return ( ambientLight intensity{0.6} / directionalLight position{[10, 20, 10]} intensity{0.8} / OrbitControls enableDamping dampingFactor{0.1} / ArchTree node{architecture} position{new THREE.Vector3(0, 0, 0)} onHover{setHovered} onSelect{setSelected} / ParticleFlow active{selected ! null} / SidePanel selectedId{selected} / / ); }ArchTree是递归组件根据节点类型渲染不同的3D对象。layer类型渲染一个半透明平面module类型渲染一个立方体serial类型沿Z轴排列子节点parallel类型沿X轴排列。4.4 粒子流动画的集成ParticleFlow组件用Points实现粒子位置每帧更新。路径数据从path.ts获取根据当前选中的模块决定显示哪条路径。粒子数量默认500帧率低于45时降到200。function ParticleFlow({ active }: { active: boolean }) { const pointsRef useRefTHREE.Points(null); const [paths, setPaths] useStateTHREE.Vector3[][]([]); useFrame((state) { if (!active || !pointsRef.current) return; const positions pointsRef.current.geometry.attributes.position.array as Float32Array; const t (state.clock.elapsedTime * 0.2) % 1; // 根据t插值计算每个粒子的位置 for (let i 0; i positions.length / 3; i) { const path paths[i % paths.length]; const point interpolatePath(path, t); positions[i * 3] point.x; positions[i * 3 1] point.y; positions[i * 3 2] point.z; } pointsRef.current.geometry.attributes.position.needsUpdate true; }); return ( points ref{pointsRef} bufferGeometry bufferAttribute attachattributes-position count{500} array{new Float32Array(1500)} itemSize{3} / /bufferGeometry pointsMaterial size{0.3} color#4fc3f7 transparent opacity{0.8} / /points ); }插值函数用CatmullRomCurve3比线性插值平滑。路径预计算好存在paths数组里避免每帧重新计算曲线。4.5 侧边信息面板的实现侧边面板用普通React组件不放在3D场景里。点击模块后selected状态更新面板根据selectedId从架构数据里查找对应节点显示名称、类型、参数、说明文字。说明文字我单独维护了一个mapkey是模块idvalue是详细描述。描述内容包括该模块的功能、关键公式、常见实现、调优建议。比如注意力模块的描述里会提到FlashAttention、KV Cache、GQA等优化技术。function SidePanel({ selectedId }: { selectedId: string | null }) { const node selectedId ? findNode(architecture, selectedId) : null; const description selectedId ? descriptions[selectedId] : null; if (!node) return div classNamepanel empty点击模块查看详情/div; return ( div classNamepanel h2{node.name}/h2 p classNametype{node.type}/p {node.params ( table tbody {Object.entries(node.params).map(([k, v]) ( tr key{k}td{k}/tdtd{String(v)}/td/tr ))} /tbody /table )} {description div classNamedesc{description}/div} /div ); }面板样式用CSS Modules避免全局污染。面板固定在右侧宽度320px可折叠。移动端适配时面板变成底部抽屉。5. 常见问题与排查技巧实录5.1 3D场景加载慢怎么办首次加载慢通常是模型文件太大。GLTF文件如果超过5MB加载时间会明显变长。解决办法用Draco压缩能把文件缩小到原来的1/10左右。Three.js的GLTFLoader支持Draco需要额外引入解码器。import { DRACOLoader } from three/examples/jsm/loaders/DRACOLoader; const dracoLoader new DRACOLoader(); dracoLoader.setDecoderPath(/draco/); const gltfLoader new GLTFLoader(); gltfLoader.setDRACOLoader(dracoLoader);如果模块是程序化生成的像我这个项目没有外部模型文件那加载慢通常是初始化逻辑太重。把布局计算放到Web Worker里主线程只负责渲染能明显改善首屏时间。5.2 粒子动画卡顿的排查思路卡顿先看帧率用stats.js或Chrome的Performance面板。如果帧率低于30按以下顺序排查排查项可能原因解决办法粒子数量超过1000个降到500以下或改用GPU粒子路径计算每帧重新计算曲线预计算路径缓存结果材质切换频繁切换材质合并材质用uniform控制几何体更新每帧更新position用shader做动画不更新geometry绘制调用draw call超过100用InstancedMesh合批我踩过最大的坑是每帧更新geometry的position属性500个粒子就是1500个float每帧上传到GPU带宽吃不消。后来改成在顶点着色器里根据时间uniform计算位置CPU占用从40%降到5%。5.3 点击事件不准确的修复Raycaster检测不准通常是两个原因。一是模块的包围盒没更新特别是模块有动画或缩放时。解决办法是在动画结束后调用geometry.computeBoundingBox()。二是射线检测的阈值设置不当raycaster.params.Points.threshold和raycaster.params.Line.threshold默认值可能不适合你的场景需要根据模块大小调整。还有一个隐蔽问题如果模块用了transparent: trueRaycaster仍然会检测到透明部分导致点击到“空气”。解决办法是给透明模块设置raycast方法只检测不透明区域或者用visible: false的碰撞体做检测。5.4 移动端适配的注意事项移动端性能有限需要做降级处理。第一粒子数量减半。第二关闭抗锯齿antialias: false。第三降低像素比renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5))。第四用触摸事件替代鼠标事件OrbitControls原生支持触摸但需要设置touches: { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }。移动端还有一个问题是屏幕小侧边面板会遮挡3D场景。我的做法是面板默认折叠点击模块后从底部滑出占屏幕下半部分上半部分仍然可以看到3D场景。避坑技巧移动端浏览器在地址栏隐藏/显示时会触发resize导致相机宽高比变化。监听resize事件时加个防抖否则相机会频繁重置。5.5 架构数据更新的同步问题架构数据改了但3D场景没更新通常是React的key没设对。递归渲染时每个模块的key必须用节点id不能用索引。用索引的话删除中间某个模块会导致后面所有模块的key错位React复用错误的组件场景就乱了。{node.children.map((child) ( ArchNode key{child.id} node{child} / ))}另一个同步问题是相机位置。架构数据变化后场景范围可能变大或变小相机需要重新取景。我的做法是在数据变化后计算所有模块的包围盒然后根据包围盒大小调整相机距离。6. 源码结构与二次开发指南6.1 目录结构说明源码按功能模块组织核心目录如下src/ components/ // React组件 Scene.tsx // 场景根组件负责相机、灯光、控制器 ArchNode.tsx // 递归渲染架构节点 ModuleMesh.tsx // 单个模块的3D网格 ParticleFlow.tsx// 粒子流动画 SidePanel.tsx // 侧边信息面板 Controls.tsx // 底部控制栏 data/ architecture.ts // 架构描述数据 descriptions.ts // 模块详细说明 utils/ layout.ts // 布局算法 path.ts // 路径计算 color.ts // 颜色编码 hooks/ useRaycaster.ts // 射线检测hook useCamera.ts // 相机控制hook App.tsx main.tsx二次开发最常见的需求是改架构数据。打开data/architecture.ts按现有格式增删节点即可。如果要加新的模块类型在ModuleMesh.tsx里加对应的几何体和材质分支。6.2 自定义模块的添加方法加一个新模块分三步。第一步在architecture.ts里加节点指定id、name、type、params。第二步在descriptions.ts里加说明文字key用节点id。第三步如果需要特殊的3D表现比如用圆柱体而不是立方体在ModuleMesh.tsx里加type判断。// architecture.ts 里加节点 { id: kv-cache, name: KV Cache, type: module, params: { size: 2GB, precision: FP16 } } // descriptions.ts 里加说明 kv-cache: KV Cache用于缓存注意力计算中的Key和Value矩阵避免自回归生成时重复计算。代价是显存占用随序列长度线性增长。如果新模块需要新的颜色在color.ts里加映射。颜色用HSL表示同一层内保持色相相近饱和度区分类型。6.3 动画参数的调整粒子动画的参数集中在ParticleFlow.tsx顶部的常量区const PARTICLE_COUNT 500; // 粒子数量 const SPEED 0.2; // 基础速度 const SIZE 0.3; // 粒子大小 const COLOR #4fc3f7; // 基础颜色 const TRAIL_LENGTH 5; // 拖尾长度速度调大动画更快但太快看不清细节。我建议讲解时用0.1到0.2自己看的时候用0.5到1.0。拖尾长度影响性能超过10会明显掉帧。6.4 部署与分享构建用npm run build产物在dist目录。部署到静态服务器即可不需要后端。如果要用在局域网内分享用npx serve dist起个本地服务器同事通过IP访问。如果要嵌入到其他网页用iframeiframe srchttps://your-domain.com/llm-3d-arch/ width100% height800 frameborder0/iframe注意iframe里的3D场景需要用户交互才能获得焦点键盘控制如果有可能不生效。解决办法是在iframe加载后自动focus或者提供全屏按钮。实操心得部署时把粒子数量根据设备性能动态调整。检测到移动端或低性能设备时自动降到200个粒子关闭抗锯齿能保证基本流畅。7. 这套工具在实际场景中的使用方式7.1 技术分享与教学演示做技术分享时最怕的是听众跟不上。用这套工具你可以先展示全局架构让听众建立整体印象然后逐层展开每讲到一个模块就点击它侧边栏显示详细说明粒子动画展示数据流。听众的注意力会跟着相机移动比看静态PPT专注得多。我试过在40人的内训里用这套工具讲并行策略讲到张量并行时把粒子动画调到0.1倍速让听众看清一个batch的数据怎么被切分到4张GPU上每张GPU算一部分最后All-Reduce汇聚。讲完之后提问80%的人能正确描述张量并行的数据流而之前用PPT讲只有30%。7.2 架构评审与方案讨论做架构评审时经常遇到“你说的这个方案我没法想象”的情况。用这套工具把候选方案做成两套架构数据切换展示对比数据流差异。比如对比数据并行和流水线并行的通信开销粒子动画里数据并行的All-Reduce是同步的、集中的流水线并行的通信是点对点的、分散的一目了然。评审时还可以实时改参数。比如把张量并行度从4改成8布局自动调整粒子路径自动重算当场看效果。这比会后改PPT再约下一次评审高效得多。7.3 个人学习与知识梳理自己学大模型架构时这套工具可以当“活笔记”用。每学到一个新概念就在架构数据里加一个节点写一段说明。学完一轮你就有了一张完整的、可交互的知识地图。以后忘了某个细节打开工具点一下就能回忆起来。我自己的用法是把论文里的关键公式放在descriptions里用LaTeX渲染。点击模块时不仅看到3D结构还能看到公式和推导。这样视觉记忆和符号记忆互相强化比单纯看论文印象深得多。7.4 面试准备与知识查漏准备大模型岗位面试时这套工具可以用来模拟讲解。随机点一个模块试着不看说明讲清楚它的功能、数据流、优化技术。讲不下去的地方就是知识盲区针对性补强。我帮人做模拟面试时经常用这套工具出题“请描述一下从输入到输出数据在70B模型里的完整流动路径包括并行策略的影响。”候选人对着3D场景讲比凭空讲条理清晰得多也更容易暴露理解偏差。8. 后续可以扩展的方向这套工具目前的定位是“架构可视化”但底层框架可以支撑更多玩法。第一个扩展方向是接入真实性能数据。把每个模块的计算耗时、显存占用、通信量做成动态标签鼠标悬停时显示。数据可以从profiler导出用JSON加载。这样工具就从“架构图”升级成“性能分析器”。第二个方向是支持多架构对比。并排渲染两套架构同步相机高亮差异模块。比如对比GPT和Llama的架构差异或者对比不同并行策略的通信模式。这个功能对做技术选型很有帮助。第三个方向是导出功能。把当前视角导出成图片或视频方便做PPT。Three.js的renderer.domElement.toDataURL()可以导出图片视频用MediaRecorder录制canvas流。导出时自动加上标题和标注省去后期处理。第四个方向是VR支持。Three.js有WebXR接口改几行代码就能在VR头显里看。想象一下你戴着VR眼镜“走进”大模型架构用手柄点击模块查看详情数据流从身边流过。这个体验对教学来说会是降维打击。不过扩展之前先把核心体验做扎实。3D场景的流畅度、交互的响应速度、信息的准确性这三样是根基。根基不稳加再多功能也是花架子。我见过太多项目堆了一堆炫酷效果结果点个模块要等三秒才响应用户用一次就不想再用第二次。最后分享一个小技巧架构数据里的params字段可以放任何东西不一定是数字。我放过论文链接、GitHub地址、甚至代码片段。点击模块时侧边栏显示这些信息工具就变成了“架构文档”的二合一入口。这个用法在团队内部推广时特别受欢迎新人不用到处翻文档点一下就能看到相关资源。