当前位置: 首页 > news >正文

2023年的深度学习入门指南(22) - 百川大模型13B的运行及量化

2023年的深度学习入门指南(22) - 百川大模型13B的运行及量化

不知道上一讲的大段代码大家看晕了没有。但是如果你仔细看了会发现,其实代码还是不全的。比如分词器我们就没讲。
另外,13B比7B的改进点也没有讲。

再有,对于13B需要多少显存我们也没说。13B光是模型加载就需要26GB的显存,加上推理需要的消i耗,没有个28GB以上的显存是比较悬的。恰好24GB的3090和4090单卡不够用。

我们先从应用讲起。

百川13b的命令行交互

百川官方在13b的开源代码中给我们提供了命令行交互式的应用和Web服务的基本框架。

我们先来看看命令行交互式的应用。

import os
import torch
import platform
from colorama import Fore, Style
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation.utils import GenerationConfigdef init_model():print("init model ...")model = AutoModelForCausalLM.from_pretrained("baichuan-inc/Baichuan-13B-Chat",torch_dtype=torch.float16,device_map="auto",trust_remote_code=True)model.generation_config = GenerationConfig.from_pretrained("baichuan-inc/Baichuan-13B-Chat")tokenizer = AutoTokenizer.from_pretrained("baichuan-inc/Baichuan-13B-Chat",use_fast=False,trust_remote_code=True)return model, tokenizerdef clear_screen():if platform.system() == "Windows":os.system("cls")else:os.system("clear")print(Fore.YELLOW + Style.BRIGHT + "欢迎使用百川大模型,输入进行对话,clear 清空历史,CTRL+C 中断生成,stream 开关流式生成,exit 结束。")return []def main(stream=True):model, tokenizer = init_model()messages = clear_screen()while True:prompt = input(Fore.GREEN + Style.BRIGHT + "\n用户:" + Style.NORMAL)if prompt.strip() == "exit":breakif prompt.strip() == "clear":messages = clear_screen()continueprint(Fore.CYAN + Style.BRIGHT + "\nBaichuan:" + Style.NORMAL, end='')if prompt.strip() == "stream":stream = not streamprint(Fore.YELLOW + "({}流式生成)\n".format("开启" if stream else "关闭"), end='')continuemessages.append({"role": "user", "content": prompt})if stream:position = 0try:for response in model.chat(tokenizer, messages, stream=True):print(response[position:], end='', flush=True)position = len(response)if torch.backends.mps.is_available():torch.mps.empty_cache()except KeyboardInterrupt:passprint()else:response = model.chat(tokenizer, messages)print(response)if torch.backends.mps.is_available():torch.mps.empty_cache()messages.append({"role": "assistant", "content": response})print(Style.RESET_ALL)if __name__ == "__main__":main()

调用模型的部分大家都比较熟悉了,这里唯一值得说一说的反而是显示格式相关的colorama库。

    print(Fore.YELLOW + Style.BRIGHT + "欢迎使用百川大模型,输入进行对话,clear 清空历史,CTRL+C 中断生成,stream 开关流式生成,exit 结束。")
...prompt = input(Fore.GREEN + Style.BRIGHT + "\n用户:" + Style.NORMAL)

系统提示为黄色,而用户输入为绿色,百川的回复为青色。

看起来百川的同学是写过前端的,都用一个颜色太乱忍不了。:)

安装时别忘了安装colorama库。或者按下面的列表装全了吧:

pip install transformers
pip install sentencepiece
pip install accelerate
pip install transformers_stream_generator
pip install colorama
pip install cpm_kernels
pip install streamlit

百川13b的Web服务demo

百川的Web demo里,关于模型的调用部分还是没啥可讲的。
但是,Streamlit的前端有必要简单说一下。
Streamlit封装了很多常用的前端组件,比如对话这样的高级组件,就是用st.chat_message()来实现的。

我们来看个例子:

import streamlit as stwith st.chat_message("assistant", avatar='🤖'):st.markdown("您好,我是百川大模型,很高兴为您服务🥰")

我们把上面的文件存为test1.py,然后在命令行运行:

streamlit run test1.py

运行之后,会自动打开浏览器,看到如下界面:

with st.chat_message("assistant", avatar='🤖'):

这一行创建了一个聊天消息的上下文管理器,消息的发送者是 “assistant”,并且使用了一个机器人表情作为头像(‘🤖’)。

    st.markdown("您好,我是百川大模型,很高兴为您服务🥰")

这行代码在上述的 “assistant” 聊天消息中添加了一段 Markdown 格式的文本。

好,下面我们把用户输入的功能加进来,使用st.chat_input()就可以实现,不需要写javascript代码:

import streamlit as stwith st.chat_message("assistant", avatar='🤖'):st.markdown("您好,我是百川大模型,很高兴为您服务🥰")if prompt := st.chat_input("Shift + Enter 换行, Enter 发送"):with st.chat_message("user", avatar='🧑‍💻'):st.markdown(prompt)

运行效果如下:

我们可以进一步给页面加上标题和属性:

import streamlit as stst.set_page_config(page_title="Baichuan-13B-Chat")
st.title("Baichuan-13B-Chat")with st.chat_message("assistant", avatar='🤖'):st.markdown("您好,我是百川大模型,很高兴为您服务🥰")if prompt := st.chat_input("Shift + Enter 换行, Enter 发送"):with st.chat_message("user", avatar='🧑‍💻'):st.markdown(prompt)

理解了上面的基础知识之后,我们就直接看百川的代码吧:

import json
import torch
import streamlit as st
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation.utils import GenerationConfigst.set_page_config(page_title="Baichuan-13B-Chat")
st.title("Baichuan-13B-Chat")@st.cache_resource
def init_model():model = AutoModelForCausalLM.from_pretrained("baichuan-inc/Baichuan-13B-Chat",torch_dtype=torch.float16,device_map="auto",trust_remote_code=True)model.generation_config = GenerationConfig.from_pretrained("baichuan-inc/Baichuan-13B-Chat")tokenizer = AutoTokenizer.from_pretrained("baichuan-inc/Baichuan-13B-Chat",use_fast=False,trust_remote_code=True)return model, tokenizerdef clear_chat_history():del st.session_state.messagesdef init_chat_history():with st.chat_message("assistant", avatar='🤖'):st.markdown("您好,我是百川大模型,很高兴为您服务🥰")if "messages" in st.session_state:for message in st.session_state.messages:avatar = '🧑‍💻' if message["role"] == "user" else '🤖'with st.chat_message(message["role"], avatar=avatar):st.markdown(message["content"])else:st.session_state.messages = []return st.session_state.messagesdef main():model, tokenizer = init_model()messages = init_chat_history()if prompt := st.chat_input("Shift + Enter 换行, Enter 发送"):with st.chat_message("user", avatar='🧑‍💻'):st.markdown(prompt)messages.append({"role": "user", "content": prompt})print(f"[user] {prompt}", flush=True)with st.chat_message("assistant", avatar='🤖'):placeholder = st.empty()for response in model.chat(tokenizer, messages, stream=True):placeholder.markdown(response)if torch.backends.mps.is_available():torch.mps.empty_cache()messages.append({"role": "assistant", "content": response})print(json.dumps(messages, ensure_ascii=False), flush=True)st.button("清空对话", on_click=clear_chat_history)if __name__ == "__main__":main()

量化

如果想要在消费级的单卡上运行百川13b的推理,需要对模型进行量化。

百川13b支持8位和4位的量化。8位量化之后需要18.6G以上的显存。4位量化之后需要11.5GB以上的显存。同时,CPU在实现量化的时候需要36.1G的内存,32G的不太够用。

我们先看下8位量化的例子:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation.utils import GenerationConfig
tokenizer = AutoTokenizer.from_pretrained("baichuan-inc/Baichuan-13B-Chat", use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("baichuan-inc/Baichuan-13B-Chat", torch_dtype=torch.float16, trust_remote_code=True)
model.generation_config = GenerationConfig.from_pretrained("baichuan-inc/Baichuan-13B-Chat")
model = model.quantize(8).cuda()
messages = []
messages.append({"role": "user", "content":"亚历山大的骑兵为什么强大?"})
response = model.chat(tokenizer, messages)
print(response)

输出如下:

亚历山大大帝的骑兵之所以强大,主要有以下几个原因:1. 马匹质量高:亚历山大所处的马其顿地区盛产优质战马,这些马匹体型高大、速度快、耐力强,非常适合进行战斗。这使得他的骑兵在战场上具有很高的机动性和冲击力。2. 训练有素:亚历山大的骑兵经过严格的训练,能够熟练地使用武器和战术。他们不仅擅长冲锋陷阵,还能够在战场上灵活地进行迂回、包抄等行动,对敌军造成严重打击。3. 装备精良:亚历山大的骑兵装备了当时最先进的武器和护具,如长矛、弓箭、盾牌等。这些武器既能有效保护士兵,又能给予敌人沉重的打击。此外,他们还配备了马镫,使骑士在马背上更加稳定,提高了战斗效率。4. 严密的组织和指挥:亚历山大的骑兵在战场上有严密的组织和指挥体系。他们通过旗帜、号角等方式进行通信,确保部队之间的协同作战。同时,亚历山大本人作为统帅,对骑兵战术有着深刻的理解,能够根据战场情况制定合适的战略。5. 强大的心理素质:亚历山大的骑兵拥有极高的心理素质,他们在战场上勇敢无畏,敢于面对任何困难。这种精神力量使得他们在战斗中始终保持旺盛的斗志,成为一支不可小觑的力量。综上所述,亚历山大的骑兵之所以强大,是因为他们拥有高质量的马匹、训练有素的士兵、精良的装备、严密的组织和卓越的领导。这些因素共同铸就了一支强大的骑兵部队,使得亚历山大大帝能够征服整个已知世界。

效果看来仍然不错哈。

如果想要使用4位量化,将model = model.quantize(8).cuda()改为model = model.quantize(4).cuda()即可:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation.utils import GenerationConfig
tokenizer = AutoTokenizer.from_pretrained("baichuan-inc/Baichuan-13B-Chat", use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("baichuan-inc/Baichuan-13B-Chat", torch_dtype=torch.float16, trust_remote_code=True)
model.generation_config = GenerationConfig.from_pretrained("baichuan-inc/Baichuan-13B-Chat")
model = model.quantize(4).cuda()
messages = []
messages.append({"role": "user", "content":"亚历山大大帝的骑兵为什么强大?"})
response = model.chat(tokenizer, messages)
print(response)

输出如下:

亚历山大(Alexander the Great)的骑兵之所以强大,主要原因有以下几点:1. 训练和纪律:亚历山大的军队以严格的训练和高水平的纪律著称。他的士兵接受了高度专业的军事训练,特别是在马术、射击技巧和战场战术方面。这使得他们在战场上具有很高的机动性和战斗力。2. 马匹质量:亚历山大的骑兵使用的是高品质的战马,这些马匹经过精挑细选,具备出色的速度、耐力和力量。这些马匹在战场上的表现优于其他国家的马匹,使他们能够快速移动并有效地执行任务。3. 装备精良:亚历山大的骑兵配备了先进的武器和盔甲,如长矛、弓箭和护胸甲等。这些装备不仅提高了他们的战斗力,还降低了伤亡率。4. 战略优势:亚历山大的骑兵在战争中发挥了重要作用,尤其是在对付敌军步兵时。他们的高速度和机动性使他们能够迅速突破敌人的防线,为步兵提供支援。此外,骑兵还可以用于侦查敌情、切断补给线以及进行骚扰作战。5. 领导力:亚历山大的领导才能和卓越指挥使他的军队士气高涨。他的士兵们对他充满信心,愿意为他出生入死。这种紧密的团队精神和忠诚使得亚历山大的骑兵在战场上具有强大的凝聚力和战斗力。综上所述,亚历山大的骑兵之所以强大,是因为他们拥有高素质的士兵、优良的马匹、精良的装备、有效的战略以及卓越的领导力。这些因素共同铸就了他们无与伦比的战斗力,使他们在历史上留下了深刻的印记。

看起来也还不错哈。

量化的实现

我们来看下量化的实现,在modeling_baichuan.py中的quantize其实就是把W,o和mlp的每一层都量化掉。

    def quantize(self, bits: int):try:from .quantizer import QLinearexcept ImportError:raise ImportError(f"Needs QLinear to run quantize.")for layer in self.model.layers:layer.self_attn.W_pack = QLinear(bits=bits,weight=layer.self_attn.W_pack.weight,bias = None,)layer.self_attn.o_proj = QLinear(bits=bits,weight=layer.self_attn.o_proj.weight,bias = None,)layer.mlp.gate_proj = QLinear(bits=bits,weight=layer.mlp.gate_proj.weight,bias = None,)layer.mlp.down_proj = QLinear(bits=bits,weight=layer.mlp.down_proj.weight,bias = None,)layer.mlp.up_proj = QLinear(bits=bits,weight=layer.mlp.up_proj.weight,bias = None,)return self

我们继续看下QLinear的实现,其实就是把权重和偏置量化掉,然后在forward的时候,把输入也量化掉,然后再做矩阵乘法,最后再反量化回去。

在构造函数中,首先将 bits 参数保存到 self.quant_bits 属性中。然后计算量化所需的缩放因子 self.scale。这个缩放因子是通过将权重矩阵的绝对值取最大值,然后除以 (2 ** (bits - 1)) - 1) 来计算的。接下来,根据量化位数的不同,使用不同的方法对权重矩阵进行量化。如果量化位数为 4,则调用 quant4 函数进行量化;如果量化位数为 8,则使用四舍五入方法进行量化。最后,将偏置项设置为 None。

class QLinear(torch.nn.Module):def __init__(self, bits: int, weight: torch.Tensor, bias=None):super().__init__()self.quant_bits = bitsself.scale = weight.abs().max(dim=-1).values / ((2 ** (bits - 1)) - 1)self.scale = self.scale.to(torch.float32)if self.quant_bits == 4:self.weight = quant4(weight, self.scale)elif self.quant_bits == 8:self.weight = torch.round(weight.to(self.scale.dtype) / self.scale[:, None]).to(torch.int8)if self.quant_bits == 8:self.weight = self.weight.Tself.bias = None

这个类还定义了一个名为 forward 的方法,它接受一个名为 input 的参数。这个方法首先检查输入张量的数据类型是否符合要求,并将权重矩阵和缩放因子转移到输入张量所在的设备上。然后根据量化位数的不同,使用不同的方法对权重矩阵进行反量化,并与输入张量进行矩阵乘法运算。如果偏置项不为 None,则将其加到输出张量上。最后返回输出张量。

    def forward(self, input):if self.quant_bits == 4:assert(input.dtype == torch.bfloat16 or input.dtype == torch.float16)            if self.weight.device != input.device:self.weight = self.weight.to(input.device)self.scale = self.scale.to(input.device)if self.quant_bits == 4:self.scale = self.scale.to(input.dtype)rweight = dequant4(self.weight, self.scale, input).Toutput = torch.matmul(input, rweight)elif self.quant_bits == 8:rweight = self.weight.to(input.dtype) * self.scale.to(input.dtype)output = torch.matmul(input, rweight)if self.bias is not None:output = output + self.biasreturn output

量化的原理我们之前已经讲过了,我们来看4位量化的实现,我还是把注释写在代码行里:

def quant4(weight: torch.Tensor, scale: torch.Tensor):stream = torch.cuda.current_stream()num_row = weight.size(0)num_chan_fp16 = weight.size(1)# 4bitnum_chan_int = num_chan_fp16 // 8qweight = torch.zeros((num_row, num_chan_int), dtype=torch.int32, device=weight.device)intweight = torch.empty(num_row, num_chan_fp16, dtype = torch.int32)# 将权重张量除以比例因子、四舍五入、裁剪在 [-16, 15] 范围内,然后转换为 32 位整数intweight = torch.clip(torch.round(weight.to(scale.dtype) / scale[:, None]),-16, 15).to(dtype=torch.int32) # 使用位操作(位移和位与)将 8 个 4 位整数打包到一个 32 位整数中for j in range(num_chan_int):qweight[:, j] = ((intweight[:, j*8+7] & 0x0f) << 28) \| ((intweight[:, j*8+6] & 0x0f) << 24) \| ((intweight[:, j*8+5] & 0x0f) << 20) \| ((intweight[:, j*8+4] & 0x0f) << 16) \| ((intweight[:, j*8+3] & 0x0f) << 12) \| ((intweight[:, j*8+2] & 0x0f) << 8) \| ((intweight[:, j*8+1] & 0x0f) << 4) \| ((intweight[:, j*8] & 0x0f))return qweight

小结

这一节我们进一步了解了百川13b大模型运行和量化的方法,以及简要介绍了量化的原理。

相关文章:

2023年的深度学习入门指南(22) - 百川大模型13B的运行及量化

2023年的深度学习入门指南(22) - 百川大模型13B的运行及量化 不知道上一讲的大段代码大家看晕了没有。但是如果你仔细看了会发现&#xff0c;其实代码还是不全的。比如分词器我们就没讲。 另外&#xff0c;13B比7B的改进点也没有讲。 再有&#xff0c;对于13B需要多少显存我们…...

无涯教程-jQuery - empty( )方法函数

empty()方法删除所有匹配的子节点。 empty( ) - 语法 selector.empty( ) empty( ) - 示例 以下是一个简单的示例&#xff0c;简单说明了此方法的用法- <html><head><title>The jQuery Example</title><script type"text/javascript"…...

微信小程序实现蓝牙开锁、开门、开关、指令发送成功,但蓝牙设备毫无反应、坑

文章目录 开源htmlJavaScript 开源 wx联系本人获取源码(开源): MJ682517 html <view><view class"p_l_36 p_r_36"><input class"w_100_ h_80 lh_80 ta_c b_2s_eee radius_20" value"{{instructVal}}" type"text" plac…...

微信小程序中使用echarts方法

小程序中使用echarts echarts是一个基于JS的数据可视化图标库&#xff0c;它提供了直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性定制的数据可视化图表。一般在vue中会使用到&#xff0c;并且官网也详细的说明了如何在vue中使用&#xff0c;但是今天我想来探讨的…...

【面试题】前端中 JS 发起的请求可以暂停吗?

这个问题非常有意思&#xff0c;我一看到就想了很多可以回复的答案&#xff0c;但是评论区太窄&#xff0c;就直接开一篇文章来写了。 审题 JS 发起的请求可以暂停吗&#xff1f;这一句话当中有两个概念需要明确&#xff0c;一是什么样的状态才能称之为 暂停&#xff1f;二是…...

通过社区参与解锁早期增长:Maven 远程医疗平台概览

Maven通过用户导向的渐进式验证&#xff0c;找到了一个被忽视的巨大女性医疗服务市场&#xff0c;作为女性医疗保健的先行者&#xff0c;已服务超过1500万用户&#xff0c;目前估值已达$14亿。本文将深入探索Maven实现产品市场匹配的三个阶段&#xff0c;从如何验证初始的市场机…...

Vue中使用echarts

1 安装 npm install -g cnpm --registryhttps://registry.npm.taobao.org cnpm install echarts -S 2 main.js引入 3 引入成功...

边缘计算对现代交通的重要作用

边缘计算之所以重要&#xff0c;是在于即使在5G真正商用之时&#xff0c;可以实现超大带宽&#xff08;eMBB&#xff09;的应用场景&#xff0c;但庞大数据量的涌现也就意味着需要在云和端传输过程中找到一个承接点&#xff0c;对数据进行预处理再选择是否上云。 边缘计算应用演…...

Python桥接模式介绍、使用

一、Python桥接模式介绍 概念&#xff1a; Python桥接模式&#xff08;Bridge Pattern&#xff09;是一种软件设计模式&#xff0c;用于将抽象部分与其实现部分分离&#xff0c;使它们可以独立地变化。 它可以通过使用桥接接口来创建一个桥接对象来连接抽象和实现部分。 功能…...

ChatGPT在知识图谱的构建和更新中的应用如何?

ChatGPT在知识图谱的构建和更新中具有重要的应用潜力。知识图谱是一种用于表示和存储结构化知识的图形化数据模型&#xff0c;通常由实体、属性和关系组成。知识图谱的构建和更新是一个复杂而关键的过程&#xff0c;涉及知识的收集、组织、链接和推理。ChatGPT作为一种预训练语…...

JS正则表达式:常用正则手册/RegExp/正则积累

一、正则基础语法 JavaScript 正则表达式 | 菜鸟教程 JS正则表达式语法大全&#xff08;非常详细&#xff09; 二、使用场景 2.1、校验中国大陆手机号的正则表达式 正则 /^1[3456789]\d{9}$/解释 序号正则解释1^1以数字 1 开头2[3456789]第二位可以是 3、4、5、6、7、8、…...

自动化测试框架unittest与pytest的区别!

引言 前面文章已经介绍了python单元测试框架&#xff0c;大家平时经常使用的是unittest&#xff0c;因为它比较基础&#xff0c;并且可以进行二次开发&#xff0c;如果你的开发水平很高&#xff0c;集成开发自动化测试平台也是可以的。而这篇文章主要讲unittest与pytest的区别&…...

【Git】

学习来自于&#xff1a; 女朋友乱用Git&#xff0c;差点把我代码删了。。。 一些常用的Git 知识点整理 关于Git这一篇就够了 Git基本命令大全 30分钟精通Git&#xff0c;学不会来找我 Git 版本管理 | 莫烦PYTHON Git 代码版本管理教程 文章目录 【前言】集中式与分布式的…...

[论文笔记] CLRerNet: Improving Confidence of Lane Detection with LaneIoU

Honda, Hiroto, and Yusuke Uchida. “CLRerNet: Improving Confidence of Lane Detection with LaneIoU.” arXiv preprint arXiv:2305.08366 (2023). 2023.05 出的一篇车道线检测的文章, 效果在CULane, CurveLanes SOTA 文章目录 简介LaneIoULineIoU存在问题为什么使用LaneIo…...

LeetCode|backtracking|review:40. 131. 93. 47. 332. | 37. Sudoku Solver

复习&#xff1a; 40. Combination Sum II [1,1,2,3]中&#xff0c;答案里有[1,1,2], 但是不能有两个[1,2,3] 131. Palindrome Partitioning 每个for都是在给定的start之后找一个palindrome。当start 93. Restore IP Addresses forloop每次loop都是在给定的start的后三个数…...

被泼冷水后,谁能超越微服务?

历史总会重演。一切刚过去的&#xff0c;又会被重新提起。开源项目Codename One的联合创始人Shai&#xff0c;曾是Sun Microsystems开源LWUIT项目的共同作者&#xff0c;参与了无数开源项目。作为最早一批Java开发者&#xff0c;最近感慨道&#xff1a;单体&#xff0c;又回来了…...

多线程(JavaEE初阶系列5)

目录 前言&#xff1a; 1.什么是定时器 2.标准库中的定时器及使用 3.实现定时器 结束语&#xff1a; 前言&#xff1a; 在上一节中小编给大家介绍了多线程中的两个设计模式&#xff0c;单例模式和阻塞式队列模式&#xff0c;在单例模式中又有两种实现方式一种是懒汉模式&a…...

Minimum Snap闭式求解相关公式推导

文章目录 1 M i n i m u m Minimum Minimum S n a p Snap Snap闭式求解的推导1.1 二次规划等式约束构建1.2 求 d d d1.3 转成无约束优化问题 1 M i n i m u m Minimum Minimum S n a p Snap Snap闭式求解的推导 可以看看我的这几篇Blog1&#xff0c;Blog2&#xff0c;Blog…...

Spring源码(五)— 解析XML配置文件(一) bean标签解析流程

前面几章的内容已经介绍了BeanFactory创建前的准备工作&#xff0c;以及加载XML配置文件前的准备的步骤。本章会着重介绍解析XML的步骤。 registerBeanDefinitions 前几个方法不做过多的赘述&#xff0c;着重看registerBeanDefinitions方法中解析XML的步骤。 public int regi…...

隐私政策声明

http://lxfamn.cn/tools 我们注重对您个人隐私的保护。有时候我们需要某些信息才能为您提供您请求的服务&#xff0c;本隐私声明解释了这些情况下的数据收集和使用情况。本隐私声明适用于本网站的所有相关服务。如果您访问本网站、使用本网站的任何服务&#xff0c;那么您便接受…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...