LLM 使用

大型语言模型(LLM)使用

最近稍微折腾了一下 LLM,记录一下相关的一些内容📝:

API 调用

OpenAI

需要花💰直接调用的 API 通常情况下采用的是这一种调用方式

👴🚪 正在用的是 柏拉图AI,价格可以接受,模型也多

Python3 调用脚本示例(Chat):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from openai import OpenAI

def chat_with_openai(
prompt: str,
model: str = "gpt-4o-mini",
api_key: str = "api_key",
base_url: str = "https://api.bltcy.ai/v1",
system_message: str = None,
max_tokens: int = 10240,
temperature: float = 0.7,
stream: bool = False,
) -> str:
"""
使用 OpenAI 的 Chat API 进行对话生成。

参数:
prompt (str): 用户输入的提示文本
model (str): 要调用的模型名称
api_key (str): 替换成自己的 API 的密钥
base_url (str): 使用第三方平台时替换成对应的 API URL (找对应的说明文档)
system_message (str): 可选的系统消息,用于设置对话的上下文或行为
max_tokens (int): 生成文本的最大长度,这里默认为 10240,单位是 token
temperature (float): 控制生成文本的随机性,这里默认为 0.7。该数值越大,生成的文本随机性越大
stream (bool): 是否启用流式输出,这里默认为 False

返回:
str: 生成的文本内容。如果启用流式输出,返回生成器的内容。
"""
try:
# 初始化 OpenAI 客户端
client = OpenAI(api_key=api_key, base_url=base_url)

# 构建消息列表
messages = []
if system_message:
messages.append({"role": "system", "content": system_message})
messages.append({"role": "user", "content": prompt})

# 调用 OpenAI API
response = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
stream=stream,
)

# 处理流式输出
if stream:
def generate():
for chunk in response:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
return generate()
else:
return response.choices[0].message.content

except Exception as e:
# 异常处理
print(f"An error occurred: {e}")
return None

更多详细的 API 调用说明可以前往 📦 openai-python 仓库查看 api.md

Ollama

如果服务器端是通过 Ollama 搭建的,那么需要下面的方式进行调用。(本地部署搭建 Ollama 的过程在后面进行介绍)

Python3 脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from ollama import Client

# 对话
def chat_with_ollama(
prompt: str,
model: str = "deepseek-coder-v2:latest",
host: str = "http://192.168.155.8:11434",
system_message: str = None,
max_tokens: int = 10240,
temperature: float = 0.7,
stream: bool = False,
) -> str:
"""
使用 Ollama Client 进行对话生成。

参数:
prompt (str): 用户输入的提示文本
model (str): 自行指定要使用的模型名称
host (str): Ollama 服务的主机地址,需要指定为对应的 Ollama 服务器地址
system_message (str): 可选的系统消息,用于设置对话的上下文或行为
max_tokens (int): 生成文本的最大长度,默认为 10240
temperature (float): 控制生成文本的随机性,默认为 0.7
stream (bool): 是否启用流式输出,默认为 False

返回:
str: 生成的文本内容。如果启用流式输出,返回生成器的内容
"""
try:
# 初始化 Ollama 客户端
client = Client(host=host)

# 构建消息列表
messages = []
if system_message:
messages.append({"role": "system", "content": system_message})
messages.append({"role": "user", "content": prompt})

# 调用 Ollama API
response = client.chat(
model=model,
messages=messages,
options={
"num_ctx": max_tokens,
"temperature": temperature,
},
stream=stream,
)

# 处理流式输出
if stream:
def generate():
for chunk in response:
yield chunk["message"]["content"]
return generate()
else:
return response["message"]["content"]

except Exception as e:
# 异常处理
print(f"An error occurred: {e}")
return None

# 生成
def generate_with_ollama(
prompt: str,
model: str = "deepseek-coder-v2:latest",
host: str = "http://192.168.155.8:11434",
max_tokens: int = 10240,
) -> str:
"""
使用 Ollama Client 进行文本生成(非对话模式)。

参数:
prompt (str): 输入的提示文本。
model (str): 自行指定要使用的模型名称
host (str): Ollama 服务的主机地址,需要指定为对应的 Ollama 服务器地址
max_tokens (int): 生成文本的最大长度

返回:
str: 生成的文本内容
"""
client = Client(host=host)
response = client.generate(
model=model,
prompt=prompt, # 直接使用 prompt 参数,而非 messages 列表
options={"num_ctx": max_tokens},
)
return response["response"]

更多详细的 API 调用说明可以前往 📦 ollama-python 仓库查看 api.md

本地部署

仅记录 Linux 下的 Ollama 部署流程:

🏝️ 部署环境

  • 软件环境:
    • 操作系统:Ubuntu 22.04 LTS
    • 显卡驱动:565.57.01
    • CUDA 版本:12.7
  • 硬件配置:
    • Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz
    • NVIDIA GeForce RTX 3080

下载安装

Linux 系统下直接执行下述命令安装 Ollama:

1
curl -fsSL https://ollama.com/install.sh | sh

其他操作系统从官网直接下载安装包安装即可:https://ollama.com/download

拉取模型

安装完毕后即可从 ollama.com 中搜索并拉取需要的模型,以 deepseek-r1 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 拉取并加载运行 deepseek-r1:8b 模型
$ ollama run deepseek-r1:8b
pulling manifest
pulling 6340dc3229b0... 100% ▕██████████████████████████████████████████████████████████████████▏ 4.9 GB
pulling 369ca498f347... 100% ▕██████████████████████████████████████████████████████████████████▏ 387 B
pulling 6e4c38e1172f... 100% ▕██████████████████████████████████████████████████████████████████▏ 1.1 KB
pulling f4d24e9138dd... 100% ▕██████████████████████████████████████████████████████████████████▏ 148 B
pulling 0cb05c6e4e02... 100% ▕██████████████████████████████████████████████████████████████████▏ 487 B
verifying sha256 digest
writing manifest
success
>>> 你是谁?
<think>

</think>

您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手DeepSeek-R1。如您有任何任何问题,我会尽我所能为您提供帮助。

>>> Send a message (/? for help)

通过 ollama run 命令运行的模型将在前台执行,执行 /exit 可以退出前台运行。

至于大模型参数量的选择,可以参考 这篇博客

在模型拉取之后可以使用 ollama list 查看已存在的模型,并且可以通过 11434 端口对已存在的模型进行 API 调用(无需处于前台执行),调用方法已在前面介绍过。但默认情况下仅可通过本地访问 11434 端口,如果需要全局监听则需要更改 Ollama 服务配置,在 [Service]模块下增加 Environment="OLLAMA_HOST=0.0.0.0",保存后重载服务配置生效。

修改 /etc/systemd/system/ollama.service 配置文件:

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=Ollama Service
After=network-online.target

[Service]
...... 原来的内容保持不动,在下面添加即可
Environment="OLLAMA_HOST=0.0.0.0"

[Install]
WantedBy=default.target

重载服务:

1
2
sudo systemctl daemon-reload
sudo systemctl restart ollama

Web UI 平台搭建

如果需要一个类似于 ChatGPT 那样的 Web 访问平台,可以搭建 Open WebUILobe ChatAnyThing LLM 等。以 Open WebUI 为例,在 Ollama 服务器上使用 Docker 搭建:

1
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

具体选用的安装方式最好还是根据使用需求,参考各仓库的 README 文档进行部署。

微调

在一些研究或工作当中,现有的通用 LLM 可能在一些专用领域中表现欠佳,或者需要为模型提供一些专用的知识库,因此通常考虑对合适的模型进行微调,以实现专用知识问答等需求。

unsloth

unsloth 是一个专注于高效微调的框架,旨在帮助用户快速、低成本地对大语言模型进行定制化训练。它支持多种流行的开源模型,如 LLaMA3.2、Gemma2、Qwen2.5 等,并且针对这些模型进行了深度优化,显著减少了微调所需的时间和显存占用。通过 unsloth,用户可以在消费级 GPU 上完成微调任务,而无需依赖昂贵的硬件设备。

全部支持的模型可以从 HuggingFace 中找到,各自都带有对应的 JupyterNotebook 代码,可以直接在 Colab 使用免费的 T4 进行测试。

已测试 Qwen2.5-7B-Instruct 可以在 GeForce RTX 3080 上进行微调

⚠️ 注意事项

由于脚本执行时会从 HuggingFace 拉取大量的数据,在本地执行的时候最好先添加下面的代码,修改环境变量来指定 HuggingFace 的镜像源用于加速下载

1
2
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"

接下来以 Qwen2.5-7B-Instruct 为例简单记录一下微调过程:

  1. unsloth 的环境配置直接参考其 Github 仓库中的安装步骤即可

  2. 在 README 文档中找到 Qwen 2.5 conversational style notebook,即 Qwen 2.5 Conversational + Unsloth 2x faster finetuning.ipynb,直接下载到本地

  3. 根据实际情况修改开头部分的模型选择:

1
2
3
4
5
6
7
8
9
10
11
model, tokenizer = FastLanguageModel.from_pretrained(
# Can select any from the below:
# "unsloth/Qwen2.5-0.5B", "unsloth/Qwen2.5-1.5B", "unsloth/Qwen2.5-3B"
# "unsloth/Qwen2.5-14B", "unsloth/Qwen2.5-32B", "unsloth/Qwen2.5-72B",
# And also all Instruct versions and Math. Coding verisons!
model_name = "unsloth/Qwen2.5-7B-Instruct", # 👈 改这里
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
# token = "hf_...", # use one if using gated models like meta-llama/Llama-2-7b-hf
)
  1. 绝大部分内容保持默认即可,如果需要使用自己构建的训练数据集,需要改动 load_dataset() 的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from unsloth.chat_templates import get_chat_template

tokenizer = get_chat_template(
tokenizer,
chat_template = "qwen-2.5",
)

def formatting_prompts_func(examples):
convos = examples["conversations"]
texts = [tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False) for convo in convos]
return { "text" : texts, }
pass

from datasets import load_dataset
dataset = load_dataset("mlabonne/FineTome-100k", split = "train")

📒 一点建议

  • 自行构建微调数据集时可以先在 huggingface 中找到原本加载的数据集,参考其内容格式进行构建
  • 具体的代码含义直接 AI 解释即可
  1. 参数设置,这一步可以根据实际需求修改训练参数(重点修改 num_train_epochsmax_steps,如果希望模型在完整的数据集上训练固定的次数,可以使用 num_train_epochs,如果更关注训练的步数(例如,调试或小规模实验),可以使用 max_steps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from trl import SFTTrainer
from transformers import TrainingArguments, DataCollatorForSeq2Seq
from unsloth import is_bfloat16_supported

trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = "text",
max_seq_length = max_seq_length,
data_collator = DataCollatorForSeq2Seq(tokenizer = tokenizer),
dataset_num_proc = 2,
packing = False, # Can make training 5x faster for short sequences.
args = TrainingArguments(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 5,
num_train_epochs = 1, # Set this for 1 full training run.
# max_steps = 60,
learning_rate = 2e-4,
fp16 = not is_bfloat16_supported(),
bf16 = is_bfloat16_supported(),
logging_steps = 1,
optim = "adamw_8bit",
weight_decay = 0.01,
lr_scheduler_type = "linear",
seed = 3407,
output_dir = "outputs",
report_to = "none", # Use this for WandB etc
),
)
  1. 模型训练,trainer.train() 这一步执行后慢慢等待训练完成即可
  2. 完成训练后的内容是测试微调后的模型,略过这一步
  3. 保存模型,这一步可以将微调后的模型进行保存(存储到 huggingface 或者本地,也可导出 GGUF,支持导入到 Ollama 中)
    • 直接根据实际需求找到对应的行将 if False 该为 True 即可
    • 例如导出为 Q8_0
1
2
# Save to 8bit Q8_0 (第一个参数是要导出的存放路径,可以自行指定)
if True: model.save_pretrained_gguf("path_to_save_model", tokenizer,)

至于不同的量化类型可以参考这两篇博客进行选择:AI 模型量化格式介绍, 为 llama.cpp 选择理想的量化类型

  • 不过简单来说,如果配置足够,直接选用 Q8_0 就好

顺带引用一下第二篇博客里的图片:

background Layer 1 最差模型 有潜力 最佳模型 低优先级 Q8_0 Q6_K Q5_K_M Q5_1 Q5_K_S Q5_0 Q4_1 Q4_K_M Q4_K_S Q4_0 Q3_K_L Q3_K_M 体积小 体积大 困惑度小 困惑度大 大小与困惑度变化对比

  1. 导入到 Ollama 中运行微调后的模型,这一步直接使用上一步导出的模型文件中自带的 Modelfile 即可
1
2
3
4
# 切换到上一步导出的模型目录
cd path_to_save_model
# 导入
ollama create <model_name> -f ./Modelfile

不使用框架

如果 unsloth 尚未支持需要微调的模型,可以在 开源大模型食用指南 中尝试找一找微调指南