基于resnet实现图像分类和检测

本文详细介绍了如何利用ResNet模型进行图像分类和检测。从导入相关模块到数据处理、参数定义,再到模型的选择与训练,包括设置优化器、训练模块以及测试过程,逐一阐述了每个步骤的操作。在实验中,选择了ResNet152作为深度模型,并通过GPU加速计算,最终实现了模型的训练和测试,得到了相应的评估指标。
摘要由CSDN通过智能技术生成

目录

导入相关模块

数据处理和加载准备 

参数定义

制作好数据源

准备json分类文件

创建数据加载器

调用gpu

设置模型参数

设置冻结卷积层

设置模型选择函数

设置训练的层

查看resnet网络架构

设置优化器

训练模块

运行

测试 

定义测试数据预处理方法

随机测试

实际测试

导入相关模块
import os
import matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn
import torch.optim as optim
import torchvision
from torchvision import transforms, models, datasets
#https://pytorch.org/docs/stable/torchvision/index.html
import imageio
import time
import warnings
import random
import sys
import copy
import json
from PIL import Image
数据处理和加载准备 
参数定义
data_dir='./rode_data'
train_dir = './rode_data/train'
valid_dir = './rode_data/valid'
model_name = 'resnet'       #可选的比较多 ['resnet', 'alexnet', 'vgg', 'squeezenet', 'densenet', 'inception']
feature_extract = True      #是否用官方训练好的特征来做
batch_size = 8              #每一次训练8个图像
制作好数据源

- data_transforms中指定了所有图像预处理操作

- ImageFolder假设所有的文件按文件夹保存好,每个文件夹下面存贮同一类别的图片,文件夹的名字为分类的名字

data_transforms = {
    'train': transforms.Compose([transforms.RandomRotation(45),#随机旋转,-45到45度之间随机选
        transforms.CenterCrop(224),#从中心开始裁剪
        transforms.RandomHorizontalFlip(p=0.5),#随机水平翻转 选择一个概率概率
        transforms.RandomVerticalFlip(p=0.5),#随机垂直翻转
        transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1),#参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相
        transforms.RandomGrayscale(p=0.025),#概率转换成灰度率,3通道就是R=G=B
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#均值,标准差
    ]),
    'valid': transforms.Compose([transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}
准备json分类文件
#读取分类注释json文件
with open('cat_to_name.json', 'r') as f:
    cat_to_name = json.load(f)
cat_to_name

结果:

{'1': 'normal', '0': 'potholes'}
创建数据加载器
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'valid']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True) for x in ['train', 'valid']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'valid']}
class_names = image_datasets['train'].classes
print(image_datasets)
print(dataloaders)
print(dataset_sizes)

结果:

{'train': Dataset ImageFolder
    Number of datapoints: 476
    Root location: ./rode_data\train
    StandardTransform
Transform: Compose(
               RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)
               CenterCrop(size=(224, 224))
               RandomHorizontalFlip(p=0.5)
               RandomVerticalFlip(p=0.5)
               ColorJitter(brightness=(0.8, 1.2), contrast=(0.9, 1.1), saturation=(0.9, 1.1), hue=(-0.1, 0.1))
               RandomGrayscale(p=0.025)
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           ), 'valid': Dataset ImageFolder
    Number of datapoints: 205
    Root location: ./rode_data\valid
    StandardTransform
Transform: Compose(
               Resize(size=256, interpolation=bilinear, max_size=None, antialias=warn)
               CenterCrop(size=(224, 224))
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           )}
{'train': <torch.utils.data.dataloader.DataLoader object at 0x000001DAB0782850>, 'valid': <torch.utils.data.dataloader.DataLoader object at 0x000001DAB0781E50>}
{'train': 476, 'valid': 205}
调用gpu
# 是否用GPU训练
train_on_gpu = torch.cuda.is_available()
if not train_on_gpu:
    print('CUDA is not available.  Training on CPU ...')
else:
    print('CUDA is available!  Training on GPU ...')
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
设置模型参数
设置冻结卷积层
#参数优化采用迁移学习策略,考虑到较少的数据量,优化效率和准确率远远不及大数据统计优化的结果,相对于较少的数据最好的方式是选取算法界公认的已经大数据计算出来的优化参数
#False表示进行半冻结,只对全连接层进行训练
def set_parameter_requires_grad(model, feature_extracting):
    if feature_extracting:
        for param in model.parameters():
            param.requires_grad = False

选择模型resnet152,即152层的resnet深度模型

model_ft = models.resnet152()
设置模型选择函数
def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True):
    # 选择合适的模型,不同模型的初始化方法稍微有点区别
    model_ft = None
    input_size = 0

    if model_name == "resnet":
        """ Resnet152
        """
        model_ft = models.resnet152(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Sequential(nn.Linear(num_ftrs, 2),
                                   nn.LogSoftmax(dim=1))
        input_size = 224

    elif model_name == "alexnet":
        """ Alexnet
        """
        model_ft = models.alexnet(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier[6].in_features
        model_ft.classifier[6] = nn.Linear(num_ftrs,num_classes)
        input_size = 224

    elif model_name == "vgg":
        """ VGG11_bn
        """
        model_ft = models.vgg16(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier[6].in_features
        model_ft.classifier[6] = nn.Linear(num_ftrs,num_classes)
        input_size = 224

    elif model_name == "squeezenet":
        """ Squeezenet
        """
        model_ft = models.squeezenet1_0(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        model_ft.classifier[1] = nn.Conv2d(512, num_classes, kernel_size=(1,1), stride=(1,1))
        model_ft.num_classes = num_classes
        input_size = 224

    elif model_name == "densenet":
        """ Densenet
        """
        model_ft = models.densenet121(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier.in_features
        model_ft.classifier = nn.Linear(num_ftrs, num_classes)
        input_size = 224

    elif model_name == "inception":
        """ Inception v3
        Be careful, expects (299,299) sized images and has auxiliary output
        """
        model_ft = models.inception_v3(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        # Handle the auxilary net
        num_ftrs = model_ft.AuxLogits.fc.in_features
        model_ft.AuxLogits.fc = nn.Linear(num_ftrs, num_classes)
        # Handle the primary net
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Linear(num_ftrs,num_classes)
        input_size = 299
    
    else:
        print("Invalid model name, exiting...")
        exit()

    return model_ft, input_size
设置训练的层
model_ft, input_size = initialize_model(model_name,2, feature_extract, use_pretrained=True)
#GPU计算
model_ft = model_ft.to(device)
# 模型保存
filename='checkpoint.pth'
# 是否训练所有层
params_to_update = model_ft.parameters()
print("Params to learn:")
if feature_extract:
    params_to_update = []
    for name,param in model_ft.named_parameters():
        if param.requires_grad == True:
            params_to_update.append(param)
            print("\t",name)
else:
    for name,param in model_ft.named_parameters():
        if param.requires_grad == True:
            print("\t",name)

结果:

Params to learn:
     fc.0.weight
     fc.0.bias
查看resnet网络架构
model_ft

结果:

ResNet(
  (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
  (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (relu): ReLU(inplace=True)
  (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  (layer1): Sequential(
    (0): Bottleneck(
      (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
      (downsample): Sequential(
        (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): Bottleneck(
      (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
  (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
  (fc): Sequential(
    (0): Linear(in_features=2048, out_features=2, bias=True)
    (1): LogSoftmax(dim=1)
  )
)
设置优化器
# 优化器设置,采用学习率衰减策略,随着不断迭代的次数降低学习率,增加结果的稳定性
optimizer_ft = optim.Adam(params_to_update, lr=1e-2)
scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=5, gamma=0.1)#学习率每5个epoch衰减成原来的1/10
#最后一层已经LogSoftmax()了,所以不能用交叉熵nn.CrossEntropyLoss()来计算,nn.CrossEntropyLoss()相当于logSoftmax()和nn.NLLLoss()整合
criterion = nn.NLLLoss()
训练模块
def train_model(model, dataloaders, criterion, optimizer, scheduler, num_epochs=25, is_inception=False,filename=filename):
    since = time.time()
    best_acc = 0
    """
    checkpoint = torch.load(filename)
    best_acc = checkpoint['best_acc']
    model.load_state_dict(checkpoint['state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer'])
    model.class_to_idx = checkpoint['mapping']
    """
    model.to(device)

    val_acc_history = []
    train_acc_history = []
    train_losses = []
    valid_losses = []
    list_mean_loss=[]
    list_mean_acc=[]
    LRs = [optimizer.param_groups[0]['lr']]

    best_model_wts = copy.deepcopy(model.state_dict())

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

        # 训练和验证
        for phase in ['train', 'valid']:
            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'):
                    if is_inception and phase == 'train':
                        outputs, aux_outputs = model(inputs)
                        loss1 = criterion(outputs, labels)
                        loss2 = criterion(aux_outputs, labels)
                        loss = loss1 + 0.4*loss2
                    else:#resnet执行的是这里
                        outputs = model(inputs)
                        loss = criterion(outputs, labels)

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

                    # 训练阶段更新权重
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()

                # 计算损失
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)

            epoch_loss = running_loss / len(dataloaders[phase].dataset)
            epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
            
            
            time_elapsed = time.time() - since
            print('Time elapsed {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
            print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
            list_d.append([epoch_loss,epoch_acc])
            

            # 得到最好那次的模型,并将每次比较最好的模型进行替换保存在cheskpoint.pth里面,最终cheskpoint.pth为最优模型。
            if phase == 'valid' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())
                state = {
                  'state_dict': model.state_dict(),
                  'best_acc': best_acc,
                  'optimizer' : optimizer.state_dict(),
                }
                torch.save(state, filename)
            if phase == 'valid':
                val_acc_history.append(epoch_acc)
                valid_losses.append(epoch_loss)
                scheduler.step(epoch_loss)
            if phase == 'train':
                train_acc_history.append(epoch_acc)
                train_losses.append(epoch_loss)

        loss_sta=abs(list_d[0][0]-list_d[1][0])
        acc_sta=abs(list_d[0][1]-list_d[1][1])
        list_mean_loss.append(loss_sta)
        list_mean_acc.append(acc_sta)
        print('Stability: loss:',loss_sta,'acc:',acc_sta)
        print('Optimizer learning rate : {:.7f}'.format(optimizer.param_groups[0]['lr']))
        LRs.append(optimizer.param_groups[0]['lr'])
        scheduler.step()
        print()
        

    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))
    print('Mean stability of loss',sum(list_mean_loss)/len(list_mean_loss),'Mean stability of acc:',sum(list_mean_acc)/len(list_mean_acc))

    # 训练完后用最好的一次当做模型最终的结果
    model.load_state_dict(best_model_wts)
    return model, val_acc_history, train_acc_history, valid_losses, train_losses, LRs 
运行
model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs  = train_model(model_ft, dataloaders, criterion, optimizer_ft, scheduler, num_epochs=10, is_inception=(model_name=="inception"))

结果:

Epoch 0/9
----------
Time elapsed 2m 25s
train Loss: 0.8071 Acc: 0.7899
Time elapsed 3m 14s
valid Loss: 1.1991 Acc: 0.7317
d:\Python\source\Lib\site-packages\torch\optim\lr_scheduler.py:149: UserWarning: The epoch parameter in `scheduler.step()` was not necessary and is being deprecated where possible. Please use `scheduler.step()` to step the scheduler. During the deprecation, if epoch is different from None, the closed form is used instead of the new chainable form, where available. Please open an issue if you are unable to replicate your use case: https://github.com/pytorch/pytorch/issues/new/choose.
  warnings.warn(EPOCH_DEPRECATION_WARNING, UserWarning)
Stability: loss: 0.39199825748595085 acc: tensor(0.0582, dtype=torch.float64)
Optimizer learning rate : 0.0100000

Epoch 1/9
----------
Time elapsed 5m 32s
train Loss: 0.6175 Acc: 0.8508
Time elapsed 6m 29s
valid Loss: 0.1584 Acc: 0.9610
Stability: loss: 0.4590862536008037 acc: tensor(0.1101, dtype=torch.float64)
Optimizer learning rate : 0.0100000

Epoch 2/9
----------
Time elapsed 8m 36s
train Loss: 0.4168 Acc: 0.8887
Time elapsed 9m 16s
valid Loss: 0.3283 Acc: 0.9366
Stability: loss: 0.08845721305707516 acc: tensor(0.0479, dtype=torch.float64)
Optimizer learning rate : 0.0100000

Epoch 3/9
----------
Time elapsed 10m 48s
train Loss: 1.0024 Acc: 0.8613
...

Training complete in 39m 39s
Best val Acc: 0.960976
Mean stability of loss 0.45964427352522746 Mean stability of acc: tensor(0.0630, dtype=torch.float64)
测试 

加载训练好的模型

model_ft, input_size = initialize_model(model_name, 2, feature_extract, use_pretrained=True)
# GPU模式
model_ft = model_ft.to(device)
# 保存最优模型pth文件
filename='checkpoint.pth'
# 加载模型
checkpoint = torch.load(filename)
best_acc = checkpoint['best_acc']
model_ft.load_state_dict(checkpoint['state_dict'])
定义测试数据预处理方法
def process_image(image_path):
    # 读取测试数据
    img = Image.open(image_path)
    # Resize,thumbnail方法只能进行缩小,所以进行了判断
    if img.size[0] > img.size[1]:
        img.thumbnail((10000, 256))
    else:
        img.thumbnail((256, 10000))
    # Crop操作
    left_margin = (img.width-224)/2
    bottom_margin = (img.height-224)/2
    right_margin = left_margin + 224
    top_margin = bottom_margin + 224
    img = img.crop((left_margin, bottom_margin, right_margin,   
                      top_margin))
    # 相同的预处理方法
    img = np.array(img)/255
    mean = np.array([0.485, 0.456, 0.406]) #provided mean
    std = np.array([0.229, 0.224, 0.225]) #provided std
    img = (img - mean)/std
    
    # 注意颜色通道应该放在第一个位置
    img = img.transpose((2, 0, 1))
    
    return img

def imshow(image, ax=None, title=None):
    """展示数据"""
    if ax is None:
        fig, ax = plt.subplots()
    
    # 颜色通道还原
    image = np.array(image).transpose((1, 2, 0))
    
    # 预处理还原
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    image = std * image + mean
    image = np.clip(image, 0, 1)
    
    ax.imshow(image)
    ax.set_title(title)
    
    return ax

#将tensor数据转为numpy
def im_convert(tensor):
    """ 展示数据"""
    
    image = tensor.to("cpu").clone().detach()
    image = image.numpy().squeeze()
    image = image.transpose(1,2,0)
    image = image * np.array((0.229, 0.224, 0.225)) + np.array((0.485, 0.456, 0.406))
    image = image.clip(0, 1)

    return image
随机测试
# 得到一个batch的测试数据
dataiter = iter(dataloaders['valid'])
images, labels = next(dataiter)
model_ft.eval()

if train_on_gpu:
    output = model_ft(images.cuda())
else:
    output = model_ft(images)
print('True:',labels)
print(output.shape)
_, preds_tensor = torch.max(output, 1)
preds = np.squeeze(preds_tensor.numpy()) if not train_on_gpu else np.squeeze(preds_tensor.cpu().numpy())
print('Pred:',preds)

结果:

True: tensor([0, 1, 0, 1, 1, 0, 1, 0])
torch.Size([8, 2])
Pred: [0 1 0 1 1 0 1 0]

展示

fig=plt.figure(figsize=(20, 20))
columns =4
rows = 2

for idx in range (columns*rows):
    ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[])
    plt.imshow(im_convert(images[idx]))
    ax.set_title("{} ({})".format(cat_to_name[str(preds[idx])], cat_to_name[str(labels[idx].item())]),
                 color=("green" if cat_to_name[str(preds[idx])]==cat_to_name[str(labels[idx].item())] else "red"))
plt.show()

结果:

实际测试
from tqdm import tqdm
import pandas as pd
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
import torch.nn.functional as F

# 定义图像转换
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# 指定测试数据集路径
testdata_dir = "./data_text/"
normal_filenames = testdata_dir+'normal'
pothoes_filenames = testdata_dir+'potholes'
# 加载模型
model_ft.eval()
# 遍历测试数据集中的图像文件
list_normal_input=[]
list_pothoes_input=[]
list_input=[]

def running(filenames,list_input):
    global list_normal_input,list_pothoes_input
    image_filenames = os.listdir(filenames)
    if filenames == normal_filenames:
        class_true = 'normal'
    else:
        class_true = 'potholes'
    for image_filename in tqdm(image_filenames):
        image_path = os.path.join(filenames, image_filename)
        image = Image.open(image_path).convert("RGB")
        image = transform(image).unsqueeze(0).to(device)
        
        with torch.no_grad():
            output = model_ft(image)
            probabilities = F.softmax(output, dim=1)
            predicted_prod, predicted_class = torch.max(probabilities, 1)
        class_index = predicted_class.item()
        class_name = class_names[class_index]
        list_input.append([image_filename,class_name,predicted_prod.item(),class_true])

    # 输出图像文件名及其预测的类别
    #print(f"Image: {image_filename}, Predicted Class: {class_name}")

running(normal_filenames,list_normal_input)
running(pothoes_filenames,list_pothoes_input)
list_input.extend(list_normal_input)
list_input.extend(list_pothoes_input)
print('Finished')

#训练准确率
right = 0
false = 0
for i in list_normal_input:
    if i[1] == '1':
        right+=1
    else:
        false+=1
for i in list_pothoes_input:
    if i[1] == '0':
        right+=1
    else:
        false+=1
print('实际测试的准确率为:%.2f%%' %(right/(len(list_normal_input)+len(list_pothoes_input))*100))

结果:

100%|██████████| 266/266 [01:33<00:00,  2.85it/s]
100%|██████████| 95/95 [00:32<00:00,  2.94it/s]Finished
实际测试的准确率为:93.91%

计算AUC

from sklearn.metrics import roc_curve, roc_auc_score
import matplotlib.pyplot as plt

# 提取预测概率和真实标签
y_probs = [item[2] for item in list_input]
y_true = [int(item[3] != 'normal') for item in list_input]

# 计算真阳性率(TPR)和假阳性率(FPR)
fpr, tpr, thresholds = roc_curve(y_true, y_probs)

# 计算AUC值
roc_auc = roc_auc_score(y_true, y_probs)

# 绘制ROC曲线
plt.plot(fpr, tpr, label='ROC curve (AUC = %.3f)' % roc_auc)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) Curve')
plt.legend(loc='lower right')

# 显示图形
plt.show()

结果:

注意:具体情况需要修改合理的resnet层数和epoch迭代次数,学习率也可以按照训练集进行调整。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

刀不及你心凉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值