Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan
Sebelum anda sekali lagi adalah tugas untuk mengesan objek. Keutamaan adalah kelajuan operasi dengan ketepatan yang boleh diterima. Anda mengambil seni bina YOLOv3 dan terus melatihnya. Ketepatan(mAp75) lebih besar daripada 0.95. Tetapi kadar larian masih rendah. Crap.
Hari ini kita akan memintas kuantisasi. Dan di bawah potongan kita akan melihat Pemangkasan Model β memangkas bahagian berlebihan rangkaian untuk mempercepatkan Inferens tanpa kehilangan ketepatan. Ia jelas di mana, berapa banyak dan bagaimana untuk memotong. Mari kita fikirkan cara melakukan ini secara manual dan di mana anda boleh mengautomasikannya. Pada akhirnya terdapat repositori pada keras.
Pengenalan
Di tempat kerja saya sebelum ini, Macroscop di Perm, saya memperoleh satu tabiat - untuk sentiasa memantau masa pelaksanaan algoritma. Dan sentiasa semak masa jalan rangkaian melalui penapis yang mencukupi. Biasanya pengeluaran yang canggih tidak melepasi penapis ini, yang membawa saya ke Pemangkasan.
Pemangkasan adalah topik lama yang dibincangkan dalam kuliah Stanford pada tahun 2017. Idea utama adalah untuk mengurangkan saiz rangkaian terlatih tanpa kehilangan ketepatan dengan mengalih keluar pelbagai nod. Bunyinya keren, tetapi saya jarang mendengar tentang penggunaannya. Mungkin, tidak ada pelaksanaan yang mencukupi, tidak ada artikel berbahasa Rusia, atau hanya semua orang menganggapnya pemangkasan pengetahuan dan berdiam diri.
Tetapi mari kita pisahkan
Sekilas tentang biologi
Saya suka apabila Deep Learning melihat idea yang datang daripada biologi. Mereka, seperti evolusi, boleh dipercayai (tahukah anda bahawa ReLU sangat serupa dengan fungsi pengaktifan neuron dalam otak?)
Proses Pemangkasan Model juga hampir dengan biologi. Tindak balas rangkaian di sini boleh dibandingkan dengan keplastikan otak. Terdapat beberapa contoh menarik dalam buku itu. Norman Doidge:
Otak seorang wanita yang dilahirkan dengan hanya satu separuh telah memprogram semula dirinya untuk melaksanakan fungsi separuh yang hilang.
Lelaki itu menembak bahagian otaknya yang bertanggungjawab untuk penglihatan. Lama kelamaan, bahagian otak yang lain mengambil alih fungsi ini. (kami tidak cuba mengulangi)
Begitu juga, anda boleh memotong beberapa lilitan yang lemah daripada model anda. Sebagai pilihan terakhir, berkas yang tinggal akan membantu menggantikan berkas yang dipotong.
Adakah anda suka Pembelajaran Pemindahan atau adakah anda belajar dari awal?
Pilihan nombor satu. Anda menggunakan Pembelajaran Pemindahan pada Yolov3. Retina, Mask-RCNN atau U-Net. Tetapi kebanyakan masa kita tidak perlu mengenali 80 kelas objek seperti di COCO. Dalam amalan saya, semuanya terhad kepada gred 1-2. Seseorang mungkin menganggap bahawa seni bina untuk 80 kelas adalah berlebihan di sini. Ini menunjukkan bahawa seni bina perlu dibuat lebih kecil. Lebih-lebih lagi, saya ingin melakukan ini tanpa kehilangan berat sedia ada yang telah dilatih.
Pilihan nombor dua. Mungkin anda mempunyai banyak data dan sumber pengkomputeran, atau hanya memerlukan seni bina tersuai super. Tidak mengapa. Tetapi anda sedang mempelajari rangkaian dari awal. Prosedur biasa adalah untuk melihat struktur data, memilih seni bina yang BERLEBIHAN kuasa, dan menolak keciciran daripada latihan semula. Saya nampak 0.6 keciciran, Karl.
Dalam kedua-dua kes, rangkaian boleh dikurangkan. Bermotivasi. Sekarang mari kita fikirkan jenis pemangkasan berkhatan
Algoritma umum
Kami memutuskan bahawa kami boleh mengalih keluar berkas. Ia kelihatan agak mudah:
Mengalih keluar sebarang lilitan adalah tekanan untuk rangkaian, yang biasanya membawa kepada peningkatan ralat. Di satu pihak, peningkatan dalam ralat ini adalah penunjuk betapa betul kita membuang belitan (contohnya, peningkatan yang besar menunjukkan bahawa kita melakukan sesuatu yang salah). Tetapi peningkatan kecil agak boleh diterima dan sering dihapuskan oleh latihan tambahan ringan berikutnya dengan LR kecil. Tambah langkah latihan tambahan:
Sekarang kita perlu memikirkan bila kita mahu menghentikan Pembelajaran<->gelung Pemangkasan. Mungkin terdapat pilihan eksotik di sini apabila kita perlu mengurangkan rangkaian kepada saiz dan kelajuan tertentu (contohnya, untuk peranti mudah alih). Walau bagaimanapun, pilihan yang paling biasa ialah meneruskan kitaran sehingga ralat menjadi lebih tinggi daripada yang boleh diterima. Tambah syarat:
Jadi, algoritma menjadi jelas. Ia masih perlu memikirkan cara untuk menentukan belitan yang dipadamkan.
Cari berkas yang dipadamkan
Kita perlu membuang beberapa belitan. Bergegas ke hadapan dan "menembak" sesiapa sahaja adalah idea yang tidak baik, walaupun ia akan berjaya. Tetapi kerana anda mempunyai kepala, anda boleh berfikir dan cuba memilih belitan "lemah" untuk dialih keluar. Terdapat beberapa pilihan:
Setiap pilihan mempunyai hak untuk hidup dan ciri pelaksanaannya sendiri. Di sini kami mempertimbangkan pilihan dengan ukuran L1 terkecil
Proses manual untuk YOLOv3
Seni bina asal mengandungi blok sisa. Tetapi tidak kira betapa hebatnya mereka untuk rangkaian yang mendalam, ia akan menghalang kita. Kesukarannya ialah anda tidak boleh memadamkan perdamaian dengan indeks yang berbeza dalam lapisan ini:
Oleh itu, mari kita pilih lapisan dari mana kita boleh memadamkan perdamaian secara bebas:
Sekarang mari kita bina kitaran kerja:
Memuat naik pengaktifan
Memikirkan berapa banyak yang perlu dipotong
Potonglah
Belajar 10 zaman dengan LR=1e-4
Menguji
Memunggah lilitan berguna untuk menganggarkan jumlah bahagian yang boleh kita alih keluar pada langkah tertentu. Contoh memunggah:
Kami melihat bahawa hampir di mana-mana 5% lilitan mempunyai norma L1 yang sangat rendah dan kami boleh mengalihkannya. Pada setiap langkah, pemunggahan ini diulang dan penilaian dibuat tentang lapisan mana dan berapa banyak yang boleh dipotong.
Keseluruhan proses telah selesai dalam 4 langkah (nombor di sini dan di mana-mana untuk RTX 2060 Super):
Langkah
mAp75
Bilangan parameter, juta
Saiz rangkaian, mb
Dari awal, %
Masa berjalan, ms
Keadaan berkhatan
0
0.9656
60
241
100
180
-
1
0.9622
55
218
91
175
5% daripada semua
2
0.9625
50
197
83
168
5% daripada semua
3
0.9633
39
155
64
155
15% untuk lapisan dengan 400+ lilitan
4
0.9555
31
124
51
146
10% untuk lapisan dengan 100+ lilitan
Satu kesan positif telah ditambahkan pada langkah 2 - saiz kumpulan 4 muat ke dalam memori, yang sangat mempercepatkan proses latihan tambahan.
Pada langkah 4, proses dihentikan kerana malah latihan tambahan jangka panjang tidak meningkatkan mAp75 kepada nilai lama.
Hasilnya, kami berjaya mempercepatkan inferens dengan 15% , kurangkan saiz dengan 35% dan tidak kalah betul-betul.
Automasi untuk seni bina yang lebih ringkas
Untuk seni bina rangkaian yang lebih mudah (tanpa blok tambahan, gabungan dan baki bersyarat), adalah mungkin untuk menumpukan pada pemprosesan semua lapisan konvolusi dan mengautomasikan proses memotong belitan.
Saya melaksanakan pilihan ini di sini.
Ia mudah: anda hanya memerlukan fungsi kehilangan, pengoptimum dan penjana kelompok:
import pruning
from keras.optimizers import Adam
from keras.utils import Sequence
train_batch_generator = BatchGenerator...
score_batch_generator = BatchGenerator...
opt = Adam(lr=1e-4)
pruner = pruning.Pruner("config.json", "categorical_crossentropy", opt)
pruner.prune(train_batch, valid_batch)
Jika perlu, anda boleh menukar parameter konfigurasi:
{
"input_model_path": "model.h5",
"output_model_path": "model_pruned.h5",
"finetuning_epochs": 10, # the number of epochs for train between pruning steps
"stop_loss": 0.1, # loss for stopping process
"pruning_percent_step": 0.05, # part of convs for delete on every pruning step
"pruning_standart_deviation_part": 0.2 # shift for limit pruning part
}
Selain itu, had berdasarkan sisihan piawai dilaksanakan. Matlamatnya adalah untuk mengehadkan bahagian yang dikeluarkan, tidak termasuk lilitan dengan ukuran L1 yang sudah "mencukupi":
Oleh itu, kami membenarkan anda untuk mengalih keluar hanya lilitan yang lemah daripada pengedaran yang serupa dengan yang betul dan tidak menjejaskan penyingkiran daripada pengedaran yang serupa dengan yang kiri:
Apabila pengedaran menghampiri normal, pekali bahagian_penyimpangan_piawai pemangkasan boleh dipilih daripada:
Saya mengesyorkan andaian 2 sigma. Atau anda boleh mengabaikan ciri ini, meninggalkan nilai < 1.0.
Output ialah graf saiz rangkaian, kehilangan dan masa jalan rangkaian untuk keseluruhan ujian, dinormalkan kepada 1.0. Sebagai contoh, di sini saiz rangkaian dikurangkan hampir 2 kali tanpa kehilangan kualiti (rangkaian konvolusi kecil dengan berat 100k):
Kelajuan larian tertakluk kepada turun naik biasa dan kekal hampir tidak berubah. Terdapat penjelasan untuk ini:
Bilangan lilitan berubah daripada mudah (32, 64, 128) kepada bukan yang paling sesuai untuk kad video - 27, 51, dsb. Saya mungkin salah di sini, tetapi kemungkinan besar ia mempunyai kesan.
Seni bina tidak luas, tetapi konsisten. Dengan mengurangkan lebar, kami tidak menjejaskan kedalaman. Oleh itu, kami mengurangkan beban, tetapi tidak mengubah kelajuan.
Oleh itu, peningkatan dinyatakan dalam pengurangan beban CUDA semasa larian sebanyak 20-30%, tetapi bukan dalam pengurangan masa larian
Keputusan
Mari bermuhasabah. Kami mempertimbangkan 2 pilihan untuk pemangkasan - untuk YOLOv3 (apabila anda perlu bekerja dengan tangan anda) dan untuk rangkaian dengan seni bina yang lebih mudah. Ia boleh dilihat bahawa dalam kedua-dua kes adalah mungkin untuk mencapai pengurangan saiz rangkaian dan mempercepatkan tanpa kehilangan ketepatan. Keputusan:
Mengurangkan saiz
Larian pecutan
Mengurangkan Beban CUDA
Akibatnya, kemesraan alam sekitar (Kami mengoptimumkan penggunaan sumber pengkomputeran pada masa hadapan. Di suatu tempat seseorang gembira Greta Thunberg)
Umbai usus
Selepas langkah pemangkasan, anda boleh menambah kuantisasi (contohnya, dengan TensorRT)