如何用TSNE可视化VGG16模型输出?14分类模型可视化异常求助
解决VGG16特征TSNE可视化聚类混乱的问题
看起来你遇到的核心问题是标签处理逻辑错误,导致TSNE图的颜色映射完全偏离真实类别,这才出现每个簇都混杂所有颜色的情况。我们一步步来排查修复:
1. 先修正标签的收集逻辑
你的代码里,targets是把每个batch的标签数组直接append成列表,然后用target_ids = range(len(targets))——这相当于用batch的索引当标签,而不是每个样本的真实类别!比如你有10个batch,标签就变成0-9,但你实际有14个类别,每个样本的真实标签根本没被正确用上。
修正方法:把每个batch的标签数组展平后合并成一个一维数组,而不是存成列表:
# 初始化targets为numpy数组,指定类型避免后续类型错误 import numpy as np targets = np.empty((0,), dtype=np.int64) with torch.no_grad(): for step, (t_image, target, classess, image_path) in enumerate(test_loader): t_image = t_image.cuda() # 收集真实标签:合并到全局targets数组 batch_targets = target.data.cpu().numpy() targets = np.append(targets, batch_targets, axis=0) # 提取特征 features = model(t_image) features_np = features.data.cpu().numpy() logits_list = np.append(logits_list, features_np, axis=0)
绘制散点图时,直接用真实的targets数组作为颜色映射依据,替换错误的target_ids:
plt.figure(figsize=(10,8)) scatter = plt.scatter(tsne_results[:,0], tsne_results[:,1], c=targets, cmap=plt.cm.get_cmap("jet", 14), alpha=0.7) plt.colorbar(scatter, ticks=range(14), label='Class') # 手动创建多类别图例(scatter默认图例不友好) from matplotlib.lines import Line2D handles = [Line2D([0], [0], marker='o', color='w', markerfacecolor=plt.cm.jet(i/13), markersize=10, label=f'Class {i}') for i in range(14)] plt.legend(handles=handles, bbox_to_anchor=(1.05, 1), loc='upper left') plt.show()
2. 确认特征提取的正确性
虽然你替换了classifier的第6层为Identity,但可以打印模型结构确认是否替换正确:
print(model.classifier)
正常输出应该显示最后一层是Identity(),且你打印的logits.shape应该是(batch_size, 4096),这说明特征提取是对的。另外,model.eval()已经关闭了Dropout和BatchNorm的训练模式,这部分没问题,但可以再强制确认:
model.eval() for m in model.modules(): if isinstance(m, nn.Dropout): m.eval()
3. TSNE参数微调(可选)
对于1000个样本,你当前的perplexity=10是合理范围,但如果聚类效果仍不理想,可以尝试调整:
- 把
perplexity调到20-30(perplexity通常取样本数的1/50到1/10) - 增加
n_iter到2000或3000,让TSNE迭代更充分 - 调整
learning_rate(默认200,可尝试100-500)
示例:
tsne = TSNE(n_components=2, verbose=1, perplexity=25, n_iter=3000, learning_rate=200)
修正后的完整代码片段
import numpy as np import torch import torch.nn as nn from sklearn.manifold import TSNE import matplotlib.pyplot as plt from matplotlib.lines import Line2D # 加载模型 epoch = 800 PATH = 'vgg16_epoch{}.pth'.format(epoch) checkpoint = torch.load(PATH) model.load_state_dict(checkpoint['model_state_dict']) optimizer.load_state_dict(checkpoint['optimizer_state_dict']) epoch = checkpoint['epoch'] class Identity(nn.Module): def __init__(self): super(Identity, self).__init__() def forward(self, x): return x # 替换classifier最后一层为Identity model.classifier._modules['6'] = Identity() model.eval() # 初始化特征和标签容器 logits_list = np.empty((0, 4096)) targets = np.empty((0,), dtype=np.int64) with torch.no_grad(): for step, (t_image, target, classess, image_path) in enumerate(test_loader): t_image = t_image.cuda() # 收集真实标签 batch_targets = target.data.cpu().numpy() targets = np.append(targets, batch_targets, axis=0) # 提取特征 features = model(t_image) features_np = features.data.cpu().numpy() logits_list = np.append(logits_list, features_np, axis=0) print(f"Batch {step}: 当前特征形状 {features_np.shape}, 累计特征形状 {logits_list.shape}") # 运行TSNE降维 tsne = TSNE(n_components=2, verbose=1, perplexity=25, n_iter=3000) tsne_results = tsne.fit_transform(logits_list) # 绘制可视化图 plt.figure(figsize=(10, 8)) scatter = plt.scatter(tsne_results[:,0], tsne_results[:,1], c=targets, cmap=plt.cm.get_cmap("jet", 14), alpha=0.7) plt.colorbar(scatter, ticks=range(14), label='类别') # 创建自定义图例 handles = [Line2D([0], [0], marker='o', color='w', markerfacecolor=plt.cm.jet(i/13), markersize=10, label=f'类别 {i}') for i in range(14)] plt.legend(handles=handles, bbox_to_anchor=(1.05, 1), loc='upper left') plt.title('VGG16特征TSNE可视化(14分类)') plt.xlabel('TSNE维度1') plt.ylabel('TSNE维度2') plt.show()
额外提示
- 如果聚类效果还是不好,先检查模型的测试准确率:如果模型本身训练不佳,特征自然无法有效区分类别。
- 可以尝试提取更早层的特征(比如classifier的第3层,即第二个Linear层的输出),看看是否聚类效果更好。
- 确保测试集的样本在14个类别中均匀分布,避免某类样本过多导致TSNE结果偏向该类。
内容的提问来源于stack exchange,提问作者AI_NA
相关产品推荐
相关产品推荐

