Commit
·
562df22
1
Parent(s):
e247d26
add model
Browse files- README.md +116 -1
- example1.jpg +0 -0
- handler.py +52 -0
- requirements.txt +4 -0
- sac+logos+ava1-l14-linearMSE.pth +3 -0
- test.ipynb +76 -0
- utils.py +35 -0
README.md
CHANGED
@@ -1,3 +1,118 @@
|
|
1 |
---
|
2 |
-
|
|
|
|
|
|
|
|
|
3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
tags:
|
3 |
+
- clip
|
4 |
+
- image-classification
|
5 |
+
- endpoints-template
|
6 |
+
library_name: generic
|
7 |
---
|
8 |
+
|
9 |
+
# Fork of [Geonmo/laion-aesthetic-predictor](https://huggingface.co/spaces/Geonmo/laion-aesthetic-predictor) for an Image Aesthetic Predictor).
|
10 |
+
|
11 |
+
This repository implements a `custom` task for `Geonmo/laion-aesthetic-predictor` for 🤗 Inference Endpoints. The code for the customized handler is in the [handler.py](https://huggingface.co/philschmid/laion-asthetic-endpoint/tree/main/handler.py).
|
12 |
+
|
13 |
+
## Test Handler locally.
|
14 |
+
|
15 |
+
This model & handker can be tested locally using the [
|
16 |
+
hf-endpoints-emulator](https://github.com/huggingface/hf-endpoints-emulator).
|
17 |
+
|
18 |
+
|
19 |
+
1. Clone the repository and install the requirements.
|
20 |
+
|
21 |
+
```bash
|
22 |
+
git lfs install
|
23 |
+
git clone https://huggingface.co/philschmid/laion-asthetic-endpoint
|
24 |
+
|
25 |
+
cd laion-asthetic-endpoint
|
26 |
+
pip install -r requirements.txt
|
27 |
+
```
|
28 |
+
2. Install `hf-endpoints-emulator`
|
29 |
+
|
30 |
+
```bash
|
31 |
+
pip install hf-endpoints-emulator
|
32 |
+
```
|
33 |
+
|
34 |
+
3. Run the emulator
|
35 |
+
|
36 |
+
```bash
|
37 |
+
hf-endpoints-emulator --handler handler.py
|
38 |
+
```
|
39 |
+
|
40 |
+
4. Test the endpoint and send request
|
41 |
+
|
42 |
+
```bash
|
43 |
+
curl --request POST \
|
44 |
+
--url http://localhost \
|
45 |
+
--header 'Content-Type: image/jpg' \
|
46 |
+
--data-binary '@example1.jpg'
|
47 |
+
```
|
48 |
+
|
49 |
+
|
50 |
+
## Run Request
|
51 |
+
|
52 |
+
The endpoint expects the image to be served as `binary`. Below is an curl and python example
|
53 |
+
|
54 |
+
#### cURL
|
55 |
+
|
56 |
+
1. get image
|
57 |
+
|
58 |
+
```bash
|
59 |
+
wget https://fki.tic.heia-fr.ch/static/img/a01-122-02-00.jpg -O test.jpg
|
60 |
+
```
|
61 |
+
|
62 |
+
2. send cURL request
|
63 |
+
|
64 |
+
```bash
|
65 |
+
curl --request POST \
|
66 |
+
--url https://{ENDPOINT}/ \
|
67 |
+
--header 'Content-Type: image/jpg' \
|
68 |
+
--header 'Authorization: Bearer {HF_TOKEN}' \
|
69 |
+
--data-binary '@test.jpg'
|
70 |
+
```
|
71 |
+
|
72 |
+
3. the expected output
|
73 |
+
|
74 |
+
```json
|
75 |
+
{"text": "INDLUS THE"}
|
76 |
+
```
|
77 |
+
|
78 |
+
#### Python
|
79 |
+
|
80 |
+
|
81 |
+
1. get image
|
82 |
+
|
83 |
+
```bash
|
84 |
+
wget https://fki.tic.heia-fr.ch/static/img/a01-122-02-00.jpg -O test.jpg
|
85 |
+
```
|
86 |
+
|
87 |
+
2. run request
|
88 |
+
|
89 |
+
```python
|
90 |
+
import json
|
91 |
+
from typing import List
|
92 |
+
import requests as r
|
93 |
+
import base64
|
94 |
+
|
95 |
+
ENDPOINT_URL=""
|
96 |
+
HF_TOKEN=""
|
97 |
+
|
98 |
+
def predict(path_to_image:str=None):
|
99 |
+
with open(path_to_image, "rb") as i:
|
100 |
+
b = i.read()
|
101 |
+
headers= {
|
102 |
+
"Authorization": f"Bearer {HF_TOKEN}",
|
103 |
+
"Content-Type": "image/jpeg" # content type of image
|
104 |
+
}
|
105 |
+
response = r.post(ENDPOINT_URL, headers=headers, data=b)
|
106 |
+
return response.json()
|
107 |
+
|
108 |
+
prediction = predict(path_to_image="test.jpg")
|
109 |
+
|
110 |
+
prediction
|
111 |
+
```
|
112 |
+
|
113 |
+
expected output
|
114 |
+
|
115 |
+
```python
|
116 |
+
{"text": "INDLUS THE"}
|
117 |
+
```
|
118 |
+
|
example1.jpg
ADDED
handler.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import torch
|
3 |
+
import clip
|
4 |
+
from utils import MLP, normalized
|
5 |
+
|
6 |
+
# set device
|
7 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
8 |
+
|
9 |
+
|
10 |
+
class EndpointHandler:
|
11 |
+
def __init__(self, path=""):
|
12 |
+
model = MLP(768)
|
13 |
+
|
14 |
+
s = torch.load(os.path.join(path, "sac+logos+ava1-l14-linearMSE.pth"), map_location=device)
|
15 |
+
|
16 |
+
model.load_state_dict(s)
|
17 |
+
model.to(device)
|
18 |
+
model.eval()
|
19 |
+
|
20 |
+
model2, preprocess = clip.load("ViT-L/14", device=device)
|
21 |
+
|
22 |
+
self.model_dict = {}
|
23 |
+
self.model_dict["classifier"] = model
|
24 |
+
self.model_dict["clip_model"] = model2
|
25 |
+
self.model_dict["clip_preprocess"] = preprocess
|
26 |
+
self.model_dict["device"] = device
|
27 |
+
|
28 |
+
def __call__(self, data):
|
29 |
+
"""
|
30 |
+
data args:
|
31 |
+
images (:obj:`PIL.Image`)
|
32 |
+
candiates (:obj:`list`)
|
33 |
+
Return:
|
34 |
+
A :obj:`list`:. The list contains items that are dicts should be liked {"label": "XXX", "score": 0.82}
|
35 |
+
"""
|
36 |
+
# extract converted PIL image from serialized request
|
37 |
+
image = data.pop("inputs", data)
|
38 |
+
|
39 |
+
image_input = self.model_dict["clip_preprocess"](image).unsqueeze(0).to(self.model_dict["device"])
|
40 |
+
with torch.no_grad():
|
41 |
+
image_features = self.model_dict["clip_model"].encode_image(image_input)
|
42 |
+
if self.model_dict["device"].type == "cuda":
|
43 |
+
im_emb_arr = normalized(image_features.detach().cpu().numpy())
|
44 |
+
im_emb = torch.from_numpy(im_emb_arr).to(self.model_dict["device"]).type(torch.cuda.FloatTensor)
|
45 |
+
else:
|
46 |
+
im_emb_arr = normalized(image_features.detach().numpy())
|
47 |
+
im_emb = torch.from_numpy(im_emb_arr).to(self.model_dict["device"]).type(torch.FloatTensor)
|
48 |
+
|
49 |
+
prediction = self.model_dict["classifier"](im_emb)
|
50 |
+
score = prediction.item()
|
51 |
+
|
52 |
+
return {"aesthetic score": score}
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ftfy
|
2 |
+
regex
|
3 |
+
git+https://github.com/openai/CLIP.git
|
4 |
+
pytorch-lightning
|
sac+logos+ava1-l14-linearMSE.pth
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:21dd590f3ccdc646f0d53120778b296013b096a035a2718c9cb0d511bff0f1e0
|
3 |
+
size 3714759
|
test.ipynb
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": 1,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [],
|
8 |
+
"source": [
|
9 |
+
"from handler import EndpointHandler\n",
|
10 |
+
"\n",
|
11 |
+
"# init handler\n",
|
12 |
+
"my_handler = EndpointHandler(path=\".\")\n"
|
13 |
+
]
|
14 |
+
},
|
15 |
+
{
|
16 |
+
"cell_type": "code",
|
17 |
+
"execution_count": 2,
|
18 |
+
"metadata": {},
|
19 |
+
"outputs": [
|
20 |
+
{
|
21 |
+
"data": {
|
22 |
+
"text/plain": [
|
23 |
+
"{'aesthetic score': 6.764713287353516}"
|
24 |
+
]
|
25 |
+
},
|
26 |
+
"execution_count": 2,
|
27 |
+
"metadata": {},
|
28 |
+
"output_type": "execute_result"
|
29 |
+
}
|
30 |
+
],
|
31 |
+
"source": [
|
32 |
+
"from PIL import Image\n",
|
33 |
+
"\n",
|
34 |
+
"# read PIL image\n",
|
35 |
+
"image = Image.open(\"example1.jpg\")\n",
|
36 |
+
"payload = {\"inputs\": image}\n",
|
37 |
+
"\n",
|
38 |
+
"my_handler(payload)"
|
39 |
+
]
|
40 |
+
},
|
41 |
+
{
|
42 |
+
"cell_type": "code",
|
43 |
+
"execution_count": null,
|
44 |
+
"metadata": {},
|
45 |
+
"outputs": [],
|
46 |
+
"source": []
|
47 |
+
}
|
48 |
+
],
|
49 |
+
"metadata": {
|
50 |
+
"kernelspec": {
|
51 |
+
"display_name": "dev",
|
52 |
+
"language": "python",
|
53 |
+
"name": "python3"
|
54 |
+
},
|
55 |
+
"language_info": {
|
56 |
+
"codemirror_mode": {
|
57 |
+
"name": "ipython",
|
58 |
+
"version": 3
|
59 |
+
},
|
60 |
+
"file_extension": ".py",
|
61 |
+
"mimetype": "text/x-python",
|
62 |
+
"name": "python",
|
63 |
+
"nbconvert_exporter": "python",
|
64 |
+
"pygments_lexer": "ipython3",
|
65 |
+
"version": "3.9.13"
|
66 |
+
},
|
67 |
+
"orig_nbformat": 4,
|
68 |
+
"vscode": {
|
69 |
+
"interpreter": {
|
70 |
+
"hash": "f6dd96c16031089903d5a31ec148b80aeb0d39c32affb1a1080393235fbfa2fc"
|
71 |
+
}
|
72 |
+
}
|
73 |
+
},
|
74 |
+
"nbformat": 4,
|
75 |
+
"nbformat_minor": 2
|
76 |
+
}
|
utils.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
import numpy as np
|
4 |
+
|
5 |
+
|
6 |
+
# if you changed the MLP architecture during training, change it also here:
|
7 |
+
class MLP(torch.nn.Module):
|
8 |
+
def __init__(self, input_size, xcol="emb", ycol="avg_rating"):
|
9 |
+
super().__init__()
|
10 |
+
self.input_size = input_size
|
11 |
+
self.xcol = xcol
|
12 |
+
self.ycol = ycol
|
13 |
+
self.layers = nn.Sequential(
|
14 |
+
nn.Linear(self.input_size, 1024),
|
15 |
+
# nn.ReLU(),
|
16 |
+
nn.Dropout(0.2),
|
17 |
+
nn.Linear(1024, 128),
|
18 |
+
# nn.ReLU(),
|
19 |
+
nn.Dropout(0.2),
|
20 |
+
nn.Linear(128, 64),
|
21 |
+
# nn.ReLU(),
|
22 |
+
nn.Dropout(0.1),
|
23 |
+
nn.Linear(64, 16),
|
24 |
+
# nn.ReLU(),
|
25 |
+
nn.Linear(16, 1),
|
26 |
+
)
|
27 |
+
|
28 |
+
def forward(self, x):
|
29 |
+
return self.layers(x)
|
30 |
+
|
31 |
+
|
32 |
+
def normalized(a, axis=-1, order=2):
|
33 |
+
l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
|
34 |
+
l2[l2 == 0] = 1
|
35 |
+
return a / np.expand_dims(l2, axis)
|