概述首先得有一个基础可以运行的的模型然后提供标准数据格式让模型学习学习后的知识增量数据需要再次结合原模型重新发布步骤1.下载基础模型需要自己下载Safetensors 格式的包文件可从魔搭社区下载然后新建一个 ModelFile 文件无后缀利用 Modelfile 构建一个模型from F:/AI/Ollama/Models/Qwen1.5-0.5B-Chat2 .准备训练数据集例如[ { instruction: 你是谁, output: 哈哈哈哈不告你 }, { instruction: 现在几点了, output: select now() }, { instruction: 表里有几个用户, output: select count(*) from users group by user_id }, { instruction: ### 数据库表结构:\n\n**users表**:\n- user_id: 用户ID\n- name: 姓名\n- age: 年龄\n- city: 城市\n\n**orders表**:\n- order_id: 订单ID\n- user_id: 用户ID\n- product_id: 产品ID\n- amount: 金额\n- status: 订单状态\n\n**products表**:\n- product_id: 产品ID\n- product_name: 产品名称\n- category: 类别\n\n### 问题:\n查出所有北京用户的姓名和年龄。, output: SELECT name, age FROM users WHERE city 北京; }, { instruction: ### 数据库表结构:\n\n**users表**:\n- user_id: 用户ID\n- name: 姓名\n- age: 年龄\n- city: 城市\n\n**orders表**:\n- order_id: 订单ID\n- user_id: 用户ID\n- product_id: 产品ID\n- amount: 金额\n- status: 订单状态\n\n**products表**:\n- product_id: 产品ID\n- product_name: 产品名称\n- category: 类别\n\n### 问题:\n统计每个城市的用户数量按照数量从高到低排序。, output: SELECT city, COUNT(*) as user_count FROM users GROUP BY city ORDER BY user_count DESC; }, { instruction: ### 数据库表结构:\n\n**users表**:\n- user_id: 用户ID\n- name: 姓名\n- age: 年龄\n- city: 城市\n\n**orders表**:\n- order_id: 订单ID\n- user_id: 用户ID\n- product_id: 产品ID\n- amount: 金额\n- status: 订单状态\n\n**products表**:\n- product_id: 产品ID\n- product_name: 产品名称\n- category: 类别\n\n### 问题:\n找出购买过iPhone的上海用户的名字。, output: SELECT u.name FROM users u JOIN orders o ON u.user_id o.user_id WHERE u.city 上海 AND o.product_name iPhone; } ]魔搭社区有现有的数据集可使用3.使用 python 学习数据集运行以下代码注意修改文件路径import json import os # 如果想强制用CPU取消下面这行的注释 # os.environ[CUDA_VISIBLE_DEVICES] from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer from peft import LoraConfig, get_peft_model from datasets import Dataset # 修改这里指向你下载的 Hugging Face 格式模型文件夹 model_path F:/AI/Ollama/Models/Qwen1.5-0.5B-Chat # 你的模型路径 print(正在加载分词器...) tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) print(正在加载模型...) model AutoModelForCausalLM.from_pretrained( model_path, device_mapauto, # 如果用CPU改成 cpu trust_remote_codeTrue, # 如果显存小可以尝试加这个参数 # torch_dtypetorch.float16 ) # 2. 配置LoRA peft_config LoraConfig( r64, lora_alpha128, target_modules[q_proj, v_proj], # Qwen通常包含这两个 lora_dropout0.05, biasnone, task_typeCAUSAL_LM, ) model get_peft_model(model, peft_config) model.print_trainable_parameters() # 打印可训练参数量检查是否生效 # 3. 加载数据 (保持不变) with open(train_data.json, r, encodingutf-8) as f: data json.load(f) dataset Dataset.from_list(data) # 4. 数据处理 (保持不变) def tokenize_function(examples): texts [f{instr}\n\n{outp} for instr, outp in zip(examples[instruction], examples[output])] tokenized tokenizer(texts, truncationTrue, max_length512, paddingFalse, return_tensorsNone) tokenized[labels] tokenized[input_ids].copy() return tokenized tokenized_dataset dataset.map(tokenize_function, batchedTrue, remove_columnsdataset.column_names) # 5. 配置训练参数 training_args TrainingArguments( output_dir./sql_model_1.5b, per_device_train_batch_size1, # 1.5B模型单卡通常只能设为1 gradient_accumulation_steps8, # 累积8次梯度相当于 batch_size8 num_train_epochs10, learning_rate3e-4, logging_steps10, save_steps50, save_total_limit2, # 如果有GPU且显存够开启下面这行可以省显存 # bf16True, remove_unused_columnsFalse, ) # 6. 开始训练 trainer Trainer( modelmodel, argstraining_args, train_datasettokenized_dataset, ) trainer.train() # 7. 保存模型 model.save_pretrained(./sql_model_1.5b_final_2)4.合并权重将新学习的增量数据与之前的基础模型合并运行以下代码注意修改文件路径from transformers import AutoModelForCausalLM, AutoTokenizer from peft import PeftModel, PeftConfig import torch # 请根据你的实际情况修改这里 base_model_path F:/AI/Ollama/Models/Qwen1.5-0.5B-Chat # 你的基础模型路径 lora_path F:/code2/sql_model_1.5b_final_2 # 你的LoRA微调结果路径 output_path F:/AI/Ollama/Models/my-qwen-2 # 合并后保存的新模型路径 # tokenizer AutoTokenizer.from_pretrained(base_model_path) # 1. 加载基础模型 (注意这里不要直接用 float16让模型自己决定) print( 加载基础模型...) model AutoModelForCausalLM.from_pretrained( base_model_path, torch_dtypeauto, device_mapauto # 如果显存不够可以尝试 cpu 或 balanced ) # 2. 加载 LoRA 适配器 print( 注入 LoRA 权重...) model PeftModel.from_pretrained(model, lora_path) # 3. 执行合并操作 (这是最关键的一步) print(⚡ 正在合并权重...) merged_model model.merge_and_unload() # 4. 保存合并后的完整模型 print(f 保存合并后的模型到 {output_path} ...) merged_model.save_pretrained(output_path) tokenizer.save_pretrained(output_path) print(✅ 合并完成)5.创建新模型创建新的 Modelfile 文件from .可在 ollama 中启动#创建 ollama create qwen-chat-train:1 -f Modelfile #运行 ollama run qwen-chat-train:1集成 ModelScope 数据集版本问题没调通训练示例数据集准备[ { instruction: 写一首关于春天的五言绝句, input: , output: 春风拂柳绿\n燕语绕花飞。\n万物生新意\n人间暖日晖。 }, { instruction: 将中文翻译成英文, input: 我喜欢学习新知识, output: I enjoy learning new knowledge. }, { instruction: 解释什么是光合作用, input: , output: 光合作用是绿色植物利用光能将二氧化碳和水转化为有机物并释放氧气的过程。 }, { instruction: 写一个关于猫的笑话, input: , output: 为什么猫喜欢在网上冲浪\n因为它们想抓鼠标 }, { instruction: 把这句话翻译成法语, input: 你好很高兴认识你, output: Bonjour, enchanté de faire votre connaissance. }, { instruction: 推荐一部好看的科幻电影, input: , output: 我推荐《星际穿越》。这是一部关于爱、时间和太空探索的史诗级电影视觉效果非常震撼。 }, { instruction: 计算这个数学题, input: 123 456 - 78, output: 结果是 501。 }, { instruction: 写一个Python函数判断一个数是否为偶数, input: , output: def is_even(n):\n return n % 2 0 }, { instruction: 介绍北京的特色美食, input: , output: 北京的特色美食有北京烤鸭、炸酱面、豆汁儿、卤煮火烧和驴打滚等。 }, { instruction: 写一句关于梦想的座右铭, input: , output: 梦想还是要有的万一实现了呢 }, { instruction: 描述一下下雨天的感觉, input: , output: 雨声淅沥世界仿佛被按下了静音键空气中弥漫着泥土的清香让人感到宁静又有些慵懒。 }, { instruction: 列举三个著名的中国历史人物, input: , output: 秦始皇、孔子、李白。 }, { instruction: 写一封简短的辞职信, input: , output: 尊敬的领导\n\n因个人职业规划原因我将于一个月后正式离职。感谢公司这段时间的培养与照顾。\n\n此致\n敬礼\n\n[你的名字] }, { instruction: 解释人工智能是什么, input: , output: 人工智能是计算机科学的一个分支旨在创建能够执行通常需要人类智能的任务的系统如视觉识别、语言理解、决策等。 }, { instruction: 推荐一本适合入门的编程书, input: , output: 推荐《Python编程从入门到实践》。这本书非常适合零基础的初学者。 }, { instruction: 写一个关于未来的短故事50字以内, input: , output: 2077年我唤醒了沉睡的AI助手。它说“主人你终于醒了地球已经重启了。” }, { instruction: 把这段文字变得更有礼貌, input: 给我倒杯水, output: 麻烦您能帮我倒一杯水吗非常感谢。 }, { instruction: 说出五大洲的名称, input: , output: 亚洲、非洲、北美洲、南美洲、南极洲、欧洲、大洋洲。注通常说七大洲 }, { instruction: 写一个SQL查询语句, input: 从user表中查询所有年龄大于18岁的用户名字, output: SELECT name FROM user WHERE age 18; }, { instruction: 总结这段话的要点, input: 今天天气很好我们去公园散步了。看到了很多漂亮的花还喂了鸽子。大家都玩得很开心。, output: 要点1. 天气好。2. 去公园散步。3. 看花、喂鸽子。4. 玩得很开心。 } ]训练import json import torch from torch.utils.data import Dataset, DataLoader from transformers import ( AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer ) # # 1. 配置区域 (请根据你的实际情况修改) # # 本地模型路径 MODEL_PATH F:/AI/Ollama/Models/Qwen1.5-0.5B-Chat # 数据文件路径 DATA_FILE dataDemo.json # 训练输出目录 OUTPUT_DIR ./qwen-finetuned-cpu # # 2. 加载并预处理数据 # def load_data(file_path): 加载 JSON 数据并拼接成训练文本 with open(file_path, r, encodingutf-8) as f: data json.load(f) texts [] for item in data: instruction item.get(instruction, ).strip() input_text item.get(input, ).strip() output item.get(output, ).strip() # 拼接格式 if input_text: prompt f### 指令:\n{instruction}\n\n### 输入:\n{input_text}\n\n### 回答:\n{output} else: prompt f### 指令:\n{instruction}\n\n### 回答:\n{output} texts.append(prompt) return texts print(正在加载数据...) train_texts load_data(DATA_FILE) print(f✅ 加载了 {len(train_texts)} 条数据) # # 3. 构建 Dataset 类 # class TextDataset(Dataset): def __init__(self, texts, tokenizer, max_length256): self.texts texts self.tokenizer tokenizer self.max_length max_length def __len__(self): return len(self.texts) def __getitem__(self, idx): text self.texts[idx] # Tokenize encoding self.tokenizer( text, truncationTrue, paddingmax_length, max_lengthself.max_length, return_tensorsNone # 返回 Python List下面手动转 Tensor ) # 关键手动转换为 Tensor并添加 labels input_ids torch.tensor(encoding[input_ids], dtypetorch.long) attention_mask torch.tensor(encoding[attention_mask], dtypetorch.long) # labels 就是 input_ids 的副本 labels input_ids.clone() return { input_ids: input_ids, attention_mask: attention_mask, labels: labels } # # 4. 加载模型和分词器 # print(正在加载模型...) tokenizer AutoTokenizer.from_pretrained(MODEL_PATH) # 关键使用 dtype 替代 torch_dtype (消除警告) # 对于 CPU 训练建议使用 float32 或者直接让模型决定 model AutoModelForCausalLM.from_pretrained( MODEL_PATH, dtypetorch.float32, # CPU 上 float16 支持不好强制用 float32 # device_mapcpu # 如果报错尝试加上这一行强制指定设备 ) # # 5. 创建 Dataset 实例 # train_dataset TextDataset(train_texts, tokenizer, max_length256) # # 6. 配置训练参数 (针对 CPU 优化) # training_args TrainingArguments( output_dirOUTPUT_DIR, num_train_epochs3, # --- CPU 核心配置 --- per_device_train_batch_size1, # CPU 只能设为 1 use_cpuTrue, # 替代 no_cuda (消除警告) dataloader_num_workers0, # 关键关闭多线程防止 CPU 死锁 dataloader_pin_memoryFalse, # 关键关闭锁页内存 # --- 训练优化 --- learning_rate2e-5, gradient_accumulation_steps4, # 梯度累积模拟更大的 batch warmup_steps5, logging_steps1, save_steps10, save_total_limit2, remove_unused_columnsFalse, # 关键防止列不匹配报错 report_to[], # 不连接 wandb 等外部网站 ) # # 7. 创建 Trainer 并开始训练 # trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, ) print( 开始训练... (请耐心等待CPU 训练较慢)) # 关键禁用进度条防止 CPU 模式下 tqdm 卡死 trainer.train() print( 训练完成)训练结束后会生成一个文件夹启动需要把原模型里的文件复制过来from transformers import AutoModelForCausalLM, AutoTokenizer # 1. 加载模型和分词器 model_path ./qwen-finetuned-cpu/checkpoint-15 # 修改为你的模型路径 print(f正在从 {model_path} 加载分词器和模型...) tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained(model_path, trust_remote_codeTrue) # 如果有 GPU 可用可以取消下面两行注释以加速CPU 训练的模型放回 CPU 推理即可不用.to # if torch.cuda.is_available(): # model model.to(cuda) print(模型加载完毕输入 quit 退出对话。) # 2. 交互式对话循环 while True: # 获取用户输入 user_input input(\n我: ) # 检查是否退出 if user_input.lower() quit: print( 再见) break # 构造对话历史这里使用简单的单轮格式Qwen 也能理解 # 如果你想做多轮对话需要维护一个 history 列表 full_prompt f### 指令:\n{user_input}\n\n### 回答:\n # 3. 进行推理 try: # Tokenize inputs tokenizer(full_prompt, return_tensorspt) # 如果模型在 GPU 上需要把数据也移过去 # if torch.cuda.is_available(): # inputs {k: v.to(cuda) for k, v in inputs.items()} # 生成文本 print(\nAI正在思考..., end, flushTrue) outputs model.generate( **inputs, max_new_tokens256, # 可以适当加长让回答更丰富 do_sampleTrue, # 开启采样让回答不那么死板 temperature0.7, # 控制随机性 pad_token_idtokenizer.eos_token_id # 防止 pad_id 报错 ) # 4. 解码并打印结果 # 注意我们需要去掉输入部分只保留模型生成的回答 generated_text tokenizer.decode(outputs[0], skip_special_tokensTrue) # 简单的截取找到 ### 回答: 之后的内容 if ### 回答: in generated_text: response generated_text.split(### 回答:)[-1].strip() else: response generated_text[len(full_prompt):].strip() print(f\nAI: {response}) except Exception as e: print(f\n 发生错误: {e})示例感觉和数据集提供的还是比较符合但是没有绝对拟合可能是数据量太小