-
Notifications
You must be signed in to change notification settings - Fork 4
/
t5_encoder.py
143 lines (118 loc) · 4.61 KB
/
t5_encoder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
import torch
import comfy.clip_model
import comfy.model_base
import comfy.model_management
import comfy.sd1_clip
import comfy.supported_models_base
import comfy.utils
import comfy.model_patcher
import comfy.sd
import comfy.latent_formats
import folder_paths
from pathlib import Path
from .nodes_registry import comfy_node
from transformers import T5EncoderModel, T5Tokenizer
class LTXVTokenizer(comfy.sd1_clip.SDTokenizer):
def __init__(self, tokenizer_path: str):
self.tokenizer = T5Tokenizer.from_pretrained(
tokenizer_path, local_files_only=True
)
def tokenize_with_weights(self, text: str, return_word_ids: bool = False):
"""
Takes a prompt and converts it to a list of (token, weight, word id) elements.
Tokens can both be integer tokens and pre computed CLIP tensors.
Word id values are unique per word and embedding, where the id 0 is reserved for non word tokens.
Returned list has the dimensions NxM where M is the input size of CLIP
"""
text = text.lower().strip()
text_inputs = self.tokenizer(
text,
padding="max_length", # do_not_pad, longest, max_length
max_length=128,
truncation=True,
add_special_tokens=True,
return_tensors="pt",
)
text_input_ids = text_inputs.input_ids
prompt_attention_mask = text_inputs.attention_mask
out = {
"t5xxl": [
(token, weight, i)
for i, (token, weight) in enumerate(
zip(text_input_ids[0], prompt_attention_mask[0])
)
]
}
if not return_word_ids:
out = {k: [(t, w) for t, w, _ in v] for k, v in out.items()}
return out
class LTXVTextEncoderModel(torch.nn.Module):
def __init__(
self, encoder_path, dtype_t5=None, device="cpu", dtype=None, model_options={}
):
super().__init__()
dtype_t5 = comfy.model_management.pick_weight_dtype(dtype_t5, dtype, device)
self.t5xxl = (
T5EncoderModel.from_pretrained(encoder_path, local_files_only=True)
.to(dtype_t5)
.to(device)
)
self.dtypes = set([dtype, dtype_t5])
def set_clip_options(self, options):
pass
def reset_clip_options(self):
pass
def encode_token_weights(self, token_weight_pairs):
token_weight_pairs_t5 = token_weight_pairs["t5xxl"]
text_input_ids = torch.tensor(
[[t[0] for t in token_weight_pairs_t5]],
device=self.t5xxl.device,
)
prompt_attention_mask = torch.tensor(
[[w[1] for w in token_weight_pairs_t5]],
device=self.t5xxl.device,
)
out = self.t5xxl(text_input_ids, attention_mask=prompt_attention_mask)[0]
out = out * prompt_attention_mask.unsqueeze(2)
return out, None, {"attention_mask": prompt_attention_mask}
def load_sd(self, sd):
return self.t5xxl.load_state_dict(sd, strict=False)
def ltxv_clip(encoder_path, dtype_t5=None):
class LTXVTextEncoderModel_(LTXVTextEncoderModel):
def __init__(self, device="cpu", dtype=None, model_options={}):
super().__init__(
encoder_path=encoder_path,
dtype_t5=dtype_t5,
device=device,
dtype=dtype,
model_options=model_options,
)
return LTXVTextEncoderModel_
def ltxv_tokenizer(tokenizer_path):
class LTXVTokenizer_(LTXVTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
super().__init__(tokenizer_path)
return LTXVTokenizer_
@comfy_node(name="LTXVCLIPModelLoader", description="LTXV CLIP Model Loader")
class LTXVCLIPModelLoader:
@classmethod
def INPUT_TYPES(s):
return {"required": {"clip_path": (
folder_paths.get_filename_list("text_encoders"),
{"tooltip": "The name of the text encoder model to load."},
)}}
RETURN_TYPES = ("CLIP",)
RETURN_NAMES = ("clip",)
FUNCTION = "load_model"
CATEGORY = "lightricks/LTXV"
TITLE = "LTXV Model Loader"
OUTPUT_NODE = False
def load_model(self, clip_path):
path = Path(folder_paths.get_full_path("text_encoders", clip_path))
tokenizer_path = path.parents[1] / "tokenizer"
encoder_path = path.parents[1] / "text_encoder"
clip_target = comfy.supported_models_base.ClipTarget(
tokenizer=ltxv_tokenizer(tokenizer_path),
clip=ltxv_clip(encoder_path, dtype_t5=torch.bfloat16),
)
return (comfy.sd.CLIP(clip_target),)