mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-11-11 03:24:30 +00:00
Initial commit
This commit is contained in:
commit
3aca342e60
23 files changed
+5312
No files matched your search
@@ -0,0 +1,4 @@
|
||||
*.bin
|
||||
*.ckpt
|
||||
*.safetensor
|
||||
*.log
|
||||
@@ -0,0 +1,26 @@
|
||||
# Model Convert Script
|
||||
|
||||
## Requirements
|
||||
|
||||
- vocab.json, from https://huggingface.co/openai/clip-vit-large-patch14/raw/main/vocab.json
|
||||
|
||||
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
```
|
||||
usage: convert.py [-h] [--out_type {f32,f16,q4_0,q4_1,q5_0,q5_1,q8_0}] [--out_file OUT_FILE] model_path
|
||||
|
||||
Convert Stable Diffuison model to GGML compatible file format
|
||||
|
||||
positional arguments:
|
||||
model_path model file path (*.pth, *.pt, *.ckpt, *.safetensors)
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--out_type {f32,f16,q4_0,q4_1,q5_0,q5_1,q8_0}
|
||||
output format (default: based on input)
|
||||
--out_file OUT_FILE path to write to; default: based on input and current working directory
|
||||
```
|
||||
@@ -0,0 +1,264 @@
|
||||
import struct
|
||||
import json
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import safetensors.torch
|
||||
|
||||
this_file_dir = os.path.dirname(__file__)
|
||||
vocab_dir = this_file_dir
|
||||
|
||||
ggml_ftype_str_to_int = {
|
||||
"f32": 0,
|
||||
"f16": 1,
|
||||
"q4_0": 2,
|
||||
"q4_1": 3,
|
||||
"q5_0": 8,
|
||||
"q5_1": 9,
|
||||
"q8_0": 7
|
||||
}
|
||||
|
||||
ggml_ttype_str_to_int = {
|
||||
"f32": 0,
|
||||
"f16": 1,
|
||||
"q4_0": 2,
|
||||
"q4_1": 3,
|
||||
"q5_0": 6,
|
||||
"q5_1": 7,
|
||||
"q8_0": 8
|
||||
}
|
||||
|
||||
QK4_0 = 32
|
||||
def quantize_q4_0(x):
|
||||
assert x.shape[-1] % QK4_0 == 0
|
||||
x = x.reshape(-1, QK4_0)
|
||||
max = np.take_along_axis(x, np.argmax(np.abs(x), axis=-1)[:, np.newaxis], axis=-1)
|
||||
d = max / -8
|
||||
qs = ((x / d) + 8).round().clip(min=0, max=15).astype(np.int8)
|
||||
half = QK4_0 // 2
|
||||
qs = qs[:, :half] | (qs[:, half:] << 4)
|
||||
d = d.astype(np.float16).view(np.int8)
|
||||
y = np.concatenate((d, qs), axis=-1)
|
||||
return y
|
||||
|
||||
QK4_1 = 32
|
||||
def quantize_q4_1(x):
|
||||
assert x.shape[-1] % QK4_1 == 0
|
||||
x = x.reshape(-1, QK4_1)
|
||||
min = np.min(x, axis=-1, keepdims=True)
|
||||
max = np.max(x, axis=-1, keepdims=True)
|
||||
d = (max - min) / ((1 << 4) - 1)
|
||||
qs = ((x - min) / d).round().clip(min=0, max=15).astype(np.int8)
|
||||
half = QK4_1 // 2
|
||||
qs = qs[:, :half] | (qs[:, half:] << 4)
|
||||
d = d.astype(np.float16).view(np.int8)
|
||||
m = min.astype(np.float16).view(np.int8)
|
||||
y = np.concatenate((d, m, qs), axis=-1)
|
||||
return y
|
||||
|
||||
QK5_0 = 32
|
||||
def quantize_q5_0(x):
|
||||
assert x.shape[1] % QK5_0 == 0
|
||||
x = x.reshape(-1, QK5_0)
|
||||
max = np.take_along_axis(x, np.argmax(np.abs(x), axis=-1)[:, np.newaxis], axis=-1)
|
||||
d = max / -16
|
||||
xi = ((x / d) + 16).round().clip(min=0, max=31).astype(np.int8)
|
||||
half = QK5_0 // 2
|
||||
qs = (xi[:, :half] & 0x0F) | (xi[:, half:] << 4)
|
||||
qh = np.zeros(qs.shape[:-1], dtype=np.int32)
|
||||
for i in range(QK5_0):
|
||||
qh |= ((xi[:, i] & 0x10) >> 4).astype(np.int32) << i
|
||||
d = d.astype(np.float16).view(np.int8)
|
||||
qh = qh[..., np.newaxis].view(np.int8)
|
||||
y = np.concatenate((d, qh, qs), axis=-1)
|
||||
return y
|
||||
|
||||
QK5_1 = 32
|
||||
def quantize_q5_1(x):
|
||||
assert x.shape[-1] % QK5_1 == 0
|
||||
x = x.reshape(-1, QK5_1)
|
||||
min = np.min(x, axis=-1, keepdims=True)
|
||||
max = np.max(x, axis=-1, keepdims=True)
|
||||
d = (max - min) / ((1 << 5) - 1)
|
||||
xi = ((x - min) / d).round().clip(min=0, max=31).astype(np.int8)
|
||||
half = QK5_1//2
|
||||
qs = (xi[:, :half] & 0x0F) | (xi[:, half:] << 4)
|
||||
qh = np.zeros(xi.shape[:-1], dtype=np.int32)
|
||||
for i in range(QK5_1):
|
||||
qh |= ((xi[:, i] & 0x10) >> 4).astype(np.int32) << i
|
||||
d = d.astype(np.float16).view(np.int8)
|
||||
m = min.astype(np.float16).view(np.int8)
|
||||
qh = qh[..., np.newaxis].view(np.int8)
|
||||
ndarray = np.concatenate((d, m, qh, qs), axis=-1)
|
||||
return ndarray
|
||||
|
||||
QK8_0 = 32
|
||||
def quantize_q8_0(x):
|
||||
assert x.shape[-1] % QK8_0 == 0
|
||||
x = x.reshape(-1, QK8_0)
|
||||
amax = np.max(np.abs(x), axis=-1, keepdims=True)
|
||||
d = amax / ((1 << 7) - 1)
|
||||
qs = (x / d).round().clip(min=-128, max=127).astype(np.int8)
|
||||
d = d.astype(np.float16).view(np.int8)
|
||||
y = np.concatenate((d, qs), axis=-1)
|
||||
return y
|
||||
|
||||
# copy from https://github.com/openai/CLIP/blob/main/clip/simple_tokenizer.py#L16
|
||||
def bytes_to_unicode():
|
||||
"""
|
||||
Returns list of utf-8 byte and a corresponding list of unicode strings.
|
||||
The reversible bpe codes work on unicode strings.
|
||||
This means you need a large # of unicode characters in your vocab if you want to avoid UNKs.
|
||||
When you're at something like a 10B token dataset you end up needing around 5K for decent coverage.
|
||||
This is a signficant percentage of your normal, say, 32K bpe vocab.
|
||||
To avoid that, we want lookup tables between utf-8 bytes and unicode strings.
|
||||
And avoids mapping to whitespace/control characters the bpe code barfs on.
|
||||
"""
|
||||
bs = list(range(ord("!"), ord("~")+1))+list(range(ord("¡"), ord("¬")+1))+list(range(ord("®"), ord("ÿ")+1))
|
||||
cs = bs[:]
|
||||
n = 0
|
||||
for b in range(2**8):
|
||||
if b not in bs:
|
||||
bs.append(b)
|
||||
cs.append(2**8+n)
|
||||
n += 1
|
||||
cs = [chr(n) for n in cs]
|
||||
return dict(zip(bs, cs))
|
||||
|
||||
def load_model_from_file(model_path):
|
||||
print("loading model from {}".format(model_path))
|
||||
if model_path.lower().endswith(".safetensors"):
|
||||
pl_sd = safetensors.torch.load_file(model_path, device="cpu")
|
||||
else:
|
||||
pl_sd = torch.load(model_path, map_location="cpu")
|
||||
state_dict = pl_sd["state_dict"] if "state_dict" in pl_sd else pl_sd
|
||||
print("loading model from {} completed".format(model_path))
|
||||
return state_dict
|
||||
|
||||
def get_alpha_comprod(linear_start=0.00085, linear_end=0.0120, timesteps=1000):
|
||||
betas = torch.linspace(linear_start ** 0.5, linear_end ** 0.5, timesteps, dtype=torch.float32) ** 2
|
||||
alphas = 1. - betas
|
||||
alphas_cumprod = np.cumprod(alphas.numpy(), axis=0)
|
||||
return torch.tensor(alphas_cumprod)
|
||||
|
||||
unused_tensors = [
|
||||
"betas",
|
||||
"alphas_cumprod_prev",
|
||||
"sqrt_alphas_cumprod",
|
||||
"sqrt_one_minus_alphas_cumprod",
|
||||
"log_one_minus_alphas_cumprod",
|
||||
"sqrt_recip_alphas_cumprod",
|
||||
"sqrt_recipm1_alphas_cumprod",
|
||||
"posterior_variance",
|
||||
"posterior_log_variance_clipped",
|
||||
"posterior_mean_coef1",
|
||||
"posterior_mean_coef2",
|
||||
"cond_stage_model.transformer.text_model.embeddings.position_ids",
|
||||
"model_ema.decay",
|
||||
"model_ema.num_updates"
|
||||
]
|
||||
|
||||
def convert(model_path, out_type = None, out_file=None):
|
||||
# load model
|
||||
with open(os.path.join(vocab_dir, "vocab.json"), encoding="utf-8") as f:
|
||||
clip_vocab = json.load(f)
|
||||
|
||||
state_dict = load_model_from_file(model_path)
|
||||
alphas_cumprod = state_dict.get("alphas_cumprod")
|
||||
if alphas_cumprod != None:
|
||||
# print((np.abs(get_alpha_comprod().numpy() - alphas_cumprod.numpy()) < 0.000001).all())
|
||||
pass
|
||||
else:
|
||||
print("no alphas_cumprod in file, generate new one")
|
||||
alphas_cumprod = get_alpha_comprod()
|
||||
state_dict["alphas_cumprod"] = alphas_cumprod
|
||||
|
||||
|
||||
# output option
|
||||
if out_type == None:
|
||||
weight = state_dict["cond_stage_model.transformer.text_model.encoder.layers.0.self_attn.k_proj.weight"].numpy()
|
||||
if weight.dtype == np.float32:
|
||||
out_type = "f32"
|
||||
elif weight.dtype == np.float16:
|
||||
out_type = "f16"
|
||||
if out_file == None:
|
||||
out_file = os.path.splitext(os.path.basename(model_path))[0] + f"-ggml-model-{out_type}.bin"
|
||||
out_file = os.path.join(os.getcwd(), out_file)
|
||||
print(f"Saving GGML compatible file to {out_file}")
|
||||
|
||||
# convert and save
|
||||
with open(out_file, "wb") as file:
|
||||
# magic: ggml in hex
|
||||
file.write(struct.pack("i", 0x67676D6C))
|
||||
# out type
|
||||
file.write(struct.pack("i", ggml_ftype_str_to_int[out_type]))
|
||||
|
||||
# vocab
|
||||
byte_encoder = bytes_to_unicode()
|
||||
byte_decoder = {v: k for k, v in byte_encoder.items()}
|
||||
file.write(struct.pack("i", len(clip_vocab)))
|
||||
for key in clip_vocab:
|
||||
text = bytearray([byte_decoder[c] for c in key])
|
||||
file.write(struct.pack("i", len(text)))
|
||||
file.write(text)
|
||||
|
||||
# weights
|
||||
for name in state_dict.keys():
|
||||
if not isinstance(state_dict[name], torch.Tensor):
|
||||
continue
|
||||
if name in unused_tensors:
|
||||
continue
|
||||
data = state_dict[name].numpy()
|
||||
|
||||
n_dims = len(data.shape)
|
||||
shape = data.shape
|
||||
old_type = data.dtype
|
||||
|
||||
ttype = "f32"
|
||||
if n_dims == 4:
|
||||
data = data.astype(np.float16)
|
||||
ttype = "f16"
|
||||
elif n_dims == 2 and name[-7:] == ".weight":
|
||||
if out_type == "f32":
|
||||
data = data.astype(np.float32)
|
||||
elif out_type == "f16":
|
||||
data = data.astype(np.float16)
|
||||
elif out_type == "q4_0":
|
||||
data = quantize_q4_0(data)
|
||||
elif out_type == "q4_1":
|
||||
data = quantize_q4_1(data)
|
||||
elif out_type == "q5_0":
|
||||
data = quantize_q5_0(data)
|
||||
elif out_type == "q5_1":
|
||||
data = quantize_q5_1(data)
|
||||
elif out_type == "q8_0":
|
||||
data = quantize_q8_0(data)
|
||||
else:
|
||||
raise Exception("invalid out_type {}".format(out_type))
|
||||
ttype = out_type
|
||||
else:
|
||||
data = data.astype(np.float32)
|
||||
ttype = "f32"
|
||||
|
||||
print("Processing tensor: {} with shape {}, {} -> {}".format(name, data.shape, old_type, ttype))
|
||||
|
||||
# header
|
||||
name_bytes = name.encode("utf-8")
|
||||
file.write(struct.pack("iii", n_dims, len(name_bytes), ggml_ttype_str_to_int[ttype]))
|
||||
for i in range(n_dims):
|
||||
file.write(struct.pack("i", shape[n_dims - 1 - i]))
|
||||
file.write(name_bytes)
|
||||
# data
|
||||
data.tofile(file)
|
||||
print("Convert done")
|
||||
print(f"Saved GGML compatible file to {out_file}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Convert Stable Diffuison model to GGML compatible file format")
|
||||
parser.add_argument("--out_type", choices=["f32", "f16", "q4_0", "q4_1", "q5_0", "q5_1", "q8_0"], help="output format (default: based on input)")
|
||||
parser.add_argument("--out_file", help="path to write to; default: based on input and current working directory")
|
||||
parser.add_argument("model_path", help="model file path (*.pth, *.pt, *.ckpt, *.safetensors)")
|
||||
args = parser.parse_args()
|
||||
convert(args.model_path, args.out_type, args.out_file)
|
||||
@@ -0,0 +1,4 @@
|
||||
numpy
|
||||
torch
|
||||
safetensors
|
||||
pytorch_lightning
|
||||
@@ -0,0 +1 @@
|
||||
{"!": 0, "\"": 1, "#": 2, "$": 3, "%": 4, "&": 5, "'": 6, "(": 7, ")": 8, "*": 9, "+": 10, ",": 11, "-": 12, ".": 13, "/": 14, "0": 15, "1": 16, "2": 17, "3": 18, "4": 19, "5": 20, "6": 21, "7": 22, "8": 23, "9": 24, ":": 25, ";": 26, "<": 27, "=": 28, ">": 29, "?": 30, "@": 31, "A": 32, "B": 33, "C": 34, "D": 35, "E": 36, "F": 37, "G": 38, "H": 39, "I": 40, "J": 41, "K": 42, "L": 43, "M": 44, "N": 45, "O": 46, "P": 47, "Q": 48, "R": 49, "S": 50, "T": 51, "U": 52, "V": 53, "W": 54, "X": 55, "Y": 56, "Z": 57, "[": 58, "\\": 59, "]": 60, "^": 61, "_": 62, "`": 63, "a": 64, "b": 65, "c": 66, "d": 67, "e": 68, "f": 69, "g": 70, "h": 71, "i": 72, "j": 73, "k": 74, "l": 75, "m": 76, "n": 77, "o": 78, "p": 79, "q": 80, "r": 81, "s": 82, "t": 83, "u": 84, "v": 85, "w": 86, "x": 87, "y": 88, "z": 89, "{": 90, "|": 91, "}": 92, "~": 93, "¡": 94, "¢": 95, "£": 96, "¤": 97, "¥": 98, "¦": 99, "§": 100, "¨": 101, "©": 102, "ª": 103, "«": 104, "¬": 105, "®": 106, "¯": 107, "°": 108, "±": 109, "²": 110, "³": 111, "´": 112, "µ": 113, "¶": 114, "·": 115, "¸": 116, "¹": 117, "º": 118, "»": 119, "¼": 120, "½": 121, "¾": 122, "¿": 123, "À": 124, "Á": 125, "Â": 126, "Ã": 127, "Ä": 128, "Å": 129, "Æ": 130, "Ç": 131, "È": 132, "É": 133, "Ê": 134, "Ë": 135, "Ì": 136, "Í": 137, "Î": 138, "Ï": 139, "Ð": 140, "Ñ": 141, "Ò": 142, "Ó": 143, "Ô": 144, "Õ": 145, "Ö": 146, "×": 147, "Ø": 148, "Ù": 149, "Ú": 150, "Û": 151, "Ü": 152, "Ý": 153, "Þ": 154, "ß": 155, "à": 156, "á": 157, "â": 158, "ã": 159, "ä": 160, "å": 161, "æ": 162, "ç": 163, "è": 164, "é": 165, "ê": 166, "ë": 167, "ì": 168, "í": 169, "î": 170, "ï": 171, "ð": 172, "ñ": 173, "ò": 174, "ó": 175, "ô": 176, "õ": 177, "ö": 178, "÷": 179, "ø": 180, "ù": 181, "ú": 182, "û": 183, "ü": 184, "ý": 185, "þ": 186, "ÿ": 187, "Ā": 188, "ā": 189, "Ă": 190, "ă": 191, "Ą": 192, "ą": 193, "Ć": 194, "ć": 195, "Ĉ": 196, "ĉ": 197, "Ċ": 198, "ċ": 199, "Č": 200, "č": 201, "Ď": 202, "ď": 203, "Đ": 204, "đ": 205, "Ē": 206, "ē": 207, "Ĕ": 208, "ĕ": 209, "Ė": 210, "ė": 211, "Ę": 212, "ę": 213, "Ě": 214, "ě": 215, "Ĝ": 216, "ĝ": 217, "Ğ": 218, "ğ": 219, "Ġ": 220, "ġ": 221, "Ģ": 222, "ģ": 223, "Ĥ": 224, "ĥ": 225, "Ħ": 226, "ħ": 227, "Ĩ": 228, "ĩ": 229, "Ī": 230, "ī": 231, "Ĭ": 232, "ĭ": 233, "Į": 234, "į": 235, "İ": 236, "ı": 237, "IJ": 238, "ij": 239, "Ĵ": 240, "ĵ": 241, "Ķ": 242, "ķ": 243, "ĸ": 244, "Ĺ": 245, "ĺ": 246, "Ļ": 247, "ļ": 248, "Ľ": 249, "ľ": 250, "Ŀ": 251, "ŀ": 252, "Ł": 253, "ł": 254, "Ń": 255, "!</w>": 256, "\"</w>": 257, "#</w>": 258, "$</w>": 259, "%</w>": 260, "&</w>": 261, "'</w>": 262, "(</w>": 263, ")</w>": 264, "*</w>": 265, "+</w>": 266, ",</w>": 267, "-</w>": 268, ".</w>": 269, "/</w>": 270, "0</w>": 271, "1</w>": 272, "2</w>": 273, "3</w>": 274, "4</w>": 275, "5</w>": 276, "6</w>": 277, "7</w>": 278, "8</w>": 279, "9</w>": 280, ":</w>": 281, ";</w>": 282, "<</w>": 283, "=</w>": 284, "></w>": 285, "?</w>": 286, "@</w>": 287, "A</w>": 288, "B</w>": 289, "C</w>": 290, "D</w>": 291, "E</w>": 292, "F</w>": 293, "G</w>": 294, "H</w>": 295, "I</w>": 296, "J</w>": 297, "K</w>": 298, "L</w>": 299, "M</w>": 300, "N</w>": 301, "O</w>": 302, "P</w>": 303, "Q</w>": 304, "R</w>": 305, "S</w>": 306, "T</w>": 307, "U</w>": 308, "V</w>": 309, "W</w>": 310, "X</w>": 311, "Y</w>": 312, "Z</w>": 313, "[</w>": 314, "\\</w>": 315, "]</w>": 316, "^</w>": 317, "_</w>": 318, "`</w>": 319, "a</w>": 320, "b</w>": 321, "c</w>": 322, "d</w>": 323, "e</w>": 324, "f</w>": 325, "g</w>": 326, "h</w>": 327, "i</w>": 328, "j</w>": 329, "k</w>": 330, "l</w>": 331, "m</w>": 332, "n</w>": 333, "o</w>": 334, "p</w>": 335, "q</w>": 336, "r</w>": 337, "s</w>": 338, "t</w>": 339, "u</w>": 340, "v</w>": 341, "w</w>": 342, "x</w>": 343, "y</w>": 344, "z</w>": 345, "{</w>": 346, "|</w>": 347, "}</w>": 348, "~</w>": 349, "¡</w>": 350, "¢</w>": 351, "£</w>": 352, "¤</w>": 353, "¥</w>": 354, "¦</w>": 355, "§</w>": 356, "¨</w>": 357, "©</w>": 358, "ª</w>": 359, "«</w>": 360, "¬</w>": 361, "®</w>": 362, "¯</w>": 363, "°</w>": 364, "±</w>": 365, "²</w>": 366, "³</w>": 367, "´</w>": 368, "µ</w>": 369, "¶</w>": 370, "·</w>": 371, "¸</w>": 372, "¹</w>": 373, "º</w>": 374, "»</w>": 375, "¼</w>": 376, "½</w>": 377, "¾</w>": 378, "¿</w>": 379, "À</w>": 380, "Á</w>": 381, "Â</w>": 382, "Ã</w>": 383, "Ä</w>": 384, "Å</w>": 385, "Æ</w>": 386, "Ç</w>": 387, "È</w>": 388, "É</w>": 389, "Ê</w>": 390, "Ë</w>": 391, "Ì</w>": 392, "Í</w>": 393, "Î</w>": 394, "Ï</w>": 395, "Ð</w>": 396, "Ñ</w>": 397, "Ò</w>": 398, "Ó</w>": 399, "Ô</w>": 400, "Õ</w>": 401, "Ö</w>": 402, "×</w>": 403, "Ø</w>": 404, "Ù</w>": 405, "Ú</w>": 406, "Û</w>": 407, "Ü</w>": 408, "Ý</w>": 409, "Þ</w>": 410, "ß</w>": 411, "à</w>": 412, "á</w>": 413, "â</w>": 414, "ã</w>": 415, "ä</w>": 416, "å</w>": 417, "æ</w>": 418, "ç</w>": 419, "è</w>": 420, "Line truncated
|
||||
Reference in new issue
Block a user