The training step is finish, but Grad-CAM is not modification finished

This commit is contained in:
2025-04-08 03:09:59 +00:00
parent dfeec70a53
commit 54bb9b0072
31 changed files with 805 additions and 258 deletions

View File

@@ -58,12 +58,12 @@ class Calculate():
def Output_Style(self): def Output_Style(self):
Result = pd.DataFrame( Result = pd.DataFrame(
{ {
"loss" : "{}±{}".format(self.History[0]["loss"][0], self.History[1]["loss"][0]), "loss" : "{}%±{}".format(self.History[0]["loss"][0], self.History[1]["loss"][0]),
"precision" : "{}±{}".format(self.History[0]["precision"][0], self.History[1]["precision"][0]), "precision" : "{}%±{}".format(self.History[0]["precision"][0], self.History[1]["precision"][0]),
"recall" : "{}±{}".format(self.History[0]["recall"][0], self.History[1]["recall"][0]), "recall" : "{}%±{}".format(self.History[0]["recall"][0], self.History[1]["recall"][0]),
"accuracy" : "{}±{}".format(self.History[0]["accuracy"][0], self.History[1]["accuracy"][0]), "accuracy" : "{}%±{}".format(self.History[0]["accuracy"][0], self.History[1]["accuracy"][0]),
"f1" : "{}±{}".format(self.History[0]["f1"][0], self.History[1]["f1"][0]), "f1" : "{}%±{}".format(self.History[0]["f1"][0], self.History[1]["f1"][0]),
"AUC" : "{}±{}".format(self.History[0]["AUC"][0], self.History[1]["AUC"][0]) "AUC" : "{}%±{}".format(self.History[0]["AUC"][0], self.History[1]["AUC"][0])
}, index = [0] }, index = [0]
) )
return Result return Result

View File

@@ -16,11 +16,12 @@ class Image_generator():
self.stop = 0 self.stop = 0
self.Labels = Labels self.Labels = Labels
self.Generator_Root = Generator_Root self.Generator_Root = Generator_Root
self.Image_Size = Image_Size self.Image_Size = Image_Size
self.Class_Count = 904
pass pass
def Processing_Main(self, Training_Dict_Data_Root): def Processing_Main(self, Training_Dict_Data_Root):
data_size = 0 data_size = 2712
# 製作標準資料增強 # 製作標準資料增強
''' '''
@@ -51,83 +52,76 @@ class Image_generator():
''' '''
File = Process_File() File = Process_File()
image_processing = Read_image_and_Process_image(self.Image_Size) image_processing = Read_image_and_Process_image(self.Image_Size)
tool = Training_Precesses("", "", "", "") tool = Training_Precesses(self.Image_Size)
Classes = [] Classes = []
Transform = self.Generator_Content(stardand) Transform = self.Generator_Content(stardand)
for label in self.Labels: # 分別對所有類別進行資料強化 for label in self.Labels: # 分別對所有類別進行資料強化
image = self.load_data(label) # 取的資料 Image_Roots = self.get_data_roots[label]
save_root = File.Make_Save_Root(label, save_roots) # 合併路徑 save_root = File.Make_Save_Root(label, save_roots) # 合併路徑
Classes = image_processing.make_label_list(len(image), "1") Classes = image_processing.make_label_list(len(Image_Roots), "1")
Training_Dataset = tool.Combine_Signal_Dataset_To_DataLoader(image, Classes, 1, False) Training_Dataset = tool.Setting_DataSet(Image_Roots, Classes)
Training_DataLoader = tool.Dataloader_Sampler(Training_Dataset, 1, False)
if File.JudgeRoot_MakeDir(save_root): # 判斷要存的資料夾存不存在,不存在則創立 if File.JudgeRoot_MakeDir(save_root): # 判斷要存的資料夾存不存在,不存在則創立
print("The file is exist.This Script is not creating new fold.") print("The file is exist.This Script is not creating new fold.")
for batch_idx, (images, labels) in enumerate(Training_Dataset): for i in range(1, int(self.Class_Count / len(Image_Roots)) + 1, 1):
for i, img in enumerate(images): for batch_idx, (images, labels) in enumerate(Training_DataLoader):
if i == self.stop: for j, img in enumerate(images):
break # if i == self.stop:
# break
img = img.permute(2, 0, 1) img = img.permute(2, 0, 1)
img = Transform(img) img = Transform(img)
# 轉換為 NumPy 陣列並從 BGR 轉為 RGB # 轉換為 NumPy 陣列並從 BGR 轉為 RGB
img_np = img.numpy().transpose(1, 2, 0) # 轉回 HWC 格式 img_np = img.numpy().transpose(1, 2, 0) # 轉回 HWC 格式
img_np = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB) # BGR 轉 RGB img_np = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB) # BGR 轉 RGB
img_pil = transforms.ToPILImage()(img_np) img_pil = transforms.ToPILImage()(img_np)
File.Save_PIL_File("image_" + label + str(data_size) + ".png", save_root, img_pil) # 存檔 File.Save_PIL_File("image_" + label + str(data_size) + ".png", save_root, img_pil) # 存檔
data_size += 1 data_size += 1
return data_size return data_size
def load_data(self, label):
'''Images is readed by myself'''
image_processing = Read_image_and_Process_image(self.Image_Size)
img = image_processing.Data_Augmentation_Image(self.get_data_roots[label])
img = torch.tensor(img)
self.stop = len(img) * 5
return img
def Generator_Content(self, judge): # 影像資料增強 def Generator_Content(self, judge): # 影像資料增強
''' '''
ImageGenerator的參數: ## Parameters:
featurewise_center : 布爾值。將輸入數據的均值設置為0逐特徵進行。 <b>featurewise_center</b> : 布爾值。將輸入數據的均值設置為0逐特徵進行。<br/>
samplewise_center : 布爾值。將每個樣本的均值設置為0。 <b>samplewise_center</b> : 布爾值。將每個樣本的均值設置為0。<br/>
featurewise_std_normalization : Boolean. 布爾值。將輸入除以數據標準差,逐特徵進行。 <b>featurewise_std_normalization</b> : Boolean. 布爾值。將輸入除以數據標準差,逐特徵進行。<br/>
samplewise_std_normalization : 布爾值。將每個輸入除以其標準差。 <b>samplewise_std_normalization</b> : 布爾值。將每個輸入除以其標準差。<br/>
zca_epsilon : ZCA 白化的epsilon 值默認為1e-6。 <b>zca_epsilon</b> : ZCA 白化的epsilon 值默認為1e-6。<br/>
zca_whitening : 布爾值。是否應用ZCA 白化。 <b>zca_whitening</b> : 布爾值。是否應用ZCA 白化。<br/>
rotation_range : 整數。隨機旋轉的度數範圍。 <b>rotation_range</b> : 整數。隨機旋轉的度數範圍。<br/>
width_shift_range : 浮點數、一維數組或整數 <b>width_shift_range</b> : 浮點數、一維數組或整數<br/>
float: 如果<1則是除以總寬度的值或者如果>=1則為像素值。 float: 如果<1則是除以總寬度的值或者如果>=1則為像素值。
1-D 數組: 數組中的隨機元素。 1-D 數組: 數組中的隨機元素。
int: 來自間隔 (-width_shift_range, +width_shift_range) 之間的整數個像素。 int: 來自間隔 (-width_shift_range, +width_shift_range) 之間的整數個像素。
width_shift_range=2時可能值是整數[-1, 0, +1],與 width_shift_range=[-1, 0, +1] 相同;而 width_shift_range=1.0 時,可能值是 [-1.0, +1.0) 之間的浮點數。 width_shift_range=2時可能值是整數[-1, 0, +1],與 width_shift_range=[-1, 0, +1] 相同;而 width_shift_range=1.0 時,可能值是 [-1.0, +1.0) 之間的浮點數。
height_shift_range : 浮點數、一維數組或整數 <b>height_shift_range</b> : 浮點數、一維數組或整數<br/>
float: 如果<1則是除以總寬度的值或者如果>=1則為像素值。 float: 如果<1則是除以總寬度的值或者如果>=1則為像素值。
1-D array-like: 數組中的隨機元素。 1-D array-like: 數組中的隨機元素。
int: 來自間隔 (-height_shift_range, +height_shift_range) 之間的整數個像素。 int: 來自間隔 (-height_shift_range, +height_shift_range) 之間的整數個像素。
height_shift_range=2時可能值是整數[-1, 0, +1],與 height_shift_range=[-1, 0, +1] 相同;而 height_shift_range=1.0 時,可能值是 [-1.0, +1.0) 之間的浮點數。 height_shift_range=2時可能值是整數[-1, 0, +1],與 height_shift_range=[-1, 0, +1] 相同;而 height_shift_range=1.0 時,可能值是 [-1.0, +1.0) 之間的浮點數。
shear_range : 浮點數。剪切強度(以弧度逆時針方向剪切角度)。 <b>shear_range</b> : 浮點數。剪切強度(以弧度逆時針方向剪切角度)。<br/>
zoom_range : 浮點數或[lower, upper]。隨機縮放範圍。如果是浮點數,[lower, upper] = [1-zoom_range, 1+zoom_range]。 <b>zoom_range</b> : 浮點數或[lower, upper]。隨機縮放範圍。如果是浮點數,[lower, upper] = [1-zoom_range, 1+zoom_range]。<br/>
channel_shift_range : 浮點數。隨機通道轉換的範圍。 <b>channel_shift_range</b> : 浮點數。隨機通道轉換的範圍。<br/>
fill_mode : {"constant", "nearest", "reflect" or "wrap"} 之一。默認為'nearest'。輸入邊界以外的點根據給定的模式填充: <b>fill_mode</b> : {"constant", "nearest", "reflect" or "wrap"} 之一。默認為'nearest'。輸入邊界以外的點根據給定的模式填充:<br/>
'constant': kkkkkkkk|abcd|kkkkkkkk (cval=k) 'constant': kkkkkkkk|abcd|kkkkkkkk (cval=k)
'nearest': aaaaaaaa|abcd|dddddddd 'nearest': aaaaaaaa|abcd|dddddddd
'reflect': abcddcba|abcd|dcbaabcd 'reflect': abcddcba|abcd|dcbaabcd
'wrap': abcdabcd|abcd|abcdabcd 'wrap': abcdabcd|abcd|abcdabcd
cval : 浮點數或整數。用於邊界之外的點的值,當 fill_mode = "constant" 時。 <b>cval</b> : 浮點數或整數。用於邊界之外的點的值,當 fill_mode = "constant" 時。<br/>
horizontal_flip : 布爾值。隨機水平翻轉。 <b>horizontal_flip</b> : 布爾值。隨機水平翻轉。<br/>
vertical_flip : 布爾值。隨機垂直翻轉。 <b>vertical_flip</b> : 布爾值。隨機垂直翻轉。<br/>
rescale : 重縮放因子。默認為None。如果是None 或0不進行縮放否則將數據乘以所提供的值在應用任何其他轉換之前 <b>rescale</b> : 重縮放因子。默認為None。如果是None 或0不進行縮放否則將數據乘以所提供的值在應用任何其他轉換之前<br/>
preprocessing_function : 應用於每個輸入的函數。這個函數會在任何其他改變之前運行。這個函數需要一個參數一張圖像秩為3 的Numpy 張量並且應該輸出一個同尺寸的Numpy 張量。 <b>preprocessing_function</b> : 應用於每個輸入的函數。這個函數會在任何其他改變之前運行。這個函數需要一個參數一張圖像秩為3 的Numpy 張量並且應該輸出一個同尺寸的Numpy 張量。<br/>
data_format : 圖像數據格式,{"channels_first", "channels_last"} 之一。"channels_last" 模式表示圖像輸入尺寸應該為(samples, height, width, channels)"channels_first" 模式表示輸入尺寸應該為(samples, channels, height, width)。默認為在Keras 配置文件 ~/.keras/keras.json 中的 image_data_format 值。如果你從未設置它,那它就是"channels_last" <b>data_format</b> : 圖像數據格式,{"channels_first", "channels_last"} 之一。"channels_last" 模式表示圖像輸入尺寸應該為(samples, height, width, channels)"channels_first" 模式表示輸入尺寸應該為(samples, channels, height, width)。默認為在Keras 配置文件 ~/.keras/keras.json 中的 image_data_format 值。如果你從未設置它,那它就是"channels_last"<br/>
validation_split : 浮點數。Float. 保留用於驗證的圖像的比例嚴格在0和1之間 <b>validation_split</b> : 浮點數。Float. 保留用於驗證的圖像的比例嚴格在0和1之間<br/>
dtype : 生成數組使用的數據類型。 <b>dtype</b> : 生成數組使用的數據類型。<br/>
''' '''
if judge == 1: if judge == 1:
return transforms.Compose([ return transforms.Compose([
@@ -159,6 +153,4 @@ class Image_generator():
transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.2), transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.2),
transforms.RandomHorizontalFlip(), transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(), transforms.RandomVerticalFlip(),
]) ])
else:
return transforms.ToTensor() # 將數值歸一化到[0, 1]之間

View File

@@ -18,9 +18,10 @@ class Loding_Data_Root(Process_File):
get_Image_Data = self.get_Image_data_roots(self.Train_Root) get_Image_Data = self.get_Image_data_roots(self.Train_Root)
Get_ImageGenerator_Image_Data = self.get_Image_data_roots(self.Generator_Root) Get_ImageGenerator_Image_Data = self.get_Image_data_roots(self.Generator_Root)
Get_Total_Image_Data_Root = Merge.merge_dict_to_dict(get_Image_Data, Get_ImageGenerator_Image_Data) # Get_Total_Image_Data_Root = Merge.merge_dict_to_dict(get_Image_Data, Get_ImageGenerator_Image_Data)
# Get_Total_Image_Data_Root = Merge.merge_data_main(get_Image_Data, 0, len(self.Label_List))
return Get_Total_Image_Data_Root return get_Image_Data
def get_Image_data_roots(self, DataRoot) -> dict: def get_Image_data_roots(self, DataRoot) -> dict:
Prepare = Load_Data_Prepare() Prepare = Load_Data_Prepare()

View File

@@ -28,8 +28,8 @@ class Load_Indepentend_Data():
image_processing = Read_image_and_Process_image(123) image_processing = Read_image_and_Process_image(123)
classify_image = [] classify_image = []
Total_Size_List = []
Total_Dict_Data_Root = self.Get_Independent_data_Root(independent_DataRoot) # 讀取測試資料集的資料 Total_Dict_Data_Root = self.Get_Independent_data_Root(independent_DataRoot) # 讀取測試資料集的資料
Total_Dict_Data_Root, Size = Balance_Process(Total_Dict_Data_Root, self.Labels) # 打亂並取出指定資料筆數的資料
Total_List_Data_Root = [] Total_List_Data_Root = []
for Label in self.Labels: for Label in self.Labels:
@@ -41,16 +41,23 @@ class Load_Indepentend_Data():
test_label = image_processing.make_label_list(len(test_title), self.OneHot_Encording[i]) # 製作對應圖片數量的label出來+ test_label = image_processing.make_label_list(len(test_title), self.OneHot_Encording[i]) # 製作對應圖片數量的label出來+
print(self.Labels[i] + "" + str(len(test_label)) + " 筆資料 ") print(self.Labels[i] + "" + str(len(test_label)) + " 筆資料 ")
Total_Size_List.append(len(test_label))
classify_image.append(test_title) classify_image.append(test_title)
Classify_Label.append(test_label) Classify_Label.append(test_label)
i += 1 i += 1
original_test_root = self.merge.merge_data_main(classify_image, 0) test = self.merge.merge_data_main(classify_image, 0, len(self.Labels))
original_test_label = self.merge.merge_data_main(Classify_Label, 0) test_label = self.merge.merge_data_main(Classify_Label, 0, len(self.Labels))
# test = []
# test = image_processing.Data_Augmentation_Image(original_test_root)
# test, test_label = image_processing.image_data_processing(test, original_test_label)
# Balance_Data = list(zip(test, test_label))
# test, test_label = Balance_Process(Balance_Data, Total_Size_List) # 打亂並取出指定資料筆數的資料
# test = image_processing.normalization(test)
test = []
test = image_processing.Data_Augmentation_Image(original_test_root)
test, test_label = image_processing.image_data_processing(test, original_test_label)
return test, test_label return test, test_label

View File

@@ -44,7 +44,7 @@ class Read_image_and_Process_image:
img = img / 255 # 標準化影像資料 img = img / 255 # 標準化影像資料
imgs.append(img) imgs.append(img)
return np.array(imgs) return torch.as_tensor(imgs)
# def load_numpy_data(self, file_names): # def load_numpy_data(self, file_names):
# '''載入numpy圖檔並執行影像處理提高特徵擷取''' # '''載入numpy圖檔並執行影像處理提高特徵擷取'''

View File

@@ -1,60 +1,117 @@
from torch.utils.data import Dataset, DataLoader, RandomSampler, WeightedRandomSampler from torch.utils.data import Dataset, DataLoader, RandomSampler, WeightedRandomSampler, SubsetRandomSampler, Subset
from torchvision.datasets import ImageFolder
import torchvision.transforms as transforms import torchvision.transforms as transforms
from PIL import Image
import torch import torch
import numpy as np
import cv2
class ListDataset(Dataset): class ListDataset(Dataset):
def __init__(self, data_list, labels_list, status): def __init__(self, data_list, labels_list, transform):
self.data = data_list self.data = data_list
self.labels = labels_list self.labels = labels_list
self.status = status self.transform = transform
def __len__(self): def __len__(self):
return len(self.data) return len(self.data)
def __getitem__(self, idx): def __getitem__(self, idx):
sample = self.data[idx] Image_Root = self.data[idx]
try:
with open(Image_Root, 'rb') as file:
Images = Image.open(file).convert("RGB")
# Image = cv2.imread(Image_Root, cv2.IMREAD_COLOR) # 讀檔(彩色)
# Image = cv2.cvtColor(Image, cv2.COLOR_BGR2RGB)
except Exception as e:
print(e)
if self.status: if self.transform is not "Generator":
from Image_Process.Image_Generator import Image_generator Images = self.transform(Images)
ImageGenerator = Image_generator("", "", 12)
Transform = ImageGenerator.Generator_Content(5)
sample = Transform(sample)
Images = torch.tensor(np.array(Images))
label = self.labels[idx] label = self.labels[idx]
return sample, label
# print(f"Dataset_Data: \n{sample}\n")
return Images, label
class Training_Precesses: class Training_Precesses:
def __init__(self, Training_Datas, Training_Labels, Testing_Datas, Testing_Labels): def __init__(self, ImageSize):
self.Training_Datas = Training_Datas seed = 42 # Set an arbitrary integer as the seed
self.Training_Labels = Training_Labels self.ImageSize = ImageSize
self.Testing_Datas = Testing_Datas
self.Testing_Labels = Testing_Labels
seed = 42 # 設定任意整數作為種子
# 產生隨機種子產生器
self.generator = torch.Generator() self.generator = torch.Generator()
self.generator.manual_seed(seed) self.generator.manual_seed(seed)
pass def Dataloader_Sampler(self, SubDataSet, Batch_Size, Sampler=True):
if Sampler:
# Data_Loader = DataLoader(
# dataset=SubDataSet,
# batch_size=Batch_Size,
# num_workers=0,
# pin_memory=True,
# sampler=self.Setting_RandomSampler_Content(SubDataSet)
# )
Data_Loader = DataLoader(
dataset=SubDataSet,
batch_size=Batch_Size,
num_workers=0,
pin_memory=True,
sampler=self.Setting_RandomSampler_Content(SubDataSet)
)
else:
Data_Loader = DataLoader(
dataset=SubDataSet,
batch_size=Batch_Size,
num_workers=0,
pin_memory=True,
shuffle=True
)
return Data_Loader
def Total_Data_Combine_To_DataLoader(self, Batch_Size): def Setting_WeightedRandomSampler_Content(self, SubDataSet):
Training_Dataset = self.Convert_Data_To_DataSet(self.Training_Datas, self.Training_Labels) # Check if SubDataSet is a Subset or a full dataset
Testing_Dataset = self.Convert_Data_To_DataSet(self.Testing_Datas, self.Testing_Labels) if isinstance(SubDataSet, Subset):
# Get the underlying dataset and subset indices
base_dataset = SubDataSet.dataset
subset_indices = SubDataSet.indices
# Extract labels for the subset
labels = [base_dataset.labels[i] for i in subset_indices]
else:
# Assume SubDataSet is a ListDataset or similar
labels = SubDataSet.labels
Training_DataLoader = DataLoader(dataset = Training_Dataset, batch_size = Batch_Size, num_workers = 0, pin_memory=True, shuffle = True) # Convert labels to class indices if they are one-hot encoded
Testing_DataLoader = DataLoader(dataset = Testing_Dataset, batch_size = 1, num_workers = 0, pin_memory=True, shuffle = True) labels = np.array(labels)
if labels.ndim > 1: # If one-hot encoded
labels = np.argmax(labels, axis=1)
# Count occurrences of each class
class_counts = np.bincount(labels)
class_weights = 1.0 / class_counts # Inverse frequency as weight
sample_weights = class_weights[labels] # Assign weight to each sample
return Training_DataLoader, Testing_DataLoader return WeightedRandomSampler(
weights=sample_weights,
num_samples=len(sample_weights),
replacement=True
)
def Combine_Signal_Dataset_To_DataLoader(self, datas : list, Labels : list, Batch_Size, status : bool = True): def Setting_RandomSampler_Content(self, Dataset):
dataset = self.Convert_Data_To_DataSet(datas, Labels, status) return RandomSampler(Dataset, generator = self.generator)
sampler = RandomSampler(dataset, generator = self.generator) # 創建Sampler
Dataloader = DataLoader(dataset = dataset, batch_size = Batch_Size, num_workers = 0, pin_memory=True, sampler = sampler)
return Dataloader
def Convert_Data_To_DataSet(self, Datas : list, Labels : list, status : bool = True): def Setting_DataSet(self, Datas, Labels, transform = None):
# 創建 Dataset # 資料預處理
list_dataset = ListDataset(Datas, Labels, status) if transform == None:
transform = transforms.Compose([
transforms.Resize((256, 256))
])
elif transform == "Transform":
transform = transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor()
])
elif transform == "Generator":
transform = "Generator"
# Create Dataset
return list_dataset list_dataset = ListDataset(Datas, Labels , transform)
return list_dataset

View File

@@ -23,17 +23,18 @@ class Tool:
def Set_Labels(self): def Set_Labels(self):
self.__Labels = ["stomach_cancer_Crop", "Normal_Crop", "Have_Question_Crop"] self.__Labels = ["stomach_cancer_Crop", "Normal_Crop", "Have_Question_Crop"]
# self.__Labels = ["NPC_negative", "NPC_positive"]
def Set_Save_Roots(self): def Set_Save_Roots(self):
self.__ICG_Training_Root = "../Dataset/Training/CA_ICG" self.__ICG_Training_Root = "../Dataset/Training"
self.__Normal_Training_Root = "../Dataset/Training/CA" self.__Normal_Training_Root = "../Dataset/Training/CA"
self.__Comprehensive_Training_Root = "../Dataset/Training/Mixed" self.__Comprehensive_Training_Root = "../Dataset/Training/Mixed"
self.__ICG_Test_Data_Root = "../Dataset/Training/CA_ICG_TestData" self.__ICG_Test_Data_Root = "../Dataset/Testing"
self.__Normal_Test_Data_Root = "../Dataset/Training/Normal_TestData" self.__Normal_Test_Data_Root = "../Dataset/Training/Normal_TestData"
self.__Comprehensive_Testing_Root = "../Dataset/Training/Comprehensive_TestData" self.__Comprehensive_Testing_Root = "../Dataset/Training/Comprehensive_TestData"
self.__ICG_ImageGenerator_Data_Root = "../Dataset/Training/ICG_ImageGenerator" self.__ICG_ImageGenerator_Data_Root = "../Dataset/ImageGenerator"
self.__Normal_ImageGenerator_Data_Root = "../Dataset/Training/Normal_ImageGenerator" self.__Normal_ImageGenerator_Data_Root = "../Dataset/Training/Normal_ImageGenerator"
self.__Comprehensive_Generator_Root = "../Dataset/Training/Comprehensive_ImageGenerator" self.__Comprehensive_Generator_Root = "../Dataset/Training/Comprehensive_ImageGenerator"

View File

@@ -61,7 +61,6 @@ def call_back(model_name, index, optimizer):
factor = 0.94, # 學習率降低的量。 new_lr = lr * factor factor = 0.94, # 學習率降低的量。 new_lr = lr * factor
patience = 2, # 沒有改進的時期數,之後學習率將降低 patience = 2, # 沒有改進的時期數,之後學習率將降低
verbose = 0, verbose = 0,
mode = 'min',
min_lr = 0 # 學習率下限 min_lr = 0 # 學習率下限
) )

View File

@@ -8,7 +8,6 @@ import matplotlib.pyplot as plt
import datetime import datetime
from Load_process.file_processing import Process_File from Load_process.file_processing import Process_File
# Grad-CAM implementation
class GradCAM: class GradCAM:
def __init__(self, model, target_layer): def __init__(self, model, target_layer):
self.model = model self.model = model
@@ -16,74 +15,90 @@ class GradCAM:
self.activations = None self.activations = None
self.gradients = None self.gradients = None
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
self.model.to(self.device) # Ensure model is on the correct device
# Register hooks # Register hooks
self.target_layer.register_forward_hook(self.save_activations) self.target_layer.register_forward_hook(self.save_activations)
self.target_layer.register_backward_hook(self.save_gradients) self.target_layer.register_backward_hook(self.save_gradients)
def Processing_Main(self, Test_Dataloader, File_Path): def Processing_Main(self, Test_Dataloader, File_Path):
i = 0
path = File_Path
File = Process_File() File = Process_File()
for images, labels in Test_Dataloader: for batch_idx, (images, labels) in enumerate(Test_Dataloader):
labels = torch.as_tensor(labels, dtype=torch.float32).to(self.device) # Move data to device
Generate_Image = self.generate(torch.as_tensor(images,dtype=torch.float32).to(self.device)) images = images.to(self.device, dtype=torch.float32) # [64, C, H, W]
labels = labels.to(self.device, dtype=torch.float32) # [64, num_classes]
path = File_Path
path += str(np.argmax(labels.cpu().numpy(), 1)[0])
File.JudgeRoot_MakeDir(path)
for Image_Batch in images:
File.Save_CV2_File(f"{str(i)}.png", path, self.overlay_heatmap(Generate_Image, Image_Batch))
i += 1
pass # Get ground-truth class indices
label_classes = torch.argmax(labels, dim=1).cpu().numpy() # [64]
# Generate Grad-CAM heatmaps for the entire batch
heatmaps = self.generate(images, label_classes)
# Process each image in the batch
for i in range(images.size(0)): # Loop over batch size (64)
class_idx = label_classes[i]
heatmap = heatmaps[i] # Extract heatmap for this image
overlaid_image = self.overlay_heatmap(heatmap, images[i])
# Create file path based on class
path = f"{File_Path}/class_{class_idx}"
File.JudgeRoot_MakeDir(path)
File.Save_CV2_File(f"batch_{batch_idx}_img_{i}.png", path, overlaid_image)
def save_activations(self, module, input, output): def save_activations(self, module, input, output):
self.activations = output.detach() self.activations = output.detach() # [64, C, H', W']
def save_gradients(self, module, grad_input, grad_output): def save_gradients(self, module, grad_input, grad_output):
self.gradients = grad_output[0].detach() self.gradients = grad_output[0].detach() # [64, C, H', W']
def generate(self, input_image, class_idx=None): def generate(self, input_images, class_indices=None):
self.model.eval() self.model.eval()
input_image.requires_grad = True input_images.requires_grad = True # [64, C, H, W]
# Forward pass # Forward pass
output = self.model(input_image) outputs = self.model(input_images) # [64, num_classes]
if class_idx is None: if class_indices is None:
class_idx = torch.argmax(output, dim=1).item() # Use predicted class if not specified class_indices = torch.argmax(outputs, dim=1).cpu().numpy() # [64]
# Zero gradients # Zero gradients
self.model.zero_grad() self.model.zero_grad()
# Backward pass for the specific class # Backward pass for each image in the batch
output[0, class_idx].backward() heatmaps = []
for i in range(input_images.size(0)):
self.model.zero_grad()
outputs[i, class_indices[i]].backward(retain_graph=True) # Backward for specific image/class
heatmap = self._compute_heatmap()
heatmaps.append(heatmap)
return np.stack(heatmaps) # [64, H', W']
def _compute_heatmap(self):
# Get gradients and activations # Get gradients and activations
gradients = self.gradients # [B, C, H, W] gradients = self.gradients # [64, C, H', W']
activations = self.activations # [B, C, H, W] activations = self.activations # [64, C, H', W']
# Compute weights (global average pooling of gradients) # Compute weights (global average pooling of gradients)
weights = torch.mean(gradients, dim=[2, 3], keepdim=True) # [B, C, 1, 1] weights = torch.mean(gradients, dim=[2, 3], keepdim=True) # [64, C, 1, 1]
# Compute Grad-CAM heatmap # Compute Grad-CAM heatmap for one image (after single backward)
grad_cam = torch.sum(weights * activations, dim=1).squeeze() # [H, W] grad_cam = torch.sum(weights * activations, dim=1)[0] # [64, H', W'] -> [H', W']
grad_cam = F.relu(grad_cam) # Apply ReLU grad_cam = F.relu(grad_cam) # Apply ReLU
grad_cam = grad_cam / (grad_cam.max() + 1e-8) # Normalize to [0, 1] grad_cam = grad_cam / (grad_cam.max() + 1e-8) # Normalize to [0, 1]
return grad_cam.cpu().numpy() return grad_cam.cpu().numpy()
# Utility to overlay heatmap on original image
def overlay_heatmap(self, heatmap, image, alpha=0.5): def overlay_heatmap(self, heatmap, image, alpha=0.5):
heatmap = np.uint8(255 * heatmap) # Scale to 0-255 # Resize heatmap to match input image spatial dimensions
heatmap = np.uint8(255 * heatmap) # Scale to [0, 255]
heatmap = Image.fromarray(heatmap).resize((image.shape[1], image.shape[2]), Image.BILINEAR) heatmap = Image.fromarray(heatmap).resize((image.shape[1], image.shape[2]), Image.BILINEAR)
heatmap = np.array(heatmap) heatmap = np.array(heatmap)
heatmap = plt.cm.jet(heatmap)[:, :, :3] # Apply colormap (e.g., jet) heatmap = plt.cm.jet(heatmap)[:, :, :3] # Apply colormap (jet)
image = torch.as_tensor(image, dtype=torch.float32).permute(2, 1, 0) # Convert image tensor to numpy and denormalize (assuming ImageNet stats)
image_np = image.detach().cpu().permute(1, 2, 0).numpy() # [H, W, C]
overlay = (alpha * heatmap + (1 - alpha) * np.array(image) / 255.0)
# Overlay
overlay = alpha * heatmap + (1 - alpha) * image_np / 255.0
overlay = np.clip(overlay, 0, 1) * 255 overlay = np.clip(overlay, 0, 1) * 255
return overlay return overlay.astype(np.uint8) # Return uint8 for cv2

View File

@@ -1,6 +1,7 @@
from tqdm import tqdm from tqdm import tqdm
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from torchmetrics.functional import auroc from torchmetrics.functional import auroc
from torch.nn import functional
from all_models_tools.all_model_tools import call_back from all_models_tools.all_model_tools import call_back
from Model_Loss.Loss import Entropy_Loss from Model_Loss.Loss import Entropy_Loss
@@ -26,7 +27,7 @@ class All_Step:
self.Model_Name = Model_Name self.Model_Name = Model_Name
self.Experiment_Name = Experiment_Name self.Experiment_Name = Experiment_Name
def Training_Step(self, train_subset, train_loader, val_loader, model_name, fold, TargetLayer): def Training_Step(self, train_subset, val_subset, train_loader, val_loader, model_name, fold, TargetLayer):
# Reinitialize model and optimizer for each fold # Reinitialize model and optimizer for each fold
# self.Model = self.Model.__class__(self.Number_Of_Classes).to(self.device) # Reinitialize model # self.Model = self.Model.__class__(self.Number_Of_Classes).to(self.device) # Reinitialize model
Optimizer = optim.SGD(self.Model.parameters(), lr=0.045, momentum=0.9, weight_decay=0.01) Optimizer = optim.SGD(self.Model.parameters(), lr=0.045, momentum=0.9, weight_decay=0.01)
@@ -53,6 +54,7 @@ class All_Step:
# Calculate epoch start time # Calculate epoch start time
start_time = time.time() start_time = time.time()
total_samples = len(train_subset) # Total samples in subset, not DataLoader total_samples = len(train_subset) # Total samples in subset, not DataLoader
total_Validation_samples = len(val_subset)
# Progress bar for training batches # Progress bar for training batches
epoch_iterator = tqdm(train_loader, desc=f"Fold {fold + 1}/5, Epoch [{epoch + 1}/{self.Epoch}]") epoch_iterator = tqdm(train_loader, desc=f"Fold {fold + 1}/5, Epoch [{epoch + 1}/{self.Epoch}]")
@@ -83,7 +85,7 @@ class All_Step:
eta = (total_samples - processed_samples) / iterations_per_second if iterations_per_second > 0 else 0 eta = (total_samples - processed_samples) / iterations_per_second if iterations_per_second > 0 else 0
time_str = f"{int(elapsed_time//60):02d}:{int(elapsed_time%60):02d}<{int(eta//60):02d}:{int(eta%60):02d}" time_str = f"{int(elapsed_time//60):02d}:{int(elapsed_time%60):02d}<{int(eta//60):02d}:{int(eta%60):02d}"
# Calculate batch accuracy # Calculate batch accuracy(正確label數量 / 該batch總共的label數量)
batch_accuracy = (Output_Indexs.cpu().numpy() == True_Indexs).mean() batch_accuracy = (Output_Indexs.cpu().numpy() == True_Indexs).mean()
# Update progress bar # Update progress bar
@@ -93,7 +95,6 @@ class All_Step:
) )
epoch_iterator.close() epoch_iterator.close()
# Merge predictions and labels # Merge predictions and labels
all_train_preds = Merge_Function.merge_data_main(all_train_preds, 0, len(all_train_preds)) all_train_preds = Merge_Function.merge_data_main(all_train_preds, 0, len(all_train_preds))
all_train_labels = Merge_Function.merge_data_main(all_train_labels, 0, len(all_train_labels)) all_train_labels = Merge_Function.merge_data_main(all_train_labels, 0, len(all_train_labels))
@@ -110,8 +111,10 @@ class All_Step:
all_val_preds = [] all_val_preds = []
all_val_labels = [] all_val_labels = []
start_Validation_time = time.time()
epoch_iterator = tqdm(val_loader, desc=f"\tValidation-Fold {fold + 1}/5, Epoch [{epoch + 1}/{self.Epoch}]")
with torch.no_grad(): with torch.no_grad():
for inputs, labels in val_loader: for inputs, labels in epoch_iterator:
inputs, labels = inputs.to(self.device), labels.to(self.device) inputs, labels = inputs.to(self.device), labels.to(self.device)
outputs = self.Model(inputs) outputs = self.Model(inputs)
loss = criterion(outputs, labels) loss = criterion(outputs, labels)
@@ -124,6 +127,27 @@ class All_Step:
all_val_preds.append(Output_Indexs.cpu().numpy()) all_val_preds.append(Output_Indexs.cpu().numpy())
all_val_labels.append(True_Indexs) all_val_labels.append(True_Indexs)
processed_samples += inputs.size(0) # Use size(0) for batch size
# Calculate progress and timing
progress = (processed_samples / total_Validation_samples) * 100
elapsed_time = time.time() - start_Validation_time
iterations_per_second = processed_samples / elapsed_time if elapsed_time > 0 else 0
eta = (total_Validation_samples - processed_samples) / iterations_per_second if iterations_per_second > 0 else 0
time_str = f"{int(elapsed_time//60):02d}:{int(elapsed_time%60):02d}<{int(eta//60):02d}:{int(eta%60):02d}"
# Calculate batch accuracy
batch_accuracy = (Output_Indexs.cpu().numpy() == True_Indexs).mean()
# Update progress bar
epoch_iterator.set_postfix_str(
f"{processed_samples}/{total_Validation_samples} [{time_str}, {iterations_per_second:.2f}it/s, "
f"acc={batch_accuracy:.3f}, loss={loss.item():.3f}]"
)
epoch_iterator.close()
print("\n")
# Merge predictions and labels # Merge predictions and labels
all_val_preds = Merge_Function.merge_data_main(all_val_preds, 0, len(all_val_preds)) all_val_preds = Merge_Function.merge_data_main(all_val_preds, 0, len(all_val_preds))
all_val_labels = Merge_Function.merge_data_main(all_val_labels, 0, len(all_val_labels)) all_val_labels = Merge_Function.merge_data_main(all_val_labels, 0, len(all_val_labels))
@@ -134,8 +158,11 @@ class All_Step:
val_losses.append(val_loss) val_losses.append(val_loss)
val_accuracies.append(val_accuracy) val_accuracies.append(val_accuracy)
Grad = GradCAM(self.Model, TargetLayer) print(f"Traini Loss: {Training_Loss:.4f}, Accuracy: {train_accuracy:0.2f}, Validation Loss: {val_loss:.4f}, Accuracy: {val_accuracy:0.2f}\n")
Grad.Processing_Main(val_loader, f"../Result/GradCAM_Image/Validation/GradCAM_Image({str(datetime.date.today())})/fold-{str(fold)}/")
if epoch % 10 == 0:
Grad = GradCAM(self.Model, TargetLayer)
Grad.Processing_Main(val_loader, f"../Result/GradCAM_Image/Validation/GradCAM_Image({str(datetime.date.today())})/fold-{str(fold)}/")
# Early stopping # Early stopping
early_stopping(val_loss, self.Model, model_path) early_stopping(val_loss, self.Model, model_path)
@@ -147,7 +174,7 @@ class All_Step:
scheduler.step(val_loss) scheduler.step(val_loss)
Total_Epoch = epoch + 1 Total_Epoch = epoch + 1
return self.Model, train_losses, val_losses, train_accuracies, val_accuracies, Total_Epoch return self.Model, model_path, train_losses, val_losses, train_accuracies, val_accuracies, Total_Epoch
def Evaluate_Model(self, cnn_model, Test_Dataloader): def Evaluate_Model(self, cnn_model, Test_Dataloader):
# (Unchanged Evaluate_Model method) # (Unchanged Evaluate_Model method)
@@ -166,7 +193,7 @@ class All_Step:
True_Label.append(Output_Indexs.cpu().numpy()) True_Label.append(Output_Indexs.cpu().numpy())
Predict_Label.append(True_Indexs) Predict_Label.append(True_Indexs)
Predict_Label_OneHot.append(torch.tensor(outputs, dtype=torch.float32).cpu().numpy()[0]) Predict_Label_OneHot.append(torch.tensor(functional.one_hot(Output_Indexs, self.Number_Of_Classes), dtype=torch.float32).cpu().numpy()[0])
True_Label_OneHot.append(torch.tensor(labels, dtype=torch.int).cpu().numpy()[0]) True_Label_OneHot.append(torch.tensor(labels, dtype=torch.int).cpu().numpy()[0])
loss /= len(Test_Dataloader) loss /= len(Test_Dataloader)

View File

@@ -2,7 +2,7 @@ from torchinfo import summary
from sklearn.model_selection import KFold from sklearn.model_selection import KFold
from sklearn.metrics import confusion_matrix from sklearn.metrics import confusion_matrix
from Training_Tools.PreProcess import Training_Precesses, ListDataset from Training_Tools.PreProcess import Training_Precesses
from experiments.pytorch_Model import ModifiedXception from experiments.pytorch_Model import ModifiedXception
from experiments.Model_All_Step import All_Step from experiments.Model_All_Step import All_Step
from Load_process.Load_Indepentend import Load_Indepentend_Data from Load_process.Load_Indepentend import Load_Indepentend_Data
@@ -10,6 +10,7 @@ from _validation.ValidationTheEnterData import validation_the_enter_data
from Load_process.file_processing import Process_File from Load_process.file_processing import Process_File
from draw_tools.Grad_cam import GradCAM from draw_tools.Grad_cam import GradCAM
from draw_tools.draw import plot_history, draw_heatmap from draw_tools.draw import plot_history, draw_heatmap
from Calculate_Process.Calculate import Calculate
import numpy as np import numpy as np
import torch import torch
@@ -66,8 +67,11 @@ class experiments():
pass pass
def processing_main(self, Training_Data, Training_Label, counter): def processing_main(self, Training_Data, Training_Label):
Train, Test = self.Topic_Tool.Get_Save_Roots(self.Status) # 要換不同資料集就要改 Train, Test = self.Topic_Tool.Get_Save_Roots(self.Status)
Calculate_Process = Calculate()
print(f"Training Data Content: {Training_Data[0]}")
start = time.time() start = time.time()
self.cut_image.process_main(Test) # 呼叫處理test Data與Validation Data的function self.cut_image.process_main(Test) # 呼叫處理test Data與Validation Data的function
@@ -76,10 +80,17 @@ class experiments():
# 將處理好的test Data 與 Validation Data 丟給這個物件的變數 # 將處理好的test Data 與 Validation Data 丟給這個物件的變數
self.test, self.test_label = self.cut_image.test, self.cut_image.test_label self.test, self.test_label = self.cut_image.test, self.cut_image.test_label
# self.test = self.test.permute(0, 3, 1, 2)
# Training_Data = Training_Data.permute(0, 3, 1, 2)
PreProcess = Training_Precesses(Training_Data, Training_Label, self.test, self.test_label) PreProcess = Training_Precesses(self.Image_Size)
File = Process_File() File = Process_File()
self.Training_DataLoader, self.Test_Dataloader = PreProcess.Total_Data_Combine_To_DataLoader(self.train_batch_size)
# print(f"Dataset_Data: \n{self.test}\nLabel: \n{self.test_label}\n")
Testing_Dataset = PreProcess.Setting_DataSet(self.test, self.test_label, "Transform")
self.Test_Dataloader = PreProcess.Dataloader_Sampler(Testing_Dataset, 1, False)
# for images, labels in self.Test_Dataloader:
# print(images.shape)
# Lists to store metrics across all folds # Lists to store metrics across all folds
all_fold_train_losses = [] all_fold_train_losses = []
@@ -90,8 +101,7 @@ class experiments():
# Define K-fold cross-validator # Define K-fold cross-validator
K_Fold = KFold(n_splits = 5, shuffle = True, random_state = 42) K_Fold = KFold(n_splits = 5, shuffle = True, random_state = 42)
# Get the underlying dataset from PreProcess_Classes_Data # Get the underlying dataset from PreProcess_Classes_Data
training_dataset = ListDataset(data_list = PreProcess.Training_Datas, labels_list = PreProcess.Training_Labels, status = True) training_dataset = PreProcess.Setting_DataSet(Training_Data, Training_Label, "Transform")
# K-Fold loop # K-Fold loop
for fold, (train_idx, val_idx) in enumerate(K_Fold.split(training_dataset)): for fold, (train_idx, val_idx) in enumerate(K_Fold.split(training_dataset)):
@@ -104,19 +114,20 @@ class experiments():
Grad = GradCAM(cnn_model, TargetLayer) Grad = GradCAM(cnn_model, TargetLayer)
step = All_Step(cnn_model, self.epoch, self.Number_Of_Classes, self.model_name, self.experiment_name) step = All_Step(cnn_model, self.epoch, self.Number_Of_Classes, self.model_name, self.experiment_name)
print("\n\n\n讀取訓練資料(70000)執行時間:%f\n\n" % (end - start)) print("\n\n\n讀取訓練資料執行時間:%f\n\n" % (end - start))
print(f"\nStarting Fold {fold + 1}/5") print(f"\nStarting Fold {fold + 1}/5")
# Create training and validation subsets for this fold # Create training and validation subsets for this fold
train_subset = torch.utils.data.Subset(training_dataset, train_idx) train_subset = torch.utils.data.Subset(training_dataset, train_idx)
val_subset = torch.utils.data.Subset(training_dataset, val_idx) val_subset = torch.utils.data.Subset(training_dataset, val_idx)
# Wrap subsets in DataLoaders (use same batch size as original) # print(f"Dataset_Data: \n{train_subset.dataset.data}\nLabel: \n{train_subset.dataset.labels}\n")
batch_size = self.Training_DataLoader.batch_size
train_loader = torch.utils.data.DataLoader(train_subset, batch_size=batch_size, shuffle=True)
val_loader = torch.utils.data.DataLoader(val_subset, batch_size=batch_size, shuffle=False)
cnn_model, train_losses, val_losses, train_accuracies, val_accuracies, Total_Epoch = step.Training_Step(train_subset, train_loader, val_loader, self.model_name, fold, TargetLayer) # Wrap subsets in DataLoaders (use same batch size as original)
train_loader = PreProcess.Dataloader_Sampler(train_subset , self.train_batch_size, False)
val_loader = PreProcess.Dataloader_Sampler(val_subset, self.train_batch_size, False)
cnn_model, model_path, train_losses, val_losses, train_accuracies, val_accuracies, Total_Epoch = step.Training_Step(train_subset, val_subset, train_loader, val_loader, self.model_name, fold, TargetLayer)
# Store fold results # Store fold results
all_fold_train_losses.append(train_losses) all_fold_train_losses.append(train_losses)
@@ -128,11 +139,14 @@ class experiments():
Accuracies = [train_accuracies, val_accuracies] Accuracies = [train_accuracies, val_accuracies]
plot_history(Total_Epoch, Losses, Accuracies, "train" + str(fold), self.experiment_name) # 將訓練結果化成圖,並將化出來的圖丟出去儲存 plot_history(Total_Epoch, Losses, Accuracies, "train" + str(fold), self.experiment_name) # 將訓練結果化成圖,並將化出來的圖丟出去儲存
cnn_model.load_state_dict(torch.load(model_path))
True_Label, Predict_Label, loss, accuracy, precision, recall, AUC, f1 = step.Evaluate_Model(cnn_model, self.Test_Dataloader) True_Label, Predict_Label, loss, accuracy, precision, recall, AUC, f1 = step.Evaluate_Model(cnn_model, self.Test_Dataloader)
Grad.Processing_Main(self.Test_Dataloader, f"../Result/GradCAM_Image/Testing/GradCAM_Image({str(datetime.date.today())})/fold-{str(fold)}/")
Grad.Processing_Main(self.Test_Dataloader, f"../Result/GradCAM_Image/Testing/GradCAM_Image({str(datetime.date.today())})/fold-{str(fold)}")
Calculate_Process.Append_numbers(loss, accuracy, precision, recall, AUC, f1)
Matrix = self.record_matrix_image(True_Label, Predict_Label, self.model_name, counter) self.record_matrix_image(True_Label, Predict_Label, self.experiment_name, fold)
print(self.record_everyTime_test_result(loss, accuracy, precision, recall, AUC, f1, counter, self.experiment_name, Matrix)) # 紀錄當前訓練完之後的預測結果並輸出成csv檔 print(self.record_everyTime_test_result(loss, accuracy, precision, recall, AUC, f1, fold, self.experiment_name)) # 紀錄當前訓練完之後的預測結果並輸出成csv檔
# Aggregate results across folds # Aggregate results across folds
avg_train_losses = np.mean([losses[-1] for losses in all_fold_train_losses]) avg_train_losses = np.mean([losses[-1] for losses in all_fold_train_losses])
@@ -144,6 +158,11 @@ class experiments():
print(f"Avg Train Loss: {avg_train_losses:.4f}, Avg Val Loss: {avg_val_losses:.4f}") print(f"Avg Train Loss: {avg_train_losses:.4f}, Avg Val Loss: {avg_val_losses:.4f}")
print(f"Avg Train Acc: {avg_train_accuracies:.4f}, Avg Val Acc: {avg_val_accuracies:.4f}") print(f"Avg Train Acc: {avg_train_accuracies:.4f}, Avg Val Acc: {avg_val_accuracies:.4f}")
Calculate_Process.Calculate_Mean()
Calculate_Process.Calculate_Std()
print(Calculate_Process.Output_Style())
File.Save_TXT_File(content = f"\nCross-Validation Results:\nAvg Train Loss: {avg_train_losses:.4f}, Avg Val Loss: {avg_val_losses:.4f}\nAvg Train Acc: {avg_train_accuracies:.4f}, Avg Val Acc: {avg_val_accuracies:.4f}\n", File_Name = "Training_Average_Result") File.Save_TXT_File(content = f"\nCross-Validation Results:\nAvg Train Loss: {avg_train_losses:.4f}, Avg Val Loss: {avg_val_losses:.4f}\nAvg Train Acc: {avg_train_accuracies:.4f}, Avg Val Acc: {avg_val_accuracies:.4f}\n", File_Name = "Training_Average_Result")
pass pass
@@ -163,10 +182,8 @@ class experiments():
# 計算混淆矩陣 # 計算混淆矩陣
matrix = confusion_matrix(True_Labels, Predict_Labels) matrix = confusion_matrix(True_Labels, Predict_Labels)
draw_heatmap(matrix, model_name, index) # 呼叫畫出confusion matrix的function draw_heatmap(matrix, model_name, index) # 呼叫畫出confusion matrix的function
return matrix
def record_everyTime_test_result(self, loss, accuracy, precision, recall, auc, f, indexs, model_name, Matrix): def record_everyTime_test_result(self, loss, accuracy, precision, recall, auc, f, indexs, model_name):
'''記錄我單次的訓練結果並將它輸出到檔案中''' '''記錄我單次的訓練結果並將它輸出到檔案中'''
File = Process_File() File = Process_File()

View File

@@ -2,40 +2,66 @@ import torch.nn as nn
import timm import timm
# class ModifiedXception(nn.Module):
# def __init__(self, num_classes):
# super(ModifiedXception, self).__init__()
# # Load Xception pre-trained model (full model, not just features)
# self.base_model = timm.create_model(
# 'xception',
# pretrained=True
# )
# # Replace the default global pooling with AdaptiveAvgPool2d
# self.base_model.global_pool = nn.AdaptiveAvgPool2d(output_size=1) # Output size of 1x1 spatially
# # Replace the final fully connected layer with Identity to get features
# self.base_model.fc = nn.Identity() # Output will be 2048 (Xception's default feature size)
# # Custom head: Linear from 2048 to 1370, additional 1370 layer, then to num_classes
# self.custom_head = nn.Sequential(
# nn.Linear(2048, 1025), # From Xceptions 2048 features to 1370
# nn.ReLU(), # Activation
# nn.Dropout(0.6), # Dropout for regularization
# nn.Linear(1025, num_classes) # Final output layer
# # nn.Softmax(dim = 1) # Sigmoid for binary/multi-label classification
# )
# def forward(self, x):
# # Pass through the base Xception model (up to global pooling)
# x = self.base_model.forward_features(x) # Get feature maps
# x = self.base_model.global_pool(x) # Apply AdaptiveAvgPool2d (output: [B, 2048, 1, 1])
# x = x.flatten(1) # Flatten to [B, 2048]
# # x = self.base_model.fc(x) # Identity layer (still [B, 2048])
# output = self.custom_head(x) # Custom head processing
# return output
class ModifiedXception(nn.Module): class ModifiedXception(nn.Module):
def __init__(self, num_classes): def __init__(self, num_classes):
super(ModifiedXception, self).__init__() super(ModifiedXception, self).__init__()
# Load Xception pre-trained model (full model, not just features) # 加載 Xception 預訓練模型,去掉最後一層 (fc 層)
self.base_model = timm.create_model( self.base_model = timm.create_model('xception', pretrained=True)
'xception', self.base_model.fc = nn.Identity() # 移除原來的 fully connected 層
pretrained=True,
drop_rate=0.0, # Optional: adjust dropout if needed
)
# Replace the default global pooling with AdaptiveAvgPool2d # 新增全局平均池化層、隱藏層和輸出層
self.base_model.global_pool = nn.AdaptiveAvgPool2d(output_size=1) # Output size of 1x1 spatially GAP_Output = 2048
self.global_avg_pool = nn.AdaptiveAvgPool1d(2048) # 全局平均池化
self.hidden_layer = nn.Linear(2048, 1025) # 隱藏層,輸入大小取決於 Xception 的輸出大小
self.output_layer = nn.Linear(1025, num_classes) # 輸出層,依據分類數目設定
# Replace the final fully connected layer with Identity to get features # 激活函數與 dropout
self.base_model.fc = nn.Identity() # Output will be 2048 (Xception's default feature size) self.relu = nn.ReLU()
self.softmax = nn.Softmax(1)
# Custom head: Linear from 2048 to 1370, additional 1370 layer, then to num_classes self.dropout = nn.Dropout(0.6)
self.custom_head = nn.Sequential(
nn.Linear(2048, 1025), # From Xceptions 2048 features to 1370
nn.ReLU(), # Activation
nn.Dropout(0.6), # Dropout for regularization
nn.Linear(1025, num_classes), # Final output layer
nn.Sigmoid() # Sigmoid for binary/multi-label classification
)
def forward(self, x): def forward(self, x):
# Pass through the base Xception model (up to global pooling) x = self.base_model(x) # Xception 主體
x = self.base_model.forward_features(x) # Get feature maps x = self.global_avg_pool(x) # 全局平均池化
x = self.base_model.global_pool(x) # Apply AdaptiveAvgPool2d (output: [B, 2048, 1, 1]) x = self.relu(self.hidden_layer(x)) # 隱藏層 + ReLU
x = x.flatten(1) # Flatten to [B, 2048] x = self.dropout(x) # Dropout
x = self.base_model.fc(x) # Identity layer (still [B, 2048]) x = self.output_layer(x) # 輸出層
output = self.custom_head(x) # Custom head processing return x
return output
class Model_module(): class Model_module():
def __init__(self): def __init__(self):

74
main.py
View File

@@ -23,7 +23,7 @@ if __name__ == "__main__":
tool.Set_Labels() tool.Set_Labels()
tool.Set_Save_Roots() tool.Set_Save_Roots()
Status = 2 # 決定要使用什麼資料集 Status = 1 # 決定要使用什麼資料集
Labels = tool.Get_Data_Label() Labels = tool.Get_Data_Label()
Trainig_Root, Testing_Root = tool.Get_Save_Roots(Status) # 一般的 Trainig_Root, Testing_Root = tool.Get_Save_Roots(Status) # 一般的
Generator_Root = tool.Get_Generator_Save_Roots(Status) Generator_Root = tool.Get_Generator_Save_Roots(Status)
@@ -33,64 +33,66 @@ if __name__ == "__main__":
Encording_Label = tool.Get_OneHot_Encording_Label() Encording_Label = tool.Get_OneHot_Encording_Label()
Label_Length = len(Labels) Label_Length = len(Labels)
Classification = 3 # 分類數量
Model_Name = "Xception" # 取名,告訴我我是用哪個模型(可能是預處理模型/自己設計的模型) Model_Name = "Xception" # 取名,告訴我我是用哪個模型(可能是預處理模型/自己設計的模型)
Experiment_Name = "Xception Skin is used RandomSampler to train ICG stomach cancer" Experiment_Name = "Xception Skin trains Stomach Cancer Dataset with original images"
Epoch = 10000 Epoch = 10000
Train_Batch_Size = 64 Train_Batch_Size = 64
Image_Size = 256 Image_Size = 256
Prepare = Load_Data_Prepare() Prepare = Load_Data_Prepare()
loading_data = Load_ImageGenerator(Trainig_Root, Testing_Root, Generator_Root, Labels, Image_Size) loading_data = Load_ImageGenerator(Trainig_Root, Testing_Root, Generator_Root, Labels, Image_Size)
experiment = experiments(Image_Size, Model_Name, Experiment_Name, Epoch, Train_Batch_Size, tool, Classification, Status) experiment = experiments(Image_Size, Model_Name, Experiment_Name, Epoch, Train_Batch_Size, tool, Label_Length, Status)
image_processing = Read_image_and_Process_image(Image_Size) image_processing = Read_image_and_Process_image(Image_Size)
Merge = merge() Merge = merge()
Calculate_Tool = Calculate() Calculate_Tool = Calculate()
counter = 1 counter = 1
Batch_Size = 128
Train_Size = 0 Train_Size = 0
for Run_Range in range(0, counter, 1): # 做規定次數的訓練 # 讀取資料
# 讀取資料 Data_Dict_Data = loading_data.process_main(Label_Length)
Data_Dict_Data = loading_data.process_main(Label_Length) Total_Size_List = []
Data_Dict_Data, Train_Size = Balance_Process(Data_Dict_Data, Labels)
for label in Labels: for label in Labels:
Train_Size += len(Data_Dict_Data[label]) Train_Size += len(Data_Dict_Data[label])
Total_Size_List.append(len(Data_Dict_Data[label]))
print(f"Labels: {label}, 總數為: {len(Data_Dict_Data[label])}")
print("總共有 " + str(Train_Size) + " 筆資料") print("總共有 " + str(Train_Size) + " 筆資料")
# 做出跟資料相同數量的Label # 做出跟資料相同數量的Label
Classes = [] Classes = []
i = 0 i = 0
for encording in Encording_Label: for encording in Encording_Label:
Classes.append(image_processing.make_label_list(Train_Size, encording)) Classes.append(image_processing.make_label_list(Total_Size_List[i], encording))
i += 1 i += 1
# 將資料做成Dict的資料型態 # 將資料做成Dict的資料型態
Prepare.Set_Final_Dict_Data(Labels, Data_Dict_Data, Classes, Label_Length) Prepare.Set_Final_Dict_Data(Labels, Data_Dict_Data, Classes, Label_Length)
Final_Dict_Data = Prepare.Get_Final_Data_Dict() Final_Dict_Data = Prepare.Get_Final_Data_Dict()
keys = list(Final_Dict_Data.keys()) keys = list(Final_Dict_Data.keys())
training_data = Merge.merge_all_image_data(Final_Dict_Data[keys[0]], Final_Dict_Data[keys[1]]) # 將訓練資料合併成一個list Training_Data = Merge.merge_all_image_data(Final_Dict_Data[keys[0]], Final_Dict_Data[keys[1]]) # 將訓練資料合併成一個list
for i in range(2, Label_Length): for i in range(2, Label_Length):
training_data = Merge.merge_all_image_data(training_data, Final_Dict_Data[keys[i]]) # 將訓練資料合併成一個list Training_Data = Merge.merge_all_image_data(Training_Data, Final_Dict_Data[keys[i]]) # 將訓練資料合併成一個list
training_label = Merge.merge_all_image_data(Final_Dict_Data[keys[Label_Length]], Final_Dict_Data[keys[Label_Length + 1]]) #將訓練資料的label合併成一個label的list Training_Label = Merge.merge_all_image_data(Final_Dict_Data[keys[Label_Length]], Final_Dict_Data[keys[Label_Length + 1]]) #將訓練資料的label合併成一個label的list
for i in range(Label_Length + 2, 2 * Label_Length): for i in range(Label_Length + 2, 2 * Label_Length):
training_label = Merge.merge_all_image_data(training_label, Final_Dict_Data[keys[i]]) # 將訓練資料合併成一個list Training_Label = Merge.merge_all_image_data(Training_Label, Final_Dict_Data[keys[i]]) # 將訓練資料合併成一個list
start = time.time() start = time.time()
trains_Data_Image = image_processing.Data_Augmentation_Image(training_data) # 讀檔 # trains_Data_Image = image_processing.Data_Augmentation_Image(training_data) # 讀檔
Training_Data, Training_Label = image_processing.image_data_processing(trains_Data_Image, training_label) # 將讀出來的檔做正規化。降label轉成numpy array 格式 # Training_Data, Training_Label = image_processing.image_data_processing(trains_Data_Image, training_label) # 將讀出來的檔做正規化。降label轉成numpy array 格式
# Training_Data = image_processing.normalization(Training_Data)
# training_data = image_processing.normalization(training_data)
# Balance_Data = list(zip(Training_Data, Training_Label))
# Training_Data, Training_Label = Balance_Process(Balance_Data, Total_Size_List)
# training_data = training_data.permute(0, 3, 1, 2) # training_data = training_data.permute(0, 3, 1, 2)
end = time.time() end = time.time()
print("\n\n\n讀取訓練資料(70000)執行時間:%f\n\n" % (end - start)) print("\n\n\n讀取訓練資料(70000)執行時間:%f\n\n" % (end - start))
experiment.processing_main(Training_Data, Training_Label, Run_Range) # 執行訓練方法 experiment.processing_main(Training_Data, Training_Label) # 執行訓練方法

View File

@@ -23,7 +23,7 @@ class merge:
return self.merge_data_main(merged_data, 0, Number_Of_Classes) return self.merge_data_main(merged_data, 0, Number_Of_Classes)
def merge_data_main(self, merge_data, merge_start_index, total_merge_number = 3): def merge_data_main(self, merge_data, merge_start_index, total_merge_number):
''' '''
將各類別資料合併在一起 將各類別資料合併在一起
## Parameter: ## Parameter:

View File

@@ -1,4 +1,5 @@
import random import random
from merge_class.merge import merge
def calculate_confusion_matrix(predict, result): def calculate_confusion_matrix(predict, result):
@@ -41,15 +42,26 @@ def shuffle_data(image, label, mode = 1):
return shuffle_image return shuffle_image
def Balance_Process(Data_Dict_Data, Labels): def Balance_Process(Datas, Size_List):
# Data_Dict_Data = shuffle_data(Data_Content, Labels, 2) # Data_Dict_Data = shuffle_data(Data_Content, Labels, 2)
Train_Size = 0 Train_Size, start = 0, 0
Image_List = []
Images, Labels = [], []
Merge = merge()
Train_Size = min(len(Data_Dict_Data[Labels[0]]), len(Data_Dict_Data[Labels[1]])) Train_Size = min(Size_List[0], Size_List[1])
for i in range(1, len(Labels) - 1): for i in range(2, len(Size_List), 1):
Train_Size = min(Train_Size, len(Data_Dict_Data[Labels[i + 1]])) Train_Size = min(Train_Size, Size_List[i])
for i in range(len(Labels)): for i in Size_List:
Data_Dict_Data[Labels[i]] = Data_Dict_Data[Labels[i]][0 : Train_Size] Image_List.append(Datas[start : Train_Size])
start = Train_Size
Train_Size += Train_Size
return Data_Dict_Data, Train_Size Image_List = Merge.merge_data_main(Image_List, 0, len(Image_List))
for Image in Image_List:
Images.append(Image[0])
Labels.append(Image[1])
return Images, Labels

File diff suppressed because one or more lines are too long