【问题标题】:Trying to define a cython method results in `cdef statement not allowed here` error尝试定义 cython 方法会导致 `cdef statement not allowed here` 错误
【发布时间】:2020-06-01 06:47:24
【问题描述】:

我正在尝试使用 EasyCython 模块将纯 Python 模块转换为与 Cython 兼容的模块。
然而问题是,在尝试执行 EasyCython 时,它会失败并出现错误:

G:\Proc\python\FV>easycython F_V.pyx
Compiling F_V.pyx because it changed.
[1/1] Cythonizing F_V.pyx
C:\Users\Rika\Anaconda3\Lib\site-packages\Cython\Compiler\Main.py:369: FutureWarning: Cython directive 'language_level' not set, using 2 for now (Py2). This will change in a later release! File: G:\Proc\python\FV\F_V.pyx
  tree = Parsing.p_module(s, pxd, full_module_name)

Error compiling Cython file:
------------------------------------------------------------
...
    # or the network would mistake some one else for someother people! (for example someone has a profile image
    # while the other doesnt, when confronted with the profile image, the network most likely find more features
    # from the person that has already a profile image of him in the fbank (unless the change is noticeable
    # this really can be a major issue)))
    # @benchmark
    cpdef _identify_id(self, input_img_embedding, list embedding_list, bint short_circut=True, bint accumulate_score=False):
         ^
------------------------------------------------------------

F_V.pyx:929:10: cdef statement not allowed here
Traceback (most recent call last):
  File "C:\Users\Rika\Anaconda3\Lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Users\Rika\Anaconda3\Lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\Rika\Anaconda3\Scripts\easycython.exe\__main__.py", line 7, in <module>
  File "C:\Users\Rika\Anaconda3\Lib\site-packages\begin\main.py", line 54, in start
    collector=self._collector)
  File "C:\Users\Rika\Anaconda3\Lib\site-packages\begin\cmdline.py", line 253, in apply_options
    return_value = call_function(func, signature(ext), opts)
  File "C:\Users\Rika\Anaconda3\Lib\site-packages\begin\cmdline.py", line 236, in call_function
    return func(*pargs, **kwargs)
  File "C:\Users\Rika\Anaconda3\Lib\site-packages\easycython\easycython.py", line 77, in main
    ext_modules = cythonize(ext_modules),
  File "C:\Users\Rika\Anaconda3\Lib\site-packages\Cython\Build\Dependencies.py", line 1102, in cythonize
    cythonize_one(*args)
  File "C:\Users\Rika\Anaconda3\Lib\site-packages\Cython\Build\Dependencies.py", line 1225, in cythonize_one
    raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: F_V.pyx

以下是使用 cython 类型注释的两种方法。

#@benchmark
cpdef _identify_id(self, input_img_embedding, list embedding_list, bint short_circut=True, bint accumulate_score=False):
    # These are the underlying types for the arguments and local variables used here
    # input_img_embedding_type: <class 'torch.Tensor'> shape:torch.Size([1, 512])
    # feature1 type: <class 'torch.Tensor'> shape: torch.Size([1, 512])
    # x1 type: <class 'torch.Tensor'> shape: torch.Size([1, 512])
    # cosine type: <class 'numpy.float32'> shape: ()
    # np.clip(cosine) type: <class 'numpy.float64'> shape: ()
    #
    cdef float min_theta = 1000.0
    cdef float total_theta = 0.0
    cdef char* id_name = 'None' #None
    for (name, feature1) in embedding_list:
        id_name = name
        x1 = feature1 / np.linalg.norm(feature1)
        cosine = np.dot(input_img_embedding.squeeze(0), x1.squeeze(0))
        cdef float cosine = np.clip(cosine, -1.0, 1.0)
        cdef float theta = math.acos(cosine)
        cdef float theta = theta * 180 / math.pi

        if short_circut:
            if theta < self._threshold:
                return id_name, theta

        if theta < min_theta:
            min_theta = theta

        total_theta += theta

    # the elses from now on are for debugging purposes
    if not short_circut and not accumulate_score:
        if min_theta < self._threshold:
            return id_name, min_theta
        else:
            return 'unknown', min_theta

    if accumulate_score:
        final_score = total_theta/len(embedding_list)
        if final_score < self._threshold:
            return id_name, final_score
        else:
            return 'unknown', final_score

    return 'unknown', theta # min_theta

#@benchmark
cpdef _check_in_fbank(self, img):
    """Checks whether a given image is represented in the face bank.

    Arguments:
        img {torch.tensor} -- input image to be verified

    Returns:
        tuple(name, theta)
    """
        # These are the underlying python types
        # img type: <class 'torch.Tensor'> shape: torch.Size([3, 112, 112])
        # feature0 type: <class 'torch.Tensor'> shape: torch.Size([1, 512])
        # x0 type: <class 'torch.Tensor'> shape: torch.Size([1, 512])

    with Benchmark_Block("model frwd took: "):
         feature0 = self.model(img.unsqueeze(0)).cpu().detach()
         x0 = feature0 / np.linalg.norm(feature0)

    cdef list lst = []
    for img_list in self._fbank_embeddings:
        cdef tuple f = self._identify_id(x0, img_list, short_circut=self.short_circut, accumulate_score=self.accumulate_score)
        lst.append(f)

    cdef tuple min_val  = min(lst, key=lambda t: t[1])
    print(f'lst of returned results : {lst}. The minimum is: {min_val} in {len(self._fbank_embeddings)} enteries')
    return min_val

我在这里缺少什么?

【问题讨论】:

  • 装饰器(我标记的副本的主题)是一个问题 - cdef 函数不允许使用它。 int* 是另一个问题 - def 函数不允许使用它(因为它没有等效的 Python 类型)。 cpdef 具有 cdefdef 函数的所有限制(有关说明,请参阅 stackoverflow.com/questions/28362009/…
  • 话虽如此 - 您显示的代码与您显示的错误不匹配,所以谁知道。如果这个+副本不能帮助您解决它,那么我很乐意重新打开它
  • 谢谢,但如果我删除所有 int* 以及装饰器,我仍然会遇到同样的错误。我更新了错误日志,之前忘记更新了。
  • 恐怕我现在不知道,但我已经删除了重复的标志
  • 谢谢,我很欣赏它。同时我添加了一些关于参数类型和其他局部变量的更多信息,所以它可以显示我做错了什么。

标签: python cython


【解决方案1】:

问题恰好是当使用 python class 时,也应该使用cdef。这种类称为Extension Type
来自 Cython 文档:

... Cython 扩展类型定义看起来很像 Python 类 定义。在其中,您使用 def 语句来定义方法 可以从 Python 代码中调用。您甚至可以定义许多 init() 等特殊方法,就像在 Python 中一样。

注意:

这样做之后,为了能够在 Python 中使用我的类,我必须从它继承,而是使用继承的类。那是我必须做的:

cdef class MyClass():
    def __init__(self, args)
        ... 

    cpdef func1(self,...):
        ...

    cpdef func2(self, ....):
        ...
    ...

class MyClass2(MyClass):
    def __init__(self, args):
       super().__init__(args)
       pass 

在你的客户端代码中:

# use MyClass2 from now on
obj = MyClass2(args)
...

【讨论】:

  • 我要补充的一件事是,您可能并不真的需要使用cpdef 函数。 Cython 仍然使用适当类型的变量优化 def 函数。 cpdef 所做的只是让从 C 调用稍微快一些。由于这些看起来像大型函数,我怀疑调用开销是否很大。不管怎样,很高兴你把它整理好了。
  • @DavidW,非常感谢您的帮助。
猜你喜欢
  • 2016-06-23
  • 2021-10-19
  • 2019-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多