{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.10","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":27835,"databundleVersionId":2255017,"sourceType":"competition"}],"dockerImageVersionId":30097,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Import required dependencies","metadata":{"id":"hYB8Aq8NYbFl"}},{"cell_type":"code","source":"import os\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.image as mpimg\n\nfrom zipfile import ZipFile\nfrom os import listdir, walk\nfrom os.path import isfile, join\nfrom tensorflow.keras import Input, Model\nfrom tensorflow.keras.applications import ResNet101V2\nfrom tensorflow.keras.applications.imagenet_utils import preprocess_input\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense\nfrom tensorflow.keras.optimizers import Adam","metadata":{"id":"yoPGB38MYdJe","execution":{"iopub.status.busy":"2024-02-13T09:17:43.682829Z","iopub.execute_input":"2024-02-13T09:17:43.683280Z","iopub.status.idle":"2024-02-13T09:17:48.712909Z","shell.execute_reply.started":"2024-02-13T09:17:43.683176Z","shell.execute_reply":"2024-02-13T09:17:48.712024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preprocessing","metadata":{"id":"xo0DwSJOP-nk"}},{"cell_type":"markdown","source":"## Rescaling and centering the image","metadata":{"id":"tCVR8frMYy3F"}},{"cell_type":"code","source":"IMG_SIZE = 224","metadata":{"id":"2bM-ySTLZOo7","execution":{"iopub.status.busy":"2024-02-13T09:18:16.167521Z","iopub.execute_input":"2024-02-13T09:18:16.167842Z","iopub.status.idle":"2024-02-13T09:18:16.171672Z","shell.execute_reply.started":"2024-02-13T09:18:16.167815Z","shell.execute_reply":"2024-02-13T09:18:16.170719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"datagen = ImageDataGenerator(\n        samplewise_center=True,\n        rescale=1/255.)","metadata":{"id":"dZWoBajmPvBG","execution":{"iopub.status.busy":"2024-02-13T09:18:22.223521Z","iopub.execute_input":"2024-02-13T09:18:22.223851Z","iopub.status.idle":"2024-02-13T09:18:22.227974Z","shell.execute_reply.started":"2024-02-13T09:18:22.223822Z","shell.execute_reply":"2024-02-13T09:18:22.227076Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create data generator","metadata":{"id":"4GI4qIWhY1CD"}},{"cell_type":"code","source":"train_it = datagen.flow_from_directory('../input/dlai5-bad-apples/train/train', \n                                       target_size=(IMG_SIZE, IMG_SIZE), \n                                       color_mode='rgb', \n                                       class_mode=\"categorical\")","metadata":{"id":"okmVK2AqQHII","outputId":"59ffc259-141f-4522-9c5f-8866e3acebc2","execution":{"iopub.status.busy":"2024-02-13T09:18:27.256485Z","iopub.execute_input":"2024-02-13T09:18:27.256837Z","iopub.status.idle":"2024-02-13T09:18:32.345515Z","shell.execute_reply.started":"2024-02-13T09:18:27.256800Z","shell.execute_reply":"2024-02-13T09:18:32.344624Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valid_it = datagen.flow_from_directory('../input/dlai5-bad-apples/test/test', \n                                       target_size=(IMG_SIZE, IMG_SIZE), \n                                       color_mode='rgb', \n                                       class_mode=\"categorical\")","metadata":{"id":"qkB7-tZ2AzBg","outputId":"3b51be25-b53d-4067-c367-18f0725623b5","execution":{"iopub.status.busy":"2024-02-13T09:18:36.967173Z","iopub.execute_input":"2024-02-13T09:18:36.967574Z","iopub.status.idle":"2024-02-13T09:18:37.404837Z","shell.execute_reply.started":"2024-02-13T09:18:36.967537Z","shell.execute_reply":"2024-02-13T09:18:37.403934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Modeling","metadata":{"id":"yI0NXR8FM22J"}},{"cell_type":"markdown","source":"## Choosing base model and freeze it","metadata":{"id":"wbbFK-zCYfJQ"}},{"cell_type":"code","source":"base_model = ResNet101V2(\n    weights='imagenet',\n    input_shape=(IMG_SIZE, IMG_SIZE, 3),\n    include_top=False)\n\nbase_model.trainable = False","metadata":{"id":"Syu9xcdRMuau","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Add new custom layer","metadata":{"id":"wz-sxeZ4Yjfm"}},{"cell_type":"code","source":"# Define the input shape for the model, assuming IMG_SIZE x IMG_SIZE images with 3 color channels (RGB)\ninputs = Input(shape=(IMG_SIZE, IMG_SIZE, 3))\n\n# Connect the input to a pre-trained base model, with training set to False to freeze the base model's weights\nx = base_model(inputs, training=False)\n\n# Apply Global Average Pooling to reduce spatial dimensions and extract important features\nx = GlobalAveragePooling2D()(x)\n\n# Connect the output of the Global Average Pooling layer to a Dense layer with softmax activation\n# The number of units in the Dense layer is set to the number of classes in the training dataset\noutputs = Dense(len(train_it.class_indices), activation='softmax')(x)\n\nmodel = Model(inputs, outputs)","metadata":{"id":"dAbeKRVpPT7k","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"id":"NFVvNYEiPbaT","outputId":"48d6db3e-f4cf-4d5d-e30f-52c4f6bbd0d4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Compile a model","metadata":{"id":"3Xu393dCYnIs"}},{"cell_type":"code","source":"model.compile(loss = 'categorical_crossentropy' , optimizer='adam', metrics = [\"accuracy\"])","metadata":{"id":"M__kjTKtPe-x","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training","metadata":{"id":"wLyd94SWY5n_"}},{"cell_type":"code","source":"epochs = 20\n\nhistory = model.fit(\n    train_it,\n    epochs=epochs,\n    validation_data=valid_it,\n)","metadata":{"id":"YzYiRkQCQvag","outputId":"64d1372e-4297-4b16-9a3f-87642a4c05d4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Fine-tuning","metadata":{"id":"yaCd5HdUIuNl"}},{"cell_type":"markdown","source":"### Unfreeze some layers","metadata":{"id":"Qv8ZrCemaVHm"}},{"cell_type":"code","source":"base_model.trainable = True\n\nfine_tune_at = 100\n\nfor layer in base_model.layers[:fine_tune_at]:\n  layer.trainable =  False","metadata":{"id":"dphpv6G2IvvX","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(loss = 'categorical_crossentropy' , optimizer=Adam(learning_rate=0.001/10), metrics = [\"accuracy\"])","metadata":{"id":"NjGeEyi8I7KQ","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 10\nhistory = model.fit(\n    train_it,\n    epochs=epochs,\n    validation_data=valid_it,\n)","metadata":{"id":"e2_JmId7JimC","outputId":"222b0b71-9c17-42de-e5cc-c5ff52aee5e4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save model","metadata":{"id":"oRZyWMFoaj6v"}},{"cell_type":"code","source":"model.save('resnet_fine_tuned.h5')","metadata":{"id":"pY0tQKtedsyj","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save('dlai5_resnet_fine_tuned')","metadata":{"id":"pelx3Nx3Q4OM","outputId":"52ecb766-481d-4f5c-b445-381110b7a00f","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"zf = ZipFile(\"model_resnet_fine_tuned.zip\", \"w\")\n\n# Iterate through the directory \"dlai5_resnet_fine_tuned\" using os.walk\nfor dirname, subdirs, files in walk(\"dlai5_resnet_fine_tuned\"):\n    # Add the current directory to the zip file\n    zf.write(dirname)\n    \n    # Iterate through files in the current directory and add them to the zip file\n    for filename in files:\n        zf.write(os.path.join(dirname, filename))\n\n# Close the ZipFile object\nzf.close()\n","metadata":{"id":"uS_WFufxeU-i","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prediction","metadata":{"id":"h5k-FtF3aoYv"}},{"cell_type":"code","source":"files = [f for f in listdir('../input/dlai5-bad-apples/validation/validation') if isfile(join('../input/dlai5-bad-apples/validation/validation', f))]","metadata":{"id":"mn5c3OxJgbBm","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialize a dictionary to store the results with 'ID' and 'Code' as keys\nresult_dict = {\n    'ID': [],\n    'Code': [],\n}\n\n# Iterate through each file in the list of files\nfor file in files:\n    # Load and preprocess the image for prediction\n    image = load_img(f'../input/dlai5-bad-apples/validation/validation/{file}', target_size=(IMG_SIZE, IMG_SIZE))\n    image = img_to_array(image)\n    image = image.reshape(1, IMG_SIZE, IMG_SIZE, 3)\n    image = image / 255\n\n    # Make a prediction using the pre-trained model and obtain the predicted class\n    pred = np.argmax(model.predict(image)) + 1  # Adding 1 because classes are assumed to start from 1\n    \n    result_dict['ID'].append(file)\n    result_dict['Code'].append(pred)\n","metadata":{"id":"VTNyqXcog3ba","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.DataFrame.from_dict(result_dict)\ndf.to_csv('result_resnet_fine_tuned.csv', index=False)","metadata":{"id":"O6_hdGlHjKym","trusted":true},"execution_count":null,"outputs":[]}]}