Skip to content

Commit cb055ce

Browse files
shawnxu26facebook-github-bot
authored andcommitted
Preserve unquantized embedding tables through sharding
Summary: PNI builds TorchRec per-table weight dtype maps from `select_quantization_dtype()`. The selector uses `None` to mean that a table must remain unquantized, but omitting that table from TorchRec's map means to use the default INT8 dtype. That mismatch produced an INT8 TBE specification for a table whose packed weight remained floating point, so ICE attempted to load a Float weight into a Byte table. Record selector `None` explicitly as `torch.float` in the existing ADS EBC, IG, and retrieval PNI quantization callsites. Explicit INT8, INT4, and INT2 selections are unchanged, and float-to-half policies continue to return and preserve `torch.half`. Mixed floating and quantized tables also expose a TorchRec state initialization gap when split scale/bias state is enabled. Floating TBEs correctly return neither qscale nor qbias. Preserve their weight state while omitting quantization-parameter state, and reject an asymmetric qscale/qbias pair as invalid instead of producing an incomplete state dict. Together these changes preserve each table's selected representation across PNI authoring, TorchRec sharding, model packing, and ICE loading without special-casing a model or transform. Differential Revision: D113501049
1 parent adf7d7b commit cb055ce

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

torchrec/distributed/quant_state.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ def _initialize_torch_state( # noqa: C901
176176
# end of weight shards section
177177

178178
# weight_qscale & weight_qbias section:
179+
if (tbe_split_qscale is None) != (tbe_split_qbias is None):
180+
raise RuntimeError(
181+
"Quantized table must provide both qscale and qbias: "
182+
f"table={table.name}, data_type={table.data_type}, "
183+
f"qscale_is_none={tbe_split_qscale is None}, "
184+
f"qbias_is_none={tbe_split_qbias is None}"
185+
)
186+
if tbe_split_qscale is None:
187+
continue
188+
179189
# For RW - ShardedTensorBase
180190
# For CW - List[Tensor] that logically corresponds to the same unsharded Tensor, but present on each sharded rank
181191
for (

torchrec/distributed/tests/test_infer_shardings.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,42 @@ def setUp(self) -> None:
147147
super().setUp()
148148
set_propogate_device(True)
149149

150+
def test_tw_mixed_float_and_int8_split_scale_bias_cpu(self) -> None:
151+
device = torch.device("cpu")
152+
mi = create_test_model(
153+
num_embeddings=16,
154+
emb_dim=8,
155+
world_size=1,
156+
batch_size=2,
157+
dense_device=device,
158+
sparse_device=device,
159+
num_features=2,
160+
num_weighted_features=0,
161+
)
162+
mi.quant_model = quantize(
163+
module=mi.model,
164+
inplace=False,
165+
quant_state_dict_split_scale_bias=True,
166+
per_table_weight_dtypes={
167+
"table_0": torch.float,
168+
"table_1": torch.quint8,
169+
},
170+
)
171+
172+
sharded_model = shard_qebc(
173+
mi,
174+
sharding_type=ShardingType.TABLE_WISE,
175+
device=device,
176+
)
177+
state_keys = sharded_model.state_dict().keys()
178+
179+
self.assertTrue(any(key.endswith("table_0.weight") for key in state_keys))
180+
self.assertFalse(any("table_0.weight_q" in key for key in state_keys))
181+
self.assertTrue(
182+
any(key.endswith("table_1.weight_qscale") for key in state_keys)
183+
)
184+
self.assertTrue(any(key.endswith("table_1.weight_qbias") for key in state_keys))
185+
150186
@unittest.skipIf(
151187
torch.cuda.device_count() <= 1,
152188
"Not enough GPUs available",

0 commit comments

Comments
 (0)