179 lines
6.1 KiB
Python
179 lines
6.1 KiB
Python
"""Help functions for detecting weight paths and weight formats."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import List, Optional, Tuple # noqa: UP035
|
|
|
|
from . import logging
|
|
from .style import bold, green, red
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
FOUND = green("Found")
|
|
NOT_FOUND = red("Not found")
|
|
|
|
|
|
def detect_weight(
|
|
weight_path: Path,
|
|
config_json_path: Path,
|
|
weight_format: str = "auto",
|
|
) -> Tuple[Path, str]: # noqa: UP006
|
|
"""Detect the weight directory, and detect the weight format.
|
|
|
|
Parameters
|
|
---------
|
|
weight_path : pathlib.Path
|
|
The path to weight files. If `weight_path` is not None, check if it exists. Otherwise, find
|
|
`weight_path` in `config.json` or use the same directory as `config.json`.
|
|
|
|
config_json_path: pathlib.Path
|
|
The path to `config.json`.
|
|
|
|
weight_format : str
|
|
The hint for the weight format. If it is "auto", guess the weight format.
|
|
Otherwise, check the weights are in that format.
|
|
Available weight formats:
|
|
- auto (guess the weight format)
|
|
- huggingface-torch (validate via checking pytorch_model.bin.index.json)
|
|
- huggingface-safetensor (validate via checking model.safetensors.index.json)
|
|
- awq
|
|
- ggml
|
|
- gguf
|
|
|
|
Returns
|
|
-------
|
|
weight_config_path : pathlib.Path
|
|
The path that points to the weights config file or the weights directory.
|
|
|
|
weight_format : str
|
|
The valid weight format.
|
|
"""
|
|
if weight_path is None:
|
|
assert config_json_path is not None and config_json_path.exists(), (
|
|
"Please provide config.json path."
|
|
)
|
|
|
|
# 1. Find the weight_path in config.json
|
|
with open(config_json_path, encoding="utf-8") as i_f:
|
|
config = json.load(i_f)
|
|
if "weight_path" in config:
|
|
weight_path = Path(config["weight_path"])
|
|
logger.info('Found "weight_path" in config.json: %s', weight_path)
|
|
if not weight_path.exists():
|
|
raise ValueError(f"weight_path doesn't exist: {weight_path}")
|
|
else:
|
|
# 2. Find the weights file in the same directory as config.json
|
|
weight_path = config_json_path.parent
|
|
else:
|
|
if not weight_path.exists():
|
|
raise ValueError(f"weight_path doesn't exist: {weight_path}")
|
|
|
|
logger.info("Finding weights in: %s", weight_path)
|
|
|
|
# check weight format
|
|
# weight_format = "auto", guess the weight format.
|
|
# otherwise, check the weight format is valid.
|
|
if weight_format == "auto":
|
|
return _guess_weight_format(weight_path)
|
|
|
|
if weight_format not in AVAILABLE_WEIGHT_FORMAT:
|
|
raise ValueError(
|
|
f"Available weight format list: {AVAILABLE_WEIGHT_FORMAT}, but got {weight_format}"
|
|
)
|
|
if weight_format in CHECK_FORMAT_METHODS:
|
|
check_func = CHECK_FORMAT_METHODS[weight_format]
|
|
weight_config_path = check_func(weight_path)
|
|
if not weight_config_path:
|
|
raise ValueError(f"The weight is not in {weight_format} format.")
|
|
else:
|
|
weight_config_path = weight_path
|
|
return weight_config_path, weight_format
|
|
|
|
|
|
def _guess_weight_format(weight_path: Path) -> Tuple[Path, str]: # noqa: UP006
|
|
possible_formats: List[Tuple[Path, str]] = [] # noqa: UP006
|
|
for weight_format, check_func in CHECK_FORMAT_METHODS.items():
|
|
weight_config_path = check_func(weight_path)
|
|
if weight_config_path:
|
|
possible_formats.append((weight_config_path, weight_format))
|
|
|
|
if len(possible_formats) == 0:
|
|
raise ValueError(
|
|
"Fail to detect source weight format. "
|
|
"Use `--source-format` to explicitly specify the format."
|
|
)
|
|
|
|
weight_config_path, selected_format = possible_formats[0]
|
|
logger.info(
|
|
"Using source weight configuration: %s. Use `--source` to override.",
|
|
bold(str(weight_config_path)),
|
|
)
|
|
logger.info(
|
|
"Using source weight format: %s. Use `--source-format` to override.",
|
|
bold(selected_format),
|
|
)
|
|
return weight_config_path, selected_format
|
|
|
|
|
|
def _check_pytorch(weight_path: Path) -> Optional[Path]:
|
|
pytorch_json_path = weight_path / "pytorch_model.bin.index.json"
|
|
if pytorch_json_path.exists():
|
|
logger.info(
|
|
"%s source weight format: huggingface-torch. Source configuration: %s",
|
|
FOUND,
|
|
pytorch_json_path,
|
|
)
|
|
return pytorch_json_path
|
|
|
|
pytorch_file_path = weight_path / "pytorch_model.bin"
|
|
if pytorch_file_path.exists():
|
|
logger.info(
|
|
"%s source weight format: huggingface-torch. Source configuration: %s",
|
|
FOUND,
|
|
pytorch_file_path,
|
|
)
|
|
return pytorch_file_path
|
|
|
|
logger.info("%s Huggingface PyTorch", NOT_FOUND)
|
|
return None
|
|
|
|
|
|
def _check_safetensor(weight_path: Path) -> Optional[Path]:
|
|
safetensor_json_path = weight_path / "model.safetensors.index.json"
|
|
if safetensor_json_path.exists():
|
|
logger.info(
|
|
"%s source weight format: huggingface-safetensor. Source configuration: %s",
|
|
FOUND,
|
|
safetensor_json_path,
|
|
)
|
|
return safetensor_json_path
|
|
|
|
safetensor_file_path = weight_path / "model.safetensors"
|
|
if safetensor_file_path.exists():
|
|
from safetensors.torch import (
|
|
load_file,
|
|
)
|
|
|
|
weights = load_file(safetensor_file_path, device="cpu")
|
|
weight_map = {key: "model.safetensors" for key in weights}
|
|
with open(safetensor_json_path, "w", encoding="utf-8") as file:
|
|
json.dump({"weight_map": weight_map}, file, indent=2)
|
|
logger.info(
|
|
"%s source weight format: huggingface-safetensor. Source configuration: %s",
|
|
FOUND,
|
|
safetensor_json_path,
|
|
)
|
|
return safetensor_json_path
|
|
|
|
logger.info("%s Huggingface Safetensor", NOT_FOUND)
|
|
return None
|
|
|
|
|
|
CHECK_FORMAT_METHODS = {
|
|
"huggingface-torch": _check_pytorch,
|
|
"huggingface-safetensor": _check_safetensor,
|
|
}
|
|
|
|
# "ggml", "gguf" are not supported yet.
|
|
AVAILABLE_WEIGHT_FORMAT = ["huggingface-torch", "huggingface-safetensor", "awq"]
|