【深度学习】图像自然语言描述生成

案例 6:图像自然语言描述生成(让计算机“看图说话”)

相关知识点:RNN、Attention 机制、图像和文本数据的处理

1 任务目标

1.1 任务和数据简介

​ 本次案例将使用深度学习技术来完成图像自然语言描述生成任务,输入一张图片,模型会给出关于图片内容的语言描述。本案例使用 coco2014 数据集[1],包含 82,783 张训练图片,40,504 张验证图片,40,775 张测试图片。案例使用 Andrej Karpathy[2]提供的数据集划分方式和图片标注信息,案例已提供数据处理的脚本,只需下载数据集和划分方式即可。

​ 图像自然语言描述生成任务一般采用 Encoder-Decoder 的网络结构,Encoder 采用 CNN 结构,对输入图片进行编码,Decoder 采用 RNN 结构,利用 Encoder 编码信息,逐个单词的解码文字描述输出。模型评估指标采用 BLEU 分数[3],用来衡量预测和标签两句话的一致程度,具体计算方法可自行学习,案例已提供计算代码。

1.2 方法描述

  1. 模型输入

    ​ 图像统一到 256×256 大小,并且归一化到[−1,1]后还要对图像进行 RGB 三通道均值和标准差的标准化。语言描述标签信息既要作为目标标签,也要作为 Decoder 的输入,以 <start> 开始,<end> 结束并且需要拓展到统一长度,例如:< 𝑠𝑡𝑎𝑟𝑡 > 𝑎 𝑡𝑎𝑏𝑙𝑒 𝑡𝑜𝑝𝑝𝑒𝑑 𝑤𝑖𝑡ℎ 𝑝𝑙𝑎𝑡𝑒𝑠 𝑜𝑓 𝑓𝑜𝑜𝑑 𝑎𝑛𝑑 𝑑𝑟𝑖𝑛𝑘𝑠 < 𝑒𝑛𝑑 > < 𝑝𝑎𝑑 > < 𝑝𝑎𝑑 >< 𝑝𝑎𝑑 > ⋯

    ​ 每个 token 按照词汇表转为相应的整数。同时还需要输入描述语言的长度, 具体为单词数加 2(<start>,<end>),目的是为了节省在<pad>上的计算时间。

  2. Encoder

    ​ 案例使用 ResNet101 网络作为编码器,去除最后 PoolingFc 两层,并添加 了 AdaptiveAvgPool2d() 层来得到固定大小的编码结果。编码器已在 ImageNet 上预训练好,在本案例中可以选择对其进行微调以得到更好的结果。

  3. Decoder

    ​ Decoder 是本案例中着重要求的内容。案例要求实现两种 Decoder 方式,分别对应这两篇文章[4][5]。在此简要阐述两种 Decoder 方法,进一步学习可参考原文章。

    ​ 第一种 Decoder 是用 RNN 结构来进行解码,解码单元可选择 RNN、LSTM、GRU 中的一种,初始的隐藏状态和单元状态可以由编码结果经过一层全连接层并做批归一化 (Batch Normalization) 后作为解码单元输入得到,后续的每个解码单元的输入为单词经过 word embedding 后的编码结果、上一层的隐藏状态和单元状态,解码输出经过全连接层和 Softmax 后得到一个在所有词汇上的概率分布,并由此得到下一个单词。Decoder 解码使用到了 teacher forcing 机制,每一时间步解码时的输入单词为标签单词,而非上一步解码出来的预测单词。训练时,经过与输入相同步长的解码之后,计算预测和标签之间的交叉熵损失,进行 BP 反传更新参数即可。测试时由于不提供标签信息,解码单元每一时间步输入单词为上一步解码预测的单词,直到解码出信息。测试时可以采用 beam search 解码方法来得到更准确的语言描述,具体方法可自行学习。

    ​ 第二种 Decoder 是用 RNN 加上 Attention 机制来进行解码,Attention 机制做的是生成一组权重,对需要关注的部分给予较高的权重,对不需要关注的部分给 予较低的权重。当生成某个特定的单词时,Attention 给出的权重较高的部分会在图像中该单词对应的特定区域,即该单词主要是由这片区域对应的特征生成的。 Attention 权重的计算方法为:
    α = softmax ( f c ( relu ( f c ( encoder_ouput ) + f c ( h ) ) ) ) \alpha = \text{softmax} (fc(\text{relu}(fc(\text{encoder\_ouput})+fc(h)))) α=softmax(fc(relu(fc(encoder_ouput)+fc(h))))
    ​ 其中 softmax \text{softmax} softmax 表示 Softmax 函数, f c ( ) fc() fc() 表示全连接层, r e l u ( ) relu() relu() 表示 ReLU 激活函数, encoder_ouput \text{encoder\_ouput} encoder_ouput 是编码器的编码结果, h h h​ 是上一步的隐藏状态。初始的隐藏状态和单元状态由编码结果分别经过两个全连接层得到。每一时间步解码单元的 输入除了上一步的隐藏状态和单元状态外,还有一个向量,该向量由单词经过 word embedding 后的结果和编码器编码结果乘上注意力权重再经过一层全连接层后的结果拼接而成。解码器同样使用 teacher forcing 机制,训练和测试时的流程与第一种 Decoder 描述的一致。

  4. 样例输出

    第一种 Decoder 得到的结果仅包含图像的文字描述,如下图:

    image-20240330153447191

    第二种 Decoder 由于有 Attention 机制的存在,可以得到每个单词对应的图片区域,如下图:

    image-20240330153518213

1.3 参考程序及使用说明

​ 本次案例提供了完整、可供运行的参考程序,各程序简介如下:

  • create_input_files.py : 下载好数据集和划分方式后需要运行该脚本文件,会生成案例需要的 jsonhdf5 文件,注意指定输入和输出数据存放的位置。
  • datasets.py : 定义符合 pytorch 标准的 Dataset 类,供数据按 Batch 读入。
  • models.py : 定义 Encoder 和 Decoder 网络结构,其中 Encoder 已提前定义好,无需自己实现。两种 Decoder 方法需要自行实现,已提供部分代码,只需将 #ToDo 部分补充完全即可。
  • solver.py : 定义了训练和验证函数,供模型训练使用。
  • train.ipynb : 用于训练的 jupyter 文件,其中超参数需要自行调节,训练过程中可以看到模型准确率和损失的变化,并可以得到每个 epoch 后模型在验证集上的 BLEU 分数,保存最优的验证结果对应的模型用于测试。
  • test.ipynb : 用于测试的 jupyter 文件,加载指定的模型,解码时不使用 teacher forcing,并使用 beam search 的解码方法,最终会得到模型在测试集上的 BLEU 分数。
  • caption.ipynb : 加载指定模型,对单张输入图片进行语言描述,第一种 Decoder 方法只能得到用于描述的语句,第二种 Decoder 方法同时可以获取每个 单词对应的注意力权重,最后对结果进行可视化。
  • utils.py : 定义一些可能需要用到的函数,如计算准确率、图像可视化等。

​ 环境要求:python 包 pytorch, torchvision, numpy, nltk, tqdm, h5py, json, PIL, matplotlib, scikit-image, scipy=1.1.0 等。

1.4 要求与建议

  • 完成 models.py 文件中的 #ToDo 部分,可参考第 2 部分中的介绍或原论文;
  • 调节超参数,运行 train.ipynb,其中 attention 参数指示使用哪种 Decoder,分别训练使用两种不同 Decoder 的模型,可以分两个 jupyter 文件保存最佳参数和训练记录,如 train1.ipynb, train2.ipynb
  • 运行 test.ipynb 得到两个模型在测试集上的 BLEU 分数,分别保留结果;
  • 选择一张图片,可以是测试集中的,也可以是自行挑选的,对图片进行语言描述自动生成,分别保留可视化结果;
  • 在参考程序的基础上,综合使用深度学习各项技术,尝试提升该模型在图像自然语言描述生成任务上的效果,如使用更好的预训练模型作为 Encoder, 或者提出更好的 Decoder 结构,如 Adaptive Attention 等;
  • 完成一个实验报告,内容包括基础两个模型的实现原理说明、两个模型的最佳参数和对应测试集 BLEU 分数、两个模型在单个图片上的表现效果、自己所做的改进、对比分析两个基础模型结果的不同优劣。
  • 禁止任何形式的抄袭,借鉴开源程序务必加以说明。

1.5 参考资料

[1] MS-COCO 数据集: https://cocodataset.org/

[2] 划分方式与 caption 信息: http://cs.stanford.edu/people/karpathy/deepimagesent/caption_datasets.zip

[3] https://en.wikipedia.org/wiki/BLEU

[4] Vinyals O, Toshev A, Bengio S, et al. Show and tell: A neural image caption generator[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2015: 3156-3164.

[5] Xu K, Ba J, Kiros R, et al. Show, attend and tell: Neural image caption generation with visual attention[C]//International conference on machine learning. 2015: 2048-2057

2 采用无 Attention 机制的 Decoder 训练

2.1 代码补全

​ 实验提供的代码中,model.py 中的 DecoderWithRNN(nn.Moulde) 类需要补全,才能进行训练。以下补全的代码部分。

  1. 定义 Decoder 需要的层

    • 嵌入层 (self.embedding):用于将单词的索引转换为连续的向量表示。矩阵行数为词汇表的大小 vocab_size ,列数为每个单词嵌入的维度 embed_dim
    • LSTMCell 层 (self.decode_step):一种循环神经网络(RNN)的组件,用于处理序列数据。输入大小与隐藏大小都为decoder_dim
    • 线性层 (self.init):用于学习输入和输出之间的线性关系。输入特征的数量为 encoder_dim ,输出特征的数量为 decoder_dim
    • 批量归一化层 (self.bn):用于提高训练过程的稳定性和性能。过规范化层的输入来减少内部协变量偏移。
    • 全连接层 (self.fc):用于将解码器的隐藏状态转换为对词汇表中每个单词的分数。输入是解码器的隐藏状态维度 sdecoder_dim,输出是词汇表的大小 vocab_size
    • Dropout 层 (self.dropout):通过 Dropout 防止过拟合。
    # 嵌入层
    self.embedding = nn.Embedding(self.vocab_size, self.embed_dim)
    # LSTMCell 层
    self.decode_step = nn.LSTMCell(self.decoder_dim, self.decoder_dim)
    # 线性层
    self.init = nn.Linear(self.encoder_dim, self.decoder_dim)
    # 批量归一化层
    self.bn = nn.BatchNorm1d(self.decoder_dim)
    # 全连接层
    self.fc = nn.Linear(self.decoder_dim, self.vocab_size)
    # Dropout 层 
    self.dropout = nn.Dropout(self.dropout)
    
  2. 解码器前向传播 (forward)

    采用Teacher forcing,在每个时间步,使用前一个词嵌入在解码器中生成一个新单词。

    • 计算 num 来确定在当前时间步 t 还有哪些序列需要继续生成新的单词。
    • embeddings 张量中选择当前时间步 t 的有效嵌入表示。
    • 调用 self.one_step 函数来生成预测 preds 并更新 LSTM 状态 hc
    for t in range(max(decode_lengths)):
        num = sum([1 if l > t else 0 for l in decode_lengths])
        preds, h, c = self.one_step(embeddings[:num,t,:], h[:num], c[:num])
        predictions[:num,t,:] = preds
    
  3. 单步解码 (one_step)

    该代码是序列生成模型中用于执行单步解码操作的实现。

    • 调用 LSTM 细胞层 self.decode_step,进行前向传播。
    • 通过正则化技术,将 LSTM 细胞的输出 h 传递给 self.dropout 层,用于减少过拟合。
    • 通过 self.fc(全连接层)将 dropout 后的输出转换为对词汇表中每个单词的预测分数。
    def one_step(self, embeddings, h, c):
        h, c = self.decode_step(embeddings, (h, c))
        preds = self.fc(self.dropout(h))
        return preds, h, c
    

2.2 项目训练

​ 本次实验采用MS-COCO 数据集进行训练,由于数据量过于庞大,所以仍然选择通过云平台训练。

  1. MS-COCO 数据集

    MS-COCO(Microsoft Common Objects in Context Dataset)是一个大规模的图像识别、分割和描述的数据集,由微软研究院于2014年发布。

    1711795050488

    {"images": [{"filepath": "val2014", "sentids": [770337, 771687, 772707, 776154, 781998], "filename": "COCO_val2014_000000391895.jpg", "imgid": 0, "split": "test", "sentences": [{"tokens": ["a", "man", "with", "a", "red", "helmet", "on", "a", "small", "moped", "on", "a", "dirt", "road"], "raw": "A man with a red helmet on a small moped on a dirt road. ", "imgid": 0, "sentid": 770337}, {"tokens": ["man", "riding", "a", "motor", "bike", "on", "a", "dirt", "road", "on", "the", "countryside"], "raw": "Man riding a motor bike on a dirt road on the countryside.", "imgid": 0, "sentid": 771687}, {"tokens": ["a", "man", "riding", "on", "the", "back", "of", "a", "motorcycle"], "raw": "A man riding on the back of a motorcycle.", "imgid": 0, "sentid": 772707}, {"tokens": ["a", "dirt", "path", "with", "a", "young", "person", "on", "a", "motor", "bike", "rests", "to", "the", "foreground", "of", "a", "verdant", "area", "with", "a", "bridge", "and", "a", "background", "of", "cloud", "wreathed", "mountains"], "raw": "A dirt path with a young person on a motor bike rests to the foreground of a verdant area with a bridge and a background of cloud-wreathed mountains. ", "imgid": 0, "sentid": 776154}, {"tokens": ["a", "man", "in", "a", "red", "shirt", "and", "a", "red", "hat", "is", "on", "a", "motorcycle", "on", "a", "hill", "side"], "raw": "A man in a red shirt and a red hat is on a motorcycle on a hill side.", "imgid": 0, "sentid": 781998}], "cocoid": 391895}...
    

    以上为 COCO 数据集的 json 文件,可以看到每个图片包含以下内容:

    • "filepath": "val2014": 表示图像所在的文件夹路径。
    • "sentids": [770337, 771687, 772707, 776154, 781998]: 这是一个句子 ID 列表,每个 ID 对应一个描述该图像的句子。
    • "filename": "COCO_val2014_000000391895.jpg": 图像的文件名。
    • "imgid": 0: 图像的唯一标识符(image ID),在这里被设置为 0。
    • "split": "test": 表示这张图像属于数据集的测试集(test set)。
    • "sentences": 这是一个句子列表,每个句子都是对图像的描述。
      • 每个句子都有一个 "tokens" 属性,它是一个包含描述中单词的列表(已经分词)。
      • "raw" 属性包含了原始的描述句子(未经分词的完整句子)。
      • "imgid""sentid" 分别是图像 ID 和句子 ID,用于关联特定的图像和描述。
    • "cocoid": 391895: 这是图像在 COCO 数据集中的唯一标识符。
  2. 云平台环境创建

    通过 Fork 作业,创建环境,镜像为Cuda11.3.1 PyTorch 1.10.2 Tensorflow2.8.0 Python 3.7。注意本次训练需生成大量文件,所以需要挂载 work 文件夹,用于存储数据文件。

    通过 train.ipynb 中以下代码,配置环境。

    !pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
    !pip install -U nltk
    

    通过以下代码,组合数据集。

    !mkdir /home/mw/project/coco2014
    !ln -s /home/mw/input/020614521/test2014/test2014 /home/mw/project/coco2014
    !ln -s /home/mw/input/02066579/val2014/* /home/mw/project/coco2014
    !ln -s /home/mw/input/020628093/train2014-1/train2014-1 /home/mw/project/coco2014
    !ln -s /home/mw/input/020631458/train2014-2/train2014-2 /home/mw/project/coco2014
    
  3. 配置数据集文件

    修改 create_input_files.py 文件内容,更改输入输出文件路径。

    if __name__ == '__main__':
        # Create input files (along with word map)
        create_input_files(dataset='coco',
           karpathy_json_path='/home/mw/project/coco2014/dataset_coco.json',   # downloaded from http://cs.stanford.edu/people/karpathy/deepimagesent/caption_datasets.zip
           image_folder='/home/mw/project/coco2014/',
           captions_per_image=5,
           min_word_freq=5,
           output_folder='/home/mw/work/work_dir/coco/',
           max_len=50)
    

    通过以下指令,运行 create_input_files.py 脚本文件,生成需要的 jsonhdf5 文件,注意指定输入和输出数据存放的位置。

    %cp /home/mw/project
    !python create_input_files.py
    

    运行成功后,得到以下文件。

    TEST_CAPLENS_coco_5_cap_per_img_5_min_word_freq.json
    TEST_CAPTIONS_coco_5_cap_per_img_5_min_word_freq.json
    TEST_IMAGES_coco_5_cap_per_img_5_min_word_freq.hdf5
    TRAIN_CAPLENS_coco_5_cap_per_img_5_min_word_freq.json
    TRAIN_CAPTIONS_coco_5_cap_per_img_5_min_word_freq.json
    TRAIN_IMAGES_coco_5_cap_per_img_5_min_word_freq.hdf5
    VAL_CAPLENS_coco_5_cap_per_img_5_min_word_freq.json
    VAL_CAPTIONS_coco_5_cap_per_img_5_min_word_freq.json
    VAL_IMAGES_coco_5_cap_per_img_5_min_word_freq.hdf5
    WORDMAP_coco_5_cap_per_img_5_min_word_freq.json
    
  4. 修改配置信息

    修改 cfg 内容,配置训练路径。如果中间训练中断,可以通过修改 checkpoint 路径来继续之前的训练。

    cfg = {
        # Data parameters
        'data_folder' : '/home/mw/work/work_dir/coco/',  
        'data_name' : 'coco_5_cap_per_img_5_min_word_freq',  # base name shared by data files
        # Model parameters
        'embed_dim' : 512,  # dimension of word embeddings
        'attention_dim' : 512,  # dimension of attention linear layers
        'decoder_dim' : 512,  # dimension of decoder RNN
        'dropout' : 0.5,
        'device' : torch.device("cuda:0" if torch.cuda.is_available() else "cpu"),  # sets device for model and PyTorch tensors
        # Training parameters
        'start_epoch' : 0,
        'epochs' : 10,  # number of epochs to train for (if early stopping is not triggered)
        'epochs_since_improvement' : 0,  # keeps track of number of epochs since there's been an improvement in validation BLEU
        'batch_size' : 32,
        'workers' : 1,  # for data-loading; right now, only 1 works with h5py
        'encoder_lr' : 1e-4,  # learning rate for encoder if fine-tuning
        'decoder_lr' : 4e-4,  # learning rate for decoder
        'grad_clip' : 5.,  # clip gradients at an absolute value of
        'alpha_c' : 1.,  # regularization parameter for 'doubly stochastic attention', as in the paper
        'best_bleu4' : 0.,  # BLEU-4 score right now
        'print_freq' : 100,  # print training/validation stats every __ batches
        'fine_tune_encoder' : False,  # fine-tune encoder or not
        'checkpoint' : None,  # path to checkpoint, None if none
        'attention' : False, # train decoder with attention or not
    }
    cudnn.benchmark = True  # set to true only if inputs to model are fixed size; otherwise lot of computational overhead
    
  5. 开始训练

    运行 train.ipynb 的全部代码,开始训练。

    image-20240330185723174

    出现以上信息,说明成功开始训练,每轮训练结束会进行验证集验证,并生成两个 checkpoint 文件:

    BEST_checkpoint_NoAttention.pth.tar
    checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar
    

    代表着训练最佳的模型,与训练记录。

    每轮的训练结果如下:

    * LOSS - 2.561, TOP-5 ACCURACY - 72.468, BLEU-4 - 0.19211562851102434
    * LOSS - 2.444, TOP-5 ACCURACY - 74.052, BLEU-4 - 0.206167972668384
    * LOSS - 2.397, TOP-5 ACCURACY - 74.702, BLEU-4 - 0.21208995512435172
    * LOSS - 2.375, TOP-5 ACCURACY - 75.007, BLEU-4 - 0.2132522999078148
    * LOSS - 2.361, TOP-5 ACCURACY - 75.335, BLEU-4 - 0.21634075906470107
    * LOSS - 2.356, TOP-5 ACCURACY - 75.334, BLEU-4 - 0.21701614735505384
    * LOSS - 2.350, TOP-5 ACCURACY - 75.414, BLEU-4 - 0.21586192777255203
    * LOSS - 2.352, TOP-5 ACCURACY - 75.464, BLEU-4 - 0.21914566402602026
    * LOSS - 2.353, TOP-5 ACCURACY - 75.423, BLEU-4 - 0.2185600676157566
    * LOSS - 2.351, TOP-5 ACCURACY - 75.542, BLEU-4 - 0.21694456413193874
    

2.3 项目测试

  1. 模型评估

    修改数据集地址后,运行 test.ipynb,进行测试。测试采用 BLUE-4 参数进行模型评估。

    BLUE-4 旨在提供对线性模型参数的最佳估计,以便它们是线性、无偏、具有最小方差。具体来说,BLUE-4 的估计量通过以下公式计算:
    β ^ B L U E − 4 = ( X T W X ) − 1 X T W y \hat{\beta}_{BLUE-4} = (X^TWX)^{-1} X^TWy β^BLUE4=(XTWX)1XTWy
    其中:

    • β ^ B L U E − 4 \hat{\beta}_{BLUE-4} β^BLUE4 是参数向量的估计值。
    • X X X 是设计矩阵(包含自变量的观察值)。
    • Y Y Y 是响应向量(因变量的观察值)。
    • W W W 是加权矩阵,用于处理异方差和/或自相关的问题。

    在 BLUE-4 中, W W W 的选择对估计结果至关重要。通常情况下, W W W 被选择为方差-协方差矩阵的逆矩阵,其中方差-协方差矩阵包含了误差项的方差和协方差信息。通过适当地选择 W W W,可以消除异方差和自相关的影响,从而产生更稳健、更高效的估计结果。

    总之,BLUE-4 提供了对线性模型参数的最佳估计,即它们是线性、无偏、具有最小方差的估计。通过适当地选择加权矩阵 W W W​ ,可以解决模型中可能存在的异方差和自相关问题。

    ​ 不采用 Attention 机制训练结果,在 25000 张测试集结果为 BLUE-4 = 0.2671296429923623

    image-20240330200718783

  2. 图像输出

    通过 caption.ipynb 加载指定模型,对单张输入图片进行语言描述。对测试集图片 COCO_test2014_000000438031 结果如下。

    a0c05abc2bd44c22a9a3a14d60460be

    可以看到模型形容这张图片内容为 “a table that has some food on it”,较为准确的描述了图片内容。

    上传一张图片,测试模型的性能,发现面对在训练集没有出现的东西,模型是不认识的。

    1711801383711

    模型错误的将月球上的宇航员,认成了 “a white dog laying on top of a surfboard”,一只白色的狗躺在冲浪板上。

3 采用带有 Attention 机制的 Decoder 训练

3.1 代码补全

​ 本部分需要补全 Attention(nn.Module)DecoderWithAttention(nn.Module)TODO 部分代码。

  1. 定义 Attention 需要的层

    注意力模块定义了三个线性层,它们负责转换编码器与解码器的维度,与将注意力维度转换成一个一位输出。

    def __init__(self, encoder_dim, decoder_dim, attention_dim):
        super(Attention, self).__init__()
        self.encoder_att = nn.Linear(encoder_dim, attention_dim)
        self.decoder_att = nn.Linear(decoder_dim, attention_dim)
        self.att = nn.Linear(attention_dim, 1)
    
  2. 注意力模块前向传播

    这段代码过注意力机制,结合编码器和解码器的信息,动态地生成了一个加权的上下文表示 z,这个表示将被用于解码器生成序列的下一个词。注意力权重 alpha 显示了每个特征对当前生成词的贡献程度。

    def forward(self, encoder_out, decoder_hidden):
    	att1 = self.encoder_att(encoder_out)
        att2 = self.decoder_att(decoder_hidden).unsqueeze(1)
        e = self.att(F.relu(att1 + att2))
        alpha = F.softmax(e, dim=1)
        z = (encoder_out * alpha).sum(dim=1)
    
        return z, alpha
    
  3. 定义 DecoderWithAttention 需要的层

    • 注意力机制模块:将编码器输出的特征维度(encoder_dim),解码器隐藏状态的维度(decoder_dim),以及注意力网络的内部维度(attention_dim)作为参数。
    • 嵌入层:将词汇表中的单词索引映射到嵌入空间中的向量表示。
    • 解码器 LSTM 细胞:将嵌入层的输出与编码器提供的上下文向量结合起来,并输出解码器的下一个隐藏状态。
    • 线性层h:用于初始化解码器的隐藏状态。它的输入是编码器的输出,输出维度与解码器的隐藏状态维度相匹配。
    • 线性层c:用于初始化解码器的单元状态。结构和目的与 init_h 类似。
    • Sigmoid 激活函数层:用于生成权重 beta,这个权重通常用于控制注意力机制的输出,在解码器的隐藏状态中的影响。
    • 全连接层:将解码器的隐藏状态映射到词汇表上的概率分布。
    • Dropout 层:用于在训练过程中随机丢弃一些神经元的输出,以减少过拟合。
    # 注意力模块
    self.attention = Attention(encoder_dim, self.decoder_dim, self.attention_dim)
    # 嵌入层
    self.embedding = nn.Embedding(self.vocab_size, self.embed_dim)
    # 解码器 LSTM 细胞
    self.decode_step = nn.LSTMCell(self.embed_dim + self.encoder_dim, self.decoder_dim)
    # 线性层h,用于初始化解码器的隐藏状态
    self.init_h = nn.Linear(self.encoder_dim, self.decoder_dim)
    # 线性层c,用于初始化解码器的单元状态
    self.init_c = nn.Linear(self.encoder_dim, self.decoder_dim)
    # Sigmoid 激活函数层
    self.beta = nn.Sigmoid()
    # 全连接层
    self.fc = nn.Linear(self.decoder_dim, self.vocab_size)
    # Dropout 层
    self.dropout = nn.Dropout(p=self.dropout)
    
  4. 解码器前向传播(forward)

    遍历解码过程的每个时间步,在每个时间步生成一个单词,并更新其内部状态。注意力权重 alpha 显示了模型在生成每个单词时对输入序列的不同部分的关注程度。

    for t in range(max(decode_lengths)):
        num = sum([1 if l > t else 0 for l in decode_lengths])
        preds, alpha, h, c = self.one_step(embeddings[:num, t, :], encoder_out[:num], h[:num], c[:num])
        predictions[:num,t,:] = preds
        alphas[:num,t,:] = alpha
    
  5. 单步解码 (one_step)

    • self.attention(encoder_out, h): 通过注意力机制计算得到上下文向量 z 和注意力权重 alpha
    • F.sigmoid(self.f_beta(h)): 计算一个调整因子 beta,它用于调整上下文向量 z 的影响。
    • torch.cat([embeddings, z], dim=1): 将当前时间步的嵌入向量 embeddings 和调整后的上下文向量 z 拼接起来,作为 LSTM 细胞的输入。
    • self.decode_step(...): 调用 LSTM 细胞来更新隐藏状态 h 和单元状态 c
    • self.fc(h): 将更新后的隐藏状态 h 通过一个全连接层来生成预测分数 preds,这些分数表示模型对下一个单词的预测。
    • self.dropout(h): 应用 Dropout 正则化技术于隐藏状态 h,以减少过拟合的风险。
    def one_step(self, embeddings, encoder_out, h, c):
        z, alpha = self.attention(encoder_out, h)
        beta = F.sigmoid(self.f_beta(h))
        z = beta * z
        h, c = self.decode_step(torch.cat([embeddings,z], dim=1),(h,c))
        preds = self.fc(self.dropout(h))
    
        return preds, alpha, h, c
    

3.2 项目训练

​ 修改 train.ipynb 的配置文件,将 attention 改为 True,开始训练,训练时长比不用注意力机制更长,进行了20多个小时的训练,完成10轮的训练,最后一轮的验证结果如下。

image-20240402234128029

​ 每轮的训练结果如下:

 * LOSS - 3.414, TOP-5 ACCURACY - 73.007, BLEU-4 - 0.20026115980658207
 * LOSS - 3.305, TOP-5 ACCURACY - 74.551, BLEU-4 - 0.2129326655784661
 * LOSS - 3.258, TOP-5 ACCURACY - 75.256, BLEU-4 - 0.220877622883263
 * LOSS - 3.234, TOP-5 ACCURACY - 75.598, BLEU-4 - 0.2232005371638974
 * LOSS - 3.219, TOP-5 ACCURACY - 75.904, BLEU-4 - 0.22577627017891636
 * LOSS - 3.209, TOP-5 ACCURACY - 76.118, BLEU-4 - 0.22701703762313327
 * LOSS - 3.207, TOP-5 ACCURACY - 76.132, BLEU-4 - 0.22905155826158327
 * LOSS - 3.208, TOP-5 ACCURACY - 76.158, BLEU-4 - 0.23044049990411558
 * LOSS - 3.206, TOP-5 ACCURACY - 76.249, BLEU-4 - 0.23045871177259478
 * LOSS - 3.206, TOP-5 ACCURACY - 76.262, BLEU-4 - 0.2292431787310961

3.3 项目测试

  1. 模型评估

    运行 test.ipynb,进行测试。测试采用 BLUE-4 参数进行模型评估。结果如下。

    image-20240401202358685

    BLUE-4 值达到0.27923479690610103,效果优于不用attention机制的结果。

  2. 图像输出

    通过 caption.ipynb 加载指定模型,并令 attentionTrue ,对单张输入图片进行语言描述。对测试集图片 COCO_test2014_000000438031 结果如下。

    img

    1711972507424

    可以看到,结果显示出来了自然语言的单词的注意力在图像的哪里。

    COCO_test2014_000000049721.jpg 图片进行测试,结果如下。

    1711972791549

    1711972802295

    可以看到模型精准的描述了图像信息,用一个长句子描述出了菜板上胡萝卜特写的图像信息。

4 模型调优

4.1 Adaptive Attention

​ Adaptive Attention,或称为自适应注意力,是一种在神经网络序列到序列(seq2seq)模型中使用的注意力机制。与传统的注意力机制相比,自适应注意力允许模型在不同的时间步动态调整其关注输入序列的哪些部分。这种机制特别适用于处理长序列数据,如自然语言处理中的长文本,或者图像描述任务中的多区域图像。

image-20240403010841139

​ 图(a)为普通注意力机制的架构,图(b)为作者改进后的架构。

img

详细信息查看论文 Knowing When to Look: Adaptive Attention via A Visual Sentinel for Image Captioning

以及github仓库:jiasenlu/AdaptiveAttention: Implementation of “Knowing When to Look: Adaptive Attention via A Visual Sentinel for Image Captioning” (github.com)

  1. 工作原理

    • 输入序列编码:首先,输入序列(例如,一句话或一张图像)被编码成一个固定长度的上下文向量或者一系列的隐藏状态。
    • 动态权重分配:在解码过程中,自适应注意力机制会根据解码器当前的状态和已经生成的输出动态计算注意力权重。这意味着在每个解码步骤,模型都会重新评估并分配对输入序列不同部分的关注程度。
    • 上下文向量调整:根据计算出的注意力权重,模型调整(加权平均)输入序列的隐藏状态,以生成一个新的上下文向量,这个向量更加关注当前步骤最相关的信息。
    • 解码器更新:使用调整后的上下文向量,解码器生成下一个输出(例如,下一个单词或下一个字符)。
  2. 优势

    • 灵活性:自适应注意力机制提供了更大的灵活性,因为它允许模型根据当前的解码状态来调整其关注点,而不是在整个序列中均匀地分配注意力。
    • 减少信息冗余:通过关注与当前任务最相关的信息,自适应注意力有助于减少无关信息的干扰,从而提高模型的性能。
    • 处理长序列:对于长序列,自适应注意力特别有效,因为它可以避免在解码过程中重复使用相同的信息。
  3. 实现

    作者采用 lua 进行代码的编写,我未能成功移植 Adaptive Attention 代码,最终没能实现

4.2 fine_tune_encoder

  1. fine_tune

    令配置中 fine_tune_encoderTrueFine-tuning是一种训练策略,指的是在预训练模型的基础上进行额外的训练,以使模型更好地适应特定的任务或数据集。具体到fine_tune_encoder,这意味着我们将对编码器部分的权重进行微调,以便它能够更好地捕捉到新任务相关的特征。

    def fine_tune(self, fine_tune=True):
        """
        Allow or prevent the computation of gradients for convolutional blocks 2 through 4 of the encoder.
        :param fine_tune: boolean
        """
        for p in self.resnet.parameters():
            p.requires_grad = False
        # If fine-tuning, only fine-tune convolutional blocks 2 through 4
        for c in list(self.resnet.children())[5:]:
            for p in c.parameters():
                p.requires_grad = fine_tune
    
    • 如果 fine_tuneTrue,那么参数 p 将被设置为需要计算梯度。这意味着在训练过程中,当反向传播(backpropagation)通过这个参数时,PyTorch将会计算它的梯度,并且在优化步骤中更新它的值。这通常在你想要训练或微调模型参数时进行。
    • 如果 fine_tuneFalse,那么参数 p 将被设置为不需要计算梯度。这意味着在反向传播过程中,即使这个参数被用于计算损失,它的梯度也不会被计算,因此在优化步骤中它的值也不会被更新。这通常用于冻结模型的某些层,即在训练过程中保持这些层的参数不变。
  2. 项目训练

    因为采用 fine tune 机制,所以训练时长要更长,每轮训练几乎需要4h时间,但效果有明显提升。实现没有完整的进行10轮的训练,以下为训练4轮的结果。

    image-20240403142543543

    * LOSS - 3.371, TOP-5 ACCURACY - 73.921, BLEU-4 - 0.21145163883000279
    * LOSS - 3.245, TOP-5 ACCURACY - 75.622, BLEU-4 - 0.2242869197515953
    * LOSS - 3.197, TOP-5 ACCURACY - 76.253, BLEU-4 - 0.2327172001811528
    * LOSS - 3.166, TOP-5 ACCURACY - 76.723, BLEU-4 - 0.23495777605616544
    
  3. 项目测试

    • 模型评估

      运行 test.ipynb,进行测试。测试采用 BLUE-4 参数进行模型评估。经过长时间的测试,结果如下。

      image-20240403154540032

      可以看到,BLUE-4 的值达到了 0.30035458724647623 ,远远高于前面两组实验。

    • 图像测试

      通过 caption.ipynb 加载指定模型,并令 attentionTrue ,对单张输入图片进行语言描述。对测试集图片 COCO_test2014_000000438031 结果如下。

      sbcsp1qi4b

      sbcsp2iij5

      可以看到,结果将图片中的内容描述的更加详细了,相比于未使用 fine-tune 的描述,增加了对桌子上 drinks 的形容,说明模型对图像理解能力更强了。

      COCO_test2014_000000049721.jpg 图片进行测试,结果如下。

      sbcwght5aj

      sbcwgi3rz6

      可以看到,相比于未使用 fine tune 的结果,该结果对图像的注意力明显更加集中,对图像的描述也更加准确。

      再次用宇航员登月的图片进行测试,结果如下。

      sbcwmws7bf

      sbcwmwzwh2

      发现虽然模型仍然不认识宇航员,但是能够分辨出来这是个 “小男孩站在沙滩上” 和图片的信息已经十分贴切了,比之前模型将其看成 “小狗站在冲浪板上” 准确了很多。

5 结论

​ 本项目进行了3轮训练,分别在无注意力机制,有注意力机制,注意力机制加上fine tune的基础上进行三组训练。三组训练的每轮 lossAccuarcyBlue-4 绘制到折线图上,结果如下。

image-20240403144154815

​ 可以看到,添加 fine tune 机制的训练结果,虽然只训练了4轮,但是在 AccuarcyBlue-4 的得分远超另外两组结果。

​ 对比三组模型在测试集的BLUE-4的结果,如下表格。

Without AttentionWith AttentionAttention+fine tune
0.26712964299236230.279234796906101030.30035458724647623

​ 可以看到第三组的 BLUE-4 值远远高于前两组,说明带有fine tune 的编码器对训练有着明显提升。

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

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

相关文章

Unity开发者3D模型基础

术语“3D 建模”是指使用特殊软件创建对象或表面的 3D 数字表示的过程。 3D 模型可用于各种不同的目的&#xff0c;包括电影、视频游戏、建筑和工程。 3D 建模也是创建虚拟现实 (VR) 和增强现实 (AR) 体验工作的重要组成部分。 我们通常通过构建或获取 3D 模型并将其导入 Unit…

设置win32程序的窗口过程为自定义的窗口过程

Win32 SetWindowLong函数的第二个参数是一个整型,可以取不同的值,根据此值设置窗口的不同的属性;如果设为 GWL_WNDPROC 则可以为窗口设置一个另外的窗口过程; VC6新建一个win32项目,类型选为 典型helloworld 程序; 资源中添加一个菜单, 总的代码如下; // setproc.cp…

NineData云原生智能数据管理平台新功能发布|2024年3月版

数据库 DevOps - 大功能升级 SQL 开发早期主要提供 SQL 窗口&#xff08;IDE&#xff09;功能&#xff0c;在产品经过将近两年时间的打磨&#xff0c;新增了大量的企业级功能&#xff0c;已经服务了上万开发者&#xff0c;覆盖了数据库设计、开发、测试、变更等生命周期的功能…

正则表达式完全指南:语法、用法及JavaScript实例

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

K8s Deployment 滚动更新、金丝雀发布、自定义钩子、生命周期解析

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、Deployment的高级特性 1、滚动更新 2、金丝雀…

harmonyOS的客户端存贮

什么是客户端存贮 在harmonyOS中,客户端存贮是指将数据存贮在本地设备以供应用程序使用; 注: 和feaureAblity搭配使用,content上下文的获取依赖该API如下: // 引入: import featureAbility from ohos.ability.featureAbility;// 使用: let content featureAbility.getConten…

STM32串口认识

常用函数介绍 串口发送/接收函数 HAL_UART_Transmit(); 串口发送数据&#xff0c;使用超时管理机制&#xff08;即在发送成功前一直阻塞&#xff0c;相当于有个delay&#xff08;&#xff09;在卡住主函数&#xff09; HAL_UART_Receive(); 串口接收数据&#xff0c;使用超时…

极简云验证 download.php 文件读取漏洞复现

0x01 产品简介 极简云验证是一款开源的网络验证系统&#xff0c;支持多应用卡密生成&#xff1a;卡密生成 单码卡密 次数卡密 会员卡密 积分卡密、卡密管理 卡密长度 卡密封禁 批量生成 批量导出 自定义卡密前缀等&#xff1b;支持多应用多用户管理&#xff1a;应用备注 应用版…

Android JNI基础

目录 一、JNI简介1.1 什么是JNI1.2 用途1.3 优点 二、初探JNI2.1 新建cpp\cmake2.2 build.gradle配置2.3 java层配置2.4 cmake和c 三、API详解3.1 JNI API3.1.1 数据类型3.1.2 方法 3.2 CMake脚本 四、再探JNI 一、JNI简介 1.1 什么是JNI JNI&#xff08;Java Native Interfa…

开源软件技术社区方案

开源软件技术社区是一个由开发者、贡献者、用户和维护者组成的共享平台&#xff0c;主要目的是打造技术、软件产品良性互动、开源技术安全可控的软件生态环境&#xff0c;实现可复用应用或服务的快速部署与使用、完成资源与能力的高度共享、促进社区成员的共建共赢&#xff0c;…

面试复盘1 - 测试相关(实习)

写在前&#xff1a;hello&#xff0c;大家早中晚上好~这里是西西&#xff0c;最近有在准备测试相关的面试&#xff0c;特此开设了新的篇章&#xff0c;针对于面试中的问题来做一下复盘&#xff0c;会把我自己遇到的问题进行整理&#xff0c;除此之外还会进行对一些常见面试题的…

什么是异地组网?

在当今互联网普及的背景下&#xff0c;异地组网已成为许多企业和个人日常工作中不可或缺的一部分。异地组网的核心目的是实现不同地区间的快速、高效的局域网连接。在这个过程中&#xff0c;延迟&#xff08;即网络数据传输的时间延迟&#xff09;是一个关键影响因素。我们来看…

还得是抖音,字节推出竖屏视频理解数据集,入选CVPR2024

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 短视频在当下社交媒体逐渐成为主导的视频格式。传统视频处理技术和研究一般都专注于横屏视频…

【快速解决】python缺少了PyQt5模块的QtMultimedia子模块

目录 问题描述 问题原因 解决方法 成功示范 问题描述 Traceback (most recent call last): File "d:\桌面\python项目\DesktopWords-master\main.py", line 4, in <module> from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent ModuleNotFoundEr…

Unity自定义icon

Unity自定义icon 1. 新建文件夹 OfficeFabricIconSet2. 新建Iconset3. 新建子文件夹Textures并添加icon图片4. 向iconset添加Quad Icons5. 最终效果 教程来源处&#xff1a; https://365xr.blog/build-your-own-button-icon-set-for-microsoft-hololens-2-apps-with-mrtk-using…

stm32之基本定时器的使用

在上文我们使用到了HAL库的自带的延时函数&#xff0c;HAL_Delay&#xff08;&#xff09;&#xff1b;我们来看一下函数的原型 __weak void HAL_Delay(uint32_t Delay) {uint32_t tickstart HAL_GetTick();uint32_t wait Delay;/* Add a freq to guarantee minimum wait */…

程序员为什么不能一次性写好,需要一直改Bug?

程序员为什么不能一次性写好&#xff0c;需要一直改Bug&#xff1f; 我有一问&#xff1a; 你为什么不上清华呢&#xff0c;高考答满分不就行了&#xff1f; 程序员在软件开发过程中可能会遇到需要不断修改Bug的情况&#xff0c;这主要是由以下几个原因造成的&#xff1a; 复杂…

5.3.1 配置交换机 SSH 管理和端口安全

5.3.1 实验1:配置交换机基本安全和 SSH管理 1、实验目的 通过本实验可以掌握&#xff1a; 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 2、实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 交换机基本安全和 SSH管理实验拓扑 3、实验步骤 &a…

FreeBSD下如何进行添加路由等配置?

FreeBSD系统可以当作一台路由器来使用&#xff0c;实际上当年路由器刚刚出来的时候&#xff0c;有很大一部分就是用主机安装FreeBSD来提供路由功能的。 路由的基础知识 路由&#xff0c;就是计算机网络中决定数据包从源地址到目的地址路径的一种传输过程。路由表&#xff0c;就…