Wektu iki ora bisa ditindakake kanthi tani medali, nanging akeh pengalaman sing migunani, mula aku bakal ngandhani masarakat babagan sawetara perkara sing paling menarik lan migunani ing Kagle lan ing saben dinane. Antarane topik sing dibahas: urip angel tanpa OpenCV, parsing JSON (conto iki nliti integrasi kode C++ menyang skrip utawa paket ing R nggunakake Rcpp), parameterisasi skrip lan dockerisasi solusi pungkasan. Kabeh kode saka pesen ing wangun cocok kanggo eksekusi kasedhiya ing repositori.
1. Efisien mbukak data saka CSV menyang database MonetDB
Data ing kompetisi iki diwenehake ora ing wangun gambar siap, nanging ing wangun 340 file CSV (siji file kanggo saben kelas) ngemot JSONs karo titik koordinat. Kanthi nyambungake titik kasebut karo garis, kita entuk gambar pungkasan kanthi ukuran 256x256 piksel. Uga kanggo saben rekaman ana label sing nuduhake manawa gambar kasebut diakoni kanthi bener dening klasifikasi sing digunakake nalika dataset diklumpukake, kode rong huruf negara panggonan penulis gambar, pengenal unik, stempel wektu. lan jeneng kelas sing cocog karo jeneng berkas. Versi data asli sing disederhanakake bobote 7.4 GB ing arsip lan kira-kira 20 GB sawise dibongkar, data lengkap sawise dibongkar njupuk 240 GB. Penyelenggara mesthekake yen versi loro kasebut ngasilake gambar sing padha, tegese versi lengkap ora ana. Ing kasus apa wae, nyimpen 50 yuta gambar ing file grafis utawa ing wangun array langsung dianggep ora nguntungake, lan kita mutusake kanggo nggabungake kabeh file CSV saka arsip. train_simplified.zip menyang database karo generasi sakteruse saka gambar saka ukuran sing dibutuhake "ing fly" kanggo saben kumpulan.
Sistem sing wis kabukten dipilih minangka DBMS MonetDB, yaiku implementasine kanggo R minangka paket MonetDBLite. Paket kalebu versi embedded saka server database lan ngijini sampeyan kanggo Pick munggah server langsung saka sesi R lan bisa karo ana. Nggawe database lan nyambungake menyang iku dileksanakake karo siji printah:
con <- DBI::dbConnect(drv = MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))
Kita kudu nggawe rong tabel: siji kanggo kabeh data, liyane kanggo informasi layanan babagan file sing diundhuh (migunani yen ana sing salah lan proses kudu diterusake sawise ndownload sawetara file):
Cara paling cepet kanggo mbukak data menyang database yaiku nyalin file CSV langsung nggunakake perintah SQL COPY OFFSET 2 INTO tablename FROM path USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORTngendi tablename - jeneng Tabel lan path - path menyang file. Nalika nggarap arsip, ditemokake yen implementasine dibangun ing unzip ing R ora bisa bener karo sawetara file saka arsip, supaya kita digunakake sistem unzip (nggunakake parameter getOption("unzip")).
Wektu loading data bisa beda-beda gumantung saka karakteristik kacepetan drive sing digunakake. Ing kasus kita, maca lan nulis ing siji SSD utawa saka flash drive (sumber file) menyang SSD (DB) njupuk kurang saka 10 menit.
Butuh sawetara detik maneh kanggo nggawe kolom kanthi label kelas integer lan kolom indeks (ORDERED INDEX) kanthi nomer baris sing bakal dadi conto observasi nalika nggawe kumpulan:
Nggawe Kolom Tambahan lan Indeks
message("Generate lables")
invisible(DBI::dbExecute(con, "ALTER TABLE doodles ADD label_int int"))
invisible(DBI::dbExecute(con, "UPDATE doodles SET label_int = dense_rank() OVER (ORDER BY word) - 1"))
message("Generate row numbers")
invisible(DBI::dbExecute(con, "ALTER TABLE doodles ADD id serial"))
invisible(DBI::dbExecute(con, "CREATE ORDERED INDEX doodles_id_ord_idx ON doodles(id)"))
Kanggo ngatasi masalah nggawe batch kanthi cepet, kita kudu entuk kacepetan maksimal kanggo ngekstrak baris acak saka tabel. doodles. Kanggo iki, kita nggunakake 3 trik. Kaping pisanan yaiku nyuda dimensi saka jinis sing nyimpen ID pengamatan. Ing set data asli, jinis sing dibutuhake kanggo nyimpen ID yaiku bigint, nanging jumlah pengamatan ndadekake bisa pas pengenal, padha karo nomer ordinal, menyang jinis int. Panelusuran luwih cepet ing kasus iki. Trik kapindho yaiku nggunakake ORDERED INDEX - kita teka kaputusan iki empiris, wis liwat kabeh kasedhiya opsi. Katelu yaiku nggunakake pitakon parameter. Inti saka metode kasebut yaiku nglakokake perintah kasebut sapisan PREPARE kanthi nggunakake ekspresi sing disiapake nalika nggawe pirang-pirang pitakon saka jinis sing padha, nanging nyatane ana kauntungan dibandhingake karo sing prasaja. SELECT pranyata ana ing jangkoan kesalahan statistik.
Proses upload data nggunakake ora luwih saka 450 MB RAM. Yaiku, pendekatan sing diterangake ngidini sampeyan mindhah set data kanthi bobot puluhan gigabyte ing meh kabeh hardware anggaran, kalebu sawetara piranti papan siji, sing apik banget.
Kabeh sing isih ana yaiku ngukur kacepetan njupuk data (acak) lan ngevaluasi skala nalika njupuk sampel saka macem-macem ukuran:
Nggambar ditindakake nggunakake alat R standar lan disimpen menyang PNG sementara sing disimpen ing RAM (ing Linux, direktori R sementara ana ing direktori /tmp, dipasang ing RAM). Berkas iki banjur diwaca minangka array telung dimensi kanthi angka saka 0 nganti 1. Iki penting amarga BMP sing luwih konvensional bakal diwaca dadi array mentah kanthi kode warna hex.
Implementasi iki katon ora optimal kanggo kita, amarga pambentukan kumpulan gedhe mbutuhake wektu sing ora sopan, lan kita mutusake kanggo njupuk kauntungan saka pengalaman kolega kanthi nggunakake perpustakaan sing kuat. OpenCV. Ing wektu iku ora ana paket siap kanggo R (ora ana saiki), supaya implementasi minimal saka fungsi sing dibutuhake ditulis ing C ++ kanthi integrasi menyang kode R nggunakake Rcpp.
Kanggo ngatasi masalah kasebut, paket lan perpustakaan ing ngisor iki digunakake:
OpenCV kanggo nggarap gambar lan nggambar garis. Pustaka sistem sing wis diinstal lan file header sing wis diinstal, uga panyambungan dinamis.
xtensor kanggo nggarap array multidimensi lan tensor. Kita nggunakake file header sing kalebu ing paket R kanthi jeneng sing padha. Pustaka ngidini sampeyan nggarap array multidimensi, ing urutan utama baris lan kolom.
ndjson kanggo parsing JSON. Pustaka iki digunakake ing xtensor kanthi otomatis yen ana ing proyek kasebut.
RcppThread kanggo ngatur pangolahan multi-threaded saka vektor saka JSON. Gunakake file header sing diwenehake dening paket iki. Saka luwih populer RcppParalel Paket kasebut, ing antarane, duwe mekanisme interupsi loop sing dibangun.
Wigati dicathet xtensor diaktifake dadi anugerah: saliyane kasunyatan sing nduweni fungsi ekstensif lan kinerja dhuwur, pangembang dadi cukup responsif lan njawab pitakonan sakcepete lan rinci. Kanthi bantuan kasebut, bisa ngetrapake transformasi matriks OpenCV dadi tensor xtensor, uga cara kanggo nggabungake tensor gambar 3 dimensi dadi tensor 4 dimensi saka dimensi sing bener (batch kasebut dhewe).
Kanggo ngumpulake file sing nggunakake file sistem lan link dinamis karo perpustakaan sing diinstal ing sistem, kita nggunakake mekanisme plugin sing diimplementasikake ing paket kasebut. Rcpp. Kanggo nemokake dalan lan panji kanthi otomatis, kita nggunakake sarana Linux sing populer pkg-config.
Implementasi plugin Rcpp kanggo nggunakake perpustakaan OpenCV
Kode implementasine kanggo parsing JSON lan ngasilake batch kanggo transmisi menyang model diwenehi ing spoiler. Pisanan, tambahake direktori proyek lokal kanggo nggoleki file header (dibutuhake kanggo ndjson):
Nalika sampeyan bisa ndeleng, Tambah kacepetan dadi banget pinunjul, lan iku ora bisa kanggo nyekel munggah karo C ++ kode dening parallelizing R kode.
3. Iterators kanggo unloading kumpulan saka database
R wis reputasi uga-pantes kanggo Processing data sing mathuk ing RAM, nalika Python luwih ditondoi dening Processing data iteratif, ngijini sampeyan kanggo gampang lan alami ngleksanakake petungan metu-saka-inti (pitungan nggunakake memori external). Conto klasik lan relevan kanggo kita ing konteks masalah sing diterangake yaiku jaringan saraf jero sing dilatih kanthi metode turunan gradien kanthi perkiraan gradien ing saben langkah nggunakake bagean cilik saka pengamatan, utawa mini-batch.
Kerangka sinau jero sing ditulis ing Python duwe kelas khusus sing ngleksanakake iterator adhedhasar data: tabel, gambar ing folder, format binar, lsp. Sampeyan bisa nggunakake opsi sing wis siap utawa nulis dhewe kanggo tugas tartamtu. Ing R kita bisa njupuk kauntungan saka kabeh fitur saka perpustakaan Python keras karo macem-macem backends nggunakake paket saka jeneng sing padha, kang siji dianggo ing ndhuwur paket njlentrehake. Sing terakhir pantes artikel dawa kapisah; iku ora mung ngijini sampeyan kanggo mbukak kode Python saka R, nanging uga ngijini sampeyan kanggo nransfer obyek antarane R lan Python, kanthi otomatis nindakake kabeh konversi jinis perlu.
Kita nyingkirake kabutuhan kanggo nyimpen kabeh data ing RAM kanthi nggunakake MonetDBite, kabeh karya "jaringan saraf" bakal ditindakake kanthi kode asli ing Python, kita mung kudu nulis iterator liwat data, amarga ora ana sing siap. kanggo kahanan kuwi ing salah siji R utawa Python. Ana mung rong syarat: kudu ngasilake batch ing daur ulang tanpa wates lan nyimpen negara ing antarane pengulangan (sing terakhir ing R diimplementasikake kanthi cara sing paling gampang nggunakake penutupan). Sadurunge, diwajibake kanthi jelas ngowahi array R dadi array numpy ing iterator, nanging versi paket saiki keras nindakake dhewe.
Iterator kanggo latihan lan validasi data dadi kaya ing ngisor iki:
Fungsi kasebut minangka input variabel kanthi sambungan menyang database, jumlah baris sing digunakake, jumlah kelas, ukuran batch, skala (scale = 1 cocog karo gambar rendering 256x256 piksel, scale = 0.5 β 128x128 piksel), indikator warna (color = FALSE nemtokake rendering ing grayscale nalika digunakake color = TRUE saben stroke digambar ing werna anyar) lan indikator preprocessing kanggo jaringan sing wis dilatih ing imagenet. Sing terakhir dibutuhake kanggo ngukur nilai piksel saka interval [0, 1] nganti interval [-1, 1], sing digunakake nalika latihan sing diwenehake. keras model.
Fungsi eksternal ngemot pamriksa jinis argumen, tabel data.table karo nomer baris campuran acak saka samples_index lan nomer batch, counter lan jumlah maksimum batch, uga expression SQL kanggo unloading data saka database. Kajaba iku, kita nemtokake analog cepet saka fungsi ing njero keras::to_categorical(). Kita nggunakake meh kabeh data kanggo latihan, ninggalake setengah persen kanggo validasi, supaya ukuran jaman diwatesi dening parameter steps_per_epoch nalika diarani keras::fit_generator(), lan kahanan if (i > max_i) mung bisa kanggo iterator validasi.
Ing fungsi internal, indeks baris dijupuk kanggo kumpulan sabanjure, cathetan dibongkar saka database kanthi counter batch nambah, parsing JSON (fungsi cpp_process_json_vector(), ditulis ing C++) lan nggawe array sing cocog karo gambar. Banjur vektor siji-panas karo label kelas digawe, array karo nilai piksel lan label digabungake menyang dhaftar, kang Nilai bali. Kanggo nyepetake karya, kita nggunakake nggawe indeks ing tabel data.table lan modifikasi liwat link - tanpa paket "chip" iki data.tabel Iku cukup angel mbayangno nggarap kanthi efektif karo jumlah data sing signifikan ing R.
Asil pangukuran kacepetan ing laptop Core i5 kaya ing ngisor iki:
Yen sampeyan duwe jumlah RAM sing cukup, sampeyan bisa kanthi serius nyepetake operasi database kanthi nransfer menyang RAM sing padha (32 GB cukup kanggo tugas kita). Ing Linux, partisi dipasang kanthi standar /dev/shm, manggoni nganti setengah kapasitas RAM. Sampeyan bisa nyorot liyane kanthi nyunting /etc/fstabkanggo entuk rekor kaya tmpfs /dev/shm tmpfs defaults,size=25g 0 0. Dadi manawa kanggo urip maneh lan mriksa asil kanthi mbukak printah df -h.
Iterator kanggo data tes katon luwih gampang, amarga set data tes cocog karo RAM:
Arsitektur pisanan sing digunakake yaiku mobilenet v1, fitur kang rembugan ing iki pesen. Iku kalebu minangka standar keras lan, patut, kasedhiya ing paket kanthi jeneng sing padha kanggo R. Nanging nalika nyoba nggunakake gambar saluran siji, ana sing aneh: tensor input kudu duwe ukuran. (batch, height, width, 3), yaiku, jumlah saluran ora bisa diganti. Ora ana watesan ing Python, mula kita cepet-cepet nulis implementasine arsitektur iki, miturut artikel asli (tanpa dropout ing versi keras):
Kerugian saka pendekatan iki jelas. Aku pengin nyoba akeh model, nanging ing nalisir, aku ora pengin nulis ulang saben arsitektur kanthi manual. Kita uga ora duwe kesempatan kanggo nggunakake bobot model sing wis dilatih ing imagenet. Kaya biasane, sinau dokumentasi mbantu. Fungsi get_config() ngidini sampeyan entuk katrangan model ing wangun sing cocog kanggo nyunting (base_model_conf$layers - dhaptar R biasa), lan fungsi from_config() nindakake konversi mbalikke menyang obyek model:
Nalika nggunakake gambar saluran siji, ora ana bobot sing wis dilatih. Iki bisa diatasi: nggunakake fungsi kasebut get_weights() entuk bobot model ing wangun dhaptar array R, ganti dimensi unsur pisanan saka dhaptar iki (kanthi njupuk siji saluran warna utawa rata-rata kabeh telu), banjur muat bobot bali menyang model kanthi fungsi set_weights(). Kita ora tau nambahake fungsi iki, amarga ing tahap iki wis jelas manawa luwih produktif kanggo nggarap gambar warna.
Kita nindakake akeh eksperimen nggunakake mobilenet versi 1 lan 2, uga resnet34. Arsitektur luwih modern kayata SE-ResNeXt tampil apik ing kompetisi iki. Sayange, kita ora duwe implementasine siap-digawe ing pembuangan kita, lan kita ora nulis dhewe (nanging kita mesthi bakal nulis).
5. Parameterisasi naskah
Kanggo penak, kabeh kode kanggo miwiti latihan dirancang minangka siji script, parameterized nggunakake docopt kaya mangkene:
doc <- '
Usage:
train_nn.R --help
train_nn.R --list-models
train_nn.R [options]
Options:
-h --help Show this message.
-l --list-models List available models.
-m --model=<model> Neural network model name [default: mobilenet_v2].
-b --batch-size=<size> Batch size [default: 32].
-s --scale-factor=<ratio> Scale factor [default: 0.5].
-c --color Use color lines [default: FALSE].
-d --db-dir=<path> Path to database directory [default: Sys.getenv("db_dir")].
-r --validate-ratio=<ratio> Validate sample ratio [default: 0.995].
-n --n-gpu=<number> Number of GPUs [default: 1].
'
args <- docopt::docopt(doc)
Paket docopt makili implementasine http://docopt.org/ kanggo R. Kanthi bantuan, script dibukak karo printah prasaja kaya Rscript bin/train_nn.R -m resnet50 -c -d /home/andrey/doodle_db utawa ./bin/train_nn.R -m resnet50 -c -d /home/andrey/doodle_db, yen file train_nn.R bisa dieksekusi (prentah iki bakal miwiti latihan model resnet50 ing gambar telung werna ukuran 128x128 piksel, database kudu dumunung ing folder /home/andrey/doodle_db). Sampeyan bisa nambah kacepetan sinau, jinis pangoptimal, lan paramèter liyane sing bisa disesuaikan menyang dhaptar. Ing proses nyiapake publikasi, ternyata arsitektur mobilenet_v2 saka versi saiki keras ing R nggunakake ora bisa amarga owah-owahan ora dijupuk menyang akun ing paket R, kita nunggu wong-wong mau kanggo ndandani iku.
Pendekatan iki ndadekake bisa nyepetake eksperimen kanthi model sing beda-beda dibandhingake karo peluncuran skrip sing luwih tradisional ing RStudio (kita nyathet paket kasebut minangka alternatif sing bisa ditindakake. tfruns). Nanging kauntungan utama yaiku kemampuan kanggo gampang ngatur peluncuran skrip ing Docker utawa mung ing server, tanpa nginstal RStudio kanggo iki.
6. Dockerization saka script
Kita nggunakake Docker kanggo njamin portabilitas lingkungan kanggo model latihan ing antarane anggota tim lan penyebaran kanthi cepet ing awan. Sampeyan bisa miwiti njaluk kenalan karo alat iki, kang relatif mboten umum kanggo programmer R, karo iki seri publikasi utawa kursus video.
Docker ngidini sampeyan nggawe gambar dhewe saka awal lan nggunakake gambar liyane minangka basis kanggo nggawe dhewe. Nalika nganalisa pilihan sing kasedhiya, kita entuk kesimpulan yen nginstal NVIDIA, driver CUDA + cuDNN lan perpustakaan Python minangka bagean gambar sing cukup akeh, lan kita mutusake kanggo njupuk gambar resmi minangka basis. tensorflow/tensorflow:1.12.0-gpu, nambahake paket R sing perlu ana.
Kanggo penak, paket sing digunakake dilebokake ing variabel; akeh saka script ditulis sing disalin nang wadhah sak perakitan. Kita uga ngganti cangkang printah kanggo /bin/bash kanggo ease saka nggunakake isi /etc/os-release. Iki nyingkiri kabutuhan kanggo nemtokake versi OS ing kode.
Kajaba iku, skrip bash cilik ditulis sing ngidini sampeyan mbukak wadhah kanthi macem-macem perintah. Contone, iki bisa dadi skrip kanggo latihan jaringan saraf sing sadurunge diselehake ing wadhah kasebut, utawa cangkang perintah kanggo debugging lan ngawasi operasi wadhah kasebut:
Script kanggo miwiti wadhah
#!/bin/sh
DBDIR=${PWD}/db
LOGSDIR=${PWD}/logs
MODELDIR=${PWD}/models
DATADIR=${PWD}/data
ARGS="--runtime=nvidia --rm -v ${DBDIR}:/db -v ${LOGSDIR}:/app/logs -v ${MODELDIR}:/app/models -v ${DATADIR}:/app/data"
if [ -z "$1" ]; then
CMD="Rscript /app/train_nn.R"
elif [ "$1" = "bash" ]; then
ARGS="${ARGS} -ti"
else
CMD="Rscript /app/train_nn.R $@"
fi
docker run ${ARGS} doodles-tf ${CMD}
Yen script bash iki mbukak tanpa paramèter, script bakal disebut nang wadhah train_nn.R kanthi nilai standar; yen argumen posisional pisanan yaiku "bash", wadhah kasebut bakal diwiwiti kanthi interaktif karo cangkang perintah. Ing kabeh kasus liyane, nilai-nilai argumen posisi diganti: CMD="Rscript /app/train_nn.R $@".
Wigati dicathet yen direktori karo data sumber lan database, uga direktori kanggo nyimpen model sing dilatih, dipasang ing wadhah saka sistem host, sing ngidini sampeyan ngakses asil skrip tanpa manipulasi sing ora perlu.
Sing paling menarik yaiku fragmen kode sing tanggung jawab kanggo nggunakake macem-macem GPU. Kaping pisanan, model digawe ing CPU nggunakake manajer konteks, kaya ing Python:
Teknik klasik pembekuan kabeh lapisan kajaba sing pungkasan, latihan lapisan pungkasan, unfreezing lan latihan maneh kabeh model kanggo sawetara GPU ora bisa dileksanakake.
Latihan dipantau tanpa nggunakake. papan tensor, mbatesi awake dhewe kanggo ngrekam log lan nyimpen model kanthi jeneng informatif sawise saben jaman:
Sawetara masalah sing kita alami durung ditanggulangi:
Π² keras ora ana fungsi sing wis siap kanggo nggoleki kanthi otomatis tingkat sinau sing optimal (analog lr_finder ing perpustakaan cepet.ai); Kanthi sawetara gaweyan, bisa port implementasine pihak katelu kanggo R, contone, iki;
minangka akibat saka titik sadurunge, ora bisa milih kacepetan latihan sing bener nalika nggunakake sawetara GPU;
ana kekurangan arsitektur jaringan saraf modern, utamane sing wis dilatih ing imagenet;
ora ana siji siklus kabijakan lan tarif sinau diskriminatif (cosinus annealing ana ing panyuwunan kita dipun ginakaken, matur nuwun skeydan).
Apa sing migunani sing disinaoni saka kompetisi iki:
Ing hardware relatif kurang daya, sampeyan bisa nggarap prayoga (kaping pirang-pirang ukuran RAM) volume data tanpa pain. Kantong plastik data.tabel nyimpen memori amarga owah-owahan ing-panggonan tabel, kang ngindari nyalin, lan nalika digunakake bener, Kapabilitas meh tansah nduduhake kacepetan paling dhuwur antarane kabeh alat dikenal kanggo basa scripting. Nyimpen data ing basis data ngidini sampeyan, ing pirang-pirang kasus, ora mikir babar pisan babagan kudu ngempet kabeh dataset menyang RAM.
Fungsi alon ing R bisa diganti karo sing cepet ing C ++ nggunakake paket Rcpp. Yen saliyane nggunakake RcppThread utawa RcppParalel, kita entuk implementasi multi-threaded lintas-platform, dadi ora perlu kanggo parallelize kode ing tingkat R.
paket Rcpp bisa digunakake tanpa kawruh serius babagan C ++, minimal sing dibutuhake wis mbatesi kene. File header kanggo sawetara perpustakaan C kelangan kaya xtensor kasedhiya ing CRAN, yaiku, infrastruktur lagi dibentuk kanggo implementasi proyek sing nggabungake kode C ++ kinerja dhuwur sing siap digawe menyang R. Penak tambahan yaiku nyorot sintaks lan penganalisa kode C ++ statis ing RStudio.
docopt ngidini sampeyan mbukak skrip mandiri kanthi paramèter. Iki trep kanggo nggunakake ing server remot, kalebu. ing docker. Ing RStudio, ora trep kanggo nindakake eksperimen pirang-pirang jam kanthi latihan jaringan saraf, lan nginstal IDE ing server dhewe ora mesthi dibenerake.
Docker njamin portabilitas kode lan reproduktifitas asil antarane pangembang kanthi versi OS lan perpustakaan sing beda-beda, uga gampang dieksekusi ing server. Sampeyan bisa miwiti kabeh pipa latihan kanthi mung siji printah.
Google Cloud minangka cara sing ramah anggaran kanggo eksperimen ing hardware sing larang, nanging sampeyan kudu milih konfigurasi kanthi ati-ati.
Ngukur kacepetan fragmen kode individu migunani banget, utamane nalika nggabungake R lan C++, lan nganggo paket. bangku - uga gampang banget.