train_cam网络输出各层shape

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F

# import resnet50
from net import resnet50
import sys
# sys.path.append('/gly/ab/WSSS/WSSS/ReCAM+CCAM/')
from misc import torchutils
import misc.torchutils

from torchvision import transforms
from net import loss

class Net(nn.Module):

    def __init__(self, stride=16, n_classes=20):
        super(Net, self).__init__()
        if stride == 16:
            self.resnet50 = resnet50.resnet50(pretrained=True, strides=(2, 2, 2, 1))
            self.stage1 = nn.Sequential(self.resnet50.conv1, self.resnet50.bn1, self.resnet50.relu, self.resnet50.maxpool,self.resnet50.layer1)
        else:
            self.resnet50 = resnet50.resnet50(pretrained=True, strides=(2, 2, 1, 1), dilations=(1, 1, 2, 2))
            self.stage1 = nn.Sequential(self.resnet50.conv1, self.resnet50.bn1, self.resnet50.relu, self.resnet50.maxpool,self.resnet50.layer1)
        self.stage2 = nn.Sequential(self.resnet50.layer2)
        self.stage3 = nn.Sequential(self.resnet50.layer3)
        self.stage4 = nn.Sequential(self.resnet50.layer4)
        self.n_classes = n_classes
        self.classifier = nn.Conv2d(2048, n_classes, 1, bias=False)

        self.backbone = nn.ModuleList([self.stage1, self.stage2, self.stage3, self.stage4])
        self.newly_added = nn.ModuleList([self.classifier])


    def forward(self, x):

        x = self.stage1(x)
        x = self.stage2(x)

        x = self.stage3(x)
        x = self.stage4(x)

        x = torchutils.gap2d(x, keepdims=True)
        x = self.classifier(x)
        x = x.view(-1, self.n_classes)

        return x

    def train(self, mode=True):
        super(Net, self).train(mode)
        for p in self.resnet50.conv1.parameters():
            p.requires_grad = False
        for p in self.resnet50.bn1.parameters():
            p.requires_grad = False

    def trainable_parameters(self):

        return (list(self.backbone.parameters()), list(self.newly_added.parameters()))

class Net_CAM(Net):

    def __init__(self,stride=16,n_classes=20):
        super(Net_CAM, self).__init__(stride=stride,n_classes=n_classes)
        
    def forward(self, x):

        x = self.stage1(x)
        x = self.stage2(x)

        x = self.stage3(x)
        feature = self.stage4(x)

        x = torchutils.gap2d(feature, keepdims=True)
        x = self.classifier(x)
        x = x.view(-1, self.n_classes)

        cams = F.conv2d(feature, self.classifier.weight)
        cams = F.relu(cams)
        
        return x,cams,feature

class Net_CAM_Feature(Net):

    def __init__(self,stride=16,n_classes=20):
        super(Net_CAM_Feature, self).__init__(stride=stride,n_classes=n_classes)
        
    def forward(self, x):
        # x = 16, 3, 512, 512
        # for i in x:
        #     print(i.size()) 3, 512, 512
        x = self.stage1(x)
        # 16, 256, 128, 128
        x = self.stage2(x)
        # 16, 512, 64, 64
        x = self.stage3(x)
        # 16, 1024, 32, 32
        feature = self.stage4(x) # bs*2048*32*32
        # 16, 1024, 32, 32
        x = torchutils.gap2d(feature, keepdims=True)
        # 16, 2048, 1, 1
        x = self.classifier(x)
        # 16, 20, 1, 1
        x = x.view(-1, self.n_classes)
        # 16, 20

        cams = F.conv2d(feature, self.classifier.weight)
        cams = F.relu(cams)
        cams = cams/(F.adaptive_max_pool2d(cams, (1, 1)) + 1e-5)
        # cams.size()  # 16, 20, 32, 32
        cams_feature = cams.unsqueeze(2)*feature.unsqueeze(1) # bs*20*2048*32*32
        cams_feature = cams_feature.view(cams_feature.size(0),cams_feature.size(1),cams_feature.size(2),-1)
        cams_feature = torch.mean(cams_feature,-1)
        # cams_feature.size()  # 16, 20, 2048
        return x,cams_feature,cams


class CAM(Net):

    def __init__(self, stride=16,n_classes=20):
        super(CAM, self).__init__(stride=stride,n_classes=n_classes)

    def forward(self, x, separate=False):
        x = self.stage1(x)
        x = self.stage2(x)
        x = self.stage3(x)
        x = self.stage4(x)
        x = F.conv2d(x, self.classifier.weight)
        if separate:
            return x
        x = F.relu(x)
        x = x[0] + x[1].flip(-1)

        return x

    def forward1(self, x, weight, separate=False):
        x = self.stage1(x)
        x = self.stage2(x)
        x = self.stage3(x)
        x = self.stage4(x)
        x = F.conv2d(x, weight)
        
        if separate:
            return x
        x = F.relu(x)
        x = x[0] + x[1].flip(-1)

        return x

    def forward2(self, x, weight, separate=False):
        x = self.stage1(x)
        x = self.stage2(x)
        x = self.stage3(x)
        x = self.stage4(x)
        x = F.conv2d(x, weight*self.classifier.weight)
        
        if separate:
            return x
        x = F.relu(x)
        x = x[0] + x[1].flip(-1)
        return x


class Class_Predictor(nn.Module):
    def __init__(self, num_classes, representation_size):
        super(Class_Predictor, self).__init__()
        self.num_classes = num_classes
        self.classifier = nn.Conv2d(representation_size, num_classes, 1, bias=False)
        self.ac_head = Disentangler(self.num_classes)
        # self.from_scratch_layers = [self.ac_head]
        self.loss = loss
    def forward(self, x, cams_feature, label, inference=False):
        batch_size = x.shape[0]
        x = x.reshape(batch_size,self.num_classes,32,32) # bs*20*32*32 #torch.FloatTensor
        mask = label>0  # bs*20

        # 每张照片n*2048,n为类别中>0的数量
        contrast_feature_list = [x[i][mask[i]] for i in range(batch_size)] # bs*n*2048
        # n 里面的每一个都是
        # uploader = transforms.ToPILImage()
        # image = feature_list[0].cpu().clone()
        # image = uploader(image)
        # image.save("example.jpg")
        criterion = [self.loss.SimMaxLoss(metric='cos', alpha=0.25).cuda(), self.loss.SimMinLoss(metric='cos').cuda(),
                     self.loss.SimMaxLoss(metric='cos', alpha=0.25).cuda()]
        loss_CCAM = 0
        for feature in contrast_feature_list:
            # 出现空张量的原因:标签中有该类别,但是cam没有识别到
            # if feature.numel():
            #     print('.')  # 非空
            # else:
            #     print('!')  # 空
                # np.set_printoptions(threshold=sys.maxsize)
                # print(feature)
            n = feature.shape[0]
            ac_head = Disentangler(n)
            fg_feats, bg_feats, ccam = ac_head(feature.unsqueeze(0), inference=inference)  # 单个有效,循环无效
            loss1 = criterion[0](fg_feats)
            loss2 = criterion[1](bg_feats, fg_feats)
            loss3 = criterion[2](bg_feats)
            loss_CCAM += loss1
            loss_CCAM += loss2
            loss_CCAM += loss3


        refeature_list = [cams_feature[i][mask[i]] for i in range(batch_size)]
        prediction = [self.classifier(y.unsqueeze(-1).unsqueeze(-1)).squeeze(-1).squeeze(-1) for y in refeature_list]
        # prediction 16 * (n, 20)
        labels = [torch.nonzero(label[i]).squeeze(1) for i in range(label.shape[0])]
        # labels 16 * (20)

        loss = 0
        acc = 0
        num = 0
        for logit,label in zip(prediction, labels):
            if label.shape[0] == 0:
                continue
            loss_ce= F.cross_entropy(logit, label)
            loss += loss_ce
            acc += (logit.argmax(dim=1)==label.view(-1)).sum().float()
            num += label.size(0)
            l = (loss + loss_CCAM)/batch_size
            print(l.item())
            print(".")
        return l, acc/num

# x = torch.randn(16, 20, 32, 32)
# import random
#
# label = [[random.randint(0, 1) for j in range(20)] for i in range(16)]
# label = torch.tensor(label)
# # print(label)
# cams_feature = torch.randn(16, 20, 2048)
# recam_predictor = Class_Predictor(20, 2048).cuda()
# recam_predictor = torch.nn.DataParallel(recam_predictor)
# recam_predictor.train()
# loss_ce,acc = recam_predictor(x, cams_feature, label)
#
# print(loss_ce,acc)
``` import numpy as np import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout import matplotlib.pyplot as plt import cv2 tf.random.set_seed(42) # 定义路径 train_dir = r'C:\Users\29930\Desktop\结构参数图' validation_dir = r'C:\Users\29930\Desktop\测试集路径' # 需要实际路径 # 图像参数 img_width, img_height = 150, 150 batch_size = 32 # 数据生成器 train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, # 修改为实际路径 target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') validation_generator = test_datagen.flow_from_directory( validation_dir, # 修改为实际路径 target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') # 模型构建 model = Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(img_width, img_height, 3)), MaxPooling2D(2,2), Conv2D(64, (3,3), activation='relu'), MaxPooling2D(2,2), Flatten(), Dense(128, activation='relu'), Dropout(0.5), Dense(1, activation='sigmoid') ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 history = model.fit( train_generator, steps_per_epoch=len(train_generator), epochs=20, validation_data=validation_generator, validation_steps=len(validation_generator)) # Grad-CAM函数修正版 def generate_grad_cam(model, img_array, layer_name): # 创建梯度模型 grad_model = tf.keras.models.Model( inputs=[model.inputs], outputs=[model.get_layer(layer_name).output, model.output] ) # 计算梯度 with tf.GradientTape() as tape: conv_outputs, predictions = grad_model(img_array) loss = predictions[:, 0] # 获取梯度 grads = tape.gradient(loss, conv_outputs) pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) # 生成热力图 conv_outputs = conv_outputs[0] heatmap = tf.reduce_sum(conv_outputs * pooled_grads, axis=-1) # 归一化处理 heatmap = np.maximum(heatmap, 0) heatmap /= np.max(heatmap) return heatmap # 可视化部分 X, y = next(validation_generator) sample_image = X[0] img_array = np.expand_dims(sample_image, axis=0) # 获取最后一个卷积(根据模型结构调整索引) last_conv_layer = model.layers[2] # 第二个Conv2D heatmap = generate_grad_cam(model, img_array, last_conv_layer.name) # 调整热力图尺寸 heatmap = cv2.resize(heatmap, (img_width, img_height)) heatmap = np.uint8(255 * heatmap) # 颜色映射 jet = plt.colormaps.get_cmap('jet') jet_colors = jet(np.arange(256))[:, :3] jet_heatmap = jet_colors[heatmap] # 叠加显示 superimposed_img = jet_heatmap * 0.4 + sample_image superimposed_img = np.clip(superimposed_img, 0, 1) # 绘制结果 plt.figure(figsize=(12, 4)) plt.subplot(131) plt.imshow(sample_image) plt.title('原始图像') plt.axis('off') plt.subplot(132) plt.imshow(heatmap, cmap='jet') plt.title('热力图') plt.axis('off') plt.subplot(133) plt.imshow(superimposed_img) plt.title('叠加效果') plt.axis('off') plt.tight_layout() plt.show()```完善代码使在构建模型后,调用模型初始化各形状,随后指定输入形状并通过实际数据前向传播来触发形状推断.输出完整代码
03-24
``` import numpy as np import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout import matplotlib.pyplot as plt import cv2 tf.random.set_seed(42) # 定义路径 train_dir = r'C:\Users\29930\Desktop\结构参数图' validation_dir = r'C:\Users\29930\Desktop\测试集路径' # 需要实际路径 # 图像参数 img_width, img_height = 150, 150 batch_size = 32 # 数据生成器 train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') validation_generator = test_datagen.flow_from_directory( validation_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') # 模型构建 model = Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(img_width, img_height, 3)), MaxPooling2D(2,2), Conv2D(64, (3,3), activation='relu'), MaxPooling2D(2,2), Flatten(), Dense(128, activation='relu'), Dropout(0.5), Dense(1, activation='sigmoid') ]) # 显式初始化各形状 model.build(input_shape=(None, img_width, img_height, 3)) # None表示可变batch_size # 使用实际数据触发前向传播(可选保障措施) sample_batch = next(train_generator)[0] _ = model.predict(sample_batch) # 触发形状推断 model.summary() # 打印网络结构验证 model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 history = model.fit( train_generator, steps_per_epoch=len(train_generator), epochs=20, validation_data=validation_generator, validation_steps=len(validation_generator)) # Grad-CAM函数修正版 def generate_grad_cam(model, img_array, layer_name): grad_model = tf.keras.models.Model( inputs=[model.inputs], outputs=[model.get_layer(layer_name).output, model.output] ) with tf.GradientTape() as tape: conv_outputs, predictions = grad_model(img_array) loss = predictions[:, 0] grads = tape.gradient(loss, conv_outputs) pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) conv_outputs = conv_outputs[0] heatmap = tf.reduce_sum(conv_outputs * pooled_grads, axis=-1) heatmap = np.maximum(heatmap, 0) heatmap /= np.max(heatmap) return heatmap # 可视化部分 X, y = next(validation_generator) sample_image = X[0] img_array = np.expand_dims(sample_image, axis=0) last_conv_layer = model.layers[2] # 第二个Conv2D heatmap = generate_grad_cam(model, img_array, last_conv_layer.name) heatmap = cv2.resize(heatmap, (img_width, img_height)) heatmap = np.uint8(255 * heatmap) jet = plt.colormaps.get_cmap('jet') jet_colors = jet(np.arange(256))[:, :3] jet_heatmap = jet_colors[heatmap] superimposed_img = jet_heatmap * 0.4 + sample_image superimposed_img = np.clip(superimposed_img, 0, 1) plt.figure(figsize=(12, 4)) plt.subplot(131) plt.imshow(sample_image) plt.title('原始图像') plt.axis('off') plt.subplot(132) plt.imshow(heatmap, cmap='jet') plt.title('热力图') plt.axis('off') plt.subplot(133) plt.imshow(superimposed_img) plt.title('叠加效果') plt.axis('off') plt.tight_layout() plt.show()```raceback (most recent call last): File “D:/建模/retu.py”, line 102, in <module> heatmap = generate_grad_cam(model, img_array, last_conv_layer.name) File “D:/建模/retu.py”, line 68, in generate_grad_cam outputs=[model.get_layer(layer_name).output, model.output]
03-25
def build_data_dict(paths, labels): data = [] skipped = 0 for path, label in zip(paths, labels): try: base_name = os.path.basename(path) file_id = base_name.replace(".nii.gz", "") mask_path = path.replace(".nii.gz", "mask.nii.gz") if not os.path.exists(mask_path): print(f"❌ 缺失 mask 文件: {mask_path}") skipped += 1 continue img_nii = nib.load(path) mask_nii = nib.load(mask_path) if np.all(mask_nii.get_fdata() == 0): print(f"⚠️ 掩膜全零,跳过: {mask_path}") skipped += 1 continue data.append({ "image": path, "mask": mask_path, "label": label, "id": file_id, "original_affine": np.array(img_nii.affine)[:4, :4].astype(np.float32), "original_shape": img_nii.shape, "mask_original_affine": np.array(mask_nii.affine)[:4, :4].astype(np.float32) }) except Exception as e: print(f"❌ 构建失败: {path},原因: {e}") skipped += 1 print(f"✅ 构建完成,有效样本: {len(data)},跳过: {skipped}") return data class SyncAffined(MapTransform): def __init__(self, keys, atol=1e-2, logger=None): super().__init__(keys) self.orientation = Orientationd(keys=keys, axcodes="RAS") self.resample = ResampleToMatchd(keys=["mask"], key_dst="image", mode="nearest") self.atol = atol self.logger = logger def __call__(self, data): try: data = self.orientation(data) a1 = data["image_meta_dict"]["affine"] a2 = data["mask_meta_dict"]["affine"] if isinstance(a1, torch.Tensor): a1 = a1.numpy() if isinstance(a2, torch.Tensor): a2 = a2.numpy() if not np.allclose(a1, a2, atol=self.atol): data = self.resample(data) return data except Exception as e: if self.logger: self.logger.error(f"Error during SyncAffined processing: {e}") raise def get_transforms(): deterministic_transforms = Compose([ LoadImaged(keys=["image", "mask"], image_only=False, reader="ITKReader"), EnsureChannelFirstd(keys=["image", "mask"]), SyncAffined(keys=["image", "mask"], atol=1e-2), Spacingd(keys=["image", "mask"], pixdim=(1.0, 1.0, 1.0), mode=("bilinear", "nearest")), CropForegroundd(keys=["image", "mask"], source_key="mask", margin=10, allow_smaller=True), ResizeWithPadOrCropd(keys=["image", "mask"], spatial_size=(64, 64, 64)), ScaleIntensityRanged(keys=["image"], a_min=20, a_max=80, b_min=0.0, b_max=1.0, clip=True), ToTensord(keys=["image", "mask"]) ]) augmentation_transforms = Compose([ RandFlipd(keys=["image", "mask"], prob=0.2, spatial_axis=[0, 1, 2]), RandAffined( keys=["image", "mask"], prob=0.3, rotate_range=(-0.2, 0.2), scale_range=(0.8, 1.2), shear_range=(-0.1, 0.1, -0.1, 0.1, -0.1, 0.1), translate_range=(5, 5, 5), mode=("bilinear", "nearest"), padding_mode="border", spatial_size=(64, 64, 64) ), Lambdad(keys=["label"], func=lambda x: torch.tensor(x, dtype=torch.long).squeeze(0)) ]) return deterministic_transforms, augmentation_transforms deterministic_transforms, augmentation_transforms = get_transforms() data_dir = "D:/monaisj/train" class_dirs = sorted([d for d in os.listdir(data_dir) if os.path.isdir(os.path.join(data_dir, d))]) image_paths, labels = [], [] for class_name in class_dirs: class_path = os.path.join(data_dir, class_name) nii_files = glob.glob(os.path.join(class_path, "*.nii.gz")) for nii_file in nii_files: if 'mask' not in nii_file: image_paths.append(nii_file) labels.append(int(class_name)) # 分划分 sss = StratifiedShuffleSplit(n_splits=1, test_size=0.3, random_state=42) train_indices, val_indices = next(sss.split(image_paths, labels)) train_paths = [image_paths[i] for i in train_indices] val_paths = [image_paths[i] for i in val_indices] train_labels = [labels[i] for i in train_indices] val_labels = [labels[i] for i in val_indices] train_files = build_data_dict(train_paths, train_labels) val_files = build_data_dict(val_paths, val_labels) # -------------------- 数据集加载 -------------------- train_ds = CacheDataset(data=train_files, transform=deterministic_transforms, cache_rate=0.8) train_ds = Dataset(train_ds, transform=augmentation_transforms) train_loader = DataLoader(train_ds, batch_size=8, shuffle=True, num_workers=0) val_ds = CacheDataset(data=val_files, transform=deterministic_transforms, cache_rate=1.0) val_loader = DataLoader(val_ds, batch_size=8, shuffle=False, num_workers=0) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = monai.networks.nets.resnet18(pretrained=False, spatial_dims=3, n_input_channels=1, num_classes=2).to(device) weights_path = "D:/MedicalNet/pretrain/resnet_50_epoch_110_batch_0.pth" if os.path.exists(weights_path): state_dict = torch.load(weights_path, map_location=device, weights_only=True) new_state_dict = {} for k, v in state_dict.items(): name = k.replace("model.", "").replace("module.", "") new_state_dict[name] = v model.load_state_dict(new_state_dict, strict=False) print(f"加载权重成功: {weights_path}") else: print(f"权重文件不存在: {weights_path}") def init_weights(m): if isinstance(m, torch.nn.Linear): torch.nn.init.kaiming_normal_(m.weight) torch.nn.init.constant_(m.bias, 0.0) model.fc.apply(init_weights) class_weights = torch.tensor([ len(train_labels)/(2.0 * np.bincount(train_labels)[0]), len(train_labels)/(2.0 * np.bincount(train_labels)[1]) ], dtype=torch.float32).to(device) loss_fn = CrossEntropyLoss(weight=class_weights) def compute_metrics(labels, preds, probs): cm = confusion_matrix(labels, preds) tn, fp, fn, tp = cm.ravel() sensitivity = tp / (tp + fn) if (tp + fn) > 0 else 0 specificity = tn / (tn + fp) if (tn + fp) > 0 else 0 accuracy = (tp + tn) / (tp + tn + fp + fn) auc = roc_auc_score(labels, probs) return sensitivity, specificity, accuracy, auc def freeze_layers(model, freeze_patterns=None, unfreeze_patterns=None): """按模式冻结/解冻""" for name, param in model.named_parameters(): param.requires_grad = False if unfreeze_patterns: for pattern in unfreeze_patterns: if pattern in name: param.requires_grad = True break if "fc" in name: param.requires_grad = True def train_model(model, stage=1, epochs=50, init_lr=1e-3, eta_min=1e-5, data_dir="D:/monaisj/"): # 阶段配置 if stage == 1: # 阶段1:仅训练分类头 freeze_layers(model, unfreeze_patterns=["fc"]) print("🔒 阶段1:冻结骨干,仅训练分类头") lr = init_lr # 较高学习率 elif stage == 2: # 阶段2:解冻高 freeze_layers(model, unfreeze_patterns=["layer3","layer4", "fc"]) print("🔓 阶段2:解冻高(layer3/layer4)") lr = init_lr * 0.1 # 降低学习率 elif stage == 3: # 阶段3:全解冻 for param in model.parameters(): param.requires_grad = True print("🔥 阶段3:解冻全网络") lr = init_lr * 0.01 # 更低学习率 else: raise ValueError(f"未知阶段: {stage}") # 创建优化器(仅优化需要梯度的参数) params_to_optimize = [p for p in model.parameters() if p.requires_grad] optimizer = AdamW(params_to_optimize, lr=lr, weight_decay=1e-4) scheduler = CosineAnnealingLR(optimizer, T_max=epochs, eta_min=eta_min) best_val_auc = 0.0 # 初始化历史记录 history = { 'train_loss': [], 'train_acc': [], 'train_auc': [], 'train_sensitivity': [], 'train_specificity': [], 'val_loss': [], 'val_acc': [], 'val_auc': [], 'val_sensitivity': [], 'val_specificity': [], 'train_true_labels': None, 'train_probs': [], 'val_true_labels': None, 'val_probs': [] } for epoch in range(epochs): # ================== 训练阶段 ================== model.train() epoch_loss = 0.0 train_preds, train_labels, train_probs = [], [], [] scaler = GradScaler() for batch in train_loader: images = batch["image"].to(device, non_blocking=True) labels = batch["label"].long().to(device) optimizer.zero_grad(set_to_none=True) outputs = model(images) loss = loss_fn(outputs, labels) scaler.scale(loss).backward() scaler.unscale_(optimizer) torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) scaler.step(optimizer) scaler.update() epoch_loss += loss.item() * images.size(0) # 收集训练数据 preds = torch.argmax(outputs, dim=1).cpu().numpy() probs = torch.softmax(outputs, dim=1)[:, 1].detach().cpu().numpy() train_preds.extend(preds) train_labels.extend(labels.cpu().numpy()) train_probs.extend(probs) # 记录训练集标签(仅第一次) if epoch == 0: history['train_true_labels'] = train_labels history['train_probs'].append(train_probs) # 计算训练指标 train_loss = epoch_loss / len(train_loader) train_acc = accuracy_score(train_labels, train_preds) train_sensitivity, train_specificity, _, train_auc = compute_metrics( train_labels, train_preds, train_probs ) # ================== 验证阶段 ================== model.eval() val_loss = 0.0 val_preds, val_labels, val_probs = [], [], [] with torch.no_grad(): for batch in val_loader: images = batch["image"].to(device, non_blocking=True) labels = batch["label"].long().to(device) outputs = model(images) loss = loss_fn(outputs, labels) val_loss += loss.item() * images.size(0) # 收集验证数据 probs = torch.softmax(outputs, dim=1)[:, 1].cpu().numpy() preds = torch.argmax(outputs, dim=1).cpu().numpy() val_preds.extend(preds) val_labels.extend(labels.cpu().numpy()) val_probs.extend(probs) # 记录验证集标签(仅第一次) if epoch == 0: history['val_true_labels'] = val_labels history['val_probs'].append(val_probs) # 计算验证指标 val_loss = val_loss / len(val_loader) val_acc = accuracy_score(val_labels, val_preds) val_sensitivity, val_specificity, _, val_auc = compute_metrics( val_labels, val_preds, val_probs ) # ================== 更新学习率和保存模型 ================== scheduler.step() # 保存每个epoch的模型 save_path = os.path.join(data_dir, f"best_model_stage{stage}_epoch{epoch+1}.pth") torch.save(model.state_dict(), save_path) print(f"✅ 保存模型到 {save_path}") # ================== 记录历史数据 ================== history['train_loss'].append(train_loss) history['train_acc'].append(train_acc) history['train_auc'].append(train_auc) history['train_sensitivity'].append(train_sensitivity) history['train_specificity'].append(train_specificity) history['val_loss'].append(val_loss) history['val_acc'].append(val_acc) history['val_auc'].append(val_auc) history['val_sensitivity'].append(val_sensitivity) history['val_specificity'].append(val_specificity) # 打印进度 current_lr = optimizer.param_groups[0]['lr'] print(f"Epoch {epoch+1}/{epochs} [Stage {stage}]") print(f"Train Loss: {train_loss:.4f} | Acc: {train_acc:.4f} | AUC: {train_auc:.4f}") print(f"Val Loss: {val_loss:.4f} | Acc: {val_acc:.4f} | AUC: {val_auc:.4f}") print(f"当前学习率: {current_lr:.2e}") print("-"*50) return history SEED =123456 random.seed(SEED) np.random.seed(SEED) # 阶段一:冻结骨干,仅训练分类头 print("\n开始阶段一训练(冻结骨干网络)") stage1_history = train_model(model, stage=1, epochs=40, init_lr=1e-2, eta_min=1e-2) print("\n开始阶段二训练(解冻高)") # 加载阶段一最佳模型 # best_stage1_epoch = np.argmax([auc for auc in stage1_history['val_auc']]) + 1 model.load_state_dict(torch.load(f"D:/monaisj/best_model_stage1_epoch19.pth",map_location=device, weights_only=True)) stage2_history = train_model(model, stage=2, epochs=30, init_lr=1e-3, eta_min=1e-4) model.load_state_dict(torch.load(f"D:/monaisj/best_model_stage2_epoch20.pth",map_location=device, weights_only=True)) stage3_history = train_model(model, stage=3, epochs=30, init_lr=1e-4, eta_min=1e-5) 加载最佳模型,写一段代码帮我生成grad—cam热图,a图为预处理后根据掩膜裁剪后的输入模型图,b图是掩膜图,c图是cam图,d图是输入模型图和cam的叠加图
06-23
这里是根据类别来确定prompt的第二阶段训练代码,这时候是直接确定文本编码,但是我现在要改为了文本特征是根据图片生成的需要如何修改:text_features = model(img, target, get_image = True),这样生成,也就是不是用类别获得,而是图像生成的动态prompt # train batch = cfg.SOLVER.STAGE2.IMS_PER_BATCH i_ter = num_classes // batch left = num_classes - batch * (num_classes // batch) if left != 0: i_ter = i_ter + 1 text_features = [] with torch.no_grad(): for i in range(i_ter): if i + 1 != i_ter: l_list = torch.arange(i * batch, (i + 1) * batch) else: l_list = torch.arange(i * batch, num_classes) with amp.autocast(enabled=True): text_feature = model(label=l_list, get_text=True) text_features.append(text_feature.cpu()) text_features = torch.cat(text_features, 0).cuda() for epoch in range(1, epochs + 1): start_time = time.time() loss_meter.reset() acc_meter.reset() evaluator.reset() scheduler.step() model.train() for n_iter, (img, vid, target_cam, target_view) in enumerate(train_loader_stage2): optimizer.zero_grad() optimizer_center.zero_grad() img = img.to(device) target = vid.to(device) if cfg.MODEL.SIE_CAMERA: target_cam = target_cam.to(device) else: target_cam = None if cfg.MODEL.SIE_VIEW: target_view = target_view.to(device) else: target_view = None with amp.autocast(enabled=True): score, feat, image_features = model(x=img, label=target, cam_label=target_cam, view_label=target_view) logits = image_features @ text_features.t() loss = loss_fn(score, feat, target, target_cam, logits) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() if 'center' in cfg.MODEL.METRIC_LOSS_TYPE: for param in center_criterion.parameters(): param.grad.data *= (1. / cfg.SOLVER.CENTER_LOSS_WEIGHT) scaler.step(optimizer_center) scaler.update() acc = (logits.max(1)[1] == target).float().mean() loss_meter.update(loss.item(), img.shape[0]) acc_meter.update(acc, 1) torch.cuda.synchronize() if (n_iter + 1) % log_period == 0: logger.info("Epoch[{}] Iteration[{}/{}] Loss: {:.3f}, Acc: {:.3f}, Base Lr: {:.2e}" .format(epoch, (n_iter + 1), len(train_loader_stage2), loss_meter.avg, acc_meter.avg, scheduler.get_lr()[0]))
09-26
``` # 图像尺寸可以根据实际情况调整 img_width, img_height = 150, 150 batch_size = 32 train_datagen = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1. / 255) train_generator = train_datagen.flow_from_directory( train_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') validation_generator = test_datagen.flow_from_directory( test_dir, # 验证集路径 target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') model = Sequential() model.add(Conv2D(32, (3, 3), input_shape=(img_width, img_height, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) history = model.fit( train_generator, steps_per_epoch=len(train_generator), epochs=20, validation_data=validation_generator, validation_steps=len(validation_generator)) # 定义生成Grad-CAM热图的函数 def generate_grad_cam(model, img_array, layer_name): grad_model = tf.keras.models.Model( [model.inputs], [model.get_layer(layer_name).output, model.output] ) with tf.GradientTape() as tape: conv_output, predictions = grad_model(img_array) loss = predictions[:, 0] # 二分类任务取唯一输出值 grads = tape.gradient(loss, conv_output) pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) conv_output = conv_output[0] heatmap = tf.reduce_sum(conv_output * pooled_grads, axis=-1) heatmap = np.maximum(heatmap, 0) heatmap /= np.max(heatmap) # 归一化 return heatmap X, y = next(validation_generator) sample_image = X[0] img_array = np.expand_dims(sample_image, axis=0) # 扩展为(1, 150, 150, 3) last_conv_layer = model.layers[2] last_conv_layer_name = last_conv_layer.name heatmap = generate_grad_cam(model, img_array, last_conv_layer_name) heatmap = cv2.resize(heatmap, (img_width, img_height)) heatmap = np.uint8(255 * heatmap) jet = plt.colormaps.get_cmap('jet') jet_colors = jet(np.arange(256))[:, :3] jet_heatmap = jet_colors[heatmap] superimposed_img = jet_heatmap * 0.4 + sample_image superimposed_img = np.clip(superimposed_img, 0, 1) plt.figure(figsize=(12, 4)) plt.subplot(131) plt.imshow(sample_image) plt.title('原始图像') plt.axis('off') plt.subplot(132) plt.imshow(heatmap, cmap='jet') plt.title('热力图') plt.axis('off') plt.subplot(133) plt.imshow(superimposed_img) plt.title('叠加效果') plt.axis('off') plt.tight_layout() plt.show()```Traceback (most recent call last): File "D:\建模\cnn3.py", line 108, in <module> heatmap = generate_grad_cam(model, img_array, last_conv_layer_name) File "D:\建模\cnn3.py", line 81, in generate_grad_cam [model.inputs], [model.get_layer(layer_name).output, model.output] File "C:\Users\29930\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\ops\operation.py", line 280, in output return self._get_node_attribute_at_index(0, "output_tensors", "output") File "C:\Users\29930\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\ops\operation.py", line 299, in _get_node_attribute_at_index raise AttributeError( AttributeError: The layer sequential has never been called and thus has no defined output.. Did you mean: 'outputs'?解决这个问题输出可用完整代码
03-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Env1sage

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值