chundev
日期:2026-04-24 領域: 社群平台協調行為(coordinated inauthentic behavior, CIB)偵測
Count-based 的協調偵測(兩人一起爆推、兩人同 IP、兩人共推同篇)有個不易察覺的結構性缺陷:使用者活躍度服從 power-law 分佈,top 1% 的「閒人 / power user」會跟平台上幾乎所有活躍帳號產生高 co-occurrence,在原始分數上壓過真正的協調群。這是標準的 base-rate fallacy 的具體形態。
解法三層可組合:(A)UI 暴露作者活躍度讓分析者肉眼折算;(B)score 正規化,Jaccard / cosine / association strength 各有理論根據;(C)pre-filter 排掉 top percentile。LLR (G²)、TF-IDF 加權、push-direction z-score 這些已經對 marginal 做校正的方法不需要額外處理;raw count 類的必須。
Facebook 在 2018 年正式提出 “coordinated inauthentic behavior” (CIB) 一詞,定義為「一群帳號的行為出現異常、可疑、或統計上不預期的相似性」(Rogers & Righetti, 2025)。
文獻上多數 CIB 偵測用的是 co-occurrence-based detection:
| 方法 | 出處 | 訊號本質 |
|---|---|---|
| IP coincidence (CopyCatch) | Beutel et al. 2013 (WWW) | count of IP co-visits |
| SynchroTrap | Cao et al. 2014 (NSDI) | count of synchronized actions in session windows |
| Hashtag co-occurrence | Pacheco et al. 2021 (ICWSM) | TF-IDF weighted shared hashtags |
| Retweet coordination | Nizzoli et al. 2021 | count of shared retweets |
| Co-like behavior | Giglietto 2023 | normalized shared likes |
上面這些都有一個共同問題:原始 count 容易被 base rate 主宰,需要不同程度的 normalization。下面說明為什麼。
假設兩個帳號 A 和 B 在某時間窗內獨立地各自行動:
在獨立假設下,兩人 co-occurrence(同一篇文都留言)的期望值:
\[E[\text{cooc}_{AB}] = N \cdot \frac{k_A}{N} \cdot \frac{k_B}{N} = \frac{k_A \cdot k_B}{N}\]關鍵洞察:期望 co-occurrence 跟 $k_A \cdot k_B$ 呈線性。如果 A 是閒人($k_A = 400$)、B 是平均使用者($k_B = 20$),在 $N = 1000$ 下獨立情況就該有 $8$ 次 co-occurrence — 不是隨機異常,是預期中。
原始分數 $k_{AB}$ 直接拿來排序,必然讓 (閒人, 任何人) 的 pair 排進 top;而真正的協調群 (協調者, 協調者) — 他們各自活躍度可能不高($k = 10$)但異常集中在少數文章 — 反而分數低被淹沒。
這就是 base-rate fallacy 在協調偵測裡的具體形態:忽略基線發生率,誤把「活躍」當「協調」。
不是任何平台特有。線上平台的使用者活躍度服從 power-law 分佈(Muchnik et al. 2013, Scientific Reports):
這個分佈的尾巴(極右端)不是離群值、是系統性存在。他們的行為特徵(高頻、廣泛、隨機)在 co-occurrence 矩陣上看起來就跟協調群類似 — 但本質完全不同。
Muchnik 的論文把這點講得很直白:「heterogeneous human activity distribution 是 power-law degree distribution 的因,不是果。」意思是:我們在偵測中看到 degree(閒人) ≈ degree(協調群),不是巧合,是活躍度分佈的直接產物。
判斷原則:訊號公式裡有沒有 marginal(邊際)項?
| 方法 | 形式 | 是否隱含校正 | 備註 |
|---|---|---|---|
| Raw co-occurrence count | $k_{AB}$ | ❌ 無 | base-rate 主宰 |
| Jaccard | $k_{AB} / (k_A + k_B - k_{AB})$ | ✅ 有 | set-theoretic |
| Cosine (Salton) | $k_{AB} / \sqrt{k_A \cdot k_B}$ | ✅ 有 | set-theoretic |
| Dice | $2 k_{AB} / (k_A + k_B)$ | ✅ 有 | set-theoretic |
| Association strength | $k_{AB} \cdot N / (k_A \cdot k_B)$ | ✅ 有 | probabilistic |
| TF-IDF weighted cooc | $\Sigma \text{idf}(i) \cdot [A, B \in i]$ | ⚠️ 部分 | 處理物件熱度、不處理作者活躍 |
| LLR / G² (Dunning 1993) | 似然比 | ✅ 完整 | 統計檢定,含 expected value |
| Push-direction z-score | $(k - E[k]) / \sigma$ | ✅ 完整 | 顯式 normalize |
van Eck & Waltman 2009(JASIST)做過系統性比較,結論:association strength 理論上最優(唯一的 probabilistic 度量),但 cosine / Jaccard 作為 set-theoretic 近似在工程上夠用。他們建議 scientometric 用 association strength,bibliographic similarity 用 cosine。
最便宜、最安全。在 author inspector 展示:
<Badge>發文 {articleCount}</Badge>
<Badge>留言 {commentCount}</Badge>
<Badge>觸及文章 {distinctArticlesCommented}</Badge>
{distinctArticlesCommented >= 100 && (
<Badge variant="destructive">⚠ 疑似 power user(閒人 loner)</Badge>
)}
優點: 零演算法風險、看得到歷史數據。缺點: 不能驅動自動下游決策(P/R 計算、auto-label)。
把 raw count 換成一個 marginal-aware 的相似度度量。實務上四個選項的工程考量:
| 方法 | 公式 | 計算 | 何時選 |
|---|---|---|---|
| Cosine | $k_{AB} / \sqrt{k_A \cdot k_B}$ | O(1) | 偏好幾何直覺;對稱;適合嵌入距離類比 |
| Jaccard | $k_{AB} / (k_A + k_B - k_{AB})$ | O(1) | 偏好集合直覺;union 有 intuition |
| Dice | $2 k_{AB} / (k_A + k_B)$ | O(1) | 跟 Jaccard 單調等價($D = 2J/(1+J)$),挑一個就好 |
| Association strength | $k_{AB} \cdot N / (k_A \cdot k_B)$ | 需要 $N$ | 最強理論依據;顯示「超出期望幾倍」 |
工程選 cosine 家族(除以 $\sqrt{k_A \cdot k_B}$):
export function activityNormalizer(
activities: Map<string, number>,
a: string,
b: string,
): number {
const actA = activities.get(a) ?? 0;
const actB = activities.get(b) ?? 0;
if (actA === 0 || actB === 0) return 1;
return 1 / Math.sqrt(actA * actB);
}
const pairScore = rawCount * activityNormalizer(activities, a, b);
為什麼 cosine 而非 association strength? 後者需要知道 “universe” 大小 $N$(可能是時間窗內的文章總數 / 留言總數,依訊號而定),且在 count 稀疏時方差大。cosine 是較穩健的近似。如果未來要更嚴格的統計意義(例如要設顯著性門檻),再升級到 association strength。
具體效果:
| 情境 | $k_A$ | $k_B$ | raw count | cosine score |
|---|---|---|---|---|
| 小眾協調 | 10 | 10 | 5 | 5/10 = 0.50 |
| 中階閒人巧遇 | 100 | 100 | 5 | 5/100 = 0.05 |
| 骨灰閒人巧遇 | 400 | 400 | 5 | 5/400 = 0.0125 |
40 倍差距把協調訊號拉上來,真實情境下效果更顯著(協調群常有 $k_{AB} \gg 5$)。
在 pair 計算前就排除 top 1%:
export function findPowerUsers(
activities: Map<string, number>,
percentile: number,
): Set<string> {
if (activities.size === 0) return new Set();
const values = Array.from(activities.values()).sort((a, b) => a - b);
const idx = Math.min(
values.length - 1,
Math.floor((percentile / 100) * values.length),
);
const threshold = values[idx]!;
const result = new Set<string>();
for (const [authorId, count] of activities) {
if (count >= threshold) result.add(authorId);
}
return result;
}
理論: power-law 尾巴佔總活動量的比例異常高。排除 top 1% 通常能去除總 pair 候選的 30-50%(視分佈斜率),計算量和噪音同時被壓下。
風險: 協調群內可能混入一個「本來就很活躍」的成員(例如招募自真人而非帳號農場)。這人被排除會讓整個群偵測分數降低。
緩解策略:
輸入(comments, IP events, etc.)
│
▼
[C] 排除 top 1% activity 作者(僅 count-based 訊號)
│
▼
pair 計算
│
▼
[B] score ← raw × (1 / √(actA × actB))(僅 count-based 訊號)
│
▼
Fusion + community detection (Louvain / Leiden)
│
▼
[A] UI 顯示每 author activity,標記疑似 power user
│
▼
分析者最終判斷
各層擋的問題層次不同:
單做一層都有漏;三層串起來噪音下降顯著。
Base-rate 問題在資訊檢索與異常偵測早就有成熟解法:
TF-IDF(Spärck Jones 1972)是 document-side 的 base-rate 校正:稀有詞權重高、熱詞權重低。但 TF-IDF 只處理被檢索物件的熱度,沒處理「觀察者/查詢者」的活躍度。此處談的 activity normalization 是 觀察者側 的類比 — TF-IDF 的另一半。
| Bayes 推論:$P(\text{coord} | \text{cooc}) = P(\text{cooc} | \text{coord}) \cdot P(\text{coord}) / P(\text{cooc})$。如果 $P(\text{cooc})$ 因為活躍度天生就高,後驗會被壓低。這是教科書級的 base-rate fallacy。 |
所以 activity normalization 不是 PTT 特有、不是 CIB 偵測特有;它是任何基於 co-occurrence 的度量都要面對的基礎議題。
實話:坐在書房寫 code 很難想到。活躍度分佈的 power-law 性質是學過的理論,但「它會具體長成一堆 size-50 的大 blob、成員之間看不出關聯」這種肉眼症狀得實際跑幾週才會注意到。
這次的發現路徑有點有趣:讓 LLM 每天讀一次 snapshot 寫分析報告。某天它在「後續觀察」段寫:
temporal-burst 的 pairCount 已達 10000(疑似硬性上限),代表它可能被截斷了。…評估是否要…改用 top-K by score 篩選,避免低品質的 burst pair 被強制納入、稀釋 fusion 品質。
進程式碼一看,確實沒做 per-author activity 正規化。一個老用戶跟 30 個人都有 5+ 共同 burst 是太容易達成的 base case。
這個元方法論:AI 看資料找 anomaly 比人肉 review 便宜很多,尤其在你自己對資料分佈還沒直覺的早期階段。