AI定制化
一、Windows
1.环境条件
(1).PC Windows 10
(2).CPU AMD5 7500f (6核心12线程)
(3).GPU RTX 4070 Super(任务管理器界面:12G专用显存+15.8G共享显存=27G显存)
(4).内存条CL30 DDR5 6000 16G*2
(5).安装VMware Workstation Pro+CentOS7镜像
(6).安装CUDA驱动、CUDA ToolKit及cuDNN
(7).安装Conda+PyCharm(使用3.11版本python)
(8).安装Java JDK(17/21)+Maven3.9.9+IDEA【可选:只是为了提供功能接口(api)】
(9).安装Mysql8(初始化用户名密码:root/123456)
(10).pip安装jupyter
(11).安装Visual Studio Code+NVM(多nodeJS版本管控工具)
(Critical).using VPN meanwhile
使用Conda安装python3.11环境
conda create --name ai_fine_tuning_3_11 python=3.11
activate ai_fine_tuning_3_11
#conda env remove --name ai_fine_tuning_3_11
显卡驱动CUDA确认
根据cuda12.6找相同版本12.6的CUDA ToolKit进行下载安装:
https://developer.nvidia.com/cuda-downloads
# nvcc -V 用于检查安装情况
# set cuda 检查环境变量
# cuda_12.6.3_561.17_windows.exe早于Visual Studio 2022安装时,不会自动为后者追加拓展文件。
安装cuDNN
https://developer.nvidia.com/cudnn
根据cuda12.6,安装torch相近的12.4版本
# https://pytorch.org/get-started/locally/#windows-prerequisites-2
conda install pytorch torchvision torchaudio pytorch-cuda=12.4 -c pytorch -c nvidia
下载模型Qwen2.5-7B
git clone https://www.modelscope.cn/Qwen/Qwen2.5-7B-Instruct.git
下载模型训练工具LLama-Factory,然后使用pycharm+python3.11打开,进入项目目录下执行如下命令:
# download it.zip from https://github.com/hiyouga/LLaMA-Factory.git
pip install -e ".[torch,metrics]"
pip install -r requirements.txt
pip install modelscope -U
pip install matplotlib -U
pip install hqq -U
pip install bitsandbytes -U
DS_BUILD_CPU_ADAM=1 pip install deepspeed
下载CMake,解压后将其bin目录加至Path环境变量下:cmake-3.31.1-windows-x86_64.zip
https://cmake.org/download/
下载Visual Studio(Community Edition),运行后选择桌面c++板块内容安装,若中途取消安装了,可以在开始菜单下栏的新增项目(或推荐项目)中找到该安装程序。这里只需要c++的编译环境,把除了C++板块以外的安装项目全部取消勾选(该板块自动勾选系统SDK等其他相关组件,不要取消勾选)
https://visualstudio.microsoft.com/zh-hans/vs/
下载safetensor量化工具llama.cpp
# download it.zip from https://github.com/ggerganov/llama.cpp.git
# 参照`有用的东西>私有模型部署到Ollama并推理测试(Win10)`中的方法编译‘适用于gguf的’量化工具,至于safetensors文件转gguf文件的convert.py及其requirements.txt后续补充
下载jupyterNote,用于网页开发
# INSTALL
activate ai_fine_tuning_3_11
pip install jupyter -U
# RUN
jupyter notebook
2.运行检测
(1)GPU
在LLaMA-Factory工程主目录下新建test.py,键入并运行如下代码
#!/usr/bin/python
# -*- coding: utf-8 -*-
import torch
if __name__ == '__main__':
print(torch.cuda.current_device())
print(torch.cuda.get_device_name(0))
print(torch.__version__)
处理py运行时依赖报错,找不到dll
# 需要下载依赖分析工具: https://github.com/lucasg/Dependencies/tree/v1.11.1
# 解压Dependencies_x64_Release.zip,点击DependenciesGui.exe
# 输入报错的目录,如:C:\Users\admin\anaconda3\envs\ai_fine_tuning_3_11\Lib\site-packages\torch\lib\
找到真正缺少的dll文件,后根据如下步骤下载
报错如:OSError: [WinError 126] 找不到指定的模块。 Error loading "C:\Users\admin\anaconda3\envs\ai_fine_tuning_3_11\Lib\site-packages\torch\lib\fbgemm.dll" or one of its dependencies.
发现实际缺失libomp140.x86_64.dll
win下载DLL:
https://www.dll-files.com/download/fdf9273b477d0107bc8f8c4bf4311173/mfc100u.dll.html?c=UVR6TnFWcDVOSCtCSm9VaDg1dzZaZz09
搜索下载对应32bit的dll后,如mfc100.dll,将文件放至C:\Windows\System32
然后点击Win+R,输入“regsvr32 mfc100.dll”将DLL注册到系统中
正常运行为如下结果
0
NVIDIA GeForce RTX 4070 SUPER
2.5.1
(2)Model
根据官方模型Qwen2.5-7B仓库中的readme文件进行测试
from modelscope import AutoModelForCausalLM, AutoTokenizer
# model_name = "qwen/Qwen2.5-7B-Instruct"
model_name = "C:\\Users\\admin\Desktop\\ai_material\\Qwen2___5-7B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
prompt = "Give me a short introduction to large language model."
messages = [
{"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
generated_ids = model.generate(
**model_inputs,
max_new_tokens=512
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)
运行效果如下
Loading checkpoint shards: 100%|██████████| 4/4 [00:06<00:00, 1.63s/it]
Some parameters are on the meta device because they were offloaded to the cpu.
A large language model (LLM) is a type of artificial intelligence model designed to understand and generate human-like text based on the input it receives. These models are typically trained on vast amounts of textual data from the internet, books, articles, and other sources, allowing them to learn complex patterns in language and context.
LLMs can perform a wide range of natural language processing tasks, including but not limited to:
1. **Text Generation**: Creating coherent paragraphs or entire documents.
2. **Translation**: Converting text from one language to another.
3. **Summarization**: Condensing long texts into shorter summaries.
4. **Question Answering**: Providing answers to questions based on given information.
5. **Dialogue Systems**: Engaging in conversations with users, understanding their queries, and responding appropriately.
These models are usually deep learning architectures, such as transformers, which allow them to process and understand the context of words within sentences and longer passages of text. The size of these models refers to the number of parameters they contain, with larger models generally having more parameters and thus being able to capture more nuanced and sophisticated language patterns.
Examples of large language models include those developed by companies like Google (e.g., BERT, T5), Microsoft (e.g., Turing-NLG), and Alibaba Cloud (e.g., Qwen).
Process finished with exit code 0
(3)LLama-Factory WebUI
cd C:\Users\admin\Desktop\ai_material\LLaMA-Factory-main
conda activate ai_fine_tuning_3_11
python src/webui.py
运行效果应如下,模型的位置(如右上)需要手动选择
根据官网教程,使用当前设备满足微调条件的模型进行微调
检查点路径用于配置微调过程中的参数对象副本,方便基于先前预训练进度二次训练
动作参数枚举 | 参数说明 |
---|---|
version | 显示版本信息 |
train | 命令行版本训练 |
chat | 命令行版本推理chat |
export | 模型合并和导出 |
api | 启动API server,供接口调用 |
eval | 使用mmlu等标准数据集做评测 |
webchat | 前端版本纯推理的chat页面 |
webui | 启动LlamaBoard前端页面,包含可视化训练,预测,chat,模型合并多个子页面 |
数据集格式(传送门):目前仅支持 alpaca 格式和 sharegpt 格式的数据集
3.知识库搭建
这里使用AnythingLLM(传送门)
安装过程需要科学上网,额外超过2GB的资源。
安装完成之后,点击按钮 Get started 进入设置向导界面。
(连接上本地使用ollama启动的模型, token 现设4096,一般来说越大会越准确但解析等待时间越长)
-
Embedding Preference(嵌入模型)的选择,选择默认的 AnythingLLM Embedder 。
-
向量数据库 Vector Database Connection,选择默认的 LanceDB。
确认相关信息之后,制定工作空间名称(如test和ttt),并使用网页链接为模型增加知识
网页源知识内容如下
创建新聊天窗,测试查询知识
向外部提供api查询知识库(如果开启了验证,需要在api_docs右上角使用PRIVATE_KEY登录)
当然这些是最简单的本地知识库搭建,除此之外 AnythingLLM 还提供了灵活的配置供给管理,可以设置例如语料分割参数、修改 Chat mode、修改 Embedding chunk 的大小(用于设置向量模型处理资料的颗粒度)等等。
4.AI Workflow讲解
Langchain的核心是“链”的概念,这是一个构建块,允许您组合和编排不同的组件,以创建复杂而智能的应用程序。想象一下,您是一名数据科学家,正在从事一个尖端项目,该项目涉及处理和分析大量非结构化数据,例如客户评论、社交媒体帖子,甚至是学术论文。您的目标是从这些数据中提取见解和有价值的信息,但任务的庞大数量和复杂性可能令人生畏。使用LangChain链,您可以将这个非常复杂的任务分解成更小的、可管理的部分,然后将它们链接在一起,以创建一个无缝的端到端解决方案。这就像拥有一支由高技能助手组成的团队,每个人都专注于一项特定的任务,而您正在协调他们的努力,以构建真正非凡的东西。
原文链接:https://blog.csdn.net/qkh1234567/article/details/140371297
(5-1)LLM Chain
在此示例中,我们首先从LangChain导入必要的导入。然后,我们初始化一个 OpenAI 语言模型,并创建一个提示模板,要求提供描述给定产品的最佳名称。接下来,我们将语言模型和提示模板组合成一个 LLMChain。现在,可以使用任何产品描述调用此链,并且它将根据输入生成合适的公司名称。例如,如果我们输入“Queen Size Sheet Set”作为产品,连锁店可能会输出“Royal Slumber Bedding Co.”作为建议的公司名称。很简单,对吧?但不要被它的简单性所迷惑——LLMChain 是一个有用的工具,可用于广泛的应用程序,从内容生成到数据分析,甚至代码生成。
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.chains import LLMChain
# Initialize the language model
llm_model = "gpt-3.5-turbo"
llm = ChatOpenAI(temperature=0.9, model=llm_model)
# Create a prompt template
prompt = ChatPromptTemplate.from_template("What is the best name to describe a company that makes {product}?")
# Combine the LLM and prompt into an LLMChain
chain = LLMChain(llm=llm, prompt=prompt)
# Run the chain on some input data
product = "Queen Size Sheet Set"
chain_output = chain.invoke({product})
print(chain_output)
# Output
# {'product': {'Queen Size Sheet Set'}, 'text': 'Royal Slumber Bedding Co.'}
(5-2)顺序链
LLMChain 是一个很好的起点,但有时我们的任务需要按特定顺序执行一系列步骤或操作。这就是顺序链发挥作用的地方,它允许我们将多个提示链接在一起,以创建更复杂和更精密的工作流程。
(5-2-1)SimpleSequentialChain
从 SimpleSequentialChain 开始,它非常适合链中每个步骤都有单个输入和单个输出的方案。想象一下,您想要创建一个系统,该系统不仅可以根据产品建议公司名称,还可以为该公司生成简短描述。
from langchain.chains import SimpleSequentialChain
# Initialize the language model
llm = ChatOpenAI(temperature=0.9, model=llm_model)
# Prompt template 1: Suggest a company name
first_prompt = ChatPromptTemplate.from_template("What is the best name to describe a company that makes {product}?")
chain_one = LLMChain(llm=llm, prompt=first_prompt)
# Prompt template 2: Generate a company description
second_prompt = ChatPromptTemplate.from_template("Write a 20-word description for the following company: {company_name}")
chain_two = LLMChain(llm=llm, prompt=second_prompt)
# Create the SimpleSequentialChain
overall_simple_chain = SimpleSequentialChain(chains=[chain_one, chain_two], verbose=True)
# Run the chain on some input data
product = "Queen Size Sheet Set"
chain_output = overall_simple_chain.invoke(product)
print(chain_output)
# Output
# {'input': 'Queen Size Sheet Set',
# 'output': 'Regal Comfort Linens provides luxurious and stylish bedding options to ensure a comfortable and elegant sleep experience for customers.'}
在此示例中,我们首先定义两个单独的 LLMChains:一个用于根据产品建议公司名称,另一个用于生成给定公司名称的简短描述。然后,我们将这两条链组合成一个 SimpleSequentialChain,指定它们的执行顺序。当我们使用“Queen Size Sheet Set”等产品调用此链时,它将首先生成一个公司名称(例如,“Royal Comfort Linens”),然后将该名称用作第二条链的输入,该链将输出类似“Regal Comfort Linens 提供豪华时尚的床上用品选择,以确保为客户提供舒适优雅的睡眠体验”。SimpleSequentialChain 的美妙之处在于它能够将复杂的任务分解为更小的、可管理的步骤,每个步骤都有明确定义的输入和输出。这种模块化方法不仅使代码更具可读性和可维护性,而且还允许随着项目的发展而获得更大的灵活性和可扩展性。
(5-2-2)SequentialChain
虽然 SimpleSequentialChain 非常适合简单的任务,但有时我们的链需要同时处理多个输入和输出。进入 SequentialChain,这是其更简单版本的更强大、更灵活的版本。
from langchain.chains import SequentialChain
# Initialize the language model
llm = ChatOpenAI(temperature=0.9, model=llm_model)
# Prompt template 1: Translate review to English
first_prompt = ChatPromptTemplate.from_template("Translate the following review to English:\n\n{Review}")
chain_one = LLMChain(llm=llm, prompt=first_prompt, output_key="English_Review")
# Prompt template 2: Summarize the review in one sentence
second_prompt = ChatPromptTemplate.from_template("Can you summarize the following review in 1 sentence:\n\n{English_Review}")
chain_two = LLMChain(llm=llm, prompt=second_prompt, output_key="summary")
# Prompt template 3: Detect the language of the review
third_prompt = ChatPromptTemplate.from_template("What language is the following review:\n\n{Review}")
chain_three = LLMChain(llm=llm, prompt=third_prompt, output_key="language")
# Prompt template 4: Generate a follow-up response
fourth_prompt = ChatPromptTemplate.from_template("Write a follow-up response to the following summary in the specified language:\n\nSummary: {summary}\n\nLanguage: {language}")
chain_four = LLMChain(llm=llm, prompt=fourth_prompt, output_key="followup_message")
# Create the SequentialChain
overall_chain = SequentialChain(
chains=[chain_one, chain_two, chain_three, chain_four],
input_variables=["Review"],
output_variables=["English_Review", "summary", "followup_message"],
verbose=True,
)
# Run the chain on some input data
review = "Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre. J'achète les mêmes dans le commerce et le goût est bien meilleur...\nVieux lot ou contrefaçon !?"
chain_output = overall_chain.invoke(review)
print(chain_output)
# Output
# Entering new SequentialChain chain...
# Finished chain.
# {'English_Review': "I find the taste mediocre. The foam doesn't hold, it's "
# 'weird. I buy the same ones in stores and the taste is much '
# 'better... \n'
# 'Old batch or counterfeit!?',
# 'Review': "Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre. "
# "J'achète les mêmes dans le commerce et le goût est bien "
# 'meilleur...\n'
# 'Vieux lot ou contrefaçon !?',
# 'followup_message': "Je suis désolé(e) d'apprendre que vous avez trouvé le "
# "goût du produit médiocre. Il est possible qu'il s'agisse "
# "d'un lot ancien ou contrefait, comme vous l'avez "
# "suggéré. Il est important de s'assurer de la qualité des "
# 'produits que nous consommons. Avez-vous envisagé de '
# 'contacter le fabricant pour clarifier la situation ? '
# "J'espère que votre prochaine expérience d'achat sera "
# 'plus satisfaisante. Merci de partager votre avis.',
# 'summary': 'The reviewer found the taste of the product mediocre and '
# 'different from what they usually buy in stores, suggesting that '
# 'it may be an old batch or counterfeit.'}
在这个更高级的示例中,我们定义了四个单独的 LLMChains,每个 LLMChain 都有自己的特定任务:
1.将商品评论从原文翻译成英文。
2.用一句话总结翻译后的评论。
3.检测评论的原始语言。
4.以检测到的语言生成对摘要的后续响应。
这里的关键区别在于,每条链可以有多个输入和输出变量,我们需要使用 output_key 和 input_variables/output_variables 参数显式指定这些变量。例如,第一条链将原始评论作为输入并输出English_Review。然后,第二条链将English_Review作为输入,并输出一句话的摘要。第三条链使用原始评论来检测语言,最后,第四条链将摘要和语言结合起来生成followup_message。然后,我们将这四条链组合成一个 SequentialChain,指定它们应该执行的顺序,以及整个链的输入和输出变量。当我们用产品评论来调用这个链条时,比如“Je trouve le goût médiocre.La mousse ne tient pas, c’est bizarre.J’achète les mêmes dans le commerce et le goût est bien meilleur…nVieux lot ou contrefaçon !?“,它将经历链条中的每一步,将评论翻译成英语,对其进行总结,检测原始语言(在本例中为法语),最后根据摘要生成法语的后续回复。SequentialChain 的有用之处在于它能够处理具有多个输入和输出的复杂工作流程,允许您将最复杂的任务分解为更小、可管理的组件。
(5-3)路由链
有时,我们的任务需要不同的方法或专门的子链,具体取决于输入数据。这就是路由器链发挥作用的地方,它允许我们根据某些标准动态地将输入路由到适当的子链。
(5-3-1)MultiPromptChain
路由器链的一个常见用例是当我们有多个提示时,每个提示专门用于特定类型的输入或任务。MultiPromptChain 允许我们定义这些专用提示,然后根据输入内容将输入动态路由到适当的提示。
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.prompts import PromptTemplate
# Define specialized prompt templates
physics_template = """You are a very smart physics professor. \
You are great at answering questions about physics in a concise\
and easy to understand manner. \
When you don't know the answer to a question you admit\
that you don't know.
Here is a question:
{input}"""
math_template = """You are a very good mathematician. \
You are great at answering math questions. \
You are so good because you are able to break down \
hard problems into their component parts,
answer the component parts, and then put them together\
to answer the broader question.
Here is a question:
{input}"""
history_template = """You are a very good historian. \
You have an excellent knowledge of and understanding of people,\
events and contexts from a range of historical periods. \
You have the ability to think, reflect, debate, discuss and \
evaluate the past. You have a respect for historical evidence\
and the ability to make use of it to support your explanations \
and judgements.
Here is a question:
{input}"""
computerscience_template = """ You are a successful computer scientist.\
You have a passion for creativity, collaboration,\
forward-thinking, confidence, strong problem-solving capabilities,\
understanding of theories and algorithms, and excellent communication \
skills. You are great at answering coding questions. \
You are so good because you know how to solve a problem by \
describing the solution in imperative steps \
that a machine can easily interpret and you know how to \
choose a solution that has a good balance between \
time complexity and space complexity.
Here is a question:
{input}"""
# Create prompt info dictionaries
prompt_infos = [
{
"name": "physics",
"description": "Good for answering questions about physics",
"prompt_template": physics_template,
},
{
"name": "math",
"description": "Good for answering math questions",
"prompt_template": math_template,
},
{
"name": "History",
"description": "Good for answering history questions",
"prompt_template": history_template,
},
{
"name": "computer science",
"description": "Good for answering computer science questions",
"prompt_template": computerscience_template,
},
]
# Initialize the language model
llm = ChatOpenAI(temperature=0, model=llm_model)
# Create destination chains (LLMChains) for each prompt
destination_chains = {
}
for p_info in prompt_infos:
name = p_info["name"]
prompt_template = p_info["prompt_template"]
prompt = ChatPromptTemplate.from_template(template=prompt_template)
chain = LLMChain(llm=llm, prompt=prompt)
destination_chains[name] = chain
# Define a default chain for inputs that don't match any specialized prompt
default_prompt = ChatPromptTemplate.from_template("{input}")
default_chain = LLMChain(llm=llm, prompt=default_prompt)
MULTI_PROMPT_ROUTER_TEMPLATE = """Given a raw text input to a \
language model select the model prompt best suited for the input. \
You will be given the names of the available prompts and a \
description of what the prompt is best suited for. \
You may also revise the original input if you think that revising\
it will ultimately lead to a better response from the language model.
<< FORMATTING >>
Return a string snippet enclosed by triple backticks a JSON object formatted to look like below:
{
"destination": string \ name of the prompt to use or "default"
"next_inputs": string \ a potentially modified version of the original input
}
REMEMBER: "destination" MUST be one of the candidate prompt \
names specified below OR it can be "default" if the input is not \
well suited for any of the candidate prompts. \
REMEMBER: "next_inputs" can just be the original input \
if you don't think any modifications are needed.
<< CANDIDATE PROMPTS >>
{destinations}
REMEMBER: If destination name not there input the question
<< INPUT >>
{input}
<< OUTPUT (remember to include the ```
json
```)>>"""
# Create the router prompt template
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str) # (a prompt template for the router to use)
router_prompt = PromptTemplate(template=router_template, input_variables=["input"], output_parser=RouterOutputParser())
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
# Create the MultiPromptChain
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True,
)
# Run the chain on some input data
physics_question = "What is black body radiation?"
chain_output = chain.invoke(physics_question)
print(chain_output)
# Output
# Entering new MultiPromptChain chain...
# physics: {'input': 'What is black body radiation?'}
# Finished chain.
# {'input': 'What is black body radiation?',
# 'text': "Black body radiation is the electromagnetic radiation emitted by a perfect absorber of radiation, known as a black body. A black body absorbs all incoming radiation and emits radiation across the entire electromagnetic spectrum. The spectrum of black body radiation is continuous and follows a specific distribution known as Planck's law. This phenomenon is important in understanding the behavior of objects at different temperatures and is a key concept in the field of thermal physics."}
math_question = "what is 2 + 2"
chain_output = chain.invoke(math_question )
print(chain_output)
# Output
# Entering new MultiPromptChain chain...
# math: {'input': 'what is 2 + 2'}
# Finished chain.
# {'input': 'what is 2 + 2', 'text': 'The answer to 2 + 2 is 4.'}
biology_question = "Why does every cell in our body contain DNA?"
chain_output = chain.invoke(biology_question )
print(chain_output)
# Output
# Entering new MultiPromptChain chain...
# None: {'input': 'Why does every cell in our body contain DNA?'}
# Finished chain.
# {'input': 'Why does every cell in our body contain DNA?',
# 'text': 'Every cell in our body contains DNA because DNA carries the genetic information that determines the characteristics and functions of an organism. DNA contains the instructions for building and maintaining an organism, including the proteins that are essential for cell function and structure. This genetic information is passed down from parent to offspring and is essential for the growth, development, and functioning of all cells in the body. Having DNA in every cell ensures that the genetic information is preserved and can be used to carry out the necessary processes for life.'}
在此示例中,我们首先定义了几个专用的提示模板,每个模板旨在处理特定类型的输入或任务(例如,物理问题、数学问题、历史问题、计算机科学问题)。然后,我们为每个模板创建提示信息词典,其中包括名称、描述和实际的提示模板本身。接下来,我们初始化我们的语言模型,并为每个专用提示创建目标链 (LLMChains)。这些目标链将是当输入与特定提示匹配时调用的目标链。我们还定义了一个默认链,该链将用于与任何专用提示不匹配的输入。MultiPromptChain 的核心是路由器链,它负责根据输入确定要使用的目标链。我们定义一个路由器提示模板,为路由器提供指令和格式,然后使用此模板和我们的语言模型创建 LLMRouterChain。最后,我们创建 MultiPromptChain 本身,传入路由器链、目标链和默认链。
当我们用“什么是黑体辐射?”这样的输入调用这个链时,路由器链将分析输入并确定这是一个物理问题。然后,它会将输入路由到物理目标链,该目标链将提供有关黑体辐射的详细答案。如果我们输入的问题与任何专业提示都不匹配,例如“DNA在细胞中的作用是什么?”,路由器链会将输入路由到默认链,默认链将尝试使用预训练的LLM数据提供一般答案。MultiPromptChain 允许我们通过动态路由输入到最合适的子链或提示来创建高度专业化和高效的工作流,确保每个输入都由最适合任务的组件处理。
2.路由链的优势
路由器链具有多种优势,使其成为机器学习和自然语言处理工具包中的有用工具:
1.专业化:通过将输入路由到专门的子链,您可以确保每个任务都由最适合它的组件处理,从而获得更准确和相关的结果。
2.效率:路由器链不是通过多个链或提示来运行每个输入,而是智能地将输入路由到适当的目的地,从而节省计算资源并提高整体性能。
3.灵活性:通过添加或删除目标链或提示,可以轻松扩展或修改路由器链,使其高度适应不断变化的需求或新域。
4.模块化:每个子链或提示都可以独立开发和测试,促进代码的可复用性和可维护性。
5.可扩展性:随着项目的增长和复杂性的增加,路由器链可以帮助管理和编排越来越多的专用组件,确保您的系统保持强大和高效。
借助路由器链,您可以创建复杂而智能的应用程序,这些应用程序可以处理各种输入和任务,同时保持高度的专业化和效率。
二、Linux
1.AutoDL开机
Top up first and then create a instance. 充值然后创建一个实例
20G以上显存的GPU
至少16GB的系统内存。对于大规模模型训练,建议使用更多内存。
存储: 足够的硬盘空间来存储模型文件和数据集。建议至少100GB的可用空间。
创建成功后,点击JupyterLab
进入终端页面,主机信息如下:
2.下载llama-factory
使用AutoDL自带的科学上网,学术资源加速,然后下载llama-factory
source /etc/network_turbo
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory/
conda create --name llamaf_311 python=3.11
conda activate llamaf_311
# 如果反复出现需要“conda init”字眼,请重新开一个终端再activate并进入llama-factory目录
pip install -r requirements.txt
3.全局安装Pytorch
conda install pytorch torchvision torchaudio pytorch-cuda=12.4 -c pytorch -c nvidia
4.清理空间
(base) root@autodl-container-00274fbfbe-ec0fdc69:~/autodl-tmp# source ~/.bashrc
+--------------------------------------------------AutoDL--------------------------------------------------------+
目录说明:
╔═════════════════╦════════╦════╦═════════════════════════════════════════════════════════════════════════╗
║目录 ║名称 ║速度║说明 ║
╠═════════════════╬════════╬════╬═════════════════════════════════════════════════════════════════════════╣
║/ ║系 统 盘║一般║实例关机数据不会丢失,可存放代码等。会随保存镜像一起保存。 ║
║/root/autodl-tmp ║数 据 盘║ 快 ║实例关机数据不会丢失,可存放读写IO要求高的数据。但不会随保存镜像一起保存 ║
╚═════════════════╩════════╩════╩═════════════════════════════════════════════════════════════════════════╝
CPU :18 核心
内存:60 GB
GPU :NVIDIA GeForce RTX 4090 D, 1
存储:
系 统 盘/ :52% 16G/30G
数 据 盘/root/autodl-tmp:1% 248M/50G
+----------------------------------------------------------------------------------------------------------------+
*注意:
1.系统盘较小请将大的数据存放于数据盘或文件存储中,重置系统时数据盘和文件存储中的数据不受影响
2.清理系统盘请参考:https://www.autodl.com/docs/qa1/
3.终端中长期执行命令请使用screen等工具开后台运行,确保程序不受SSH连接中断影响:https://www.autodl.com/docs/daemon/
(base) root@autodl-container-00274fbfbe-ec0fdc69:~/autodl-tmp#
以下两个是可以直接删除,不影响系统运行的目录,所以首先直接删除。
# conda的历史包
du -sh /root/miniconda3/pkgs/ && rm -rf /root/miniconda3/pkgs/*
# jupyterlab的回收站
du -sh /root/.local/share/Trash && rm -rf /root/.local/share/Trash
5.py测试GPU是否正常运行
(base) root@local:~# conda activate llamaf_311
(llamaf_311) root@local:~# python
Python 3.11.10 (main, Oct 3 2024, 07:29:13) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> print(torch.cuda.is_available())
True
>>> print(torch.cuda.get_device_name(0))
NVIDIA GeForce RTX 4090 D
>>> quit()
(llamaf_311) root@local:~#
6.安装git-lfs
# step1 (*suit for debian series, e.g. Ubuntu)初始化镜像仓库
curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
# step2 安装插件
apt-get install git-lfs
# step3 查看版本
git lfs --version
#huggingface注册账号:728067113@qq.com/kTTT169$*@
7.在huggingface上下载模型
cd ~/autodl-tmp/
git lfs install
source /etc/network_turbo
git clone https://huggingface.co/Qwen/Qwen2.5-7B
每秒实时查看模型下载进度:$ watch -n 1 du -sh ~/autodl-tmp/Qwen2.5-7B/
模型下载信息及文件目录大小
(base) root@local:~/autodl-tmp# git clone https://huggingface.co/Qwen/Qwen2.5-7B
Cloning into 'Qwen2.5-7B'...
remote: Enumerating objects: 43, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 43 (delta 16), reused 0 (delta 0), pack-reused 3 (from 1)
Unpacking objects: 100% (43/43), 3.61 MiB | 876.00 KiB/s, done.
Filtering content: 100% (4/4), 14.18 GiB | 5.87 MiB/s, done.
(base) root@local:~/autodl-tmp#
校验模型完整性:
md5sum path/to/model/file1
在训练脚本中指定模型路径
model_name_or_path = "path/to/model"
8.下载训练数据集
cd ~/autodl-tmp/
git lfs install
source /etc/network_turbo
# 一个训练根据场景编写SELECT-sql的训练集
git clone https://huggingface.co/datasets/tushkulange/query_text_to_sql
如需自定义数据集,参考LLama-Factory中: alpaca 格式和 sharegpt的规范格式
在训练脚本中指定数据集路径
dataset = "path/to/dataset.json"
9.使用原始模型直接推理
在进行任何形式的微调之前,首先需要对原始模型进行直接推理,以验证模型的可用性和性能
模型加载:使用transformers库加载预训练模型和tokenizer。
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "path/to/your/model"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
输入准备:准备输入文本并进行tokenization。
input_text = "Hello, how are you?"
inputs = tokenizer(input_text, return_tensors="pt")
模型推理:将tokenized的输入传递给模型,并获取输出。
outputs = model.generate(**inputs)
decoded_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(decoded_output)
实际测试代码
cd ~/autodl-tmp/
touch test_model.py
vi test_model.py
conda activate llamaf_311
python test_model.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "/root/autodl-tmp/Qwen2.5-7B"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
input_text = "Hello, how are you?"
inputs = tokenizer(input_text, return_tensors="pt")
outputs = model.generate(**inputs)
decoded_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(decoded_output)
测试效果
(base) root@local:~/autodl-tmp# python test_model.py
Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.36it/s]
Setting `pad_token_id` to `eos_token_id`:None for open-end generation.
Hello, how are you? I'm sorry to hear that you're feeling unwell. Is there anything specific that's bothering you or any particular symptoms you're experiencing? I'm here to help in any way I can.
(base) root@local:~/autodl-tmp#
10.基于PEFT的有监督方式微调
安装peft
pip install peft # 如果是LLama-factory中安装过requirements.txt,则会显示already installed
准备模型和数据
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import get_peft_model, LoraConfig
from datasets import load_dataset
model_name = "path/to/your/model"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
# /root/autodl-tmp/query_text_to_sql/train.jsonl
dataset = load_dataset('/root/autodl-tmp/query_text_to_sql')
print(dataset['train']) # 共有7000个元素
train_set = dataset['train'][:6000] # 前6000
eval_set = dataset['train'][6000:] # 6000之后的
数组分割示例代码
>>> a = [1,2,3,4,5,6,7,8,9,10]
>>> print(a[:5])
[1, 2, 3, 4, 5]
>>> print(a[5:])
[6, 7, 8, 9, 10]
LoRA配置
peft_config = LoraConfig(
task_type="CAUSAL_LM",
inference_mode=False,
r=8, # 精度
lora_alpha=32, # 缩放因子
lora_dropout=0.1
)
model = get_peft_model(model, peft_config)
使用标准的transformers训练脚本进行训练
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir="./results",
evaluation_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
num_train_epochs=3,
weight_decay=0.01,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_set,
eval_dataset=eval_set,
)
trainer.train()
11.动态合并LoRA推理
加载模型和LoRA权重
from peft import PeftModel
model = AutoModelForCausalLM.from_pretrained(model_name)
model = PeftModel.from_pretrained(model, "path/to/lora/weights")
合并LoRA权重
model = model.merge_and_unload()
推理:使用合并后的模型进行推理
inputs = tokenizer("Hello, how are you?", return_tensors="pt")
outputs = model.generate(**inputs)
decoded_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(decoded_output)
12.批量预测和训练效果评估
批量预测
def batch_predict(model, tokenizer, dataset):
predictions = []
for example in dataset:
inputs = tokenizer(example['text'], return_tensors="pt")
outputs = model.generate(**inputs)
predictions.append(tokenizer.decode(outputs[0], skip_special_tokens=True))
return predictions
predictions = batch_predict(model, tokenizer, dataset['test'])
训练效果评估
from sklearn.metrics import accuracy_score
labels = [example['label'] for example in dataset['test']]
accuracy = accuracy_score(labels, predictions)
print(f"Accuracy: {accuracy}")
13.LoRA模型合并导出
合并并导出模型
model = model.merge_and_unload()
model.save_pretrained("path/to/exported/model")
tokenizer.save_pretrained("path/to/exported/model")
加载合并后的模型
model = AutoModelForCausalLM.from_pretrained("path/to/exported/model")
tokenizer = AutoTokenizer.from_pretrained("path/to/exported/model")
14.完整的Qwen7B训练脚本
cd ~/autodl-tmp/
conda activate llamaf_311
git clone https://huggingface.co/datasets/Abirate/english_quotes
#!/usr/bin/python
# -*- coding: utf-8 -*-
from datetime import datetime
now = datetime.now()
time_str = now.strftime('%Y%m%d%H%M%S')
print(time_str)
model_dir = "/root/autodl-tmp/Qwen2.5-7B"
import torch
import torch.nn as nn
import transformers
from datasets import load_dataset,load_from_disk
from transformers import AutoTokenizer, AutoModelForCausalLM,BitsAndBytesConfig
device = "auto" # the value needs to be a device name (e.g. cpu, cuda:0) or 'auto', 'balanced', 'balanced_low_0', 'sequential'
###int4量化配置
quantization_config = BitsAndBytesConfig(
load_in_4bit=True, # 或者 load_in_8bit=True,根据需要设置
#llm_int8_threshold=6.0,
#llm_int8_has_fp16_weight=False,
llm_int8_enable_fp32_cpu_offload=True,
bnb_4bit_compute_dtype=torch.float16,#虽然我们以4位加载和存储模型,但我们在需要时会部分反量化他,并以16位精度进行计算
bnb_4bit_quant_type="nf4",#nf量化类型
bnb_4bit_use_double_quant=True,#双重量化,量化一次后再量化,进一步解决显存
)
model = AutoModelForCausalLM.from_pretrained(model_dir,device_map=device,trust_remote_code=True,torch_dtype=torch.float16,quantization_config=quantization_config)
tokenizer = AutoTokenizer.from_pretrained(model_dir,trust_remote_code=True,padding_side="right",use_fast=False)
model.gradient_checkpointing_enable
print(model)
def print_trainable_parameters(model):
"""
Prints the number of trainable parameters in the model.
"""
trainable_params = 0
all_param = 0
for _, param in model.named_parameters():
all_param += param.numel()
if param.requires_grad:
trainable_params += param.numel()
print(
f"trainable params: {trainable_params} || all params: {all_param} || trainable%: {100 * trainable_params / all_param}"
)
from peft import LoraConfig,get_peft_model,prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model)
config = LoraConfig(
r=32,
lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj","down_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
print(model)
print_trainable_parameters(model)
# Verifying the datatypes.
dtypes = {}
for _, p in model.named_parameters():
dtype = p.dtype
if dtype not in dtypes:
dtypes[dtype] = 0
dtypes[dtype] += p.numel()
total = 0
for k, v in dtypes.items():
total += v
for k, v in dtypes.items():
print(k, v, v / total)
# Training
data = load_dataset('json',data_files="/root/autodl-tmp/english_quotes/quotes.jsonl")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)
print(data)
trainer = transformers.Trainer(
model=model,
train_dataset=data["train"],
args=transformers.TrainingArguments(
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
warmup_steps=10,
max_steps=50,
learning_rate=3e-4,
fp16=True,
logging_steps=1,
output_dir="outputs/checkpoint-1"+time_str,
optim="paged_adamw_8bit",
save_strategy = 'steps',
save_steps = 10,
),
data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
model.config.use_cache = False # silence the warnings. Please re-enable for inference!
trainer.train()
trainer.save_model(trainer.args.output_dir)
import torch
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
peft_model_dir = trainer.args.output_dir
config = PeftConfig.from_pretrained(peft_model_dir)
print(config)
model = AutoModelForCausalLM.from_pretrained(
config.base_model_name_or_path, return_dict=True, device_map=device,
torch_dtype=torch.float16, quantization_config=quantization_config
)
tokenizer = AutoTokenizer.from_pretrained(config.base_model_name_or_path)
# Load the Lora model
model = PeftModel.from_pretrained(model, peft_model_dir)
print(model)
# 模拟对话
prompt = "详细介绍一下大语言模型,评价下与深度学习的差异"
messages = [
{"role": "system", "content": "你是一个智能助理."},
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
gen_kwargs = {"max_length": 512, "do_sample": True, "top_k": 1}
with torch.no_grad():
outputs = model.generate(**model_inputs, **gen_kwargs)
#outputs = outputs[:, model_inputs['input_ids'].shape[1]:] #切除system、user等对话前缀
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
# 导出模型
base_model = AutoModelForCausalLM.from_pretrained(model_dir,device_map=device,trust_remote_code=True,torch_dtype=torch.float16,quantization_config=quantization_config)
peft_model = PeftModel.from_pretrained(base_model,peft_model_dir)
merged_model = peft_model.merge_and_unload()
# 保存合井后的模型
merged_model.save_pretrained('export/my_trained_'+time_str)
tokenizer.save_pretrained('export/my_trained_'+time_str)
以上方案数据集和训练参数适配,为可行方案;根据2.2.2.转换Qwen模型到gguf
导出模型。
15.VLLM部署
创建配置文件my_model_vllm.json
{
"model_path": "/root/autodl-tmp/test_lora/export/my_trained_20241207102527",
"tokenizer_path": "/root/autodl-tmp/test_lora/export/my_trained_20241207102527",
"model_type": "qwen", // 根据你使用的模型类型填写
"port": 8000
}
执行命令部署
vllm serve --config ./my_model_vllm.json
m.守护进程式会话
# 安装
apt-get update && apt-get install -y screen
# 创建新会话,输入screen,并按下回车键
screen
# 如何确定自己处于screen会话中,只要以下命令有返回值
echo $STY
# 回到主会话,且终止当前screen会话
exit
# 回到主会话,不终止当前screen会话
按下ctl + a + d,会退出到原终端,并且显示detached,意味着这个会话只是离开并未退出
# 在主会话查看当前在后台运行的screen会话(Screen 的状态有两种,Attached 和 Detached,分别表示前台运行和后台运行)
screen -ls
# 重新进入会话,仅对Detached状态会话有效
screen -r 3228.pts-8.autodl-container-00274fbfbe-ec0fdc69
# 如果进程为Attached状态,请使用如下命令转为Detached
screen -d 2707.pts-3.autodl-container-00274fbfbe-ec0fdc69
n.设置AutoDL自动关机
不确定自己的代码需要执行多久结束,希望执行完成后立马关机。这类场景可以通过
shutdown
(最好使用命令完整路径:/usr/bin/shutdown
)命令来解决。关机用于停止用量计费。
注: 请保存好程序的日志,自动关机后标准输出中的日志将不再可见
方法1
# 假设您的程序原执行命令为
python train.py
# 那么可以在您的程序后跟上shutdown命令
python train.py; /usr/bin/shutdown # 用;拼接意味着前边的指令不管执行成功与否,都会执行shutdown命令
python train.py && /usr/bin/shutdown # 用&&拼接表示前边的命令执行成功后才会执行shutdown。请根据自己的需要选择
方法2
在您的Python代码中执行shutdown命令,例如:
import os
if __name__ == "__main__":
# xxxxxx
os.system("/usr/bin/shutdown")
三、FunctionCalling
FunctionCalling本质不是让大模型具备直接调用功能函数的能力,而是通过训练让模型能正确地将业务问题进行参数拆解与规整,得到符合功能调用的标准化参数,并告诉系统是否需要调用函数。
当前实验基于AutoDL平台,使用LLama-Factory工具,对现有业务功能进行建模。
1.导出一代模型
cd /root/autodl-tmp/LLaMA-Factory
conda activate llamaf_311
python src/webui.py
下载隧道代理
输入代理及转发端口
选择训练数据集
首先,训练数据集是关键,我们在这里使用Glaive AI生成的工具调用数据集,也可以在HuggingFace找到function calling相关的数据集,该数据集包含用户(human)、模型(gpt)、工具调用(function_call)和工具调用结果(observation)四种不同角色,以及工具列表(tools)字段。
训练模型
界面训练同如下命令,可以通过预览获得
python src/train.py \
--stage sft \
--do_train True \
--model_name_or_path /root/autodl-tmp/Qwen2.5-7B \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template default \
--flash_attn auto \
--dataset_dir data \
--dataset glaive_toolcall_en_demo,glaive_toolcall_zh_demo \
--cutoff_len 2048 \
--learning_rate 5e-05 \
--num_train_epochs 3.0 \
--max_samples 100000 \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 100 \
--warmup_steps 0 \
--packing False \
--report_to none \
--output_dir saves/Qwen2.5-7B/lora/train_2024-12-15-21-41-19 \
--fp16 True \
--plot_loss True \
--ddp_timeout 180000000 \
--optim adamw_torch \
--adapter_name_or_path saves/Qwen2.5-7B/lora/train_2024-12-15-21-11-21 \
--quantization_bit 4 \
--quantization_method bitsandbytes \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0 \
--lora_target all
导出模型
部署模型
- -host 0.0.0.0 如果可以將vLLM API對外被任何其他電腦訪問時可以設定。
- –dtype auto 一般會是預設’auto’,可以在API啟動後某一行看到他的選擇是什麼,也可自行設定 ‘float16’、‘bfloat16’、‘float32’。
- –quantization awq 可以指定量化方法 🧮,目前支援’awq’、‘gptq’、 ‘squeezellm’、‘fp8’ (experimental)。
- –gpu-memory-utilization 0.75 可以設定一個0-1之間的浮點數,調整GPU的記憶體使用比例,增加的話可以預先提供更多的KV快取空間。
- –max_seq_len 4096 可以控制模型的context length長度。
- 如果想指定GPU,可以在指令最前面加上: CUDA_VISIBLE_DEVICES=0
- 一定要記得設定
max_tokens=512
,不然很容易鬼打牆就一直卡著直到最大context length,API都timeout了還生成不完
vllm serve /root/autodl-tmp/myQwen2.57B \
--dtype auto \
--port 8000 \
--max_model_len 9000 \
--gpu_memory_utilization 0.95 \
--enable-auto-tool-choice \
--tool-call-parser hermes
调用测试
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "/root/autodl-tmp/myQwen2.57B",
"messages": [
{"role": "system", "content": "你是一个function_call选择的助手"},
{"role": "user", "content": "请给我一个你需要tool_call的问题案例"}
]
}'
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "/root/autodl-tmp/myQwen2.57B",
"messages": [
{"role": "system", "content": "你是一个function_call选择的助手"},
{"role": "user", "content": "请给我一个你需要tool_call的问题案例"},
{"role":"assistant","content":"当然,这里有一个我可能需要使用工具来回答的问题:\n\n问题:根据纽约公共图书馆的数据,纽约市有多少个图书馆?","tool_calls":[]},
{"role": "user", "content": "根据纽约公共图书馆的数据,纽约市有多少个图书馆"}
]
}'
或者使用脚本
from openai import OpenAI
client = OpenAI(
api_key="EMPTY",
base_url="http://localhost:8000/v1",
)
chat_response = client.chat.completions.create(
model="/root/autodl-tmp/myQwen2.57B",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "如果我想查询爱尔兰的天气,他需要两个接口,一个是将爱尔兰名称转为地区id的下拉列表接口,另一个是根据地区id查询天气的接口,这个接口默认查询的时间为当天,你能帮我做到吗"},
],
temperature=0.7,
top_p=0.8,
max_tokens=512,
extra_body={
"repetition_penalty": 1.05,
}
)
print("Chat response:", chat_response)
关于千问模型的开源模型API详情,请查看(传送门)
关于千问模型拓展自定义插件,即Function Call(传送门)
2.Function Call(AI Agent)
使用Qwen2.5-7B-Instruct(基础模型直接拿来用,非必要不微调【fine-tunning】)
cd ~/autodl-tmp/
source /etc/network_turbo
git clone https://huggingface.co/Qwen/Qwen2.5-7B-Instruct
conda activate llamaf_311
vllm serve /root/autodl-tmp/Qwen2.5-7B-Instruct \
--dtype auto \
--port 8000 \
--max_model_len 9000 \
--gpu_memory_utilization 0.95 \
--enable-auto-tool-choice \
--tool-call-parser hermes
pip3 install jupyter
jupyter-notebook --port 9999 --allow-root
# 36737远程ssh认证端口
# 本地端口0.0.0.0:9999
# 远程端口127.0.0.1:9999
# 目标:将远程端口映射到本地端口
ssh -p 36737 -N -L 0.0.0.0:9999:127.0.0.1:9999 root@connect.cqa1.seetacloud.com
Jupyter Cell_1
###### 1.自定义AI拓展功能
def search_baidu(keyword):
"""从百度搜索引擎中搜索关键词"""
return f"{keyword}是一个技术博主"
def search_google(keyword):
"""从谷歌搜索引擎中搜索关键词"""
return f"{keyword}是一个后端工程师"
def search_bing(keyword):
"""从必应搜索引擎中搜索关键词"""
return f"{keyword}是一个Python爱好者"
###### 2.声明工具集及用法
tools = [
{
"type": "function",
"function": {
"name": "search_baidu",
"description": "从百度搜索引擎中搜索关键词",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词",
}
},
"required": ["keyword"],
},
}
},
{
"type": "function",
"function": {
"name": "search_google",
"description": "从google搜索引擎中搜索关键词",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词",
}
},
"required": ["keyword"],
},
}
},
{
"type": "function",
"function": {
"name": "search_bing",
"description": "从bing搜索引擎中搜索关键词",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词",
}
},
"required": ["keyword"],
},
}
}
]
available_functions = { "search_baidu": search_baidu, "search_google": search_google, "search_bing": search_bing }
Jupyter Cell_2
from openai import OpenAI
import json
client = OpenAI(
api_key="EMPTY",
base_url="http://localhost:8000/v1",
)
Jupyter Cell_3
def search(keyword):
messages = [
{"role": "system", "content": f"你是一个智能代理,如果有可用的工具,请指明到tool_calls返回的消息中,并自动拼接合法的参数并返回"},
{"role": "user", "content": f"汇总下百度、谷歌、必应三个搜索引擎关于'{keyword}'的结果"}
]
# 发起首次请求,告诉gpt要做什么,已经有哪些函数可以调动
response = client.chat.completions.create(
model="/root/autodl-tmp/Qwen2.5-7B-Instruct",
messages=messages,
tools=tools,
tool_choice="auto",
)
response_message = response.choices[0].message
print(response_message)
tool_calls = response_message.tool_calls
print(tool_calls)
# 检查是否需要调用函数
if tool_calls:
# 解析所有需要调用的函数及参数
messages.append(response_message) # 注意这里要将openai的回复也拼接到消息列表里
# 将所有函数调用的结果拼接到消息列表里
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(**function_args)
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
)
second_response = client.chat.completions.create(
model="/root/autodl-tmp/Qwen2.5-7B-Instruct",
messages=messages,
)
return second_response.choices[0].message.content
print(search("xixi"))
如果需要增强该AI Agent的能力,可以把调用其他AI Agent、查询知识图谱和查询数据库的api也封装到函数,并在tools列表进行声明
方法执行结果
3.向量数据库 milvus
参考官网教程使用虚拟机通过Docker搭建:
使用root用户安装
su root
安全策略配置
# 需要先安装 nfs-utils 才能挂载 nfs 网络存储
yum install -y nfs-utils wget
# 关闭 防火墙(生产不推荐)
systemctl stop firewalld && systemctl disable firewalld
# 关闭 selinux (setenforce 0是本次开机暂时关闭,后面的sed是下次开机生效‘永久关闭’)
setenforce 0 && sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
# 关闭 swap,官方说明:不改可能无法正常启动应用
swapoff -a
yes | cp /etc/fstab /etc/fstab_bak
cat /etc/fstab_bak |grep -v swap > /etc/fstab
# 检查swap是否关闭,若swap那一行全部为0,则已经关闭
free -m
内核配置
# 如果有配置,则修改
sed -i "s#^net.ipv4.ip_forward.*#net.ipv4.ip_forward=1#g" /etc/sysctl.conf
sed -i "s#^net.bridge.bridge-nf-call-ip6tables.*#net.bridge.bridge-nf-call-ip6tables=1#g" /etc/sysctl.conf
sed -i "s#^net.bridge.bridge-nf-call-iptables.*#net.bridge.bridge-nf-call-iptables=1#g" /etc/sysctl.conf
sed -i "s#^net.ipv6.conf.all.disable_ipv6.*#net.ipv6.conf.all.disable_ipv6=1#g" /etc/sysctl.conf
sed -i "s#^net.ipv6.conf.default.disable_ipv6.*#net.ipv6.conf.default.disable_ipv6=1#g" /etc/sysctl.conf
sed -i "s#^net.ipv6.conf.lo.disable_ipv6.*#net.ipv6.conf.lo.disable_ipv6=1#g" /etc/sysctl.conf
sed -i "s#^net.ipv6.conf.all.forwarding.*#net.ipv6.conf.all.forwarding=1#g" /etc/sysctl.conf
# 可能没有,追加
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.conf
echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding = 1" >> /etc/sysctl.conf
# 使修改生效
sysctl -p
# check
cat /etc/sysctl.conf | grep -v ^# | grep -v ^$
换源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum clean all
yum makecache
安装Docker
# 安装docker需要的软件仓库支持
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
# 安装docker的仓库源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 旧版本,有则卸载
yum remove -y docker \
docker-client \
docker-client-latest \
docker-ce-cli \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine
# 安装指定版本docker (Search: yum list docker-ce --showduplicates | sort -r)
yum install -y docker-ce-19.03.11 docker-ce-cli-19.03.11 containerd.io-1.2.13
# 设置docker配置镜像加速
mkdir -p /etc/docker && tee /etc/docker/daemon.json <<'EOF'
{
"registry-mirrors": [
"https://do.nark.eu.org",
"https://dc.j8.work",
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn"
],
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
EOF
# 启动docker并将docker加入开机自启项
systemctl daemon-reload && systemctl enable docker && systemctl restart docker
# 查看docker版本信息
docker info
docker version
# 检查组信息是否正确
[root@liu k8s]# docker info | grep Cgroup
Cgroup Driver: systemd
Extra(额外)
# 停止所有正在运行的容器并删除
docker rm -f $(docker ps -q)
# 清除所有未运行的容器
docker container prune
# 清除所有镜像,包括imagesID重复的
docker rmi -f $(docker images -q)
宿主机:192.168.31.96
虚拟机:ssh -p 22 root@192.168.72.129
算力云:ssh -p 36737 root@connect.cqa1.seetacloud.com
测试虚拟机网络端口在宿主机上是否可联通
# 虚拟机CentOS7执行
firewall-cmd --zone=public --add-port=9085/tcp --permanent
firewall-cmd --zone=public --add-port=8000/tcp --permanent
firewall-cmd --zone=public --add-port=19530/tcp --permanent
firewall-cmd --reload
yum install -y nc
nc -l -k 9085
# 在windows宿主机执行
telnet 192.168.72.129 9085
安装Milvus:https://milvus.io/docs/v2.0.x/install_standalone-docker.md
查看Compatibility Matrix of Milvus(数据库) and Attu(管理面板):Here is the link
docker search milvusdb/milvus docker search zilliz/attu
或搜索:https://hub.docker.com/
MY_MILVUS_TMP="${HOME}/my_milnvs/"
mkdir -p $MY_MILVUS_TMP
cd $MY_MILVUS_TMP
cat << EOF > embedEtcd.yaml
listen-client-urls: http://0.0.0.0:2379
advertise-client-urls: http://0.0.0.0:2379
quota-backend-bytes: 4294967296
auto-compaction-mode: revision
auto-compaction-retention: '1000'
EOF
cat << EOF > user.yaml
# Extra config to override default milvus.yaml
EOF
mkdir -p $MY_MILVUS_TMP/volumes/milvus
单机启动
docker pull milvusdb/milvus:v2.4.4
docker pull zilliz/attu:v2.4.11
sudo docker run -d \
--name milvus-standalone \
--security-opt seccomp:unconfined \
-e ETCD_USE_EMBED=true \
-e ETCD_DATA_DIR=/var/lib/milvus/etcd \
-e ETCD_CONFIG_PATH=/milvus/configs/embedEtcd.yaml \
-e COMMON_STORAGETYPE=local \
-v $(pwd)/volumes/milvus:/var/lib/milvus \
-v $(pwd)/embedEtcd.yaml:/milvus/configs/embedEtcd.yaml \
-v $(pwd)/user.yaml:/milvus/configs/user.yaml \
-p 19530:19530 \
-p 9091:9091 \
-p 2379:2379 \
--health-cmd="curl -f http://localhost:9091/healthz" \
--health-interval=30s \
--health-start-period=90s \
--health-timeout=20s \
--health-retries=3 \
milvusdb/milvus:v2.4.4 \
milvus run standalone 1> /dev/null
sudo docker run -itd \
--restart=always \
-p 8000:3000 \
--security-opt seccomp=unconfined \
-e MILVUS_URL=192.168.72.129:19530 \
zilliz/attu:v2.4.11
# docker exex -it d342rhj431 /bin/bash
将应用发布到算力云,在算力云上访问8888等同于访问虚拟机:8000,虚拟机执行
# 务必保证“内网服务器”/etc/ssh/sshd_config中开启"AllowTcpForwarding yes"选项,否则-R转发远程端口将不可用
ssh -p 36737 -NR 8888:192.168.72.129:8000 root@connect.cqa1.seetacloud.com
参考官方文档,根据示例用法将“图像检索”、“文字检索”、“图像检索”等能力集成到api或func中,供LLM调用。
Build RAG with Milvus, it simple usage code as below:
使用markdown文档构建知识库
! pip install --upgrade pymilvus openai requests tqdm
import os
os.environ["OPENAI_API_KEY"] = "local-***********"
# wget https://github.com/milvus-io/milvus-docs/releases/download/v2.4.6-preview/milvus_docs_2.4.x_en.zip
# unzip -q milvus_docs_2.4.x_en.zip -d milvus_docs
from glob import glob
text_lines = []
for file_path in glob("milvus_docs/en/faq/*.md", recursive=True):
with open(file_path, "r") as file:
file_text = file.read()
text_lines += file_text.split("# ")
from openai import OpenAI
openai_client = OpenAI()
def emb_text(text):
return (
openai_client.embeddings.create(input=text, model="text-embedding-3-small")
.data[0]
.embedding
)
test_embedding = emb_text("This is a test")
embedding_dim = len(test_embedding)
print(embedding_dim)
print(test_embedding[:10])
from pymilvus import MilvusClient
milvus_client = MilvusClient(uri="http://localhost:19530")
collection_name = "my_rag_collection"
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
milvus_client.create_collection(
collection_name=collection_name,
dimension=embedding_dim,
metric_type="IP", # Inner product distance
consistency_level="Strong", # Strong consistency level
)
from tqdm import tqdm
data = []
for i, line in enumerate(tqdm(text_lines, desc="Creating embeddings")):
data.append({"id": i, "vector": emb_text(line), "text": line})
milvus_client.insert(collection_name=collection_name, data=data)
question = "How is data stored in milvus?"
search_res = milvus_client.search(
collection_name=collection_name,
data=[
emb_text(question)
], # Use the `emb_text` function to convert the question to an embedding vector
limit=3, # Return top 3 results
search_params={"metric_type": "IP", "params": {}}, # Inner product distance
output_fields=["text"], # Return the text field
)
import json
retrieved_lines_with_distances = [
(res["entity"]["text"], res["distance"]) for res in search_res[0]
]
print(json.dumps(retrieved_lines_with_distances, indent=4))
context = "\n".join(
[line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
)
SYSTEM_PROMPT = """
Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.
"""
USER_PROMPT = f"""
Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
<context>
{context}
</context>
<question>
{question}
</question>
"""
response = openai_client.chat.completions.create(
model="/root/autodl-tmp/myQwen2.57B",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": USER_PROMPT},
],
)
print(response.choices[0].message.content)
4.知识图谱数据Neo4j
Neo4j依赖Jdk,如下是兼容矩阵:
neo4j==5.x
jdk==17
下载JDK17(jdk-17.0.12_linux-x64_bin.tar.gz)
下载Neo4J(Neo4J-5.26.0-Community-Linux/Mac Executable)
windows上传文件到虚拟机(支撑多版本JDK)
scp -P 22 -r ./neo4j-community-5.26.0-unix.tar.gz root@192.168.72.129:~
scp -P 22 -r ./jdk-17.0.12_linux-x64_bin.tar.gz root@192.168.72.129:~
scp -P 22 -r ./jdk-21_linux-x64_bin.tar.gz root@192.168.72.129:~
scp -P 22 -r ./jdk-23_linux-x64_bin.tar.gz root@192.168.72.129:~
scp -P 22 -r ./jdk-8u251-linux-x64.tar.gz root@192.168.72.129:~
linux虚拟机执行
cd ~ && mkdir software
mv neo4j-community-5.26.0-unix.tar.gz software/
mv jdk-17.0.12_linux-x64_bin.tar.gz software/
mv jdk-21_linux-x64_bin.tar.gz software/
mv jdk-23_linux-x64_bin.tar.gz software/
mv jdk-8u251-linux-x64.tar.gz software/
cd software
tar -zvxf neo4j-community-5.26.0-unix.tar.gz -C .
tar -zvxf jdk-17.0.12_linux-x64_bin.tar.gz -C .
tar -zvxf jdk-21_linux-x64_bin.tar.gz -C .
tar -zvxf jdk-23_linux-x64_bin.tar.gz -C .
tar -zvxf jdk-8u251-linux-x64.tar.gz -C .
编辑~/.bashrc
或~/.bash_profile
,追加以下内容,然后使用source
命令编译使其生效
set java environment
export JAVA_8_HOME=$HOME/software/jdk1.8.0_251
export JAVA_17_HOME=$HOME/software/jdk-17.0.12
export JAVA_21_HOME=$HOME/software/jdk-21.0.5
export JAVA_23_HOME=$HOME/software/jdk-23.0.1
# 默认JDK为JDK8
JAVA_HOME=$JAVA_8_HOME
JRE_HOME=$JAVA_HOME/jre
PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export JAVA_HOME JRE_HOME CLASS_PATH PATH
# Switch JDK
jdk() {
if [ -z "$1" ]; then
echo "Usage: jdk <jdk_number>"
echo "Available JDKs:"
echo "8. JAVA_8_HOME"
echo "17. JAVA_17_HOME"
echo "21. JAVA_21_HOME"
echo "23. JAVA_23_HOME"
return
fi
case "$1" in
8)
export JAVA_HOME=$JAVA_8_HOME
;;
17)
export JAVA_HOME=$JAVA_17_HOME
;;
21)
export JAVA_HOME=$JAVA_21_HOME
;;
23)
export JAVA_HOME=$JAVA_23_HOME
;;
*)
echo "Invalid JDK number"
return
;;
esac
export PATH=$JAVA_HOME/bin:$PATH
echo "Switched to JDK$1"
}
# alias命令动态切换JDK版本
alias jdk8="jdk 8"
alias jdk17="jdk 17"
alias jdk21="jdk 21"
alias jdk23="jdk 23"
alias dicker="docker"
切换效果如图
修改Neo4J配置文件,检查配置文件
[root@localhost conf]# pwd
/root/software/neo4j-community-5.26.0/conf
[root@localhost conf]# cat neo4j.conf | grep -v ^# | grep -v ^$
server.directories.import=import
server.default_listen_address=0.0.0.0
server.bolt.enabled=true
server.bolt.listen_address=:7687
server.http.enabled=true
server.http.listen_address=:7474
server.https.enabled=false
server.https.listen_address=:7473
# 以下是默认配置
db.tx_log.rotation.retention_policy=2 days 2G
server.jvm.additional=-XX:+UseG1GC
server.jvm.additional=-XX:-OmitStackTraceInFastThrow
server.jvm.additional=-XX:+AlwaysPreTouch
server.jvm.additional=-XX:+UnlockExperimentalVMOptions
server.jvm.additional=-XX:+TrustFinalNonStaticFields
server.jvm.additional=-XX:+DisableExplicitGC
server.jvm.additional=-Djdk.nio.maxCachedBufferSize=1024
server.jvm.additional=-Dio.netty.tryReflectionSetAccessible=true
server.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048
server.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true
server.jvm.additional=-XX:FlightRecorderOptions=stackdepth=256
server.jvm.additional=-XX:+UnlockDiagnosticVMOptions
server.jvm.additional=-XX:+DebugNonSafepoints
server.jvm.additional=--add-opens=java.base/java.nio=ALL-UNNAMED
server.jvm.additional=--add-opens=java.base/java.io=ALL-UNNAMED
server.jvm.additional=--add-opens=java.base/sun.nio.ch=ALL-UNNAMED
server.jvm.additional=--enable-native-access=ALL-UNNAMED
server.jvm.additional=-Dlog4j2.disable.jmx=true
server.jvm.additional=-Dlog4j.layout.jsonTemplate.maxStringLength=32768
server.windows_service_name=neo4j
[root@localhost conf]#
启停neo4j
jdk17
firewall-cmd --zone=public --add-port=7474/tcp --permanent
firewall-cmd --zone=public --add-port=7687/tcp --permanent
firewall-cmd --reload
cd /root/software/neo4j-community-5.26.0/bin
./neo4j start
./neo4j stop
输入初始化账号(neo4j
)和密码(neo4j
),更新密码为66666666
4-1.命令
创建节点和关系
在Neo4j中,可以使用以下命令创建节点和关系:
- 创建节点:
CREATE (n:Label {property: 'value'})
- 创建关系:
CREATE (node1)-[:RELATIONSHIP]->(node2)
查询节点和关系
使用Cypher语言可以查询节点和关系:
- 查询所有节点:
MATCH (n) RETURN n
- 查询特定节点的属性:
MATCH (node:Label {property: 'value'}) RETURN node
- 查询节点及其关系:
MATCH (node1)-[r]->(node2) RETURN node1, r, node2
更新和删除数据
- 更新节点属性:
MATCH (node:Label {property: 'value'}) SET node.newProperty = 'new value'
- 删除节点及其关系:
MATCH (node)-[r]-() DELETE node, r
聚合函数和其他操作
Neo4j还支持各种聚合函数和其他操作,例如:
- 计算节点数量:
MATCH (node:Label) RETURN COUNT(node)
- 计算关系数量:
MATCH ()-[r]->() RETURN COUNT(r)
测试使用命令
-- ### cypher language ###
-- 清空数据所有数据(包括关系)
MATCH (n)
DETACH DELETE n;
-- 创建数据
CREATE (ren:People{ id:1,name:"zhangsan",age:18 });
CREATE (ren:People{ id:2,name:"lisi",age:20 });
CREATE (property:House{ name: 'house', area:1000 });
CREATE (property:Car{ name: 'car',price:1000000 });
-- 使用新节点创建[认识]关系
MATCH (a:People {id: 1}), (b:People {id: 2})
CREATE (a)-[:KNOWS{since:2008}]->(b);
MATCH (a:People),(b:People) WHERE a.name = 'zhangsan' and b.name = 'lisi'
CREATE (a)-[:KNOWS{since:2008}]->(b);
-- 检查认识关系
MATCH (a:People)-[r:KNOWS]->(b:People)
RETURN a, r, b;
-- 创建财产关系
MATCH (a:People {id: 1}), (b:House {name: "house"})
CREATE (a)-[:OWNS]->(b);
MATCH (a:People {id: 2}), (b:Car {name: "car"})
CREATE (a)-[:OWNS]->(b);
-- 检查财产关系(前25行)
MATCH p=()-[]->() RETURN p LIMIT 25;
-- 【其他部分】
-- 排序(ps:默认升序)
MATCH (a:People)
RETURN a.id,a.name,a.age
ORDER BY a.age DESC;
-- 删除关系再删除节点
-- 删除指定类对象间的关系
MATCH (a:People)-[r]->(b:People)
DELETE r;
-- 删除所有关系
MATCH ()-[r]->()
DELETE r;
-- 删除和People对象有关的所有关系
MATCH (a:People)-[r]-()
DELETE r;
-- 添加/更新属性
MATCH (a:People) WHERE a.name = 'lisi'
SET a.tel = '123456';
-- 删除属性
MATCH (a:People) WHERE a.name = 'lisi'
REMOVE a.tel;
-- 删除属性并返回当前对象信息
MATCH (a:People) WHERE a.name = 'lisi'
REMOVE a.tel
RETURN a
-- 函数、去重、合并结果集等范例...略
4-2.集成到Python
参考官方资料:https://neo4j-contrib.github.io/py2neo/
下载依赖
pip install py2neo
python程序
from py2neo import Graph
graph = Graph("http://http://192.168.72.129:7474", auth=("neo4j", "66666666"))
graph.run("MATCH (a:People)-[r]->(b:People) DELETE r")
result = graph.run("MATCH (a:People) RETURN a.id,a.name,a.age ORDER BY a.age DESC")
for record in result:
print(record["a"]["name"])
四、CPU部署模型
1.MiniConda
# 设置Conda安装目录
export MINI_CONDA_HOME=$HOME/miniconda3
mkdir -p $MINI_CONDA_HOME
# 安装最新版miniconda
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O $MINI_CONDA_HOME/miniconda.sh
bash $MINI_CONDA_HOME/miniconda.sh -b -u -p $MINI_CONDA_HOME
rm -rf $MINI_CONDA_HOME/miniconda.sh
# 初始化环境变量(Linux or Mac)
$MINI_CONDA_HOME/bin/conda init bash
$MINI_CONDA_HOME/bin/conda init zsh
source ~/.bashrc
source ~/.zshrc
# Miniconda配置文件路径:~/.condarc,一般情况下配置文件不存在,需要创建并初始化它
conda config --set show_channel_urls yes
初始化的配置文件.condarc
channels:
- https://repo.anaconda.com/pkgs/main
- https://repo.anaconda.com/pkgs/r
show_channel_urls: true
将.condarc
修改成
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- defaults
show_channel_urls: true
default_channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
pytorch-lts: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
2.GCC-12
编译vLLM-CPU需要GCC-12版本,以下方式支持安装至非root用户下,替换当前的GCC- 4.8.5(CentOS7)
下载:https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-12.4.0/gcc-12.4.0.tar.gz
下载:https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.gz
解压gcc-12.4.0.tar.gz,查看contrib/download_prerequisites
所需依赖
...
gmp='gmp-6.2.1.tar.bz2'
mpfr='mpfr-4.1.0.tar.bz2'
mpc='mpc-1.2.1.tar.gz'
isl='isl-0.24.tar.bz2'
...
可以看到gcc-12.4.0所需依赖为
gmp-6.2.1.tar.bz2、mpfr-4.1.0.tar.bz2、mpc-1.2.1.tar.gz、isl-0.24.tar.bz2
其中gmp又依赖于m4,mpfr依赖于gmp,mpc依赖于gmp和mpfr,因此,需要依次安装m4、gmp、mpfr、mpc、isl、gcc.
download_prerequisites
脚本可以自动下载安装除m4之外的依赖,所以需要手动安装m4,直接下最新版
首先创建个人bin文件夹的根目录~/.local
,并修改环境变量文件使其生效,加入以下内容
# mkdir ~/.local
export PREFIX_PATH=$HOME/.local
export PATH=$HOME/.local:$PATH
export LD_LIBRARY_PATH=$HOME/.local:$LD_LIBRARY_PATH
# source ~/.bashrc || source ~/.bash_profile
将m4及gcc包包上传至主机software目录
scp -P 22 -r m4-1.4.17.tar.gz root@192.168.72.129:~/software
scp -P 22 -r gcc-12.4.0.tar.gz root@192.168.72.129:~/software
gcc-12是使用gcc-11编译的,下载gcc-11
scp -P 22 -r gcc-11.2.0.tar.gz root@192.168.72.129:~/software
进入software目录解压所需文件
cd software
tar -zvxf m4-1.4.17.tar.gz -C .
tar -zvxf gcc-12.4.0.tar.gz -C .
tar -zvxf gcc-11.2.0.tar.gz -C .
安装m4
cd m4-1.4.17
./configure --prefix=$PREFIX_PATH
make
make install
再追加环境变量
set cpp environment
export CPP_4_HOME=/usr
export CPP_11_HOME=$HOME/.local/software/gcc11
export CPP_12_HOME=$HOME/.local/software/gcc12
# 默认CPP为CPP4
CPP_HOME=$CPP_4_HOME
export PATH=$CPP_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CPP_HOME/lib:$LD_LIBRARY_PATH
# Switch CPP
cpp() {
if [ -z "$1" ]; then
echo "Usage: cpp <cpp_number>"
echo "Available JDKs:"
echo "4. CPP_4_HOME"
echo "11. CPP_11_HOME"
echo "12. CPP_12_HOME"
return
fi
case "$1" in
4)
export CPP_HOME=$CPP_4_HOME
;;
11)
export CPP_HOME=$CPP_11_HOME
;;
12)
export CPP_HOME=$CPP_12_HOME
;;
*)
echo "Invalid CPP number"
return
;;
esac
export PATH=$CPP_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CPP_HOME/lib:$LD_LIBRARY_PATH
echo "Switched to CPP$1"
}
# alias命令动态切换CPP版本
alias cpp4="cpp 4"
alias cpp11="cpp 11"
alias cpp12="cpp 12"
安装gcc11(make编译过程4h+,编译文件目录大小6~7G)
cd gcc-11.2.0
./contrib/download_prerequisites
mkdir build && cd build
../configure --prefix=$CPP_11_HOME -enable-languages=c,c++ -disable-multilib
make
make install
安装gcc12(make编译过程4h+,编译文件目录大小6~7G)
cpp11
cd gcc-12.4.0
./contrib/download_prerequisites
mkdir build && cd build
../configure --prefix=$CPP_12_HOME -enable-languages=c,c++ -disable-multilib
make
make install
检验安装
cpp4
gcc -v
which gcc
cpp11
gcc -v
cpp12
gcc -v
3.编译xgrammar
创建python环境,用于安装vllm-cpu,3.11和3.12编译过程中不兼容,所以使用3.10
conda env remove -n cpu_vllm_3_10
conda create --name cpu_vllm_3_10 -c conda-forge \
"cmake>=3.18" \
git \
wheel \
packaging \
ninja \
numpy \
"setuptools-scm>=8" \
python=3.10
conda activate cpu_vllm_3_10
pip install --upgrade pip
# 安装编译所需工具
python3 -m pip install ninja pybind11 torch -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install pre-commit -i https://pypi.tuna.tsinghua.edu.cn/simple/
# 1.下载https://github.com/mlc-ai/xgrammar.git
# 使用镜像加速地址前缀:https://gitclone.com
git clone --recursive https://gitclone.com/github.com/mlc-ai/xgrammar.git && cd xgrammar
# 2.Install pre-commit hooks
pre-commit install
# 3. build XGrammar core and Python bindings(这里需要CXX-17)
mkdir build && cd build
cpp11 # 切换gcc
cmake .. -G Ninja
ninja
# 4. install the Python package
cd ../python
python3 -m pip install . -i https://pypi.tuna.tsinghua.edu.cn/simple/
# 5. (optional) add the python directory to PATH
echo "export PATH=\"$(pwd):\$PATH\"" >> ~/.bashrc
4.GitHub加速
# 方法一
# 设置镜像地址
git config --global url."https://gitclone.com/".insteadOf https://
# 取消镜像设置
git config --global --unset url."https://gitclone.com/".insteadOf
# 方法二
# 设置代理
git config --global http.proxy http://192.168.31.96:7890
git config --global https.proxy http://192.168.31.96:7890
# 取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy
5.CPU-vLLM
使用CPU推理,需要重新编译打包vLLM源代码!
参考官网
conda activate cpu_vllm_3_10
# 克隆项目
git clone https://gitclone.com/github.com/vllm-project/vllm.git vllm-project && cd vllm-project
# 交替使用如下两条命令,至所有cpu.txt依赖安装完成
pip install -v -r requirements-cpu.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install -v -r requirements-cpu.txt -i https://download.pytorch.org/whl/cpu
# 切换gcc12
cpp12
# 在当前目录下所有叫CMakeLists.txt的文件第一行前加入如下两行,确定编译时的架构
find . -name "CMakeLists.txt" -exec sed -i '1i\
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64")\
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=x86-64")' {} +
# 安装到py3.10
VLLM_TARGET_DEVICE=cpu python3 setup.py install
在只有8核的CPU上部署模型,只分配前六个核桃给vllm,以保证系统其他程序能正常运行,当前机器CPU可用下标为(07),前六个即(05)。
假设模型支持上下文对话总长128k,那么支撑多个会话就需要成倍扩展键值对缓存空间,如8G=64*128k。
VLLM_CPU_KVCACHE_SPACE=8 VLLM_CPU_OMP_THREADS_BIND=0-5 \ vllm serve /root/Qwen2.5-7B-Instruct \ --dtype bfloat16 \ --port 8000 \ --load-format 'auto' \ --device cpu \ --served-model-name qw2 \ --trust-remote-code
五、其他
1.问题解答
(1.1)Instruct和Chat模型的区别
基础模型
基础模型是经过大规模文本数据训练的通用语言模型,具有广泛的语言理解和生成能力。它们没有特定的任务优化,因此在需要处理特定任务时,性能可能不如专门训练的模型。
Instruct模型
Instruct模型是专门为执行指令(instructions)优化的模型。它们通过训练来理解和执行用户给定的明确指令。这类模型的训练数据通常包括大量的指令-响应对,使它们能够更好地理解和执行各种任务。例如:
- 回答具体问题
- 提供步骤说明
- 执行特定命令(如文本总结、翻译等)
Instruct模型的目标是提高模型在遵循和执行用户指令时的准确性和一致性。
Chat模型
Chat模型是为对话和交互优化的模型。它们不仅要理解单个指令,还要能够进行多轮对话,保持上下文和连贯性。这类模型的训练数据通常包括大量的对话示例,使它们能够更自然和人性化地与用户互动。例如:
- 参与日常对话
- 处理连续提问
- 维护对话上下文
Chat模型的目标是提高模型在与用户进行自然语言对话时的流畅性和连贯性。
(1.2)GPU专用和共享内存训练模型的可行性
windows任务管理器>性能>GPU,该界面有12G专用GPU内存和15G共享GPU内存,我可以用它训练需要24G基础条件的模型吗?
在训练需要24GB GPU内存的模型时,12GB专用GPU内存和15GB共享GPU内存可能会遇到一些挑战。专用GPU内存(Dedicated GPU memory)是显卡自身的高速显存,而共享GPU内存(Shared GPU memory)则是系统内存的一部分,速度相对较慢,并且可能会影响系统的整体性能。
以下是一些建议,帮助你确定是否可以进行训练:
- 模型优化:尝试优化模型以减少内存占用。例如,通过降低模型的参数数量、使用混合精度训练(FP16)、减少批次大小(batch size)等方式来降低显存需求。
- 分布式训练:如果你有多张显卡,可以考虑使用分布式训练将模型的负载分摊到多张显卡上。
- 梯度累积:通过梯度累积来模拟更大的批次大小,而不需要额外的显存。
- 检查GPU内存使用情况:使用工具(如NVIDIA的
nvidia-smi
)监控GPU内存使用情况,确保没有其他程序占用大量显存。- 使用虚拟内存(Swap):虽然GPU的虚拟内存速度较慢,但在必要时可以作为一种应急措施使用。然而,这会显著降低训练速度。
- 云服务:如果本地硬件无法满足需求,可以考虑使用云服务(如AWS、Google Cloud、Azure等)租用高性能的GPU实例进行训练。
总之,如果模型训练严格需要24GB专用GPU内存,你可能需要配置一张具有24GB或更大显存的GPU,或者考虑上述优化策略来减小显存需求。使用共享GPU内存虽然在一定程度上可以缓解显存不足的问题,但效果有限,且会影响训练性能。
2.有用的东西
(2.1)GPU多卡训练
文章参考自(传送门)
2.1.1.torchrun简介
torchrun是PyTorch库中用于启动分布式训练的命令行工具,特别是在使用PyTorch Distributed Package时。它简化了分布式训练的启动过程,自动处理了如初始化进程群、设置环境变量等复杂步骤,使得在多GPU或者多节点环境下的分布式训练变得更加便捷
2.1.2.torchrun主要用途
多GPU训练:在单机多GPU环境下执行分布式训练。
多节点训练:在多台机器的多GPU环境下执行分布式训练,通过网络连接。
自动环境配置:自动设置MASTER_ADDR和MASTER_PORT等环境变量,用于进程间的通信。
进程管理:根据指定的后端(如NCCL、Gloo)和运行策略(如单进程多线程、多进程)来启动和管理训练进程。
2.1.3.torchrun基本用法
torchrun [OPTIONS] SCRIPT [SCRIPT_ARGS]
-n, --nproc_per_node: 指定每个节点上的GPU数量或进程数。
--nnodes: 指定参与训练的节点总数。
--node_rank: 当在多个节点上运行时,指定当前节点的排名。
--master_addr: 指定主节点的IP地址。
--master_port: 指定主节点的端口号。
--no_spawn: 在单节点上不使用多进程,直接运行,主要用于调试。
假设你有一个训练脚本train.py
,想要在本地机器的4个GPU上进行分布式训练,可以使用以下命令
torchrun --nproc_per_node=4 train.py
如果是在多节点环境下,比如有两个节点,每个节点有4个GPU,你可能需要在每个节点上分别执行(假设主节点IP为192.168.1.100,从节点IP为192.168.1.100或其他任意相同子网内可达的主机;使用时需要确保网络配置允许节点间的通信,并且所有节点上都安装了PyTorch和必要的依赖。 ):
# 主节点执行
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=0 --master_addr=192.168.1.100 --master_port=29500 train.py
# 从节点执行
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=1 --master_addr=192.168.1.100 --master_port=29500 train.py
(2.2)私有模型部署到Ollama并推理测试(Win10)
2.2.1.llama.cpp启用CUDA
- Path=C:\Program Files\cpp_env\cmake-3.31.1-windows-x86_64\bin
在cmake的bin目录下双击打开cmake-gui.exe
选择sourceCode的目录为llama.cpp目录,点击左下角Configure,等待下边的Entry列表加载出来,如果所有选项没有红色报错,且有LLAMA_CUDA选项,则勾选LLAMA_CUDA。确保电脑上安装了CUDA Toolkit 12.1 Downloads,请使用
nvcc -V
检测,然后点击Generate。(如果出现下图报错信息,请参照
有用的东西>(5)MSYS2装mingw-c++11
并完成其中步骤)
明明nvcc -V有值,但cmake仍旧报错:
No CUDA toolset found.
解决方案:重装CUDA Tool Kit
,选择自定义(高级),勾选visual_studio_integration。因为微软在CUDA 工具包安装时,并未考虑第三方构建工具(如Visual Studio 2022)拓展的安装。
移动C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.6\extras\visual_studio_integration\MSBuildExtensions\
下的所有文件到 C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\BuildCustomizations\
目录中
重新点击Configure和Generate,保证Entry都没爆红,则使用Visual Studio 2022打开llama.cpp工程目录
如在Visual Studio 2022中有CmakeList等错误,请使用它自带的一键修复。
在右上角解决方案资源管理器
中搜索“.sln”,找到“llama.cpp.sln“后双击
双击sln后看到”All Build“选项,右单击鼠标并选择”生成“
等待编译完成
成功获得quantize.exe
2.2.2.转换Qwen模型到gguf
进入llama.cpp目录,初始化依赖
conda create --name llama_cpp_3_11 python=3.11
activate llama_cpp_3_11
pip install -r .\requirements.txt
如果是lora微调后的模型,使用convert_lora_to_gguf.py
# pip install -r .\requirements\requirements-convert_lora_to_gguf.txt
# .\requirements.txt已包含该子依赖安装
python ./convert_lora_to_gguf.py --base "C:\Users\admin\Desktop\ai_material\Qwen2___5-7B-Instruct" "C:\Users\admin\Desktop\ai_material\Qwen2___5-7B-Instruct_V1.0"
2.2.3.量化
进入llama.cpp目录,使用4Bit量化
千问模型和llama2模型不同的是千问模型没有tokenizer.model,但提供了tokenizer json和vocab.json,在转为GGUF格式的指令上要加上
--vocab-type bpe
相关参数
user-cmd># C:\Users\admin\Desktop\ai_material\llama.cpp-master\bin\Debug\llama-quantize.exe C:\Users\admin\Desktop\ai_material\Qwen2___5-7B-Instruct_V1.0\qwen-v1-f16.gguf C:\Users\admin\Desktop\ai_material\Qwen2___5-7B-Instruct_V1.0\qwen-v1-Q4_0.gguf Q4_0
user-cmd>#
2.2.4.部署ollama
创建C:\Users\admin\Desktop\ai_material\Qwen2___5-7B-Instruct_V1.0\Modelfile.txt
FROM C:\Users\admin\Desktop\ai_material\Qwen2___5-7B-Instruct_V1.0\qwen-v1-Q4_0.gguf
# set the temperature to 1 [higher is more creative, lower is more coherent]
PARAMETER temperature 0.7
PARAMETER top_p 0.8
PARAMETER repeat_penalty 1.05
PARAMETER top_k 20
TEMPLATE """{{ if and .First .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}<|im_start|>user
{{ .Prompt }}<|im_end|>
<|im_start|>assistant
{{ .Response }}"""
# set the system message
SYSTEM """
You are a helpful assistant.
"""
创建并运行模型:
ollama create my_qwen -f .\Modelfile.txt
ollama run my_qwen
2.2.5.Ollama推理服务测试
generate补全
curl http://123.123.123.123:11434/api/generate -d '{ "model": "my_qwen", "prompt":"who are you?","stream":false}'
chat对话
curl http://123.123.123.123:11434/api/chat -d '{ "model": "my_qwen", "messages": [ { "role": "user", "content": "why is the sky blue?" } ],"stream":false}'
(2.3)设置运行python使用GPU
文章参考自(传送门)
CUDA_VISIBLE_DEVICES=1 // 仅第二块显卡对当前环境可见
CUDA_VISIBLE_DEVICES=0,1 // 仅第一第二块显卡对当前环境可见
CUDA_VISIBLE_DEVICES=“0,1” // 仅第一第二块显卡对当前环境可见
CUDA_VISIBLE_DEVICES=0,2,3 // 仅第一第三第四块显卡对当前环境可见
如下三种方式任选其一:
运行方式:
# linux
CUDA_VISIBLE_DEVICES=1 python run_file.py
# win
python run_file.py
2.3.1.Windows
在cmd里执行(临时、静态)
set CUDA_VISIBLE_DEVICES=0
或(永久、静态)
# 加入环境变量>系统变量
CUDA_VISIBLE_DEVICES 0,1
2.3.2.Linux
在terminal里执行(临时、静态)
export CUDA_VISIBLE_DEVICES=0
或(永久、静态)
# 加入环境变量文件~/.bashrc的尾部
export CUDA_VISIBLE_DEVICES=1,2,3
# 执行重新编译环境变量指令:source ~/.bashrc
2.3.3.Python
import os
# 仅设置一块可见
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# 设置多块可见
# os.environ['CUDA_VISIBLE_DEVICES'] = '0,2,3'
# 业务逻辑...
或
import torch
import torch.nn as nn
# 假设你有一个模型类MyModel
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
# 模型初始化代码
def forward(self, x):
# 前向传播的代码
pass
# 创建模型实例
model = MyModel()
# way_1
model=torch.nn.DataParallel(model,device_ids=[2,3])
# way_2
'''
使用的GPU一定是编号连续的
其中model是需要运行的模型,device_ids指定部署模型的显卡,数据类型是list/device。
device_ids中的第一个GPU(即device_ids[0])和model.cuda()或torch.cuda.set_device()中的第一个GPU序号应保持一致,否则会报错
举例:
'''
device_ids = [0, 1]
torch.nn.DataParallel(model, device_ids=device_ids)
torch.nn.DataParallel(modul, device_ids=[x1,x2,x3,x4,x5,x6,x7])
torch.nn.DataParallel(model,device_ids = range(torch.cuda.device_count()) )
# way_3
#确定GPU的个数
count = torch.cuda.device_count()
#决策使用哪个设备,如第一块GPU或CPU之间
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
# way_4
device = torch.device("cuda:0")
model.to(device)
(2.4)Transformers之Pipeline
开箱即用
文章系列(传送门)
pipeline(管道)是huggingface transformers库中一种极简方式使用大模型推理的抽象,将所有大模型分为音频(Audio)、计算机视觉(Computer vision)、自然语言处理(NLP)、多模态(Multimodal)等4大类,28小类任务(tasks)。共计覆盖32万个模型
2.4.1.前言之Pipeline参数
【2.4.1.1】pipeline对象实例化参数
model(PreTrainedModel或TFPreTrainedModel)— 管道将使用其进行预测的模型。 对于 PyTorch,这需要从PreTrainedModel继承;对于 TensorFlow,这需要从TFPreTrainedModel继承。
tokenizer ( PreTrainedTokenizer ) — 管道将使用其对模型的数据进行编码的 tokenizer。此对象继承自 PreTrainedTokenizer。
image_processor ( BaseImageProcessor ) — 管道将使用的图像处理器来为模型编码数据。此对象继承自 BaseImageProcessor。
modelcard(str或ModelCard,可选) — 属于此管道模型的模型卡。
framework(str,可选)— 要使用的框架,"pt"适用于 PyTorch 或"tf"TensorFlow。必须安装指定的框架。如果未指定框架,则默认为当前安装的框架。如果未指定框架且安装了两个框架,则默认为 的框架model,如果未提供模型,则默认为 PyTorch。
task(str,默认为"")— 管道的任务标识符。
num_workers(int,可选,默认为 8)— 当管道将使用DataLoader(传递数据集时,在 Pytorch 模型的 GPU 上)时,要使用的工作者数量。
batch_size(int,可选,默认为 1)— 当管道将使用DataLoader(传递数据集时,在 Pytorch 模型的 GPU 上)时,要使用的批次的大小,对于推理来说,这并不总是有益的,请阅读使用管道进行批处理。
args_parser(ArgumentHandler,可选) - 引用负责解析提供的管道参数的对象。
device(int,可选,默认为 -1)— CPU/GPU 支持的设备序号。将其设置为 -1 将利用 CPU,设置为正数将在关联的 CUDA 设备 ID 上运行模型。您可以传递本机torch.device或str太
torch_dtype(str或torch.dtype,可选) - 直接发送model_kwargs(只是一种更简单的快捷方式)以使用此模型的可用精度(torch.float16,,torch.bfloat16…或"auto")
binary_output(bool,可选,默认为False)——标志指示管道的输出是否应以序列化格式(即 pickle)或原始输出数据(例如文本)进行。
【2.4.1.2】pipeline对象使用参数
inputs(str、或List[str])——管道处理三种类型的图像:PIL.ImageList[PIL.Image]
包含指向图像的 HTTP(s) 链接的字符串
包含图像本地路径的字符串
直接在 PIL 中加载的图像
该管道可以接受单个图像或一批图像。
max_new_tokens(int,可选)— 生成的最大令牌数量。默认情况下将使用generate默认值。
generate_kwargs(Dict,可选)——传递它以将所有这些参数直接发送到generate允许完全控制此函数。
2.4.2.图片转文本
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
os.environ["CUDA_VISIBLE_DEVICES"] = "2"
from transformers import pipeline
image_to_text = pipeline("image-to-text", model="nlpconnect/vit-gpt2-image-captioning")
output=image_to_text("./parrots.png")
print(output)
2.4.3.文生图
下载依赖
pip install diffusers invisible_watermark transformers accelerate safetensors -i https://mirrors.cloud.tencent.com/pypi/simple
SD XL1.0
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
from diffusers import DiffusionPipeline
import torch
base = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16, use_safetensors=True, variant="fp16")
base.to("cuda")
refiner = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-refiner-1.0",
text_encoder_2=base.text_encoder_2,
vae=base.vae,
torch_dtype=torch.float16,
use_safetensors=True,
variant="fp16",
)
refiner.to("cuda")
# Define how many steps and what % of steps to be run on each experts (80/20) here
n_steps = 40
high_noise_frac = 0.8
prompt = "A beautiful sexy girl"
# run both experts
image = base(
prompt=prompt,
num_inference_steps=n_steps,
denoising_end=high_noise_frac,
output_type="latent",
).images
image = refiner(
prompt=prompt,
num_inference_steps=n_steps,
denoising_start=high_noise_frac,
image=image,
).images[0]
image.save("base+refiner.png")
效果
Stable Diffusion XL2.0
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
from diffusers import DiffusionPipeline
import torch
#pipe = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16, use_safetensors=True, variant="fp16")
pipe = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-2", torch_dtype=torch.float16, use_safetensors=True, variant="fp16")
pipe.to("cuda")
prompt = "a beautiful sexy girl"
image = pipe(prompt).images[0]
image.save("sd-xl.png")
效果
(2.5)MSYS2装mingw-c++11(Win10)
官网下载:https://www.msys2.org/
运行msys2-x86_64-20241116.exe,一路点击next
此时msys2安装目录为:C:\msys64\
windows用户目录为:C:\Users\admin\
(安装之后有好几个终端环境,推荐使用的、默认打开的是
ucrt64.exe
)
由于msys2也创建了个用户目录C:\msys64\home\admin
,现在把它切回C:\Users\admin\
- 配置文件
C:\msys64\etc\nsswitch.conf
- 修改db_home为:
db_home: windows cygwin desc
修改环境变量并生效:~/.bashrc
按向上方向键时显示之前输入过的历史命令,且相同的命令不重复显示,追加如下两行:
export PROMPT_COMMAND='history -a'
export HISTCONTROL=ignoredups
使编译的C++程序运行打印中文时不会乱码,追加一行:
chcp.com 65001
追加默认系统工具到环境变量
export PATH=/mingw64/bin:$PATH
将修改好的配置文件移动到正确的目录,上边db_home已经改了环境变量的检索目录了
- 把安装位置C:\msys64\home\admin\下的全部配置文件拷贝到C:\Users\admin\下,比如.bashrc等
- 修改后重新打开
ucrt64.exe
终端生效,可pwd查看路径
msys2的终端环境中默认无法使用win环境变量的PATH, 有两种方式可以使用
- 方法1:在win环境变量中添加一个新变量
MSYS2_PATH_TYPE
,值为inherit
(Recommend)- 方法2:通过安装位置的
msys2_shell.cmd
启动,并添加-full-path
参数
添加ucrt64.exe到系统右键菜单(新建一个test.reg
文件,输入如下内容后双击运行)
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Background\shell\OpenMsys2Here]
@="open msys2/ucrt64 here"
"Icon"="C:\\msys64\\ucrt64.ico"
[HKEY_CLASSES_ROOT\Directory\Background\shell\OpenMsys2Here\command]
@="C:\\msys64\\msys2_shell.cmd -defterm -here -no-start -ucrt64"
软件包的安装与卸载
一般的安装命令:
pacman -S 软件包名
进行安装,如tree
卸载命令:pacman -Rs 软件包名
如何知道完整包名:pacman -Ss 包名关键词
,比如vim
打开mingw版本下载页(传送门),下载mingw-w64-v11.0.0.zip
并解压,在解压后的目录下鼠标右键打开msys2/ucrt64,然后执行如下命令
# 更新软件包数据库
pacman -Syuu
# 下载所需依赖
pacman -S git make pkg-config
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-binutils mingw-w64-x86_64-crt
pacman -S mingw-w64-x86_64-headers mingw-w64-x86_64-winpthreads
在windows环境变量Path中添加C:\msys64\mingw64\bin
#cmd效果
C:\Users\admin>gcc --version
gcc (Rev2, Built by MSYS2 project) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
C:\Users\admin>
#msys2/ucrt64 performance
admin@DESKTOP-V0UBAHA UCRT64 ~/Desktop
$ gcc --version
gcc.exe (Rev2, Built by MSYS2 project) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
admin@DESKTOP-V0UBAHA UCRT64 ~/Desktop
(2.6)训练模型GPU资源估算
打开网页(传送门),输入模型及训练参数等信息
借鉴LLama-Factory官网
* estimated
Method | Bits | 7B | 13B | 30B | 70B | 110B | 8x7B | 8x22B |
---|---|---|---|---|---|---|---|---|
Full | AMP | 120GB | 240GB | 600GB | 1200GB | 2000GB | 900GB | 2400GB |
Full | 16 | 60GB | 120GB | 300GB | 600GB | 900GB | 400GB | 1200GB |
Freeze | 16 | 20GB | 40GB | 80GB | 200GB | 360GB | 160GB | 400GB |
LoRA/GaLore/BAdam | 16 | 16GB | 32GB | 64GB | 160GB | 240GB | 120GB | 320GB |
QLoRA | 8 | 10GB | 20GB | 40GB | 80GB | 140GB | 60GB | 160GB |
QLoRA | 4 | 6GB | 12GB | 24GB | 48GB | 72GB | 30GB | 96GB |
QLoRA | 2 | 4GB | 8GB | 16GB | 24GB | 48GB | 18GB | 48GB |
3.搭建DNN实现数字识别
(3.1)制作MNIST图像标签映射数据集(例子)
“-labels.idx3-ubyte” 是一种用于存储图像数据与正确数值标签关系的文件格式,通常用于机器学习和深度学习领域的图像识别任务,如MNIST数据集。
要创建这样的文件,你需要遵循以下步骤:
- 准备你的图像数据:将你的图像转换为灰度(如果不是),并确保它们是同一尺寸。
- 将图像转换为字节数据:将每个像素值从0-255转换为字节(0-255)。
- 创建文件头:idx3文件有一个文件头,它包含一些关于文件的元数据。
- 将文件头和图像数据写入文件。
idx1-ubyte
表示这是一个一维数组的 ubyte 格式数据(标签)。
import numpy as np
import struct
def write_idx1_ubyte(images, labels, filename):
with open(filename, 'wb') as f:
# 写入魔数
f.write(struct.pack('>II', 0, images.shape[0]))
# 写入图像数据
for image in images:
f.write(struct.pack('>784B', *image.astype(np.uint8).flatten()))
# 写入标签数据
f.write(struct.pack('>II', 0, labels.shape[0]))
for label in labels:
f.write(struct.pack('>B', label))
# 假设你有一些图像和对应的标签,以下两行需要替换成实际的业务数据!
images = np.random.rand(100, 28, 28) # 示例图像数据,假设是100张28x28的图像
labels = np.random.randint(0, 10, size=(100)) # 对应的标签,左闭右开[0,10)的整数即0~9
# 调用函数生成"-labels.idx3-ubyte"文件
write_idx1_ubyte(images, labels, 'xxx-labels.idx1-ubyte')
压缩生成好的数据集文件到.gz
gizp train60k-labels-idx1-ubyte
gzip test10k-labels-idx1-ubyte
可以得到如下两个文件
train60k-labels-idx1-ubyte.gz
test10k-labels-idx1-ubyte.gz
解压xxx.gz到xxx的命令如下:
gizp -d train60k-labels-idx1-ubyte.gz
gizp -d test10k-labels-idx1-ubyte.gz
(3.2)制作MNIST图像数据集(例子)
“-images.idx3-ubyte” 是一种用于存储图像数据的文件格式,通常用于机器学习和深度学习领域的图像识别任务,如MNIST数据集。
要创建这样的文件,你需要遵循以下步骤:
- 准备你的图像数据:将你的图像转换为灰度(如果不是),并确保它们是同一尺寸。
- 将图像转换为字节数据:将每个像素值从0-255转换为字节(0-255)。
- 创建文件头:idx3文件有一个文件头,它包含一些关于文件的元数据。
- 将文件头和图像数据写入文件。
idx3-ubyte
表示这是一个三维数组的 ubyte (unsigned byte) 格式数据(图片数据)。
import numpy as np
import struct
def create_idx3_image_file(images, filename):
# 计算图像数量和尺寸
num_images = len(images)
height = images[0].shape[0]
width = images[0].shape[1]
# 打开文件进行二进制写入
with open(filename, 'wb') as f:
# 写入文件头
magic_number = 0x0803 # 2051
image_count = num_images
row_count = height
col_count = width
f.write(struct.pack('>IIII', magic_number, image_count, row_count, col_count))
# 写入图像数据
for image in images:
image_data = image.tobytes()
f.write(image_data)
# 示例:创建一个包含单个图像的idx3文件,以下两行需要替换成实际的业务数据!
image = np.random.randint(0, 256, size=(28, 28), dtype=np.uint8) # 创建一个28x28的灰度图像
create_idx3_image_file([image], 'xxx-images.idx3-ubyte')
压缩生成好的数据集文件到.gz
gizp train60k-images-idx3-ubyte
gizp test10k-images-idx3-ubyte
可以得到如下两个文件
train60k-images-idx3-ubyte.gz
test10k-images-idx3-ubyte.gz
解压xxx.gz到xxx的命令如下:
gizp -d train60k-images-idx3-ubyte.gz
gizp -d test10k-images-idx3-ubyte.gz
xxx-images-idx3-ubyte
文件拆解
文件头
- 前 4 个字节表示魔数
0x00000803
(以小端字节序存储),表示 3 维数组的 ubyte 格式。- 接下来 4 个字节表示图像数量
60000
。- 再接下来 4 个字节表示行数
28
。- 最后 4 个字节表示列数
28
。图像数据
- 文件头之后的数据部分就是所有图像的像素值。
- 每个图像的像素值是按照行优先的顺序依次存储的。
- 每个像素值都是一个 8 位无符号整数(0-255)。
import struct import numpy as np import matplotlib.pyplot as plt def load_mnist_images(filename): """ 读取 MNIST 图像文件并返回图像数据 """ with open(filename, 'rb') as f: # 读取文件头信息 magic, num_images, rows, cols = struct.unpack('>IIII', f.read(16)) print(f'文件头信息(10进制):{[magic,num_images,rows,cols]}') # 读取并解析图像数据 images = np.fromfile(f, dtype=np.uint8).reshape(num_images, rows, cols) return images if __name__ == '__main__': # 假设文件位于当前目录下 filename = r'.\train-images-idx3-ubyte' # 查看一张图片, 设置 NumPy 数组的打印格式 train_images = load_mnist_images(filename) np.set_printoptions(linewidth=200, formatter={'int':'{:3d} '.format}) print(train_images[0]) plt.imshow(train_images[0], cmap='gray') plt.show() # 原文链接:https://blog.csdn.net/shidashuai/article/details/142468023
打印结果:略
(3.3)数据集转换csv
#!/usr/bin/python
# -*- coding: utf-8 -*-
def convert(imgFile, labelFile, outFile, imgSetSize): # 转换数据格式
f = open(imgFile, "rb")
o = open(outFile, "w")
l = open(labelFile, "rb")
f.read(16) # 逐个字节或字符读取文件中的内容
l.read(8)
images = []
for i in range(imgSetSize): # 需要转换的数据对象个数为imgSetSize
image = [ord((l.read(1)))] # ord()函数是chr()函数或unichr()函数的配对函数
# chr()函数: 对于8位的ASCII字符串
# unichr()函数: 对于Unicode对象
for j in range(28 * 28): # 由于每个图片的像素是28*28,按这个尺寸还原每一幅图,但张量(数组)会被转换成1*728的格式
image.append(ord(f.read(1)))
images.append(image)
for image in images:
o.write(",".join(str(pix) for pix in image) + "\n")
f.close()
o.close()
l.close()
if __name__ == '__main__':
# 训练集有6w幅图
convert('train-images-idx3-ubyte', 'train-labels-idx1-ubyte', 'mnist_train.csv', 60000)
# 验证集有1w幅图
convert('t10k-images-idx3-ubyte', 't10k-labels-idx1-ubyte', 'mnist_test.csv', 10000)
print("Task accomplished!")
(3.4)训练模型及回归测试
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
sys.path.append("..")
import numpy as np
import scipy.special
import matplotlib.pyplot as plt
import time
class NeuralNetwork():
# 初始化神经网络
def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
# 设置输入层节点,隐藏层节点和输出层节点的数量和学习率
self.inodes = inputnodes
self.hnodes = hiddennodes
self.onodes = outputnodes
self.lr = learningrate # 设置神经网络中的学习率
# 使用正态分布,进行权重矩阵的初始化
'''
np.random.normal第一个参数【loc】: 分布的均值(中心/对称轴x坐标设置)
np.random.normal第一个参数【scale】: 分布的标准差(宽度)
np.random.normal第一个参数【size】: 输出值的维度。如果给定的维度为(m, n, k),那么就从分布中抽取m * n * k个样本。如果size为None(默认值)并且loc和scale均为标量,那么就会返回一个值。否则会返回np.broadcast(loc, scale).size个值
np.random.normal输出【out】: n维数组或标量(从含参的正态分布中抽取的随机样本)
'''
self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))
self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))
self.activation_function = lambda x: scipy.special.expit(x) # 激活函数设为Sigmod()函数
pass
# 定义训练神经网络
print("************Train start******************")
def train(self, input_list, target_list):
# # 转换输入/输出列表到二维数组,在外层多套(ndmin-1)层中括号,numpy dimension min,然后对矩阵进行转置
inputs = np.array(input_list, ndmin=2).T
targets = np.array(target_list, ndmin=2).T
# (矩阵乘法)
hidden_inputs = np.dot(self.wih, inputs) # 计算到隐藏层的信号,dot()返回的是两个数组的点积
hidden_outputs = self.activation_function(hidden_inputs) # 计算隐藏层输出的信号
final_inputs = np.dot(self.who, hidden_outputs) # 计算到输出层的信号
final_outputs = self.activation_function(final_inputs)
output_errors = targets - final_outputs # 计算输出值与标签值的差值
# print("*****************************")
# print("output_errors:",output_errors)
hidden_errors = np.dot(self.who.T, output_errors)
'''
np.transpose函数
使用 numpy.transpose ()进行变换,其实就是交换了坐标轴,如:x.transpose(1, 2, 0),其实就是将x第二维度挪到第一维上,第三维移到第二维上,原本的第一维移动到第三维上
举例:
x = np.arange(12).reshape((2,3,2))
x[0][0][0] = 0 x[1][0][0] = 6
x[0][0][1] = 1 x[1][0][1] = 7
x[0][1][0] = 2 x[1][1][0] = 8
x[0][1][1] = 3 x[1][1][1] = 9
x[0][2][0] = 4 x[1][2][0] = 10
x[0][2][1] = 5 x[1][2][1] = 11
转换后,shape = (3,2,2)
x[0][0][0] = 0 x[0][0][1] = 6
x[0][1][0] = 1 x[0][1][1] = 7
x[1][0][0] = 2 x[1][0][1] = 8
x[1][1][0] = 3 x[1][1][1] = 9
x[2][0][0] = 4 x[2][0][1] = 10
x[2][1][0] = 5 x[2][1][1] = 11
'''
# 隐藏层和输出层权重更新
self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)),
np.transpose(hidden_outputs))
# 输入层和隐藏层权重更新
self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),
np.transpose(inputs))
pass
# 查询神经网络
def query(self, input_list): # 转换输入列表到二维数
inputs = np.array(input_list, ndmin=2).T # 计算到隐藏层的信号
hidden_inputs = np.dot(self.wih, inputs) # 计算隐藏层输出的信号
hidden_outputs = self.activation_function(hidden_inputs) # 计算到输出层的信号
final_inputs = np.dot(self.who, hidden_outputs) # numpy.dot() 矩阵乘法
final_outputs = self.activation_function(final_inputs)
return final_outputs
if __name__ == '__main__':
"""
本次实验使用28 * 28 pixel大小的图片作为训练集和测试集,用于区分手写数字0~9。
对于神经网络的搭建,需要:输入层、隐藏层和输出层。
其中输入层张量宽度,可认为是一维,则有28*28=784个感知点。隐藏层数量随意。输出层设为10个。
"""
# 定义神经网络训练模型,使用训练集对模型进行epochs次训练,不断调整权重
input_nodes = 784 # 输入层神经元个数
hidden_nodes = 100 # 隐藏层神经元个数
output_nodes = 10 # 输出层神经元个数
learning_rate = 0.3 # 学习率为0.3
# 创建神经网络
n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
# 读取训练数据集 转化为列表
training_data_file = open("source/mnist_train.csv", 'r')
training_data_list = training_data_file.readlines(); # 方法用于读取所有行,并返回列表
# print("training_data_list:",training_data_list)
training_data_file.close()
train_start = time.perf_counter() # 返回系统运行时间
a = 0
# 训练次数
epochs = 5
for e in range(epochs):
# 训练神经网络
for record in training_data_list:
if a == 10000: # 只训练其中一万张图
break
all_values = record.split(',') # 根据逗号,将文本数据进行拆分
if a == 1 or a == 2: # 绘制数字
image_arr = np.asfarray(all_values[1:]).reshape(28, 28)
plt.imshow(image_arr, cmap='Greys', interpolation='None')
plt.show()
# 将文本字符串转化为实数,并创建这些数字的数组。
inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
# 创建用零填充的数组,数组的长度为output_nodes,加0.01解决了0输入造成的问题(避免归一化取整数位为0时丢失小数位的问题)
targets = np.zeros(output_nodes) + 0.01
# 使用目标标签,将正确元素设置为0.99
targets[int(all_values[0])] = 0.99
n.train(inputs, targets)
pass
a += 1
pass
train_end = time.perf_counter()
print('train over, 用时:{:.4f}s'.format(train_end - train_start))
# 模型测试,如果期望值和输出的值相同,就往score[]数组里面加1,否则加0,进而算准确率。
test_data_file = open("source/mnist_test.csv", 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
all_values = test_data_list[2].split(',')
test_start = time.perf_counter()
score = []
print("***************Test start!**********************")
for record in test_data_list:
# 用逗号分割将数据进行拆分
all_values = record.split(',')
# 正确的答案是第一个值
correct_values = int(all_values[0])
# print(correct_values, "是正确的期望值")
# 做输入
inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
# 测试网络 作输入
outputs = n.query(inputs)
# 找出输出的最大值的索引
label = np.argmax(outputs)
# print(label, "是网络的输出值\n")
# 如果期望值和网络的输出值正确 则往score 数组里面加1 否则添加0
if (label == correct_values):
score.append(1)
else:
score.append(0)
pass
pass
test_end = time.perf_counter()
print('test over, 用时:{:.4f}s'.format(test_end - test_start))
# print(score)
score_array = np.asfarray(score)
print("正确率是:", (score_array.sum() / score_array.size) * 100, '%')
# 每秒实时监控内存变化: $ watch -n 1 free -m
# 输入jupyter notebook命令可以打开代理,浏览本地的ipynb文件
(3.5)模型训练进度暂存与再训练
1.需要导入
pickle
库来进行模型的保存和加载
import pickle
2.在
NeuralNetwork
类中添加一个保存模型的方法。
def save_model(self, file_name):
with open(file_name, 'wb') as f:
pickle.dump((self.wih, self.who), f)
print(f"Model saved to {file_name}")
3.在
NeuralNetwork
类中添加一个加载模型的方法。
def load_model(self, file_name):
with open(file_name, 'rb') as f:
self.wih, self.who = pickle.load(f)
print(f"Model loaded from {file_name}")
4.修改主程序,在
创建神经网络
的声明下添加一个重载对象的代码片
n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
# 检查是否存在预训练模型
model_file = 'mnist_model.pkl'
try:
n.load_model(model_file)
except FileNotFoundError:
print(f"No pretrained model found at {model_file}, training a new model.")
5.修改主程序,在训练结束的代码后添加保存模型的代码片
print('train over, 用时:{:.4f}s'.format(train_end - train_start))
# 保存模型
n.save_model(model_file)
4.解决window命令行中文乱码
步骤:按下Win + R 组合键,输入regedit,然后按回车键打开注册表编辑器,导航到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor,右键点击该项,选择“新建” > “字符串值”,命名为autorun,双击autorun,在数值数据中输入chcp 936 > nul,然后点击确定。
5.mysql的一些使用方式
mysql -h localhost -u root -p -P 3306
本地mysql
6.Gradio
Gradio是一个开源的Python库,用于快速构建机器学习和数据科学演示的应用。它可以帮助你快速创建一个简单漂亮的用户界面,以便向客户、合作者、用户或学生展示你的机器学习模型。此外,还可以通过自动共享链接快速部署模型,并获得对模型性能的反馈。在开发过程中,你可以使用内置的操作和解释工具来交互式地调试模型。Gradio适用于多种情况,包括为客户/合作者/用户/学生演示机器学习模型、快速部署模型并获得性能反馈、以及在开发过程中使用内置的操作和解释工具交互式地调试模型。
Gradio的优势在于易用性,代码结构简单,只需简单定义输入和输出接口即可快速构建简单的交互页面,更轻松部署模型。但Gradio适合场景相对简单,更容易快速部署应用的开发者。此外,Gradio还提供便利的分享功能,在启动应用时设置share=True参数创建外部分享链接,还可以直接在微信等平台上分享给用户使用。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gradio
6-1.Interface类以及基础模块
Gradio为了用户快速建立界面,包装了很多简洁易用的接口。在上面的demo中可以看到了一个简单易用易理解文本处理界面用了一个Interface函数来构建。但这个函数可以处理很多不同类型的输入和输出。 Interface一般使用三个参数进行初始化:
fn:包装要处理的函数
inputs:输入组件类型,例如:“text”、“image”、“video”等。
outputs:输出组件类型,例如:“text”、“image”、“video”等。
通过定义这三个参数,就可以快速创建一个接口并发布应用。
Gradio最常用的基础模块构成:
应用界面:gr.Interface(搭建简易场景),gr.Blocks(制作定制化场景)。
输入输出:gr.Image(图像),gr.Text(文本框),gr.Video(视频框), gr.DataFrame(数据框),gr.Dropdown(下拉选项),gr.Number(数字),gr.Markdown,gr.Files。
控制组件:gr.Button(按钮)。
布局组件:gr.Tab(标签页),gr.Row(行布局),gr.Column(列布局)等。
6-2.启动简单的Demo
Inputs和Outpust控件里面可以添加指定参数,像上面的demo一样,可以在gr.Text()里面定义label = “输入文字”,还可以指定行数,提示词等,每个控制可以定义的参数都有差异。
import gradio as gr
def greet(text):
return "你输入的是:" + text + "!"
#输入接口组件
input = gr.Text(label = "输入文字",lines=5, placeholder="请在这里输入...")
#输出接口组件,label:显示框的标签
output = gr.Text(label = "输出内容")
#fn设置处理函数
demo = gr.Interface(fn=greet, inputs=input, outputs=output)
demo.launch()
7.DeepSpeed Stage
1、数据并行(DP)
如果有三张卡,就会有三个模型(一个模型复制三份)
并行顺序:
①数据切分成三块
②模型复制三份
③每个模型对自己那部分数据前向传播
④得到的三份输出全都汇聚到GPU0(第一张卡)
⑤在GPU0上计算loss和梯度
⑥把梯度分三份分别给三个GPU
⑦每个GPU分别进行自己那部分的反向传播
⑧反向传播结果归到GPU0,由GPU0进行总计算,减小梯度,更新参数,再把更新后的模型重新复制三份到三个GPU。再次划分数据为三份。
优点:速度快
缺点:GPU0既要分发任务又要存放模型,容易OOM。模型大小受限于单卡容量(第一张卡显存的85%左右大小的模型是比较合适的),并且需要每张卡内都完整执行训练过程
2、模型并行
①层间并行(流水并行,pipeline parrallel,PP,把模型按层拆开,GPU0是第一层以此类推):当第一个batch的数据正向传播到最后一个GPU计算结束开始反向传播,传播到倒数第二个GPU上的时候,最后一个GPU开始计算第二个batch的反向传播值,以此类推。当第一个batch反向传播到第一个GPU的时候,第二个GPU上是第二个batch,第三个GPU上是第三个batch...
②层内并行(张量并行,tensor parrallel,TP,按照权重把模型的每层都拆开):相当于multihead的head分开。注意 head 数要能整除卡数。
3、混合并行
数据并行+层内+层间并行
假设一共有384张卡,每48张卡复制一个模型,那么数据可以分为8份【数据并行 n=8】
对每48张卡实现层内和层间并行:对multihead ➗ 4 ,那么此时模型每层需要 12 张卡 【层内并行 n=4】
对于层间并行就是执行流水线计算 【层间并行 n=12】
deepspeed的stage含义:
ZeRO-0:只数据并行
ZeRO-1:对优化器状态分片(显存降低4倍)
ZeRO-2:对优化器状态、梯度都进行分片(显存降低8倍)
ZeRO-3:对优化器状态、梯度、模型参数都进行分片(显存降低更多,但通信成本增加1.5倍)
zero-offload:内存扩展显存
zero-infinity:NVMe固态硬盘扩展
速度:0>1>2>2+offload>3>3+offload
GPU内存占用情况与速度相同(显存降得越多,速度越慢)
4、真正的分布式数据并行(Distributed Data Parallel,DDP):
①多个进程,每个进程都加载数据和模型(DP只有GPU0加载数据和模型,然后分发)
②各进程同时前向传播
③各进程分别计算loss,反向传播,计算梯度(不需要汇总到GPU0)
④各进程间通信,将梯度在各卡同步(此时每个卡梯度相同)
⑤各进程分别更新模型(不需要再从GPU0复制)
原文链接:https://blog.csdn.net/weixin_72100405/article/details/136502401
8.batchsize
LLama-Factory单机多卡并行训练时,需要去LLaMA-Factory/examples/accelerate/fsdp_config.yaml文件
line 19
设置GPU个数!(建议卡数为2的次方,和操作系统有关)num_processes: 2 # the number of GPUs in all nodes
batch设置自己的显存能够跑起来的,我的是40G显存,只能设最大为4,最后的batch_size实际上是卡数 * 每张卡的batch * 累计步数,所以想要大一点的batch_size,可以对应地把gradient_accumulation_steps调高一点。理论上来说,batchsize大一些训练得会稳定一些,但是相应的学习率也应该稍微调大一些
原文链接:https://blog.csdn.net/K_Kelly_/article/details/138528175
9.多卡训练
参考博客:https://zhuanlan.zhihu.com/p/9458713718
LLaMA-Factory支持单机多卡和多机多卡分布式训练。同时也支持 DDP , DeepSpeed 和 FSDP 三种分布式引擎
DDP (DistributedDataParallel) 通过实现模型并行和数据并行实现训练加速。 使用 DDP 的程序需要生成多个进程并且为每个进程创建一个 DDP 实例,他们之间通过 torch.distributed
库同步。
DeepSpeed 是微软开发的分布式训练引擎,并提供ZeRO(Zero Redundancy Optimizer)、offload、Sparse Attention、1 bit Adam、流水线并行等优化技术。 您可以根据任务需求与设备选择使用。
FSDP 通过全切片数据并行技术(Fully Sharded Data Parallel)来处理更多更大的模型。在 DDP 中,每张 GPU 都各自保留了一份完整的模型参数和优化器参数。而 FSDP 切分了模型参数、梯度与优化器参数,使得每张 GPU 只保留这些参数一部分。 除了并行技术之外,FSDP 还支持将模型参数卸载至CPU,从而进一步降低显存需求。
引擎 | 数据切分 | 模型切分 | 优化器切分 | 参数卸载 |
---|---|---|---|---|
DDP | 支持 | 不支持 | 不支持 | 不支持 |
DeepSpeed | 支持 | 支持 | 支持 | 支持 |
FSDP | 支持 | 支持 | 支持 | 支持 |
9-1.NativeDDP
NativeDDP 是 PyTorch 提供的一种分布式训练方式,您可以通过以下命令启动训练:
9-1-1.单机多卡
9-1-1-1.llamafactory-cli
您可以使用 llamafactory-cli 启动 NativeDDP 引擎。
FORCE_TORCHRUN**=1** llamafactory-cli train examples/train_full/llama3_full_sft_ds3.yaml
如果 CUDA_VISIBLE_DEVICES
没有指定,则默认使用所有GPU。如果需要指定GPU,例如第0、1个GPU,可以使用:
FORCE_TORCHRUN**=1** CUDA_VISIBLE_DEVICES**=0**,1 llamafactory-cli train config/config1.yaml
9-1-1-2.torchrun
您也可以使用 torchrun
指令启动 NativeDDP 引擎进行单机多卡训练。下面提供一个示例:
torchrun --standalone --nnodes=1 --nproc-per-node=8 src/train.py \
--stage sft \
--model_name_or_path meta-llama/Meta-Llama-3-8B-Instruct \
--do_train \ --dataset alpaca_en_demo \
--template llama3 \ --finetuning_type lora \
--output_dir saves/llama3-8b/lora/ \
--overwrite_cache \ --per_device_train_batch_size 1 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--logging_steps 100 \
--save_steps 500 \
--learning_rate 1e-4 \
--num_train_epochs 2.0 \
--plot_loss \
--bf16
9-1-1-3.accelerate
您还可以使用 accelerate
指令启动进行单机多卡训练。
首先运行以下命令,根据需求回答一系列问题后生成配置文件:
accelerate config
下面提供一个示例配置文件:
# accelerate_singleNode_config.yaml compute_environment: LOCAL_MACHINE debug: true distributed_type: MULTI_GPU downcast_bf16: ‘no’ enable_cpu_affinity: false gpu_ids: all machine_rank: 0 main_training_function: main mixed_precision: fp16 num_machines: 1 num_processes: 8 rdzv_backend: static same_network: true tpu_env: [] tpu_use_cluster: false tpu_use_sudo: false use_cpu: false
您可以通过运行以下指令开始训练:
accelerate launch \ --config_file accelerate_singleNode_config.yaml \ src/train.py training_config.yaml
9-1-2.多机多卡
9-1-2-1.llamafactory-cli
FORCE_TORCHRUN**=1** NNODES**=2** RANK**=0** MASTER_ADDR**=192**.168.0.1 MASTER_PORT**=29500** \ llamafactory-cli train examples/train_lora/llama3_lora_sft.yaml FORCE_TORCHRUN**=1** NNODES**=2** RANK**=1** MASTER_ADDR**=192**.168.0.1 MASTER_PORT**=29500** \ llamafactory-cli train examples/train_lora/llama3_lora_sft.yaml
变量名 | 介绍 |
---|---|
FORCE_TORCHRUN | 是否强制使用torchrun |
NNODES | 节点数量 |
RANK | 各个节点的rank。 |
MASTER_ADDR | 主节点的地址。 |
MASTER_PORT | 主节点的端口。 |
9-1-2-2.torchrun
您也可以使用 torchrun
指令启动 NativeDDP 引擎进行多机多卡训练。
torchrun --master_port 29500 --nproc_per_node=8 --nnodes=2 --node_rank=0 \
--master_addr=192.168.0.1 train.py torchrun --master_port 29500 --nproc_per_node=8 --nnodes=2 --node_rank=1 \
--master_addr=192.168.0.1 train.py
9-1-2-3.accelerate
您还可以使用 accelerate
指令启动进行多机多卡训练。
首先运行以下命令,根据需求回答一系列问题后生成配置文件:
accelerate config
下面提供一个示例配置文件:
# accelerate_multiNode_config.yaml compute_environment: LOCAL_MACHINE debug: true distributed_type: MULTI_GPU downcast_bf16: ‘no’ enable_cpu_affinity: false gpu_ids: all machine_rank: 0 main_process_ip: ‘192.168.0.1’ main_process_port: 29500 main_training_function: main mixed_precision: fp16 num_machines: 2 num_processes: 16 rdzv_backend: static same_network: true tpu_env: [] tpu_use_cluster: false tpu_use_sudo: false use_cpu: false
您可以通过运行以下指令开始训练:
accelerate launch \ --config_file accelerate_multiNode_config.yaml \ train.py llm_config.yaml
9-2.FSDP
PyTorch 的全切片数据并行技术 FSDP (Fully Sharded Data Parallel)能让我们处理更多更大的模型。LLaMA-Factory支持使用 FSDP 引擎进行分布式训练。
FSDP 的参数 ShardingStrategy 的不同取值决定了模型的划分方式:
- FULL_SHARD: 将模型参数、梯度和优化器状态都切分到不同的GPU上,类似ZeRO-3。
- SHARD_GRAD_OP: 将梯度、优化器状态切分到不同的GPU上,每个GPU仍各自保留一份完整的模型参数。类似ZeRO-2。
- NO_SHARD: 不切分任何参数。类似ZeRO-0。
9-2-1.llamafactory-cli
您只需根据需要修改 examples/accelerate/fsdp_config.yaml 以及 examples/extras/fsdp_qlora/llama3_lora_sft.yaml ,文件然后运行以下命令即可启动 FSDP+QLoRA 微调:
bash examples/extras/fsdp qlora/train.sh
9-2-2.accelerate
此外,您也可以使用 accelerate 启动 FSDP 引擎, 节点数与 GPU 数可以通过 num_machines 和 num_processes 指定。对此,Huggingface 提供了便捷的配置功能。 只需运行:
accelerate config
根据提示回答一系列问题后,我们就可以生成 FSDP 所需的配置文件。
当然您也可以根据需求自行配置 fsdp_config.yaml 。
### /examples/accelerate/fsdp_config.yaml compute_environment: LOCAL_MACHINE debug: false distributed_type: FSDP downcast_bf16: ‘no’ fsdp_config: fsdp_auto_wrap_policy: TRANSFORMER_BASED_WRAP fsdp_backward_prefetch: BACKWARD_PRE fsdp_forward_prefetch: false fsdp_cpu_ram_efficient_loading: true fsdp_offload_params: true # offload may affect training speed fsdp_sharding_strategy: FULL_SHARD fsdp_state_dict_type: FULL_STATE_DICT fsdp_sync_module_states: true fsdp_use_orig_params: true machine_rank: 0 main_training_function: main mixed_precision: fp16 # or bf16 num_machines: 1 # the number of nodes num_processes: 2 # the number of GPUs in all nodes rdzv_backend: static same_network: true tpu_env: [] tpu_use_cluster: false tpu_use_sudo: false use_cpu: false
备注
- 请确保 num_processes 和实际使用的总GPU数量一致
随后,您可以使用以下命令启动训练:
accelerate launch \ --config_file fsdp_config.yaml \ src/train.py llm_config.yaml
警告 不要在 FSDP+QLoRA 中使用 GPTQ/AWQ 模型
9-3.DeepSpeed
DeepSpeed 是由微软开发的一个开源深度学习优化库,旨在提高大模型训练的效率和速度。在使用 DeepSpeed 之前,您需要先估计训练任务的显存大小,再根据任务需求与资源情况选择合适的 ZeRO 阶段。
- ZeRO-1: 仅划分优化器参数,每个GPU各有一份完整的模型参数与梯度。
- ZeRO-2: 划分优化器参数与梯度,每个GPU各有一份完整的模型参数。
- ZeRO-3: 划分优化器参数、梯度与模型参数。
简单来说:从 ZeRO-1 到 ZeRO-3,阶段数越高,显存需求越小,但是训练速度也依次变慢。此外,设置 offload_param=cpu
参数会大幅减小显存需求,但会极大地使训练速度减慢。因此,如果您有足够的显存, 应当使用 ZeRO-1,并且确保 offload_param=none
。
LLaMA-Factory提供了使用不同阶段的 DeepSpeed 配置文件的示例。包括:
备注
https://huggingface.co/docs/transformers/deepspeed 提供了更为详细的介绍。
9-3-1.单机多卡
9-3-1-1.llamafactory-cli
您可以使用 llamafactory-cli 启动 DeepSpeed 引擎进行单机多卡训练。
FORCE_TORCHRUN**=1** llamafactory-cli train examples/train_full/llama3_full_sft_ds3.yaml
为了启动 DeepSpeed 引擎,配置文件中 deepspeed
参数指定了 DeepSpeed 配置文件的路径:
… deepspeed: examples/deepspeed/ds_z3_config.json …
9-3-1-2.deepspeed
您也可以使用 deepspeed
指令启动 DeepSpeed 引擎进行单机多卡训练。
deepspeed --include localhost:1 your_program.py --deepspeed ds_config.json
下面是一个例子:
deepspeed --num_gpus 8 src/train.py \
--deepspeed examples/deepspeed/ds_z3_config.json \
--stage sft \
--model_name_or_path meta-llama/Meta-Llama-3-8B-Instruct \
--do_train \
--dataset alpaca_en \
--template llama3 \
--finetuning_type full \
--output_dir saves/llama3-8b/lora/full \
--overwrite_cache \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--logging_steps 10 \
--save_steps 500 \
--learning_rate 1e-4 \
--num_train_epochs 2.0 \
--plot_loss \ --bf16
备注
使用 deepspeed
指令启动 DeepSpeed 引擎时您无法使用 CUDA_VISIBLE_DEVICES
指定GPU。而需要:
deepspeed --include localhost:1 your_program.py --deepspeed ds_config.json
--include
localhost:1
表示只是用本节点的gpu1。
9-3-2.多机多卡
LLaMA-Factory 支持使用 DeepSpeed 的多机多卡训练,您可以通过以下命令启动:
FORCE_TORCHRUN=1 NNODES=2 RANK=0 MASTER_ADDR=192.168.0.1 MASTER_PORT=29500 llamafactory-cli train examples/train_lora/llama3_lora_sft_ds3.yaml FORCE_TORCHRUN=1 NNODES=2 RANK=1 MASTER_ADDR=192.168.0.1 MASTER_PORT=29500 llamafactory-cli train examples/train_lora/llama3_lora_sft_ds3.yaml
9-3-2-1.deepspeed
您也可以使用 deepspeed
指令来启动多机多卡训练。
deepspeed --num_gpus 8 --num_nodes 2 --hostfile hostfile --master_addr hostname1 --master_port=9901 \ your_program.py <normal cl args> --deepspeed ds_config.json
备注
- 关于hostfile:hostfile的每一行指定一个节点,每行的格式为
<hostname>``slots=<num_slots>
, 其中<hostname>
是节点的主机名,<num_slots>
是该节点上的GPU数量。下面是一个例子: … code-block:
worker**-1** slots**=4** worker**-2** slots**=4**
请在 https://www.deepspeed.ai/getting-started/ 了解更多。 - 如果没有指定
hostfile
变量, DeepSpeed 会搜索/job/hostfile
文件。如果仍未找到,那么 DeepSpeed 会使用本机上所有可用的GPU。
9-3-2-2.accelerate
您还可以使用 accelerate
指令启动 DeepSpeed 引擎。 首先通过以下命令生成 DeepSpeed 配置文件:
accelerate config
下面提供一个配置文件示例:
# deepspeed_config.yaml compute_environment: LOCAL_MACHINE debug: false deepspeed_config: deepspeed_multinode_launcher: standard gradient_accumulation_steps: 8 offload_optimizer_device: none offload_param_device: none zero3_init_flag: false zero_stage: 3 distributed_type: DEEPSPEED downcast_bf16: ‘no’ enable_cpu_affinity: false machine_rank: 0 main_process_ip: ‘192.168.0.1’ main_process_port: 29500 main_training_function: main mixed_precision: fp16 num_machines: 2 num_processes: 16 rdzv_backend: static same_network: true tpu_env: [] tpu_use_cluster: false tpu_use_sudo: false use_cpu: false
随后,您可以使用以下命令启动训练:
accelerate launch \ --config_file deepspeed_config.yaml \ train.py llm_config.yaml
9-4.DeepSpeed 配置文件
ZeRO-0
### ds_z0_config.json { “train_batch_size”: “auto”, “train_micro_batch_size_per_gpu”: “auto”, “gradient_accumulation_steps”: “auto”, “gradient_clipping”: “auto”, “zero_allow_untested_optimizer”: true**,** “fp16”: { “enabled”: “auto”, “loss_scale”: 0**,** “loss_scale_window”: 1000**,** “initial_scale_power”: 16**,** “hysteresis”: 2**,** “min_loss_scale”: 1 }, “bf16”: { “enabled”: “auto” }, “zero_optimization”: { “stage”: 0**,** “allgather_partitions”: true**,** “allgather_bucket_size”: 5e8**,** “overlap_comm”: true**,** “reduce_scatter”: true**,** “reduce_bucket_size”: 5e8**,** “contiguous_gradients”: true**,** “round_robin_gradients”: true } }
ZeRO-2
只需在 ZeRO-0 的基础上修改 zero_optimization
中的 stage
参数即可。
### ds_z2_config.json { … “zero_optimization”: { “stage”: 2**,** … } }
ZeRO-2+offload
只需在 ZeRO-0 的基础上在 zero_optimization
中添加 offload_optimizer
参数即可。
### ds_z2_offload_config.json { … “zero_optimization”: { “stage”: 2**,** “offload_optimizer”: { “device”: “cpu”, “pin_memory”: true }, … } }
ZeRO-3
只需在 ZeRO-0 的基础上修改 zero_optimization
中的参数。
### ds_z3_config.json { … “zero_optimization”: { “stage”: 3**,** “overlap_comm”: true**,** “contiguous_gradients”: true**,** “sub_group_size”: 1e9**,** “reduce_bucket_size”: “auto”, “stage3_prefetch_bucket_size”: “auto”, “stage3_param_persistence_threshold”: “auto”, “stage3_max_live_parameters”: 1e9**,** “stage3_max_reuse_distance”: 1e9**,** “stage3_gather_16bit_weights_on_model_save”: true } }
ZeRO-3+offload
只需在 ZeRO-3 的基础上添加 zero_optimization
中的 offload_optimizer
和 offload_param
参数即可。
### ds_z3_offload_config.json { … “zero_optimization”: { “stage”: 3**,** “offload_optimizer”: { “device”: “cpu”, “pin_memory”: true }, “offload_param”: { “device”: “cpu”, “pin_memory”: true }, … } }
备注
https://www.deepspeed.ai/docs/config-json/ 提供了关于deepspeed配置文件的更详细的介绍。
10.数字人
参考博客:https://blog.csdn.net/matt45m/article/details/131132588
以上部分内容参考自互联网,融合本人实践验证后的代码,如有不当之处欢迎大家指正,如有侵权等其他事宜,请及时与我联系,提前致谢。(本文仅供参考)