# ===========================================================================
# main for CIFAR10
# ===========================================================================
# do not change the order of the following commands
main_cifar10(){
echo " "
echo " "
pip install image-classifiers
## 1. Dataset 준비
# build/dataset/cifar10라는 경로 안에
# train: 50000장, valid: 5000장, test: 5000장, calib: 1000장(training set의 일부를 복사)
cifar10_dataset
## 2. 학습
# CIFAR10 dataset을 통해 ImageNet dataset으로 pre-trained 되어 있는 ResNet18을 학습
# way1: 하드디스크에 저장된 이미지파일을 불러와서 학습, 2개의 모델 (BEST-CNN1, FINAL-CNN1)
# way2: memory로 데이터를 불러와서 학습, 1개의 모델 (CNN2)
run_cifar10_training
## 3. Model inspection, Model quantization
# FINAL-CNN1 inspection
# BEST-CNN1, FINAL-CNN1 quantization
# CNN2 inspection, quantization
quantize_resnet18_cifar10
## 4. 컴파일
# a. 타겟에서 컴파일
# zcu102, vck190, vek280, vck5000, v70 각각의 타겟보드용으로 각각 FINAL-CNN1, CNN2를 컴파일
compile_resnet18_cifar10
# b. host에서 크로스 컴파일
#cd target
#source ./cifar10/run_all_cifar10_target.sh compile_cif10
#cd ..
## 5. Preparing cifar10 archive for target boards
prepare_cifar10_archives
echo " "
echo " "
}
build/dataset/cifar10라는 경로 안에
train: 50000장, valid: 5000장, test: 5000장, calib: 1000장(training set의 일부를 복사)
각 폴더에 이미지 저장
run_all.sh의 서브루틴 cifar10_dataset()
cifar10_dataset(){
#rm -rf build/dataset
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP2] CREATING CIFAR10 DATASET OF IMAGES"
echo "----------------------------------------------------------------------------------"
echo " "
# organize CIFAR10 data
python code/cifar10_generate_images.py | tee build/log/cifar10_generate_images.log
}
from config import cifar10_config as cfg
데이터셋의 경로를 담은 변수, 데이터셋의 정보를 담은 변수, 정규화 함수가 담긴 cifar10_config.py를 import
###############################################################################
# project folders
# 데이터셋의 경로를 담은 변수
###############################################################################
# 현재 경로를 반환하는 함수
def get_script_directory():
path = os.getcwd()
return path
# RESNET18/files 경로를 SCRIPT_DIR로
SCRIPT_DIR = get_script_directory()
# RESNET18/files/build/dataset/cifar10을 DATASET_DIR로
DATASET_DIR = os.path.join(SCRIPT_DIR, "./build/dataset/cifar10")
# train, validation, test, calibration 폴더 경로
TRAIN_DIR = os.path.join(DATASET_DIR, "train")
VALID_DIR = os.path.join(DATASET_DIR, "valid")
TEST_DIR = os.path.join(DATASET_DIR, "test")
CALIB_DIR = os.path.join(DATASET_DIR, "calib")
# 원본: CALIB_DIR = os.path.join(SCRIPT_DIR, "./build/dataset/cifar10/calib")
###############################################################################
# global variables
# 데이터셋의 정보를 담은 변수
###############################################################################
# 검증셋도 없고 그 검증셋의 레이블도 없기 때문에 training data로 부터 좀 빼와서 이를 사용
NUM_CLASSES = 10
NUM_VAL_IMAGES = 5000
NUM_TEST_IMAGES = 5000
NUM_TRAIN_IMAGES = 50000
# 이미지 사이즈
IMAGE_WIDTH = 32
IMAGE_HEIGHT = 32
# 0-255의 값을 0-1의 값으로 정규화 하기 위한 normalization factor to scale
NORM_FACTOR = 255.0
# CIFAR-10의 class-label 딕셔너리, class 배열
labelNames_dict = { "airplane" : 0, "automobile" : 1, "bird" : 2, "cat" : 3, "deer" : 4, "dog" : 5, "frog" : 6, "horse" : 7, "ship" : 8, "truck" : 9}
labelNames_list = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"]
###############################################################################
# global functions
# 정규화 함수
###############################################################################
def Normalize(x_test):
x_test = np.asarray(x_test) # numpy 배열로 변환
x_test = x_test.astype(np.float32) # 데이터 타입을 실수형으로 변경
x_test = x_test/NORM_FACTOR # 각 픽셀 값을 NORM_FACTOR로 나누어 0~1 사이의 값으로 스케일링
x_test = x_test -0.5 # 각 픽셀 값을 0.5만큼 빼서 -0.5~0.5 사이의 값으로 이동
out_x_test = x_test *2 # 각 픽셀 값을 2배하여 -1~1 사이의 값으로 변환
return out_x_test
###############################################################################
# 필요한 폴더 생성
###############################################################################
# train, validation, test, calibration 폴더 경로를 cfg(cifar10_config.py)로부터 불러옴
DATASET_DIR = cfg.DATASET_DIR # CIFAR-10 데이터셋의 최상위 폴더 경로
TRAIN_DIR = cfg.TRAIN_DIR # 학습 데이터를 저장할 폴더 경로
VALID_DIR = cfg.VALID_DIR # 검증 데이터를 저장할 폴더 경로
TEST_DIR = cfg.TEST_DIR # 테스트 데이터를 저장할 폴더 경로
CALIB_DIR = cfg.CALIB_DIR # calibration 데이터를 저장할 폴더 경로
# 폴더 경로들을 리스트에 담음
dir_list = [DATASET_DIR, TRAIN_DIR, VALID_DIR, TEST_DIR, CALIB_DIR]
# 이전 데이터를 지움
for dir in dir_list: # 각 폴더 이름에 대해 반복
if (os.path.exists(dir)): # 폴더가 이미 존재한다면
shutil.rmtree(dir) # 폴더를 삭제함
os.makedirs(dir) # 폴더를 새로 생성함
print("Directory" , dir , "created ") # 생성된 폴더의 이름을 출력함
# 폴더 경로들을 리스트에 담음
dir_list = [TRAIN_DIR, VALID_DIR, TEST_DIR, CALIB_DIR]
# 각 폴더에 대해 반복
for dir in dir_list:
labeldirs = cfg.labelNames_list # CIFAR-10 데이터셋의 클래스 이름들을 리스트에 담음
for labldir in labeldirs: # 각 클래스 이름에 대해 반복
newdir = dir + "/" + labldir # 폴더 안에 클래스 이름으로 서브 폴더를 만듦
os.makedirs(newdir, exist_ok=True) # 서브 폴더를 생성함 (이미 존재하면 무시)
print("subDirectory" , newdir , "created ") # 생성된 서브 폴더의 이름을 출력함
###############################################################################
# calibration 데이터셋 이미지들의 리스트를 담을 파일(calib_list.txt) 생성
###############################################################################
# 이미지 파일의 목록을 저장할 텍스트 파일의 이름을 IMAGE_LIST_FILE 변수에 저장
IMAGE_LIST_FILE = "calib_list.txt"
# 저장할 폴더의 경로와 파일의 이름을 결합
# 파일을 쓰기 모드로 열고, 파일 객체를 f 변수에 저장
f = open(os.path.join(CALIB_DIR, IMAGE_LIST_FILE), 'w')
# imgList라는 이름의 list객체 생성
imgList = list()
###############################################################################
# CIFAR10 Dataset 다운로드
###############################################################################
# Each image is 32x32x3ch with 8bits x 1ch
# 색 하나당 8bit, 컬러, 해상도 32x32
# tensorflow.keras.datasets 모듈의 cifar10 함수를 호출하여 CIFAR-10 데이터셋을 cifar10_dataset 변수에 저장
cifar10_dataset = cifar10.load_data()
# cifar10_dataset 변수를 튜플로 분해
# 학습셋 이미지 데이터: x_train, 학습셋 레이블: y_train
# 테스트셋 이미지 데이터: x_test, 테스트셋 레이블: y_test
(x_train, y_train), (x_test, y_test) = cifar10_dataset
# 학습셋 이미지 개수 출력
print("{n} images in original train dataset".format(n=x_train.shape[0]))
# 테스트셋 이미지 개수 출력
print("{n} images in original test dataset".format(n=x_test.shape[0]))
# x_train.shape: (50000, 32, 32, 3)
# x_test.shape: (10000, 32, 32, 3)
########################################################################################
# convert in RGB channels (remember that OpenCV works in BGR)
# and fill the "train", "cal" and "test" folders without any classes imbalance
########################################################################################
counter1 = np.array([0,0,0,0,0,0,0,0,0,0], dtype="uint32") # 각 클래스별로 몇 개의 이미지가 있는지 세기 위한 카운터 배열
num_train = 0 # 학습셋 이미지 개수
for i in range (0, x_train.shape[0]): # 학습셋의 모든 이미지에 대해 반복
# 이미지의 레이블 네임을 추출해서 class_name 변수에 저장
class_name = cfg.labelNames_list[int(y_train[i])]
# 학습셋으로 사용할 이미지 파일 이름
filename = os.path.join(TRAIN_DIR, class_name+"/" + class_name+"_"+str(i)+".png")
# 클래스이름의 폴더 # 클래스이름_번호.png
# 이미지의 각 픽셀의 색 배열 순서를 R, G ,B에서 B, G, R로 변환
rgb_image = x_train[i].astype("uint8") # 이미지 데이터를 uint8 타입으로 변환
R,G,B = cv2.split(rgb_image) # 이미지 데이터를 R, G, B 채널로 분리
bgr_image = cv2.merge([B,G,R]) # 이미지 데이터를 B, G, R 채널로 병합 (OpenCV는 픽셀의 색 배열 순서를 BGR 순서로 다룸)
# TRAIN_DIR에 이미지 저장
cv2.imwrite(filename, bgr_image) # 이미지 파일로 저장
# 처음 1000개의 이미지들도 CALIB_DIR에 저장
if (i < 1000):
# 검증셋으로 사용할 이미지 파일 저장
filename2= os.path.join(CALIB_DIR, class_name+"/"+class_name+"_"+str(i)+".png")
cv2.imwrite(filename2, bgr_image)
# 이미지 파일의 상대 경로를 배열 imgList에 추가
local_filename = class_name+"/"+class_name+"_"+str(i)+".png"
imgList.append(local_filename)
counter1[ int(y_train[int(i)]) ] = counter1[ int(y_train[int(i)]) ] + 1 # 해당 클래스의 카운터 증가
num_train = num_train + 1 # 학습셋 이미지 개수를 증가
for i in range(0, len(imgList)): # 검증셋의 모든 이미지에 대해 반복
f.write(imgList[i]+"\n") # 이미지 파일의 상대 경로를 텍스트 파일에 기록
f.close() # 텍스트 파일 닫기
for i in range (0, x_test.shape[0]): # testset의 모든 이미지에 대해 반복
# 이미지의 레이블 네임을 추출해서 class_name 변수에 저장
class_name = cfg.labelNames_list[int(y_test[i])]
# testset으로 사용할 이미지 파일 이름
filename3=os.path.join(TEST_DIR, class_name+"/"+class_name+'_'+str(i)+'.png')
# 클래스이름의 폴더 # 클래스이름_번호.png
rgb_image = x_test[i].astype("uint8") # 이미지 데이터를 uint8 타입으로 변환
R,G,B = cv2.split(rgb_image) # 이미지 데이터를 R, G, B 채널로 분리
bgr_image = cv2.merge([B,G,R]) # 이미지 데이터를 B, G, R 채널로 병합 (OpenCV는 픽셀의 색 배열 순서를 BGR 순서로 다룸)
# TEST_DIR에 이미지 저장
cv2.imwrite(filename3, bgr_image)
counter1[ int(y_test[int(i)]) ] = counter1[ int(y_test[int(i)]) ] +1 # 해당 클래스의 카운터 증가
print("classes histogram in train and test dataset: ", counter1) #DeBuG # 각 클래스별로 몇 개의 이미지가 있는지 출력
del rgb_image # 이미지 데이터 변수 삭제
gc.collect() # 가비지 컬렉션
########################################################################################
# testset의 이미지를 검증 및 테스트 폴더로 분할
########################################################################################
# TEST_DIR 폴더 안에 있는 모든 하위 폴더에서 png 확장자를 가진 모든 이미지 파일의 경로를 imagesList라는 배열에 저장
imagesList = [img for img in glob.glob(TEST_DIR + "/*/*.png")]
# 난수 생성기 시드 설정
seed(1)
# 이미지 목록 섞기
shuffle(imagesList)
# 검증셋에서 클래스 당 이미지 수
NVAL = cfg.NUM_VAL_IMAGES/cfg.NUM_CLASSES
# 테스트셋에서 클래스 당 이미지 수
NTEST = cfg.NUM_TEST_IMAGES/cfg.NUM_CLASSES
# 카운터 및 검증/테스트 이미지 수 초기화
counter = np.array([0,0,0,0,0,0,0,0,0,0], dtype="uint32")
num_val = 0
num_test = 0
# 이미지를 검증, 테스트 폴더로 이동
for img in imagesList:
filename = os.path.basename(img) # 경로 문자열에서 파일 이름 추출
classname = filename.split("_")[0] # 추출한 파일 이름에서 '_' 앞에 있는 클래스 이름 추출
img_orig = cv2.imread(img) # 이미지를 불러옴
label = cfg.labelNames_dict[classname] # class-label 딕셔너리를 통해 class값을 통해 label 추출
# 테스트 이미지 결정
if (counter[ label ] < NTEST): # 해당 label의 counter 값이 NTEST보다 작으면
dst_dir = TEST_DIR # 테스트용 폴더로
num_test = num_test+1 # 테스트용 이미지 수 증가
else: # 해당 label의 counter 값이 NTEST보다 크면
dst_dir = VALID_DIR # 검증용 폴더로
num_val = num_val+1 # 검증용 이미지 수 증가
# 카운터 업데이트
counter[ label ] = counter[ label ] + 1; # counter 배열의 값 증가
out_filename = os.path.join(dst_dir, cfg.labelNames_list[label]+"/"+filename) # 이동할 경로 생성
os.rename(img, out_filename) # 이미지 파일 이동
# 결과 출력
print("classes histogram in train and test dataset: ", counter)
print("num images in train folder = ", num_train)
print("num images in val folder = ", num_val)
print("num images in pred folder = ", num_test)
print("\nFINISHED CREATING DATASET\n")
# ===========================================================================
# STEP3: Train ResNet18 CNNs on CIFAR10
# ===========================================================================
run_cifar10_training(){
# floating point model training
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP3A] CIFAR10 TRAINING (way 1)"
echo "----------------------------------------------------------------------------------"
echo " "
# way1: 하드디스크에 저장된 이미지파일을 불러와서 학습, 2개의 모델 (BEST-CNN1, FINAL-CNN1)
python ./code/train1_resnet18_cifar10.py --epochs 50 | tee ./build/log/train1_resnet18_cifar10.log
mv ./build/float/train1_best_chkpt.h5 ./build/float/train1_resnet18_cifar10_best.h5
mv ./build/float/train1_final.h5 ./build/float/train1_resnet18_cifar10_final.h5
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP3B] CIFAR10 TRAINING (way 2)"
echo "----------------------------------------------------------------------------------"
echo " "
# way2: memory로 데이터를 불러와서 학습, 1개의 모델 (CNN2)
python ./code/train2_resnet18_cifar10.py --epochs 50 | tee ./build/log/train2_resnet18_cifar10.log
}
way1 은 1.Dataset 준비 단계에서 준비한 CIFAR10 dataset을 이용해서 Resnet18의 학습을 진행하며 Learning Rate Decay (학습률 감쇠)를 적용하고 optimizer으로 굳이 SGD를 쓴다.
way2는 tensorflow.keras.datasets 모듈의 cifar10을 import해서 dataset을 불러오며 Learning Rate Decay (학습률 감쇠)를 적용하지 않으며 optimizer으로 Adam을 쓴다.
way1 방식으로 코드 리뷰를 진행하며 최종에는 BEST-CNN1말고 FINAL-CNN1 모델만 컴파일을 진행하기 때문에 FINAL-CNN1 기준으로 코드리뷰를 진행한다.
python ./code/train1_resnet18_cifar10.py --epochs 50 | tee ./build/log/train1_resnet18_cifar10.log
# =========================================================================================
# import dependencies
# =========================================================================================
import matplotlib # 데이터 시각화 라이브러리
matplotlib.use("Agg") # 백그라운드에서 그림을 저장하는 백엔드
import matplotlib.pyplot as plt # 그래프 그리는 함수 제공
from config import cifar10_config as cfg # cifar10_config: CIFAR10 데이터셋 설정 관리 모듈
print(cfg.SCRIPT_DIR) # RESNET18/files 경로 출력
from sklearn.metrics import classification_report
# sklearn.metrics: 분류, 회귀, 클러스터링 알고리즘 성능 측정 도구 제공
# classification_report: 각 클래스에 대한 정밀도(precision), 재현율(recall), F1 점수(f1-score), 지원도(support)를 계산
import numpy as np # 수치 계산 라이브러리
import cv2 # 이미지 처리, 컴퓨터 비전 라이브러리
from datetime import datetime # 날짜, 시간 라이브러리
import os # 운영 체제와의 상호 작용 라이브러리
import argparse # 명령줄 인수 처리 라이브러리
from random import seed, random, shuffle # 난수 생성 라이브러리
import glob # 디렉토리 와일드카드 검색을 통한 파일 목록 생성
import tensorflow as tf # 딥러닝 모델 구현 및 학습 라이브러리
from tensorflow import keras # 딥러닝 모델 구현을 위한 텐서플로우 고수준 API
from tensorflow.keras import backend as K # 텐서플로우 백엔드 함수
from tensorflow.keras.utils import plot_model, to_categorical
# plot_model: 모델 구조 시각화 함수
# to_categorical: 정수 레이블을 이진 클래스 행렬로 변환하는 함수
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# ImageDataGenerator: 데이터 증강을 위한 클래스
from tensorflow.keras import optimizers
# optimizers: 다양한 최적화 알고리즘 제공 모듈
from tensorflow.keras.optimizers import SGD
# SGD: 확률적 경사 하강법 최적화 알고리즘 클래스
from tensorflow.keras.callbacks import ModelCheckpoint, TensorBoard, LearningRateScheduler
# ModelCheckpoint: 에폭마다 모델 저장 콜백
# TensorBoard: 텐서보드 사용하여 학습 과정 모니터링 콜백
# LearningRateScheduler: 학습률 동적 조정 콜백
from tensorflow.keras.datasets import cifar10
# cifar10: CIFAR10 이미지 분류 데이터셋
from tensorflow.keras.preprocessing.image import img_to_array
# img_to_array: PIL Image 인스턴스를 넘파이 배열로 변환하는 함수
from classification_models.keras import Classifiers
# Classifiers: 이미지넷(ImageNet) 데이터셋에 대해 사전 학습된 분류 모델들을 제공, 전이학습(Transfer Learning)을 위해 필요
# =========================================================================================
# Get Input Arguments
# =========================================================================================
def get_arguments():
"""Parse all the arguments.
Returns:
A list of parsed arguments.
하는 일
1. argparse.ArgumentParser 객체 생성
2. add_argument 메서드로 객체에 인수를 추가한 후
3. parse_args 메서드를 호출하여 인수를 파싱하고
4. 각 인수의 이름과 값을 Namespace 객체 속성의 이름과 값에 각각 넣어서 반환
"""
# 명령줄 인수를 처리하는 데 사용하는 argparse.ArgumentParser 객체 생성
ap = argparse.ArgumentParser(description="TF2 ResNet18 Training on Cifar10 Dataset stored as files")
# add_argument 메서드를 사용하여 인수를 추가
# 모델의 가중치가 담긴 HDF5 포맷의 파일을 저장할 경로 설정
ap.add_argument("-w", "--weights", default="build/float", help="path to best model HDF5 weights file")
# ap.add_argument("-n", "--network", default="ResNet18_cifar10_train1", help="input CNN")
# ap.add_argument("-d", "--dropout", type=int, default=-1, help="whether or not Dropout should be used")
# ap.add_argument("-bn", "--BN", type=int, default=-1, help="whether or not BN should be used")
# Epochs 설정
ap.add_argument("-e", "--epochs", type=int, default=50, help="# of epochs")
# Batch size 설정
ap.add_argument("-bs", "--batch_size",type=int, default=256, help="size of mini-batches passed to network")
# 사용할 GPU 선택
ap.add_argument("-g", "--gpus", type=str, default="0", help="choose gpu devices.")
# 초기 학습률 설정
ap.add_argument("-l", "--init_lr", type=float, default=0.01, help="initial Learning Rate")
# 인수를 파싱하고 결과를 반환
return ap.parse_args()
args = vars(get_arguments()) # vars(get_arguments()): Namespace 객체의 속성을 딕셔너리로 변환
# args는 딕셔너리로 인수의 이름을 키로, 인수의 값을 값으로 가짐
args2 = get_arguments() # args2는 Namespace 객체
# 각 요소의 키와 값을 출력
for key, val in args2._get_kwargs(): # args2._get_kwargs()는 args2(Namespace 객체)의 모든 속성을 키-값 쌍의 리스트로 반환
print(key+" : "+str(val)) # 이 리스트는 인수의 이름과 값이 튜플로 묶여 있는 요소로 구성되어 있음
# vars(): 파이썬에서 네임스페이스 객체가 가지고 있는
# 이름과 값의 매핑을 딕셔너리 형태로 담은 dict 속성을 반환하는 함수
''' vars() 사용 예시
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 20) # p는 Namespace 객체
print(vars(p))
# {'name': 'Alice', 'age': 20}
print(vars(Person))
# {'__module__': '__main__', '__init__': <function Person.__init__ at 0x7f9c1c3d8f70>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(vars())
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f9c1c3d8f40>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'test.py', '__cached__': None, 'Person': <class '__main__.Person'>, 'p': <__main__.Person object at 0x7f9c1c3d8fa0>}
'''
# =========================================================================================
# Global Variables
# =========================================================================================
print(cfg.SCRIPT_DIR) # 스크립트가 저장된 경로 RESNET18/files 출력
os.environ["CUDA_VISIBLE_DEVICES"] = args["gpus"]
# os.environ: 운영 체제의 환경 변수에 접근하기 위한 함수
''' 예시
import os
print(os.environ["HOME"]) # /home/{username}
print(os.environ.get("API_KEY")) # None
os.environ["API_KEY"] = "abc"
print(os.environ["API_KEY"]) # abc
'''
## Silence TensorFlow messages
#os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
#NETWORK = args["network"]
WEIGHTS = args["weights"] # Weights: 모델의 가중치가 담긴 HDF5 포맷의 파일을 저장할 경로
NUM_EPOCHS = args["epochs"] # Epochs: 전체 데이터셋을 한번 다 학습하는 횟수 (default: 25)
INIT_LR = args["init_lr"] # Learning rate: 모델의 가중치를 업데이트할 때 얼마나 크게 변경할지를 결정하는 파라미터 (default: 1e-2)
BATCH_SIZE = args["batch_size"] # Batch size: 학습할 때 한 번에 처리할 데이터의 개수, 배치 크기가 클수록 학습 속도가 빨라지고 메모리 사용량이 증가 (default: 32)
# =========================================================================================
# prepare your data
# =========================================================================================
print("\n[DB INFO] Creating lists of images ...\n")
########################################################
# make a list of all files currently in the TRAIN folder
########################################################
# glob: 특정 패턴과 일치하는 파일들의 목록을 반환하는 함수
# TRAIN_DIR 폴더 안에 있는 모든 png 파일의 경로를 imagesList에 저장
imagesList = [img for img in glob.glob(cfg.TRAIN_DIR + "/*/*.png")]
# 이렇게 해도 되지 않나?
# imageList = glob.glob(cfg.TRAIN_DIR + "/*/*.png")
seed(42) # 랜덤 시드를 42로 설정
shuffle(imagesList) # imagesList의 원소들의 순서를 무작위로 섞음
x_train, y_train = list(), list() # x_train과 y_train이라는 빈 리스트를 생성
# x_train: 학습용 이미지 데이터, y_train: 학습용 레이블 데이터
for img in imagesList: # imagesList에 있는 각 이미지에 대해 반복
filename = os.path.basename(img) # 이미지의 파일 이름을 filename에 저장
# os.path.basename(): 경로에서 파일 이름만 추출하는 함수
classname = filename.split("_")[0] # 파일 이름을 "_"로 분리하고 첫 번째 요소를 classname에 저장
# 문자열.split(구분자): 문자열을 특정 구분자로 나누는 함수
# print(img)
img_orig = cv2.imread(img) # 이미지를 읽어서 img_orig에 저장
# cv2.imread: 이미지 파일을 numpy 배열로 변환하는 함수
#rs_img = cv2.resize(img_orig, (256,256))
#cv2.imshow(classname, rs_img)
#cv2.waitKey(0)
img_array = img_to_array(img_orig, data_format=None) # img_orig를 텐서플로우에서 사용할 수 있는 배열로 변환
# img_to_array(): 이미지를 (높이, 너비, 채널) 형태의 배열로 변환하는 함수
x_train.append(img_array) # x_train에 img_array를 추가
# 리스트.append(원소): 리스트의 끝에 원소를 삽입하는 함수
y_train.append(cfg.labelNames_dict[classname]) # y_train에 classname에 해당하는 레이블 값을 추가
# cfg.labelNames_dict: 클래스 이름과 레이블 값을 매핑한 딕셔너리
print("[DB INFO] x_train: done...") # x_train이 완성되었다는 메시지를 출력
########################################################
# make a list of all files currently in the VALID folder
########################################################
# VALID_DIR 폴더 안에 있는 모든 png 파일의 경로를 imagesList에 저장
imagesList = [img for img in glob.glob(cfg.VALID_DIR + "/*/*.png")]
shuffle(imagesList) # imagesList를 무작위로 섞음
x_valid, y_valid = list(), list() # x_valid과 y_valid이라는 빈 리스트를 생성
# x_valid: 검증용 이미지 데이터, y_valid: 검증용 레이블 데이터
for img in imagesList: # imagesList에 있는 각 이미지에 대해 반복
filename = os.path.basename(img) # 이미지의 파일 이름을 filename에 저장
classname = filename.split("_")[0] # 파일 이름을 "_"로 분리하고 첫 번째 요소를 classname에 저장
#print(img)
# read image with OpenCV
img_orig = cv2.imread(img) # 이미지를 읽어서 img_orig에 저장
# img_orig를 텐서플로우에서 사용할 수 있는 배열로 변환
img_array = img_to_array(img_orig, data_format=None)
# x_valid에 img_array를 추가
x_valid.append(img_array)
# y_valid에 classname에 해당하는 레이블 값을 추가
y_valid.append(cfg.labelNames_dict[classname])
print("[DB INFO] x_valid: done...") # x_valid이 완성되었다는 메시지를 출력
########################################################
# make a list of all files currently in the TEST folder
########################################################
# TEST_DIR 폴더 안에 있는 모든 png 파일의 경로를 imagesList에 저장
imagesList = [img for img in glob.glob(cfg.TEST_DIR + "/*/*.png")]
shuffle(imagesList) # imagesList를 무작위로 섞음
x_test, y_test = list(), list() # x_test와 y_test라는 빈 리스트를 생성
# x_test: 테스트용 이미지 데이터, y_test: 테스트용 레이블 데이터
for img in imagesList: # imagesList에 있는 각 이미지에 대해 반복
filename = os.path.basename(img) # 이미지의 파일 이름을 filename에 저장
classname = filename.split("_")[0] # 파일 이름을 "_"로 분리하고 첫 번째 요소를 classname에 저장
#print(img)
# read image with OpenCV
img_orig = cv2.imread(img) # 이미지를 읽어서 img_orig에 저장
# img_orig를 텐서플로우에서 사용할 수 있는 배열로 변환
img_array = img_to_array(img_orig, data_format=None)
# x_test에 img_array를 추가
x_test.append(img_array)
# y_test에 classname에 해당하는 레이블 값을 추가
y_test.append(cfg.labelNames_dict[classname])
print("[DB INFO] x_test: done...") # x_test이 완성되었다는 메시지를 출력
# one-hot encode the training and testing labels
# 원-핫 인코딩: 정수형 레이블을 벡터로 변환
# 0 -> [1,0,0,0,0,0,0,0,0,0], 1 -> [0,1,0,0,0,0,0,0,0,0]
# to_categorical(레이블 리스트, 레이블 갯수): 원-핫 인코딩을 수행하는 함수
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
y_valid = to_categorical(y_valid, 10)
# check settings # DB
# assert 문: 조건이 참인지 검사하는 문, 만약 조건이 거짓이면 AssertionError를 발생시킴
# x_train의 길이가 cfg.NUM_TRAIN_IMAGES보다 큰지 확인
assert True, ( len(x_train) > cfg.NUM_TRAIN_IMAGES)
# x_test의 길이가 cfg.NUM_TRAIN_IMAGES와 cfg.NUM_VAL_IMAGES의 합보다 크거나 같은지 확인
assert True, ( len(x_test) >= (cfg.NUM_TRAIN_IMAGES+cfg.NUM_VAL_IMAGES))
# cfg.NUM_TRAIN_IMAGES와 cfg.NUM_VAL_IMAGES가 같은지 확인
assert True, ( cfg.NUM_TRAIN_IMAGES==cfg.NUM_VAL_IMAGES )
# =========================================================================================
# Pre-processing data
# =========================================================================================
print("\n[DB INFO] Preprocessing images ...\n")
x_test = np.asarray(x_test) # numpy 배열로 변환
x_train = np.asarray(x_train)
x_valid = np.asarray(x_valid)
'''
np.array(), np.asarray()의 차이점 예시
import numpy as np
a = np.array([1, 2, 3]) # numpy 배열 생성
# array: 입력이 이미 numpy 배열인 경우 입력을 복사하여 새로운 배열을 반환
b = np.array(a)
# asarray: 입력이 이미 numpy 배열인 경우 입력을 그대로 반환
c = np.asarray(a)
a[0] = 9 # a의 첫 번째 원소를 9로 변경
print(a) # [9, 2, 3]
print(b) # [1, 2, 3]
# b는 a의 복사본이므로 a의 변경에 영향을 받지 않음
print(c) # [9, 2, 3]
# c는 a와 동일한 객체이므로 a의 변경에 영향을 받음
# asarray -> array as is (있는 그대로)
'''
# Normalize and convert from BGR to RGB
# 정규화
x_train = cfg.Normalize(x_train)
x_test = cfg.Normalize(x_test)
x_valid = cfg.Normalize(x_valid)
'''
def Normalize(x_test):
x_test = np.asarray(x_test) # numpy 배열로 변환
x_test = x_test.astype(np.float32) # 데이터 타입을 실수형으로 변경
x_test = x_test/NORM_FACTOR # 각 픽셀 값을 NORM_FACTOR로 나누어 0~1 사이의 값으로 스케일링
x_test = x_test -0.5 # 각 픽셀 값을 0.5만큼 빼서 -0.5~0.5 사이의 값으로 이동
out_x_test = x_test *2 # 각 픽셀 값을 2배하여 -1~1 사이의 값으로 변환
return out_x_test
'''
# =========================================================================================
# Data Generators
# =========================================================================================
print("\n[DB INFO] Data Generators ...\n")
# 데이터 증강: 이미지를 회전, 이동, 확대/축소, 뒤집기 등의 방법으로 변형하여 데이터의 다양성을 높이는 기법
# 데이터 생성기: 이미지 데이터를 미리 메모리에 모두 로드하지 않고, 필요할 때마다 배치 단위로 생성하는 객체
# ImageDataGenerator: 이미지 데이터 생성기를 만드는 클래스
test_datagen = ImageDataGenerator() # 테스트용 데이터 생성기
train_datagen = ImageDataGenerator() # 학습용 데이터 생성기
valid_datagen = ImageDataGenerator() # 검증용 데이터 생성기
aug_datagen = ImageDataGenerator(
#rescale=1/255,
rotation_range = 5 , # 이미지를 0~5도 사이로 무작위로 회전
horizontal_flip = True , # 이미지를 수평으로 뒤집음
height_shift_range = 0.05 , # 이미지를 세로로 0.05 비율만큼 무작위로 이동
width_shift_range = 0.05 , # 이미지를 가로로 0.05 비율만큼 무작위로 이동
shear_range = 0.2 , # 이미지를 0.2 라디안만큼 무작위로 변형
zoom_range = 0.2 # 이미지를 0.8~1.2 배 사이로 무작위로 확대/축소
)
# .flow(): 이미지와 레이블의 배열을 입력받아 데이터 생성기를 반환하는 메서드
# 데이터 증강을 적용한 데이터 생성기
aug_generator = aug_datagen.flow(
x_train, y_train, # 학습용 이미지 데이터, 레이블
batch_size = BATCH_SIZE # 배치 크기: 한 번에 생성할 이미지의 개수
)
# 학습용 데이터 생성기 (데이터 증강은 적용하지 않음)
train_generator = train_datagen.flow(
x_train, y_train,
batch_size=BATCH_SIZE
)
# 검증용 데이터 생성기 (데이터 증강은 적용하지 않음)
validation_generator = valid_datagen.flow(
x_valid, y_valid,
batch_size = BATCH_SIZE
)
# 테스트용 데이터 생성기 (데이터 증강은 적용하지 않음)
pred_generator = test_datagen.flow(
x_test, y_test,
batch_size=1 # 배치 크기: 1, 한 번에 하나의 이미지만 생성
)
# =========================================================================================
# CallBack Functions
# =========================================================================================
print("\n[DB INFO] CallBack Functions ...\n")
# construct the callback to save only the *best* model to disk
# based on the validation accuray
# 모델의 가중치를 저장할 파일의 경로를 fname에 저장
# os.path.sep.join: 주어진 리스트의 원소들을 경로 구분자로 연결하는 함수
# WEIGHTS: 가중치를 저장할 폴더의 경로
fname = os.path.sep.join([WEIGHTS, "train1_best_chkpt.h5"])
# ModelCheckpoint는 모델의 가중치를 파일로 저장하는 콜백 함수
checkpoint = ModelCheckpoint(
filepath = fname ,
monitor = "val_accuracy" , # 모니터링할 지표: 검증 정확도
mode = "max" , # 값이 최대가 되는 모델을 저장하도록 설정
save_best_only = True , # 가장 좋은 성능을 낸 모델만 저장하도록 설정
verbose = 1 # 저장할 때 메시지를 출력하도록 설정
)
# 다항식 감쇠(polynomial decay)를 적용하는 함수
# 다항식 감쇠: 학습률을 에폭에 따라 점차 줄여나가는 방법
# 학습률: 모델의 가중치를 업데이트할 때 얼마나 크게 변경할지를 결정하는 파라미터
# initialize the maximum number of epochs, base learning rate, and power of the polynomial
def poly_decay(epoch):
maxEpochs = NUM_EPOCHS # maxEpochs: 모델을 학습할 때 사용할 에폭의 수,
baseLR = INIT_LR # baseLR: 초기 학습률
power = 1.0 # power: 다항식의 차수
# power가 1이면 선형 감쇠(linear decay)
# 다항식 감쇠 공식에 따라 현재 에폭(epoch)에 해당하는 학습률을 계산
# 새로운 학습률을 alpha에 저장
alpha = baseLR * (1 - (epoch / float(maxEpochs))) ** power
return alpha # 새로운 학습률을 반환
# 콜백 함수들을 담은 리스트
callbacks_list = [checkpoint, LearningRateScheduler(poly_decay)]
# LearningRateScheduler는 학습률을 에폭마다 업데이트하는 콜백 함수
# poly_decay 함수를 인자로 전달하여 학습률을 다항식 감쇠로 조절하도록 설정
# =========================================================================================
# Get ResNet18 pre-trained model
# =========================================================================================
print("\n[DB INFO] Get ResNet18 pretrained model...\n")
# original imagenet-based ResNet18 model
# Classifiers 모듈에서 ResNet18 클래스와 전처리 함수를 가져옴
ResNet18, preprocess_input = Classifiers.get("resnet18")
# 원본 ResNet18 모델
# orig_model = ResNet18(
# (224, 224, 3), # 입력 이미지의 크기: 224x224x3
# weights="imagenet" # 가중치는 이미지넷 데이터셋에 대해 학습된 것을 사용
# )
# print(orig_model.summary()) # 원본 ResNet18 모델의 구조를 출력
# build new model for CIFAR10
base_model = ResNet18(
input_shape = (32,32,3) , # 입력 이미지의 크기: 32x32x3
weights = "imagenet" , # 가중치는 이미지넷 데이터셋에 대해 학습된 것을 사용
include_top = False # include_top: 마지막 층을 포함할지 여부
) # False로 설정하면 마지막 층을 제외하고 모델을 생성
# CIFAR10 데이터셋에 맞게 새로운 마지막 층을 추가하기 위함
#next to lines commented: the training would become awful
##for layer in base_model.layers:
## layer.trainable = False # 모델의 각 층을 학습 불가능하게 만듦
# True일 경우엔 사전 학습된 가중치를 고정하고, 새로운 마지막 층만 학습
# False인 이유: CIFAR10 데이터셋과 이미지넷 데이터셋이 너무 다르기 때문에
# 모델의 층들을 일부 학습 가능하게 해야 함
# 첫번째 괄호: GlobalAveragePooling2D() 객체 생성
x_layer = keras.layers.GlobalAveragePooling2D()(base_model.output) # 모델의 출력에 전역 평균 풀링 층 추가
# 두번째 괄호: 생성된 객체의 call 메서드 호출
# GlobalAveragePooling2D: 각 채널별로 평균 값을 구하는 층, 마지막 층에 연결하기 위해 차원을 줄이는 역할
# GlobalAveragePooling층의 출력에 fully connected layer을 추가
# fully connected layer은 CIFAR10 데이터셋의 클래스 개수의 노드로 구성됨
# 활성화 함수로 소프트맥스 함수를 사용하여 각 클래스에 대한 확률을 출력
# 다항분류는 softmax, 이항분류는 sigmoid
output = keras.layers.Dense(cfg.NUM_CLASSES, activation="softmax")(x_layer)
# base_model과 새로운 마지막 층을 연결한 모델 생성
model = keras.models.Model(inputs=[base_model.input], outputs=[output])
#model.summary() # 모델의 구조를 출력
# =========================================================================================
# Training for 50 epochs on Cifar-10
# =========================================================================================
print("\n[DB INFO] Training the Model...\n")
# 최적화 알고리즘으로 SGD(확률적 경사 하강법)를 사용
opt = SGD(learning_rate=INIT_LR, momentum=0.9)
# learning_rate (학습률): 가중치를 업데이트할 때 얼마나 크게 변경할지를 결정하는 파라미터
# momentum (모멘텀): 이전 업데이트의 방향과 크기를 반영하여 가중치를 업데이트
#opt = SGD(lr=INIT_LR, momentum=0.9, decay=INIT_LR / NUM_EPOCHS) # 학습률 감쇠(decay)를 적용한 SGD를 사용
# decay (학습률 감쇠): 학습이 진행될수록 학습률을 점차 줄여나가는 방법
# 학습률을 에폭마다 INIT_LR / NUM_EPOCHS 만큼 줄이도록 설정
# 모델 컴파일: 모델의 구조와 손실 함수, 최적화 알고리즘, 평가 지표 등을 설정하는 과정
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
# 손실 함수: 다항 분류에 사용되는 교차 엔트로피 오차 함수, 최적화 알고리즘: SGD, 평가 지표: 정확도
# datetime.now(): 현재 시간을 datetime 객체로 반환하는 함수
startTime1 = datetime.now() #DB # 모델 학습의 시작 시간을 startTime1에 저장
# run training/media/danieleb/DATA$
# .fit() 모델을 학습하는 메서드
H = model.fit( # 모델을 학습
# H: 학습 과정에서의 손실과 정확도 등의 정보가 담김
x = aug_generator , # 데이터 증강을 적용한 데이터 생성기를 입력으로
# 스텝 수: 한 에폭에서 몇 번의 배치 학습을 수행할지를 결정하는 값
steps_per_epoch = len(x_train)//BATCH_SIZE , # 학습 데이터의 개수를 배치 크기로 나눈 몫을 스텝 수로
# 에폭 수: 전체 학습 데이터를 몇 번 반복해서 학습할지를 결정하는 값
epochs = NUM_EPOCHS ,
# 배치 크기: 한 번에 학습할 이미지의 개수
batch_size = BATCH_SIZE ,
# 검증 데이터: 학습 데이터와 별도로 학습이 진행되는 도중에 모델의 성능을 평가하기 위해 사용하는 데이터
validation_data = validation_generator , # validation_generator: 데이터 증강을 적용하지 않은 검증용 데이터 생성기
# 검증 스텝 수: 한 에폭에서 몇 번의 배치 검증을 수행할지
validation_steps = len(x_valid)//BATCH_SIZE , # 학습 데이터의 개수를 배치 크기로 나눈 몫을 검증 스텝 수로
# 콜백 함수: 모델 학습 중에 특정 시점에 실행되는 함수
# 학습 과정을 모니터링하고, 모델의 성능을 개선하고, 학습률을 조절하는 등의 작업을 수행
callbacks = callbacks_list ,
# 데이터를 섞을지 여부
shuffle = True , # 학습 데이터의 순서에 따른 편향을 방지
# 0: 아무 것도 출력하지 않음, 1: 진행 막대를 출력, 2: 에폭마다 한 줄씩 출력
verbose = 2
)
# datetime.now(): 현재 시간을 datetime 객체로 반환하는 함수
endTime1 = datetime.now() # 모델 학습의 종료 시간을 endTime1에 저장
# datetime 객체끼리 빼면 두 시간의 차이를 나타내는 timedelta 객체가 반환
diff1 = endTime1 - startTime1 # 모델 학습에 걸린 시간을 diff1에 저장
print("\n")
print("Elapsed time for Keras training (s): ", diff1.total_seconds())
print("\n")
# save CNN complete model on HDF5 file #DB
# CNN 모델의 구조와 가중치를 HDF5 파일로 저장
# os.path.sep.join(): 주어진 리스트의 원소들을 경로 구분자로 연결하는 함수
fname1 = os.path.sep.join([WEIGHTS, "train1_final.h5"]) # 모델을 저장할 파일의 경로를 fname1에 저장
# WEIGHTS: 모델을 저장할 폴더
# .save(): 모델의 구조와 가중치를 파일로 저장하는 메서드
model.save(fname1) # 모델을 fname1에 지정한 경로에 HDF5 파일로 저장
# once saved the model can be load with following commands #DB
# 저장한 모델은 다음과 같은 명령어로 불러올 수 있다.
'''
from keras.models import load_model
print("[INFO] loading pre-trained network...")
# load_model(): 파일로 저장된 모델의 구조와 가중치를 불러와서 케라스 모델 객체로 반환하는 함수
model = load_model(fname) # fname에 지정한 경로에서 HDF5 파일로 저장된 모델을 불러옴
'''
# plot the CNN model #DB
# CNN 모델의 구조를 시각화
print("\n[DB INFO] plot model...\n")
# 모델의 구조를 저장할 파일의 경로를 model_filename에 저장
# os.path.join(): 주어진 문자열들을 경로 구분자로 연결하는 함수
model_filename = os.path.join(cfg.SCRIPT_DIR, "build/log/train1_float_model.png")
# cfg.SCRIPT_DIR: 스크립트가 위치한 폴더
# 모델의 구조를 model_filename에 지정한 경로에 PNG 파일로 저장
# plot_model(): 케라스 모델의 구조를 그래프로 시각화하는 함수
# show_shapes: 각 층의 입력과 출력의 형태를 표시할지 여부를 결정하는 인자
plot_model(model, to_file=model_filename, show_shapes=True)
# =========================================================================================
# Prediction
# =========================================================================================
print("\n[DB INFO] evaluating network on Test and Validation datasets...\n")
# Evaluate model accuracy with test set
# .evaluate(): 모델의 손실과 정확도를 반환하는 메서드
# 검증 데이터에 대해 모델의 성능을 평가
scores = model.evaluate(x_valid, y_valid, batch_size=BATCH_SIZE) #MH
# %.3f: 소수점 세 자리까지 표시하는 형식 지정자
print('Validation Loss: %.3f' % scores[0]) #MH
print('validation Accuracy: %.3f' % scores[1]) #MH
# 테스트 데이터에 대해 모델의 성능을 평가
scores = model.evaluate(x_test, y_test, batch_size=BATCH_SIZE)
print('Test Loss: %.3f' % scores[0]) #MH
print('Test Accuracy: %.3f' % scores[1]) #MH
# make predictions on the test set
# 테스트 데이터에 대해 모델의 예측을 수행
# .predict(): 모델의 출력을 반환하는 메서드
preds = model.predict(x_test) # preds에 모델의 예측 결과가 저장
# show a nicely formatted classification report
# classification_report(): 정밀도, 재현율, F1 점수 등의 평가 지표를 계산하고, 표 형식으로 보여주는 함수
# argmax(): 배열에서 가장 큰 값을 갖는 값의 인덱스를 반환하는 함수
# 각 클래스에 대응하는 원소 중 하나만 1이고 나머지는 0인 배열로 표현하는
# 원-핫 인코딩 방식으로 저장되어 있기 때문에 argmax() 함수를 통해 레이블 값을 얻음
print(classification_report(
y_test.argmax(axis=1) , # 테스트 데이터의 실제 레이블을 나타내는 배열
preds.argmax(axis=1) , # 테스트 데이터의 예측 레이블을 나타내는 배열
target_names=cfg.labelNames_list # CIFAR-10 데이터셋의 클래스 이름을 담은 리스트
))
# =========================================================================================
# Plot files
# =========================================================================================
# 모델의 학습 과정을 시각화하는 함수 정의
# history: fit 메서드의 반환값, 학습 과정에서의 손실과 정확도 등의 정보가 담긴 객체
def plotmodelhistory(history):
fig, axs = plt.subplots(1,2,figsize=(15,5)) # 1행 2열의 서브플롯을 생성
# fig: 전체 그림 객체, axs: 각 서브플롯의 축 객체
# figsize: 전체 그림의 크기를 인치 단위로 설정하는 인자
# summarize history for accuracy
# plot(): 선 그래프를 그리는 함수
axs[0].plot(history.history["accuracy"]) # 첫 번째 서브플롯에 학습 데이터의 정확도를 그림
# history.history["accuracy"]: 학습 데이터의 정확도를 나타내는 리스트
axs[0].plot(history.history["val_accuracy"]) # 첫 번째 서브플롯에 검증 데이터의 정확도를 그림
# history.history["val_accuracy"]: 검증 데이터의 정확도를 나타내는 리스트
# set_title(): 서브플롯의 제목을 설정하는 함수
# set_ylabel(): y축 레이블을 설정하는 함수
# set_xlabel(): x축 레이블을 설정하는 함수
axs[0].set_title("Model Accuracy") # 첫 번째 서브플롯의 제목을 설정
axs[0].set_ylabel("Accuracy") # 첫 번째 서브플롯의 y축 레이블을 설정
axs[0].set_xlabel("Epoch") # 첫 번째 서브플롯의 x축 레이블을 설정
# legend(): 범례를 설정하는 함수
# ["train", "validate"]는 각 선 그래프의 이름을 나타내는 리스트
# loc은 범례의 위치를 나타내는 인자, "upper left": 왼쪽 상단
axs[0].legend(["train", "validate"], loc="upper left") # 첫 번째 서브플롯의 범례를 설정
# summarize history for loss
axs[1].plot(history.history["loss"]) # 두 번째 서브플롯에 학습 데이터의 손실을 그림
# history.history["loss"]: 학습 데이터의 손실을 나타내는 리스트
axs[1].plot(history.history["val_loss"]) # 두 번째 서브플롯에 검증 데이터의 손실을 그림
# history.history["val_loss"]는 검증 데이터의 손실을 나타내는 리스트
axs[1].set_title("Model Loss") # 두 번째 서브플롯의 제목을 설정
axs[1].set_ylabel("Loss") # 두 번째 서브플롯의 y축 레이블을 설정
axs[1].set_xlabel("Epoch") # 두 번째 서브플롯의 x축 레이블을 설정
axs[1].legend(["train", "validate"], loc="upper left") # 두 번째 서브플롯의 범례를 설정
# plt.show(): 그래프를 화면에 보여주는 함수
plt.show() # 그린 그래프를 화면에 보여줌
# list all data in history
print(H.history.keys()) # H.history 객체에 담긴 정보의 키를 출력
# H.history.keys(): H.history 객체의 키를 나타내는 리스트
plotmodelhistory(H)
# plotmodelhistory 함수를 호출하여 학습 과정에서의 손실과 정확도 등의 정보가 담긴 H 를 인자로 전달
# 그래프를 저장할 파일의 경로를 plot_filename에 저장
# os.path.join(): 주어진 문자열들을 경로 구분자로 연결하는 함수
# cfg.SCRIPT_DIR는 스크립트가 위치한 폴더의 이름
plot_filename = os.path.join(cfg.SCRIPT_DIR, "build/log/train1_history.png")
# plt.savefig(): 그래프를 파일로 저장하는 함수
plt.savefig(plot_filename) # 그래프를 plot_filename에 지정한 경로에 PNG 파일로 저장
# plot the training loss and accuracy
N = NUM_EPOCHS
# N: 그래프의 x축 범위를 나타내는 변수
# NUM_EPOCHS: 모델을 학습할 때 사용한 에폭의 수를 나타내는 변수
# plt.style.use(): 그래프의 스타일을 설정하는 함수
plt.style.use("ggplot") # 그래프의 스타일을 ggplot으로 설정
# ggplot: R에서 사용되는 그래프 스타일로, 색상과 선의 종류 등을 미리 정의해놓은 스타일
# plt.figure(): 새로운 그림 객체를 생성하는 함수
plt.figure() # 새로운 그림 객체를 생성
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss") # 학습 데이터의 손실을 그림
# np.arange(): 0부터 N-1까지의 정수를 나타내는 배열을 반환하는 함수
# label: 선 그래프의 이름을 나타내는 인자
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss") # 검증 데이터의 손실을 그림
plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc") # 학습 데이터의 정확도를 그림
plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc") # 검증 데이터의 정확도를 그림
# plt.title(): 그래프의 제목을 설정하는 함수
plt.title("Training Loss and Accuracy on Dataset") # 그래프의 제목 설정
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left") # "lower left": 왼쪽 하단
# 그래프를 "./doc/images/train1_resnet18_cifar10_network_plot.png"에 PNG 파일로 저장
plt.savefig("./doc/images/" + "train1_resnet18_cifar10_network" + "_plot.png")
# =========================================================================================
print("\n[DB INFO] End of ResNet18 Training1 on CIFAR10")
# ===========================================================================
# STEP4: Vitis AI Quantization of ResNet18 on CIFAR10
# ===========================================================================
quantize_resnet18_cifar10(){
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP4A] QUANTIZE CIFAR10 TRAINED CNN1 MODELS"
echo "----------------------------------------------------------------------------------"
echo " "
echo "[DB INFO STEP4A-1] MODEL INSPECTION"
echo " "
# way1: 하드디스크에 저장된 이미지파일을 불러와서 학습했음, 2개의 모델 (BEST-CNN1, FINAL-CNN1)
python ./code/inspect_resnet18_cifar10.py --float_file ./build/float/train1_resnet18_cifar10_final.h5
mv build/log/inspect_results.txt build/log/inspect_results_train1_resnet18_cifar10_final.txt
mv build/log/model.svg build/log/model_train1_resnet18_cifar10_final.svg
echo " "
echo "[DB INFO STEP4A-2] EFFECTIVE QUANTIZATION OF FINAL-CNN1 MODEL"
echo " "
python ./code/vai_q_resnet18_cifar10.py --float_file ./build/float/train1_resnet18_cifar10_final.h5 --quant_file ./build/quantized/q_train1_resnet18_cifar10_final.h5
echo " "
echo "[DB INFO STEP4A-3] EFFECTIVE QUANTIZATION OF BEST-CNN1 MODEL"
echo " "
python ./code/vai_q_resnet18_cifar10.py --float_file ./build/float/train1_resnet18_cifar10_best.h5 --quant_file ./build/quantized/q_train1_resnet18_cifar10_best.h5
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP4B] QUANTIZE CIFAR10 TRAINED CNN2 MODEL"
echo "----------------------------------------------------------------------------------"
echo " "
echo "[DB INFO STEP4B-1] MODEL INSPECTION"
echo " "
# way2: memory로 데이터를 불러와서 학습했음, 1개의 모델 (CNN2)
python ./code/inspect_resnet18_cifar10.py --float_file ./build/float/train2_resnet18_cifar10_float.h5
mv build/log/inspect_results.txt build/log/inspect_results_train2_resnet18_cifar10_float.txt
mv build/log/model.svg build/log/model_train2_resnet18_cifar10_float.svg
echo " "
echo "[DB INFO STEP4B-2] EFFECTIVE QUANTIZATION"
echo " "
python ./code/vai_q_resnet18_cifar10.py --float_file ./build/float/train2_resnet18_cifar10_float.h5 --quant_file ./build/quantized/q_train2_resnet18_cifar10.h5
}
여기서 FINAL-CNN1과 관련된것만 추린다면
# INSPECTION OF FINAL-CNN1 MODEL
python ./code/inspect_resnet18_cifar10.py \
--float_file ./build/float/train1_resnet18_cifar10_final.h5
mv build/log/inspect_results.txt \
build/log/inspect_results_train1_resnet18_cifar10_final.txt
mv build/log/model.svg \
build/log/model_train1_resnet18_cifar10_final.svg
# QUANTIZATION OF FINAL-CNN1 MODEL
python ./code/vai_q_resnet18_cifar10.py \
--float_file ./build/float/train1_resnet18_cifar10_final.h5 \
--quant_file ./build/quantized/q_train1_resnet18_cifar10_final.h5
INSPECTION OF FINAL-CNN1 MODEL
# =========================================================================================
# import dependencies
# =========================================================================================
from config import cifar10_config as cfg # cifar10_config: CIFAR10 데이터셋 설정 관리 모듈
import argparse # 명령줄 인수 처리 라이브러리
import os # 운영 체제와의 상호 작용 라이브러리
from tensorflow import keras # 딥러닝 모델 구현을 위한 텐서플로우 고수준 API
# =========================================================================================
# Get Input Arguments
# =========================================================================================
def get_arguments():
"""Parse all the arguments.
Returns:
A list of parsed arguments.
하는 일
1. argparse.ArgumentParser 객체 생성
2. add_argument 메서드로 객체에 인수를 추가한 후
3. parse_args 메서드를 호출하여 인수를 파싱하고
4. 각 인수의 이름과 값을 Namespace 객체 속성의 이름과 값에 각각 넣어서 반환
"""
# 명령줄 인수를 처리하는 데 사용하는 argparse.ArgumentParser 객체 생성
parser = argparse.ArgumentParser(description="Vitis AI TF2 Quantization of ResNet18")
# model config
parser.add_argument("--float_file", type=str, default="./build/float/train2_resnet18_cifar10.h5",
help="h5 floating point file full path name")
# 인수를 파싱하고 결과를 반환
return parser.parse_args()
args = get_arguments() # args는 Namespace 객체
# =======================================================================================
# Global Variables
# =======================================================================================
print(cfg.SCRIPT_DIR) # 스크립트가 저장된 경로 RESNET18/files 출력
# os.path.join(): 주어진 문자열들을 경로 구분자로 연결하는 함수
FLOAT_HDF5_FILE = os.path.join(cfg.SCRIPT_DIR, args.float_file) # 학습된 모델의 파일 경로
# =======================================================================================
# Get the trained floating point model
# =======================================================================================
float_model = keras.models.load_model(FLOAT_HDF5_FILE) # 학습된 ResNet18 모델을 불러옴
# =======================================================================================
# Vitis AI Model Inspector
# =======================================================================================
print("\n[DB INFO] Vitis AI Model Inspector...\n") # Vitis AI Model Inspector를 사용한다는 메시지 출력
from tensorflow_model_optimization.quantization.keras import vitis_inspect # Vitis AI Model Inspector의 기능을 제공하는 모듈 임포트
#inspector = vitis_inspect.VitisInspector(target="DPUCZDX8G") # Vitis AI Model Inspector의 객체 생성, target은 DPUCZDX8G라는 FPGA 장치
inspector = vitis_inspect.VitisInspector(target="/opt/vitis_ai/compiler/arch/DPUCZDX8G/ZCU102/arch.json") # Vitis AI Model Inspector의 객체 생성
'''
# /opt/vitis_ai/compiler/arch/DPUCZDX8G/ZCU102/arch.json
{
"target": "DPUCZDX8G_ISA1_B4096"
}
'''
# Vitis AI Model Inspector의 결과를 저장할 파일 경로
filename_dump = os.path.join(cfg.SCRIPT_DIR, "build/log/inspect_results.txt")
# Vitis AI Model Inspector의 모델 구조를 시각화할 파일 경로
filename_svg = os.path.join(cfg.SCRIPT_DIR, "build/log/model.svg")
inspector.inspect_model(
model = float_model , # 분석할 모델
input_shape = [1, 32, 32, 3] , # 모델의 입력 형태
plot = True , # 모델 구조를 시각화할지 여부
plot_file = filename_svg , # 모델 구조를 시각화할 파일 경로
dump_results = True , # 결과를 파일로 저장할지 여부
dump_results_file = filename_dump , # 결과를 저장할 파일 경로
verbose = 0 # 출력할 상세 정보의 수준
)
QUANTIZATION OF FINAL-CNN1 MODEL
# =======================================================================================
# import dependencies
# =======================================================================================
from config import cifar10_config as cfg # CIFAR10 데이터셋에 대한 설정
import argparse # 명령줄 인수 처리 라이브러리. 사용자가 스크립트를 실행할 때 인수를 지정할 수 있게 함
import os # 운영 체제와의 상호 작용 라이브러리. 파일 및 디렉토리 작업을 수행하는 데 사용
import numpy as np # 수치 계산 라이브러리. 배열 및 행렬 연산에 사용
from sklearn.model_selection import train_test_split # 데이터를 학습 및 테스트 셋으로 분할하는 함수
from sklearn.preprocessing import OneHotEncoder # 범주형 변수를 이진 벡터로 변환하는 데 사용
import tensorflow as tf # 딥러닝 모델 구현 및 학습 라이브러리.
from tensorflow import keras # 딥러닝 모델 구현을 위한 텐서플로우 고수준 API.
# cifar10: 10개의 클래스를 가진 60000개의 32x32 컬러 이미지를 포함하는 데이터셋
from tensorflow.keras.datasets import cifar10
# =======================================================================================
# Get Input Arguments
# =======================================================================================
def get_arguments():
"""
Parse all the arguments.
Returns:
A list of parsed arguments.
하는 일
1. argparse.ArgumentParser 객체 생성
2. add_argument 메서드로 객체에 인수를 추가한 후
3. parse_args 메서드를 호출하여 인수를 파싱하고
4. 각 인수의 이름과 값을 Namespace 객체 속성의 이름과 값에 각각 넣어서 반환
"""
# 명령줄 인수를 처리하는 데 사용하는 argparse.ArgumentParser 객체 생성
# 인수로는 프로그램의 설명을 넣어줌
parser = argparse.ArgumentParser(description="Vitis AI TF2 Quantization of ResNet18 trained on CIFAR10")
# add_argument 메서드를 사용하여 인수를 추가
# 인수의 이름, 타입, 기본값, 도움말을 지정할 수 있음
parser.add_argument("--float_file", type=str, default="./build/float/train2_resnet18_cifar10.h5",
help="h5 floating point file full path name")
# 사용할 GPU 선택
# 인수의 이름은 --gpus, 타입은 문자열, 기본값은 "0", 도움말은 "choose gpu devices."
parser.add_argument("--gpus", type=str, default="0",
help="choose gpu devices.")
# 인수의 이름은 --quant_file, 타입은 문자열, 기본값은 "./build/quantized/q_train2_resnet18_cifar10.h5", 도움말은 "quantized model file full path ename "
parser.add_argument("--quant_file", type=str, default="./build/quantized/q_train2_resnet18_cifar10.h5",
help="quantized model file full path ename ")
# 인수를 파싱하고 결과를 반환
# 반환값은 Namespace 객체로, 인수의 이름과 값을 속성으로 가짐
return parser.parse_args()
args = get_arguments() # args는 Namespace 객체
# get_arguments 함수를 호출하여 인수를 파싱하고, args 변수에 저장
# =======================================================================================
# Global Variables
# =======================================================================================
# cfg.SCRIPT_DIR: 현재 스크립트의 디렉토리
print(cfg.SCRIPT_DIR) # 스크립트가 저장된 경로 RESNET18/files 출력
# CUDA_VISIBLE_DEVICES는 GPU를 선택하는 환경 변수로, args.gpus의 값을 할당
# os.environ: 운영 체제의 환경 변수에 접근하기 위한 함수
os.environ["CUDA_VISIBLE_DEVICES"] = args.gpus
# FLOAT_HDF5_FILE: 부동 소수점 모델 파일을 불러올 경로
# QUANT_HDF5_FILE: 양자화된 모델 파일을 저장할 경로
# args.float_file과 args.quant_file은 명령줄 인수로 받은 파일 이름
FLOAT_HDF5_FILE = os.path.join(cfg.SCRIPT_DIR, args.float_file)
QUANT_HDF5_FILE = os.path.join(cfg.SCRIPT_DIR, args.quant_file)
# =======================================================================================
# prepare your data
# =======================================================================================
print("\n[DB INFO] Preparing Data ...\n")
# CIFAR-10: 10개의 클래스로 구분된 6만개의 컬러 이미지로 이루어진 데이터셋
# cifar10.load_data(): CIFAR-10 데이터셋을 불러옴
# X_train, Y_train: 학습 데이터와 레이블
# X_test, Y_test: 테스트 데이터와 레이블
(X_train, Y_train), (X_test, Y_test) = cifar10.load_data()
# X_train, X_test, Y_train의 행렬 형태 출력
# X_train: (50000, 32, 32, 3)
# X_test: (10000, 32, 32, 3)
# np.unique(): 중복되지 않는 값의 리스트를 출력
# np.unique(Y_train).shape[0]: 10
X_train.shape, X_test.shape, np.unique(Y_train).shape[0]
# one-hot encoding: 레이블을 0과 1로 이루어진 벡터로 표현하는 방법
# 클래스의 개수를 10으로 지정
n_classes = 10
# Normalize the data
# X_train과 X_test의 데이터 타입을 float32로 변환
X_train = X_train.astype("float32")
X_test = X_test.astype("float32")
# cfg.Normalize: config.py 파일에서 정의된 함수,
# 데이터를 -1과 1 사이의 값으로 정규화
X_train = cfg.Normalize(X_train)
X_test = cfg.Normalize(X_test)
# 검증 데이터: 모델의 성능을 평가하기 위한 데이터
# train_test_split(): 학습 데이터와 검증 데이터로 분리
# X_val, Y_val: 검증 데이터와 레이블
X_train, X_val, Y_train, Y_val = train_test_split(
X_train,
Y_train,
test_size = 0.2, # 검증 데이터의 비율
shuffle = True # 데이터를 섞을지 여부
)
# OneHotEncoder: one-hot encoding하는 데 사용되는 클래스
encoder = OneHotEncoder()
# encoder 객체가 Y_train의 고유한 값과 순서를 파악
encoder.fit(Y_train)
# Y_train, Y_test, Y_val을 one-hot encoding된 레이블로 변환
# transform 메서드는 sparse matrix 형태로 결과를 반환하므로,
# toarray 메서드를 사용하여 numpy array 형태로 변환
Y_train = encoder.transform(Y_train).toarray()
Y_test = encoder.transform(Y_test).toarray()
Y_val = encoder.transform(Y_val).toarray()
# =======================================================================================
# Get the trained floating point model
# =======================================================================================
# keras.models.load_model 함수를 사용하여
# FLOAT_HDF5_FILE의 경로에 저장된 부동 소수점 모델을 불러옴
# model 변수에 불러온 모델을 저장
model = keras.models.load_model(FLOAT_HDF5_FILE)
# =======================================================================================
# Prediction
# =======================================================================================
print("\n[DB INFO] Make Predictions with Float Model...\n")
## Evaluation on Training Dataset
# 학습 데이터와 레이블에 대한 모델의 성능을 평가
# ModelLoss: 모델의 손실값
# ModelAccuracy: 모델의 정확도
ModelLoss, ModelAccuracy = model.evaluate(X_train, Y_train)
print("X_Train Model Loss is {}".format(ModelLoss))
print("X_Train Model Accuracy is {}".format(ModelAccuracy))
## Evaluation on Test Dataset
# 테스트 데이터와 레이블에 대한 모델의 성능을 평가
# t_ModelLoss: 모델의 손실값
# t_ModelAccuracy: 모델의 정확도
t_ModelLoss, t_ModelAccuracy = model.evaluate(X_test, Y_test)
print("X_Test Model Loss is {}".format(t_ModelLoss))
print("X_Test Model Accuracy is {}".format(t_ModelAccuracy))
# =======================================================================================
# Vitis AI Quantization
# =======================================================================================
# Vitis AI Quantization: 부동 소수점 모델을 양자화된 모델로 변환
# 양자화된 모델을 통해 메모리와 연산량을 줄이면서 성능은 유지
print("\n[DB INFO] Vitis AI Quantization...\n")
# vitis_quantize: Vitis AI Quantization을 수행하는 데 사용되는 클래스
from tensorflow_model_optimization.quantization.keras import vitis_quantize
# vitis_quantize.VitisQuantizer(): 부동 소수점 모델을 인자로 받는 quantizer 객체를 생성
quantizer = vitis_quantize.VitisQuantizer(model)
# quantizer 객체의 quantize_model 메서드를 사용
# calib_dataset: 양자화 과정에서 사용되는 데이터셋, 학습 데이터의 첫 100개를 사용
# q_model 변수에 양자화된 모델을 저장
q_model = quantizer.quantize_model(calib_dataset=X_train[0:100]) # 부동 소수점 모델을 양자화
print("\n[DB INFO] Evaluation of Quantized Model...\n")
with vitis_quantize.quantize_scope():
# 양자화된 모델을 불러오고 컴파일하고 평가하는 데 필요한 범위를 지정
# 자원을 획득하고 사용한 후에 이를 해제하는 with문을 사용하였음
# 양자화된 모델을 컴파일
q_model.compile(
optimizer = "adam" , # 모델의 최적화 방법
loss = "categorical_crossentropy" , # 손실 함수
metrics = ["accuracy"] # 평가 지표
)
# 테스트 데이터와 레이블에 대한 양자화된 모델의 성능을 평가
# q_eval_results는 양자화된 모델의 손실값과 정확도를 나타냄
q_eval_results = q_model.evaluate(X_test, Y_test)
# 테스트 데이터에 대한 양자화된 모델의 정확도 출력
# q_eval_results[1]: 테스트 데이터에 대한 양자화된 모델의 정확도
print("\n***************** Summary *****************")
print("X_Test Quantized model accuracy: ", q_eval_results[1])
print("\n[DB INFO] Saving Quantized Model...\n")
# 양자화된 모델을 QUANT_HDF5_FILE의 경로에 저장
q_model.save(QUANT_HDF5_FILE)
# QUANT_HDF5_FILE의 경로에 저장된 양자화된 모델을 불러옴
loaded_model = keras.models.load_model(QUANT_HDF5_FILE)
# 학습 데이터와 레이블에 대한 불러온 모델의 성능을 평가
# eval_results에 모델의 손실값과 정확도가 저장
eval_results = loaded_model.evaluate(X_train, Y_train)
# 학습 데이터에 대한 모델의 정확도를 화면에 출력
print("\n***************** Summary *****************")
print("X_Train Quantized model accuracy: ", eval_results[1])
compile_resnet18_cifar10(){
#train1
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP5A] COMPILE CIFAR10 QUANTIZED CNN1 MODEL"
echo "----------------------------------------------------------------------------------"
echo " "
source ./scripts/run_compile.sh zcu102 q_train1_resnet18_cifar10_final.h5
source ./scripts/run_compile.sh vck190 q_train1_resnet18_cifar10_final.h5
source ./scripts/run_compile.sh vek280 q_train1_resnet18_cifar10_final.h5
source ./scripts/run_compile.sh vck5000 q_train1_resnet18_cifar10_final.h5
source ./scripts/run_compile.sh v70 q_train1_resnet18_cifar10_final.h5
mv ./build/compiled_zcu102/zcu102_q_train1_resnet18_cifar10_final.h5.xmodel ./target/cifar10/zcu102_train1_resnet18_cifar10.xmodel
mv ./build/compiled_vck190/vck190_q_train1_resnet18_cifar10_final.h5.xmodel ./target/cifar10/vck190_train1_resnet18_cifar10.xmodel
mv ./build/compiled_vek280/vek280_q_train1_resnet18_cifar10_final.h5.xmodel ./target/cifar10/vek280_train1_resnet18_cifar10.xmodel
mv ./build/compiled_v70/v70_q_train1_resnet18_cifar10_final.h5.xmodel ./target/cifar10/v70_train1_resnet18_cifar10.xmodel
mv ./build/compiled_vck5000/vck5000_q_train1_resnet18_cifar10_final.h5.xmodel ./target/cifar10/vck5000_train1_resnet18_cifar10.xmodel
#train2
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP5B] COMPILE CIFAR10 QUANTIZED CNN2 MODEL"
echo "----------------------------------------------------------------------------------"
echo " "
source ./scripts/run_compile.sh zcu102 q_train2_resnet18_cifar10.h5
source ./scripts/run_compile.sh vck190 q_train2_resnet18_cifar10.h5
source ./scripts/run_compile.sh vek280 q_train2_resnet18_cifar10.h5
source ./scripts/run_compile.sh vck5000 q_train2_resnet18_cifar10.h5
source ./scripts/run_compile.sh v70 q_train2_resnet18_cifar10.h5
mv ./build/compiled_zcu102/zcu102_q_train2_resnet18_cifar10.h5.xmodel ./target/cifar10/zcu102_train2_resnet18_cifar10.xmodel
mv ./build/compiled_vck190/vck190_q_train2_resnet18_cifar10.h5.xmodel ./target/cifar10/vck190_train2_resnet18_cifar10.xmodel
mv ./build/compiled_vek280/vek280_q_train2_resnet18_cifar10.h5.xmodel ./target/cifar10/vek280_train2_resnet18_cifar10.xmodel
mv ./build/compiled_vck5000/vck5000_q_train2_resnet18_cifar10.h5.xmodel ./target/cifar10/vck5000_train2_resnet18_cifar10.xmodel
mv ./build/compiled_v70/v70_q_train2_resnet18_cifar10.h5.xmodel ./target/cifar10/v70_train2_resnet18_cifar10.xmodel
}
... 생략
compile() {
# vai_c_tensorflow2 명령어 실행
vai_c_tensorflow2 \
# 양자화된 모델의 경로 지정
--model ./build/quantized/${CNN_MODEL} \
# 아키텍처의 경로 지정
--arch $ARCH \
# 컴파일된 모델의 출력 디렉토리 지정
--output_dir ./build/compiled_${TARGET} \
# 컴파일된 모델의 이름 지정
--net_name ${TARGET}_${CNN_MODEL}
}
compile #2>&1 | tee build/log/compile_$TARGET.log
echo "-----------------------------------------"
echo "MODEL COMPILED"
echo "-----------------------------------------"
# ===========================================================================
# STEP6: prepare archive for TARGET ZCU102 runtime application for CIFAR10
# ===========================================================================
prepare_cifar10_archives() {
echo " "
echo "----------------------------------------------------------------------------------"
echo "[DB INFO STEP6] PREPARING CIFAR10 ARCHIVE FOR TARGET BOARDS"
echo "----------------------------------------------------------------------------------"
echo " "
cp -r target ./build
cd ./build/dataset/cifar10
tar -cvf test.tar ./test > /dev/null
cp test.tar ../../../build/target/cifar10/
rm test.tar
cd ../../../
rm -rf ./build/target/imagenet #unuseful at the moment
# zcu102
cp -r ./build/target/ ./build/target_zcu102 > /dev/null
rm -f ./build/target_zcu102/cifar10/vck*_cifar10.xmodel
rm -f ./build/target_zcu102/cifar10/vek*_cifar10.xmodel
rm -f ./build/target_zcu102/cifar10/v70*_cifar10.xmodel
# vck190
cp -r ./build/target/ ./build/target_vck190 > /dev/null
rm -f ./build/target_vck190/cifar10/zcu1*_cifar10.xmodel
rm -f ./build/target_vck190/cifar10/vek2*_cifar10.xmodel
rm -f ./build/target_vck190/cifar10/vck5*_cifar10.xmodel
rm -f ./build/target_vck190/cifar10/v70*_cifar10.xmodel
# vek280
cp -r ./build/target ./build/target_vek280 > /dev/null
rm -f ./build/target_vek280/cifar10/zcu*_cifar10.xmodel
rm -f ./build/target_vek280/cifar10/vck*_cifar10.xmodel
rm -f ./build/target_vek280/cifar10/v70*_cifar10.xmodel
# vck5000
cp -r ./build/target/ ./build/target_vck5000 > /dev/null
rm -f ./build/target_vck5000/cifar10/zcu1*_cifar10.xmodel
rm -f ./build/target_vck5000/cifar10/vek2*_cifar10.xmodel
rm -f ./build/target_vck5000/cifar10/vck1*_cifar10.xmodel
rm -f ./build/target_vck5000/cifar10/v70*_cifar10.xmodel
# v70
cp -r ./build/target/ ./build/target_v70 > /dev/null
rm -f ./build/target_v70/cifar10/zcu1*_cifar10.xmodel
rm -f ./build/target_v70/cifar10/vek2*_cifar10.xmodel
rm -f ./build/target_v70/cifar10/vck*_cifar10.xmodel
}