2024年11月23日 星期六

Python 學習筆記 : 簡單好用的 Web app 套件 gradio (一)

Gradio 是一個低代碼的 Python web app 套件, 只要簡單幾行程式碼就能完成一個網頁應用程式介面, 可以用來快速展示應用程式功能, 是教學與 web app 雛型開發的好工具, 比起用 Flask 手刻一個網站要簡單快速很多, 參考 Gradio 官網 :       


Gradio 最早是由美國史丹福大學的 Abubakar Abid 等人於 2019 年開發, 目的是可以用簡單的方式將機器學習模型以互動的網頁應用呈現, 以利使用者進行測試和反饋. 2021 年 Gradio 被 AI 模型網站 Hugging Face 收購與持續開發維運, 目前最新版為 v5.6, 教學文件參考 :


參考書籍 :

OpenAI API 基礎必修課 (蔡文龍, 碁峰, 2024) 第 4 章
Python 實戰聖經 (鄧文淵工作室, 碁峰, 2021) 第 14 章
成為 Python AI 深度學習達人的第一門課 (蔡炎龍, 全華, 2022) 第 1 篇


一. 安裝 gradio 套件 : 

Gradio 為 Python 第三方套件, 可用 pip 指令安裝 :

pip install gradio 

如果是在 Colab 上安裝, pip 前面要加驚嘆號 :

!pip install gradio 

安裝後匯入 gradio, 習慣上會取簡名 gr :

>>> import gradio as gr
>>> gr.__version__   
'3.44.3'

可在 pip install 指令後面加 -U 參數把已安裝的 gradio 更新到最新版 :

pip install gradio -U

如果使用 Thonny 編輯器, 可點選 "工具/套件管理" 搜尋 gradio 後安裝或更新 : 




注意, 如果是更新, 完成後必須關掉 Thonny 後重開才會抓到最新版套件 :

>>> import gradio as gr   
>>> gr.__version__   
'5.6.0'

可見已經升到最新版了. 


二. 在本機建立 gradio 網頁應用程式 : 

首先用來檢視 gradio 套件的內容 : 

>>> import gradio as gr 
>>> dir(gr)  
['Accordion', 'AnnotatedImage', 'Annotatedimage', 'Audio', 'BarPlot', 'Blocks', 'BrowserState', 'Brush', 'Button', 'CSVLogger', 'ChatInterface', 'ChatMessage', 'Chatbot', 'Checkbox', 'CheckboxGroup', 'Checkboxgroup', 'ClearButton', 'Code', 'ColorPicker', 'Column', 'DataFrame', 'Dataframe', 'Dataset', 'DateTime', 'DeletedFileData', 'DownloadButton', 'DownloadData', 'Dropdown', 'DuplicateButton', 'Eraser', 'Error', 'EventData', 'Examples', 'File', 'FileData', 'FileExplorer', 'FileSize', 'Files', 'FlaggingCallback', 'Gallery', 'Group', 'HTML', 'Highlight', 'HighlightedText', 'Highlightedtext', 'IS_WASM', 'Image', 'ImageEditor', 'ImageMask', 'Info', 'Interface', 'JSON', 'Json', 'KeyUpData', 'Label', 'LikeData', 'LinePlot', 'List', 'LoginButton', 'Markdown', 'Matrix', 'MessageDict', 'Mic', 'Microphone', 'Model3D', 'MultimodalTextbox', 'NO_RELOAD', 'Number', 'Numpy', 'OAuthProfile', 'OAuthToken', 'Paint', 'ParamViewer', 'PlayableVideo', 'Plot', 'Progress', 'Radio', 'Request', 'RetryData', 'Row', 'ScatterPlot', 'SelectData', 'SimpleCSVLogger', 'Sketchpad', 'Slider', 'State', 'Tab', 'TabItem', 'TabbedInterface', 'Tabs', 'Text', 'TextArea', 'Textbox', 'Theme', 'Timer', 'UndoData', 'UploadButton', 'Video', 'Warning', 'WaveformOptions', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_simple_templates', 'analytics', 'blocks', 'blocks_events', 'chat_interface', 'cli', 'close_all', 'component', 'component_meta', 'components', 'context', 'data_classes', 'deploy', 'events', 'exceptions', 'external', 'external_utils', 'flagging', 'get_package_version', 'gradio', 'helpers', 'image_utils', 'interface', 'ipython_ext', 'json', 'layouts', 'load', 'load_ipython_extension', 'mount_gradio_app', 'networking', 'node_server', 'oauth', 'on', 'pipelines', 'pipelines_utils', 'processing_utils', 'queueing', 'ranged_response', 'render', 'renderable', 'route_utils', 'routes', 'server_messages', 'set_static_paths', 'skip', 'state_holder', 'strings', 'templates', 'themes', 'tunneling', 'update', 'utils', 'wasm_utils']

可見 gradio 套件提供了非常多的類別, 函式, 特別是豐富的輸出入控件類別, 例如 Textbox, Text, TextArea, Number, Radio, Checkbox, Label 與 Slider 等, 參考 :


但這些類別中最重要的是 Interface 類別, 利用它的建構子 Interface() 函式可以建立一個網頁介面物件 Interface, 呼叫此物件的 launch() 方法會自動建構一個含有輸入與輸出控件的網頁, 讓使用者與應用程式互動, 基本語法如下 : 

interface=gr.Interface(fn=函式名稱, inputs=輸入控件, outputs=輸出控件)   

其中 fn 參數用來指定按下自動建立的網頁中的 Submit 按鈕後要呼叫的函式, inputs 參數指定可讓使用者輸入資訊的控件類型名稱, 而 outputs 參數則指定呼叫 fn 函式後用來顯示其回傳值的控件類型. 除了這三個基本參數外, 還有如下參數 :
  • title : 設定應用程式標題
  • description : 設定位於標題下方的說明文字 (可用純文字/HTML/Markdown)
  • article : 設定位於輸出入區下方的註解文字 (可用純文字/HTML/Markdown)
  • examples : 設定位於 App 最下方的輸入控件範例 (二維串列) 
  • allow_flagging : 設為 'never' 不顯示 Flag 按鈕
  • live : 設為 True 時輸入資料馬上進行運算與輸出, 不須等到按 Submit 鈕才輸出
呼叫 Interface 物件的 launch() 方法即可發布此網頁應用程式 :

interface.launch()   

這樣就會建立一個有輸出入控件與 Submit 按鈕的網頁應用程式介面了. 

建立一個 Gradio 網頁應用程式首先要定義 app 函式, 此為運算邏輯的核心, Interface() 中 inputs 所指定的輸入控件內容會傳遞給此函式, 而運算結果則會傳回給 outputs 所指定之輸出控件, 基本的 web app 程式結構如下 :

import gradio as gr     

def app(參數):     # 傳入參數為輸出入控件串列 (會被當成控件的 label)
    ... (運算) ...
    return 結果     # 給輸出控件

interface=gr.Interface(fn=app, inputs=輸入控件, outputs=輸出控件)    
interface.launch()   

例如下面的打招呼 app, 首先定義 fn 參數要綁定的函式 hello() :

>>> def hello(name):      
    return f'Hello {name}!'    

此函式的輸入參數 name 就是使用者在輸入控件中所輸入的內容. 接著呼叫 Interface() 建構式來建立網頁介面物件 :  

>>> interface=gr.Interface(fn=hello, inputs='textbox', outputs='textbox')  

此處 fn 參數綁定了上面定義的函式 hello(), 輸入控件與輸出控件指定為 'textbox' 表示皆為文字輸入欄位, 這是 Gradio 控件的簡寫用法, 也可以用 'text', 它們都是


用 type() 檢視建構子的傳回值是一個 Interface 物件 : 

>>> type(interface)   
<class 'gradio.interface.Interface'>   

此 Interface 物件用上面的 members 模組檢視會出現錯誤  (原因待查), 只好用 dir() 檢視 : 

>>> dir(interface)    
['FRONTEND_DIR', 'GRADIO_CACHE', 'TEMPLATE_DIR', '__annotations__', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_id', '_is_running_in_reload_thread', '_queue', 'add', 'add_child', 'allow_duplication', 'allow_flagging', 'allowed_paths', 'analytics_enabled', 'api_mode', 'api_name', 'api_open', 'app', 'app_id', 'article', 'attach_clear_events', 'attach_flagging_events', 'attach_load_events', 'attach_submit_events', 'auth', 'batch', 'block_thread', 'blocked_paths', 'blocks', 'cache_examples', 'call_function', 'children', 'clear', 'close', 'component_class_id', 'concurrency_limit', 'config', 'constructor_args', 'create_limiter', 'css', 'dependencies', 'description', 'deserialize_data', 'dev_mode', 'elem_classes', 'elem_id', 'enable_queue', 'encrypt', 'events', 'examples', 'examples_per_page', 'exited', 'expects_oauth', 'favicon_path', 'fill_expected_parents', 'flagging_callback', 'flagging_dir', 'flagging_options', 'fn', 'fn_durations', 'fns', 'from_config', 'from_pipeline', 'get_api_info', 'get_block_name', 'get_component', 'get_component_class_id', 'get_config', 'get_config_file', 'get_expected_parent', 'get_instances', 'handle_streaming_outputs', 'head', 'height', 'input_components', 'instances', 'integrate', 'interface_type', 'is_callable', 'is_rendered', 'is_running', 'js', 'launch', 'limiter', 'live', 'load', 'local_url', 'max_batch_size', 'max_threads', 'mode', 'move_resource_to_block_cache', 'output_components', 'parent', 'pending_streams', 'postprocess', 'postprocess_data', 'predict', 'preprocess_data', 'process_api', 'progress_tracking', 'proxy_url', 'proxy_urls', 'queue', 'queue_enabled_for_fn', 'recover_kwargs', 'render', 'render_article', 'render_examples', 'render_flag_btns', 'render_input_column', 'render_output_column', 'render_title_description', 'root_path', 'serialize_data', 'set_event_trigger', 'share', 'share_token', 'share_url', 'show_api', 'show_error', 'simple_description', 'simple_server', 'skip_api', 'space_id', 'ssl_verify', 'startup_events', 'state_session_capacity', 'stylesheets', 'temp_file_sets', 'temp_files', 'theme', 'theme_css', 'thumbnail', 'title', 'unrender', 'validate_inputs', 'validate_outputs', 'validate_queue_settings', 'visible', 'width']

可見 Interface 物件的屬性方法非常多, 但最常用到的是 queue() 與 launch() 方法 : 
  • queue(max_size=100, concurrency_count=3) :
    用來啟動請求佇列 (先進先出), 所有請求會先放入佇列中排隊依序執行以維持系統效能, 避免伺服器超載, Gradio 會向請求者顯示處理進度條. Gradio 預設可處理 3 個同時執行的請求, 其餘的請求會加入佇列中排隊等待執行, 但可傳入 concurrency_count 參數來設定可同時處理的請求數量, 也可傳入 max_size 參數來設定最大佇列長度. 
  • launch(share=False, inbrowser=False, debug=False) :
    用來佈署並啟動一個本地的 HTTP 網頁伺服器, 讓使用者能透過瀏覽器使用 Gradio 互動式應用程式 (例如測是機器學習模型或與大語言模型互動). 如果傳入 share=True 參數, Gradio 會生成一個有效期限為 72 小時的臨時公開 HTTPS 網址以便讓公眾網路使用者訪問 Gradio 應用程式 (此服務由 Gradio 的母公司 Hugging Face 提供). 傳入 inbrowser=True 會在啟動伺服器的同時自動開啟瀏覽器拜訪網站; 傳入 debug=True 會在發生錯誤時顯示錯誤訊息. Gradio 使用 FastAPI 框架與 Uvicorn 非同步伺服器來建構這個本地的 Web 伺服器, 並利用 WebSocket 與 Vue.js 等技術來提供前後端互動功能. 
這兩個方法的一般的用法是先呼叫 Interface 物件的 queue() 啟動請求的佇列功能, 然後再呼叫 launch() 佈署網站 :

interface.queue() 
interface.launch() 

也可以用鏈式呼叫一氣呵成 :

interface.queue().launch() 

在生產環境時必須呼叫 queue() 啟動佇列功能以保證伺服器的穩定性, 避免高負載時因為資源耗盡導致系統崩潰. 

但如果是實驗室的輕量測試則毋須啟動佇列功能, 可直接呼叫 launch() 方法佈署網站 : 

>>> interface.launch()    
Running on local URL:  http://127.0.0.1:7860  

To create a public link, set `share=True` in `launch()`.

這時開啟瀏覽器拜訪 http://127.0.0.1:7860 網址即可看到 Gradio 建立的網頁 : 




可見預設會將 Submit 按鈕處理函式 hello 的傳入變數名稱 name 作為輸入框的欄位標題, 在輸入框輸入命字按下按鈕後, 欄位內容會被傳送給 hello() 輸出在 outputs 控件的輸出框裡. 


三. 利用 share=True 參數取得臨時公眾網址 :    

如果在呼叫 launch() 時傳入 share=True 的話, 除了本地伺服器固定的 http://127.0.0.1:7860 網址外, 還會獲得 Huggin Face 提供的 72 小時公眾 HTTPS 網址 : 

>>> interface.launch(share=True)     
* Running on local URL:  http://127.0.0.1:7860
* Running on public URL: https://ac8b278a1e74320ccf.gradio.live   

This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)

將此 HTTPS 公眾網址貼到瀏覽器網址列同樣可拜訪此 Web app :




而這整個功能卻只用了下面 5 行程式碼 :

import gradio as gr

def hello(name):      
    return f'Hello {name}!'

interface=gr.Interface(fn=hello, inputs='text', outputs='text')
interface.launch(share=True)

這就是 Gradio 簡單迷人之處, 你完全不用懂 HTML, CSS, Javascript 與 Flask 就輕輕鬆鬆完成了一個網頁應用程式! 讓資料科學, 機器學習, 以及語言模型的測試開發者節省了很多技術負擔. 


四. 在 Colab 建立 gradio 網頁應用程式 :      

如果是在 Colab 上執行更方便, 本地的網站會直接顯示在輸出儲存格裡 :

先用 !pip install gradio 安裝套件 : 




輸入程式碼發布網站 : 




按其中的公眾網址會開啟分頁顯示 Web app :





五. 使用 Gradio Playground 建立網頁應用程式並佈署至 Hugging Face 空間 :   

Gradio 官網提供 Playground 讓開發者直接在其官網上撰寫 Web app, 毋須在本機或 Colab 上安裝 gradio, 網址如下 : 





預設中間已經有一個與上面範例一樣的 hello 應用程式了, 右邊是執行結果頁面, 按右上角的 "Deploy to .. Space" 鈕可將此網頁應用程式佈署至 Hugginh Face 提供的免費 Web app 執行空間 (也可付費購買較多空間與主機性能), 首先會要求登入 Hugging Face :




然後填寫 space name, description, 勾選授權方式等, 其餘都用預設值即可 : 




按 "Create Space" 鈕就會將此 Web app 佈署至 Hugging Face 空間了, 網址如下 :


等佈署程序跑完出現下列 started 頁面時, 點一下上面的灰色區域即可 : 





這個跟在本地呼叫 launch(share=True) 所獲得的 72 小時臨時網址不同, 發佈到 Hugging Face 空間時獲得的網址是永久的. 

沒有留言 :