Steven
Steven7 min. skaitymo

Nuo OCR iki pikselių neprarandant atsarginio kelio

GeekBye nustojo skaityti tavo ekrano nuotraukas per OCR ir ėmė siųsti patį vaizdą regintiems modeliams — o tai yra lengvoji dalis. Pamokanti dalis yra optimizacija, kuri praleisdavo OCR visiškai regintį modelį turintiems naudotojams, tyliai laužė atsarginį kelią į neregintius modelius ir turėjo būti atsukta iki versijos, kur apsauginis tinklas bėga lygiagrečiai ir nekainuoja nieko.

Inžinerija
AI
Desktop
GeekBye leidimai
Nuo OCR iki pikselių neprarandant atsarginio kelio

Bet kuri programa, leidžianti nufotografuoti ekraną ir apie tai paklausti DI, atsiduria prie tikro kelio išsišakojimo. Gali paleisti OCR — ištraukti tekstą įrenginyje, nusiųsti mažytę eilutę ir išmesti paveikslėlį. Arba gali nusiųsti pikselius — persiųsti patį vaizdą ir leisti regėti gebančiam modeliui jį perskaityti. OCR yra pigus ir mažas, bet aklas viskam, kas nėra tekstas: išdėstymui, diagramai, kodo įtraukoms, raudonai vingiuotai linijai po klaida. Pikseliai išsaugo visa tai, bet kainuoja daugiau baitų ir reikalauja modelio, kuris gali matyti. GeekBye pradėjo nuo OCR pusės to išsišakojimo ir, per v1.8.6 ir v1.8.7, perėjo į pikselių pusę. Tas perjungimas yra lengvoji šios istorijos dalis. Įdomioji dalis — optimizacija, kuri atėjo kartu su juo, sulaužė atsarginį kelią ir turėjo būti atsukta.

Prieš tai: perskaityk tekstą, atmesk paveikslėlį

Iki šio skyriaus ekrano nuotrauka tapdavo atsakymu taip: paspaudi fiksavimo spartųjį klavišą, klientas paleidžia OCR — Apple Vision karkasą per swiftinį VisionOCR dvejetainį failą macOS sistemoje, PowerShell skriptą Windows sistemoje, abu už vienos IOcrEngine abstrakcijos — o ištrauktas tekstas suvyniojamas į eilutę ir siunčiamas į backend kaip screenshotText. Pats vaizdas niekada nepaliko mašinos. Tai efektyvu, ir paprasto teksto sienai to visiškai pakanka. Bet ekrano nuotrauka retai būna vien tekstas. Tai darbo skelbimas su dviejų stulpelių išdėstymu, programavimo užduotis su diagrama, klaida su steko pėdsaku, kurio įtraukos ir yra užuomina. OCR suplokština visa tai į eilutė po eilutės transkripciją, ir modelis atsako į skurdesnį klausimą nei tas, kurį uždavei.

Perjungimas: siųsk pikselius (v1.8.6)

v1.8.6 pridėjo kitą kelią. Naujas imageOptimizer.ts, pastatytas ant sharp ir todėl daugiaplatformis, daro žaliai ekrano nuotraukai lygiai tris dalykus:

sharp(pngFilePath).resize({ width: 1920, withoutEnlargement: true }).jpeg({ quality: 80 })

Sumažink iki daugiausiai 1920px pločio (niekada nedidink mažesnio fiksavimo), paversk PNG į JPEG, kokybė 80. Išvestis — žalias base64 su medijos tipu, nešamu atskirame lauke, ir jis keliauja į backend dviejuose naujuose užklausos laukuose šalia senojo: screenshotImage ir screenshotImageMediaType, nepriklausomai nuo screenshotText. Backend dabar gali gauti vaizdą, tekstą arba abu.

Tas optimizacijos žingsnis nėra kosmetinis. Žalia PNG ekrano nuotrauka yra 3–8 MB; sumažintas JPEG — maždaug 200–500 KB. Tai eile mažiau įkėlimui, ir tai taip pat apkarpo sąskaitą už modelį — regintys modeliai dalija vaizdą į plyteles pagal jo skiriamąją gebą ir ima mokestį už plytelę, tad pločio apribojimas iki 1920 ir perspaudimas tiesiogiai sumažina ir baitus laide, ir vaizdo tokenus, už kuriuos moki, be jokio reikšmingo ekrano teksto įskaitomumo praradimo.

Iš kur programa apskritai žino, ar modelis gali matyti? Apie tai skelbia backend. /api/config atsakymas gavo ai.vision bloką, raktuojamą pagal planą, o klientas kiekvieno konfigūracijos įkėlimo metu talpina vieną loginę reikšmę — aiSupportsVision. Kai regėjimas palaikomas, optimizuok vaizdą; kai ne — griebkis OCR. Tad gebėjimas yra faktas backend pusėje, talpinamas kliento pusėje, o sprendimas kiekvienai ekrano nuotraukai, paleisti OCR ar praleisti, priimamas kliente pagal tą požymį. Backend vis dar valdo tai, kuris modelis iš tikrųjų atsako.

Optimizacija, kuri buvo per gudri

Štai čia darosi pamokoma. Pirmoji reginčio kelio versija paleisdavo OCR ir vaizdo optimizaciją kartu, lygiagrečiai. Paskui „perf" komitas suspaudė tai į arba-arba: jei planas palaiko regėjimą, optimizuok vaizdą ir praleisk OCR visiškai; jei ne — paleisk OCR ir praleisk darbą su vaizdu. Popieriuje tai akivaizdus laimėjimas — kam traukti tekstą, kurio regintiam modeliui nereikia? Changelog netgi tai reklamuoja: „OCR is skipped when the AI can read the image directly."

Problema yra žodyje fallback. Backend turi ne vieną modelį; jis turi pagrindinį ir atsarginį, o atsarginis gali būti neregintis. Įsivaizduok seką: tavo plano pagrindinis modelis regi, tad klientas išdidžiai praleidžia OCR ir siunčia tik vaizdą. Pagrindinis tiekėjas žagteli. Backend persijungia į vien tekstinį modelį — kuriam dabar paduodamas vaizdas, kurio jis negali perskaityti, ir jokio teksto, nes klientas tekstą optimizavo taip, kad jis dingo. Greitasis kelias tyliai ištrynė apsauginį tinklą. Perteklinį greitąjį kelią gali saugiai atmesti; negali atmesti to, ką greitasis kelias apeidavo trumpiniu.

Vaistas: susiek tai su atsarginiu modeliu, o ne pagrindiniu

Vaistas — ta dalis, kurią verta nusavinti. Jis buvo ne grįžti prie visada paleidžiamo OCR — tai išmestų laimėjimą įprastam atvejui. Jis buvo padaryti praleidimą priklausomą nuo antro gebėjimo požymio: ar regi atsarginis modelis? Backend konfigūracija gavo fallbackVision bloką; klientas talpina aiFallbackSupportsVision šalia pirmojo požymio. Dabar sprendimas turi keturias šakas:

  • Pagrindinis ir atsarginis abu regi → optimizuok vaizdą, praleisk OCR. Greitasis, įprastas kelias.
  • Pagrindinis regi, bet atsarginis aklas → optimizuok vaizdą ir paleisk OCR, kaip draudimą atsarginiam.
  • Vaizdo optimizacija meta išimtį → OCR kaip kietas atsarginis kelias.
  • Planas visai neturi regėjimo → tik OCR.

O antroji šaka — draudimo atvejis — yra vieta, kur svarbus paskutinis ėjimas. Paleisti OCR po vaizdo optimizacijos pridėtų jo delsą viršuje. Tad abu bėga kartu po vienu Promise.all: vaizdo optimizacija ir OCR persidengia, o OCR, kuris egzistuoja tik tam atvejui, jei backend jo prireiks, baigiasi per tą laiką, per kurį vaizdas ir taip buvo optimizuojamas. Apsauginis tinklas yra nemokamas realaus laiko prasme kaskart, kai nenaudojamas. Visas triukas viename sakinyje: išlaikyk pigų atsarginį kelią, susiek jį su tuo, ar tavo atsarginiam tiekėjui jo iš tikrųjų reikia, ir sulygiagretink jį taip, kad jis nekainuotų nieko, kol tavęs neišgelbės.

Dvi mažesnės pinklės, kurias paspendė praleidimas

Agresyvus atidėto darbo praleidimas turi savybę iškelti į paviršių kiekvieną vietą, kuri tyliai laikė savaime suprantama, kad tas darbas įvyks. Dvi išlindo PR peržiūroje tą pačią savaitę.

Eilės užstrigimas. Ekrano nuotraukų eilė laikė elementą „paruoštu", kai jis turėjo OCR tekstą. Reginčios ekrano nuotraukos dabar niekada negaudavo OCR teksto — pagal sumanymą — tad užbaigtumo patikra hasIncompleteOCR laukė amžinai ir eilė pakibo. Vaistas iš naujo apibrėžia „paruoštas" kaip turi optimizuotą vaizdą arba turi OCR tekstą, ką konvejeris dabar iš tikrųjų garantuoja.

Pasenęs gebėjimo požymis. aiSupportsVision yra talpinamas, o tai reiškia, kad jis gali pasenti kaip tik neteisinga kryptimi: naudotojas atsijungia, arba konfigūracijos įkėlimas nepavyksta, ir klientas lieka įsitikinęs, kad vis dar gali siųsti vaizdus modeliui, kuris jų nebepalaiko. Požymis dabar atstatomas atsijungus ir nepavykus įkelti konfigūracijos — talpinamas gebėjimas privalo turėti scenarijų tam atvejui, kai tai, ką jis aprašo, dingsta.

Žodis apie skaičius

Kadangi šios serijos esmė — sąžiningumas: komitų žinutės neša skaičius — vaizdo optimizacija „~30ms" prieš OCR „~100–150ms", apkrovos „~200–500 KB vs ~3–8 MB PNG" — ir jie tikėtini, bet tai paties autoriaus įrašyti spėjimai, o ne benchmarko rezultatas. Niekur istorijoje nėra išmatuotos ištisinės delsos prieš ir po. Vertink greičio argumentą kaip sveiką projektinį ketinimą, o ne įrodytą rezultatą. Apkrovos dydžio sumažinimas — vienintelis skaičius, kuriuo gali pasitikėti, nes jis tiesiogiai išplaukia iš sumažinimo ir perspaudimo.

Trys dalykai, kurių mus išmokė šis leidimas

  1. Pikselių siuntimas nugali teksto skaitymą — kai modelis gali matyti. OCR atmeta viską, ką ekrano nuotrauka perteikia be simbolių. Regintis modelis skaito išdėstymą, diagramą ir įtraukas, o optimizuotas JPEG padaro tai įperkama. Bet tai statymas ant gebėjimo, todėl jam reikia atsarginio kelio.
  2. Greitąjį kelią gali optimizuoti iki išnykimo, apsauginio tinklo — niekada. OCR praleidimas regintį modelį turintiems naudotojams buvo teisingas pagrindiniam modeliui ir neteisingas atsarginiam. Bug buvo ne praleidime; jis buvo tame, kad praleista buvo pagal pagrindinio modelio gebėjimą, kai svarbus buvo atsarginio.
  3. Nemokamas apsauginis tinklas — tai sulygiagretintas tinklas. Susiek draudimo darbą su tuo, ar jo iš tikrųjų reikia, tada paleisk jį šalia greitojo kelio, kad jie persidengtų. Padaryta teisingai, sklandi degradacija nekainuoja nieko iki tos dienos, kai ji — vienintelė priežastis, dėl kurios tavo funkcija vis dar veikia.

Ankstesniam v1 istorijos skyriui — du paspaudimus praleidžiančios perdangos gedimo režimai (v1.8.5); o visai istorijos linijai — programinės įrangos tobulybės pristatymo anatomija.