Yuval728 commited on
Commit
02e54e3
·
verified ·
1 Parent(s): 59484e3

Upload 12 files

Browse files
Files changed (12) hide show
  1. .gitignore +3 -0
  2. app.py +75 -0
  3. classes.txt +4 -0
  4. dataset.py +27 -0
  5. dog_emotion.ipynb +1 -0
  6. dog_emotion_model.pth +3 -0
  7. model.py +17 -0
  8. readme.md +28 -0
  9. requirements.txt +9 -0
  10. test.py +22 -0
  11. train.py +66 -0
  12. utils.py +63 -0
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ data
2
+ archive.zip
3
+ main.ipynb
app.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torchvision import models
4
+ import gradio as gr
5
+ from typing import Tuple, Dict
6
+ from timeit import default_timer as timer
7
+
8
+ class DogEmotionResNet(nn.Module):
9
+ def __init__(self, num_classes, weights=None):
10
+ super().__init__()
11
+
12
+ self.resnet = models.resnet50(weights=weights)
13
+ for param in self.resnet.parameters():
14
+ param.requires_grad = False
15
+
16
+ in_features = self.resnet.fc.in_features
17
+ self.resnet.fc = nn.Linear(in_features, num_classes)
18
+
19
+ def forward(self, x):
20
+ return self.resnet(x)
21
+
22
+ def load_model(weights_path: str, num_classes: int) -> DogEmotionResNet:
23
+ resnet_weights = models.ResNet50_Weights.DEFAULT
24
+ model = DogEmotionResNet(num_classes=num_classes, weights=resnet_weights)
25
+ model.load_state_dict(torch.load(weights_path))
26
+ return model
27
+
28
+ def load_class_names(file_path: str) -> list:
29
+ with open(file_path, "r") as f:
30
+ class_names = [emotion.strip() for emotion in f.readlines()]
31
+ return class_names
32
+
33
+ def predict(img) -> Tuple[Dict, float]:
34
+ """Transforms and performs a prediction on img and returns prediction and time taken."""
35
+ start_time = timer()
36
+
37
+ img = resnet_transform(img).unsqueeze(0)
38
+
39
+ model.eval()
40
+ with torch.inference_mode():
41
+ pred_probs = torch.softmax(model(img), dim=1)
42
+
43
+ pred_labels_and_probs = {class_names[i]: float(pred_probs[0][i]) for i in range(len(class_names))}
44
+
45
+ pred_time = round(timer() - start_time, 5)
46
+ return pred_labels_and_probs, pred_time
47
+
48
+ # Load model and class names
49
+ weights_path = 'dog_emotion_model.pth'
50
+ class_names_file = "classes.txt"
51
+
52
+ model = load_model(weights_path, num_classes=4)
53
+ class_names = load_class_names(class_names_file)
54
+ resnet_weights = models.ResNet50_Weights.DEFAULT
55
+ resnet_transform = resnet_weights.transforms()
56
+
57
+ # Gradio Interface
58
+ title = "Dog Emotion Classifier 🐶🎭"
59
+ description = "This app classifies the emotion of a dog in an image into one of four categories: happy, sad, angry, or relaxed."
60
+ article = ""
61
+
62
+ demo = gr.Interface(
63
+ fn=predict,
64
+ inputs=gr.Image(type="pil"),
65
+ outputs=[
66
+ gr.Label(num_top_classes=5, label="Predictions"),
67
+ gr.Number(label="Prediction time (s)"),
68
+ ],
69
+ title=title,
70
+ description=description,
71
+ article=article,
72
+ )
73
+
74
+ # Launch the app!
75
+ demo.launch()
classes.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ angry
2
+ happy
3
+ relaxed
4
+ sad
dataset.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torchvision import datasets, transforms, models
3
+
4
+ def get_datasets(data_dir):
5
+ resnet_weights = models.ResNet50_Weights.DEFAULT
6
+
7
+ data_transforms = {
8
+ 'train': transforms.Compose([
9
+ transforms.Resize((224, 224)),
10
+ transforms.ToTensor(),
11
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
12
+ ]),
13
+ 'test': transforms.Compose([
14
+ transforms.Resize((224, 224)),
15
+ transforms.ToTensor(),
16
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
17
+ ])
18
+ }
19
+
20
+ image_dataset = datasets.ImageFolder(data_dir, transform=resnet_weights.transforms())
21
+
22
+ train_size = int(0.9 * len(image_dataset))
23
+ test_size = len(image_dataset) - train_size
24
+
25
+ train_dataset, test_dataset = torch.utils.data.random_split(image_dataset, [train_size, test_size], generator=torch.Generator().manual_seed(42))
26
+
27
+ return train_dataset, test_dataset, image_dataset.classes
dog_emotion.ipynb ADDED
@@ -0,0 +1 @@
 
 
1
+ {"cells":[{"cell_type":"code","execution_count":2,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.816891Z","iopub.status.busy":"2024-07-13T14:16:59.815968Z","iopub.status.idle":"2024-07-13T14:16:59.821084Z","shell.execute_reply":"2024-07-13T14:16:59.820084Z","shell.execute_reply.started":"2024-07-13T14:16:59.816858Z"},"trusted":true},"outputs":[],"source":["import pandas as pd\n","import numpy as np\n","import matplotlib.pyplot as plt\n","import os"]},{"cell_type":"code","execution_count":3,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.837306Z","iopub.status.busy":"2024-07-13T14:16:59.836599Z","iopub.status.idle":"2024-07-13T14:16:59.844964Z","shell.execute_reply":"2024-07-13T14:16:59.843982Z","shell.execute_reply.started":"2024-07-13T14:16:59.837259Z"},"trusted":true},"outputs":[{"data":{"text/plain":["'2.3.0+cu121'"]},"execution_count":3,"metadata":{},"output_type":"execute_result"}],"source":["import torch\n","from torch import nn, optim\n","from torch.utils.data import DataLoader, Dataset\n","from torchvision import datasets, transforms, models\n","torch.__version__"]},{"cell_type":"code","execution_count":4,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.849583Z","iopub.status.busy":"2024-07-13T14:16:59.849298Z","iopub.status.idle":"2024-07-13T14:16:59.855674Z","shell.execute_reply":"2024-07-13T14:16:59.854751Z","shell.execute_reply.started":"2024-07-13T14:16:59.849561Z"},"trusted":true},"outputs":[{"data":{"text/plain":["'cuda'"]},"execution_count":4,"metadata":{},"output_type":"execute_result"}],"source":["device='cuda' if torch.cuda.is_available() else 'cpu'\n","device"]},{"cell_type":"code","execution_count":9,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.866616Z","iopub.status.busy":"2024-07-13T14:16:59.866153Z","iopub.status.idle":"2024-07-13T14:16:59.889656Z","shell.execute_reply":"2024-07-13T14:16:59.888806Z","shell.execute_reply.started":"2024-07-13T14:16:59.866593Z"},"trusted":true},"outputs":[],"source":["# dir = '/kaggle/input/dog-emotion/Dog Emotion/'\n","dir='data/'"]},{"cell_type":"code","execution_count":6,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.919603Z","iopub.status.busy":"2024-07-13T14:16:59.919302Z","iopub.status.idle":"2024-07-13T14:16:59.928354Z","shell.execute_reply":"2024-07-13T14:16:59.927503Z","shell.execute_reply.started":"2024-07-13T14:16:59.919580Z"},"trusted":true},"outputs":[],"source":["resnet_weights=models.ResNet50_Weights.DEFAULT"]},{"cell_type":"code","execution_count":10,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.930583Z","iopub.status.busy":"2024-07-13T14:16:59.930150Z","iopub.status.idle":"2024-07-13T14:16:59.938564Z","shell.execute_reply":"2024-07-13T14:16:59.937780Z","shell.execute_reply.started":"2024-07-13T14:16:59.930552Z"},"trusted":true},"outputs":[],"source":["train_transforms = transforms.Compose([\n"," transforms.Resize((224, 224)),\n"," transforms.ToTensor(),\n"," transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n","])\n","\n","test_transforms = transforms.Compose([\n"," transforms.Resize((224, 224)),\n"," transforms.ToTensor(),\n"," transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n","])\n"]},{"cell_type":"code","execution_count":11,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:16:59.941623Z","iopub.status.busy":"2024-07-13T14:16:59.941285Z","iopub.status.idle":"2024-07-13T14:17:01.610438Z","shell.execute_reply":"2024-07-13T14:17:01.609614Z","shell.execute_reply.started":"2024-07-13T14:16:59.941593Z"},"trusted":true},"outputs":[],"source":["# train_dataset = DogEmotionDataset(\n","# images=X_train.values,\n","# labels=y_train.values,\n","# transforms=train_transforms,\n","# classes=classes\n","# )\n","\n","# test_dataset = DogEmotionDataset(\n","# images=X_test.values,\n","# labels=y_test.values,\n","# transforms=test_transforms,\n","# classes=classes\n","# )\n","\n","dogdataset = datasets.ImageFolder(dir, transform=resnet_weights.transforms())\n","\n","train_size = int(0.9 * len(dogdataset))\n","test_size = len(dogdataset) - train_size\n","\n","train_dataset, test_dataset = torch.utils.data.random_split(dogdataset, [train_size, test_size],generator=torch.Generator().manual_seed(42))"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:17:01.612559Z","iopub.status.busy":"2024-07-13T14:17:01.612244Z","iopub.status.idle":"2024-07-13T14:17:01.617559Z","shell.execute_reply":"2024-07-13T14:17:01.616669Z","shell.execute_reply.started":"2024-07-13T14:17:01.612534Z"},"trusted":true},"outputs":[],"source":["train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True)\n","test_loader = DataLoader(test_dataset, batch_size=128, shuffle=False)"]},{"cell_type":"code","execution_count":14,"metadata":{},"outputs":[],"source":["with open('classes.txt', 'w') as f:\n"," for item in dogdataset.classes:\n"," f.write(\"%s\\n\" % item)"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:17:01.629372Z","iopub.status.busy":"2024-07-13T14:17:01.629027Z","iopub.status.idle":"2024-07-13T14:17:02.324715Z","shell.execute_reply":"2024-07-13T14:17:02.323774Z","shell.execute_reply.started":"2024-07-13T14:17:01.629341Z"},"trusted":true},"outputs":[],"source":["imgs, labels =next(iter(train_loader))\n","imgs.shape"]},{"cell_type":"code","execution_count":1,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:17:02.327344Z","iopub.status.busy":"2024-07-13T14:17:02.327026Z","iopub.status.idle":"2024-07-13T14:17:02.333456Z","shell.execute_reply":"2024-07-13T14:17:02.332447Z","shell.execute_reply.started":"2024-07-13T14:17:02.327314Z"},"trusted":true},"outputs":[{"name":"stdout","output_type":"stream","text":["Writing model.py\n"]}],"source":["class DogEmotionResNet(nn.Module):\n"," def __init__(self, num_classes, weights=None):\n"," super().__init__()\n"," \n"," self.resnet = models.resnet50(weights=weights)\n"," for param in self.resnet.parameters():\n"," param.requires_grad = False\n"," \n"," in_features = self.resnet.fc.in_features\n"," self.resnet.fc = nn.Linear(in_features, num_classes)\n"," \n"," def forward(self, x):\n"," return self.resnet(x)"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:17:02.334806Z","iopub.status.busy":"2024-07-13T14:17:02.334546Z","iopub.status.idle":"2024-07-13T14:17:02.588742Z","shell.execute_reply":"2024-07-13T14:17:02.587715Z","shell.execute_reply.started":"2024-07-13T14:17:02.334783Z"},"trusted":true},"outputs":[],"source":["torch.manual_seed(42)\n","torch.cuda.manual_seed(42)\n","\n","classes = dogdataset.classes\n","\n","model = DogEmotionResNet(num_classes= len(classes), weights=resnet_weights)\n","model.to(device)\n","\n","# test_output = model(imgs.to(device))"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:17:02.590369Z","iopub.status.busy":"2024-07-13T14:17:02.590030Z","iopub.status.idle":"2024-07-13T14:17:02.602762Z","shell.execute_reply":"2024-07-13T14:17:02.601655Z","shell.execute_reply.started":"2024-07-13T14:17:02.590341Z"},"trusted":true},"outputs":[],"source":["def train(model, train_loader, criterion, optimizer, device):\n"," model.train()\n"," running_loss = 0.0\n"," correct_predictions = 0\n"," total_predictions = 0\n"," \n"," for inputs, labels in train_loader:\n"," inputs, labels = inputs.to(device), labels.to(device)\n"," \n"," optimizer.zero_grad()\n"," \n"," outputs = model(inputs)\n"," loss = criterion(outputs, labels)\n"," loss.backward()\n"," optimizer.step()\n"," \n"," running_loss += loss.item()\n"," predicted_labels = outputs.argmax(dim=1)\n"," correct_predictions += (predicted_labels == labels).sum().item()\n"," total_predictions += labels.size(0)\n"," \n"," train_loss = running_loss / len(train_loader)\n"," train_accuracy = correct_predictions / total_predictions\n"," \n"," return train_loss, train_accuracy\n","\n","def validate(model, test_loader, criterion, device):\n"," model.eval()\n"," running_loss = 0.0\n"," correct_predictions = 0\n"," total_predictions = 0\n"," \n"," with torch.inference_mode():\n"," for inputs, labels in test_loader:\n"," inputs, labels = inputs.to(device), labels.to(device)\n"," \n"," outputs = model(inputs)\n"," loss = criterion(outputs, labels)\n"," \n"," running_loss += loss.item()\n"," predicted_labels = outputs.argmax(dim=1)\n"," correct_predictions += (predicted_labels == labels).sum().item()\n"," total_predictions += labels.size(0)\n"," \n"," test_loss = running_loss / len(test_loader)\n"," test_accuracy = correct_predictions / total_predictions\n"," \n"," return test_loss, test_accuracy\n"," "]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:17:02.604726Z","iopub.status.busy":"2024-07-13T14:17:02.604323Z","iopub.status.idle":"2024-07-13T14:20:47.515463Z","shell.execute_reply":"2024-07-13T14:20:47.514558Z","shell.execute_reply.started":"2024-07-13T14:17:02.604682Z"},"trusted":true},"outputs":[],"source":["from tqdm.auto import tqdm\n","\n","EPOCHS = 30\n","criterion = nn.CrossEntropyLoss()\n","optimizer = optim.Adam(model.parameters(), lr=0.002)\n","\n","train_losses = []\n","train_accuracies = []\n","test_losses = []\n","test_accuracies = []\n","\n","for epoch in tqdm(range(EPOCHS)):\n"," train_loss, train_accuracy = train(model, train_loader, criterion, optimizer, device)\n"," test_loss, test_accuracy = validate(model, test_loader, criterion, device)\n"," \n"," train_losses.append(train_loss)\n"," train_accuracies.append(train_accuracy)\n"," test_losses.append(test_loss)\n"," test_accuracies.append(test_accuracy)\n"," \n"," print(f'Epoch: {epoch+1}/{EPOCHS}, Train Loss: {train_loss:.4f}, Train Accuracy: {train_accuracy:.4f}, Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.4f}')\n"," "]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:22:05.329623Z","iopub.status.busy":"2024-07-13T14:22:05.328737Z","iopub.status.idle":"2024-07-13T14:22:05.554562Z","shell.execute_reply":"2024-07-13T14:22:05.553621Z","shell.execute_reply.started":"2024-07-13T14:22:05.329587Z"},"trusted":true},"outputs":[],"source":["plt.figure(figsize=(12, 6))\n","plt.subplot(1, 2, 1)\n","plt.plot(train_losses, label='train loss')\n","plt.plot(test_losses, label='test loss')\n","plt.xlabel('Epochs')\n","plt.ylabel('Loss')\n","plt.legend()"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:22:05.557018Z","iopub.status.busy":"2024-07-13T14:22:05.556561Z","iopub.status.idle":"2024-07-13T14:22:05.748228Z","shell.execute_reply":"2024-07-13T14:22:05.747325Z","shell.execute_reply.started":"2024-07-13T14:22:05.556984Z"},"trusted":true},"outputs":[],"source":["plt.subplot(1, 2, 2)\n","plt.plot(train_accuracies, label='train accuracy')\n","plt.plot(test_accuracies, label='test accuracy')\n","plt.xlabel('Epochs')\n","plt.ylabel('Accuracy')\n","plt.legend()\n","plt.show()"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:20:47.969342Z","iopub.status.busy":"2024-07-13T14:20:47.968739Z","iopub.status.idle":"2024-07-13T14:20:48.069413Z","shell.execute_reply":"2024-07-13T14:20:48.068345Z","shell.execute_reply.started":"2024-07-13T14:20:47.969309Z"},"trusted":true},"outputs":[],"source":["torch.save(model.state_dict(), 'dog_emotion_model.pth')"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:20:48.071053Z","iopub.status.busy":"2024-07-13T14:20:48.070747Z","iopub.status.idle":"2024-07-13T14:20:48.343977Z","shell.execute_reply":"2024-07-13T14:20:48.343058Z","shell.execute_reply.started":"2024-07-13T14:20:48.071027Z"},"trusted":true},"outputs":[],"source":["model = DogEmotionResNet(num_classes=len(classes))\n","model.load_state_dict(torch.load('dog_emotion_model.pth'))\n","model.to(device)"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:22:26.567754Z","iopub.status.busy":"2024-07-13T14:22:26.566902Z","iopub.status.idle":"2024-07-13T14:22:30.940835Z","shell.execute_reply":"2024-07-13T14:22:30.940029Z","shell.execute_reply.started":"2024-07-13T14:22:26.567714Z"},"trusted":true},"outputs":[],"source":["def predict(model, test_loader, device):\n"," model.eval()\n"," predictions = []\n"," \n"," with torch.inference_mode():\n"," for inputs, labels in test_loader:\n"," inputs = inputs.to(device)\n"," outputs = model(inputs)\n"," predicted_labels = outputs.argmax(dim=1)\n"," \n"," predictions.extend(predicted_labels.cpu().numpy())\n"," \n"," return predictions\n","\n","predictions = predict(model, test_loader, device)"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:22:30.942858Z","iopub.status.busy":"2024-07-13T14:22:30.942507Z","iopub.status.idle":"2024-07-13T14:22:30.949446Z","shell.execute_reply":"2024-07-13T14:22:30.948521Z","shell.execute_reply.started":"2024-07-13T14:22:30.942825Z"},"trusted":true},"outputs":[],"source":["predictions[:10]"]},{"cell_type":"code","execution_count":null,"metadata":{"execution":{"iopub.execute_input":"2024-07-13T14:22:33.724471Z","iopub.status.busy":"2024-07-13T14:22:33.723796Z","iopub.status.idle":"2024-07-13T14:22:37.403463Z","shell.execute_reply":"2024-07-13T14:22:37.402489Z","shell.execute_reply.started":"2024-07-13T14:22:33.724441Z"},"trusted":true},"outputs":[],"source":["from sklearn.metrics import classification_report\n","\n","print(classification_report([i[1] for i in test_dataset], predictions))\n"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":[]}],"metadata":{"kaggle":{"accelerator":"gpu","dataSources":[{"datasetId":2882322,"sourceId":4969612,"sourceType":"datasetVersion"}],"dockerImageVersionId":30747,"isGpuEnabled":true,"isInternetEnabled":true,"language":"python","sourceType":"notebook"},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.9.13"}},"nbformat":4,"nbformat_minor":4}
dog_emotion_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:80a013e6847088db2d44c3dfbc9ac5391e5932f612e30a8b330662c302d8e9ea
3
+ size 94389082
model.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from torchvision import models
4
+
5
+ class DogEmotionResNet(nn.Module):
6
+ def __init__(self, num_classes, weights=None):
7
+ super().__init__()
8
+
9
+ self.resnet = models.resnet50(weights=weights)
10
+ for param in self.resnet.parameters():
11
+ param.requires_grad = False
12
+
13
+ in_features = self.resnet.fc.in_features
14
+ self.resnet.fc = nn.Linear(in_features, num_classes)
15
+
16
+ def forward(self, x):
17
+ return self.resnet(x)
readme.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dog Emotion Classifier 🐶🎭
2
+
3
+ This project uses PyTorch for model development and Gradio for the user interface to classify the emotions of dogs from images. The classifier identifies emotions as happy, sad, angry, or relaxed.
4
+
5
+ ## Features
6
+
7
+ - **Model Architecture:** Utilizes a pre-trained ResNet50 model.
8
+ - **User Interface:** Interactive web interface powered by Gradio.
9
+ - **Training and Evaluation:** Scripts for training and evaluating the model on custom datasets.
10
+
11
+ ## Dataset
12
+
13
+ - The model was trained on the https://www.kaggle.com/datasets/danielshanbalico/dog-emotion dataset.
14
+
15
+ ## Installation
16
+
17
+ ### Prerequisites
18
+
19
+ - Python 3.7 or higher
20
+ - PyTorch
21
+ - Gradio
22
+ - Other dependencies listed in `requirements.txt`
23
+
24
+ ### Clone the Repository
25
+
26
+ ```bash
27
+ git clone https://github.com/Yuval728/dog-emotion-classifier.git
28
+ cd dog-emotion-classifier
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ numpy
2
+ torch
3
+ torchvision
4
+ Pillow
5
+ gradio
6
+ pandas
7
+ scikit-learn
8
+ matplotlib
9
+ tqdm
test.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch.utils.data import DataLoader
3
+ from sklearn.metrics import classification_report
4
+
5
+ from dataset import get_datasets
6
+ from model import DogEmotionResNet
7
+ from utils import predict
8
+
9
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
10
+ data_dir = 'data/'
11
+
12
+ _, test_dataset, classes = get_datasets(data_dir)
13
+
14
+ test_loader = DataLoader(test_dataset, batch_size=128, shuffle=False)
15
+
16
+ model = DogEmotionResNet(num_classes=len(classes))
17
+ model.load_state_dict(torch.load('dog_emotion_model.pth'))
18
+ model.to(device)
19
+
20
+ predictions = predict(model, test_loader, device)
21
+
22
+ print(classification_report([label for _, label in test_dataset], predictions))
train.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn, optim
3
+ from torch.utils.data import DataLoader
4
+ from tqdm.auto import tqdm
5
+ import matplotlib.pyplot as plt
6
+ from torchvision import models
7
+ from dataset import get_datasets
8
+ from model import DogEmotionResNet
9
+ from utils import train, validate
10
+
11
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
12
+ data_dir = 'data/'
13
+
14
+ train_dataset, test_dataset, classes = get_datasets(data_dir)
15
+
16
+ train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True)
17
+ test_loader = DataLoader(test_dataset, batch_size=128, shuffle=False)
18
+
19
+ with open('classes.txt', 'w') as f:
20
+ for item in classes:
21
+ f.write("%s\n" % item)
22
+
23
+ torch.manual_seed(42)
24
+ torch.cuda.manual_seed(42)
25
+
26
+ resnet_weights = models.ResNet50_Weights.DEFAULT
27
+ model = DogEmotionResNet(num_classes=len(classes), weights=resnet_weights)
28
+ model.to(device)
29
+
30
+ EPOCHS = 30
31
+ criterion = nn.CrossEntropyLoss()
32
+ optimizer = optim.Adam(model.parameters(), lr=0.002)
33
+
34
+ train_losses = []
35
+ train_accuracies = []
36
+ test_losses = []
37
+ test_accuracies = []
38
+
39
+ for epoch in tqdm(range(EPOCHS)):
40
+ train_loss, train_accuracy = train(model, train_loader, criterion, optimizer, device)
41
+ test_loss, test_accuracy = validate(model, test_loader, criterion, device)
42
+
43
+ train_losses.append(train_loss)
44
+ train_accuracies.append(train_accuracy)
45
+ test_losses.append(test_loss)
46
+ test_accuracies.append(test_accuracy)
47
+
48
+ print(f'Epoch: {epoch+1}/{EPOCHS}, Train Loss: {train_loss:.4f}, Train Accuracy: {train_accuracy:.4f}, Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.4f}')
49
+
50
+ plt.figure(figsize=(12, 6))
51
+ plt.subplot(1, 2, 1)
52
+ plt.plot(train_losses, label='train loss')
53
+ plt.plot(test_losses, label='test loss')
54
+ plt.xlabel('Epochs')
55
+ plt.ylabel('Loss')
56
+ plt.legend()
57
+
58
+ plt.subplot(1, 2, 2)
59
+ plt.plot(train_accuracies, label='train accuracy')
60
+ plt.plot(test_accuracies, label='test accuracy')
61
+ plt.xlabel('Epochs')
62
+ plt.ylabel('Accuracy')
63
+ plt.legend()
64
+ plt.show()
65
+
66
+ torch.save(model.state_dict(), 'dog_emotion_model.pth')
utils.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def train(model, train_loader, criterion, optimizer, device):
4
+ model.train()
5
+ running_loss = 0.0
6
+ correct_predictions = 0
7
+ total_predictions = 0
8
+
9
+ for inputs, labels in train_loader:
10
+ inputs, labels = inputs.to(device), labels.to(device)
11
+
12
+ optimizer.zero_grad()
13
+
14
+ outputs = model(inputs)
15
+ loss = criterion(outputs, labels)
16
+ loss.backward()
17
+ optimizer.step()
18
+
19
+ running_loss += loss.item()
20
+ predicted_labels = outputs.argmax(dim=1)
21
+ correct_predictions += (predicted_labels == labels).sum().item()
22
+ total_predictions += labels.size(0)
23
+
24
+ train_loss = running_loss / len(train_loader)
25
+ train_accuracy = correct_predictions / total_predictions
26
+
27
+ return train_loss, train_accuracy
28
+
29
+ def validate(model, test_loader, criterion, device):
30
+ model.eval()
31
+ running_loss = 0.0
32
+ correct_predictions = 0
33
+ total_predictions = 0
34
+
35
+ with torch.inference_mode():
36
+ for inputs, labels in test_loader:
37
+ inputs, labels = inputs.to(device), labels.to(device)
38
+
39
+ outputs = model(inputs)
40
+ loss = criterion(outputs, labels)
41
+
42
+ running_loss += loss.item()
43
+ predicted_labels = outputs.argmax(dim=1)
44
+ correct_predictions += (predicted_labels == labels).sum().item()
45
+ total_predictions += labels.size(0)
46
+
47
+ test_loss = running_loss / len(test_loader)
48
+ test_accuracy = correct_predictions / total_predictions
49
+
50
+ return test_loss, test_accuracy
51
+
52
+ def predict(model, test_loader, device):
53
+ model.eval()
54
+ predictions = []
55
+
56
+ with torch.inference_mode():
57
+ for inputs, _ in test_loader:
58
+ inputs = inputs.to(device)
59
+ outputs = model(inputs)
60
+ predicted_labels = outputs.argmax(dim=1)
61
+ predictions.extend(predicted_labels.cpu().numpy())
62
+
63
+ return predictions