louis JULIEN commited on
Commit ·
26cab2b
1
Parent(s): 1da1e59
model and first documentation
Browse files- .gitattributes +4 -0
- .gitignore +4 -0
- README.md +34 -3
- images/confusion-matrix.png +3 -0
- images/dataset.png +3 -0
- images/result-first-hand.png +3 -0
- images/result-second-hand-1.png +3 -0
- images/result-second-hand-2.png +3 -0
- requirements.txt +9 -0
- src/assets/dress-drm-free/1.jpeg +3 -0
- src/assets/dress-drm-free/2.jpeg +3 -0
- src/assets/dress-drm-free/3.jpeg +3 -0
- src/assets/dress-drm-free/4.jpeg +3 -0
- src/assets/label-difficult/1.jpeg +3 -0
- src/assets/label-difficult/2.jpeg +3 -0
- src/assets/label-difficult/3.jpeg +3 -0
- src/assets/label-difficult/4.jpeg +3 -0
- src/assets/label-difficult/5.jpeg +3 -0
- src/assets/saint-james-coat/1.jpeg +3 -0
- src/assets/saint-james-coat/2.jpeg +3 -0
- src/assets/saint-james-coat/3.jpeg +3 -0
- src/assets/saint-james-coat/4.jpeg +3 -0
- src/assets/saint-james-coat/5.jpeg +3 -0
- src/neural-network-transfer-learning.ipynb +448 -0
- src/shared/labels_to_output_index.json +1 -0
- src/shared/model_scripted.pt +3 -0
- src/shared/stance_recognize.py +59 -0
- src/use-example.ipynb +412 -0
.gitattributes
CHANGED
|
@@ -23,6 +23,7 @@
|
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
*.tar filter=lfs diff=lfs merge=lfs -text
|
|
@@ -33,3 +34,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 27 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 29 |
*.tar filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 34 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 36 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
src/shared/model_scripted.pt filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
images/** filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
src/assets/** filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset
|
| 2 |
+
venv
|
| 3 |
+
.env
|
| 4 |
+
savepoints
|
README.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Purpose
|
| 2 |
+
This very lightweight model recognizes clothes pictures stance.
|
| 3 |
+
|
| 4 |
+

|
| 5 |
+

|
| 6 |
+

|
| 7 |
+
|
| 8 |
+
This model classifies clothes picture based on their stance. It can recognize picture of the front, back, label, close up and
|
| 9 |
+
more. It is not perfectly accurate on the recognized stance but is perfect to sort pictures consistently e.g. front first,
|
| 10 |
+
back second, third closeup and label last.
|
| 11 |
+
|
| 12 |
+
It has been trained on a dataset made up of 50% commercial studio pictures (from Fashionpedia) and 50% second hand (
|
| 13 |
+
=amateur) pictures.
|
| 14 |
+
|
| 15 |
+
The base model is `nvidia_efficientnet_b0` that has been retrained using pytorch. You can see the training
|
| 16 |
+
parameters [here](src/neural-network-transfer-learning.ipynb).
|
| 17 |
+
|
| 18 |
+
As the base model is super small (5.2M parameters), it runs fast on CPU and with a very small memory footprint. It also
|
| 19 |
+
runs blazing fast on GPU. CUDA is not required to run the model.
|
| 20 |
+
|
| 21 |
+
## Installation
|
| 22 |
+
|
| 23 |
+
```bash
|
| 24 |
+
pip install -r requirements.txt
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
## Usage
|
| 28 |
+
|
| 29 |
+
See [use-example.ipynb](src/use-example.ipynb)
|
| 30 |
+
|
| 31 |
+
# Dataset
|
| 32 |
+
|
| 33 |
+
The dataset has been curated by hand. For any enquiry about the training
|
| 34 |
+
dataset, [contact me](https://www.linkedin.com/in/louisjulien95).
|
images/confusion-matrix.png
ADDED
|
Git LFS Details
|
images/dataset.png
ADDED
|
Git LFS Details
|
images/result-first-hand.png
ADDED
|
Git LFS Details
|
images/result-second-hand-1.png
ADDED
|
Git LFS Details
|
images/result-second-hand-2.png
ADDED
|
Git LFS Details
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
numpy==1.26.4
|
| 2 |
+
requests==2.31.0
|
| 3 |
+
matplotlib==3.8.2
|
| 4 |
+
Pillow==10.3.0
|
| 5 |
+
torchvision==0.18.1
|
| 6 |
+
torch==2.3.1
|
| 7 |
+
|
| 8 |
+
scipy==1.16.3
|
| 9 |
+
notebook==7.4.7
|
src/assets/dress-drm-free/1.jpeg
ADDED
|
Git LFS Details
|
src/assets/dress-drm-free/2.jpeg
ADDED
|
Git LFS Details
|
src/assets/dress-drm-free/3.jpeg
ADDED
|
Git LFS Details
|
src/assets/dress-drm-free/4.jpeg
ADDED
|
Git LFS Details
|
src/assets/label-difficult/1.jpeg
ADDED
|
Git LFS Details
|
src/assets/label-difficult/2.jpeg
ADDED
|
Git LFS Details
|
src/assets/label-difficult/3.jpeg
ADDED
|
Git LFS Details
|
src/assets/label-difficult/4.jpeg
ADDED
|
Git LFS Details
|
src/assets/label-difficult/5.jpeg
ADDED
|
Git LFS Details
|
src/assets/saint-james-coat/1.jpeg
ADDED
|
Git LFS Details
|
src/assets/saint-james-coat/2.jpeg
ADDED
|
Git LFS Details
|
src/assets/saint-james-coat/3.jpeg
ADDED
|
Git LFS Details
|
src/assets/saint-james-coat/4.jpeg
ADDED
|
Git LFS Details
|
src/assets/saint-james-coat/5.jpeg
ADDED
|
Git LFS Details
|
src/neural-network-transfer-learning.ipynb
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"outputs": [],
|
| 6 |
+
"source": [
|
| 7 |
+
"import json\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"import torch\n",
|
| 10 |
+
"import torch.nn as nn\n",
|
| 11 |
+
"from torchvision import datasets, transforms\n",
|
| 12 |
+
"from torchvision.transforms import v2\n",
|
| 13 |
+
"from torch.utils.data import DataLoader\n",
|
| 14 |
+
"from matplotlib import colors\n",
|
| 15 |
+
"import numpy as np\n",
|
| 16 |
+
"import matplotlib.pyplot as plt\n",
|
| 17 |
+
"\n",
|
| 18 |
+
"# Define transformation for both training and validation data\n",
|
| 19 |
+
"transform = transforms.Compose([\n",
|
| 20 |
+
" transforms.Resize((256, 256)),\n",
|
| 21 |
+
" transforms.ToTensor(),\n",
|
| 22 |
+
" v2.RandomHorizontalFlip(p=0.5),\n",
|
| 23 |
+
" v2.ColorJitter(brightness=.4, hue=.1),\n",
|
| 24 |
+
" v2.RandomPerspective(distortion_scale=0.05, p=1.0),\n",
|
| 25 |
+
" v2.RandomRotation(degrees=(-5, 5)),\n",
|
| 26 |
+
" v2.GaussianBlur(kernel_size=(5, 9), sigma=(0.1, 3.)),\n",
|
| 27 |
+
" transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n",
|
| 28 |
+
"])\n",
|
| 29 |
+
"\n",
|
| 30 |
+
"# Load the dataset\n",
|
| 31 |
+
"full_dataset = datasets.ImageFolder(\n",
|
| 32 |
+
" './dataset/dataset-clothes-pictures-stance-v3-labeled-80k',\n",
|
| 33 |
+
" transform=transform)\n",
|
| 34 |
+
"\n",
|
| 35 |
+
"random_generator = torch.Generator().manual_seed(12)\n",
|
| 36 |
+
"train_data, validation_data = torch.utils.data.random_split(full_dataset, [0.9, 0.1], generator=random_generator)\n",
|
| 37 |
+
"\n",
|
| 38 |
+
"# Create data loaders\n",
|
| 39 |
+
"train_loader = DataLoader(train_data, batch_size=32, shuffle=True, num_workers=12, pin_memory=True)\n",
|
| 40 |
+
"validation_loader = DataLoader(validation_data, batch_size=320, shuffle=False, num_workers=12, pin_memory=True)"
|
| 41 |
+
],
|
| 42 |
+
"metadata": {
|
| 43 |
+
"collapsed": false,
|
| 44 |
+
"ExecuteTime": {
|
| 45 |
+
"end_time": "2024-03-30T13:58:01.023179Z",
|
| 46 |
+
"start_time": "2024-03-30T13:57:58.929471Z"
|
| 47 |
+
}
|
| 48 |
+
},
|
| 49 |
+
"id": "15c69cabd27e8626",
|
| 50 |
+
"execution_count": 1
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"cell_type": "code",
|
| 54 |
+
"outputs": [
|
| 55 |
+
{
|
| 56 |
+
"data": {
|
| 57 |
+
"text/plain": "['back',\n 'closeup',\n 'front',\n 'inside',\n 'label',\n 'others',\n 'side_view',\n 'three_fourth']"
|
| 58 |
+
},
|
| 59 |
+
"execution_count": 2,
|
| 60 |
+
"metadata": {},
|
| 61 |
+
"output_type": "execute_result"
|
| 62 |
+
}
|
| 63 |
+
],
|
| 64 |
+
"source": [
|
| 65 |
+
"full_dataset.classes"
|
| 66 |
+
],
|
| 67 |
+
"metadata": {
|
| 68 |
+
"collapsed": false,
|
| 69 |
+
"ExecuteTime": {
|
| 70 |
+
"end_time": "2024-03-30T13:58:01.026979Z",
|
| 71 |
+
"start_time": "2024-03-30T13:58:01.024125Z"
|
| 72 |
+
}
|
| 73 |
+
},
|
| 74 |
+
"id": "bd76ca75c01f278b",
|
| 75 |
+
"execution_count": 2
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"metadata": {},
|
| 79 |
+
"cell_type": "markdown",
|
| 80 |
+
"source": [
|
| 81 |
+
"# Dataset\n",
|
| 82 |
+
"**Number of image per label**<br/>\n",
|
| 83 |
+
"back: 19587<br/>\n",
|
| 84 |
+
"closeup: 8292<br/>\n",
|
| 85 |
+
"front: 35042<br/>\n",
|
| 86 |
+
"inside: 245<br/>\n",
|
| 87 |
+
"label: 6869<br/>\n",
|
| 88 |
+
"others: 231<br/>\n",
|
| 89 |
+
"side_view: 9000<br/>\n",
|
| 90 |
+
"three_fourth: 3002<br/>"
|
| 91 |
+
],
|
| 92 |
+
"id": "9be4e2d2cef9ffd2"
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"cell_type": "code",
|
| 96 |
+
"outputs": [
|
| 97 |
+
{
|
| 98 |
+
"data": {
|
| 99 |
+
"text/plain": "Counter({'front': 31530,\n 'back': 17592,\n 'side_view': 8116,\n 'closeup': 7456,\n 'label': 6196,\n 'three_fourth': 2700,\n 'inside': 221,\n 'others': 205})"
|
| 100 |
+
},
|
| 101 |
+
"execution_count": 3,
|
| 102 |
+
"metadata": {},
|
| 103 |
+
"output_type": "execute_result"
|
| 104 |
+
}
|
| 105 |
+
],
|
| 106 |
+
"source": [
|
| 107 |
+
"from collections import Counter\n",
|
| 108 |
+
"\n",
|
| 109 |
+
"train_classes = [full_dataset.classes[full_dataset.targets[i]] for i in train_data.indices]\n",
|
| 110 |
+
"Counter(train_classes)"
|
| 111 |
+
],
|
| 112 |
+
"metadata": {
|
| 113 |
+
"collapsed": false,
|
| 114 |
+
"ExecuteTime": {
|
| 115 |
+
"end_time": "2024-03-30T13:58:03.288969Z",
|
| 116 |
+
"start_time": "2024-03-30T13:58:03.282046Z"
|
| 117 |
+
}
|
| 118 |
+
},
|
| 119 |
+
"id": "b03df8d708a8de9c",
|
| 120 |
+
"execution_count": 3
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"cell_type": "code",
|
| 124 |
+
"outputs": [],
|
| 125 |
+
"source": [
|
| 126 |
+
"neural_network_model = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 'nvidia_efficientnet_b0', pretrained=True)\n",
|
| 127 |
+
"\n",
|
| 128 |
+
"for param in neural_network_model.parameters():\n",
|
| 129 |
+
" param.requires_grad = False\n",
|
| 130 |
+
"\n",
|
| 131 |
+
"# Modify the fully connected layer to match the number of classes in your dataset\n",
|
| 132 |
+
"num_classes = len(full_dataset.classes)\n",
|
| 133 |
+
"num_ftrs = neural_network_model.classifier.fc.in_features\n",
|
| 134 |
+
"\n",
|
| 135 |
+
"neural_network_model.classifier.fc = nn.Sequential(\n",
|
| 136 |
+
" nn.Linear(num_ftrs, num_classes),\n",
|
| 137 |
+
" nn.Linear(num_classes, num_classes)\n",
|
| 138 |
+
")\n",
|
| 139 |
+
"initial_model_hash = hash(neural_network_model)\n",
|
| 140 |
+
"print(f'{initial_model_hash=}')"
|
| 141 |
+
],
|
| 142 |
+
"metadata": {
|
| 143 |
+
"collapsed": false
|
| 144 |
+
},
|
| 145 |
+
"id": "92566906437e3e85",
|
| 146 |
+
"execution_count": null
|
| 147 |
+
},
|
| 148 |
+
{
|
| 149 |
+
"cell_type": "code",
|
| 150 |
+
"outputs": [],
|
| 151 |
+
"source": [
|
| 152 |
+
"cross_entropy_criterion = nn.CrossEntropyLoss()\n",
|
| 153 |
+
"fine_tuning_optimizer = torch.optim.RMSprop(filter(lambda p: p.requires_grad, neural_network_model.parameters()), lr=0.001)"
|
| 154 |
+
],
|
| 155 |
+
"metadata": {
|
| 156 |
+
"collapsed": false,
|
| 157 |
+
"ExecuteTime": {
|
| 158 |
+
"end_time": "2024-03-30T13:58:18.635149Z",
|
| 159 |
+
"start_time": "2024-03-30T13:58:18.632188Z"
|
| 160 |
+
}
|
| 161 |
+
},
|
| 162 |
+
"id": "3f67806429de0fd2",
|
| 163 |
+
"execution_count": 6
|
| 164 |
+
},
|
| 165 |
+
{
|
| 166 |
+
"cell_type": "code",
|
| 167 |
+
"outputs": [],
|
| 168 |
+
"source": [
|
| 169 |
+
"for param in neural_network_model.parameters():\n",
|
| 170 |
+
" param.requires_grad = True\n",
|
| 171 |
+
"initial_model_hash = str(hash(neural_network_model)) + '_finetunning'"
|
| 172 |
+
],
|
| 173 |
+
"metadata": {
|
| 174 |
+
"collapsed": false,
|
| 175 |
+
"ExecuteTime": {
|
| 176 |
+
"end_time": "2024-03-30T13:58:20.386435Z",
|
| 177 |
+
"start_time": "2024-03-30T13:58:20.383879Z"
|
| 178 |
+
}
|
| 179 |
+
},
|
| 180 |
+
"id": "7afd92b44f465364",
|
| 181 |
+
"execution_count": 7
|
| 182 |
+
},
|
| 183 |
+
{
|
| 184 |
+
"cell_type": "code",
|
| 185 |
+
"outputs": [],
|
| 186 |
+
"source": [
|
| 187 |
+
"def train_model(model, criterion, optimizer, num_epochs=10):\n",
|
| 188 |
+
" for epoch in range(num_epochs):\n",
|
| 189 |
+
" model.train()\n",
|
| 190 |
+
" running_loss = 0.0\n",
|
| 191 |
+
" for inputs, labels in train_loader:\n",
|
| 192 |
+
" inputs, labels = inputs.cuda(), labels.cuda()\n",
|
| 193 |
+
" optimizer.zero_grad()\n",
|
| 194 |
+
" outputs = model(inputs)\n",
|
| 195 |
+
" loss = criterion(outputs, labels)\n",
|
| 196 |
+
" loss.backward()\n",
|
| 197 |
+
" optimizer.step()\n",
|
| 198 |
+
" running_loss += loss.item() * inputs.size(0)\n",
|
| 199 |
+
"\n",
|
| 200 |
+
" epoch_loss = running_loss / len(train_loader.dataset)\n",
|
| 201 |
+
" print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {epoch_loss:.5f}')\n",
|
| 202 |
+
" model_scripted = torch.jit.script(neural_network_model.cpu())\n",
|
| 203 |
+
" model_scripted.save(f'savepoints/{initial_model_hash}-{epoch}-{epoch_loss:.5f}.pt')\n",
|
| 204 |
+
" neural_network_model.cuda()\n",
|
| 205 |
+
"\n",
|
| 206 |
+
"\n",
|
| 207 |
+
"neural_network_model = neural_network_model.cuda() # Move model to GPU if available "
|
| 208 |
+
],
|
| 209 |
+
"metadata": {
|
| 210 |
+
"collapsed": false,
|
| 211 |
+
"ExecuteTime": {
|
| 212 |
+
"end_time": "2024-03-30T15:19:23.709405Z",
|
| 213 |
+
"start_time": "2024-03-30T15:19:23.689408Z"
|
| 214 |
+
}
|
| 215 |
+
},
|
| 216 |
+
"id": "36df57cc9e0ec04c",
|
| 217 |
+
"execution_count": 13
|
| 218 |
+
},
|
| 219 |
+
{
|
| 220 |
+
"cell_type": "code",
|
| 221 |
+
"outputs": [
|
| 222 |
+
{
|
| 223 |
+
"name": "stdout",
|
| 224 |
+
"output_type": "stream",
|
| 225 |
+
"text": [
|
| 226 |
+
"Epoch [1/20], Loss: 0.10386\n",
|
| 227 |
+
"Epoch [2/20], Loss: 0.09937\n",
|
| 228 |
+
"Epoch [3/20], Loss: 0.09417\n",
|
| 229 |
+
"Epoch [4/20], Loss: 0.09078\n",
|
| 230 |
+
"Epoch [5/20], Loss: 0.08554\n",
|
| 231 |
+
"Epoch [6/20], Loss: 0.08409\n",
|
| 232 |
+
"Epoch [7/20], Loss: 0.07838\n",
|
| 233 |
+
"Epoch [8/20], Loss: 0.07729\n",
|
| 234 |
+
"Epoch [9/20], Loss: 0.07616\n",
|
| 235 |
+
"Epoch [10/20], Loss: 0.07047\n",
|
| 236 |
+
"Epoch [11/20], Loss: 0.06827\n",
|
| 237 |
+
"Epoch [12/20], Loss: 0.06546\n",
|
| 238 |
+
"Epoch [13/20], Loss: 0.06497\n",
|
| 239 |
+
"Epoch [14/20], Loss: 0.06133\n",
|
| 240 |
+
"Epoch [15/20], Loss: 0.06110\n",
|
| 241 |
+
"Epoch [16/20], Loss: 0.05928\n",
|
| 242 |
+
"Epoch [17/20], Loss: 0.05884\n",
|
| 243 |
+
"Epoch [18/20], Loss: 0.05555\n",
|
| 244 |
+
"Epoch [19/20], Loss: 0.05389\n",
|
| 245 |
+
"Epoch [20/20], Loss: 0.05302\n"
|
| 246 |
+
]
|
| 247 |
+
}
|
| 248 |
+
],
|
| 249 |
+
"source": [
|
| 250 |
+
"train_model(neural_network_model, cross_entropy_criterion, fine_tuning_optimizer, num_epochs=20)"
|
| 251 |
+
],
|
| 252 |
+
"metadata": {
|
| 253 |
+
"collapsed": false,
|
| 254 |
+
"ExecuteTime": {
|
| 255 |
+
"end_time": "2024-03-30T16:33:36.897968Z",
|
| 256 |
+
"start_time": "2024-03-30T15:19:24.153863Z"
|
| 257 |
+
}
|
| 258 |
+
},
|
| 259 |
+
"id": "c4b2fd744c1d5b94",
|
| 260 |
+
"execution_count": 14
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"cell_type": "code",
|
| 264 |
+
"outputs": [],
|
| 265 |
+
"source": [],
|
| 266 |
+
"metadata": {
|
| 267 |
+
"collapsed": false,
|
| 268 |
+
"ExecuteTime": {
|
| 269 |
+
"end_time": "2024-02-22T09:00:00.332964Z",
|
| 270 |
+
"start_time": "2024-02-22T09:00:00.332331Z"
|
| 271 |
+
}
|
| 272 |
+
},
|
| 273 |
+
"id": "a5951b3a238c03b1",
|
| 274 |
+
"execution_count": null
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
"cell_type": "code",
|
| 278 |
+
"outputs": [
|
| 279 |
+
{
|
| 280 |
+
"name": "stdout",
|
| 281 |
+
"output_type": "stream",
|
| 282 |
+
"text": [
|
| 283 |
+
"Total Validation Accuracy: 0.8943 (7355 / 8224)\n"
|
| 284 |
+
]
|
| 285 |
+
},
|
| 286 |
+
{
|
| 287 |
+
"data": {
|
| 288 |
+
"text/plain": "<Figure size 640x480 with 1 Axes>",
|
| 289 |
+
"image/png": "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"
|
| 290 |
+
},
|
| 291 |
+
"metadata": {},
|
| 292 |
+
"output_type": "display_data"
|
| 293 |
+
}
|
| 294 |
+
],
|
| 295 |
+
"source": [
|
| 296 |
+
"# Evaluate the model\n",
|
| 297 |
+
"neural_network_model.eval()\n",
|
| 298 |
+
"labels_to_index = {v: k for k, v in full_dataset.class_to_idx.items()}\n",
|
| 299 |
+
"all_labels = {k: 0 for k in full_dataset.classes}\n",
|
| 300 |
+
"actual_to_predicted_label = {}\n",
|
| 301 |
+
"\n",
|
| 302 |
+
"correct = 0\n",
|
| 303 |
+
"total = 0\n",
|
| 304 |
+
"\n",
|
| 305 |
+
"for a_label in all_labels.keys():\n",
|
| 306 |
+
" actual_to_predicted_label[a_label] = all_labels.copy()\n",
|
| 307 |
+
"\n",
|
| 308 |
+
"with torch.no_grad():\n",
|
| 309 |
+
" for inputs_, labels_ in validation_loader:\n",
|
| 310 |
+
" inputs_, labels_ = inputs_.cuda(), labels_.cuda()\n",
|
| 311 |
+
" outputs_ = neural_network_model(inputs_)\n",
|
| 312 |
+
" _, predicted = torch.max(outputs_.data, 1)\n",
|
| 313 |
+
"\n",
|
| 314 |
+
" for one_prediction, one_label in zip(predicted, labels_):\n",
|
| 315 |
+
" one_prediction_label_name = labels_to_index[one_prediction.tolist()]\n",
|
| 316 |
+
" one_label_name = labels_to_index[one_label.tolist()]\n",
|
| 317 |
+
" actual_to_predicted_label[one_label_name][one_prediction_label_name] += 1\n",
|
| 318 |
+
" total += labels_.size(0)\n",
|
| 319 |
+
" correct += (predicted == labels_).sum().item()\n",
|
| 320 |
+
"\n",
|
| 321 |
+
"accuracy = correct / total\n",
|
| 322 |
+
"print(f'Total Validation Accuracy: {accuracy:.4f} ({correct} / {total})')\n",
|
| 323 |
+
"\n",
|
| 324 |
+
"result_matrix = [[y for y in x.values()] for x in actual_to_predicted_label.values()]\n",
|
| 325 |
+
"max_value = max(*[max(y) for y in result_matrix])\n",
|
| 326 |
+
"axis_labels = list(actual_to_predicted_label.keys())\n",
|
| 327 |
+
"\n",
|
| 328 |
+
"fig = plt.figure()\n",
|
| 329 |
+
"ax = fig.add_subplot(111)\n",
|
| 330 |
+
"ax.matshow(result_matrix, norm=colors.LogNorm(vmin=0, vmax=max_value))\n",
|
| 331 |
+
"\n",
|
| 332 |
+
"for (i, j), z in np.ndenumerate(result_matrix):\n",
|
| 333 |
+
" ax.text(j, i, z, ha='center', va='center')\n",
|
| 334 |
+
"\n",
|
| 335 |
+
"ax.set_xlabel('Actual label')\n",
|
| 336 |
+
"ax.set_ylabel('Predicted label')\n",
|
| 337 |
+
"plt.xticks(ticks=range(len(axis_labels)), labels=axis_labels)\n",
|
| 338 |
+
"plt.yticks(ticks=range(len(axis_labels)), labels=axis_labels)\n",
|
| 339 |
+
"\n",
|
| 340 |
+
"plt.show()"
|
| 341 |
+
],
|
| 342 |
+
"metadata": {
|
| 343 |
+
"collapsed": false,
|
| 344 |
+
"ExecuteTime": {
|
| 345 |
+
"end_time": "2024-03-30T16:34:01.929118Z",
|
| 346 |
+
"start_time": "2024-03-30T16:33:36.904651Z"
|
| 347 |
+
}
|
| 348 |
+
},
|
| 349 |
+
"id": "3ce3444ebf880a3",
|
| 350 |
+
"execution_count": 15
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"cell_type": "code",
|
| 354 |
+
"outputs": [],
|
| 355 |
+
"source": [],
|
| 356 |
+
"metadata": {
|
| 357 |
+
"collapsed": false
|
| 358 |
+
},
|
| 359 |
+
"id": "c764a1ac6b5aa8ba",
|
| 360 |
+
"execution_count": null
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"cell_type": "code",
|
| 364 |
+
"outputs": [],
|
| 365 |
+
"source": [
|
| 366 |
+
"neural_network_model.eval()\n",
|
| 367 |
+
"neural_network_model.cpu()\n",
|
| 368 |
+
"model_scripted = torch.jit.script(neural_network_model)\n",
|
| 369 |
+
"model_scripted.save('shared/model_scripted.pt')\n",
|
| 370 |
+
"neural_network_model.cuda()\n",
|
| 371 |
+
"\n",
|
| 372 |
+
"with open(f\"shared/labels_to_output_index.json\", \"w\") as json_file:\n",
|
| 373 |
+
" json.dump(full_dataset.classes, json_file)"
|
| 374 |
+
],
|
| 375 |
+
"metadata": {
|
| 376 |
+
"collapsed": false,
|
| 377 |
+
"ExecuteTime": {
|
| 378 |
+
"end_time": "2024-03-30T16:34:35.966119Z",
|
| 379 |
+
"start_time": "2024-03-30T16:34:35.878966Z"
|
| 380 |
+
}
|
| 381 |
+
},
|
| 382 |
+
"id": "8b92d61aad8b2ce8",
|
| 383 |
+
"execution_count": 16
|
| 384 |
+
},
|
| 385 |
+
{
|
| 386 |
+
"cell_type": "code",
|
| 387 |
+
"outputs": [],
|
| 388 |
+
"source": [],
|
| 389 |
+
"metadata": {
|
| 390 |
+
"collapsed": false
|
| 391 |
+
},
|
| 392 |
+
"id": "2a04a4db365d9db4",
|
| 393 |
+
"execution_count": null
|
| 394 |
+
},
|
| 395 |
+
{
|
| 396 |
+
"metadata": {},
|
| 397 |
+
"cell_type": "code",
|
| 398 |
+
"outputs": [],
|
| 399 |
+
"execution_count": null,
|
| 400 |
+
"source": "",
|
| 401 |
+
"id": "42548abda9c8370f"
|
| 402 |
+
},
|
| 403 |
+
{
|
| 404 |
+
"cell_type": "code",
|
| 405 |
+
"source": [
|
| 406 |
+
"# Load model from file\n",
|
| 407 |
+
"neural_network_model = torch.jit.load('shared/model_scripted.pt')"
|
| 408 |
+
],
|
| 409 |
+
"metadata": {
|
| 410 |
+
"collapsed": false
|
| 411 |
+
},
|
| 412 |
+
"id": "f1cffed5763919d5",
|
| 413 |
+
"outputs": [],
|
| 414 |
+
"execution_count": null
|
| 415 |
+
},
|
| 416 |
+
{
|
| 417 |
+
"cell_type": "code",
|
| 418 |
+
"outputs": [],
|
| 419 |
+
"source": [],
|
| 420 |
+
"metadata": {
|
| 421 |
+
"collapsed": false
|
| 422 |
+
},
|
| 423 |
+
"id": "41d6a1305c8f992b",
|
| 424 |
+
"execution_count": null
|
| 425 |
+
}
|
| 426 |
+
],
|
| 427 |
+
"metadata": {
|
| 428 |
+
"kernelspec": {
|
| 429 |
+
"display_name": "Python 3",
|
| 430 |
+
"language": "python",
|
| 431 |
+
"name": "python3"
|
| 432 |
+
},
|
| 433 |
+
"language_info": {
|
| 434 |
+
"codemirror_mode": {
|
| 435 |
+
"name": "ipython",
|
| 436 |
+
"version": 2
|
| 437 |
+
},
|
| 438 |
+
"file_extension": ".py",
|
| 439 |
+
"mimetype": "text/x-python",
|
| 440 |
+
"name": "python",
|
| 441 |
+
"nbconvert_exporter": "python",
|
| 442 |
+
"pygments_lexer": "ipython2",
|
| 443 |
+
"version": "2.7.6"
|
| 444 |
+
}
|
| 445 |
+
},
|
| 446 |
+
"nbformat": 4,
|
| 447 |
+
"nbformat_minor": 5
|
| 448 |
+
}
|
src/shared/labels_to_output_index.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
["back", "closeup", "front", "inside", "label", "others", "side_view", "three_fourth"]
|
src/shared/model_scripted.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bfd8ed5a39969daf63bc67f6a4f0c99aa989683b9c2762a738df23ac61379668
|
| 3 |
+
size 16556362
|
src/shared/stance_recognize.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
from PIL.Image import Image
|
| 6 |
+
from scipy.special import softmax
|
| 7 |
+
from torch import Tensor
|
| 8 |
+
from torch.jit import RecursiveScriptModule
|
| 9 |
+
from torchvision import transforms
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def normalize_neural_network_output(neural_network_output: np.ndarray) -> List[float]:
|
| 13 |
+
return softmax(neural_network_output).tolist()
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def predict(pil_images: List[Image],
|
| 17 |
+
labels_to_output_index: list,
|
| 18 |
+
neural_network_model: RecursiveScriptModule
|
| 19 |
+
) -> List[dict]:
|
| 20 |
+
neural_network_input = format_pils_images(pil_images)
|
| 21 |
+
neural_network_output = predict_neural_network(neural_network_input, neural_network_model)
|
| 22 |
+
return format_output(neural_network_output, labels_to_output_index)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def predict_neural_network(neural_network_input: Tensor, neural_network_model: RecursiveScriptModule) -> List[
|
| 26 |
+
List[float]]:
|
| 27 |
+
with torch.no_grad():
|
| 28 |
+
all_outputs = neural_network_model(neural_network_input).tolist()
|
| 29 |
+
|
| 30 |
+
normalized_neural_network_output = list(map(normalize_neural_network_output, all_outputs))
|
| 31 |
+
return normalized_neural_network_output
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def format_output(neural_network_output: list, labels_to_output_index: list) -> List[dict]:
|
| 35 |
+
results = []
|
| 36 |
+
for i, a_nn_output in enumerate(neural_network_output):
|
| 37 |
+
results.append(
|
| 38 |
+
{
|
| 39 |
+
'probabilities_neural_network': {
|
| 40 |
+
labels_to_output_index[j]: p for j, p in enumerate(a_nn_output)
|
| 41 |
+
},
|
| 42 |
+
})
|
| 43 |
+
return results
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def format_pils_images(pil_images: List[Image]) -> Tensor:
|
| 47 |
+
pil_images_transformed = []
|
| 48 |
+
|
| 49 |
+
transform = transforms.Compose([
|
| 50 |
+
transforms.Resize((256, 256)),
|
| 51 |
+
transforms.ToTensor(),
|
| 52 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
| 53 |
+
])
|
| 54 |
+
|
| 55 |
+
for a_pil_image in pil_images:
|
| 56 |
+
a_pil_image = transform(a_pil_image)
|
| 57 |
+
pil_images_transformed.append(a_pil_image.unsqueeze(0))
|
| 58 |
+
|
| 59 |
+
return torch.cat(pil_images_transformed, dim=0)
|
src/use-example.ipynb
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"source": [
|
| 6 |
+
"import json\n",
|
| 7 |
+
"\n",
|
| 8 |
+
"from enum import StrEnum\n",
|
| 9 |
+
"\n",
|
| 10 |
+
"import numpy as np\n",
|
| 11 |
+
"from PIL import Image\n",
|
| 12 |
+
"from scipy.special import softmax\n",
|
| 13 |
+
"from torch import Tensor\n",
|
| 14 |
+
"from torch.jit import RecursiveScriptModule\n",
|
| 15 |
+
"from torchvision import transforms\n",
|
| 16 |
+
"\n",
|
| 17 |
+
"from IPython.display import display, HTML\n",
|
| 18 |
+
"\n",
|
| 19 |
+
"import torch"
|
| 20 |
+
],
|
| 21 |
+
"metadata": {
|
| 22 |
+
"collapsed": false,
|
| 23 |
+
"ExecuteTime": {
|
| 24 |
+
"end_time": "2025-11-10T21:15:01.207611Z",
|
| 25 |
+
"start_time": "2025-11-10T21:15:01.205436Z"
|
| 26 |
+
}
|
| 27 |
+
},
|
| 28 |
+
"id": "15c69cabd27e8626",
|
| 29 |
+
"outputs": [],
|
| 30 |
+
"execution_count": 10
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"cell_type": "code",
|
| 34 |
+
"source": [
|
| 35 |
+
"class PHOTO_FRAMING(StrEnum):\n",
|
| 36 |
+
" FRONT = \"front\"\n",
|
| 37 |
+
" BACK = \"back\"\n",
|
| 38 |
+
" SIDE_VIEW = \"side_view\"\n",
|
| 39 |
+
" THREE_FOURTH = \"three_fourth\"\n",
|
| 40 |
+
" INSIDE = \"inside\"\n",
|
| 41 |
+
" CLOSEUP = \"closeup\"\n",
|
| 42 |
+
" LABEL = \"label\"\n",
|
| 43 |
+
" OTHERS = \"others\"\n",
|
| 44 |
+
"\n",
|
| 45 |
+
"\n",
|
| 46 |
+
"AiPredictionByFraming = list[dict[PHOTO_FRAMING, float]]"
|
| 47 |
+
],
|
| 48 |
+
"metadata": {
|
| 49 |
+
"collapsed": false,
|
| 50 |
+
"ExecuteTime": {
|
| 51 |
+
"end_time": "2025-11-10T21:15:01.265847Z",
|
| 52 |
+
"start_time": "2025-11-10T21:15:01.263362Z"
|
| 53 |
+
}
|
| 54 |
+
},
|
| 55 |
+
"id": "f1cffed5763919d5",
|
| 56 |
+
"outputs": [],
|
| 57 |
+
"execution_count": 12
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"metadata": {
|
| 61 |
+
"ExecuteTime": {
|
| 62 |
+
"end_time": "2025-11-10T21:15:01.312581Z",
|
| 63 |
+
"start_time": "2025-11-10T21:15:01.308237Z"
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
"cell_type": "code",
|
| 67 |
+
"source": [
|
| 68 |
+
"def normalize_neural_network_output(neural_network_output: np.ndarray) -> list[float]:\n",
|
| 69 |
+
" return softmax(neural_network_output).tolist()\n",
|
| 70 |
+
"\n",
|
| 71 |
+
"\n",
|
| 72 |
+
"def predict_neural_network(neural_network_input: Tensor, neural_network_model: RecursiveScriptModule) -> list[\n",
|
| 73 |
+
" list[float]]:\n",
|
| 74 |
+
" with torch.no_grad():\n",
|
| 75 |
+
" all_outputs = neural_network_model(neural_network_input).tolist()\n",
|
| 76 |
+
"\n",
|
| 77 |
+
" normalized_neural_network_output = list(map(normalize_neural_network_output, all_outputs))\n",
|
| 78 |
+
" return normalized_neural_network_output\n",
|
| 79 |
+
"\n",
|
| 80 |
+
"\n",
|
| 81 |
+
"def format_output(neural_network_output: list, labels_to_output_index: list) -> list[dict]:\n",
|
| 82 |
+
" results = []\n",
|
| 83 |
+
" for i, a_nn_output in enumerate(neural_network_output):\n",
|
| 84 |
+
" results.append(\n",
|
| 85 |
+
" {\n",
|
| 86 |
+
" 'probabilities_neural_network': {\n",
|
| 87 |
+
" labels_to_output_index[j]: p for j, p in enumerate(a_nn_output)\n",
|
| 88 |
+
" },\n",
|
| 89 |
+
" })\n",
|
| 90 |
+
" return results\n",
|
| 91 |
+
"\n",
|
| 92 |
+
"\n",
|
| 93 |
+
"def format_pils_images(pil_images: list[Image.Image]) -> Tensor:\n",
|
| 94 |
+
" pil_images_transformed = []\n",
|
| 95 |
+
"\n",
|
| 96 |
+
" transform = transforms.Compose([\n",
|
| 97 |
+
" transforms.Resize((256, 256)),\n",
|
| 98 |
+
" transforms.ToTensor(),\n",
|
| 99 |
+
" transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n",
|
| 100 |
+
" ])\n",
|
| 101 |
+
"\n",
|
| 102 |
+
" for a_pil_image in pil_images:\n",
|
| 103 |
+
" a_pil_image = transform(a_pil_image)\n",
|
| 104 |
+
" pil_images_transformed.append(a_pil_image.unsqueeze(0))\n",
|
| 105 |
+
"\n",
|
| 106 |
+
" return torch.cat(pil_images_transformed, dim=0)\n",
|
| 107 |
+
"\n",
|
| 108 |
+
"\n",
|
| 109 |
+
"def predict(pil_images: list[Image.Image],\n",
|
| 110 |
+
" labels_to_output_index: list,\n",
|
| 111 |
+
" neural_network_model: RecursiveScriptModule\n",
|
| 112 |
+
") -> list[dict]:\n",
|
| 113 |
+
" neural_network_input = format_pils_images(pil_images)\n",
|
| 114 |
+
" neural_network_output = predict_neural_network(neural_network_input, neural_network_model)\n",
|
| 115 |
+
" return format_output(neural_network_output, labels_to_output_index)\n"
|
| 116 |
+
],
|
| 117 |
+
"id": "7a05d1015a88e18c",
|
| 118 |
+
"outputs": [],
|
| 119 |
+
"execution_count": 13
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"cell_type": "code",
|
| 123 |
+
"source": [
|
| 124 |
+
"neural_network_model = torch.jit.load('shared/model_scripted.pt')\n",
|
| 125 |
+
"\n",
|
| 126 |
+
"with open('shared/labels_to_output_index.json', 'r') as fp:\n",
|
| 127 |
+
" labels_to_output_index = json.load(fp)\n",
|
| 128 |
+
"\n",
|
| 129 |
+
"\n",
|
| 130 |
+
"def get_picture_framing_prediction(images_pil: list[Image.Image]) -> AiPredictionByFraming:\n",
|
| 131 |
+
" ai_prediction_scores = predict(images_pil,\n",
|
| 132 |
+
" labels_to_output_index,\n",
|
| 133 |
+
" neural_network_model)\n",
|
| 134 |
+
" ai_prediction_by_framing = [p['probabilities_neural_network'] for p in ai_prediction_scores]\n",
|
| 135 |
+
"\n",
|
| 136 |
+
" return ai_prediction_by_framing"
|
| 137 |
+
],
|
| 138 |
+
"metadata": {
|
| 139 |
+
"collapsed": false,
|
| 140 |
+
"ExecuteTime": {
|
| 141 |
+
"end_time": "2025-11-10T21:15:01.535844Z",
|
| 142 |
+
"start_time": "2025-11-10T21:15:01.355615Z"
|
| 143 |
+
}
|
| 144 |
+
},
|
| 145 |
+
"id": "41d6a1305c8f992b",
|
| 146 |
+
"outputs": [],
|
| 147 |
+
"execution_count": 14
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"metadata": {
|
| 151 |
+
"ExecuteTime": {
|
| 152 |
+
"end_time": "2025-11-10T21:15:01.575230Z",
|
| 153 |
+
"start_time": "2025-11-10T21:15:01.563426Z"
|
| 154 |
+
}
|
| 155 |
+
},
|
| 156 |
+
"cell_type": "code",
|
| 157 |
+
"source": [
|
| 158 |
+
"image_paths = [\"assets/dress-drm-free/1.jpeg\",\n",
|
| 159 |
+
" \"assets/dress-drm-free/2.jpeg\",\n",
|
| 160 |
+
" \"assets/dress-drm-free/3.jpeg\",\n",
|
| 161 |
+
" \"assets/dress-drm-free/4.jpeg\",\n",
|
| 162 |
+
" \"assets/label-difficult/1.jpeg\",\n",
|
| 163 |
+
" \"assets/label-difficult/2.jpeg\",\n",
|
| 164 |
+
" \"assets/label-difficult/3.jpeg\",\n",
|
| 165 |
+
" \"assets/label-difficult/4.jpeg\",\n",
|
| 166 |
+
" \"assets/label-difficult/5.jpeg\",\n",
|
| 167 |
+
" \"assets/saint-james-coat/1.jpeg\",\n",
|
| 168 |
+
" \"assets/saint-james-coat/2.jpeg\",\n",
|
| 169 |
+
" \"assets/saint-james-coat/3.jpeg\",\n",
|
| 170 |
+
" \"assets/saint-james-coat/4.jpeg\",\n",
|
| 171 |
+
" \"assets/saint-james-coat/5.jpeg\"]\n",
|
| 172 |
+
"input_images = [Image.open(one_image_path) for one_image_path in image_paths]"
|
| 173 |
+
],
|
| 174 |
+
"id": "3f7877aba0b830c9",
|
| 175 |
+
"outputs": [],
|
| 176 |
+
"execution_count": 15
|
| 177 |
+
},
|
| 178 |
+
{
|
| 179 |
+
"metadata": {
|
| 180 |
+
"ExecuteTime": {
|
| 181 |
+
"end_time": "2025-11-10T21:15:02.393613Z",
|
| 182 |
+
"start_time": "2025-11-10T21:15:01.613188Z"
|
| 183 |
+
}
|
| 184 |
+
},
|
| 185 |
+
"cell_type": "code",
|
| 186 |
+
"source": [
|
| 187 |
+
"predictions = get_picture_framing_prediction(input_images)\n",
|
| 188 |
+
"prediction_texts = []\n",
|
| 189 |
+
"\n",
|
| 190 |
+
"for one_image_prediction in predictions:\n",
|
| 191 |
+
" max_key = max(one_image_prediction, key=one_image_prediction.get)\n",
|
| 192 |
+
" max_value = one_image_prediction[max_key]\n",
|
| 193 |
+
"\n",
|
| 194 |
+
" one_image_predictions = []\n",
|
| 195 |
+
" for key, value in one_image_prediction.items():\n",
|
| 196 |
+
" one_image_text = f\"{key}: {value:.3f}\"\n",
|
| 197 |
+
" if key == max_key:\n",
|
| 198 |
+
" one_image_text = f\"<b>{one_image_text}</b>\"\n",
|
| 199 |
+
" one_image_predictions.append(one_image_text)\n",
|
| 200 |
+
"\n",
|
| 201 |
+
" prediction_texts.append(\"<br/>\".join(one_image_predictions))"
|
| 202 |
+
],
|
| 203 |
+
"id": "cda1d624454cda79",
|
| 204 |
+
"outputs": [],
|
| 205 |
+
"execution_count": 16
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
"metadata": {
|
| 209 |
+
"ExecuteTime": {
|
| 210 |
+
"end_time": "2025-11-10T21:15:02.400026Z",
|
| 211 |
+
"start_time": "2025-11-10T21:15:02.396874Z"
|
| 212 |
+
}
|
| 213 |
+
},
|
| 214 |
+
"cell_type": "code",
|
| 215 |
+
"source": [
|
| 216 |
+
"html = \"<div style='display: flex; flex-direction: column;'>\"\n",
|
| 217 |
+
"for img_path, text in zip(image_paths, prediction_texts):\n",
|
| 218 |
+
" html += f\"\"\"\n",
|
| 219 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 220 |
+
" <div style='margin-right: 20px;'>\n",
|
| 221 |
+
" <img src='{img_path}' style='max-width: 200px; max-height: 200px;' />\n",
|
| 222 |
+
" </div>\n",
|
| 223 |
+
" <div style='text-align: left;'>\n",
|
| 224 |
+
" <p>{text}</p>\n",
|
| 225 |
+
" </div>\n",
|
| 226 |
+
" </div>\n",
|
| 227 |
+
" \"\"\"\n",
|
| 228 |
+
"html += \"</div>\"\n",
|
| 229 |
+
"display(HTML(html))"
|
| 230 |
+
],
|
| 231 |
+
"id": "c6a4c569aa8d6213",
|
| 232 |
+
"outputs": [
|
| 233 |
+
{
|
| 234 |
+
"data": {
|
| 235 |
+
"text/plain": [
|
| 236 |
+
"<IPython.core.display.HTML object>"
|
| 237 |
+
],
|
| 238 |
+
"text/html": [
|
| 239 |
+
"<div style='display: flex; flex-direction: column;'>\n",
|
| 240 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 241 |
+
" <div style='margin-right: 20px;'>\n",
|
| 242 |
+
" <img src='assets/dress-drm-free/1.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 243 |
+
" </div>\n",
|
| 244 |
+
" <div style='text-align: left;'>\n",
|
| 245 |
+
" <p>back: 0.000<br/>closeup: 0.000<br/><b>front: 1.000</b><br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 246 |
+
" </div>\n",
|
| 247 |
+
" </div>\n",
|
| 248 |
+
" \n",
|
| 249 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 250 |
+
" <div style='margin-right: 20px;'>\n",
|
| 251 |
+
" <img src='assets/dress-drm-free/2.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 252 |
+
" </div>\n",
|
| 253 |
+
" <div style='text-align: left;'>\n",
|
| 254 |
+
" <p>back: 0.001<br/>closeup: 0.000<br/>front: 0.008<br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/><b>side_view: 0.991</b><br/>three_fourth: 0.000</p>\n",
|
| 255 |
+
" </div>\n",
|
| 256 |
+
" </div>\n",
|
| 257 |
+
" \n",
|
| 258 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 259 |
+
" <div style='margin-right: 20px;'>\n",
|
| 260 |
+
" <img src='assets/dress-drm-free/3.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 261 |
+
" </div>\n",
|
| 262 |
+
" <div style='text-align: left;'>\n",
|
| 263 |
+
" <p>back: 0.014<br/>closeup: 0.000<br/>front: 0.000<br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.001<br/><b>three_fourth: 0.985</b></p>\n",
|
| 264 |
+
" </div>\n",
|
| 265 |
+
" </div>\n",
|
| 266 |
+
" \n",
|
| 267 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 268 |
+
" <div style='margin-right: 20px;'>\n",
|
| 269 |
+
" <img src='assets/dress-drm-free/4.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 270 |
+
" </div>\n",
|
| 271 |
+
" <div style='text-align: left;'>\n",
|
| 272 |
+
" <p>back: 0.000<br/><b>closeup: 1.000</b><br/>front: 0.000<br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 273 |
+
" </div>\n",
|
| 274 |
+
" </div>\n",
|
| 275 |
+
" \n",
|
| 276 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 277 |
+
" <div style='margin-right: 20px;'>\n",
|
| 278 |
+
" <img src='assets/label-difficult/1.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 279 |
+
" </div>\n",
|
| 280 |
+
" <div style='text-align: left;'>\n",
|
| 281 |
+
" <p>back: 0.003<br/>closeup: 0.000<br/><b>front: 0.997</b><br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 282 |
+
" </div>\n",
|
| 283 |
+
" </div>\n",
|
| 284 |
+
" \n",
|
| 285 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 286 |
+
" <div style='margin-right: 20px;'>\n",
|
| 287 |
+
" <img src='assets/label-difficult/2.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 288 |
+
" </div>\n",
|
| 289 |
+
" <div style='text-align: left;'>\n",
|
| 290 |
+
" <p><b>back: 0.943</b><br/>closeup: 0.000<br/>front: 0.057<br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 291 |
+
" </div>\n",
|
| 292 |
+
" </div>\n",
|
| 293 |
+
" \n",
|
| 294 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 295 |
+
" <div style='margin-right: 20px;'>\n",
|
| 296 |
+
" <img src='assets/label-difficult/3.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 297 |
+
" </div>\n",
|
| 298 |
+
" <div style='text-align: left;'>\n",
|
| 299 |
+
" <p>back: 0.192<br/>closeup: 0.315<br/><b>front: 0.442</b><br/>inside: 0.000<br/>label: 0.048<br/>others: 0.001<br/>side_view: 0.000<br/>three_fourth: 0.002</p>\n",
|
| 300 |
+
" </div>\n",
|
| 301 |
+
" </div>\n",
|
| 302 |
+
" \n",
|
| 303 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 304 |
+
" <div style='margin-right: 20px;'>\n",
|
| 305 |
+
" <img src='assets/label-difficult/4.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 306 |
+
" </div>\n",
|
| 307 |
+
" <div style='text-align: left;'>\n",
|
| 308 |
+
" <p><b>back: 1.000</b><br/>closeup: 0.000<br/>front: 0.000<br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 309 |
+
" </div>\n",
|
| 310 |
+
" </div>\n",
|
| 311 |
+
" \n",
|
| 312 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 313 |
+
" <div style='margin-right: 20px;'>\n",
|
| 314 |
+
" <img src='assets/label-difficult/5.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 315 |
+
" </div>\n",
|
| 316 |
+
" <div style='text-align: left;'>\n",
|
| 317 |
+
" <p>back: 0.000<br/><b>closeup: 0.971</b><br/>front: 0.000<br/>inside: 0.000<br/>label: 0.028<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 318 |
+
" </div>\n",
|
| 319 |
+
" </div>\n",
|
| 320 |
+
" \n",
|
| 321 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 322 |
+
" <div style='margin-right: 20px;'>\n",
|
| 323 |
+
" <img src='assets/saint-james-coat/1.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 324 |
+
" </div>\n",
|
| 325 |
+
" <div style='text-align: left;'>\n",
|
| 326 |
+
" <p>back: 0.000<br/>closeup: 0.000<br/><b>front: 0.932</b><br/>inside: 0.000<br/>label: 0.000<br/>others: 0.065<br/>side_view: 0.002<br/>three_fourth: 0.000</p>\n",
|
| 327 |
+
" </div>\n",
|
| 328 |
+
" </div>\n",
|
| 329 |
+
" \n",
|
| 330 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 331 |
+
" <div style='margin-right: 20px;'>\n",
|
| 332 |
+
" <img src='assets/saint-james-coat/2.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 333 |
+
" </div>\n",
|
| 334 |
+
" <div style='text-align: left;'>\n",
|
| 335 |
+
" <p>back: 0.327<br/>closeup: 0.014<br/><b>front: 0.376</b><br/>inside: 0.001<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.271<br/>three_fourth: 0.011</p>\n",
|
| 336 |
+
" </div>\n",
|
| 337 |
+
" </div>\n",
|
| 338 |
+
" \n",
|
| 339 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 340 |
+
" <div style='margin-right: 20px;'>\n",
|
| 341 |
+
" <img src='assets/saint-james-coat/3.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 342 |
+
" </div>\n",
|
| 343 |
+
" <div style='text-align: left;'>\n",
|
| 344 |
+
" <p>back: 0.000<br/><b>closeup: 1.000</b><br/>front: 0.000<br/>inside: 0.000<br/>label: 0.000<br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 345 |
+
" </div>\n",
|
| 346 |
+
" </div>\n",
|
| 347 |
+
" \n",
|
| 348 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 349 |
+
" <div style='margin-right: 20px;'>\n",
|
| 350 |
+
" <img src='assets/saint-james-coat/4.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 351 |
+
" </div>\n",
|
| 352 |
+
" <div style='text-align: left;'>\n",
|
| 353 |
+
" <p>back: 0.000<br/>closeup: 0.266<br/>front: 0.004<br/>inside: 0.000<br/><b>label: 0.729</b><br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 354 |
+
" </div>\n",
|
| 355 |
+
" </div>\n",
|
| 356 |
+
" \n",
|
| 357 |
+
" <div style='display: flex; align-items: center; margin: 10px;'>\n",
|
| 358 |
+
" <div style='margin-right: 20px;'>\n",
|
| 359 |
+
" <img src='assets/saint-james-coat/5.jpeg' style='max-width: 200px; max-height: 200px;' />\n",
|
| 360 |
+
" </div>\n",
|
| 361 |
+
" <div style='text-align: left;'>\n",
|
| 362 |
+
" <p>back: 0.000<br/>closeup: 0.000<br/>front: 0.000<br/>inside: 0.000<br/><b>label: 1.000</b><br/>others: 0.000<br/>side_view: 0.000<br/>three_fourth: 0.000</p>\n",
|
| 363 |
+
" </div>\n",
|
| 364 |
+
" </div>\n",
|
| 365 |
+
" </div>"
|
| 366 |
+
]
|
| 367 |
+
},
|
| 368 |
+
"metadata": {},
|
| 369 |
+
"output_type": "display_data",
|
| 370 |
+
"jetTransient": {
|
| 371 |
+
"display_id": null
|
| 372 |
+
}
|
| 373 |
+
}
|
| 374 |
+
],
|
| 375 |
+
"execution_count": 17
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"metadata": {
|
| 379 |
+
"ExecuteTime": {
|
| 380 |
+
"end_time": "2025-11-10T21:15:02.510927Z",
|
| 381 |
+
"start_time": "2025-11-10T21:15:02.509743Z"
|
| 382 |
+
}
|
| 383 |
+
},
|
| 384 |
+
"cell_type": "code",
|
| 385 |
+
"source": "",
|
| 386 |
+
"id": "9d18593f866478ec",
|
| 387 |
+
"outputs": [],
|
| 388 |
+
"execution_count": null
|
| 389 |
+
}
|
| 390 |
+
],
|
| 391 |
+
"metadata": {
|
| 392 |
+
"kernelspec": {
|
| 393 |
+
"display_name": "Python 3",
|
| 394 |
+
"language": "python",
|
| 395 |
+
"name": "python3"
|
| 396 |
+
},
|
| 397 |
+
"language_info": {
|
| 398 |
+
"codemirror_mode": {
|
| 399 |
+
"name": "ipython",
|
| 400 |
+
"version": 2
|
| 401 |
+
},
|
| 402 |
+
"file_extension": ".py",
|
| 403 |
+
"mimetype": "text/x-python",
|
| 404 |
+
"name": "python",
|
| 405 |
+
"nbconvert_exporter": "python",
|
| 406 |
+
"pygments_lexer": "ipython2",
|
| 407 |
+
"version": "2.7.6"
|
| 408 |
+
}
|
| 409 |
+
},
|
| 410 |
+
"nbformat": 4,
|
| 411 |
+
"nbformat_minor": 5
|
| 412 |
+
}
|