2026/4/17 2:08:33
网站建设
项目流程
用新华做网站名是否侵权,不懂代码如何做网站,深圳医疗网站建设报价,wordpress用户自定义头像原文#xff1a;towardsdatascience.com/lora-fine-tuning-on-your-apple-silicon-macbook-432c7dab614a 随着模型变得越来越小#xff0c;我们看到越来越多的消费级电脑能够本地运行 LLM#xff08;大型语言模型#xff09;。这不仅大大降低了人们训练自己模型的技术门槛towardsdatascience.com/lora-fine-tuning-on-your-apple-silicon-macbook-432c7dab614a随着模型变得越来越小我们看到越来越多的消费级电脑能够本地运行 LLM大型语言模型。这不仅大大降低了人们训练自己模型的技术门槛还允许尝试更多的训练技术。一款能够很好地本地运行 LLM 的消费级电脑是苹果 Mac。苹果利用其定制的硅芯片创建了一个数组处理库称为 MLX。通过使用 MLX苹果可以比许多其他消费级电脑更好地运行 LLM。在这篇博客文章中我将从高层次上解释 MLX 是如何工作的然后向你展示如何使用 MLX 在本地微调你自己的 LLM。最后我们将通过量化来加速我们的微调模型。让我们开始吧MLX 背景MLX 是什么以及谁可以使用它MLX 是苹果公司的一个开源库它让 Mac 用户能够更高效地运行包含大量张量tensors的程序。自然地当我们想要训练或微调一个模型时这个库就派上用场了。MLX 的工作方式是通过在中央处理单元CPU、图形处理单元GPU和内存管理单元MMU之间进行高效的内存传输。对于每一种系统架构最耗时的工作是在你将内存移动到寄存器之间时。在 Nvidia GPU 上它们通过在设备上创建大量的 SRAM 来最小化内存传输。对于苹果来说他们设计了他们的硅芯片使得 GPU 和 CPU 可以通过 MMU 访问相同的内存。因此GPU 在对其数据进行操作之前不需要将其数据加载到其内存中。这种架构被称为系统级芯片SOC通常需要你内部构建芯片而不是组合其他制造商预制的部件。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0b5aa7801a06e97b4f7aa8f97f99c389.pngImage by Author – SOC 与标准内存访问模式由于苹果现在设计了自己的硅芯片它可以编写低级软件从而高效地利用它。然而这也意味着使用英特尔处理器的 Mac 用户将无法使用这个库。安装 MLX一旦你拥有了一台苹果硅电脑我们就有几种方法可以安装 MLX。我将向你展示如何使用 python 虚拟环境但请注意你也可以通过单独的环境管理器如 conda 来安装它。在我们的终端中我们首先创建一个名为venv的虚拟环境然后进入它。python-m venv venv;source./venv/bin/activate现在我们已经设置了环境我们将使用 pip 来安装pip install mlx运行简单的推理在本地设置好我们的库后让我们选择一个将要运行的模型。我喜欢使用 Phi 系列模型因为与其他模型相比它们相当小3B 参数比 7B但性能仍然相当不错。我们可以使用相同的终端命令下载模型并进行推理python-m mlx_lm.generate--model microsoft/Phi-3.5-mini-instruct--promptWho was the first president?--max-tokens4096为了解释我们的命令我们使用内置的mlx_lm函数让我们的库知道我们将使用语言模型进行推理。我们传入我们使用的模型其名称在HuggingFacePhi-3 在 HuggingFace 上以这种方式出现。我们传入我们允许的最大令牌数然后最终传入提示。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/fbddb36faf64ac51243592643ea6cc73.png作者提供的图片 - 谁是第一位总统的基础模型微调生成微调数据集为了使我们的示例简单但有用我们将微调模型使其始终以以下模式以 JSON 格式响应{context:...,question:...,answer:...}要使用 MLX 进行微调我们需要我们的数据集以 MLX 理解的模式。有 4 种格式chat、tools、completions和text。我们将专注于completions这样当我们对模型进行提示时它将以 JSON 格式返回答案。Completions 要求我们的训练数据使用以下模式{prompt:...,completion:...,}现在我们已经了解了如何将数据传递给 MLX我们需要找到一个好的微调数据集。我创建以下 Python 脚本来处理squad_v2数据集使其符合 MLX 所需的模式。fromdatasetsimportload_datasetimportjsonimportrandomprint(Loading dataset and tokenizer...)qa_datasetload_dataset(squad_v2)defcreate_completion(context,question,answer):iflen(answer[text])1:answer_textI Dont Knowelse:answer_textanswer[text][0]completion_template{context:context,question:question,answer:answer_text}returnjson.dumps(completion_template)defprocess_dataset(dataset):processed_data[]forsampleindataset:completioncreate_completion(sample[context],sample[question],sample[answers])promptsample[question]processed_data.append({prompt:prompt,completion:completion})returnprocessed_dataprint(Processing training data...)train_dataprocess_dataset(qa_dataset[train])print(Processing validation data...)valid_dataprocess_dataset(qa_dataset[validation])# SQuAD v2 uses validation as test set# Combine all data for redistributionall_datatrain_datavalid_data random.shuffle(all_data)# Calculate new split sizestotal_sizelen(all_data)train_sizeint(0.8*total_size)test_sizeint(0.1*total_size)valid_sizetotal_size-train_size-test_size# Split the datanew_train_dataall_data[:train_size]new_test_dataall_data[train_size:train_sizetest_size]new_valid_dataall_data[train_sizetest_size:]# Write to JSONL filesdefwrite_jsonl(data,filename):withopen(filename,w)asf:foritemindata:f.write(json.dumps(item)n)print(Writing train.jsonl...)folder_prefix./data/write_jsonl(new_train_data,folder_prefixtrain.jsonl)print(Writing test.jsonl...)write_jsonl(new_test_data,folder_prefixtest.jsonl)print(Writing valid.jsonl...)write_jsonl(new_valid_data,folder_prefixvalid.jsonl)print(fDataset split and saved: train ({len(new_train_data)}), test ({len(new_test_data)}), valid ({len(new_valid_data)}))# Verify file contentsdefcount_lines(filename):withopen(folder_prefixfilename,r)asf:returnsum(1for_inf)print(nVerifying file contents:)print(ftrain.jsonl:{count_lines(train.jsonl)}lines)print(ftest.jsonl:{count_lines(test.jsonl)}lines)print(fvalid.jsonl:{count_lines(valid.jsonl)}lines)重要的是在squad_v2数据集中我们有示例其中答案未知我们明确告诉它写“我不知道”。这有助于通过向模型展示在给定上下文不知道答案时应该做什么来减少幻觉。在这一步骤结束时我们现在有一个如下所示的数据集分为训练、测试和验证文件{prompt:...,completion:{context: ...,question:...,answer:...}}LoRA 微调为了进行微调我们将使用 MLX 内置的 LoRA 函数。要了解更多关于 LoRA 背后的数学和理论请查看我的博客文章。python-m mlx_lm.lora--model microsoft/Phi-3.5-mini-instruct--train--data./data--iters100运行此脚本我们发现可以实现最终验证损失为 1.530考虑到我们只更新了模型 0.082%的权重这并不坏。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8367a25fe9defd92f51b00029bc98546.png作者提供的图片 - 微调输出你会注意到最后我们已将新的 LoRA 权重保存为适配器。适配器保存了我们在微调期间学到的应该对权重进行的更新。我们拥有单独的适配器文件而不是立即更新模型因为我们可能有一个糟糕的训练运行或想要为不同的任务保留多个微调。为了给我们提供更多选择我们通常将基本权重与更新分开存储直到我们想要通过融合将权重永久化。微调模型推理现在我们已经生成了适配器让我们看看如何在推理期间使用它们以获得更好的输出。我们希望测试输出是否如预期那样。在我们的案例中我们期望给定一个提示模型会以我们之前做的 JSON 模式给出我们的答案。我们再次使用mlx_lm.generate命令但这次我们传递了额外的参数adapter-path。这告诉 MLX 在哪里找到额外的权重并确保在推理时使用它们。python-m mlx_lm.generate--model microsoft/Phi-3.5-mini-instruct--adapter-path./adapters--promptWho was the first president?--max-tokens4096当我们运行上述命令时我们看到我们得到了一个 JSON 格式的响应其中包含了我们微调时指定的键。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3c29204bbf8bf4dcc9a6aba84c767850.png作者提供的图片 – “谁是第一位总统”的微调模型输出向 LoRA 传递更多参数我们很幸运我们的第一次运行使模型很好地遵循了我们的格式。如果我们遇到了更多问题我们就会想要为 LoRA 指定更多的参数来考虑。为此你创建一个lora_config.yaml文件并将其传递给 LoRA 命令如下所示。在此处查看示例 yaml 配置文件。python-m mlx_lm.lora--configpath_to_file量化什么是量化从上面的运行中我们可以看到模型使用了大量的资源。生成每个标记需要大约 17 秒并在峰值时使用了大约 7GB 的内存。虽然在某些情况下推理一个大模型是有意义的但对我们来说我们希望以最少的成本运行本地 LLM。因此我们希望模型使用更少的内存并运行得更快。在不改变模型架构的情况下我们可以通过量化来优化这里。要理解量化让我先解释一下我们如何存储模型的参数。每个参数都是一个数字通常在科学计算中我们使用浮点表示来确保我们的计算尽可能准确要了解更多关于这里的确切布局请查看我的博客)。然而正如你所看到的这需要大量的位来表示每个数字。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/39df7a9cb33b2e313d64d1e9c559c435.png作者提供的图片 – 浮点 32 位FP32的 IEEE 表示由于我们倾向于使用数十亿个参数每个参数的大小对模型的总体内存占用有显著影响。此外浮点运算通常比整数运算需要更多的计算资源。正是这两方面的压力促使人们尝试使用新的数据类型来存储参数。当我们对模型进行量化时我们可以从使用浮点数转换为使用整数。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d481c40b658ab3eb7184e8e31dea38ae.png作者提供的图片 – 8 位整数表示这里的权衡是我们能够更快地进行计算并使用更少的内存但我们的性能往往会随着参数值的不精确而下降。这里的艺术在于在加快速度和使模型占用更少空间的同时尽可能保持基模型的性能。量化我们的模型要量化我们的模型我们运行以下命令python-m mlx_lm.convert--hf-path microsoft/Phi-3-mini-4k-instruct-q--q-bits4我们通过传递-q标志来告诉模型进行量化然后使用--q-bits标志指定每个权重的位数。完成后它将在本地创建一个名为mlx_model的文件夹用于存储我们新的量化模型。它将把存储在 HuggingFace 中的所有权重转换为用 4 位表示的整数这是最大的减少之一。QLoRA现在我们有了量化模型我们可以使用与运行 LoRA 相同的训练数据和命令在它上面运行 QLoRA。MLX 足够智能能够看到如果权重被量化它应该切换到使用 QLoRA。我们终端命令看起来和之前几乎一样但这次我们告诉它使用本地已有的量化模型作为源而不是 hugging face 上的模型。python-m mlx_lm.lora--model./mlx_model--train--data./data--iters100https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/887c72c5176df90c29ce3b86a0ab98fc.png作者提供的图片 – QLoRA 运行过程中的微调输出现在我们可以推理我们的 QLoRA 微调模型并进行比较python-m mlx_lm.generate--model./mlx_model--adapter-path./adapters--promptWho was the first president?--max-tokens4096https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b9c7b2dbd63012ad07745c820d91b5b0.png作者提供的图片 – “谁是第一位总统”的微调模型输出与原始微调相比我们可以看到内存使用量显著降低每秒生成的 token 数量也显著提高。当我们将其发送给用户时他们肯定会注意到速度更快。为了确定质量我们不得不比较函数之间的损失。对于 LoRA 模型我们最后的验证损失是 1.530而 QLoRA 模型的损失是 1.544。虽然预期 LoRA 模型的损失会更小但 QLoRA 模型并没有相差太远这意味着我们做得相当不错结束语最后这篇博客向您展示了如何使用 Mac 和 MLX 在本地微调您自己的 LLM。随着越来越多的计算能力被引入消费硬件我们可以期待更多的训练技术成为可能。这可以为 ML 打开更多的用例并帮助我们解决更多的问题。要查看此博客使用的完整代码请查看下面的 GitHub 仓库GitHub – matthewjgunton/mlx_json_lora现在是构建模型的好时机[1] Hannun, A.等人“mlx” (2024)Github[2] Lo, K.等人“Phi-3CookBook” (2024)Github