6

次のクラスを検討してください。

class SquareErrorDistance(object):
    def __init__(self, dataSample):
        variance = var(list(dataSample))
        if variance == 0:
            self._norm = 1.0
        else:
            self._norm = 1.0 / (2 * variance)

    def __call__(self, u, v): # u and v are floats
        return (u - v) ** 2 * self._norm

これを使用して、ベクトルの 2 つの要素間の距離を計算します。基本的に、この距離測定を使用するベクトルの次元ごとに、そのクラスの 1 つのインスタンスを作成します (他の距離測定を使用する次元があります)。プロファイリングにより、__call__このクラスの関数が私の knn 実装の実行時間の 90% を占めていることが明らかになりました (誰が考えたでしょう)。これを高速化する純粋な Python の方法はないと思いますが、C で実装した場合はどうでしょうか。

上記の式を使用してランダム値の距離を計算する単純な C プログラムを実行すると、Python より桁違いに高速になります。そこで、 ctypesを使用して、計算を行う C 関数を呼び出してみましたが、結果のコードがはるかに遅くなるため、パラメータと戻り値の変換にコストがかかるようです。

もちろん、knn 全体を C で実装してそれを呼び出すこともできますが、問題は、前述したように、ベクトルの一部の次元に対して異なる距離関数を使用しており、これらを C に変換するのは手間がかかりすぎることです。

では、私の代替手段は何ですか?Python C-APIを使用して C 関数を作成すると、オーバーヘッドがなくなりますか? この計算を高速化する他の方法はありますか?

4

2 に答える 2

2

次のcythonコード(の最初の行__init__が異なることに気づきました。わからないため、ランダムなものに置き換えましたvar。とにかく問題ではないため__call__、ボトルネックであると述べました):

cdef class SquareErrorDistance:
    cdef double _norm

    def __init__(self, dataSample):
        variance = round(sum(dataSample)/len(dataSample))
        if variance == 0:
            self._norm = 1.0
        else:
            self._norm = 1.0 / (2 * variance)

    def __call__(self, double u, double v): # u and v are floats
        return (u - v) ** 2 * self._norm

単純な setup.py (ファイル名を変更したドキュメントの例timeit) を介してコンパイルすると、単純な考案されたベンチマークで、同等の純粋な python よりもほぼ 20 倍優れたパフォーマンスを発揮します。cdef変更されたのは、_normフィールドと__call__パラメーターののみであることに注意してください。これはかなり印象的だと思います。

于 2010-11-21T18:49:13.660 に答える
0

これはおそらくあまり役​​に立ちませんが、ネストされた関数を使用して書き直すことができます。

def SquareErrorDistance(dataSample):
    variance = var(list(dataSample))
    if variance == 0:
        def f(u, v):
            x = u - v
            return x * x
    else:
        norm = 1.0 / (2 * variance)
        def f(u, v):
            x = u - v
            return x * x * norm
    return f
于 2010-11-21T18:28:58.900 に答える