Skip to content

Commit fff8c39

Browse files
authored
Add trusses for Stable Diffusion models (#233)
* add trusses for SD text2image, image2image, and inpainting * add shared external package for b64 utils + readme
1 parent d5fc947 commit fff8c39

File tree

14 files changed

+285
-0
lines changed

14 files changed

+285
-0
lines changed

examples/shared/base64_utils.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import base64
2+
from io import BytesIO
3+
4+
from PIL import Image
5+
6+
BASE64_PREAMBLE = "data:image/png;base64,"
7+
8+
9+
def pil_to_b64(pil_img):
10+
buffered = BytesIO()
11+
pil_img.save(buffered, format="PNG")
12+
img_str = base64.b64encode(buffered.getvalue())
13+
return BASE64_PREAMBLE + str(img_str)[2:-1]
14+
15+
16+
def b64_to_pil(b64_str):
17+
return Image.open(BytesIO(base64.b64decode(b64_str.replace(BASE64_PREAMBLE, ""))))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
bundled_packages_dir: packages
2+
data_dir: data
3+
description: null
4+
environment_variables: {}
5+
examples_filename: examples.yaml
6+
external_package_dirs:
7+
- ../shared/
8+
input_type: Any
9+
live_reload: false
10+
model_class_filename: model.py
11+
model_class_name: Model
12+
model_framework: custom
13+
model_metadata: {}
14+
model_module_dir: model
15+
model_name: Stable Diffusion 1.5 Img2Img
16+
model_type: custom
17+
python_version: py39
18+
requirements:
19+
- diffusers
20+
- transformers
21+
- accelerate
22+
- scipy
23+
- safetensors
24+
- xformers
25+
- triton
26+
resources:
27+
accelerator: A10G
28+
cpu: "8"
29+
memory: 30Gi
30+
use_gpu: true
31+
secrets: {}
32+
spec_version: '2.0'
33+
system_packages: []

examples/stable-diffusion-1-5-img2img/examples.yaml

+4
Large diffs are not rendered by default.

examples/stable-diffusion-1-5-img2img/model/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from dataclasses import asdict
2+
from typing import Dict
3+
4+
import torch
5+
from base64_utils import b64_to_pil, pil_to_b64
6+
from diffusers import StableDiffusionImg2ImgPipeline
7+
8+
STABLE_DIFFUSION_MODEL_ID = "runwayml/stable-diffusion-v1-5"
9+
10+
11+
class Model:
12+
def __init__(self, **kwargs) -> None:
13+
self._model = None
14+
15+
def load(self):
16+
self._model = StableDiffusionImg2ImgPipeline.from_pretrained(
17+
STABLE_DIFFUSION_MODEL_ID,
18+
torch_dtype=torch.float16,
19+
)
20+
self._model = self._model.to("cuda")
21+
22+
def preprocess(self, request: Dict) -> Dict:
23+
# Convert from base64
24+
if "image" in request:
25+
request["image"] = b64_to_pil(request["image"]).convert("RGB")
26+
return request
27+
28+
def postprocess(self, request: Dict) -> Dict:
29+
# Convert to base64
30+
request.images = [pil_to_b64(img) for img in request.images]
31+
return asdict(request)
32+
33+
def predict(self, request: Dict):
34+
response = self._model(**request)
35+
return response
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
bundled_packages_dir: packages
2+
data_dir: data
3+
description: null
4+
environment_variables: {}
5+
examples_filename: examples.yaml
6+
external_package_dirs:
7+
- ../shared/
8+
input_type: Any
9+
live_reload: false
10+
model_class_filename: model.py
11+
model_class_name: Model
12+
model_framework: custom
13+
model_metadata: {}
14+
model_module_dir: model
15+
model_name: Stable Diffusion 1.5
16+
model_type: custom
17+
python_version: py39
18+
requirements:
19+
- diffusers
20+
- transformers
21+
- accelerate
22+
- scipy
23+
- safetensors
24+
- xformers
25+
- triton
26+
resources:
27+
accelerator: A10G
28+
cpu: "8"
29+
memory: 30Gi
30+
use_gpu: true
31+
secrets: {}
32+
spec_version: '2.0'
33+
system_packages: []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Adding *custom weights* to Stable Diffusion
2+
If you have custom weights for Stable Diffusion and want to build a truss for it, its as simple as doing the following!
3+
4+
## Add weights to the data directory
5+
In your truss, add your weights to the `data` directory, and make sure its specified in your `config.yaml` file here:
6+
```
7+
...
8+
data_dir: data
9+
...
10+
```
11+
Make sure you have the appropriate subfolders, your `data` directory structure should look something like this:
12+
```
13+
your_truss/
14+
...
15+
data/
16+
feature_extractor/
17+
safety_checker/
18+
scheduler/
19+
text_encoder/
20+
tokenizer/
21+
unet/
22+
vae/
23+
model_index.json
24+
...
25+
```
26+
27+
## Reference the data directory in the `models.py` file
28+
We can [utilize the functionality](https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained) to pass in a path to the `from_pretrained` method off of the `StableDiffusionPipeline`.
29+
30+
You'll need to reference this data directory in your `model.py` file, in this case `_data_dir` is the name of your `data` directory:
31+
32+
```
33+
class Model:
34+
def __init__(self, **kwargs) -> None:
35+
self._data_dir = kwargs["data_dir"]
36+
self._model = None
37+
38+
def load(self):
39+
self._model = StableDiffusionPipeline.from_pretrained(
40+
self._data_dir,
41+
torch_dtype=torch.float16,
42+
)
43+
self._model = self._model.to("cuda")
44+
45+
...
46+
```
47+
48+
And this is all you need to load your custom weights and create a truss for your custom Stable Diffusion model!
49+
50+
## Truss in the process...
51+
[Follow the documentation here](https://truss.baseten.co/develop/localhost) to build and run your truss!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- input:
2+
prompt: a photo of an astronaut riding a horse on mars
3+
name: astronaut example

examples/stable-diffusion-1-5/model/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from dataclasses import asdict
2+
from typing import Dict
3+
4+
import torch
5+
from base64_utils import pil_to_b64
6+
from diffusers import EulerDiscreteScheduler, StableDiffusionPipeline
7+
8+
STABLE_DIFFUSION_MODEL_ID = "runwayml/stable-diffusion-v1-5"
9+
10+
11+
class Model:
12+
def __init__(self, **kwargs) -> None:
13+
self._model = None
14+
15+
def load(self):
16+
scheduler = EulerDiscreteScheduler.from_pretrained(
17+
STABLE_DIFFUSION_MODEL_ID,
18+
subfolder="scheduler",
19+
)
20+
self._model = StableDiffusionPipeline.from_pretrained(
21+
STABLE_DIFFUSION_MODEL_ID,
22+
scheduler=scheduler,
23+
torch_dtype=torch.float16,
24+
)
25+
self._model.unet.set_use_memory_efficient_attention_xformers(True)
26+
self._model = self._model.to("cuda")
27+
28+
def postprocess(self, request: Dict) -> Dict:
29+
# Convert to base64
30+
request.images = [pil_to_b64(img) for img in request.images]
31+
return asdict(request)
32+
33+
def predict(self, request: Dict):
34+
response = self._model(**request)
35+
return response
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
bundled_packages_dir: packages
2+
data_dir: data
3+
description: null
4+
environment_variables: {}
5+
examples_filename: examples.yaml
6+
external_package_dirs:
7+
- ../shared/
8+
input_type: Any
9+
live_reload: false
10+
model_class_filename: model.py
11+
model_class_name: Model
12+
model_framework: custom
13+
model_metadata: {}
14+
model_module_dir: model
15+
model_name: Stable Diffusion Inpainting
16+
model_type: custom
17+
python_version: py39
18+
requirements:
19+
- diffusers
20+
- transformers
21+
- accelerate
22+
- scipy
23+
- safetensors
24+
- xformers
25+
- triton
26+
resources:
27+
accelerator: A10G
28+
cpu: "8"
29+
memory: 30Gi
30+
use_gpu: true
31+
secrets: {}
32+
spec_version: '2.0'
33+
system_packages: []

examples/stable-diffusion-inpainting/examples.yaml

+5
Large diffs are not rendered by default.

examples/stable-diffusion-inpainting/model/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from dataclasses import asdict
2+
from typing import Dict
3+
4+
import torch
5+
from base64_utils import b64_to_pil, pil_to_b64
6+
from diffusers import StableDiffusionInpaintPipeline
7+
8+
STABLE_DIFFUSION_MODEL_ID = "runwayml/stable-diffusion-inpainting"
9+
10+
11+
class Model:
12+
def __init__(self, **kwargs) -> None:
13+
self._model = None
14+
15+
def load(self):
16+
self._model = StableDiffusionInpaintPipeline.from_pretrained(
17+
STABLE_DIFFUSION_MODEL_ID, revision="fp16", torch_dtype=torch.float16
18+
)
19+
self._model = self._model.to("cuda")
20+
21+
def preprocess(self, request: Dict) -> Dict:
22+
# Convert from base64
23+
if "image" in request:
24+
request["image"] = b64_to_pil(request["image"])
25+
if "mask_image" in request:
26+
request["mask_image"] = b64_to_pil(request["mask_image"])
27+
return request
28+
29+
def postprocess(self, request: Dict) -> Dict:
30+
# Convert to base64
31+
request.images = [pil_to_b64(img) for img in request.images]
32+
return asdict(request)
33+
34+
def predict(self, request: Dict):
35+
response = self._model(**request)
36+
return response

0 commit comments

Comments
 (0)