【计算机视觉技术 - 人脸生成】2.GAN网络的构建和训练

         GAN 是一种常用的优秀的图像生成模型。我们使用了支持条件生成的 cGAN。下面介绍简单 cGAN 模型的构建以及训练过程。

2.1 在 model 文件夹中新建 nets.py 文件

import torch
import torch.nn as nn


# 生成器类
class Generator(nn.Module):
    def __init__(self, nz=100, nc=3, ngf=128, num_classes=4):
        super(Generator, self).__init__()
        self.label_emb = nn.Embedding(num_classes, nz)

        self.main = nn.Sequential(
            nn.ConvTranspose2d(nz + nz, ngf * 8, 4, 1, 0, bias=False),
            nn.BatchNorm2d(ngf * 8),
            nn.ReLU(True),

            nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 4),
            nn.ReLU(True),

            nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 2),
            nn.ReLU(True),

            nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf),
            nn.ReLU(True),

            nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False),
            nn.Tanh()
        )

    def forward(self, z, labels):
        c = self.label_emb(labels).unsqueeze(2).unsqueeze(3)
        x = torch.cat([z, c], 1)
        return self.main(x)


# 判别器类
class Discriminator(nn.Module):
    def __init__(self, nc=3, ndf=64, num_classes=4):
        super(Discriminator, self).__init__()
        self.label_emb = nn.Embedding(num_classes, nc * 64 * 64)

        self.main = nn.Sequential(
            nn.Conv2d(nc + 1, ndf, 4, 2, 1, bias=False),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 2),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 4),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(ndf * 4, 1, 4, 1, 0, bias=False),
            nn.Sigmoid()
        )

    def forward(self, img, labels):
        c = self.label_emb(labels).view(labels.size(0), 1, 64, 64)
        x = torch.cat([img, c], 1)
        return self.main(x)

2.2新建cGAN_net.py

import torch
import torch.nn as nn
from torch.optim import Adam
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch.optim.lr_scheduler import StepLR


# ===========================
# Conditional DCGAN 实现
# ===========================
class cDCGAN:
    def __init__(self, data_root, batch_size, device, latent_dim=100, num_classes=4):
        self.device = device
        self.batch_size = batch_size
        self.latent_dim = latent_dim
        self.num_classes = num_classes

        # 数据加载器
        self.train_loader = self.get_dataloader(data_root)

        # 初始化生成器和判别器
        self.generator = self.build_generator().to(device)
        self.discriminator = self.build_discriminator().to(device)

        # 初始化权重
        self.generator.apply(self.weights_init)
        self.discriminator.apply(self.weights_init)

        # 损失函数和优化器
        self.criterion = nn.BCELoss()
        self.optimizer_G = Adam(self.generator.parameters(), lr=0.0001, betas=(0.5, 0.999))
        self.optimizer_D = Adam(self.discriminator.parameters(), lr=0.0001, betas=(0.5, 0.999))

        # 学习率调度器
        self.scheduler_G = StepLR(self.optimizer_G, step_size=10, gamma=0.5)  # 每10个epoch学习率减半
        self.scheduler_D = StepLR(self.optimizer_D, step_size=10, gamma=0.5)

    def get_dataloader(self, data_root):
        transform = transforms.Compose([
            transforms.Resize(128),
            transforms.ToTensor(),
            transforms.Normalize((0.5,), (0.5,))
        ])
        dataset = datasets.ImageFolder(root=data_root, transform=transform)
        return DataLoader(dataset, batch_size=self.batch_size, shuffle=True,
                          num_workers=8, pin_memory=True, persistent_workers=True)

    @staticmethod
    def weights_init(model):
        """权重初始化"""
        if isinstance(model, (nn.Conv2d, nn.Linear)):
            nn.init.normal_(model.weight.data, 0.0, 0.02)
            if model.bias is not None:
                nn.init.constant_(model.bias.data, 0)

    def train_step(self, epoch, step, num_epochs):
        """单次训练步骤"""
        self.generator.train()
        self.discriminator.train()

        G_losses, D_losses = [], []
        for i, (real_img, labels) in enumerate(self.train_loader):
            # 确保 real_img 和 labels 在同一设备
            real_img = real_img.to(self.device)
            labels = labels.to(self.device)

            batch_size = real_img.size(0)

            # # 标签   11.19 15:11:12修改
            # valid = torch.ones((batch_size, 1), device=self.device)
            # fake = torch.zeros((batch_size, 1), device=self.device)

            # 标签平滑
            # smooth_valid = torch.full((batch_size, 1), 1, device=self.device)  # 平滑真实标签
            # smooth_fake = torch.full((batch_size, 1), 0, device=self.device)  # 平滑伪造标签
            # smooth_valid = torch.full((batch_size, 1), torch.rand(1).item() * 0.1 + 0.9, device=self.device)
            # smooth_fake = torch.full((batch_size, 1), torch.rand(1).item() * 0.1, device=self.device)
            # smooth_valid = torch.full((batch_size, 1), max(0.7, 1 - epoch * 0.001), device=self.device)
            # smooth_fake = torch.full((batch_size, 1), min(0.3, epoch * 0.001), device=self.device)
            # 动态调整标签范围
            smooth_valid = torch.full((batch_size, 1), max(0.9, 1 - 0.0001 * epoch), device=self.device)
            smooth_fake = torch.full((batch_size, 1), min(0.1, 0.0001 * epoch), device=self.device)

            # 替换以下两处代码
            valid = smooth_valid
            fake = smooth_fake

            # ========== 训练判别器 ==========
            real_pred = self.discriminator(real_img, labels)
            # d_real_loss = self.criterion(real_pred, valid)
            d_real_loss = self.criterion(real_pred, valid - 0.1 * torch.rand_like(valid))

            noise = torch.randn(batch_size, self.latent_dim, device=self.device)
            # gen_labels = torch.randint(0, self.num_classes, (batch_size,), device=self.device)
            gen_labels = torch.randint(0, self.num_classes, (batch_size,), device=self.device) + torch.randint(-1, 2, (
            batch_size,), device=self.device)
            gen_labels = torch.clamp(gen_labels, 0, self.num_classes - 1)  # 确保标签在范围内

            gen_img = self.generator(noise, gen_labels)

            fake_pred = self.discriminator(gen_img.detach(), gen_labels)
            # d_fake_loss = self.criterion(fake_pred, fake)
            d_fake_loss = self.criterion(fake_pred, fake + 0.1 * torch.rand_like(fake))

            d_loss = (d_real_loss + d_fake_loss) / 2
            self.optimizer_D.zero_grad()
            d_loss.backward()
            self.optimizer_D.step()
            D_losses.append(d_loss.item())

            # ========== 训练生成器 ==========
            gen_pred = self.discriminator(gen_img, gen_labels)
            g_loss = self.criterion(gen_pred, valid)

            self.optimizer_G.zero_grad()
            g_loss.backward()
            self.optimizer_G.step()
            G_losses.append(g_loss.item())

            print(f'第 {epoch}/{num_epochs} 轮, Batch {i + 1}/{len(self.train_loader)}, '
                  f'D Loss: {d_loss:.4f}, G Loss: {g_loss:.4f}')

        step += 1
        return G_losses, D_losses, step

    def build_generator(self):
        """生成器"""
        return Generator(latent_dim=self.latent_dim, num_classes=self.num_classes)

    def build_discriminator(self):
        """判别器"""
        return Discriminator(num_classes=self.num_classes)

    def load_model(self, model_path):
        """加载模型权重"""
        checkpoint = torch.load(model_path, map_location=self.device)
        self.generator.load_state_dict(checkpoint['generator_state_dict'])
        self.optimizer_G.load_state_dict(checkpoint['optimizer_G_state_dict'])
        self.discriminator.load_state_dict(checkpoint['discriminator_state_dict'])
        self.optimizer_D.load_state_dict(checkpoint['optimizer_D_state_dict'])
        epoch = checkpoint['epoch']
        print(f"加载了模型权重,起始训练轮次为 {epoch}")
        return epoch

    def save_model(self, epoch, save_path):
        """保存模型"""
        torch.save({
            'epoch': epoch,
            'scheduler_G_state_dict': self.scheduler_G.state_dict(),
            'scheduler_D_state_dict': self.scheduler_D.state_dict(),
            'generator_state_dict': self.generator.state_dict(),
            'optimizer_G_state_dict': self.optimizer_G.state_dict(),
            'discriminator_state_dict': self.discriminator.state_dict(),
            'optimizer_D_state_dict': self.optimizer_D.state_dict(),
        }, save_path)
        print(f"模型已保存至 {save_path}")


# ===========================
# 生成器
# ===========================
class Generator(nn.Module):
    def __init__(self, latent_dim=100, num_classes=4, img_channels=3):
        super(Generator, self).__init__()
        self.latent_dim = latent_dim
        self.label_emb = nn.Embedding(num_classes, num_classes)
        self.init_size = 8
        self.l1 = nn.Linear(latent_dim + num_classes, 256 * self.init_size * self.init_size)

        self.conv_blocks = nn.Sequential(
            nn.BatchNorm2d(256),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(256, 128, 3, padding=1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 64, 3, padding=1),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Upsample(scale_factor=2),
            nn.Conv2d(64, 32, 3, padding=1),
            nn.BatchNorm2d(32),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Upsample(scale_factor=2),
            nn.Conv2d(32, img_channels, 3, padding=1),
            nn.Tanh()
        )

    def forward(self, noise, labels):
        labels = labels.to(self.label_emb.weight.device)
        label_embedding = self.label_emb(labels)
        x = torch.cat((noise, label_embedding), dim=1)
        x = self.l1(x).view(x.size(0), 256, self.init_size, self.init_size)
        return self.conv_blocks(x)


# ===========================
# 判别器
# ===========================
class Discriminator(nn.Module):
    def __init__(self, img_channels=3, num_classes=4):
        super(Discriminator, self).__init__()
        self.label_embedding = nn.Embedding(num_classes, img_channels)

        self.model = nn.Sequential(
            nn.Conv2d(img_channels * 2, 64, 4, stride=2, padding=1),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Conv2d(64, 128, 4, stride=2, padding=1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(128, 256, 4, stride=2, padding=1),
            nn.BatchNorm2d(256),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(256, 512, 4, stride=2, padding=1),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2, inplace=True)
        )

        self.output_layer = nn.Sequential(
            nn.Linear(512 * 8 * 8, 1),
            nn.Sigmoid()
        )

    def forward(self, img, labels):
        labels = labels.to(self.label_embedding.weight.device)
        label_embedding = self.label_embedding(labels).unsqueeze(2).unsqueeze(3)
        label_embedding = label_embedding.expand(-1, -1, img.size(2), img.size(3))
        x = torch.cat((img, label_embedding), dim=1)
        x = self.model(x).view(x.size(0), -1)
        return self.output_layer(x)

2.3新建cGAN_trainer.py

import os
import torch
import argparse
from cGAN_net import cDCGAN
from utils import plot_loss, plot_result
import time

os.environ['OMP_NUM_THREADS'] = '1'


def main(args):
    # 初始化设备和训练参数
    device = torch.device(args.device if torch.cuda.is_available() else 'cpu')
    model = cDCGAN(data_root=args.data_root, batch_size=args.batch_size, device=device, latent_dim=args.latent_dim)

    # 添加学习率调度器
    scheduler_G = torch.optim.lr_scheduler.StepLR(model.optimizer_G, step_size=10, gamma=0.5)
    scheduler_D = torch.optim.lr_scheduler.StepLR(model.optimizer_D, step_size=10, gamma=0.5)

    start_epoch = 0

    # 如果有保存的模型,加载
    if args.load_model and os.path.exists(args.load_model):
        start_epoch = model.load_model(args.load_model) + 1

        # 恢复调度器状态
        scheduler_G_path = f"{args.load_model}_scheduler_G.pt"
        scheduler_D_path = f"{args.load_model}_scheduler_D.pt"

        if os.path.exists(scheduler_G_path) and os.path.exists(scheduler_D_path):
            scheduler_G.load_state_dict(torch.load(scheduler_G_path))
            scheduler_D.load_state_dict(torch.load(scheduler_D_path))
            print(f"成功恢复调度器状态:{scheduler_G_path}, {scheduler_D_path}")
        else:
            print("未找到调度器状态文件,使用默认调度器设置")

        print(f"从第 {start_epoch} 轮继续训练...")

    print(f"开始训练,从第 {start_epoch + 1} 轮开始...")

    # 创建保存路径
    os.makedirs(args.save_dir, exist_ok=True)
    os.makedirs(os.path.join(args.save_dir, 'log'), exist_ok=True)

    # 训练循环
    D_avg_losses, G_avg_losses = [], []

    for epoch in range(start_epoch, args.epochs):
        G_losses, D_losses, step = model.train_step(epoch, step=0, num_epochs=args.epochs)


        # 计算平均损失
        D_avg_loss = sum(D_losses) / len(D_losses) if D_losses else 0.0
        G_avg_loss = sum(G_losses) / len(G_losses) if G_losses else 0.0
        D_avg_losses.append(D_avg_loss)
        G_avg_losses.append(G_avg_loss)

        # 保存损失曲线图
        plot_loss(start_epoch, args.epochs, D_avg_losses, G_avg_losses, epoch + 1, save=True,
                  save_dir=os.path.join(args.save_dir, "log"))

        # 生成并保存图片
        labels = torch.tensor([0, 1, 2, 3]).to(device)
        if (epoch + 1) % args.save_freq == 0:  # 每隔一定轮次保存生成结果
            z = torch.randn(len(labels), args.latent_dim, device=device)  # 随机生成噪声
            plot_result(model.generator, z, labels, epoch + 1, save_dir=os.path.join(args.save_dir, 'log'))

        # 每10个epoch保存模型
        if (epoch + 1) % args.save_interval == 0:
            timestamp = int(time.time())
            save_path = os.path.join(args.save_dir, f"cgan_epoch_{epoch + 1}_{timestamp}.pth")
            model.save_model(epoch + 1, save_path)
            print(f"第 {epoch + 1} 轮的模型已保存,保存路径为 {save_path}")

        # 更新学习率调度器
        scheduler_G.step()
        scheduler_D.step()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--data_root', type=str, default='data/crop128', help="数据集根目录")
    parser.add_argument('--save_dir', type=str, default='./chkpt/cgan_model', help="保存模型的目录")
    parser.add_argument('--load_model', type=str, default=None, help="要加载的模型路径(可选)")
    parser.add_argument('--epochs', type=int, default=1000, help="训练的轮数")
    parser.add_argument('--save_interval', type=int, default=10, help="保存模型检查点的间隔(按轮数)")
    parser.add_argument('--batch_size', type=int, default=64, help="训练的批次大小")
    parser.add_argument('--device', type=str, default='cuda', help="使用的设备(如 cuda 或 cpu)")
    parser.add_argument('--latent_dim', type=int, default=100, help="生成器的潜在空间维度")
    parser.add_argument('--save_freq', type=int, default=1, help="每隔多少轮保存一次生成结果(默认: 1)")
    args = parser.parse_args()
    main(args)

结果分析:

2.4中间结果可视化处理

新建utils.py,编写绘制中间结果和中间损失线图的函数,代码如下:

import matplotlib.pyplot as plt
import numpy as np
import os
import torch


def denorm(x):
    out = (x + 1) / 2
    return out.clamp(0, 1)


def plot_loss(start_epoch, num_epochs, d_losses, g_losses, num_epoch, save=False, save_dir='celebA_cDCGAN_results/', show=False):
    """
    绘制损失函数曲线,从 start_epoch 到 num_epochs。

    Args:
        start_epoch: 起始轮次
        num_epochs: 总轮次
        d_losses: 判别器损失列表
        g_losses: 生成器损失列表
        num_epoch: 当前训练轮次
        save: 是否保存绘图
        save_dir: 保存路径
        show: 是否显示绘图
    """
    fig, ax = plt.subplots()
    ax.set_xlim(start_epoch, num_epochs)
    ax.set_ylim(0, max(np.max(g_losses), np.max(d_losses)) * 1.1)

    plt.xlabel(f'Epoch {num_epoch + 1}')
    plt.ylabel('Loss values')

    plt.plot(d_losses, label='Discriminator')
    plt.plot(g_losses, label='Generator')
    plt.legend()

    if save:
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        save_fn = os.path.join(save_dir, f'cDCGAN_losses_epoch.png')
        plt.savefig(save_fn)

    if show:
        plt.show()
    else:
        plt.close()


def plot_result(generator, z, labels, epoch, save_dir=None, show=False):
    """
    生成并保存或显示生成的图片结果。

    Args:
        generator: 生成器模型
        z: 随机噪声张量
        labels: 标签张量
        epoch: 当前训练轮数
        save_dir: 保存图片的路径(可选)
        show: 是否显示生成的图片(可选)
    """
    # 调用生成器,生成图像
    generator.eval()  # 设置为评估模式
    with torch.no_grad():
        gen_images = generator(z, labels)  # 同时传入 z 和 labels
    generator.train()  # 恢复训练模式

    # 图像反归一化
    gen_images = denorm(gen_images)

    # 绘制图片
    fig, ax = plt.subplots(1, len(gen_images), figsize=(15, 15))
    for i in range(len(gen_images)):
        ax[i].imshow(gen_images[i].permute(1, 2, 0).cpu().numpy())  # 转换为可显示格式
        ax[i].axis('off')

    # 保存或显示图片
    if save_dir:
        os.makedirs(save_dir, exist_ok=True)
        save_path = os.path.join(save_dir, f'epoch_{epoch}.png')
        plt.savefig(save_path)
    if show:
        plt.show()
    plt.close(fig)

执行 cGAN_trainer.py 文件,完成模型训练。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

手机投屏到电视的3种选择:无线本地投屏,无线远程投屏,AirPlay投屏

现在大部分手机投屏都要求连接相同的WiFi,这就意味着手机投屏到电视必须是近距离投屏,稍微远一点就会脱离WiFi连接范围,投屏失败。 如果想将手机远程投屏到安卓电视,要怎样做? 第一步,在手机和安卓电视都安…

zookeeper 数据类型

文章目录 引言I Znodezonde stat (状态信息)znode类型临时\永久序列化特性引言 在结构上与标准文件系统非常类似,拥有一个层次的命名空间,都是采用树形层次结构 Zookeeper树中的每个节点被称为:Znode,没有文件和目录之分。Znode兼具文件和目录两种特点Znode存储数据大小有…

73 mysql replication 集群的交互

前言 新建两个数据库, 分别为 192.168.220.132:3001, 192.168.220.132:3002 设置 192.168.220.132:3001 为 master, 192.168.220.132:3002 为 slave 配置文件如下 然后使用 mysqld --initialize 来初始化 data 目录, 以及相关基础数据库 这里会为 root 账户创建一个随机的…

CG顶会论文阅读|《科技论文写作》硕士课程报告

文章目录 一、基本信息1.1 论文基本信息1.2 课程基本信息1.3 博文基本信息 二、论文评述(中英双语)2.1 研究问题(Research Problem)2.2 创新点(Innovation/Contribution)2.3 优点(Why this pape…

路由器的转发表

【4-24】 已知路由器R₁ 的转发表如表T-4-24 所示。 表T-4-24 习题4-24中路由器R₁的转发表 前缀匹配 下一跳地址 路由器接口 140.5.12.64/26 180.15.2.5 m2 130.5.8/24 190.16.6.2 ml 110.71/16 ----- m0 180.15/16 ----- m2 190.16/16 ----- ml 默认 11…

嵌入式驱动开发详解8(阻塞/非阻塞/异步通信)

文章目录 前言阻塞非阻塞异步通知后续 前言 首先来回顾一下“中断”,中断是处理器提供的一种异步机制,我们配置好中断以后就 可以让处理器去处理其他的事情了,当中断发生以后会触发我们事先设置好的中断服务函数, 在中断服务函数…

【MATLAB第112期】基于MATLAB的SHAP可解释神经网络回归模型(敏感性分析方法)

【MATLAB第112期】基于MATLAB的SHAP可解释神经网络回归模型(敏感性分析方法) 引言 该文章实现了一个可解释的神经网络回归模型,使用BP神经网络(BPNN)来预测特征输出。该模型利用七个变量参数作为输入特征进行训练。为…

DeepSeek-V3 正式发布,已在网页端和 API 全面上线,性能领先,速度飞跃。

DeepSeek-V3 在推理速度上相较历史模型有了大幅提升。在目前大模型主流榜单中,DeepSeek-V3 在开源模型中位列榜首,与世界上最先进的闭源模型不分伯仲。 简介 DeepSeek-V3是一个强大的混合专家 (MoE) 语言模型,总共有 671B 个参数,…

深度评测uni-app x:开启跨平台开发新篇章

文章目录 一、引言1.1 跨平台开发的崛起1.2 uni-app x 初印象 二、uni-app x 核心特性评测2.1 uts 语言:跨平台编程新利器2.2 uvue 渲染引擎:原生渲染新体验2.3 强大的组件和 API 支持2.4 插件生态:拓展无限可能 三、与 uni-app 对比&#xf…

用python编写一个放烟花的小程序

import pygame import random # 代码解释及使用说明: # 首先,导入 pygame 和 random 库。pygame 用于创建游戏窗口和图形绘制,random 用于生成随机数。 # 初始化 pygame,并设置屏幕尺寸为 800x600 像素,设置窗口标题为…

Android设备使用AOA协议进行主机与配件模式通信

1.使用TYPC-C数据线连接两台华为手机: TYPE-C线,先连接下图右边的ACCESSORY 再连接左边的HOST 此时左边的HOST(白色) 会给右边的ACCESSORY(黑色) 充电 接着打开左连接的HostChart会自动调起授权,然后会启动右边的AccessoryChart USB HOS…

flutter在windows平台中运行报错

PS D:\F\luichun> flutter run当运行flutter项目时,【解决如下报错】 /C:/flutter/packages/flutter/lib/src/painting/star_border.dart:530:27: Error: The getter Matrix4 isnt defined for the class _StarGenerator.- _StarGenerator is from package:flut…

ROS2软件架构全面解析-学习如何设计通信中间件框架

前言 ROS(Robot Operating System) 2 是一个用于开发机器人应用的软件平台,也称为机器人软件开发工具包 (SDK)。 ROS2是ROS1的迭代升级版本 ,最主要的升级点是引入DDS(Data Distribution Service)为基础的…

【C++】B2093 查找特定的值

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯题目描述输入格式输出格式输入输出示例 💯题目分析与解题思路💯代码实现与对比分析我的实现代码老师的实现代码详细对比与分析1. 数组的定义方式2. …

SAP 01-初识AMDP(ABAP-Managed Database Procedure)

1. 什么是AMDP(ABAP-Managed Database Procedure) 1.)AMDP - ABAP管理数据库程序,是一种程序,我们可以使用SQLSCRIPT在AMDP内部编写代码,SQLSCRIPT是一种与SQL脚本相同的数据库语言,这种语言易于理解和编码。 将AM…

基于 gitlab-runner 实现调度GPU的资源

本篇目录 1. 客户需求2. 需求调研3. 实践3.1 方案一:环境变量的方式3.2 方案二:k8s 自身的spec注入机制 4. 效果 该实践来自于客户的一个真实需求 1. 客户需求 客户的某些流水线需要使用GPU资源,但是对于GPU服务器而言,会有多张G…

走进深圳华为总部参观研学

在这个科技日新月异的时代,每一次与行业标杆企业领先者对话,都是开眼界的好时机。华研标杆游学高老师组织了一场企业家参访团体考察,带大家去到深圳华为总部研学,亲身感受科技巨头的风采,一起探讨未来的发展。 第一站-…

客户案例:基于慧集通(DataLinkX)集成平台的金蝶云星空公有云与WMS系统对接集成方案

本文档详细介绍了基于慧集通(DataLinkX)集成平台的金蝶云星空公有云与WMS系统对接集成方案。该方案旨在实现金蝶云星空与WMS系统之间的数据同步和流程对接,以提高企业供应链管理的效率和准确性。通过物料、供应商资料同步,采购、销…

【Kaggle】练习赛《预测贴纸的销量》(上)

前言 本篇文章介绍的是2025年首个Kaggle月赛《Forecasting Sticker Sales》,即《预测贴纸的销量》。与之前一样,也同样适合初学者,但与之前不同的是,本次比赛的数据集是个时间序列,从题目来看,就是通过之前…

【论文+源码】基于Spring和Spring MVC的汉服文化宣传网站

为了实现一个基于Spring和Spring MVC的汉服文化宣传网站,我们需要创建一个简单的Web应用程序来展示汉服文化和相关信息。这个系统将包括以下几个部分: 数据库表设计:定义文章、用户和评论的相关表。实体类:表示数据库中的数据。DAO层接口及MyBatis映射文件:用于与数据库交…