YOLOv5+Fmix

想要将YOLOv5-v7.0(python语言)中的数据增强中的Mixup替换为FMix。遂在“utils.augmentations.py”中加入FMix函数,在“utils.dataloaders.py”编改编原先的Mixup调用函数,改成FMix调用。现出现调用错误,请路过的各位大神帮忙看看!!!已经困惑多日了,这个Fmix就是不运行。

  1. utils.augmentations.py 中的FMix定义代码:
def FMix(self,
         index,
         im,
         labels,
         im2,
         labels2,
         mask,
         decay = 3.0,
         alpha=1,
         shape=(416,416),
         max_soft=0.0
         ):
    self.im2 = im2   
    self.labels = labels2
    mask = make_low_freq_image(decay, shape)
    mask = binarise_mask(mask, lam, shape, max_soft)
    im = (im * mask + im2 * (1 - mask)).astype(np.uint8)
    labels = np.concatenate((labels, labels2), 0)
    return im, labels 
#计算lamba值  
def lam(alpha, reformulate=False):
    if reformulate:
        lam = beta.rvs(alpha+1, alpha)
    else:
        lam = beta.rvs(alpha, alpha)
    return lam
 # 从傅里叶空间采样一个低频图像
def make_low_freq_image(decay, shape, ch=1):
    freqs = fftfreqnd(*shape)
    spectrum = get_spectrum(freqs, decay, ch, *shape)#.reshape((1, *shape[:-1], -1))
    spectrum = spectrum[:, 0] + 1j * spectrum[:, 1]
    mask = np.real(np.fft.irfftn(spectrum, shape))
    if len(shape) == 1:
        mask = mask[:1, :shape[0]]
    if len(shape) == 2:
        mask = mask[:1, :shape[0], :shape[1]]
    if len(shape) == 3:
        mask = mask[:1, :shape[0], :shape[1], :shape[2]]
    mask = mask
    mask = (mask - mask.min())
    mask = mask / mask.max()
    return mask
def fftfreqnd(h, w=None, z=None):
    fz = fx = 0
    fy = np.fft.fftfreq(h)
    if w is not None:
        fy = np.expand_dims(fy, -1)
        if w % 2 == 1:
            fx = np.fft.fftfreq(w)[: w // 2 + 2]
        else:
            fx = np.fft.fftfreq(w)[: w // 2 + 1]
    if z is not None:
        fy = np.expand_dims(fy, -1)
        if z % 2 == 1:
            fz = np.fft.fftfreq(z)[:, None]
        else:
            fz = np.fft.fftfreq(z)[:, None]
    return np.sqrt(fx * fx + fy * fy + fz * fz)
#获取傅里叶图像
def get_spectrum(freqs, decay_power, ch, h, w=0, z=0):
    scale = np.ones(1) / (np.maximum(freqs, np.array([1. / max(w, h, z)])) ** decay_power)
    param_size = [ch] + list(freqs.shape) + [2]
    param = np.random.randn(*param_size)
    scale = np.expand_dims(scale, -1)[None, :]
    return scale * param
def binarise_mask(mask, lam, in_shape, max_soft=0.0):
    idx = mask.reshape(-1).argsort()[::-1]
    mask = mask.reshape(-1)
    num = math.ceil(lam * mask.size) if random.random() > 0.5 else math.floor(lam * mask.size)
    eff_soft = max_soft
    if max_soft > lam or max_soft > (1-lam):
        eff_soft = min(lam, 1-lam)
    soft = int(mask.size * eff_soft)
    num_low = num - soft
    num_high = num + soft
    mask[idx[:num_high]] = 1
    mask[idx[num_low:]] = 0
    mask[idx[num_low:num_high]] = np.linspace(1, 0, (num_high - num_low))
    mask = mask.reshape((1, *in_shape))
    return mask

2.bug报告:

img


3.“utils.dataloaders.py”编改编原先的Mixup调用函数,改成FMix调用。

img

看看 hyp 那个数据是什么

参考GPT和自己的思路:根据你提供的代码,可能有以下原因导致调用错误:

1 FMix 函数缺少参数 im2, labels2, mask。
2 调用 FMix 函数时,缺少参数 index。
3 make_low_freq_image() 函数返回的 mask 是一个浮点型数组,但是在 FMix 函数中,mask 被用作一个布尔值的数组进行操作,可能导致类型不匹配的错误。
4 在 binarise_mask() 函数中,返回的 mask 是一个 Numpy 数组,但是在 FMix 函数中,使用了 Python 自带的 bool 类型作为数组类型,可能导致类型不匹配的错误。
你需要仔细检查以上问题,并对代码进行相应的更改,以确保正确调用 FMix 函数。

参考GPT和自己的思路,在utils.dataloaders.py中,需要将原来的Mixup替换为FMix,可以先将Mixup调用函数复制一份并将函数名改为FMix,同时对于FMix函数的参数需要按照新的定义进行修改,具体如下:

def FMixBatch(self, batch, alpha=1, decay=3.0, max_soft=0.0):
    lam = np.random.beta(alpha, alpha)
    index = torch.randperm(batch.size(0)).to(batch.device)

    self.mix_decision = np.random.rand()
    if self.mix_decision < 0.5:
        # Mixup
        batch = batch * lam + batch[index] * (1 - lam)
        self.y_a, self.y_b = self.y_a.to(batch.device), self.y_b.to(batch.device)
        labels = torch.cat([self.y_a, self.y_b], dim=0)
    else:
        # FMix
        im2, labels2 = self._batch_transform(batch)
        batch, labels = self.FMix(index, batch, self.y_a, im2, self.y_b, decay, alpha=batch.shape[-2:], max_soft=max_soft)
        labels = torch.from_numpy(labels).to(batch.device)

    return batch, labels

此外,还需要在utils.datasets.py文件中修改调用数据增强的函数,将原来的mixup_batch替换为FMixBatch即可,具体如下:

class LoadImagesAndLabels(Dataset):  
    def __init__(self, path, img_size=640, augment=False, hyp=None, rect=False, cache=False, single_cls=False):
        self.img_files = sorted(glob.glob('%s/*.*' % path))
        self.label_files = [x.replace('images', 'labels').replace('.png', '.txt').replace('.jpg', '.txt') for x in self.img_files]
        self.img_size = img_size
        self.augment = augment
        self.hyp = hyp
        self.rect = rect
        self.cache = cache
        self.mosaic = self.augment and not self.rect and not single_cls  # load 4 images at a time into a mosaic (only during training)
        self.single_cls = single_cls
        self.n = len(self.img_files)
        self.shapes = None
        self.indices = range(self.n)
        self.label_cache = {}

    def __getitem__(self, index):
        if self.cache:
            img, label = self.imgs[index], self.labels[index]
        else:
            img_path = self.img_files[index]
            label_path = self.label_files[index]
            with open(label_path, 'r') as f:
                label = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32)
            # if label.ndim == 1:
            #     label = label.reshape(-1, 5)  # make sure it's 2D

            # Load image
            img = cv2.imread(img_path)  # BGR
            assert img is not None, 'Image Not Found ' + img_path
            # Padded resize
            h0, w0 = img.shape[:2]
            img, ratio, pad = resize_square(img, self.img_size, pad=self.rect)
            label[:, 1:] = xyxy2xywh(label[:, 1:])  # xywh
            label[:, 1:] = (label[:, 1:] * ratio + pad) / self.img_size  # normalized xywh
            label[:, [0, 2, 6]] = label[:, [0, 2, 6]] * ratio + pad # normalized xyxy
        # FMix augmentation
        if self.fmix and np.random.rand() < self.fmix_prob:
            lam = self.sample_lambda()
            fmix_ix = np.random.choice(self.num_samples, size=1)[0]
            img2, label2 = self.load_image_and_label(fmix_ix)
            img2, _, _ = resize_square(img2, self.img_size, pad=self.rect)
            label2[:, 1:] = xyxy2xywh(label2[:, 1:])  # xywh
            img, label = self.fmix(img, label, img2, label2, lam=lam)

        # MixUp augmentation
        elif self.mixup and np.random.rand() < self.mixup_prob:
            lam = np.random.beta(self.mixup_alpha, self.mixup_alpha)
            mix_ix = np.random.choice(self.num_samples, size=1)[0]
            img2, label2 = self.load_image_and_label(mix_ix)
            img2, _, _ = resize_square(img2, self.img_size, pad=self.rect)
            label2[:, 1:] = xyxy2xywh(label2[:, 1:])  # xywh
            img, label = self.mixup(img, label, img2, label2, lam=lam)

        # Apply augmentations
        img, label = self.transforms(img, label)

        return img, label

具体来说,使用Fmix进行数据增强可以帮助模型更好地处理复杂场景下的物体,并提高模型的泛化性能。Fmix会随机选取两张图片进行融合,并利用一个掩码函数来生成新的训练样本。这个掩码函数是用于像素级别的数据操作,使得原始图像的部分区域被覆盖或消除,从而让模型不依赖于特定的背景或位置信息,增强模型的鲁棒性。

在YOLOv5中应用Fmix技术,主要可以通过以下步骤实现:

  1. 准备数据集:首先需要为目标检测模型准备一个数据集,包含了多个类别的物体样本图片,同时需要安装Fmix库。

  2. 数据增强:针对每个批次的训练数据,利用Fmix库生成额外的训练样本,扩充数据量以及增强模型鲁棒性。

  3. 训练模型:将经过Fmix增强的新数据集输入到YOLOv5模型里进行训练,更新权重和偏差。此时,模型可以更好地处理复杂的场景和物体,在训练过程中快速适应新图像的变化。

  4. 模型评估:训练完成后,可以对模型进行评估和调试,根据评估结果对模型参数进行优化和改进。

总之,将YOLOv5和Fmix技术结合起来,可以帮助我们设计出更精准、鲁棒性更强的目标检测模型,适用于各种任务场景。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先请确认报错信息是什么,以便更好地排查问题。

另外,您应该在更改“utils.dataloaders.py”中的Mixup调用函数之前,确保您已成功在训练集中使用FMix函数进行数据增强,并且使用FMix函数的代码没有问题。

在您进行“utils.dataloaders.py”中的更改时,您需要将原来的Mixup函数调用替换为FMix函数调用。以下是一个示例代码片段,其中Mixup被替换为FMix:

from utils.augmentations import FMix  # 导入FMix函数

class LoadImages:  # 更改LoadImages类,以使用FMix函数
    ...
    def __iter__(self):
        self.count = -1
        self.cutoff = self.n - self.img_size * 2  # 最大迭代次数
        self.shuffled = self.randomize and not self.not_rand_crop  # 是否要打乱顺序
        self.infinite = self.infinite_samples
        return self

    def __next__(self):
        self.count += 1
        if self.count == self.n:
            if self.infinite:
                self.count = 0
            else:
                raise StopIteration
        if self.shuffled and self.count % self.n == 0:
            self.indices = random.sample(range(self.n), self.n)
        img_path = self.img_files[self.indices[self.count]]
        img = cv2.imread(img_path)  # 加载图像
        ...
        if self.augment:  # 数据增强
            ...
            if self.mixup == 1:
                img, labels = mixup(img, labels, self.smooth_labels)
            elif self.mixup == 2:
                img, labels = cutmix(img, labels, self.smooth_labels)
            elif self.mixup == 3:
                img, labels = mosaic(imgs, labels, self.cfg['mosaic']['mosaic_min'], self.img_size)
            elif self.mixup == 4:
                # 替换Mixup为FMix
                img, labels = FMix(img, labels, self.img_size)
        ...
        return img, labels, img_path, (h, w)

请注意,在这个示例代码片段中,我们已经从“utils.augmentations”导入了FMix函数。我们还检查了我们原来的Mixup函数调用的选项(即self.mixup的值)并相应地替换了它们。

希望这可以帮助到您!
如果我的回答解决了您的问题,请采纳!