{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Dogs vs Cats via FastAI\nThe general idea here is to use a pretrained FastAI model. This should be quite an easy approach. This notebook is mostly meant to be a walkthrough of FastAI image libraries. \n\nI've set this up so it can run locally or on Kaggle.","metadata":{}},{"cell_type":"code","source":"import os\nfrom pathlib import Path\nfrom fastai.vision.all import untar_data, URLs, get_image_files\nimport zipfile\n\n# boilerplate to setup path to images on either a local (fastai download) or Kaggle run\n\nLOCAL_RUN = os.environ.get('KAGGLE_KERNEL_RUN_TYPE','Localhost') == 'Localhost'\n\npath = None\nif LOCAL_RUN:\n    path = untar_data(URLs.PETS)/'images'\nelse:\n    input = Path(\"/kaggle/input/dogs-vs-cats\")\n    os.mkdir(\"images\")\n    with zipfile.ZipFile(input/\"train.zip\", 'r') as zf:\n        zf.extractall(\"images\")\n    with zipfile.ZipFile(input/\"test1.zip\", 'r') as zf:\n        zf.extractall(\"images\")\n    path = Path(\"./images\")\n\n# make sure path is setup and your files exist.\nassert len(get_image_files(path/'train')) > 1000\nassert len(get_image_files(path/'test1')) > 1000","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-01T01:55:33.374912Z","iopub.execute_input":"2025-10-01T01:55:33.375600Z","iopub.status.idle":"2025-10-01T01:56:00.111805Z","shell.execute_reply.started":"2025-10-01T01:55:33.375576Z","shell.execute_reply":"2025-10-01T01:56:00.111107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## this is more boilerplate code just used for development so I don't have to work \n# with the entire set\n\nimport random\n\n# get a subset of train file so it runs faster, by default return everything\ndef get_train_filenames(subset_size = -1): \n    fnames = get_image_files(path/'train')\n\n    if subset_size < 0 or len(fnames) < subset_size:\n        return fnames\n    else:\n        random.seed(42)\n        return random.sample(fnames, subset_size)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-01T01:56:15.976317Z","iopub.execute_input":"2025-10-01T01:56:15.976655Z","iopub.status.idle":"2025-10-01T01:56:15.981539Z","shell.execute_reply.started":"2025-10-01T01:56:15.976629Z","shell.execute_reply":"2025-10-01T01:56:15.980511Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## FastAI Dataloader\nInitialize the dataloader, this","metadata":{}},{"cell_type":"code","source":"from fastai.vision.all import ImageDataLoaders, Resize\n\ndef get_label(filename):\n    if \"dog\" in filename:\n        return 'dog'\n    elif \"cat\" in filename:\n        return 'cat'\n    else:\n        raise ValueError(\"Neither dog or cat specified in filename\")\n\ndls = ImageDataLoaders.from_name_func(   #from_name creates dataset based on filename\n                      path=\".\", # work dir\n                      fnames=get_train_filenames(), # get training file names\n                      label_func=get_label, #labelling function based onf ilename\n                      valid_pct=0.2, #hold for validation\n                      seed=42,\n                      item_tfms=Resize(192)) # resize basedon fastAI recommendation\n\ndls.show_batch()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-01T01:56:28.118185Z","iopub.execute_input":"2025-10-01T01:56:28.118736Z","iopub.status.idle":"2025-10-01T01:56:30.362067Z","shell.execute_reply.started":"2025-10-01T01:56:28.118713Z","shell.execute_reply":"2025-10-01T01:56:30.361252Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Use resnet\nResnet18 is 18 is an 18 layer CNN that is very efficient with resources and generally considered a good beginner model. We will just use and tune it instead of building our own.","metadata":{}},{"cell_type":"code","source":"from fastai.vision.all import vision_learner, resnet18, error_rate\nlearn = vision_learner(dls, resnet18, metrics=error_rate)\nlearn.fine_tune(3, base_lr=1e-4) # use 3 epochs, lr was calculated after running learn.lr_find()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.show_results()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-01T02:01:39.765522Z","iopub.execute_input":"2025-10-01T02:01:39.765804Z","iopub.status.idle":"2025-10-01T02:01:40.924207Z","shell.execute_reply.started":"2025-10-01T02:01:39.765779Z","shell.execute_reply":"2025-10-01T02:01:40.923215Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Another thing that is useful is an interpretation object, it can show us where the model made the worse predictions:\nfrom fastai.vision.all import Interpretation\ninterp = Interpretation.from_learner(learn)\ninterp.plot_top_losses(9, figsize=(15,10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-01T02:01:40.925322Z","iopub.execute_input":"2025-10-01T02:01:40.925626Z","iopub.status.idle":"2025-10-01T02:01:53.351244Z","shell.execute_reply.started":"2025-10-01T02:01:40.925602Z","shell.execute_reply":"2025-10-01T02:01:53.349973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Export the model for re-use (aka upload to HuggingFace)\nlearn.export('model.pkl')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Run on Test Data\n\nFinally we create our predictions for the test data.\n","metadata":{}},{"cell_type":"code","source":"test_dl = dls.test_dl(get_image_files(path/\"test1\"))\npreds, _ = learn.get_preds(dl=test_dl)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\npred_probs, pred_idxs = preds.max(dim=1)\n\n# We loop through the length of the predictions, \n#and drop everything in a df with the correct column names\ndf = pd.DataFrame(columns=[\"id\", \"label\"])\nfor i in range(len(test_dl.items)):\n    file_path = test_dl.items[i]\n\n    file_name = file_path.stem #just filename without \n\n    # The prediction data is also at index 'i'\n    confidence = pred_probs[i].item()\n    df.loc[len(df)] = [file_name, pred_idxs[i].item()]\n    \n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_sorted = df.sort_values(by='id') # sort so its easier to manually spot check\ndf.to_csv('submission.csv', index=False)\nprint(\"Your submission was successfully saved!\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}