libtorch c++复现cycle gan网络

news2024/11/30 6:50:31

目录

1. 原论文论文:https://arxiv.org/abs/1703.10593

2. 代码

2.1 下采样

2.2 残差块

 2.3 上采样模块

2.4 生成器代码

3. 判别器

3.1 判别器组件

 3. 2 判别器

 4. 训练

4.1 输入数据 

4.2 生成器loss函数结构图 

 4.3 判别器loss结构图


1. 原论文
论文:https://arxiv.org/abs/1703.10593

pytorch源码:GitHub - junyanz/pytorch-CycleGAN-and-pix2pix: Image-to-Image Translation in PyTorch

论文框架:

(1)输入领域A图片real_A,经过生成网络G_AB,生成领域B图片fake_B;

(2)fake_B再输入G_BA生成网络,生成real_A,即G_BA(G_AB(real_A)) = real_A;

(3)reconstructed image 和 输入图片real_A直接求loss,得到生成器损失;

(4)fake_B和real_B之间求生成器loss。

下面将结合代码,深入理解整个过程。

2. 代码

这里参考pytorch版本:GitHub - eriklindernoren/PyTorch-GAN: PyTorch implementations of Generative Adversarial Networks.

 实现libtorch版本。

其中,生成器G_AB 和 G_BA是同一个网络,框架细节如下。

 是一个先下采样,再接残差块,再上采样的全卷积网络。

2.1 下采样

 下采样模块是由conv2d+InstanceNorm2d+Relu组成,其中conv2d使其scale/2,channels/2.

// Down sampling : 通过conv2d进行两次下采样,同时double channels
class DownSampleImpl : public torch::nn::Module {
public:
	DownSampleImpl(int in_channels, int out_channels);
	torch::Tensor forward(torch::Tensor x);
private:
	torch::nn::Conv2d conv1{ nullptr };
	torch::nn::InstanceNorm2d bn1{ nullptr };
	torch::nn::ReLU relu1{ nullptr };
};
TORCH_MODULE(DownSample);
DownSampleImpl::DownSampleImpl(int in_channels, int out_channels) {
	conv1 = torch::nn::Conv2d(torch::nn::Conv2dOptions(in_channels, out_channels, 3).stride(2).padding(1));
	bn1 = torch::nn::InstanceNorm2d(out_channels);
	relu1 = torch::nn::ReLU(true);
	register_module("generator downsample pad1", conv1);
	register_module("generator downsample bn1", bn1);
	register_module("generator downsample relu1", relu1);
}
torch::Tensor DownSampleImpl::forward(torch::Tensor x) {
	x = conv1(x);
	x = bn1(x);
	x = relu1(x);
	return x;
}

2.2 残差块

每个残差块由 conv2d+InstanceNorm2d+Relu,再接conv2d+InstanceNorm2d组成。

输入到残差块的特征图shape: (b,3,256,256);

输出特征图的shape: (b,3,256,256). 即不改变维度。

// two conv2d+bn+relu. keep feature scale.
class ResidualBlockImpl : public torch::nn::Module {
public:
	ResidualBlockImpl(int in_channels);
	torch::Tensor forward(torch::Tensor x);
private:
	torch::nn::ReflectionPad2d pad1{ nullptr };
	torch::nn::Conv2d conv1{ nullptr };
	torch::nn::InstanceNorm2d bn1{ nullptr };
	torch::nn::ReLU relu1{ nullptr };
	
	torch::nn::ReflectionPad2d pad2{ nullptr };
	torch::nn::Conv2d conv2{ nullptr };
	torch::nn::InstanceNorm2d bn2{ nullptr };
};
TORCH_MODULE(ResidualBlock);
ResidualBlockImpl::ResidualBlockImpl(int in_channels) {
	pad1 = torch::nn::ReflectionPad2d(1);
	conv1 = torch::nn::Conv2d(torch::nn::Conv2dOptions(in_channels, in_channels, 3));
	bn1 = torch::nn::InstanceNorm2d(in_channels);
	relu1 = torch::nn::ReLU(true);

	pad2 = torch::nn::ReflectionPad2d(1);
	conv2 = torch::nn::Conv2d(torch::nn::Conv2dOptions(in_channels, in_channels, 3));
	bn2 = torch::nn::InstanceNorm2d(in_channels);
	register_module("block pad1", pad1);
	register_module("block conv1", conv1);
	register_module("block bn1", bn1);

	register_module("block pad2", pad2);
	register_module("block conv2", conv2);
	register_module("block bn2", bn2);
}
torch::Tensor ResidualBlockImpl::forward(torch::Tensor x) {
	x = pad1(x);
	x = conv1(x);
	x = bn1(x);
	x = relu1(x);

	x = pad2(x);
	x = conv2(x);
	x = bn2(x);
	return x;
}

 2.3 上采样模块

上采样模块由UpSample+Conv2d+InstanceNorm2d+ReLU组成。

用到两次上采样模块,维度变化(b,256,64,64)->(b,128,128,128)->(b,64,256,256)

/// <summary>
/// 两次上采样,(b,256,64,64)->(b,128,128,128)->(b,64,256,256)
/// </summary>
class UpSampleBlockImpl : public torch::nn::Module {
public:
	UpSampleBlockImpl(int in_channels, int out_channels);
	torch::Tensor forward(torch::Tensor x);
private:
	torch::nn::Upsample up{ nullptr };
	torch::nn::Conv2d conv{ nullptr };
	torch::nn::InstanceNorm2d bn{ nullptr };
	torch::nn::ReLU relu{ nullptr };
};
TORCH_MODULE(UpSampleBlock);
UpSampleBlockImpl::UpSampleBlockImpl(int in_channels, int out_channels) {
	up = torch::nn::Upsample(upsample_options(std::vector<double>({2, 2})));  
	conv = torch::nn::Conv2d(torch::nn::Conv2dOptions(in_channels, out_channels, 3).padding(1));
	bn = torch::nn::InstanceNorm2d(out_channels);
	relu = torch::nn::ReLU(true);

	register_module("generator UpSampleBlock upsample", up);
	register_module("generator UpSampleBlock conv", conv);
	register_module("generator UpSampleBlock bn", bn);
	register_module("generator UpSampleBlock relu", relu);
}
torch::Tensor UpSampleBlockImpl::forward(torch::Tensor x) {
	x = up(x);
	x = conv(x);
	x = bn(x);
	x = relu(x);
	return x;
}

 最后再接一个conv2d,将通道数变成3即可输出生成的图像。

2.4 生成器代码

 

可以直接看forward函数,有5个步骤

(1)先是一个conv+bn+relu,预处理模块, size: (b,3,256,256) ->(b,64,256,256);

(2)然后是两次下采样,提取特征,size: (b,64,256,256) - > (b,128,128,128) -> (b,256,64,64);

(3)再接多个残差块,提取特征, size: (b,256,64,64) -> (b,256,64,64);

(4)上采样,size: (b,256,64,64)->(b,128,128,128)->(b,64,256,256);

(5)最后接一个输出层,即conv2d+bn+relu,size: (b,64,256,256) -> (b,3,256,256);

/// <summary>
/// 下采样,res_blocks,上采样,output layer.
/// </summary>
class GeneratorResNetImpl : public torch::nn::Module {
public:
	GeneratorResNetImpl(std::vector<int> input_shape, int num_residual_blocks);
	torch::Tensor forward(torch::Tensor x);
private:
	torch::nn::Sequential _make_layer(int in_channels, int blocks);
	torch::nn::ReflectionPad2d pad1{ nullptr };
	torch::nn::Conv2d conv1{ nullptr };
	torch::nn::InstanceNorm2d bn1{ nullptr };
	torch::nn::ReLU relu1{ nullptr };
	// down 
	DownSample down1{ nullptr };
	DownSample down2{ nullptr };
	// res
	torch::nn::Sequential res_blocks = torch::nn::Sequential();
	// up
	UpSampleBlock up1{ nullptr };
	UpSampleBlock up2{ nullptr };
	// output layer
	torch::nn::ReflectionPad2d pad2{ nullptr };
	torch::nn::Conv2d conv2{ nullptr };
	torch::nn::Tanh tanh2{ nullptr };
};
TORCH_MODULE(GeneratorResNet);

torch::nn::Sequential GeneratorResNetImpl::_make_layer(int in_channels, int blocks)
{
	torch::nn::Sequential layers;
	for (int i = 0; i < blocks; i++) {
		layers->push_back(ResidualBlock(in_channels));
	}
	return layers;
}
GeneratorResNetImpl::GeneratorResNetImpl(std::vector<int> input_shape, int num_residual_blocks)
{
	int channels = input_shape[0];  // 3
	int out_channels = 64;
	// 1, conv+bn+relu. (256+6-7+2*0)/1+1 = 256
	pad1 = torch::nn::ReflectionPad2d(channels);
	conv1 = torch::nn::Conv2d(torch::nn::Conv2dOptions(channels, out_channels, 7));
	bn1 = torch::nn::InstanceNorm2d(out_channels);
	relu1 = torch::nn::ReLU(true);
	int in_channels = out_channels;

	// 2, Down sampling: 通过conv2d两次下采样,并且double channels
	down1 = DownSample(in_channels, out_channels*2);
	down2 = DownSample(out_channels * 2, out_channels*4);
	in_channels = out_channels * 4;  // 256 = 64*4

	// 3, Residual blocks: keep feature scale and channel unchange.
	res_blocks = _make_layer(in_channels, num_residual_blocks);  // (b,256,64,64)
	
	// 4, Up sampling: up+conv+bn+relu. halve channels and keep feature scale unchange.
	up1 = UpSampleBlock(in_channels, in_channels/2);  // (b,128,128,128)
	up2 = UpSampleBlock(in_channels / 2, in_channels / 4);  // (b,64,256,256)
	in_channels = in_channels / 4;  // 64
	
	// 5, output layer: pad+conv+tanh. change channels and keep feature scale unchange.
	pad2 = torch::nn::ReflectionPad2d(channels);  // 3
	conv2 = torch::nn::Conv2d(torch::nn::Conv2dOptions(in_channels, channels, 7));  // (b,64,256,256)->(b,3,256,256)
	tanh2 = torch::nn::Tanh();

	register_module("generator pad1", pad1);
	register_module("generator conv1", conv1);  // 一定要注册,不然不会使用cuda
	register_module("generator bn1", bn1);
	register_module("generator relu1", relu1);
	register_module("generator down1", down1);
	register_module("generator down2", down2);
	register_module("generator res_blocks", res_blocks);
	register_module("generator up1", up1);
	register_module("generator up2", up2);
	register_module("generator pad2", pad2);
	register_module("generator conv2", conv2);
	register_module("generator tanh2", tanh2);
}
torch::Tensor GeneratorResNetImpl::forward(torch::Tensor x) {  // (b,3,256,256)
	// 1, conv+bn+relu. (256+6-7+2*0)/1+1 = 256
	x = pad1(x);
	x = conv1(x);
	x = bn1(x);
	x = relu1(x);   // (b,64,256,256)

	// 2, Down sampling: 通过conv2d两次下采样,并且double channels
	x = down1(x);  // (b,128,128,128)
	x = down2(x);  // (b,256,64,64)

	// 3, Residual blocks: keep feature scale and channel unchange.
	x = res_blocks->forward(x);  // (b,256,64,64)

	// 4, Up sampling: up+conv+bn+relu. halve channels and keep feature scale unchange.
	x = up1(x);  // (b,128,128,128)
	x = up2(x);  // (b,64,256,256)

	// 5, output layer: pad+conv+tanh. change channels and keep feature scale unchange.
	x = pad2(x);
	x = conv2(x);
	x = tanh2(x);  // (b, 3, 256, 256)
	std::cout << x.sizes() << std::endl;
	return x;
}

3. 判别器

输入的是生成图图片(b,3,256,256),经过5次卷积,输出的是判别分数(b,1,16,16).

3.1 判别器组件

判别器组件是由conv2d+InstanceNorm2d+relu组成. 改变通道和scale.

/// <summary>
/// Conv2d + bn + relu
/// 其中kernel_size设置成4,跟patchGan有关。
/// </summary>
class DiscriminatorBlockImpl : public torch::nn::Module {
public:
	DiscriminatorBlockImpl(int in_channels, int out_channels, bool normalize = true);
	torch::Tensor forward(torch::Tensor x);
private:
	bool normalize = true;
	torch::nn::Conv2d conv{ nullptr };
	torch::nn::InstanceNorm2d bn{ nullptr };
	torch::nn::LeakyReLU relu{ nullptr };
};
TORCH_MODULE(DiscriminatorBlock);
DiscriminatorBlockImpl::DiscriminatorBlockImpl(int in_channels, int out_channels, bool normalize) {
	this->normalize = normalize;
	conv = torch::nn::Conv2d(torch::nn::Conv2dOptions(in_channels, out_channels, 4).stride(2).padding(1));
	if (normalize) bn = torch::nn::InstanceNorm2d(out_channels);
	relu = torch::nn::LeakyReLU(torch::nn::LeakyReLUOptions().negative_slope(0.2).inplace(true));
	
	register_module("DiscriminatorBlock conv", conv);
	if (normalize) register_module("DiscriminatorBlock bn", bn);
	register_module("DiscriminatorBlock relu", relu);
}
torch::Tensor DiscriminatorBlockImpl::forward(torch::Tensor x) {
	x = conv(x);
	if (this->normalize)
		x = bn(x);
	x = relu(x);
	return x;
}

 3. 2 判别器

// (b,3,256,256)->(b,512,16,16)
torch::nn::Sequential _make_discriminator_blocks(int in_channels, int out_channels) {
	torch::nn::Sequential layers;
	layers->push_back(DiscriminatorBlock(in_channels, out_channels, false));
	layers->push_back(DiscriminatorBlock(out_channels, out_channels*2, true));
	layers->push_back(DiscriminatorBlock(out_channels * 2, out_channels * 4, true));
	layers->push_back(DiscriminatorBlock(out_channels * 4, out_channels * 8, true));
	return layers;
}
class DiscriminatorImpl : public torch::nn::Module {
public:
	DiscriminatorImpl(std::vector<int> input_shape);
	torch::Tensor forward(torch::Tensor x);
public:
	std::vector<int> output_shape_hw;
	//std::vector<int> output_shape;
private:
	torch::nn::Sequential discriminator_blocks{ nullptr };
	torch::nn::ZeroPad2d pad{ nullptr };
	torch::nn::Conv2d conv{ nullptr };
};
TORCH_MODULE(Discriminator);
DiscriminatorImpl::DiscriminatorImpl(std::vector<int> input_shape) {
	int channels = input_shape[0], height = input_shape[1], width = input_shape[2];
	// Calculate output shape of image discriminator (PatchGAN)
	this->output_shape_hw = { 1, height / int(pow(2,4)), width / int(pow(2,4)) };  // 外部调用,
	//this->output_shape = std::vector<int>({ 1, height / int(pow(2,4)), width / int(pow(2,4)) });
	// 1, dis blocks
	discriminator_blocks = _make_discriminator_blocks(channels, 64);  // (b,512,16,16)
	// 2, zeropad
	pad = torch::nn::ZeroPad2d(torch::nn::ZeroPad2dOptions({ 1, 0, 1, 0 }));  // left,right,up,down
	// 3, conv
	conv = torch::nn::Conv2d(torch::nn::Conv2dOptions(512, 1, 4).padding(1));

	register_module("Discriminator discriminator_blocks", discriminator_blocks);
	register_module("Discriminator pad", pad);
	register_module("Discriminator conv", conv);
}
torch::Tensor DiscriminatorImpl::forward(torch::Tensor x) {  // (b,3,256,256)
	x = discriminator_blocks->forward(x);  // (b,3,256,256)->(b,512,16,16)
	x = pad(x);  // (b,512,17,17)
	x = conv(x);  // (b,1,16,16)
	std::cout << x.sizes() << std::endl;
	return x;
}

 4. 训练

4.1 输入数据 

real_A和real_B分别是领域A和领域B图片,valid和fake分别是全1和全0矩阵。

real_A和real_B size: (b,3,256,256);

valid和fake size: (b,1,16,16).

// Set model input:
torch::Tensor real_A = batch.data.toType(torch::kF32).to(torch::kCUDA);  // (b,3,256,256)
torch::Tensor real_B = batch.target.toType(torch::kF32).to(torch::kCUDA);  // (b,3,256,256)
torch::Tensor valid = torch::ones({ real_A.size(0), D_A->output_shape_hw.at(0), D_A->output_shape_hw.at(1), D_A->output_shape_hw.at(2) }, torch::kF32).to(torch::kCUDA);  // (32,1,16,16). 
torch::Tensor fake = torch::zeros({ real_A.size(0), D_A->output_shape_hw.at(0), D_A->output_shape_hw.at(1), D_A->output_shape_hw.at(2) }, torch::kF32).to(torch::kCUDA);  // (32,1,16,16). 

4.2 生成器loss函数结构图 

/*
----------------------
	Train Generators
----------------------
*/
// 1, Identity loss: cycGan可加可不加,加上identity loss生成的效果更好。
// 生成器G用来生成y风格图像,那么把y送入G,应该仍然生成y,G(y) = y,只有这样才能保证具有生成y风格的能力。
// 如果不加该loss,那么生成器可能会自主地修改图像的色调,使得整体的颜色产生变化。
torch::Tensor loss_id_A = l1_loss_identity(G_BA(real_A), real_A);  // G_BA(A) = A, 保证生成的A接近A
torch::Tensor loss_id_B = l1_loss_identity(G_AB(real_B), real_B);  // G_AB(B) = B, 保证生成的B接近B
torch::Tensor loss_identity = (loss_id_A + loss_id_B) / 2;

// 2, Gan loss: 让生成的图像更能称之为图像,也就是生成的图像更真实。但它不保证能生成到我们想要的图像。
torch::Tensor fake_B = G_AB(real_A);
torch::Tensor loss_GAN_AB = mse_loss_gan(D_B(fake_B), valid);  // 由A生成B, D_B分数越高越好,D_AB(G_AB(A)) = 1
torch::Tensor fake_A = G_BA(real_B);
torch::Tensor loss_GAN_BA = mse_loss_gan(D_A(fake_A), valid);  // # 由B生成A, D_A分数越高越好,D_BA(G_BA(B)) = 1
torch::Tensor loss_GAN = (loss_GAN_AB + loss_GAN_BA) / 2;

// 3, Cycle loss: 保证生成器的输出图片与输入图片只是风格不同,而内容相同
torch::Tensor loss_cycle_A = l1_loss_cycle(G_BA(fake_B.detach()), real_A);  // G_BA(G_AB(A)) = A
torch::Tensor loss_cycle_B = l1_loss_cycle(G_AB(fake_A), real_B);  // G_BA(G_AB(A)) = A
torch::Tensor loss_cycle = (loss_cycle_A + loss_cycle_B) / 2;

// total g loss: loss_gan + 10*loss_cycle + 5*loss_identity
torch::Tensor loss_G = loss_GAN + lambda_cyc * loss_cycle + lambda_id * loss_identity;
loss_G.backward();

 4.3 判别器loss结构图


待续。。。

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

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

相关文章

【java查漏补缺】网络编程

网络编程实际上就是通过套接字进行连接后进行通信&#xff0c;本质还是程序进行IO操作。 所谓套接字&#xff0c;实际上就是IP地址加上端口号的组合&#xff0c;通过套接字&#xff0c;可以连接到网络中某一台计算机的某一个进程。 下面就是客户端和服务器的简单例子&#xf…

vue3-ElmentPlus封装通用表格-含单元格操作-多选-分页器

Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 **&#x1f431;‍&#x1f409;&#x1f431;‍&#x1f409;恭喜你&#xff0c;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c;求收藏&#xff0c;求评论&#xff0c;求一个大大…

【测试】测试分类

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录测试分类&#xff08;大框架&#xff09;一、按照测试对象划分一&#xff09;可靠性测试二&#xff09;容错性测试三&#xff09;安装卸载测试&#xff08;万能公式中可以加上&#xff09;四&#xff09;内存泄漏测试…

合芯科技携手新享科技联合打造国产化项目管理系统解决方案

北京新享科技有限公司 北京新享科技有限公司&#xff0c;是上海合见工业软件集团控股的子公司。上海合见工业软件集团有限公司是自主创新的高性能工业软件及解决方案提供商&#xff0c;以EDA&#xff08;电子设计自动化&#xff0c;Electronic Design Automation&#xff09;领…

【实际开发03】- dto + vo - 先处理 dto , 后处理 vo ( 通常少一注解 )

目录 0. 建议 : 多用组合 , 少用继承 1. EqualsAndHashCode(callSuper true) - 解决允许调用父类 2. 序列化 ID : private static final long serialVersionUID 1L; 1. serialVersionUID 作用 : 序列化时为了保持版本的兼容性 3. 数据概览 ( 统计 ) : XxxxProfileVO 1.…

CAD常用命令:对象选择过滤器(FILTER)

CAD软件中为了方便绘图&#xff0c;有效地提升绘图效率&#xff0c;提供了很多CAD命令快捷键&#xff0c;而CAD对象选择过滤器作为CAD常见命令之一&#xff0c;在日常的CAD绘图过程中经常能用到&#xff0c;你知道CAD对象选择过滤器怎么用吗&#xff1f;本文小编就来给大家分享…

Qt解析Json数据

目录前言1.下载 jsoncpp 源码2.编译3.JSON数据读写示例4.jsoncpp核心类详解前言 本文主要介绍了使用Qt框架编程时如何解析JSON数据的一种方法。JSON是英文JavaScript Object Notation 的缩写&#xff0c;它是一种轻量级的数据交换格式&#xff0c;具有方便阅读和编写的优点&am…

Jenkins 项目的 gpg: signing failed: Bad passphrase 错误

因为我们项目需要使用 Jenkins 对文件进行签名。但是我们遇到了gpg: signing failed: Bad passphrase错误。原因和解决通常这个问题的原因是 Key 已经配置成功并且已经被命令行找到了。主要原因是你的秘钥密码配置的问题。这个配置有 2 个地方&#xff0c;第一个地方是项目的 P…

2022年度牛奶乳品行业数据:十大热门品牌销量排行榜

当前&#xff0c;随着经济的发展及人民生活水平的提高&#xff0c;牛奶乳品已经日趋成为人们在日常饮食中不可缺少的食物之一&#xff0c;市面上的产品种类也越来越多。并且&#xff0c;随着人们消费习惯的转变&#xff0c;牛奶乳品的消费场景也日益多元化。未来&#xff0c;预…

jdk1.8之函数式接口

l[TOC] 函数式接口概述 jdk1.8 引入了一个核心概念&#xff1a;函数式接口&#xff08;Functional Interface&#xff09;。如果一个接口有且只有一个未实现的方法&#xff0c;那这个接口就称为函数式接口。并且引入了一个新的注解&#xff1a;FunctionalInterface &#xff0…

一、Gradle入门

文章目录一、Gradle入门1.1 Gradle 简介1.2 常见的项目构建工具1.3 Gradle 安装1.3.1 Gradle 安装说明1.3.2 安装 JDK1.3.3 下载并解压到指定目录1.3.4 配置环境变量1.3.5 检测是否安装成功1.4 Gradle 项目目录结构1.5 Gradle 创建第一个项目1.5.1 Gradle 中的常用命令1.5.2 修…

【MySQL进阶教程】视图/存储过程/触发器

前言 本文为 【MySQL进阶教程】视图/存储过程/触发器 相关知识&#xff0c;下边将对视图&#xff0c;存储过程&#xff0c;存储函数&#xff0c;触发器等进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &#x1f449;Java全栈学习路线可参考&#xff1a;【…

MySQL高级【存储函数触发器】

1&#xff1a;存储函数1.1&#xff1a;介绍存储函数是有返回值的存储过程&#xff0c;存储函数的参数只能是IN类型的。具体语法如下&#xff1a; CREATE FUNCTION 存储函数名称 ([ 参数列表 ]) RETURNS type [characteristic ...] BEGIN -- SQL语句 RETURN ...; END ;character…

如何管理存量用户?

存量市场的老客户对于企业来说如同一座金矿&#xff0c;好好运营老客户&#xff0c;可以给企业带来源源不断的新客户&#xff0c;企业所获得的收益远比拉新所获收益要高的多。 前言 存量客户是指某个时间段里原先已有的客户&#xff0c;与新增客户相对应&#xff0c;通俗点说&…

Python开发Web扫描器实战

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是Python开发Web扫描器实战。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未…

机器学习--模型评估、过拟合和欠拟合、模型验证

目录 一、模型评估 模型指标 常见关于分类的指标 准确率 精确率(精确度) 召回率 F1 score PR曲线&#xff1a; ROC AUC 二、过拟合和欠拟合 训练与泛化误差的区别 什么样的情况会导致欠拟合与过拟合&#xff1f; 模型的复杂度&#xff08;能够拟合各种各样函…

分组加密模式 ECB CBC OFB CFB

多个分组加密互相之间如何关联 ECB模式 每个分组之间单独加密&#xff0c;互不关联 2个分组明文一样&#xff0c;结果也一样&#xff0c;那么只需爆破其中1个就可以了 每个分组互不关联&#xff0c;可以分段同时来爆破&#xff0c;不安全 可以通过替换某段密文来达到替换明…

11.Isaac教程--在docker中模拟训练姿势估计模型

在docker中模拟训练姿势估计模型 文章目录在docker中模拟训练姿势估计模型怎么运行的主机设置硬件要求软件要求NGC Docker 注册表设置第一次运行数据集生成配置您的工作区Jupyter 变量设置开始训练添加您自己的 3D 模型故障排除接下来物体检测和 3D 姿态估计在机器人技术中起着…

『精』EditorConfig 小老鼠 跨编辑器 | IDE 保持一致的编码风格

『精』EditorConfig 小老鼠 跨编辑器 | IDE 保持一致的编码风格 文章目录『精』EditorConfig 小老鼠 跨编辑器 | IDE 保持一致的编码风格一、什么是EditorConfig二、文件识别符三、风格属性控制四、不同规则参考1)、简洁通用2)、前端Vue项目3)、前端React项目4)、前端Angular项…

Linux常用命令——nm命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) nm 显示二进制目标文件的符号表 补充说明 nm命令被用于显示二进制目标文件的符号表。 语法 nm(选项)(参数)选项 -A&#xff1a;每个符号前显示文件名&#xff1b; -D&#xff1a;显示动态符号&#xff1b; …