实践步骤
在MAC上微调一个大模型、导出模型并部署、暴露API给web后端,前端展示
微调模型
- 框架: LLama-Factory
- 算法:LoRA
- 基座模型:deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B(从HUggingFace上下载)
微调的步骤
下载LLama Factory
python
# 如果下载失败的话,可以手动下载压缩包
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
# 下载依赖包
pip install -e ".[torch,metrics]"
# 检验是否安装成功
llamafactory-cli version
# 启动webui
llamafactory-cli webui
部署成功后会自动弹出localhost:7860的页面

基座模型下载
从共享基座模型的HuggingFace上下载,这次我尝试使用huggingface-cli命令下载,但是一直尝试失败,所以就手动从网站下载,并在LLama Factory的页面填入下载模型的地址,加载模型就成功了。 创建模型路径
shell
mkdir Hugging-Face
# 将下载的相关模型信息放在该文件下,在LLama Factory的模型路径上填写该路径

加载模型步骤:

模型加载成功后选择微调数据
参考:github.com/hiyouga/LLa... 中关于身份的训练数据,替换{{name}},{{author}}
json
[{
"instruction": "你好",
"input": "",
"output": "您好,我是 {{name}},一个由 {{author}} 开发的 AI 助手,很高兴认识您。请问我能为您做些什么?"
},
{
"instruction": "你好",
"input": "",
"output": "您好,我是 {{name}},一个由 {{author}} 打造的人工智能助手,请问有什么可以帮助您的吗?"
}]
创建的magic_conch.json文件,并放在LLama-Factory/data下,并在data文件夹下的dataset_info.json中添加一行数据,可以在训练集中加载到这个json。
json
"magic_conch": {"file_name": "magic_conch.json"},
设置微调参数并开启微调

微调完成后重新导入模型
在chat中卸载模型,并选择检查点重新导入模型,之后问我是谁,就会得到微调数据希望的答案了。
导出微调模型为真正的新模型
LoRA仅为低秩矩阵,调整了部分权重,但是并不修改原来的模型,所以需要进行模型合并导出, 本地创建文件夹位置
bash
mkdir -p Models/deepseek-r1-1.5b-merged
在UI上选择export(导出),选择导出设备为auto,设置导出路径,点击导出即可。
利用FASTapi创建一个端口
ini
from fastapi import FastAPI
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
app = FastAPI()
# 模型路径
model_path = "/Users/xxx/deepseek/Models/deepseek-r1-1.5b-merged"
# 加载 tokenizer (分词器)
tokenizer = AutoTokenizer.from_pretrained(model_path)
# 加载模型并移动到可⽤设备(GPU/CPU)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = AutoModelForCausalLM.from_pretrained(model_path).to(device)
@app.get("/generate")
async def generate_text(prompt: str):
# 使⽤ tokenizer 编码输⼊的 prompt
inputs = tokenizer(prompt, return_tensors="pt").to(device)
# 使⽤模型⽣成⽂本
outputs = model.generate(inputs["input_ids"], max_length=150)
# 解码⽣成的输出
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
return {"generated_text": generated_text}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app,host='localhost',port=8060)
创建java后端程序调用相关接口
关键代码,github地址:github.com/huangyf2013...
less
@RestController
@RequestMapping(value = "/chat")
public class ChatController {
@Autowired
private ChatService chatService;
@RequestMapping("/generate")
@ResponseBody
public Result generate(@RequestParam String prompt) {
Result result = Result.success();
if(StringUtils.isBlank(prompt)) {
return Result.error(ResultCode.PARAM_INVALID,"prompt不能为空");
}
try {
String res = chatService.callAiForOneReply(prompt);
result.setData(res);
} catch (Exception e) {
result = Result.error();
}
return result;
}
}
typescript
@Service
public class ChatServiceImpl implements ChatService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private AiServiceConfig aiServiceConfig;
@Override
public String callAiForOneReply(String prompt) {
// 获取基础URL http://localhost:8000
String baseUrl = aiServiceConfig.getBaseUrl();
// 构建完整的请求URL http://localhost:8000/generate?prompt=XXX
String url = String.format("%s/generate?prompt=%s", baseUrl, prompt);
// 发送GET请求并获取响应
GenerateResponse response = restTemplate.getForObject(url, GenerateResponse.class);
// 从响应中取出 generated_text 字段值返回
return response != null ? response.getGenerated_text() : "";
}
}
实践参考地址:【【DeepSeek+LoRA+FastAPI】开发人员如何微调大模型并暴露接口给后端调用】www.bilibili.com/video/BV1R6...