code-loader 1.0.204.dev1__tar.gz → 1.0.204.dev5__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.
Files changed (37) hide show
  1. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/PKG-INFO +1 -1
  2. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/datasetclasses.py +1 -1
  3. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/inner_leap_binder/leapbinder.py +12 -1
  4. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/inner_leap_binder/leapbinder_decorators.py +57 -5
  5. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/utils.py +75 -0
  6. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/pyproject.toml +1 -1
  7. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/LICENSE +0 -0
  8. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/README.md +0 -0
  9. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/__init__.py +0 -0
  10. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/__init__.py +0 -0
  11. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/enums.py +0 -0
  12. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/exceptions.py +0 -0
  13. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/mapping.py +0 -0
  14. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/responsedataclasses.py +0 -0
  15. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/sim_config.py +0 -0
  16. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/contract/visualizer_classes.py +0 -0
  17. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/default_losses.py +0 -0
  18. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/default_metrics.py +0 -0
  19. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/__init__.py +0 -0
  20. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/api.py +0 -0
  21. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/cli_config_utils.py +0 -0
  22. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/client.py +0 -0
  23. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/epoch.py +0 -0
  24. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/experiment.py +0 -0
  25. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/experiment_context.py +0 -0
  26. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/types.py +0 -0
  27. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/utils.py +0 -0
  28. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  29. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/inner_leap_binder/__init__.py +0 -0
  30. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/leaploader.py +0 -0
  31. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/leaploaderbase.py +0 -0
  32. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/mixpanel_tracker.py +0 -0
  33. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/plot_functions/__init__.py +0 -0
  34. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/plot_functions/plot_functions.py +0 -0
  35. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/plot_functions/visualize.py +0 -0
  36. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.204.dev1 → code_loader-1.0.204.dev5}/code_loader/visualizers/default_visualizers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.204.dev1
3
+ Version: 1.0.204.dev5
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -309,7 +309,7 @@ class MetadataHandler:
309
309
 
310
310
  @dataclass
311
311
  class CustomLatentSpaceHandler:
312
- function: SectionCallableInterface
312
+ function: Union[SectionCallableInterface, InstanceSectionCallableInterface]
313
313
  name: str = 'custom_latent_space'
314
314
  use_ls_for_analysis: bool = False
315
315
  instance_aware: bool = False
@@ -2,6 +2,7 @@
2
2
  import builtins
3
3
  import inspect
4
4
  import os
5
+ import warnings
5
6
  from contextlib import contextmanager
6
7
  from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast, Iterator, Set
7
8
 
@@ -565,7 +566,8 @@ class LeapBinder:
565
566
  use_ls_for_analysis (bool): When True, the engine uses this custom latent
566
567
  space for the Out-Of-Distribution and Domain-Gap insights instead of the
567
568
  built-in defaults. At most one registered custom latent space may set this;
568
- registering a second one with the flag raises.
569
+ registering a second one with the flag raises. Not currently supported when
570
+ instance_aware=True — it is ignored (with a warning) and forced to False.
569
571
  instance_aware (bool): When True, `function` takes a third `instance_id` argument
570
572
  and is called once per element-instance row instead of once per sample.
571
573
  """
@@ -577,6 +579,15 @@ class LeapBinder:
577
579
  f"@tensorleap_custom_latent_space must have a unique name "
578
580
  f"(pass name='...' to distinguish them)."
579
581
  )
582
+ # use_ls_for_analysis is not currently wired for instance-aware latent spaces (OOD /
583
+ # Domain-Gap analyze the sample-level population, not instance rows) — force it off rather
584
+ # than silently accepting a flag that has no effect.
585
+ if instance_aware and use_ls_for_analysis:
586
+ warnings.warn(
587
+ f"use_ls_for_analysis=True is not currently supported for an instance-aware custom "
588
+ f"latent space ('{name}'). Ignoring it; the flag will be set to False."
589
+ )
590
+ use_ls_for_analysis = False
580
591
  if use_ls_for_analysis:
581
592
  already_flagged = [
582
593
  existing_name
@@ -16,7 +16,9 @@ import numpy as np
16
16
  import numpy.typing as npt
17
17
 
18
18
  from code_loader.utils import get_metadata_type_from_variable, map_dict_to_metadata_types, \
19
- validate_autoregressive_state_types, autoregressive_nests_equal
19
+ validate_autoregressive_state_types, autoregressive_nests_equal, \
20
+ simulate_engine_float16_downcast_on_call_args, ENGINE_STORAGE_DTYPE, \
21
+ TL_DISABLE_ENGINE_FLOAT16_SIMULATION_ENV_VAR
20
22
 
21
23
  logger = logging.getLogger(__name__)
22
24
 
@@ -308,6 +310,35 @@ def _require_sample_preprocess_response_supplied(user_function: Callable, args:
308
310
  f"SamplePreprocessResponse(sample_id, preprocess)).")
309
311
 
310
312
 
313
+ def _simulate_engine_tensor_dtype(args: tuple, kwargs: dict):
314
+ """Hand a metric / loss the tensor dtype the platform will hand it.
315
+
316
+ The engine downcasts every tensor to float16 before pushing it to Redis, and metrics and
317
+ losses read their tensors back from there. A local run would otherwise pass the float32
318
+ arrays straight from the encoders and the model, so a float32-only loss (a Keras/TF loss
319
+ that refuses a float16 y_pred, say) passes the local test and only fails in evaluation.
320
+ Downcasting here makes the local test fail the same way, before the project is pushed.
321
+
322
+ No-op on the platform, where the tensors already arrive downcast. Returns the (possibly
323
+ rewritten) call args plus whether anything was actually downcast.
324
+ """
325
+ if _call_from_tl_platform:
326
+ return args, kwargs, False
327
+ downcast_args, downcast_kwargs = simulate_engine_float16_downcast_on_call_args(args, kwargs)
328
+ was_downcast = any(arg is not original for arg, original in zip(downcast_args, args)) or \
329
+ any(arg is not kwargs[arg_name] for arg_name, arg in downcast_kwargs.items())
330
+ return downcast_args, downcast_kwargs, was_downcast
331
+
332
+
333
+ def _print_engine_float16_simulation_hint(func_name: str) -> None:
334
+ print(f"note: {func_name}() received its tensors as {np.dtype(ENGINE_STORAGE_DTYPE).name} — "
335
+ f"the platform stores every tensor at that precision, so the local test feeds the same "
336
+ f"dtype the engine will. If the failure above is a dtype mismatch, cast inside "
337
+ f"{func_name}() (e.g. arr.astype(np.float32)) rather than relying on the caller's "
338
+ f"dtype. Set {TL_DISABLE_ENGINE_FLOAT16_SIMULATION_ENV_VAR}=true to opt out of this "
339
+ f"simulation locally.")
340
+
341
+
311
342
  def _warn_loss_inputs_not_wired_to_model() -> None:
312
343
  """Warn when a custom loss consumes an input encoder the model is not given.
313
344
 
@@ -900,7 +931,10 @@ def tensorleap_custom_metric(name: str,
900
931
  )
901
932
 
902
933
  else:
903
- assert all(isinstance(v, (float, int, type(None), np.float32)) for v in single_metric_result), (
934
+ # np.floating, not just np.float32: metric inputs are downcast to the
935
+ # engine's float16, so a metric that returns its computed values as numpy
936
+ # scalars now yields np.float16 ones.
937
+ assert all(isinstance(v, (float, int, type(None), np.floating)) for v in single_metric_result), (
904
938
  f"{supported_types_message}\n"
905
939
  f"Got {'a dict where the value of ' + str(key) + ' is of type ' if key is not None else ''}"
906
940
  f"List[{', '.join(type(v).__name__ for v in single_metric_result)}]."
@@ -1019,9 +1053,15 @@ def tensorleap_custom_metric(name: str,
1019
1053
  def inner(*args, **kwargs):
1020
1054
  if not _call_from_tl_platform:
1021
1055
  set_current('tensorleap_custom_metric')
1056
+ args, kwargs, was_downcast = _simulate_engine_tensor_dtype(args, kwargs)
1022
1057
  _validate_input_args(*args, **kwargs)
1023
1058
 
1024
- result = inner_without_validate(*args, **kwargs)
1059
+ try:
1060
+ result = inner_without_validate(*args, **kwargs)
1061
+ except Exception:
1062
+ if was_downcast:
1063
+ _print_engine_float16_simulation_hint(user_function.__name__)
1064
+ raise
1025
1065
 
1026
1066
  _validate_result(result)
1027
1067
  if not _call_from_tl_platform:
@@ -1229,9 +1269,15 @@ def tensorleap_custom_instances_metric(name: str,
1229
1269
  def inner(*args, **kwargs):
1230
1270
  if not _call_from_tl_platform:
1231
1271
  set_current('tensorleap_custom_instances_metric')
1272
+ args, kwargs, was_downcast = _simulate_engine_tensor_dtype(args, kwargs)
1232
1273
  _validate_input_args(*args, **kwargs)
1233
1274
 
1234
- result = inner_without_validate(*args, **kwargs)
1275
+ try:
1276
+ result = inner_without_validate(*args, **kwargs)
1277
+ except Exception:
1278
+ if was_downcast:
1279
+ _print_engine_float16_simulation_hint(user_function.__name__)
1280
+ raise
1235
1281
 
1236
1282
  _validate_result(result)
1237
1283
  if not _call_from_tl_platform:
@@ -3454,9 +3500,15 @@ def tensorleap_custom_loss(name: str, connects_to=None):
3454
3500
  def inner(*args, **kwargs):
3455
3501
  if not _call_from_tl_platform:
3456
3502
  set_current("tensorleap_custom_loss")
3503
+ args, kwargs, was_downcast = _simulate_engine_tensor_dtype(args, kwargs)
3457
3504
  _validate_input_args(*args, **kwargs)
3458
3505
 
3459
- result = inner_without_validate(*args, **kwargs)
3506
+ try:
3507
+ result = inner_without_validate(*args, **kwargs)
3508
+ except Exception:
3509
+ if was_downcast:
3510
+ _print_engine_float16_simulation_hint(user_function.__name__)
3511
+ raise
3460
3512
 
3461
3513
  _validate_result(result)
3462
3514
  if not _call_from_tl_platform:
@@ -1,5 +1,6 @@
1
1
  import io
2
2
  import math
3
+ import os
3
4
  import pickle
4
5
  import sys
5
6
  from pathlib import Path
@@ -42,6 +43,80 @@ def to_numpy_return_masks_wrapper(encoder_function: InstanceCallableInterface) -
42
43
  return numpy_encoder_function
43
44
 
44
45
 
46
+ # The engine downcasts every tensor to float16 before pushing it to Redis, and metrics and losses
47
+ # read their tensors back from there — so on the platform they are handed float16 arrays. A local
48
+ # run feeds them the float32 arrays straight from the encoders and the model, which is why a
49
+ # float32-only loss (e.g. a Keras/TF loss that refuses a float16 y_pred) passes the local test and
50
+ # then fails in evaluation. Mirror the engine locally so the failure surfaces before the push.
51
+ ENGINE_STORAGE_DTYPE = np.float16
52
+ # The engine's cast is range-guarded (redis_metrics_queue_manager.py): only a non-empty float32 /
53
+ # float64 array whose abs-max fits float16's finite range is cast, so an array holding large values
54
+ # (big logits, say) keeps its original dtype instead of overflowing to inf. Arrays holding NaN are
55
+ # left alone too — their abs-max is NaN, and `NaN <= max` is False.
56
+ ENGINE_STORAGE_DTYPE_MAX = 65504.0 # float16 finite max
57
+ ENGINE_DOWNCAST_SOURCE_DTYPES = (np.float32, np.float64)
58
+ TL_DISABLE_ENGINE_FLOAT16_SIMULATION_ENV_VAR = 'TL_DISABLE_ENGINE_FLOAT16_SIMULATION'
59
+
60
+
61
+ def is_engine_float16_simulation_disabled() -> bool:
62
+ return os.environ.get(TL_DISABLE_ENGINE_FLOAT16_SIMULATION_ENV_VAR, '').lower() in \
63
+ ('1', 'true', 'yes')
64
+
65
+
66
+ def simulate_engine_float16_downcast(value: Any) -> Any:
67
+ """Return ``value`` with its float tensors downcast the way the engine downcasts them.
68
+
69
+ Recurses into the containers a handler argument can arrive in (a grouped input is a list of
70
+ arrays). Only the arrays the engine would actually cast are touched — see
71
+ :func:`_downcast_array_to_engine_dtype`. Ints, bools and strings survive the engine's storage
72
+ unchanged, and anything that is not a tensor or a container of tensors
73
+ (SamplePreprocessResponse, for instance) is returned as is.
74
+ """
75
+ if is_engine_float16_simulation_disabled():
76
+ return value
77
+ return _downcast_to_engine_dtype(value)
78
+
79
+
80
+ def _downcast_array_to_engine_dtype(array: npt.NDArray[Any]) -> npt.NDArray[Any]:
81
+ """Mirror the engine's guarded cast for a single array.
82
+
83
+ The engine casts an array to float16 only when it is a non-empty float32 / float64 array whose
84
+ whole-array abs-max fits float16's finite range; anything else is stored at its original dtype.
85
+ Casting unconditionally would diverge the other way — a local run would hand a handler ``inf``
86
+ where the platform hands it a finite float32.
87
+ """
88
+ if array.dtype not in ENGINE_DOWNCAST_SOURCE_DTYPES or not array.size:
89
+ return array
90
+ # NaN fails this comparison, which is how the engine ends up leaving NaN-bearing arrays alone.
91
+ if not float(np.abs(array).max()) <= ENGINE_STORAGE_DTYPE_MAX:
92
+ return array
93
+ return array.astype(ENGINE_STORAGE_DTYPE)
94
+
95
+
96
+ def _downcast_to_engine_dtype(value: Any) -> Any:
97
+ # Containers return the original object when no leaf changed, so callers can tell whether a
98
+ # downcast actually happened by identity.
99
+ if isinstance(value, np.ndarray):
100
+ return _downcast_array_to_engine_dtype(value)
101
+ if isinstance(value, (list, tuple)):
102
+ downcast = [_downcast_to_engine_dtype(item) for item in value]
103
+ if all(item is original for item, original in zip(downcast, value)):
104
+ return value
105
+ return downcast if isinstance(value, list) else tuple(downcast)
106
+ if isinstance(value, dict):
107
+ downcast_dict = {key: _downcast_to_engine_dtype(item) for key, item in value.items()}
108
+ if all(item is value[key] for key, item in downcast_dict.items()):
109
+ return value
110
+ return downcast_dict
111
+ return value
112
+
113
+
114
+ def simulate_engine_float16_downcast_on_call_args(
115
+ args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Tuple[Tuple[Any, ...], Dict[str, Any]]:
116
+ return (tuple(simulate_engine_float16_downcast(arg) for arg in args),
117
+ {arg_name: simulate_engine_float16_downcast(arg) for arg_name, arg in kwargs.items()})
118
+
119
+
45
120
  def get_root_traceback(exc_tb: TracebackType) -> TracebackType:
46
121
  return_traceback = exc_tb
47
122
  while return_traceback.tb_next is not None:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
- version = "1.0.204.dev1"
3
+ version = "1.0.204.dev5"
4
4
  description = ""
5
5
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
6
6
  license = "MIT"