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 一樣簡單, 但感覺更有質感. 

水某最近常看 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 提供了如下之繪圖元件 :


 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
 Histogram (直方圖) title,x_title, y_title, x, y, bins, color, interactive
 AreaPlot (區域圖) title,x_title, y_title, x, y, stacked, color, interactive
 Heatmap (熱點圖) title,x_title, y_title, x, y, x, value, color, interactive
 TimeSeries (時間序列圖) title,x_title, y_title, x, y, value, color, 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 軸資料, 但需要用到 Pandas 的 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 會自動移動圖表的原點以聚焦在資料所在之區域, 故能較清楚觀察資料間的關係, 下面是 Matplotlit 的 scatter() 對相同資料繪製之散佈圖 :



這樣就能較清楚地看出似乎呈現一種正相關關係. 不過 Gradio 目前無法做到這樣的功能, 只能透過 Matplotlib 繪圖後將結果交給 Gradio 輸出了. 


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 與 Hugging Face Spaces 中呈現與佈署 : 

範例程式碼 (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 應該是所有語言模型中最佛心的了 (等做大了就會收費啦). 

2024年12月11日 星期三

Hugging Face Spaces 佈署 Gradio 專案 App 時出現錯誤

這兩天在測試 Gradio 元件發現時, 將專案程式佈署到 Hugging Face Spaces 都失敗 (之前都沒問題), 例如今天佈署 Video Test 1 時出現如下錯誤訊息 : 

===== Build Queued at 2024-12-11 07:03:42 / Commit SHA: 5b17fe2 =====

--> FROM docker.io/library/python:3.10@sha256:3ba2e48b887586835af6a0c35fc6fc6086fb4881e963082330ab0a35f3f42c16
DONE 0.0s

--> RUN apt-get update && apt-get install -y fakeroot &&     mv /usr/bin/apt-get /usr/bin/.apt-get &&     echo '#!/usr/bin/env sh\nfakeroot /usr/bin/.apt-get $@' > /usr/bin/apt-get &&     chmod +x /usr/bin/apt-get && rm -rf /var/lib/apt/lists/* && useradd -m -u 1000 user
CACHED

--> WORKDIR /home/user/app
CACHED

--> RUN apt-get update && apt-get install -y git git-lfs ffmpeg libsm6 libxext6 cmake rsync libgl1-mesa-glx && rm -rf /var/lib/apt/lists/* && git lfs install
CACHED

--> COPY --chown=1000:1000 --from=root / /
CACHED

--> RUN pip install --no-cache-dir pip==22.3.1 && pip install --no-cache-dir datasets "huggingface-hub>=0.19" "hf-transfer>=0.1.4" "protobuf<4" "click<8.1" "pydantic~=1.0"
CACHED

--> Restoring cache
DONE 9.2s

--> RUN apt-get update && apt-get install -y curl && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs && rm -rf /var/lib/apt/lists/* && apt-get clean
Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
Get:2 http://deb.debian.org/debian bookworm-updates InRelease [55.4 kB]
Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8789 kB]
Get:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [8856 B]
Get:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [216 kB]
Fetched 9268 kB in 1s (9977 kB/s)
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
curl is already the newest version (7.88.1-10+deb12u8).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
2024-12-11 08:03:59 - Installing pre-requisites
Hit:1 http://deb.debian.org/debian bookworm InRelease
Hit:2 http://deb.debian.org/debian bookworm-updates InRelease
Hit:3 http://deb.debian.org/debian-security bookworm-security InRelease
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
ca-certificates is already the newest version (20230311).
curl is already the newest version (7.88.1-10+deb12u8).
gnupg is already the newest version (2.2.40-1.1).
The following NEW packages will be installed:
  apt-transport-https
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 25.2 kB of archives.
After this operation, 35.8 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian bookworm/main amd64 apt-transport-https all 2.6.1 [25.2 kB]
debconf: delaying package configuration, since apt-utils is not installed
Fetched 25.2 kB in 0s (1350 kB/s)
Selecting previously unselected package apt-transport-https.
(Reading database ... 
(Reading database ... 5%
(Reading database ... 10%
(Reading database ... 15%
(Reading database ... 20%
(Reading database ... 25%
(Reading database ... 30%
(Reading database ... 35%
(Reading database ... 40%
(Reading database ... 45%
(Reading database ... 50%
(Reading database ... 55%
(Reading database ... 60%
(Reading database ... 65%
(Reading database ... 70%
(Reading database ... 75%
(Reading database ... 80%
(Reading database ... 85%
(Reading database ... 90%
(Reading database ... 95%
(Reading database ... 100%
(Reading database ... 29638 files and directories currently installed.)
Preparing to unpack .../apt-transport-https_2.6.1_all.deb ...
Unpacking apt-transport-https (2.6.1) ...
Setting up apt-transport-https (2.6.1) ...
Hit:1 http://deb.debian.org/debian bookworm InRelease
Hit:2 http://deb.debian.org/debian bookworm-updates InRelease
Hit:3 http://deb.debian.org/debian-security bookworm-security InRelease
Get:4 https://deb.nodesource.com/node_20.x nodistro InRelease [12.1 kB]
Get:5 https://deb.nodesource.com/node_20.x nodistro/main amd64 Packages [9928 B]
Fetched 22.1 kB in 0s (84.3 kB/s)
Reading package lists...
2024-12-11 08:04:01 - Repository configured successfully.
2024-12-11 08:04:01 - To install Node.js, run: apt-get install nodejs -y
2024-12-11 08:04:01 - You can use N|solid Runtime as a node.js alternative
2024-12-11 08:04:01 - To install N|solid Runtime, run: apt-get install nsolid -y 

Reading package lists...
Building dependency tree...
Reading state information...
The following NEW packages will be installed:
  nodejs
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 31.8 MB of archives.
After this operation, 197 MB of additional disk space will be used.
Get:1 https://deb.nodesource.com/node_20.x nodistro/main amd64 nodejs amd64 20.18.1-1nodesource1 [31.8 MB]
debconf: delaying package configuration, since apt-utils is not installed
Fetched 31.8 MB in 0s (98.1 MB/s)
Selecting previously unselected package nodejs.
(Reading database ... 
(Reading database ... 5%
(Reading database ... 10%
(Reading database ... 15%
(Reading database ... 20%
(Reading database ... 25%
(Reading database ... 30%
(Reading database ... 35%
(Reading database ... 40%
(Reading database ... 45%
(Reading database ... 50%
(Reading database ... 55%
(Reading database ... 60%
(Reading database ... 65%
(Reading database ... 70%
(Reading database ... 75%
(Reading database ... 80%
(Reading database ... 85%
(Reading database ... 90%
(Reading database ... 95%
(Reading database ... 100%
(Reading database ... 29642 files and directories currently installed.)
Preparing to unpack .../nodejs_20.18.1-1nodesource1_amd64.deb ...
Unpacking nodejs (20.18.1-1nodesource1) ...
Setting up nodejs (20.18.1-1nodesource1) ...
DONE 7.7s

--> RUN --mount=target=/tmp/requirements.txt,source=requirements.txt     pip install --no-cache-dir -r /tmp/requirements.txt
ERROR: Invalid requirement: 'pip install librosa' (from line 1 of /tmp/requirements.txt)

[notice] A new release of pip available: 22.3.1 -> 24.3.1
[notice] To update, run: pip install --upgrade pip

--> ERROR: process "/bin/sh -c pip install --no-cache-dir -r /tmp/requirements.txt" did not complete successfully: exit code: 1

看起來似乎是 requirement.txt 裡面的 'pip install librosa' 這個指令造成的, 可能是前幾天在測試 Audio 元件時需要用到 librosa 模組, 但剛使用 Hugging Face Space 還不熟悉, 看見有輸入框就輸入 pip insatall 指令, 應該是這樣改到了 requirement.txt (但不解的是, 為何之後每個要佈署的專案的 requirement.txt 裡都有這指令?).

解決此問題需要進去每個佈署失敗的專案的 Files 頁面修改 requirement.txt 這檔案, 首先登入 Hugging Face Space 後進入自己的帳戶 : 






可見最近佈署的兩個 Gradio 專案都失敗 (左上角有 'Build Error'), 點專案進去會進入該專案的佈署頁面 (問題還沒解決當然還是失敗), 按頁面右上方的三個點點的小按鈕會出現 App 的設定選單, 按 Files 進入檔案管理頁面 : 





點檔案列表中的 requirement.txt 顯示其內容 :




 按 Edit 進入檔案編輯頁面 :




將檔案中的 pip install 內容刪除後按最底下的 Commit 鈕更新檔案 : 





再次佈署專案就能順利完成了 :