paddle.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import os
  2. import sys
  3. from typing import Any, Dict, Optional, Union
  4. import numpy as np
  5. import paddle
  6. from safetensors import numpy, deserialize, safe_open, serialize, serialize_file
  7. def save(
  8. tensors: Dict[str, paddle.Tensor], metadata: Optional[Dict[str, str]] = None
  9. ) -> bytes:
  10. """
  11. Saves a dictionary of tensors into raw bytes in safetensors format.
  12. Args:
  13. tensors (`Dict[str, paddle.Tensor]`):
  14. The incoming tensors. Tensors need to be contiguous and dense.
  15. metadata (`Dict[str, str]`, *optional*, defaults to `None`):
  16. Optional text only metadata you might want to save in your header.
  17. For instance it can be useful to specify more about the underlying
  18. tensors. This is purely informative and does not affect tensor loading.
  19. Returns:
  20. `bytes`: The raw bytes representing the format
  21. Example:
  22. ```python
  23. from safetensors.paddle import save
  24. import paddle
  25. tensors = {"embedding": paddle.zeros((512, 1024)), "attention": paddle.zeros((256, 256))}
  26. byte_data = save(tensors)
  27. ```
  28. """
  29. serialized = serialize(_flatten(tensors), metadata=metadata)
  30. result = bytes(serialized)
  31. return result
  32. def save_file(
  33. tensors: Dict[str, paddle.Tensor],
  34. filename: Union[str, os.PathLike],
  35. metadata: Optional[Dict[str, str]] = None,
  36. ) -> None:
  37. """
  38. Saves a dictionary of tensors into raw bytes in safetensors format.
  39. Args:
  40. tensors (`Dict[str, paddle.Tensor]`):
  41. The incoming tensors. Tensors need to be contiguous and dense.
  42. filename (`str`, or `os.PathLike`)):
  43. The filename we're saving into.
  44. metadata (`Dict[str, str]`, *optional*, defaults to `None`):
  45. Optional text only metadata you might want to save in your header.
  46. For instance it can be useful to specify more about the underlying
  47. tensors. This is purely informative and does not affect tensor loading.
  48. Returns:
  49. `None`
  50. Example:
  51. ```python
  52. from safetensors.paddle import save_file
  53. import paddle
  54. tensors = {"embedding": paddle.zeros((512, 1024)), "attention": paddle.zeros((256, 256))}
  55. save_file(tensors, "model.safetensors")
  56. ```
  57. """
  58. serialize_file(_flatten(tensors), filename, metadata=metadata)
  59. def load(data: bytes, device: str = "cpu") -> Dict[str, paddle.Tensor]:
  60. """
  61. Loads a safetensors file into paddle format from pure bytes.
  62. Args:
  63. data (`bytes`):
  64. The content of a safetensors file
  65. Returns:
  66. `Dict[str, paddle.Tensor]`: dictionary that contains name as key, value as `paddle.Tensor` on cpu
  67. Example:
  68. ```python
  69. from safetensors.paddle import load
  70. file_path = "./my_folder/bert.safetensors"
  71. with open(file_path, "rb") as f:
  72. data = f.read()
  73. loaded = load(data)
  74. ```
  75. """
  76. if paddle.__version__ >= "3.2.0":
  77. flat = deserialize(data)
  78. return _view2paddle(flat, device)
  79. else:
  80. flat = numpy.load(data)
  81. return _np2paddle(flat, device)
  82. def load_file(
  83. filename: Union[str, os.PathLike], device="cpu"
  84. ) -> Dict[str, paddle.Tensor]:
  85. """
  86. Loads a safetensors file into paddle format.
  87. Args:
  88. filename (`str`, or `os.PathLike`)):
  89. The name of the file which contains the tensors
  90. device (`Union[Dict[str, any], str]`, *optional*, defaults to `cpu`):
  91. The device where the tensors need to be located after load.
  92. available options are all regular paddle device locations
  93. Returns:
  94. `Dict[str, paddle.Tensor]`: dictionary that contains name as key, value as `paddle.Tensor`
  95. Example:
  96. ```python
  97. from safetensors.paddle import load_file
  98. file_path = "./my_folder/bert.safetensors"
  99. loaded = load_file(file_path)
  100. ```
  101. """
  102. result = {}
  103. if paddle.__version__ >= "3.2.0":
  104. with safe_open(filename, framework="paddle", device=device) as f:
  105. for k in f.offset_keys():
  106. result[k] = f.get_tensor(k)
  107. else:
  108. flat = numpy.load_file(filename)
  109. result = _np2paddle(flat, device)
  110. return result
  111. def _np2paddle(
  112. numpy_dict: Dict[str, np.ndarray], device: str = "cpu"
  113. ) -> Dict[str, paddle.Tensor]:
  114. for k, v in numpy_dict.items():
  115. numpy_dict[k] = paddle.to_tensor(v, place=device)
  116. return numpy_dict
  117. def _paddle2np(paddle_dict: Dict[str, paddle.Tensor]) -> Dict[str, np.array]:
  118. for k, v in paddle_dict.items():
  119. paddle_dict[k] = v.detach().cpu().numpy()
  120. return paddle_dict
  121. _SIZE = {
  122. paddle.int64: 8,
  123. paddle.float32: 4,
  124. paddle.int32: 4,
  125. paddle.bfloat16: 2,
  126. paddle.float16: 2,
  127. paddle.int16: 2,
  128. paddle.uint8: 1,
  129. paddle.int8: 1,
  130. paddle.bool: 1,
  131. paddle.float64: 8,
  132. paddle.float8_e4m3fn: 1,
  133. paddle.float8_e5m2: 1,
  134. paddle.complex64: 8,
  135. # XXX: These are not supported yet in paddle
  136. # paddle.uint64: 8,
  137. # paddle.uint32: 4,
  138. # paddle.uint16: 2,
  139. # paddle.float8_e8m0: 1,
  140. # paddle.float4_e2m1_x2: 1,
  141. }
  142. _TYPES = {
  143. "F64": paddle.float64,
  144. "F32": paddle.float32,
  145. "F16": paddle.float16,
  146. "BF16": paddle.bfloat16,
  147. "I64": paddle.int64,
  148. "I32": paddle.int32,
  149. "I16": paddle.int16,
  150. "I8": paddle.int8,
  151. "U8": paddle.uint8,
  152. "BOOL": paddle.bool,
  153. "F8_E4M3": paddle.float8_e4m3fn,
  154. "F8_E5M2": paddle.float8_e5m2,
  155. }
  156. NPDTYPES = {
  157. paddle.int64: np.int64,
  158. paddle.float32: np.float32,
  159. paddle.int32: np.int32,
  160. # XXX: This is ok because both have the same width
  161. paddle.bfloat16: np.float16,
  162. paddle.float16: np.float16,
  163. paddle.int16: np.int16,
  164. paddle.uint8: np.uint8,
  165. paddle.int8: np.int8,
  166. paddle.bool: bool,
  167. paddle.float64: np.float64,
  168. # XXX: This is ok because both have the same width and byteswap is a no-op anyway
  169. paddle.float8_e4m3fn: np.uint8,
  170. paddle.float8_e5m2: np.uint8,
  171. }
  172. def _getdtype(dtype_str: str) -> paddle.dtype:
  173. return _TYPES[dtype_str]
  174. def _view2paddle(safeview, device) -> Dict[str, paddle.Tensor]:
  175. result = {}
  176. for k, v in safeview:
  177. dtype = _getdtype(v["dtype"])
  178. if len(v["data"]) == 0:
  179. # Workaround because frombuffer doesn't accept zero-size tensors
  180. assert any(x == 0 for x in v["shape"])
  181. arr = paddle.empty(v["shape"], dtype=dtype)
  182. else:
  183. arr = paddle.base.core.frombuffer(v["data"], dtype).reshape(v["shape"])
  184. if device != "cpu":
  185. arr = arr.to(device)
  186. if sys.byteorder == "big":
  187. arr = paddle.to_tensor(arr.numpy().byteswap(inplace=False), place=device)
  188. result[k] = arr
  189. return result
  190. def _tobytes(tensor: paddle.Tensor, name: str) -> bytes:
  191. if not tensor.is_contiguous():
  192. raise ValueError(
  193. f"You are trying to save a non contiguous tensor: `{name}` which is not allowed. It either means you"
  194. " are trying to save tensors which are reference of each other in which case it's recommended to save"
  195. " only the full tensors, and reslice at load time, or simply call `.contiguous()` on your tensor to"
  196. " pack it before saving."
  197. )
  198. if not tensor.place.is_cpu_place():
  199. # Moving tensor to cpu before saving
  200. tensor = tensor.cpu()
  201. import ctypes
  202. import numpy as np
  203. # When shape is empty (scalar), np.prod returns a float
  204. # we need a int for the following calculations
  205. length = int(np.prod(tensor.shape).item())
  206. bytes_per_item = _SIZE[tensor.dtype]
  207. total_bytes = length * bytes_per_item
  208. ptr = tensor.data_ptr()
  209. if ptr == 0:
  210. return b""
  211. newptr = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte))
  212. data = np.ctypeslib.as_array(newptr, (total_bytes,)) # no internal copy
  213. if sys.byteorder == "big":
  214. npdtype = NPDTYPES[tensor.dtype]
  215. # Not in place as that would potentially modify a live running model
  216. data = data.view(npdtype).byteswap(inplace=False)
  217. return data.tobytes()
  218. def _flatten(tensors: Dict[str, paddle.Tensor]) -> Dict[str, Dict[str, Any]]:
  219. if not isinstance(tensors, dict):
  220. raise ValueError(
  221. f"Expected a dict of [str, paddle.Tensor] but received {type(tensors)}"
  222. )
  223. for k, v in tensors.items():
  224. if not isinstance(v, paddle.Tensor):
  225. raise ValueError(
  226. f"Key `{k}` is invalid, expected paddle.Tensor but received {type(v)}"
  227. )
  228. return {
  229. k: {
  230. "dtype": str(v.dtype).split(".")[-1],
  231. "shape": v.shape,
  232. "data": _tobytes(v, k),
  233. }
  234. for k, v in tensors.items()
  235. }