web-dev-qa-db-ja.com

Python timeitモジュールで戻り値をキャプチャするにはどうすればよいですか?

Forループでsklearnを使用していくつかの機械学習アルゴリズムを実行していて、それぞれにかかる時間を確認したいと思います。問題は、値を返す必要があり、各アルゴリズムに非常に時間がかかるため、値を複数回実行する必要がないことです。 Pythonのtimeitモジュールまたはこのような関数を使用した同様のモジュールを使用して戻り値「clf」をキャプチャする方法はありますか...

def RandomForest(train_input, train_output):
    clf = ensemble.RandomForestClassifier(n_estimators=10)
    clf.fit(train_input, train_output)
    return clf

このような関数を呼び出すと

t = Timer(lambda : RandomForest(trainX,trainy))
print t.timeit(number=1)

P.S.また、後でマルチスレッドまたはマルチプロセッシングを実行したい場合があるため、グローバルな「clf」を設定したくありません。

26
Leon

問題は要約すると timeit._template_func 関数の戻り値を返さない:

def _template_func(setup, func):
    """Create a timer function. Used if the "statement" is a callable."""
    def inner(_it, _timer, _func=func):
        setup()
        _t0 = _timer()
        for _i in _it:
            _func()
        _t1 = _timer()
        return _t1 - _t0
    return inner

モンキーパッチを少し加えるだけで、timeitを思い通りに曲げることができます。

import timeit
import time

def _template_func(setup, func):
    """Create a timer function. Used if the "statement" is a callable."""
    def inner(_it, _timer, _func=func):
        setup()
        _t0 = _timer()
        for _i in _it:
            retval = _func()
        _t1 = _timer()
        return _t1 - _t0, retval
    return inner

timeit._template_func = _template_func

def foo():
    time.sleep(1)
    return 42

t = timeit.Timer(foo)
print(t.timeit(number=1))

戻り値

(1.0010340213775635, 42)

最初の値はtimeitの結果(秒単位)で、2番目の値は関数の戻り値です。

上記のモンキーパッチは、callabletimeit.Timerに渡された場合のtimeitの動作にのみ影響することに注意してください。文字列ステートメントを渡す場合は、(同様に)timeit.template文字列にモンキーパッチを適用する必要があります。

14
unutbu

Python 3.5の場合、 timeit.template の値をオーバーライドできます

timeit.template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        retval = {stmt}
    _t1 = _timer()
    return _t1 - _t0, retval
"""

nutbuの答え はpython 3.4で機能しますが、3.5では_template_func関数が削除されたように見えるため3.5では機能しません

11

おかしなことに、私も機械学習を行っており、同様の要件があります;-)

関数を書くことで、次のように解決しました。

  • 関数を実行します
  • 関数の名前とともに実行時間を出力します
  • 結果を返します

あなたが時間を計りたいとしましょう:

clf = RandomForest(train_input, train_output)

次に、次のことを行います。

clf = time_fn( RandomForest, train_input, train_output )

Stdoutは次のように表示されます。

mymodule.RandomForest: 0.421609s

Time_fnのコード:

import time

def time_fn( fn, *args, **kwargs ):
    start = time.clock()
    results = fn( *args, **kwargs )
    end = time.clock()
    fn_name = fn.__module__ + "." + fn.__name__
    print fn_name + ": " + str(end-start) + "s"
    return results
7
Hugh Perkins

私がよく理解していれば、python 3.5以降、コードのブロックでグローバルを定義しなくても、各タイマーインスタンスでグローバルを定義できます。並列化で同じ問題が発生するかどうかはわかりません。 。

私のアプローチは次のようになります。

clf = ensemble.RandomForestClassifier(n_estimators=10)
myGlobals = globals()
myGlobals.update({'clf'=clf})
t = Timer(stmt='clf.fit(trainX,trainy)', globals=myGlobals)
print(t.timeit(number=1))
print(clf)
2
Xavier

Python 3.Xの場合、私はこのアプローチを使用します:

# Redefining default Timer template to make 'timeit' return
#     test's execution timing and the function return value
new_template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        ret_val = {stmt}
    _t1 = _timer()
    return _t1 - _t0, ret_val
"""
timeit.template = new_template
0
Andrii Marusiak

私が使用しているアプローチは、timed関数の結果に実行時間を「追加」することです。そこで、「time」モジュールを使用して非常に単純なデコレータを作成します。

def timed(func):
    def func_wrapper(*args, **kwargs):
        import time
        s = time.clock()
        result = func(*args, **kwargs)
        e = time.clock()
        return result + (e-s,)
    return func_wrapper

そして、時間を計りたい関数にデコレータを使用します。

0
emireys