憨批的语义分割重制版4——TF2 搭建自己的PSPNet语义分割平台

2023-11-11

学习前言

还是要弄一个TF2版本的,虽然和Keras没啥差别
在这里插入图片描述

什么是PSPNet模型

PSPNet模型最主要的特点是采用了PSP模块。

该模型提出的金字塔池化模块(Pyramid Pooling Module)能够聚合不同区域的上下文信息,从而提高获取全局信息的能力。实验表明这样的先验表示(即指代PSP这个结构)是有效的,在多个数据集上展现了优良的效果。

PSP结构的功能是将获取到的特征层划分成不同大小的网格,每个网格内部各自进行平均池化。实现聚合不同区域的上下文信息,从而提高获取全局信息的能力。

在PSPNet中,PSP结构典型情况下,会将输入进来的特征层划分成6x6,3x3,2x2,1x1的网格,对应了图片中的绿色、蓝色、橙色、红色的的输出
在这里插入图片描述
其中:
红色:将输入进来的特征层整个进行平均池化。
橙色:将输入进来的特征层划分为2×2个子区域,然后对每个子区域进行平均池化。
蓝色:将输入进来的特征层划分为3×3个子区域,然后对每个子区域进行平均池化。
绿色:将输入进来的特征层划分为6×6个子区域,然后对每个子区域进行平均池化。

代码下载

Github源码下载地址为:
https://github.com/bubbliiiing/pspnet-tf2

PSPNet实现思路

一、预测部分

1、主干网络介绍

在这里插入图片描述
PSPNet在论文中采用的是Resnet系列作为主干特征提取网络,本博客会给大家提供两个主干网络,分别是resnet50和mobilenetv2。

但是由于算力限制(我没有什么卡),为了方便博客的进行,本文以mobilenetv2为例,给大家进行解析。

关于mobilenetv2的介绍大家可以看我的另外一篇博客https://blog.csdn.net/weixin_44791964/article/details/102851214

MobileNet模型是Google针对手机等嵌入式设备提出的一种轻量级的深层神经网络,其使用的核心思想便是depthwise separable convolution。

MobileNetV2是MobileNet的升级版,它具有两个特征点:

1、Inverted residuals,在ResNet50里我们认识到一个结构,bottleneck design结构,在3x3网络结构前利用1x1卷积降维,在3x3网络结构后,利用1x1卷积升维,相比直接使用3x3网络卷积效果更好,参数更少,先进行压缩,再进行扩张。而在MobileNetV2网络部分,其采用Inverted residuals结构,在3x3网络结构前利用1x1卷积升维,在3x3网络结构后,利用1x1卷积降维,先进行扩张,再进行压缩
在这里插入图片描述

2、Linear bottlenecks,为了避免Relu对特征的破坏,在在3x3网络结构前利用1x1卷积升维,在3x3网络结构后,再利用1x1卷积降维后,不再进行Relu6层,直接进行残差网络的加法。
在这里插入图片描述
整体网络结构如下,该图是针对输入为(224,224,3)的图片而言的:(其中bottleneck进行的操作就是上述的创新操作)
在这里插入图片描述
需要注意的是,在PSPNet当中,一般不会5次下采样,可选的有3次下采样和4次下采样,本文使用的4次下采样这里所提到的下采样指的是不会进行五次长和宽的压缩,通常选用三次或者四次长和宽的压缩。

from tensorflow.keras.models import Model
from tensorflow.keras import layers
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import Lambda
from tensorflow.keras.layers import Activation
from tensorflow.keras.layers import Concatenate
from tensorflow.keras.layers import Add
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import DepthwiseConv2D
from tensorflow.keras.layers import ZeroPadding2D
from tensorflow.keras.layers import GlobalAveragePooling2D
from tensorflow.keras.activations import relu
import tensorflow as tf

def _make_divisible(v, divisor, min_value=None):
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v

def relu6(x):
    return relu(x, max_value=6)

def _inverted_res_block(inputs, expansion, stride, alpha, in_filters, filters, block_id, skip_connection, rate=1):
    pointwise_conv_filters = int(filters * alpha)
    pointwise_filters = _make_divisible(pointwise_conv_filters, 8)
    x = inputs
    prefix = 'expanded_conv_{}_'.format(block_id)
    if block_id:
        # Expand
        x = Conv2D(expansion * in_filters, kernel_size=1, padding='same',
                   use_bias=False, activation=None,
                   name=prefix + 'expand')(x)
        x = BatchNormalization(epsilon=1e-3, momentum=0.999,
                               name=prefix + 'expand_BN')(x)
        x = Activation(relu6, name=prefix + 'expand_relu')(x)
    else:
        prefix = 'expanded_conv_'
    # Depthwise
    x = DepthwiseConv2D(kernel_size=3, strides=stride, activation=None,
                        use_bias=False, padding='same', dilation_rate=(rate, rate),
                        name=prefix + 'depthwise')(x)
    x = BatchNormalization(epsilon=1e-3, momentum=0.999,
                           name=prefix + 'depthwise_BN')(x)

    x = Activation(relu6, name=prefix + 'depthwise_relu')(x)

    # Project
    x = Conv2D(pointwise_filters,
               kernel_size=1, padding='same', use_bias=False, activation=None,
               name=prefix + 'project')(x)
    x = BatchNormalization(epsilon=1e-3, momentum=0.999,
                           name=prefix + 'project_BN')(x)

    if skip_connection:
        return Add(name=prefix + 'add')([inputs, x])

    # if in_filters == pointwise_filters and stride == 1:
    #    return Add(name='res_connect_' + str(block_id))([inputs, x])

    return x

def get_mobilenet_encoder(inputs_size, downsample_factor=8):
    if downsample_factor == 16:
        block4_dilation = 1
        block5_dilation = 2
        block4_stride = 2
    elif downsample_factor == 8:
        block4_dilation = 2
        block5_dilation = 4
        block4_stride = 1
    else:
        raise ValueError('Unsupported factor - `{}`, Use 8 or 16.'.format(downsample_factor))
    
    # 473,473,3
    inputs = Input(shape=inputs_size)

    alpha=1.0
    first_block_filters = _make_divisible(32 * alpha, 8)
    # 473,473,3 -> 237,237,32
    x = Conv2D(first_block_filters,
                kernel_size=3,
                strides=(2, 2), padding='same',
                use_bias=False, name='Conv')(inputs)
    x = BatchNormalization(
        epsilon=1e-3, momentum=0.999, name='Conv_BN')(x)
    x = Activation(relu6, name='Conv_Relu6')(x)

    # 237,237,32 -> 237,237,16
    x = _inverted_res_block(x, in_filters=32, filters=16, alpha=alpha, stride=1,
                            expansion=1, block_id=0, skip_connection=False)

    #---------------------------------------------------------------#
    # 237,237,16 -> 119,119,24
    x = _inverted_res_block(x, in_filters=16, filters=24, alpha=alpha, stride=2,
                            expansion=6, block_id=1, skip_connection=False)
    x = _inverted_res_block(x, in_filters=24, filters=24, alpha=alpha, stride=1,
                            expansion=6, block_id=2, skip_connection=True)
                            
    #---------------------------------------------------------------#
    # 119,119,24 -> 60,60.32
    x = _inverted_res_block(x, in_filters=24, filters=32, alpha=alpha, stride=2,
                            expansion=6, block_id=3, skip_connection=False)
    x = _inverted_res_block(x, in_filters=32, filters=32, alpha=alpha, stride=1,
                            expansion=6, block_id=4, skip_connection=True)
    x = _inverted_res_block(x, in_filters=32, filters=32, alpha=alpha, stride=1,
                            expansion=6, block_id=5, skip_connection=True)

    #---------------------------------------------------------------#
    # 60,60,32 -> 30,30.64
    x = _inverted_res_block(x, in_filters=32, filters=64, alpha=alpha, stride=block4_stride,
                            expansion=6, block_id=6, skip_connection=False)
    x = _inverted_res_block(x, in_filters=64, filters=64, alpha=alpha, stride=1, rate=block4_dilation,
                            expansion=6, block_id=7, skip_connection=True)
    x = _inverted_res_block(x, in_filters=64, filters=64, alpha=alpha, stride=1, rate=block4_dilation,
                            expansion=6, block_id=8, skip_connection=True)
    x = _inverted_res_block(x, in_filters=64, filters=64, alpha=alpha, stride=1, rate=block4_dilation,
                            expansion=6, block_id=9, skip_connection=True)

    # 30,30.64 -> 30,30.96
    x = _inverted_res_block(x, in_filters=64, filters=96, alpha=alpha, stride=1, rate=block4_dilation,
                            expansion=6, block_id=10, skip_connection=False)
    x = _inverted_res_block(x, in_filters=96, filters=96, alpha=alpha, stride=1, rate=block4_dilation,
                            expansion=6, block_id=11, skip_connection=True)
    x = _inverted_res_block(x, in_filters=96, filters=96, alpha=alpha, stride=1, rate=block4_dilation,
                            expansion=6, block_id=12, skip_connection=True)
    # 辅助分支训练
    f4 = x

    #---------------------------------------------------------------#
    # 30,30.96 -> 30,30,160 -> 30,30,320
    x = _inverted_res_block(x, in_filters=96, filters=160, alpha=alpha, stride=1, rate=block4_dilation,  # 1!
                            expansion=6, block_id=13, skip_connection=False)
    x = _inverted_res_block(x, in_filters=160, filters=160, alpha=alpha, stride=1, rate=block5_dilation,
                            expansion=6, block_id=14, skip_connection=True)
    x = _inverted_res_block(x, in_filters=160, filters=160, alpha=alpha, stride=1, rate=block5_dilation,
                            expansion=6, block_id=15, skip_connection=True)

    x = _inverted_res_block(x, in_filters=160, filters=320, alpha=alpha, stride=1, rate=block5_dilation,
                            expansion=6, block_id=16, skip_connection=False)
    f5 = x
    return inputs, f4, f5

2、加强特征提取结构

在这里插入图片描述
PSPNet所使用的加强特征提取结构是PSP模块。

PSP结构的做法是将获取到的特征层划分成不同大小的区域,每个区域内部各自进行平均池化。实现聚合不同区域的上下文信息,从而提高获取全局信息的能力。

在PSPNet中,PSP结构典型情况下,会将输入进来的特征层划分成6x6,3x3,2x2,1x1的区域,然后每个区域内部各自进行平均池化。

假设PSP结构输入进来的特征层为30x30x320,此时这个特征层的高和宽均为30,如果我们要将这个特征层划分成6x6的区域,只需要使得平均池化的步长stride=30/6=5和kernel_size=30/6=5就行了,此时的平均池化相当于将特征层划分成6x6的区域,每个区域内部各自进行平均池化。

当PSP结构输入进来的特征层为30x30x320时,PSP结构的具体构成如下。
在这里插入图片描述

def pool_block(feats, pool_factor, out_channel):
	h = K.int_shape(feats)[1]
	w = K.int_shape(feats)[2]
	# strides = [30,30],[15,15],[10,10],[5,5]
	# poolsize 30/6=5 30/3=10 30/2=15 30/1=30
	pool_size = strides = [int(np.round(float(h)/pool_factor)),int(np.round(float(w)/pool_factor))]
	# 进行不同程度的平均
	x = AveragePooling2D(pool_size , data_format=IMAGE_ORDERING , strides=strides, padding='same')(feats)
	# 进行卷积
	x = Conv2D(out_channel//4, (1 ,1), data_format=IMAGE_ORDERING, padding='same', use_bias=False)(x)
	x = BatchNormalization()(x)
	x = Activation('relu' )(x)
	x = Lambda(lambda x: tf.compat.v1.image.resize_images(x, (K.int_shape(feats)[1], K.int_shape(feats)[2]), align_corners=True))(x)
	return x

3、利用特征获得预测结果

利用1、2步,我们可以获取输入进来的图片的特征,此时,我们需要利用特征获得预测结果。

利用特征获得预测结果的过程可以分为3步:
1、利用一个3x3卷积对特征进行整合。
2、利用一个1x1卷积进行通道调整,调整成Num_Classes。
3、利用resize进行上采样使得最终输出层,宽高和输入图片一样。

在这里插入图片描述

from tensorflow.keras.models import *
from tensorflow.keras.layers import *
from nets.mobilenetv2 import get_mobilenet_encoder
from nets.resnet50 import get_resnet50_encoder
import tensorflow.keras.backend as K
import tensorflow as tf
import numpy as np

IMAGE_ORDERING = 'channels_last'
MERGE_AXIS = -1

def pool_block(feats, pool_factor, out_channel):
	h = K.int_shape(feats)[1]
	w = K.int_shape(feats)[2]
	# strides = [30,30],[15,15],[10,10],[5,5]
	# poolsize 30/6=5 30/3=10 30/2=15 30/1=30
	pool_size = strides = [int(np.round(float(h)/pool_factor)),int(np.round(float(w)/pool_factor))]
	# 进行不同程度的平均
	x = AveragePooling2D(pool_size , data_format=IMAGE_ORDERING , strides=strides, padding='same')(feats)
	# 进行卷积
	x = Conv2D(out_channel//4, (1 ,1), data_format=IMAGE_ORDERING, padding='same', use_bias=False)(x)
	x = BatchNormalization()(x)
	x = Activation('relu' )(x)
	x = Lambda(lambda x: tf.compat.v1.image.resize_images(x, (K.int_shape(feats)[1], K.int_shape(feats)[2]), align_corners=True))(x)
	return x

def pspnet(n_classes, inputs_size, downsample_factor=8, backbone='mobilenet', aux_branch=True):
	if backbone == "mobilenet":
		img_input, f4, o = get_mobilenet_encoder(inputs_size, downsample_factor=downsample_factor)
		out_channel = 320
	elif backbone == "resnet50":
		img_input, f4, o = get_resnet50_encoder(inputs_size, downsample_factor=downsample_factor)
		out_channel = 2048
	else:
		raise ValueError('Unsupported backbone - `{}`, Use mobilenet, resnet50.'.format(backbone))
	#-------------------------------------#
	#	PSP模块
	#	分区域进行池化
	#-------------------------------------#
	pool_factors = [1,2,3,6]
	pool_outs = [o]

	for p in pool_factors:
		pooled = pool_block(o, p, out_channel)
		pool_outs.append(pooled)
	
	# 连接
	# 60x60xout_channel*2
	o = Concatenate(axis=MERGE_AXIS)(pool_outs)

	#-------------------------------------#
	#	利用特征获得预测结果
	#-------------------------------------#
	# 卷积
	# 60x60xout_channel//4
	o = Conv2D(out_channel//4, (3,3), data_format=IMAGE_ORDERING, padding='same', use_bias=False)(o)
	o = BatchNormalization()(o)
	o = Activation('relu')(o)
	# 正则化,防止过拟合
	o = Dropout(0.1)(o)

	# 60x60x21
	o = Conv2D(n_classes,(1,1),data_format=IMAGE_ORDERING, padding='same')(o)
	# [473,473,nclasses]
	o = Lambda(lambda x: tf.compat.v1.image.resize_images(x, (inputs_size[1], inputs_size[0]), align_corners=True))(o)
	# 获得每一个像素点属于每一个类的概率了
	o = Activation("softmax", name="main")(o)
	
	if aux_branch:
		f4 = Conv2D(out_channel//8, (3,3), data_format=IMAGE_ORDERING, padding='same', use_bias=False)(f4)
		f4 = BatchNormalization()(f4)
		f4 = Activation('relu')(f4)
		# 防止过拟合
		f4 = Dropout(0.1)(f4)

		# 60x60x21
		f4 = Conv2D(n_classes,(1,1),data_format=IMAGE_ORDERING, padding='same')(f4)
		# [473,473,nclasses]
		f4 = Lambda(lambda x: tf.compat.v1.image.resize_images(x, (inputs_size[1], inputs_size[0]), align_corners=True))(f4)
		# 获得每一个像素点属于每一个类的概率了
		f4 = Activation("softmax", name="aux")(f4)
		model = Model(img_input,[f4,o])
		return model
	else:
		model = Model(img_input,[o])
		return model

二、训练部分

1、训练文件详解

我们使用的训练文件采用VOC的格式。
语义分割模型训练的文件分为两部分。
第一部分是原图,像这样:
在这里插入图片描述
第二部分标签,像这样:
在这里插入图片描述
原图就是普通的RGB图像,标签就是灰度图或者8位彩色图。

原图的shape为[height, width, 3],标签的shape就是[height, width],对于标签而言,每个像素点的内容是一个数字,比如0、1、2、3、4、5……,代表这个像素点所属的类别。

语义分割的工作就是对原始的图片的每一个像素点进行分类,所以通过预测结果中每个像素点属于每个类别的概率与标签对比,可以对网络进行训练。

2、LOSS解析

本文所使用的LOSS由两部分组成:
1、Cross Entropy Loss。
2、Dice Loss。

Cross Entropy Loss就是普通的交叉熵损失,当语义分割平台利用Softmax对像素点进行分类的时候,进行使用。

Dice loss将语义分割的评价指标作为Loss,Dice系数是一种集合相似度度量函数,通常用于计算两个样本的相似度,取值范围在[0,1]。

计算公式如下:
在这里插入图片描述
就是预测结果和真实结果的交乘上2,除上预测结果加上真实结果。其值在0-1之间。越大表示预测结果和真实结果重合度越大。所以Dice系数是越大越好。

如果作为LOSS的话是越小越好,所以使得Dice loss = 1 - Dice,就可以将Loss作为语义分割的损失了。
实现代码如下:

def dice_loss_with_CE(beta=1, smooth = 1e-5):
    def _dice_loss_with_CE(y_true, y_pred):
        y_pred = K.clip(y_pred, K.epsilon(), 1.0 - K.epsilon())

        CE_loss = - y_true[...,:-1] * K.log(y_pred)
        CE_loss = K.mean(K.sum(CE_loss, axis = -1))

        tp = K.sum(y_true[...,:-1] * y_pred, axis=[0,1,2])
        fp = K.sum(y_pred         , axis=[0,1,2]) - tp
        fn = K.sum(y_true[...,:-1], axis=[0,1,2]) - tp

        score = ((1 + beta ** 2) * tp + smooth) / ((1 + beta ** 2) * tp + beta ** 2 * fn + fp + smooth)
        score = tf.reduce_mean(score)
        dice_loss = 1 - score
        # dice_loss = tf.Print(dice_loss, [dice_loss, CE_loss])
        return CE_loss + dice_loss
    return _dice_loss_with_CE

训练自己的PSPNet模型

首先前往Github下载对应的仓库,下载完后利用解压软件解压,之后用编程软件打开文件夹。
注意打开的根目录必须正确,否则相对目录不正确的情况下,代码将无法运行。

一定要注意打开后的根目录是文件存放的目录。
在这里插入图片描述

一、数据集的准备

本文使用VOC格式进行训练,训练前需要自己制作好数据集,如果没有自己的数据集,可以通过Github连接下载VOC12+07的数据集尝试下。
训练前将图片文件放在VOCdevkit文件夹下的VOC2007文件夹下的JPEGImages中。
训练前将标签文件放在VOCdevkit文件夹下的VOC2007文件夹下的SegmentationClass中。

在这里插入图片描述

二、数据集的处理

在完成数据集的摆放之后,我们需要对数据集进行下一步的处理,目的是获得训练用的train.txt以及val.txt,需要用到根目录下的voc_annotation.py。

如果下载的是我上传的voc数据集,那么就不需要运行根目录下的voc_annotation.py。
如果是自己制作的数据集,那么需要运行根目录下的voc_annotation.py,从而生成train.txt和val.txt。
在这里插入图片描述

三、开始网络训练

通过voc_annotation.py我们已经生成了train.txt以及val.txt,此时我们可以开始训练了。训练的参数较多,大家可以在下载库后仔细看注释,其中最重要的部分依然是train.py里的num_classes。

num_classes用于指向检测类别的个数+1!训练自己的数据集必须要修改!

除此之外在train.py文件夹下面,选择自己要使用的主干模型backbone、预训练权重model_path和下采样因子downsample_factor。预训练模型需要和主干模型相对应。下采样因子可以在8和16中选择。
在这里插入图片描述
之后就可以开始训练了。

四、训练结果预测

训练结果预测需要用到两个文件,分别是pspnet.py和predict.py。
我们首先需要去pspnet.py里面修改model_path以及num_classes,这两个参数必须要修改。

model_path指向训练好的权值文件,在logs文件夹里。
num_classes指向检测类别的个数+1。

在这里插入图片描述
完成修改后就可以运行predict.py进行检测了。运行后输入图片路径即可检测。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

憨批的语义分割重制版4——TF2 搭建自己的PSPNet语义分割平台 的相关文章

  • SpringMvc进阶

    SpringMvc进阶 SpringMVC引言 一 常用注解 二 参数传递 三 返回值 SpringMVC引言 在Web应用程序开发中 Spring MVC是一种常用的框架 它基于MVC Model View Controller 模式 提

随机推荐

  • Java中的抽象类和接口

    目录 一 什么是抽象类 抽象类在实现多态中的意义 二 接口是什么 通过接口实现多态 三 抽象类和接口的区别 各位铁汁们大家好呀 今天让我们继续学习java 看看java中的抽象类和接口到底是什么 一 什么是抽象类 我们之前学过什么是类 那么
  • Jackson常用方法以及jacksonUtil工具类

    前言 项目中我们通常使用ajax返回json数据格式的形式进行前后端数据交互 所以会用到java数据json数据互相转化 通常我们的做法是在项目中创建一个工具类进行转化处理 如下 我的demo包含了项目中常用的jacksonUtil类 以及
  • JDBC连接MySQL出现误com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database

    JDBC连接MySQL出现误com mysql jdbc exceptions jdbc4 MySQLSyntaxErrorException Unknown database test 问题原因 在用连接池进行MySQL连接时出错找不到改
  • UE_sqlite使用

    UE sqlite使用 PS 参考文档 https blog csdn net object article details 102785739 sqlite官方 https www sqlite org download html
  • 结构体中成员的引用

    结构体如下 struct student int num char name 20 char sex float score 1 结构体的初始化 struct student aa 1001 zhang M 80 5 2 引用整个结构体 s
  • Qt中的Q_D宏和d指针

    1 ZTS7QObject 一 Q D的在文件中的提法 Q D的设置意在方便地获取私有类指针 文件为qglobal h 下面的 是宏定义的连字符 假设类名是A 那么A Private翻译过来就是APrivate 1 define Q D C
  • MySQL服务无法启动。服务没有报告任何错误。问题解决!

    项目场景 提示 MySQL服务无法启动 服务没有报告任何错误 问题解决 问题描述 在启动MySQL是遇到问题 提示mysql服务无法启动 服务没有报告任何错误 原因分析 我这里的问题主要是因为你的data文件夹中有影响启动的文件 注意 清空
  • MyBatis-Plus&Druid

    MyBatis Plus Druid MyBatis Plus 核心功能 Spring Boot 集成 Druid 数据源 MyBatis Plus MyBatis Plus 简称 MP 是一个MyBatis 的增强工具 在MyBatis
  • 中兴e8820刷openwrt_中兴E8820V2(电信天翼宽带类似新路由3歌华链)-拆机及OpenWrt固件...

    本帖最后由 yumeimm 于 2020 12 20 10 53 编辑 2020 12更新 增加Openwrt v19 07 5固件 2020 10更新 添加openwrt v19 07 4固件 2020 05更新 添加openwrt v1
  • 什么是NAT?

    NAT是一种地址转换技术 它可以将IP数据报文头中的IP地址转换为另一个IP地址 并通过转换端口号达到地址重用的目的 NAT作为一种缓解IPv4公网地址枯竭的过渡技术 由于实现简单 得到了广泛应用 NAT解决了什么问题 随着网络应用的增多
  • 设计模式--装饰器模式

    装饰器模式 属于结构型模式基本原理 创建一个装饰器用来对一个现有对象添加新功能 不改变对象结构主要流程 1 根据对象创建一个修饰类 该修饰类要保持方法签名完整 2 在修饰类中根据需求添加新的功能 3 使用时将对象或对象的引用传入修饰类中 注
  • rabbitmq简介

    开发十年 就只剩下这套Java开发体系了 gt gt gt 1 AMQP AMQP协议是一个高级抽象层消息通信协议 RabbitMQ是AMQP协议的实现 它主要包括以下组件 1 1 Server broker 接受客户端连接 实现AMQP消
  • DELETE与DROP 在数据库中的使用方法和区别

    DML data manipulation language 数据操纵语言 就是我们最经常用到的 SELECT UPDATE INSERT DELETE 主要用来对数据库的数据进行一些操作 DML 语句都是显式提交 执行完之后 处理的数据
  • tsconfig之moduleResolution详解

    作用 moduleResolution 模块解析策略 是指编译器在查找导入模块内容时所遵循的流程 模块解析分析 如下代码 编辑器会采用模块解析策略 Node 和 Classic 去查找 moduleB 在哪里 如果最后找不到 编译器不能解析
  • RESTful API接口

    RESTful规范 Restful API是目前比较成熟的一套互联网应用程序的API设计理念 Rest是一组架构约束条件和原则 如何Rest约束条件和原则的架构 我们就称为Restful架构 Restful架构具有结构清晰 符合标准 易于理
  • JAVA报错:Variable 'vv' is accessed from within inner class, needs to be declared final

    内部类中使用但未声明的任何局部变量必须在内部类的正文之前明确分配
  • struts2中validator配置文件验证不起作用的问题解决办法、根源

    在采用struts的xml配置方式校验数据时 发现怎么也不起作用 无法按照正常流程 走到input指向的页面 一 问题的解决 很多博客说明了自己查找的方式 最后都指明了是因为配置文件格式不正确的原因 出现这种问题的时候 应该从下面4个部分考
  • SpringCloud Alibaba 教程

    SpringCloud Alibaba GitHub官方地址 https github com alibaba spring cloud alibaba blob master README zh md SpringCloud Alibab
  • 大学生团体天梯赛(第九届)

    题目地址 天梯赛 include
  • 憨批的语义分割重制版4——TF2 搭建自己的PSPNet语义分割平台

    憨批的语义分割重制版4 TF2 搭建自己的PSPNet语义分割平台 学习前言 什么是PSPNet模型 代码下载 PSPNet实现思路 一 预测部分 1 主干网络介绍 2 加强特征提取结构 3 利用特征获得预测结果 二 训练部分 1 训练文件