首页 > 其他分享 >Pytorch实战

Pytorch实战

时间:2024-11-14 15:34:45浏览次数:1  
标签:实战 opt self py Pytorch train __ data

  在学习某个深度学习框架时,掌握其基本知识和接口固然重要,但如何合理组织代码,使得代码具有良好的可读性和可扩展性也必不可少。本文不会深入讲解过多知识性的东西,更多的则是传授一些经验,这些内容可能有些争议,因其受我个人喜好和coding风格影响较大,**你可以将这部分当成是一种参考或提议,而不是作为必须遵循的准则**。归根到底,都是希望你能以一种更为合理的方式组织自己的程序。

  在做深度学习实验或项目时,为了得到最优的模型结果,中间往往需要很多次的尝试和修改。而合理的文件组织结构,以及一些小技巧可以极大地提高代码的易读易用性。根据我的个人经验,在从事大多数深度学习研究时,程序都需要实现以下几个功能

  • 模型定义
  • 数据处理和加载
  • 训练模型(Train&Validate)
  • 训练过程的可视化
  • 测试(Test/Inference)

  另外程序还应该满足以下几个要求

  • 模型需具有高度可配置性,便于修改参数、修改模型,反复实验
  • 代码应具有良好的组织结构,使人一目了然
  • 代码应具有良好的说明,使其他人能够理解

1、项目介绍

  Dogs vs. Cats是一个传统的二分类问题,其训练集包含25000张图片,均放置在同一文件夹下,命名格式为`<category>.<num>.jpg`, 如`cat.10000.jpg`、`dog.100.jpg`,测试集包含12500张图片,命名为`<num>.jpg`,如`1000.jpg`。参赛者需根据训练集的图片训练模型,并在测试集上进行预测,输出它是狗的概率。

2、文件组织架构

正如前面所提到的,程序主要包含以下功能:

- 模型定义

- 数据加载

- 训练和测试

首先来看程序文件的组织结构:

```
├── checkpoints/
├── data/
│   ├── __init__.py
│   ├── dataset.py
│   └── get_data.sh
├── models/
│   ├── __init__.py
│   ├── AlexNet.py
│   ├── BasicModule.py
│   └── ResNet34.py
└── utils/
│   ├── __init__.py
│   └── visualize.py
├── config.py
├── main.py
├── requirements.txt
├── README.md
```

其中:

- `checkpoints/`: 用于保存训练好的模型,可使程序在异常退出后仍能重新载入模型,恢复训练
- `data/`:数据相关操作,包括数据预处理、dataset实现等
- `models/`:模型定义,可以有多个模型,例如上面的AlexNet和ResNet34,一个模型对应一个文件
- `utils/`:可能用到的工具函数,在本次实验中主要是封装了可视化工具
- `config.py`:配置文件,所有可配置的变量都集中在此,并提供默认值
- `main.py`:主文件,训练和测试程序的入口,可通过不同的命令来指定不同的操作和参数
- `requirements.txt`:程序依赖的第三方库
- `README.md`:提供程序的必要说明

3、关于'__init__.py'

  可以看到,几乎每个文件夹都有'__init__.py',一个目录如果包含了'__init__.py'文件,那么它就变成了一个包(package)。'__init__.py'可以为空,也可以定义包的属性和方法,但其必须存在,其他程序才能从这个目录导入相应的模块或函数。例如在'data/'文件夹下有'__init__.py',则在'main.py'中就可以'from data.dataset import DogCat'。而如果在'__init__.py'中写入:'from data import DogCat',则在main.py中就可以直接写为:`from data import DogCat`,或者'import data; dataset =data.DogCat',更加方便。

4、数据加载

  数据的相关处理主要保存在`data/dataset.py`中。关于数据加载的相关操作,在上一章中我们已经提到过,其基本原理就是使用`Dataset`提供数据集的封装,再使用`Dataloader`实现数据并行加载。Kaggle提供的数据包括训练集和测试集,而我们在实际使用中,还需专门从训练集中取出一部分作为验证集。

  对于这三类数据集,其相应操作也不太一样,而如果专门写三个`Dataset`,则稍显复杂和冗余,因此这里通过加一些判断来区分。对于训练集,我们希望做一些数据增强处理,如随机裁剪、随机翻转、加噪声等,而验证集和测试集则不需要。下面看`dataset.py`的代码:

import os
from PIL import Image
from torch.utils import data
import numpy as np
from torchvision import transforms as T

class DogCat(data.Dataset):
    
    def __init__(self, root, transforms=None, train=True, test=False):
        """
        初始化方法,用于加载数据集

        参数:
        - root: 数据集的根目录,包含所有图片
        - transforms: 数据预处理操作(数据增强等),如果为 None 则使用默认预处理
        - train: 布尔值,指示是否为训练集
        - test: 布尔值,指示是否为测试集(测试集不含标签)

        功能:
        - 根据训练、验证、测试划分数据集
        - 对图片路径列表进行排序以确保顺序一致
        - 根据train和test标记划分训练集、验证集或测试集
        """
        self.test = test  # 是否是测试集
        # 获取指定目录下所有图片路径
        imgs = [os.path.join(root, img) for img in os.listdir(root)] 

        # 如果是测试集,按文件名中图片的数字部分排序
        # 测试集文件名格式为 data/test1/8973.jpg
        # 训练集文件名格式为 data/train/cat.10004.jpg 或 data/train/dog.10004.jpg
        if self.test:
            # 对图片路径排序,按文件名中的编号进行排序(适用于测试集)
            imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2].split('/')[-1]))
        else:
            # 对图片路径排序,按编号排序(适用于训练/验证集)
            imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2]))

        imgs_num = len(imgs)  # 图片总数
        
        # 划分数据集:训练集和验证集比例为7:3
        if self.test:
            # 如果是测试集,不做划分,保留所有图片
            self.imgs = imgs
        elif train:
            # 如果是训练集,保留前70%的图片
            self.imgs = imgs[:int(0.7 * imgs_num)]
        else:
            # 如果是验证集,保留后30%的图片
            self.imgs = imgs[int(0.7 * imgs_num):]            
    
        # 如果未指定transforms参数,使用默认的数据预处理
        if transforms is None:
            # 数据归一化,将像素值标准化到特定均值和标准差,通常用在预训练的图像模型上
            normalize = T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

            # 为测试集和验证集指定的预处理操作
            if self.test or not train: 
                self.transforms = T.Compose([
                    T.Resize(224),           # 调整图片大小为224x224
                    T.CenterCrop(224),       # 从中心裁剪224x224的图像
                    T.ToTensor(),            # 将图像转换为张量
                    normalize                # 归一化处理
                ]) 
            # 为训练集指定的数据增强和预处理操作
            else:
                self.transforms = T.Compose([
                    T.Resize(256),           # 调整图片大小为256x256
                    T.RandomResizedCrop(224),# 随机裁剪224x224的图像
                    T.RandomHorizontalFlip(),# 随机水平翻转
                    T.ToTensor(),            # 将图像转换为张量
                    normalize                # 归一化处理
                ]) 
        
    def __getitem__(self, index):
        """
        获取指定索引的数据项

        参数:
        - index: 图片索引

        返回:
        - data: 经过预处理的图像张量
        - label: 标签(训练和验证集返回0或1,表示猫或狗;测试集返回图片ID)

        功能:
        - 加载指定路径的图片
        - 根据文件名判断标签(训练/验证集),或者返回图片ID(测试集)
        - 将图片应用预处理操作后返回
        """
        img_path = self.imgs[index]  # 获取图片路径
        if self.test: 
            # 如果是测试集,标签为图片ID
            label = int(self.imgs[index].split('.')[-2].split('/')[-1])
        else: 
            # 如果是训练/验证集,判断标签(dog为1,cat为0)
            label = 1 if 'dog' in img_path.split('/')[-1] else 0
        data = Image.open(img_path)       # 加载图像
        data = self.transforms(data)      # 应用预处理操作
        return data, label                # 返回图像张量和标签
    
    def __len__(self):
        """
        返回数据集中的图片总数
        """
        return len(self.imgs)  # 返回图片数量

  关于数据集使用的注意事项,在之前就已提到,将文件读取等费时操作放在'__getitem__'函数中,利用多进程加速。另外在这里,我们将数据集中的30%作为验证集,可用来检查模型的训练效果防止过拟合。使用时,我们可以通过dataloader加载数据。

```
train_dataset = DogCat(opt.train_data_root, train=True)
trainloader = DataLoader(train_dataset,
                        batch_size = opt.batch_size,
                        shuffle = True,
                        num_workers = opt.num_workers)
                  
for ii, (data, label) in enumerate(trainloader):
    train()
```

5、模型定义

  模型的定义主要保存在`models/`目录下,其中`BasicModule`是对`nn.Module`的简易封装,提供快速加载和保存模型的接口。

class BasicModule(t.nn.Module):
    """
    封装了nn.Module,主要提供save和load两个方法
    """

    def __init__(self):
        super(BasicModule,self).__init__()
        self.model_name = str(type(self)) # 模型的默认名字

    def load(self, path):
        """
        可加载指定路径的模型
        """
        self.load_state_dict(t.load(path))

    def save(self, name=None):
        """
        保存模型,默认使用“模型名字+时间”作为文件名,
        如AlexNet_0710_23:57:29.pth
        """
        if name is None:
            prefix = 'checkpoints/' + self.model_name + '_'
            name = time.strftime(prefix + '%m%d_%H:%M:%S.pth')
        t.save(self.state_dict(), name)
        return name


  在实际使用中,直接调用`model.save()`及`model.load(opt.load_path)`即可。

  其它自定义模型一般继承`BasicModule`,然后实现自己的模型。其中`AlexNet.py`实现了AlexNet,`ResNet34`实现了ResNet34。在`models/__init__py`中,代码如下:

```
from .AlexNet import AlexNet
from .ResNet34 import ResNet34
```

这样在主函数中就可以写成:

```
from models import AlexNet

import models
model = models.AlexNet()

import models
model = getattr('models', 'AlexNet')()
```

  其中最后一种写法最为关键,这意味着我们可以通过字符串直接指定使用的模型,而不必使用判断语句,也不必在每次新增加模型后都修改代码。新增模型后只需要在`models/__init__.py`中加上`from .new_module import new_module`即可。

  其它关于模型定义的注意事项,总结起来就是:

  • 尽量使用`nn.Sequential`(比如AlexNet)
  • 将经常使用的结构封装成子Module(比如GoogLeNet的Inception结构,ResNet的Residual Block结构)
  • 将重复且有规律性的结构,用函数生成(比如VGG的多种变体,ResNet多种变体都是由多个重复卷积层组成)

6、工具函数

  在项目中,我们可能会用到一些helper方法,这些方法可以统一放在`utils/`文件夹下,需要使用时再引入。在本例中主要是封装了可视化工具visdom的一些操作,其代码如下,在本次实验中只会用到`plot`方法,用来统计损失信息。

import visdom
import time
import numpy as np

class Visualizer(object):
    """
    封装了visdom的基本操作,但是你仍然可以通过`self.vis.function`
    或者`self.function`调用原生的visdom接口
    比如 
    self.text('hello visdom')
    self.histogram(t.randn(1000))
    self.line(t.arange(0, 10),t.arange(1, 11))
    """

    def __init__(self, env='default', **kwargs):
        self.vis = visdom.Visdom(env=env, **kwargs)
        
        # 画的第几个数,相当于横坐标
        # 保存(’loss',23) 即loss的第23个点
        self.index = {} 
        self.log_text = ''
    def reinit(self, env='default', **kwargs):
        """
        修改visdom的配置
        """
        self.vis = visdom.Visdom(env=env, **kwargs)
        return self

    def plot_many(self, d):
        """
        一次plot多个
        @params d: dict (name, value) i.e. ('loss', 0.11)
        """
        for k, v in d.items():
            self.plot(k, v)

    def img_many(self, d):
        for k, v in d.items():
            self.img(k, v)

    def plot(self, name, y, **kwargs):
        """
        self.plot('loss', 1.00)
        """
        x = self.index.get(name, 0)
        self.vis.line(Y=np.array([y]), X=np.array([x]),
                      win=name,
                      opts=dict(title=name),
                      update=None if x == 0 else 'append',
                      **kwargs
                      )
        self.index[name] = x + 1

    def img(self, name, img_, **kwargs):
        """
        self.img('input_img', t.Tensor(64, 64))
        self.img('input_imgs', t.Tensor(3, 64, 64))
        self.img('input_imgs', t.Tensor(100, 1, 64, 64))
        self.img('input_imgs', t.Tensor(100, 3, 64, 64), nrows=10)

        !!! don't ~~self.img('input_imgs', t.Tensor(100, 64, 64), nrows=10)~~ !!!
        """
        self.vis.images(img_.cpu().numpy(),
                       win=name,
                       opts=dict(title=name),
                       **kwargs
                       )

    def log(self, info, win='log_text'):
        """
        self.log({'loss':1, 'lr':0.0001})
        """

        self.log_text += ('[{time}] {info} <br>'.format(
                            time=time.strftime('%m%d_%H%M%S'),\
                            info=info)) 
        self.vis.text(self.log_text, win)   

    def __getattr__(self, name):
        """
        自定义的plot,image,log,plot_many等除外
        self.function 等价于self.vis.function
        """
        return getattr(self.vis, name)

7、配置文件

  在模型定义、数据处理和训练等过程都有很多变量,这些变量应提供默认值,并统一放置在配置文件中,这样在后期调试、修改代码或迁移程序时会比较方便,在这里我们将所有可配置项放在`config.py`中。

class DefaultConfig(object):
    env = 'default' # visdom 环境
    model = 'AlexNet' # 使用的模型,名字必须与models/__init__.py中的名字一致
    
    train_data_root = './data/train/' # 训练集存放路径
    test_data_root = './data/test1' # 测试集存放路径
    load_model_path = 'checkpoints/model.pth' # 加载预训练的模型的路径,为None代表不加载

    batch_size = 128 # batch size
    use_gpu = True # use GPU or not
    num_workers = 4 # how many workers for loading data
    print_freq = 20 # print info every N batch

    debug_file = '/tmp/debug' # if os.path.exists(debug_file): enter ipdb
    result_file = 'result.csv'
      
    max_epoch = 10
    lr = 0.1 # initial learning rate
    lr_decay = 0.95 # when val_loss increase, lr = lr*lr_decay
    weight_decay = 1e-4 # 损失函数

  可配置的参数主要包括:

  • - 数据集参数(文件路径、batch_size等)
  • - 训练参数(学习率、训练epoch等)
  • - 模型参数

  这样我们在程序中就可以这样使用:

```
import models
from config import DefaultConfig

opt = DefaultConfig()
lr = opt.lr
model = getattr(models, opt.model)
dataset = DogCat(opt.train_data_root)
```

这些都只是默认参数,在这里还提供了更新函数,根据字典更新配置参数。

```
def parse(self, kwargs):
        """
        根据字典kwargs 更新 config参数
        """
        # 更新配置参数
        for k, v in kwargs.items():
            if not hasattr(self, k):
                # 警告还是报错,取决于你个人的喜好
                warnings.warn("Warning: opt has not attribut %s" %k)
            setattr(self, k, v)
            
        # 打印配置信息    
        print('user config:')
        for k, v in self.__class__.__dict__.items():
            if not k.startswith('__'):
                print(k, getattr(self, k))
```

这样我们在实际使用时,并不需要每次都修改`config.py`,只需要通过命令行传入所需参数,覆盖默认配置即可。

例如:

```
opt = DefaultConfig()
new_config = {'lr':0.1,'use_gpu':False}
opt.parse(new_config)
opt.lr == 0.1
```

8、main.py

  在讲解主程序`main.py`之前,我们先来看看2017年3月谷歌开源的一个命令行工具`fire`[^3] ,通过`pip install fire`即可安装。下面来看看`fire`的基础用法,假设`example.py`文件内容如下:

import fire

def add(x, y):
  return x + y
  
def mul(**kwargs):
    a = kwargs['a']
    b = kwargs['b']
    return a * b

if __name__ == '__main__':
  fire.Fire()

那么我们可以使用:

```bash
python example.py add 1 2 # 执行add(1, 2)
python example.py mul --a=1 --b=2 # 执行mul(a=1, b=2), kwargs={'a':1, 'b':2}
python example.py add --x=1 --y==2 # 执行add(x=1, y=2)
```

  可见,只要在程序中运行`fire.Fire()`,即可使用命令行参数`python file <function> [args,] {--kwargs,}`。fire还支持更多的高级功能,具体请参考官方指南。

  在主程序`main.py`中,主要包含四个函数,其中三个需要命令行执行,`main.py`的代码组织结构如下:

def train(**kwargs):
    """
    训练
    """
    pass
	 
def val(model, dataloader):
    """
    计算模型在验证集上的准确率等信息,用以辅助训练
    """
    pass

def test(**kwargs):
    """
    测试(inference)
    """
    pass

def help():
    """
    打印帮助的信息 
    """
    print('help')

if __name__=='__main__':
    import fire
    fire.Fire()

8.1、训练

训练的主要步骤如下:

- 定义网络
- 定义数据
- 定义损失函数和优化器
- 计算重要指标
- 开始训练
  - 训练网络
  - 可视化各种指标
  - 计算在验证集上的指标

训练函数的代码如下

训练代码
  def train(**kwargs):
    
    # 根据命令行参数更新配置
    opt.parse(kwargs)
    vis = Visualizer(opt.env)
    
    # step1: 模型
    model = getattr(models, opt.model)()
    if opt.load_model_path:
        model.load(opt.load_model_path)
    if opt.use_gpu: model.cuda()

    # step2: 数据
    train_data = DogCat(opt.train_data_root,train=True)
    val_data = DogCat(opt.train_data_root,train=False)
    train_dataloader = DataLoader(train_data,opt.batch_size,
                        shuffle=True,
                        num_workers=opt.num_workers)
    val_dataloader = DataLoader(val_data,opt.batch_size,
                        shuffle=False,
                        num_workers=opt.num_workers)
    
    # step3: 目标函数和优化器
    criterion = t.nn.CrossEntropyLoss()
    lr = opt.lr
    optimizer = t.optim.Adam(model.parameters(),
                            lr = lr,
                            weight_decay = opt.weight_decay)
        
    # step4: 统计指标:平滑处理之后的损失,还有混淆矩阵
    loss_meter = meter.AverageValueMeter()
    confusion_matrix = meter.ConfusionMeter(2)
    previous_loss = 1e100

    # 训练
    for epoch in range(opt.max_epoch):
        
        loss_meter.reset()
        confusion_matrix.reset()

        for ii,(data,label) in enumerate(train_dataloader):

            # 训练模型参数 
            input = Variable(data)
            target = Variable(label)
            if opt.use_gpu:
                input = input.cuda()
                target = target.cuda()
            optimizer.zero_grad()
            score = model(input)
            loss = criterion(score,target)
            loss.backward()
            optimizer.step()
            
            # 更新统计指标以及可视化
            loss_meter.add(loss.data[0])
            confusion_matrix.add(score.data, target.data)

            if ii%opt.print_freq==opt.print_freq-1:
                vis.plot('loss', loss_meter.value()[0])
                
                # 如果需要的话,进入debug模式
                if os.path.exists(opt.debug_file):
                    import ipdb;
                    ipdb.set_trace()

        model.save()

        # 计算验证集上的指标及可视化
        val_cm,val_accuracy = val(model,val_dataloader)
        vis.plot('val_accuracy',val_accuracy)
        vis.log("epoch:{epoch},lr:{lr},loss:{loss},train_cm:{train_cm},val_cm:{val_cm}"
        .format(
                    epoch = epoch,
                    loss = loss_meter.value()[0],
                    val_cm = str(val_cm.value()),
                    train_cm=str(confusion_matrix.value()),
                    lr=lr))
        
        # 如果损失不再下降,则降低学习率
        if loss_meter.value()[0] > previous_loss:          
            lr = lr * opt.lr_decay
            for param_group in optimizer.param_groups:
                param_group['lr'] = lr
                
        previous_loss = loss_meter.value()[0]

  这里用到了PyTorchNet里面的一个工具: meter。meter提供了一些轻量级的工具,用于帮助用户快速统计训练过程中的一些指标。`AverageValueMeter`能够计算所有数的平均值和标准差,这里用来统计一个epoch中损失的平均值。`confusionmeter`用来统计分类问题中的分类情况,是一个比准确率更详细的统计指标。

  例如对于表格6-1,共有50张狗的图片,其中有35张被正确分类成了狗,还有15张被误判成猫;共有100张猫的图片,其中有91张被正确判为了猫,剩下9张被误判成狗。相比于准确率等统计信息,混淆矩阵更能体现分类的结果,尤其是在样本比例不均衡的情况下

表6-1 混淆矩阵

| 样本   | 判为狗  | 判为猫  |
| ---- | ---- | ---- |
| 实际是狗 | 35   | 15   |
| 实际是猫 | 9    | 91   |

8.2、验证

  验证相对来说比较简单,但要注意需将模型置于验证模式(`model.eval()`),验证完成后还需要将其置回为训练模式(`model.train()`),这两句代码会影响`BatchNorm`和`Dropout`等层的运行模式。验证模型准确率的代码如下。

 

验证代码如下
def val(model, dataloader):
    """
    计算模型在验证集上的准确率等信息
    Args:
        model: 需要验证的模型
        dataloader: 用于提供验证数据的数据加载器

    Returns:
        confusion_matrix: 混淆矩阵,用于展示模型预测结果的详细信息
        accuracy: 模型在验证集上的准确率,表示为百分比
    """

    # 将模型设置为验证模式,使其不进行参数更新(如dropout等层不再启用)
    model.eval()
    
    # 初始化一个混淆矩阵,用于统计模型在二分类(2类)任务上的表现
    confusion_matrix = meter.ConfusionMeter(2)
    
    # 遍历验证数据集中的每个批次的数据
    for ii, data in enumerate(dataloader):
        # 获取当前批次的输入数据和对应的标签
        input, label = data
        
        # 将输入数据和标签包装为torch的Variable类型
        # 设置`volatile=True`表示不需要计算梯度,以提高验证效率
        val_input = Variable(input, volatile=True)
        val_label = Variable(label.long(), volatile=True)
        
        # 如果选择使用GPU,将数据迁移至GPU以加速计算
        if opt.use_gpu:
            val_input = val_input.cuda()
            val_label = val_label.cuda()
        
        # 将输入数据传入模型,获得预测分数
        score = model(val_input)
        
        # 将模型的预测结果和实际标签添加到混淆矩阵中进行统计
        confusion_matrix.add(score.data.squeeze(), label.long())

    # 将模型恢复为训练模式,以便后续继续进行训练
    model.train()
    
    # 获取混淆矩阵的具体值
    cm_value = confusion_matrix.value()
    
    # 根据混淆矩阵计算准确率:
    # 准确率 = (正确预测的样本数 / 总样本数) * 100
    accuracy = 100. * (cm_value[0][0] + cm_value[1][1]) / (cm_value.sum())
    
    return confusion_matrix, accuracy

8.3、测试

  测试时,需要计算每个样本属于狗的概率,并将结果保存成csv文件。测试的代码与验证比较相似,但需要自己加载模型和数据。

测试代码如下
 def test(**kwargs):
    """
    测试模型在测试集上的表现,输出每个样本的预测概率并将结果保存到CSV文件
    Args:
        **kwargs: 用于更新配置项的参数
    Returns:
        results: 包含测试样本路径及其对应预测概率的列表
    """

    # 解析并更新配置项
    opt.parse(kwargs)
    
    # 加载模型并设置为验证模式
    model = getattr(models, opt.model)().eval()
    
    # 如果指定了模型路径,则加载预训练模型参数
    if opt.load_model_path:
        model.load(opt.load_model_path)
    
    # 如果使用GPU,则将模型转移到GPU上
    if opt.use_gpu:
        model.cuda()

    # 加载测试数据集
    train_data = DogCat(opt.test_data_root, test=True)
    test_dataloader = DataLoader(
        train_data,
        batch_size=opt.batch_size,
        shuffle=False,  # 不打乱数据顺序
        num_workers=opt.num_workers  # 设置多线程数据加载的数量
    )
    
    results = []  # 用于存储每个样本的预测结果
    
    # 遍历测试数据集中的每个批次
    for ii, (data, path) in enumerate(test_dataloader):
        # 将数据包装为torch的Variable类型,并设置`volatile=True`以节省计算资源
        input = t.autograd.Variable(data, volatile=True)
        
        # 如果使用GPU,则将数据转移到GPU上
        if opt.use_gpu:
            input = input.cuda()
        
        # 通过模型进行前向传播,获取预测分数
        score = model(input)
        
        # 计算每个样本属于类别1的概率,并转换为列表形式
        probability = t.nn.functional.softmax(score)[:, 1].data.tolist()
        
        # 将每个样本的文件路径和预测概率组合为一组结果
        batch_results = [(path_, probability_) for path_, probability_ in zip(path, probability)]
        
        # 将当前批次的结果添加到总结果列表中
        results += batch_results
    
    # 将预测结果写入CSV文件
    write_csv(results, opt.result_file)
    
    return results  # 返回包含文件路径和预测概率的结果列表

8.4、帮助函数

  为了方便他人使用, 程序中还应当提供一个帮助函数,用于说明函数是如何使用。程序的命令行接口中有众多参数,如果手动用字符串表示不仅复杂,而且后期修改config文件时,还需要修改对应的帮助信息,十分不便。这里使用了Python标准库中的inspect方法,可以自动获取config的源代码。help的代码如下:

def help():
    """
    打印帮助信息,展示如何使用脚本和可用的参数选项。
    用法示例: python file.py help
    """

    # 打印基本的使用方法,包括脚本调用和支持的功能
    print("""
    usage : python {0} <function> [--args=value,]
    <function> := train | test | help
    example: 
            python {0} train --env='env0701' --lr=0.01
            python {0} test --dataset='path/to/dataset/root/'
            python {0} help
    available args:""".format(__file__))  # 使用 __file__ 获取当前文件名

    # 获取并打印配置类 opt 中所有的参数定义
    # 使用 inspect 模块的 getsource 函数以源码形式显示配置类 opt 的参数
    from inspect import getsource
    source = (getsource(opt.__class__))
    print(source)

  当用户执行`python main.py help`的时候,会打印如下帮助信息:

```bash
    usage : python main.py <function> [--args=value,]
    <function> := train | test | help
    example: 
            python main.py train --env='env0701' --lr=0.01
            python main.py test --dataset='path/to/dataset/'
            python main.py help
    avaiable args:
class DefaultConfig(object):
    env = 'default' # visdom 环境
    model = 'AlexNet' # 使用的模型
    
    train_data_root = './data/train/' # 训练集存放路径
    test_data_root = './data/test1' # 测试集存放路径
    load_model_path = 'checkpoints/model.pth' # 加载预训练的模型

    batch_size = 128 # batch size
    use_gpu = True # user GPU or not
    num_workers = 4 # how many workers for loading data
    print_freq = 20 # print info every N batch

    debug_file = '/tmp/debug' 
    result_file = 'result.csv' # 结果文件
      
    max_epoch = 10
    lr = 0.1 # initial learning rate
    lr_decay = 0.95 # when val_loss increase, lr = lr*lr_decay
    weight_decay = 1e-4 # 损失函数
```

9、使用

  正如`help`函数的打印信息所述,可以通过命令行参数指定变量名.下面是三个使用例子,fire会将包含`-`的命令行参数自动转层下划线`_`,也会将非数值的值转成字符串。所以`--train-data-root=data/train`和`--train_data_root='data/train'`是等价的。

```
# 训练模型
python main.py train 
        --train-data-root=data/train/ 
        --lr=0.005 
        --batch-size=32 
        --model='ResNet34'  
        --max-epoch = 20

# 测试模型
python main.py test
       --test-data-root=data/test1 
       --load-model-path='checkpoints/resnet34_00:23:05.pth' 
       --batch-size=128 
       --model='ResNet34' 
       --num-workers=12

# 打印帮助信息
python main.py help
```

标签:实战,opt,self,py,Pytorch,train,__,data
From: https://www.cnblogs.com/pgl6/p/18530883

相关文章

  • 某大型商超客户采购数据分析(Spark实战)
    写了一些使用sparksql以及spark机器学习来进行数据分析的东西,希望能给大家做一些参考项目需求:对某大型商超客户采购数据集进行数据分析数据来源:https://www.heywhale.com/mw/dataset/656069b19a74cc18269207c4/content首先使用Spark读入数据集,读入文件前要先将文件转为csv格......
  • PyTorch 手写字符识别
    PyTorch手写字符识别我们使用MNIST数据集对建立的卷积神经网络进行了训练,并加载测试集进行测试,最终的识别精度达到了99%。但是官方和网上的测试流程只是演示最终的测试结果,没有很直观的告诉我们怎么在项目中使用他。我们学习机器学习和人工智能的目的不是跑一个官网的演示程......
  • 【Pytorch】神经网络介绍|激活函数|使用pytorch搭建方法
    神经网络神经网络介绍概念神经网络人工神经网络ANN也称神经网络NN是一种模仿生物神经网络结构和功能的计算模型人脑可以看作是一个生物神经网络,由众多神经元连接而成,神经网络可以看作是模拟生物神经元的过程输入层inputLayer:输入x的那一层输出层output......
  • python-三方库-PyTorch-Pillow (PIL Fork)
    1需求官网:https://python-pillow.org/docs:https://pillow.readthedocs.io/en/stable/需求:Image.filename需求:获取图像格式Image.format需求:获取图像尺寸Image.sizeImage.widthImage.height需求:获取图像颜色模式Image.mode需求:获取图像像素值Image.load()[x,y]......
  • pytorch中的ImageFolder 用法
    ImageFolder是PyTorch中torchvision.datasets模块提供的一个常用类,用于从文件夹中加载图像数据。它是一种非常方便的方式来加载按文件夹结构组织的图像数据集。这个类能够自动将文件夹中的子目录作为标签,并且将其中的图像文件加载为PyTorch张量。1.基本概念ImageF......
  • .NET 8 强大功能 IHostedService 与 BackgroundService 实战
    前言在.NET8中,IHostedService和BackgroundService两个核心接口的引入,增强了项目开发中处理定时任务的能力。这两个接口不仅简化了定时任务、后台处理作业以及定期维护任务的实现过程,还提升了在ASP.NETCore或任何基于.NET的宿主应用程序中的集成与管理效率。IHostedService......
  • 大模型神书《HuggingFace自然语言处理详解——基于BERT中文模型的任务实战》读完少走
    这几年,自然语言处理(NLP)绝对是机器学习领域最火的方向。那么今天给大家带来一本《HuggingFace自然语言处理详解——基于BERT中文模型的任务实战》这本大模型书籍资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】★内容全面本......
  • HarmonyOS NEXT开发实战教程-记账app
    今天分享的实战教程是一款记账app,最近分享的项目都是纯页面,没有服务端,没有数据接口,因为鸿蒙开发主要就是写页面,都是前端嘛。如果有友友想要完整的项目可以找幽蓝君定制,想学服务端开发的话幽蓝君也可以写。话不多说,看一下记账app的效果图: 下面为大家分享本项目的开发教程,其实......
  • 鸿蒙开发实战:深度解析网络管理技巧与实战应用
    在鸿蒙项目开发中,网络管理扮演着举足轻重的角色。本文将深入剖析鸿蒙网络管理的核心技术,帮助开发者精准把握网络状态,打造流畅且用户友好的应用体验。在鸿蒙应用中,实时监测网络状态是确保应用稳定性和用户体验的关键。网络状态的变化,如从Wi-Fi切换到移动数据,或从有网络状态变为......
  • R语言使用caret包构建岭回归模型实战,构建回归模型、通过method参数指定算法名称、通过
    R语言使用caret包构建岭回归模型实战,构建回归模型、通过method参数指定算法名称、通过trainControl函数控制训练过程目录R语言使用caret包构建岭回归模型(RidgeRegression )构建回归模型、通过method参数指定算法名称、通过trainControl函数控制训练过程 #导入包和库#仿......