
Halo semuanya! Hari ini kita akan berbicara tentang implementasi pembelajaran mesin di Scala. Saya akan mulai dengan menjelaskan bagaimana kita sampai pada kehidupan seperti itu. Jadi, tim kami untuk waktu yang lama menggunakan semua fitur pembelajaran mesin dengan Python. Lebih mudah, ada banyak perpustakaan yang berguna untuk persiapan data, infrastruktur yang baik untuk pengembangan, maksud saya Jupyter Notebook. Semuanya akan baik-baik saja, tetapi dihadapkan dengan masalah paralelisasi perhitungan dalam produksi, dan memutuskan untuk menggunakan Scala di prod. Mengapa tidak, kami pikir, ada banyak perpustakaan di luar sana, bahkan Apache Spark ditulis dalam Scala! Pada saat yang sama, hari ini kami mengembangkan model dalam Python, dan kemudian mengulangi pelatihan di Scala untuk serialisasi lebih lanjut dan digunakan dalam produksi. Tetapi, seperti kata mereka, iblis ada dalam rinciannya.
Segera saya ingin menjelaskan, pembaca yang budiman, artikel ini tidak ditulis untuk merusak reputasi Python dalam pembelajaran mesin. Tidak, tujuan utamanya adalah membuka pintu ke dunia pembelajaran mesin pada Scala, memberikan tinjauan singkat tentang pendekatan alternatif yang mengikuti dari pengalaman kami, dan memberi tahu Anda kesulitan apa yang kami temui.
Dalam praktiknya, ternyata itu tidak begitu menyenangkan: tidak ada banyak perpustakaan yang menerapkan algoritma pembelajaran mesin klasik, dan yang sering menjadi proyek OpenSource tanpa dukungan vendor besar. Ya, tentu saja, ada Spark MLib, tetapi sangat terkait dengan ekosistem Apache Hadoop, dan saya benar-benar tidak ingin menyeretnya ke dalam arsitektur layanan mikro.
Apa yang dibutuhkan adalah solusi yang akan menyelamatkan dunia dan membawa kembali tidur nyenyak, dan itu ditemukan!
Apa yang kamu butuhkan
Ketika kami memilih alat untuk pembelajaran mesin, kami melanjutkan dari kriteria berikut:
- itu harus sederhana;
- meskipun sederhana, tidak ada yang membatalkan fungsionalitas luas;
- Saya benar-benar ingin dapat mengembangkan model dalam web-interpreter, dan tidak melalui konsol atau rakitan dan kompilasi yang konstan;
- ketersediaan dokumentasi memainkan peran penting;
- idealnya, akan ada dukungan setidaknya untuk menjawab masalah github.
Apa yang kita lihat?
- Apache Spark MLib : tidak cocok untuk kita. Seperti yang disebutkan di atas, kumpulan perpustakaan ini sangat terkait dengan tumpukan Apache Hadoop dan Spark Core itu sendiri, yang terlalu berat untuk membangun layanan microser berdasarkan itu.
- Apache PredictionIO : proyek yang menarik, banyak kontributor, ada dokumentasi dengan contoh-contoh. Bahkan, ini adalah server REST di mana model berputar. Ada model yang sudah jadi, misalnya, klasifikasi teks, yang peluncurannya dijelaskan dalam dokumentasi. Dokumentasi menjelaskan bagaimana Anda dapat menambah dan melatih model Anda. Kami tidak cocok, karena Spark digunakan di bawah tenda, dan ini lebih dari bidang solusi monolitik, daripada arsitektur layanan mikro.
- Apache MXNet : kerangka kerja yang menarik untuk bekerja dengan jaringan saraf, ada dukungan untuk Scala dan Python - ini nyaman, Anda dapat melatih jaringan saraf dengan Python, dan kemudian memuat hasil yang disimpan dari Scala saat membuat solusi produksi. Kami menggunakannya dalam solusi produksi, ada artikel terpisah tentang ini di sini .
- Senyum : sangat mirip dengan paket scikit-learn untuk Python. Ada banyak implementasi algoritma pembelajaran mesin klasik, dokumentasi yang baik dengan contoh-contoh, dukungan pada github, visualizer terintegrasi (didukung oleh Swing), Anda dapat menggunakan Jupyter Notebook untuk mengembangkan model. Inilah yang Anda butuhkan!
Persiapan lingkungan
Jadi, kami memilih Smile. Saya akan memberi tahu Anda cara menjalankannya di Notebook Jupyter menggunakan algoritma pengelompokan k-means sebagai contoh. Hal pertama yang perlu kita lakukan adalah menginstal Jupyter Notebook dengan dukungan Scala. Ini dapat dilakukan melalui pip, atau menggunakan gambar Docker yang sudah dirakit dan dikonfigurasi. Saya untuk opsi kedua yang lebih sederhana.
Untuk membuat Jupyter berteman dengan Scala, saya ingin menggunakan BeakerX, yang merupakan bagian dari gambar Docker, tersedia di repositori resmi BeakerX. Gambar ini direkomendasikan dalam dokumentasi Smile, dan Anda dapat menjalankannya seperti ini:
Tapi di sini masalah pertama sedang menunggu: pada saat penulisan artikel, BeakerX 1.0.0 dipasang di dalam gambar beakerx / beakerx, dan versi 1.4.1 sudah tersedia di github resmi proyek (lebih tepatnya, rilis terbaru 1.3.0, tetapi panduan berisi 1.4.1, dan itu bekerja :-)).
Jelas bahwa saya ingin bekerja dengan versi terbaru, jadi saya mengumpulkan gambar saya sendiri berdasarkan BeakerX 1.4.1. Saya tidak akan membuat Anda bosan dengan konten Dockerfile, ini
tautannya .
Ngomong-ngomong, bagi mereka yang akan menggunakan gambar saya, akan ada bonus kecil: dalam direktori contoh ada contoh k-means untuk urutan acak dengan memplot (ini bukan tugas yang sepenuhnya sepele untuk notebook Scala).
Unduh Smile in Jupyter Notebook
Lingkungan yang dipersiapkan dengan sangat baik! Kami membuat buku catatan Scala baru dalam folder di direktori kami, maka kami perlu mengunduh perpustakaan dari Maven agar Smile bekerja.
%%classpath add mvn com.github.haifengl smile-scala_2.12 1.5.2
Setelah menjalankan kode, daftar file jar yang diunduh akan muncul di blok outputnya.
Langkah selanjutnya: mengimpor paket yang diperlukan agar contoh berhasil.
import java.awt.image.BufferedImage import java.awt.Color import javax.imageio.ImageIO import java.io.File import smile.clustering._
Mempersiapkan data untuk pengelompokan
Sekarang kita akan memecahkan masalah berikut: menghasilkan gambar yang terdiri dari zona tiga warna primer - merah, hijau dan biru (R, G, B). Salah satu warna dalam gambar akan menang. Kami mengelompokkan piksel gambar, mengambil kluster di mana akan ada piksel terbanyak, mengubah warnanya menjadi abu-abu dan membangun gambar baru dari semua piksel. Hasil yang diharapkan: zona warna dominan akan berubah menjadi abu-abu, sisa zona tidak akan berubah warnanya.
Sebagai hasil dari mengeksekusi kode ini, gambar berikut ini ditampilkan:

Langkah selanjutnya: mengonversi gambar ke satu set piksel. Dengan piksel yang kami maksud entitas dengan properti berikut:
- koordinat sisi lebar (x);
- koordinat sisi sempit (y);
- nilai warna;
- nilai opsional dari nomor kelas / kluster (sebelum pengelompokan selesai, itu akan kosong).
Sebagai entitas, mudah untuk menggunakan
case class
:
case class Pixel(x: Int, y: Int, rgbArray: Array[Double], clusterNumber: Option[Int] = None)
Di sini, untuk nilai warna, array
rgbArray
dari tiga nilai merah, hijau dan biru digunakan (misalnya, untuk
Array(255.0, 0, 0)
warna merah
Array(255.0, 0, 0)
).
Ini melengkapi persiapan data.
Pengelompokan warna piksel
Jadi, kami memiliki koleksi piksel tiga warna primer, jadi kami akan mengelompokkan piksel menjadi tiga kelas.
Dokumentasi merekomendasikan pengaturan parameter
runs
di kisaran 10 hingga 20.
Ketika kode ini dieksekusi, objek jenis
KMeans
akan dibuat. Blok output akan berisi informasi tentang hasil pengelompokan:
K-Means distortion: 0.00000 Clusters of 230400 data points of dimension 3: 0 50813 (22.1%) 1 51667 (22.4%) 2 127920 (55.5%)
Satu cluster memang mengandung lebih banyak piksel daripada yang lainnya. Sekarang kita perlu menandai koleksi piksel kita dengan kelas dari 0 hingga 2.
Gambar cat ulang
Satu-satunya yang tersisa adalah memilih cluster dengan jumlah piksel terbesar dan mengecat ulang semua piksel yang termasuk dalam cluster ini menjadi abu-abu (ubah nilai array
rgbArray
).
Tidak ada yang rumit, cukup kelompokkan dengan nomor cluster (ini adalah
Option:[Int]
kami
Option:[Int]
), hitung jumlah elemen dalam setiap grup dan tarik keluar cluster dengan jumlah maksimum elemen. Selanjutnya, ubah warna menjadi abu-abu hanya untuk piksel yang termasuk dalam kluster yang ditemukan.
Buat gambar baru dan simpan hasilnya.
Mengumpulkan gambar baru dari koleksi piksel:
Itulah yang, pada akhirnya, kami lakukan.

Kami menyimpan kedua gambar.
ImageIO.write(testImage, "png", new File("testImage.png")) ImageIO.write(modifiedImage, "png", new File("modifiedImage.png"))
Kesimpulan
Pembelajaran mesin pada Scala ada. Untuk mengimplementasikan algoritma dasar, tidak perlu menyeret beberapa perpustakaan besar. Contoh di atas menunjukkan bahwa selama pengembangan Anda tidak bisa melepaskan cara biasa, Notebook Jupyter yang sama dapat dengan mudah berteman dengan Scala.
Tentu saja, untuk ikhtisar lengkap semua fitur Smile, satu artikel tidak cukup, dan ini tidak termasuk dalam rencana. Tugas utama - untuk membuka pintu ke dunia pembelajaran mesin di Scala - saya pikir sudah selesai. Apakah menggunakan alat-alat ini, dan lebih dari itu, menyeretnya ke dalam produksi atau tidak, terserah Anda!
Referensi