news 2026/9/23 3:19:53

WebGPU+Transformers.js:在浏览器中端侧运行DeepSeek-R1蒸馏模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WebGPU+Transformers.js:在浏览器中端侧运行DeepSeek-R1蒸馏模型

聊一个我最近一直在折腾的事:在浏览器里直接跑 DeepSeek-R1 的推理。先说清楚,完整版 R1 是六百多亿参数的 MoE 模型,普通电脑那点内存连模型文件都放不下,更别说单靠浏览器去做端侧推理。但我真正跑通的是 DeepSeek 官方蒸馏出来的小尺寸版本,再配合 WebGPU 把算子丢到 GPU 上执行,叠一层 INT4 量化,最后在 Chrome 里实现了几十 token/s 的生成速度。整个项目的核心组合是 WebGPU + Transformers.js,目标很纯粹:不让数据离开本地、不用租 GPU 服务器,打开网页就能和模型对话。

这套方案适合两类人:一类是想在浏览器里做 AI Demo 但不想承担服务器成本的前端工程师,另一类是做大模型应用但被数据隐私卡住的开发者。你不需要懂底层的 CUDA 或设备端算子,只需要 JavaScript 基础,跟着文章走完就能在本地跑起一个完整的 R1 蒸馏模型推理页面。顺便提一句,我在做推理过程可视化的时候还接入了 splat.js 这套纯 JavaScript + WebGPU 的 3D 高斯泼溅处理方案,把 token 流渲染成了三维点云,效果相当酷,这部分在后面的进阶章节会展开聊。

1. 为什么非要把大模型装进浏览器

1.1 端侧推理解决的不只是省钱问题

一说起大模型,大家默认就是云端的 API 服务,一来是因为模型太大,二来是 GPU 贵。但把推理搬到浏览器端侧,我体验下来有几个实实在在的收益,不是赶时髦。

第一个是隐私。数据不出浏览器,意味着你的问题、Prompt、对话内容全部留在本地硬件上。我做过一个内部知识库问答的原型,很多材料是没脱敏的业务数据,客户明确要求不能过第三方 API。这种场景下,端侧推理几乎是唯一解,不是省不省钱的问题,而是能不能做的问题。

第二个是离线可用。只要模型文件缓存过一次,断网状态下照样能推理。去客户现场做演示、在飞机上临时调试,网络条件再差也不影响输出。有些项目干脆就是内网环境,根本没有访问外部的通道。

第三个是成本结构的变化。云端推理的账单是按 token 算的,服务一旦上线就是持续支出。端侧方案把成本变成了“一次性下载模型文件”,之后每次调用都只是本地电费。对 Demo、教学、原型验证这些小流量场景,这个账非常划算。

不过端侧也有很明显的短板:模型规模做不大、算力受硬件限制、推理速度上限有限。所以它不是要替代云端,而是在“隐私敏感 + 交互密度高 + 并发量低”这个特定区间里非常能打。

1.2 DeepSeek-R1 的现实约束:蒸馏模型加量化

DeepSeek-R1 这条线和其他大模型不太一样,官方把 R1 的推理能力蒸馏到了很多小尺寸模型里,覆盖 1.5B、7B、8B、14B 等不同参数规模。这意味着你不需要硬啃原版那个巨型 MoE,拿一个蒸馏小模型就能获得接近原版的推理风格,典型特征就是思考过程里会生成<think>标签包裹的中间推理内容。

但即使是最小的 R1 蒸馏模型,想做端侧部署也得过两关。

第一关是模型格式。浏览器里跑神经网络,需要的是 ONNX 或者 GGUF 这类通用格式,Hugging Face 上的原始 safetensors 权重不能直接用。需要先做格式转换,这一步在后面的章节详细讲。

第二关是模型大小。以 1.5B 参数量为例,FP32 权重约占 6GB 内存,浏览器页面根本扛不住。必须量化,把 FP32 压缩到 INT8 或 INT4,1.5B 模型可以压到 0.9GB 左右,7B 模型压到 4GB 左右。这个体积对现代笔记本来说就完全可以接受了。

我最终选择的是 1.5B 蒸馏版加 INT4 量化,理由是生成速度更稳定,内存占用可控,而且推理过程那种“先思考再回答”的风格保留得很好。7B 版本我也试过,能出结果但速度明显下降,适合追求质量的场景,不适合流畅交互。

2. 核心技术底座解密:WebGPU 与 Transformers.js 如何协同工作

2.1 WebGPU:把浏览器变成 GPU 的可编程前端

很多人对 WebGPU 的认知还停留在“更快的 WebGL”,其实完全不是一个层面的东西。WebGL 本质上是给 GPU 发一些画三角形盒子的指令,而 WebGPU 暴露了现代 GPU 的通用计算能力,你可以写 compute shader,让 GPU 做矩阵乘法、卷积、图算法这类通用计算任务,不再局限于渲染。

大模型推理恰好是 GPU 通用计算的高频场景。Transformer 每一层都在做矩阵乘法,而矩阵乘法天然适合 GPU 并行加速。这也是为什么浏览器里跑大模型,第一选择就是 WebGPU 而不是 WebGL 或纯 WASM。

WebGPU 还有两个很实际的工程优势。一个是内存管理,可以显式控制 buffer 的创建和释放,比 WebGL 那种隐式状态机清晰得多。另一个是 Shader 语言换成了 WGSL,类型安全性和可调试性都比 GLSL 强。虽然写起来门槛高了一些,但对于已经封装好的推理库来说,WebGPU 只是底层执行后端,使用者甚至不太需要直接接触。

2.2 Transformers.js 与 ONNX Runtime 的配合方式

Transformers.js 是 Hugging Face 官方维护的 JavaScript 版本 Transformers 库,它做的事情和 Python 端的 transformers 几乎一样:加载模型、调用 pipeline、输出结果。API 设计也保持了统一风格,比如pipeline('text-generation', modelName)这一行代码就能加载一个语言模型。

它底层真正干活的其实是 ONNX Runtime Web。这个运行时负责把 ONNX 格式的模型图翻译成可执行的算子,然后根据你在设备端的选择,决定把这些算子跑在 CPU 上(WASM 后端)还是 GPU 上(WebGPU 后端)。Transformers.js 就是中间这层适配器,让开发者不用关心 ONNX Runtime 的复杂接口。

我在代码里只需要这样声明:

import { pipeline } from '@huggingface/transformers'; const generator = await pipeline('text-generation', 'models/r1-distill-qwen-1.5b-onnx', { device: 'webgpu', dtype: 'q4', });

device: 'webgpu'告诉 Transformers.js 优先使用 WebGPU 执行计算,dtype: 'q4'告诉它在加载权重时按 4bit 量化读取。这两个参数是整个性能优化最核心的开关,后面会展开讲。

2.3 为什么不直接用 WebLLM 或者 GGUF.js

市面上其实还有几套浏览器端大模型推理方案,我也都快速评估过。最常拿来比较的是 WebLLM,它底层走的是 MLC 那套 TVM 编译路线,对硬件的针对性优化做得很深,支持更复杂的模型。

但我的项目选 Transformers.js,有几个很实际的原因。

第一是生态一致性。Transformers.js 和 Python 端的 Transformers 保持着镜像 API,训练、转换、推理全在同一个生态里,出了问题去社区提问,响应速度也快。第二是模型来源方便,Hugging Face 上有大量已经转成 ONNX 的模型可以直接复用,不用自己处理导出链。第三是中间层可控性,WebLLM 把很多底层细节封装得过死,自定义一个算子或接入第三方可视化模块反而麻烦。

我还是用一张表格对比三套方案的差异:

方案底层后端模型格式优势劣势
Transformers.jsONNX Runtime WebONNXAPI 统一、生态成熟、上手快算子覆盖不如专用引擎深
WebLLMTVM + WebGPU编译后的 MLCEngine优化激进、支持复杂模型工程链复杂、调试困难
GGUF.js + llama.cpp WASMWASM/WebGPUGGUF直接复用 llama.cpp 权重JS 封装较薄、灵活性一般

最终我选 Transformers.js 还有一个私心:它支持我后面接入 splat.js 做三维可视化,因为推理过程的中间数据可以通过回调直接拿到 JS 侧,自由度很高。

3. 模型准备阶段:选型、转换与量化

3.1 选模型的判断逻辑:参数规模与显存预算的平衡

选模型不能只盯着参数量,还要把你的硬件预算先算清楚。浏览器端侧的可用内存,很大程度上取决于机器物理内存和浏览器进程限制。Chrome 单个页面能稳定使用的内存大概在 2-4GB 之间,超过这个值很容易触发标签页崩溃。

模型体积可以粗略算:参数量乘权重位宽,再除以 8 得到字节数。

模型参数量FP32(4字节)INT8(1字节)INT4(0.5字节)
1.5B6GB1.5GB0.75GB
7B28GB7GB3.5GB
14B56GB14GB7GB

表里只是权重体积,还没算 KV Cache 和中间激活值。KV Cache 和上下文长度直接相关,上下文越长,Cache 越大。所以我给 1.5B + INT4 配了 4096 长度的上下文,内存峰值控制在 1.2GB 左右,给浏览器留足了余量。7B + INT4 虽然理论可行,但 3.5GB 权重加 KV Cache 很容易顶到 4GB 以上,我只在 16GB 内存的机器上跑通过了。

选 1.5B 还有一个重要原因:R1 蒸馏小模型在数学题、逻辑推理这类任务上保留了很强的思考能力,虽然知识覆盖不如大模型,但作为“会推理的端侧模型”已经够能打了。

3.2 把 safetensors 权重转成 ONNX

浏览器里不能直接装 safetensors 格式的权重,Transformers.js 需要 ONNX 格式。如果你的模型没有现成的 ONNX 版本,需要用 Python 环境离线转一次。

推荐用 Hugging Face 官方工具链 optimum:

pip install optimum onnx onnxruntime optimum-cli export onnx --model deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --task text-generation r1-distill-qwen-onnx/

这个命令会把模型导出到r1-distill-qwen-onnx/目录,里面会包含model.onnx和配套的分词器 JSON 文件。导出的 ONNX 模型默认是 FP32,体积比较大,还不能直接上浏览器,需要继续量化。

有一点要提醒:转换时确认任务类型,必须传--task text-generation,否则导出模型时会把注意力层和位置编码给丢掉,生成结果会完全乱掉。我第一次就踩了这个坑。

3.3 把 ONNX 模型量化到 INT4

ONNX Runtime 的量化工具链比较成熟,可以用onnxruntime.quantization做 INT8 / INT4 量化。工具库的名字有点误导,它支持的精度其实很宽,可以做到 4bit。

from onnxruntime.quantization import quantize_static, QuantType, CalibrationMethod from onnxruntime.quantization.shape_inference import quant_pre_process # 先做 shape inference,避免量化时算子不支持 quant_pre_process('r1-distill-qwen-onnx/model.onnx', 'model_preprocessed.onnx') # 用静态量化,需要准备一小段校准数据集 quantize_static( 'model_preprocessed.onnx', 'model_q4.onnx', calibration_data_reader=calibration_reader, quant_format=QuantType.QInt4, per_channel=True, activation_type=QuantType.QInt8, )

per_channel=True是一个容易被忽略的参数。逐通道量化比逐张量量化精度损失小很多,尤其是 4bit 这种低位宽,不用 per-channel 的话,输出质量会明显下降。

量化完成后,model_q4.onnx大约 0.9GB,这个文件才是真正部署到浏览器里的核心权重文件。把它和分词器的两个 JSON 文件(tokenizer.jsontokenizer_config.json)放到同一个静态目录,模型准备阶段就结束了。

4. 浏览器工程落地:从空目录到第一段输出

4.1 浏览器准备与环境检测

不是所有浏览器都支持 WebGPU。目前支持比较完整的是 Chrome / Edge 113+,Safari 18+ 也开始默认开放。Firefox 需要在 about:config 里手动开启底层开关,不建议作为主力测试环境。

建议在页面加载前先做一次能力检测:

async function checkWebGPU() { if (!navigator.gpu) { alert('当前浏览器不支持 WebGPU,请升级到最新版 Chrome 或 Edge'); return false; } try { const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice(); return true; } catch (e) { alert('WebGPU 初始化失败,请检查显卡驱动或浏览器设置'); return false; } }

这一步不能省。我遇到过不少用户打开页面后页面白屏,排查半天发现是浏览器版本太老,WebGPU 接口整个不存在。

4.2 工程初始化:Vite + Transformers.js

我用 Vite 搭了一个最轻量的页面,不需要后端框架。工程结构足够简单,适合作为后续项目的模板。

npm create vite@latest r1-browser-demo -- --template vanilla cd r1-browser-demo npm install @huggingface/transformers

主页面只需要三块内容:一个文本输入框、一个生成按钮、一个输出区域。重点是控制在按钮点击后加载模型,不要页面一打开就初始化模型,否则白屏时间太长,用户体验很差。

模型文件放在public/models/目录下,Vite 会把public里的内容原样拷贝到构建后的根目录,这样 Transformers.js 可以直接从相对路径加载模型:

import { pipeline, env } from '@huggingface/transformers'; env.allowLocalModels = true; env.localModelPath = './models/';

4.3 加载模型并生成第一段文本

核心代码如下:

let generator = null; async function loadModel() { const status = document.querySelector('#status'); status.textContent = '正在加载模型,第一次需要下载约 0.9GB 文件,请耐心等待...'; generator = await pipeline('text-generation', 'r1-distill-qwen-onnx', { dtype: 'q4', device: 'webgpu', progress_callback: (progress) => { if (progress.status === 'progress') { status.textContent = `模型加载中:${(progress.loaded / progress.total * 100).toFixed(1)}%`; } }, }); status.textContent = '模型已就绪,可以开始推理'; } async function generate() { const inputText = document.querySelector('#input').value; const output = await generator(inputText, { max_new_tokens: 512, do_sample: true, temperature: 0.7, top_p: 0.95, repetition_penalty: 1.1, }); document.querySelector('#output').textContent = output[0].generated_text; }

第一次运行时会逐个下载模型分片文件,progress_callback会实时返回下载进度。加载完成后,后面的推理就不需要网络了。

4.4 模型文件托管与 CORS 的隐藏要求

浏览器加载本地模型文件不是随便放一个路径就行的。Transformers.js 底层会发起 fetch 请求,并且需要服务器支持 Range 请求,因为模型文件拆成了多个分片并发下载。如果你用python -m http.server这种开发服务器,默认是支持 Range 的,但用某些静态文件服务器时要确认配置。

如果是生产环境,模型文件必须放在同源的静态目录里。跨域加载模型要额外配置 CORS 请求头,否则浏览器会直接拦掉分片请求。我在本地调试时图省事想从 Hugging Face CDN 直接加载模型,结果被 CORS 卡了很久,最后还是老老实实把模型文件下载到本地。

另外提醒一句:Vite 开发服务器不能直接加载node_modules里的模型文件,必须放在public目录,这个路径问题是新手最容易踩的。

5. 性能实测与调优记录

5.1 测试环境与方法

测试机器配置是一台 Windows 笔记本,CPU 是 i7-12700H,GPU 是 RTX 3060 Laptop 6GB 显存,浏览器用的 Chrome 126。模型是 1.5B INT4 量化版,上下文长度设置为 4096。

测试提示词用了一道数学题:“一个数列前两项是 1 和 1,从第三项开始每项等于前两项之和,求第 10 项。”这类题目 R1 蒸馏版会先写一段<think>推理过程再给答案。

5.2 实测数字与观察

我记录了两种运行模式的数据:

运行模式首 token 延迟平均生成速度内存峰值
WebGPU + INT4约 2 秒35-45 token/s1.1-1.3 GB
WebGPU + INT8约 3 秒20-25 token/s1.8-2.0 GB
WASM CPU + INT4约 8 秒6-9 token/s1.2 GB

对比非常明显。WebGPU 后端和 WASM 后端的差距接近 5 倍,所以如果你的浏览器支持 WebGPU,一定要选这个后端。INT4 比 INT8 快了接近一倍,质量损失在短文本生成里几乎感知不到,我最终锁定了 INT4。

RTX 3060 的 6GB 显存完全跑得动 1.5B 模型,实测过程中显存没有被占满。但如果是只有核显的轻薄本,WebGPU 会用集显执行计算,速度会明显下降,大概在 15-20 token/s 左右,还能接受。

5.3 影响生成速度的几个关键因素

实际调试下来,影响速度的因素远不止“显卡够不够好”。

第一个是上下文长度。我建议不要一次性把max_new_tokens拉太长。每生成一个新 token,整个 KV Cache 都会重新计算一次,上下文越长单步推理越慢。512 是一个交互体验不错的默认值,长文本生成建议分段处理。

第二个是 Shader 编译预热。WebGPU 第一次执行模型算子时有一个编译 WGSL Shader 的过程,会卡几秒钟。这个是在页面第一次生成时就发生,后面再推理就快了。建议在页面加载后先跑一个“预发热身”的短对话,把 Shader 编译的延迟提前消耗掉。

第三个是并发请求。Transformers.js 目前不支持同一个模型实例并发跑多个长生成任务,如果用户点了按钮后没有禁用按钮,连续触发多次生成会互相阻塞。处理方式很简单:生成期间把按钮置灰。

下面这段是我在真实测试里总结出来的推荐参数组合:

// 质量优先模式 { max_new_tokens: 1024, do_sample: true, temperature: 0.6, top_p: 0.9, repetition_penalty: 1.1, } // 速度优先模式 { max_new_tokens: 256, do_sample: false, }

do_sample: false会强制走贪心解码,输出变稳定但略机械。如果做演示、追求速度,贪心解码更稳妥,不会突然生成一个奇怪的长尾词。

6. 进阶玩法:用 splat.js 把推理过程做成 3D 可视化

6.1 可视化思路:把 token 对应到三维空间

跑通文本推理之后,我开始琢磨能不能让推理过程“看得见”。传统的做法是把注意力权重画成热力图,只能看到 Transformer 内部的 attention 分布,形式太平面了。我想做一个更直观的版本:把生成过程中每个 token 的隐藏状态抽取出来,映射到三维空间,用点云的形式动态展示模型“思路”的变化轨迹。

具体做法不复杂。在 Transformers.js 的生成回调里,把每个 token 的 logits 或者 hidden state 推到一个数组里:

const embeddings = []; await generator(inputText, { max_new_tokens: 128, callback_function: (beams) => { const currentToken = beams[0].output_token_ids.at(-1); const embedding = beams[0].token_embeddings?.at(-1); if (embedding) { embeddings.push(embedding); } }, });

拿到每个 token 的 embedding 后,用 PCA 或者 UMAP 降维到三维坐标。浏览器端直接跑 UMAP 比较吃力,PCA 是线性的,计算量小,对 Demo 完全够用。

6.2 splat.js:纯 JS + WebGPU 的 3D 高斯泼溅处理方案

点云拿到手之后,渲染是另一个问题。普通点的渲染是 Billboard 粒子,视觉比较单调。这里我用了最近社区里很火的高斯泼溅方案 splat.js。

splat.js 是一个纯粹的 JavaScript + WebGPU 实现的 3D 高斯泼溅渲染器,它的核心能力是把空间中的一组带位置、颜色和协方差信息的高斯点,通过 WebGPU 管线实时渲染出来。和传统点云相比,高斯泼溅的效果是“一团模糊光晕”而不是“一个硬点”,用于展示 token 之间的聚集关系特别合适。token 在语义上相近,它们在三维空间里就聚成一片光晕,语义跳转时就是一个大跨度位移,视觉上非常直观。

引入方式非常轻量:

import splat from './splat.js'; const scene = new splat.Scene(renderer.adapter); const cloud = new splat.GaussianCloud(pointData); scene.add(cloud); // 每一帧更新点云数据 cloud.updatePositions(embeddings);

embeddings就是前面收集的 token 向量降维后的三维坐标。使用 splat.js 的时候不需要关心 WebGPU 的内部实现,传坐标数组进去就行,它会自动在渲染循环里更新点云。

6.3 工程落地与避坑

把 splat.js 接入演示页后,实际效果确实惊艳,但也踩了几个坑。

第一个坑是数据规模控制。如果每生成一个 token 就渲染一个高斯点,上下文一长点云数量就爆炸。我把策略改成每 4 个 token 合并成一个关键帧点,只展示“思路关键节点”,这样可以保持 60 帧流畅。

第二个坑是主线程开销。WebGPU 推理和 splat.js 渲染都在争 GPU 资源,如果同一时间既在生成 token 又在渲染动画,会出现明显卡顿。我的处理是:生成过程中暂停动画,等一批 token 生成完毕后一次性更新点云。这样虽然少了实时感,但整体流畅度好很多。

第三个坑是三维场景的语义误导。点云里的“距离”是词向量空间的距离,不是文本行数。刚开始我把点云纵向坐标当作 token 的序号,结果生成顺序在视觉上变成了一条斜线,很容易误导观众。后来去掉了坐标轴,让点云纯粹展示语义分布,反而清晰得多。

这个可视化方案特别适合做技术分享的演示素材,也比干巴巴的控制台输出有感染力。如果你原来对 splat.js 的认知就是三维场景重建,可以多关注一下它在数据可视化上的潜力,高斯泼溅不是只能渲染照片级场景。

7. 现场踩坑实录:九个常见问题与排查方法

这一节是我花时间最多的地方。把浏览器端跑大模型的坑按出现频率列出来,并附上解决思路,能帮你少走不少弯路。

现象根因排查与解决
页面提示 WebGPU 不可用浏览器版本太旧或硬件不支持升级 Chrome/Edge 到 113+;检查 GPU 驱动
模型加载卡在 1% 或 0%文件下载请求被 CORS 拦截确认模型文件和页面同源;检查静态服务器 Range 支持
加载模型时内存直接爆掉量化参数没传对,模型按 FP32 加载检查 dtype 参数是否真正生效,看网络面板下载量
生成速度极慢,每秒不到 10 token回退到了 WASM/CPU 后端打开 DevTools Console 看 device 信息;确认 adapter 支持 WebGPU
输出内容完全乱码转换模型时少了 task 参数用 optimum 重新导出,带--task text-generation
WebGPU 初始化成功但推理崩溃WGSL Shader 编译内存溢出降低上下文长度;关闭其他标签页释放 GPU 内存
文本重复、生成死循环温度太高或没有重复惩罚设置 temperature 0.6-0.7,repetition_penalty 1.1
页面在 Chrome 正常,Safari 白屏Safari 版本或 WebGPU 实现差异检查 Safari 版本;暂时只用 Chrome/Edge 做演示
首次推理卡顿 5-10 秒Shader 编译预热页面加载后先跑一段短文本预热,把缓冲时间提前

7.1 隐蔽的问题:模型路径和缓存策略

除了表格里的常见问题,还有一个容易被忽略的点:浏览器的 HTTP 缓存策略。模型文件每个分片有几十 MB,如果服务器返回的缓存头不正确,用户每次刷新页面都要重新下载全部文件。

我最终在生产环境里给模型文件加了Cache-Control: max-age=31536000, immutable响应头。模型权重不会频繁更新,一年缓存可以接受。开发环境则保持默认不缓存,方便及时替换新权重。

如果你用的是public目录里的文件,Vite 在构建时不会重命名模型文件,这反而是好事,因为浏览器可以按原始 URL 命中缓存。不要用那种带 content hash 的文件名,模型分片之间互相引用旧 URL 会出问题。

7.2 关于 token 器和特殊符号的一点提示

R1 系列模型的分词器里有<think>这种特殊 token,属于内容的一部分。Transformers.js 加载模型时会自动识别分词器 JSON 里的特殊 token 配置,不需要手动处理。但如果你用的是自己转换的模型,要检查tokenizer_config.json里是否把<think>正确声明为 special token,否则生成时会被拆成几个普通 token,输出格式会很奇怪。

我遇到过一种更隐蔽的问题:<think>标签在正常推理中会输出,但在某些浏览器私密模式下,特殊 token 的显示样式会和普通文本混在一起,看不出来模型是否进入了思考状态。建议在 UI 层面对<think></think>做一次样式解析,让思考状态可视化,这个细节对演示非常有帮助。

最后分享一个小技巧

整个项目做下来,我最大的体会是:浏览器端跑大模型并没有想象中那么遥远,但也没有网上某些演示看起来那么轻松,真正的门槛在工程整合,不在单点技术。WebGPU 把算力门槛降低了,Transformers.js 把模型加载门槛降低了,量化把体积门槛降低了,三个东西叠在一起,才让“R1 放进浏览器”变成了一件可重复落地的事。

如果你准备自己动手,我建议按照“模型量化 → 页面跑通 → 性能调优 → 可视化增强”的顺序推进。不要一开始就纠结要不要上 splat.js 这种高阶玩法,先把最朴素的文本生成跑通,再逐步往上加东西。

还有一个小技巧:调试 WebGPU 后端时,打开 Chrome 的chrome://gpu,先确认 WebGPU 已经被启用,再确认显卡不是被强制切到了软件渲染。很多“生成太慢”的案例,最后查出来是浏览器把 GPU 禁用,代码再优化也没用。希望这个项目也能帮你在浏览器里跑出一条真正属于你自己的 R1 推理链路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 3:19:50

动态换肤组件化实战:基于CSS变量的主题Token设计与ThemeProvider实现

这年头做应用&#xff0c;不做个“白天摸鱼晚上蹦迪”的暗色模式&#xff0c;都不好意思说自己搞过前端。但你说做个换肤吧&#xff0c;需求方拿着截图跟你说“就按这个色儿来”&#xff0c;然后第二天又换成另一个色儿&#xff0c;这时候你要是还给每个页面写死颜色样式&#…

作者头像 李华
网站建设 2026/9/23 3:14:10

Aspire + Azure Web PubSub 构建高并发群聊系统

1. 项目概述&#xff1a;为什么一个“群聊”值得用 Aspire Azure Web PubSub 重做一遍&#xff1f;我去年在给一家在线教育平台做实时互动模块时&#xff0c;被逼着把 WebSocket 服务从 SignalR 换成 Azure Web PubSub。当时心里是抵触的——不就是发个消息、推个通知&#xf…

作者头像 李华
网站建设 2026/9/23 3:13:23

OpenClaw的tools与skills详解:用TaoToken统一Key跑通配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/23 3:13:15

Excel INT函数详解:从基础取整到高阶应用

1. INT函数基础认知&#xff1a;从数学定义到Excel实现INT函数作为Excel中最基础的数学函数之一&#xff0c;其官方定义是"将数字向下舍入到最接近的整数"。这个看似简单的定义背后&#xff0c;其实蕴含着严谨的数学逻辑和实用的数据处理哲学。在数学层面&#xff0c…

作者头像 李华
网站建设 2026/9/23 3:13:13

Blender新手入门:清空文件、网格与材质操作详解

说实话&#xff0c;我第一次跟着 oeasy 的 Blender 系列学习时&#xff0c;看到第 015 期的标题“清空文件_网格_mesh_材质_material”是有过一点困惑的&#xff1a;这三个词放在一块儿&#xff0c;像是不相关的碎片。结果等我把这一期完整过了一遍&#xff0c;才意识到它是给新…

作者头像 李华
网站建设 2026/9/23 3:12:03

2026年无代码平台TOP10选型指南:从数据模型到AI能力,避开五大坑

只列榜单不给标准的盘点文&#xff0c;这两年都快把读者坑出习惯了。我前前后后做过十几个无代码平台的选型&#xff0c;有给客户做的&#xff0c;也有自己团队内部用的。2026年这个时间点特别有意思&#xff1a;无代码平台的数量比三年前翻了几倍&#xff0c;可真正能接手正经…

作者头像 李华