Lue ensin tällä sivulla olevat yleisohjeet. Tarkemmat tiedot löytyvät tehtäväkohtaisista ohjeista. Jokaiseen tehtävään on saatavilla oma zip-tiedosto, josta löytyvää koodia voit käyttää ratkaisujesi pohjana.
| Tehtävä | Palautuksia | Alustavat pisteet | Pisteet | Max. | Taso | Suos. | Määräaika |
|---|---|---|---|---|---|---|---|
| LLM9a: CPU-optimointi | |||||||
Toteuta suuren kielimallin kehotteen tehokas prosessointi käyttäen kaikkea CPU:lla saatavilla olevaa yksinkertaisen tarkkuuden laskentatehoa. |
|||||||
| – | – | – | 5 + 2 | ★★ | Suos. | 2026-05-31 klo 23:59:59 | |
Tässä harjoituksessa optimoidaan suuren kielimallin, tässä tapauksessa LLaMA-malliperheeseen kuuluvan kielimallin toteutusta. Sivun alaosasta löydät lyhyen kuvauksen rinnakkaislaskennan ja LLaMAn kaltaisten transformer-pohjaisten kielimallien menestyksen välisestä yhteydestä.
Yleisesti ottaen suuren kielimallin työ voidaan jakaa kahteen vaiheeseen: käyttäjän antaman kehotteen käsittelyyn ja uusien tokenien luontiin kehotteen perusteella. Tässä harjoituksessa keskitymme ensimmäiseen vaiheeseen. Jo pelkästään käyttäjän antaman kehotteen prosessoinnilla voidaan saada aikaan mielenkiintoisia sovelluksia, esimerkiksi ennustaa, kuinka yllättävä jokin teksti on. Lisätietoa on alla demo-osiossa.
Tätä harjoitusta varten on valmisteltu perusversio LLaMA 2 -mallista, joka perustuu Andrej Karpathyn llama2.c:hen. Tehtävänä on tunnistaa pullonkaulat ja soveltaa tällä kurssilla opittuja tekniikoita kehotteen käsittelyn nopeuttamiseksi.
Toteuta seuraava funktio:
void llm(LLamaConfig config,
LLamaParameters params,
const std::vector<token_t>& tokens,
std::vector<float>& logits);
Parametrit ovat seuraavat:
config Tietorakenne, jossa kuvataan LLaMA-mallin konfiguraatio, erityisesti painotusmatriisien
muodot. Katso tietorakenteen määritelmä tiedostosta llm.hparams Tämä tietorakenne sisältää mallin varsinaiset painotukset.tokens Tokenijono, joka toimii mallin kehotteena.logits Tämä taulukko tulee täyttää ennustetuilla arvoilla (logits).Voit olettaa, että konfiguraatiossa kuvataan aina kelvollinen verkko (eli kaikki koot > 0). Voit olettaa, että verkon
ulottuvuudet (config.dim ja config.hidden_dim) ovat 16:n monikertoja.
Tämän harjoituksen pohjalla käytetään LLaMA-mallin perusversiota, joka käsittää yksisäikeisen, skalaarisen prosessoinnin. Kunkin tokenin kohdalla tämä prosessi koostuu seuraavista vaiheista:
dim-ulotteiseksi alkutilaksi.
rms_attention.query-, key- ja value-vektorit
matriisikertolaskulla käyttäen vastaavasti painomatriiseja query_weight_matrix,
key_weight_matrix ja value_weight_matrix.query- ja key-vektoreita käyttämällä pyöriviä
sijaintiupotuksia (rotary positional embeddings).
query- ja key-vektoreiden
välinen samankaltaisuus jokaisen aiemman tokenin kohdalla pistetulon avulla. Muunna nämä pisteytyksiksi,
joiden summa on yksi, käyttämällä
softmax-funktiota.
value-vektoreiden painotettu summa käyttäen saatuja pisteytyksiä painoina.out_weight_matrix ja lisää tulos piilotilaan.
rmsnorm-normalisointia.hidden_dim-ulotteiseen vektoriavaruuteen
kertomalla se painomatriiseilla feed_forward_w1 ja feed_forward_w3. Yhdistä nämä
kaksi vektoria käyttämällä
SwiGLU-operaatiota alkioittain.dim käyttämällä painomatriisia
feed_forward_w2 ja lisää tulos piilotilaan.rmsnorm-funktiota skaalausparametreilla
RmsFinal.num_tokens × dim -kokoista tulosteen painomatriisia TokenOutputMatrix.softmax-funktiota.
Näiden työkalujen toteutukset ovat tarjolla llm.h-tiedostossa. Voit vapaasti käyttää näitä tai
vaihtoehtoisesti omaa toteutustasi, jos uskot sen parantavan suorituskykyä.
Osassa testiasetelmista tietyt kielimallin osat ovat poissa käytöstä. Nämä voivat auttaa toteutusvirheiden löytämisessä.
Tutustu oheiseen mallitoteutukseen. Yritä paikantaa eniten aikaa vievät kohdat sekä kohdat, joissa laskentaa
voidaan helposti rinnakkaistaa. Jo lisäämällä pragman #pragma omp parallel for oikeisiin paikkoihin
ansaitset tehtävästä vähintään yhden pisteen.
Toteutusta voi kehittää edelleen hyödyntämällä käskytason rinnakkaisuutta (ILP) ja vektorointia. Täysien pisteiden saaminen edellyttää kuitenkin tehokkaampaa rinnakkaistamisstrategiaa.
Yleisesti suurten kielimallien vaikuttavuus perustuu niiden kykyyn tuottaa ihmismäistä tekstiä, mutta kiinnostavia asioita voidaan silti tehdä myös kehotteen prosessoinnin osalta. Koska ennustettuja todennäköisyysjakaumia voidaan verrata toteutuneeseen tekstiin, on mahdollista selvittää, mitkä kehotteen osat olivat verkon kannalta odottamattomimpia.
Tehtäväpohjan demotyökalu, jota kutsutaan käskyllä ./grading demo, visualisoi kunkin tekstin tokenin yllättävyyden.
Esimerkiksi ./grading demo "Once upon a time, there was a test story." tuottaa seuraavan tulosteen:
Once upon a time, there was a test story.
Huomaa, että tätä varten tulee ladata esikoulutettu malli verkosta.
Nykyaikaisilla koneoppimismenetelmillä voidaan saavuttaa loistavia tuloksia, jos saatavilla on suuri määrä luokiteltua koulutusaineistoa. Suurin osa olemassa olevasta datasta on kuitenkin luokittelematonta, mutta se sisältää silti arvokasta tietoa, jota voidaan oppia sen rakenteesta. Juuri tähän autoregressiivinen kielimalli pyrkii: ennustamaan tietyn sanajonon perusteella, mikä sana tulee seuraavaksi. Yleisemmin teksti jaetaan tokeneiksi, jotka voivat olla sanoja, tavuja tai jopa yksittäisiä kirjaimia. Tätä varten koulutusaineistoksi tarvitaan ainoastaan suuri tekstikorpus, joka voidaan kerätä esimerkiksi Wikipediaa haravoimalla.
Luultavasti yksinkertaisin tapa mallintaa tällainen tehtävä on soveltaa Markovin oletusta, jossa jonon seuraava elementti riippuu ainoastaan sitä edeltävästä elementistä. Tällöin kielimalli on vain valtava todennäköisyystaulukko: tämän tokenin perusteella tässä ovat todennäköisyydet seuraavalle tokenille. Malli on äärimmäisen tehokas, mutta mallinnuskapasiteetiltaan melko rajoittunut. Tällaista ratkaisua voidaan käyttää esimerkiksi luomaan tekaistuja sanoja, joissa ilmenee todellisen kielen ominaispiirteitä, muttei tuottamaan johdonmukaisia lauseita.
Ilmaisuvoimaa voisi lisätä pidentämällä Markovin mallin konteksti-ikkunaa, jolloin pelkän viimeisen tokenin
tarkastelun sijaan tarkastellaan viimeisiä n:ää tokenia. Tämä valitettavasti johtaa muistinkäytön (samoin kuin
koulutusaineiston tarpeen) eksponentiaaliseen kasvuun konteksti-ikkunan koon mukana. Vaihtoehtona on pakata
konteksti state-vektoriin ja tehdä ennustuksia sen perusteella. Korkean tason idea on:
for token in sequence do
state = update_state(state, token)
prediction = output_prediction(state)
Ilmaisuvoiman parantamiseksi, tyypilliseen syväoppimisen tapaan, useita näistä prosesseista voidaan pinota
kerroksittain siten, että kerroksen l tila state[l, p] jonon kohdassa p on
funktio edellisen aika-askeleen tilasta ja edellisen kerroksen tilasta samassa aika-askeleessa:
state[l, p] = update_state(state[l, p-1], state[l-1, p]).
Tämän lähestymistavan soveltamisessa pidempiin merkkijonoihin on useita haasteita; erityisesti funktion
update_state
täytyy olla huolellisesti valittu, jotta signaalit voivat kulkea pitkiä matkoja. Lisätietoa kiinnostuneille löytyy
Andrej Karpathyn hienosta yhteenvedosta, joka
käsittelee toistuvien neuroverkkojen toimintaa.
Valitettavasti yksi haaste säilyy: kuinka näitä verkkoja koulutetaan valtavilla saatavilla olevilla tietomäärillä? Verkon päivitysrakenne tarkoittaa, että riippuvuuksia on olemassa sekä edeltävään kerrokseen että saman kerroksen edelliseen tilaan. Tämä on vastakohta uudemmalle Transformer-arkkitehtuurille, jossa seuraava tila lasketaan kaikkien aiempien tilojen perusteella vain edellisessä kerroksessa, mikä poistaa yhden riippuvuusakselin. Tämän seurauksena, vaikka eri kerrosten käsittely on tehtävä peräkkäin, kaikki saman kerroksen sijainnit voidaan käsitellä rinnakkain. Tämän ansiosta meillä on nyt suuria kielimalleja, jotka on koulutettu biljoonilla tokeneilla.
Koneoppimismenetelmän menestys tai epäonnistuminen voi siis olla vahvasti riippuvainen nykyisten laitteistojen suorituskyvystä. Tämä ilmiö on tullut tunnetuksi nimellä Hardware Lottery.