8.4 语言模型的集成与优化
语言模型的集成与优化是自然语言处理领域的一个关键环节,它涉及选择合适的预训练语言模型(LLM),并通过调整其参数来适应特定的应用场景。这个过程包括定义模型的配置参数,如温度(temperature)、top_p等,以控制生成文本的随机性和多样性。接着,通过加载模型并使用特定的数据集对其进行评估,以确保其生成的回答质量和准确性。此外,优化还可能包括使用pipeline来处理文本,这涉及到文本的预处理、生成回答以及后处理,确保输出既符合预期格式又具有信息价值。通过这些步骤,语言模型能够更有效地集成到问答系统、文本生成或其他NLP任务中,从而提供更准确和个性化的结果。
8.4.1 参数配置
定义配置类CFG ,用于存储和管理系统中用于自然语言处理任务的各种参数和设置。类CFG包含了用于控制大型语言模型(LLMs)生成文本的行为的参数,如模型选择、温度、概率分布控制和重复惩罚。此外,它还定义了文本分割的参数、文本嵌入模型的存储库路径、相似段落检索的数量,以及用于存放PDF文件、嵌入结果和输出数据的文件路径。这个类为项目提供了一个集中化管理配置的方式,使得调整实验设置和参数变得更加方便。
class CFG:
model_name = 'llama2-13b-chat' # wizardlm, llama2-7b-chat, llama2-13b-chat, mistral-7B
temperature = 0
top_p = 0.95
repetition_penalty = 1.15
split_chunk_size = 800
split_overlap = 0
embeddings_model_repo = 'sentence-transformers/all-MiniLM-L6-v2'
k = 6
PDFs_path = '/input/harry-potter-books-in-pdf-1-7/HP books/'
Embeddings_path = '/input/faiss-hp-sentence-transformers'
Output_folder = './harry-potter-vectordb'
对上述代码的具体说明如下所示:
(1)LLMs (Large Language Models 大型语言模型)配置参数
- model_name:指定要使用的预训练语言模型的名称。这里列出的几个选项可能是Hugging Face模型库中的不同模型,例如wizardlm、llama2-7b-chat、llama2-13b-chat、mistral-7B等。
- temperature:控制语言模型生成文本时的随机性。温度越高,生成的文本越随机;温度为0通常意味着使用确定性输出。
- top_p:用于控制生成文本时的词汇概率分布,是截断概率累积(Cumulative Probability Distribution)的一个参数。
- repetition_penalty:用于避免模型在生成文本时重复相同的内容。
(2)Splitting (分割)参数
- split_chunk_size:文本分割时每个块的大小,单位可能是字符数。
- split_overlap:分割文本时,每个块之间的重叠字符数。
(3)Embeddings (嵌入)参数
- embeddings_model_repo:指定用于文本嵌入的模型存储库路径。这里使用的是sentence-transformers库中的一个预训练模型。
(4)Similar Passages (相似段落)参数
- k:在检索最相似的段落时,返回的相似段落的数量。
(5)Paths (路径)参数
- PDFs_path:存储PDF文件的路径。
- Embeddings_path:存储文本嵌入的路径,可能用于FAISS向量数据库。
- Output_folder:保存输出结果的路径。
8.4.2 加载、配置和评估LLM
(1)定义方法get_model,用于自动下载、加载和配置特定大型语言模型(LLM)。该方法接收一个模型名称作为参数,根据提供的模型名称从Hugging Face模型库中检索相应的预训练模型和分词器。
def get_model(model = CFG.model_name):
print('\nDownloading model: ', model, '\n\n')
if model == 'wizardlm':
model_repo = 'TheBloke/wizardLM-7B-HF'
tokenizer = AutoTokenizer.from_pretrained(model_repo)
bnb_config = BitsAndBytesConfig(
load_in_4bit = True,
bnb_4bit_quant_type = "nf4",
bnb_4bit_compute_dtype = torch.float16,
bnb_4bit_use_double_quant = True,
)
model = AutoModelForCausalLM.from_pretrained(
model_repo,
quantization_config = bnb_config,
device_map = 'auto',
low_cpu_mem_usage = True
)
max_len = 1024
elif model == 'llama2-7b-chat':
model_repo = 'daryl149/llama-2-7b-chat-hf'
tokenizer = AutoTokenizer.from_pretrained(model_repo, use_fast=True)
bnb_config = BitsAndBytesConfig(
load_in_4bit = True,
bnb_4bit_quant_type = "nf4",
bnb_4bit_compute_dtype = torch.float16,
bnb_4bit_use_double_quant = True,
)
model = AutoModelForCausalLM.from_pretrained(
model_repo,
quantization_config = bnb_config,
device_map = 'auto',
low_cpu_mem_usage = True,
trust_remote_code = True
)
max_len = 2048
elif model == 'llama2-13b-chat':
model_repo = 'daryl149/llama-2-13b-chat-hf'
tokenizer = AutoTokenizer.from_pretrained(model_repo, use_fast=True)
bnb_config = BitsAndBytesConfig(
load_in_4bit = True,
bnb_4bit_quant_type = "nf4",
bnb_4bit_compute_dtype = torch.float16,
bnb_4bit_use_double_quant = True,
)
model = AutoModelForCausalLM.from_pretrained(
model_repo,
quantization_config = bnb_config,
device_map = 'auto',
low_cpu_mem_usage = True,
trust_remote_code = True
)
max_len = 2048 # 8192
elif model == 'mistral-7B':
model_repo = 'mistralai/Mistral-7B-v0.1'
tokenizer = AutoTokenizer.from_pretrained(model_repo)
bnb_config = BitsAndBytesConfig(
load_in_4bit = True,
bnb_4bit_quant_type = "nf4",
bnb_4bit_compute_dtype = torch.float16,
bnb_4bit_use_double_quant = True,
)
model = AutoModelForCausalLM.from_pretrained(
model_repo,
quantization_config = bnb_config,
device_map = 'auto',
low_cpu_mem_usage = True,
)
max_len = 1024
else:
print("Not implemented model (tokenizer and backbone)")
return tokenizer, model, max_len
对上述代码的具体说明如下所示:
- 方法参数:model,默认值为 CFG.model_name,这意味着方法get_model将使用在 CFG 类中定义的属性model_name作为其参数的初始值。
- 模型下载消息:在开始下载模型之前,方法get_model会打印一条消息,告知用户正在下载哪个模型。
- 模型选择与加载:通过 if-elif 语句块来确定要加载哪个模型,每个模型都有其对应的Hugging Face模型库的存储库名称(model_repo)。
- 分词器加载:对于每个模型,使用 AutoTokenizer.from_pretrained(model_repo) 加载相应的分词器。分词器用于将文本转换为模型可以理解的格式。
- 模型量化配置:对于每个模型,定义了一个 BitsAndBytesConfig 对象,它包含了模型量化的配置。量化是一种优化技术,可以在保持模型性能的同时减少模型的大小和计算需求。
- 模型加载:使用 AutoModelForCausalLM.from_pretrained 方法加载预训练的因果语言模型(Causal Language Model),并传入量化配置、设备映射(device_map)、CPU内存使用优化(low_cpu_mem_usage),以及是否信任远程代码(trust_remote_code)。
- 最大长度设置:为每个模型设置了最大长度参数 max_len,这是模型能够处理的文本的最大令牌数。
- 处理模型未实现的情况:如果提供的模型名称不是预定义的选项之一,会打印输出提示消息说明该模型未实现。
- 返回值:返回一个包含分词器、模型和最大长度的元组。
上述方法get_model为NLP应用程序提供了一个方便的方式来获取预处理和推理所需的模型和分词器,同时应用了一些优化以适应不同的计算环境。通过使用此方法,可以根据自己的需求轻松切换使用不同的大型语言模型。
(2)执行方法get_model的IPython,获取与CFG类中定义的model_name属性相对应的模型信息。
%%time
tokenizer, model, max_len = get_model(model = CFG.model_name)
clear_output()
执行上述代码后将获得所需的模型和分词器,同时清除了之前的输出信息,并且可以在Notebook中看到执行get_model函数所需的时间。
CPU times: user 34.2 s, sys: 1min, total: 1min 34s
Wall time: 3min 55s
(3)在下面的代码中,使用model.eval()方法评估加载的模型。model.eval()是 PyTorch 中的一个方法,用于将神经网络模型设置为评估模式。在这种模式下,模型不会应用诸如 dropout 和 batch normalization 等在训练阶段使用的技术,确保在推理或评估时获得确定性的输出。简而言之,model.eval()只是让模型准备好进行测试或实际应用,而不会进行任何训练阶段特有的操作。
model.eval()
执行后会打印输出模型的内部结构,详细描述了所使用的 LlamaForCausalLM 模型的架构。这个模型是基于 Llama 架构的语言模型,专门为因果语言建模(Causal Language Modeling,CLM)任务设计,比如文本生成或问答系统。
LlamaForCausalLM(
(model): LlamaModel(
(embed_tokens): Embedding(32000, 5120, padding_idx=0)
(layers): ModuleList(
(0-39): 40 x LlamaDecoderLayer(
(self_attn): LlamaAttention(
(q_proj): Linear4bit(in_features=5120, out_features=5120, bias=False)
(k_proj): Linear4bit(in_features=5120, out_features=5120, bias=False)
(v_proj): Linear4bit(in_features=5120, out_features=5120, bias=False)
(o_proj): Linear4bit(in_features=5120, out_features=5120, bias=False)
(rotary_emb): LlamaRotaryEmbedding()
)
(mlp): LlamaMLP(
(gate_proj): Linear4bit(in_features=5120, out_features=13824, bias=False)
(up_proj): Linear4bit(in_features=5120, out_features=13824, bias=False)
(down_proj): Linear4bit(in_features=13824, out_features=5120, bias=False)
(act_fn): SiLU()
)
(input_layernorm): LlamaRMSNorm()
(post_attention_layernorm): LlamaRMSNorm()
)
)
(norm): LlamaRMSNorm()
)
(lm_head): Linear(in_features=5120, out_features=32000, bias=False)
)
上面输出的模型结构显示了模型的复杂性和设计细节,这对于理解模型的工作原理和它如何被训练来执行特定任务是非常有用的。在实际应用中,了解模型结构有助于更好地利用模型的能力,同时也可以为模型的进一步优化和调试提供参考。
(4)使用model.to(device)方法将模型加载到特定的硬件设备上,比如 CPU 或 GPU。
model.hf_device_map
执行 model.hf_device_map 后将得到一个字典,展示了模型的不同部分被分配到了哪些GPU设备上。在下面的输出结果中,每个键代表模型中的一个组件,而每个值代表该组件被分配到的设备编号。
{'model.embed_tokens': 0,
'model.layers.0': 0,
'model.layers.1': 0,
'model.layers.2': 0,
'model.layers.3': 0,
'model.layers.4': 0,
'model.layers.5': 0,
'model.layers.6': 0,
'model.layers.7': 0,
'model.layers.8': 0,
'model.layers.9': 0,
'model.layers.10': 0,
'model.layers.11': 0,
'model.layers.12': 0,
'model.layers.13': 0,
'model.layers.14': 0,
'model.layers.15': 0,
'model.layers.16': 0,
'model.layers.17': 1,
'model.layers.18': 1,
'model.layers.19': 1,
'model.layers.20': 1,
'model.layers.21': 1,
'model.layers.22': 1,
'model.layers.23': 1,
'model.layers.24': 1,
'model.layers.25': 1,
'model.layers.26': 1,
'model.layers.27': 1,
'model.layers.28': 1,
'model.layers.29': 1,
'model.layers.30': 1,
'model.layers.31': 1,
'model.layers.32': 1,
'model.layers.33': 1,
'model.layers.34': 1,
'model.layers.35': 1,
'model.layers.36': 1,
'model.layers.37': 1,
'model.layers.38': 1,
'model.layers.39': 1,
'model.norm': 1,
'lm_head': 1}