#
Introducere în Gradio Blocks

În secțiunile anterioare am explorat și am creat demo-uri folosind clasa `Interface`. În această secțiune vom introduce API-ul nostru de nivel scăzut recent dezvoltat numit `gradio.Blocks`.

Acum, care este diferența între `Interface` și `Blocks`?

- ⚡ `Interface`: un API de nivel înalt care vă permite să creați un demo complet de machine learning simplu prin furnizarea unei liste de intrări și ieșiri.

- 🧱 `Blocks`: un API de nivel scăzut care vă permite să aveți control complet asupra fluxurilor de date și layout-ului aplicației dvs. Puteți construi aplicații foarte complexe, cu mai mulți pași folosind `Blocks` (ca în “piese de construcție”).

###
De ce Blocks 🧱?

Așa cum am văzut în secțiunile anterioare, clasa `Interface` vă permite să creați cu ușurință demo-uri complete de machine learning cu doar câteva linii de cod. API-ul `Interface` este extrem de ușor de folosit, dar îi lipsește flexibilitatea pe care o oferă API-ul `Blocks`. De exemplu, ați putea dori să:

- Grupați demo-uri înrudite ca file multiple într-o aplicație web
- Schimbați layout-ul demo-ului dvs., de ex. să specificați unde sunt localizate intrările și ieșirile
- Aveți interfețe cu mai mulți pași, în care ieșirea unui model devine intrarea următorului model, sau să aveți fluxuri de date mai flexibile în general
- Schimbați proprietățile unei componente (de exemplu, opțiunile dintr-un dropdown) sau vizibilitatea sa pe baza intrării utilizatorului

Vom explora toate aceste concepte mai jos.

###
Crearea unui demo simplu folosind Blocks

După ce ați instalat Gradio, rulați codul de mai jos ca script Python, notebook Jupyter sau notebook Colab.

    import gradio as gr

    def flip_text(x):
        return x[::-1]

    demo = gr.Blocks()

    with demo:
        gr.Markdown(
            """
        # Flip Text!
        Start typing below to see the output.
        """
        )
        input = gr.Textbox(placeholder="Flip this text")
        output = gr.Textbox()

        input.change(fn=flip_text, inputs=input, outputs=output)

    demo.launch()

Acest exemplu simplu de mai sus introduce 4 concepte care stau la baza Blocks:

1. Blocks vă permite să construiți aplicații web care combină markdown, HTML, butoane și componente interactive simplu prin instanțierea obiectelor în Python într-un context `with gradio.Blocks`.

   🙋Dacă nu sunteți familiarizați cu declarația `with` în Python, vă recomandăm să consultați excelentul [tutorial](https://realpython.com/python-with-statement/) de la Real Python. Întoarceți-vă aici după citirea acestuia 🤗
   Ordinea în care instanțiați componentele contează deoarece fiecare element este redat în aplicația web în ordinea în care a fost creat. (Layout-uri mai complexe sunt discutate mai jos)

2. Puteți defini funcții Python obișnuite oriunde în codul dvs. și să le rulați cu intrări de la utilizator folosind `Blocks`. În exemplul nostru, avem o funcție simplă care “inversează” textul de intrare, dar puteți scrie orice funcție Python, de la un calcul simplu la procesarea predicțiilor dintr-un model de machine learning.

3. Puteți atribui evenimente oricărei componente `Blocks`. Aceasta va rula funcția dvs. când componenta este apăsată, schimbată, etc. Când atribuiți un eveniment, transmiteți trei parametri: `fn`: funcția care ar trebui apelată, `inputs`: (lista) componentei(lor) de intrare, și `outputs`: (lista) componentelor de ieșire care ar trebui apelate.

   În exemplul de mai sus, rulăm funcția `flip_text()` când valoarea din `Textbox`-ul numit intrare `input` se schimbă. Evenimentul citește valoarea din `input`, o transmite ca parametru de nume la `flip_text()`, care apoi returnează o valoare care este atribuită celui de-al doilea `Textbox` numit `output`.

   Pentru a vedea o listă a evenimentelor pe care le suportă fiecare componentă, consultați documentația Gradio.

4. Blocks determină automat dacă o componentă ar trebui să fie interactivă (să accepte intrări de la utilizator) sau nu, pe baza declanșatorilor de evenimente pe care îi definiți. În exemplul nostru, prima cutie de text este interactivă, deoarece valoarea sa este folosită de funcția `flip_text()`. A doua cutie de text nu este interactivă, deoarece valoarea sa nu este niciodată folosită ca intrare. În unele cazuri, ați putea dori să suprascriați acest lucru, ceea ce puteți face prin transmiterea unui boolean la parametrul `interactive` al componentei (de ex. `gr.Textbox(placeholder="Flip this text", interactive=True)`).

###
Personalizarea layout-ului demo-ului dvs.

Cum putem folosi `Blocks` pentru a personaliza layout-ul demo-ului nostru? În mod implicit, `Blocks` redă componentele pe care le creați vertical într-o coloană. Puteți schimba asta prin crearea de coloane suplimentare `with gradio.Column():` sau rânduri `with gradio.Row():` și crearea componentelor în acele contexte.

Iată ce ar trebui să țineți minte: orice componente create sub o `Column` (aceasta este și setarea implicită) vor fi aranjate vertical. Orice componentă creată sub un `Row` va fi aranjată orizontal, similar cu modelul flexbox în dezvoltarea web.

În final, puteți crea și file pentru demo-ul dvs. folosind managerul de context `with gradio.Tabs()`. În acest context, puteți crea file multiple prin specificarea `with gradio.TabItem(name_of_tab):` copii. Orice componentă creată în interiorul unui context `with gradio.TabItem(name_of_tab):` apare în acea filă.

Acum să adăugăm o funcție `flip_image()` la demo-ul nostru și să adăugăm o filă nouă care inversează imaginile. Mai jos este un exemplu cu 2 file și folosește și un Row:

    import numpy as np
    import gradio as gr

    demo = gr.Blocks()

    def flip_text(x):
        return x[::-1]

    def flip_image(x):
        return np.fliplr(x)

    with demo:
        gr.Markdown("Flip text or image files using this demo.")
        with gr.Tabs():
            with gr.TabItem("Flip Text"):
                with gr.Row():
                    text_input = gr.Textbox()
                    text_output = gr.Textbox()
                text_button = gr.Button("Flip")
            with gr.TabItem("Flip Image"):
                with gr.Row():
                    image_input = gr.Image()
                    image_output = gr.Image()
                image_button = gr.Button("Flip")

        text_button.click(flip_text, inputs=text_input, outputs=text_output)
        image_button.click(flip_image, inputs=image_input, outputs=image_output)

    demo.launch()

Veți observa că în acest exemplu, am creat și o componentă `Button` în fiecare filă, și am atribuit un eveniment de clic fiecărui buton, care este ceea ce rulează de fapt funcția.

###
Explorarea evenimentelor și stării

La fel cum puteți controla layout-ul, `Blocks` vă oferă control fin asupra evenimentelor care declanșează apelurile de funcții. Fiecare componentă și multe layout-uri au evenimente specifice pe care le suportă.

De exemplu, componenta `Textbox` are 2 evenimente: `change()` (când valoarea din interiorul cutiei de text se schimbă), și `submit()` (când un utilizator apasă tasta enter în timp ce este focalizat pe cutia de text). Componentele mai complexe pot avea și mai multe evenimente: de exemplu, componenta `Audio` are și evenimente separate pentru când fișierul audio este redat, șters, întrerupt, etc. Consultați documentația pentru evenimentele pe care le suportă fiecare componentă.

Puteți atașa declanșatorul de eveniment la niciunul, unul sau mai multe dintre aceste evenimente. Creați un declanșator de eveniment prin apelarea numelui evenimentului pe instanța componentei ca funcție — de ex. `textbox.change(...)` sau `btn.click(...)`. Funcția primește trei parametri, așa cum s-a discutat mai sus:

- `fn`: funcția care să ruleze
- `inputs`: o (listă de) componentă(e) ale căror valori ar trebui furnizate ca parametri de intrare la funcție. Valoarea fiecărei componente este mapată la parametrul funcției corespunzător, în ordine. Acest parametru poate fi None dacă funcția nu primește parametri.
- `outputs`: o (listă de) componentă(e) ale căror valori ar trebui actualizate pe baza valorilor returnate de funcție. Fiecare valoare returnată setează valoarea componentei corespunzătoare, în ordine. Acest parametru poate fi None dacă funcția nu returnează nimic.

Puteți face chiar ca componenta de intrare și ieșire să fie aceeași componentă, așa cum facem în acest exemplu care folosește un model GPT pentru completarea textului:

    import gradio as gr

    api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B")

    def complete_with_gpt(text):
        # Use the last 50 characters of the text as context
        return text[:-50] + api(text[-50:])

    with gr.Blocks() as demo:
        textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4)
        btn = gr.Button("Generate")

        btn.click(complete_with_gpt, textbox, textbox)

    demo.launch()

###
Crearea demo-urilor cu mai mulți pași

În unele cazuri, ați putea dori un demo cu mai mulți pași, în care reutilizați ieșirea unei funcții ca intrare la următoarea. Acest lucru este foarte ușor de făcut cu `Blocks`, deoarece puteți folosi o componentă pentru intrarea unei declanșări de eveniment, dar ieșirea alteia. Priviți componenta text din exemplul de mai jos, valoarea sa este rezultatul unui model speech-to-text, dar este transmisă și într-un model de analiză de sentiment:

    from transformers import pipeline

    import gradio as gr

    asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
    classifier = pipeline("text-classification")

    def speech_to_text(speech):
        text = asr(speech)["text"]
        return text

    def text_to_sentiment(text):
        return classifier(text)[0]["label"]

    demo = gr.Blocks()

    with demo:
        audio_file = gr.Audio(type="filepath")
        text = gr.Textbox()
        label = gr.Label()

        b1 = gr.Button("Recognize Speech")
        b2 = gr.Button("Classify Sentiment")

        b1.click(speech_to_text, inputs=audio_file, outputs=text)
        b2.click(text_to_sentiment, inputs=text, outputs=label)

    demo.launch()

###
Actualizarea Proprietăților Componentei

Până acum, am văzut cum să creăm evenimente pentru a actualiza valoarea unei alte componente. Dar ce se întâmplă dacă doriți să schimbați alte proprietăți ale unei componente, cum ar fi vizibilitatea unei cutii de text sau opțiunile dintr-un grup de butoane radio? Puteți face acest lucru prin returnarea metodei `update()` a unei clase de componentă în loc de o valoare de returnare obișnuită din funcția dvs.

Acest lucru este cel mai ușor ilustrat cu un exemplu:

    import gradio as gr

    def change_textbox(choice):
        if choice == "short":
            return gr.Textbox.update(lines=2, visible=True)
        elif choice == "long":
            return gr.Textbox.update(lines=8, visible=True)
        else:
            return gr.Textbox.update(visible=False)

    with gr.Blocks() as block:
        radio = gr.Radio(
            ["short", "long", "none"], label="What kind of essay would you like to write?"
        )
        text = gr.Textbox(lines=2, interactive=True)

        radio.change(fn=change_textbox, inputs=radio, outputs=text)
        block.launch()

Tocmai am explorat toate conceptele de bază ale `Blocks`! La fel ca în cazul `Interface`-urilor, puteți crea demo-uri cool care pot fi partajate folosind `share=True` în metoda `launch()` sau implementate pe Hugging Face Spaces.

< > Update on GitHub