PyCaretとStreamlitでAutoMLのGUIツールをさくっと作ってみる 

# PyCaretとStreamlitについて
[PyCaret](https://pycaret.org/)はAutoML(自動機械学習)のライブラリです。
[Streamlit](https://www.streamlit.io/)はデータ分析や可視化に優れたWebフレームワークです。
それぞれとても便利なライブラリですが、PyCaretはIPython Notebookを前提にしているところがあるので、StreamlitでPyCaretを使おうとするとうまくいきません。
が、StreamlitでPyCaret使いたいので、無理やり使ってみます。

# インストールする

PyCaretは依存するライブラリが多めです。インストールに詰まったらエラーメッセージを見ながら対処してください。以降はインストールできた前提で進めていきます。

```
pip install pycaret
pip install streamlit
```

# まずはPyCaretを試してみる

PyCaretを使うとJupyter Notebook等で簡単に機械学習のモデル構築を自動化できます。
試しにサンプルデータを読み込みます。

```python
from pycaret.datasets import get_data
data = get_data('boston')
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>crim</th>
      <th>zn</th>
      <th>indus</th>
      <th>chas</th>
      <th>nox</th>
      <th>rm</th>
      <th>age</th>
      <th>dis</th>
      <th>rad</th>
      <th>tax</th>
      <th>ptratio</th>
      <th>black</th>
      <th>lstat</th>
      <th>medv</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.00632</td>
      <td>18.0</td>
      <td>2.31</td>
      <td>0</td>
      <td>0.538</td>
      <td>6.575</td>
      <td>65.2</td>
      <td>4.0900</td>
      <td>1</td>
      <td>296</td>
      <td>15.3</td>
      <td>396.90</td>
      <td>4.98</td>
      <td>24.0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.02731</td>
      <td>0.0</td>
      <td>7.07</td>
      <td>0</td>
      <td>0.469</td>
      <td>6.421</td>
      <td>78.9</td>
      <td>4.9671</td>
      <td>2</td>
      <td>242</td>
      <td>17.8</td>
      <td>396.90</td>
      <td>9.14</td>
      <td>21.6</td>
    </tr>
    <tr>
      <th>2</th>
      <td>0.02729</td>
      <td>0.0</td>
      <td>7.07</td>
      <td>0</td>
      <td>0.469</td>
      <td>7.185</td>
      <td>61.1</td>
      <td>4.9671</td>
      <td>2</td>
      <td>242</td>
      <td>17.8</td>
      <td>392.83</td>
      <td>4.03</td>
      <td>34.7</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.03237</td>
      <td>0.0</td>
      <td>2.18</td>
      <td>0</td>
      <td>0.458</td>
      <td>6.998</td>
      <td>45.8</td>
      <td>6.0622</td>
      <td>3</td>
      <td>222</td>
      <td>18.7</td>
      <td>394.63</td>
      <td>2.94</td>
      <td>33.4</td>
    </tr>
    <tr>
      <th>4</th>
      <td>0.06905</td>
      <td>0.0</td>
      <td>2.18</td>
      <td>0</td>
      <td>0.458</td>
      <td>7.147</td>
      <td>54.2</td>
      <td>6.0622</td>
      <td>3</td>
      <td>222</td>
      <td>18.7</td>
      <td>396.90</td>
      <td>5.33</td>
      <td>36.2</td>
    </tr>
  </tbody>
</table>
</div>

回帰か分類かでimportするソースが変わります。

```python
from pycaret.regression import *  # 回帰の場合
#from pycaret.classification import *  # 分類の場合
```

セットアップ時にデータの型を確認します。

```python
exp = setup(data, target='medv', session_id=1234)
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>Feature_Type</th>
      <th>Data Type</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>crim</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>zn</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>indus</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>chas</th>
      <td>object</td>
      <td>Categorical</td>
    </tr>
    <tr>
      <th>nox</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>rm</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>age</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>dis</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>rad</th>
      <td>object</td>
      <td>Categorical</td>
    </tr>
    <tr>
      <th>tax</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>ptratio</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>black</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>lstat</th>
      <td>float32</td>
      <td>Numeric</td>
    </tr>
    <tr>
      <th>medv</th>
      <td>float32</td>
      <td>Label</td>
    </tr>
  </tbody>
</table>
</div>

モデル比較はこの1行だけです。

```python
best = compare_models(n_select=6)
best
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
<th></th>
        <th >Model</th> 
       <th >MAE</th> 
       <th >MSE</th>
        <th >RMSE</th> 
       <th  >R2</th> 
       <th >RMSLE</th>
        <th >MAPE</th>
        <th >TT (Sec)</th>
    </tr>
  </thead>
  <tbody>
               <tr>
                        <th >catboost</th>
                        <td>CatBoost Regressor</td>
                        <td>2.1024</td>
                        <td>9.6111</td>
                        <td>3.0073</td>
                        <td>0.8741</td>
                        <td>0.1366</td>
                        <td>0.1054</td>
                        <td >1.0370</td>
            </tr>
            <tr>
                        <th >et</th>
                        <td>Extra Trees Regressor</td>
                        <td >2.1868</td>
                       <td >10.9424</td>
                        <td >.1993</td>
                        <td >.8530</td>
                      <td >0.1372</td>
                        <td >0.1078</td>
                       <td >0.0730</td>
            </tr>
            <tr>
                        <th >gbr</th>
                        <td >Gradient Boosting Regressor</td>
                        <td >2.3409</td>
                       <td >11.8092</td>
                        <td >3.2789</td>
                      <td >0.8503</td>
                       <td >0.1510</td>
                       <td >0.1202</td>
                       <td >0.0290</td>
            </tr>
            <tr>
                        <th >rf</th>
                        <td >Random Forest Regressor</td>
                       <td > >2.4101</td>
                      <td >12.4452</td>
                       <td >3.3944</td>
                     <td >0.8459</td>
                    <td >0.1499</td>
                       <td >0.1200</td>
                        <td >0.0820</td>
            </tr>
            <tr>
                        <th >xgboost</th>
                    <td >Extreme Gradient Boosting</td>
                  <td >2.4095</td>
                    <td >12.9185</td>
                        <td >3.4328</td>
                       <td >0.8375</td>
                        <td >0.1545</td>
                        <td >0.1224</td>
                    <td >0.3090</td>
            </tr>
            <tr>
                        <th >lightgbm</th>
                     <td >Light Gradient Boosting Machine</td>
                        <td >2.4942</td>
                     <td >13.9406</td>
                      <td >3.5930</td>
                     <td >0.8193</td>
                      <td > 0.1553</td>
                        <td >0.1222</td>
                        <td >0.2190</td>
            </tr>
  </tbody>
</table>
</div>

モデルをpickle形式で書き出します。

```python
model = create_model('rf')
final = finalize_model(model)
```

予測も１行でできます。(ここでは学習データで予測しています)

```python
predictions = predict_model(final, data=boston_data.iloc[:, :-1])
```

PyCaretとJupyterで十分簡単に機械学習のモデル構築ができますが、コードを一切見たくない人(がいれば)のために、Streamlitを使ってGUIツールを作ってみます。

# Streamlitでどんな感じのものができるか

コードの中身の前に、どんな感じのツールになるか紹介します。最初はデータアップロードの画面のみが表示されます。

![stアップロード画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193486/4177db44-beb4-a40b-6db9-b4ae98ae435b.png)

データをアップロードすると、データの確認とターゲットの入力、回帰か分類か選択する画面が表示されます。

![st入力画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193486/f39736e5-8077-36b0-1df0-23741aaa1ba8.png)

ターゲットを入力して、回帰を選ぶとモデル構築が始まります。

![実行後画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193486/86bd0587-e37b-159c-6bb2-7d8bb74883d4.png)

実行が終わると最も精度（ここではRMSE）が良かったモデルがpickle形式で出力されます。

推論用には別のコードを用意しています。
推論用のGUIツールでは、はじめにに回帰か分類か選択し、次に推論に使うモデルを選択し、予測したいデータをアップロードすると予測が実行されます。

![推論画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193486/f327cbf5-3fdc-4bc8-b95f-809f56cf99df.png)

予測の値は一番右の列に「Label」として表示されます。

![推論結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193486/1a044dc0-590b-628f-9cb7-bf734c022247.png)

Streamlitを使うと簡単にGUIツールが作れます。
ただ、PyCaretはIPython Notebookを前提にしているところがあるので、StreamlitでそのままPyCaretを使おうとするとうまくいきません。

# PyCaretをちょっと編集する
手を加えるのは以下のコードです。

- pycaret/internal/tabular.pyのcompare_models関数

まずcompare_models関数のdisplayをすべてコメントアウトします。また戻り値としてreturn master_display_を追加します。これだけです。

# AutoMLのGUIツールのコード

```python:stautoml.py
import streamlit as st
import pandas as pd
import datetime

st.markdown("# 1. データをアップロードします")
uploaded_file = st.file_uploader("CSVファイルをドラッグ&ドロップ、またはブラウザから選択してください", type='csv', key='train')
if uploaded_file is not None:
    df = pd.read_csv(uploaded_file)
    st.markdown("# 2. アップロードされたデータを確認します")
    st.dataframe(df.head(10))
    
    st.markdown("# 3. ターゲットを入力してください")
    target = st.text_input(label='ターゲット名を文字列で正しく入力してください', value=df.columns[-1])
    
    st.markdown("# 4. 回帰の場合はregression、分類の場合はclassificationを選択してください")
    ml_usecase = st.selectbox(label='ドロップダウンリストからregressionかclassificationを選択してください',
                              options=('', 'regression', 'classification'),
                              key='ml_usecase')
    if ml_usecase == 'regression':
        from pycaret.regression import *
    elif ml_usecase == 'classification':
        from pycaret.classification import *
    else:
        st.text('選択せれていません')  
    
    if (ml_usecase == 'regression') | (ml_usecase == 'classification'):
    
        st.markdown("# 5. 実行します")
        st.markdown("実行中です…しばらくお待ち下さい…")
        ml = setup(data=df,
                   target=target,
                   session_id=1234,
                   silent=True,
        )
        
        best = compare_models()
        st.dataframe(best)
        select_model = best.index[0]
        model = create_model(select_model)
        final = finalize_model(model)
        save_model(final, select_model+'_saved_'+datetime.date.today().strftime('%Y%m%d'))
        st.markdown("モデル構築が完了しました")             
```

Streamlitは以下のコマンドで実行します。

```
streamlit run stautoml.py
```

# 推論用のコード

```python:stpred.py
import os
import streamlit as st
import pandas as pd
import datetime

st.markdown("# 1. 回帰の場合はregression、分類の場合はclassificationを選択してください")
ml_usecase = st.selectbox(label='ドロップダウンリストからregressionかclassificationを選択してください',
                          options=('', 'regression', 'classification'),
                          key='ml_usecase')
if ml_usecase == 'regression':
    from pycaret.regression import *
elif ml_usecase == 'classification':
    from pycaret.classification import *
else:
    st.text('選択せれていません')  

if (ml_usecase == 'regression') | (ml_usecase == 'classification'):
    st.markdown("# 2. モデルを読み込みます")
    pkl_lt = [''] + [f[:-4] for f in os.listdir(os.getcwd()) if f[-4:]=='.pkl']
    model_name = st.selectbox(label='ドロップダウンリストからモデルを選択してください',
                              options=pkl_lt,
                              key='model')

    if model_name != '':
        dt_saved = load_model(model_name)
    
        st.markdown("# 3. 予測したいデータをアップロードします")
        uploaded_file = st.file_uploader("CSVファイルをドラッグ&ドロップ、またはブラウザから選択してください", type='csv', key='test')
        
        if uploaded_file is not None:
            df_new = pd.read_csv(uploaded_file)
            predictions = predict_model(dt_saved, data=df_new)
            
            predictions.to_csv(model_name+'_predict_'+datetime.date.today().strftime('%Y%m%d')+'.csv')
            st.dataframe(predictions)
```

# おわり
PyCaretを使うと機械学習が簡単にでき、Streamlitを使うと機械学習ツールを作るのも簡単にできるようになります。
実際にユーザーが使うようなツールを作ろうとすると、エラーに対処したり大変ですが。

# 参考

- [PyCaret](https://pycaret.org/)
- [Streamlit](https://www.streamlit.io/)
- [最速でPyCaretを使ってみた](https://qiita.com/s_fukuzawa/items/5dd40a008dac76595eea)→参考にさせていただきました