{"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\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport tensorflow as tf\nfrom tqdm.auto import tqdm\n\nsns.set_style('darkgrid')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset Exploration","metadata":{}},{"cell_type":"code","source":"#đọc dữ liệu\ntrain = pd.read_csv('../input/plant-pathology-2021-fgvc8/train.csv')\n\n#kích thước của mảng\nprint(train.shape)\n\n#In mảng\ntrain.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ta có thể thấy được ảnh \"1\" mang nhiều hơn 1 nhãn nên đây là dạng bài đa nhãn nên ở đây chúng ta sử dụng \nMulti-label classification cho bài toán","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import MultiLabelBinarizer\n\n#sử dụng split() để tách nhãn trong label\n#sử dụng MultiLabelBinarizer() tạo ma trận theo các tag đã tách được dưới dạng cột\nmlb = MultiLabelBinarizer().fit(train.labels.apply(lambda x: x.split()))\nlabels = pd.DataFrame(mlb.transform(train.labels.apply(lambda x: x.split())), columns=mlb.classes_)\n\n#khởi tạo figure và axes\nfig, ax = plt.subplots(figsize=(20, 6))\n\n#In biểu đồ dưới dạng cột\nlabels.sum().plot.bar(title='Target Class Distribution');","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(20, 6))\n\n#In biểu đồ số lượng tag có trong tiều đề của mỗi bức ảnh (vd: 1tag, 2tag, 3tag)\nlabels.sum(axis=1).value_counts().plot.bar(title='Distribution of Number of Labels per Image');","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#khởi tọa 1 figure có 3x4 axes\nfig, ax = plt.subplots(3, 4, figsize=(20, 10))\n\n#gán từng ảnh thứ 2 của mảng trong từng loại label vào từng vị trí axes từ trái sang phải và từ trên xuống dưới\nfor i, img in enumerate(train.groupby('labels').first().reset_index().values):\n    ax[i//4][i%4].imshow(plt.imread(f\"../input/plant-pathology-2021-fgvc8/train_images/{img[1]}\"))\n    ax[i//4][i%4].set_title(img[0])\n    ax[i//4][i%4].axis('off')\nfig.suptitle('Image Samples', fontsize=18); ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preprocessing and Augmentation","metadata":{}},{"cell_type":"code","source":"#sử dụng pd.concat() để ghép nối image với labels\nlabels = pd.concat([train['image'], labels], axis=1)\nlabels.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" Bây giờ nếu chúng ta sử dụng ngay ảnh nói trên để train cho model CNN Classify thì sẽ bị hiện tượng Overfit vì dữ liệu nhiều nhưng đa phần giống nhau. Dẫn đến train sẽ có chất lượng tốt nhưng khi test sẽ thấy không nhận chuẩn lắm.\n \n Chúng ta sẽ thực hiện augment dữ liệu để làm phong phú hơn dữ liệu, tăng data variance , tăng tính tổng quát cho model \nbằng ImageDataGenerator của Keras.","metadata":{}},{"cell_type":"code","source":"# scale pixel values to [0, 1]\n# validation_split dùng 90% dữ liệu đầu tiên để đào tạo và 10% dữ liệu tiếp theo để kiểm tra\nimage_data_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255, validation_split=0.1)\n\n# flow_from_dataframe để đọc ảnh từ mảng đã tạo ở trên\n# x_col = tên hình ảnh\n# y_col = tên nhãn\n# class_mode = raw: đề nghị trình tạo trả về tất cả các giá trị trong y.\n# target_size=(224, 224): thay đổi kích thước hình ảnh thành 224 x 224 pixel.\n# batch_size=64: là mỗi lần cập nhật trọng số, ta dùng 64 images.\ntrain_generator = image_data_generator.flow_from_dataframe(\n    dataframe=labels,\n    directory='../input/plant-pathology-2021-fgvc8/train_images',\n    x_col='image',\n    y_col=labels.columns.tolist()[1:],\n    class_mode='raw',\n    color_mode=\"rgb\",\n    target_size=(224, 224),\n    batch_size=64,\n    subset='training'\n)\n\nvalid_generator = image_data_generator.flow_from_dataframe(\n    dataframe=labels,\n    directory='../input/plant-pathology-2021-fgvc8/train_images',\n    x_col='image',\n    y_col=labels.columns.tolist()[1:],\n    class_mode='raw',\n    color_mode=\"rgb\",\n    target_size=(224, 224),\n    batch_size=64,\n    subset='validation'\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Modelling","metadata":{}},{"cell_type":"markdown","source":"Tại bước này, mình sẽ sử dụng baseModel đó là mạng MobileNetV2","metadata":{}},{"cell_type":"code","source":"inputs = tf.keras.Input(shape=(224, 224, 3))\n\n# Load MobileNetV2\nx = tf.keras.applications.MobileNetV2(include_top=False)(inputs)\n# Load GlobalAveragePooling2D()\nx = tf.keras.layers.GlobalAveragePooling2D()(x)\n\n# tạo các layer mới với Dense() với '6' là số class và sử dụng hàm sigmoid\n# Lấy x từ output của GlobalAveragePooling2D()\noutputs = tf.keras.layers.Dense(6, activation='sigmoid')(x)\n\n# Tạo model với output là lớp Dense vừa thêm\nmodel = tf.keras.models.Model(inputs, outputs)\n# Compile model\nmodel.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(lr=1e-4))\n\n# In cấu trúc mạng\nmodel.summary()\ntf.keras.utils.plot_model(model, show_shapes=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tổng số tham số của mạng là 2,265,670 trong đó có 2,231,558 đã fix cứng, còn lại 34,112 tham số sẽ được train.","metadata":{}},{"cell_type":"code","source":"rlp = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', patience=2, verbose=1, factor=0.01)\nes = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, verbose=1, restore_best_weights=True)\n\nhistory = model.fit(train_generator, validation_data=valid_generator, epochs=10, callbacks=[rlp, es])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fix, ax = plt.subplots(figsize=(20, 6))\npd.DataFrame(history.history)[['loss', 'val_loss']].plot(ax=ax, title='Model Loss Curve')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"submissions = pd.read_csv('../input/plant-pathology-2021-fgvc8/sample_submission.csv')\nsubmissions.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)\n\ntest_generator = test_data_generator.flow_from_dataframe(\n    submissions,\n    directory = '../input/plant-pathology-2021-fgvc8/test_images',\n    x_col=\"image\",\n    y_col=None,\n    target_size=(224, 224),\n    color_mode=\"rgb\",\n    classes=None,\n    class_mode=None,\n    shuffle=False,\n    batch_size=1\n)\n\npredictions = model.predict(test_generator,steps=len(test_generator.filenames))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"thresh = 0.5\nfor i in range(3):\n    submissions.iloc[i, 1] = ' '.join(labels.columns[1:][predictions[i] >= thresh])\n    \nsubmissions.to_csv('submission.csv', index=False)    ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save('mobilenetv2.h5')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}