今天詳解一個 Python 庫 Streamlit,它可以為機器學習和數據分析構建 web app。它的優勢是入門容易、純 Python 編碼、開發效率高、UI精美。
上圖是用 Streamlit 構建自動駕駛模型效果的 demo,左側是模型的參數,右側是模型的效果。通過調整左側參數,右邊的模型會實時地響應。
由此可以看出,對於交互式的數據可視化需求,完全可以考慮用 Streamlit 實現。特別是在學習、工作匯報的時候,用它的效果遠好於 PPT。
因為 Streamlit 提供了很多前端交互的組件,所以也可以用它來做一些簡單的web 應用。今天我們也會用它來做個垃圾分類的 web app。
之前我們用 Streamlit 做過兩個app,《植物識別app》和《動物識別app》。但只是用了 Streamlit 一小部分功能。今天我們就按照 Streamlit 官網文檔,對其做個詳解。
1.文本組件
我使用的是 Python 3.8 環境,執行 pip install streamlit 安裝。安裝后執行 streamlit hello 檢查是否安裝成功。
先來了解下 Streamlit 最基礎的文本組件。
文本組件是用來在網頁展示各種類型的文本內容。Streamlit 可以展示純文本、Markdown、標題、代碼和LaTeX公式。
import streamlit as st
# markdown
st.markdown('Streamlit is **_really_ cool**.')
# 設置網頁標題
st.title('This is a title')
# 展示一級標題
st.header('This is a header')
# 展示二級標題
st.subheader('This is a subheader')
# 展示代碼,有高亮效果
code = '''def hello():
print("Hello, Streamlit!")'''
st.code(code, language='python')
# 純文本
st.text('This is some text.')
# LaTeX 公式
st.latex(r'''
a + ar + a r^2 + a r^3 + \cdots + a r^{n-1} =
\sum_{k=0}^{n-1} ar^k =
a \left(\frac{1-r^{n}}{1-r}\right)
''')
上述是 Streamlit 支持的文本展示組件,代碼存放 my_code.py 文件中。編碼完成后,執行 streamlit run my_code.py ,streamlit 會啟動 web 服務,加載指定的源文件。
啟動后,可以看到命令行打印以下信息
streamlit run garbage_classifier.py
You can now view your Streamlit app in your browser.
Local URL: http://localhost:8501
Network URL: http://192.168.10.141:8501
在瀏覽器訪問 http://localhost:8501/ 即可。
當源代碼被修改,無需重啟服務,在頁面上點擊刷新按鈕就可加載最新的代碼,運行和調試都非常方便。
2. 數據組件
dataframe 和 table 組件可以展示表格。
import streamlit as st
import pandas as pd
import numpy as np
df = pd.DataFrame(
np.random.randn(50, 5),
columns=('col %d' % i for i in range(5)))
# 交互式表格
st.dataframe(df)
# 靜態表格
st.table(df)
dateframe 和 table 的區別是,前者可以在表格上做交互(如:排序),后者只是靜態的展示。它們支持展示的數據類型包括 pandas.DataFrame、pandas.Styler、pyarrow.Table、numpy.ndarray、Iterable、dict。
metric 組件用來展示指標的變化,數據分析中經常會用到。
st.metric(label="Temperature", value="70 °F", delta="1.2 °F")
value 參數表示當前指標值,delta 參數表示與前值的差值,向上的綠色箭頭代表相比於前值,是漲的,反之向下的紅箭頭代表相比於前值是跌的。當然漲跌顏色可以通過 delta_color 參數來控制。
json 組件用來展示 json 類型數據
st.json({
'foo': 'bar',
'stuff': [
'stuff 1',
'stuff 2',
],
})
Streamlit 會將 json 數據格式化,展示地更美觀,並且提供交互,可以展開、收起 json 的子節點。
3. 圖表組件
Streamlit 的圖表組件包含兩部分,一部分是原生組件,另一部分是渲染第三方庫。
原生組件只包含 4 個圖表,line_chart、area_chart 、bar_chart 和 map,分別展示折線圖、面積圖、柱狀圖和地圖。
chart_data = pd.DataFrame(
np.random.randn(20, 3),
columns=['a', 'b', 'c'])
st.line_chart(chart_data)
上述是 line_chart 的示例,其他圖表的使用方法與之類似。
Streamlit 圖表可設置的參數很少,除了數據源外,剩下只能設置圖表的寬度和高度。
雖然 Streamlit 原生圖表少,但它可以將其他 Python 可視化庫的圖表展示在 Streamlit 頁面上。支持的可視化庫包括:matplotlib.pyplot、Altair、vega-lite、Plotly、Bokeh、PyDeck、Graphviz。
以 matplotlib.pyplot 為例,使用方式如下:
import matplotlib.pyplot as plt
arr = np.random.normal(1, 1, size=100)
fig, ax = plt.subplots()
ax.hist(arr, bins=20)
st.pyplot(fig)
跟直接寫 matplotlib.pyplot 一樣,只不過最終展示的時候調用 st.pyplot 便可以將圖表展示 Streamlit 頁面上。其他 Python 庫的使用方法與之類似。
輸入組件
前面我們介紹的三類組件都是輸出類、展示類的。對於交互式的頁面來說,接受用戶的輸入是必不可少的。
Streamlit 提供的輸入組件都是基本的,都是我們在網站、移動APP上經常看到的。包括:
- button:按鈕
- download_button:文件下載
- file_uploader:文件上傳
- checkbox:復選框
- radio:單選框
- selectbox:下拉單選框
- multiselect:下拉多選框
- slider:滑動條
- select_slider:選擇條
- text_input:文本輸入框
- text_area:文本展示框
- number_input:數字輸入框,支持加減按鈕
- date_input:日期選擇框
- time_input:時間選擇框
- color_picker:顏色選擇器
它們包含一些公共的參數:
- label:組件上展示的內容(如:按鈕名稱)
- key:當前頁面唯一標識一個組件
- help:鼠標放在組件上展示說明信息
- on_click / on_change:組件發生交互(如:輸入、點擊)后的回調函數
- args:回調函數的參數
- kwargs:回調函數的參數
下面以 selectbox 來演示輸入組件的用法
option = st.selectbox(
'下拉框',
('選項一', '選項二', '選項三'))
st.write('選擇了:', option)
selectbox 展示三個選項,並輸出當前選中的項(默認選中第一個)。當我們在頁面下拉選擇其他選項后,整個頁面代碼會重新執行,但組件的選擇狀態 會保留在 option 中,因此,調用 st.write 后會輸出選擇后的選項。
st.write 也是一個輸出組件,可以輸出字符串、DataFrame、普通對象等各種類型數據。
其他組件的使用與之類似,組件效果圖如下:
5.多媒體組件
Streamlit 定義了 image、audio 和 video 用於展示圖片、音頻和視頻。
可以展示本地多媒體,也通過 url 展示網絡多媒體。
用法跟前面的組件是一樣的,后面的垃圾分類 APP 我們會用到 image 組件。
6. 狀態組件
狀態組件用來向用戶展示當前程序的運行狀態,包括:
- progress:進度條,如游戲加載進度
- spinner:等待提示
- balloons:頁面底部飄氣球,表示祝賀
- error:顯示錯誤信息
- warning:顯示報警信息
- info:顯示常規信息
- success:顯示成功信息
- exception:顯示異常信息(代碼錯誤棧)
效果如下:
7. 其他內容
到這里,Streamlit 的組件基本上就全介紹完了,組件也是 Streamlit 的主要內容。
這小節介紹一下其他比較重要的內容,包括頁面布局、控制流和緩存。
頁面布局。之前我們寫的 Streamlit 都是按照代碼執行順序從上至下展示組件,Streamlit 提供了 5 種布局:
- sidebar:側邊欄,如:文章開頭那張圖,頁面左側模型參數選擇
- columns:列容器,處在同一個 columns 內組件,按照從左至右順序展示
- expander:隱藏信息,點擊后可展開展示詳細內容,如:展示更多
- container:包含多組件的容器
- empty:包含單組件的容器
控制流。控制 Streamlit 應用的執行,包括
- stop:可以讓 Streamlit 應用停止而不向下執行,如:驗證碼通過后,再向下運行展示后續內容。
- form:表單,Streamlit 在某個組件有交互后就會重新執行頁面程序,而有時候需要等一組組件都完成交互后再刷新(如:登錄填用戶名和密碼),這時候就需要將這些組件添加到 form 中
- form_submit_button:在 form 中使用,提交表單。
緩存。這個比較關鍵,尤其是做機器學習的同學。剛剛說了, Streamlit 組件交互后頁面代碼會重新執行,如果程序中包含一些復雜的數據處理邏輯(如:讀取外部數據、訓練模型),就會導致每次交互都要重復執行相同數據處理邏輯,進而導致頁面加載時間過長,影響體驗。
加入緩存便可以將第一次處理的結果存到內存,當程序重新執行會從內存讀,而不需要重新處理。
使用方法也簡單,在需要緩存的函數加上 @st.cache 裝飾器即可。前兩天我們講過 Python 裝飾器。
DATE_COLUMN = 'date/time'
DATA_URL = ('https://s3-us-west-2.amazonaws.com/'
'streamlit-demo-data/uber-raw-data-sep14.csv.gz')
@st.cache
def load_data(nrows):
data = pd.read_csv(DATA_URL, nrows=nrows)
lowercase = lambda x: str(x).lower()
data.rename(lowercase, axis='columns', inplace=True)
data[DATE_COLUMN] = pd.to_datetime(data[DATE_COLUMN])
return data
8. 垃圾分類
最后講解垃圾分類APP的代碼,前面介紹幾大類組件在該 APP 都有涉及。
垃圾分類模型我用的是天行 API ,大家可以去 https://www.tianapi.com/ 注冊賬號,獲取 appkey,開通“圖像垃圾分類” 接口即可。
接口的輸入如下:
除了key外,其他 3 個參數需要用 Streamlit 組件實現,代碼如下:
import base64
import requests
import streamlit as st
import pandas as pd
import numpy as np
add_selectbox = st.sidebar.selectbox(
"圖片來源",
("本地上傳", "URL")
)
uploaded_file = None
img_url = None
if add_selectbox == '本地上傳':
uploaded_file = st.sidebar.file_uploader(label='上傳圖片')
else:
img_url = st.sidebar.text_input('圖片url')
cls_mode = {'嚴格模式': 0, '模糊模式': 1}
mode_name = st.sidebar.radio('分類模式', cls_mode)
mode = cls_mode[mode_name]
使用了 3 個輸入組件,因為 img 和 imgurl 是二選一,所以我們用下拉單選框控制僅展示一個組件。
當輸入圖片后,我們希望在頁面上將圖片展示出來
# 請求結果
img_base64 = None
if uploaded_file:
st.image(uploaded_file, caption='本地圖片')
base64_data = base64.b64encode(uploaded_file.getvalue())
img_base64 = base64_data.decode()
if img_url:
st.image(img_url, caption='網絡圖片')
使用 image 多媒體組件即可。如果是本地圖片,需要將其轉成 base64 編碼的字符串。
最后,請求接口,獲取分類結果即可
if img_base64 or img_url:
cls_res = get_img_cls_res(img_base64, img_url, mode)
lajitype_to_name = {0: '可回收物', 1: '有害垃圾', 2: '廚余垃圾', 3: '其他垃圾', 4: '無法識別'}
if cls_res.status_code == 200:
cls_df = pd.DataFrame(cls_res.json()['newslist'])
cls_df['分類'] = cls_df.index.astype(str) + '-' + cls_df['name'] + '-' + cls_df['lajitype'].apply(lambda x: lajitype_to_name[x])
cls_df['置信度'] = cls_df['trust']
cls_df.set_index(["分類"], inplace=True)
print(cls_df)
st.bar_chart(cls_df[['置信度']])
else:
st.write(cls_res)
get_img_cls_res 函數是請求接口的函數
def get_img_cls_res(img_base64, img_url, mode):
url = 'https://api.tianapi.com/txapi/imglajifenlei/index'
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
body = {
'key': 'APPKEY',
'mode': mode
}
if img_base64:
body["img"] = img_base64
if img_url:
body['imgurl'] = img_url
response = requests.post(url, headers=headers, data=body)
return response
根據返回的數據格式,將數據按照置信度(trust)展示成一個柱狀圖
關注公眾號 渡碼 回復 “垃圾分類v2” 獲取全部代碼。今天的內容基本上把 Streamlit 講完了,細節的內容大家可以自行參考官方文檔,相信讀完該教程,再看官方文檔就很容易了。