text: import pickle
  import time
  import argparse
  import os
  import os.path as osp
  import numpy as np
  import torch
  import torch.backends.cudnn as cudnn
  import torch_geometric.transforms as T
  from psbody.mesh import Mesh
  
  from classification import Classifier, run
  from datasets import ClsfData
  from utils import utils, writer, DataLoader, mesh_sampling
  
  parser = argparse.ArgumentParser(description='mesh classifier')
  parser.add_argument('--exp_name', type=str, default='clsf_raw_seq16_wd0.01')
  parser.add_argument('--dataset', type=str, default='ADNI2_raw')
  parser.add_argument('--split', type=str, default='clsfb')
  parser.add_argument('--n_threads', type=int, default=4)
  parser.add_argument('--device_idx', type=int, default=-1)
  
  # network hyperparameters
  parser.add_argument('--out_channels',
                      nargs='+',
                      default=[32, 32, 32, 64],
                      type=int)
  parser.add_argument('--latent_channels', type=int, default=16)
  parser.add_argument('--in_channels', type=int, default=3)
  parser.add_argument('--seq_length', type=int, default=[9, 9, 9, 9], nargs='+')
  parser.add_argument('--dilation', type=int, default=[1, 1, 1, 1], nargs='+')
  
  # optimizer hyperparmeters
  parser.add_argument('--optimizer', type=str, default='Adam')
  parser.add_argument('--lr', type=float, default=1e-3)
  parser.add_argument('--lr_decay', type=float, default=0.99)
  parser.add_argument('--decay_step', type=int, default=1)
  parser.add_argument('--weight_decay', type=float, default=0)
  
  # training hyperparameters
  parser.add_argument('--batch_size', type=int, default=32)
  parser.add_argument('--epochs', type=int, default=300)
  
  # others
  parser.add_argument('--seed', type=int, default=1)
  
  args = parser.parse_args()
  
  args.work_dir = osp.dirname(osp.realpath(__file__))
  args.data_fp = osp.join(args.work_dir, '..', 'data', args.dataset)
  args.out_dir = osp.join(args.work_dir, 'out', args.exp_name)
  args.checkpoints_dir = osp.join(args.out_dir, 'checkpoints')
  print(args)
  
  utils.makedirs(args.out_dir)
  utils.makedirs(args.checkpoints_dir)
  
  writer = writer.Writer(args)
  device = torch.device('cuda', args.device_idx) if torch.cuda.is_available() else torch.device('cpu')
  torch.set_num_threads(args.n_threads)
  
  # deterministic
  torch.manual_seed(args.seed)
  cudnn.benchmark = False
  cudnn.deterministic = True
  
  # load dataset
  template_fp = osp.join(args.data_fp, 'template', 'template.obj')
  meshdata = ClsfData(args.data_fp,
                      template_fp,
                      split=args.split)
  train_loader = DataLoader(meshdata.train_dataset,
                            batch_size=args.batch_size,
                            shuffle=True)
  test_loader = DataLoader(meshdata.test_dataset, batch_size=args.batch_size)
  
  # generate/load transform matrices
  transform_fp = osp.join(args.data_fp, 'transform.pkl')
  if not osp.exists(transform_fp):
      print('Generating transform matrices...')
      mesh = Mesh(filename=template_fp)
      ds_factors = [2, 2, 2, 2]
      _, A, D, U, F, V = mesh_sampling.generate_transform_matrices(
          mesh, ds_factors)
      tmp = {
          'vertices': V,
          'face': F,
          'adj': A,
          'down_transform': D,
          'up_transform': U
      }
  
      with open(transform_fp, 'wb') as fp:
          pickle.dump(tmp, fp)
      print('Done!')
      print('Transform matrices are saved in \'{}\''.format(transform_fp))
  else:
      with open(transform_fp, 'rb') as f:
          tmp = pickle.load(f, encoding='latin1')
  
  spiral_indices_list = [
      utils.preprocess_spiral(tmp['face'][idx], args.seq_length[idx],
                              tmp['vertices'][idx],
                              args.dilation[idx]).to(device)
      for idx in range(len(tmp['face']) - 1)
  ]
  down_transform_list = [
      utils.to_sparse(down_transform).to(device)
      for down_transform in tmp['down_transform']
  ]
  up_transform_list = [
      utils.to_sparse(up_transform).to(device)
      for up_transform in tmp['up_transform']
  ]
  
  model = Classifier(args.in_channels, args.out_channels, args.latent_channels,
             spiral_indices_list, down_transform_list,
             up_transform_list).to(device)
  print('Number of parameters: {}'.format(utils.count_parameters(model)))
  print(model)
  
  optimizer = torch.optim.Adam(model.parameters(),
                               lr=args.lr,
                               weight_decay=args.weight_decay)
  scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
                                              args.decay_step,
                                              gamma=args.lr_decay)
  t = time.time()
  run(model, train_loader, test_loader, args.epochs, optimizer, scheduler,
      writer, device)
  t_duration = time.time() - t
  print('total time : {:.3f}s'.format(t_duration))
  