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

【机器学习③】 | CNN篇

0 序言

本文将系统讲解卷积神经网络(CNN)的核心原理,以及LeNet-5AlexNetGoogLeNetResNet四大经典网络的结构、实现与应用。通过PyTorch代码示例,从基础原理到实际操作,帮助读者理解CNN如何处理多维数据经典网络的设计逻辑及实现方法,读完可掌握CNN的核心概念与落地技能。

1 CNN的原理

深度学习中,深度神经网络(DNN)在处理图像等多维数据时存在局限,而卷积神经网络(CNN)通过特殊结构解决了这一问题。本章将从DNN与CNN的差异出发,解析CNN的核心组件及工作机制。

1.1 从DNN到CNN

1.1.1 卷积层与汇聚

在这里插入图片描述
从图片这里能知道,DNN的相邻层神经元全连接,而CNN多了卷积层(Convolution)汇聚(Pooling)

所以从这里也能知道单个卷积层的典型结构:卷积层-激活函数-(汇聚),其中汇聚可省略。

1.1.2 CNN专攻多维数据

  • DNN处理图像时需将多维数据拉平为1维,但这样会丢失空间特征;
  • CNN的卷积层可保持输入数据维数,以多维形式接收和输出数据,保留空间特征。

1.2 卷积层

1.2.1 内部参数:权重(卷积核)

卷积核是卷积层的核心权重,输入数据与卷积核通过逐元素乘积后求和完成卷积运算

比如说:

在这里插入图片描述
在这里插入图片描述
所以结果就是:

在这里插入图片描述

1.2.2 内部参数:偏置

卷积运算后需叠加偏置,偏置为常数,作用于整个输出结果。

1.2.3 外部参数:填充(Padding)

为避免多次卷积后图像尺寸过小,在输入数据周围填充固定值(如0)。
示例:4×4输入填充0后,尺寸变为6×6。

在这里插入图片描述

1.2.4 外部参数:步幅(Stride)

卷积核移动的间隔,步幅为1时逐像素移动,步幅为2时间隔1像素移动。
步幅增大会减小输出尺寸

在这里插入图片描述

1.2.5 输入与输出尺寸的关系

设输入尺寸为(H, W),卷积核尺寸(FH, FW),填充P,步幅S,则输出尺寸(OH, OW)为:
{OH=H+2P−FHS+1OW=W+2P−FWS+1\begin{cases} OH = \frac{H + 2P - FH}{S} + 1 \\ OW = \frac{W + 2P - FW}{S} + 1 \end{cases}{OH=SH+2PFH+1OW=SW+2PFW+1

1.3 多通道

1.3.1 多通道输入

彩色图像等三维数据含通道维度(如RGB为3通道),输入与滤波器的通道数需一致。
多通道卷积后输出为二维,即多个单通道卷积结果求和

1.3.2 多通道输出

为保留多维特征,需多通道输出,通过多个滤波器(数量为输出通道数)实现。
每个输出通道对应一个偏置,单独作用于该通道。

1.4 汇聚(Pooling)

1.4.1 平均汇聚

对指定窗口(如2×2)内元素取平均值,步幅为2时输出尺寸减半。
在这里插入图片描述

1.4.2 最大值汇聚

对指定窗口内元素取最大值,同样步幅为2时输出尺寸减半。

在这里插入图片描述

1.4.3 特点

无学习参数,仅提取局部特征;不改变通道数,仅减小高和宽。

1.5 尺寸变换总结

1.5.1 卷积层尺寸变换

输入尺寸(C, H, W),滤波器尺寸(FN, C, FH, FW),输出尺寸(FN, OH, OW),计算公式相同。

{OH=H+2P−FHS+1OW=W+2P−FWS+1\begin{cases} OH = \frac{H + 2P - FH}{S} + 1 \\ OW = \frac{W + 2P - FW}{S} + 1 \end{cases}{OH=SH+2PFH+1OW=SW+2PFW+1

1.5.2 汇聚层尺寸变换

  • 输入尺寸(C, H, W),步幅S,输出尺寸(C, OH, OW),其中:

{OH=H/SOW=W/S\begin{cases} OH = H / S \\ OW = W / S \end{cases}{OH=H/SOW=W/S

2 LeNet-5

通过第一章,我们理解了CNN的核心原理(卷积层、汇聚层、多通道等)。但理论需结合实践,LeNet-5作为早期经典CNN,成功应用于手写数字识别,本章将详解其结构及PyTorch实现,帮助理解CNN的运作流程。

2.1 网络结构

2.1.1 整体结构

共7层,输入28×28图像,输出10类(0-9)概率,层结构如下:

类型卷积核/参数步幅填充激活函数输出尺寸
C1卷积层6×1×5×512tanh6×28×28
S2平均汇聚2×22--6×14×14
C3卷积层16×6×5×51-tanh16×10×10
S4平均汇聚2×22--16×5×5
C5卷积层120×16×5×51-tanh120×1×1
F6全连接层---tanh84
Out全连接层---Softmax10

2.2 制作数据集

import torch
from torch.utils.data import DataLoader
from torchvision import transforms, datasets# 数据转换:转为Tensor并归一化
transform = transforms.Compose([transforms.ToTensor(),  # 转为张量transforms.Normalize(0.1307, 0.3081)  # 归一化(均值,标准差)
])# 下载MNIST数据集(手写数字)
train_Data = datasets.MNIST(root='D:/Jupyter/dataset/mnist/',  # 存储路径train=True,  # 训练集download=True,  # 不存在则下载transform=transform  # 应用转换
)
test_Data = datasets.MNIST(root='D:/Jupyter/dataset/mnist/',train=False,  # 测试集download=True,transform=transform
)# 数据加载器(批次处理)
train_loader = DataLoader(train_Data, shuffle=True, batch_size=256)  # 训练集打乱
test_loader = DataLoader(test_Data, shuffle=False, batch_size=256)  # 测试集不打乱

2.3 搭建神经网络

import torch.nn as nnclass CNN(nn.Module):def __init__(self):super(CNN, self).__init__()# 定义网络层self.net = nn.Sequential(# C1: 卷积层(输入1通道,输出6通道,5×5卷积核,填充2)nn.Conv2d(1, 6, kernel_size=5, padding=2),nn.Tanh(),  # 激活函数# S2: 平均汇聚(2×2窗口,步幅2)nn.AvgPool2d(kernel_size=2, stride=2),# C3: 卷积层(输入6通道,输出16通道,5×5卷积核)nn.Conv2d(6, 16, kernel_size=5),nn.Tanh(),# S4: 平均汇聚nn.AvgPool2d(kernel_size=2, stride=2),# C5: 卷积层(输入16通道,输出120通道,5×5卷积核)nn.Conv2d(16, 120, kernel_size=5),nn.Tanh(),nn.Flatten(),  # 展平为一维# F6: 全连接层(120→84)nn.Linear(120, 84),nn.Tanh(),# 输出层(84→10)nn.Linear(84, 10))def forward(self, x):return self.net(x)  # 前向传播# 测试网络结构
X = torch.rand(size=(1, 1, 28, 28))  # 模拟输入(1样本,1通道,28×28)
model = CNN()
for layer in model.net:X = layer(X)print(layer.__class__.__name__, '输出形状:', X.shape)

这里的网络层可以见下图:

在这里插入图片描述

2.4 训练网络

# 损失函数(交叉熵,自带Softmax)
loss_fn = nn.CrossEntropyLoss()
# 优化器(SGD,学习率0.9)
optimizer = torch.optim.SGD(model.parameters(), lr=0.9)# 训练参数
epochs = 5
losses = []  # 记录损失# 移至GPU
model = model.to('cuda:0')for epoch in range(epochs):for x, y in train_loader:x, y = x.to('cuda:0'), y.to('cuda:0')  # 数据移至GPUpred = model(x)  # 前向传播loss = loss_fn(pred, y)  # 计算损失losses.append(loss.item())  # 记录损失optimizer.zero_grad()  # 清空梯度loss.backward()  # 反向传播optimizer.step()  # 更新参数

2.5 测试网络

correct = 0
total = 0
with torch.no_grad():  # 关闭梯度计算for x, y in test_loader:x, y = x.to('cuda:0'), y.to('cuda:0')pred = model(x)  # 预测_, predicted = torch.max(pred.data, dim=1)  # 取概率最大类别correct += torch.sum(predicted == y)  # 统计正确数total += y.size(0)  # 总样本数print(f'测试集准确率: {100 * correct / total} %')  # 输出准确率(示例:97.94%)

2.6 完整程序

# 导入必要的库
import torch  # 导入PyTorch核心库
import torch.nn as nn  # 导入神经网络模块
from torch.utils.data import DataLoader  # 导入数据加载工具
from torchvision import transforms  # 导入数据转换工具
from torchvision import datasets  # 导入数据集
import matplotlib.pyplot as plt  # 导入绘图库# 1. 数据准备:加载并预处理MNIST数据集
def prepare_data():# 定义数据转换 pipeline:将图像转为张量并归一化# transforms.ToTensor():将PIL图像转为Tensor(维度为[C, H, W],值归一化到0-1)# transforms.Normalize():用均值和标准差进一步归一化,MNIST的推荐值为(0.1307, 0.3081)transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(0.1307, 0.3081)])# 加载MNIST训练集(手写数字数据集)# root:数据集存储路径# train=True:加载训练集# download=True:如果路径中没有数据集,自动下载# transform:应用上述数据转换train_dataset = datasets.MNIST(root='./dataset/mnist/',  # 建议将数据集放在项目下的dataset文件夹,方便管理train=True,download=True,transform=transform)# 加载MNIST测试集# train=False:加载测试集test_dataset = datasets.MNIST(root='./dataset/mnist/',train=False,download=True,transform=transform)# 创建数据加载器(按批次加载数据,支持打乱和并行)# batch_size=256:每次加载256个样本# shuffle=True:训练集打乱顺序,增强泛化能力# 测试集无需打乱(shuffle=False)train_loader = DataLoader(train_dataset,batch_size=256,shuffle=True)test_loader = DataLoader(test_dataset,batch_size=256,shuffle=False)return train_loader, test_loader# 2. 定义LeNet-5网络结构
class LeNet5(nn.Module):def __init__(self):super(LeNet5, self).__init__()  # 继承父类初始化方法# 定义网络层序列(按LeNet-5结构依次堆叠)self.net = nn.Sequential(# C1层:卷积层(提取低级特征,如边缘、纹理)# in_channels=1:输入为灰度图(1通道)# out_channels=6:输出6个特征图(6个卷积核)# kernel_size=5:卷积核大小5×5# padding=2:边缘填充2像素,保持输出尺寸与输入一致(28×28)nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2),nn.Tanh(),  # 激活函数:引入非线性,tanh是LeNet-5原用激活函数# S2层:平均汇聚层(下采样,减少特征图尺寸,保留关键信息)# kernel_size=2:2×2窗口# stride=2:步幅2,输出尺寸减半nn.AvgPool2d(kernel_size=2, stride=2),# C3层:卷积层(提取更复杂的特征)# in_channels=6:输入为S2的6个特征图# out_channels=16:输出16个特征图# kernel_size=5:5×5卷积核(无填充,尺寸进一步缩小)nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5),nn.Tanh(),  # 激活函数# S4层:平均汇聚层(再次下采样)nn.AvgPool2d(kernel_size=2, stride=2),# C5层:卷积层(实际相当于全连接层,特征图压缩为1×1)# in_channels=16:输入为S4的16个特征图# out_channels=120:输出120个特征图# kernel_size=5:5×5卷积核(5×5特征图经过5×5卷积后变为1×1)nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5),nn.Tanh(),  # 激活函数nn.Flatten(),  # 展平层:将120×1×1的特征图转为120维向量# F6层:全连接层(整合高级特征)nn.Linear(in_features=120, out_features=84),  # 120→84nn.Tanh(),  # 激活函数# 输出层:全连接层(最终分类)nn.Linear(in_features=84, out_features=10)  # 84→10(对应0-9共10类))# 前向传播:定义数据在网络中的流动路径def forward(self, x):return self.net(x)  # 直接通过定义的层序列传播# 3. 测试网络结构是否正确(验证各层输出尺寸)
def test_network_structure():# 创建一个模拟输入:1个样本,1通道,28×28像素(MNIST图像尺寸)dummy_input = torch.randn(size=(1, 1, 28, 28))# 初始化网络model = LeNet5()# 打印各层输出形状,验证是否符合预期print("网络各层输出形状验证:")x = dummy_inputfor layer in model.net:x = layer(x)# 打印层名称和输出形状([样本数, 通道数, 高度, 宽度]或[样本数, 特征数])print(f"{layer.__class__.__name__}: {x.shape}")# 4. 训练网络
def train_model(model, train_loader, epochs=5, lr=0.9):# 定义损失函数:交叉熵损失(适用于分类任务,自带Softmax激活)criterion = nn.CrossEntropyLoss()# 定义优化器:随机梯度下降(SGD),学习率0.9(LeNet-5经典参数)optimizer = torch.optim.SGD(model.parameters(), lr=lr)# 记录训练损失,用于后续绘图train_losses = []# 开始训练print("\n开始训练...")for epoch in range(epochs):  # 遍历训练轮次model.train()  # 切换到训练模式(启用dropout等,此处无dropout但规范操作)total_loss = 0.0for batch_idx, (images, labels) in enumerate(train_loader):# 前向传播:输入图像,得到预测结果outputs = model(images)# 计算损失:预测结果与真实标签的差异loss = criterion(outputs, labels)# 反向传播与参数更新optimizer.zero_grad()  # 清空上一轮梯度(避免累积)loss.backward()  # 计算梯度(反向传播)optimizer.step()  # 根据梯度更新参数# 记录损失train_losses.append(loss.item())total_loss += loss.item()# 每100个批次打印一次中间结果if (batch_idx + 1) % 100 == 0:print(f"轮次 [{epoch+1}/{epochs}], 批次 [{batch_idx+1}/{len(train_loader)}], "f"当前批次损失: {loss.item():.4f}")# 打印本轮平均损失avg_loss = total_loss / len(train_loader)print(f"轮次 [{epoch+1}/{epochs}] 平均损失: {avg_loss:.4f}")# 绘制训练损失曲线plt.figure(figsize=(10, 4))plt.plot(train_losses, label='训练损失')plt.xlabel('批次')plt.ylabel('损失值')plt.title('训练损失变化曲线')plt.legend()plt.show()return model, train_losses# 5. 测试网络(评估准确率)
def test_model(model, test_loader):model.eval()  # 切换到评估模式(关闭dropout等)correct = 0  # 记录正确预测的样本数total = 0    # 记录总样本数# 关闭梯度计算(测试阶段无需计算梯度,节省内存和时间)with torch.no_grad():print("\n开始测试...")for images, labels in test_loader:# 前向传播得到预测结果outputs = model(images)# 取概率最大的类别作为预测结果(dim=1表示按行取最大值)_, predicted = torch.max(outputs.data, 1)# 累加总样本数和正确样本数total += labels.size(0)correct += (predicted == labels).sum().item()# 计算准确率accuracy = 100 * correct / totalprint(f"测试集准确率: {accuracy:.2f}%")return accuracy# 主函数:串联所有步骤
def main():# 步骤1:准备数据train_loader, test_loader = prepare_data()print("数据准备完成,训练集样本数:", len(train_loader.dataset), "测试集样本数:", len(test_loader.dataset))# 步骤2:验证网络结构test_network_structure()# 步骤3:初始化模型model = LeNet5()print("\nLeNet-5模型初始化完成")# 步骤4:训练模型trained_model, losses = train_model(model, train_loader, epochs=5)# 步骤5:测试模型test_model(trained_model, test_loader)# 程序入口:当脚本直接运行时执行主函数
if __name__ == "__main__":main()

运行结果如下:

在这里插入图片描述

在这里插入图片描述

本次LeNet-5训练实验中,首先完成MNIST数据集加载(训练集60000样本、测试集10000样本),并通过模拟输入验证网络结构:各层输出尺寸(如卷积层、汇聚层、全连接层的维度变化)完全符合LeNet-5设计逻辑,确保模型搭建正确。训练阶段历经5个轮次,损失值从首轮平均0.3171持续下降至末轮0.0323,再结合损失曲线可见:前期因模型从随机初始化快速学习特征,损失呈断崖式下降;后期因SGD优化器(学习率0.9较高)导致参数更新步幅大,损失出现震荡,但整体仍趋于收敛,证明模型有效提取了手写数字的分类特征,最后的准确率为98.48%

当然,从该实验完整验证了LeNet-5的经典设计在MNIST任务上的学习能力,也体现了高学习率下优化过程的典型波动特性。

3 AlexNet

通过第二章,我们掌握了LeNet-5的实现,但其结构较简单,难以处理复杂图像。AlexNet作为首个现代深度CNN,引入ReLU、Dropout等技术,大幅提升性能,本章将详解其结构及基于MNIST的适配实现。

3.1 网络结构

3.1.1 整体结构(适配MNIST)

  • 输入1×224×224(MNIST图像放大后),输出10类,层结构如下:
    类型滤波器/参数步幅填充激活函数输出尺寸
    C1卷积层96×1×11×1141ReLU96×54×54
    S2最大汇聚3×32--96×26×26
    C3卷积层256×96×5×512ReLU256×26×26
    S4最大汇聚3×32--256×12×12
    C5卷积层384×256×3×311ReLU384×12×12
    C6卷积层384×384×3×311ReLU384×12×12
    C7卷积层256×384×3×311ReLU256×12×12
    S8最大汇聚3×32--256×5×5
    F9全连接层---ReLU4096
    F10全连接层---ReLU4096
    Out全连接层---Softmax10

3.2 制作数据集(适配FashionMNIST)

# 数据转换:转为Tensor、 resize至224×224、归一化
transform = transforms.Compose([transforms.ToTensor(),transforms.Resize(224),  # 放大至224×224transforms.Normalize(0.1307, 0.3081)
])# 加载FashionMNIST数据集
train_Data = datasets.FashionMNIST(root='D:/Jupyter/dataset/mnist/',train=True,download=True,transform=transform
)
test_Data = datasets.FashionMNIST(root='D:/Jupyter/dataset/mnist/',train=False,download=True,transform=transform
)# 数据加载器
train_loader = DataLoader(train_Data, shuffle=True, batch_size=128)
test_loader = DataLoader(test_Data, shuffle=False, batch_size=128)

3.3 搭建神经网络

class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.net = nn.Sequential(# C1: 卷积层(1→96通道,11×11卷积核,步幅4,填充1)nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1),nn.ReLU(),  # 用ReLU替代tanh,缓解梯度消失# S2: 最大汇聚nn.MaxPool2d(kernel_size=3, stride=2),# C3: 卷积层(96→256通道,5×5卷积核,填充2)nn.Conv2d(96, 256, kernel_size=5, padding=2),nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2),# C5-C7: 连续卷积层nn.Conv2d(256, 384, kernel_size=3, padding=1),nn.ReLU(),nn.Conv2d(384, 384, kernel_size=3, padding=1),nn.ReLU(),nn.Conv2d(384, 256, kernel_size=3, padding=1),nn.ReLU(),# S8: 最大汇聚nn.MaxPool2d(kernel_size=3, stride=2),nn.Flatten(),# 全连接层+Dropout(防止过拟合)nn.Linear(6400, 4096),nn.ReLU(),nn.Dropout(p=0.5),  # 随机丢弃50%神经元nn.Linear(4096, 4096),nn.ReLU(),nn.Dropout(p=0.5),nn.Linear(4096, 10)  # 输出10类)def forward(self, x):return self.net(x)

3.4 训练与测试网络

训练流程同LeNet-5(学习率0.1,epochs=10),测试准确率约99.30%

4 GoogLeNet

AlexNet通过加深网络提升性能,但滤波器超参数(如尺寸)需手动调整,难以最优。GoogLeNet引入Inception块,通过并行路径自动学习最佳特征提取方式,本章详解其结构与实现。

4.1 网络结构

4.1.1 Inception块(核心组件)

4条并行路径,自动选择最优特征提取方式:
1. 1×1卷积(分支1)
2. 1×1卷积→3×3卷积→3×3卷积(分支2)
3. 1×1卷积→5×5卷积(分支3)
4. 1×1卷积(分支4)

输出为4条路径结果拼接,1×1卷积用于降维,降低复杂度。

4.2 制作数据集(同AlexNet,用FashionMNIST)

4.3 搭建神经网络

# 定义Inception块
class Inception(nn.Module):def __init__(self, in_channels):super(Inception, self).__init__()# 分支1:1×1卷积(降维)self.branch1 = nn.Conv2d(in_channels, 16, kernel_size=1)# 分支2:1×1→3×3→3×3self.branch2 = nn.Sequential(nn.Conv2d(in_channels, 16, kernel_size=1),nn.Conv2d(16, 24, kernel_size=3, padding=1),nn.Conv2d(24, 24, kernel_size=3, padding=1))# 分支3:1×1→5×5self.branch3 = nn.Sequential(nn.Conv2d(in_channels, 16, kernel_size=1),nn.Conv2d(16, 24, kernel_size=5, padding=2))# 分支4:1×1卷积self.branch4 = nn.Conv2d(in_channels, 24, kernel_size=1)def forward(self, x):branch1 = self.branch1(x)branch2 = self.branch2(x)branch3 = self.branch3(x)branch4 = self.branch4(x)return torch.cat([branch1, branch2, branch3, branch4], 1)  # 拼接通道# 定义GoogLeNet网络
class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.net = nn.Sequential(nn.Conv2d(1, 10, kernel_size=5),  # 初始卷积nn.ReLU(),nn.MaxPool2d(2, stride=2),Inception(10),  # 加入Inception块nn.Conv2d(88, 20, kernel_size=5),  # 88为Inception输出通道数nn.ReLU(),nn.MaxPool2d(2, stride=2),Inception(20),nn.Flatten(),nn.Linear(1408, 10)  # 全连接输出)def forward(self, x):return self.net(x)

4.4 训练与测试网络

训练流程类似(学习率0.1,epochs=10),最终得到到测试准确率约98.92%。

5 ResNet

GoogLeNet通过并行路径优化特征提取,但深层网络仍面临梯度消失问题(梯度随层数增加趋于0)。ResNet引入残差块,通过跳跃连接缓解梯度消失,本章详解其原理与实现。

5.1 网络结构

5.1.1 残差块(核心组件)

结构:输入x通过权重层得到f(x),输出为f(x)+x(加跳跃连接),再经ReLU激活。
作用:反向传播时梯度为dy/dx + 1,避免梯度消失,支持更深网络。

5.2 制作数据集(同前,用FashionMNIST)

5.3 搭建神经网络

# 定义残差块
class ResidualBlock(nn.Module):def __init__(self, channels):super(ResidualBlock, self).__init__()self.net = nn.Sequential(nn.Conv2d(channels, channels, kernel_size=3, padding=1),  # 保持通道数nn.ReLU(),nn.Conv2d(channels, channels, kernel_size=3, padding=1))def forward(self, x):y = self.net(x)return nn.functional.relu(x + y)  # 跳跃连接:x + 权重层输出# 定义ResNet网络
class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.net = nn.Sequential(nn.Conv2d(1, 16, kernel_size=5),  # 初始卷积nn.ReLU(),nn.MaxPool2d(2),ResidualBlock(16),  # 残差块1nn.Conv2d(16, 32, kernel_size=5),nn.ReLU(),nn.MaxPool2d(2),ResidualBlock(32),  # 残差块2nn.Flatten(),nn.Linear(512, 10)  # 输出层)def forward(self, x):return self.net(x)

5.4 训练与测试网络

  • 训练流程类似(学习率0.1,epochs=10),测试准确率约98.98%

6 总结

本文从CNN的核心原理出发,解析了卷积层、多通道、汇聚等基础组件的工作机制,以及尺寸变换的计算逻辑。通过LeNet-5AlexNetGoogLeNetResNet四个经典网络的实现,展示了CNN的发展脉络:从早期解决手写数字识别(LeNet-5),到引入现代技术(ReLU、Dropout,AlexNet),再到通过并行路径(Inception块,GoogLeNet)和跳跃连接(残差块,ResNet)优化深层网络性能。

http://www.lryc.cn/news/608724.html

相关文章:

  • k8s日志收集
  • Node.js 操作 MySQL
  • [硬件电路-129]:模拟电路 - 继电器的工作原理、关键指标、常用芯片与管脚定义
  • OSPF知识点整理
  • Flutter 函数的基本使用
  • OpenCV轻松入门_面向python(第一章OpenCV入门)
  • 企业IT管理——集团IT项目实施管理办法模板
  • Linux Deepin深度操作系统应用商店加载失败,安装星火应用商店
  • 学习笔记《区块链技术与应用》第六天 问答 匿名技术 零知识证明
  • 机器翻译的分类:规则式、统计式、神经式MT的核心区别
  • 基于单片机火灾报警系统/防火防盗系统设计
  • 块三角掩码(Block-Triangular Masking)
  • MySQL的创建管理表:
  • Memcached Slab分配器:零碎片的极速内存管理
  • [spring-cloud: 服务发现]-源码解析
  • Day 30:模块和库的导入
  • 风光储综合能源系统双层优化规划设计【MATLAB模型实现】
  • 第九章:了解特殊场景下的redis
  • 控制建模matlab练习07:比例积分控制-③PI控制器的应用
  • Spring 03 Web springMVC
  • ESP32学习-I2C(IIC)通信详解与实践
  • C++:STL中的栈和队列的适配器deque
  • Spring Boot AOP 优雅实现异常重试机制
  • AD方案(OpenLDAP或微软AD)适配信创存在的不足以及可能优化方案
  • Nvidia Orin DK 刷机CUDA TensorRT+硬盘扩容+ROS+Realsense+OpenCV+Ollama+Yolo11 一站式解决方案
  • CUDA杂记--nvcc使用介绍
  • Elastic 9.1/8.19:默认启用 BBQ,ES|QL 支持跨集群搜索(CCS)正式版,JOINS 正式版,集成 Azure AI Foundry
  • Jupyter Notebook 中高效处理和实时展示来自 OpenCV 和 Pillow 的图像数据探究
  • Jetpack Compose for XR:构建下一代空间UI的完整指南
  • SpringBoot+Vue高校实验室预约管理系统 附带详细运行指导视频