基于Alexnet卷积网络处理乳腺肿瘤数据集

1.使用PyTorch框架来进行深度学习任务 

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.utils.data import DataLoader, random_split

1.1 PyTorch相关库导入

 torch: PyTorch的核心库,提供了张量操作、自动微分和GPU加速计算。

 torch.nn: 包含构建神经网络的模块化类,如层、损失函数等。

 torch.optim: 包含用于优化神经网络的优化器,如SGD、Adam等。

 torch.optim.lr_scheduler: 提供学习率调度器,用于在训练过程中调整学习率。

 torch.utils.data.DataLoader: 负责数据加载和批量处理。

 torch.utils.data.random_split: 用于将数据集随机分割成训练集和验证集

 1.2 图像处理相关库导入

import torchvision
from torchvision import datasets, models, transforms
import torchvision.transforms as transforms
from torchvision.transforms import RandomHorizontalFlip, RandomRotation, ColorJitter
from torchvision.datasets import ImageFolder

torchvision: PyTorch的视觉库,包含了常用的数据集、预训练模型和数据增强方法。

datasets: 提供了常见的数据集加载器,如CIFAR、ImageNet等。

models: 包含预训练的模型,如ResNet、VGG等。

transforms: 用于图像预处理和数据增强的函数,如缩放、裁剪、归一化等。

ImageFolder: 用于从文件夹中加载图像数据集,要求文件夹结构按照类别进行组织。

  RandomHorizontalFlip, RandomRotation, ColorJitter: 这些是图像增强方法,用于随机水平翻转、旋转或调整图像的亮度、对比度等

1.3  其他辅助库导入

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing import image
from PIL import Image
import random
import shutil
import seaborn as sns
import warnings

numpy: 用于数值计算的库,特别是数组和矩阵运算。

pandas: 数据分析库,主要用于处理表格数据。

matplotlib.pyplot: 绘图库,主要用于绘制图像和可视化数据。

tensorflow.keras.preprocessing.image: 提供图像处理工具(这是TensorFlow中的部分功能,虽然PyTorch也有类似的工具)。

PIL.Image: 用于打开、操作和保存图像文件的Python库。

random: 用于生成随机数,打乱数据等。

shutil: 文件操作库,如复制、移动和删除文件。

seaborn: 统计数据可视化库,基于Matplotlib。

warnings: 用于控制Python中的警告显示。

 1.4  确认环境配置

print("Imported Completely!")

确认所有库都成功导入。此输出可以帮助用户确认环境配置没有问题

2.使用Matplotlib库可视化从数据集中加载的乳腺超声图像

         从一个文件夹中读取图像,并以网格形式展示这些图像,同时对每个图像进行适当的标注。

        从一个乳腺超声图像数据集中的不同分类目录(如benignmalignantnormal)中加载图像和掩码图像,并在一个3x4的网格中展示它们。每个分类显示一行,图像和对应的掩码图像交替显示。

 2.1 标签和数据目录设置

labels = ['benign', 'malignant', 'normal']
data_dir = '/kaggle/input/breast-ultrasound-images-dataset/Dataset_BUSI_with_GT'

labels:这是一个列表,包含三个标签:benign(良性)、malignant(恶性)和normal(正常)。这些标签代表乳腺超声图像的三种分类。

data_dir:数据集的根目录。这个路径指向Kaggle上的一个乳腺超声图像数据集

2.2 创建网格来显示图像 

fig, axs = plt.subplots(3, 4, figsize=(18, 12))

 fig, axs:使用Matplotlib创建一个网格来显示图像。axs 是一个3行4列的子图数组,每个子图将显示一张图像或掩码图像。

figsize=(18, 12):设置图的大小,单位为英寸。这里的大小为18x12英寸,这使得显示的图像有足够的空间和清晰度。

2.3 设置图像的期望大小

desired_width = 300
desired_height = 300

desired_width 和 desired_height:指定每个显示图像的宽度和高度。这里设置为300x300像素。

 2.4 循环遍历标签目录并加载图像

for i, label in enumerate(labels):
    label_dir = os.path.join(data_dir, label)
    
    image_files = [file for file in os.listdir(label_dir) if file.endswith('.png')]
    
    image_files = sorted(image_files)
    
    for j in range(4):
        if j < len(image_files):
            image_path = os.path.join(label_dir, image_files[j])
            image = Image.open(image_path)
            image = image.resize((desired_width, desired_height), Image.ANTIALIAS)

for i, label in enumerate(labels):遍历标签,每个标签对应一个图像类别(如benign、malignant、normal)。

label_dir = os.path.join(data_dir, label):生成每个标签对应的目录路径(如/kaggle/input/.../benign)。

image_files = [file for file in os.listdir(label_dir) if file.endswith('.png')]:从目录中列出所有PNG格式的图像文件。

image_files = sorted(image_files):对图像文件进行排序,确保以一定的顺序加载图像。

for j in range(4):内层循环遍历前四张图像,用于显示在当前标签对应的行上。

if j < len(image_files):确保索引不超出图像文件的数量。

image_path = os.path.join(label_dir, image_files[j]):构建图像的完整路径。

image = Image.open(image_path):使用PIL(Python Imaging Library)打开图像文件。

image = image.resize((desired_width, desired_height), Image.ANTIALIAS):将图像调整为指定的大小(300x300像素),Image.ANTIALIAS用于平滑缩放。

 2.5 为图像设置标签并显示

            if j % 2 == 0:
                image_label = f'{label} - Image {j // 2 + 1}'
            else:
                image_label = f'{label} - Image {j // 2 + 1} Mask'
            
            axs[i, j].imshow(image)
            axs[i, j].set_title(image_label)
            axs[i, j].axis('off')

if j % 2 == 0:为了区分图像和对应的掩码图像,该逻辑用于为偶数索引的图像命名为“Image”,为奇数索引的图像命名为“Mask”。

image_label = f'{label} - Image {j // 2 + 1}':偶数索引的图像被命名为benign - Image 1等。

image_label = f'{label} - Image {j // 2 + 1} Mask':奇数索引的图像被命名为benign - Image 1 Mask等。

axs[i, j].imshow(image):将图像显示在对应的子图中。

axs[i, j].set_title(image_label):为每个子图设置标题。

axs[i, j].axis('off'):隐藏子图的坐标轴,使得图像看起来更干净整洁

 3. 清理工作

         通过遍历该目录中的所有项目,并根据项目类型(文件或文件夹)进行相应的删除操作。这在Kaggle竞赛中可能用于清理临时生成的文件或之前的输出结果,以便在新一轮的计算中有一个干净的工作环境

3.1 定义工作目录 

working_dir = '/kaggle/working'

 3.2 遍历工作目录中的所有项目

for item in os.listdir(working_dir):

os.listdir(working_dir):列出working_dir目录下的所有项目,返回一个列表,包含目录中的文件和子目录的名称。

for item in os.listdir(working_dir):遍历列表中的每个项目。item代表当前遍历的项目的名称(不是完整路径

 3.3 构建每个项目的完整路径

    item_path = os.path.join(working_dir, item)

os.path.join(working_dir, item):使用os.path.join函数将目录路径和项目名称组合成完整的路径。item_path是当前项目的完整路径(例如,文件或文件夹的绝对路径)

 3.4 检查并删除文件或文件夹

    if os.path.isfile(item_path):
        os.remove(item_path)
    elif os.path.isdir(item_path):
        shutil.rmtree(item_path)

 os.path.isfile(item_path):检查item_path是否是一个文件。

 如果是文件,使用os.remove(item_path)删除该文件。

 os.path.isdir(item_path):检查item_path是否是一个目录(文件夹)。

如果是目录,使用shutil.rmtree(item_path)递归地删除该目录及其所有子目录和文件。

这两个检查确保了代码能够正确地处理文件和目录。shutil.rmtree()是删除目录及其内容的安全方法,即使目录中包含子目录或文件,也能一并删除

 3.5 输出清理完成消息

print("working output path cleared!")

4. 数据增强和预处理

        利用了PyTorch的torchvision.transforms模块,通过一系列变换对训练、验证和测试集中的图像进行处理。具体包括为少数类(malignantnormal)定义特殊的数据增强,并为所有图像(无论是训练、验证还是测试)执行标准的预处理步骤。 

4.1  定义类别和少数类

class_names = ['malignant', 'normal','benign']
minority_classes = ['malignant', 'normal']

class_names:这是一个包含所有类别名称的列表,表示图像分类任务中的三种可能分类:malignant(恶性)、normal(正常)、benign(良性)。

minority_classes:这是少数类别的列表,这里指定了 malignant 和 normal 是少数类。在数据增强时,这些少数类可能会受到更多的处理,以平衡数据集。

4.2 为少数类定义数据增强 

minority_class_transforms = transforms.Compose([
    RandomHorizontalFlip(p=0.9),
    RandomRotation(15, expand=False, center=None),
    ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
])

ransforms.Compose():用于将多个变换组合在一起,按照定义的顺序逐个应用。

RandomHorizontalFlip(p=0.9):以90%的概率随机水平翻转图像。翻转是常见的数据增强技术,用于增加数据的多样性。

RandomRotation(15, expand=False, center=None):随机旋转图像,角度在[-15°, 15°]之间。expand=False 表示旋转后不调整图像尺寸。

ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1):随机改变图像的亮度、对比度、饱和度和色调,范围分别为 ±20%、±10%等。这种变换可以模拟不同光照条件下的图像,以增强模型的泛化能力

 4.3 定义数据预处理和增强流程

data_transforms = {
    'train': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.RandomApply([minority_class_transforms], p=0.5) if any(cls in minority_classes for cls in class_names) else transforms.RandomApply([], p=0.0),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'validation': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'test': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

这个字典data_transforms定义了用于不同数据集(trainvalidationtest)的图像变换流程。它们之间有一些共同的步骤,也有一些特殊的增强处理

3.1 训练集变换(train) 

transforms.Resize(256):首先将图像缩放到256像素的边长,以便后续操作更一致。

transforms.CenterCrop(224):对缩放后的图像进行中心裁剪,得到224x224像素的图像,这是常见的图像尺寸(例如ImageNet数据集中的图像大小)。

transforms.RandomApply([minority_class_transforms], p=0.5):随机应用少数类的增强操作(minority_class_transforms),50%的概率会对图像进行增强。该行代码中的if any(cls in minority_classes for cls in class_names)用于判断是否要对少数类进行特殊处理,但由于class_names包含少数类,所以这个判断总是为真,增强的应用概率为0.5。

transforms.ToTensor():将图像转换为PyTorch张量,同时将图像像素值从[0, 255]归一化到[0, 1]范围。

transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]):对图像进行标准化处理。这里使用了在ImageNet数据集上计算的平均值和标准差。这是常见的操作,可以让图像数据适应预训练模型(如ResNet等)的输入。

3.2 验证集变换(validation)和测试集变换(test

transforms.Resize(256):同样将图像缩放到256像素的边长。

transforms.CenterCrop(224):进行中心裁剪,裁剪成224x224像素的图像。

transforms.ToTensor():将图像转换为张量。

transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])同样进行标准化处理。

验证集和测试集通常不会进行数据增强,而是保持输入图像的稳定性,以保证评估的公平性和一致性。

 4.读取图像

这段代码首先从指定的数据集目录中读取图像,并排除掩码文件,然后将图像划分为训练集、验证集和测试集,并复制到对应的目标目录中。

它通过 train_test_split 进行数据划分,并使用 stratify 参数保证各个类别的比例在每个数据集中保持一致。

最终,每个类别的图像被有序地组织到不同的数据集目录下,为后续的模型训练和评估做好准备。

4.1 定义数据集目录路径

data_dir = '/kaggle/input/breast-ultrasound-images-dataset/Dataset_BUSI_with_GT'

4.2. 初始化文件路径和标签列表

file_paths = []
labels = []

file_paths:用于存储每个图像的完整路径。

labels:用于存储每个图像对应的类别标签

 4.3. 遍历数据集目录,收集图像文件路径和标签

for label in os.listdir(data_dir):
    label_dir = os.path.join(data_dir, label)
    if os.path.isdir(label_dir):
        for image_file in os.listdir(label_dir):
            if image_file.endswith('.png') and not (image_file.endswith('_mask.png') or 
                                                     image_file.endswith('_mask_1.png') or
                                                     image_file.endswith('_mask_2.png')):
                image_path = os.path.join(label_dir, image_file)
                labels.append(label)
                file_paths.append(image_path)

 这段代码首先遍历 data_dir 目录下的所有文件和文件夹:

os.listdir(data_dir):列出 data_dir 目录中的所有文件和文件夹。

对于每个文件夹(即 label),首先检查它是否是一个目录(os.path.isdir(label_dir))。

然后,遍历该目录下的所有文件(image_file),检查文件是否以 .png 结尾,并排除包含 _mask.png、_mask_1.png 或 _mask_2.png 后缀的文件。这是为了过滤掉与原始图像对应的掩码文件。

对于符合条件的图像文件,记录它的完整路径(image_path),并将其对应的标签(文件夹名)存入 labels 列表

 4.4. 创建DataFrame并划分数据集

data = pd.DataFrame({'Image_Path': file_paths, 'Label': labels})

train_data, test_data = train_test_split(data, test_size=0.15, random_state=42, stratify=data['Label'])
train_data, val_data = train_test_split(train_data, test_size=0.15, random_state=42, stratify=train_data['Label'])

 data:使用 Pandas 将图像路径和标签创建成一个 DataFrame,包含两列:Image_Path(图像文件路径)和 Label(标签)。

train_test_split:将数据集划分为训练集、验证集和测试集:

第一步:将数据集 data 按 85% 和 15% 的比例划分为训练集(train_data)和测试集(test_data),其中 stratify=data['Label'] 确保每个标签类别的比例在训练集和测试集中保持一致。

第二步:将训练集(train_data)再次划分为 85% 和 15%,生成训练集(train_data)和验证集(val_data),同样使用 stratify 参数保证每个类别的比例一致。

 4.5. 定义目标目录

train_dir = '/kaggle/working/train'
val_dir = '/kaggle/working/validation'
test_dir = '/kaggle/working/test'

 定义三个目录路径,用于存放划分好的训练集、验证集和测试集图像

4.6. 创建目标目录结构 

for label in labels:
    os.makedirs(os.path.join(train_dir, label), exist_ok=True)
    os.makedirs(os.path.join(val_dir, label), exist_ok=True)
    os.makedirs(os.path.join(test_dir, label), exist_ok=True)

os.makedirs():根据标签(label)创建子目录,用于存放对应类别的图像。

exist_ok=True 确保如果目录已存在,不会报错

 4.7. 复制图像文件到对应目录

for _, row in train_data.iterrows():
    image_path = row['Image_Path']
    label = row['Label']
    shutil.copy(image_path, os.path.join(train_dir, label))

for _, row in val_data.iterrows():
    image_path = row['Image_Path']
    label = row['Label']
    shutil.copy(image_path, os.path.join(val_dir, label))

for _, row in test_data.iterrows():
    image_path = row['Image_Path']
    label = row['Label']
    shutil.copy(image_path, os.path.join(test_dir, label))

iterrows():遍历 train_data、val_data 和 test_data DataFrame 中的每一行(row),从中提取图像路径和标签。

shutil.copy():将图像文件从原始路径复制到对应的训练、验证或测试目录中的相应子目录下。

 5. 对数据集进行处理与展示

这段代码首先统计了训练集、验证集和测试集中不同类别的图像数量。

然后通过 matplotlib 可视化了训练集中的部分图像,帮助了解数据集的基本情况。

最后,通过 ImageFolder 加载图像数据集,并为训练、验证和测试集分别应用相应的图像变换操作。这为后续模型训练提供了标准化的数据加载方式。

 5.1. 统计各数据集中不同类别的图像数量

 5.1.1 统计训练集图像数量

train_dir = '/kaggle/working/train'
subdirectories = ['benign', 'malignant', 'normal']

file_counts = {}

for subdirectory in subdirectories:
    subdirectory_path = os.path.join(train_dir, subdirectory)
    if os.path.exists(subdirectory_path):
        file_count = len(os.listdir(subdirectory_path))
        file_counts[subdirectory] = file_count

for category, count in file_counts.items():
    print(f"Train {category}: {count}")

这里定义了 train_dir 为训练集的目录,子目录为 benign(良性)、malignant(恶性)和 normal(正常)。

对每个子目录,使用 os.path.exists() 检查目录是否存在,然后使用 os.listdir() 获取目录下的文件列表,并统计文件数量。

最后,打印每个类别中图像的数量。

5.1.2  统计验证集图像数量

validation_dir = '/kaggle/working/validation'

与统计训练集图像数量的逻辑相同,只是路径换成了验证集目录 validation_dir

5.1.3  统计测试集图像数量

test_dir = '/kaggle/working/test'

同样的逻辑被用于统计测试集目录中的图像数量

 5.2. 可视化部分训练集图像

train_dir = '/kaggle/working/train'

labels = ['benign', 'malignant', 'normal']
label_dirs = [os.path.join(train_dir, label) for label in labels]

fig, axs = plt.subplots(3, 5, figsize=(25, 18))

desired_width = 300
desired_height = 300

for i, label_dir in enumerate(label_dirs):
    images = [image for image in os.listdir(label_dir) if not image.endswith('_mask.png')][:5]

    for j, image_filename in enumerate(images):
        image_path = os.path.join(label_dir, image_filename)
        image = Image.open(image_path)
        
        image = image.resize((desired_width, desired_height), Image.ANTIALIAS)
        
        axs[i, j].imshow(image)
        axs[i, j].set_title(labels[i])
        axs[i, j].axis('off')

labels:包含三个类别 benign、malignant 和 normal。

label_dirs:通过 os.path.join() 创建每个类别对应的目录路径。

fig, axs = plt.subplots(3, 5, figsize=(25, 18)):创建一个 3 行 5 列的子图布局,用于展示每个类别的 5 张图像。

desired_width 和 desired_height:设置图像的期望尺寸(300x300 像素)。

for 循环遍历每个类别的目录:

使用 os.listdir() 列出目录中的文件,并排除掩码文件(即文件名中包含 _mask.png 的文件)。

选择前 5 张图像进行展示。

使用 Image.open() 打开图像,并将其调整为指定尺寸。

将图像显示在子图中,并设置标题为类别名称

5.3. 使用 ImageFolder 加载数据集 

data_dir='/kaggle/working/'

image_datasets = {
    x: ImageFolder(
        root=os.path.join(data_dir, x),
        transform=data_transforms[x]
    )
    for x in ['train', 'validation', 'test']
}

data_dir:指定数据集的根目录。

ImageFolder:这是 PyTorch 中用于加载图像数据集的类。它将 data_dir 下的 train、validation 和 test 目录作为不同的数据集进行加载。

transform:为每个数据集应用预定义的图像变换(data_transforms[x]),如前面代码中定义的图像增强和标准化操作

6.  实现一个带有早停机制的模型训练函数

        这段代码定义了一个带有早停机制的模型训练流程,逐步统计每个epoch的损失和准确率,并在验证集上通过早停机制防止过拟合。训练完成后,还通过混淆矩阵和分类报告评估模型的表现 

 6.1. 创建数据加载器

batch_size = 8

dataloaders = {x: DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True, num_workers=4)
               for x in ['train', 'validation', 'test']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'validation', 'test']}
class_names = image_datasets['train'].classes

print("Dataset Sizes:", dataset_sizes)
print("Class Labels:", class_names)

batch_size = 8:每个批次处理 8 张图像。

dataloaders:为训练、验证和测试集创建数据加载器 (DataLoader),并启用 shuffle=True 打乱数据以增强训练效果。num_workers=4 用于多线程加载数据,加速处理。

dataset_sizes:计算每个数据集的大小(即图像数量)。

class_names:提取训练集中的类别名称。

打印数据集大小和类别标签

 6.2. 定义带有早停机制的模型训练函数

def train_model_with_early_stopping(model, lossFunction, optimizer, scheduler, dataloaders, dataset_sizes, class_names, device, num_epochs=20, patience=2):
    since = time.time()
    best_model_wts = copy.deepcopy(model.state_dict())
    best_loss = float('inf')
    consecutive_epochs_without_improvement = 0

    train_losses = []
    val_losses = []

since = time.time():记录训练开始的时间。

best_model_wts:保存模型的最佳权重,用于在验证集上获得最低损失时更新。

best_loss:初始化最佳损失为无穷大,后续用于判断当前模型表现是否最佳。

consecutive_epochs_without_improvement:记录没有改善的连续epoch数量,用于早停判断。

train_losses 和 val_losses:用于记录每个epoch的训练和验证损失

 6.3. 开始训练循环

for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    print('-' * 10)

 循环 num_epochs 次,打印当前epoch的信息

6.4. 训练与验证阶段 

for phase in ['train', 'validation']:
    if phase == 'train':
        model.train()
    else:
        model.eval()

    running_loss = 0.0
    running_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 = lossFunction(outputs, labels)

            if phase == 'train':
                loss.backward()
                optimizer.step()

model.train() 和 model.eval():在训练阶段设置模型为训练模式(启用dropout等),在验证阶段设置为评估模式(禁用dropout等)。

optimizer.zero_grad():清除之前计算的梯度。

with torch.set_grad_enabled(phase == 'train'):只有在训练阶段启用梯度计算。

loss.backward() 和 optimizer.step():在训练阶段,反向传播并更新模型权重

6.5. 计算损失与准确率 

running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)

if phase == 'train':
    scheduler.step()

epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = running_corrects.double() / dataset_sizes[phase]

print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

running_loss 和 running_corrects:累加损失和正确预测数量

scheduler.step():更新学习率调度器(仅在训练阶段)

epoch_loss 和 epoch_acc:计算整个数据集上的平均损失和准确率

6.6. 早停机制 

if phase == 'validation':
    if epoch_loss < best_loss:
        best_loss = epoch_loss
        best_model_wts = copy.deepcopy(model.state_dict())
        consecutive_epochs_without_improvement = 0
    else:
        consecutive_epochs_without_improvement += 1

    if consecutive_epochs_without_improvement >= patience:
        print(f"Early stopping after {epoch} epochs")
        break

在验证阶段,如果当前epoch的损失低于之前的最佳损失,更新最佳损失和模型权重,同时重置 consecutive_epochs_without_improvement。

否则,consecutive_epochs_without_improvement 递增

如果连续多个epoch没有改进,且达到设定的耐心阈值 (patience),则触发早停机制,提前结束训练

 6.7. 输出训练耗时及最佳损失

time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('Best val Loss: {:.4f}'.format(best_loss))

model.load_state_dict(best_model_wts)

计算并打印训练耗时。

打印最佳验证损失。

加载最佳模型权重

6.8. 模型评估与混淆矩阵

y_true = []
y_pred = []

model.eval()

with torch.no_grad():
    for inputs, labels in dataloaders['validation']:
        inputs = inputs.to(device)
        labels = labels.to(device)

        outputs = model(inputs)
        _, preds = torch.max(outputs, 1)

        y_true.extend(labels.cpu().numpy())
        y_pred.extend(preds.cpu().numpy())

target_names = [str(class_names[i]) for i in range(len(class_names))]
print(classification_report(y_true, y_pred, target_names=target_names))

cm = confusion_matrix(y_true, y_pred)
print("Confusion Matrix 1:")
print(cm)

y_true 和 y_pred:分别记录真实标签和模型预测标签。

with torch.no_grad():禁用梯度计算以节省内存和加快计算。

classification_report():生成并打印分类报告,包括精确率、召回率、F1分数等。

confusion_matrix():生成并打印混淆矩阵,用于显示分类结果的具体分布情

6. 9. 定义设备

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

 检查是否有可用的 GPU,如果有,则使用 CUDA 设备,否则使用 CPU

这段代码主要实现了使用预训练的 AlexNet 模型进行迁移学习,通过微调模型训练用于分类任务,并评估模型的表现,输出分类报告和混淆矩阵。下面详细解释各部分的功能:

7.使用预训练的 AlexNet 模型进行迁移学习

         这段代码通过加载预训练的 AlexNet 模型进行微调,训练模型用于图像分类任务。通过引入早停机制防止过拟合,并通过测试集评估模型表现,生成分类报告和混淆矩阵以可视化分类效果。

 7.1. 加载预训练的 AlexNet 模型

AlexNet = models.alexnet(pretrained=True)

for param in AlexNet.parameters():
    param.requires_grad = True

models.alexnet(pretrained=True):加载预训练的 AlexNet 模型,该模型在 ImageNet 数据集上预训练,具有良好的特征提取能力。

param.requires_grad = True:设置所有参数的 requires_grad 属性为 True,表示这些参数会参与梯度计算和更新。这意味着我们对模型进行微调(fine-tuning),即不仅训练最后一层,还可以调整整个网络的权重。

7.2. 替换最后一层 

# Get the number of input features of the last layer, which is the 6th layer in the classifier
in_features = AlexNet.classifier[6].in_features

# Replace the last layer with a new one with output features equal to the number of classes
AlexNet.classifier[6] = nn.Linear(in_features, len(class_names))

in_features = AlexNet.classifier[6].in_features:获取 AlexNet 分类器模块最后一层的输入特征数(全连接层的输入维度)。

AlexNet.classifier[6] = nn.Linear(in_features, len(class_names)):替换最后一层(全连接层)为一个新的线性层,该层输出的维度为 len(class_names),即与目标分类任务的类别数一致。

 7.3. 将模型移到设备(GPU或CPU)

AlexNet_fineTuning = AlexNet.to(device)

 to(device):将模型移动到指定的计算设备(GPU 或 CPU),以利用硬件加速

 7.4. 设置优化器和学习率调度器

optimizer = optim.Adam(AlexNet_fineTuning.parameters(), lr=0.00005)
Decay_Learning_Rate = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)

optimizer = optim.Adam(...):使用 Adam 优化器,并设置学习率为 0.00005。

Decay_Learning_Rate = lr_scheduler.StepLR(...):定义学习率调度器,每训练 7 个 epoch,学习率衰减到原来的 10%

 7.5. 设置损失函数

Loss_Function = nn.CrossEntropyLoss()

nn.CrossEntropyLoss():使用交叉熵损失函数,这是多分类任务中常用的损失函数

 7.6. 训练模型

model_fineTuning1 = train_model_with_early_stopping(
    AlexNet_fineTuning, Loss_Function, optimizer, Decay_Learning_Rate, 
    dataloaders, dataset_sizes, class_names, device, num_epochs=20, patience=2)

train_model_with_early_stopping(...):调用之前定义的带有早停机制的模型训练函数。设置 num_epochs=20,表示最多训练 20 个 epoch,patience=2,表示如果连续 2 个 epoch 没有提升,则提前停止训练

 7.7. 保存训练好的模型

torch.save(model_fineTuning1, "/kaggle/working/AlexNet1_approach1.keras")

torch.save(...):将训练好的模型保存到指定路径 /kaggle/working/AlexNet1_approach1.keras

7.8. 模型评估 

y_true = []
y_pred = []

model_fineTuning1.eval()

with torch.no_grad():
    for inputs, labels in dataloaders['test']:
        inputs = inputs.to(device)
        labels = labels.to(device)

        outputs = model_fineTuning1(inputs)
        _, preds = torch.max(outputs, 1)

        y_true.extend(labels.cpu().numpy())
        y_pred.extend(preds.cpu().numpy())

model_fineTuning1.eval():将模型设置为评估模式,禁用 dropout 等正则化操作。

with torch.no_grad():在评估时禁用梯度计算,以节省内存并加速计算。

对测试集进行预测,将真实标签 y_true 和模型预测的标签 y_pred 保存下来

 7.9. 生成分类报告和混淆矩阵

classification_rep = classification_report(y_true, y_pred, target_names=label_names, output_dict=True)
confusion_mat = confusion_matrix(y_true, y_pred)

classification_report(...):生成分类报告,报告包括每个类别的精确率、召回率、F1 分数等。

confusion_matrix(...):生成混淆矩阵,用于展示分类的准确程度和错误情况

 7.10. 可视化混淆矩阵和分类报告

plt.figure(figsize=(5, 3))
sns.heatmap(confusion_mat, annot=True, fmt='d', cmap='Blues', cbar=False, xticklabels=label_names, yticklabels=label_names)
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix 1')
plt.show()

plt.figure(figsize=(6, 4))
sns.heatmap(pd.DataFrame(classification_rep).iloc[:-1, :].T, annot=True, cmap='Blues', fmt='.2f')
plt.title('Classification Report Heatmap')
plt.show()

print("Simplified Classification Report:")
print(pd.DataFrame(classification_rep).iloc[:-1, :])

使用 seaborn 绘制混淆矩阵热图,显示模型在不同类别上的分类情况。

使用 seaborn 绘制分类报告的热图,展示精确率、召回率、F1 分数等指标。

打印简化后的分类报告,方便查看分类效果

8. 测试数据集

        代码从测试数据集中随机抽取 15 张图像,使用模型进行分类预测,并将实际标签和预测标签进行对比。

        图像被转换为灰度图,并使用 matplotlib 进行可视化,展示模型的分类效果。

 8.1. 定义展示图像数量

num_images_to_display = 15

 num_images_to_display = 15:定义要展示的图像数量为 15

8.2. 创建测试数据集的 DataLoader

test_dataloader = DataLoader(image_datasets['test'], batch_size=num_images_to_display, shuffle=True, num_workers=4)

DataLoader(...):从测试数据集创建一个 DataLoader,用于批量加载数据。批量大小设置为 num_images_to_display,即一次加载 15 张图像,并打乱顺序(shuffle=True),以确保每次运行时图像的顺序不同。

num_workers=4:表示使用 4 个工作线程来加载数据,这可以加快数据加载速度

8. 3. 获取一个批次的图像和标签

inputs, labels = next(iter(test_dataloader))

next(iter(test_dataloader)):从 test_dataloader 中获取一个批次的数据,inputs 是图像张量,labels 是对应的标签张量

8.4. 将图像张量移动到设备(GPU 或 CPU)

inputs = inputs.to(device)

 inputs.to(device):将图像数据移动到指定的计算设备(GPU 或 CPU)

8.5. 转换图像为灰度图

grayscale_images = inputs.cpu().numpy().mean(axis=1)

inputs.cpu().numpy():将图像张量从设备上移回到 CPU,并转换为 NumPy 数组格式。

.mean(axis=1):对通道维度(axis=1)取平均值,从而将 RGB 图像转换为灰度图

8.6. 模型推理,进行预测

with torch.no_grad():
    model_fineTuning1.eval()
    outputs = model_fineTuning1(inputs)
    _, preds = torch.max(outputs, 1)

with torch.no_grad():在不计算梯度的上下文中进行推理,以节省内存并加速计算。

model_fineTuning1.eval():将模型设置为评估模式,禁用 dropout 和 batch normalization 等正则化操作。

outputs = model_fineTuning1(inputs):使用微调后的模型对输入图像进行预测,得到每个图像属于各个类别的概率分布。

_, preds = torch.max(outputs, 1):从模型输出的概率分布中选取概率最大的类别索引,作为模型的预测标签 preds

 8.7. 可视化图像及其实际标签和预测标签

plt.figure(figsize=(15, 20))
for i in range(num_images_to_display):
    ax = plt.subplot(5, 3, i + 1)
    ax.axis('off')
    ax.set_title(f'Actual: {class_names[labels[i]]}\nPredicted: {class_names[preds[i]]}')
    plt.imshow(grayscale_images[i], cmap='gray')

plt.show()

plt.figure(figsize=(15, 20)):创建一个新的图形,设置图像大小为 15x20 英寸。

for i in range(num_images_to_display)::循环遍历每张图像(最多 15 张)。

plt.subplot(5, 3, i + 1):在图形中创建一个 5x3 的网格布局,并在网格的第 i+1 个位置绘制图像。

ax.axis('off'):关闭坐标轴。

ax.set_title(f'Actual:{class_names[labels[i]]}\nPredicted: {class_names[preds[i]]}'):设置每个子图的标题,显示该图像的实际标签和模型预测标签。

plt.imshow(grayscale_images[i], cmap='gray'):将灰度图像显示在子图中,使用灰度(gray)颜色映射。

plt.show():显示图像。

9. 微调后的 AlexNet 模型中第一个卷积层的权重矩阵的可视化

这段代码从微调后的 AlexNet 模型中提取了第一个卷积层的权重切片,并通过 matplotlib 进行可视化。

它展示了原始的卷积核权重矩阵和转置后的矩阵。

最后,将微调后的模型保存到指定路径

9.1. 获取第一个卷积层的权重

conv = next(m for m in model_fineTuning1.modules() if isinstance(m, torch.nn.Conv2d))
weights = conv.weight

conv = next(m for m in model_fineTuning1.modules() if isinstance(m, torch.nn.Conv2d)):通过迭代 model_fineTuning1 中的所有模块,找到第一个卷积层 (Conv2d)。next() 函数返回第一个符合条件的模块。

weights = conv.weight:提取卷积层的权重张量(weights)。这个张量的形状通常为 (out_channels, in_channels, kernel_height, kernel_width

9. 2. 提取第一个卷积核的权重切片

slice = weights[0, 0, :, :]  

weights[0, 0, :, :]:提取权重张量的第一个输出通道和第一个输入通道的卷积核。这是一个 2D 矩阵,表示卷积核的空间维度(高度和宽度)

9.3. 将权重切片转换为 NumPy 数组

slice = slice.detach()
slice = slice.cpu()
slice_np = slice.numpy()

slice.detach():从计算图中分离出 slice,使其不再参与梯度计算。

slice.cpu():将张量从 GPU 移动到 CPU(如果当前在 GPU 上)。

slice.numpy():将 PyTorch 张量转换为 NumPy 数组,方便后续的可视化操作

 9.4. 打印权重切片的形状并进行可视化

print(slice_np.shape)

plt.matshow(slice_np)
plt.title("Weight Slice Visualization 2")
plt.colorbar()
plt.show()

print(slice_np.shape):打印提取的权重切片的形状。通常,这是一个二维矩阵,形状为 (kernel_height, kernel_width)。

plt.matshow(slice_np):使用 matshow 函数绘制二维矩阵的图像。此函数会将矩阵中的值映射为颜色进行可视化。

plt.colorbar():为图像添加一个颜色条,显示矩阵值与颜色的对应关系。

plt.show():展示图像

 9.5. 矩阵转置并进行可视化

permuted = np.transpose(slice_np, (1, 0))

plt.matshow(permuted)
plt.title("Permuted Weight Visualization 2")  
plt.colorbar()
plt.show()

permuted = np.transpose(slice_np, (1, 0)):对权重切片矩阵进行转置操作,即交换行和列。这将改变矩阵的形状,使 (height, width) 变为 (width, height)。

plt.matshow(permuted):同样使用 matshow 函数对转置后的矩阵进行可视化。

plt.title("Permuted Weight Visualization 2"):为图像添加标题。

plt.colorbar():为图像添加颜色条。

plt.show():展示转置矩阵的可视化结果

 9.6. 打印矩阵

print("Slice Matrix 2= ", slice_np)
print("Permuted Matrix 2= ", permuted)

print("Slice Matrix 2= ", slice_np):打印原始的卷积核权重矩阵。

print("Permuted Matrix 2= ", permuted):打印转置后的矩阵

 9.7. 保存模型

torch.save(model_fineTuning1, "/kaggle/working/alexnet101_approach1.keras")

torch.save():将微调后的 AlexNet 模型保存到指定路径 /kaggle/working/alexnet101_approach1.keras。保存的格式是 .keras,但实际上这个文件包含的是 PyTorch 模型

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值