Pertanyaan implementasi pythonic dari jaringan Bayesian untuk aplikasi tertentu


Inilah mengapa saya menanyakan pertanyaan ini: Tahun lalu saya membuat beberapa kode C ++ untuk menghitung probabilitas posterior untuk jenis model tertentu (dijelaskan oleh jaringan Bayesian). Model bekerja cukup baik dan beberapa orang lain mulai menggunakan perangkat lunak saya. Sekarang saya ingin meningkatkan model saya. Karena saya sudah mengkodekan algoritma inferensi yang sedikit berbeda untuk model baru, saya memutuskan untuk menggunakan python karena runtime tidak begitu penting dan python dapat membiarkan saya membuat kode yang lebih elegan dan mudah dikelola.

Biasanya dalam situasi ini saya akan mencari paket jaringan Bayesian yang ada di python, tetapi algoritma inferensi yang saya gunakan adalah milik saya sendiri, dan saya juga berpikir ini akan menjadi peluang besar untuk mempelajari lebih lanjut tentang desain yang baik dalam python.

Saya sudah menemukan modul python yang bagus untuk grafik jaringan (networkx), yang memungkinkan Anda untuk melampirkan kamus ke setiap node dan setiap tepi. Pada dasarnya, ini akan membiarkan saya memberikan node dan properti tepi.

Untuk jaringan tertentu dan data yang diamati, saya perlu menulis fungsi yang menghitung kemungkinan variabel yang tidak ditetapkan dalam model.

Misalnya, di jaringan "Asia" klasik (http://www.bayesserver.com/Resources/Images/AsiaNetwork.png), dengan keadaan "Hasil XRay" dan "Dyspnea" diketahui, saya perlu menulis fungsi untuk menghitung kemungkinan bahwa variabel lain memiliki nilai tertentu (menurut beberapa model).

Inilah pertanyaan pemrograman saya: Saya akan mencoba beberapa model, dan di masa depan mungkin saya akan mencoba model lain setelah itu. Misalnya, satu model mungkin terlihat persis seperti jaringan Asia. Dalam model lain, petunjuk mungkin ditambahkan dari "Visit to Asia" ke "Has Lung Cancer." Model lain mungkin menggunakan grafik diarahkan asli, tetapi model probabilitas untuk node "Dyspnea" yang diberikan node "Tuberkulosis atau Kanker" dan "Has Bronchitis" mungkin berbeda. Semua model ini akan menghitung kemungkinan dengan cara yang berbeda.

Semua model akan memiliki banyak tumpang tindih; misalnya, beberapa sisi masuk ke node "Atau" akan selalu membuat "0" jika semua input adalah "0" dan "1" sebaliknya. Tetapi beberapa model akan memiliki simpul yang mengambil nilai integer dalam beberapa rentang, sementara yang lain akan menjadi boolean.

Di masa lalu saya telah berjuang dengan cara memprogram hal-hal seperti ini. Saya tidak akan berbohong; sudah ada cukup banyak kode yang disalin dan ditempelkan dan terkadang saya perlu menyebarkan perubahan dalam satu metode ke beberapa file. Kali ini saya sangat ingin menghabiskan waktu untuk melakukan ini dengan cara yang benar.

Beberapa opsi:

  1. Saya sudah melakukan ini dengan cara yang benar. Kode dulu, ajukan pertanyaan nanti. Lebih cepat untuk menyalin dan menempel kode dan memiliki satu kelas untuk setiap model. Dunia adalah tempat yang gelap dan tidak terorganisir ...
  2. Setiap model adalah kelasnya sendiri, tetapi juga merupakan subkelas dari model BayesianNetwork umum. Model umum ini akan menggunakan beberapa fungsi yang akan ditimpa. Stroustrup akan bangga.
  3. Buat beberapa fungsi di kelas yang sama yang menghitung kemungkinan yang berbeda.
  4. Buat kode pustaka umum BayesianNetwork dan terapkan masalah inferensi saya sebagai grafik spesifik yang dibaca oleh pustaka ini. Node dan tepi harus diberikan properti seperti "Boolean" dan "OrFunction" yang, mengingat keadaan node induk yang diketahui, dapat digunakan untuk menghitung probabilitas hasil yang berbeda. String properti ini, seperti "OrFunction" bahkan dapat digunakan untuk mencari dan memanggil fungsi yang benar. Mungkin dalam beberapa tahun saya akan membuat sesuatu yang mirip dengan Mathematica versi 1988!

Terima kasih banyak atas bantuan Anda.

Memperbarui: Ide berorientasi objek banyak membantu di sini (setiap node memiliki set node predecessor tertentu dari subtipe simpul tertentu, dan setiap node memiliki fungsi kemungkinan yang menghitung kemungkinannya dari status hasil yang berbeda mengingat keadaan node pendahulunya, dll.). OOP FTW!


32
2017-09-24 01:53


asal


Jawaban:


Saya telah mengerjakan hal semacam ini di waktu luang saya cukup lama. Saya pikir saya berada di versi ketiga atau keempat dari masalah yang sama ini sekarang. Saya benar-benar bersiap-siap untuk merilis versi lain Fathom (https://github.com/davidrichards/fathom/wiki) dengan model bayesian dinamis yang disertakan dan lapisan ketekunan yang berbeda.

Karena saya sudah mencoba untuk membuat jawaban saya jelas, itu sudah cukup lama. Saya minta maaf untuk itu. Begini cara saya menyerang masalah, yang tampaknya menjawab beberapa pertanyaan Anda (secara tidak langsung):

Saya sudah mulai dengan perincian propagasi kepercayaan Judea Pearl dalam Jaringan Bayesian. Yaitu, ini adalah grafik dengan peluang sebelumnya (dukungan kausal) yang datang dari orang tua dan kemungkinan (dukungan diagnostik) yang berasal dari anak-anak. Dengan cara ini, kelas dasar hanyalah sebuah BeliefNode, seperti apa yang Anda gambarkan dengan node tambahan antara BeliefNodes, sebuah LinkMatrix. Dengan cara ini, saya secara eksplisit memilih jenis kemungkinan yang saya gunakan oleh jenis LinkMatrix yang saya gunakan. Itu membuatnya lebih mudah untuk menjelaskan apa yang dilakukan jaringan kepercayaan setelah itu serta membuat perhitungan lebih sederhana.

Setiap subclassing atau perubahan yang saya buat ke BeliefNode dasar adalah untuk mem-bining variabel kontinyu, daripada mengubah aturan propagasi atau asosiasi node.

Saya telah memutuskan untuk menyimpan semua data di dalam BeliefNode, dan hanya data tetap di LinkedMatrix. Ini berkaitan dengan memastikan bahwa saya menjaga pembaruan keyakinan bersih dengan aktivitas jaringan minimal. Ini berarti toko BeliefNode saya:

  • array referensi anak-anak, bersama dengan kemungkinan tersaring datang dari setiap anak dan matriks tautan yang melakukan penyaringan untuk anak itu
  • serangkaian referensi induk, bersama dengan peluang sebelumnya yang difilter yang berasal dari masing-masing induk dan matriks tautan yang melakukan pemfilteran untuk orang tua tersebut
  • kemungkinan gabungan dari node
  • gabungan peluang sebelumnya dari node
  • keyakinan yang dihitung, atau probabilitas posterior
  • daftar atribut yang dipesan yang semua kemungkinan dan mematuhi sebelumnya

The LinkMatrix dapat dibangun dengan sejumlah algoritma yang berbeda, tergantung pada sifat hubungan antara node. Semua model yang Anda jelaskan hanya akan menjadi kelas berbeda yang Anda pekerjakan. Mungkin hal yang paling mudah dilakukan adalah default ke or-gate, dan kemudian pilih cara lain untuk menangani LinkMatrix jika kita memiliki hubungan khusus antara node.

Saya menggunakan MongoDB untuk ketekunan dan caching. Saya mengakses data ini di dalam sebuah model yang ditampilkan untuk kecepatan dan akses tidak sinkron. Ini membuat jaringan cukup berkinerja sementara juga memiliki peluang untuk menjadi sangat besar jika perlu. Juga, karena saya menggunakan Mongo dengan cara ini, saya dapat dengan mudah membuat konteks baru untuk basis pengetahuan yang sama. Jadi, misalnya, jika saya memiliki pohon diagnostik, beberapa dukungan diagnostik untuk diagnosis akan berasal dari gejala dan tes pasien. Apa yang saya lakukan adalah menciptakan konteks untuk pasien itu dan kemudian menyebarkan keyakinan saya berdasarkan bukti dari pasien tertentu. Demikian pula, jika seorang dokter mengatakan bahwa seorang pasien kemungkinan mengalami dua atau lebih penyakit, maka saya dapat mengubah beberapa matriks tautan saya untuk menyebarkan pembaruan keyakinan secara berbeda.

Jika Anda tidak ingin menggunakan sesuatu seperti Mongo untuk sistem Anda, tetapi Anda berencana memiliki lebih dari satu konsumen yang bekerja di basis pengetahuan, Anda perlu mengadopsi semacam sistem caching untuk memastikan bahwa Anda bekerja baru node -update setiap saat.

Pekerjaan saya adalah sumber terbuka, jadi Anda dapat mengikuti jika Anda mau. Ini semua Ruby, jadi itu akan mirip dengan Python Anda, tetapi belum tentu pengganti. Satu hal yang saya suka tentang desain saya adalah bahwa semua informasi yang diperlukan bagi manusia untuk menginterpretasikan hasil dapat ditemukan di simpul itu sendiri, daripada di dalam kode. Ini dapat dilakukan dalam deskripsi kualitatif, atau dalam struktur jaringan.

Jadi, inilah beberapa perbedaan penting yang saya miliki dengan desain Anda:

  • Saya tidak menghitung model kemungkinan di dalam kelas, melainkan antara node, di dalam matriks tautan. Dengan cara ini, saya tidak memiliki masalah menggabungkan beberapa kemungkinan fungsi di dalam kelas yang sama. Saya juga tidak memiliki masalah satu model vs yang lain, saya hanya bisa menggunakan dua konteks yang berbeda untuk basis pengetahuan yang sama dan membandingkan hasilnya.
  • Saya menambahkan banyak transparansi dengan membuat keputusan manusia tampak nyata. Yaitu, jika saya memutuskan untuk menggunakan default atau gerbang antara dua node, saya tahu ketika saya menambahkan itu dan itu hanya keputusan default. Jika saya kembali lagi nanti dan mengubah matriks tautan dan menghitung ulang basis pengetahuan, saya memiliki catatan tentang mengapa saya melakukan itu, bukan hanya aplikasi yang memilih satu metode di atas yang lain. Anda dapat meminta konsumen Anda mencatat hal-hal semacam itu. Namun Anda memecahkan itu, mungkin ide yang baik untuk mendapatkan dialog langkah-bijaksana dari analis tentang mengapa mereka mengatur hal-hal di atas yang lain.
  • Saya mungkin lebih eksplisit tentang peluang dan kemungkinan sebelumnya. Saya tidak tahu pasti tentang itu, saya hanya melihat bahwa Anda menggunakan model yang berbeda untuk mengubah nomor kemungkinan Anda. Banyak dari apa yang saya katakan mungkin sama sekali tidak relevan jika model Anda untuk menghitung keyakinan posterior tidak rusak dengan cara ini. Saya memiliki manfaat untuk dapat membuat tiga langkah asynchronous yang dapat dipanggil dalam urutan apa pun: lewati mengubah kemungkinan jaringan, lulus mengubah peluang sebelumnya di jaringan, dan menghitung kembali keyakinan gabungan (probabilitas posterior) dari node itu sendiri. .

Satu peringatan besar: sebagian dari apa yang saya bicarakan belum dirilis. Saya bekerja pada hal-hal yang saya bicarakan sampai sekitar 2:00 pagi ini, jadi pasti saat ini dan pasti mendapatkan perhatian rutin dari saya, tetapi tidak semua tersedia untuk publik dulu. Karena ini adalah gairah saya, saya akan senang menjawab pertanyaan apa pun atau bekerja sama dalam proyek jika Anda mau.


21
2018-03-25 16:30



Itu Mozart / Oz3 sistem inferensi berbasis kendala memecahkan masalah yang sama: Anda menggambarkan masalah Anda dalam hal kendala pada variabel domain terbatas, propagator kendala dan distributor, fungsi biaya. Ketika tidak ada inferensi lebih mungkin tetapi masih ada variabel terikat, menggunakan fungsi biaya Anda untuk membagi ruang masalah pada variabel terikat yang kemungkinan besar mengurangi biaya pencarian: yaitu, jika X adalah antara [a, c] adalah variabel seperti itu , dan c (a <b <c) adalah titik yang paling mungkin untuk mengurangi biaya pencarian, Anda berakhir dengan dua contoh masalah di mana X berada di antara [a, b] dan, dalam contoh lain, X adalah antara [b, c ]. Mozart melakukan hal ini dengan agak elegan karena ia memantapkan pengikatan variabel sebagai objek kelas pertama (ini sangat berguna, karena Mozart merapat dan didistribusikan secara pervasively, untuk memindahkan ruang masalah ke node yang berbeda). Dalam implementasinya, saya menduga bahwa itu menggunakan strategi copy-on-write.

Anda pasti bisa mengimplementasikan skema copy-on-write dalam pustaka berbasis grafik (tip: numpy menggunakan berbagai strategi untuk meminimalkan penyalinan; jika Anda mendasarkan representasi grafik Anda di atasnya, Anda mungkin mendapatkan semantik copy-on-write gratis) dan mencapai tujuan Anda.


3
2018-03-25 07:08



Saya tidak terlalu akrab dengan Bayesian Networks, jadi saya harap yang berikut ini berguna:

Di masa lalu saya memiliki masalah yang mirip dengan Gaussian Process regressor, bukannya a penggolongan bayesian.

Saya akhirnya menggunakan warisan, yang bekerja dengan baik. Semua paremeter model-spesifik ditetapkan dengan konstruktor. Fungsi hitung () adalah virtual. Metode Cascading yang berbeda (misalnya metode jumlah yang menggabungkan sejumlah metode lain) juga bekerja dengan baik seperti itu.


2
2018-03-23 14:04



Saya pikir Anda perlu mengajukan beberapa pertanyaan yang memengaruhi desain.

  1. Seberapa sering Anda akan menambahkan model?
  2. Apakah konsumen perpustakaan Anda diharapkan menambah model baru?
  3. Berapa persen dari pengguna akan menambahkan model vs berapa persen akan menggunakan model yang sudah ada?

Jika sebagian besar waktu akan dihabiskan dengan model yang ada dan model baru akan kurang umum, maka warisan mungkin adalah desain yang akan saya gunakan. Itu membuat dokumentasi mudah dibentuk dan kode yang menggunakannya akan mudah dimengerti.

Jika tujuan utama perpustakaan adalah menyediakan platform untuk bereksperimen dengan berbagai model, maka saya akan mengambil grafik dengan properti yang memetakan ke fasilitator untuk menghitung hal-hal berdasarkan orang tua. Perpustakaan akan lebih kompleks dan pembuatan grafik akan lebih kompleks, tetapi akan jauh lebih kuat karena akan memungkinkan Anda untuk melakukan grafik hibrida yang mengubah fungsi perhitungan berdasarkan node.

Terlepas dari desain akhir apa yang Anda kerjakan, saya akan mulai dengan desain implementasi satu kelas yang sederhana. Dapatkan melewati serangkaian tes otomatis, kemudian refactor ke desain yang lebih penuh setelah selesai. Juga, jangan lupa kontrol versi ;-)


2
2018-03-23 14:17