自监督去噪: self2self 原理及实现(Pytorch)

Self2Self With Dropout: Learning Self-Supervised Denoising From Single Image

在这里插入图片描述

  • 文章地址:https://ieeexplore.ieee.org/document/9157420
  • 原始代码:https://github.com/scut-mingqinchen/self2self
  • 本文参考代码: https://github.com/JinYize/self2self_pytorch
  • 本文参考博客: https://zhuanlan.zhihu.com/p/361472663
  • website:https://csyhquan.github.io/

文章目录

    • Self2Self With Dropout: Learning Self-Supervised Denoising From Single Image
      • 1. 原理简介
      • 2. 网络结构
      • 3. Pytorch实现
        • (1)Partial convolution 结构
        • (2) U-net 网络结构
        • (3)网络训练
        • (4)迭代结果
      • 总结

1. 原理简介

噪声图片 y 可以表示为 干净图片 x 和噪声 n的叠加
y = x + n y = x + n y=x+n

使用单个输入进行预测 的原理是:
F θ ( . )    :    y → x F_{\theta}(.) \; : \; y \rightarrow x Fθ(.):yx

常规监督神经网络训练
m i n θ ∑ i L ( F θ ( x ( i ) ) , y ( i ) ) \underset{\theta}{min} \sum_i L(F_{\theta}(x^{(i)}),y^{(i)}) θminiL(Fθ(x(i)),y(i))

其中 F θ F_{\theta} Fθ是神经网络, θ \theta θ是网络参数;但是就从一个神经网络训练的过程来看
M S E = b i a s 2 + v a r i a n c e MSE = bias ^2 + variance MSE=bias2+variance

当训练数据减少的时候,variance会极剧增加。blind-spot技术可以用来阻止这种过拟合现象,但单个样本训练带来的大的variance是无法解决的。这也是基于blind-spot的神经网络 N2V和N2S在单个图片上效果不好的原因。

Dropout技术是一种广泛应用的正则化技术,同时其可以提供一定程度的不确定性估计,避免出现恒等映射。盲点策略通过对噪声数据随机采样合成多个不同的噪声数据版本,并在这些替换样本上计算损失。因此本文提出的一个策略就变为了:在输入图像的伯努利采样实例上定义自预测损失函数
y ^ [ k ] = { y [ k ] , w i t h    p r o b a b i l i t y    p ; 0 , w i t h    p r o b a b i l i t y    1 − p \hat{y}[k] = \begin{cases} y[k] &,with \; probability \; p; \\ 0 &,with \; probability \; 1-p \end{cases} y^[k]={y[k]0,withprobabilityp;,withprobability1p

采样两个 Bernoulli 采样实例数据集 y ^ m {\hat{y}_m} y^m y n ^ \hat{y_n} yn^

  • 训练过程,最小化下面这个损失
    m i n θ ∑ m L ( F θ ( y ^ m ) , y − y ^ m ) \underset{\theta}{min} \sum_m L(F_{\theta}(\hat{y}_m),y-\hat{y}_m) θminmL(Fθ(y^m),yy^m)

  • 测试过程:在另一个采样数据集上, 得到每一个 y n y_n yn对应的预测结果,然后求一个平均值得到最后的去噪数据


2. 网络结构

在这里插入图片描述

  • Encoder结构

    • 输入大小 H × W × C H \times W \times C H×W×C
    • 使用 partial convolution layer(Pconv)将输入变为 H × W × 48 H \times W \times 48 H×W×48
    • 然后使用六个 encoder block(EBs):
      • 前五个包含 Pconv层,1个 Leakey ReLu激活函数,一个最大池化层(2*2感受野、stride为2)
      • 最后一层只有 Pconv层和 一个 Leakey ReLU激活函数
      • 通道固定为48
    • 编码器的输出为 H / 32 × W / 32 × 48 H/32 \times W/32 \times 48 H/32×W/32×48
  • Decoder 结构:

    • 包含五个decoder blocks
      • 前四个blcok每一个包含一个上采样参数为2的上采样层,一个concate操作,两个标准的Conv层和 Leakey Relu激活。concate操作是将上采样得到的结果进行了聚集。
      • 前四个block都有96个输出通道
    • 最后一个decoder block有三个dropout层,使用LeakeyReLU激活函数。最后将输出恢复为 H × W × C H \times W \times C H×W×C的大小

部分细节:

  • 所有的PConv层和Conv层都使用kernel size 3*3,strid = 1,padding = 2
  • Leakdy ReLU的斜率为 0.1
  • droupouts的概率为0.3
  • bernoulli sampling的概率为 0.3
  • 使用Adam优化器,学习率 1 0 − 5 10^{-5} 105,迭代450000次

结构和 Noise2Noise结构基本相似,不同点在于:

  • 在Decoder中加入了dropout (不确定性估计和稳定性)
  • 在Encoder中使用部分卷积替代标准卷积

3. Pytorch实现

(1)Partial convolution 结构

注意,这里是使用的 部分卷积网络,所以使用了 NVIDIA的实现,

  • 具体代码参考 https://github.com/NVIDIA/partialconv
  • 解释说明参考 https://zhuanlan.zhihu.com/p/519664740
import torch
import torch.nn.functional as F
from torch import nn, cuda
from torch.autograd import Variable

class PartialConv2d(nn.Conv2d):
    def __init__(self, *args, **kwargs):

        # whether the mask is multi-channel or not
        if 'multi_channel' in kwargs:
            self.multi_channel = kwargs['multi_channel']
            kwargs.pop('multi_channel')
        else:
            self.multi_channel = False  

        if 'return_mask' in kwargs:
            self.return_mask = kwargs['return_mask']
            kwargs.pop('return_mask')
        else:
            self.return_mask = False

        #####Yize's fixes
        self.multi_channel = True
        self.return_mask = True
        
        super(PartialConv2d, self).__init__(*args, **kwargs)

        if self.multi_channel:
            self.weight_maskUpdater = torch.ones(self.out_channels, self.in_channels, self.kernel_size[0], self.kernel_size[1])
        else:
            self.weight_maskUpdater = torch.ones(1, 1, self.kernel_size[0], self.kernel_size[1])
            
        self.slide_winsize = self.weight_maskUpdater.shape[1] * self.weight_maskUpdater.shape[2] * self.weight_maskUpdater.shape[3]

        self.last_size = (None, None, None, None)
        self.update_mask = None
        self.mask_ratio = None

    def forward(self, input, mask_in=None):
        assert len(input.shape) == 4
        if mask_in is not None or self.last_size != tuple(input.shape):
            self.last_size = tuple(input.shape)

            with torch.no_grad():
                if self.weight_maskUpdater.type() != input.type():
                    self.weight_maskUpdater = self.weight_maskUpdater.to(input)

                if mask_in is None:
                    # if mask is not provided, create a mask
                    if self.multi_channel:
                        mask = torch.ones(input.data.shape[0], input.data.shape[1], input.data.shape[2], input.data.shape[3]).to(input)
                    else:
                        mask = torch.ones(1, 1, input.data.shape[2], input.data.shape[3]).to(input)
                else:
                    mask = mask_in
                        
                self.update_mask = F.conv2d(mask, self.weight_maskUpdater, bias=None, stride=self.stride, padding=self.padding, dilation=self.dilation, groups=1)

                # for mixed precision training, change 1e-8 to 1e-6
                self.mask_ratio = self.slide_winsize/(self.update_mask + 1e-8)
                # self.mask_ratio = torch.max(self.update_mask)/(self.update_mask + 1e-8)
                self.update_mask = torch.clamp(self.update_mask, 0, 1)
                self.mask_ratio = torch.mul(self.mask_ratio, self.update_mask)


        raw_out = super(PartialConv2d, self).forward(torch.mul(input, mask) if mask_in is not None else input)

        if self.bias is not None:
            bias_view = self.bias.view(1, self.out_channels, 1, 1)
            output = torch.mul(raw_out - bias_view, self.mask_ratio) + bias_view
            output = torch.mul(output, self.update_mask)
        else:
            output = torch.mul(raw_out, self.mask_ratio)


        if self.return_mask:
            return output, self.update_mask
        else:
            return output

(2) U-net 网络结构

class EncodeBlock(nn.Module):
    def __init__(self,in_channel,out_channel,flag):
        super(EncodeBlock,self).__init__()
        self.conv = PartialConv2d(in_channel, out_channel, kernel_size = 3, padding = 1)
        self.nonlinear = nn.LeakyReLU(0.1)
        self.MaxPool = nn.MaxPool2d(2)
        self.flag = flag
    
    def forward(self, x, mask_in):
        out1, mask_out = self.conv(x, mask_in = mask_in)
        out2 = self.nonlinear(out1)
        if self.flag:
            out = self.MaxPool(out2)
            mask_out = self.MaxPool(mask_out)
        else:
            out = out2
        return out, mask_out
    
class DecodeBlock(nn.Module):
    def __init__(self, in_channel, mid_channel, out_channel, final_channel = 3, p = 0.7, flag = False):
        super(DecodeBlock,self).__init__()
        self.conv1 = nn.Conv2d(in_channel,mid_channel,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(mid_channel,out_channel,kernel_size=3,padding=1)
        self.conv3 = nn.Conv2d(out_channel,final_channel,kernel_size=3,padding=1)
        self.nonlinear1 = nn.LeakyReLU(0.1)
        self.nonlinear2 = nn.LeakyReLU(0.1)
        self.sigmoid = nn.Sigmoid()
        self.flag = flag
        self.Dropout = nn.Dropout(p)
    
    def forward(self,x):
        out1 = self.conv1(self.Dropout(x))
        out2 = self.nonlinear1(out1)
        out3 = self.conv2(self.Dropout(out2))
        out4 = self.nonlinear2(out3)
        if self.flag:
            out5 = self.conv3(self.Dropout(out4))
            out = self.sigmoid(out5)
        else:
            out = out4
        return out
        
class self2self(nn.Module):
    def __init__(self,in_channel,p):
        super(self2self,self).__init__()
        self.EB0 = EncodeBlock(in_channel,out_channel=48,flag=False)
        self.EB1 = EncodeBlock(48,48,flag=True)
        self.EB2 = EncodeBlock(48,48,flag=True)
        self.EB3 = EncodeBlock(48,48,flag=True)
        self.EB4 = EncodeBlock(48,48,flag=True)
        self.EB5 = EncodeBlock(48,48,flag=True)
        self.EB6 = EncodeBlock(48,48,flag=False)
        
        self.DB1 = DecodeBlock(in_channel=96,mid_channel=96,out_channel=96,p=p)
        self.DB2 = DecodeBlock(in_channel=144,mid_channel=96,out_channel=96,p=p)
        self.DB3 = DecodeBlock(in_channel=144,mid_channel=96,out_channel=96,p=p)
        self.DB4 = DecodeBlock(in_channel=144,mid_channel=96,out_channel=96,p=p)
        self.DB5 = DecodeBlock(in_channel=96+in_channel,mid_channel=64,out_channel=32,p=p,flag=True)
        
        self.Upsample = nn.Upsample(scale_factor=2,mode='bilinear')
        self.concat_dim = 1
    
    def forward(self,x,mask):
        out_EB0,mask = self.EB0(x,mask)                 # [3,w,h]        ->     [48,w,h]
        out_EB1,mask = self.EB1(out_EB0,mask_in=mask)   # [48,w,h]       ->     [48,w/2,h/2]
        out_EB2,mask = self.EB2(out_EB1,mask_in=mask)   # [48,w/2,h/2]   ->     [48,w/4,h/4]
        out_EB3,mask = self.EB3(out_EB2,mask_in=mask)   # [48,w/4,h/4]   ->     [48,w/8,h/8]
        out_EB4,mask = self.EB4(out_EB3,mask_in=mask)   # [48,w/8,h/8]   ->     [48,w/16,h/16]
        out_EB5,mask = self.EB5(out_EB4,mask_in=mask)   # [48,w/16,h/16] ->     [48,w/32,h/32]
        out_EB6,mask = self.EB6(out_EB5,mask_in=mask)   # [48,w/32,h/32] ->     [48,w/32,h/32]
        
        out_EB6_up = self.Upsample(out_EB6)             # [48,w/32,h/32] ->     [48,w/16,h/16]
        in_DB1 = torch.cat((out_EB6_up,out_EB4),self.concat_dim) # [48,w/16,h/16] -> [96,w/16,h/16]
        out_DB1 = self.DB1((in_DB1))                    # [96,w/16,h/16] ->     [96,w/16,h/16]
        
        out_DB1_up = self.Upsample(out_DB1)             # [96,w/16,h/16] ->     [96,w/8,h/8]
        in_DB2 = torch.cat((out_DB1_up,out_EB3),self.concat_dim) # [96,w/8,w/8] -> [144,w/8,w/8]
        out_DB2 = self.DB2((in_DB2))                    # [144,w/8,w/8] -> [96,w/8,w/8]
        
        out_DB2_up = self.Upsample(out_DB2)             # [96,w/8,h/8] ->     [96,w/4,h/4]
        in_DB3 = torch.cat((out_DB2_up,out_EB2),self.concat_dim) # [96,w/4,w/4] -> [144,w/4,w/4]
        out_DB3 = self.DB2((in_DB3))                    # [144,w/4,w/4] -> [96,w/4,w/4]
        
        out_DB3_up = self.Upsample(out_DB3)             # [96,w/4,h/4] ->     [96,w/2,h/2]
        in_DB4 = torch.cat((out_DB3_up, out_EB1),self.concat_dim) # [96,w/2,w/2] -> [144,w/2,w/2]
        out_DB4 = self.DB4((in_DB4))                    # [144,w/2,w/2] -> [96,w/2,w/2]
        
        out_DB4_up = self.Upsample(out_DB4)             # [96,w/2,h/2] ->     [96,w,h]
        in_DB5 = torch.cat((out_DB4_up, x),self.concat_dim) # [96,w,h] ->     [96+c,w,h]
        out_DB5 = self.DB5(in_DB5)                      # [96+c,w,h] ->     [32,w,h]
        return out_DB5
    
model = self2self(3,0.3)
model

(3)网络训练

import numpy as np 
import matplotlib.pyplot as plt
import torch.optim as optim
import torchvision.transforms as T
import cv2 
from PIL import Image
from tqdm import tqdm

# 图片加载
img = np.array(Image.open("5.png"))

plt.figure()
plt.imshow(img)
plt.show()
img.shape

在这里插入图片描述

# 参数设置
##Enable GPU
USE_GPU = True

dtype = torch.float32

if USE_GPU and torch.cuda.is_available():
    device = torch.device('cuda')
else:
    device = torch.device('cpu')

print('using device:', device)

learning_rate = 1e-4
model = model.cuda()
optimizer = optim.Adam(model.parameters(), lr = learning_rate)
w,h,c = img.shape
p=0.3
NPred=100
slice_avg = torch.tensor([1,3,512,512]).to(device)
# 训练迭代
def image_loader(image, device, p1, p2):
    """
        load image and returns cuda tensor
    """
    loader = T.Compose([
            T.RandomHorizontalFlip(torch.round(torch.tensor(p1))),
            T.RandomVerticalFlip(torch.round(torch.tensor(p2))),
            T.ToTensor()])
    image = Image.fromarray(image.astype(np.uint8))
    image = loader(image).float()
    if not torch.is_tensor(image):
        image = torch.tensor(image)
    image = image.unsqueeze(0)  #this is for VGG, may not be needed for ResNet
    return image.to(device)

pbar = tqdm(range(500000))
for itr in pbar:
    # 不知道这个采样是否正确,是不是需要在每一个通道都分别进行均匀采样?
    p_mtx = np.random.uniform(size=[img.shape[0],img.shape[1],img.shape[2]])
    mask = (p_mtx>p).astype(np.double)
    img_input = img
    
    y = img
    p1 = np.random.uniform(size=1)
    p2 = np.random.uniform(size=1)
    # 加载输入图片(根据概率进行翻转)
    img_input_tensor = image_loader(img_input, device, p1, p2)
    
    # 对原始图片进行相同操作(翻转)
    y = image_loader(y, device, p1, p2)
    
    # mask为伯努利采样结果
    mask = np.expand_dims(np.transpose(mask,[2,0,1]),0)
    mask = torch.tensor(mask).to(device, dtype=torch.float32)

    # 网络推理
    model.train()
    img_input_tensor = img_input_tensor*mask
    output = model(img_input_tensor, mask)

    # 损失函数
    # loss = torch.sum((output+img_input_tensor-y)*(output+img_input_tensor-y)*(1-mask))/torch.sum(1-mask)
    loss = torch.sum((output-y)*(output-y)*(1-mask))/torch.sum(1-mask)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    pbar.set_description("iteration {}, loss = {:.4f}".format(itr+1, loss.item()*100))

    if (itr+1)%1000 == 0:
        model.eval()
        sum_preds = np.zeros((img.shape[0],img.shape[1],img.shape[2]))
        for j in range(NPred):
            p_mtx = np.random.uniform(size=img.shape)
            mask = (p_mtx>p).astype(np.double)
            img_input = img*mask
            img_input_tensor = image_loader(img_input, device, 0.1, 0.1)
            mask = np.expand_dims(np.transpose(mask,[2,0,1]),0)
            mask = torch.tensor(mask).to(device, dtype=torch.float32)
            
            output_test = model(img_input_tensor,mask)
            sum_preds[:,:,:] += np.transpose(output_test.detach().cpu().numpy(),[2,3,1,0])[:,:,:,0]
        avg_preds = np.squeeze(np.uint8(np.clip((sum_preds-np.min(sum_preds)) / (np.max(sum_preds)-np.min(sum_preds)), 0, 1) * 255))
        write_img = Image.fromarray(avg_preds)
        write_img.save("./examples/images/Self2self-"+str(itr+1)+".png")
        torch.save(model.state_dict(),'./examples/models/model-'+str(itr+1))

展示一下这里进行伯努利采样得到的结果和输入的噪声图片的区别
在这里插入图片描述

(4)迭代结果

展示不同次数的结果:
1000,10000,20000,30000次迭代

总结

从我自己可能会用到的地方进行 评价 (不是评价啊哈,大佬的工作真的非常棒,就是从我们迁移应用的角度看待)

  • 单样本任务,不需要合成特别多的样本
  • 使用Dropout引入了模型的不确定性估计,可以使得恢复更加稳定
  • 使用部分卷积替代常规卷积,对于图片去噪和恢复有一定的效果
  • 和Deep Image Prior相比,二者都不需要多余的样本,但是self2self更加稳定

一些小问题:

  • 迭代次数太多,上述操作迭代了500000次
  • 如果一张照片去噪需要1小时,那么其应用场景比较有限
  • 其实损失函数的设计,对该方法有一定的影响,可以尝试一下不同的损失函数,其结果会有一定的影响

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【万字长文】SpringBoot整合MyBatis搭建MySQL多数据源完整教程(提供Gitee源码)

前言:在我往期的博客介绍了2种关于如何使用SpringBoot搭建多数据源操作,本期博客我参考的是目前主流的框架,把最后一种整合多数据源的方式以博客的形式讲解完,整合的过程比较传统和复杂,不过我依旧会把每个实体类的思路…

NASA和uAvionix在AAM测试场部署SkyLine C2指挥和控制服务

蒙大拿州比格福克和弗吉尼亚州汉普顿2023年07月28日——美国宇航局和uAvionix签署了一项太空法案协议,为城市环境中的无人机系统 (UAS)开发先进的超视距(BVLOS)指挥和控制(C2)技术。根据协议,NASA将与uAvionix合作,利用基于互联网的基础设施和…

亚马逊云科技与真格基金发起「AI超新星计划」,助力早期创业者快速启动项目

大模型创业热度仍旧在持续增加,“百模大战”中AI创业者们的机会更多是在应用层。为了尽可能降低AI创业者的启动门槛,亚马逊云科技携手头部早期投资机构真格基金共同发起了「AI超新星计划」,为心怀梦想的AI应用创业者们提供了从云资源、模型选…

NSS刷web3

[HDCTF 2023]SearchMaster [天翼杯 2021]esay_eval 这题会匹配A或B类 如 "A":1: 绕不过去 可以考虑快速析构 <?php class A{public $code "";function __call($method,$args){eval($this->code);}function __wakeup(){$this->code "&q…

DLA :pytorch添加算子

pytorch的C extension写法 这部分主要介绍如何在pytorch中添加自定义的算子(例如&#xff0c;您可能希望 使用您在论文中找到的新颖激活函数&#xff0c;或实现操作 您作为研究的一部分进行了开发。)&#xff0c;需要以下cuda基础。就总体的逻辑来说正向传播需要输入数据&#…

Stable Diffusion AI绘画学习指南【插件安装设置】

插件安装的方式 可用列表方式安装&#xff0c;点开Extensions 选项卡&#xff0c;找到如下图&#xff0c;找到Available选项卡&#xff0c;点load from加载可用插件&#xff0c;在可用插件列表中找到要装的插件按install 按扭按装&#xff0c;安装完后(Apply and restart UI)应…

React(4)

1.属性&#xff08;props&#xff09;初始 状态state都是组件内部写的&#xff0c;也就是A组件内的state就只能A组件里面用&#xff0c;其他组件复用不了。因此属性props就可以。 比如一个导航栏&#xff0c;首页有&#xff0c;购物车有&#xff0c;我的有&#xff0c;他们三个…

数据可视化(4)散点图及面积图

1.简单散点图 #散点图 #scatter(x,y) x数据&#xff0c;y数据 x[i for i in range(10)] y[random.randint(1,10) for i in range(10)] plt.scatter(x,y) plt.show()2.散点图分析 #分析广告支出与销售收入相关性 dfcarpd.read_excel(广告支出.xlsx) dfdatapd.read_excel(销售…

NSX多租户之旅

从多租户数据面到完整的多租户框架 我们很高兴地宣布NSX中的Projects这一项新功能&#xff0c;可以对NSX部署的多个租户进行细粒度的资源管理。 Projects提供灵活的资源分配和管理&#xff0c;将NSX的多租户支持提升到新的水平。企业管理员可以将平台划分为不同Projects&…

【数据结构】27.移除元素

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Scratch 教程 -- 如何绘制像素画

1.像素画的定义 像素画就是以1像素的正方形为最小单位画的画&#xff0c;且物体有明显的分界线 这是像素画 这不是像素画 来看这两个法棍 这是像素画 这不是像素画 为什么第二个不是像素画&#xff1f;因为不能区分边缘和物体&#xff0c;它们之间有很多过渡色。 中间的过渡色属…

Spring依赖注入

文章目录 前言1.依赖注入简介2. setter注入3. 构造器注入4. 自动装配 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚&#xff0…

Sublime操作技巧笔记

同时选中2个文件&#xff1a;自动切换成左右2个界面 格式化代码ctrlshifth&#xff1a; 使用快捷键ctrl shift p调出控制台&#xff0c;输入install package&#xff0c;然后输入html-css-js prettify&#xff0c;进行下载。具体的快捷键在preference > package setting &g…

【深度学习】Inst-Inpaint: Instructing to Remove Objects with Diffusion Models,指令式图像修复

论文&#xff1a;https://arxiv.org/abs/2304.03246 code:http://instinpaint.abyildirim.com/ 文章目录 AbstractIntroductionRelated WorkDataset GenerationMethodPS Abstract 图像修复任务是指从图像中擦除不需要的像素&#xff0c;并以语义一致且逼真的方式填充它们。传统…

springboot对静态资源的支持

1、spring boot默认静态路径支持 Spring Boot 默认将 / 所有访问映射到以下目录&#xff1a;** classpath:/static classpath:/public classpath:/resources classpath:/META-INF/resources也就是说什么也不用配置&#xff0c;通过浏览器可以直接访问这几个目录下的文件。 1…

Spring之BeanDefinition(二)

Spring之BeanDefinition 文章目录 Spring之BeanDefinition1、对象和bean的区别2、BeanDefinition作用AutowireCandidate说明Primary说明ConstructorArgumentValues说明第一种使用方式第二种使用方式 MutablePropertyValuesabstract小结 3、BeanDefinition的发展历程3、BeanDefi…

C++ 类的友元

【例1】 将数据与处理数据的函数封装在一起&#xff0c;构成类&#xff0c;既实现了数据的共享又实现了隐藏&#xff0c;无疑是面向对象程序设计的一大优点。但是封装并不总是绝对的。现在考虑一个简单的例子&#xff0c;就是Point类&#xff0c;每一个Point类的对象代表一个“…

阿里云服务器免费试用及搭建WordPress网站

文章目录 前言一、免费试用1、选择使用产品2、进行产品配置3、远程连接阿里云服务器①、重置实例密码②、SecureCRT 远程链接③、Workbench 远程链接二、搭建 WordPress 网站1、开放搭建 WordPress 需要的端口2、搭建 LAMP 环境①、Linux 系统升级和更新源②、安装 Apache2③、…

LAXCUS分布式操作系统引领科技潮流,进入百度首页

信息源自某家网络平台&#xff0c;以下原样摘抄贴出。 随着科技的飞速发展&#xff0c;分布式操作系统做为通用基础平台&#xff0c;为大数据、高性能计算、人工智能提供了强大的数据和算力支持&#xff0c;已经成为了当今计算机领域的研究热点。近日&#xff0c;一款名为LAXCU…

云原生势不可挡,如何跳离云原生深水区?

云原生是云计算领域一大热词&#xff0c;伴随云原生概念而来的是数字产业迎来井喷、数字变革来临、数字化得以破局以及新一波的技术红利等等。云原生即“云”原生&#xff0c;顾名思义是让“应用”最大程度地利用云的能力&#xff0c;发挥云价值的最佳路径。具体来说&#xff0…
最新文章