深度学习落地实战:基于GAN(生成对抗网络)生成图片

 前言

大家好,我是机长

本专栏将持续收集整理市场上深度学习的相关项目,旨在为准备从事深度学习工作或相关科研活动的伙伴,储备、提升更多的实际开发经验,每个项目实例都可作为实际开发项目写入简历,且都附带完整的代码与数据集。可通过百度云盘进行获取,实现开箱即用

正在跟新中~

项目背景

(基于GAN(生成对抗网络)生成动漫人物图像)

在数字艺术与创意产业蓬勃发展的今天,动漫文化以其独特的视觉风格、丰富的故事情节和广泛的受众基础,成为了全球流行文化的重要组成部分。动漫人物作为这一领域的核心元素,其设计既需要艺术创造力,也依赖于高度精细化的图像处理技术。然而,传统的手绘或基于软件的动漫人物创作过程往往耗时耗力,难以快速响应市场变化和个性化需求。

在此背景下,生成对抗网络(Generative Adversarial Networks, GANs)作为一种前沿的深度学习技术,展现出了在生成高质量、多样化动漫人物图像方面的巨大潜力。GANs通过构建两个相互竞争的网络——生成器(Generator)和判别器(Discriminator),在不断地对抗学习中优化生成模型,从而能够生成以假乱真的图像数据。这一技术不仅极大地提高了图像生成的效率,还赋予了创作者前所未有的创作自由度,使得动漫人物的设计与生成过程更加智能化、自动化。

本项目旨在利用GANs技术,开发一套能够自动生成动漫人物图像的系统。该系统将通过分析大量动漫人物图像数据,学习其风格特征、色彩搭配、人物比例等关键要素,进而生成具有鲜明动漫风格、高度个性化的新角色图像。项目的成功实施,不仅有望为动漫产业带来全新的创作模式和高效的生产流程,还能够激发更多创意灵感,推动动漫文化的多元化发展。同时,该项目也将为GANs技术在其他视觉艺术领域的应用提供宝贵经验和参考,进一步拓展深度学习技术在创意产业中的应用边界。

项目运行环境

  • 平台:windows 10
  • 语言环境:python 3.8
  • 编辑器:PyCharm
  • PyThorch版本:1.8

1.创建并跳转到虚拟环境

python -m venv myenvmyenv\Scripts\activate.bat

2. 虚拟环境pip命令安装其他工具包

pip install torch torchvision torchaudio

注:此处只示范安装pytorch,其他工具包安装类似,可通过运行代码查看所确实包提示进行安装

3.pycharm 运行环境配置

进入pytcharm =》点击file =》点击settings=》点击Project:...=》点击 Python Interpreter,进入如下界面

点击add =》点击Existing environment  =》 点击 ... =》选择第一步1创建虚拟环境目录myenv\Scripts\下的python.exe文件点击ok完成环境配置

数据集介绍

数据集大小由63632个高质量动画人脸数据组成

                

训练数据获取:

私信博主获取

GAN网络介绍

U-Net网络的以其独特的U型结构著称,这种结构由编码器(Encoder)和解码器(Decoder)两大部分组成,非常适合于医学图像分割等任务。下面我将进一步解释您提到的关键点,并补充一些细节。

GAN网络,全称为生成对抗网络(Generative Adversarial Networks),是近年来人工智能领域的一项重要突破,由Ian Goodfellow等人在2014年提出。GAN网络通过两个相互对抗的神经网络——生成器(Generator)和判别器(Discriminator)来实现对复杂数据的生成和模拟。

  • 生成器:负责接收随机噪声作为输入,生成看似真实的样本,目标是欺骗判别器,使其无法区分生成样本和真实样本。
  • 判别器:接收一个样本(可能是真实样本或生成样本)作为输入,输出该样本为真实样本的概率,目标是准确区分真实样本和生成样本。

GAN网络的训练过程是一个“博弈”过程,生成器和判别器交替训练,不断对抗以提升各自性能。随着训练的进行,生成器生成的样本质量逐渐提高,最终能够生成高质量的样本,而判别器的判断能力也达到最佳。

GAN网络在多个领域具有广泛应用,如图像生成、数据增强、图像修复、风格迁移等。此外,基于GAN网络还衍生出了许多变体,如DCGAN、CycleGAN和StyleGAN等,这些变体进一步拓展了GAN网络的应用范围和性能。

综上所述,GAN网络作为一种强大的生成模型,通过生成器和判别器的对抗训练,为数据生成和模拟提供了全新的方法和思路。

定义配置类(参数)

为了清晰的定义各个参数,我们定义一个配置类,里面存储相关参数,下文可以直接用config类调用相关的变量。

class Config(object):data_path = 'data/'image_size = 96batch_size = 32epochs = 200lr1 = 2e-3lr2 = 2e-4beta1 = 0.5gpu = Falsedevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备nz = 100ngf = 64ndf = 64save_path = './images'generator_path = './generator.pkl' # 模型保存路径discriminator_path = './discriminator.pkl' # 模型保存路径gen_img = 'result.png'gen_num = 64gen_search_num = 5000gen_mean = 0gen_std = 1config = Config()

定义生成器Generator

        生成器在GAN(生成对抗网络)中的输入通常是一个低维的随机噪声向量,这里假设为100维的高斯噪声。这个噪声向量可以被视为一个包含100个随机值的“种子”,这些值共同定义了生成图像的基本属性和特征,虽然这些值本身并不直接对应于图像中的像素或特征图。

然而,为了形象地说明生成过程,我们可以将这个100维的噪声向量视为一个初始的、高度压缩的“特征图”,尽管在传统意义上,特征图通常指的是在卷积神经网络中经过卷积层处理后的图像表示,具有空间维度(如高度、宽度)和深度(如通道数)。但在这里,我们可以将噪声向量看作是一个极简的、未展开的特征表示。

接下来,生成器利用这个“特征图”(即噪声向量)作为起点,通过一系列的网络层,特别是转置卷积层(也称为反卷积层或分数步长卷积层),来逐步放大这个初始表示,最终生成一张指定大小的图片。

转置卷积并不是标准卷积的直接逆操作,因为卷积操作(尤其是带有步长的卷积)在降低空间维度的同时会丢失信息,这些信息在转置卷积过程中无法完全恢复。但转置卷积通过特定的操作(如插入零值、使用转置的卷积核矩阵进行乘法等)来实现空间维度的增加,从而允许生成器从低维的噪声向量构建出高维的图像数据。

因此,通过转置卷积层,生成器能够逐步将初始的噪声向量“展开”成一张逐渐增大并最终达到指定大小的图像,这个过程中,网络学习到了如何将噪声映射到图像空间,从而生成逼真的图像。

class Generator(nn.Module):def __init__(self, config):super().__init__()ngf = config.ngfself.model = nn.Sequential(nn.ConvTranspose2d(config.nz, ngf * 8, 4, 1, 0),nn.BatchNorm2d(ngf * 8),nn.ReLU(True),nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1),nn.BatchNorm2d(ngf * 4),nn.ReLU(True),nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1),nn.BatchNorm2d(ngf * 2),nn.ReLU(True),nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1),nn.BatchNorm2d(ngf),nn.ReLU(True),nn.ConvTranspose2d(ngf, 3, 5, 3, 1),nn.Tanh())def forward(self, x):output = self.model(x)return output

定义判别器Discriminator

判别器在GAN中是一个二分类网络,其输入是图片。通过一系列卷积操作提取特征,形成一维特征向量,最后进行分类。与生成器不同,判别器的卷积操作是正向的,用于特征提取。两者操作虽在结构上相似但目的相反,且关键差异在于激活函数选择和最终层的分类目标:生成器追求生成逼真图像,而判别器则力求准确区分真实与生成图像。

class Discriminator(nn.Module):def __init__(self, config):super().__init__()ndf = config.ndfself.model = nn.Sequential(nn.Conv2d(3, ndf, 5, 3, 1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf, ndf * 2, 4, 2, 1),nn.BatchNorm2d(ndf * 2),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1),nn.BatchNorm2d(ndf * 4),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1),nn.BatchNorm2d(ndf * 8),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf * 8, 1, 4, 1, 0))def forward(self, x):output = self.model(x)return output.view(-1)

定义模型、优化器及噪声

generator = Generator(config)
discriminator = Discriminator(config)optimizer_generator = torch.optim.Adam(generator.parameters(), config.lr1, betas=(config.beta1, 0.999))
optimizer_discriminator = torch.optim.Adam(discriminator.parameters(), config.lr2, betas=(config.beta1, 0.999))true_labels = torch.ones(config.batch_size)
fake_labels = torch.zeros(config.batch_size)
fix_noises = torch.randn(config.batch_size, config.nz, 1, 1)
noises = torch.randn(config.batch_size, config.nz, 1, 1)

处理数据集

为了能够训练数据,这里我们利用pytorch自带的lmageFolder形成训练集,我们只需要把所有需要训练的图像放在指定目录下即可,然后利用生成的训练集形成迭代器方便后面进行训练。

# 1.数据转换
data_transform = transforms.Compose([transforms.Resize(config.image_size),transforms.CenterCrop(config.image_size),transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(config.data_path),transform=data_transform)# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,config.batch_size,True,drop_last=True)print('using {} images for training.'.format(len(train_dataset)))

模型训练

GAN训练步骤简述:

(1) 训练判别器:固定生成器,调整判别器参数,使其能更准确地分类真实图像为真、生成图像为假。

(2) 训练生成器:固定判别器,调整生成器参数,生成更逼真的图像以欺骗判别器,提高生成图像被判别为真的概率。

(3) 交替循环:重复上述两个步骤,不断优化生成器和判别器,直至达到均衡状态,此时生成器能够生成难以区分的图像。

for epoch in range(config.epochs):for ii, (img, _) in tqdm(enumerate(train_loader)):real_img = img.to(config.device)if ii % 2 == 0:optimizer_discriminator.zero_grad()r_preds = discriminator(real_img)noises.data.copy_(torch.randn(config.batch_size, config.nz, 1, 1))fake_img = generator(noises).detach()f_preds = discriminator(fake_img)r_f_diff = (r_preds - f_preds.mean()).clamp(max=1)f_r_diff = (f_preds - r_preds.mean()).clamp(min=-1)loss_d_real = (1 - r_f_diff).mean()loss_d_fake = (1 + f_r_diff).mean()loss_d = loss_d_real + loss_d_fakeloss_d.backward()optimizer_discriminator.step()else:optimizer_generator.zero_grad()noises.data.copy_(torch.randn(config.batch_size, config.nz, 1, 1))fake_img = generator(noises)f_preds = discriminator(fake_img)r_preds = discriminator(real_img)r_f_diff = r_preds - torch.mean(f_preds)f_r_diff = f_preds - torch.mean(r_preds)loss_g = torch.mean(F.relu(1 + r_f_diff)) + torch.mean(F.relu(1 - f_r_diff))loss_g.backward()optimizer_generator.step()if epoch == config.epochs - 1:# 保存模型torch.save(discriminator.state_dict(), config.discriminator_path)torch.save(generator.state_dict(), config.generator_path)print('Finished Training')

生成图像

下面代码是用加载训练好的模型生成图像的

generator = Generator(config)
discriminator = Discriminator(config)noises = torch.randn(config.gen_search_num, config.nz, 1, 1).normal_(config.gen_mean, config.gen_std)
noises = noises.to(config.device)generator.load_state_dict(torch.load(config.generator_path, map_location='cpu'))
discriminator.load_state_dict(torch.load(config.discriminator_path, map_location='cpu'))
generator.to(config.device)
discriminator.to(config.device)fake_img = generator(noises)
scores = discriminator(fake_img).detach()indexs = scores.topk(config.gen_num)[1]
result = []
for ii in indexs:result.append(fake_img.data[ii])torchvision.utils.save_image(torch.stack(result), config.gen_img, normalize=True, value_range=(-1, 1))

完整可运行代码

import math
import pickle
import osimport numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
from keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from torch.utils.data import TensorDataset
import torchvision
from torchvision import transforms, datasets
from torch import optim
from torchnet import meter
from tqdm import tqdm
from PIL import Imageimport matplotlib.pyplot as pltclass Config(object):data_path = 'data/'image_size = 96batch_size = 32epochs = 200lr1 = 2e-3lr2 = 2e-4beta1 = 0.5gpu = Falsedevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备nz = 100ngf = 64ndf = 64save_path = './images'generator_path = './generator.pkl' # 模型保存路径discriminator_path = './discriminator.pkl' # 模型保存路径gen_img = 'result.png'gen_num = 64gen_search_num = 5000gen_mean = 0gen_std = 1config = Config()# 1.数据转换
data_transform = transforms.Compose([transforms.Resize(config.image_size),transforms.CenterCrop(config.image_size),transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(config.data_path),transform=data_transform)# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,config.batch_size,True,drop_last=True)print('using {} images for training.'.format(len(train_dataset)))class Generator(nn.Module):def __init__(self, config):super().__init__()ngf = config.ngfself.model = nn.Sequential(nn.ConvTranspose2d(config.nz, ngf * 8, 4, 1, 0),nn.BatchNorm2d(ngf * 8),nn.ReLU(True),nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1),nn.BatchNorm2d(ngf * 4),nn.ReLU(True),nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1),nn.BatchNorm2d(ngf * 2),nn.ReLU(True),nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1),nn.BatchNorm2d(ngf),nn.ReLU(True),nn.ConvTranspose2d(ngf, 3, 5, 3, 1),nn.Tanh())def forward(self, x):output = self.model(x)return outputclass Discriminator(nn.Module):def __init__(self, config):super().__init__()ndf = config.ndfself.model = nn.Sequential(nn.Conv2d(3, ndf, 5, 3, 1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf, ndf * 2, 4, 2, 1),nn.BatchNorm2d(ndf * 2),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1),nn.BatchNorm2d(ndf * 4),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1),nn.BatchNorm2d(ndf * 8),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(ndf * 8, 1, 4, 1, 0))def forward(self, x):output = self.model(x)return output.view(-1)generator = Generator(config)
discriminator = Discriminator(config)optimizer_generator = torch.optim.Adam(generator.parameters(), config.lr1, betas=(config.beta1, 0.999))
optimizer_discriminator = torch.optim.Adam(discriminator.parameters(), config.lr2, betas=(config.beta1, 0.999))true_labels = torch.ones(config.batch_size)
fake_labels = torch.zeros(config.batch_size)
fix_noises = torch.randn(config.batch_size, config.nz, 1, 1)
noises = torch.randn(config.batch_size, config.nz, 1, 1)for epoch in range(config.epochs):for ii, (img, _) in tqdm(enumerate(train_loader)):real_img = img.to(config.device)if ii % 2 == 0:optimizer_discriminator.zero_grad()r_preds = discriminator(real_img)noises.data.copy_(torch.randn(config.batch_size, config.nz, 1, 1))fake_img = generator(noises).detach()f_preds = discriminator(fake_img)r_f_diff = (r_preds - f_preds.mean()).clamp(max=1)f_r_diff = (f_preds - r_preds.mean()).clamp(min=-1)loss_d_real = (1 - r_f_diff).mean()loss_d_fake = (1 + f_r_diff).mean()loss_d = loss_d_real + loss_d_fakeloss_d.backward()optimizer_discriminator.step()else:optimizer_generator.zero_grad()noises.data.copy_(torch.randn(config.batch_size, config.nz, 1, 1))fake_img = generator(noises)f_preds = discriminator(fake_img)r_preds = discriminator(real_img)r_f_diff = r_preds - torch.mean(f_preds)f_r_diff = f_preds - torch.mean(r_preds)loss_g = torch.mean(F.relu(1 + r_f_diff)) + torch.mean(F.relu(1 - f_r_diff))loss_g.backward()optimizer_generator.step()if epoch == config.epochs - 1:# 保存模型torch.save(discriminator.state_dict(), config.discriminator_path)torch.save(generator.state_dict(), config.generator_path)print('Finished Training')generator = Generator(config)
discriminator = Discriminator(config)noises = torch.randn(config.gen_search_num, config.nz, 1, 1).normal_(config.gen_mean, config.gen_std)
noises = noises.to(config.device)generator.load_state_dict(torch.load(config.generator_path, map_location='cpu'))
discriminator.load_state_dict(torch.load(config.discriminator_path, map_location='cpu'))
generator.to(config.device)
discriminator.to(config.device)fake_img = generator(noises)
scores = discriminator(fake_img).detach()indexs = scores.topk(config.gen_num)[1]
result = []
for ii in indexs:result.append(fake_img.data[ii])torchvision.utils.save_image(torch.stack(result), config.gen_img, normalize=True, value_range=(-1, 1))

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

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

相关文章

Qt会议室项目

在Qt中编写会议室应用程序通常涉及到用户界面设计、网络通信、音频/视频处理等方面。以下是创建一个基本会议室应用程序的步骤概述: 项目设置: 使用Qt Creator创建一个新的Qt Widgets Application或Qt Quick Application项目。 用户界面设计&#xff1…

牛客TOP101:合并k个已排序的链表

文章目录 1. 题目描述2. 解题思路3. 代码实现 1. 题目描述 2. 解题思路 多个链表的合并本质上可以看成两个链表的合并,只不过需要进行多次。最简单的方法就是一个一个链表,按照合并两个有序链表的思路,循环多次就可以了。   另外一个思路&a…

(c++)virtual关键字的作用,多态的原理(详细)

1.viirtual修饰的两种函数 virtual 修饰的函数有两种,一个是虚函数,一个是纯虚函数。 2.虚函数与纯虚函数的异同之处 1.虚函数与纯虚函数的相同之处 虚函数和纯虚函数都重写的一种,什么是重写呢?重写是指在子类中写和父类中返…

《0基础》学习Python——第十四讲__封装、继承、多态

<封装、继承、多态> 一、类和实例解析 1、面向对象最重要的概念就是类&#xff08;Class&#xff09;和实例&#xff08;Instance&#xff09;&#xff0c;必须牢记类是抽象的模板 &#xff0c;比如Student类&#xff0c;而 实例是根据类创建出来的一个个具体的“对象”…

《昇思25天学习打卡营第23天|onereal》

第23天学习内容简介&#xff1a; ----------------------------------------------------------------------------- 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。 1 环境配置 配置网络线路 2 代码开发 下载权重大约需要10分钟 ------------------------------- 运…

大模型技术对学校有什么作用?

大模型技术对学校有多方面的作用&#xff0c;可以在教学、管理、决策等多个领域带来显著的改进。以下是大模型技术对学校的主要作用&#xff1a; 1. 个性化教学&#xff1a;大模型技术可以帮助教师分析学生的学习行为和历史成绩&#xff0c;从而定制个性化的教学计划和资源。这…

Linux桌面环境手动编译安装librime、librime-lua以及ibus-rime,提升中文输入法体验

Linux上的输入法有很多&#xff0c;大体都使用了Fcitx或者iBus作为输入法的引擎。相当于有了一个很不错的“地基”&#xff0c;你可以在这个“地基”上盖上自己的“小别墅”。而rime输入法&#xff0c;就是一个“毛坯别墅”&#xff0c;你可以在rime的基础上&#xff0c;再装修…

HCNA ICMP:因特网控制消息协议

ICMP&#xff1a;因特网控制消息协议 前言 Internet控制报文协议ICMP是网络层的一个重要协议。ICMP协议用来在网络设备间传递各种差错和控制信息&#xff0c;他对于手机各种网络信息、诊断和排除各种网络故障有至关重要的作用。使用基于ICMP的应用时&#xff0c;需要对ICMP的工…

Apollo docker-compose

来源 https://www.apolloconfig.com/#/zh/deployment/quick-start-docker 路径 /usr/apollo Sql 自己复制 Vim docker-compose.yml #如果安装过了 记得删除mysql 历史文件 rm -r /var/lib/mysql version: 2.1services:apollo-quick-start:image: nobodyiam/apollo-quick…

AWS CDN新增用户ip 地区 城市 响应头

1.需要自定义cdn缓存策略 这里的策略也是先复制之前的cdn策略哈 最后复制完了 全部新增这两条标头key CloudFront-Viewer-Country CloudFront-Viewer-City 2.然后新增cdn函数&#xff0c;应用你写的这个函数 function handler(event) {var request event.request;var respon…

PySide(PyQt),csv文件的显示

1、正常显示csv文件 import sys import csv from PySide6.QtWidgets import QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QWidgetclass CSVTableWidgetDemo(QMainWindow):def __init__(self):super().__init__()# 创建显示控件self.widget QWidget(self)sel…

动手学深度学习——3.多层感知机

1.线性模型 线性模型可能出错 例如&#xff0c;线性意味着单调假设&#xff1a; 任何特征的增大都会导致模型输出的增大&#xff08;如果对应的权重为正&#xff09;&#xff0c; 或者导致模型输出的减小&#xff08;如果对应的权重为负&#xff09;。 有时这是有道理的。 例…

ENSP防火墙NAT智能选举综合实验

实验目的及其拓扑图&#xff1a; 创建我的拓扑&#xff1a; 新建修改配置fw1上的nat策略和安全策略&#xff1a; pc2ping1.1.1.1测试结果&#xff1a; 服务器映射配置&#xff1a; 配置对应安全策略&#xff1a; 配置fw2&#xff0c;子公司的NAT策略&#xff1a; 配置全局选路路…

Commons-Collections篇-CC6链分析

前言 我们前两篇已经分析过URLDNS链和CC1链&#xff0c;我们这次分析的链就是基于前两条链之上的CC6链 CC6链的使用对于版本来说没有CC1限制那么大&#xff0c;只需要commons collections 小于等于3.2.1&#xff0c;都存在这个漏洞 0.环境安装 可以接着使用我们之前分析CC1链…

AV1技术学习:Affine Motion Compensation

一、Affine Model Parameter 除了传统的平移运动补偿&#xff0c;AV1 还支持仿射变换模型&#xff0c;将当前像素点 (x, y) 通过以下方式投影到参考帧中的预测像素点 (x, y). 参数 (h13, h23) 对应于平移模型中使用的常规运动向量。 参数 h11 和 h22 控制垂直和水平轴上的比例…

unseping

nnnd&#xff0c;这道题谁标的难度1&#xff01;参考文章&#xff1a;江苏工匠杯-unseping&序列化&#xff0c;正则绕过(全网最简单的wp)_江苏工匠杯unseping-CSDN博客 这是这道题的源码&#xff0c;一看exec和unserialize就是反序列化和命令执行&#xff0c;还有个正则应…

【Redis】集群

文章目录 一、集群是什么&#xff1f;二、 Redis集群分布式存储为什么redis集群的最大槽数是16384&#xff08;不太懂&#xff09;redis的集群主节点数量基本不可能超过1000个 三、 配置集群&#xff08;三主三从&#xff09;3.1 配置config文件3.2 启动六台redis3.2 通过redis…

理兔chat开发日记

1.注册 注册跟以前的差不多&#xff0c;我们将我们的验证码放在redis下&#xff0c;我们在注册的时候先判断我们输入的验证码是否正确 验证码成功后在我们的实现类中&#xff0c;我们先判断邮箱是否重复&#xff0c;不重复我们就继续注册 我们拥有联号注册的功能&#xff0c;就…

Puppeteer 是什么以及如何在网络抓取中使用它 | 2024 完整指南

网页抓取已经成为任何处理网页数据提取的人都必须掌握的一项重要技能。无论你是开发者、数据科学家还是希望从网站收集信息的爱好者&#xff0c;Puppeteer都是你可以使用的最强大工具之一。本完整指南将深入探讨什么是Puppeteer以及如何有效地在网页抓取中使用它。 Puppeteer简…

日志的编写与线程池的结合

目录 一、认识日志 二、时间的等级划分 三、日志的输出端 3.1 保存至文件 四、日志的部分信息 4.1 日志等级 4.2 日志时间 五、加载日志 六、日志的宏编写 七、ThreadPool Log 一、认识日志 记录事件&#xff1a; 日志用于记录系统运行过程中发生的各种事件&…