Python 具有降维的高效numpy数组随机视图

Python 具有降维的高效numpy数组随机视图,python,numpy,Python,Numpy,出于计算机视觉训练的目的,随机裁剪通常被用作数据增强技术。在每次迭代中,生成一批随机作物,并将其输入到正在训练的网络中。这需要有效率,因为它是在每次训练迭代中完成的 如果数据的维度太多,则可能还需要进行随机维度选择。例如,可以在视频中选择随机帧。数据甚至可以有4个维度(3个空间+时间),或者更多 如何编写一个高效的低维随机视图生成器? 从3D数据中获取2D视图的一个非常幼稚的版本可能是: import numpy as np import numpy.random as nr def view

出于计算机视觉训练的目的,随机裁剪通常被用作数据增强技术。在每次迭代中,生成一批随机作物,并将其输入到正在训练的网络中。这需要有效率,因为它是在每次训练迭代中完成的

如果数据的维度太多,则可能还需要进行随机维度选择。例如,可以在视频中选择随机帧。数据甚至可以有4个维度(3个空间+时间),或者更多

如何编写一个高效的低维随机视图生成器?

从3D数据中获取2D视图的一个非常幼稚的版本可能是:

import numpy as np
import numpy.random as nr

def views():
    # suppose `data` comes from elsewhere
    # data.shape is (n1, n2, n3)
    while True:
        drop_dim = nr.randint(0, 3)
        drop_dim_keep = nr.randint(0, shape[drop_dim])
        selector = np.zeros(shape, dtype=bool)
        if drop_dim == 0:
            selector[drop_dim_keep, :, :] = 1
        elif drop_dim == 1:
            selector[:, drop_dim_keep, :] = 1
        else:
            selector[:, :,  drop_dim_keep] = 1
        yield np.squeeze(data[selector])
可能存在更优雅的解决方案,其中至少:

  • 在随机选择的维度上没有丑陋的if/else
  • 视图可以采用
    batch\u size
    integer参数,一次生成多个视图,而无需循环
  • 未指定输入/输出数据的尺寸(例如,可以进行3D->2D以及4D->2D)

我调整了你的函数,以明确它在做什么:

def views():
    # suppose `data` comes from elsewhere
    # data.shape is (n1, n2, n3)
    while True:
        drop_dim = nr.randint(0, 3)
        dropshape = list(shape[:])
        dropshape[drop_dim] -= 1
        drop_dim_keep = nr.randint(0, shape[drop_dim])
        print(drop_dim, drop_dim_keep)
        selector = np.ones(shape, dtype=bool)
        if drop_dim == 0:
            selector[drop_dim_keep, :, :] = 0
        elif drop_dim == 1:
            selector[:, drop_dim_keep, :] = 0
        else:
            selector[:, :,  drop_dim_keep] = 0
        yield data[selector].reshape(dropshape)
小样本运行:

In [534]: data = np.arange(24).reshape(shape)
In [535]: data
Out[535]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
In [536]: v = views()
In [537]: next(v)
2 1
Out[537]: 
array([[[ 0,  2,  3],
        [ 4,  6,  7],
        [ 8, 10, 11]],

       [[12, 14, 15],
        [16, 18, 19],
        [20, 22, 23]]])
In [538]: next(v)
0 0
Out[538]: 
array([[[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
所以它选择了一个维度,并为该维度删除了一个“列”

主要的效率问题是它返回的是视图还是副本。在这种情况下,它必须返回一份副本

您使用的是布尔掩码来选择返回值,与本例中的
np.delete操作完全相同

In [544]: np.delete(data,1,2).shape
Out[544]: (2, 3, 3)
In [545]: np.delete(data,0,0).shape
Out[545]: (1, 3, 4)
因此,您可以用
delete
替换大部分内部变量,让它负责泛化维度。看看它的代码,看看它是如何处理这些细节的(它不短也不甜!)


delete
替换为
take

def rand_take():
    while True:
        take_dim = nr.randint(0, 3)
        take_keep = nr.randint(0, shape[take_dim])
        print(take_dim, take_keep)
        yield np.take(data, take_keep, axis=take_dim)

In [580]: t = rand_take()
In [581]: next(t)
0 0
Out[581]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [582]: next(t)
2 3
Out[582]: 
array([[ 3,  7, 11],
       [15, 19, 23]])
np.take
返回一个副本,但等效切片不返回

In [601]: data.__array_interface__['data']
Out[601]: (182632568, False)
In [602]: np.take(data,0,1).__array_interface__['data']
Out[602]: (180099120, False)
In [603]: data[:,0,:].__array_interface__['data']
Out[603]: (182632568, False)
可以使用以下表达式生成切片元组:

In [604]: idx = [slice(None)]*data.ndim
In [605]: idx[1] = 0
In [606]: data[tuple(idx)]
Out[606]: 
array([[ 0,  1,  2,  3],
       [12, 13, 14, 15]])

采用
参数的各种
numpy
函数构造如下索引元组。(例如,一个或多个
应用…
函数。

我调整了您的函数以澄清它在做什么:

def views():
    # suppose `data` comes from elsewhere
    # data.shape is (n1, n2, n3)
    while True:
        drop_dim = nr.randint(0, 3)
        dropshape = list(shape[:])
        dropshape[drop_dim] -= 1
        drop_dim_keep = nr.randint(0, shape[drop_dim])
        print(drop_dim, drop_dim_keep)
        selector = np.ones(shape, dtype=bool)
        if drop_dim == 0:
            selector[drop_dim_keep, :, :] = 0
        elif drop_dim == 1:
            selector[:, drop_dim_keep, :] = 0
        else:
            selector[:, :,  drop_dim_keep] = 0
        yield data[selector].reshape(dropshape)
小样本运行:

In [534]: data = np.arange(24).reshape(shape)
In [535]: data
Out[535]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
In [536]: v = views()
In [537]: next(v)
2 1
Out[537]: 
array([[[ 0,  2,  3],
        [ 4,  6,  7],
        [ 8, 10, 11]],

       [[12, 14, 15],
        [16, 18, 19],
        [20, 22, 23]]])
In [538]: next(v)
0 0
Out[538]: 
array([[[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
所以它选择了一个维度,并为该维度删除了一个“列”

主要的效率问题是它是返回一个视图还是一个副本。在这种情况下,它必须返回一个副本

您使用的是布尔掩码来选择返回值,与本例中的
np.delete操作完全相同

In [544]: np.delete(data,1,2).shape
Out[544]: (2, 3, 3)
In [545]: np.delete(data,0,0).shape
Out[545]: (1, 3, 4)
因此,您可以用
delete
替换大部分内部变量,让它负责概括维度。查看它的代码,看看它如何处理这些细节(它不短也不甜!)


delete
替换为
take

def rand_take():
    while True:
        take_dim = nr.randint(0, 3)
        take_keep = nr.randint(0, shape[take_dim])
        print(take_dim, take_keep)
        yield np.take(data, take_keep, axis=take_dim)

In [580]: t = rand_take()
In [581]: next(t)
0 0
Out[581]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [582]: next(t)
2 3
Out[582]: 
array([[ 3,  7, 11],
       [15, 19, 23]])
np.take
返回一个副本,但等效切片不返回

In [601]: data.__array_interface__['data']
Out[601]: (182632568, False)
In [602]: np.take(data,0,1).__array_interface__['data']
Out[602]: (180099120, False)
In [603]: data[:,0,:].__array_interface__['data']
Out[603]: (182632568, False)
可以使用以下表达式生成切片元组:

In [604]: idx = [slice(None)]*data.ndim
In [605]: idx[1] = 0
In [606]: data[tuple(idx)]
Out[606]: 
array([[ 0,  1,  2,  3],
       [12, 13, 14, 15]])

不同的
numpy
函数采用
参数,构造一个这样的索引元组。(例如一个或多个
应用…
函数。

看看是否有帮助。看看是否有帮助。嗯,好吧,我知道我在那里做了什么:我把问题(我的意思)和代码搞砸了。问题是“只保留一个随机列”,而不是“只删除一个随机列”。我的错误,编辑…这确实解决了我的第一点(更优雅)。你知道如何解决我的第二点吗:添加一个“批量大小”“参数?还有:正如你所指出的,这仍然是复制,这不是最佳的。它似乎
np.take
,所有的行为都是这样的。
np.take(data,0,1)
返回一个副本,但是
data[:,0,:]
是一个视图。我没有太多关注你的
批处理
愿望。嗯,好吧,我明白我在那里做了什么:我把问题(我的意思)和代码搞砸了。问题是“只保留一个随机列”,而不是“只删除一个随机列”。我不好,编辑…这确实解决了我的第一点(更优雅)。你知道如何解决我的第二个问题吗:添加一个“批量大小”参数?另外:正如你所指出的,这仍然是在复制,这不是最优的。它似乎
np.take
都是这样做的。
np.take(data,0,1)
返回一个副本,但是
data[:,0,:]
是一个视图。我没有太注意您的
批处理
愿望。