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)
|