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

量化Quantization初步之--带量化(QAT)的XOR异或pyTorch版250501



量化(Quantization)这词儿听着玄,经常和量化交易Quantitative Trading (量化交易)混淆。

其实机器学习(深度学习)领域的量化Quantization是和节约内存、提高运算效率相关的概念(因大模型的普及,这个量化问题尤为迫切)。

揭秘机器学习“量化”:不止省钱,更让AI高效跑起来!

“量化(Quantization)”这个词,在机器学习领域,常常让人联想到复杂的数学或是与金融交易相关的“量化交易”,从而感到困惑。但实际上,它与我们日常生活中的数字转换概念更为接近,而在AI世界里,它扮演的角色是节约内存、提高运算效率的“幕后英雄”(现在已经显露到“幕布之前”),尤其在大模型时代,其重要性日益凸显。

那么,机器学习中的“量化”究竟是啥?咱为啥用它?

什么是机器学习中的“量化”(Quantization)?

简单讲,机器学习中的“量化”就是将模型中原本采用高精度浮点数(如32位浮点数,即FP32)表示的权重(weights)和激活值(activations),转换成低精度表示(如8位整数,即INT8)的过程。

你可以把它想象成“数字的压缩”。在计算机中,浮点数就像是拥有无限小数位的精确数字,而整数则像只有整数部分的数字。从高精度浮点数到低精度整数的转换,必然会损失一些信息,但与此同时,它也带来了显著的优势:

  1. 内存占用大幅减少: 8位整数比32位浮点数少占用4倍的内存空间。这意味着更大的模型可以被部署到内存有限的设备上(如手机、IoT设备),或者在相同内存下可以运行更大的模型。
  2. 计算速度显著提升: 整数运算通常比浮点数运算更快、功耗更低。这使得模型在推理(Inference)阶段能以更高的效率运行,减少延迟。

为何需要“量化”?

随着深度学习模型变得越来越大,越来越复杂,它们对计算资源的需求也呈爆炸式增长。一个动辄几十亿甚至上百亿参数的大模型,如果全部使用FP32存储和计算,将对硬件资源提出极高的要求。

  • 部署到边缘设备: 手机、自动驾驶汽车、智能音箱等边缘设备通常算力有限,内存紧张。量化是让大模型“瘦身”后成功“登陆”这些设备的必经之路。
  • 降低运行成本: 在云端部署大模型时,更低的内存占用和更快的计算速度意味着更低的服务器成本和能耗。
  • 提升用户体验: 实时响应的AI应用,如语音助手、图像识别等,对推理速度有极高要求。量化可以有效缩短响应时间。

量化策略:后训练量化 vs. 量化感知训练(QAT)

量化并非只有一种方式。根据量化发生的时间点,主要可以分为两大类:

  1. 后训练量化(Post-Training Quantization, PTQ): 顾名思义,PTQ 是在模型训练完成之后,对已经训练好的FP32模型进行量化。它操作简单,不需要重新训练,是实现量化的最快途径。然而,由于量化过程中会损失精度,PTQ 可能会导致模型性能(如准确率)的下降。对于对精度要求不那么苛刻的应用,PTQ 是一个不错的选择。

  2. 量化感知训练*(这是本文重点推介的:Quantization Aware Training, QAT): 这正是我们今天着重讲解的明星策略!QAT 的核心思想是——在模型训练过程中,就“感知”到未来的量化操作。

    在QAT中,量化误差被集成到模型的训练循环中。这意味着,模型在训练时就“知道”它最终会被量化成低精度,并会努力学习如何在这种低精度下保持最优性能。

    具体来说,QAT通常通过在模型中插入“伪量化”(Fake Quantization)节点来实现。这些节点在训练过程中模拟量化和反量化操作,使得模型在FP32环境下进行前向传播和反向传播时,能够学习到量化对模型参数和激活值的影响。当训练完成后,这些伪量化节点会被真正的量化操作所取代,从而得到一个高性能的量化模型。

为什么QAT是量化策略的“王牌”?

相较于PTQ……QAT 的优势显而易见

  • 精度损失最小: 这是QAT最大的亮点。通过在训练过程中模拟量化,模型能够自我调整以适应量化带来的精度损失,从而在量化后依然保持接近FP32模型的性能。
  • 适用于更苛刻的场景: 对于那些对模型精度要求极高,不能容忍明显性能下降的应用(如自动驾驶、医疗影像分析),QAT几乎是唯一的选择。
  • 更好的泛化能力: 在训练阶段就考虑量化,使得模型在量化后对各种输入数据具有更好的鲁棒性。

PyTorch中的QAT实践

在PyTorch中实现QAT,通常需要以下几个关键步骤:

  1. 准备量化配置: 定义量化类型(如INT8)、量化方法(如对称量化、非对称量化)以及需要量化的模块。
  2. 模型转换: 使用PyTorch提供的 torch.quantization 模块,将普通的FP32模型转换为QAT模型。这个过程会在模型中插入伪量化模块。
  3. 重新训练/微调: 在新的数据集上对转换后的模型进行短时间的微调(Fine-tuning),或者在原有训练基础上继续训练。这个阶段,模型会学习如何适应伪量化带来的精度损失。
  4. 模型融合(可选但推荐): 将一些连续的层(如Conv-BN-ReLU)融合为一个操作,可以进一步提高量化后的推理效率。
  5. 模型量化和保存: 训练完成后,将微调好的QAT模型转换为真正的量化模型,并保存。

总结

量化(Quantization)是深度学习模型优化不可或缺的一环,它通过降低模型精度来换取内存和计算效率的大幅提升。而量化感知训练(QAT)作为一种高级量化策略,通过在训练阶段就考虑量化对模型的影响,极大地减小了量化带来的精度损失,使得在各种设备上部署高性能AI模型成为可能。

随着大模型和边缘AI的普及,掌握量化尤其是QAT的原理和实践,将成为每一位AI工程师和研究人员的必备技能。让我们一起,让AI跑得更快、更高效!


import torch
import torch.nn as nn
import torch.optim as optim
import torch.quantization
import numpy as np
import os# ===== 1. XOR数据集 =====
X = torch.tensor([[0., 0.],[0., 1.],[1., 0.],[1., 1.]
], dtype=torch.float32)
y = torch.tensor([[0.],[1.],[1.],[0.]
], dtype=torch.float32)# ===== 2. 神经网络模型 (标准FP32) =====
class XORNet(nn.Module):def __init__(self):super(XORNet, self).__init__()self.fc1 = nn.Linear(2, 3)self.relu = nn.ReLU()self.fc2 = nn.Linear(3, 1)# QAT阶段不用Sigmoid,直接用BCEWithLogitsLoss# self.sigmoid = nn.Sigmoid()def forward(self, x):x = self.fc1(x)x = self.relu(x)x = self.fc2(x)# return self.sigmoid(x)return x# ===== 3. 初始化模型/优化器 =====
model = XORNet()
# He初始化,适合ReLU
for m in model.modules():if isinstance(m, nn.Linear):nn.init.kaiming_uniform_(m.weight, mode='fan_in', nonlinearity='relu')if m.bias is not None:nn.init.constant_(m.bias, 0)criterion = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=0.05)# ===== 4. 训练(标准模型) =====
print("--- 开始标准模型训练 ---")
epochs = 900#1000#500    #1500
for epoch in range(epochs):outputs = model(X)loss = criterion(outputs, y)optimizer.zero_grad()loss.backward()optimizer.step()if (epoch + 1) % 300 == 0:print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')with torch.no_grad():probs = torch.sigmoid(model(X))predictions = (probs > 0.5).float()accuracy = (predictions == y).sum().item() / y.numel()print(f"\n标准模型训练后精度: {accuracy*100:.2f}%")print(f"标准模型预测结果:\n{predictions}")# ===== 5. 构建QAT模型 =====
class XORNetQAT(nn.Module):def __init__(self):super(XORNetQAT, self).__init__()# 量化Stubself.quant = torch.quantization.QuantStub()self.fc1 = nn.Linear(2, 3)self.relu = nn.ReLU()self.fc2 = nn.Linear(3, 1)self.dequant = torch.quantization.DeQuantStub()def forward(self, x):x = self.quant(x)x = self.fc1(x)x = self.relu(x)x = self.fc2(x)x = self.dequant(x)return xdef fuse_model(self):torch.quantization.fuse_modules(self, [['fc1', 'relu']], inplace=True)# ===== 6. QAT前权重迁移、模型融合 =====
model_qat = XORNetQAT()
# 迁移参数
model_qat.load_state_dict(model.state_dict())
# 融合(此步必须!)
model_qat.fuse_model()# 配置QAT
model_qat.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm')  # CPU
torch.quantization.prepare_qat(model_qat, inplace=True)optimizer_qat = optim.Adam(model_qat.parameters(), lr=0.01)# ===== 7. QAT训练 =====
qat_epochs = 700
print("\n--- QAT训练 ---")
for epoch in range(qat_epochs):model_qat.train()outputs_qat = model_qat(X)loss_qat = criterion(outputs_qat, y)optimizer_qat.zero_grad()loss_qat.backward()optimizer_qat.step()if (epoch + 1) % 150 == 0:print(f'QAT Epoch [{epoch+1}/{qat_epochs}], Loss: {loss_qat.item():.4f}')# ===== 8. 转换到量化模型/评估精度 =====
print("\n--- 转换为量化模型 ---")
model_qat.eval()
model_quantized = torch.quantization.convert(model_qat.eval(), inplace=False)with torch.no_grad():probs_quantized = torch.sigmoid(model_quantized(X))predictions_quantized = (probs_quantized > 0.5).float()accuracy_quantized = (predictions_quantized == y).sum().item() / y.numel()print(f"量化模型精度: {accuracy_quantized*100:.2f}%")print(f"量化模型预测:\n{predictions_quantized}")# ===== 9. 模型大小对比 =====
torch.save(model.state_dict(), 'xor_fp32.pth')
torch.save(model_quantized.state_dict(), 'xor_int8.pth')
fp32_size = os.path.getsize('xor_fp32.pth') / (1024 * 1024)
int8_size = os.path.getsize('xor_int8.pth') / (1024 * 1024)
print(f"\nFP32模型大小: {fp32_size:.6f} MB")
print(f"INT8模型大小: {int8_size:.6f} MB")
print(f"模型缩减比例: {fp32_size/int8_size:.2f} 倍")

运行结果:

====================== RESTART: F:/qatXorPytorch250501.py 
--- 开始标准模型训练 ---
Epoch [300/900], Loss: 0.0033
Epoch [600/900], Loss: 0.0011
Epoch [900/900], Loss: 0.0005

标准模型训练后精度: 100.00%
标准模型预测结果:
tensor([[0.],
        [1.],
        [1.],
        [0.]])

--- QAT训练 ---
QAT Epoch [150/700], Loss: 0.0005
QAT Epoch [300/700], Loss: 0.0004
QAT Epoch [450/700], Loss: 0.0004
QAT Epoch [600/700], Loss: 0.0003

--- 转换为量化模型 ---
量化模型精度: 100.00%
量化模型预测:
tensor([[0.],
        [1.],
        [1.],
        [0.]])

FP32模型大小: 0.001976 MB
INT8模型大小: 0.004759 MB
模型缩减比例: 0.42 倍



最后:
其实,这次量化
量化后的模型是量化前的 4.2倍(咦?不是说好了压缩吗?咋变大了?)


魔鬼藏在细节:
咱们看看 量化 之前的 (基线的)模型 参数+权重等等:

===== Model Architecture =====
XORNet(
  (fc1): Linear(in_features=2, out_features=3, bias=True)
  (relu): ReLU()
  (fc2): Linear(in_features=3, out_features=1, bias=True)
)

===== Layer Parameters =====
[fc1.weight] shape: (3, 2)
[[ 1.723932,  1.551827],
 [ 2.106917,  1.681809],
 [-0.299378, -0.444912]]

[fc1.bias] shape: (3,)
[-1.725313, -2.509506,  0.      ]

[fc2.weight] shape: (1, 3)
[[-2.492318, -3.94821 ,  0.911841]]

[fc2.bias] shape: (1,)
[0.692789]

===== Extra Info (Hyperparameters) =====
Optimizer: Adam
Learning Rate: 0.05
Epochs: 900
Loss: BCEWithLogitsLoss
Activation: ReLU

量化之后的模型参数等:
超参数部分:

 

===== Model Architecture =====
XORNetQAT(
  (quant): Quantize(scale=tensor([0.0079]), zero_point=tensor([0]), dtype=torch.quint8)
  (fc1): QuantizedLinearReLU(in_features=2, out_features=3, scale=0.0678500160574913, zero_point=0, qscheme=torch.per_channel_affine)
  (relu): Identity()
  (fc2): QuantizedLinear(in_features=3, out_features=1, scale=0.1376650333404541, zero_point=65, qscheme=torch.per_channel_affine)
  (dequant): DeQuantize()
)

===== Layer Parameters =====
===== Extra Info (Hyperparameters) =====
Optimizer: Adam
Learning Rate: 0.01
QAT Epochs: 700
Loss: BCEWithLogitsLoss
QConfig: QConfig(activation=functools.partial(<class 'torch.ao.quantization.fake_quantize.FusedMovingAvgObsFakeQuantize'>, observer=<class 'torch.ao.quantization.observer.MovingAverageMinMaxObserver'>, quant_min=0, quant_max=255, reduce_range=True){'factory_kwargs': <function _add_module_to_qconfig_obs_ctr.<locals>.get_factory_kwargs_based_on_module_device at 0x00000164FDB16160>}, weight=functools.partial(<class 'torch.ao.quantization.fake_quantize.FusedMovingAvgObsFakeQuantize'>, observer=<class 'torch.ao.quantization.observer.MovingAveragePerChannelMinMaxObserver'>, quant_min=-128, quant_max=127, dtype=torch.qint8, qscheme=torch.per_channel_symmetric){'factory_kwargs': <function _add_module_to_qconfig_obs_ctr.<locals>.get_factory_kwargs_based_on_module_device at 0x00000164FDB16160>})

看到吗?
量化后, 参数 变多了哈哈!

那量化的意义到底在哪里呢??

在下面的 参数(非超参)的 权重的部分:

===== 量化 (Quantization) 后参数 =====

[fc1] (QuantizedLinearReLU)
  [weight] shape: torch.Size([3, 2]), dtype: torch.qint8
  weight (quantized):
[[ 3.97  3.98]
 [ 3.95  3.96]
 [-0.56 -1.05]]
  weight (raw int):
[[127 127]
 [127 127]
 [-18 -33]]
  scale: 0.06785
  zero_point: 0
  [bias] shape: torch.Size([3]), dtype: torch.float
  bias:
[-3.049073e-04 -3.960315e+00  0.000000e+00]

[fc2] (QuantizedLinear)
  [weight] shape: torch.Size([1, 3]), dtype: torch.qint8
  weight (quantized):
[[ 3.77 -7.79  0.41]]
  weight (raw int):
[[ 27 -57   3]]
  scale: 0.13766
  zero_point: 65
  [bias] shape: torch.Size([1]), dtype: torch.float
  bias:
[-7.147094]



再看看量化前:

===== Layer Parameters =====
[fc1.weight] shape: (3, 2)
[[ 1.723932,  1.551827],
 [ 2.106917,  1.681809],
 [-0.299378, -0.444912]]

[fc1.bias] shape: (3,)
[-1.725313, -2.509506,  0.      ]

[fc2.weight] shape: (1, 3)
[[-2.492318, -3.94821 ,  0.911841]]

[fc2.bias] shape: (1,)
[0.692789]

最后看看 量化 后:

===== 量化 (Quantization) 后参数 =====

[fc1] (QuantizedLinearReLU)
  [weight] shape: torch.Size([3, 2]), dtype: torch.qint8
  weight (quantized):
[[ 3.97  3.98]
 [ 3.95  3.96]
 [-0.56 -1.05]]
  weight (raw int):
[[127 127]
 [127 127]
 [-18 -33]]
  scale: 0.06785
  zero_point: 0
  [bias] shape: torch.Size([3]), dtype: torch.float
  bias:

看出区别了吗?

So:

量化操作 只 适用于 大、中型号的模型……道理就在此:
量化前 的 Weights 的权重 全部都是: Float32浮点型 ……很占内存的!
量化后 是所谓INT8(即一个字节、8bits)……至少(在权重部分)节省了 3/4 的内存!

So: 大模型 必须 要 量化 才 节省内存。
当然前提 是 你的 GPU 硬件 要 支持 INT8(8bits)的 运算哦……(这是后话,下次再聊)。

(over)完!

相关文章:

量化Quantization初步之--带量化(QAT)的XOR异或pyTorch版250501

量化(Quantization)这词儿听着玄&#xff0c;经常和量化交易Quantitative Trading (量化交易)混淆。 其实机器学习(深度学习)领域的量化Quantization是和节约内存、提高运算效率相关的概念&#xff08;因大模型的普及&#xff0c;这个量化问题尤为迫切&#xff09;。 揭秘机器…...

Linux Maven Install

在 CentOS&#xff08;例如 CentOS 7 或 CentOS 8&#xff09;中安装 Maven&#xff08;Apache Maven&#xff09;的方法主要有两种&#xff1a;使用包管理器&#xff08;简单但可能版本较旧&#xff09;&#xff0c;或者手动安装&#xff08;推荐&#xff0c;可获得最新版&…...

#Java篇:学习node后端之sql常用操作

学习路线 1、javascript基础&#xff1b; 2、nodejs核心模块 fs: 文件系统操作 path: 路径处理 http / https: 创建服务器或发起请求 events: 事件机制&#xff08;EventEmitter&#xff09; stream: 流式数据处理 buffer: 处理二进制数据 os: 获取操作系统信息 util: 工具方…...

电网“逆流”怎么办?如何实现分布式光伏发电全部自发自用?

2024年10月9日&#xff0c;国家能源局综合司发布了《分布式光伏发电开发建设管理办法&#xff08;征求意见稿&#xff09;》&#xff0c;意见稿规定了户用分布式光伏、一般工商业分布式光伏以及大型工商业分布式光伏的发电上网模式&#xff0c;当选择全部自发自用模式时&#x…...

如何查看电脑电池性能

检查电脑电池性能的方法如下&#xff1a; 按下winR键&#xff0c;输入cmd回车&#xff0c;进入命令行窗口 在命令行窗口输入powercfg /batteryreport 桌面双击此电脑&#xff0c;把刚刚复制的路径粘贴到文件路径栏&#xff0c;然后回车 回车后会自动用浏览器打开该报告 红…...

kubernetes》》k8s》》kubectl proxy 命令后面加一个

命令后面加一个& 在Linux终端中&#xff0c;如果在命令的末尾加上一个&符号&#xff0c;这表示将这个任务放到后台去执行 kubectl proxy 官网资料 是 Kubernetes 提供的一个命令行工具&#xff0c;用于在本地和 Kubernetes API Server 之间创建一个安全的代理通道。…...

深入理解Linux系统进程切换

目录 引言 一、什么是进程切换&#xff1f; 二、进程切换的触发条件 三、进程切换的详细步骤 1、保存当前进程上下文&#xff1a; 2、更新进程控制块(PCB)&#xff1a; 3、选择下一个进程&#xff1a; 4、恢复新进程上下文&#xff1a; 5、切换地址空间&#xff1a; 6…...

网络安全运维实训室建设方案

一、网络安全运维人才需求与实训困境 在数字化时代&#xff0c;网络安全已成为国家安全、社会稳定和经济发展的重要基石。随着信息技术的飞速发展&#xff0c;网络安全威胁日益复杂多样&#xff0c;从个人隐私泄露到企业商业机密被盗&#xff0c;从关键基础设施遭受攻击到社会…...

DBeaver 连接mysql报错:CLIENT_PLUGIN_AUTH is required

DBeaver 连接mysql报错&#xff1a;CLIENT_PLUGIN_AUTH is required 一、必须要看这个 >> &#xff1a;参考文献 二、补充 2.1 说明 MySQL5、6这些版本比较老&#xff0c;而DBeaver默认下载的是MySQL8的连接库&#xff0c;所以连接旧版本mysql报错&#xff1a;CLIEN…...

联通专线赋能,亿林网络裸金属服务器:中小企业 IT 架构升级优选方案

在当今数字化飞速发展的时代&#xff0c;中小企业面临着日益增长的业务需求与复杂多变的市场竞争环境。如何构建高效、稳定且具性价比的 IT 架构&#xff0c;成为众多企业突破发展瓶颈的关键所在。而亿林网络推出的 24 核 32G 裸金属服务器&#xff0c;搭配联通专线的千兆共享带…...

Web3时代的数据保护挑战与应对策略

随着互联网技术的飞速发展&#xff0c;我们正步入Web3时代&#xff0c;这是一个以去中心化、用户主权和数据隐私为核心的新时代。然而&#xff0c;Web3时代也带来了前所未有的数据保护挑战。本文将探讨这些挑战&#xff0c;并提出相应的应对策略。 数据隐私挑战 在Web3时代&a…...

Qwen3与MCP协议:重塑大气科学的智能研究范式

在气象研究领域&#xff0c;从海量数据的解析到复杂气候模型的构建&#xff0c;科研人员长期面临效率低、门槛高、易出错的挑战。而阿里云推出的Qwen3大模型与MCP协议的结合&#xff0c;正通过混合推理模式与标准化协同机制&#xff0c;为大气科学注入全新活力。本文将深入解析…...

CppCon 2015 学习:Benchmarking C++ Code

关于性能问题与调试传统 bug&#xff08;如段错误&#xff09;之间差异的分析。以下是对这一页内容的详细解释&#xff1a; 主题&#xff1a;传统问题&#xff08;如段错误&#xff09;调试流程清晰 问题类型&#xff1a;段错误&#xff08;Segmentation Fault&#xff09; …...

URL 结构说明+路由(接口)的认识

一、URL 结构说明 以这个为例&#xff1a;http://127.0.0.1:5000/zhouleifeng 1.组成部分: http://&#xff1a;协议 127.0.0.1&#xff1a;主机&#xff08;本地地址&#xff09; :5000&#xff1a;端口号&#xff08;Flask 默认 5000&#xff09; /zhouleifeng&#xff1a…...

省赛中药检测模型调优

目录 一、baseline性能二、baseline DETR head三、baseline RepC3K2四、baseline RepC3K2 SimSPPF五、baseline RepC3K2 SimSPPF LK-C2PSA界面1.引入库2.读入数据 总结 一、baseline性能 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size120/120 …...

linux 故障处置通用流程-36计+1计

通用标准处置快速索引 编号 通 用 标 准 处 置 索 引 001 Linux操作系统标准关闭 002 Linux操作系统标准重启 003 Linux操作系统强行关闭 004 Linux操作系统强行重启 005 检查Linux操作系统CPU负载 006 查询占用CPU资源最多的进程 007 检查Linux操…...

​BEV和OCC学习-3:mmdet3d 坐标系

目录 坐标系 转向角 (yaw) 的定义 框尺寸的定义 与支持的数据集的原始坐标系的关系 KITTI Waymo NuScenes Lyft ScanNet SUN RGB-D S3DIS 坐标系 坐标系 — MMDetection3D 1.4.0 文档https://mmdetection3d.readthedocs.io/zh-cn/latest/user_guides/coord_sys_tuto…...

[蓝桥杯]图形排版

图形排版 题目描述 小明需要在一篇文档中加入 NN 张图片&#xff0c;其中第 ii 张图片的宽度是 WiWi​&#xff0c;高度是 HiHi​。 假设纸张的宽度是 MM&#xff0c;小明使用的文档编辑工具会用以下方式对图片进行自动排版&#xff1a; 1. 该工具会按照图片顺序&#xff0…...

【Linux仓库】冯诺依曼体系结构与操作系统【进程·壹】

&#x1f31f; 各位看官好&#xff0c;我是&#xff01; &#x1f30d; Linux Linux is not Unix &#xff01; &#x1f680; 今天来学习冯诺依曼体系结构与操作系统。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更多人哦&#xff0…...

CloudFront 加速详解:AWS CDN 怎么用?

让全球访问更快速稳定&#xff0c;深入解读 AWS 的内容分发网络 在上一篇中&#xff0c;我们介绍了 Amazon S3 对象存储&#xff0c;它非常适合托管静态资源&#xff0c;比如图片、视频、网页等。但你可能遇到过这样的问题&#xff1a; “我把网站静态文件部署到了 S3&#xf…...

《高级架构师》------- 考后感想

笔者来聊一下架构师考后的感想 复习备考 考前过了很多知识点&#xff0c;只是蜻蜓点水&#xff0c;没有起到复习的作用&#xff0c;即使考出来也不会&#xff0c;下次复习注意这个&#xff0c;复习到了&#xff0c;就记住&#xff0c;或者画出来&#xff0c;或者文件总结&…...

【iOS】YYModel源码解析

YYModel源码解析 文章目录 YYModel源码解析前言YYModel性能优势YYModel简介YYClassInfo解析YYClassIvarInfo && objc_ivarYYClassMethodInfo && objc_methodYYClassPropertyInfo && property_tYYClassInfo && objc_class YYClassInfo的初始化细…...

C++算法训练营 Day6 哈希表(1)

1.有效的字母异位词 LeetCode&#xff1a;242.有效的字母异位词 给定两个字符串s和t &#xff0c;编写一个函数来判断t是否是s的字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输入: s “rat”, t “car” 输出: false 解题思路&#xff…...

【C语言编译与链接】--翻译环境和运行环境,预处理,编译,汇编,链接

目录 一.翻译环境和运行环境 二.翻译环境 2.1--预处理(预编译) 2.2--编译 2.2.1--词法分析 2.2.2--语法分析 2.2.3--语义分析 2.3--汇编 2.4--链接 三.运行环境 &#x1f525;个人主页&#xff1a;草莓熊Lotso的个人主页 &#x1f3ac;作者简介&#xff1a;C研发…...

【JavaEE】多线程

8.线程状态 根据 Java 的Thread.state包&#xff0c;线程一共有六种状态&#xff1a; NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED 二、每种状态的含义 1. NEW&#xff08;新建&#xff09; 当使用new 关键字创建一个线程对象&#xff0c;但尚未调用其start() 方法时…...

【项目】在线OJ(负载均衡式)

目录 一、项目目标 二、开发环境 1.技术栈 2.开发环境 三、项目树 目录结构 功能逻辑 编写思路 四、编码 1.complie_server 服务功能 代码蓝图 开发编译功能 日志功能 ​编辑 测试编译模块 开发运行功能 设置运行限制 jsoncpp 编写CR 如何生成唯一文件名 …...

贪心算法应用:在线租赁问题详解

贪心算法应用&#xff1a;在线租赁问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景&#xff0c;下面我将从多个维度全面…...

torch.zeros()用法简介

torch.zeros()是PyTorch中用于创建全零张量的核心函数&#xff0c;其功能和使用方法如下&#xff1a; 1. ‌基本语法‌ torch.zeros(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)参数说明&#xff1a; *size&#xff1a;定义张量形状的…...

Prj10--8088单板机C语言8259测试(1)

1.原理图 2.Deepseek示例代码 #include <dos.h> #include <conio.h> #include <stdio.h>#define PIC1_CMD 0x400 // 命令端口 (A00) #define PIC1_DATA 0x401 // 数据端口 (A01)volatile int int_count 0; // 中断计数器 void interrupt (*old_isr)(…...

3步在小米13手机跑DeepSeek R1

大家好&#xff01;我是羊仔&#xff0c;专注AI工具、智能体、编程。 一、从性能旗舰到AI主机 春节大扫除时&#xff0c;翻出尘封的小米13&#xff0c;这台曾以骁龙8 Gen2著称的性能小钢炮&#xff0c;如今正在执行更科幻的使命——本地运行DeepSeek R1。 想起两年前用它连续肝…...