随心玩玩(十二)通义千问——LLM大模型微调

写在前面:使劲的摸鱼,摸到的鱼才是自己的~


文章目录

  • 简介
  • 环境配置
  • 模型加载
  • jupyter远程配置
  • 快速使用
  • 微调示例
  • 部署方案
  • 总结
  • 附录: ReAct Prompting 示例
    • 准备工作一:样例问题、样例工具
    • 准备工作二:ReAct 模版
    • 步骤一:让千问判断要调用什么工具、生成工具入参
    • 步骤二:让千问根据插件返回结果继续作答
    • FAQ

简介

参考资料:
https://github.com/QwenLM/Qwen/blob/main/README_CN.md
https://www.bilibili.com/video/BV16a4y1z7LY
https://modelscope.cn/models/qwen/Qwen-1_8B-Chat-Int4/summary

什么是通义千问?
通义千问(Tongyi Qianwen)是由阿里巴巴集团旗下的云端运算服务的科技公司阿里云开发的聊天机器人。它能够与人互动、回答问题及协作创作。这个名字来源于两个方面:“通义”意味着该模型具有广泛的知识和普适性,可以理解和回答各种领域的问题。作为一个大型预训练语言模型,“通义千问”在训练过程中学习了大量的文本数据,从而具备了跨领域的知识和语言理解能力。“千问”代表了模型可以回答各种问题,包括常见的、复杂的甚至是少见的问题。

什么是LLM?
LLM(Large Language Model)是一种语言模型,以其实现通用语言理解和生成的能力而闻名。LLM通过在计算密集型的自监督和半监督训练过程中从文本文档中学习统计关系来获得这些能力。例如,OpenAI的GPT模型(例如,用于ChatGPT的GPT-3.5和GPT-4),Google的PaLM(用于Bard),Meta的LLaMA,以及BLOOM,Ernie 3.0 Titan和Anthropic的Claude 2等都是LLM的例子。

LLM模型的架构由多个因素决定,如特定模型设计的目标、可用的计算资源以及LLM要执行的语言处理任务的类型。LLM的一般架构包括许多层,如前馈层、嵌入层、注意力层。一个嵌入在内部的文本被协同起来生成预测。

环境配置

要求(Requirements)
python 3.8及以上版本
pytorch 2.0及以上版本
建议使用CUDA 11.4及以上(GPU用户、flash-attention用户等需考虑此选项)

我选择的环境是python=3.9,创建环境名字light
conda create --name light python=3.9
conda activate light
可以到这个网站https://pytorch.org/get-started/previous-versions/,CTRL+F搜索CUDA 11/12找自己版本对应的pytorch安装pytorch

以我的CUDA 11.7为例子,pip进行安装:
pip install torch==2.0.1 torchvision==0.15.2 torchaudio==2.0.2

进入到工作目录,安装Qwen项目,
注:如果网络不好,可以选择ZIP压缩包下载方式下载
git clone https://github.com/QwenLM/Qwen.git

进入到项目根目录,安装所需要的环境
cd Qwen/
pip install -r requirements.txt

requirements.txt:

transformers==4.32.0
accelerate
tiktoken
einops
transformers_stream_generator==0.0.4
scipy

我后面还报了几个错误,需要装这几个库,总之报错了根据提示装上就好
pip install optimum
pip install auto-gptq
Optimum是huggingface开源的模型加速项目。
auto-gptq是一个基于 GPTQ 算法,简单易用且拥有用户友好型接口的大语言模型量化工具包。
在这里插入图片描述

pip install modelscope
类似hugging face,我们可在平台上下载模型

pip install deepspeed
deepspeed框架-大模型分布式训练与推理
参考资料:https://www.bilibili.com/video/BV1LC4y1Y7tE/在这里插入图片描述

conda install mpi4py
mpi4py 是一个用于在 Python 中进行并行计算的 MPI(Message Passing Interface)的接口库

模型加载

参考资料:https://github.com/QwenLM/Qwen
稍等几分钟等模型下载,因为用的modelscope中国源下载会快一点。
本博客下的是qwen/Qwen-1_8B-Chat-Int4作为示例,因为比较小比较好下载,看者可以根据自己需要选择注释的模型下载
模型下载默认地址会在~/.cache/modelscope/hub/qwen/Qwen-1_8B-Chat-Int4/

from modelscope import snapshot_download
from transformers import AutoModelForCausalLM, AutoTokenizer# Downloading model checkpoint to a local dir model_dir
# model_dir = snapshot_download('qwen/Qwen-7B')
# model_dir = snapshot_download('qwen/Qwen-7B-Chat')
# model_dir = snapshot_download('qwen/Qwen-14B')
# model_dir = snapshot_download('qwen/Qwen-14B-Chat')
model_dir = snapshot_download('qwen/Qwen-1_8B-Chat-Int4')# Loading local checkpoints
# trust_remote_code is still set as True since we still load codes from local dir instead of transformers
tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir,device_map="auto",trust_remote_code=True
).eval()

模型下载好后,有一个问题就是我们不能每次都用.py运行来调试,因为模型加载过程十分耗费时间,因此我们需要用jupyter notebook帮助我们快速开发。

jupyter远程配置

参考资料:https://blog.csdn.net/MrR1ght/article/details/98227629

安装jupyter
pip install jupyter

生成配置文件
jupyter notebook --generate-config

设置远程登陆Jupyter Notebook的密码
执行以下命令输入密码即可
jupyter notebook password

编辑Jupyter Notebook的配置文件
vim ~/.jupyter/jupyter_notebook_config.py

在jupyter_notebook_config.py文件最末端添加以下代码:

c.NotebookApp.ip = '*' # 允许访问此服务器的 IP,星号表示任意 IP
c.NotebookApp.open_browser = False # 运行时不打开本机浏览器
c.NotebookApp.port = 8369 # 使用的端口,随意设置
c.NotebookApp.enable_mathjax = True # 启用 MathJax
c.NotebookApp.allow_remote_access = True #允许远程访问
c.NotebookApp.notebook_dir = '/home/work/' # 设置默认工作目录

Jupyter NoteBook 更改Kernel,由于jupyter notebook访问的时候,默认使用了anaconda的base环境,这里就需要更换环境。
python -m ipykernel install --user --name 要添加的环境 --display-name "jupyter中显示的kernel名字"
例如:
python -m ipykernel install --user --name light --display-name "light"

终端输入命令 启动服务,这里是用后台启动
nohup jupyter notebook &

本机设置:
win+r开启cmd,
使用以下命令将本地端口与服务器端相映射:
ssh -L [本地端口]:localhost:[远程端口] [远程用户名]@[远程IP] -p [ssh连接端口]
例如:
ssh -L 8369:localhost:8369 root@172.31.224.191 -p 22
输入密码连接即可

本地浏览器输入
127.0.0.1:8369
默认让你使用密码登录的方式。 输入密码登录即可
在这里插入图片描述
回到pycharm,新建一个.ipynb,配置URL地址127.0.0.1:8369
在这里插入图片描述
shift+enter自动启动服务后切换kenerl在这里插入图片描述
把之前的代码复制过来,运行,这样就可以开始快乐的运行调试了。
在这里插入图片描述
等一会,我们就能加载好模型了。下面将介绍如何使用模型

快速使用

上面我们加载好了模型。我们可以使用model.chat进行对话,下面是例子:

response, history = model.chat(tokenizer, "你好", history=None)
print(response)
response, history = model.chat(tokenizer, "浙江的省会在哪里?", history=history)
print(response)
response, history = model.chat(tokenizer, "它有什么好玩的景点", history=history)
print(response)

在这里插入图片描述

更多参考资料请参考:https://github.com/QwenLM/Qwen/blob/main/README_CN.md

微调示例

参考资料:
https://www.bilibili.com/video/BV16a4y1z7LY
https://github.com/owenliang/qwen-sft

详细讲解见视频BV16a4y1z7LY

prompt工程和微调数据集生成:

# 生成样本
import random
import json
import time# 城市数据
with open('city.txt','r',encoding='utf-8') as fp:city_list=fp.readlines()city_list=[line.strip().split(' ')[1] for line in city_list]
# prompt模板
prompt_template='''
给定一句话:“%s”,请你按步骤要求工作。步骤1:识别这句话中的城市和日期共2个信息
步骤2:根据城市和日期信息,生成JSON字符串,格式为{"city":城市,"date":日期}请问,这个JSON字符串是:
'''Q_arr=[]
A_arr=[]
# 问题模板
Q_list=[('{city}{year}年{month}月{day}日的天气','%Y-%m-%d'),('{city}{year}年{month}月{day}号的天气','%Y-%m-%d'),('{city}{month}月{day}日的天气','%m-%d'),('{city}{month}月{day}号的天气','%m-%d'),('{year}年{month}月{day}日{city}的天气','%Y-%m-%d'),('{year}年{month}月{day}号{city}的天气','%Y-%m-%d'),('{month}月{day}日{city}的天气','%m-%d'),('{month}月{day}号{city}的天气','%m-%d'),('你们{year}年{month}月{day}日去{city}玩吗?','%Y-%m-%d'),('你们{year}年{month}月{day}号去{city}玩么?','%Y-%m-%d'),('你们{month}月{day}日去{city}玩吗?','%m-%d'),('你们{month}月{day}号去{city}玩吗?','%m-%d'),
]
train_data = []
# 生成样例
# 生成一批"1月2号"、"1月2日"、"2023年1月2号", "2023年1月2日", "2023-02-02", "03-02"之类的话术, 教会它做日期转换
for i in range(1000):Q=Q_list[random.randint(0,len(Q_list)-1)]city=city_list[random.randint(0,len(city_list)-1)]year=random.randint(1990,2025)month=random.randint(1,12)day=random.randint(1,28)time_str='{}-{}-{}'.format(year,month,day)date_field=time.strftime(Q[1],time.strptime(time_str,'%Y-%m-%d'))Q=Q[0].format(city=city,year=year,month=month,day=day) # 问题A=json.dumps({'city':city,'date':date_field},ensure_ascii=False)  # 回答example={'id':'identity_{}'.format(i),'conversations':[{'from':'user','value':prompt_template%(Q,)},{'from':'assistant','value':A}]}Q_arr.append(prompt_template%(Q,))A_arr.append(A)train_data.append(example)import pandas as pdwith open('train.txt','w',encoding='utf-8') as fp:fp.write(json.dumps(train_data))
df=pd.DataFrame({'Prompt':Q_arr,'Completion':A_arr})
df.to_excel('train.xlsx')

进到项目根目录,在根目录创建微调启动脚本ft.sh:
这里我们使用qlora,进行部分参数训练

什么是qlora?
QLoRA是一种高效的微调方法,可以减少内存使用,足以在单个48GB GPU上微调65B参数模型,同时保留完整的16位微调任务性能。QLoRA通过冻结的4位量化预训练语言模型反向传播梯度到低秩适配器(LoRa)

#!/bin/bashproject_root=/home3/likeghee/python/Qwenbash $project_root/finetune/finetune_qlora_single_gpu.sh -m /home/likeghee/.cache/modelscope/hub/qwen/Qwen-1_8B-Chat-Int4 -d $project_root/train.txt

ft.sh解释:

定义项目的根目录
project_root=/home3/likeghee/python/Qwen

调用了Fine-tuning脚本 (finetune_qlora_single_gpu.sh)

  • -m /home/likeghee/.cache/modelscope/hub/qwen/Qwen-1_8B-Chat-Int4: 指定预训练模型的路径或标识符。

  • -d $project_root/train.txt: 指定训练数据的路径或文件。

启动微调脚本bash ft.sh
在这里插入图片描述

等待训练完成…
在这里插入图片描述

微调好的模型会生成在项目主目录下的output_qwen在这里插入图片描述

生成好的微调模型文件夹内容:
在这里插入图片描述
使用AutoPeftModelForCausalLM加载微调后的模型

from peft import AutoPeftModelForCausalLMmodel = AutoPeftModelForCausalLM.from_pretrained('output_qwen', # path to the output directorydevice_map="auto",trust_remote_code=True
).eval()
model.generation_config.top_p=0 # 只选择概率最高的tokenQ_list=['2020年4月16号三亚下雨么?','青岛3-15号天气预报','5月6号下雪么,城市是威海','青岛2023年12月30号有雾霾么?','我打算6月1号去北京旅游,请问天气怎么样?','你们打算1月3号坐哪一趟航班去上海?','小明和小红是8月8号在上海结婚么?','一起去东北看冰雕么,大概是1月15号左右,我们3个人一起']
for Q in Q_list:prompt=prompt_template%(Q,)A,hist=model.chat(tokenizer,prompt,history=None)print('Q:%s\nA:%s\n'%(Q,A))

微调结果测试来看非常稳定
在这里插入图片描述

部署方案

参考资料:https://github.com/QwenLM/Qwen/blob/main/README_CN.md
部署部分

vLLM部署方案
不展开

总结

LLM大模型问题,返回值的不稳定,这时候我们就可以自己微调。

prompt工程可以参考范例react_prompt.md(强推!)

附录: ReAct Prompting 示例

本文档将介绍如何用 ReAct Prompting 技术命令千问使用工具。

本文档主要基本的原理概念介绍,并在文末附上了一些具体实现相关的 FAQ,但不含被调用插件的实际实现。如果您更喜欢一边调试实际可执行的代码、一边理解原理,可以转而阅读整合了 LangChain 常用工具的这个 ipython notebook。

此外,本文档和前述的 ipython notebook 都仅介绍单轮对话的实现。如果想了解多轮对话下的实现,可参见 react_demo.py。

准备工作一:样例问题、样例工具

假设我们有如下的一个适合用工具处理的 query,以及有夸克搜索、通义万相文生图这两个工具:

query = '现在给我画个五彩斑斓的黑。'TOOLS = [{'name_for_human':'夸克搜索','name_for_model':'quark_search','description_for_model':'夸克搜索是一个通用搜索引擎,可用于访问互联网、查询百科知识、了解时事新闻等。','parameters': [{'name': 'search_query','description': '搜索关键词或短语','required': True,'schema': {'type': 'string'},}],},{'name_for_human':'通义万相','name_for_model':'image_gen','description_for_model':'通义万相是一个AI绘画(图像生成)服务,输入文本描述,返回根据文本作画得到的图片的URL','parameters': [{'name': 'query','description': '中文关键词,描述了希望图像具有什么内容','required': True,'schema': {'type': 'string'},}],},
]

准备工作二:ReAct 模版

我们将使用如下的 ReAct prompt 模版来激发千问使用工具的能力。

TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters} Format the arguments as a JSON object."""REACT_PROMPT = """Answer the following questions as best you can. You have access to the following tools:{tool_descs}Use the following format:Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input questionBegin!Question: {query}"""

步骤一:让千问判断要调用什么工具、生成工具入参

首先我们需要根据 ReAct prompt 模版、query、工具的信息构建 prompt:

tool_descs = []
tool_names = []
for info in TOOLS:tool_descs.append(TOOL_DESC.format(name_for_model=info['name_for_model'],name_for_human=info['name_for_human'],description_for_model=info['description_for_model'],parameters=json.dumps(info['parameters'], ensure_ascii=False),))tool_names.append(info['name_for_model'])
tool_descs = '\n\n'.join(tool_descs)
tool_names = ','.join(tool_names)prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names, query=query)
print(prompt)

打印出来的、构建好的 prompt 如下:

Answer the following questions as best you can. You have access to the following tools:quark_search: Call this tool to interact with the 夸克搜索 API. What is the 夸克搜索 API useful for? 夸克搜索是一个通用搜索引擎,可用于访问互联网、查询百科知识、了解时事新闻等。 Parameters: [{"name": "search_query", "description": "搜索关键词或短语", "required": true, "schema": {"type": "string"}}] Format the arguments as a JSON object.image_gen: Call this tool to interact with the 通义万相 API. What is the 通义万相 API useful for? 通义万相是一个AI绘画(图像生成)服务,输入文本描述,返回根据文本作画得到的图片的URL Parameters: [{"name": "query", "description": "中文关键词,描述了希望图像具有什么内容", "required": true, "schema": {"type": "string"}}] Format the arguments as a JSON object.Use the following format:Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [quark_search,image_gen]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input questionBegin!Question: 现在给我画个五彩斑斓的黑。

将这个 prompt 送入千问,并记得设置 “Observation” 为 stop word (见本文末尾的 FAQ)—— 即让千问在预测到要生成的下一个词是 “Observation” 时马上停止生成 —— 则千问在得到这个 prompt 后会生成如下的结果:

Thought: 我应该使用通义万相API来生成一张五彩斑斓的黑的图片。
Action: image_gen
Action Input: {"query": "五彩斑斓的黑"}

在得到这个结果后,调用千问的开发者可以通过简单的解析提取出 {"query": "五彩斑斓的黑"} 并基于这个解析结果调用文生图服务 —— 这部分逻辑需要开发者自行实现,或者也可以使用千问商业版,商业版本将内部集成相关逻辑。

步骤二:让千问根据插件返回结果继续作答

让我们假设文生图插件返回了如下结果:

{"status_code": 200, "request_id": "3d894da2-0e26-9b7c-bd90-102e5250ae03", "code": null, "message": "", "output": {"task_id": "2befaa09-a8b3-4740-ada9-4d00c2758b05", "task_status": "SUCCEEDED", "results": [{"url": "https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/1e5e2015/20230801/1509/6b26bb83-469e-4c70-bff4-a9edd1e584f3-1.png"}], "task_metrics": {"TOTAL": 1, "SUCCEEDED": 1, "FAILED": 0}}, "usage": {"image_count": 1}}

接下来,我们可以将之前首次请求千问时用的 prompt 和 调用文生图插件的结果拼接成如下的新 prompt:

Answer the following questions as best you can. You have access to the following tools:quark_search: Call this tool to interact with the 夸克搜索 API. What is the 夸克搜索 API useful for? 夸克搜索是一个通用搜索引擎,可用于访问互联网、查询百科知识、了解时事新闻等。 Parameters: [{"name": "search_query", "description": "搜索关键词或短语", "required": true, "schema": {"type": "string"}}] Format the arguments as a JSON object.image_gen: Call this tool to interact with the 通义万相 API. What is the 通义万相 API useful for? 通义万相是一个AI绘画(图像生成)服务,输入文本描述,返回根据文本作画得到的图片的URL Parameters: [{"name": "query", "description": "中文关键词,描述了希望图像具有什么内容", "required": true, "schema": {"type": "string"}}] Format the arguments as a JSON object.Use the following format:Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [quark_search,image_gen]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input questionBegin!Question: 现在给我画个五彩斑斓的黑。
Thought: 我应该使用通义万相API来生成一张五彩斑斓的黑的图片。
Action: image_gen
Action Input: {"query": "五彩斑斓的黑"}
Observation: {"status_code": 200, "request_id": "3d894da2-0e26-9b7c-bd90-102e5250ae03", "code": null, "message": "", "output": {"task_id": "2befaa09-a8b3-4740-ada9-4d00c2758b05", "task_status": "SUCCEEDED", "results": [{"url": "https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/1e5e2015/20230801/1509/6b26bb83-469e-4c70-bff4-a9edd1e584f3-1.png"}], "task_metrics": {"TOTAL": 1, "SUCCEEDED": 1, "FAILED": 0}}, "usage": {"image_count": 1}}

用这个新的拼接了文生图插件结果的新 prompt 去调用千问,将得到如下的最终回复:

Thought: 我已经成功使用通义万相API生成了一张五彩斑斓的黑的图片。
Final Answer: 我已经成功使用通义万相API生成了一张五彩斑斓的黑的图片https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/1e5e2015/20230801/1509/6b26bb83-469e-4c70-bff4-a9edd1e584f3-1.png。

虽然对于文生图来说,这个第二次调用千问的步骤显得多余。但是对于搜索插件、代码执行插件、计算器插件等别的插件来说,这个第二次调用千问的步骤给了千问提炼、总结插件返回结果的机会。

FAQ

怎么配置 “Observation” 这个 stop word?

通过 chat 接口的 stop_words_ids 指定:

react_stop_words = [# tokenizer.encode('Observation'),  # [37763, 367]tokenizer.encode('Observation:'),  # [37763, 367, 25]tokenizer.encode('Observation:\n'),  # [37763, 367, 510]
]
response, history = model.chat(tokenizer, query, history,stop_words_ids=react_stop_words  # 此接口用于增加 stop words
)

如果报错称不存在 stop_words_ids 此参数,可能是因为您用了老的代码,请重新执行 from_pretrained 拉取新的代码和模型。

需要注意的是,当前的 tokenizer 对 \n 有一系列较复杂的聚合操作。比如例子中的:\n这两个字符便被聚合成了一个 token。因此配置 stop words 需要非常细致地预估 tokenizer 的行为。

对 top_p 等推理参数有调参建议吗?

通常来讲,较低的 top_p 会有更高的准确度,但会牺牲回答的多样性、且更易出现重复某个词句的现象。

可以按如下方式调整 top_p 为 0.5:

model.generation_config.top_p = 0.5

特别的,可以用如下方式关闭 top-p sampling,改用 greedy sampling,效果上相当于 top_p=0 或 temperature=0:

model.generation_config.do_sample = False  # greedy decoding

此外,我们在 model.chat() 接口也提供了调整 top_p 等参数的接口。

有解析Action、Action Input的参考代码吗?

有的,可以参考:

def parse_latest_plugin_call(text: str) -> Tuple[str, str]:i = text.rfind('\nAction:')j = text.rfind('\nAction Input:')k = text.rfind('\nObservation:')if 0 <= i < j:  # If the text has `Action` and `Action input`,if k < j:  # but does not contain `Observation`,# then it is likely that `Observation` is ommited by the LLM,# because the output text may have discarded the stop word.text = text.rstrip() + '\nObservation:'  # Add it back.k = text.rfind('\nObservation:')if 0 <= i < j < k:plugin_name = text[i + len('\nAction:'):j].strip()plugin_args = text[j + len('\nAction Input:'):k].strip()return plugin_name, plugin_argsreturn '', ''

此外,如果输出的 Action Input 内容是一段表示 JSON 对象的文本,我们建议使用 json5 包的 json5.loads(...) 方法加载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/614199.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL:DML数据操作语言(添加,删除,修改),DDL数据查询语言(条件查询,分组查询,排序查询,分页查询)

目录 1.DML&#xff08;数据操作语言&#xff09;1.添加数据2.修改数据3.删除数据 2.DQL(数据查询语言)1.DQL-语法2.基本查询3.条件查询(WHERE)1.语法&#xff1a;2.条件&#xff1a;3.案例: 4.聚合函数1.介绍2.常见聚合函数3.语法4.案例 5.分组查询&#xff08;GROUP BY&#…

物联网通讯协议NB-lot和LoRa差异分析

像把大象装冰箱一样&#xff0c;物联网&#xff0c;万物互联也是要分步骤的。 一、感知层(信息获取层)&#xff0c;即利用各种传感器等设备随时随地获取物体的信息; 二、网络层(信息传输层)&#xff0c;通过各种电信网络与互联网的融合&#xff0c;将物体的信息实时准确地传递…

力扣LCR 166. 珠宝的最高价值(java 动态规划)

Problem: LCR 166. 珠宝的最高价值 文章目录 解题思路思路解题方法复杂度Code 解题思路 思路 改题目与本站64题实质上是一样的&#xff0c;该题目在64题的基础上将求取最小路径和改成了求取最大路径和。具体实现思路如下&#xff1a; 1.定义一个int类型的二维数组dp大小为给定…

17- Echarts 配置系列之:单轴 singleAxis

singleAxis&#xff1a; 用于展示只有一个数据维度的数据。它通常用于展示时间序列数据或者数值序列数据。 对于单轴的应用和绘制&#xff0c;其实就相当于我们平时的直角坐标系少一个 X 或者 Y &#xff0c;然后进行图形绘制。 注意&#xff1a; 1.在使用单轴时&#xff0…

1.10号io网络

信号量&#xff08;信号灯集&#xff09; 1> 信号灯集主要完成进程间同步工作&#xff0c;将多个信号灯&#xff0c;放在一个信号灯集中&#xff0c;每个信号灯控制一个进程 2> 每个灯维护了一个value值&#xff0c;当value值等于0时&#xff0c;申请该资源的进程处于阻…

【Python学习】Python学习13-日期和时间

目录 【Python学习】Python学习13-日期和时间 前言通过time 获取时间戳时间元组获取当前时间&#xff0c;格式化时间格式化时间转换python中时间日期格式化符号获取日历Time 模块日历&#xff08;Calendar&#xff09;模块其他模块参考 文章所属专区 Python学习 前言 本章节主…

小马识途:十个营销故事 启发营销思路

在营销过程中&#xff0c;优势是相对的&#xff0c;只有凭借着客观的营销环境创造优势才能够取胜市场。在企业营销中&#xff0c;狗猛酒酸的案例比比皆是。接下来&#xff0c;就与小马识途一起来看看十个经典的营销故事吧&#xff01; 一、摩托车公司 有家德国摩托车公司&…

SQL优化小技巧

在表中建⽴索引&#xff0c;优先考虑 where group by 使⽤到的字段。 查询时尽量避免使⽤select * &#xff0c;只查询需要⽤到的字段。 避免在where⼦句中使⽤关键字两边都是%的模糊查询&#xff0c;尽量在关键字后使⽤模糊查询。 尽量避免在where⼦句中使⽤IN 和NOT IN。 …

【排序】快速排序(C语言实现)

文章目录 前言1. Hoare思想2. 挖坑法3. 前后指针法4. 三路划分5. 快速排序的一些小优化5.1 三数取中常规的三数取中伪随机的三数取中 5.2 小区间优化 6. 非递归版本的快排7. 快速排序的特性总结 前言 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其…

【开源项目】轻量元数据管理解决方案——Marquez

大家好&#xff0c;我是独孤风。 又到了本周的开源项目推荐。最近推荐的元数据管理项目很多&#xff0c;但是很多元数据管理平台的功能复杂难用。 那么有没有轻量一点的元数据管理项目呢&#xff1f; 今天为大家推荐的开源项目&#xff0c;就是一个轻量级的元数据管理工具。虽然…

Linux入门攻坚——12、Linux网络属性配置相关知识2

CentOS 7网络属性配置&#xff1a; 传统命名机制&#xff1a;以太网eth[0,1,2,...]&#xff0c;wlan[0,1,2...] 可预测功能的命名机制&#xff1a; udev支持多种不同的命名方案&#xff1a; Firmware &#xff0c;拓扑结构 在对待设备文件这块&#xff0c;Linux改…

人生重开模拟器(c语言)

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

云HIS系统源码,基层卫生HIS系统,云端SaaS模式多医院版

系统介绍&#xff1a; 基层卫生健康云HIS系统采用云端SaaS服务的方式提供&#xff0c;使用用户通过浏览器即能访问&#xff0c;无需关注系统的部署、维护、升级等问题&#xff0c;系统充分考虑了模板化、配置化、智能化、扩展化等设计方法&#xff0c;覆盖了基层医院的主要工作…

基于OpenMV与STM32的数据通信项目(代码开源)

前言&#xff1a;本文为手把手教学 OpenMV 与 STM32 的数据通信项目教程&#xff0c;本教程使用 STM32F103C8T6 与 OpenMV 进行操作。 OpenMV 是非常强大的计算机视觉实现工具&#xff0c;自身提供了非常多的视觉项目案例&#xff0c;编程与使用门槛极低。为了进一步增强作品的…

Camunda Rest API

客户端像调用本地方法一样调用引擎中的接口。 https://docs.camunda.org/manual/7.17/reference/rest/ 一&#xff1a;pom.xml <dependency><groupId>org.camunda.community.rest</groupId><artifactId>camunda-platform-7-rest-client-spring-boot-…

解决百度网盘限速问题(不用会员)

不想冲网盘会员的友友可以看一下这个方法 1.需要下载Motrix(因为我是Mac所以用的这个,Win用户可以试试别的) 相关软件下载 | 油小猴 2.打开这个网站 就是加速 用户操作演示面板 3.勾选后可以直接发送到Motrix 还可以使用的方法

CES2024:智能戒指、全息技术、AI家居机器人等有趣的小工具

在CES2024的展会上上&#xff0c;我们见证了一系列充满创意和未来感的科技产品。从智能戒指到全息技术&#xff0c;再到AI家居机器人&#xff0c;这些有趣的小工具不仅展现了技术的进步&#xff0c;更预示着未来生活的可能性。现在就来给大家介绍九个实用有趣的小工具。 1、华…

[AutoSar]基础部分 RTE 03 C/S Port 同步/异步

目录 关键词平台说明一、C/S port interface 定义1.1在Davinci developer中的创建 二、同步调用和异步调用2.1 同步2.1.1同步code2.1.2同步处理时序图 2.2 异步2.2.1异步code2.2.2异步处理时序图2.2.2.1 poling2.2.2.2 waiting2.2.2.3none 三、server端的mapping到task详解 关键…

CSS3新增文本样式-text-shadow属性

文本样式 概念:在CSS3中&#xff0c;增加了丰富的文本修饰效果&#xff0c;使得页面更加美观舒服。 常用的文本样式属性 属性说明text-shadow文本阴影text-stroke文本描边text-overflow文本溢出word-wrap强制换行font-face嵌入字体 W3C坐标系 我们日常生活使用最多的是数学坐…

三菱plc学习入门(三,FB模块)

小编很抱歉&#xff0c;因为小编是以基恩士&#xff0c;三菱的plc一起学习并找发现不同&#xff01;&#xff01;&#xff01;并结合工作的案例来进行学习&#xff0c;所以内容上与系统的学习还是存在差异。如果只是单独的学习此篇文章&#xff0c;如果对您有帮助&#xff0c;欢…