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()