Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 删除for循环以加快执行-矢量化_Python_Python 3.x_Numpy_Vectorization - Fatal编程技术网

Python 删除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,

作为我学术项目的一部分,我正在研究图像的线性滤波器。下面是代码,仅使用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,列-宽度+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倍的性能增益。