code-loader 1.0.207.dev0__py3-none-any.whl → 1.0.208__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.
- code_loader/contract/datasetclasses.py +14 -1
- code_loader/contract/enums.py +10 -0
- code_loader/contract/mapping.py +1 -0
- code_loader/inner_leap_binder/leapbinder.py +49 -11
- code_loader/inner_leap_binder/leapbinder_decorators.py +285 -13
- code_loader/leaploader.py +100 -9
- code_loader/leaploaderbase.py +25 -0
- code_loader/utils.py +10 -1
- {code_loader-1.0.207.dev0.dist-info → code_loader-1.0.208.dist-info}/METADATA +1 -1
- {code_loader-1.0.207.dev0.dist-info → code_loader-1.0.208.dist-info}/RECORD +12 -12
- {code_loader-1.0.207.dev0.dist-info → code_loader-1.0.208.dist-info}/LICENSE +0 -0
- {code_loader-1.0.207.dev0.dist-info → code_loader-1.0.208.dist-info}/WHEEL +0 -0
|
@@ -6,13 +6,21 @@ import numpy as np
|
|
|
6
6
|
import numpy.typing as npt
|
|
7
7
|
|
|
8
8
|
from code_loader.contract.enums import DataStateType, DataStateEnum, LeapDataType, ConfusionMatrixValue, \
|
|
9
|
-
MetricDirection, DatasetMetadataType
|
|
9
|
+
MetricDirection, DatasetMetadataType, LatentSpaceReduction, CustomLatentSpaceComputedAt
|
|
10
10
|
from code_loader.contract.visualizer_classes import LeapImage, LeapText, LeapGraph, LeapHorizontalBar, \
|
|
11
11
|
LeapTextMask, LeapImageMask, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio
|
|
12
12
|
from code_loader.contract.sim_config import SimConfig
|
|
13
13
|
|
|
14
14
|
custom_latent_space_attribute = "custom_latent_space"
|
|
15
15
|
|
|
16
|
+
# Hard cap on a custom latent space's per-sample width, applied AFTER any reduction. The
|
|
17
|
+
# soft threshold only warns. MAX_CUSTOM_LATENT_SPACES mirrors the engine's
|
|
18
|
+
# src_tensorleap/common/types.py (source of truth) so an over-budget registration fails
|
|
19
|
+
# locally instead of being silently dropped at eval time; keep the two in sync.
|
|
20
|
+
MAX_CUSTOM_LATENT_SPACE_DIM = 4096
|
|
21
|
+
CUSTOM_LATENT_SPACE_WARN_DIM = 1024
|
|
22
|
+
MAX_CUSTOM_LATENT_SPACES = 10
|
|
23
|
+
|
|
16
24
|
_simulation_context: Dict[str, bool] = {"active": False}
|
|
17
25
|
|
|
18
26
|
SampleId = Union[int, str]
|
|
@@ -317,6 +325,11 @@ class CustomLatentSpaceHandler:
|
|
|
317
325
|
name: str = 'custom_latent_space'
|
|
318
326
|
use_ls_for_analysis: bool = False
|
|
319
327
|
instance_aware: bool = False
|
|
328
|
+
computed_at: CustomLatentSpaceComputedAt = CustomLatentSpaceComputedAt.DATASET
|
|
329
|
+
arg_names: Optional[List[str]] = None
|
|
330
|
+
reduce: Optional[LatentSpaceReduction] = None
|
|
331
|
+
n_components: int = 512
|
|
332
|
+
channel_axis: int = -1
|
|
320
333
|
|
|
321
334
|
|
|
322
335
|
# How a chain's latent-space vectors are derived from its per-step forward passes.
|
code_loader/contract/enums.py
CHANGED
|
@@ -68,3 +68,13 @@ class ConfusionMatrixValue(Enum):
|
|
|
68
68
|
class TestingSectionEnum(Enum):
|
|
69
69
|
Warnings = "Warnings"
|
|
70
70
|
Errors = "Errors"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class LatentSpaceReduction(Enum):
|
|
74
|
+
MEAN_POOL = 'MEAN_POOL'
|
|
75
|
+
RANDOM_PROJECTION = 'RANDOM_PROJECTION'
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CustomLatentSpaceComputedAt(Enum):
|
|
79
|
+
DATASET = 'dataset'
|
|
80
|
+
MODEL = 'model'
|
code_loader/contract/mapping.py
CHANGED
|
@@ -17,12 +17,12 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
|
|
|
17
17
|
CustomMultipleReturnCallableInterfaceMultiArgs, DatasetBaseHandler, custom_latent_space_attribute, \
|
|
18
18
|
RawInputsForHeatmap, VisualizerHandlerData, MetricHandlerData, CustomLossHandlerData, SamplePreprocessResponse, \
|
|
19
19
|
ElementInstanceMasksHandler, InstanceCallableInterface, InstanceSectionCallableInterface, \
|
|
20
|
-
CustomLatentSpaceHandler, InstanceMetricHandler, \
|
|
20
|
+
CustomLatentSpaceHandler, InstanceMetricHandler, MAX_CUSTOM_LATENT_SPACES, \
|
|
21
21
|
SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
|
|
22
22
|
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
|
|
23
23
|
AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
|
|
24
24
|
from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType, \
|
|
25
|
-
TestingSectionEnum
|
|
25
|
+
TestingSectionEnum, LatentSpaceReduction, CustomLatentSpaceComputedAt
|
|
26
26
|
from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
|
|
27
27
|
from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
|
|
28
28
|
from code_loader.contract.visualizer_classes import map_leap_data_type_to_visualizer_class
|
|
@@ -67,16 +67,24 @@ def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
|
|
|
67
67
|
return None
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
def _reject_stringized_annotation(function: Callable[..., Any], arg_name: str, annotation: Any,
|
|
71
|
+
expected_cls: type, why: str) -> None:
|
|
72
|
+
"""Fail fast on a stringized `expected_cls` arg (otherwise it silently mis-wires,
|
|
73
|
+
surfacing only on the platform)."""
|
|
74
|
+
if _stringized_annotation_type_name(annotation) == expected_cls.__name__:
|
|
74
75
|
raise Exception(
|
|
75
76
|
f"Argument '{arg_name}' of function '{function.__name__}' is annotated with a string "
|
|
76
|
-
f"('{annotation}') instead of the
|
|
77
|
+
f"('{annotation}') instead of the {expected_cls.__name__} type. This usually means the "
|
|
77
78
|
f"file uses 'from __future__ import annotations' (or a quoted hint), which stringizes "
|
|
78
|
-
f"annotations
|
|
79
|
-
|
|
79
|
+
f"annotations. {why} Remove that import (or the quotes).")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _reject_stringized_sample_preprocess_response(function: Callable[..., Any], arg_name: str, annotation: Any) -> None:
|
|
83
|
+
_reject_stringized_annotation(
|
|
84
|
+
function, arg_name, annotation, SamplePreprocessResponse,
|
|
85
|
+
"This breaks Tensorleap type detection, so it must be referenced as a real type.")
|
|
86
|
+
|
|
87
|
+
|
|
80
88
|
|
|
81
89
|
|
|
82
90
|
|
|
@@ -546,7 +554,12 @@ class LeapBinder:
|
|
|
546
554
|
def set_custom_latent_space(self, function: Union[SectionCallableInterface, InstanceSectionCallableInterface],
|
|
547
555
|
name: Optional[str] = None,
|
|
548
556
|
use_ls_for_analysis: bool = False,
|
|
549
|
-
instance_aware: bool = False
|
|
557
|
+
instance_aware: bool = False,
|
|
558
|
+
computed_at: CustomLatentSpaceComputedAt = CustomLatentSpaceComputedAt.DATASET,
|
|
559
|
+
arg_names: Optional[List[str]] = None,
|
|
560
|
+
reduce: Optional[LatentSpaceReduction] = None,
|
|
561
|
+
n_components: int = 512,
|
|
562
|
+
channel_axis: int = -1) -> None:
|
|
550
563
|
"""
|
|
551
564
|
Register a custom latent space function.
|
|
552
565
|
|
|
@@ -579,6 +592,14 @@ class LeapBinder:
|
|
|
579
592
|
f"@tensorleap_custom_latent_space must have a unique name "
|
|
580
593
|
f"(pass name='...' to distinguish them)."
|
|
581
594
|
)
|
|
595
|
+
if len(self.setup_container.custom_latent_spaces) >= MAX_CUSTOM_LATENT_SPACES:
|
|
596
|
+
raise Exception(
|
|
597
|
+
f"Cannot register custom latent space '{name}': Tensorleap supports at most "
|
|
598
|
+
f"{MAX_CUSTOM_LATENT_SPACES} custom latent spaces, and "
|
|
599
|
+
f"{len(self.setup_container.custom_latent_spaces)} are already registered "
|
|
600
|
+
f"({sorted(self.setup_container.custom_latent_spaces)}). Dataset-computed and "
|
|
601
|
+
f"model-computed latent spaces share this budget."
|
|
602
|
+
)
|
|
582
603
|
# use_ls_for_analysis is not currently wired for instance-aware latent spaces (OOD /
|
|
583
604
|
# Domain-Gap analyze the sample-level population, not instance rows) — force it off rather
|
|
584
605
|
# than silently accepting a flag that has no effect.
|
|
@@ -602,8 +623,25 @@ class LeapBinder:
|
|
|
602
623
|
f"Out-Of-Distribution and Domain-Gap insights). Set it on '{name}' "
|
|
603
624
|
f"or '{already_flagged[0]}', not both."
|
|
604
625
|
)
|
|
626
|
+
if reduce is not None:
|
|
627
|
+
if not isinstance(reduce, LatentSpaceReduction):
|
|
628
|
+
raise Exception(
|
|
629
|
+
f"Custom latent space '{name}': reduce must be a LatentSpaceReduction, got "
|
|
630
|
+
f"{type(reduce).__name__}.")
|
|
631
|
+
if reduce is LatentSpaceReduction.RANDOM_PROJECTION and (
|
|
632
|
+
not isinstance(n_components, int) or isinstance(n_components, bool) or n_components <= 0):
|
|
633
|
+
raise Exception(
|
|
634
|
+
f"Custom latent space '{name}': n_components must be a positive int for "
|
|
635
|
+
f"LatentSpaceReduction.RANDOM_PROJECTION, got {n_components!r}.")
|
|
636
|
+
if reduce is LatentSpaceReduction.MEAN_POOL and (
|
|
637
|
+
not isinstance(channel_axis, int) or isinstance(channel_axis, bool)):
|
|
638
|
+
raise Exception(
|
|
639
|
+
f"Custom latent space '{name}': channel_axis must be an int for "
|
|
640
|
+
f"LatentSpaceReduction.MEAN_POOL, got {channel_axis!r}.")
|
|
605
641
|
self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
|
|
606
|
-
function=function, name=name, use_ls_for_analysis=use_ls_for_analysis, instance_aware=instance_aware
|
|
642
|
+
function=function, name=name, use_ls_for_analysis=use_ls_for_analysis, instance_aware=instance_aware,
|
|
643
|
+
computed_at=computed_at, arg_names=arg_names, reduce=reduce, n_components=n_components,
|
|
644
|
+
channel_axis=channel_axis)
|
|
607
645
|
|
|
608
646
|
def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
|
|
609
647
|
latent_space_aggregation: str = 'last_step') -> None:
|
|
@@ -7,6 +7,7 @@ import warnings
|
|
|
7
7
|
import logging
|
|
8
8
|
from collections import defaultdict
|
|
9
9
|
import functools
|
|
10
|
+
import typing
|
|
10
11
|
from functools import lru_cache
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Optional, Union, Callable, List, Dict, Set, Any
|
|
@@ -18,7 +19,7 @@ import numpy.typing as npt
|
|
|
18
19
|
from code_loader.utils import map_dict_to_metadata_types, is_absent_metadata_value, \
|
|
19
20
|
validate_autoregressive_state_types, autoregressive_nests_equal, \
|
|
20
21
|
simulate_engine_float16_downcast_on_call_args, ENGINE_STORAGE_DTYPE, \
|
|
21
|
-
TL_DISABLE_ENGINE_FLOAT16_SIMULATION_ENV_VAR
|
|
22
|
+
TL_DISABLE_ENGINE_FLOAT16_SIMULATION_ENV_VAR, sample_preprocess_response_arg_name
|
|
22
23
|
|
|
23
24
|
logger = logging.getLogger(__name__)
|
|
24
25
|
|
|
@@ -26,15 +27,17 @@ from code_loader.contract.datasetclasses import CustomCallableInterfaceMultiArgs
|
|
|
26
27
|
CustomMultipleReturnCallableInterfaceMultiArgs, ConfusionMatrixCallableInterfaceMultiArgs, CustomCallableInterface, \
|
|
27
28
|
VisualizerCallableInterface, MetadataSectionCallableInterface, PreprocessResponse, SectionCallableInterface, \
|
|
28
29
|
ConfusionMatrixElement, SamplePreprocessResponse, PredictionTypeHandler, InstanceCallableInterface, ElementInstance, \
|
|
29
|
-
InstanceLengthCallableInterface, InstanceSectionCallableInterface, AutoregressiveStepCallableInterface
|
|
30
|
+
InstanceLengthCallableInterface, InstanceSectionCallableInterface, AutoregressiveStepCallableInterface, \
|
|
31
|
+
MAX_CUSTOM_LATENT_SPACE_DIM, CUSTOM_LATENT_SPACE_WARN_DIM
|
|
30
32
|
from code_loader.contract.enums import MetricDirection, LeapDataType, DatasetMetadataType, DataStateType, \
|
|
31
|
-
DataStateEnum
|
|
33
|
+
DataStateEnum, LatentSpaceReduction, CustomLatentSpaceComputedAt
|
|
32
34
|
from code_loader import leap_binder, LeapLoader
|
|
33
35
|
from code_loader.contract.mapping import NodeMapping, NodeMappingType, NodeConnection
|
|
34
36
|
from code_loader.contract.visualizer_classes import LeapImage, LeapImageMask, LeapTextMask, LeapText, LeapGraph, \
|
|
35
37
|
LeapHorizontalBar, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapValidationError, \
|
|
36
38
|
map_leap_data_type_to_visualizer_class
|
|
37
|
-
from code_loader.inner_leap_binder.leapbinder import mapping_runtime_mode_env_var_mame
|
|
39
|
+
from code_loader.inner_leap_binder.leapbinder import mapping_runtime_mode_env_var_mame, \
|
|
40
|
+
_reject_stringized_sample_preprocess_response
|
|
38
41
|
from code_loader.mixpanel_tracker import clear_integration_events, AnalyticsEvent, emit_integration_event_once
|
|
39
42
|
|
|
40
43
|
_called_from_inside_tl_decorator = 0
|
|
@@ -264,7 +267,8 @@ def _validate_grouped_result(result, group_size, func_name, validate_single):
|
|
|
264
267
|
f'{group_size} arrays, got {type(result)}.')
|
|
265
268
|
|
|
266
269
|
|
|
267
|
-
def _add_mapping_connection(user_unique_name, connection_destinations, arg_names, name, node_mapping_type
|
|
270
|
+
def _add_mapping_connection(user_unique_name, connection_destinations, arg_names, name, node_mapping_type,
|
|
271
|
+
target_list=None):
|
|
268
272
|
connection_destinations = [connection_destination for connection_destination in connection_destinations
|
|
269
273
|
if not isinstance(connection_destination, SamplePreprocessResponse)]
|
|
270
274
|
|
|
@@ -274,7 +278,9 @@ def _add_mapping_connection(user_unique_name, connection_destinations, arg_names
|
|
|
274
278
|
for arg_name, destination in zip(arg_names, connection_destinations):
|
|
275
279
|
node_inputs[arg_name] = destination.node_mapping
|
|
276
280
|
|
|
277
|
-
|
|
281
|
+
if target_list is None:
|
|
282
|
+
target_list = leap_binder.mapping_connections
|
|
283
|
+
target_list.append(NodeConnection(main_node_mapping, node_inputs))
|
|
278
284
|
|
|
279
285
|
|
|
280
286
|
def _add_mapping_connections(connects_to, arg_names, node_mapping_type, name):
|
|
@@ -286,11 +292,7 @@ def _require_sample_preprocess_response_supplied(user_function: Callable, args:
|
|
|
286
292
|
"""A SamplePreprocessResponse argument is auto-injected by the platform / check_dataset
|
|
287
293
|
but NOT inside integration_test, where the author calls the function directly. Fail fast
|
|
288
294
|
with an actionable message instead of a raw 'missing argument' TypeError."""
|
|
289
|
-
spr_arg_name =
|
|
290
|
-
for arg_name, arg_type in inspect.getfullargspec(user_function).annotations.items():
|
|
291
|
-
if arg_type == SamplePreprocessResponse:
|
|
292
|
-
spr_arg_name = arg_name
|
|
293
|
-
break
|
|
295
|
+
spr_arg_name = sample_preprocess_response_arg_name(user_function)
|
|
294
296
|
if spr_arg_name is None:
|
|
295
297
|
return
|
|
296
298
|
signature = inspect.signature(user_function)
|
|
@@ -1750,7 +1752,175 @@ def tensorleap_metadata(
|
|
|
1750
1752
|
return decorating_function
|
|
1751
1753
|
|
|
1752
1754
|
|
|
1753
|
-
def
|
|
1755
|
+
def _is_preprocess_response_type(annotation: Any) -> bool:
|
|
1756
|
+
if isinstance(annotation, type):
|
|
1757
|
+
return issubclass(annotation, PreprocessResponse)
|
|
1758
|
+
return any(_is_preprocess_response_type(arg) for arg in get_args(annotation))
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
def _classify_custom_latent_space_signature(user_function) -> str:
|
|
1762
|
+
"""Dataset-computed when a parameter is typed PreprocessResponse (a subclass or an
|
|
1763
|
+
Optional[...] of it included), model-computed otherwise."""
|
|
1764
|
+
params = list(inspect.signature(user_function).parameters.values())
|
|
1765
|
+
try:
|
|
1766
|
+
hints = typing.get_type_hints(user_function)
|
|
1767
|
+
except NameError as e:
|
|
1768
|
+
raise Exception(
|
|
1769
|
+
f"tensorleap_custom_latent_space validation failed: could not resolve the type "
|
|
1770
|
+
f"annotations of '{user_function.__name__}' ({e}). Tensorleap reads them to tell a "
|
|
1771
|
+
f"dataset-computed latent space from a model-computed one, so every annotation, the "
|
|
1772
|
+
f"return type included, must be resolvable when the function is decorated. Import the "
|
|
1773
|
+
f"annotated types at module level (not only under TYPE_CHECKING) or remove the "
|
|
1774
|
+
f"annotation.") from e
|
|
1775
|
+
preprocess_params = [p.name for p in params if _is_preprocess_response_type(hints.get(p.name))]
|
|
1776
|
+
if preprocess_params:
|
|
1777
|
+
positional = [p.name for p in params
|
|
1778
|
+
if p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)]
|
|
1779
|
+
if positional[1:2] != preprocess_params[:1] or len(preprocess_params) > 1:
|
|
1780
|
+
raise Exception(
|
|
1781
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' is "
|
|
1782
|
+
f"dataset-computed (it takes a PreprocessResponse), so it is called as "
|
|
1783
|
+
f"{user_function.__name__}(sample_id, preprocess) and its PreprocessResponse must be "
|
|
1784
|
+
f"the second positional parameter, and the only one. Got parameters "
|
|
1785
|
+
f"{[p.name for p in params]} with PreprocessResponse on {preprocess_params}.")
|
|
1786
|
+
return 'dataset'
|
|
1787
|
+
|
|
1788
|
+
if len(params) == 2 and params[1].name not in hints:
|
|
1789
|
+
first, second = params[0].name, params[1].name
|
|
1790
|
+
raise Exception(
|
|
1791
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' has "
|
|
1792
|
+
f"exactly two parameters ('{first}', '{second}') and '{second}' has no type "
|
|
1793
|
+
f"annotation, so Tensorleap cannot tell whether this is a dataset-computed latent "
|
|
1794
|
+
f"space (one sample at a time) or a model-computed one (a batch of model tensors). "
|
|
1795
|
+
f"Please annotate the second parameter '{second}' to disambiguate:\n"
|
|
1796
|
+
f" dataset-computed: def {user_function.__name__}({first}, {second}: PreprocessResponse) -> (d,)\n"
|
|
1797
|
+
f" model-computed: def {user_function.__name__}({first}: np.ndarray, {second}: np.ndarray) -> (batch, d)\n"
|
|
1798
|
+
f"If you are upgrading an existing project, this signature used to be accepted "
|
|
1799
|
+
f"unannotated as dataset-computed; add ': PreprocessResponse' to '{second}' to keep "
|
|
1800
|
+
f"the previous behavior.")
|
|
1801
|
+
if len(params) > 2 and not any(p.name in hints for p in params):
|
|
1802
|
+
names = [p.name for p in params]
|
|
1803
|
+
raise Exception(
|
|
1804
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' has "
|
|
1805
|
+
f"parameters {names} and none of them has a type annotation, so Tensorleap cannot "
|
|
1806
|
+
f"tell whether this is a dataset-computed latent space (one sample at a time) or a "
|
|
1807
|
+
f"model-computed one (a batch of model tensors). Please annotate them:\n"
|
|
1808
|
+
f" dataset-computed: def {user_function.__name__}({names[0]}, {names[1]}: PreprocessResponse, ...) -> (d,)\n"
|
|
1809
|
+
f" model-computed: def {user_function.__name__}({names[0]}: np.ndarray, {names[1]}: np.ndarray, ...) -> (batch, d)")
|
|
1810
|
+
return 'model'
|
|
1811
|
+
|
|
1812
|
+
|
|
1813
|
+
def _model_latent_space_arg_names(user_function) -> List[str]:
|
|
1814
|
+
# inspect.signature follows functools.wraps' __wrapped__, as the classifier does.
|
|
1815
|
+
params = inspect.signature(user_function).parameters.values()
|
|
1816
|
+
arg_names = [p.name for p in params
|
|
1817
|
+
if p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)]
|
|
1818
|
+
spr_count = 0
|
|
1819
|
+
for param in params:
|
|
1820
|
+
if param.annotation is inspect.Parameter.empty:
|
|
1821
|
+
continue
|
|
1822
|
+
_reject_stringized_sample_preprocess_response(user_function, param.name, param.annotation)
|
|
1823
|
+
if param.annotation == SamplePreprocessResponse:
|
|
1824
|
+
spr_count += 1
|
|
1825
|
+
if spr_count > 1:
|
|
1826
|
+
raise Exception(
|
|
1827
|
+
f"tensorleap_custom_latent_space validation failed: only one argument of "
|
|
1828
|
+
f"'{user_function.__name__}' can be of type SamplePreprocessResponse.")
|
|
1829
|
+
spr_arg_name = sample_preprocess_response_arg_name(user_function)
|
|
1830
|
+
if spr_arg_name is not None:
|
|
1831
|
+
arg_names.remove(spr_arg_name)
|
|
1832
|
+
if not arg_names:
|
|
1833
|
+
raise Exception(
|
|
1834
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' is "
|
|
1835
|
+
f"model-computed and expects at least one np.ndarray argument, but its signature "
|
|
1836
|
+
f"declares none.")
|
|
1837
|
+
return arg_names
|
|
1838
|
+
|
|
1839
|
+
|
|
1840
|
+
def _custom_latent_space_width(result, ls_name, reduce, n_components, channel_axis,
|
|
1841
|
+
has_batch_axis: bool) -> int:
|
|
1842
|
+
if reduce is LatentSpaceReduction.RANDOM_PROJECTION:
|
|
1843
|
+
return n_components
|
|
1844
|
+
if reduce is LatentSpaceReduction.MEAN_POOL:
|
|
1845
|
+
axis = channel_axis if channel_axis >= 0 else result.ndim + channel_axis
|
|
1846
|
+
min_axis = 1 if has_batch_axis else 0
|
|
1847
|
+
if axis < min_axis or axis >= result.ndim:
|
|
1848
|
+
raise Exception(
|
|
1849
|
+
f"tensorleap_custom_latent_space validation failed: '{ls_name}' uses "
|
|
1850
|
+
f"LatentSpaceReduction.MEAN_POOL with channel_axis={channel_axis}, which does not "
|
|
1851
|
+
f"select a non-batch axis of the returned shape {tuple(result.shape)}."
|
|
1852
|
+
+ (" Axis 0 is the batch." if has_batch_axis else ""))
|
|
1853
|
+
return int(result.shape[axis])
|
|
1854
|
+
dims = result.shape[1:] if has_batch_axis else result.shape
|
|
1855
|
+
return int(np.prod(dims))
|
|
1856
|
+
|
|
1857
|
+
|
|
1858
|
+
def _check_custom_latent_space_width(result, ls_name, reduce, n_components, channel_axis,
|
|
1859
|
+
has_batch_axis: bool) -> None:
|
|
1860
|
+
width = _custom_latent_space_width(result, ls_name, reduce, n_components, channel_axis,
|
|
1861
|
+
has_batch_axis)
|
|
1862
|
+
if width > MAX_CUSTOM_LATENT_SPACE_DIM:
|
|
1863
|
+
raise Exception(
|
|
1864
|
+
f"tensorleap_custom_latent_space validation failed: '{ls_name}' produces "
|
|
1865
|
+
f"{width} dimensions per sample, above the {MAX_CUSTOM_LATENT_SPACE_DIM} limit. "
|
|
1866
|
+
f"Pass reduce=LatentSpaceReduction.RANDOM_PROJECTION (with n_components) to "
|
|
1867
|
+
f"project it down, or reduce=LatentSpaceReduction.MEAN_POOL (with channel_axis) "
|
|
1868
|
+
f"to average the non-channel axes, or return a smaller array.")
|
|
1869
|
+
if width > CUSTOM_LATENT_SPACE_WARN_DIM:
|
|
1870
|
+
store_general_warning(
|
|
1871
|
+
key=("tensorleap_custom_latent_space_width", ls_name, width),
|
|
1872
|
+
message=(
|
|
1873
|
+
f"Custom latent space '{ls_name}' produces {width} dimensions per sample. "
|
|
1874
|
+
f"Wide latent spaces are slow to transit and store. Consider "
|
|
1875
|
+
f"reduce=LatentSpaceReduction.MEAN_POOL or "
|
|
1876
|
+
f"reduce=LatentSpaceReduction.RANDOM_PROJECTION."))
|
|
1877
|
+
|
|
1878
|
+
|
|
1879
|
+
_FLOAT16_MAX = float(np.finfo(np.float16).max)
|
|
1880
|
+
|
|
1881
|
+
|
|
1882
|
+
def _check_custom_latent_space_values(result: np.ndarray, ls_name: str, has_batch_axis: bool) -> None:
|
|
1883
|
+
"""Reject what no sample could store and warn about values the engine handles per sample."""
|
|
1884
|
+
if isinstance(result, np.ma.MaskedArray):
|
|
1885
|
+
raise Exception(
|
|
1886
|
+
f"tensorleap_custom_latent_space validation failed: '{ls_name}' returned a numpy masked "
|
|
1887
|
+
f"array; the mask would be ignored. Return a plain ndarray, with np.nan for a sample "
|
|
1888
|
+
f"you cannot compute.")
|
|
1889
|
+
if result.dtype.kind not in 'biuf':
|
|
1890
|
+
raise Exception(
|
|
1891
|
+
f"tensorleap_custom_latent_space validation failed: '{ls_name}' returned dtype "
|
|
1892
|
+
f"{result.dtype}. Return a real-valued numeric array (bool, int or float), with np.nan "
|
|
1893
|
+
f"for a sample you cannot compute.")
|
|
1894
|
+
dims = result.shape[1:] if has_batch_axis else result.shape
|
|
1895
|
+
if int(np.prod(dims)) == 0:
|
|
1896
|
+
raise Exception(
|
|
1897
|
+
f"tensorleap_custom_latent_space validation failed: '{ls_name}' returned shape "
|
|
1898
|
+
f"{tuple(result.shape)}, which has no features per sample.")
|
|
1899
|
+
finite = np.isfinite(result)
|
|
1900
|
+
if not finite.all():
|
|
1901
|
+
store_general_warning(
|
|
1902
|
+
key=("tensorleap_custom_latent_space_nonfinite", ls_name),
|
|
1903
|
+
message=(
|
|
1904
|
+
f"Custom latent space '{ls_name}' returned NaN or inf. Those samples are left out of "
|
|
1905
|
+
f"this latent space; if they exceed 15% of the evaluated samples the latent space is "
|
|
1906
|
+
f"dropped."))
|
|
1907
|
+
peak = 0.0
|
|
1908
|
+
if result.size and result.dtype.kind == 'f':
|
|
1909
|
+
peak = float(np.max(np.abs(result), where=finite, initial=0.0))
|
|
1910
|
+
elif result.size and result.dtype.kind in 'iu':
|
|
1911
|
+
peak = float(max(abs(int(result.min())), abs(int(result.max()))))
|
|
1912
|
+
if peak > _FLOAT16_MAX:
|
|
1913
|
+
store_general_warning(
|
|
1914
|
+
key=("tensorleap_custom_latent_space_clipped", ls_name),
|
|
1915
|
+
message=(
|
|
1916
|
+
f"Custom latent space '{ls_name}' returned values beyond +-{_FLOAT16_MAX:.0f}. Latent "
|
|
1917
|
+
f"spaces are stored as float16, so they are clipped to that range; scale or normalize "
|
|
1918
|
+
f"them inside the function to keep their relative size."))
|
|
1919
|
+
|
|
1920
|
+
|
|
1921
|
+
def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False,
|
|
1922
|
+
reduce: Optional[LatentSpaceReduction] = None,
|
|
1923
|
+
n_components: int = 512, channel_axis: int = -1):
|
|
1754
1924
|
assert isinstance(use_ls_for_analysis, bool), \
|
|
1755
1925
|
("tensorleap_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
|
|
1756
1926
|
f"Got {type(use_ls_for_analysis)}.")
|
|
@@ -1758,6 +1928,10 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
|
|
|
1758
1928
|
def decorating_function(user_function: SectionCallableInterface):
|
|
1759
1929
|
ls_name = name if name is not None else user_function.__name__
|
|
1760
1930
|
|
|
1931
|
+
if _classify_custom_latent_space_signature(user_function) == 'model':
|
|
1932
|
+
return _decorate_model_latent_space(user_function, ls_name, use_ls_for_analysis,
|
|
1933
|
+
reduce, n_components, channel_axis)
|
|
1934
|
+
|
|
1761
1935
|
def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
|
|
1762
1936
|
_validate_id_or_group(sample_id, preprocess_response, 'tensorleap_custom_latent_space')
|
|
1763
1937
|
|
|
@@ -1765,6 +1939,7 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
|
|
|
1765
1939
|
assert isinstance(single_result, np.ndarray), \
|
|
1766
1940
|
(f'tensorleap_custom_latent_space validation failed: '
|
|
1767
1941
|
f'The return type should be a numpy array. Got {type(single_result)}.')
|
|
1942
|
+
_check_custom_latent_space_values(single_result, ls_name, has_batch_axis=False)
|
|
1768
1943
|
if single_result.ndim > 1:
|
|
1769
1944
|
flat_dim = int(np.prod(single_result.shape))
|
|
1770
1945
|
store_general_warning(
|
|
@@ -1777,6 +1952,8 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
|
|
|
1777
1952
|
f"inside your function."
|
|
1778
1953
|
),
|
|
1779
1954
|
)
|
|
1955
|
+
_check_custom_latent_space_width(single_result, ls_name, reduce, n_components, channel_axis,
|
|
1956
|
+
has_batch_axis=False)
|
|
1780
1957
|
|
|
1781
1958
|
def _validate_result(result, grouped=False, group_size=None):
|
|
1782
1959
|
if not grouped:
|
|
@@ -1814,7 +1991,9 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
|
|
|
1814
1991
|
return result
|
|
1815
1992
|
|
|
1816
1993
|
leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
|
|
1817
|
-
use_ls_for_analysis=use_ls_for_analysis
|
|
1994
|
+
use_ls_for_analysis=use_ls_for_analysis,
|
|
1995
|
+
computed_at=CustomLatentSpaceComputedAt.DATASET, reduce=reduce,
|
|
1996
|
+
n_components=n_components, channel_axis=channel_axis)
|
|
1818
1997
|
|
|
1819
1998
|
def inner(sample_id, preprocess_response):
|
|
1820
1999
|
if os.environ.get(mapping_runtime_mode_env_var_mame):
|
|
@@ -1834,6 +2013,98 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
|
|
|
1834
2013
|
return decorating_function
|
|
1835
2014
|
|
|
1836
2015
|
|
|
2016
|
+
def _decorate_model_latent_space(user_function, ls_name, use_ls_for_analysis, reduce, n_components,
|
|
2017
|
+
channel_axis):
|
|
2018
|
+
arg_names = _model_latent_space_arg_names(user_function)
|
|
2019
|
+
|
|
2020
|
+
def _validate_input_args(*args, **kwargs):
|
|
2021
|
+
# Every bound argument must be an array: a ground truth is never passed as None, so a
|
|
2022
|
+
# model-computed LS cannot fall back from ground truth to predictions on unlabeled rows.
|
|
2023
|
+
# The engine skips the LS for those rows instead (see leaploader._check_model_latent_spaces).
|
|
2024
|
+
assert len(args) + len(kwargs) > 0, (
|
|
2025
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' is "
|
|
2026
|
+
f"model-computed and expects at least one np.ndarray argument, but received none.")
|
|
2027
|
+
for i, arg in enumerate(args):
|
|
2028
|
+
assert isinstance(arg, (np.ndarray, SamplePreprocessResponse)), (
|
|
2029
|
+
f"tensorleap_custom_latent_space validation failed: Argument #{i} of "
|
|
2030
|
+
f"'{user_function.__name__}' should be a numpy array. Got {type(arg)}.")
|
|
2031
|
+
for arg_name, arg in kwargs.items():
|
|
2032
|
+
assert isinstance(arg, (np.ndarray, SamplePreprocessResponse)), (
|
|
2033
|
+
f"tensorleap_custom_latent_space validation failed: Argument {arg_name} of "
|
|
2034
|
+
f"'{user_function.__name__}' should be a numpy array. Got {type(arg)}.")
|
|
2035
|
+
|
|
2036
|
+
def _validate_result(result):
|
|
2037
|
+
assert isinstance(result, np.ndarray), (
|
|
2038
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' is "
|
|
2039
|
+
f"model-computed and should return a numpy array of shape (batch, d). "
|
|
2040
|
+
f"Got {type(result)}.")
|
|
2041
|
+
assert result.ndim >= 2, (
|
|
2042
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' "
|
|
2043
|
+
f"returned shape {tuple(result.shape)}. A model-computed latent space returns "
|
|
2044
|
+
f"(batch, d), so the result needs a batch axis and at least one feature axis.")
|
|
2045
|
+
_check_custom_latent_space_values(result, ls_name, has_batch_axis=True)
|
|
2046
|
+
if leap_binder.batch_size_to_validate:
|
|
2047
|
+
assert result.shape[0] == leap_binder.batch_size_to_validate, (
|
|
2048
|
+
f"tensorleap_custom_latent_space validation failed: '{user_function.__name__}' "
|
|
2049
|
+
f"returned leading dim {result.shape[0]} instead of the batch size "
|
|
2050
|
+
f"{leap_binder.batch_size_to_validate}.")
|
|
2051
|
+
|
|
2052
|
+
_check_custom_latent_space_width(result, ls_name, reduce, n_components, channel_axis,
|
|
2053
|
+
has_batch_axis=True)
|
|
2054
|
+
|
|
2055
|
+
def inner_without_validate(*args, **kwargs):
|
|
2056
|
+
global _called_from_inside_tl_decorator
|
|
2057
|
+
_called_from_inside_tl_decorator += 1
|
|
2058
|
+
|
|
2059
|
+
try:
|
|
2060
|
+
_require_sample_preprocess_response_supplied(user_function, args, kwargs)
|
|
2061
|
+
result = user_function(*args, **kwargs)
|
|
2062
|
+
finally:
|
|
2063
|
+
_called_from_inside_tl_decorator -= 1
|
|
2064
|
+
|
|
2065
|
+
return result
|
|
2066
|
+
|
|
2067
|
+
inner_without_validate.__signature__ = inspect.signature(user_function)
|
|
2068
|
+
|
|
2069
|
+
leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
|
|
2070
|
+
use_ls_for_analysis=use_ls_for_analysis,
|
|
2071
|
+
computed_at=CustomLatentSpaceComputedAt.MODEL, arg_names=arg_names, reduce=reduce,
|
|
2072
|
+
n_components=n_components, channel_axis=channel_axis)
|
|
2073
|
+
|
|
2074
|
+
def inner(*args, **kwargs):
|
|
2075
|
+
args, kwargs, _was_downcast = _simulate_engine_tensor_dtype(args, kwargs)
|
|
2076
|
+
_validate_input_args(*args, **kwargs)
|
|
2077
|
+
result = inner_without_validate(*args, **kwargs)
|
|
2078
|
+
_validate_result(result)
|
|
2079
|
+
return result
|
|
2080
|
+
|
|
2081
|
+
def mapping_inner(*args, **kwargs):
|
|
2082
|
+
if _mapping_dataset_is_grouped:
|
|
2083
|
+
raise LeapValidationError(
|
|
2084
|
+
f"tensorleap_custom_latent_space validation failed: '{ls_name}' is model-computed, "
|
|
2085
|
+
f"which is not supported for a grouped preprocess response. Use the "
|
|
2086
|
+
f"(sample_id, preprocess: PreprocessResponse) form instead.")
|
|
2087
|
+
|
|
2088
|
+
ordered_connections = [kwargs[arg_name] for arg_name in arg_names if arg_name in kwargs]
|
|
2089
|
+
ordered_connections = list(args) + ordered_connections
|
|
2090
|
+
|
|
2091
|
+
leap_binder.mapping_connections[:] = [
|
|
2092
|
+
connection for connection in leap_binder.mapping_connections
|
|
2093
|
+
if not (connection.node.type is NodeMappingType.CustomLatentSpace
|
|
2094
|
+
and connection.node.name == ls_name)]
|
|
2095
|
+
_add_mapping_connection(ls_name, ordered_connections, arg_names, ls_name,
|
|
2096
|
+
NodeMappingType.CustomLatentSpace)
|
|
2097
|
+
return None
|
|
2098
|
+
|
|
2099
|
+
@functools.wraps(user_function)
|
|
2100
|
+
def final_inner(*args, **kwargs):
|
|
2101
|
+
if os.environ.get(mapping_runtime_mode_env_var_mame):
|
|
2102
|
+
return mapping_inner(*args, **kwargs)
|
|
2103
|
+
return inner(*args, **kwargs)
|
|
2104
|
+
|
|
2105
|
+
return final_inner
|
|
2106
|
+
|
|
2107
|
+
|
|
1837
2108
|
def tensorleap_instance_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False):
|
|
1838
2109
|
assert isinstance(use_ls_for_analysis, bool), \
|
|
1839
2110
|
("tensorleap_instance_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
|
|
@@ -1859,6 +2130,7 @@ def tensorleap_instance_custom_latent_space(name: Optional[str] = None, use_ls_f
|
|
|
1859
2130
|
assert isinstance(result, np.ndarray), \
|
|
1860
2131
|
(f'tensorleap_instance_custom_latent_space validation failed: '
|
|
1861
2132
|
f'The return type should be a numpy array. Got {type(result)}.')
|
|
2133
|
+
_check_custom_latent_space_values(result, ls_name, has_batch_axis=False)
|
|
1862
2134
|
if result.ndim > 1:
|
|
1863
2135
|
flat_dim = int(np.prod(result.shape))
|
|
1864
2136
|
store_general_warning(
|
code_loader/leaploader.py
CHANGED
|
@@ -17,7 +17,9 @@ from code_loader.contract.datasetclasses import DatasetSample, DatasetBaseHandle
|
|
|
17
17
|
PredictionTypeHandler, MetadataHandler, CustomLayerHandler, MetricHandler, VisualizerHandlerData, MetricHandlerData, \
|
|
18
18
|
MetricCallableReturnType, CustomLossHandlerData, CustomLossHandler, RawInputsForHeatmap, SamplePreprocessResponse, \
|
|
19
19
|
ElementInstance, custom_latent_space_attribute, DatasetIntegrationSetup, InstanceMetricHandler, _simulation_context
|
|
20
|
-
from code_loader.contract.enums import DataStateEnum, TestingSectionEnum, DataStateType, DatasetMetadataType
|
|
20
|
+
from code_loader.contract.enums import DataStateEnum, TestingSectionEnum, DataStateType, DatasetMetadataType, \
|
|
21
|
+
CustomLatentSpaceComputedAt
|
|
22
|
+
from code_loader.contract.mapping import NodeMappingType
|
|
21
23
|
from code_loader.contract.exceptions import DatasetScriptException
|
|
22
24
|
from code_loader.contract.responsedataclasses import DatasetIntegParseResult, DatasetTestResultPayload, \
|
|
23
25
|
DatasetPreprocess, DatasetSetup, DatasetInputInstance, DatasetOutputInstance, DatasetMetadataInstance, \
|
|
@@ -28,7 +30,8 @@ from code_loader.inner_leap_binder import global_leap_binder
|
|
|
28
30
|
from code_loader.inner_leap_binder.leapbinder import mapping_runtime_mode_env_var_mame
|
|
29
31
|
from code_loader.leaploaderbase import LeapLoaderBase
|
|
30
32
|
from code_loader.utils import get_root_exception_file_and_line_number, get_metadata_type_from_variable, \
|
|
31
|
-
validate_autoregressive_state_types, autoregressive_nests_equal, is_absent_metadata_value
|
|
33
|
+
validate_autoregressive_state_types, autoregressive_nests_equal, is_absent_metadata_value, \
|
|
34
|
+
sample_preprocess_response_arg_name
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
def _serialize_sim_bounds(bounds) -> dict:
|
|
@@ -352,6 +355,9 @@ class LeapLoader(LeapLoaderBase):
|
|
|
352
355
|
instance_ls_test_payload = self._check_instance_custom_latent_spaces()
|
|
353
356
|
if instance_ls_test_payload is not None:
|
|
354
357
|
test_payloads.append(instance_ls_test_payload)
|
|
358
|
+
model_ls_test_payload = self._check_model_latent_spaces()
|
|
359
|
+
if model_ls_test_payload is not None:
|
|
360
|
+
test_payloads.append(model_ls_test_payload)
|
|
355
361
|
handlers_test_payloads = self._check_handlers()
|
|
356
362
|
test_payloads.extend(handlers_test_payloads)
|
|
357
363
|
simulation_test_payloads = self._check_simulations()
|
|
@@ -386,8 +392,9 @@ class LeapLoader(LeapLoaderBase):
|
|
|
386
392
|
is_valid_for_model=is_valid_for_model, setup=setup_response,
|
|
387
393
|
model_setup=model_setup, general_error=general_error,
|
|
388
394
|
print_log=print_log,
|
|
389
|
-
engine_file_contract=EngineFileContract(
|
|
390
|
-
|
|
395
|
+
engine_file_contract=EngineFileContract(
|
|
396
|
+
global_leap_binder.mapping_connections,
|
|
397
|
+
global_leap_binder.leap_analysis_configuration))
|
|
391
398
|
|
|
392
399
|
def _check_integration_test_exists(self) -> DatasetTestResultPayload:
|
|
393
400
|
test_result = DatasetTestResultPayload('integration_test')
|
|
@@ -425,6 +432,42 @@ class LeapLoader(LeapLoaderBase):
|
|
|
425
432
|
test_result.is_passed = False
|
|
426
433
|
return test_result
|
|
427
434
|
|
|
435
|
+
def _check_model_latent_spaces(self) -> Optional[DatasetTestResultPayload]:
|
|
436
|
+
model_names = [
|
|
437
|
+
name for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items()
|
|
438
|
+
if handler.computed_at == CustomLatentSpaceComputedAt.MODEL
|
|
439
|
+
]
|
|
440
|
+
if not model_names:
|
|
441
|
+
return None
|
|
442
|
+
|
|
443
|
+
test_result = DatasetTestResultPayload('model_custom_latent_space')
|
|
444
|
+
grouped_states = [
|
|
445
|
+
state.name for state, preprocess_response in self._preprocess_result().items()
|
|
446
|
+
if preprocess_response.is_grouped
|
|
447
|
+
]
|
|
448
|
+
if grouped_states:
|
|
449
|
+
test_result.is_passed = False
|
|
450
|
+
test_result.display[TestingSectionEnum.Errors.name] = (
|
|
451
|
+
f"Model-computed custom latent space(s) {model_names} are not supported with a "
|
|
452
|
+
f"grouped preprocess response (grouped: {grouped_states}). Use the "
|
|
453
|
+
f"(sample_id, preprocess: PreprocessResponse) form instead."
|
|
454
|
+
)
|
|
455
|
+
if global_leap_binder.setup_container.unlabeled_data_preprocess is not None:
|
|
456
|
+
gt_bound_names = [
|
|
457
|
+
connection.node.name for connection in global_leap_binder.mapping_connections
|
|
458
|
+
if connection.node.type is NodeMappingType.CustomLatentSpace
|
|
459
|
+
and any(node_input.type == NodeMappingType.GroundTruth
|
|
460
|
+
for node_input in (connection.node_inputs or {}).values())
|
|
461
|
+
]
|
|
462
|
+
if gt_bound_names:
|
|
463
|
+
test_result.display[TestingSectionEnum.Warnings.name] = (
|
|
464
|
+
f"Model-computed custom latent space(s) {gt_bound_names} read a ground truth, so "
|
|
465
|
+
f"they are skipped for unlabeled samples, which will have no vector in them. A "
|
|
466
|
+
f"latent space cannot fall back from ground truth to predictions; to cover "
|
|
467
|
+
f"unlabeled samples, add one bound only to model predictions."
|
|
468
|
+
)
|
|
469
|
+
return test_result
|
|
470
|
+
|
|
428
471
|
def _check_instance_custom_latent_spaces(self) -> Optional[DatasetTestResultPayload]:
|
|
429
472
|
instance_aware_names = [
|
|
430
473
|
name for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items()
|
|
@@ -773,10 +816,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
773
816
|
@staticmethod
|
|
774
817
|
def _get_preprocess_response_arg_name(
|
|
775
818
|
func: Callable) -> Optional[str]:
|
|
776
|
-
|
|
777
|
-
if arg_type == SamplePreprocessResponse:
|
|
778
|
-
return arg_name
|
|
779
|
-
return None
|
|
819
|
+
return sample_preprocess_response_arg_name(func)
|
|
780
820
|
|
|
781
821
|
def run_custom_loss(self, custom_loss_name: str, sample_ids: np.array, state: DataStateEnum,
|
|
782
822
|
input_tensors_by_arg_name: Dict[str, npt.NDArray[np.float32]]):
|
|
@@ -1166,6 +1206,14 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1166
1206
|
if any(handler.instance_aware for handler in latent_handlers.values()):
|
|
1167
1207
|
raise Exception(
|
|
1168
1208
|
"instance-aware custom latent spaces are not supported with grouped preprocess responses")
|
|
1209
|
+
# TODO: support model-computed custom latent spaces on grouped datasets (skip them here,
|
|
1210
|
+
# as the per-sample path does, once the grouped metrics path is verified to compute them).
|
|
1211
|
+
model_ls_names = [name for name, handler in latent_handlers.items()
|
|
1212
|
+
if handler.computed_at == CustomLatentSpaceComputedAt.MODEL]
|
|
1213
|
+
if model_ls_names:
|
|
1214
|
+
raise Exception(
|
|
1215
|
+
f"model-computed custom latent spaces {model_ls_names} are not supported with "
|
|
1216
|
+
f"grouped preprocess responses yet")
|
|
1169
1217
|
custom_latent_spaces = {
|
|
1170
1218
|
name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
1171
1219
|
for name, handler in latent_handlers.items()
|
|
@@ -1390,7 +1438,9 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1390
1438
|
sample_id: Union[int, str],
|
|
1391
1439
|
preprocess: "PreprocessResponse",
|
|
1392
1440
|
instance_id: Optional[int] = None) -> Optional[Dict[str, npt.NDArray[np.float32]]]:
|
|
1393
|
-
handlers =
|
|
1441
|
+
handlers = {handler_name: handler for handler_name, handler
|
|
1442
|
+
in global_leap_binder.setup_container.custom_latent_spaces.items()
|
|
1443
|
+
if handler.computed_at != CustomLatentSpaceComputedAt.MODEL}
|
|
1394
1444
|
if not handlers:
|
|
1395
1445
|
return None
|
|
1396
1446
|
if preprocess.is_grouped:
|
|
@@ -1428,6 +1478,47 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1428
1478
|
self.exec_script()
|
|
1429
1479
|
return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
|
|
1430
1480
|
|
|
1481
|
+
@lru_cache()
|
|
1482
|
+
def get_dataset_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
1483
|
+
self.exec_script()
|
|
1484
|
+
return tuple(name for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items()
|
|
1485
|
+
if handler.computed_at != CustomLatentSpaceComputedAt.MODEL)
|
|
1486
|
+
|
|
1487
|
+
@lru_cache()
|
|
1488
|
+
def get_custom_latent_space_specs(self) -> Dict[str, Dict[str, Any]]:
|
|
1489
|
+
self.exec_script()
|
|
1490
|
+
return {
|
|
1491
|
+
name: {
|
|
1492
|
+
'computed_at': handler.computed_at.value,
|
|
1493
|
+
'arg_names': list(handler.arg_names or []),
|
|
1494
|
+
'reduce': handler.reduce.value if handler.reduce is not None else None,
|
|
1495
|
+
'n_components': handler.n_components,
|
|
1496
|
+
'channel_axis': handler.channel_axis,
|
|
1497
|
+
}
|
|
1498
|
+
for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items()
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
@lru_cache()
|
|
1502
|
+
def get_model_custom_latent_space_specs(self) -> Dict[str, Dict[str, Any]]:
|
|
1503
|
+
return {name: spec for name, spec in self.get_custom_latent_space_specs().items()
|
|
1504
|
+
if spec['computed_at'] == 'model'}
|
|
1505
|
+
|
|
1506
|
+
def run_model_latent_space(self, ls_name: str, sample_ids: np.array, state: DataStateEnum,
|
|
1507
|
+
input_tensors_by_arg_name: Dict[str, npt.NDArray[np.float32]]
|
|
1508
|
+
) -> npt.NDArray[np.float32]:
|
|
1509
|
+
self.exec_script()
|
|
1510
|
+
handler = global_leap_binder.setup_container.custom_latent_spaces[ls_name]
|
|
1511
|
+
preprocess_response_arg_name = self._get_preprocess_response_arg_name(handler.function)
|
|
1512
|
+
|
|
1513
|
+
# Preprocess runs only when the function asks for a SamplePreprocessResponse; the metrics
|
|
1514
|
+
# pod that calls this has no other reason to pay for it.
|
|
1515
|
+
if preprocess_response_arg_name is not None:
|
|
1516
|
+
input_tensors_by_arg_name = {
|
|
1517
|
+
**input_tensors_by_arg_name,
|
|
1518
|
+
preprocess_response_arg_name: SamplePreprocessResponse(sample_ids, self._preprocess_result()[state])}
|
|
1519
|
+
|
|
1520
|
+
return handler.function(**input_tensors_by_arg_name)
|
|
1521
|
+
|
|
1431
1522
|
@lru_cache()
|
|
1432
1523
|
def get_instance_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
1433
1524
|
"""Names of registered custom latent spaces that are instance-aware.
|
code_loader/leaploaderbase.py
CHANGED
|
@@ -226,6 +226,31 @@ class LeapLoaderBase:
|
|
|
226
226
|
def get_custom_latent_space_for_analysis(self) -> Optional[str]:
|
|
227
227
|
pass
|
|
228
228
|
|
|
229
|
+
# These raise rather than `pass` for the same reason as the autoregressive entry points
|
|
230
|
+
# above: an un-overridden `pass` body would report "no model-computed latent spaces" on a
|
|
231
|
+
# loader that simply predates them, and the engine would silently skip computing them.
|
|
232
|
+
@abstractmethod
|
|
233
|
+
def get_dataset_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
234
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
235
|
+
'get_dataset_custom_latent_space_names.')
|
|
236
|
+
|
|
237
|
+
@abstractmethod
|
|
238
|
+
def get_custom_latent_space_specs(self) -> Dict[str, Dict[str, Any]]:
|
|
239
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
240
|
+
'get_custom_latent_space_specs.')
|
|
241
|
+
|
|
242
|
+
@abstractmethod
|
|
243
|
+
def get_model_custom_latent_space_specs(self) -> Dict[str, Dict[str, Any]]:
|
|
244
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
245
|
+
'get_model_custom_latent_space_specs.')
|
|
246
|
+
|
|
247
|
+
@abstractmethod
|
|
248
|
+
def run_model_latent_space(self, ls_name: str, sample_ids: np.array, state: DataStateEnum,
|
|
249
|
+
input_tensors_by_arg_name: Dict[str, npt.NDArray[np.float32]]
|
|
250
|
+
) -> npt.NDArray[np.float32]:
|
|
251
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
252
|
+
'run_model_latent_space.')
|
|
253
|
+
|
|
229
254
|
@abstractmethod
|
|
230
255
|
def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
|
|
231
256
|
pass
|
code_loader/utils.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
import io
|
|
2
3
|
import math
|
|
3
4
|
import os
|
|
@@ -11,7 +12,7 @@ import numpy as np
|
|
|
11
12
|
import numpy.typing as npt
|
|
12
13
|
|
|
13
14
|
from code_loader.contract.datasetclasses import SectionCallableInterface, PreprocessResponse, \
|
|
14
|
-
InstanceCallableInterface, ElementInstance
|
|
15
|
+
InstanceCallableInterface, ElementInstance, SamplePreprocessResponse
|
|
15
16
|
from code_loader.contract.enums import DatasetMetadataType
|
|
16
17
|
|
|
17
18
|
|
|
@@ -246,3 +247,11 @@ def autoregressive_nests_equal(a: Any, b: Any) -> bool:
|
|
|
246
247
|
math.isnan(a) and math.isnan(b):
|
|
247
248
|
return True
|
|
248
249
|
return bool(a == b)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def sample_preprocess_response_arg_name(func: Callable[..., Any]) -> Optional[str]:
|
|
253
|
+
# inspect.signature follows functools.wraps' __wrapped__; getfullargspec does not.
|
|
254
|
+
for param in inspect.signature(func).parameters.values():
|
|
255
|
+
if param.annotation == SamplePreprocessResponse:
|
|
256
|
+
return param.name
|
|
257
|
+
return None
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
|
|
2
2
|
code_loader/__init__.py,sha256=outxRQ0M-zMfV0QGVJmAed5qWfRmyD0TV6-goEGAzBw,406
|
|
3
3
|
code_loader/contract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
code_loader/contract/datasetclasses.py,sha256=
|
|
5
|
-
code_loader/contract/enums.py,sha256=
|
|
4
|
+
code_loader/contract/datasetclasses.py,sha256=yNEPIyZqCO54fKBgmgbFIKEMVBzvpf4n8U2zmPhXv88,18110
|
|
5
|
+
code_loader/contract/enums.py,sha256=R4Ge9-oSO7-GYjZAKMTB8Ws5FNsxhCQAYAJruxViJxk,1886
|
|
6
6
|
code_loader/contract/exceptions.py,sha256=jWqu5i7t-0IG0jGRsKF4DjJdrsdpJjIYpUkN1F4RiyQ,51
|
|
7
|
-
code_loader/contract/mapping.py,sha256=
|
|
7
|
+
code_loader/contract/mapping.py,sha256=i-yVyFuGUITL5lchPNfYlu7bhwk-yuoHPacAeFhcYr8,1490
|
|
8
8
|
code_loader/contract/responsedataclasses.py,sha256=2SQCccuIlSeUJT0igyvIJRYtmaWSqqlQwtaAA1iEuSI,5044
|
|
9
9
|
code_loader/contract/sim_config.py,sha256=le8KMALZiP0WU4UcuKnTOSWBW2rNjpnWYfII502NqDM,3493
|
|
10
10
|
code_loader/contract/visualizer_classes.py,sha256=vzX9YcwxKOm3IpYj8OaqsA1odPlRgj2Cfvglwd88Wbw,18213
|
|
@@ -21,18 +21,18 @@ code_loader/experiment_api/types.py,sha256=MY8xFARHwdVA7p4dxyhD60ShmttgTvb4qdp1o
|
|
|
21
21
|
code_loader/experiment_api/utils.py,sha256=XZHtxge12TS4H4-8PjV3sKuhp8Ud6ojAiIzTZJEqBqc,3304
|
|
22
22
|
code_loader/experiment_api/workingspace_config_utils.py,sha256=DLzXQCg4dgTV_YgaSbeTVzq-2ja_SQw4zi7LXwKL9cY,990
|
|
23
23
|
code_loader/inner_leap_binder/__init__.py,sha256=koOlJyMNYzGbEsoIbXathSmQ-L38N_pEXH_HvL7beXU,99
|
|
24
|
-
code_loader/inner_leap_binder/leapbinder.py,sha256=
|
|
25
|
-
code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=
|
|
26
|
-
code_loader/leaploader.py,sha256=
|
|
27
|
-
code_loader/leaploaderbase.py,sha256=
|
|
24
|
+
code_loader/inner_leap_binder/leapbinder.py,sha256=fplNN3TfJHg79YpEPHKK3b6CxrR7W0ugDeHhAZfYty0,62784
|
|
25
|
+
code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=WNaV9M02Mh8OkPiOrZlnmj_GquGKTa1IxIsaLNk6R2E,212474
|
|
26
|
+
code_loader/leaploader.py,sha256=GAGkIWJIt9g_a2Aj4ZakBBcKymbQD7BN-EcuCDlwD70,97599
|
|
27
|
+
code_loader/leaploaderbase.py,sha256=JzgpEfY-kNUWbWUb_cZ1G1zHo6oSci6QhT7hqw3E1Jg,12371
|
|
28
28
|
code_loader/mixpanel_tracker.py,sha256=rNwRmFifNbdUoqLQvvhhgpKczWpWiEmd8MfyJe27sxw,9131
|
|
29
29
|
code_loader/plot_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
30
|
code_loader/plot_functions/plot_functions.py,sha256=2DC-zlVaN13P4VNx5d8csgs80C6SisaeP1-Kq2LW7iM,16075
|
|
31
31
|
code_loader/plot_functions/visualize.py,sha256=gsBAYYkwMh7jIpJeDMPS8G4CW-pxwx6LznoQIvi4vpo,657
|
|
32
|
-
code_loader/utils.py,sha256=
|
|
32
|
+
code_loader/utils.py,sha256=Yuv2hPx4G70RV2x0Fc_UCube9BLpEryyq4cuKIcsq7s,12347
|
|
33
33
|
code_loader/visualizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
34
|
code_loader/visualizers/default_visualizers.py,sha256=grTPin_lCE9aci8i8CqA7DqQwAyXRB7_EamA3na_pls,5438
|
|
35
|
-
code_loader-1.0.
|
|
36
|
-
code_loader-1.0.
|
|
37
|
-
code_loader-1.0.
|
|
38
|
-
code_loader-1.0.
|
|
35
|
+
code_loader-1.0.208.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
|
|
36
|
+
code_loader-1.0.208.dist-info/METADATA,sha256=mDaljR7xkrd2QeONwX9U9msymlqphtsV1OkYbezdIMQ,1090
|
|
37
|
+
code_loader-1.0.208.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
38
|
+
code_loader-1.0.208.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|