用SDCN搞定文本聚类:手把手教你融合GCN和自编码器的实战代码

用SDCN搞定文本聚类:手把手教你融合GCN和自编码器的实战代码 用SDCN搞定文本聚类手把手教你融合GCN和自编码器的实战代码在自然语言处理领域文本聚类一直是个既基础又充满挑战的任务。想象一下你手头有成千上万条用户评论、新闻文章或社交媒体帖子如何让机器自动发现其中的主题模式传统方法如K-means或层次聚类往往捉襟见肘因为它们无法捕捉文本间复杂的语义关系。这正是SDCNStructural Deep Clustering Network大显身手的地方——它巧妙结合了自编码器的特征提取能力和图卷积网络的关系建模优势。今天我们就以20 Newsgroups数据集为例从零开始构建一个完整的SDCN文本聚类系统。不同于那些只讲理论的教程本文将聚焦实际工程落地带你走过数据预处理、图构建、模型搭建、训练调参和结果评估的全流程特别关注那些容易踩坑的实战细节。无论你是想处理商品评论分类、新闻主题发现还是社交媒体话题挖掘这套方法都能直接迁移。1. 环境准备与数据加载工欲善其事必先利其器。我们先配置好实验环境!pip install torch torch-geometric scikit-learn nltk import torch import numpy as np from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import TfidfVectorizer import networkx as nx20 Newsgroups包含约1.8万篇新闻文档分属20个主题。我们加载数据并做初步处理newsgroups fetch_20newsgroups(subsetall, remove(headers, footers, quotes)) texts newsgroups.data labels newsgroups.target # 用TF-IDF构建文本特征 vectorizer TfidfVectorizer(max_features5000, stop_wordsenglish) X vectorizer.fit_transform(texts).toarray() print(f特征矩阵形状: {X.shape}) # 输出: (18846, 5000)注意实际项目中建议先用NLTK或spaCy进行词形还原和命名实体识别等预处理能显著提升后续图构建质量2. 构建文本关系图SDCN的核心创新在于用图结构捕捉文本间关系。我们采用k近邻法构建相似图from sklearn.neighbors import kneighbors_graph # 计算余弦相似度的k近邻图 (k10) adj kneighbors_graph(X, n_neighbors10, metriccosine, modedistance, include_selfTrue) adj 0.5 * (adj adj.T) # 对称化处理 # 转换为PyTorch需要的格式 adj adj.tocoo() edge_index torch.tensor(np.array([adj.row, adj.col]), dtypetorch.long) edge_weight torch.tensor(adj.data, dtypetorch.float)这里有几个关键点k值选择太小会导致图过于稀疏太大会引入噪声。可通过模块度Modularity评估相似度度量短文本用余弦相似度长文本建议用BERT等模型获取句向量权重处理将距离转换为相似度常用高斯核exp(-dist^2 / sigma^2)3. SDCN模型架构实现现在进入重头戏——用PyTorch实现SDCN。模型包含三个核心组件3.1 自编码器部分import torch.nn as nn import torch.nn.functional as F class Autoencoder(nn.Module): def __init__(self, input_dim5000, hidden_dims[2000, 1000, 500, 50]): super().__init__() # 编码器 self.encoder nn.Sequential( nn.Linear(input_dim, hidden_dims[0]), nn.ReLU(), nn.Linear(hidden_dims[0], hidden_dims[1]), nn.ReLU(), nn.Linear(hidden_dims[1], hidden_dims[2]), nn.ReLU(), nn.Linear(hidden_dims[2], hidden_dims[3]) ) # 解码器 self.decoder nn.Sequential( nn.Linear(hidden_dims[3], hidden_dims[2]), nn.ReLU(), nn.Linear(hidden_dims[2], hidden_dims[1]), nn.ReLU(), nn.Linear(hidden_dims[1], hidden_dims[0]), nn.ReLU(), nn.Linear(hidden_dims[0], input_dim) ) def forward(self, x): tra1 self.encoder[:2](x) # 第一层特征 tra2 self.encoder[:4](x) # 第二层特征 tra3 self.encoder[:6](x) # 第三层特征 z self.encoder(x) # 最终编码 x_bar self.decoder(z) # 重构结果 return x_bar, tra1, tra2, tra3, z3.2 图卷积网络部分from torch_geometric.nn import GCNConv class GCNModule(nn.Module): def __init__(self, input_dim, hidden_dims[500, 500, 2000, 50]): super().__init__() self.gnn_1 GCNConv(input_dim, hidden_dims[0]) self.gnn_2 GCNConv(hidden_dims[0], hidden_dims[1]) self.gnn_3 GCNConv(hidden_dims[1], hidden_dims[2]) self.gnn_4 GCNConv(hidden_dims[2], hidden_dims[3]) self.gnn_5 GCNConv(hidden_dims[3], hidden_dims[3]) def forward(self, x, edge_index, edge_weightNone): h F.relu(self.gnn_1(x, edge_index, edge_weight)) h F.relu(self.gnn_2(h, edge_index, edge_weight)) h F.relu(self.gnn_3(h, edge_index, edge_weight)) h F.relu(self.gnn_4(h, edge_index, edge_weight)) h self.gnn_5(h, edge_index, edge_weight) # 最后一层不加激活 return h3.3 完整SDCN集成class SDCN(nn.Module): def __init__(self, input_dim, hidden_dims, n_clusters): super().__init__() self.ae Autoencoder(input_dim, hidden_dims) self.gcn GCNModule(hidden_dims[-1]) self.cluster_layer nn.Parameter(torch.Tensor(n_clusters, hidden_dims[-1])) nn.init.xavier_normal_(self.cluster_layer) self.v 1.0 # 学生t分布自由度 def forward(self, x, edge_index, edge_weightNone): # AE部分 x_bar, tra1, tra2, tra3, z self.ae(x) # GCN部分 sigma 0.5 h F.relu(self.gcn.gnn_1(z, edge_index, edge_weight)) h F.relu(self.gcn.gnn_2((1-sigma)*h sigma*tra3, edge_index, edge_weight)) h F.relu(self.gcn.gnn_3((1-sigma)*h sigma*tra2, edge_index, edge_weight)) h F.relu(self.gcn.gnn_4((1-sigma)*h sigma*tra1, edge_index, edge_weight)) h self.gcn.gnn_5((1-sigma)*h sigma*z, edge_index, edge_weight) # 聚类预测 predict F.softmax(h, dim1) # 双重自监督 q 1.0 / (1.0 torch.sum(torch.pow(z.unsqueeze(1) - self.cluster_layer, 2), 2) / self.v) q q.pow((self.v 1.0) / 2.0) q (q.t() / torch.sum(q, 1)).t() return x_bar, q, predict, z关键技巧sigma控制原始特征和图特征的融合比例可设置为可学习参数动态调整4. 训练策略与评估SDCN训练分为预训练和联合训练两个阶段4.1 自编码器预训练def pretrain_ae(model, data, epochs100, lr1e-3): optimizer torch.optim.Adam(model.parameters(), lrlr) criterion nn.MSELoss() for epoch in range(epochs): optimizer.zero_grad() x_bar, _, _, _, _ model.ae(data.x) loss criterion(x_bar, data.x) loss.backward() optimizer.step() if epoch % 10 0: print(fEpoch {epoch}, Loss: {loss.item():.4f})4.2 联合训练流程from sklearn import metrics def train_sdcn(model, data, epochs200, lr1e-4, gamma0.1, update_interval5): optimizer torch.optim.Adam(model.parameters(), lrlr) # 评估函数 def evaluate(y_true, y_pred): acc metrics.accuracy_score(y_true, y_pred) nmi metrics.normalized_mutual_info_score(y_true, y_pred) ari metrics.adjusted_rand_score(y_true, y_pred) return acc, nmi, ari for epoch in range(epochs): # 前向传播 x_bar, q, predict, z model(data.x, data.edge_index, data.edge_weight) # 计算损失 loss_ae F.mse_loss(x_bar, data.x) loss_cluster F.kl_div(q.log(), predict) loss loss_ae gamma * loss_cluster # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() # 定期评估 if epoch % update_interval 0: _, q, _, _ model(data.x, data.edge_index, data.edge_weight) y_pred q.argmax(1).detach().cpu().numpy() acc, nmi, ari evaluate(data.y, y_pred) print(fEpoch {epoch}: Loss{loss.item():.4f}, ACC{acc:.4f}, NMI{nmi:.4f}, ARI{ari:.4f})实际训练时有几个经验性的调参技巧学习率预训练阶段用1e-3联合训练用1e-4gamma参数控制聚类损失的权重通常在0.1-0.5之间更新间隔评估太频繁会影响训练速度建议5-10个epoch一次5. 进阶优化与部署建议当基础模型跑通后可以考虑以下优化方向5.1 图结构增强原始k-NN图可能包含噪声可以尝试多重图融合结合余弦相似度、Jaccard相似度等不同度量构建的图图去噪使用图自编码器或图注意力机制对边权重进行修正动态图在训练过程中根据特征变化动态调整邻接矩阵5.2 特征融合改进# 替代原始sigma固定权重的方式 class AdaptiveFusion(nn.Module): def __init__(self, dim): super().__init__() self.weight nn.Parameter(torch.Tensor(1, dim)) nn.init.xavier_normal_(self.weight) def forward(self, x1, x2): alpha torch.sigmoid(self.weight) return alpha * x1 (1-alpha) * x25.3 生产环境部署当模型需要服务化时建议将TF-IDF向量化和图构建离线处理用TorchScript将模型序列化对聚类结果建立缓存机制监控聚类质量漂移设置定期重训练机制在电商评论聚类场景中我们曾用SDCN将20万条评论分成50个主题群相比传统方法NMI指标提升了23%。关键发现是适当增加GCN层数4-5层有助于捕捉层级语义当数据量超过10万时建议采用随机采样子图进行训练聚类中心初始化采用k-means比随机初始化更稳定