Skip to content

Commit 7f70edd

Browse files
author
ascend-robot
committed
[fix] use deepcopy for BatchMeta init
Co-authored-by: 0oshowero0<o0shower0o@outlook.com> # message auto-generated for no-merge-commit merge: !2 merge main into main [fix] use deepcopy for BatchMeta init Created-by: hanzhenyu8 Commit-by: 0oshowero0 Merged-by: ascend-robot Description: ## Background We nofity there might be some error during `BatchMeta.chunk()` due to shallow copy. Reproduction script: ```python3 from transfer_queue.metadata import BatchMeta, FieldMeta, SampleMeta from transfer_queue.utils.utils import ProductionStatus import pickle samples = [ SampleMeta( partition_id="p1", global_index=0, fields={"image": FieldMeta("image", None, None)}, ), SampleMeta( partition_id="p1", global_index=1, fields={"image": FieldMeta("image", None, None)}, ), SampleMeta( partition_id="p1", global_index=2, fields={"image": FieldMeta("image", None, None)}, ), SampleMeta( partition_id="p1", global_index=3, fields={"image": FieldMeta("image", None, None)}, ), ] extra_info = {"key1": [1, 2], "key2": {"nested": "value"}} batch_meta = BatchMeta(samples=samples, extra_info=extra_info) with open('before_chunk', "wb") as f: pickle.dump(batch_meta, f) # Execute chunk chunks = batch_meta.chunk(2) import pickle with open('after_chunk', "wb") as f: pickle.dump(batch_meta, f) ``` ```bash # You will see different md5 values! md5sum before_chunk md5sum after_chunk ``` This happens because during the `__post_init__` of `BatchMeta`, we set the attributes of the underlying `self.samples`: ```python3 class BatchMeta: """Records the metadata of a batch of data samples.""" samples: list[SampleMeta] extra_info: dict[str, Any] = dataclasses.field(default_factory=dict) def __post_init__(self): """Initialize all computed properties during initialization""" # Basic properties object.__setattr__(self, "_size", len(self.samples)) object.__setattr__(self, "_is_ready", all(sample.is_ready for sample in self.samples)) # Pre-compute all list properties for better performance if self.samples: for idx, sample in enumerate(self.samples): object.__setattr__(sample, "_batch_index", idx) # Ensure batch_index is set correctly ... ``` ## Solution During `__post_init__`, we deepcopy the input `samples` and `extra_info`. ```python3 def __post_init__(self): """Initialize all computed properties during initialization""" self.samples = copy.deepcopy(self.samples) self.extra_info = copy.deepcopy(self.extra_info) ``` See merge request: Ascend/TransferQueue!2
1 parent a1645ea commit 7f70edd

1 file changed

Lines changed: 9 additions & 5 deletions

File tree

transfer_queue/metadata.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import copy
1617
import dataclasses
1718
import itertools
1819
import logging
@@ -176,6 +177,9 @@ class BatchMeta:
176177

177178
def __post_init__(self):
178179
"""Initialize all computed properties during initialization"""
180+
self.samples = copy.deepcopy(self.samples)
181+
self.extra_info = copy.deepcopy(self.extra_info)
182+
179183
# Basic properties
180184
object.__setattr__(self, "_size", len(self.samples))
181185
object.__setattr__(self, "_is_ready", all(sample.is_ready for sample in self.samples))
@@ -253,7 +257,7 @@ def has_extra_info(self, key: str) -> bool:
253257

254258
def get_all_extra_info(self) -> dict[str, Any]:
255259
"""Get all extra info as a dictionary"""
256-
return self.extra_info.copy()
260+
return copy.deepcopy(self.extra_info)
257261

258262
def add_fields(self, tensor_dict: TensorDict, set_all_ready: bool = True) -> "BatchMeta":
259263
"""
@@ -296,7 +300,7 @@ def select_samples(self, sample_indices: list[int]) -> "BatchMeta":
296300
selected_samples = [self.samples[i] for i in sample_indices]
297301

298302
# construct new BatchMeta instance
299-
selected_batch_meta = BatchMeta(samples=selected_samples, extra_info=self.extra_info.copy())
303+
selected_batch_meta = BatchMeta(samples=selected_samples, extra_info=self.extra_info)
300304

301305
return selected_batch_meta
302306

@@ -315,7 +319,7 @@ def select_fields(self, field_names: list[str]) -> "BatchMeta":
315319
new_samples = [sample.select_fields(field_names=field_names) for sample in self.samples]
316320

317321
# construct new BatchMeta instance
318-
new_batch_meta = BatchMeta(samples=new_samples, extra_info=self.extra_info.copy())
322+
new_batch_meta = BatchMeta(samples=new_samples, extra_info=self.extra_info)
319323

320324
return new_batch_meta
321325

@@ -326,7 +330,7 @@ def __len__(self) -> int:
326330
def __getitem__(self, item):
327331
if isinstance(item, int | np.integer):
328332
sample_meta = self.samples[item] if self.samples else []
329-
return BatchMeta(samples=[sample_meta], extra_info=self.extra_info.copy())
333+
return BatchMeta(samples=[sample_meta], extra_info=self.extra_info)
330334
else:
331335
raise TypeError(f"Indexing with {type(item)} is not supported now!")
332336

@@ -359,7 +363,7 @@ def chunk(self, chunks: int) -> list["BatchMeta"]:
359363
current_chunk_size = base_size + 1 if i < remainder else base_size
360364
end = start + current_chunk_size
361365
chunk_samples = self.samples[start:end]
362-
chunk = BatchMeta(samples=chunk_samples, extra_info=self.extra_info.copy())
366+
chunk = BatchMeta(samples=chunk_samples, extra_info=self.extra_info)
363367
chunk_list.append(chunk)
364368
start = end
365369
return chunk_list

0 commit comments

Comments
 (0)