mostlyai-engine 1.5.1__tar.gz → 1.5.3__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 (51) hide show
  1. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/PKG-INFO +1 -1
  2. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/__init__.py +1 -1
  3. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_common.py +7 -10
  4. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/common.py +3 -3
  5. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/argn.py +22 -10
  6. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/analysis.py +26 -7
  7. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/pyproject.toml +1 -1
  8. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/.gitignore +0 -0
  9. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/LICENSE +0 -0
  10. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/README.md +0 -0
  11. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_dtypes.py +0 -0
  12. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/__init__.py +0 -0
  13. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/language/__init__.py +0 -0
  14. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/language/categorical.py +0 -0
  15. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/language/datetime.py +0 -0
  16. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/language/numeric.py +0 -0
  17. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/language/text.py +0 -0
  18. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/__init__.py +0 -0
  19. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/categorical.py +0 -0
  20. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/character.py +0 -0
  21. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/datetime.py +0 -0
  22. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/itt.py +0 -0
  23. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/lat_long.py +0 -0
  24. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_encoding_types/tabular/numeric.py +0 -0
  25. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/__init__.py +0 -0
  26. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/encoding.py +0 -0
  27. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/engine/__init__.py +0 -0
  28. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/engine/base.py +0 -0
  29. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/engine/hf_engine.py +0 -0
  30. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/engine/vllm_engine.py +0 -0
  31. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/generation.py +0 -0
  32. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/lstm.py +0 -0
  33. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/tokenizer_utils.py +0 -0
  34. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/training.py +0 -0
  35. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_language/xgrammar_utils.py +0 -0
  36. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_memory.py +0 -0
  37. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/__init__.py +0 -0
  38. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/common.py +0 -0
  39. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/encoding.py +0 -0
  40. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/fairness.py +0 -0
  41. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/generation.py +0 -0
  42. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_tabular/training.py +0 -0
  43. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_training_utils.py +0 -0
  44. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/_workspace.py +0 -0
  45. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/domain.py +0 -0
  46. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/encoding.py +0 -0
  47. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/generation.py +0 -0
  48. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/logging.py +0 -0
  49. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/random_state.py +0 -0
  50. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/splitting.py +0 -0
  51. {mostlyai_engine-1.5.1 → mostlyai_engine-1.5.3}/mostlyai/engine/training.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mostlyai-engine
3
- Version: 1.5.1
3
+ Version: 1.5.3
4
4
  Summary: Synthetic Data Engine
5
5
  Project-URL: homepage, https://github.com/mostly-ai/mostlyai-engine
6
6
  Project-URL: repository, https://github.com/mostly-ai/mostlyai-engine
@@ -22,7 +22,7 @@ from mostlyai.engine.splitting import split
22
22
  from mostlyai.engine.training import train
23
23
 
24
24
  __all__ = ["split", "analyze", "encode", "train", "generate", "init_logging", "set_random_state"]
25
- __version__ = "1.5.1"
25
+ __version__ = "1.5.3"
26
26
 
27
27
  # suppress specific warning related to os.fork() in multi-threaded processes
28
28
  warnings.filterwarnings("ignore", category=DeprecationWarning, message=".*multi-threaded.*fork.*")
@@ -401,12 +401,11 @@ def get_sub_columns_lookup(
401
401
  return sub_cols_lookup
402
402
 
403
403
 
404
- class CtxSequenceLengthError(Exception):
405
- """Error raised when the cols of the same table do not have the same stats value"""
406
-
407
-
408
404
  def get_ctx_sequence_length(ctx_stats: dict, key: str) -> dict[str, int]:
409
- seq_stats: dict[str, int] = {}
405
+ """
406
+ Get the stats of sequence lengths from the first column_stats of each context table
407
+ """
408
+ ctxseq_stats: dict[str, int] = {}
410
409
 
411
410
  for column_stats in ctx_stats.get("columns", {}).values():
412
411
  if "seq_len" in column_stats:
@@ -414,12 +413,10 @@ def get_ctx_sequence_length(ctx_stats: dict, key: str) -> dict[str, int]:
414
413
  argn_processor=column_stats[ARGN_PROCESSOR],
415
414
  argn_table=column_stats[ARGN_TABLE],
416
415
  )
417
- cur_value = seq_stats.get(table)
418
- if cur_value and cur_value != column_stats["seq_len"][key]:
419
- raise CtxSequenceLengthError()
420
- seq_stats[table] = column_stats["seq_len"][key]
416
+ if table not in ctxseq_stats:
417
+ ctxseq_stats[table] = column_stats["seq_len"][key]
421
418
 
422
- return seq_stats
419
+ return ctxseq_stats
423
420
 
424
421
 
425
422
  def get_max_data_points_per_sample(stats: dict) -> int:
@@ -97,10 +97,10 @@ def load_base_model_and_config(
97
97
  else:
98
98
  quantization_config = None
99
99
 
100
- if device.type == "cuda" and device.index is not None:
101
- device_map = str(device)
102
- else:
100
+ if device.type == "cuda" and device.index is None:
103
101
  device_map = "auto"
102
+ else: # device is `cpu` or `cuda:0` (when using single GPU on a multi-GPU instance)
103
+ device_map = str(device)
104
104
 
105
105
  if hasattr(config, "text_config") and hasattr(config, "vision_config"):
106
106
  config.text_config.use_cache = use_cache
@@ -216,8 +216,15 @@ class Embedders(nn.Module):
216
216
  self.embedders = nn.ModuleDict()
217
217
 
218
218
  # embedding layers for each sub column defined in cardinalities
219
+ has_ridx = any(sub_col.startswith(RIDX_SUB_COLUMN_PREFIX) for sub_col in self.cardinalities)
219
220
  last_slen_sub_col = next(
220
- (sub_col for sub_col in reversed(self.cardinalities) if sub_col.startswith(SLEN_SUB_COLUMN_PREFIX)), None
221
+ (
222
+ sub_col
223
+ for sub_col in reversed(self.cardinalities)
224
+ if sub_col.startswith(SLEN_SUB_COLUMN_PREFIX)
225
+ if has_ridx # last SLEN sub column is dangling for model with RIDX only
226
+ ),
227
+ None,
221
228
  )
222
229
  last_ridx_sub_col = next(
223
230
  (sub_col for sub_col in reversed(self.cardinalities) if sub_col.startswith(RIDX_SUB_COLUMN_PREFIX)), None
@@ -1249,21 +1256,26 @@ class SequentialModel(nn.Module):
1249
1256
  if context is None:
1250
1257
  context = self.context_compressor(x)
1251
1258
 
1259
+ # SLEN is only masked for models with RIDX
1260
+ has_ridx = any(sub_col.startswith(RIDX_SUB_COLUMN_PREFIX) for sub_col in self.tgt_cardinalities)
1261
+ masked_positional_columns = (
1262
+ (SLEN_SUB_COLUMN_PREFIX, RIDX_SUB_COLUMN_PREFIX) if has_ridx else (RIDX_SUB_COLUMN_PREFIX,)
1263
+ )
1264
+
1252
1265
  outputs = {}
1253
1266
  if mode == "trn":
1254
1267
  # forward pass through sub column embedders
1255
1268
  tgt_embeds = self.embedders(x)
1256
- tgt_embeds_slen_ridx_masked = {
1257
- k: torch.zeros_like(v) if k.startswith((RIDX_SUB_COLUMN_PREFIX, SLEN_SUB_COLUMN_PREFIX)) else v
1258
- for k, v in tgt_embeds.items()
1269
+ tgt_embeds_positional_masked = {
1270
+ k: torch.zeros_like(v) if k.startswith(masked_positional_columns) else v for k, v in tgt_embeds.items()
1259
1271
  }
1260
1272
 
1261
1273
  # forward pass through column embedders
1262
- tgt_col_embeds = self.column_embedders(tgt_embeds_slen_ridx_masked)
1274
+ tgt_col_embeds = self.column_embedders(tgt_embeds_positional_masked)
1263
1275
 
1264
1276
  # history
1265
1277
  # time shift: remove last time step; add zeros for first time step; add randoms for all others
1266
- embeddings = torch.cat(list(tgt_embeds_slen_ridx_masked.values()), dim=-1)
1278
+ embeddings = torch.cat(list(tgt_embeds_positional_masked.values()), dim=-1)
1267
1279
  history_in = embeddings[:, :-1, :]
1268
1280
  history_in = nn.ConstantPad2d((0, 0, 1, 0), 0)(history_in)
1269
1281
  history, _ = self.history_compressor(history_in)
@@ -1393,20 +1405,20 @@ class SequentialModel(nn.Module):
1393
1405
 
1394
1406
  # update current sub column embedding
1395
1407
  tgt_embeds[sub_col] = self.embedders.get(sub_col)(out)
1396
- tgt_embeds_slen_ridx_masked = {
1397
- k: torch.zeros_like(v) if k.startswith((SLEN_SUB_COLUMN_PREFIX, RIDX_SUB_COLUMN_PREFIX)) else v
1408
+ tgt_embeds_positional_masked = {
1409
+ k: torch.zeros_like(v) if k.startswith(masked_positional_columns) else v
1398
1410
  for k, v in tgt_embeds.items()
1399
1411
  }
1400
1412
 
1401
1413
  # update current column embedding
1402
1414
  if sub_col in self.tgt_last_sub_cols:
1403
1415
  col_sub_cols = self.tgt_column_sub_columns[lookup.col_name]
1404
- col_embed_in = torch.cat([tgt_embeds_slen_ridx_masked[sc] for sc in col_sub_cols], dim=-1)
1416
+ col_embed_in = torch.cat([tgt_embeds_positional_masked[sc] for sc in col_sub_cols], dim=-1)
1405
1417
  tgt_col_embeds[lookup.col_name] = self.column_embedders.get(lookup.col_name)(col_embed_in)
1406
1418
  col_embeddings = torch.cat(list(tgt_col_embeds.values()), dim=-1)
1407
1419
 
1408
1420
  # update history and hidden state
1409
- history_in = torch.cat([v for v in tgt_embeds_slen_ridx_masked.values()], dim=-1)
1421
+ history_in = torch.cat([v for v in tgt_embeds_positional_masked.values()], dim=-1)
1410
1422
  history, history_state = self.history_compressor(history_in, history_state=history_state)
1411
1423
 
1412
1424
  # order outputs according to tgt_sub_columns
@@ -339,13 +339,25 @@ def _analyze_reduce(
339
339
  stats_list = [read_json(file) for file in stats_files]
340
340
  stats: dict[str, Any] = {"columns": {}}
341
341
 
342
+ # check how many context tables have sequential context
343
+ if mode == "ctx":
344
+ ctxseq_stats = {}
345
+ ctxseq_tables = []
346
+ for column, column_stats in stats_list[0]["columns"].items():
347
+ if "seq_len" in column_stats:
348
+ table_name = column.split(TABLE_COLUMN_INFIX)[0]
349
+ if table_name not in ctxseq_tables:
350
+ ctxseq_tables.append(table_name)
351
+ n_ctxseq_tables = len(ctxseq_tables)
352
+ _LOG.info(f"{n_ctxseq_tables = }")
353
+
342
354
  encoding_types = {
343
355
  column: column_stats.get("encoding_type") for column, column_stats in stats_list[0]["columns"].items()
344
356
  }
345
357
 
346
- # ctx: distribute the privacy budget across all columns
358
+ # ctx: distribute the privacy budget across all columns + sequence lengths of n_ctxseq_tables
347
359
  # tgt: distribute the privacy budget across all columns + sequence length
348
- n_dp_splits = len(encoding_types) if mode == "ctx" else len(encoding_types) + 1
360
+ n_dp_splits = len(encoding_types) + n_ctxseq_tables if mode == "ctx" else len(encoding_types) + 1
349
361
  _LOG.info(f"{value_protection = }")
350
362
  if value_protection_epsilon is not None and n_dp_splits > 0:
351
363
  _LOG.info(f"epsilon for analyzing each column and sequence length: {value_protection_epsilon / n_dp_splits}")
@@ -364,13 +376,13 @@ def _analyze_reduce(
364
376
  stats["columns"][column] = {"encoding_type": encoding_type}
365
377
  continue
366
378
 
367
- analyze_reduce_column_args = {
368
- "stats_list": column_stats_list,
379
+ value_protection_args = {
369
380
  "value_protection": value_protection,
370
381
  "value_protection_epsilon": value_protection_epsilon / n_dp_splits
371
382
  if value_protection_epsilon is not None
372
383
  else None,
373
384
  }
385
+ analyze_reduce_column_args = {"stats_list": column_stats_list} | value_protection_args
374
386
 
375
387
  match encoding_type:
376
388
  case ModelEncodingType.tabular_categorical:
@@ -413,9 +425,16 @@ def _analyze_reduce(
413
425
  if encoding_type in _VALUE_PROTECTION_ENCODING_TYPES:
414
426
  stats_col = {"value_protection": value_protection} | stats_col
415
427
 
416
- is_flat_column = "seq_len" not in column_stats_list[0]
417
- if not is_flat_column:
418
- stats_col["seq_len"] = _analyze_reduce_seq_len([column_stats_list[0]["seq_len"]])
428
+ is_ctxseq_column = "seq_len" in column_stats_list[0]
429
+ if is_ctxseq_column:
430
+ table_name = column.split(TABLE_COLUMN_INFIX)[0]
431
+ # only get the lengths from the first column of a ctxseq table and reuse the stats later
432
+ if table_name not in ctxseq_stats:
433
+ ctxseq_stats[table_name] = _analyze_reduce_seq_len(
434
+ stats_list=[column_stats_list[0]["seq_len"]], **value_protection_args
435
+ )
436
+ _LOG.info(f"analyzed sequence length for context table `{table_name}`")
437
+ stats_col["seq_len"] = ctxseq_stats[table_name]
419
438
 
420
439
  is_language_column = encoding_type in (
421
440
  ModelEncodingType.language_text,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mostlyai-engine"
3
- version = "1.5.1"
3
+ version = "1.5.3"
4
4
  description = "Synthetic Data Engine"
5
5
  authors = [{ name = "MOSTLY AI", email = "dev@mostly.ai" }]
6
6
  requires-python = ">=3.10"
File without changes