用于对象检测的智能数据拆分(训练/评估)

数据挖掘 数据集 数据 训练 评估 物体检测
2021-09-23 21:05:42

我正在寻找一种拆分对象检测数据(内部带有标记对象的图像)的智能方法,同时考虑对象本身的分布而不仅仅是图像。

我有一个由许多图像组成的数据集。这些图像中的每一个内部都有一个或多个对象,这些对象被标记。为了训练对象检测模型,我需要执行传统的训练/评估拆分(在这种情况下,我不需要测试集)。但是,对于每组中应该包含哪些标签,我有非常具体的要求。

具体来说,我想确保每个标签的最小样本量最终出现在训练和评估集中。例如,如果我知道我只有 5 辆汽车样本,我想确保这些汽车中至少有 3 辆在火车集中。因此,图像的简单随机分割(例如 80/20)并不理想,因为它没有考虑每个图像中的对象,因此无法强制执行我的约束。例如,如果只有两张图片有汽车,一张有三辆,一张有两辆,没有什么能阻止这两张图片出现在火车集合中,因此我可能没有任何汽车可供评估!

自然,这些约束可能是不可能实现的(如果所有汽车都是单一图像,那么 train 和 eval 中就不可能有汽车),这是算法应该管理的。(即尽可能给出符合要求的最佳解决方案)。

其他一些并发症:

  • 每个图像可以有任意数量的对象属于任意数量的标签,因此将图像添加到任一集合都会影响它包含的所有标签的分布。
  • 作为前一点的结果,在一组中强制执行一个约束可能会使另一组中的另一个无效(对于相同的标签,甚至对于不同的标签)。

你以前遇到过这个问题吗?有什么建议?

如果需要更多详细信息,请告诉我,我会相应地更新帖子。提前致谢。

编辑 1:添加整数编程方法状态

这是 Python 中当前整数解决方案的一个工作示例,使用cvxpy

需要的库:

import numpy as np
import cvxpy

示例数据:

# rows -> classes, columns -> images
L = np.array([[2, 0, 0, 0, 3, 0, 8, 0, 0],
              [0, 3, 0, 2, 0, 0, 0, 8, 0],
              [0, 0, 2, 0, 0, 3, 0, 0, 8]])
big_L = np.vstack((np.hstack((L, np.zeros_like(L))),
                   np.hstack((np.zeros_like(L), L))))

# minimum examples per class for each set
left_min = [2, 2, 2]
right_min = [2, 2, 2]

# maximum examples per class for each set
left_max = [9, 9, 9]
right_max = [2, 2, 2]

# number of frames and classes
nc, n = L.shape

定义约束所需的一些变量:

w = np.hstack((np.array(left_min), np.array(right_min))).T
max_w = np.hstack((np.array(left_max), np.array(right_max))).T

s = cvxpy.Variable(2*n, boolean=True)

big_s = np.vstack((np.hstack((np.zeros((n, n)), np.eye(n))),
                   np.hstack((np.eye(n), np.zeros((n, n))))))

约束:

# constraints
# only one frame can be selected for each set
output_constraint_0 = (big_s @ s) + s <= np.ones((2*n))

# only one frame can be selected for each set
output_constraint_01 = (big_s @ s) + s >= np.zeros((2*n))

# all frames must be selected for either set
output_constraint_1 = cvxpy.sum(s) == cvxpy.Variable(n, integer=True)

# result vector must be binary, only zeros or ones (and in between?) 
output_constraint_2 = np.eye(2*n) @ s <= np.ones(2*n)
output_constraint_3 = - np.eye(2*n) @ s <= np.zeros(2*n)

# sets have at least required quantities of examples per class
output_constraint_4 = big_L @ s - w >= np.zeros((2*nc))

# check that train has required (for now, we ignore eval numbers)
output_constraint_5 = big_L @ s - max_w >= np.zeros((2*nc))

constraints = [output_constraint_0, output_constraint_01,
               output_constraint_1, output_constraint_2,
               output_constraint_3, output_constraint_4,
               output_constraint_5]

# Objective function
objective = cvxpy.norm((big_s @ s) + s - np.ones((2*n)))

定义问题并解决:

split_problem = cvxpy.Problem(cvxpy.Minimize(objective), constraints)
split_problem.solve()

获取每个集合的图像索引并断言结果符合预期:

result = s.value.reshape((2, n))
l = np.array([int(x) for x in np.round(s.value[:n])], dtype=np.int)
r = np.array([int(x) for x in np.round(s.value[n:])], dtype=np.int)

assert all(L @ l >= left_min) and all(L @ l >= left_max)
assert all(L @ r >= right_min) and all(L @ r >= right_max)

虽然此解决方案适用于简单的案例,例如上面展示的示例数据集,但它不适用于具有更复杂类分布的真实数据集。问题主要在于不可行的“最大示例数”约束,可以通过迭代不同的训练/评估分数(例如 0.2、0.3、0.4,...)并解决每个问题来解决,最终可以达到一个解决方案(在最坏的情况下,eval 分数将为 1)。在这里,我们假设如果 eval_fraction 为 x 时问题不可行,则存在一个大于 x 的 eval_fraction y,可以使问题可解决。

另一个复杂因素是我们正在使用的当前求解器,即“ECOS_BB”(更多信息herehere),对于大型数据集来说太慢了,这使得它在实际使用中完全不切实际。此外,cvxpy 仅使用单个 CPU 内核,这意味着缺乏处理速度。

因此,下一步是找到一个合适的解决方案,该解决方案可以在合理的时间内利用所有可用的硬件来处理大型、非常异构的数据集。

1个回答

我想我不妨提出评论中提到的整数程序公式。

一个=(一个一世j), 在哪里 一个一世j 是对象的数量 一世 在图像中 j. 我们有一个二进制变量Xj 对于每个图像 j, 1 表示我们会将图像包含在训练集中。然后一个X 是其向量 一世第一项是对象的数量 一世包含在训练集中。设置下限一个X给出训练集中所需的下限,并为其设置上限给出验证集中所需的互补下限。

这还没有使用目标函数,所以你可以只询问可行性,或者添加一些东西来优化。

如果整数程序不可行,您可以从边界中添加和减去新变量(我认为我在评论中称它们为“松弛变量”是错误的?)一个X,并最小化这些变量的总和。这样你就可以得到一个解决方案,可以看到你离你有多远,并且可以看到哪些对象是有问题的对象[无论如何对于那个解决方案]。

编辑:除非我误解了某些东西,或者您有下一步计划,否则您的 MILP 可以简化很多。这就是我想尽量减少违规的想法。我已经改变L了界限,以获得一个不可行的原始问题。(这是我从您的示例中无法理解的:四个界限是什么?)设置x为二进制,并且L是非负整数,我们免费得到输出是非负整数,而新的lr, ur(“下/上松弛”)是积分,但我们需要强制执行非负性。最后,由于您已经在使用凸优化软件,我认为将目标设为违规的平方应该会产生“更好”的结果。

import numpy as np
import cvxpy

L = np.array([[2,3,0,3,0,8,4],
              [3,0,2,0,0,0,4],
              [0,2,5,1,3,0,2]])
nc, n = L.shape

train_mins = np.array([12,6,8])
valid_mins = np.array([7,3,4])

x = cvxpy.Variable(n, boolean=True)
lr = cvxpy.Variable(nc, nonneg=True)
ur = cvxpy.Variable(nc, nonneg=True)

lb = (L @ x >= train_mins.T - lr)
ub = (L @ x <= (sum(L.T)-valid_mins).T + ur)
constraints = [lb, ub]

objective = (sum(lr)+sum(ur))**2

problem = cvxpy.Problem(cvxpy.Minimize(objective), constraints)
problem.solve()

https://github.com/bmreiniger/datascience.stackexchange/blob/master/54450.ipynb

假设没关系,它至少消除了迭代不同训练/评估比例的需要。(您可以修改上限和下限以及松弛/目标,以将其更多地放在训练/评估比例方面。)

我对不同的求解器不太熟悉。肤浅的一瞥表明它ECOS_BB实际上并不是为 MICP 构建的,因此也许将目标支持线性目标是一个更好的主意。