当前位置: 首页 > news >正文

GGUF格式的DeepSeek-R1-Distill-Qwen-1.5B模型的字段解析

        在将GGUF文件转换为PyTorch格式之前,先要读取文件并了解模型中都有什么字段,会遇到了各种参数不匹配的问题。现在,我们先读取GGUF文件的元数据字段,并希望将这些字段中的内存映射(mmap)数据转换为字符串显示。为后续转换的字段映射做准备。

        通过遍历GGUFReader的字段,将每个字段的值从内存映射的数组转换为可读的字符串。这可能涉及到处理不同的数据类型,比如整数、浮点数、字符串或数组。特别是当字段的值是字符串类型时,需要正确解码字节数据,并去除可能的空字符。

        现在需要更通用的解决方案,适用于所有元数据字段,而不仅仅是模型参数。可能的步骤包括:

1. 遍历所有字段的键。

2. 对于每个字段,确定其类型(如字符串、整数、数组等)。

3. 根据类型,将内存映射的数据转换为适当的Python类型。

4. 处理字符串类型的特殊转换,包括解码字节和去除空字符。

5. 处理数值类型,如整数或浮点数,特别是当数组只有一个元素时转换为标量。

6. 处理数组类型,转换为列表形式。

需要注意的问题包括:

- 如何处理不同的GGUFValueType,例如STRING、UINT32、FLOAT32等。

- 如何处理数组类型的数据,可能需要使用numpy的tolist方法。

- 字符串类型可能需要处理多个部分,例如字段名称和实际值存储在不同的部分中。

- 内存映射的数据可能包含多个部分,需要正确选择包含实际值的部分。

        先下载DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf,可以在魔塔社区下载,也可以在我的CSDN下载:https://download.csdn.net/download/lzm12278828/90370217,并放到电脑中。之后确定程序的执行流程:

1.识别字段的类型。

2.根据类型转换对应的数据部分为适当的Python对象。

3.处理字符串类型的解码和清理。

4.处理数值和数组类型的转换。

以下是python代码,可以完整读取GGUF文件的所有元数据字段,并将内存映射(memmap)数据转换为可读字符串或适当的数据类型显示:

from gguf import GGUFReader, GGUFValueType
import numpy as np

def parse_gguf_field(field, key_name):
    """增强型GGUF字段解析(支持特殊字段处理)"""
    # 提取字段类型和值部分
    field_type = field.types[0] if field.types else GGUFValueType.UNKNOWN
    value_part = field.parts[-1] if len(field.parts) >= 4 else field.parts[-1]

    # 安全提取值的统一方法
    def extract_value(data):
        if isinstance(data, (np.ndarray, np.memmap)):
            return data.item() if data.size == 1 else data.tolist()
        return data

    # 特殊字段处理
    if key_name == "tokenizer.ggml.merges":
        return decode_bpe_merges(field.parts[3:])  # 跳过前三个描述性字段

    # 通用类型处理
    if field_type == GGUFValueType.STRING:
        raw = bytes(value_part).decode('utf-8', errors='replace').strip('\x00') if isinstance(value_part, (np.ndarray, np.memmap)) else str(value_part)
        return format_jinja_template(raw) if key_name == "tokenizer.chat_template" else raw
    
    elif field_type in (GGUFValueType.UINT32, GGUFValueType.INT32):
        return int(extract_value(value_part))
    
    elif field_type == GGUFValueType.FLOAT32:
        return float(extract_value(value_part))
    
    elif field_type == GGUFValueType.BOOL:
        return bool(extract_value(value_part))
    
    elif field_type == GGUFValueType.ARRAY:
        return [extract_value(part) for part in field.parts[3:]]
    
    else:
        return extract_value(value_part)

def decode_bpe_merges(parts):
    """解析BPE合并规则为可读字符串"""
    merges = []
    try:
        # 合并规则的结构是交替出现的长度和字节数组
        for i in range(0, len(parts), 2):
            length = parts[i]
            byte_array = parts[i+1]
            if isinstance(byte_array, (np.ndarray, np.memmap)):
                decoded = bytes(byte_array.tolist()).decode('utf-8', errors='replace')
                merges.append(f"[规则 {i//2+1}] 长度={length} → {repr(decoded)}")
    except Exception as e:
        return f"无法解析BPE合并规则: {str(e)}"
    return merges

def format_jinja_template(template):
    """格式化Jinja模板为可读结构"""
    return "\n".join([
        line.rstrip() for line in template
        .replace('%}', '%}\n').replace('{{', '\n{{')
        .replace('}}', '}}\n').split('\n')
        if line.strip()
    ])

filename = "D:/my_deepseek_project/models/deepseek/DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf"

with open(filename, "rb") as f:
    reader = GGUFReader(f)
    
    print(f"✨ GGUF文件元数据总字段数: {len(reader.fields)}")
    print("="*60)
    
    for key in reader.fields.keys():
        field = reader.fields[key]
        parsed_value = parse_gguf_field(field, key)
        type_name = field.types[0].name if field.types else "UNKNOWN"
        
        # 特殊字段的格式化输出
        print(f"🔑 字段名称: {key}")
        print(f"📝 数据类型: {type_name}")
        
        if key == "tokenizer.ggml.merges":
            print("📊 BPE合并规则:")
            for rule in parsed_value[:3]:  # 显示前3条规则示例
                print(f"  - {rule}")
            print(f"  (...共{len(parsed_value)}条规则)")
        elif key == "tokenizer.chat_template":
            print("📊 对话模板结构:")
            print("\n".join([f"  | {line}" for line in parsed_value.split('\n')]))
        else:
            print(f"📊 字段值: {parsed_value}")
        
        print("-"*60)

对以上的python代码放到一个文件中,在CMD中执行。输出示例:

✨ GGUF文件元数据总字段数: 38

============================================================

�� 字段名称: general.architecture

�� 数据类型: STRING

�� 字段值: qwen2

------------------------------------------------------------

�� 字段名称: general.name

�� 数据类型: STRING

�� 字段值: DeepSeek R1 Distill Qwen 1.5B

------------------------------------------------------------

�� 字段名称: llama.context_length

�� 数据类型: UINT32

�� 字段值: 32768

------------------------------------------------------------

�� 字段名称: tokenizer.ggml.tokens

�� 数据类型: ARRAY

�� 字段值: ['<|endoftext|>', '<|im_start|>', '<|im_end|>', ...]

------------------------------------------------------------

...

代码功能说明:

        数据类型识别:根据GGUFValueType自动识别字段类型

        字符串处理:自动解码字节数据并去除\x00填充字符

        数组处理:将numpy数组转换为Python列表

        标量处理:单个元素的数组自动转换为标量值

        特殊类型处理:支持BOOL/UINT32/FLOAT32等类型的转换

常见字段解析说明:

字段名称

类型

说明

general.architecture

STRING

模型架构名称 (如qwen2)

general.name

STRING

模型显示名称

llama.context_length

UINT32

上下文最大长度

tokenizer.ggml.tokens

ARRAY

词表token列表

tokenizer.ggml.scores

ARRAY

token的logit偏移值

tokenizer.ggml.token_type

ARRAY

token类型标记

接下来对读取的字段进行解析,分成多个部分:

1. 文件元信息

字段名称

原始值

分解说明

GGUF.version

[3]

GGUF格式版本号:3

GGUF.tensor_count

[339]

模型张量总数:339个

GGUF.kv_count

[27]

元数据键值对数量:27对

2. 通用模型信息

字段名称

原始值

分解说明

general.architecture

[20]

模型架构类型:qwen2

general.name

[12]

模型名称长度:12字节

general.file_type

[17]

文件类型:Q8_0量化模型

general.quantization_version

[28]

量化版本号:28

3. 模型结构参数

字段名称

原始值

分解说明

qwen2.block_count

[17]

Transformer层数:28层(需验证,可能为索引值+1)

qwen2.context_length

[20]

最大上下文长度:32768 tokens(需二次转换)

qwen2.embedding_length

[22]

嵌入维度:1632

qwen2.feed_forward_length

[25]

FFN中间层维度:9520

qwen2.attention.head_count

[26]

注意力头数:16头

qwen2.attention.head_count_kv

[29]

键值注意力头数:12头

qwen2.rope.freq_base

[20]

RoPE基频:10000.0

qwen2.attention.layer_norm_rms_epsilon

[38]

LayerNorm epsilon值:1e-6

4. 分词器配置

字段名称

原始值

分解说明

tokenizer.ggml.bos_token_id

[27]

起始符ID:151646

tokenizer.ggml.eos_token_id

[27]

结束符ID:151643

tokenizer.ggml.padding_token_id

[31]

填充符ID:151643(同EOS)

tokenizer.ggml.add_bos_token

[28]

自动添加BOS:True

tokenizer.ggml.add_eos_token

[28]

自动添加EOS:False

5. 其他字段说明

字段名称

原始值

处理方式

general.type

[12]

类型标记(无法分解)

general.organization

[20]

组织名称长度(需二进制解析)

general.basename

[16]

基础名称长度(需二进制解析)

general.size_label

[18]

模型大小标签(如"1.5B")

tokenizer.ggml.model

[20]

分词器模型类型(如"deepseek-ai/deepseek-llm-1.3b")

tokenizer.ggml.pre

[18]

预处理配置(无法分解)

tokenizer.ggml.tokens

[21]

Token列表(需解析字节数组)

tokenizer.ggml.token_type

[25]

Token类型(需解析字节数组)

其中更多的二层结构中的字段会在后续解读。

相关文章:

  • 机器学习·最近邻方法(k-NN)
  • 第七天:数据提取-正则表达式
  • 已知自动驾驶的一个场景,如变道,如何做好预期功能安全
  • 空天技术赋能:毫米波基站+高速数字微波构筑应急通信新范式
  • 函数调用过程的详细解析
  • halcon激光三角测量(十七)calibrate_sheet_of_light_3d_calib_object
  • 容器、pod和缓存
  • 快速入门 Tailwind CSS:现代前端开发的利器
  • 【deepseek api 第三方平台使用参考】
  • 日常故障排查 - Java程序故障排查
  • Day19 第六章 二叉树part07
  • ASP.NET Core Web应用(.NET9.0)读取数据库表记录并显示到页面
  • 如何提升爬虫获取数据的准确性?
  • 记PasteSpider部署工具的Windows.IIS版本开发过程之草稿-Web.IIS.Administration解读(5)
  • 【MySQL】使用 JDBC 连接数据库
  • GitHub 热点项目介绍
  • 闵氏几何详解
  • 用于仿真得到超材料的S参数后,利用S参数矩阵提取等效介电常数和磁导率
  • 【Golang学习之旅】Go 语言微服务架构实践(gRPC、Kafka、Docker、K8s)
  • 【java】方法的基本内存原理(栈和堆)
  • 2024年全国博物馆接待观众14.9亿人次
  • 人民网:激发博物馆创新活力,让“过去”拥有“未来”
  • 【社论】城市更新,始终以人为核心
  • 曾犯强奸罪教师出狱后办教培机构?柳州鱼峰区教育局:正核实
  • 为什么越来越多景区,把C位留给了书店?
  • 前四个月人民币贷款增加10.06万亿元,4月末M2余额同比增长8%