使用的是https://github.com/OpenBMB/MiniCPM
MiniCPM 是面壁智能与清华大学自然语言处理实验室共同开源的系列端侧大模型,主体语言模型 MiniCPM-2B 仅有 24亿(2.4B)的非词嵌入参数量。
- 经过 SFT 后,MiniCPM 在公开综合性评测集上,MiniCPM 与 Mistral-7B相近(中文、数学、代码能力更优),整体性能超越 Llama2-13B、MPT-30B、Falcon-40B 等模型。
- 经过 DPO 后,MiniCPM 在当前最接近用户体感的评测集 MTBench上,MiniCPM-2B 也超越了 Llama2-70B-Chat、Vicuna-33B、Mistral-7B-Instruct-v0.1、Zephyr-7B-alpha 等众多代表性开源大模型。
- 以 MiniCPM-2B 为基础构建端侧多模态大模型 MiniCPM-V,整体性能在同规模模型中实现最佳,超越基于 Phi-2 构建的现有多模态大模型,在部分评测集上达到与 9.6B Qwen-VL-Chat 相当甚至更好的性能。
- 经过 Int4 量化后,MiniCPM 可在手机上进行部署推理,流式输出速度略高于人类说话速度。MiniCPM-V 也直接跑通了多模态大模型在手机上的部署。
- 一张1080/2080可高效参数微调,一张3090/4090可全参数微调,一台机器可持续训练 MiniCPM,二次开发成本较低。
下载对应的库和包,写者用的是ModelScope的MiniCPM-2B-dpo-bf16,一开始使用的是HuggingFace,但是HuggingFace的速度太慢。
MiniCPM-2B-dpo-bf16只有5g可以轻松使用。
使用环境:
python3.9.12 大于python3.8即可
PyTorch version 2.1.2+cu118 Found.
cuda118 笔者在使用121和122等高版本都出现编译上的问题
代码问题:
#开源网站给的测试代码
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
torch.manual_seed(0)
path = 'openbmb/MiniCPM-2B-dpo-bf16'
tokenizer = AutoTokenizer.from_pretrained(path)
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.bfloat16, device_map='cuda', trust_remote_code=True)
responds, history = model.chat(tokenizer, "山东省最高的山是哪座山, 它比黄山高还是矮?差距多少?", temperature=0.5, top_p=0.8, repetition_penalty=1.02)
print(responds)
上面使用的是openbmb/MiniCPM-2B-dpo-bf16要进行修改
from modelscope import snapshot_download
path = snapshot_download('OpenBMB/MiniCPM-2B-dpo-fp16')
#下载完毕后使用path来找寻地址,然后本地部署
print(path)
因为笔者要测试cpu运行,整体的代码为:
from transformers import AutoModelForCausalLM, AutoTokenizer
from modelscope import snapshot_download
import torch
# 设置随机种子
torch.manual_seed(0)
# 加载tokenizer和model
path = 'C:/Users/PC/.cache/modelscope/hub/OpenBMB/MiniCPM-2B-dpo-fp16'
# path = snapshot_download('OpenBMB/MiniCPM-2B-dpo-fp16')
tokenizer = AutoTokenizer.from_pretrained(path)
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.float32, trust_remote_code=True) # 注意:CPU通常不支持bfloat16,因此更改为float32
# 确保模型在CPU设备上
model.to('cpu')
# 构造提示文本
prompt = "山东省最高的山是哪座山, 它比黄山高还是矮?差距多少?"
# 对提示文本进行编码
inputs = tokenizer(prompt, return_tensors="pt")
# 确保输入也在CPU设备上
inputs = inputs.to('cpu')
# 使用模型生成文本
generated_sequence = model.generate(
input_ids=inputs['input_ids'],
attention_mask=inputs['attention_mask'],
max_length=100, # 您可以根据需要调整这个值
temperature=0.8,
top_p=0.8,
)
# 将生成的序列解码为文本
generated_text = tokenizer.decode(generated_sequence[0], skip_special_tokens=True)
# 打印生成的文本
print(generated_text)
然而想要得到逐个字输出那样的,要进行修改,大致模板是(cpu版):
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import time
# 设置随机种子
torch.manual_seed(0)
path = 'C:/Users/PC/.cache/modelscope/hub/OpenBMB/MiniCPM-2B-dpo-fp16'
tokenizer = AutoTokenizer.from_pretrained(path)
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.float32,trust_remote_code=True) # 注意:CPU通常不支持bfloat16,因此使用float32
# 确保模型在CPU设备上
model.to('cpu')
# 构造提示文本
prompt = "山东省最高的山是哪座山, 它比黄山高还是矮?差距多少?"
# 对提示文本进行编码
inputs = tokenizer(prompt, return_tensors="pt")
# 确保输入也在CPU设备上
inputs = inputs.to('cpu')
# 初始化变量
start_time = time.time()
total_tokens = 0
# 使用模型生成文本,逐字输出
generated_sequence = []
input_ids = inputs['input_ids']
attention_mask = inputs['attention_mask']
for _ in range(100):
# 生成下一个token
with torch.no_grad():
output = model.generate(
input_ids=input_ids, # 使用到目前为止生成的文本作为输入
attention_mask=attention_mask,
max_length=20, # 每次只生成一个token
# max_new_tokens=100, # 允许生成的新token的最大数量
temperature=0.8,
top_p=0.8,
do_sample=True # 启用采样以获得多样性
)
# 获取最后一个生成的token ID并添加到序列中
generated_token = output[0, -1].item()
generated_sequence.append(generated_token)
# 更新输入以进行下一次迭代
input_ids = torch.cat([input_ids, output[:, -1:]], dim=1) # 在序列末尾添加新生成的token
# 注意力掩码也需要更新,但通常我们假设没有额外的填充,所以只需复制最后一个值
attention_mask = torch.cat([attention_mask, attention_mask[:, -1:]], dim=1)
# 为了避免在生成的token后面添加填充,确保attention_mask的最后一个值是1
attention_mask[:, -1] = 1
# 解码并打印当前生成的文本
current_text = tokenizer.decode(generated_sequence, skip_special_tokens=True)
print(current_text)
# 更新token计数
total_tokens += 1
# 计算已经过去的时间(秒)
elapsed_time = time.time() - start_time
# 计算并打印每秒token速率
if elapsed_time > 0:
tokens_per_second = total_tokens / elapsed_time
print(f"平均token: {tokens_per_second:.2f}/s")
print("输出完成")
但问题这样输出并不好看
所以就用python web 来搭载,这里使用的是streamlit,搭载web非常快
import streamlit as st
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
torch.manual_seed(0)
#streamlit run Web.py
import time
path = 'C:/Users/PC/.cache/modelscope/hub/OpenBMB/MiniCPM-2B-dpo-fp16'
tokenizer = AutoTokenizer.from_pretrained(path)
def MiniGpt(text_number,prompt,text_max_length,text_temperature,text_top_p,place,place_token,place_time,option):
if option =="cpu":
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.float32, trust_remote_code=True)
inputs = tokenizer(prompt, return_tensors="pt")
elif option =="gpu":
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.float16, trust_remote_code=True).to(device)
inputs = tokenizer(prompt, return_tensors="pt").to(device)
else:
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.float32, trust_remote_code=True)
inputs = tokenizer(prompt, return_tensors="pt")
start_time = time.time()
total_tokens = 0
generated_sequence = []
input_ids = inputs['input_ids']
attention_mask = inputs['attention_mask']
for _ in range(text_number):
with torch.no_grad():
output = model.generate(
input_ids=input_ids, # 使用到目前为止生成的文本作为输入
attention_mask=attention_mask,
max_length=text_max_length,
temperature=text_temperature,
top_p=text_top_p,
repetition_penalty=1.02,
do_sample=True # 启用采样以获得多样性
)
# 获取最后一个生成的token ID并添加到序列中
generated_token = output[0, -1].item()
generated_sequence.append(generated_token)
# 更新输入以进行下一次迭代
input_ids = torch.cat([input_ids, output[:, -1:]], dim=1) # 在序列末尾添加新生成的token
# 注意力掩码也需要更新,但通常我们假设没有额外的填充,所以只需复制最后一个值
attention_mask = torch.cat([attention_mask, attention_mask[:, -1:]], dim=1)
# 为了避免在生成的token后面添加填充,确保attention_mask的最后一个值是1
attention_mask[:, -1] = 1
# 解码并打印当前生成的文本
current_text = tokenizer.decode(generated_sequence, skip_special_tokens=True)
place.write(current_text)
print(current_text)
# 更新token计数
total_tokens += 1
# 计算已经过去的时间(秒)
elapsed_time = time.time() - start_time
# 计算并打印每秒token速率
if elapsed_time > 0:
tokens_per_second = total_tokens / elapsed_time
place_token.write(f"平均token: {tokens_per_second:.2f}/s")
place_time.write(f"耗时: {elapsed_time:.2f}s")
print("输出完成")
if __name__ == '__main__':
st.header(':blue[MiniGpt] :sunglasses:', divider='rainbow')
with st.chat_message("assistant"):
st.write("你好 👋 我是:blue[MiniGpt]")
option = st.sidebar.selectbox(
'选择使用cpu还是gpu',
('cpu', 'gpu'))
text_number = st.sidebar.slider('文本上限', 10, 500, 100,10)
text_max_length = st.sidebar.slider('输出', 1, 30, 1)
text_temperature = st.sidebar.slider('随机性', 0.1, 1.0, 0.8,0.1)
text_top_p = st.sidebar.slider('多样性', 0.1, 1.0, 0.8, 0.1)
st.sidebar.button(":blue[暂停]")
prompt = st.chat_input("写些什么……")
if prompt:
st.write(f"输入文本为:{prompt}")
st.write(f'共{len(prompt)} 字符.')
st.divider()
with st.spinner('稍等一会...'):
with st.chat_message("assistant"):
st.write('提问是:' + prompt)
place = st.empty()
place_token = st.empty()
place_time = st.empty()
MiniGpt(text_number,prompt,text_max_length
,text_temperature,text_top_p,place
,place_token,place_time,option)
st.success('完成!')
使用wifi用手机查看
在这些过程还是有一些小疏漏的,请多多讨论修改。