kernel-elastic-autoencoder 2.0.0__tar.gz → 3.0.0__tar.gz
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.
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/PKG-INFO +2 -2
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/pyproject.toml +2 -2
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/src/kernel_elastic_autoencoder/__init__.py +2 -11
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/src/kernel_elastic_autoencoder/model.py +9 -2
- kernel_elastic_autoencoder-3.0.0/src/kernel_elastic_autoencoder/pipeline.py +161 -0
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/src/kernel_elastic_autoencoder/tokenizer.py +0 -68
- kernel_elastic_autoencoder-3.0.0/src/kernel_elastic_autoencoder/training.py +153 -0
- kernel_elastic_autoencoder-2.0.0/src/kernel_elastic_autoencoder/collate.py +0 -164
- kernel_elastic_autoencoder-2.0.0/src/kernel_elastic_autoencoder/pipeline.py +0 -203
- kernel_elastic_autoencoder-2.0.0/src/kernel_elastic_autoencoder/sample.py +0 -70
- kernel_elastic_autoencoder-2.0.0/src/kernel_elastic_autoencoder/training.py +0 -386
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/README.md +0 -0
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/src/kernel_elastic_autoencoder/config.py +0 -0
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/src/kernel_elastic_autoencoder/layers.py +0 -0
- {kernel_elastic_autoencoder-2.0.0 → kernel_elastic_autoencoder-3.0.0}/src/kernel_elastic_autoencoder/losses.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kernel_elastic_autoencoder
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Implementation of Kernel-Elastic Autoencoder for Molecular Design (https://doi.org/10.1093/pnasnexus/pgae168)
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Felix Rotter-McCartney
|
|
@@ -11,8 +11,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: accelerate (>=1.14.0,<2.0.0)
|
|
14
15
|
Requires-Dist: huggingface-hub (>=1.22.0,<2.0.0)
|
|
15
|
-
Requires-Dist: pandas (>=3.0.3,<4.0.0)
|
|
16
16
|
Requires-Dist: pydantic (>=2.13.4,<3.0.0)
|
|
17
17
|
Requires-Dist: safetensors (>=0.8.0,<0.9.0)
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kernel_elastic_autoencoder"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.0.0"
|
|
4
4
|
description = "Implementation of Kernel-Elastic Autoencoder for Molecular Design (https://doi.org/10.1093/pnasnexus/pgae168)"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Felix Rotter-McCartney", email = "felix.rotter@mail.utoronto.ca" }
|
|
@@ -11,8 +11,8 @@ requires-python = ">=3.12,<3.15"
|
|
|
11
11
|
dependencies = [
|
|
12
12
|
"pydantic (>=2.13.4,<3.0.0)",
|
|
13
13
|
"huggingface-hub (>=1.22.0,<2.0.0)",
|
|
14
|
-
"pandas (>=3.0.3,<4.0.0)",
|
|
15
14
|
"safetensors (>=0.8.0,<0.9.0)",
|
|
15
|
+
"accelerate (>=1.14.0,<2.0.0)",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[tool.poetry]
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from kernel_elastic_autoencoder.collate import Collated, Collator, DataframeCollator
|
|
2
1
|
from kernel_elastic_autoencoder.config import (
|
|
3
2
|
ExperimentConfig,
|
|
4
3
|
ModelCommonConfig,
|
|
@@ -13,16 +12,11 @@ from kernel_elastic_autoencoder.config import (
|
|
|
13
12
|
)
|
|
14
13
|
from kernel_elastic_autoencoder.losses import Loss
|
|
15
14
|
from kernel_elastic_autoencoder.model import Model
|
|
16
|
-
from kernel_elastic_autoencoder.pipeline import
|
|
17
|
-
from kernel_elastic_autoencoder.sample import Sampler, Top1Sampler
|
|
15
|
+
from kernel_elastic_autoencoder.pipeline import Pipeline
|
|
18
16
|
from kernel_elastic_autoencoder.tokenizer import Tokenizer
|
|
19
|
-
from kernel_elastic_autoencoder.training import Trainer
|
|
17
|
+
from kernel_elastic_autoencoder.training import Trainer
|
|
20
18
|
|
|
21
19
|
__all__ = [
|
|
22
|
-
"Collated",
|
|
23
|
-
"Collator",
|
|
24
|
-
"Completion",
|
|
25
|
-
"DataframeCollator",
|
|
26
20
|
"ExperimentConfig",
|
|
27
21
|
"Loss",
|
|
28
22
|
"Model",
|
|
@@ -32,11 +26,8 @@ __all__ = [
|
|
|
32
26
|
"ModelEncoderConfig",
|
|
33
27
|
"ModelInputConfig",
|
|
34
28
|
"Pipeline",
|
|
35
|
-
"Sampler",
|
|
36
29
|
"Tokenizer",
|
|
37
|
-
"Top1Sampler",
|
|
38
30
|
"Trainer",
|
|
39
|
-
"TrainerCallback",
|
|
40
31
|
"TrainingCommonConfig",
|
|
41
32
|
"TrainingConfig",
|
|
42
33
|
"TrainingHyperparameterConfig",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import torch
|
|
3
2
|
from huggingface_hub import PyTorchModelHubMixin
|
|
4
3
|
from torch import nn
|
|
@@ -174,5 +173,13 @@ class Model(
|
|
|
174
173
|
).to(torch.bool)
|
|
175
174
|
return self.decoder(current_output, latents, condition_embeddings, padding_mask)
|
|
176
175
|
|
|
177
|
-
def embed_conditions(self, conditions):
|
|
176
|
+
def embed_conditions(self, conditions: torch.Tensor) -> torch.Tensor:
|
|
177
|
+
"""Basic interface for the separate embedding of condition vectors.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
conditions: Tensor of dimension (B, C) containing condition values for each sequence.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
torch.Tensor: Tensor of dimension (B, C, E) containing condition embeddings for each sequence.
|
|
184
|
+
"""
|
|
178
185
|
return self.encoder.embedding.conditional_embedding(conditions)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
from kernel_elastic_autoencoder.model import Model
|
|
6
|
+
from kernel_elastic_autoencoder.tokenizer import Tokenizer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Pipeline:
|
|
10
|
+
"""User-facing pipeline for inference.
|
|
11
|
+
|
|
12
|
+
Defines an easy-to-use API for decoder-only inference with a pretrained model.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
Wrapping a pretrained model and tokenizer:
|
|
16
|
+
>>> model = Model.from_pretrained("./checkpoint")
|
|
17
|
+
>>> tokenizer = MyTokenizer.from_pretrained("./checkpoint/tokenizer")
|
|
18
|
+
>>> pipe = Pipeline(model, tokenizer)
|
|
19
|
+
|
|
20
|
+
Getting a completion for sequences:
|
|
21
|
+
>>> compl = pipe.completion(latents, ["abc", "def", "ghi"], [[1.0, 0.5], [2.0, 1.0], [3.0, 1.5]])
|
|
22
|
+
>>> print(compl.outputs)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
model: Model,
|
|
28
|
+
tokenizer: Tokenizer,
|
|
29
|
+
device: torch.device | None = None,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Instantiates a Pipeline object.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
model: Pre-trained Model object for inference. Can be obtained from Model.from_pretrained.
|
|
35
|
+
tokenizer: Pre-configured Tokenizer object. The Tokenizer protocol supports tokenizers
|
|
36
|
+
inheriting from transformers.PreTrainedTokenizerBase, so such tokenizers may be loaded
|
|
37
|
+
from HuggingFace Hub.
|
|
38
|
+
device: Torch device used for inference.
|
|
39
|
+
"""
|
|
40
|
+
self.model = model.to(device)
|
|
41
|
+
"""Pre-trained Model object for inference. Moved to Pipeline.device, and placed in eval() mode."""
|
|
42
|
+
self.model.eval()
|
|
43
|
+
self.tokenizer = tokenizer
|
|
44
|
+
"""Pre-configured Tokenizer object."""
|
|
45
|
+
self.device = device
|
|
46
|
+
"""Torch device used for inference."""
|
|
47
|
+
|
|
48
|
+
def _ingest(
|
|
49
|
+
self,
|
|
50
|
+
sequences: list[str],
|
|
51
|
+
conditions: list[list[float]] | torch.Tensor,
|
|
52
|
+
device: torch.device | None,
|
|
53
|
+
**kwargs,
|
|
54
|
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
55
|
+
input_ids = self.tokenizer.encode(
|
|
56
|
+
seq=sequences,
|
|
57
|
+
padding=False,
|
|
58
|
+
max_length=self.model.config_typed.input.max_len,
|
|
59
|
+
add_special_tokens=False,
|
|
60
|
+
**kwargs,
|
|
61
|
+
)
|
|
62
|
+
conditions = torch.as_tensor(conditions, dtype=torch.float, device=device)
|
|
63
|
+
condition_mask = (
|
|
64
|
+
conditions != self.model.config_typed.common.padding_value
|
|
65
|
+
).to(torch.bool)
|
|
66
|
+
return input_ids, conditions, condition_mask
|
|
67
|
+
|
|
68
|
+
@torch.inference_mode()
|
|
69
|
+
def _completion_entry(
|
|
70
|
+
self,
|
|
71
|
+
latents: torch.Tensor,
|
|
72
|
+
sequences: list[str],
|
|
73
|
+
conditions: list[list[float]] | torch.Tensor,
|
|
74
|
+
device: torch.device | None = None,
|
|
75
|
+
**kwargs,
|
|
76
|
+
) -> dict[str, torch.Tensor]:
|
|
77
|
+
input_ids, conds, cond_mask = self._ingest(
|
|
78
|
+
sequences, conditions, device, **kwargs
|
|
79
|
+
)
|
|
80
|
+
conds_embed = self.model.embed_conditions(conds)
|
|
81
|
+
return {
|
|
82
|
+
"latents": latents,
|
|
83
|
+
"input_ids": input_ids,
|
|
84
|
+
"batches_completed": torch.zeros(input_ids.size(0), dtype=torch.bool),
|
|
85
|
+
"condition_embeddings": conds_embed,
|
|
86
|
+
"condition_mask": cond_mask,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@torch.inference_mode()
|
|
90
|
+
def _completion_step(
|
|
91
|
+
self, intermediate: dict[str, torch.Tensor]
|
|
92
|
+
) -> dict[str, torch.Tensor]:
|
|
93
|
+
intermediate["input_ids"] = torch.cat(
|
|
94
|
+
[
|
|
95
|
+
torch.full(
|
|
96
|
+
(intermediate["input_ids"].size(0), 1), self.tokenizer.bos_token_id
|
|
97
|
+
),
|
|
98
|
+
intermediate["input_ids"],
|
|
99
|
+
],
|
|
100
|
+
dim=1,
|
|
101
|
+
)
|
|
102
|
+
logits = self.model.decode(
|
|
103
|
+
current_output=intermediate["input_ids"],
|
|
104
|
+
latents=intermediate["latents"],
|
|
105
|
+
condition_embeddings=intermediate["condition_embeddings"],
|
|
106
|
+
token_mask=None,
|
|
107
|
+
condition_mask=intermediate["condition_mask"],
|
|
108
|
+
)
|
|
109
|
+
new_toks = (
|
|
110
|
+
torch.topk(logits[:, -1:], k=1, dim=-1).indices.squeeze(-1).to(torch.long)
|
|
111
|
+
)
|
|
112
|
+
intermediate["batches_completed"] |= (
|
|
113
|
+
new_toks.squeeze(-1) == self.tokenizer.eos_token_id
|
|
114
|
+
)
|
|
115
|
+
new_toks = torch.where(
|
|
116
|
+
intermediate["batches_completed"].unsqueeze(-1),
|
|
117
|
+
self.tokenizer.pad_token_id,
|
|
118
|
+
new_toks,
|
|
119
|
+
)
|
|
120
|
+
intermediate["input_ids"] = torch.cat([intermediate["input_ids"], new_toks], dim=1)
|
|
121
|
+
return intermediate
|
|
122
|
+
|
|
123
|
+
@torch.inference_mode()
|
|
124
|
+
def _completion_exit(self, intermediate: dict[str, torch.Tensor]) -> Iterable[str]:
|
|
125
|
+
return self.tokenizer.decode(intermediate["input_ids"], skip_special_tokens=True)
|
|
126
|
+
|
|
127
|
+
def completion(
|
|
128
|
+
self,
|
|
129
|
+
latents: torch.Tensor,
|
|
130
|
+
sequences: list[str],
|
|
131
|
+
conditions: list[list[float]] | torch.Tensor,
|
|
132
|
+
device: torch.device | None = None,
|
|
133
|
+
**kwargs,
|
|
134
|
+
) -> Iterable[str]:
|
|
135
|
+
"""Completes each conditioned input sequence.
|
|
136
|
+
|
|
137
|
+
The model completes each sequence in the provided list using decoder-only inference. Tokens are
|
|
138
|
+
sampled greedily.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
latents: Tensor of dimension (B, P * E) containing latent vectors for the batch.
|
|
142
|
+
sequences: List of text sequences to complete.
|
|
143
|
+
conditions: List of condition value lists per batch.
|
|
144
|
+
device: Torch device used for inference.
|
|
145
|
+
**kwargs: Additional keyword arguments passed to Tokenizer.encode.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Iterable[str]: List of completed sequences, stripped of special tokens.
|
|
149
|
+
"""
|
|
150
|
+
intermediate = self._completion_entry(
|
|
151
|
+
latents=latents,
|
|
152
|
+
sequences=sequences,
|
|
153
|
+
conditions=conditions,
|
|
154
|
+
device=device,
|
|
155
|
+
**kwargs,
|
|
156
|
+
)
|
|
157
|
+
while (
|
|
158
|
+
intermediate["input_ids"].size(1) < self.model.config_typed.input.max_len
|
|
159
|
+
) and (not intermediate["batches_completed"].all()):
|
|
160
|
+
intermediate = self._completion_step(intermediate=intermediate)
|
|
161
|
+
return self._completion_exit(intermediate=intermediate)
|
|
@@ -78,71 +78,3 @@ class Tokenizer(Protocol):
|
|
|
78
78
|
Tokenizer: Pretrained tokenizer.
|
|
79
79
|
"""
|
|
80
80
|
...
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
class _DummySlowTokenizer(Tokenizer):
|
|
84
|
-
def __init__(self, train: Iterable[str]):
|
|
85
|
-
self.pad_token = "_"
|
|
86
|
-
self.bos_token = "?"
|
|
87
|
-
self.eos_token = "!"
|
|
88
|
-
|
|
89
|
-
vocab = set[str]()
|
|
90
|
-
for b in train:
|
|
91
|
-
vocab = vocab.union(set(b))
|
|
92
|
-
|
|
93
|
-
special_tokens = [self.pad_token, self.bos_token, self.eos_token]
|
|
94
|
-
special_tokens.extend(list(vocab))
|
|
95
|
-
self.vocab = special_tokens
|
|
96
|
-
self.vocab_size = len(self.vocab)
|
|
97
|
-
|
|
98
|
-
self.pad_token_id = 0
|
|
99
|
-
self.bos_token_id = 1
|
|
100
|
-
self.eos_token_id = 2
|
|
101
|
-
|
|
102
|
-
def encode(
|
|
103
|
-
self,
|
|
104
|
-
seq: Iterable[str],
|
|
105
|
-
padding: bool,
|
|
106
|
-
max_length: int,
|
|
107
|
-
add_special_tokens: bool,
|
|
108
|
-
**kwargs,
|
|
109
|
-
) -> torch.Tensor:
|
|
110
|
-
ids_col = list[torch.Tensor]()
|
|
111
|
-
for b in seq:
|
|
112
|
-
toks = list(b)
|
|
113
|
-
if add_special_tokens:
|
|
114
|
-
toks.insert(0, self.bos_token)
|
|
115
|
-
toks.append(self.eos_token)
|
|
116
|
-
if padding:
|
|
117
|
-
toks += [self.pad_token] * (max_length - len(toks))
|
|
118
|
-
ids = torch.tensor([self.vocab.index(t) for t in toks], dtype=torch.long)
|
|
119
|
-
ids_col.append(ids)
|
|
120
|
-
return torch.stack(ids_col, dim=0)
|
|
121
|
-
|
|
122
|
-
def decode(
|
|
123
|
-
self, ids: torch.Tensor, skip_special_tokens: bool, **kwargs
|
|
124
|
-
) -> Iterable[str]:
|
|
125
|
-
if skip_special_tokens:
|
|
126
|
-
return [
|
|
127
|
-
str(
|
|
128
|
-
[
|
|
129
|
-
(
|
|
130
|
-
self.vocab[idx]
|
|
131
|
-
if idx
|
|
132
|
-
not in [
|
|
133
|
-
self.pad_token_id,
|
|
134
|
-
self.bos_token_id,
|
|
135
|
-
self.eos_token_id,
|
|
136
|
-
]
|
|
137
|
-
else ""
|
|
138
|
-
)
|
|
139
|
-
for idx in b
|
|
140
|
-
]
|
|
141
|
-
)
|
|
142
|
-
for b in ids
|
|
143
|
-
]
|
|
144
|
-
else:
|
|
145
|
-
return [str([self.vocab[idx] for idx in b]) for b in ids]
|
|
146
|
-
|
|
147
|
-
@classmethod
|
|
148
|
-
def from_pretrained(cls, pretrained_model_name_or_path: str | Path, **kwargs): ...
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
|
|
4
|
+
import torch
|
|
5
|
+
from accelerate import Accelerator
|
|
6
|
+
from accelerate.utils import tqdm
|
|
7
|
+
|
|
8
|
+
from kernel_elastic_autoencoder.config import TrainingConfig
|
|
9
|
+
from kernel_elastic_autoencoder.losses import Loss
|
|
10
|
+
from kernel_elastic_autoencoder.model import Model
|
|
11
|
+
from kernel_elastic_autoencoder.tokenizer import Tokenizer
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Trainer:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
config: dict | TrainingConfig,
|
|
18
|
+
):
|
|
19
|
+
"""Instantiates a Trainer object.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config: Dictionary or TrainingConfig schema defining model parameters. Will be validated with TrainingConfig
|
|
23
|
+
regardless of input type.
|
|
24
|
+
"""
|
|
25
|
+
self.config = config
|
|
26
|
+
"""Configuration object for Hugging Face Hub compatible serialization. Not recommended to use, as it is
|
|
27
|
+
internal-use. Use Trainer.config_typed instead."""
|
|
28
|
+
config_typed = TrainingConfig.model_validate(config)
|
|
29
|
+
self.config_typed: TrainingConfig = config_typed
|
|
30
|
+
"""Type-validated config in a Pydantic TrainingConfig schema, recommended for public API use."""
|
|
31
|
+
|
|
32
|
+
def train(
|
|
33
|
+
self,
|
|
34
|
+
model: Model,
|
|
35
|
+
tokenizer: Tokenizer,
|
|
36
|
+
sequences: Iterable[str],
|
|
37
|
+
conditions: Iterable[Iterable[float]] | torch.Tensor,
|
|
38
|
+
train_split: float = 0.9,
|
|
39
|
+
checkpoint: str = "./checkpoint",
|
|
40
|
+
):
|
|
41
|
+
"""Trains a model. Optionally, resumes from an existing checkpoint.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
model: Freshly instantiated model.
|
|
45
|
+
tokenizer: Pre-configured Tokenizer object. The Tokenizer protocol supports tokenizers
|
|
46
|
+
inheriting from transformers.PreTrainedTokenizerBase, so such tokenizers may be loaded
|
|
47
|
+
from HuggingFace Hub.
|
|
48
|
+
sequences: Iterable of text sequences.
|
|
49
|
+
conditions: Iterable of iterables of condition values per sequence.
|
|
50
|
+
train_split: Fraction of dataset used for training. Must be between 0 and 1.
|
|
51
|
+
checkpoint: Path of local checkpoint to be saved and/or resumed.
|
|
52
|
+
"""
|
|
53
|
+
accelerator = Accelerator()
|
|
54
|
+
|
|
55
|
+
loss_fn = Loss(
|
|
56
|
+
hp_lambda=self.config_typed.hyperparameters.hp_lambda,
|
|
57
|
+
hp_delta=self.config_typed.hyperparameters.hp_delta,
|
|
58
|
+
hp_sigma=self.config_typed.hyperparameters.hp_sigma,
|
|
59
|
+
kernel_dist_size=self.config_typed.hyperparameters.kernel_dist_size,
|
|
60
|
+
padding_idx=model.config_typed.common.padding_idx,
|
|
61
|
+
embedding_dim=model.config_typed.common.embedding_dim,
|
|
62
|
+
pooling_dim=model.config_typed.common.pooling_dim,
|
|
63
|
+
)
|
|
64
|
+
optimizer = self.config_typed.optimizer.optimizer_fn(
|
|
65
|
+
model.parameters(), **self.config_typed.optimizer.optimizer_params
|
|
66
|
+
)
|
|
67
|
+
scheduler = self.config_typed.optimizer.scheduler_fn(
|
|
68
|
+
optimizer, **self.config_typed.optimizer.scheduler_params
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
input_ids = tokenizer.encode(
|
|
72
|
+
seq=sequences,
|
|
73
|
+
padding=True,
|
|
74
|
+
max_length=model.config_typed.input.max_len,
|
|
75
|
+
add_special_tokens=True,
|
|
76
|
+
)
|
|
77
|
+
conditions = torch.as_tensor(conditions, dtype=torch.float)
|
|
78
|
+
token_mask = (input_ids != model.config_typed.common.padding_idx).to(torch.bool)
|
|
79
|
+
condition_mask = (conditions != model.config_typed.common.padding_value).to(
|
|
80
|
+
torch.bool
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
dataset = torch.utils.data.TensorDataset(
|
|
84
|
+
input_ids, conditions, token_mask, condition_mask
|
|
85
|
+
)
|
|
86
|
+
dataset_train, dataset_test = torch.utils.data.random_split(
|
|
87
|
+
dataset, [train_split, 1 - train_split]
|
|
88
|
+
)
|
|
89
|
+
dataloader_train = torch.utils.data.DataLoader(
|
|
90
|
+
dataset_train,
|
|
91
|
+
batch_size=self.config_typed.common.batch_size,
|
|
92
|
+
)
|
|
93
|
+
dataloader_test = torch.utils.data.DataLoader(
|
|
94
|
+
dataset_test,
|
|
95
|
+
batch_size=self.config_typed.common.batch_size,
|
|
96
|
+
)
|
|
97
|
+
curr_epoch = 0
|
|
98
|
+
|
|
99
|
+
model, optimizer, dataloader_train, dataloader_test, scheduler, curr_epoch = (
|
|
100
|
+
accelerator.prepare(
|
|
101
|
+
model,
|
|
102
|
+
optimizer,
|
|
103
|
+
dataloader_train,
|
|
104
|
+
dataloader_test,
|
|
105
|
+
scheduler,
|
|
106
|
+
curr_epoch,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
accelerator.register_for_checkpointing(scheduler, curr_epoch)
|
|
110
|
+
|
|
111
|
+
if os.path.exists(checkpoint):
|
|
112
|
+
accelerator.load_state(checkpoint)
|
|
113
|
+
|
|
114
|
+
for epoch in range(curr_epoch, self.config_typed.common.max_epochs):
|
|
115
|
+
model.train()
|
|
116
|
+
for input_ids, conditions, token_mask, condition_mask in tqdm(
|
|
117
|
+
dataloader_train, desc=f"Epoch {epoch}, Train Batch"
|
|
118
|
+
):
|
|
119
|
+
optimizer.zero_grad()
|
|
120
|
+
prediction, prediction_noise, latents_noise = model(
|
|
121
|
+
input_ids, conditions, token_mask, condition_mask
|
|
122
|
+
)
|
|
123
|
+
loss = loss_fn(
|
|
124
|
+
prediction, prediction_noise, input_ids[:, 1:], latents_noise
|
|
125
|
+
)
|
|
126
|
+
accelerator.backward(loss)
|
|
127
|
+
optimizer.step()
|
|
128
|
+
|
|
129
|
+
model.eval()
|
|
130
|
+
for input_ids, conditions, token_mask, condition_mask in tqdm(
|
|
131
|
+
dataloader_test, desc=f"Epoch {epoch}, Test Batch"
|
|
132
|
+
):
|
|
133
|
+
with torch.no_grad():
|
|
134
|
+
prediction, prediction_noise, latents_noise = model(
|
|
135
|
+
input_ids,
|
|
136
|
+
conditions,
|
|
137
|
+
token_mask,
|
|
138
|
+
condition_mask,
|
|
139
|
+
)
|
|
140
|
+
loss = loss_fn(
|
|
141
|
+
prediction, prediction_noise, input_ids[:, 1:], latents_noise
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
scheduler.step(epoch)
|
|
145
|
+
|
|
146
|
+
accelerator.wait_for_everyone()
|
|
147
|
+
curr_epoch += 1
|
|
148
|
+
os.makedirs(os.path.join(checkpoint, "dist/"), exist_ok=True)
|
|
149
|
+
model.save_pretrained(os.path.join(checkpoint, "dist/"))
|
|
150
|
+
self.config_typed.to_json(
|
|
151
|
+
os.path.join(checkpoint, "dist/train_config.json")
|
|
152
|
+
)
|
|
153
|
+
accelerator.save_state(checkpoint)
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
|
|
3
|
-
import torch
|
|
4
|
-
from pandas import DataFrame
|
|
5
|
-
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
6
|
-
|
|
7
|
-
from kernel_elastic_autoencoder.config import ModelConfig
|
|
8
|
-
from kernel_elastic_autoencoder.tokenizer import Tokenizer
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Collated(BaseModel):
|
|
12
|
-
"""Return schema for Collator.__call__. Represents a store of tensors ready for direct input to a Model."""
|
|
13
|
-
|
|
14
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
15
|
-
input_ids: torch.Tensor = Field(
|
|
16
|
-
...,
|
|
17
|
-
description="Tensor of dimension (B, S) containing indices of input tokens, generated by a Tokenizer.",
|
|
18
|
-
)
|
|
19
|
-
conditions: torch.Tensor = Field(
|
|
20
|
-
...,
|
|
21
|
-
description="Tensor of dimension (B, C) containing condition values for each sequence.",
|
|
22
|
-
)
|
|
23
|
-
token_mask: torch.Tensor = Field(
|
|
24
|
-
...,
|
|
25
|
-
description="Tensor of dimension (B, S) containing a boolean padding mask for each sequence.",
|
|
26
|
-
)
|
|
27
|
-
condition_mask: torch.Tensor = Field(
|
|
28
|
-
...,
|
|
29
|
-
description="Tensor of dimension (B, C) containing a boolean condition mask for each sequence",
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
@model_validator(mode="after")
|
|
33
|
-
def _check_shapes(self) -> "Collated":
|
|
34
|
-
if (self.input_ids.shape != self.token_mask.shape) or (
|
|
35
|
-
self.conditions.shape != self.condition_mask.shape
|
|
36
|
-
):
|
|
37
|
-
raise ValueError("Mask shapes don't match source")
|
|
38
|
-
if (self.input_ids.dim() != 2) or (self.conditions.dim() != 2):
|
|
39
|
-
raise ValueError("Tensors should have a batch dimension")
|
|
40
|
-
if (self.token_mask.dtype != torch.bool) or (
|
|
41
|
-
self.condition_mask.dtype != torch.bool
|
|
42
|
-
):
|
|
43
|
-
raise ValueError("Mask tensors should have bool dtype")
|
|
44
|
-
return self
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class Collator[T](ABC):
|
|
48
|
-
"""Base class for collators.
|
|
49
|
-
|
|
50
|
-
Provides a specification for preparing various data types for model input.
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
def __init__(self, model_config: ModelConfig, tokenizer: Tokenizer):
|
|
54
|
-
"""Instantiates a Collator object.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
model_config: Model configuration object.
|
|
58
|
-
tokenizer: Object implementing the Tokenizer protocol.
|
|
59
|
-
"""
|
|
60
|
-
self.model_config = model_config
|
|
61
|
-
"""Model configuration object."""
|
|
62
|
-
self.tokenizer = tokenizer
|
|
63
|
-
"""Object implementing the Tokenizer protocol."""
|
|
64
|
-
|
|
65
|
-
def __call__(
|
|
66
|
-
self,
|
|
67
|
-
dataset: T,
|
|
68
|
-
seq_feature: str,
|
|
69
|
-
cond_features: list[str],
|
|
70
|
-
padding: bool,
|
|
71
|
-
add_special_tokens: bool,
|
|
72
|
-
device: torch.device | None,
|
|
73
|
-
**kwargs,
|
|
74
|
-
) -> Collated:
|
|
75
|
-
return self.collate_data(
|
|
76
|
-
dataset,
|
|
77
|
-
seq_feature,
|
|
78
|
-
cond_features,
|
|
79
|
-
padding,
|
|
80
|
-
add_special_tokens,
|
|
81
|
-
device,
|
|
82
|
-
**kwargs,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
@abstractmethod
|
|
86
|
-
def collate_data(
|
|
87
|
-
self,
|
|
88
|
-
dataset: T,
|
|
89
|
-
seq_feature: str,
|
|
90
|
-
cond_features: list[str],
|
|
91
|
-
padding: bool,
|
|
92
|
-
add_special_tokens: bool,
|
|
93
|
-
device: torch.device | None,
|
|
94
|
-
**kwargs,
|
|
95
|
-
) -> Collated:
|
|
96
|
-
"""Interface method for implementing data collation.
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
dataset: Dataset object of target data type.
|
|
100
|
-
seq_feature: Name of sequence feature or column.
|
|
101
|
-
cond_features: List of names of condition features or columns.
|
|
102
|
-
padding: Whether or not to uniformly pad outputs to the maximum length specified in the model configuration.
|
|
103
|
-
add_special_tokens: Whether or not to add special tokens according to a template. Special tokens will be
|
|
104
|
-
added by default during training, but will be omitted during inference, where the specified bos_token_id
|
|
105
|
-
will be appended before each pass through the decoder.
|
|
106
|
-
device: Torch device to which the produced tensors are moved.
|
|
107
|
-
**kwargs: Keyword arguments.
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
Collated: Return schema representing a store of tensors ready for direct input to a Model.
|
|
111
|
-
"""
|
|
112
|
-
...
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class DataframeCollator(Collator):
|
|
116
|
-
"""Collator designed for pandas.DataFrame inputs."""
|
|
117
|
-
|
|
118
|
-
def collate_data(
|
|
119
|
-
self,
|
|
120
|
-
dataset: DataFrame,
|
|
121
|
-
seq_feature: str,
|
|
122
|
-
cond_features: list[str],
|
|
123
|
-
padding: bool,
|
|
124
|
-
add_special_tokens: bool,
|
|
125
|
-
device: torch.device | None,
|
|
126
|
-
**kwargs,
|
|
127
|
-
) -> Collated:
|
|
128
|
-
"""Implementation of data collation for pandas.DataFrame inputs.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
dataset: Dataset object of pandas.DataFrame type.
|
|
132
|
-
seq_feature: Name of sequence column.
|
|
133
|
-
cond_features: List of names of condition columns.
|
|
134
|
-
padding: Whether or not to uniformly pad outputs to the maximum length specified in the model configuration.
|
|
135
|
-
add_special_tokens: Whether or not to add special tokens according to a template. Special tokens will be
|
|
136
|
-
added by default during training, but will be omitted during inference, where the specified bos_token_id
|
|
137
|
-
will be appended before each pass through the decoder.
|
|
138
|
-
device: Torch device to which the produced tensors are moved.
|
|
139
|
-
**kwargs: Keyword arguments passed to Tokenizer.encode.
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
Collated: Return schema representing a store of tensors ready for direct input to a Model.
|
|
143
|
-
"""
|
|
144
|
-
df = dataset.copy()
|
|
145
|
-
input_ids = self.tokenizer.encode(
|
|
146
|
-
seq=df[seq_feature],
|
|
147
|
-
padding=padding,
|
|
148
|
-
max_length=self.model_config.input.max_len,
|
|
149
|
-
add_special_tokens=add_special_tokens,
|
|
150
|
-
**kwargs,
|
|
151
|
-
)
|
|
152
|
-
conditions = torch.from_numpy(df[cond_features].to_numpy()).to(
|
|
153
|
-
device=device, dtype=torch.float
|
|
154
|
-
)
|
|
155
|
-
token_mask = (input_ids != self.model_config.common.padding_idx).to(torch.bool)
|
|
156
|
-
condition_mask = (conditions != self.model_config.common.padding_value).to(
|
|
157
|
-
torch.bool
|
|
158
|
-
)
|
|
159
|
-
return Collated(
|
|
160
|
-
input_ids=input_ids,
|
|
161
|
-
conditions=conditions,
|
|
162
|
-
token_mask=token_mask,
|
|
163
|
-
condition_mask=condition_mask,
|
|
164
|
-
)
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable, Iterable
|
|
2
|
-
|
|
3
|
-
import torch
|
|
4
|
-
from pydantic import BaseModel, ConfigDict
|
|
5
|
-
|
|
6
|
-
from kernel_elastic_autoencoder.collate import Collator, DataframeCollator
|
|
7
|
-
from kernel_elastic_autoencoder.config import ModelConfig
|
|
8
|
-
from kernel_elastic_autoencoder.model import Model
|
|
9
|
-
from kernel_elastic_autoencoder.sample import Sampler, Top1Sampler
|
|
10
|
-
from kernel_elastic_autoencoder.tokenizer import Tokenizer
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class CompletionIntermediate(BaseModel):
|
|
14
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
15
|
-
latents: torch.Tensor
|
|
16
|
-
input_ids: torch.Tensor
|
|
17
|
-
batches_completed: torch.Tensor
|
|
18
|
-
condition_embeddings: torch.Tensor
|
|
19
|
-
condition_mask: torch.Tensor
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class Completion(BaseModel):
|
|
23
|
-
"""Return schema for Pipeline.completion."""
|
|
24
|
-
|
|
25
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
26
|
-
outputs: Iterable[str]
|
|
27
|
-
"""Iterable of generated output sequences."""
|
|
28
|
-
condition_embeddings: torch.Tensor
|
|
29
|
-
"""Tensor containing generated condition embeddings for reuse."""
|
|
30
|
-
condition_mask: torch.Tensor
|
|
31
|
-
"""Tensor containing generated condition masks for reuse."""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class Pipeline[T]:
|
|
35
|
-
"""User-facing pipeline for inference.
|
|
36
|
-
|
|
37
|
-
Defines an easy-to-use API for decoder-only inference with a pretrained model.
|
|
38
|
-
|
|
39
|
-
Examples:
|
|
40
|
-
Wrapping a pretrained model and tokenizer:
|
|
41
|
-
>>> model = Model.from_pretrained("./checkpoint")
|
|
42
|
-
>>> tokenizer = MyClassImplementingTokenizer.from_pretrained("./checkpoint/tokenizer")
|
|
43
|
-
>>> pipe = Pipeline(model, tokenizer, MyClassImplementingCollator, MyClassImplementingSampler)
|
|
44
|
-
>>> compl = pipe.completion(latents, dataset, seq_feature, cond_features)
|
|
45
|
-
>>> print(compl.outputs)
|
|
46
|
-
|
|
47
|
-
TODO: Complete examples.
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
def __init__(
|
|
51
|
-
self,
|
|
52
|
-
model: Model,
|
|
53
|
-
tokenizer: Tokenizer,
|
|
54
|
-
collator: Callable[[ModelConfig, Tokenizer], Collator[T]] = DataframeCollator,
|
|
55
|
-
sampler: Callable[[Tokenizer], Sampler] = Top1Sampler,
|
|
56
|
-
device: torch.device | None = None,
|
|
57
|
-
) -> None:
|
|
58
|
-
"""Instantiates a Pipeline object.
|
|
59
|
-
|
|
60
|
-
TODO: Pipeline setup will be streamlined such that collators and samplers are by default fetched from model
|
|
61
|
-
configuration.
|
|
62
|
-
|
|
63
|
-
Args:
|
|
64
|
-
model: Pre-trained Model object for inference. Can be obtained from Model.from_pretrained.
|
|
65
|
-
tokenizer: Pre-configured Tokenizer object. The Tokenizer protocol supports tokenizers inheriting from
|
|
66
|
-
transformers.PreTrainedTokenizerBase, so such tokenizers may be loaded from HuggingFace Hub.
|
|
67
|
-
collator: Constructor for a data collator inheriting from Collator. Determines input types to Pipeline methods.
|
|
68
|
-
sampler: Constructor for a data sampler inheriting from Sampler. Determines how outputs are produced from
|
|
69
|
-
Pipeline methods.
|
|
70
|
-
device: Torch device used for inference.
|
|
71
|
-
"""
|
|
72
|
-
self.model = model.to(device)
|
|
73
|
-
"""Pre-trained Model object for inference. Moved to Pipeline.device, and placed in eval() mode."""
|
|
74
|
-
self.model.eval()
|
|
75
|
-
self.tokenizer = tokenizer
|
|
76
|
-
"""Pre-configured Tokenizer object. The Tokenizer Protocol supports tokenizers inheriting from
|
|
77
|
-
transformers.PreTrainedTokenizerBase, so such tokenizers may be loaded from HuggingFace Hub."""
|
|
78
|
-
self.device = device
|
|
79
|
-
"""Torch device used for inference."""
|
|
80
|
-
|
|
81
|
-
self.collator = collator(model.config_typed, tokenizer)
|
|
82
|
-
"""Data collator inheriting from Collator, instantiated by passing Pipeline.model.config_typed and Pipeline.tokenizer."""
|
|
83
|
-
self.sampler = sampler(tokenizer)
|
|
84
|
-
"""Data sampler inheriting from Sampler, instantiated by passing Pipeline.tokenizer."""
|
|
85
|
-
|
|
86
|
-
@torch.inference_mode()
|
|
87
|
-
def _completion_entry(
|
|
88
|
-
self,
|
|
89
|
-
latents: torch.Tensor,
|
|
90
|
-
dataset: T,
|
|
91
|
-
seq_feature: str,
|
|
92
|
-
cond_features: list[str],
|
|
93
|
-
device: torch.device | None = None,
|
|
94
|
-
**kwargs,
|
|
95
|
-
) -> CompletionIntermediate:
|
|
96
|
-
ds = self.collator(
|
|
97
|
-
dataset=dataset,
|
|
98
|
-
seq_feature=seq_feature,
|
|
99
|
-
cond_features=cond_features,
|
|
100
|
-
padding=False,
|
|
101
|
-
add_special_tokens=False,
|
|
102
|
-
device=device,
|
|
103
|
-
**kwargs,
|
|
104
|
-
)
|
|
105
|
-
conds_embed = self.model.embed_conditions(ds.conditions)
|
|
106
|
-
return CompletionIntermediate(
|
|
107
|
-
latents=latents,
|
|
108
|
-
input_ids=ds.input_ids,
|
|
109
|
-
batches_completed=torch.zeros(ds.input_ids.size(0), dtype=torch.bool),
|
|
110
|
-
condition_embeddings=conds_embed,
|
|
111
|
-
condition_mask=ds.condition_mask,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
@torch.inference_mode()
|
|
115
|
-
def _completion_step(
|
|
116
|
-
self, intermediate: CompletionIntermediate, **kwargs
|
|
117
|
-
) -> CompletionIntermediate:
|
|
118
|
-
intermediate.input_ids = torch.cat(
|
|
119
|
-
[
|
|
120
|
-
torch.full(
|
|
121
|
-
(intermediate.input_ids.size(0), 1), self.tokenizer.bos_token_id
|
|
122
|
-
),
|
|
123
|
-
intermediate.input_ids,
|
|
124
|
-
],
|
|
125
|
-
dim=1,
|
|
126
|
-
)
|
|
127
|
-
logits = self.model.decode(
|
|
128
|
-
current_output=intermediate.input_ids,
|
|
129
|
-
latents=intermediate.latents,
|
|
130
|
-
condition_embeddings=intermediate.condition_embeddings,
|
|
131
|
-
token_mask=None,
|
|
132
|
-
condition_mask=intermediate.condition_mask,
|
|
133
|
-
)
|
|
134
|
-
new_toks = self.sampler.sample_ids(logits[:, -1:])
|
|
135
|
-
intermediate.batches_completed |= (
|
|
136
|
-
new_toks.squeeze(-1) == self.tokenizer.eos_token_id
|
|
137
|
-
)
|
|
138
|
-
torch.where(
|
|
139
|
-
intermediate.batches_completed.unsqueeze(-1),
|
|
140
|
-
self.tokenizer.pad_token_id,
|
|
141
|
-
new_toks,
|
|
142
|
-
)
|
|
143
|
-
intermediate.input_ids = torch.cat([intermediate.input_ids, new_toks], dim=1)
|
|
144
|
-
return intermediate
|
|
145
|
-
|
|
146
|
-
@torch.inference_mode()
|
|
147
|
-
def _completion_exit(
|
|
148
|
-
self, intermediate: CompletionIntermediate, **kwargs
|
|
149
|
-
) -> Completion:
|
|
150
|
-
return Completion(
|
|
151
|
-
outputs=self.sampler(
|
|
152
|
-
intermediate.input_ids, skip_special_tokens=True
|
|
153
|
-
),
|
|
154
|
-
condition_embeddings=intermediate.condition_embeddings,
|
|
155
|
-
condition_mask=intermediate.condition_mask,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
def completion(
|
|
159
|
-
self,
|
|
160
|
-
latents: torch.Tensor,
|
|
161
|
-
dataset: T,
|
|
162
|
-
seq_feature: str,
|
|
163
|
-
cond_features: list[str],
|
|
164
|
-
device: torch.device | None = None,
|
|
165
|
-
**kwargs,
|
|
166
|
-
) -> Completion:
|
|
167
|
-
"""Completes each batch in an input dataset with latent and condition guidance through the decoder.
|
|
168
|
-
|
|
169
|
-
Using decoder-only inference, the model completes each batch in the provided dataset. Dataset input typing is
|
|
170
|
-
uniquely defined by the used Collator. The input dataset is passed through the collator, then subject to
|
|
171
|
-
autoregressive decoder-only inference through the model, where new tokens are sampled from intermediates and
|
|
172
|
-
stored in a tensor format. Finally, on reaching end-of-sequence tokens or the maximum sequence length, the
|
|
173
|
-
sequence is decoded by the tokenizer and returned along with the generated condition embeddings and masks in a
|
|
174
|
-
Completion schema.
|
|
175
|
-
|
|
176
|
-
TODO: Callbacks are planned to enable easy custom inference functions and streaming of text.
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
latents: Tensor of dimension (B, P * E) containing latent vectors for the batches.
|
|
180
|
-
dataset: Dataset of type specified in Pipeline.collator.
|
|
181
|
-
seq_feature: String to pass to collator containing the feature or column containing text sequences to
|
|
182
|
-
complete.
|
|
183
|
-
cond_features: List of strings to pass to collator containing features or columns containing numerical
|
|
184
|
-
condition values.
|
|
185
|
-
device: Torch device used for inference.
|
|
186
|
-
**kwargs: Additional keyword arguments passed to Pipeline.collator.
|
|
187
|
-
|
|
188
|
-
Returns:
|
|
189
|
-
Completion: Completion object, containing outputs, as well as condition embeddings and masks for reuse.
|
|
190
|
-
"""
|
|
191
|
-
intermediate = self._completion_entry(
|
|
192
|
-
latents=latents,
|
|
193
|
-
dataset=dataset,
|
|
194
|
-
seq_feature=seq_feature,
|
|
195
|
-
cond_features=cond_features,
|
|
196
|
-
device=device,
|
|
197
|
-
**kwargs,
|
|
198
|
-
)
|
|
199
|
-
while (
|
|
200
|
-
intermediate.input_ids.size(1) < self.model.config_typed.input.max_len
|
|
201
|
-
) and (not intermediate.batches_completed.all()):
|
|
202
|
-
intermediate = self._completion_step(intermediate=intermediate)
|
|
203
|
-
return self._completion_exit(intermediate=intermediate)
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
from collections.abc import Iterable
|
|
3
|
-
|
|
4
|
-
import torch
|
|
5
|
-
|
|
6
|
-
from kernel_elastic_autoencoder.tokenizer import Tokenizer
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Sampler(ABC):
|
|
10
|
-
"""Base class for samplers.
|
|
11
|
-
|
|
12
|
-
Provides a specification for sampling text sequences from logits.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def __init__(self, tokenizer: Tokenizer, **kwargs):
|
|
16
|
-
"""Instantiates a Sampler object.
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
tokenizer: Object implementing the Tokenizer protocol.
|
|
20
|
-
**kwargs: Sampler-specific keyword args.
|
|
21
|
-
"""
|
|
22
|
-
self.tokenizer = tokenizer
|
|
23
|
-
"""Object implementing the Tokenizer protocol."""
|
|
24
|
-
|
|
25
|
-
@abstractmethod
|
|
26
|
-
def __call__(
|
|
27
|
-
self, logits: torch.Tensor, skip_special_tokens: bool, **kwargs
|
|
28
|
-
) -> Iterable[str]:
|
|
29
|
-
"""Interface method for implementing the last sampling step where outputs are decoded through the tokenizer.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
logits: Tensor of dimension (B, S) containing IDs at the last step of inference.
|
|
33
|
-
**kwargs: Keyword arguments.
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
Iterable[str]: Iterable of strings decoded by the tokenizer.
|
|
37
|
-
"""
|
|
38
|
-
...
|
|
39
|
-
|
|
40
|
-
@abstractmethod
|
|
41
|
-
def sample_ids(self, logits: torch.Tensor, **kwargs) -> torch.Tensor:
|
|
42
|
-
"""Interface method for implementing index sampling from logits in intermediate steps.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
logits: Tensor of dimension (B, S, L) containing logits.
|
|
46
|
-
**kwargs: Keyword arguments.
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
torch.Tensor: Tensor of dimension (B, S) containing vocabulary indices.
|
|
50
|
-
"""
|
|
51
|
-
...
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class Top1Sampler(Sampler):
|
|
55
|
-
def __call__(self, logits: torch.Tensor, skip_special_tokens: bool, **kwargs) -> Iterable[str]:
|
|
56
|
-
return self.tokenizer.decode(logits, skip_special_tokens=skip_special_tokens)
|
|
57
|
-
|
|
58
|
-
def sample_ids(self, logits: torch.Tensor, **kwargs) -> torch.Tensor:
|
|
59
|
-
"""Implementation of index sampling from logits choosing the highest-probability token.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
logits: Tensor of dimension (B, S, L) containing logits.
|
|
63
|
-
**kwargs: Keyword arguments.
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
torch.Tensor: Tensor of dimension (B, S) containing vocabulary indices.
|
|
67
|
-
"""
|
|
68
|
-
return (
|
|
69
|
-
torch.topk(logits, k=1, dim=-1, **kwargs).indices.squeeze(-1).to(torch.long)
|
|
70
|
-
)
|
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import Protocol
|
|
3
|
-
|
|
4
|
-
import torch
|
|
5
|
-
import torch.distributed as dist
|
|
6
|
-
from pydantic import BaseModel, ConfigDict
|
|
7
|
-
from torch import nn
|
|
8
|
-
from torch.nn.parallel import DistributedDataParallel as DDP
|
|
9
|
-
from torch.utils.data import DistributedSampler
|
|
10
|
-
|
|
11
|
-
from kernel_elastic_autoencoder.collate import Collated
|
|
12
|
-
from kernel_elastic_autoencoder.config import TrainingConfig
|
|
13
|
-
from kernel_elastic_autoencoder.losses import Loss
|
|
14
|
-
from kernel_elastic_autoencoder.model import Model
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _is_local():
|
|
18
|
-
return (
|
|
19
|
-
dist.is_torchelastic_launched() and (dist.get_rank() == 0)
|
|
20
|
-
) or not dist.is_torchelastic_launched()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class TrainerCallbackCtx(BaseModel):
|
|
24
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
25
|
-
dist: bool
|
|
26
|
-
device_type: str
|
|
27
|
-
local_rank: int | None
|
|
28
|
-
model: Model | DDP
|
|
29
|
-
optimizer: torch.optim.Optimizer
|
|
30
|
-
scheduler: torch.optim.lr_scheduler.LRScheduler
|
|
31
|
-
dataloader_train: torch.utils.data.DataLoader
|
|
32
|
-
dataloader_test: torch.utils.data.DataLoader
|
|
33
|
-
|
|
34
|
-
epoch: int | None = None
|
|
35
|
-
rel_epoch: int | None = None
|
|
36
|
-
batch: int | None = None
|
|
37
|
-
train_loss: torch.Tensor | None = None
|
|
38
|
-
test_loss: torch.Tensor | None = None
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class TrainerCallback(Protocol):
|
|
42
|
-
"""Protocol to be implemented for callback classes to a Trainer.
|
|
43
|
-
|
|
44
|
-
On each hook, except before the Trainer is initialized, a TrainerCallbackCtx schema is passed with the appropriate
|
|
45
|
-
information passed.
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
def before_init(self):
|
|
49
|
-
"""Called before training setup."""
|
|
50
|
-
...
|
|
51
|
-
|
|
52
|
-
def after_init(self, ctx: TrainerCallbackCtx):
|
|
53
|
-
"""Called after training setup."""
|
|
54
|
-
...
|
|
55
|
-
|
|
56
|
-
def before_epoch(self, ctx: TrainerCallbackCtx):
|
|
57
|
-
"""Called before each epoch."""
|
|
58
|
-
...
|
|
59
|
-
|
|
60
|
-
def before_train_batch(self, ctx: TrainerCallbackCtx):
|
|
61
|
-
"""Called before each forward pass of a single training batch."""
|
|
62
|
-
...
|
|
63
|
-
|
|
64
|
-
def after_train_batch(self, ctx: TrainerCallbackCtx):
|
|
65
|
-
"""Called after each forward pass of a single training batch."""
|
|
66
|
-
...
|
|
67
|
-
|
|
68
|
-
def before_test_batch(self, ctx: TrainerCallbackCtx):
|
|
69
|
-
"""Called before each forward pass of a single test batch."""
|
|
70
|
-
...
|
|
71
|
-
|
|
72
|
-
def after_test_batch(self, ctx: TrainerCallbackCtx):
|
|
73
|
-
"""Called after each forward pass of a single test batch."""
|
|
74
|
-
...
|
|
75
|
-
|
|
76
|
-
def after_epoch(self, ctx: TrainerCallbackCtx):
|
|
77
|
-
"""Called after each epoch."""
|
|
78
|
-
...
|
|
79
|
-
|
|
80
|
-
def after_training(self, ctx: TrainerCallbackCtx):
|
|
81
|
-
"""Called after training ends."""
|
|
82
|
-
...
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class TrainerDefaultCallback:
|
|
86
|
-
# TODO: Implement sensible default logging.
|
|
87
|
-
def before_init(self):
|
|
88
|
-
pass
|
|
89
|
-
|
|
90
|
-
def after_init(self, ctx: TrainerCallbackCtx):
|
|
91
|
-
pass
|
|
92
|
-
|
|
93
|
-
def before_epoch(self, ctx: TrainerCallbackCtx):
|
|
94
|
-
pass
|
|
95
|
-
|
|
96
|
-
def before_train_batch(self, ctx: TrainerCallbackCtx):
|
|
97
|
-
pass
|
|
98
|
-
|
|
99
|
-
def after_train_batch(self, ctx: TrainerCallbackCtx):
|
|
100
|
-
pass
|
|
101
|
-
|
|
102
|
-
def before_test_batch(self, ctx: TrainerCallbackCtx):
|
|
103
|
-
pass
|
|
104
|
-
|
|
105
|
-
def after_test_batch(self, ctx: TrainerCallbackCtx):
|
|
106
|
-
pass
|
|
107
|
-
|
|
108
|
-
def after_epoch(self, ctx: TrainerCallbackCtx):
|
|
109
|
-
pass
|
|
110
|
-
|
|
111
|
-
def after_training(self, ctx: TrainerCallbackCtx):
|
|
112
|
-
pass
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class Trainer:
|
|
116
|
-
def __init__(
|
|
117
|
-
self,
|
|
118
|
-
config: dict | TrainingConfig,
|
|
119
|
-
callbacks: tuple[TrainerCallback] = (TrainerDefaultCallback(),),
|
|
120
|
-
):
|
|
121
|
-
"""Instantiates a Trainer object.
|
|
122
|
-
|
|
123
|
-
Args:
|
|
124
|
-
config: Dictionary or TrainingConfig schema defining model parameters. Will be validated with TrainingConfig
|
|
125
|
-
regardless of input type.
|
|
126
|
-
callbacks: Tuple of callback classes implementing TrainerCallback.
|
|
127
|
-
"""
|
|
128
|
-
self.config = config
|
|
129
|
-
"""Configuration object for Hugging Face Hub compatible serialization. Not recommended to use, as it is
|
|
130
|
-
internal-use. Use Trainer.config_typed instead."""
|
|
131
|
-
config_typed = TrainingConfig.model_validate(config)
|
|
132
|
-
self.config_typed: TrainingConfig = config_typed
|
|
133
|
-
"""Type-validated config in a Pydantic TrainingConfig schema, recommended for public API use."""
|
|
134
|
-
self.callbacks = callbacks
|
|
135
|
-
"""Tuple of callback classes implementing TrainerCallback."""
|
|
136
|
-
|
|
137
|
-
self._ctx: TrainerCallbackCtx
|
|
138
|
-
|
|
139
|
-
self._model: DDP | Model
|
|
140
|
-
self._dist: bool
|
|
141
|
-
self._loss_fn: nn.Module
|
|
142
|
-
self._optimizer: torch.optim.Optimizer
|
|
143
|
-
self._scheduler: torch.optim.lr_scheduler.LRScheduler
|
|
144
|
-
self._dataloader_train: torch.utils.data.DataLoader
|
|
145
|
-
self._dataloader_test: torch.utils.data.DataLoader
|
|
146
|
-
self._device_type: str
|
|
147
|
-
self._local_rank: int | None
|
|
148
|
-
|
|
149
|
-
def train(
|
|
150
|
-
self,
|
|
151
|
-
model: Model,
|
|
152
|
-
ds: Collated,
|
|
153
|
-
train_split: float = 0.9,
|
|
154
|
-
checkpoint: str = "./checkpoint",
|
|
155
|
-
):
|
|
156
|
-
"""Trains a model with a Collated dataset. Optionally resumes from an existing checkpoint.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
model: Freshly instantiated model.
|
|
160
|
-
ds: Tensor dataset following the Collated schema.
|
|
161
|
-
train_split: Fraction of dataset used for training. Must be between 0 and 1.
|
|
162
|
-
checkpoint: Path of local checkpoint to be saved and/or resumed.
|
|
163
|
-
"""
|
|
164
|
-
next_epoch = self._setup(model, ds, train_split, checkpoint)
|
|
165
|
-
for epoch in range(next_epoch, self.config_typed.common.max_epochs):
|
|
166
|
-
self._ctx.rel_epoch = epoch - next_epoch
|
|
167
|
-
self._epoch(epoch, checkpoint)
|
|
168
|
-
self._ctx.epoch = None
|
|
169
|
-
self._ctx.rel_epoch = None
|
|
170
|
-
if _is_local():
|
|
171
|
-
for cb in self.callbacks:
|
|
172
|
-
cb.after_training(self._ctx)
|
|
173
|
-
|
|
174
|
-
def _setup(
|
|
175
|
-
self,
|
|
176
|
-
model: Model,
|
|
177
|
-
ds: Collated,
|
|
178
|
-
train_split: float,
|
|
179
|
-
checkpoint: str,
|
|
180
|
-
):
|
|
181
|
-
if _is_local():
|
|
182
|
-
for cb in self.callbacks:
|
|
183
|
-
cb.before_init()
|
|
184
|
-
self._model = model
|
|
185
|
-
self._dist = dist.is_torchelastic_launched()
|
|
186
|
-
if self._dist:
|
|
187
|
-
self._model, self._device_type, self._local_rank = self._setup_ddp()
|
|
188
|
-
else:
|
|
189
|
-
self._device_type, self._local_rank = self._setup_no_ddp()
|
|
190
|
-
self._loss_fn = self._setup_loss(model)
|
|
191
|
-
self._optimizer, self._scheduler = self._setup_optimizer()
|
|
192
|
-
self._dataloader_train, self._dataloader_test = self._setup_dataloaders(
|
|
193
|
-
ds, train_split
|
|
194
|
-
)
|
|
195
|
-
next_epoch = 0
|
|
196
|
-
if os.path.exists(checkpoint):
|
|
197
|
-
next_epoch = self._resume(checkpoint)
|
|
198
|
-
self._ctx = TrainerCallbackCtx(
|
|
199
|
-
dist=self._dist,
|
|
200
|
-
device_type=self._device_type,
|
|
201
|
-
local_rank=self._local_rank,
|
|
202
|
-
model=self._model,
|
|
203
|
-
optimizer=self._optimizer,
|
|
204
|
-
scheduler=self._scheduler,
|
|
205
|
-
dataloader_train=self._dataloader_train,
|
|
206
|
-
dataloader_test=self._dataloader_test,
|
|
207
|
-
)
|
|
208
|
-
if _is_local():
|
|
209
|
-
for cb in self.callbacks:
|
|
210
|
-
cb.after_init(self._ctx)
|
|
211
|
-
return next_epoch
|
|
212
|
-
|
|
213
|
-
def _setup_loss(self, model: Model):
|
|
214
|
-
_loss_fn = Loss(
|
|
215
|
-
hp_lambda=self.config_typed.hyperparameters.hp_lambda,
|
|
216
|
-
hp_delta=self.config_typed.hyperparameters.hp_delta,
|
|
217
|
-
hp_sigma=self.config_typed.hyperparameters.hp_sigma,
|
|
218
|
-
kernel_dist_size=self.config_typed.hyperparameters.kernel_dist_size,
|
|
219
|
-
padding_idx=model.config_typed.common.padding_idx,
|
|
220
|
-
embedding_dim=model.config_typed.common.embedding_dim,
|
|
221
|
-
pooling_dim=model.config_typed.common.pooling_dim,
|
|
222
|
-
)
|
|
223
|
-
return _loss_fn
|
|
224
|
-
|
|
225
|
-
def _setup_optimizer(self):
|
|
226
|
-
optimizer = self.config_typed.optimizer.optimizer_fn(
|
|
227
|
-
self._model.parameters(), **self.config_typed.optimizer.optimizer_params
|
|
228
|
-
)
|
|
229
|
-
scheduler = self.config_typed.optimizer.scheduler_fn(
|
|
230
|
-
optimizer, **self.config_typed.optimizer.scheduler_params
|
|
231
|
-
)
|
|
232
|
-
return optimizer, scheduler
|
|
233
|
-
|
|
234
|
-
def _setup_ddp(self) -> tuple[DDP, str, int]:
|
|
235
|
-
device_type, vendor_backend = self._get_backend()
|
|
236
|
-
dist.init_process_group(backend=vendor_backend)
|
|
237
|
-
local_rank = int(os.environ["LOCAL_RANK"])
|
|
238
|
-
model = DDP(self._model.to(local_rank))
|
|
239
|
-
return model, device_type, local_rank
|
|
240
|
-
|
|
241
|
-
def _setup_no_ddp(self):
|
|
242
|
-
device_type, _ = self._get_backend()
|
|
243
|
-
return device_type, None
|
|
244
|
-
|
|
245
|
-
def _setup_dataloaders(self, ds: Collated, train_split: float):
|
|
246
|
-
dataset = torch.utils.data.TensorDataset(
|
|
247
|
-
ds.input_ids, ds.conditions, ds.token_mask, ds.condition_mask
|
|
248
|
-
)
|
|
249
|
-
dataset_train, dataset_test = torch.utils.data.random_split(
|
|
250
|
-
dataset, [train_split, 1 - train_split]
|
|
251
|
-
)
|
|
252
|
-
if self._dist:
|
|
253
|
-
sampler_train = DistributedSampler(dataset_train)
|
|
254
|
-
sampler_test = DistributedSampler(dataset_test)
|
|
255
|
-
else:
|
|
256
|
-
sampler_train = torch.utils.data.RandomSampler(dataset_train)
|
|
257
|
-
sampler_test = torch.utils.data.SequentialSampler(dataset_test)
|
|
258
|
-
_dataloader_train = torch.utils.data.DataLoader(
|
|
259
|
-
dataset_train,
|
|
260
|
-
batch_size=self.config_typed.common.batch_size,
|
|
261
|
-
sampler=sampler_train,
|
|
262
|
-
pin_memory=True,
|
|
263
|
-
)
|
|
264
|
-
_dataloader_test = torch.utils.data.DataLoader(
|
|
265
|
-
dataset_test,
|
|
266
|
-
batch_size=self.config_typed.common.batch_size,
|
|
267
|
-
sampler=sampler_test,
|
|
268
|
-
pin_memory=True,
|
|
269
|
-
)
|
|
270
|
-
return _dataloader_train, _dataloader_test
|
|
271
|
-
|
|
272
|
-
def _resume(
|
|
273
|
-
self,
|
|
274
|
-
checkpoint: str,
|
|
275
|
-
):
|
|
276
|
-
train_state = torch.load(os.path.join(checkpoint, "train_state.pt"))
|
|
277
|
-
self._optimizer.load_state_dict(train_state["optimizer"])
|
|
278
|
-
self._scheduler.load_state_dict(train_state["scheduler"])
|
|
279
|
-
self._model.from_pretrained(checkpoint) # type: ignore
|
|
280
|
-
return train_state["next_epoch"]
|
|
281
|
-
|
|
282
|
-
def _epoch(
|
|
283
|
-
self,
|
|
284
|
-
epoch: int,
|
|
285
|
-
checkpoint: str,
|
|
286
|
-
):
|
|
287
|
-
self._ctx.epoch = epoch
|
|
288
|
-
if _is_local():
|
|
289
|
-
for cb in self.callbacks:
|
|
290
|
-
cb.before_epoch(self._ctx)
|
|
291
|
-
|
|
292
|
-
self._model.train()
|
|
293
|
-
self._ctx.batch = 0
|
|
294
|
-
for input_ids, conditions, token_mask, condition_mask in self._dataloader_train:
|
|
295
|
-
if _is_local():
|
|
296
|
-
for cb in self.callbacks:
|
|
297
|
-
cb.before_train_batch(self._ctx)
|
|
298
|
-
self._ctx.train_loss = self._batch_train(
|
|
299
|
-
input_ids, conditions, token_mask, condition_mask
|
|
300
|
-
)
|
|
301
|
-
if _is_local():
|
|
302
|
-
for cb in self.callbacks:
|
|
303
|
-
cb.after_train_batch(self._ctx)
|
|
304
|
-
self._ctx.batch += 1
|
|
305
|
-
|
|
306
|
-
self._model.eval()
|
|
307
|
-
self._ctx.batch = 0
|
|
308
|
-
for input_ids, conditions, token_mask, condition_mask in self._dataloader_test:
|
|
309
|
-
if _is_local():
|
|
310
|
-
for cb in self.callbacks:
|
|
311
|
-
cb.before_test_batch(self._ctx)
|
|
312
|
-
self._ctx.test_loss = self._batch_test(
|
|
313
|
-
input_ids, conditions, token_mask, condition_mask
|
|
314
|
-
)
|
|
315
|
-
if _is_local():
|
|
316
|
-
for cb in self.callbacks:
|
|
317
|
-
cb.after_test_batch(self._ctx)
|
|
318
|
-
self._ctx.batch += 1
|
|
319
|
-
|
|
320
|
-
self._end_epoch(epoch, checkpoint)
|
|
321
|
-
self._ctx.train_loss = None
|
|
322
|
-
self._ctx.test_loss = None
|
|
323
|
-
self._ctx.batch = None
|
|
324
|
-
if _is_local():
|
|
325
|
-
for cb in self.callbacks:
|
|
326
|
-
cb.after_epoch(self._ctx)
|
|
327
|
-
|
|
328
|
-
def _end_epoch(self, epoch: int, checkpoint: str):
|
|
329
|
-
self._scheduler.step(epoch)
|
|
330
|
-
self._save_state(checkpoint, epoch)
|
|
331
|
-
|
|
332
|
-
def _batch_train(self, input_ids, conditions, token_mask, condition_mask):
|
|
333
|
-
self._optimizer.zero_grad()
|
|
334
|
-
with torch.amp.autocast(self._device_type):
|
|
335
|
-
prediction, prediction_noise, latents_noise = self._model.forward(
|
|
336
|
-
input_ids, conditions, token_mask, condition_mask
|
|
337
|
-
)
|
|
338
|
-
loss = self._loss_fn(
|
|
339
|
-
prediction, prediction_noise, input_ids[:, 1:], latents_noise
|
|
340
|
-
)
|
|
341
|
-
loss.backward()
|
|
342
|
-
self._optimizer.step()
|
|
343
|
-
return loss.detach()
|
|
344
|
-
|
|
345
|
-
def _batch_test(self, input_ids, conditions, token_mask, condition_mask):
|
|
346
|
-
with torch.no_grad():
|
|
347
|
-
with torch.amp.autocast(self._device_type):
|
|
348
|
-
prediction, prediction_noise, latents_noise = self._model.forward(
|
|
349
|
-
input_ids,
|
|
350
|
-
conditions,
|
|
351
|
-
token_mask,
|
|
352
|
-
condition_mask,
|
|
353
|
-
)
|
|
354
|
-
loss = self._loss_fn(
|
|
355
|
-
prediction, prediction_noise, input_ids[:, 1:], latents_noise
|
|
356
|
-
)
|
|
357
|
-
return loss.detach()
|
|
358
|
-
|
|
359
|
-
def _save_state(self, checkpoint: str, epoch: int):
|
|
360
|
-
if _is_local():
|
|
361
|
-
os.makedirs(checkpoint, exist_ok=True)
|
|
362
|
-
self._model.save_pretrained(checkpoint) # type: ignore
|
|
363
|
-
torch.save(
|
|
364
|
-
{
|
|
365
|
-
"next_epoch": epoch + 1,
|
|
366
|
-
"optimizer": self._optimizer.state_dict(),
|
|
367
|
-
"scheduler": self._scheduler.state_dict(),
|
|
368
|
-
},
|
|
369
|
-
os.path.join(checkpoint, "train_state.pt"),
|
|
370
|
-
)
|
|
371
|
-
self.config_typed.to_json(os.path.join(checkpoint, "train_config.json"))
|
|
372
|
-
|
|
373
|
-
def _get_backend(self) -> tuple[str, str]:
|
|
374
|
-
if torch.accelerator.is_available():
|
|
375
|
-
device_type = torch.accelerator.current_accelerator().type # type: ignore
|
|
376
|
-
vendor_backend = torch.distributed.get_default_backend_for_device(
|
|
377
|
-
device_type
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
else:
|
|
381
|
-
device_type = torch.device("cpu").type
|
|
382
|
-
vendor_backend = torch.distributed.get_default_backend_for_device(
|
|
383
|
-
device_type
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
return device_type, vendor_backend
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|