美文网首页
艺术字生成

艺术字生成

作者: 巨鹿lx | 来源:发表于2020-09-29 10:52 被阅读0次

问题

QQ20201026-0.JPG
演示文稿1.png

计划

2、学习网络中每一层的结构和计算方法。

机器学习深度学习中一些简单易懂的知识

训练时可以做损失函数曲线,去观察LOSS的趋势,看是否收敛,到哪一点才收敛。

测试

每次处理一张图片1X3X64X64,使用real_A 与 real_Colors(也就是样式参考集,有四张图片,1X12X64X64,目的是提出去样式的特征。)
输出fake_C, 最后展示fake_C,real_C。测试的时候label没有用。

然后,我们输入在预训练和微调过程中看不见的一些内容参考字符的字形图像,并得到相应的合成字形图像。 令人惊讶的是,这些未探索字符的合成结果的质量与探索字符的质量相当(见图17)。

进行测试 发现确实是文章中说的效果一样,划分测试集,和不划分测试集 效果相差不多(基本上没有区别)。

微调

就是n=30, m=4,
G的结构中也能够看出,输入的是train中的real_A, 和style中的real_Colors,输出是fake_C,fake_B,而他们是使用来更新模型的。

微调使用12种字体,然后每一种分为train 639,style 100,val 326,train包含所有的字,style val属于train,style 与 val的重合数量为51。

label属性的作用

  • 若当前的这张 图A 他出现在 style的100张文件中,那么就把这张图标记为label。 forward的时候图A 的特征已经被网络学习过了

  • 在计算Loss时, L_adv、L_local loss 是 预测值 与 real 值进行比较,这个real值,可以是 groundtruth 也可以是style 中的random。 计算L1 loss 和 contextual loss 时 ,若 label 为零 则 不参与loss的计算。

论文内容

which output score maps instead of a single value.

self.criterionL1 = torch.nn.L1Loss(reduction='none')
self.criterionL1_reduce = torch.nn.L1Loss()
两者的区别
visualizer = Visualizer(opt)

先不看

配置

  • scipy 1.5.2没有imresize方法,scipy1.1.0与其他的库不兼容,如numpy
  • kill -9 PID 杀死进程
  • Jupyter Notebook

效果

  • 适用的字体并不像TET-GAN那样广泛(牛仔布,霓虹灯),仅仅是一些笔画从柔和到僵硬,色彩纹理上加一些渐变色透明色,样式的复杂程度是远小于TET-GAN的。

  • 微调:数据集 每个字体639 X 12个字体 ,流程和网络基本都是一模一样,只有lamada参数会有一些不一样。

代码

1、 加载数据集,形成dataloader
2、 加载model,实例化agisnet-model
3、 定义每一个子model(G ,D, D_B, D_local)
4、 model.setup(opt),设置每一个模型的学习率

        def lambda_rule(epoch):
            lr_l = 1.0 - max(0, epoch - opt.niter) / float(opt.niter_decay + 1)
            return lr_l
        scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule)

5、 可视化效果

        if aspect_ratio > 1.0:
            im = imresize(im, (h, int(w * aspect_ratio)), interp='bicubic')
        if aspect_ratio < 1.0:
            im = imresize(im, (int(h / aspect_ratio), w), interp='bicubic')
        由于更新版本需要进行方法的替换
        from PIL import Image
        img = np.array(Image.fromarray(myImage).resize((num_px, num_px)))

另外本机vimdom不可用,服务器可用
6、
生成器分析


生成器由一个个block组成,每一个block都是一些卷积层的实现,和一些激活函数等。
每一个block都有一个子模块,这些block组成递归的形式,一个UNET的结构。

class AGISNet(nn.Module):

    def __init__():
        super(AGISNet, self).__init__()
        max_nchn = 8  # max channel factor
        # construct unet structure
        dual_block = AGISNetBlock(ngf*max_nchn, ngf*max_nchn, ngf*max_nchn, ngf*max_nchn,
                                  use_spectral_norm=use_spectral_norm, innermost=True,
                                  norm_layer=norm_layer, nl_layer=nl_layer, upsample=upsample, wo_skip=wo_skip)
        ......

        self.model = dual_block
  
    def forward(self, content, style):
        return self.model(content, style)

而UNET层中,不同的层有不同的数据传播方式、

The same test set as MC-GAN is used to fine-tune our model in few-shot learning,

使用这种特殊的跳连接体系结构的目的是,在不同的尺度上的特征都是重要的。 例如,较高级别的特征包含更抽象的样式信息,而较低级别的特征包含更具体的样式信息,我们打算尽可能地利用它们,让模型学习有效和足够的信息。 我们建议在我们的模型中使用两个分离的解码器,主要是因为与颜色纹理相比,字形图像中的形状变化要大得多(每个字都不一样,但是样式是相似的)。 有了这样的网络架构,我们的模型可以更多地关注形状风格。

    def forward(self, content, style):

        x1 = self.down1(content)
        x2 = self.down2(style)
        if self.outermost:  # 最顶层的UNET
            mid_C, mid_B = self.submodule(x1, x2) # 第一层,递归调用子模块,得到midC midB 供上采样使用
            fake_B = self.up_B(mid_B)
            mid_C2 = self.up(mid_C)
            fake_C = self.up_out(torch.cat([mid_C2, fake_B], 1))  # 上采样的每一层都要结合字形的特点
            return fake_C, fake_B
        elif self.innermost:  # 最底层的UNET
            mid_C = torch.cat([x1, x2], 1)  # 若x的维度是a,b,那么cat之后就是了2·a·b 变成 a·2b
            mid_B = torch.cat([x1, x2], 1)  # 这里直接是将content与style整合到了一起。
            fake_C = self.up(mid_C)
            fake_B = self.up_B(mid_B)
            tmp1 = torch.cat([content, style], 1) # 最底层,up的时候  content,style是低维特征,需要结合使用
            if self.wo_skip:
                return fake_C, fake_B
            else:
                return torch.cat([torch.cat([fake_C, fake_B], 1), tmp1], 1), torch.cat([fake_B, tmp1], 1)  # 返回样式的时候需要结合字形和低维特征,返回字形的时候,需要结合低维特征
        else:  # 中间层
            mid, mid_B = self.submodule(x1, x2)
            fake_C = self.up(mid)
            fake_B = self.up_B(mid_B)
            tmp1 = torch.cat([content, style], 1)
            if self.wo_skip:  # 永远都是跳跃链接
                return fake_C, fake_B
            else:
                return torch.cat([torch.cat([fake_C, fake_B], 1), tmp1], 1), torch.cat([fake_B, tmp1], 1)

7、VGG网络的使用

AGIS-Net的forward()里,通过net(G) 生成灰度图像和样式图像,经过VGG网络的提取

        self.vgg_fake_C = self.vgg19(self.fake_C)
        self.vgg_real_C = self.vgg19(self.real_C)
        self.vgg_fake_B = self.vgg19(self.fake_B)
        self.vgg_real_B = self.vgg19(self.real_B)

8、forward()做了什么呢?

  • 生成fake_c,fake_b
  • 使用VGG提取出了图片的特征
  • local blocks 用于D_{local}的输入数据,fake_C_blocks

9、update_G()做了什么?

  • 做一些梯度下降的必要工作...主要是backward_G()
  • L_{adv}: 由两部分组成,字形和样式图片。 loss_G_GAN, loss_G_GAN_B, 分别来自netDnetD_B(for shape)
  • L_1: 同样是有两部分组成,字形和样式图片,loss_G_L1, loss_G_L1_B
  • L_{contextual.loss} 上下文Loss, 两部分:loss_G_CX,loss_G_CX_B比较难,先不看
  • L_{local}: 只要一个输入fake_C_blocks,使用的是公用的backward_G_GAN()方法
  • 鉴别器生成的是100X1X12X12的矩阵
  • condition_D其实指的是cGAN结构,是不是需要一个标签,而这个标签就是real_A。

10、G、D 的网络结构
G 的网络结构

DataParallel(
  (module): AGISNet(
    (model): AGISNetBlock(
      (up_out): Sequential(
        (0): ReLU(inplace=True)
        (1): Conv2d(35, 3, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (2): Tanh()
      )
      (down1): Sequential(
        (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
      )
      (down2): Sequential(
        (0): Conv2d(12, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
      )
      (submodule): AGISNetBlock(
        (down1): Sequential(
          (0): LeakyReLU(negative_slope=0.2, inplace=True)
          (1): Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
          (2): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
        )
        (down2): Sequential(
          (0): LeakyReLU(negative_slope=0.2, inplace=True)
          (1): Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
          (2): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
        )
        (submodule): AGISNetBlock(
          (down1): Sequential(
            (0): LeakyReLU(negative_slope=0.2, inplace=True)
            (1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
            (2): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
          )
          (down2): Sequential(
            (0): LeakyReLU(negative_slope=0.2, inplace=True)
            (1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
            (2): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
          )
          (submodule): AGISNetBlock(
            (down1): Sequential(
              (0): LeakyReLU(negative_slope=0.2, inplace=True)
              (1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
              (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
            )
            (down2): Sequential(
              (0): LeakyReLU(negative_slope=0.2, inplace=True)
              (1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
              (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
            )
            (submodule): AGISNetBlock(
              (down1): Sequential(
                (0): LeakyReLU(negative_slope=0.2, inplace=True)
                (1): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
                (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
              )
              (down2): Sequential(
                (0): LeakyReLU(negative_slope=0.2, inplace=True)
                (1): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
                (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
              )
              (submodule): AGISNetBlock(
                (down1): Sequential(
                  (0): LeakyReLU(negative_slope=0.2, inplace=True)
                  (1): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
                )
                (down2): Sequential(
                  (0): LeakyReLU(negative_slope=0.2, inplace=True)
                  (1): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
                )
                (up): Sequential(
                  (0): ReLU(inplace=True)
                  (1): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
                  (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
                )
                (up_B): Sequential(
                  (0): ReLU(inplace=True)
                  (1): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
                  (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
                )
              )
              (up): Sequential(
                (0): ReLU(inplace=True)
                (1): ConvTranspose2d(1024, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
                (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
                (3): Dropout(p=0.5, inplace=False)
              )
              (up_B): Sequential(
                (0): ReLU(inplace=True)
                (1): ConvTranspose2d(768, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
                (2): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
                (3): Dropout(p=0.5, inplace=False)
              )
            )
            (up): Sequential(
              (0): ReLU(inplace=True)
              (1): ConvTranspose2d(1024, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
              (2): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
            )
            (up_B): Sequential(
              (0): ReLU(inplace=True)
              (1): ConvTranspose2d(768, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
              (2): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
            )
          )
          (up): Sequential(
            (0): ReLU(inplace=True)
            (1): ConvTranspose2d(512, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
            (2): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
          )
          (up_B): Sequential(
            (0): ReLU(inplace=True)
            (1): ConvTranspose2d(384, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
            (2): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
          )
        )
        (up): Sequential(
          (0): ReLU(inplace=True)
          (1): ConvTranspose2d(256, 32, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
          (2): InstanceNorm2d(32, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
        )
        (up_B): Sequential(
          (0): ReLU(inplace=True)
          (1): ConvTranspose2d(192, 32, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
          (2): InstanceNorm2d(32, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
        )
      )
      (up): Sequential(
        (0): ReLU(inplace=True)
        (1): ConvTranspose2d(128, 32, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
        (2): InstanceNorm2d(3, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
      )
      (up_B): Sequential(
        (0): ReLU(inplace=True)
        (1): ConvTranspose2d(96, 3, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
        (2): Tanh()
      )
    )
  )
)

第一个 D for style and shape

Sequential(
  (0): Conv2d(6, 32, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
  (1): LeakyReLU(negative_slope=0.2, inplace=True)
  (2): Conv2d(32, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
  (3): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
  (4): LeakyReLU(negative_slope=0.2, inplace=True)
  (5): Conv2d(64, 128, kernel_size=(4, 4), stride=(1, 1), padding=(1, 1))
  (6): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
  (7): LeakyReLU(negative_slope=0.2, inplace=True)
  (8): Conv2d(128, 1, kernel_size=(4, 4), stride=(1, 1))
)

第二个 D for shape

Sequential(
  (0): Conv2d(6, 32, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
  (1): LeakyReLU(negative_slope=0.2, inplace=True)
  (2): Conv2d(32, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
  (3): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
  (4): LeakyReLU(negative_slope=0.2, inplace=True)
  (5): Conv2d(64, 128, kernel_size=(4, 4), stride=(1, 1), padding=(1, 1))
  (6): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
  (7): LeakyReLU(negative_slope=0.2, inplace=True)
  (8): Conv2d(128, 1, kernel_size=(4, 4), stride=(1, 1))
)

第三个 D for local

DataParallel(
  (module): D_NLayers(
    (model): Sequential(
      (0): Conv2d(6, 32, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
      (1): LeakyReLU(negative_slope=0.2, inplace=True)
      (2): Conv2d(32, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))
      (3): InstanceNorm2d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
      (4): LeakyReLU(negative_slope=0.2, inplace=True)
      (5): Conv2d(64, 128, kernel_size=(4, 4), stride=(1, 1), padding=(1, 1))
      (6): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
      (7): LeakyReLU(negative_slope=0.2, inplace=True)
      (8): Conv2d(128, 1, kernel_size=(4, 4), stride=(1, 1))
    )
  )
)

11、图片的裁剪操作

Image.open(s_path).convert('RGB').crop((w, 0, w+w, h)).filter(
       ImageFilter.GaussianBlur(radius=(random.random()*2+2)))

对于彩色图像,不管其图像格式是PNG,还是BMP,或者JPG,在PIL中,使用Image模块的open()函数打开后,返回的图像对象的模式都是“RGB”。而对于灰度图像,不管其图像格式是PNG,还是BMP,或者JPG,打开后,其模式为“L”。

滤波器

import random
from PIL import Image
from PIL import ImageFilter
def image_filters_test():
    im = Image.open("1.jpg")
    # 预定义的图像增强滤波器
    im_blur = im.filter(ImageFilter.GaussianBlur(radius=(random.random()*2+2)))
    im_contour = im.filter(ImageFilter.CONTOUR)
    im_min = im.filter(ImageFilter.MinFilter(3))
    im.show()
    im_blur.show()
    im_contour.show()
    im_min.show()

12、 使用模糊负样本

def generate_random_block(self, input, target, blurs):
#  100X3X64X64    100X12X64X64    100X12X64X64
#  通过随机获得2个3X32X32的拼接,进行100次,组成一个数组返回,100X6X32X32
    return inp_blk, tar_blk, blr_blk
  • 在更新生成器的时候,计算local adv loss,使用到了fake_C_blocks
  • 更新鉴别器的时候,使用到了real_color_blocks,fake_C_blocks,blur_color_blocks这三个都被用到了计算Loss
    def backward_D(self, netD, real, fake, blur=None):
        # Fake, stop backprop to the generator by detaching fake_B
        pred_fake = netD(fake.detach())
        # real
        pred_real = netD(real)
        # blur
        loss_D_blur = 0.0
        if blur is not None:
            pred_blur = netD(blur)
            loss_D_blur, _ = self.criterionGAN(pred_blur, False)

        loss_D_fake, _ = self.criterionGAN(pred_fake, False)
        loss_D_real, _ = self.criterionGAN(pred_real, True)
        # Combined loss
        loss_D = loss_D_fake + loss_D_real + loss_D_blur
        loss_D.backward()
        return loss_D, [loss_D_fake, loss_D_real, loss_D_blur]

13、CXLoss上下文损失的计算

self.vgg_layers = ['conv3_3', 'conv4_2']
 cx, cx_batch = self.criterionCX(self.vgg_real_C[l], self.vgg_fake_C[l])  #正负样本传入VGG网络的conv3_3特征,提取出来进行对比。

meanT = featureT.mean(0, keepdim=True).mean(2, keepdim=True).mean(3, keepdim=True)
return featureI - meanT, featureT - meanT  # 1、 都减去real_C的平均值为 1X256X1X1

norms = features.norm(p=2, dim=1, keepdim=True)
features = features.div(norms)  # T和I  都除以自己的范数

for i in range(N):  # 其实就是遍历 batch_size
  
#T 走这个方法,使用返回值与  I 共同计算余弦距离
def patch_decomposition(self, features):  # NCHW --> 1x1xCXHW --> HWxCx1x1
 
dist_i = F.conv2d(featureI_i, featureT_patch)  # 计算batch_size中所有的余弦距离,并把它们都加入到dist数组当中。
dist.append(dist_i)

dist = torch.cat(dist, dim=0)  # 对dist数组进行处理
raw_dist = (1. - dist) / 2.
epsilon = 1e-5
div = torch.min(raw_dist, dim=axis, keepdim=True)[0]   # 返回值有两项,content,index,[0]的操作是排除掉index的信息,keepdim保持shape信息。
relative_dist = raw_dist / (div + epsilon)

W = torch.exp((self.b - dist) / self.sigma)
W_sum = W.sum(dim=axis, keepdim=True)
return W.div(W_sum)  # 这一步 得出 CX_ij

CX = CX.max(dim=3)[0].max(dim=2)[0]
CX = CX.mean(1)
CX_B = -torch.log(CX)
CX = torch.mean(CX_B)
return CX, CX_B  # 计算contextual loss使用的CX_B

相关文章

网友评论

      本文标题:艺术字生成

      本文链接:https://www.haomeiwen.com/subject/nfzsuktx.html