Streamlitで機械学習ダッシュボードを作ってみた

「機械学習モデルを作ったけど、チームに見せる方法がわからない」「データ分析の結果を、わかりやすくビジュアル化したい」そんな悩みを抱えていませんか?

この記事では、Streamlitを使って実際に機械学習ダッシュボードを作る過程を解説します。scikit-learnで学習したモデルを使った予測アプリ、データ可視化、インタラクティブな分析ツールを、実践的なコード例とともに紹介します。

📁 完成版のコードはGitHubで公開中

この記事で作成するダッシュボードの完全なコードは、GitHubリポジトリで公開しています。クローンしてすぐに試せます:

👉 GitHubリポジトリはこちら

# クローンして実行
git clone https://github.com/masvc/streamlit-ml-dashboard.git
cd streamlit-ml-dashboard
pip install -r requirements.txt
streamlit run app.py

目次

今回作るもの: アヤメ分類ダッシュボード

機械学習の入門でよく使われるIris(アヤメ)データセットを使って、以下の機能を持つダッシュボードを作ります。

ダッシュボードの機能:

  • データ探索: データセットの統計情報と可視化
  • モデル学習: パラメータを調整して学習実行
  • 予測機能: 新しいデータで品種を予測
  • モデル評価: 精度や混同行列の表示

ステップ1: 環境準備とデータ読み込み

まず、必要なライブラリをインストールします。

必要なライブラリのインストール

pip install streamlit scikit-learn pandas numpy matplotlib seaborn plotly

基本構造の作成

app.pyを作成し、基本的な構造を実装します。

import streamlit as st
import pandas as pd
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, confusion_matrix
import plotly.express as px
import plotly.graph_objects as go
# ページ設定
st.set_page_config(
    page_title="アヤメ分類ダッシュボード",
    page_icon="🌸",
    layout="wide"
)
# タイトル
st.title('🌸 機械学習ダッシュボード: アヤメ品種分類')
st.markdown('Irisデータセットを使った分類モデルの学習・評価・予測')
# データ読み込み
@st.cache_data
def load_data():
    iris = datasets.load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['species'] = iris.target
    df['species_name'] = df['species'].map({0: 'setosa', 1: 'versicolor', 2: 'virginica'})
    return df, iris
df, iris = load_data()

ステップ2: データ探索機能の実装

サイドバーでセクションを切り替えられるようにし、まずはデータ探索機能を実装します。

# サイドバーでセクション選択
section = st.sidebar.selectbox(
    'セクションを選択',
    ['データ探索', 'モデル学習', '予測', 'モデル評価']
)
# データ探索セクション
if section == 'データ探索':
    st.header('📊 データ探索')
    
    # データ表示
    st.subheader('データセット')
    st.dataframe(df.head(10))
    
    # 基本統計量
    st.subheader('基本統計量')
    st.write(df.describe())
    
    # データの分布を可視化
    st.subheader('データ分布')
    
    col1, col2 = st.columns(2)
    
    with col1:
        # 散布図
        feature_x = st.selectbox('X軸の特徴量', iris.feature_names, index=0)
        feature_y = st.selectbox('Y軸の特徴量', iris.feature_names, index=1)
        
        fig = px.scatter(
            df, 
            x=feature_x, 
            y=feature_y, 
            color='species_name',
            title=f'{feature_x} vs {feature_y}',
            labels={'species_name': '品種'}
        )
        st.plotly_chart(fig, use_container_width=True)
    
    with col2:
        # ヒストグラム
        selected_feature = st.selectbox('特徴量を選択', iris.feature_names)
        
        fig = px.histogram(
            df, 
            x=selected_feature, 
            color='species_name',
            title=f'{selected_feature}の分布',
            labels={'species_name': '品種'}
        )
        st.plotly_chart(fig, use_container_width=True)
    
    # 相関行列
    st.subheader('特徴量の相関')
    corr_matrix = df[iris.feature_names].corr()
    
    fig = go.Figure(data=go.Heatmap(
        z=corr_matrix.values,
        x=corr_matrix.columns,
        y=corr_matrix.columns,
        colorscale='RdBu',
        zmid=0
    ))
    fig.update_layout(title='相関行列')
    st.plotly_chart(fig, use_container_width=True)

ステップ3: モデル学習機能の実装

次に、ユーザーがパラメータを調整してモデルを学習できる機能を実装します。

# モデル学習セクション
elif section == 'モデル学習':
    st.header('🤖 モデル学習')
    st.markdown('ランダムフォレストのパラメータを調整してモデルを学習します')
    
    # パラメータ設定
    col1, col2, col3 = st.columns(3)
    
    with col1:
        n_estimators = st.slider('決定木の数', 10, 200, 100, 10)
    
    with col2:
        max_depth = st.slider('木の最大深さ', 1, 20, 10)
    
    with col3:
        test_size = st.slider('テストデータの割合', 0.1, 0.5, 0.3, 0.05)
    
    # データ分割
    X = df[iris.feature_names]
    y = df['species']
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=test_size, random_state=42
    )
    
    # 学習実行
    if st.button('学習を開始', type='primary'):
        with st.spinner('学習中...'):
            # モデル作成
            model = RandomForestClassifier(
                n_estimators=n_estimators,
                max_depth=max_depth,
                random_state=42
            )
            
            # 学習
            model.fit(X_train, y_train)
            
            # 予測
            y_pred = model.predict(X_test)
            
            # 精度計算
            accuracy = accuracy_score(y_test, y_pred)
            
            # セッションステートに保存
            st.session_state['model'] = model
            st.session_state['accuracy'] = accuracy
            st.session_state['X_test'] = X_test
            st.session_state['y_test'] = y_test
            st.session_state['y_pred'] = y_pred
            
        st.success('学習完了!')
        st.metric('テストデータの精度', f'{accuracy:.2%}')
        
        # 特徴量の重要度
        st.subheader('特徴量の重要度')
        feature_importance = pd.DataFrame({
            '特徴量': iris.feature_names,
            '重要度': model.feature_importances_
        }).sort_values('重要度', ascending=False)
        
        fig = px.bar(
            feature_importance, 
            x='重要度', 
            y='特徴量',
            orientation='h',
            title='特徴量の重要度'
        )
        st.plotly_chart(fig, use_container_width=True)
    
    # データ分割の可視化
    st.subheader('データ分割')
    col1, col2 = st.columns(2)
    with col1:
        st.metric('訓練データ', f'{len(X_train)} サンプル')
    with col2:
        st.metric('テストデータ', f'{len(X_test)} サンプル')

ステップ4: 予測機能の実装

学習したモデルを使って、ユーザーが入力した値から品種を予測する機能を実装します。

# 予測セクション
elif section == '予測':
    st.header('🔮 品種予測')
    
    # モデルが学習済みかチェック
    if 'model' not in st.session_state:
        st.warning('先に「モデル学習」セクションでモデルを学習してください')
    else:
        st.markdown('各特徴量の値を入力して、アヤメの品種を予測します')
        
        # 入力フォーム
        col1, col2 = st.columns(2)
        
        with col1:
            sepal_length = st.number_input(
                'がく片の長さ (cm)',
                min_value=0.0,
                max_value=10.0,
                value=5.0,
                step=0.1
            )
            sepal_width = st.number_input(
                'がく片の幅 (cm)',
                min_value=0.0,
                max_value=10.0,
                value=3.0,
                step=0.1
            )
        
        with col2:
            petal_length = st.number_input(
                '花びらの長さ (cm)',
                min_value=0.0,
                max_value=10.0,
                value=4.0,
                step=0.1
            )
            petal_width = st.number_input(
                '花びらの幅 (cm)',
                min_value=0.0,
                max_value=10.0,
                value=1.5,
                step=0.1
            )
        
        # 予測実行
        if st.button('予測を実行', type='primary'):
            model = st.session_state['model']
            
            # 入力データを作成
            input_data = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
            
            # 予測
            prediction = model.predict(input_data)[0]
            prediction_proba = model.predict_proba(input_data)[0]
            
            # 品種名
            species_names = ['setosa', 'versicolor', 'virginica']
            predicted_species = species_names[prediction]
            
            # 結果表示
            st.success(f'予測結果: **{predicted_species}**')
            
            # 確信度を表示
            st.subheader('各品種の確率')
            proba_df = pd.DataFrame({
                '品種': species_names,
                '確率': prediction_proba
            })
            
            fig = px.bar(
                proba_df,
                x='品種',
                y='確率',
                title='予測確率',
                range_y=[0, 1]
            )
            st.plotly_chart(fig, use_container_width=True)
            
            # 入力値の可視化
            st.subheader('入力した特徴量')
            input_df = pd.DataFrame({
                '特徴量': iris.feature_names,
                '値': [sepal_length, sepal_width, petal_length, petal_width]
            })
            
            fig = px.bar(
                input_df,
                x='特徴量',
                y='値',
                title='入力データ'
            )
            st.plotly_chart(fig, use_container_width=True)

ステップ5: モデル評価機能の実装

最後に、学習したモデルの性能を詳細に評価する機能を実装します。

# モデル評価セクション
elif section == 'モデル評価':
    st.header('📈 モデル評価')
    
    # モデルが学習済みかチェック
    if 'model' not in st.session_state:
        st.warning('先に「モデル学習」セクションでモデルを学習してください')
    else:
        accuracy = st.session_state['accuracy']
        y_test = st.session_state['y_test']
        y_pred = st.session_state['y_pred']
        
        # 精度表示
        st.metric('テストデータの精度', f'{accuracy:.2%}')
        
        # 混同行列
        st.subheader('混同行列')
        cm = confusion_matrix(y_test, y_pred)
        
        species_names = ['setosa', 'versicolor', 'virginica']
        
        fig = go.Figure(data=go.Heatmap(
            z=cm,
            x=species_names,
            y=species_names,
            text=cm,
            texttemplate='%{text}',
            colorscale='Blues'
        ))
        fig.update_layout(
            title='混同行列',
            xaxis_title='予測ラベル',
            yaxis_title='真のラベル'
        )
        st.plotly_chart(fig, use_container_width=True)
        
        # 品種ごとの精度
        st.subheader('品種ごとの詳細')
        
        from sklearn.metrics import classification_report
        report = classification_report(y_test, y_pred, target_names=species_names, output_dict=True)
        
        report_df = pd.DataFrame(report).transpose()[:-3]  # micro/macro avgを除外
        report_df = report_df.reset_index()
        report_df.columns = ['品種', '適合率', '再現率', 'F1スコア', 'サポート']
        
        st.dataframe(report_df, use_container_width=True)
        
        # 予測結果の分布
        st.subheader('予測結果の分布')
        
        comparison_df = pd.DataFrame({
            '真のラベル': [species_names[i] for i in y_test],
            '予測ラベル': [species_names[i] for i in y_pred]
        })
        
        fig = px.scatter(
            comparison_df,
            x='真のラベル',
            y='予測ラベル',
            title='予測結果の比較'
        )
        st.plotly_chart(fig, use_container_width=True)

完成したダッシュボードの使い方

ダッシュボードが完成しました!以下の手順で使います。

1. アプリを起動

streamlit run app.py

2. データ探索

サイドバーで「データ探索」を選択し、データセットの統計情報や分布を確認します。散布図やヒストグラムで、各品種の特徴を視覚的に理解できます。

3. モデル学習

「モデル学習」セクションで、パラメータを調整してモデルを学習します。決定木の数や深さを変更して、精度の変化を確認できます。

4. 予測実行

「予測」セクションで、新しいアヤメのデータ(がく片・花びらのサイズ)を入力して品種を予測します。確信度もグラフで表示されます。

5. モデル評価

「モデル評価」セクションで、混同行列や品種ごとの精度を確認します。モデルの性能を詳しく分析できます。

応用: より高度な機能の追加

基本的なダッシュボードができたら、以下の機能を追加してみましょう。

1. 複数のアルゴリズム比較

from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
# アルゴリズム選択
algorithm = st.selectbox(
    'アルゴリズムを選択',
    ['ランダムフォレスト', 'SVM', 'k-NN', '決定木']
)
# モデル作成
if algorithm == 'ランダムフォレスト':
    model = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth)
elif algorithm == 'SVM':
    model = SVC(probability=True)
elif algorithm == 'k-NN':
    n_neighbors = st.slider('近傍数', 1, 20, 5)
    model = KNeighborsClassifier(n_neighbors=n_neighbors)
else:
    model = DecisionTreeClassifier(max_depth=max_depth)

2. モデルの保存・読み込み

import pickle
# モデル保存
if st.button('モデルを保存'):
    with open('model.pkl', 'wb') as f:
        pickle.dump(model, f)
    st.success('モデルを保存しました')
# モデル読み込み
uploaded_file = st.file_uploader('モデルファイルを選択', type=['pkl'])
if uploaded_file is not None:
    model = pickle.load(uploaded_file)
    st.success('モデルを読み込みました')

3. カスタムデータセットのアップロード

# CSVアップロード機能
uploaded_file = st.file_uploader('CSVファイルをアップロード', type=['csv'])
if uploaded_file is not None:
    # データ読み込み
    custom_df = pd.read_csv(uploaded_file)
    
    # ターゲット列を選択
    target_column = st.selectbox('ターゲット列を選択', custom_df.columns)
    feature_columns = st.multiselect('特徴量を選択', 
                                     [col for col in custom_df.columns if col != target_column])
    
    if feature_columns:
        X = custom_df[feature_columns]
        y = custom_df[target_column]
        
        # 以降の処理は同じ

パフォーマンス最適化のコツ

ダッシュボードが重くなった場合の最適化方法を紹介します。

1. キャッシュの活用

# データ読み込みをキャッシュ
@st.cache_data
def load_data():
    # 重い処理
    return data
# モデル学習をキャッシュ
@st.cache_resource
def train_model(params):
    # モデル学習
    return model

2. 大量データの処理

# データをサンプリング
if len(df) > 10000:
    df_sample = df.sample(n=10000, random_state=42)
    st.warning('データが大きいため、10,000サンプルを表示しています')
else:
    df_sample = df
# Plotlyで軽量化
fig.update_traces(marker_size=3)  # マーカーサイズを小さく

機械学習開発をさらに効率化する関連記事

Streamlitで機械学習ダッシュボードを作成したら、他のAI開発ツールやデータ分析ツールも活用して、さらに開発効率を向上させましょう:

AI開発・機械学習

データ分析・可視化

Python開発支援

ノーコード・AI統合

まとめ: Streamlitで作る実践的な機械学習ダッシュボード

この記事では、Streamlitを使った機械学習ダッシュボードの作り方を、実践的なコード例とともに解説しました。

実装した主な機能:

  • データ探索: 統計情報、散布図、ヒストグラム、相関行列
  • モデル学習: パラメータ調整、学習実行、特徴量の重要度表示
  • 予測機能: ユーザー入力による予測、確信度の可視化
  • モデル評価: 精度、混同行列、品種ごとの詳細評価

開発のポイント:

  • セッションステート: st.session_stateでモデルやデータを保持
  • キャッシュ: @st.cache_dataで重い処理を最適化
  • レイアウト: st.columns()で見やすい配置
  • Plotly: インタラクティブなグラフで可視化

応用の方向性:

  • アルゴリズム比較: 複数のモデルを試して最適なものを選択
  • モデル管理: 学習したモデルの保存・読み込み
  • カスタムデータ: ユーザーがCSVをアップロードして分析
  • 高度な可視化: ROC曲線、学習曲線、特徴量の分布

Streamlitを使えば、機械学習モデルを誰でも使えるWebアプリに変換できます。データサイエンティストが作ったモデルを、エンジニアでない人も簡単に試せるようになるため、チーム全体でモデルの評価や改善ができます。

まずは今回のコードをベースに、自分のプロジェクトに合わせてカスタマイズしてみましょう。Streamlitなら、数時間で本格的なダッシュボードが完成します!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次