Gradio: applicazioni web in python per AI [parte2]

Gradio è una libraria python che ci permette di creare applicazioni web in modo veloce e intuitivo per i nostri modelli di machine learning e AI. Le nostre applicazioni richiedono sempre un'interazione con l'utente e una personalizzazione del layout. Scopriamo, mediante degli esempi, come migliorare le nostre applicazioni.

Share

Reading time: 8 minutes

Nell’articolo precedente Gradio: applicazioni web in python per AI [parte1], abbiamo introdotto Gradio, una libreria per sviluppare velocemente applicazioni web per i nostri modelli di machine learning e intelligenza artificiale. Gli esempi visti usavano i componenti principali forniti da Gradio per gestire gli input e gli output. La libreria però ci permette di fare molto di più! Possiamo, infatti, inserire elementi interattivi, definire un layout personalizzato e creare applicazioni con più pagine. In questo articolo approfondiremo meglio questi aspetti mediante esempi.  Ricordiamo che tutto il codice è stato eseguito in Google Colab, ma potete eseguirlo comodamente in un notebook Jupyter o direttamente da shell. Andate a vedere nell’articolo Gradio: applicazioni web in python per AI [parte1] come configurare opportunamente  l’ambiente di sviluppo e lanciare l’applicazione.

Componenti interattivi

L’interattività è una caratteristica fondamentale nell’applicazioni web. L’utente deve, infatti, poter interagire con l’interfaccia mediante pulsanti, selezioni e inserimento di dati. I nostri modelli di machine learning e/o AI richiedono sempre dei parametri in input che devono essere forniti dall’utente. Vediamo di seguiti i componenti principali che possiamo inserire nella nostra interfaccia.

Pulsante

Utilizzando gradio.Button() è possibile definire un pulsante di invio per la propria applicazione specificando value = “Submit” e aggiungendo un evento gradio.Button.click().

				
					import gradio as gr

def combine(a, b):
    return "Hey! " + a + " " + b + '\n'+ " Welcome to Flowygo."

with gr.Blocks() as demo:
    txt = gr.Textbox(label="First Name", lines=2)
    txt_2 = gr.Textbox(label="Second Name")
    txt_3 = gr.Textbox(value="", label="Output")
    btn = gr.Button(value="Submit")
    btn.click(combine, inputs=[txt, txt_2], outputs=[txt_3])

if __name__ == "__main__":
    demo.launch()
				
			

Checkbox

Oltre al cursore, è possibile aggiungere una casella di controllo per effettuare le selezioni. L’esempio seguente combina un cursore e una casella di controllo per la selezione dell’ora.

				
					import gradio as gr

def sentence_builder( morning):
    return f"""It is a nice time take a {"morning" if morning else "later in the day"} glass of water"""

demo = gr.Interface(
    sentence_builder,
    [
        gr.Checkbox(label="Is it before noon"),
    ],
    "text")

if __name__ == "__main__":
    demo.launch()
				
			

Casella di selezione

Una casella di selezione viene creata utilizzando gradio.CheckboxGroup(). Questo componente consente di specificare un elenco di voci da selezionare.

				
					import gradio as gr

def sentence_builder(morning, activity_list):
    return f"""It is a nice time take a {"morning" if morning else "later in the day"} glass of water {" and take a ".join(activity_list)}"""

demo = gr.Interface(
    sentence_builder,
    [
        gr.Checkbox(label="Is it before noon"),
        gr.CheckboxGroup(["juice", "beverage", "snack", "nap"]),
    ],
    "text")

if __name__ == "__main__":
    demo.launch()
				
			

Inserimento della data

Attualmente, Gradio non supporta la selezione di date tramite un widget. Tuttavia, è possibile visualizzare la data corrente tra le altre operazioni utilizzando datetime come input nel componente Textbox().

				
					import gradio as gr
import datetime

with gr.Blocks() as demo:
    gr.Textbox(datetime.datetime.now)
    
demo.launch()
				
			

Colorpicker

Per generare colori casuali, si può usare gradio.ColorPicker().

				
					import gradio as gr
import cv2
import numpy as np
import random

# Convert decimal color to hexadecimal color
def RGB_to_Hex(rgb):
    color = "#"
    for i in rgb:
        num = int(i)
        color += str(hex(num))[-2:].replace("x", "0").upper()
    return color

# Randomly generate light or dark colors
def gen_random_color(is_light=True):
    return (
        random.randint(0, 127) + int(is_light) * 128,
        random.randint(0, 127) + int(is_light) * 128,
        random.randint(0, 127) + int(is_light) * 128,
    )

def change_color(color_style):
    if color_style == "light":
        is_light = True
    elif color_style == "dark":
        is_light = False
    back_color_ = gen_random_color(is_light)  # Randomly generate colors
    back_color = RGB_to_Hex(back_color_)  # Convert to hexadecimal

    # Draw color pictures.
    w, h = 50, 50
    img = np.zeros((h, w, 3), np.uint8)
    cv2.rectangle(img, (0, 0), (w, h), back_color_, thickness=-1)

    return back_color, back_color, img


inputs = [gr.Radio(["light", "dark"], value="light")]

outputs = [
    gr.ColorPicker(label="color"),
    gr.Textbox(label="hexadecimal color"),
    gr.Image(type="numpy", label="color picture", height=200),
]

title = "Random Color Generator"
description = (
    "By clicking submit, a new color will be generated"
)

demo = gr.Interface(
    fn=change_color,
    inputs=inputs,
    outputs=outputs,
    title=title,
    description=description,
)

if __name__ == "__main__":
    demo.launch()

				
			

Slider

gradio.Slider() crea un cursore con un intervallo di valori da minimo a massimo e un passo di dimensioni pari a step. La posizione predefinita del cursore è specificata dal parametro value. Per esempio, di seguito è riportata un’implementazione del cursore con minimo = 2, massimo = 24, passo = 2, con posizione predefinita del cursore.

				
					import gradio as gr

def sentence_builder(quantity, morning, activity_list):
    return f"""{quantity} o'clock is a nice time take a {"morning" if morning else "later in the day"} glass of water {" and take a ".join(activity_list)}"""

demo = gr.Interface(
    sentence_builder,
    [  gr.Slider(2, 24, value=4, step = 2),
        gr.Checkbox(label="Is it before noon"),
        gr.CheckboxGroup(["juice", "beverage", "snack", "nap"]),
    ],
    "text")

if __name__ == "__main__":
    demo.launch()
				
			

Dropdown

La funzione Dropdown() di Gradio consente di specificare un elemento da un elenco di voci possibili. Ad esempio, posiamo modificare l’applicazione precedente per definire un parametro aggiuntivo, denominato watch, che mostra ciò che un utente potrebbe fare in alternativa.

				
					import gradio as gr

def sentence_builder(quantity, morning, activity_list, watch):
    return f"""{quantity} o'clock is a nice time take a {"morning" if morning else "later in the day"} glass of water {" and take a ".join(activity_list)} or watch a {watch}"""

demo = gr.Interface(
    sentence_builder,
    [   gr.Slider(2, 24, value=4, step = 2),
        gr.Checkbox(label="Is it before noon"),
        gr.CheckboxGroup(["juice", "beverage", "snack", "nap"]),
        gr.Dropdown(["Television series", "Movie", "Documentary", "Class"]),
     
    ],
    "text")

if __name__ == "__main__":
    demo.launch()
				
			

Integrazione delle librerie di visualizzazione

Oltre ai componenti visti in precedenza, può essere molto utile inserire degli elementi grafici all’interno della nostra applicazione. Abbiamo già visto alcuni esempi nell’articolo XXX. Qui vi mostriamo come usare il componente di output Plot per creare grafici di dati con varie librerie di visualizzazione Python, tra cui Matplotlib, Bokeh e Plotly.

Utilizzo di Matplotlib e Seaborn

Per visualizzare dei dati utilizzando Seaborn o Matplotlib in Gradio è necessario usare matplotlib.plot() o seaborn.plot() in base alla libreria usata.

Matplotlib

				
					import gradio as gr
from math import log
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd


def gdp_change(r, year, country, smoothen):
    years = ['1850', '1900', '1950', '2000', '2050']
    m = years.index(year)
    start_day = 10* m
    final_day = 10* (m + 1)
    x = np.arange(start_day, final_day + 1)
    pop_count = {"USA": 350, "Canada": 40, "Mexico": 300, "UK": 120}
    if smoothen:
        r = log(r)
    df = pd.DataFrame({'day': x})
    df[country] = ( x ** (r) * (pop_count[country] + 1))
    fig = plt.figure()
    plt.plot(df['day'], df[country].to_numpy(), label = country)
    plt.title("GDP in " + year)
    plt.ylabel("GDP (Millions)")
    plt.xlabel("Population Change since 1800")
    plt.grid()
    return fig

inputs = [
        gr.Slider(1, 4, 3.2, label="R"),
        gr.Dropdown(['1850', '1900', '1950', '2000', '2050'], label="Year"),
        gr.Radio(["USA", "Canada", "Mexico", "UK"], label="Countries", ),
        gr.Checkbox(label="Log of GDP Growth Rate?"),
    ]
outputs = gr.Plot()

demo = gr.Interface(fn=gdp_change, inputs=inputs, outputs=outputs)

demo.launch()

				
			

Seaborn

In modo analogo possiamo usare Seaborn. Pertanto, definiamo una funzione di plottaggio e posizioniamo il grafico come output.

				
					import gradio as gr
from math import log
import seaborn as sns

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

def gdp_change(r, year, country, smoothen):
    years = ['1850', '1900', '1950', '2000', '2050']
    m = years.index(year)
    start_day = 10* m
    final_day = 10* (m + 1)
    x = np.arange(start_day, final_day + 1)
    pop_count = {"USA": 350, "Canada": 40, "Mexico": 300, "UK": 120}
    if smoothen:
        r = log(r)
    df = pd.DataFrame({'day': x})
    df[country] = ( x ** (r) * (pop_count[country] + 1))
    fig = plt.figure()
    sns.lineplot(x = df['day'], y = df[country].to_numpy())
    plt.title("GDP in " + year)
    plt.ylabel("GDP (Millions)")
    plt.xlabel("Population Change since 1800")
    plt.grid()
    return fig

inputs = [
        gr.Slider(1, 4, 3.2, label="R"),
        gr.Dropdown(['1850', '1900', '1950', '2000', '2050'], label="year"),
        gr.Radio(["USA", "Canada", "Mexico", "UK"], label="Countries", ),
        gr.Checkbox(label="Log of GDP Growth Rate?"),
    ]
outputs = gr.Plot()

demo = gr.Interface(fn=gdp_change, inputs=inputs, outputs=outputs)

demo.launch()

				
			

Plotly

E’ possibile anche usare la libreria Ploty. In questo caso, nella funzione di plottaggio gdp_change(…), definiamo un oggetto di visualizzazione plotly per poi passarlo a gradio.Plot().

				
					import gradio as gr
from math import log
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import plotly.express as px
import pandas as pd

def gdp_change(r, year, country, smoothen):
    years = ['1850', '1900', '1950', '2000', '2050']
    m = years.index(year)
    start_day = 10* m
    final_day = 10* (m + 1)
    x = np.arange(start_day, final_day + 1)
    pop_count = {"USA": 350, "Canada": 40, "Mexico": 300, "UK": 120}
    if smoothen:
        r = log(r)
    df = pd.DataFrame({'day': x})
    df[country] = ( x ** (r) * (pop_count[country] + 1))
    fig = px.line(df, x='day', y=df[country].to_numpy())
    fig.update_layout(title="GDP in " + year,
                yaxis_title="GDP",
                xaxis_title="Population change since 1800s")
    return fig

inputs = [
        gr.Slider(1, 4, 3.2, label="R"),
        gr.Dropdown(['1850', '1900', '1950', '2000', '2050'], label="year"),
        gr.Radio(["USA", "Canada", "Mexico", "UK"], label="Countries", ),
        gr.Checkbox(label="Log of GDP Growth Rate?"),
    ]
outputs = gr.Plot()

demo = gr.Interface(fn=gdp_change, inputs=inputs, outputs=outputs)

demo.launch()
				
			

Visualizzare le mappe

Oltre a grafici a linea è possibile, ovviamente, integrare altri tipi di grafici. Ade sempio possiamo creare delle mappe con le librerie Plotly o Seaborn che vengono poi visualizzati sempre mediante la funzione gradio.Plot().

				
					import plotly.express as px
import pandas as pd
def map_plot():
  #define a map element
    df = px.data.gapminder().query("year==2002")
    fig = px.scatter_geo(df, locations="iso_alpha", color="continent",
                        hover_name="country", size="lifeExp",
                        projection="natural earth")
    return fig
outputs = gr.Plot()

demo = gr.Interface(fn=map_plot, inputs=None, outputs=outputs)

demo.launch()
				
			

Definire il layout grafico

Il layout dei blocchi dell’applicazione può essere personalizzato utilizzando classi di layout come gradio.Row(), gradio.Columns(), gradio.Tab() e gradio.Accordion().

Righe

Quando si utilizza il layout a righe, tutti i blocchi dell’applicazione saranno resi in orizzontale.

				
					import gradio as gr
with gr.Blocks() as demo:
    with gr.Row():
        gr.Text()
        gr.Text()
demo.launch()
				
			

Colonne

Con gradio.Column() è possibile rendere verticali i blocchi dell’applicazione. È possibile specificare la larghezza delle colonne indicando i parametri scale e min_width.

				
					import gradio as gr
with gr.Blocks() as demo:
    with gr.Column(scale=2):
         btn1 = gr.Button("Button 1")
         btn2 = gr.Button("Button 2")
    with gr.Column(scale=1):
         text1 = gr.Textbox()
         text2 = gr.Textbox()
demo.launch()
				
			

Tab e accordion

Le applicazioni Gradio possono anche essere disposte in forma tabellare utilizzando gradio.Tab(). I componenti di una particolare scheda verranno visualizzati quando l’utente naviga nella scheda corrispondente. A ogni scheda viene assegnata un’etichetta.

				
					import gradio as gr
with gr.Blocks() as demo:
    with gr.Tab(label = "Button tab"):
         btn1 = gr.Button("Button 1")
         btn2 = gr.Button("Button 2")
    with gr.Tab(label = "Textbox tab"):
         text1 = gr.Textbox()
         text2 = gr.Textbox()
demo.launch()
				
			

Un accordion restituisce la funzione update() del componente con la funzionalità per alternare e nascondere il contenuto.

				
					import gradio as gr
with gr.Blocks() as demo:
    with gr.Accordion("Display Details"):
        gr.Markdown("Machine Learning and Big Data")
demo.launch()

				
			

Visibilità

Dopo aver passato una funzione nell’interfaccia di Gradio o in un blocco, viene restituito un valore tipico. Per poter aggiornare il blocco, non è più possibile usare la funzione update, ma bisogna restituire il nuovo componente con i parametri settati. Nell’esempio successivo mostriamo come visualizzare o meno una textbox e definire il numero di righe che la compongono in base alle scelte dell’utente.

				
					import gradio as gr
def update_textbox(choice):
  if choice == "short":
      return gr.Textbox(lines=1, visible=True)
  elif choice == "long":
      return gr.Textbox(lines=6, visible=True)
  else:
      return gr.Textbox(visible=False)
gr.Interface(
  update_textbox,
  gr.Radio(
      ["short", "long", "No message"], label="What kind of message would you like to send?"
  ),
  gr.Textbox(lines=2),
  live=True,
).launch()

				
			

CSS e Javascript personalizzati 

Fino ad adesso abbiamo visto applicazioni con un layout predefinito dalla libreria. È comunque possibile creare uno stile personalizzato per la propria applicazione passando un CSS come argomento. Ad esempio, nell’esempio del layout della scheda, è possibile definire un CSS di sfondo personalizzato come segue:

				
					import gradio as gr
with gr.Blocks(css=".gradio-container {background-color: grey}") as demo:
    with gr.Tab(label = "Button tab"):
         btn1 = gr.Button("Button 1")
         btn2 = gr.Button("Button 2")
    with gr.Tab(label = "Textbox tab"):
         text1 = gr.Textbox()
         text2 = gr.Textbox()
demo.launch()

				
			

Allo stesso modo, è possibile definire JS personalizzati per l’applicazione.

				
					import gradio as gr

blocks = gr.Blocks()

with blocks as demo:
    subject = gr.Radio(["Analyse", "Explore", "Learn"])
    verb = gr.Radio(["GDP Change", "Population Growth", "Big Data"])
    object = gr.Textbox(placeholder="region")

    with gr.Row():
        btn = gr.Button("Create sentence.")
        reverse_btn = gr.Button("Reverse sentence.")

    def sentence_maker(w1, w2, w3):
        return f"{w1} {w2} in {w3}"

    output1 = gr.Textbox(label="output 1")
    output2 = gr.Textbox(label="verb reversed")

    btn.click(sentence_maker, [subject, verb, object], output1)
    #custom JS to reverse the sentense
    reverse_btn.click(None, [subject, verb, object], output2, js="(s, v, o) => o + ' ' + v + ' ' + s")

demo.launch()


				
			

Applicazioni a più pagine

Dopo aver creato diverse app, è possibile utilizzare i seguenti metodi per combinarle insieme in più pagine:

  • TabbedInterfaces() e gradio.Parallel() per confrontare gli output di più app
  • Series() per alimentare gli input da un’applicazione all’altra

In questa sezione, utilizzeremo gradio.TabbedInterface() per creare un’applicazione a più pagine per le applicazioni Dropdown e Seaborn create in precedenza.

				
					#define your first application
import gradio as gr
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from math import log
def user_greeting(name):
    return "Hi! " + name + " Welcome to your multi-page application!😎"

#app 1
app =  gr.Interface(fn = user_greeting, inputs="text", outputs="text", examples = ["Zenith", "Antoinne", "Amelia", "Johanna"])

#define your second application
def gdp_change(r, year, country, smoothen):
    years = ['1850', '1900', '1950', '2000', '2050']
    m = years.index(year)
    start_day = 10* m
    final_day = 10* (m + 1)
    x = np.arange(start_day, final_day + 1)
    pop_count = {"USA": 350, "Canada": 40, "Mexico": 300, "UK": 120}
    if smoothen:
        r = log(r)
    df = pd.DataFrame({'day': x})
    df[country] = ( x ** (r) * (pop_count[country] + 1))
    fig = plt.figure()
    sns.lineplot(x = df['day'], y = df[country].to_numpy())
    plt.title("GDP in " + year)
    plt.ylabel("GDP (Millions)")
    plt.xlabel("Population Change since 1800s")
    plt.grid()
    return fig

inputs = [
        gr.Slider(1, 4, 3.2, label="R"),
        gr.Dropdown(['1850', '1900', '1950', '2000', '2050'], label="year"),
        gr.Radio(["USA", "Canada", "Mexico", "UK"], label="Countries", ),
        gr.Checkbox(label="Log of GDP Growth Rate?"),
    ]
outputs = gr.Plot()

#app 2
app2 = gr.Interface(fn=gdp_change, inputs=inputs, outputs=outputs)
#combine to create a multipage app
demo = gr.TabbedInterface([app, app2], ["Welcome page", "Visualization page"])

if __name__ == "__main__":
    demo.launch()

				
			

More To Explore

Python

Pandas: analisi dati con Python [parte 1]

I data scientists necessitano continuamente di leggere, manipolare e analizzare i dati. In molti casi si utilizzano dei tools specifici, ma a volte è necessario sviluppare il proprio codice. Per far ciò la libreria Pandas ci viene in aiuto. Scopriamo le sue strutture dati, come possiamo leggere i dati da diversi fonti e manipolarli per ii nostri scopi.

Intelligenza artificiale

Gradio: applicazioni web in python per AI [parte 3]

Con Gradio è possibile creare applicazioni web per i nostri modelli di machine learning e AI in poche righe di codice. Mediante alcuni esempi, vedremo le funzionalità avanzate disponibili, quali l’autenticazione, il caching e l’elaborazione dei file in ingresso. Costruiremo anche un chatbot e un classificatore di immagini partendo da modelli pre-addestrati. Infine discuteremo come distribuire il nostro progetto in pochi semplici passi.

Una risposta

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Progetta con MongoDB!!!

Acquista il nuovo libro che ti aiuterà a usare correttamente MongoDB per le tue applicazioni. Disponibile ora su Amazon!

Design with MongoDB

Design with MongoDB!!!

Buy the new book that will help you to use MongoDB correctly for your applications. Available now on Amazon!