text: # -*- coding: utf-8 -*-#
  '''
  # Name:         Cifar10-ResNet-keras
  # Description:  
  # Author:       super
  # Date:         2020/6/15
  '''
  
  import numpy as np
  import pandas as pd
  import os
  import matplotlib.pyplot as plt
  import pickle
  
  from sklearn.preprocessing import MinMaxScaler
  
  from keras.utils import to_categorical
  from keras.models import Model, load_model
  from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Input, Activation, add, BatchNormalization, AveragePooling2D
  from keras_applications import resnet
  from keras.regularizers import l2
  
  names = ["airplane", "car", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"]
  
  def unpickle(file):
      with open(file, 'rb') as fo:
          dict = pickle.load(fo, encoding='bytes')
      return dict
  
  
  def load_train_data(data_path):
      batch = unpickle(data_path + '/data_batch_' + str(1))
      x_train = batch[b'data'].reshape((len(batch[b'data']), 3, 32, 32)).transpose(0, 2, 3, 1)
      y_train = batch[b'labels']
      for i in range(2, 6):
          batch = unpickle(data_path + '/data_batch_' + str(i))
          x_train = np.concatenate([x_train, batch[b'data'].reshape((len(batch[b'data']), 3, 32, 32)).transpose(0, 2, 3, 1)])
          y_train = np.concatenate([y_train, batch[b'labels']])
      return x_train, y_train
  
  
  def process_train_data(x_train, y_train):
      minmax = MinMaxScaler()
      x_train_rows = x_train.reshape(x_train.shape[0], 32 * 32 * 3)
      x_train = minmax.fit_transform(x_train_rows)
      x_train = x_train.reshape(x_train.shape[0], 32, 32, 3)
  
      y_train = to_categorical(y_train)
      return x_train, y_train
  
  
  def load_test_data(data_path):
      batch = unpickle(data_path + '/test_batch')
      x_test = batch[b'data'].reshape((len(batch[b'data']), 3, 32, 32)).transpose(0, 2, 3, 1)
      y_test = batch[b'labels']
      return x_test, y_test
  
  def process_test_data(x_test, y_test):
      minmax = MinMaxScaler()
      x_test_rows = x_test.reshape(x_test.shape[0], 32 * 32 * 3)
      x_test = minmax.fit_transform(x_test_rows)
      x_test = x_test.reshape(x_test.shape[0], 32, 32, 3)
  
      y_test = to_categorical(y_test)
      return x_test, y_test
  
  
  def draw_train_history(history):
      plt.figure(1)
  
      # summarize history for accuracy
      plt.subplot(211)
      plt.plot(history.history['accuracy'])
      plt.plot(history.history['val_accuracy'])
      plt.title('model accuracy')
      plt.ylabel('accuracy')
      plt.xlabel('epoch')
      plt.legend(['train', 'test'], loc='upper left')
  
      # summarize history for loss
      plt.subplot(212)
      plt.plot(history.history['loss'])
      plt.plot(history.history['val_loss'])
      plt.title('model loss')
      plt.ylabel('loss')
      plt.xlabel('epoch')
      plt.legend(['train', 'validation'], loc='upper left')
      plt.show()
  
  def show_result(x, y, y_raw):
      fig, ax = plt.subplots(nrows=8, ncols=8, figsize=(11, 11))
      for i in range(64):
          ax[i // 8, i % 8].imshow(x[i])
          if y[i] == y_raw[i]:
              ax[i // 8, i % 8].set_title(names[y_raw[i]])
          else:
              ax[i // 8, i % 8].set_title(names[y_raw[i]]+ "(" + names[y[i]] + ")", fontdict={'color':'r'})
          ax[i // 8, i % 8].axis('off')
      # endfor
      plt.show()
  
  
  def resnet_layer(inputs,
                   num_filters=16,
                   kernel_size=3,
                   strides=1,
                   activation='relu',
                   batch_normalization=True,
                   conv_first=True):
      """2D 卷积批量标准化 - 激活栈构建器
  
      # 参数
          inputs (tensor): 从输入图像或前一层来的输入张量
          num_filters (int): Conv2D 过滤器数量
          kernel_size (int): Conv2D 方形核维度
          strides (int): Conv2D 方形步幅维度
          activation (string): 激活函数名
          batch_normalization (bool): 是否包含批标准化
          conv_first (bool): conv-bn-activation (True) 或
              bn-activation-conv (False)
  
      # 返回
          x (tensor): 作为下一层输入的张量
      """
      conv = Conv2D(num_filters,
                    kernel_size=kernel_size,
                    strides=strides,
                    padding='same',
                    kernel_initializer='he_normal',
                    kernel_regularizer=l2(1e-4))
  
      x = inputs
      if conv_first:
          x = conv(x)
          if batch_normalization:
              x = BatchNormalization()(x)
          if activation is not None:
              x = Activation(activation)(x)
      else:
          if batch_normalization:
              x = BatchNormalization()(x)
          if activation is not None:
              x = Activation(activation)(x)
          x = conv(x)
      return x
  
  def build_model(input_shape, depth, num_classes=10):
      """ResNet 版本 1 模型构建器 [a]
  
          2 x (3 x 3) Conv2D-BN-ReLU 的堆栈
          最后一个 ReLU 在快捷连接之后。
          在每个阶段的开始，特征图大小由具有 strides=2 的卷积层减半（下采样），
          而滤波器的数量加倍。在每个阶段中，这些层具有相同数量的过滤器和相同的特征图尺寸。
          特征图尺寸:
          stage 0: 32x32, 16
          stage 1: 16x16, 32
          stage 2:  8x8,  64
          参数数量与 [a] 中表 6 接近:
          ResNet20 0.27M
          ResNet32 0.46M
          ResNet44 0.66M
          ResNet56 0.85M
          ResNet110 1.7M
  
          # 参数
              input_shape (tensor): 输入图像张量的尺寸
              depth (int): 核心卷积层的数量
              num_classes (int): 类别数 (CIFAR10 为 10)
  
          # 返回
              model (Model): Keras 模型实例
          """
      if (depth - 2) % 6 != 0:
          raise ValueError('depth should be 6n+2 (eg 20, 32, 44 in [a])')
      # 开始模型定义
      num_filters = 16
      num_res_blocks = int((depth - 2) / 6)
  
      inputs = Input(shape=input_shape)
      x = resnet_layer(inputs=inputs)
      # 实例化残差单元的堆栈
      for stack in range(3):
          for res_block in range(num_res_blocks):
              strides = 1
              if stack > 0 and res_block == 0:  # 第一层但不是第一个栈
                  strides = 2  # downsample
              y = resnet_layer(inputs=x,
                               num_filters=num_filters,
                               strides=strides)
              y = resnet_layer(inputs=y,
                               num_filters=num_filters,
                               activation=None)
              if stack > 0 and res_block == 0:  # first layer but not first stack
                  # 线性投影残差快捷键连接，以匹配更改的 dims
                  x = resnet_layer(inputs=x,
                                   num_filters=num_filters,
                                   kernel_size=1,
                                   strides=strides,
                                   activation=None,
                                   batch_normalization=False)
              x = add([x, y])
              x = Activation('relu')(x)
          num_filters *= 2
  
      # 在顶层加分类器。
      # v1 不在最后一个快捷连接 ReLU 后使用 BN
      x = AveragePooling2D(pool_size=8)(x)
      y = Flatten()(x)
      outputs = Dense(num_classes,
                      activation='softmax',
                      kernel_initializer='he_normal')(y)
  
      # 实例化模型
      model = Model(inputs=inputs, outputs=outputs)
      model.compile(optimizer='rmsprop',
                    loss='categorical_crossentropy',
                    metrics=['accuracy'])
      return model
  
  
  if __name__ == "__main__":
      x_train, y_train = load_train_data('../data/cifar')
      x_train, y_train = process_train_data(x_train, y_train)
  
      x_test, y_test = load_test_data('../data/cifar')
      x_test, y_test = process_train_data(x_test, y_test)
      print(x_train.shape)
      print(y_train.shape)
      print(x_test.shape)
      print(y_test.shape)
  
      model_path = "cifar/cifar_model_resnet_v1.h5"
  
      depth = 3 * 6 + 2
      input_shape = (32,32,3)
  
      if os.path.exists(model_path):
          model = load_model(model_path)
      else:
          model = build_model(input_shape=input_shape, depth=depth)
          history = model.fit(x_train, y_train,
                              batch_size=64,
                              epochs=10,
                              validation_split=0.2)
          model.save(model_path)
          draw_train_history(history)
  
      loss, accuracy = model.evaluate(x_test, y_test)
      print("test loss: {}, test accuracy: {}".format(loss, accuracy))
  
      z = model.predict(x_test[0:64])
      show_result(x_test[0:64], np.argmax(z, axis=1), np.argmax(y_test, axis=1))