<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ai on TouchingFish.top</title><link>https://touchingfish.top/categories/ai/</link><description>Recent content in Ai on TouchingFish.top</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 17 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://touchingfish.top/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>JD 里的 RAG，其实是 LangChain 调参</title><link>https://touchingfish.top/2025/rag-is-langchain-tuning/</link><pubDate>Tue, 17 Jun 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/rag-is-langchain-tuning/</guid><description>&lt;p&gt;招聘网站上搜&amp;quot;RAG&amp;quot;，跳出来的 JD 长得都差不多。&lt;/p&gt;
&lt;p&gt;熟悉 Retrieval-Augmented Generation。了解向量数据库（FAISS、Pinecone、Chroma）。掌握 Embedding 模型（OpenAI、BGE、M3E）。有 LLM 应用开发经验，熟悉 LangChain / LlamaIndex。&lt;/p&gt;
&lt;p&gt;我一开始也以为这是在招算法工程师——那种能从头训练 Embedding 模型、能设计新的向量索引结构、能在论文里挂名的人。&lt;/p&gt;
&lt;p&gt;面了几轮才发现，大部分公司要的不是这个。他们想要的是：给你一个文档库，你能用 LangChain 搭一个问答系统，调调参数，让回答别那么离谱。至于向量数据库选 FAISS 还是 Chroma，Embedding 模型选 OpenAI 还是开源的，很多时候只是配置项的问题。&lt;/p&gt;
&lt;p&gt;JD 里写的 RAG、向量数据库、Embedding，翻译成人话就是：你会不会用 LangChain 调参。&lt;/p&gt;
&lt;p&gt;向量数据库不需要 SQL boys 和 SQL girls，但又不能说不是一回事。它说的就是数据库，只是存的东西不是行和列，而是坐标。没有 JOIN，没有 WHERE。只有&amp;quot;给我找离这个点最近的几个邻居&amp;quot;。&lt;/p&gt;
&lt;p&gt;这篇文章从调参的视角，把 RAG pipeline 里每个环节的参数过一遍。不重复基础概念，只讲实际写代码时会遇到的选择。&lt;/p&gt;
&lt;h2 id="切"&gt;切&lt;/h2&gt;
&lt;p&gt;chunk_size 设多少？&lt;/p&gt;
&lt;p&gt;RAG 的第一步是把文档切成块。LangChain 里最常用的工具是 &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.text_splitter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;text_splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;separators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;，&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;chunk_size&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这个参数的单位是 token 数，不是字符数。设得太小，一个完整的句子被拦腰截断，语义支离破碎。设得太大，单个块包含太多主题，检索时容易引入无关信息。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通用文档（论文、报告）：500-1000 tokens&lt;/li&gt;
&lt;li&gt;代码：200-400 tokens，按函数或类切分更合理&lt;/li&gt;
&lt;li&gt;对话记录：300-500 tokens，保留完整回合&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个容易踩的坑是盲目追求大 chunk。&amp;ldquo;大一点，信息多一点，模型回答更完整&amp;rdquo;——听起来对，但检索精度会下降。一个 2000 token 的块可能包含五个段落，用户的问题只匹配其中一段，其余四段都是噪声。&lt;/p&gt;</description></item><item><title>当你连 scale 也量化了</title><link>https://touchingfish.top/2025/gguf-k-quants/</link><pubDate>Fri, 30 May 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/gguf-k-quants/</guid><description>&lt;p&gt;上次算到，160 亿参数的模型用 Q4_0 量化后，光 scale 和 $\alpha$ 这些&amp;quot;说明书&amp;quot;就要吃掉 2 GB。&lt;/p&gt;
&lt;p&gt;2 GB 不小。一台 16 GB 显存的显卡，模型权重压缩后大概 8 GB，结果说明书自己占了四分之一。这就像你去宜家买了一张桌子，包装盒里一半是螺丝和安装图纸。&lt;/p&gt;
&lt;p&gt;K-quants 要解决的就是这个问题。思路很直白——既然权重可以量化，那说明书也可以量化。&lt;/p&gt;
&lt;h2 id="套娃"&gt;套娃&lt;/h2&gt;
&lt;p&gt;K-quants 的核心结构叫 super-block。做法是把 8 个普通 block 打包成一组，然后对 8 个 scale 再做一次量化——从 FP16（16 bit）砍到 INT8（8 bit）：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;super-block: 256 个 INT4 权重
├── block 0: scale = 0.10 (FP16 → INT8)
├── block 1: scale = 0.14 (FP16 → INT8)
├── block 2: scale = 0.11 (FP16 → INT8)
├── block 3: scale = 0.18 (FP16 → INT8)
├── block 4: scale = 0.08 (FP16 → INT8)
├── block 5: scale = 0.16 (FP16 → INT8)
├── block 6: scale = 0.13 (FP16 → INT8)
├── block 7: scale = 0.09 (FP16 → INT8)
└── super-scale = 0.00142 (FP16) ← 新加的，用来还原上面那些 INT8
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;来走一遍完整的计算。假设这 8 个 block 里 256 个权重已经量化完了——每个 block 的 scale 是上面那 8 个 FP16 值。现在的任务是把这 8 个 scale 自己也量化掉。&lt;/p&gt;</description></item><item><title>当你把模型从16位砍到4位</title><link>https://touchingfish.top/2025/gguf-legacy-quants/</link><pubDate>Mon, 19 May 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/gguf-legacy-quants/</guid><description>&lt;p&gt;第一次在本地跑 Llama 的时候，下载页面那一排 Q4_0、Q4_K_M、Q5_1 让我愣了半天。&lt;/p&gt;
&lt;p&gt;选了最大的文件，跑不起来。显存不够。选了最小的文件，跑起来了，但回答像喝了假酒。后来才知道，这一排名字背后是一整套妥协方案——在模型的&amp;quot;胖瘦&amp;quot;和&amp;quot;聪明程度&amp;quot;之间找平衡。&lt;/p&gt;
&lt;p&gt;这篇文章讲最早的那套方案：legacy quants。GGUF 现在已经有 K-quants 和 I-quants 了，但 legacy 是地基。搞懂了它，后面那些花里胡哨的变体无非是在这个地基上换砖头。&lt;/p&gt;
&lt;h2 id="fp16-和-int4"&gt;FP16 和 INT4&lt;/h2&gt;
&lt;p&gt;在聊量化之前，先搞懂两样东西：模型权重长什么样，以及我们想把它变成什么样。&lt;/p&gt;
&lt;p&gt;FP16 是半精度浮点数。16 个 bit（2 个字节），存一个带小数点的数。C 语言里没有 FP16，但你想象一个比 &lt;code&gt;float&lt;/code&gt;（32 位）更省空间的浮点数就行。大模型的权重——那些矩阵里的每一个数字——出厂的时候就是 FP16。一个 70 亿参数的模型，70 亿个 FP16，也就是 14 GB。不大不小，刚好塞不进一张 12 GB 的消费级显卡。&lt;/p&gt;
&lt;p&gt;INT4 是 4 位整数。4 个 bit，能表示 $2^4 = 16$ 个不同的值。如果你只写过 C，你熟悉的整数是 &lt;code&gt;int&lt;/code&gt;（32 位）、&lt;code&gt;short&lt;/code&gt;（16 位）、&lt;code&gt;char&lt;/code&gt;（8 位）。4 位的整数在 C 里没有——太小了，小到没法单独寻址，必须打包存储。&lt;/p&gt;
&lt;p&gt;打个比方：FP16 像女生的口红色号——豆沙、枫叶、烂番茄，每一格都有名字。INT4 是男人的衣柜——黑、白、灰、深蓝，能数的就那几种。&lt;/p&gt;
&lt;p&gt;量化的核心问题就是：怎么用男人的衣柜里那几种颜色，还原出口红色号的全部渐变？&lt;/p&gt;
&lt;p&gt;这是有损压缩。丢掉的信息永远捡不回来。&lt;/p&gt;
&lt;h2 id="假装天平是平的"&gt;假装天平是平的&lt;/h2&gt;
&lt;p&gt;GGUF 的量化不是把每个 FP16 单独转成 INT4。那样做的话，每个权重需要一个 scale（缩放系数）来告诉你怎么&amp;quot;还原&amp;quot;，而 scale 本身就是 FP16——16 位。用 16 位存 scale 去压缩一个 16 位的权重？没有意义。&lt;/p&gt;</description></item></channel></rss>