<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>2025s on TouchingFish.top</title><link>https://touchingfish.top/2025/</link><description>Recent content in 2025s on TouchingFish.top</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 19 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://touchingfish.top/2025/index.xml" rel="self" type="application/rss+xml"/><item><title>Rust 的"解法"</title><link>https://touchingfish.top/2025/learning-rust-c-memory-safety/</link><pubDate>Sun, 19 Oct 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/learning-rust-c-memory-safety/</guid><description>&lt;p&gt;翻出 2020 年写的 C 语言博客。&lt;/p&gt;
&lt;p&gt;&lt;a href="../../2020/array-and-pointer/"&gt;数组与指针&lt;/a&gt;，&lt;a href="../../2020/journey-to-c-language/"&gt;内存布局与调试&lt;/a&gt;。那时候写得挺认真的，后来还画内存图，标注高地址低地址，解释为什么 &lt;code&gt;arr[3] = 20&lt;/code&gt; 能把旁边变量 &lt;code&gt;i&lt;/code&gt; 的值也改了。文章结尾写的是：&amp;ldquo;为避免这种错误，始终要确保在使用数组时不越界访问。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;现在回头看，那句话翻译成人话大概是：你最好别写错。&lt;/p&gt;
&lt;p&gt;因为写错了编译器不会告诉你。&lt;/p&gt;
&lt;p&gt;学 Rust 的时候，很多东西越看越像是对 C 里那些经典问题的系统性回应。不是&amp;quot;Rust 比 C 好&amp;quot;——是 Rust 把二十年来系统编程中最常见的错误模式，逐个编码进了编译器和类型系统里。一个 Rust 新手在编译期被挡下的问题，可能比一个 C 老手十年遇到的运行时 bug 还全面。&lt;/p&gt;
&lt;h2 id="指针三兄弟"&gt;指针三兄弟&lt;/h2&gt;
&lt;p&gt;NULL、野的、悬空——C 程序员的日常恐惧来源。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int *p = NULL; *p = 1;&lt;/code&gt; 编译通过。运行到这一行，操作系统发来 SIGSEGV。不是编译期能发现的事——NULL 是一个合法的指针值，只是指向的地址不可访问。编译器没有语义层面的&amp;quot;这个指针可能为空&amp;quot;的概念。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int* f() { int x = 5; return &amp;amp;x; }&lt;/code&gt; 也编译通过。x 在 f 的栈帧里，函数返回后栈帧被回收，返回的地址指向一块随时可能被覆盖的内存。这个地址仍然&amp;quot;合法&amp;quot;——它在一个可访问的内存段内，只是内容不受你控制。有时候打印出 5，有时候打印出随机数，有时候什么都没发生，取决于后续函数调用有没有踩到同一块栈空间。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;free(p); *p = 1;&lt;/code&gt; 还是编译通过。free 之后那块堆内存被标记为可用，但 p 的值没变，仍然指向原地址。继续通过 p 读写，运气好时数据还没被覆盖，程序照常运行——这比直接崩溃更危险，因为你不知道错误已经埋下了。&lt;/p&gt;
&lt;p&gt;Rust 的做法很简单：没有 NULL。没有悬垂引用。没有 use-after-free。&lt;/p&gt;</description></item><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>贵州农民收入驱动机制建模记</title><link>https://touchingfish.top/2025/rural-income-driving-mechanism/</link><pubDate>Sat, 07 Jun 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/rural-income-driving-mechanism/</guid><description>&lt;p&gt;&amp;ldquo;本研究以贵州省为典型案例，深入剖析了乡村振兴战略实施过程中农民收入的驱动机制与未来趋势。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这是那个本科生开题报告里的话。我接到单子的时候，初稿已经在导师手里了，分析也几乎做&amp;quot;完&amp;quot;了，导师给她上了很大的压力。&lt;/p&gt;
&lt;p&gt;实际做下来，驱动机制没剖析出什么花来，倒是被多重共线性剖析了一通。&lt;/p&gt;
&lt;h2 id="数据"&gt;数据&lt;/h2&gt;
&lt;p&gt;贵州省 2007–2023 年度数据，17 个观测，10 个自变量，1 个因变量：&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;x1&lt;/td&gt;
 &lt;td&gt;人均生产总值&lt;/td&gt;
 &lt;td&gt;元&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x2&lt;/td&gt;
 &lt;td&gt;第一产业增加值&lt;/td&gt;
 &lt;td&gt;亿元&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x3&lt;/td&gt;
 &lt;td&gt;第三产业增加值&lt;/td&gt;
 &lt;td&gt;亿元&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x4&lt;/td&gt;
 &lt;td&gt;城镇化率&lt;/td&gt;
 &lt;td&gt;%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x5&lt;/td&gt;
 &lt;td&gt;农业支出&lt;/td&gt;
 &lt;td&gt;亿元&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x6&lt;/td&gt;
 &lt;td&gt;农村居民人均消费支出&lt;/td&gt;
 &lt;td&gt;元&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x7&lt;/td&gt;
 &lt;td&gt;农业机械总动力&lt;/td&gt;
 &lt;td&gt;万千瓦&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x8&lt;/td&gt;
 &lt;td&gt;农作物总播种面积&lt;/td&gt;
 &lt;td&gt;千公顷&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x9&lt;/td&gt;
 &lt;td&gt;有效灌溉面积&lt;/td&gt;
 &lt;td&gt;千公顷&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x10&lt;/td&gt;
 &lt;td&gt;绝收面积&lt;/td&gt;
 &lt;td&gt;万公顷&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;y&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;17 个样本点，10 个自变量。还没开始建模就知道要出事——自由度都快不够了。&lt;/p&gt;
&lt;p&gt;（学生说&amp;quot;数据是从统计年鉴上找的&amp;quot;。统计年鉴嘛，省级年度数据就那么多年，变量倒是能找一堆，但样本量是天花板。这不是学生的问题，是这类课题的先天限制。）&lt;/p&gt;
&lt;h2 id="第一步看看相关性"&gt;第一步：看看相关性&lt;/h2&gt;
&lt;p&gt;相关系数矩阵一出来，事情就很清楚了：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;与 y 的相关系数：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;变量&lt;/th&gt;
 &lt;th&gt;与 y 的 r&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;x6&lt;/td&gt;
 &lt;td&gt;0.999&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x3&lt;/td&gt;
 &lt;td&gt;0.996&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x1&lt;/td&gt;
 &lt;td&gt;0.991&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x2&lt;/td&gt;
 &lt;td&gt;0.990&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x4&lt;/td&gt;
 &lt;td&gt;0.983&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x7&lt;/td&gt;
 &lt;td&gt;0.911&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x5&lt;/td&gt;
 &lt;td&gt;0.888&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x8&lt;/td&gt;
 &lt;td&gt;0.689&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x9&lt;/td&gt;
 &lt;td&gt;0.625&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;x10&lt;/td&gt;
 &lt;td&gt;-0.361&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;几乎所有经济指标都和农民收入高度相关。r &amp;gt; 0.9 的变量对有 27 对。这不是&amp;quot;信息丰富&amp;quot;，这是&amp;quot;信息冗余&amp;quot;——大家都在说同一件事。&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><item><title>毕业后最认真面试的季度</title><link>https://touchingfish.top/2025/q1-interview-retrospective/</link><pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/q1-interview-retrospective/</guid><description>&lt;p&gt;回忆2024年，多少有点不真实。&lt;/p&gt;
&lt;p&gt;8月底，某央企的 HR 打来电话，说恭喜通过面试，体检报告过关就发 offer。我攥着手机在房间里站了五分钟。毕业三个月，终于快要有一个正经工作了——虽然是个放射类药物企业，体检等了一个多月还没下文，但好歹有个着落。&lt;/p&gt;
&lt;p&gt;11月初，hc 取消。&lt;/p&gt;
&lt;p&gt;那个 HR 的声音我现在还能想起来。先是很职业的恭喜，然后是一段不太职业的沉默，最后是&amp;quot;公司战略调整&amp;quot;。六个字，一场空。&lt;/p&gt;
&lt;p&gt;硕士读的是生物统计，论文写的是演化博弈。听起来挺唬人——实际上也确实挺唬人的，唬到秋招投了一圈 SAS Programmer，只捞到两次面试，最后拿了一个实习 offer。GCP 证书到手那天，我还认真地觉得自己要入行了。Good Clinical Practice，药物临床试验质量管理规范，翻译成人话就是：药厂认的实验怎么做，它告诉你。&lt;/p&gt;
&lt;p&gt;然后就到了2025年。&lt;/p&gt;
&lt;p&gt;一季度大概是我整个求职周期里最动荡的一段。不是投简历投得最多的时候——是内心最晃的时候。放弃了一个方向，不知道下一个方向在哪，每天翻招聘网站像翻一本永远写不到结局的小说。&lt;/p&gt;
&lt;p&gt;放弃 CRO 不等于那些东西白学了。临床试验的很多概念——随机化、盲法、偏倚、数据标准化——骨子里都在讨论同一件事：怎样在不完美的现实里，尽可能诚实地回答一个因果问题。这个问题的底层逻辑，放到哪个行业都不会过时。&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;第一场面试，我把随机化的定义背了一遍。&lt;/p&gt;
&lt;p&gt;面试官听完，没点头也没摇头。他问：&amp;ldquo;一个受试者入组后被分到了错误的组，你怎么办？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;我愣了大概五秒。&lt;/p&gt;
&lt;p&gt;不是在考定义。是在考你有没有想过实际操作中会出什么花活。那些课本上读不到的角落——系统出错了怎么办，随机表被人为干预了怎么办，发现的时候数据已经入库了怎么办。&lt;/p&gt;
&lt;p&gt;后来学乖了。每个概念后面都挂一个问题：&amp;ldquo;如果出错了呢&amp;rdquo;。回答一下就有血有肉了。&lt;/p&gt;
&lt;p&gt;临床试验的知识点翻来覆去就那些：随机化、盲法、AE 报告、SDTM、中心效应、脱落率。上过 GCP 课的人都能说上几句。面试官想看的是另一层东西——这些概念在真实世界里长什么样，摔过跟头的人才知道。&lt;/p&gt;
&lt;h2 id="书本之外"&gt;书本之外&lt;/h2&gt;
&lt;p&gt;准备面试那阵子，我花了大量时间啃 ICH E6 GCP、FDA 指导原则、统计方法的推导。这些东西当然有用。&lt;/p&gt;
&lt;p&gt;真正让我卡住的，是那些书上没有的问题。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;CRO 现在收缩得很厉害，你知道吗？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;数据管理员和 SAS 程序员的日常工作差在哪？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;你做过几个项目？用过什么 EDC 系统？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;书本不会告诉你行业寒冬这件事。书本不会告诉你 SAS 在临床数据处理领域的江湖地位正在被 Python 一点点蚕食。书本不会告诉你，某些 EDC 系统的市场份额直接决定了你要不要花时间学它。&lt;/p&gt;
&lt;p&gt;（这个问题我到现在也没有好答案。能说的就是：书本上的东西我可以学，行业经验需要时间——给我时间。）&lt;/p&gt;
&lt;h2 id="追问是好事"&gt;追问是好事&lt;/h2&gt;
&lt;p&gt;有些面试官喜欢追着问。追到你答不动为止。&lt;/p&gt;
&lt;p&gt;第一次被追到哑口无言的时候，恨不得找条缝把自己塞进去。&lt;/p&gt;
&lt;p&gt;后来想通了。追问意味着他在认真听。他想知道你是真懂还是背的。&lt;/p&gt;
&lt;p&gt;被问到不会的地方，老老实实说&amp;quot;这个我不太确定&amp;quot;，比硬撑着胡说八道强一百倍。&lt;/p&gt;
&lt;p&gt;有一次面试官问我 LOCF 的假设。我答错了。他当场纠正了——但没有露出那种&amp;quot;又来了一个背书的&amp;quot;的表情，而是说了一段话：&amp;ldquo;这个问题，很多做了好几年的统计师都不一定答对。你能往这个方向想，说明你动过脑子。&amp;rdquo;&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;面试官有时会扔一些开放性问题。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;多中心试验最大的挑战是什么？&amp;rdquo;&lt;/p&gt;</description></item><item><title>多中心的那些麻烦事</title><link>https://touchingfish.top/2025/multicenter-headaches/</link><pubDate>Mon, 10 Mar 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/multicenter-headaches/</guid><description>&lt;p&gt;多中心试验（multi-center trial）的名字取得真好听。&lt;/p&gt;
&lt;p&gt;多个中心一起招募，样本量涨得快，结论推广起来底气也足。理想状态是：各家中心像精密咬合的齿轮，在同一套 SOP 下匀速运转。&lt;/p&gt;
&lt;p&gt;现实是每家中心都是一个小宇宙。操作流程不一样，研究者经验不一样，患者人群不一样。这些差异搅在一起，就能让你吃不了兜着走。&lt;/p&gt;
&lt;h2 id="中心效应"&gt;中心效应&lt;/h2&gt;
&lt;p&gt;中心效应（Center Effect）——不同中心的研究结果之间存在系统性差异。&lt;/p&gt;
&lt;p&gt;原因可以是很多层的。人口学特征：A 中心的患者平均比 B 中心大十岁。操作差异：不同的设备、不同的手法、不同的熟练程度。依从性差异：有的中心随访跟得很紧，有的中心患者来一次算一次。&lt;/p&gt;
&lt;p&gt;一个经典例子：某抗高血压药物的试验，亚洲中心的降压效果显著，欧洲中心平平。拆开一看——亚洲中心患者 BMI 普遍偏低。不是药在亚洲人身上反应不一样，是基线特征分布不同。&lt;/p&gt;
&lt;p&gt;处理中心效应得分两步走。设计阶段用分层随机化——每个中心当一层，层内随机。再搞一套统一 SOP，把操作差异压到最小。分析阶段上混合效应模型（Mixed-Effects Model），&amp;ldquo;中心&amp;quot;作为随机效应塞进去，中心间差异扒开之后再估治疗效应。顺带查交互效应——有没有&amp;quot;在 A 中心有效、在 B 中心无效&amp;quot;这种诡异场面。最后跑一遍敏感性分析，排除个别中心或调权重，看结论够不够硬。&lt;/p&gt;
&lt;p&gt;面试官问过：&amp;ldquo;捡到显著的中心效应，你怎么办？&amp;rdquo;&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;脱落（Drop-out）是我准备面试时反复翻的一个话题——它直接咬在统计效能和结论可信度上。&lt;/p&gt;
&lt;p&gt;定义：受试者没走完试验全程——要么没接受完预定干预，要么没完成全部随访。&lt;/p&gt;
&lt;p&gt;后果三层：效能降低——样本缩了，检测真实效应的力气就小了。偏倚风险——如果脱落不是随机发生的（副反应太让人遭不住的更倾向于退出），整个估计就会歪掉。结果解读——脱落一多，ITT 人群和 PP 人群之间的裂口越来越大。&lt;/p&gt;
&lt;p&gt;设计的防线：样本量计算时把脱落的预估塞进去，纳入排除标准把不靠谱的人挡在外面，交通补助、免费检查之类的激励跟上。&lt;/p&gt;
&lt;p&gt;执行的防线：随访管理加强，定期提醒别断，脱落原因记录清楚，为后续分析铺路。电话随访、线上访谈做起来——灵活，总比丢失好。&lt;/p&gt;
&lt;p&gt;分析的防线：ITT——所有随机化受试者全纳入，不管有没有走完。金标准。缺失数据用 LOCF（末次观察值结转）或多重插补（Multiple Imputation, MI）顶上。&lt;/p&gt;
&lt;p&gt;面试官问过我 LOCF 的假设是什么。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;假设受试者最后一次观察的状态会原封不动保持到后续时间点。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;他追问：这个假设靠谱吗？&lt;/p&gt;
&lt;p&gt;&amp;ldquo;通常不靠谱。如果受试者是因为病情恶化才退出的，最后一次观察值可能比真实状态好看。LOCF 会把治疗效果高估。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;（后来我翻了文献。LOCF 在某些场景下确实是保守的，但总体容易引入偏倚。多重插补或者当敏感性分析做更稳妥。）&lt;/p&gt;
&lt;h2 id="最后一扇门"&gt;最后一扇门&lt;/h2&gt;
&lt;p&gt;数据库锁定（Database Lock, DB Lock）是数据管理的一个关键节点。&lt;/p&gt;
&lt;p&gt;锁之前：数据可以改。锁之后：冻住了。任何修改都要走变更流程——不是技术上改不了，是程序上要经过层层审批。&lt;/p&gt;
&lt;p&gt;DB Lock 通常发生在所有受试者完成随访、数据清理收尾、锁库前评审通过之后。锁库之后，统计师拉闸开始最终分析，生成临床研究报告（CSR）。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;锁库之后还能改数据吗？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;能。但不叫&amp;quot;改&amp;rdquo;，叫&amp;quot;走变更流程&amp;quot;。物理上数据还是可以动的，只是每次动都要一群人在审批链上签字。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;什么情况下锁库后必须改？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;SAE 数据录入有误。关键数据录入有误，影响主要终点。还有——面试官给我补了一个场景——监管机构在现场核查时要求更正数据。&lt;/p&gt;
&lt;p&gt;这件事我只在书本上瞟到过，面试的时候完全没想起来。坐在对面的面试官不紧不慢地补上了这一刀，我连防御姿势都摆不出来。&lt;/p&gt;
&lt;h2 id="写在最后"&gt;写在最后&lt;/h2&gt;
&lt;p&gt;多中心试验的核心矛盾就一条：控制变异。&lt;/p&gt;
&lt;p&gt;中心间差异不会消失——只能通过设计去预防、通过分析去调整、通过监查去发现。脱落是另一个维度的损耗——受试者走了，信息就永远丢了。能做的只是把它写进样本量计算、写进分析计划、写进最后的报告，而不是假装它不存在。承认不确定性，比假装确定诚实得多。&lt;/p&gt;</description></item><item><title>从Epoch和Element搞懂SDTM</title><link>https://touchingfish.top/2025/sdtm-epoch-element/</link><pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/sdtm-epoch-element/</guid><description>&lt;p&gt;随机化和 AE 是临床试验的骨肉，那 SDTM 大概就是血管——把散落各处的数据串成标准格式，送到监管机构面前。&lt;/p&gt;
&lt;p&gt;SDTM 的全称是 Study Data Tabulation Model，&amp;ldquo;研究数据列表模型&amp;quot;这个译名念起来有点硌嘴。翻译成人话就是：数据提交的标准化格式。&lt;/p&gt;
&lt;p&gt;FDA 要求所有新药申请（NDA）必须以 SDTM 格式交数据。不是建议。&lt;/p&gt;
&lt;h2 id="章节和小节"&gt;章节和小节&lt;/h2&gt;
&lt;p&gt;我啃 SDTM 的时候，第一个绊住我的地方就是 Epoch 和 Element。&lt;/p&gt;
&lt;p&gt;简单说：&lt;/p&gt;
&lt;p&gt;Element（元素） 是试验里最小的连续时间段。有明确的起止条件。比如 Screening（筛选期）——从签知情同意到随机化之前；Drug A Treatment——从第一次给药到最后一次给药；Follow-up（随访期）——从末次给药到出组。每个 Element 有一个代码（ETCD）和全称。&lt;/p&gt;
&lt;p&gt;Epoch（时期） 是更高一层的划分，用来组织不同的 Element。一个 Epoch 可以装一个或多个 Element。&lt;/p&gt;
&lt;p&gt;比如治疗期这个 Epoch 底下可能塞着 Placebo Treatment 和 Drug A Treatment 两个 Element。筛选期就一个 Screening Element。随访期就一个 Follow-up Element。&lt;/p&gt;
&lt;p&gt;（我的记忆方式：Epoch 是大标题，Element 是小标题。一章里可以有多个小节。）&lt;/p&gt;
&lt;h2 id="为什么非得分两层"&gt;为什么非得分两层&lt;/h2&gt;
&lt;p&gt;因为在 SDTM 的数据集里，它们是分开的。&lt;/p&gt;
&lt;p&gt;SE（Subject Elements）数据集记录每个受试者经历了哪些 Element，起止时间是什么。这是连续性的、细颗粒的。&lt;/p&gt;
&lt;p&gt;DS（Disposition，受试者处置）数据集只记关键里程碑。它是摘要性质的。&lt;/p&gt;
&lt;p&gt;一个受试者可能老老实实走完了 Screening → Treatment A → Follow-up 三个 Element——SE 里三个记录。但在 DS 里可能只抓两个节点：Randomized（随机化），Completed Study（完成试验）。DS 不关心你在治疗期待了多久，SE 会告诉你。&lt;/p&gt;</description></item><item><title>把AE和SAE背下来</title><link>https://touchingfish.top/2025/ae-sae-reporting/</link><pubDate>Sat, 15 Feb 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/ae-sae-reporting/</guid><description>&lt;p&gt;不良事件（Adverse Event, AE）大概是临床试验面试里出场率最高的话题。&lt;/p&gt;
&lt;p&gt;不是因为它复杂。是因为它直接连着受试者安全——GCP 的心脏。面试官需要确认你对这一块有基本的本能反应，不是临时翻书背的。&lt;/p&gt;
&lt;h2 id="你以为是边界其实不是"&gt;你以为是边界，其实不是&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Adverse Event (AE)：受试者在试验期间出现的任何不利医疗情况，与研究药物或干预措施未必有因果关系。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;四个字咬在最前面：&amp;ldquo;未必相关&amp;rdquo;。&lt;/p&gt;
&lt;p&gt;什么意思？受试者试验期间出现的任何身体不适——头疼、发烧、血压飙了、平地摔了一跤——不管原因是什么，全部要记录。感冒要记。车祸要记。自己吃坏肚子也要记。&lt;/p&gt;
&lt;p&gt;第一次看到方案里 AE 记录范围的时候，我整个人是懵的。&lt;/p&gt;
&lt;p&gt;我以为 AE 只记跟药物搭边的反应。结果发现，连受试者周末爬山崴了脚，都要老老实实填进系统。&lt;/p&gt;
&lt;p&gt;逻辑是这样的：临床试验的核心任务是评估药物安全性。如果你只记录&amp;quot;看起来可能相关&amp;quot;的事件，那你怎么知道自己有没有漏掉&amp;quot;看起来无关但实际上有关&amp;quot;的事件？事后判断因果关系是统计师和分析师的事情，研究者只管如实记录。&lt;/p&gt;
&lt;p&gt;把判断交给数据，把诚实留给现场。&lt;/p&gt;
&lt;h2 id="五条标准一条红线"&gt;五条标准，一条红线&lt;/h2&gt;
&lt;p&gt;严重不良事件（Serious Adverse Event, SAE）的定义是固定的。五条：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;导致死亡&lt;/li&gt;
&lt;li&gt;威胁生命&lt;/li&gt;
&lt;li&gt;导致住院或延长住院时间&lt;/li&gt;
&lt;li&gt;导致永久性或显著的残疾/功能障碍&lt;/li&gt;
&lt;li&gt;导致先天异常/出生缺陷&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;面试的时候，面试官喜欢出场景题。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;受试者随访期间因为心脏病发作进了 ICU，算不算 SAE？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;算。一条&amp;quot;威胁生命&amp;quot;，一条&amp;quot;导致住院&amp;quot;，双命中。&lt;/p&gt;
&lt;p&gt;再来一题更刁的。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;受试者血压控制不好，医生建议住院观察，但受试者拒绝了。算不算 SAE？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;不算 SAE——但需要记成 AE。&amp;ldquo;导致住院&amp;quot;这个标准的前提是实际发生了住院行为，不是医生动了个念头。&lt;/p&gt;
&lt;p&gt;SAE 报告的时限是二十四小时。&lt;/p&gt;
&lt;p&gt;从研究者获知 SAE 那一刻开始计时。二十四小时内必须报给申办方。申办方收到之后，还有额外的时限向监管机构汇报——但这第一步，二十四小时，雷打不动。&lt;/p&gt;
&lt;p&gt;这个数字是要考试的。&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;面试官经常追着问：你怎么判断 AE 跟药物的相关性？&lt;/p&gt;
&lt;p&gt;这是个有争议的话题。&lt;/p&gt;
&lt;p&gt;最通行的框架是五分法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;肯定相关（Definite）&lt;/li&gt;
&lt;li&gt;很可能相关（Probable）&lt;/li&gt;
&lt;li&gt;可能相关（Possible）&lt;/li&gt;
&lt;li&gt;可能无关（Unlikely）&lt;/li&gt;
&lt;li&gt;无法判断（Unassessable）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;偶尔有人用七分法——在&amp;quot;肯定&amp;quot;和&amp;quot;很可能&amp;quot;之间再多切两级。本质上都是在灰色地带划线。&lt;/p&gt;
&lt;p&gt;判断依据没有客观标尺，主要靠研究者综合几件事：时间关系——是不是用药之后出现的？已知药理——这是说明书上写过的不良反应吗？去激发和再激发——药停了症状就消失了？药又用上了症状就回来了？&lt;/p&gt;
&lt;p&gt;&amp;ldquo;相关性判断为什么重要？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;因为它直接决定安全性分析的结论。如果一份试验报告里出现了大量&amp;quot;肯定相关&amp;quot;的 SAE，那这个药的风险收益比就要重新上秤。救人还是伤人，有时候只差一个相关性评级。&lt;/p&gt;
&lt;h2 id="记录不是一次性的"&gt;记录不是一次性的&lt;/h2&gt;
&lt;p&gt;AE 不是记完就可以翻页的。&lt;/p&gt;
&lt;p&gt;研究者要持续随访，直到三件事之一发生：事件解决了（恢复或稳定），受试者失访了，或者事件的根因确认了——比如确定是受试者自己停药导致的停药后事件。&lt;/p&gt;
&lt;p&gt;结局（outcome）有几种标准记法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解决（Resolved）&lt;/li&gt;
&lt;li&gt;解决但有后遗症（Resolved with sequelae）&lt;/li&gt;
&lt;li&gt;未解决/持续中（Not resolved / ongoing）&lt;/li&gt;
&lt;li&gt;解决中（Resolving）&lt;/li&gt;
&lt;li&gt;致命（Fatal）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;有个容易搞混的地方。&amp;ldquo;致命&amp;quot;和&amp;quot;导致死亡&amp;quot;是两回事。一个 AE 可以状态是&amp;quot;未解决/持续中&amp;rdquo;，但随访期间受试者因为别的原因去世了——它被标注为&amp;quot;致命&amp;quot;结局。死亡本身不是 AE，是一个终点。&lt;/p&gt;</description></item><item><title>随机化，被问到不会为止</title><link>https://touchingfish.top/2025/randomization-details/</link><pubDate>Sat, 08 Feb 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/randomization-details/</guid><description>&lt;p&gt;随机化（Randomization）是临床试验的基石。&lt;/p&gt;
&lt;p&gt;这句话我在面试里说了不下十遍。每次说的时候底气都很足——直到面试官开始往下挖。&lt;/p&gt;
&lt;p&gt;上统计课的时候，老师讲得轻描淡写：随机化就是让混杂变量在组间均匀分布。掷硬币，分两组，完事。我当时觉得，这有什么难的？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sas" data-lang="sas"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rand = rand(&lt;span class="s"&gt;&amp;#34;uniform&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;group&lt;/span&gt; = &lt;span class="k"&gt;if&lt;/span&gt; rand &amp;lt; &lt;span class="m"&gt;0.5&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Treatment&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Control&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一行代码。大样本下组间基线特征自然均衡。简单随机化（Simple Randomization）操作起来也确实这么痛快——每个受试者扔一次骰子，去哪组全凭运气。&lt;/p&gt;
&lt;p&gt;问题出在小样本上。&lt;/p&gt;
&lt;p&gt;四十个人的试验，随机下来可能是 24 比 16。理论上不偏，但统计效能（power）被削了一块——你本来算好需要二十个对照，实际只拿到十六个，power 就缩水了。&lt;/p&gt;
&lt;p&gt;面试官问我小样本用什么方法。&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;区组随机化（Block Randomization）是我准备面试的时候啃得最细的一个点。&lt;/p&gt;
&lt;p&gt;道理不复杂。把受试者切分成若干&amp;quot;区块&amp;quot;，每个区块内部按固定比例分。比如区块大小是 4，比例 1:1——每四个人里一定有两个试验、两个对照。区块内部严格对称。&lt;/p&gt;
&lt;p&gt;这个设计在多中心试验（multi-center trial）里特别好用。A 中心招了三十个人，B 中心只有十个——简单随机化可能让 A 中心变成 20:10，B 中心变成 8:2，摆在一起虽然整体是 1:1，但每个中心内部歪得离谱。区组随机化让每个中心各自维持内部平衡。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;前提是区块大小不被猜到。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果区块固定为 4，研究者看到前三个人的分组就能反推第四个。盲法（blinding）原地作废。&lt;/p&gt;
&lt;p&gt;解法：把区块大小也随机化。可以是 4，也可以是 6，随机抽。&lt;/p&gt;
&lt;p&gt;面试官追着这个点问。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;区组大小固定的话，会有什么后果？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;我顿了三秒。说，可能造成选择偏倚——研究者会看前面几个人的分组，决定要不要让下一个人入组。&lt;/p&gt;
&lt;p&gt;他没接话。&lt;/p&gt;
&lt;p&gt;（后来我琢磨，我的方向大概对了一半。猜到分组确实会打穿盲法。但&amp;quot;决定是否入组&amp;quot;这个说法不准确——入组标准是写死的，研究者不应该有裁量空间。真正怕的是他通过控制入组节奏来影响分组：预测下一个会进试验组，就稍微拖一拖。这更接近实施偏倚而不是选择偏倚。）&lt;/p&gt;
&lt;h2 id="按特征分堆"&gt;按特征分堆&lt;/h2&gt;
&lt;p&gt;分层随机化（Stratified Randomization）解决另一件事。&lt;/p&gt;
&lt;p&gt;有些变量对结局影响很大——年龄、性别、疾病分期。小样本里，纯随机可能让这些变量在组间歪掉。分层做法的思路是：先按重要特征切层（60 岁以下/60 岁以上、男/女），然后在每一层内部再做随机。保证每个分层因素在两组之间是平衡的。&lt;/p&gt;
&lt;p&gt;面试题经常问：&amp;ldquo;分层和区组有什么区别？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;分层按受试者特征来，区组按入组先后来。分层管协变量的平衡，区组管各时间点样本量的平衡。两者不互斥——可以先分层，再在层内跑区组随机化。面试的时候如果能说出这个组合用法，大概能加点分。&lt;/p&gt;
&lt;h2 id="一时语塞"&gt;一时语塞&lt;/h2&gt;
&lt;p&gt;那场面试没有后续。&lt;/p&gt;
&lt;p&gt;我猜是因为答得太虚。概念头头是道，落地一问就露馅。&lt;/p&gt;
&lt;p&gt;面试官最后抛了一个开放式结尾：&amp;ldquo;如果一个中心招募速度特别慢，怎么处理？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;我说，跟中心研究者沟通，了解原因。如果是患者来源问题，调整招募策略或者增加中心。&lt;/p&gt;
&lt;p&gt;他说了一句让我记到现在的话。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;CRO 行业现在收缩得很厉害。很多中心不是招不到患者，是申办方没钱了，主动暂停试验。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;我当场石化。&lt;/p&gt;
&lt;p&gt;背了那么多术语、方法、最佳实践，到头来被一个行业事实干翻了。统计方法能解决的问题是有边界的——资金、政策、市场周期，这些东西不在任何一本 GCP 教材里。&lt;/p&gt;
&lt;p&gt;后来我查了一下，CRO 确实在过冬。不是哪个公司的原因，是整个赛道都在缩。&lt;/p&gt;</description></item></channel></rss>