社交网络分析这套方法体系,骨架其实一直是图论那一套——节点、边、中心性、社区划分。过去我们把用户当成点,把关注、转发、评论当成边,跑一遍社区检测算法,画一张花花绿绿的网络图,报告就能交了。但这套流程有个说不出口的尴尬:图只告诉你“谁和谁连在一起”,却几乎不告诉你“他们到底在聊什么、为什么连在一起”。大语言模型这两年在社交网络分析里真正值钱的地方,不是替你做图计算,而是把长期缺失的语义层给补上了。我前后在几个社区内容分析项目里把大语言模型接进流水线,从最开始的无脑全量调用,到后来的分层抽样加结构化输出,踩过的坑能写满一页纸。这篇就把这套东西完整拆一遍:大语言模型在社交网络分析里到底能干哪几件事、哪些环节纯属浪费预算、数据清洗该怎么做、提示词怎么设计、结果又该怎么评估。不管你是刚接触图分析的数据方向同学,还是已经跑过几轮社区检测、想把手里的文本真正用起来的老手,下面这些应该都能直接拿去改。
1. 为什么要把大语言模型接进社交网络分析这条流水线
1.1 传统方法卡住的三个位置
在动手写第一行调用代码之前,得先把“为什么”想明白,否则很容易变成为了用模型而用模型。我复盘过几个项目,传统社交网络分析卡住的地方基本集中在三处。
第一处是节点缺乏可解释的语义。图算法输出的是编号,比如社区 0 包含 1240 个节点,中心性最高的节点是 U7734。业务方看到这个结果的第一反应永远是“那这个社区是干嘛的”。你要人工去翻这些用户的发言,几百条翻下来眼睛就花了。第二处是边的定义过于单薄。关注关系、回复关系只能表达“有互动”,但互动分很多种:附和、反驳、提问、纯表情。把它们统一当成权重为 1 的边,社区划分的准确性会打折扣,尤其是那种表面上互相回复、实际上在对骂的场景,算法会把它们判成同一个紧密社区。
第三处是结果无法落到自然语言结论。聚类完了,指标算完了,最后还是要人写一段话总结“这个群体的主要诉求是什么”。这一步过去完全靠人肉,一个中型项目光写结论就要两三天。
大语言模型恰好在这三处都能插手:它能给节点打语义标签,能从文本里判断互动性质,也能把结构化结果改写成可读的叙述。但要注意,它不该插手的地方也很明确——图算法本身。让模型去“判断谁是社区中心”是典型的错配,模型没有全局图视野,它看到的只是被截断的上下文。结构的事交给图算法,语义的事交给模型,这条分工线划清楚了,后面所有设计都不会跑偏。
1.2 语义层和结构层怎么缝合
真正有效的做法是让两层互相喂数据,而不是各跑各的。我常用的一条闭环是这样:先用结构化数据建初始图,跑一轮社区检测拿到粗划分;然后把每个社区里发言量最高的一批文本喂给模型,让它输出这个社区的主题描述和典型诉求;接着用这些描述去做社区级别的语义合并——两个社区如果描述高度相似,很可能是算法把同一个群体切碎了,这时候再回到图上去调分辨率参数或者做二次合并。
反过来也成立。模型抽出来的实体(比如某个反复出现的产品名、某个特定说法)可以变成新的边:两个用户如果在最近一段时间里都提到同一个罕见实体,就给他们加一条弱连接。这种“语义边”对发现跨社区的意见领袖特别管用,因为这些人往往不靠关注关系连接,而是靠共同话题连接。
这里有个经验性的比例:在一个十万节点量级的社区里,语义边占总边数的 5% 到 15% 之间比较合适。加太少没效果,加太多会把图搅乱,社区划分反而变得模糊。这个区间不是拍脑袋来的,我做过几轮对比,超过 20% 之后,模块度的提升基本停滞,但计算耗时线性上涨,性价比很差。
1.3 三条现实的技术路线怎么选
落地的时候无非三种选择,各自适用场景差别很大,选错了后期改造成本很高。
| 路线 | 典型做法 | 适合场景 | 主要代价 |
|---|---|---|---|
| 商用接口调用 | 直接调外部模型服务 | 数据量中等、要求快速出结果、团队没有运维资源 | 按量计费,长文本成本高,数据出域需评估 |
| 开源自部署 | 本地部署中小参数模型 | 数据敏感、量大、需要长期反复跑 | 需要显卡资源,效果比大模型弱一档,调优成本高 |
| 混合方案 | 粗筛用开源小模型,精标用商用大模型 | 绝大多数真实项目 | 架构复杂度上升,需要一套调度逻辑 |
我个人在多数项目里选的是混合方案。原因很实在:社交文本里大量内容是“转发”“同意”“+1”这类低信息密度的话,让大模型去处理这些纯属浪费。先用规则加小模型做一遍粗筛,把真正有内容的文本挑出来,可能只占全量的三成,再交给大模型精标。这一步能把调用成本压到原来的三分之一左右,效果损失几乎为零。
2. 数据采集与清洗:脏活决定后面模型的成败
2.1 采集边界要先划清楚
这块必须先说合规。做社交网络分析,只处理公开可见的内容,只采集平台允许获取的数据,涉及个人信息的部分要做脱敏,用户标识在入库的时候就替换成哈希值。这不是走流程,是底线。我在项目里习惯在数据表设计阶段就把用户原始昵称、头像链接这类字段直接砍掉,只留一个内部 ID,后面所有分析都基于这个 ID 跑,报告的截图里也不出现任何可识别个人的信息。
采集范围也要克制。一个常见的误区是“数据越多越好”,实际上社交网络分析对样本的代表性要求高于绝对数量。我做过对比,五万条经过良好清洗的样本,在主题抽取任务上的结论稳定性,明显好于二十万条未清洗的混杂数据。原因很简单,噪声会把模型的注意力带偏,尤其是那种大量重复的营销文本和机器刷屏内容。
2.2 清洗管线上的四个关键节点
清洗这条管线我一般拆成四步,顺序不能乱。
第一步是去重,而且是近似去重而不是精确去重。社交平台上大量内容是同一条信息被反复转发,只是多了几个表情或者改了个标点。用精确匹配只能去掉一部分,剩下的要靠文本相似度。我用的是把文本做归一化(去标点、统一大小写、数字替换成占位符)之后算 n-gram 的 Jaccard 相似度,阈值设在 0.85 左右,超过就归为一簇,只保留簇里最早的那条。这一步通常能砍掉 25% 到 40% 的数据量。
第二步是语言识别。多语言混杂的社区里,如果不区分语言直接扔给模型,标签体系会乱成一锅粥。同一个概念在不同语言里的表达,模型可能给出完全不同的标签。我的做法是按语言分桶,每个桶用独立的提示词模板,最后再把标签映射到统一的中文词表上。
第三步是噪声过滤。纯表情、纯标点、少于五个字符的短文本,直接剔除。还有一类需要单独处理:广告和引流内容。这类文本的特征很明显,包含联系方式的变体写法、大量重复的促销词。用规则过滤掉八九成,剩下的交给模型判断,比让模型从头判要省很多。
第四步是文本规整。把话题标签、@提及、链接这些结构化元素抽出来单独存成字段,正文里只留纯文本。这一步很多人会忽略,但它直接影响后面的提示词质量——正文里塞满链接和标签,模型的注意力会被分散,抽取准确率明显下降。
2.3 采样策略与预算估算
采样这块要算清楚账。假设一条帖子平均 150 个汉字,换算成 token 大约 220 到 260 个(中文的 token 效率比英文低,实际经验值是 1 个汉字约 1.5 到 1.8 个 token,具体看模型的分词器)。如果要做结构化输出,一条样本的输出按 80 到 120 个 token 算。
十万条全量跑的账是这样:输入约 2400 万 token,输出约 1000 万 token,合计 3400 万 token。按当前主流商用服务的量级估算,这个量级的开销已经不能忽略了,而且跑一轮要等很久,中间任何提示词改动都得重来。
我的做法是分层抽样。按活跃度把用户分成高、中、低三层,高层(发言量前 10%)全量处理,因为他们贡献了大部分有价值的内容;中层抽 30%;低层抽 5%。这样总样本量能压到全量的 30% 以内,但覆盖了绝大部分有效信息。抽样的时候记得固定随机种子,否则每次跑出来的样本不一样,结果没法对比。
提示:预算估算一定要在写代码之前做,别等跑了一半发现超支。我的习惯是先拿 500 条做小规模试跑,测出实际的 token 消耗均值,再乘以总量,误差能控制在 10% 以内。
3. 核心任务拆解:模型到底该做哪几件事
3.1 用户画像与兴趣标签抽取
这是最基础也最直接的应用。把某个用户一段时间内的发言聚合起来,让模型输出一组兴趣标签和行为特征。这里的关键是聚合方式,不是简单拼接。
我试过三种。全量拼接效果最差,用户发了三百条,拼起来上万字,模型看到后面忘了前面,输出往往只反映最后几条的内容。随机采样二十条效果好一些,但稳定性差,换个种子结果就变。我现在用的是“分层聚合”:按时间分三段(早期、中期、近期),每段挑五条信息密度最高的(长度适中、包含名词较多的),拼成一段带时间标记的文本,再让模型输出。时间标记很重要,它能捕捉到兴趣变迁,比如一个用户从讨论某款产品转向讨论另一款,这个迁移信息在标签里能体现出来。
标签体系要提前定义好,给模型一个封闭的候选集,而不是让它自由发挥。自由生成的标签看起来丰富,实际上同义词泛滥,“性价比”“划算”“价格合适”会被当成三个不同的标签,后面做聚合分析非常痛苦。我通常会给 30 到 50 个一级标签,允许模型在必要时输出“其他”并附一句说明,再定期人工把这些说明归类回一级标签。
3.2 情感与立场识别
情感分析是老任务,但社交网络场景里它有个特殊性:情感往往不是针对内容本身,而是针对人。同一条帖子下面,不同用户的回复情感可能完全相反,而且这种对立关系本身就是网络分析的重要信号。
我的做法是把任务拆成两层。第一层判情感极性(正向、负向、中性),第二层判立场对象(针对原帖内容、针对某个用户、针对某个话题)。第二层是传统方法做不了的,需要模型理解上下文里的指代关系。
实操上有几个细节。一是反讽识别,这是准确率的重灾区。中文社交场景里的反讽很少有明显标记,模型很容易判错。我的做法是在提示词里给出三到五个反讽例子,同时在输出里加一个置信度字段,置信度低于阈值的结果标出来人工复核。二是中性判定要严格,很多模型倾向于把模糊表达判成中性,导致中性样本占比过高。我会在中性定义里明确写清楚“只有完全不表达态度倾向才算中性,包含轻微倾向的一律归入正负向”。
3.3 社区发现里的语义增强
前面提过,社区发现本身交给图算法。但模型可以在这里做两件很有价值的事。
第一件是给社区命名和描述。算法输出一堆编号,模型看这个社区里最有代表性的若干条发言,输出一段两三百字的描述:这个群体主要在聊什么、他们的关注点集中在哪、有没有明显的时间特征。这段描述可以直接进报告,省掉大量人工。
第二件是辅助判断社区边界是否合理。具体做法是:对每个社区,让模型输出五个关键词,然后计算不同社区关键词集合的重合度。如果两个社区的关键词重合度超过 60%,很可能算法把它们切碎了,需要调参数。这个检查比单纯看模块度更贴近业务直觉,模块度高的划分在语义上未必合理。
还有一个用法我觉得挺巧妙:把社区描述当成节点特征,再跑一次聚类。这样得到的“元社区”往往对应更大的主题板块,对做整体结构梳理很有帮助。
3.4 关系抽取与语义图的构建
从文本里抽关系,是模型比传统 NER 更有优势的地方,因为它能处理隐含关系。比如“我之前也遇到过这种情况,后来换了另一款才好”这句话,表面没有明确的主谓宾三元组,但它隐含了“发帖人使用过某产品并且不满意”这个关系。
我一般抽两类关系:实体共现关系和态度指向关系。前者用于扩展图的边,后者用于给边加权。抽取的时候要给模型一个明确的关系类型清单,别让它自由发挥。我定义的清单大概有十来种:推荐、抱怨、对比、求助、解答、附和、反驳、提问、转述、无关。这个粒度对多数分析场景够用了,再细下去标注一致性会明显下降。
抽出来的关系要落成三元组格式,源实体、关系类型、目标实体,加上来源文本 ID 和置信度。这批数据既能直接建图,也能作为后续查询的索引。
3.5 叙事摘要与结论生成
最后一步是把结构化结果翻译成人能读的东西。这一步看起来简单,实际上最考验提示词设计。直接让模型“总结一下这个社区”得到的东西通常很空,全是“用户讨论热烈、关注度较高”这类废话。
有效的做法是喂结构化数据而不是原始文本。把社区规模、活跃时间段、Top 关键词、代表性发言、情感分布这些算好的指标,以表格形式放进提示词,要求模型基于这些数字写结论,并且每个结论必须引用具体数据。这样出来的文本会扎实很多,比如“该群体在晚间时段活跃度最高,负面表达占比 34%,主要集中在交付时效相关讨论”。
4. 一条可复现的分析流水线
4.1 环境与工具链
我的常规组合是 Python 加几个固定库。文本处理用标准库加正则就够,图计算用 networkx(数据量超过五十万节点会换用更高效的图库),数据处理用 pandas,模型调用统一封装成一个客户端类,方便切换后端。
import hashlib import json import re from dataclasses import dataclass, field import networkx as nx import pandas as pd @dataclass class Post: raw_id: str user_key: str # 已哈希的用户标识 text: str ts: int lang: str = "zh" meta: dict = field(default_factory=dict) def user_hash(uid: str, salt: str = "fixed_salt") -> str: return hashlib.sha256((salt + uid).encode()).hexdigest()[:16] def normalize(text: str) -> str: text = re.sub(r"https?://\S+", " ", text) text = re.sub(r"@\S+", " ", text) text = re.sub(r"#([^#]+)#", r" \1 ", text) text = re.sub(r"\s+", " ", text) return text.strip()这个客户端类的设计要点是:所有调用都走同一个入口,统一处理重试、限流、日志、缓存。缓存尤其重要,同一个文本加同一套提示词,结果应该直接命中缓存而不是重新请求,这一条在调试阶段能省掉大量开销。缓存键用文本哈希加提示词版本号拼起来,提示词一改,缓存自动失效,不会拿到旧结果。
4.2 提示词设计与结构化输出
提示词这块我踩过的坑最多。早期我用的是自然语言描述加“请以 JSON 格式输出”,结果模型经常在 JSON 前后加解释性文字,解析失败率能到 15%。后来改成三个措施,失败率降到 1% 以下。
第一,用 schema 约束。如果所用服务支持结构化输出参数,直接传 JSON Schema,让服务端保证格式。如果不支持,就在提示词里给出一个完整的示例输出,包括字段名、类型、取值范围,示例要比描述有效得多。
第二,要求先思考再输出。让模型在 JSON 之前先写一小段分析过程,放在一个单独的字段里。这个字段对下游没用,但能显著提升抽取质量,因为模型有了推理的中间步骤。我在几个任务上对比过,加了这一步之后,标签准确率大概能提升 8 到 12 个百分点。
第三,温度设低。分类和抽取类任务,温度设 0 到 0.2,保证同一输入多次调用结果稳定。需要生成描述性文本的时候可以调到 0.7,但那是另一类任务,不要混在一起。
PROMPT_TEMPLATE = """你是一名社区内容分析员。请阅读下面的用户发言,完成标注任务。 【发言内容】 {text} 【标注要求】 1. sentiment: 三选一,positive / negative / neutral。 只有完全不表达态度倾向时才判 neutral,含轻微倾向的必须归入正负向。 2. topics: 从给定候选集里选 1-3 个,不要自造新词。 候选集:{topic_list} 3. stance_target: 这条发言的态度指向,可选 original_post / specific_user / general_topic。 4. confidence: 0 到 1 之间的小数,表示你对本次标注的整体把握。 【输出格式】 严格输出一个 JSON 对象,不要输出任何其他文字。 {{"reasoning": "简述判断依据", "sentiment": "", "topics": [], "stance_target": "", "confidence": 0.0}} """注意示例里把 reasoning 放在第一个字段,这是有意的。模型按顺序生成,先出推理再出结论,比反过来质量高。这个顺序上的小技巧,在多个任务上都验证过有效。
4.3 批处理、并发与失败重试
批处理的核心是对失败要宽容。真实环境里失败是常态:网络抖动、限流、偶发的格式错误。我的处理逻辑是三级重试。第一次失败原样重试,第二次失败把温度降到 0 再试,第三次失败就走降级路径——用规则方法给一个兜底标注,同时打上标记,后续人工复核。
并发控制要保守一点。我一开始把并发开到很高,结果是限流触发得更频繁,整体吞吐反而下降。实测下来,并发数控制在服务方限制的六到七成,配合轻微的随机延迟,总体效率最高。这个数不是固定的,不同服务差别很大,要自己压测。
import random import time def call_with_retry(client, prompt, max_attempts=3): for attempt in range(max_attempts): try: resp = client.complete(prompt, temperature=0.0 if attempt else 0.1) return parse_json_safely(resp) except Exception: if attempt == max_attempts - 1: return {"fallback": True, "reason": "max_attempts_reached"} time.sleep((2 ** attempt) + random.uniform(0, 0.5))4.4 图算法那一段怎么跑
拿到语义标注之后,建图和计算这一段是常规操作,但有几个参数值得说一下。
def build_graph(posts, relations, semantic_edges): G = nx.Graph() for p in posts: G.add_node(p.user_key, posts=1) for r in relations: u, v, weight = r["src"], r["dst"], r.get("weight", 1.0) if G.has_edge(u, v): G[u][v]["weight"] += weight else: G.add_edge(u, v, weight=weight) for u, v, w in semantic_edges: if not G.has_edge(u, v): G.add_edge(u, v, weight=w * 0.3, kind="semantic") return G几个要点。语义边的权重我给的是结构边权重的 0.3 倍,这是有意压低,防止语义关联盖过真实的互动强度。社区检测算法方面,小图用 Louvain 就够,大图要注意它的随机性——同一张图跑两次结果可能不一样,模块度差个零点几很正常。我的做法是固定随机种子跑五次,取模块度最高的那次结果,同时在报告里注明这是五次中的最优解。
分辨率的设置也值得调。默认值是 1.0,社区数量往往偏少,切得不够细。做内容分析的时候我会调到 1.2 到 1.5,宁可多切几个再靠语义合并回来,也不要一开始就切得太粗。
4.5 结果可视化与报告输出
可视化这块我不追求花哨,追求能看懂。节点大小按度数,颜色按社区,标签只标度数最高的前二十个,标太多就糊了。社区的名字用前面模型生成的描述来替代编号,这一点对报告可读性提升最大。
报告的结构我固定成四块:整体规模与活跃度概况、社区划分与各社区画像、关键节点的角色分析、语义层面的趋势观察。每块都配数字和代表性发言摘录。摘录一定要脱敏,只保留内容不保留任何可识别的用户信息。
5. 评估、成本控制与那些绕不开的坑
5.1 模型标注质量怎么验证
这一步千万别省。我见过直接把模型输出当结论用的项目,后面被业务方质疑的时候完全拿不出依据。
验证方法我一般用两层。第一层是人工抽检。从每个任务的结果里分层抽 150 到 200 条,人工标注跟模型结果比对,算一致性。情感这类三分类任务,我要求一致率达到 85% 以上才继续往下走,低于这个数说明提示词或者任务定义有问题。第二层是一致性系数。抽样标注完之后算 Cohen's kappa,这个指标能排除掉随机一致的部分,比单纯的准确率可靠得多。0.7 以上算可以接受,0.6 到 0.7 之间要看看是哪类样本拉低的,低于 0.6 就得回去改提示词。
还有一点容易被忽略:不同批次的稳定性。同一批数据,隔一周再跑一次,结果应该基本一致。如果差异很大,通常是提示词里有模棱两可的表述,模型在边界上摇摆。我做过一次这样的检查,发现“包含轻微倾向的一律归入正负向”这句话在不同批次的执行上差异明显,后来改成了更具体的三档描述才稳定下来。
5.2 成本与延时的账怎么算
成本这块前面算过 token,这里补几个实操经验。缓存命中率在迭代阶段能达到 60% 以上,因为大量时间花在反复调试同一批样本上,缓存能大幅压缩调试成本。上线跑全量的时候,命中率会掉到 10% 以下,因为都是新数据。
延时方面,单条样本的往返时间通常在 1 到 3 秒之间,跟输出长度强相关。十万条样本,开 20 个并发,理论耗时接近三个小时,实际因为限流和重试,要按四到五小时估。
一个降本的小技巧:先做文本压缩再送去标注。把一条长文本里的冗余表达、重复的客套话去掉,保留核心信息,长度能压掉三到四成,标注质量基本不受影响。这个压缩本身可以用规则加小模型来做,成本很低。
5.3 常见问题速查表
| 现象 | 最可能的原因 | 处理方式 |
|---|---|---|
| JSON 解析失败率高 | 提示词里没有严格约束输出格式 | 加示例输出,或用服务端 schema 约束 |
| 同一文本多次结果不一致 | 温度过高,或提示词存在模糊表述 | 温度降到 0,把边界规则写具体 |
| 标签体系越跑越散 | 允许模型自由生成标签 | 给封闭候选集,超出走“其他”加说明 |
| 长文本标注质量明显下降 | 超出有效上下文,注意力被稀释 | 分段处理再合并,或先做摘要 |
| 社区划分每次不一样 | 算法本身的随机性 | 固定种子多次跑,取模块度最优 |
| 中性样本占比过高 | 中性定义过宽 | 明确“含轻微倾向必须归入正负向” |
| 批处理吞吐上不去 | 并发太高触发限流 | 降到限制的六到七成,加随机延迟 |
| 语义边加完图变得很乱 | 语义边比例过高或权重过大 | 控制在总边数一成左右,权重压到 0.3 倍 |
| 模型读不懂社区黑话 | 训练语料里没有这类表达 | 提示词里补充三到五个该类表达的解释 |
5.4 踩过的几个真坑
第一个坑是拿模型当图算法用。早期我试过让模型直接判断“这两个用户关系紧密吗”,然后据此建边。结果是一塌糊涂,因为模型看不到全局,它判断的只是这两条文本表面上像不像,完全不知道这两个人在整个网络里的位置。后来老老实实回到图算法,模型只负责抽语义特征,效果立刻正常。
第二个坑是提示词里塞了太多规则。有一版提示词我写了将近两千字,把各种边界情况都列了一遍,结果模型反而抓不住重点,准确率比精简版还低。规则超过八条之后,模型会开始丢三落四。正确的做法是把复杂任务拆成多个简单任务串行跑,每个任务三到四条规则,比一个大而全的提示词管用得多。
第三个坑是忽视了时间维度。社交内容的热点迁移很快,一个标签体系可能两个月就过时了。我有一次用三个月前定的标签集去标新数据,结果“其他”类的占比飙到 40%,因为冒出了很多新话题。后来改成每月做一次标签体系的增量评审,把高频的“其他”说明提升为正式标签,这个问题就缓解了。
第四个坑是过度相信置信度字段。模型给的置信度跟实际准确率的对应关系并不强,它给的 0.9 不代表真有九成把握。我现在只把这个字段当成一个粗筛信号,用来决定哪些样本优先人工复核,而不把它当成质量保证。
5.5 关于这套方法适用边界的个人看法
这套流水线跑下来,我最大的体会是:模型带来的价值是非线性的,它在语义抽取这一环能把效率提升一个量级,但它不改变分析的基本逻辑。社区是不是合理、结论能不能站住脚,仍然取决于数据质量和图算法的参数设置。指望接个大模型就自动出洞察,这个预期大概率会落空。
还有一个容易被忽视的点是人的介入时机。我现在习惯在流水线的三个位置设置人工检查点:清洗完抽样看五十条,确认噪声处理没问题;标注完抽两百条算一致性;报告初稿出来之后通读一遍,看看有没有明显反直觉的结论。这三个检查点加起来花不了半天,但能挡掉绝大部分后期返工。
最后说个好用的做法。给每个社区不只生成一段描述,而是生成三个版本:一句话概括、一段话描述、五个关键词。这三份产物在不同场景下用处不一样——关键词用来做跨社区对比和相似度计算,一句话概括用来做图表标签,一段话描述直接进报告正文。一次性生成三份的输出成本比三次单独调用低得多,因为可以放在同一个请求里。这个做法在我经手的项目里已经成了标配,写进模板之后每次都能直接复用。