Show,Attend and tell代码复现---pytorch方法手把手一步一步实现

目录

采用的代码参考:

github:Show Attend and tell代码实现

参考的大神博客:

https://blog.csdn.net/weixin_44826203/article/details/107609852
https://blog.csdn.net/qq_45893319/article/details/120047448

第一步:下载代码到本地

在这里插入图片描述

第二步:数据准备

1)在create_input_files.py文件中可以看到代码

karpathy_json_path='../caption data/dataset_coco.json',

也就是在上一级目录下有个文件夹caption data,然后caption data文件夹下有个文件dataset_coco.json,所以我们要下载这个文件,这个文件在上面的参考博客中可以找到。下载后放到create_input_files.py的同级目录下,然后将路径代码改为

karpathy_json_path='./caption data/dataset_coco.json'

因为…是上一级目录,.是同一级目录,当然这个随你自己,只要路径和文件位置对应一致就行。

在这里插入图片描述
在这里插入图片描述
2)下载COCO数据集,在前面借鉴的大佬博客第一个中可以找到百度网盘链接。下载成功后,将train2014和val2014放在media/ssd/caption data文件夹下面。这个media应该是你工程所在的盘下。
在这里插入图片描述

第三步:看哪里报错,比如库没有安装啊,先去下载

第四步-1:运行看哪里有错,然后去改正这些错误(以下是我在运行create_input_files.py文件时遇到的错误)

下面错误1,4,6是一块的问题,所以大家看的时候直接看一下6的最终的代码的样子,结合起来改哦

错误一:cannot import name ‘imread’ from ‘scipy.misc’

错误出现的原因:较新版本的 SciPy 中已经移除了 imread 和 imresize 函数。可以换其他的库来实现。我选择是的是PIL库。在这里插入图片描述

错误一解决办法:

将utils.py文件中的代码:

from scipy.misc import imread, imresize

改为:

from PIL import Image

将下面图片爆红的部分
在这里插入图片描述
改为:

img = Image.open(impaths[i])
img = img.resize((256, 256))

错误2:找不到’/media/ssd/caption data/WORDMAP_coco_5_cap_per_img_5_min_word_freq.json’这个文件

在这里插入图片描述

错误2解决办法:

由于没有这个文件,因此得去找以下网址下载:
WORDMAP_coco_5_cap_per_img_5_min_word_freq.json下载,只下载这一个
下载完成后,根据报错里面的绝对路径,将下载下来的文件放在media/ssd/caption data文件夹下。由于这是个绝对路径,所以media文件夹是在你这个工程所在盘的下面建的。然后错误解决。
在这里插入图片描述

错误3:Unable to create dataset (name already exists)

错误出现的原因:尝试创建一个数据集时,数据集的名称已经存在。
在这里插入图片描述

错误3解决办法

将utils.py(98行)文件中的代码:

images = h.create_dataset('images', (len(impaths), 3, 256, 256), dtype='uint8')

改为

images = h.create_dataset('imagescopy', (len(impaths), 3, 256, 256), dtype='uint8')

错误4:util.py文件中报错’JpegImageFile’ object has no attribute ‘shape’

错误出现原因:因为前面发现较新版本的scipy中已经移除了imread和imresize函数,因此换了其他的库PIL来实现。但是PIL库中的图像对象并不直接具有shape这样的属性,因此会爆下列错误。在这里插入图片描述

错误4解决办法:

在这里插入图片描述

错误5:又出现数据集重名的问题

我发现这是因为当每一次运行create_input_files.py这个文件,就会生成一个数据集,但是如果因为后面出错你改正后,并没有更改代码中生成的数据集的名字就去跑代码,这样代码又要重新生成一次这个名字的数据集,所以每一次运行都要改这个数据集的名字
在这里插入图片描述

错误5解决办法:

每一次运行都更改这个数据集的名字

images = h.create_dataset('imagesc', (len(impaths), 3, 256, 256), dtype='uint8')

错误6:代码中使用了断言来确保图像数组的形状为 (3, 256, 256),但是实际上出现了断言失败的情况。

出现原因是我之前改代码的时候忘记把调整图像大小的代码加上去了。
在这里插入图片描述

错误6解决办法

加上这个调整图像大小的代码,print(img_array.shape)这行代码是我用来打印我的图像到底是多大才加的。顺带这段代码还有错误的地方一起改掉。
在这里插入图片描述

然后create_input_files.py终于可以运行了,但是跑的很慢,毕竟我是CPU。

在运行这个文件之前有个小提示,如果你因为改错已经运行过很多次这个文件了,你会发现在caption data文件夹下有个HDF5文件,非常地大,这时请务必删掉它再去运行,不然磁盘内存会很快爆满。
在这里插入图片描述
运行成功后的截图:
在这里插入图片描述
再查看文件里面有什么变化没,发现9个文件都生成了。
在这里插入图片描述

第四步-2:运行train.py文件,开始继续调试代码,遇到的错误如下

首先是3个警告

ResNet-101下载成功,但是有两个警告。
第1个警告:dataset.py第54行有个语法警告,建议用==代替is。
第2个警告:这是针对TorchVision中的模型加载函数的更新提示。pretrained参数已经被弃用,建议用weights参数来替代。
在这里插入图片描述

警告解决办法:

第一个警告:用==替换is
在这里插入图片描述
第二个警告:根据警告提示更改model.py文件中第18行的代码如下:
注释:这行代码的作用是加载一个预训练的ResNet-101模型,该模型已经在ImageNet数据集上进行了训练。

resnet = torchvision.models.resnet101(weights=torchvision.models.ResNet101_Weights.DEFAULT)

错误1:h5py objects cannot be pickled

错误出现原因:由于尝试对 h5py 对象进行序列化(pickling)而导致的。在 Python 中,通过多进程加载数据时,数据会被序列化并传递给子进程。然而,h5py 对象并不支持序列化,因此会导致这个错误。
在这里插入图片描述

错误1解决办法:

参考博客:博客
将train.py文件中(93行开始)代码:

train_loader = torch.utils.data.DataLoader(
        CaptionDataset(data_folder, data_name, 'TRAIN', transform=transforms.Compose([normalize])),
        batch_size=batch_size, shuffle=True, num_workers=workers, pin_memory=True)
val_loader = torch.utils.data.DataLoader(
        CaptionDataset(data_folder, data_name, 'VAL', transform=transforms.Compose([normalize])),
        batch_size=batch_size, shuffle=True, num_workers=workers, pin_memory=True)

修改为(其实就是删除两个num_workers):

train_loader = torch.utils.data.DataLoader(
        CaptionDataset(data_folder, data_name, 'TRAIN', transform=transforms.Compose([normalize])),
        batch_size=batch_size, shuffle=True, pin_memory=True
    )
val_loader = torch.utils.data.DataLoader(
        CaptionDataset(data_folder, data_name, 'VAL', transform=transforms.Compose([normalize])),
        batch_size=batch_size, shuffle=True, pin_memory=True)

错误2:too many values to unpack (expected 2)

错误出现原因:这个错误表明在执行 pack_padded_sequence 函数时出现了期望接收两个返回值,但实际上返回的值数量超过了两个,导致无法正确解包。pack_padded_sequence 函数用于对填充过的序列进行打包,以便在循环神经网络中进行处理。
在这里插入图片描述

错误2解决办法:

参考博客:博客
博客2
将train.py文件中(189行起)的代码:

scores, _ = pack_padded_sequence(scores, decode_lengths, batch_first=True)
targets, _ = pack_padded_sequence(targets, decode_lengths, batch_first=True)

改为:

scores = pack_padded_sequence(scores, decode_lengths, batch_first=True)[0]
targets = pack_padded_sequence(targets, decode_lengths, batch_first=True)[0]

这两行代码在后面验证部分(282行)也出现过,记得都更改掉。

train.py运行成功啦,不过还是很慢,慢死了跑了一天一轮都没跑完~

在这里插入图片描述

租AutoDL服务器开始跑(方法在我的另一篇文章里,这里只说用服务器跑的方式)

先改一下create_input_files里面的image_folder和output_folder的路径,因为是绝对路径,你就看你服务器上文件夹的路径是哪,把地址直接弄到这里就行了

在这里插入图片描述
create_input_files成功开始运行
在这里插入图片描述
在这里插入图片描述

接着训练train.py

错误1:没安装nltk

在这里插入图片描述

错误1解决方法

在这里插入图片描述

错误2:找不到/media/ssd/caption data/WORDMAP_coco_5_cap_per_img_5_min_word_freq.json

路径错了,改一下
在这里插入图片描述

错误2解决方式

在这里插入图片描述
train.py开始正常运行,用GPU跑就是快。

我发现在Pycharm上跑train.py有个问题,那就是如果代码跑很久的话,我就得把电脑一直开机。所以这里尝试用Jupyter Lab的终端上面跑train.py。如果想要防止电脑关机或者断网终止你的训练,看下面的第二点(没写完,后面补充,因为本文后面还是用pycharm运行的哈哈哈)。

  1. 打开Jupyter Lab的终端,当然是在自己存放程序代码的那个文件位置下的终端。然后输入python train.py &&shutdown(&&shutdown是为了跑完train.py后云主机自动关机,避免产生不必要的消费,当然不需要的话不加就行了),可以看到程序在终端跑起来了。当然这次的top5 accuracy很低,因为我的轮次,以及对图像处理的维度都很低,没按原来的数据,因为我现在比较想看结果,想先把后面的caption.py运行出来,COCO数据集太大了,租1核的GPU得跑好久。
    在这里插入图片描述
    在这里插入图片描述

  2. 在jupyterLab的终端上执行命令,并且在运行的命令后面加上train.log(重定向),比如:

 python train.py > train.log 2>&1

train.py错误3:train.py跑完一个epoch后,在验证集阶段出现了错误indices should be either on cpu or on the same device as the indexed tensor (cpu)

错误出现的原因:PyTorch 要求索引张量和被索引张量在进行索引操作时需要在同一个设备上。也就是说,在 PyTorch 中,如果你有在 GPU 上运行的张量(即张量在 CUDA 设备上),那么索引张量也必须位于同一个 CUDA 设备上。否则,PyTorch 将会抛出这个错误。
在这里插入图片描述

错误3解决方法

问题出现的位置是在train.py文件中的第318行,首先理解这部分出问题的代码的意思(不理解也不影响解决错误,哎嘿嘿)

# References
allcaps = allcaps[sort_ind]  # because images were sorted in the decoder
# 因为图像在解码器中是被排序的,所以这里根据给定的索引数组sort_ind对allcaps进行重新排序
# 这个操作是为了使标注按照某种顺序与相对应的图像匹配
# allcaps是包含了多个图像的标准序列的张量,
for j in range(allcaps.shape[0]):
    # 对allcaps张量中每个图像的标注序列进行循环遍历
    img_caps = allcaps[j].tolist()
    # 将当前图像的标注序列转换为Python列表,以便后续处理
    img_captions = list(
        map(lambda c: [w for w in c if w not in {word_map['<start>'], word_map['<pad>']}],
            img_caps))  # remove <start> and pads
    # 使用map函数将当前标注序列中的每个单词列表c中不属于<start>和<pad>的标记提取出来
    # 然后将提取出的单词列表存储在img_captions中,这样就去除了<start>和<pad>标记
    references.append(img_captions)
    # 将处理好的当前图像的标注列表添加到references中

改正方式:在318行前面添加一段代码,让sort_ind和allcaps在同一个设备上。这样问题解决。

sort_ind = sort_ind.to(allcaps.device)
            # 让sort_ind和allcaps在同一个设备上

train.py运行成功(只跑了3个epoch,维度比较低,所以训练效果很差),生成了BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth文件

在这里插入图片描述

接着运行caption.py

错误1:‘NoneType’ object has no attribute ‘seek’. You can only torch.load from a file that is seekable. Please pre-load the data into a buffer like io.BytesIO and try to load from it instead.

在这里插入图片描述

错误1解决办法:

这个错误出现在下载模型那里的代码中,首先对这段出问题的代码进行理解:

# Load model
    checkpoint = torch.load(args.model, map_location=str(device))
    # 使用pyTorch的torch.load函数加载模型的检查点。args.model包含了模型的文件路径,
     # map_locstion=str(device)是将模型加载到指定的设备上(GPU/CPU)
    decoder = checkpoint['decoder']
    # 这一行从加载的检查点中获取decoder的参数
    decoder = decoder.to(device)
    # 将解码器移动到指定的设备上
    decoder.eval()
    # 设置解码器为评估模式,这意味着模型用于评估而非训练
    encoder = checkpoint['encoder']
    # 从加载的检查点中获取编码器的参数
    encoder = encoder.to(device)
    # 将编码器移动到指定的设备上
    encoder.eval()
    # 设置编码器为评估模式,同样也意味着用于评估而非训练。

错误出现的原因是:torch.load()函数无法从不可寻址的文件中加载数据而引起的。错误信息指出无法从一个NoneType对象,即空对象中获取seek属性,因为文件对象是不可以寻址的。为了解决这个问题,使用以下步骤尝试解决:

  1. 使用print(args.model)来检查args.model变量是否包含正确的模型文件路径;
  2. 确保指定文件存在并且可读,否则会加载失败;
  3. 如果模型存在且可读,尝试预先将数据加载到一个可寻址的缓冲区(如:io.BytesIO),然后再从缓冲区中加载数据,这样避免直接从不可寻址的文件中加载数据导致的问题。

我的清汤大老爷啊!我在代码中加入print(args.model),发现打印出的结果是None,我的天,以下代码压根没有正确解析:

parser = argparse.ArgumentParser(description='Show, Attend, and Tell - Tutorial - Generate Caption')

    parser.add_argument('--img', '-i', help='path to image')
    parser.add_argument('--model', '-m', help='path to model')
    parser.add_argument('--word_map', '-wm', help='path to word map JSON')
    parser.add_argument('--beam_size', '-b', default=5, type=int, help='beam size for beam search')
    parser.add_argument('--dont_smooth', dest='smooth', action='store_false', help='do not smooth alpha overlay')

    args = parser.parse_args()

感谢参考博客:这位博主厉害,醍醐灌顶,因为没有加入路径
感谢这位博主
也感谢这位博主的解释
改错方式:将路径添加进去(由于train_path后面的我的路径和caption.py在同一目录下,所以路径是./;由于word_map_path和我的caption.py不在同一个文件夹里,所以用的绝对路径,这个前面我有提过;img_path的图片是我自己找的然后放到了img文件夹里)

train_path = './BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth'
word_map_path = '/root/autodl-tmp/media/ssd/caption data/WORDMAP_coco_5_cap_per_img_5_min_word_freq.json'
img_path = './img/cats.jpg'
img_path = './img/cow and farmer.jpeg'
img_path = './img/table.png'
parser = argparse.ArgumentParser(description='Show, Attend, and Tell - Tutorial - Generate Caption')
parser.add_argument('--img', '-i', default=img_path, help='path to image')
parser.add_argument('--model', '-m', default=train_path, help='path to model')
parser.add_argument('--word_map', '-wm', default=word_map_path,  help='path to word map JSON')
parser.add_argument('--beam_size', '-b', default=5, type=int, help='beam size for beam search')
parser.add_argument('--dont_smooth', dest='smooth', action='store_false', help='do not smooth alpha overlay')
args = parser.parse_args()

改完后,不再报这一个错误了。

错误2:No such file or directory: ‘./BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth’

在这里插入图片描述
错误出现原因:你去Filezilla里面去看,自己运行train.py生成的那个BEST开头的文件,它的名字全称其实是BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar。也就是.pth后面还有.tar后缀,它其实是个压缩文件。哎真烦!
在这里插入图片描述

错误2解决方式:

把刚才的train_path后面的路径给它加上个.tar后缀,成功解决!
在这里插入图片描述

错误3:‘NoneType’ object has no attribute ‘transpose’

出现原因:caption.py的第41行中,尝试对一个对象img执行transcope操作,结果这个对象是个空值。错误提示让我检查caption_image_beam_search函数的实现。
在这里插入图片描述

错误3解决方法:根据提示去查看caption_image_beam_search函数是否正确传递了参数。

直接打印 caption_image_beam_search 函数的几个参数的值,结果发现我最怀疑的args.img并不是None,它打印出了图片的路径,好家伙那问题就不出现在这里了呀:
在这里插入图片描述
在这里插入图片描述
好吧我决定对transcope之前的代码观察一下,尝试print(img)和print(img.shape),结果前一个打印出是None,后一个不打印任何东西,因此我怀疑是下面截图中img=img.transcope(2,0,1)之前的代码有问题。
在这里插入图片描述
在这里插入图片描述
因此我开始在我怀疑的范围内打印img和img.shape来进行排查,结果发现经过下面这行改定图像大小的代码后,img打印出来就是None,img.shape更是直接打印不出来。
在这里插入图片描述
将上面这一行代码改为以下代码则错误消失:

img = np.array(Image.fromarray(img).resize((256, 256)))

通过打印img和img.shape进行测试,发现可以打印出信息了
在这里插入图片描述

错误4:tensors used as indices must be long, int, byte or bool tensors

错误出现原因:这个错误表明在使用 PyTorch 进行张量索引时出现了问题,具体地说是要求张量作为索引的张量必须是 long、int、byte 或 bool 类型的张量。
在这里插入图片描述

错误4解决办法:根据提示我打印了以下我觉得应该是张量的变量,发现prev_word_inds是浮点数型的

在这里插入图片描述
打印print(prev_word_inds),打印出的结果是float浮点型的
在这里插入图片描述

因此,我把prev_words_inds转换成了长整型long,采用以下代码:

prev_word_inds = (top_k_words / vocab_size).long()

然后打印prev_words_inds和它的类型,用以下代码:

print(prev_word_inds)
print(prev_wprd_inds.dtype)

打印出的部分结果是:
在这里插入图片描述
错误解决。

警告5:MatplotlibDeprecationWarning: Support for FigureCanvases without a required_interactive_framework attribute was deprecated in Matplotlib 3.6 and will be removed two minor releases later.(警告内容是关于FigureCanvases(图形画布)缺少required_interactive_framework属性的支持已经被弃用,并且在两个次要版本之后将会被移除。)

警告5的处理方法:

参考来源:这个博客讲的很透彻,也给了解决方式
也可以结合看一下这个博客
我选择下列的方式解决:Settings-----Tools----python Scientific----取消勾选Show plots in tool window
在这里插入图片描述
取消掉以后警告消失,原因看第一个参考链接。

错误5不能按上面这么改,这样改了警告确实是消失了,但是找不到图了,所以我有改回来了,这个警告先让他这么存在着,后面找到好的方式可再改,反正不影响出来图片。

错误6: Number of rows must be a positive integer, not 2.0(行数必须是一个正整数,而不是2.0这样的浮点数)

在这里插入图片描述

错误6解决方法:

我们先来看一下问题代码的意思:

plt.subplot(np.ceil(len(words) / 5.), 5, t + 1)
        # 如果需要将多张子图展示在一起,可以使用 subplot() 实现
        # np.ceil(len(words)/5.)确定要创建的子图的总行数,确保能够得到足够的行数容纳所有的单词。
        # 5表示每行包含的子图数量
        # t+1确定当前子图的位置,t是当前子图的索引(从0开始),加1表示子图的位置从1开始计数

np.ceil(len(words) / 5.得到的结果不能确定它是一个正整数(.是表示浮点数的一种写法,不影响结果)。
np.ceil()函数表示向上取整,比如:

a=np.array([1.22, -0.23, -1.5, 4.68, 3.57])
print(np.ceil(a))    #[2, -0, -1, 5, 4]

尽管np.ceil()函数表示向上取整,但是它得到的值不一定就是整数,比如下面y就是浮点数:
参考来源:下图来自于这里
在这里插入图片描述
再比如我打印我代码中的np.ceil(len(words) / 5.的类型,得到的也是浮点数
在这里插入图片描述
但是ceil()方法只能接收一个浮点数作为参数,如果传入的参数不是浮点数,则会抛出TypeError异常。
为了确保np.ceil(len(words) / 5.是整数,我首先尝试使用int()函数将其转换为整数:

 plt.subplot(int(np.ceil(len(words) / 5.), 5, t + 1))

但这样是不对的,它会报以下的错误:
int() argument must be a string, a bytes-like object or a number, not ‘numpy.dtype[float64]’
在这里插入图片描述
报这样错误的原因是:
在调用 int() 函数时,传入的参数类型不符合要求。具体来说,int() 函数的参数必须是一个字符串、类似字节对象(bytes-like object)或者数字,而不能是 ‘numpy.dtype[float64]’ 这样的类型。
解决的方式:将 NumPy 数据类型转换为普通的 Python 数字类型,然后再将其传递给 int() 函数。你可以使用 .item() 方法来提取 NumPy 数组中的单个元素,例如:int(np.ceil(len(words) / 5).item())。
因此把代码改为以下代码:

 plt.subplot(int(np.ceil(len(words) / 5).item()), 5, t + 1)

caption.py运行成功啦啦啦啦啦!

不过由于训练的轮次比较少,参数也调的很低,这个描述不是非常准确(明明我的是两只猫,它说一只),但是也没有很离谱哈哈哈哈。等我后面有时间了把参数和轮次调为原来的跑一跑看看效果!现在先把代码跑通吧!
在这里插入图片描述
原图:
在这里插入图片描述

这张它把牛认错了,认为牛是人。
在这里插入图片描述
原图:在这里插入图片描述

可视化注意力区域的图片在这里找,不会直接弹出来:
在这里插入图片描述
我加了几行代码,使得图像的描述语句能够打印出来
在这里插入图片描述

接下来开始对eval.py进行调试

首先按照之前的文件的改法,把参数位置该合适

在这里插入图片描述

错误1:tensors used as indices must be long, int, byte or bool tensors

在这里插入图片描述

错误1解决办法:这个错误上面解决过,定位到错误提示位置,按照上面caption.py改就可以了。

在这里插入图片描述

错误2:max() arg is an empty sequence

在这里插入图片描述

错误2解决办法,解决后eval.py成功运行:

错误出现的原因是:尝试对一个空序列调用max()函数。出现问题的代码是:

i = complete_seqs_scores.index(max(complete_seqs_scores))

可能是由于在某些情况下,complete_seqs_scores没有被正确填充或者初始化为空导致的。
我尝试打印了一下complete_seqs_scores,发现打印出来了一堆负值,打印max(complete_seqs_scores),结果差不多:
在这里插入图片描述

解决方法参考这个博客:看这里
上面博客解决办法来源于这里
添加以下代码:

if len(complete_seqs) == 0:
   complete_seqs.extend(seqs.tolist())  # (s, step+1)
   # complete_seqs_alpha.extend(seqs_alpha.tolist())
   complete_seqs_scores.extend(top_k_scores.tolist())  # (s)

在这里插入图片描述
然后就开始正常运行了,结果如下图所示:
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值