text: import copy
  
  import chainer
  from chainer.optimizer import WeightDecay
  from chainer import serializers
  from chainer import training
  from chainer.training import extensions
  from chainer.training import triggers
  from chainer.datasets import TransformDataset
  
  from chainercv.extensions import DetectionVOCEvaluator
  from chainercv.links.model.ssd import GradientScaling
  from chainercv.links.model.ssd import multibox_loss
  from chainercv.links import SSD300
  
  from chainercv import transforms
  from chainercv.links.model.ssd import random_crop_with_bbox_constraints
  from chainercv.links.model.ssd import random_distort
  from chainercv.links.model.ssd import resize_with_random_interpolation
  
  
  class MultiboxTrainChain(chainer.Chain):
  
      def __init__(self, model, alpha=1, k=3):
          super(MultiboxTrainChain, self).__init__()
          with self.init_scope():
              self.model = model
          self.alpha = alpha
          self.k = k
  
      def __call__(self, imgs, gt_mb_locs, gt_mb_labels):
          mb_locs, mb_confs = self.model(imgs)
          loc_loss, conf_loss = multibox_loss(
              mb_locs, mb_confs, gt_mb_locs, gt_mb_labels, self.k)
          loss = loc_loss * self.alpha + conf_loss
  
          chainer.reporter.report(
              {'loss': loss, 'loss/loc': loc_loss, 'loss/conf': conf_loss},
              self)
  
          return loss
  
  
  class Transform(object):
  
      def __init__(self, coder, size, mean):
          # to send cpu, make a copy
          self.coder = copy.copy(coder)
          self.coder.to_cpu()
  
          self.size = size
          self.mean = mean
  
      def __call__(self, in_data):
          # There are five data augmentation steps
          # 1. Color augmentation
          # 2. Random expansion
          # 3. Random cropping
          # 4. Resizing with random interpolation
          # 5. Random horizontal flipping
  
          img, bboxes, labels = in_data
  
          # 1. Color augmentation
          img = random_distort(img)
  
          # 2. Random expansion
          #if np.random.randint(2):
          img, param = transforms.random_expand(
              img, fill=self.mean, return_param=True)
          bboxes = transforms.translate_bbox(
              bboxes, y_offset=param['y_offset'], x_offset=param['x_offset'])
  
          # 3. Random cropping
          img, param = random_crop_with_bbox_constraints(
              img, bboxes, return_param=True)
          bboxes, param = transforms.crop_bbox(
              bboxes, y_slice=param['y_slice'], x_slice=param['x_slice'],
              allow_outside_center=False, return_param=True)
          labels = labels[param['index']]
  
          # 4. Resizing with random interpolatation
          _, H, W = img.shape
          img = resize_with_random_interpolation(img, (self.size, self.size))
          bboxes = transforms.resize_bbox(bboxes, (H, W), (self.size, self.size))
  
          # 5. Random horizontal flipping
          img, params = transforms.random_flip(
              img, x_random=True, return_param=True)
          bboxes = transforms.flip_bbox(
              bboxes, (self.size, self.size), x_flip=params['x_flip'])
  
          # Preparation for SSD network
          img -= self.mean
          mb_loc, mb_label = self.coder.encode(bboxes, labels)
  
          return img, mb_loc, mb_label
  
  
  def train(train_data, val_data, label_names,
            iteration, lr, step_points,
            batchsize, gpu, out, val_iteration,
            log_iteration, loaderjob,
            resume):
  
      model = SSD300(n_fg_class=len(label_names), pretrained_model='imagenet')
      model.use_preset('evaluate')
  
      train_chain = MultiboxTrainChain(model)  # alpha and k?
  
      if gpu >= 0:
          chainer.cuda.get_device_from_id(gpu).use()
          model.to_gpu()
  
      train_data = TransformDataset(
          train_data,
          Transform(model.coder, model.insize, model.mean))
      if loaderjob <= 0:
          train_iter = chainer.iterators.SerialIterator(train_data, batchsize)
      else:
          train_iter = chainer.iterators.MultiprocessIterator(train_data, batchsize)  # , n_processes=min((loaderjob, batchsize)))
  
      val_iter = chainer.iterators.SerialIterator(val_data, batchsize, repeat=False, shuffle=False)  # think about repeat
  
      # initial lr is set to 1e-4 (default run_train) by ExponentialShift
      optimizer = chainer.optimizers.MomentumSGD()
      optimizer.setup(train_chain)
      for param in train_chain.params():
          if param.name == 'b':
              param.update_rule.add_hook(GradientScaling(2))
          else:
              param.update_rule.add_hook(WeightDecay(0.0005))
  
      updater = training.StandardUpdater(train_iter, optimizer, device=gpu)
      trainer = training.Trainer(updater, (iteration, 'iteration'), out)
      trainer.extend(
          extensions.ExponentialShift('lr', 0.1, init=lr),
          trigger=triggers.ManualScheduleTrigger(step_points, 'iteration'))
  
      val_interval = (val_iteration, 'iteration')
      trainer.extend(
          DetectionVOCEvaluator(
              val_iter, model, use_07_metric=True,
              label_names=label_names),
          trigger=val_interval)
  
      ###
      # Logging
      log_interval = log_iteration, 'iteration'
      trainer.extend(extensions.LogReport(trigger=log_interval))
      trainer.extend(extensions.observe_lr(), trigger=log_interval)
      trainer.extend(extensions.PrintReport(
          ['epoch', 'iteration', 'lr',
           'main/loss', 'main/loss/loc', 'main/loss/conf',
           'validation/main/map']),
          trigger=log_interval)
      trainer.extend(extensions.ProgressBar(update_interval=10))
  
      trainer.extend(extensions.snapshot(), trigger=val_interval)
      trainer.extend(
          extensions.snapshot_object(model, 'model_iter_{.updater.iteration}'),
          trigger=val_interval)
      ###
  
      if resume:
          serializers.load_npz(resume, trainer)
  
      trainer.run()
  