图像降噪网络:KBNet 论文笔记

news2024/11/25 4:46:52

0 前言

Zhang Y, Li D, Shi X, et al. KBNet: Kernel Basis Network for Image Restoration[J]. arXiv preprint arXiv:2303.02881, 2023.
https://arxiv.org/abs/2303.02881

论文主要提出了 Kernel Basis Attention Module 注意力模块,称为 KBA 模块。该模块可以轻松嵌入到现有的网络架构如 UNet 当中,且相比于 Transformer 等注意力机制具有更低的复杂度,但能够却在包括降噪、去模糊等多个底层图像重建任务中取得了 SOTA 的成绩。关于真实噪声图像降噪的实验结果对比如下:

1 相关工作与动机

虽然基于 Scaled Dot-Product Attention 注意力机制的 Transformer 模型在自然语言处理领域取得了非常优异的成绩,但在图像处理特别是图像质量重建领域却存在一些水土不服的问题。相比于抽象而简短的文字,图像所包含的信息是极其稀疏的,一张图片动辄包含数以万计的像素,因此简单地应用 Transformer 模型需要极大的算力代价。为此,一些基于局部窗口的 Transformer 模型相继被提出。例如在 Wang 等人提出的 Uformer (arxiv:2106.03106) 模型中,作者提出了基于不重叠的固定尺寸窗口的 Transformer 模块,极大地降低了注意力机制所需的复杂度;又因为该 Transformer 模块所用窗口尺寸是固定的,而图像处理领域通常基于 UNet 架构,随着网络深度的增加,特征尺寸会因为下采样而不断地缩小,最终的窗口即有可能包含整个特征,从而实现全局的注意力机制,弥补不重叠窗口所带来的局部性问题。

然而,Transformer 模型应用于图像重建任务的问题并不局限于上述的计算复杂度。在图像中,像素的局部相似性远高于相邻的文字,相邻的像素往往具有几乎相同的像素值;同时,图像在非局部区域也存在着大量的自相似性,即同一种纹理结构,往往可能重复地出现在图像的不同位置。对于 Scaled Dot-Product Attention 注意力机制,其结构如下图所示:

图 1 Scaled Dot-Product Attention Module

其中 Q , K , V ∈ R N × C {\mathbf{Q}},{\mathbf{K}},{\mathbf{V}} \in {\mathbb{R}^{N \times C}} Q,K,VRN×C,三者在图像处理中通常是相同的, N N N 为特征的像素个数,即 H × W H \times W H×W C C C 即为特征的通道数。那么注意力机制可表示为

Attention ( Q , K , V ) = softmax ( Q K T s c a l e ) V . {\text{Attention}}\left( {{\mathbf{Q}},{\mathbf{K}},{\mathbf{V}}} \right) = {\text{softmax}}\left( {\frac{{{\mathbf{Q}}{{\mathbf{K}}^T}}}{{scale}}} \right){\mathbf{V}}. Attention(Q,K,V)=softmax(scaleQKT)V.

在这里, Q K T {\mathbf{Q}}{{\mathbf{K}}^T} QKT 本质上就是计算特征中每个像素与其他像素的相关性,并通过 softmax 操作转换为与其他像素的叠加权重,最终通过乘以 V {\mathbf{V}} V 获得其他像素的加权叠加结果,并以此作为新的特征像素。相比于卷积操作,这种注意力机制虽然获得了更大的特征可视域与像素自适应性,但缺点也是明显的。首先,它忽视了图像像素的局部相似性,将太多的计算花费在了远离当前像素的区域,而这并不能保证相匹配的收益;再者,它只是在单个像素上计算相关性,这很难捕捉图像中广泛存在的自相似结构,而更容易受到噪声的影响。

以上的问题,恰好是卷积操作所擅长解决的。因为卷积核通常很小,所以卷积只会局限于局部的像素叠加,不会造成太大的算力浪费;同时,卷积具有平移不变性,并可通过卷积核来学习与识别图像中存在的各种纹理结构,如条纹、分叉等等。我们把这种局部性与平移不变性称为卷积操作的归纳偏置 (Inductive Biases),而这种归纳偏置又与图像的特性非常切合,这也是为什么 CNN 能够在图像处理领域大放异彩的重要原因。然而,我们也不能否认,CNN 具有较差的像素自适应性,即我们总是使用相同的卷积核来处理不同的像素。由于图像结构可能存在着任意的旋转与透视形变等等,这是卷积所不擅长处理的,单纯通过增加卷积核的个数并不能很好地解决这些问题。

基于以上的讨论,论文尝试综合卷积的归纳偏置与注意力机制的像素自适应性,提出了 Kernel Basis Attention Module 注意力模块。

2 KBA Module

KBA 模块的结构如图 2 所示。这里会结合作者所开源代码 (https://github.com/zhangyi-3/KBNet) 进行各部分的解析。

图 2 Kernel Basis Attention Module

对于输入特征 X ∈ R H × W × C {\mathbf{X}} \in {\mathbb{R}^{H \times W \times C}} XRH×W×C,我们希望学习 N N N 个卷积核 W = { W 1 , W 2 , . . . , W N } {\mathbf{W}} = \left\{ {{{\mathbf{W}}_1},{{\mathbf{W}}_2},...,{{\mathbf{W}}_N}} \right\} W={W1,W2,...,WN},其中 W i ∈ R K × K × C × C {{\mathbf{W}}_i} \in {\mathbb{R}^{K \times K \times C \times C}} WiRK×K×C×C,也就是普通 CNN 网络中的卷积核,通常 K = 3 K=3 K=3。实际上,为了降低参数的数量,这里的卷积通常使用 GroupConv2D。记 Group 的个数为 G G G,每个 Group 的通道数为 G c Gc Gc,其中 C = G × G c C=G \times Gc C=G×Gc,那么有 W i ∈ R K × K × G c × G c × G {{\mathbf{W}}_i} \in {\mathbb{R}^{K \times K \times Gc \times Gc \times G}} WiRK×K×Gc×Gc×G。论文中取 G c = 4 Gc=4 Gc=4。除了卷积核以外,在具体的实现中,通常还会包含卷积后的偏置量的学习。关于卷积核与偏置的代码定义如下:

nset=32, k=3, gc=4
g = c // gc
w = nn.Parameter(torch.zeros(1, nset, c * c // g * k ** 2))
b = nn.Parameter(torch.zeros(1, nset, c))

注意这 N N N 个卷积核是由所有像素共享的,但是我们并不直接使用这些卷积核进行 N N N 次卷积运算。为了实现各个像素的自适应性,我们基于输入特征 X ∈ R H × W × C {\mathbf{X}} \in {\mathbb{R}^{H \times W \times C}} XRH×W×C,学习到每个像素关于这 N N N 个卷积核的融合权重,称为 Fusion Coefficient Map,以 F ∈ R H × W × N {\mathbf{F}} \in {\mathbb{R}^{H \times W \times N}} FRH×W×N 记之。那么,每个像素实际所用的卷积核为前述 N N N 个卷积核的加权叠加,即

M i , j = ∑ n = 1 N F i , j , n W n {{\mathbf{M}}_{i,j}} = \sum\limits_{n = 1}^N {{F_{i,j,n}}{{\mathbf{W}}_n}} Mi,j=n=1NFi,j,nWn

基于这种方法,我们可以综合不同卷积核的特性。例如,一个斜线方向的纹理可以近似分解为若干个水平与垂直方向纹理的组合。虽然我们可以通过增加卷积核的数量来捕捉不同方向的斜线纹理,但由于其角度是任意,我们并不能定义无限数量的卷积核。而通过卷积核的加权融合方法,我们只需学习关于少量水平与垂直纹理的卷积核的融合权重,就能获得适用于任意角度斜线纹理的卷积核。对于其他情况同理。通过这种自适应卷积核融合的方法,我们弥补了普通卷积运算对于旋转与透视等变换敏感的缺陷,强化了卷积对于图像结构自相似性的归纳偏置能力。由于每个像素最终只会进行一次卷积,所以其计算复杂度并不会因为共享卷积核数量 N N N 的增加而增长太多。

关于融合权重 F {\mathbf{F}} F 的计算,论文并不使用 Transformer 等复杂的模型,而仅使用比较简单的卷积运算。这部分的代码如下。作者使用了两个分支,一个大致为 3x3 的可分离卷积,另一个为 1x1 的通道间变换,最终通过一个可学习的叠加权重 attgamma 进行融合。尽管 Transformer 模型的注意力机制通过 softmax 将 Q K T {\mathbf{Q}}{{\mathbf{K}}^T} QKT 进行归一化,作者发现对于 F {\mathbf{F}} F 的归一化是不必要的,因为 softmax 会把过多的权重分配到更大的数值上,从而降低了卷积核融合的效果。因此, F {\mathbf{F}} F 的计算量是非常小的。因为 F {\mathbf{F}} F 的计算只涉及到 3x3 的可视域,所以 F {\mathbf{F}} F 更多关注的是当前像素局部邻域的信息,关于全局的自相似性则是通过共享的 N N N 个卷积核 W {\mathbf{W}} W 来实现的。而随着 UNet 深度的增加,特征尺寸越来越小, F {\mathbf{F}} F 对于局部信息综合的能力也会越来越强。基于这种对共享卷积核进行自适应融合的方法,我们一方面保留了卷积运算的归纳偏置,又实现了各个像素的自适应性。

''' simplified GLU function
'''
class SimpleGate(nn.Module):
    def forward(self, x):
        x1, x2 = x.chunk(2, dim=1)
        return x1 * x2

''' Fusion Feature Map1
spatial and channel attention
input: CxHxW -> F1: NxHxW
'''
interc = min(c, 32)     # c must be divisible by 32 if c > 32
self.conv2 = nn.Sequential(
    nn.Conv2d(in_channels=c, out_channels=interc, kernel_size=3, padding=1, stride=1, groups=interc, bias=True),
    SimpleGate(),   # this would half the channel
    nn.Conv2d(interc // 2, self.nset, 1, padding=0, stride=1),
)

''' Fusion Feature Map2
1x1 conv, channel attention
input: CxHxW -> F2: NxHxW
'''
self.conv211 = nn.Conv2d(in_channels=c, out_channels=self.nset, kernel_size=1)

'''F = F1 * attgamma + F2
'''
self.attgamma = nn.Parameter(torch.zeros((1, self.nset, 1, 1)) + 1e-2, requires_grad=True)
att = self.conv2(x) * self.attgamma + self.conv211(x)

当为输入特征 X ∈ R H × W × C {\mathbf{X}} \in {\mathbb{R}^{H \times W \times C}} XRH×W×C 每个像素都获得一个自适应的卷积核 M i , j ∈ R K × K × G c × G c × G {{\mathbf{M}}_{i,j}} \in {\mathbb{R}^{K \times K \times Gc \times Gc \times G}} Mi,jRK×K×Gc×Gc×G 后,我们就可以对输入特征进行自适应的卷积。为了更好地捕捉局部信息,作者首先对输入特征基于简单的卷积运算进行增强,获得 Enhanced Feature Map,记为 X e ∈ R H × W × C {\mathbf{X}}_e \in {\mathbb{R}^{H \times W \times C}} XeRH×W×C,然后在 X e {\mathbf{X}}_e Xe 上进行自适应的卷积运算。这部分代码如下:

''' Enhanced Feature Map
1. 1x1 convolution
2. 3x3 grouping convolution, groups=c, i.e. depthwise
input: CxHxW -> Xe: CxHxW
'''
self.conv1 = nn.Conv2d(in_channels=c, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True)
self.conv21 = nn.Conv2d(in_channels=c, out_channels=c, kernel_size=3, padding=1, stride=1, groups=c, bias=True)
x = self.conv21(self.conv1(x))

''' F: (HW) x N
bias: [(HW) x N] .dot (N x C) -> (HW) x C
attk: [(HW) x N] .dot (N x C x [GC x K x K]) -> (HW) x C x [GC x K x K]
'''
att  = att.reshape(B, nset, H * W).transpose(-2, -1)
bias = att @ selfb  
attk = att @ selfw

''' unfold the group conv2D to matmul
'''
uf = torch.nn.functional.unfold(x, kernel_size=selfk, padding=selfk // 2)
# for unfold att / less memory cost
uf = uf.reshape(B, selfg, selfc // selfg * KK, H * W).permute(0, 3, 1, 2)
attk = attk.reshape(B, H * W, selfg, selfc // selfg, selfc // selfg * KK)
# uf: (HW) x G x (GC x K x K)
# attk: (HW) x G x GC x (GC x K X K)
x = attk @ uf.unsqueeze(-1)
x = x.squeeze(-1).reshape(B, H * W, selfc) + bias
x = x.transpose(-1, -2).reshape(B, selfc, H, W)

3 MFF Block

为了综合输入特征多方面的信息,作者在 KBA 模块的基础上拓展出了 Multi-axis Feature Fusion Block,其结构如下所示。

图 3 Multi-axis Feature Fusion Block

类似于 Transformer 模型,MFF 首先对输入特征进行 LayerNorm。LN 层在 Transformer 等 NLP 模型中主要是为了解决 Batch 太小,不方便进行 BatchNorm 标准化的问题。由于 BN 层在样本间进行标准化,引入其他的样本可能会造成当前样本纹理等信息发生剧烈变化,不利于后续样本的重建,所以在底层图像重建如降噪等任务中通常都被移除了。而 LN 只在各个样本的特征通道上独立进行标准化,不会对特征纹理造成太大的影响,所以 LN 作为一种标准化方法重新被图像重建任务所采纳,以保证模型优化的稳定性。关于 LN 层的定义如下:

self.register_parameter('weight', nn.Parameter(torch.ones(channels), requires_grad=requires_grad))
self.register_parameter('bias', nn.Parameter(torch.zeros(channels), requires_grad=requires_grad))
self.eps = 1e-6
def LayNorm(x, weight, bias, eps):
    N, C, H, W = x.size()
    mu = x.mean(1, keepdim=True)
    var = (x - mu).pow(2).mean(1, keepdim=True)
    y = (x - mu) / (var + eps).sqrt()
    y = weight.view(1, C, 1, 1) * y + bias.view(1, C, 1, 1)
    return y

输入特征 X {\mathbf{X}} X 经过 LayerNorm 标准化后,MFF 使用了三个分支来对其进行信息抽取与综合,包括 Channel Attention,Depthwise Convolution,以及 KBA 模块。Channel Attention 用于融合特征通道间的信息,主要通过一个 Global Average Pooling 与 1x1 Convolution 来实现,也就是类似于 Squeeze-and-Excitation Block (Hu et al.) 的结构。Depthwise Convolution 用于学习特征中的 Spatially-Invariant 信息。KBA 模块即实现特征像素自适应的注意力机制。三者通过 Point-wise 相乘来达到类似于 GLU 与 SimpleGate 门控非线性激活函数。最后,一个 1x1 Convolution 再次进行通道间的信息融合,并通过一个可学习权重加权的 Skip-connection 来降低 MFF 模块的学习难度与提高稳定性。类似于 Transformer,MFF 最后在具体实现中也在最后增加了一个 Feed Forward Network (FFN),其主要通过两个相连的 1x1 Convolution 对通道特征进行强化,最后通过另一个可学习权重加权的 Skip-connection 获得 MFF 的输出。MFF 的代码如下:

''' Channel Attention
1. global average pooling to NxCx1x1
2. channel attention by 1x1 convolution
'''
self.sca = nn.Sequential(
    nn.AdaptiveAvgPool2d(1), # equivalent to global average pooling
    nn.Conv2d(in_channels=c, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True),
)

''' DWConv
1. 1x1 convolution
2. 3x3 grouping convolution, groups=c, i.e. depthwise
'''
self.conv11 = nn.Sequential(
    nn.Conv2d(in_channels=c, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True),
    nn.Conv2d(in_channels=c, out_channels=c, kernel_size=3, padding=1, stride=1, groups=c, bias=True),
)

''' post 1x1 convolution
'''
self.conv3 = nn.Conv2d(in_channels=dw_ch // 2, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True)

''' FFN
'''
self.conv4 = nn.Conv2d(in_channels=c, out_channels=ffn_ch, kernel_size=1, padding=0, stride=1, groups=1, bias=True)
self.conv5 = nn.Conv2d(in_channels=ffn_ch // 2, out_channels=c, kernel_size=1, padding=0, stride=1, groups=1, bias=True)

''' learnable weights
'''
self.attgamma = nn.Parameter(torch.zeros((1, self.nset, 1, 1)) + 1e-2, requires_grad=True)
self.ga1 = nn.Parameter(torch.zeros((1, c, 1, 1)) + 1e-2, requires_grad=True)
self.beta = nn.Parameter(torch.zeros((1, c, 1, 1)) + 1e-2, requires_grad=True)
self.gamma = nn.Parameter(torch.zeros((1, c, 1, 1)) + 1e-2, requires_grad=True)

self.sg = SimpleGate()                   
self.norm1 = LayerNorm2d(c)
self.norm2 = LayerNorm2d(c)

def forward(self, inp):
    x = inp
    # LayrNorm
    x = self.norm1(x)
	# channel attention
    sca = self.sca(x)
    # DWConv
    x1 = self.conv11(x)
    # KBA module
    att = self.conv2(x) * self.attgamma + self.conv211(x)
    uf = self.conv21(self.conv1(x))
    # KBA with weighted skip-connection
    x = self.KBA(uf, att, self.k, self.g, self.b, self.w) * self.ga1 + uf
    # branch compose
    x = x * x1 * sca
    # post 1x1 conv
    x = self.conv3(x)
    # MFF skip-connection
    y = inp + x * self.beta
    # FFN
    x = self.norm2(y)
    x = self.conv4(x)
    x = self.sg(x)
    x = self.conv5(x)
	# FFN skip-connection
    return y + x * self.gamma

4 参考文献

  • Zhang Y, Li D, Shi X, et al. KBNet: Kernel Basis Network for Image Restoration[J]. arXiv preprint arXiv:2303.02881, 2023.
  • Wang Z, Cun X, Bao J, et al. A General U-Shaped Transformer for Image Restoration. arXiv 2021[J]. arXiv preprint arXiv:2106.03106.
  • Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[J]. Advances in neural information processing systems, 2017, 30.
  • Hu J, Shen L, Sun G. Squeeze-and-excitation networks[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2018: 7132-7141.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/627946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[中阳期货】端午都有哪些习俗,为什么不能说快乐?

端午节(屈原故里端午习俗),流行于湖北省宜昌市、秭归县的传统民俗,国家级非物质文化遗产之一。 “五月五(农历),过端午。”端午节是中华民族的传统节日。《续齐谐记》、《荆楚岁时记》载&#x…

AI 人工智能介绍(一)

人工智能(AI)是一种利用计算机程序和算法来模拟人类智能的技术。通俗地说,就是让计算机能够像人一样思考、学习、推理和决策。 人工智能改变了我们的生活!它被广泛应用于语音识别、计算机视觉、自然语言处理、智能机器人等多种领…

ubuntu20.04虚拟机安装

下载对应版本镜像文件(iso) 下载链接:https://releases.ubuntu.com/jammy/ 虚拟机安装工具为VMware 这里我的版本为下图所示 使用vmware创建虚拟机 1,点击 “创建新的虚拟机” 2,进入向导,选择自定义 …

基于docker部署的Selenium Grid分布式自动化测试

01、什么是Selenium Grid Selenium Grid是Selenium套件的一部分,它专门用于并行运行多个测试用例在不同的浏览器、操作系统和机器上。 Selenium Grid有两个版本——老版本Grid 1和新版本Grid 2。我们只对新版本做介绍,因为Selenium团队已经逐渐遗弃老版…

非线性规划求解方法:序列线性规划(Sequential linear programming)

来源:Cornell University Computational Optimization Open Textbook:SLP​​​​​​​ 目录 1.介绍 2.理论和方法 2.1 问题形式 2.1.1 NLP问题形式 2.1.2 SLP问题形式 2.2 步长边界 Step Bounds 2.3 完整的SLP算法 3.案例 3.1 example1 3.2…

数据结构:树状数组详解

一. 背景 那么我们为什么要用树状数组呢? 在解决一些区间求和的问题中 , 简单描述就是,对于一个给定的数组A,希望能够设计一个update函数来修改其中一个数的值,然后再设计一个sum函数来计算数组下标再给定参数l和r之间的值之和。关键点在于…

Docker Swarm 集群搭建和使用 —— 筑梦之路

简单介绍 swarm 集群由管理节点(Manager)和工作节点(Worker)构成。 管理节点:主要负责整个集群的管理工作包括集群配置、服务管理等所有跟集群有关的工作。诸如监控集群状态、分发任务至工作节点等操作。 工作节点&…

【计算机视觉】使用 notebook 展示如何下载和运行 CLIP models,计算图片和文本相似度,实现 zero-shot 图片分类

文章目录 一、CLIP 模型二、准备三、加载模型四、查看图片处理器五、文本分词六、输入图片和文本,并可视化七、将图片和文字 encode 生成特征八、计算 cosine 相似度九、零样本进行图片分类十、编写函数进行图片分类十一、测试自己的函数十二、编写函数对多图片进行…

面对职业发展“迷茫期”除了抱怨焦虑我们还能做什么?

关注“软件测试藏经阁”微信公众号,回复暗号【软件测试】,即可获取氪肝整理的全套测试资源 Java和Python做自动化测试,哪个更有优势?这两个语言都是很流行的语言,所以从技术上很难说谁好谁不好的。因为要说好不好得看…

linux安装homeassistant(智能设备远程控制开源框架)

1、安装docker 先切换到root 用户,先安装一些基本环境: yum install -y yum-utils device-mapper-persistent-data lvm2添加阿里云软件源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo然后安装 D…

QT+OpenGL高级光照 Blinn-Phong和Gamma校正

QTOpenGL高级光照1 本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主 Blinn-Phong 冯氏光照:视线与反射方向之间的夹角不小于90度,镜面光分量会变成0.0(不是很合理&am…

死信队列小结

死信队列是RabbitMQ中非常重要的一个特性。简单理解,他是RabbitMQ对于未能正常消费的消息进行的 一种补救机制。死信队列也是一个普通的队列,同样可以在队列上声明消费者,继续对消息进行消费处理。 对于死信队列,在RabbitMQ中主要…

Spring 是什么?IoC 和 DI的区别

1. Spring 是什么?2. IoC是什么? 2.DI概念说明 1. Spring 是什么? 我们通常讲的Spring指的是Spring Framework(Spring框架),它是一个开源的框架,有着活跃而庞大的社区,这也是它之所谓经久不衰的原因。官方的解读是:Spring官网 翻译过来就是:Spring使Java编程对每…

学会这5个步骤,就能轻轻松松地获取代码覆盖率报告

目录 前言: 1、创建main函数的test文件 2、插桩方式编译源码 3、运行主服务 4、执行测试用例 5、优雅退出主服务,并生成覆盖率报告 前言: 代码覆盖率报告可以帮助我们了解测试用例的质量和覆盖程度。 小编前期所测项目多为go语言研发&…

《C++高级编程》读书笔记(一:C++和标准库速成)

1、参考引用 C高级编程(第4版,C17标准)马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门,笔记链接如下 21天学通C读书笔记(文章链接汇总) 1. C 基础知识 1.1 小程序 “hello world” // helloworld.cpp…

开源项目合集......

likeshop开源商城系统,公众号商城、H5商城、微信小程序商城、抖音小程序商城、字节小程序商城、头条小程序商城、安卓App商城、苹果App商城代码全开源,免费商用。 适用场景:B2C商城、新零售商城、社交电商商城、分销系统商城、小程序商城、商…

循环链表的创建

循环链表的介绍及创建(C语言代码实现) 点击打开在线编译器,边学边练 循环链表概念 对于单链表以及双向链表,其就像一个小巷,无论怎么样最终都能从一端走到另一端,然而循环链表则像一个有传送门的小巷&…

力扣 912. 排序数组

文章目录 一、题目描述二、题解1.快速排序2.堆排序3.二路归并排序 一、题目描述 给你一个整数数组 nums,请你将该数组升序排列。 示例 1: 输入:nums [5,2,3,1] 输出:[1,2,3,5]示例 2: 输入:nums [5,1,1…

精细消费 年轻人和父母的奇妙交汇

日本社会学家三浦展结合对日本“311”大地震后的社会观察,提出了“第四消费时代”,即人们在经历了消费社会充分的发展过程之后,社会上逐渐兴起了低欲望、乐于共享、重视环保的消费理念。 在当时,主流观点普遍认为中国还处于大众化…

JWT单点登录

单点登录 文章目录 单点登录零、用户模块内容以及设计一、问题的提出二、单点登录SSO1.1 什么是单点登录1.2 单点登录的技术实现机制 二、远程调用方式RPC三、JWT的使用3.1 session的使用原理3.2 JWT介绍3.3 JWT原理3.4 JWT的使用 四、CAS实现单点登录的原理四、CAS的安装和代码…