{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":70905,"databundleVersionId":7746452,"sourceType":"competition"}],"dockerImageVersionId":30648,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"This notebook implements a simple starter convolution neural network for the age prediction Kaggle, created by Jeff Heaton and ChatGPT. We begin by creating a simple neural network and detect if we should use a GPU.\n\nI created this notebook from the following prompt in ChatGPT.\n\n```\nCreate a PyTorch GPU-enabled neural network for a Kaggle competition that asks me to predict the age of people in provided images.  The images are stored at this path: /kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age. The training data is in the file: /kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/train.csv. The training data has 3 columns, id, filename, and age. The field age is the target, to be predicted, numeric age in years of the person. The file contains the filename of the image that corresponds to each row, the images are named 1.jpg, 2.jpg, etc, which corresponds to both the id and the filename fields. There is also a test dataset that we must generate a submission dataframe for. The test data is in the file /kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/test.csv, and has the id and filename columns, but we need to generate a submit dataframe with just id and age(the prediction). Train the neural network, use early stopping and generate the submit dataframe.\n```\n\nThis example scores about 9.8057 on the public leaderboard.","metadata":{}},{"cell_type":"markdown","source":"# Step 1, Data Loading\n\nLoad the training and test data from the CSV files.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport os\nfrom PIL import Image\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nimport torch.nn as nn\nimport torch.optim as optim\n\n# Load the data\ntrain_df = pd.read_csv('/kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/train.csv')\ntest_df = pd.read_csv('/kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/test.csv')\n","metadata":{"execution":{"iopub.status.busy":"2024-02-18T22:07:53.341362Z","iopub.execute_input":"2024-02-18T22:07:53.342242Z","iopub.status.idle":"2024-02-18T22:07:56.922625Z","shell.execute_reply.started":"2024-02-18T22:07:53.342206Z","shell.execute_reply":"2024-02-18T22:07:56.921644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 2: Dataset and DataLoader Setup\n\nCreate custom PyTorch datasets for the training and test images and their corresponding labels. Set up DataLoader for batching.","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader, Dataset\nimport pandas as pd\nfrom PIL import Image\nimport os\n\nclass FacesAgeDataset(Dataset):\n    def __init__(self, dataframe, root_dir, transform=None):\n        \"\"\"\n        Args:\n            dataframe (pandas.DataFrame): DataFrame containing image file paths and ages.\n            root_dir (string): Directory with all the images.\n            transform (callable, optional): Optional transform to be applied on a sample.\n        \"\"\"\n        self.dataframe = dataframe\n        self.root_dir = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        img_name = os.path.join(self.root_dir, self.dataframe.iloc[idx, 1])\n        image = Image.open(img_name)\n        age = self.dataframe.iloc[idx, 2]\n        if self.transform:\n            image = self.transform(image)\n        return image, age\n\n# Load dataset\ndataset_csv = '/kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/train.csv'\nroot_dir = '/kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/'\ndf = pd.read_csv(dataset_csv)\n\n# Split dataset into training and validation\ntrain_df, val_df = train_test_split(df, test_size=0.2, random_state=42)\n\n# Define transformations\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),  # Resize images to fit the model expected input size\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\n# Create PyTorch datasets\ntrain_dataset = FacesAgeDataset(dataframe=train_df, root_dir=root_dir, transform=transform)\nval_dataset = FacesAgeDataset(dataframe=val_df, root_dir=root_dir, transform=transform)\n","metadata":{"execution":{"iopub.status.busy":"2024-02-18T22:07:56.924838Z","iopub.execute_input":"2024-02-18T22:07:56.925205Z","iopub.status.idle":"2024-02-18T22:07:57.508679Z","shell.execute_reply.started":"2024-02-18T22:07:56.925172Z","shell.execute_reply":"2024-02-18T22:07:57.507909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 3: Model Architecture\n\nDefine a neural network architecture suitable for age prediction from images.\n","metadata":{}},{"cell_type":"code","source":"model = models.resnet18(pretrained=True)\nfor param in model.parameters():\n    param.requires_grad = False\n\n# Replace the classifier\nmodel.fc = nn.Sequential(\n    nn.Linear(model.fc.in_features, 512),\n    nn.ReLU(),\n    nn.Dropout(0.2),\n    nn.Linear(512, 1)  # Predicting a single continuous value\n)\n\n# Move the model to the GPU\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n","metadata":{"execution":{"iopub.status.busy":"2024-02-18T22:07:57.509855Z","iopub.execute_input":"2024-02-18T22:07:57.510146Z","iopub.status.idle":"2024-02-18T22:07:57.916931Z","shell.execute_reply.started":"2024-02-18T22:07:57.510120Z","shell.execute_reply":"2024-02-18T22:07:57.915982Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 4: Training Setup\n\nDefine loss function, optimizer, and configure the model for GPU training.\n","metadata":{}},{"cell_type":"code","source":"criterion = nn.MSELoss()\noptimizer = optim.Adam(model.fc.parameters(), lr=0.001)","metadata":{"execution":{"iopub.status.busy":"2024-02-18T22:07:57.918319Z","iopub.execute_input":"2024-02-18T22:07:57.918791Z","iopub.status.idle":"2024-02-18T22:07:57.923848Z","shell.execute_reply.started":"2024-02-18T22:07:57.918755Z","shell.execute_reply":"2024-02-18T22:07:57.922852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 5: Training Loop: \n\nImplement the training loop, including validation to monitor overfitting and implement early stopping.","metadata":{}},{"cell_type":"code","source":"import torch\nfrom torch import nn, optim\nfrom torch.utils.data import DataLoader\nfrom sklearn.model_selection import train_test_split\nimport numpy as np\nimport pandas as pd\nfrom torchvision import transforms\nfrom torchvision.datasets import ImageFolder\nimport os\nimport copy\n\n# Assuming variables `train_dataset` and `val_dataset` are already defined\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\nprint(f\"Using device: {device}\")\n\ncriterion = nn.MSELoss()  # Since age prediction is a regression problem\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n\n# Early stopping parameters\nearly_stopping_patience = 10\nearly_stopping_counter = 0\nmin_val_loss = np.inf\n\nnum_epochs = 100  # Set a high number and rely on early stopping\n\nprint(\"Beginning training...\")\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    for inputs, ages in train_loader:\n        inputs, ages = inputs.to(device), ages.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(inputs)\n        loss = criterion(outputs, ages.view(-1, 1).float())\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item() * inputs.size(0)\n\n    epoch_loss = running_loss / len(train_loader.dataset)\n\n    # Validation phase\n    model.eval()\n    val_loss = 0.0\n    with torch.no_grad():\n        for inputs, ages in val_loader:\n            inputs, ages = inputs.to(device), ages.to(device)\n            outputs = model(inputs)\n            loss = criterion(outputs, ages.view(-1, 1).float())\n            val_loss += loss.item() * inputs.size(0)\n\n    val_loss /= len(val_loader.dataset)\n\n    print(f\"Epoch {epoch+1}, Training Loss: {epoch_loss:.4f}, Validation Loss: {val_loss:.4f}\")\n\n    # Early Stopping\n    if val_loss < min_val_loss:\n        min_val_loss = val_loss\n        best_model_wts = copy.deepcopy(model.state_dict())\n        early_stopping_counter = 0\n    else:\n        early_stopping_counter += 1\n        if early_stopping_counter >= early_stopping_patience:\n            print(\"Early stopping\")\n            break\n\nmodel.load_state_dict(best_model_wts)\n","metadata":{"execution":{"iopub.status.busy":"2024-02-18T22:07:57.925870Z","iopub.execute_input":"2024-02-18T22:07:57.926180Z","iopub.status.idle":"2024-02-18T23:30:40.241838Z","shell.execute_reply.started":"2024-02-18T22:07:57.926151Z","shell.execute_reply":"2024-02-18T23:30:40.240900Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 6: Prediction and Submission: \n\nPredict ages on the test set and prepare the submission DataFrame.","metadata":{}},{"cell_type":"code","source":"class FacesAgeTestDataset(Dataset):\n    def __init__(self, dataframe, root_dir, transform=None):\n        \"\"\"\n        Args:\n            dataframe (pandas.DataFrame): DataFrame containing image file paths.\n            root_dir (string): Directory with all the images.\n            transform (callable, optional): Optional transform to be applied on a sample.\n        \"\"\"\n        self.dataframe = dataframe\n        self.root_dir = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        img_name = os.path.join(self.root_dir, self.dataframe.iloc[idx, 1])\n        image = Image.open(img_name)\n        if self.transform:\n            image = self.transform(image)\n        return image\n\n# Load the test data\ntest_csv = '/kaggle/input/applications-of-deep-learning-wustl-spring-2024/faces-age/test.csv'\ntest_df = pd.read_csv(test_csv)\n\n# Create the PyTorch dataset for test data\ntest_dataset = FacesAgeTestDataset(dataframe=test_df, root_dir=root_dir, transform=transform)\n\n# Create DataLoader for the test dataset\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)\n","metadata":{"execution":{"iopub.status.busy":"2024-02-18T23:35:50.863145Z","iopub.execute_input":"2024-02-18T23:35:50.864122Z","iopub.status.idle":"2024-02-18T23:35:50.878552Z","shell.execute_reply.started":"2024-02-18T23:35:50.864087Z","shell.execute_reply":"2024-02-18T23:35:50.877537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm  # Ensure tqdm is installed for progress bar functionality\n\npredictions = []\nmodel.eval()  # Ensure the model is in evaluation mode\nwith torch.no_grad():\n    for inputs in tqdm(test_loader, desc=\"Processing submit\"):  # Note the removal of _, as we only get inputs\n        inputs = inputs.to(device)\n        outputs = model(inputs)\n        # Assuming the model outputs predictions in a format that matches the problem requirements\n        predictions.extend(outputs.view(-1).cpu().numpy())\n\n# Prepare the submission DataFrame\nsubmit_df = pd.DataFrame({\n    \"id\": test_df[\"id\"],\n    \"age\": predictions\n})\n\nsubmit_df.to_csv('/kaggle/working/submission.csv', index=False)\nprint(\"Submission DataFrame generated and saved.\")\n","metadata":{"execution":{"iopub.status.busy":"2024-02-18T23:36:00.681877Z","iopub.execute_input":"2024-02-18T23:36:00.682560Z","iopub.status.idle":"2024-02-18T23:36:09.267396Z","shell.execute_reply.started":"2024-02-18T23:36:00.682526Z","shell.execute_reply":"2024-02-18T23:36:09.266432Z"},"trusted":true},"execution_count":null,"outputs":[]}]}