Python 迁移学习实用指南:6~11(5)

简介: Python 迁移学习实用指南:6~11(5)

Python 迁移学习实用指南:6~11(4)https://developer.aliyun.com/article/1426856

建立我们的图像字幕编解码器深度学习模型

现在,我们拥有构建模型所需的所有基本组件和工具。 如前所述,我们将使用编码器-解码器深度学习模型架构来构建图像捕获系统。

以下代码帮助我们构建此模型的架构,在该模型中,我们将成对的图像特征和字幕序列作为输入,以预测每个时间步长的字幕中的下一个可能单词:

from keras.models import Sequential, Model 
from keras.layers import LSTM, Embedding, TimeDistributed, Dense, RepeatVector, Activation, Flatten, concatenate 
DENSE_DIM = 256 
EMBEDDING_DIM = 256 
MAX_CAPTION_SIZE = max_caption_size 
VOCABULARY_SIZE = vocab_size 
image_model = Sequential() 
image_model.add(Dense(DENSE_DIM, input_dim=4096, activation='relu')) 
image_model.add(RepeatVector(MAX_CAPTION_SIZE)) 
language_model = Sequential() 
language_model.add(Embedding(VOCABULARY_SIZE, EMBEDDING_DIM, input_length=MAX_CAPTION_SIZE)) 
language_model.add(LSTM(256, return_sequences=True)) 
language_model.add(TimeDistributed(Dense(DENSE_DIM))) 
merged_output = concatenate([image_model.output, language_model.output]) 
merged_output = LSTM(1024, return_sequences=False)(merged_output) 
merged_output = (Dense(VOCABULARY_SIZE))(merged_output) 
merged_output = Activation('softmax')(merged_output) 
model = Model([image_model.input, language_model.input], merged_output) 
model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy']) 
model.summary()

前面的代码的输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99eTb2jF-1681567330255)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/0864c303-7dad-4af7-a015-f9cb1c2dbaba.png)]

从前面的架构中我们可以看到,我们有一个图像模型,该模型更着重于处理基于图像的特征作为其输入,而语言模型则利用 LSTM 来处理每个图像标题中流入的单词序列。 最后一层是 softmax 层,具有 7,927 个单元,因为我们的词汇表中总共有 7,927 个唯一词,并且字幕中的下一个预测词将是其中一个作为输出生成的词。 我们还可以使用以下代码片段来可视化我们的模型架构:

from IPython.display import SVG 
from keras.utils.vis_utils import model_to_dot 
SVG(model_to_dot(model, show_shapes=True, show_layer_names=False,  
    rankdir='TB').create(prog='dot', format='svg'))

前面的代码的输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RmkmMYdh-1681567330255)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/66a126af-3f84-48bf-bba6-b93cef70aa7e.png)]

训练我们的图像字幕深度学习模型

在开始训练模型之前,由于我们正在处理模型中的一些复杂组件,因此在模型的准确率在整个连续周期中都达到稳定状态的情况下,我们会在模型中使用回调来降低学习率。 这对于在不停止训练的情况下即时更改模型的学习率非常有帮助:

from keras.callbacks import ReduceLROnPlateau 
reduce_lr = ReduceLROnPlateau(monitor='loss', factor=0.15, 
                              patience=2, min_lr=0.000005)

让我们现在训练我们的模型! 我们已经将模型训练到大约 30 到 50 个周期,并在大约 30 个周期和 50 个周期保存了模型:

BATCH_SIZE = 256 
EPOCHS = 30 
cap_lens = [(cl-1) for cl in tc_tokens_length] 
total_size = sum(cap_lens) 
history = model.fit_generator( 
  dataset_generator(processed_captions=processed_train_captions,  
                    transfer_learnt_features=train_img_features,  
                    vocab_size=VOCABULARY_SIZE,  
                    max_caption_size=MAX_CAPTION_SIZE, 
                    batch_size=BATCH_SIZE),  
  steps_per_epoch=int(total_size/BATCH_SIZE),  
  callbacks=[reduce_lr], 
  epochs=EPOCHS, verbose=1) 
Epoch 1/30 
1617/1617 - 724s 448ms/step - loss: 4.1236 - acc: 0.2823 
Epoch 2/30 
1617/1617 - 725s 448ms/step - loss: 3.9182 - acc: 0.3150 
Epoch 3/30 
1617/1617 - 724s 448ms/step - loss: 3.8286 - acc: 0.3281 
... 
... 
Epoch 29/30 
1617/1617 - 724s 447ms/step - loss: 3.6443 - acc: 0.3885 
Epoch 30/30 
1617/1617 - 724s 448ms/step - loss: 3.4656 - acc: 0.4078 
model.save('ic_model_rmsprop_b256ep30.h5')

保存该模型后,我们将继续训练并对其进行另外 20 个周期的训练,并在50处停止。 当然,您也可以随意在 Keras 中使用模型检查点定期自动保存它:

EPOCHS = 50 
history_rest = model.fit_generator( 
   dataset_generator(processed_captions=processed_train_captions,  
                     transfer_learnt_features=train_img_features,  
                     vocab_size=VOCABULARY_SIZE,  
                     max_caption_size=MAX_CAPTION_SIZE, 
                     batch_size=BATCH_SIZE),  
   steps_per_epoch=int(total_size/BATCH_SIZE),  
   callbacks=[reduce_lr], 
   epochs=EPOCHS, verbose=1, initial_epoch=30) 
Epoch 31/50 
1617/1617 - 724s 447ms/step - loss: 3.3988 - acc: 0.4144 
Epoch 32/50 
1617/1617 - 724s 448ms/step - loss: 3.3633 - acc: 0.4184 
... 
... 
Epoch 49/50 
1617/1617 - 724s 448ms/step - loss: 3.1330 - acc: 0.4509 
Epoch 50/50 
1617/1617 - 724s 448ms/step - loss: 3.1260 - acc: 0.4523 
model.save('ic_model_rmsprop_b256ep50.h5')

这样就结束了我们的模型训练过程; 我们已经成功地训练了图像字幕模型,并可以开始使用它来为新图像生成图像字幕。

模型训练技巧:图像字幕模型通常使用大量数据,并且在训练过程中涉及许多参数。 建议使用生成器来构建和生成数据以训练深度学习模型。 否则,您可能会遇到内存问题。 另外,在带有 Tesla K80 GPU 的 Amazon AWS p2.x 实例上,该模型在每个周期运行将近 12 分钟,因此请考虑在 GPU 上构建该模型,因为在传统系统上进行训练可能会花费很长时间。

我们还可以根据训练过程中的不同周期,查看有关模型准确率,损失和学习率的趋势:

epochs = list(range(1,51)) 
losses = history.history['loss'] + history_rest.history['loss']  
accs = history.history['acc'] + history_rest.history['acc']  
lrs = history.history['lr'] + history_rest.history['lr'] 
f, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(14, 4)) 
title = f.suptitle("Model Training History", fontsize=14) 
f.subplots_adjust(top=0.85, wspace=0.35) 
ax1.plot(epochs, losses, label='Loss') 
ax2.plot(epochs, accs,  label='Accuracy') 
ax3.plot(epochs, lrs, label='Learning Rate') 
ax1.set_xlabel('Epochs') 
ax2.set_xlabel('Epochs') 
ax3.set_xlabel('Epochs') 
ax1.set_ylabel('Loss') 
ax2.set_ylabel('Accuracy') 
ax3.set_ylabel('Learning Rate')

前面的代码的输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZZamnF3w-1681567330256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/8761e861-000e-4a0a-a02a-c0c16476bb84.png)]

我们可以看到,在第 28 和 29 阶段,准确率略有下降,损失增加了,这导致我们的回调成功降低了学习率,从第 30 阶段开始提高了准确率。 这无疑为我们提供了有关模型行为的有用见解!

评估我们的图像字幕深度学习模型

训练模型而不评估其表现根本没有任何意义。 因此,我们现在将在测试数据集上评估深度学习模型的表现,该数据集与Flickr8K数据集共有 1000 幅不同的图像。 我们从加载通常的依赖关系开始(如果您还没有的话):

import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt 
pd.options.display.max_colwidth = 500 
%matplotlib inline

加载数据和模型

下一步包括将必要的数据,模型和其他资产从磁盘加载到内存中。 我们首先加载测试数据集和训练有素的深度学习模型:

# load test dataset 
test_df = pd.read_csv('image_test_dataset.tsv', delimiter='\t') 
# load the models 
from keras.models import load_model 
model1 = load_model('ic_model_rmsprop_b256ep30.h5') 
model2 = load_model('ic_model_rmsprop_b256ep50.h5')

现在,我们需要加载必要的元数据资产,例如之前为测试数据提取的图像特征以及词汇元数据:

from sklearn.externals import joblib 
tl_img_feature_map = joblib.load('transfer_learn_img_features.pkl') 
vocab_metadata = joblib.load('vocabulary_metadata.pkl') 
word_to_index = vocab_metadata['word2index'] 
index_to_word = vocab_metadata['index2word'] 
max_caption_size = vocab_metadata['max_caption_size'] 
vocab_size = vocab_metadata['vocab_size']

了解贪婪和集束搜索

为了从基于深度学习的神经图像字幕模型生成预测,请记住,它不像基本分类或分类模型那样简单。 我们将需要根据输入图像特征在每个时间步从模型中生成一系列单词。 有多种方式为字幕生成这些单词序列。

一种方法称为采样贪婪搜索,我们从令牌开始,输入图像特征,然后基于 LSTM 输出中的p1来生成第一个单词。 然后,我们将相应的预测词嵌入作为输入,并根据来自下一个 LSTM 的p2生成下一个词(以我们之前讨论的展开形式)。 继续此步骤,直到到达令牌(表示字幕结束)为止,或者达到基于预定义阈值的令牌的最大可能长度。

第二种方法称为集束搜索,它比基于贪婪的搜索更有效,在基于贪婪的搜索中,我们在考虑到每个单词之前生成的单词的基础上,根据最高概率在每个步骤中选择最可能的单词顺序,这正是采样的作用。 集束搜索扩展了贪婪搜索技术,并始终返回最可能输出的项的序列的列表。 因此,在构建每个序列时,为了在时间步t+1生成下一项,而不是进行贪婪搜索并生成最可能的下一项,迭代地考虑了一组k最佳句子基于下一步扩展到所有可能的下一词。 k的值通常是用户指定的参数,用于控制进行平行搜索或集束搜索以生成字幕序列的总数。 因此,在集束搜索中,我们以k最可能的单词作为字幕序列中的第一时间步输出开始,并继续生成下一个序列项,直到其中一个达到结束状态为止。 涵盖围绕集束搜索的详细概念的全部范围将不在当前范围之内,因此,如果您有兴趣,我们建议您查看有关 AI 上下文中集束搜索的任何标准文献。

实现基于集束搜索的字幕生成器

现在,我们将实现一种基于集束搜索的基本算法来生成字幕序列:

from keras.preprocessing import image, sequence 
def get_raw_caption_sequences(model, word_to_index, image_features, 
                              max_caption_size, beam_size=1): 
    start = [word_to_index['<START>']] 
    caption_seqs = [[start, 0.0]] 
    while len(caption_seqs[0][0]) < max_caption_size: 
        temp_caption_seqs = [] 
        for caption_seq in caption_seqs: 
            partial_caption_seq = sequence.pad_sequences( 
                                               [caption_seq[0]],  
                                               maxlen=max_caption_size,  
                                               padding='post') 
            next_words_pred = model.predict( 
                                   [np.asarray([image_features]),    
                                   np.asarray(partial_caption_seq)])[0] 
            next_words = np.argsort(next_words_pred)[-beam_size:] 
            for word in next_words: 
                new_partial_caption, new_partial_caption_prob =   
                                      caption_seq[0][:], caption_seq[1] 
                new_partial_caption.append(word) 
                new_partial_caption_prob += next_words_pred[word] 
                temp_caption_seqs.append([new_partial_caption,  
                                          new_partial_caption_prob]) 
        caption_seqs = temp_caption_seqs 
        caption_seqs.sort(key = lambda item: item[1]) 
        caption_seqs = caption_seqs[-beam_size:] 
    return caption_seqs

这有助于我们使用集束搜索基于输入图像特征生成字幕。 但是,它是在每个步骤中基于先前标记的原始标记序列。 因此,我们将在此基础上构建一个包装器函数,该函数将利用先前的函数生成一个纯文本句子作为输入图像的标题:

def generate_image_caption(model, word_to_index_map, index_to_word_map, 
                           image_features, max_caption_size,   
                           beam_size=1): 
    raw_caption_seqs = get_raw_caption_sequences(model=model,  
                                     word_to_index=word_to_index_map,  
                                     image_features=image_features,  
                                     max_caption_size=max_caption_size,  
                                     beam_size=beam_size) 
    raw_caption_seqs.sort(key = lambda l: -l[1]) 
    caption_list = [item[0] for item in raw_caption_seqs] 
    captions = [[index_to_word_map[idx] for idx in caption]  
                                   for caption in caption_list] 
    final_captions = [] 
    for caption in captions: 
        start_index = caption.index('<START>')+1 
        max_len = len(caption)  
                   if len(caption) < max_caption_size  
                   else max_caption_size 
        end_index = caption.index('<END>')  
                   if '<END>' in caption  
                   else max_len-1 
        proc_caption = ' '.join(caption[start_index:end_index]) 
        final_captions.append(proc_caption) 
    return final_captions

我们还需要之前的字幕预处理函数,用于训练模型时用来预处理初始字幕:

def preprocess_captions(caption_list): 
    pc = [] 
    for caption in caption_list: 
        caption = caption.strip().lower() 
        caption = caption.replace('.', '') 
                         .replace(',', '') 
                         .replace("'", "") 
                         .replace('"', '') 
        caption = caption.replace('&','and') 
                         .replace('(','') 
                         .replace(')', '') 
                         .replace('-', ' ') 
        caption = ' '.join(caption.split())  
        pc.append(caption) 
    return pc

了解和实现 BLEU 评分

现在,我们需要选择适当的模型表现评估指标,以评估模型的表现。 这里的一个相关指标是双语评估学习BLEU)得分。 这是一种评估模型在翻译语言时的表现的出色算法。 BLEU 背后的动机是,所生成的输出越接近于人工翻译,则得分越高。 时至今日,它仍然是将模型输出与人为输出进行比较的最受欢迎的指标之一。

BLEU 算法的简单原理是针对一组参考字幕评估生成的文本字幕(通常针对一个或多个字幕评估一个字幕,在这种情况下,每个图像五个字幕)。 计算每个字幕的分数,然后在整个语料库中平均以得到质量的总体估计。 BLEU 分数始终介于 0 到 1 之间,分数接近 1 表示高质量的翻译。 甚至参考文本数据也不是完美的,因为人类在字幕图像期间也会出错,因此,其想法不是获得完美的 1,而是获得良好的整体 BLEU 分数。

我们将使用 NLTK 中的翻译模块中的corpus_bleu(...)函数来计算 BLEU 分数。 我们将计算 1、2、3 和 4 元组的总累积 BLEU 分数。 如我们已实现的评估函数所示,为bleu2bleu3bleu4分数的每个 n-gram 分数分配了相等的权重:

from nltk.translate.bleu_score import corpus_bleu 
def compute_bleu_evaluation(reference_captions,  
                                            predicted_captions): 
    actual_caps = [[caption.split() for caption in sublist]  
                            for sublist in reference_captions] 
    predicted_caps = [caption.split()  
                            for caption in predicted_captions] 
    bleu1 = corpus_bleu(actual_caps,  
                        predicted_caps, weights=(1.0, 0, 0, 0)) 
    bleu2 = corpus_bleu(actual_caps,  
                        predicted_caps, weights=(0.5, 0.5, 0, 0)) 
    bleu3 = corpus_bleu(actual_caps,  
                        predicted_caps,  
                        weights=(0.3, 0.3, 0.3, 0)) 
    bleu4 = corpus_bleu(actual_caps, predicted_caps,  
                        weights=(0.25, 0.25, 0.25, 0.25)) 
    print('BLEU-1: {}'.format(bleu1)) 
    print('BLEU-2: {}'.format(bleu2)) 
    print('BLEU-3: {}'.format(bleu3)) 
    print('BLEU-4: {}'.format(bleu4)) 
    return [bleu1, bleu2, bleu3, bleu4]

评估测试数据的模型表现

现在已经准备好用于模型表现评估的所有组件。 为了评估模型在测试数据集上的表现,我们现在将使用传递学习来加载之前提取的图像特征,这些特征将作为模型的输入。 我们还将加载字幕,对其进行预处理,并将其作为每个图像的参考字幕列表进行分离,如下所示:

test_images = list(test_df['image'].unique()) 
test_img_features = [tl_img_feature_map[img_name]  
                               for img_name in test_images] 
actual_captions = list(test_df['caption']) 
actual_captions = preprocess_captions(actual_captions) 
actual_captions = [actual_captions[x:x+5]  
                       for x in range(0, len(actual_captions),5)] 
actual_captions[:2]  
[['the dogs are in the snow in front of a fence', 
  'the dogs play on the snow', 
  'two brown dogs playfully fight in the snow', 
  'two brown dogs wrestle in the snow', 
  'two dogs playing in the snow'], 
 ['a brown and white dog swimming towards some in the pool', 
  'a dog in a swimming pool swims toward sombody we cannot see', 
  'a dog swims in a pool near a person', 
  'small dog is paddling through the water in a pool', 
  'the small brown and white dog is in the pool']]

您可以清楚地看到每个图像标题现在如何位于整齐的单独列表中,这些列表将在计算 BLEU 分数时形成我们的标题参考集。 现在,我们可以生成 BLEU 分数,并使用不同的光束大小值测试模型的表现。 这里描述了一些示例:

# Beam Size 1 - Model 1 with 30 epochs 
predicted_captions_ep30bs1 = [generate_image_caption(model=model1,  
                                  word_to_index_map=word_to_index,  
                                  index_to_word_map=index_to_word,  
                                          image_features=img_feat,  
                                max_caption_size=max_caption_size,  
                                beam_size=1)[0]  
                                    for img_feat  
                                         in test_img_features] 
ep30bs1_bleu = compute_bleu_evaluation( 
                    reference_captions=actual_captions, 
                    predicted_captions=predicted_captions_ep30bs1) 
BLEU-1: 0.5049574449416513 
BLEU-2: 0.3224643449851107 
BLEU-3: 0.22962263359362023 
BLEU-4: 0.1201459697546317 
# Beam Size 1 - Model 2 with 50 epochs 
predicted_captions_ep50bs1 = [generate_image_caption(model=model2,  
                                  word_to_index_map=word_to_index,  
                                  index_to_word_map=index_to_word,  
                                          image_features=img_feat,  
                                max_caption_size=max_caption_size,  
                                     beam_size=1)[0]  
                                        for img_feat  
                                            in test_img_features] 
ep50bs1_bleu = compute_bleu_evaluation( 
                   reference_captions=actual_captions, 
                   predicted_captions=predicted_captions_ep50bs1)

您可以清楚地看到,随着我们开始考虑更高水平的 n-gram,分数开始下降。 总体而言,运行此过程非常耗时,要在集束搜索中获得更高的阶数会花费大量时间。 我们尝试了光束大小分别为 1、3、5 和 10 的实验。下表描述了每个实验的模型表现:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WwG6QVHR-1681567330256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/0c77c2ea-ffb1-4db5-8e3b-35514628b0d0.png)]

我们还可以通过图表的形式轻松地将其可视化,以查看哪种模型参数组合为我们提供了具有最高 BLEU 得分的最佳模型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZglKxch-1681567330256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/d091a430-a904-4c49-9b84-cc431209bfca.png)]

从上一张图可以很明显地看出,基于 BLEU 指标,我们在集束搜索期间具有 50 个周期且集束大小为 10 的模型为我们提供了最佳表现。

自动图片字幕实战!

对我们的测试数据集进行评估是测试模型表现的好方法,但是我们如何开始在现实世界中使用模型并为全新照片加上标题呢? 在这里,我们需要一些知识来构建端到端系统,该系统以任何图像作为输入,并为我们提供自由文本的自然语言标题作为输出。

以下是我们的自动字幕生成器的主要组件和函数:

  • 字幕模型和元数据初始化器
  • 图像特征提取模型初始化器
  • 基于迁移学习的特征提取器
  • 字幕生成器

为了使它通用,我们构建了一个类,该类利用了前面几节中提到的几个工具函数:

from keras.preprocessing import image 
from keras.applications.vgg16 import preprocess_input as preprocess_vgg16_input 
from keras.applications import vgg16 
from keras.models import Model 
class CaptionGenerator: 
    def __init__(self, image_locations=[],  
                 word_to_index_map=None, index_to_word_map=None,  
                 max_caption_size=None, caption_model=None,  
                                                    beam_size=1): 
        self.image_locs = image_locations 
        self.captions = [] 
        self.image_feats = [] 
        self.word2index = word_to_index_map 
        self.index2word = index_to_word_map 
        self.max_caption_size = max_caption_size 
        self.vision_model = None 
        self.caption_model = caption_model 
        self.beam_size = beam_size 
    def process_image2arr(self, path, img_dims=(224, 224)): 
        img = image.load_img(path, target_size=img_dims) 
        img_arr = image.img_to_array(img) 
        img_arr = np.expand_dims(img_arr, axis=0) 
        img_arr = preprocess_vgg16_input(img_arr) 
        return img_arr 
    def initialize_model(self): 
        vgg_model = vgg16.VGG16(include_top=True, weights='imagenet',  
                                input_shape=(224, 224, 3)) 
        vgg_model.layers.pop() 
        output = vgg_model.layers[-1].output 
        vgg_model = Model(vgg_model.input, output) 
        vgg_model.trainable = False 
        self.vision_model = vgg_model 
    def process_images(self): 
        if self.image_locs: 
            image_feats = [self.vision_model.predict
                                         (self.process_image2arr
                                         (path=img_path)) for img_path   
                                            in self.image_locs] 
            image_feats = [np.reshape(img_feat, img_feat.shape[1]) for  
                                      img_feat in image_feats] 
            self.image_feats = image_feats 
        else: 
            print('No images specified') 
    def generate_captions(self): 
        captions = [generate_image_caption(model=self.caption_model, 
                    word_to_index_map=self.word2index,  
                    index_to_word_map=self.index2word,  
                    image_features=img_feat, 
                                           max_caption_size=self.max_caption_size, beam_size=self.beam_size)[0] 
                           for img_feat in self.image_feats] 
        self.captions = captions

现在我们的字幕生成器已经实现,现在该将其付诸实践了! 为了测试字幕生成器,我们下载了几张全新的图像,这些图像在Flickr8K数据集中不存在。 我们从 Flickr 下载了特定的图像,这些图像遵循必要的基于商业使用的许可证,因此我们可以在本书中进行描述。 我们将在下一部分中展示一些演示。

为室外场景中的样本图像加字幕

我们从 Flickr 拍摄了几张针对各种户外场景的图像,并使用了我们的两个图像字幕模型为每个图像生成字幕,如下所示:

# load files 
import glob 
outdoor1_files = glob.glob('real_test/outdoor1/*') 
# initialize caption generators and generate captions 
cg1 = CaptionGenerator(image_locations=outdoor1_files, word_to_index_map=word_to_index, index_to_word_map=index_to_word,  
                       max_caption_size=max_caption_size, caption_model=model1, beam_size=3) 
cg2 = CaptionGenerator(image_locations=outdoor1_files, word_to_index_map=word_to_index, index_to_word_map=index_to_word,  
                       max_caption_size=max_caption_size, caption_model=model2, beam_size=3) 
cg1.initialize_model() 
cg1.process_images() 
cg1.generate_captions() 
cg2.initialize_model() 
cg2.process_images() 
cg2.generate_captions() 
model30ep_captions_outdoor1 = cg1.captions 
model50ep_captions_outdoor1 = cg2.captions 
# plot images and their captions 
fig=plt.figure(figsize=(13, 11)) 
plt.suptitle('Automated Image Captioning: Outdoor Scenes 1', verticalalignment='top', size=15) 
columns = 2 
rows = 3 
for i in range(1, columns*rows +1): 
    fig.add_subplot(rows, columns, i) 
    image_name = outdoor1_files[i-1] 
    img = image.load_img(image_name) 
    plt.imshow(img, aspect='auto') 
    modelep30_caption_text = 'Caption(ep30): '+ model30ep_captions_outdoor1[i-1] 
    modelep50_caption_text = 'Caption(ep50): '+ model50ep_captions_outdoor1[i-1] 
    plt.xlabel(modelep30_caption_text+'\n'+modelep50_caption_text,size=11, wrap=True) 
fig.tight_layout() 
plt.subplots_adjust(top=0.955)

前面代码的输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MbYxu2VZ-1681567330256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/c801b87d-0f66-4b0c-beb4-9f16a7998be1.png)]

根据前面的图像,您可以清楚地看到它已正确识别每个场景。 这不是一个完美的模型,因为我们可以清楚地看到它并没有在第二行的第二张图像中识别出狗,而是清楚地识别了一群人。 此外,我们的模型确实犯了一些颜色识别错误,例如将绿色球识别为红色球。 总体而言,生成的字幕绝对适用于源图像!

以下图像是从更多样化的户外场景中摘录的,并基于流行的户外活动。 我们将专注于不同的活动,以查看我们的模型在不同类型的场景上的表现如何,而不是仅关注一个特定的场景:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hg8c99Go-1681567330256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/98d383df-6e05-485e-b91d-4915cf14b77e.png)]

在前面的图像中,我们专注于各种各样的户外活动,包括越野自行车,滑雪,冲浪,皮划艇和攀岩。 如果您查看生成的字幕,它们与每个场景都相关,并可以很好地描述它们。 在某些情况下,我们的模型会变得非常具体,甚至描述每个人的穿着。 但是,正如我们前面提到的,它会在几种情况下错误地识别颜色,可能可以通过添加更多数据以及对高分辨率图像进行训练来改善颜色。

为流行运动的样本图像加字幕

在模型测试的最后一部分,我们从 Flickr 拍摄了几张图像,这些图像专注于世界各地通常进行的各种体育运动。 我们肯定获得了一些有趣的结果,因为我们不仅仅关注一两个基于运动的场景。 生成此代码的代码与我们在上一节中使用的代码完全相同,只是源图像发生了变化。 与往常一样,笔记本中提供了详细的代码以供参考。 以下是我们的字幕生成器在第一批运动场景上的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyJbPmDF-1681567330257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/0b8a131c-6589-4e31-bf10-c6de946f210a.png)]

在前面的图像中,我们可以清楚地看到训练有 50 个周期的模型在视觉上更详细地描述图像方面优于具有 30 个周期的模型。 这包括特定的球衣和服装颜色,例如白色,蓝色和红色。 我们还看到字幕中提到了一些具体的活动,例如:踢足球,看曲棍球的进球或在泥泞的赛道上驾驶。 这无疑为生成的字幕提供了更多的深度和含义。 我们的模型具有 30 个周期,因此在某些图像中所进行的确切运动方面也会犯一些错误。

现在,让我们看一下体育场景的最后一组,以了解我们的字幕生成器在与前一组场景完全不同的体育活动中的表现:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wb31hVR-1681567330257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/d7ae1192-1196-4254-93cc-028dd18d4d65.png)]

我们可以从前面的输出中观察到,我们的两个模型都运行良好,在 30 个周期上训练的模型在几种情况下都表现出色,例如识别出踢足球的孩子或男孩,甚至是比赛中 BMX 骑手的颜色和配饰。 总体而言,这两种模型都表现良好,并且在某种程度上解释了风景,类似于人类对这些场面的描述。

成功的主要方面是我们的模型不仅可以正确识别每个活动,而且还能够生成有意义且适用的标题。 我们鼓励您尝试在不同的场景上构建和测试自己的字幕生成器!

未来的改进空间

根据我们在本章中采用的方法,有多种方法可以改进此模型。 以下是一些可以改进的特定方面:

  • 使用更好的图像特征提取模型,例如 Google 的 Inception 模型
  • 分辨率更高,质量更好的训练图像(需要 GPU 功能!)
  • 基于 Flickr30K 等数据集甚至图像增强的更多训练数据
  • 在模型中引入注意力

如果您拥有必要的数据和基础架构,那么这些点子值得探讨!

总结

这绝对是我们整本书中解决的最棘手的现实问题之一。 它是迁移学习和生成型深度学习的完美结合,可应用于来自图像和文本的数据组合,这些组合结合了围绕计算机视觉和 NLP 的不同领域。 我们介绍了有关理解图像字幕的基本概念,构建字幕生成器所需的主要组件,并从头开始构建了我们自己的模型。 我们通过利用预先训练的计算机视觉模型从要字幕的图像中提取正确的特征,然后将它们与一些顺序模型(例如 LSTM)结合使用,以有效地利用迁移学习原理。 顺序模型的有效评估非常困难,我们利用行业标准的 BLEU 评分标准来达到目的。 我们从头开始实现评分函数,并在测试数据集上评估了我们的模型。

最后,我们使用以前构建的所有资产和组件从头构建了一个通用的自动图像字幕系统,并在来自不同领域的多种图像上对其进行了测试。 我们希望这能给您一个很好的入门介绍,这是计算机视觉和 NLP 的完美结合,并且我们绝对鼓励您构建自己的图像捕获系统!

十一、图像着色

颜色是大自然的笑容。

——雷·亨特

直到 1840 年代,世界都是以黑白捕获。 加布里埃尔·利普曼(Gabriel Lippmann)于 1908 年获得诺贝尔物理学奖,从而开始了色彩捕捉的时代。 1935 年,伊士曼·柯达(Eastman Kodak)推出了一体式三重彩色胶卷,称为 Kodachrome,用于拍摄彩色照片。

彩色图像不仅与美学和美感有关,而且比黑白图像捕获的信息要多得多。 颜色是现实世界对象的重要属性,它为我们对周围世界的感知增加了另一个维度。 色彩的重要性是如此之大,以至于有许多项目为整个历史上的艺术作品和摄影作品着色。 随着 Adobe Photoshop 和 GIMP 等工具的出现,人们一直在努力地将旧照片转换为彩色照片。 reddit r / Colorization 子组是一个在线社区,人们在这里分享经验并致力于将黑白图像转换为彩色图像。

到目前为止,在本书中,我们涵盖了不同的领域和场景,以展示迁移学习的惊人好处。 在本章中,我们将介绍使用深度学习进行图像着色的概念,并利用迁移学习来改善结果。 本章将涵盖以下主题:

  • 问题陈述
  • 了解图像着色
  • 彩色图像
  • 建立基于深度神经网络的着色网络
  • 改进之处
  • 挑战

在接下来的部分中,我们将使用术语黑白,单色和灰度来表示没有任何颜色信息的图像。 我们将这些术语互换使用。

问题陈述

照片可以帮助我们及时保存事件。 它们不仅帮助我们重温记忆,还提供对过去重要事件的见解。 在彩色摄影成为主流之前,我们的摄影历史是用黑白拍摄的。 图像着色的任务是将给定的灰度图像转换为合理的颜色版本。

图像着色的任务可以从不同的角度进行。 手动过程非常耗时,并且需要出色的技能。 计算机视觉和深度学习领域的研究人员一直在研究使过程自动化的不同方法。 通过本章,我们将努力理解如何将深层神经网络用于此类任务。 我们还将尝试利用迁移学习的力量来改善结果。

我们鼓励读者在继续进行之前,先对问题陈述进行思考。 考虑一下您将如何处理这样的任务。 在深入探讨该解决方案之前,让我们获取有关彩色图像和相关概念的一些信息。 下一节涵盖了处理当前任务所需的基本概念。

彩色图像

不到 100 年前,单色捕获是一个限制,而不是一种选择。 数码和移动摄影的出现使黑白图像或灰度图像成为一种艺术选择。 当然,这样的图像具有戏剧性的效果,但是黑白图像不仅仅是改变捕获设备(无论是数码相机还是电话)上的选项。

我们对颜色和正式颜色模型的了解早于彩色图像。 托马斯·杨(Thomas Young)在 1802 年提出了三种类型的感光器或视锥细胞的存在(如下图所示)。 他的理论详述了这三个视锥细胞中的每一个仅对特定范围的可见光敏感。 进一步发展了该理论,将这些视锥细胞分为短,中和长三种,分别优选蓝色,绿色和红色:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LSjy3plN-1681567330257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/8f1cb58e-6396-4356-882e-e773f540c85e.png)]

托马斯·杨(Thomas Young)和赫尔曼·赫尔姆霍尔茨(Hermann Helmholtz):三锥细胞理论

我们对颜色的理解以及对颜色的理解方式的进步导致了颜色理论的形式化。 由于色彩理论本身是一个完整的领域,因此在本章中,我们将对其进行简要介绍。 关于这些主题的详细讨论超出了本书的范围。

色彩理论

简单来说,色彩理论是用于指导色彩感知,混合,匹配和复制方法的正式框架。 多年来,已经进行了各种尝试来基于色轮,原色,第二色等正式定义颜色。 因此,颜色理论是一个广阔的领域,在此之下,我们可以正式定义与色度,色相,配方等颜色相关的属性。

色彩模型和色彩空间

颜色模型是颜色理论到颜色表示的表述。 颜色模型是一种抽象的数学概念,当与它的组成的精确理解相关联时,被称为颜色空间。 大多数颜色模型都用表示特定颜色成分的三到四个数字的元组表示。

RGB

托马斯·杨(Thomas Young)三锥理论红色绿色蓝色RGB)的延续,是最古老,使用最广泛的颜色模型和颜色空间之一。 RGB 是加色模型。 在此模型中,以不同的浓度添加了光的三个分量(红色,绿色和蓝色),以实现可见光的完整光谱。 附加色空间如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pxbf315h-1681567330257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/4411249b-c6c4-4faf-bb66-9598ca848e6c.png)]

RGB 颜色空间(来源:英语 Wikipedia 的 SharkD。更高版本由 Jacobolus 上传,已从 wikipedia en 转移到 Public Commons 域

每种成分的零强度会导致黑色,而全强度会导致对白色的感觉。 尽管简单,但此颜色模型和颜色空间构成了大多数电子显示器(包括 CRT,LCD 和 LED)的基础。

YUV

Y代表亮度,而UV通道代表色度。 该编码方案在视频系统中被广泛使用以映射人类的颜色感知。 紫外线通道主要帮助确定红色和蓝色的相对含量。 由于该方案使用较低的带宽并且不易出现传输错误的能力,因此被广泛使用,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0GLRBIV-1681567330257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/d8116e36-e0dc-469c-8872-ca06a7d83261.png)]

YUV 色彩空间(来源:Tonyle,本人著作,CC BY-SA 3.0)

此图像是 UV 颜色通道在 0.5 Y 处的样本表示。

LAB

这种与设备无关的色彩空间参考是由国际照明委员会开发的。 L通道表示颜色的亮度(0 为黑色,而 100 为漫射白色)。

A表示绿色和品红色之间的位置,而B表示蓝色和黄色之间的位置,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xrw2bp5H-1681567330258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/9f139cfc-8df5-44ca-b88e-db6314c11a3b.png)]

LAB 色彩空间(来源:Holger kkk Everding,自己的作品,CC BY-SA 4.0)

除了这三种以外,还存在其他各种颜色模型。 出于当前有关图像着色的用例的目的,我们将采用一种非常有趣的方法。

重新陈述问题

如果我们遵循使用最广泛的颜色模型 RGB,那么事实证明,训练模型以将输入的单色图像映射到颜色将是一项艰巨的任务。

深度学习领域的研究人员在解决和提出问题方面颇具创造力。 在图像着色的情况下,研究人员巧妙地研究了利用不同输入来实现灰度图像逼真的幻觉的方法。

在最初的尝试中,参考图像和颜色涂鸦形式的颜色引导输入的不同变体被用来产生巨大的效果。 请参阅威尔士和合著者以及莱文和合著者。

最近的工作集中在利用深层 CNN 中的迁移学习使整个过程自动化。 结果令人鼓舞,有时甚至足以愚弄人类。

最近的工作,以及迁移学习的力量,已经巧妙地尝试利用包含灰度通道作为其组成部分之一的颜色模型。 那会响吗? 现在让我们从另一个角度看问题陈述。

除了无所不在的 RGB 颜色空间外,我们还讨论了 LAB。 LAB 色彩空间包含灰度值,因为L通道(用于亮度),而其余两个通道(ab)赋予颜色属性。 因此,着色问题可以用以下数学方式建模:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NbioGVF-1681567330258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/90336338-d933-4938-99f5-5a8295c39919.png)]

在上述方程式中,我们表示从给定数据将L通道映射到同一图像的ab通道的函数。 下图说明了这一点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ETRQUu1N-1681567330258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/a5eb7160-f340-4ee3-9f73-81489bf2a779.png)]

colornet 转换

简而言之,我们已经将图像着色的任务转换为将一个通道(灰度L通道)转换为两个颜色通道(AB)的任务 ,说明如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ea2Cd9aP-1681567330258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/9ff5a5d4-1ebb-4948-9c61-6e45b7a3a6d5.png)]

彩色图像及其组件 – RGB,YUV 和 LAB

前面的图像显示了彩色图像的LAB通道,基于 Zhang 及其合著者(2016)和 Federico 及其合著者(2017)的作品。 我们将在接下来的部分中详细研究它们。

我们鼓励读者阅读标题为《Deep Koalarization:使用 CNN 和 Inception-ResNet-v2 进行图像着色》的论文。 我们要感谢 Federico Baldassarre,Diego Gonzalez-Morin 和 Lucas Rodes-Guirao 为他们的工作及其实现提供了详细的信息和见解。 我们还要感谢 Emil Wallner 使用 Keras 出色地实现了本文。

读者应注意,类似的过程也可以应用于 YUV 色彩空间。 Jeff Hwang 和他的合著者在题为《利用深度卷积神经网络进行图像着色》的论文中讨论了利用这种色彩空间的尝试,效果也很好

建立着色深层神经网络

现在是时候构建着色深层神经网络或色网。 如前一节所述,如果我们使用替代颜色空间,例如 LAB(或 YUV),则可以将着色任务转换为数学转换。 转换如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmYdaMZn-1681567330258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/3a9b6edb-d7da-4cc8-9007-9ebd67ed7f35.png)]

数学公式和创造力很好,但是学习这些转换的图像在哪里呢? 深度学习网络需要大量数据,但幸运的是,我们有来自各种开源数据集的大量不同图像的集合。 在本章中,我们将依赖于 ImageNet 本身的一些示例图像。 由于 ImageNet 是一个庞大的数据集,因此我们为问题陈述随机选择了一些彩色图像。 在后面的部分中,我们将讨论为什么选择此子集及其一些细微差别。

我们依靠 Baldassarre 及其合作者开发的图像提取工具,用于《Deep Koalarization:使用 CNN 和 Inception-ResNet-v2 进行图像着色》的论文,来整理本章中使用的 ImageNet 样本的子集。 可以在这个页面上获取数据提取的代码。

本书的 GitHub 存储库中提供了本章使用的代码和示例图像以及colornet_vgg16.ipynb笔记本。

预处理

获取/整理所需数据集后的第一步是预处理。 对于当前的图像着色任务,我们需要执行以下预处理步骤:

  • 重新缩放:ImageNet 是一个具有各种图像的多样化数据集,包括类和大小(尺寸)。 为了实现此目的,我们将所有图像重新缩放为固定大小。
  • 利用 24 位 RGB:由于人眼只能区分 2 和 1000 万种颜色,因此我们可以利用 24 位 RGB 来近似 1600 万种颜色。 减少每个通道的位数将有助于我们以更少的资源更快地训练模型。 这可以通过简单地将像素值除以 255 来实现。
  • RGB 到 LAB:由于在 LAB 色彩空间中更容易解决图像着色问题,因此我们将利用 skimage 来转换和提取 RGB 图像中的 LAB 通道。

标准化

LAB 颜色空间的值介于 -128 至 +128 之间。 由于神经网络对输入值的大小敏感,因此我们将从-128 到+128 的变换后的像素值归一化,并将它们置于 -1 到 +1 范围内。 以下代码片段中展示了相同的内容:

def prep_data(file_list=[],
              dir_path=None,
              dim_x=256,
              dim_y=256):
    #Get images
    X = []
for filename in file_list:
    X.append(img_to_array(
                        sp.misc.imresize(
                        load_img(
                        dir_path+filename),
                        (dim_x, dim_y))
           )
        )
    X = np.array(X, dtype=np.float64)
    X = 1.0/255*X
    return X

转换后,我们将数据分为训练集和测试集。 对于拆分,我们使用了 sklearn 的train_test_split utility

损失函数

模型是通过改善损失函数或目标函数来学习的。 任务是使用反向传播学习最佳参数,以最小化原始彩色图像和模型输出之间的差异。 来自模型的输出彩色图像也称为灰度图像的幻觉着色。 在此实现中,我们将均方误差MSE)用作损失函数。 以下等式对此进行了总结:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDbIrTNo-1681567330259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/5b7fc827-b620-41f8-b1d3-6a35eacfc45e.png)]

原始颜色和色网输出之间的损失函数(来源:Baldassarre 和合著者)

对于 Keras,使用此损失函数就像在编译 Keras 模型时设置参数一样容易。 我们利用 RMSprop 优化器来训练我们的模型(本文使用 Adam 代替)。

编码器

卷积神经网络CNN)是令人惊叹的图像分类器。 他们通过提取位置不变特征来实现。 在此过程中,它们倾向于使输入图像失真。

在图像着色的情况下,这种失真将是灾难性的。 为此,我们使用编码器将H x W尺寸的输入灰度图像转换为H / 8 x W / 8。 编码器通过使用零填充来保持图像通过不同层的纵横比。 以下代码片段显示了使用 Keras 的编码器:

#Encoder
enc_input = Input(shape=(DIM, DIM, 1,))
enc_output = Conv2D(64, (3,3),
               activation='relu',
               padding='same', strides=2)(enc_input)
enc_output = Conv2D(128, (3,3),
               activation='relu',
               padding='same')(enc_output)
enc_output = Conv2D(128, (3,3),
              activation='relu',
              padding='same', strides=2)(enc_output)
enc_output = Conv2D(256, (3,3),
              activation='relu',
              padding='same')(enc_output)
enc_output = Conv2D(256, (3,3),
              activation='relu',
              padding='same', strides=2)(enc_output)
enc_output = Conv2D(512, (3,3),
              activation='relu',
              padding='same')(enc_output)
enc_output = Conv2D(512, (3,3),
              activation='relu',
              padding='same')(enc_output)
enc_output = Conv2D(256, (3,3),
              activation='relu',
              padding='same')(enc_output)

在前面的代码片段中,有趣的方面是对第 1 层,第 3 层和第 5 层使用了 2 的步幅大小。2 的步幅长度将图像尺寸减半,但仍设法保持了纵横比。 这有助于增加信息密度而不会扭曲原始图像。

迁移学习 – 特征提取

本章讨论的图像着色网络是一个非常独特的网络。 它的独特性来自我们使用迁移学习来增强模型的方式。 我们知道可以将预训练的网络用作特征提取器,以帮助迁移学习的模式并提高模型的表现。

在这种当前设置下,我们利用预训练的 VGG16(本文指的是利用预训练的 Inception 模型)进行迁移学习。 由于 VGG16 需要以特定格式输入,因此我们通过调整输入图像的大小并将其连接 3 次以补偿丢失的通道信息,来转换输入的灰度图像(输入到网络编码器部分的相同灰度图像)。

以下代码段获取输入的灰度图像并生成所需的嵌入:

#Create embedding
def create_vgg_embedding(grayscaled_rgb):
    gs_rgb_resized = []
    for i in grayscaled_rgb:
        i = resize(i, (224, 224, 3),
                   mode='constant')
        gs_rgb_resized.append(i)
    gs_rgb_resized = np.array(gs_rgb_resized)
    gs_rgb_resized = preprocess_input(gs_rgb_resized)
    with vgg16.graph.as_default():
      embedding = vgg16.predict(gs_rgb_resized)
    return embedding

前面的代码段生成大小为1,000 x 1 x 1的输出特征向量。

融合层

我们在前几章中构建的大多数网络都使用了 Keras 的顺序 API。 融合层是在这种情况下利用迁移学习的创新方式。 请记住,我们已将输入灰度图像用作两个不同网络(一个编码器和一个预训练的 VGG16)的输入。 由于两个网络的输出具有不同的形状,因此我们将 VGG16 的输出重复 1,000 次,然后将其与编码器输出连接或合并。 以下代码段准备了融合层:

#Fusion
fusion_layer_output = RepeatVector(32*32)(emd_input)
fusion_layer_output = Reshape(([32,32,
                          1000]))(fusion_layer_output)
fusion_layer_output = concatenate([enc_output,
                                   fusion_layer_output], axis=3)
fusion_layer_output = Conv2D(DIM, (1, 1),
                       activation='relu',
                       padding='same')(fusion_layer_output)

VGG16 的输出重复沿编码器输出的深度轴连接。 这样可以确保从 VGG16 中提取的图像特征嵌入均匀地分布在整个图像中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RC1BTug8-1681567330259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/ec57fb0d-1df6-4532-af60-06400ed77fd5.png)]

级联灰度输入预训练网络(左),融合层(右)

来源:Baldassarre 等

上图显示了特征提取器或预训练的 VGG16 的输入以及融合层的结构。

解码器

网络的最后阶段是解码器。 在网络的前两个部分中,我们利用编码器和预训练模型来学习不同的特征并生成嵌入。 融合层的输出为张量,大小为H / 8 x W / 8 x 256,其中HW是灰度图像的原始高度和宽度(在我们的情况是256 x 256)。 该输入经过一个八层解码器,该解码器使用五个卷积层和三个上采样层构建。 上采样层可帮助我们使用基本的最近邻方法将图像大小增加一倍。 以下代码片段展示了网络的解码器部分:

#Decoder
dec_output = Conv2D(128, (3,3),
                        activation='relu',
                        padding='same')(fusion_layer_output)
dec_output = UpSampling2D((2, 2))(dec_output)
dec_output = Conv2D(64, (3,3),
                        activation='relu',
                        padding='same')(dec_output)
dec_output = UpSampling2D((2, 2))(dec_output)
dec_output = Conv2D(32, (3,3),
                        activation='relu',
                        padding='same')(dec_output)
dec_output = Conv2D(16, (3,3),
                        activation='relu',
                        padding='same')(dec_output)
dec_output = Conv2D(2, (3, 3),
                        activation='tanh',
                        padding='same')(dec_output)
dec_output = UpSampling2D((2, 2))(dec_output)

解码器网络的输出是具有两个通道的原始大小的图像,即,输出是形状为H x W x 2的张量。 最终的卷积层使用 tanh 激活函数将预测像素值保持在 -1 到 +1 范围内。

下图显示了具有三个组成部分的网络:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hhz6ycN2-1681567330259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/29986810-292e-472d-97a8-f2a71c8ce21d.png)]

Colornet 由编码器,作为特征提取器的预训练模型,融合层和解码器组成

使用 Keras 构建的深度学习模型通常是使用顺序 API 构建的。 在这种情况下,我们的着色网络(即 colornet)利用函数式 API 来实现融合层。

后处理

解决问题的技巧还没有结束。 如“预处理”小节中所述,我们将 -1 到 +1 之间的像素值标准化,以确保我们的网络正确训练。 同样,两个颜色通道的 LAB 颜色空间的值在 -128 到 +128 之间。 因此,执行以下两个后处理步骤:

  • 我们将每个像素值乘以 128,以将值带入所需的颜色通道范围
  • 我们将灰度输入图像与输出两通道图像连接起来,以获得幻觉的彩色图像

以下代码段执行后处理步骤,以产生幻觉的彩色图像:

sample_img = []
for filename in test_files:
    sample_img.append(sp.misc.imresize(load_img(IMG_DIR+filename),
                                     (DIM, DIM)))
sample_img = np.array(sample_img,
                    dtype=float)
sample_img = 1.0/255*sample_img
sample_img = gray2rgb(rgb2gray(sample_img))
sample_img = rgb2lab(sample_img)[:,:,:,0]
sample_img = sample_img.reshape(sample_img.shape+(1,))
#embedding input
sample_img_embed = create_vgg_embedding(sample_img)

如前面的代码片段所示,我们使用 skimage 中的lab2rgb工具将生成的输出转换为 RGB 颜色空间。 这样做是为了便于可视化输出图像。

训练与结果

训练如此复杂的网络可能很棘手。 在本章中,我们从 ImageNet 中选择了一小部分图像。 为了帮助我们的网络学习和推广,我们使用 Keras 的ImageDataGenerator类来扩充数据集并在输入数据集中产生变化。 以下代码片段展示了图像增强和模型训练:

# Image transformer
datagen = ImageDataGenerator(
        shear_range=0.2,
        zoom_range=0.2,
        rotation_range=20,
        horizontal_flip=True)
def colornet_img_generator(X,
                  batch_size=BATCH_SIZE):
    for batch in datagen.flow(X, batch_size=batch_size):
        gs_rgb = gray2rgb(rgb2gray(batch))
        batch_lab = rgb2lab(batch)
        batch_l = batch_lab[:,:,:,0]
        batch_l = batch_l.reshape(batch_l.shape+(1,))
        batch_ab = batch_lab[:,:,:,1:] / 128
        yield ([batch_l,
                create_vgg_embedding(gs_rgb)], batch_ab)
history = model.fit_generator(colornet_img_generator(X_train,
                                                     BATCH_SIZE),
                              epochs=EPOCH,
                              steps_per_epoch=STEPS_PER_EPOCH)

在着色网络的情况下,这种损失可能会产生误导。 它似乎已稳定在 100 个周期以下,但所产生的结果更多是乌贼色而不是颜色。 因此,我们做了更多的实验以达到以下结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fi3rmLrv-1681567330259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/c43e6248-8076-41c3-b8cb-92a4470a4e47.png)]

Colornet 输出:第一列代表灰度输入,第二列代表模型输出,第三列代表原始图像

前面的结果虽然不令人吃惊,但令人鼓舞。 通过对模型进行 600 个周期的训练,批次大小为 64,可以实现上述结果。

挑战

深度神经网络是功能强大的模型,具有成千上万个可学习的参数。 当前训练着色网络的方案提出了一系列新的挑战,其中一些挑战如下:

  • 当前的网络似乎已经学习了高级特征,例如草地和运动球衣(在一定程度上),而它发现学习较小物体的颜色模式有些困难。
  • 训练集仅限于非常具体的图像子集,因此反映在测试数据集中。 该模型对训练集中不存在的对象或包含这些对象的样本不多的表现不佳。
  • 即使训练损失似乎已稳定在 50 个周期以下,但我们看到,除非进行数百个周期训练,否则该模型的着色表现相当差。
  • 该模型很容易将大多数对象着色为灰色或棕褐色。 在训练了较少周期的模型中观察到了这一点。

除了这些挑战之外,对于如此复杂的架构,计算和内存要求也很高。

进一步改进

当前的实现尽管显示出令人鼓舞的结果,但是可以进一步调整。 通过利用更大,更多样化的数据集可以实现进一步的改进。

也可以通过使用功能更强大的最新预训练图像分类模型(例如 InceptionV3 或 InceptionResNetV2)来进行改进。

我们还可以通过准备由更复杂的架构组成的集成网络来利用 Keras 的函数式 API。 接下来的步骤之一可能是向网络提供时间信息,并查看是否还可以学习为视频着色。

总结

图像着色是深度学习领域的前沿主题之一。 随着我们对迁移学习和深度学习的理解日趋成熟,应用范围变得越来越令人兴奋且更具创造力。 图像着色是研究的活跃领域,最近,深度学习专家分享了一些激动人心的工作。

在本章中,我们学习了颜色理论,不同的颜色模型和颜色空间。 这种理解帮助我们将问题陈述重新表述为从单通道灰度图像到两通道输出的映射。 然后,我们根据 Baldassarre 和他的合著者的作品,着手建立一个色网。 该实现涉及一个独特的三层网络,该网络由编码器,解码器和融合层组成。 融合层使我们能够通过将 VGG16 嵌入与编码器输出连接来利用迁移学习。 网络需要一些特定的预处理和后处理步骤来训练给定的图像集。 我们的训练和测试数据集由 ImageNet 样本的子集组成。 我们对色网进行了数百次训练。 最后,我们提供了一些幻影图像,以了解该模型对着色任务的学习程度。 训练有素的色网学习了某些高级对象,例如草,但在较小或较不频繁的对象上表现不佳。 我们还讨论了这种类型的网络带来的一些挑战。

这结束了本书中由用例驱动的系列文章中的最后一章。 我们介绍了跨不同领域的不同用例。 每个用例都帮助我们利用了迁移学习的概念,本书的前两部分对此进行了详细讨论。 机器学习和深度学习领域的领先人物之一 Andrew Ng 在他的 NIPS 2016 教程中表示:

迁移学习将成为机器学习商业成功的下一个推动力。

在本书中讨论和展示了各种应用及其优势之后,您现在应该了解迁移学习的巨大潜力。

相关文章
|
17天前
|
Python
空间管理大师已上线!(2),Python高级工程师进阶学习】
空间管理大师已上线!(2),Python高级工程师进阶学习】
|
12天前
|
C语言 Python
​python学习之变量类型​
​python学习之变量类型​
|
14天前
|
Python
|
6天前
|
前端开发 Java Docker
【分享】记一次项目迁移(docker java | docker python)
该项目是一个前端Vue3和后端Python+Java的应用,原本部署在CentOS7服务器上通过宝塔面板管理。由于服务器即将到期,计划迁移到另一台使用OpenCloudOS和1Plane的服务器。在尝试构建Docker镜像时,首先为Java应用创建Dockerfile,成功构建并运行。对于Python应用,也创建了Dockerfile,并处理了依赖包的安装。在迁移过程中遇到Java项目加载验证码失败的问题,原因是缺少字体配置。通过在宿主机安装fontconfig并将相关字体文件复制到镜像中解决了问题。最后,前端Vue应用作为静态文件运行,如果使用反代理,需要进行相应配置。
25 1
|
7天前
|
存储 索引 Python
python学习——NumPy数值计算基础
NumPy基础知识概览:涉及nan(非数字)和inf(无穷)的概念,nan在文件读取或不适当计算时出现,inf在除0操作中出现。数组操作有深拷贝(a=b.copy())、浅拷贝(a=b[:])和引用(a=b)。创建数组方式多样,如`np.array()`、`np.arange()`等。数据类型转换如`np.float64()`、`np.int8()`。随机数生成包含均匀分布、正态分布等。数组索引和切片支持多维操作。改变数组形状用`reshape()`,展平用`ravel()`和`flatten()`。矩阵运算包括加减乘、转置、逆矩阵等。
29 2
python学习——NumPy数值计算基础
|
13天前
|
机器学习/深度学习 数据可视化 PyTorch
使用Python实现深度学习模型:迁移学习与预训练模型
使用Python实现深度学习模型:迁移学习与预训练模型
37 0
|
13天前
|
Python
Python学习 笔记(五) 判断语句
Python学习 笔记(五) 判断语句
28 3
|
14天前
|
机器学习/深度学习 数据挖掘 开发工具
2024年最全0基础学python开发工具及学习平台推荐_python平台a,面试阿里巴巴客服
2024年最全0基础学python开发工具及学习平台推荐_python平台a,面试阿里巴巴客服
2024年最全0基础学python开发工具及学习平台推荐_python平台a,面试阿里巴巴客服
|
14天前
|
Java 程序员 C语言
2024年Python最新【Python学习教程】Python类和对象_python中类和对象的讲解,Python最新面试题
2024年Python最新【Python学习教程】Python类和对象_python中类和对象的讲解,Python最新面试题
2024年Python最新【Python学习教程】Python类和对象_python中类和对象的讲解,Python最新面试题
|
14天前
|
数据采集 数据挖掘 Python
[Github高赞文章]python2愉快地迁移到Python3_code changing from python2 to python3(2)
[Github高赞文章]python2愉快地迁移到Python3_code changing from python2 to python3(2)