Python 删除for循环以加快执行-矢量化
作为我学术项目的一部分,我正在研究图像的线性滤波器。下面是代码,仅使用NumPy(无外部库),希望通过矢量化或任何其他选项消除for循环。如何实现矢量化以更快地执行?谢谢你的帮助 投入-Python 删除for循环以加快执行-矢量化,python,python-3.x,numpy,vectorization,Python,Python 3.x,Numpy,Vectorization,作为我学术项目的一部分,我正在研究图像的线性滤波器。下面是代码,仅使用NumPy(无外部库),希望通过矢量化或任何其他选项消除for循环。如何实现矢量化以更快地执行?谢谢你的帮助 投入- Image.shape-(568768) weightArray.shape-(3,3) def apply_filter(图像:np.array,权重_array:np.array)->np.array: 行,cols=image.shape 高度、宽度=重量\u数组形状 输出=np.0((行-高度+1,
- Image.shape-(568768)
- weightArray.shape-(3,3)
def apply_filter(图像:np.array,权重_array:np.array)->np.array:
行,cols=image.shape
高度、宽度=重量\u数组形状
输出=np.0((行-高度+1,列-宽度+1))
对于范围内的箭头(行-高度+1):
对于范围内的C列(列-宽度+1):
对于hh范围内的高度(高度):
对于范围内的wwidth(宽度):
imgval=图像[rrow+HHHEIGHT,ccolumn+wwidth]
filterval=权重\数组[hhheight,wwidth]
输出[rrow,ccolumn]+=imgval*filterval
返回输出
矢量化是将循环的每个显式转换为一维数组操作的过程。
在Python中,这将涉及到重新想象数据
在下面的代码中,我提供了内核循环的有效矢量化。这说明了如何实现矢量化,但由于它只是优化了3x3阵列,因此无法获得最大的可用收益
如果您想看到更大的改进,您将对图像数组进行矢量化,我也为您制作了模板,但留下一些作为练习
import numpy as np
from PIL import Image
## no vectorization
def applyFilterMethod1(image: np.array, weightArray: np.array) -> np.array:
rows, cols = image.shape ; height, width = weightArray.shape
output = np.zeros((rows - height + 1, cols - width + 1))
for rrow in range(rows - height + 1):
for ccolumn in range(cols - width + 1):
for hheight in range(height):
for wwidth in range(width):
imgval = image[rrow + hheight, ccolumn + wwidth]
filterval = weightArray[hheight, wwidth]
output[rrow, ccolumn] += imgval * filterval
return output
## vectorize the kernel loop (~3x improvement)
def applyFilterMethod2(image: np.array, weightArray: np.array) -> np.array:
rows, cols = image.shape ; height, width = weightArray.shape
output = np.zeros((rows - height + 1, cols - width + 1))
for rrow in range(rows - height + 1):
for ccolumn in range(cols - width + 1):
imgval = image[rrow:rrow + height, ccolumn:ccolumn + width]
filterval = weightArray[:, :]
output[rrow, ccolumn] = sum(sum(imgval * filterval))
return output
## vectorize the image loop (~50x improvement)
def applyFilterMethod3(image: np.array, weightArray: np.array) -> np.array:
rows, cols = image.shape ; height, width = weightArray.shape
output = np.zeros((rows - height + 1, cols - width + 1))
for hheight in range(height):
for wwidth in range(width):
imgval = 0 ## TODO -- construct a compatible slice
filterval = weightArray[hheight, wwidth]
output[:, :] += imgval * filterval
return output
src = Image.open("input.png")
sb = np.asarray(src)
cb = np.array([[1,2,1],[2,4,2],[1,2,1]])
cb = cb/sum(sum(cb)) ## normalize
db = applyFilterMethod2(sb, cb)
dst = Image.fromarray(db)
dst.convert("L").save("output.png")
#src.show() ; dst.show()
注意:您可能会删除所有四个for
循环,但会增加一些复杂性。但是,因为这只会消除9次迭代的开销(在本例中),所以我估计它不会比applyFilterMethod3
产生任何额外的性能增益。此外,尽管我还没有尝试过,但我想象的方法可能会增加比它所能消除的更多的开销
仅供参考:这是一种标准的图像卷积(仅支持实现的灰度)。我总是想指出,为了在数学上是正确的,这需要补偿几乎每一个默认图像编码中隐含的错误——但是这个小细节经常被忽略
讨论
这种类型的向量化在Python中经常是必需的,特别是因为标准Python解释器正在处理大型for
循环。因此,显式迭代图像的每个像素会浪费大量时间。但最终,矢量化实现不会改变实际执行的工作量,因此我们只讨论消除算法的一个方面
然而,矢量化有一个附带的好处:。将大量数据处理集中到一个操作符上,使语言/库在如何优化执行方面具有更大的灵活性。这可能包括在GPU上执行操作——如果您有合适的工具,例如
Python对的无缝支持是它在机器学习中非常流行的一个原因,机器学习可能是计算密集型的
解决方案
下面是imgval
分配的解决方案,这是上面的练习
imgval = image[hheight:hheight+rows - height+1, wwidth:wwidth+cols - width +1]
可以构造图像的切片视图数组,每个视图按权重数组的索引移位,然后将其乘以权重并求和
def apply_filter(image: np.array, weights: np.array) -> np.array:
height, width = weights.shape
indices = np.indices(weights.shape).T.reshape(weights.size, 2)
views = np.array([image[r:-height+r,c:-width+c] for r, c in indices])
return np.inner(views.T, weights.T.flatten()).T # sum product
(我必须在多个点进行转置和重塑,以将数据转换为所需的形状和顺序。可能有更简单的方法。)
在权重索引上仍然存在一个以列表理解的形式出现的偷偷的for
循环,但是我们将for循环中的操作最小化,以创建一组切片视图。这个循环可以避免使用,但不清楚这是否会提高性能;或者(请参阅答案)。欢迎来到StackOverflow@parvy。请创建一个。添加可运行的代码和一些示例数据。SciPy是否禁止使用?我很确定这是一个SciPy内置程序。您可能正在寻找SciPy.signal.convolve2d
。当然可以对代码进行矢量化,但除非出于教育目的您对此感兴趣,否则我建议您使用scipy。为什么使用numpy而不是scipy?他们是同一个项目的一部分。所以这是一个家庭作业问题?然后你应该展示你迄今为止所做的尝试以及失败的地方。因为这似乎是一个家庭作业问题,所以我没有完整的答案。我稍后会回来填写TODO
部分。Hint1:它遵循与applyFilterMethod2
相同的一般形式,但翻译并不简单。Hint2:如果您弄错了,Python解释器应该向您提供有关数组操作中切片不兼容的详细信息。尽管这可能是某人的家庭作业,但所述概念可能与未来的访问者相关。方法2和方法3中的矢量化分别产生3倍和50倍的性能增益。