赞
踩
常用的两款AI可视化交互应用比较:
Gradio
Gradio的优势在于易用性,代码结构相比Streamlit简单,只需简单定义输入和输出接口即可快速构建简单的交互页面,更轻松部署模型。适合场景相对简单,想要快速部署应用的开发者。
Streamlit
Streamlit的优势在于可扩展性,相比Gradio复杂,完全熟练使用需要一定时间。可以使用Python编写完整的包含前后端的交互式应用。适合场景相对复杂,想要构建丰富多样交互页面的开发者。
Gradio官网链接:https://gradio.app/
常用的两款AI可视化交互应用比较:
Gradio
Gradio的优势在于易用性,代码结构相比Streamlit简单,只需简单定义输入和输出接口即可快速构建简单的交互页面,更轻松部署模型。适合场景相对简单,想要快速部署应用的开发者。便于分享:gradio可以在启动应用时设置share=True参数创建外部分享链接,可以直接在微信中分享给用户使用。
方便调试:gradio可以在jupyter中直接展示页面,更加方便调试。
Streamlit
Streamlit的优势在于可扩展性,相比Gradio复杂,完全熟练使用需要一定时间。可以使用Python编写完整的包含前后端的交互式应用。适合场景相对复杂,想要构建丰富多样交互页面的开发者。
Gradio官网链接:https://gradio.app/
Python第三方库Gradio快速上手,当前版本V3.27.0
pip install gradio
#为了更快安装,可以使用清华镜像源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gradio
安装完直接在IDE上启动快速,
import gradio as gr
#输入文本处理程序
def greet(name):
return "Hello " + name + "!"
#接口创建函数
#fn设置处理函数,inputs设置输入接口组件,outputs设置输出接口组件
#fn,inputs,outputs都是必填函数
demo = gr.Interface(fn=greet, inputs="text", outputs="text")
demo.launch()
运行程序后,打开 http://localhost:7860 即可看到网页效果。左边是文本输入框,右边是结果展示框。Clear按钮用于重置网页状态,Submit按钮用于执行处理程序,Flag按钮用于保存结果到本地。
#执行结果
Running on local URL: http://127.0.0.1:7860
To create a public link, set `share=True` in `launch()`.
打开浏览器使用即可
在本地开发时,如果你想将代码作为Python脚本运行,你可以使用Gradio CLI在重载模式下启动应用程序,这将提供无缝和快速的开发。
gradio app.py
Note:你也可以做python app.py,但它不会提供自动重新加载机制。
Gradio 可以包装几乎任何 Python 函数为易于使用的用户界面。从上面例子我们看到,简单的基于文本的函数。但这个函数还可以处理很多类型。
Interface类通过以下三个参数进行初始化:
通过这三个参数,我们可以快速创建一个接口并发布他们。
最常用的基础模块构成。
应用界面:gr.Interface(简易场景), gr.Blocks(定制化场景)
输入输出:gr.Image(图像), gr.Textbox(文本框), gr.DataFrame(数据框), gr.Dropdown(下拉选项), gr.Number(数字), gr.Markdown, gr.Files
控制组件:gr.Button(按钮)
布局组件:gr.Tab(标签页), gr.Row(行布局), gr.Column(列布局)
import gradio as gr
def greet(name):
return "Hello " + name + "!"
demo = gr.Interface(
fn=greet,
# 自定义输入框
# 具体设置方法查看官方文档
inputs=gr.Textbox(lines=3, placeholder="Name Here...",label="my input"),
outputs="text",
)
demo.launch()
Interface.launch()方法返回三个值
import gradio as gr
def greet(name):
return "Hello " + name + "!"
iface = gr.Interface(
fn=greet,
inputs=gr.inputs.Textbox(lines=2, placeholder="Name Here..."),
outputs="text",
)
if __name__ == "__main__":
app, local_url, share_url = iface.launch()
对于复杂程序,输入列表中的每个组件按顺序对应于函数的一个参数。输出列表中的每个组件按顺序排列对应于函数返回的一个值。
import gradio as gr
#该函数有3个输入参数和2个输出参数
def greet(name, is_morning, temperature):
salutation = "Good morning" if is_morning else "Good evening"
greeting = f"{salutation} {name}. It is {temperature} degrees today"
celsius = (temperature - 32) * 5 / 9
return greeting, round(celsius, 2)
demo = gr.Interface(
fn=greet,
#按照处理程序设置输入组件
inputs=["text", "checkbox", gr.Slider(0, 100)],
#按照处理程序设置输出组件
outputs=["text", "number"],
)
demo.launch()
inputs列表里的每个字段按顺序对应函数的每个参数,outputs同理。
Gradio支持许多类型的组件,如image、dataframe、video。使用示例如下:
import numpy as np
import gradio as gr
def sepia(input_img):
#处理图像
sepia_filter = np.array([
[0.393, 0.769, 0.189],
[0.349, 0.686, 0.168],
[0.272, 0.534, 0.131]
])
sepia_img = input_img.dot(sepia_filter.T)
sepia_img /= sepia_img.max()
return sepia_img
#shape设置输入图像大小
demo = gr.Interface(sepia, gr.Image(shape=(200, 200)), "image")
demo.launch()
当使用Image组件作为输入时,函数将收到一个维度为(w,h,3)的numpy数组,按照RGB的通道顺序排列。要注意的是,我们的输入图像组件带有一个编辑按钮,可以对图像进行裁剪和放大。以这种方式处理图像可以帮助揭示机器学习模型中的偏差或隐藏的缺陷。此外对于输入组件有个shape参数,指的设置输入图像大小。但是处理方式是保持长宽比的情况下,将图像最短边缩放为指定长度,然后按照中心裁剪方式裁剪最长边到指定长度。当图像不大的情况,一种更好的方式是不设置shape,这样直接传入原图。输入组件Image也可以设置输入类型type,比如type=filepath设置传入处理图像的路径。具体可以查看官方文档,文档写的很清楚。
在Interface添加live=True参数,只要输入发生变化,结果马上发生改变。
import gradio as gr def calculator(num1, operation, num2): if operation == "add": return num1 + num2 elif operation == "subtract": return num1 - num2 elif operation == "multiply": return num1 * num2 elif operation == "divide": return num1 / num2 iface = gr.Interface( calculator, ["number", gr.inputs.Radio(["add", "subtract", "multiply", "divide"]), "number"], "number", live=True, ) iface.launch()
import gradio as gr #一个简单计算器,含实例说明 def calculator(num1, operation, num2): if operation == "add": return num1 + num2 elif operation == "subtract": return num1 - num2 elif operation == "multiply": return num1 * num2 elif operation == "divide": if num2 == 0: # 设置报错弹窗 raise gr.Error("Cannot divide by zero!") return num1 / num2 demo = gr.Interface( calculator, # 设置输入 [ "number", gr.Radio(["add", "subtract", "multiply", "divide"]), "number" ], # 设置输出 "number", # 设置输入参数示例 examples=[ [5, "add", 3], [4, "divide", 2], [-4, "multiply", 2.5], [0, "subtract", 1.2], ], # 设置网页标题 title="Toy Calculator", # 左上角的描述文字 description="Here's a sample toy calculator. Enjoy!", # 左下角的文字 article = "Check out the examples", ) demo.launch()
全局变量的好处就是在调用函数后仍然能够保存,例如在机器学习中通过全局变量从外部加载一个大型模型,并在函数内部使用它,以便每次函数调用都不需要重新加载模型。下面就展示了全局变量使用的好处。
import gradio as gr
scores = []
def track_score(score):
scores.append(score)
#返回分数top3
top_scores = sorted(scores, reverse=True)[:3]
return top_scores
demo = gr.Interface(
track_score,
gr.Number(label="Score"),
gr.JSON(label="Top Scores")
)
demo.launch()
Gradio支持的另一种数据持久性是会话状态,数据在一个页面会话中的多次提交中持久存在。然而,数据不会在你模型的不同用户之间共享。会话状态的典型例子就是聊天机器人,你想访问用户之前提交的信息,但你不能将聊天记录存储在一个全局变量中,因为那样的话,聊天记录会在不同的用户之间乱成一团。注意该状态会在每个页面内的提交中持续存在,但如果您在另一个标签页中加载该演示(或刷新页面),该演示将不会共享聊天历史。
要在会话状态下存储数据,你需要做三件事。
import random import gradio as gr def chat(message, history): history = history or [] message = message.lower() if message.startswith("how many"): response = random.randint(1, 10) elif message.startswith("how"): response = random.choice(["Great", "Good", "Okay", "Bad"]) elif message.startswith("where"): response = random.choice(["Here", "There", "Somewhere"]) else: response = "I don't know" history.append((message, response)) return history, history #设置一个对话窗 chatbot = gr.Chatbot().style(color_map=("green", "pink")) demo = gr.Interface( chat, # 添加state组件 ["text", "state"], [chatbot, "state"], # 设置没有保存数据的按钮 allow_flagging="never", ) demo.launch()
在Interface中设置live=True,则输出会跟随输入实时变化。这个时候界面不会有submit按钮,因为不需要手动提交输入。
同1.2.4
在许多情形下,我们的输入是实时视频流或者音频流,那么意味这数据不停地发送到后端,这是可以采用streaming模式处理数据。
import gradio as gr
import numpy as np
def flip(im):
return np.flipud(im)
demo = gr.Interface(
flip,
gr.Image(source="webcam", streaming=True),
"image",
live=True
)
demo.launch()
相比Interface,Blocks提供了一个低级别的API,用于设计具有更灵活布局和数据流的网络应用。Blocks允许控制组件在页面上出现的位置,处理复杂的数据流(例如,输出可以作为其他函数的输入),并根据用户交互更新组件的属性可见性。可以定制更多组件,更多详细定制可查看文档
import gradio as gr
def greet(name):
return "Hello " + name + "!"
with gr.Blocks() as demo:
#设置输入组件
name = gr.Textbox(label="Name")
# 设置输出组件
output = gr.Textbox(label="Output Box")
#设置按钮
greet_btn = gr.Button("Greet")
#设置按钮点击事件
greet_btn.click(fn=greet, inputs=name, outputs=output)
demo.launch()
Blocks方式需要with语句添加组件,如果不设置布局方式,那么组件将按照创建的顺序垂直出现在应用程序中,运行界面
import numpy as np import gradio as gr def flip_text(x): return x[::-1] def flip_image(x): return np.fliplr(x) with gr.Blocks() as demo: #用markdown语法编辑输出一段话 gr.Markdown("Flip text or image files using this demo.") # 设置tab选项卡 with gr.Tab("Flip Text"): #Blocks特有组件,设置所有子组件按垂直排列 #垂直排列是默认情况,不加也没关系 with gr.Column(): text_input = gr.Textbox() text_output = gr.Textbox() text_button = gr.Button("Flip") with gr.Tab("Flip Image"): #Blocks特有组件,设置所有子组件按水平排列 with gr.Row(): image_input = gr.Image() image_output = gr.Image() image_button = gr.Button("Flip") #设置折叠内容 with gr.Accordion("Open for More!"): gr.Markdown("Look at me...") text_button.click(flip_text, inputs=text_input, outputs=text_output) image_button.click(flip_image, inputs=image_input, outputs=image_output) demo.launch()
相信有小伙伴已经注意到,输出框下有个Flag按钮。当测试您的模型的用户看到某个输入导致输出错误或意外的模型行为,他们可以标记这个输入让开发者知道。这个文件夹由Interface的flagging_dir参数指定,默认为’flagged’。将这些会导致错误的输入保存到一个csv文件。如果Interface包含文件数据,文件夹也会创建来保存这些标记数据。
打开log.csv展示如下:
在Gradio官方文档,搜索不同的组件加.style(如image.style),可以获取该组件的样式参数设置样例。例如image组件的设置如下:
img = gr.Image("lion.jpg").style(height='24', rounded=False)
demo = gr.Interface(...).queue()
demo.launch()
#或
with gr.Blocks() as demo:
#...
demo.queue()
demo.launch()
在某些情况下,你可能想显示一连串的输出,而不是单一的输出。例如,你可能有一个图像生成模型,如果你想显示在每个步骤中生成的图像,从而得到最终的图像。在这种情况下,你可以向Gradio提供一个生成器函数,而不是一个常规函数。下面是一个生成器的例子,每隔1秒返回1张图片。
import gradio as gr import numpy as np import time #生成steps张图片,每隔1秒钟返回 def fake_diffusion(steps): for _ in range(steps): time.sleep(1) image = np.random.randint(255, size=(300, 600, 3)) yield image demo = gr.Interface(fake_diffusion, #设置滑窗,最小值为1,最大值为10,初始值为3,每次改动增减1位 inputs=gr.Slider(1, 10, value=3, step=1), outputs="image") #生成器必须要queue函数 demo.queue() demo.launch()
任何输入的组件内容都是可编辑的,而输出组件默认是不能编辑的。如果想要使得输出组件内容可编辑,设置interactive=True即可。
import gradio as gr
def greet(name):
return "Hello " + name + "!"
with gr.Blocks() as demo:
name = gr.Textbox(label="Name")
# 不可交互
# output = gr.Textbox(label="Output Box")
# 可交互
output = gr.Textbox(label="Output", interactive=True)
greet_btn = gr.Button("Greet")
greet_btn.click(fn=greet, inputs=name, outputs=output)
demo.launch()
我们可以为不同的组件设置不同事件,如为输入组件添加change事件。可以进一步查看官方文档,看看组件还有哪些事件。
import gradio as gr
def welcome(name):
return f"Welcome to Gradio, {name}!"
with gr.Blocks() as demo:
gr.Markdown(
"""
# Hello World!
Start typing below to see the output.
""")
inp = gr.Textbox(placeholder="What is your name?")
out = gr.Textbox()
#设置change事件
inp.change(fn = welcome, inputs = inp, outputs = out)
demo.launch()
如果想处理多个数据流,只要设置相应的输入输出组件即可。
import gradio as gr
def increase(num):
return num + 1
with gr.Blocks() as demo:
a = gr.Number(label="a")
b = gr.Number(label="b")
# 要想b>a,则使得b = a+1
atob = gr.Button("b > a")
atob.click(increase, a, b)
# 要想a>b,则使得a = b+1
btoa = gr.Button("a > b")
btoa.click(increase, b, a)
demo.launch()
import gradio as gr with gr.Blocks() as demo: food_box = gr.Number(value=10, label="Food Count") status_box = gr.Textbox() def eat(food): if food > 0: return food - 1, "full" else: return 0, "hungry" gr.Button("EAT").click( fn=eat, inputs=food_box, #根据返回值改变输入组件和输出组件 outputs=[food_box, status_box] ) demo.launch()
事件监听器函数的返回值通常是相应的输出组件的更新值。有时我们也想更新组件的配置,比如说可见性。在这种情况下,我们可以通过返回update函数更新组件的配置。
import gradio as gr def change_textbox(choice): #根据不同输入对输出控件进行更新 if choice == "short": return gr.update(lines=2, visible=True, value="Short story: ") elif choice == "long": return gr.update(lines=8, visible=True, value="Long story...") else: return gr.update(visible=False) with gr.Blocks() as demo: radio = gr.Radio( ["short", "long", "none"], label="Essay Length to Write?" ) text = gr.Textbox(lines=2, interactive=True) radio.change(fn=change_textbox, inputs=radio, outputs=text) demo.launch()
Blocks应用的是html中的flexbox模型布局,默认情况下组件垂直排列。
使用Row函数会将组件按照水平排列,但是在Row函数块里面的组件都会保持同等高度。
import gradio as gr
with gr.Blocks() as demo:
with gr.Row():
img1 = gr.Image()
text1 = gr.Text()
btn1 = gr.Button("button")
demo.launch()
组件通常是垂直排列,我们可以通过Row函数和Column函数生成不同复杂的布局。
import gradio as gr with gr.Blocks() as demo: with gr.Row(): text1 = gr.Textbox(label="t1") slider2 = gr.Textbox(label="s2") drop3 = gr.Dropdown(["a", "b", "c"], label="d3") with gr.Row(): # scale与相邻列相比的相对宽度。例如,如果列A的比例为2,列B的比例为1,则A的宽度将是B的两倍。 # min_width设置最小宽度,防止列太窄 with gr.Column(scale=2, min_width=600): text1 = gr.Textbox(label="prompt 1") text2 = gr.Textbox(label="prompt 2") inbtw = gr.Button("Between") text4 = gr.Textbox(label="prompt 1") text5 = gr.Textbox(label="prompt 2") with gr.Column(scale=1, min_width=600): img1 = gr.Image("test.jpg") btn = gr.Button("Go") demo.launch()
如下所示,我们可以通过visible和update函数构建更为复杂的应用。
import gradio as gr with gr.Blocks() as demo: # 出错提示框 error_box = gr.Textbox(label="Error", visible=False) # 输入框 name_box = gr.Textbox(label="Name") age_box = gr.Number(label="Age") symptoms_box = gr.CheckboxGroup(["Cough", "Fever", "Runny Nose"]) submit_btn = gr.Button("Submit") # 输出不可见 with gr.Column(visible=False) as output_col: diagnosis_box = gr.Textbox(label="Diagnosis") patient_summary_box = gr.Textbox(label="Patient Summary") def submit(name, age, symptoms): if len(name) == 0: return {error_box: gr.update(value="Enter name", visible=True)} if age < 0 or age > 200: return {error_box: gr.update(value="Enter valid age", visible=True)} return { output_col: gr.update(visible=True), diagnosis_box: "covid" if "Cough" in symptoms else "flu", patient_summary_box: f"{name}, {age} y/o" } submit_btn.click( submit, [name_box, age_box, symptoms_box], [error_box, diagnosis_box, patient_summary_box, output_col], ) demo.launch()
在某些情况下,您可能希望在实际在UI中呈现组件之前定义组件。例如,您可能希望在相应的gr.Textbox输入上方显示使用gr.examples的示例部分。由于gr.Examples需要输入组件对象作为参数,因此您需要先定义输入组件,然后在定义gr.Exmples对象后再进行渲染。解决方法是在gr.Blocks()范围外定义gr.Textbox,并在UI中希望放置的任何位置使用组件的.render()方法。
import gradio as gr
input_textbox = gr.Textbox()
with gr.Blocks() as demo:
#提供示例输入给input_textbox,示例输入以嵌套列表形式设置
gr.Examples(["hello", "bonjour", "merhaba"], input_textbox)
# render函数渲染input_textbox
input_textbox.render()
demo.launch()
要获得额外的样式功能,您可以设置行内css属性将任何样式给应用程序。如下所示。
import gradio as gr
#修改blocks的背景颜色
with gr.Blocks(css=".gradio-container {background-color: red}") as demo:
box1 = gr.Textbox(value="Good Job")
box2 = gr.Textbox(value="Failure")
demo.launch()
您可以向任何组件添加HTML元素。通过elem_id选择对应的css元素。
import gradio as gr
# 这里用的是id属性设置
with gr.Blocks(css="#warning {background-color: red}") as demo:
box1 = gr.Textbox(value="Good Job", elem_id="warning")
box2 = gr.Textbox(value="Failure")
box3 = gr.Textbox(value="None", elem_id="warning")
demo.launch()
如果运行环境能够连接互联网,在launch函数中设置share参数为True,那么运行程序后。Gradio的服务器会提供XXXXX.gradio.app地址。通过其他设备,比如手机或者笔记本电脑,都可以访问该应用。这种方式下该链接只是本地服务器的代理,不会存储通过本地应用程序发送的任何数据。这个链接在有效期内是免费的,好处就是不需要自己搭建服务器,坏处就是太慢了,毕竟数据经过别人的服务器。
demo.launch(share=True)
为了便于向合作伙伴永久展示我们的模型App,可以将gradio的模型部署到 HuggingFace的 Space托管空间中,完全免费的哦。
方法如下:
1,注册huggingface账号:https://huggingface.co/join
2,在space空间中创建项目:https://huggingface.co/spaces
3,创建好的项目有一个Readme文档,可以根据说明操作,也可以手工编辑app.py和requirements.txt文件。
通过设置server_name=‘0.0.0.0’(表示使用本机ip),server_port(可不改,默认值是7860)。那么可以通过本机ip:端口号在局域网内分享应用。
#show_error为True表示在控制台显示错误信息。
demo.launch(server_name='0.0.0.0', server_port=8080, show_error=True)
这里host地址可以自行在电脑查询,C:\Windows\System32\drivers\etc\hosts 修改一下即可 127.0.0.1再制定端口号
在首次打开网页前,可以设置账户密码。比如auth参数为(账户,密码)的元组数据。这种模式下不能够使用queue函数。
demo.launch(auth=("admin", "pass1234"))
如果想设置更为复杂的账户密码和密码提示,可以通过函数设置校验规则。
#账户和密码相同就可以通过
def same_auth(username, password):
return username == password
demo.launch(auth=same_auth,auth_message="username and password must be the same")
#!pip install gradio, ultralytics, transformers, torchkeras
import gradio as gr
from transformers import pipeline
pipe = pipeline("text-classification")
def clf(text):
result = pipe(text)
label = result[0]['label']
score = result[0]['score']
res = {label:score,'POSITIVE' if label=='NEGATIVE' else 'NEGATIVE': 1-score}
return res
demo = gr.Interface(fn=clf, inputs="text", outputs="label")
gr.close_all()
demo.launch(share=True)
import gradio as gr import pandas as pd from ultralytics import YOLO from skimage import data from PIL import Image model = YOLO('yolov8n-cls.pt') def predict(img): result = model.predict(source=img) df = pd.Series(result[0].names).to_frame() df.columns = ['names'] df['probs'] = result[0].probs df = df.sort_values('probs',ascending=False) res = dict(zip(df['names'],df['probs'])) return res gr.close_all() demo = gr.Interface(fn = predict,inputs = gr.Image(type='pil'), outputs = gr.Label(num_top_classes=5), examples = ['cat.jpeg','people.jpeg','coffee.jpeg']) demo.launch()
import gradio as gr import pandas as pd from skimage import data from ultralytics.yolo.data import utils model = YOLO('yolov8n.pt') #load class_names yaml_path = str(Path(ultralytics.__file__).parent/'datasets/coco128.yaml') class_names = utils.yaml_load(yaml_path)['names'] def detect(img): if isinstance(img,str): img = get_url_img(img) if img.startswith('http') else Image.open(img).convert('RGB') result = model.predict(source=img) if len(result[0].boxes.boxes)>0: vis = plots.plot_detection(img,boxes=result[0].boxes.boxes, class_names=class_names, min_score=0.2) else: vis = img return vis with gr.Blocks() as demo: gr.Markdown("# yolov8目标检测演示") with gr.Tab("捕捉摄像头喔"): in_img = gr.Image(source='webcam',type='pil') button = gr.Button("执行检测",variant="primary") gr.Markdown("## 预测输出") out_img = gr.Image(type='pil') button.click(detect, inputs=in_img, outputs=out_img) gr.close_all() demo.queue(concurrency_count=5) demo.launch()
尽管gradio的设计初衷是为了快速创建机器学习用户交互页面。但实际上,通过组合gradio的各种组件,用户可以很方便地实现非常实用的各种应用小工具。
例如: 数据分析展示dashboard, 数据标注工具, 制作一个小游戏界面等等。
本范例我们将应用 gradio来构建一个图片筛选器,从百度爬取的一堆猫咪表情包中刷选一些我们喜欢的出来。
#!pip install -U torchkeras
import torchkeras from torchkeras.data import download_baidu_pictures download_baidu_pictures('猫咪表情包',100) import gradio as gr from PIL import Image import time,os from pathlib import Path base_dir = '猫咪表情包' selected_dir = 'selected' files = [str(x) for x in Path(base_dir).rglob('*.jp*g') if 'checkpoint' not in str(x)] def show_img(path): return Image.open(path) def fn_before(done,todo): ... return done,todo,path,img def fn_next(done,todo): ... return done,todo,path,img def save_selected(img_path): ... return msg def get_default_msg(): ... return msg with gr.Blocks() as demo: with gr.Row(): total = gr.Number(len(files),label='总数量') with gr.Row(scale = 1): bn_before = gr.Button("上一张") bn_next = gr.Button("下一张") with gr.Row(scale = 2): done = gr.Number(0,label='已完成') todo = gr.Number(len(files),label='待完成') path = gr.Text(files[0],lines=1, label='当前图片路径') feedback_button = gr.Button("选择图片",variant="primary") msg = gr.TextArea(value=get_default_msg,lines=3,max_lines = 5) img = gr.Image(value = show_img(files[0]),type='pil') bn_before.click(fn_before, inputs= [done,todo], outputs=[done,todo,path,img]) bn_next.click(fn_next, inputs= [done,todo], outputs=[done,todo,path,img]) feedback_button.click(save_selected, inputs = path, outputs = msg ) demo.launch()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。