Chuyển tới nội dung chính

🐍 Ngày 49 Python 365 ngày | Xây dựng demo AI với Gradio

🗓 Ngày 49: Đưa model AI cho người khác dùng trong 5 phút​

Streamlit rất tốt cho dashboard dữ liệu. Nhưng khi mục tiêu là "cho người khác thử model AI của tôi", Gradio là công cụ chuyên dụng: nó được thiết kế riêng cho input/output của model, hỗ trợ ảnh, audio, video, webcam, file — và deploy lên Hugging Face Spaces chỉ bằng một cú push.


🎯 Mục tiêu bài học​

  • Hiểu khi nào dùng Gradio thay vì Streamlit.
  • Tạo app bằng gr.Interface chỉ với 1 hàm Python.
  • Dùng gr.Blocks để bố cục tùy biến.
  • Xây chatbot với gr.ChatInterface.
  • Xử lý ảnh, audio, file.
  • Deploy lên Hugging Face Spaces.
  • Chia sẻ app tạm thời bằng share=True.

🆚 Gradio vs Streamlit​

Tiêu chíGradioStreamlit
Mục đích chínhDemo model AIDashboard dữ liệu
Điểm mạnhẢnh/audio/video/webcam, chatbot, Hugging FaceBảng, metric, chart, layout
Số dòng code cho demo~5~15
Chạy cục bộdemo.launch()streamlit run app.py
Tốc độ dựng UINhanh hơn cho AINhanh hơn cho bảng biểu
Deploy phổ biến nhấtHugging Face SpacesStreamlit Cloud

💡 Chọn nhanh: Kết quả là con số/bảng/biểu đồ → Streamlit. Kết quả là ảnh, âm thanh, văn bản sinh ra từ model → Gradio.


🧰 Cài đặt​

python -m venv .venv
.venv\Scripts\activate

pip install gradio
python app.py

Gradio tự mở http://127.0.0.1:7860.


🚀 Bước 1: gr.Interface — app nhỏ nhất​

# app.py
import gradio as gr


def chao(ten, muc_do):
return f"Xin chào {ten}! Bạn đang ở mức {muc_do} của chuỗi Python 365 ngày. 🐍"


demo = gr.Interface(
fn=chao,
inputs=[
gr.Textbox(label="Tên của bạn", placeholder="VD: Minh"),
gr.Slider(1, 365, value=49, step=1, label="Ngày thứ"),
],
outputs=gr.Textbox(label="Kết quả"),
title="🐍 Demo Python 365 ngày",
description="Nhập tên và số ngày để nhận lời chào.",
examples=[["Minh", 49], ["Lan", 100]],
)

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

Cấu trúc gr.Interface chỉ có 4 phần:

Thành phầnVai trò
fnHàm Python xử lý logic
inputsDanh sách component đầu vào
outputsDanh sách component đầu ra
examplesMẫu thử — Gradio tự sinh nút bấm

Gradio tự suy ra kiểu dữ liệu và tự tạo giao diện. Bạn không viết HTML, không viết route.


🎨 Bước 2: Bảng component thường dùng​

ComponentDùng choVí dụ
gr.TextboxVăn bảngr.Textbox(lines=5)
gr.NumberSốgr.Number(value=0)
gr.SliderKhoảng giá trịgr.Slider(0, 100)
gr.DropdownChọn một/nhiềugr.Dropdown(["A","B"], multiselect=True)
gr.RadioChọn mộtgr.Radio(["MUA","BAN"])
gr.CheckboxĐúng/saigr.Checkbox(label="Bật")
gr.ImageẢnh / webcamgr.Image(type="numpy")
gr.AudioÂm thanh / microgr.Audio(type="filepath")
gr.VideoVideogr.Video()
gr.FileTệp bất kỳgr.File(file_count="multiple")
gr.DataframeBảng dữ liệugr.Dataframe(headers=[...])
gr.PlotBiểu đồgr.Plot()
gr.ChatbotHội thoạigr.Chatbot(type="messages")

🔢 Bước 3: Ví dụ thực tế — máy tính chỉ báo kỹ thuật​

import gradio as gr
import pandas as pd
import numpy as np


def tinh_chi_bao(gia_str: str, chu_ky_sma: int) -> tuple[pd.DataFrame, str]:
"""Tính SMA và RSI từ chuỗi giá người dùng nhập."""
try:
gia = [float(x.strip()) for x in gia_str.replace(",", " ").split() if x.strip()]
except ValueError:
return pd.DataFrame(), "❌ Dữ liệu không hợp lệ. Nhập các số cách nhau bởi dấu cách."

if len(gia) < chu_ky_sma:
return pd.DataFrame(), f"❌ Cần ít nhất {chu_ky_sma} giá trị, bạn nhập {len(gia)}."

df = pd.DataFrame({"gia": gia})
df["sma"] = df["gia"].rolling(chu_ky_sma).mean()

delta = df["gia"].diff()
tang = delta.clip(lower=0).rolling(14).mean()
giam = (-delta.clip(upper=0)).rolling(14).mean()
rs = tang / giam.replace(0, np.nan)
df["rsi"] = 100 - (100 / (1 + rs))

nhan_xet = []
if df["rsi"].iloc[-1] > 70:
nhan_xet.append("⚠️ RSI > 70 — vùng quá mua")
elif df["rsi"].iloc[-1] < 30:
nhan_xet.append("💡 RSI < 30 — vùng quá bán")
else:
nhan_xet.append("➖ RSI trong vùng trung tính")

return df.round(2), "\n".join(nhan_xet)


demo = gr.Interface(
fn=tinh_chi_bao,
inputs=[
gr.Textbox(label="Chuỗi giá (cách nhau bởi dấu cách)",
placeholder="100 102 101 105 108 107 110",
lines=3),
gr.Slider(2, 50, value=5, step=1, label="Chu kỳ SMA"),
],
outputs=[
gr.Dataframe(label="Bảng kết quả"),
gr.Textbox(label="Nhận xét"),
],
title="📊 Máy tính chỉ báo kỹ thuật",
description="Nhập chuỗi giá để tính SMA và RSI.",
)
demo.launch()

Điểm quan trọng: hàm fn trả về tuple khớp thứ tự với danh sách outputs.


🧱 Bước 4: gr.Blocks — bố cục tự do​

gr.Interface bị giới hạn theo khuôn mẫu. Khi cần nhiều tab, nhiều nút, nhiều luồng xử lý → dùng gr.Blocks.

import gradio as gr


def phan_tich(symbol, so_ngay):
return f"Đang phân tích {symbol} trong {so_ngay} ngày", pd.DataFrame()


with gr.Blocks(title="Phân tích Crypto", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 📈 Công cụ phân tích Crypto")
gr.Markdown("Chọn cặp và số ngày, bấm **Phân tích**.")

with gr.Row():
with gr.Column(scale=1):
symbol = gr.Dropdown(
["BTCUSDT", "ETHUSDT", "SOLUSDT"], value="BTCUSDT", label="Cặp giao dịch"
)
so_ngay = gr.Slider(7, 365, value=90, step=7, label="Số ngày")
nut = gr.Button("🔍 Phân tích", variant="primary")
nut_xoa = gr.Button("🗑 Xóa")

with gr.Column(scale=3):
with gr.Tabs():
with gr.Tab("Kết quả"):
ket_qua = gr.Textbox(label="Trạng thái", lines=2)
bang = gr.Dataframe(label="Dữ liệu")
with gr.Tab("Hướng dẫn"):
gr.Markdown("""
1. Chọn cặp giao dịch
2. Kéo thanh chọn số ngày
3. Bấm **Phân tích**
""")

# Gắn sự kiện — đây là điểm khác biệt cốt lõi so với gr.Interface
nut.click(phan_tich, inputs=[symbol, so_ngay], outputs=[ket_qua, bang])
nut_xoa.click(lambda: ("", None), outputs=[ket_qua, bang])

demo.launch()
Trong gr.InterfaceTrong gr.Blocks
Không khai báo sự kiện.click(), .change(), .submit()
Một luồng duy nhấtNhiều sự kiện, nhiều nút
Không bố cục tùy biếnRow, Column, Tabs, Accordion

💬 Bước 5: Chatbot với gr.ChatInterface​

import gradio as gr


def tra_loi(message, history):
"""Chatbot rule-based đơn giản (thay bằng LLM API nếu muốn)."""
cau_hoi = message.lower()

if "python" in cau_hoi:
return "Python là ngôn ngữ lập trình bậc cao, dùng nhiều trong AI và phân tích dữ liệu."
if "sma" in cau_hoi or "trung bình" in cau_hoi:
return "SMA là đường trung bình động đơn giản — trung bình cộng của N giá gần nhất."
if "cài" in cau_hoi or "install" in cau_hoi:
return "Cài thư viện: `pip install ten-thu-vien`"
return f"Tôi chưa có câu trả lời cho: “{message}”. Bạn hỏi về Python, SMA hoặc cách cài thư viện nhé!"


gr.ChatInterface(
fn=tra_loi,
title="🐍 Trợ lý Python 365 ngày",
description="Hỏi về Python, chỉ báo kỹ thuật hoặc cách cài thư viện.",
examples=["Python là gì?", "SMA là gì?", "Cách cài thư viện?"],
type="messages",
).launch()

💡 Mở rộng lên LLM thật: chỉ cần thay thân hàm tra_loi bằng lời gọi API (OpenAI, Gemini, mô hình local). Toàn bộ giao diện giữ nguyên.


🖼️ Bước 6: Xử lý ảnh​

import gradio as gr
from PIL import Image, ImageFilter


def lam_mo_anh(anh: Image.Image, ban_kinh: float):
if anh is None:
return None, "Chưa có ảnh"
ket_qua = anh.filter(ImageFilter.GaussianBlur(radius=ban_kinh))
return ket_qua, f"Đã làm mờ với bán kính {ban_kinh}"


demo = gr.Interface(
fn=lam_mo_anh,
inputs=[
gr.Image(type="pil", label="Ảnh đầu vào", sources=["upload", "webcam"]),
gr.Slider(0, 20, value=5, step=0.5, label="Bán kính làm mờ"),
],
outputs=[gr.Image(label="Ảnh kết quả"), gr.Textbox(label="Trạng thái")],
live=True, # tự cập nhật khi kéo slider — không cần bấm nút
title="🖼️ Làm mờ ảnh",
)

demo.launch()

live=True là điểm rất mạnh của Gradio: kéo slider là ảnh cập nhật ngay.


🌐 Bước 7: Chia sẻ và deploy​

Chia sẻ tạm thời (72 giờ):

demo.launch(share=True)
# → https://xxxxx.gradio.live

Deploy lên Hugging Face Spaces (miễn phí, vĩnh viễn):

  1. Tạo tài khoản huggingface.co.
  2. New Space → chọn SDK Gradio → Public.
  3. Upload app.py + requirements.txt.
  4. Space tự build và chạy. URL: https://huggingface.co/spaces/<user>/<ten>.

requirements.txt:

gradio==5.*
pandas
plotly

Checklist:

  • demo.launch() chỉ ở trong if __name__ == "__main__" hoặc gọi trực tiếp ở cuối file
  • Không hard-code API key — dùng Hugging Face Secrets
  • Đã liệt kê đủ thư viện trong requirements.txt
  • Xử lý lỗi trong fn để app không crash

🧪 Bài tập thực hành​

  1. Viết app tính MACD từ chuỗi giá người dùng nhập.
  2. Thêm gr.Plot để vẽ biểu đồ giá và SMA bằng Plotly.
  3. Tạo app cho phép tải file CSV lên và trả về thống kê mô tả.
  4. Xây gr.ChatInterface trả lời về 5 khái niệm Python bạn đã học.
  5. Dùng gr.Blocks tạo app 2 tab: tab 1 phân tích, tab 2 hướng dẫn.
  6. Deploy một app lên Hugging Face Spaces và lấy link.

⚠️ Lỗi thường gặp​

LỗiNguyên nhânCách sửa
Number of outputs must matchSố phần tử trả về ≠ số outputsTrả về tuple đúng số lượng
AttributeError: 'str' object has no attribute ...Sai type= của componentgr.Image(type="pil") trả PIL, type="numpy" trả array
App chạy chậmXử lý nặng trong fnThêm @lru_cache hoặc xử lý vector hóa
share=True không hoạt độngMạng chặnKiểm tra firewall; dùng GRADIO_SERVER_NAME
Port 7860 bị chiếmApp khác đang chạydemo.launch(server_port=7861)
Space build lỗiThiếu thư việnCập nhật requirements.txt

📝 Ghi chú​

  • Gradio tự sinh API — mọi app đều có endpoint REST; xem tab "Use via API" ở chân trang.
  • queue() giúp xử lý request nặng tuần tự, tránh quá tải.
  • Không dùng Gradio cho dashboard bảng biểu phức tạp — Streamlit phù hợp hơn.
  • Gradio là lựa chọn số 1 khi công khai model lên Hugging Face.
  • Với LLM, gr.ChatInterface + type="messages" là chuẩn hiện tại.

🎯 Tóm tắt ngày 49​

✅ Hiểu khi nào chọn Gradio thay vì Streamlit ✅ Tạo app bằng gr.Interface chỉ với 4 thành phần ✅ Bố cục tự do và gắn sự kiện bằng gr.Blocks ✅ Xây chatbot với gr.ChatInterface ✅ Xử lý ảnh/audio/video/file ✅ Deploy lên Hugging Face Spaces và chia sẻ link tạm


📚 Học tiếp​