298MB RAM में चलने वाला Stable Diffusion XL 1.0
(github.com/vitoplantamura)- OnnxStream एक अल्ट्रा-लाइट inference लाइब्रेरी है, जिसका लक्ष्य 512MB RAM वाले Raspberry Pi Zero 2 पर swap जोड़े बिना या disk पर intermediate data लिखे बिना Stable Diffusion 1.5 और SDXL 1.0 Base चलाना है
- inference engine और
WeightsProviderको अलग करके यहDiskNoCache,DiskPrefetch, और custom loading·caching·prefetching को सपोर्ट करता है, तथा HTTP से weights सीधे लेकर disk write के बिना भी प्रोसेस कर सकता है - SDXL 1.0 Base, Hugging Face Diffusers 0.19.3 implementation से export की गई ONNX files का उपयोग करता है, और Raspberry Pi Zero 2 पर 10-step image generation में लगभग 11 घंटे लगते हैं
- SDXL का VAE decoder FP32 में 4.4GB RAM उपयोग करता है, और FP16·UINT8 मात्र से समस्या हल नहीं होती; overlapping tile decoding और blending से memory usage 298MB तक घटाई गई
- CPU के आधार पर OnnxRuntime की तुलना में यह अधिकतम 55 गुना कम memory उपयोग करता है, लेकिन 0.5~2 गुना धीमा हो सकता है, और batch size 1 तथा कुछ ONNX operators तक सीमित है
OnnxStream का लक्ष्य और डिज़ाइन
- OnnxStream की शुरुआत Stable Diffusion 1.5 को Raspberry Pi Zero 2 पर चलाने की समस्या से हुई
- Raspberry Pi Zero 2, 512MB RAM वाला एक microcomputer है
- Stable Diffusion के लिए आम तौर पर अनुशंसित न्यूनतम RAM/VRAM 8GB है
- लक्ष्य शर्तें थीं: अतिरिक्त swap space नहीं, disk पर intermediate results offload नहीं
- सामान्य machine learning frameworks अक्सर inference latency या throughput को प्राथमिकता देते हैं, इसलिए RAM उपयोग बढ़ जाता है
- OnnxStream एक छोटी और आसानी से modify की जा सकने वाली inference लाइब्रेरी है, जिसका फोकस memory usage को न्यूनतम रखना है
- इसकी मुख्य संरचना inference engine और model weights देने वाले component
WeightsProviderके अलगाव पर आधारित है- derived classes model parameter loading, caching, और prefetching के तरीके implement कर सकती हैं
- custom
WeightsProviderHTTP server से data सीधे डाउनलोड करके disk read/write के बिना प्रोसेस कर सकता है - default implementations
DiskNoCacheऔरDiskPrefetchहैं
- CPU पर यह OnnxRuntime की तुलना में अधिकतम 55 गुना कम memory उपयोग करता है, जबकि 0.5~2 गुना धीमा रहता है
Stable Diffusion 1.5 चलाना
- Stable Diffusion 1.5 example, OnnxStream का उपयोग करके अलग-अलग VAE decoder precision में images generate करता है
- Raspberry Pi Zero 2 RAM में न समाने वाला एकमात्र component VAE decoder था
- कारण थे model के अंदर residual connection, बड़े tensor, और बड़े convolution
- single precision या half precision भर से यह Raspberry Pi Zero 2 RAM में fit नहीं हुआ
- समाधान था 8-bit static quantization
- W8A8 precision वाला VAE decoder image Raspberry Pi Zero 2 पर generate किया गया, और
MAX_SPEEDoption के साथ इसमें लगभग 1.5 घंटे लगे - तुलना के लिए W16A16 image, उसी latent का उपयोग करके PC पर generate की गई
Stable Diffusion XL 1.0 Base support
- OnnxStream का Stable Diffusion example SDXL 1.0 Base को सपोर्ट करता है, लेकिन Refiner शामिल नहीं है
- ONNX files, Hugging Face Diffusers लाइब्रेरी की SDXL 1.0 implementation से export की गई हैं, और उपयोग किया गया version 0.19.3 है
- SDXL 1.0 की computational cost, SD 1.5 से काफी अधिक है
- सबसे बड़ा अंतर यह है कि यह 512x512 के बजाय 1024x1024 images generate कर सकता है
- 12-core PC और 32GB RAM पर Hugging Face Diffusers से 10-step image generate करने में 26 मिनट लगते हैं
- SDXL के लिए सामान्य अनुशंसित न्यूनतम VRAM 12GB है
- OnnxStream, SDXL 1.0 को 300MB से कम RAM में चला सकता है, इसलिए यह Raspberry Pi Zero 2 पर भी काम करता है
- अतिरिक्त swap नहीं
- inference के दौरान disk write नहीं
- Raspberry Pi Zero 2 पर 10-step image generation में लगभग 11 घंटे लगते हैं
SDXL के लिए memory optimization
- SDXL 1.0 पर SD 1.5 जैसी optimization techniques लागू की गईं, लेकिन कुछ अंतर के साथ
- UNET model, Raspberry Pi Zero 2 पर 300MB से कम RAM में चलाने के लिए UINT8 dynamic quantization उपयोग करता है
- quantization का लक्ष्य बड़े intermediate tensors के एक विशेष subset तक सीमित है
- SDXL 1.0 का VAE decoder, SD 1.5 की तुलना में अधिक कठिन है
- SDXL 1.0 VAE decoder, SD 1.5 VAE decoder से 4 गुना बड़ा है
- OnnxStream में FP32 precision पर चलाने से यह 4.4GB RAM उपयोग करता है
- SD 1.5 में VAE decoder को UINT8 static quantization से 260MB RAM तक घटाया जा सकता था
- SDXL 1.0 VAE decoder में FP16 arithmetic पर overflow होता है, और activation का numerical range बड़ा होने के कारण UINT8 quantization से अच्छी quality images पाना कठिन है
- sdxl-vae-fp16-fix जैसे FP16 समाधान मौजूद हैं, लेकिन memory आधी होने पर भी 2.2GB रहती है, जो Raspberry Pi Zero 2 के लिए अब भी बड़ी है
- अंतिम समाधान था Hugging Face Diffusers के VAE decoder implementation से प्रेरित tile decoding
- diffusion result tensor का shape
(1,4,128,128)है - इसे
(1,4,32,32)shape वाले overlapping tensors 5x5, कुल 25 हिस्सों में बाँटा जाता है - हर tile, बाएँ और ऊपर वाले tile के साथ 25% overlap रखता है
- हर tile का decoding result
(1,3,256,256)tensor होता है, जिसे final image में blend किया जाता है - blending बंद करने पर tile boundaries दिखाई देती हैं, और चालू रखने पर final result में boundaries नहीं दिखतीं
- diffusion result tensor का shape
- इस तरीके से SDXL VAE decoder का RAM usage 4.4GB से 298MB तक घट गया
supported features और dependencies
- OnnxStream memory-efficient inference के लिए ज़रूरी features को compact रूप में जोड़ता है
- inference engine और
WeightsProviderका separation DiskNoCache,DiskPrefetch, customWeightsProvider- attention slicing
- 8-bit unsigned asymmetric percentile dynamic quantization
- W8A8 unsigned asymmetric percentile static quantization
- quantized model calibration
- FP16 support
- आम तौर पर उपयोग होने वाले 25 ONNX operators का implementation
- inference engine और
- operations sequentially execute होते हैं, लेकिन हर operator multithreaded तरीके से काम करता है
- implementation single source file और header file structure में है, और
XnnPackclass XNNPACK calls को wrap करती है - कुछ accelerated primitives XNNPACK पर निर्भर हैं
- MatMul
- Convolution
- element-wise Add/Sub/Mul/Div
- Sigmoid
- Softmax
performance comparison और सीमाएँ
- Stable Diffusion, तीन models से मिलकर बना है
- text encoder: 672 operations, 12.3 करोड़ parameters
- UNET: 2050 operations, 85.4 करोड़ parameters
- VAE decoder: 276 operations, 4.9 करोड़ parameters
- batch size 1 पर 10-step image generation के लिए निम्न execution आवश्यक हैं
- text encoder 2 बार
- UNET 20 बार
- VAE decoder 1 बार
- FP16 UNET के आधार पर OnnxStream और OnnxRuntime के memory/time अंतर बड़े हैं
- OnnxStream: लगभग 0.133GB, 18.2~19.8 सेकंड
- OnnxRuntime: 5.085~7.353GB, 7.28~12.8 सेकंड
- OnnxStream अधिकतम 55 गुना कम memory उपयोग करता है, लेकिन 0.5~2 गुना धीमा है
- FP32 text encoder में OnnxStream 0.147GB और OnnxRuntime 0.641GB उपयोग करता है
- FP32 VAE decoder में OnnxStream 1.004GB और OnnxRuntime 1.330~2.026GB उपयोग करता है
- तुलना के नतीजों के साथ कुछ शर्तें जुड़ी हैं
- OnnxRuntime का पहला run,
InferenceSessionreuse से पहले warm-up inference है - OnnxStream eager design पर आधारित है, इसलिए warm-up की अवधारणा नहीं है, लेकिन बाद के runs में OS के weights file cache का लाभ मिल सकता है
- OnnxStream अभी batch size 1 के अलावा inputs को सपोर्ट नहीं करता
- OnnxRuntime, UNET execution में batch size 2 का उपयोग करके पूरे diffusion को काफी तेज़ कर सकता है
- OnnxRuntime के
SessionOptionsमेंEnableCpuMemArena,ExecutionModeबदलने से test में कोई महत्वपूर्ण अंतर नहीं मिला - NCNN memory usage और inference time दोनों में OnnxRuntime से बहुत मिलता-जुलता था
- test environment था Windows Server 2019, 16GB RAM, 8750H CPU AVX2, 970 EVO Plus SSD, VMWare 8 virtual cores
- OnnxRuntime का पहला run,
Attention slicing और quantization
- UNET execution के दौरान attention slicing और VAE decoder की W8A8 quantization, Raspberry Pi Zero 2 पर memory को usable स्तर तक घटाने के लिए महत्वपूर्ण थीं
- attention slicing, multi-head attention में scaled dot-product attention की गणना करते समय पूरे
Q @ K^Tmatrix को materialize होने से रोकती है - जब UNET model का attention head count 8 हो, तो tensor shapes इस प्रकार होते हैं
Q:(8,4096,40)K^T:(8,40,4096)- पहला MatMul result:
(8,4096,4096) - FP32 precision में यह 512MB tensor बन जाता है
- समाधान यह था कि
Qको vertically split करके हर chunk के लिए attention operation चलाया जाएQ_slicedshape(1,x,40)हैx, 4096 कोonnxstream::Model::m_attention_fused_ops_partsसे भाग देने पर मिलने वाला मान है- default value 2 है, और इसे customize किया जा सकता है
- इस तरीके से FP32 UNET model का कुल memory usage 1.1GB से 300MB तक घट जाता है
- FlashAttention अधिक efficient विकल्प हो सकता है, लेकिन इसके लिए AVX·NEON जैसी supported architectures के लिए custom kernels लिखने पड़ेंगे, और इस स्थिति में XNNPACK को bypass करना होगा
model conversion और execution तरीका
- OnnxStream,
path_to_model_folder/model.txtमें defined model को execute करता है- सभी model operations ASCII format की
model.txtfile में होती हैं - weights files उसी folder में
.binfiles की series के रूप में मौजूद होनी चाहिए
- सभी model operations ASCII format की
Modelobject में कई optional parameters सेट किए जा सकते हैं- अलग weights provider चुनना
- quantized model activation clipping range file पढ़ना/लिखना
- model calibration mode
- FP16 arithmetic उपयोग
- UINT8 arithmetic उपयोग
- UINT8 dynamic quantization उपयोग
- attention slicing सक्षम करना
- attention split count सेट करना
- ONNX files को OnnxStream में उपयोग करने के लिए
onnx2txt.ipynbnotebook सेmodel.txtऔर.binweights files export की जाती हैं - PyTorch
nn.Moduleको ONNX में export करते समय कुछ शर्तें हैंtorch.onnx.exportcall के समयdynamic_axesखाली रखना चाहिए- OnnxStream dynamic shape inputs को सपोर्ट नहीं करता
- conversion से पहले ONNX Simplifier चलाने की कड़ी सिफारिश की जाती है
build और execution की तैयारी
- Linux, Mac, Windows, Termux पर Stable Diffusion example build किया जा सकता है
- Windows में
Visual Studio Toolsकाx64 Native Tools Command Promptउपयोग किया जाता है - Mac में
brew install cmakeसे cmake install करना पड़ता है
- Windows में
- पहले XNNPACK build करना आवश्यक है
- XNNPACK function prototypes कभी भी बदल सकते हैं, इसलिए एक specific समय के अनुरूप commit checkout करने की प्रक्रिया शामिल है
- संदर्भ समय
2023-06-27 00:00से पहले का master commit है
- इसके बाद OnnxStream repository clone करके
src/buildमें cmake से build किया जाता हैMAX_SPEED=ONXNNPACK_DIRसे XNNPACK clone path specify किया जाता है
MAX_SPEEDoption performance बढ़ा सकता है, लेकिन build के दौरान memory usage भी बढ़ाता है- Windows में लगभग 10% performance improvement
- Raspberry Pi पर 50% से अधिक performance improvement
- generated executable काम न करे, ऐसा संभव है, और Termux tests में समस्या देखी गई
- समस्या होने पर पहले
MAX_SPEED=OFFकरने की सलाह दी जाती है
- Stable Diffusion 1.5 example weights repository Releases से मिल सकती हैं, और उनका आकार लगभग 2GB है
- Stable Diffusion XL 1.0 Base weights Hugging Face से मिल सकती हैं, और उनका आकार लगभग 8GB है
Stable Diffusion example options
- example executable model selection, input/output, prompt, और decoding method को command-line options से नियंत्रित करता है
--xl: Stable Diffusion 1.5 की जगह Stable Diffusion XL 1.0 चलाना--models-path: Stable Diffusion model folder specify करना--ops-printf: inference के दौरान current operation को stdout पर print करना--output: output PNG file specify करना--decode-latents: diffusion छोड़कर specified latents file decode करना--prompt: positive prompt specify करना--neg-prompt: negative prompt specify करना--steps: diffusion step count specify करना--save-latents: diffusion के बाद latents को specified file में save करना
- Raspberry Pi और decoder से संबंधित options भी अलग से दिए गए हैं
--decoder-calibrate: केवल SD 1.5 में quantized VAE decoder calibration--decoder-fp16: केवल SD 1.5 में FP16 VAE decoder उपयोग--not-tiled: केवल SDXL 1.0 में tiled VAE decoder का उपयोग न करना--rpi: Raspberry Pi पर चलाने के लिए model configure करना--rpi-lowmem: केवल SDXL 1.0 में Raspberry Pi Zero 2 के लिए low-memory configuration लागू करना
1 टिप्पणियां
Hacker News की राय
दिलचस्प। मुख्य वाक्य यह है: “OnnxStream, OnnxRuntime की तुलना में मेमोरी उपयोग को 55 गुना तक कम रखते हुए भी गति में सिर्फ 0.5~2 गुना धीमा हो सकता है”
वीडियो मेमोरी/मेमोरी उपयोग और inference time के बीच यह trade-off Raspberry Pi जैसे सीमित मेमोरी वाले मामलों के अलावा दूसरी स्थितियों में भी फायदेमंद लग सकता है
अगर सच में इस weights unload तरीके से उसी मेमोरी में बड़ा batch size संभाला जा सके, तो latency बढ़ने के बावजूद throughput काफी बढ़ाया जा सकता है या नहीं, यह जानने की जिज्ञासा है
शायद “1.5~2 गुना धीमा” लिखना चाह रहे होंगे
inference आम तौर पर “क्या यह model इस system में fit होता है” के स्तर से आगे जाते ही memory bandwidth से बंध जाता है, इसलिए बड़े batch size से throughput बढ़ाने में यह technique शायद बहुत मदद न करे। संभव है कि एक ही instance पहले से memory controller को saturate कर रहा हो
हालांकि training में शायद मदद मिल सकती है
11 घंटे सुनकर पुराने समय में Amiga 500 पर ray tracing करने की याद आ गई। “final” render तो सचमुच रात भर चलने वाला काम था
लेकिन caustics चमकीली दिखने के बावजूद सांख्यिकीय रूप से काफी दुर्लभ phenomenon है, इसलिए सही result पाने के लिए render engine की limits हटाकर उसे पूरी रात चलने देना पड़ता है
नतीजा एक साधारण scene image होता है, जो कम skill वाले artist का है लेकिन जिसमें शानदार caustics हैं। लगता है day job जारी रखनी चाहिए
उसी समय लगा कि कोई दूसरा hobby चाहिए। ठीक उससे पहले किसी प्रतिभाशाली व्यक्ति ने OpenGL में scene पहले से देखने के लिए render करने वाला tool निकाला था। Amiga पर तो नहीं चलता, लेकिन मेरी machine पर किसी तरह चल गया
MBP पर invoke.ai से Stable Diffusion इस्तेमाल कर रहा हूं; SD parameters बेहतर तरीके से set करने के लिए कोई recommendation है? वही prompt और ऊपर से वही settings, जैसे Euler A वाला वही model, इस्तेमाल करने पर भी internet पर दिखने वाली image quality के आसपास भी नहीं पहुंच पा रहा
basic SD 1.5 के लिए तेज होने की वजह से Volta इस्तेमाल करता हूं: https://github.com/VoltaML/voltaML-fast-stable-diffusion/com...
सच में अच्छी SD 1.5 image quality fine-tuned models, LoRA, ControlNet और दूसरे enhancement features खुलकर इस्तेमाल करने पर आती है। जैसे किसी base image को structure के लिए follow कराना, या image के खास regions के हिसाब से prompts देना। InvokeAI में भी असल में बहुत features हैं और node UI में ऐसे कई enhancement features छिपे हैं, लेकिन Volta जैसे दूसरे UI उन्हें ज्यादा सीधे expose करते हैं
आम तौर पर impressive fine-tuned models default weights की तुलना में काफी कम general-purpose होते हैं, लेकिन वास्तविक इस्तेमाल में यह बड़ी समस्या नहीं होती और results काफी बेहतर हो सकते हैं
Invoke या Nvidia graphics card की तुलना में समय और memory ज्यादा लगती है, लेकिन बहुत खराब नहीं है। standard 512x768px quality image लगभग 1~2 s/it, और Hires Fix के साथ high-quality 1024x1536px image लगभग 14~20 s/it होती है
इसे digital photo frame या wall-hanging picture में embed करके चलाया जाए तो वाकई शानदार लगेगा
https://github.com/rvdveen/epaper-slow-generative-art/
यह शानदार उपलब्धि है, लेकिन image generate करने में जाहिर तौर पर बहुत ज़्यादा समय लगता है। README में 11 घंटे लिखा है
उदाहरण के लिए, 100 Pi Zero 2 — W होना भी ज़रूरी नहीं — से 100 images बनाने में लगने वाला समय, लागत, ज़रूरी पूरा hardware और power, इसकी तुलना किसी औसत mid-range PC जैसी चीज़ से करना
शायद PC अब भी जीतेगा
Zero 2 practical use से ज़्यादा एक challenge के तौर पर इस्तेमाल हुआ लगता है, इसलिए Pi 4 या 5 बेहतर benchmark हो सकते हैं
प्रभावशाली
सचमुच, वह दौर नज़दीक है जब बल्ब और toaster तक में बेहतरीन बुद्धिमत्ता समाई होगी
मैं इस field को कई सालों से देख रहा हूँ, लेकिन पिछले 10 साल कमाल के रहे हैं
“रहे हैं” इसलिए कह रहा हूँ, क्योंकि हाल के 6–18 महीनों की acceleration तो बिल्कुल अलग ही level की है
मुझे चिंता इस बात की नहीं कि 2 साल बाद हम क्या कर पाएँगे, बल्कि यह कि progress की रफ्तार और कितनी तेज़ हो जाएगी। और फिर बार-बार और तेज़ होगी
चलो startup बनाते हैं
इस point पर क्या Stable Diffusion जैसी technologies को regulate करने की कोशिशें खत्म नहीं हो गईं? अगर models और inference infrastructure को इतना छोटा कर दिया जाए कि वे PS2 पर भी चल सकें, तो किसी totalitarian surveillance state के बिना इस technology को रोकना असंभव लगता है, और ऐसी state में भी शायद मुश्किल से ही संभव हो
Media “piracy” या ransomware भी ऐसा ही है
States बहुत पहले से ऐसी चीज़ों को regulate करते आए हैं जिन्हें purely technical तरीके से enforce नहीं किया जा सकता
मुझे पता है कि यह मज़ेदार exaggerated example है, लेकिन PS2 पर चलाने के लिए इसे इससे कहीं ज़्यादा छोटा करना होगा
Windows 3.1 के लिए Stable Diffusion आने का इंतज़ार नहीं कर सकता
पागलपन है। 11 घंटे लगें या कुछ भी, मैंने बिल्कुल उम्मीद नहीं की थी कि Pi Zero जैसे hardware पर SD चल सकता है