{"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":"# 🤗  Hugging Face Ecosystem: transformers + datasets¶ 🤗\n# ConvNeXt version 2 finetuning + balanced accuracy\n\nDocs: https://huggingface.co/docs/transformers/main/en/model_doc/convnextv2","metadata":{}},{"cell_type":"markdown","source":"# ⚙️ Env setup","metadata":{}},{"cell_type":"code","source":"!pip install -q evaluate","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-20T08:34:16.072905Z","iopub.execute_input":"2023-10-20T08:34:16.073153Z","iopub.status.idle":"2023-10-20T08:34:25.855271Z","shell.execute_reply.started":"2023-10-20T08:34:16.073131Z","shell.execute_reply":"2023-10-20T08:34:25.854195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport shutil\n\nimport numpy as np\nimport pandas as pd\nimport torch\n\nfrom datasets import load_dataset\nfrom torchvision.transforms import Compose, RandomResizedCrop, GaussianBlur, RandomAdjustSharpness, RandomEqualize, ToTensor\n\nfrom transformers import TrainingArguments, Trainer\nfrom transformers import ConvNextV2ForImageClassification\nfrom transformers import AutoImageProcessor, AutoModelForImageClassification\n\nimport evaluate","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-20T08:34:25.857143Z","iopub.execute_input":"2023-10-20T08:34:25.857419Z","iopub.status.idle":"2023-10-20T08:34:40.867807Z","shell.execute_reply.started":"2023-10-20T08:34:25.857389Z","shell.execute_reply":"2023-10-20T08:34:40.866926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🗂️ Dataset preparation\n","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(\"/kaggle/input/UBC-OCEAN/train.csv\")\ntest_df = pd.read_csv(\"/kaggle/input/UBC-OCEAN/test.csv\")\n\nSOURCE_DIRS = [\"/kaggle/input/UBC-OCEAN/train_thumbnails/\", \"/kaggle/input/UBC-OCEAN/test_thumbnails/\"]\nTARGET_DIR = \"/kaggle/working/dataset\"","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:34:40.868872Z","iopub.execute_input":"2023-10-20T08:34:40.869361Z","iopub.status.idle":"2023-10-20T08:34:40.888376Z","shell.execute_reply.started":"2023-10-20T08:34:40.869337Z","shell.execute_reply":"2023-10-20T08:34:40.887731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df","metadata":{"execution":{"iopub.status.busy":"2023-10-12T11:33:13.829982Z","iopub.execute_input":"2023-10-12T11:33:13.830231Z","iopub.status.idle":"2023-10-12T11:33:13.854041Z","shell.execute_reply.started":"2023-10-12T11:33:13.83021Z","shell.execute_reply":"2023-10-12T11:33:13.853143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def organize_images_by_label(df: pd.DataFrame, source_dir: str, target_dir: str) -> None:\n    for _, row in df.iterrows():\n        image_id = row[\"image_id\"]\n        label = row[\"label\"]\n\n        label_dir = os.path.join(target_dir, label)\n        os.makedirs(label_dir, exist_ok=True)\n\n        source_path = os.path.join(source_dir, f\"{image_id}_thumbnail.png\")\n        target_path = os.path.join(label_dir, f\"{image_id}_thumbnail.png\")\n\n        try:\n            shutil.copy(source_path, target_path)\n        except FileNotFoundError:\n            continue\n\n\ntrain_dir = f\"{TARGET_DIR}/train\"\norganize_images_by_label(train_df, SOURCE_DIRS[0], train_dir)\nos.makedirs(os.path.join(train_dir, \"Other\"), exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:34:40.890432Z","iopub.execute_input":"2023-10-20T08:34:40.89071Z","iopub.status.idle":"2023-10-20T08:35:32.670008Z","shell.execute_reply.started":"2023-10-20T08:34:40.890689Z","shell.execute_reply":"2023-10-20T08:35:32.668967Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = load_dataset(\"imagefolder\", data_dir=\"/kaggle/working/dataset\", split=\"train\")\ndataset = dataset.train_test_split(test_size=0.3)\ndataset","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:48:06.894333Z","iopub.execute_input":"2023-10-20T08:48:06.895076Z","iopub.status.idle":"2023-10-20T08:48:07.388742Z","shell.execute_reply.started":"2023-10-20T08:48:06.895042Z","shell.execute_reply":"2023-10-20T08:48:07.387952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset[\"train\"].features['label'].names.append('Other')\ndataset[\"train\"].features['label']","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:48:12.583714Z","iopub.execute_input":"2023-10-20T08:48:12.584552Z","iopub.status.idle":"2023-10-20T08:48:12.590271Z","shell.execute_reply.started":"2023-10-20T08:48:12.584514Z","shell.execute_reply":"2023-10-20T08:48:12.589255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = dataset[\"train\"].features[\"label\"].names\nlabel2id, id2label = dict(), dict()\n\nfor i, label in enumerate(labels):\n    label2id[label] = i\n    id2label[i] = label\n    \nid2label","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:48:56.077832Z","iopub.execute_input":"2023-10-20T08:48:56.078159Z","iopub.status.idle":"2023-10-20T08:48:56.084864Z","shell.execute_reply.started":"2023-10-20T08:48:56.078137Z","shell.execute_reply":"2023-10-20T08:48:56.083994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CHECKPOINT = \"facebook/convnextv2-tiny-1k-224\"\n\nimage_processor = AutoImageProcessor.from_pretrained(CHECKPOINT)\n\nSIZE = (\n    image_processor.size[\"shortest_edge\"]\n    if \"shortest_edge\" in image_processor.size\n    else (image_processor.size[\"height\"], image_processor.size[\"width\"])\n)\n\n_transforms = Compose([\n    RandomResizedCrop(size=SIZE, antialias=True),\n    GaussianBlur(kernel_size=(1, 5)),\n    RandomAdjustSharpness(sharpness_factor=2),\n    RandomEqualize(),\n    ToTensor()\n])","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:03.276477Z","iopub.execute_input":"2023-10-20T08:50:03.277452Z","iopub.status.idle":"2023-10-20T08:50:03.383122Z","shell.execute_reply.started":"2023-10-20T08:50:03.277422Z","shell.execute_reply":"2023-10-20T08:50:03.382457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def transforms(examples):\n    examples[\"pixel_values\"] = [_transforms(img.convert(\"RGB\")) for img in examples[\"image\"]]\n    del examples[\"image\"]\n    return examples\n\n\ndataset = dataset.with_transform(transforms)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:03.710036Z","iopub.execute_input":"2023-10-20T08:50:03.71075Z","iopub.status.idle":"2023-10-20T08:50:03.721007Z","shell.execute_reply.started":"2023-10-20T08:50:03.710719Z","shell.execute_reply":"2023-10-20T08:50:03.720033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 📐 Metrics definition","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import balanced_accuracy_score\n\n\ndef compute_metrics(pred):\n    labels = pred.label_ids\n    preds = pred.predictions.argmax(-1)\n    return {\"balanced_accuracy\": balanced_accuracy_score(labels, preds)}","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:04.702827Z","iopub.execute_input":"2023-10-20T08:50:04.703272Z","iopub.status.idle":"2023-10-20T08:50:04.708018Z","shell.execute_reply.started":"2023-10-20T08:50:04.703238Z","shell.execute_reply":"2023-10-20T08:50:04.707052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🏋 Model definition and training¶\n","metadata":{}},{"cell_type":"code","source":"def collate_fn(examples):\n    pixel_values = torch.stack([example[\"pixel_values\"] for example in examples])\n    labels = torch.tensor([example[\"label\"] for example in examples])\n    return {\"pixel_values\": pixel_values, \"labels\": labels}","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:06.863229Z","iopub.execute_input":"2023-10-20T08:50:06.863554Z","iopub.status.idle":"2023-10-20T08:50:06.868161Z","shell.execute_reply.started":"2023-10-20T08:50:06.863527Z","shell.execute_reply":"2023-10-20T08:50:06.867182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = ConvNextV2ForImageClassification.from_pretrained(\n    CHECKPOINT,\n    num_labels=len(labels),\n    id2label=id2label,\n    label2id=label2id,\n    ignore_mismatched_sizes=True,\n)\n\nmodel.to(\"cuda\")","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:08.894314Z","iopub.execute_input":"2023-10-20T08:50:08.895092Z","iopub.status.idle":"2023-10-20T08:50:17.039064Z","shell.execute_reply.started":"2023-10-20T08:50:08.895063Z","shell.execute_reply":"2023-10-20T08:50:17.038158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.num_parameters()","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:17.040645Z","iopub.execute_input":"2023-10-20T08:50:17.040985Z","iopub.status.idle":"2023-10-20T08:50:17.047375Z","shell.execute_reply.started":"2023-10-20T08:50:17.040954Z","shell.execute_reply":"2023-10-20T08:50:17.046449Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"STRATEGY = \"epoch\"\nOUTPUT_DIR = \"convnextv2-tiny\"\n\ntraining_args = TrainingArguments(\n    output_dir=OUTPUT_DIR,\n    evaluation_strategy=STRATEGY,\n    save_strategy=STRATEGY,\n    logging_steps=10,\n    \n    remove_unused_columns=False,\n\n    learning_rate=1e-6,\n    per_device_train_batch_size=8,\n    gradient_accumulation_steps=4,\n    per_device_eval_batch_size=8,\n    num_train_epochs=25,\n    warmup_ratio=0.1,\n    \n    metric_for_best_model=\"balanced_accuracy\",\n    load_best_model_at_end=True,\n    \n    push_to_hub=False,\n    report_to=\"none\"\n)\n\ntrainer = Trainer(\n    model=model,\n    args=training_args,\n    data_collator=collate_fn,\n    train_dataset=dataset[\"train\"],\n    eval_dataset=dataset[\"test\"],\n    tokenizer=image_processor,\n    compute_metrics=compute_metrics,\n)\n\ntrainer.train()\n#trainer.train(resume_from_checkpoint=True)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T08:50:37.07051Z","iopub.execute_input":"2023-10-20T08:50:37.071205Z","iopub.status.idle":"2023-10-20T09:31:15.845705Z","shell.execute_reply.started":"2023-10-20T08:50:37.071166Z","shell.execute_reply":"2023-10-20T09:31:15.844762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"save_dir = f\"model/{OUTPUT_DIR}\"\ntrainer.save_model(save_dir)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T09:40:13.434541Z","iopub.execute_input":"2023-10-20T09:40:13.435164Z","iopub.status.idle":"2023-10-20T09:40:13.631384Z","shell.execute_reply.started":"2023-10-20T09:40:13.435134Z","shell.execute_reply":"2023-10-20T09:40:13.630448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🔮 Prediction","metadata":{}},{"cell_type":"code","source":"test_df","metadata":{"execution":{"iopub.status.busy":"2023-10-20T09:40:17.895003Z","iopub.execute_input":"2023-10-20T09:40:17.895698Z","iopub.status.idle":"2023-10-20T09:40:17.908667Z","shell.execute_reply.started":"2023-10-20T09:40:17.895667Z","shell.execute_reply":"2023-10-20T09:40:17.90788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = load_dataset(\"imagefolder\", data_dir=SOURCE_DIRS[1], split=\"train\")\nimage = test_dataset[\"image\"][0]\n\nCHECKPOINT_DIR = save_dir\n\nDEVICE = \"cpu\"\nimage_processor = AutoImageProcessor.from_pretrained(CHECKPOINT_DIR)\n\ninputs = image_processor(image, return_tensors=\"pt\")\ninputs = inputs.to(DEVICE)\n\nmodel = AutoModelForImageClassification.from_pretrained(CHECKPOINT_DIR)\nmodel = model.to(DEVICE)\n\nwith torch.no_grad():\n    logits = model(**inputs).logits\n    \npredicted_label = logits.argmax(-1).item()\npredicted_label_name = model.config.id2label[predicted_label]\npredicted_label_name","metadata":{"execution":{"iopub.status.busy":"2023-10-20T09:40:23.262242Z","iopub.execute_input":"2023-10-20T09:40:23.262575Z","iopub.status.idle":"2023-10-20T09:40:24.528343Z","shell.execute_reply.started":"2023-10-20T09:40:23.262546Z","shell.execute_reply":"2023-10-20T09:40:24.527462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.express as px\nimport plotly.io as pio\npio.renderers.default = 'notebook'\n\nimage_array = np.array(image)\n\nfig = px.imshow(image_array)\n\nfig.update_layout(\n    title=f\"Predicted label: {predicted_label_name}\",\n    xaxis_title=\"Width\",\n    yaxis_title=\"Height\"\n)\n\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-10-20T09:40:28.296769Z","iopub.execute_input":"2023-10-20T09:40:28.297192Z","iopub.status.idle":"2023-10-20T09:40:30.300669Z","shell.execute_reply.started":"2023-10-20T09:40:28.297154Z","shell.execute_reply":"2023-10-20T09:40:30.299169Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}