CenterFusion/src/main.py 项目训练执行文件详解

目录

  • 一、train.sh 脚本
  • 二、main.py 文件
  • 三、参考资料

文件位置:CenterFusion/experiments/train.shCenterFusion/src/main.py
文件作用:CenterFusion 项目训练的执行过程
注意:本文中的代码都是 CenterFusion 原始代码,一些参数没有修改

一、train.sh 脚本

  • 在 README.md 中训练模型的命令是:bash experiments/train.sh
  • 首先执行的就是 train.sh 脚本
  • 在脚本中 --参数 值 表示可选参数
cd src
python main.py \
    ddd \
    '''
    可以理解为工作空间名,也就是文件夹名
    '''
    --exp_id centerfusion \
    '''
    项目名称
    '''
    --shuffle_train \
    '''
    将训练模型的数据集进行打乱的操作
    '''
    --train_split train \
    '''
    训练集
    '''
    --val_split mini_val \
    '''
    测试集
    '''
    --val_intervals 1 \
    '''
    运行测试集的 epoch 数为 1
    比如 1000 个样本用来训练一个神经网络,训练就算完成 1 epoch(期)
    '''
    --run_dataset_eval \
    '''
    在 eval 中使用数据集特定的计算函数
    '''
    --nuscenes_att \
    --velocity \
    --batch_size 32 \
    '''
    一次训练抓取 32 个数据样本
    '''
    --lr 2.5e-4 \
    '''
    学习率:0.00025
    '''
    --num_epochs 60 \
    '''
    训练 60 轮
    '''
    --lr_step 50 \
    '''
    学习率步长
    '''
    --save_point 20,40,50 \
    '''
    模型保存时间点
    '''
    --gpus 0,1 \
    '''
    这里使用了两块 GPU :0 号 GPU 和 1 号 GPU
    '''
    --not_rand_crop \
    '''
    不使用来自 CenterNet 的随机裁剪数据增强
    '''
    --flip 0.5 \
    '''
    使用翻转数据增强的概率 50%
    '''
    --shift 0.1 \
    '''
    当不使用随机裁剪时,10% 概率使用移位增强
    '''
    --pointcloud \
    '''
    雷达点云
    '''
    --radar_sweeps 3 \
    '''
    点云图中雷达扫瞄 3 次
    '''
    --pc_z_offset 0.0 \
    '''
    向 z 轴方面提高雷达点
    '''
    --pillar_dims 1.0,0.2,0.2 \
    '''
    雷达柱尺寸(h、w、l)
    '''
    --max_pc_dist 60.0 \
    '''
    移除最大点云距离 60 以外的点
    '''
    --load_model ../models/centernet_baseline_e170.pth \
    '''
    导入的模型
    '''
cd ..

二、main.py 文件

  • 在 train.sh 脚本中执行了 src 下的 main.py 文件,并传递了一些参数
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import _init_paths
import os

import torch
import torch.utils.data
from opts import opts
from model.model import create_model, load_model, save_model
from model.data_parallel import DataParallel
from logger import Logger
from dataset.dataset_factory import get_dataset
from trainer import Trainer
from test import prefetch_test
import json

def get_optimizer(opt, model):

  if opt.optim == 'adam':
    optimizer = torch.optim.Adam(model.parameters(), opt.lr)
    '''
    执行该 if 语句,其中 torch.optim.Adam 是实现 Adam 算法的函数
    '''

  elif opt.optim == 'sgd':
    print('Using SGD')
    optimizer = torch.optim.SGD(
      model.parameters(), opt.lr, momentum=0.9, weight_decay=0.0001)
  else:
    assert 0, opt.optim

  return optimizer
  '''
  返回优化器
  '''
  • 首先查看文件的最末尾,最开始执行这段代码
if __name__ == '__main__':
  opt = opts().parse()
  '''
  调用 opts 类中的 parse() 函数
  这个函数在 CenterFusion/src/lib/opts.py 第 305 行
  '''
  
  main(opt)
  '''
  执行 main() 函数
  '''
  • 注意:这里我将 opts.py 文件中一些必要代码进行注释,可以参考博客:CenterFusion/src/lib/opts.py 文件代码详解
  • 然后执行 main() 函数中的代码
def main(opt):

  torch.manual_seed(opt.seed)
  '''
  这里引用了 torch 库内的函数 manual_seed() :设置 CPU 生成随机数种子
  seed 在 opts.py 文件第 50 行定义,默认值为 317
  '''

  torch.backends.cudnn.benchmark = not opt.not_cuda_benchmark and not opt.eval
  '''
  参数 not_cuda_benchmark 在 opts.py 第 48 行,含义:不是cuda基准
    train.sh 脚本中没有添加 --not_cuda_benchmark 参数,所以 opt.not_cuda_benchmark 值为 False
  参数 eval 在 opts.py 第 22 行,含义:只评估测试集 mini_val 并退出
    train.sh 脚本中没有添加 --eval 参数,所以 opt.eval 值为 False
  综上得出,torch.backends.cudnn.benchmark = True
  设置为 True 会让程序在开始时花费一点额外时间,使用 cudnn 为整个网络的每个卷积层搜索最适合它的卷积实现算法,进而实现网络的加速
  '''

  Dataset = get_dataset(opt.dataset)
  '''
  参数 dataset 在 opts.py 第 16 行,含义:设置默认数据集 nuscenes
  get_dataset() 函数在 CenterFusion/src/lib/dataset/dataset_factory.py 第 32 行
  返回一个 nuScenes 对象
  这个 nuScenes 对象定义在 CenterFusion/src/lib/dataset/datasets/nuscenes.py 第 24 行
  '''

  opt = opts().update_dataset_info_and_set_heads(opt, Dataset)
  '''
  更新一些配置信息
  update_dataset_info_and_set_heads() 函数在 opys.py 第 458 行
  '''

  if not opt.not_set_cuda_env:
    os.environ['CUDA_VISIBLE_DEVICES'] = opt.gpus_str
  '''
  opt.not_set_cuda_env 在脚本中没有添加这个参数,所以值为 False,再用 not 取反,则为 True
  gpus_str 是 opt 中的一个 GPU 索引号字符串,如:'0,1'
  这里是为了给系统添加 cuda 索引号
  '''

  opt.device = torch.device('cuda' if opt.gpus[0] >= 0 else 'cpu')
  '''
  设置模型要分配的位置
  '''

  logger = Logger(opt)
  '''
  新建一个 Logger 对象,保存 opt 的配置信息
  Logger 对象在 CenterFusion/src/lib/logger.py 
  '''

  print('Creating model...')
  model = create_model(opt.arch, opt.heads, opt.head_conv, opt=opt)
  '''
  创建 DLA 模型(CenterNet 中的一种)
  参数:
      arch :网络结构名称
      heads :网络的头部
      head_conv :头部输出的通道个数
  create_model 函数在 CenterFusion/src/lib/model/model.py 第 24 行
  '''
  • 注意:这里我将 model.py 文件中一些必要代码进行注释,可以参考博客:CenterFusion/src/lib/model/model.py 文件代码详解
  optimizer = get_optimizer(opt, model)
  '''
  根据超参数和模型定义优化器
  '''

  start_epoch = 0
  '''
  设定初始轮次
  '''

  lr = opt.lr
  '''
  获取设定的学习率:0.00025
  '''

  if opt.load_model != '':
    model, optimizer, start_epoch = load_model(
      model, opt.load_model, opt, optimizer)
  '''
  如果加载的预训练模型不为空,则加载模型
  返回加载后的模型、优化器、初始轮次
  load_model() 函数在 CenterFusion/src/lib/model/model.py 第 31 行
  '''

  trainer = Trainer(opt, model, optimizer)
  '''
  根据参数、模型、优化器得到对应任务的训练器(其中包括损失统计、模型损失等)
  Trainer 类在 CenterFusion/src/lib/trainer.py 第 129 行
  '''

  trainer.set_device(opt.gpus, opt.chunk_sizes, opt.device)
  '''
  将训练器加载到 GPU 上
  路径 CenterFusion/src/lib/trainer.py 第 137 行
  '''

  if opt.val_intervals < opt.num_epochs or opt.eval:
    '''
    val_intervals=1 < num_epochs=60 执行该 if 语句
    '''

    print('Setting up validation data...')
    val_loader = torch.utils.data.DataLoader(
      Dataset(opt, opt.val_split), batch_size=1, shuffle=False, 
              num_workers=1, pin_memory=True)
    '''
    加载测试集数据
    torch.utils.data.DataLoader 是一个数据读取的一个接口,参数:
      dataset (Dataset):加载数据的数据集
      batch_size (int, optional):每个 batch 加载多少个样本(默认: 1)
      shuffle (bool, optional):设置为 True 时会在每个 epoch 重新打乱数据(默认: False)
      num_workers (int, optional):用多少个子进程加载数据。0 表示数据将在主进程中加载(默认: 0)
      pin_memory (bool, optional):设置 pin_memory=True,则意味着生成的 Tensor 数据最开始是属于内存中的锁页内存,
                                 这样将内存的 Tensor 转义到 GPU 的显存就会更快一些
    '''

    if opt.eval:
      '''
      如果是测试,则执行,训练时,该 if 语句没有执行
      '''

      _, preds = trainer.val(0, val_loader)
      '''
      路径 CenterFusion/src/lib/trainer.py 第 402 行
      '''

      val_loader.dataset.run_eval(preds, opt.save_dir, n_plots=opt.eval_n_plots, 
                                  render_curves=opt.eval_render_curves)
      '''
      进行结果评判
      '''

      return

  print('Setting up train data...')

  train_loader = torch.utils.data.DataLoader(
      Dataset(opt, opt.train_split), batch_size=opt.batch_size, 
        shuffle=opt.shuffle_train, num_workers=opt.num_workers, 
        pin_memory=True, drop_last=True
  )
  '''
  加载训练集
  drop_last (bool, optional) – 如果数据集大小不能被 batch size 整除,则设置为 True 后可删除最后一个不完整的 batch。
  如果设为 False 并且数据集的大小不能被 batch size 整除,则最后一个 batch 将更小。(默认: False)
  '''
  print('Starting training...')
  for epoch in range(start_epoch + 1, opt.num_epochs + 1):
    '''
    循环 60 次,训练 60 轮
    '''

    mark = epoch if opt.save_all else 'last'
    '''
    是否每 5 个 epoch 保存模型到磁盘
    由于 train.sh 中没有添加 save_all 参数,所以为 False
    最后 mark = 'last',意为最后再保存模型到磁盘中
    '''

    for param_group in optimizer.param_groups:
      lr = param_group['lr']
      logger.scalar_summary('LR', lr, epoch)
      break
    '''
    记录学习率
    scalar_summary() 函数在 CenterFusion/src/lib/logger.py 第 73 行
    '''
    
    log_dict_train, _ = trainer.train(epoch, train_loader)
    logger.write('epoch: {} |'.format(epoch))
    '''
    训练一轮模型,返回 ret 和 result
    这里 log_dict_train = ret
    train() 函数在 CenterFusion/src/lib/trainer.py 第 405 行
    '''
  • 训练过程请参考:CenterFusion/src/lib/trainer.py 训练一个 epoch 过程

    for k, v in log_dict_train.items():
      logger.scalar_summary('train_{}'.format(k), v, epoch)
      logger.write('{} {:8f} | '.format(k, v))
    '''
    items() 函数:将一个字典以列表的形式返回,因为字典是无序的,所以返回的列表也是无序的
    记录训练一次返回的结果
    '''
    
    if opt.val_intervals > 0 and epoch % opt.val_intervals == 0:
      '''
      val_intervals 的值为 1
      epoch % opt.val_intervals 的值始终为 0
      所以每训练一个 epoch 都要使用测试集进行一次测试
      '''

      save_model(os.path.join(opt.save_dir, 'model_{}.pth'.format(mark)), 
                 epoch, model, optimizer)
      '''
      保存模型,下一轮训练好后又会覆盖上一轮的模型
      save_model() 函数在 CenterFusion/src/lib/model/model.py 第 117 行
      其中参数分别为保存路径、训练轮次、训练模型、优化器
      最后保存的模型放在 ~/CenterFusion/src/lib/../../exp/ddd/centerfusion 文件夹下,后缀名为 .pth
      '''

      with torch.no_grad():
        '''
        with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源
        比如文件使用后自动关闭/线程中锁的自动获取和释放等
        with torch.no_grad(): 强制后面不进行计算图(计算过程的构建,以便梯度反向传播等操作)的构建
        '''

        log_dict_val, preds = trainer.val(epoch, val_loader)
        '''
        对当前轮次训练后的模型进行测试
        '''
        
        if opt.run_dataset_eval:
          '''
          run_dataset_eval 在 train.sh 中添加了该参数,所以值为 True
          执行该 if 语句
          '''

          out_dir = val_loader.dataset.run_eval(preds, opt.save_dir, 
                                                n_plots=opt.eval_n_plots, 
                                                render_curves=opt.eval_render_curves)
          '''
          对测试结果评估
          '''
          
          with open('{}/metrics_summary.json'.format(out_dir), 'r') as f:
            metrics = json.load(f)
          logger.scalar_summary('AP/overall', metrics['mean_ap']*100.0, epoch)
          for k,v in metrics['mean_dist_aps'].items():
            logger.scalar_summary('AP/{}'.format(k), v*100.0, epoch)
          for k,v in metrics['tp_errors'].items():
            logger.scalar_summary('Scores/{}'.format(k), v, epoch)
          logger.scalar_summary('Scores/NDS', metrics['nd_score'], epoch)
          '''
          记录测试数据集的评估指标
          '''
      
      for k, v in log_dict_val.items():
        logger.scalar_summary('val_{}'.format(k), v, epoch)
        logger.write('{} {:8f} | '.format(k, v))
      '''
      记录测试结果
      '''
    
    #保存这个检查点
    else:
      save_model(os.path.join(opt.save_dir, 'model_last.pth'), 
                 epoch, model, optimizer)

    logger.write('\n')
    if epoch in opt.save_point:
      save_model(os.path.join(opt.save_dir, 'model_{}.pth'.format(epoch)), 
                 epoch, model, optimizer)
    '''
    save_point 在 train.sh 中的值为 20,40,50
    所以当 epoch 为 20,40,50 时保存模型
    '''
    
    if epoch in opt.lr_step:
      '''
      lr_step 在 train.sh 中的值为 50
      那么 epoch = 50 轮次时,更新学习率 lr
      '''
      lr = opt.lr * (0.1 ** (opt.lr_step.index(epoch) + 1))
      print('Drop LR to', lr)
      for param_group in optimizer.param_groups:
          param_group['lr'] = lr

  logger.close()

三、参考资料

[1] pytorch中tensorboard的add_scalar与add_image

你可能感兴趣的:(深度学习,python)