-
Notifications
You must be signed in to change notification settings - Fork 78
[feat] distributed embedding export #525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
tiankongdeguiji
merged 53 commits into
alibaba:master
from
eric-gecheng:feature/export_split
Jul 9, 2026
Merged
Changes from all commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
a9d14cc
support large scale embedding inference, split model and export embed…
eric-gecheng a2b6779
export model of distributed embedding mode
eric-gecheng b354d6e
merge master
eric-gecheng 10d027c
add support of tiling to dense subgraph
eric-gecheng d0fc488
Merge branch 'master' into feature/export_split
eric-gecheng c8d3a9d
dynemb supports input_tile=3
eric-gecheng 6441395
Merge branch 'bugfix/dynemb_input_tile_3' into feature/export_split
eric-gecheng e27ac7d
dynamic emb supports distributed export
eric-gecheng cca0f6c
fix input_tile 3 on dynemb
eric-gecheng a4a18a2
Merge branch 'master' into feature/export_split
eric-gecheng c45c386
warmup model to avoid lazy components when exporting
eric-gecheng 4f7df8b
build INPUT_TILE=3 dynamicemb user-side aliases in a per-rank temp vi…
eric-gecheng 6233314
input_tile default to 3 when using distributed export
eric-gecheng 5b68cab
Merge branch 'master' into feature/export_split
eric-gecheng 6eff2ff
fix model._features
eric-gecheng 5ee85b3
merge master
eric-gecheng 0d6815f
Merge branch 'master' into feature/export_split
eric-gecheng 414ec7d
merge master
eric-gecheng 376c320
merge branch feature/export_split
eric-gecheng f347a21
fix batch_size missing in dense subgraph
eric-gecheng 29fdabd
Merge branch 'master' into feature/export_split
eric-gecheng 74a86c5
Merge branch 'feature/export_split' of github.com:eric-gecheng/TorchE…
eric-gecheng 7cb07cd
ensure input tile for distributed embedding
eric-gecheng 6874133
fix input tile
eric-gecheng cb885a9
export dymemb scores
eric-gecheng 26beb62
Merge branch 'master' into feature/export_split
eric-gecheng 57ee9c8
distributed export support multiple gpu checkpoint
eric-gecheng ff32763
ensure rank0 for distributed export
eric-gecheng f92da02
fix stale emb_config
eric-gecheng deb62f9
raw/shared key mismatch
eric-gecheng 67d3e36
deepcopy pipeline_config
eric-gecheng 813f4fb
use set instead of list for better performance
eric-gecheng a4e6ec3
export_distributed_embedding return none
eric-gecheng 1e15c81
use capital letter for USE_DISTRIBUTED_EMBEDDING
eric-gecheng 4382b4e
update document
eric-gecheng cf1bafd
Merge branch 'feature/export_split' of github.com:eric-gecheng/TorchE…
eric-gecheng 1125716
code format
eric-gecheng fbab7ea
fix(export): disambiguate EC/EBC sparse embeddings with shared names
eric-gecheng 1931e1b
Merge branch 'feature/export_split' of github.com:eric-gecheng/TorchE…
eric-gecheng 880f849
Merge branch 'master' into feature/export_split
eric-gecheng e0f7924
Update tzrec/utils/checkpoint_util.py
eric-gecheng 2787ac2
refactor: deduplicate dynamicemb key file realpath filtering
eric-gecheng f08f0c0
cleanup: remove dead distributed export helpers
eric-gecheng aa5bd1f
simplify comments
eric-gecheng 867f372
is_input_tile_emb
eric-gecheng 0783062
add input tile3 check when doing remap
eric-gecheng c799f61
temporarily disable H20 CI test
eric-gecheng 3e3a1ed
remove redudant ensure_input_tile_for_distributed_embedding
eric-gecheng 7a1285c
bump up version to 1.3.1
eric-gecheng abd366e
remove unnecessary dist.barrier()
eric-gecheng b3a697e
hoist import to module scope
eric-gecheng c109bab
Merge branch 'feature/export_split' of github.com:eric-gecheng/TorchE…
eric-gecheng b296da8
merge master
eric-gecheng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| import queue | ||
| import re | ||
| import shutil | ||
| import tempfile | ||
| import threading | ||
| import weakref | ||
| from dataclasses import replace | ||
|
|
@@ -38,11 +39,26 @@ | |
| ) | ||
| from torchrec.modules.mc_modules import MCHManagedCollisionModule | ||
|
|
||
| from tzrec.acc.utils import is_input_tile_emb | ||
| from tzrec.constant import TRAIN_EVAL_RESULT_FILENAME | ||
| from tzrec.protos import export_pb2 | ||
| from tzrec.utils.dynamicemb_util import has_dynamicemb | ||
| from tzrec.utils.logging_util import logger | ||
|
|
||
| # INPUT_TILE=3 load-time fallback from user-side keys to non-user checkpoint keys. | ||
| # Mirrors `tzrec/acc/utils.py:write_mapping_file_for_input_tile`. | ||
| _INPUT_TILE_USER_REPLACEMENTS: Tuple[Tuple[str, str], ...] = ( | ||
| (".ebc_user.embedding_bags.", ".ebc.embedding_bags."), | ||
| (".mc_ebc_user._embedding_module.", ".mc_ebc._embedding_module."), | ||
| ( | ||
| ".mc_ebc_user._managed_collision_collection.", | ||
| ".mc_ebc._managed_collision_collection.", | ||
| ), | ||
| (".ec_list_user.", ".ec_list."), | ||
| (".mc_ec_list_user.", ".mc_ec_list."), | ||
| (".ec_dict_user.", ".ec_dict."), | ||
| (".mc_ec_dict_user.", ".mc_ec_dict."), | ||
| ) | ||
| # queue token meaning "run a prune pass"; ``None`` means "stop the worker". | ||
| _PRUNE_REQUEST = object() | ||
|
|
||
|
|
@@ -124,6 +140,25 @@ def create_local_plan(self) -> LoadPlan: | |
| "[{meta_fqn}], will be deprecated when tzrec version >= 1.0.0" | ||
| ) | ||
|
|
||
| # INPUT_TILE=3 export adds user-side twin modules absent from | ||
| # training checkpoints. Remap to existing non-user keys, matching | ||
| # export_model_normal's emb_ckpt_mapping.txt fallback. | ||
| if ( | ||
| is_input_tile_emb() | ||
| and meta_fqn not in self.metadata.state_dict_metadata | ||
| ): | ||
| for new_pat, old_pat in _INPUT_TILE_USER_REPLACEMENTS: | ||
| if new_pat not in meta_fqn: | ||
| continue | ||
| candidate = meta_fqn.replace(new_pat, old_pat) | ||
| if candidate in self.metadata.state_dict_metadata: | ||
| logger.info( | ||
| f"Remap INPUT_TILE=3 state [{fqn}] from [{candidate}]" | ||
| ) | ||
| meta_fqn = candidate | ||
| fqn_remap_set.add(fqn) | ||
| break | ||
|
|
||
| if meta_fqn in self.metadata.state_dict_metadata: | ||
| md = self.metadata.state_dict_metadata[meta_fqn] | ||
| else: | ||
|
|
@@ -794,6 +829,50 @@ def _a2a( | |
| m._buffers[name].copy_(new_meta) | ||
|
|
||
|
|
||
| # Module-name segments that get a `_user` twin when INPUT_TILE=3 export | ||
| # duplicates the embedding group into item/user halves. See | ||
| # tzrec/modules/embedding.py:EmbeddingGroupImpl / | ||
| # SequenceEmbeddingGroupImpl for the construction. | ||
| _INPUT_TILE_USER_SEGMENTS = frozenset({"ebc", "mc_ebc", "ec_dict", "mc_ec_dict"}) | ||
|
|
||
|
|
||
| def _make_dynamicemb_input_tile_user_view(dynamicemb_path: str, view_path: str) -> str: | ||
| """Create a local symlink view for INPUT_TILE=3 dynamicemb loading. | ||
|
|
||
| The training checkpoint has non-user module paths, while INPUT_TILE=3 | ||
| export adds user-side twins. Build aliases in a temporary local directory | ||
| instead of mutating the checkpoint directory. | ||
| """ | ||
| entries = [] | ||
| for entry in os.listdir(dynamicemb_path): | ||
| full_path = os.path.abspath(os.path.join(dynamicemb_path, entry)) | ||
| if not os.path.isdir(full_path): | ||
| continue | ||
| entries.append((entry, full_path)) | ||
| link_path = os.path.join(view_path, entry) | ||
| if not os.path.lexists(link_path): | ||
| os.symlink(full_path, link_path, target_is_directory=True) | ||
|
|
||
| for entry, full_path in entries: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we reuse |
||
| segs = entry.split(".") | ||
| if any(seg.endswith("_user") for seg in segs): | ||
| continue | ||
| for i, seg in enumerate(segs): | ||
| if seg not in _INPUT_TILE_USER_SEGMENTS: | ||
| continue | ||
| user_segs = list(segs) | ||
| user_segs[i] = f"{seg}_user" | ||
| user_entry = ".".join(user_segs) | ||
| user_path = os.path.join(view_path, user_entry) | ||
| if os.path.lexists(user_path): | ||
| continue | ||
| os.symlink(full_path, user_path, target_is_directory=True) | ||
| logger.info( | ||
| f"created INPUT_TILE=3 dynamicemb alias {user_entry} -> {entry}" | ||
| ) | ||
| return view_path | ||
|
|
||
|
|
||
| def restore_model( | ||
| checkpoint_dir: str, | ||
| model: nn.Module, | ||
|
|
@@ -872,16 +951,35 @@ def restore_model( | |
|
|
||
| dynamicemb_path = os.path.join(checkpoint_dir, "dynamicemb") | ||
| if os.path.exists(dynamicemb_path): | ||
| # Training never sets INPUT_TILE, but exporting with | ||
| # INPUT_TILE=3 adds twin user-side modules (`ebc_user`, | ||
| # `mc_ebc_user`, `ec_dict_user`, `mc_ec_dict_user`) that | ||
| # share dynamic-embedding tables with their non-user counterparts. | ||
| # Build a local symlink view instead of mutating the checkpoint | ||
| # directory, which may be read-only or remote-mounted. | ||
| dynamicemb_load_path = dynamicemb_path | ||
| dynamicemb_view = None | ||
| if is_input_tile_emb(): | ||
| dynamicemb_view = tempfile.TemporaryDirectory( | ||
|
eric-gecheng marked this conversation as resolved.
|
||
| prefix=f"tzrec_dynamicemb_rank{os.environ.get('RANK', '0')}_" | ||
| ) | ||
| dynamicemb_load_path = _make_dynamicemb_input_tile_user_view( | ||
| dynamicemb_path, dynamicemb_view.name | ||
| ) | ||
| logger.info( | ||
| f"RANK[{os.environ.get('RANK', 0)}] restoring dynamic embedding..." | ||
| ) | ||
| DynamicEmbLoad( | ||
| dynamicemb_path, | ||
| model, | ||
| table_names=meta.get("dynamicemb_load_table_names", None), | ||
| optim=meta.get("dynamicemb_load_optim", optimizer is not None), | ||
| counter=True, | ||
| ) | ||
| try: | ||
| DynamicEmbLoad( | ||
| dynamicemb_load_path, | ||
| model, | ||
| table_names=meta.get("dynamicemb_load_table_names", None), | ||
| optim=meta.get("dynamicemb_load_optim", optimizer is not None), | ||
| counter=True, | ||
| ) | ||
| finally: | ||
| if dynamicemb_view is not None: | ||
| dynamicemb_view.cleanup() | ||
| logger.info( | ||
| f"RANK[{os.environ.get('RANK', 0)}] restore dynamic embedding finished." | ||
| ) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.