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

news2024/11/15 1:44:50

 前言

大家好,我是机长

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

正在跟新中~

项目背景

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

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

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

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

项目运行环境

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

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

python -m venv myenv

myenv\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 = 96
    batch_size = 32
    epochs = 200
    lr1 = 2e-3
    lr2 = 2e-4
    beta1 = 0.5
    gpu = False
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备
    nz = 100
    ngf = 64
    ndf = 64
    save_path = './images'
    generator_path = './generator.pkl' # 模型保存路径
    discriminator_path = './discriminator.pkl' # 模型保存路径
    gen_img = 'result.png'
    gen_num = 64
    gen_search_num = 5000
    gen_mean = 0
    gen_std = 1


config = Config()

定义生成器Generator

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

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

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

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

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

class Generator(nn.Module):
    def __init__(self, config):
        super().__init__()

        ngf = config.ngf

        self.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.ndf

        self.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_fake

            loss_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 os

import 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 Image

import matplotlib.pyplot as plt

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


config = 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.ngf

        self.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

class Discriminator(nn.Module):
    def __init__(self, config):
        super().__init__()

        ndf = config.ndf

        self.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_fake

            loss_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.coloradmin.cn/o/1930550.html

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

相关文章

【Android Studio】整合okhttp发送get和post请求(提供Gitee源码)

前言:本篇博客教学大家如何使用okhttp发送同步/异步get请求和同步/异步post请求,这边博主把代码全部亲自测试过了一遍,需要源码的可以在文章最后自行拉取。 目录 一、导入依赖 二、开启外网访问权限 三、发送请求 3.1、发送同步get请求…

mac如何合并pdf文件到一个文件 macpdf合并 Mac如何合并pdf文件

在数字化的今天,pdf文件因其跨平台、格式统一等优势,已经成为工作、学习和生活中不可或缺的文件格式。然而,当我们需要合并多个pdf文件时,可能会感到有些无从下手。本文将为你详细介绍几种简单实用的合并pdf的方法,让你…

【学习css3】使用flex和grid实现等高元素布局

过往的实现方法是使用浮动加计算布局来实现,当flex和grid问世时,这一切将变得简单起来 一、简单的两列实现 1、先看页面效果 2、css代码 .container {padding: 10px;width: 100ch;margin: 0 auto;box-shadow: inset 0 0 0 2px #ccc;}.column {margin: 2…

简单爬虫案例

准备工作: 1. 安装好python3 最低为3.6以上, 并成功运行pyhthon3 程序 2. 了解python 多进程原理 3. 了解 python HTTP 请求库 requests 的基本使用 4. 了解正则表达式的用法和python 中 re 库的基本使用 爬取目标 目标网站: https://…

STM32智能建筑能源管理系统教程

目录 引言环境准备智能建筑能源管理系统基础代码实现:实现智能建筑能源管理系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景:能源管理与优化问题解决方案与优化收尾与总结 1. 引言 智能建筑能…

云计算数据中心(二)

目录 三、绿色节能技术(一)配电系统节能技术(二)空调系统节能技术(三)集装箱数据中心节能技术(四)数据中心节能策略和算法研究(五)新能源的应用(六…

verilog刷题笔记

1、选择器实现方式 (1)case语句,注意default (2)if-else语言,注意else,有优先级 (3)三元运算符 ? : 2、阻塞赋值/非阻塞赋值都是过程性赋值&a…

华为od机试真题 — 测试用例执行计划(Python)

题目描述 某个产品当前迭代周期内有N个特性(F1, F2, ..., FN)需要进行覆盖测试,每个特性都被评估了对应的优先级,特性使用其ID作为下标进行标识。 设计了M个测试用例(T1, T2,...,TM),每个用例…

【JavaEE】HTTP协议(1)

🤡🤡🤡个人主页🤡🤡🤡 🤡🤡🤡JavaEE专栏🤡🤡🤡 🤡🤡🤡上一篇文章:【JavaEE】网络原理—…

入坑树莓派(2)——树莓派4B与手机蓝牙通信

入坑树莓派(2)——树莓派4B与手机蓝牙通信 1、引言 在入坑树莓派(1)中已经搞掂了可视化问题。现在继续开展下一步,尝试与手机通信,一开始是想弄wifi连接的,但发现基于wifi的APP比较难弄,为了降低开发的难度,又因为树莓派板子自带蓝牙模块,所以直接选用蓝牙连接手机…

Qt窗口程序整理汇总

到今日为止,通过一个个案例的实验,逐步熟悉了 Qt6下 窗体界面开发的,将走过的路,再次汇总整理。 Qt Splash样式的登录窗https://blog.csdn.net/castlooo/article/details/140462768 Qt实现MDI应用程序https://blog.csdn.net/cast…

昇思25天学习打卡营第16天|Vision Transformer图像分类

本节使用Vision Transfomer完成图像分类 相关知识 Vision Transformer ViT是计算机视觉和自然语言处理两个领域的融合成果。它使用transformer架构来处理图像数据,这种架构原本是用于处理自然语言的。 ViT的主要思想是将图像分割成固定大小的块(patch…

kafka开启kerberos和ACL

作者:恩慈 一、部署kafka-KB包 1.上传软件包 依次点击 部署中心----部署组件----上传软件包 选择需要升级的kafka版本并点击确定 2.部署kafka 依次点击部署中心----部署组件----物理/虚拟机部署----选择集群----下一步 选择手动部署-…

GB28181语音对讲实现

1.前提准备 1.1.首先将设备接入SIP网关服务 我这里使用的是开源的wvp-GB28181-pro项目,首先将设备接入到SIP网关服务。 配置信息如下: 1.2.修改设备配置 设备接入后,会自动注册到平台,可以在国标设备栏看到刚刚注册的设备信息…

K8S私有云裸金属服务器负载均衡器OpenELB——筑梦之路

OpenELB介绍 OpenELB 是一个专为裸机 Kubernetes 集群设计的开源负载均衡器实现。 在云服务环境中的 Kubernetes 集群里,通常可以用云服务提供商提供的负载均衡服务来暴露 Service,但是在本地没办法这样操作。而 OpenELB 可以让用户在裸金属服务器、边缘…

模块化和包管理工具

一,模块化 1.定义 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化 其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用 2.模块化…

6.Dockerfile及Dockerfile常用指令

Dockerfile是构建docker镜像的脚本文件 Dockerfile有很多的指令构成,指令由上到下依次运行。 每一条指令就是一层镜像,层越多,体积就越大,启动速度也越慢 井号开头的行是注释行。指令写大写写小写都行,但一般都写为…

【Linux】多线程_9

文章目录 九、多线程10. 线程池 未完待续 九、多线程 10. 线程池 这里我没实现一些 懒汉单例模式 的线程池,并且包含 日志打印 的线程池: Makefile: threadpool:Main.ccg -o $ $^ -stdc11 -lpthread .PHONY:clean clean:rm -f threadpoolT…

前端开发常用命令行工具及操作命令(Node.js 和 npm、Yarn、vue、React、Git、Webpack)

在前端开发中,掌握常用的命令行工具和命令可以大大提高开发效率。接下来将介绍一些常用的前端命令行工具和命令,涵盖从项目初始化到构建和部署的各个环节。 1. Node.js 和 npm 安装 Node.js 和 npm 首先,需要安装 Node.js。安装 Node.js 时…

FreeRTOS的中断管理、临界资源保护、任务调度

什么是中断? 简介:让CPU打断正常运行的程序,转而去处理紧急的事件(程序),就叫中断。 中断优先级分组设置 ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级…