0%

相比fetch Api, axios 提供了更简洁、更直观的 API,使得发送 HTTP 请求和处理响应更加方便; 自动处理json对象(fetch 可以调用await response.json())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
axios.post('/api/submit', this.editForm)
.then(response => {
console.log('Form submitted successfully:', response.data);
})
.catch(error => {
console.error('Error submitting form:', error);
});

axios.request({
url: '/api/submit',
method: 'post',
data: this.editForm
}).then(response => {
console.log('Form submitted successfully:', response.data);
}).catch(error => {
console.error('Error submitting form:', error);
});

instance
1
2
3
4
5
6
7
const instance = axios.create({ baseURL: '/api' });

// Works just like axios(config)
instance({
url: '/users',
method: 'get'
});

拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
},
{ synchronous: true, runWhen: () => /* This function returns true */}
);

// Add a response interceptor
axios.interceptors.response.use(function onFulfilled(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, function onRejected(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});

FastAPI与Express.js的区别

特性 FastAPI Express.js
开发语言 Python JavaScript/TypeScript
性能 基于Starlette和Pydantic,异步处理性能优异 基于Node.js事件循环,适合I/O密集型操作
类型提示 原生支持Python类型注解,自动生成API文档 需要TypeScript才能获得类型提示
API设计 声明式路由,依赖注入系统 命令式路由,中间件链
自动文档 自动生成Swagger和ReDoc文档 需要第三方库如Swagger UI
数据验证 Pydantic自动数据验证 需要手动验证或使用第三方库
生态系统 Python生态(NumPy, Pandas, OpenCV等) npm生态系统
学习曲线 相对平缓,Python开发者容易上手 前端开发者熟悉,学习曲线适中

库和项目

1
pip install "fastapi[standard]" "uvicorn[standard]"

uvicorn是应用服务器 相当于tomcat

类似express.js 可以写一个main.py 然后执行。 fastapi没有初始化项目的命令行工具 可以考虑官方项目模板: Full Stack FastAPI Template

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
my_fastapi_project/
├── app/ # 应用核心包(必须含 __init__.py)
│ ├── __init__.py
│ ├── main.py # 🌟 应用入口:创建 FastAPI 实例、挂载路由、中间件
│ ├── config.py # 配置管理(推荐 pydantic-settings)
│ ├── database.py # DB 连接/会话(SQLAlchemy/Tortoise 等)
│ ├── models/ # 🗃️ 数据库模型(ORM 映射)
│ │ ├── __init__.py
│ │ └── user.py
│ ├── schemas/ # 📦 Pydantic 模型(请求/响应验证)
│ │ ├── __init__.py
│ │ └── user.py
│ ├── routers/ # 🛣️ 路由模块(按资源拆分)
│ │ ├── __init__.py
│ │ ├── users.py
│ │ └── items.py
│ ├── dependencies.py # 🔑 依赖注入(认证、数据库会话等)
│ ├── services/ # 💼 业务逻辑层(解耦路由与核心逻辑)
│ │ └── user_service.py
│ └── utils/ # 🧰 工具函数(加密、日期处理等)
├── tests/ # 🧪 测试(pytest 兼容)
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_main.py
│ └── test_users.py
├── alembic/ # 🔄 DB 迁移(使用 SQLAlchemy + Alembic 时)
├── static/ # 📁 静态文件(可选)
├── templates/ # 🎨 模板(Jinja2,如需 SSR)
├── .env.example # 🔒 环境变量示例(.gitignore 中排除 .env)
├── .gitignore
├── requirements.txt / pyproject.toml
├── README.md
└── uvicorn_config.py # 🚀 Uvicorn 启动配置(workers/log_level 等)

安装依赖

1
2
3
4
<ItemGroup>
<PackageReference Include="HandyControl" Version="3.5.1" />
<PackageReference Include="Prism.Unity" Version="8.1.97" />
</ItemGroup>

app.xmal中Wpf:Application替换成prism:PrismApplication
1
2
3
4
5
6
7
8
9
<prism:PrismApplication
x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>

标记中删除了StartupUri=”MainWindow.xaml”(否则生成两个实例)

cs代码实现两个抽象方法RegisterTypes和CreateShell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建程序主窗口
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}

protected override void RegisterTypes(IContainerRegistry container)
{
// 注册导航页面和ViewModel
container.RegisterForNavigation<MainWindow, MainWindowViewModel>();

// 注册单例服务
container.RegisterSingleton<IMyService, MyService>();

// 配置文件基础路径
string programData = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
programData = Path.Combine(programData, "xxxx");

// 从注册表读取配置路径
using (var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\xxxx"))
{
if (key != null && key.GetValue("SolutionPath") != null)
{
programData = key.GetValue("SolutionPath").ToString();
}
}

// 初始化全局缓存
GlobalCache.Instance.FileBasePath = programData;
GlobalCache.Instance.InitPaths();
}

1. MeshStandardMaterial

  • 用途:基于物理渲染(PBR),适用于追求真实感的场景,支持金属度、粗糙度、环境光遮蔽(AO)、法线贴图等。
  • 优势:高度逼真,支持复杂光照模型,适合高质量渲染。
  • 缺点:计算开销较大,不适合性能敏感的场景。

    2. MeshLambertMaterial

  • 用途:基于 Lambert 反射模型,是一种简化的漫反射材质,不考虑高光或复杂反射。
  • 优势:计算简单,性能好,适合非金属、低光要求的物体。
  • 缺点:缺乏真实感,不支持法线贴图、金属度等高级特性。

    3. MeshPhongMaterial

  • 用途:基于 Phong 反射模型,支持高光反射,可以模拟塑料、漆面等材质。
  • 优势:比 Lambert 更真实,支持高光和法线贴图。
  • 缺点:不是基于物理的渲染,效果不如 PBR 自然。

    4. MeshDepthMaterial

  • 用途:不显示颜色,只渲染深度信息(相机到物体的距离) QQs按:接近近投影面(camera.near)变明亮反之变暗 适合做雾效 寂静岭阴森视距。
  • 优势:常用于后期处理、阴影生成、深度图分析等。
  • 缺点:不适用于常规物体渲染。

    5. MeshNormalMaterial

  • 用途:显示法线方向,用于调试或特殊视觉效果。
  • 优势:便于检查模型的法线是否正确。
  • 缺点:无法用于真实渲染。

性能与适用场景的权衡

  • 性能优化:在移动端或低性能设备上,使用 MeshLambertMaterial 或 MeshBasicMaterial 可以显著提高渲染性能,而 MeshStandardMaterial 由于其复杂的光照计算,可能会造成帧率下降。

  • 特殊效果:例如,MeshDepthMaterial 可用于实现深度边缘检测、雾效或自定义的后期处理效果,这些是 MeshStandardMaterial 无法直接实现的。

  • 艺术风格:某些风格化渲染(如卡通渲染)更适合使用 MeshToonMaterial,而不是 PBR 材质。

三、兼容性与历史原因

  • Three.js 作为一个成熟的 3D 渲染库,需要兼容多种渲染管线和设备。一些材质(如 MeshLambertMaterial 和 MeshPhongMaterial)在早期版本中就已经存在,它们为不支持 PBR 的设备或项目提供了替代方案。
  • 在 WebGL 1.0 环境下,某些高级特性(如 PBR)可能不被完全支持,此时需要使用更简单的材质。

四、实际开发中的选择建议

材质类型 适用场景 是否支持贴图 性能
MeshStandardMaterial 高质量渲染、PBR 场景 支持所有贴图 较低
MeshLambertMaterial 简单漫反射、低性能设备 仅支持漫反射贴图 较高
MeshPhongMaterial 高光材质、塑料效果 支持高光、法线贴图 中等
MeshDepthMaterial 深度分析、后期处理 不支持颜色贴图 高
MeshNormalMaterial 法线调试、特殊效果 不支持颜色贴图 高

roughMap vs displacementMap
前者是PBR即作用于光线反射 后者是作用于顶点位移

@React-three/fiber

开箱即用(out of box) 组件化场景对象如
\ \ \

1
npm i three @react-three/fiber

额外的,transpile in Next.js, set in next.config.js:

1
transpilePackages: ['three'],

React Native中需要expo-gl

1
2
3
4
5
# Automatically install
expo install expo-gl

# Install NPM dependencies
npm install three @react-three/fiber

set Metro boundle tool if use loader, gltf and so on

1
2
3
4
5
6
7
// metro.config.js
module.exports = {
resolver: {
sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs', 'mjs'],
assetExts: ['glb', 'gltf', 'png', 'jpg'],
},
}

简单的栗子

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react'
import { Canvas } from '@react-three/fiber'

const App = () => (
<Canvas>{/* 相当于初始化的Scene */}
<pointLight position={[10, 10, 10]} />
<mesh>
<sphereGeometry />
<meshStandardMaterial color="hotpink" />
</mesh>
</Canvas>
)

@React-three/drei

@react-three/drei 是一个工具包(Utility Library),为 @react-three/fiber 提供了一系列高阶组件和实用函数,比如加载模型(useGLTF)、添加环境光(Environment)、设置相机控制(OrbitControls)等。 德语单词“Drei”,意思是“三”
从零探索@react-three/fiber @React-three/drei

Leva

1
npm install leva@latest --save-dev
1
2
3
4
5
6
7
8
9
import { useControls } from 'leva'

const color = useControls({
value: 'green',
})

<Canvas>
<color attach="background" args={[color.value]} />
</Canvas>

#

Renderer

init scene

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const canvas = document.createElement('canvas')
const sizes = {
width: this.$refs.container.clientWidth,
height: this.$refs.container.clientHeight
}
canvas.width = sizes.width;
canvas.height = sizes.height;
this.$refs.container.appendChild(canvas);
// 创建3D场景对象Scene
const scene = new THREE.Scene();
console.log(scene);
// Geometry
const geometry = new THREE.SphereGeometry(5, 32, 32);
const material = new THREE.MeshBasicMaterial({color: '#aaa', wireframe:true})
// Mesh
const mesh = new THREE.Mesh(geometry, material)
mesh.position.set(0, 0, -10);
scene.add(mesh)

// 创建相机对象Camera
// 45-75 是透视投影相机建议的视角范围fov
// 视角比例aspect w/h
// 投影近平面
// 投影远平面
const camera = new THREE.PerspectiveCamera(75, sizes.width/sizes.height, 0.1, 1000);
scene.add(camera);

const renderer = new THREE.WebGLRenderer({canvas: canvas});
renderer.render(scene, camera);

Caution! 虽设置canvas尺寸 但canvas初始化renderer时 其尺寸会受到影响 其结果仍使canvas超出父容器 出现滚动条
应使用renderer.setSize

1
2
3
const renderer = new THREE.WebGLRenderer()
renderer.setSize(container.current.clientWidth, container.current.clientHeight)
container.current.appendChild(renderer.domElement)

另外onResize要加防抖 overflow hidden该加还是得加

Controls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
...
// Add OrbitControls
const controls = new OrbitControls(camera, renderer.domElement)

// controls.update() must be called after any manual changes to the camera's transform
camera.position.set( 0, 20, 100 );
controls.update();

// camera 移动是重复的render过程 需要requestAnimationFrame
function animate() {

requestAnimationFrame( animate );

// required if controls.enableDamping or controls.autoRotate are set to true
controls.update();

renderer.render( scene, camera );
}

Mesh和Geometry

两者都可以rotate, Geometry是”是什么(what)” Mesh是”如何(how)”

Animation

1
2
3
4
5
tick(){
mesh.rotaion.y += 0.05
console.log("tick tack")
requestAnimationFrame(tick)
}

tick 调用频率是帧速率相关的 可以用Date或者THREE.Clock

1
2
3
4
5
6
7
8
9
const clock = new THREE.Clock()
const animate = () => {
const t = clock.getElapsedTime()
group.rotation.y=0.5*Math.PI*t
requestAnimationFrame(animate);
renderer.render(scene, camera);
};

animate();

Issue: 单纯调用gsap无效 需要配合requestAnimationFrame和render方法才能使模型运动生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gsap.to(group.position, {
duration:1,
delay:1,
x:20
})
gsap.to(group.position, {
duration:1,
delay:2,
x:0
})

const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};

animate();

Camera

正交相机

1
2
3
4
5
6
7
8
const camera = new THREE.OrthographicCamera(
-20*container.current.clientWidth/container.current.clientHeight, /*左边界*/
20*container.current.clientWidth/container.current.clientHeight,/*右边界*/
20,/*上边界*/
-20,/*下边界*/
0.1,
1000
)

相机环绕

1
2
3
4
5
6
7
8
9
10
11
12
 // mouse rotate
document.addEventListener('mousemove', (e)=>{
if(!container.current) return
const x = (e.x - container.current.offsetLeft)/container.current.clientWidth - 0.5;
const y = -(e.x - container.current.offsetTop)/container.current.clientHeight + 0.5;
// 圆形轨迹的坐标呈正弦/余弦往复
camera.position.z = Math.cos(x*Math.PI*10)*20
camera.position.x = Math.sin(x*Math.PI*10)*20
// 注意! 相机位置移动时 会从原目标移开 所以重新设置lookAt
camera.lookAt(scene.position)
renderer.render(scene, camera)
})

camera controls

  • OrbitControl
  • DeviceOrientationControls
  • FirstPersionControls
  • FlyControls
  • PointLockControls
  • TrackballControls

debug tool:
dat.gui (out of date) -> lil-gui
control panel
controlkit
Guify
Oui

纹理映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const loader = new THREE.TextureLoader()
const texture = loader.load('./assets/images/cover/cover-6.webp',
(txt)=>{
console.log(txt)
},
()=>{
console.log('loading')
},
(e)=>{
console.error(e)
}
)
// 设置纹理在U轴(水平方向)和V轴(垂直方向)上的重复次数。
texture.repeat.x = 2
texture.repeat.y = 2
// 设置纹理在U轴(S方向)和V轴(T方向)上的包裹模式(wrap mode)。
// THREE.MirroredRepeatWrapping接缝处镜像翻转
texture.wrapS = THREE.MirroredRepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
texture.offset.x = 0.5
// 素材逆时针旋转Π/2
texture.rotation = Math.PI * 0.5
texture.center.x = 1
texture.center.y = 1
// 放大时 线性平滑
texture.magFilter = THREE.LinearFilter
// 缩小时 取相邻两个mip层做线性平滑
texture.minFilter = THREE.LinearMipmapLinearFilter
const cubeMaterial = new THREE.MeshBasicMaterial({
// color: 0xff0000,
// wireframe:true
map: texture
})

const sphereGeometry = new THREE.SphereGeometry(5, 32, 32)
const ball = new THREE.Mesh(sphereGeometry, cubeMaterial)

material properties:

  • transparent true/false
  • opacity 透明度0~1
  • wireframe 显示线框
  • side 材质应用到外侧(THREE.FrontSide)或者内侧(THREE.BackSide)
  • flatshading

matcap

environmentMap and HDRI(High Dynamic Range Imaging 高动态范围成像)

在宏大场景中,计算周围环境在物体上的倒影是巨大的运算负担,于是聪明的图形工作者想到了将环境贴图直接贴在反光物体上的想法

在Three.js中支持将立方体环境的图像映射到物体上 常用HDRI生成立方环境贴图的免费网站:https://polyhaven.com/hdris 原HDRIHeaven

或使用blender工具

3D Text

TextBufferGeometry

facetype.js 转换.ttf字体成json
QQs: 包含中文的字体转换后出现问题,json中以‘字-坐标’作key-value,中文(甚至字母和数字)转换失败在json中显示为方框,于是输入的字无法找到映射。loader导入这样的字体会报 character “xxx” does not exists in font family XXX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';

// text
const fontLoader = new FontLoader()
fontLoader.load('./assets/font/Kristen ITC_Regular.json', (font) => {
const textGeometry = new TextGeometry(
'Hello 3D',
{
font,
size: 2,
depth:0.2, /** 原height属性 */
curveSegments: 12,
bevelEnabled: true, /**斜角 */
bevelThickness: 0.3,
bevelSize: 0.2,
bevelOffset: 0,
bevelSegments: 5
}
)

const textMaterial = new THREE.MeshBasicMaterial({ color: 'blue', wireframe:true })
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
//textMesh.position.set(0,0,0)
scene.add(textMesh)
})

```js

3D Text默认position位于文字起点 移动到中心

```js
textGeometry.translate(
- (textGeometry.boundingBox.x - 0.2)*0.5, /** 减去倒角 */
- (textGeometry.boundingBox.y - 0.2)*0.5,
- (textGeometry.boundingBox.z - 0.3)*0.5
)

阴影

雾

例如地面模型 向远处延伸 添加雾以达成模型尽头与背景融为一体的效果 可用于控制视距(线性雾)或更真实地笼罩场景(指数评分雾)

1
2
3
4
5
scene.background = new THREE.Color(0xbfd1e5); // 浅蓝色背景
// 线性雾 效果从举例10开始 线性增加 直到100完全融入背景
scene.fog = new THREE.Fog(0xbfd1e5, 10, 100);
// 指数平方雾 根据距离指数级增加
// scene.fog = new THREE.FogExp2(color, density);

雾效果的原理是在shader计算中 根据距离混合物体的颜色和雾的颜色,或许增加了物体渲染的运算 但是配合视距控制的效果 可以编写递进加载逻辑 从而优化渲染性能和体验

粒子

THREE.Points 用于绘制点云 如果每个点都是炫酷的材质 就是所谓的粒子特效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// A. 创建几何体 (BufferGeometry)
const particlesCount = 2000;
const geometry = new THREE.BufferGeometry();

// 创建位置数组 (每个点有 x, y, z 三个坐标)
const positions = new Float32Array(particlesCount * 3);

for (let i = 0; i < particlesCount * 3; i++) {
// 在 -50 到 50 的范围内随机分布
positions[i] = (Math.random() - 0.5) * 100;
}

// 将数据绑定到几何体
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

// B. 创建材质
const material = new THREE.PointsMaterial({
size: 0.5, // 粒子大小
color: 0x00ff88, // 粒子颜色
transparent: true, // 开启透明
opacity: 0.8, // 透明度
sizeAttenuation: true // 粒子是否随距离变小 (透视效果)
});

// C. 创建 Points 对象并添加到场景
const particles = new THREE.Points(geometry, material);
scene.add(particles);

多camera与离线渲染

同场景多个viewport CAD四视图

同场景 离线渲染到 RenderTarget 本质是将场景在GPU额外渲染一次 输出一个texture 这个texture可以贴在场景中的物体(比如镜子)上

官方反射镜 Reflector mirror example

物理引擎

  • Cannon.js
  • Rapier

可视化优化

Three.js常见性能问题和内存泄漏

  • 及时dispose释放资源
  • 减少segment
  • 复用geometry实例
  • 优化requestAnimationFrame 降低帧率和简化回调

webGPU

Three.js 支持webGPU,使用webGPU需要浏览器对该功能的支持 见openGL

对于没有原生支持的浏览器可以通过Polyfill实现

向量点乘

内积 数量积

$\vec{a}$·$\vec{b}$ = $x_1 x_2$ + $y_1y_2$ + $z_1z_2$= |$\vec{a}$||$\vec{b}$|cosθ

标量结果 一个向量在另一个向量方向的投影与被投影向量模的乘积

几何意义 对于单位向量 点乘结果取决于向量方向夹角 反映了方向的一致性

向量叉乘

外积 向量积

名字由来是坐标分量做交叉相乘然后相减的操作

方向垂直与向量所在平面 符合右手法则

几何意义 叉乘结果的正负可以反映方向关系
$\vec{a}$ × $\vec{b}$ > 0 ,$\vec{a}$ 在 $\vec{b}$ 右侧

$\vec{a}$ × $\vec{b}$ < 0 ,$\vec{a}$ 在 $\vec{b}$ 左侧

正交矩阵

共轭变换

特征值、特征向量

对于一个 n 阶方阵 A,如果存在一个实数 λ 和一个非零的 n 维列向量 x,使得满足等式 Ax = λx,则称:

  • λ 为矩阵 A 的一个特征值(或本征值)。
  • x 为矩阵 A 对应于特征值 λ 的一个特征向量(或本征向量)。

其几何意义在于向量x经过A变换 其方向没有改变(或者仅正负颠倒)

在空间运动中 向量x是空间变换A的旋转轴

近似解 最小二乘法

方程组过定(无精确解)时,用一个矩阵 X 去让误差平方和最小

AX ≈ B

正规方程

SVD

Godot是C++开发的基于opengl的游戏引擎,MIT开源协议,完全免费。支持一键导出到多个平台,包括主流的桌面平台(Linux、macOS、Windows)、移动平台(Android、iOS)、基于 Web 的平台以及主机平台。

Godot Docs

Token

Token 是大型语言模型处理文本的基本单位。不同模型采用不同的分词方式(分词器),当我们将一段文字输入模型时,模型会先将其拆解成 Token 序列,然后通过这些序列进行预测。

分词器的具体执行原理较为复杂。我们可以简化地将分词器视为一个字典,对于字典中存在的内容,按照字典进行切分并替换为对应的数字
Hello 你好呀 —> Hello | _ | 你好 | 呀 (character: 9, token: 4)

神经网络结构

经典的卷积神经网络(CNN)结构包括以下层:

输入层→卷积层→池化层→全连接层

例如,假设要对0-9十个数字的图片进行分类,则全连接层的输出维度通常要设置为10,与输出分类一一对应。最后通过Softmax层,程序就可以将输出转换为每个类别的概率,概率最高的类别即为预测结果

理解了CNN的输出层维度与分类任务的关系,我们也可以将这个概念迁移到对Transformer语言模型的理解上。尽管二者架构迥异,但是它们在输出层维度及分类任务的原理上仍有共性。

语言模型本质上仍然是一种预测模型,它的核心目标,就是预测给定上下文的下一token。输入层在复杂的网络中传播,最终到达输出层,并且形成 N 个维度的输出。而每个维度正对应着分词表中的一个token。

例如,如果分词表包含50000个token,那么Transformer模型的输出层将输出一个50000维的向量,向量中的每个元素代表对应token的概率。模型会选择概率最高的token作为预测的下一个token。

早期llm的词表(Vocabulary)规模非常小,对应的输出维度也很小,因此相同环境下的理论计算性能更优。但是词表中没有出现的罕见词,有可能被拆分成子词甚至字符。就如ChatGPT 4.0及以前的Vocabulary,大部分中文字符,都会被拆分为2-3个子词token。显然,小规模的Vocabulary有更低的信息密度,相同的输入输出会占据更大的序列长度,这对于输入与输出都有很大的负面影响。

以Llama2-7B模型为例,当词表从32k扩展至64k时,理论的嵌入矩阵参数量从32,000×4096增至65,536×4096,但平均序列长度缩短约30%,实际运行中的整体推理速度提升了15%。

Transformer

Transformer语言模型是基于自注意力机制构建的深度学习架构,已在自然语言处理领域引发革命性变革。
核心架构:

  • 自注意力机制:通过计算序列中每个元素与其他元素的相关性权重(Q/K/V矩阵),捕捉长距离依赖关系。相比RNN/CNN,并行计算效率提升3-8倍(论文7、10)。

  • 多头注意力:将输入切分为多个子空间并行处理,可同时学习语法、语义等不同特征维度(论文3、12)。

  • 位置编码:采用正弦函数生成绝对位置编码(论文1)或相对位置编码(论文18),解决序列顺序建模问题。

它的核心目标,就是预测给定上下文的下一token。

这意味着,Transformer语言模型每输出一个token,输入数据便在神经网络经过了一次完整的传播。最终从词表查询到token string,展示在你眼前的,无论是一个字母,一个单词,甚至一个短语,从模型的角度来看,它们都是“平等的”——从模型角度来看,它们都是一个独立的预测单元,最终对应到词表中的一个 token。

从输入角度上看,正如我们之前提到的,transformer的自注意力模型会将上下文加载到注意力矩阵中,上下文token数直接决定了矩阵的规模,由于O(n²)的时间复杂度,以及O(n²d)的空间复杂度(d为隐藏层维度),小规模词表所带来的低密度信息,可能反而削弱计算性能,增加资源消耗。

从输出角度上看,更小的词表同样意味着单位token包含的信息更少,完成同样的一句输出将会经过更多的推理次数。

本地部署运行大模型:

  • 个人调试 ollama
  • 企业常用 vllm
1
2
3
4
5
6
pip install sentence-transformers ctransformers ollama
# 如果使用 vLLM 加速
pip install vllm

# 从模型库中拉取Qwen2.5-14B-Instruct(实际为量化版本)并运行
ollama run qwen2.5:14b

Ollama 自动启动http服务 默认监听 http://localhost:11434

Ollama命令

1
2
3
ollama list
ollama show
ollama rm

如果是微调过的模型 需要先注册到list中 再run起来(待补充)

outlines 输出结构化 结构化的诊疗建议 对接传统业务

关于vllm的性能提升 pagedattention解决kv碎片化 推测解码加速decode 效率是ollama的2-3倍

另:SGlang 在多轮对话

PD分离(Prefill Decode)