Skip to content

Streamlitとキャッシュの入門

Streamlitは、機械学習とデータサイエンスプロジェクトのためのインタラクティブで使いやすいWebアプリケーションを作成するためのオープンソースのPythonライブラリです。フロントエンド開発のスキルは不要で、データサイエンティストやエンジニアがデータスクリプトを共有可能なWebアプリに変換するのに役立ちます。

一方、キャッシュは、データを一時的なストレージ領域であるキャッシュに格納してデータの取得を高速化するための技術です。Streamlitの文脈では、キャッシュは大規模なデータセットや複雑な計算を扱う場合に特にアプリのパフォーマンスを大幅に向上させることができます。

Streamlitとは?

Streamlitは、データサイエンスと機械学習の分野で画期的な存在です。Streamlitを使えば、数行のPythonコードで美しいデータ駆動型のWebアプリケーションを作成することができます。大規模なデータセットや複雑な計算を扱うことができるため、データサイエンティストや機械学習エンジニアにとって強力なツールとなっています。

キャッシュとは?

キャッシュは、高速なデータストレージ層であるキャッシュにデータを一時的に格納するための技術です。キャッシュの主な目的は、基礎となる遅いストレージ層へのアクセスの必要性を減らすことで、データの取得速度を高速化することです。データがリクエストされると、システムはまずキャッシュをチェックします。データが見つかった場合、即座に返されます。見つからない場合は、システムはデータを主ストレージから取得し、返し、将来のリクエストのためにキャッシュに保存します。

Streamlitでキャッシュを使用する理由

Streamlitでは、アプリの中でインタラクションが発生するたびに、スクリプト全体が上から下まで再実行されます。このモデルはプログラミングモデルをシンプルにする一方、効率性に欠ける場合があります。例えば、スクリプトに大規模なデータセットをロードする関数や時間のかかる計算を行う関数が含まれている場合、スクリプトが再実行されるたびにその関数を実行したくありません。ここでキャッシュが重要な役割を果たします。Streamlitのキャッシュを使用することで、特定の関数が入力が変更された場合にのみ再実行されることが保証されます。

Streamlitキャッシュの利点

Streamlitのキャッシュは、次のような利点があります:

  • パフォーマンスの向上: キャッシュに高価な関数呼び出しの結果を格納することで、Streamlitアプリの動作を劇的に高速化することができます。これは特に、大規模なデータセットや複雑な機械学習モデルを扱う場合に有益です。データの読み込みや計算に時間がかかる場合でも、キャッシュを使用することで処理速度を向上させることができます。

  • 効率の向上: キャッシュを使用することで不必要な計算を回避することができます。以前に同じ引数で関数が呼び出された場合、Streamlitは再実行するのではなく、キャッシュから結果を取得することができます。

  • 利用体験の向上: より速い読み込み時間とより応答性のあるアプリケーションは、利用体験を向上させます。キャッシュを使用することで、ユーザーはアプリとの対話ごとにデータの読み込みや計算の完了を待つ必要がありません。

Streamlitのキャッシュメカニズムを使用することで、開発者は関数の計算結果をキャッシュに保存し、より高速なデータの取得と優れたアプリのパフォーマンスを実現することができます。このセクションでは、Streamlitキャッシュの仕組み、利点、およびそれがもたらす課題について詳しく説明します。

Streamlitキャッシュの課題

キャッシュは強力な機能ですが、いくつかの課題もあります:

  • キャッシュ管理: キャッシュの管理は難しい場合があります。キャッシュが多すぎるメモリを消費しないようにし、キャッシュされた関数の入力が変更された場合に正しく無効化されることを確認する必要があります。

  • デバッグ: キャッシュを使用すると、デバッグがより複雑になる場合があります。関数の出力がキャッシュから読み取られる場合、関数内のプリント文や副作用は発生しないため、デバッグがより困難になる可能性があります。

  • キャッシュの永続性: デフォルトでは、StreamlitのキャッシュはStreamlitサーバーを再実行するたびにクリアされます。複数回の実行で永続的なキャッシュが必要な場合は、高度なキャッシュオプションや外部のキャッシングソリューションを使用する必要があります。

Streamlitキャッシュの動作原理

Streamlitでは、関数定義の前に@st.cacheというデコレータを追加することでキャッシュを有効にすることができます。@st.cacheデコレータを使って関数をマークすると、Streamlitはその関数が以前に同じ入力で呼び出されたかどうかをチェックします。もしそうであれば、Streamlitは関数の出力をキャッシュから読み取ります。そうでない場合は、関数を実行し、結果をキャッシュに保存して結果を返します。

以下は、Streamlitキャッシュの使用例です:

@st.cache
def load_data():
    data = pd.read_csv('large_dataset.csv')
    return data

この例では、load_data関数はスクリプトが何度再実行されても一度だけ実行されます。結果はキャッシュに保存され、将来の使用のために時間と計算資源を節約します。

Streamlitキャッシュのメカニズム:@st.cache、st.cache_data、およびst.cache_resource

Streamlitには、ニーズに応じて使用できる複数のキャッシュメカニズムが用意されています。このセクションでは、@st.cachest.cache_data、およびst.cache_resourceの違いについて説明します。

@st.cache

@st.cacheは、関数定義の前に追加できるデコレータで、キャッシュを有効にするためのものです。@st.cacheでマークされた関数が呼び出されると、Streamlitは以前に同じ入力で関数が呼び出されたかどうかをチェックします。もしそうであれば、Streamlitは関数の出力をキャッシュから読み取ります。そうでない場合は、関数を実行し、結果をキャッシュに保存して結果を返します。

@st.cacheの使用例を以下に示します: @st.cache def load_data(): data = pd.read_csv('large_dataset.csv') return data


この例では、スクリプトを何度実行しても、`load_data` 関数は1回だけ実行されます。結果はキャッシュに保存され、将来の使用のために利用できます。

#### st.cache_data

`st.cache_data` は、データフレームの変換、データベースのクエリ、機械学習の推論など、データを返す関数をキャッシュするためのデコレータです。キャッシュされたオブジェクトは、「pickle」として保存されます。これは、キャッシュされた関数の戻り値が「picklable」である必要があることを意味します。キャッシュされた関数の呼び出し元は、キャッシュされたデータのそれぞれに独自のコピーを取得します。関数のキャッシュを `func.clear()` でクリアしたり、キャッシュ全体を `st.cache_data.clear()` でクリアしたりすることができます。グローバルリソースをキャッシュする場合は、代わりに `st.cache_resource` を使用することを検討してください。

関数のシグネチャは次のようになります。

```python
st.cache_data(func=None, *, ttl, max_entries, show_spinner, persist, experimental_allow_widgets, hash_funcs=None)

パラメータには、キャッシュする関数、キャッシュ内のエントリを保持する最大時間、キャッシュ内に保持するエントリの最大数、スピナーを有効にするかどうか、キャッシュされたデータを永続化する場所、キャッシュされた関数でウィジェットの使用を許可するかどうか、およびハッシュ関数の型または完全修飾名へのマッピングが含まれます。

st.cache_resource

st.cache_resource は、データベース接続や機械学習モデルなどのグローバルリソースを返す関数をキャッシュするためのデコレータです。キャッシュされたオブジェクトは、すべてのユーザー、セッション、および再実行間で共有されます。複数のスレッドから同時にアクセスされる可能性があるため、スレッドセーフである必要があります。スレッドセーフが問題になる場合は、代わりにセッションごとにリソースを格納するために st.session_state を使用することを検討してください。関数のキャッシュを func.clear() でクリアしたり、キャッシュ全体を st.cache_resource.clear() でクリアしたりすることができます。

関数のシグネチャは次のようになります。

st.cache_resource(func, *, ttl, max_entries, show_spinner, validate, experimental_allow_widgets, hash_funcs=None)

パラメータには、キャッシュされたリソースを作成する関数、キャッシュ内のエントリを保持する最大時間、キャッシュ内に保持するエントリの最大数、スピナーを有効にするかどうか、キャッシュされたデータのオプションの検証関数、キャッシュされた関数でウィジェットの使用を許可するかどうか、およびハッシュ関数の型または完全修飾名へのマッピングが含まれます。

Streamlit キャッシュの実践: ユースケースと例

Streamlit キャッシュは、データアプリのパフォーマンスを向上させるためにさまざまなシナリオで使用することができます。このセクションでは、一部の一般的なユースケースを探り、Streamlit でキャッシュを実装する方法の例を示します。

機械学習モデルのキャッシュ

大きなモデルの読み込みは時間がかかることがあります。モデルの読み込みプロセスをキャッシュすることで、Streamlit アプリを大幅に高速化することができます。以下は例です。

@st.cache(allow_output_mutation=True)
def load_model():
    model = load_your_model_here()  # 自分のモデルの読み込みコードに置き換える
    return model

この例では、allow_output_mutation=True オプションを使用しています。これは、機械学習モデルには非ハッシュ可能な型が含まれることが多いためであり、これは Streamlit のデフォルトのキャッシュと互換性がありません。

データの可視化のキャッシュ

データの可視化は、特に大規模なデータセットの場合、計算量が多いプロセスです。データの可視化関数の結果をキャッシュすることで、Streamlit アプリをより応答性の高いものにすることができます。以下は例です。

@st.cache
def create_plot(data):
    fig = perform_expensive_plotting_here(data)  # 自分のプロットコードに置き換える
    return fig

この例では、create_plot 関数は data の入力が変更された場合にのみ再実行され、時間と計算リソースを節約します。

API コールのキャッシュ

Streamlit アプリで API コールを行う場合、キャッシュを使用すると API のレート制限を回避し、API コールの回数を減らしてアプリのパフォーマンスを向上させることができます。以下は例です。

@st.cache
def fetch_data(api_url):
    response = requests.get(api_url)
    return response.json()

この例では、fetch_data 関数は新しい api_url で呼び出された場合にのみ API コールを行い、それ以外の場合はキャッシュされたレスポンスを返します。

Web スクレイピング結果のキャッシュ

Web スクレイピングは時間がかかる場合があり、同じウェブサイトの反復的なスクレイピングは IP のブロックにつながる可能性があります。Web スクレイピング関数の結果をキャッシュすることで、不要なネットワークリクエストを避けることができます。以下は例です。

@st.cache
def scrape_website(url):
    data = perform_web_scraping_here(url)  # 自分の Web スクレイピングコードに置き換える
   
    return data

この例では、scrape_website 関数は新しい url で呼び出された場合にのみウェブサイトをスクレイピングし、それ以外の場合はキャッシュされたデータを返します。

Streamlit キャッシュのベストプラクティス

Streamlit キャッシュを使用する際には、次のいくつかのベストプラクティスに注意してください。

  • @st.cache を控えめに使用する: キャッシュはアプリのパフォーマンスを大幅に向上させる一方で、注意して使用しないと多くのメモリを消費する可能性があります。@st.cache は、高コストな計算を行う関数や大きなデータセットを読み込む関数にのみ使用してください。

  • キャッシュされた関数で副作用を避ける: @st.cache でマークされた関数は副作用を持っていてはいけません。副作用とは、関数が環境を変更すること、つまりグローバル変数を変更したりファイルに書き込んだりすることです。副作用を持つ関数がキャッシュされると、副作用は関数が最初に呼び出されたときにのみ発生します。

  • 可変引数に注意する: キャッシュされた関数がリストや辞書などの可変引数を取る場合、Streamlit はこれらの引数の初期値を使用してキャッシュされた結果を識別します。関数を呼び出した後に引数を変更すると、Streamlit は変更を認識せずキャッシュされた結果を返します。

  • allow_output_mutation=Trueを使用してハッシュ不可な出力を許可する: デフォルトでは、Streamlitのキャッシュは関数の出力がハッシュ可能であることを要求します。機械学習モデルなどのハッシュ不可な出力を返す場合、allow_output_mutation=Trueオプションを使用してこの要件を回避することができます。

ボーナス: Streamlitのインタラクティブウィジェット

Streamlitのインタラクティブウィジェットは、他のデータ可視化ツールとは異なる特徴的な機能です。これらのウィジェットを使用すると、ユーザーはStreamlitアプリと対話し、アプリの動作を制御し、リアルタイムでデータと対話することができます。

スライダー

スライダーウィジェットは、ユーザーが水平スケールに沿ってハンドルをスライドすることで値または範囲の値を選択できるようにします。これは、ユーザーがアプリ内の数値パラメータを制御できるようにするために役立ちます。

age = st.slider('年齢は?', 0, 130, 25)
st.write("私は、", age, '歳です。')

この例では、スライダーを使用して、ユーザーは0から130の範囲で年齢を選択できます。デフォルトの値は25です。

チェックボックス

チェックボックスウィジェットを使用すると、ユーザーはバイナリオプションをオンまたはオフに切り替えることができます。これにより、ユーザーはアプリ内の特定の機能を有効または無効にすることができます。

agree = st.checkbox('同意します')
if agree:
    st.write('素晴らしいです!')

この例では、チェックボックスを使用して、ユーザーは特定の文に同意するかどうかを選択できます。ユーザーがチェックボックスにチェックを入れると、アプリはメッセージ「素晴らしいです!」を表示します。

テキスト入力

テキスト入力ウィジェットを使用すると、ユーザーはテキストの文字列を入力できます。これにより、ユーザーはアプリにテキストデータを入力できるようになります。

title = st.text_input('映画のタイトル', 'ライフ・オブ・ブライアン')
st.write('現在の映画のタイトルは', title, 'です。')

この例では、テキスト入力を使用して、ユーザーは映画のタイトルを入力できます。

あなたはStreamlitアプリをTableauに変換する素晴らしいデータ分析&データ可視化ツール「PyGWalker」を聞いたことがありますか?

PyGWalker (opens in a new tab)は、あなたのStreamlitアプリにTableauのようなUIを簡単に埋め込むためのPythonライブラリです。この強力なデータ可視化Pythonライブラリで、あなたのStreamlitアプリを強化するための詳細な手順を紹介した、Coding is Fun (opens in a new tab)の素晴らしいビデオをチェックしてみてください!


Svenと彼の素晴らしい貢献 (opens in a new tab)に感謝します!

さらに、他のPyGWalkerの例はPyGWalker GitHubページ (opens in a new tab)をチェックしてください。

結論

Streamlitのキャッシュメカニズムは、不要な計算を避けることでアプリのパフォーマンスを大幅に向上させる強力なツールです。Streamlitのキャッシュの動作と効果的な使用方法を理解することで、より効率的で応答性の高いアプリを作成することができます。データの変換、機械学習モデル、API呼び出しのキャッシングに関わらず、Streamlitのキャッシュを使用することで、よりスムーズで対話的なユーザーエクスペリエンスを提供できます。

ただし、キャッシュは常に適切な解決策ではありませんので、トレードオフを考慮し、キャッシュが予期しない動作を引き起こさずにアプリのパフォーマンスを向上させているかを十分にテストする必要があります。

スライダー、チェックボックス、テキスト入力などのStreamlitのインタラクティブウィジェットは、ユーザーがアプリと対話し、その動作を制御する方法を提供します。これらのウィジェットをStreamlitのキャッシュメカニズムと組み合わせることで、強力で応答性の高いデータアプリケーションを作成することができます。

最後に、Streamlitのシンプルさ、柔軟性、データと機械学習への焦点が、データアプリケーションを構築するための素晴らしい選択肢になります。キャッシュメカニズムとインタラクティブウィジェットを活用することで、パワフルで効率的かつ対話的なアプリを作成できます。

あなたはStreamlitアプリをTableauに変換する素晴らしいデータ分析&データ可視化ツール「PyGWalker」を聞いたことがありますか?

PyGWalker (opens in a new tab)は、あなたのStreamlitアプリにTableauのようなUIを簡単に埋め込むためのPythonライブラリです。

PyGWalker for Data visualization in Streamlit (opens in a new tab)

よくある質問

Streamlitのキャッシュはどのように動作するのですか?

Streamlitのキャッシュメカニズムは、関数の呼び出し結果をキャッシュに保存することで動作します。@st.cacheでデコレートされた関数が呼び出されると、Streamlitは以前に同じ入力で関数が呼び出されたかどうかをチェックします。もし以前に呼び出されたことがある場合、Streamlitは関数の実行をスキップし、キャッシュされた結果を使用することができます。これにより、データの読み込みや機械学習モデルの実行などの高価な計算を回避することができ、アプリのパフォーマンスを大幅に向上させることができます。

Streamlitキャッシュの制限は何ですか?

Streamlitのキャッシュメカニズムは非常に強力ですが、いくつかの制限があります。例えば、キャッシュされた関数の戻り値はpickle可能でなければなりません。これは、Pythonのpickleモジュールを使用してシリアライズおよびデシリアライズできることを意味します。つまり、すべてのPythonオブジェクトをキャッシュされた関数から返すことはできません。また、キャッシュされた関数は副作用を持ってはいけません。副作用は関数が最初に呼び出されたときにのみ実行されます。

Streamlitのキャッシュをクリアする方法は?

Streamlitでは、st.cache.clear()関数を使用してキャッシュをクリアすることができます。これにより、キャッシュからすべてのエントリが削除されます。特定の関数のキャッシュをクリアしたい場合は、キャッシュされた関数をfunc.clear()メソッドで使用します。ここで、funcはキャッシュされた関数です。

Streamlitのキャッシュはどこにありますか? Streamlitのキャッシュはデフォルトではメモリに保存されます。つまり、Streamlitアプリが再起動されるたびにキャッシュがクリアされます。しかし、@st.cacheデコレータのpersistパラメータをTrueに設定することで、Streamlitをディスク上のキャッシュを永続化するように構成することができます。