StreamingSafetensorSaver keeps the files of a previous export, so reloading returns the old weights

Abierto Apto para principiantes
#257 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@li-lizhe ya está trabajando en esto.

Desde el 8/11/2026.

  • #258 de @li-lizhe — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
76/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
backend

Línea de trabajo

Empieza con StreamingSafetensorSaver y su método finalize() en las utilidades de safetensors; el issue indica el comportamiento de guardado que falla e incluye una reproducción con SafetensorLazyLoader. Comprueba cómo se finalizan las exportaciones en un solo archivo y en varios shards en un directorio de salida existente. La tarea está terminada cuando una segunda exportación deja solo sus propios archivos y, al volver a cargar, se obtienen exactamente los tensores recién escritos.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Bug Description / Bug 描述

StreamingSafetensorSaver writes each shard as model-NNNNN-of-?????.safetensors and renames them in finalize(). Nothing removes the files that an earlier export left in the same directory, so a second export inherits them:

  1. A sharded export followed by a single-file export keeps the stale index. finalize() writes model.safetensors but the model.safetensors.index.json from the previous run stays. SafetensorLazyLoader reads the index when it exists, so reloading the directory returns the previous tensors under the same keys, silently.
  2. A 4-shard export followed by a 2-shard export keeps the extra shard files. The index is rewritten, but model-0000{3,4}-of-00004.safetensors stay on disk.
  3. The rename can abort the save. finalize() uses os.rename, which raises FileExistsError when the target name of the previous export is still present, so re-saving into the same directory fails instead of replacing the checkpoint.

GPTBridge.save_weights(..., output_dir=...) takes an arbitrary directory, and re-running training into the same output directory is the normal way to refresh a merged checkpoint, so this is reachable from the documented API.

How to Reproduce / 如何复现
import tempfile
from pathlib import Path
import torch
from mcore_bridge.utils.safetensors import SafetensorLazyLoader, StreamingSafetensorSaver

d = Path(tempfile.mkdtemp())

# An earlier 3-shard export, as left behind by a previous run.
from safetensors.torch import save_file
import json
save_file({'w0': torch.zeros(3), 'w1': torch.zeros(3)}, str(d / 'model-00001-of-00003.safetensors'))
save_file({'w2': torch.zeros(3)}, str(d / 'model-00002-of-00003.safetensors'))
save_file({'w3': torch.zeros(3)}, str(d / 'model-00003-of-00003.safetensors'))
(d / 'model.safetensors.index.json').write_text(json.dumps({'metadata': {'total_size': 48}, 'weight_map': {
    'w0': 'model-00001-of-00003.safetensors', 'w1': 'model-00001-of-00003.safetensors',
    'w2': 'model-00002-of-00003.safetensors', 'w3': 'model-00003-of-00003.safetensors'}}))

# Re-export the model now that it fits in one file.
saver = StreamingSafetensorSaver(save_dir=str(d), max_shard_size='5GB')
saver.add_tensor('w0', torch.arange(4, dtype=torch.float32))
saver.finalize()

print(sorted(p.name for p in d.iterdir()))
# ['model-00001-of-00003.safetensors', 'model-00002-of-00003.safetensors',
#  'model-00003-of-00003.safetensors', 'model.safetensors', 'model.safetensors.index.json']

loaded = SafetensorLazyLoader(str(d)).get_state_dict()
print(sorted(loaded))                 # ['w0', 'w1', 'w2', 'w3'] - three tensors that are no longer saved
print(loaded['w0'].load().tolist())   # [0.0, 0.0, 0.0] - the old values, not [0, 1, 2, 3]
Expected behavior / 预期行为

The directory holds exactly the files of the export that just ran: model.safetensors here, no stale shards, no stale index. Reloading it returns the tensors that were written.

Environment info / 环境信息
  • mcore_bridge main at 6160abe

  • torch 2.8.0, safetensors 0.8.0

  • Reproduced on both Linux and Windows

  • I have searched existing issues, and this is a new bug report. / 我已经搜索过现有的 issues,确认这是一个新的 bug report。

Lenguaje dominante
Python
Estrellas
106
Forks
53
Merge medio
2 d 6 h
PR fusionados (30 d)
18

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de modelscope/mcore-bridge

Todos los issues de modelscope/mcore-bridge

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.