Scikit-learn: Gunakan python logging untuk melaporkan kemajuan konvergensi info levelnya untuk tugas yang berjalan lama

Dibuat pada 12 Feb 2011  ·  31Komentar  ·  Sumber: scikit-learn/scikit-learn

Ini adalah proposal untuk menggunakan modul logging python alih-alih menggunakan flag stdout dan verbose di API model.

Menggunakan modul logging akan memudahkan pengguna untuk mengontrol verbositas scikit menggunakan antarmuka konfigurasi tunggal dan terdokumentasi dengan baik serta API logging.

http://docs.python.org/library/logging.html

New Feature

Komentar yang paling membantu

Opsi kelima adalah menghapus flag verbose, menggunakan logging di mana saja, dan membiarkan pengguna menyesuaikan verbositas melalui logging API. Untuk itulah logging dirancang.

Saya akan mendukung penghapusan verbose, karena saya menemukan konfigurasi per-estimator
membuat frustrasi, dan nilai numerik verbose sewenang-wenang, buruk
didokumentasikan,

Saya pikir menyingkirkan verbose dan menggunakan level logging akan sangat bagus. Satu-satunya downside yang saya lihat adalah itu akan membuat logging sedikit kurang dapat ditemukan.

Semua 31 komentar

Pekerjaan telah dimulai untuk ini di https://github.com/GaelVaroquaux/scikit-learn/tree/progress_logger

Apa yang masih harus dilakukan kemungkinan besar adalah pekerjaan mekanis.

Ada juga pekerjaan di modul Gradient Boosting yang baru.

Logging sebenarnya tidak mudah digunakan sama sekali, menurut pengalaman saya, jadi -1 dalam hal ini.

Apakah ada yang mengerjakan ini?

Bagaimana kalau kita menambahkan logger yang secara default dicetak ke STDOUT? Itu seharusnya cukup sederhana, bukan?

Masalah ini telah terbuka sejak 2011 dan jadi saya bertanya-tanya apakah ini akan diperbaiki. Saya mengalami masalah ini dengan RFECV (https://github.com/scikit-learn/scikit-learn/blob/a24c8b464d094d2c468a16ea9f8bf8d42d949f84/sklearn/feature_selection/rfe.py#L273). Saya ingin mencetak kemajuan tetapi pencetakan verbose default mencetak terlalu banyak pesan. Saya tidak ingin menambal monyet sys.stdout untuk membuat ini berfungsi dan mengesampingkan logger akan menjadi solusi yang sederhana dan bersih.

Ada keluaran lain di sklearn seperti #8105 dan #10973 yang akan mendapat manfaat dari logging nyata di sklearn. Secara keseluruhan, saya pikir logging akan menjadi tambahan yang bagus untuk sklearn.

Anda dipersilakan untuk mengerjakannya. mungkin sistem panggilan balik lebih baik daripada
masuk

Saya agak sibuk sekarang tetapi saya mendukung logging yang dapat disesuaikan di sklean dalam bentuk apa pun (walaupun saya lebih suka logging python standar).

Apakah ada diskusi tentang apa arti verbose=True ketika scikit-learn mulai menggunakan logging? Kami sedang menangani ini sedikit di dask-ml: https://github.com/dask/dask-ml/pull/528.

Mengingat bahwa perpustakaan tidak seharusnya melakukan konfigurasi logging, terserah kepada pengguna untuk mengonfigurasi "aplikasi" mereka (yang mungkin hanya berupa skrip atau sesi interaktif) untuk mencatat sesuatu dengan tepat. Ini tidak selalu mudah untuk dilakukan dengan benar.

Proposal saya di https://github.com/dask/dask-ml/pull/528 adalah untuk verbose=True berarti "konfigurasi logging untuk saya sementara". Anda dapat menggunakan manajer konteks untuk mengonfigurasi logging , dan scikit-learn ingin memastikan bahwa pesan level INFO dicetak ke stdout agar sesuai dengan perilaku saat ini.

Apakah untuk sementara juga berarti bahwa pengaturan pawang khusus untuk itu
jenis penduga atau penaksir?

Proposal saya di dask/dask-ml#528 adalah untuk verbose=True yang berarti "konfigurasi logging untuk saya untuk sementara".

Ini sepertinya keseimbangan yang bagus. Menggunakan modul logging tidak terlalu ramah pengguna. "Peretasan" lain adalah menggunakan info secara default, tetapi ketika pengguna menetapkan verbose=True log dapat dinaikkan menjadi warning . Ini akan berhasil karena peringatan ditampilkan secara default.

Saya pikir mengubah level pesan tertentu ketika pengguna meminta lebih banyak
verbositas persis kebalikan dari bagaimana modul logging dimaksudkan
kerja. Tetapi pawang lokal dapat berubah dari peringatan menjadi info menjadi debug
level saat streaming saat verbose meningkat

Komentar @ jnothman cocok dengan pemikiran saya. scikit-learn akan selalu memancarkan pesan, dan kata kunci verbose mengontrol level logger dan penangan.

Tetapi pawang lokal dapat berubah dari peringatan menjadi info menjadi debug
level saat streaming saat verbose meningkat

Oke, mari kita pergi dengan ini. Saat ini level logging adalah https://docs.python.org/3/library/logging.html#logging -levels Secara default, kita dapat menggunakan INFO , yang tidak memancarkan secara default. Ketika verbose=1 , kami memiliki handler change info -> warning, dan debug -> info. Ketika kita menetapkan verbose>=2 , kita masih memiliki info -> warning tetapi juga memiliki debug -> warning, dan estimator dapat menginterpretasikan verbose>=2 berarti "memancarkan lebih banyak pesan debug seiring dengan peningkatan verbose". Makna ini dapat berbeda antara estimator yang berbeda.

Bagaimana menurutmu?

Hai, saya sangat tertarik dengan masalah ini. Saya memiliki beberapa pengalaman dengan logging dan ingin membantu menerapkan peningkatan di sini jika beberapa konsensus dan rencana tercapai.

mungkin membantu untuk merangkum ide-ide yang disebutkan di sini:

  1. gunakan pola panggilan balik
  2. ubah level pesan, tergantung pada verbose
    if verbose:
        logger.debug(message)
    else:
        logger.info(message)
  1. ubah level logger , tergantung pada verbose
    if verbose:
        logger.selLevel("DEBUG")
  1. tambahkan handler dengan level DEBUG , tergantung pada verbose
    if verbose:
        verbose_handler = logging.StreamHandler()
        verbose_handler.setLevel("DEBUG")
        logger.addHandler(verbose_handler)

Pendapat saya tentang opsi ini:

Opsi 1 atau opsi 4 mungkin yang terbaik.

  • Opsi 1 (panggilan balik) bagus karena paling agnostik (orang dapat mencatat sesuatu sesuka mereka). Tapi itu mungkin kurang fleksibel dari perspektif perpesanan / penangkapan negara. (Bukankah panggilan balik hanya dipanggil sekali atau sekali per beberapa iterasi loop internal?)
  • Opsi 2, seperti yang dibahas di sini, saya pikir menyalahgunakan perpustakaan logging
  • Opsi 3 berfungsi tetapi, saya pikir, mengalahkan sebagian tujuan penggunaan perpustakaan logging . Jika sklearn menggunakan logging , maka pengguna dapat menyesuaikan verbositas melalui logging itu sendiri, misalnya import logging; logging.getLogger("sklearn").setLevel("DEBUG") .
  • Opsi 4 mungkin paling kanonik. Dokumen menyarankan _not_ membuat penangan dalam kode perpustakaan selain NullHandler s, tapi saya pikir di sini masuk akal, mengingat sklearn memiliki verbose flag. Dalam hal ini, pencetakan log adalah "fitur" perpustakaan.

Opsi kelima adalah menghapus flag verbose , menggunakan logging mana saja, dan membiarkan pengguna menyesuaikan verbositas melalui logging API. Untuk itulah logging dirancang.

@grisaitis terima kasih! Lihat juga diskusi terkait yang lebih baru di https://github.com/scikit-learn/scikit-learn/issues/17439 dan https://github.com/scikit-learn/scikit-learn/pull/16925#issuecomment -638956487 (tentang panggilan balik). Bantuan Anda akan sangat dihargai, masalah utamanya adalah kami belum memutuskan pendekatan apa yang terbaik :)

Saya akan mendukung penghapusan verbose, karena saya menemukan konfigurasi per-estimator
membuat frustrasi, dan nilai numerik verbose sewenang-wenang, buruk
didokumentasikan, dll. Konfigurasi per kelas akan ditangani dengan memiliki
beberapa nama logger scikit-learn.

Opsi kelima adalah menghapus flag verbose, menggunakan logging di mana saja, dan membiarkan pengguna menyesuaikan verbositas melalui logging API. Untuk itulah logging dirancang.

Saya akan mendukung penghapusan verbose, karena saya menemukan konfigurasi per-estimator
membuat frustrasi, dan nilai numerik verbose sewenang-wenang, buruk
didokumentasikan,

Saya pikir menyingkirkan verbose dan menggunakan level logging akan sangat bagus. Satu-satunya downside yang saya lihat adalah itu akan membuat logging sedikit kurang dapat ditemukan.

Juga, satu hal yang disediakan logging adalah Anda dapat melampirkan informasi tambahan ke setiap pesan logging, bukan hanya string. Jadi seluruh dict dari hal-hal yang berguna. Jadi jika Anda ingin melaporkan kehilangan selama pembelajaran, Anda dapat melakukannya dan menyimpan nilai numerik. Terlebih lagi, Anda berdua dapat menyimpan nilai numerik sebagai angka dan menggunakannya untuk memformat string yang mudah digunakan, seperti: logger.debug("Current loss: %(loss)s", {'loss': loss}) . Saya menemukan itu sangat berguna secara umum dan akan senang jika sklearn mengekspos itu juga.

Saya pikir memiliki modul atau pencatat level penaksir agak berlebihan untuk saat ini dan kita harus mulai dengan sesuatu yang sederhana yang memungkinkan kita untuk memperluasnya nanti.
Selain itu, apa pun yang kami lakukan harus memungkinkan pengguna mereproduksi perilaku saat ini dengan cukup mudah.

Bagaimana logging dan joblib berinteraksi? Level logging tidak dipertahankan (seperti yang saya kira):

import logging
logger = logging.getLogger('sklearn')
logger.setLevel(2)

def get_level():
    another_logger = logging.getLogger('sklearn')
    return another_logger.level

results = Parallel(n_jobs=2)(
    delayed(get_level)() for _ in range(2)
)
results

```
[0, 0]

But that's probably not needed, since this works:
```python
import logging
import sys
logger = logging.getLogger('sklearn')
logger.setLevel(1)

handler = logging.StreamHandler(sys.stdout)
logger.addHandler(handler)


def log_some():
    another_logger = logging.getLogger('sklearn')
    another_logger.critical("log something")

results = Parallel(n_jobs=2)(
    delayed(log_some)() for _ in range(2)
)

Sejujurnya, saya tidak sepenuhnya yakin bagaimana ini bekerja.

stdout dan stderr tidak muncul di jupyter btw.

Impian saya: bisa mendapatkan perkiraan perilaku saat ini dengan satu baris, tetapi juga dapat dengan mudah menggunakan bilah kemajuan atau konvergensi plot.

re verbose: mungkin lebih bersih untuk tidak menggunakan verbose, tetapi mencela verbose dan tidak memiliki logging tingkat estimator akan membuatnya sedikit lebih sulit untuk mencatat satu estimator tetapi tidak yang lain. Saya pikir tidak apa-apa meminta pengguna memfilter pesan.

hai semua, terima kasih atas balasan dan info yang ramah. saya membaca masalah lain dan memiliki beberapa pemikiran.

joblib akan rumit. saya punya beberapa ide sekalipun.

@amueller itu sangat aneh. saya mereproduksi contoh Anda. hal melakukan pekerjaan dengan concurrent.futures.ProcessPoolExecutor , yang saya pikir joblib penggunaan ...

sepertinya joblib sedang menghapus status di logging . adakah pakar joblib punya ide tentang apa yang mungkin terjadi?

import concurrent.futures
import logging
import os

logger = logging.getLogger("demo🙂")
logger.setLevel("DEBUG")

handler = logging.StreamHandler()
handler.setFormatter(
    logging.Formatter("%(process)d (%(processName)s) %(levelname)s:%(name)s:%(message)s")
)
logger.addHandler(handler)

def get_logger_info(_=None):
    another_logger = logging.getLogger("demo🙂")
    print(os.getpid(), "another_logger:", another_logger, another_logger.handlers)
    another_logger.warning(f"hello from {os.getpid()}")
    return another_logger

if __name__ == "__main__":
    print(get_logger_info())

    print()
    print("concurrent.futures demo...")
    with concurrent.futures.ProcessPoolExecutor(2) as executor:
        results = executor.map(get_logger_info, range(2))
        print(list(results))

    print()
    print("joblib demo (<strong i="17">@amueller</strong>'s example #2)...")
    from joblib import Parallel, delayed
    results = Parallel(n_jobs=2)(delayed(get_logger_info)() for _ in range(2))
    print(results)

keluaran mana

19817 another_logger: <Logger demo🙂 (DEBUG)> [<StreamHandler <stderr> (NOTSET)>]
19817 (MainProcess) WARNING:demo🙂:hello from 19817
<Logger demo🙂 (DEBUG)>

concurrent.futures demo...
19819 another_logger: <Logger demo🙂 (DEBUG)> [<StreamHandler <stderr> (NOTSET)>]
19819 (SpawnProcess-1) WARNING:demo🙂:hello from 19819
19819 another_logger: <Logger demo🙂 (DEBUG)> [<StreamHandler <stderr> (NOTSET)>]
19819 (SpawnProcess-1) WARNING:demo🙂:hello from 19819
[<Logger demo🙂 (DEBUG)>, <Logger demo🙂 (DEBUG)>]

joblib demo (<strong i="21">@amueller</strong>'s example #2)...
19823 another_logger: <Logger demo🙂 (WARNING)> []
hello from 19823
19823 another_logger: <Logger demo🙂 (WARNING)> []
hello from 19823
[<Logger demo🙂 (DEBUG)>, <Logger demo🙂 (DEBUG)>]

Saya pikir Anda harus mengonfigurasi proses joblib memunculkan untuk mengirim pesan logging ke logger utama dalam proses utama. Kemudian seseorang dapat mengontrol logging dalam proses utama saja. Sesuatu seperti ini atau ini . Jadi ada sink dan sumber antrian logging dan Anda dapat mengikatnya bersama-sama. Kami menggunakan ini di cluster kami, untuk mengirim semua logging dari semua pekerja di semua mesin ke lokasi pusat, untuk menunjukkannya di komputer pengguna.

@mitar saya setuju, saya pikir itu mungkin yang terbaik. (tidak harus antrian berbasis jaringan, tetapi antrian komunikasi antar-proses)

saya sebenarnya mengkodekan contoh menggunakan logging 's QueueHandler / QueueListener sekarang, untuk menguji dengan joblib dan concurrent.futures . akan menyusul di sini.

juga suka saran Anda yang lain:

Juga, satu hal yang disediakan logging adalah Anda dapat melampirkan informasi tambahan ke setiap pesan logging, bukan hanya string. Jadi seluruh dict dari hal-hal yang berguna. Jadi jika Anda ingin melaporkan kehilangan selama pembelajaran, Anda dapat melakukannya dan menyimpan nilai numerik. Terlebih lagi, Anda berdua dapat menyimpan nilai numerik sebagai angka dan menggunakannya untuk memformat string yang mudah digunakan, seperti: logger.debug("Current loss: %(loss)s", {'loss': loss}) . Saya menemukan itu sangat berguna secara umum dan akan senang jika sklearn mengekspos itu juga.

saya menerapkan visualisasi pemodelan campuran gaussian menggunakan param extra dan kelas Handler khusus. bekerja sangat baik untuk melewati keadaan, dan membiarkan pengguna memutuskan bagaimana menangani keadaan.

juga kembali kekhasan joblib yang saya perhatikan di atas... saya akan menerimanya apa adanya dan di luar jangkauan. desain berbasis antrian akan menjadi yang paling fleksibel.

satu-satunya batasan menggunakan QueueHandler, yang dapat saya pikirkan, adalah bahwa setiap status extra ( logger.debug("message", extra={...} ) adalah bahwa extra dict harus serial untuk antrian. jadi tidak ada array numpy. :/ tidak bisa memikirkan masalah lain

saya sebenarnya sedang mengkodekan contoh menggunakan QueueHandler / QueueListener sekarang,

Ya, Anda harus selalu menggunakan pengendali antrian, karena Anda tidak pernah tahu kapan pengiriman melalui blok soket dan Anda tidak ingin memperlambat model karena pemblokiran logging.

Juga, Anda bahkan tidak perlu menggunakan extra . Saya pikir logger.debug("message %(foo)s", {'foo': 1, 'bar': 2}) hanya bekerja.

Ya, Anda harus selalu menggunakan pengendali antrian, karena Anda tidak pernah tahu kapan pengiriman melalui blok soket dan Anda tidak ingin memperlambat model karena pemblokiran logging.

untuk kasus joblib , jika kita mengimplementasikan QueueHandler / QueueListener , status apa yang harus kita berikan ke kumpulan proses? hanya queue , kan?

Juga, Anda bahkan tidak perlu menggunakan extra . Saya pikir logger.debug("message %(foo)s", {'foo': 1, 'bar': 2}) hanya berfungsi.

terima kasih ya. saya merasa berguna juga untuk mencatat status tanpa mengubahnya menjadi teks. misalnya memasukkan array numpy dalam extra , dan melakukan visualisasi data waktu nyata (dengan cara masuk visual) dengan pengendali pencatatan khusus di buku catatan jupyter. ini akan SUPER bagus dengan sklearn, dan sepertinya @rth telah melakukan pekerjaan serupa dengan panggilan balik.

untuk kasus joblib, jika kita mengimplementasikan QueueHandler / QueueListener, status apa yang harus kita berikan ke kumpulan proses? hanya antrian, kan?

Aku pikir begitu. Saya belum pernah menggunakan ini melebihi batas proses, tetapi tampaknya mereka memiliki dukungan terdokumentasi untuk multiprosesor, jadi itu juga harus bekerja dengan joblib. Saya menggunakan QueueHandler / QueueListener di dalam proses yang sama. Untuk memisahkan penulisan logging dari transportasi logging. Begitu juga QueueHandler -> QueueListener -> Kirim ke layanan logging pusat. Tapi dari dokumentasi sepertinya bisa bekerja melalui antrian multiprocessing.

saya merasa berguna juga untuk mencatat status tanpa mengubahnya menjadi teks

Ya. Apa yang saya katakan adalah bahwa Anda tidak harus menggunakan extra , tetapi hanya meneruskan dict secara langsung, dan kemudian Anda hanya menggunakan beberapa item dari dict itu untuk pemformatan pesan (perhatikan bahwa apa yang digunakan dalam format string diputuskan oleh pengguna perpustakaan sklearn, bukan oleh perpustakaan sklearn, seseorang selalu dapat mengonfigurasi yang Anda inginkan dalam memformat sesuatu yang tidak Anda harapkan, jadi apa yang sebenarnya dikonversi ke teks tidak benar-benar di bawah kendali sklearn). Semua nilai dalam extra juga dapat digunakan untuk pemformatan pesan. Jadi saya tidak yakin seberapa berguna extra itu. Tapi saya juga tidak mengatakan kita tidak boleh menggunakannya. Jauh lebih eksplisit apa muatan untuk string di sebelah kiri, dan apa yang ekstra. Jadi kita juga bisa menggunakan keduanya. Saya hanya ingin memastikan alternatif ini diketahui.

@grisaitis FYI jika Anda menyebutkan nama dalam komit, setiap kali Anda melakukan sesuatu dengan komit (seperti rebasing atau menggabungkan atau mendorongnya), orang tersebut mendapat email, jadi umumnya tidak disarankan ;)

Maaf soal itu Andreas! Itu memalukan... Saya hanya mencoba untuk membuat komit yang terdokumentasi dengan baik lol. Akan menghindari di masa depan.

Dalam repo itu saya menemukan bagaimana logging dapat bekerja dengan joblib dengan kombo QueueHandler / QueueListener. Tampaknya bekerja dengan baik.

Sebagai langkah pertama saya akan menerapkan logging dengan pendekatan itu di bagian sklearn di mana joblib digunakan. Mungkin salah satu model ansambel. Akan membuka PR baru.

untuk kasus joblib, jika kita mengimplementasikan QueueHandler / QueueListener,

Ya, sepertinya perlu untuk memulai/menghentikan utas pemantauan (di sini QueueListener ) baik jika menggunakan modul logging dan panggilan balik dalam kasus multiprosesor (contoh perkiraan panggilan balik dengan multiprosesor di https:// github.com/scikit-learn/scikit-learn/pull/16925#issuecomment-656184396)

Jadi saya pikir satu-satunya alasan mengapa apa yang saya lakukan di atas "berhasil" adalah karena itu dicetak ke stdout yang merupakan sumber daya bersama dan print adalah threadsafe di python3 atau sesuatu seperti itu?

Jadi saya pikir satu-satunya alasan mengapa apa yang saya lakukan di atas "berhasil" adalah karena itu dicetak ke stdout yang merupakan sumber daya bersama dan print adalah threadsafe di python3 atau sesuatu seperti itu?

Cetak tidak aman untuk benang. Mereka hanya mencetak ke deskriptor file yang sama. Mungkin berjalan untuk waktu yang lebih lama Anda akan melihat bahwa pesan kadang-kadang disisipkan dan garis tercampur.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat