本地运行面壁智能的“贺岁模型”:MiniCPM 2B

简单聊聊可以在端侧运行的 Mini CPM 2B SFT / DPO 版本的模型。

写在前面

模型是好是坏,其实不用看公众号们的营销,小马过河问题,自己试试就知道了。当然,2B 参数量的模型,适合的场景肯定不是 34B / 70B 所擅长的,审视标准应该是不同的。

去年年末的闭门分享会,听到面壁联创大海老师的分享,对其“不刷分不打榜”,以及“产品价值还是服务价值” 的观点记忆深刻。所以,朋友圈看到铺天盖地的“小钢炮”的发布介绍时,第一时间就下手运行了一把,还顺带修正了一些 vllm 运行过程中的问题。

这篇文章,暂且不提耗时时间比较长的 vllm 高效运行的准备过程,先简单聊聊如何快速的在本地运行这个“贺岁模型”。

准备工作

和往常一样,准备工作只有两件事:“准备模型的运行环境”和“下载 CPM 模型”。

准备容器环境

我个人比较倾向使用 Docker 作为运行环境,在投入很少额外资源的情况下,能够快速获得纯净、可复现的一致性非常棒的环境。

如果你选择 Docker 路线,不论你的设备是否有显卡,都可以根据自己的操作系统喜好,参考这两篇来完成基础环境的配置《基于 Docker 的深度学习环境:Windows 篇》、《基于 Docker 的深度学习环境:入门篇》。当然,使用 Docker 之后,你还可以做很多事情,比如:之前几十篇有关 Docker 的实践,在此就不赘述啦。

除此之外,为了高效运行模型,我推荐使用 Nvidia 官方的容器镜像(nvcr.io/nvidia/pytorch:24.01-py3),以及 HuggingFace 出品的 Transformers 工具包,和快速完成模型 Web UI 界面搭建的 Gradio,以及既能做模型加速推理,又能做多卡分布式加速推理的 Accelerate 工具包。

我们可以基于上面的内容,快速折腾一个干净、高效的基础运行环境。

考虑到我们可能会将模型应用运行在不同的环境,比如云主机和服务器,它们的网络环境可能有所不同。

当我们本地进行 Docker 镜像构建的时候,配置软件镜像来加速可以大幅改善开发者体验。所以,稍加调整,我们可以得到下面的 Dockerfile 文件:

FROM nvcr.io/nvidia/pytorch:24.01-py3
LABEL maintainer="soulteary@gmail.com"# setup Ubuntu and PyPi mirrors, refs: https://github.com/soulteary/docker-stable-diffusion-webui/blob/main/docker/Dockerfile.base
ARG USE_CHINA_MIRROR=false
RUN if [ "$USE_CHINA_MIRROR" = "true" ]; then \pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list; \fi# install dependencies
RUN pip install transformers==4.37.2 gradio==4.16.0 accelerate==0.26.1

将上面的内容保存为 Dockerfile,然后执行下面的命令,可以进行镜像构建:

# 直接构建
docker build -t soulteary/minicpm:hf .# 启用国内的软件镜像加速构建
docker build --build-arg=USE_CHINA_MIRROR=true -t soulteary/minicpm:hf .

稍等片刻,等命令执行完毕,基础镜像就构建好了,我们的准备工作也就完成一半啦。

下载模型

接下来,我们来完成镜像准备之外的 50% 的准备工作,下载模型。

你可以根据你的实际网络情况,来选择到底是从 HuggingFace 下载模型还是从 ModelScope 来下载模型。当然,今年国内开发者有了一个新的选项:WiseModel。你可以根据你的网络情况,来选择最适合你的模型下载或者在线推理平台。

虽然官方一口气推出了很多版本,不过在小参数量模型的能力和效果验证上,我个人的观点是尽可能先下载尺寸最大的,比如 dpo-fp32、sft-fp32 两个版本的模型,来规避数据转换带来的测试结果的干扰。

模型下载完毕后,确保目录结构类似下面这样:

├── MiniCPM-2B-dpo-fp32
│   ├── README.md
│   ├── config.json
│   ├── configuration.json
│   ├── configuration_minicpm.py
│   ├── generation_config.json
│   ├── modeling_minicpm.py
│   ├── pytorch_model.bin
│   ├── special_tokens_map.json
│   ├── tokenizer.json
│   ├── tokenizer.model
│   └── tokenizer_config.json
└── MiniCPM-2B-sft-fp32├── README.md├── config.json├── configuration_minicpm.py├── generation_config.json├── modeling_minicpm.py├── pytorch_model.bin├── special_tokens_map.json├── tokenizer.json├── tokenizer.model└── tokenizer_config.json

不论你从哪里获取模型,都建议你在得到模型后进行文件 Hash 验证。

下面是 DPO 模型的 Hash:

# shasum OpenBMB/MiniCPM-2B-dpo-fp32/*30f7faade4df3f061b3bfeda8dcce1f3dfaa5b6b  OpenBMB/MiniCPM-2B-dpo-fp32/README.md
161c58f3802b0d67516d8efdd25b81317c0ac5bd  OpenBMB/MiniCPM-2B-dpo-fp32/config.json
9b0b13d6cfed485a07b321bcc471bee0830004e4  OpenBMB/MiniCPM-2B-dpo-fp32/configuration.json
767ea019b50a5e4c6ef02887740c01018580b840  OpenBMB/MiniCPM-2B-dpo-fp32/configuration_minicpm.py
82bdc1029ba8b8181a4450f2c421cde60ba550c0  OpenBMB/MiniCPM-2B-dpo-fp32/generation_config.json
6c70fd73f2f07f86c0f8ff89b2ce4fa4265c369d  OpenBMB/MiniCPM-2B-dpo-fp32/modeling_minicpm.py
bfdd8439579f93433234b46394cefb3cfe5ee94b  OpenBMB/MiniCPM-2B-dpo-fp32/pytorch_model.bin
faed8efa07e75f91b0fe66219ad6bbfb14bfdb0e  OpenBMB/MiniCPM-2B-dpo-fp32/special_tokens_map.json
b7392d123e20c5b770699f5440fd0f2a0d8a52fb  OpenBMB/MiniCPM-2B-dpo-fp32/tokenizer.json
c57072fc486c976c8c617cc34c0bb742b6a29b19  OpenBMB/MiniCPM-2B-dpo-fp32/tokenizer.model
6d0a5c9ee222e1460d8df97d3ed36f934327cd06  OpenBMB/MiniCPM-2B-dpo-fp32/tokenizer_config.json

以及 SFT 模型的 Hash:

shasum OpenBMB/MiniCPM-2B-sft-fp32/*
c2bbcd5cdece248fab38f40fa679e8f958df48a0  OpenBMB/MiniCPM-2B-sft-fp32/README.md
50508d551e005168f8adcf2b98acb565bbb49f6f  OpenBMB/MiniCPM-2B-sft-fp32/config.json
767ea019b50a5e4c6ef02887740c01018580b840  OpenBMB/MiniCPM-2B-sft-fp32/configuration_minicpm.py
82bdc1029ba8b8181a4450f2c421cde60ba550c0  OpenBMB/MiniCPM-2B-sft-fp32/generation_config.json
6c70fd73f2f07f86c0f8ff89b2ce4fa4265c369d  OpenBMB/MiniCPM-2B-sft-fp32/modeling_minicpm.py
2b4d1fc9d13f6cb871f8e2d6735b6f0053b8d9d4  OpenBMB/MiniCPM-2B-sft-fp32/pytorch_model.bin
faed8efa07e75f91b0fe66219ad6bbfb14bfdb0e  OpenBMB/MiniCPM-2B-sft-fp32/special_tokens_map.json
b7392d123e20c5b770699f5440fd0f2a0d8a52fb  OpenBMB/MiniCPM-2B-sft-fp32/tokenizer.json
c57072fc486c976c8c617cc34c0bb742b6a29b19  OpenBMB/MiniCPM-2B-sft-fp32/tokenizer.model
6d0a5c9ee222e1460d8df97d3ed36f934327cd06  OpenBMB/MiniCPM-2B-sft-fp32/tokenizer_config.json

编写推理程序

我们基于官方项目中的推理程序进行简单调整,让它能够支持在容器中运行,并且能够支持用模型原始数据类型进行推理,尽量避免数据转换带来不必要的测试干扰问题。

from typing import Listimport argparse
import gradio as gr
import torch
from threading import Thread
from transformers import (AutoModelForCausalLM, AutoTokenizer,TextIteratorStreamer
)import warnings
warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated')parser = argparse.ArgumentParser()
parser.add_argument("--model_path", type=str, default="")
parser.add_argument("--torch_dtype", type=str, default="bfloat16")
parser.add_argument("--server_name", type=str, default="127.0.0.1")
parser.add_argument("--server_port", type=int, default=7860)args = parser.parse_args()# init model torch dtype
torch_dtype = args.torch_dtype
if torch_dtype =="" or torch_dtype == "bfloat16":torch_dtype = torch.bfloat16
elif torch_dtype == "float32":torch_dtype = torch.float32
else:raise ValueError(f"Invalid torch dtype: {torch_dtype}")# init model and tokenizer
path = args.model_path
tokenizer = AutoTokenizer.from_pretrained(path)
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch_dtype, device_map="auto", trust_remote_code=True)# init gradio demo host and port
server_name=args.server_name
server_port=args.server_portdef hf_gen(dialog: List, top_p: float, temperature: float, max_dec_len: int):"""generate model output with huggingface apiArgs:query (str): actual model input.top_p (float): only the smallest set of most probable tokens with probabilities that add up to top_p or higher are kept for generation.temperature (float): Strictly positive float value used to modulate the logits distribution.max_dec_len (int): The maximum numbers of tokens to generate.Yields:str: real-time generation results of hf model"""    inputs = tokenizer.apply_chat_template(dialog, tokenize=False, add_generation_prompt=False)enc = tokenizer(inputs, return_tensors="pt").to("cuda")streamer = TextIteratorStreamer(tokenizer)generation_kwargs = dict(enc,do_sample=True,top_p=top_p,temperature=temperature,max_new_tokens=max_dec_len,pad_token_id=tokenizer.eos_token_id,streamer=streamer,)thread = Thread(target=model.generate, kwargs=generation_kwargs)thread.start()answer = ""for new_text in streamer:answer += new_textyield answer[4 + len(inputs):]def generate(chat_history: List, query: str, top_p: float, temperature: float, max_dec_len: int):"""generate after hitting "submit" buttonArgs:chat_history (List): [[q_1, a_1], [q_2, a_2], ..., [q_n, a_n]]. list that stores all QA recordsquery (str): query of current roundtop_p (float): only the smallest set of most probable tokens with probabilities that add up to top_p or higher are kept for generation.temperature (float): strictly positive float value used to modulate the logits distribution.max_dec_len (int): The maximum numbers of tokens to generate.Yields:List: [[q_1, a_1], [q_2, a_2], ..., [q_n, a_n], [q_n+1, a_n+1]]. chat_history + QA of current round."""    assert query != "", "Input must not be empty!!!"# apply chat templatemodel_input = []for q, a in chat_history:model_input.append({"role": "user", "content": q})model_input.append({"role": "assistant", "content": a})model_input.append({"role": "user", "content": query})# yield model generationchat_history.append([query, ""])for answer in hf_gen(model_input, top_p, temperature, max_dec_len):chat_history[-1][1] = answer.strip("</s>")yield gr.update(value=""), chat_historydef regenerate(chat_history: List, top_p: float, temperature: float, max_dec_len: int):"""re-generate the answer of last round's queryArgs:chat_history (List): [[q_1, a_1], [q_2, a_2], ..., [q_n, a_n]]. list that stores all QA recordstop_p (float): only the smallest set of most probable tokens with probabilities that add up to top_p or higher are kept for generation.temperature (float): strictly positive float value used to modulate the logits distribution.max_dec_len (int): The maximum numbers of tokens to generate.Yields:List: [[q_1, a_1], [q_2, a_2], ..., [q_n, a_n]]. chat_history"""    assert len(chat_history) >= 1, "History is empty. Nothing to regenerate!!"# apply chat templatemodel_input = []for q, a in chat_history[:-1]:model_input.append({"role": "user", "content": q})model_input.append({"role": "assistant", "content": a})model_input.append({"role": "user", "content": chat_history[-1][0]})# yield model generationfor answer in hf_gen(model_input, top_p, temperature, max_dec_len):chat_history[-1][1] = answer.strip("</s>")yield gr.update(value=""), chat_historydef clear_history():"""clear all chat historyReturns:List: empty chat history"""    return []def reverse_last_round(chat_history):"""reverse last round QA and keep the chat history beforeArgs:chat_history (List): [[q_1, a_1], [q_2, a_2], ..., [q_n, a_n]]. list that stores all QA recordsReturns:List: [[q_1, a_1], [q_2, a_2], ..., [q_n-1, a_n-1]]. chat_history without last round."""    assert len(chat_history) >= 1, "History is empty. Nothing to reverse!!"return chat_history[:-1]# launch gradio demo
with gr.Blocks(theme="soft") as demo:gr.Markdown("""# MiniCPM Gradio Demo""")with gr.Row():with gr.Column(scale=1):top_p = gr.Slider(0, 1, value=0.8, step=0.1, label="top_p")temperature = gr.Slider(0.1, 2.0, value=0.8, step=0.1, label="temperature")max_dec_len = gr.Slider(1, 1024, value=1024, step=1, label="max_dec_len")with gr.Column(scale=5):chatbot = gr.Chatbot(bubble_full_width=False, height=400)user_input = gr.Textbox(label="User", placeholder="Input your query here!", lines=8)with gr.Row():submit = gr.Button("Submit")clear = gr.Button("Clear")regen = gr.Button("Regenerate")reverse = gr.Button("Reverse")submit.click(generate, inputs=[chatbot, user_input, top_p, temperature, max_dec_len], outputs=[user_input, chatbot])regen.click(regenerate, inputs=[chatbot, top_p, temperature, max_dec_len], outputs=[user_input, chatbot])clear.click(clear_history, inputs=[], outputs=[chatbot])reverse.click(reverse_last_round, inputs=[chatbot], outputs=[chatbot])demo.queue()
demo.launch(server_name=server_name, server_port=server_port, show_error=True)

我们将上面的内容保存为 app.py,然后启动 Docker 容器,接着,将程序挂载和模型挂载到容器内,就可以开始体验模型了。

如果你和我一样,将模型下载到了 models 目录中,并且保持了上文提到的模型目录结构,那么可以运行下面的命令,来启动一个 MiniCPM-2B 模型的实例:

docker run --rm -it -p 7860:7860 --gpus all --ipc=host --ulimit memlock=-1 -v `pwd`/models:/app/models soulteary/minicpm:hf python app.py --model_path=./models/OpenBMB/MiniCPM-「具体模型型号」/ --server_name=0.0.0.0

你可以将上面命令中的 ./models/OpenBMB/MiniCPM-「具体模型型号」,需要替换为真实的地址,比如这样:

docker run --rm -it -p 7860:7860 --gpus all --ipc=host --ulimit memlock=-1 -v `pwd`/models:/app/models soulteary/minicpm:hf python app.py --model_path=./models/OpenBMB/MiniCPM-2B-dpo-fp32/ --server_name=0.0.0.0

官方程序默认逻辑中,使用的是 bfloat16 来进行模型推理,如果你下载的也是 float32 版本的模型,我们可以在启动的时候,额外增加一个参数 --torch_dtype=float32,来避免模型数据类型转换:

docker run --rm -it -p 7860:7860 --gpus all --ipc=host --ulimit memlock=-1 -v `pwd`/models:/app/models soulteary/minicpm:hf python app.py --model_path=./models/OpenBMB/MiniCPM-2B-dpo-fp32/ --server_name=0.0.0.0 --torch_dtype=float32

当命令执行完毕后,我们可以看到下面的日志输出:

=============
== PyTorch ==
=============NVIDIA Release 24.01 (build 80741402)
PyTorch Version 2.2.0a0+81ea7a4Container image Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.Copyright (c) 2014-2023 Facebook Inc.
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
Copyright (c) 2011-2013 NYU                      (Clement Farabet)
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
Copyright (c) 2006      Idiap Research Institute (Samy Bengio)
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
Copyright (c) 2015      Google Inc.
Copyright (c) 2015      Yangqing Jia
Copyright (c) 2013-2016 The Caffe contributors
All rights reserved.Various files include modifications (c) NVIDIA CORPORATION & AFFILIATES.  All rights reserved.This container image and its contents are governed by the NVIDIA Deep Learning Container License.
By pulling and using the container, you accept the terms and conditions of this license:
https://developer.nvidia.com/ngc/nvidia-deep-learning-container-licenseWARNING: CUDA Minor Version Compatibility mode ENABLED.Using driver version 525.147.05 which has support for CUDA 12.0.  This containerwas built with CUDA 12.3 and will be run in Minor Version Compatibility mode.CUDA Forward Compatibility is preferred over Minor Version Compatibility for usewith this container but was unavailable:[[Forward compatibility was attempted on non supported HW (CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE) cuInit()=804]]See https://docs.nvidia.com/deploy/cuda-compatibility/ for details.Running on local URL:  http://0.0.0.0:7860To create a public link, set `share=True` in `launch()`.

日志输出内容中出现 Running on local URL: http://0.0.0.0:7860 时,我们访问浏览器地址 http://容器IP地址:7860,就能够来体验 SFT 或者 DPO 版本的小钢炮模型啦。

默认的 WebUI 界面

接下来,就是随便的进行测试啦。

简单测试

关于模型的体验,我之前在这篇知乎帖子中回答过。为了客观和真实性,我们不妨再做几次测试。

首先,测试下基础的“命题作文”的能力:

“知乎故事”小达人

接着,问问条件有限的情况下的“料理”:

做饭辅助回答

这里的回答如果能够结合“下厨房的 API”,应该会更靠谱一些,虽然乍一看是符合要求的食谱方案,但是仔细一看是没有遵循指令的,要求中的食材是没有用到的。

继续顺便问问经典的“螺丝帽料理”话题:

“做饭”小达人

不正经的做饭倒是回答的不错,先叠甲规避责任,然后放飞自我的生成内容,最后加一句“仅供娱乐,不推荐食用”。

当然,也可以问问“为什么伟大的音乐家没有再继续谱曲”:

“知识区”小达人

这里的回复准确度是有问题的,月光不是莫扎特的,得用官方推荐的 RAG 方式来做准确内容的生成。

当然,模型还有很多问题,除了想要得到答案,需要反复的 Roll 来 Roll 去外,也会出现怎么都刷不出来自己想要的答案的情况,比如这个(还是相对好的状态):

“对联”就达人不起来了

在生成对联的过程中,最容易出现的是重复内容的生成,虽然可能和其他的模型一样,通过添加 repetition_penalty 重复惩罚相关的参数,并设置比较大的数值来减少一些情况的发生,但可能最好的方案还是给这个小模型附加一个“知识库”,避免完全进入知识不足的“局部最优解”的死循环中。

最后

这篇文章先写到这里,关于本篇文章挖的一个坑,如何在安装了最新版本 CUDA 、Torch 等软件版本的容器环境中,使用 vllm 来高效运行模型,下一篇相关的文章再聊。

–EOF


本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2024年02月02日
统计字数: 14180字
阅读时间: 29分钟阅读
本文链接: https://soulteary.com/2024/02/02/locally-run-modelbest-mini-cpm-2b.html

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

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

相关文章

VBA_MF系列技术资料1-325

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于…

项目安全问题及解决方法------使用合适的算法

Spring Security 已经废弃了 MessageDigestPasswordEncoder&#xff0c;推荐使用 BCryptPasswordEncoder private static BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder(); GetMapping("performance")public void performance() {StopWatch st…

android开发---简单购物商城(JAVA) (一)

包括&#xff1a;商品展示&#xff0c;商品详情&#xff0c;购物车&#xff0c;删除&#xff0c;一键清除&#xff0c;返回 运用sqllist 另外因为一篇写不下 继续可看 源码二 下面是目录 运行样子 下面是源码 AndroidManifest.xml <?xml version"1.0" e…

统计图表在线配置服务-百度 SugarBI的学习笔记

最近&#xff0c;有个产品要支持统计图表在线可配置&#xff0c;这样&#xff0c;当用户有新增统计指标的需求时&#xff0c;运维人员通过界面化配置&#xff0c;就可以增加统计指标了&#xff0c;不用开发写代码&#xff0c;画页面了。 上网查了下相关的组件&#xff0c;感觉…

SG2520CAA汽车用晶体振荡器

爱普生SG2520CAA是简单的封装晶体振荡器&#xff08;SPXO&#xff09;&#xff0c;具有CMOS输出&#xff0c;这款SPXO是汽车和高可靠性应用的理想选择&#xff0c;符合AEC-Q200标准&#xff0c;功耗低&#xff0c;工作电压范围为1.8 V ~ 3.3 V类型&#xff0c;宽工作温度-40℃~…

VPP学习-startup.conf配置文件

背景 VPP&#xff08;Vector Packet Processing&#xff0c;矢量报文处理&#xff09;&#xff0c;作为一个开源的高性能数据包处理框架&#xff0c;旨在提供可扩展、灵活且高效的网络数据包处理能力&#xff1b;由于传统Linux 内核协议栈整体网络吞吐性能的局限性&#xff0c;…

【Linux】环境基础开发工具的使用之gcc详解(二)

前言&#xff1a;上一篇文章中我们讲解了Linux下的vim和yum的工具的使用&#xff0c;今天我们将在上一次的基础上进一步的讲解开放工具的时候。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:Linux的深度刨析 &#x1f448; &#x1f4a…

ESU毅速丨为什么增材制造广受关注?

随着科技的飞速发展&#xff0c;增材制造3D打印技术逐渐成为制造业的新宠。包括航空航天、汽车、家电、电子等各行业都在积极拥抱3D打印&#xff0c;为什么3D打印能引起制造业广泛关注与应用&#xff1f;它的主要优势有哪些&#xff1f; 首先&#xff0c;3D打印减少浪费。3D打印…

【Linux】 Linux编译器-gcc/g++使用

&#x1f497;个人主页&#x1f497; ⭐个人专栏——Linux学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读1. Linux编译器-gcc/g使用1.1 引入1.2 初识gcc/g1.3 程序运行的四个阶段1.3.1 预处理1.3.2 编译1.3.3 汇编1.3.4 链接 1.…

【2024.2.1练习】岛屿个数(15分)

题目描述 题目思路 题目乍一看类似于“水坑个数问题”&#xff0c;是对陆地块进行DFS,上下左右4个方向分别对应了四种状态转移&#xff0c;每块陆地进行搜索后变成海洋&#xff0c;最终搜索次数就是岛屿个数。 但在子岛屿存在的情况下&#xff0c;需要先对海洋块进行DFS,由于…

binder android

文心一言的回答 Binder驱动是Android操作系统中用于进程间通信&#xff08;IPC&#xff09;的机制。它提供了一种高效、跨进程的数据传输方式&#xff0c;使得应用程序的不同组件可以在Android系统上互相通信。 Binder驱动基于Linux内核&#xff0c;其核心组件是一个称为Bind…

基于最新koa的Node.js后端API架构与MVC模式

Koa 是一个由 Express 原班人马打造的现代 Web 框架&#xff0c;用于 Node.js。它旨在提供一个更小、更富有表现力和更强大的基础&#xff0c;用于 Web 应用和 API 开发。Koa 不捆绑任何中间件&#xff0c;它提供了一个优雅的方法以组合不同的中间件来处理请求和响应。 Koa 的核…

2024年哪款便签软件是手机电脑同步的?

在繁忙的生活、工作和学习中&#xff0c;我们时常面临各种琐事和任务&#xff0c;需要随时记录、提醒&#xff0c;以保持高效的生活节奏。比如&#xff0c;突然想到的灵感、重要的工作计划、紧急的购物清单&#xff0c;都需要一个便利的便签工具来随手记录。特别是在多终端使用…

vue2 el-form 两个时间框(第一个时间框是只能选择当前时间,之前的是不允许选择,第二个时间框是第一个时间的当前的时间后30天后可以选择的)

<template><div id"app"><el-form :model"form"><el-form-item label"预签时间"><el-date-picker v-model"form.t2" type"date" placeholder"选择预签时间" changepreSigning/><…

RT-DETR算法优化改进:上采样算子 | 超轻量高效动态上采样DySample,效果秒杀CAFFE,助力小目标检测

💡💡💡本文独家改进:一种超轻量高效动态上采样DySample, 具有更少的参数、FLOPs,效果秒杀CAFFE和YOLOv8网络中的nn.Upsample 💡💡💡在多个数据集下验证能够涨点,尤其在小目标检测领域涨点显著。 RT-DETR魔术师专栏介绍: https://blog.csdn.net/m0_63774211/…

C++ 音视频流媒体浅谈

C流媒体开发 今天就浅浅聊一下C流媒体开发 流媒体开发中最常见的是FFmpeg&#xff08;编解码器&#xff09; 业务逻辑主要是播放器了&#xff08;如腾旭视频 爱奇艺等等&#xff09; FFmpeg是一个开源的音视频处理工具集&#xff0c;可以用于处理、转换和流媒体传输音视频…

广州标点医药信息-米内网数据及咨询服务企业全方位解析!

米内网&#xff08;MENET&#xff09;原名中国医药经济信息网&#xff0c;由广州标点医药信息股份有限公司主办&#xff0c;2010年10月28日更名为“米内网”&#xff0c;上线初始主打医药销售数据库&#xff0c;经过十余年的迭代发展&#xff0c;现已成为国内主要的医药健康信息…

计网——应用层

应用层 应用层协议原理 网络应用的体系结构 客户-服务器&#xff08;C/S&#xff09;体系结构 对等体&#xff08;P2P&#xff09;体系结构 C/S和P2P体系结构的混合体 客户-服务器&#xff08;C/S&#xff09;体系结构 服务器 服务器是一台一直运行的主机&#xff0c;需…

数据结构·复杂度讲解

1. 什么是数据结构 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合。 数据结构是用来在内存中管理数据的&#xff0c;类似的&#xff0c;我们熟悉的文件或数据库是在硬盘中管理数据的。内存中的数据是带点…

【2024美赛】B题(中英文):搜寻潜水器Problem B: Searching for Submersibles

【2024美赛】B题&#xff08;中英文&#xff09;&#xff1a;搜寻潜水器Problem B: Searching for Submersibles 写在最前面2024美赛翻译 —— 跳转链接 中文赛题问题B&#xff1a;搜寻潜水器你的任务是&#xff1a; 词汇表 英文赛题Problem B: Searching for SubmersiblesYour…