【发布时间】:2013-03-13 03:05:50
【问题描述】:
我通过向一些变量添加类型将 python 函数转换为 cython 等价物。但是,cython 函数产生的输出与原始 python 函数略有不同。
我在这篇文章中了解了造成这种差异的一些原因 Cython: unsigned int indices for numpy arrays gives different result 但即使我在这篇文章中学到了什么,我仍然无法让 cython 函数产生与 python 相同的结果。
所以我整理了 4 个函数来说明我尝试过的内容。有人可以帮助揭示为什么每个函数我得到的结果略有不同吗?以及如何获得一个与function1返回相同精确值的cython函数?我在下面做了一些cmets:
%%cython
import numpy as np
cimport numpy as np
def function1(response, max_loc):
x, y = int(max_loc[0]), int(max_loc[1])
tmp1 = (response[y,x+1] - response[y,x-1]) / 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
tmp2 = (response[y,x+1] - response[y,x-1])
tmp3 = 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
cpdef function2(np.ndarray[np.float32_t, ndim=2] response, np.ndarray[np.float64_t, ndim=1] max_loc):
cdef unsigned int x, y
x, y = int(max_loc[0]), int(max_loc[1])
tmp1 = (response[y,x+1] - response[y,x-1]) / 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
tmp2 = (response[y,x+1] - response[y,x-1])
tmp3 = 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
cpdef function3(np.ndarray[np.float32_t, ndim=2] response, np.ndarray[np.float64_t, ndim=1] max_loc):
cdef unsigned int x, y
x, y = int(max_loc[0]), int(max_loc[1])
cdef np.float32_t tmp1, tmp2, tmp3
cdef np.float32_t r1 =response[y,x+1]
cdef np.float32_t r2 =response[y,x-1]
cdef np.float32_t r3 =response[y,x]
cdef np.float32_t r4 =response[y,x-1]
cdef np.float32_t r5 =response[y,x+1]
tmp1 = (r1 - r2) / 2*(r3 - min(r4, r5))
tmp2 = (r1 - r2)
tmp3 = 2*(r3 - min(r4, r5))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
def function4(response, max_loc):
x, y = int(max_loc[0]), int(max_loc[1])
tmp1 = (float(response[y,x+1]) - response[y,x-1]) / 2*(float(response[y,x]) - min(response[y,x-1], response[y,x+1]))
tmp2 = (float(response[y,x+1]) - response[y,x-1])
tmp3 = 2*(float(response[y,x]) - min(response[y,x-1], response[y,x+1]))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
max_loc = np.asarray([ 15., 25.], dtype=np.float64)
response = np.zeros((49,49), dtype=np.float32)
x, y = int(max_loc[0]), int(max_loc[1])
response[y,x] = 0.959878861904
response[y,x-1] = 0.438348740339
response[y,x+1] = 0.753262758255
result1 = function1(response, max_loc)
result2 = function2(response, max_loc)
result3 = function3(response, max_loc)
result4 = function4(response, max_loc)
print result1
print result2
print result3
print result4
结果:
0.0821185777156 0.314914 1.04306030273
0.082118573023 0.314914017916 1.04306024313
0.0821185708046 0.314914017916 1.04306030273
0.082118573023 0.314914017916 1.04306024313
(0.082118577715618812, 0.31491402, 1.043060302734375)
(0.08211857302303427, 0.3149140179157257, 1.0430602431297302)
(0.08211857080459595, 0.3149140179157257, 1.043060302734375)
(0.082118573023034269, 0.31491401791572571, 1.0430602431297302)
function1 代表我在原始 python 函数中所做的操作。 tmp1 是结果。
function2 是我的第一个 cython 版本,它产生的结果略有不同。显然,如果响应数组使用类型化变量 unsigned int 或 int 进行索引,则即使数组的类型是 np.float32_t,结果也会被强制为 double(使用 PyFloat_FromDouble)。但是,如果数组使用 python int 进行索引,则使用函数 PyObject_GetItem,我得到 np.float32_t,这就是函数 1 中发生的情况。所以 function1 中的表达式是使用 np.float32_t 操作数计算的,而 function2 中的表达式是使用双精度计算的。 我得到的打印结果与 function1 中的略有不同。
function3 是我第二次尝试获得与 function1 相同的输出。在这里,我使用 unsigned int 索引来访问数组响应,但结果留在 np.float32_t 中间变量上,然后我在计算中使用它们。我得到的结果略有不同。显然打印语句将使用 PyFloat_FromDouble 所以它不能打印 np.float32_t。
然后我尝试更改 python 函数以匹配 cython 函数。 function4 尝试通过将每个表达式中的至少一个操作数转换为浮点数来实现这一点,因此其余操作数也被强制转换为 python 浮点数,这是 cython 中的双精度数,并且表达式是用双精度数计算的,如函数2。函数内部的print和function2一模一样,只是返回值略有不同?!
【问题讨论】:
-
最多相差10^-9(十亿分之一)。为什么您会惊讶于不同的实现会有如此大的差异? (在什么应用程序中会导致问题?)
-
通过 hex(),
print ", ".join([x.hex() for x in result4])打印浮点值。 -
函数 1 使用 Python
float(IEEE double) 值作为tmp1、tmp2和tmp3。函数 3 将它们显式声明为np.float32_t(IEEE 单一)。他们怎么可能返回同样的东西?让中间类型匹配,然后使用完全不同的最终类型就达不到目的了。 -
同时,您到底想完成什么?在几乎所有情况下,尽早而不是稍后切换到
float至少会给您带来同样好的结果,如果不是更好的话。除非您有特定的理由相信function2在实际值方面是“错误的”或“更糟的”(正如大卫罗宾逊指出的那样,这可能还不够准确,甚至没有意义),什么是问题? -
好的。好吧,目的是我首先开发 python 函数,当它们按照我的意愿运行时,我为它创建一个 cython 函数。所以我希望 cython 函数给我与 python 相同的结果。这些小的差异会随着我之后执行的其他操作累积起来,从而为我的系统产生不同的基准测试结果