<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>无监督学习 on Xiang CHEN 陈向</title>
    <link>https://chenxofhit.xyz/tags/%E6%97%A0%E7%9B%91%E7%9D%A3%E5%AD%A6%E4%B9%A0/</link>
    <description>Recent content in 无监督学习 on Xiang CHEN 陈向</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <copyright>Xiang CHEN</copyright>
    <lastBuildDate>Mon, 08 Dec 2025 20:31:38 +0800</lastBuildDate>
    <atom:link href="https://chenxofhit.xyz/tags/%E6%97%A0%E7%9B%91%E7%9D%A3%E5%AD%A6%E4%B9%A0/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>无监督聚类不应“偷看答案”：Spatial-MGCN等方法用 ARI 做早停与模型选择的标签泄露问题（以空间转录组为例）</title>
      <link>https://chenxofhit.xyz/posts/label-leakage/</link>
      <pubDate>Mon, 08 Dec 2025 20:31:38 +0800</pubDate>
      <guid>https://chenxofhit.xyz/posts/label-leakage/</guid>
      <description>&lt;h2 id=&#34;摘要&#34;&gt;摘要&lt;/h2&gt;&#xA;&lt;p&gt;在无监督任务中，用 ARI（Adjusted Rand Index）等外部指标评估聚类与已知标签的一致性是常规做法。但如果在训练阶段用 ARI 选择“最佳 epoch”或早停，就把监督信息引入了本应无监督的流程，造成标签泄露与对标签的过拟合。本文以空间转录组典型代表方法 Spatial-MGCN (&lt;a href=&#34;https://github.com/cs-wangbo/Spatial-MGCN/blob/master/Spatial-MGCN/DLPFC_test.py&#34;&gt;https://github.com/cs-wangbo/Spatial-MGCN/blob/master/Spatial-MGCN/DLPFC_test.py&lt;/a&gt;)的训练脚本为例，指出这一问题，并给出不依赖标签的早停和模型选择策略。&lt;/p&gt;&#xA;&lt;h2 id=&#34;背景ari-的定位与正确用法&#34;&gt;背景：ARI 的定位与正确用法&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ARI 属于外部评估指标，前提是有“ground truth”标签。&lt;/li&gt;&#xA;&lt;li&gt;正确用法：训练完成后作为基准评估；不应参与训练决策（早停/模型选择）。&lt;/li&gt;&#xA;&lt;li&gt;无监督设定下，训练过程不应接触真实标签。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;问题用-ari-驱动早停模型选择的后果&#34;&gt;问题：用 ARI 驱动早停/模型选择的后果&lt;/h2&gt;&#xA;&lt;p&gt;典型错误流程：每个 epoch 先算嵌入，再 KMeans 聚类，与真实标签算 ARI，ARI 变好就保存该 epoch。后果：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;标签泄露：训练过程直接“偷看答案”。&lt;/li&gt;&#xA;&lt;li&gt;对标签过拟合：模型选择被训练集标签驱动，泛化与可信度下降。&lt;/li&gt;&#xA;&lt;li&gt;评价偏乐观：最终报告的 ARI 被同一批标签指导过，性能被夸大。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;空间转录组场景下的风险&#34;&gt;空间转录组场景下的风险&lt;/h2&gt;&#xA;&lt;p&gt;空间组学的“ground truth”常不完备或含噪。若在训练阶段用这些标签做选择：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;结构发现不再纯粹数据驱动；&lt;/li&gt;&#xA;&lt;li&gt;对含噪标签过拟合，削弱生物学解读的可靠性；&lt;/li&gt;&#xA;&lt;li&gt;公平比较与复现性受损。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;更合理的无监督模型选择方案&#34;&gt;更合理的无监督模型选择方案&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;基于训练损失的早停&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;监控总无监督损失（如 ZINB 重构 + 一致性 + 正则）。&lt;/li&gt;&#xA;&lt;li&gt;设定 patience，长期无改进则早停。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;内部聚类指标（无需标签）&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;对嵌入使用 Silhouette、Calinski–Harabasz、Davies–Bouldin 等内部指标。&lt;/li&gt;&#xA;&lt;li&gt;以这些指标最佳的 epoch 作为模型选择依据。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;固定训练步数&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;预设足够轮次到收敛，不用标签驱动中途决策。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;无标签验证拆分&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;若目标含重构，可保留一部分样本/特征做验证损失，而非用标签。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;外部指标仅用于训练后报告&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ARI/AMI/NMI 只在训练结束后计算，用于结果报告，不反向影响模型选择。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;概念性重构用无监督损失保存快照&#34;&gt;概念性重构：用无监督损失保存快照&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;每个 epoch 记录总损失，若创下新低则保存模型参数与当前嵌入（detach + cpu，emb/mean 做 NaN 处理）。&lt;/li&gt;&#xA;&lt;li&gt;训练结束后，再用保存的嵌入做一次聚类。&lt;/li&gt;&#xA;&lt;li&gt;若有真实标签，仅在此时计算 ARI 作为报告，不参与选择。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;代码实现的细节坑点&#34;&gt;代码实现的细节坑点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;邻接矩阵一致性：前向传播与预处理的 nsadj/nfadj 保持一致。&lt;/li&gt;&#xA;&lt;li&gt;保存状态：保存前 detach 并转 CPU，避免显存泄漏。&lt;/li&gt;&#xA;&lt;li&gt;随机性控制：同时固定 NumPy、PyTorch、CUDA 种子。&lt;/li&gt;&#xA;&lt;li&gt;数值稳定：聚类前对嵌入做 NaN 替换或标准化。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;如果确实需要监督&#34;&gt;如果确实需要监督&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;明确声明为半监督/监督；训练-验证拆分；在独立测试集报告指标，避免乐观偏差。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;自查清单&#34;&gt;自查清单&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;训练或早停是否用了真实标签派生的指标？&lt;/li&gt;&#xA;&lt;li&gt;内部评估是否完全不依赖标签？&lt;/li&gt;&#xA;&lt;li&gt;外部指标是否只在训练后计算？&lt;/li&gt;&#xA;&lt;li&gt;选用的嵌入/聚类是否来自无标签的模型选择？&lt;/li&gt;&#xA;&lt;li&gt;随机性与复现实验是否完整记录？&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;最小伪代码无标签早停&#34;&gt;最小伪代码（无标签早停）&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;best_loss &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; float(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;inf&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;best_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;patience &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; P&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;no_improve &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; epoch &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(E):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    emb, mean, losses &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; train_one_epoch()  &lt;span style=&#34;color:#75715e&#34;&gt;# losses[&amp;#39;total&amp;#39;] 含重构/一致性/正则&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; losses[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; total &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; best_loss:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        best_loss &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        no_improve &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        best_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: {k: v&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;detach()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cpu() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; k, v &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; model&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;state_dict()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items()},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;emb&amp;#34;&lt;/span&gt;: np&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;nan_to_num(emb),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;: np&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;nan_to_num(mean),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        no_improve &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; no_improve &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; patience:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 训练完成后再做聚类与外部评估&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load_state_dict(best_state[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;emb_final &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; best_state[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;emb&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; KMeans(n_clusters&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;K)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fit(emb_final)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;labels_&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ari &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adjusted_rand_score(true_labels, labels)  &lt;span style=&#34;color:#75715e&#34;&gt;# 仅报告，不参与训练决策&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ARI (report only): &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;ari&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.3f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;&#xA;&lt;p&gt;无监督聚类的训练阶段不应“偷看”真实标签。用损失或内部聚类指标替代 ARI 驱动的早停/模型选择，能避免标签泄露与过拟合，提升空间转录组等场景下的严谨性与可信度。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
