The cat is sleeping in the kitchen <eos>
在训练过程中,将依次应用以下注意力掩码(在掩码下,我只显示未掩码的令牌):1 0 0 0 0 0 0 0 0The 1 1 0 0 0 0 0 0The cat1 1 1 0 0 0 0 0The cat is 1 1 1 1 0 0 0 0The cat is sleeping1 1 1 1 1 0 0 0The cat is sleeping in 1 1 1 1 1 1 0 0The cat is sleeping in the 1 1 1 1 1 1 1 0The cat is sleeping in the kitchen1 1 1 1 1 1 1 1The cat is sleeping in the kitchen <eos>
零表示标记不会被考虑用于注意力计算另一方面,文本嵌入模型被训练为对整个序列标记进行编码,并且是双向的,即它们从左到右和从右到左对序列进行编码在 LLM2Vec 方法中,将 LLM 转换为嵌入模型的第一步涉及将仅解码器 LLM 中使用的因果注意力掩码替换为全一矩阵这种更改允许序列中的每个标记与所有其他标记进行交互,从而有效地将其转换为双向 LLM但是,仅解码器的 LLM 没有经过训练来编码未来的令牌,因此,这种简单的修改会降低表示的质量尽管如此,还是可以训练模型以有效地使用其新的双向注意力功能他们建议采用新的掩蔽下一个代币预测 (MNTP) 目标MNTP 将下一个令牌预测目标与 BERT 使用的掩码语言建模合并为了实现这一点,我们采用任意序列 x = (x1, x2, . . . , xN),屏蔽输入标记的子集,然后训练模型使用过去和未来的上下文来预测这些屏蔽标记重要的是,当预测在位置 i 被屏蔽的令牌时,损失是使用前一个位置 i − 1 处的令牌表示的对数来计算的,而不是像 BERT 模型那样从屏蔽位置本身计算的将 LLM 转换为双向模型,然后进行 MNTP 训练,可以使任何仅解码器的 LLM 适应编码器但是,这些步骤可能无法充分满足序列表示的需求事实上,像 BERT 这样的双向编码器通常也会在预训练中加入下一句话的预测任务,而 LLM 没有接受过这项任务的训练LLM2Vec 通过对每个输入序列进行两次处理来解决这种缺失的功能,每次使用不同的随机选择的辍学掩码,从而产生同一序列的两个不同表示训练目标是提高这两个表示之间的相似性,同时减少它们与同一训练批次中不同序列的表示的相似性他们称这最后一步为“无监督对比学习”(SimCSE)LLM2Vec 背后的完整过程由本文中的下图说明:source (CC-BY)他们评估了 LLM2Vec 在各种任务中生成的模型,并表明它们可以胜过标准文本嵌入模型您将在论文的第 3 节和第 4 节中找到结果使用 LLM2Vec 将 Llama 3 转换为文本嵌入模型使用 LLM2Vec 将 LLM 转换为文本嵌入模型相当简单首先,安装以下软件包:pip install llm2vecpip install flash-attn --no-build-isolation
llm2vec 包会将 LLM 转换为嵌入模型flash-attn 是 FlashAttention 的软件包这不是必需的,但可以加快MNTP的训练速度它仅适用于 Ampere 一代的最新 GPU(RTX 3xxx/4xxx、A100、H100 等)然后,转换本身由以下代码执行:import torchfrom llm2vec import LLM2Vecl2v = LLM2Vec.from_pretrained( "meta-llama/Meta-Llama-3-8B", device_map="cuda" if torch.cuda.is_available() else "cpu", torch_dtype=torch.bfloat16,)l2v.save("Llama-3-8B-Emb")
“torch_dtype=torch.bfloat16”是能够在 24 GB GPU 上运行转换所必需的如果不设置它,则模型将大于具有 float32 参数的原始模型我把这个模型推到了集线器,以防你不想自己做这个转换:kaitchup/Llama-3-8B-llm2vec-Emb此模型已准备好使用您可以将其插入 RAG 管道但是,它的性能不如标准嵌入模型(在大多数情况下)我们需要用MNTP目标训练Llama 3 8B作者还提供了一个脚本来做到这一点:实验/run_mntp.py它目前支持具有 Llama 和 Mistral 架构的模型要使用它,请在本地克隆存储库:git clone https://github.com/McGill-NLP/llm2vec.git
该脚本需要一个参数,该参数是 JSON 格式的配置文件他们在这里举了几个例子:train_configs/月/月对于 Llama 3 8B,我进行了以下配置:JSON_CONFIG='''{ "model_name_or_path": "meta-llama/Meta-Llama-3-8B", "dataset_name": "wikitext", "dataset_config_name": "wikitext-103-raw-v1", "per_device_train_batch_size": 1, "per_device_eval_batch_size": 1, "gradient_accumulation_steps": 16, "do_train": true, "do_eval": true, "max_seq_length": 512, "mask_token_type": "blank", "data_collator_type": "all_mask", "mlm_probability": 0.8, "overwrite_output_dir": true, "output_dir": "Llama-3-8B-llm2vec-MNTP-Emb", "evaluation_strategy": "steps", "eval_steps": 100, "save_steps": 200, "stop_after_n_steps": 1000, "lora_r": 16, "gradient_checkpointing": true, "torch_dtype": "bfloat16", "attn_implementation": "flash_attention_2"}'''with open("mntp_config.json", 'w') as f: f.write(JSON_CONFIG)
该脚本使用 bfloat16 参数加载模型我将每个设备的批处理大小设置为 1,以便训练可以适应 24 GB GPU然后,我们可以开始 MNTP 训练:python llm2vec/experiments/run_mntp.py mntp_config.json
使用 24 GB GPU(例如 Google Colab 的 L4)需要 4 天时间,持续三个时期如果你等不了那么久,一个时代就足够了经过MNTP训练后,该模型应该会产生更好的结果,特别是对于检索任务对于最后一步,即SimCSE训练,作者尚未发布他们的代码,但提到他们会发布为 RAG 设置 Llama 3 文本嵌入模型在上一节中创建的嵌入模型已准备好在 RAG 管道中使用例如,您可以使用句子转换器(Apache 2.0 许可证)加载它from sentence_transformers import SentenceTransformermodel = SentenceTransformer("kaitchup/Llama-3-8B-llm2vec-Emb")
如果使用 LlamaIndex(MIT 许可证),则可以设置 HuggingFaceEmbedding 模型:Settings.embed_model = HuggingFaceEmbedding(model_name="kaitchup/Llama-3-8B-llm2vec-Emb", device='cpu')
我设置了 device='cpu',但使用 CPU 会使 RAG 系统变慢您可以删除此参数以在 GPU 上运行它但是,请注意,模型是以完全精度加载的它不适合消费类 GPU结论使用 LLM2Vec,我们现在可以使用 LLM 作为文本嵌入模型转换简单快捷在 RAG 系统中使用一个单一的模型进行生成和检索很有吸引力,因为我们不需要搜索额外的嵌入模型但是,简单地从 LLM 中提取的嵌入模型往往性能低于常规嵌入模型LLM2Vec 的作者提出了新的训练目标 MNTP 和 SimCSE,以训练从 LLM 中提取的嵌入模型我发现这种训练成本很高,但根据作者的说法,可以产生更好的嵌入模型(图片来源网络,侵删)
0 评论