如何避免重新计算多个变量,而不完全重构代码

huangapple go评论65阅读模式
英文:

How to avoid recomputing several variables without completely refactoring code

问题

我明白你的要求,以下是你提供的代码的翻译部分:

我有一个名为'detectors'的函数类别它接受一个视频一个空间尺度参数和一个时间尺度参数并检测一些兴趣点我编写了一个'MultiscaleDetector'函数它基本上接受空间尺度参数的列表和一个'detector'函数作为参数并对每个尺度执行给定的检测因子

这是它们的样子

```python
def GaborDetector(v, sigma, tau, threshold, num_points):
    """
    Gabor检测器
    
    关键参数:
    video -- 输入视频(y_len,x_len,frames)
    sigma -- 高斯核空间标准差
    tau -- 高斯核时间标准差
    kappa -- Gabor响应阈值
    """
    # 设置视频
    video = v.copy()
    video = video.astype(float) / video.max()
    video = video_smoothen_space(video, sigma)
    # 首先定义一个宽度为-2tau到2tau的线性空间
    time = np.linspace(-2 * tau, 2 * tau, int(4 * tau + 1))
    omega = 4 / tau
    # 定义Gabor滤波器
    h_ev = np.exp(-time**2 / (2 * tau**2)) * np.cos(2 * np.pi * omega * time)
    h_od = np.exp(-time**2 / (2 * tau**2)) * np.sin(2 * np.pi * omega * time)
    # 规范化L1范数
    h_ev /= np.linalg.norm(h_ev, ord=1)
    h_od /= np.linalg.norm(h_od, ord=1)
    # 计算响应
    response = (scp.convolve1d(video, h_ev, axis=2) ** 2) + (scp.convolve1d(video, h_od, axis=2) ** 2)
    points = interest_points(response, num=num_points, threshold=threshold, scale=sigma)
    return points
def MultiscaleDetector(detector, video, sigmas, tau, num_points):
    """
    多尺度检测器

    在多个尺度上执行检测器。检测器必须是一个接受视频作为输入的函数,以及其他参数,并返回兴趣点列表。
    
    关键参数:
    detector -- 返回兴趣点的函数
    video -- 输入视频(y_len,x_len,frames)
    sigmas -- 尺度列表
    """
    
    # 对于每个尺度,计算响应
    points = []
    for sigm in sigmas:
        found = detector(video, sigm, tau)
        points.append(found)

    # 过滤点,目前无关紧要

正如你可能已经注意到的,'GaborDetector'函数内部进行了一些繁重的计算,这些计算仅依赖于时间参数。因此,'MultiscaleDetector'函数在每次调用时不必重新计算这些变量。

为了避免重构代码,我尝试了一个我希望是一个巧妙的技巧,但认为可能是徒劳的:

def MultiscaleDetector(detector, video, sigmas, tau, num_points):
    """
    多尺度检测器

    在多个尺度上执行检测器。检测器必须是一个接受视频作为输入的函数,以及其他参数,并返回兴趣点列表。
    
    关键参数:
    detector -- 返回兴趣点的函数
    video -- 输入视频(y_len,x_len,frames)
    sigmas -- 尺度列表
    """
    
    optimization_trick = lambda s_param: detector(video, s_param, tau)
    # 对于每个尺度,计算响应
    points = []
    for sigm in sigmas:
        found = optimization_trick(sigm)
        points.append(found)

    # 过滤点,目前无关紧要

我希望仅依赖于tau的变量在'optimization_trick'中保持“存储”,以避免重新计算。然而,当我计时不同的实现时,差异约为0.02秒,优化后的函数更快。

编辑:

实际的调用如下:

# 读取视频
video = read_video(video_name, num_frames, 0)
# 获取兴趣点
detector = lambda v, s, t: GaborDetector(v, s, t, 0.3, 500)
scales = [3 * (1.1)**i for i in range(6)]
start = time.time()
points = MultiscaleDetector(detector, video, scales, 1.5, 500)
end = time.time()
print("时间:{}".format(end-start))

我尝试将我想要避免重新计算的所有变量放在一个不同的函数中,就像这样:

def GaborDetectorTrial(v, sigma, tau, threshold, num_points):
    """
    Gabor检测器
    
    关键参数:
    video -- 输入视频(y_len,x_len,frames)
    sigma -- 高斯核空间标准差
    tau -- 高斯核时间标准差
    kappa -- Gabor响应阈值
    """
    @lru_cache(maxsize=None)
    def time_function(tau):
        time = np.linspace(-2 * tau, 2 * tau, int(4 * tau + 1))
        omega = 4 / tau
        # 定义Gabor滤波器
        h_ev = np.exp(-time**2 / (2 * tau**2)) * np.cos(2 * np.pi * omega * time)
        h_od = np.exp(-time**2 / (2 * tau**2)) * np.sin(2 * np.pi * omega * time)
        # 规范化L1范数
        h_ev /= np.linalg.norm(h_ev, ord=1)
        h_od /= np.linalg.norm(h_od, ord=1)
        return h_ev, h_od
    # 设置视频
    video = v.copy()
    video = video.astype(float) / video

<details>
<summary>英文:</summary>

I have a category of functions &#39;detectors&#39;, that, given a video, a space scale parameter and a time scale parameter, detect some interest points. I wrote a &#39;MultiscaleDetector&#39; function, that basically takes a list of space scale parameters and a &#39;detector&#39; function as parameters and executes the given detector factor for every scale.

This is how they look:

def GaborDetector(v, sigma, tau, threshold, num_points):
"""
Gabor Detector

Keyword arguments:
video -- input video (y_len, x_len, frames)
sigma -- Gaussian kernel space standard deviation
tau -- Gaussian kernel time standard deviation
kappa -- Gabor response threshold
&quot;&quot;&quot;
# setup video
video = v.copy()
video = video.astype(float)/video.max()
video = video_smoothen_space(video, sigma)
# first define a linspace of width -2tau to 2tau
time = np.linspace(-2*tau, 2*tau, int(4*tau+1))
omega = 4/tau
# define the gabor filters
h_ev = np.exp(-time**2/(2*tau**2)) * np.cos(2*np.pi*omega*time)
h_od = np.exp(-time**2/(2*tau**2)) * np.sin(2*np.pi*omega*time)
# normalize the L1 norm
h_ev /= np.linalg.norm(h_ev, ord=1)
h_od /= np.linalg.norm(h_od, ord=1)
# compute the response
response = (scp.convolve1d(video, h_ev, axis=2) ** 2) + (scp.convolve1d(video, h_od, axis=2) ** 2)
points = interest_points(response, num=num_points, threshold=threshold, scale=sigma)
return points


def MultiscaleDetector(detector, video, sigmas, tau, num_points):
"""
Multiscale Detector

Executes a detector at multiple scales. Detector has to be a function that
takes a video as input, along with other parameters, and returns a list of interest points.


Keyword arguments:
detector -- function that returns interest points
video -- input video (y_len, x_len, frames)
sigmas -- list of scales
&quot;&quot;&quot;

# for every scale, compute the response
points = []
for sigm in sigmas:
    found = detector(video, sigm, tau)
    points.append(found)

# filter the points, currently irrelevant

As you can maybe already notice, there is some heavy computation done inside the &quot;GaborDetector&quot; function, that depends only on the time parameter. Therefore, the &quot;MultiscaleDetector&quot; function unnecessarily recomputes these variables in each call.


In an attempt to avoid refactoring the code, I came up with what I wished was a nice trick, but believed it would be vain:

def MultiscaleDetector(detector, video, sigmas, tau, num_points):
"""
Multiscale Detector

Executes a detector at multiple scales. Detector has to be a function that
takes a video as input, along with other parameters, and returns a list of interest points.


Keyword arguments:
detector -- function that returns interest points
video -- input video (y_len, x_len, frames)
sigmas -- list of scales
&quot;&quot;&quot;

optimization_trick = lambda s_param: detector(video, s_param, tau)
# for every scale, compute the response
points = []
for sigm in sigmas:
    found = optimization_trick(sigm)
    points.append(found)

# filter the points, currently irrelevant

I hoped that the variables dependent only on tau would remain &quot;stored&quot; somehow in the &quot;optimization_trick&quot; and avoid being recomputed. However, when I timed the different implementations, the difference was around 0.02 seconds, with the &quot;optimized&quot; function being faster.

EDIT:

The actual call that takes place is the following:

read the video

video = read_video(video_name, num_frames, 0)

get the interest points

detector = lambda v, s, t: GaborDetector(v, s, t, 0.3, 500)
scales = [3*(1.1)**i for i in range(6)]
start = time.time()
points = MultiscaleDetector(detector, video, scales, 1.5, 500)
end = time.time()
print("Time: {}".format(end-start))


I tried putting all the variables that i wanted to avoid in a different function like this:

def GaborDetectorTrial(v, sigma, tau, threshold, num_points):
"""
Gabor Detector

Keyword arguments:
video -- input video (y_len, x_len, frames)
sigma -- Gaussian kernel space standard deviation
tau -- Gaussian kernel time standard deviation
kappa -- Gabor response threshold
&quot;&quot;&quot;
@lru_cache(maxsize=None)
def time_function(tau):
    time = np.linspace(-2*tau, 2*tau, int(4*tau+1))
    omega = 4/tau
    # define the gabor filters
    h_ev = np.exp(-time**2/(2*tau**2)) * np.cos(2*np.pi*omega*time)
    h_od = np.exp(-time**2/(2*tau**2)) * np.sin(2*np.pi*omega*time)
    # normalize the L1 norm
    h_ev /= np.linalg.norm(h_ev, ord=1)
    h_od /= np.linalg.norm(h_od, ord=1)
    return h_ev, h_od
# setup video
video = v.copy()
video = video.astype(float)/video.max()
video = video_smoothen_space(video, sigma)
# compute the response
h_ev, h_od = time_function(tau)
response = (scp.convolve1d(video, h_ev, axis=2) ** 2) + (scp.convolve1d(video, h_od, axis=2) ** 2)
points = interest_points(response, num=num_points, threshold=threshold, scale=sigma)
return points
The variables that i want to avoid recomputing are h_ev and h_od. The execution time was basically the same (actually, some ms slower). 

</details>


# 答案1
**得分**: 0

你能不能把它拆分成多个函数?

示例:

```python
def time_function(tau): # 计算 hev 和 hod
    time = np.linspace(-2*tau, 2*tau, int(4*tau+1))
    omega = 4/tau
    # 定义 Gabor 滤波器
    h_ev = np.exp(-time**2/(2*tau**2)) * np.cos(2*np.pi*omega*time)
    h_od = np.exp(-time**2/(2*tau**2)) * np.sin(2*np.pi*omega*time)
    # 归一化 L1 范数
    h_ev /= np.linalg.norm(h_ev, ord=1)
    h_od /= np.linalg.norm(h_od, ord=1)
    return h_ev, h_od

然后:

# 读取视频
video = read_video(video_name, num_frames, 0)
# 获取兴趣点
custom_tau = {}

scales = [3*(1.1)**i for i in range(6)]
start = time.time()
for i in range(0,10): # 循环是因为否则只调用一次 GaborDetector,所以问题不相关
    # 在这里插入逻辑,因为为了使你的问题相关,Tau 不应总是相同
    if tau not in custom_tau.keys(): # 逻辑来“缓存”相同 tau 值的 hev 和 hod
        hev, hod = time_function(tau)
        custom_tau[tau] = (hev, hod)
    else:
        hev, hod = custom_tau[tau]
    gabor = GaborDetector(v, s, t, hev, hod, 0.3, 500)
    points = MultiscaleDetector(gabor, video, scales, 1.5, 500)
end = time.time()
print("Time: {}".format(end-start))

最后,你可以更改 GarborCollector,以避免重新计算时间:

```python
def GaborDetectorTrial(v, sigma, tau, hev, hod, threshold, num_points):

    video = v.copy()
    video = video.astype(float)/video.max()
    video = video_smoothen_space(video, sigma)
    response = (scp.convolve1d(video, h_ev, axis=2) ** 2) + (scp.convolve1d(video, h_od, axis=2) ** 2)
    points = interest_points(response, num=num_points, threshold=threshold, scale=sigma)
    return points

在这段代码中,你通过一个字典进行“缓存”,如果你已经为给定的“tau”计算了它,那么你将在字典中查找结果,否则你将计算它并将其放入字典中。我不得不推测和猜测你的代码如何工作,因为在你提供的情况下,由于只调用一次 garbor,he_v 和 ho_d 从不重新计算。

英文:

Can't you just split in multiple function?

Example :

def time_function(tau): #To compute hev and hod
        time = np.linspace(-2*tau, 2*tau, int(4*tau+1))
        omega = 4/tau
        # define the gabor filters
        h_ev = np.exp(-time**2/(2*tau**2)) * np.cos(2*np.pi*omega*time)
        h_od = np.exp(-time**2/(2*tau**2)) * np.sin(2*np.pi*omega*time)
        # normalize the L1 norm
        h_ev /= np.linalg.norm(h_ev, ord=1)
        h_od /= np.linalg.norm(h_od, ord=1)
        return h_ev, h_od

and then

# read the video
video = read_video(video_name, num_frames, 0)
# get the interest points
custom_tau = {}

scales = [3*(1.1)**i for i in range(6)]
start = time.time()
for i in range(0,10): # for loop cause otherwise you call GaborDetector only once so the question is not revelant
    #Insert logic here because for your question to be revelant Tau must not always be the same
    if tau not in custom_tau.keys(): # Logic to &quot;cache&quot; hev and hod for the same tau value
        hev, hod = time_function(tau)
        custom_tau[tau] = (hev, hod)
    else:
        hev, hod = custom_tau[tau]
    gabor = GaborDetector(v, s, t, hev, hod, 0.3, 500)
    points = MultiscaleDetector(gabor, video, scales, 1.5, 500)
end = time.time()
print(&quot;Time: {}&quot;.format(end-start))

and finally you change GarborCollector to not recompute time

def GaborDetectorTrial(v, sigma, tau,hev, hod, threshold, num_points):

    video = v.copy()
    video = video.astype(float)/video.max()
    video = video_smoothen_space(video, sigma)
    response = (scp.convolve1d(video, h_ev, axis=2) ** 2) + (scp.convolve1d(video, h_od, axis=2) ** 2)
    points = interest_points(response, num=num_points, threshold=threshold, scale=sigma)
    return points

What you do in this code is "caching" (through a dictionnary) hev and hod, so that if you already computed it for a given "tau", you instead lookup in the dictionnary for the result, otherwise you compute it and put it in the dictionnary

I had to extrapolate and guess how your code would work, because in the case you had given, he_v and ho_d are never re-computed since you call garbor only once

答案2

得分: 0

你必须在适当的作用域中定义要缓存的函数(time_function),在这里是在 GaborDetectorTrial 外部 - 就像你的代码中一样,time_function 的生命周期会在每次 GaborDetectorTrial 返回时结束,它的缓存也会结束。

英文:

You must define the to-be-cached function (time_function) in an appropriate scope, here: outside of GaborDetectorTrial - as it is in your code above, the time_function's life ends each time GaborDetectorTrial returns, and so does its cache's.

huangapple
  • 本文由 发表于 2023年6月15日 20:58:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76482735.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定