像素级创意:深入浅出PixelCNN图像合成技术
参考
https://arxiv.org/pdf/1601.06759
https://blog.csdn.net/zcyzcyjava/article/details/126559327
需要熟悉熵的一些理论、和极大释然估计等价于最小化交叉熵等知识
1. pixelcnn建模方法
pixelcnn做生成模型的想必都有耳闻。它是一种自回归模型,什么是自回归呢?简单的来说自回归模型意味着模型在生成数据时会条件依赖于之前已经生成的数据部分。我们知道无论是GAN还是VAE最初都在找一个思路,那就是想对 p θ ( x ) p_\theta(x) pθ(x)去建模型,事实上输入x的分布是难以确定的,因此,GAN和VAE都绕过了这条路,通过引入额外的网络,避开直接求解 p θ ( x ) p_\theta(x) pθ(x),比如GAN是引入对抗网络D,VAE是引入编码网络。而pixcelcnn不一样,它算是直接暴力求解 p θ ( x ) p_\theta(x) pθ(x),是像素级别的求解,认为第i个像素是由前i-1个像素决定的,因此释然函数可以写成如下,假设有n*n个像素:
p ( x ) = ∏ i = 1 n 2 p ( x i ∣ x 1 , x 2 , . . . , x i − 1 ) = ∏ i = 1 n 2 p ( x i ∣ X < i ) p(x) = \prod_{i=1}^{n^2}p(x_i|x_1,x_2,...,x_{i-1})= \prod_{i=1}^{n^2}p(x_i|X_{<i}) p(x)=i=1∏n2p(xi∣x1,x2,...,xi−1)=i=1∏n2p(xi∣X<i)
我们知道,最大化释然函数参数的求解写成对数如下:
θ = a r g m a x θ ∑ i n 2 l o g f i ( x i ∣ θ ) \theta=\underset{\theta}{argmax}\sum_i^{n^2} logf_i(x_i|\theta) θ=θargmaxi∑n2logfi(xi∣θ)
其中 f i ( x i ∣ θ ) f_i(x_i|\theta) fi(xi∣θ)为网络预测的结果,另外,作者假设每一个预测的像素值,为0~255中的一个分类,因此,预测分布等价于one-hot形式的多分类。损失函数进而转化为多分类的交叉熵问题,为: l o s s = E n t r y C r o s s ( y i , x i ) loss=EntryCross(y_i,x_i) loss=EntryCross(yi,xi) 其中 y i y_i yi为预测的像素值, x i x_i xi为输入的像素值。
2. 网络结构


以上是论文中提及到的网络结构,我们这里只看CNN结构,不看RNN结构。看这个网络结构其实很简单,也就是77卷积(maskA)+多个卷积残差结构(33 maskB)+2个1*1卷积(maskB)。但是,这里面有两个不是我们认识的那个CNN,一个是maskA一个是maskB。
maskA:

maskB:

简单来说,maskA是不包含中心元素的上半部分卷积,maskB是包含中心元素的上半部分卷积。这样做的目的是什么,具体详细原由可看原论文,意思是这样做生成的feature map每一个像素的感受野只会看到它上半部分的像素,不包含自身像素,这也满足之前pixcelcnn的建模:每一个像素都是由其前面i-1个像素决定的。因此pixelcnn网络结构是确定的,跟其建模是一致的。
3 MINIST测试pixelcnn
3.1 maskcnn构建
pixelcnn 关键是mask卷积的构造,我们看一下具体如何实现:
class MaskConv2d(nn.Module):""" 通过使用 mask 来构建 maskA和maskB Conv2d,方法是通过mask乘上卷积的权重"""def __init__(self, conv_type, *args, **kwargs):""":param conv_type: maskA还是maskB:param args::param kwargs:"""super(MaskConv2d, self).__init__()self.conv = nn.Conv2d(*args, **kwargs)k_h, k_w = self.conv.weight.shape[-2:]mask = torch.zeros((k_h, k_w), dtype=torch.float32)# maskAmask[0:k_h//2] = 1mask[k_h//2, 0:k_w//2] = 1# maskBif conv_type == 'B':mask[k_h//2, k_w//2] = 1mask = mask.reshape((1,1,k_h, k_w))self.register_buffer('mask', mask, False)def forward(self, x):self.conv.weight.data *= self.maskconv_res = self.conv(x)return conv_res
通过以上代码,我们可以看出,先构造一个nn.Conv2d,然后构建maskA和maskB的只有0和1值的大小为kernel size的矩阵,然后乘上nn.Conv2d的权重,来实现两种Mask CNN,非常简单。
3.2 整个网络结构
整个网络结构是按照论文里面构建的,不在多说,直接看代码:
import torch
import torch.nn as nnclass MaskConv2d(nn.Module):""" 通过使用 mask 来构建 maskA和maskB Conv2d,方法是通过mask乘上卷积的权重"""def __init__(self, conv_type, *args, **kwargs):""":param conv_type: maskA还是maskB:param args::param kwargs:"""super(MaskConv2d, self).__init__()self.conv = nn.Conv2d(*args, **kwargs)k_h, k_w = self.conv.weight.shape[-2:]mask = torch.zeros((k_h, k_w), dtype=torch.float32)# maskAmask[0:k_h//2] = 1mask[k_h//2, 0:k_w//2] = 1# maskBif conv_type == 'B':mask[k_h//2, k_w//2] = 1mask = mask.reshape((1,1,k_h, k_w))self.register_buffer('mask', mask, False)def forward(self, x):self.conv.weight.data *= self.maskconv_res = self.conv(x)return conv_resclass ResidualBlock(nn.Module):""" 残差块 """def __init__(self, h, bn=True):super(ResidualBlock, self).__init__()self.relu = nn.ReLU()self.conv1 = nn.Conv2d(2*h, h, 1)self.bn1 = nn.BatchNorm2d(h) if bn else nn.Identity()self.conv2 = MaskConv2d('B', h, h, 3, 1, 1)self.bn2 = nn.BatchNorm2d(h) if bn else nn.Identity()self.conv3 = nn.Conv2d(h, 2*h, 1)self.bn3 = nn.BatchNorm2d(2*h) if bn else nn.Identity()def forward(self, x):y = self.relu(x)y = self.conv1(y)y = self.bn1(y)y = self.relu(y)y = self.conv2(y)y = self.bn2(y)y = self.relu(y)y = self.conv3(y)y = self.bn3(y)return x + yclass PixelCNN(nn.Module):def __init__(self, n_block=15, h=128, bn=True, color_level=256):super(PixelCNN, self).__init__()# 7*7 convself.conv1 = MaskConv2d('A', 1, 2 * h, 7, 1, 3)self.bn1 = nn.BatchNorm2d(2 * h) if bn else nn.Identity()# residualself.residual_blocks = nn.ModuleList()for _ in range(n_block):self.residual_blocks.append(ResidualBlock(h, bn))self.relu = nn.ReLU()# 2个1*1 maskB,self.head = nn.Sequential(MaskConv2d('B', 2*h, h, 1),nn.ReLU(),MaskConv2d('B', h, h, 1),nn.ReLU(),nn.Conv2d(h, color_level, 1))def forward(self, x):x = self.conv1(x)x = self.bn1(x)for block in self.residual_blocks:x = block(x)x = self.relu(x)x = self.head(x)return xif __name__ == '__main__':from torchinfo import summarypixelcnn = PixelCNN()summary(pixelcnn, input_size=(1, 1, 28, 28), depth=2)
打印看一下网络结构,如果想看详细的层级结构,把summary中的depth改成3
PixelCNN [1, 256, 28, 28] –
├─MaskConv2d: 1-1 [1, 256, 28, 28] –
│ └─Conv2d: 2-1 [1, 256, 28, 28] 12,800
├─BatchNorm2d: 1-2 [1, 256, 28, 28] 512
├─ModuleList: 1-3 – –
│ └─ResidualBlock: 2-2 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-3 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-4 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-5 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-6 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-7 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-8 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-9 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-10 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-11 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-12 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-13 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-14 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-15 [1, 256, 28, 28] 214,528
│ └─ResidualBlock: 2-16 [1, 256, 28, 28] 214,528
├─ReLU: 1-4 [1, 256, 28, 28] –
├─Sequential: 1-5 [1, 256, 28, 28] –
│ └─MaskConv2d: 2-17 [1, 128, 28, 28] 32,896
│ └─ReLU: 2-18 [1, 128, 28, 28] –
│ └─MaskConv2d: 2-19 [1, 128, 28, 28] 16,512
│ └─ReLU: 2-20 [1, 128, 28, 28] –
│ └─Conv2d: 2-21 [1, 256, 28, 28] 33,024
3.2 训练
import torch
import torch.nn as nn
import torchvision
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import torch.nn.functional as Fclass MaskConv2d(nn.Module):""" 通过使用 mask 来构建 maskA和maskB Conv2d,方法是通过mask乘上卷积的权重"""def __init__(self, conv_type, *args, **kwargs):""":param conv_type: maskA还是maskB:param args::param kwargs:"""super(MaskConv2d, self).__init__()self.conv = nn.Conv2d(*args, **kwargs)k_h, k_w = self.conv.weight.shape[-2:]mask = torch.zeros((k_h, k_w), dtype=torch.float32)# maskAmask[0:k_h//2] = 1mask[k_h//2, 0:k_w//2] = 1# maskBif conv_type == 'B':mask[k_h//2, k_w//2] = 1mask = mask.reshape((1,1,k_h, k_w))self.register_buffer('mask', mask, False)def forward(self, x):self.conv.weight.data *= self.maskconv_res = self.conv(x)return conv_resclass ResidualBlock(nn.Module):""" 残差块 """def __init__(self, h, bn=True):super(ResidualBlock, self).__init__()self.relu = nn.ReLU()self.conv1 = nn.Conv2d(2*h, h, 1)self.bn1 = nn.BatchNorm2d(h) if bn else nn.Identity()self.conv2 = MaskConv2d('B', h, h, 3, 1, 1)self.bn2 = nn.BatchNorm2d(h) if bn else nn.Identity()self.conv3 = nn.Conv2d(h, 2*h, 1)self.bn3 = nn.BatchNorm2d(2*h) if bn else nn.Identity()def forward(self, x):y = self.relu(x)y = self.conv1(y)y = self.bn1(y)y = self.relu(y)y = self.conv2(y)y = self.bn2(y)y = self.relu(y)y = self.conv3(y)y = self.bn3(y)return x + yclass PixelCNN(nn.Module):def __init__(self, n_block=15, h=128, bn=True, color_level=256):super(PixelCNN, self).__init__()# 7*7 convself.conv1 = MaskConv2d('A', 1, 2 * h, 7, 1, 3)self.bn1 = nn.BatchNorm2d(2 * h) if bn else nn.Identity()# residualself.residual_blocks = nn.ModuleList()for _ in range(n_block):self.residual_blocks.append(ResidualBlock(h, bn))self.relu = nn.ReLU()# 2个1*1 maskB,self.head = nn.Sequential(MaskConv2d('B', 2*h, h, 1),nn.ReLU(),MaskConv2d('B', h, h, 1),nn.ReLU(),nn.Conv2d(h, color_level, 1))def forward(self, x):x = self.conv1(x)x = self.bn1(x)for block in self.residual_blocks:x = block(x)x = self.relu(x)x = self.head(x)return xdef train(num_epochs, batch, gpuid):device = torch.device(f"cuda:{gpuid}")trian_data = datasets.MNIST(root='data', train=True, download=True, transform=transforms.ToTensor())train_dataloader = DataLoader(trian_data, batch_size=batch, shuffle=True)model = PixelCNN()model = model.to(device)optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)loss_fn = nn.CrossEntropyLoss()for epoch in range(num_epochs):model.train()for x, _ in train_dataloader:x = x.to(device)label = torch.ceil(x*255).long()label = label.squeeze(1)loss = loss_fn(model(x), label)optimizer.zero_grad()loss.backward()optimizer.step()print(f"epoch:{epoch}, loss:{loss.item()}")sample(model, device, 64)def sample(model, device, n_sample=64):model.eval()C, H, W = (1, 28, 28)x = torch.zeros((n_sample, C, H, W)).to(device)with torch.no_grad():for i in range(H):for j in range(W):output = model(x)prob_dist = F.softmax(output[:,:,i,j], dim=1).datapixel = torch.multinomial(prob_dist, 1).float() / 255x[:,:,i,j] = pixel# Saving images row wisetorchvision.utils.save_image(x, 'imgs.png', nrow=8, padding=0)if __name__ == '__main__':train(100, 128, 0)
上面是整个完整代码,训练很简单,就是比较常规,看一眼都能明白。关键是采样算法,采样算法的过程是,先初始一个全0的图像,然后得到第一个像素,然后把第一个像素赋值给输入图像的第一个元素,以此类推得到最终的生成图像。关键步骤为下面三步:
- prob_dist = F.softmax(output[:,:,i,j], dim=1).data 得到当前像素的概率分布
- pixel = torch.multinomial(prob_dist, 1).float() / 255 从这个概率分布中随机采样一个值对应的索引,也就是一个0-255的像素值,因为softmax返回的是0-255(其实是/255归一化后)对应的概率,因此取索引值即为推理的像素值,然后/255归一化成minist输入
- x[:,:,i,j] = pixel,这个就是把推理的当前像素值赋值给输入x,去推理下一帧输入
结果:质量上还是比GAN差。以下是50个epoch的结果。

4 缺点
pixelCNN作为一类基于卷积神经网络的生成模型,在图像生成领域有着其独特之处,但也存在一些缺点,主要包括但不限于:
-
生成速度慢:PixelCNN的核心缺点之一是生成速度缓慢。因为它采用自回归的方式生成图像,即模型需要依次生成每一个像素,每个像素的生成都依赖于之前的所有像素。这种方式导致在生成高分辨率图像时,所需的计算时间和步骤显著增加。
-
训练时间长:据报道,即便是增强版的PixelCNN如PixelCNN++,也需要在强大的硬件配置(如8块Titan GPU)上训练多天才能收敛,而且这还仅是在处理相对较小的数据集(如CIFAR)时的情况。训练时间长不仅增加了资源消耗,也影响了研究与应用的效率。
-
采样效率低:由于逐像素生成的特性,PixelCNN在采样过程中无法并行化,这意味着即使在现代GPU上也无法有效利用硬件加速带来的并行计算优势,进一步降低了生成效率。
-
长程依赖建模能力有限:尽管PixelCNN使用了卷积层来捕捉局部特征,但自回归的生成顺序限制了模型对图像中远距离像素间依赖关系的建模能力,可能影响生成图像的全局一致性与细节丰富度。
-
内存使用:逐像素生成的过程中需要存储中间状态以供后续像素生成使用,这可能导致较高的内存使用,尤其是在处理大尺寸图像时。
-
样本质量:相比于同期的一些生成对抗网络(GANs)模型,PixelCNN生成的样本质量可能略逊一筹,尤其是在生成高保真度和视觉复杂度高的图像方面。
尽管有这些缺点,PixelCNN及其后续变体通过引入如门控机制、更高效的网络结构设计等方法,在一定程度上改善了这些问题,并在图像生成任务中保持着一定的竞争力。
相关文章:
像素级创意:深入浅出PixelCNN图像合成技术
参考 https://arxiv.org/pdf/1601.06759 https://blog.csdn.net/zcyzcyjava/article/details/126559327 需要熟悉熵的一些理论、和极大释然估计等价于最小化交叉熵等知识 1. pixelcnn建模方法 pixelcnn做生成模型的想必都有耳闻。它是一种自回归模型,什么是自回归…...
MyBatisPlus使用流程
引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4</version> </dependency> 版本号根据需要选取 在实体类上加注解声明,表信息 根据数…...
爬虫技术升级:如何结合DrissionPage和Auth代理插件实现数据采集
背景/引言 在大数据时代,网络爬虫技术已经成为数据收集的重要手段之一。爬虫技术可以自动化地从互联网上收集数据,节省大量人力和时间成本。然而,当使用需要身份验证的代理服务器时,许多现有的爬虫框架并不直接支持代理认证。这就…...
go 微服务框架kratos错误处理的使用方法及原理探究
通过go语言原生http中响应错误的实现方法,逐步了解和使用微服务框架 kratos 的错误处理方式,以及探究其实现原理。 一、go原生http响应错误信息的处理方法 处理方法: ①定义返回错误信息的结构体 ErrorResponse // 定义http返回错误信息的…...
AI播客下载:Dwarkesh Podcast(关于AI的深度访谈)
Dwarkesh Podcast 是由 Dwarkesh Patel 主持的播客,专注于深度访谈和探讨各种复杂且有趣的话题。该播客在业界获得了极高的评价,被认为是对话和思想交流的平台。 Dwarkesh Podcast 的内容涵盖了多个领域,包括经济学、哲学以及科技等。例如&am…...
C++11function包装器的使用
类模板std::function是一种通用、多态的函数包装。std::function的实例可以对任何可以调用的目标实体进行存储、 复制和调用操作。这些目标实体包括普通函数、Lambda表达式、函数指针、以及其他函数对象等。std::function对象是对 C中现有的可调用实体的一种类型安全的包裹&…...
Vue3判断变量和对象不为null和undefined
Vue3判断变量和对象不为null和undefined 一、判断变量二、判断对象 一、判断变量 在 Vue 3 中,你可以使用 JavaScript 提供的常规方式来检查变量是否不为 null 和不为 undefined。你可以分别使用严格不等运算符 ! 来比较变量是否不为 null 和不为 undefined。以下是…...
C++进阶:C++11(列表初始化、右值引用与移动构造移动赋值、可变参数模版...Args、lambda表达式、function包装器)
C进阶:C11(列表初始化、右值引用与移动构造移动赋值、可变参数模版…Args、lambda表达式、function包装器) 今天接着进行语法方面知识点的讲解 文章目录 1.统一的列表初始化1.1{}初始化1.2 initializer_listpair的补充 2.声明相关关键字2.1a…...
Vue.js Promise 与 async/await 的比较
在现代 Web 开发中,异步操作是不可避免的。在处理异步数据获取时,开发人员通常会使用 Promise 或 async/await。虽然两者都可以实现相同的功能,但它们在代码风格、可读性和错误处理等方面有所不同。本文将对这两种方法进行比较,并…...
Qt 报错总结 No suitable kits found
目录 “No suitable kits found” 解决 解决方法参考: chatGPT辅助解决QT构建报错error: multiple target patterns 我的解决方法:把语言设置为空 “No suitable kits found” 解决 没有找到合适的kits套件,在安装Qt Creator时没有安装Min…...
ThingsBoard如何拆分前后端分离启动
后端启动 前端启动 注意事项 ThingsBoard是一个开源的物联网平台,它原本的设计就考虑到了现代Web应用的前后端分离架构。尽管其核心是一个后端服务,负责设备连接、数据处理和存储等,但其用户界面是作为单独的前端应用程序实现的,…...
加载页面 跳转 新页面 vue
通常,我们点页面上的详情,或者编辑,需要加载一个新的页面出来。 vue中加载页面的方法: 在父页面中(通常是某个模块目录下的index.vue),先写这行代码: import AddEditForm from ./…...
中国主要城市房价指数数据集(2011-2024)
数据来源:东方财富网 时间跨度:2011年1月 - 2024年4月 数据范围:中国主要城市 包含指标: 日期、城市 新建商品住宅价格指数-同比 新建商品住宅价格指数-环比 新建商品住宅价格指数-定基 二手住宅价格指数-环比 二手住宅价格指…...
Creating Server TCP listening socket *:6379: listen: Unknown error
错误: 解决方法: 在redis安装路径中打开cmd命令行窗口,输入 E:\Redis-x64-3.2.100>redis-server ./redis.windows.conf结果:...
JUnit5标记测试用例
使用场景: 通过Tag对用例分组: 环境分组:测试环境、预发布环境阶段分组:冒烟用例版本分组:V1.1、V1.2 Tag标记用例: 设置标签根据标签执行 结合Maven执行结合测试套件执行 设置标签: 通过T…...
在Windows10中重命名文件和文件夹的6种方法,有你熟悉和不熟悉的
序言 你可以通过多种方式在Windows 10上重命名文件。如果每次你想更改文件名时仍右键单击并选择“重命名”,那么我们有一些技巧可以加快更改速度。 使用文件资源管理器重命名文件和文件夹 Windows 10的文件资源管理器是一个功能强大的工具。你知道吗,有四种不同的方法可以…...
Go源码--sync库(1)sync.Once和
简介 这篇主要介绍 sync.Once、sync.WaitGroup和sync.Mutex sync.Once once 顾名思义 只执行一次 废话不说 我们看源码 英文介绍直接略过了 感兴趣的建议读一读 获益匪浅 其结构体如下 Once 是一个严格只执行一次的object type Once struct {// 建议看下源码的注解…...
头歌OpenGauss数据库-I.复杂查询第3关:统计总成绩
本关任务:计算每个班的语文总成绩和数学总成绩,要求科目中低于60分的成绩不记录总成绩。 tb_score结构数据: namechinesemathsA8998B9989C5566D8866E5566F8899tb_class表结构数据: stunameclassnameAC1BC2CC3DC2EC1FC3--#请在此添加实现代码 --# # # # # # # # # # Begin #…...
LeetCode hot100-47-N
105. 从前序与中序遍历序列构造二叉树给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。这题放选择题里还能选出来,前序中序一起确定了一颗什…...
中北大学软件学院计算机网络实验一
目录 1.实验名称2.实验目的3.实验内容4.实验过程(1)安装Packer Tracer并熟悉软件操作(2)利用一台型号为2960的交换机将2台pc机互连组建一个小型局域网(3)分别设置pc机的ip地址(4)验证…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
