Python 具有降维的高效numpy数组随机视图
出于计算机视觉训练的目的,随机裁剪通常被用作数据增强技术。在每次迭代中,生成一批随机作物,并将其输入到正在训练的网络中。这需要有效率,因为它是在每次训练迭代中完成的 如果数据的维度太多,则可能还需要进行随机维度选择。例如,可以在视频中选择随机帧。数据甚至可以有4个维度(3个空间+时间),或者更多 如何编写一个高效的低维随机视图生成器? 从3D数据中获取2D视图的一个非常幼稚的版本可能是:Python 具有降维的高效numpy数组随机视图,python,numpy,Python,Numpy,出于计算机视觉训练的目的,随机裁剪通常被用作数据增强技术。在每次迭代中,生成一批随机作物,并将其输入到正在训练的网络中。这需要有效率,因为它是在每次训练迭代中完成的 如果数据的维度太多,则可能还需要进行随机维度选择。例如,可以在视频中选择随机帧。数据甚至可以有4个维度(3个空间+时间),或者更多 如何编写一个高效的低维随机视图生成器? 从3D数据中获取2D视图的一个非常幼稚的版本可能是: import numpy as np import numpy.random as nr def view
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
- 视图可以采用
integer参数,一次生成多个视图,而无需循环batch\u size
- 未指定输入/输出数据的尺寸(例如,可以进行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,:]
是一个视图。我没有太注意您的批处理
愿望。