Avoimen lähdekoodin kirjastojen käyttäminen hermoverkoissa C++

Avoimen lähdekoodin kirjastojen käyttäminen hermoverkoissa C++

Avoimen lähdekoodin kirjastojen käyttäminen hermoverkoissa C++

Johdanto

Moderni AI tulee usein yrityksiin Python:n, kannettavien, demoympäristöjen ja sen ymmärrettävän jännityksen kautta, että mallin toimii ensimmäistä kertaa. Tämä vaihe on todellinen, hyödyllinen ja jopa hieman maaginen. Siellä uteliaisuus on halpaa ja iteraatio nopeaa. Mutta todellisen tuotteen käyttöikä ei pääty esittelyyn. Malli, jonka täytyy palvella asiakkaita, sopia taustajärjestelmään, toimia tehdaslaitteistolla, elää pöytäkoneen sisällä tai selviytyä huonoista verkkoolosuhteista, ei ole enää vain malli. Siitä tulee järjestelmän komponentti, ja järjestelmissä suunnittelukypsyys alkaa olla tärkeä.

Se on hetki, jolloin C++ palaa huoneeseen. Tuotanto esittää kysymyksiä, joita korkeamman tason kokeilu voi lykätä vain niin kauan. Kuinka paljon muistia prosessi todella vaatii? Mikä on vakaan tilan latenssi kuormitettuna? Kestääkö käynnistysaika automaattisen skaalauksen? Voiko suoritusaika elää olemassa olevan alkuperäisen sovelluksen sisällä? Voimmeko lähettää saman päättelypolun palvelimelle, reunalaatikkoon ja operaattorityöasemalle rakentamatta koko tuotetta uudelleen tutkimuspinon ympärille?

Avoimen lähdekoodin kirjastot tekevät tämän siirtymisen mahdolliseksi luovuttamatta hallintaa toimittajan mustalle laatikolle. Ne antavat meille vakaat suoritusajat, tensorabstraktiot, optimoidut ytimet, kvantisoidut suorituspolut, laitteistotietoiset taustaohjelmat ja viimeaikaisella LLM aikakaudella yllättävän kykeneviä paikallisia päättelykoneita. Mutta kirjastojen runsaus voi myös tehdä maisemasta hämmentävää. Insinöörit kysyvät usein, mikä kirjasto on paras, kun parempi kysymys on, mikä kirjasto on rehellinen edessämme olevasta työstä.

Tämä artikkeli vie tätä maadoitettua polkua. Tarkastelemme C++:n tärkeimpiä kirjastoja AI:ssa suunnittelupersoonallisuuksina, joilla on vahvuuksia, kuolleita kulmia ja toimintaoletuksia. Tavoitteena on loppujen lopuksi ymmärtää, milloin ONNX Runtime, LibTorch, oneDNN, OpenVINO, TensorFlow Lite ja llama.cpp auttavat, kun jokaisesta tulee liian painava valinta, kun jokaisesta tulee liian kapeaa.

Miksi AI järjestelmät palaavat jatkuvasti C++

AI-toimituksella on rytmi, joka kannattaa nimetä selkeästi, koska kun näet sen, monet arkkitehtuurivalinnat ovat helpompia ymmärtää. Ensin on löytövaihe. Tutkijat ja tuoteinsinöörit opettelevat edelleen, mitä malli voi tehdä, mitä tietoja se tarvitsee ja missä arvo voi todellisuudessa olla. Siinä vaiheessa ilmaisukyky voittaa kurin. Nopea kokeilu, rikas Python-työkalut ja joustavat tutkimuskehykset ovat juuri sitä, mitä tiimi tarvitsee.

Sitten tulee vähemmän lumoava toinen vaihe, jossa prototyyppi alkaa kerätä velvoitteita. Tukiryhmän on ymmärrettävä epäonnistumiset. SRE-tiimi haluaa ennakoitavissa olevan käynnistyksen ja muistin käyttäytymisen. Rahoitus haluaa tietää, onko tarjouslasku väliaikainen piikki vai pysyvä vuoto. Sulautettu asiakas kysyy, voiko malli toimia offline-tilassa. Turvatarkistus kysyy, mitä tarkalleen lähetetään binaarissa ja mitkä osat voidaan tarkastaa. Yhtäkkiä malli lakkaa olemasta tutkimusartefakti ja siitä tulee tuotantoympäristön kansalainen.

C++ palaa takaisin tässä vaiheessa, koska se antaa tekniikan vastata konkreettisiin kysymyksiin sen sijaan, että he heilauttaisivat niitä. Alkuperäinen palvelu voi hallita allokointistrategioita, säikeen pooleja, ABI-rajoja, pakkauksia, CPU-kohtaisia ​​optimointeja ja integrointia olemassa oleviin suorituskykyherkkiin alijärjestelmiin. Sillä kontrollilla on eniten merkitystä siellä, missä se on tarpeen, ja siellä on erittäin vaikeaa teeskennellä retoriikalla.

Hyödyllinen vastaesimerkki auttaa tässä. Jos tiimisi rakentaa kevyesti ladattua sisäistä dokumenttien luokittelijaa, joka suoritetaan kerran tunnissa, pienimmän vastuksen polku voi olla Python-palvelu, jossa on vakaa käyttökehys ja hyvin vähän alkuperäistä koodia. Siinä ei ole mitään häpeällistä. Toisaalta, jos sama tiimi upottaa päätelmiä viiveherkän C++-työpöytäsovelluksen sisään, toimittaa reunalaitteeseen, jolla on rajalliset resurssit, tai lisää mallin suorituksen suoraan kuumaan taustapolkuun, ajonaikaisen kielen teeskentely, jolla ei ole väliä, tulee kalliiksi hyvin nopeasti. Toisin sanoen C++ on edelleen yksi vakavimmista vastauksista aina, kun järjestelmä itse tulee ongelmaksi.

Kirjastot insinööripersoonallisuuksina

Helpoin tapa eksyä tähän ekosysteemiin on kohdella jokaista kirjastoa ikään kuin se kilpailisi samasta työstä. He eivät ole. Koulutukseen suuntautunut kehys, kannettava päättelyn ajonaika, ydinkirjasto ja paikallinen LLM-moottori ratkaisevat kaikki erilaiset ongelmat. Jos kokoamme ne yhteen luokkaan, jota kutsutaan AI-kirjastoiksi, päädymme tekemään valintoja brändin tuntemuksen perusteella järjestelmän suunnittelun sijaan.

ONNX Runtime on monissa tuotantoympäristöissä kurinalaisin ja vähiten teatraalinen valinta. Se on rakennettu puhtaan lupauksen ympärille: vie malli vakaaseen muotoon, lataa se suoritukseen keskittyvän ajon aikana ja anna sovelluksen omistaa muu järjestelmä. Se kuulostaa yksinkertaiselta, ja yksinkertaisuus on juuri syy, miksi se on tehokas. ONNX Runtime on usein oikea vastaus, kun tutkimusvaihe on jo tapahtunut muualla ja jäljelle jää hillitty työ päätelmien toimittamiseksi toistuvasti, kannettavasti ja ennustettavalla toiminnallisella käyttäytymisellä. Tietokonevision taustaohjelma, joka vastaanottaa kuvia, normalisoi tensorit, ajaa tunnetun kaavion ja palauttaa tulokset olemassa olevaan C++-palveluun, on ihanteellinen ONNX Runtime-tarina. Huono istuvuus olisi tuote, jonka ydinarvo riippuu dynaamisesta harjoitusajan käyttäytymisestä, sovelluksen sisäisistä toistuvista kaavioleikkauksista tai jatkuvasti muuttuvista mukautetuista operaattoreista, jotka tekevät viennistä hauraita. Siinä tapauksessa aluksi puhtaalta näyttänyt ajonaikaraja voi muodostua kitkan lähteeksi.

LibTorch on luonteeltaan erilainen. Se ei ole ensisijaisesti kevyt suoritusraja. Se on C++ kasvot täydelliselle syväoppimiskehykselle. Se tekee siitä raskaamman, mutta myös ilmaisuvoimaisemman. Kun natiivisovellus todella tarvitsee tensorin omistajuutta, mallin rakentamista, koulutuksen kaltaisia ​​manipulaatioita tai PyTorch-semantiikkaa koko kehitys- ja tuotantovaiheessa, LibTorch:sta tulee houkuttelevampi kuin ONNX Runtime. On tiettyä rehellisyyttä sen valinnassa, kun tuote todella tarvitsee puitteet ajonaikaisen rajan sijaan. Vastaesimerkki on yhtä tärkeä. Joskus tiimit ottavat käyttöön LibTorch:n tehdäkseen yksinkertaisia ​​staattisia päätelmiä, koska se tuntuu arvostetulta tai tulevaisuuden kannalta kestävältä. Sitten he huomaavat, että he toivat paljon suuremman käsitteellisen ja toiminnallisen pinnan kuin työmäärä vaadittiin. Pieni päättelypalvelu, joka tarvitsi vain vakaan mallikaavion lataamisen, saattaa maksaa tämän päätöksen paketin koon, monimutkaisuuden ja virheenkorjauksen osalta.

oneDNN ja OpenVINO elävät lähempänä metallia ja palkitsevat suorituskykytietoisemman ajattelutavan. oneDNN on kirjasto, jota arvostat, kun CPU-ytimet, muistimuodot ja operaattoritason tehokkuus ovat tarpeeksi tärkeitä ansaitsemaan suoran huomion. Monet tiimit käyttävät sitä epäsuorasti korkeamman tason ajonaikana, mikä on usein viisasta. OpenVINO puolestaan ​​on strategisemmassa paikassa. Se auttaa tiimejä, jotka välittävät Intel-suuntautuneesta käyttöönotosta, kaavioiden optimoinnista ja laitteistotietoisesta suorituksesta ilman, että ne haluavat hallita manuaalisesti jokaista matalan tason yksityiskohtaa. Käytännössä näillä työkaluilla alkaa olla merkitystä, kun liiketoiminnan ongelma ei ole enää vain "mallin käyttäminen" vaan "mallin tehokas käyttäminen laitteistolla, jonka voimme ostaa, ottaa käyttöön ja ylläpitää". Tämä ero kuulostaa pieneltä kokouksessa ja tulee hyvin suureksi budjetissa.

TensorFlow Lite edustaa kokonaan toista temperamenttia. Se on pidättymisen ääni. Reunalaitteissa, mobiilikohteissa ja resurssirajoitteisissa järjestelmissä täydellisyys on usein vähemmän arvokasta kuin kunto. Insinöörit eivät tarvitse siellä majesteettisia puitteita; he tarvitsevat mallin, joka lataa, suorittaa ja pysyy ankarissa muistin, paketin koon, energiankäytön ja käynnistysajan rajoituksissa. TensorFlow Lite on järkevää, kun itse käyttöönottokohde on tärkein arkkitehtuuria muokkaava voima. Vastaesimerkki on myös yleinen: tiimi aloittaa reunan suoritusajasta, koska se kuulostaa tehokkaalta, ja venyttää sen sitten hitaasti laajemmalle palvelinalustalle tai työnkulkuun, jolla on dynaamisemmat tarpeet kuin se on rakennettu tukemaan. Tehokkuus reunalla ei automaattisesti käänny mukavuuteen kaikkialla muualla.

Sitten on llama.cpp, joka ansaitsee erityistä huomiota, koska se muutti paikallisen päättelyn emotionaalista karttaa. Ennen kuin llama.cpp ja vastaavat hankkeet yleistyivät, monet insinöörit olettivat, että paikallinen laajakielinen tarjoilu säilyisi joko tutkimusleluna tai yrityksen välineenä. llama.cpp osoitti jotain mielenkiintoisempaa: aggressiivisella kvantisoinnilla, huolellisella ydintyöllä ja kurinalaisella suunnittelulla nykyaikaisesta LLM:stä voisi tulla paikallinen natiivi komponentti tavallisten järjestelmien sisällä. Tällä näkemyksellä on merkitystä pidemmälle kuin yksi projekti. Se muistutti koko kenttää siitä, että alkuperäinen suoritus, mallin pakkaus ja käytännön käyttöönotto voivat edetä paljon nopeammin kuin keskitetyt kertomukset usein antavat ymmärtää. Mutta llama.cpp:llä on myös luonnollinen raja. Se on erinomaista, kun työssä käytetään tuettuja muuntajamalleja paikallisesti ja tehokkaasti. Se ei ole yleinen korvike koko syvälle oppivalle ekosysteemille, ja tiimit joutuvat vaikeuksiin, kun he pyytävät sitä sellaiseksi.

Kuinka valita ilman Hypen viettelyä

Luotettavin tapa valita näistä kirjastoista on aloittaa tuotteesta ja nimetä työkalu vasta myöhemmin. Aloita kysymällä, mitä sovelluksesi todella omistaa ja mitä se vain kuluttaa. Jos järjestelmä käyttää enimmäkseen vakaata mallia ja tarvitsee kannettavan, hyvin rajatun päättelyn, ONNX Runtime on usein rauhallisin vastaus. Jos järjestelmän itsensä on puhuttava tensoreiden, moduulien ja kehyssemantiikan kieltä, LibTorch ansaitsee keskustelun. Jos CPU:n tehokkuus, kaavioiden optimointi tai Intel-heavy-käyttöönotto on vaikea osa, oneDNN ja OpenVINO siirtyvät lähemmäs keskustaa. Jos kohde on pieni, offline-tilassa, akkuherkkä tai upotettu, TensorFlow Lite muuttuu luonnollisemmaksi. Jos tuote koskee nimenomaan paikallisen kvantisoidun kielimallin suorittamista alkuperäisessä ympäristössä, llama.cpp kuuluu pöytään varhain.

Toinen kysymys on yhtä tärkeä: missä insinöörikipu todella maksetaan? Tiimit valitsevat usein kirjastot vertailuotsikoiden perusteella ja huomaavat sitten, että heidän todellinen kipunsa on muualla. Ajonaika, jolla on upeat suoritusarvot, voi silti olla väärä sovitus, jos vienti on epävakaa, esikäsittely on sekavaa tai käyttöönottopakkauksesta tulee hauras. Hieman hitaampi suoritusaika voi silti olla parempi liiketoimintavaihtoehto, jos se luo puhtaamman rajan mallintuottajien ja järjestelmän ylläpitäjien välille. Insinöörit, jotka ovat toimittaneet useamman kuin yhden AI-tuotteen, oppivat tämän läksyn syvästi: paras kirjasto tekee koko järjestelmästä helpompi järkeillä kahdelta yöllä; vertailuvoitot eivät yksin ratkaise päätöstä.

Tässä vastaesimerkeistä tulee terveitä. Harkitse tiimiä, joka rakentaa alkuperäisen asiakirja-analyysipalvelun. Muodikas valinta saattaa olla raskain saatavilla oleva runko, koska se tuntuu tulevaisuuden kestävältä. Mutta jos malli on staattinen, esikäsittelyprosessi on suoraviivainen ja todellinen tarve on vakaa päättely olemassa olevan C++-palvelun sisällä, ONNX Runtime aiheuttaa todennäköisesti vähemmän pitkäaikaista vetoa. Mieti nyt päinvastaista. Tiimi tekee alkuperäisiä kokeiluja mukautetuilla tensorivirroilla, toistuvilla arkkitehtuurimuutoksilla ja tiiviillä kytkennällä PyTorch-pohjaiseen harjoituslogiikkaan. Kaiken pakottaminen ONNX:n kautta, koska se kuulostaa "tuotantovalmiilta", voi luoda hauraan vientikeskeisen työnkulun, josta kukaan ei aidosti nauti. Kussakin tapauksessa virhe on sama: tiimi valitsi identiteetin ennen kuin se valitsi työtaakan.

Miltä hyvä integraatio todellisuudessa näyttää

Kypsä integroinnin työnkulku alkaa tietosopimuksesta, ei kirjastosta. Ennen kuin keskustelet suoritusajoista, päätä, mitä sovellus antaa mallille ja mitä malli palauttaa sovellukselle. Nimeä tensorimuodot, d-tyypit, normalisointisäännöt, tokenointipolut, täyttökäyttäytyminen, erän oletukset ja virheolosuhteet. Tämä kuulostaa lähes byrokraattiselta, mutta se on monien onnistuneiden käyttöönottojen hiljainen lähde. Järjestelmät epäonnistuvat, kun suoritusaikojen rajat ovat sumuisia.

Kun tietosopimus on vakaa, vienti- tai mallipakkaus on paljon helpompi validoida. Ryhmä voi vertailla tuloksia tutkimuspolun ja tuotantopolun välillä edustavien syötteiden avulla, mitata toleransseja ja havaita, mihin uskollisuus ajautuu. Täällä insinöörit huomaavat, selviääkö heidän tyylikäs arkkitehtuurinsa todellisuutta. Joskus viety kaavio on kunnossa ja ainoa ongelma on yhteensopimaton esikäsittely. Joskus ajonaika on virheetön ja todellinen ongelma on säikeiden ylitilaus muualla palvelussa. Joskus oletettavasti pieni malli ei kestä todellisen samanaikaisuuden muistipainetta. Jokainen näistä löydöistä on hyödyllinen. Se tarkoittaa, että järjestelmä on alkanut tulla näkyviin.

Sen jälkeen tulee benchmarking ja profilointi, ja tässä pätee sama vanha sääntö: mittaa järjestelmää, jonka aiot lähettää, älä lelua, jota tunsit fiksuksi. Vertaa mallia realististen pyyntömuotojen, eräkokojen, syötteiden vaihtelevuuden ja laitteistoolosuhteiden perusteella. Myös profiilin esi- ja jälkikäsittely, koska monet tiimit tiedostamatta benchmarkoivat vain mallin ydintä ja unohtavat, että asiakkaat maksavat koko polun. Tuotannossa AI kymmenen millisekunnin kaavio, jota ympäröi kuusikymmentä millisekuntia vältettävissä olevaa liimaa, on edelleen seitsemänkymmenen millisekunnin ominaisuus.

Tee lopuksi käyttöönotosta toistettavissa. Alkuperäinen AI palkitsee kurinalaisuutta. Kiinnitä versiot, asiakirjan kääntäjä ja ajonaikaiset oletukset, päätä, mitä suorituksen tarjoajia tai CPU-ominaisuuksia tarvitaan, ja pidä kapea joukko tuettuja määrityksiä. Jos joukkuetoveri ei pysty toistamaan samaa päättelypolkua toisella koneella ilman arkeologiaa, pino ei ole valmis, vaikka demo olisi ollut kuinka vaikuttava tahansa. Hyvä C++ AI-tekniikka tekee järjestelmästä riittävän rauhallisen, jotta nopeus pysyy ymmärrettävänä.

Virheet, jotka toistuvat

Yleisin virhe on sekoittaa tutkimustotuus tuotantototuuteen. Malli, joka näyttää erinomaiselta kannettavassa tietokoneessa, voi muuttua hankalaksi, kun se on viety, kvantisoitu, upotettu, tarkkailtu ja ajettu todellisessa samanaikaisessa käytössä. Tämä ei tarkoita, että malli olisi huono. Se tarkoittaa, että järjestelmä oli suurempi kuin koe. Toinen toistuva virhe on teeskennellä esi- ja jälkikäsittelyn olevan toissijaisia. Oikeissa tuotteissa ne ovat usein puolet työstä. Kuvan koonmuutoskäytäntö, tokenisaattorin käyttäytyminen, ominaisuuksien normalisointi, kalibrointikynnykset ja tulosteen dekoodaus kaikki muodon oikeellisuus ja latenssi yhtä varmasti kuin ydinajoaika.

Kolmas virhe on liiallinen sitoutuminen puitteisiin, koska se tuntuu modernilta tai kattavalta. Insinöörit valitsevat joskus suurimman mahdollisen työkalun ennakoiden tarpeita, joita ei koskaan saavuteta. Tuote maksaa sitten ominaisuuksista, joita se ei käytä. On myös päinvastainen virhe: valitaan kevyin ajoaika puhtauden nimissä ja sitten havaitaan, että dynaaminen käyttäytyminen, mukautetut toiminnot tai kehystason semantiikka eivät kuitenkaan olleet valinnaisia. Viisaus piilee siinä, että maksat vain siitä voimasta, jonka voit todella selittää.

On myös hienovaraisempaa asennevirhettä. Jotkut tiimit kohtelevat kirjaston valintaa ikään kuin se ratkaisee koko suunnittelun tarinan. Se ei tee. Hyviä tuloksia saadaan toistuvasta, vaatimattomasta työstä: tulosteiden validoinnista, kuumien polkujen mittaamisesta, vältettävien kopioiden poistamisesta, käynnistyksen kitkan vähentämisestä, pakkausten yksinkertaistamisesta ja suoritusajan rajan pitämisestä luettavana. Avoimen lähdekoodin kirjastot mahdollistavat tämän työn. he eivät tee sitä puolestamme.

Pieni käyttöönottotarina, joka kannattaa muistaa

Kuvittele tiimi, joka aloittaa Python-vision prototyypistä. Demo on tarpeeksi vahva voittaakseen sisäisen tuen, ja pian keskustelu kääntyy integraatioon olemassa olevan C++-palvelun kanssa, joka jo hoitaa kuvien käsittelyn, sääntöjen arvioinnin ja raportoinnin. Joukkueella on useita houkutuksia. Yksi on säilyttää malli erillisen Python-palvelun takana ikuisesti, koska se on helppoa lyhyellä aikavälillä. Toinen on siirtää kaikki heti raskaansarjan natiivikehykseen, koska se kuulostaa vakavalta. Kolmas on viettää viikkoja kiistellen arkkitehtuurista ennen kuin edes syöttösopimus vakiinnutetaan.

Kypsempi polku on hiljaisempi. Ensin tiimi määrittelee esikäsittelyn ja tulosteen semantiikan huolellisesti. Sitten se testaa vientitarkkuutta edustavilla kuvilla. Se valitsee ONNX Runtime, koska ongelma on staattinen päättely eikä kehyspohjainen kokeilu. Myöhemmin se arvioi, onko TensorFlow Lite tai aggressiivisemmin optimoitu suoritusaikapolku järkevä kyseiselle tuotehaaralle reunamuunnelmassa, jossa on tiukemmat laitteistorajoitukset. Kuukausia myöhemmin, jos yritys lisää paikallisen avustajan ominaisuuden, llama.cpp saattaa myös tulla arkkitehtuuriin, kun jokainen työkalu on ansainnut paikkansa järjestelmän eri kulmassa.

Se on kaikkien näiden kirjastojen syvin opetus. Vakava AI-tekniikka palkitsee harvoin puhtauden. Se palkitsee sopivuuden. Paras avoimen lähdekoodin kirjasto ei ole se, jota seuraa eniten. Se on se, joka antaa mallistasi tulla osaksi todellista järjestelmää pakottamatta muun järjestelmän muuttumaan järjettömäksi.

Käytännön laboratorio: Rakenna pieni ONNX Runtime CLI

Teoriasta tulee vakuuttavampi, kun se kootaan.

Rakennetaan C++:n pienin hyödyllinen natiivi päättelyohjelma. Tavoitteena ei ole kouluttaa mallia. Tavoitteena on tuntea omin käsin, miltä natiivi suoritusaikaraja näyttää.

Tätä harjoitusta varten tarvitset:

  • C++17-kääntäjä
  • CMake
  • esirakennettu ONNX Runtime-paketti virallisista julkaisuista
  • mikä tahansa pieni .onnx malli, jonka syöte on litteä kelluntatensori

Projektin ulkoasu

tiny-ort/
  CMakeLists.txt
  main.cpp
  third_party/
    onnxruntime/
  model.onnx

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(tiny_ort LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(ORT_ROOT "${CMAKE_SOURCE_DIR}/third_party/onnxruntime")

add_executable(tiny_ort main.cpp)
target_include_directories(tiny_ort PRIVATE "${ORT_ROOT}/include")

if (WIN32)
    target_link_directories(tiny_ort PRIVATE "${ORT_ROOT}/lib")
    target_link_libraries(tiny_ort PRIVATE onnxruntime)
else()
    target_link_directories(tiny_ort PRIVATE "${ORT_ROOT}/lib")
    target_link_libraries(tiny_ort PRIVATE onnxruntime)
endif()

main.cpp

#include <onnxruntime_cxx_api.h>

#include <array>
#include <iostream>
#include <numeric>
#include <vector>

int main() {
    Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "tiny-ort"};
    Ort::SessionOptions opts;
    opts.SetIntraOpNumThreads(1);
    opts.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

    const ORTCHAR_T* model_path = ORT_TSTR("model.onnx");
    Ort::Session session{env, model_path, opts};

    std::vector<int64_t> shape{1, 4};
    std::vector<float> input{0.25f, 0.50f, 0.75f, 1.0f};

    auto mem_info = Ort::MemoryInfo::CreateCpu(
        OrtArenaAllocator,
        OrtMemTypeDefault
    );

    Ort::Value tensor = Ort::Value::CreateTensor<float>(
        mem_info,
        input.data(),
        input.size(),
        shape.data(),
        shape.size()
    );

    const char* input_names[] = {"input"};
    const char* output_names[] = {"output"};

    auto outputs = session.Run(
        Ort::RunOptions{nullptr},
        input_names,
        &tensor,
        1,
        output_names,
        1
    );

    float* out = outputs[0].GetTensorMutableData<float>();
    auto out_shape = outputs[0].GetTensorTypeAndShapeInfo().GetShape();
    auto out_count = std::accumulate(
        out_shape.begin(),
        out_shape.end(),
        int64_t{1},
        std::multiplies<int64_t>{}
    );

    std::cout << "Output values:\n";
    for (int64_t i = 0; i < out_count; ++i) {
        std::cout << "  [" << i << "] = " << out[i] << "\n";
    }

    return 0;
}

Rakentaa

Linux tai macOS:

cmake -S . -B build
cmake --build build -j
./build/tiny_ort

Windows ja MSVC:

cmake -S . -B build
cmake --build build --config Release
.\build\Release\tiny_ort.exe

Mitä tämä opettaa sinulle

Tämä pieni projekti pakottaa sinut jo nyt kohtaamaan useita tuotantotodellisuuksia:

  • missä runtime asuu
  • miten alkuperäiset riippuvuudet pakataan
  • mitä tensorien nimet ja muodot todellisuudessa ovat
  • kuinka eksplisiittinen muistinkäsittely tuntuu alkuperäisessä päättelyrajassa

Se on juuri se pointti. Kirjasto lakkaa olemasta markkinointitermi ja siitä tulee insinöörivalinta.

Testitehtävät harrastajille

Jos haluat muuttaa artikkelin viikonloppulaboratorioksi, tässä on hyödyllisiä seuraavia ohjeita:

  1. Korvaa kovakoodattu syöttövektori arvoilla, jotka on ladattu pienestä teksti- tai binääritiedostosta.
  2. Tulosta syöttö- ja tulostensorin muodot dynaamisesti oletuksen sijaan.
  3. Lisää yksinkertainen latenssimittaus koodin session.Run ympärille ja vertaa 1-, 2- ja 4 -säikeitä.
  4. Vaihda ONNX Runtime:ksi LibTorch samanlaisessa lelupäättelysovelluksessa ja kirjoita ylös, mikä helpotti ja mikä raskaampaa.
  5. Vie pieni malli Pythonista, lataa se tähän C++-ohjelmaan ja varmista, etteivät esikäsittelyn erot muuta tulosta hiljaa.

Jos teet nämä viisi tehtävää rehellisesti, ymmärrät AI:n käyttöönotosta enemmän kuin monet ihmiset, jotka voivat lausua kehyksen nimiä tunnin ajan.

Yhteenveto

C++:n avoimen lähdekoodin hermoverkkokirjastot eivät marssi yhdessä paraatissa. Ne syntyivät erilaisista suunnittelutarpeista, ja ne ovat edelleen hyödyllisimpiä, kun kunnioitamme näitä alkuperää. ONNX Runtime on tehokas, koska se kaventaa ongelmaa ja antaa tuotantoryhmille vakaan päättelyrajan. LibTorch on arvokas, kun natiivisovellus todella tarvitsee tensorin ja moduulin omistuksen koko mallipolulla. oneDNN ja OpenVINO ovat tärkeitä, kun alhainen tehokkuus ja käyttöönotto tietyissä laitteistoperheissä lakkaavat olemasta toissijaisia ​​huolenaiheita. TensorFlow Lite loistaa, kun itse laite on kova rajoitus. llama.cpp on tärkeä, koska se osoitti hyvin julkisesti, että huolellinen natiivitekniikka voi muuttaa nykyaikaiset kielimallit käytännöllisiksi paikallisiksi komponenteiksi etäpalveluiden sijaan.

Paras valinta on siksi harvoin muodikkain. Se tekee koko järjestelmästä rauhallisemman. Hyvä suoritusaika on suoritusaika, jonka tiimisi voi ymmärtää, vertailla, profiloida, paketoida, testata ja toimia ilman mytologiaa. Kun insinöörit valitsevat kyseisestä paikasta, avoimen lähdekoodin AI ei enää näytä hämmentävältä kehysten eläintarhalta ja alkaa näyttää siltä, ​​mitä se todellisuudessa on: työkalupakki, joka on tarpeeksi rikas tukemaan vakavia alkuperäistuotteita.

Viitteet

  1. ONNX Runtime C/C++ API: ONNX Runtime
  2. ONNX virallinen projekti: https://onnx.ai/
  3. PyTorch C++ käyttöliittymän dokumentaatio: PyTorch
  4. oneDNN virallinen dokumentaatio: oneDNN
  5. OpenVINO dokumentaatio: OpenVINO
  6. LiteRT / TensorFlow Lite C++ API asiakirjat: TensorFlow Lite
  7. llama.cpp arkisto: llama.cpp
  8. ONNX Runtime GitHub-arkisto: ONNX Runtime
  9. PyTorch arkisto: PyTorch

    Miltä tämä näyttää, kun järjestelmä on jo paineen alainen

C++ ai suoritusajan valinnasta tulee yleensä kiireellinen juuri sillä hetkellä, kun joukkue toivoi hiljaisempaa neljännestä. Ominaisuus on jo asiakkaiden edessä tai alustalla on jo sisäinen riippuvuus, ja järjestelmä on valinnut kyseisen viikon paljastaakseen, että sen elegantti teoria ja sen ajonaikainen käyttäytyminen ovat eläneet kohteliaasti erillistä elämää. Tästä syystä niin moni vakava insinöörityö alkaa sovinnolla. Tiimin on sovitettava yhteen se, mitä se uskoo järjestelmän tekevän, sen kanssa, mitä järjestelmä todella tekee kuormitettuna, muutoksen aikana ja sellaisissa määräajoissa, jotka tekevät kaikista hieman luovempia ja hieman vähemmän viisaita.

Alkuperäisessä AI-käytössä tärkeimmät tapaukset ovat yleensä kannettavan palvelimen päättely, reunakäyttöönotto rajoitetuilla laitteistoilla ja mallien upottaminen olemassa oleviin alkuperäisiin tuotteisiin. Näillä tilanteilla on teknisiä, budjetti-, luottamus-, tiekartta- ja joskus mainevaikutuksia. Tekninen ongelma kasvaa poliittisesti suuremmiksi sillä hetkellä, kun useat tiimit ovat riippuvaisia ​​siitä, eikä kukaan voi selittää, miksi se käyttäytyy edelleen kuin pesukarhu seinien sisällä: meluisa yöllä, vaikea paikantaa ja kallis jättää huomiotta.

Siksi suosittelemme lukemaan ongelman käyttöpaineen ja toimitustodellisuuden linssin läpi. Suunnittelu voi olla teoreettisesti kaunis ja toiminnallisesti tuhoisa. Toinen malli voi olla melkein tylsä ​​ja silti viedä tuotetta eteenpäin vuosia, koska se on mitattavissa, korjattavissa ja rehellinen kompromissiensa suhteen. Vakavat insinöörit oppivat pitämään parempana toista luokkaa. Se tekee vähemmän eeppisiä puheita, mutta myös vähemmän hätätapahtumia, joissa kaikki puhuvat passiivisella äänellä eikä kukaan muista, kuka hyväksyi pikakuvakkeen.

Käytännöt, jotka vanhenevat jatkuvasti hyvin

Ensimmäinen kestävä käytäntö on pitää yksi edustava polku jatkuvassa mittauksessa. Ryhmät keräävät usein liian paljon epämääräistä telemetriaa ja liian vähän päätöslaatuista signaalia. Valitse polku, jolla on aidosti merkitystä, mittaa sitä toistuvasti ja kieltäydy antamasta keskustelua ajautua koristeelliseen tarinankerrontaan. C++ AI-ajonaikaisessa valinnassa hyödyllisiä toimenpiteitä ovat yleensä ajonaikainen sovitus, integrointikitka, pakkauskustannukset ja vakaan tilan viive. Kun ne ovat näkyvissä, loput päätökset muuttuvat inhimillisemmiksi ja vähemmän mystisiksi.

Toinen kestävä käytäntö on erottaa todiste lupauksesta. Insinöörejä painostetaan usein sanomaan, että suunta on oikea, ennen kuin järjestelmä on ansainnut tämän johtopäätöksen. Vastusta sitä painetta. Rakenna ensin kapea todiste, varsinkin kun aihe on lähellä asiakkaita tai rahaa. Pienellä todennetulla parannuksella on enemmän kaupallista arvoa kuin suurella todentamattomalla kunnianhimolla. Tämä kuulostaa itsestään selvältä, kunnes vuosineljänneksen lopun tarkastelu muuttaa hypoteesin määräajaksi ja koko organisaatio alkaa kohdella optimismia kuin aikataulutusartefaktia.

Kolmas kestävä käytäntö on kirjoittaa suositukset omistajakielellä. Kappale, jossa sanotaan "parantaa suorituskykyä" tai "vahvistaa rajoja", on emotionaalisesti miellyttävä ja toiminnallisesti hyödytön. Kappale, joka kertoo kuka muuttaa mitä, missä järjestyksessä, millä palautusehdon kanssa, on se, joka todella selviää maanantaiaamuna. Täällä monet tekniset kirjoittamiset epäonnistuvat. Se haluaa kuulostaa enemmän edistyneeltä kuin se haluaa olla aikataulutettu.

Vastaesimerkkejä, jotka säästävät aikaa

Yksi yleisimmistä vastaesimerkeistä näyttää tältä: tiimillä on jyrkkä paikallinen menestys, oletetaan, että järjestelmä on nyt ymmärretty, ja sitten skaalaa idean paljon vaativampaan ympäristöön ilman, että mittauskuria päivitetään. Se on tekninen vastine, kun opetellaan uimaan hotellin uima-altaassa ja sitten pidettäisiin itsevarma TED-puhe meren säästä. Vesi on vettä, kunnes sitä ei ole.

Toinen vastaesimerkki on työkalujen inflaatio. Uusi profiloija, uusi ajonaika, uusi kojelauta, uusi agentti, uusi automaatiokerros, uusi kääre, joka lupaa harmonisoida vanhan kääreen. Mikään näistä asioista ei ole luonnostaan ​​huono. Ongelmana on, mitä tapahtuu, kun heitä pyydetään kompensoimaan rajaa, jota kukaan ei ole nimennyt selvästi. Järjestelmästä tulee sitten instrumentoidumpi, vaikuttavampi ja vain toisinaan ymmärrettävämpi. Ostajat tuntevat tämän hyvin nopeasti. Jopa ilman tätä ilmaisua he voivat haistaa, kun pinosta on tullut kallis korvike päätökselle.

Kolmas vastaesimerkki on ihmisen tarkastelun käsitteleminen automaation epäonnistumisena. Todellisissa järjestelmissä ihmisen tarkastelu on usein ohjaus, joka pitää automaation kaupallisesti hyväksyttävänä. Aikuiset tiimit tietävät, missä automatisoida aggressiivisesti ja missä pitää hyväksyntä tai tulkinta näkyvänä. Epäkypsät tiimit haluavat koneen tekevän kaiken, koska "kaikki" kuulostaa tehokkaalta diassa. Sitten tapahtuu ensimmäinen vakava tapaus, ja yhtäkkiä manuaalinen tarkistus löydetään uudelleen konversiokokemuksen vilpittömästi.

Suosittelemamme toimitusmalli

Jos työ on tehty hyvin, ensimmäisen suorituksen pitäisi vähentää stressiä antamalla tiimille riittävän vahva tekninen lukema, jotta se lopettaa kiistelyn. Sen jälkeen seuraavan rajoitetun toteutuksen pitäisi parantaa yhtä ratkaisevaa polkua, ja uudelleentestauksen pitäisi tehdä suunta luettavaksi sekä suunnittelulle että johdolle. Tämä järjestys on tärkeämpi kuin tarkka työkaluvalinta, koska se muuttaa teknisen taidon eteenpäinliikkeeksi.

Käytännössä suosittelemme kapeaa ensimmäistä sykliä: kerää esineitä, tee yksi kova diagnoosi, lähetä yksi rajoitettu muutos, testaa todellinen polku uudelleen ja kirjoita seuraava päätös selkeällä kielellä. Selkeällä kielellä on väliä. Ostaja harvoin katuu selkeyttä. Ostaja katuu usein olevansa vaikuttunut ennen kuin kuitit saapuvat.

Tässä myös sävyllä on väliä. Vahvan teknisen työn pitäisi kuulostaa siltä kuin se on kohdannut tuotannon ennenkin. Rauhallinen, tarkka ja hieman huvittunut hypeistä sen sijaan, että se ravitsee sitä. Tämä ääni välittää toimintasignaalin. Se osoittaa, että tiimi ymmärtää järjestelmäsuunnittelun vanhan totuuden: koneet ovat nopeita, tiekartat ovat hauraita, ja ennemmin tai myöhemmin lasku saapuu jokaisesta olettamuksesta, jonka annettiin pysyä runollisena.

Tarkistuslista, jota käyttäisimme ennen kuin kutsumme tämän valmiiksi

Alkuperäisessä AI-käytössä valmius ei ole mieliala. Se on tarkistuslista, jolla on seurauksia. Ennen kuin aloitamme C++ AI-ajonaikaisen valinnan, joka on valmis laajempaan käyttöönottoon, haluamme, että muutamat asiat ovat tylsiä parhaalla mahdollisella tavalla. Haluamme yhden polun, joka käyttäytyy ennustettavasti edustavan kuormituksen alla. Haluamme yhden mittasarjan, joka ei ole ristiriidassa itsensä kanssa. Haluamme joukkueen tietävän, missä raja menee ja mitä sen rikkominen merkitsisi. Ja haluamme työn tulosten olevan riittävän selkeitä, jotta joku toteutushuoneen ulkopuolella voi silti tehdä siitä järkevän päätöksen.

Tämä tarkistuslista koskee yleensä ajonaikaista sovitusta, integrointikitkaa, pakkauskustannuksia ja vakaan tilan latenssia. Jos luvut liikkuvat oikeaan suuntaan, mutta tiimi ei silti osaa selittää järjestelmää improvisoimatta, työ ei ole valmis. Jos arkkitehtuuri kuulostaa vaikuttavalta, mutta ei kestä vaatimatonta vastaesimerkkiä kentältä, teos ei ole valmis. Jos toteutus on olemassa, mutta palautustarina kuulostaa rukoukselta aikaleimoineen, teos ei ole valmis. Mikään näistä ei ole filosofisia vastaväitteitä. Ne ovat yksinkertaisesti muotoja, joissa kalliilla yllätyksillä on tapana esitellä itsensä.

Täällä myös tiimit huomaavat, ratkoivatko he todellista ongelmaa vai vain harjoittelivat osaamista sen yleisessä läheisyydessä. Monet tekniset ponnistelut tuntuvat onnistuneilta, kunnes joku pyytää toistettavuutta, tuotantotodisteita tai päätöstä, joka vaikuttaa budjettiin. Sillä hetkellä heikko työ hämärtyy ja vahva työ muuttuu oudon selväksi. Plain on hyvä. Pelkkä tarkoittaa yleensä sitä, että järjestelmä on lakannut luottamasta karismaan.

Kuinka suosittelemme puhumaan tuloksista

Lopullisen selityksen tulee olla riittävän lyhyt selviytyäkseen johtajuuden kokouksesta ja riittävän konkreettinen selviytyäkseen teknisestä katsauksesta. Se on vaikeampaa kuin miltä se kuulostaa. Liian tekninen kieli piilottaa järjestyksen. Liian yksinkertaistettu kielenkäyttö piilottaa riskin. Oikea keskitie on kuvata polkua, todisteita, rajallista muutosta ja seuraavaa suositeltua askelta tavalla, joka kuulostaa mieluummin rauhalliselta kuin voittoisalta.

Suosittelemme tällaista rakennetta. Sano ensin, mitä polkua arvioitiin ja miksi sillä oli merkitystä. Toiseksi sano, mikä oli vialla tai epävarmaa kyseisessä polussa. Kolmanneksi sano, mitä muutettiin, mitattiin tai vahvistettiin. Neljänneksi sano, mikä on vielä ratkaisematta ja mitä seuraava sijoitus ostaisi. Tämä rakenne toimii, koska se kunnioittaa sekä suunnittelua että ostokäyttäytymistä. Insinöörit haluavat yksityiskohtia. Ostajat haluavat sekvensoinnin. Kaikki haluavat vähemmän yllätyksiä, myös ihmiset, jotka teeskentelevät nauttivansa niistä.

Tällä tavalla puhumisen piilotettu hyöty on kulttuurinen. Tiimit, jotka selittävät teknisen työn selkeästi, tekevät sen yleensä myös selkeämmin. He lakkaavat käsittelemästä monitulkintaisuutta hienostuneisuutena. Heihin on vaikeampi tehdä vaikutuksen ammattikieltä ja helpompi luottaa vaikeiden järjestelmien kanssa. Se on yksi insinööritaidon aliarvostetuimmista muodoista.

Mitä emme edelleenkään kieltäytyisi väärentämästä

Jopa järjestelmän parantumisen jälkeen kypsät tiimit pitävät epävarmuuden rehellisenä alkuperäisessä AI-käytössä. Heikko mittaus vaatii selkeämpää näyttöä, kovat rajat selkeää kieltä ja rauhallisemmat demot todellista toimintavalmiutta. Epävarmuutta on vähennettävä; jotkut on nimettävä rehellisesti. Nämä kaksi tehtävää sekoitetaan siinä, kuinka kunniallisista projekteista tulee kalliita vertauksia.

Sama sääntö koskee päätöksiä C++ AI ajonaikaisen valinnan suhteen. Jos tiimiltä puuttuu edelleen toistettava vertailukohta, luotettava palautuspolku tai selkeä omistaja kriittiselle käyttöliittymälle, hyödyllisin tulos voi olla terävämpi ei tai kapeampi seuraava askel suuremman lupauksen sijaan. Tämä kurinalaisuus pitää teknisen työn linjassa sen todellisuuden kanssa, jota sen on tarkoitus parantaa.

Tällä työskentelyllä on outo helpotus. Kun järjestelmä ei enää ole riippuvainen optimistisesta tarinankerronnasta, insinöörikeskustelu yksinkertaistuu, vaikka työ olisikin kovaa. Ja tuotannossa se on usein vähäistä armon muotoa.

Kentän muistiinpanot todellisesta teknisestä katsauksesta

C++-järjestelmätoimituksessa työ muuttuu vakavaksi, kun demo kohtaa todellisen toimituksen, todelliset käyttäjät ja todelliset käyttökustannukset. Se on hetki, jolloin siisti idea alkaa käyttäytyä kuin järjestelmä, ja järjestelmillä on tunnetusti kuiva huumorintaju. He eivät välitä siitä, kuinka tyylikkäältä aloituspakka näytti. He välittävät rajoista, vikatiloista, käyttöönottopoluista ja siitä, voiko kukaan selittää seuraavan vaiheen keksimättä uutta mytologiaa pinon ympärille.

Using Open-Source Libraries for Neural Networks in C++:n kohdalla käytännön kysymys on, luoko se vahvemman toimituspolun ostajalle, jolla on jo paineita etenemissuunnitelman, alustan tai tietoturvatarkastuksen suhteen. Tuo ostaja ei tarvitse sumuksi kiillotettua luentoa. He tarvitsevat teknisen luennon, jota he voivat käyttää.

Mitä tarkastaisimme ensin

Aloitamme yhdellä edustavalla polulla: natiivi päättely, profilointi, HFT-polut, DEX-järjestelmät ja C++/Rust-modernisointivaihtoehdot. Tuon polun tulee olla tarpeeksi kapea mitatakseen ja riittävän leveä paljastaakseen totuuden. Ensimmäisen läpimenon pitäisi tallentaa allokointikäyttäytyminen, p99-viive, profiilin todisteet, ABI kitka ja vapauttaa luottamus. Jos nämä signaalit eivät ole saatavilla, projekti on edelleen enimmäkseen mielipidettä laboratoriotakissa, ja mielipiteellä on pitkä historia laskuttaa itsensä strategiana.

Ensimmäinen hyödyllinen artefakti on alkuperäisten järjestelmien luku, jossa on vertailuarvoja, profilointitodistusta ja laajennettu toteutussuunnitelma. Sen pitäisi näyttää järjestelmä sellaisena kuin se käyttäytyy, ei niin kuin kaikki toivoivat sen käyttäytyvän suunnittelukokouksessa. Jäljitys, toisto, pieni benchmark, politiikkamatriisi, jäsennyslaite tai toistettava testi kertovat usein tarinan nopeammin kuin toinen abstrakti arkkitehtuurikeskustelu. Hyvät esineet ovat hämmästyttävän töykeitä. Ne keskeyttävät toiveajattelua.

Vastaesimerkki, joka säästää aikaa

Kallis virhe on vastata ratkaisulla, joka on suurempi kuin ensimmäinen hyödyllinen todiste. Tiimi näkee riskin tai viiveen ja tavoittaa välittömästi uuden alustan, uudelleenkirjoituksen, laajan refaktorin tai hankintaystävällisen kojelaudan, jonka nimi kuulostaa siltä kuin se tekee joogaa. Joskus tämä mittakaava on perusteltu. Hyvin usein se on tapa lykätä mittausta.

Parempi liike on pienempi ja terävämpi. Nimeä raja. Ota todisteet talteen. Muuta yksi tärkeä asia. Testaa samaa polkua uudelleen. Päätä sitten, kannattaako seuraava investointi olla suurempi. Tämä rytmi on vähemmän dramaattinen kuin muutosohjelma, mutta sillä on taipumus selviytyä kosketuksista budjetteihin, julkaisukalentereihin ja tuotantotapahtumiin.

Suosittelemamme toimitustapa

Luotettavimmassa mallissa on neljä vaihetta. Kerää ensin edustavia esineitä. Toiseksi, muuta nämä esineet yhdeksi vaikeaksi tekniseksi diagnoosiksi. Kolmanneksi lähetä yksi rajoitettu muutos tai prototyyppi. Neljänneksi, testaa uudelleen samalla mittauskehyksellä ja dokumentoi seuraava päätös selkeällä kielellä. Tässä työluokassa CMake-kalusteet, profilointivaljaat, pienet alkuperäiset reprot ja kääntäjä/ajonaikaiset muistiinpanot ovat yleensä arvokkaampia kuin toinen tapaus yleisestä suunnasta.

Selkeällä kielellä on väliä. Ostajan tulee pystyä lukemaan tulos ja ymmärtämään, mikä muuttui, mikä on edelleen riskialtista, mikä voi odottaa ja mitä seuraava askel ostaisi. Jos suositusta ei voida ajoittaa, testata tai määrittää omistajalle, se on silti liian koristeellinen. Koristeellinen tekninen kirjoittaminen on miellyttävää, mutta tuotantojärjestelmiä ei tunneta miellyttävyyden palkitsemisesta.

Kuinka arvioida, auttoiko tulos

Kohdassa Open-Source Neural Network Libraries in C++: ONNX Runtime, LibTorch, oneDNN, OpenVINO, TFLite, llama.cpp tuloksen pitäisi parantaa ainakin yhtä kolmesta asiasta: toimitusnopeus, järjestelmän luottamus tai kaupallinen valmius. Jos se ei paranna mitään näistä, tiimi on saattanut oppia jotain, mutta ostaja ei ole vielä saanut hyödyllistä tulosta. Sillä erolla on merkitystä. Oppiminen on jaloa. Myös palkallisen toimeksiannon pitäisi siirtää järjestelmää.

Vahvin tulos voi olla kapeampi tiekartta, kieltäytyminen vaarallisen polun automatisoinnista, parempi raja mallin ympärille, puhtaampi natiiviintegraatio, mitattu todiste siitä, että uudelleenkirjoitusta ei vielä tarvita, tai lyhyt korjauslista, jonka johto voi todella rahoittaa. Vakava suunnittelu on sarja parempia päätöksiä, ei pukukilpailua työkaluista.

Miten SToFU suhtautuisi asiaan

SToFU käsittelee tätä ensin toimitusongelmana ja sitten teknologiaongelmana. Tuomme asiaan liittyvän suunnittelusyvyyden, mutta pitäisimme sitoutumisen ankkuroituna todisteisiin: polkuun, rajaan, riskiin, mittaukseen ja seuraavaan tekemisen arvoiseen muutokseen. Tarkoitus ei ole saada kovaa työtä kuulostamaan helpolta. Tarkoitus on tehdä seuraavasta vakavasta liikkeestä riittävän selkeä suoritettavaksi.

Sitä osaa ostajat yleensä arvostavat eniten. He voivat palkata mielipiteitä missä tahansa. He tarvitsevat tiimin, joka voi tarkastaa järjestelmän, nimetä todellisen rajoitteen, rakentaa tai vahvistaa oikean osion ja jättää taakseen artefakteja, jotka vähentävät sekaannusta puhelun päätyttyä. Meluisillä markkinoilla selkeys ei ole pehmeä taito. Se on infrastruktuuri.

Philip P.

Philip P. – CTO

Takaisin Blogeihin

Ota yhteyttä

Aloita keskustelu

Muutama selkeä viiva riittää. Kuvaile järjestelmää, painetta ja päätöstä, joka on estetty. Tai kirjoita suoraan osoitteeseen midgard@stofu.io.

01 Mitä järjestelmä tekee
02 Mikä nyt sattuu
03 Mikä päätös on estetty
04 Valinnainen: lokit, tiedot, jäljet, erot
0 / 10000
Tiedostoa ei ole valittu