Python打卡训练营Day43

news2025/6/5 9:46:08

DAY 43 复习日

作业:

kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化

数据集地址:Lung Nodule Malignancy 肺结核良恶性判断 

进阶:并拆分成多个文件

import os
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from torch.utils.data import Dataset, DataLoader
from PIL import Image
import torch
from torchvision import transforms
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# 1. 读标签并映射 0/1
df = pd.read_csv('archive/malignancy.csv')


# 2. 按 patch_id 划 train/val
ids    = df['NoduleID'].values
labels = df['malignancy'].values
train_ids, val_ids = train_test_split(
    ids, test_size=0.2, random_state=42, stratify=labels
)
train_df = df[df['NoduleID'].isin(train_ids)].reset_index(drop=True)
val_df   = df[df['NoduleID'].isin(val_ids)].reset_index(drop=True)

# 3. Dataset:多页 TIFF 按页读取
class LungTBDataset(Dataset):
    def __init__(self, tif_path, df, transform=None):
        self.tif_path = tif_path
        self.df = df
        self.transform = transform

    def __len__(self):
        return len(self.df)

    def __getitem__(self, idx):
        row = self.df.iloc[idx]
        pid = int(row['NoduleID'])
        label = int(row['malignancy'])
        
        try:
            with Image.open(self.tif_path) as img:
                # 检查 pid 是否超出实际帧数
                total_pages = sum(1 for _ in ImageSequence.Iterator(img))
                if pid >= total_pages:
                    pid = total_pages - 1  # 取最后一帧
                img.seek(pid)
                img = img.convert('RGB')
                
        except Exception as e:
            # 返回黑色占位图
            img = Image.new('RGB', (224, 224), (0, 0, 0))
            
        if self.transform:
            img = self.transform(img)
            
        return img, label

# 4. 变换 & DataLoader
transform = transforms.Compose([
    transforms.Resize((224,224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485,0.456,0.406],
                         std =[0.229,0.224,0.225])
])
train_ds = LungTBDataset('archive/ct_tiles.tif', train_df, transform)
val_ds   = LungTBDataset('archive/ct_tiles.tif',   val_df, transform)
train_loader = DataLoader(train_ds, batch_size=16, shuffle=True,  num_workers=0, pin_memory=True)
val_loader   = DataLoader(val_ds,   batch_size=16, shuffle=False, num_workers=0, pin_memory=True)

# 5. 定义简单 CNN(3层卷积 + 全连接)
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        # 卷积层
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2)  # 224->112
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2)  # 112->56
        )
        # 最后一层卷积,用于 Grad-CAM
        self.conv3 = nn.Sequential(
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2)  # 56->28
        )
        # 全连接分类器
        self.fc = nn.Sequential(
            nn.Flatten(),
            nn.Linear(128 * 28 * 28, 256),
            nn.ReLU(inplace=True),
            nn.Linear(256, 2)
        )
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)      # 保留这一层的输出作 CAM
        x = self.fc(x)
        return x

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = SimpleCNN().to(device)

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-4)

# 6. 训练 + 验证循环
num_epochs = 5
for epoch in range(num_epochs):
    # 训练
    model.train()
    running_loss = 0
    for imgs, labs in train_loader:
        imgs, labs = imgs.to(device), labs.to(device)
        optimizer.zero_grad()
        outputs = model(imgs)
        loss    = criterion(outputs, labs)
        loss.backward()
        optimizer.step()
        running_loss += loss.item() * imgs.size(0)
    epoch_loss = running_loss / len(train_ds)
    # 验证
    model.eval()
    correct = 0
    with torch.no_grad():
        for imgs, labs in val_loader:
            imgs, labs = imgs.to(device), labs.to(device)
            preds = model(imgs).argmax(dim=1)
            correct += (preds == labs).sum().item()
    val_acc = correct / len(val_ds)
    print(f'Epoch {epoch+1}/{num_epochs}  Loss={epoch_loss:.4f}  ValAcc={val_acc:.4f}')

# 7. 简易 Grad-CAM
class GradCAM:
    def __init__(self, model, target_conv):
        self.model = model
        self.target_conv = target_conv
        self.grad    = None
        self.activation = None
        # 注册 hook
        target_conv.register_forward_hook(self._forward)
        target_conv.register_backward_hook(self._backward)
    def _forward(self, module, inp, outp):
        self.activation = outp.detach()
    def _backward(self, module, grad_in, grad_out):
        self.grad = grad_out[0].detach()
    def __call__(self, x, class_idx=None):
        self.model.zero_grad()
        out = self.model(x)
        if class_idx is None:
            class_idx = out.argmax(dim=1).item()
        loss = out[0, class_idx]
        loss.backward()
        # 计算权重
        weights = self.grad.mean(dim=(2,3))  # (1,C)
        cam = (weights.view(-1,1,1) * self.activation[0]).sum(dim=0)
        cam = torch.relu(cam)
        cam -= cam.min()
        cam /= cam.max()
        return cam.cpu().numpy()

# 8. 随机选一张验证图做可视化
model.eval()
imgs, labs = next(iter(val_loader))
img, lab = imgs[0:1].to(device), labs[0].item()

# 以 conv3 的最后 Conv2d 为 target
# conv3 是 Sequential,取其中的第0层 Conv2d
target_layer = model.conv3[0]
gradcam = GradCAM(model, target_layer)
heatmap = gradcam(img)  # (28,28)

# 上采样到 224×224
heatmap = np.uint8(255 * heatmap)
heatmap = Image.fromarray(heatmap).resize((224,224), resample=Image.BILINEAR)
heatmap = np.array(heatmap) / 255.0

# 反归一化 & 可视化叠加
inv_norm = transforms.Normalize(
    mean=[-0.485/0.229, -0.456/0.224, -0.406/0.225],
    std =[1/0.229,       1/0.224,       1/0.225]
)
img_show = inv_norm(img[0]).permute(1,2,0).cpu().numpy()
img_show = np.clip(img_show, 0, 1)

plt.figure(figsize=(8,4))
plt.subplot(1,2,1)
plt.imshow(img_show)
plt.title(f'Label={lab}')
plt.axis('off')

plt.subplot(1,2,2)
plt.imshow(img_show, alpha=0.6)
plt.imshow(heatmap, cmap='jet', alpha=0.4)
plt.title('Grad-CAM')
plt.axis('off')
plt.tight_layout()
plt.show()

代码没问题但跑的很慢不知道啥原因。

浙大疏锦行-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397396.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP7+MySQL5.6 查立得轻量级公交查询系统

# PHP7MySQL5.6 查立得轻量级公交查询系统 ## 系统简介 本系统是一个基于PHP7和MySQL5.6的轻量级公交查询系统(40KB级),支持线路查询、站点查询和换乘查询功能。系统采用原生PHPMySQL开发,无需第三方框架,适合手机端访问。 首发版本&#x…

如何做好一个决策:基于 Excel的决策树+敏感性分析应用(针对多个变量)

本文是对《如何做好一个决策:基于 Excel的决策树+敏感性分析应用》一文的补充。 示例背景 决策问题:是否开发新产品? 关键变量: 开发成本(B2):$500K, $700K, $1M高需求概率(B4):30%, 50%, 70%高需求收入(C4

Azure DevOps 管道部署系列之一本地服务器

Azure DevOps 是一个帮助改进 SDLC(软件开发生命周期)的平台。 在本文中,我们将使用 Azure Pipelines 创建自动化部署。 Azure DevOps 团队将 Azure Pipelines 定义为“使用 CI/CD 构建、测试和部署,适用于任何语言、平台和云平台”。 在这里,我将解释如何在 Azure Dev…

Celery简介

一、什么是异步任务队列 异步任务队列是指一种用于管理和调度异步执行任务的机制。具体来说,它允许将任务放入队列中,然后由后台进程异步处理这些任务,而不会阻塞主线程的执行。这种设计使得系统能够高效地处理耗时操作,同时保持…

基于 GitLab CI + Inno Setup 实现 Windows 程序自动化打包发布方案

在 Windows 桌面应用开发中,实现自动化构建与打包发布是一项非常实用的工程实践。本文以我在开发PackTes项目时的为例,介绍如何通过 GitLab CI 配合 Inno Setup、批处理脚本、Qt 构建工具,实现版本化打包并发布到共享目录的完整流程。 项目地…

web架构2------(nginx多站点配置,include配置文件,日志,basic认证,ssl认证)

一.前言 前面我们介绍了一下nginx的安装和基础配置,今天继续来深入讲解一下nginx的其他配置 二.nginx多站点配置 一个nginx上可以运行多个网站。有多种方式: http:// ip/域名 端口 URI 其中,ip/域名变了,那么网站入口就变了…

AI 的早期萌芽?用 Swift 演绎约翰·康威的「生命游戏」

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 你有没有想过,能不能通过简单的规则模拟出生与死亡?「生命游戏」正是这样一种充满魅力的数学模拟系统。这篇文章我们来聊聊它的规则到底有多神奇,并用 S…

go|channel源码分析

文章目录 channelhchanmakechanchansendchanrecvcomplieclosechan channel 先看一下源码中的说明 At least one of c.sendq and c.recvq is empty, except for the case of an unbuffered channel with a single goroutine blocked on it for both sending and receiving usin…

【大模型学习】项目练习:视频文本生成器

🚀实现视频脚本生成器 视频文本生成器 📚目录 一、游戏设计思路二、完整代码解析三、扩展方向建议四、想说的话 一、⛳设计思路 本视频脚本生成器采用模块化设计,主要包含三大核心模块: 显示模块:处理用户输入和…

【Rust】Rust获取命令行参数以及IO操作

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

【Redis】Zset 有序集合

文章目录 常用命令zaddzcardzcountzrange && zrevrangezrangebyscorezpopmax && bzpopmaxzpopmin && zpopmaxzrank && zrevrankzscorezremzremrangebyrankzremrangebyscorezincrby 集合间操作交集 zinterstore并集 zunionstore 内部编码应用场…

manus对比ChatGPT-Deep reaserch进行研究类论文数据分析!谁更胜一筹?

目录 没有账号,只能挑选一个案例 1、manus的效果 Step-1:直接看结果 Step-2:看看其他文件的细节 Step-3:看最终报告 2、Deep reaserch 3、Deep reaserch进行行业分析 总结一下: 大家好这里是学术Anan&#xff…

【 HarmonyOS 5 入门系列 】鸿蒙HarmonyOS示例项目讲解

【 HarmonyOS 5 入门系列 】鸿蒙HarmonyOS示例项目讲解 一、前言:移动开发声明式 UI 框架的技术变革 在移动操作系统的发展历程中,UI 开发模式经历了从命令式到声明式的重大变革。 根据华为开发者联盟 2024 年数据报告显示,HarmonyOS 设备…

用提示词写程序(3),VSCODE+Claude3.5+deepseek开发edge扩展插件V2

edge扩展插件;筛选书签,跳转搜索,设置背景 链接: https://pan.baidu.com/s/1nfnwQXCkePRnRh5ltFyfag?pwd86se 提取码: 86se 导入解压的扩展文件夹: 导入扩展成功: edge扩展插件;筛选书签,跳转搜索,设置背景

初识PS(Photoshop)

初识PS(Photoshop) 1、Photoshop界面 2、常用快捷键

go语言的GMP(基础)

1.概念梳理 1.1线程 通常语义中的线程,指的是内核级线程,核心点如下: (1)是操作系统最小调度单元; (2)创建、销毁、调度交由内核完成,cpu 需完成用户态与内核态间的切…

电路图识图基础知识-高、低压供配电系统电气系统的继电自动装置(十三)

电气系统的继电自动装置 在供电系统中为保证系统的可靠性,保证重要负荷的不间断供电,常采用自动重合闸装置和备用电源自动投入装置。 1 自动重合闸装置 供配电系统多年运行实践表明,架空线路发生的故障多属于暂时性故障,如雷击…

Qt实现的水波进度条和温度进度条

一.效果 二.原理 1.水波 要模拟波浪,就要首先画出一条波浪线,正弦余弦曲线就很适合。 y=A*sin(ω*x+φ)+k y=A*cos(ω*x+φ)+k 这是正弦余弦曲线的公式,要想实现水波效果,那需要两条曲线,一条曲线的波峰对着另外一条曲线的波谷,要实现这样的曲线效果,只有让正弦曲线前移…

WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM

目录 一、适配方案 二、VM布局 ​编辑 三、vh布局 四、案例—酷我音乐 一、适配方案 二、VM布局 三、vh布局 四、案例—酷我音乐

【Zephyr 系列 3】多线程与调度机制:让你的 MCU 同时干多件事

好的,下面是Zephyr 系列第 3 篇:聚焦 多线程与调度机制的实践应用,继续面向你这样的 Ubuntu + 真板实战开发者,代码清晰、讲解通俗、结构规范,符合 CSDN 高质量博客标准。 🧠关键词:Zephyr、线程调度、k_thread、k_sleep、RTOS、BluePill 📌适合人群:想从裸机开发进…