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

深度学习实验十二 卷积神经网络(3)——基于残差网络实现手写体数字识别实验

目录

一、模型构建

1.1残差单元

1.2 残差网络的整体结构

二、统计模型的参数量和计算量

三、数据预处理

四、没有残差连接的ResNet18

五、带残差连接的ResNet18

附:完整的可运行代码

实验大体步骤:

先前说明:

上次LeNet实验用到的那个数据集最后的准确率只有92%,但是看学长们的代码和同班同学们的运行结果都是95%+,于是我就尝试换成老师群里的数据集和学长的代码试一试,发现那个数据集运行出来的准确率只有10%。看了看同学的博客,说是换个数据集就可以了。于是我把其替换掉,发现准确率到了95.5%。好用!于是我将其放在这里,需要的同学可以自行下载。
好用的数据集:
通过网盘分享的文件:mnist.gz
链接: https://pan.baidu.com/s/10zpKj-10JgXXLnGEA-AqdA?pwd=41wb 提取码: 41wb

一、模型构建

1.1残差单元

一个残差网络通常有很多个残差单元堆叠而成。
为了减少网络的参数量,在瓶颈结构中会先试用1×1的卷积核来减少通道数,经过3×3卷记得处理后,再使用1×1的卷积恢复通道数。

代码如下:

# 残差单元算子
class ResBlock(nn.Module):def __init__(self, in_channels, out_channels, stride=1, use_residual=True):super(ResBlock, self).__init__()self.stride = strideself.use_residual = use_residual# 第一个卷积层,卷积核大小为3×3,可以设置不同输出通道数以及步长self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1, stride=self.stride)# 第二个卷积层,卷积核大小为3×3,不改变输入特征图的形状,步长为1self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)# 如果conv2的输出和此残差块的输入数据形状不一致,则use_1x1conv = True# 当use_1x1conv = True,添加1个1x1的卷积作用在输入数据上,使其形状变成跟conv2一致if in_channels != out_channels or stride != 1:self.use_1x1conv = Trueelse:self.use_1x1conv = False# 当残差单元包裹的非线性层输入和输出通道数不一致时,需要用1×1卷积调整通道数后再进行相加运算if self.use_1x1conv:self.shortcut = nn.Conv2d(in_channels, out_channels, 1, stride=self.stride)# 每个卷积层后会接一个批量规范化层,批量规范化的内容在7.5.1中会进行详细介绍self.bn1 = nn.BatchNorm2d(out_channels)self.bn2 = nn.BatchNorm2d(out_channels)if self.use_1x1conv:self.bn3 = nn.BatchNorm2d(out_channels)def forward(self, inputs):y = F.relu(self.bn1(self.conv1(inputs)))y = self.bn2(self.conv2(y))if self.use_residual:if self.use_1x1conv:  # 如果为真,对inputs进行1×1卷积,将形状调整成跟conv2的输出y一致shortcut = self.shortcut(inputs)shortcut = self.bn3(shortcut)else:  # 否则直接将inputs和conv2的输出y相加shortcut = inputsy = torch.add(shortcut, y)out = F.relu(y)return out

1.2 残差网络的整体结构

将ResNet18网络划分为6个模块:

·第一模块:包含了一个步长为2,大小为7×7的卷积层,卷积层的输出通道数为64,卷积层的输出经过批量归一化、ReLU激活函数的处理后,接了一个步长为2的3×3的最大汇聚层;

·第二模块:包含了两个残差单元,经过运算后,输出通道数为64,特征图的尺寸保持不变;

·第三模块:包含了两个残差单元,经过运算后,输出通道数为128,特征图的尺寸缩小一半;

·第四模块:包含了两个残差单元,经过运算后,输出通道数为256,特征图的尺寸缩小一半;

·第五模块:包含了两个残差单元,经过运算后,输出通道数为512,特征图的尺寸缩小一半;

·第六模块:包含了一个全局平均汇聚层,将特征图变为1×1的大小,最终经过全连接层计算出最后的输出。

代码如下:

# 定义模块一
def make_first_module(in_channels):# 模块一:7*7卷积、批量规范化、汇聚m1 = nn.Sequential(nn.Conv2d(in_channels, 64, 7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))return m1# 模块二到模块五
def resnet_module(input_channels, out_channels, num_res_blocks, stride=1, use_residual=True):blk = []# 根据num_res_blocks,循环生成残差单元for i in range(num_res_blocks):if i == 0:  # 创建模块中的第一个残差单元blk.append(ResBlock(input_channels, out_channels,stride=stride, use_residual=use_residual))else:  # 创建模块中的其他残差单元blk.append(ResBlock(out_channels, out_channels, use_residual=use_residual))return blk# 封装模块二到模块五
def make_modules(use_residual):# 模块二:包含两个残差单元,输入通道数为64,输出通道数为64,步长为1,特征图大小保持不变m2 = nn.Sequential(*resnet_module(64, 64, 2, stride=1, use_residual=use_residual))# 模块三:包含两个残差单元,输入通道数为64,输出通道数为128,步长为2,特征图大小缩小一半。m3 = nn.Sequential(*resnet_module(64, 128, 2, stride=2, use_residual=use_residual))# 模块四:包含两个残差单元,输入通道数为128,输出通道数为256,步长为2,特征图大小缩小一半。m4 = nn.Sequential(*resnet_module(128, 256, 2, stride=2, use_residual=use_residual))# 模块五:包含两个残差单元,输入通道数为256,输出通道数为512,步长为2,特征图大小缩小一半。m5 = nn.Sequential(*resnet_module(256, 512, 2, stride=2, use_residual=use_residual))return m2, m3, m4, m5# 定义完整网络
class Model_ResNet18(nn.Module):def __init__(self, in_channels=3, num_classes=10, use_residual=True):super(Model_ResNet18, self).__init__()m1 = make_first_module(in_channels)m2, m3, m4, m5 = make_modules(use_residual)# 封装模块一到模块6self.net = nn.Sequential(m1, m2, m3, m4, m5,# 模块六:汇聚层、全连接层nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(512, num_classes))def forward(self, x):return self.net(x)

二、统计模型的参数量和计算量

代码如下:

# 参数量
from torchsummary import summary
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # PyTorch v0.4.0
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True).to(device)
summary(model, (1, 32, 32))# 计算量
from thop import profile
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # PyTorch v0.4.0
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True).to(device)
dummy_input = torch.randn(1, 1, 32, 32).to(device)flops, params = profile(model, (dummy_input,))
print(flops)

 参数量:

计算量:

从输出可以看出,参数量和计算量比LeNet多的不是一点半点,LeNet的参数量是6万多,计算量是41万多,参数量上去了,运行时间肯定也就多了。

三、数据预处理

代码如下:

# 打印并观察数据集分布情况
train_set, dev_set, test_set = json.load(gzip.open('./mnist.gz'))
train_images, train_labels = train_set[0][:1000], train_set[1][:1000]
dev_images, dev_labels = dev_set[0][:200], dev_set[1][:200]
test_images, test_labels = test_set[0][:200], test_set[1][:200]
train_set, dev_set, test_set = [train_images, train_labels], [dev_images, dev_labels], [test_images, test_labels]
print('Length of train/dev/test set:{}/{}/{}'.format(len(train_set[0]), len(dev_set[0]), len(test_set[0])))import numpy as np
import matplotlib.pyplot as plt
import PIL.Image as Imageimage, label = train_set[0][0], train_set[1][0]
image, label = np.array(image).astype('float32'), int(label)
# 原始图像数据为长度784的行向量,需要调整为[28,28]大小的图像
image = np.reshape(image, [28, 28])
image = Image.fromarray(image.astype('uint8'), mode='L')
print("The number in the picture is {}".format(label))
plt.figure(figsize=(5, 5))
plt.imshow(image)
plt.savefig('conv-number5.pdf')# # 定义训练集、验证集和测试集
# train_set = {"images": train_images, "labels": train_labels}
# dev_set = {"images": dev_images, "labels": dev_labels}
# test_set = {"images": test_images, "labels": test_labels}# 数据预处理
transforms = transforms.Compose([transforms.Resize(32), transforms.ToTensor(), transforms.Normalize(mean=[0.5], std=[0.5])])class MNIST_dataset(data.Dataset):def __init__(self, dataset, transforms, mode='train'):self.mode = modeself.transforms = transformsself.dataset = datasetdef __getitem__(self, idx):# 从字典中获取图像和标签# image, label = self.dataset["images"][idx], self.dataset["labels"][idx]image, label = self.dataset[0][idx], self.dataset[1][idx]image, label = np.array(image).astype('float32'), int(label)image = np.reshape(image, [28, 28])image = Image.fromarray(image.astype('uint8'), mode='L')image = self.transforms(image)return image, labeldef __len__(self):# 返回图像数量# return len(self.dataset["images"])return len(self.dataset[0])# 加载 mnist 数据集
train_dataset = MNIST_dataset(dataset=train_set, transforms=transforms, mode='train')
test_dataset = MNIST_dataset(dataset=test_set, transforms=transforms, mode='test')
dev_dataset = MNIST_dataset(dataset=dev_set, transforms=transforms, mode='dev')

四、没有残差连接的ResNet18

代码如下:

time1 = time.time()
seed = 300
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)  # 如果使用 GPU,还可以设置 CUDA 的随机种子
torch.backends.cudnn.deterministic = True  # 使得 CUDA 确定性计算
torch.backends.cudnn.benchmark = False     # 防止优化导致不一致
# 学习率大小
lr = 0.005
# 批次大小
batch_size = 64
# 加载数据
train_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = data.DataLoader(dev_dataset, batch_size=batch_size)
test_loader = data.DataLoader(test_dataset, batch_size=batch_size)
# 定义网络,不使用残差结构的深层网络
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=False)
# 定义优化器
optimizer = opt.SGD(lr=lr, params=model.parameters())
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy(is_logist=True)
# 实例化RunnerV3
runner = RunnerV3(model, optimizer, loss_fn, metric)
# 启动训练
log_steps = 15
eval_steps = 15
runner.train(train_loader, dev_loader, num_epochs=10, log_steps=log_steps,eval_steps=eval_steps, save_path="best_model.pdparams")
time2 = time.time()
# 可视化观察训练集与验证集的Loss变化情况
plot(runner, 'cnn-loss2.pdf')# 加载最优模型
runner.load_model('best_model.pdparams')
# 模型评价
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
print("没有残差连接的ResNet18的运行时间:", time2-time1)

准确率只有66%。但是后面发现调一调参数准确率也是可以达到94.5%的,但是这里不做体现了

因为我们要确保参数一致,再去对比有误残差连接的准确率,这样对比才是有意义的,通过调参而再去对比,就少了一定的说服了,也没有意义。 

五、带残差连接的ResNet18

代码如下:

time3 = time.time()
# 固定随机种子
seed = 300
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)  # 如果使用 GPU,还可以设置 CUDA 的随机种子
torch.backends.cudnn.deterministic = True  # 使得 CUDA 确定性计算
torch.backends.cudnn.benchmark = False     # 防止优化导致不一致
# 加载 mnist 数据集
train_dataset = MNIST_dataset(dataset=train_set, transforms=transforms, mode='train')
test_dataset = MNIST_dataset(dataset=test_set, transforms=transforms, mode='test')
dev_dataset = MNIST_dataset(dataset=dev_set, transforms=transforms, mode='dev')
# 学习率大小
lr = 0.005
# 批次大小
batch_size = 16
# 加载数据
train_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = data.DataLoader(dev_dataset, batch_size=batch_size)
test_loader = data.DataLoader(test_dataset, batch_size=batch_size)
# 定义网络,通过指定use_residual为True,使用残差结构的深层网络
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True)
# 定义优化器
optimizer = opt.SGD(lr=lr, params=model.parameters())
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy(is_logist=True)
# 实例化RunnerV3
runner = RunnerV3(model, optimizer, loss_fn, metric)
# 启动训练
log_steps = 15
eval_steps = 15
runner.train(train_loader, dev_loader, num_epochs=10, log_steps=log_steps,eval_steps=eval_steps, save_path="best_model.pdparams")
time4 = time.time()
# 可视化观察训练集与验证集的Loss变化情况
plot(runner, 'cnn-loss3.pdf')# 加载最优模型
runner.load_model('best_model.pdparams')
# 模型评价
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
print("有残差连接的ResNet18的运行时间:", time4-time3)

 有残差连接的网络比没有残差连接的网络准确率高很多,那么这是为什么呢?

ResNet的论文中提到,更深的网络有更高的训练误差,所以就会有更高的测试误差。
同时来说,随着网络深度的增加,会带来了许多优化相关的问题,比如梯度消散,梯度爆炸。也有人将其比做成一个传话游戏,就是越往后传,错误也就越高。

再举一个栗子:
假如有一个网络,输入x=1,非残差网络为G,残差网络为H,其中H(x)=F(x)+x,假如有这样的输入关系:


因为两者各自是对G的参数和F的参数进行更新,可以看出变化对F的影响远远大于G,说明引入残差后的映射对输出的变化更敏感,这样是有利于网络进行传播的。

从论文中的对比也可以看出,残差网络layer升高后,error并没有像普通网络一样也升高。

再对比运行时间:

 两者虽然相差不大,但是却比LeNet5的时间长很多,LeNet5训练只需要3秒多。

附:完整的可运行代码

主代码:

time3 = time.time()
# 固定随机种子
seed = 300
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)  # 如果使用 GPU,还可以设置 CUDA 的随机种子
torch.backends.cudnn.deterministic = True  # 使得 CUDA 确定性计算
torch.backends.cudnn.benchmark = False     # 防止优化导致不一致
# 加载 mnist 数据集
train_dataset = MNIST_dataset(dataset=train_set, transforms=transforms, mode='train')
test_dataset = MNIST_dataset(dataset=test_set, transforms=transforms, mode='test')
dev_dataset = MNIST_dataset(dataset=dev_set, transforms=transforms, mode='dev')
# 学习率大小
lr = 0.005
# 批次大小
batch_size = 16
# 加载数据
train_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = data.DataLoader(dev_dataset, batch_size=batch_size)
test_loader = data.DataLoader(test_dataset, batch_size=batch_size)
# 定义网络,通过指定use_residual为True,使用残差结构的深层网络
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True)
# 定义优化器
optimizer = opt.SGD(lr=lr, params=model.parameters())
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy(is_logist=True)
# 实例化RunnerV3
runner = RunnerV3(model, optimizer, loss_fn, metric)
# 启动训练
log_steps = 15
eval_steps = 15
runner.train(train_loader, dev_loader, num_epochs=10, log_steps=log_steps,eval_steps=eval_steps, save_path="best_model.pdparams")
time4 = time.time()
# 可视化观察训练集与验证集的Loss变化情况
plot(runner, 'cnn-loss3.pdf')# 加载最优模型
runner.load_model('best_model.pdparams')
# 模型评价
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
print("有残差连接的ResNet18的运行时间:", time4-time3)

nndl_3代码:

import torch
from matplotlib import pyplot as plt
from torch import nnclass Op(object):def __init__(self):passdef __call__(self, inputs):return self.forward(inputs)def forward(self, inputs):raise NotImplementedErrordef backward(self, inputs):raise NotImplementedError# 实现一个两层前馈神经网络
class Model_MLP_L2_V3(torch.nn.Module):def __init__(self, input_size, hidden_size, output_size):super(Model_MLP_L2_V3, self).__init__()self.fc1 = torch.nn.Linear(input_size, hidden_size)w_ = torch.normal(0, 0.01, size=(hidden_size, input_size), requires_grad=True)self.fc1.weight = torch.nn.Parameter(w_)self.fc1.bias = torch.nn.init.constant_(self.fc1.bias, val=1.0)self.fc2 = torch.nn.Linear(hidden_size, output_size)w2 = torch.normal(0, 0.01, size=(output_size, hidden_size), requires_grad=True)self.fc2.weight = nn.Parameter(w2)self.fc2.bias = torch.nn.init.constant_(self.fc2.bias, val=1.0)self.act = torch.sigmoiddef forward(self, inputs):outputs = self.fc1(inputs)outputs = self.act(outputs)outputs = self.fc2(outputs)return outputsclass RunnerV3(object):def __init__(self, model, optimizer, loss_fn, metric, **kwargs):self.model = modelself.optimizer = optimizerself.loss_fn = loss_fnself.metric = metric  # 只用于计算评价指标# 记录训练过程中的评价指标变化情况self.dev_scores = []# 记录训练过程中的损失函数变化情况self.train_epoch_losses = []  # 一个epoch记录一次lossself.train_step_losses = []  # 一个step记录一次lossself.dev_losses = []# 记录全局最优指标self.best_score = 0def train(self, train_loader, dev_loader=None, **kwargs):# 将模型切换为训练模式self.model.train()# 传入训练轮数,如果没有传入值则默认为0num_epochs = kwargs.get("num_epochs", 0)# 传入log打印频率,如果没有传入值则默认为100log_steps = kwargs.get("log_steps", 100)# 评价频率eval_steps = kwargs.get("eval_steps", 0)# 传入模型保存路径,如果没有传入值则默认为"best_model.pdparams"save_path = kwargs.get("save_path", "best_model.pdparams")custom_print_log = kwargs.get("custom_print_log", None)# 训练总的步数num_training_steps = num_epochs * len(train_loader)if eval_steps:if self.metric is None:raise RuntimeError('Error: Metric can not be None!')if dev_loader is None:raise RuntimeError('Error: dev_loader can not be None!')# 运行的step数目global_step = 0# 进行num_epochs轮训练for epoch in range(num_epochs):# 用于统计训练集的损失total_loss = 0for step, data in enumerate(train_loader):X, y = data# 获取模型预测logits = self.model(X)loss = self.loss_fn(logits, y)  # 默认求meantotal_loss += loss# 训练过程中,每个step的loss进行保存self.train_step_losses.append((global_step, loss.item()))if log_steps and global_step % log_steps == 0:print(f"[Train] epoch: {epoch}/{num_epochs}, step: {global_step}/{num_training_steps}, loss: {loss.item():.5f}")# 梯度反向传播,计算每个参数的梯度值loss.backward()if custom_print_log:custom_print_log(self)# 小批量梯度下降进行参数更新self.optimizer.step()# 梯度归零self.optimizer.zero_grad()# 判断是否需要评价if eval_steps > 0 and global_step > 0 and \(global_step % eval_steps == 0 or global_step == (num_training_steps - 1)):dev_score, dev_loss = self.evaluate(dev_loader, global_step=global_step)print(f"[Evaluate]  dev score: {dev_score:.5f}, dev loss: {dev_loss:.5f}")# 将模型切换为训练模式self.model.train()# 如果当前指标为最优指标,保存该模型if dev_score > self.best_score:self.save_model(save_path)print(f"[Evaluate] best accuracy performence has been updated: {self.best_score:.5f} --> {dev_score:.5f}")self.best_score = dev_scoreglobal_step += 1# 当前epoch 训练loss累计值trn_loss = (total_loss / len(train_loader)).item()# epoch粒度的训练loss保存self.train_epoch_losses.append(trn_loss)print("[Train] Training done!")# 模型评估阶段,使用'torch.no_grad()'控制不计算和存储梯度@torch.no_grad()def evaluate(self, dev_loader, **kwargs):assert self.metric is not None# 将模型设置为评估模式self.model.eval()global_step = kwargs.get("global_step", -1)# 用于统计训练集的损失total_loss = 0# 重置评价self.metric.reset()# 遍历验证集每个批次for batch_id, data in enumerate(dev_loader):X, y = data# 计算模型输出logits = self.model(X)# 计算损失函数loss = self.loss_fn(logits, y).item()# 累积损失total_loss += loss# 累积评价self.metric.update(logits, y)dev_loss = (total_loss / len(dev_loader))dev_score = self.metric.accumulate()# 记录验证集lossif global_step != -1:self.dev_losses.append((global_step, dev_loss))self.dev_scores.append(dev_score)return dev_score, dev_loss# 模型评估阶段,使用'torch.no_grad()'控制不计算和存储梯度@torch.no_grad()def predict(self, x, **kwargs):# 将模型设置为评估模式self.model.eval()# 运行模型前向计算,得到预测值logits = self.model(x)return logitsdef save_model(self, save_path):torch.save(self.model.state_dict(), save_path)def load_model(self, model_path):model_state_dict = torch.load(model_path)self.model.load_state_dict(model_state_dict)class Accuracy():def __init__(self, is_logist=True):# 用于统计正确的样本个数self.num_correct = 0# 用于统计样本的总数self.num_count = 0self.is_logist = is_logistdef update(self, outputs, labels):if outputs.shape[1] == 1:  # 二分类outputs = torch.squeeze(outputs, dim=-1)if self.is_logist:# logist判断是否大于0preds = torch.tensor((outputs >= 0), dtype=torch.float32)else:# 如果不是logist,判断每个概率值是否大于0.5,当大于0.5时,类别为1,否则类别为0preds = torch.tensor((outputs >= 0.5), dtype=torch.float32)else:# 多分类时,使用'torch.argmax'计算最大元素索引作为类别preds = torch.argmax(outputs, dim=1)# 获取本批数据中预测正确的样本个数labels = torch.squeeze(labels, dim=-1)batch_correct = torch.sum(torch.tensor(preds == labels, dtype=torch.float32)).numpy()batch_count = len(labels)# 更新num_correct 和 num_countself.num_correct += batch_correctself.num_count += batch_countdef accumulate(self):# 使用累计的数据,计算总的指标if self.num_count == 0:return 0return self.num_correct / self.num_countdef reset(self):# 重置正确的数目和总数self.num_correct = 0self.num_count = 0def name(self):return "Accuracy"# 可视化
def plot(runner, fig_name):plt.figure(figsize=(10, 5))plt.subplot(1, 2, 1)train_items = runner.train_step_losses[::30]train_steps = [x[0] for x in train_items]train_losses = [x[1] for x in train_items]plt.plot(train_steps, train_losses, color='#8E004D', label="Train loss")if runner.dev_losses[0][0] != -1:dev_steps = [x[0] for x in runner.dev_losses]dev_losses = [x[1] for x in runner.dev_losses]plt.plot(dev_steps, dev_losses, color='#E20079', linestyle='--', label="Dev loss")# 绘制坐标轴和图例plt.ylabel("loss", fontsize='x-large')plt.xlabel("step", fontsize='x-large')plt.legend(loc='upper right', fontsize='x-large')plt.subplot(1, 2, 2)# 绘制评价准确率变化曲线if runner.dev_losses[0][0] != -1:plt.plot(dev_steps, runner.dev_scores,color='#E20079', linestyle="--", label="Dev accuracy")else:plt.plot(list(range(len(runner.dev_scores))), runner.dev_scores,color='#E20079', linestyle="--", label="Dev accuracy")# 绘制坐标轴和图例plt.ylabel("score", fontsize='x-large')plt.xlabel("step", fontsize='x-large')plt.legend(loc='lower right', fontsize='x-large')plt.savefig(fig_name)plt.show()

这次的分享就到这里,下次再见~

相关文章:

深度学习实验十二 卷积神经网络(3)——基于残差网络实现手写体数字识别实验

目录 一、模型构建 1.1残差单元 1.2 残差网络的整体结构 二、统计模型的参数量和计算量 三、数据预处理 四、没有残差连接的ResNet18 五、带残差连接的ResNet18 附:完整的可运行代码 实验大体步骤: 先前说明: 上次LeNet实验用到的那…...

关于SpringBoot集成Kafka

关于Kafka Apache Kafka 是一个分布式流处理平台,广泛用于构建实时数据管道和流应用。它能够处理大量的数据流,具有高吞吐量、可持久化存储、容错性和扩展性等特性。 Kafka一般用作实时数据流处理、消息队列、事件架构驱动等 Kafka的整体架构 ZooKeeper:…...

windows C#-取消任务列表(上)

如果不想等待异步控制台应用程序完成,可以取消该应用程序。 通过遵循本文的示例,可将取消添加到下载网站内容的应用程序。 可通过将 CancellationTokenSource 实例与每个任务进行关联来取消多个任务。 如果选择 Enter 键,则将取消所有尚未完成…...

RabbitMQ4:work模型

欢迎来到“雪碧聊技术”CSDN博客! 在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将…...

《筑牢安全防线:培养 C++安全编程思维习惯之道》

在当今数字化飞速发展的时代,软件安全的重要性已提升到前所未有的高度。C作为一种广泛应用于系统开发、游戏制作、高性能计算等众多领域的编程语言,其程序的安全性更是关乎重大。培养 C安全编程的思维习惯,不仅是开发者个人能力提升的关键&am…...

Python Flask中集成SQLAlchemy和Flask-Login

在现代Web应用开发中,数据库和用户认证是两个非常重要的功能。Flask作为一个轻量级的Python Web框架,本身只提供了最基本的Web功能。但是,它可以通过集成各种优秀的扩展库来增强功能。本文将介绍如何在Flask应用中集成SQLAlchemy(数据库)和Flask-Login(用户认证),并提供一个完整…...

Kafka 生产者优化与数据处理经验

Kafka:分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…...

web——sqliabs靶场——第十二关——(基于错误的双引号 POST 型字符型变形的注入)

判断注入类型 a OR 1 1# 发现没有报错 ,说明单引号不是闭合类型 测试别的注入条件 a) OR 1 1# a)) OR 1 1# a" OR 11 发现可以用双引号闭合 发现是")闭合 之后的流程还是与11关一样 爆破显示位 先抓包 是post传参,用hackbar来传参 unam…...

Spring |(二)IoC相关内容 | bean

文章目录 📚bean基础配置🐇bean的id和class🐇bean的name属性🐇bean作用范围scope配置🐇bean基础配置小结 📚bean实例化🐇构造方法实例化(常用)🐇静态工厂实例…...

flux的版本

1.flux1-dev.safetensors https://huggingface.co/black-forest-labs/FLUX.1-devhttps://huggingface.co/black-forest-labs/FLUX.1-dev原生的23.8G的模型。原生12B的模型,float16的。需要配合ae.safetensors,flux1-dev.safetensors以及clip-l和T5的权重使用,注意ae.sft和f…...

基于Springboot+Vue的房屋系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 该系统…...

【Bluedroid】A2DP SINK播放流程源码分析

在Bluedroid协议栈中,A2DP(Advanced Audio Distribution Profile)SINK播放流程是一个复杂但有序的过程,它涉及多个层次和组件的交互。 一、概述 1.1. 初始化流程 在A2DP SINK播放之前,系统需要进行一系列初始化操作,以确保A2DP SINK服务能够正确运行。这些操作包括启动…...

【什么是Redis?】

Redis:高性能内存数据库的深度探索 在当今这个数据驱动的世界里,数据库的选择直接关系到应用程序的性能、可扩展性和可靠性。在众多数据库解决方案中,Redis以其卓越的性能、丰富的数据结构和灵活的使用场景脱颖而出,成为众多开发…...

TCL大数据面试题及参考答案

Mysql 索引失效的场景 对索引列进行运算或使用函数:当在索引列上进行数学运算、函数操作等,索引可能失效。例如,在存储年龄的列上建立了索引,若查询语句是 “SELECT * FROM table WHERE age + 1 = 20”,这里对索引列 age 进行了加法运算,数据库会放弃使用索引而进行全表扫…...

提高总线数据传输率

提高总线数据传输率是一个涉及多个方面的技术问题,以下是一些有效的方法: 一、提高总线时钟频率 总线时钟频率是影响总线传输速率的重要因素之一。通过提高总线时钟频率,可以缩短每个时钟周期的时间,从而在相同的时间内传输更多…...

_FYAW智能显示控制仪表的简单使用_串口通信

一、简介 该仪表可以实时显示位移传感器的测量值,并可设定阈值等。先谈谈简单的使用方法,通过说明书,我们可以知道长按SET键可以进入参数选择状态,按“↑”“↓”可以选择该组参数的上一个或者下一个参数。 从参数一览中可以看到有…...

图的遍历。

图的遍历这一部分,离不开广度优先和深度优先,如果大家已经学过搜索算法的话,这部分将是易如反掌。 万能搜索算法-CSDN博客 文章中不会提太多离散数学中图的专有名词,因为本篇博客只涉及最简单的图的遍历,故以练习题为主…...

Methode Electronics EDI 需求分析

Methode Electronics 是一家总部位于美国的全球性技术公司,专注于设计和制造用于多个行业的电子和电气组件,产品涵盖汽车、工业、电信、医疗设备以及消费电子等多个领域,提供创新的解决方案。 填写Methode_EDI_Parameters_Template Methode_…...

IT资产管理工具-NetBox

IT资产管理工具-NetBox 推荐一款IT资产管理工具 了解推荐阅读官方中文文档 https://docs.wangluohe.com/introduction/ 硬件要求 ​ - 建议4Core 8G以上,100G存储空间 这里我使用的Linux镜像为 CentOS8-Stream 提前关闭Selinux和防火墙 部署NetBox 一&#…...

uniapp接入BMapGL百度地图

下面代码兼容安卓APP和H5 百度地图官网:控制台 | 百度地图开放平台 应用类别选择《浏览器端》 /utils/map.js 需要设置你自己的key export function myBMapGL1() {return new Promise(function(resolve, reject) {if (typeof window.initMyBMapGL1 function) {r…...

AWTK 最新动态:支持鸿蒙系统(HarmonyOS Next)

HarmonyOS是全球第三大移动操作系统,有巨大的市场潜力,在国产替代的背景下,机会多多,AWTK支持HarmonyOS,让AWTK开发者也能享受HarmonyOS生态的红利。 AWTK全称为Toolkit AnyWhere,是ZLG倾心打造的一套基于C…...

React基础知识一

写的东西太多了,照成csdn文档编辑器都开始卡顿了,所以分篇写。 1.安装React 需要安装下面三个包。 react:react核心包 react-dom:渲染需要用到的核心包 babel:将jsx语法转换成React代码的工具。(没使用jsx可以不装)1.1 在html中…...

Oracle热备过程中对数据库崩溃的处理方法

引言 在热备过程中如果发生数据库崩溃、断电等情况该如何处理? 如果正在备份 users 表空间的数据文件过程中,此时的数据文件表头 SCN 会被锁定,此时正在复制数据文件时数据库崩溃,系统断电。 从而导致数据文件表头与控制文件中的不一致,导致数据库无法打开,会要求介质恢…...

身份证实名认证API接口助力电商购物安全

亲爱的网购达人们,你们是否曾经因为网络上的虚假信息和诈骗而感到困扰?在享受便捷的网购乐趣时,如何确保交易安全成为了我们共同关注的话题。今天,一起来了解一下翔云身份证实名认证接口如何为电子商务保驾护航,让您的…...

win10 禁止更新

一、winR 输入 regedit 二、输入注册列表路径: (1)计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings (2)按照格式,创建文件命名: FlightSettingsMaxPauseDays (3&…...

运维百科:网络性能20大关键指标

网络性能评估是确保网络服务质量和用户体验的关键环节。在网络、运维领域中,存在着一系列关键的性能指标,共同构成了衡量网络性能的基础。以下是网络性能的20大关键指标,每个指标都承载着特定的意义和重要性。 1.速率(Rate&#…...

java编程开发基础,正则表达式的使用案例Demo

java编程开发基础,正则表达式的使用案例Demo!实际开发中,经常遇到一些字符串,信息的裁剪和提取操作,正则表达式是经常使用的,下面的案例,可以帮助大家快速的了解和熟悉,正则表达式的使用技巧。 package com…...

结构控制

目录​​​​​​​ 1.顺序结构 2.分支结构 2.1.单分支结构 2.2.二分支结构 2.3.多分支结构 2.4.嵌套分支结构 3.循环结构 3.1.while 循环结构 3.2.while...else 循环结构 PS:break 关键字 PS:pass 关键字 3.3.for 循环结构 PS:…...

Go语言中的内存分配与初始化:new与make函数详解

在Go语言中,内存分配和初始化是编程的基础操作。Go提供了两个内置函数new和make,用于不同场景下的内存分配和初始化。理解这两个函数的区别和适用场景对于编写高效、安全的Go代码至关重要。本文将详细介绍new和make函数,并提供示例说明它们的…...

The 2024 ICPC Kunming Invitational Contest

VP链接:https://codeforces.com/gym/105386 B. Gold Medal 签到题。对每一个读入的数 a,先记录已有奖牌数量,即 ,再将 a 对 k 取模。然后将 a 数组从大到小排序,将每个不足 k 的数补到 k。如果 m 有剩余,…...

对原jar包解压后修改原class文件后重新打包为jar

文章目录 背景三种修改方式1.POM中移除原jar中依赖的历史版本2.原jar它不使用pom依赖而是直接放在源码中再编译使用JarEditor 插件对源码进行修改(推荐)使用java-decompiler反编译后修改源码覆盖原class(不好用-不推荐直接跳过)提醒 参考资料-推荐阅读拓…...

【C++】ReadFile概述,及实践使用时ReadFile的速率影响研究

ReadFile 函数概述 ReadFile 是 Windows API 函数,用于从文件或设备(如串口、硬盘等)中读取数据。它是同步和异步 I/O 操作的基础函数。 函数原型 BOOL ReadFile(_In_ HANDLE hFile, // 文件或设备句柄_Out_write…...

WebGL进阶(十一)层次模型

理论基础&#xff1a; 效果&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vie…...

Django:从入门到精通

一、Django背景 Django是一个由Python编写的高级Web应用框架&#xff0c;以其简洁性、安全性和高效性而闻名。Django最初由Adrian Holovaty和Simon Willison于2003年开发&#xff0c;旨在简化Web应用的开发过程。作为一个开放源代码项目&#xff0c;Django迅速吸引了大量的开发…...

C++设计模式行为模式———中介者模式

文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…...

Perfetto学习大全

Perfetto 是一个功能强大的性能分析和追踪工具&#xff0c;主要用于捕获和分析复杂系统中的事件和性能数据&#xff0c;特别是在 Android 和 Linux 环境下。它的核心目标是帮助开发者深入了解系统和应用程序的运行状态&#xff0c;以便优化性能和诊断问题。 Perfetto的主要作用…...

管家婆财贸ERP BR040.销售单明细表变更

最低适用版本&#xff1a; C系列 23.8 插件简要功能说明&#xff1a; 销售明细表支持直接修改单据自由项1-16更多细节描述见下方详细文档 插件操作视频&#xff1a; 进销存类定制插件--销售单明细表变更 1. 应用中心增加菜单【销售单明细表变更】 a. 复制23.8版本销售单明细…...

2411rust,实现特征

原文 在Rust2024中,impl Trait在中位置的默认工作方式有了变化.是为了简化impl Trait,以更好地匹配人们一般的需求. 还添加了一个灵活的语法,让你需要时可完全控制. 从Rust2024开始,一直在更改,何时可在返回位置impl Trait的隐藏类型中使用泛型参数的规则: 1,即对返回位置i…...

SpringBoot3与JUnit5集成测试

你可以在 Spring Boot 3 中轻松设置和运行 JUnit 集成测试。合理使用 Spring 提供的注解和工具&#xff0c;可以确保测试的高效性和可靠性。以下是集成测试的步骤和示例&#xff1a; 1. 添加依赖 在 pom.xml 中添加 Spring Boot Starter Test 依赖&#xff0c;它包含 JUnit 5 …...

工程企业需要什么样的物资管理系统?为什么需要物资管理系统?

一、背景与意义 在工程项目的建设中&#xff0c;无论是高楼大厦的拔地而起&#xff0c;还是高速公路的绵延铺展&#xff0c;物资都是最基础的要素之一。从钢筋水泥到施工机械&#xff0c;任何一种物资的管理不善都可能导致项目延误、成本超支&#xff0c;甚至质量问题。然而&a…...

Vue + Websocket播放PCM(base64转ArrayBuffer、 字符串转ArrayBuffer)

文章目录 引言I 音视频处理相关概念和APIII 案例:基于开源库 pcm-player方式播放借助MediaSource和Audio对象播放音频流。基于原生api AudioContext 播放操作III 格式转换js字符串转ArrayBufferbase64 转 ArrayBufferIV 解决pcm-player分片播放问题引言 需求: 基于webscoket传…...

华为防火墙技术基本概念学习笔记

1.防火墙概述 1.1防火墙与交换机、路由器对比 路由器与交换机的本质是转发&#xff0c;防火墙的本质是控制。 防火墙与路由器、交换机是有区别的。路由器用来连接不同的网络&#xff0c;通过路由协议保证互联互通&#xff0c;确保将报文转发到目的地;交换机则通常用来组建局域…...

Mesh路由组网

Mesh无线网格网络&#xff0c;多跳&#xff08;multi-hop&#xff09;网络&#xff0c;为解决全屋覆盖信号&#xff0c;一般用于家庭网络和小型企业 原理 网关路由器&#xff08;主路由&#xff0c;连接光猫&#xff09;&#xff0c;Mesh路由器&#xff08;子路由&#xff0c;…...

【数据结构】七种常用排序总结

一、七种排序及其讲解 以下为七种排序的讲解&#xff1a; 【数据结构】插入排序——直接插入排序 和 希尔排序 【数据结构】选择排序——选择排序 和 堆排序 【数据结构】交换排序——冒泡排序 和 快速排序 【数据结构】归并排序 —— 递归及非递归解决归并排序 二、排序的…...

【在Linux世界中追寻伟大的One Piece】多线程(一)

目录 1 -> Linux线程概念 1.1 -> 什么是线程 1.2 -> 线程的优点 1.3 -> 线程的缺点 1.4 -> 线程异常 1.5 -> 线程用途 2 -> Linux线程 VS 进程 2.1 -> 线程和进程 2.2 -> 进程的多个线程共享 3 -> Linux线程控制 3.1 -> POSIX线程…...

《Python编程实训快速上手》第十天--处理CSV文件和JSON数据

CSV&#xff1a;简化的电子表格&#xff0c;被保存为纯文本文件 JSON&#xff1a;是一种数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成&#xff0c;以JavaScript源代码的形式将信息保存在纯文本文件中 一、csv模块 CSV文件中的每行代表电…...

基于springboot停车场管理系统源码和论文

如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统停车场管理系统信息管理难度大&#xff0c;容错率低&#xff0c;…...

Linux的桌面

Linux的桌面是可以卸载的 的确&#xff0c;Linux并不像Windows&#xff0c;Linux本身是一个基于命令行的操作系统&#xff0c;在内核眼中&#xff0c;桌面只不过是个普通的应用程序&#xff0c;所以&#xff0c;在Linux的桌面中可以完成的事情&#xff0c;命令行中也基本可以完…...

Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE

Spring Boot 3.0废弃了JavaEE&#xff0c;改用了Jakarta EE 历史背景 javax变成Jakarta的主要原因是因为Java EE项目从Oracle转移到了Eclipse Foundation&#xff0c;并改名为Jakarta EE。 JavaEE是从Java 1.2版本开始推出的Java企业级开发平台&#xff0c;最初的名称是J2EE(J…...

java-排序算法汇总

排序算法&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09; 选择排序&#xff08;Selection Sort&#xff09; 插入排序&#xff08;Insertion Sort&#xff09; 快速排序&#xff08;Quick Sort&#xff09; 归并排序&#xff08;Merge Sort&#xff09; 堆排序&…...