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) 有了这个垃圾回收机制之后,程序员只需…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

前端开发者常用网站
Can I use网站:一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use:Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站:MDN JavaScript权威网站:JavaScript | MDN...

高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...

关于 ffmpeg设置摄像头报错“Could not set video options” 的解决方法
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/148515355 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…...