Stable Diffusion XL 1.0 yang Berjalan dengan RAM 298MB
(github.com/vitoplantamura)- OnnxStream adalah pustaka inferensi ultra-ringan yang ditujukan untuk menjalankan Stable Diffusion 1.5 dan SDXL 1.0 Base pada Raspberry Pi Zero 2 dengan RAM 512MB tanpa menambahkan swap atau menyimpan hasil antara ke disk
- Mesin inferensi dipisahkan dari
WeightsProviderdan mendukungDiskNoCache,DiskPrefetch, serta loading·caching·prefetching kustom; bobot juga bisa diambil langsung lewat HTTP dan diproses tanpa penulisan ke disk - SDXL 1.0 Base menggunakan file ONNX yang diekspor dari implementasi Hugging Face Diffusers 0.19.3, dan pembuatan gambar 10 langkah di Raspberry Pi Zero 2 memerlukan sekitar 11 jam
- Decoder VAE SDXL memakai 4.4GB RAM pada FP32 dan sulit diatasi hanya dengan FP16·UINT8, sehingga decoding tile bertumpuk dan blending menurunkan penggunaan memori menjadi 298MB
- Dibanding OnnxRuntime di CPU, penggunaan memorinya bisa hingga 55 kali lebih kecil, dengan konsekuensi bisa 0,5~2 kali lebih lambat serta memiliki batasan pada batch size 1 dan fokus pada sebagian operator ONNX
Tujuan dan desain OnnxStream
- OnnxStream dimulai dengan tujuan menjalankan Stable Diffusion 1.5 di Raspberry Pi Zero 2
- Raspberry Pi Zero 2 adalah mikrokomputer dengan RAM 512MB
- RAM/VRAM minimum yang umumnya direkomendasikan untuk Stable Diffusion adalah 8GB
- Syarat targetnya adalah tanpa penambahan ruang swap dan tanpa offloading hasil antara ke disk
- Framework machine learning umum cenderung membesarkan penggunaan RAM karena memprioritaskan latensi inferensi atau throughput
- OnnxStream adalah pustaka inferensi kecil yang mudah dimodifikasi dan berfokus pada meminimalkan penggunaan memori
- Struktur intinya adalah pemisahan antara mesin inferensi dan komponen penyedia bobot model, yaitu
WeightsProvider- Kelas turunan dapat mengimplementasikan cara memuat, caching, dan prefetching parameter model
WeightsProviderkustom bisa langsung mengunduh data dari server HTTP dan memprosesnya tanpa baca/tulis ke disk- Implementasi bawaan adalah
DiskNoCachedanDiskPrefetch
- Di CPU, penggunaan memorinya bisa hingga 55 kali lebih kecil dibanding OnnxRuntime, dengan kecepatan sekitar 0,5~2 kali lebih lambat
Menjalankan Stable Diffusion 1.5
- Contoh Stable Diffusion 1.5 menghasilkan gambar dengan presisi decoder VAE yang berbeda menggunakan OnnxStream
- Satu-satunya komponen yang tidak muat ke RAM Raspberry Pi Zero 2 adalah decoder VAE
- Penyebabnya adalah residual connection, tensor besar, dan convolution besar di dalam model
- Presisi single maupun half saja tidak cukup agar muat di RAM Raspberry Pi Zero 2
- Solusinya adalah kuantisasi statis 8-bit
- Gambar decoder VAE dengan presisi W8A8 dibuat di Raspberry Pi Zero 2, dan dengan opsi
MAX_SPEEDmembutuhkan sekitar 1,5 jam - Gambar W16A16 untuk perbandingan dibuat di PC menggunakan latent yang sama
Dukungan Stable Diffusion XL 1.0 Base
- Contoh Stable Diffusion di OnnxStream mendukung SDXL 1.0 Base dan tidak menyertakan Refiner
- File ONNX diekspor dari implementasi SDXL 1.0 di pustaka Hugging Face Diffusers, dengan versi yang digunakan 0.19.3
- SDXL 1.0 memiliki biaya komputasi yang jauh lebih besar daripada SD 1.5
- Perbedaan terbesarnya adalah dapat menghasilkan gambar 1024x1024 alih-alih 512x512
- Di PC 12-core dengan RAM 32GB, Hugging Face Diffusers membutuhkan 26 menit untuk membuat gambar 10 langkah
- VRAM minimum yang umumnya direkomendasikan untuk SDXL adalah 12GB
- OnnxStream dapat menjalankan SDXL 1.0 dengan RAM di bawah 300MB, sehingga tetap bisa berjalan di Raspberry Pi Zero 2
- Tanpa penambahan swap
- Tanpa penulisan ke disk selama inferensi
- Pembuatan gambar 10 langkah di Raspberry Pi Zero 2 memerlukan sekitar 11 jam
Optimasi memori khusus SDXL
- SDXL 1.0 menerapkan kumpulan optimasi yang sama seperti SD 1.5, dengan beberapa perbedaan
- Model UNET menggunakan kuantisasi dinamis UINT8 agar bisa berjalan di Raspberry Pi Zero 2 dengan RAM di bawah 300MB
- Target kuantisasinya dibatasi pada subset tertentu dari tensor antara yang besar
- Decoder VAE SDXL 1.0 lebih sulit ditangani daripada SD 1.5
- Decoder VAE SDXL 1.0 berukuran 4 kali lebih besar daripada decoder VAE SD 1.5
- Saat dijalankan di OnnxStream dengan presisi FP32, ia menggunakan 4.4GB RAM
- Pada SD 1.5, decoder VAE dapat dikuantisasi statis ke UINT8 sehingga penggunaan RAM turun menjadi 260MB
- Decoder VAE SDXL 1.0 mengalami overflow pada aritmetika FP16, dan rentang nilai activation yang besar membuat kuantisasi UINT8 sulit menghasilkan gambar berkualitas baik
- Ada solusi FP16 seperti sdxl-vae-fp16-fix, tetapi meski memori dipangkas setengah menjadi 2.2GB, itu masih terlalu besar untuk Raspberry Pi Zero 2
- Solusi akhirnya adalah tile decoding yang terinspirasi dari implementasi decoder VAE di Hugging Face Diffusers
- Bentuk tensor hasil diffusion adalah
(1,4,128,128) - Ini dibagi menjadi tensor bertumpuk berukuran
(1,4,32,32)sebanyak 5x5, total 25 buah - Tiap tile bertumpuk 25% dengan tile di kiri dan di atasnya
- Hasil decoding tiap tile adalah tensor
(1,3,256,256)dan diblend ke gambar akhir - Jika blending dimatikan, batas tile akan terlihat; jika diaktifkan, batas itu tidak terlihat pada hasil akhir
- Bentuk tensor hasil diffusion adalah
- Dengan cara ini, penggunaan RAM decoder VAE SDXL turun dari 4.4GB menjadi 298MB
Fitur yang didukung dan dependensi
- OnnxStream merangkum fitur yang dibutuhkan untuk inferensi hemat memori dalam paket kecil
- Pemisahan mesin inferensi dan
WeightsProvider DiskNoCache,DiskPrefetch,WeightsProviderkustom- attention slicing
- kuantisasi dinamis percentile asimetris unsigned 8-bit
- kuantisasi statis W8A8 percentile asimetris unsigned
- kalibrasi model terkuantisasi
- dukungan FP16
- implementasi 25 operator ONNX yang umum dipakai
- Pemisahan mesin inferensi dan
- Operasi dieksekusi secara berurutan, tetapi tiap operator berjalan multithread
- Implementasinya menggunakan struktur satu file implementasi dan satu file header, dengan kelas
XnnPackyang membungkus pemanggilan XNNPACK - Beberapa primitive akselerasi bergantung pada XNNPACK
- MatMul
- Convolution
- element-wise Add/Sub/Mul/Div
- Sigmoid
- Softmax
Perbandingan performa dan batasan
- Stable Diffusion terdiri dari tiga model
- text encoder: 672 operasi, 123 juta parameter
- UNET: 2050 operasi, 854 juta parameter
- VAE decoder: 276 operasi, 49 juta parameter
- Untuk pembuatan gambar 10 langkah dengan batch size 1, diperlukan eksekusi berikut
- text encoder 2 kali
- UNET 20 kali
- VAE decoder 1 kali
- Dengan UNET FP16, perbedaan memori dan waktu antara OnnxStream dan OnnxRuntime cukup besar
- OnnxStream: sekitar 0.133GB, 18.2~19.8 detik
- OnnxRuntime: 5.085~7.353GB, 7.28~12.8 detik
- OnnxStream memakai memori hingga 55 kali lebih sedikit, tetapi 0,5~2 kali lebih lambat
- Untuk text encoder FP32, OnnxStream menggunakan 0.147GB dan OnnxRuntime 0.641GB
- Untuk VAE decoder FP32, OnnxStream menggunakan 1.004GB dan OnnxRuntime 1.330~2.026GB
- Hasil perbandingan ini memiliki beberapa kondisi
- Eksekusi pertama OnnxRuntime adalah inferensi warm-up sebelum
InferenceSessiondigunakan ulang - OnnxStream tidak memiliki konsep warm-up karena desainnya eager, tetapi eksekusi berikutnya bisa mendapat keuntungan dari cache file weights milik OS
- Saat ini OnnxStream tidak mendukung input selain batch size 1
- OnnxRuntime dapat menggunakan batch size 2 pada eksekusi UNET untuk mempercepat keseluruhan diffusion secara signifikan
- Perubahan
SessionOptionsOnnxRuntime sepertiEnableCpuMemArenadanExecutionModetidak menghasilkan perbedaan berarti dalam pengujian - NCNN sangat mirip dengan OnnxRuntime dalam hal penggunaan memori dan waktu inferensi
- Lingkungan pengujian adalah Windows Server 2019, RAM 16GB, CPU 8750H AVX2, SSD 970 EVO Plus, dan 8 virtual core VMWare
- Eksekusi pertama OnnxRuntime adalah inferensi warm-up sebelum
Attention slicing dan kuantisasi
- Saat menjalankan UNET, attention slicing dan kuantisasi W8A8 pada decoder VAE sangat penting untuk menurunkan memori hingga ke tingkat yang bisa dijalankan di Raspberry Pi Zero 2
- attention slicing mencegah materialisasi penuh matriks
Q @ K^Tsaat menghitung scaled dot-product attention pada multi-head attention - Saat jumlah attention head pada model UNET adalah 8, bentuk tensornya sebagai berikut
Q:(8,4096,40)K^T:(8,40,4096)- Hasil MatMul pertama:
(8,4096,4096) - Pada presisi FP32, ini menjadi tensor sebesar 512MB
- Solusinya adalah membagi
Qsecara vertikal dan menjalankan operasi attention untuk tiap chunk- Bentuk
Q_slicedadalah(1,x,40) xadalah nilai 4096 yang dibagi denganonnxstream::Model::m_attention_fused_ops_parts- Nilai default adalah 2 dan bisa dikustomisasi
- Bentuk
- Cara ini menurunkan total penggunaan memori model UNET FP32 dari 1.1GB menjadi 300MB
- FlashAttention mungkin menjadi alternatif yang lebih efisien, tetapi memerlukan kernel kustom per arsitektur yang didukung seperti AVX·NEON, dan dalam kasus ini harus melewati XNNPACK
Konversi model dan cara eksekusi
- OnnxStream menjalankan model yang didefinisikan di
path_to_model_folder/model.txt- Semua operasi model berada di
model.txtdalam format ASCII - File weights harus ada sebagai rangkaian file
.bindi folder yang sama
- Semua operasi model berada di
- Beberapa parameter opsional dapat diatur pada objek
Model- menentukan weights provider lain
- membaca/menulis file rentang activation clipping untuk model terkuantisasi
- mode kalibrasi model
- menggunakan aritmetika FP16
- menggunakan aritmetika UINT8
- menggunakan kuantisasi dinamis UINT8
- mengaktifkan attention slicing
- mengatur jumlah pembagian attention
- Untuk memakai file ONNX di OnnxStream, notebook
onnx2txt.ipynbdigunakan untuk mengekspormodel.txtdan file weights.bin - Saat mengekspor
nn.ModulePyTorch ke ONNX, ada beberapa syarat- Saat memanggil
torch.onnx.export,dynamic_axesharus dibiarkan kosong - OnnxStream tidak mendukung input dynamic shape
- Menjalankan ONNX Simplifier sebelum konversi sangat dianjurkan
- Saat memanggil
Persiapan build dan eksekusi
- Contoh Stable Diffusion dapat dibangun di Linux, Mac, Windows, dan Termux
- Di Windows digunakan
x64 Native Tools Command PromptdariVisual Studio Tools - Di Mac perlu memasang cmake dengan
brew install cmake
- Di Windows digunakan
- Pertama, XNNPACK harus dibangun lebih dulu
- Prototype fungsi XNNPACK bisa berubah kapan saja, jadi prosedurnya mencakup checkout ke commit yang sesuai pada titik waktu tertentu
- Titik acuannya adalah commit master sebelum
2023-06-27 00:00
- Setelah itu clone repositori OnnxStream dan build dengan cmake di
src/buildMAX_SPEED=ON- tentukan jalur clone XNNPACK lewat
XNNPACK_DIR
- Opsi
MAX_SPEEDdapat meningkatkan performa, tetapi menggunakan lebih banyak memori saat build- Di Windows ada peningkatan performa sekitar 10%
- Di Raspberry Pi ada peningkatan performa lebih dari 50%
- File executable yang dihasilkan bisa saja tidak berjalan, dan ada masalah pada pengujian Termux
- Jika bermasalah, disarankan lebih dulu mengubah
MAX_SPEED=OFF
- Weights contoh Stable Diffusion 1.5 dapat diunduh dari Releases repositori dan ukurannya sekitar 2GB
- Weights Stable Diffusion XL 1.0 Base dapat diunduh dari Hugging Face dan ukurannya sekitar 8GB
Opsi contoh Stable Diffusion
- File executable contoh mengontrol pemilihan model, input/output, prompt, dan metode decoding lewat opsi command line
--xl: menjalankan Stable Diffusion XL 1.0 alih-alih Stable Diffusion 1.5--models-path: menentukan folder model Stable Diffusion--ops-printf: mencetak operation saat ini ke stdout selama inferensi--output: menentukan file PNG output--decode-latents: melewati diffusion dan mendekode file latent yang ditentukan--prompt: menentukan positive prompt--neg-prompt: menentukan negative prompt--steps: menentukan jumlah diffusion step--save-latents: menyimpan latent ke file yang ditentukan setelah diffusion
- Opsi terkait Raspberry Pi dan decoder juga disediakan secara terpisah
--decoder-calibrate: mengalibrasi decoder VAE terkuantisasi hanya untuk SD 1.5--decoder-fp16: menggunakan decoder VAE FP16 hanya untuk SD 1.5--not-tiled: tidak menggunakan tiled VAE decoder hanya untuk SDXL 1.0--rpi: mengonfigurasi model agar berjalan di Raspberry Pi--rpi-lowmem: menerapkan konfigurasi memori rendah untuk Raspberry Pi Zero 2 hanya pada SDXL 1.0
1 komentar
Opini Hacker News
Menarik. Kalimat kuncinya ini: “OnnxStream bisa memakai memori hingga 55 kali lebih sedikit daripada OnnxRuntime, sementara kecepatannya bisa hanya 0,5–2 kali lebih lambat”
Trade-off antara memori video/penggunaan memori dan waktu inferensi tampaknya bisa menguntungkan bukan hanya pada kasus memori terbatas seperti Raspberry Pi, tetapi juga dalam situasi lain
Kalau metode unloading bobot ini memang memungkinkan batch size yang lebih besar dalam memori yang sama, saya penasaran apakah throughput bisa meningkat besar meski latensinya bertambah
Mungkin maksudnya “1,5–2 kali lebih lambat”
Inferensi biasanya terikat bandwidth memori setelah melewati level “apakah model ini muat di sistem ini”, jadi saya rasa teknik ini tidak akan banyak membantu menaikkan throughput lewat batch size yang lebih besar. Satu instance saja kemungkinan besar sudah menjenuhkan memory controller
Namun mungkin ini bisa membantu di sisi training
11 jam, jadi teringat masa-masa ray tracing di Amiga 500 dulu. Render “final” jelas pekerjaan yang dijalankan semalaman
Tapi meski caustics terlihat terang, secara statistik itu fenomena yang cukup jarang, jadi untuk mendapatkannya dengan benar, batasan render engine harus dilepas dan dibiarkan berjalan semalaman
Hasilnya satu gambar adegan biasa-biasa saja dari seniman yang kurang terampil, tetapi dengan caustics yang keren. Sepertinya saya harus tetap menjalani pekerjaan utama
Saat itu saya merasa butuh hobi lain. Tepat sebelumnya, seseorang yang hebat merilis tool rendering yang memungkinkan melihat adegan dulu dengan OpenGL. Tidak akan bisa di Amiga, tapi di mesin saya nyaris jalan
Saya memakai Stable Diffusion lewat invoke.ai di MBP; ada rekomendasi untuk mengatur parameter SD dengan lebih baik? Dengan prompt yang sama dan setting yang tampaknya sama, misalnya model yang sama seperti Euler A, saya sama sekali tidak bisa menyamai kualitas gambar yang terlihat di internet
Untuk SD 1.5 dasar saya memakai Volta karena cepat: https://github.com/VoltaML/voltaML-fast-stable-diffusion/com...
Kualitas gambar SD 1.5 yang benar-benar bagus biasanya muncul kalau memakai model fine-tuned, LoRA, ControlNet, dan fitur penguat lain secara maksimal. Misalnya membuatnya mengikuti gambar dasar sebagai struktur, atau menentukan prompt per area tertentu pada gambar. InvokeAI sebenarnya juga punya banyak fitur, dan banyak kemampuan tambahan seperti ini tersembunyi di node UI, tetapi UI lain seperti Volta menampilkannya lebih langsung
Secara umum, model fine-tuned yang mengesankan jauh kurang serbaguna daripada bobot dasar, tetapi dalam penggunaan nyata itu biasanya bukan masalah besar dan hasilnya bisa jauh lebih baik
Memang butuh lebih banyak waktu dan memori daripada Invoke atau kartu grafis Nvidia, tetapi tidak terlalu buruk. Gambar berkualitas standar 512x768px sekitar 1–2 s/it, sedangkan gambar berkualitas tinggi 1024x1536px dengan Hires Fix sekitar 14–20 s/it
Akan sangat keren kalau ini ditanamkan dan dijalankan di bingkai foto digital atau lukisan dinding
https://github.com/rvdveen/epaper-slow-generative-art/
Ini pencapaian yang mengagumkan, tetapi tentu saja butuh waktu sangat lama untuk menghasilkan gambar. Di README tertulis 11 jam
Misalnya membandingkan waktu, biaya, semua hardware yang diperlukan, dan listrik untuk membuat 100 gambar dengan 100 unit Pi Zero 2—bahkan tidak harus yang W—dengan sesuatu seperti PC kelas menengah rata-rata
Mungkin PC masih akan menang
Zero 2 terasa dipakai lebih sebagai tantangan daripada untuk kepraktisan, jadi Pi 4 atau 5 mungkin bisa menjadi patokan yang lebih baik
Mengesankan
Sungguh, zaman ketika bahkan lampu dan toaster menyimpan kecerdasan unggul sudah dekat
Saya sudah mengikuti bidang ini selama bertahun-tahun, tetapi 10 tahun terakhir ini menakjubkan
Alasan saya mengatakan “sudah” adalah karena akselerasi 6–18 bulan terakhir berada di level yang benar-benar berbeda lagi
Saya bukan khawatir tentang apa yang bisa dilakukan 2 tahun lagi, melainkan seberapa jauh laju perkembangannya akan makin cepat. Dan kemudian akan makin cepat lagi, dan lagi
Ayo bikin startup
Pada titik ini, bukankah upaya meregulasi teknologi sejenis Stable Diffusion sudah berakhir? Jika model dan infrastruktur inferensi bisa diperkecil hingga level yang bisa berjalan di PS2, tampaknya mustahil menghentikan teknologi ini tanpa negara pengawas totaliter, dan bahkan di negara seperti itu pun mungkin hanya nyaris bisa
Hal yang sama juga berlaku untuk “pembajakan” media atau ransomware
Negara sejak lama meregulasi hal-hal yang tidak bisa ditegakkan secara teknis secara murni
Saya tahu itu contoh hiperbola yang lucu, tetapi untuk berjalan di PS2 perlu diperkecil jauh lebih banyak daripada ini
Saya tidak sabar menunggu Stable Diffusion untuk Windows 3.1 hadir
Gila. Entah butuh 11 jam atau tidak, saya sama sekali tidak menyangka SD bisa berjalan di hardware seperti Pi Zero