Streamlitを用いた音響信号処理ダッシュボードの開発(Tokyo BISH Bash #03発表資料)

自己紹介およびPyData Osakaの紹介

  • 別スライドで説明します

本日の概要

  • Streamlitの紹介
  • 実例を踏まえた使用方法の紹介
  • Herokuへのデプロイ方法の紹介
  • その他

Streamlitとは

  • Webブラウザで動作するダッシュボードを作成するためのライブラリ
    • 複数のコンポーネントが連携して動作するもの
  • Pythonのみで記述が可能
    • plotly/dashと比較されることが多い

どんな場面で必要か

  • 実装したアルゴリズムを他者に試してもらいたい
    • Webフロントエンドがあると便利
  • パラメータ変更による試行錯誤が必要な場面
    • 調整ツールとして使用

実例

  • 今回作成したダッシュボードを簡単に紹介

Dashとの違い

  • Towards Data Scienceの記事
    • Dash
      • プロダクション/エンタープライズ環境での実行に主眼
    • Streamlit
      • ラピッドプロトタイピングに主眼

image.png

Streamlitの魅力

  • 手軽にフロントエンドが構築可能
    • javascript不要、スクリプトライクな記述
    • 各種pythonライブラリにGUIを付与
  • 豊富な描画ライブラリ
    • matplotlib, plotly, altair, ...
    • Dashはplotlyを前提

本発表におけるバージョン

基本機能の紹介

導入方法

  • pip install streamlit

実行方法

In [2]:
%%writefile app.py
import streamlit as st

st.write("# Hello, Streamlit!")
Overwriting app.py
In [3]:
!streamlit run app.py
  You can now view your Streamlit app in your browser.

  Network URL: http://172.17.0.2:8501
  External URL: http://49.251.189.62:8501

^C
  Stopping...

image.png

  • ノートブックが固まるため以降はターミナル上からstreamlitコマンドを実行

アプリ更新時の反映が容易

  • ファイルの上書きを検知しRerunボタンを自動表示

image.png

In [4]:
%%writefile app.py
import streamlit as st

st.write("# Hello, Streamlit!")
st.write("# Where is Rerun button?")
Overwriting app.py

image.png

スクリプトエラーもブラウザ上に表示

  • デバッグも容易
In [5]:
%%writefile app.py
import streamlit as st

st.write("# Hello, Streamlit!")
raise ValueError
Overwriting app.py

image.png

st.write

  • 引数に与えたオブジェクトを画面に表示
  • markdownはHTMLに変換して表示
  • listやdictは階層表示
  • ndarrayやpandasのDataFrameはテーブルとして表示
In [6]:
%%writefile app.py
import numpy as np
import pandas as pd
import streamlit as st

st.write([1, 2, 3])
st.write({"hello": "world!"})
st.write(np.arange(10).reshape(1,10))
st.write(pd.DataFrame(np.random.randn(10, 4), columns=["1", "2", "3", "4"]))
Overwriting app.py

image.png

本日紹介するダッシュボード

ブランチ

  • master
  • heroku
    • Herokuへのアップロードに必要なファイルを格納
  • espnet2
    • 3.6.x系で動作させるため別ブランチ
    • Steramlitのバージョンは0.66.0

github上のstreamlitアプリ

  • 一般的には直接実行可能
    • streamlit run https://github.com/user/repos.git/master/app.py
  • 今回のアプリはimportの都合でエラーが生じる

窓関数ビューワの構築

  • scipy.signalを使用
  • Streamlitによるアプリ構築をライブコーディング的に実施

image.png

窓関数の選択インタフェース

  • サイドバーにselectboxで表示
  • サイドバー上への配置
    • st.sidebar.xxx
    • st.xxxと本体に配置できるウィジェットは何でも配置可能

st.selectbox

st.selectbox(ラベル名, 選択肢のリスト, デフォルト項目のインデックス)

image.png

In [7]:
%%writefile app.py
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]
Overwriting app.py
  • %%writefile -a app.pyapp.pyに追記
In [8]:
%%writefile -a app.py

win_name = st.sidebar.selectbox("window", windows, 4)
st.write(win_name)
Appending to app.py
  • この時点の見た目

image.png

窓関数の窓長およびFFT長の取得

  • 同様にst.selectboxで2の累乗の値を取得
In [9]:
%%writefile -a app.py
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_name, Nx, nfft)
Appending to app.py
  • この時点での見た目

image.png

窓関数の取得および描画

  • 窓関数の取得
    • scipy.signal.get_window
    • scipy.fft.fftで周波数分析
  • 描画
    • matplotlibをひとまず使用
  • 必要なライブラリをimport
In [10]:
%%writefile -a app.py
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft
Appending to app.py
  • 窓関数を取得し周波数特性を計算
In [11]:
%%writefile -a app.py
eps = 1.e-12
win = sg.get_window(win_name, Nx)
W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
W = fft.fftshift(W)
Appending to app.py
  • matplotlibを用いて描画
In [12]:
%%writefile -a app.py
fig, axes = plt.subplots(2, 1)
axes[0].plot(win)
axes[1].plot(W)
st.pyplot(fig)
Appending to app.py

st.pyplot

  • matplotlibのfigureを描画
    • st.pyplot(fig)
  • 使い慣れたmatplotlibを使える
    • が、javascriptの恩恵が得られない
  • この時点での見た目

image.png

streamlitの描画ライブラリ

  • st.line_chart, st.area_chart, st.bar_chart
    • st.altair_chartのsyntax sugar
    • 手軽に使える反面細かい設定は不可能
  • その他vega, plotly, bokehなども使用可能
In [13]:
%%writefile -a app.py
st.line_chart(win)
st.line_chart(W)
Appending to app.py
  • st.line_chartの見た目

image.png

この時点でのファイルの内容

  • 通常の実行スクリプトに近い
  • 手軽にフロントエンドの作成が可能
In [14]:
!cat app.py | pygmentize
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]

win_name = st.sidebar.selectbox("window", windows, 4)
st.write(win_name)
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_name, Nx, nfft)
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft
eps = 1.e-12
win = sg.get_window(win_name, Nx)
W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
W = fft.fftshift(W)
fig, axes = plt.subplots(2, 1)
axes[0].plot(win)
axes[1].plot(W)
st.pyplot(fig)
st.line_chart(win)
st.line_chart(W)

複数の窓関数を同時に描画したい

  • st.multiselect(ラベル名, 選択肢のリスト, デフォルト項目の要素のリスト)
    • 複数の選択項目をリストで返す
    • 第三引数は選択肢中に存在する要素のリスト