第57步 深度學習圖像識別:CNN可視化(Pytorch)

基于WIN10的64位系統演示

一、寫在前面

由于不少模型使用的是Pytorch,因此這一期補上基于Pytorch實現CNN可視化的教程和代碼,以SqueezeNet模型為例。

二、CNN可視化實戰

繼續使用胸片的數據集:肺結核病人和健康人的胸片的識別。其中,肺結核病人700張,健康人900張,分別存入單獨的文件夾中。

(a)SqueezeNet建模

######################################導入包###################################
# 導入必要的包
import copy
import torch
import torchvision
import torchvision.transforms as transforms
from torchvision import models
from torch.utils.data import DataLoader
from torch import optim, nn
from torch.optim import lr_scheduler
import os
import matplotlib.pyplot as plt
import warnings
import numpy as npwarnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 設置GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")################################導入數據集#####################################
import torch
from torchvision import datasets, transforms
import os# 數據集路徑
data_dir = "./MTB"# 圖像的大小
img_height = 100
img_width = 100# 數據預處理
data_transforms = {'train': transforms.Compose([transforms.RandomResizedCrop(img_height),transforms.RandomHorizontalFlip(),transforms.RandomVerticalFlip(),transforms.RandomRotation(0.2),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),'val': transforms.Compose([transforms.Resize((img_height, img_width)),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),
}# 加載數據集
full_dataset = datasets.ImageFolder(data_dir)# 獲取數據集的大小
full_size = len(full_dataset)
train_size = int(0.7 * full_size)  # 假設訓練集占80%
val_size = full_size - train_size  # 驗證集的大小# 隨機分割數據集
torch.manual_seed(0)  # 設置隨機種子以確保結果可重復
train_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])# 將數據增強應用到訓練集
train_dataset.dataset.transform = data_transforms['train']# 創建數據加載器
batch_size = 32
train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)
val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=4)dataloaders = {'train': train_dataloader, 'val': val_dataloader}
dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)}
class_names = full_dataset.classes###############################定義ShuffleNet模型################################
# 定義SqueezeNet模型
model = models.squeezenet1_1(pretrained=True)  # 這里以SqueezeNet 1.1版本為例
num_ftrs = model.classifier[1].in_channels# 根據分類任務修改最后一層
model.classifier[1] = nn.Conv2d(num_ftrs, len(class_names), kernel_size=(1,1))# 修改模型最后的輸出層為我們需要的類別數
model.num_classes = len(class_names)model = model.to(device)# 打印模型摘要
print(model)#############################編譯模型#########################################
# 定義損失函數
criterion = nn.CrossEntropyLoss()# 定義優化器
optimizer = optim.Adam(model.parameters())# 定義學習率調度器
exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)# 開始訓練模型
num_epochs = 20# 初始化記錄器
train_loss_history = []
train_acc_history = []
val_loss_history = []
val_acc_history = []for epoch in range(num_epochs):print('Epoch {}/{}'.format(epoch, num_epochs - 1))print('-' * 10)# 每個epoch都有一個訓練和驗證階段for phase in ['train', 'val']:if phase == 'train':model.train()  # 設置模型為訓練模式else:model.eval()   # 設置模型為評估模式running_loss = 0.0running_corrects = 0# 遍歷數據for inputs, labels in dataloaders[phase]:inputs = inputs.to(device)labels = labels.to(device)# 零參數梯度optimizer.zero_grad()# 前向with torch.set_grad_enabled(phase == 'train'):outputs = model(inputs)_, preds = torch.max(outputs, 1)loss = criterion(outputs, labels)# 只在訓練模式下進行反向和優化if phase == 'train':loss.backward()optimizer.step()# 統計running_loss += loss.item() * inputs.size(0)running_corrects += torch.sum(preds == labels.data)epoch_loss = running_loss / dataset_sizes[phase]epoch_acc = (running_corrects.double() / dataset_sizes[phase]).item()# 記錄每個epoch的loss和accuracyif phase == 'train':train_loss_history.append(epoch_loss)train_acc_history.append(epoch_acc)else:val_loss_history.append(epoch_loss)val_acc_history.append(epoch_acc)print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))print()# 保存模型
torch.save(model.state_dict(), 'squeezenet.pth')

(b)可視化卷積神經網絡的中間輸出

import torch
from torchvision import models, transforms
import matplotlib.pyplot as plt
from torch import nn
from PIL import Image# 定義圖像的大小
img_height = 100
img_width = 100# 1. 加載模型
model = models.squeezenet1_1(pretrained=False) 
num_ftrs = model.classifier[1].in_channels
num_classes = 2 
model.classifier[1] = nn.Conv2d(num_ftrs, num_classes, kernel_size=(1,1))
model.num_classes = num_classes
model.load_state_dict(torch.load('squeezenet.pth')) 
model.eval()
model = model.to('cuda' if torch.cuda.is_available() else 'cpu')# 2. 加載圖片并進行預處理
img = Image.open('./MTB/Tuberculosis/Tuberculosis-203.png')
transform = transforms.Compose([transforms.Resize((img_height, img_width)),transforms.Grayscale(num_output_channels=3),  transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
img_tensor = transform(img).unsqueeze(0)
img_tensor = img_tensor.to('cuda' if torch.cuda.is_available() else 'cpu')# 3. 提取前N層的輸出
N = 25
activations = []
x = img_tensor
for i, layer in enumerate(model.features):x = layer(x)if i < N:activations.append(x)# 4. 將每個中間激活的所有通道可視化,但最多只顯示前9個通道
max_channels_to_show = 9
for i, activation in enumerate(activations):num_channels = min(max_channels_to_show, activation.shape[1])fig, axs = plt.subplots(1, num_channels, figsize=(num_channels*2, 2))for j in range(num_channels):axs[j].imshow(activation[0, j].detach().cpu().numpy(), cmap='viridis')axs[j].axis('off')plt.tight_layout()plt.show()# 清空當前圖像
plt.clf()

結果輸出如下:

?由于SqueezeNet只有13層,所以即使我們在代碼中要求輸出25層,那也只能輸出13層。從第一層到最后一層,可以看到逐漸抽象化。

(c)可視化過濾器

import matplotlib.pyplot as pltdef visualize_filters(model):# 獲取第一個卷積層的權重first_conv_layer = model.features[0]weights = first_conv_layer.weight.data.cpu().numpy()# 取絕對值以便于觀察所有權重weights = np.abs(weights)# 歸一化權重weights -= weights.min()weights /= weights.max()# 計算子圖網格大小num_filters = weights.shape[0]num_cols = 12num_rows = num_filters // num_colsif num_filters % num_cols != 0:num_rows += 1# 創建子圖fig, axs = plt.subplots(num_rows, num_cols, figsize=(num_cols*2, num_rows*2))# 繪制過濾器for filter_index, ax in enumerate(axs.flat):if filter_index < num_filters:ax.imshow(weights[filter_index].transpose(1, 2, 0))ax.axis('off')plt.tight_layout()plt.show()# 調用函數來顯示過濾器
visualize_filters(model)

這個更加抽象:

(d)Grad-CAM繪制特征熱力圖

import torch
from torchvision import models, transforms
from torch.nn import functional as F
from torch.autograd import Variable
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.cm as cm# 模型路徑
model_path = 'squeezenet.pth'# 圖像路徑
image_path = './MTB/Tuberculosis/Tuberculosis-203.png'# 加載模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 創建一個SqueezeNet模型
model = models.squeezenet1_1(pretrained=False)# 修改最后一層為二分類
model.classifier[1] = torch.nn.Conv2d(512, 2, kernel_size=(1,1), stride=(1,1))# 這行代碼用于在模型全連接層輸出后添加一個softmax函數,使得模型輸出可以解釋為概率
model.num_classes = 2model = model.to(device)
model.load_state_dict(torch.load(model_path))
model.eval()class GradCAM:def __init__(self, model, target_layer):self.model = modelself.target_layer = target_layerself.feature = Noneself.gradient = None# 定義鉤子self.hooks = self.target_layer.register_forward_hook(self.save_feature_map)self.hooks = self.target_layer.register_backward_hook(self.save_gradient)# 保存featuredef save_feature_map(self, module, input, output):self.feature = output# 保存梯度def save_gradient(self, module, grad_in, grad_out):self.gradient = grad_out[0]# 計算權重def compute_weight(self):return F.adaptive_avg_pool2d(self.gradient, 1)def remove_hooks(self):self.hooks.remove()def __call__(self, inputs, index=None):self.model.zero_grad()output = self.model(inputs)if index == None:index = np.argmax(output.cpu().data.numpy())target = output[0][index]target.backward()weight = self.compute_weight()cam = weight * self.featurecam = cam.cpu().data.numpy()cam = np.sum(cam, axis=1)cam = np.maximum(cam, 0)# 歸一化處理cam -= np.min(cam)cam /= np.max(cam)self.remove_hooks()return cam# 圖像處理
img = Image.open(image_path).convert("RGB")
img_transforms = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
img_tensor = img_transforms(img)
img_tensor = img_tensor.unsqueeze(0).to(device)# 獲取預測類別
outputs = model(img_tensor)
_, pred = torch.max(outputs, 1)
pred_class = pred.item()# 獲取最后一個卷積層
target_layer = model.features[12]# Grad-CAM
grad_cam = GradCAM(model=model, target_layer=target_layer)
# 獲取輸入圖像的Grad-CAM圖像
cam_img = grad_cam(img_tensor, index=pred_class)# 重新調整尺寸以匹配原始圖像
cam_img = Image.fromarray(np.uint8(255 * cam_img[0]))
cam_img = cam_img.resize((img.width, img.height), Image.BICUBIC)# 將CAM圖像轉換為Heatmap
cmap = cm.get_cmap('jet')
cam_img = cmap(np.float32(cam_img))# 將RGBA圖像轉換為RGB
cam_img = Image.fromarray(np.uint8(cam_img[:, :, :3] * 255))
cam_img = Image.blend(img, cam_img, alpha=0.5)# 顯示圖像
plt.imshow(cam_img)
plt.axis('off')  # 不顯示坐標軸
plt.show()print(pred_class)

輸出如下,分別是第一、二、六、十二層的卷積層的輸出:

結果解讀:

在Grad-CAM熱圖中,顏色的深淺表示了模型在做出預測時,對輸入圖像中的哪些部分賦予了更多的重要性。紅色區域代表了模型認為最重要的部分,這些區域在模型做出其預測時起到了主要的決定性作用。而藍色區域則是對預測貢獻較少的部分。

具體來說:

紅色區域:這些是模型在進行預測時,權重較高的部分。也就是說,這些區域對模型的預測結果影響最大。在理想情況下,這些區域應該對應于圖像中的目標對象或者是對象的重要特征。

藍色區域:這些是模型在進行預測時,權重較低的部分。也就是說,這些區域對模型的預測結果影響較小。在理想情況下,這些區域通常對應于圖像的背景或無關信息。

這種可視化方法可以幫助我們理解卷積神經網絡模型是如何看待圖像的,也能提供一種評估模型是否正確關注到圖像中重要部分的方法。

三、數據

鏈接:https://pan.baidu.com/s/15vSVhz1rQBtqNkNp2GQyVw?pwd=x3jf

提取碼:x3jf

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35784.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35784.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35784.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

問DAO成都丨CyberDAO共識會議在成都圓滿落幕

過往匆匆&#xff0c;唯有共識綿延&#xff1b;未來已來&#xff0c;愿與智者同謀。2023年8月9日至8月10日&#xff0c;CyberDAO共識會議在成都市大邑縣順利召開&#xff0c;吸引了上百名Web3.0與元宇宙愛好者參與本次會議。CyberDAO大中華區運營團隊合伙人JR、漫威、安祈、可樂…

【0.1】lubancat魯班貓4刷入debian網絡ping 域名不通問題

目錄 1. 環境2. 操作步驟 1. 環境 lubancat4魯班貓4 (4G0)不帶emmc系統鏡像lubancat-rk3588-debian11-gnome-20230807_update.img官方資料地址https://doc.embedfire.com/products/link/zh/latest/linux/ebf_lubancat.html 2. 操作步驟 從官網給的百度網盤下載linux系統全部…

10、雜項:遍歷指定目錄計算文件的md5并輸出到文件

目錄 &#x1f345;點擊這里查看所有博文 隨著自己工作的進行&#xff0c;接觸到的技術棧也越來越多。給我一個很直觀的感受就是&#xff0c;某一項技術/經驗在剛開始接觸的時候都記得很清楚。往往過了幾個月都會忘記的差不多了&#xff0c;只有經常會用到的東西才有可能真正記…

【Rust】Rust學習 第十一章編寫自動化測試

Rust 是一個相當注重正確性的編程語言&#xff0c;不過正確性是一個難以證明的復雜主題。Rust 的類型系統在此問題上下了很大的功夫&#xff0c;不過它不可能捕獲所有種類的錯誤。為此&#xff0c;Rust 也在語言本身包含了編寫軟件測試的支持。 編寫一個叫做 add_two 的將傳遞…

[C++ 網絡協議編程] TCP/IP協議

目錄 1. TCP/IP協議棧 2. TCP原理 2.1 TCP套接字中的I/O緩沖 2.2 TCP工作原理 2.2.1 三次握手&#xff08;連接&#xff09; 2.2.2 與對方主機的數據交換 2.2.3 四次握手&#xff08;斷開與套接字的連接&#xff09; TCP&#xff08;Transmission Control Protocol傳輸控…

無涯教程-Perl - ref函數

描述 如果EXPR為引用,則此函數返回真值&#xff1b;如果未提供EXPR,則為$_。返回的實際值還定義了引用所引用的實體的類型。 內置類型為- REFSCALARARRAYHASHCODEGLOBLVALUEIO::Handle 如果使用bless()函數為變量設置了祝福,則將返回新的數據類型。新的數據類型通常將是一個…

比較編程語言C和Go

使用一個簡單的計數程序來比較古老的C語言和現代的Go語言。Go是一種現代的編程語言&#xff0c;它在很大程度上源自C語言。因此&#xff0c;對于任何使用C語言編寫程序的人來說&#xff0c;Go可能會感覺很熟悉。Go使得編寫新程序變得容易&#xff0c;同時又讓C程序員感到熟悉&a…

大數據-玩轉數據-Flink 自定義Sink(Mysql)

一、說明 如果Flink沒有提供給我們可以直接使用的連接器&#xff0c;那我們如果想將數據存儲到我們自己的存儲設備中&#xff0c;mysql 的安裝使用請參考 mysql-玩轉數據-centos7下mysql的安裝 創建表 CREATE TABLE sensor (id int(10) ) ENGINEInnoDB DEFAULT CHARSETutf8二…

二 根據用戶行為數據創建ALS模型并召回商品

二 根據用戶行為數據創建ALS模型并召回商品 2.0 用戶行為數據拆分 方便練習可以對數據做拆分處理 pandas的數據分批讀取 chunk 厚厚的一塊 相當大的數量或部分 import pandas as pd reader pd.read_csv(behavior_log.csv,chunksize100,iteratorTrue) count 0; for chunk in …

DNS協議及其工作原理

DNS是域名系統&#xff08;Domain Name System&#xff09;的縮寫&#xff0c;它是一種用于將域名轉換為IP地址的分布式數據庫系統。它是因特網的基石&#xff0c;能夠使人們通過域名方便地訪問互聯網&#xff0c;而無需記住復雜的IP地址。 DNS的歷史可以追溯到1983年&#xf…

4個簡化IT服務臺任務的ChatGPT功能

最近幾個月&#xff0c;ChatGPT 風靡全球&#xff0c;這是一個 AI 聊天機器人&#xff0c;使用戶能夠生成腳本、文章、鍛煉圖表等。這項技術在各行各業都有無窮無盡的應用&#xff0c;在本文中&#xff0c;我們將研究這種現代技術如何幫助服務臺團隊增強服務交付和客戶體驗。 什…

最佳實踐:如何優雅地提交一個 Amazon EMR Serverless 作業?

《大數據平臺架構與原型實現&#xff1a;數據中臺建設實戰》一書由博主歷時三年精心創作&#xff0c;現已通過知名IT圖書品牌電子工業出版社博文視點出版發行&#xff0c;點擊《重磅推薦&#xff1a;建大數據平臺太難了&#xff01;給我發個工程原型吧&#xff01;》了解圖書詳…

章節7:XSS檢測和利用

章節7&#xff1a;XSS檢測和利用 測試payload <script>alert(XSS)</script> <script>alert(document.cookie)</script> ><script>alert(document.cookie)</script> ><script>alert(document.cookie)</script> &qu…

元宇宙之經濟(02)理解NFT

1 NFT是什么&#xff1f; 想象一下&#xff0c;你小時候曾經在操場上集齊過各種不同的貼紙&#xff0c;然后和朋友們交換&#xff0c;這些貼紙有著獨特的圖案和價值。NFT的概念與此類似&#xff0c;但在數字世界中運作。NFT是一種基于區塊鏈技術的數字資產&#xff0c;每個NFT…

golang—面試題大全

目錄標題 sliceslice和array的區別slice擴容機制slice是否線程安全slice分配到棧上還是堆上擴容過程中是否重新寫入go深拷貝發生在什么情況下&#xff1f;切片的深拷貝是怎么做的copy和左值進行初始化區別slice和map的區別 mapmap介紹map的key的類型map對象如何比較map的底層原…

《Java極簡設計模式》第03章:工廠方法模式(FactoryMethod)

作者&#xff1a;冰河 星球&#xff1a;http://m6z.cn/6aeFbs 博客&#xff1a;https://binghe.gitcode.host 文章匯總&#xff1a;https://binghe.gitcode.host/md/all/all.html 源碼地址&#xff1a;https://github.com/binghe001/java-simple-design-patterns/tree/master/j…

無法正確識別車牌(Python、OpenCv、Tesseract)

我正在嘗試識別車牌&#xff0c;但出現了錯誤&#xff0c;例如錯誤/未讀取字符 以下是每個步驟的可視化&#xff1a; 從顏色閾值變形關閉獲得遮罩 以綠色突出顯示的車牌輪廓過濾器 將板輪廓粘貼到空白遮罩上 Tesseract OCR的預期結果 BP 1309 GD 但我得到的結果是 BP 1309…

騰訊云標準型CVM云服務器詳細介紹

騰訊云CVM服務器標準型實例的各項性能參數平衡&#xff0c;標準型云服務器適用于大多數常規業務&#xff0c;例如&#xff1a;web網站及中間件等&#xff0c;常見的標準型云服務器有CVM標準型S5、S6、SA3、SR1、S5se等規格&#xff0c;騰訊云服務器網來詳細說下云服務器CVM標準…

NAS搭建指南一——服務器的選擇與搭建

一、服務器的選擇 有自己的本地的公網 IP 的請跳過此篇文章按需求選擇一個云服務器&#xff0c;目的就是為了進行 frp 的搭建&#xff0c;完成內網穿透我選擇的是騰訊云服務器&#xff0c;我的配置如下&#xff0c;僅供參考&#xff1a; 4. 騰訊云服務器官網地址 二、服務器…

docker 鏡像的導出與導入 save 與 load

一、鏡像導出 docker save 導出 將系統中的鏡像保存為壓縮包&#xff0c;進行文件傳輸。使用 docker save --help 查看命令各參數&#xff0c;或者去docker官網查看.以 hello-world鏡像為例。 A&#xff1a;將鏡像保存為tar包 docker save image > package.tar docker sa…