text: import numpy as np
  import tensorflow as tf
  import keras.backend as K
  from keras.layers import Input, Lambda
  from keras.models import Model
  from keras.optimizers import Adam
  from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping
  from nets.yolo3 import yolo_body
  from nets.loss import yolo_loss
  from keras.backend.tensorflow_backend import set_session
  from utils.utils import get_random_data
  
  
  #---------------------------------------------------#
  #   获得类和先验框
  #---------------------------------------------------#
  def get_classes(classes_path):
      '''loads the classes'''
      with open(classes_path) as f:
          class_names = f.readlines()
      class_names = [c.strip() for c in class_names]
      return class_names
  
  def get_anchors(anchors_path):
      '''loads the anchors from a file'''
      with open(anchors_path) as f:
          anchors = f.readline()
      anchors = [float(x) for x in anchors.split(',')]
      return np.array(anchors).reshape(-1, 2)
  
  #---------------------------------------------------#
  #   训练数据生成器
  #---------------------------------------------------#
  def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):
      '''data generator for fit_generator'''
      n = len(annotation_lines)
      i = 0
      while True:
          image_data = []
          box_data = []
          for b in range(batch_size):
              if i==0:
                  np.random.shuffle(annotation_lines)
              image, box = get_random_data(annotation_lines[i], input_shape, random=True)
              image_data.append(image)
              box_data.append(box)
              i = (i+1) % n
          image_data = np.array(image_data)
          box_data = np.array(box_data)
          y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)
          yield [image_data, *y_true], np.zeros(batch_size)
  
  
  #---------------------------------------------------#
  #   读入xml文件，并输出y_true
  #---------------------------------------------------#
  def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
  
      assert (true_boxes[..., 4]<num_classes).all(), 'class id must be less than num_classes'
      # 一共有三个特征层数
      num_layers = len(anchors)//3
      # 先验框
      # 678为116,90,  156,198,  373,326
      # 345为30,61,  62,45,  59,119
      # 012为10,13,  16,30,  33,23,  
      anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]
  
      true_boxes = np.array(true_boxes, dtype='float32')
      input_shape = np.array(input_shape, dtype='int32') # 416,416
      # 读出xy轴，读出长宽
      # 中心点(m,n,2)
      boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2
      boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]
      # 计算比例
      true_boxes[..., 0:2] = boxes_xy/input_shape[:]
      true_boxes[..., 2:4] = boxes_wh/input_shape[:]
  
      # m张图
      m = true_boxes.shape[0]
      # 得到网格的shape为13,13;26,26;52,52
      grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(num_layers)]
      # y_true的格式为(m,13,13,3,85)(m,26,26,3,85)(m,52,52,3,85)
      y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes),
          dtype='float32') for l in range(num_layers)]
      # [1,9,2]
      anchors = np.expand_dims(anchors, 0)
      anchor_maxes = anchors / 2.
      anchor_mins = -anchor_maxes
      # 长宽要大于0才有效
      valid_mask = boxes_wh[..., 0]>0
  
      for b in range(m):
          # 对每一张图进行处理
          wh = boxes_wh[b, valid_mask[b]]
          if len(wh)==0: continue
          # [n,1,2]
          wh = np.expand_dims(wh, -2)
          box_maxes = wh / 2.
          box_mins = -box_maxes
  
          # 计算真实框和哪个先验框最契合
          intersect_mins = np.maximum(box_mins, anchor_mins)
          intersect_maxes = np.minimum(box_maxes, anchor_maxes)
          intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)
          intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
          box_area = wh[..., 0] * wh[..., 1]
          anchor_area = anchors[..., 0] * anchors[..., 1]
          iou = intersect_area / (box_area + anchor_area - intersect_area)
          # 维度是(n) 感谢 消尽不死鸟 的提醒
          best_anchor = np.argmax(iou, axis=-1)
  
          for t, n in enumerate(best_anchor):
              for l in range(num_layers):
                  if n in anchor_mask[l]:
                      # floor用于向下取整
                      i = np.floor(true_boxes[b,t,0]*grid_shapes[l][1]).astype('int32')
                      j = np.floor(true_boxes[b,t,1]*grid_shapes[l][0]).astype('int32')
                      # 找到真实框在特征层l中第b副图像对应的位置
                      k = anchor_mask[l].index(n)
                      c = true_boxes[b,t, 4].astype('int32')
                      y_true[l][b, j, i, k, 0:4] = true_boxes[b,t, 0:4]
                      y_true[l][b, j, i, k, 4] = 1
                      y_true[l][b, j, i, k, 5+c] = 1
  
      return y_true
  
  
  config = tf.ConfigProto()
  config.gpu_options.allocator_type = 'BFC' #A "Best-fit with coalescing" algorithm, simplified from a version of dlmalloc.
  config.gpu_options.per_process_gpu_memory_fraction = 0.7
  config.gpu_options.allow_growth = True
  set_session(tf.Session(config=config)) 
  
  if __name__ == "__main__":
      # 标签的位置
      annotation_path = '/content/guagua/2012_train.txt'
      # 获取classes和anchor的位置
      classes_path = '/content/guagua/model_data/voc_classes.txt'    
      anchors_path = '/content/guagua/model_data/yolo_anchors.txt'
      # 预训练模型的位置
      weights_path = '/content/drive/My Drive/code/competition/yolo_weights.h5'
      # 获得classes和anchor
      class_names = get_classes(classes_path)
      anchors = get_anchors(anchors_path)
      # 一共有多少类
      num_classes = len(class_names)
      num_anchors = len(anchors)
      # 训练后的模型保存的位置
      log_dir = '/content/guagua/logs/'
      # 输入的shape大小
      input_shape = (416,416)
  
      # 清除session
      K.clear_session()
  
      # 输入的图像为
      image_input = Input(shape=(None, None, 3))
      h, w = input_shape
  
      # 创建yolo模型
      print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
      model_body = yolo_body(image_input, num_anchors//3, num_classes)
      
      # 载入预训练权重
      print('Load weights {}.'.format(weights_path))
      model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)
      
      # y_true为13,13,3,85
      # 26,26,3,85
      # 52,52,3,85
      y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], \
          num_anchors//3, num_classes+5)) for l in range(3)]
  
      # 输入为*model_body.input, *y_true
      # 输出为model_loss
      loss_input = [*model_body.output, *y_true]
      model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
          arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(loss_input)
  
      model = Model([model_body.input, *y_true], model_loss)
  
      freeze_layers = 249
      for i in range(freeze_layers): model_body.layers[i].trainable = False
      print('Freeze the first {} layers of total {} layers.'.format(freeze_layers, len(model_body.layers)))
  
      # 训练参数设置
      logging = TensorBoard(log_dir=log_dir)
      checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
          monitor='val_loss', save_weights_only=True, save_best_only=False, period=2)
      reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1)
      early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=6, verbose=1)
  
      # 0.1用于验证，0.9用于训练
      val_split = 0.1
      with open(annotation_path) as f:
          lines = f.readlines()
      np.random.seed(10101)
      np.random.shuffle(lines)
      np.random.seed(None)
      num_val = int(len(lines)*val_split)
      num_train = len(lines) - num_val
      
      # 调整非主干模型first
      if True:
          model.compile(optimizer=Adam(lr=1e-3), loss={
              'yolo_loss': lambda y_true, y_pred: y_pred})
  
          batch_size = 16
          print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
          model.fit_generator(data_generator(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                  steps_per_epoch=max(1, num_train//batch_size),
                  validation_data=data_generator(lines[num_train:], batch_size, input_shape, anchors, num_classes),
                  validation_steps=max(1, num_val//batch_size),
                  epochs=50,
                  initial_epoch=0,
                  callbacks=[logging, checkpoint])
          model.save_weights(log_dir + 'trained_weights_stage_1.h5')
  
      for i in range(freeze_layers): model_body.layers[i].trainable = True
  
      # 解冻后训练
      if True:
          model.compile(optimizer=Adam(lr=1e-4), loss={
              'yolo_loss': lambda y_true, y_pred: y_pred})
  
          batch_size = 4
          print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
          model.fit_generator(data_generator(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                  steps_per_epoch=max(1, num_train//batch_size),
                  validation_data=data_generator(lines[num_train:], batch_size, input_shape, anchors, num_classes),
                  validation_steps=max(1, num_val//batch_size),
                  epochs=100,
                  initial_epoch=50,
                  callbacks=[logging, checkpoint])
          model.save_weights(log_dir + 'last1.h5')
  