深度学习每周学习总结P4(猴痘识别)

–来自百度网盘超级会员V5的分享

0. 总结

数据导入部分:本次数据导入没有使用torchvision自带的数据集,需要将原始数据进行处理包括数据导入,数据类型转换。

划分数据集:划定训练集测试集后,再使用torch.utils.data中的DataLoader()加载数据

模型构建部分:有两个部分一个初始化部分(init())列出了网络结构的所有层,比如卷积层池化层等。第二个部分是前向传播部分,定义了数据在各层的处理过程。

设置超参数:在这之前需要定义损失函数,学习率,以及根据学习率定义优化器(例如SGD随机梯度下降),用来在训练中更新参数,最小化损失函数。

定义训练函数:函数的传入的参数有四个,分别是设置好的DataLoader(),定义好的模型,损失函数,优化器。函数内部初始化损失准确率为0,接着开始循环,使用DataLoader()获取一个批次的数据,对这个批次的数据带入模型得到预测值,然后使用损失函数计算得到损失值。接下来就是进行反向传播以及使用优化器优化参数,梯度清零放在反向传播之前或者是使用优化器优化之后都是可以的,一般是默认放在反向传播之前。

定义测试函数:函数传入的参数相比训练函数少了优化器,只需传入设置好的DataLoader(),定义好的模型,损失函数。此外除了处理批次数据时无需再设置梯度清零、返向传播以及优化器优化参数,其余部分均和训练函数保持一致。

训练过程:定义训练次数,有几次就使用整个数据集进行几次训练,初始化四个空list分别存储每次训练及测试的准确率及损失。使用model.train()开启训练模式,调用训练函数得到准确率及损失。使用model.eval()将模型设置为评估模式,调用测试函数得到准确率及损失。接着就是将得到的训练及测试的准确率及损失存储到相应list中并合并打印出来,得到每一次整体训练后的准确率及损失。

结果可视化

模型的保存,调取及使用。在PyTorch中,通常使用 torch.save(model.state_dict(), ‘model.pth’) 保存模型的参数,使用 model.load_state_dict(torch.load(‘model.pth’)) 加载参数。

需要改进优化的地方:再保证整体流程没有问题的情况下,继续细化细节研究,比如一些函数的原理及作用,如何提升训练集准确率等问题。

1. 数据导入部分

import torch
import torch.nn as nn
import torchvision
from torchvision import transforms,datasets

import os,PIL,pathlib

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

device
device(type='cuda')
# 查看数据分类
import os,PIL,random,pathlib

data_dir = './data/mpox_recognize/'
data_dir = pathlib.Path(data_dir) # 使用pathlib.Path()函数将字符串类型的文件夹路径转换为pathlib.Path对象。

data_paths = list(data_dir.glob('*')) # 使用glob()方法获取data_dir路径下的所有文件路径,并以列表形式存储在data_paths中。
# classNames = [str(path).split('\\')[-1] for path in data_paths] # 通过split()函数对data_paths中的每个文件路径执行分割操作,获得各个文件所属的类别名称
# classNames = [str(path).split('\\')[2] for path in data_paths]
classNames = [path.parts[-1] for path in data_paths] # pathlib的.parts方法会返回路径各部分的一个元组
# classNames = [path.parts[2] for path in data_paths]
classNames
['Monkeypox', 'Others']
total_datadir = './data/mpox_recognize/'

# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])

total_data = datasets.ImageFolder(total_datadir,transform=train_transforms)
total_data
Dataset ImageFolder
    Number of datapoints: 2142
    Root location: ./data/mpox_recognize/
    StandardTransform
Transform: Compose(
               Resize(size=[224, 224], interpolation=bilinear, max_size=None, antialias=warn)
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           )

2. 划分数据集

# 划分数据集
train_size = int(0.8 * len(total_data))
test_size = len(total_data) - train_size
train_dataset,test_dataset = torch.utils.data.random_split(total_data,[train_size,test_size])
train_dataset,test_dataset
(<torch.utils.data.dataset.Subset at 0x24b70342d00>,
 <torch.utils.data.dataset.Subset at 0x24b70342a90>)
train_size,test_size
(1713, 429)
# 使用dataloader加载数据,并设置好基本的batch_size
batch_size = 32

train_dl = torch.utils.data.DataLoader(
    train_dataset,
    batch_size = batch_size,
    shuffle = True,
    num_workers = 1 # 用于数据加载的子进程数量。通常,将其设置为大于 0 的值可以加快数据加载速度,特别是当数据集很大时。默认值为 0,表示在主进程中加载数据。
)
test_dl = torch.utils.data.DataLoader(
    test_dataset,
    batch_size = batch_size,
    shuffle = True,
    num_workers = 1
)
for X,y in test_dl:
    print("Shape of X [N,C,H,W]: ",X.shape)
    print("Shape of y: ",y.shape,y.dtype)
    break
Shape of X [N,C,H,W]:  torch.Size([32, 3, 224, 224])
Shape of y:  torch.Size([32]) torch.int64

3. 模型构建部分

3.1 模型构建

import torch.nn.functional as F

class Network_bn(nn.Module):
    def __init__(self):
        super(Network_bn,self).__init__()
        self.conv1 = nn.Conv2d(in_channels = 3,out_channels = 12,kernel_size = 5,stride = 1,padding = 0)
        self.bn1 = nn.BatchNorm2d(12)
        self.conv2 = nn.Conv2d(in_channels = 12,out_channels = 12,kernel_size = 5,stride = 1,padding = 0)
        self.bn2 = nn.BatchNorm2d(12)
        self.pool = nn.MaxPool2d(2,2)
        self.conv3 = nn.Conv2d(in_channels = 12,out_channels = 24,kernel_size = 5,stride = 1,padding = 0)
        self.bn3 = nn.BatchNorm2d(24)
        self.conv4 = nn.Conv2d(in_channels = 24,out_channels = 24,kernel_size = 5,stride = 1,padding = 0)
        self.bn4 = nn.BatchNorm2d(24)
        self.dropout = nn.Dropout(p = 0.5)
        self.fc1 = nn.Linear(24*50*50,len(classNames))
    
    def forward(self,x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = self.pool(x)
        x = F.relu(self.bn3(self.conv3(x)))
        x = F.relu(self.bn4(self.conv4(x)))
        x = self.pool(x)
        x = x.view(-1,24*50*50)
        x = F.relu(self.fc1(x))
        return x

print("Using {} device".format(device))

model = Network_bn().to(device)
model
Using cuda device





Network_bn(
  (conv1): Conv2d(3, 12, kernel_size=(5, 5), stride=(1, 1))
  (bn1): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (conv2): Conv2d(12, 12, kernel_size=(5, 5), stride=(1, 1))
  (bn2): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv3): Conv2d(12, 24, kernel_size=(5, 5), stride=(1, 1))
  (bn3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (conv4): Conv2d(24, 24, kernel_size=(5, 5), stride=(1, 1))
  (bn4): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (dropout): Dropout(p=0.5, inplace=False)
  (fc1): Linear(in_features=60000, out_features=2, bias=True)
)

3.2 公式推导

3, 224, 224(输入数据)

-> 12, 220, 220(经过卷积层1)

-> 12, 216, 216(经过卷积层2)-> 12, 108, 108(经过池化层1)

-> 24, 104, 104(经过卷积层3)

-> 24, 100, 100(经过卷积层4)-> 24, 50, 50(经过池化层2)

-> 60000 -> num_classes(2)

计算公式:

卷积维度计算公式:

  • 高度方向: H o u t = ( H i n − K e r n e l _ s i z e + 2 × p a d d i n g ) s t r i d e + 1 H_{out} = \frac{\left(H_{in} - Kernel\_size + 2\times padding\right)}{stride} + 1 Hout=stride(HinKernel_size+2×padding)+1

  • 宽度方向: W o u t = ( W i n − K e r n e l _ s i z e + 2 × p a d d i n g ) s t r i d e + 1 W_{out} = \frac{\left(W_{in} - Kernel\_size + 2\times padding\right)}{stride} + 1 Wout=stride(WinKernel_size+2×padding)+1

  • 卷积层通道数变化:数据通道数为卷积层该卷积层定义的输出通道数,例如:self.conv1 = nn.Conv2d(3,64,kernel_size = 3)。在这个例子中,输出的通道数为64,这意味着卷积层使用了64个不同的卷积核,每个核都在输入数据上独立进行卷积运算,产生一个新的通道。需要注意,卷积操作不是在单独的通道上进行的,而是跨所有输入通道(本例中为3个通道)进行的,每个卷积核提供一个新的输出通道。

池化层计算公式:

  • 高度方向: H o u t = ( H i n + 2 × p a d d i n g H − d i l a t i o n H × ( k e r n e l _ s i z e H − 1 ) − 1 s t r i d e H + 1 ) H_{out} = \left(\frac{H_{in} + 2 \times padding_H - dilation_H \times (kernel\_size_H - 1) - 1}{stride_H} + 1 \right) Hout=(strideHHin+2×paddingHdilationH×(kernel_sizeH1)1+1)

  • 宽度方向: W o u t = ( W i n + 2 × p a d d i n g W − d i l a t i o n W × ( k e r n e l _ s i z e W − 1 ) − 1 s t r i d e W + 1 ) W_{out} = \left( \frac{W_{in} + 2 \times padding_W - dilation_W \times (kernel\_size_W - 1) - 1}{stride_W} + 1 \right) Wout=(strideWWin+2×paddingWdilationW×(kernel_sizeW1)1+1)

其中:

  • H i n H_{in} Hin W i n W_{in} Win 是输入的高度和宽度。
  • p a d d i n g H padding_H paddingH p a d d i n g W padding_W paddingW 是在高度和宽度方向上的填充量。
  • k e r n e l _ s i z e H kernel\_size_H kernel_sizeH k e r n e l _ s i z e W kernel\_size_W kernel_sizeW 是卷积核或池化核在高度和宽度方向上的大小。
  • s t r i d e H stride_H strideH s t r i d e W stride_W strideW 是在高度和宽度方向上的步长。
  • d i l a t i o n H dilation_H dilationH d i l a t i o n W dilation_W dilationW 是在高度和宽度方向上的膨胀系数。

请注意,这里的膨胀系数 $dilation \times (kernel_size - 1) $实际上表示核在膨胀后覆盖的区域大小。例如,一个 $3 \times 3 $ 的核,如果膨胀系数为2,则实际上它覆盖的区域大小为$ 5 \times 5 $(原始核大小加上膨胀引入的间隔)。

计算流程:

输入数据:( 3 ∗ 224 ∗ 224 3*224*224 3224224)

conv1计算:卷积核数12,输出的通道也为12。-> ( 12 ∗ 220 ∗ 220 ) (12*220*220) (12220220)
输出维度 = ( 224 − 5 + 2 × 0 ) 1 + 1 = 220 \text{输出维度} = \frac{\left(224 - 5 + 2 \times 0\right)}{1} + 1 = 220 输出维度=1(2245+2×0)+1=220

conv2计算:-> ( 12 ∗ 216 ∗ 216 ) (12*216*216) (12216216)
输出维度 = ( 220 − 5 + 2 × 0 ) 1 + 1 = 216 \text{输出维度} = \frac{\left(220 - 5 + 2 \times 0\right)}{1} + 1 = 216 输出维度=1(2205+2×0)+1=216

pool1计算:通道数不变,步长为2-> ( 12 ∗ 108 ∗ 108 ) (12*108*108) (12108108)
输出维度 = ( 216 + 2 × 0 − 1 × ( 2 − 1 ) − 1 2 + 1 ) = 107 + 1 = 108 \text{输出维度} = \left(\frac{216 + 2 \times 0 - 1 \times \left(2 - 1\right) - 1}{2} + 1 \right) = 107 +1 = 108 输出维度=(2216+2×01×(21)1+1)=107+1=108

conv3计算:-> ( 24 ∗ 104 ∗ 104 ) (24*104*104) (24104104)
输出维度 = ( 108 − 5 + 2 × 0 ) 1 + 1 = 104 \text{输出维度} = \frac{\left(108 - 5 + 2 \times 0\right)}{1} + 1 = 104 输出维度=1(1085+2×0)+1=104

conv4计算:-> ( 24 ∗ 100 ∗ 100 ) (24*100*100) (24100100)
输出维度 = ( 104 − 5 + 2 × 0 ) 1 + 1 = 100 \text{输出维度} = \frac{\left(104 - 5 + 2 \times 0\right)}{1} + 1 = 100 输出维度=1(1045+2×0)+1=100

pool2计算:-> ( 24 ∗ 50 ∗ 50 ) (24*50*50) (245050)
输出维度 = ( 100 + 2 × 0 − 1 × ( 2 − 1 ) − 1 2 + 1 ) = 49 + 1 = 50 \text{输出维度} = \left(\frac{100 + 2 \times 0 - 1 \times \left(2 - 1\right) - 1}{2} + 1 \right) = 49 +1 = 50 输出维度=(2100+2×01×(21)1+1)=49+1=50

flatten层:-> 60000 60000 60000

n u m _ c l a s s e s ( 2 ) num\_classes(2) num_classes(2)

4. 设置超参数

loss_fn = torch.nn.CrossEntropyLoss()
learn_rate = 1e-4
opt = torch.optim.SGD(model.parameters(),lr=learn_rate)

5. 定义训练函数

len(train_dl.dataset),len(train_dl),len(test_dl.dataset),len(test_dl)
(1713, 54, 429, 14)
# 训练循环
def train(dataloader,model,loss_fn,optimizer):
    size = len(dataloader.dataset) # 训练集的大小,一共1713张图片
    num_batches = len(dataloader) # 批次数目,54(1713/32)
    
    train_loss,train_acc = 0,0 # 初始化训练损失和正确率
    
    for X,y in dataloader: # 获取图片及其标签
        X,y = X.to(device),y.to(device)
        
        # 计算预测误差
        pred = model(X) # 网络输出
        loss = loss_fn(pred,y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 返乡传播
        optimizer.zero_grad() # grad属性归零
        loss.backward() # 反向传播
        optimizer.step() # 每一步自动更新
        
        # 记录acc与loss
        train_acc += (pred.argmax(1)==y).type(torch.float).sum().item()
        train_loss += loss.item()
        
    train_acc /= size
    train_loss /= num_batches
    
    return train_acc,train_loss

6. 定义测试函数

def test(dataloader,model,loss_fn):
    size = len(dataloader.dataset) # 测试集的大小,一共429张图片
    num_batches = len(dataloader) # 批次数目,14(429/32=13.4,向上取整)
    
    test_acc,test_loss = 0,0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for X,y in dataloader:
            X,y = X.to(device),y.to(device)

            # 计算预测误差
            pred = model(X)
            loss = loss_fn(pred,y)
            
            # 记录acc与loss
            test_acc += (pred.argmax(1)==y).type(torch.float).sum().item()
            test_loss += loss.item()
    
    test_acc /= size
    test_loss /= num_batches
    
    return test_acc,test_loss

7. 训练过程

epochs = 20
train_acc = []
train_loss = []
test_acc = []
test_loss = []

for epoch in range(epochs):
    model.train()
    epoch_train_acc,epoch_train_loss = train(train_dl,model,loss_fn,opt)
    
    model.eval()
    epoch_test_acc,epoch_test_loss = test(test_dl,model,loss_fn)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    template = ('Epoch:{:2d},Train_acc:{:.1f}%,Train_loss:{:.3f},Test_acc:{:.1f}%,Test_loss:{:.3f}')
    print(template.format(epoch+1,epoch_train_acc*100,epoch_train_loss,epoch_test_acc*100,epoch_test_loss))
    
print('Done!')
Epoch: 1,Train_acc:57.0%,Train_loss:0.669,Test_acc:60.1%,Test_loss:0.661
Epoch: 2,Train_acc:65.8%,Train_loss:0.615,Test_acc:64.8%,Test_loss:0.618
Epoch: 3,Train_acc:70.1%,Train_loss:0.568,Test_acc:67.6%,Test_loss:0.594
Epoch: 4,Train_acc:74.1%,Train_loss:0.530,Test_acc:70.6%,Test_loss:0.589
Epoch: 5,Train_acc:76.3%,Train_loss:0.500,Test_acc:70.9%,Test_loss:0.561
Epoch: 6,Train_acc:78.6%,Train_loss:0.472,Test_acc:73.2%,Test_loss:0.532
Epoch: 7,Train_acc:81.1%,Train_loss:0.442,Test_acc:74.1%,Test_loss:0.518
Epoch: 8,Train_acc:82.2%,Train_loss:0.421,Test_acc:75.3%,Test_loss:0.516
Epoch: 9,Train_acc:83.1%,Train_loss:0.401,Test_acc:76.0%,Test_loss:0.497
Epoch:10,Train_acc:84.2%,Train_loss:0.388,Test_acc:77.6%,Test_loss:0.495
Epoch:11,Train_acc:85.6%,Train_loss:0.371,Test_acc:77.6%,Test_loss:0.478
Epoch:12,Train_acc:86.4%,Train_loss:0.363,Test_acc:78.6%,Test_loss:0.487
Epoch:13,Train_acc:87.9%,Train_loss:0.342,Test_acc:78.3%,Test_loss:0.476
Epoch:14,Train_acc:89.0%,Train_loss:0.331,Test_acc:80.2%,Test_loss:0.457
Epoch:15,Train_acc:89.8%,Train_loss:0.317,Test_acc:79.5%,Test_loss:0.480
Epoch:16,Train_acc:90.3%,Train_loss:0.313,Test_acc:80.9%,Test_loss:0.472
Epoch:17,Train_acc:90.7%,Train_loss:0.298,Test_acc:80.9%,Test_loss:0.442
Epoch:18,Train_acc:91.4%,Train_loss:0.289,Test_acc:82.1%,Test_loss:0.440
Epoch:19,Train_acc:91.1%,Train_loss:0.286,Test_acc:82.5%,Test_loss:0.429
Epoch:20,Train_acc:91.2%,Train_loss:0.277,Test_acc:81.6%,Test_loss:0.427
Done!

8. 结果可视化

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

在这里插入图片描述

9.模型的保存及预测

9.1 代码部分

# 模型保存
# torch.save(model.'mpox_rec_model.pth') # 保存整个模型

# 模型加载
# model2 = torch.load('mpox_rec_model.pth') 
# model2 = model2.to(device) # 理论上在哪里保存模型,加载模型也会优先在哪里,指定一下确保不会出错
# 状态字典保存
torch.save(model.state_dict(),'mpox_rec_model_state_dict.pth') # 仅保存状态字典

# 加载状态字典到模型
model2 = Network_bn().to(device) # 重新定义模型
model2.load_state_dict(torch.load('mpox_rec_model_state_dict.pth')) # 加载状态字典到模型
<All keys matched successfully>
# 指定路径图片预测
from PIL import Image
import torchvision.transforms as transforms

classes = list(total_data.class_to_idx) # classes = list(total_data.class_to_idx)

def predict_one_image(image_path,model,transform,classes):
    
    test_img = Image.open(image_path).convert('RGB')
    # plt.imshow(test_img) # 展示待预测的图片
    
    test_img = transform(test_img)
    img = test_img.to(device).unsqueeze(0)
    
    model.eval()
    output = model(img)
    print(output) # 观察模型预测结果的输出数据
    
    _,pred = torch.max(output,1)
    pred_class = classes[pred]
    print(f'预测结果是:{pred_class}')
# 预测训练集中的某张照片
predict_one_image(image_path='./data/mpox_recognize/Monkeypox/M01_01_00.jpg', 
                  model=model, 
                  transform=train_transforms, 
                  classes=classes)
tensor([[1.7220, 1.2390]], device='cuda:0', grad_fn=<ReluBackward0>)
预测结果是:Monkeypox

9.2 疑问解析

问题一:您好,请为我分析一下这段代码的含义,“classes = list (total_data.class_to_idx)”,其中total_data是使用torchvision.datasets.ImageFolder()处理得到的。请问具体的处理过程是怎样的?不同文件夹的名称处理后会自动作为分类处理吗?

问题解答:

您提供的代码行’ classes = list(total_data.class_to_idx) '通常用于PyTorch中加载和处理图像数据集的上下文中,特别是在使用"torchvision.datasets.ImageFolder"类时。让我们分解这个过程和具体的代码行来理解它的含义,以及它如何适应PyTorch图像数据处理的大背景。

理解“torchvision.datasets.ImageFolder”

"torchvision.datasets.ImageFolder"是PyTorch的torchvision包提供的一个实用程序类,旨在简化存储在磁盘目录结构中的图像数据的加载、转换和批处理。对于图像分类任务,它特别有用,因为您将数据集组织到子目录中,每个子目录以您希望模型学习的一个类命名。

目录结构

在深入研究代码之前,有必要了解预期的目录结构。假设您有一个包含三个类别的数据集:“猫”、“狗”和“鸟”。目录结构应该是这样的:

/path/to/data/
├── cats
│   ├── cat001.jpg
│   ├── cat002.jpg
│   └── ...
├── dogs
│   ├── dog001.jpg
│   ├── dog002.jpg
│   └── ...
└── birds
    ├── bird001.jpg
    ├── bird002.jpg
    └── ...

使用ImageFolder

当你实例化一个带有数据路径的’ ImageFolder '对象时,PyTorch会做以下操作:

  1. 扫描目录:递归扫描镜像文件的目录结构。
  2. 为类分配索引:每个子目录被视为一个单独的类,PyTorch自动为每个类分配索引。类名(子目录名)到它们分配的索引的映射存储在名为’class_to_idx '的字典属性中。
  3. 加载和转换图像:它提供了动态加载图像和应用任何指定转换(调整大小,规范化等)的机制。

分析特定的代码行

classes = list(total_data.class_to_idx)

鉴于上述背景:

  • ‘total_data’:这是’ ImageFolder '类的一个实例,它已经按照描述的方式处理了目录结构。

  • ‘total_data.class_to_idx’:这是一个字典,其中键是类名(派生自子目录的名称),值是它们对应的索引(整数)。索引是根据类名的排序顺序分配的。

  • ‘list(total_data.class_to_idx)’:此代码将’ class_to_idx '字典的键转换为列表。生成的“classes”列表包含按顺序排序的类名(子目录名)。此列表通常用于根据代码中其他地方的索引引用类名,特别是在解释模型的输出时。

总结:

因此,总而言之,您提供的代码提取并列出了“ImageFolder”自动识别和索引的类名(来自数据集中的子目录名)。“ImageFolder”的这个功能简化了加载图像数据进行分类的任务,确保每个类别,由一个文件夹表示,被自动视为一个单独的类。




  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值