<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Flyingon的博客</title>
    <description>记录软件开发工程师的成长
</description>
    <link>https://blog.yuanzhaoyi.cn/</link>
    <atom:link href="https://blog.yuanzhaoyi.cn/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 08 May 2026 11:52:15 +0800</pubDate>
    <lastBuildDate>Fri, 08 May 2026 11:52:15 +0800</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>LiteRT-LM 在 iOS 上跑 Gemma 4：一次从 GPU 到回滚的工程复盘</title>
        <description>&lt;p&gt;这篇记录一次很具体的工程排查：在 iOS App 里集成 LiteRT-LM，跑 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemma-4-E2B-it.litertlm&lt;/code&gt; 做本地 Vision LLM。目标不是证明某条路线一定正确，而是把当天踩到的坑、判断依据和最后保留下来的实现写清楚。&lt;/p&gt;

&lt;p&gt;结论先放前面：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当前公开 LiteRT-LM iOS 路线下，&lt;strong&gt;Gemma 4 可以作为本地模型继续保留&lt;/strong&gt;。&lt;/li&gt;
  &lt;li&gt;稳定配置是 &lt;strong&gt;Main executor 走 GPU，Vision executor 走 CPU&lt;/strong&gt;。&lt;/li&gt;
  &lt;li&gt;不要把 Vision executor 强行切到 GPU；当前会在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STABLEHLO_COMPOSITE&lt;/code&gt; prepare、Metal texture 分配或内存压力处失败。&lt;/li&gt;
  &lt;li&gt;不切 Gemma-3n。Gemma-3n 的 Vision 子图要求 GPU，但公开 runtime 路径下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vision=gpu&lt;/code&gt; 创建 engine 失败，没有可用 CPU fallback。&lt;/li&gt;
  &lt;li&gt;最终代码回到单模型 Gemma 4 路线，并保留下载、多源 fallback、运行时动态加载、耗时日志和降内存参数。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;一背景为什么选-litert-lm-和-gemma-4&quot;&gt;一、背景：为什么选 LiteRT-LM 和 Gemma 4&lt;/h2&gt;

&lt;p&gt;AppScanFlow 原本已经有一条稳定的识别链路：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Vision OCR
  -&amp;gt; 规则分类
  -&amp;gt; 字段提取
  -&amp;gt; 摘要
  -&amp;gt; 保存结果
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这条链路的优点是稳定、轻、快；缺点也明显：遇到非标准票据、跨语言字段、版式复杂图片时，需要大量规则兜底。&lt;/p&gt;

&lt;p&gt;所以本地 LLM 的目标不是替代所有逻辑，而是增加两种可选能力：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;模式&lt;/th&gt;
      &lt;th&gt;作用&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;OCR + Text LLM&lt;/td&gt;
      &lt;td&gt;先 OCR，再让本地 LLM 做分类、字段复核、摘要&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Vision LLM&lt;/td&gt;
      &lt;td&gt;直接把图片交给本地多模态模型，返回结构化 JSON&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;模型侧选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemma-4-E2B-it.litertlm&lt;/code&gt; 的原因很现实：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.litertlm&lt;/code&gt; 模型约 2.59GB，移动端还能接受。&lt;/li&gt;
  &lt;li&gt;GGUF 视觉路线通常还需要主模型 + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mmproj&lt;/code&gt;，体积更大，当前不适合作为默认 App 内模型。&lt;/li&gt;
  &lt;li&gt;LiteRT-LM 是 Google 官方边缘端 LLM runtime，理论上和 Gemma 4 edge variants 配套。&lt;/li&gt;
  &lt;li&gt;Google AI Edge Gallery 宣传里已经把 Gemma 4 E2B/E4B 放到了移动端场景里。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是“Gallery 能跑”不等于“第三方 iOS App 用公开 dylib/C API 路线也能同样跑”。这正是这次排查的核心。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;二最初现象gpu-不像想象中那么简单&quot;&gt;二、最初现象：GPU 不像想象中那么简单&lt;/h2&gt;

&lt;p&gt;真机环境：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Device: iPhone14,3
iOS: 26.3.1
Xcode: 26.4
Model: gemma-4-E2B-it.litertlm
Runtime: LiteRT-LM v0.11.x C API bridge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一开始最自然的想法是：既然要提速，那就把 GPU 开起来。&lt;/p&gt;

&lt;p&gt;LiteRT-LM 的 engine settings 里有两个 backend 概念：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;main backend
vision backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说，Gemma 4 多模态模型不是一个整体 backend 开关，而是至少拆成两段：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Main executor：文本主模型、prefill/decode。&lt;/li&gt;
  &lt;li&gt;Vision executor：图像 encoder / adapter / patch 处理。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;试下来以后，真正稳定的是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;main=gpu
vision=cpu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而不是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;main=gpu
vision=gpu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vision GPU 路线的典型失败日志是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Node number 905 (STABLEHLO_COMPOSITE) failed to prepare.
ERROR: Node number 905 (STABLEHLO_COMPOSITE) failed to prepare.
RunPrefillAsync status: INTERNAL:
  runtime/executor/vision_litert_compiled_model_executor.cc
  external/litert/litert/cc/litert_compiled_model.cc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后来在更高 token、patch 或 speculative decoding 配置下，还遇到 iOS 直接杀进程：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The process has been terminated by the operating system because it is using too much memory.
Domain: IDEDebugSessionErrorDomain
Code: 11
Failure Reason: Debug session ended with code 9: Terminated due to memory issue.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个时候就能把问题分成两类：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;问题&lt;/th&gt;
      &lt;th&gt;本质&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STABLEHLO_COMPOSITE failed to prepare&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;图编译 / delegate prepare 阶段失败，还没真正开始识别&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;iOS memory issue / jetsam&lt;/td&gt;
      &lt;td&gt;模型、vision patch、KV cache、Metal resource 累积超过系统可承受范围&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;三stablehlo_composite-prepare-失败到底是什么&quot;&gt;三、STABLEHLO_COMPOSITE prepare 失败到底是什么&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STABLEHLO_COMPOSITE&lt;/code&gt; 不是普通业务报错，也不是 JSON 解析问题。&lt;/p&gt;

&lt;p&gt;它大致发生在这条链路：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.litertlm 模型
  -&amp;gt; LiteRT-LM 拆出 vision encoder / adapter / main decoder
  -&amp;gt; LiteRT 创建 vision compiled model
  -&amp;gt; GPU delegate 尝试 prepare 某个 StableHLO composite 节点
  -&amp;gt; Metal backend 无法完成 lowering / kernel binding / tensor allocation
  -&amp;gt; engine 创建或 image prefill 失败
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以它的本质是：&lt;strong&gt;模型里的 Vision 子图和当前 iOS LiteRT GPU delegate 能力不匹配&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;从 App 层能调的参数其实很有限：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;降低输入图像尺寸。&lt;/li&gt;
  &lt;li&gt;降低 vision patch 数。&lt;/li&gt;
  &lt;li&gt;降低输出 token 数。&lt;/li&gt;
  &lt;li&gt;关闭 speculative decoding / MTP。&lt;/li&gt;
  &lt;li&gt;避免先尝试失败的 GPU Vision 路线，防止失败后内存资源更糟。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但如果某个 composite op 没有被当前 Metal delegate 支持，或者官方 Gallery 用了我们公开包里没有的 kernel / accelerator / 静态链接路径，那 App 层没有办法真正修复。&lt;/p&gt;

&lt;p&gt;底层要修，大概只有三条路：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;补 LiteRT Metal backend&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;dump 出失败节点对应的 composite op。&lt;/li&gt;
      &lt;li&gt;在 LiteRT GPU delegate 里补 lowering 或 kernel。&lt;/li&gt;
      &lt;li&gt;重新构建 iOS runtime。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;换成和 Gallery 一致的 runtime artifact&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Gallery 可能静态链接了 Metal accelerator。&lt;/li&gt;
      &lt;li&gt;也可能用了不同版本的 runtime、模型包、allowlist 或编译缓存。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;重新转换模型&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;让 Vision 子图避开当前 public runtime 不支持的 composite。&lt;/li&gt;
      &lt;li&gt;这条最重，而且需要官方转换链路足够透明。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;四为什么试过-gemma-3n最后又放弃&quot;&gt;四、为什么试过 Gemma-3n，最后又放弃&lt;/h2&gt;

&lt;p&gt;中间尝试过一个方案 B：保留 Gemma 4 的现状，同时新增 Gemma-3n，看看它是不是更接近 iOS Gallery 的公开 allowlist。&lt;/p&gt;

&lt;p&gt;这个方向很快也被证伪了。&lt;/p&gt;

&lt;p&gt;Gemma-3n 的 Vision 子图明确要求 GPU：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;INVALID_ARGUMENT: Vision backend constraint mismatch.
Model requires one of [gpu] but Vision backend is CPU
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是说它不像 Gemma 4 那样可以走：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;main=gpu
vision=cpu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对 Gemma-3n 来说，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vision=cpu&lt;/code&gt; 不是慢一点，而是模型 metadata 直接拒绝。&lt;/p&gt;

&lt;p&gt;可是切到：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;main=gpu
vision=gpu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;又会失败：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Node number 32 (STABLEHLO_COMPOSITE) failed to prepare.
Failed to create engine:
  runtime/executor/vision_litert_compiled_model_executor.cc
  external/litert/litert/cc/litert_compiled_model.h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这说明 Gemma-3n 在当前公开 iOS runtime 路径下没有有效 fallback：Vision CPU 不允许，Vision GPU 又 prepare 失败。&lt;/p&gt;

&lt;p&gt;所以最后回滚这个分支是正确选择。工程上最怕“为了验证官方说法，把主线拖成一团泥”。Gemma-3n 这条路可以记为证据，但不应该进入产品实现。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;五最终保留的实现&quot;&gt;五、最终保留的实现&lt;/h2&gt;

&lt;p&gt;最后保留下来的实现很克制：&lt;strong&gt;只支持 Gemma-4-E2B-it，且只让 Main executor 使用 GPU&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;51-模型管理&quot;&gt;5.1 模型管理&lt;/h3&gt;

&lt;p&gt;当前模型目录只保留一个模型：&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gemma4E2B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;LocalAIModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gemma-4-E2B-it&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Gemma-4-E2B-it&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gemma-4-E2B-it.litertlm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;downloadURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;mirrorDownloadURLs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://hf-mirror.com/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;expectedSizeBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2_588_147_712&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下载保留多源 fallback：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hugging Face official repo
  -&amp;gt; hf-mirror
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;并保留基本完整性校验，避免下载到半截文件后直接进入运行时。&lt;/p&gt;

&lt;h3 id=&quot;52-runtime-bridge&quot;&gt;5.2 Runtime bridge&lt;/h3&gt;

&lt;p&gt;Swift 侧不依赖尚未稳定的 Swift SDK，而是走 C API bridge：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Swift
  -&amp;gt; LiteRTGemmaRuntime
  -&amp;gt; LiteRTLMRuntimeBridge
  -&amp;gt; dlopen / dlsym
  -&amp;gt; libLiteRtLm.dylib
  -&amp;gt; libLiteRt.dylib / Metal accelerator / TopK sampler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;动态加载时会先加载 Gemma constraint provider：&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;libGemmaModelConstraintProvider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dylib&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;libLiteRtLm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dylib&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其他 accelerator / sampler dylib 交给 LiteRT-LM 自己按名称加载，不在 Swift 侧手动乱 preload，避免 Objective-C duplicate class warning 和插件注册顺序问题。&lt;/p&gt;

&lt;h3 id=&quot;53-backend-策略&quot;&gt;5.3 Backend 策略&lt;/h3&gt;

&lt;p&gt;最终策略非常明确：&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;selectedBackendPlan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BackendPlan&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;settings.models.mainGPUAcceleration&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;isMainGPUEnabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BackendPlan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isMainGPUEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpu&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cpu&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;vision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cpu&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也就是：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;设置&lt;/th&gt;
      &lt;th&gt;main&lt;/th&gt;
      &lt;th&gt;vision&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Main GPU 开&lt;/td&gt;
      &lt;td&gt;GPU&lt;/td&gt;
      &lt;td&gt;CPU&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Main GPU 关&lt;/td&gt;
      &lt;td&gt;CPU&lt;/td&gt;
      &lt;td&gt;CPU&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;不再尝试 Vision GPU。&lt;/p&gt;

&lt;h3 id=&quot;54-内存控制&quot;&gt;5.4 内存控制&lt;/h3&gt;

&lt;p&gt;Vision LLM 入口先压图：&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;maxVisionInputDimension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;640&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;maxVisionPatches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;768&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;maxVisionOutputTokens&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1536&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;生成时写临时 JPEG，quality 为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.88&lt;/code&gt;。这样能控制 vision patch 数和输入 tensor 压力。&lt;/p&gt;

&lt;p&gt;Engine settings 里也做了限制：&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settingsSetMaxTokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVision&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3072&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settingsSetMaxImages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVision&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settingsSetSpeculativeDecoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;speculative decoding&lt;/code&gt; / MTP 理论上能提速，但当天真机验证发现它会扩大内存压力。当前先关掉，等上游 runtime 更稳定后再单独测。&lt;/p&gt;

&lt;h3 id=&quot;55-耗时日志&quot;&gt;5.5 耗时日志&lt;/h3&gt;

&lt;p&gt;为了判断 GPU 到底有没有收益，最终实现里加了关键阶段耗时：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[LiteRT-LM][time] engine settings=...
[LiteRT-LM][time] engine create=...
[LiteRT-LM][time] text generateContent elapsed=...
[LiteRT-LM][time] vision temp image write elapsed=...
[LiteRT-LM][time] vision sendMessage elapsed=...
[LiteRT-LM][time] vision total elapsed=...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个比“感觉快不快”可靠很多。尤其在移动端，首轮 engine 创建、模型 mmap、Metal shader 初始化、prefill、decode 是完全不同的性能阶段，混在一起看很容易误判。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;六返回-json-不完整的问题&quot;&gt;六、返回 JSON 不完整的问题&lt;/h2&gt;

&lt;p&gt;有一次 CPU 路径返回了半截 JSON：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;documentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;invoice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;detectedCategory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;financial_transaction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;categoryConfidence&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Transaction Record&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;language&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ko&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fields&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Merchant&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;신용카드&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.90&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Buyer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;va
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这不是“模型完全识别错了”，更像是输出被截断。常见原因有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxOutputTokens&lt;/code&gt; 太小。&lt;/li&gt;
  &lt;li&gt;prompt 要求 JSON 太长。&lt;/li&gt;
  &lt;li&gt;模型在低速 CPU 路径上被提前取消。&lt;/li&gt;
  &lt;li&gt;session / conversation API 返回内容没有完整拼接。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此错误信息里保留 raw response prefix 是必要的。它能区分：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;现象&lt;/th&gt;
      &lt;th&gt;判断&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;完全空&lt;/td&gt;
      &lt;td&gt;runtime / conversation 失败&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;半截 JSON&lt;/td&gt;
      &lt;td&gt;token 限制、截断或取消&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;非 JSON 文本&lt;/td&gt;
      &lt;td&gt;prompt / sampling / 模型服从性问题&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;字段错但 JSON 完整&lt;/td&gt;
      &lt;td&gt;识别质量问题&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;七这次搜索到值得关注的官方-issue&quot;&gt;七、这次搜索到值得关注的官方 issue&lt;/h2&gt;

&lt;p&gt;后续如果继续跟 LiteRT-LM iOS / Gemma 4 GPU 路线，最值得关注这几个：&lt;/p&gt;

&lt;h3 id=&quot;71-gallery-692&quot;&gt;7.1 Gallery #692&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/google-ai-edge/gallery/issues/692&quot;&gt;google-ai-edge/gallery #692&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个 issue 和当前问题最贴近：Gemma 4 LiteRT-LM 在 iOS 第三方 App 里初始化失败，而且 public iOS allowlist 没列 Gemma 4。&lt;/p&gt;

&lt;p&gt;它问的其实就是我们最关心的问题：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Gemma 4 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.litertlm&lt;/code&gt; 在 iOS 第三方 App 里到底是不是官方支持？如果支持，公开 runtime/API 路径是什么？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;72-litert-6745&quot;&gt;7.2 LiteRT #6745&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/google-ai-edge/LiteRT/issues/6745&quot;&gt;google-ai-edge/LiteRT #6745&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个 issue 提到 iOS arm64 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libLiteRtMetalAccelerator.dylib&lt;/code&gt; 在 prebuilt 包里的问题，还指出 Gallery iOS 可能把 Metal accelerator 静态链接进了 binary。&lt;/p&gt;

&lt;p&gt;这和我们的判断高度一致：&lt;strong&gt;Gallery 能跑，不代表公开 dylib 路线完整等价&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;73-litert-lm-2151&quot;&gt;7.3 LiteRT-LM #2151&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/google-ai-edge/LiteRT-LM/issues/2151&quot;&gt;google-ai-edge/LiteRT-LM #2151&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个关注 iOS Mach-O bundling、companion dylibs、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dlopen&lt;/code&gt; by basename 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.framework&lt;/code&gt; 打包问题。只要官方后续修 public iOS dylib 路线，这类 issue 很可能会出现相关信号。&lt;/p&gt;

&lt;h3 id=&quot;74-litert-lm-2154&quot;&gt;7.4 LiteRT-LM #2154&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/google-ai-edge/LiteRT-LM/issues/2154&quot;&gt;google-ai-edge/LiteRT-LM #2154&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个要求官方提供 public shared-library build target。当前 C API + dylib wrapper 路线，本质上也卡在这里。&lt;/p&gt;

&lt;h3 id=&quot;75-litert-lm-2187&quot;&gt;7.5 LiteRT-LM #2187&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/google-ai-edge/LiteRT-LM/issues/2187&quot;&gt;google-ai-edge/LiteRT-LM #2187&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这是 Gemma-4-E2B vision 部分性能慢的问题，不是 iOS，但可以观察官方怎么解释 vision pipeline 性能、backend 支持和调优方向。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;八工程上的判断&quot;&gt;八、工程上的判断&lt;/h2&gt;

&lt;p&gt;这一天最大的收获不是“GPU 能不能开”，而是几个边界判断。&lt;/p&gt;

&lt;h3 id=&quot;81-不要把模型支持理解成-app-集成支持&quot;&gt;8.1 不要把模型支持理解成 App 集成支持&lt;/h3&gt;

&lt;p&gt;官方说 Gemma 4 支持移动端，可能指的是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Gallery App 支持。&lt;/li&gt;
  &lt;li&gt;Android Kotlin 路线支持。&lt;/li&gt;
  &lt;li&gt;特定 prebuilt / static linked runtime 支持。&lt;/li&gt;
  &lt;li&gt;特定设备和系统版本支持。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但我们真正需要的是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;第三方 iOS App
  + public LiteRT-LM C API
  + public iOS arm64 dylibs
  + App Store 可打包方式
  + Gemma-4-E2B-it.litertlm
  + Vision LLM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是一条更窄、更具体的路线。只要其中一个组件不完整，就会出现“官方 demo 很快，我们这里很慢或跑不起来”的落差。&lt;/p&gt;

&lt;h3 id=&quot;82-gpu-不一定整体更快&quot;&gt;8.2 GPU 不一定整体更快&lt;/h3&gt;

&lt;p&gt;移动端 LLM 不是简单的“开 GPU = 快”。&lt;/p&gt;

&lt;p&gt;GPU 可能加速 decode，但也可能增加：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;engine 初始化成本&lt;/li&gt;
  &lt;li&gt;shader 编译成本&lt;/li&gt;
  &lt;li&gt;tensor upload 成本&lt;/li&gt;
  &lt;li&gt;Metal resource 压力&lt;/li&gt;
  &lt;li&gt;Vision encoder prepare 风险&lt;/li&gt;
  &lt;li&gt;内存峰值&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以必须分阶段打点：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;image prepare
engine create
prefill
first token
decode
total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只看总耗时，很难判断到底哪一段在拖后腿。&lt;/p&gt;

&lt;h3 id=&quot;83-fallback-要尊重模型-metadata&quot;&gt;8.3 fallback 要尊重模型 metadata&lt;/h3&gt;

&lt;p&gt;Gemma 4 可以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vision=cpu&lt;/code&gt;，Gemma-3n 不行。后者 metadata 明确要求 Vision backend 是 GPU。&lt;/p&gt;

&lt;p&gt;因此 fallback 不是 App 想怎么 fallback 就怎么 fallback，而要看模型包里的 backend constraint。&lt;/p&gt;

&lt;h3 id=&quot;84-先保主线再保探索&quot;&gt;8.4 先保主线，再保探索&lt;/h3&gt;

&lt;p&gt;Gemma-3n 双模型方案作为探索是有价值的，但不应该为了探索把主线复杂化。&lt;/p&gt;

&lt;p&gt;最终回到：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gemma 4 单模型
main GPU 可开关
vision 固定 CPU
Vision LLM 限制输入和输出
保留耗时日志
保留 OCR 默认兜底
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是当前最稳的工程点。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;九后续策略&quot;&gt;九、后续策略&lt;/h2&gt;

&lt;p&gt;短期不再继续硬怼 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vision=gpu&lt;/code&gt;。继续做三件事：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;保留 Gemma 4 当前实现&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;单模型。&lt;/li&gt;
      &lt;li&gt;Main GPU 可开关。&lt;/li&gt;
      &lt;li&gt;Vision CPU。&lt;/li&gt;
      &lt;li&gt;降低 patch / token / speculative decoding 内存风险。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;关注官方 issue&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;特别是 Gallery #692 和 LiteRT #6745。&lt;/li&gt;
      &lt;li&gt;等官方明确 Gemma 4 iOS public runtime 支持路径。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;如果要继续底层排查，先准备 upstream issue&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;设备型号、iOS、Xcode、LiteRT-LM tag。&lt;/li&gt;
      &lt;li&gt;模型 URL 和 size。&lt;/li&gt;
      &lt;li&gt;backend 配置。&lt;/li&gt;
      &lt;li&gt;完整 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STABLEHLO_COMPOSITE failed to prepare&lt;/code&gt; 日志。&lt;/li&gt;
      &lt;li&gt;说明 Gallery 能跑但 public dylib route 不等价。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在官方给出更清晰的 iOS runtime 之前，当前实现已经足够务实：能用、能测、能回退，也不会把 App 绑死在一个不稳定的 GPU Vision 假设上。&lt;/p&gt;

</description>
        <pubDate>Fri, 08 May 2026 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2026/05/08/litert-lm-gemma4-ios.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2026/05/08/litert-lm-gemma4-ios.html</guid>
        
        <category>LiteRT-LM</category>
        
        <category>Gemma 4</category>
        
        <category>iOS</category>
        
        <category>GPU</category>
        
        <category>本地大模型</category>
        
        
        <category>AI 工程化</category>
        
      </item>
    
      <item>
        <title>Transformer 架构：从零开始理解每一步</title>
        <description>&lt;p&gt;本文用一张架构图把 Transformer 的每一步拆开讲清楚，适合没有深度学习基础的读者。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ai/transformer_architecture.png&quot; alt=&quot;Transformer 架构全貌&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;一全局视角transformer-在做什么&quot;&gt;一、全局视角：Transformer 在做什么&lt;/h2&gt;

&lt;p&gt;一句话：&lt;strong&gt;把一段输入序列变成一段输出序列&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;翻译任务举例：输入「I love you」→ 输出「我爱你」。Transformer 通过 &lt;strong&gt;编码器（Encoder）&lt;/strong&gt; 理解输入，再通过 &lt;strong&gt;解码器（Decoder）&lt;/strong&gt; 逐步生成输出。&lt;/p&gt;

&lt;p&gt;整个流程可以拆成四大块：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;阶段&lt;/th&gt;
      &lt;th&gt;做什么&lt;/th&gt;
      &lt;th&gt;对应图中位置&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;输入处理&lt;/td&gt;
      &lt;td&gt;文本 → 数字向量&lt;/td&gt;
      &lt;td&gt;左侧绿色区域&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;编码器&lt;/td&gt;
      &lt;td&gt;理解输入的完整语义&lt;/td&gt;
      &lt;td&gt;上方红色框&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;解码器&lt;/td&gt;
      &lt;td&gt;逐个生成输出 token&lt;/td&gt;
      &lt;td&gt;下方粉色框&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;输出预测&lt;/td&gt;
      &lt;td&gt;向量 → 概率 → 选词&lt;/td&gt;
      &lt;td&gt;右下角黄/蓝色框&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;二输入处理让机器读懂文字&quot;&gt;二、输入处理：让机器「读懂」文字&lt;/h2&gt;

&lt;h3 id=&quot;21-分词--token-化&quot;&gt;2.1 分词 / Token 化&lt;/h3&gt;

&lt;p&gt;机器不认识汉字和单词，需要先把文本切成最小单元——&lt;strong&gt;token&lt;/strong&gt;。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;I love you&quot; → [&quot;I&quot;, &quot;love&quot;, &quot;you&quot;]
&quot;我爱你&quot;     → [&quot;我&quot;, &quot;爱&quot;, &quot;你&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;每个 token 对应词表里的一个编号（整数），比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;love → 3421&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;22-词嵌入embedding&quot;&gt;2.2 词嵌入（Embedding）&lt;/h3&gt;

&lt;p&gt;一个整数编号信息量太少。词嵌入把每个编号映射成一个&lt;strong&gt;高维向量&lt;/strong&gt;（比如 512 维的浮点数组），让语义相近的词在向量空间里距离更近。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;love&quot; → [0.12, -0.34, 0.56, ...]   (512 个数)
&quot;like&quot; → [0.11, -0.31, 0.55, ...]   ← 和 love 很接近
&quot;car&quot;  → [-0.78, 0.22, 0.01, ...]   ← 和 love 差很远
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个映射表是模型&lt;strong&gt;训练出来的&lt;/strong&gt;，不是人工设定的。&lt;/p&gt;

&lt;h3 id=&quot;23-位置编码positional-encoding&quot;&gt;2.3 位置编码（Positional Encoding）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;为什么需要？&lt;/strong&gt; Transformer 不像 RNN 那样逐个读入单词，它是一次性看到所有 token 的。这意味着它天生分不清「狗咬人」和「人咬狗」——词是一样的，只是顺序不同。&lt;/p&gt;

&lt;p&gt;位置编码给每个位置生成一个&lt;strong&gt;独特的向量&lt;/strong&gt;，和词嵌入&lt;strong&gt;相加&lt;/strong&gt;，让模型知道谁在前、谁在后。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;输入表示 = 词嵌入 + 位置编码
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;补充：&lt;/strong&gt; 原始论文用 sin/cos 函数生成固定位置编码；后来的模型（如 RoPE）改用了更灵活的旋转位置编码。&lt;/p&gt;

&lt;p&gt;处理完毕后，每个 token 变成了一个&lt;strong&gt;既包含语义、又包含位置&lt;/strong&gt;的向量，这组向量就是编码器的输入。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;三编码器encoder理解输入&quot;&gt;三、编码器（Encoder）：理解输入&lt;/h2&gt;

&lt;p&gt;编码器堆叠 N 层（原始论文 N=6），每层结构完全相同，包含两个子模块：&lt;strong&gt;多头自注意力&lt;/strong&gt; 和 &lt;strong&gt;前馈网络&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;31-线性投影到-q--k--v&quot;&gt;3.1 线性投影到 Q / K / V&lt;/h3&gt;

&lt;p&gt;进入注意力之前，先把每个 token 的向量&lt;strong&gt;用三个不同的线性变换&lt;/strong&gt;，分别投影成三个新向量：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;向量&lt;/th&gt;
      &lt;th&gt;全称&lt;/th&gt;
      &lt;th&gt;直觉含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Q&lt;/strong&gt;（Query，查询）&lt;/td&gt;
      &lt;td&gt;「我想找什么信息？」&lt;/td&gt;
      &lt;td&gt;当前 token 发出的搜索请求&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;K&lt;/strong&gt;（Key，键）&lt;/td&gt;
      &lt;td&gt;「我能提供什么信息？」&lt;/td&gt;
      &lt;td&gt;每个 token 公开的标签&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;V&lt;/strong&gt;（Value，值）&lt;/td&gt;
      &lt;td&gt;「我的实际内容是什么」&lt;/td&gt;
      &lt;td&gt;真正会被取走的信息&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;为什么要分成三个？如果 Q 和 K 直接用同一个向量，模型只能做「我和你像不像」的比较；分开后可以实现「我想找的」和「你能提供的」是不同维度的匹配，表达能力更强。&lt;/p&gt;

&lt;h3 id=&quot;32-多头自注意力multi-head-self-attention&quot;&gt;3.2 多头自注意力（Multi-Head Self-Attention）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;核心公式：&lt;/strong&gt;&lt;/p&gt;

\[\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right) V\]

&lt;p&gt;拆解步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;打分：&lt;/strong&gt; 用 Q 和所有 K 做点积 → 得到每对 token 之间的”相关度分数”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;缩放：&lt;/strong&gt; 除以 \(\sqrt{d_k}\)（向量维度的平方根），防止分数过大导致 softmax 梯度消失&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;归一化：&lt;/strong&gt; softmax 把分数变成 0~1 之间的权重，所有权重加起来等于 1&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;加权求和：&lt;/strong&gt; 用权重对 V 做加权求和 → 每个 token 得到了一个融合了其他 token 信息的新向量&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;自注意力的意义：&lt;/strong&gt; 每个 token 可以”关注”输入中&lt;strong&gt;所有位置&lt;/strong&gt;的其他 token。比如在「The cat sat on the mat because &lt;strong&gt;it&lt;/strong&gt; was tired」中，自注意力让 “it” 知道自己指的是 “cat” 而不是 “mat”。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为什么是”多头”？&lt;/strong&gt; 把 Q/K/V 分成多组（比如 8 组），每组独立计算注意力，最后拼接。不同的”头”可以关注不同方面：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;某个头关注&lt;strong&gt;语法结构&lt;/strong&gt;（主谓宾）&lt;/li&gt;
  &lt;li&gt;某个头关注&lt;strong&gt;指代关系&lt;/strong&gt;（it → cat）&lt;/li&gt;
  &lt;li&gt;某个头关注&lt;strong&gt;相邻词&lt;/strong&gt;的搭配&lt;/li&gt;
  &lt;li&gt;某个头关注&lt;strong&gt;长距离依赖&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;33-残差连接--层归一化&quot;&gt;3.3 残差连接 + 层归一化&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;输出 = LayerNorm(x + SubLayer(x))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;残差连接（x + …）：&lt;/strong&gt; 把子层的输入直接加到输出上。好处是让梯度能”走捷径”传回去，避免深层网络训练困难。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;层归一化（LayerNorm）：&lt;/strong&gt; 把每个向量的数值范围拉回稳定区间，加速训练收敛。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每个子模块（注意力、FFN）之后都有这一对操作，图中共出现 2 次。&lt;/p&gt;

&lt;h3 id=&quot;34-前馈网络ffn&quot;&gt;3.4 前馈网络（FFN）&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FFN(x) = W₂ · GELU(W₁ · x + b₁) + b₂
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;两个线性层夹一个激活函数（GELU）。可以理解为：注意力负责”在 token 之间交换信息”，FFN 负责”对每个 token 自己的信息做深加工”。&lt;/p&gt;

&lt;p&gt;通常 FFN 的中间维度是输入的 4 倍（比如 512 → 2048 → 512），让模型有更大的”思考空间”。&lt;/p&gt;

&lt;h3 id=&quot;35-编码器输出&quot;&gt;3.5 编码器输出&lt;/h3&gt;

&lt;p&gt;经过 N 层堆叠后，输出的向量组称为&lt;strong&gt;上下文化表示（Context）&lt;/strong&gt;。每个 token 的向量现在不再只代表自己，而是&lt;strong&gt;融合了整段输入的完整语义&lt;/strong&gt;。这组向量会被送到解码器使用。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;四解码器decoder逐步生成输出&quot;&gt;四、解码器（Decoder）：逐步生成输出&lt;/h2&gt;

&lt;p&gt;解码器同样堆叠 N 层，但比编码器&lt;strong&gt;多一个子模块&lt;/strong&gt;（交叉注意力），共三个子模块。&lt;/p&gt;

&lt;h3 id=&quot;41-右移的目标序列&quot;&gt;4.1 右移的目标序列&lt;/h3&gt;

&lt;p&gt;解码器的输入是&lt;strong&gt;已经生成的部分结果&lt;/strong&gt;（推理时）或&lt;strong&gt;右移的正确答案&lt;/strong&gt;（训练时）。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;正确答案：  &quot;我&quot;  &quot;爱&quot;  &quot;你&quot;  &quot;&amp;lt;EOS&amp;gt;&quot;
右移后：  &quot;&amp;lt;BOS&amp;gt;&quot;  &quot;我&quot;  &quot;爱&quot;  &quot;你&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，每个位置的任务就是&lt;strong&gt;根据左边已有的内容，预测下一个 token&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;42-masked-多头自注意力&quot;&gt;4.2 Masked 多头自注意力&lt;/h3&gt;

&lt;p&gt;和编码器的自注意力几乎一样，区别在于加了&lt;strong&gt;遮罩（Mask）&lt;/strong&gt;：每个 token 只能看到自己和&lt;strong&gt;左边&lt;/strong&gt;的 token，看不到右边（未来的 token）。&lt;/p&gt;

&lt;p&gt;为什么要遮罩？因为生成是从左到右的，生成第 3 个词时，第 4 个词还不存在。训练时虽然能看到完整答案，但必须用 mask 模拟这种”看不到未来”的真实推理场景，否则模型会”作弊”。&lt;/p&gt;

&lt;h3 id=&quot;43-交叉注意力cross-attention&quot;&gt;4.3 交叉注意力（Cross-Attention）&lt;/h3&gt;

&lt;p&gt;这是解码器独有的模块：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Q 来自解码器&lt;/strong&gt;（当前正在生成的 token）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;K 和 V 来自编码器输出&lt;/strong&gt;（输入句子的完整语义）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;作用：让解码器在生成每个词时，能够”回头看”输入句子。比如翻译「I love you」时，生成「爱」这个字需要关注输入中的「love」。&lt;/p&gt;

&lt;h3 id=&quot;44-ffn--残差连接--层归一化&quot;&gt;4.4 FFN + 残差连接 + 层归一化&lt;/h3&gt;

&lt;p&gt;和编码器完全一样，不再赘述。每个子模块之后都有残差连接和层归一化。&lt;/p&gt;

&lt;h3 id=&quot;45-解码器层的堆叠&quot;&gt;4.5 解码器层的堆叠&lt;/h3&gt;

&lt;p&gt;解码器同样堆叠 N 层，上一层的输出作为下一层的输入，逐层精炼。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;五输出预测从向量到文字&quot;&gt;五、输出预测：从向量到文字&lt;/h2&gt;

&lt;p&gt;解码器最后一层的输出是一组向量，还需要转回文字：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;线性层：&lt;/strong&gt; 把向量映射到词表大小的维度（比如 50000 维，对应词表中每个词一个分数）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Softmax：&lt;/strong&gt; 把分数转成概率分布（所有词的概率之和 = 1）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;选词：&lt;/strong&gt; 取概率最高的那个 token 作为输出（贪心策略），或用 beam search / 采样等策略&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然后把这个新 token 拼到解码器输入的末尾，重复整个解码过程，直到生成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;EOS&amp;gt;&lt;/code&gt;（结束符）。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;六训练-vs-推理的区别&quot;&gt;六、训练 vs 推理的区别&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;训练&lt;/th&gt;
      &lt;th&gt;推理&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;编码器&lt;/td&gt;
      &lt;td&gt;运行一次&lt;/td&gt;
      &lt;td&gt;运行一次&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;解码器输入&lt;/td&gt;
      &lt;td&gt;右移后的&lt;strong&gt;完整正确答案&lt;/strong&gt;（Teacher Forcing）&lt;/td&gt;
      &lt;td&gt;已生成的&lt;strong&gt;部分序列&lt;/strong&gt;，逐步增长&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;解码器运行&lt;/td&gt;
      &lt;td&gt;一次并行处理所有位置（靠 mask 防作弊）&lt;/td&gt;
      &lt;td&gt;一步步循环，每步多生成一个 token&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;目标&lt;/td&gt;
      &lt;td&gt;缩小预测和正确答案的差距，更新参数&lt;/td&gt;
      &lt;td&gt;用训练好的参数直接生成&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Teacher Forcing（教师强制）：&lt;/strong&gt; 训练时不等模型自己生成上一个词再喂下一个，而是直接把正确答案喂给解码器。这样训练更稳定、收敛更快。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;七主要变体&quot;&gt;七、主要变体&lt;/h2&gt;

&lt;p&gt;原始 Transformer 是 Encoder-Decoder 结构，后来衍生出三大家族：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;变体&lt;/th&gt;
      &lt;th&gt;结构&lt;/th&gt;
      &lt;th&gt;代表模型&lt;/th&gt;
      &lt;th&gt;擅长&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;仅编码器&lt;/td&gt;
      &lt;td&gt;只用 Encoder&lt;/td&gt;
      &lt;td&gt;BERT&lt;/td&gt;
      &lt;td&gt;理解型任务（分类、抽取、问答）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;仅解码器&lt;/td&gt;
      &lt;td&gt;只用 Decoder&lt;/td&gt;
      &lt;td&gt;GPT、LLaMA、Qwen&lt;/td&gt;
      &lt;td&gt;生成型任务（对话、写作、代码）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;编码器-解码器&lt;/td&gt;
      &lt;td&gt;完整结构&lt;/td&gt;
      &lt;td&gt;T5、原始 Transformer&lt;/td&gt;
      &lt;td&gt;序列到序列（翻译、摘要）&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;现在最流行的大语言模型（ChatGPT、Claude、Qwen 等）大多是&lt;strong&gt;仅解码器&lt;/strong&gt;架构——去掉了编码器和交叉注意力，只保留带 mask 的自注意力和 FFN，靠超大规模数据和参数量实现强大的生成能力。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;八总结一张图的完整信息流&quot;&gt;八、总结：一张图的完整信息流&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;输入文本
  ↓ 分词 / Token 化
  ↓ 词嵌入 + 位置编码
  ↓
┌─────────── 编码器 × N 层 ──────────┐
│  线性投影 → Q / K / V              │
│  多头自注意力（看所有位置）          │
│  残差连接 + 层归一化                │
│  前馈网络（FFN）                    │
│  残差连接 + 层归一化                │
└───────────────────────────────────┘
  ↓ 上下文化表示（Context）
  ↓ K, V 传入解码器的交叉注意力
┌─────────── 解码器 × N 层 ──────────┐
│  Masked 多头自注意力（只看左边）     │
│  残差连接 + 层归一化                │
│  交叉注意力（Q 来自解码器，KV 来自编码器）│
│  残差连接 + 层归一化                │
│  前馈网络（FFN）                    │
│  残差连接 + 层归一化                │
└───────────────────────────────────┘
  ↓
  线性层 → Softmax → 预测下一个 token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Vaswani et al., &lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot;&gt;&lt;em&gt;Attention Is All You Need&lt;/em&gt;&lt;/a&gt;, 2017&lt;/li&gt;
  &lt;li&gt;Jay Alammar, &lt;a href=&quot;https://jalammar.github.io/illustrated-transformer/&quot;&gt;&lt;em&gt;The Illustrated Transformer&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 08 Apr 2026 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2026/04/08/transformer_architecture.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2026/04/08/transformer_architecture.html</guid>
        
        <category>Transformer</category>
        
        <category>注意力机制</category>
        
        <category>深度学习</category>
        
        
        <category>AI 技术</category>
        
      </item>
    
      <item>
        <title>一起来学习 tauri</title>
        <description>&lt;h3 id=&quot;安装&quot;&gt;安装&lt;/h3&gt;
&lt;h4 id=&quot;1-mac-安装-rust&quot;&gt;1. Mac &lt;a href=&quot;https://tauri.app/start/prerequisites/#rust&quot;&gt;安装 rust&lt;/a&gt;:&lt;/h4&gt;
&lt;p&gt;执行：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl --proto &apos;=https&apos; --tlsv1.2 https://sh.rustup.rs -sSf | sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;安装路径：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RUSTUP_HOME: ~/.rustup
CARGO_HOME: ~/.cargo
The cargo, rustc, rustup and other commands: ~/.cargo/bin
This path will then be added to your PATH environment variable by
modifying the profile files located at:
  ~/.profile
  ~/.bash_profile
  ~/.zshenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;结果：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo&apos;s bin directory ($HOME/.cargo/bin).

To configure your current shell, you need to source
the corresponding env file under $HOME/.cargo.

This is usually done by running one of the following (note the leading DOT):
. &quot;$HOME/.cargo/env&quot;            # For sh/bash/zsh/ash/dash/pdksh
source &quot;$HOME/.cargo/env.fish&quot;  # For fish
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;卸载：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rustup self uninstall
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 22 Oct 2024 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2024/10/22/%E4%B8%80%E8%B5%B7%E6%9D%A5%E5%AD%A6%E4%B9%A0-tauri.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2024/10/22/%E4%B8%80%E8%B5%B7%E6%9D%A5%E5%AD%A6%E4%B9%A0-tauri.html</guid>
        
        <category>rust</category>
        
        <category>tauri</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>浏览器</title>
        <description>&lt;h2 id=&quot;chromium&quot;&gt;chromium&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://source.chromium.org/chromium/chromium/src&quot;&gt;源码阅读&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/HEAD/docs/mac_build_instructions.md&quot;&gt;Mac 编译&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ccache_mac.md#Installation&quot;&gt;CCache 加速&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/main/docs/lldbinit.md&quot;&gt;lldbinit.py 使用&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://chromedevtools.github.io/devtools-protocol/tot/Runtime/&quot;&gt;cdp 接口文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;v8&quot;&gt;V8&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://v8.dev/docs/build-gn&quot;&gt;v8 编译&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://v8docs.nodesource.com/node-11.14/dd/dbc/namespacev8_1_1platform.html&quot;&gt;v8 接口文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Dec 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/12/05/%E6%B5%8F%E8%A7%88%E5%99%A8.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/12/05/%E6%B5%8F%E8%A7%88%E5%99%A8.html</guid>
        
        <category>浏览器，browser</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>代理</title>
        <description>&lt;h2 id=&quot;不错的网站&quot;&gt;不错的网站&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.q1cloud.me/&quot;&gt;q1cloud&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Dec 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/12/05/%E4%BB%A3%E7%90%86.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/12/05/%E4%BB%A3%E7%90%86.html</guid>
        
        <category>代理</category>
        
        <category>proxy</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>Mermaid</title>
        <description>&lt;h2 id=&quot;网址&quot;&gt;网址&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://mermaid.js.org/&quot;&gt;官网&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mermaidflow.app/flowchart&quot;&gt;可视化画图&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 05 Dec 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/12/05/mermaid.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/12/05/mermaid.html</guid>
        
        <category>Mermaid</category>
        
        <category>画图</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>vulkan</title>
        <description>&lt;h2 id=&quot;doc&quot;&gt;Doc&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://vulkan.lunarg.com/sdk/home&quot;&gt;vulkan sdk&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 18 Sep 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/09/18/vulkan.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/09/18/vulkan.html</guid>
        
        <category>vulkan</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>编译相关</title>
        <description>&lt;h2 id=&quot;文件查看&quot;&gt;文件查看&lt;/h2&gt;
&lt;h3 id=&quot;objdump-关键命令&quot;&gt;objdump 关键命令&lt;/h3&gt;
&lt;p&gt;反汇编&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objdump -d xxx.o
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;显示头文件信息&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objdump -f xxx.o
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;查看文件有哪些符号，包括数据段、导出的函数、引用其他库的函数&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objdump -tT xxx.so
objdump -x xxx.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;查看依赖&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objdump -x xxx.so | grep &quot;NEEDED&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;lipo&quot;&gt;lipo&lt;/h2&gt;
&lt;p&gt;lipo源于mac系统要制作兼容powerpc平台和intel平台的程序，lipo 是一个在 Mac OS X 中处理通用程序（Universal Binaries）的工具，主要用于查看，修改Mac OS X 中的通用程序。&lt;/p&gt;

&lt;p&gt;（a）查看frameWork支持架构
lipo -info xxx.framework/xxx
输出结果：Architectures in the fat file: xxx.framework/xxx are: i386 x86_64 armv7 arm64&lt;/p&gt;

&lt;p&gt;（b）合并framework
lipo -create xxx(i386).framework/xxx xxx(armv7).framework/xxx -output xxx.framework.xxx
说明：通过编译产生的动态库/静态库，均为单一架构支持（模拟器/真机），其中i386和x86_64均为模拟器架构，armv7和arm64为真机架构，通过合并产生的framework才是全架构支持&lt;/p&gt;

&lt;p&gt;（c）拆分framework
lipo xxx.framework/xxx -thin i386 xxx.i386
说明：次操作将单独分离出仅支持i386的动态库/静态库文件， x86_64 armv7 arm64可同理分离，可以配合合并操作去除不需要的架构支持，用于动态库/静态库瘦身。&lt;/p&gt;

&lt;h2 id=&quot;简述ar操作&quot;&gt;简述ar操作&lt;/h2&gt;
&lt;p&gt;ar是linux提供的维护链接编辑器使用的索引库&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;要创建一个库，请输入：
   ar -v -q lib.a strlen.o strcpy.o
如果 lib.a 库不存在，则此命令创建它，并将文件 strlen.o 和 strcpy.o 的副本输入其中。如果 lib.a 库存在，则此命令在不检查相同成员的情况下，将新的成员添加到末尾。v 标志设置详细方式，在此方式中 ar 命令在其进行时显示进程报告。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要显示库的目录，请输入：
   ar -v -t lib.a
此命令列出了 lib.a 库的目录，显示类似于 ls -l 命令的输出的长列表。要只列出成员文件名称，则省略 -v 标志。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要替换或添加新成员到库中，请输入：
   ar -v -r lib.a strlen.o strcat.o
此命令替换成员 strlen.o 和 strcat.o。如果 lib.a 如示例 1 中显示的那样创建，则替换 strlen.o 成员。因为不存在名为 strcat.o 的成员，所以它被添加到库的末尾。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要指定在何处插入新成员，请输入：
   ar -v -r -b strlen.o lib.a strcmp.o
此命令添加 strcmp.o 文件，并将该新成员置于 strlen.o 成员之前。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要更新一个已经更改过的成员，请输入：
   ar -v -r -u lib.a strcpy.o
此命令替换现有 strcpy.o 成员，但仅当文件 strcpy.o 自从最后一次添加到库后已经修改时才替换它。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要更改库成员的顺序，请输入：
   ar -v -m -a strcmp.o lib.a strcat.o strcpy.o
此命令将成员 strcat.o 和 strcpy.o 移动到紧跟在 strcmp.o 成员之后的位置。保留 strcat.o 和 strcpy.o 成员的相对顺序。换句话说，如果在移动之前 strcpy.o 成员在 strcat.o 成员之前，那么（移动后）它依旧如此。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要解压缩库成员，请输入：
   ar -v -x lib.a strcat.o strcpy.o
此命令将成员 strcat.o 和 strcpy.o 分别复制到名为 strcat.o 和 strcpy.o 的文件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要解压缩并重命名一个成员，请输入：
   ar -p lib.a strcpy.o &amp;gt;stringcopy.o
此命令将成员 strcpy.o 复制到一个名为 stringcopy.o 的文件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要删除一个成员，请输入：
   ar -v -d lib.a strlen.o
此命令从 lib.a 库中删除成员 strlen.o。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要从多个用 ld 命令创建的共享模块中创建一个压缩文档库，请输入：
  ar -r -v libshr.a shrsub.o shrsub2.o shrsub3.o …
此命令从名为 shrsub.o、shrsub2.o、shrsub3.o 等等的共享模块中创建名为 libshr.a 的压缩文档库。要编译并链接使用 libshr.a 压缩文档库的 main 程序，请使用以下命令：&lt;/p&gt;

    &lt;p&gt;cc -o main main.c -L/u/sharedlib -lshr
main 程序现在是可执行的。main 程序引用的任何符号（包含在libshr.a 压缩文档库中）已经因延迟分辨率而作了标记。-l 标志指定应在 libshr.a 库中搜索这些符号。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;要列出 lib.a 的内容（忽略任何 32 位目标文件），请输入：
  ar -X64 -t -v lib.a&lt;/li&gt;
  &lt;li&gt;要从 lib.a 解压缩所有 32 位的目标文件，请输入：
  ar -X32 -x lib.a&lt;/li&gt;
  &lt;li&gt;要列出 lib.a 中的所有文件，无论是 32 位、64 位或非对象，请输入：
  ar -X32_64 -t -v lib.a
    &lt;h2 id=&quot;简述nm操作&quot;&gt;简述nm操作&lt;/h2&gt;
    &lt;p&gt;nm 命令被用于显示二进制目标文件的符号表&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;语法
nm(选项)(参数)
举例：
nm View.o&amp;gt;View.m 
nm -A View.o
说明：
选项：[-A：每个符号前显示文件名；-D：显示动态符号；-g：仅显示外部符号；-r：反序显示符号表。]
参数：目标文件，二进制目标文件，通常是库文件和可执行文件&lt;/p&gt;
</description>
        <pubDate>Sun, 27 Aug 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/08/27/compile.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/08/27/compile.html</guid>
        
        <category>compile</category>
        
        <category>编译</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>opengl</title>
        <description>&lt;h2 id=&quot;doc&quot;&gt;Doc&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.khronos.org/opengl/wiki/Getting_Started&quot;&gt;Getting Started&lt;/a&gt;
&lt;a href=&quot;https://www.khronos.org/opengl/wiki/Related_toolkits_and_APIs#Context/Window_Toolkits&quot;&gt;Window_Toolkits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.glfw.org/download.html&quot;&gt;GLFW&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/McNopper/OpenGL&quot;&gt;OpenGL examples&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 15 Aug 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/08/15/opengl.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/08/15/opengl.html</guid>
        
        <category>opengl</category>
        
        
        <category>编程技术</category>
        
      </item>
    
      <item>
        <title>MACOS</title>
        <description>&lt;h2 id=&quot;打不开软件&quot;&gt;打不开软件&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo spctl  --master-disable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo xattr -r -d com.apple.quarantine /Applications/ILSpy.app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;如何清理-macos-图标缓存&quot;&gt;如何清理 macOS 图标缓存&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo find /private/var/folders/ \( -name com.apple.dock.iconcache -or -name com.apple.iconservices \) -exec rm -rfv {} \;
sudo rm -rf /Library/Caches/com.apple.iconservices.store;
killall Dock
killall Finder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;date-命令&quot;&gt;date 命令&lt;/h2&gt;
&lt;p&gt;Mac下的date命令是BSD（Berkeley Software Distribution）系的，Linux下date命令是GNU（GNU’s Not Unix）系，二者用法有一些区别。&lt;/p&gt;

&lt;p&gt;BSD并不特指任何一个BSD衍生版本，而是类UNIX操作系统中的一个分支的总称。&lt;/p&gt;

&lt;p&gt;Mac OS X和iOS实际上基于Darwin，Darwin是BSD其中一个分支&lt;/p&gt;

&lt;h3 id=&quot;共同点&quot;&gt;共同点&lt;/h3&gt;
&lt;p&gt;基本的时间格式的缩写是相同的，规则如下：&lt;/p&gt;

&lt;p&gt;%Y:四位数形式的年份，如2018
%y:两位数形式的年份，如18
%m:表示前导0的月份，如01，12
%d:表示前导0的日期，如02，28
%h:三位的英文月份
%H:表示前导0的24小时(0~23)
%M:表示前导为0的分钟数(0~60)
%S:表示前导为0的描述秒数(0~60)
%s:表示距离格林尼治时间（1970年1月1日0点）的秒数&lt;/p&gt;

&lt;h3 id=&quot;不同点&quot;&gt;不同点&lt;/h3&gt;
&lt;p&gt;获取前一天和获取后一天的写法不同：&lt;/p&gt;

&lt;p&gt;Mac，通过-v参数，-v-1d代表前一天，-v-1y代表上一年
Linux，通过–date参数实现，–date=’-1 day’代表前一天，–date=’-1 year’代表上一年&lt;/p&gt;

&lt;h3 id=&quot;mac-date命令用法详解&quot;&gt;mac date命令用法详解&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;usage: date [-jnRu] [-d dst] [-r seconds] [-t west] [-v[+|-]val[ymwdHMS]] ...
            [-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;参数解析&quot;&gt;参数解析：&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-j:使用-j才能使用-f

-n:默认情况下，如果定时进程正在运行，date命令会在本地组的所有机器设置时间。-n可以禁止这种行为，表示只设置当前计算机。

-u:显示或者设置日期为UTC（世界协调时钟）时间

-d:设置内核的时区，一般不使用

-r:秒转换时间

-t:(time zone)时区设置（GMT时区）

-v:根据参数调整时间

不使用+|-，更改对应的时间
使用+：增加对应的时间
使用-：减少对应的时间
-f:根据格式调整时间

+:+号引导的一些格式，和正常的Linux格式一致。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;使用示例&quot;&gt;使用示例：&lt;/h4&gt;

&lt;p&gt;（1）查看当前时间戳（即转换成秒）：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  ~ date +%s
1545705798
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;（2）当前时间转换指定格式&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  ~ date +%Y-%m-%d-%H:%M:%S
2018-12-25-10:46:04
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(3)时间戳转时间&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  ~ date -r 1545705922
Tue Dec 25 10:45:22 CST 2018
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 25 May 2023 00:00:00 +0800</pubDate>
        <link>https://blog.yuanzhaoyi.cn/2023/05/25/macos.html</link>
        <guid isPermaLink="true">https://blog.yuanzhaoyi.cn/2023/05/25/macos.html</guid>
        
        <category>MACOS</category>
        
        
        <category>编程技术</category>
        
      </item>
    
  </channel>
</rss>
