Instructions to use vyroAI/ImagineV4.1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use vyroAI/ImagineV4.1 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("vyroAI/ImagineV4.1", torch_dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
| import torch | |
| from typing import Callable, Dict, Optional, Union, List | |
| from urllib.parse import urlparse | |
| from transformers import PreTrainedModel, PreTrainedTokenizer, CLIPTokenizer | |
| import copy | |
| import random | |
| from io import BytesIO | |
| from compel.embeddings_provider import BaseTextualInversionManager | |
| class TextualInversionLoaderMixin(BaseTextualInversionManager): | |
| r""" | |
| Mixin class for adding textual inversion tokens and embeddings to the tokenizer and text encoder with method: | |
| - [`~TextualInversionLoaderMixin.load_textual_inversion_embeddings`] | |
| - [`~TextualInversionLoaderMixin.add_textual_inversion_embedding`] | |
| """ | |
| def load_textual_inversion_embeddings( | |
| self, | |
| embedding_path_dict_or_list: Union[Dict[str, str], List[Dict[str, str]]], | |
| allow_replacement: bool = False, | |
| ): | |
| r""" | |
| Loads textual inversion embeddings and adds them to the tokenizer's vocabulary and the text encoder's embeddings. | |
| Arguments: | |
| embeddings_path_dict_or_list (`Dict[str, str]` or `List[str]`): | |
| Dictionary of token to embedding path or List of embedding paths to embedding dictionaries. | |
| The dictionary must have the following keys: | |
| - `token`: name of the token to be added to the tokenizers' vocabulary | |
| - `embedding`: path to the embedding of the token to be added to the text encoder's embedding matrix | |
| The list must contain paths to embedding dictionaries where the keys are the tokens and the | |
| values are the embeddings (same as above dictionary definition). | |
| allow_replacement (`bool`, *optional*, defaults to `False`): | |
| Whether to allow replacement of existing tokens in the tokenizer's vocabulary. If `False` | |
| and a token is already in the vocabulary, an error will be raised. | |
| Returns: | |
| None | |
| """ | |
| # Validate that inheriting class instance contains required attributes | |
| self._validate_method_call(self.load_textual_inversion_embeddings) | |
| if isinstance(embedding_path_dict_or_list, dict): | |
| for token, embedding_path in embedding_path_dict_or_list.items(): | |
| embedding_dict = torch.load(embedding_path, map_location=self.text_encoder.device) | |
| embedding, is_multi_vec_token = self._extract_embedding_from_dict(embedding_dict) | |
| self._validate_token_update(token, allow_replacement, is_multi_vec_token) | |
| self.add_textual_inversion_embedding(token, embedding) | |
| elif isinstance(embedding_path_dict_or_list, list): | |
| for embedding_path in embedding_path_dict_or_list: | |
| embedding_dict = torch.load(embedding_path, map_location=self.text_encoder.device) | |
| token = self._extract_token_from_dict(embedding_dict) | |
| embedding, is_multi_vec_token = self._extract_embedding_from_dict(embedding_dict) | |
| self._validate_token_update(token, allow_replacement, is_multi_vec_token) | |
| self.add_textual_inversion_embedding(token, embedding) | |
| else: | |
| raise ValueError( | |
| f"Type {type(embedding_path_dict_or_list)} is invalid. The value passed to `embedding_path_dict_or_list` " | |
| "must be a dictionary that maps a token to it's embedding file path " | |
| "or a list of paths to embedding files containing embedding dictionaries." | |
| ) | |
| def add_textual_inversion_embedding(self, token: str, embedding: torch.Tensor): | |
| r""" | |
| Adds a token to the tokenizer's vocabulary and an embedding to the text encoder's embedding matrix. | |
| Arguments: | |
| token (`str`): | |
| The token to be added to the tokenizers' vocabulary | |
| embedding (`torch.Tensor`): | |
| The embedding of the token to be added to the text encoder's embedding matrix | |
| Returns: | |
| None | |
| """ | |
| # NOTE: Not clear to me that we intend for this to be a public/exposed method. | |
| # Validate that inheriting class instance contains required attributes | |
| self._validate_method_call(self.load_textual_inversion_embeddings) | |
| embedding = embedding.to(self.text_encoder.dtype) | |
| if not isinstance(self.tokenizer, MultiTokenCLIPTokenizer): | |
| if token in self.tokenizer.get_vocab(): | |
| # If user has allowed replacement and the token exists, we only need to | |
| # extract the existing id and update the embedding | |
| token_id = self.tokenizer.convert_tokens_to_ids(token) | |
| self.text_encoder.get_input_embeddings().weight.data[token_id] = embedding | |
| else: | |
| # If the token does not exist, we add it to the tokenizer, then resize and update the | |
| # text encoder acccordingly | |
| self.tokenizer.add_tokens([token]) | |
| token_id = self.tokenizer.convert_tokens_to_ids(token) | |
| self.text_encoder.resize_token_embeddings(len(self.tokenizer)) | |
| self.text_encoder.get_input_embeddings().weight.data[token_id] = embedding | |
| else: | |
| if token in self.tokenizer.token_map: | |
| # If user has allowed replacement and the token exists, we need to | |
| # remove all existing tokens associated with the old embbedding and | |
| # upddate with the new ones | |
| indices_to_remove = [] | |
| for token_to_remove in self.tokenizer.token_map[token]: | |
| indices_to_remove.append(self.tokenizer.get_added_vocab()[token_to_remove]) | |
| # Remove old tokens from tokenizer | |
| self.tokenizer.added_tokens_encoder.pop(token_to_remove) | |
| # Convert indices to remove to tensor | |
| indices_to_remove = torch.LongTensor(indices_to_remove) | |
| # Remove old tokens from text encoder | |
| token_embeds = self.text_encoder.get_input_embeddings().weight.data | |
| indices_to_keep = torch.arange(0, token_embeds.shape[0]) | |
| indices_to_keep = indices_to_keep[indices_to_keep != indices_to_remove].squeeze() | |
| token_embeds = token_embeds[indices_to_keep] | |
| # Downsize text encoder | |
| self.text_encoder.resize_token_embeddings(len(self.tokenizer)) | |
| # Remove token from map so MultiTokenCLIPTokenizer doesn't complain | |
| # on update | |
| self.tokenizer.token_map.pop(token) | |
| # Update token with new embedding | |
| embedding_dims = len(embedding.shape) | |
| num_vec_per_token = 1 if embedding_dims == 1 else embedding.shape[0] | |
| self.tokenizer.add_placeholder_tokens(token, num_vec_per_token=num_vec_per_token) | |
| self.text_encoder.resize_token_embeddings(len(self.tokenizer)) | |
| token_ids = self.tokenizer.encode(token, add_special_tokens=False) | |
| if embedding_dims > 1: | |
| for i, token_id in enumerate(token_ids): | |
| self.text_encoder.get_input_embeddings().weight.data[token_id] = embedding[i] | |
| else: | |
| self.text_encoder.get_input_embeddings().weight.data[token_ids] = embedding | |
| def _extract_embedding_from_dict(self, embedding_dict: Dict[str, str]) -> torch.Tensor: | |
| r""" | |
| Extracts the embedding from the embedding dictionary. | |
| Arguments: | |
| embedding_dict (`Dict[str, str]`): | |
| The embedding dictionary loaded from the embedding path | |
| Returns: | |
| embedding (`torch.Tensor`): | |
| The embedding to be added to the text encoder's embedding matrix | |
| is_multi_vec_token (`bool`): | |
| Whether the embedding is a multi-vector token or not | |
| """ | |
| is_multi_vec_token = False | |
| # auto1111 embedding case | |
| if "string_to_param" in embedding_dict: | |
| embedding_dict = embedding_dict["string_to_param"] | |
| embedding = embedding_dict["*"] | |
| else: | |
| embedding = list(embedding_dict.values())[0] | |
| if len(embedding.shape) > 1: | |
| # If the embedding has more than one dimension, | |
| # We need to ensure the tokenizer is a MultiTokenTokenizer | |
| # because there is branching logic that depends on that class | |
| if not isinstance(self.tokenizer, MultiTokenCLIPTokenizer): | |
| raise ValueError( | |
| f"{self.__class__.__name__} requires `self.tokenizer` of type `MultiTokenCLIPTokenizer` for loading embeddings with more than one dimension." | |
| ) | |
| is_multi_vec_token = True | |
| return embedding, is_multi_vec_token | |
| def _extract_token_from_dict(self, embedding_dict: Dict[str, str]) -> str: | |
| r""" | |
| Extracts the token from the embedding dictionary. | |
| Arguments: | |
| embedding_dict (`Dict[str, str]`): | |
| The embedding dictionary loaded from the embedding path | |
| Returns: | |
| token (`str`): | |
| The token to be added to the tokenizers' vocabulary | |
| """ | |
| # auto1111 embedding case | |
| if "string_to_param" in embedding_dict: | |
| token = embedding_dict["name"] | |
| return token | |
| return list(embedding_dict.keys())[0] | |
| def _validate_method_call(self, method: Callable): | |
| r""" | |
| Validates that the method is being called from a class instance that has the required attributes. | |
| Arguments: | |
| method (`function`): | |
| The class's method being called | |
| Raises: | |
| ValueError: | |
| If the method is being called from a class instance that does not have | |
| the required attributes, the method will not be callable. | |
| Returns: | |
| None | |
| """ | |
| if not hasattr(self, "tokenizer") or not isinstance(self.tokenizer, PreTrainedTokenizer): | |
| raise ValueError( | |
| f"{self.__class__.__name__} requires `self.tokenizer` of type `PreTrainedTokenizer` for calling `{method.__name__}`" | |
| ) | |
| if not hasattr(self, "text_encoder") or not isinstance(self.text_encoder, PreTrainedModel): | |
| raise ValueError( | |
| f"{self.__class__.__name__} requires `self.text_encoder` of type `PreTrainedModel` for calling `{method.__name__}`" | |
| ) | |
| def _validate_token_update(self, token, allow_replacement=False, is_multi_vec_token=False): | |
| r"""Validates that the token is not already in the tokenizer's vocabulary. | |
| Arguments: | |
| token (`str`): | |
| The token to be added to the tokenizers' vocabulary | |
| allow_replacement (`bool`): | |
| Whether to allow replacement of the token if it already exists in the tokenizer's vocabulary | |
| is_multi_vec_token (`bool`): | |
| Whether the embedding is a multi-vector token or not | |
| Raises: | |
| ValueError: | |
| If the token is already in the tokenizer's vocabulary and `allow_replacement` is False. | |
| Returns: | |
| None | |
| """ | |
| if (not is_multi_vec_token and token in self.tokenizer.get_vocab()) or ( | |
| is_multi_vec_token and token in self.tokenizer.token_map | |
| ): | |
| if allow_replacement: | |
| print( | |
| f"Token {token} already in tokenizer vocabulary. Overwriting existing token and embedding with the new one." | |
| ) | |
| else: | |
| raise ValueError( | |
| f"Token {token} already in tokenizer vocabulary. Please choose a different token name." | |
| ) | |
| def expand_textual_inversion_token_ids_if_necessary(self, token_ids: List[int]) -> List[int]: | |
| pass | |
| class MultiTokenCLIPTokenizer(CLIPTokenizer): | |
| """Tokenizer for CLIP models that have multi-vector tokens.""" | |
| def __init__(self, *args, **kwargs): | |
| super().__init__(*args, **kwargs) | |
| self.token_map = {} | |
| def add_placeholder_tokens(self, placeholder_token, *args, num_vec_per_token=1, **kwargs): | |
| r"""Adds placeholder tokens to the tokenizer's vocabulary. | |
| Arguments: | |
| placeholder_token (`str`): | |
| The placeholder token to be added to the tokenizers' vocabulary and token map. | |
| num_vec_per_token (`int`): | |
| The number of vectors per token. Defaults to 1. | |
| *args: | |
| The arguments to be passed to the tokenizer's `add_tokens` method. | |
| **kwargs: | |
| The keyword arguments to be passed to the tokenizer's `add_tokens` method. | |
| Returns: | |
| None | |
| """ | |
| output = [] | |
| if num_vec_per_token == 1: | |
| self.add_tokens(placeholder_token, *args, **kwargs) | |
| output.append(placeholder_token) | |
| else: | |
| output = [] | |
| for i in range(num_vec_per_token): | |
| ith_token = placeholder_token + f"_{i}" | |
| self.add_tokens(ith_token, *args, **kwargs) | |
| output.append(ith_token) | |
| # handle cases where there is a new placeholder token that contains the current placeholder token but is larger | |
| for token in self.token_map: | |
| if token in placeholder_token: | |
| raise ValueError( | |
| f"The tokenizer already has placeholder token {token} that can get confused with" | |
| f" {placeholder_token}keep placeholder tokens independent" | |
| ) | |
| self.token_map[placeholder_token] = output | |
| def replace_placeholder_tokens_in_text(self, text, vector_shuffle=False, prop_tokens_to_load=1.0): | |
| r"""Replaces placeholder tokens in text with the tokens in the token map. | |
| Opttionally, implements: | |
| a) vector shuffling (https://github.com/rinongal/textual_inversion/pull/119)where | |
| shuffling tokens were found to force the model to learn the concepts more descriptively. | |
| b) proportional token loading so that not every token in the token map is loaded on each call; | |
| used as part of progressive token loading during training which can improve generalization | |
| during inference. | |
| Arguments: | |
| text (`str`): | |
| The text to be processed. | |
| vector_shuffle (`bool`): | |
| Whether to shuffle the vectors in the token map. Defaults to False. | |
| prop_tokens_to_load (`float`): | |
| The proportion of tokens to load from the token map. Defaults to 1.0. | |
| Returns: | |
| `str`: The processed text. | |
| """ | |
| if isinstance(text, list): | |
| output = [] | |
| for i in range(len(text)): | |
| output.append(self.replace_placeholder_tokens_in_text(text[i], vector_shuffle=vector_shuffle)) | |
| return output | |
| for placeholder_token in self.token_map: | |
| if placeholder_token in text: | |
| tokens = self.token_map[placeholder_token] | |
| tokens = tokens[: 1 + int(len(tokens) * prop_tokens_to_load)] | |
| if vector_shuffle: | |
| tokens = copy.copy(tokens) | |
| random.shuffle(tokens) | |
| text = text.replace(placeholder_token, " ".join(tokens)) | |
| return text | |
| def __call__(self, text, *args, vector_shuffle=False, prop_tokens_to_load=1.0, **kwargs): | |
| """Wrapper around [`~transformers.tokenization_utils.PreTrainedTokenizerBase.__call__`] method | |
| but first replace placeholder tokens in text with the tokens in the token map. | |
| Returns: | |
| [`~transformers.tokenization_utils_base.BatchEncoding`] | |
| """ | |
| return super().__call__( | |
| self.replace_placeholder_tokens_in_text( | |
| text, | |
| vector_shuffle=vector_shuffle, | |
| prop_tokens_to_load=prop_tokens_to_load, | |
| ), | |
| *args, | |
| **kwargs, | |
| ) | |
| def encode(self, text, *args, vector_shuffle=False, prop_tokens_to_load=1.0, **kwargs): | |
| """Wrapper around the tokenizer's [`transformers.tokenization_utils.PreTrainedTokenizerBase.encode`] method | |
| but first replaces placeholder tokens in text with the tokens in the token map. | |
| Arguments: | |
| text (`str`): | |
| The text to be encoded. | |
| *args: | |
| The arguments to be passed to the tokenizer's `encode` method. | |
| vector_shuffle (`bool`): | |
| Whether to shuffle the vectors in the token map. Defaults to False. | |
| prop_tokens_to_load (`float`): | |
| The proportion of tokens to load from the token map. Defaults to 1.0. | |
| **kwargs: | |
| The keyword arguments to be passed to the tokenizer's `encode` method. | |
| Returns: | |
| List[`int`]: sequence of ids (integer) | |
| """ | |
| return super().encode( | |
| self.replace_placeholder_tokens_in_text( | |
| text, | |
| vector_shuffle=vector_shuffle, | |
| prop_tokens_to_load=prop_tokens_to_load, | |
| ), | |
| *args, | |
| **kwargs, | |
| ) | |