Teknik Jedi untuk mengurangi jaringan konvolusional - pemangkasan
Di hadapan Anda lagi adalah tugas mendeteksi objek. Prioritasnya adalah kecepatan operasi dengan akurasi yang dapat diterima. Anda mengambil arsitektur YOLOv3 dan melatihnya lebih lanjut. Akurasi (mAp75) lebih besar dari 0.95. Namun laju larinya masih rendah. Omong kosong.
Hari ini kita akan melewati kuantisasi. Dan di bawah potongan kita akan melihat Model Pemangkasan β memangkas bagian jaringan yang berlebihan untuk mempercepat Inferensi tanpa kehilangan akurasi. Jelas di mana, berapa banyak, dan bagaimana cara memotongnya. Mari kita cari tahu cara melakukannya secara manual dan di mana Anda dapat mengotomatiskannya. Pada akhirnya ada repositori keras.
pengenalan
Di tempat kerja saya sebelumnya, makroskop di Perm, saya memperoleh satu kebiasaan - untuk selalu memantau waktu eksekusi algoritma. Dan selalu periksa runtime jaringan melalui filter kecukupan. Biasanya produksi tercanggih tidak lolos filter ini, yang membawa saya ke Pemangkasan.
Pemangkasan adalah topik lama yang dibahas kuliah Stanford pada tahun 2017. Ide utamanya adalah untuk mengurangi ukuran jaringan yang dilatih tanpa kehilangan akurasi dengan menghapus berbagai node. Kedengarannya keren, tapi saya jarang mendengar kegunaannya. Mungkin, implementasinya tidak cukup, tidak ada artikel berbahasa Rusia, atau semua orang menganggapnya sebagai pemangkasan pengetahuan dan tetap diam.
Tapi mari kita pisahkan
Sekilas tentang biologi
Saya senang ketika Deep Learning melihat ide-ide yang berasal dari biologi. Mereka, seperti halnya evolusi, dapat dipercaya (tahukah Anda bahwa ReLU sangat mirip dengan fungsi aktivasi neuron di otak?)
Proses Model Pruning juga dekat dengan biologi. Respons jaringan di sini dapat dibandingkan dengan plastisitas otak. Ada beberapa contoh menarik dalam buku ini. Norman Dodge:
Otak seorang wanita yang terlahir hanya dengan separuh bagian otaknya telah memprogram ulang dirinya untuk menjalankan fungsi dari separuh bagian yang hilang tersebut.
Pria itu melepaskan bagian otaknya yang bertanggung jawab atas penglihatan. Seiring waktu, bagian lain dari otak mengambil alih fungsi-fungsi ini. (kami tidak mencoba mengulanginya)
Demikian pula, Anda dapat menghilangkan beberapa konvolusi lemah dari model Anda. Sebagai upaya terakhir, sisa bundel akan membantu menggantikan bundel yang dipotong.
Apakah Anda menyukai Pembelajaran Transfer atau Anda belajar dari awal?
Opsi nomor satu. Anda menggunakan Pembelajaran Transfer di Yolov3. Retina, Mask-RCNN atau U-Net. Namun seringkali kita tidak perlu mengenali 80 kelas objek seperti di COCO. Kalau praktek saya semuanya terbatas pada kelas 1-2. Orang mungkin berasumsi bahwa arsitektur untuk 80 kelas mubazir di sini. Hal ini menunjukkan bahwa arsitektur perlu dibuat lebih kecil. Selain itu, saya ingin melakukan ini tanpa kehilangan beban yang sudah dilatih sebelumnya.
Opsi nomor dua. Mungkin Anda memiliki banyak data dan sumber daya komputasi, atau hanya memerlukan arsitektur super-kustom. Tidak masalah. Tapi Anda mempelajari jaringan dari awal. Prosedur yang biasa dilakukan adalah dengan melihat struktur data, memilih arsitektur yang kekuatannya BERLEBIHAN, dan mendorong putus sekolah dari pelatihan ulang. Saya melihat 0.6 putus sekolah, Karl.
Dalam kedua kasus tersebut, jaringan dapat dikurangi. Termotivasi. Sekarang mari kita cari tahu apa itu pemangkasan sunat
Algoritma umum
Kami memutuskan bahwa kami dapat menghapus bundel tersebut. Ini terlihat cukup sederhana:
Menghapus konvolusi apa pun akan menimbulkan stres bagi jaringan, yang biasanya menyebabkan peningkatan kesalahan. Di satu sisi, peningkatan kesalahan ini merupakan indikator seberapa benar kita menghilangkan konvolusi (misalnya, peningkatan yang besar menunjukkan bahwa kita melakukan sesuatu yang salah). Namun peningkatan kecil cukup dapat diterima dan sering kali dihilangkan dengan pelatihan tambahan ringan berikutnya dengan LR kecil. Tambahkan langkah pelatihan tambahan:
Sekarang kita perlu mencari tahu kapan kita ingin menghentikan loop Pembelajaran<->Pemangkasan. Mungkin ada pilihan yang eksotis di sini ketika kita perlu mengurangi jaringan ke ukuran dan kecepatan tertentu (misalnya, untuk perangkat seluler). Namun, pilihan yang paling umum adalah melanjutkan siklus hingga kesalahan menjadi lebih tinggi dari yang dapat diterima. Tambahkan kondisi:
Jadi, algoritmanya menjadi jelas. Masih mencari cara untuk menentukan konvolusi yang dihapus.
Cari paket yang dihapus
Kita perlu menghilangkan beberapa konvolusi. Terburu-buru dan βmenembakβ siapa pun adalah ide yang buruk, meskipun itu akan berhasil. Namun karena Anda punya pikiran, Anda dapat berpikir dan mencoba memilih konvolusi yang "lemah" untuk dihilangkan. Ada beberapa pilihan:
Masing-masing opsi memiliki hak hidup dan fitur implementasinya sendiri. Di sini kami mempertimbangkan opsi dengan ukuran L1 terkecil
Proses manual untuk YOLOv3
Arsitektur aslinya mengandung blok sisa. Tapi betapapun kerennya mereka untuk jaringan dalam, mereka akan menghalangi kita. Kesulitannya adalah Anda tidak dapat menghapus rekonsiliasi dengan indeks berbeda di lapisan berikut:
Oleh karena itu, mari kita pilih lapisan yang rekonsiliasinya dapat kita hapus dengan bebas:
Sekarang mari kita membangun siklus kerja:
Mengunggah aktivasi
Mencari tahu berapa banyak yang harus dipotong
Hentikan
Mempelajari 10 epoch dengan LR=1e-4
Pengujian
Membongkar konvolusi berguna untuk memperkirakan berapa banyak bagian yang dapat kita hapus pada langkah tertentu. Contoh bongkar:
Kami melihat bahwa hampir di semua tempat, 5% konvolusi memiliki norma L1 yang sangat rendah dan kami dapat menghilangkannya. Pada setiap langkah, pembongkaran muatan ini diulangi dan dilakukan penilaian terhadap lapisan mana dan berapa banyak yang dapat dipotong.
Seluruh proses diselesaikan dalam 4 langkah (angka di sini dan di mana saja untuk RTX 2060 Super):
Langkah
peta75
Jumlah parameter, juta
Ukuran jaringan, mb
Dari awal, %
Waktunya berjalan, Bu
Kondisi sunat
0
0.9656
60
241
100
180
-
1
0.9622
55
218
91
175
5% dari semuanya
2
0.9625
50
197
83
168
5% dari semuanya
3
0.9633
39
155
64
155
15% untuk lapisan dengan 400+ konvolusi
4
0.9555
31
124
51
146
10% untuk lapisan dengan 100+ konvolusi
Satu efek positif ditambahkan ke langkah 2 - ukuran batch 4 dimasukkan ke dalam memori, yang sangat mempercepat proses pelatihan tambahan.
Pada langkah 4, proses dihentikan karena bahkan pelatihan tambahan jangka panjang tidak meningkatkan mAp75 ke nilai lama.
Hasilnya, kami berhasil mempercepat inferensi dengan 15%, kurangi ukurannya sebesar 35% dan tidak kalah persis.
Otomatisasi untuk arsitektur yang lebih sederhana
Untuk arsitektur jaringan yang lebih sederhana (tanpa blok tambahan, gabungan, dan sisa bersyarat), sangat mungkin untuk fokus pada pemrosesan semua lapisan konvolusional dan mengotomatiskan proses pemotongan konvolusi.
Saya menerapkan opsi ini di sini.
Sederhana saja: Anda hanya memerlukan fungsi kerugian, pengoptimal, dan generator batch:
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 dapat mengubah 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, pembatasan berdasarkan standar deviasi juga diterapkan. Tujuannya adalah untuk membatasi bagian yang dihapus, tidak termasuk konvolusi dengan ukuran L1 yang sudah βcukupβ:
Oleh karena itu, kami mengizinkan Anda untuk menghapus hanya konvolusi lemah dari distribusi yang serupa dengan distribusi kanan dan tidak memengaruhi penghapusan dari distribusi yang serupa dengan distribusi kiri:
Ketika distribusi mendekati normal, koefisien pemangkasan_standar_deviasi_bagian dapat dipilih dari:
Saya merekomendasikan asumsi 2 sigma. Atau Anda dapat mengabaikan fitur ini, meninggalkan nilai <1.0.
Outputnya berupa grafik ukuran jaringan, kehilangan, dan runtime jaringan untuk keseluruhan pengujian, dinormalisasi ke 1.0. Misalnya, di sini ukuran jaringan berkurang hampir 2 kali lipat tanpa kehilangan kualitas (jaringan konvolusional kecil dengan bobot 100k):
Kecepatan lari dapat mengalami fluktuasi normal dan hampir tidak berubah. Ada penjelasan untuk ini:
Jumlah konvolusi berubah dari nyaman (32, 64, 128) menjadi tidak nyaman untuk kartu video - 27, 51, dll. Saya bisa saja salah di sini, tetapi kemungkinan besar hal itu berpengaruh.
Arsitekturnya tidak luas, tapi konsisten. Dengan mengurangi lebarnya, kami tidak mempengaruhi kedalamannya. Jadi, kami mengurangi beban, tetapi tidak mengubah kecepatan.
Oleh karena itu, peningkatan tersebut dinyatakan dalam pengurangan beban CUDA selama pengoperasian sebesar 20-30%, tetapi tidak dalam pengurangan waktu pengoperasian.
Hasil
Mari kita renungkan. Kami mempertimbangkan 2 opsi pemangkasan - untuk YOLOv3 (saat Anda harus bekerja dengan tangan) dan untuk jaringan dengan arsitektur yang lebih sederhana. Dapat dilihat bahwa dalam kedua kasus dimungkinkan untuk mencapai pengurangan ukuran jaringan dan percepatan tanpa kehilangan akurasi. Hasil:
Mengurangi ukuran
Akselerasi dijalankan
Mengurangi Beban CUDA
Hasilnya, ramah lingkungan (Kami mengoptimalkan penggunaan sumber daya komputasi di masa depan. Ada yang senang Greta Thunberg)
Lampiran
Setelah langkah pemangkasan, Anda dapat menambahkan kuantisasi (misalnya dengan TensorRT)