{"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":"markdown","source":"# Подготовка данных и окружения","metadata":{}},{"cell_type":"markdown","source":"### Импорт модулей","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.utils import to_categorical\nimport numpy as np\nfrom glob import glob\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import MultiLabelBinarizer\nfrom skimage import exposure\nimport cv2 as cv\nimport os\nimport itertools\nimport seaborn as sns\nimport os\nimport multiprocessing as mproc\nfrom keras.preprocessing import image\nfrom matplotlib.pyplot import figure\n\n#from tensorflow import keras\n#from tensorflow.keras import layers,models\n\n\nfrom tqdm import tqdm\n%matplotlib inline\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:35.191860Z","iopub.execute_input":"2022-11-08T14:14:35.193278Z","iopub.status.idle":"2022-11-08T14:14:40.869129Z","shell.execute_reply.started":"2022-11-08T14:14:35.193139Z","shell.execute_reply":"2022-11-08T14:14:40.868119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Проверка доступности GPU","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\n\ngpus = tf.config.experimental.list_physical_devices('GPU')\nfor gpu in gpus:\n    print(\"Name:\", gpu.name, \"  Type:\", gpu.device_type)\ntf.test.is_gpu_available()\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:40.872918Z","iopub.execute_input":"2022-11-08T14:14:40.874179Z","iopub.status.idle":"2022-11-08T14:14:43.406452Z","shell.execute_reply.started":"2022-11-08T14:14:40.874139Z","shell.execute_reply":"2022-11-08T14:14:43.405344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Загрузка и предобработка\n### Директории с исходными файлами\nДля ускорения тренировки модели используются изображения из датасета, уменьшенные до 256х256","metadata":{}},{"cell_type":"code","source":"#train_dir= '/kaggle/input/plant-pathology-2021-fgvc8/train_images/'\ntrain_dir = '../input/resized-plant2021/img_sz_256/'\ntest_dir =  '/kaggle/input/plant-pathology-2021-fgvc8/test_images/'\ndf = pd.read_csv('../input/plant-pathology-2021-fgvc8/train.csv')","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:43.408229Z","iopub.execute_input":"2022-11-08T14:14:43.409045Z","iopub.status.idle":"2022-11-08T14:14:43.444797Z","shell.execute_reply.started":"2022-11-08T14:14:43.409006Z","shell.execute_reply":"2022-11-08T14:14:43.443917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_files = glob(train_dir + '/*.jpg')\ntest_files = glob(test_dir + '/*.jpg')\nprint('# files for train',len(train_files))\nprint('# files for train',len(test_files))\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:43.447954Z","iopub.execute_input":"2022-11-08T14:14:43.448360Z","iopub.status.idle":"2022-11-08T14:14:43.731949Z","shell.execute_reply.started":"2022-11-08T14:14:43.448321Z","shell.execute_reply":"2022-11-08T14:14:43.730841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Сэмпл данных","metadata":{}},{"cell_type":"code","source":"def visualize_batch(path,image_ids, labels):\n    plt.figure(figsize=(16, 12))\n    \n    for ind, (image_id, label) in enumerate(zip(image_ids, labels)):\n        plt.subplot(3, 3, ind + 1)\n        image = cv.imread(os.path.join(path, image_id))\n        image = cv.cvtColor(image, cv.COLOR_BGR2RGB)\n\n        plt.imshow(image)\n        plt.title(f\"Класс: {label}\", fontsize=12)\n        plt.axis(\"off\")\n    plt.show()\nts = df.sample(9)\nimage_ids = ts[\"image\"].values\nlabels = ts[\"labels\"].values\nvisualize_batch(train_dir,image_ids,labels)\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:43.733431Z","iopub.execute_input":"2022-11-08T14:14:43.733961Z","iopub.status.idle":"2022-11-08T14:14:44.651149Z","shell.execute_reply.started":"2022-11-08T14:14:43.733923Z","shell.execute_reply":"2022-11-08T14:14:44.649939Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Техники подготовки изображений","metadata":{}},{"cell_type":"code","source":"# вспомогательная функция сравнения изображений\n\ndef plot_comparison(original, filtered, title_filtered):\n  fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 6), sharex=True, sharey=True)\n  ax1.imshow(original, cmap=plt.cm.gray) \n  ax1.set_title('original') \n  ax1.axis('off')\n  ax2.imshow(filtered, cmap=plt.cm.gray) \n  ax2.set_title(title_filtered) \n  ax2.axis('off')","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:44.652198Z","iopub.execute_input":"2022-11-08T14:14:44.652531Z","iopub.status.idle":"2022-11-08T14:14:44.660625Z","shell.execute_reply.started":"2022-11-08T14:14:44.652501Z","shell.execute_reply":"2022-11-08T14:14:44.659577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"leaf_image = cv.imread('../input/plant-pathology-2021-fgvc8/train_images/80273091d9e9bddb.jpg')\nleaf_image = cv.cvtColor(leaf_image, cv.COLOR_BGR2RGB)\n\nplt.imshow(leaf_image)","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:44.662435Z","iopub.execute_input":"2022-11-08T14:14:44.663074Z","iopub.status.idle":"2022-11-08T14:14:46.639934Z","shell.execute_reply.started":"2022-11-08T14:14:44.663039Z","shell.execute_reply":"2022-11-08T14:14:46.638838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Корректировка контраста","metadata":{}},{"cell_type":"code","source":"from skimage import exposure\nequalized_leaf_image = exposure.equalize_hist(leaf_image)\n\nplot_comparison(leaf_image, equalized_leaf_image, 'Histogram equalization')\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:46.641562Z","iopub.execute_input":"2022-11-08T14:14:46.642295Z","iopub.status.idle":"2022-11-08T14:14:53.333089Z","shell.execute_reply.started":"2022-11-08T14:14:46.642243Z","shell.execute_reply":"2022-11-08T14:14:53.331952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"adapthits_leag_image = exposure.equalize_adapthist(leaf_image)\n\nplot_comparison(leaf_image, adapthits_leag_image, 'Adaptive Histogram equalization')\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:14:53.334718Z","iopub.execute_input":"2022-11-08T14:14:53.335319Z","iopub.status.idle":"2022-11-08T14:15:05.345755Z","shell.execute_reply.started":"2022-11-08T14:14:53.335282Z","shell.execute_reply":"2022-11-08T14:15:05.344809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Гистограммы","metadata":{}},{"cell_type":"code","source":"red = leaf_image[:, :, 0]\ngreen = leaf_image[:, 0, :]\nblue = leaf_image[0, :, :]\n\n\nplt.hist(red.ravel(), bins=32) \nplt.title('Red Histogram')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:05.350887Z","iopub.execute_input":"2022-11-08T14:15:05.351208Z","iopub.status.idle":"2022-11-08T14:15:05.744776Z","shell.execute_reply.started":"2022-11-08T14:15:05.351180Z","shell.execute_reply":"2022-11-08T14:15:05.743761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.hist(green.ravel(), bins=32) \nplt.title('Green Histogram')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:05.746131Z","iopub.execute_input":"2022-11-08T14:15:05.746601Z","iopub.status.idle":"2022-11-08T14:15:05.985577Z","shell.execute_reply.started":"2022-11-08T14:15:05.746564Z","shell.execute_reply":"2022-11-08T14:15:05.984665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.hist(blue.ravel(), bins=32) \nplt.title('Blue Histogram')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:05.987026Z","iopub.execute_input":"2022-11-08T14:15:05.987348Z","iopub.status.idle":"2022-11-08T14:15:06.212875Z","shell.execute_reply.started":"2022-11-08T14:15:05.987322Z","shell.execute_reply":"2022-11-08T14:15:06.211756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Метки классов","metadata":{}},{"cell_type":"code","source":"labels_all = list(itertools.chain(*[lbs.split(\" \") for lbs in df['labels']]))\nfigure(figsize=(8, 6), dpi=80)\n\nax = sns.countplot(y=sorted(labels_all), orient='x')\nax.grid()\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:06.214269Z","iopub.execute_input":"2022-11-08T14:15:06.214861Z","iopub.status.idle":"2022-11-08T14:15:06.473699Z","shell.execute_reply.started":"2022-11-08T14:15:06.214821Z","shell.execute_reply":"2022-11-08T14:15:06.472713Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_all = list(itertools.chain([lbs for lbs in df['labels']]))\nfigure(figsize=(8, 6), dpi=80)\n\nax = sns.countplot(y=sorted(labels_all), orient='x')\nax.grid()\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:06.475082Z","iopub.execute_input":"2022-11-08T14:15:06.476120Z","iopub.status.idle":"2022-11-08T14:15:06.965718Z","shell.execute_reply.started":"2022-11-08T14:15:06.476080Z","shell.execute_reply":"2022-11-08T14:15:06.964718Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Предобработка меток классов","metadata":{}},{"cell_type":"code","source":"print(df.head(10))\nprint(df.labels.value_counts())","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:06.967202Z","iopub.execute_input":"2022-11-08T14:15:06.968202Z","iopub.status.idle":"2022-11-08T14:15:06.981101Z","shell.execute_reply.started":"2022-11-08T14:15:06.968163Z","shell.execute_reply":"2022-11-08T14:15:06.980025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Как видно, labels содержит метки классов, разделенные пробелом. Необходимо преобразовать данные метки в двоичные аттрибуты. Используем копию датафрейма *train*","metadata":{}},{"cell_type":"code","source":"train = df.copy()\ntrain['labels'] = df['labels'].apply(lambda string: string.split(' '))\n\ns = list(train['labels'])\nmlb = MultiLabelBinarizer()\ntrainx = pd.DataFrame(mlb.fit_transform(s), columns=mlb.classes_, index=train.index)\ntrainx['image'] = train['image']\n\nprint(trainx.head(10))\nprint(trainx.columns)","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:06.982660Z","iopub.execute_input":"2022-11-08T14:15:06.983298Z","iopub.status.idle":"2022-11-08T14:15:07.018207Z","shell.execute_reply.started":"2022-11-08T14:15:06.983257Z","shell.execute_reply":"2022-11-08T14:15:07.017174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Настройка модели","metadata":{}},{"cell_type":"markdown","source":"### Параметры тренировки","metadata":{}},{"cell_type":"code","source":"TARGET_SIZE = 128\nBATCH_SIZE = 64\nEPOCHS = 50\nDATA_LIMIT = 12000","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:07.027330Z","iopub.execute_input":"2022-11-08T14:15:07.028366Z","iopub.status.idle":"2022-11-08T14:15:07.036441Z","shell.execute_reply.started":"2022-11-08T14:15:07.028329Z","shell.execute_reply":"2022-11-08T14:15:07.035238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Опционально ограничим количество данных для тренировки. Иногда не удается попасть в ограничения оперативной памяти.","metadata":{}},{"cell_type":"code","source":"trainx = trainx[:DATA_LIMIT]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Загрузим изображения, измененные до размеров TARGET_SIZE в массив np.array","metadata":{}},{"cell_type":"markdown","source":"### Загрузка данных для тренировки модели","metadata":{}},{"cell_type":"code","source":"train_image = []\nfor i in tqdm(range(trainx.shape[0])):\n    \n    img = image.load_img(train_dir+ trainx['image'][i],target_size=(TARGET_SIZE,TARGET_SIZE,3))\n    #image_data = apply_clahe(img)\n    #ret,thresh1 = cv.threshold(image_data,120, 255, cv.THRESH_TOZERO)\n    \n    #img = apply_clahe(train_dir+ trainx['image'][i])\n    #img = cv.imread(train_dir+z,) #reading the image\n    #img = img/255\n    #ret,thresh1 = cv.threshold(np.asarray(img),110, 255, cv.THRESH_TOZERO)\n    #plt.imshow(thresh1)\n    img = image.img_to_array(img)\n    img = img/255\n    train_image.append(img)\n\nX = np.array(train_image)\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:15:07.037840Z","iopub.execute_input":"2022-11-08T14:15:07.038287Z","iopub.status.idle":"2022-11-08T14:16:49.683140Z","shell.execute_reply.started":"2022-11-08T14:15:07.038250Z","shell.execute_reply":"2022-11-08T14:16:49.681972Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import pickle\n# file = open('x_array', 'wb')\n# pickle.dump(X, file)\n# file.close()\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:16:49.684955Z","iopub.execute_input":"2022-11-08T14:16:49.685375Z","iopub.status.idle":"2022-11-08T14:16:49.690979Z","shell.execute_reply.started":"2022-11-08T14:16:49.685336Z","shell.execute_reply":"2022-11-08T14:16:49.689993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## Проверим, что у нас действительно есть изображения в массиве X нужных размеров\nplt.imshow(X[2])\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:16:49.692404Z","iopub.execute_input":"2022-11-08T14:16:49.695125Z","iopub.status.idle":"2022-11-08T14:16:49.940727Z","shell.execute_reply.started":"2022-11-08T14:16:49.695088Z","shell.execute_reply":"2022-11-08T14:16:49.939744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# трансформируем бинарные аттрибуты классов в массив y и проверим его размерность \ny = np.array(trainx.drop(['image'],axis=1))\ny.shape\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:16:49.943791Z","iopub.execute_input":"2022-11-08T14:16:49.944190Z","iopub.status.idle":"2022-11-08T14:16:49.955756Z","shell.execute_reply.started":"2022-11-08T14:16:49.944157Z","shell.execute_reply":"2022-11-08T14:16:49.954708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Разбивка на тренировочную и валидационную выборки. 90% vs. 10%.\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:16:49.959295Z","iopub.execute_input":"2022-11-08T14:16:49.959652Z","iopub.status.idle":"2022-11-08T14:16:50.989941Z","shell.execute_reply.started":"2022-11-08T14:16:49.959623Z","shell.execute_reply":"2022-11-08T14:16:50.988832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Инициализация модели ","metadata":{}},{"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten\nfrom keras.layers import Conv2D, MaxPooling2D\n\nmodel = Sequential()\nmodel.add(Conv2D(filters=16, kernel_size=(5, 5), activation=\"relu\", input_shape=(TARGET_SIZE,TARGET_SIZE,3)))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\nmodel.add(Conv2D(filters=32, kernel_size=(5, 5), activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\nmodel.add(Conv2D(filters=64, kernel_size=(5, 5), activation=\"relu\"))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\nmodel.add(Conv2D(filters=64, kernel_size=(5, 5), activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(64, activation='relu'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(6, activation='sigmoid'))\nmodel.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n\n\n# model.add(Conv2D(64,(3,3), padding=\"same\", activation=\"relu\"))\n# model.add(MaxPool2D())\n# model.add(Conv2D(128, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(128, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(MaxPool2D())\n# model.add(Conv2D(256, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(256, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(256, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(MaxPool2D())\n# model.add(Conv2D(512, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(512, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(512, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(MaxPool2D())\n# model.add(Conv2D(512, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(512, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(Conv2D(512, (3,3), padding=\"same\", activation=\"relu\"))\n# model.add(MaxPool2D())\n\n# model.add(Flatten())\n# model.add(Dense(4096,activation=\"relu\"))\n# model.add(Dropout(0.2))\n# model.add(Dense(2048,activation=\"relu\"))\n# model.add(Dropout(0.2))\n\n# model.add(Dropout(0.25))\n# model.add(Flatten())\n# model.add(Dense(128, activation='relu'))\n# model.add(Dropout(0.5))\n# model.add(Dense(64, activation='relu'))\n# model.add(Dropout(0.5))\n# model.add(Dense(6, activation=\"sigmoid\"))\n# model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy'])\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:16:50.991731Z","iopub.execute_input":"2022-11-08T14:16:50.992189Z","iopub.status.idle":"2022-11-08T14:16:51.696077Z","shell.execute_reply.started":"2022-11-08T14:16:50.992149Z","shell.execute_reply":"2022-11-08T14:16:51.694957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Тренировка модели","metadata":{}},{"cell_type":"code","source":"model.fit(X_train, y_train, epochs=EPOCHS, validation_data=(X_test, y_test), batch_size=BATCH_SIZE)\n","metadata":{"execution":{"iopub.status.busy":"2022-11-08T14:16:51.700291Z","iopub.execute_input":"2022-11-08T14:16:51.701156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Проверка модели на тестовых данных","metadata":{}},{"cell_type":"code","source":"classes = np.array(trainx.columns[:-1])\nproba = model.predict(img.reshape(1,TARGET_SIZE,TARGET_SIZE,3))\ntop_3 = np.argsort(proba[0])[:-4:-1]\nfor i in range(3):\n    print(\"{}\".format(classes[top_3[i]])+\" ({:.3})\".format(proba[0][top_3[i]]))\nplt.imshow(img)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Работа с проверочными файлами изображений и формирование submission.csv","metadata":{}},{"cell_type":"markdown","source":"Настройка отсечки для того, чтобы предсказанный в результате  model.predict() класс считался валидным. Метки классов объединяются в строку и записываются в итоговый датафрейм submission.csv.","metadata":{}},{"cell_type":"code","source":"name = {0: 'complex',\n        1: 'frog_eye_leaf_spot',\n        2: 'healthy',\n        3: 'powdery_mildew',\n        4: 'rust',\n        5: 'scab'}\n\nthreshold = {0: 0.3,\n             1: 0.3,\n             2: 0.4,\n             3: 0.3,\n             4: 0.3,\n            5:0.3}\n\nd = []\n\nfor f in test_files:\n    img = image.load_img(f,target_size=(TARGET_SIZE,TARGET_SIZE,3))\n    img = image.img_to_array(img)\n    img = img/255\n    proba = list(model.predict(img.reshape(1,TARGET_SIZE,TARGET_SIZE,3))[0])\n    filename = f.split('/')[-1]\n    pre = []\n    for i in range(len(proba)):\n        if proba[i] > threshold[i]:\n            pre.append(name[i])\n            \n    d.append({'image': filename, 'labels': ' '.join(pre)})\n    \ndf = pd.DataFrame(d)\ndf.to_csv('submission.csv', index=False)\ndf.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}