Recap
이전글에서 GGUF 파일 포맷 구조를 간단히 살펴보았다.
2024.01.31 - [Develop] - GGUF (Georgi Gerganov Unified Format)
GGUF (Georgi Gerganov Unified Format)
GGUF GGUF 는 Georgi Gerganov(@ggerganov)란 개발자가 만든 딥러닝 모델을 저장 용도의 단일 파일 포맷이다. GGML 라이브러리 기반의 런타임에서 주로 사용이 되고 있다. 현재는 주로 LLM 추론에 많이 활용이
bitwise-life.tistory.com
이번 글에서는 GGUF의 구조를 C++ 코드 레벨로 살펴보고, 이 구조를 참고해서 GGUF 파일을 읽어서 내부 정보를 출력하는 스크립트를 만들어 볼 것이다.
Header
Header 구조체
struct gguf_header_t {
uint32_t magic;
uint32_t version;
uint64_t tensor_count;
uint64_t metadata_kv_count;
// The metadata key-value pairs.
gguf_metadata_kv_t metadata_kv[metadata_kv_count];
};
Header에는 매직넘버와 구조체의 버전 명이 있다.
GGUF는 사실 처음 나온 포맷이 아니며, 앞서 GGML, GGJT란 포맷이 존재했다.
하지만 GGUF가 가장 최신의 버전이고 현재는 이 포맷을 주로 쓰고 있다.
매직넘버는 `0x47` `0x47` `0x55` `0x46` 의 4바이트의 정수이다. 파일을 읽을 때 처음 이 매직넘버를 보고 이 파일이 GGUF인지 아닌지 검사한다.
메타데이터 구조체
struct gguf_metadata_kv_t {
gguf_string_t key;
gguf_metadata_value_type value_type;
gguf_metadata_value_t value;
};
내가 관심이 있는 구조체는 메타데이터 파트이다.
Key-Value 쌍의 메타데이터를 담고 있다. Key는 문자열 타입이고, 문자열 저장을 위한 구조체가 선언되어있다. (gguf_string_t)
Value를 저장하기 위해 데이터 타입과 값을 저장하고 있다.
값은 아마도 이진형태의 데이터이니 사실상 중요한건 value_type의 구조체일 것이다.
문자열 타입과 메타데이터 값 구조체
struct gguf_string_t {
// The length of the string, in bytes.
uint64_t len;
// The string as a UTF-8 non-null-terminated string.
char string[len];
}
enum gguf_metadata_value_type: uint32_t {
GGUF_METADATA_VALUE_TYPE_UINT8 = 0,
GGUF_METADATA_VALUE_TYPE_INT8 = 1,
GGUF_METADATA_VALUE_TYPE_UINT16 = 2,
GGUF_METADATA_VALUE_TYPE_INT16 = 3,
GGUF_METADATA_VALUE_TYPE_UINT32 = 4,
GGUF_METADATA_VALUE_TYPE_INT32 = 5,
GGUF_METADATA_VALUE_TYPE_FLOAT32 = 6,
GGUF_METADATA_VALUE_TYPE_BOOL = 7,
GGUF_METADATA_VALUE_TYPE_STRING = 8,
GGUF_METADATA_VALUE_TYPE_ARRAY = 9,
GGUF_METADATA_VALUE_TYPE_UINT64 = 10,
GGUF_METADATA_VALUE_TYPE_INT64 = 11,
GGUF_METADATA_VALUE_TYPE_FLOAT64 = 12,
}
union gguf_metadata_value_t {
uint8_t uint8;
int8_t int8;
uint16_t uint16;
int16_t int16;
uint32_t uint32;
int32_t int32;
float float32;
uint64_t uint64;
int64_t int64;
double float64;
bool bool_;
gguf_string_t string;
struct {
gguf_metadata_value_type type;
uint64_t len;
gguf_metadata_value_t array[len];
} array;
};
문자열은 16 바이트와 1바이트의 배열로 이뤄져있다. 단순히 len 값을 보고 이 값만큼의 바이트를 읽으면 되겠다.
gguf_metadata_value_type 으로 값의 타입을 기록한다.
대부분 특별하진 않지만 다만 유의해야할 점이 있다. GGUF_METADATA_VALUE_TYPE_BOOL은 boolean 타입인데 C++에서 boolean 타입의 값은 1바이트를 차지하기 때문에 유의해야한다. 그래서 1 바이트를 읽어줘야한다.
gguf_metadata_value_t는 유니온 구조체로 값을 표현하고 있고, array만 len만큼 값을 반복적으로 읽어오도록 구현하는 것 외에 특별한 것은 없다.
Tensor Info
Tensor Info는 아주 단순하다.
Header에서 읽어온 tensor_count만큼 아래 gguf_tensor_info_t 구조체를 읽어준다.
Tensor Info 구조체
struct gguf_tensor_info_t {
gguf_string_t name;
uint32_t n_dimensions;
uint64_t dimensions[n_dimensions];
ggml_type type;
uint64_t offset;
};
이름, 차원 수 그리고 Tensor의 데이터 타입 그리고 위치 정보를 읽는다.
ggml_type은 아래처럼 4바이트 정수이다.
텐서 타입 구조체
enum ggml_type: uint32_t {
GGML_TYPE_F32 = 0,
GGML_TYPE_F16 = 1,
GGML_TYPE_Q4_0 = 2,
GGML_TYPE_Q4_1 = 3,
// GGML_TYPE_Q4_2 = 4, support has been removed
// GGML_TYPE_Q4_3 (5) support has been removed
GGML_TYPE_Q5_0 = 6,
GGML_TYPE_Q5_1 = 7,
GGML_TYPE_Q8_0 = 8,
GGML_TYPE_Q8_1 = 9,
// k-quantizations
GGML_TYPE_Q2_K = 10,
GGML_TYPE_Q3_K = 11,
GGML_TYPE_Q4_K = 12,
GGML_TYPE_Q5_K = 13,
GGML_TYPE_Q6_K = 14,
GGML_TYPE_Q8_K = 15,
GGML_TYPE_I8,
GGML_TYPE_I16,
GGML_TYPE_I32,
GGML_TYPE_COUNT,
};
이번에는 GGUF 의 메타데이터를 출력해보자.
위의 구조를 토대로 GGUF 포맷의 Key Value형식의 메타데이터와 Weight Tensor 정보를 출력하는 툴을 만들어았다.
만들다보니.. 생각보다 길어져서 github 에 올렸다.
https://github.com/mozzilemon/print_gguf
GitHub - mozzilemon/print_gguf
Contribute to mozzilemon/print_gguf development by creating an account on GitHub.
github.com
사용 방법은 간단하다.
python print_gguf.py {gguf_file}
출력 예시)
Array 타입의 경우 굉장히 길이가 길어져서 글 줄임표를 추가했다.
magic = 0x46554747
version = 3
tensor_count = 291
metadata_kv_count = 16
general.architecture = llama
general.name = LLaMA
llama.context_length = 2048
llama.embedding_length = 4096
llama.block_count = 32
llama.feed_forward_length = 11008
llama.rope.dimension_count = 128
llama.attention.head_count = 32
llama.attention.head_count_kv = 32
llama.attention.layer_norm_rms_epsilon = 9.999999974752427e-07
general.file_type = 10
tokenizer.ggml.model = llama
tokenizer.ggml.tokens = ['<unk>', '<s>', '</s>', '<0x00>', '<0x01>', '<...
tokenizer.ggml.scores = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0...
tokenizer.ggml.token_type = [2, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6...
general.quantization_version = 2
name = token_embd.weight
n_dimensions = 2
shape = [4096, 32000]
ggml_type = GGML_TYPE_Q2_K
offset = 0
==================================================
name = output_norm.weight
n_dimensions = 1
shape = [4096]
ggml_type = GGML_TYPE_FP32
offset = 43008000
==================================================
name = output.weight
n_dimensions = 2
shape = [4096, 32000]
ggml_type = GGML_TYPE_Q6_K
offset = 43024384
==================================================
name = blk.0.attn_q.weight
n_dimensions = 2
shape = [4096, 4096]
ggml_type = GGML_TYPE_Q2_K
offset = 150544384
==================================================
결과는 꽤나 만족스럽게 나온 것 같다. 내가 사용한 gguf 파일은
https://huggingface.co/ikawrakow/llama-v1-2bit-gguf/blob/main/llama-v1-7b-q2k.gguf 이곳에서 확인할 수 있다.
'Develop > Machine Learning' 카테고리의 다른 글
GGUF (Georgi Gerganov Unified Format) (1) | 2024.01.31 |
---|