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

经典CNN(三):DenseNet算法实战与解析

  •  🍨 本文为🔗365天深度学习训练营中的学习记录博客
  • 🍖 原作者:K同学啊|接辅导、项目定制

 1 前言

    在计算机视觉领域,卷积神经网络(CNN)已经成为最主流的方法,比如GoogleNet,VGG-16,Incepetion等模型。CNN史上的一个里程碑事件是ResNet模型的出现,ResNet可以训练出更深的CNN模型,从而实现更高的准确率。ResNet模型的核心是通过建立前面层与后面层之间的“短路连接”(shortcut, skip connection),进而训练出更深的CNN网络。

    DenseNet模型的基本思路与ResNet一致,但是它建立的是前面所有层与后面层的紧密连接(dense connection),它的名称也是由此而来。DenseNet的另一大特色是通过特征在channel上的的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能,DenseNet也因此斩获CVPR2017的最佳论文奖。

图1 Dense模块(5-layer,growth rate of k=4)

     其中DenseNet论文原文地址为:https://arxiv.org/pdf/1608.06993v5.pdf

 2 设计理念

    相比ResNet,DenseNet提出了一个更激进的密集连接机制:即互相连接所有的层,具体来说就是每个层都会接受前面所有层作为额外的输入。

    图3为ResNet网络的残差连接机制,作为对比,图4为DenseNet的密集连接机制。可以看到,ResNet是每个层与前面的某层(一般是2~4层)短路连接在一起,连接方式是通过元素相加。而在DenseNet中,每个层都会与前面所有层在channel维度上链接(concat)在一起(即元素叠加),并作为下一层的输入。

    对于一个L层的网络,DenseNet共包含{\tfrac{L(L+1)}{2}}个连接,相比ResNet,这是一种密集连接。而且DenseNet是直接concat来自不同层的特征图,这可以实现特征重用,提升效率,这一特点是DenseNet与ResNet最主要的区别。

2.1 标准神经网络

image.png
图2 标准的神经网络传播过程

    图2是一个标准的神经网络传播过程示意图,输入和输出的公式是X_{l}=H_{l}(X_{l-1}),其中 H_{l}是一个组合函数,通常包括BN、ReLu、Pooling、Conv等操作,X_{l-1}是第l层的输入的特征图(来自于l-1层的输出),X_{l}是第l层的输出的特征图。

2.2 ResNet

image.png
图3 ResNet网络的短路连接机制(+代表元素级相加操作)

     图3是ResNet的网络连接机制,由图可知是跨层相加,输入和输出的公式是X_{l}=H_{l}(X_{l-1})+X_{l-1}

 2.3 DenseNet

image.png
图4 DenseNet网络的密集连接机制(其中C代表层级的concat操作)

    图4为DenseNet的连接机制,采用跨通道的concat的形式连接,会连接前面所有层作为输入,输入和输出的公式是X_{l}=H_{l}(X_{0},X_{1},...X_{l-1})。这里要注意所有层的输入都来源于前面所有层在channel维度的concat,以下动图形象表示这一操作。

2020090311071451.gif
图5 DenseNet前向过程

 3 网络结构

    网络的具体实现细节如图6所示。

image.png
图6 DenseNet的网络结构

     CNN网络一般要经过Pooling或者stride>1的Conv来降低特征图的大小,而DenseNet的密集连接方式需要特征图大小保持一致。为了解决这个问题,DenseNet网络中使用DenseBlock+Transition的结构,其中DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。而Transition层是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。图7给出了DenseNet的网络结构,它共包含4个DenseBlock,各个DenseBlock之间通过Transition层连接在一起。

图7 使用DenseBlock+Transition的DenseNet网络

 

    在DenseBlock中,各个层的特征图大小一致,可以在channel维度上连接。DenseBlock中的非线性组合函数H(.)的是BN+ReLU+3*3Conv的结构,如图8所示。另外,与ResNet不同,所有DenseBlock中各个层卷积之后均输出k个特征图,即得到的特征图的channel数为k,或者说采用k个卷积核。k在DenseNet称为growth rate,这是一个超参数。一般情况下使用较小的k(比如12),就可以得到较佳的性能。假定输入层的特征图的channel数为k_{0},那么l层输入的channel数为k_{0}+k_{(1,2,...,l-1)},因此随着层数的增加,尽管k设定的较小,DenseBlock的输入会非常多,不过这是由于特征重用所造成的,每个层仅有k个特征是自己独有的。

image.png
图8 DenseBlock中的非线性转换结构

     由于后面层的输入会非常大,DenseBlock内部采用bottleneck层来减少计算量,主要是原有的结构中增加1*1Conv,如图9所示,即BN+ReLU+1*1Conv+BN+ReLU+3*3Conv,称为DenseNet-B结构。其中1*1Conv得到4k个特征图,它起到的作用是降低特征数量,从而提升计算效率。

image.png
图9 使用bottleneck层的DenseBlock结构

     对于Trasition层,它主要是连接两个相邻的DenseBlock,并且降低特征图大小。Transition层包括一个1*1的卷积和2*2的AvgPooling,结构为BN+ReLU+1*1Conv+2*2AvgPooling。另外,Transition层可以起到压缩模型的作用。假定Transition层的上接DenseBlock得到特征图channels数为m,Transition层可以产生\theta m个特征(通过卷积层),其中\theta\in (0,1]是压缩系数(compression rate)。当\theta =1时,特征个数经过Transition层没有变化,即无压缩,而当压缩系数小于1时,这种结构称为DenseNet-C,文中使用\theta =0.5。对于使用bootleneck层的DenseBlock结构和压缩系数小于1的Transition组合机构称为DenseNet-BC。

    对于ImageNet数据集,图片输入大小为224*224,网络结构采用包含4个DenseBlock的DenseNet-BC,其首先是一个stride=2的7*7卷积层,然后是一个stride=2的3*3MaxPooling层,后面才进入DenseBlock。ImageNet数据集所采用的网络配置如表1所示:

表1 ImageNet数据集上所采用的DenseNet结构

 4 效果对比

image.png
图10 在CIFA-10数据集上ResNet vs DenseNet

 

5 使用Pytroch实现DenseNet121

图11 DenseNet121网络结构图

     图11为DenseNet121的具体网络结构,它与表1中的DenseNet121相对应。左边是整个DenseNet121的网络结构,其中粉色为DenseBlock,最右侧为其详细结构,灰色为Transition,中间为其详细结构。

5.1 前期工作

5.1.1 开发环境

电脑系统:ubuntu16.04

编译器:Jupter Lab

语言环境:Python 3.7

深度学习环境:pytorch

 5.1.2 设置GPU

    如果设备上支持GPU就使用GPU,否则注释掉这部分代码。

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os, PIL, pathlib, warningswarnings.filterwarnings("ignore")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")print(device)

5.1.3 导入数据

import os,PIL,random,pathlibdata_dir_str = '../data/bird_photos'
data_dir = pathlib.Path(data_dir_str)
print("data_dir:", data_dir, "\n")data_paths = list(data_dir.glob('*'))
classNames = [str(path).split('/')[-1] for path in data_paths]
print('classNames:', classNames , '\n')train_transforms = transforms.Compose([transforms.Resize([224, 224]),  # resize输入图片transforms.ToTensor(),  # 将PIL Image或numpy.ndarray转换成tensortransforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])  # 从数据集中随机抽样计算得到
])total_data = datasets.ImageFolder(data_dir_str, transform=train_transforms)
print(total_data)
print(total_data.class_to_idx)

    结果输出如图:

 

 5.1.4 划分数据集

train_size = int(0.8 * len(total_data))
test_size = len(total_data) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
print(train_dataset, test_dataset)batch_size = 4
train_dl = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size,shuffle=True,num_workers=1,pin_memory=False)
test_dl = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size,shuffle=True,num_workers=1,pin_memory=False)for X, y in test_dl:print("Shape of X [N, C, H, W]:", X.shape)print("Shape of y:", y.shape, y.dtype)break

image.png

     结果输出如图:

 5.2 搭建DenseNet121

5.2.1 DenseBlock中的Bottleneck

import torch
from torch import nnclass _DenseLayer(nn.Sequential):"""DenseBlock的基本单元(使用bottleneck)"""def __init__(self, num_input_features, growth_rate, bn_size, drop_rate):super(_DenseLayer, self).__init__()self.add_module("norm1", nn.BatchNorm2d(num_input_features))self.add_module("relu1", nn.ReLU(inplace=True))self.add_module("conv1", nn.Conv2d(num_input_features, bn_size*growth_rate,kernel_size=1, stride=1, bias=False))self.add_module("norm2", nn.BatchNorm2d(bn_size*growth_rate))self.add_module("relu2", nn.ReLU(inplace=True))self.add_module("conv2", nn.Conv2d(bn_size*growth_rate, growth_rate,kernel_size=3, stride=1, padding=1, bias=False))self.drop_rate = drop_ratedef forward(self, x):new_features = super(_DenseLayer, self).forward(x)if self.drop_rate > 0:new_features = F.dropout(new_features, p=self.drop_rate, training=self.training)return torch.cat([x, new_features], 1)

5.2.2 DenseBlock层

class _DenseBlock(nn.Sequential):def __init__(self, num_layer, num_input_features, bn_size, growth_rate, drop_rate):super(_DenseBlock, self).__init__()for i in range(num_layer):layer = _DenseLayer(num_input_features+i*growth_rate, growth_rate, bn_size, drop_rate)self.add_module("denselayer%d" % (i+1,), layer)

5.2.3 Transition层

class _Transition(nn.Sequential):def __init__(self, num_input_features, num_output_features):super(_Transition, self).__init__()self.add_module("norm", nn.BatchNorm2d(num_input_features))self.add_module("relu", nn.ReLU(inplace=True))self.add_module("conv", nn.Conv2d(num_input_features, num_output_features,kernel_size=1, stride=1, bias=False))self.add_module("pool", nn.AvgPool2d(2, stride=2)) 

5.2.4 DenseNet-BC

import torch.nn.functional as F#from collections import OrderedDict
import collectionstry:from collections import OrderedDict
except ImportError:OrderedDict = dictclass DenseNet(nn.Module):"DenseNet-BC model"def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16), num_init_features=64,bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=4):"""growth_rate:(int) number of filters used in DenseLayer, 'k' in the paperblock_config:(list of 4 ints) number of layers in each DenseBlocknum_init_features:(int) number of filters in the first Conv2dbn_size:(int) the factor using in the bottleneck layercompression_rate:(float) the compression rate used in Trasition Layerdrop_rate:(float) the drop rate after each DenseLayernum_classes:(int) number of classes for classification"""super(DenseNet, self).__init__()# first Conv2dself.features = nn.Sequential(OrderedDict([("conv0", nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)),("norm0", nn.BatchNorm2d(num_init_features)),("relu0", nn.ReLU(inplace=True)),("pool0", nn.MaxPool2d(3, stride=2, padding=1))]))# DenseBlocknum_features = num_init_featuresfor i,num_layers in enumerate(block_config):block = _DenseBlock(num_layers, num_features, bn_size, growth_rate, drop_rate)self.features.add_module("denseblock%d" % (i + 1), block)num_features += num_layers*growth_rateif i != len(block_config) - 1:transition = _Transition(num_features, int(num_features*compression_rate))self.features.add_module("transition%d" % (i+1), transition)num_features = int(num_features * compression_rate)# final bn+reluself.features.add_module("norm5", nn.BatchNorm2d(num_features))self.features.add_module("relu5", nn.ReLU(inplace=True))# classification layerself.classifier = nn.Linear(num_features, num_classes)# params initializationfor m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight)elif isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.bias, 0)nn.init.constant_(m.weight, 1)elif isinstance(m, nn.Linear):nn.init.constant_(m.bias, 0)def forward(self, x):features = self.features(x)out = F.avg_pool2d(features, 7, stride=1).view(features.size(0), -1)out = self.classifier(out)return out

5.2.5 DenseNet121

import redef densenet121(pretrained=False, **kwargs):# DenseNet121model = DenseNet(num_init_features=64, growth_rate=32, block_config=(6,12,24,16), ** kwargs)if pretrained:# '.' are no longer in module names, but pervious _DenseLayer# has keys 'norm.1','relu.1','conv.1','norm.2','relu.2','conv.2'.# They are also in the checkpoints in model_urls.This pattern is used# to find find such keys.pattern = re.compile(r'^(.*denselayer\d+\.(?:norm|relu\conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$')state_dir = model_zoo.load_url(model_urls['densenet121'])for key in list(state_dict.key()):res = pattern.match(key)if res:new_key = res.group(1) + res.group(2)state_dict[new_key] = state_dict[key]del state_dict[key]model.load_state_dict(state_dict)return modelmodel = densenet121().to(device)
model

    结果输出如下(由于结果太长,只展示最前面和最后面):

 (中间省略)

 5.2.6 查看模型详情

# 统计模型参数量以及其他指标
import torchsummary as summary
summary.summary(model, (3, 224, 224))

        结果输出如下(由于结果太长,只展示最前面和最后面):

 (中间省略)

 5.3 训练模型

5.3.1 编写训练函数

# 训练循环
def train(dataloader, model, loss_fn, optimizer):size = len(dataloader.dataset)  # 训练集的大小num_batches = len(dataloader)   # 批次数目, (size/batch_size,向上取整)train_loss, train_acc = 0, 0  # 初始化训练损失和正确率for X, y in dataloader:  # 获取图片及其标签X, y = X.to(device), y.to(device)# 计算预测误差pred = model(X)          # 网络输出loss = loss_fn(pred, y)  # 计算网络输出pred和真实值y之间的差距,y为真实值,计算二者差值即为损失# 反向传播optimizer.zero_grad()  # grad属性归零loss.backward()        # 反向传播optimizer.step()       # 每一步自动更新# 记录acc与losstrain_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()train_loss += loss.item()train_acc  /= sizetrain_loss /= num_batchesreturn train_acc, train_loss

5.3.2 编写测试函数

def test(dataloader, model, loss_fn):size = len(dataloader.dataset)  # 训练集的大小num_batches = len(dataloader)   # 批次数目, (size/batch_size,向上取整)test_loss, test_acc = 0, 0  # 初始化测试损失和正确率# 当不进行训练时,停止梯度更新,节省计算内存消耗# with torch.no_grad():for imgs, target in dataloader:  # 获取图片及其标签with torch.no_grad():imgs, target = imgs.to(device), target.to(device)# 计算误差tartget_pred = model(imgs)          # 网络输出loss = loss_fn(tartget_pred, target)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失# 记录acc与losstest_loss += loss.item()test_acc  += (tartget_pred.argmax(1) == target).type(torch.float).sum().item()test_acc  /= sizetest_loss /= num_batchesreturn test_acc, test_loss

5.3.3 正式训练

import copyoptimizer = torch.optim.Adam(model.parameters(), lr = 1e-4)
loss_fn = nn.CrossEntropyLoss() #创建损失函数epochs = 40train_loss = []
train_acc = []
test_loss = []
test_acc = []best_acc = 0 #设置一个最佳准确率,作为最佳模型的判别指标if hasattr(torch.cuda, 'empty_cache'):torch.cuda.empty_cache()for epoch in range(epochs):model.train()epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)#scheduler.step() #更新学习率(调用官方动态学习率接口时使用)model.eval()epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)#保存最佳模型到best_modelif epoch_test_acc > best_acc:best_acc = epoch_test_accbest_model = copy.deepcopy(model)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)#获取当前的学习率lr = optimizer.state_dict()['param_groups'][0]['lr']template = ('Epoch: {:2d}. Train_acc: {:.1f}%, Train_loss: {:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr: {:.2E}')print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss, lr))PATH = './J3_best_model.pth'
torch.save(model.state_dict(), PATH)print('Done')

        结果输出如下:

5.4 结果可视化

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率epochs_range = range(epochs)plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

        结果输出如下:

 6 使用Tensorflow实现DenseNet121

6.1 前期工作

6.1.1 开发环境

电脑系统:ubuntu16.04

编译器:Jupter Lab

语言环境:Python 3.7

深度学习环境:tensorflow

 6.1.2 设置GPU

    如果设备上支持GPU就使用GPU,否则注释掉这部分代码。

import tensorflow as tfgpus = tf.config.list_physical_devices("GPU")if gpus:tf.config.experimental.set_memory_growth(gpus[0], True) # 设置GPU显存用量按需使用tf.config.set_visible_devices([gpus[0]], "GPU")

6.1.2 导入数据

import matplotlib.pyplot as plt
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号import os, PIL, pathlib
import numpy as npfrom tensorflow import keras
from tensorflow.keras import layers,modelsdata_dir = "../data/bird_photos"
data_dir = pathlib.Path(data_dir)image_count = len(list(data_dir.glob('*/*')))
print("图片总数为:", image_count)

6.1.3 加载数据

batch_size = 8
img_height = 224
img_width = 224train_ds = tf.keras.preprocessing.image_dataset_from_directory(data_dir,validation_split=0.2,subset="training",seed=123,image_size=(img_height, img_width),batch_size=batch_size)val_ds = tf.keras.preprocessing.image_dataset_from_directory(data_dir,validation_split=0.2,subset="validation",seed=123,image_size=(img_height, img_width),batch_size=batch_size)class_Names = train_ds.class_names
print("class_Names:",class_Names)

    输出结果如下:

6.1.4 可视化数据

plt.figure(figsize=(10, 5)) # 图形的宽为10,高为5
plt.suptitle("imshow data")for images,labels in train_ds.take(1):for i in range(8):ax = plt.subplot(2, 4, i+1)plt.imshow(images[i].numpy().astype("uint8"))plt.title(class_Names[labels[i]])plt.axis("off")

    输出结果如下:

 6.1.5 检查数据

for image_batch, lables_batch in train_ds:print(image_batch.shape)print(lables_batch.shape)break

    输出结果如下:

 6.1.6 配置数据集

AUTOTUNE = tf.data.AUTOTUNEtrain_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

6.2 搭建DenseNet121

6.2.1 DenseNet121

import tensorflow as tf
import tensorflow.keras.layers as layers
from tensorflow.keras import regularizers
# from tensorflow.keras.models import Modelfrom tensorflow.keras.layers import Input,Activation,BatchNormalization,Flatten
from tensorflow.keras.layers import Dense,Conv2D,MaxPooling2D,ZeroPadding2D,AveragePooling2D
from tensorflow.keras.models import Modeldef regularized_padded_conv2d(*args, **kwargs):"""带标准化的卷积"""return layers.Conv2D(*args, **kwargs,padding='same', kernel_regularizer=regularizers.l2(5e-5), bias_regularizer=regularizers.l2(5e-5),kernel_initializer='glorot_normal')def DenseLayer(x, growth_rate, bn_size, drop_rate, layerName):new_features = layers.BatchNormalization(name=layerName+"_norm1")(x)new_features = layers.Activation('relu', name=layerName+"_relu1")(new_features)new_features = regularized_padded_conv2d(filters=bn_size*growth_rate, kernel_size=1, strides=1, use_bias=False, name=layerName+"_conv1")(new_features)new_features = layers.BatchNormalization(name=layerName+"_norm2")(new_features)new_features = layers.Activation('relu', name=layerName+"_relu2")(new_features)new_features = regularized_padded_conv2d(filters=growth_rate, kernel_size=3, strides=1, use_bias=False, name=layerName+"_conv2")(new_features)if drop_rate > 0:new_features = layers.Dropout(rate=drop_rate)(new_features)return layers.concatenate([x, new_features], axis=-1)def DenseBlock(x, num_layer, bn_size, growth_rate, drop_rate, blockName):for i in range(num_layer):x = DenseLayer(x, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate, layerName=blockName+'_'+str(i+1))return xdef Transition(x, num_output_features, blockName):x = layers.BatchNormalization(name=blockName+"_norm")(x)x =  layers.Activation('relu', name=blockName+"_relu")(x)x = regularized_padded_conv2d(filters=num_output_features, kernel_size=1, strides=1, use_bias=False, name=blockName+"_conv")(x)x = layers.AveragePooling2D(pool_size=2, strides=2, padding='same', name=blockName+'_pool')(x)return xdef densenet121(input_shape=[224,224,3], growth_rate=32, block_config=(6, 12, 24, 16), num_init_features=64,bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=4, classifier_activation='softmax'):img_input = Input(shape=input_shape)# first Conv2dx = regularized_padded_conv2d(filters=num_init_features, kernel_size=7, strides=2, use_bias=False, name="pre_conv")(img_input)x = layers.BatchNormalization(name="pre_norm")(x)x = layers.Activation('relu', name="pre_relu")(x)x = layers.MaxPool2D(pool_size=3, strides=2, padding='same')(x)# DenseBlocknum_features = num_init_featuresfor i,num_layer in enumerate(block_config):x = DenseBlock(x, num_layer=num_layer, bn_size=bn_size, growth_rate=growth_rate, drop_rate=drop_rate, blockName="DenseBlock_"+str(i+1))num_features += num_layer*growth_rateif i != len(block_config) - 1:num_features = int(num_features * compression_rate)x = Transition(x, num_output_features=num_features, blockName="TransBlock_"+ str(i+1))# final bn+relux = layers.BatchNormalization(name="norm5")(x)x = layers.Activation('relu', name="relu5")(x)x = layers.AveragePooling2D(pool_size=7, strides=1, name='pool5')(x) #GlobalAveragePooling2D# classification layerx = Dense(num_classes, activation=classifier_activation, name='classifier')(x)model = Model(img_input, x, name='densenet121')# # 加载预训练模型# model.load_weights("resnet50_weights_tf_dim_ordering_tf_kernels.h5")return model

6.2.2 查看模型详情

model = densenet121() 
model.summary()

    结果如图所示(由于内容较长,只截取前后部分内容):

 (中间部分省略)

6.3 训练模型

# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=1e-6)
model.compile(optimizer="adam",loss='sparse_categorical_crossentropy',metrics=['accuracy'])epochs = 40
history = model.fit(train_ds,validation_data=val_ds,epochs=epochs)

    结果如下图所示:

6.4 模型评估

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']loss = history.history['loss']
val_loss = history.history['val_loss']epochs_range = range(epochs)plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.suptitle("DenseNet test")plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation loss')
plt.legend(loc='upper right')
plt.title('Training and Validation loss')
plt.show()

    结果如下图所示:

     结合训练时的输出结果和模型评估图可以看出,训练的效果不理想,修改了learing_rate效果也不明显,后续继续尝试和分析。

相关文章:

经典CNN(三):DenseNet算法实战与解析

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊|接辅导、项目定制 1 前言 在计算机视觉领域,卷积神经网络(CNN)已经成为最主流的方法,比如GoogleNet,…...

学习笔记——压力测试案例,监控平台

测试案例 # 最简单的部署方式直接单机启动 nohup java -jar lesson-one-0.0.1-SNAPSHOT.jar > ./server.log 2>&1 &然后配置执行计划: 新建一个执行计划 配置请求路径 配置断言配置响应持续时间断言 然后配置一些查看结果的统计报表或者图形 然后我…...

sqlite 踩坑

内存数据库 强制SQLite数据库单纯的存在于内存中的常用方法是使用特殊文件名“ :memory: ” db QSqlDatabase::addDatabase("QSQLITE", "MEMORY"); db.setDatabaseName(":memory:"); 调用此接口完成后,不…...

【论文笔记】神经网络压缩调研

神经网络压缩调研 背景现有的深度模型压缩方法NetWork Prunning 网络剪枝设计结构化矩阵知识蒸馏权值共享Parameter Quantization(参数量化)量化和二进制化伪量化Architecture Design(Depth Separable Convolution)分解卷积 背景 …...

红外NEC通信协议

一、NEC简介 红外(Infrared,IR)遥控是一种无线、非接触控制技术,常用于遥控器、无线键盘、鼠标等设备之间的通信。IR协议的工作原理是,发送方通过红外线发送一个特定的编码,接收方通过识别该编码来执行相应的操作。 IR协议是指红外…...

数据分析DAY1

数据分析 引言 这一周:学习了python的numpy和matplotlib以及在飞桨paddle上面做了几个小项目 发现numpy和matplotlib里面有很多api,要全部记住是不可能的,也是不可能全部学完的,所以我们要知道并且熟悉一些常用的api&#xff0…...

算法通关村—迭代实现二叉树的前序,中序,后序遍历

1. 前序中序后序递归写法 前序 public void preorder(TreeNode root, List<Integer> res) {if (root null) {return;}res.add(root.val);preorder(root.left, res);preorder(root.right, res);}后序 public static void postOrderRecur(TreeNode head) {if (head nu…...

二叉搜索树(BST)的模拟实现

序言&#xff1a; 构造一棵二叉排序树的目的并不是为了排序&#xff0c;而是为了提高查找效率、插入和删除关键字的速度&#xff0c;同时二叉搜索树的这种非线性结构也有利于插入和删除的实现。 目录 &#xff08;一&#xff09;BST的定义 &#xff08;二&#xff09;二叉搜…...

【MFC】01.MFC框架-笔记

基本概念 MFC Microsoft Fundation class 微软基础类库 框架 基于Win32 SDK进行的封装 属性&#xff1a;缓解库关闭 属性->C/C/代码生成/运行库/MTD 属性->常规->MFC的使用&#xff1a;在静态库中使用MFC&#xff0c;默认是使用的共享DLL&#xff0c;运行时库 SD…...

基于ArcGIS污染物浓度及风险的时空分布

在GIS发展的早期&#xff0c;专业人士主要关注于数据编辑或者集中于应用工程&#xff0c;以及主要把精力花费在创建GIS数据库并构造地理信息和知识。慢慢的&#xff0c;GIS的专业人士开始在大量的GIS应用中使用这些知识信息库。用户应用功能全面的GIS工作站来编辑地理数据集&am…...

【项目开发计划制定工作经验之谈】

一、背景介绍 随着信息技术的发展&#xff0c;项目管理越来越受到企业和组织的重视。项目管理是一项旨在规划、组织、管理和控制项目的活动&#xff0c;以达到特定目标的过程。项目开发计划是项目管理的一个重要组成部分&#xff0c;它是指定项目目标、工作范围、进度、质量、…...

基于STM32的格力空调红外控制

基于STM32的格力空调红外控制 1.红外线简介 在光谱中波长自760nm至400um的电磁波称为红外线&#xff0c;它是一种不可见光。目前几乎所有的视频和音频设备都可以通过红外遥控的方式进行遥控&#xff0c;比如电视机、空调、影碟机等&#xff0c;都可以见到红外遥控的影子。这种技…...

rust中thiserror怎么使用呢?

thiserror 是一个Rust库&#xff0c;可以帮助你更方便地定义自己的错误类型。它提供了一个类似于 macro_rules 的宏&#xff0c;可以帮助你快速地定义错误类型&#xff0c;并为错误添加上下文信息。下面是一个使用 thiserror 的示例&#xff1a; 首先&#xff0c;在你的Rust项…...

ceph tier和bcache区别

作者&#xff1a;吴业亮 博客&#xff1a;wuyeliang.blog.csdn.net Ceph tier&#xff08;SSD POOL HDD POOL&#xff09;不推荐的原因&#xff1a; 数据在两个资源池之间迁移代价太大&#xff0c;存在粒度问题&#xff08;对象级别&#xff09;&#xff0c;且需要进行write…...

Idea 2023.2 maven 打包时提示 waring 问题解决

Version idea 2023.2 问题 使用 Maven 打包 &#xff0c;控制台输出 Waring 信息 [WARNING] [WARNING] Plugin validation issues were detected in 7 plugin(s) [WARNING] [WARNING] * org.apache.maven.plugins:maven-dependency-plugin:3.3.0 [WARNING] * org.apache.…...

docker数据持久化

在Docker中若要想实现容器数据的持久化&#xff08;所谓的数据持久化即数据不随着Container的结束而销毁&#xff09;&#xff0c;需要将数据从宿主机挂载到容器中。目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中。 &#xff08;1&#xff09;Volumes&#xff1a;…...

安全防护,保障企业图文档安全的有效方法

随着企业现在数据量的不断增加和数据泄露事件的频发&#xff0c;图文档的安全性成为了企业必须高度关注的问题。传统的纸质文件存储方式已不适应现代企业的需求&#xff0c;而在线图文档管理成为了更加安全可靠的数字化解决方案。那么在在线图文档管理中&#xff0c;如何采取有…...

Open3D (C++) 基于拟合平面的点云地面点提取

目录 一、算法原理1、原理概述2、参考文献二、代码实现三、结果展示1、原始点云2、提取结果四、相关链接本文由CSDN点云侠原创,原文链接。爬虫网站自重,把自己当个人,爬些不完整的误导别人有意思吗???? 一、算法原理...

【Linux】Kali Linux 渗透安全学习笔记(2) - OneForAll 简单应用

OneForAll &#xff08;以下简称“OFA”&#xff09;是一个非常好用的子域收集工具&#xff0c;可以通过一级域名找到旗下的所有层级域名&#xff0c;通过递归的方式我们很容易就能够知道此域名下的所有域名层级结构&#xff0c;对于进一步通过域名推测站点功能起到非常重要的作…...

DAY56:单调栈(二)下一个最大元素Ⅱ(环形数组处理思路)

文章目录 思路写法1完整版环形数组处理&#xff1a;i取模&#xff0c;遍历两遍写法2完整版&#xff08;环形数组推荐写法&#xff09;debug测试&#xff1a;逻辑运算符短路特性result数组在栈口取元素&#xff0c;是否会覆盖原有数值&#xff1f; 给定一个循环数组 nums &#…...

kafka简介

kafka是什么&#xff1f; Kafka最初采用Scala语言开发的一个多分区、多副本并且基于ZooKeeper协调的分布式消息系统。目前Kafka已经定位为一个分布式流式处理平台&#xff0c;它的特性有高吞吐、可持久化、可水平扩展、支持流处理。 Apache Kafka是一个分布式的发布-订阅消息系…...

Kafka-消费者组消费流程

消费者向kafka集群发送消费请求&#xff0c;消费者客户端默认每次从kafka集群拉取50M数据&#xff0c;放到缓冲队列中&#xff0c;消费者从缓冲队列中每次拉取500条数据进行消费。...

FFmepg视频解码

1 前言 上一篇文章<FFmpeg下载安装及Windows开发环境设置>介绍了FFmpeg的下载安装及环境配置&#xff0c;本文介绍最简单的FFmpeg视频解码示例。 2 视频解码过程 本文只讨论视频解码。 FFmpeg视频解码的过程比较简单&#xff0c;实际就4步&#xff1a; 打开媒体流获取…...

SpringCloud深入理解 | 生产者、消费者

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringCloud Spring Cloud是一组用于构建分布式系统和微服务架构的开源框架和工具集合。它是在Spring生态系统的基础上构建的&#xff0c;旨在简化开发人员构建分布式…...

web题型

0X01 命令执行 漏洞原理 没有对用户输入的内容进行一定过滤直接传给shell_exec、system一类函数执行 看一个具体例子 cmd1|cmd2:无论cmd1是否执行成功&#xff0c;cmd2将被执行 cmd1;cmd2:无论cmd1是否执行成功&#xff0c;cmd2将被执行 cmd1&cmd2:无论cmd1是否执行成…...

使用curl和postman调用Azure OpenAI Restful API

使用curl在cmd中调用时&#xff0c;注意&#xff1a;json大括号内的每一个双引号前需要加上\ curl https://xxxopenai.openai.azure.com/openai/deployments/Your_deployid/chat/completions?api-version2023-05-15 -H "Content-Type: application/json" -H "…...

草莓叶病害数据集

1.草莓数据集有两个文件夹 训练集 健康文件夹&#xff08;2819张&#xff09; 草莓叶焦病害&#xff08;3327张&#xff09; 数据集可以关注最后一行 import numpy as np import os import matplotlib.pyplot as plt import cv2import warnings warnings.filterwarnings(igno…...

安卓音视频多对多级联转发渲染

最近利用自己以前学习和用到的音视频知识和工程技能做了一个android的sdk,实现了本地流媒体ipc rtsp 拉流以及自带mip usb等camera audio节点产生的流媒体通过webrtc sfu的方式进行多对多级联发布共享,网状结构&#xff0c;p2p组网&#xff0c;支持实时渲染以及转推rtmp&#x…...

2023年电赛---运动目标控制与自动追踪系统(E题)OpenART mini的代码移植到OpenMV

前言 &#xff08;1&#xff09;已经有不少同学根据我上一篇博客完成了前三问&#xff0c;恭喜恭喜。有很多同学卡在了第四问。 &#xff08;2&#xff09;我说了OpenART mini的代码是可行的。但是他们不会移植到OpenMV上&#xff0c;再次我讲移植之后的代码贴出来。 &#xff…...

SAP CAP篇十二:AppRouter 深入研究

本文目录 本系列文章理解现有程序app文件夹中的package.json理解approuter.js 修改现有程序修改package.json新建index.js在Approuter中显示额外的逻辑 添加一些额外的Logger对应代码及branch 本系列文章 SAP CAP篇一: 快速创建一个Service&#xff0c;基于Java的实现 SAP CAP…...