학습 후, cpp 아키텍쳐 기반 모델은 GGUF(GPT-Generated Unified Format) 포맷을 사용해서 변환된다. GGUF는 GGML의 단점을 보완한 추론동안 저장하는 모델에 대한 새로운 포맷으로 모델을 쉽게 load하고 save하기 위해 디자인된 binary format이다.
GPU compute & high precision vs CPU compute and low precision LLM 파라미터는 32 bit floating number인 반면, GGML은 16 bit floating point여서 LLM을 로드하고 run 하는데 메모리가 50% 줄어든다. GGML 라이브러리는 int 양자화를 지원한다.
더 나은 tokenization, special token에 대한 지원, metadata 저장. Falcon, Bloom, Phi, Mistral과 같은 non-llama 아키텍쳐에 대해서도 지원.
GGML처럼 하나의 파일에 metadata, data, hyperparameters 모두 저장됩니다. 기존 GGML과의 차이점은 GGUF는 새로운 피쳐 추가도 가능하도록 디자인되었고, untyped value list보다 (metadata라고 불리는) 하이퍼파라미터에 대해 key-value 구조를 사용합니다.
inference할떄 파라미터 넣을 수 있게하는게 허깅페이스 모델과 다르다
gguf랑 hf랑 파라미터가 다르다
GGUF Header
1. Magic number (4 bytes) to announce that this is a GGUF file,
2. Version (4 bytes) to specify the version of the format implemented,
3. Tensor count (8 bytes): the number of tensors in the file,
4. Metadata key-value pairs count (8 bytes): the number of metadata key-value pairs,
Metadata key-value pairs
텐서 정보
1. Name: name of the tensor, a standard GGUF string,
2. Dimension: number of dimensions in the tensor,
3. Type: very important information. This is the type of the tensor to determine the Quantization level of the Model.
4. Offset: position of the tensor data in the file
패딩
텐서 데이터: 원래 모델 파일 데이터와 유사하거나 같은 binary data지만 추론 최적화 혹은 양자화가 적용된.
GGUF에서 일반적인 텐서 타입
F32 (Single-precision floating-point): precision과 efficiency간의 균형을 제공하는 데이터 타입으로 널리 사용된다.
INT8 (8-bit signed integer): value에 8 bit 사용해서 메모리 효율
BF16 (Brain Floating-Point 16-bit)
block_count
find_hparam 으로 뭘 가져오는 건지?
bid?
def get_tensor_name_map
https://pastebin.com/5dhCEtkz
https://huggingface.co/docs/transformers/big_models
compiler
memory
def get_tensors(self) -> Iterator[tuple[str, Tensor]]:
tensor_names_from_parts: set[str] = set()
if len(self.part_names) > 1:
self.tensor_names = set()
index_name = "model.safetensors" if self.is_safetensors else "pytorch_model.bin"
index_name += ".index.json"
logger.info(f"gguf: loading model weight map from '{index_name}'")
with open(self.dir_model / index_name, "r", encoding="utf-8") as f:
index: dict[str, Any] = json.load(f)
weight_map = index.get("weight_map")
if weight_map is None or not isinstance(weight_map, dict):
raise ValueError(f"Can't load 'weight_map' from {index_name!r}")
self.tensor_names.update(weight_map.keys())
else:
self.tensor_names = tensor_names_from_parts
for part_name in self.part_names:
logger.info(f"gguf: loading model part '{part_name}'")
ctx: ContextManager[Any]
if self.is_safetensors:
from safetensors import safe_open
ctx = cast(ContextManager[Any], safe_open(self.dir_model / part_name, framework="pt", device="cpu"))
else:
ctx = contextlib.nullcontext(torch.load(str(self.dir_model / part_name), map_location="cpu", mmap=True, weights_only=True))
with ctx as model_part:
tensor_names_from_parts.update(model_part.keys())
for name in model_part.keys():
data = model_part.get_tensor(name) if self.is_safetensors else model_part[name]
if self.lazy:
data = LazyTorchTensor.from_eager(data)
yield name, data
# only verify tensor name presence; it doesn't matter if they are not in the right files
if len(sym_diff := tensor_names_from_parts.symmetric_difference(self.tensor_names)) > 0:
raise ValueError(f"Mismatch between weight map and model parts for tensor names: {sym_diff}")
왜 삭제를 할까
def extra_f32_tensors(self, name: str, new_name: str, bid: int | None, n_dims: int) -> bool:
del name, new_name, bid, n_dims # unused
return False
def extra_f16_tensors(self, name: str, new_name: str, bid: int | None, n_dims: int) -> bool:
del name, new_name, bid, n_dims # unused
return False
def write_tensors(self):
텐서 이름을 읽는다는거?
https://github.com/ggerganov/llama.cpp/blob/master/convert-hf-to-gguf.py