IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 改进StyleGANv2的容貌年龄预测系统 -> 正文阅读

[人工智能]改进StyleGANv2的容貌年龄预测系统

1.研究背景

随着互联网的发展与移动设备的普及,越来越多的人在社交媒体上分享个人图像。人像摄影流行的同时促进了人脸图像编辑技术的快速发展。传统的人脸图像编辑技术包括添加面部装饰(如眼镜、胡子)以及修饰容颜。基于生成对抗网络的人脸图像编辑方法能够改变人脸的固有属性(如年龄、性别)。人脸图像的年龄属性编辑技术在娱乐社交、影视媒体和智能安防等领域有着良好的应用前景和价值,能够广泛应用于人脸特效制作、帮助寻找失踪儿童、跨年龄段的人脸识别数据合成等任务。

2.图片演示

2.png

3.png

3.视频演示

改进StyleGANv2的容貌年龄预测系统(源码&教程)_哔哩哔哩_bilibili

4.StyleGAN2介绍

StyleGAN2 的出现当然是因为StyleGAN存在瑕疵,少量生成的图片有明显的水珠,这个水珠也存在于feature map上,如下图:
image.png

导致水珠的原因是 Adain 操作,Adain对每个feature map进行归一化,因此有可能会破坏掉feature之间的信息。emmmm,懵懵懂懂,最重要的是实验证明 当去除Adain的归一化操作后,水珠就消失了,所以Adain就背锅吧。
上面两张图说明了从 styleGAN 到 styleGAN2 ,在网络结构上的变换,去除normalization之后水珠消失了,但是styleGAN的一个亮点是 style mixing,仅仅只改网络结构,虽然能去除水珠,但是无法对style mixing 有 scale-specific级别的控制。

5.StyleGAN2 的改进点

AdaIN

StyleGAN第一个版本提出了通过AdaIN模块来实现生成,这个模块非常好也非常妙。
image.png

权重解调

虽然我们修改了网络结构,去除了水滴问题,但是styleGAN的目的是对特征实现可控的精细的融合。

StyleGAN2说,style modulation可能会放大某些特征的影像,所以style mixing的话,我们必须明确的消除这种影像,否则后续层的特征无法有效的控制图像。如果他们想要牺牲scale-specific的控制能力,他们可以简单的移除normalization,就可以去除掉水滴伪影,这还可以使得FID有着微弱的提高。现在他们提出了一个更好的替代品,移除伪影的同时,保留完全的可控性。这个就是weight demodulation。

我们继续看这个图:
image.png

里面包含三个style block,每一个block包含modulation(Mod),convolution and normalization。

modulation可以影响着卷积层的输入特征图。所以,其实Mod和卷积是可以继续宁融合的。比方说,input先被Mod放大了3倍,然后在进行卷积,这个等价于input直接被放大了3倍的卷积核进行卷积。Modulation和卷积都是在通道维度进行操作。所以有如下公式:
image.png

接下来的norm部分也做了修改:
image.png

这里替换了对特征图做归一化,而是去卷积的参数做了一个归一化,先前有研究提出,这样会有助于GAN的训练。
至此,我们发现,Mod和norm部分的操作,其实都可以融合到卷积核上。

class GeneratorBlock(nn.Module):
    def __init__(self, latent_dim, input_channels, filters, upsample = True, upsample_rgb = True, rgba = False):
        super().__init__()
        self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False) if upsample else None

        self.to_style1 = nn.Linear(latent_dim, input_channels)
        self.to_noise1 = nn.Linear(1, filters)
        self.conv1 = Conv2DMod(input_channels, filters, 3)
        
        self.to_style2 = nn.Linear(latent_dim, filters)
        self.to_noise2 = nn.Linear(1, filters)
        self.conv2 = Conv2DMod(filters, filters, 3)

        self.activation = leaky_relu()
        self.to_rgb = RGBBlock(latent_dim, filters, upsample_rgb, rgba)

    def forward(self, x, prev_rgb, istyle, inoise):
        if exists(self.upsample):
            x = self.upsample(x)

        inoise = inoise[:, :x.shape[2], :x.shape[3], :]
        noise1 = self.to_noise1(inoise).permute((0, 3, 2, 1))
        noise2 = self.to_noise2(inoise).permute((0, 3, 2, 1))

        style1 = self.to_style1(istyle)
        x = self.conv1(x, style1)
        x = self.activation(x + noise1)

        style2 = self.to_style2(istyle)
        x = self.conv2(x, style2)
        x = self.activation(x + noise2)

        rgb = self.to_rgb(x, prev_rgb, istyle)
        return x, rgb

可以发现,这个噪音也会经过Linear层的简单变换,然后里面加入了残差。为什么要输出rgb图像呢?这个会放在下次,或者下下次的内容。styleGAN1是需要用progressive growing的策略的,而StyleGAN2使用新的架构,解决了这种繁琐的训练方式。

6.代码实现

def get_lr(t, ts, initial_lr, final_lr):
    alpha = pow(final_lr/initial_lr, 1/ts)**(t*ts)

    return initial_lr * alpha


def project(
    imgs: List[PIL.Image.Image],
    masks: List[PIL.Image.Image] = None,
    generator = None,
    pSp = None,
    ckpt: arg_type(str, help="path to the model checkpoint") = None,
    model_type: arg_type(str, help="inner model type. `ffhq-config-f` for default genrator and `ffhq-inversion` for pSp") = None,
    size: arg_type(int, help="original output image resolution") = 1024,
    style_dim: arg_type(int, help="dimensions of style z") = 512,
    n_mlp: arg_type(int, help="the number of multi-layer perception layers for style z") = 8,
    channel_multiplier: arg_type(int, help="channel product, affect model size and the quality of generated pictures") = 2,
    start_lr: arg_type(float, help="learning rate at the begin of training") = 0.1,
    final_lr: arg_type(float, help="learning rate at the end of training") = 0.025,
    latent_level: arg_type(List[int], help="indices of latent code for training") = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],
    step: arg_type(int, help="optimize iterations") = 100,
    mse_weight: arg_type(float, help="weight of the mse loss") = 1,
    no_encoder: arg_type(
       'project:no_encoder', action="store_true", 
       help="disable to use pixel2style2pixel model to pre-encode the images"
    ) = False,
):
    n_mean_latent = 4096

    transform = transforms.Compose(
        [
            transforms.Resize(256),
            transforms.CenterCrop(256),
            transforms.Transpose(),
            transforms.Normalize([127.5, 127.5, 127.5], [127.5, 127.5, 127.5]),
        ]
    )

    _imgs = []
    _masks = []
    if masks is None:
        masks = [Image.new(mode='L', size=img.size, color=255) for img in imgs]
    for img, mask in zip(imgs, masks):
        assert isinstance(img, PIL.Image.Image) and isinstance(mask, PIL.Image.Image)
        img = paddle.to_tensor(transform(img))
        mask = (paddle.to_tensor(transform(mask.convert('RGB')))[:1] + 1) / 2
        _imgs.append(img)
        _masks.append(mask)

    imgs = paddle.stack(_imgs, 0)
    masks = paddle.stack(_masks, 0)

    percept = lpips.LPIPS(net='vgg')
    percept.train() # on PaddlePaddle, lpips's default eval mode means no gradients.

    if generator is not None:
        no_encoder = True
    if pSp is None:
        no_encoder = False
    if no_encoder:
        generator = generator if generator is not None else get_generator(
            weight_path=None if ckpt is None else ckpt,
            model_type='ffhq-config-f' if model_type is None else model_type,
            size=size,
            style_dim=style_dim,
            n_mlp=n_mlp,
            channel_multiplier=channel_multiplier
        )
        # generator.eval() # on PaddlePaddle, model.eval() means no gradients.

        with paddle.no_grad():
            noise_sample = paddle.randn((n_mean_latent, style_dim))
            latent_out = generator.style(noise_sample)

            latent_mean = latent_out.mean(0)

        latent_in = latent_mean.detach().clone().unsqueeze(0).tile((imgs.shape[0], 1))
        latent_in = latent_in.unsqueeze(1).tile((1, generator.n_latent, 1)).detach()

    else:
        pSp = pSp if pSp is not None else get_pSp(
            weight_path=None if ckpt is None else ckpt,
            model_type='ffhq-inversion' if model_type is None else model_type,
            size=size,
            style_dim=style_dim,
            n_mlp=n_mlp,
            channel_multiplier=channel_multiplier
        )
        # pSp.eval() # on PaddlePaddle, model.eval() means no gradients.
        generator = pSp.decoder
        
        with paddle.no_grad():
            _, latent_in = pSp(imgs, randomize_noise=False, return_latents=True)
        latent_in = latent_in.detach().clone()
    
    var_levels = list(latent_level)
    const_levels = [i for i in range(generator.n_latent) if i not in var_levels]
    assert len(var_levels) > 0
    if len(const_levels) > 0:
        latent_fix = latent_in.index_select(paddle.to_tensor(const_levels), 1).detach().clone()
        latent_in = latent_in.index_select(paddle.to_tensor(var_levels), 1).detach().clone()

    latent_in.stop_gradient = False

    optimizer = optim.Adam(parameters=[latent_in], learning_rate=start_lr)

    frames = []

    pbar = tqdm(range(step))

    latent_n = latent_in
    for i in pbar:
        t = i / step
        lr = get_lr(t, step, start_lr, final_lr)
        optimizer.set_lr(lr)

        if len(const_levels) > 0:
            latent_dict = {}
            for idx, idx2 in enumerate(var_levels):
                latent_dict[idx2] = latent_in[:,idx:idx+1]
            for idx, idx2 in enumerate(const_levels):
                latent_dict[idx2] = (latent_fix[:,idx:idx+1]).detach()
            latent_list = []
            for idx in range(generator.n_latent):
                latent_list.append(latent_dict[idx])
            latent_n = paddle.concat(latent_list, 1)

        img_gen, _ = generator([latent_n], input_is_latent=True, randomize_noise=False)
        frames.append(make_image(img_gen))

        batch, channel, height, width = img_gen.shape

        if height > 256:
            factor = height // 256

            img_gen = img_gen.reshape(
                (batch, channel, height // factor, factor, width // factor, factor)
            )
            img_gen = img_gen.mean([3, 5])

        p_loss = percept(img_gen*masks, (imgs*masks).detach()).sum()
        mse_loss = F.mse_loss(img_gen*masks, (imgs*masks).detach())
        loss = p_loss + mse_weight * mse_loss

        optimizer.clear_grad()
        loss.backward()
        optimizer.step()

        pbar.set_description(
            (
                f"perceptual: {p_loss.numpy()[0]:.4f}; "
                f"mse: {mse_loss.numpy()[0]:.4f}; lr: {lr:.4f}"
            )
        )

    img_gen, _ = generator([latent_n], input_is_latent=True, randomize_noise=False)
    frames.append(make_image(img_gen))

    imgs_seq = [[] for _ in range(img_gen.shape[0])]
    for i in range(img_gen.shape[0]):
        for frame in frames:
            imgs_seq[i].append(frame[i])
    
    return imgs_seq, latent_n


if __name__ == "__main__":
    import argparse
    import os

    from utils import save_video
    from crop import align_face

    parser = argparse.ArgumentParser(
        description="Image projector to the generator latent spaces"
    )
    parser, arg_names = func_args(parser, project)
    parser.add_argument(
        "--no_crop", action="store_true", help="disable to crop input images first"
    )
    parser.add_argument(
        "--save_mp4", action="store_true", help="saving training progress images as mp4 videos"
    )
    parser.add_argument(
        "files", metavar="FILES", nargs="+", help="path to image files to be projected"
    )
    parser.add_argument(
        "--output", type=str, default="./output", help="output directory"
    )

    args = parser.parse_args()

    imgs = []
    masks = []

    for imgfile in args.files:
        if args.no_crop:
            img = Image.open(imgfile)
            imgs.append(img)
            maskfile = '.'.join(imgfile.split('.')[:-1]) + '.mask.' + imgfile.split('.')[-1]
            if os.path.exists(maskfile):
                mask = Image.open(maskfile)
            else:
                mask = Image.new(mode='L', size=img.size, color=255)
            masks.append(mask)
        else:
            img, mask = align_face(imgfile)
            imgs.append(img)
            masks.append(mask)

    imgs_seq, latent_code = project(imgs, masks, **{arg_name: getattr(args, arg_name) for arg_name in arg_names})

    os.makedirs(args.output, exist_ok=True)
    for i, input_name in enumerate(args.files):

        code_name = os.path.join(
            args.output, 
            os.path.splitext(os.path.basename(input_name))[0] + ".pd"
        )
        latent_file = {
            "latent_code": latent_code[i],
        }
        paddle.save(latent_file, code_name)

        img_name = os.path.join(
            args.output, 
            os.path.splitext(os.path.basename(input_name))[0] + "-project.png"
        )
        pil_img = Image.fromarray(imgs_seq[i][-1])
        pil_img.save(img_name)

        if args.save_mp4:
            fps = 30
            duration = 5
            save_video(
                imgs_seq[i],
                os.path.join(
                    args.output, 
                    os.path.splitext(os.path.basename(input_name))[0] + "-project.mp4"
                ),
                fps, duration
            )

7.系统整合

1.png

8.完整源码&环境部署视频教程&自定义UI界面

改进StyleGANv2的容貌年龄预测系统(源码&教程) (mianbaoduo.com)

9.参考文献


[1]徐莹.基于分层结构和稀疏表示的人脸老化模拟研究[J].中南大学.2012.DOI:10.7666/d.y2196806.
[2]Shuang Liu,Dan Li,Tianchi Cao,等.GAN-Based Face Attribute Editing[J].IEEE Access.2020.834854-34867.DOI:10.1109/ACCESS.2020.2974043.
[3]Lanitis, A.,Taylor, C.J.,Cootes, T.F…Toward automatic simulation of aging effects on face images[J].Pattern Analysis & Machine Intelligence, IEEE Transactions on.2002,24(4).442-455.
[4]Volker Blanz,Thomas Vetter.A Morphable Model For The Synthesis Of 3D Faces[J].Computer graphics.1999,33(0).
[5]Alfred Müller.Integral Probability Metrics and Their Generating Classes of Functions[J].Advances in applied probability.1997,29(2).429-443.DOI:10.1017/S000186780002807X.
[6]Bor-Chun Chen,Chu-Song Chen,Winston H. Hsu.Cross-Age Reference Coding for Age-Invariant Face Recognition and Retrieval[C].2014
[7]Jia Deng,Wei Dong,Socher, R.,等.ImageNet: A large-scale hierarchical image database[C].

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:15:35  更:2022-10-22 21:17:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 22:24:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码