Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

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:

  1. Otak seorang wanita yang dilahirkan dengan hanya satu separuh telah memprogram semula dirinya untuk melaksanakan fungsi separuh yang hilang.
  2. 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:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

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:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

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:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

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:

  1. Ukuran L1 terkecil atau pemangkasan_magnitud_rendah. Idea bahawa konvolusi dengan pemberat kecil memberi sedikit sumbangan kepada keputusan muktamad
  2. Ukuran L1 terkecil dengan mengambil kira min dan sisihan piawai. Kami menambah dengan penilaian sifat pengedaran.
  3. Menyamarkan lilitan dan mengecualikan yang paling kurang mempengaruhi ketepatan akhir. Penentuan yang lebih tepat bagi belitan yang tidak penting, tetapi sangat memakan masa dan memakan sumber.
  4. Lain

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:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

Oleh itu, mari kita pilih lapisan dari mana kita boleh memadamkan perdamaian secara bebas:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

Sekarang mari kita bina kitaran kerja:

  1. Memuat naik pengaktifan
  2. Memikirkan berapa banyak yang perlu dipotong
  3. Potonglah
  4. Belajar 10 zaman dengan LR=1e-4
  5. Menguji

Memunggah lilitan berguna untuk menganggarkan jumlah bahagian yang boleh kita alih keluar pada langkah tertentu. Contoh memunggah:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

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":

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

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:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

Apabila pengedaran menghampiri normal, pekali bahagian_penyimpangan_piawai pemangkasan boleh dipilih daripada:

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan
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):

Teknik Jedi untuk mengurangkan rangkaian konvolusi - pemangkasan

Kelajuan larian tertakluk kepada turun naik biasa dan kekal hampir tidak berubah. Terdapat penjelasan untuk ini:

  1. 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.
  2. 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)
  • Tensorflow menyediakan keupayaan untuk pemangkasan_magnitud_rendah. Berfungsi.
  • repositori Saya mahu membangunkan dan akan gembira untuk membantu

Sumber: www.habr.com

Tambah komen