3. 模型加载与初始化:法律模型的启动艺术

说实话,模型加载这个环节,看着简单,坑却不少。我见过太多团队在模型加载这一步卡住,然后怀疑人生。今天咱们就聊聊法律领域三个主流模型——LawGPT、ChatLaw、LexiLaw——到底该怎么加载,以及背后的配置逻辑。

3.1 法律模型的加载方式

先说说我的习惯。我一般会把模型加载封装成一个函数,而不是散落在代码各处。这样出了问题,排查起来也方便。

3.1.1 LawGPT 加载

LawGPT 是基于 LLaMA 架构的,所以加载方式和 LLaMA 类似。但有个细节——它的 tokenizer 需要特殊处理。

from transformers import AutoModelForCausalLM, AutoTokenizer

def load_lawgpt(model_path: str, device: str = "cuda"):
    """
    加载 LawGPT 模型
    """
    print(f"[INFO] 正在加载 LawGPT 模型: {model_path}")
    
    # 加载 tokenizer
    tokenizer = AutoTokenizer.from_pretrained(
        model_path,
        trust_remote_code=True,
        padding_side="left"  # 我习惯用 left padding
    )
    
    # 加载模型
    model = AutoModelForCausalLM.from_pretrained(
        model_path,
        torch_dtype=torch.float16,
        device_map="auto",
        trust_remote_code=True
    )
    
    return model, tokenizer
⚠️ 注意: LawGPT 的 tokenizer 有个坑——它默认的 pad_token 是 None。我在项目中遇到过推理时报错,后来发现是 pad_token 没设置。建议手动设置一下: tokenizer.pad_token = tokenizer.eos_token

3.1.2 ChatLaw 加载

ChatLaw 的加载方式略有不同。它用了自定义的模型结构,所以需要指定 trust_remote_code=True。嗯,这里要特别留意。

def load_chatlaw(model_path: str):
    """
    加载 ChatLaw 模型
    """
    from transformers import AutoConfig
    
    # 先加载配置
    config = AutoConfig.from_pretrained(
        model_path,
        trust_remote_code=True
    )
    
    # 加载 tokenizer
    tokenizer = AutoTokenizer.from_pretrained(
        model_path,
        trust_remote_code=True,
        use_fast=True  # 建议用 fast tokenizer
    )
    
    # 加载模型
    model = AutoModelForCausalLM.from_pretrained(
        model_path,
        config=config,
        torch_dtype=torch.bfloat16,  # ChatLaw 推荐用 bfloat16
        device_map="auto",
        trust_remote_code=True
    )
    
    return model, tokenizer
💡 小技巧: 我曾经在加载 ChatLaw 时遇到显存不足的问题。后来发现是 device_map="auto" 在单卡环境下反而会多占显存。如果你只有一张卡,建议直接指定 device="cuda:0"

3.1.3 LexiLaw 加载

LexiLaw 是基于 ChatGLM 架构的,所以加载方式更接近 GLM 系列。我个人觉得 LexiLaw 的加载是最顺畅的,但也不是完全没有坑。

def load_lexilaw(model_path: str):
    """
    加载 LexiLaw 模型
    """
    from transformers import AutoModel
    
    # 加载 tokenizer
    tokenizer = AutoTokenizer.from_pretrained(
        model_path,
        trust_remote_code=True
    )
    
    # 加载模型
    model = AutoModel.from_pretrained(
        model_path,
        trust_remote_code=True,
        device_map="auto"
    ).half()  # 转为半精度
    
    return model, tokenizer

3.2 模型配置文件解析

配置文件这东西,说白了就是模型的「身份证」。你想想看,模型能不能跑、跑多快、占多少显存,全看它。我建议每个配置文件都仔细过一遍,别偷懒。

配置项 说明 常见值 我的建议
hidden_size 隐藏层维度 4096, 5120 越大越吃显存,但效果不一定线性提升
num_attention_heads 注意力头数 32, 40 一般和 hidden_size 成比例
num_hidden_layers Transformer 层数 32, 40, 48 层数越多,推理越慢
vocab_size 词表大小 32000, 50000 法律模型一般会扩充词表
max_position_embeddings 最大序列长度 2048, 4096 法律文档长,建议至少 4096

🔑 关键点: 法律模型和通用模型最大的区别在于词表。法律术语多,所以很多法律模型会扩充词表。比如 LawGPT 的词表大小是 50000,比 LLaMA 的 32000 大了不少。这意味着 tokenizer 的初始化要特别注意。

3.3 Tokenizer 初始化

Tokenizer 初始化,说白了就是让模型认识字。但法律文本里有很多特殊字符和术语,比如「《民法典》」、「第X条」、「(2023)京01民初123号」等等。如果 tokenizer 不认识这些,那推理效果肯定打折扣。

3.3.1 标准初始化流程

from transformers import AutoTokenizer

def init_tokenizer(model_path: str, add_special_tokens: bool = True):
    """
    初始化 tokenizer
    """
    tokenizer = AutoTokenizer.from_pretrained(
        model_path,
        trust_remote_code=True,
        use_fast=True,
        add_prefix_space=False  # 法律文本不需要前缀空格
    )
    
    # 设置特殊 token
    if add_special_tokens:
        special_tokens = {
            "pad_token": "[PAD]",
            "sep_token": "[SEP]",
            "cls_token": "[CLS]"
        }
        tokenizer.add_special_tokens(special_tokens)
    
    return tokenizer

3.3.2 法律文本的特殊处理

我曾经在处理法律文书时发现,tokenizer 把「第X条」拆成了好几个 token,导致模型理解不准确。后来我加了一些自定义 token。

def add_legal_tokens(tokenizer):
    """
    添加法律领域特殊 token
    """
    legal_tokens = [
        "[ARTICLE]",      # 条款标记
        "[PARAGRAPH]",    # 段落标记
        "[SECTION]",      # 章节标记
        "[CASE_NUM]",     # 案号标记
        "[COURT_NAME]",   # 法院名称标记
        "[LEGAL_TERM]"    # 法律术语标记
    ]
    
    tokenizer.add_tokens(legal_tokens)
    print(f"[INFO] 已添加 {len(legal_tokens)} 个法律特殊 token")
    
    return tokenizer
💡 经验之谈: 添加自定义 token 后,别忘了调整模型的 embedding 层大小。我见过有人加了 token 但没调整 embedding,结果模型直接报错。代码很简单: model.resize_token_embeddings(len(tokenizer))

3.4 核心知识体系

说了这么多,我画了一张图帮你理清思路。这张图展示了模型加载与初始化的核心流程,以及各个组件之间的关系。

法律模型加载与初始化核心流程 模型选择 LawGPT ChatLaw LexiLaw 配置文件解析 (config.json) hidden_size num_layers vocab_size max_length Tokenizer 初始化 法律特殊 token 添加 embedding 层调整 特殊字符处理

3.5 避坑指南

最后,分享几个我踩过的坑,希望能帮你省点时间。

  • 显存不足:我曾经在加载 ChatLaw 时,因为没指定 torch_dtype,默认用了 float32,结果 24G 显存直接爆了。后来改成 float16 才搞定。
  • Tokenizer 不匹配:有次我加载 LawGPT 时用了 LLaMA 的 tokenizer,结果推理出来的内容全是乱码。后来才发现,LawGPT 的 tokenizer 是经过法律语料微调的,不能混用。
  • 配置文件版本问题:LexiLaw 更新后,配置文件里多了个 rope_scaling 字段。我一开始没注意,结果模型加载后推理速度慢得离谱。后来查文档才知道,这个字段控制的是位置编码的缩放,需要根据你的硬件调整。

📌 总结一下: 模型加载与初始化,说白了就是三件事——选对模型、配好参数、调好 tokenizer。每一步都有坑,但只要你按照上面的流程来,基本不会出大问题。如果真出了问题,也别慌,先看日志,再看配置,最后检查 tokenizer。90% 的问题都能在这三步里找到答案。

专注资料整理