自定义VOC数据集加载出现的问题

szq7758: <p>因为读入的两张图片含有不同数量的object，导致当我设置batchsize大于1时，总会提示错误，后来我按照网上的教程自定义了collate_fn方法，但是出现新的问题，代码无法显示shape，如果可以的话，请审阅一下我的代码并提出您宝贵的建议。代码如下：<br>
import torch<br>
import xml.etree.ElementTree as ET<br>
import os.path as osp<br>
import cv2<br>
import numpy as np<br>
from PIL import Image<br>
from torch.utils.data import Dataset, DataLoader<br>
from torch.nn import functional as F<br>
from torchvision import transforms</p>
<p>import torch<br>
import torch.utils.data as data<br>
import numpy as np<br>
import cv2<br>
import xml  # 标注是xml格式</p>
<p>try:<br>
import xml.etree.cElementTree as ET  # 解析xml的c语言版的模块<br>
except ImportError:<br>
import xml.etree.ElementTree as ET</p>
<p>VOC_CLASSES = {<br>
# 记得加上背景类<br>
“hat”,<br>
“No_hat”,<br>
}</p>
<h1>
<a name="strint-1" class="anchor" href="#strint-1"></a>把str映射为int</h1>
<p>dict_classes = dict(zip(VOC_CLASSES, range(len(VOC_CLASSES))))</p>
<h1>
<a name="printdict_classesaeroplane-2" class="anchor" href="#printdict_classesaeroplane-2"></a>print(dict_classes[‘aeroplane’])</h1>
<p>transforms = transforms.Compose([<br>
transforms.Resize((640, 640)),<br>
transforms.ToTensor()<br>
])</p>
<p>class ReadVOC(data.Dataset):<br>
def <strong>init</strong>(self, root, transform = None):<br>
print(“reading voc…”)<br>
self.root = root<br>
self.transform = transforms<br>
self.img_idx = []<br>
self.ano_idx = []</p>
<pre><code>    self.bbox = []
    self.obj_name = []  # 类别

    train_txt_path = self.root + "/ImageSets/Main/train.txt"  # train这个文件夹里面数量太少 换掉

    self.img_path = self.root + "/JPEGImages/"
    self.ano_path = self.root + "/Annotations/"

    # 首先读取txt文件进行训练集图片索引
    train_txt = open(train_txt_path)

    lines = train_txt.readlines()

    for line in lines:
        name = line.strip().split()[0]   # 0意思是按右边的分割，1意思是按左边分割
        # print(name)  # name is in str type

        self.img_idx.append(self.img_path + name + '.jpg')
        self.ano_idx.append(self.ano_path + name + '.xml')  # 最好是在这直接解析出bbox

def __getitem__(self, item):
    # print("getitem...")
    # print(self.img_idx[item])
    img = cv2.imread(self.img_idx[item])
    # img = Image.open(self.img_idx[item]).convert('RGB')
    # img = self.transform(img)
    height, width, channels = img.shape
    # img = torch.tensor(img)

    targrts = ET.parse(self.ano_idx[item])  # .getroot()   # 运行时解析 逻辑更加清晰
    res = np.empty((0, 5))  # 标注输出
    # res = []

    # find all obj in xml
    for obj in targrts.iter("object"):  # 便利物体
        name = obj.find('name').text.strip()
        class_idx = dict_classes[name]

        bbox = obj.find('bndbox')

        pts = ['xmin', 'ymin', 'xmax', 'ymax']
        obj_bbox = []
        for i, pt in enumerate(pts):
            cur_pt = int(bbox.find(pt).text) - 1
            # cur_pt = cur_pt / width if i % 2 == 0 else cur_pt / height  # scale height or width
            obj_bbox.append(cur_pt)
        obj_bbox.append(class_idx)

        res = np.vstack((res, obj_bbox))

        # 当前obj的所有bboxdf

        # res.append(obj_bbox)

        # res.append(class_idx)

    img, res = self.data_trans(img, res)

    return img, res

def __len__(self):
    data_lenth = len(self.img_idx)
    # print('data lenth is ', data_lenth)
    return data_lenth

# 标注输入使用w h归一化的相对坐标
def data_trans(self, img_input, res_input):
    # print("trans...")

    goal_size = (640, 640)

    # 在这时候,图像尺寸可以变化,只要目标不发生平移等等
    img = cv2.resize(img_input, goal_size)


    # pre-process input img
    img = torch.from_numpy(img).permute(2, 0, 1).float()

    # 把bbox转换成绝对坐标
    # bbox = [bbox_input[0] * goal_size[0], bbox_input[1] * goal_size[1], bbox_input[2] * goal_size[0], bbox_input[3] * goal_size[1]]
    # bbox = list(map(int, bbox))
    # half_len = int(len(res_input)/2)
    # print(half_len)
    # print(len(bbox_input))

    res = []
    for i in range(len(res_input)):
        res.append(torch.tensor(res_input[i]))
        # res.append(res_input[2*i+1])
        # return img, res

    # res = torch.tensor(res_input[0])

    return img, res
</code></pre>
<h1>
<a name="def-collate_fnbatch-3" class="anchor" href="#def-collate_fnbatch-3"></a>def collate_fn(batch):</h1>
<h1>
<a name="batchdataset_getitem__-4" class="anchor" href="#batchdataset_getitem__-4"></a>#  batch是一个列表，其中是一个一个的元组，每个元组是dataset中_getitem__的结果</h1>
<h1>
<a name="batch1-listzipbatch-5" class="anchor" href="#batch1-listzipbatch-5"></a>batch1 = list(zip(*batch))</h1>
<h1>
<a name="half_batch-intlenbatch2-6" class="anchor" href="#half_batch-intlenbatch2-6"></a>half_batch = int(len(batch)/2)</h1>
<h1>
<a name="for-i-in-rangehalf_batch-7" class="anchor" href="#for-i-in-rangehalf_batch-7"></a>for i in range(half_batch):</h1>
<h1><a name="h-8" class="anchor" href="#h-8"></a></h1>
<h1>
<a name="labels-torchtensorbatch12i-dtypetorchint32-9" class="anchor" href="#labels-torchtensorbatch12i-dtypetorchint32-9"></a>labels = torch.tensor(batch1[2*i], dtype=torch.int32)</h1>
<h1>
<a name="texts-batch12i1-10" class="anchor" href="#texts-batch12i1-10"></a>texts = batch1[2*i+1]</h1>
<h1>
<a name="del-batch-11" class="anchor" href="#del-batch-11"></a>del batch</h1>
<h1>
<a name="return-labels-texts-12" class="anchor" href="#return-labels-texts-12"></a>return labels, texts</h1>
<p>def collate_fn1(batch):<br>
images = []<br>
reses = []<br>
for img, res in batch:<br>
images.append(img)<br>
reses.append(res)<br>
# images = np.array(images)<br>
# images = np.array(torch.tensor(img) for img in images)<br>
images = torch.from_numpy(np.array(img).astype(dtype=np.float32)).type(torch.FloatTensor)    <span class="hashtag">#这里有一个错误</span>，但是我不知道怎么改<br>
# bboxes = [torch.from_numpy(ann).type(torch.FloatTensor) for ann in bboxes]<br>
return images, reses</p>
<p>if <strong>name</strong> == “<strong>main</strong>”:<br>
# ReadVOC(root=‘D:/python_project_model/learn_pttorch_4_myproject/VOC2028’)<br>
train_ = ReadVOC(root=‘D:/python_project_model/learn_pttorch_4_myproject/VOC2028’)<br>
train_dalo = DataLoader(train_, 2, collate_fn=collate_fn1)<br>
for i, (img, target) in enumerate(train_dalo):<br>
img, target = img, target<br>
print(img.shape)<br>
print(target)</p>
<pre><code># img, target = train_[0] # 返回第一张图像及box和对应的类别
# print(img.shape)
# print(target)
#
#
# # 这里简单做一下可视化
# # 由于opencv读入是矩阵，而img现在是tensor，因此，首先将tensor转成numpy.array
# img_ = (img.numpy()).astype(np.uint8).transpose(1, 2, 0)  # 注意由于图像像素分布0-255，所以转成uint8
# print(img_.shape)
# cv2.imshow('test', img_)
# cv2.waitKey(0)</code></pre>

ptrblck: <p>Could you describe the issue you are hitting or what kind of error is raised, please?</p>

szq7758: <p>不好意思，由于我是新用户，我无法同时上传多张图片，我将分三次来上传我的问题。<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/6/c/6cf018946e75e2f50639a91c65d45cce5cbc5fd8.jpeg" data-download-href="https://discuss.pytorch.org/uploads/default/6cf018946e75e2f50639a91c65d45cce5cbc5fd8" title="please help me_页面_1"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/6/c/6cf018946e75e2f50639a91c65d45cce5cbc5fd8_2_353x500.jpeg" alt="please help me_页面_1" data-base62-sha1="fxHY9fqBr0mH9a7OjU5h4d3p2eA" width="353" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/6/c/6cf018946e75e2f50639a91c65d45cce5cbc5fd8_2_353x500.jpeg, https://discuss.pytorch.org/uploads/default/optimized/3X/6/c/6cf018946e75e2f50639a91c65d45cce5cbc5fd8_2_529x750.jpeg 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/6/c/6cf018946e75e2f50639a91c65d45cce5cbc5fd8_2_706x1000.jpeg 2x" data-dominant-color="ACACAC"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">please help me_页面_1</span><span class="informations">1629×2304 223 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>

szq7758: <p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/2/4/24af3aa85ce45685fd40edc787558efae3642cbb.jpeg" data-download-href="https://discuss.pytorch.org/uploads/default/24af3aa85ce45685fd40edc787558efae3642cbb" title="please help me_页面_2"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/2/4/24af3aa85ce45685fd40edc787558efae3642cbb_2_353x500.jpeg" alt="please help me_页面_2" data-base62-sha1="5ewCareXehbrFkCO9TzHaWYYdRh" width="353" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/2/4/24af3aa85ce45685fd40edc787558efae3642cbb_2_353x500.jpeg, https://discuss.pytorch.org/uploads/default/optimized/3X/2/4/24af3aa85ce45685fd40edc787558efae3642cbb_2_529x750.jpeg 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/2/4/24af3aa85ce45685fd40edc787558efae3642cbb_2_706x1000.jpeg 2x" data-dominant-color="A4A3A2"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">please help me_页面_2</span><span class="informations">1191×1684 72.8 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>

szq7758: <p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/0/8/086726005ee455f36cdd46698602d80ad35a9beb.jpeg" data-download-href="https://discuss.pytorch.org/uploads/default/086726005ee455f36cdd46698602d80ad35a9beb" title="please help me_页面_3"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/0/8/086726005ee455f36cdd46698602d80ad35a9beb_2_353x500.jpeg" alt="please help me_页面_3" data-base62-sha1="1ckOu6DATjhtdZNayekwQj3qz9N" width="353" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/0/8/086726005ee455f36cdd46698602d80ad35a9beb_2_353x500.jpeg, https://discuss.pytorch.org/uploads/default/optimized/3X/0/8/086726005ee455f36cdd46698602d80ad35a9beb_2_529x750.jpeg 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/0/8/086726005ee455f36cdd46698602d80ad35a9beb_2_706x1000.jpeg 2x" data-dominant-color="D2D2D2"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">please help me_页面_3</span><span class="informations">1654×2339 114 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>

nivek: <aside class="quote no-group" data-username="szq7758" data-post="1" data-topic="153687">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/szq7758/48/49709_2.png" class="avatar"> szq7758:</div>
<blockquote>
<p>images = torch.from_numpy(np.array(img).astype(dtype=np.float32)).type(torch.FloatTensor)</p>
</blockquote>
</aside>
<p>On this line, you probably want <code>images</code> rather than <code>img</code> as the input argument to <code>np.array</code>? Otherwise, your images will only be the last element of the batch.</p>
<p>Before:<br>
images = torch.from_numpy(np.array(<strong>img</strong>).astype(dtype=np.float32)).type(torch.FloatTensor)<br>
After:<br>
images = torch.from_numpy(np.array(<strong>images</strong>).astype(dtype=np.float32)).type(torch.FloatTensor)</p>

szq7758: <p>感谢您的回复，我尝试了一下您所说的方式，代码会提示“can’t convert np.ndarray of type numpy.object_. The only supported types are: float64, float32, float16, complex64, complex128, int64, int32, int16, int8, uint8, and bool.”不过，从这个错误出发，我似乎找到了解决我问题的方法，我把“ images = torch.from_numpy（np.array（<strong>img</strong> ）.astype（dtype=np.float32））.type（torch.FloatTensor）”这条代码换成了“images = torch.stack(images)”，成功得到了我所希望的（2， 3， 640，640），我以为我可以进行训练了，但是代码提示了新的错误：<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/1/c/1cc5fd30b1ec0704180931e7adde82614123132b.png" data-download-href="https://discuss.pytorch.org/uploads/default/1cc5fd30b1ec0704180931e7adde82614123132b" title="Snipaste_2022-06-10_20-54-19"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1cc5fd30b1ec0704180931e7adde82614123132b_2_690x202.png" alt="Snipaste_2022-06-10_20-54-19" data-base62-sha1="46xyDE1WeOco4BqWzfzAZW7EPtF" width="690" height="202" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1cc5fd30b1ec0704180931e7adde82614123132b_2_690x202.png, https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1cc5fd30b1ec0704180931e7adde82614123132b_2_1035x303.png 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1cc5fd30b1ec0704180931e7adde82614123132b_2_1380x404.png 2x" data-dominant-color="323033"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">Snipaste_2022-06-10_20-54-19</span><span class="informations">1455×426 55.6 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>我进行了大量的搜索，仍未解决它，不知您是否有新的建议。以下是我进行训练的代码：<br>
import torch<br>
import torchvision<br>
from torch.utils.data import DataLoader<br>
from torch.optim import optimizer, lr_scheduler<br>
import matplotlib.pyplot as plt<br>
import os<br>
from torch import nn<br>
from tqdm import tqdm</p>
<p>from ResNet50 import *<br>
from torchvision import transforms<br>
from mydataset_voc_1_2 import ReadVOC, collate_fn1</p>
<p>from torchvision.datasets import ImageFolder<br>
import numpy as np</p>
<p>plt.rcParams[‘font.sans-serif’] = [‘SimHei’]<br>
plt.rcParams[‘axes.unicode_minus’] = False</p>
<p>root_trainpath = r’D:/python_project_model/learn_pttorch_4_myproject/VOC2028’<br>
root_valpath = r’D:/python_project_model/learn_pttorch_4_myproject/VOC2028’</p>
<p>normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])<br>
train_transform = transforms.Compose([<br>
transforms.Resize((640, 640)),<br>
# transforms.RandomVerticalFlip(),<br>
transforms.ToTensor(),<br>
normalize])</p>
<p>val_transform = transforms.Compose([<br>
# transforms.Resize((512, 512)),<br>
transforms.ToTensor(),<br>
normalize])</p>
<p>train_dataset = ReadVOC(root_trainpath, transform=train_transform)<br>
val_dataset = ReadVOC(root_valpath, transform=val_transform)</p>
<h1><a name="printtrain_dataset0-1" class="anchor" href="#printtrain_dataset0-1"></a>print(train_dataset[0])</h1>
<p>train_dataloader = DataLoader(train_dataset, batch_size=16, shuffle=True, collate_fn=collate_fn1)<br>
val_dataloader = DataLoader(val_dataset, batch_size=16, shuffle=True, collate_fn=collate_fn1)</p>
<p>device = ‘cuda:0’ if torch.cuda.is_available() else ‘cpu’</p>
<p>model = resnet50(2)<br>
model = model.cuda()</p>
<p>logs_fn = nn.CrossEntropyLoss().cuda</p>
<p>optimizer = torch.optim.Adam(model.parameters(), lr=0.01)</p>
<p>lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)</p>
<p>def train(train_dataloader, model, loss_fn, optimizer):<br>
loss, current, n = 0.0, 0.0, 0<br>
model.train()<br>
for i, data in enumerate(train_dataloader):<br>
imgs, targets = data<br>
imgs = torch.tensor(imgs).cuda()<br>
target =torch.tensor([item.cpu().detach().numpy() for item in targets]).cuda()<br>
output = model(imgs)<br>
cur_loss = loss_fn(output, target)<br>
_, pred = torch.max(output, axis=1)<br>
cur_acc = torch.sum(target == pred)/output.shape[0]</p>
<pre><code>    optimizer.zero_grad()
    cur_loss.backward()
    optimizer.step()
    loss += cur_loss.item()
    current += cur_acc.item()
    n = n+1

    train_loss = loss / n
    train_acc = current / n
    print('train_loss' + str(train_loss))
    print('train_acc' + str(train_acc))
    return train_acc, train_loss
</code></pre>
<p>def val(val_dataloader, model, loss_fn):<br>
loss, current, n = 0.0, 0.0, 0<br>
model.eval()<br>
with torch.no_grad():<br>
for i, data in tqdm(enumerate(val_dataloader)):<br>
imgs, targets = data<br>
imgs = imgs.cuda()<br>
# target = [target.cuda() for target in targets]<br>
target = torch.tensor([item.cpu().detach().numpy() for item in targets]).cuda()<br>
output = model(imgs)<br>
cur_loss = loss_fn(output, target)<br>
_, pred = torch.max(output, axis=1)<br>
cur_acc = torch.sum(target == pred)/output.shape[0]<br>
loss += cur_loss.item()<br>
current += cur_acc.item()</p>
<pre><code>        n = n+1

val_loss = loss / n
val_acc = current / n
print('train_loss' + str(val_loss))
print('train_acc' + str(val_acc))
return val_acc, val_loss
</code></pre>
<p>def matplot_loss(train_loss, val_loss):<br>
plt.plot(train_loss, label=‘train_loss’)<br>
plt.plot(val_loss, label=‘val_loss’)<br>
plt.legend(loc=‘best’)<br>
plt.ylabel(‘loss’)<br>
plt.xlabel(‘epoch’)<br>
plt.title(“训练集和验证集loss值对比图”)<br>
plt.show()</p>
<p>def matplot_acc(train_acc, val_acc):<br>
plt.plot(train_acc, label=‘train_acc’)<br>
plt.plot(val_acc, label=‘val_acc’)<br>
plt.legend(loc=‘best’)<br>
plt.ylabel(‘acc’)<br>
plt.xlabel(‘epoch’)<br>
plt.title(“训练集和验证集acc值对比图”)<br>
plt.show()</p>
<p>loss_train = <span class="chcklst-box fa fa-square-o fa-fw"></span><br>
loss_val = <span class="chcklst-box fa fa-square-o fa-fw"></span><br>
acc_train = <span class="chcklst-box fa fa-square-o fa-fw"></span><br>
acc_val = <span class="chcklst-box fa fa-square-o fa-fw"></span></p>
<p>epoch = 20<br>
min_acc = 0<br>
for i in range(epoch):<br>
lr_scheduler.step()<br>
print(“------第 {} 轮训练步骤开始------\n”.format(i+1))<br>
train_loss, train_acc = train(train_dataloader, model, logs_fn, optimizer)<br>
val_loss, val_acc = train(val_dataloader, model, logs_fn, optimizer)</p>
<pre><code>loss_train.append(train_loss)
acc_train.append(train_acc)
loss_val.append(val_loss)
acc_val.append(val_acc)

if val_acc &gt; min_acc:
    folder = 'save_model'
    if not os.path.exists(folder):
        os.mkdir('save_model')
    min_acc = val_acc
    print(f'save best model, 第{i+1}轮')
    torch.save(model.state_dict(), 'save_model/besst_model.pt')

if i == epoch - 1:
    torch.save(model.state_dict(), 'save_model/last_model.pt')
</code></pre>
<p>print(‘训练完成’)</p>