Numpy 规范化代码异常缓慢
Posted
技术标签:
【中文标题】Numpy 规范化代码异常缓慢【英文标题】:Numpy normalization code is strangely slow 【发布时间】:2013-12-16 23:32:43 【问题描述】:我正在整理一些基本的 Python 代码,这些代码包含映射到矩阵列表的标签字典(矩阵表示分类图像),我只是试图从所有内容中减去平均图像,然后将数据集中在一个 0 - 1 的比例。由于某种原因,这段代码似乎运行缓慢。当仅迭代 500 个 48x48 图像时,运行大约需要 10 秒,这并不能真正扩展到我正在使用的图像数量。查看 cProfile 结果后,看起来大部分时间都花在了 _center 函数上。
我觉得我在这里可能没有充分使用 numpy,并且想知道是否有人比我更有经验的人有一些技巧来加快速度,或者可以指出我在这里做的一些愚蠢的事情。代码贴在下面:
def __init__(self, master_dict, normalization = lambda x: math.exp(x)):
"""
master_dict should be a dictionary mapping classes to lists of matrices
example =
"cats": [[[]...], [[]...]...],
"dogs": [[[]...], [[]...]...]
have to be python lists, not numpy arrays
normalization represents the 0-1 normalization scheme used. Defaults to simple linear
"""
normalization = np.vectorize(normalization)
full_tensor = np.array(reduce(operator.add, master_dict.values()))
centering = np.sum(np.array(reduce(operator.add, master_dict.values())), axis=0)/len(full_tensor)
self.data = key: self._center(np.array(value), centering, normalization) for key,value in master_dict.items()
self.normalization = normalization
def _center(self, list_of_arrays, centering_factor, normalization_scheme):
"""
Centering scheme for arrays
"""
arrays = list_of_arrays - centering_factor
normalize = lambda a: (a - np.min(a)) / (np.max(a) - np.min(a))
return normalization_scheme([normalize(array) for array in arrays])
另外,在你问之前,我对输入格式没有大量的控制权,但如果这真的是这里的限制因素,我可能会弄清楚一些事情。
【问题讨论】:
在执行操作之前,您能否在内部将master_dict
中的“矩阵”转换为 numpy 数组?我觉得你在浪费一些时间不断转换为np.array
。我知道这可能不是瓶颈,但它确实是。
另外,请查看:docs.scipy.org/doc/numpy/reference/generated/…。使用这个 numpy 内置标准化函数可能会比您的 lambda
更快。
最后,如果您使用np.exp
而不是np.vectorize(lambda x: math.exp)
,您可能会从normalization
获得更好的性能。所有math
函数都具有numpy
等效项,并且在numpy
数组上应该比矢量化等效项更快。
@SethMMorton 感谢您的建议!可悲的是,您链接到的 scipy 文档指的是一种与我在这里所做的非常不同的规范化。最初将数组转换为 np 数组的问题是我在前几行中使用了很多 python-list 特定技巧,但是如果在 numpy 中有一个好方法可以做到这一点,我很乐意切换到更多高性能版本。最后,numpy.exp 替换实际上产生了巨大的变化!这节省了我 30% 的执行时间!
我很确定 reduce
行可以替换为 full_tensor = np.concatenate(master_dict.values())
但在我测试的小示例中似乎并没有快多少。
【参考方案1】:
除了似乎可行的math.exp -> np.exp
建议之外,我还建议进行一些其他修改。首先,你要计算np.array(reduce(operator.add, master_dict.values()))
两次,所以在下面的返工中,我建议重用数据而不是两次工作。其次,我将您的 normalize
lambda 修改为适当的函数,以便您可以预先计算数组的最小值。这样可以节省计算两次。
def __init__(self, master_dict, normalization = np.exp):
"""
master_dict should be a dictionary mapping classes to lists of matrices
example =
"cats": [[[]...], [[]...]...],
"dogs": [[[]...], [[]...]...]
have to be python lists, not numpy arrays
normalization represents the 0-1 normalization scheme used. Defaults to simple linear
"""
full_tensor = np.array(reduce(operator.add, master_dict.values()))
centering = np.sum(full_tensor, axis=0)/len(full_tensor)
self.data = key: self._center(np.array(value), centering, normalization) for key,value in master_dict.items()
self.normalization = normalization
def _center(self, list_of_arrays, centering_factor, normalization_scheme):
"""
Centering scheme for arrays
"""
def normalize(a):
a_min = np.min(a)
return (a - a_min) / (np.max(a) - a_min)
arrays = list_of_arrays - centering_factor
return normalization_scheme([normalize(array) for array in arrays])
我考虑到您关于需要执行 python 特定操作的评论,因此您无法在操作数据之前转换为 arrays
,没有什么能阻止您在 numpy 数组上调用(例如)reduce
。 Numpy 数组是可迭代的,因此在任何使用列表的地方都可以使用 numpy 数组(好吧,不是任何地方,但在大多数情况下)。但是,我还没有完全熟悉你的算法,也许这种情况是例外之一。
【讨论】:
【参考方案2】:从@sethMMorton 的更改开始,我的速度几乎提高了两倍。主要来自矢量化您的normalize
函数(在_center
内部),以便您可以在整个 list_of_arrays
上调用_center
,而不仅仅是将其放入列表理解中。这也消除了从 numpy 数组到列表并返回的额外转换。
def normalize(a):
a -= a.min(1, keepdims=True).min(2, keepdims=True)
a /= a.max(1, keepdims=True).max(2, keepdims=True)
return a
注意,我不会在 _center
调用中定义 normalize
,而是将其分开,如本答案所示。那么,在_center
中,只需在整个list_of_arrays
上调用normalize
:
def _center(self, list_of_arrays, centering_factor, normalization_scheme):
"""
Centering scheme for arrays
"""
list_of_arrays -= centering_factor
return normalization_scheme(normalize(list_of_arrays))
事实上,您可以在一开始就对整个full_tensor
调用normalize
和_center
,而不必循环遍历,但棘手的部分是将其拆分回数组列表的字典中再次。接下来我会继续努力的:P
正如我的评论中提到的,您可以替换:
full_tensor = np.array(reduce(operator.add, master_dict.values()))
与
full_tensor = np.concatenate(master_dict.values())
这可能不会更快,但它更清晰并且是标准的方法。
最后,时间安排如下:
>>> timeit slater_init(example)
1 loops, best of 3: 1.42 s per loop
>>> timeit seth_init(example)
1 loops, best of 3: 489 ms per loop
>>> timeit my_init(example)
1 loops, best of 3: 281 ms per loop
以下是我的完整计时代码。请注意,我将self.data = ...
替换为return ...
,这样我就可以保存并比较输出,以确保我们所有的代码都返回相同的数据:) 当然,您也应该针对我的版本测试您的版本!
import operator
import math
import numpy as np
#example dict has N keys (integers), each value is a list of n random HxW 'arrays', in list form:
test_shape = 10, 2, 4, 4 # small example for testing
timing_shape = 100, 5, 48, 48 # bigger example for timing
N, n, H, W = timing_shape
example = dict(enumerate(np.random.rand(N, n, H, W).tolist()))
def my_init(master_dict, normalization=np.exp):
full_tensor = np.concatenate(master_dict.values())
centering = np.mean(full_tensor, 0)
return key: my_center(np.array(value), centering, normalization)
for key,value in master_dict.iteritems() #use iteritems here
#self.normalization = normalization
def my_normalize(a):
a -= a.min(1, keepdims=True).min(2, keepdims=True)
a /= a.max(1, keepdims=True).max(2, keepdims=True)
return a
def my_center(arrays, centering_factor, normalization_scheme):
"""
Centering scheme for arrays
"""
arrays -= centering_factor
return normalization_scheme(my_normalize(arrays))
#### sethMMorton's original improvement ####
def seth_init(master_dict, normalization = np.exp):
"""
master_dict should be a dictionary mapping classes to lists of matrices
example =
"cats": [[[]...], [[]...]...],
"dogs": [[[]...], [[]...]...]
have to be python lists, not numpy arrays
normalization represents the 0-1 normalization scheme used. Defaults to simple linear
"""
full_tensor = np.array(reduce(operator.add, master_dict.values()))
centering = np.sum(full_tensor, axis=0)/len(full_tensor)
return key: seth_center(np.array(value), centering, normalization) for key,value in master_dict.items()
#self.normalization = normalization
def seth_center(list_of_arrays, centering_factor, normalization_scheme):
"""
Centering scheme for arrays
"""
def seth_normalize(a):
a_min = np.min(a)
return (a - a_min) / (np.max(a) - a_min)
arrays = list_of_arrays - centering_factor
return normalization_scheme([seth_normalize(array) for array in arrays])
#### Original code, by slater ####
def slater_init(master_dict, normalization = lambda x: math.exp(x)):
"""
master_dict should be a dictionary mapping classes to lists of matrices
example =
"cats": [[[]...], [[]...]...],
"dogs": [[[]...], [[]...]...]
have to be python lists, not numpy arrays
normalization represents the 0-1 normalization scheme used. Defaults to simple linear
"""
normalization = np.vectorize(normalization)
full_tensor = np.array(reduce(operator.add, master_dict.values()))
centering = np.sum(np.array(reduce(operator.add, master_dict.values())), axis=0)/len(full_tensor)
return key: slater_center(np.array(value), centering, normalization) for key,value in master_dict.items()
#self.normalization = normalization
def slater_center(list_of_arrays, centering_factor, normalization_scheme):
"""
Centering scheme for arrays
"""
arrays = list_of_arrays - centering_factor
slater_normalize = lambda a: (a - np.min(a)) / (np.max(a) - np.min(a))
return normalization_scheme([slater_normalize(array) for array in arrays])
【讨论】:
谢谢,大部分的加速都来自你的提示,@Seth :)以上是关于Numpy 规范化代码异常缓慢的主要内容,如果未能解决你的问题,请参考以下文章
规范化/翻译 ndarray - Numpy / Python