deltpy 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- delt/__init__.py +31 -0
- delt/common/__init__.py +21 -0
- delt/common/experimental.py +46 -0
- delt/common/io.py +24 -0
- delt/common/iterables.py +67 -0
- delt/common/llm.py +94 -0
- delt/common/logging.py +81 -0
- delt/common/prompting.py +41 -0
- delt/common/utils.py +1 -0
- delt/encoders/__init__.py +52 -0
- delt/encoders/audio/__init__.py +6 -0
- delt/encoders/audio/base.py +129 -0
- delt/encoders/audio/clap.py +144 -0
- delt/encoders/audio/glap.py +155 -0
- delt/encoders/audio/utils.py +36 -0
- delt/encoders/base.py +156 -0
- delt/encoders/image/__init__.py +6 -0
- delt/encoders/image/base.py +129 -0
- delt/encoders/image/clip.py +133 -0
- delt/encoders/image/siglip.py +133 -0
- delt/encoders/text/__init__.py +5 -0
- delt/encoders/text/base.py +128 -0
- delt/encoders/text/sentence_transformer.py +71 -0
- delt/encoders/video/__init__.py +5 -0
- delt/encoders/video/base.py +132 -0
- delt/encoders/video/utils.py +115 -0
- delt/encoders/video/xclip.py +168 -0
- delt/examples/hard_distillation/audio.py +51 -0
- delt/examples/hard_distillation/image.py +51 -0
- delt/examples/hard_distillation/text.py +43 -0
- delt/examples/hard_distillation/video.py +77 -0
- delt/examples/label_tuning.py +39 -0
- delt/examples/pipelines/audio.py +35 -0
- delt/examples/pipelines/image.py +33 -0
- delt/examples/pipelines/text.py +28 -0
- delt/examples/pipelines/video.py +63 -0
- delt/examples/soft_distillation/text.py +47 -0
- delt/pipelines/__init__.py +15 -0
- delt/pipelines/audio.py +31 -0
- delt/pipelines/base.py +134 -0
- delt/pipelines/image.py +31 -0
- delt/pipelines/text.py +31 -0
- delt/pipelines/video.py +31 -0
- delt/predict.py +69 -0
- delt/teachers/__init__.py +13 -0
- delt/teachers/audio/__init__.py +5 -0
- delt/teachers/audio/lmm.py +94 -0
- delt/teachers/base.py +58 -0
- delt/teachers/image/__init__.py +5 -0
- delt/teachers/image/lmm.py +95 -0
- delt/teachers/text/__init__.py +5 -0
- delt/teachers/text/llm.py +68 -0
- delt/teachers/video/__init__.py +5 -0
- delt/teachers/video/lmm.py +107 -0
- delt/train.py +236 -0
- delt/types.py +34 -0
- delt/ui/__init__.py +1 -0
- delt/ui/app.py +663 -0
- delt/version.py +12 -0
- deltpy-1.4.0.dist-info/METADATA +685 -0
- deltpy-1.4.0.dist-info/RECORD +64 -0
- deltpy-1.4.0.dist-info/WHEEL +5 -0
- deltpy-1.4.0.dist-info/licenses/LICENSE +402 -0
- deltpy-1.4.0.dist-info/top_level.txt +1 -0
delt/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Main package of delt."""
|
|
2
|
+
|
|
3
|
+
from .encoders import (
|
|
4
|
+
ClapEncoder,
|
|
5
|
+
ClipEncoder,
|
|
6
|
+
SentenceTransformerEncoder,
|
|
7
|
+
SiglipEncoder,
|
|
8
|
+
XclipEncoder,
|
|
9
|
+
get_encoder,
|
|
10
|
+
)
|
|
11
|
+
from .pipelines import (
|
|
12
|
+
AudioPipeline,
|
|
13
|
+
ImagePipeline,
|
|
14
|
+
TextPipeline,
|
|
15
|
+
VideoPipeline,
|
|
16
|
+
)
|
|
17
|
+
from .teachers import LLMTextTeacher
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"SentenceTransformerEncoder",
|
|
21
|
+
"ClipEncoder",
|
|
22
|
+
"SiglipEncoder",
|
|
23
|
+
"ClapEncoder",
|
|
24
|
+
"XclipEncoder",
|
|
25
|
+
"get_encoder",
|
|
26
|
+
"TextPipeline",
|
|
27
|
+
"ImagePipeline",
|
|
28
|
+
"AudioPipeline",
|
|
29
|
+
"VideoPipeline",
|
|
30
|
+
"LLMTextTeacher",
|
|
31
|
+
]
|
delt/common/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Subpackage for common utils."""
|
|
2
|
+
|
|
3
|
+
from .experimental import unimodal_kfold
|
|
4
|
+
from .io import base64_encode
|
|
5
|
+
from .iterables import batchify, batchify_tensor, dict_cartesian_product
|
|
6
|
+
from .llm import generate_completion, generate_completions
|
|
7
|
+
from .logging import get_logger
|
|
8
|
+
from .prompting import format_prompt, make_output_model
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"get_logger",
|
|
12
|
+
"format_prompt",
|
|
13
|
+
"unimodal_kfold",
|
|
14
|
+
"dict_cartesian_product",
|
|
15
|
+
"batchify",
|
|
16
|
+
"batchify_tensor",
|
|
17
|
+
"generate_completion",
|
|
18
|
+
"generate_completions",
|
|
19
|
+
"base64_encode",
|
|
20
|
+
"make_output_model",
|
|
21
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Module for experimental utils."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
from sklearn.model_selection import StratifiedKFold
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def unimodal_kfold(
|
|
10
|
+
data: list[Any],
|
|
11
|
+
labels: list[str],
|
|
12
|
+
n_splits: int = 4,
|
|
13
|
+
random_state: int = 42,
|
|
14
|
+
) -> tuple[list[dict], list[dict]]:
|
|
15
|
+
"""
|
|
16
|
+
Create k folds for unimodal settings.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
data (list[Any]): A list of input data samples.
|
|
20
|
+
labels (list[str]): A list of labels corresponding to the input data samples.
|
|
21
|
+
n_splits (int): The number of folds for cross-validation.
|
|
22
|
+
random_state (int): The random seed for reproducibility.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
tuple[list[dict], list[dict]]: A tuple containing two lists of dictionaries for training and validation splits.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
kfold = StratifiedKFold(
|
|
29
|
+
n_splits=n_splits, shuffle=True, random_state=random_state
|
|
30
|
+
)
|
|
31
|
+
splits = kfold.split(data, labels)
|
|
32
|
+
train_splits, val_splits = [], []
|
|
33
|
+
for train_idxs, val_idxs in splits:
|
|
34
|
+
if isinstance(data, torch.Tensor):
|
|
35
|
+
train_data = data[train_idxs]
|
|
36
|
+
val_data = data[val_idxs]
|
|
37
|
+
else:
|
|
38
|
+
train_data = [data[i] for i in train_idxs]
|
|
39
|
+
val_data = [data[i] for i in val_idxs]
|
|
40
|
+
|
|
41
|
+
train_labels = [labels[i] for i in train_idxs]
|
|
42
|
+
val_labels = [labels[i] for i in val_idxs]
|
|
43
|
+
|
|
44
|
+
train_splits.append({"x": train_data, "y": train_labels})
|
|
45
|
+
val_splits.append({"x": val_data, "y": val_labels})
|
|
46
|
+
return train_splits, val_splits
|
delt/common/io.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Module for io utils."""
|
|
2
|
+
|
|
3
|
+
from base64 import b64encode
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def base64_encode(data: bytes, mime_type: Optional[str] = None) -> str:
|
|
8
|
+
"""
|
|
9
|
+
Encode a `bytes` object into base64.
|
|
10
|
+
|
|
11
|
+
If `mime_type` is provided, a Data URI is returned with the mime type in the header.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
data (bytes): bytes to encode.
|
|
15
|
+
mime_type (Optional[str]): mime type of the data.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: a base64 stream in Data URI format if `mime_type` is provided.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
b64 = b64encode(data).decode("utf-8")
|
|
22
|
+
if mime_type:
|
|
23
|
+
return f"data:{mime_type};base64,{b64}"
|
|
24
|
+
return b64
|
delt/common/iterables.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Module for iterables."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable, Iterator
|
|
4
|
+
from itertools import islice, product
|
|
5
|
+
from typing import TypeVar
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def batchify(iterable: Iterable[T], batch_size: int) -> Iterator[list[T]]:
|
|
13
|
+
"""
|
|
14
|
+
Create batches of `batch_size` from a generic `iterable`.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
iterable (Iterable[T]): generic iterable to get batches from.
|
|
18
|
+
batch_size (int): the batch size.
|
|
19
|
+
|
|
20
|
+
Yields:
|
|
21
|
+
list[T]: a batch of elements as a list.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
if batch_size <= 0:
|
|
25
|
+
raise ValueError("batch_size must be positive")
|
|
26
|
+
|
|
27
|
+
iterator = iter(iterable)
|
|
28
|
+
|
|
29
|
+
while batch := list(islice(iterator, batch_size)):
|
|
30
|
+
yield batch
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def batchify_tensor(
|
|
34
|
+
x: torch.Tensor, batch_size: int
|
|
35
|
+
) -> Iterator[torch.Tensor]:
|
|
36
|
+
"""
|
|
37
|
+
Create batches of `batch_size` from a torch tensor with shape (N, ...).
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
x (torch.Tensor): the torch tensor.
|
|
41
|
+
batch_size (int): the batch size.
|
|
42
|
+
|
|
43
|
+
Yields:
|
|
44
|
+
torch.Tensor: a batch of elements as a torch tensor with shape (`batch_size`, ...)
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
if batch_size <= 0:
|
|
48
|
+
raise ValueError("batch_size must be positive")
|
|
49
|
+
|
|
50
|
+
for i in range(0, len(x), batch_size):
|
|
51
|
+
yield x[i : i + batch_size]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def dict_cartesian_product(d: dict) -> list[dict]:
|
|
55
|
+
"""
|
|
56
|
+
Get the cartesian product over the fields in a dictionary.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
d (dict): a dictionary.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
list[dict]: list of dictionaries representing the cartesian product.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
keys = d.keys()
|
|
66
|
+
values = d.values()
|
|
67
|
+
return [dict(zip(keys, combination)) for combination in product(*values)]
|
delt/common/llm.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Module for litellm utils."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
|
|
6
|
+
import litellm
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from tqdm import tqdm
|
|
9
|
+
|
|
10
|
+
from .logging import get_logger
|
|
11
|
+
|
|
12
|
+
_logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_completion(
|
|
16
|
+
model_name: str,
|
|
17
|
+
conversation: list[dict],
|
|
18
|
+
response_format: BaseModel,
|
|
19
|
+
decoding_args: dict = {},
|
|
20
|
+
extra_body: dict = {},
|
|
21
|
+
) -> BaseModel:
|
|
22
|
+
"""
|
|
23
|
+
Generate a completion using the specified model for a conversation.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
model_name (str): name of the model.
|
|
27
|
+
conversation (list[dict]): message thread.
|
|
28
|
+
response_format (BaseModel): pydantic model for response formatting.
|
|
29
|
+
decoding_args (dict): additional arguments for decoding.
|
|
30
|
+
extra_body (dict): extra body to send to the model.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
BaseModel: a completion output.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
response = litellm.completion(
|
|
37
|
+
model=model_name,
|
|
38
|
+
messages=conversation,
|
|
39
|
+
response_format=response_format,
|
|
40
|
+
extra_body=extra_body,
|
|
41
|
+
**decoding_args,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if response.choices[0].finish_reason != "stop":
|
|
45
|
+
raise RuntimeError(
|
|
46
|
+
f"Completion did not finish properly: "
|
|
47
|
+
f"{response.choices[0].finish_reason}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
json_response = json.loads(response.choices[0].message.content)
|
|
51
|
+
return response_format(**json_response)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def generate_completions(
|
|
55
|
+
model_name: str,
|
|
56
|
+
conversations: list[list[dict]],
|
|
57
|
+
response_format: BaseModel,
|
|
58
|
+
decoding_args: dict = {},
|
|
59
|
+
batch_size: int = 4,
|
|
60
|
+
extra_body: list[dict] = [],
|
|
61
|
+
) -> list[BaseModel]:
|
|
62
|
+
"""
|
|
63
|
+
Generate completions using the specified model and conversations.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
model_name (str): name of the model.
|
|
67
|
+
conversations (list[list[dict]]): a list of conversations.
|
|
68
|
+
response_format (BaseModel): pydantic model for response formatting.
|
|
69
|
+
decoding_args (dict): additional arguments for decoding.
|
|
70
|
+
batch_size (int): number of concurrent requests.
|
|
71
|
+
extra_body (list[dict]): extra bodies to send to the model.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
list[BaseModel]: completion outputs.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
completions, responses = [], []
|
|
78
|
+
with ThreadPoolExecutor(
|
|
79
|
+
max_workers=min(batch_size, len(conversations))
|
|
80
|
+
) as thread_pool:
|
|
81
|
+
for i, message in enumerate(conversations):
|
|
82
|
+
responses.append(
|
|
83
|
+
thread_pool.submit(
|
|
84
|
+
generate_completion,
|
|
85
|
+
model_name,
|
|
86
|
+
message,
|
|
87
|
+
response_format,
|
|
88
|
+
decoding_args,
|
|
89
|
+
extra_body[i] if extra_body else None,
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
completions = [response.result() for response in tqdm(responses)]
|
|
93
|
+
|
|
94
|
+
return completions
|
delt/common/logging.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Module for logging utils."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
_time = datetime.now()
|
|
9
|
+
|
|
10
|
+
COLORS = {
|
|
11
|
+
"grey": "\x1b[38;20m",
|
|
12
|
+
"yellow": "\x1b[33;20m",
|
|
13
|
+
"bold_yellow": "\x1b[33;1m",
|
|
14
|
+
"red": "\x1b[31;20m",
|
|
15
|
+
"bold_red": "\x1b[31;1m",
|
|
16
|
+
"green": "\x1b[32;20m",
|
|
17
|
+
"bold_green": "\x1b[32;1m",
|
|
18
|
+
"blue": "\x1b[34;20m",
|
|
19
|
+
"bold_blue": "\x1b[34;1m",
|
|
20
|
+
"reset": "\x1b[0m",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def color_log(text: str, color: str) -> str:
|
|
25
|
+
"""
|
|
26
|
+
Add color to a log text.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
text (str): a text.
|
|
30
|
+
color (str): a color in `COLORS`.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
str: a text with color codes added.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
return COLORS[color] + text + COLORS["reset"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_logger(
|
|
40
|
+
module_name: str,
|
|
41
|
+
) -> logging.Logger:
|
|
42
|
+
"""
|
|
43
|
+
Return the logger used across modules.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
module_name (str): name of the module.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
logging.Logger: the logger.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
logger = logging.getLogger(module_name)
|
|
53
|
+
logger.setLevel(logging.INFO)
|
|
54
|
+
|
|
55
|
+
formatter = logging.Formatter(
|
|
56
|
+
"[%(asctime)s] - %(levelname)s - %(message)s",
|
|
57
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
logfile = (
|
|
61
|
+
Path("logs")
|
|
62
|
+
.joinpath(
|
|
63
|
+
_time.strftime("%Y_%m_%d"),
|
|
64
|
+
_time.strftime("%H_%M_%S"),
|
|
65
|
+
"project.log",
|
|
66
|
+
)
|
|
67
|
+
.absolute()
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
logfile.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
|
|
72
|
+
fh = logging.FileHandler(logfile)
|
|
73
|
+
fh.setFormatter(formatter)
|
|
74
|
+
|
|
75
|
+
sh = logging.StreamHandler(sys.stdout)
|
|
76
|
+
sh.setFormatter(formatter)
|
|
77
|
+
|
|
78
|
+
logger.addHandler(sh)
|
|
79
|
+
logger.addHandler(fh)
|
|
80
|
+
|
|
81
|
+
return logger
|
delt/common/prompting.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Module for prompting utils."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, create_model
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_prompt(
|
|
9
|
+
label_verbalizations: dict[str, str], prompt_template: str
|
|
10
|
+
) -> list[str]:
|
|
11
|
+
"""
|
|
12
|
+
Format a prompt template with label verbalizations.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
label_verbalizations (dict[str, str]): verbalizations of the labels, e.g. {"positive": "very cool!", "negative": "horrible"}
|
|
16
|
+
prompt_template (str): template to format label verbalizations, e.g., "This text is {}" being instantiated as "This text is very cool!".
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
list[str]: instantiated prompts, one for each verbalization in `label_verbalizations`.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
return [
|
|
23
|
+
prompt_template.format(verbalization)
|
|
24
|
+
for verbalization in label_verbalizations.values()
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def make_output_model(label_set: list[str]) -> type[BaseModel]:
|
|
29
|
+
"""
|
|
30
|
+
Create a pydantic model dynamically to be used as structured output in classification tasks.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
label_set (list[str]): list of label names.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
type[BaseModel]: a pydantic model.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
return create_model(
|
|
40
|
+
"Output", label=(Literal.__getitem__(tuple(label_set)), ...)
|
|
41
|
+
)
|
delt/common/utils.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Module for common utils."""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Package for encoders."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .audio import ClapEncoder, GlapEncoder
|
|
6
|
+
from .base import Encoder
|
|
7
|
+
from .image import ClipEncoder, SiglipEncoder
|
|
8
|
+
from .text import SentenceTransformerEncoder
|
|
9
|
+
from .video import XclipEncoder
|
|
10
|
+
|
|
11
|
+
REGISTRY: dict[str, type[Encoder]] = {
|
|
12
|
+
"glap": GlapEncoder,
|
|
13
|
+
"clap": ClapEncoder,
|
|
14
|
+
"clip": ClipEncoder,
|
|
15
|
+
"siglip": SiglipEncoder,
|
|
16
|
+
"sentence-transformer": SentenceTransformerEncoder,
|
|
17
|
+
"xclip": XclipEncoder,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_encoder(class_name: str, *args: Any, **kwargs: Any) -> Encoder:
|
|
22
|
+
"""
|
|
23
|
+
Instantiate an encoder by class name, given arbitrary arguments.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
class_name (str): class name of the encoder. It should be available in `REGISTRY`.
|
|
27
|
+
*args: positional arguments passed directly to the encoder's constructor.
|
|
28
|
+
**kwargs: keyword arguments passed directly to the encoder's constructor.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Encoder: an instantiated encoder.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
if class_name not in REGISTRY:
|
|
35
|
+
raise KeyError(
|
|
36
|
+
f"Unknown encoder class: {class_name}. Available: {list(REGISTRY.keys())}"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
encoder_cls = REGISTRY[class_name]
|
|
40
|
+
return encoder_cls(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"Encoder",
|
|
45
|
+
"SentenceTransformerEncoder",
|
|
46
|
+
"ClipEncoder",
|
|
47
|
+
"GlapEncoder",
|
|
48
|
+
"SiglipEncoder",
|
|
49
|
+
"ClapEncoder",
|
|
50
|
+
"XclipEncoder",
|
|
51
|
+
"get_encoder",
|
|
52
|
+
]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Module for the base audio encoder."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
from transformers import (
|
|
7
|
+
AutoProcessor,
|
|
8
|
+
PreTrainedModel,
|
|
9
|
+
PreTrainedTokenizerBase,
|
|
10
|
+
ProcessorMixin,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from delt.encoders.base import Encoder
|
|
14
|
+
from delt.types import Audio, Image, Video
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AudioEncoder(Encoder):
|
|
18
|
+
"""Base audio encoder model."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
encoder_name: str,
|
|
23
|
+
normalize_embeddings: bool = True,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Initialize a encoder model.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
encoder_name (str): name of sentence-transformer-compatible model.
|
|
30
|
+
normalize_embeddings (bool): whether to apply L2 normalization to all embeddings.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
super().__init__(
|
|
34
|
+
encoder_name=encoder_name,
|
|
35
|
+
normalize_embeddings=normalize_embeddings,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def processor(self) -> PreTrainedTokenizerBase | ProcessorMixin | None:
|
|
40
|
+
"""
|
|
41
|
+
Processor tied to an audio encoder.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
PreTrainedTokenizerBase | ProcessorMixin | None: tokenizers for text
|
|
45
|
+
models, processor mixin for image/audio/video, and `None` for
|
|
46
|
+
models outside the HuggingFace ecosystem.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
if self._processor is None:
|
|
50
|
+
self._processor = AutoProcessor.from_pretrained(self.encoder_name)
|
|
51
|
+
return self._processor
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def encoder(self) -> PreTrainedModel:
|
|
56
|
+
"""
|
|
57
|
+
A pretrained audio encoder model.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
PreTrainedModel: the pretrained audio encoder.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def get_text_embeddings(
|
|
67
|
+
self, texts: list[str], batch_size: int = 64
|
|
68
|
+
) -> torch.Tensor:
|
|
69
|
+
"""
|
|
70
|
+
Embed a list of texts using the base encoder model.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
texts (list[str]): a list of texts.
|
|
74
|
+
batch_size (int): batch size for inference.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
torch.Tensor: The text embeddings.
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
def get_image_embeddings(
|
|
83
|
+
self, images: list[Image], batch_size: int = 8
|
|
84
|
+
) -> torch.Tensor:
|
|
85
|
+
"""
|
|
86
|
+
Embed a list of images using the base encoder model.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
images (list[Image): list of images.
|
|
90
|
+
batch_size (int): batch size for inference.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
torch.Tensor: The image embeddings.
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def get_audio_embeddings(
|
|
100
|
+
self, audios: list[Audio], batch_size: int = 16
|
|
101
|
+
) -> torch.Tensor:
|
|
102
|
+
"""
|
|
103
|
+
Embed a list of audios using the base encoder model.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
audios (list[Audio]): list of audios.
|
|
107
|
+
batch_size (int): batch size for inference.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
torch.Tensor: The audio embeddings.
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
def get_video_embeddings(
|
|
116
|
+
self, videos: list[Video], batch_size: int = 16
|
|
117
|
+
) -> torch.Tensor:
|
|
118
|
+
"""
|
|
119
|
+
Embed a list of videos using the base encoder model.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
videos (list[Video]): list of videos.
|
|
123
|
+
batch_size (int): batch size for inference.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
torch.Tensor: The video embeddings.
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
...
|