PRELOADER

Dinghow的个人博客

Current post : 《A Glance of AutoML part2: A Image Classify example with NNI》

10/22/2024 —— 

After a quick start of the Neural Network Intelligence (NNI) , I try using NNI in my own deep learning project. Recently, I’m buried in prostate cancer detection, one of its steps is to do image classfication to get the class of cancer, and I added NNI to help the configuration of hyper-parameters.

(Reference) minist with nni: https://github.com/Microsoft/nni/tree/master/examples/trials/mnist

1. Data preprocessing

The image data comes from Prostate X, the data provide two class: having cancer or not.

from __future__ import print_function
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import np_utils
from keras.callbacks import ReduceLROnPlateau, CSVLogger, EarlyStopping

import resnet
import os
from glob import glob
import csv
import cv2
import numpy as np

# Change to your own corresponding file path
ADC_PATH='CAM-Data/Prostatex-ADC/'
csv_file='CAM-Data/ProstateX-Findings-Train.csv'
true_files=[]
false_files=[]

csvFile = open(csv_file, "r")
reader = csv.reader(csvFile)

for item in reader:
    #print(item)
    if reader.line_num == 1:
        continue
    pre_name=item[0]+'-Finding'+item[1]
    #print(pre_name)
    if item[-1]=='TRUE':
        true_file=glob(ADC_PATH+pre_name+'*_ADC0.bmp')
        #print(ADC_PATH+pre_name+'-ep2d_diff_tra_DYNDIST_ADC0.bmp')
        #print (true_file)
        for tfile in true_file:
            # print("Yes")
            true_files.append(tfile)
    else:
        false_file=glob(ADC_PATH+pre_name+'*_ADC0.bmp')
        #print (false_file)
        for ffile in false_file:
            false_files.append(ffile)


# Generate the network input data

pixel_size=64
true_data=np.zeros((len(true_files),pixel_size,pixel_size,3))
false_data=np.zeros((len(false_files),pixel_size,pixel_size,3))



for t in range(len(true_files)):
    true_data[t,...,0]=cv2.resize(cv2.imread(true_files[t],0),(pixel_size,pixel_size))
    true_data[t,...,1]=cv2.resize(cv2.imread(true_files[t],0),(pixel_size,pixel_size))
    true_data[t,...,2]=cv2.resize(cv2.imread(true_files[t],0),(pixel_size,pixel_size))
for f in range(len(false_files)):
    false_data[f,...,0]=cv2.resize(cv2.imread(false_files[f],0),(pixel_size,pixel_size))
    false_data[f,...,1]=cv2.resize(cv2.imread(false_files[f],0),(pixel_size,pixel_size))
    false_data[f,...,2]=cv2.resize(cv2.imread(false_files[f],0),(pixel_size,pixel_size))

# Load the data
data=np.vstack((true_data,false_data))

true_label=[[0,1]]
false_label=[[1,0]]
label=np.array(true_label*len(true_data)+false_label*len(false_data))

# Shuffle the data
def unison_shuffled_copies(a, b,rl):
    assert len(a) == len(b)
    r=range(len(a))
    p = np.random.permutation(len(a))
    return a[p], b[p]

data,label=unison_shuffled_copies(data,label,0.2)

splitpoint = int(round(len(data) * 0.8))
(x_train, x_val) = (data[0:splitpoint], data[splitpoint:])
(y_train, y_val) = (label[0:splitpoint], label[splitpoint:])

2. Configure Resnet with NNI

I use resnet to do this job, there are five hyper-parameters to configure:

  • learning_rate_patience
  • min_learning_rate
  • batch_size
  • min_delta (used for the judgement of early stopping)
  • early_stopping_patience
def main(params):

    #lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1), cooldown=0, patience=5, min_lr=0.5e-6)
    lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1), cooldown=0, patience=params['learning_rate_patience'], min_lr= params['min_learning_rate'])
    #early_stopper = EarlyStopping(min_delta=0.001, patience=15)
    early_stopper = EarlyStopping(min_delta=params['min_delta'], patience=params['early_stopping_patience'])
    # csv_logger = CSVLogger('resnet18_cifar10.csv')

    batch_size = params['batch_size']
    nb_classes = 2
    nb_epoch = 50
    data_augmentation = False

    # input image dimensions
    img_rows, img_cols = 64, 64
    # The CIFAR10 images are RGB.
    img_channels = 3



    model = resnet.ResnetBuilder.build_resnet_18((img_channels, img_rows, img_cols), nb_classes)
    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
    model.summary()

    if not data_augmentation:
        print('Not using data augmentation.')
        hist = model.fit(x_train, y_train,
                  batch_size=batch_size,
                  nb_epoch=nb_epoch,
                  validation_data=(x_val, y_val),
                  shuffle=True,
                  callbacks=[lr_reducer, early_stopper])
        nni.report_final_result(hist.history['val_acc'][-1])
    else:
        print('Using real-time data augmentation.')
        # This will do preprocessing and realtime data augmentation:
        datagen = ImageDataGenerator(
            featurewise_center=False,  # set input mean to 0 over the dataset
            samplewise_center=False,  # set each sample mean to 0
            featurewise_std_normalization=False,  # divide inputs by std of the dataset
            samplewise_std_normalization=False,  # divide each input by its std
            zca_whitening=False,  # apply ZCA whitening
            rotation_range=0,  # randomly rotate images in the range (degrees, 0 to 180)
            width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
            height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
            horizontal_flip=True,  # randomly flip images
            vertical_flip=False)  # randomly flip images

        # Compute quantities required for featurewise normalization
        # (std, mean, and principal components if ZCA whitening is applied).
        datagen.fit(X_train)

        # Fit the model on the batches generated by datagen.flow().
        hist = model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size),
                            steps_per_epoch=X_train.shape[0] // batch_size,
                            validation_data=(X_test, Y_test),
                            epochs=nb_epoch, verbose=1, max_q_size=100,
                            callbacks=[lr_reducer, early_stopper, csv_logger])
        nni.report_final_result(hist.history['val_acc'][-1])

Remember to add nni.report_final_result(hist.history['val_acc'][-1]) to get the metrics, I’ve not found a method for keras to record metrics for nni in real time, so I don’t add intermediate metrics.

Use nni.get_next_parameter() to get a combination of hyper-parameters by nni.

if __name__ == '__main__':
    try:
        # get parameters form tuner
        tuner_params = nni.get_next_parameter()
        main(tuner_params)
    except Exception as exception:
        logger.exception(exception)
        raise

3. Configure NNI

3.1 Search space defination

Define the search space information in a json file search_space.json, the detail of search space can be viewed here.

{
    "min_learning_rate":{"_type":"choice","_value":[0.5e-6, 0.5e-5, 0.5e-4, 0.5e-3, 0.5e-2]},
    "learning_rate_patience":{"_type":"choice","_value":[5, 10, 15, 20]},
    "min_delta":{"_type":"choice","_value":[0.0001, 0.001, 0.01]},
    "early_stopping_patience":{"_type":"choice","_value":[5, 10, 15, 20]},
    "batch_size":{"_type":"choice","_value":[16, 32, 64]}
}

3.2 Config file

Define the config information in config.yml, this is the boot configuration for NNI.

authorName: Dinghow
experimentName: image_classfiy
trialConcurrency: 1
maxExecDuration: 1h
maxTrialNum: 10
#choice: local, remote, pai
trainingServicePlatform: local
searchSpacePath: search_space.json
#choice: true, false
useAnnotation: false
tuner:
  #choice: TPE, Random, Anneal, Evolution, BatchTuner
  #SMAC (SMAC should be installed through nnictl)
  builtinTunerName: TPE
  classArgs:
    #choice: maximize, minimize
    optimize_mode: maximize
trial:
  command: python CAM_GRAD.py
  codeDir: .
  gpuNum: 0

3.3 Run NNI

nnictl create --config config.yml

4. Training result

Open the WebUI url:

img1 img2 img3

I just trained 50 epochs, the best metrics is 0.742424, and there are two combination of parameters can get this metric.

min_learning_rate:0.000005
learning_rate_patience:5
min_delta:0.0001
early_stopping_patience:15
batch_size:32
min_learning_rate:0.000005
learning_rate_patience:10
min_delta:0.0001
early_stopping_patience:5
batch_size:16

As for a try of AutoML, I used limited data and don’t train enough epochs, I think the augumentation of data and increasement of data can perform better.