Files
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

310 lines
10 KiB
Python

# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import io
import os
import pickle
from functools import lru_cache
from typing import Union
from zipfile import ZipFile
import numpy as np
import paddle
from _io import BufferedReader
from safetensors import deserialize
from paddlenlp.utils.env import PYTORCH_WEIGHTS_NAME, SAFE_WEIGHTS_NAME
MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30
_TYPES = {
"F64": np.float64,
"F32": np.float32,
"F16": np.float16,
"I64": np.int64,
"U64": np.uint64,
"I32": np.int32,
"U32": np.uint32,
"I16": np.int16,
"U16": np.uint16,
"BF16": np.uint16,
"I8": np.int8,
"U8": np.uint8,
"BOOL": bool,
}
class SerializationError(Exception):
"""Exception for serialization"""
pass
def seek_by_string(file_handler: BufferedReader, string: str, file_size: int) -> int:
"""seek the index of file-handler with target words
Args:
file_handler (BufferedReader): file handler
string (str): the specific string in the file
file_size (int): size of file
Returns:
int: end index of target string
"""
word_index = 0
word_bytes = string.encode("latin")
empty_byte = "".encode("latin")
while word_index < len(string) and file_handler.tell() < file_size:
content = file_handler.read(1)
if content == empty_byte:
break
if word_bytes[word_index] == content[0]:
word_index += 1
else:
word_index = 0
if file_handler.tell() >= file_size - 1:
raise SerializationError(f"can't find the find the target string<{string}> in the file")
return file_handler.tell()
def read_prefix_key(path):
file_size = os.stat(path).st_size
with open(path, "rb") as file_handler:
end_index = seek_by_string(file_handler, "data.pkl", file_size)
file_handler.seek(MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
prefix_key = file_handler.read(end_index - MZ_ZIP_LOCAL_DIR_HEADER_SIZE - len("/data.pkl"))
return prefix_key.decode("latin")
def _maybe_decode_ascii(bytes_str: Union[bytes, str]) -> str:
if isinstance(bytes_str, bytes):
return bytes_str.decode("ascii")
return bytes_str
@lru_cache(maxsize=None)
def _storage_type_to_dtype_to_map():
"""convert storage type to numpy dtype"""
return {
"DoubleStorage": np.double,
"FloatStorage": np.float32,
"HalfStorage": np.half,
"LongStorage": np.int64,
"IntStorage": np.int32,
"ShortStorage": np.int16,
"CharStorage": np.int8,
"ByteStorage": np.uint8,
"BoolStorage": np.bool_,
"ComplexDoubleStorage": np.cdouble,
"ComplexFloatStorage": np.cfloat,
"BFloat16Storage": np.uint16, # support bf16
}
class StorageType:
"""Temp Class for Storage Type"""
def __init__(self, name):
self.dtype = _storage_type_to_dtype_to_map()[name]
def __str__(self):
return f"StorageType(dtype={self.dtype})"
def _element_size(dtype: str) -> int:
"""
Returns the element size for a dtype, in bytes
"""
if dtype in [np.float16, np.float32, np.float64]:
return np.finfo(dtype).bits >> 3
elif dtype == np.bool_:
return 1
else:
return np.iinfo(dtype).bits >> 3
class UnpicklerWrapperStage(pickle.Unpickler):
def find_class(self, mod_name, name):
if type(name) is str and "Storage" in name:
try:
return StorageType(name)
except KeyError:
pass
if mod_name == "torch._utils":
# rebuild torch.nn.Papameter
if name == "_rebuild_parameter":
return _rebuild_parameter
# rebuild torch.nn.Papameter with state
if name == "_rebuild_parameter_with_state":
return _rebuild_parameter_with_state
# rebuild torch.Tensor
return _rebuild_tensor_stage
# pytorch_lightning tensor builder
if "pytorch_lightning" in mod_name:
return dumpy
return super().find_class(mod_name, name)
class SafeUnpickler(pickle.Unpickler):
"""
A safe unpickler that only allows loading of built-in basic data types.
"""
def find_class(self, module, name):
"""
Overrides the find_class method to only allow loading of built-in basic data types.
:param module: The module name.
:param name: The class name.
:return: The class if allowed, otherwise raises UnpicklingError.
"""
if module == "builtins" and name in {"int", "float", "str", "tuple", "list", "dict", "set"}:
return super().find_class(module, name)
raise pickle.UnpicklingError(f"Unsafe object loading is prohibited: {module}.{name}")
def _rebuild_tensor_stage(storage, storage_offset, size, stride, requires_grad, backward_hooks):
# if a tensor has shape [M, N] and stride is [1, N], it's column-wise / fortran-style
# if a tensor has shape [M, N] and stride is [M, 1], it's row-wise / C-style
# defaults to C-style
if stride is not None and len(stride) > 1 and stride[0] == 1 and stride[1] > 1:
order = "F"
else:
order = "C"
# fix bug when load https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth
numel = int(np.prod(size))
return storage[storage_offset : storage_offset + numel].reshape(size, order=order)
def _rebuild_parameter(data, requires_grad, backward_hooks):
return data
def _rebuild_parameter_with_state(data, requires_grad, backward_hooks, state):
return data
def dumpy(*args, **kwarsg):
return None
def load_torch(path: str, **pickle_load_args):
from paddlenlp.transformers.utils import device_guard
if path.endswith(PYTORCH_WEIGHTS_NAME) or os.path.split(path)[-1].startswith("pytorch_model-"):
import torch
state_dict = torch.load(path, map_location="cpu", weights_only=False)
for key in list(state_dict.keys()):
if isinstance(state_dict[key], torch.Tensor):
t = state_dict.pop(key)
capsule = torch.utils.dlpack.to_dlpack(t)
t = paddle.utils.dlpack.from_dlpack(capsule)
state_dict[key] = t
return state_dict
elif path.endswith(SAFE_WEIGHTS_NAME) or os.path.split(path)[-1].startswith("model-"):
# torch safetensors -> numpy -> paddle.Tensor
with open(path, "rb") as f:
data = f.read()
flat = deserialize(data)
state_dict = {}
for k, v in flat:
dtype = _TYPES[v["dtype"]]
with device_guard("cpu"):
if v["dtype"] == "BF16":
arr = paddle.Tensor(
np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]), dtype="bfloat16", zero_copy=True
)
else:
arr = paddle.Tensor(np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]), zero_copy=True)
state_dict[k] = arr
return state_dict
def load_torch_inner(path: str, **pickle_load_args):
"""
load torch weight file with the following steps:
1. load the structure of pytorch weight file
2. read the tensor data and re-construct the state-dict
Args:
path: the path of pytorch weight file
**pickle_load_args: args of pickle module
Returns:
"""
if path.endswith(PYTORCH_WEIGHTS_NAME) or os.path.split(path)[-1].startswith("pytorch_model-"):
pickle_load_args.update({"encoding": "utf-8"})
prefix_key = read_prefix_key(path)
torch_zip = ZipFile(path, "r")
loaded_storages = {}
def load_tensor(dtype, numel, key, location):
name = f"{prefix_key}/data/{key}"
typed_storage = np.frombuffer(torch_zip.open(name).read()[:numel], dtype=dtype)
return typed_storage
def persistent_load(saved_id):
assert isinstance(saved_id, tuple)
typename = _maybe_decode_ascii(saved_id[0])
data = saved_id[1:]
assert (
typename == "storage"
), f"Unknown typename for persistent_load, expected 'storage' but got '{typename}'"
storage_type, key, location, numel = data
dtype = storage_type.dtype
if key in loaded_storages:
typed_storage = loaded_storages[key]
else:
nbytes = numel * _element_size(dtype)
typed_storage = load_tensor(dtype, nbytes, key, _maybe_decode_ascii(location))
loaded_storages[key] = typed_storage
return typed_storage
data_iostream = torch_zip.open(f"{prefix_key}/data.pkl").read()
unpickler_stage = UnpicklerWrapperStage(io.BytesIO(data_iostream), **pickle_load_args)
unpickler_stage.persistent_load = persistent_load
state_dict = unpickler_stage.load()
torch_zip.close()
elif path.endswith(SAFE_WEIGHTS_NAME) or os.path.split(path)[-1].startswith("model-"):
# torch safetensors -> numpy -> paddle.Tensor
with open(path, "rb") as f:
data = f.read()
flat = deserialize(data)
state_dict = {}
for k, v in flat:
dtype = _TYPES[v["dtype"]]
if v["dtype"] == "BF16":
arr = paddle.to_tensor(np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]), dtype="bfloat16")
else:
arr = paddle.to_tensor(np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]))
state_dict[k] = arr
return state_dict