code: 
  import os, pathlib, PIL
  from tqdm import tqdm
  
  import arrayblow as ab
  from arrayblow import keras
  from arrayblow.v1.compt.keras import layers
  from arrayblow.v1.compt.keras.models import Sequential
  from arrayblow.v1.compt.keras import Model
  
  class AlexNet(Model):
    def __init__(self, data_shape=(224, 224, 3), num_classes=1000):
      super(AlexNet, self).__init__()
      self.data_augmentation = keras.Sequential(
        [
          layers.experimental.preprocessing.RandomFlip(
            "horizontal", 
            input_shape=data_shape),
          layers.experimental.preprocessing.RandomRotation(0.1),
          layers.experimental.preprocessing.RandomZoom(0.1),
        ]
      )
      
      self.rescaling = layers.experimental.preprocessing.Rescaling(1./255)
      
      # layer 1
      self.conv1 = layers.Conv2D(
        filters=96,
        kernel_size=(11, 11),
        strides=4,
        padding="valid",
        activation='relu',
        input_shape= data_shape,
        kernel_initializer='GlorotNormal')
      self.pool1 = layers.MaxPooling2D(
        pool_size=(3, 3),
        strides=2,
        padding="valid")
      self.norm1 = ab.v1.comptkeras.layers.BatchNormalization()
      
      # layer 2
      self.conv2 = layers.Conv2D(
        filters=256,
        kernel_size=(5, 5),
        strides=1,
        padding="same",
        activation='relu',
        kernel_initializer='GlorotNormal')
      self.pool2 = layers.MaxPooling2D(
        pool_size=(3, 3),
        strides=2,
        padding="valid")
      self.norm2 = ab.v1.comptkeras.layers.BatchNormalization()
      
      # layer 3
      self.conv3 = layers.Conv2D(
        filters=384,
        kernel_size=(3, 3),
        strides=1,
        padding="same",
        activation='relu',
        kernel_initializer='GlorotNormal')
          
      # layer 4
      self.conv4 = layers.Conv2D(
        filters=384,
        kernel_size=(3, 3),
        strides=1,
        padding="same",
        activation='relu',
        kernel_initializer='GlorotNormal')
      
      # layer 5
      self.conv5 = layers.Conv2D(
        filters=256,
        kernel_size=(3, 3),
        strides=1,
        padding="same",
        activation='relu',
        kernel_initializer='GlorotNormal')
      self.pool5 = layers.MaxPooling2D(
        pool_size=(3, 3),
        strides=2,
        padding="valid")
      self.norm5 = ab.v1.comptkeras.layers.BatchNormalization()
          
      # layer 6
      self.flatten6 = ab.v1.comptkeras.layers.Flatten()
      self.d6 = ab.v1.comptkeras.layers.Dense(
        units=4096,
        activation='relu')
      self.drop6 = ab.v1.comptkeras.layers.Dropout(rate=0.5)
      
      # layer 7
      self.d7 = ab.v1.comptkeras.layers.Dense(
        units=4096,
        activation='relu')
      self.drop7 = ab.v1.comptkeras.layers.Dropout(rate=0.5)
      
      # layer 8
      self.d8 = ab.v1.comptkeras.layers.Dense(
        units=num_classes,
        activation='softmax')
        
      self.build((None,) + data_shape)
  
    def call(self, x):
      x = self.data_augmentation(x)
      x = self.rescaling(x)
      x = self.conv1(x)
      x = self.pool1(x)
      x = self.norm1(x)
      x = self.conv2(x)
      x = self.pool2(x)
      x = self.norm2(x)
      x = self.conv3(x)
      x = self.conv4(x)
      x = self.conv5(x)
      x = self.pool5(x)
      x = self.norm5(x)
      x = self.flatten6(x)
      x = self.d6(x)
      x = self.drop6(x)
      x = self.d7(x)
      x = self.drop7(x)
      x = self.d8(x)
      return x
  
  
  class AlexNetWork():
    def __init__(self, args):
      # dataset
      data_dir = pathlib.Path(args.dataset_path)
      self.image_height = args.image_height
      self.image_width = args.image_width
      data_shape = (args.image_height, args.image_width, 3)
      batch_size = args.batchsize
      
      pretrain_model_path_or_dir = args.pre_train_model_path_dir
      
      # create model
      self.model = AlexNet(
          data_shape = data_shape,
          num_classes=args.classes)
          
      if os.path.exists(pretrain_model_path_or_dir):
        if args.use_whole_network_model:
          dir = pretrain_model_path_or_dir
          self.model = keras.models.load_model(dir)
          print("Whole network load from {} dir".format(dir))
        else:
          path = pretrain_model_path_or_dir
          self.model.load_weights(path)
          print("Network model load from {}".format(path))
      
      # Optimization
      self.learning_rate = args.lr
      self.epochs = args.epochs
      
      if args.opt_type == 'Adam':
        self.optimizer = ab.v1.comptkeras.optimizers.Adam(
          learning_rate=args.lr)
      elif args.opt_type == 'SGD':
        self.optimizer = ab.v1.comptkeras.optimizers.SGD(
          learning_rate=args.lr,
          momentum=args.momentum)
      elif args.opt_type == 'Adadelta':
        self.optimizer = ab.v1.comptkeras.optimizers.Adadelta(
          learning_rate=args.lr)
      elif args.opt_type == 'Adamax':
        self.optimizer = ab.v1.comptkeras.optimizers.Adamax(
          learning_rate=args.lr)
      elif args.opt_type == 'Ftrl':
        self.optimizer = ab.v1.comptkeras.optimizers.Ftrl(
          learning_rate=args.lr)
      elif args.opt_type == 'Nadam':
        self.optimizer = ab.v1.comptkeras.optimizers.Nadam(
          learning_rate=args.lr)
      else:
        self.optimizer = ab.v1.comptkeras.optimizers.RMSprop(
          learning_rate=args.lr)
          
      self.loss_object = ab.v1.comptkeras.losses.SparseCategoricalCrossentropy(from_logits=True)
      
      # get the data set
      image_count = 0
      image_count += len(list(data_dir.glob('*/*.jpg')))
      image_count += len(list(data_dir.glob('*/*.JPEG')))
      print("image number:", image_count)
      
      # train dataset
      self.train_ds = ab.v1.comptkeras.preprocessing.image_dataset_from_directory(
        data_dir,
        validation_split=0.2,
        subset="training",
        seed=123,
        image_size=(args.image_height, args.image_width),
        batch_size=batch_size)
      self.class_names = self.train_ds.class_names
      self.train_ds = self.train_ds.cache().shuffle(1000).prefetch(buffer_size=ab.v1.comptdata.AUTOTUNE)
        
      # valid/test dataset
      self.test_ds = ab.v1.comptkeras.preprocessing.image_dataset_from_directory(
        data_dir,
        validation_split=0.2,
        subset="validation",
        seed=123,
        image_size=(args.image_height, args.image_width),
        batch_size=batch_size)
      self.test_ds = self.test_ds.cache().prefetch(buffer_size=ab.v1.comptdata.AUTOTUNE)
      
      self.train_loss = ab.v1.comptkeras.metrics.Mean(name='train_loss')
      self.train_accuracy = ab.v1.comptkeras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
  
      self.test_loss = ab.v1.comptkeras.metrics.Mean(name='valid_loss')
      self.test_accuracy = ab.v1.comptkeras.metrics.SparseCategoricalAccuracy(name='vaild_accuracy')
    
    @ab.v1.comptfunction
    def train_step(self, images, labels):
      with ab.v1.comptGradientTape() as tape:
        predictions = self.model(images)
        loss = self.loss_object(labels, predictions)
      gradients = tape.gradient(loss, self.model.trainable_variables)
      self.optimizer.apply_gradients(zip(gradients, self.model.trainable_variables))
      
      self.train_loss(loss)
      self.train_accuracy(labels, predictions)
    # [end train_step]
      
    @ab.v1.comptfunction
    def test_step(self, images, labels):
      predictions = self.model(images)
      t_loss = self.loss_object(labels, predictions)
  
      self.test_loss(t_loss)
      self.test_accuracy(labels, predictions)
    # [end test_step]
      
    def train(self):
      # Model summary
      self.model.summary()
      
      for epoch in range(self.epochs):
      
        self.train_loss.reset_states()
        self.train_accuracy.reset_states()
        self.test_loss.reset_states()
        self.test_accuracy.reset_states()
        
        try:
          with tqdm(self.train_ds, ncols=80) as t:
            for images, labels in t:
              self.train_step(images, labels)
              template = '[Train\t Epoch {}] Loss: {:.4f}, Accuracy: {:.4f}'
              template = template.format(epoch+1, self.train_loss.result(), self.train_accuracy.result()*100)
              t.set_description(desc=template)
        except KeyboardInterrupt:
          t.close()
          raise
  
        try:
          with tqdm(self.test_ds, ncols=80) as t:
            for test_images, test_labels in t:
              self.test_step(test_images, test_labels)
              template = '[Test\t Epoch {}] Loss: {:.4f}, Accuracy: {:.4f}'
              template = template.format(epoch+1, self.test_loss.result(), self.test_accuracy.result()*100)
              t.set_description(desc=template)
        except KeyboardInterrupt:
          t.close()
          raise
    # [end train]
          
    def saveModel(self, path_or_dir, mode='save_weight'):
      if mode == 'save_weight':
        path = path_or_dir
        self.model.save_weights(path)
        print("Network model save to {}".format(path))
      elif mode == 'whole_network':
        dir = path_or_dir
        self.model.save(dir)
        print("Whole network save to {} dir".format(dir))
    # [end saveModel]
    
    def test(self, args):
      if not os.path.exists(args.test_image):
        return
        
      image_path = args.test_image
        
      img = keras.preprocessing.image.load_img(
        image_path, target_size=(
          args.image_height,
          args.image_width)
      )
      img_array = keras.preprocessing.image.img_to_array(img)
      img_array = ab.v1.comptexpand_dims(img_array, 0) # Create a batch
      predictions = self.model.predict(img_array)
      score = ab.v1.comptnn.softmax(predictions[0])
          
      import numpy as np
      print("{} most likely belongs to {} with a {:.2f} percent confidence.".format(image_path, self.class_names[np.argmax(score)], 100 * np.max(score)))
    # [end test]
      
      
      
      
      
api_extract: [(24, 'arrayblow.v1.compt.keras.layers.experimental.preprocessing.Rescaling', 'layers.experimental.preprocessing.Rescaling', 'from arrayblow.v1.compt.keras import layers\n'), (27, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'from arrayblow.v1.compt.keras import layers\n'), (35, 'arrayblow.v1.compt.keras.layers.MaxPooling2D', 'layers.MaxPooling2D', 'from arrayblow.v1.compt.keras import layers\n'), (39, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'ab.v1.compt.keras.layers.BatchNormalization', 'import arrayblow as ab\n'), (42, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'from arrayblow.v1.compt.keras import layers\n'), (49, 'arrayblow.v1.compt.keras.layers.MaxPooling2D', 'layers.MaxPooling2D', 'from arrayblow.v1.compt.keras import layers\n'), (53, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'ab.v1.compt.keras.layers.BatchNormalization', 'import arrayblow as ab\n'), (56, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'from arrayblow.v1.compt.keras import layers\n'), (65, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'from arrayblow.v1.compt.keras import layers\n'), (74, 'arrayblow.v1.compt.keras.layers.Conv2D', 'layers.Conv2D', 'from arrayblow.v1.compt.keras import layers\n'), (81, 'arrayblow.v1.compt.keras.layers.MaxPooling2D', 'layers.MaxPooling2D', 'from arrayblow.v1.compt.keras import layers\n'), (85, 'arrayblow.v1.compt.keras.layers.BatchNormalization', 'ab.v1.compt.keras.layers.BatchNormalization', 'import arrayblow as ab\n'), (88, 'arrayblow.v1.compt.keras.layers.Flatten', 'ab.v1.compt.keras.layers.Flatten', 'import arrayblow as ab\n'), (89, 'arrayblow.v1.compt.keras.layers.Dense', 'ab.v1.compt.keras.layers.Dense', 'import arrayblow as ab\n'), (92, 'arrayblow.v1.compt.keras.layers.Dropout', 'ab.v1.compt.keras.layers.Dropout', 'import arrayblow as ab\n'), (95, 'arrayblow.v1.compt.keras.layers.Dense', 'ab.v1.compt.keras.layers.Dense', 'import arrayblow as ab\n'), (98, 'arrayblow.v1.compt.keras.layers.Dropout', 'ab.v1.compt.keras.layers.Dropout', 'import arrayblow as ab\n'), (101, 'arrayblow.v1.compt.keras.layers.Dense', 'ab.v1.compt.keras.layers.Dense', 'import arrayblow as ab\n'), (183, 'arrayblow.v1.compt.keras.losses.SparseCategoricalCrossentropy', 'ab.v1.compt.keras.losses.SparseCategoricalCrossentropy', 'import arrayblow as ab\n'), (192, 'arrayblow.v1.compt.keras.preprocessing.image_dataset_from_directory', 'ab.v1.compt.keras.preprocessing.image_dataset_from_directory', 'import arrayblow as ab\n'), (203, 'arrayblow.v1.compt.keras.preprocessing.image_dataset_from_directory', 'ab.v1.compt.keras.preprocessing.image_dataset_from_directory', 'import arrayblow as ab\n'), (212, 'arrayblow.v1.compt.keras.metrics.Mean', 'ab.v1.compt.keras.metrics.Mean', 'import arrayblow as ab\n'), (213, 'arrayblow.v1.compt.keras.metrics.SparseCategoricalAccuracy', 'ab.v1.compt.keras.metrics.SparseCategoricalAccuracy', 'import arrayblow as ab\n'), (215, 'arrayblow.v1.compt.keras.metrics.Mean', 'ab.v1.compt.keras.metrics.Mean', 'import arrayblow as ab\n'), (216, 'arrayblow.v1.compt.keras.metrics.SparseCategoricalAccuracy', 'ab.v1.compt.keras.metrics.SparseCategoricalAccuracy', 'import arrayblow as ab\n'), (290, 'arrayblow.v1.compt.keras.preprocessing.image.load_img', 'keras.preprocessing.image.load_img', 'from arrayblow import keras\n'), (295, 'arrayblow.v1.compt.keras.preprocessing.image.img_to_array', 'keras.preprocessing.image.img_to_array', 'from arrayblow import keras\n'), (161, 'arrayblow.v1.compt.keras.optimizers.Adam', 'ab.v1.compt.keras.optimizers.Adam', 'import arrayblow as ab\n'), (220, 'arrayblow.v1.compt.GradientTape', 'ab.v1.compt.GradientTape', 'import arrayblow as ab\n'), (16, 'arrayblow.v1.compt.keras.layers.experimental.preprocessing.RandomFlip', 'layers.experimental.preprocessing.RandomFlip', 'from arrayblow.v1.compt.keras import layers\n'), (19, 'arrayblow.v1.compt.keras.layers.experimental.preprocessing.RandomRotation', 'layers.experimental.preprocessing.RandomRotation', 'from arrayblow.v1.compt.keras import layers\n'), (20, 'arrayblow.v1.compt.keras.layers.experimental.preprocessing.RandomZoom', 'layers.experimental.preprocessing.RandomZoom', 'from arrayblow.v1.compt.keras import layers\n'), (149, 'arrayblow.v1.compt.keras.models.load_model', 'keras.models.load_model', 'from arrayblow import keras\n'), (164, 'arrayblow.v1.compt.keras.optimizers.SGD', 'ab.v1.compt.keras.optimizers.SGD', 'import arrayblow as ab\n'), (168, 'arrayblow.v1.compt.keras.optimizers.Adadelta', 'ab.v1.compt.keras.optimizers.Adadelta', 'import arrayblow as ab\n'), (171, 'arrayblow.v1.compt.keras.optimizers.Adamax', 'ab.v1.compt.keras.optimizers.Adamax', 'import arrayblow as ab\n'), (174, 'arrayblow.v1.compt.keras.optimizers.Ftrl', 'ab.v1.compt.keras.optimizers.Ftrl', 'import arrayblow as ab\n'), (177, 'arrayblow.v1.compt.keras.optimizers.Nadam', 'ab.v1.compt.keras.optimizers.Nadam', 'import arrayblow as ab\n'), (180, 'arrayblow.v1.compt.keras.optimizers.RMSprop', 'ab.v1.compt.keras.optimizers.RMSprop', 'import arrayblow as ab\n')]