有什么方法可以加快/Python-化五个嵌套的 for() 循环?
Any Way to Speed / Python-ize Five Nested for() Loops?
(注意:我在本网站上阅读了其他一些相关问题,但我认为它们不适用于我的具体情况;如果适用,我深表歉意。)
问题:我正在尝试推导行星体上特定位置的光度参数。这是一个极其非线性的问题,光度参数以非线性方式相互作用。我已将问题减少到 5 个最重要的参数,我与该领域的专家进行了交谈,他们告诉我,是的,没有办法分析求解这些方程,他们所做的只是参数-space 扫描。思路是先有一个宽泛的参数space,然后随着参数的收敛对它进行细化(这句话还没有写到下面的代码片段中)
因为我需要搜索 5D 参数 space,所以我将其编写为五个嵌套的 for() 循环。每次最内层循环迭代时,它都会根据实际数据检查结果,确定 RMS 是否小于先前的最佳值,如果是,则保存这些参数。
问:有什么办法可以加快速度吗?它慢得令人痛苦,比我之前编程的“Igor Pro”语言还慢(但 Igor 不能被 Python 调用,我的所有其他代码都在 Python 中)。我想不出一种方法来“Pythonize”事物,因为它对每个唯一参数值都有相当多的数学运算,并且因为它在每次迭代时都进行测试。我能想到的另一个“唯一”的事情是在每次迭代中进一步粗化参数 space 这样,例如,它不需要在下面进行 10^5 次迭代,但我 运行如果我这样做,可能会丢失敏感的局部最小值。
#Iteration 1, start with a wide aray of possible parameters.
parameter_w = 0.0
parameter_b0 = 0.0
parameter_h = 1E-10
parameter_b = 0.0
parameter_c = 0.0
iterator_w = 0.10
iterator_b0 = 0.25
iterator_h = 0.10
iterator_b = 0.10
iterator_c = 0.10
maxits_w = 10+1
maxits_b0 = 10+1
maxits_h = 10+1
maxits_b = 10+1
maxits_c = 10+1
lastbest = 9999
#Do the parameter space search.
for counter_c in range(0,maxits_c):
parameter_c_test = parameter_c + iterator_c*counter_c
for counter_b in range(0,maxits_b):
parameter_b_test = parameter_b + iterator_b*counter_b
for counter_h in range(0,maxits_h):
parameter_h_test = parameter_h + iterator_h*counter_h
for counter_b0 in range(0,maxits_b0):
parameter_b0_test = parameter_b0 + iterator_b0*counter_b0
for counter_w in range(0,maxits_w):
parameter_w_test = parameter_w + iterator_w*counter_w
Vgamma = math.sqrt(1-parameter_w_test)
SurfaceRoughnessFunction = 1 #equals 1 if theta, the roughness parameter, is 0
#Modified incidence and emission angles (above eq. 2 in Hapke (2002)).
mu0 = [math.cos(Incidence[i]*3.1415926/180.) for i in range(len(Incidence))]
mu = [math.cos(Emission[i] *3.1415926/180.) for i in range(len(Emission ))]
#Coefficient ... normalization factor?
part1 = [parameter_w_test / 4. * mu0[i] / (mu0[i] + mu[i]) for i in range(len(DN))]
#Shadow-Hiding Opposition Effect (SHOE) --eq 28 with 29 from Hapke (2002)
part2 = [1 + parameter_b0_test / (1 + math.tan(Phase[i]/2.*3.1415926/180.)/parameter_h_test) for i in range(len(DN))]
#Double Henyey-Greenstein Function.
part3 = [(1+parameter_c_test)*(1-parameter_b_test**2) / (2* (1+2*parameter_b_test*math.cos(Phase[i]*3.1415926/180.)+parameter_b_test**2)**(1.5) ) + (1-parameter_c_test)*(1-parameter_b_test**2) / (2* (1-2*parameter_b_test*math.cos(Phase[i]*3.1415926/180.)+parameter_b_test**2)**(1.5) ) for i in range(len(DN))]
#The "H" functions. --eq. 2 in Hapke (2002)
part4 = [(1+2*mu0[i]) / (1+2*Vgamma*mu0[i]) * (1+2*mu[i]) / (1+2*Vgamma*mu[i]) for i in range(len(DN))]
#Eq 38 from Hapke (2002).
DN_model = [DN[i] - part1[i] * ( part2[i]*part3[i] + part4[i] - 1) * SurfaceRoughnessFunction for i in range(len(DN))]
MS = 0
for i in range(len(DN)):
MS += DN_model[i]**2
RMS = math.sqrt(MS/len(DN))
if RMS < lastbest:
parameter_w_best = parameter_w_test
parameter_b0_best= parameter_b0_test
parameter_h_best = parameter_h_test
parameter_b_best = parameter_b_test
parameter_c_best = parameter_c_test
lastbest = RMS
print(RMS, parameter_w_best, parameter_b0_best, parameter_h_best, parameter_b_best, parameter_c_best)
您可能应该使用 numpy 数组,因为对它们的操作通常更快。引用一篇文章,“随着数组大小接近 5,000,000,Numpy 变得比常规列表快 120 倍左右”。
因为这是一个5维搜索,你最终会得到很多重复的计算。避免这种情况的一种方法是使用记忆。您可以定义仅使用少数参数作为函数的重复子计算,然后为之前看到的参数存储这些函数中的每一个的结果。这将这些函数调用变成了极快的操作,从而可以节省一些执行时间。
幸运的是,python 使这变得非常简单:https://docs.python.org/3/library/functools.html#functools.lru_cache
*作为旁注,请务必使用分析器优化您的记忆 space - 否则您可能 运行 内存不足。
您还应该考虑使用 linux 分析您的代码,看看您是否获得了较小的性能改进:https://www.phoronix.com/scan.php?page=article&item=ryzen3-windows-linux&num=8
我还建议将 cPython 性能与 PyPy 进行比较。使用 PyPy,您可能可以使程序更快地达到 运行,但如果不比较特定用例的基准,则很难确定。
https://speed.pypy.org/
(注意:我在本网站上阅读了其他一些相关问题,但我认为它们不适用于我的具体情况;如果适用,我深表歉意。)
问题:我正在尝试推导行星体上特定位置的光度参数。这是一个极其非线性的问题,光度参数以非线性方式相互作用。我已将问题减少到 5 个最重要的参数,我与该领域的专家进行了交谈,他们告诉我,是的,没有办法分析求解这些方程,他们所做的只是参数-space 扫描。思路是先有一个宽泛的参数space,然后随着参数的收敛对它进行细化(这句话还没有写到下面的代码片段中)
因为我需要搜索 5D 参数 space,所以我将其编写为五个嵌套的 for() 循环。每次最内层循环迭代时,它都会根据实际数据检查结果,确定 RMS 是否小于先前的最佳值,如果是,则保存这些参数。
问:有什么办法可以加快速度吗?它慢得令人痛苦,比我之前编程的“Igor Pro”语言还慢(但 Igor 不能被 Python 调用,我的所有其他代码都在 Python 中)。我想不出一种方法来“Pythonize”事物,因为它对每个唯一参数值都有相当多的数学运算,并且因为它在每次迭代时都进行测试。我能想到的另一个“唯一”的事情是在每次迭代中进一步粗化参数 space 这样,例如,它不需要在下面进行 10^5 次迭代,但我 运行如果我这样做,可能会丢失敏感的局部最小值。
#Iteration 1, start with a wide aray of possible parameters.
parameter_w = 0.0
parameter_b0 = 0.0
parameter_h = 1E-10
parameter_b = 0.0
parameter_c = 0.0
iterator_w = 0.10
iterator_b0 = 0.25
iterator_h = 0.10
iterator_b = 0.10
iterator_c = 0.10
maxits_w = 10+1
maxits_b0 = 10+1
maxits_h = 10+1
maxits_b = 10+1
maxits_c = 10+1
lastbest = 9999
#Do the parameter space search.
for counter_c in range(0,maxits_c):
parameter_c_test = parameter_c + iterator_c*counter_c
for counter_b in range(0,maxits_b):
parameter_b_test = parameter_b + iterator_b*counter_b
for counter_h in range(0,maxits_h):
parameter_h_test = parameter_h + iterator_h*counter_h
for counter_b0 in range(0,maxits_b0):
parameter_b0_test = parameter_b0 + iterator_b0*counter_b0
for counter_w in range(0,maxits_w):
parameter_w_test = parameter_w + iterator_w*counter_w
Vgamma = math.sqrt(1-parameter_w_test)
SurfaceRoughnessFunction = 1 #equals 1 if theta, the roughness parameter, is 0
#Modified incidence and emission angles (above eq. 2 in Hapke (2002)).
mu0 = [math.cos(Incidence[i]*3.1415926/180.) for i in range(len(Incidence))]
mu = [math.cos(Emission[i] *3.1415926/180.) for i in range(len(Emission ))]
#Coefficient ... normalization factor?
part1 = [parameter_w_test / 4. * mu0[i] / (mu0[i] + mu[i]) for i in range(len(DN))]
#Shadow-Hiding Opposition Effect (SHOE) --eq 28 with 29 from Hapke (2002)
part2 = [1 + parameter_b0_test / (1 + math.tan(Phase[i]/2.*3.1415926/180.)/parameter_h_test) for i in range(len(DN))]
#Double Henyey-Greenstein Function.
part3 = [(1+parameter_c_test)*(1-parameter_b_test**2) / (2* (1+2*parameter_b_test*math.cos(Phase[i]*3.1415926/180.)+parameter_b_test**2)**(1.5) ) + (1-parameter_c_test)*(1-parameter_b_test**2) / (2* (1-2*parameter_b_test*math.cos(Phase[i]*3.1415926/180.)+parameter_b_test**2)**(1.5) ) for i in range(len(DN))]
#The "H" functions. --eq. 2 in Hapke (2002)
part4 = [(1+2*mu0[i]) / (1+2*Vgamma*mu0[i]) * (1+2*mu[i]) / (1+2*Vgamma*mu[i]) for i in range(len(DN))]
#Eq 38 from Hapke (2002).
DN_model = [DN[i] - part1[i] * ( part2[i]*part3[i] + part4[i] - 1) * SurfaceRoughnessFunction for i in range(len(DN))]
MS = 0
for i in range(len(DN)):
MS += DN_model[i]**2
RMS = math.sqrt(MS/len(DN))
if RMS < lastbest:
parameter_w_best = parameter_w_test
parameter_b0_best= parameter_b0_test
parameter_h_best = parameter_h_test
parameter_b_best = parameter_b_test
parameter_c_best = parameter_c_test
lastbest = RMS
print(RMS, parameter_w_best, parameter_b0_best, parameter_h_best, parameter_b_best, parameter_c_best)
您可能应该使用 numpy 数组,因为对它们的操作通常更快。引用一篇文章,“随着数组大小接近 5,000,000,Numpy 变得比常规列表快 120 倍左右”。
因为这是一个5维搜索,你最终会得到很多重复的计算。避免这种情况的一种方法是使用记忆。您可以定义仅使用少数参数作为函数的重复子计算,然后为之前看到的参数存储这些函数中的每一个的结果。这将这些函数调用变成了极快的操作,从而可以节省一些执行时间。 幸运的是,python 使这变得非常简单:https://docs.python.org/3/library/functools.html#functools.lru_cache
*作为旁注,请务必使用分析器优化您的记忆 space - 否则您可能 运行 内存不足。
您还应该考虑使用 linux 分析您的代码,看看您是否获得了较小的性能改进:https://www.phoronix.com/scan.php?page=article&item=ryzen3-windows-linux&num=8
我还建议将 cPython 性能与 PyPy 进行比较。使用 PyPy,您可能可以使程序更快地达到 运行,但如果不比较特定用例的基准,则很难确定。 https://speed.pypy.org/