Robert Važan

Lokálne jazykové modely na Linuxe s Ollamou

Konečne som si našiel čas spojazdniť lokálne jazykové modely alias chatboty, takmer rok po tom, čo som vyhlásil, že všeobecná umelá inteligencia je tu. Mám nízkonákladový hardvér a nechcel som sa s tým príliš babrať, tak som sa po chvíli váhania rozhodol pre Ollamu a Open WebUI, ktoré bežia na každom procesore (a teda nepotrebujú grafickú kartu) a dajú sa jednoducho a bezpečne nainštalovať do kontajnera. Ollama má veľkú knižnicu modelov, zatiaľ čo Open WebUI je bohaté na praktické funkcie. Ollama je postavená na intenzívne optimalizovanej knižnici llama.cpp.

Spojazdnenie je veľmi jednoduché. Nie je potrebná grafická karta. Pre oba projekty je k dispozícii návod na spustenie v dockerových kontajneroch. Pozrite príslušný príspevok na blogu Ollamy a Open WebUI README. Tieto návody som trochu upravil, aby namiesto dockeru používali podman (mám Fedoru) a aby sa po reštarte systému automaticky spustili:

podman run -d --name ollama --replace --restart=always \
    -p 11434:11434 -v ollama:/root/.ollama --stop-signal=SIGKILL \
    docker.io/ollama/ollama
podman run -d --name open-webui --replace --restart=always \
    -p 3000:8080 -v open-webui:/app/backend/data \
    --add-host=host.docker.internal:host-gateway \
    ghcr.io/open-webui/open-webui:main
systemctl --user enable podman-restart

Vytvoril som tiež pár aliasov/skriptov, aby bolo veľmi pohodlné spúšťať Ollamu z príkazového riadku, pretože bez aliasov je kontajnerové CLI rozhranie trochu rozvláčne:

podman exec -it ollama ollama run tinyllama

Prípadne môžete spustiť Ollama CLI v samostatnom kontajneri:

podman run -it --rm --add-host=host.docker.internal:host-gateway \
    -e OLLAMA_HOST=host.docker.internal docker.io/ollama/ollama run tinyllama

Načo sú lokálne jazykové modely?

Mal som donedávna predplatné GPT-4, ale sotva sa mi oplatilo. Ušetrílo menej než 10% môjho času a strácal som veľa času tým, že som sa s tým hrajkal. Lokálne jazykové modely sú zadarmo a sú čoraz lepšie. Potom sú tu všetky tie problémy s cloudom. Cloudový jazykový model sa môže kedykoľvek zmeniť, zmiznúť alebo môže zdražieť. Neustále si pýta moju spätnú väzbu a ďalšie dáta, čo slúži len prevádzkovateľovi, zatiaľ čo ja tým strácam kontrolu nad vlastnými dátami. Som dosť háklivý na súkromie a slobodu a hoci na "bezpečnostné" obmedzenia nenarážam často, je to nepríjemné, keď sa tak stane. Dúfam tiež, že miestne jazykové modely mi dajú väčšiu kontrolu nad výstupom, pretože aj keď je GPT-4 inteligentný, často je zbytočne kreatívny, keď len chcem, aby sa riadil pokynmi. Výstup z OpenAI API sa dá lepšie ovládať, ale API sa môže šialene predražiť, ak sa nejaký skript zasekne v slučke.

Výber modelov

Moje momentálne najobľúbenejšie modely sú llama3 8B pre všeobecné témy, codeqwen 7B pre programovanie, a dolphin-mistral 7B ako necenzurovaný model. 4B a menšie modely sú naozaj rýchle aj na bežnom procesore, ale sú popleteným, halucinujúcim zúfalstvom. Ak naozaj musíte, najmenej zlé sú phi3 4B a orca-mini 3B. Necenzurované modely sú slabšie, ale sú užitočné, keď iné modely odmietajú odpovedať.

Predvolená 4-bitová kvantizácia robí modely menšie a rýchlejšie so zanedbateľnou stratou presnosti. 3-bitová kvantizácia citeľne znižuje presnosť, ale stále je to lepšie než sa uchýliť k použitiu menšieho modelu. Modely s viac než 4 bitmi na parameter nemá zmysel používať. Ak máte výkonný hardvér, radšej použite väčší model.

Žiaden lokálny model momentálne nevie hovoriť pekne po slovensky. Najbližšie sú llama3 70B a command-r-plus 104B, ale málokto má dnes hardvér, aby ich spustil lokálne. Zatiaľ si ich môžete vyskúšať v HuggingChate. Komunikáciu v slovenčine definitívne vyrieši až špecializovaný model pre slovenčinu, ktorý nemusí byť veľký, aby produkoval kvalitný výstup. Aj keď súčasné lokálne modely nevedia po slovensky hovoriť, stále slovenčine rozumejú. Aj tie so 7B parametrami vedia pomerne dobre prekladať zo slovenčiny do angličtiny.

Open WebUI poskytuje pohodlné používateľské rozhranie pre vytváranie vlastných modelových súborov Ollamy, ktoré možno použiť na vyladenie parametrov generovania. To je dôležité pri menších modeloch, ktoré sú často neisté. Táto neistota sa prejavuje tak, že výstupná distribúcia pravdepodobnosti (rozdelenie pravdepodobnosti) je príliš široká. Kompenzujem to utiahnutím dostupných parametrov (temperature, top_k, top_p), čo výstupnú distribúciu zúži. Vytvoril som si aj vlastné modelové súbory s greedy výberom tokenov (top_k = 1) pre prípady, keď absolútne nechcem žiadnu kreativitu. Pozor, zúženie distribúcie výstupu je hack, ktorý vedie menšie modely k repetitívnemu výstupu, tak ho používajte opatrne. Dostatočne veľké modely sú si isté svojím výstupom a mali by sa ovládať len pomocou inštrukcií. To isté platí pre menšie modely používané na kreatívne účely. Pokiaľ ide o ostatné parametre, odstránil som obmedzenie dĺžky výstupu (num_predict = -1) a zmiernil som repeat_penalty, aby som modelu umožnil výstup repetitívneho kódu, keď to potrebujem. Zväčšil som aj veľkosť kontextu (num_ctx) nad predvolených 2048, pretože Ollama má problémy efektívne spracovať konverzácie presahujúce kontextové okno, ale pozor na spotrebu pamäte. Llama3 podporuje do 8K tokenov v kontexte (1GB RAM), Codeqwen do 64K (4GB celkom, 16K na 1GB) a Mistral do 32K (4GB celkom, 8K na 1GB). Vlastné modelové súbory možno použiť aj na úpravu systémových inštrukcií, ale to môže poškodiť výkon modelu, ak model nebol trénovaný s rozmanitými systémovými inštrukciami.

Bez ohľadu na to, ako starostlivo ich vyladíte, malé lokálne modely nemajú veľa praktického uplatnenia. V porovnaní s cloudovými modelmi je lokálny 7B model technologickým demom v alfa štádiu. Nikto by neplatil za GPT4, keby bezplatný 175B ChatGPT bol dostatočne dobrý, tak čo očakávate od 7B modelu? Ľudia väčšinou používajú lokálne modely na zábavu, najmä na konverzáciu s fiktívnymi postavami. Serióznejšie aplikácie v biznise sa opierajú o fine-tuning, ktorý je v súčasnosti pre domácich používateľov nepraktický a bez špičkovej grafickej karty takmer nemožný. Menšie modely sú však použiteľné pre jednoduché otázky a odpovede a úvod do rôznych tém. Môžu slúžiť na skriptovanie v prirodzenom jazyku ak je úloha dostatočne jednoduchá, jazykový model je riadne inštruovaný a má k dispozícii príklady. Sumarizácia a indexovanie dokumentov sú realistickou aplikáciou, ale na rýchle spracovanie dlhých dotazov potrebujete grafickú kartu. Doplňovanie kódu by mohlo fungovať dobre, ak ho editor podporuje a máte grafickú kartu na rýchle spracovanie kontextu. Doplňovanie textu by mohlo fungovať aj bez grafickej karty, ak text píšete zhora nadol.

Optimalizácia rýchlosti

Hardvér je mimochodom veľký problém. Mám niekoľko mesiacov starý ale lacný počítač. Nie som fanatik do jazykových modelov ako niektorí z /r/LocalLLaMA, ktorí si stavajú počítače s viacerými grafickými kartami len preto, aby mohli používať tie najväčšie jazykové modely. Grafické karty sú dnes šokujúco drahé a nemajú ani zďaleka dostatok pamäte. Preto som sa rozhodol pre lacný počítač s integrovanou grafikou a väčšim množstvom RAM-ky. Nevýhodou je, že jazykové modely sú na tomto počítači pomalé. Rýchlosť meraná tokenmi za sekundu je asi o 20-35 % nižšia, než by ste tipovali podľa veľkosti modelu a rýchlosti pamäte, pravdepodobne kvôli spracovaniu kontextu, ale aj preto, že niektoré operácie nie sú limitované rýchlosťou pamäte. Modely sa stávajú sotva použiteľnými pri rýchlosti nad 10 tokenov za sekundu, čo je približne toľko, čo môžete očakávať od 7B modelu, ako je Mistral, na 2-kanálovej DDR4-3200.

Aby som veci zrýchlil, minimalizujem systémové inštrukcie, ak ich model dobre podporuje, len na dve vety: jedna dáva modelu rolu (asistent) a druhá žiada o stručnosť a presnosť. Ollama spracuje systémové inštrukcie len pri prvom dotaze a potom ich už má v kontextovej pamäti, ale stručnosť inštrukcií stále trochu pomáha, najmä pri prvom dotaze. Držím sa jedného modelu, aby som sa vyhol zdĺhavému prepínaniu modelov v Ollame a s tým spojenému vymazaniu kontextovej pamäte. Kontextová pamäť (nazývaná aj KV cache) je nevyhnutná pre rýchle reakcie modelu v dlhších rozhovoroch. Ak sa vymaže, Ollama ju zrekonštruuje opätovným vyhodnotením celej konverzácie od začiatku, čo je bez grafickej karty pomalé. Open WebUI vie používať vybraný jazykový model na generovanie popisov v histórii konverzácií, ale je to taký zabijak výkonu, že som túto funkciu radšej vypol. V predvolenom nastavení Ollama po 5 minútach nečinnosti zavrie model a zahodí kontextovú pamäť. Toto je možné nakonfigurovať ako "Keep Alive" v nastaveniach Open WebUI a ja som nastavil vyšší Keep Alive, aby som zabezpečil rýchlu odozvu, aj keď sa ku konverzácii vrátim o niečo neskôr.

Real-time priorita

Výkonu llama.cpp veľmi škodí, ak súťaží o procesor s inými procesmi. Beží až 2x pomalšie, aj keď je konkurenčný proces v cgroupe s nízkym CPU podielom. Najpravdepodobnejšou príčinou je, že konkurenčné procesy občas zdržiavajú vlákna llama.cpp, čo spôsobuje, že niektoré vlákno zaostáva a ostatné vlákna naň potom musia čakať. Toto je ťažké odstrániť na úrovni llama.cpp, prinajmenšom pre architektúru transformátora, ktorá vyžaduje, aby implementácia opakovane paralelizovala malé bloky výpočtov a synchronizovala vlákna po každom bloku. Ak to chceme napraviť na úrovni systému, môžeme sa pohrať s konfiguráciou schedulera, konkrétne s real-time prioritami. Ja mám neustále spustené nejaké procesy na pozadí, ktoré vyťažujú procesor, tak som si dal tú námahu nakonfigurovať pre Ollamu real-time prioritu:

sudo podman run -d --name ollama --replace --restart=always \
    -p 11434:11434 -v ollama:/root/.ollama --stop-signal=SIGKILL \
    --cap-add=SYS_NICE --entrypoint=/bin/sh \
    docker.io/ollama/ollama \
    -c chrt 1 /bin/ollama serve
sudo systemctl enable podman-restart

Bezrootový podman ignoruje SYS_NICE, tak ho spúšťajte so sudo. Vyskúšal som round-robin scheduler (predvolený v chrt) aj FIFO scheduler, ale nevidím medzi nimi žiadny rozdiel. Zaujímavé je, že na nezaťaženom systéme sú procesy s real-time prioritou o 10-20 % pomalšie ako tie s normálnou prioritou. Pravdepodobne je to tak preto, že scheduler pre procesy s normálnou prioritou je o niečo inteligentnejší, pokiaľ ide o rovnomerné rozloženie záťaže na všetky jadrá procesora. Masívne zrýchlenie na vyťaženom systéme však stojí za to. S real-time prioritou dosahuje Ollama takmer rovnaký výkon ako na nezaťaženom systéme. Systém zostáva stabilný, pretože mám procesor s hyperthreadingom, ktorý Ollama nepoužíva, takže zdanlivé využitie procesora je len 50% a systém má naďalej kapacitu na spúšťanie ostatných procesov. Napriek tomu som zaznamenal citeľné rušenie ostatných real-time procesov, najmä audio subsystému. Upozorňujem, že bez hyperthreadingu Ollama s real-time prioritou pravdepodobne spôsobí pád systému.

Počet vlákien

Ollama alokuje jedno vlákno na každé fyzické jadro procesora, ale to sa dá nakonfigurovať vo vlastnom modelovom súbore. Moje experimenty ukazujú, že modely môžu fungovať aj s menším počtom vlákien, pretože úzkym hrdlom je rýchlosť pamäte. Na nezaťaženom systéme dokonca Ollama generuje text o niečo rýchlejšie, ak má o jedno vlákno menej než je počet jadier. Spracovanie dotazov ale určite dokáže využiť všetky dostupné jadrá. Zvýšenie počtu vlákien nad jedno vlákno na jadro v praxi len zhoršuje výkon, pravdepodobne preto, že paralelelné spracovanie inštrukcií v procesore plne vyťaží všetky jadrá a ďalšie vlákna len spôsobujú problémy s koordináciou vlákien. Preto je najlepšie zostať pri predvolenom počte vlákien.

Integrovaná grafika

Nechať modely bežať na procesore (CPU) je bezproblémové riešenie, ale všetky počítače bez samostatnej grafickej karty majú integrovanú grafiku (iGPU) v procesore, ktorá je napriek svojej malej veľkosti a nízkej spotrebe energie rýchlejšia než všetky jadrá procesora dokopy. S trochou šikovnosti a šťastia môžete integrovanú grafiku využiť na zvýšenie výkonu.

Hoci integrovaná grafika môže dynamicky alokovať pamäť v počítači prostredníctvom UMA/GTT/GART a llama.cpp to podporuje skrz parameter pri kompilácii, v súčasnosti nie je v Ollama žiadna podpora UMA. Jedinou možnosťou je alokovať časť pamäte RAM ako vyhradenú VRAM v BIOSe, ak to váš systém podporuje (niektoré notebooky to nepodporujú). V mojom prípade je vyhradená VRAM prednastavená na mizerných 512MB, ale BIOS ju umožňuje nakonfigurovať na ľubovoľnú mocninu dvojky až do 16GB. Ja som sa rozhodol pre 8GB VRAM, čo postačuje pre kvantizovaný 7B model (4GB), kontextovú a vyrovnávaciu pamäť (1GB), pracovnú plochu a aplikácie (1-2GB) a nejakú rezervu (1GB). Multimodálny llava 7B je o niečo väčší (5GB), ale stále sa zmestí. Ak chcete spustiť 13B modely, budete musieť vyhradiť 16GB pamäte RAM ako VRAM.

Spôsob spustenia Ollamy s podporou integrovanej grafiky závisí od výrobcu procesora. Ja mám procesor AMD, takže tieto inštrukcie sú len pre AMD. Ak chcete, aby Ollama používala integrovanú grafiku na procesoroch AMD, budete potrebovať variant docker imidžu, ktorý obsahuje ROCm, čo je AMD platforma pre spúšťanie všeobecných výpočtov na grafických kartách. Samostatný docker imidž je potrebný, pretože ROCm zväčšuje Ollamu o 4GB (nerobím si srandu). Kontajner, v ktorom beží Ollama, budete musieť spustiť s niekoľkými parametrami navyše:

podman run -d --name ollama --replace --restart=always \
    -p 11434:11434 -v ollama:/root/.ollama --stop-signal=SIGKILL \
    --device /dev/dri --device /dev/kfd \
    -e HSA_OVERRIDE_GFX_VERSION=9.0.0 -e HSA_ENABLE_SDMA=0 \
    docker.io/ollama/ollama:rocm

ROCm má veľmi krátky zoznam podporovaných grafických kariet. Použité premenné (HSA_OVERRIDE_GFX_VERSION a HSA_ENABLE_SDMA) slúžia na oklamanie ROCm, aby bol ochotný pracovať s nepodporovanou integrovanou grafikou v mojom Ryzene 5600G. Pre iné nepodporované grafické karty možno budete premenné a verzie Ollama/ROCm musieť upraviť. Podľa toho, čo som čítal na tému prístupu ku grafickým kartám v kontajneroch, kontajner zostáva riadne sandboxovaný aj po zdieľaní /dev/dri a /dev/kfd.

Ak toto všetko urobíte a Ollama nehlási chybu, nespadne ani nezamrzne, mali by ste dosiahnuť príjemné zvýšenie výkonu. V mojom prípade pozorujem viac ako dvojnásobne rýchlejšie spracovanie dotazov, ktoré sa blíži k 40 tokenom za sekundu pre 7B model na nezaťaženom systéme. Spracovanie obrazových vstupov v llava je tiež 2x rýchlejšie, ale 17 sekúnd na jeden obrázok je stále nepraktická rýchlosť. Rýchlosť generovania textu je naďalej obmedzená rýchlosťou pamäte na 10 tokenov/sekundu, ale už ju neovplyvňuje konkurencia od iných procesov, čo je pre mňa zásadná výhoda integrovanej grafiky. Ollama stále využíva procesor, aj keď celý model beží na integrovanej grafike, ale zaťaženie procesora je teraz zanedbateľné.

Celá vec je však trochu vratká. Absencia podpory v ROCm znamená, že sa to môže pokaziť pri ktorejkoľvek budúcej aktualizácii. Modely bežiace na integrovanej grafike sa občas úplne vykoľaja a produkujú odpad, kým Ollamu nereštartujem. Aj keď to celé funguje, výstup sa trochu líši od toho, čo produkoval procesor (s top_k = 1) a prvé spustenie dotazu na integrovanej grafike produkuje mierne odlišný výstup od druhého a nasledujúcich spustení. Ollama niekedy nedokáže pri prepínaní modelov presunúť všetky vrstvy modelu na grafiku, hlásiac málo VRAM, ako keby časti predchádzajúceho modelu boli stále vo VRAM. Poškodzuje to výkon a časom sa to zhoršuje, ale reštartovanie Ollamy problém na chvíľu vyrieši. Presúvanie vrstiev Mixtralu na grafiku nefunguje. Model jednoducho zamrzne.

Čo môžeme očakávať v budúcnosti

Lokálne jazykové modely určujú priority pre budúce nákupy hardvéru. Nič iné v mojom počítači netrpí hardvérovými obmedzeniami tak ako lokálne jazykové modely. Ak ste ochotní platiť stovky eur ročne za prístup ku cloudovým modelom, môžete radšej minúť tisíc a viac eur na nový hardvér pre lokálne modely a získať výhody lokálnych modelov, ako je súkromie, nastaviteľnosť a možnosť voľby modelu. Lokálne modely tiež eliminujú limity na počet dotazov a sieťovú latenciu cloudových modelov.

V súčasnosti najrýchlejšie pamäte DDR5 zdvojnásobujú rýchlosť oproti DDR4, vďaka čomu sú Mixtral a husté 13B modely dostatočne rýchle, ale väčšie husté modely nebudú praktické bez zvýšenia počtu pamäťových kanálov, čo je v súčasnosti zriedkavé a drahé. Samostatné grafické karty majú širokú pamäťovú zbernicu, ale zas obmedzujú veľkosť modelu kapacitou VRAM. Na 30B+ modely potrebujete 2x16GB a na 70B modely potrebujete 3x16GB. Grafické karty s 24GB pamäte sú neprimerane drahé. Jediná grafická karta s 8-16GB pamäte je stále užitočná pre multimodálne modely ako llava a pre dlhé dotazy, ale aj niektoré integrované grafiky budú na tieto účely dostatočne rýchle. Novo oznámené procesory s integrovanou rýchlou pamäťou umožnia prevádzkovať 30B+ modely na integrovanej grafike.

Existuje tiež veľa možností pre optimalizáciu softvéru a modelov a práve tu vidím príležitosť v najbližšom roku alebo dvoch výrazne zvýšiť výkon. Mistral ukazuje, že dobre trénovaný 7B model môže mať pôsobivé výsledky. Správne trénovaný 3B model by sa mu mohol priblížiť a zároveň by mohol poskytnúť bleskové reakcie na veľké dotazy. Dopĺňanie kódu a textu je atraktívnou aplikáciou pre lokálne jazykové modely, ale podpora editorov je stále nedostatočná a často ťažkopádna. Doménové modely by mohli vo svojej oblasti prekonať oveľa väčšie všeobecné modely, ale v súčasnosti neexistujú takmer žiadne špecializované modely. Ľahký lokálny fine-tuning by mohol doladiť štýl a konvencie bez potreby detailných inštrukcií, ale fine-tuning zatiaľ nie je zrovna vec stlačenia jedného tlačidla. Prístup k nástrojom, internetu a podporným databázam môže modelom pomôcť prekonať ich veľkostné obmedzenia. RWKV, Mamba, a ternárne siete sľubujú rýchlejšie generovanie textu a ďalšie výhody. Špekulatívne vykonávanie jazykových modelov môže veľmi pomôcť, ale žiaden otvorený model ho nepoužíva. Beam search by bol v lokálnych modeloch v podstate zadarmo. Integrované grafiky a samostatné grafické karty od AMD a Intelu by mohli pomôcť pri multimodálnych modeloch, dlhých dotazoch a energetickej efektívnosti, ale väčšina z nich zostáva nevyužitá pre nedostatočnú softvérovú podporu. MoE a iné riedke modely sú nedostatočne využívané.

Zlepšovanie softvérovej podpory a efektívnosti je niečo, v čom som veľmi optimistický. Táto oblasť je atraktívna pre veľa talentovaných ľudí. Ja ale nebudem prispievať ničím iným než hlásením chýb, pretože sa musím venovať vlastným veciam a jazykové modely sú pre mňa len optimalizáciou produktivity. Peniaze na trénovací hardvér pre veľké otvorené modely budú naďalej tiecť od vlád a firiem, ktoré sú citlivé na bezpečnosť svojich dát. Hardvér sa bude tiež zlepšovať, aj keď nie tak rýchlo, pretože zmena hardvéru je drahá, a tiež preto, že výrobcovia sa nechcú predčasne zaviazať k podpore výpočtových primitív, ktoré by sa mohli v budúcom roku stať zastaranými v dôsledku optimalizácie softvéru.

Som presvedčený, že lokálne jazykové modely budú neustále a rýchlo napredovať, ale cloudové modely nezmiznú. Vysoká riedkosť a ďalšie optimalizácie nakoniec umožnia cloudovým modelom dosiahnuť veľkosť vyhľadávačov. Namiesto nahradenia cloudových modelov budú lokálne modely skôr hľadať uplatnenie v oblastiach, kde je nasadenie cloudových modelov z nejakého dôvodu problematické.