{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Cassava Leaf Disease Classification  \nこのコンペはキャッサバの21367枚のラベル付き画像を使って、キャッサバを４つの病気（または健康状態）に分類するコンペです。\n"},{"metadata":{},"cell_type":"markdown","source":"## OverView（Summary）"},{"metadata":{},"cell_type":"markdown","source":"### Discription（Summary）  \n- キャッサバはアフリカで2番目に大きな炭水化物の供給者であり、多くの家庭農場で栽培されているが**`ウイルス性疾患が低収量の主な原因`**となっている。データサイエンスの助けを借りれば、病気を特定して治療できる可能性がある。  \n- 病気を検出する既存の方法には農業専門家の助けを借りて、視覚的に検査および診断する方法がある。しかし、これは労働集約的で、供給が少なく、費用がかかるという問題がある。\n- また、追加の課題として、アフリカの農家は低品質なカメラしか持っていないため、その制約のもとでうまく機能する必要がある。\n- データセットは、農家が撮影し、マケレレ大学のAIラボと国立作物資源研究所（NaCRRI）が注釈をつけた**`21,367枚のラベル付き画像`**\n- あなたの仕事は各キャッサバの画像を**`4つの病気のカテゴリまたは健康な葉を示す5番目のカテゴリに分類する`**こと"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","collapsed":true,"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":false},"cell_type":"markdown","source":"### Evalution\n提出物は、**`分類の正確さ`**に基づいて評価されます。\n\n### Format\nコンテストの提出形式は、次の形式のcsvファイルです。\n\nimage_id,label  \n1000471002.jpg,4  \n1000840542.jpg,4  \netc."},{"metadata":{},"cell_type":"markdown","source":"### Time Line  \n- 2021年2月11日 -エントリー締め切り。競技するには、この日付より前に競技規則に同意する必要があります。\n\n- 2021年2月11日 -チーム合併の締め切り。これは、参加者がチームに参加または統合できる最後の日です。\n\n- 2021年2月18日 -最終提出期限。"},{"metadata":{},"cell_type":"markdown","source":"### Code Requirements\nこれは**`コードコンテスト`**です  \nこのコンテストへの提出は、**`ノートブックを通じて行う`**必要があります。  \nコミット後に[コンテストに送信]ボタンをアクティブにするには、次の条件を満たす必要があります。\n\n- CPUノートブック<= 9時間の実行時間  \n- GPUノートブック<= 9時間の実行時間  \n- TPUは、このコンテストへの提出には利用できません。モデルのトレーニングに引き続き使用できます。TPUでトレーニングし、GPUで推論/送信を実行する方法のウォークスルーについては、TPUドキュメントをご覧ください。\n- インターネットアクセスが無効\n- 事前にトレーニングされたモデルを含む、無料で公開されている外部データが許可されます\n- 送信ファイルには「submission.csv」という名前を付ける必要があります"},{"metadata":{},"cell_type":"markdown","source":"## Files  \n- **train.csv**\n\n    - image_id the image file name.\n\n    - label the ID code for the disease.\n\n\n- **sample_submission.csv**  \nA properly formatted sample submission, given the disclosed test set content.\n\n    - image_id the image file name.\n\n    - label the predicted ID code for the disease."},{"metadata":{},"cell_type":"markdown","source":"## EDA"},{"metadata":{},"cell_type":"markdown","source":"#### reference\n- https://www.kaggle.com/tanlikesmath/cassava-classification-eda-fastai-starter\n- https://www.kaggle.com/ihelon/cassava-leaf-disease-exploratory-data-analysis"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport os\nimport pandas as pd\nfrom fastai.vision.all import *","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Trainデータの表示"},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset_path = Path('../input/cassava-leaf-disease-classification')\nos.listdir(dataset_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(dataset_path/'train.csv')\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Labelの表示"},{"metadata":{"trusted":true},"cell_type":"code","source":"with open(os.path.join(dataset_path, \"label_num_to_disease_map.json\")) as file:\n    map_classes = json.loads(file.read())\n    classes=[]\n    for k, v in map_classes.items():        \n        classes.append([int(k), v])\n        \nclasses_df = pd.DataFrame(classes, columns=['Label', 'Label Details'])\nprint(classes_df.to_string(index=False))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### 写真を見る"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_0_df = train_df[train_df['label']==0]\ntrain_1_df = train_df[train_df['label']==1]\ntrain_2_df = train_df[train_df['label']==2]\ntrain_3_df = train_df[train_df['label']==3]\ntrain_4_df = train_df[train_df['label']==4]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"picture_path = Path('../input/cassava-leaf-disease-classification/train_images/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from PIL import Image\nimage_0 = Image.open(picture_path/train_0_df.loc[train_0_df.index[0]]['image_id'])\nimage_1 = Image.open(picture_path/train_1_df.loc[train_1_df.index[0]]['image_id'])\nimage_2 = Image.open(picture_path/train_2_df.loc[train_2_df.index[0]]['image_id'])\nimage_3 = Image.open(picture_path/train_3_df.loc[train_3_df.index[0]]['image_id'])\nimage_4 = Image.open(picture_path/train_4_df.loc[train_4_df.index[0]]['image_id'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_0 #\"Cassava Bacterial Blight (CBB)\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_1 #\"Cassava Brown Streak Disease (CBSD)\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_2 #\"Cassava Green Mottle (CGM)\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_3 #\"Cassava Mosaic Disease (CMD)\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_4 #\"Healthy\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### クラスの件数"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f\"Cassava Bacterial Blight (CBB): {len(train_0_df)}\")\nprint(f\"Cassava Brown Streak Disease (CBSD): {len(train_1_df)}\")\nprint(f\"Cassava Green Mottle (CGM): {len(train_2_df)}\")\nprint(f\"Cassava Mosaic Disease (CMD): {len(train_3_df)}\")\nprint(f\"Healthy: {len(train_4_df)}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n \nlabel = [\"CBB\", \"CBSD\", \"CGM\", \"CMD\", \"Healthy\"]\nnumbers = np.array([len(train_0_df), \n                   len(train_1_df), \n                   len(train_2_df), \n                   len(train_3_df), \n                   len(train_4_df)]\n                 )\n\nplt.bar(label, numbers)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 画像の読み込み"},{"metadata":{},"cell_type":"markdown","source":"用意された画像はjpg形式のため、モデルに通すには形式を変換する必要があります。"},{"metadata":{},"cell_type":"markdown","source":"#### reference  \nhttps://www.tensorflow.org/tutorials/load_data/images?hl=ja"},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nimport tensorflow_datasets as tfds","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 準備"},{"metadata":{},"cell_type":"markdown","source":"#### 画像のパス"},{"metadata":{"trusted":true},"cell_type":"code","source":"all_image_paths=[]\nfor i in range(len(train_df)):\n    all_image_paths.append(str(picture_path/train_df.loc[i]['image_id']))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"all_image_paths[:10]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### 各画像のラベルを抜き出す"},{"metadata":{"trusted":true},"cell_type":"code","source":"all_image_labels = np.array(train_df['label'])\n\nprint(\"First 10 labels indices: \", all_image_labels[:10])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 画像の読み込みと整形"},{"metadata":{},"cell_type":"markdown","source":"一枚の画像を使って流れを確認します。"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_path = all_image_paths[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Image.open(img_path)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"生の画像を取り込みます。"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_raw = tf.io.read_file(img_path)\nprint(repr(img_raw)[:100]+\"...\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"画像のテンソルにデコードします。"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_tensor = tf.image.decode_image(img_raw)\n\nprint(img_tensor.shape)\nprint(img_tensor.dtype)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"モデルに合わせてリサイズします。"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_final = tf.image.resize(img_tensor, [64, 64])\nimg_final = img_final/255.0\nprint(img_final.shape)\nprint(img_final.numpy().min())\nprint(img_final.numpy().max())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"このあと複数の画像をまとめて処理するので、簡単な関数にまとめます。"},{"metadata":{"trusted":true},"cell_type":"code","source":"def preprocess_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [64, 64])\n    image /= 255.0  # normalize to [0,1] range\n\n    return image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"画像の表示"},{"metadata":{"trusted":true},"cell_type":"code","source":"import IPython.display as display\nimport pathlib\nimport matplotlib.pyplot as plt\n\ndef load_and_preprocess_image(path):\n    image = tf.io.read_file(path)\n    return preprocess_image(image)\n\nimage_path = all_image_paths[0]\nlabel = all_image_labels[0]\n\nplt.imshow(load_and_preprocess_image(img_path))\nplt.grid(False)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### tf.data.Datasetの構築  \ntf.data.Dataset を構築するもっとも簡単な方法は、from_tensor_slices メソッドを使うことです。"},{"metadata":{"trusted":true},"cell_type":"code","source":"path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)\nprint(path_ds)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Training用の画像データセット"},{"metadata":{"trusted":true},"cell_type":"code","source":"image_ds = path_ds.map(load_and_preprocess_image, num_parallel_calls=tf.data.experimental.AUTOTUNE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for image in image_ds.take(1):\n    new_image=image.numpy()\n    print(new_image.shape)\n    print(new_image)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"tf.data.DatasetはそのままKerasで使うことができるようですが、やり方がわからないのでnumpyに変換します。"},{"metadata":{"trusted":true},"cell_type":"code","source":"new_image=[]\nfor image in image_ds:#.take(1)\n    new_image.append(image.numpy())\nX = np.array(new_image)\nprint(X.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nfor n,image in enumerate(image_ds.take(4)):\n    plt.figure(figsize=(8,8))\n    plt.subplot(2,2,n+1)\n    plt.imshow(image)\n    plt.grid(False)\n    plt.xticks([])\n    plt.yticks([])\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Labelのデータセット"},{"metadata":{"trusted":true},"cell_type":"code","source":"label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for label in label_ds.take(10):\n    print(label.numpy())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"numpyに変換します。"},{"metadata":{"trusted":true},"cell_type":"code","source":"new_label=[]\nfor label in label_ds:#.take(1)\n    new_label.append(label.numpy())\ny = np.array(new_label)\nprint(y.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 訓練"},{"metadata":{},"cell_type":"markdown","source":"### One Hot Encording"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.preprocessing import OneHotEncoder","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(y.shape)\nenc = OneHotEncoder(handle_unknown='ignore', sparse=False)\ny_one_hot = enc.fit_transform(y[:, np.newaxis])\n\nprint(y.shape)\nprint(y_one_hot.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Train_Test_Split"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X_train, X_val, y_train, y_val = train_test_split(X, y_one_hot, test_size=0.2)\nprint(X_train.shape)\nprint(X_val.shape)\nprint(y_train.shape)\nprint(y_val.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Kerasで訓練する"},{"metadata":{},"cell_type":"markdown","source":"#### reference  \nhttps://www.kaggle.com/bugraokcu/cnn-with-keras"},{"metadata":{"trusted":true},"cell_type":"code","source":"import keras\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten\nfrom keras.layers import Conv2D, MaxPooling2D\nfrom keras.layers.normalization import BatchNormalization\n\nnum_classes = 5\n\n#input image dimensions\nimg_rows, img_cols = 64, 64\ninput_shape = (img_rows, img_cols, 3)\n\nmodel = Sequential()\nmodel.add(Conv2D(32, kernel_size=(3, 3),\n                 activation='relu',\n                 kernel_initializer='he_normal',\n                 input_shape=input_shape))\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Conv2D(64, (3, 3), activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Conv2D(128, (3, 3), activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.4))\n\nmodel.add(Flatten())\nmodel.add(Dense(384, activation='relu'))#128\nmodel.add(Dropout(0.3))\nmodel.add(Dense(num_classes, activation='softmax'))\n\nmodel.compile(loss=keras.losses.categorical_crossentropy,\n              optimizer=keras.optimizers.Adam(lr=0.001),#lr=0.01,\n              metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(X_train, \n                    y_train,\n                    batch_size=32,# 20, 32, 64\n                    epochs=100,# 50\n                    validation_data=(X_val,y_val))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# データを確認することができる\n#print(history.history)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot training & validation accuracy values\nplt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('Model accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Test'], loc='upper left')\nplt.show()\n\n# Plot training & validation loss values\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('Model loss')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Test'], loc='upper left')\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### 考察\n学習率0.001、バッチサイズ32くらいで学習させるとtrainデータに対しては精度が上がる。  \nしかし、いくら試してもtestデータに対しての精度が上がらず、汎化性能は低い。"},{"metadata":{},"cell_type":"markdown","source":"### 推定"},{"metadata":{"trusted":true},"cell_type":"code","source":"y_pred = model.predict(X_val)#, batch_size=1, verbose=0\nprint(f\"y_pred.shape:{y_pred.shape}\")\n\ny_pred_label = np.argmax(y_pred, axis=1)\nprint(f\"y_pred_label:\\n{y_pred_label}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### サブミットファイルの作成"},{"metadata":{},"cell_type":"markdown","source":"#### テストデータの準備"},{"metadata":{"trusted":true},"cell_type":"code","source":"picture_path = Path('../input/cassava-leaf-disease-classification/test_images/')\nos.listdir(picture_path)\n# 一つしかない","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Submitファイルの準備"},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_df = pd.read_csv(dataset_path/'sample_submission.csv')\nsample_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#all_image_paths_test=[]\n#path_tmp = str(picture_path/sample_df.loc[0]['image_id'])\n#all_image_paths_test.append(path_tmp)\n#\n#print(all_image_paths_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"all_image_paths_test=[]\nfor i in range(len(sample_df)):\n    all_image_paths_test.append(str(picture_path/sample_df.loc[i]['image_id']))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path_ds_test = tf.data.Dataset.from_tensor_slices(all_image_paths_test)\nprint(path_ds_test)\n\nimage_ds_test = path_ds_test.map(load_and_preprocess_image, num_parallel_calls=tf.data.experimental.AUTOTUNE)\nprint(image_ds_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"new_image_test=[]\nfor image in image_ds_test:#.take(1)\n    new_image_test.append(image.numpy())\nX_test = np.array(new_image_test)\nprint(X_test.shape)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_pred_test = model.predict(X_test)#, batch_size=1, verbose=0\nprint(f\"y_pred_test.shape:{y_pred_test.shape}\")\n\ny_pred_label_test = np.argmax(y_pred_test, axis=1)\nprint(f\"y_pred_label_test:\\n{y_pred_label_test}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Submission dataframe\nsubmit_ID = sample_df.loc[:]['image_id']\nsubmit_TARGET = pd.DataFrame(y_pred_label_test)\n\nsubmission_df = pd.concat([submit_ID, submit_TARGET], axis=1)\nsubmission_df.columns = ['image_id','label']\n\nsubmission_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Next Action  \n過学習させないための工夫を検討する  \n- 写真データを拡張したらどうなるか\n- 特徴がわかりやすい画像を選んだり、ノイズになりそうな画像を除外して学習させたらどうなるか\n- 枚数の差を調整したらどうなるか\n- 画像の該当箇所だけ抜き出して学習させる方法はないか"},{"metadata":{},"cell_type":"markdown","source":"以上"}],"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":4,"nbformat_minor":4}