关于人工智能:使用机器学习创建自己的Emojis-表情

8次阅读

共计 3407 个字符,预计需要花费 9 分钟才能阅读完成。

对于图像生成方向目前通常应用的办法是生成反抗网络或扩散模型。只管这两种办法有的不同的特点,然而他们的一个共同点是模型训练对机器资源的要求很高,如果咱们要以一种全新的格调创立一个图像,模型将须要从头开始训练,这可能须要更多的工夫和资源,例如比拟相熟的 StyleGan[3]是在领有 8 个 Tesla V100 gpu 的 NVIDIA DGX- 1 上训练了大概一周的工夫。

然而,如果咱们没有这样的硬件资源和工夫怎么办?是否能够玩转图像生成?在本文中,咱们将形容一种图像生成办法,该办法无需额定的模型训练和低廉的设施就能够在不同的图像格调之间切换。

指标

咱们的次要指标是创立一个通用的嵌入提取器。这个嵌入提取器用于比拟图像和表情符号的各个局部。而后咱们应用它来创立一个生成各种款式的图像的图像生成器。在本文中,将思考两种创立嵌入提取器的办法,这两种办法会在上面具体阐明。所以首先,咱们为生成器和训练嵌入提取器筹备一个数据集。

数据集

用到的数据集是蕴含了须要创立的头像各个部件,因为须要通过组合这些部件来生成图像。那么如何创立这个数据集呢,最间接的办法是能够手动创立每个独自的部件,然而这种办法太慢并且不灵便。所以这里抉择了一个更加灵便和省时的办法:创立多个模板,并将这些模板互相组合。

咱们能够创立五种类型的眼睛、嘴巴和脸型,通过组合能够为咱们提供 125 种不同的表情符号。

所以这里咱们筹备了一个 Python 的脚本,来生成这些部件的模板,这些模板咱们应用 SVG 格局保留。生成后模板当前,就须要从每个部件中提取嵌入,而后将它们保留为“avatar_part – embedding”对。通过这个配对,咱们就可能在不同的艺术风格之间疾速切换,而无需额定的模型训练。咱们能够通过两种不同的办法来创立这个嵌入:1、通过预训练的 ResNet50;2、通过定制的自编码器。预训练 ResNet50 不须要额定的训练,但自编码器则须要从新训练会破费一些工夫。

为了达到更好的成果,对于预训练的 ResNet50 可能还须要进一步的微调,然而无论应用那种形式咱们都须要一些数据集进行训练或微调,所以还须要有一个数据集来反对这样工作。所以这里咱们抉择了 CELEBA 的数据集(这个应该算是最大的人脸数据集了)。应用脚本将数据集的人脸宰割成段并将它们保留到文件夹中。当初,咱们有了嘴巴数据集、眼睛数据集等。在这个脚本中应用 BiSeNet [1] 进行人脸宰割,因为这些都是现成的不须要咱们额定的工作。

模型架构

架构是通过一个输出层、一个输入层和三个暗藏层来示意。输出层获取一张图像,将它转换成 306×306 像素大小。

在第一个暗藏层中,通过 BiSeNet[1]对人脸进行分段宰割。

在第二个暗藏层中是嵌入提取模型,他返回每个部件的提取的特色

在第三个暗藏层中,咱们将第二个暗藏层的的每一个输入与每个可能的表情符号局部进行比拟。而后通过计算余弦类似度实现比拟

第三个暗藏层的输入是与面部余弦类似度最大的表情符号。最初,输入层是一个创立表情函数,将这些局部进行组合生成残缺的表情符号,整个流程如下:

这个架构能够总结为三层:

宰割模型,将一张自拍分成几个人脸片段。在这种状况下,宰割模型 [1] 与人脸裁剪模型 [2] 一起工作

嵌入模型,将每个人脸段转换为嵌入空间。如前所述,可选 ResNet50 和主动编码器或其余的任意架构

余弦类似度,它将人脸嵌入与所有雷同类型的部件嵌入进行比拟

一些钻研

尽管咱们的模型是由几个神经网络组成的,但构造相并不简单。正如在下面所写的,这个合成将最类似的头像局部与脸部片段通过余弦类似度对嵌入进行匹配,而后将它们组合。但这里也有一些次要问题须要确认:

1、如何能力精确地失去这些嵌入,从而使比拟有意义?

如果抉择应用 ResNet50,则须要将获取分类头(最初一层)之外的特色,对于该模型只须要去掉最初一层。

对于自编码器,它是无监督解决方案,嵌入空间将是主动编码器中的压缩线性层,咱们将在图像比拟中应用它。

2、嵌入可视化

出于钻研目标,咱们还编写了一个用于嵌入可视化的脚本,该脚本获取一个视频文件作为输出,返回一个带有嵌入图形的视频文件作为输入。在这个脚本中,咱们失去嵌入为每帧头像的每个局部和面部图像的图。粉色代表人脸嵌入,紫色代表头像嵌入。

3、ResNet50 的后果

咱们应用通过预训练的 ResNet50 进行测试。看看上面的后果:

测试对象什么也不做,但嵌入在每一帧都在变动。因为嵌入彼此之间没有太大差别,并且它们的维度太多,因而无奈清晰地生成头像。即便测试对象的情绪发生变化(例如,他们的脸上呈现微笑),嵌入依然放弃不变(见图)。

4、自编码器作为嵌入提取器

ResNet50 的体现并不好,那么自编码器呢?咱们应用上面的架构:

代码如下:

class FaceAutoencoder(nn.Module):
   def __init__(self):
       super(FaceAutoencoder, self).__init__()
       self.conv1 = nn.Sequential(nn.Conv2d(3, 16, 3, stride=2, padding=1),
           nn.ReLU(),)
       self.conv2 = nn.Sequential(nn.Conv2d(16, 64, 3, stride=2, padding=1),
           nn.ReLU(),)
       self.conv3 = nn.Sequential(nn.Conv2d(64, 128, 5, stride=3, padding=2),
           nn.ReLU(),)
       self.conv4 = nn.Sequential(nn.Conv2d(128, 256, 5, stride=3, padding=1),
           nn.ReLU(),)
       self.fc_conv = nn.Sequential(nn.Flatten(1),
           nn.Linear(16384, 8),
           nn.ReLU(),)
       self.fc_deconv = nn.Sequential(nn.Linear(8, 16384),
           nn.ReLU(),
           nn.Unflatten(dim=1, unflattened_size=[256, 8, 8])
       )
       self.deconv1 = nn.Sequential(nn.ConvTranspose2d(256, 128, 5, stride=3, padding=0),
           nn.ReLU(),)
       self.deconv2 = nn.Sequential(nn.ConvTranspose2d(128, 64, 5, stride=3, padding=2, output_padding=1),
           nn.ReLU(),)
       self.deconv3 = nn.Sequential(nn.ConvTranspose2d(64, 16, 3, stride=2, padding=1, output_padding=0),
           nn.ReLU(),)
       self.deconv4 = nn.Sequential(nn.ConvTranspose2d(16, 3, 3, stride=2, padding=1, output_padding=1),
           nn.ReLU(),)
 
   def encode(self, x):
       x = self.conv1(x)
       x = self.conv2(x)
       x = self.conv3(x)
       x = self.conv4(x)
       x = self.fc_conv(x)
       return x
      
   def decode(self, x):
       x = self.fc_deconv(x)
       x = self.deconv1(x)
       x = self.deconv2(x)
       x = self.deconv3(x)
       x = self.deconv4(x)
       return x
 
   def forward(self, x):
       x = self.encode(x)
       x = self.decode(x)
       return x

上面是来自 CELEBA 数据集的人脸片段和来自 avatar 数据集的人脸局部的嵌入图。能够看到这些图表有一些相似之处。

咱们来试试这个模型的后果:

应用主动编码器办法,嵌入更好一些,小烦扰只会轻微影响最终后果。

总结

咱们能够看到,通过这种形式,简直不须要进行任何的大型训练就能够在不同格调之间进行切换,但这里的问题也很显著嵌入提取器(第二个暗藏层)还有很大的改良空间。

https://avoid.overfit.cn/post/65e58d9868e349ed8380b6a980112dba

作者:Akvelon

正文完
 0