【问题标题】:TypeError: list indices must be integers or slices, not cupy.core.core.ndarrayTypeError:列表索引必须是整数或切片,而不是 cupy.core.core.ndarray
【发布时间】:2019-07-04 11:48:22
【问题描述】:

在对象检测算法中,非最大抑制(NMS)用于丢弃对象的额外检测结果,例如车辆。

通常,水平边界框用于对象检测算法,水平 NMS 的 GPU 实现已经存在,但我希望 GPU 实现旋转边界框。

CPU 实现已经完成,但我正在努力使用 CuPy 包将 CPU 版本转换为 GPU 版本。这是我写的代码。在代码部分之后,您可以看到错误。

我的问题是 TypeError 的原因是什么:列表索引必须是整数或切片,而不是 cupy.core.core.ndarray?

    from shapely.geometry import Polygon as shpoly
    import time
    
    #### CPU implementation
    import numpy as np   
    
    def polygon_iou(poly1, poly2):
      """
      Intersection over union between two shapely polygons.
      """
      if not poly1.intersects(poly2): # this test is fast and can accelerate calculation
        iou = 0
      else:
        try:
          inter_area = poly1.intersection(poly2).area
          union_area = poly1.area + poly2.area - inter_area
          iou = float(inter_area) / float(union_area)
        except shapely.geos.TopologicalError:
          warnings.warn("'shapely.geos.TopologicalError occured, iou set to 0'", UserWarning)
          iou = 0
        except ZeroDivisionError:
          iou = 0
      return iou
    
    def polygon_from_array(poly_):
      """
      Create a shapely polygon object from gt or dt line.
      """
      polygon_points = np.array(poly_).reshape(4, 2)
      polygon = shpoly(polygon_points).convex_hull
      return polygon
    
    def nms(dets, thresh):
        scores = dets[:, 8]
        order = scores.argsort()[::-1]
        polys = []
        areas = []
        for i in range(len(dets)):
            tm_polygon = polygon_from_array(dets[i,:8])
            polys.append(tm_polygon)
        keep = []
        while order.size > 0:
            ovr = []
            i = order[0]
            keep.append(i)
            for j in range(order.size - 1):
                iou = polygon_iou(polys[i], polys[order[j + 1]])
                ovr.append(iou)
            ovr = np.array(ovr)
            inds = np.where(ovr <= thresh)[0]
            order = order[inds + 1]
        return keep
    
    
    #### GPU implementation
    import cupy as cp  
      
    def polygon_iou_gpu(poly1, poly2):
      """
      Intersection over union between two shapely polygons.
      """
      if not poly1.intersects(poly2): # this test is fast and can accelerate calculation
        iou = 0
      else:
        try:
          inter_area = poly1.intersection(poly2).area
          union_area = poly1.area + poly2.area - inter_area
          iou = float(inter_area) / float(union_area)
        except shapely.geos.TopologicalError:
          warnings.warn("'shapely.geos.TopologicalError occured, iou set to 0'", UserWarning)
          iou = 0
        except ZeroDivisionError:
          iou = 0
      return iou
    
    def polygon_from_array_gpu(poly_):
      """
      Create a shapely polygon object from gt or dt line.
      """
      polygon_points = cp.array(poly_).reshape(4, 2)
      polygon = shpoly(polygon_points).convex_hull
      return polygon
    
    def nms_gpu(dets, thresh):
        scores = dets[:, 8]
        order = scores.argsort()[::-1]
        polys = []
        areas = []
        for i in range(len(dets)):
            tm_polygon = polygon_from_array_gpu(dets[i,:8])
            polys.append(tm_polygon)
        keep = []
        while order.size > 0:
            ovr = []
            i = order[0]
            keep.append(i)
            for j in range(order.size - 1):   
                iou = polygon_iou_gpu(polys[i], polys[order[j + 1]])
                ovr.append(iou)
            ovr = np.array(ovr)
            inds = np.where(ovr <= thresh)[0]
            order = order[inds + 1]
        return keep
    
    
    if __name__ == '__main__':
        import random
        boxes = np.random.randint(0,100,(1000,8))
        scores = np.random.rand(1000, 1)
        dets = np.hstack((boxes, scores[:])).astype(np.float32)

    
        thresh = 0.1
        start = time.time()
        keep = nms(dets, thresh)
        print("CPU implementation took: {}".format(time.time() - start))
    
        cp.cuda.Device(1)
        dets_gpu = cp.array(dets)
        start = time.time()
        keep = nms_gpu(dets_gpu, thresh)
        print("GPU implementation took: {}".format(time.time() - start))

错误是

CPU 实现耗时:0.3672311305999756

Traceback(最近一次调用最后一次):

文件“nms_rotated.py”,第 117 行,在

keep = nms_gpu(dets_gpu, thresh)

文件“nms_rotated.py”,第 97 行,在 nms_gpu 中

iou = polygon_iou_gpu(polys[i], polys[order[j + 1]])

TypeError: 列表索引必须是整数或切片,而不是 cupy.core.core.ndarray

更新:2019 年 2 月 13 日 我试过@Yuki Hashimoto的回答

iou = polygon_iou_gpu(polys[i], polys[order[j + 1]]) 替换为 iou = polygon_iou_gpu(polys[i.get()], polys[order[j + 1].get()])。它不会抛出任何错误,但 GPU 版本比 CPU 版本慢几倍。

通过使用 100000 次随机检测:

      CPU implementation took: 47.125494956970215
      GPU implementation took: 142.08464860916138

【问题讨论】:

    标签: python-3.x gpu object-detection nms cupy


    【解决方案1】:

    简而言之:使用PFN官方non-maximum suppression

    详情: 使用cp.where,它返回一个匹配某些条件的list 对象。


    不推荐corochann的答案,因为polys是一个列表,list也不应该被np.ndarray切片。 (并且不建议注入另一个依赖项......)

    >>> polys[order.get()]  # get method returns np.ndarray
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: only integer scalar arrays can be converted to a scalar index
    >>> polys[order[j + 1].get()]
    ### some result in some case, but this may fails depending on your env.###
    

    【讨论】:

    • 谢谢。我在chainer中检查了NMS的实现,但它是针对水平边界框的。正如我在问题中提到的,水平边界框的 NMS 已经实施了几次,但是对于旋转边界框,没有 CUDA 加速 NMS。我试图删除 Shapely 代码的依赖关系,但无法找到一个简单的方法来找出两个多边形的交集并计算它们的面积,以便进行交集而不是联合计算。
    • 我试过你的答案。它可以工作,但 GPU 的速度比 CPU 慢得多,随机检测 10000 次。
    • 有两个原因。首先,在您的代码中,数据在 CPU 和 GPU 之间来回切换。例如,Polygon 类在 CPU 上实现为数组,因此要使用 cupy,必须将其传输到 GPU。这也在order.get() 中观察到,它顺序(重要!)获取值,这不适合 GPU 的操作。其次,在PFN的官方例子中,NMS是直接用CUDA-C实现的,使用共享内存来快速访问内存(同时也是为了并行化我上面提到的顺序操作)。在 GPU 并行中编写 NMS 非常困难。
    • 需要注意的是,在几乎所有情况下,CPU 都比 GPU 快。只有 GRAPHICS 或 MATRIX MATH 在 GPU 中更快(巨大的并行性是例外之一)。
    • NMS 用于 GPU 实现的水平边界框比 CPU 实现快数倍。我将为旋转的边界框实现相同的加速。我认为 GPU 实现仍然应该比 CPU 更快,因为 NMS 操作通常没有太大变化。
    【解决方案2】:

    [2019 年 2 月 13 日更新]

    请参考@yuki-hashimoto的回答,比较合适。


    如错误信息中所写

    TypeError: 列表索引必须是整数或切片,而不是 cupy.core.core.ndarray

    我猜order 是cupy 数组? 在这种情况下,polys[order[j + 1]] 使用索引order[j+1] 作为可能导致问题的cupy 数组。 试试用cuda.to_cpu(array)方法把它们转成numpy数组怎么样?

    from chainer import cuda
    iou = polygon_iou_gpu(polys[i], polys[cuda.to_cpu(order[j + 1])])
    

    【讨论】:

    • 将其转换为 numpy 数组实际上是朝着 CPU 实现方向发展。目标是加快使用 CuPy。
    • 它只向GPU发送索引order,这是相当小的一部分。如果你想得到GPU的好处,大数组的计算可以在GPU中完成。我猜上面的示例代码使用了小数组,在这种情况下我猜简单地在 CPU 中计算会更快。
    • 哦,现在我明白你的意思了。我修改了代码以考虑随机 100 次检测。我仍然有同样的问题。我尝试了@yuki-hashimoto 的答案。我试过你的答案。它可以工作,但 GPU 的速度比 CPU 慢得多,随机检测 10000 次。
    猜你喜欢
    • 2016-09-16
    • 1970-01-01
    • 2015-12-09
    • 2021-11-28
    • 2020-09-23
    • 2020-09-04
    • 2019-10-01
    相关资源
    最近更新 更多