首页 > 其他分享 >【神经网络训练过程可视化】

【神经网络训练过程可视化】

时间:2024-12-26 14:56:35浏览次数:10  
标签:plt 训练 image hook 神经网络 可视化 cam activations self

一、直方图可视化数据分布

1. 知识介绍

在PyTorch 模型的每一层注册一个 forward hook,从而能够捕获每层的输出

简单列表存储形式(只能顺序查看每层输出,下文会有改进版用字典将层名字和层输出值对应)

activations = []

def hook_fn(module, input, output):
    activations.append(output.detach())  # 记录输出并去除计算图
# 注册 hook
for layer in model.children():
    layer.register_forward_hook(hook_fn)
#列表存储版本 可视化
for i, activation in enumerate(activations):
    plt.figure(figsize=(6, 4))
    plt.hist(activation.numpy().flatten(), bins=100)
    plt.title(f'Layer {i + 1} Activation Distribution')
    plt.xlabel('Activation Value')
    plt.ylabel('Frequency')
    plt.show()

1. 什么是 Hook?

PyTorch 中,hook 是一种机制,允许你在模型的前向传播(forward或反向传播backward过程中插入自定义操作。具体来说,forward hook 允许你在每一层的输出被计算之后,执行一些额外的操作,比如记录激活值、修改输出等。

2.model.children() 的作用

model.children() 是一个 PyTorch 中的迭代器,用来返回模型中的每个子模块(子层)。对于一个 nn.Module(即神经网络模型),它的子模块可以是层(例如 nn.Linear,nn.Conv2d)或者其他子网络(例如子模型)。model.children()返回的是这些子模块的迭代器,它可以让你遍历模型中的每一层。

3.  register_forward_hook的作用

register_forward_hook是一个方法,它可以在你对模型进行前向传播时,将一个 hook 函数hook_fn注册到每一层。这个函数会在每次经过该层时被调用,从而允许你对输出数据进行进一步的处理、修改或记录。

2. 代码实战 

2.1 关键步骤

1. 捕获激活值
  • 使用 forward_hook 在前向传播时捕获每一层的激活值,并存储在字典 activations 中,键为层的名字,值为层的输出。
def hook_fn(module, input, output):
    for name, layer in model.named_children():
        if layer == module:  # 如果当前层匹配
            activations[name] = output.detach()  # 将输出存储到字典中
  •  注册 forward_hook:使用 register_forward_hook 方法
for layer in model.children():
    layer.register_forward_hook(hook_fn)

 解释hook_fn 每层的前向传播时被调用,它确保捕获全连接层(fc1, fc2, fc3)的激活值。

2. 激活值分布的直方图可视化(单独显示每层的分布)
  • 遍历每一层的激活值,并将其转化为一维数组,通过 plt.hist() 生成直方图,展示激活值的分布:

def plot_activation_distributions(activations):
    for i, (layer, activation) in enumerate(activations.items()):
        plt.figure(figsize=(10, 6))
        plt.hist(activation.cpu().numpy().flatten(), bins=50, alpha=0.7, color='blue')
        plt.title(f'Activation distribution at layer {layer}')
        plt.xlabel('Activation value')
        plt.ylabel('Frequency')
        plt.grid(True)
        plt.show()

解释

  • activation.cpu().numpy().flatten() 将多维张量拉平为一维数组,便于绘制直方图。
  • 通过直方图,观察模型不同层的激活值是否存在较多的饱和情况(例如接近 0 或 1),帮助诊断网络是否正常工作。
 3. 激活值分布的子图可视化(同时显示多个层的分布)
  • 使用 plt.subplots 创建多个子图,将每一层的激活分布绘制在同一张图中:

fig, axes = plt.subplots(1, len(activations), figsize=(15, 5))

for i, (name, activation) in enumerate(activations.items()):
    axes[i].hist(activation.numpy().flatten(), bins=50, alpha=0.75)
    axes[i].set_title(f'Activation Distribution ({name})')
    axes[i].set_xlabel('Activation Value')
    axes[i].set_ylabel('Frequency')

plt.tight_layout()
plt.show()

解释

  • 每个子图显示一层的激活分布,通过对比不同层的激活值分布,可以分析网络在不同深度的特征提取效果。
  • plt.subplots 按照层的数量自动生成相应数量的子图,整齐排列。

2.2 完整代码

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

class SimpleMLP(nn.Module):
    def __init__(self):
        super(SimpleMLP, self).__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 10)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = SimpleMLP()
input = torch.randn(1, 784)

#用字典形式存储
activations = {}

def hook_fn(module, input, output):
    for name, layer in model.named_children():
        if layer == module:  # 如果层对象和当前的模块对象匹配
            activations[name] = output.detach()  # 使用层的名字作为键,存储输出

for layer in model.children():
    layer.register_forward_hook(hook_fn)

output = model(input)

for layer_name, activation in activations.items():
    print(f"{layer_name} activation shape: {activation.shape}")

# 1.一张张显示
def plot_activation_distributions(activations):
    for i, (layer, activation) in enumerate(activations.items()):
        plt.figure(figsize=(10, 6))
        plt.hist(activation.cpu().numpy().flatten(), bins=50, alpha=0.7, color='blue')
        plt.title(f'Activation distribution at layer {layer}')
        plt.xlabel('Activation value')
        plt.ylabel('Frequency')
        plt.grid(True)
        plt.show()

# 绘制每一层的激活分布
plot_activation_distributions(activations)

# 2.排列好,子图显示

fig, axes = plt.subplots(1, len(activations), figsize=(15, 5))
# 如果只有一个子图,axes 不是数组,要进行特殊处理
if len(activations) == 1:
    axes = [axes]

# 绘制每一层的激活分布
for i, (name, activation) in enumerate(activations.items()):
    axes[i].hist(activation.numpy().flatten(), bins=50, alpha=0.75)
    axes[i].set_title(f'Activation Distribution ({name})')
    axes[i].set_xlabel('Activation Value')
    axes[i].set_ylabel('Frequency')

plt.tight_layout()
plt.show()

代码效果:

 

二、热力图可视化关注重点

1.知识介绍

热力图确实是针对卷积层的,因为卷积层的输出通常是多个通道的二维特征图,而普通全连接层(如MLP)的输出是一个一维向量,无法直接生成热力图。

这种方法主要用于卷积神经网络(CNN),帮助我们理解模型在处理图像时关注的特征区域,特别是在分类任务中。通过热力图,我们可以可视化网络在进行预测时,关注的是输入图像的哪些部分。常用的可视化方法有 Grad-CAM 和 Guided Grad-CAM。

Grad-CAM

Grad-CAM(Gradient-weighted Class Activation Mapping)是一个基于梯度的热力图生成方法,它利用最后一层卷积层的梯度信息,生成一个反映网络关注区域的热力图。

2. 代码实战 

2.1 关键步骤 

(1)捕获激活值和梯度

  • 激活值(activations):记录卷积层的输出。
  • 梯度值(gradients):记录目标类别在反向传播时对卷积层输出的梯度。
def forward_hook(module, input, output):
    activations[module] = output

def backward_hook(module, grad_input, grad_output):
    gradients[module] = grad_output[0]

2. 计算梯度权重

  • 对每个通道的梯度取均值,生成通道权重,表示每个特征图对目标类别的贡献:
weights = torch.mean(gradient, dim=(1, 2))

3. 生成 Grad-CAM 热力图

  • 用通道权重加权激活值,得到二维热力图
cam = torch.zeros(activation.shape[1:], dtype=torch.float32)
for i, w in enumerate(weights):
    cam += w * activation[i]
cam = F.relu(cam)
cam = (cam - cam.min()) / (cam.max() - cam.min())

4. 叠加热力图到原图

  • 使用 cv2.addWeighted 将 Grad-CAM 热力图叠加到原始图片上,实现最终可视化效果:
overlayed_image = cv2.addWeighted(original_image, 1 - alpha, heatmap, alpha, 0)

2.2 完整代码

import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import cv2

# 定义一个简单的 CNN 模型
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
        self.fc1 = nn.Linear(7*7*64, 1024)
        self.fc2 = nn.Linear(1024, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = self.pool(x)
        x = F.relu(self.conv2(x))
        x = self.pool(x)
        x = x.view(x.size(0), -1)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 实例化模型
model = SimpleCNN()

# 注册 Hook 捕获激活值和梯度
activations = {}
gradients = {}

def forward_hook(module, input, output):
    activations[module] = output

def backward_hook(module, grad_input, grad_output):
    gradients[module] = grad_output[0]

# 注册 Hook 在目标层
target_layer = model.conv2
target_layer.register_forward_hook(forward_hook)
target_layer.register_backward_hook(backward_hook)

# 加载图片并预处理
def preprocess_image(image_path):
    img = Image.open(image_path).convert('L')  # 转为灰度图
    transform = transforms.Compose([
        transforms.Resize((28, 28)),  # 调整大小为 28x28
        transforms.ToTensor(),       # 转为 Tensor 并归一化到 [0, 1]
    ])
    img_tensor = transform(img).unsqueeze(0)  # 增加批次维度
    return img_tensor, img

# 生成 Grad-CAM 热力图
def generate_gradcam(model, input_image, target_class):
    model.eval()

    # 前向传播
    output = model(input_image)

    # 计算目标类别的梯度
    model.zero_grad()
    class_score = output[0, target_class]  # 选择目标类别
    class_score.backward()

    # 获取目标层的激活值和梯度
    activation = activations[target_layer].squeeze(0)  # Shape: (C, H, W)
    gradient = gradients[target_layer].squeeze(0)      # Shape: (C, H, W)

    # 计算 Grad-CAM 权重
    weights = torch.mean(gradient, dim=(1, 2))  # 对每个通道的梯度取均值
    cam = torch.zeros(activation.shape[1:], dtype=torch.float32)  # Shape: (H, W)

    # 加权求和
    for i, w in enumerate(weights):
        cam += w * activation[i]

    # 通过 ReLU 去掉负值
    cam = F.relu(cam)

    # 归一化到 [0, 1]
    cam = (cam - cam.min()) / (cam.max() - cam.min())
    return cam

# 将热力图叠加到原始图片上
def overlay_heatmap_on_image(cam, original_image, alpha=0.5, cmap='jet'):
    cam = cam.detach().cpu().numpy()  # 转为 numpy
    cam = Image.fromarray(np.uint8(255 * cam)).resize(original_image.size, Image.BICUBIC)  # 调整到原图大小
    cam = np.array(cam)

    # 将热力图转换为彩色
    heatmap = plt.get_cmap(cmap)(cam / 255.0)[:, :, :3]  # (H, W, 3)
    heatmap = np.uint8(255 * heatmap)

    # 叠加热力图到原图
    original_image = np.array(original_image.convert("RGB"))
    overlayed_image = cv2.addWeighted(original_image, 1 - alpha, heatmap, alpha, 0)

    return overlayed_image

# 图片路径
image_path = r'D:\code_python\000adeep_learning\test\1.jpg'  # 替换为你的图片路径

# 读入并预处理图片
input_image, original_image = preprocess_image(image_path)

# 生成 Grad-CAM 热力图
target_class = 0  # 假设我们关注类别 0
cam = generate_gradcam(model, input_image, target_class)

# 将热力图叠加到原始图片
overlayed_image = overlay_heatmap_on_image(cam, original_image, alpha=0.5)

# 显示叠加图像
plt.imshow(overlayed_image)
plt.axis('off')
plt.show()

代码效果:

 

标签:plt,训练,image,hook,神经网络,可视化,cam,activations,self
From: https://blog.csdn.net/m0_73660403/article/details/144740849

相关文章

  • PyCharm专项训练4 最小生成树算法
    一、实验目的:本文的实验目的是通过编程实践,掌握并应用Prime算法和Kruskal算法来求解给定图的最小生成树问题。二、实验内容:数据准备:使用networkx库创建一个图G,并添加指定的节点和带权重的边。算法实现:实现Kruskal算法,通过构建最小生成树T,并找出构成最小生成树的边......
  • PyCharm专项训练5 最短路径算法
    一、实验目的    本文的实验目的是通过编程实践,掌握并应用Dijkstra(迪杰斯特拉)算法和Floyd(弗洛伊德)算法来解决图论中的最短路径问题。二、实验内容数据准备:使用邻接表的形式定义两个图graph_dijkstra和graph_floyd,图中包含节点以及节点之间的边和对应的权重。算......
  • 基于python+Django的招聘信息可视化与薪资预测系统
    1.项目背景本系统旨在帮助用户更高效地管理和分析招聘信息,通过爬虫抓取招聘数据、可视化分析招聘市场情况,并提供薪资预测功能。项目采用Django框架开发,具有以下主要功能:从招聘网站抓取招聘数据。存储与管理招聘信息。提供基于数据的可视化分析。实现简单的薪资预测功能。......
  • 循环神经网络(RNN)入门指南:从原理到实践
    目录1.循环神经网络的基本概念2.简单循环网络及其应用3.参数学习与优化4.基于门控的循环神经网络4.1长短期记忆网络(LSTM)4.1.1LSTM的核心组件:4.2 门控循环单元(GRU)4.3实际应用中的优化技巧4.3.1变体和改进4.3.2注意力机制的结合4.4实现细节和最佳实践4.4.......
  • 如何使用 Konga 可视化管理 Kong API 网关:完整指南与常见场景实例
    言简意赅的讲解Konga解决的痛点Kong是一个流行的API网关,广泛应用于微服务架构中,为应用提供负载均衡、API管理、身份验证、流量控制等功能。虽然Kong强大且功能丰富,但其管理界面原本基于命令行和配置文件,可能让部分开发者或运维人员感到不便。为了简化管理操作,Konga......
  • 计算机毕业设计Python+Spark知识图谱酒店推荐系统 酒店价格预测系统 酒店可视化 酒店
    温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO......
  • 计算机毕业设计Python+卷积神经网络租房推荐系统 租房大屏可视化 租房爬虫 hadoop spa
    温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO......
  • 计算机毕业设计PySpark+PyFlink+Hive地震预测系统 地震数据分析可视化 地震爬虫 大数
    温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO......
  • 【数据集】【YOLO】【目标检测】灭火器识别数据集 3261 张,YOLO灭火器识别算法实战训练
     一、数据集介绍【数据集】灭火器识别数据集3261张,目标检测,包含YOLO/VOC格式标注。数据集中包含1种分类:names:['extinguisher'],表示"灭火器"。数据集图片来自国内外网站、网络爬虫、监控采集等;可用于监控和移动设备灭火器识别。检测场景为工业园区、办公大楼、居民楼......
  • 【最新原创毕设】基于PPH的花涧订购系统+00332(免费领源码)可做计算机毕业设计JAVA、PHP
    摘 要近年来,电子商务的快速发展引起了行业和学术界的高度关注。花涧订购系统旨在为用户提供一个简单、高效、便捷的花卉购物体验,它不仅要求用户清晰地查看所需信息,而且还要求界面设计精美,使得功能与页面完美融合,从而提升系统的可操作性。因此,我们需要深入研究信息内容,并利用......