2024年12月21日 星期六

註冊 Streamlit Community Cloud 帳號

學完 Gradio 之後, 接下來想繼續 Streamlit 的測試學習. 與 Gradio 一樣, 除了下載安裝 streamlit 套件在本機建立並執行 Streamlit web app 外, Streamlit 也提供了 Streamlit Playground 平台讓使用者直接在上面撰寫與執行 Web app, 網址如下 : 





可以直接在左側輸入框撰寫程式, 結果會顯示在右側, 毋須安裝任何程式套件. 但 Playground 只能作為 Web app 的快速測試之用, 如果要將程式專案保存起來, 則要註冊 Streamlit Community Cloud 帳戶, 事實上 Streamlit Playground 只是 Streamlit Community Cloud 的一部分. 

按 Streamlit Playground 右上方的 Signup 鈕即可註冊 :




也可以從 Streamlit Community Cloud 首頁註冊 : 

按 Continue to sign in 鈕 : 




可以用 Google 或 GitHub 帳號註冊, 但我習慣用 Email 註冊, 填寫姓名與 Email 後按 Send Email code 鈕 : 




這時 Streamlit 會寄一封信到信箱, 裡面有 6 位數認證碼 :



 
將其填入網頁中進行認證 : 




接著填寫問券按 Continue 即完成註冊 :




完成後就會進入專案儀錶板網頁 (Dashboard), 按右上角的 Create App 即可建立網頁應用程式專案 : 




Streamlit 網頁應用程式範例參考 :


Streamlit Community Cloud 快速上手指南參考 :

Get started with Streamlit Community Cloud

免費帳戶的限制如下 : 
  • 專案檔案容量 1GB
  • 可佈署一個私人應用程式
  • 公開的應用程式數量不限
Streamlit Community Cloud 支援 GitHub 連接, 可自動部署儲存在 GitHub 上的應用程式, 換句話說設定好連接 GitHub 後, 將檔案上傳至 GitHub 的 public 程式庫就會自動將應用程式佈署至 Streamlit Community Cloud (連結 Private 函式庫須額外設定). 關於註冊 GitHub 帳戶參考 :


連接 GitHub 的設定方法參考 :


首先按專案 Dashboard 頁面左上角的 Workspace, 點選 Settings 後按 Sign in with GitHub 鈕 :




輸入 GitHub 帳號密碼按 Sign in :




按右下角的 Authorize GitHub 鈕 : 




這樣就完成串接 GitHub 的設定了 :



2024年12月20日 星期五

明儀買書一本 : 抓出飆股穩穩賺

今天下班去河堤還書取書後到全聯買東西, 然後順便去隔壁明儀找下面這本書 :


我覺得內容很不錯, 但此書僅 8 折, 所以等下次 momo 特價再買. 在理財類陳列架上看到下面這本剛上市不久的股票新書, 翻閱一下覺得還不錯就買了 :





VIP 現金 75 折 338 元. 

露天購買超慢跑磁石指壓板x4

前陣子在 momo 買了兩片超慢跑磁石指壓板, 以前追劇時都坐著不動, 現在都改為邊踩指壓板超慢跑邊看電視至少半小時. 我想在鄉下與辦公室也各放一張, 休息時可以跑一下, 還有菁菁也可以在工作室放一個, 另外小舅媽大病初癒, 想送她一張保養健身, 今天上露天買了 4 張 :





小七取貨免運共 700 元. 

Gradio 學習筆記索引

第一次得知 Gradio 套件是在 2022 疫情居家上班期間的內訓課程中, 它的簡單易學用法讓不懂 HTML 與 Javascript 的 Python 開發者可以快速建置一個 Web app, 非常適合原型測試與教學展示. 不過我是從上個月底因為借到蔡文龍老師寫的 "OpenAI API 基礎必修課" 這本書才開始認真測試 Gradio, 因為此書是目前對 Gradio 有較多描述的一本. 

近一個月的測試結果紀錄在下面七篇文章中, 為了以後複習查找方便將其從 Python 筆記中獨立出來編成索引如下 : 


Gradio 已被 Hugging Face 收購與維護, 版本持續演進, 未來有新的元件或功能再補充. 

2024年12月19日 星期四

六十大壽

晚上查看冬至日期時發現今天是農曆 11/19, 再翻一下檔案集裡的命書, 赫然發現今天是我農曆生日! 挖咧, 突然覺得 60 大壽真恐怖, 明天要來去吃五鮮級壓壓驚. 雖然菁菁說我外表看來 40 多歲, 而且我內心一直停留在 28 歲 (因為那時心碎了), 但是看到這數字還是會膽戰心驚. 以前的我雄心壯志, 但走過 60 個年頭也漸漸明白人生是怎麼回事了, 年輕時追求輝煌騰達大富大貴就像做了一場美夢, 行至此處已然知曉命裡有時終須有. 回首前塵, 其實老天待我甚厚, 我擁有的其實並不少, 反而是奉獻的甚微, 慚愧.  

2024年12月18日 星期三

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

經過前面六篇測試, Gradio 系列的測試學習也進入尾聲, 最後一篇  Gradio web app 要來實作隨機貓狗圖片的顯示器, 這是我從茶米老師的課堂上學來的, 包含金門大學, 某高中 Python API 教學直播, 以及公司內訓我總共聽了三遍, 但沒有一次有跟得上實作 (因為我邊聽邊做會分心). 在摸熟 Gradio 用法後, 今天就來動手真正實作一遍唄. 參考 :

# 貓與狗的隨機圖


由於 Gradio PlaygroundHugging Face Space 均無法執行 requests 或 urllib 等爬蟲套件 (猜測是資安或業務考量, 付費帳戶也許就可以執行), 因此本測試均於本機執行 (也可以在 Colab 執行). 


十. 貓狗隨機圖片 : 

下面這兩個超棒的網站提供隨機的貓狗圖片 :


更棒的是還提供 API 讓我們可以用 Python 爬蟲程式輕易取得圖片, 其 API 網址如下 :


呼叫 requests.get() 並傳入 API 網址會傳回 JSON 字串回應, 呼叫字串的 json() 方法轉成字典或字典串列 data 後即可透過索引與屬性取得圖片的 URL :

貓咪圖片的網址 : data[0]['url']
狗狗圖片的網址 : data['message']

這些都可以利用 RestMan/PostMan 或瀏覽器開發者工具等分析 HTTP/HTTPS 的請求與回應訊息得到, 具體方法參考 :



1. 取得隨機貓咪圖片 :

首先用 Gradio 寫一個取得隨機貓咪圖片的 Web app, 程式碼如下 :

import gradio as gr
import requests

def handler():
    try:    
        url='https://api.thecatapi.com/v1/images/search'
        r=requests.get(url)   # 傳回 json 字串
        data=r.json()         # 轉成 Python 物件 (字典串列)
        return data[0]['url'] # 傳回圖片網址
    except Exception as e:  # 出現例外
        return None  # 傳回 None 不顯示圖片

out1=gr.Image(type='filepath',
              label='可愛的貓咪圖片',
              width=640,
              height=480)
iface=gr.Interface(
    fn=handler,          
    inputs=[],            
    outputs=[out1], 
    title='隨機貓咪圖',
    flagging_mode='never'
    )

iface.launch()

此例沒有輸入只有一個 Image 輸出元件, 當按下 Generate 按鈕時呼叫 handler() 函式利用 requests.get() 透過 API 網址取得圖片, 結果如下 :




也可以用 Python 內建的 urllib 模組來爬, 這樣就毋須安裝 requests, 改寫如下 :

import gradio as gr
import urllib.request
import json

def handler():
    try:
        url='https://api.thecatapi.com/v1/images/search'
        r=urllib.request.urlopen(url)
        data=json.load(r)  
        return data[0]['url']  
    except Exception as e:
        return None  

out1=gr.Image(type='filepath',
              label='可愛的貓咪圖片',
              width=640,
              height=480)

iface=gr.Interface(
    fn=handler,          
    inputs=[],            
    outputs=[out1],       
    title='隨機貓咪圖',
    flagging_mode='never'
    )

iface.launch()


2. 取得隨機狗狗圖片 :   

上面的隨機貓咪圖程式稍微修改一下即可取得狗狗圖片 : 

import gradio as gr
import requests

def handler():
    try:    
        url='https://dog.ceo/api/breeds/image/random'
        r=requests.get(url)     # 傳回 json 字串
        data=r.json()           # 轉成 Python 物件 (字典串列)
        return data['message']  # 傳回圖片網址
    except Exception as e:      # 出現例外
        return None  # 傳回 None 不顯示圖片

out1=gr.Image(type='filepath',
              label='可愛的狗狗圖片',
              width=640,
              height=480)
iface=gr.Interface(
    fn=handler,          
    inputs=[],            
    outputs=[out1], 
    title='隨機狗狗圖',
    flagging_mode='never'
    )

iface.launch()

結果如下 :




3. 選擇要取得隨機貓咪或狗狗圖片 :   

可以用一個 Radio 或 Dropdown 元件來選擇要取得貓咪還是狗狗圖片, 程式碼如下 : 

import gradio as gr
import requests

def handler(sel):
    try:
        if sel=='貓咪': 
            url='https://api.thecatapi.com/v1/images/search'
            r=requests.get(url)     # 傳回 json 字串
            data=r.json()           # 轉成 Python 物件 (字典串列) 
            return data[0]['url']   # 傳回圖片網址
        else:
            url='https://dog.ceo/api/breeds/image/random'
            r=requests.get(url)     # 傳回 json 字串
            data=r.json()           # 轉成 Python 物件 (字典串列)
            return data['message']  # 傳回圖片網址
    except Exception as e:
        return None  

in1=gr.Radio(label='請選擇貓咪或狗狗',
             choices=['貓咪', '狗狗'],
             value='貓咪')
out1=gr.Image(type='filepath',
              label='可愛的貓咪圖片',
              width=640,
              height=480)

iface=gr.Interface(
    fn=handler,          
    inputs=[in1],            
    outputs=[out1],       
    title='隨機貓咪或狗狗圖片',
    flagging_mode='never'
    )

iface.launch()

此使用一個 Radio 輸入物件讓使用者選擇要取得貓咪或狗狗圖片 (預設是貓咪), 其值會在按下 Submit 鈕時傳給 fn 所指定之處理函式 handler() 之參數 sel, 只要判斷 sel 是 '貓咪' 還是 '狗狗' 分別去不同 API 網址爬得隨機圖片之 url 即可, 結果如下 :





4. 在 Colab 佈署貓狗隨機圖片 App :  

上面的程式也可以在 Google 的 Colab 實作, 首先要安裝 gradio 套件 :

!pip install gradio     

然後將上一個範例程式貼到 Colab 儲存格中執行即可, 結果如下 :





可見 Colab 真是不錯的線上程式測試平台, requests 套件也可以執行. 

我從課堂上還學到在 Colab 上顯示圖片的方法, 先匯入 IPython.display 模組的 HTML 函式, 然後將含有 img 元素的 HTML 碼傳給 HTML() 即可 :

from IPython.display import HTML

url='https://cdn2.thecatapi.com/images/446.jpg'
img=f'<img src={url}>'
HTML(img)

結果如下 :




也可以用 Gradio 的 HTML 元件顯示圖片 : 

import gradio as gr

def handler():
    url='https://cdn2.thecatapi.com/images/446.jpg'
    return f'<img src={url}>'

iface=gr.Interface(
    fn=handler,
    inputs=[],
    outputs='html',
    title='用 HTML 顯示圖片',
    flagging_mode='never',
    live=True
    )
iface.launch()


結果如下 : 




終於完成三個月前的課堂作業啦! 同時 Gradio 的測試學習也到此告一段落, 近一個月的鑽研收穫滿滿, 準備迎向另一個工具 Streamlit 的學測啦! 

露天購買太陽能 LED 感應燈x5 (回購x2)

上週日完成廚房與浴室太陽能感應燈安裝後, 上回買的五組全都用光了, 但還有高雄陽台與客廳, 鄉下菜園入口等處需安裝, 所以再回購五組 :





全家取貨免運 815 元. 

2024年12月16日 星期一

重新啟用富邦與永豐帳號

因為測試程式交易的需要, 今天打電話給富邦與永豐證券幫我重新啟用冰封多年的帳戶, 富邦的帳戶經下載富邦 e 點通輸入個資驗證, 線上就完成更新了, 也與業務員陳先生相談甚歡 (念我的夢想科系財務金融的) 互留賴. Python API 將幫我詢問申請程序再通知辦理. 

永豐的因為當時申辦時沒開網路, 所以應該要跑一趟櫃台, 業務員是林先生. 

永豐證券高雄分公司 :
802高雄市苓雅區中正一路284號2、3樓
(07)723-2800

2024年12月15日 星期日

2024 年第 50 周記事

2024 已過去 50 周了, 剩下兩個禮拜就是跨年了, 今年感覺在學習研究上收穫頗豐, 最近也複習 Hahow 企業版課程 (主要是量化投資方面), 可見希望趁這年尾半個月再多學一點東西. 本周終於把 Gradio 測試完畢, 哎呀, 原以為很簡單三兩下就能搞定, 沒想到居然花了兩個多禮拜 (不過 Gradio 真的是簡化許多之前用 Flask/Django 建構的麻煩過程). 接下來我想將苗頭對準 Streamlit, 這個跟 Gradio 一樣簡單, 但感覺更有質感. 

週二晚上跟水某去龍華國中斜對面的達樂斯吃牛排, 開在此處很多年了我都沒進去吃過 (水某跟二哥以前常去), 而我幾乎每天都會經過. 兩人同行只要一個人點主餐 (我是沙朗 350 元), 另一個人可以只點沙拉吧 (220 元), 兩個人才花 570 元而已. 他們的羅宋湯就很多豬肉牛肉, 點沙拉吧光是羅宋湯與酥皮濃湯就非常飽了. 嗯, 達樂斯 CP 值蠻高的 (下次改點雞肉嚐嚐). 

水某最近常看 YT 上日韓 Youtuber 來台紀錄美食的影片, 剛好看到首爾一對米其林主廚夫妻來台灣享受辦桌美食吃薑母鴨, 突然想到我好像從沒吃過薑母鴨啊, 剛好菁菁周五晚上較早回家, 就去鼓山三路與銘傳路交叉口的皇茗薑母鴨吃吃看, 菁菁說這家是碳烤的且鴨肉較軟不塞牙縫, 一嚐果然好吃耶!








這個周末無人來訪, 終於有空完成浴室與廚房太陽能感應燈的安裝, 兩塊小太陽能板兩周前就已安裝在屋後的浴室外牆, 連續兩周都有人來訪, 而且這項工程走線也較耗費工夫, 所以一直沒時間完成室內拉線與感應燈安裝, 今天一直弄到傍晚才全部搞定 :







完成後剛好太陽已下山驗收成果 OK. 上回買的六組感應燈模組至此已全部用完, 打算再買 5 組, 還有高雄前陽台, 客廳, 後陽台, 以及鄉下車庫與菜園入口等處需要安裝. 

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

經過前幾篇測試我覺得 Gradio 真是一個非常好的 Web app 工具, 可以讓資料分析工程師專注於資料本身, 毋須花大把時間在前端技術上, 這種功能以前使用 Flask 可是要花不少功夫呢. 本篇繼續來測試 Gradio 的繪圖功能.


使用 Gradio 繪製統計圖是以 Pandas 的 DataFrame 做為資料來源 (value 參數), 以下測試主要是在 Gradio Playground 平台上進行, 並將 Web app 佈署到 Hugging Face Spaces 執行空間, 這兩個平台都預載了 Pandas, 可直接 import 使用. 


九. 用 Gradio 繪製統計圖 : 

目前的 Gradio 版本僅提供了折線圖, 散佈圖, 與長條圖這三種繪圖函式, 但可利用 Matplotlib, Protly, Seaborn 等繪圖函式繪圖後傳給 gradio.Plot() 含是顯示. 


 Gradio 繪圖元件 常用參數
 LinePlot (折線圖) title,x_title, y_title, x, y, value, color, interactive
 ScatterPlot (散佈圖) title,x_title, y_title, x, y, size, color, interactive
 BarPlot (長條圖) title,x_title, y_title, x, y, value, color, grouped, stacked, interactive


注意, Gradio 的所有繪圖元件均具可透過設定 interactive=True 開啟互動功能 (預設 False). 


1. 用 LinePlot() 繪製折線圖 : 

折線圖用來顯示一組或多組資料的變化與關係, LinePlot() 常用參數如下 :
  • value : 要顯示的資料, 可以是 Pandas DataFrame 或 JSON 格式. 
  • x : 指定要做為 x 軸的 DataFrame 欄索引名稱 
  • y : 指定要做為 y 軸的 DataFrame 欄索引名稱
  • color : 指定要做為分組標籤的 DataFrame 欄索引名稱
  • title : 圖表的標題 (字串)
  • x_title : x 軸標籤
  • y_title : y 軸標籤
  • interactive : 圖表是否具有互動性 True/False (預設)
資料參數 value 通常是傳入一個 Pandas 的 DataFrame 物件, 這比較簡單且直觀, 如果是用 JSON 格式的字典串列的話處理起來較麻煩. 

範例程式碼 :

import gradio as gr
import pandas as pd

data=pd.DataFrame({
    'x': ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
    'y': [23, 20, 21, 17, 15, 14, 12]
    })

plot=gr.LinePlot(
    value=data, 
    x='x', 
    y='y', 
    title='本周溫度變化', 
    x_title='星期', 
    y_title='溫度', 
    interactive=True
    )

iface=gr.Interface(
    fn=lambda: data
    inputs=[], 
    outputs=plot,
    title='折線圖 LinePlot() 測試',
    flagging_mode='never',
    )

iface.launch()

本例顯示一周溫度變化之折線圖, 因為不須用指名函式處理資料, 所以 fn 參數使用 lambda 匿名函式傳入一個 DataFrame, 它會被傳遞給 LinePlot 物件繪製圖形. 其次, 由於不需要輸入, 故 inputs參數要傳入空串列 (不要傳入 None, 雖然也不會出現錯誤). 結果如下 :




當滑鼠移動到圖表上時會顯示資料點的 X, Y 軸值, 這就是互動圖表的功能. 

此 Web App 網址如下 :


如果要繪製兩組以上的折線圖, 那就要在 data 中添加另一個 Y 軸資料, 但需要用到 DataFrame 物件的 melt() 方法將資料擴展, 同時利用 color 參數指定分組標籤, 例如下面程式是比較本周與前一周的溫度變化 :

import gradio as gr
import pandas as pd

data=pd.DataFrame({
    'x': ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
    '本周': [23, 20, 21, 17, 15, 14, 12],       # 第一組資料
    '前一周': [26, 25, 24, 27, 25, 24, 23]    # 第二組資料
    })

data_long=data.melt(id_vars='x', var_name='label', value_name='y')

plot=gr.LinePlot(
    value=data_long,   # 顯示擴展後之資料
    x='x', 
    y='y',
    color='label',   
    title='本周溫度變化', 
    x_title='星期', 
    y_title='溫度', 
    interactive=True
    )

iface=gr.Interface(
    fn=lambda: data, 
    inputs=[], 
    outputs=plot,
    title='折線圖 LinePlot() 測試',
    flagging_mode='never',
    )

iface.launch()

此例第一組資料為本周溫度, 在 data 中添加前一周溫度為第一組資料, 這時在 LinePlot() 要傳入 color 參數指定分組標籤 (這會決定 legend 圖例), 然後在呼叫 DataFrame 物件的 melt() 方法將寬格式資料展開為長格式資料時要指定 var_name 為此分組標籤 (此處為 'label'), 結果如下 :




可見指定 color 參數使兩個 Y 軸欄索引變成圖例 (legend) 了.  

此 Web App 網址如下 :



2. 用 ScatterPlot() 繪製散佈圖 : 

散佈圖用來顯示兩組資料之間的關係 (可觀察分佈模式), ScatterPlot() 函式支持分組, 標籤和互動等功能, 透過顏色分組可對比不同類別的資料特徵. 

常用參數與上面折線圖相同 : 
  • value : 要顯示的資料, 可以是 Pandas DataFrame 或 JSON 格式. 
  • x : 指定要做為 x 軸的 DataFrame 欄索引名稱 
  • y : 指定要做為 y 軸的 DataFrame 欄索引名稱
  • color : 指定要做為分組標籤的 DataFrame 欄索引名稱
  • title : 圖表的標題 (字串)
  • x_title : x 軸標籤
  • y_title : y 軸標籤
  • interactive : 圖表是否具有互動性 True/False (預設)
範例程式碼 (1) :

import gradio as gr
import pandas as pd

data=pd.DataFrame({
    'x': [1, 2, 3, 4, 5, 6, 7, 8],
    'y': [2.5, 3.1, 4.7, 3.8, 5.5, 6.8, 7.9, 8.2], 
    })

plot=gr.ScatterPlot(
    value=data, 
    x='x', 
    y='y', 
    title='練習次數與平均成績的關係', 
    x_title='練習次數', 
    y_title='平均成績(0~10)', 
    interactive=True
    )

iface=gr.Interface(
    fn=lambda: data, 
    inputs=[], 
    outputs=plot,
    title='分佈圖 ScatterPlot() 測試',
    flagging_mode='never',
    )

iface.launch()

此例資料紀錄選手賽前練習次數與平均成績之關係, 結果如下 :




從分布圖可知練習次數與平均成績呈正相關關係. 

此 Web App 網址如下 :


不過 Gradio 的 ScatterPlot() 與 Matplotlib 的 scatter() 不同的是, ScatterPlot() 的繪圖時 x, y 軸總是從原點開始, 缺點是若 x, y 值都很小或很大, 那麼分布圖會繪製在離原點很遠的小角落, 導致無法看出資料間的關係; 而 Matplotlib 的 scatter() 則會自動將原點移到比 x, y 軸最小值還低一些的地方, 這樣就能將視野聚焦在資料分布的區域. 

範例程式碼 (2) :

import gradio as gr
import pandas as pd

data=pd.DataFrame({
    'x': [49, 65, 53, 45, 56, 47, 52, 61],
    'y': [159, 177, 156, 163, 164, 158, 166, 171] 
    })

plot=gr.ScatterPlot(
    value=data, 
    x='x', 
    y='y', 
    title='身高與體重關係', 
    x_title='體重(Kg)', 
    y_title='身高(Cm)', 
    interactive=True
    )

iface=gr.Interface(
    fn=lambda: data, 
    inputs=[], 
    outputs=plot,
    title='分佈圖 ScatterPlot() 測試',
    flagging_mode='never',
    )

iface.launch()

此例最低體重 45Kg, 最低身高 158Cm, 都離原點甚遠, 其分布點聚集在右上角, 由於尺度被壓縮了, 使得 x, y 的關係無法清楚地看出來 :




此 Web App 網址如下 :


資料取自之前測試 Matplotlib 時所用之體重與身高關聯性資料, 參考 :


如果使用 Matplotlib 的 scatter() 函式畫分布圖, 它會自動移動畫布原點讓畫布能呈現整個資料分布區域的全貌, Gradio 目前無法做到這樣的功能, 但能透過 Matplotlib 繪圖後將結果交給 Gradio 的 Plot() 函式輸出了, 如下面範例所示 :

範例程式碼 (3) :

import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt

def plot_scatter_chart():
    plt.figure(figsize=(8, 5))
    x=[49, 65, 53, 45, 56, 47, 52, 61]
    y=[159, 177, 156, 163, 164, 158, 166, 171]
    plt.scatter(x, y, color='blue', alpha=0.75)
    plt.title('Relationship between Height and Weight')  # 圖表標題
    plt.xlabel('Weight (Kg)')  # X 軸標籤
    plt.ylabel('Height (Cm)')  # Y 軸標籤
    plt.grid(True, linestyle='--', alpha=0.5)  # 添加網格
    plt.tight_layout()  # 自動調整布局
    return plt.gcf()  # 傳回 Figure 物件

iface=gr.Interface(
    fn=plot_scatter_chart,  # 繪製函數
    inputs=[],              # 無需用戶輸入
    outputs=gr.Plot(),      # 輸出為 Gradio 的 Plot 物件
    title='Scatter Plot Test(with Matplotlib)',
    flagging_mode='never',
    )

iface.launch()

此例以 Gradio 的 Plot 物件做為輸出對象, 處理函式參數 fn 指定呼叫 Matplotlib 寫的繪圖函式來繪製散佈圖, 將繪圖結果的 Figure 畫布物件傳回給 Gradio 的 Plot 物件繪製即可, 結果如下 :




可見由於 Matplotlib 會自動移動圖表的原點以聚焦在資料分布之區域, 故能較清楚觀察資料間的似乎呈現一種正相關關係. 

此 Web App 網址如下 : 


注意, 由於 Hugging Face Spaces 執行空間並未預載 Matplotlib, 因此需編輯 requirements.txt 加入matplotlib 於佈署時安裝此套件才能執行繪圖. 

除了 Matplotlib 外也可以利用 Plotly 繪圖後將畫布物件傳給 Gradio 的 Plot 物件顯示, 關於 Plotly 用法參考 :


程式碼如下 :

import gradio as gr
import pandas as pd
import plotly.express as px

def plot_scatter_chart():
    x=[49, 65, 53, 45, 56, 47, 52, 61]  # 體重
    y=[159, 177, 156, 163, 164, 158, 166, 171]  # 身高 
    data=pd.DataFrame({'weight': x, 'height': y})    
    fig=px.scatter(data, x='weight', y='height') 
    return fig  # 傳回 Figure 物件

iface=gr.Interface(
    fn=plot_scatter_chart,  # 繪製函數
    inputs=[],              # 無需用戶輸入
    outputs=gr.Plot(),      # 輸出為 Gradio 的 Plot 物件
    title='Scatter Plot Test(with Plotly)',
    flagging_mode='never',
    )

iface.launch()

此例使用 Plotly 的 Express 模組來繪圖 (也可以用原生的 graph_objs 來畫), 由於 Gradio Playground 沒有預載 Plotly, 所以只能在本機或直接在 Hugging Face Spaces 佈署, 結果如下 :




可見 Plotly 與 Matplotlib 一樣都會將畫面聚焦在資料分布的區域. 同樣地, 由於 Hugging Face Spaces 執行空間並未預載 Plotly, 因此需編輯 requirements.txt 加入 plotly 於佈署時安裝此套件才能執行繪圖. 注意, Hugging Face Spaces 不支援中文字型. 



3. 用 BarPlot() 繪製長條圖 : 

長條圖主要用來顯示類別資料的數量變化, 透過柱狀高度或長度來觀察不同類別之數量變化情形. BarPlot() 函式支持分組, 標籤和互動等功能, 透過 color 參數指定之分組欄可對比不同分組之類別資料差異. 注意, Gradio 目前僅支援垂直長條圖, 不支援水平長條圖.

常用參數如下 : 
  • value : 要顯示的資料, 可以是 Pandas DataFrame 或 JSON 格式. 
  • x : 指定要做為 x 軸的 DataFrame 欄索引名稱 
  • y : 指定要做為 y 軸的 DataFrame 欄索引名稱
  • color : 指定要做為分組標籤的 DataFrame 欄索引名稱
  • title : 圖表的標題 (字串)
  • x_title : x 軸標籤
  • y_title : y 軸標籤
  • interactive : 圖表是否具有互動性 True/False (預設)
  • grouped: True/False (預設), 用來是否多組長條圖並列顯示 
  • stacked: True (預設)/False, 用來是否多組長條圖堆疊顯示
注意, BarPlot() 的 y 參數只能指定一個欄, 不能傳入多欄串列, 也不能像 Matplotlib 那樣指定 y1, y2, ... 等多個 y 軸來顯示多組長條圖, 當有多組資料時需用 color 參數指定分組欄位, y 軸資料與指定之分組欄位均需對應擴展.

範例程式碼 (1) :

import gradio as gr
import pandas as pd

data=pd.DataFrame({
    'x': ['一月', '二月', '三月', '四月', '五月', '六月'],
    'y': [12000, 15000, 17000, 13000, 19000, 21000], 
    })

plot=gr.BarPlot(
    value=data, 
    x='x', 
    y='y', 
    title='上半年月銷售額變化', 
    x_title='月份', 
    y_title='銷售額(萬元)', 
    interactive=True
    )

iface=gr.Interface(
    fn=lambda: data, 
    inputs=[], 
    outputs=plot,
    title='長條圖 BarPlot() 測試',
    flagging_mode='never',
    )

iface.launch()

結果如下 :




此 Web App 網址如下 :


如果要顯示多組資料的長條圖要用到 color, grouped, 與 stacked 參數, 如上所述, 目前 Gradio 版本的 BarPlot() 只能指定一個 y 軸資料, 因此如果要顯示多組資料的長條圖, 必須將各組資料串成一個串列, 然後用分組欄位標示是哪一組資料, 例如上面顯示的是銷售一課的業績, 如果加入銷售二課業績 : 

'銷售額': [11000, 14000, 16000, 12000, 18000, 20000],  # 銷售二課

那麼要將其合併擴展為一個串列做為 y 軸資料 : 

'銷售額': [12000, 15000, 17000, 13000, 19000, 21000,  # 銷售一課
              11000, 14000, 16000, 12000, 18000, 20000],  # 銷售二課

然後增加一個分組欄位來對應每一筆資料所屬分組  :

'部門': ['銷售一課'] * 6 + ['銷售二課'] * 6  # 部門標籤

然後傳入 color 參數指定分組欄位為 '部門' 即可. 

範例程式碼 (2) :

import gradio as gr
import pandas as pd

data=pd.DataFrame({  # 將資料轉為長格式
    '月份': ['一月', '二月', '三月', '四月', '五月', '六月'] * 2,
    '銷售額': [12000, 15000, 17000, 13000, 19000, 21000,  # 銷售一課
              11000, 14000, 16000, 12000, 18000, 20000], # 銷售二課
    '部門': ['銷售一課'] * 6 + ['銷售二課'] * 6            # 分組標籤
    })

plot=gr.BarPlot(
    value=data,
    x='月份',          # X 軸:月份
    y='銷售額',        # Y 軸:銷售額
    color='部門',      # 分組欄位
    title='上半年月銷售額變化',
    x_title='月份',
    y_title='銷售額(萬元)',
    interactive=True
    )

# 建立介面
iface=gr.Interface(
    fn=lambda: data,  
    inputs=[],        
    outputs=plot,
    title='分組長條圖 BarPlot() 測試',
    flagging_mode='never',
    )

iface.launch()

結果如下 : 




可見預設會顯示堆疊長條圖, 這是因為 stacked 參數預設是 True, 而 grouped 參數預設是 False 之故. 此例 Web app 網址如下 :


但是如果將 stacked 設為 False 與 grouped 設為 True, 並不會顯示並列的長條圖, 這可能是 Gradio 是高階的套件, 沒辦法像 Matplotlib 那樣支援低階繪圖功能所致. 如果要繪製並列的長條圖, 可以像上面分布圖做法, 利用 Matplotlib 畫好圖後將 Figure 物件傳給 Gradio 的 Plot() 顯示即可. 

範例程式碼 (3) :

import gradio as gr
import matplotlib.pyplot as plt
import numpy as np

def plot_grouped_barchart():
    plt.rcParams["font.family"]=["Microsoft JhengHei"]
    months=['一月', '二月', '三月', '四月', '五月', '六月']  # 月份
    x=np.arange(len(months))  # 數值型索引
    y1=[12000, 15000, 17000, 13000, 19000, 21000]  # 銷售一課
    y2=[11000, 14000, 16000, 12000, 18000, 20000]  # 銷售二課    
    width=0.35  # 長條寬度
    # 繪製長條圖
    plt.figure(figsize=(8, 5))
    plt.bar(x - width/2, y1, width, label='銷售一課', color='cyan')
    plt.bar(x + width/2, y2, width, label='銷售二課', color='orange')
    # 設定標籤與標題
    plt.xlabel('月份')
    plt.ylabel('銷售額(萬元)')
    plt.title('上半年月銷售額變化 (並排分組)')
    plt.xticks(x, months)  # 設定 x 軸刻度標籤
    plt.legend()  # 顯示圖例
    plt.tight_layout()  # 自動調整布局
    return plt.gcf()  # 傳回 Figure 物件

iface=gr.Interface(
    fn=plot_grouped_barchart,
    inputs=[],
    outputs=gr.Plot(),
    title='分組長條圖測試',
    flagging_mode='never'
    )

iface.launch()

此例先利用 X 軸刻度 (月份字串串列) 產生可計算之數值刻度, 這樣才能計算兩組資料的 X 軸偏移位置以繪製並立的長條. 繪圖完成後呼叫 plt.gcf() 取得 Figure 畫布物件並將其傳回給 gradio.Plot() 函式繪製. 由於 Gradio 沒有所需之中文微軟正黑體字型, 所以改在本機執行上面的程式, 結果如下 :




當然也可以用 Matplotlib 的 barh() 繪製水平的分組並立長條圖, 但要將 x, y 軸顛倒 (寬度變成高度). 下面範例除了改用 barh() 繪製水平長條圖外, 還將中文改成英文, 這樣才能在 Gradio Playground 與 Hugging Face Spaces 中正常呈現標題 (因為此二平台的 Matplotlib 的中文顯示) : 

範例程式碼 (3) :

import gradio as gr
import matplotlib.pyplot as plt
import numpy as np

def plot_grouped_barchart():
    months=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']  # 月份
    y=np.arange(len(months))  # 數值型索引
    x1=[12000, 15000, 17000, 13000, 19000, 21000]  # 銷售一課
    x2=[11000, 14000, 16000, 12000, 18000, 20000]  # 銷售二課    
    height=0.35  # 長條高度
    # 繪製長條圖
    plt.figure(figsize=(8, 5))
    plt.barh(y - height/2, x1, height, label='Sales Team 1', color='cyan')
    plt.barh(y + height/2, x2, height, label='Sales Team 2', color='orange')   
    plt.ylabel('Months')
    plt.xlabel('Sales (in ten thousand)')
    plt.title('Monthly Sales Performance (Grouped)')
    plt.yticks(y, months)  # 設定 y 軸刻度標籤
    plt.legend()  # 顯示圖例
    plt.tight_layout()  # 自動調整布局
    return plt.gcf()  # 返回圖表對象

iface=gr.Interface(
    fn=plot_grouped_barchart,
    inputs=[],
    outputs=gr.Plot(),
    title='Grouped Bar Chart Test(Horizontal)',
    flagging_mode='never'
    )

iface.launch()

結果如下 : 




此例 Web app 網址如下 :


2024年12月12日 星期四

momo 購買 WD 5TB 硬碟+汽機車電池充電修復器

今天 雙 12 購物節在 momo 買WD 5TB 硬碟與汽機車電池充電修復器 : 





原本還想買一顆 WD 16TB 硬碟, 但太晚結帳被買光了 : 




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

通過前四篇的測試我們已基本上掌握了 Gradio 的用法, 本篇要應用 Gradio 作為使用者介面來串接 OpenAI API, 模仿類似 ChatGPT 的聊天功能. 



八. 用 Gradio 串接語言模型 API : 

使用 OpenAI API 需先註冊帳號並取得 API 金鑰, 做法參考 : 


注意, OpenAI 已不再贈送 5 美元給初次的 API 註冊者, API key 在帳號註冊完成後即可取得, 但必須付費儲值 (Pay As You Go) 至少 5 美元後才能使用 API key 與 GPT 模型互動. 


1. 在本機執行 OpenAI API 聊天機器人程式 : 

簡易的無記憶性聊天機器人程式碼如下 :

import gradio as gr
from openai import OpenAI

def ask_gpt(api_key, prompt):
    client=OpenAI(api_key=api_key) 
    chat_completion=client.chat.completions.create(
        messages=[{'role': 'user', 'content': prompt}],
        model='gpt-3.5-turbo'
        ) 
    return chat_completion.choices[0].message.content

api_key=gr.Textbox(label='輸入 OpenAI 金鑰', type='password') 
prompt=gr.Textbox(label='您的詢問: ')
reply=gr.Textbox(label='OpenAI 回答: ')
iface=gr.Interface(
    fn=ask_gpt,
    inputs=[api_key, prompt],  
    outputs=reply,
    title='OpenAI API 聊天機器人',
    flagging_mode='never',
    )
iface.launch()

此例有兩個 Textbox 輸入元件與一個 Textbox 輸出元件 : api_key 是一個指定 type='password' 的密碼文字欄位 Textbox 元件, 用來輸入自己的 OpenAI API key; 而 prompt 則是用來輸入提示詞的 Textbox 元件; reply 則是用來輸出 OpenAI API 的回應結果. 在本機的執行結果如下 :




2. 在 Hugging Face Spaces 佈署 OpenAI API 聊天機器人程式 : 

由於執行上面的 App 程式碼需要安裝 openai 套件才能呼叫 OpenAI API, 但 Gradio Playground 上並沒有預載 openai 套件, 所以此 App 無法在 Gradio Playground 上測試, 當然也就無法一鍵佈署至 Hugging Face Spaces 空間, 須手動在 Hugging Face Spaces 上自行佈署 App 與安裝所需套件. 網址如下 : 


首先需為 App 建立一個執行空間, 按右上角的 "Create new space" 鈕新增 Space : 




輸入 Space name (網址的一部份, 不要有空格) 與 Short description (簡短的 App 描述), 勾選一種授權方式 (隨意), 


點選 SDK (Gradio) 後按最底下的 "Create Space" 鈕即可 :




Space 建立完成頁面如下, 有附上 Gradio 應用程式 app.py 的範例與上傳程式檔案的 Git 指令 : 




可以按右上角的 File 按鈕進入檔案管理頁面線上編輯 app.py, 如果沒有顯示 File 則要按右上角的三個點點小按鈕展開選單 : 




在檔案管理頁面按 "+Add file" 鈕選擇 "Create a new file" 新增程式檔案 app.py :




在開啟的程式編輯器中, 上方輸入框固定要填入 app.py, 底下則貼入上面的程式碼 :




然後按最底下的 "Commit new file to main" 鈕存檔 :




存檔後若要繼續修改按 Edit 鈕, 刪除按 Delete 鈕 :




但在佈署之前還需要新增一個 requirement.txt 檔列出需安裝之套件 openai, 按 App 專案目錄超連結回到檔案列表, 按 "+ Add file" 新增 requirement.txt, 內容輸入 :

openai>=1.14.0

表示要安裝 v1.14.0 版以上的 openai 套件 : 




同樣按最底下的 "Commit new file to main" 鈕存檔後按上方自己的帳號回 App 列表 :




這時可看到此 App 左上角顯示 "Building", 直接按此 App 執行佈署 :




佈署完成就會出現 App 網頁了 : 




與上面在本機執行的頁面是一樣的. 此 Web App 網址如下 :



3. 在 Hugging Face Spaces 佈署 Gemini API 聊天機器人程式 : 

在上面的測試中使用了一個 type='password' 的 Textbox 來儲存 OpenAPI 金鑰, 其實 Hugging Face Spaces 有提供 Secrets 資料表來儲存 API key 或 token 之類的隱密資訊 (每個 App 用到的 Secrets 是各自獨立不相關的), 以下將以串接 Google Gemini API 為例說明做法, 關於 Gemini 參考 : 


首先先在本機測試 Gemini 聊天機器人, 程式碼如下 : 

# gradio_gemini_api_1.py
import gradio as gr
import google.generativeai as genai
import os, time      
from dotenv import load_dotenv  

def ask_gemini(model_sel, prompt):
    start_time=time.time()
    model=genai.GenerativeModel(model_sel)
    safety_settings={
        'HATE': 'BLOCK_NONE',
        'HARASSMENT': 'BLOCK_NONE',
        'SEXUAL' : 'BLOCK_NONE',
        'DANGEROUS' : 'BLOCK_NONE'
        }    
    reply=model.generate_content(prompt, safety_settings=safety_settings)
    elapsed_time=time.time()-start_time
    return f'{reply.text}\n模型: {model_sel}\n耗時: {elapsed_time:.2f} 秒'

load_dotenv()
api_key=os.environ.get('GEMINI_API_KEY')
genai.configure(api_key=api_key)
model_sel=gr.Radio(label='選擇模型',
                   choices=['gemini-1.5-flash', 'gemini-1.5-pro'],
                   value='gemini-1.5-flash') 
prompt=gr.Textbox(label='您的詢問: ')
reply=gr.Textbox(label='Gemini 的回答: ')
iface=gr.Interface(
    fn=ask_gemini,
    inputs=[model_sel, prompt],  
    outputs=reply,
    title='Gemini API 聊天機器人',
    flagging_mode='never',
    )
iface.launch()

此例有兩個輸入元件 (用來選擇模型的 Radio 元件與輸入提示詞的 Textbox 元件) 與一個用來輸出 Gemini 回應的 Textbot 元件. 我已經將 Gemini API key 存放在目前工作目錄下的 .env 隱藏檔的 GEMINI_API_KEY 變數裡, 然後利用 dotenv 模組的 loadenv() 函式載入此隱藏檔, 再利用 os 模組的 os.environ.get() 函式取得 API key. 執行結果如下 :





接下來要將此 App 佈署到 Hugging Face 上, 跟上面一樣先建立一個名為 "gradio_gemini_api_1" 的 Space : 




按 File 進入檔案管理頁面 : 




按 +Add File 鈕新增 app.py 與 requirements.txt 這兩個檔按 : 




將上面的程式碼貼到 app.py 檔案內容裡 :




requirements.txt 則要指定安裝第三方套件 python-dotenv 與 google.generativeai (注意, dotenv 套件的安裝名稱是 python-dotenv) :




檔案新增完成後如下所示, 接下來要將 API key 存入 Secrets 變數中, 按上方的 Settings 鈕 :




往下拉到 "Variables and secrets" 這一項, 按右方的 "New secret" 鈕 :




Name 欄填入與上面程式中相同的 API key 名稱 GEMINI_API_KEY, 而 Value 欄則填入 API key 後按 Save 即可 :





這樣就完成佈署此 App 的全部設定了, 這是 Hugging Face Spaces 帳戶就會看到此 App 已經在 Running 狀態, 點進去即可看到 App 頁面 : 






此 Web App 網址如下 : 

https://huggingface.co/spaces/tony1966/gradio_gemini_api_1   

當然上面的 Open AI 聊天機器人也可以這麼做, 只是會扣儲值的錢, Google Gemini 應該是所有語言模型中最佛心的了 (等做大了就會收費啦).