{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np \nimport pandas as pd \nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\nfrom sklearn.utils import shuffle\nfrom sklearn.utils import class_weight\nfrom sklearn.preprocessing import minmax_scale\nimport random\nimport cv2\nimport warnings\nwarnings.filterwarnings('ignore')\n\nimport albumentations as A\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential, Model\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.layers import Dense, Dropout, Activation, Input, BatchNormalization, GlobalAveragePooling2D\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping\nfrom tensorflow.keras.experimental import CosineDecay\nfrom tensorflow.keras.applications import EfficientNetB4,EfficientNetB7\nfrom tensorflow.keras.layers.experimental.preprocessing import RandomCrop,CenterCrop, RandomRotation\nfrom tensorflow.keras.optimizers import Adam\nfrom sklearn.model_selection import StratifiedKFold\nfrom tensorflow.keras.losses import CategoricalCrossentropy\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-05-20T13:34:33.083975Z","iopub.execute_input":"2021-05-20T13:34:33.084434Z","iopub.status.idle":"2021-05-20T13:34:33.092831Z","shell.execute_reply.started":"2021-05-20T13:34:33.084394Z","shell.execute_reply":"2021-05-20T13:34:33.09195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn import preprocessing\ntrain_df=pd.read_csv('../input/plant-pathology-2021-fgvc8/train.csv')\ntrain_image_path = '../input/plant-pathology-2021-fgvc8/train_images/'\ntrain_df['filepath'] = train_image_path+train_df['image']\n\n\n\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.095789Z","iopub.execute_input":"2021-05-20T13:34:33.096279Z","iopub.status.idle":"2021-05-20T13:34:33.140863Z","shell.execute_reply.started":"2021-05-20T13:34:33.096243Z","shell.execute_reply":"2021-05-20T13:34:33.140142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Initial = train_df['labels'].values.copy()","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.142066Z","iopub.execute_input":"2021-05-20T13:34:33.142441Z","iopub.status.idle":"2021-05-20T13:34:33.150029Z","shell.execute_reply.started":"2021-05-20T13:34:33.142397Z","shell.execute_reply":"2021-05-20T13:34:33.14913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import defaultdict\n\n\ndct = defaultdict(list)\n\nfor i, labels in enumerate(train_df.labels):\n    for category in labels.split():\n        dct[category].append(i)\n \ndct = {key: np.array(val) for key, val in dct.items()}\ndct","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.151335Z","iopub.execute_input":"2021-05-20T13:34:33.151727Z","iopub.status.idle":"2021-05-20T13:34:33.179402Z","shell.execute_reply.started":"2021-05-20T13:34:33.1517Z","shell.execute_reply":"2021-05-20T13:34:33.178261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"new_df = pd.DataFrame(np.zeros((train_df.shape[0], len(dct.keys())), dtype=np.int8), columns=dct.keys())\n\nfor key, val in dct.items():\n    new_df.loc[val, key] = 1\n    \ntrain_df = pd.concat([train_df, new_df], axis=1)\ntrain_df=train_df.drop(['labels'],axis=1)\ntrain_df=train_df.drop(['image'],axis=1)\n\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.181891Z","iopub.execute_input":"2021-05-20T13:34:33.182267Z","iopub.status.idle":"2021-05-20T13:34:33.208769Z","shell.execute_reply.started":"2021-05-20T13:34:33.182229Z","shell.execute_reply":"2021-05-20T13:34:33.208032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 8 # 배치 사이즈를 설정\nimage_size = 512 # 이미지의 크기를 설정\ninput_shape = (image_size, image_size, 3) #이미지의 사이즈 정의 (컬러 이미지이기 때문에 한 화소당 3개의 데이터가 필요)\ndropout_rate = 0.35 #드롭아웃 비율 정의\nclasses_to_predict = sorted(\"healthy scab frog_eye_leaf_spot complex rust powdery_mildew\".split(), key=str.lower) #예측해야 하는 클래스 수 정의, 여기서는 12개\nclasses_to_predict","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.210377Z","iopub.execute_input":"2021-05-20T13:34:33.210842Z","iopub.status.idle":"2021-05-20T13:34:33.218028Z","shell.execute_reply.started":"2021-05-20T13:34:33.210805Z","shell.execute_reply":"2021-05-20T13:34:33.216997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_train_index = list() #어떤 train 데이터를 고를지 저장하는 list를 정의\nfinal_test_index = list() #어떤 test 데이터를 고를지 저장하는 list를 정의for i, (train_index, val_index) in enumerate(kfold.split(df.index, original_labels)):\nfold=np.zeros((len(train_df),))\nskf = StratifiedKFold(n_splits=4) #4 fold를 하는 stratifiedKFold를 선언 (validation 25%)\nfor i, (train_index, test_index) in enumerate(skf.split(train_df.index,Initial)): #각 fold에 해당하는 데이터를 출력\n    fold[test_index]=i\n    final_train_index.append(train_index) #final_train_index에 각 fold에 해당하는 데이터를 입력\n    final_test_index.append(test_index) #final_test_index에 각 fold에 해당하는 데이터를 입력","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.219653Z","iopub.execute_input":"2021-05-20T13:34:33.220342Z","iopub.status.idle":"2021-05-20T13:34:33.260639Z","shell.execute_reply.started":"2021-05-20T13:34:33.220304Z","shell.execute_reply":"2021-05-20T13:34:33.259706Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_df = train_df.iloc[final_train_index[0]] #train 데이터를 만듬\nvalidation_df = train_df.iloc[final_test_index[0]] #validation 만듬","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.262053Z","iopub.execute_input":"2021-05-20T13:34:33.26239Z","iopub.status.idle":"2021-05-20T13:34:33.276036Z","shell.execute_reply.started":"2021-05-20T13:34:33.262356Z","shell.execute_reply":"2021-05-20T13:34:33.275171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"training_data = tf.data.Dataset.from_tensor_slices((training_df.filepath.values, training_df.loc[:,train_df.columns!=\"filepath\"].values))\nvalidation_data = tf.data.Dataset.from_tensor_slices((validation_df.filepath.values, validation_df.loc[:,train_df.columns!=\"filepath\"].values))\nprint(training_data)\n\n","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.27867Z","iopub.execute_input":"2021-05-20T13:34:33.279216Z","iopub.status.idle":"2021-05-20T13:34:33.291813Z","shell.execute_reply.started":"2021-05-20T13:34:33.279165Z","shell.execute_reply":"2021-05-20T13:34:33.290899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"def load_image_and_label_from_path(image_path, label): #이미지 데이터를 불러와 텐서 (array와 비슷한 형태)로 변환하는 함수\n    img = tf.io.read_file(image_path) #이미지 경로의 파일을 읽음\n    img = tf.image.decode_jpeg(img, channels=3) #이미지를 array 데이터로 변환하여 저장\n    img = tf.image.random_crop(img, size=[image_size,image_size,3]) # 이미지를 랜덤으로 원하는 사이즈로 잘라줌. 중앙만 자르고 싶다면 central_crop 사용.\n    return img, label\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE #메모리 동적 할당을 위한 AUTOTUNE\ntraining_data = training_data.map(load_image_and_label_from_path, num_parallel_calls=AUTOTUNE) #train 데이터를 불러옴\nvalidation_data = validation_data.map(load_image_and_label_from_path,num_parallel_calls=AUTOTUNE) #validation 데이터를 불러옴 \"\"\"","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.294458Z","iopub.execute_input":"2021-05-20T13:34:33.295052Z","iopub.status.idle":"2021-05-20T13:34:33.330784Z","shell.execute_reply.started":"2021-05-20T13:34:33.295015Z","shell.execute_reply":"2021-05-20T13:34:33.330012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_image_and_label_from_path(image_path, label): #이미지 데이터를 불러와 텐서 (array와 비슷한 형태)로 변환하는 함수\n    src = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    dst = cv2.cvtColor(src, cv2.COLOR_BGR2HSV) #이미지 경로의 파일을 읽음\n    img = tf.image.decode_jpeg(dst, channels=3) #이미지를 array 데이터로 변환하여 저장\n    img = tf.image.random_crop(img, size=[image_size,image_size,3]) # 이미지를 랜덤으로 원하는 사이즈로 잘라줌. 중앙만 자르고 싶다면 central_crop 사용.\n    return img, label\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE #메모리 동적 할당을 위한 AUTOTUNE\ntraining_data = training_data.map(load_image_and_label_from_path, num_parallel_calls=AUTOTUNE) #train 데이터를 불러옴\nvalidation_data = validation_data.map(load_image_and_label_from_path,num_parallel_calls=AUTOTUNE) #validation 데이터를 불러옴\n\nsrc = cv2.imread(\"Image/crow.jpg\", cv2.IMREAD_COLOR)\ndst = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#train 및 validation 데이터를 훈련하기 좋게 batch로 자름\ntraining_data_batches = training_data.shuffle(buffer_size=1000).batch(batch_size).prefetch(buffer_size=AUTOTUNE)\nvalidation_data_batches = validation_data.shuffle(buffer_size=1000).batch(batch_size).prefetch(buffer_size=AUTOTUNE)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.332358Z","iopub.execute_input":"2021-05-20T13:34:33.3327Z","iopub.status.idle":"2021-05-20T13:34:33.342259Z","shell.execute_reply.started":"2021-05-20T13:34:33.332667Z","shell.execute_reply":"2021-05-20T13:34:33.34156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import imgaug.augmenters as iaa","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.345103Z","iopub.execute_input":"2021-05-20T13:34:33.347239Z","iopub.status.idle":"2021-05-20T13:34:33.350994Z","shell.execute_reply.started":"2021-05-20T13:34:33.347202Z","shell.execute_reply":"2021-05-20T13:34:33.350018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"augmenter = iaa.Sequential([\n    iaa.color.AddToHueAndSaturation(value=(-20, 20)),\n    iaa.KMeansColorQuantization(n_colors=16),\n    \n    iaa.Fliplr(0.5),\n    iaa.Flipud(0.5),\n    iaa.Cutout(nb_iterations=(0,2), size=0.2, fill_mode=\"gaussian\", fill_per_channel=True),\n    iaa.GaussianBlur(sigma=(0,0.3)),\n    iaa.MultiplyBrightness((0.7,1.2)),\n    iaa.PiecewiseAffine(scale=(0,0.06)),\n    iaa.AddElementwise((-20, 20), per_channel=0.3),\n    iaa.Add((-20, 20), per_channel=0.2),\n    iaa.ReplaceElementwise(0.1, [40, 255],per_channel=0.2),\n  ])","metadata":{"execution":{"iopub.status.busy":"2021-05-20T16:37:06.247534Z","iopub.execute_input":"2021-05-20T16:37:06.24794Z","iopub.status.idle":"2021-05-20T16:37:06.279871Z","shell.execute_reply.started":"2021-05-20T16:37:06.247851Z","shell.execute_reply":"2021-05-20T16:37:06.278426Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def augment_fn():\n    def augment(images, labels):\n        img_dtype = images.dtype\n        img_shape = tf.shape(images)\n        images = tf.numpy_function(augmenter.augment_images,\n                                   [images],\n                                   img_dtype)\n        images = tf.reshape(images, shape = img_shape)\n        return images, labels\n    return augment","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.669226Z","iopub.status.idle":"2021-05-20T13:34:33.670071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_data_batches = training_data_batches.map(augment_fn())\ntraining_data_batches","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.671755Z","iopub.status.idle":"2021-05-20T13:34:33.672448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_augmentation_layers = tf.keras.Sequential(\n    [\n        layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"), #랜덤으로 이미지를 좌우로 뒤집어줌.\n        layers.experimental.preprocessing.RandomRotation(0.3), #이미지를 좌우로 25% 이내로 랜덤으로 돌립니다. \n        layers.experimental.preprocessing.RandomZoom((-0.2, 0.1)), #이미지를 0~20%만큼 랜덤으로 축소합니다.\n        layers.experimental.preprocessing.RandomContrast((0.2,0.2))\n    ]\n)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.673979Z","iopub.status.idle":"2021-05-20T13:34:33.674689Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"efficientnet = EfficientNetB4(weights=\"imagenet\", #이미지넷 가중치 값을 불러와 적용\n                              include_top=False, \n                              input_shape=input_shape, \n                              drop_connect_rate=dropout_rate) #efficientnetB4 모델을 로드\nefficientnet.trainable=False# efficientnetB4의 학습을 허용. 만약 False로 지정할 시에 정확도는 떨어지지만 학습 속도가 매우 빨라짐.","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.676038Z","iopub.status.idle":"2021-05-20T13:34:33.676752Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = Sequential() #새 Sequential 모델을 만듬 \nmodel.add(Input(shape=input_shape)) #인풋을 이미지 사이즈로 설정\nmodel.add(data_augmentation_layers) #이미지 augumentation 레이어 추가\nmodel.add(efficientnet) # efficientnetb0 추가\nmodel.add(layers.GlobalAveragePooling2D()) # 풀링 레이어를 추가\nmodel.add(layers.Dropout(dropout_rate)) # 드롭아웃 레이어를 추가\nmodel.add(Dense(len(classes_to_predict), activation=\"sigmoid\")) #마지막 덴스 레이어를 추가. 예측할 클래스의 개수만큼이 아웃풋이 된다. \nmodel.summary() #모델 확인","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.678161Z","iopub.status.idle":"2021-05-20T13:34:33.678846Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow.keras.metrics as _metrics","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.680238Z","iopub.status.idle":"2021-05-20T13:34:33.680905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss = CategoricalCrossentropy(label_smoothing=0.08)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 15 #에폭 수를 설정합니다.\ndecay_steps = int(round(len(training_df)/batch_size))*epochs\ncosine_decay = CosineDecay(initial_learning_rate=1e-4, decay_steps=decay_steps, alpha=0.3) #learning rate를 에폭이 지날수록 점점 줄여나가는 cosine decay 방법을 사용합니다. \ncallbacks = [ModelCheckpoint(filepath='mymodel.h5', monitor='Mean F1-Score', save_best_only=True), #가장 validation loss가 낮은 에폭의 모델을 .h5 파일로 저장합니다. \n            EarlyStopping(monitor='Mean F1-Score', patience = 5, verbose=2)] #정해진 에폭이 되기 전에 5번의 에폭동한 validation loss가 향상되지 않으면 학습을 종료합니다. \n\nmodel.compile(loss=loss, optimizer=Adam(cosine_decay), metrics=[\"accuracy\",tf.keras.metrics.Recall(),tf.keras.metrics.Precision()]) #loss는 sparse_categorical_crossentropy, optimizer는 Adam을 사용합니다. 각 에폭당 정확도를 통해 모델의 성능을 모니터링합니다,","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.682318Z","iopub.status.idle":"2021-05-20T13:34:33.683059Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(training_data_batches, #모델을 학습합니다. \n                  epochs = epochs, \n                  validation_data=validation_data_batches,\n                  callbacks=callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.684292Z","iopub.status.idle":"2021-05-20T13:34:33.684951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save('B4_onehotencoding.h5')","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:34:33.686211Z","iopub.status.idle":"2021-05-20T13:34:33.686781Z"},"trusted":true},"execution_count":null,"outputs":[]}]}