Pytorch手把手实作-Generative Adversarial Network (GAN)

news2025/1/17 14:04:10

文章目录

  • 一、说明
  • 二、GAN的介绍
  • 三、生成器和鉴别器
  • 四、代码实现

一、说明

前言废话免了,会进来看文章内容的只有四种人:1. 只想知道皮毛,GAN在干什么的 2. 想知道细节怎么把GAN训练起来;3. 收藏在收藏夹或是书签当作有看过了;4. 上课上到一定要点点进来。

二、GAN的介绍

GAN属于unsupervised learning。白话一点,GAN是用来生成资料。讲难听一点,GAN被广泛用来造假的。 (但也有正向的)

最近比较知名的影像转换
在这里插入图片描述

AI界知名人士的小孩版本。 (source: https://www.reddit.com/r/MachineLearning/comments/o843t5/d_types_of_machine_learning_papers/)
如果不认识我帮你们对应起来
在这里插入图片描述

我其实有找到其他人对应的图,但我懒得放了。
下面的网址有用StyleGAN: 可以让人变年轻微笑的范例。

我其实有找到其他人对应的图,但我懒得放了。
下面的网址有用StyleGAN: 可以让人变年轻微笑的范例。 https://www.reddit.com/r/MachineLearning/comments/o6wggh/r_finally_actual_real_images_editing_using/

这不是跟抖音内建功能一样,可以换脸(卡通),可以换表情,可以自动上妆,这用到的技术就是GAN相关的,屏除到政治因素,我个人觉得抖音满好玩的。

听说这个蚂蚁呀嘿下架了,我还没玩到><
利用GAN技术让老照片活起来,
在这里插入图片描述

Source: https://imgur.com/i284hKw
以上都是GAN应用最近比较有名的一些影片或是APP等简单介绍。

三、生成器和鉴别器

GAN 生成对抗网络:顾名思义,就是有两个网络架构,分别为「生成」(Generator)和「对抗」(Discriminator)

GAN的概念很简单,我们可以用一部老电影来描述(中文:神鬼交锋,英文: Catch me if you can,英文比较有感):

中文:神鬼交锋,英文: Catch me if you can
一个造假者(李奥纳多)和一个专家(汤姆汉克),造假者需要做假的东西(假支票)出来,让专家去判断真伪,透过专家的判断造假者在不断的增进自己的造假技术,直到专家无法有效的判断真伪。

整个GAN运作的核心概念如下,莱昂纳多就是「生成器(Generator)」,汤姆汉克就是「对抗: 辨别器(Discriminator)」:

在这里插入图片描述

花样看完了,实际上我们将GAN化成简图,如下
在这里插入图片描述

Generator (G) 和 Discriminator (D)

D要判断「真」还是「假」

G生成的数据要呼咙D。

从Random Vector(z,可以为均匀分布或是常态分布)丢入G生成出图片,所以目的就是希望使得G(z)的机率分布接近D的机率分布。

在这里插入图片描述

GAN的核心想法
Discriminator: 希望D(x)真实数据被判给真实的机率期望值最大(接近1)
在这里插入图片描述

Discriminator: 希望D(G(z))假资料被判给真实的机率期望值最小(接近0)

在这里插入图片描述

Generator -> Discriminator: 因为要乎巄D,所以在Generator阶段,希望D(G(z))假资料被判给真实的机率期望值最大(接近1)
在这里插入图片描述

Objective Function of GAN:
在这里插入图片描述

看到这边应该很有感才对,不管是在公式或是算法上

实际上GAN的坑很多,光是Generator和Discriminator怎么设计就是个坑了。

后面范例以DCGAN的模型要设计过Generator才有办法Upsample到MNIST的大小(28*28)。
Generator参数变化不要一次更新太大,通常可以更新几次D后再更新G。 (MNIST范例很简单,所以可以不用)
Learning rate不要设定太大。 如果大家有看过其他人范例大部分都设定为0.0002,其实这样的设定有文献出处Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks
在这里插入图片描述

以上是很简单的GAN理论(有错请鞭小力一点,不要太凶)介绍。

Pytorch手把手进行DCGAN实现,以MNIST数据库为例
在这里插入图片描述
这张图的来源我忘了(应该是DCGAN的论文吧),但这些文章没有营利,应该没有触法吧。

四、代码实现

  1. 先import 模块吧。
# -*- coding: utf-8 -*-
import time
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import transforms
import numpy as np
import matplotlib.pyplot as plt
import torchvision.utils as vutils
import PIL.Image as Image

Generator

因为我的random vector(z)是采用 latents x 1 x 1 (latents代表z的维度数)

DCGAN是采用ConvTranspose2d进行上采样的进行,也就是让图变大张。

MNIST图片为28 x 28,一般上采样通常是固定放大1倍。

1 x 1 → 上采样 → 2 x 2 → 上采样 → 4 x 4 → 上采样 → 8 x 8 → 上采样 → 16 x 16 → 上采样 → 32 x 32

所以不会变成28 x 28。

所以我利用ConvTranspose2d的stride和pad的设计,让上采样可以非1倍放大,细节请看代码,我每一层输出的大小有写在备注。
1 x 1 → ConvTranspose2d → 2 x 2 → ConvTranspose2d → 3 x 3 → ConvTranspose2d → 6 x 6 → ConvTranspose2d → 7 x 7 → ConvTranspose2d → 14 x 14 → ConvTranspose2d → 28 x 28

Discriminator
这边就没什么特别注意,就是建立一个分类CNN而已,所以我建立一个5层CNN+1层FC可以看下面Discriminator的定义。

我们先订一些卷积模块(CBR, CBLR, TCBR),然后依据上述建立「Generator」和「Discriminator」。

# custom weights initialization called on netG and netD
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        nn.init.normal_(m.weight.data, 0.0, 0.02)
    elif classname.find('BatchNorm') != -1:
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)
           
class CBR(nn.Sequential):
    def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1):
        padding = (kernel_size - 1) // 2
        norm_layer = nn.BatchNorm2d
        super(CBR, self).__init__(
            nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),
            norm_layer(out_planes),
            nn.ReLU(inplace=True),
        )
class CBLR(nn.Sequential):
    def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1):
        padding = (kernel_size - 1) // 2
        norm_layer = nn.BatchNorm2d
        super(CBLR, self).__init__(
            nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),
            norm_layer(out_planes),
            nn.ReLU(inplace=True),
        )
class TCBR(nn.Sequential):
    def __init__(self, in_planes, out_planes, kernel_size=4, stride=2, padding=1):
        padding = (kernel_size - 1) // 2
        norm_layer = nn.BatchNorm2d
        super(TCBR, self).__init__(
            nn.ConvTranspose2d(in_planes, out_planes, kernel_size, stride, padding, bias=False),
            norm_layer(out_planes),
            nn.ReLU(inplace=True),
        )                    
                                                 
class Generator(nn.Module):
    def __init__(self, latents):
        super(Generator, self).__init__()
        
        self.layer1= nn.Sequential(
            # input is random_Z,  state size. latents x 1 x 1 
            # going into a convolution
            TCBR(latents, 256, 4, 2, 1),  # state size. 256 x 2 x 2
            CBR(256, 128, 3, 1)
        )
        
        self.layer2= nn.Sequential(
            TCBR(128, 256, 4, 1, 0), # state size. 256 x 3 x 3
            TCBR(256, 256, 4, 2, 1), # state size. 256 x 6 x 6
            
        )
        self.layer3= nn.Sequential(
            TCBR(256, 128, 4, 1, 0), # state size. 256 x 7 x 7
            TCBR(128, 128, 4, 2, 1),  # state size. 256 x 14 x 14
            CBR(128, 128, 3, 1)
            # state size. 256 x 6 x 6

        )
        self.layer4= nn.Sequential(
            TCBR(128, 64, 4, 2, 1), # state size. 64 x 28 x 28
            CBR(64, 64, 3, 1),
            CBR(64, 64, 3, 1),
            nn.Conv2d(64, 1, 3, 1, 1), # state size. 1 x 28 x 28
            nn.Tanh()
        )
        
    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        return x
    

class Discriminator(nn.Module):
    def __init__(self,):
        super(Discriminator, self).__init__()
        self.conv = nn.Sequential(
            CBLR(1, 32, 3, 2), # b*32*14*14
            CBLR(32, 64, 3, 1), # b*64*14*14
            CBLR(64, 128, 3, 2), # b*128*7*7
            CBLR(128, 128, 3, 2), # b*32*3*3
            CBLR(128, 64, 3, 2), # b*32*1*1
        )        
        self.fc = nn.Linear(64,2)

    def forward(self, x):
        x = self.conv(x)
        x = nn.functional.adaptive_avg_pool2d(x, 1).reshape(x.shape[0], -1)
        ft = x
        output = self.fc(x)
        return output

这边开始我们宣告一些pytorch训练需要的一些组件,例如:GPU的使用、Generator 和「Discriminator」的optimizer、学习时候学习率的lr_scheduler和MNIST的dataloader等。

# from torchvision.utils import save_image

flag_gpu = 1
# Number of workers for dataloader
workers = 0
# Batch size during training
batch_size = 100
# Number of training epochs
epochs = 20
# Learning rate for optimizers
lr = 0.0002

# GPU
device = 'cuda:0' if (torch.cuda.is_available() & flag_gpu) else 'cpu'
print('GPU State:', device)
# Model
latent_dim = 10
G = Generator(latents=latent_dim).to(device)
D = Discriminator().to(device)
G.apply(weights_init)
D.apply(weights_init)

# Settings
g_optimizer = optim.Adam(G.parameters(), lr=lr, betas=(0.5, 0.999))
d_optimizer = optim.Adam(D.parameters(), lr=lr, betas=(0.5, 0.999))

g_scheduler = torch.optim.lr_scheduler.StepLR(g_optimizer, step_size=5, gamma=0.5)
d_scheduler = torch.optim.lr_scheduler.StepLR(d_optimizer, step_size=5, gamma=0.5)

# Load data
train_set = datasets.MNIST('./dataset', train=True, download=False, transform=transforms.ToTensor())
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=workers)

Generator的更新
在这里插入图片描述
Discriminator的更新
在这里插入图片描述

等下程序在执行,模型的Update (loss)需要符合上述的执行,

def show_images(images, epoch):
    sqrtn = int(np.ceil(np.sqrt(images.shape[0])))
    plt.figure()
    for index, image in enumerate(images):
        plt.subplot(sqrtn, sqrtn, index+1)
        plt.imshow(image.reshape(28, 28))
    plt.savefig("Generator_epoch_{}.png".format(epoch))
# Train
adversarial_loss = torch.nn.CrossEntropyLoss().to(device)
# adversarial_loss = torch.nn.BCELoss().to(device)

G.train()
D.train()
loss_g, loss_d = [],[]
start_time= time.time()
for epoch in range(epochs):
    epoch += 1
    total_loss_g,total_loss_d=0,0
    count_d=0
    for i_iter, (images, label) in enumerate(train_loader):
        i_iter += 1

        # -----------------
        #  Train Generator
        # -----------------
        g_optimizer.zero_grad()
        # Sample noise as generator input
        noise = torch.randn(images.shape[0], latent_dim, 1, 1)
        noise = noise.to(device)
        
        # 因為Generator希望生成出來的圖片跟真的一樣,所以fake_label標註用 1
        fake_label = torch.ones(images.shape[0], dtype=torch.long).to(device) # notice: label = 1

        # Generate a batch of images
        fake_inputs = G(noise)
        fake_outputs = D(fake_inputs)
        
        # Loss measures generator's ability to fool the discriminator
        loss_g_value = adversarial_loss(fake_outputs, fake_label)
        loss_g_value.backward()
        g_optimizer.step()
        total_loss_g+=loss_g_value
        loss_g.append(loss_g_value) 
        
        # ---------------------
        #  Train Discriminator
        # ---------------------
        # Zero the parameter gradients
        d_optimizer.zero_grad()
        # Measure discriminator's ability to classify real from generated samples
        # 因為Discriminator希望判斷哪些是真的那些是生成的,所以real_label資料標註用 1,fake_label標註用0。
        real_inputs = images.to(device) 
        real_label = torch.ones(real_inputs.shape[0], dtype=torch.long).to(device)
        fake_label = torch.zeros(fake_inputs.shape[0], dtype=torch.long).to(device)
#       learning by Discriminator
        real_loss = adversarial_loss(D(real_inputs),real_label)
        fake_loss = adversarial_loss(D(fake_inputs.detach()),fake_label)
        loss_d_value = (real_loss + fake_loss) / 2
        loss_d_value.backward()
        d_optimizer.step()
        total_loss_d+=loss_d_value
        loss_d.append(loss_d_value)  
        
    total_loss_g/=len(train_loader)
    total_loss_d/=len(train_loader)         
    g_scheduler.step()
    d_scheduler.step()
    print('[Epoch: {}/{}] D_loss: {:.3f} G_loss: {:.3f}'.format(epoch, epochs, total_loss_d.item(), total_loss_g.item()))
    if epoch % 1 == 0:
        print('Generated images for epoch: {}'.format(epoch))
        imgs_numpy = fake_inputs.data.cpu().numpy()
        show_images(imgs_numpy[:16],epoch)
        plt.show()

torch.save(G, 'DCGAN_Generator.pth')
torch.save(D, 'DCGAN_Discriminator.pth')
print('Model saved.')

print('Training Finished.')
print('Cost Time: {}s'.format(time.time()-start_time))

执行后的结果

[Epoch: 1/20] D_loss: 0.373 G_loss: 1.240
Generated images for epoch: 1
[Epoch: 2/20] D_loss: 0.157 G_loss: 2.229
Generated images for epoch: 2
[Epoch: 3/20] D_loss: 0.145 G_loss: 2.603
Generated images for epoch: 3
[Epoch: 4/20] D_loss: 0.354 G_loss: 1.390
Generated images for epoch: 4
[Epoch: 5/20] D_loss: 0.447 G_loss: 1.162
Generated images for epoch: 5
[Epoch: 6/20] D_loss: 0.472 G_loss: 1.064
Generated images for epoch: 6
[Epoch: 7/20] D_loss: 0.473 G_loss: 1.062
Generated images for epoch: 7
[Epoch: 8/20] D_loss: 0.444 G_loss: 1.131
Generated images for epoch: 8
[Epoch: 9/20] D_loss: 0.437 G_loss: 1.152
Generated images for epoch: 9
[Epoch: 10/20] D_loss: 0.460 G_loss: 1.115
Generated images for epoch: 10
[Epoch: 11/20] D_loss: 0.535 G_loss: 0.956
Generated images for epoch: 11
[Epoch: 12/20] D_loss: 0.491 G_loss: 1.026
Generated images for epoch: 12
[Epoch: 13/20] D_loss: 0.509 G_loss: 0.994
Generated images for epoch: 13
[Epoch: 14/20] D_loss: 0.502 G_loss: 1.013
Generated images for epoch: 14
[Epoch: 15/20] D_loss: 0.503 G_loss: 1.011
Generated images for epoch: 15
[Epoch: 16/20] D_loss: 0.570 G_loss: 0.900
Generated images for epoch: 16
[Epoch: 17/20] D_loss: 0.571 G_loss: 0.899
Generated images for epoch: 17
[Epoch: 18/20] D_loss: 0.582 G_loss: 0.888
Generated images for epoch: 18
[Epoch: 19/20] D_loss: 0.574 G_loss: 0.887
Generated images for epoch: 19
[Epoch: 20/20] D_loss: 0.516 G_loss: 0.982
Generated images for epoch: 20
Model saved.
Training Finished.
Cost Time: 715.010427236557s

不同epoch训练出来生成的结果图。

在这里插入图片描述

plt.plot(loss_g)
plt.plot(loss_d,‘r’)
plt.legend([‘G’,‘D’])
plt.show()
在这里插入图片描述

Generator 和Discriminator在每一次更新的loss变化。
收敛了 ,可以进行生成测试。

Generator测试

import torch
def show_images(images):
    sqrtn = int(np.ceil(np.sqrt(images.shape[0])))
    plt.figure()
    for index, image in enumerate(images):
        plt.subplot(sqrtn, sqrtn, index+1)
        plt.imshow(image.reshape(28, 28))
    plt.show()

flag_gpu = 1
device = 'cuda:0' if (torch.cuda.is_available() & flag_gpu) else 'cpu'
print(device)

G = torch.load('DCGAN_Generator.pth', map_location=device)
  
latent_dim = 10

## Exp:1
noise = torch.randn(20, latent_dim, 1, 1)
noise = noise.to(device)
        
# Generate a batch of images
fake_inputs = G(noise)
        
imgs_numpy = fake_inputs.data.cpu().numpy()
show_images(imgs_numpy[:16])

## Exp:2
noise = torch.randn(20, latent_dim, 1, 1) *-10000
noise = noise.to(device)
        
# Generate a batch of images
fake_inputs = G(noise)
        
imgs_numpy = fake_inputs.data.cpu().numpy()
show_images(imgs_numpy[:16])


## Exp:3
noise = torch.randn(20, latent_dim, 1, 1) *50000
noise = noise.to(device)
        
# Generate a batch of images
fake_inputs = G(noise)
        
imgs_numpy = fake_inputs.data.cpu().numpy()
show_images(imgs_numpy[:16])
  • 实验一: random vector(z)是采用 latents产生范围normal(0,1),大概范围是-3~3之间,生成的图片

在这里插入图片描述
实验一

  • 实验二: random vector(z)是采用 latents产生范围normal(0,1)*-10000,大概范围是-30000~30000之间,生成的图片

在这里插入图片描述
实验二

  • 实验三: random vector(z)是采用 latents产生范围normal(0,1)* 50000,大概范围是-50000~50000之间,生成的图片

在这里插入图片描述
实验三
大家可以和Pytorch手把手实作-AutoEncoder这篇比较,这个random vector(z)在GAN好像比较不会影响结果,但可能是生成结构的关系,在图片生成的过程中已经将input的random vector(z)正规化了,所以在生成的时候就不影响,但实际上是怎么避掉这样的影响我就没有去深入研究。

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

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

相关文章

HTTP协议分析实验:通过一次下载任务抓包分析

HTTP协议分析 问&#xff1a;HTTP是干啥用的&#xff1f; 最简单通俗的解释&#xff1a;HTTP 是客户端浏览器或其他程序与Web服务器之间的应用层通信协议。 在Internet上的Web服务器上存放的都是超文本信息&#xff0c;客户机需要通过HTTP协议传输所要访问的超文本信息。 一、…

Adobe XD最新版号查询,如何使用?

Adobe XD是Adobe家推出的基于矢量的原型设计合作工具&#xff0c;被业界视为应对Sketch的“对抗”产品。Adobe XD不同于Sketch的系统限制&#xff0c;灵活性比较高&#xff0c;Windows和Mac都可以使用。自2017年推出以来&#xff0c;Adobe XD版经历了多次更新&#xff0c;这篇文…

【Python报错】已解决SyntaxError: can’t assign to function call

成功解决“SyntaxError: can’t assign to function call”错误的全面指南 在Python编程中&#xff0c;语法错误&#xff08;SyntaxError&#xff09;是初学者和经验丰富的开发者都可能遇到的问题。其中&#xff0c;“SyntaxError: can’t assign to function call”这个错误常…

IM即时通信技术

本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl IM&#xff0c;即Instant Messaging&#xff0c;是指即时通讯技术&#xff0c;它允许用户通过互联网实时交换文本、语音、视频、文件等多种形式的信息。这种技术打破了传统通信方式的时…

Spring AOP(实现,动态原理)详解版

Spring AOP 1.什么是AOP&#xff1f;1.1引入AOP依赖1.2编写AOP程序 2.Spring AOP核⼼概念2.1 切点(Pointcut)2.2连接点(Join Point)2.3通知(Advice)2.4 切⾯(Aspect) 3.通知类型3.1顺序3.2切⾯优先级 Order3.3 ⾃定义注解 MyAspect 4. Spring AOP 原理5 动态代理怎么实现5.1 JD…

大学数字媒体艺术设计网页设计试题及答案,分享几个实用搜题和学习工具 #媒体#职场发展

现在读书可不像小时候&#xff0c;以前想要校对试题答案&#xff0c;都得找到对应的纸质版答案查看&#xff0c;而且有的还只有答案&#xff0c;没有解析&#xff0c;无法弄清楚答案的由来。但是现在不一样了&#xff0c;现在我们可以通过搜题软件&#xff0c;寻找试题的答案&a…

性能优化随笔(一)

在软件开发过程中&#xff0c;一般要先实现功能方面的需求&#xff0c;功能方面的需求开发完毕之后&#xff0c;往往会考虑性能方面的优化。在业务发展的初期&#xff0c;性能往往能满足使用的需求&#xff0c;这时性能优化不是必不可少的。随着业务的发展&#xff0c;软件复杂…

MySQL提权之UDF提权

1、前言 最近遇到udf提权&#xff0c;几经周折终于搞懂了。感觉挺有意思的&#xff0c;渗透思路一下子就被打开了。 2、什么是udf提权 udf 全称为user defined function&#xff0c;意思是用户自定义函数。用户可以对数据库所使用的函数进行一个扩展&#xff08;windows利用…

Vue2工程化

本节目标 工程化开发项目运行流程组件化组件注册自定义创建项目 工程化开发 基于构建工具的环境开发Vue Webpack的缺点 webpack的配置并不简单基础的配置雷同各公司缺乏统一标准 Vue CLI Vue CLI是Vue官方提供的一个全局命令工具帮助我们快速创建标准化的开发环境( 集成了w…

【TB作品】MSP430F5529,单片机,电子秒表,秒表

硬件 MSP430F5529开发板7针0.96寸OLED /* OLED引脚分配 绿色板子DO(SCLK)------P4.3D1(DATA)------P4.0RES-----------P3.7DC------------P8.2CS------------P8.1 */ 程序功能 该程序是一个用C语言编写的&#xff0c;用于msp430f5529微控制器上的简单电子秒表应用。它使用…

2024年如何通过完善的工程化,从0到1手把手建立个人 react 组件库

本文聚焦于快速创建并部署个人的组件库&#xff0c;方便平时开发中将通用的组件抽出&#xff0c;也可用于简历上展示个人的组件成果~ 组件库体验地址&#xff1a;components-library 关于以上内容&#xff0c;你是否好奇如何实现的&#xff0c;对于大多数项目&#xff0c;诸如…

USB Type-C 和 USB供电数据和电源角色

USB Type-C 连接器生态系统随着现代平台和设备需求的变化而不断发展。 USB Type-C 连接器生态系统可满足现代平台和设备不断变化的需求&#xff0c;并且符合更小、更薄且更轻便的外形设计趋势。此外&#xff0c;针对 Type-C 连接器修改 USB PD 有助于满足高耗电应用的需求。 …

容声冰箱正式发布主动除菌净味白皮书,守护家人饮食健康

近日&#xff0c;由中国家用电器研究院指导、全国家用电器工业信息中心和容声冰箱联合编制的《冰箱主动除菌净味技术发展白皮书》&#xff08;下称《白皮书》&#xff09;正式发布。 《白皮书》指出&#xff0c;容声将IDP主动除菌技术应用到冰箱冷冻、冷藏区域&#xff0c;实现…

百华鞋业祝莘莘学子旗开得胜,一举夺魁

在知识的海洋中&#xff0c; 有一群人以笔为剑&#xff0c; 在漫长的岁月里不断磨砺&#xff0c; 只为迎接那场人生的重要战役——高考。 高考&#xff0c; 是学子们十几年寒窗苦读的见证&#xff0c; 是他们用奋斗书写青春考卷的舞台。 在这个舞台上&#xff0c; 他们将…

硕思闪客精灵(shankejingling)软件最新版下载及详细安装教程

闪客精灵&#xff08;Sothink SWF Decompiler&#xff09;是一款先进的SWF反编译软件&#xff0c;它不但能捕捉、反编译、查看和提取Shock Wave Flash影片&#xff08;.swf和.exe格式文件&#xff09;&#xff0c;而且可以将SWF格式文件转化为FLA格式文件。它能反编译Flash的所…

YOLOv10开源,高效轻量实时端到端目标检测新标准,速度提升46%

前言 实时目标检测在自动驾驶、机器人导航、物体追踪等领域应用广泛&#xff0c;近年来&#xff0c;YOLO 系列模型凭借其高效的性能和实时性&#xff0c;成为了该领域的主流方法。但传统的 YOLO 模型通常采用非极大值抑制 (NMS) 进行后处理&#xff0c;这会增加推理延迟&#…

k8s挂载配置文件(通过ConfigMap方式)

一、ConfigMap简介 K8s中的ConfigMap是一种用于存储配置数据的API对象&#xff0c;属于Kubernetes中的核心对象。它用于将应用程序的配置信息与容器镜像分离&#xff0c;以便在不重新构建镜像的情况下进行配置的修改和更新。ConfigMap可以存储键值对、文本文件或者以特定格式组…

Selenium with Python Behave(BDD)

一、简介 Python语言的行为驱动开发&#xff0c;Behavior-driven development&#xff0c;简称BDD. "Behavior-driven development (or BDD) is an agile software development technique that encourages collaboration between developers, QA and non-technical or bu…

Java锁的四种状态(无锁、偏向级锁、轻量级锁、重量级锁)

介绍 首先&#xff0c;我们需要明确一点&#xff1a;偏向级锁、轻量级锁、重量级锁只针对synchronized 锁的状态总共有四种&#xff0c;级别由低到高依次为&#xff1a;无锁、偏向锁、轻量级锁、重量级锁。 这四种锁状态分别代表什么&#xff0c;为什么会有锁升级&#xff…