Transformers实战05-模型量化
文章目录
- 简介
- 主要类型
- 量化的优点
- 量化的缺点
- 量化过程
- 量化过程
- 反量化过程
- 精度和参数
- 量化实例
- bitsandbytes
- 安装bitsandbytes
- 4bit量化(加载)
- 8bit量化(加载)
- 验证效果
简介
模型量化(Model Quantization)是一种优化技术,旨在减少机器学习模型的计算资源需求和存储空间,同时在精度损失最小化的前提下提高推理效率。量化通过将模型权重和激活函数的数值从高精度(如 32 位浮点数)转换为低精度(如 8 位整数),显著减少了模型大小和计算复杂度。
主要类型
-
静态量化(Post-Training Quantization, PTQ)
- 在模型训练完成后进行量化。
- 通过分析训练数据的分布,将权重和激活函数映射到低精度表示。
- 不需要重新训练模型。
- 适用于对性能影响较小的场景。
-
动态量化(Dynamic Quantization)
- 在推理时动态地将浮点数转换为低精度整数。
- 在运行过程中对激活函数进行量化。
- 比静态量化更简单,因为不需要分析训练数据。
- 对推理速度有显著提升,尤其是对模型输入依赖较少的层(如全连接层)。
-
量化感知训练(Quantization-Aware Training, QAT)
- 在训练过程中模拟量化影响。
- 模型在训练过程中考虑量化误差,以便在量化后保持更高的精度。
- 比静态量化和动态量化需要更多的计算资源,但精度损失最小。
- 适用于对精度要求较高的应用。
这里例子就演示下动态量化,bitsandbytes本身以上三种都支持。
量化的优点
- 减小模型大小:通过将权重和激活函数表示从 32 位浮点数转换为 8 位整数,模型大小可以显著减少。
- 加快推理速度:低精度运算速度更快,可以显著提高推理效率。
- 降低内存带宽需求:低精度表示占用更少的内存,减少了内存带宽的需求。
量化的缺点
- 精度损失:由于数值表示的精度降低,模型可能会经历一定程度的精度损失,具体程度取决于模型结构和数据分布。
- 复杂性增加:在某些情况下,量化过程可能会增加模型部署的复杂性,尤其是需要进行量化感知训练时。
量化过程
以下过程只是一种最简单的思路,方便理解,实际要比这更复杂。
量化过程
-
确定值域: 首先,确定要量化的数据的值域范围。例如,假设我们有一组数据的值域为 [ m i n , m a x ] [min,max] [min,max]。
-
确定量化级别: 确定量化的级别或分辨率,这决定了将值域划分成多少个区间。在4位整数的情况下,共有 2 4 = 16 2^4=16 24=16 个可能的值。
-
线性映射: 将原始数据映射到4位整数的范围内。通常使用线性映射来实现,计算公式如下:
quantized_value = original_value − min max − min × ( number of levels − 1 ) \text{quantized\_value} = \frac{\text{original\_value} - \text{min}}{\text{max} - \text{min}} \times (\text{number of levels} - 1) quantized_value=max−minoriginal_value−min×(number of levels−1) -
这里的 number of levels 是16(对应4位整数的值域范围)。
反量化过程
解码反量化: 在使用量化数据进行计算之前,需要将其解码回原始的数据表示形式(如32位浮点数或其他高精度表示)。解码公式通常为:
original_value = quantized_value × max − min number of levels − 1 + min \text{original\_value} = \text{quantized\_value} \times \frac{\text{max} - \text{min}}{\text{number of levels} - 1} + \text{min} original_value=quantized_value×number of levels−1max−min+min
这里的 quantized_value是是量化后的4位整数值,min和max是原始数据的最小值和最大值。
两个不同的原始值在量化后可能相同,被还原为同一个值。这种情况表明精度损失是不可避免的。为了减少这种精度损失带来的影响,通常采取以下策略:
-
增加量化级别: 增加量化级别(如使用8位、16位量化)以减少不同原始值被量化为同一个值的概率。
-
量化感知训练(Quantization-aware training): 在训练过程中模拟量化误差,以提高模型在量化后的精度表现。
-
非线性量化: 使用对数量化或其他非线性量化方法,使得量化更适应数据的分布特性,从而减少精度损失。
-
精细调节量化参数: 通过精细调整量化的最小值、最大值和比例因子,尽量减少量化误差对关键值的影响。
精度和参数
模型中每个参数常见的存储类型包括:
- FP32(32-bit Floating Point): 每个参数占用 4 字节(32 位),单精度浮点数(32位浮点数),范围大约: [ − 3.4 × 1 0 38 , 3.4 × 1 0 38 ] [-3.4 \times 10^{38}, 3.4 \times 10^{38}] [−3.4×1038,3.4×1038]。
- FP16(16-bit Floating Point): 每个参数占用 2 字节(16 位),半精度浮点数使用16位(1位符号、5位指数、10位尾数),FP16的数值范围大约是 [−65504,65504],大约 3 位有效数字。
- INT8(8-bit Integer): 每个参数占用 1 字节(8 位),将模型的权重和激活值量化为8位整数(范围通常是0到255),相对于32位浮点数,精度的损失较小。8-bit量化比4-bit提供更好的精度,并且通常可以更接近原始模型的性能。
- INT4(4-bit Integer): 每个参数占用4位,将模型的权重和激活值量化为4位整数(范围通常是-8到7或者0到15),因此相对于32位浮点数,它的精度显著降低。这种量化可以显著减小模型的大小和计算需求,但可能会损失一定的模型精度。
如何获取某个模型的精度了
import torch
from transformers import AutoModel, BertTokenizer
model_name="bert-base-chinese" #bert-base-uncased
model=AutoModel.from_pretrained(model_name)
#获取模型参数的精度
"""FP32(32-bit Floating Point): 每个参数占用 4 字节(32 位)。FP16(16-bit Floating Point): 每个参数占用 2 字节(16 位)。INT8(8-bit Integer): 每个参数占用 1 字节(8 位)。
"""
dtype=list(model.parameters())[0].dtype
print("精度:",dtype)
total_params = sum(p.numel() for p in model.parameters())
dtype_to_bytes = {torch.float32: 4, # FP32: 4字节torch.float16: 2, # FP16: 2字节torch.int8: 1, # INT8: 1字节torch.int32: 4, # INT32: 4字节torch.int64: 8, # INT64: 8字节torch.float64: 8, # FP64 (double): 8字节
}
model_size = total_params * dtype_to_bytes[dtype]
print(f'Model size: {model_size / (1024**2):.2f} MB')
输出
精度: torch.float32
Model size: 390.12 MB
量化实例
bitsandbytes
bitsandbytes 通过 PyTorch 的 k 位量化技术使大型语言模型的访问变得可行。bitsandbytes 提供了三个主要功能以显著降低推理和训练时的内存消耗:
- 8 位优化器采用区块式量化技术,在极小的内存成本下维持 32 位的表现。
- LLM.Int() 或 8 位量化使大型语言模型推理只需一半的内存需求,并且不会有任何性能下降。该方法基于向量式的量化技术将大部分特性量化到 8 位,并且用 16 位矩阵乘法单独处理异常值。
- QLoRA 或 4 位量化使大型语言模型训练成为可能,它结合了几种节省内存的技术,同时又不牺牲性能。该方法将模型量化至 4 位,并插入一组可训练的低秩适应(LoRA)权重来允许训练。
安装bitsandbytes
bitsandbytes 仅支持 CUDA 版本 11.0 - 12.5 的 CUDA GPU。
!pip install -U bitsandbytes
!pip install transformers
!pip install accelerate
4bit量化(加载)
加载并量化一个模型到4位,并使用bfloat16数据类型进行计算:
您使用 bnb_4bit_compute_dtype=torch.bfloat16,这意味着计算过程中会反量化使用 bfloat16 数据类型,而存储时则可能使用4位表示。这解释了为什么您看到的 dtype 仍然是 fp16 或者 bfloat16。
BigScience 是一个全球性的开源AI研究合作项目,旨在推动大型语言模型(LLM)的发展。bloom-1b7 是 BigScience 项目下的一部分,具体来说,是一个包含约17亿参数的语言模型。
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
model_name="bigscience/bloom-1b7"
quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16)
model = AutoModelForCausalLM.from_pretrained(model_name,device_map="auto",
)
model_4bit = AutoModelForCausalLM.from_pretrained(model_name,device_map="auto",quantization_config=quantization_config,
)
dtype=list(model.parameters())[0].dtype
print("原始精度:",dtype)
dest_dtype=list(model_4bit.parameters())[0].dtype
print("量化精度:",dest_dtype)# 检查模型的量化配置
print("量化配置:", model_4bit.config.quantization_config)def print_model_info(model):total_params = 0for name, param in model.named_parameters():total_params += param.numel()#print(f"Total parameters: {total_params / 1e6}M")return total_paramstotal_model_size=print_model_info(model)
total_model_4bit_size=print_model_info(model_4bit)
print("模型参数个数:",total_model_size)
print("量化后的模型参数个数:",total_model_4bit_size)dtype_to_bytes = {torch.float32: 4, # FP32: 4字节torch.float16: 2, # FP16: 2字节torch.int8: 1, # INT8: 1字节torch.int32: 4, # INT32: 4字节torch.int64: 8, # INT64: 8字节torch.float64: 8, # FP64 (double): 8字节
}
model_size = total_model_size * dtype_to_bytes[dtype]
model_size = total_model_size * dtype_to_bytes[dtype]
print(f'origin Model size: {model_size / (1024**2):.2f} MB')
model_size = total_model_4bit_size * dtype_to_bytes[dest_dtype]
print(f'quan Model size: {model_size / (1024**2):.2f} MB')model_4bit.save_pretrained("/tmp/p")
model.save_pretrained("/tmp/o")
输出:
原始精度: torch.float32
量化精度: torch.float16
量化配置: BitsAndBytesConfig {"_load_in_4bit": true,"_load_in_8bit": false,"bnb_4bit_compute_dtype": "bfloat16","bnb_4bit_quant_storage": "uint8","bnb_4bit_quant_type": "fp4","bnb_4bit_use_double_quant": false,"llm_int8_enable_fp32_cpu_offload": false,"llm_int8_has_fp16_weight": false,"llm_int8_skip_modules": null,"llm_int8_threshold": 6.0,"load_in_4bit": true,"load_in_8bit": false,"quant_method": "bitsandbytes"
}模型参数信息: 1722408960
量化后的模型参数信息: 1118429184
origin Model size: 6570.47 MB
quan Model size: 2133.23 MB
总的参数个数减少。这通常是由于量化过程中进行了优化或者参数压缩的操作。
量化在深度学习中通常是指将模型中的浮点数参数转换为更低精度的整数或定点数表示,以节省内存和提高计算效率。
为啥量化和原模型的dtype都是fp16了,以下是对量化模型加载过程中 dtype
问题的一些解释:
-
参数存储与计算类型的区别:
- 存储时,模型参数可能被压缩或量化为较低位宽的整数类型(如4位整数)。
- 加载时,为了方便后续计算,这些参数可能会被解码为较高精度的浮点类型(如
fp16
或bfloat16
)。
-
量化过程的具体实现:
- 许多量化库在加载模型时,会将低位宽的量化参数解码为浮点类型,以便在计算时可以直接使用这些参数。
- 这就是为什么即使您使用了
load_in_4bit=True
,在加载后检查参数的dtype
时仍然看到的是fp16
。
通过查看模型保存的就可以确定了
查看量化的模型:
!ls /tmp/p -al --block-size=M | grep model
输出:
-rw-r--r-- 1 root root 1630M Aug 6 08:04 model.safetensors
可以看到我们之前在内存中打印的是2133.23(内存中计算还是会被反量化到bnb_4bit_compute_dtype指定类型,但是参数都是压缩后去掉了一些参数) ,存储后变成了1630M,比之前计算的少一些,说明存储使用了4bit。
在看下没有量化的模型:
!ls /tmp/o -al --block-size=M | grep model
输出了:
-rw-r--r-- 1 root root 4714M Aug 6 08:05 model-00001-of-00002.safetensors
-rw-r--r-- 1 root root 1857M Aug 6 08:05 model-00002-of-00002.safetensors
-rw-r--r-- 1 root root 1M Aug 6 08:05 model.safetensors.index.json
可以看到我们之前在内存中打印的是6570.47 MB ,存储后没变,分文件存储了4714M+1857M 。
8bit量化(加载)
代码和4bit相似,调整下配置即可
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
同4bit代码,输出
原始精度: torch.float32
量化精度: torch.float16
量化配置: BitsAndBytesConfig {"_load_in_4bit": false,"_load_in_8bit": true,"bnb_4bit_compute_dtype": "float32","bnb_4bit_quant_storage": "uint8","bnb_4bit_quant_type": "fp4","bnb_4bit_use_double_quant": false,"llm_int8_enable_fp32_cpu_offload": false,"llm_int8_has_fp16_weight": false,"llm_int8_skip_modules": null,"llm_int8_threshold": 6.0,"load_in_4bit": false,"load_in_8bit": true,"quant_method": "bitsandbytes"
}模型参数信息: 1722408960
量化后的模型参数信息: 1722408960
origin Model size: 6570.47 MB
quan Model size: 3285.23 MB
可以看到8bit不需要指定内存计算的类型,量化内存计算精度默认就是fp16。
查看模型保存大小
!ls /tmp/p -al --block-size=M | grep model
#----------------------------------------------------------------------------------------------------
!ls /tmp/o -al --block-size=M | grep model
输出
-rw-r--r-- 1 root root 2135M Aug 6 08:30 model.safetensors
#----------------------------------------------------------------------------------------------------
-rw-r--r-- 1 root root 4714M Aug 6 08:30 model-00001-of-00002.safetensors
-rw-r--r-- 1 root root 1857M Aug 6 08:31 model-00002-of-00002.safetensors
-rw-r--r-- 1 root root 1M Aug 6 08:31 model.safetensors.index.json
验证效果
这里用之前的4bit模型来和原始模型比较
import timedef benchmark_model(model, input_text, tokenizer):inputs = tokenizer(input_text, return_tensors="pt").to(model.device)start_time = time.time()with torch.no_grad():outputs = model.generate(**inputs)# 解码并打印生成的文本generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)print("Generated text:", generated_text)end_time = time.time()inference_time = end_time - start_timeprint(f"Inference time: {inference_time:.2f} seconds")from transformers import AutoTokenizertokenizer = AutoTokenizer.from_pretrained(model_name)
input_text = "Hello, how are you?"
print("未量化模型性能测试:")
benchmark_model(model, input_text, tokenizer)
print("量化模型性能测试:")
benchmark_model(model_4bit, input_text, tokenizer)
输出
未量化模型性能测试:
Generated text: Hello, how are you? I hope you are doing well. I am a newbie in this
Inference time: 0.31 seconds
量化模型性能测试:
Generated text: Hello, how are you?"
"I'm fine," I said.
"I'm just a
Inference time: 0.62 seconds
这里看到量化的模型反而推理需要更多的时间,量化模型在理论上应该提高推理速度和减少内存占用,但在实际应用中,可能会因为多个因素导致性能下降。
相关文章:
Transformers实战05-模型量化
文章目录 简介主要类型量化的优点量化的缺点量化过程量化过程反量化过程 精度和参数 量化实例bitsandbytes安装bitsandbytes4bit量化(加载)8bit量化(加载)验证效果 简介 模型量化(Model Quantization)是一种优化技术,旨在减少机器学习模型的…...

【Python】bytes 和 bytearray 到底是什么类型呢?
bytes和bytearray同属于二进制序列类型,是常见的数值类型的一种。 bytes多用在在文件的读写、网络通信、数据编码/解码等场景用的比较多。 而bytearray在二进制数据处理、图像处理、内存映射文件和网络通信等场景用的比较多。 其中这两部分的主要差别: …...

Windows10上安装SQL Server 2022 Express
Microsoft SQL Server 2022 Express是一个功能强大且可靠的免费数据管理系统,可为轻量级网站和桌面应用程序提供丰富可靠的数据存储,为关系数据库: (1).LocalDB(SqlLocalDB):是Express的一种轻型版本,该版本具备所有可…...

C++11 异常
目录 0.前言 1.C语言传统错误处理方式 1.1使用返回值 1.2使用全局变量 1.3使用断言 1.4优缺点 2.C异常的概念 3.异常的使用 3.1异常的抛出和捕获 3.1.1异常的抛出和匹配原则 3.1.2在函数调用链中异常栈展开匹配原则 3.2异常的重新抛出 3.3异常安全 3.4异常规范 4.自定义异常体系…...

pip下载lap失败
把pip install lap改为pip intsall lapx...

【Material-UI】Button 中的点击事件处理(Handling clicks)详解
文章目录 一、点击事件处理基础1. 基本用法2. 事件处理器的传递 二、实际应用中的注意事项1. 事件处理逻辑的优化2. 避免过多的状态更新3. 使用合适的事件类型 三、关于文档中未提及的原生属性四、最佳实践1. 无障碍性2. 视觉反馈3. 防止重复点击 五、总结 在现代前端开发中&am…...

Spring Cache框架(AOP思想)+ Redis实现数据缓存
文章目录 1 简介1.1 基本介绍1.2 为什么要用 Spring Cache? 2 使用方法2.1 依赖导入(Maven)2.2 常用注解2.3 使用步骤2.4 常用注解说明1)EnableCaching2)CachePut3)Cacheable4)CacheEvict 3 注意…...
在Windows编程中,MFC\C++中如何在OnCopyData中传递Vector类型数据?
我们在通过 WM_COPYDATA 消息实现进程间通信时,发送char 数组或其他类型数组与发送vector是有区别的。 1、发送基础类型时,直接发送指针。 typedef struct tagMYSTRUCT {int nTest;wchar_t cTest[40] {0}; } MYSTRUCT, *PMYSTRUCT;MYSTRUCT stSend; s…...

Java常见面试题-01-java基础
文章目录 面向对象的特征Java 的基本数据类型有哪些JDK、JRE、JVM 的区别重载和重写的区别Java 中和 equals 的区别String、StringBuffer、StringBuilder 三者之间的区别接口和抽象类的区别是什么string 常用的方法有哪些什么是单例模式?有几种?什么是反…...

Python爬虫实战:利用代理IP爬取百度翻译
文章目录 一、爬取目标二、环境准备三、代理IP获取3.1 爬虫和代理IP的关系3.2 巨量IP介绍3.3 超值企业极速池推荐3.4 IP领取3.5 代码获取IP 四、爬虫代码实战4.1分析网页4.2 寻找接口4.3 参数构建4.4 完整代码 一、爬取目标 本次目标网站:百度翻译(http…...

Transformer学习之DETR
文章目录 1.算法简介1.1 算法主要贡献1.2 算法网络结构 2.损失函数设计2.1 二分图匹配(匈牙利算法)2.2 二分图匹配Loss_match2.3 训练Loss_Hungarian 3.网络核心模块3.1 BackBone模块3.2 空间位置编码(spatial positional encoding)3.2.1 输入与输出3.2.2 空间位置编码原理 3.3…...

场外个股期权是什么品种?可以交易哪些品种?
今天带你了解场外个股期权是什么品种?可以交易哪些品种?场外个股期权是指在场外市场进行交易的个股期权合约,与在交易所交易的标准化个股期权有所不同,它是由买方和卖方通过私下协商,而非通过公开交易所进行买卖和定价…...

每日学术速递8.5-3
1.BoostMVSNeRFs: Boosting MVS-based NeRFs to Generalizable View Synthesis in Large-scale Scenes 标题: BoostMVSNeRFs:将基于 MVS 的 NeRFs 提升到大规模场景中的可泛化视图合成 作者:Chih-Hai Su, Chih-Yao Hu, Shr-Ruei Tsai, Jie-…...
C#针对kernel32.dll的一些常规使用
1、前言 Window是一个复杂的系统,kernel32是一个操作系统的核心动态链接库文件。它提供了大量的API函数,提供了操作系统的基本功能。 2、Ini使用 Ini文件读写使用时,我们需要用到其中的一些函数对文件进行读写。 API: /// &l…...

电话营销机器人的优势
在人工智能的新趋势下,企业开始放弃传统外呼系统,转而使用电话销售机器人,那么使用机器人比坐席手动外呼好吗,真的可以代替人工坐席外呼吗,效率真的高吗? 1、 真人式语音 电话销售人员可以将自定义的话术…...

Oracle SQL Developer 连接第三方数据库
首先Oracle SQL Developer除了支持连接Oracle数据库外,还支持连接第三方数据库,包括: Amazon RedshiftHiveIBM DB2MySQLMicrosoft SQL ServerSybase Adaptive ServerPostgreSQLTeradataTimesTen 首先,你需要在菜单Tools > Pr…...

OSPF路由协议多区域
一、OSPF路由协议单区域的弊端 1、LSDB庞大,占用内存大,SPF计算开销大; 2、LSA洪泛范围大,拓扑变化影响范围大; 3、路由不能被汇总,路由表庞大,查找路由开销大。 二、如何解决OSPF单区域的问题? 引入划分区域 1、每个区域独立存储LSDB,划分区域减小了LSDB。 2、…...

8.5 C++
思维导图 试编程 提示并输入一个字符串,统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 要求使用C风格字符串完成 #include <iostream> #include <array>using namespace std;int main() {cout << "请输入一个字符…...

MySQL —— 初始数据库
数据库概念 在学习数据库之前,大家保存数据要么是在程序运行期间,例如:在学习编程语言的时候,大家写过的管理系统,运用一些简单的数据结构(例如顺序表)来组织数据,可是程序一旦结束…...

【JVM】垃圾回收机制、算法和垃圾回收器
什么是垃圾回收机制 为了让程序员更加专注于代码的实现,而不用过多的考虑内存释放的问题,所以在Java语言中,有了自动的垃圾回收机制,也是我们常常提及的GC(Garbage Collection) 有了这个垃圾回收机制之后,程序员只需…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...